command/input: support globs
This commit is contained in:
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;
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user