diff --git a/Makefile b/Makefile index a3ac596bc2..cd7f7c7fe2 100644 --- a/Makefile +++ b/Makefile @@ -328,7 +328,7 @@ ifeq ($(HAVE_UDEV), 1) DEFINES += $(UDEV_CFLAGS) LIBS += $(UDEV_LIBS) JOYCONFIG_LIBS += $(UDEV_LIBS) - OBJ += input/udev_joypad.o + OBJ += input/udev_input.o input/udev_joypad.o JOYCONFIG_OBJ += tools/udev_joypad.o endif diff --git a/config.def.h b/config.def.h index e73d8e8643..57ccb2ed9d 100644 --- a/config.def.h +++ b/config.def.h @@ -75,6 +75,7 @@ enum INPUT_XENON360, INPUT_WII, INPUT_XINPUT, + INPUT_UDEV, INPUT_LINUXRAW, INPUT_APPLE, INPUT_QNX, @@ -175,6 +176,8 @@ enum #define INPUT_DEFAULT_DRIVER INPUT_PSP #elif defined(GEKKO) #define INPUT_DEFAULT_DRIVER INPUT_WII +#elif defined(HAVE_UDEV) +#define INPUT_DEFAULT_DRIVER INPUT_UDEV #elif defined(PANDORA) || defined(HAVE_VIDEOCORE) #define INPUT_DEFAULT_DRIVER INPUT_LINUXRAW #elif defined(HAVE_X11) diff --git a/driver.c b/driver.c index 3e2207348e..e839afb05d 100644 --- a/driver.c +++ b/driver.c @@ -161,6 +161,9 @@ static const input_driver_t *input_drivers[] = { #ifdef ANDROID &input_android, #endif +#ifdef HAVE_UDEV + &input_udev, +#endif #if defined(__linux__) && !defined(ANDROID) &input_linuxraw, #endif diff --git a/driver.h b/driver.h index 60905b5427..d3c1396e54 100644 --- a/driver.h +++ b/driver.h @@ -613,6 +613,7 @@ extern const input_driver_t input_xenon360; extern const input_driver_t input_gx; extern const input_driver_t input_xinput; extern const input_driver_t input_linuxraw; +extern const input_driver_t input_udev; extern const input_driver_t input_apple; extern const input_driver_t input_qnx; extern const input_driver_t input_rwebinput; diff --git a/gfx/context/drm_egl_ctx.c b/gfx/context/drm_egl_ctx.c index 479633a12c..79e42026a7 100644 --- a/gfx/context/drm_egl_ctx.c +++ b/gfx/context/drm_egl_ctx.c @@ -545,9 +545,15 @@ void gfx_ctx_destroy(void) static void gfx_ctx_input_driver(const input_driver_t **input, void **input_data) { +#ifdef HAVE_UDEV + void *udev = input_udev.init(); + *input = udev ? &input_udev : NULL; + *input_data = udev; +#else void *linuxinput = input_linuxraw.init(); *input = linuxinput ? &input_linuxraw : NULL; *input_data = linuxinput; +#endif } static bool gfx_ctx_has_focus(void) diff --git a/gfx/context/vc_egl_ctx.c b/gfx/context/vc_egl_ctx.c index 4ab814bcb2..ca8ddfb873 100644 --- a/gfx/context/vc_egl_ctx.c +++ b/gfx/context/vc_egl_ctx.c @@ -337,9 +337,15 @@ static void gfx_ctx_destroy(void) static void gfx_ctx_input_driver(const input_driver_t **input, void **input_data) { +#ifdef HAVE_UDEV + void *udev = input_udev.init(); + *input = udev ? &input_udev : NULL; + *input_data = udev; +#else void *linuxinput = input_linuxraw.init(); *input = linuxinput ? &input_linuxraw : NULL; *input_data = linuxinput; +#endif } static bool gfx_ctx_has_focus(void) diff --git a/input/input_common.c b/input/input_common.c index 7e55d5c7a9..a66ae67fbc 100644 --- a/input/input_common.c +++ b/input/input_common.c @@ -38,6 +38,11 @@ #include #endif +#ifdef __linux +#include +#include +#endif + #include "../file.h" static const rarch_joypad_driver_t *joypad_drivers[] = { @@ -624,6 +629,117 @@ const struct rarch_key_map rarch_key_map_rwebinput[] = { }; #endif +#ifdef __linux +const struct rarch_key_map rarch_key_map_linux[] = { + { KEY_ESC, RETROK_ESCAPE }, + { KEY_1, RETROK_1 }, + { KEY_2, RETROK_2 }, + { KEY_3, RETROK_3}, + { KEY_4, RETROK_4 }, + { KEY_5, RETROK_5 }, + { KEY_6, RETROK_6 }, + { KEY_7, RETROK_7 }, + { KEY_8, RETROK_8 }, + { KEY_9, RETROK_9 }, + { KEY_0, RETROK_0 }, + { KEY_MINUS, RETROK_MINUS }, + { KEY_EQUAL, RETROK_EQUALS }, + { KEY_BACKSPACE, RETROK_BACKSPACE }, + { KEY_TAB, RETROK_TAB }, + { KEY_Q, RETROK_q }, + { KEY_W, RETROK_w }, + { KEY_E, RETROK_e }, + { KEY_R, RETROK_r }, + { KEY_T, RETROK_t }, + { KEY_Y, RETROK_y }, + { KEY_U, RETROK_u }, + { KEY_I, RETROK_i }, + { KEY_O, RETROK_o }, + { KEY_P, RETROK_p }, + { KEY_LEFTBRACE, RETROK_LEFTBRACKET }, + { KEY_RIGHTBRACE, RETROK_RIGHTBRACKET }, + { KEY_ENTER, RETROK_RETURN }, + { KEY_LEFTCTRL, RETROK_LCTRL }, + { KEY_A, RETROK_a }, + { KEY_S, RETROK_s }, + { KEY_D, RETROK_d }, + { KEY_F, RETROK_f }, + { KEY_G, RETROK_g }, + { KEY_H, RETROK_h }, + { KEY_J, RETROK_j }, + { KEY_K, RETROK_k }, + { KEY_L, RETROK_l }, + { KEY_SEMICOLON, RETROK_SEMICOLON }, + { KEY_APOSTROPHE, RETROK_QUOTE }, + { KEY_GRAVE, RETROK_BACKQUOTE }, + { KEY_LEFTSHIFT, RETROK_LSHIFT }, + { KEY_BACKSLASH, RETROK_BACKSLASH }, + { KEY_Z, RETROK_z }, + { KEY_X, RETROK_x }, + { KEY_C, RETROK_c }, + { KEY_V, RETROK_v }, + { KEY_B, RETROK_b }, + { KEY_N, RETROK_n }, + { KEY_M, RETROK_m }, + { KEY_COMMA, RETROK_COMMA }, + { KEY_DOT, RETROK_PERIOD }, + { KEY_SLASH, RETROK_SLASH }, + { KEY_RIGHTSHIFT, RETROK_RSHIFT }, + { KEY_KPASTERISK, RETROK_KP_MULTIPLY }, + { KEY_LEFTALT, RETROK_LALT }, + { KEY_SPACE, RETROK_SPACE }, + { KEY_CAPSLOCK, RETROK_CAPSLOCK }, + { KEY_F1, RETROK_F1 }, + { KEY_F2, RETROK_F2 }, + { KEY_F3, RETROK_F3 }, + { KEY_F4, RETROK_F4 }, + { KEY_F5, RETROK_F5 }, + { KEY_F6, RETROK_F6 }, + { KEY_F7, RETROK_F7 }, + { KEY_F8, RETROK_F8 }, + { KEY_F9, RETROK_F9 }, + { KEY_F10, RETROK_F10 }, + { KEY_NUMLOCK, RETROK_NUMLOCK }, + { KEY_SCROLLLOCK, RETROK_SCROLLOCK }, + { KEY_KP7, RETROK_KP7 }, + { KEY_KP8, RETROK_KP8 }, + { KEY_KP9, RETROK_KP9 }, + { KEY_KPMINUS, RETROK_KP_MINUS }, + { KEY_KP4, RETROK_KP4 }, + { KEY_KP5, RETROK_KP5 }, + { KEY_KP6, RETROK_KP6 }, + { KEY_KPPLUS, RETROK_KP_PLUS }, + { KEY_KP1, RETROK_KP1 }, + { KEY_KP2, RETROK_KP2 }, + { KEY_KP3, RETROK_KP3 }, + { KEY_KP0, RETROK_KP0 }, + { KEY_KPDOT, RETROK_KP_PERIOD }, + + { KEY_F11, RETROK_F11 }, + { KEY_F12, RETROK_F12 }, + + { KEY_KPENTER, RETROK_KP_ENTER }, + { KEY_RIGHTCTRL, RETROK_RCTRL }, + { KEY_KPSLASH, RETROK_KP_DIVIDE }, + { KEY_SYSRQ, RETROK_PRINT }, + { KEY_RIGHTALT, RETROK_RALT }, + + { KEY_HOME, RETROK_HOME }, + { KEY_UP, RETROK_UP }, + { KEY_PAGEUP, RETROK_PAGEUP }, + { KEY_LEFT, RETROK_LEFT }, + { KEY_RIGHT, RETROK_RIGHT }, + { KEY_END, RETROK_END }, + { KEY_DOWN, RETROK_DOWN }, + { KEY_PAGEDOWN, RETROK_PAGEDOWN }, + { KEY_INSERT, RETROK_INSERT }, + { KEY_DELETE, RETROK_DELETE }, + + { KEY_PAUSE, RETROK_PAUSE }, + { 0, RETROK_UNKNOWN }, +}; +#endif + static enum retro_key rarch_keysym_lut[RETROK_LAST]; void input_init_keyboard_lut(const struct rarch_key_map *map) diff --git a/input/input_common.h b/input/input_common.h index 3ce49342e9..f23412ffcc 100644 --- a/input/input_common.h +++ b/input/input_common.h @@ -113,6 +113,7 @@ extern const struct rarch_key_map rarch_key_map_x11[]; extern const struct rarch_key_map rarch_key_map_sdl[]; extern const struct rarch_key_map rarch_key_map_dinput[]; extern const struct rarch_key_map rarch_key_map_rwebinput[]; +extern const struct rarch_key_map rarch_key_map_linux[]; void input_init_keyboard_lut(const struct rarch_key_map *map); enum retro_key input_translate_keysym_to_rk(unsigned sym); diff --git a/input/linuxraw_input.c b/input/linuxraw_input.c index dac339062e..340c598208 100644 --- a/input/linuxraw_input.c +++ b/input/linuxraw_input.c @@ -35,129 +35,6 @@ typedef struct linuxraw_input } linuxraw_input_t; -struct key_bind -{ - uint8_t x; - enum retro_key sk; -}; - -static unsigned keysym_lut[RETROK_LAST]; -static const struct key_bind lut_binds[] = { - { KEY_ESC, RETROK_ESCAPE }, - { KEY_1, RETROK_1 }, - { KEY_2, RETROK_2 }, - { KEY_3, RETROK_3}, - { KEY_4, RETROK_4 }, - { KEY_5, RETROK_5 }, - { KEY_6, RETROK_6 }, - { KEY_7, RETROK_7 }, - { KEY_8, RETROK_8 }, - { KEY_9, RETROK_9 }, - { KEY_0, RETROK_0 }, - { KEY_MINUS, RETROK_MINUS }, - { KEY_EQUAL, RETROK_EQUALS }, - { KEY_BACKSPACE, RETROK_BACKSPACE }, - { KEY_TAB, RETROK_TAB }, - { KEY_Q, RETROK_q }, - { KEY_W, RETROK_w }, - { KEY_E, RETROK_e }, - { KEY_R, RETROK_r }, - { KEY_T, RETROK_t }, - { KEY_Y, RETROK_y }, - { KEY_U, RETROK_u }, - { KEY_I, RETROK_i }, - { KEY_O, RETROK_o }, - { KEY_P, RETROK_p }, - { KEY_LEFTBRACE, RETROK_LEFTBRACKET }, - { KEY_RIGHTBRACE, RETROK_RIGHTBRACKET }, - { KEY_ENTER, RETROK_RETURN }, - { KEY_LEFTCTRL, RETROK_LCTRL }, - { KEY_A, RETROK_a }, - { KEY_S, RETROK_s }, - { KEY_D, RETROK_d }, - { KEY_F, RETROK_f }, - { KEY_G, RETROK_g }, - { KEY_H, RETROK_h }, - { KEY_J, RETROK_j }, - { KEY_K, RETROK_k }, - { KEY_L, RETROK_l }, - { KEY_SEMICOLON, RETROK_SEMICOLON }, - { KEY_APOSTROPHE, RETROK_QUOTE }, - { KEY_GRAVE, RETROK_BACKQUOTE }, - { KEY_LEFTSHIFT, RETROK_LSHIFT }, - { KEY_BACKSLASH, RETROK_BACKSLASH }, - { KEY_Z, RETROK_z }, - { KEY_X, RETROK_x }, - { KEY_C, RETROK_c }, - { KEY_V, RETROK_v }, - { KEY_B, RETROK_b }, - { KEY_N, RETROK_n }, - { KEY_M, RETROK_m }, - { KEY_COMMA, RETROK_COMMA }, - { KEY_DOT, RETROK_PERIOD }, - { KEY_SLASH, RETROK_SLASH }, - { KEY_RIGHTSHIFT, RETROK_RSHIFT }, - { KEY_KPASTERISK, RETROK_KP_MULTIPLY }, - { KEY_LEFTALT, RETROK_LALT }, - { KEY_SPACE, RETROK_SPACE }, - { KEY_CAPSLOCK, RETROK_CAPSLOCK }, - { KEY_F1, RETROK_F1 }, - { KEY_F2, RETROK_F2 }, - { KEY_F3, RETROK_F3 }, - { KEY_F4, RETROK_F4 }, - { KEY_F5, RETROK_F5 }, - { KEY_F6, RETROK_F6 }, - { KEY_F7, RETROK_F7 }, - { KEY_F8, RETROK_F8 }, - { KEY_F9, RETROK_F9 }, - { KEY_F10, RETROK_F10 }, - { KEY_NUMLOCK, RETROK_NUMLOCK }, - { KEY_SCROLLLOCK, RETROK_SCROLLOCK }, - { KEY_KP7, RETROK_KP7 }, - { KEY_KP8, RETROK_KP8 }, - { KEY_KP9, RETROK_KP9 }, - { KEY_KPMINUS, RETROK_KP_MINUS }, - { KEY_KP4, RETROK_KP4 }, - { KEY_KP5, RETROK_KP5 }, - { KEY_KP6, RETROK_KP6 }, - { KEY_KPPLUS, RETROK_KP_PLUS }, - { KEY_KP1, RETROK_KP1 }, - { KEY_KP2, RETROK_KP2 }, - { KEY_KP3, RETROK_KP3 }, - { KEY_KP0, RETROK_KP0 }, - { KEY_KPDOT, RETROK_KP_PERIOD }, - - { KEY_F11, RETROK_F11 }, - { KEY_F12, RETROK_F12 }, - - { KEY_KPENTER, RETROK_KP_ENTER }, - { KEY_RIGHTCTRL, RETROK_RCTRL }, - { KEY_KPSLASH, RETROK_KP_DIVIDE }, - { KEY_SYSRQ, RETROK_PRINT }, - { KEY_RIGHTALT, RETROK_RALT }, - - { KEY_HOME, RETROK_HOME }, - { KEY_UP, RETROK_UP }, - { KEY_PAGEUP, RETROK_PAGEUP }, - { KEY_LEFT, RETROK_LEFT }, - { KEY_RIGHT, RETROK_RIGHT }, - { KEY_END, RETROK_END }, - { KEY_DOWN, RETROK_DOWN }, - { KEY_PAGEDOWN, RETROK_PAGEDOWN }, - { KEY_INSERT, RETROK_INSERT }, - { KEY_DELETE, RETROK_DELETE }, - - { KEY_PAUSE, RETROK_PAUSE }, -}; - -static void init_lut(void) -{ - unsigned i; - memset(keysym_lut, 0, sizeof(keysym_lut)); - for (i = 0; i < ARRAY_SIZE(lut_binds); i++) - keysym_lut[lut_binds[i].sk] = lut_binds[i].x; -} - static void linuxraw_resetKbmd(void) { if (oldKbmd != 0xffff) @@ -232,7 +109,7 @@ static void *linuxraw_input_init(void) atexit(linuxraw_resetKbmd); linuxraw->joypad = input_joypad_init_driver(g_settings.input.joypad_driver); - init_lut(); + input_init_keyboard_lut(rarch_key_map_linux); driver.stdin_claimed = true; // We need to disable use of stdin command interface if stdin is supposed to be used for input. return linuxraw; @@ -240,7 +117,8 @@ static void *linuxraw_input_init(void) static bool linuxraw_key_pressed(linuxraw_input_t *linuxraw, int key) { - return linuxraw->state[keysym_lut[key]]; + unsigned sym = input_translate_rk_to_keysym((enum retro_key)key); + return linuxraw->state[sym]; } static bool linuxraw_is_pressed(linuxraw_input_t *linuxraw, const struct retro_keybind *binds, unsigned id) @@ -346,6 +224,7 @@ static void linuxraw_input_poll(void *data) static uint64_t linuxraw_get_capabilities(void *data) { + (void)data; uint64_t caps = 0; caps |= (1 << RETRO_DEVICE_JOYPAD); diff --git a/input/udev_input.c b/input/udev_input.c new file mode 100644 index 0000000000..122cee7d20 --- /dev/null +++ b/input/udev_input.c @@ -0,0 +1,550 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2013 - Hans-Kristian Arntzen + * + * RetroArch 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 Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch 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 RetroArch. + * If not, see . + */ + +#include "input_common.h" +#include "../general.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef struct udev_input udev_input_t; +typedef void (*device_poll_cb)(udev_input_t *udev, int fd); + +struct input_device +{ + int fd; + dev_t dev; + device_poll_cb poll_cb; + char devnode[PATH_MAX]; +}; + +struct udev_input +{ + struct udev *udev; + struct udev_monitor *monitor; + + const rarch_joypad_driver_t *joypad; + uint8_t key_state[(KEY_MAX + 7) / 8]; + + int epfd; + struct input_device **devices; + unsigned num_devices; + + int16_t mouse_x; + int16_t mouse_y; + bool mouse_l, mouse_r, mouse_m; +}; + +static inline bool get_bit(const uint8_t *buf, unsigned bit) +{ + return buf[bit >> 3] & (1 << (bit & 7)); +} + +static inline void clear_bit(uint8_t *buf, unsigned bit) +{ + buf[bit >> 3] &= ~(1 << (bit & 7)); +} + +static inline void set_bit(uint8_t *buf, unsigned bit) +{ + buf[bit >> 3] |= 1 << (bit & 7); +} + +static void udev_poll_keyboard(udev_input_t *udev, int fd) +{ + int i, len; + struct input_event events[32]; + while ((len = read(fd, events, sizeof(events))) > 0) + { + len /= sizeof(*events); + for (i = 0; i < len; i++) + { + switch (events[i].type) + { + case EV_KEY: + if (events[i].value) + set_bit(udev->key_state, events[i].code); + else + clear_bit(udev->key_state, events[i].code); + break; + + default: + break; + } + } + } +} + +static void udev_poll_mouse(udev_input_t *udev, int fd) +{ + int i, len; + struct input_event events[32]; + while ((len = read(fd, events, sizeof(events))) > 0) + { + len /= sizeof(*events); + for (i = 0; i < len; i++) + { + switch (events[i].type) + { + case EV_KEY: + switch (events[i].code) + { + case BTN_LEFT: + udev->mouse_l = events[i].value; + break; + + case BTN_RIGHT: + udev->mouse_r = events[i].value; + break; + + case BTN_MIDDLE: + udev->mouse_m = events[i].value; + break; + + default: + break; + } + break; + + case EV_REL: + switch (events[i].code) + { + case REL_X: + udev->mouse_x += events[i].value; + break; + + case REL_Y: + udev->mouse_y += events[i].value; + break; + + default: + break; + } + break; + + default: + break; + } + } + } +} + +static bool hotplug_available(udev_input_t *udev) +{ + if (!udev->monitor) + return false; + + struct pollfd fds = {0}; + fds.fd = udev_monitor_get_fd(udev->monitor); + fds.events = POLLIN; + return (poll(&fds, 1, 0) == 1) && (fds.revents & POLLIN); +} + +static bool add_device(udev_input_t *udev, const char *devnode, device_poll_cb cb) +{ + struct stat st; + if (stat(devnode, &st) < 0) + return false; + +#if 0 + // If we have added this device already, skip it (could have aliases in /dev/input). + unsigned i; + for (i = 0; i < udev->num_devices; i++) + if (st.st_dev == udev->devices[i]->dev) + return true; +#endif + + int fd = open(devnode, O_RDONLY | O_NONBLOCK); + if (fd < 0) + return false; + + struct input_device *device = (struct input_device*)calloc(1, sizeof(*device)); + if (!device) + { + close(fd); + return false; + } + + device->fd = fd; + device->dev = st.st_dev; + device->poll_cb = cb; + + strlcpy(device->devnode, devnode, sizeof(device->devnode)); + + struct input_device **tmp = (struct input_device**)realloc(udev->devices, + (udev->num_devices + 1) * sizeof(*udev->devices)); + + if (!tmp) + { + close(fd); + free(device); + return false; + } + + tmp[udev->num_devices++] = device; + udev->devices = tmp; + + struct epoll_event event = {0}; + event.events = EPOLLIN; + event.data.ptr = device; + if (epoll_ctl(udev->epfd, EPOLL_CTL_ADD, fd, &event) < 0) // Shouldn't happen, but just check it. + RARCH_ERR("Failed to add FD (%d) to epoll list (%s).\n", fd, strerror(errno)); + + return true; +} + +static void remove_device(udev_input_t *udev, const char *devnode) +{ + unsigned i; + for (i = 0; i < udev->num_devices; i++) + { + if (!strcmp(devnode, udev->devices[i]->devnode)) + { + close(udev->devices[i]->fd); + free(udev->devices[i]); + memmove(udev->devices + i, udev->devices + i + 1, + (udev->num_devices - (i + 1)) * sizeof(*udev->devices)); + udev->num_devices--; + } + } +} + +static void handle_hotplug(udev_input_t *udev) +{ + struct udev_device *dev = udev_monitor_receive_device(udev->monitor); + if (!dev) + return; + + const char *val_keyboard = udev_device_get_property_value(dev, "ID_INPUT_KEYBOARD"); + const char *val_mouse = udev_device_get_property_value(dev, "ID_INPUT_MOUSE"); + const char *action = udev_device_get_action(dev); + const char *devnode = udev_device_get_devnode(dev); + + bool is_keyboard = val_keyboard && !strcmp(val_keyboard, "1") && devnode; + bool is_mouse = val_mouse && !strcmp(val_mouse, "1") && devnode; + + if (!is_keyboard && !is_mouse) + goto end; + + if (!strcmp(action, "add")) + { + RARCH_LOG("[udev]: Hotplug add %s: %s.\n", is_keyboard ? "keyboard" : "mouse", devnode); + device_poll_cb cb = is_keyboard ? udev_poll_keyboard : udev_poll_mouse; + add_device(udev, devnode, cb); + } + else if (!strcmp(action, "remove")) + { + RARCH_LOG("[udev]: Hotplug remove %s: %s.\n", is_keyboard ? "keyboard" : "mouse", devnode); + remove_device(udev, devnode); + } + +end: + udev_device_unref(dev); +} + +static void udev_input_poll(void *data) +{ + udev_input_t *udev = (udev_input_t*)data; + udev->mouse_x = udev->mouse_y = 0; + + while (hotplug_available(udev)) + handle_hotplug(udev); + + int i; + struct epoll_event events[32]; + int ret = epoll_wait(udev->epfd, events, ARRAY_SIZE(events), 0); + + for (i = 0; i < ret; i++) + { + if (events[i].events & EPOLLIN) + { + struct input_device *device = (struct input_device*)events[i].data.ptr; + device->poll_cb(udev, device->fd); + } + } + + input_joypad_poll(udev->joypad); +} + +static int16_t udev_mouse_state(udev_input_t *udev, unsigned id) +{ + switch (id) + { + case RETRO_DEVICE_ID_MOUSE_X: + return udev->mouse_x; + case RETRO_DEVICE_ID_MOUSE_Y: + return udev->mouse_y; + case RETRO_DEVICE_ID_MOUSE_LEFT: + return udev->mouse_l; + case RETRO_DEVICE_ID_MOUSE_RIGHT: + return udev->mouse_r; + default: + return 0; + } +} + +static int16_t udev_lightgun_state(udev_input_t *udev, unsigned id) +{ + switch (id) + { + case RETRO_DEVICE_ID_LIGHTGUN_X: + return udev->mouse_x; + case RETRO_DEVICE_ID_LIGHTGUN_Y: + return udev->mouse_y; + case RETRO_DEVICE_ID_LIGHTGUN_TRIGGER: + return udev->mouse_l; + case RETRO_DEVICE_ID_LIGHTGUN_CURSOR: + return udev->mouse_m; + case RETRO_DEVICE_ID_LIGHTGUN_TURBO: + return udev->mouse_r; + case RETRO_DEVICE_ID_LIGHTGUN_START: + return udev->mouse_m && udev->mouse_r; + case RETRO_DEVICE_ID_LIGHTGUN_PAUSE: + return udev->mouse_m && udev->mouse_l; + default: + return 0; + } +} + +static bool udev_is_pressed(udev_input_t *udev, const struct retro_keybind *binds, unsigned id) +{ + if (id < RARCH_BIND_LIST_END) + { + const struct retro_keybind *bind = &binds[id]; + return bind->valid && get_bit(udev->key_state, input_translate_rk_to_keysym(binds[id].key)); + } + else + return false; +} + +static int16_t udev_analog_pressed(udev_input_t *udev, + const struct retro_keybind *binds, unsigned index, unsigned id) +{ + unsigned id_minus = 0; + unsigned id_plus = 0; + input_conv_analog_id_to_bind_id(index, id, &id_minus, &id_plus); + + int16_t pressed_minus = udev_is_pressed(udev, + binds, id_minus) ? -0x7fff : 0; + int16_t pressed_plus = udev_is_pressed(udev, + binds, id_plus) ? 0x7fff : 0; + return pressed_plus + pressed_minus; +} + +static int16_t udev_input_state(void *data, const struct retro_keybind **binds, + unsigned port, unsigned device, unsigned index, unsigned id) +{ + udev_input_t *udev = (udev_input_t*)data; + int16_t ret; + + switch (device) + { + case RETRO_DEVICE_JOYPAD: + return udev_is_pressed(udev, binds[port], id) || + input_joypad_pressed(udev->joypad, port, binds[port], id); + + case RETRO_DEVICE_ANALOG: + ret = udev_analog_pressed(udev, binds[port], index, id); + if (!ret) + ret = input_joypad_analog(udev->joypad, port, index, id, binds[port]); + return ret; + + case RETRO_DEVICE_KEYBOARD: + return id < RETROK_LAST && get_bit(udev->key_state, input_translate_rk_to_keysym(id)); + + case RETRO_DEVICE_MOUSE: + return udev_mouse_state(udev, id); + + case RETRO_DEVICE_LIGHTGUN: + return udev_lightgun_state(udev, id); + + default: + return 0; + } +} + +static bool udev_bind_button_pressed(void *data, int key) +{ + udev_input_t *udev = (udev_input_t*)data; + return udev_is_pressed(udev, g_settings.input.binds[0], key) || + input_joypad_pressed(udev->joypad, 0, g_settings.input.binds[0], key); +} + +static void udev_input_free(void *data) +{ + if (!data) + return; + + unsigned i; + udev_input_t *udev = (udev_input_t*)data; + if (udev->joypad) + udev->joypad->destroy(); + + if (udev->epfd >= 0) + close(udev->epfd); + + for (i = 0; i < udev->num_devices; i++) + { + close(udev->devices[i]->fd); + free(udev->devices[i]); + } + free(udev->devices); + + if (udev->monitor) + udev_monitor_unref(udev->monitor); + if (udev->udev) + udev_unref(udev->udev); + + free(udev); +} + +static bool open_devices(udev_input_t *udev, const char *type, device_poll_cb cb) +{ + struct udev_list_entry *devs; + struct udev_list_entry *item; + + struct udev_enumerate *enumerate = udev_enumerate_new(udev->udev); + if (!enumerate) + return false; + + udev_enumerate_add_match_property(enumerate, type, "1"); + udev_enumerate_scan_devices(enumerate); + devs = udev_enumerate_get_list_entry(enumerate); + for (item = devs; item; item = udev_list_entry_get_next(item)) + { + const char *name = udev_list_entry_get_name(item); + struct udev_device *dev = udev_device_new_from_syspath(udev->udev, name); + const char *devnode = udev_device_get_devnode(dev); + + int fd = devnode ? open(devnode, O_RDONLY | O_NONBLOCK) : -1; + + if (devnode) + { + RARCH_LOG("[udev] Adding device %s as type %s.\n", devnode, type); + if (!add_device(udev, devnode, cb)) + RARCH_ERR("[udev] Failed to open device: %s (%s).\n", devnode, strerror(errno)); + } + + udev_device_unref(dev); + } + + udev_enumerate_unref(enumerate); + return true; +} + +static void *udev_input_init(void) +{ + udev_input_t *udev = (udev_input_t*)calloc(1, sizeof(*udev)); + if (!udev) + return NULL; + + udev->udev = udev_new(); + if (!udev->udev) + { + RARCH_ERR("Failed to create udev handle.\n"); + goto error; + } + + udev->monitor = udev_monitor_new_from_netlink(udev->udev, "udev"); + if (udev->monitor) + { + udev_monitor_filter_add_match_subsystem_devtype(udev->monitor, "input", NULL); + udev_monitor_enable_receiving(udev->monitor); + } + + udev->epfd = epoll_create(32); + if (udev->epfd < 0) + { + RARCH_ERR("Failed to create epoll FD.\n"); + goto error; + } + + if (!open_devices(udev, "ID_INPUT_KEYBOARD", udev_poll_keyboard)) + { + RARCH_ERR("Failed to open keyboard.\n"); + goto error; + } + + if (!open_devices(udev, "ID_INPUT_MOUSE", udev_poll_mouse)) + { + RARCH_ERR("Failed to open mouse.\n"); + goto error; + } + + udev->joypad = input_joypad_init_driver(g_settings.input.joypad_driver); + input_init_keyboard_lut(rarch_key_map_linux); + return udev; + +error: + udev_input_free(udev); + return NULL; +} + +static uint64_t udev_input_get_capabilities(void *data) +{ + (void)data; + + return + (1 << RETRO_DEVICE_JOYPAD) | + (1 << RETRO_DEVICE_ANALOG) | + (1 << RETRO_DEVICE_KEYBOARD) | + (1 << RETRO_DEVICE_MOUSE) | + (1 << RETRO_DEVICE_LIGHTGUN); +} + +static void udev_input_grab_mouse(void *data, bool state) +{ + // Dummy for now. Might be useful in the future. + (void)data; + (void)state; +} + +static bool udev_input_set_rumble(void *data, unsigned port, enum retro_rumble_effect effect, uint16_t strength) +{ + udev_input_t *udev = (udev_input_t*)data; + return input_joypad_set_rumble(udev->joypad, port, effect, strength); +} + +static const rarch_joypad_driver_t *udev_input_get_joypad_driver(void *data) +{ + udev_input_t *udev = (udev_input_t*)data; + return udev->joypad; +} + +const input_driver_t input_udev = { + udev_input_init, + udev_input_poll, + udev_input_state, + udev_bind_button_pressed, + udev_input_free, + NULL, + NULL, + udev_input_get_capabilities, + "udev", + udev_input_grab_mouse, + udev_input_set_rumble, + udev_input_get_joypad_driver, +}; + diff --git a/input/udev_joypad.c b/input/udev_joypad.c index 64399c9cd9..27a5f8972f 100644 --- a/input/udev_joypad.c +++ b/input/udev_joypad.c @@ -253,77 +253,6 @@ static void udev_joypad_poll(void) (((1UL << ((nr) % (sizeof(long) * CHAR_BIT))) & ((addr)[(nr) / (sizeof(long) * CHAR_BIT)])) != 0) #define NBITS(x) ((((x) - 1) / (sizeof(long) * CHAR_BIT)) + 1) -#if 0 -static void test_initial_rumble(int fd, const char *path) -{ - // Check for rumble features. - unsigned long ffbit[NBITS(FF_MAX)] = {0}; - if (ioctl(fd, EVIOCGBIT(EV_FF, sizeof(ffbit)), ffbit) >= 0) - { - if (test_bit(FF_RUMBLE, ffbit)) - RARCH_LOG("[udev]: Pad (%s) supports force feedback.\n", - path); - - int effects; - if (ioctl(fd, EVIOCGEFFECTS, &effects) >= 0) - RARCH_LOG("[udev]: Pad (%s) supports %d force feedback effects.\n", path, effects); - - if (effects >= 2) - { - struct ff_effect effect; - bool support_ff[2]; - int effects[2] = {-1, -1}; - - // Strong rumble. - memset(&effect, 0, sizeof(effect)); - effect.type = FF_RUMBLE; - effect.id = -1; - effect.u.rumble.strong_magnitude = 0x8000; - effect.u.rumble.weak_magnitude = 0; - support_ff[0] = ioctl(fd, EVIOCSFF, &effect) == 0; - if (support_ff[0]) - { - RARCH_LOG("[udev]: (%s) supports \"strong\" rumble effect (id %d).\n", - path, effect.id); - effects[RETRO_RUMBLE_STRONG] = effect.id; // Gets updated by ioctl(). - } - - // Weak rumble. - memset(&effect, 0, sizeof(effect)); - effect.type = FF_RUMBLE; - effect.id = -1; - effect.u.rumble.strong_magnitude = 0; - effect.u.rumble.weak_magnitude = 0xc000; - support_ff[1] = ioctl(fd, EVIOCSFF, &effect) == 0; - if (support_ff[1]) - { - RARCH_LOG("[udev]: Pad (%s) supports \"weak\" rumble effect (id %d).\n", - path, effect.id); - effects[RETRO_RUMBLE_WEAK] = effect.id; - } - - struct input_event play; - memset(&play, 0, sizeof(play)); - play.type = EV_FF; - play.code = effects[0]; - play.value = true; - write(fd, &play, sizeof(play)); - rarch_sleep(500); - play.value = false; - write(fd, &play, sizeof(play)); - rarch_sleep(500); - play.code = effects[1]; - play.value = true; - write(fd, &play, sizeof(play)); - rarch_sleep(500); - play.value = false; - write(fd, &play, sizeof(play)); - } - } - -} -#endif - static int open_joystick(const char *path) { int fd = open(path, O_RDWR | O_NONBLOCK); @@ -490,10 +419,6 @@ static void check_device(const char *path, bool hotplugged) #else (void)hotplugged; #endif - -#if 0 - test_initial_rumble(fd, path); -#endif } else {