Working on refactoring inits

This commit is contained in:
Ben Buhse 2026-01-26 08:50:52 -06:00
commit 87ec2d4f60
No known key found for this signature in database
GPG key ID: 7916ACFCD38FD0B4
5 changed files with 115 additions and 75 deletions

View file

@ -6,40 +6,50 @@ const Output = @This();
context: *Context, context: *Context,
output_v1: *river.OutputV1, river_output_v1: *river.OutputV1,
width: i32 = 0, width: i32 = 0,
height: i32 = 0, height: i32 = 0,
x: i32 = 0, x: i32 = 0,
y: i32 = 0, y: i32 = 0,
tags: u32 = 0x0001,
link: wl.list.Link, link: wl.list.Link,
pub fn init(output: *Output, context: *Context, river_output_v1: *river.OutputV1) void { pub fn create(context: *Context, river_output_v1: *river.OutputV1) !*Output {
var output = try utils.allocator.create(Output);
errdefer output.destroy();
output.* = .{ output.* = .{
.context = context, .context = context,
.output_v1 = river_output_v1, .river_output_v1 = river_output_v1,
.link = undefined, // Handled by the wl.list .link = undefined, // Handled by the wl.list
}; };
output.output_v1.setListener(*Output, outputListener, output); output.river_output_v1.setListener(*Output, outputListener, output);
return output;
}
pub fn destroy(output: *Output) void {
output.river_output_v1.destroy();
utils.allocator.destroy(output);
} }
fn outputListener(river_output_v1: *river.OutputV1, event: river.OutputV1.Event, output: *Output) void { fn outputListener(river_output_v1: *river.OutputV1, event: river.OutputV1.Event, output: *Output) void {
assert(output.output_v1 == river_output_v1); assert(output.river_output_v1 == river_output_v1);
switch (event) { switch (event) {
.removed => { .removed => output.destroy(),
river_output_v1.destroy(); .wl_output => |ev| {
utils.allocator.destroy(output); log.debug("initializing new river_output_v1 corresponding to wl_output: {d}", .{ev.name});
},
.wl_output => {
// log.debug("initializing new river_output_v1 corresponding to wl_output: {d}", .{ev.name});
}, },
.dimensions => |ev| { .dimensions => |ev| {
output.width = ev.width; output.width = ev.width;
output.height = ev.height; output.height = ev.height;
}, },
.position => |ev| { .position => |ev| {
// TODO - CONFIG: Allow setting output position (do I even need to do this?)
output.x = ev.x; output.x = ev.x;
output.y = ev.y; output.y = ev.y;
}, },

View file

@ -6,7 +6,7 @@ const Seat = @This();
context: *Context, context: *Context,
seat_v1: *river.SeatV1, river_seat_v1: *river.SeatV1,
focused: ?*Window, focused: ?*Window,
@ -25,26 +25,33 @@ pub const PendingManage = struct {
}; };
}; };
pub fn init(seat: *Seat, context: *Context, river_seat_v1: *river.SeatV1) void { pub fn create(context: *Context, river_seat_v1: *river.SeatV1) !*Seat {
var seat = try utils.allocator.create(Seat);
errdefer seat.destroy();
seat.* = .{ seat.* = .{
.context = context, .context = context,
.seat_v1 = river_seat_v1, .river_seat_v1 = river_seat_v1,
.focused = null, .focused = null,
.link = undefined, // Handled by the wl.list .link = undefined, // Handled by the wl.list
}; };
seat.seat_v1.setListener(*Seat, seatListener, seat); seat.river_seat_v1.setListener(*Seat, seatListener, seat);
return seat;
}
pub fn destroy(seat: *Seat) void {
seat.river_seat_v1.destroy();
utils.allocator.destroy(seat);
} }
fn seatListener(river_seat_v1: *river.SeatV1, event: river.SeatV1.Event, seat: *Seat) void { fn seatListener(river_seat_v1: *river.SeatV1, event: river.SeatV1.Event, seat: *Seat) void {
assert(seat.seat_v1 == river_seat_v1); assert(seat.river_seat_v1 == river_seat_v1);
switch (event) { switch (event) {
.removed => { .removed => seat.destroy(),
river_seat_v1.destroy(); .wl_seat => |ev| {
utils.allocator.destroy(seat); log.debug("initializing new river_seat_v1 corresponding to wl_seat: {d}", .{ev.name});
},
.wl_seat => {
// log.debug("initializing new river_seat_v1 corresponding to wl_seat: {d}", .{ev.name});
}, },
.pointer_enter => |ev| seat.setWindowFocus(ev.window), .pointer_enter => |ev| seat.setWindowFocus(ev.window),
.window_interaction => |ev| seat.setWindowFocus(ev.window), .window_interaction => |ev| seat.setWindowFocus(ev.window),
@ -54,8 +61,9 @@ fn seatListener(river_seat_v1: *river.SeatV1, event: river.SeatV1.Event, seat: *
} }
} }
fn setWindowFocus(seat: *Seat, window_v1: ?*river.WindowV1) void { // river_window_v1 needs to be optional because ev.window is optional
const wv1 = window_v1 orelse return; fn setWindowFocus(seat: *Seat, river_window_v1: ?*river.WindowV1) void {
const wv1 = river_window_v1 orelse return;
const window: *Window = @ptrCast(@alignCast(wv1.getUserData())); const window: *Window = @ptrCast(@alignCast(wv1.getUserData()));
seat.pending_manage.pending_focus = .{ .window = window }; seat.pending_manage.pending_focus = .{ .window = window };
} }
@ -73,7 +81,7 @@ pub fn manage(seat: *Seat) void {
} }
} }
seat.focused = window; seat.focused = window;
seat.seat_v1.focusWindow(window.window_v1); seat.river_seat_v1.focusWindow(window.river_window_v1);
window.pending_render.focused = true; window.pending_render.focused = true;
}, },
.clear_focus => { .clear_focus => {
@ -82,23 +90,23 @@ pub fn manage(seat: *Seat) void {
focused.pending_render.focused = false; focused.pending_render.focused = false;
} }
seat.focused = null; seat.focused = null;
seat.seat_v1.clearFocus(); seat.river_seat_v1.clearFocus();
}, },
} }
} }
if (seat.pending_manage.should_warp_pointer) { if (seat.pending_manage.should_warp_pointer) {
const window = seat.focused orelse { const window = seat.focused orelse {
log.err("Trying to warp pointer without focused window.", .{}); log.err("Trying to warp-on-focus-change without a focused window.", .{});
return; return;
}; };
// TODO - CONFIG: Allow disabling this behaviour // TODO - CONFIG: Allow disabling this behaviour
// Warp pointer to center of focused window; // Warp pointer to center of focused window;
// because the x and y coords are set later, we need to check them here. // because the x and y coords are set during render, we need to check if
// there are new coordinates in window.pending_render.
const pointer_x: i32 = (window.pending_render.x orelse window.x) + @divTrunc(window.width, 2); const pointer_x: i32 = (window.pending_render.x orelse window.x) + @divTrunc(window.width, 2);
const pointer_y: i32 = (window.pending_render.y orelse window.y) + @divTrunc(window.height, 2); const pointer_y: i32 = (window.pending_render.y orelse window.y) + @divTrunc(window.height, 2);
seat.seat_v1.pointerWarp(pointer_x, pointer_y); seat.river_seat_v1.pointerWarp(pointer_x, pointer_y);
log.debug("warped pointer to {}, {}", .{ pointer_x, pointer_y });
} }
} }

View file

@ -6,8 +6,8 @@ const Window = @This();
context: *Context, context: *Context,
window_v1: *river.WindowV1, river_window_v1: *river.WindowV1,
node_v1: *river.NodeV1, river_node_v1: *river.NodeV1,
width: u31 = 0, width: u31 = 0,
height: u31 = 0, height: u31 = 0,
@ -17,6 +17,9 @@ y: i32 = 0,
fullscreen: bool = false, fullscreen: bool = false,
maximized: bool = false, maximized: bool = false,
tags: u32 = 0x0001,
output: ?*Output,
initialized: bool = false, initialized: bool = false,
/// State consumed in manage() phase, reset at end of manage(). /// State consumed in manage() phase, reset at end of manage().
@ -42,23 +45,40 @@ pub const PendingRender = struct {
focused: ?bool = null, focused: ?bool = null,
}; };
pub fn init(window: *Window, context: *Context, river_window_v1: *river.WindowV1) void { pub fn create(context: *Context, river_window_v1: *river.WindowV1, output: ?*Output) !*Window {
var window = try utils.allocator.create(Window);
errdefer window.destroy();
window.* = .{ window.* = .{
.context = context, .context = context,
.window_v1 = river_window_v1, .river_window_v1 = river_window_v1,
.node_v1 = river_window_v1.getNode() catch @panic("Failed to get node"), .river_node_v1 = river_window_v1.getNode() catch @panic("Failed to get node"),
.output = output,
.link = undefined, // Handled by the wl.list .link = undefined, // Handled by the wl.list
}; };
window.window_v1.setListener(*Window, windowListener, window); if (output) |out| {
window.tags = if (out.tags != 0) out.tags else 0x0001;
} else {
window.tags = 0x0001;
}
window.river_window_v1.setListener(*Window, windowListener, window);
return window;
}
pub fn destroy(window: *Window) void {
window.river_window_v1.destroy();
window.river_node_v1.destroy();
utils.allocator.destroy(window);
} }
fn windowListener(river_window_v1: *river.WindowV1, event: river.WindowV1.Event, window: *Window) void { fn windowListener(river_window_v1: *river.WindowV1, event: river.WindowV1.Event, window: *Window) void {
assert(window.window_v1 == river_window_v1); assert(window.river_window_v1 == river_window_v1);
switch (event) { switch (event) {
.closed => { .closed => {
river_window_v1.destroy();
window.node_v1.destroy();
window.context.wm.window_count -= 1; window.context.wm.window_count -= 1;
{ {
var it = window.context.wm.seats.iterator(.forward); var it = window.context.wm.seats.iterator(.forward);
@ -80,7 +100,7 @@ fn windowListener(river_window_v1: *river.WindowV1, event: river.WindowV1.Event,
} }
} }
window.link.remove(); window.link.remove();
utils.allocator.destroy(window); window.destroy();
}, },
.dimensions => |ev| { .dimensions => |ev| {
// The protocol requires these are strictly greather than zero. // The protocol requires these are strictly greather than zero.
@ -104,10 +124,10 @@ pub fn manage(window: *Window) void {
// TODO: We might want to think about paying attention to the decoration_hint event // TODO: We might want to think about paying attention to the decoration_hint event
// If we do, this would need to move, I think? // If we do, this would need to move, I think?
window.window_v1.useSsd(); window.river_window_v1.useSsd();
window.window_v1.setCapabilities(.{ .fullscreen = true, .maximize = true }); window.river_window_v1.setCapabilities(.{ .fullscreen = true, .maximize = true });
window.window_v1.setTiled(.{ .top = true, .bottom = true, .left = true, .right = true }); window.river_window_v1.setTiled(.{ .top = true, .bottom = true, .left = true, .right = true });
} }
// Any new state since the last manage event // Any new state since the last manage event
@ -118,7 +138,7 @@ pub fn manage(window: *Window) void {
if (pending_manage.height) |new_height| { if (pending_manage.height) |new_height| {
window.width = new_width; window.width = new_width;
window.height = new_height; window.height = new_height;
window.window_v1.proposeDimensions(new_width, new_height); window.river_window_v1.proposeDimensions(new_width, new_height);
} }
} }
@ -127,19 +147,19 @@ pub fn manage(window: *Window) void {
window.fullscreen = fullscreen; window.fullscreen = fullscreen;
if (fullscreen) { if (fullscreen) {
const output = window.context.wm.outputs.first() orelse @panic("Failed to get output"); const output = window.context.wm.outputs.first() orelse @panic("Failed to get output");
window.window_v1.fullscreen(output.output_v1); window.river_window_v1.fullscreen(output.river_output_v1);
window.window_v1.informFullscreen(); window.river_window_v1.informFullscreen();
} else { } else {
window.window_v1.exitFullscreen(); window.river_window_v1.exitFullscreen();
window.window_v1.informNotFullscreen(); window.river_window_v1.informNotFullscreen();
} }
} }
if (pending_manage.maximized) |maximized| { if (pending_manage.maximized) |maximized| {
window.maximized = maximized; window.maximized = maximized;
if (maximized) { if (maximized) {
window.window_v1.informMaximized(); window.river_window_v1.informMaximized();
} else { } else {
window.window_v1.informUnmaximized(); window.river_window_v1.informUnmaximized();
} }
} }
} }
@ -154,7 +174,7 @@ pub fn render(window: *Window) void {
window.x = new_x; window.x = new_x;
window.y = new_y; window.y = new_y;
window.node_v1.setPosition(window.x, window.y); window.river_node_v1.setPosition(window.x, window.y);
} else { } else {
log.err("Window.pending_render with only x set", .{}); log.err("Window.pending_render with only x set", .{});
} }
@ -177,7 +197,7 @@ pub fn render(window: *Window) void {
fn applyBorders(window: *Window, color: utils.RiverColor) void { fn applyBorders(window: *Window, color: utils.RiverColor) void {
const border_width = window.context.config.border_width; const border_width = window.context.config.border_width;
const all_sides = river.WindowV1.Edges{ .top = true, .bottom = true, .left = true, .right = true }; const all_sides = river.WindowV1.Edges{ .top = true, .bottom = true, .left = true, .right = true };
window.window_v1.setBorders(all_sides, border_width, color.red, color.green, color.blue, color.alpha); window.river_window_v1.setBorders(all_sides, border_width, color.red, color.green, color.blue, color.alpha);
} }
const std = @import("std"); const std = @import("std");
@ -189,5 +209,6 @@ const river = wayland.client.river;
const utils = @import("utils.zig"); const utils = @import("utils.zig");
const Context = @import("Context.zig"); const Context = @import("Context.zig");
const Output = @import("Output.zig");
const log = std.log.scoped(.Window); const log = std.log.scoped(.Window);

View file

@ -42,24 +42,20 @@ pub fn create(context: *Context, window_manager_v1: *river.WindowManagerV1) !*Wi
pub fn destroy(wm: *WindowManager) void { pub fn destroy(wm: *WindowManager) void {
var window_it = wm.windows.iterator(.forward); var window_it = wm.windows.iterator(.forward);
while (window_it.next()) |window| { while (window_it.next()) |window| {
window.window_v1.destroy();
window.node_v1.destroy();
window.link.remove(); window.link.remove();
utils.allocator.destroy(window); window.destroy();
} }
var output_it = wm.outputs.iterator(.forward); var output_it = wm.outputs.iterator(.forward);
while (output_it.next()) |output| { while (output_it.next()) |output| {
output.output_v1.destroy();
output.link.remove(); output.link.remove();
utils.allocator.destroy(output); output.destroy();
} }
var seat_it = wm.seats.iterator(.forward); var seat_it = wm.seats.iterator(.forward);
while (seat_it.next()) |seat| { while (seat_it.next()) |seat| {
seat.seat_v1.destroy();
seat.link.remove(); seat.link.remove();
utils.allocator.destroy(seat); seat.destroy();
} }
utils.allocator.destroy(wm); utils.allocator.destroy(wm);
@ -164,7 +160,6 @@ fn windowManagerV1Listener(window_manager_v1: *river.WindowManagerV1, event: riv
std.posix.exit(1); std.posix.exit(1);
}, },
.manage_start => { .manage_start => {
log.debug("manage start", .{});
if (!context.initialized) { if (!context.initialized) {
// This code (should) only be run once while initializing the WM, so let's // This code (should) only be run once while initializing the WM, so let's
// mark it as cold. // mark it as cold.
@ -172,7 +167,7 @@ fn windowManagerV1Listener(window_manager_v1: *river.WindowManagerV1, event: riv
context.initialized = true; context.initialized = true;
const seat = wm.seats.first() orelse @panic("Failed to get seat"); const seat = wm.seats.first() orelse @panic("Failed to get seat");
const river_seat_v1 = seat.seat_v1; const river_seat_v1 = seat.river_seat_v1;
context.xkb_bindings.addBinding(river_seat_v1, xkbcommon.Keysym.t, .{ .mod4 = true }, .{ .spawn = &.{"foot"} }); context.xkb_bindings.addBinding(river_seat_v1, xkbcommon.Keysym.t, .{ .mod4 = true }, .{ .spawn = &.{"foot"} });
context.xkb_bindings.addBinding(river_seat_v1, xkbcommon.Keysym.j, .{ .mod4 = true }, .focus_next); context.xkb_bindings.addBinding(river_seat_v1, xkbcommon.Keysym.j, .{ .mod4 = true }, .focus_next);
context.xkb_bindings.addBinding(river_seat_v1, xkbcommon.Keysym.k, .{ .mod4 = true }, .focus_prev); context.xkb_bindings.addBinding(river_seat_v1, xkbcommon.Keysym.k, .{ .mod4 = true }, .focus_prev);
@ -202,10 +197,8 @@ fn windowManagerV1Listener(window_manager_v1: *river.WindowManagerV1, event: riv
} }
} }
window_manager_v1.manageFinish(); window_manager_v1.manageFinish();
log.debug("manage end===================", .{});
}, },
.render_start => { .render_start => {
log.debug("render start", .{});
{ {
var it = wm.seats.iterator(.forward); var it = wm.seats.iterator(.forward);
while (it.next()) |seat| { while (it.next()) |seat| {
@ -225,12 +218,11 @@ fn windowManagerV1Listener(window_manager_v1: *river.WindowManagerV1, event: riv
} }
} }
window_manager_v1.renderFinish(); window_manager_v1.renderFinish();
log.debug("render end==================", .{});
}, },
.output => |ev| { .output => |ev| {
log.debug("initializing new wl_output: {d}", .{ev.id.getId()});
// TODO: Support multi-output // TODO: Support multi-output
var output = utils.allocator.create(Output) catch @panic("Out of memory"); const output = Output.create(context, ev.id) catch @panic("Out of memory");
output.init(context, ev.id);
wm.outputs.append(output); wm.outputs.append(output);
}, },
.seat => |ev| { .seat => |ev| {
@ -242,17 +234,17 @@ fn windowManagerV1Listener(window_manager_v1: *river.WindowManagerV1, event: riv
utils.versionNotSupported(river.SeatV1, river_seat_v1_version, MIN_RIVER_SEAT_V1_VERSION); utils.versionNotSupported(river.SeatV1, river_seat_v1_version, MIN_RIVER_SEAT_V1_VERSION);
} }
var seat = utils.allocator.create(Seat) catch @panic("Out of memory"); const seat = Seat.create(context, river_seat_v1) catch @panic("Out of memory");
seat.init(context, river_seat_v1);
wm.seats.append(seat); wm.seats.append(seat);
}, },
.window => |ev| { .window => |ev| {
var window = utils.allocator.create(Window) catch @panic("Out of memory"); const seat = wm.seats.first() orelse @panic("Failed to get seat");
window.init(context, ev.id); // TODO: Support multiple outputs
const output = wm.outputs.first() orelse @panic("Failed to get output");
const window = Window.create(context, ev.id, output) catch @panic("Out of memory");
// TODO - CONFIG: Allow appending window instead of prepending // TODO - CONFIG: Allow appending window instead of prepending
wm.windows.prepend(window); wm.windows.prepend(window);
const seat = wm.seats.first() orelse @panic("Failed to get seat");
seat.pending_manage.pending_focus = .{ .window = window }; seat.pending_manage.pending_focus = .{ .window = window };
seat.pending_manage.should_warp_pointer = true; seat.pending_manage.should_warp_pointer = true;

View file

@ -19,7 +19,10 @@ const XkbBinding = struct {
context: *Context, context: *Context,
link: wl.list.Link, link: wl.list.Link,
fn init(xkb_binding: *XkbBinding, xkb_binding_v1: *river.XkbBindingV1, command: Command, context: *Context) void { fn create(xkb_binding_v1: *river.XkbBindingV1, command: Command, context: *Context) !*XkbBinding {
var xkb_binding = try utils.allocator.create(XkbBinding);
errdefer xkb_binding.destroy();
xkb_binding.* = .{ xkb_binding.* = .{
.xkb_binding_v1 = xkb_binding_v1, .xkb_binding_v1 = xkb_binding_v1,
.command = command, .command = command,
@ -28,6 +31,13 @@ const XkbBinding = struct {
}; };
xkb_binding.xkb_binding_v1.setListener(*XkbBinding, xkbBindingListener, xkb_binding); xkb_binding.xkb_binding_v1.setListener(*XkbBinding, xkbBindingListener, xkb_binding);
return xkb_binding;
}
fn destroy(xkb_binding: *XkbBinding) void {
xkb_binding.xkb_binding_v1.destroy();
utils.allocator.destroy(xkb_binding);
} }
fn xkbBindingListener(xkb_binding_v1: *river.XkbBindingV1, event: river.XkbBindingV1.Event, xkb_binding: *XkbBinding) void { fn xkbBindingListener(xkb_binding_v1: *river.XkbBindingV1, event: river.XkbBindingV1.Event, xkb_binding: *XkbBinding) void {
@ -93,7 +103,7 @@ const XkbBinding = struct {
.close_window => { .close_window => {
const seat = context.wm.seats.first() orelse return; const seat = context.wm.seats.first() orelse return;
if (seat.focused) |window| { if (seat.focused) |window| {
window.window_v1.close(); window.river_window_v1.close();
} }
}, },
.exit => { .exit => {
@ -141,8 +151,7 @@ pub fn addBinding(xkb_bindings: *XkbBindings, river_seat_v1: *river.SeatV1, keys
return; return;
}; };
const xkb_binding = utils.allocator.create(XkbBinding) catch @panic("Out of memory"); const xkb_binding = XkbBinding.create(xkb_binding_v1, command, xkb_bindings.context) catch @panic("Out of memory");
xkb_binding.init(xkb_binding_v1, command, xkb_bindings.context);
xkb_bindings.bindings.append(xkb_binding); xkb_bindings.bindings.append(xkb_binding);
xkb_binding_v1.enable(); xkb_binding_v1.enable();