Refactor initialization and Context struct

I tried to make it a little bit easier to follow and get rid of the
need to call back to context.x.y.z (as much) [I hope]
This commit is contained in:
Ben Buhse 2026-01-24 17:48:01 -06:00
commit 30231f1149
No known key found for this signature in database
GPG key ID: 7916ACFCD38FD0B4
8 changed files with 187 additions and 118 deletions

View file

@ -2,95 +2,52 @@
//
// SPDX-License-Identifier: GPL-3.0-or-later
/// Context to pass some info around.
pub const Context = struct {
allocator: mem.Allocator,
initialized: bool,
display: *wl.Display,
registry: *wl.Registry,
compositor: ?*wl.Compositor = null,
shm: ?*wl.Shm = null,
wm: WindowManager,
xkb_bindings: XkbBindings,
config: Config,
fn registryListener(registry: *wl.Registry, event: wl.Registry.Event, context: *Context) void {
switch (event) {
.global => |ev| {
if (mem.orderZ(u8, ev.interface, wl.Compositor.interface.name) == .eq) {
if (ev.version < 4) versionNotSupported(wl.Compositor, ev.version, 4);
context.compositor = registry.bind(ev.name, wl.Compositor, 4) catch |e| {
log.err("Failed to bind to compositor: {any}", .{@errorName(e)});
std.posix.exit(1);
};
} else if (mem.orderZ(u8, ev.interface, wl.Shm.interface.name) == .eq) {
context.shm = registry.bind(ev.name, wl.Shm, 1) catch |e| {
log.err("Failed to bind to shm: {any}", .{@errorName(e)});
std.posix.exit(1);
};
} else if (mem.orderZ(u8, ev.interface, river.WindowManagerV1.interface.name) == .eq) {
const window_manager_v1 = registry.bind(ev.name, river.WindowManagerV1, 3) catch |e| {
log.err("Failed to bind to window_manager_v1: {any}", .{@errorName(e)});
std.posix.exit(1);
};
context.wm.init(context, window_manager_v1);
} else if (mem.orderZ(u8, ev.interface, river.XkbBindingsV1.interface.name) == .eq) {
const xkb_bindings_v1 = registry.bind(ev.name, river.XkbBindingsV1, 2) catch |e| {
log.err("Failed to bind to xkb_bindings_v1: {any}", .{@errorName(e)});
std.posix.exit(1);
};
context.xkb_bindings.init(context, xkb_bindings_v1);
}
},
// We don't need .global_remove
.global_remove => {},
}
}
/// Wayland globals that we need to bind to
const Globals = struct {
wl_compositor: ?*wl.Compositor = null,
river_window_manager_v1: ?*river.WindowManagerV1 = null,
river_xkb_bindings_v1: ?*river.XkbBindingsV1 = null,
};
const usage: []const u8 =
\\usage: beansprout [options]
\\
\\ -i, --image <PATH> TODO TODO TODO TODO
\\ -h, --help TODO TODO TODO TODO
\\ -V, --version TODO TODO TODO TODO
\\
;
pub fn main() !void {
const allocator = std.heap.c_allocator;
const wayland_display_var = try utils.allocator.dupeZ(u8, process.getEnvVarOwned(utils.allocator, "WAYLAND_DISPLAY") catch {
log.err("Error getting WAYLAND_DISPLAY environment variable. Exiing", .{});
std.posix.exit(1);
});
defer utils.allocator.free(wayland_display_var);
const wl_display = wl.Display.connect(null) catch {
log.err("Error connecting to Wayland server. Exiting", .{});
std.posix.exit(1);
};
defer wl_display.disconnect();
const registry = try wl_display.getRegistry();
const wl_registry = try wl_display.getRegistry();
var context: Context = .{
.allocator = allocator,
.initialized = false,
.display = wl_display,
.registry = registry,
.wm = undefined,
.xkb_bindings = undefined,
// Hardcoded config for now
.config = .{ .border_width = 2, .border_color_focused = utils.parseRgbaComptime("0x89b4fa"), .border_color_unfocused = utils.parseRgbaComptime("0x1e1e2e") },
};
var globals: Globals = .{};
wl_registry.setListener(*Globals, registryListener, &globals);
registry.setListener(*Context, Context.registryListener, &context);
const errno = context.display.roundtrip();
const errno = wl_display.roundtrip();
if (errno != .SUCCESS) {
log.err("Initial roundtrip failed: E{s}", .{@tagName(errno)});
std.posix.exit(1);
}
const wl_compositor = globals.wl_compositor orelse interfaceNotAdvertised(wl.Compositor);
const river_window_manager_v1 = globals.river_window_manager_v1 orelse interfaceNotAdvertised(river.WindowManagerV1);
const river_xkb_bindings_v1 = globals.river_xkb_bindings_v1 orelse interfaceNotAdvertised(river.XkbBindingsV1);
const context = try Context.create(
wl_display,
wl_registry,
wl_compositor,
river_window_manager_v1,
river_xkb_bindings_v1,
// Hardcoded config for now
.{ .border_width = 2, .border_color_focused = utils.parseRgbaComptime("0x89b4fa"), .border_color_unfocused = utils.parseRgbaComptime("0x1e1e2e") },
);
defer context.destroy();
while (true) {
if (wl_display.dispatch() != .SUCCESS) {
log.err("wayland display dispatch failed", .{});
@ -101,6 +58,32 @@ pub fn main() !void {
log.info("Exiting beansprout", .{});
}
fn registryListener(registry: *wl.Registry, event: wl.Registry.Event, globals: *Globals) void {
switch (event) {
.global => |ev| {
if (mem.orderZ(u8, ev.interface, wl.Compositor.interface.name) == .eq) {
if (ev.version < 4) versionNotSupported(wl.Compositor, ev.version, 4);
globals.wl_compositor = registry.bind(ev.name, wl.Compositor, 4) catch |e| {
log.err("Failed to bind to compositor: {any}", .{@errorName(e)});
std.posix.exit(1);
};
} 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| {
log.err("Failed to bind to window_manager_v1: {any}", .{@errorName(e)});
std.posix.exit(1);
};
} else if (mem.orderZ(u8, ev.interface, river.XkbBindingsV1.interface.name) == .eq) {
globals.river_xkb_bindings_v1 = registry.bind(ev.name, river.XkbBindingsV1, 2) catch |e| {
log.err("Failed to bind to xkb_bindings_v1: {any}", .{@errorName(e)});
std.posix.exit(1);
};
}
},
// We don't need .global_remove
.global_remove => {},
}
}
// TODO: Actually use this...
fn interfaceNotAdvertised(comptime WaylandGlobal: type) noreturn {
log.err("{s} not advertised. Exiting", .{WaylandGlobal.interface.name});
@ -114,6 +97,7 @@ fn versionNotSupported(comptime WaylandGlobal: type, have_version: u32, need_ver
const std = @import("std");
const mem = std.mem;
const process = std.process;
const wayland = @import("wayland");
const river = wayland.client.river;
@ -121,6 +105,7 @@ const wl = wayland.client.wl;
const utils = @import("utils.zig");
const Config = @import("Config.zig");
const Context = @import("Context.zig");
const WindowManager = @import("WindowManager.zig");
const XkbBindings = @import("XkbBindings.zig");