From 1e3b8ed16149e8e83d88cbc187b9aee3f5d6812a Mon Sep 17 00:00:00 2001 From: tiosgz Date: Sun, 29 May 2022 19:59:41 +0000 Subject: [PATCH] Seat: avoid leaking eaten keys to client on focus Until now, Seat.setFocusRaw sent all pressed keys to the client, including ones that should be eaten. (Try e.g. changing focus to a nested wlroots compositor with a terminal open to easily see it.) However, only filtering out the eaten keys is not enough; they were eaten only once all mappings had been executed. Therefore, the original function had to be split into one looking up mappings and another executing them. --- river/Keyboard.zig | 13 ++++++++++--- river/KeycodeSet.zig | 5 +++++ river/Seat.zig | 38 ++++++++++++++++++++++++++++++++------ 3 files changed, 47 insertions(+), 9 deletions(-) diff --git a/river/Keyboard.zig b/river/Keyboard.zig index 3007e26..c93c7b2 100644 --- a/river/Keyboard.zig +++ b/river/Keyboard.zig @@ -17,6 +17,7 @@ const Self = @This(); const std = @import("std"); +const assert = std.debug.assert; const wlr = @import("wlroots"); const wl = @import("wayland").server.wl; const xkb = @import("xkbcommon"); @@ -61,6 +62,7 @@ pub fn init(self: *Self, seat: *Seat, input_device: *wlr.InputDevice) !void { defer keymap.unref(); const wlr_keyboard = self.input_device.device.keyboard; + wlr_keyboard.data = @ptrToInt(self); if (!wlr_keyboard.setKeymap(keymap)) return error.SetKeymapFailed; @@ -111,9 +113,14 @@ fn handleKey(listener: *wl.Listener(*wlr.Keyboard.event.Key), event: *wlr.Keyboa if (!released and handleBuiltinMapping(sym)) return; } - // Handle user-defined mapping - const mapped = self.seat.handleMapping(keycode, modifiers, released, xkb_state); - if (mapped and !released) self.eaten_keycodes.add(event.keycode); + // Handle user-defined mappings + const mapped = self.seat.hasMapping(keycode, modifiers, released, xkb_state); + if (mapped) { + if (!released) self.eaten_keycodes.add(event.keycode); + + const handled = self.seat.handleMapping(keycode, modifiers, released, xkb_state); + assert(handled); + } const eaten = if (released) self.eaten_keycodes.remove(event.keycode) else mapped; diff --git a/river/KeycodeSet.zig b/river/KeycodeSet.zig index bceb039..a70e0ec 100644 --- a/river/KeycodeSet.zig +++ b/river/KeycodeSet.zig @@ -50,3 +50,8 @@ pub fn remove(self: *Self, old: u32) bool { return false; } + +/// Removes other's contents from self (if present) +pub fn subtract(self: *Self, other: Self) void { + for (other.items[0..other.len]) |item| _ = self.remove(item); +} diff --git a/river/Seat.zig b/river/Seat.zig index baf1056..54cd853 100644 --- a/river/Seat.zig +++ b/river/Seat.zig @@ -31,6 +31,7 @@ const DragIcon = @import("DragIcon.zig"); const Cursor = @import("Cursor.zig"); const InputManager = @import("InputManager.zig"); const Keyboard = @import("Keyboard.zig"); +const KeycodeSet = @import("KeycodeSet.zig"); const Switch = @import("Switch.zig"); const Mapping = @import("Mapping.zig"); const LayerSurface = @import("LayerSurface.zig"); @@ -253,12 +254,20 @@ pub fn setFocusRaw(self: *Self, new_focus: FocusTarget) void { // Send keyboard enter/leave events and handle pointer constraints if (target_surface) |wlr_surface| { - if (self.wlr_seat.getKeyboard()) |keyboard| { + if (self.wlr_seat.getKeyboard()) |wlr_keyboard| { + var keycodes = KeycodeSet{ + .items = wlr_keyboard.keycodes, + .len = wlr_keyboard.num_keycodes, + }; + + const keyboard = @intToPtr(*Keyboard, wlr_keyboard.data); + keycodes.subtract(keyboard.eaten_keycodes); + self.wlr_seat.keyboardNotifyEnter( wlr_surface, - &keyboard.keycodes, - keyboard.num_keycodes, - &keyboard.modifiers, + &keycodes.items, + keycodes.len, + &wlr_keyboard.modifiers, ); } else { self.wlr_seat.keyboardNotifyEnter(wlr_surface, null, 0, null); @@ -349,8 +358,25 @@ pub fn handleViewUnmap(self: *Self, view: *View) void { if (self.focused == .view and self.focused.view == view) self.focus(null); } +/// Is there a user-defined mapping for passed keycode, modifiers and keyboard state? +pub fn hasMapping( + self: *Self, + keycode: xkb.Keycode, + modifiers: wlr.Keyboard.ModifierMask, + released: bool, + xkb_state: *xkb.State, +) bool { + const modes = &server.config.modes; + for (modes.items[self.mode_id].mappings.items) |*mapping| { + if (mapping.match(keycode, modifiers, released, xkb_state)) { + return true; + } + } + return false; +} + /// Handle any user-defined mapping for passed keycode, modifiers and keyboard state -/// Returns true if the key was handled +/// Returns true if at least one mapping was run pub fn handleMapping( self: *Self, keycode: xkb.Keycode, @@ -359,7 +385,7 @@ pub fn handleMapping( xkb_state: *xkb.State, ) bool { const modes = &server.config.modes; - // In case more than on mapping matches, all of them are activated + // In case more than one mapping matches, all of them are activated var handled = false; for (modes.items[self.mode_id].mappings.items) |*mapping| { if (mapping.match(keycode, modifiers, released, xkb_state)) {