From e0d210bdf0cf1d5f9f328e8120eea99392f797d9 Mon Sep 17 00:00:00 2001 From: Tin Date: Fri, 10 Nov 2023 07:08:02 +0100 Subject: [PATCH] Handle movement through XInput if enabled Cleanup modifiers - This is preparation so that once Wayland adds support for these they're not an alien X11 bitset. - Removed ones that aren't actual modifiers. Tidy up code Signed-off-by: Tin --- src/display-x11.cc | 99 ++++++++++++++++++++-------------------- src/mouse-events.cc | 20 ++++++-- src/mouse-events.h | 109 ++++++++++++++++++++++++++++++++++++++++---- src/x11.cc | 17 ++++--- src/x11.h | 5 +- 5 files changed, 178 insertions(+), 72 deletions(-) diff --git a/src/display-x11.cc b/src/display-x11.cc index 884ea8f8e..5d3841e8f 100644 --- a/src/display-x11.cc +++ b/src/display-x11.cc @@ -7,7 +7,7 @@ * Copyright (C) 2018-2021 François Revol et al. * Copyright (c) 2004, Hannu Saransaari and Lauri Hakkarainen * Copyright (c) 2005-2021 Brenden Matthews, Philip Kovacs, et. al. - * (see AUTHORS) + * (see AUTHORS) * All rights reserved. * * This program is free software: you can redistribute it and/or modify @@ -383,17 +383,36 @@ bool display_output_x11::main_loop_wait(double t) { XNextEvent(display, &ev); #if defined(BUILD_MOUSE_EVENTS) && defined(BUILD_XINPUT) + // no need to check whether these events have been consumed because + // they're global and shouldn't be propagated if (ev.type == GenericEvent && ev.xcookie.extension == window.xi_opcode) { - XGetEventData(display, &ev.xcookie); - if (ev.xcookie.evtype == XI_Motion) { - auto *data = reinterpret_cast(ev.xcookie.data); + if (!XGetEventData(display, &ev.xcookie)) { + NORM_ERR("unable to get XInput event data"); + continue; + } + + auto *data = reinterpret_cast(ev.xcookie.data); + + // the only way to differentiate between a scroll and move event is + // though valuators - move has first 2 set, other axis movements have other. + bool is_cursor_move = data->valuators.mask_len > 2 && + XIMaskIsSet(data->valuators.mask, 0) && XIMaskIsSet(data->valuators.mask, 1); + if (data->evtype == XI_Motion && is_cursor_move) { Window query_result = query_x11_window_at_pos(display, data->root_x, data->root_y); static bool cursor_inside = false; if ((query_result != 0 && query_result == window.window) || ((query_result == window.desktop || query_result == window.root || query_result == 0) && data->root_x >= window.x && data->root_x < (window.x + window.width) && data->root_y >= window.y && data->root_y < (window.y + window.height))) { + // cursor is inside conky + + modifier_state_t mods = x11_modifier_state(data->mods.effective); + llua_mouse_hook(mouse_move_event( + data->root_x - window.x, data->root_y - window.x, + data->root_x, data->root_y, mods + )); + if (!cursor_inside) { llua_mouse_hook(mouse_crossing_event( mouse_event_t::AREA_ENTER, @@ -506,34 +525,23 @@ bool display_output_x11::main_loop_wait(double t) { case ButtonPress: #ifdef BUILD_MOUSE_EVENTS - if (ev.xbutton.button == 4) { - consumed = llua_mouse_hook(mouse_scroll_event( - ev.xbutton.x, ev.xbutton.y, ev.xbutton.x_root, ev.xbutton.y_root, - scroll_direction_t::SCROLL_UP, ev.xbutton.state - )); - } else if (ev.xbutton.button == 5) { - consumed = llua_mouse_hook(mouse_scroll_event( - ev.xbutton.x, ev.xbutton.y, ev.xbutton.x_root, ev.xbutton.y_root, - scroll_direction_t::SCROLL_DOWN, ev.xbutton.state - )); - } else { - mouse_button_t button; - switch (ev.xbutton.button) { - case Button1: - button = mouse_button_t::BUTTON_LEFT; - break; - case Button2: - button = mouse_button_t::BUTTON_RIGHT; - break; - case Button3: - button = mouse_button_t::BUTTON_MIDDLE; - break; + { + modifier_state_t mods = x11_modifier_state(ev.xbutton.state); + if (4 <= ev.xbutton.button && ev.xbutton.button <= 7) { + scroll_direction_t direction = x11_scroll_direction(ev.xbutton.button); + consumed = llua_mouse_hook(mouse_scroll_event( + ev.xbutton.x, ev.xbutton.y, + ev.xbutton.x_root, ev.xbutton.y_root, + direction, mods + )); + } else { + mouse_button_t button = x11_mouse_button_code(ev.xbutton.button); + consumed = llua_mouse_hook(mouse_button_event( + mouse_event_t::MOUSE_PRESS, + ev.xbutton.x, ev.xbutton.y, ev.xbutton.x_root, ev.xbutton.y_root, + button, mods + )); } - consumed = llua_mouse_hook(mouse_button_event( - mouse_event_t::MOUSE_PRESS, - ev.xbutton.x, ev.xbutton.y, ev.xbutton.x_root, ev.xbutton.y_root, - button, ev.xbutton.state - )); } #endif /* BUILD_MOUSE_EVENTS */ if (own_window.get(*state)) { @@ -549,24 +557,14 @@ bool display_output_x11::main_loop_wait(double t) { case ButtonRelease: #ifdef BUILD_MOUSE_EVENTS - /* don't report scrollwheel release events */ - if (ev.xbutton.button != Button4 && ev.xbutton.button != Button5) { - mouse_button_t button; - switch (ev.xbutton.button) { - case Button1: - button = mouse_button_t::BUTTON_LEFT; - break; - case Button2: - button = mouse_button_t::BUTTON_RIGHT; - break; - case Button3: - button = mouse_button_t::BUTTON_MIDDLE; - break; - } + /* don't report scroll release events */ + if (4 > ev.xbutton.button || ev.xbutton.button > 7) { + modifier_state_t mods = x11_modifier_state(ev.xbutton.state); + mouse_button_t button = x11_mouse_button_code(ev.xbutton.button); consumed = llua_mouse_hook(mouse_button_event( mouse_event_t::MOUSE_RELEASE, ev.xbutton.x, ev.xbutton.y, ev.xbutton.x_root, ev.xbutton.y_root, - button, ev.xbutton.state + button, mods )); } #endif /* BUILD_MOUSE_EVENTS */ @@ -585,9 +583,12 @@ bool display_output_x11::main_loop_wait(double t) { can't forward the event without filtering XQueryTree output. */ case MotionNotify: - consumed = llua_mouse_hook(mouse_move_event( - ev.xmotion.x, ev.xmotion.y, ev.xmotion.x_root, ev.xmotion.y_root, ev.xmotion.state - )); + if (window.xi_opcode == 0) { + modifier_state_t mods = x11_modifier_state(ev.xmotion.state); + consumed = llua_mouse_hook(mouse_move_event( + ev.xmotion.x, ev.xmotion.y, ev.xmotion.x_root, ev.xmotion.y_root, mods + )); + } break; case LeaveNotify: case EnterNotify: diff --git a/src/mouse-events.cc b/src/mouse-events.cc index eaca2888d..f164021d9 100644 --- a/src/mouse-events.cc +++ b/src/mouse-events.cc @@ -25,6 +25,7 @@ #include #include #include +#include "logging.h" /* Lua helper functions */ void push_table_value(lua_State *L, std::string key, std::string value) { @@ -62,11 +63,16 @@ void push_bitset(lua_State *L, std::bitset it, for (size_t i = 0; i < N; i++) push_table_value(L, labels[i], it.test(i)); } -const std::array mod_names = { - {"shift", "lock", "control", "mod1", "num_lock", "mod3", "mod4", "mod5", - "mouse_left", "mouse_right", "mouse_middle", "scroll_up", "scroll_down"}}; +const std::array mod_names = {{ + "shift", + "control", + "alt", + "super", + "caps_lock", + "num_lock", +}}; -void push_mods(lua_State *L, std::bitset<13> mods) { +void push_mods(lua_State *L, modifier_state_t mods) { lua_pushstring(L, "mods"); push_bitset(L, mods, mod_names); lua_settable(L, -3); @@ -142,6 +148,12 @@ void push_table_value(lua_State *L, std::string key, mouse_button_t button) { case BUTTON_MIDDLE: lua_pushstring(L, "middle"); break; + case BUTTON_BACK: + lua_pushstring(L, "back"); + break; + case BUTTON_FORWARD: + lua_pushstring(L, "forward"); + break; default: lua_pushnil(L); break; diff --git a/src/mouse-events.h b/src/mouse-events.h index e842a9cc9..341c8c14e 100644 --- a/src/mouse-events.h +++ b/src/mouse-events.h @@ -22,8 +22,16 @@ #define MOUSE_EVENTS_H #include +#include #include +#include "config.h" +#include "logging.h" + +#ifdef BUILD_X11 +#include +#endif /* BUILD_X11 */ + extern "C" { #include } @@ -49,19 +57,53 @@ enum mouse_event_t { // above list if it has other event codes or a standard file containing them. // Left mouse button event code -#define BTN_LEFT 0x110 +#define BTN_LEFT 0x110 // Right mouse button event code -#define BTN_RIGHT 0x111 +#define BTN_RIGHT 0x111 // Middle mouse button event code -#define BTN_MIDDLE 0x112 +#define BTN_MIDDLE 0x112 + +// Back mouse button event code +#define BTN_BACK 0x116 +// Forward mouse button event code +#define BTN_FORWARD 0x115 #endif enum mouse_button_t: uint32_t { BUTTON_LEFT = BTN_LEFT, BUTTON_RIGHT = BTN_RIGHT, BUTTON_MIDDLE = BTN_MIDDLE, + BUTTON_BACK = BTN_BACK, + BUTTON_FORWARD = BTN_FORWARD, }; +#ifdef BUILD_X11 +inline mouse_button_t x11_mouse_button_code(unsigned int x11_mouse_button) { + mouse_button_t button; + switch (x11_mouse_button) { + case Button1: + button = BUTTON_LEFT; + break; + case Button2: + button = BUTTON_MIDDLE; + break; + case Button3: + button = BUTTON_RIGHT; + break; + case 8: + button = BUTTON_BACK; + break; + case 9: + button = BUTTON_FORWARD; + break; + default: + DBGP("X11 button %d is not mapped", x11_mouse_button); + break; + } + return button; +} +#endif /* BUILD_X11 */ + struct mouse_event { mouse_event_t type; // type of event size_t time; // ms since epoch when the event happened @@ -82,35 +124,82 @@ struct mouse_positioned_event : public mouse_event { void push_lua_data(lua_State *L) const; }; +typedef std::bitset<6> modifier_state_t; +enum modifier_key: uint32_t { + MOD_SHIFT = 0, + MOD_CONTROL = 1, + MOD_ALT = 2, + // Windows/MacOS key on most keyboards + MOD_SUPER = 3, + MOD_CAPS_LOCK = 4, + MOD_NUM_LOCK = 5, +}; +std::string modifier_name(modifier_key key); + +#ifdef BUILD_X11 +inline modifier_state_t x11_modifier_state(unsigned int mods) { + modifier_state_t result; + result[MOD_SHIFT] = mods & ShiftMask; + result[MOD_CONTROL] = mods & ControlMask; + result[MOD_ALT] = mods & Mod1Mask; + result[MOD_SUPER] = mods & Mod4Mask; + result[MOD_CAPS_LOCK] = mods & LockMask; + result[MOD_NUM_LOCK] = mods & Mod2Mask; + return result; +} +#endif /* BUILD_X11 */ + struct mouse_move_event : public mouse_positioned_event { - std::bitset<13> mods; // held buttons and modifiers (ctrl, shift, ...) + modifier_state_t mods; // held buttons and modifiers (ctrl, shift, ...) - mouse_move_event(size_t x, size_t y, size_t x_abs, size_t y_abs, std::bitset<13> mods = 0): mouse_positioned_event{mouse_event_t::MOUSE_MOVE, x, y, x_abs, y_abs}, mods(mods) {}; + mouse_move_event(size_t x, size_t y, size_t x_abs, size_t y_abs, modifier_state_t mods = 0): mouse_positioned_event{mouse_event_t::MOUSE_MOVE, x, y, x_abs, y_abs}, mods(mods) {}; void push_lua_data(lua_State *L) const; }; enum scroll_direction_t: uint8_t { - SCROLL_UP = 0, + SCROLL_UNKNOWN = 0, + SCROLL_UP, SCROLL_DOWN, SCROLL_LEFT, SCROLL_RIGHT, }; +#ifdef BUILD_X11 +inline scroll_direction_t x11_scroll_direction(unsigned int x11_mouse_button) { + scroll_direction_t direction = SCROLL_UNKNOWN; + switch (x11_mouse_button) { + case Button4: + direction = SCROLL_UP; + break; + case Button5: + direction = SCROLL_DOWN; + break; + case 6: + direction = SCROLL_LEFT; + break; + case 7: + direction = SCROLL_RIGHT; + break; + } + return direction; +} +#endif /* BUILD_X11 */ + struct mouse_scroll_event : public mouse_positioned_event { - std::bitset<13> mods; // held buttons and modifiers (ctrl, shift, ...) + modifier_state_t mods; // held buttons and modifiers (ctrl, shift, ...) scroll_direction_t direction; - mouse_scroll_event(size_t x, size_t y, size_t x_abs, size_t y_abs, scroll_direction_t direction, std::bitset<13> mods = 0): mouse_positioned_event{mouse_event_t::MOUSE_SCROLL, x, y, x_abs, y_abs}, direction(direction), mods(mods) {}; + mouse_scroll_event(size_t x, size_t y, size_t x_abs, size_t y_abs, scroll_direction_t direction, modifier_state_t mods = 0): mouse_positioned_event{mouse_event_t::MOUSE_SCROLL, x, y, x_abs, y_abs}, direction(direction), mods(mods) {}; void push_lua_data(lua_State *L) const; }; struct mouse_button_event : public mouse_positioned_event { - std::bitset<13> mods; // held buttons and modifiers (ctrl, shift, ...) + modifier_state_t mods; // held buttons and modifiers (ctrl, shift, ...) mouse_button_t button; - mouse_button_event(mouse_event_t type, size_t x, size_t y, size_t x_abs, size_t y_abs, mouse_button_t button, std::bitset<13> mods = 0): mouse_positioned_event{type, x, y, x_abs, y_abs}, button(button), mods(mods) {}; + mouse_button_event(mouse_event_t type, size_t x, size_t y, size_t x_abs, size_t y_abs, mouse_button_t button, modifier_state_t mods = 0): mouse_positioned_event{type, x, y, x_abs, y_abs}, button(button), mods(mods) {}; void push_lua_data(lua_State *L) const; }; diff --git a/src/x11.cc b/src/x11.cc index 3dff77f61..924ee1fd6 100644 --- a/src/x11.cc +++ b/src/x11.cc @@ -28,6 +28,7 @@ */ #include +#include #include #include "common.h" #include "config.h" @@ -578,7 +579,7 @@ void destroy_window() { memset(&window, 0, sizeof(struct conky_x11_window)); } -void x11_init_window(lua::state &l __attribute__((unused)), bool own) { +void x11_init_window(lua::state &l, bool own) { DBGP("enter x11_init_window()"); // own is unused if OWN_WINDOW is not defined (void)own; @@ -951,12 +952,11 @@ void x11_init_window(lua::state &l __attribute__((unused)), bool own) { if (own_window.get(l)) { input_mask |= StructureNotifyMask | ButtonPressMask | ButtonReleaseMask; } -#endif /* OWN_WINDOW */ #ifdef BUILD_MOUSE_EVENTS /* it's not recommended to add event masks to special windows in X; causes a * crash */ - if (own_window_type.get(l) != TYPE_DESKTOP) { - input_mask |= ButtonPressMask | ButtonReleaseMask | PointerMotionMask; + if (own && own_window_type.get(l) != TYPE_DESKTOP) { + input_mask |= ButtonPressMask | ButtonReleaseMask; } bool xinput_ok = false; #ifdef BUILD_XINPUT @@ -974,7 +974,9 @@ void x11_init_window(lua::state &l __attribute__((unused)), bool own) { NORM_ERR("Error: XInput 2.0 is not supported!"); break; } - unsigned char mask_bytes[(XI_LASTEVENT + 7) / 8] = {0}; /* must be zeroed! */ + + const size_t mask_size = (XI_LASTEVENT + 7) / 8; + unsigned char mask_bytes[mask_size] = {0}; /* must be zeroed! */ XISetMask(mask_bytes, XI_Motion); XIEventMask ev_masks[1]; @@ -985,10 +987,11 @@ void x11_init_window(lua::state &l __attribute__((unused)), bool own) { xinput_ok = true; } while (false); #endif /* BUILD_XINPUT */ - if (!xinput_ok && own_window_type.get(l) != TYPE_DESKTOP) { - input_mask |= EnterWindowMask | LeaveWindowMask; + if (!xinput_ok && own && own_window_type.get(l) != TYPE_DESKTOP) { + input_mask |= PointerMotionMask | EnterWindowMask | LeaveWindowMask; } #endif /* BUILD_MOUSE_EVENTS */ +#endif /* OWN_WINDOW */ window.event_mask = input_mask; XSelectInput(display, window.window, input_mask); diff --git a/src/x11.h b/src/x11.h index 116a9285d..362219518 100644 --- a/src/x11.h +++ b/src/x11.h @@ -90,9 +90,10 @@ struct conky_x11_window { #ifdef BUILD_XFT XftDraw *xftdraw; #endif /*BUILD_XFT*/ -#ifdef BUILD_XINPUT +#ifdef BUILD_MOUSE_EVENTS + // Don't feature gate with BUILD_XINPUT; controls fallback. int32_t xi_opcode; -#endif /* BUILD_XINPUT */ +#endif /* BUILD_MOUSE_EVENTS */ int width; int height;