Fix two crashes

One was where WM was assuming that a seat existed during first manage,
but that's not always true, so we have to check that before running the
initialization code. I also split that off into its own function like in
Window.

The other crash was when trying to calculate the layout with the
output's width and/or height equal to zero, it would crash subtracting
the border width.

I discovered both of these when try to restart beansprout without
restarting River.
This commit is contained in:
Ben Buhse 2026-02-18 15:30:05 -06:00
commit 08be768d99
No known key found for this signature in database
GPG key ID: 7916ACFCD38FD0B4
2 changed files with 76 additions and 56 deletions

View file

@ -72,7 +72,63 @@ pub fn prevOutput(wm: *WindowManager, current: *Output) ?*Output {
return @fieldParentPtr("link", prev_link);
}
fn manage_start(wm: *WindowManager) void {
fn initialize(wm: *WindowManager) void {
if (wm.context.initialized) return;
// We need a seat to intitialize this stuff, so let's just not do it right now.
// The WM can run fine without it, though, it won't be fully usuable.
const seat = wm.seats.first() orelse return;
const river_seat_v1 = seat.river_seat_v1;
const context = wm.context;
context.initialized = true;
// Tag bindings
for (context.config.tag_binds.items) |tag_bind| {
comptime var i: u8 = 1;
comptime var buffer: [1]u8 = undefined;
inline while (i <= 9) : (i += 1) {
const tags: u32 = 1 << (i - 1);
buffer[0] = i + '0';
const command: XkbBindings.Command = switch (tag_bind.command) {
.set_output_tags => .{ .set_output_tags = tags },
.set_window_tags => .{ .set_window_tags = tags },
.toggle_output_tags => .{ .toggle_output_tags = tags },
.toggle_window_tags => .{ .toggle_window_tags = tags },
else => unreachable,
};
context.xkb_bindings.addBinding(river_seat_v1, @field(xkbcommon.Keysym, &buffer), tag_bind.modifiers, command);
}
}
// Rest of the keybinds
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
for (context.config.pointer_binds.items) |pointer_bind| {
const binding = river_seat_v1.getPointerBinding(pointer_bind.button, pointer_bind.modifiers) catch {
log.err("Failed to create pointer binding", .{});
continue;
};
switch (pointer_bind.action) {
.move_window => {
if (seat.move_pointer_binding) |old| old.destroy();
binding.setListener(*Seat, Seat.movePointerBindingListener, seat);
seat.move_pointer_binding = binding;
},
.resize_window => {
if (seat.resize_pointer_binding) |old| old.destroy();
binding.setListener(*Seat, Seat.resizePointerBindingListener, seat);
seat.resize_pointer_binding = binding;
},
}
binding.enable();
}
}
fn manage(wm: *WindowManager) void {
const river_window_manager_v1 = wm.river_window_manager_v1;
const context = wm.context;
@ -82,54 +138,7 @@ fn manage_start(wm: *WindowManager) void {
if (!context.initialized) {
// This code runs during initial startup and after config reloads.
@branchHint(.cold);
context.initialized = true;
const seat = wm.seats.first() orelse @panic("Failed to get seat");
const river_seat_v1 = seat.river_seat_v1;
// Tag bindings
for (context.config.tag_binds.items) |tag_bind| {
comptime var i: u8 = 1;
comptime var buffer: [1]u8 = undefined;
inline while (i <= 9) : (i += 1) {
const tags: u32 = 1 << (i - 1);
buffer[0] = i + '0';
const command: XkbBindings.Command = switch (tag_bind.command) {
.set_output_tags => .{ .set_output_tags = tags },
.set_window_tags => .{ .set_window_tags = tags },
.toggle_output_tags => .{ .toggle_output_tags = tags },
.toggle_window_tags => .{ .toggle_window_tags = tags },
else => unreachable,
};
context.xkb_bindings.addBinding(river_seat_v1, @field(xkbcommon.Keysym, &buffer), tag_bind.modifiers, command);
}
}
// Rest of the keybinds
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
for (context.config.pointer_binds.items) |pointer_bind| {
const binding = river_seat_v1.getPointerBinding(pointer_bind.button, pointer_bind.modifiers) catch {
log.err("Failed to create pointer binding", .{});
continue;
};
switch (pointer_bind.action) {
.move_window => {
if (seat.move_pointer_binding) |old| old.destroy();
binding.setListener(*Seat, Seat.movePointerBindingListener, seat);
seat.move_pointer_binding = binding;
},
.resize_window => {
if (seat.resize_pointer_binding) |old| old.destroy();
binding.setListener(*Seat, Seat.resizePointerBindingListener, seat);
seat.resize_pointer_binding = binding;
},
}
binding.enable();
}
wm.initialize();
}
{
@ -147,7 +156,7 @@ fn manage_start(wm: *WindowManager) void {
river_window_manager_v1.manageFinish();
}
fn render_start(wm: *WindowManager) void {
fn render(wm: *WindowManager) void {
const river_window_manager_v1 = wm.river_window_manager_v1;
{
var it = wm.seats.iterator(.forward);
@ -171,8 +180,8 @@ fn windowManagerV1Listener(window_manager_v1: *river.WindowManagerV1, event: riv
.unavailable => {
fatal("Window manager unavailable (some other wm instance is running). Exiting", .{});
},
.manage_start => wm.manage_start(),
.render_start => wm.render_start(),
.manage_start => wm.manage(),
.render_start => wm.render(),
.output => |ev| {
const output = Output.create(context, ev.id) catch @panic("Out of memory");
wm.outputs.append(output);