oxwm

https://git.tonybtw.com/oxwm.git git://git.tonybtw.com/oxwm.git

refactor: get rid of `config` and `config_path_global` globals in main module

Commit
5acaef31838a86fc11df2478fdad3a143aaf9f42
Parent
2cbdc67
Author
emzywastaken <amiamemetoo@gmail.com>
Date
2026-02-21 20:38:35
`initialize_default_config()` now takes `*config_mod.Config`

Diff

diff --git a/src/main.zig b/src/main.zig
index 33bce64..4cabe96 100644
--- a/src/main.zig
+++ b/src/main.zig
@@ -25,11 +25,6 @@ const IconicState: c_long = 3;
 const IsViewable: c_int = 2;
 const snap_distance: i32 = 32;
 
-/// Config built from Lua, passed into `WindowManager.init`.
-var config: config_mod.Config = undefined;
-/// Path to the loaded config file.
-var config_path_global: ?[]const u8 = null;
-
 /// File descriptor of the X11 connection, used in spawn_child_setup.
 /// Set from `wm.x11_fd` after init.
 var x11_fd: c_int = -1;
@@ -109,7 +104,7 @@ fn init_config(allocator: std.mem.Allocator) void {
 }
 
 fn validate_config(allocator: std.mem.Allocator, config_path: []const u8) !void {
-    config = config_mod.Config.init(allocator);
+    var config = config_mod.Config.init(allocator);
     defer config.deinit();
 
     if (!lua.init(&config)) {
@@ -168,7 +163,8 @@ pub fn main() !void {
 
     std.debug.print("oxwm starting\n", .{});
 
-    config = config_mod.Config.init(allocator);
+    var config = config_mod.Config.init(allocator);
+    var config_path_global: ?[]const u8 = null;
 
     if (lua.init(&config)) {
         const loaded = if (std.fs.cwd().statFile(config_path)) |_|
@@ -183,11 +179,11 @@ pub fn main() !void {
             std.debug.print("loaded config from {s}\n", .{config_path});
         } else {
             std.debug.print("no config found, using defaults\n", .{});
-            initialize_default_config();
+            initialize_default_config(&config);
         }
     } else {
         std.debug.print("failed to init lua, using defaults\n", .{});
-        initialize_default_config();
+        initialize_default_config(&config);
     }
 
     var wm = WindowManager.init(allocator, config, config_path_global) catch |err| {
@@ -196,8 +192,8 @@ pub fn main() !void {
     };
     defer wm.deinit();
 
-    // Propagate values that transitional code in this file still reads
-    // from module-level vars. These will be removed soon.
+    // x11_fd is read by spawn_child_setup() which runs in a forked child
+    // and cannot access wm directly.
     x11_fd = wm.x11_fd;
 
     std.debug.print("display opened: screen={d} root=0x{x}\n", .{ wm.display.screen, wm.display.root });
@@ -207,7 +203,7 @@ pub fn main() !void {
     grab_keybinds(&wm.display, &wm);
     scan_existing_windows(&wm.display, &wm);
 
-    try run_autostart_commands(allocator, config.autostart.items);
+    try run_autostart_commands(allocator, wm.config.autostart.items);
     std.debug.print("entering event loop\n", .{});
     run_event_loop(&wm);
 
@@ -234,45 +230,45 @@ fn make_keybind_str(mod: u32, key: u64, action: config_mod.Action, str_arg: []co
     return kb;
 }
 
-fn initialize_default_config() void {
+fn initialize_default_config(cfg: *config_mod.Config) void {
     const mod_key: u32 = 1 << 6;
     const shift_key: u32 = 1 << 0;
     const control_key: u32 = 1 << 2;
 
-    config.add_keybind(make_keybind(mod_key, 0xff0d, .spawn_terminal)) catch {};
-    config.add_keybind(make_keybind_str(mod_key, 'd', .spawn, "rofi -show drun")) catch {};
-    config.add_keybind(make_keybind_str(mod_key, 's', .spawn, "maim -s | xclip -selection clipboard -t image/png")) catch {};
-    config.add_keybind(make_keybind(mod_key, 'q', .kill_client)) catch {};
-    config.add_keybind(make_keybind(mod_key | shift_key, 'q', .quit)) catch {};
-    config.add_keybind(make_keybind(mod_key | shift_key, 'r', .reload_config)) catch {};
-    config.add_keybind(make_keybind(mod_key, 'j', .focus_next)) catch {};
-    config.add_keybind(make_keybind(mod_key, 'k', .focus_prev)) catch {};
-    config.add_keybind(make_keybind(mod_key | shift_key, 'j', .move_next)) catch {};
-    config.add_keybind(make_keybind(mod_key | shift_key, 'k', .move_prev)) catch {};
-    config.add_keybind(make_keybind_int(mod_key, 'h', .resize_master, -50)) catch {};
-    config.add_keybind(make_keybind_int(mod_key, 'l', .resize_master, 50)) catch {};
-    config.add_keybind(make_keybind(mod_key, 'i', .inc_master)) catch {};
-    config.add_keybind(make_keybind(mod_key, 'p', .dec_master)) catch {};
-    config.add_keybind(make_keybind(mod_key, 'a', .toggle_gaps)) catch {};
-    config.add_keybind(make_keybind(mod_key, 'f', .toggle_fullscreen)) catch {};
-    config.add_keybind(make_keybind(mod_key, 0x0020, .toggle_floating)) catch {};
-    config.add_keybind(make_keybind(mod_key, 'n', .cycle_layout)) catch {};
-    config.add_keybind(make_keybind_int(mod_key, 0x002c, .focus_monitor, -1)) catch {};
-    config.add_keybind(make_keybind_int(mod_key, 0x002e, .focus_monitor, 1)) catch {};
-    config.add_keybind(make_keybind_int(mod_key | shift_key, 0x002c, .send_to_monitor, -1)) catch {};
-    config.add_keybind(make_keybind_int(mod_key | shift_key, 0x002e, .send_to_monitor, 1)) catch {};
+    cfg.add_keybind(make_keybind(mod_key, 0xff0d, .spawn_terminal)) catch {};
+    cfg.add_keybind(make_keybind_str(mod_key, 'd', .spawn, "rofi -show drun")) catch {};
+    cfg.add_keybind(make_keybind_str(mod_key, 's', .spawn, "maim -s | xclip -selection clipboard -t image/png")) catch {};
+    cfg.add_keybind(make_keybind(mod_key, 'q', .kill_client)) catch {};
+    cfg.add_keybind(make_keybind(mod_key | shift_key, 'q', .quit)) catch {};
+    cfg.add_keybind(make_keybind(mod_key | shift_key, 'r', .reload_config)) catch {};
+    cfg.add_keybind(make_keybind(mod_key, 'j', .focus_next)) catch {};
+    cfg.add_keybind(make_keybind(mod_key, 'k', .focus_prev)) catch {};
+    cfg.add_keybind(make_keybind(mod_key | shift_key, 'j', .move_next)) catch {};
+    cfg.add_keybind(make_keybind(mod_key | shift_key, 'k', .move_prev)) catch {};
+    cfg.add_keybind(make_keybind_int(mod_key, 'h', .resize_master, -50)) catch {};
+    cfg.add_keybind(make_keybind_int(mod_key, 'l', .resize_master, 50)) catch {};
+    cfg.add_keybind(make_keybind(mod_key, 'i', .inc_master)) catch {};
+    cfg.add_keybind(make_keybind(mod_key, 'p', .dec_master)) catch {};
+    cfg.add_keybind(make_keybind(mod_key, 'a', .toggle_gaps)) catch {};
+    cfg.add_keybind(make_keybind(mod_key, 'f', .toggle_fullscreen)) catch {};
+    cfg.add_keybind(make_keybind(mod_key, 0x0020, .toggle_floating)) catch {};
+    cfg.add_keybind(make_keybind(mod_key, 'n', .cycle_layout)) catch {};
+    cfg.add_keybind(make_keybind_int(mod_key, 0x002c, .focus_monitor, -1)) catch {};
+    cfg.add_keybind(make_keybind_int(mod_key, 0x002e, .focus_monitor, 1)) catch {};
+    cfg.add_keybind(make_keybind_int(mod_key | shift_key, 0x002c, .send_to_monitor, -1)) catch {};
+    cfg.add_keybind(make_keybind_int(mod_key | shift_key, 0x002e, .send_to_monitor, 1)) catch {};
 
     var tag_index: i32 = 0;
     while (tag_index < 9) : (tag_index += 1) {
         const keysym: u64 = @as(u64, '1') + @as(u64, @intCast(tag_index));
-        config.add_keybind(make_keybind_int(mod_key, keysym, .view_tag, tag_index)) catch {};
-        config.add_keybind(make_keybind_int(mod_key | shift_key, keysym, .move_to_tag, tag_index)) catch {};
-        config.add_keybind(make_keybind_int(mod_key | control_key, keysym, .toggle_view_tag, tag_index)) catch {};
-        config.add_keybind(make_keybind_int(mod_key | control_key | shift_key, keysym, .toggle_tag, tag_index)) catch {};
+        cfg.add_keybind(make_keybind_int(mod_key, keysym, .view_tag, tag_index)) catch {};
+        cfg.add_keybind(make_keybind_int(mod_key | shift_key, keysym, .move_to_tag, tag_index)) catch {};
+        cfg.add_keybind(make_keybind_int(mod_key | control_key, keysym, .toggle_view_tag, tag_index)) catch {};
+        cfg.add_keybind(make_keybind_int(mod_key | control_key | shift_key, keysym, .toggle_tag, tag_index)) catch {};
     }
 
-    config.add_button(.{ .click = .client_win, .mod_mask = mod_key, .button = 1, .action = .move_mouse }) catch {};
-    config.add_button(.{ .click = .client_win, .mod_mask = mod_key, .button = 3, .action = .resize_mouse }) catch {};
+    cfg.add_button(.{ .click = .client_win, .mod_mask = mod_key, .button = 1, .action = .move_mouse }) catch {};
+    cfg.add_button(.{ .click = .client_win, .mod_mask = mod_key, .button = 3, .action = .resize_mouse }) catch {};
 }
 
 fn grab_keybinds(display: *Display, wm: *WindowManager) void {
@@ -281,7 +277,7 @@ fn grab_keybinds(display: *Display, wm: *WindowManager) void {
 
     _ = xlib.XUngrabKey(display.handle, xlib.AnyKey, xlib.AnyModifier, display.root);
 
-    for (config.keybinds.items) |keybind| {
+    for (wm.config.keybinds.items) |keybind| {
         if (keybind.key_count == 0) continue;
         const first_key = keybind.keys[0];
         const keycode = xlib.XKeysymToKeycode(display.handle, @intCast(first_key.keysym));
@@ -300,7 +296,7 @@ fn grab_keybinds(display: *Display, wm: *WindowManager) void {
         }
     }
 
-    for (config.buttons.items) |button| {
+    for (wm.config.buttons.items) |button| {
         if (button.click == .client_win) {
             for (modifiers) |modifier| {
                 _ = xlib.XGrabButton(
@@ -319,7 +315,7 @@ fn grab_keybinds(display: *Display, wm: *WindowManager) void {
         }
     }
 
-    std.debug.print("grabbed {d} keybinds from config\n", .{config.keybinds.items.len});
+    std.debug.print("grabbed {d} keybinds from config\n", .{wm.config.keybinds.items.len});
 }
 
 fn get_state(display: *Display, window: xlib.Window, wm: *WindowManager) c_long {
@@ -484,7 +480,7 @@ fn manage(display: *Display, win: xlib.Window, window_attrs: *xlib.XWindowAttrib
     client.old_width = window_attrs.width;
     client.old_height = window_attrs.height;
     client.old_border_width = window_attrs.border_width;
-    client.border_width = config.border_width;
+    client.border_width = wm.config.border_width;
 
     update_title(display, client, wm);
 
@@ -512,7 +508,7 @@ fn manage(display: *Display, win: xlib.Window, window_attrs: *xlib.XWindowAttrib
     client.y = @max(client.y, monitor.win_y);
 
     _ = xlib.XSetWindowBorderWidth(display.handle, win, @intCast(client.border_width));
-    _ = xlib.XSetWindowBorder(display.handle, win, config.border_unfocused);
+    _ = xlib.XSetWindowBorder(display.handle, win, wm.config.border_unfocused);
     tiling.send_configure(client);
 
     update_window_type(display, client, wm);
@@ -627,7 +623,7 @@ fn handle_key_press(display: *Display, event: *xlib.XKeyEvent, wm: *WindowManage
 
     _ = wm.chord.push(.{ .mod_mask = clean_state, .keysym = keysym });
 
-    for (config.keybinds.items) |keybind| {
+    for (wm.config.keybinds.items) |keybind| {
         if (keybind.key_count == 0) continue;
 
         if (keybind.key_count == wm.chord.index) {
@@ -649,7 +645,7 @@ fn handle_key_press(display: *Display, event: *xlib.XKeyEvent, wm: *WindowManage
     }
 
     var has_partial_match = false;
-    for (config.keybinds.items) |keybind| {
+    for (wm.config.keybinds.items) |keybind| {
         if (keybind.key_count > wm.chord.index) {
             var matches = true;
             for (0..wm.chord.index) |i| {
@@ -676,7 +672,7 @@ fn handle_key_press(display: *Display, event: *xlib.XKeyEvent, wm: *WindowManage
 
 fn execute_action(display: *Display, action: config_mod.Action, int_arg: i32, str_arg: ?[]const u8, wm: *WindowManager) void {
     switch (action) {
-        .spawn_terminal => spawn_terminal(),
+        .spawn_terminal => spawn_terminal(wm),
         .spawn => {
             if (str_arg) |cmd| spawn_command(cmd);
         },
@@ -741,28 +737,28 @@ fn reload_config(display: *Display, wm: *WindowManager) void {
 
     ungrab_keybinds(display);
 
-    config.keybinds.clearRetainingCapacity();
-    config.buttons.clearRetainingCapacity();
-    config.rules.clearRetainingCapacity();
-    config.blocks.clearRetainingCapacity();
+    wm.config.keybinds.clearRetainingCapacity();
+    wm.config.buttons.clearRetainingCapacity();
+    wm.config.rules.clearRetainingCapacity();
+    wm.config.blocks.clearRetainingCapacity();
 
     lua.deinit();
-    _ = lua.init(&config);
+    _ = lua.init(&wm.config);
 
-    const loaded = if (config_path_global) |path|
+    const loaded = if (wm.config_path) |path|
         lua.load_file(path)
     else
         lua.load_config();
 
     if (loaded) {
-        if (config_path_global) |path| {
+        if (wm.config_path) |path| {
             std.debug.print("reloaded config from {s}\n", .{path});
         } else {
             std.debug.print("reloaded config from ~/.config/oxwm/config.lua\n", .{});
         }
     } else {
         std.debug.print("reload failed, restoring defaults\n", .{});
-        initialize_default_config();
+        initialize_default_config(&wm.config);
     }
 
     bar_mod.destroy_bars(wm.bars, gpa.allocator(), display.handle);
@@ -814,12 +810,13 @@ fn spawn_command(cmd: []const u8) void {
     }
 }
 
-fn spawn_terminal() void {
+// TODO: take in the terminal cmd directly.
+fn spawn_terminal(wm: *WindowManager) void {
     const pid = std.posix.fork() catch return;
     if (pid == 0) {
         spawn_child_setup();
         var term_buf: [256]u8 = undefined;
-        const terminal = config.terminal;
+        const terminal = wm.config.terminal;
         if (terminal.len >= term_buf.len) {
             std.posix.exit(1);
         }
@@ -940,10 +937,10 @@ fn toggle_client_tag(display: *Display, tag_mask: u32, wm: *WindowManager) void
 fn toggle_gaps(wm: *WindowManager) void {
     const monitor = monitor_mod.selected_monitor orelse return;
     if (monitor.gap_inner_h == 0) {
-        monitor.gap_inner_h = config.gap_inner_h;
-        monitor.gap_inner_v = config.gap_inner_v;
-        monitor.gap_outer_h = config.gap_outer_h;
-        monitor.gap_outer_v = config.gap_outer_v;
+        monitor.gap_inner_h = wm.config.gap_inner_h;
+        monitor.gap_inner_v = wm.config.gap_inner_v;
+        monitor.gap_outer_h = wm.config.gap_outer_h;
+        monitor.gap_outer_v = wm.config.gap_outer_v;
     } else {
         monitor.gap_inner_h = 0;
         monitor.gap_inner_v = 0;
@@ -1432,7 +1429,7 @@ fn movemouse(display: *Display, wm: *WindowManager) void {
         arrange(monitor, wm);
     }
 
-    if (config.auto_tile and !was_floating) {
+    if (wm.config.auto_tile and !was_floating) {
         const drop_monitor = client.monitor orelse return;
         const center_x = client.x + @divTrunc(client.width, 2);
         const center_y = client.y + @divTrunc(client.height, 2);
@@ -1572,7 +1569,7 @@ fn handle_button_press(display: *Display, event: *xlib.XButtonEvent, wm: *Window
     }
 
     const clean_state = clean_mask(event.state, wm);
-    for (config.buttons.items) |button| {
+    for (wm.config.buttons.items) |button| {
         if (button.click != .client_win) continue;
         const button_clean_mask = clean_mask(button.mod_mask, wm);
         if (clean_state == button_clean_mask and event.button == button.button) {
@@ -1770,7 +1767,7 @@ fn handle_property_notify(display: *Display, event: *xlib.XPropertyEvent, wm: *W
 fn unfocus_client(display: *Display, client: ?*Client, reset_input_focus: bool, wm: *WindowManager) void {
     const unfocus_target = client orelse return;
     grabbuttons(display, unfocus_target, false, wm);
-    _ = xlib.XSetWindowBorder(display.handle, unfocus_target.window, config.border_unfocused);
+    _ = xlib.XSetWindowBorder(display.handle, unfocus_target.window, wm.config.border_unfocused);
     if (reset_input_focus) {
         _ = xlib.XSetInputFocus(display.handle, display.root, xlib.RevertToPointerRoot, xlib.CurrentTime);
         _ = xlib.XDeleteProperty(display.handle, display.root, wm.atoms.net_active_window);
@@ -1821,7 +1818,7 @@ fn grabbuttons(display: *Display, client: *Client, focused: bool, wm: *WindowMan
             xlib.None,
         );
     }
-    for (config.buttons.items) |button| {
+    for (wm.config.buttons.items) |button| {
         if (button.click == .client_win) {
             for (modifiers) |modifier| {
                 _ = xlib.XGrabButton(
@@ -1867,7 +1864,7 @@ fn focus(display: *Display, target_client: ?*Client, wm: *WindowManager) void {
         client_mod.detach_stack(client);
         client_mod.attach_stack(client);
         grabbuttons(display, client, true, wm);
-        _ = xlib.XSetWindowBorder(display.handle, client.window, config.border_focused);
+        _ = xlib.XSetWindowBorder(display.handle, client.window, wm.config.border_focused);
         if (!client.never_focus) {
             _ = xlib.XSetInputFocus(display.handle, client.window, xlib.RevertToPointerRoot, xlib.CurrentTime);
             _ = xlib.XChangeProperty(display.handle, display.root, wm.atoms.net_active_window, xlib.XA_WINDOW, 32, xlib.PropModeReplace, @ptrCast(&client.window), 1);
@@ -2151,7 +2148,7 @@ fn apply_rules(display: *Display, client: *Client, wm: *WindowManager) void {
     client.tags = 0;
     var rule_focus = false;
 
-    for (config.rules.items) |rule| {
+    for (wm.config.rules.items) |rule| {
         const class_matches = if (rule.class) |rc| std.mem.indexOf(u8, class_str, rc) != null else true;
         const instance_matches = if (rule.instance) |ri| std.mem.indexOf(u8, instance_str, ri) != null else true;
         const title_matches = if (rule.title) |rt| std.mem.indexOf(u8, std.mem.sliceTo(&client.name, 0), rt) != null else true;