Merge pull request #9160 from jordan-woyak/xinput2-stuck-keys
Linux/XInput2: Fix keys being stuck pressed on focus loss.
This commit is contained in:
commit
97de366b0f
|
@ -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];
|
||||
|
||||
mask.mask_len = sizeof(mask_buf);
|
||||
mask.mask = mask_buf;
|
||||
memset(mask_buf, 0, sizeof(mask_buf));
|
||||
|
||||
{
|
||||
unsigned char mask_buf[(XI_LASTEVENT + 7) / 8] = {};
|
||||
XISetMask(mask_buf, XI_ButtonPress);
|
||||
XISetMask(mask_buf, XI_ButtonRelease);
|
||||
XISetMask(mask_buf, XI_RawMotion);
|
||||
|
||||
XIEventMask mask;
|
||||
mask.mask = mask_buf;
|
||||
mask.mask_len = sizeof(mask_buf);
|
||||
|
||||
SelectEventsForDevice(&mask, pointer_deviceid);
|
||||
}
|
||||
|
||||
{
|
||||
unsigned char mask_buf[(XI_LASTEVENT + 7) / 8] = {};
|
||||
XISetMask(mask_buf, XI_KeyPress);
|
||||
XISetMask(mask_buf, XI_KeyRelease);
|
||||
XISetMask(mask_buf, XI_FocusOut);
|
||||
|
||||
SelectEventsForDevice(DefaultRootWindow(m_display), &mask, pointer_deviceid);
|
||||
SelectEventsForDevice(DefaultRootWindow(m_display), &mask, keyboard_deviceid);
|
||||
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<char, 32> 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
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
|
||||
extern "C" {
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/extensions/XInput2.h>
|
||||
|
@ -24,7 +26,7 @@ class KeyboardMouse : public Core::Device
|
|||
private:
|
||||
struct State
|
||||
{
|
||||
char keyboard[32];
|
||||
std::array<char, 32> 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
|
||||
|
|
Loading…
Reference in New Issue