command/input: support globs
This commit is contained in:
		
				
					committed by
					
						 Isaac Freund
						Isaac Freund
					
				
			
			
				
	
			
			
			
						parent
						
							7f1f9152f2
						
					
				
				
					commit
					931b6268e7
				
			| @ -465,6 +465,8 @@ The _input_ command can be used to create a configuration rule for an input | |||||||
| device identified by its _name_. | device identified by its _name_. | ||||||
| The _name_ of an input device consists of its type, its numerical vendor id, | 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 -. | its numerical product id and finally its self-advertised name, separated by -. | ||||||
|  | Simple globbing patterns are supported, see the rules section for further | ||||||
|  | information on globs. | ||||||
|  |  | ||||||
| A list of all device properties that can be configured may be 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. | However note that not every input device supports every property. | ||||||
|  | |||||||
| @ -262,7 +262,7 @@ pub const ScrollButton = struct { | |||||||
|     } |     } | ||||||
| }; | }; | ||||||
|  |  | ||||||
| identifier: []const u8, | glob: []const u8, | ||||||
|  |  | ||||||
| // Note: Field names equal name of the setting in the 'input' command. | // Note: Field names equal name of the setting in the 'input' command. | ||||||
| events: ?EventState = null, | events: ?EventState = null, | ||||||
| @ -281,15 +281,15 @@ tap: ?TapState = null, | |||||||
| @"scroll-button": ?ScrollButton = null, | @"scroll-button": ?ScrollButton = null, | ||||||
|  |  | ||||||
| pub fn deinit(self: *Self) void { | pub fn deinit(self: *Self) void { | ||||||
|     util.gpa.free(self.identifier); |     util.gpa.free(self.glob); | ||||||
| } | } | ||||||
|  |  | ||||||
| pub fn apply(self: *Self, device: *InputDevice) void { | pub fn apply(self: *const Self, device: *InputDevice) void { | ||||||
|     const libinput_device: *c.libinput_device = @ptrCast(device.wlr_device.getLibinputDevice() orelse return); |     const libinput_device: *c.libinput_device = @ptrCast(device.wlr_device.getLibinputDevice() orelse return); | ||||||
|     log.debug("applying input configuration to device: {s}", .{device.identifier}); |     log.debug("applying input configuration '{s}' to device '{s}'.", .{ self.glob, device.identifier }); | ||||||
|  |  | ||||||
|     inline for (@typeInfo(Self).Struct.fields) |field| { |     inline for (@typeInfo(Self).Struct.fields) |field| { | ||||||
|         if (comptime mem.eql(u8, field.name, "identifier")) continue; |         if (comptime mem.eql(u8, field.name, "glob")) continue; | ||||||
|  |  | ||||||
|         if (@field(self, field.name)) |setting| { |         if (@field(self, field.name)) |setting| { | ||||||
|             log.debug("applying setting: {s}", .{field.name}); |             log.debug("applying setting: {s}", .{field.name}); | ||||||
| @ -300,7 +300,7 @@ pub fn apply(self: *Self, device: *InputDevice) void { | |||||||
|  |  | ||||||
| pub fn parse(self: *Self, setting: []const u8, value: []const u8) !void { | pub fn parse(self: *Self, setting: []const u8, value: []const u8) !void { | ||||||
|     inline for (@typeInfo(Self).Struct.fields) |field| { |     inline for (@typeInfo(Self).Struct.fields) |field| { | ||||||
|         if (comptime mem.eql(u8, field.name, "identifier")) continue; |         if (comptime mem.eql(u8, field.name, "glob")) continue; | ||||||
|  |  | ||||||
|         if (mem.eql(u8, setting, field.name)) { |         if (mem.eql(u8, setting, field.name)) { | ||||||
|             // Special-case the settings which are not enums. |             // Special-case the settings which are not enums. | ||||||
| @ -329,10 +329,10 @@ pub fn parse(self: *Self, setting: []const u8, value: []const u8) !void { | |||||||
| } | } | ||||||
|  |  | ||||||
| pub fn write(self: *Self, writer: anytype) !void { | pub fn write(self: *Self, writer: anytype) !void { | ||||||
|     try writer.print("{s}\n", .{self.identifier}); |     try writer.print("{s}\n", .{self.glob}); | ||||||
|  |  | ||||||
|     inline for (@typeInfo(Self).Struct.fields) |field| { |     inline for (@typeInfo(Self).Struct.fields) |field| { | ||||||
|         if (comptime mem.eql(u8, field.name, "identifier")) continue; |         if (comptime mem.eql(u8, field.name, "glob")) continue; | ||||||
|         if (@field(self, field.name)) |setting| { |         if (@field(self, field.name)) |setting| { | ||||||
|             // Special-case the settings which are not enums. |             // Special-case the settings which are not enums. | ||||||
|             if (comptime mem.eql(u8, field.name, "pointer-accel")) { |             if (comptime mem.eql(u8, field.name, "pointer-accel")) { | ||||||
|  | |||||||
| @ -22,6 +22,8 @@ const ascii = std.ascii; | |||||||
| const wlr = @import("wlroots"); | const wlr = @import("wlroots"); | ||||||
| const wl = @import("wayland").server.wl; | const wl = @import("wayland").server.wl; | ||||||
|  |  | ||||||
|  | const globber = @import("globber"); | ||||||
|  |  | ||||||
| const server = &@import("main.zig").server; | const server = &@import("main.zig").server; | ||||||
| const util = @import("util.zig"); | const util = @import("util.zig"); | ||||||
|  |  | ||||||
| @ -82,7 +84,7 @@ pub fn init(device: *InputDevice, seat: *Seat, wlr_device: *wlr.InputDevice) !vo | |||||||
|     if (!isKeyboardGroup(wlr_device)) { |     if (!isKeyboardGroup(wlr_device)) { | ||||||
|         // Apply any matching input device configuration. |         // Apply any matching input device configuration. | ||||||
|         for (server.input_manager.configs.items) |*input_config| { |         for (server.input_manager.configs.items) |*input_config| { | ||||||
|             if (mem.eql(u8, input_config.identifier, identifier)) { |             if (globber.match(identifier, input_config.glob)) { | ||||||
|                 input_config.apply(device); |                 input_config.apply(device); | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  | |||||||
| @ -49,7 +49,10 @@ pointer_constraints: *wlr.PointerConstraintsV1, | |||||||
| input_method_manager: *wlr.InputMethodManagerV2, | input_method_manager: *wlr.InputMethodManagerV2, | ||||||
| text_input_manager: *wlr.TextInputManagerV3, | text_input_manager: *wlr.TextInputManagerV3, | ||||||
|  |  | ||||||
|  | /// List of input device configurations. Ordered by glob generality, with | ||||||
|  | /// the most general towards the start and the most specific towards the end. | ||||||
| configs: std.ArrayList(InputConfig), | configs: std.ArrayList(InputConfig), | ||||||
|  |  | ||||||
| devices: wl.list.Head(InputDevice, .link), | devices: wl.list.Head(InputDevice, .link), | ||||||
| seats: std.TailQueue(Seat) = .{}, | seats: std.TailQueue(Seat) = .{}, | ||||||
|  |  | ||||||
|  | |||||||
| @ -16,6 +16,11 @@ | |||||||
|  |  | ||||||
| const std = @import("std"); | const std = @import("std"); | ||||||
| const mem = std.mem; | const mem = std.mem; | ||||||
|  | const meta = std.meta; | ||||||
|  | const math = std.math; | ||||||
|  | const sort = std.sort; | ||||||
|  |  | ||||||
|  | const globber = @import("globber"); | ||||||
|  |  | ||||||
| const server = &@import("../main.zig").server; | const server = &@import("../main.zig").server; | ||||||
| const util = @import("../util.zig"); | const util = @import("../util.zig"); | ||||||
| @ -39,7 +44,7 @@ pub fn listInputs( | |||||||
|     var it = server.input_manager.devices.iterator(.forward); |     var it = server.input_manager.devices.iterator(.forward); | ||||||
|     while (it.next()) |device| { |     while (it.next()) |device| { | ||||||
|         const configured = for (server.input_manager.configs.items) |*input_config| { |         const configured = for (server.input_manager.configs.items) |*input_config| { | ||||||
|             if (mem.eql(u8, input_config.identifier, device.identifier)) { |             if (globber.match(device.identifier, input_config.glob)) { | ||||||
|                 break true; |                 break true; | ||||||
|             } |             } | ||||||
|         } else false; |         } else false; | ||||||
| @ -82,36 +87,50 @@ pub fn input( | |||||||
|     if (args.len < 4) return Error.NotEnoughArguments; |     if (args.len < 4) return Error.NotEnoughArguments; | ||||||
|     if (args.len > 4) return Error.TooManyArguments; |     if (args.len > 4) return Error.TooManyArguments; | ||||||
|  |  | ||||||
|     // Try to find an existing InputConfig with matching identifier, or create |     try globber.validate(args[1]); | ||||||
|  |  | ||||||
|  |     // Try to find an existing InputConfig with matching glob pattern, or create | ||||||
|     // a new one if none was found. |     // a new one if none was found. | ||||||
|     const input_config = for (server.input_manager.configs.items) |*input_config| { |     for (server.input_manager.configs.items) |*input_config| { | ||||||
|         if (mem.eql(u8, input_config.identifier, args[1])) { |         if (mem.eql(u8, input_config.glob, args[1])) { | ||||||
|             try input_config.parse(args[2], args[3]); |             try input_config.parse(args[2], args[3]); | ||||||
|             break input_config; |  | ||||||
|         } |         } | ||||||
|     } else blk: { |     } else { | ||||||
|         const identifier_owned = try util.gpa.dupe(u8, args[1]); |         const glob_owned = try util.gpa.dupe(u8, args[1]); | ||||||
|         errdefer util.gpa.free(identifier_owned); |         errdefer util.gpa.free(glob_owned); | ||||||
|  |  | ||||||
|         try server.input_manager.configs.ensureUnusedCapacity(1); |         try server.input_manager.configs.ensureUnusedCapacity(1); | ||||||
|         const input_config = server.input_manager.configs.addOneAssumeCapacity(); |         const input_config = server.input_manager.configs.addOneAssumeCapacity(); | ||||||
|         errdefer _ = server.input_manager.configs.pop(); |         errdefer _ = server.input_manager.configs.pop(); | ||||||
|  |  | ||||||
|         input_config.* = .{ |         input_config.* = .{ | ||||||
|             .identifier = identifier_owned, |             .glob = glob_owned, | ||||||
|         }; |         }; | ||||||
|         try input_config.parse(args[2], args[3]); |         try input_config.parse(args[2], args[3]); | ||||||
|  |     } | ||||||
|  |  | ||||||
|         break :blk input_config; |     // Sort input configs by generality. | ||||||
|     }; |     sort.insertion(InputConfig, server.input_manager.configs.items, {}, lessThan); | ||||||
|  |  | ||||||
|     // Update matching existing input devices. |     // We need to update all input device matching the glob. The user may | ||||||
|  |     // add an input configuration at an arbitrary position in the generality | ||||||
|  |     // ordered list, so the simplest way to ensure the device is configured | ||||||
|  |     // correctly is to apply all input configurations again, in order. | ||||||
|     var it = server.input_manager.devices.iterator(.forward); |     var it = server.input_manager.devices.iterator(.forward); | ||||||
|     while (it.next()) |device| { |     while (it.next()) |device| { | ||||||
|         if (mem.eql(u8, device.identifier, args[1])) { |         // Device does not match the glob given in the command, so its | ||||||
|             input_config.apply(device); |         // configuration state after applying all configs again would be | ||||||
|             // We don't break here because it is common to have multiple input |         // the same. | ||||||
|             // devices with the same identifier. |         if (!globber.match(device.identifier, args[1])) continue; | ||||||
|  |  | ||||||
|  |         for (server.input_manager.configs.items) |ic| { | ||||||
|  |             if (globber.match(device.identifier, ic.glob)) { | ||||||
|  |                 ic.apply(device); | ||||||
|  |             } | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | fn lessThan(_: void, a: InputConfig, b: InputConfig) bool { | ||||||
|  |     return globber.order(a.glob, b.glob) == .gt; | ||||||
|  | } | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user