From e5d0b09df53d474e713969923d541abff4ddf3e1 Mon Sep 17 00:00:00 2001 From: Themaister Date: Wed, 25 Sep 2013 19:32:18 +0200 Subject: [PATCH 01/10] Start adding Linux Udev joypad input. --- Makefile | 21 +- input/input_common.c | 3 + input/input_common.h | 1 + input/udev_joypad.c | 481 +++++++++++++++++++++++++++++++++++++++++++ qb/config.libs.sh | 4 +- 5 files changed, 505 insertions(+), 5 deletions(-) create mode 100644 input/udev_joypad.c diff --git a/Makefile b/Makefile index 492c13e03a..aef29240d3 100644 --- a/Makefile +++ b/Makefile @@ -182,6 +182,7 @@ endif ifeq ($(HAVE_SDL), 1) OBJ += gfx/sdl_gfx.o input/sdl_input.o input/sdl_joypad.o audio/sdl_audio.o JOYCONFIG_OBJ += input/sdl_joypad.o + JOYCONFIG_LIBS += $(SDL_LIBS) DEFINES += $(SDL_CFLAGS) $(BSD_LOCAL_INC) LIBS += $(SDL_LIBS) endif @@ -316,11 +317,19 @@ ifeq ($(HAVE_PYTHON), 1) OBJ += gfx/py_state/py_state.o endif +ifeq ($(HAVE_UDEV), 1) + DEFINES += $(UDEV_CFLAGS) + LIBS += $(UDEV_LIBS) + JOYCONFIG_LIBS += $(UDEV_LIBS) + OBJ += input/udev_joypad.o + JOYCONFIG_OBJ += tools/udev_joypad.o +endif + ifeq ($(HAVE_NEON),1) - OBJ += audio/sinc_neon.o + OBJ += audio/sinc_neon.o # When compiled without this, tries to attempt to compile sinc lerp, # which will error out - DEFINES += -DSINC_LOWER_QUALITY -DHAVE_NEON + DEFINES += -DSINC_LOWER_QUALITY -DHAVE_NEON endif OBJ += audio/utils.o @@ -376,9 +385,9 @@ retroarch: $(OBJ) tools/retroarch-joyconfig: $(JOYCONFIG_OBJ) @$(if $(Q), $(shell echo echo LD $@),) ifeq ($(CXX_BUILD), 1) - $(Q)$(CXX) -o $@ $(JOYCONFIG_OBJ) $(SDL_LIBS) $(LDFLAGS) $(LIBRARY_DIRS) + $(Q)$(CXX) -o $@ $(JOYCONFIG_OBJ) $(JOYCONFIG_LIBS) $(LDFLAGS) $(LIBRARY_DIRS) else - $(Q)$(CC) -o $@ $(JOYCONFIG_OBJ) $(SDL_LIBS) $(LDFLAGS) $(LIBRARY_DIRS) + $(Q)$(CC) -o $@ $(JOYCONFIG_OBJ) $(JOYCONFIG_LIBS) $(LDFLAGS) $(LIBRARY_DIRS) endif tools/retrolaunch/retrolaunch: $(RETROLAUNCH_OBJ) @@ -393,6 +402,10 @@ tools/linuxraw_joypad.o: input/linuxraw_joypad.c @$(if $(Q), $(shell echo echo CC $<),) $(Q)$(CC) $(CFLAGS) $(DEFINES) -DIS_JOYCONFIG -c -o $@ $< +tools/udev_joypad.o: input/udev_joypad.c + @$(if $(Q), $(shell echo echo CC $<),) + $(Q)$(CC) $(CFLAGS) $(DEFINES) -DIS_JOYCONFIG -c -o $@ $< + tools/input_common_launch.o: input/input_common.c @$(if $(Q), $(shell echo echo CC $<),) $(Q)$(CC) $(CFLAGS) $(DEFINES) -DIS_RETROLAUNCH -c -o $@ $< diff --git a/input/input_common.c b/input/input_common.c index 0e7418bfb8..2e2e7c2615 100644 --- a/input/input_common.c +++ b/input/input_common.c @@ -48,6 +48,9 @@ static const rarch_joypad_driver_t *joypad_drivers[] = { #ifdef HAVE_DINPUT &dinput_joypad, #endif +#ifdef HAVE_UDEV + &udev_joypad, +#endif #if defined(__linux) && !defined(ANDROID) &linuxraw_joypad, #endif diff --git a/input/input_common.h b/input/input_common.h index 96d641dbdc..ef0becf5bf 100644 --- a/input/input_common.h +++ b/input/input_common.h @@ -93,6 +93,7 @@ const char *input_joypad_name(const rarch_joypad_driver_t *driver, unsigned joyp extern const rarch_joypad_driver_t dinput_joypad; extern const rarch_joypad_driver_t linuxraw_joypad; +extern const rarch_joypad_driver_t udev_joypad; extern const rarch_joypad_driver_t winxinput_joypad; // Named as such to avoid confusion with xb1/360 port code extern const rarch_joypad_driver_t sdl_joypad; diff --git a/input/udev_joypad.c b/input/udev_joypad.c new file mode 100644 index 0000000000..c0bd5bde57 --- /dev/null +++ b/input/udev_joypad.c @@ -0,0 +1,481 @@ +/* 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 + +// Udev/Evdev Linux joypad driver. +// More complex and extremely low level, +// but only Linux driver which can support joypad rumble. +// Uses udev for device detection + hotplug. +// +// Code adapted from SDL 2.0's implementation. + +#define NUM_BUTTONS 32 +#define NUM_AXES 32 +#define NUM_HATS 4 + +struct udev_joypad +{ + int fd; + dev_t device; + + // Input state polled + bool buttons[NUM_BUTTONS]; + int16_t axes[NUM_AXES]; + uint16_t hats[NUM_HATS]; + + // Maps keycodes -> button/axes + uint8_t button_bind[KEY_MAX]; + uint8_t axes_bind[ABS_MAX]; + struct input_absinfo absinfo[NUM_AXES]; + + char ident[256]; +}; + +static struct udev *g_udev; +static struct udev_monitor *g_udev_mon; +static struct udev_joypad g_pads[MAX_PLAYERS]; + +/* +static bool udev_joypad_init_pad(const char *path, struct udev_joypad *pad) +{ + if (pad->fd >= 0) + return false; + + // Device can have just been created, but not made accessible (yet). + // IN_ATTRIB will signal when permissions change. + if (access(path, R_OK) < 0) + return false; + + pad->fd = open(path, O_RDONLY | O_NONBLOCK); + + *pad->ident = '\0'; + if (pad->fd >= 0) + { + if (ioctl(pad->fd, JSIOCGNAME(sizeof(g_settings.input.device_names[0])), pad->ident) >= 0) + { + RARCH_LOG("[Joypad]: Found pad: %s on %s.\n", pad->ident, path); + + if (g_hotplug) + { + char msg[512]; + snprintf(msg, sizeof(msg), "Joypad #%u (%s) connected.", (unsigned)(pad - g_pads), pad->ident); +#ifndef IS_JOYCONFIG + msg_queue_push(g_extern.msg_queue, msg, 0, 60); +#endif + } + } + + else + RARCH_ERR("[Joypad]: Didn't find ident of %s.\n", path); + + struct epoll_event event; + event.events = EPOLLIN; + event.data.ptr = pad; + epoll_ctl(g_epoll, EPOLL_CTL_ADD, pad->fd, &event); + return true; + } + else + { + RARCH_ERR("[Joypad]: Failed to open pad %s (error: %s).\n", path, strerror(errno)); + return false; + } +} + +static void handle_plugged_pad(void) +{ + size_t event_size = sizeof(struct inotify_event) + NAME_MAX + 1; + uint8_t *event_buf = (uint8_t*)calloc(1, event_size); + if (!event_buf) + return; + + int rc; + while ((rc = read(g_notify, event_buf, event_size)) >= 0) + { + struct inotify_event *event = NULL; + // Can read multiple events in one read() call. + for (int i = 0; i < rc; i += event->len + sizeof(struct inotify_event)) + { + event = (struct inotify_event*)&event_buf[i]; + + if (strstr(event->name, "js") != event->name) + continue; + + unsigned index = strtoul(event->name + 2, NULL, 0); + if (index >= MAX_PLAYERS) + continue; + + if (event->mask & IN_DELETE) + { + if (g_pads[index].fd >= 0) + { + if (g_hotplug) + { + char msg[512]; + snprintf(msg, sizeof(msg), "Joypad #%u (%s) disconnected.", index, g_pads[index].ident); +#ifndef IS_JOYCONFIG + msg_queue_push(g_extern.msg_queue, msg, 0, 60); +#endif + } + + RARCH_LOG("[Joypad]: Joypad %s disconnected.\n", g_pads[index].ident); + close(g_pads[index].fd); + memset(g_pads[index].buttons, 0, sizeof(g_pads[index].buttons)); + memset(g_pads[index].axes, 0, sizeof(g_pads[index].axes)); + g_pads[index].fd = -1; + *g_pads[index].ident = '\0'; + + input_config_autoconfigure_joypad(index, NULL, NULL); + } + } + // Sometimes, device will be created before acess to it is established. + else if (event->mask & (IN_CREATE | IN_ATTRIB)) + { + char path[PATH_MAX]; + snprintf(path, sizeof(path), "/dev/input/%s", event->name); + bool ret = udev_joypad_init_pad(path, &g_pads[index]); + + if (*g_pads[index].ident && ret) + input_config_autoconfigure_joypad(index, g_pads[index].ident, "udev"); + } + } + } + + free(event_buf); +} +*/ + +static void handle_hat(struct udev_joypad *pad, + unsigned hat, unsigned axis, int value) +{ +} + +static int16_t compute_axis(const struct input_absinfo *info, int value) +{ + return 0; +} + +static void poll_pad(unsigned i) +{ + struct udev_joypad *pad = &g_pads[i]; + if (pad->fd < 0) + return; + + int len; + struct input_event events[32]; + + while ((len = read(pad->fd, events, sizeof(events))) > 0) + { + len /= sizeof(*events); + for (int i = 0; i < len; i++) + { + int code = events[i].code; + switch (events[i].type) + { + case EV_KEY: + if (code >= BTN_MISC) + pad->buttons[pad->button_bind[code]] = events[i].value; + break; + + case EV_ABS: + if (code >= ABS_MISC) + break; + + switch (code) + { + case ABS_HAT0X: + case ABS_HAT0Y: + case ABS_HAT1X: + case ABS_HAT1Y: + case ABS_HAT2X: + case ABS_HAT2Y: + case ABS_HAT3X: + case ABS_HAT3Y: + { + code -= ABS_HAT0X; + handle_hat(pad, code / 2, code % 2, events[i].value); + break; + } + + default: + { + unsigned axis = pad->axes_bind[code]; + pad->axes[axis] = compute_axis(&pad->absinfo[axis], events[i].value); + break; + } + } + break; + + default: + break; + } + } + } +} + +static void udev_joypad_poll(void) +{ + for (unsigned i = 0; i < MAX_PLAYERS; i++) + poll_pad(i); +} + +#define test_bit(nr, addr) \ + (((1UL << ((nr) % (sizeof(long) * CHAR_BIT))) & ((addr)[(nr) / (sizeof(long) * CHAR_BIT)])) != 0) +#define NBITS(x) ((((x) - 1) / (sizeof(long) * CHAR_BIT)) + 1) + +static int open_joystick(const char *path) +{ + int fd = open(path, O_RDONLY | O_NONBLOCK); + if (fd < 0) + return fd; + + unsigned long evbit[NBITS(EV_MAX)] = {0}; + unsigned long keybit[NBITS(KEY_MAX)] = {0}; + unsigned long absbit[NBITS(ABS_MAX)] = {0}; + + if ((ioctl(fd, EVIOCGBIT(0, sizeof(evbit)), evbit) < 0) || + (ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keybit)), keybit) < 0) || + (ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(absbit)), absbit) < 0)) + return false; + + // Has to at least support EV_KEY interface. + if (!test_bit(EV_KEY, evbit)) + return false; + + return fd; +} + +static int find_vacant_pad(void) +{ + for (unsigned i = 0; i < MAX_PLAYERS; i++) + if (g_pads[i].fd < 0) + return i; + return -1; +} + +static void free_pad(unsigned pad) +{ + if (g_pads[pad].fd >= 0) + close(g_pads[pad].fd); + memset(&g_pads[pad], 0, sizeof(g_pads[pad])); + g_pads[pad].fd = -1; +} + +static bool add_pad(unsigned i, int fd) +{ + struct udev_joypad *pad = &g_pads[i]; + if (ioctl(fd, EVIOCGNAME(sizeof(pad->ident)), pad->ident) < 0) + return false; + + RARCH_LOG("[udev]: Found pad: %s on.\n", pad->ident); + + struct stat st; + if (fstat(fd, &st) < 0) + return false; + + unsigned long keybit[NBITS(KEY_MAX)] = {0}; + unsigned long absbit[NBITS(ABS_MAX)] = {0}; + + if ((ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keybit)), keybit) < 0) || + (ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(absbit)), absbit) < 0)) + return false; + + // Go through all possible keycodes, check if they are used, + // and map them to button/axes/hat indices. + unsigned buttons = 0; + unsigned axes = 0; + for (int i = BTN_JOYSTICK; i < KEY_MAX && buttons < NUM_BUTTONS; i++) + if (test_bit(i, keybit)) + pad->button_bind[i] = buttons++; + for (int i = BTN_MISC; i < BTN_JOYSTICK; i++) + if (test_bit(i, keybit)) + pad->button_bind[i] = buttons++; + for (int i = 0; i < ABS_MISC && axes < NUM_AXES; i++) + { + // Skip hats for now. + if (i == ABS_HAT0X) + { + i = ABS_HAT3Y; + continue; + } + + if (test_bit(i, absbit)) + { + struct input_absinfo *abs = &pad->absinfo[axes]; + if (ioctl(fd, EVIOCGABS(i), abs) < 0) + continue; + pad->axes_bind[i] = axes++; + } + } + + pad->device = st.st_rdev; + pad->fd = fd; + + return true; +} + +static void check_device(const char *path) +{ + struct stat st; + if (stat(path, &st) < 0) + return; + + for (unsigned i = 0; i < MAX_PLAYERS; i++) + if (st.st_rdev == g_pads[i].device) + return; + + int fd = open_joystick(path); + if (fd < 0) + return; + + int pad = find_vacant_pad(); + if (pad < 0) + { + close(fd); + return; + } + + if (add_pad(pad, fd)) + { + // Autodetect. + } + else + close(fd); +} + +static void udev_joypad_destroy(void) +{ + for (unsigned i = 0; i < MAX_PLAYERS; i++) + free_pad(i); + + if (g_udev_mon) + udev_monitor_unref(g_udev_mon); + g_udev_mon = NULL; + if (g_udev) + udev_unref(g_udev); + g_udev = NULL; +} + +static bool udev_joypad_init(void) +{ + for (unsigned i = 0; i < MAX_PLAYERS; i++) + free_pad(i); + struct udev_list_entry *devs = NULL; + struct udev_list_entry *item = NULL; + + g_udev = udev_new(); + if (!g_udev) + return false; + + g_udev_mon = udev_monitor_new_from_netlink(g_udev, "udev"); + if (g_udev_mon) + { + udev_monitor_filter_add_match_subsystem_devtype(g_udev_mon, "input", NULL); + udev_monitor_enable_receiving(g_udev_mon); + } + + struct udev_enumerate *enumerate = udev_enumerate_new(g_udev); + if (!enumerate) + goto error; + + udev_enumerate_add_match_property(enumerate, "ID_INPUT_JOYSTICK", "1"); + udev_enumerate_scan_devices(enumerate); + devs = udev_enumerate_get_list_entry(enumerate); + for (struct udev_list_entry *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(g_udev, name); + check_device(udev_device_get_devnode(dev)); + udev_device_unref(dev); + } + + udev_enumerate_unref(enumerate); + return true; + +error: + if (enumerate) + udev_enumerate_unref(enumerate); + udev_joypad_destroy(); + return false; +} + +static bool udev_joypad_button(unsigned port, uint16_t joykey) +{ + const struct udev_joypad *pad = &g_pads[port]; + + return joykey < NUM_BUTTONS && pad->buttons[joykey]; +} + +static int16_t udev_joypad_axis(unsigned port, uint32_t joyaxis) +{ + if (joyaxis == AXIS_NONE) + return 0; + + const struct udev_joypad *pad = &g_pads[port]; + + int16_t val = 0; + if (AXIS_NEG_GET(joyaxis) < NUM_AXES) + { + val = pad->axes[AXIS_NEG_GET(joyaxis)]; + if (val > 0) + val = 0; + // Kernel returns values in range [-0x7fff, 0x7fff]. + } + else if (AXIS_POS_GET(joyaxis) < NUM_AXES) + { + val = pad->axes[AXIS_POS_GET(joyaxis)]; + if (val < 0) + val = 0; + } + + return val; +} + +static bool udev_joypad_query_pad(unsigned pad) +{ + return pad < MAX_PLAYERS && g_pads[pad].fd >= 0; +} + +static const char *udev_joypad_name(unsigned pad) +{ + if (pad >= MAX_PLAYERS) + return NULL; + + return *g_pads[pad].ident ? g_pads[pad].ident : NULL; +} + +const rarch_joypad_driver_t udev_joypad = { + udev_joypad_init, + udev_joypad_query_pad, + udev_joypad_destroy, + udev_joypad_button, + udev_joypad_axis, + udev_joypad_poll, + udev_joypad_name, + "udev", +}; + diff --git a/qb/config.libs.sh b/qb/config.libs.sh index e19ac3b923..7c8b81f400 100644 --- a/qb/config.libs.sh +++ b/qb/config.libs.sh @@ -245,6 +245,8 @@ else HAVE_XVIDEO='no' fi +check_pkgconf UDEV libudev + check_lib STRL -lc strlcpy check_pkgconf PYTHON python3 @@ -255,6 +257,6 @@ add_define_make OS "$OS" # Creates config.mk and config.h. add_define_make GLOBAL_CONFIG_DIR "$GLOBAL_CONFIG_DIR" -VARS="RGUI ALSA OSS OSS_BSD OSS_LIB AL RSOUND ROAR JACK COREAUDIO PULSE SDL OPENGL GLES VG EGL KMS GBM DRM DYLIB GETOPT_LONG THREADS CG LIBXML2 SDL_IMAGE ZLIB DYNAMIC FFMPEG AVCODEC AVFORMAT AVUTIL SWSCALE FREETYPE XVIDEO X11 XEXT XF86VM XINERAMA NETPLAY NETWORK_CMD STDIN_CMD COMMAND SOCKET_LEGACY FBO STRL PYTHON FFMPEG_ALLOC_CONTEXT3 FFMPEG_AVCODEC_OPEN2 FFMPEG_AVIO_OPEN FFMPEG_AVFORMAT_WRITE_HEADER FFMPEG_AVFORMAT_NEW_STREAM FFMPEG_AVCODEC_ENCODE_AUDIO2 FFMPEG_AVCODEC_ENCODE_VIDEO2 BSV_MOVIE VIDEOCORE NEON FLOATHARD FLOATSOFTFP" +VARS="RGUI ALSA OSS OSS_BSD OSS_LIB AL RSOUND ROAR JACK COREAUDIO PULSE SDL OPENGL GLES VG EGL KMS GBM DRM DYLIB GETOPT_LONG THREADS CG LIBXML2 SDL_IMAGE ZLIB DYNAMIC FFMPEG AVCODEC AVFORMAT AVUTIL SWSCALE FREETYPE XVIDEO X11 XEXT XF86VM XINERAMA NETPLAY NETWORK_CMD STDIN_CMD COMMAND SOCKET_LEGACY FBO STRL PYTHON FFMPEG_ALLOC_CONTEXT3 FFMPEG_AVCODEC_OPEN2 FFMPEG_AVIO_OPEN FFMPEG_AVFORMAT_WRITE_HEADER FFMPEG_AVFORMAT_NEW_STREAM FFMPEG_AVCODEC_ENCODE_AUDIO2 FFMPEG_AVCODEC_ENCODE_VIDEO2 BSV_MOVIE VIDEOCORE NEON FLOATHARD FLOATSOFTFP UDEV" create_config_make config.mk $VARS create_config_header config.h $VARS From b916c5ace1acdcc7bd387f9f3fe94e5da96c2438 Mon Sep 17 00:00:00 2001 From: Themaister Date: Wed, 25 Sep 2013 21:41:26 +0200 Subject: [PATCH 02/10] More fixups in udev joypads. --- input/linuxraw_joypad.c | 8 +- input/udev_joypad.c | 275 +++++++++++++++++++++------------------- 2 files changed, 147 insertions(+), 136 deletions(-) diff --git a/input/linuxraw_joypad.c b/input/linuxraw_joypad.c index 8a91cda5b2..18f953b209 100644 --- a/input/linuxraw_joypad.c +++ b/input/linuxraw_joypad.c @@ -84,14 +84,14 @@ static bool linuxraw_joypad_init_pad(const char *path, struct linuxraw_joypad *p { RARCH_LOG("[Joypad]: Found pad: %s on %s.\n", pad->ident, path); +#ifndef IS_JOYCONFIG if (g_hotplug) { char msg[512]; snprintf(msg, sizeof(msg), "Joypad #%u (%s) connected.", (unsigned)(pad - g_pads), pad->ident); -#ifndef IS_JOYCONFIG msg_queue_push(g_extern.msg_queue, msg, 0, 60); -#endif } +#endif } else @@ -137,14 +137,14 @@ static void handle_plugged_pad(void) { if (g_pads[index].fd >= 0) { +#ifndef IS_JOYCONFIG if (g_hotplug) { char msg[512]; snprintf(msg, sizeof(msg), "Joypad #%u (%s) disconnected.", index, g_pads[index].ident); -#ifndef IS_JOYCONFIG msg_queue_push(g_extern.msg_queue, msg, 0, 60); -#endif } +#endif RARCH_LOG("[Joypad]: Joypad %s disconnected.\n", g_pads[index].ident); close(g_pads[index].fd); diff --git a/input/udev_joypad.c b/input/udev_joypad.c index c0bd5bde57..d083dd7444 100644 --- a/input/udev_joypad.c +++ b/input/udev_joypad.c @@ -22,12 +22,13 @@ #include #include #include +#include #include #include #include #include -// Udev/Evdev Linux joypad driver. +// Udev/evdev Linux joypad driver. // More complex and extremely low level, // but only Linux driver which can support joypad rumble. // Uses udev for device detection + hotplug. @@ -46,137 +47,31 @@ struct udev_joypad // Input state polled bool buttons[NUM_BUTTONS]; int16_t axes[NUM_AXES]; - uint16_t hats[NUM_HATS]; + int8_t hats[NUM_HATS][2]; // Maps keycodes -> button/axes uint8_t button_bind[KEY_MAX]; uint8_t axes_bind[ABS_MAX]; struct input_absinfo absinfo[NUM_AXES]; - char ident[256]; + char *ident; + char *path; }; static struct udev *g_udev; static struct udev_monitor *g_udev_mon; static struct udev_joypad g_pads[MAX_PLAYERS]; -/* -static bool udev_joypad_init_pad(const char *path, struct udev_joypad *pad) +static inline int16_t compute_axis(const struct input_absinfo *info, int value) { - if (pad->fd >= 0) - return false; - - // Device can have just been created, but not made accessible (yet). - // IN_ATTRIB will signal when permissions change. - if (access(path, R_OK) < 0) - return false; - - pad->fd = open(path, O_RDONLY | O_NONBLOCK); - - *pad->ident = '\0'; - if (pad->fd >= 0) - { - if (ioctl(pad->fd, JSIOCGNAME(sizeof(g_settings.input.device_names[0])), pad->ident) >= 0) - { - RARCH_LOG("[Joypad]: Found pad: %s on %s.\n", pad->ident, path); - - if (g_hotplug) - { - char msg[512]; - snprintf(msg, sizeof(msg), "Joypad #%u (%s) connected.", (unsigned)(pad - g_pads), pad->ident); -#ifndef IS_JOYCONFIG - msg_queue_push(g_extern.msg_queue, msg, 0, 60); -#endif - } - } - - else - RARCH_ERR("[Joypad]: Didn't find ident of %s.\n", path); - - struct epoll_event event; - event.events = EPOLLIN; - event.data.ptr = pad; - epoll_ctl(g_epoll, EPOLL_CTL_ADD, pad->fd, &event); - return true; - } + int range = info->maximum - info->minimum; + int axis = (value - info->minimum) * 0xffffll / range - 0x7fffll; + if (axis > 0x7fff) + return 0x7fff; + else if (axis < -0x7fff) + return -0x7fff; else - { - RARCH_ERR("[Joypad]: Failed to open pad %s (error: %s).\n", path, strerror(errno)); - return false; - } -} - -static void handle_plugged_pad(void) -{ - size_t event_size = sizeof(struct inotify_event) + NAME_MAX + 1; - uint8_t *event_buf = (uint8_t*)calloc(1, event_size); - if (!event_buf) - return; - - int rc; - while ((rc = read(g_notify, event_buf, event_size)) >= 0) - { - struct inotify_event *event = NULL; - // Can read multiple events in one read() call. - for (int i = 0; i < rc; i += event->len + sizeof(struct inotify_event)) - { - event = (struct inotify_event*)&event_buf[i]; - - if (strstr(event->name, "js") != event->name) - continue; - - unsigned index = strtoul(event->name + 2, NULL, 0); - if (index >= MAX_PLAYERS) - continue; - - if (event->mask & IN_DELETE) - { - if (g_pads[index].fd >= 0) - { - if (g_hotplug) - { - char msg[512]; - snprintf(msg, sizeof(msg), "Joypad #%u (%s) disconnected.", index, g_pads[index].ident); -#ifndef IS_JOYCONFIG - msg_queue_push(g_extern.msg_queue, msg, 0, 60); -#endif - } - - RARCH_LOG("[Joypad]: Joypad %s disconnected.\n", g_pads[index].ident); - close(g_pads[index].fd); - memset(g_pads[index].buttons, 0, sizeof(g_pads[index].buttons)); - memset(g_pads[index].axes, 0, sizeof(g_pads[index].axes)); - g_pads[index].fd = -1; - *g_pads[index].ident = '\0'; - - input_config_autoconfigure_joypad(index, NULL, NULL); - } - } - // Sometimes, device will be created before acess to it is established. - else if (event->mask & (IN_CREATE | IN_ATTRIB)) - { - char path[PATH_MAX]; - snprintf(path, sizeof(path), "/dev/input/%s", event->name); - bool ret = udev_joypad_init_pad(path, &g_pads[index]); - - if (*g_pads[index].ident && ret) - input_config_autoconfigure_joypad(index, g_pads[index].ident, "udev"); - } - } - } - - free(event_buf); -} -*/ - -static void handle_hat(struct udev_joypad *pad, - unsigned hat, unsigned axis, int value) -{ -} - -static int16_t compute_axis(const struct input_absinfo *info, int value) -{ - return 0; + return axis; } static void poll_pad(unsigned i) @@ -217,7 +112,7 @@ static void poll_pad(unsigned i) case ABS_HAT3Y: { code -= ABS_HAT0X; - handle_hat(pad, code / 2, code % 2, events[i].value); + pad->hats[code >> 1][code & 1] = events[i].value; break; } @@ -237,8 +132,52 @@ static void poll_pad(unsigned i) } } +static bool hotplug_available(void) +{ + if (!g_udev_mon) + return false; + struct pollfd fds = {0}; + fds.fd = udev_monitor_get_fd(g_udev_mon); + fds.events = POLLIN; + return (poll(&fds, 1, 0) == 1) && (fds.revents & POLLIN); +} + +static void check_device(const char *path, bool hotplugged); +static void remove_device(const char *path); + +static void handle_hotplug(void) +{ + struct udev_device *dev = udev_monitor_receive_device(g_udev_mon); + if (!dev) + return; + + const char *val = udev_device_get_property_value(dev, "ID_INPUT_JOYSTICK"); + const char *action = udev_device_get_action(dev); + const char *devnode = udev_device_get_devnode(dev); + + if (!val || strcmp(val, "1") || !devnode) + goto end; + + if (!strcmp(action, "add")) + { + RARCH_LOG("[udev]: Hotplug add: %s.\n", devnode); + check_device(devnode, true); + } + else if (!strcmp(action, "remove")) + { + RARCH_LOG("[udev]: Hotplug remove: %s.\n", devnode); + remove_device(devnode); + } + +end: + udev_device_unref(dev); +} + static void udev_joypad_poll(void) { + while (hotplug_available()) + handle_hotplug(); + for (unsigned i = 0; i < MAX_PLAYERS; i++) poll_pad(i); } @@ -260,13 +199,17 @@ static int open_joystick(const char *path) if ((ioctl(fd, EVIOCGBIT(0, sizeof(evbit)), evbit) < 0) || (ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keybit)), keybit) < 0) || (ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(absbit)), absbit) < 0)) - return false; + goto error; // Has to at least support EV_KEY interface. if (!test_bit(EV_KEY, evbit)) - return false; + goto error; return fd; + +error: + close(fd); + return -1; } static int find_vacant_pad(void) @@ -281,17 +224,20 @@ static void free_pad(unsigned pad) { if (g_pads[pad].fd >= 0) close(g_pads[pad].fd); + free(g_pads[pad].path); memset(&g_pads[pad], 0, sizeof(g_pads[pad])); g_pads[pad].fd = -1; + + input_config_autoconfigure_joypad(pad, NULL, NULL); } -static bool add_pad(unsigned i, int fd) +static bool add_pad(unsigned i, int fd, const char *path) { struct udev_joypad *pad = &g_pads[i]; - if (ioctl(fd, EVIOCGNAME(sizeof(pad->ident)), pad->ident) < 0) + if (ioctl(fd, EVIOCGNAME(sizeof(g_settings.input.device_names[0])), pad->ident) < 0) return false; - RARCH_LOG("[udev]: Found pad: %s on.\n", pad->ident); + RARCH_LOG("[udev]: Plugged pad: %s on port #%u.\n", pad->ident, i); struct stat st; if (fstat(fd, &st) < 0) @@ -328,25 +274,37 @@ static bool add_pad(unsigned i, int fd) struct input_absinfo *abs = &pad->absinfo[axes]; if (ioctl(fd, EVIOCGABS(i), abs) < 0) continue; - pad->axes_bind[i] = axes++; + if (abs->maximum > abs->minimum) + { + pad->axes[axes] = compute_axis(abs, abs->value); + pad->axes_bind[i] = axes++; + } } } pad->device = st.st_rdev; pad->fd = fd; + pad->path = strdup(path); + if (*pad->ident) + input_config_autoconfigure_joypad(i, pad->ident, "udev"); return true; } -static void check_device(const char *path) +static void check_device(const char *path, bool hotplugged) { struct stat st; if (stat(path, &st) < 0) return; for (unsigned i = 0; i < MAX_PLAYERS; i++) + { if (st.st_rdev == g_pads[i].device) + { + RARCH_LOG("[udev]: Device ID %u is already plugged.\n", (unsigned)st.st_rdev); return; + } + } int fd = open_joystick(path); if (fd < 0) @@ -359,12 +317,43 @@ static void check_device(const char *path) return; } - if (add_pad(pad, fd)) + if (add_pad(pad, fd, path)) { - // Autodetect. +#ifndef IS_JOYCONFIG + if (hotplugged) + { + char msg[512]; + snprintf(msg, sizeof(msg), "Joypad #%u (%s) connected.", pad, path); + msg_queue_push(g_extern.msg_queue, msg, 0, 60); + RARCH_LOG("[udev]: %s\n", msg); + } +#else + (void)hotplugged; +#endif } else + { + RARCH_ERR("[udev]: Failed to add pad: %s.\n", path); close(fd); + } +} + +static void remove_device(const char *path) +{ + for (unsigned i = 0; i < MAX_PLAYERS; i++) + { + if (g_pads[i].path && !strcmp(g_pads[i].path, path)) + { +#ifndef IS_JOYCONFIG + char msg[512]; + snprintf(msg, sizeof(msg), "Joypad #%u (%s) disconnected.", i, g_pads[i].ident); + msg_queue_push(g_extern.msg_queue, msg, 0, 60); + RARCH_LOG("[udev]: %s\n", msg); +#endif + free_pad(i); + break; + } + } } static void udev_joypad_destroy(void) @@ -383,7 +372,11 @@ static void udev_joypad_destroy(void) static bool udev_joypad_init(void) { for (unsigned i = 0; i < MAX_PLAYERS; i++) - free_pad(i); + { + g_pads[i].fd = -1; + g_pads[i].ident = g_settings.input.device_names[i]; + } + struct udev_list_entry *devs = NULL; struct udev_list_entry *item = NULL; @@ -409,7 +402,7 @@ static bool udev_joypad_init(void) { const char *name = udev_list_entry_get_name(item); struct udev_device *dev = udev_device_new_from_syspath(g_udev, name); - check_device(udev_device_get_devnode(dev)); + check_device(udev_device_get_devnode(dev), false); udev_device_unref(dev); } @@ -423,11 +416,30 @@ error: return false; } +static bool udev_joypad_hat(const struct udev_joypad *pad, uint16_t hat) +{ + unsigned h = GET_HAT(hat); + if (h >= NUM_HATS) + return false; + + switch (GET_HAT_DIR(hat)) + { + case HAT_LEFT_MASK: return pad->hats[h][0] < 0; + case HAT_RIGHT_MASK: return pad->hats[h][0] > 0; + case HAT_UP_MASK: return pad->hats[h][1] < 0; + case HAT_DOWN_MASK: return pad->hats[h][1] > 0; + default: return 0; + } +} + static bool udev_joypad_button(unsigned port, uint16_t joykey) { const struct udev_joypad *pad = &g_pads[port]; - return joykey < NUM_BUTTONS && pad->buttons[joykey]; + if (GET_HAT_DIR(joykey)) + return udev_joypad_hat(pad, joykey); + else + return joykey < NUM_BUTTONS && pad->buttons[joykey]; } static int16_t udev_joypad_axis(unsigned port, uint32_t joyaxis) @@ -443,7 +455,6 @@ static int16_t udev_joypad_axis(unsigned port, uint32_t joyaxis) val = pad->axes[AXIS_NEG_GET(joyaxis)]; if (val > 0) val = 0; - // Kernel returns values in range [-0x7fff, 0x7fff]. } else if (AXIS_POS_GET(joyaxis) < NUM_AXES) { From 89fff9d790966170d7693144a7c01be3e9af0d3c Mon Sep 17 00:00:00 2001 From: Themaister Date: Wed, 25 Sep 2013 22:40:34 +0200 Subject: [PATCH 03/10] Add force feedback. --- input/udev_joypad.c | 96 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 94 insertions(+), 2 deletions(-) diff --git a/input/udev_joypad.c b/input/udev_joypad.c index d083dd7444..907088322e 100644 --- a/input/udev_joypad.c +++ b/input/udev_joypad.c @@ -26,7 +26,7 @@ #include #include #include -#include +#include // Udev/evdev Linux joypad driver. // More complex and extremely low level, @@ -54,6 +54,10 @@ struct udev_joypad uint8_t axes_bind[ABS_MAX]; struct input_absinfo absinfo[NUM_AXES]; + int num_effects; + int effects[2]; // [0] - strong, [1] - weak + bool support_ff[2]; + char *ident; char *path; }; @@ -173,6 +177,27 @@ end: udev_device_unref(dev); } +static void udev_set_rumble(unsigned i, unsigned effect, bool state) +{ + struct udev_joypad *pad = &g_pads[i]; + + if (pad->fd < 0) + return; + if (!pad->support_ff[effect]) + return; + + struct input_event play; + memset(&play, 0, sizeof(play)); + play.type = EV_FF; + play.code = pad->effects[effect]; + play.value = state; + if (write(pad->fd, &play, sizeof(play)) < (ssize_t)sizeof(play)) + { + RARCH_ERR("[udev]: Failed to set rumble effect %u on pad %u.\n", + effect, i); + } +} + static void udev_joypad_poll(void) { while (hotplug_available()) @@ -180,6 +205,19 @@ static void udev_joypad_poll(void) for (unsigned i = 0; i < MAX_PLAYERS; i++) poll_pad(i); + +#if 0 // Debug rumble. + static bool old_0; + static bool old_1; + bool new_0 = g_pads[0].buttons[0]; + bool new_1 = g_pads[0].buttons[1]; + if (new_0 != old_0) + udev_set_rumble(0, 0, new_0); + if (new_1 != old_1) + udev_set_rumble(0, 1, new_1); + old_0 = new_0; + old_1 = new_1; +#endif } #define test_bit(nr, addr) \ @@ -188,7 +226,7 @@ static void udev_joypad_poll(void) static int open_joystick(const char *path) { - int fd = open(path, O_RDONLY | O_NONBLOCK); + int fd = open(path, O_RDWR | O_NONBLOCK); if (fd < 0) return fd; @@ -223,7 +261,12 @@ static int find_vacant_pad(void) static void free_pad(unsigned pad) { if (g_pads[pad].fd >= 0) + { + udev_set_rumble(pad, 0, false); + udev_set_rumble(pad, 1, false); close(g_pads[pad].fd); + } + free(g_pads[pad].path); memset(&g_pads[pad], 0, sizeof(g_pads[pad])); g_pads[pad].fd = -1; @@ -288,6 +331,55 @@ static bool add_pad(unsigned i, int fd, const char *path) if (*pad->ident) input_config_autoconfigure_joypad(i, pad->ident, "udev"); + // 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 #%u (%s) supports force feedback.\n", + i, path); + + if (ioctl(fd, EVIOCGEFFECTS, &pad->num_effects) >= 0) + RARCH_LOG("[udev]: Pad #%u (%s) supports %d force feedback effects.\n", i, path, pad->num_effects); + + if (pad->num_effects >= 2) + { + struct ff_effect effect; + + // 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; + effect.replay.length = 20000; + effect.replay.delay = 0; + pad->support_ff[0] = ioctl(fd, EVIOCSFF, &effect) == 0; + if (pad->support_ff[0]) + { + RARCH_LOG("[udev]: Pad #%u (%s) supports \"strong\" rumble effect (id %d).\n", + i, path, effect.id); + pad->effects[0] = 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; + effect.replay.length = 20000; + effect.replay.delay = 0; + pad->support_ff[1] = ioctl(fd, EVIOCSFF, &effect) == 0; + if (pad->support_ff[1]) + { + RARCH_LOG("[udev]: Pad #%u (%s) supports \"weak\" rumble effect (id %d).\n", + i, path, effect.id); + pad->effects[1] = effect.id; // Gets updated by ioctl(). + } + } + } + return true; } From 7855781cd849ac1859c59d877ae13b969afa2dc1 Mon Sep 17 00:00:00 2001 From: Themaister Date: Wed, 25 Sep 2013 22:59:05 +0200 Subject: [PATCH 04/10] Hook up rumble interfaces to input drivers. --- apple/common/apple_joypad.c | 1 + driver.h | 7 +++++++ input/dinput.c | 8 ++++++++ input/input_common.c | 19 ++++++++++++++++--- input/input_common.h | 4 ++++ input/linuxraw_input.c | 10 +++++++++- input/linuxraw_joypad.c | 1 + input/sdl_input.c | 8 ++++++++ input/sdl_joypad.c | 1 + input/udev_joypad.c | 10 +++++++--- input/winxinput_joypad.c | 1 + input/x11_input.c | 7 +++++++ 12 files changed, 70 insertions(+), 7 deletions(-) diff --git a/apple/common/apple_joypad.c b/apple/common/apple_joypad.c index 96ed6812ba..1ae1e247ce 100644 --- a/apple/common/apple_joypad.c +++ b/apple/common/apple_joypad.c @@ -92,6 +92,7 @@ const rarch_joypad_driver_t apple_joypad = { apple_joypad_button, apple_joypad_axis, apple_joypad_poll, + NULL, apple_joypad_name, "apple" }; diff --git a/driver.h b/driver.h index ccd02fc08c..ac6dd1748a 100644 --- a/driver.h +++ b/driver.h @@ -323,6 +323,12 @@ enum keybind_set_id KEYBINDS_ACTION_LAST }; +enum rarch_rumble_effect +{ + RARCH_RUMBLE_STRONG = 0, + RARCH_RUMBLE_WEAK = 1 +}; + typedef struct input_driver { void *(*init)(void); @@ -335,6 +341,7 @@ typedef struct input_driver const char *ident; void (*grab_mouse)(void *data, bool state); + bool (*set_rumble)(void *data, unsigned port, enum rarch_rumble_effect effect, bool state); } input_driver_t; struct rarch_viewport; diff --git a/input/dinput.c b/input/dinput.c index 2639970c28..ddd327fd8c 100644 --- a/input/dinput.c +++ b/input/dinput.c @@ -334,6 +334,12 @@ static void dinput_grab_mouse(void *data, bool state) IDirectInputDevice8_Acquire(di->mouse); } +static bool dinput_set_rumble(void *data, unsigned port, enum rarch_rumble_effect effect, bool state) +{ + struct dinput_input *di = (struct dinput_input*)data; + return input_joypad_set_rumble(di->joypad, port, effect, state); +} + const input_driver_t input_dinput = { dinput_init, dinput_poll, @@ -344,6 +350,7 @@ const input_driver_t input_dinput = { "dinput", dinput_grab_mouse, + dinput_set_rumble, }; // Keep track of which pad indexes are 360 controllers @@ -651,6 +658,7 @@ const rarch_joypad_driver_t dinput_joypad = { dinput_joypad_button, dinput_joypad_axis, dinput_joypad_poll, + NULL, dinput_joypad_name, "dinput", }; diff --git a/input/input_common.c b/input/input_common.c index 2e2e7c2615..02468c0e5f 100644 --- a/input/input_common.c +++ b/input/input_common.c @@ -48,12 +48,12 @@ static const rarch_joypad_driver_t *joypad_drivers[] = { #ifdef HAVE_DINPUT &dinput_joypad, #endif -#ifdef HAVE_UDEV - &udev_joypad, -#endif #if defined(__linux) && !defined(ANDROID) &linuxraw_joypad, #endif +#ifdef HAVE_UDEV + &udev_joypad, +#endif #ifdef HAVE_SDL &sdl_joypad, #endif @@ -106,6 +106,19 @@ const char *input_joypad_name(const rarch_joypad_driver_t *driver, unsigned joyp return driver->name(joypad); } +bool input_joypad_set_rumble(const rarch_joypad_driver_t *driver, + unsigned port, enum rarch_rumble_effect effect, bool state) +{ + if (!driver) + return false; + + int joy_index = g_settings.input.joypad_map[port]; + if (joy_index < 0 || joy_index >= MAX_PLAYERS) + return false; + + return driver->set_rumble(joy_index, effect, state); +} + bool input_joypad_pressed(const rarch_joypad_driver_t *driver, unsigned port, const struct retro_keybind *binds, unsigned key) { diff --git a/input/input_common.h b/input/input_common.h index ef0becf5bf..faba3be263 100644 --- a/input/input_common.h +++ b/input/input_common.h @@ -66,6 +66,7 @@ typedef struct rarch_joypad_driver bool (*button)(unsigned, uint16_t); int16_t (*axis)(unsigned, uint32_t); void (*poll)(void); + bool (*set_rumble)(unsigned, enum rarch_rumble_effect, bool); // Optional const char *(*name)(unsigned); const char *ident; @@ -81,6 +82,9 @@ bool input_joypad_pressed(const rarch_joypad_driver_t *driver, int16_t input_joypad_analog(const rarch_joypad_driver_t *driver, unsigned port, unsigned index, unsigned id, const struct retro_keybind *binds); +bool input_joypad_set_rumble(const rarch_joypad_driver_t *driver, + unsigned port, enum rarch_rumble_effect effect, bool state); + int16_t input_joypad_axis_raw(const rarch_joypad_driver_t *driver, unsigned joypad, unsigned axis); bool input_joypad_button_raw(const rarch_joypad_driver_t *driver, diff --git a/input/linuxraw_input.c b/input/linuxraw_input.c index 64eab0a0ae..90dde34034 100644 --- a/input/linuxraw_input.c +++ b/input/linuxraw_input.c @@ -285,6 +285,12 @@ static void linuxraw_input_free(void *data) free(data); } +static bool linuxraw_set_rumble(void *data, unsigned port, enum rarch_rumble_effect effect, bool state) +{ + linuxraw_input_t *linuxraw = (linuxraw_input_t*)data; + return input_joypad_set_rumble(linuxraw->joypad, port, effect, state); +} + static void linuxraw_input_poll(void *data) { linuxraw_input_t *linuxraw = (linuxraw_input_t*)data; @@ -316,5 +322,7 @@ const input_driver_t input_linuxraw = { linuxraw_bind_button_pressed, linuxraw_input_free, NULL, - "linuxraw" + "linuxraw", + NULL, + linuxraw_set_rumble, }; diff --git a/input/linuxraw_joypad.c b/input/linuxraw_joypad.c index 18f953b209..ecc0544e94 100644 --- a/input/linuxraw_joypad.c +++ b/input/linuxraw_joypad.c @@ -312,6 +312,7 @@ const rarch_joypad_driver_t linuxraw_joypad = { linuxraw_joypad_button, linuxraw_joypad_axis, linuxraw_joypad_poll, + NULL, linuxraw_joypad_name, "linuxraw", }; diff --git a/input/sdl_input.c b/input/sdl_input.c index d438c3f8a5..a75302af10 100644 --- a/input/sdl_input.c +++ b/input/sdl_input.c @@ -214,6 +214,12 @@ static void sdl_input_free(void *data) free(data); } +static bool sdl_set_rumble(void *data, unsigned port, enum rarch_rumble_effect effect, bool state) +{ + sdl_input_t *sdl = (sdl_input_t*)data; + return input_joypad_set_rumble(sdl->joypad, port, effect, state); +} + static void sdl_poll_mouse(sdl_input_t *sdl) { Uint8 btn = SDL_GetRelativeMouseState(&sdl->mouse_x, &sdl->mouse_y); @@ -240,5 +246,7 @@ const input_driver_t input_sdl = { sdl_input_free, NULL, "sdl", + NULL, + sdl_set_rumble, }; diff --git a/input/sdl_joypad.c b/input/sdl_joypad.c index 812936be56..e18ef87e95 100644 --- a/input/sdl_joypad.c +++ b/input/sdl_joypad.c @@ -170,6 +170,7 @@ const rarch_joypad_driver_t sdl_joypad = { sdl_joypad_button, sdl_joypad_axis, sdl_joypad_poll, + NULL, sdl_joypad_name, "sdl", }; diff --git a/input/udev_joypad.c b/input/udev_joypad.c index 907088322e..5295031e70 100644 --- a/input/udev_joypad.c +++ b/input/udev_joypad.c @@ -177,14 +177,14 @@ end: udev_device_unref(dev); } -static void udev_set_rumble(unsigned i, unsigned effect, bool state) +static bool udev_set_rumble(unsigned i, unsigned effect, bool state) { struct udev_joypad *pad = &g_pads[i]; if (pad->fd < 0) - return; + return false; if (!pad->support_ff[effect]) - return; + return false; struct input_event play; memset(&play, 0, sizeof(play)); @@ -195,7 +195,10 @@ static void udev_set_rumble(unsigned i, unsigned effect, bool state) { RARCH_ERR("[udev]: Failed to set rumble effect %u on pad %u.\n", effect, i); + return false; } + + return true; } static void udev_joypad_poll(void) @@ -578,6 +581,7 @@ const rarch_joypad_driver_t udev_joypad = { udev_joypad_button, udev_joypad_axis, udev_joypad_poll, + udev_set_rumble, udev_joypad_name, "udev", }; diff --git a/input/winxinput_joypad.c b/input/winxinput_joypad.c index bc9f419df0..fd19711fb9 100644 --- a/input/winxinput_joypad.c +++ b/input/winxinput_joypad.c @@ -371,6 +371,7 @@ const rarch_joypad_driver_t winxinput_joypad = { winxinput_joypad_button, winxinput_joypad_axis, winxinput_joypad_poll, + NULL, // FIXME: Add rumble. winxinput_joypad_name, "winxinput", }; diff --git a/input/x11_input.c b/input/x11_input.c index 781094f85b..d788e5bdf8 100644 --- a/input/x11_input.c +++ b/input/x11_input.c @@ -269,6 +269,12 @@ static void x_grab_mouse(void *data, bool state) x11->grab_mouse = state; } +static bool x_set_rumble(void *data, unsigned port, enum rarch_rumble_effect effect, bool state) +{ + x11_input_t *x11 = (x11_input_t*)data; + return input_joypad_set_rumble(x11->joypad, port, effect, state); +} + const input_driver_t input_x = { x_input_init, x_input_poll, @@ -278,5 +284,6 @@ const input_driver_t input_x = { NULL, "x", x_grab_mouse, + x_set_rumble, }; From a01ef18f8093949a959e99f078da7991571f026a Mon Sep 17 00:00:00 2001 From: Themaister Date: Wed, 25 Sep 2013 23:21:32 +0200 Subject: [PATCH 05/10] Add RETRO_ENVIRONMENT_GET_RUMBLE_INTERFACE. --- driver.c | 8 ++++++++ driver.h | 11 ++++------- dynamic.c | 9 +++++++++ general.h | 1 + input/dinput.c | 2 +- input/input_common.c | 2 +- input/input_common.h | 4 ++-- input/linuxraw_input.c | 2 +- input/sdl_input.c | 2 +- input/x11_input.c | 2 +- libretro.h | 24 ++++++++++++++++++++++++ 11 files changed, 53 insertions(+), 14 deletions(-) diff --git a/driver.c b/driver.c index 019d5d1e1a..d96275c7fd 100644 --- a/driver.c +++ b/driver.c @@ -309,6 +309,14 @@ void driver_set_nonblock_state(bool nonblock) g_extern.audio_data.nonblock_chunk_size : g_extern.audio_data.block_chunk_size; } +bool driver_set_rumble_state(unsigned port, enum retro_rumble_effect effect, bool enable) +{ + if (driver.input && driver.input_data) + return driver.input->set_rumble(driver.input_data, port, effect, enable); + else + return false; +} + uintptr_t driver_get_current_framebuffer(void) { #ifdef HAVE_FBO diff --git a/driver.h b/driver.h index ac6dd1748a..d7ef4613ef 100644 --- a/driver.h +++ b/driver.h @@ -323,12 +323,6 @@ enum keybind_set_id KEYBINDS_ACTION_LAST }; -enum rarch_rumble_effect -{ - RARCH_RUMBLE_STRONG = 0, - RARCH_RUMBLE_WEAK = 1 -}; - typedef struct input_driver { void *(*init)(void); @@ -341,7 +335,7 @@ typedef struct input_driver const char *ident; void (*grab_mouse)(void *data, bool state); - bool (*set_rumble)(void *data, unsigned port, enum rarch_rumble_effect effect, bool state); + bool (*set_rumble)(void *data, unsigned port, enum retro_rumble_effect effect, bool state); } input_driver_t; struct rarch_viewport; @@ -502,6 +496,9 @@ void driver_set_nonblock_state(bool nonblock); uintptr_t driver_get_current_framebuffer(void); retro_proc_address_t driver_get_proc_address(const char *sym); +// Used by RETRO_ENVIRONMENT_GET_RUMBLE_INTERFACE +bool driver_set_rumble_state(unsigned port, enum retro_rumble_effect effect, bool enable); + extern driver_t driver; //////////////////////////////////////////////// Backends diff --git a/dynamic.c b/dynamic.c index d1b356e067..ece577c181 100644 --- a/dynamic.c +++ b/dynamic.c @@ -756,6 +756,15 @@ bool rarch_environment_cb(unsigned cmd, void *data) break; } + case RETRO_ENVIRONMENT_GET_RUMBLE_INTERFACE: + { + RARCH_LOG("Environ GET_RUMBLE_INTERFACE.\n"); + struct retro_rumble_interface *iface = (struct retro_rumble_interface*)data; + iface->set_rumble_state = driver_set_rumble_state; + break; + } + + // Private extensions for internal use, not part of libretro API. case RETRO_ENVIRONMENT_SET_LIBRETRO_PATH: RARCH_LOG("Environ (Private) SET_LIBRETRO_PATH.\n"); diff --git a/general.h b/general.h index eb0c981526..0a3e9d8d6c 100644 --- a/general.h +++ b/general.h @@ -712,6 +712,7 @@ void rarch_set_fullscreen(bool fullscreen); void rarch_disk_control_set_eject(bool state, bool log); void rarch_disk_control_set_index(unsigned index); void rarch_disk_control_append_image(const char *path); +bool rarch_set_rumble_state(unsigned port, enum retro_rumble_effect effect, bool enable); void rarch_init_autosave(void); void rarch_deinit_autosave(void); void rarch_take_screenshot(void); diff --git a/input/dinput.c b/input/dinput.c index ddd327fd8c..13d62fc8ab 100644 --- a/input/dinput.c +++ b/input/dinput.c @@ -334,7 +334,7 @@ static void dinput_grab_mouse(void *data, bool state) IDirectInputDevice8_Acquire(di->mouse); } -static bool dinput_set_rumble(void *data, unsigned port, enum rarch_rumble_effect effect, bool state) +static bool dinput_set_rumble(void *data, unsigned port, enum retro_rumble_effect effect, bool state) { struct dinput_input *di = (struct dinput_input*)data; return input_joypad_set_rumble(di->joypad, port, effect, state); diff --git a/input/input_common.c b/input/input_common.c index 02468c0e5f..daa69be9cb 100644 --- a/input/input_common.c +++ b/input/input_common.c @@ -107,7 +107,7 @@ const char *input_joypad_name(const rarch_joypad_driver_t *driver, unsigned joyp } bool input_joypad_set_rumble(const rarch_joypad_driver_t *driver, - unsigned port, enum rarch_rumble_effect effect, bool state) + unsigned port, enum retro_rumble_effect effect, bool state) { if (!driver) return false; diff --git a/input/input_common.h b/input/input_common.h index faba3be263..66866b2ba1 100644 --- a/input/input_common.h +++ b/input/input_common.h @@ -66,7 +66,7 @@ typedef struct rarch_joypad_driver bool (*button)(unsigned, uint16_t); int16_t (*axis)(unsigned, uint32_t); void (*poll)(void); - bool (*set_rumble)(unsigned, enum rarch_rumble_effect, bool); // Optional + bool (*set_rumble)(unsigned, enum retro_rumble_effect, bool); // Optional const char *(*name)(unsigned); const char *ident; @@ -83,7 +83,7 @@ int16_t input_joypad_analog(const rarch_joypad_driver_t *driver, unsigned port, unsigned index, unsigned id, const struct retro_keybind *binds); bool input_joypad_set_rumble(const rarch_joypad_driver_t *driver, - unsigned port, enum rarch_rumble_effect effect, bool state); + unsigned port, enum retro_rumble_effect effect, bool state); int16_t input_joypad_axis_raw(const rarch_joypad_driver_t *driver, unsigned joypad, unsigned axis); diff --git a/input/linuxraw_input.c b/input/linuxraw_input.c index 90dde34034..823c95e70f 100644 --- a/input/linuxraw_input.c +++ b/input/linuxraw_input.c @@ -285,7 +285,7 @@ static void linuxraw_input_free(void *data) free(data); } -static bool linuxraw_set_rumble(void *data, unsigned port, enum rarch_rumble_effect effect, bool state) +static bool linuxraw_set_rumble(void *data, unsigned port, enum retro_rumble_effect effect, bool state) { linuxraw_input_t *linuxraw = (linuxraw_input_t*)data; return input_joypad_set_rumble(linuxraw->joypad, port, effect, state); diff --git a/input/sdl_input.c b/input/sdl_input.c index a75302af10..e2c2dcff2d 100644 --- a/input/sdl_input.c +++ b/input/sdl_input.c @@ -214,7 +214,7 @@ static void sdl_input_free(void *data) free(data); } -static bool sdl_set_rumble(void *data, unsigned port, enum rarch_rumble_effect effect, bool state) +static bool sdl_set_rumble(void *data, unsigned port, enum retro_rumble_effect effect, bool state) { sdl_input_t *sdl = (sdl_input_t*)data; return input_joypad_set_rumble(sdl->joypad, port, effect, state); diff --git a/input/x11_input.c b/input/x11_input.c index d788e5bdf8..c1a4ce7977 100644 --- a/input/x11_input.c +++ b/input/x11_input.c @@ -269,7 +269,7 @@ static void x_grab_mouse(void *data, bool state) x11->grab_mouse = state; } -static bool x_set_rumble(void *data, unsigned port, enum rarch_rumble_effect effect, bool state) +static bool x_set_rumble(void *data, unsigned port, enum retro_rumble_effect effect, bool state) { x11_input_t *x11 = (x11_input_t*)data; return input_joypad_set_rumble(x11->joypad, port, effect, state); diff --git a/libretro.h b/libretro.h index 8d23356138..973c9b36dc 100755 --- a/libretro.h +++ b/libretro.h @@ -514,8 +514,32 @@ enum retro_mod // Lets the core know how much time has passed since last invocation of retro_run(). // The frontend can tamper with the timing to fake fast-forward, slow-motion, frame stepping, etc. // In this case the delta time will use the reference value in frame_time_callback.. + // +#define RETRO_ENVIRONMENT_GET_RUMBLE_INTERFACE (23 | RETRO_ENVIRONMENT_EXPERIMENTAL) + // struct retro_rumble_interface * -- + // Gets an interface which is used by a libretro core to set state of rumble motors in controllers. + // A strong and weak motor is supported, and they can be controlled indepedently. +enum retro_rumble_effect +{ + RETRO_RUMBLE_STRONG = 0, + RETRO_RUMBLE_WEAK = 1, + + RETRO_RUMBLE_DUMMY = INT_MAX +}; + +// Sets rumble state for joypad plugged in port 'port'. Rumble effects are controlled independently, +// and setting e.g. strong rumble does not override weak rumble. +// Should only be called when rumble state changes. +// +// Returns true if rumble state request was honored. Calling this before first retro_run() is likely to return false. +typedef bool (*retro_set_rumble_state_t)(unsigned port, enum retro_rumble_effect effect, bool enable); +struct retro_rumble_interface +{ + retro_set_rumble_state_t set_rumble_state; +}; + // Notifies libretro that audio data should be written. typedef void (*retro_audio_callback_t)(void); From 8b1ac3bc9e7d0fad1c7799006626d088288ae797 Mon Sep 17 00:00:00 2001 From: Themaister Date: Wed, 25 Sep 2013 23:58:02 +0200 Subject: [PATCH 06/10] Add rumble tests to libretro-test. --- driver.c | 2 +- input/input_common.c | 2 +- input/udev_joypad.c | 13 ++++++++----- libretro-test/libretro-test.c | 30 ++++++++++++++++++++++++++++++ 4 files changed, 40 insertions(+), 7 deletions(-) diff --git a/driver.c b/driver.c index d96275c7fd..254a782820 100644 --- a/driver.c +++ b/driver.c @@ -311,7 +311,7 @@ void driver_set_nonblock_state(bool nonblock) bool driver_set_rumble_state(unsigned port, enum retro_rumble_effect effect, bool enable) { - if (driver.input && driver.input_data) + if (driver.input && driver.input_data && driver.input->set_rumble) return driver.input->set_rumble(driver.input_data, port, effect, enable); else return false; diff --git a/input/input_common.c b/input/input_common.c index daa69be9cb..3ddf32e3fc 100644 --- a/input/input_common.c +++ b/input/input_common.c @@ -109,7 +109,7 @@ const char *input_joypad_name(const rarch_joypad_driver_t *driver, unsigned joyp bool input_joypad_set_rumble(const rarch_joypad_driver_t *driver, unsigned port, enum retro_rumble_effect effect, bool state) { - if (!driver) + if (!driver || !driver->set_rumble) return false; int joy_index = g_settings.input.joypad_map[port]; diff --git a/input/udev_joypad.c b/input/udev_joypad.c index 5295031e70..aea413d37c 100644 --- a/input/udev_joypad.c +++ b/input/udev_joypad.c @@ -177,8 +177,9 @@ end: udev_device_unref(dev); } -static bool udev_set_rumble(unsigned i, unsigned effect, bool state) +static bool udev_set_rumble(unsigned i, enum retro_rumble_effect effect, bool state) { + fprintf(stderr, "Rumble: Pad %u, Effect %u, State %u.\n", i, (unsigned)effect, (unsigned)state); struct udev_joypad *pad = &g_pads[i]; if (pad->fd < 0) @@ -264,13 +265,11 @@ static int find_vacant_pad(void) static void free_pad(unsigned pad) { if (g_pads[pad].fd >= 0) - { - udev_set_rumble(pad, 0, false); - udev_set_rumble(pad, 1, false); close(g_pads[pad].fd); - } free(g_pads[pad].path); + if (g_pads[pad].ident) + *g_pads[pad].ident = '\0'; memset(&g_pads[pad], 0, sizeof(g_pads[pad])); g_pads[pad].fd = -1; @@ -280,8 +279,12 @@ static void free_pad(unsigned pad) static bool add_pad(unsigned i, int fd, const char *path) { struct udev_joypad *pad = &g_pads[i]; + pad->ident = g_settings.input.device_names[i]; if (ioctl(fd, EVIOCGNAME(sizeof(g_settings.input.device_names[0])), pad->ident) < 0) + { + RARCH_LOG("[udev]: Failed to get pad name.\n"); return false; + } RARCH_LOG("[udev]: Plugged pad: %s on port #%u.\n", pad->ident, i); diff --git a/libretro-test/libretro-test.c b/libretro-test/libretro-test.c index abf5c6c2c5..791818bc2a 100644 --- a/libretro-test/libretro-test.c +++ b/libretro-test/libretro-test.c @@ -61,6 +61,8 @@ static retro_environment_t environ_cb; static retro_input_poll_t input_poll_cb; static retro_input_state_t input_state_cb; +static struct retro_rumble_interface rumble; + void retro_set_environment(retro_environment_t cb) { environ_cb = cb; @@ -173,6 +175,30 @@ static void update_input(void) x_coord = (x_coord + dir_x) & 31; y_coord = (y_coord + dir_y) & 31; + + if (rumble.set_rumble_state) + { + static bool old_start; + static bool old_select; + bool start = input_state_cb(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START); + bool select = input_state_cb(0, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT); + if (old_start != start) + { + fprintf(stderr, "Strong rumble: %s.\n", start ? "ON": "OFF"); + if (!rumble.set_rumble_state(0, RETRO_RUMBLE_STRONG, start)) + fprintf(stderr, "Strong rumble; failed to set state.\n"); + } + + if (old_select != select) + { + fprintf(stderr, "Weak rumble: %s.\n", select ? "ON": "OFF"); + if (!rumble.set_rumble_state(0, RETRO_RUMBLE_WEAK, select)) + fprintf(stderr, "Weak rumble; failed to set state.\n"); + } + + old_start = start; + old_select = select; + } } static void render_checkered(void) @@ -262,6 +288,10 @@ bool retro_load_game(const struct retro_game_info *info) struct retro_keyboard_callback cb = { keyboard_cb }; environ_cb(RETRO_ENVIRONMENT_SET_KEYBOARD_CALLBACK, &cb); + if (environ_cb(RETRO_ENVIRONMENT_GET_RUMBLE_INTERFACE, &rumble)) + fprintf(stderr, "Rumble environment supported.\n"); + else + fprintf(stderr, "Rumble environment not supported.\n"); check_variables(); From 65fb094f30b1e47532b9e2777756a1480a195221 Mon Sep 17 00:00:00 2001 From: Themaister Date: Thu, 26 Sep 2013 00:10:02 +0200 Subject: [PATCH 07/10] Small cleanups. --- input/udev_joypad.c | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/input/udev_joypad.c b/input/udev_joypad.c index aea413d37c..6346ae45ec 100644 --- a/input/udev_joypad.c +++ b/input/udev_joypad.c @@ -179,7 +179,6 @@ end: static bool udev_set_rumble(unsigned i, enum retro_rumble_effect effect, bool state) { - fprintf(stderr, "Rumble: Pad %u, Effect %u, State %u.\n", i, (unsigned)effect, (unsigned)state); struct udev_joypad *pad = &g_pads[i]; if (pad->fd < 0) @@ -209,19 +208,6 @@ static void udev_joypad_poll(void) for (unsigned i = 0; i < MAX_PLAYERS; i++) poll_pad(i); - -#if 0 // Debug rumble. - static bool old_0; - static bool old_1; - bool new_0 = g_pads[0].buttons[0]; - bool new_1 = g_pads[0].buttons[1]; - if (new_0 != old_0) - udev_set_rumble(0, 0, new_0); - if (new_1 != old_1) - udev_set_rumble(0, 1, new_1); - old_0 = new_0; - old_1 = new_1; -#endif } #define test_bit(nr, addr) \ @@ -383,6 +369,9 @@ static bool add_pad(unsigned i, int fd, const char *path) i, path, effect.id); pad->effects[1] = effect.id; // Gets updated by ioctl(). } + + udev_set_rumble(i, RETRO_RUMBLE_STRONG, false); + udev_set_rumble(i, RETRO_RUMBLE_WEAK, false); } } From 3a2e3ce2778c153daa05aaaf82c80040b7d3e91f Mon Sep 17 00:00:00 2001 From: Themaister Date: Thu, 26 Sep 2013 00:49:13 +0200 Subject: [PATCH 08/10] Minor tweaks. --- input/udev_joypad.c | 30 +++++++++++------------------- 1 file changed, 11 insertions(+), 19 deletions(-) diff --git a/input/udev_joypad.c b/input/udev_joypad.c index 6346ae45ec..c1014de752 100644 --- a/input/udev_joypad.c +++ b/input/udev_joypad.c @@ -257,15 +257,15 @@ static void free_pad(unsigned pad) if (g_pads[pad].ident) *g_pads[pad].ident = '\0'; memset(&g_pads[pad], 0, sizeof(g_pads[pad])); - g_pads[pad].fd = -1; + g_pads[pad].fd = -1; + g_pads[pad].ident = g_settings.input.device_names[pad]; input_config_autoconfigure_joypad(pad, NULL, NULL); } static bool add_pad(unsigned i, int fd, const char *path) { struct udev_joypad *pad = &g_pads[i]; - pad->ident = g_settings.input.device_names[i]; if (ioctl(fd, EVIOCGNAME(sizeof(g_settings.input.device_names[0])), pad->ident) < 0) { RARCH_LOG("[udev]: Failed to get pad name.\n"); @@ -344,14 +344,12 @@ static bool add_pad(unsigned i, int fd, const char *path) effect.id = -1; effect.u.rumble.strong_magnitude = 0x8000; effect.u.rumble.weak_magnitude = 0; - effect.replay.length = 20000; - effect.replay.delay = 0; pad->support_ff[0] = ioctl(fd, EVIOCSFF, &effect) == 0; if (pad->support_ff[0]) { RARCH_LOG("[udev]: Pad #%u (%s) supports \"strong\" rumble effect (id %d).\n", i, path, effect.id); - pad->effects[0] = effect.id; // Gets updated by ioctl(). + pad->effects[RETRO_RUMBLE_STRONG] = effect.id; // Gets updated by ioctl(). } // Weak rumble. @@ -360,18 +358,13 @@ static bool add_pad(unsigned i, int fd, const char *path) effect.id = -1; effect.u.rumble.strong_magnitude = 0; effect.u.rumble.weak_magnitude = 0xc000; - effect.replay.length = 20000; - effect.replay.delay = 0; pad->support_ff[1] = ioctl(fd, EVIOCSFF, &effect) == 0; if (pad->support_ff[1]) { RARCH_LOG("[udev]: Pad #%u (%s) supports \"weak\" rumble effect (id %d).\n", i, path, effect.id); - pad->effects[1] = effect.id; // Gets updated by ioctl(). + pad->effects[RETRO_RUMBLE_WEAK] = effect.id; // Gets updated by ioctl(). } - - udev_set_rumble(i, RETRO_RUMBLE_STRONG, false); - udev_set_rumble(i, RETRO_RUMBLE_WEAK, false); } } @@ -393,17 +386,14 @@ static void check_device(const char *path, bool hotplugged) } } + int pad = find_vacant_pad(); + if (pad < 0) + return; + int fd = open_joystick(path); if (fd < 0) return; - int pad = find_vacant_pad(); - if (pad < 0) - { - close(fd); - return; - } - if (add_pad(pad, fd, path)) { #ifndef IS_JOYCONFIG @@ -489,7 +479,9 @@ static bool udev_joypad_init(void) { const char *name = udev_list_entry_get_name(item); struct udev_device *dev = udev_device_new_from_syspath(g_udev, name); - check_device(udev_device_get_devnode(dev), false); + const char *devnode = udev_device_get_devnode(dev); + if (devnode) + check_device(devnode, false); udev_device_unref(dev); } From 0415ccf97e63ed3dadb250ef7ad2836bafcc17ce Mon Sep 17 00:00:00 2001 From: Themaister Date: Thu, 26 Sep 2013 11:20:13 +0200 Subject: [PATCH 09/10] Fixes to force feedback. --- input/udev_joypad.c | 143 +++++++++++++++++++++++++++++++++----------- 1 file changed, 108 insertions(+), 35 deletions(-) diff --git a/input/udev_joypad.c b/input/udev_joypad.c index c1014de752..dd5afb26db 100644 --- a/input/udev_joypad.c +++ b/input/udev_joypad.c @@ -56,7 +56,7 @@ struct udev_joypad int num_effects; int effects[2]; // [0] - strong, [1] - weak - bool support_ff[2]; + bool has_set_ff[2]; char *ident; char *path; @@ -183,9 +183,37 @@ static bool udev_set_rumble(unsigned i, enum retro_rumble_effect effect, bool st if (pad->fd < 0) return false; - if (!pad->support_ff[effect]) + if (pad->num_effects < 2) return false; + // Have to defer the force feedback settings to here. + // For some reason, effects are getting dropped when they're set at init. + // Setting at init seems to work for pads which are hotplugged ... + // + // This approach might be cleaner in the end if we end up supporting configurable force feedback. + if (!pad->has_set_ff[effect]) + { + struct ff_effect e; + memset(&e, 0, sizeof(e)); + e.type = FF_RUMBLE; + e.id = -1; + switch (effect) + { + case RETRO_RUMBLE_STRONG: e.u.rumble.strong_magnitude = 0xc000; break; + case RETRO_RUMBLE_WEAK: e.u.rumble.weak_magnitude = 0xc000; break; + default: return false; + } + + if (ioctl(pad->fd, EVIOCSFF, &e) < 0) + { + RARCH_ERR("Failed to set rumble effect on pad #%u.\n", i); + return false; + } + + pad->has_set_ff[effect] = true; + pad->effects[effect] = e.id; + } + struct input_event play; memset(&play, 0, sizeof(play)); play.type = EV_FF; @@ -214,12 +242,84 @@ 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); if (fd < 0) return fd; + unsigned long evbit[NBITS(EV_MAX)] = {0}; unsigned long keybit[NBITS(KEY_MAX)] = {0}; unsigned long absbit[NBITS(ABS_MAX)] = {0}; @@ -233,6 +333,7 @@ static int open_joystick(const char *path) if (!test_bit(EV_KEY, evbit)) goto error; + return fd; error: @@ -272,6 +373,7 @@ static bool add_pad(unsigned i, int fd, const char *path) return false; } + RARCH_LOG("[udev]: Plugged pad: %s on port #%u.\n", pad->ident, i); struct stat st; @@ -333,39 +435,6 @@ static bool add_pad(unsigned i, int fd, const char *path) if (ioctl(fd, EVIOCGEFFECTS, &pad->num_effects) >= 0) RARCH_LOG("[udev]: Pad #%u (%s) supports %d force feedback effects.\n", i, path, pad->num_effects); - - if (pad->num_effects >= 2) - { - struct ff_effect effect; - - // 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; - pad->support_ff[0] = ioctl(fd, EVIOCSFF, &effect) == 0; - if (pad->support_ff[0]) - { - RARCH_LOG("[udev]: Pad #%u (%s) supports \"strong\" rumble effect (id %d).\n", - i, path, effect.id); - pad->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; - pad->support_ff[1] = ioctl(fd, EVIOCSFF, &effect) == 0; - if (pad->support_ff[1]) - { - RARCH_LOG("[udev]: Pad #%u (%s) supports \"weak\" rumble effect (id %d).\n", - i, path, effect.id); - pad->effects[RETRO_RUMBLE_WEAK] = effect.id; // Gets updated by ioctl(). - } - } } return true; @@ -407,6 +476,10 @@ static void check_device(const char *path, bool hotplugged) #else (void)hotplugged; #endif + +#if 0 + test_initial_rumble(fd, path); +#endif } else { From 0b17db0743b5ef9d34dedfca2712dd6b5fdd2e30 Mon Sep 17 00:00:00 2001 From: Themaister Date: Thu, 26 Sep 2013 11:23:36 +0200 Subject: [PATCH 10/10] Add udev to ./configure --- qb/config.params.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/qb/config.params.sh b/qb/config.params.sh index c71ea1ba00..e45321b40c 100644 --- a/qb/config.params.sh +++ b/qb/config.params.sh @@ -1,6 +1,7 @@ HAVE_RGUI=yes # Disable RGUI HAVE_DYNAMIC=yes # Disable dynamic loading of libretro library HAVE_SDL=auto # SDL support +HAVE_UDEV=auto # Udev/Evdev gamepad support HAVE_LIBRETRO= # libretro library used HAVE_MAN_DIR= # Manpage install directory HAVE_THREADS=auto # Threading support