From bbb12a7560b0f8e44ef3881e11e60c9db6e8449d Mon Sep 17 00:00:00 2001 From: Jordan Woyak Date: Mon, 19 Oct 2020 10:21:28 -0500 Subject: [PATCH] Linux/XInput2: Fix keys being stuck pressed on focus loss. --- .../ControllerInterface/Xlib/XInput2.cpp | 79 ++++++++++++------- .../ControllerInterface/Xlib/XInput2.h | 11 ++- 2 files changed, 57 insertions(+), 33 deletions(-) diff --git a/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.cpp b/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.cpp index 9ec1c2be35..dac0c085fa 100644 --- a/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.cpp +++ b/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.cpp @@ -97,11 +97,11 @@ void PopulateDevices(void* const hwnd) // Apply the event mask to the device and all its slaves. Only used in the // constructor. Remember, each KeyboardMouse has its own copy of the event // stream, which is how multiple event masks can "coexist." -void KeyboardMouse::SelectEventsForDevice(Window window, XIEventMask* mask, int deviceid) +void KeyboardMouse::SelectEventsForDevice(XIEventMask* mask, int deviceid) { // Set the event mask for the master device. mask->deviceid = deviceid; - XISelectEvents(m_display, window, mask, 1); + XISelectEvents(m_display, DefaultRootWindow(m_display), mask, 1); // Query all the master device's slaves and set the same event mask for // those too. There are two reasons we want to do this. For mouse devices, @@ -109,20 +109,19 @@ void KeyboardMouse::SelectEventsForDevice(Window window, XIEventMask* mask, int // devices) emit those. For keyboard devices, selecting slaves avoids // dealing with key focus. - XIDeviceInfo* all_slaves; - XIDeviceInfo* current_slave; int num_slaves; - - all_slaves = XIQueryDevice(m_display, XIAllDevices, &num_slaves); + XIDeviceInfo* const all_slaves = XIQueryDevice(m_display, XIAllDevices, &num_slaves); for (int i = 0; i < num_slaves; i++) { - current_slave = &all_slaves[i]; - if ((current_slave->use != XISlavePointer && current_slave->use != XISlaveKeyboard) || - current_slave->attachment != deviceid) + XIDeviceInfo* const slave = &all_slaves[i]; + if ((slave->use != XISlavePointer && slave->use != XISlaveKeyboard) || + slave->attachment != deviceid) + { continue; - mask->deviceid = current_slave->deviceid; - XISelectEvents(m_display, window, mask, 1); + } + mask->deviceid = slave->deviceid; + XISelectEvents(m_display, DefaultRootWindow(m_display), mask, 1); } XIFreeDeviceInfo(all_slaves); @@ -138,34 +137,44 @@ KeyboardMouse::KeyboardMouse(Window window, int opcode, int pointer, int keyboar // "context." m_display = XOpenDisplay(nullptr); - int min_keycode, max_keycode; - XDisplayKeycodes(m_display, &min_keycode, &max_keycode); - - int unused; // should always be 1 - XIDeviceInfo* pointer_device = XIQueryDevice(m_display, pointer_deviceid, &unused); + // should always be 1 + int unused; + XIDeviceInfo* const pointer_device = XIQueryDevice(m_display, pointer_deviceid, &unused); name = std::string(pointer_device->name); XIFreeDeviceInfo(pointer_device); - XIEventMask mask; - unsigned char mask_buf[(XI_LASTEVENT + 7) / 8]; + { + unsigned char mask_buf[(XI_LASTEVENT + 7) / 8] = {}; + XISetMask(mask_buf, XI_ButtonPress); + XISetMask(mask_buf, XI_ButtonRelease); + XISetMask(mask_buf, XI_RawMotion); - mask.mask_len = sizeof(mask_buf); - mask.mask = mask_buf; - memset(mask_buf, 0, sizeof(mask_buf)); + XIEventMask mask; + mask.mask = mask_buf; + mask.mask_len = sizeof(mask_buf); - XISetMask(mask_buf, XI_ButtonPress); - XISetMask(mask_buf, XI_ButtonRelease); - XISetMask(mask_buf, XI_RawMotion); - XISetMask(mask_buf, XI_KeyPress); - XISetMask(mask_buf, XI_KeyRelease); + SelectEventsForDevice(&mask, pointer_deviceid); + } - SelectEventsForDevice(DefaultRootWindow(m_display), &mask, pointer_deviceid); - SelectEventsForDevice(DefaultRootWindow(m_display), &mask, keyboard_deviceid); + { + unsigned char mask_buf[(XI_LASTEVENT + 7) / 8] = {}; + XISetMask(mask_buf, XI_KeyPress); + XISetMask(mask_buf, XI_KeyRelease); + XISetMask(mask_buf, XI_FocusOut); + + XIEventMask mask; + mask.mask = mask_buf; + mask.mask_len = sizeof(mask_buf); + + SelectEventsForDevice(&mask, keyboard_deviceid); + } // Keyboard Keys + int min_keycode, max_keycode; + XDisplayKeycodes(m_display, &min_keycode, &max_keycode); for (int i = min_keycode; i <= max_keycode; ++i) { - Key* temp_key = new Key(m_display, i, m_state.keyboard); + Key* const temp_key = new Key(m_display, i, m_state.keyboard.data()); if (temp_key->m_keyname.length()) AddInput(temp_key); else @@ -284,6 +293,10 @@ void KeyboardMouse::UpdateInput() delta_y += delta_delta; } break; + case XI_FocusOut: + // Clear keyboard state on FocusOut as we will not be receiving KeyRelease events. + m_state.keyboard.fill(0); + break; } XFreeEventData(m_display, &event.xcookie); @@ -300,6 +313,14 @@ void KeyboardMouse::UpdateInput() // Get the absolute position of the mouse pointer if (mouse_moved) UpdateCursor(); + + // KeyRelease and FocusOut events are sometimes not received. + // Cycling Alt-Tab and landing on the same window results in a stuck "Alt" key. + // Unpressed keys are released here. + std::array keyboard; + XQueryKeymap(m_display, keyboard.data()); + for (size_t i = 0; i != keyboard.size(); ++i) + m_state.keyboard[i] &= keyboard[i]; } std::string KeyboardMouse::GetName() const diff --git a/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.h b/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.h index af452725fc..a9442b6332 100644 --- a/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.h +++ b/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.h @@ -6,6 +6,8 @@ #pragma once +#include + extern "C" { #include #include @@ -24,7 +26,7 @@ class KeyboardMouse : public Core::Device private: struct State { - char keyboard[32]; + std::array keyboard; unsigned int buttons; Common::Vec2 cursor; Common::Vec2 axis; @@ -90,7 +92,7 @@ private: }; private: - void SelectEventsForDevice(Window window, XIEventMask* mask, int deviceid); + void SelectEventsForDevice(XIEventMask* mask, int deviceid); void UpdateCursor(); public: @@ -106,8 +108,9 @@ private: Window m_window; Display* m_display; State m_state{}; - int xi_opcode; - const int pointer_deviceid, keyboard_deviceid; + const int xi_opcode; + const int pointer_deviceid; + const int keyboard_deviceid; std::string name; }; } // namespace ciface::XInput2