diff --git a/src/config/Config.zig b/src/config/Config.zig index 8b1ddad77..bc6f8feac 100644 --- a/src/config/Config.zig +++ b/src/config/Config.zig @@ -335,6 +335,11 @@ class: ?[:0]const u8 = null, /// but valid. /// - only a single key input is allowed, "ctrl+a+b" is invalid. /// +/// Valid modifiers are "shift", "ctrl" (alias: "control"), +/// "alt" (alias: "opt", "option"), and "super" (alias: "cmd", "command"). +/// You may use the modifier or the alias. When debugging keybinds, +/// the non-aliased modifier will always be used in output. +/// /// Action is the action to take when the trigger is satisfied. It takes /// the format "action" or "action:param". The latter form is only valid /// if the action requires a parameter. diff --git a/src/input/Binding.zig b/src/input/Binding.zig index ed7a93d48..24fb469a6 100644 --- a/src/input/Binding.zig +++ b/src/input/Binding.zig @@ -57,13 +57,27 @@ pub fn parse(raw_input: []const u8) !Binding { if (std.mem.eql(u8, part, field.name)) { // Repeat not allowed if (@field(result.mods, field.name)) return Error.InvalidFormat; - @field(result.mods, field.name) = true; continue :loop; } } } + // Alias modifiers + const alias_mods = .{ + .{ "cmd", "super" }, .{ "command", "super" }, + .{ "opt", "alt" }, .{ "option", "alt" }, + .{ "control", "ctrl" }, + }; + inline for (alias_mods) |pair| { + if (std.mem.eql(u8, part, pair[0])) { + // Repeat not allowed + if (@field(result.mods, pair[1])) return Error.InvalidFormat; + @field(result.mods, pair[1]) = true; + continue :loop; + } + } + // If the key starts with "physical" then this is an physical key. const physical = "physical:"; const key_part = if (std.mem.startsWith(u8, part, physical)) key_part: { @@ -754,6 +768,48 @@ test "parse: triggers" { try testing.expectError(Error.InvalidFormat, parse("a+b=ignore")); } +test "parse: modifier aliases" { + const testing = std.testing; + + try testing.expectEqual(Binding{ + .trigger = .{ + .mods = .{ .super = true }, + .key = .a, + }, + .action = .{ .ignore = {} }, + }, try parse("cmd+a=ignore")); + try testing.expectEqual(Binding{ + .trigger = .{ + .mods = .{ .super = true }, + .key = .a, + }, + .action = .{ .ignore = {} }, + }, try parse("command+a=ignore")); + + try testing.expectEqual(Binding{ + .trigger = .{ + .mods = .{ .alt = true }, + .key = .a, + }, + .action = .{ .ignore = {} }, + }, try parse("opt+a=ignore")); + try testing.expectEqual(Binding{ + .trigger = .{ + .mods = .{ .alt = true }, + .key = .a, + }, + .action = .{ .ignore = {} }, + }, try parse("option+a=ignore")); + + try testing.expectEqual(Binding{ + .trigger = .{ + .mods = .{ .ctrl = true }, + .key = .a, + }, + .action = .{ .ignore = {} }, + }, try parse("control+a=ignore")); +} + test "parse: action invalid" { const testing = std.testing;