Add exit_river keybinding

Recently, river removed the hardcoded Ctrl+Alt Delete keybinding that
exits river and replaces it with a new `exit_session` request. This adds
support for that request via the new `exit_session` bind. We also added
3 hardcoded default keybinds to: exit river, reload the config, and
open foot. This way, if the config fails to load or is missing, you
should still be able to try reload. I guess you're still SOL if you have
at least one keybind and it's not reload_config, but you do what you can
do.
This commit is contained in:
Ben Buhse 2026-03-06 09:21:07 -06:00
commit 678d0563ed
No known key found for this signature in database
GPG key ID: 7916ACFCD38FD0B4
11 changed files with 293 additions and 108 deletions

View file

@ -127,9 +127,27 @@ pub fn create() !*Config {
};
}
if (config.keybinds.count() == 0) {
config.addFallbackKeybinds();
}
return config;
}
fn addFallbackKeybinds(config: *Config) void {
const Entry = struct { keybind.Key, XkbBindings.Command };
const fallbacks = [_]Entry{
.{ .{ .modifiers = .{ .ctrl = true, .mod1 = true }, .keysym = .Delete }, @unionInit(XkbBindings.Command, "exit_river", {}) },
.{ .{ .modifiers = .{ .mod4 = true, .shift = true }, .keysym = .R }, @unionInit(XkbBindings.Command, "reload_config", {}) },
.{ .{ .modifiers = .{ .mod4 = true }, .keysym = .T }, .{ .spawn = utils.gpa.dupe([]const u8, &.{
utils.gpa.dupe(u8, "foot") catch @panic("Out of memory"),
}) catch @panic("Out of memory") } },
};
for (&fallbacks) |*entry| {
config.keybinds.put(utils.gpa, entry[0], entry[1]) catch @panic("Out of memory");
}
}
pub fn destroy(config: *Config) void {
for (config.keybinds.values()) |cmd| {
cmd.deinit();

View file

@ -22,6 +22,7 @@ pub const Command = union(enum) {
reload_config,
toggle_fullscreen,
close_window,
exit_river,
// Tag management
set_output_tags: u32,
set_window_tags: u32,
@ -70,6 +71,7 @@ pub const Command = union(enum) {
.reload_config,
.toggle_fullscreen,
.close_window,
.exit_river,
.set_output_tags,
.set_window_tags,
.toggle_output_tags,
@ -246,6 +248,7 @@ const XkbBinding = struct {
window.river_window_v1.close();
}
},
.exit_river => context.wm.river_window_manager_v1.exitSession(),
.set_output_tags => |tags| {
const seat = first_seat orelse return;
const output = seat.focused_output orelse return;

View file

@ -140,6 +140,7 @@ pub fn load(config: *Config, parser: *kdl.Parser, hostname: ?[]const u8) !void {
.reload_config,
.toggle_fullscreen,
.close_window,
.exit_river,
.increment_primary_count,
.decrement_primary_count,
.swap_next,

View file

@ -262,7 +262,8 @@ fn registryListener(registry: *wl.Registry, event: wl.Registry.Event, globals: *
fatal("Failed to bind to river_layer_shell_v1: {any}", .{@errorName(e)});
};
} else if (mem.orderZ(u8, ev.interface, river.WindowManagerV1.interface.name) == .eq) {
globals.river_window_manager_v1 = registry.bind(ev.name, river.WindowManagerV1, 3) catch |e| {
if (ev.version < 4) utils.versionNotSupported(river.WindowManagerV1, ev.version, 4);
globals.river_window_manager_v1 = registry.bind(ev.name, river.WindowManagerV1, 4) catch |e| {
fatal("Failed to bind to river_window_manager_v1: {any}", .{@errorName(e)});
};
} else if (mem.orderZ(u8, ev.interface, river.XkbBindingsV1.interface.name) == .eq) {