[x11] Implement raw keyboard input via XInput2

UI key presses are still handled by regular X events, but in-game
"button" presses arrive via raw keyboard events. This gives transparent
handling of keyboard repeat (UI keys see repeat, game keys do not),
without messing with the server's settings (yay, that was most annoying
when it came to debugging), and the keyboard is never grabbed, so this
is a fairly user-friendly setup.

At first, I wasn't too keen on capturing them from the root window
(thinking about the user's security), but after a lot of investigation,
I found a post by Peter Hutterer
(http://who-t.blogspot.com/2011/09/whats-new-in-xi-21-raw-events.html)
commenting that root window events were added to XInput2 specifically
for games. Since application focus is tracked and unfocused key events
are dropped very early on, there's no way for code further down the
food-chain to know there even was an event, abusing the access would
require modifying the x11 input code, in which case all bets are off
anyway and any attempt at security anywhere in the code will fail,
meaning that nefarious progs code and the like shouldn't be a problem.
This commit is contained in:
Bill Currie 2021-11-23 11:18:32 +09:00
parent 0c1927d631
commit af5d92708d
1 changed files with 53 additions and 11 deletions

View File

@ -851,26 +851,66 @@ event_button (XEvent *event)
}
}
static unsigned
in_x11_process_key_button (unsigned keycode, int press)
{
// X11 protocol supports only 256 keys. The key codes are the AT scan codes
// offset by 8 (so Esc is 9 instead of 1).
unsigned key = (keycode - 8) & 0xff;
x11_key_buttons[key].state = press;
return key;
}
static void
event_key (XEvent *event)
{
int key;
unsigned key = 0;
x_time = event->xkey.time;
// X11 protocol supports only 256 keys. The key codes are the AT scan codes
// offset by 8 (so Esc is 9 instead of 1).
key = (event->xkey.keycode - 8) & 0xff;
x11_key_buttons[key].state = event->type == KeyPress;
if (!x11_have_xi) {
key = in_x11_process_key_button (event->xkey.keycode,
event->type == KeyPress);
}
x11_key.shift = event->xmotion.state & 0xff;
XLateKey (&event->xkey, &x11_key.code, &x11_key.unicode);
if (event->type != KeyPress || !in_x11_send_key_event ()) {
if (!(event->type == KeyPress && in_x11_send_key_event ())
&& !x11_have_xi) {
in_x11_send_button_event (x11_keyboard_device.devid,
&x11_key_buttons[key],
x11_keyboard_device.event_data);
}
}
static void
xi_raw_key (void *event, int press)
{
if (!x_have_focus) {
//avoid being a keylogger
return;
}
XIRawEvent *re = event;
unsigned key = in_x11_process_key_button (re->detail, press);
// Send only the button press: event_key takes care of the UI key event,
// which includes character translation and repeat (XInput2 raw key events
// do not repeat)
in_x11_send_button_event (x11_keyboard_device.devid,
&x11_key_buttons[key],
x11_keyboard_device.event_data);
}
static void
xi_raw_key_press (void *event)
{
xi_raw_key (event, 1);
}
static void
xi_raw_key_resease (void *event)
{
xi_raw_key (event, 0);
}
static void
xi_raw_motion (void *event)
{
@ -963,6 +1003,8 @@ event_generic (XEvent *event)
{
// XI_LASTEVENT is the actual last event, not +1
static void (*xi_event_handlers[XI_LASTEVENT + 1]) (void *) = {
[XI_RawKeyPress] = xi_raw_key_press,
[XI_RawKeyRelease] = xi_raw_key_resease,
[XI_RawMotion] = xi_raw_motion,
[XI_RawButtonPress] = xi_raw_button_press,
[XI_RawButtonRelease] = xi_raw_button_resease,
@ -1296,6 +1338,8 @@ in_x11_xi_select_events (void)
};
XISetMask (mask, XI_BarrierHit);
XISetMask (mask, XI_BarrierLeave);
XISetMask (mask, XI_RawKeyPress);
XISetMask (mask, XI_RawKeyRelease);
XISelectEvents (x_disp, x_root, &evmask, 1);
}
@ -1324,10 +1368,6 @@ in_x11_xi_setup_grabs (void)
XIGrabEnter (x_disp, dev, x_win, None, XIGrabModeAsync, XIGrabModeAsync,
0, &evmask, 1, &modif);
//FIXME doesn't seem to do anything. Is it actually necessary?
//here for reference
//XIGrabFocusIn (x_disp, dev, x_win, XIGrabModeAsync, XIGrabModeAsync,
// 0, &evmask, 1, &modif);
}
static void
@ -1395,6 +1435,8 @@ IN_X11_Preinit (void)
x11_event_handler_id = IE_Add_Handler (x11_event_handler, 0);
x11_have_xi = in_x11_check_xi2 ();
X11_AddEvent (KeyPress, &event_key);
X11_AddEvent (KeyRelease, &event_key);
X11_AddEvent (FocusIn, &event_focusin);