Keyboard: eat key release event for mappings
Until now, only the event (press/release) for which a mapping was present got eaten, and the other was passed to the client. From this commit, a press mapping eats both events and a release mapping eats nothing (and a press+release combo eats both). This fixes behavior of some clients that do not make a difference between press and release (e.g. Firefox with a fullscreen video exiting fullscreen even on an Esc release event).
This commit is contained in:
		| @ -24,6 +24,7 @@ const xkb = @import("xkbcommon"); | ||||
| const server = &@import("main.zig").server; | ||||
| const util = @import("util.zig"); | ||||
|  | ||||
| const KeycodeSet = @import("KeycodeSet.zig"); | ||||
| const Seat = @import("Seat.zig"); | ||||
|  | ||||
| const log = std.log.scoped(.keyboard); | ||||
| @ -31,6 +32,9 @@ const log = std.log.scoped(.keyboard); | ||||
| seat: *Seat, | ||||
| input_device: *wlr.InputDevice, | ||||
|  | ||||
| /// Pressed keys for which a mapping was triggered on press | ||||
| eaten_keycodes: KeycodeSet = .{}, | ||||
|  | ||||
| key: wl.Listener(*wlr.Keyboard.event.Key) = wl.Listener(*wlr.Keyboard.event.Key).init(handleKey), | ||||
| modifiers: wl.Listener(*wlr.Keyboard) = wl.Listener(*wlr.Keyboard).init(handleModifiers), | ||||
| destroy: wl.Listener(*wlr.Keyboard) = wl.Listener(*wlr.Keyboard).init(handleDestroy), | ||||
| @ -108,12 +112,12 @@ fn handleKey(listener: *wl.Listener(*wlr.Keyboard.event.Key), event: *wlr.Keyboa | ||||
|     } | ||||
|  | ||||
|     // Handle user-defined mapping | ||||
|     if (!self.seat.handleMapping( | ||||
|         keycode, | ||||
|         modifiers, | ||||
|         released, | ||||
|         xkb_state, | ||||
|     )) { | ||||
|     const mapped = self.seat.handleMapping(keycode, modifiers, released, xkb_state); | ||||
|     if (mapped and !released) self.eaten_keycodes.add(event.keycode); | ||||
|  | ||||
|     const eaten = if (released) self.eaten_keycodes.remove(event.keycode) else mapped; | ||||
|  | ||||
|     if (!eaten) { | ||||
|         // If key was not handled, we pass it along to the client. | ||||
|         const wlr_seat = self.seat.wlr_seat; | ||||
|         wlr_seat.setKeyboard(self.input_device); | ||||
|  | ||||
							
								
								
									
										52
									
								
								river/KeycodeSet.zig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								river/KeycodeSet.zig
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,52 @@ | ||||
| // This file is part of river, a dynamic tiling wayland compositor. | ||||
| // | ||||
| // Copyright 2022 The River Developers | ||||
| // | ||||
| // This program is free software: you can redistribute it and/or modify | ||||
| // it under the terms of the GNU General Public License as published by | ||||
| // the Free Software Foundation, version 3. | ||||
| // | ||||
| // This program is distributed in the hope that it will be useful, | ||||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
| // GNU General Public License for more details. | ||||
| // | ||||
| // You should have received a copy of the GNU General Public License | ||||
| // along with this program. If not, see <https://www.gnu.org/licenses/>. | ||||
|  | ||||
| const Self = @This(); | ||||
|  | ||||
| const std = @import("std"); | ||||
| const assert = std.debug.assert; | ||||
| const log = std.log.scoped(.keyboard); | ||||
|  | ||||
| const wlr = @import("wlroots"); | ||||
|  | ||||
| items: [32]u32 = undefined, | ||||
| len: usize = 0, | ||||
|  | ||||
| pub fn add(self: *Self, new: u32) void { | ||||
|     for (self.items[0..self.len]) |item| if (new == item) return; | ||||
|  | ||||
|     comptime assert(@typeInfo(std.meta.fieldInfo(Self, .items).field_type).Array.len == | ||||
|         @typeInfo(std.meta.fieldInfo(wlr.Keyboard, .keycodes).field_type).Array.len); | ||||
|  | ||||
|     if (self.len == self.items.len) { | ||||
|         log.err("KeycodeSet limit reached, code {d} omitted", .{new}); | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     self.items[self.len] = new; | ||||
|     self.len += 1; | ||||
| } | ||||
|  | ||||
| pub fn remove(self: *Self, old: u32) bool { | ||||
|     for (self.items[0..self.len]) |item, idx| if (old == item) { | ||||
|         self.len -= 1; | ||||
|         if (self.len > 0) self.items[idx] = self.items[self.len]; | ||||
|  | ||||
|         return true; | ||||
|     }; | ||||
|  | ||||
|     return false; | ||||
| } | ||||
		Reference in New Issue
	
	Block a user