From c6f9e61d38f674d81ba8786739f9fe38490670d0 Mon Sep 17 00:00:00 2001 From: Jeffrey Bosboom Date: Thu, 13 Apr 2023 20:10:00 -0700 Subject: [PATCH] XInput2: Accept input from keyboards other than the first master XInput2 was created to support multiple pointer/keyboard pairs (often called MPX for multi-pointer X). Dolphin's XInput2 implementation has always supported MPX by creating a KeyboardMouse object per master pointer. Since commit bbb12a7, Dolphin's keyboard state is filtered by the output of XQueryKeymap. As a core X function, XQueryKeymap queries "the" keyboard, which by default is the first master keyboard. As a result, Dolphin will ignore keys pressed on other master keyboards unless the first master is simultaneously pressing the same keys. XInput2 doesn't provide a function to query the keyboard state. There is no XIQueryKeymap and the current state is not a member of the XIKeyClassInfo returned by XIQueryDevice. Instead, XInput2 allows a master pointer to be nominated as "the" pointer on a per-client basis, with "the" keyboard automatically becoming the associated master keyboard. The "documentation" [1] says passing None for the window is only for debugging purposes, but it is documented in the XISetClientPointer man page and seems to be the only way to query keyboards beyond the first. With this commit, Dolphin correctly reads keys from keyboards other than the first master keyboard. To test, use the xinput command-line utility to create a master pointer and reattach a keyboard to the associated master keyboard. [1]: https://who-t.blogspot.com/2009/07/xi2-recipes-part-6.html (the XInput2 developer's blog) --- .../Core/InputCommon/ControllerInterface/Xlib/XInput2.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.cpp b/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.cpp index e9f8d2fff2..07d8e4bfa5 100644 --- a/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.cpp +++ b/Source/Core/InputCommon/ControllerInterface/Xlib/XInput2.cpp @@ -166,6 +166,10 @@ KeyboardMouse::KeyboardMouse(Window window, int opcode, int pointer, int keyboar name = std::string(pointer_device->name); XIFreeDeviceInfo(pointer_device); + // Tell core X functions which keyboard is "the" keyboard for this + // X connection. + XISetClientPointer(m_display, None, pointer_deviceid); + { unsigned char mask_buf[(XI_LASTEVENT + 7) / 8] = {}; XISetMask(mask_buf, XI_ButtonPress); @@ -387,6 +391,10 @@ void KeyboardMouse::UpdateInput() // 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. + // Because we called XISetClientPointer in the constructor, XQueryKeymap + // will return the state of the associated keyboard, even if it isn't the + // first master keyboard. (XInput2 doesn't provide a function to query + // keyboard state.) std::array keyboard; XQueryKeymap(m_display, keyboard.data()); for (size_t i = 0; i != keyboard.size(); ++i)