diff --git a/docs/README.pod b/docs/README.pod index d278943..fd48502 100644 --- a/docs/README.pod +++ b/docs/README.pod @@ -128,8 +128,8 @@ By default the encryption keys are stored in $XDG_CACHE_DIR/moonlight or ~/.cach =item B<-mapping> [I] -Use I as the mapping file for all inputs specified after this B<-mapping>. -If no B<-input> is specified after the B<-mapping> this mapping is used for autoloaded inputs. +Use I as the mapping file for all inputs. +This mapping file should have the same format as the gamecontrollerdb.txt for SDL. =item B<-input> [I] diff --git a/src/config.c b/src/config.c index 1381905..47ddd71 100644 --- a/src/config.c +++ b/src/config.c @@ -1,7 +1,7 @@ /* * This file is part of Moonlight Embedded. * - * Copyright (C) 2015, 2016 Iwan Timmer + * Copyright (C) 2015-2017 Iwan Timmer * * Moonlight is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -37,7 +37,6 @@ #define write_config_bool(fd, key, value) fprintf(fd, "%s = %s\n", key, value?"true":"false"); bool inputAdded = false; -static bool mapped = true; const char* audio_device = NULL; static struct option long_options[] = { @@ -152,11 +151,9 @@ static void parse_argument(int c, char* value, PCONFIGURATION config) { perror("Too many inputs specified"); exit(-1); } - config->inputs[config->inputsCount].path = value; - config->inputs[config->inputsCount].mapping = config->mapping; + config->inputs[config->inputsCount] = value; config->inputsCount++; inputAdded = true; - mapped = true; break; case 'k': config->mapping = get_path(value, getenv("XDG_DATA_DIRS")); @@ -164,7 +161,6 @@ static void parse_argument(int c, char* value, PCONFIGURATION config) { fprintf(stderr, "Unable to open custom mapping file: %s\n", value); exit(-1); } - mapped = false; break; case 'l': config->sops = false; @@ -352,10 +348,7 @@ void config_parse(int argc, char* argv[], PCONFIGURATION config) { } if (inputAdded) { - if (!mapped) { - fprintf(stderr, "Mapping option should be followed by the input to be mapped.\n"); - exit(-1); - } else if (config->mapping == NULL) { + if (config->mapping == NULL) { fprintf(stderr, "Please specify mapping file as default mapping could not be found.\n"); exit(-1); } diff --git a/src/config.h b/src/config.h index de7d3a8..3478f71 100644 --- a/src/config.h +++ b/src/config.h @@ -1,7 +1,7 @@ /* * This file is part of Moonlight Embedded. * - * Copyright (C) 2015, 2016 Iwan Timmer + * Copyright (C) 2015-2017 Iwan Timmer * * Moonlight is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -25,11 +25,6 @@ enum codecs { CODEC_UNSPECIFIED, CODEC_H264, CODEC_HEVC }; -struct input_config { - char* path; - char* mapping; -}; - typedef struct _CONFIGURATION { STREAM_CONFIGURATION stream; char* app; @@ -44,7 +39,7 @@ typedef struct _CONFIGURATION { bool fullscreen; bool forcehw; bool unsupported_version; - struct input_config inputs[MAX_INPUTS]; + char* inputs[MAX_INPUTS]; int inputsCount; enum codecs codec; } CONFIGURATION, *PCONFIGURATION; diff --git a/src/input/evdev.c b/src/input/evdev.c index 441c770..1879e82 100644 --- a/src/input/evdev.c +++ b/src/input/evdev.c @@ -38,6 +38,13 @@ #include #include #include +#include + +#if __BYTE_ORDER == __LITTLE_ENDIAN +#define int16_to_le(val) val +#else +#define int16_to_le(val) ((((val) >> 8) & 0x00FF) | (((val) << 8) & 0xFF00)) +#endif struct input_abs_parms { int min, max; @@ -48,7 +55,7 @@ struct input_abs_parms { struct input_device { struct libevdev *dev; - struct mapping map; + struct mapping* map; int key_map[KEY_MAX]; int abs_map[ABS_MAX]; int hats_state[3][2]; @@ -209,35 +216,35 @@ static bool evdev_handle_event(struct input_event *ev, struct input_device *dev) mouseCode = BUTTON_RIGHT; break; default: - if (index == dev->map.btn_a) + if (index == dev->map->btn_a) gamepadCode = A_FLAG; - else if (index == dev->map.btn_x) + else if (index == dev->map->btn_x) gamepadCode = X_FLAG; - else if (index == dev->map.btn_y) + else if (index == dev->map->btn_y) gamepadCode = Y_FLAG; - else if (index == dev->map.btn_b) + else if (index == dev->map->btn_b) gamepadCode = B_FLAG; - else if (index == dev->map.btn_dpup) + else if (index == dev->map->btn_dpup) gamepadCode = UP_FLAG; - else if (index == dev->map.btn_dpdown) + else if (index == dev->map->btn_dpdown) gamepadCode = DOWN_FLAG; - else if (index == dev->map.btn_dpright) + else if (index == dev->map->btn_dpright) gamepadCode = RIGHT_FLAG; - else if (index == dev->map.btn_dpleft) + else if (index == dev->map->btn_dpleft) gamepadCode = LEFT_FLAG; - else if (index == dev->map.btn_leftstick) + else if (index == dev->map->btn_leftstick) gamepadCode = LS_CLK_FLAG; - else if (index == dev->map.btn_rightstick) + else if (index == dev->map->btn_rightstick) gamepadCode = RS_CLK_FLAG; - else if (index == dev->map.btn_leftshoulder) + else if (index == dev->map->btn_leftshoulder) gamepadCode = LB_FLAG; - else if (index == dev->map.btn_rightshoulder) + else if (index == dev->map->btn_rightshoulder) gamepadCode = RB_FLAG; - else if (index == dev->map.btn_start) + else if (index == dev->map->btn_start) gamepadCode = PLAY_FLAG; - else if (index == dev->map.btn_back) + else if (index == dev->map->btn_back) gamepadCode = BACK_FLAG; - else if (index == dev->map.btn_guide) + else if (index == dev->map->btn_guide) gamepadCode = SPECIAL_FLAG; } @@ -251,9 +258,9 @@ static bool evdev_handle_event(struct input_event *ev, struct input_device *dev) dev->buttonFlags |= gamepadCode; else dev->buttonFlags &= ~gamepadCode; - } else if (index == dev->map.btn_lefttrigger) + } else if (index == dev->map->btn_lefttrigger) dev->leftTrigger = ev->value?UCHAR_MAX:0; - else if (index == dev->map.btn_righttrigger) + else if (index == dev->map->btn_righttrigger) dev->rightTrigger = ev->value?UCHAR_MAX:0; else { fprintf(stderr, "Unmapped button: %d\n", ev->code); @@ -292,28 +299,28 @@ static bool evdev_handle_event(struct input_event *ev, struct input_device *dev) case ABS_HAT3Y: dev->hats_state[hat_index][har_dir_index] = ev->value < 0 ? 0 : (ev->value == 0 ? 1 : 2); int hat_state = hat_constants[dev->hats_state[hat_index][0]][dev->hats_state[hat_index][1]]; - if (hat_index == dev->map.hat_dpup) - set_hat(dev->buttonFlags, UP_FLAG, hat_state, dev->map.hat_dir_dpup); - if (hat_index == dev->map.hat_dpdown) - set_hat(dev->buttonFlags, DOWN_FLAG, hat_state, dev->map.hat_dir_dpdown); - if (hat_index == dev->map.hat_dpright) - set_hat(dev->buttonFlags, HAT_RIGHT, hat_state, dev->map.hat_dir_dpright); - if (hat_index == dev->map.hat_dpleft) - set_hat(dev->buttonFlags, HAT_LEFT, hat_state, dev->map.hat_dir_dpleft); + if (hat_index == dev->map->hat_dpup) + set_hat(dev->buttonFlags, UP_FLAG, hat_state, dev->map->hat_dir_dpup); + if (hat_index == dev->map->hat_dpdown) + set_hat(dev->buttonFlags, DOWN_FLAG, hat_state, dev->map->hat_dir_dpdown); + if (hat_index == dev->map->hat_dpright) + set_hat(dev->buttonFlags, HAT_RIGHT, hat_state, dev->map->hat_dir_dpright); + if (hat_index == dev->map->hat_dpleft) + set_hat(dev->buttonFlags, HAT_LEFT, hat_state, dev->map->hat_dir_dpleft); break; } default: - if (index == dev->map.abs_leftx) - dev->leftStickX = evdev_convert_value(ev, dev, &dev->xParms, dev->map.reverse_leftx); - else if (index == dev->map.abs_lefty) - dev->leftStickY = evdev_convert_value(ev, dev, &dev->yParms, !dev->map.reverse_lefty); - else if (index == dev->map.abs_rightx) - dev->rightStickX = evdev_convert_value(ev, dev, &dev->rxParms, dev->map.reverse_rightx); - else if (index == dev->map.abs_righty) - dev->rightStickY = evdev_convert_value(ev, dev, &dev->ryParms, !dev->map.reverse_righty); - else if (index == dev->map.abs_lefttrigger) + if (index == dev->map->abs_leftx) + dev->leftStickX = evdev_convert_value(ev, dev, &dev->xParms, dev->map->reverse_leftx); + else if (index == dev->map->abs_lefty) + dev->leftStickY = evdev_convert_value(ev, dev, &dev->yParms, !dev->map->reverse_lefty); + else if (index == dev->map->abs_rightx) + dev->rightStickX = evdev_convert_value(ev, dev, &dev->rxParms, dev->map->reverse_rightx); + else if (index == dev->map->abs_righty) + dev->rightStickY = evdev_convert_value(ev, dev, &dev->ryParms, !dev->map->reverse_righty); + else if (index == dev->map->abs_lefttrigger) dev->leftTrigger = evdev_convert_value_byte(ev, dev, &dev->zParms); - else if (index == dev->map.abs_righttrigger) + else if (index == dev->map->abs_righttrigger) dev->rightTrigger = evdev_convert_value_byte(ev, dev, &dev->rzParms); else gamepadModified = false; @@ -383,7 +390,7 @@ static int evdev_handle(int fd) { return LOOP_OK; } -void evdev_create(const char* device, char* mapFile) { +void evdev_create(const char* device, struct mapping* mappings) { int fd = open(device, O_RDONLY|O_NONBLOCK); if (fd <= 0) { fprintf(stderr, "Failed to open device %s\n", device); @@ -391,6 +398,31 @@ void evdev_create(const char* device, char* mapFile) { return; } + struct libevdev *evdev = libevdev_new(); + libevdev_set_fd(evdev, fd); + + int16_t guid[8] = {0}; + guid[0] = int16_to_le(libevdev_get_id_bustype(evdev)); + guid[2] = int16_to_le(libevdev_get_id_vendor(evdev)); + guid[4] = int16_to_le(libevdev_get_id_product(evdev)); + guid[6] = int16_to_le(libevdev_get_id_version(evdev)); + + char str_guid[33]; + char* buf = str_guid; + for (int i = 0; i < 16; i++) + buf += sprintf(buf, "%02x", ((unsigned char*) guid)[i]); + + while (mappings != NULL && strncmp(str_guid, mappings->guid, 32) != 0) + mappings = mappings->next; + + if (mappings == NULL) { + fprintf(stderr, "No mapping available for %s\n", device); + fflush(stderr); + close(fd); + libevdev_free(evdev); + return; + } + int dev = numDevices; numDevices++; @@ -407,11 +439,8 @@ void evdev_create(const char* device, char* mapFile) { memset(&devices[dev], 0, sizeof(devices[0])); devices[dev].fd = fd; - devices[dev].dev = libevdev_new(); - libevdev_set_fd(devices[dev].dev, devices[dev].fd); - - if (mapFile != NULL) - mapping_load(mapFile, &(devices[dev].map)); + devices[dev].dev = evdev; + devices[dev].map = mappings; int nbuttons = 0; for (int i = BTN_JOYSTICK; i < KEY_MAX; ++i) { @@ -433,12 +462,12 @@ void evdev_create(const char* device, char* mapFile) { } devices[dev].controllerId = -1; - evdev_init_parms(&devices[dev], &(devices[dev].xParms), devices[dev].map.abs_leftx); - evdev_init_parms(&devices[dev], &(devices[dev].yParms), devices[dev].map.abs_lefty); - evdev_init_parms(&devices[dev], &(devices[dev].zParms), devices[dev].map.abs_lefttrigger); - evdev_init_parms(&devices[dev], &(devices[dev].rxParms), devices[dev].map.abs_rightx); - evdev_init_parms(&devices[dev], &(devices[dev].ryParms), devices[dev].map.abs_righty); - evdev_init_parms(&devices[dev], &(devices[dev].rzParms), devices[dev].map.abs_righttrigger); + evdev_init_parms(&devices[dev], &(devices[dev].xParms), devices[dev].map->abs_leftx); + evdev_init_parms(&devices[dev], &(devices[dev].yParms), devices[dev].map->abs_lefty); + evdev_init_parms(&devices[dev], &(devices[dev].zParms), devices[dev].map->abs_lefttrigger); + evdev_init_parms(&devices[dev], &(devices[dev].rxParms), devices[dev].map->abs_rightx); + evdev_init_parms(&devices[dev], &(devices[dev].ryParms), devices[dev].map->abs_righty); + evdev_init_parms(&devices[dev], &(devices[dev].rzParms), devices[dev].map->abs_righttrigger); if (grabbingDevices) { if (ioctl(fd, EVIOCGRAB, 1) < 0) { diff --git a/src/input/evdev.h b/src/input/evdev.h index aca8699..e52319a 100644 --- a/src/input/evdev.h +++ b/src/input/evdev.h @@ -17,7 +17,7 @@ * along with Moonlight; if not, see . */ -void evdev_create(const char* device, char* mapFile); +void evdev_create(const char* device, struct mapping* mappings); void evdev_loop(); void evdev_init(); diff --git a/src/input/mapping.c b/src/input/mapping.c index 2260c12..dd357ba 100644 --- a/src/input/mapping.c +++ b/src/input/mapping.c @@ -23,7 +23,9 @@ #include #include -void mapping_load(char* fileName, struct mapping* map) { +struct mapping* mapping_load(char* fileName) { + struct mapping* mappings = NULL; + struct mapping* map = NULL; FILE* fd = fopen(fileName, "r"); if (fd == NULL) { fprintf(stderr, "Can't open mapping file: %s\n", fileName); @@ -39,6 +41,17 @@ void mapping_load(char* fileName, struct mapping* map) { if (guid == NULL || name == NULL) continue; + struct mapping* newmap = malloc(sizeof(struct mapping)); + if (newmap == NULL) { + fprintf(stderr, "Not enough memory"); + exit(EXIT_FAILURE); + } else if (mappings == NULL) + mappings = newmap; + else + map->next = newmap; + + map = newmap; + strncpy(map->guid, guid, sizeof(map->guid)); strncpy(map->name, name, sizeof(map->name)); @@ -133,4 +146,6 @@ void mapping_load(char* fileName, struct mapping* map) { map->platform[32] = '\0'; } free(line); + + return mappings; } diff --git a/src/input/mapping.h b/src/input/mapping.h index 44e2c10..c80b0a5 100644 --- a/src/input/mapping.h +++ b/src/input/mapping.h @@ -17,6 +17,8 @@ * along with Moonlight; if not, see . */ +#pragma once + #include struct mapping { @@ -41,6 +43,8 @@ struct mapping { short abs_lefttrigger, abs_righttrigger; short btn_lefttrigger, btn_righttrigger; + + struct mapping* next; }; -void mapping_load(char* fileName, struct mapping* map); +struct mapping* mapping_load(char* fileName); diff --git a/src/input/sdlinput.c b/src/input/sdlinput.c index 67e0670..cf91d79 100644 --- a/src/input/sdlinput.c +++ b/src/input/sdlinput.c @@ -1,7 +1,7 @@ /* * This file is part of Moonlight Embedded. * - * Copyright (C) 2015 Iwan Timmer + * Copyright (C) 2015-2017 Iwan Timmer * * Moonlight is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -43,11 +43,11 @@ static GAMEPAD_STATE gamepads[4]; static int keyboard_modifiers; static int activeGamepadMask = 0; -void sdlinput_init() { +void sdlinput_init(char* mappings) { memset(gamepads, 0, sizeof(gamepads)); SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER); - SDL_GameControllerAddMappingsFromFile("gamecontrollerdb.txt"); + SDL_GameControllerAddMappingsFromFile(mappings); for (int i = 0; i < SDL_NumJoysticks(); ++i) { if (SDL_IsGameController(i)) { diff --git a/src/input/sdlinput.h b/src/input/sdlinput.h index e7cd2f3..4e6e371 100644 --- a/src/input/sdlinput.h +++ b/src/input/sdlinput.h @@ -1,7 +1,7 @@ /* * This file is part of Moonlight Embedded. * - * Copyright (C) 2015 Iwan Timmer + * Copyright (C) 2015-2017 Iwan Timmer * * Moonlight is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -51,7 +51,7 @@ static const short keyCodes[] = { 0x26, //SDLK_UP }; -void sdlinput_init(); +void sdlinput_init(char* mappings); int sdlinput_handle_event(SDL_Event* event); #endif /* HAVE_SDL */ diff --git a/src/input/udev.c b/src/input/udev.c index b587cd9..6766b91 100644 --- a/src/input/udev.c +++ b/src/input/udev.c @@ -1,7 +1,7 @@ /* * This file is part of Moonlight Embedded. * - * Copyright (C) 2015 Iwan Timmer + * Copyright (C) 2015-2017 Iwan Timmer * * Moonlight is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,6 +19,7 @@ #include "../loop.h" +#include "udev.h" #include "evdev.h" #include @@ -31,7 +32,7 @@ #include static bool autoadd; -static char* defaultMapfile; +static struct mapping* defaultMappings; static struct udev *udev; static struct udev_monitor *udev_mon; @@ -45,7 +46,7 @@ static int udev_handle(int fd) { const char *devnode = udev_device_get_devnode(dev); int id; if (devnode != NULL && sscanf(devnode, "/dev/input/event%d", &id) == 1) { - evdev_create(devnode, defaultMapfile); + evdev_create(devnode, defaultMappings); } } udev_device_unref(dev); @@ -53,7 +54,7 @@ static int udev_handle(int fd) { return LOOP_OK; } -void udev_init(bool autoload, char* mapfile) { +void udev_init(bool autoload, struct mapping* mappings) { udev = udev_new(); if (!udev) { fprintf(stderr, "Can't create udev\n"); @@ -74,7 +75,7 @@ void udev_init(bool autoload, char* mapfile) { const char *devnode = udev_device_get_devnode(dev); int id; if (devnode != NULL && sscanf(devnode, "/dev/input/event%d", &id) == 1) { - evdev_create(devnode, mapfile); + evdev_create(devnode, mappings); } udev_device_unref(dev); } @@ -86,7 +87,7 @@ void udev_init(bool autoload, char* mapfile) { udev_monitor_filter_add_match_subsystem_devtype(udev_mon, "input", NULL); udev_monitor_enable_receiving(udev_mon); - defaultMapfile = mapfile; + defaultMappings = mappings; int udev_fd = udev_monitor_get_fd(udev_mon); loop_add_fd(udev_fd, &udev_handle, POLLIN); diff --git a/src/input/udev.h b/src/input/udev.h index b834adc..9414b6a 100644 --- a/src/input/udev.h +++ b/src/input/udev.h @@ -17,5 +17,7 @@ * along with Moonlight; if not, see . */ -void udev_init(bool autoload, char* mapfile); +#include "mapping.h" + +void udev_init(bool autoload, struct mapping* mappings); void evdev_destroy(); diff --git a/src/main.c b/src/main.c index 5b45639..18c9675 100644 --- a/src/main.c +++ b/src/main.c @@ -28,6 +28,7 @@ #include "platform.h" #include "sdl.h" +#include "input/mapping.h" #include "input/evdev.h" #include "input/udev.h" #include "input/cec.h" @@ -145,13 +146,13 @@ static void help() { printf("\t-localaudio\t\tPlay audio locally\n"); printf("\t-surround\t\tStream 5.1 surround sound (requires GFE 2.7)\n"); printf("\t-keydir \tLoad encryption keys from directory\n"); + printf("\t-mapping \t\tUse as gamepad mappings configuration file\n"); #ifdef HAVE_SDL printf("\n Video options (SDL Only)\n\n"); printf("\t-windowed\t\tDisplay screen in a window\n"); #endif #ifdef HAVE_EMBEDDED printf("\n I/O options\n\n"); - printf("\t-mapping \t\tUse as gamepad mapping configuration file (use before -input)\n"); printf("\t-input \t\tUse as input. Can be used multiple times\n"); printf("\t-audio \t\tUse as audio output device\n"); printf("\t-forcehw \t\tTry to use video hardware acceleration\n"); @@ -231,20 +232,24 @@ int main(int argc, char* argv[]) { } else if (strcmp("stream", config.action) == 0) { pair_check(&server); if (IS_EMBEDDED(system)) { + struct mapping* mappings = mapping_load(config.mapping); + for (int i=0;i