Keyboard: fix key handling/eating logic

As noticed by leon-p, last refactorings made river send a release event
to the client even if the press event has been eaten. In addition, the
introduction of input method support means that we need to remember
*why* we've eaten the key.

Also make KeycodeSet more strict: i am not aware of any case when a
keyboard could have the same key pressed twice (specifically, keyboard
groups have this handled in wlroots), so make the behavior follow a
smaller set of possible scenarios.
This commit is contained in:
tiosgz
2024-01-27 11:43:40 +00:00
parent 69a51cadb4
commit 66f1881a72
3 changed files with 56 additions and 33 deletions
+26 -21
View File
@@ -135,33 +135,38 @@ fn handleKey(listener: *wl.Listener(*wlr.Keyboard.event.Key), event: *wlr.Keyboa
if (!released and handleBuiltinMapping(sym)) return;
}
// If we sent a pressed event to the client we should send the matching release event as well,
// even if the release event triggers a mapping or would otherwise be sent to an input method.
const sent_to_client = blk: {
if (released and !self.eaten_keycodes.remove(event.keycode)) {
// Every sent press event, to a regular client or the IM, should have
// the corresponding release event sent to the same client.
// Similarly, no press event means no release event.
const eat_reason = blk: {
// We can only eat a key on press; never on release
if (released) break :blk self.eaten_keycodes.remove(event.keycode);
if (self.device.seat.handleMapping(keycode, modifiers, released, xkb_state)) {
break :blk self.eaten_keycodes.add(event.keycode, .mapping);
} else if (self.getInputMethodGrab() != null) {
break :blk self.eaten_keycodes.add(event.keycode, .im_grab);
}
break :blk .none;
};
switch (eat_reason) {
.none => {
const wlr_seat = self.device.seat.wlr_seat;
wlr_seat.setKeyboard(self.device.wlr_device.toKeyboard());
wlr_seat.keyboardNotifyKey(event.time_msec, event.keycode, event.state);
break :blk true;
} else {
break :blk false;
}
};
if (self.device.seat.handleMapping(keycode, modifiers, released, xkb_state)) {
if (!released) self.eaten_keycodes.add(event.keycode);
} else if (self.getInputMethodGrab()) |keyboard_grab| {
if (!released) self.eaten_keycodes.add(event.keycode);
if (!sent_to_client) {
},
.mapping => {},
.im_grab => if (self.getInputMethodGrab()) |keyboard_grab| {
keyboard_grab.setKeyboard(keyboard_grab.keyboard);
keyboard_grab.sendKey(event.time_msec, event.keycode, event.state);
}
} else if (!sent_to_client) {
const wlr_seat = self.device.seat.wlr_seat;
wlr_seat.setKeyboard(self.device.wlr_device.toKeyboard());
wlr_seat.keyboardNotifyKey(event.time_msec, event.keycode, event.state);
},
}
// Release mappings don't interact with anything
if (released) _ = self.device.seat.handleMapping(keycode, modifiers, released, xkb_state);
}
fn isModifier(keysym: xkb.Keysym) bool {