oxwm

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

refactor(client): remove allocator global, document functions and `window_to_client` now takes a monitors list

Commit
c7d9989d6ce32ef3e510168640c089d2c872ba36
Parent
5660dcb
Author
emzywastaken <amiamemetoo@gmail.com>
Date
2026-02-21 00:35:47
- Removed the allocator global and `init()` function. create and destroy
now take allocator as a parameter.
- window_to_client no longer does a hidden `@import("monitor.zig")` to
reach the global. It now takes `monitors: ?*Monitor` as its first
parameter.

Diff

diff --git a/src/client.zig b/src/client.zig
index c92c51d..0c47ed3 100644
--- a/src/client.zig
+++ b/src/client.zig
@@ -38,22 +38,19 @@ pub const Client = struct {
     window: xlib.Window = 0,
 };
 
-var allocator: std.mem.Allocator = undefined;
-
-pub fn init(alloc: std.mem.Allocator) void {
-    allocator = alloc;
-}
-
-pub fn create(window: xlib.Window) ?*Client {
+/// Initialises a new `Client` for the given X window.
+pub fn create(allocator: std.mem.Allocator, window: xlib.Window) ?*Client {
     const client = allocator.create(Client) catch return null;
     client.* = Client{ .window = window };
     return client;
 }
 
-pub fn destroy(client: *Client) void {
+/// Frees a client previously returned by `create`.
+pub fn destroy(allocator: std.mem.Allocator, client: *Client) void {
     allocator.destroy(client);
 }
 
+/// Prepends `client` to the front of its monitor's client list.
 pub fn attach(client: *Client) void {
     if (client.monitor) |monitor| {
         client.next = monitor.clients;
@@ -61,6 +58,7 @@ pub fn attach(client: *Client) void {
     }
 }
 
+/// Removes `client` from its monitor's client list.
 pub fn detach(client: *Client) void {
     if (client.monitor) |monitor| {
         var current_ptr: *?*Client = &monitor.clients;
@@ -74,6 +72,7 @@ pub fn detach(client: *Client) void {
     }
 }
 
+/// Prepends `client` to the front of its monitor's focus stack.
 pub fn attach_stack(client: *Client) void {
     if (client.monitor) |monitor| {
         client.stack_next = monitor.stack;
@@ -81,6 +80,7 @@ pub fn attach_stack(client: *Client) void {
     }
 }
 
+/// Removes `client` from its monitor's focus stack.
 pub fn detach_stack(client: *Client) void {
     if (client.monitor) |monitor| {
         var current_ptr: *?*Client = &monitor.stack;
@@ -94,15 +94,15 @@ pub fn detach_stack(client: *Client) void {
     }
 }
 
-pub fn window_to_client(window: xlib.Window) ?*Client {
-    const monitor_mod = @import("monitor.zig");
-    var current_monitor = monitor_mod.monitors;
+/// Searches all monitors for a client whose X window matches `window`.
+///
+/// `monitors` is the head of the monitor linked list.
+pub fn window_to_client(monitors: ?*Monitor, window: xlib.Window) ?*Client {
+    var current_monitor = monitors;
     while (current_monitor) |monitor| {
         var current_client = monitor.clients;
         while (current_client) |client| {
-            if (client.window == window) {
-                return client;
-            }
+            if (client.window == window) return client;
             current_client = client.next;
         }
         current_monitor = monitor.next;
@@ -110,17 +110,8 @@ pub fn window_to_client(window: xlib.Window) ?*Client {
     return null;
 }
 
-pub fn next_tiled(client: ?*Client) ?*Client {
-    var current = client;
-    while (current) |iter| {
-        if (!iter.is_floating and is_visible(iter)) {
-            return iter;
-        }
-        current = iter.next;
-    }
-    return null;
-}
-
+/// Returns true if `client` is visible on its monitor's currently selected
+/// tag set.
 pub fn is_visible(client: *Client) bool {
     if (client.monitor) |monitor| {
         return (client.tags & monitor.tagset[monitor.sel_tags]) != 0;
@@ -128,22 +119,35 @@ pub fn is_visible(client: *Client) bool {
     return false;
 }
 
+/// Returns true if `client` is visible on the given tag bitmask.
 pub fn is_visible_on_tag(client: *Client, tags: u32) bool {
     return (client.tags & tags) != 0;
 }
 
+/// Returns the first non-floating, visible client at or after `client`.
+pub fn next_tiled(client: ?*Client) ?*Client {
+    var current = client;
+    while (current) |iter| {
+        if (!iter.is_floating and is_visible(iter)) return iter;
+        current = iter.next;
+    }
+    return null;
+}
+
+/// Returns the first non-floating client on `client`'s monitor that shares
+/// any tag with `client`. Used for `attach_aside` ordering.
 pub fn next_tagged(client: *Client) ?*Client {
     const monitor = client.monitor orelse return null;
     var walked = monitor.clients;
     while (walked) |iter| {
-        if (!iter.is_floating and is_visible_on_tag(iter, client.tags)) {
-            return iter;
-        }
+        if (!iter.is_floating and is_visible_on_tag(iter, client.tags)) return iter;
         walked = iter.next;
     }
     return null;
 }
 
+/// Inserts `client` just after the first client that shares its tags,
+/// falling back to prepend if none exists.
 pub fn attach_aside(client: *Client) void {
     const at = next_tagged(client);
     if (at == null) {
@@ -154,6 +158,7 @@ pub fn attach_aside(client: *Client) void {
     at.?.next = client;
 }
 
+/// Counts non-floating, visible clients on `monitor`.
 pub fn count_tiled(monitor: *Monitor) u32 {
     var count: u32 = 0;
     var current = next_tiled(monitor.clients);
@@ -164,19 +169,19 @@ pub fn count_tiled(monitor: *Monitor) u32 {
     return count;
 }
 
+/// Returns the tiled client on `monitor` whose bounds contain (`point_x`,
+/// `point_y`), excluding `exclude`.  Returns null if none found.
 pub fn tiled_window_at(exclude: *Client, monitor: *Monitor, point_x: i32, point_y: i32) ?*Client {
     const tags = monitor.tagset[monitor.sel_tags];
     var current = monitor.clients;
 
     while (current) |client| {
         if (client != exclude and !client.is_floating and (client.tags & tags) != 0) {
-            const client_x = client.x;
-            const client_y = client.y;
             const client_w = client.width + client.border_width * 2;
             const client_h = client.height + client.border_width * 2;
 
-            if (point_x >= client_x and point_x < client_x + client_w and
-                point_y >= client_y and point_y < client_y + client_h)
+            if (point_x >= client.x and point_x < client.x + client_w and
+                point_y >= client.y and point_y < client.y + client_h)
             {
                 return client;
             }
@@ -186,6 +191,8 @@ pub fn tiled_window_at(exclude: *Client, monitor: *Monitor, point_x: i32, point_
     return null;
 }
 
+/// Moves `client` to just before `target` in the monitor's client list.
+/// Does nothing if they are on different monitors.
 pub fn insert_before(client: *Client, target: *Client) void {
     const monitor = target.monitor orelse return;
     if (client.monitor != monitor) return;
@@ -209,6 +216,8 @@ pub fn insert_before(client: *Client, target: *Client) void {
     }
 }
 
+/// Swaps the positions of `client_a` and `client_b` in their shared monitor's
+/// client list.  Does nothing if they are on different monitors.
 pub fn swap_clients(client_a: *Client, client_b: *Client) void {
     const monitor = client_a.monitor orelse return;
     if (client_b.monitor != monitor) return;
@@ -229,31 +238,15 @@ pub fn swap_clients(client_a: *Client, client_b: *Client) void {
     if (next_a == client_b) {
         client_a.next = next_b;
         client_b.next = client_a;
-        if (prev_a) |prev| {
-            prev.next = client_b;
-        } else {
-            monitor.clients = client_b;
-        }
+        if (prev_a) |prev| prev.next = client_b else monitor.clients = client_b;
     } else if (next_b == client_a) {
         client_b.next = next_a;
         client_a.next = client_b;
-        if (prev_b) |prev| {
-            prev.next = client_a;
-        } else {
-            monitor.clients = client_a;
-        }
+        if (prev_b) |prev| prev.next = client_a else monitor.clients = client_a;
     } else {
         client_a.next = next_b;
         client_b.next = next_a;
-        if (prev_a) |prev| {
-            prev.next = client_b;
-        } else {
-            monitor.clients = client_b;
-        }
-        if (prev_b) |prev| {
-            prev.next = client_a;
-        } else {
-            monitor.clients = client_a;
-        }
+        if (prev_a) |prev| prev.next = client_b else monitor.clients = client_b;
+        if (prev_b) |prev| prev.next = client_a else monitor.clients = client_a;
     }
 }
diff --git a/src/main.zig b/src/main.zig
index d5811e1..faf0b6d 100644
--- a/src/main.zig
+++ b/src/main.zig
@@ -249,7 +249,6 @@ pub fn main() !void {
 
     cursors = Cursors.init(&display);
     _ = xlib.XDefineCursor(display.handle, display.root, cursors.normal);
-    client_mod.init(allocator);
     monitor_mod.init(allocator);
     tiling.set_display(display.handle);
     tiling.set_screen_size(display.screen_width(), display.screen_height());
@@ -697,7 +696,7 @@ fn handle_map_request(display: *Display, event: *xlib.XMapRequestEvent) void {
     if (window_attributes.override_redirect != 0) {
         return;
     }
-    if (client_mod.window_to_client(event.window) != null) {
+    if (client_mod.window_to_client(monitor_mod.monitors, event.window) != null) {
         return;
     }
 
@@ -705,7 +704,7 @@ fn handle_map_request(display: *Display, event: *xlib.XMapRequestEvent) void {
 }
 
 fn manage(display: *Display, win: xlib.Window, window_attrs: *xlib.XWindowAttributes) void {
-    const client = client_mod.create(win) orelse return;
+    const client = client_mod.create(gpa.allocator(), win) orelse return;
     var trans: xlib.Window = 0;
 
     client.x = window_attrs.x;
@@ -722,7 +721,7 @@ fn manage(display: *Display, win: xlib.Window, window_attrs: *xlib.XWindowAttrib
     update_title(display, client);
 
     if (xlib.XGetTransientForHint(display.handle, win, &trans) != 0) {
-        if (client_mod.window_to_client(trans)) |transient_client| {
+        if (client_mod.window_to_client(monitor_mod.monitors, trans)) |transient_client| {
             client.monitor = transient_client.monitor;
             client.tags = transient_client.tags;
         }
@@ -790,7 +789,7 @@ fn manage(display: *Display, win: xlib.Window, window_attrs: *xlib.XWindowAttrib
 }
 
 fn handle_configure_request(display: *Display, event: *xlib.XConfigureRequestEvent) void {
-    const client = client_mod.window_to_client(event.window);
+    const client = client_mod.window_to_client(monitor_mod.monitors, event.window);
 
     if (client) |managed_client| {
         if ((event.value_mask & xlib.c.CWBorderWidth) != 0) {
@@ -1816,7 +1815,7 @@ fn handle_button_press(display: *Display, event: *xlib.XButtonEvent) void {
         return;
     }
 
-    const click_client = client_mod.window_to_client(event.window);
+    const click_client = client_mod.window_to_client(monitor_mod.monitors, event.window);
     if (click_client) |found_client| {
         focus(display, found_client);
         if (monitor_mod.selected_monitor) |selmon| {
@@ -1848,7 +1847,7 @@ fn handle_button_press(display: *Display, event: *xlib.XButtonEvent) void {
 }
 
 fn handle_client_message(display: *Display, event: *xlib.XClientMessageEvent) void {
-    const client = client_mod.window_to_client(event.window) orelse return;
+    const client = client_mod.window_to_client(monitor_mod.monitors, event.window) orelse return;
 
     if (event.message_type == atoms.net_wm_state) {
         const action = event.data.l[0];
@@ -1877,13 +1876,13 @@ fn handle_client_message(display: *Display, event: *xlib.XClientMessageEvent) vo
 }
 
 fn handle_destroy_notify(display: *Display, event: *xlib.XDestroyWindowEvent) void {
-    const client = client_mod.window_to_client(event.window) orelse return;
+    const client = client_mod.window_to_client(monitor_mod.monitors, event.window) orelse return;
     std.debug.print("destroy_notify: window=0x{x}\n", .{event.window});
     unmanage(display, client);
 }
 
 fn handle_unmap_notify(display: *Display, event: *xlib.XUnmapEvent) void {
-    const client = client_mod.window_to_client(event.window) orelse return;
+    const client = client_mod.window_to_client(monitor_mod.monitors, event.window) orelse return;
     std.debug.print("unmap_notify: window=0x{x}\n", .{event.window});
     unmanage(display, client);
 }
@@ -1932,7 +1931,7 @@ fn unmanage(display: *Display, client: *Client) void {
         }
     }
 
-    client_mod.destroy(client);
+    client_mod.destroy(gpa.allocator(), client);
     update_client_list(display);
     bar_mod.invalidate_bars();
 }
@@ -1942,7 +1941,7 @@ fn handle_enter_notify(display: *Display, event: *xlib.XCrossingEvent) void {
         return;
     }
 
-    const client = client_mod.window_to_client(event.window);
+    const client = client_mod.window_to_client(monitor_mod.monitors, event.window);
     const target_mon = if (client) |c| c.monitor else monitor_mod.window_to_monitor(display.handle, display.root, event.window);
     const selmon = monitor_mod.selected_monitor;
 
@@ -2001,12 +2000,12 @@ fn handle_property_notify(display: *Display, event: *xlib.XPropertyEvent) void {
         return;
     }
 
-    const client = client_mod.window_to_client(event.window) orelse return;
+    const client = client_mod.window_to_client(monitor_mod.monitors, event.window) orelse return;
 
     if (event.atom == xlib.XA_WM_TRANSIENT_FOR) {
         var trans: xlib.Window = 0;
         if (!client.is_floating and xlib.XGetTransientForHint(display.handle, client.window, &trans) != 0) {
-            client.is_floating = client_mod.window_to_client(trans) != null;
+            client.is_floating = client_mod.window_to_client(monitor_mod.monitors, trans) != null;
             if (client.is_floating) {
                 if (client.monitor) |monitor| {
                     arrange(monitor);