Make Config.keybinds into a hash map

This helps us with de-duplication. Previously, if you had host-specific
keybinds on the same key combination, the compositor would choose the
first... which is the opposite of how everything else in our config
handling works.
This commit is contained in:
Ben Buhse 2026-02-16 19:44:05 -06:00
commit 4c0117724e
No known key found for this signature in database
GPG key ID: 7916ACFCD38FD0B4
3 changed files with 26 additions and 11 deletions

View file

@ -38,7 +38,8 @@ bar_config: ?BarConfig = null,
/// Tag bind entries parsed from config (tag_bind nodes in keybinds block)
tag_binds: std.ArrayList(Keybind) = .{},
keybinds: std.ArrayList(Keybind) = .{},
// We use a hash map so that duplicate keybinds can be easily de-duplicated
keybinds: keybind_helper.Map = .{},
pointer_binds: std.ArrayList(PointerBind) = .{},
input_configs: std.ArrayList(InputConfig) = .{},
@ -90,8 +91,8 @@ pub fn create() !*Config {
config.load(&file_reader.interface) catch |err| {
log.err("Error while loading config: {s}. Continuing with default config", .{@errorName(err)});
// Free any partially-loaded state and reset to defaults
for (config.keybinds.items) |keybind| {
switch (keybind.command) {
for (config.keybinds.values()) |cmd| {
switch (cmd) {
.spawn => |argv| {
for (argv) |arg| utils.gpa.free(arg);
utils.gpa.free(argv);
@ -120,8 +121,8 @@ pub fn create() !*Config {
}
pub fn destroy(config: *Config) void {
for (config.keybinds.items) |keybind| {
switch (keybind.command) {
for (config.keybinds.values()) |cmd| {
switch (cmd) {
.spawn => |argv| {
for (argv) |arg| utils.gpa.free(arg);
utils.gpa.free(argv);

View file

@ -105,10 +105,8 @@ fn manage_start(wm: *WindowManager) void {
}
}
// Rest of the keybinds
for (context.config.keybinds.items) |keybind| {
// Keysyms should only be null in tag_binds (above)
std.debug.assert(keybind.keysym != null);
context.xkb_bindings.addBinding(river_seat_v1, keybind.keysym.?, keybind.modifiers, keybind.command);
for (context.config.keybinds.keys(), context.config.keybinds.values()) |key, command| {
context.xkb_bindings.addBinding(river_seat_v1, key.keysym, key.modifiers, command);
}
// Pointer bindings

View file

@ -11,6 +11,13 @@ pub const Keybind = struct {
keysym: ?xkbcommon.Keysym,
};
pub const Key = struct {
modifiers: river.SeatV1.Modifiers,
keysym: xkbcommon.Keysym,
};
pub const Map = std.AutoArrayHashMapUnmanaged(Key, XkbBindings.Command);
pub fn load(config: *Config, parser: *kdl.Parser, hostname: ?[]const u8) !void {
while (try parser.next()) |event| {
switch (event) {
@ -172,11 +179,20 @@ pub fn load(config: *Config, parser: *kdl.Parser, hostname: ?[]const u8) !void {
},
};
try config.keybinds.append(utils.gpa, .{
const gop = try config.keybinds.getOrPut(utils.gpa, .{
.modifiers = modifiers,
.command = command,
.keysym = keysym,
});
if (gop.found_existing) {
switch (gop.value_ptr.*) {
.spawn => |argv| {
for (argv) |arg| utils.gpa.free(arg);
utils.gpa.free(argv);
},
else => unreachable,
}
}
gop.value_ptr.* = command;
} else {
helpers.logWarnInvalidNode(node.name);
}