From e35c147cd5b8fcd363b7ecc495292733b25d96f5 Mon Sep 17 00:00:00 2001 From: Isaac Freund Date: Sat, 17 Sep 2022 11:26:45 +0200 Subject: [PATCH] river: refactor keyboard groups implementation This reduces the impact of keyboard groups on the Keyboard.zig implementation and otherwise improves consistency with patterns used elsewhere in rivers code. There are also two small changes to the riverctl interface: - keyboard-group-add-keyboard is renamed to keyboard-group-add - keyboard-group-remove is added to support removing keyboards from a group. --- completions/bash/riverctl | 3 +- completions/fish/riverctl.fish | 7 +- completions/zsh/_riverctl | 3 +- doc/riverctl.1.scd | 20 +++--- river/InputDevice.zig | 39 +++++++---- river/Keyboard.zig | 99 ++++++++++------------------ river/KeyboardGroup.zig | 108 +++++++++++++++++-------------- river/Seat.zig | 20 ++---- river/command.zig | 7 +- river/command/keyboard_group.zig | 45 +++++++------ 10 files changed, 179 insertions(+), 172 deletions(-) diff --git a/completions/bash/riverctl b/completions/bash/riverctl index c0b0bbd..9793b6f 100644 --- a/completions/bash/riverctl +++ b/completions/bash/riverctl @@ -5,7 +5,8 @@ function __riverctl_completion () OPTS=" \ keyboard-group-create \ keyboard-group-destroy \ - keyboard-group-add-keyboard \ + keyboard-group-add \ + keyboard-group-remove \ csd-filter-add \ exit \ float-filter-add \ diff --git a/completions/fish/riverctl.fish b/completions/fish/riverctl.fish index 4b81ef1..aabf99c 100644 --- a/completions/fish/riverctl.fish +++ b/completions/fish/riverctl.fish @@ -62,9 +62,10 @@ complete -c riverctl -x -n '__fish_riverctl_complete_arg 1' -a 'set-repeat' complete -c riverctl -x -n '__fish_riverctl_complete_arg 1' -a 'set-cursor-warp' -d 'Set the cursor warp mode.' complete -c riverctl -x -n '__fish_riverctl_complete_arg 1' -a 'xcursor-theme' -d 'Set the xcursor theme' # Keyboardgroups -complete -c riverctl -x -n '__fish_riverctl_complete_arg 1' -a 'keyboard-group-create' -d 'Create a keyboard group.' -complete -c riverctl -x -n '__fish_riverctl_complete_arg 1' -a 'keyboard-group-destroy' -d 'Destroy a keyboard group.' -complete -c riverctl -x -n '__fish_riverctl_complete_arg 1' -a 'keyboard-group-add-keyboard' -d 'Add a keyboard to a keyboard group.' +complete -c riverctl -x -n '__fish_riverctl_complete_arg 1' -a 'keyboard-group-create' -d 'Create a keyboard group.' +complete -c riverctl -x -n '__fish_riverctl_complete_arg 1' -a 'keyboard-group-destroy' -d 'Destroy a keyboard group.' +complete -c riverctl -x -n '__fish_riverctl_complete_arg 1' -a 'keyboard-group-add' -d 'Add a keyboard to a keyboard group.' +complete -c riverctl -x -n '__fish_riverctl_complete_arg 1' -a 'keyboard-group-remove' -d 'Remove a keyboard from a keyboard group.' # Subcommands complete -c riverctl -x -n '__fish_seen_subcommand_from focus-output' -a 'next previous' diff --git a/completions/zsh/_riverctl b/completions/zsh/_riverctl index 3250b50..f5cd125 100644 --- a/completions/zsh/_riverctl +++ b/completions/zsh/_riverctl @@ -58,7 +58,8 @@ _riverctl_subcommands() # Keyboard groups 'keyboard-group-create:Create a keyboard group' 'keyboard-group-destroy:Destroy a keyboard group' - 'keyboard-group-add-keyboard:Add a keyboard to a keyboard group' + 'keyboard-group-add:Add a keyboard to a keyboard group' + 'keyboard-group-remove:Remove a keyboard from a keyboard group' # Input 'input:Configure input devices' 'list-inputs:List all input devices' diff --git a/doc/riverctl.1.scd b/doc/riverctl.1.scd index 38ac669..d98aafe 100644 --- a/doc/riverctl.1.scd +++ b/doc/riverctl.1.scd @@ -330,26 +330,30 @@ A complete list may be found in _/usr/include/linux/input-event-codes.h_ *list-input-configs* List all input configurations. -*keyboard-group-create* _keyboard_group_name_ +*keyboard-group-create* _group_name_ Create a keyboard group. A keyboard group collects multiple keyboards in a single logical keyboard. This means that all state, like the active modifiers, is shared between the keyboards in a group. -*keyboard-group-destroy* _keyboard_group_name_ - Destroy the keyboard group of the given name. All attached keyboards +*keyboard-group-destroy* _group_name_ + Destroy the keyboard group with the given name. All attached keyboards will be released, making them act as seperate devices again. -*keyboard-group-add-keyboard* _keyboard_group_name_ _input_device_identifier_ - Add a keyboard to a keyboard group, identified by the keyboards input - device identifier. Any currently connected and future keyboards matching - the identifier will be added to the group. +*keyboard-group-add* _group_name_ _input_device_name_ + Add a keyboard to a keyboard group, identified by the keyboard's + input device name. Any currently connected and future keyboards with + the given name will be added to the group. + +*keyboard-group-remove* _group_name_ _input_device_name_ + Remove a keyboard from a keyboard group, identified by the keyboard's + input device name. The _input_ command can be used to create a configuration rule for an input device identified by its _name_. The _name_ of an input device consists of its type, its numerical vendor id, its numerical product id and finally its self-advertised name, separated by -. -A list of all device properties that can be configured maybe found below. +A list of all device properties that can be configured may be found below. However note that not every input device supports every property. *input* _name_ *events* *enabled*|*disabled*|*disabled-on-external-mouse* diff --git a/river/InputDevice.zig b/river/InputDevice.zig index 3b4e117..83da4ed 100644 --- a/river/InputDevice.zig +++ b/river/InputDevice.zig @@ -50,6 +50,12 @@ pub fn init(device: *InputDevice, seat: *Seat, wlr_device: *wlr.InputDevice) !vo else => @tagName(wlr_device.type), }; + // wlroots 0.15 leaves wlr_input_device->name NULL for keyboard groups. + // This wart has been cleaned up in 0.16, so just work around it until that is released. + // TODO(wlroots): Remove this hack + + const name = if (isKeyboardGroup(wlr_device)) "wlr_keyboard_group" else wlr_device.name; + const identifier = try std.fmt.allocPrint( util.gpa, "{s}-{}-{}-{s}", @@ -57,7 +63,7 @@ pub fn init(device: *InputDevice, seat: *Seat, wlr_device: *wlr.InputDevice) !vo device_type, wlr_device.vendor, wlr_device.product, - mem.trim(u8, mem.span(wlr_device.name), &ascii.spaces), + mem.trim(u8, mem.span(name), &ascii.spaces), }, ); errdefer util.gpa.free(identifier); @@ -77,15 +83,19 @@ pub fn init(device: *InputDevice, seat: *Seat, wlr_device: *wlr.InputDevice) !vo wlr_device.events.destroy.add(&device.destroy); - // Apply any matching input device configuration. - for (server.input_manager.configs.items) |*input_config| { - if (mem.eql(u8, input_config.identifier, identifier)) { - input_config.apply(device); + // Keyboard groups are implemented as "virtual" input devices which we don't want to expose + // in riverctl list-inputs as they can't be configured. + if (!isKeyboardGroup(wlr_device)) { + // Apply any matching input device configuration. + for (server.input_manager.configs.items) |*input_config| { + if (mem.eql(u8, input_config.identifier, identifier)) { + input_config.apply(device); + } } - } - server.input_manager.devices.append(device); - seat.updateCapabilities(); + server.input_manager.devices.append(device); + seat.updateCapabilities(); + } log.debug("new input device: {s}", .{identifier}); } @@ -95,12 +105,19 @@ pub fn deinit(device: *InputDevice) void { util.gpa.free(device.identifier); - device.link.remove(); - device.seat.updateCapabilities(); + if (!isKeyboardGroup(device.wlr_device)) { + device.link.remove(); + device.seat.updateCapabilities(); + } device.* = undefined; } +fn isKeyboardGroup(wlr_device: *wlr.InputDevice) bool { + return wlr_device.type == .keyboard and + wlr.KeyboardGroup.fromKeyboard(wlr_device.device.keyboard) != null; +} + fn handleDestroy(listener: *wl.Listener(*wlr.InputDevice), _: *wlr.InputDevice) void { const device = @fieldParentPtr(InputDevice, "destroy", listener); @@ -108,7 +125,7 @@ fn handleDestroy(listener: *wl.Listener(*wlr.InputDevice), _: *wlr.InputDevice) switch (device.wlr_device.type) { .keyboard => { - const keyboard = @fieldParentPtr(Keyboard, "provider", @ptrCast(*Keyboard.Provider, device)); + const keyboard = @fieldParentPtr(Keyboard, "device", device); keyboard.deinit(); util.gpa.destroy(keyboard); }, diff --git a/river/Keyboard.zig b/river/Keyboard.zig index f4f962c..eeec2ee 100644 --- a/river/Keyboard.zig +++ b/river/Keyboard.zig @@ -25,19 +25,13 @@ 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 InputDevice = @import("InputDevice.zig"); -const KeyboardGroup = @import("KeyboardGroup.zig"); -const KeycodeSet = @import("KeycodeSet.zig"); const log = std.log.scoped(.keyboard); -pub const Provider = union(enum) { - device: InputDevice, - group: *KeyboardGroup, -}; - -provider: Provider, +device: InputDevice, /// Pressed keys for which a mapping was triggered on press eaten_keycodes: KeycodeSet = .{}, @@ -46,8 +40,11 @@ key: wl.Listener(*wlr.Keyboard.event.Key) = wl.Listener(*wlr.Keyboard.event.Key) modifiers: wl.Listener(*wlr.Keyboard) = wl.Listener(*wlr.Keyboard).init(handleModifiers), pub fn init(self: *Self, seat: *Seat, wlr_device: *wlr.InputDevice) !void { - const wlr_keyboard = wlr_device.device.keyboard; - wlr_keyboard.data = @ptrToInt(self); + self.* = .{ + .device = undefined, + }; + try self.device.init(seat, wlr_device); + errdefer self.device.deinit(); const context = xkb.Context.new(.no_flags) orelse return error.XkbContextFailed; defer context.unref(); @@ -57,70 +54,37 @@ pub fn init(self: *Self, seat: *Seat, wlr_device: *wlr.InputDevice) !void { const keymap = xkb.Keymap.newFromNames(context, null, .no_flags) orelse return error.XkbKeymapFailed; defer keymap.unref(); + const wlr_keyboard = self.device.wlr_device.device.keyboard; + wlr_keyboard.data = @ptrToInt(self); + if (!wlr_keyboard.setKeymap(keymap)) return error.SetKeymapFailed; - self.* = .{ - .provider = undefined, - }; - if (wlr.KeyboardGroup.fromKeyboard(wlr_keyboard)) |grp| { - const group = @intToPtr(*KeyboardGroup, grp.data); - self.provider = .{ .group = group }; - } else { - self.provider = .{ .device = undefined }; - try self.provider.device.init(seat, wlr_device); - } + wlr_keyboard.setRepeatInfo(server.config.repeat_rate, server.config.repeat_delay); wlr_keyboard.events.key.add(&self.key); wlr_keyboard.events.modifiers.add(&self.modifiers); - - wlr_keyboard.setRepeatInfo(server.config.repeat_rate, server.config.repeat_delay); } pub fn deinit(self: *Self) void { self.key.link.remove(); self.modifiers.link.remove(); - switch (self.provider) { - .device => { - const wlr_keyboard = self.provider.device.wlr_device.device.keyboard; - if (wlr_keyboard.group) |group| group.removeKeyboard(wlr_keyboard); - self.provider.device.deinit(); - }, - .group => {}, - } + self.device.deinit(); self.* = undefined; } -/// This event is raised when a key is pressed or released. fn handleKey(listener: *wl.Listener(*wlr.Keyboard.event.Key), event: *wlr.Keyboard.event.Key) void { + // This event is raised when a key is pressed or released. const self = @fieldParentPtr(Self, "key", listener); - switch (self.provider) { - .device => { - if (self.provider.device.wlr_device.device.keyboard.group != null) return; - self.handleKeyImpl(self.provider.device.seat, self.provider.device.wlr_device, event); - }, - .group => self.handleKeyImpl(self.provider.group.seat, self.provider.group.group.input_device, event), - } -} + const wlr_keyboard = self.device.wlr_device.device.keyboard; -/// Simply pass modifiers along to the client -fn handleModifiers(listener: *wl.Listener(*wlr.Keyboard), _: *wlr.Keyboard) void { - const self = @fieldParentPtr(Self, "modifiers", listener); - switch (self.provider) { - .device => { - if (self.provider.device.wlr_device.device.keyboard.group != null) return; - self.handleModifiersImpl(self.provider.device.seat, self.provider.device.wlr_device); - }, - .group => self.handleModifiersImpl(self.provider.group.seat, self.provider.group.group.input_device), - } -} + // If the keyboard is in a group, this event will be handled by the group's Keyboard instance. + if (wlr_keyboard.group != null) return; -fn handleKeyImpl(self: *Self, seat: *Seat, wlr_device: *wlr.InputDevice, event: *wlr.Keyboard.event.Key) void { - const wlr_keyboard = wlr_device.device.keyboard; + self.device.seat.handleActivity(); - seat.handleActivity(); - seat.clearRepeatingMapping(); + self.device.seat.clearRepeatingMapping(); // Translate libinput keycode -> xkbcommon const keycode = event.keycode + 8; @@ -137,7 +101,7 @@ fn handleKeyImpl(self: *Self, seat: *Seat, wlr_device: *wlr.InputDevice, event: !released and !isModifier(sym)) { - seat.cursor.hide(); + self.device.seat.cursor.hide(); break; } } @@ -148,11 +112,11 @@ fn handleKeyImpl(self: *Self, seat: *Seat, wlr_device: *wlr.InputDevice, event: } // Handle user-defined mappings - const mapped = seat.hasMapping(keycode, modifiers, released, xkb_state); + const mapped = self.device.seat.hasMapping(keycode, modifiers, released, xkb_state); if (mapped) { if (!released) self.eaten_keycodes.add(event.keycode); - const handled = seat.handleMapping(keycode, modifiers, released, xkb_state); + const handled = self.device.seat.handleMapping(keycode, modifiers, released, xkb_state); assert(handled); } @@ -160,8 +124,8 @@ fn handleKeyImpl(self: *Self, seat: *Seat, wlr_device: *wlr.InputDevice, event: if (!eaten) { // If key was not handled, we pass it along to the client. - const wlr_seat = seat.wlr_seat; - wlr_seat.setKeyboard(wlr_device); + const wlr_seat = self.device.seat.wlr_seat; + wlr_seat.setKeyboard(self.device.wlr_device); wlr_seat.keyboardNotifyKey(event.time_msec, event.keycode, event.state); } } @@ -170,6 +134,17 @@ fn isModifier(keysym: xkb.Keysym) bool { return @enumToInt(keysym) >= xkb.Keysym.Shift_L and @enumToInt(keysym) <= xkb.Keysym.Hyper_R; } +fn handleModifiers(listener: *wl.Listener(*wlr.Keyboard), _: *wlr.Keyboard) void { + const self = @fieldParentPtr(Self, "modifiers", listener); + const wlr_keyboard = self.device.wlr_device.device.keyboard; + + // If the keyboard is in a group, this event will be handled by the group's Keyboard instance. + if (wlr_keyboard.group != null) return; + + self.device.seat.wlr_seat.setKeyboard(self.device.wlr_device); + self.device.seat.wlr_seat.keyboardNotifyModifiers(&wlr_keyboard.modifiers); +} + /// Handle any builtin, harcoded compsitor mappings such as VT switching. /// Returns true if the keysym was handled. fn handleBuiltinMapping(keysym: xkb.Keysym) bool { @@ -189,9 +164,3 @@ fn handleBuiltinMapping(keysym: xkb.Keysym) bool { else => return false, } } - -/// Simply pass modifiers along to the client -fn handleModifiersImpl(_: *Self, seat: *Seat, wlr_device: *wlr.InputDevice) void { - seat.wlr_seat.setKeyboard(wlr_device); - seat.wlr_seat.keyboardNotifyModifiers(&wlr_device.device.keyboard.modifiers); -} diff --git a/river/KeyboardGroup.zig b/river/KeyboardGroup.zig index dc0f883..e759bc0 100644 --- a/river/KeyboardGroup.zig +++ b/river/KeyboardGroup.zig @@ -14,12 +14,12 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -const Self = @This(); +const KeyboardGroup = @This(); const std = @import("std"); -const heap = std.heap; +const assert = std.debug.assert; const mem = std.mem; -const debug = std.debug; + const wlr = @import("wlroots"); const wl = @import("wayland").server.wl; const xkb = @import("xkbcommon"); @@ -33,81 +33,95 @@ const Seat = @import("Seat.zig"); const Keyboard = @import("Keyboard.zig"); seat: *Seat, -group: *wlr.KeyboardGroup, +wlr_group: *wlr.KeyboardGroup, name: []const u8, -keyboard_identifiers: std.ArrayListUnmanaged([]const u8) = .{}, +identifiers: std.StringHashMapUnmanaged(void) = .{}, -pub fn init(self: *Self, seat: *Seat, _name: []const u8) !void { - log.debug("new keyboard group: '{s}'", .{_name}); +pub fn create(seat: *Seat, name: []const u8) !void { + log.debug("new keyboard group: '{s}'", .{name}); - const group = try wlr.KeyboardGroup.create(); - errdefer group.destroy(); - group.data = @ptrToInt(self); + const node = try util.gpa.create(std.TailQueue(KeyboardGroup).Node); + errdefer util.gpa.destroy(node); - const name = try util.gpa.dupe(u8, _name); - errdefer util.gpa.free(name); + const wlr_group = try wlr.KeyboardGroup.create(); + errdefer wlr_group.destroy(); - self.* = .{ - .group = group, - .name = name, + const owned_name = try util.gpa.dupe(u8, name); + errdefer util.gpa.free(owned_name); + + node.data = .{ + .wlr_group = wlr_group, + .name = owned_name, .seat = seat, }; - seat.addDevice(self.group.input_device); - seat.wlr_seat.setKeyboard(self.group.input_device); + seat.addDevice(wlr_group.input_device); + seat.keyboard_groups.append(node); } -pub fn deinit(self: *Self) void { - log.debug("removing keyboard group: '{s}'", .{self.name}); +pub fn destroy(group: *KeyboardGroup) void { + log.debug("destroying keyboard group: '{s}'", .{group.name}); - util.gpa.free(self.name); - for (self.keyboard_identifiers.items) |id| util.gpa.free(id); - self.keyboard_identifiers.deinit(util.gpa); + util.gpa.free(group.name); + { + var it = group.identifiers.keyIterator(); + while (it.next()) |id| util.gpa.free(id.*); + } + group.identifiers.deinit(util.gpa); - // wlroots automatically removes all keyboards from the group. - self.group.destroy(); + group.wlr_group.destroy(); + + const node = @fieldParentPtr(std.TailQueue(KeyboardGroup).Node, "data", group); + group.seat.keyboard_groups.remove(node); + util.gpa.destroy(node); } -pub fn addKeyboardIdentifier(self: *Self, _id: []const u8) !void { - if (containsIdentifier(self, _id)) return; - log.debug("keyboard group '{s}' adding identifier: '{s}'", .{ self.name, _id }); +pub fn addIdentifier(group: *KeyboardGroup, new_id: []const u8) !void { + if (group.identifiers.contains(new_id)) return; - const id = try util.gpa.dupe(u8, _id); - errdefer util.gpa.free(id); - try self.keyboard_identifiers.append(util.gpa, id); + log.debug("keyboard group '{s}' adding identifier: '{s}'", .{ group.name, new_id }); - // Add any existing matching keyboard to group. + const owned_id = try util.gpa.dupe(u8, new_id); + errdefer util.gpa.free(owned_id); + + try group.identifiers.put(util.gpa, owned_id, {}); + + // Add any existing matching keyboards to the group. var it = server.input_manager.devices.iterator(.forward); while (it.next()) |device| { - if (device.seat != self.seat) continue; + if (device.seat != group.seat) continue; if (device.wlr_device.type != .keyboard) continue; - if (mem.eql(u8, _id, device.identifier)) { + if (mem.eql(u8, new_id, device.identifier)) { log.debug("found existing matching keyboard; adding to group", .{}); const wlr_keyboard = device.wlr_device.device.keyboard; - if (!self.group.addKeyboard(wlr_keyboard)) continue; // wlroots logs its own errors. + if (!group.wlr_group.addKeyboard(wlr_keyboard)) { + // wlroots logs an error message to explain why this failed. + continue; + } } // Continue, because we may have more than one device with the exact - // same identifier. That is in fact the reason for the keyboard group + // same identifier. That is in fact one reason for the keyboard group // feature to exist in the first place. } } -pub fn containsIdentifier(self: *Self, id: []const u8) bool { - for (self.keyboard_identifiers.items) |ki| { - if (mem.eql(u8, ki, id)) return true; +pub fn removeIdentifier(group: *KeyboardGroup, id: []const u8) !void { + if (group.identifiers.fetchRemove(id)) |kv| { + util.gpa.free(kv.key); } - return false; -} -pub fn addKeyboard(self: *Self, keyboard: *Keyboard) !void { - debug.assert(keyboard.provider != .group); - const wlr_keyboard = keyboard.provider.device.wlr_device.device.keyboard; - log.debug("keyboard group '{s}' adding keyboard: '{s}'", .{ self.name, keyboard.provider.device.identifier }); - if (!self.group.addKeyboard(wlr_keyboard)) { - log.err("failed to add keyboard to group", .{}); - return error.OutOfMemory; + var it = server.input_manager.devices.iterator(.forward); + while (it.next()) |device| { + if (device.seat != group.seat) continue; + if (device.wlr_device.type != .keyboard) continue; + + if (mem.eql(u8, device.identifier, id)) { + const wlr_keyboard = device.wlr_device.device.keyboard; + assert(wlr_keyboard.group == group.wlr_group); + group.wlr_group.removeKeyboard(wlr_keyboard); + } } } diff --git a/river/Seat.zig b/river/Seat.zig index aef48d2..4881092 100644 --- a/river/Seat.zig +++ b/river/Seat.zig @@ -32,6 +32,7 @@ const DragIcon = @import("DragIcon.zig"); const InputDevice = @import("InputDevice.zig"); const InputManager = @import("InputManager.zig"); const Keyboard = @import("Keyboard.zig"); +const KeyboardGroup = @import("KeyboardGroup.zig"); const KeycodeSet = @import("KeycodeSet.zig"); const LayerSurface = @import("LayerSurface.zig"); const Mapping = @import("Mapping.zig"); @@ -41,7 +42,6 @@ const Switch = @import("Switch.zig"); const View = @import("View.zig"); const ViewStack = @import("view_stack.zig").ViewStack; const XwaylandOverrideRedirect = @import("XwaylandOverrideRedirect.zig"); -const KeyboardGroup = @import("KeyboardGroup.zig"); const log = std.log.scoped(.seat); const PointerConstraint = @import("PointerConstraint.zig"); @@ -128,6 +128,10 @@ pub fn deinit(self: *Self) void { self.cursor.deinit(); self.mapping_repeat_timer.remove(); + while (self.keyboard_groups.first) |node| { + node.data.destroy(); + } + while (self.focus_stack.first) |node| { self.focus_stack.remove(node); util.gpa.destroy(node); @@ -478,18 +482,7 @@ fn tryAddDevice(self: *Self, wlr_device: *wlr.InputDevice) !void { try keyboard.init(self, wlr_device); - // Add this keyboard to a keyboard group, if the group contains a - // matching identifier and if the keyboard isn't a group itself. - if (keyboard.provider == .device) { - var it = self.keyboard_groups.first; - while (it) |node| : (it = node.next) { - if (node.data.containsIdentifier(keyboard.provider.device.identifier)) { - try node.data.addKeyboard(keyboard); - break; - } - } - } - + self.wlr_seat.setKeyboard(keyboard.device.wlr_device); if (self.wlr_seat.keyboard_state.focused_surface) |wlr_surface| { self.wlr_seat.keyboardNotifyClearFocus(); self.keyboardNotifyEnter(wlr_surface); @@ -522,7 +515,6 @@ pub fn updateCapabilities(self: *Self) void { var it = server.input_manager.devices.iterator(.forward); while (it.next()) |device| { - log.debug(">>>> '{s}'", .{device.identifier}); if (device.seat == self) { switch (device.wlr_device.type) { .keyboard => capabilities.keyboard = true, diff --git a/river/command.zig b/river/command.zig index 57fa35a..55dd7ca 100644 --- a/river/command.zig +++ b/river/command.zig @@ -89,9 +89,10 @@ const command_impls = std.ComptimeStringMap( .{ "unmap-switch", @import("command/map.zig").unmapSwitch }, .{ "xcursor-theme", @import("command/xcursor_theme.zig").xcursorTheme }, .{ "zoom", @import("command/zoom.zig").zoom }, - .{ "keyboard-group-create", @import("command/keyboard_group.zig").keyboardGroupCreate}, - .{ "keyboard-group-destroy", @import("command/keyboard_group.zig").keyboardGroupDestroy}, - .{ "keyboard-group-add-keyboard", @import("command/keyboard_group.zig").keyboardGroupAddIdentifier}, + .{ "keyboard-group-create", @import("command/keyboard_group.zig").keyboardGroupCreate }, + .{ "keyboard-group-destroy", @import("command/keyboard_group.zig").keyboardGroupDestroy }, + .{ "keyboard-group-add", @import("command/keyboard_group.zig").keyboardGroupAdd }, + .{ "keyboard-group-remove", @import("command/keyboard_group.zig").keyboardGroupRemove }, }, ); // zig fmt: on diff --git a/river/command/keyboard_group.zig b/river/command/keyboard_group.zig index 85763f0..4453006 100644 --- a/river/command/keyboard_group.zig +++ b/river/command/keyboard_group.zig @@ -32,19 +32,13 @@ pub fn keyboardGroupCreate( if (args.len < 2) return Error.NotEnoughArguments; if (args.len > 2) return Error.TooManyArguments; - var it = seat.keyboard_groups.first; - while (it) |node| : (it = node.next) { - if (mem.eql(u8, node.data.name, args[1])) { - const msg = try util.gpa.dupe(u8, "error: failed to create keybaord group: group of same name already exists\n"); - out.* = msg; - return; - } + if (keyboardGroupFromName(seat, args[1]) != null) { + const msg = try util.gpa.dupe(u8, "error: failed to create keybaord group: group of same name already exists\n"); + out.* = msg; + return; } - const node = try util.gpa.create(std.TailQueue(KeyboardGroup).Node); - errdefer util.gpa.destroy(node); - try node.data.init(seat, args[1]); - seat.keyboard_groups.append(node); + try KeyboardGroup.create(seat, args[1]); } pub fn keyboardGroupDestroy( @@ -54,18 +48,15 @@ pub fn keyboardGroupDestroy( ) Error!void { if (args.len < 2) return Error.NotEnoughArguments; if (args.len > 2) return Error.TooManyArguments; - const kg = keyboardGroupFromName(seat, args[1]) orelse { + const group = keyboardGroupFromName(seat, args[1]) orelse { const msg = try util.gpa.dupe(u8, "error: no keyboard group with that name exists\n"); out.* = msg; return; }; - kg.deinit(); - const node = @fieldParentPtr(std.TailQueue(KeyboardGroup).Node, "data", kg); - seat.keyboard_groups.remove(node); - util.gpa.destroy(node); + group.destroy(); } -pub fn keyboardGroupAddIdentifier( +pub fn keyboardGroupAdd( seat: *Seat, args: []const [:0]const u8, out: *?[]const u8, @@ -73,12 +64,28 @@ pub fn keyboardGroupAddIdentifier( if (args.len < 3) return Error.NotEnoughArguments; if (args.len > 3) return Error.TooManyArguments; - const kg = keyboardGroupFromName(seat, args[1]) orelse { + const group = keyboardGroupFromName(seat, args[1]) orelse { const msg = try util.gpa.dupe(u8, "error: no keyboard group with that name exists\n"); out.* = msg; return; }; - try kg.addKeyboardIdentifier(args[2]); + try group.addIdentifier(args[2]); +} + +pub fn keyboardGroupRemove( + seat: *Seat, + args: []const [:0]const u8, + out: *?[]const u8, +) Error!void { + if (args.len < 3) return Error.NotEnoughArguments; + if (args.len > 3) return Error.TooManyArguments; + + const group = keyboardGroupFromName(seat, args[1]) orelse { + const msg = try util.gpa.dupe(u8, "error: no keyboard group with that name exists\n"); + out.* = msg; + return; + }; + try group.removeIdentifier(args[2]); } fn keyboardGroupFromName(seat: *Seat, name: []const u8) ?*KeyboardGroup {