git.lucas.co / cce-compositor
Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git

commitea736309ada1a5993867834e08d745b646111e51
parent664541dbf0
authorLeon Henrik Plickat <[email protected]>
date2024-01-03 21:30
keyboard-groups: use globber for identifier matching

 doc/riverctl.1.scd               |  3 ++-
 river/Keyboard.zig               | 17 ++++++++++-------
 river/KeyboardGroup.zig          | 37 ++++++++++++++++++++++++++-----------
 river/command/keyboard_group.zig |  3 +++
 4 files changed, 41 insertions(+), 19 deletions(-)

diff --git a/doc/riverctl.1.scd b/doc/riverctl.1.scd
index 5553306..b46185d 100644
--- a/doc/riverctl.1.scd
+++ b/doc/riverctl.1.scd
@@ -442,7 +442,8 @@ matches everything while _\*\*_ and the empty string are invalid.
 *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.
+	the given name will be added to the group. Simple globbing patterns are
+	supported, see the rules section for further information on globs.
 
 *keyboard-group-remove* _group_name_ _input_device_name_
 	Remove a keyboard from a keyboard group, identified by the keyboard's
diff --git a/river/Keyboard.zig b/river/Keyboard.zig
index 97eae23..69c34b2 100644
--- a/river/Keyboard.zig
+++ b/river/Keyboard.zig
@@ -21,6 +21,7 @@ const assert = std.debug.assert;
 const wlr = @import("wlroots");
 const wl = @import("wayland").server.wl;
 const xkb = @import("xkbcommon");
+const globber = @import("globber");
 
 const server = &@import("main.zig").server;
 const util = @import("util.zig");
@@ -52,13 +53,15 @@ pub fn init(self: *Self, seat: *Seat, wlr_device: *wlr.InputDevice) !void {
     // wlroots will log a more detailed error if this fails.
     if (!wlr_keyboard.setKeymap(server.config.keymap)) return error.OutOfMemory;
 
-    // Add to keyboard group, if applicable.
-    var it = seat.keyboard_groups.first;
-    while (it) |node| : (it = node.next) {
-        if (node.data.identifiers.contains(self.device.identifier)) {
-            // wlroots will log an error if this fails explaining the reason.
-            _ = node.data.wlr_group.addKeyboard(wlr_keyboard);
-            break;
+    // Add to keyboard-group, if applicable.
+    var group_it = seat.keyboard_groups.first;
+    outer: while (group_it) |group_node| : (group_it = group_node.next) {
+        for (group_node.data.globs.items) |glob| {
+            if (globber.match(glob, self.device.identifier)) {
+                // wlroots will log an error if this fails explaining the reason.
+                _ = group_node.data.wlr_group.addKeyboard(wlr_keyboard);
+                break :outer;
+            }
         }
     }
 
diff --git a/river/KeyboardGroup.zig b/river/KeyboardGroup.zig
index 617c042..cf68bde 100644
--- a/river/KeyboardGroup.zig
+++ b/river/KeyboardGroup.zig
@@ -20,6 +20,7 @@ const std = @import("std");
 const assert = std.debug.assert;
 const mem = std.mem;
 
+const globber = @import("globber");
 const wlr = @import("wlroots");
 const wl = @import("wayland").server.wl;
 const xkb = @import("xkbcommon");
@@ -35,7 +36,7 @@ const Keyboard = @import("Keyboard.zig");
 seat: *Seat,
 wlr_group: *wlr.KeyboardGroup,
 name: []const u8,
-identifiers: std.StringHashMapUnmanaged(void) = .{},
+globs: std.ArrayListUnmanaged([]const u8) = .{},
 
 pub fn create(seat: *Seat, name: []const u8) !void {
     log.debug("new keyboard group: '{s}'", .{name});
@@ -63,11 +64,11 @@ pub fn destroy(group: *KeyboardGroup) void {
     log.debug("destroying keyboard group: '{s}'", .{group.name});
 
     util.gpa.free(group.name);
-    {
-        var it = group.identifiers.keyIterator();
-        while (it.next()) |id| util.gpa.free(id.*);
+
+    for (group.globs.items) |glob| {
+        util.gpa.free(glob);
     }
-    group.identifiers.deinit(util.gpa);
+    group.globs.deinit(util.gpa);
 
     group.wlr_group.destroy();
 
@@ -77,14 +78,23 @@ pub fn destroy(group: *KeyboardGroup) void {
 }
 
 pub fn addIdentifier(group: *KeyboardGroup, new_id: []const u8) !void {
-    if (group.identifiers.contains(new_id)) return;
+    for (group.globs.items) |glob| {
+        if (mem.eql(u8, glob, new_id)) return;
+    }
 
     log.debug("keyboard group '{s}' adding identifier: '{s}'", .{ group.name, new_id });
 
     const owned_id = try util.gpa.dupe(u8, new_id);
     errdefer util.gpa.free(owned_id);
 
-    try group.identifiers.put(util.gpa, owned_id, {});
+    // Glob is validated in the command handler.
+    try group.globs.append(util.gpa, owned_id);
+    errdefer {
+        // Not used now, but if at any point this function is modified to that
+        // it may return an error after the glob pattern is added to the list,
+        // the list will have a pointer to freed memory in its last position.
+        _ = group.globs.pop();
+    }
 
     // Add any existing matching keyboards to the group.
     var it = server.input_manager.devices.iterator(.forward);
@@ -92,7 +102,7 @@ pub fn addIdentifier(group: *KeyboardGroup, new_id: []const u8) !void {
         if (device.seat != group.seat) continue;
         if (device.wlr_device.type != .keyboard) continue;
 
-        if (mem.eql(u8, new_id, device.identifier)) {
+        if (globber.match(device.identifier, new_id)) {
             log.debug("found existing matching keyboard; adding to group", .{});
 
             if (!group.wlr_group.addKeyboard(device.wlr_device.toKeyboard())) {
@@ -108,8 +118,13 @@ pub fn addIdentifier(group: *KeyboardGroup, new_id: []const u8) !void {
 }
 
 pub fn removeIdentifier(group: *KeyboardGroup, id: []const u8) !void {
-    if (group.identifiers.fetchRemove(id)) |kv| {
-        util.gpa.free(kv.key);
+    for (group.globs.items, 0..) |glob, index| {
+        if (mem.eql(u8, glob, id)) {
+            _ = group.globs.orderedRemove(index);
+            break;
+        }
+    } else {
+        return;
     }
 
     var it = server.input_manager.devices.iterator(.forward);
@@ -117,7 +132,7 @@ pub fn removeIdentifier(group: *KeyboardGroup, id: []const u8) !void {
         if (device.seat != group.seat) continue;
         if (device.wlr_device.type != .keyboard) continue;
 
-        if (mem.eql(u8, device.identifier, id)) {
+        if (globber.match(device.identifier, id)) {
             const wlr_keyboard = device.wlr_device.toKeyboard();
             assert(wlr_keyboard.group == group.wlr_group);
             group.wlr_group.removeKeyboard(wlr_keyboard);
diff --git a/river/command/keyboard_group.zig b/river/command/keyboard_group.zig
index 4453006..442fbdc 100644
--- a/river/command/keyboard_group.zig
+++ b/river/command/keyboard_group.zig
@@ -17,6 +17,8 @@
 const std = @import("std");
 const mem = std.mem;
 
+const globber = @import("globber");
+
 const server = &@import("../main.zig").server;
 const util = @import("../util.zig");
 
@@ -69,6 +71,7 @@ pub fn keyboardGroupAdd(
         out.* = msg;
         return;
     };
+    try globber.validate(args[2]);
     try group.addIdentifier(args[2]);
 }