diff --git a/CMakeLists.txt b/CMakeLists.txt index 919b3ff..074ed68 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,6 +4,7 @@ cmake_minimum_required(VERSION 3.1) SET(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake") aux_source_directory(./src SRC_LIST) +aux_source_directory(./src/input SRC_LIST) list(APPEND SRC_LIST ./src/video/fake.c) set(MOONLIGHT_DEFINITIONS) diff --git a/src/input/cec.c b/src/input/cec.c new file mode 100644 index 0000000..b7af990 --- /dev/null +++ b/src/input/cec.c @@ -0,0 +1,103 @@ +/* + * This file is part of Moonlight Embedded. + * + * Copyright (C) 2015 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 + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Moonlight is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Moonlight; if not, see . + */ + +#ifdef HAVE_LIBCEC +#include + +static libcec_configuration g_config; +static char g_strPort[50] = { 0 }; +static libcec_interface_t g_iface; +static ICECCallbacks g_callbacks; + +static int on_cec_keypress(void* userdata, const cec_keypress key) { + char value; + switch (key.keycode) { + case CEC_USER_CONTROL_CODE_UP: + value = KEY_UP; + break; + case CEC_USER_CONTROL_CODE_DOWN: + value = KEY_DOWN; + break; + case CEC_USER_CONTROL_CODE_LEFT: + value = KEY_LEFT; + break; + case CEC_USER_CONTROL_CODE_RIGHT: + value = KEY_RIGHT; + break; + case CEC_USER_CONTROL_CODE_ENTER: + case CEC_USER_CONTROL_CODE_SELECT: + value = KEY_ENTER; + break; + case CEC_USER_CONTROL_CODE_ROOT_MENU: + value = KEY_TAB; + break; + case CEC_USER_CONTROL_CODE_AN_RETURN: + case CEC_USER_CONTROL_CODE_EXIT: + value = KEY_ESC; + break; + default: + value = 0; + break; + } + + if (value != 0) { + short code = 0x80 << 8 | keyCodes[value]; + LiSendKeyboardEvent(code, (key.duration > 0)?KEY_ACTION_DOWN:KEY_ACTION_UP, 0); + } +} + +void init_cec() { + libcecc_reset_configuration(&g_config); + g_config.clientVersion = LIBCEC_VERSION_CURRENT; + g_config.bActivateSource = 0; + g_callbacks.CBCecKeyPress = &on_cec_keypress; + g_config.callbacks = &g_callbacks; + snprintf(g_config.strDeviceName, sizeof(g_config.strDeviceName), "Moonlight"); + g_config.callbacks = &g_callbacks; + g_config.deviceTypes.types[0] = CEC_DEVICE_TYPE_PLAYBACK_DEVICE; + + if (libcecc_initialise(&g_config, &g_iface, NULL) != 1) { + fprintf(stderr, "Failed to initialize libcec interface\n"); + fflush(stderr); + return; + } + + g_iface.init_video_standalone(g_iface.connection); + + cec_adapter devices[10]; + int8_t iDevicesFound = g_iface.find_adapters(g_iface.connection, devices, sizeof(devices) / sizeof(devices), NULL); + + if (iDevicesFound <= 0) { + fprintf(stderr, "No CEC devices found\n"); + fflush(stderr); + libcecc_destroy(&g_iface); + return; + } + + strcpy(g_strPort, devices[0].comm); + if (!g_iface.open(g_iface.connection, g_strPort, 5000)) { + fprintf(stderr, "Unable to open the device on port %s\n", g_strPort); + fflush(stderr); + libcecc_destroy(&g_iface); + return; + } + + g_iface.set_active_source(g_iface.connection, g_config.deviceTypes.types[0]); +} +#endif /* HAVE_LIBCEC */ \ No newline at end of file diff --git a/src/input/cec.h b/src/input/cec.h new file mode 100644 index 0000000..298ca44 --- /dev/null +++ b/src/input/cec.h @@ -0,0 +1,20 @@ +/* + * This file is part of Moonlight Embedded. + * + * Copyright (C) 2015 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 + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Moonlight is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Moonlight; if not, see . + */ + +void init_cec(); \ No newline at end of file diff --git a/src/input.c b/src/input/evdev.c similarity index 52% rename from src/input.c rename to src/input/evdev.c index 5c63c58..66e4c44 100644 --- a/src/input.c +++ b/src/input/evdev.c @@ -17,6 +17,8 @@ * along with Moonlight; if not, see . */ +#include "../loop.h" + #include "keyboard.h" #include "mapping.h" #include "global.h" @@ -24,20 +26,13 @@ #include "libevdev/libevdev.h" #include "limelight-common/Limelight.h" -#ifdef HAVE_LIBCEC -#include -#endif - #include #include #include #include #include -#include #include #include -#include -#include #include #include #include @@ -55,7 +50,6 @@ struct input_device { struct libevdev *dev; struct mapping map; int fd; - int fdindex; char modifiers; __s32 mouseDeltaX, mouseDeltaY, mouseScroll; short controllerId; @@ -68,107 +62,17 @@ struct input_device { struct input_abs_parms dpadxParms, dpadyParms; }; -static struct pollfd* fds = NULL; static struct input_device* devices = NULL; -static int numDevices = 0, numFds = 0; +static int numDevices = 0; static int assignedControllerIds = 0; static short* currentKey; static short* currentAbs; static bool* currentReverse; -static bool autoadd; -static char* defaultMapfile; -static struct udev *udev; -static struct udev_monitor *udev_mon; -static int udev_fdindex; +static bool (*handler) (struct input_event*, struct input_device*); -static int sig_fdindex; - -#ifdef HAVE_LIBCEC -static libcec_configuration g_config; -static char g_strPort[50] = { 0 }; -static libcec_interface_t g_iface; -static ICECCallbacks g_callbacks; - -static int on_cec_keypress(void* userdata, const cec_keypress key) { - char value; - switch (key.keycode) { - case CEC_USER_CONTROL_CODE_UP: - value = KEY_UP; - break; - case CEC_USER_CONTROL_CODE_DOWN: - value = KEY_DOWN; - break; - case CEC_USER_CONTROL_CODE_LEFT: - value = KEY_LEFT; - break; - case CEC_USER_CONTROL_CODE_RIGHT: - value = KEY_RIGHT; - break; - case CEC_USER_CONTROL_CODE_ENTER: - case CEC_USER_CONTROL_CODE_SELECT: - value = KEY_ENTER; - break; - case CEC_USER_CONTROL_CODE_ROOT_MENU: - value = KEY_TAB; - break; - case CEC_USER_CONTROL_CODE_AN_RETURN: - case CEC_USER_CONTROL_CODE_EXIT: - value = KEY_ESC; - break; - default: - value = 0; - break; - } - - if (value != 0) { - short code = 0x80 << 8 | keyCodes[value]; - LiSendKeyboardEvent(code, (key.duration > 0)?KEY_ACTION_DOWN:KEY_ACTION_UP, 0); - } -} - -static void init_cec() { - libcecc_reset_configuration(&g_config); - g_config.clientVersion = LIBCEC_VERSION_CURRENT; - g_config.bActivateSource = 0; - g_callbacks.CBCecKeyPress = &on_cec_keypress; - g_config.callbacks = &g_callbacks; - snprintf(g_config.strDeviceName, sizeof(g_config.strDeviceName), "Moonlight"); - g_config.callbacks = &g_callbacks; - g_config.deviceTypes.types[0] = CEC_DEVICE_TYPE_PLAYBACK_DEVICE; - - if (libcecc_initialise(&g_config, &g_iface, NULL) != 1) { - fprintf(stderr, "Failed to initialize libcec interface\n"); - fflush(stderr); - return; - } - - g_iface.init_video_standalone(g_iface.connection); - - cec_adapter devices[10]; - int8_t iDevicesFound = g_iface.find_adapters(g_iface.connection, devices, sizeof(devices) / sizeof(devices), NULL); - - if (iDevicesFound <= 0) { - fprintf(stderr, "No CEC devices found\n"); - fflush(stderr); - libcecc_destroy(&g_iface); - return; - } - - strcpy(g_strPort, devices[0].comm); - if (!g_iface.open(g_iface.connection, g_strPort, 5000)) { - fprintf(stderr, "Unable to open the device on port %s\n", g_strPort); - fflush(stderr); - libcecc_destroy(&g_iface); - return; - } - - g_iface.set_active_source(g_iface.connection, g_config.deviceTypes.types[0]); -} -#endif - -static void input_init_parms(struct input_device *dev, struct input_abs_parms *parms, int code) { +static void evdev_init_parms(struct input_device *dev, struct input_abs_parms *parms, int code) { parms->flat = libevdev_get_abs_flat(dev->dev, code); parms->min = libevdev_get_abs_minimum(dev->dev, code); parms->max = libevdev_get_abs_maximum(dev->dev, code); @@ -177,154 +81,19 @@ static void input_init_parms(struct input_device *dev, struct input_abs_parms *p parms->diff = parms->max - parms->min; } -void input_create(const char* device, char* mapFile) { - int fd = open(device, O_RDONLY|O_NONBLOCK); - if (fd <= 0) { - fprintf(stderr, "Failed to open device %s\n", device); - fflush(stderr); - return; - } - - int dev = numDevices; - int fdindex = numFds; - numDevices++; - numFds++; - - if (fds == NULL) { - fds = malloc(sizeof(struct pollfd)); - devices = malloc(sizeof(struct input_device)); - } else { - fds = realloc(fds, sizeof(struct pollfd)*numFds); - devices = realloc(devices, sizeof(struct input_device)*numDevices); - } - - if (fds == NULL || devices == NULL) { - fprintf(stderr, "Not enough memory\n"); - exit(EXIT_FAILURE); - } - - 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); - devices[dev].fdindex = fdindex; - fds[fdindex].fd = devices[dev].fd; - fds[fdindex].events = POLLIN; - - if (mapFile != NULL) - mapping_load(mapFile, &(devices[dev].map)); - - devices[dev].controllerId = -1; - input_init_parms(&devices[dev], &(devices[dev].xParms), devices[dev].map.abs_x); - input_init_parms(&devices[dev], &(devices[dev].yParms), devices[dev].map.abs_y); - input_init_parms(&devices[dev], &(devices[dev].zParms), devices[dev].map.abs_z); - input_init_parms(&devices[dev], &(devices[dev].rxParms), devices[dev].map.abs_rx); - input_init_parms(&devices[dev], &(devices[dev].ryParms), devices[dev].map.abs_ry); - input_init_parms(&devices[dev], &(devices[dev].rzParms), devices[dev].map.abs_rz); - input_init_parms(&devices[dev], &(devices[dev].dpadxParms), devices[dev].map.abs_dpad_x); - input_init_parms(&devices[dev], &(devices[dev].dpadyParms), devices[dev].map.abs_dpad_y); -} - -static void input_remove(int devindex) { +static void evdev_remove(int devindex) { numDevices--; - numFds--; if (devices[devindex].controllerId >= 0) assignedControllerIds &= ~(1 << devices[devindex].controllerId); - int fdindex = devices[devindex].fdindex; - if (fdindex != numFds && numFds > 0) { - memcpy(&fds[fdindex], &fds[numFds], sizeof(struct pollfd)); - if (numFds == udev_fdindex) - udev_fdindex = fdindex; - else if (numFds == sig_fdindex) - sig_fdindex = fdindex; - else { - for (int i=0;i 0) memcpy(&devices[devindex], &devices[numDevices], sizeof(struct input_device)); fprintf(stderr, "Removed input device\n"); } -void input_init(char* mapfile) { - #ifdef HAVE_LIBCEC - init_cec(); - #endif - - udev = udev_new(); - if (!udev) { - fprintf(stderr, "Can't create udev\n"); - exit(1); - } - - autoadd = (numDevices == 0); - if (autoadd) { - struct udev_enumerate *enumerate = udev_enumerate_new(udev); - udev_enumerate_add_match_subsystem(enumerate, "input"); - udev_enumerate_scan_devices(enumerate); - struct udev_list_entry *devices = udev_enumerate_get_list_entry(enumerate); - - struct udev_list_entry *dev_list_entry; - udev_list_entry_foreach(dev_list_entry, devices) { - const char *path = udev_list_entry_get_name(dev_list_entry); - struct udev_device *dev = udev_device_new_from_syspath(udev, path); - const char *devnode = udev_device_get_devnode(dev); - int id; - if (devnode != NULL && sscanf(devnode, "/dev/input/event%d", &id) == 1) { - input_create(devnode, mapfile); - } - udev_device_unref(dev); - } - - udev_enumerate_unref(enumerate); - } - - udev_mon = udev_monitor_new_from_netlink(udev, "udev"); - udev_monitor_filter_add_match_subsystem_devtype(udev_mon, "input", NULL); - udev_monitor_enable_receiving(udev_mon); - - udev_fdindex = numFds++; - sig_fdindex = numFds++; - - if (fds == NULL) - fds = malloc(sizeof(struct pollfd)*numFds); - else - fds = realloc(fds, sizeof(struct pollfd)*numFds); - - if (fds == NULL) { - fprintf(stderr, "Not enough memory\n"); - exit(EXIT_FAILURE); - } - - defaultMapfile = mapfile; - fds[udev_fdindex].fd = udev_monitor_get_fd(udev_mon); - fds[udev_fdindex].events = POLLIN; - - main_thread_id = pthread_self(); - sigset_t sigset; - sigemptyset(&sigset); - sigaddset(&sigset, SIGHUP); - sigaddset(&sigset, SIGTERM); - sigaddset(&sigset, SIGINT); - sigaddset(&sigset, SIGQUIT); - sigprocmask(SIG_BLOCK, &sigset, NULL); - fds[sig_fdindex].fd = signalfd(-1, &sigset, 0); - fds[sig_fdindex].events = POLLIN | POLLERR | POLLHUP; -} - -void input_destroy() { - udev_unref(udev); -} - -static short input_convert_value(struct input_event *ev, struct input_device *dev, struct input_abs_parms *parms, bool reverse) { +static short evdev_convert_value(struct input_event *ev, struct input_device *dev, struct input_abs_parms *parms, bool reverse) { if (abs(ev->value - parms->avg) < parms->flat) return 0; else if (ev->value > parms->max) @@ -337,7 +106,7 @@ static short input_convert_value(struct input_event *ev, struct input_device *de return (ev->value - (ev->value>parms->avg?parms->flat*2:0) - parms->min) * (SHRT_MAX-SHRT_MIN) / (parms->max-parms->min-parms->flat*2) - SHRT_MIN; } -static char input_convert_value_byte(struct input_event *ev, struct input_device *dev, struct input_abs_parms *parms) { +static char evdev_convert_value_byte(struct input_event *ev, struct input_device *dev, struct input_abs_parms *parms) { if (abs(ev->value-parms->min)flat) return 0; else if (ev->value>parms->max) @@ -348,7 +117,7 @@ static char input_convert_value_byte(struct input_event *ev, struct input_device } } -static int input_convert_value_direction(struct input_event *ev, struct input_device *dev, struct input_abs_parms *parms, bool reverse) { +static int evdev_convert_value_direction(struct input_event *ev, struct input_device *dev, struct input_abs_parms *parms, bool reverse) { if (ev->value > (parms->avg+parms->range/4)) return reverse?-1:1; else if (ev->value < (parms->avg-parms->range/4)) @@ -357,7 +126,7 @@ static int input_convert_value_direction(struct input_event *ev, struct input_de return 0; } -static bool input_handle_event(struct input_event *ev, struct input_device *dev) { +static bool evdev_handle_event(struct input_event *ev, struct input_device *dev) { bool gamepadModified = false; switch (ev->type) { @@ -495,19 +264,19 @@ static bool input_handle_event(struct input_event *ev, struct input_device *dev) case EV_ABS: gamepadModified = true; if (ev->code == dev->map.abs_x) - dev->leftStickX = input_convert_value(ev, dev, &dev->xParms, dev->map.reverse_x); + dev->leftStickX = evdev_convert_value(ev, dev, &dev->xParms, dev->map.reverse_x); else if (ev->code == dev->map.abs_y) - dev->leftStickY = input_convert_value(ev, dev, &dev->yParms, dev->map.reverse_y); + dev->leftStickY = evdev_convert_value(ev, dev, &dev->yParms, dev->map.reverse_y); else if (ev->code == dev->map.abs_rx) - dev->rightStickX = input_convert_value(ev, dev, &dev->rxParms, dev->map.reverse_rx); + dev->rightStickX = evdev_convert_value(ev, dev, &dev->rxParms, dev->map.reverse_rx); else if (ev->code == dev->map.abs_ry) - dev->rightStickY = input_convert_value(ev, dev, &dev->ryParms, dev->map.reverse_ry); + dev->rightStickY = evdev_convert_value(ev, dev, &dev->ryParms, dev->map.reverse_ry); else if (ev->code == dev->map.abs_z) - dev->leftTrigger = input_convert_value_byte(ev, dev, &dev->zParms); + dev->leftTrigger = evdev_convert_value_byte(ev, dev, &dev->zParms); else if (ev->code == dev->map.abs_rz) - dev->rightTrigger = input_convert_value_byte(ev, dev, &dev->rzParms); + dev->rightTrigger = evdev_convert_value_byte(ev, dev, &dev->rzParms); else if (ev->code == dev->map.abs_dpad_x) { - int dir = input_convert_value_direction(ev, dev, &dev->dpadxParms, dev->map.reverse_dpad_x); + int dir = evdev_convert_value_direction(ev, dev, &dev->dpadxParms, dev->map.reverse_dpad_x); if (dir == 1) { dev->buttonFlags |= RIGHT_FLAG; dev->buttonFlags &= ~LEFT_FLAG; @@ -519,7 +288,7 @@ static bool input_handle_event(struct input_event *ev, struct input_device *dev) dev->buttonFlags |= LEFT_FLAG; } } else if (ev->code == dev->map.abs_dpad_y) { - int dir = input_convert_value_direction(ev, dev, &dev->dpadyParms, dev->map.reverse_dpad_y); + int dir = evdev_convert_value_direction(ev, dev, &dev->dpadyParms, dev->map.reverse_dpad_y); if (dir == 1) { dev->buttonFlags |= DOWN_FLAG; dev->buttonFlags &= ~UP_FLAG; @@ -540,7 +309,7 @@ static bool input_handle_event(struct input_event *ev, struct input_device *dev) return true; } -static bool input_handle_mapping_event(struct input_event *ev, struct input_device *dev) { +static bool evdev_handle_mapping_event(struct input_event *ev, struct input_device *dev) { switch (ev->type) { case EV_KEY: if (currentKey != NULL) { @@ -553,7 +322,7 @@ static bool input_handle_mapping_event(struct input_event *ev, struct input_devi case EV_ABS: if (currentAbs != NULL) { struct input_abs_parms parms; - input_init_parms(dev, &parms, ev->code); + evdev_init_parms(dev, &parms, ev->code); if (ev->value > parms.avg + parms.range/2) { *currentAbs = ev->code; @@ -569,91 +338,104 @@ static bool input_handle_mapping_event(struct input_event *ev, struct input_devi return true; } -static void input_drain(void) { +static void evdev_drain(void) { for (int i = 0; i < numDevices; i++) { struct input_event ev; while (libevdev_next_event(devices[i].dev, LIBEVDEV_READ_FLAG_NORMAL, &ev) >= 0); } } -static bool input_poll(bool (*handler) (struct input_event*, struct input_device*)) { - while (poll(fds, numFds, -1)) { - if (fds[udev_fdindex].revents > 0) { - struct udev_device *dev = udev_monitor_receive_device(udev_mon); - const char *action = udev_device_get_action(dev); - if (action != NULL) { - if (autoadd && strcmp("add", action) == 0) { - const char *devnode = udev_device_get_devnode(dev); - int id; - if (devnode != NULL && sscanf(devnode, "/dev/input/event%d", &id) == 1) { - input_create(devnode, defaultMapfile); - } +static int evdev_handle(int fd) { + for (int i=0;i= 0) { + if (rc == LIBEVDEV_READ_STATUS_SYNC) + fprintf(stderr, "Error: cannot keep up\n"); + else if (rc == LIBEVDEV_READ_STATUS_SUCCESS) { + if (!handler(&ev, &devices[i])) + return LOOP_RETURN; } - udev_device_unref(dev); } - } else if (fds[sig_fdindex].revents > 0) { - struct signalfd_siginfo info; - read(fds[sig_fdindex].fd, &info, sizeof(info)); - - switch (info.ssi_signo) { - case SIGINT: - case SIGTERM: - case SIGQUIT: - case SIGHUP: - return false; - } - } - for (int i=0;i 0) { - int rc; - struct input_event ev; - while ((rc = libevdev_next_event(devices[i].dev, LIBEVDEV_READ_FLAG_NORMAL, &ev)) >= 0) { - if (rc == LIBEVDEV_READ_STATUS_SYNC) - fprintf(stderr, "Error: cannot keep up\n"); - else if (rc == LIBEVDEV_READ_STATUS_SUCCESS) { - if (!handler(&ev, &devices[i])) - return true; - } - } - if (rc == -ENODEV) { - input_remove(i); - } else if (rc != -EAGAIN && rc < 0) { - fprintf(stderr, "Error: %s\n", strerror(-rc)); - exit(EXIT_FAILURE); - } + if (rc == -ENODEV) { + evdev_remove(i); + } else if (rc != -EAGAIN && rc < 0) { + fprintf(stderr, "Error: %s\n", strerror(-rc)); + exit(EXIT_FAILURE); } } } - - return false; + return LOOP_OK; } -static void input_map_key(char* keyName, short* key) { +void evdev_create(const char* device, char* mapFile) { + int fd = open(device, O_RDONLY|O_NONBLOCK); + if (fd <= 0) { + fprintf(stderr, "Failed to open device %s\n", device); + fflush(stderr); + return; + } + + int dev = numDevices; + numDevices++; + + if (devices == NULL) { + devices = malloc(sizeof(struct input_device)); + } else { + devices = realloc(devices, sizeof(struct input_device)*numDevices); + } + + if (devices == NULL) { + fprintf(stderr, "Not enough memory\n"); + exit(EXIT_FAILURE); + } + + 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].controllerId = -1; + evdev_init_parms(&devices[dev], &(devices[dev].xParms), devices[dev].map.abs_x); + evdev_init_parms(&devices[dev], &(devices[dev].yParms), devices[dev].map.abs_y); + evdev_init_parms(&devices[dev], &(devices[dev].zParms), devices[dev].map.abs_z); + evdev_init_parms(&devices[dev], &(devices[dev].rxParms), devices[dev].map.abs_rx); + evdev_init_parms(&devices[dev], &(devices[dev].ryParms), devices[dev].map.abs_ry); + evdev_init_parms(&devices[dev], &(devices[dev].rzParms), devices[dev].map.abs_rz); + evdev_init_parms(&devices[dev], &(devices[dev].dpadxParms), devices[dev].map.abs_dpad_x); + evdev_init_parms(&devices[dev], &(devices[dev].dpadyParms), devices[dev].map.abs_dpad_y); + + loop_add_fd(devices[dev].fd, &evdev_handle, POLLIN); +} + +static void evdev_map_key(char* keyName, short* key) { printf("Press %s\n", keyName); currentKey = key; currentAbs = NULL; *key = -1; - if (!input_poll(input_handle_mapping_event)) - exit(1); + loop_main(); usleep(250000); - input_drain(); + evdev_drain(); } -static void input_map_abs(char* keyName, short* abs, bool* reverse) { +static void evdev_map_abs(char* keyName, short* abs, bool* reverse) { printf("Move %s\n", keyName); currentKey = NULL; currentAbs = abs; currentReverse = reverse; *abs = -1; - if (!input_poll(input_handle_mapping_event)) - exit(1); + loop_main(); usleep(250000); - input_drain(); + evdev_drain(); } -static void input_map_abskey(char* keyName, short* key, short* abs, bool* reverse) { +static void evdev_map_abskey(char* keyName, short* key, short* abs, bool* reverse) { printf("Press %s\n", keyName); currentKey = key; currentAbs = abs; @@ -661,53 +443,54 @@ static void input_map_abskey(char* keyName, short* key, short* abs, bool* revers *key = -1; *abs = -1; *currentReverse = false; - if (!input_poll(input_handle_mapping_event)) - exit(1); + loop_main(); usleep(250000); - input_drain(); + evdev_drain(); } -void input_map(char* fileName) { +void evdev_map(char* fileName) { struct mapping map; - input_map_abs("Left Stick Right", &(map.abs_x), &(map.reverse_x)); - input_map_abs("Left Stick Up", &(map.abs_y), &(map.reverse_y)); - input_map_key("Left Stick Button", &(map.btn_thumbl)); + handler = evdev_handle_mapping_event; - input_map_abs("Right Stick Right", &(map.abs_rx), &(map.reverse_rx)); - input_map_abs("Right Stick Up", &(map.abs_ry), &(map.reverse_ry)); - input_map_key("Right Stick Button", &(map.btn_thumbr)); + evdev_map_abs("Left Stick Right", &(map.abs_x), &(map.reverse_x)); + evdev_map_abs("Left Stick Up", &(map.abs_y), &(map.reverse_y)); + evdev_map_key("Left Stick Button", &(map.btn_thumbl)); - input_map_abskey("D-Pad Right", &(map.btn_dpad_right), &(map.abs_dpad_x), &(map.reverse_dpad_x)); + evdev_map_abs("Right Stick Right", &(map.abs_rx), &(map.reverse_rx)); + evdev_map_abs("Right Stick Up", &(map.abs_ry), &(map.reverse_ry)); + evdev_map_key("Right Stick Button", &(map.btn_thumbr)); + + evdev_map_abskey("D-Pad Right", &(map.btn_dpad_right), &(map.abs_dpad_x), &(map.reverse_dpad_x)); if (map.btn_dpad_right >= 0) - input_map_key("D-Pad Left", &(map.btn_dpad_left)); + evdev_map_key("D-Pad Left", &(map.btn_dpad_left)); else map.btn_dpad_left = -1; - input_map_abskey("D-Pad Down", &(map.btn_dpad_down), &(map.abs_dpad_y), &(map.reverse_dpad_y)); + evdev_map_abskey("D-Pad Down", &(map.btn_dpad_down), &(map.abs_dpad_y), &(map.reverse_dpad_y)); if (map.btn_dpad_down >= 0) - input_map_key("D-Pad Up", &(map.btn_dpad_up)); + evdev_map_key("D-Pad Up", &(map.btn_dpad_up)); else map.btn_dpad_up = -1; - input_map_key("Button X (1)", &(map.btn_west)); - input_map_key("Button A (2)", &(map.btn_south)); - input_map_key("Button B (3)", &(map.btn_east)); - input_map_key("Button Y (4)", &(map.btn_north)); - input_map_key("Back Button", &(map.btn_select)); - input_map_key("Start Button", &(map.btn_start)); - input_map_key("Special Button", &(map.btn_mode)); + evdev_map_key("Button X (1)", &(map.btn_west)); + evdev_map_key("Button A (2)", &(map.btn_south)); + evdev_map_key("Button B (3)", &(map.btn_east)); + evdev_map_key("Button Y (4)", &(map.btn_north)); + evdev_map_key("Back Button", &(map.btn_select)); + evdev_map_key("Start Button", &(map.btn_start)); + evdev_map_key("Special Button", &(map.btn_mode)); bool ignored; - input_map_abskey("Left Trigger", &(map.btn_tl2), &(map.abs_z), &ignored); - input_map_abskey("Right Trigger", &(map.btn_tr2), &(map.abs_rz), &ignored); + evdev_map_abskey("Left Trigger", &(map.btn_tl2), &(map.abs_z), &ignored); + evdev_map_abskey("Right Trigger", &(map.btn_tr2), &(map.abs_rz), &ignored); - input_map_key("Left Bumper", &(map.btn_tl)); - input_map_key("Right Bumper", &(map.btn_tr)); + evdev_map_key("Left Bumper", &(map.btn_tl)); + evdev_map_key("Right Bumper", &(map.btn_tr)); mapping_save(fileName, &map); } -void input_loop() { - input_poll(input_handle_event); +void evdev_init() { + handler = evdev_handle_event; } diff --git a/src/input.h b/src/input/evdev.h similarity index 82% rename from src/input.h rename to src/input/evdev.h index f77d075..bb9b178 100644 --- a/src/input.h +++ b/src/input/evdev.h @@ -17,9 +17,8 @@ * along with Moonlight; if not, see . */ -void input_init(char* mapfile); -void input_create(char* device, char* mapFile); -void input_loop(); -void input_map(char* fileName); +void evdev_create(const char* device, char* mapFile); +void evdev_loop(); +void evdev_map(char* fileName); -void input_destroy(); +void evdev_init(); diff --git a/src/keyboard.h b/src/input/keyboard.h similarity index 100% rename from src/keyboard.h rename to src/input/keyboard.h diff --git a/src/mapping.c b/src/input/mapping.c similarity index 100% rename from src/mapping.c rename to src/input/mapping.c diff --git a/src/mapping.h b/src/input/mapping.h similarity index 100% rename from src/mapping.h rename to src/input/mapping.h diff --git a/src/input/udev.c b/src/input/udev.c new file mode 100644 index 0000000..b587cd9 --- /dev/null +++ b/src/input/udev.c @@ -0,0 +1,97 @@ +/* + * This file is part of Moonlight Embedded. + * + * Copyright (C) 2015 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 + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Moonlight is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Moonlight; if not, see . + */ + +#include "../loop.h" + +#include "evdev.h" + +#include + +#include +#include +#include +#include +#include +#include + +static bool autoadd; +static char* defaultMapfile; + +static struct udev *udev; +static struct udev_monitor *udev_mon; +static int udev_fd; + +static int udev_handle(int fd) { + struct udev_device *dev = udev_monitor_receive_device(udev_mon); + const char *action = udev_device_get_action(dev); + if (action != NULL) { + if (autoadd && strcmp("add", action) == 0) { + 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); + } + } + udev_device_unref(dev); + } + return LOOP_OK; +} + +void udev_init(bool autoload, char* mapfile) { + udev = udev_new(); + if (!udev) { + fprintf(stderr, "Can't create udev\n"); + exit(1); + } + + autoadd = autoload; + if (autoload) { + struct udev_enumerate *enumerate = udev_enumerate_new(udev); + udev_enumerate_add_match_subsystem(enumerate, "input"); + udev_enumerate_scan_devices(enumerate); + struct udev_list_entry *devices = udev_enumerate_get_list_entry(enumerate); + + struct udev_list_entry *dev_list_entry; + udev_list_entry_foreach(dev_list_entry, devices) { + const char *path = udev_list_entry_get_name(dev_list_entry); + struct udev_device *dev = udev_device_new_from_syspath(udev, path); + 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); + } + udev_device_unref(dev); + } + + udev_enumerate_unref(enumerate); + } + + udev_mon = udev_monitor_new_from_netlink(udev, "udev"); + udev_monitor_filter_add_match_subsystem_devtype(udev_mon, "input", NULL); + udev_monitor_enable_receiving(udev_mon); + + defaultMapfile = mapfile; + + int udev_fd = udev_monitor_get_fd(udev_mon); + loop_add_fd(udev_fd, &udev_handle, POLLIN); +} + +void evdev_destroy() { + udev_unref(udev); +} diff --git a/src/input/udev.h b/src/input/udev.h new file mode 100644 index 0000000..b834adc --- /dev/null +++ b/src/input/udev.h @@ -0,0 +1,21 @@ +/* + * This file is part of Moonlight Embedded. + * + * Copyright (C) 2015 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 + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Moonlight is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Moonlight; if not, see . + */ + +void udev_init(bool autoload, char* mapfile); +void evdev_destroy(); diff --git a/src/loop.c b/src/loop.c new file mode 100644 index 0000000..d00097b --- /dev/null +++ b/src/loop.c @@ -0,0 +1,113 @@ +/* + * This file is part of Moonlight Embedded. + * + * Copyright (C) 2015 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 + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Moonlight is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Moonlight; if not, see . + */ + +#include "loop.h" + +#include "global.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +static struct pollfd* fds = NULL; +static FdHandler* fdHandlers = NULL; +static int numFds = 0; + +static int sigFd; + +static int loop_sig_handler(int fd) { + struct signalfd_siginfo info; + read(fd, &info, sizeof(info)); + switch (info.ssi_signo) { + case SIGINT: + case SIGTERM: + case SIGQUIT: + case SIGHUP: + return LOOP_RETURN; + } + return LOOP_OK; +} + +void loop_add_fd(int fd, FdHandler handler, int events) { + int fdindex = numFds; + numFds++; + + if (fds == NULL) { + fds = malloc(sizeof(struct pollfd)); + fdHandlers = malloc(sizeof(FdHandler*)); + } else { + fds = realloc(fds, sizeof(struct pollfd)*numFds); + fdHandlers = realloc(fdHandlers, sizeof(FdHandler*)*numFds); + } + + if (fds == NULL || fdHandlers == NULL) { + fprintf(stderr, "Not enough memory\n"); + exit(EXIT_FAILURE); + } + + fds[fdindex].fd = fd; + fds[fdindex].events = events; +} + +void loop_remove_fd(int fd) { + numFds--; + int fdindex; + + for (int i=0;i 0) { + memcpy(&fds[fdindex], &fds[numFds], sizeof(struct pollfd)); + memcpy(&fdHandlers[fdindex], &fdHandlers[numFds], sizeof(FdHandler*)); + } +} + +void loop_main() { + main_thread_id = pthread_self(); + sigset_t sigset; + sigemptyset(&sigset); + sigaddset(&sigset, SIGHUP); + sigaddset(&sigset, SIGTERM); + sigaddset(&sigset, SIGINT); + sigaddset(&sigset, SIGQUIT); + sigprocmask(SIG_BLOCK, &sigset, NULL); + sigFd = signalfd(-1, &sigset, 0); + loop_add_fd(sigFd, loop_sig_handler, POLLIN | POLLERR | POLLHUP); + +//static bool evdev_poll(bool (*handler) (struct input_event*, struct input_device*)) { + while (poll(fds, numFds, -1)) { + for (int i=0;i 0) { + int ret = fdHandlers[i](fds[i].fd); + if (ret == LOOP_RETURN) { + return; + } + break; + } + } + } +} diff --git a/src/loop.h b/src/loop.h new file mode 100644 index 0000000..e4550ca --- /dev/null +++ b/src/loop.h @@ -0,0 +1,28 @@ +/* + * This file is part of Moonlight Embedded. + * + * Copyright (C) 2015 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 + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * Moonlight is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Moonlight; if not, see . + */ + +#define LOOP_RETURN 1 +#define LOOP_OK 0 + +typedef int(*FdHandler)(int fd); + +void loop_add_fd(int fd, FdHandler handler, int events); +void loop_remove_fd(int fd); + +void loop_main(); \ No newline at end of file diff --git a/src/main.c b/src/main.c index 621f661..fd5225e 100644 --- a/src/main.c +++ b/src/main.c @@ -17,13 +17,17 @@ * along with Moonlight; if not, see . */ +#include "loop.h" #include "client.h" #include "connection.h" #include "video.h" #include "audio.h" -#include "input.h" #include "discover.h" +#include "input/evdev.h" +#include "input/udev.h" +#include "input/cec.h" + #include "limelight-common/Limelight.h" #include @@ -60,10 +64,14 @@ static void stream(STREAM_CONFIGURATION* config, const char* address, const char client_start_app(config, address, appId, sops, localaudio); video_init(); + evdev_init(); + #ifdef HAVE_LIBCEC + cec_init(); + #endif /* HAVE_LIBCEC */ LiStartConnection(address, config, &connection_callbacks, decoder_callbacks, &audio_callbacks, NULL, NULL, 0, client_get_server_version()); - input_loop(); + loop_main(); LiStopConnection(); } @@ -172,6 +180,7 @@ int main(int argc, char* argv[]) { int option_index = 0; bool sops = true; bool localaudio = false; + bool autoadd = true; int c; while ((c = getopt_long_only(argc, argv, "-abc:d:efg:h:i:j:k:lm:n", long_options, &option_index)) != -1) { switch (c) { @@ -205,7 +214,8 @@ int main(int argc, char* argv[]) { app = optarg; break; case 'j': - input_create(optarg, mapping); + evdev_create(optarg, mapping); + autoadd = false; break; case 'k': mapping = get_path(optarg); @@ -238,8 +248,8 @@ int main(int argc, char* argv[]) { perror("No filename for mapping"); exit(-1); } - input_init(mapping); - input_map(address); + udev_init(autoadd, mapping); + evdev_map(address); exit(0); } @@ -263,7 +273,7 @@ int main(int argc, char* argv[]) { pair_check(); applist(address); } else if (strcmp("stream", action) == 0) { - input_init(mapping); + udev_init(autoadd, mapping); pair_check(); stream(&config, address, app, sops, localaudio); } else if (strcmp("pair", action) == 0)