From b8e6e7a9fe2581963f39ba0757092c2f60dc2cfe Mon Sep 17 00:00:00 2001 From: Shaolin Date: Fri, 27 Apr 2018 20:05:16 -0300 Subject: [PATCH] libobs: Use xcb-xinput when available for events This enables extra mouse buttons to be detected by libobs. This allows up to 29 extra buttons. --- libobs/CMakeLists.txt | 22 +++- libobs/obs-hotkey.h | 2 + libobs/obs-nix.c | 229 ++++++++++++++++++++++++++++++++++++------ libobs/obsconfig.h.in | 1 + 4 files changed, 219 insertions(+), 35 deletions(-) diff --git a/libobs/CMakeLists.txt b/libobs/CMakeLists.txt index 00c0ebceb..bddfa5c22 100644 --- a/libobs/CMakeLists.txt +++ b/libobs/CMakeLists.txt @@ -13,6 +13,13 @@ endif() if(UNIX) if (NOT APPLE) + find_package(X11_XCB REQUIRED) + find_package(XCB OPTIONAL_COMPONENTS XINPUT) + if (XCB_XINPUT_FOUND) + set(USE_XINPUT "1") + else() + set(USE_XINPUT "0") + endif() find_package(PulseAudio) if (NOT "${PULSEAUDIO_LIBRARY}" STREQUAL "") message(STATUS "Found PulseAudio - Audio Monitor enabled") @@ -22,14 +29,13 @@ if(UNIX) endif() else() set(HAVE_PULSEAUDIO "0") + set(USE_XINPUT "0") endif() find_package(DBus QUIET) - if (NOT APPLE) - find_package(X11_XCB REQUIRED) - endif() else() set(HAVE_DBUS "0") set(HAVE_PULSEAUDIO "0") + set(USE_XINPUT "0") endif() find_package(ImageMagick QUIET COMPONENTS MagickCore) @@ -199,6 +205,16 @@ elseif(UNIX) ${libobs_PLATFORM_DEPS} ${X11_XCB_LIBRARIES}) + if(USE_XINPUT) + include_directories( + ${XCB_XINPUT_INCLUDE_DIR}) + add_definitions( + ${XCB_DEFINITIONS}) + set(libobs_PLATFORM_DEPS + ${XCB_XINPUT_LIBRARY} + ${libobs_PLATFORM_DEPS}) + endif() + if(HAVE_PULSEAUDIO) set(libobs_PLATFORM_DEPS ${libobs_PLATFORM_DEPS} diff --git a/libobs/obs-hotkey.h b/libobs/obs-hotkey.h index 0385d6550..6c8c05090 100644 --- a/libobs/obs-hotkey.h +++ b/libobs/obs-hotkey.h @@ -32,6 +32,8 @@ const size_t OBS_INVALID_HOTKEY_ID = (size_t)-1; const size_t OBS_INVALID_HOTKEY_PAIR_ID = (size_t)-1; #endif +#define XINPUT_MOUSE_LEN 33 + enum obs_key { #define OBS_HOTKEY(x) x, #include "obs-hotkeys.h" diff --git a/libobs/obs-nix.c b/libobs/obs-nix.c index 888ca663d..355b832e4 100644 --- a/libobs/obs-nix.c +++ b/libobs/obs-nix.c @@ -16,6 +16,7 @@ along with this program. If not, see . ******************************************************************************/ +#include "obs-internal.h" #if defined(__FreeBSD__) #define _GNU_SOURCE #endif @@ -28,13 +29,13 @@ #include #include #include +#if USE_XINPUT +#include +#endif #include #include #include -#include #include -#include "util/dstr.h" -#include "obs-internal.h" const char *get_module_extension(void) { @@ -356,6 +357,12 @@ struct obs_hotkeys_platform { xcb_keysym_t *keysyms; int num_keysyms; int syms_per_code; + +#if USE_XINPUT + bool pressed[XINPUT_MOUSE_LEN]; + bool update[XINPUT_MOUSE_LEN]; + bool button_pressed[XINPUT_MOUSE_LEN]; +#endif }; #define MOUSE_1 (1<<16) @@ -736,6 +743,54 @@ error1: return error != NULL || reply == NULL; } +static xcb_screen_t *default_screen(obs_hotkeys_platform_t *context, + xcb_connection_t *connection) +{ + int def_screen_idx = XDefaultScreen(context->display); + xcb_screen_iterator_t iter; + + iter = xcb_setup_roots_iterator(xcb_get_setup(connection)); + while (iter.rem) { + if (def_screen_idx-- == 0) + return iter.data; + + xcb_screen_next(&iter); + } + + return NULL; +} + +static inline xcb_window_t root_window(obs_hotkeys_platform_t *context, + xcb_connection_t *connection) +{ + xcb_screen_t *screen = default_screen(context, connection); + if (screen) + return screen->root; + return 0; +} + +#if USE_XINPUT +static inline void registerMouseEvents(struct obs_core_hotkeys *hotkeys) +{ + obs_hotkeys_platform_t *context = hotkeys->platform_context; + xcb_connection_t *connection = XGetXCBConnection( + context->display); + xcb_window_t window = root_window(context, connection); + + struct { + xcb_input_event_mask_t head; + xcb_input_xi_event_mask_t mask; + } mask; + mask.head.deviceid = XCB_INPUT_DEVICE_ALL_MASTER; + mask.head.mask_len = sizeof(mask.mask) / sizeof(uint32_t); + mask.mask = XCB_INPUT_XI_EVENT_MASK_RAW_BUTTON_PRESS | + XCB_INPUT_XI_EVENT_MASK_RAW_BUTTON_RELEASE; + + xcb_input_xi_select_events(connection, window, 1, &mask.head); + xcb_flush(connection); +} +#endif + bool obs_hotkeys_platform_init(struct obs_core_hotkeys *hotkeys) { Display *display = XOpenDisplay(NULL); @@ -745,6 +800,9 @@ bool obs_hotkeys_platform_init(struct obs_core_hotkeys *hotkeys) hotkeys->platform_context = bzalloc(sizeof(obs_hotkeys_platform_t)); hotkeys->platform_context->display = display; +#if USE_XINPUT + registerMouseEvents(hotkeys); +#endif fill_base_keysyms(hotkeys); fill_keycodes(hotkeys); return true; @@ -764,41 +822,147 @@ void obs_hotkeys_platform_free(struct obs_core_hotkeys *hotkeys) hotkeys->platform_context = NULL; } -static xcb_screen_t *default_screen(obs_hotkeys_platform_t *context, - xcb_connection_t *connection) -{ - int def_screen_idx = XDefaultScreen(context->display); - xcb_screen_iterator_t iter; - - iter = xcb_setup_roots_iterator(xcb_get_setup(connection)); - while (iter.rem) { - if (def_screen_idx-- == 0) { - return iter.data; - break; - } - - xcb_screen_next(&iter); - } - - return NULL; -} - -static inline xcb_window_t root_window(obs_hotkeys_platform_t *context, - xcb_connection_t *connection) -{ - xcb_screen_t *screen = default_screen(context, connection); - if (screen) - return screen->root; - return 0; -} - static bool mouse_button_pressed(xcb_connection_t *connection, obs_hotkeys_platform_t *context, obs_key_t key) { + bool ret = false; + +#if USE_XINPUT + memset(context->pressed, 0, XINPUT_MOUSE_LEN); + memset(context->update, 0, XINPUT_MOUSE_LEN); + + xcb_generic_event_t *ev; + while ((ev = xcb_poll_for_event(connection))) { + if ((ev->response_type & ~80) == XCB_GE_GENERIC) { + switch (((xcb_ge_event_t *) ev)->event_type) { + case XCB_INPUT_RAW_BUTTON_PRESS: { + xcb_input_raw_button_press_event_t *mot; + mot = (xcb_input_raw_button_press_event_t *) ev; + if (mot->detail < XINPUT_MOUSE_LEN) { + context->pressed[mot->detail-1] = true; + context->update[mot->detail-1] = true; + } else { + blog(LOG_WARNING, "Unsupported button"); + } + break; + } + case XCB_INPUT_RAW_BUTTON_RELEASE: { + xcb_input_raw_button_release_event_t *mot; + mot = (xcb_input_raw_button_release_event_t *) ev; + if (mot->detail < XINPUT_MOUSE_LEN) + context->update[mot->detail-1] = true; + else + blog(LOG_WARNING, "Unsupported button"); + break; + } + default: + break; + } + } + free(ev); + } + + // Mouse 2 for OBS is Right Click and Mouse 3 is Wheel Click. + // Mouse Wheel axis clicks (xinput mot->detail 4 5 6 7) are ignored. + switch (key) { + case OBS_KEY_MOUSE1: + ret = context->pressed[0] || context->button_pressed[0]; + break; + case OBS_KEY_MOUSE2: + ret = context->pressed[2] || context->button_pressed[2]; + break; + case OBS_KEY_MOUSE3: + ret = context->pressed[1] || context->button_pressed[1]; + break; + case OBS_KEY_MOUSE4: + ret = context->pressed[7] || context->button_pressed[7]; + break; + case OBS_KEY_MOUSE5: + ret = context->pressed[8] || context->button_pressed[8]; + break; + case OBS_KEY_MOUSE6: + ret = context->pressed[9] || context->button_pressed[9]; + break; + case OBS_KEY_MOUSE7: + ret = context->pressed[10] || context->button_pressed[10]; + break; + case OBS_KEY_MOUSE8: + ret = context->pressed[11] || context->button_pressed[11]; + break; + case OBS_KEY_MOUSE9: + ret = context->pressed[12] || context->button_pressed[12]; + break; + case OBS_KEY_MOUSE10: + ret = context->pressed[13] || context->button_pressed[13]; + break; + case OBS_KEY_MOUSE11: + ret = context->pressed[14] || context->button_pressed[14]; + break; + case OBS_KEY_MOUSE12: + ret = context->pressed[15] || context->button_pressed[15]; + break; + case OBS_KEY_MOUSE13: + ret = context->pressed[16] || context->button_pressed[16]; + break; + case OBS_KEY_MOUSE14: + ret = context->pressed[17] || context->button_pressed[17]; + break; + case OBS_KEY_MOUSE15: + ret = context->pressed[18] || context->button_pressed[18]; + break; + case OBS_KEY_MOUSE16: + ret = context->pressed[19] || context->button_pressed[19]; + break; + case OBS_KEY_MOUSE17: + ret = context->pressed[20] || context->button_pressed[20]; + break; + case OBS_KEY_MOUSE18: + ret = context->pressed[21] || context->button_pressed[21]; + break; + case OBS_KEY_MOUSE19: + ret = context->pressed[22] || context->button_pressed[22]; + break; + case OBS_KEY_MOUSE20: + ret = context->pressed[23] || context->button_pressed[23]; + break; + case OBS_KEY_MOUSE21: + ret = context->pressed[24] || context->button_pressed[24]; + break; + case OBS_KEY_MOUSE22: + ret = context->pressed[25] || context->button_pressed[25]; + break; + case OBS_KEY_MOUSE23: + ret = context->pressed[26] || context->button_pressed[26]; + break; + case OBS_KEY_MOUSE24: + ret = context->pressed[27] || context->button_pressed[27]; + break; + case OBS_KEY_MOUSE25: + ret = context->pressed[28] || context->button_pressed[28]; + break; + case OBS_KEY_MOUSE26: + ret = context->pressed[29] || context->button_pressed[29]; + break; + case OBS_KEY_MOUSE27: + ret = context->pressed[30] || context->button_pressed[30]; + break; + case OBS_KEY_MOUSE28: + ret = context->pressed[31] || context->button_pressed[31]; + break; + case OBS_KEY_MOUSE29: + ret = context->pressed[32] || context->button_pressed[32]; + break; + default: + break; + } + + for (int i = 0; i != XINPUT_MOUSE_LEN; i++) + if (context->update[i]) + context->button_pressed[i] = context->pressed[i]; +#else xcb_generic_error_t *error = NULL; xcb_query_pointer_cookie_t qpc; xcb_query_pointer_reply_t *reply; - bool ret = false; qpc = xcb_query_pointer(connection, root_window(context, connection)); reply = xcb_query_pointer_reply(connection, qpc, &error); @@ -818,6 +982,7 @@ static bool mouse_button_pressed(xcb_connection_t *connection, free(reply); free(error); +#endif return ret; } diff --git a/libobs/obsconfig.h.in b/libobs/obsconfig.h.in index b978bdb04..c08fa7321 100644 --- a/libobs/obsconfig.h.in +++ b/libobs/obsconfig.h.in @@ -18,6 +18,7 @@ #define BUILD_CAPTIONS @BUILD_CAPTIONS@ #define HAVE_DBUS @HAVE_DBUS@ #define HAVE_PULSEAUDIO @HAVE_PULSEAUDIO@ +#define USE_XINPUT @USE_XINPUT@ #define LIBOBS_IMAGEMAGICK_DIR_STYLE_6L 6 #define LIBOBS_IMAGEMAGICK_DIR_STYLE_7GE 7 #define LIBOBS_IMAGEMAGICK_DIR_STYLE @LIBOBS_IMAGEMAGICK_DIR_STYLE@