oxwm

https://git.tonybtw.com/oxwm.git git://git.tonybtw.com/oxwm.git
7,803 bytes raw
1
const std = @import("std");
2
const xlib = @import("x11/xlib.zig");
3
const Monitor = @import("monitor.zig").Monitor;
4
5
pub const Client = struct {
6
    name: [256]u8 = std.mem.zeroes([256]u8),
7
    min_aspect: f32 = 0,
8
    max_aspect: f32 = 0,
9
    x: i32 = 0,
10
    y: i32 = 0,
11
    width: i32 = 0,
12
    height: i32 = 0,
13
    old_x: i32 = 0,
14
    old_y: i32 = 0,
15
    old_width: i32 = 0,
16
    old_height: i32 = 0,
17
    base_width: i32 = 0,
18
    base_height: i32 = 0,
19
    increment_width: i32 = 0,
20
    increment_height: i32 = 0,
21
    max_width: i32 = 0,
22
    max_height: i32 = 0,
23
    min_width: i32 = 0,
24
    min_height: i32 = 0,
25
    hints_valid: bool = false,
26
    border_width: i32 = 0,
27
    old_border_width: i32 = 0,
28
    tags: u32 = 0,
29
    is_fixed: bool = false,
30
    is_floating: bool = false,
31
    is_urgent: bool = false,
32
    never_focus: bool = false,
33
    old_state: bool = false,
34
    is_fullscreen: bool = false,
35
    next: ?*Client = null,
36
    stack_next: ?*Client = null,
37
    monitor: ?*Monitor = null,
38
    window: xlib.Window = 0,
39
};
40
41
/// Initialises a new `Client` for the given X window.
42
pub fn create(allocator: std.mem.Allocator, window: xlib.Window) ?*Client {
43
    const client = allocator.create(Client) catch return null;
44
    client.* = Client{ .window = window };
45
    return client;
46
}
47
48
/// Frees a client previously returned by `create`.
49
pub fn destroy(allocator: std.mem.Allocator, client: *Client) void {
50
    allocator.destroy(client);
51
}
52
53
/// Prepends `client` to the front of its monitor's client list.
54
pub fn attach(client: *Client) void {
55
    if (client.monitor) |monitor| {
56
        client.next = monitor.clients;
57
        monitor.clients = client;
58
    }
59
}
60
61
/// Removes `client` from its monitor's client list.
62
pub fn detach(client: *Client) void {
63
    if (client.monitor) |monitor| {
64
        var current_ptr: *?*Client = &monitor.clients;
65
        while (current_ptr.*) |current| {
66
            if (current == client) {
67
                current_ptr.* = client.next;
68
                return;
69
            }
70
            current_ptr = &current.next;
71
        }
72
    }
73
}
74
75
/// Prepends `client` to the front of its monitor's focus stack.
76
pub fn attach_stack(client: *Client) void {
77
    if (client.monitor) |monitor| {
78
        client.stack_next = monitor.stack;
79
        monitor.stack = client;
80
    }
81
}
82
83
/// Removes `client` from its monitor's focus stack.
84
pub fn detach_stack(client: *Client) void {
85
    if (client.monitor) |monitor| {
86
        var current_ptr: *?*Client = &monitor.stack;
87
        while (current_ptr.*) |current| {
88
            if (current == client) {
89
                current_ptr.* = client.stack_next;
90
                return;
91
            }
92
            current_ptr = &current.stack_next;
93
        }
94
    }
95
}
96
97
/// Searches all monitors for a client whose X window matches `window`.
98
///
99
/// `monitors` is the head of the monitor linked list.
100
pub fn window_to_client(monitors: ?*Monitor, window: xlib.Window) ?*Client {
101
    var current_monitor = monitors;
102
    while (current_monitor) |monitor| {
103
        var current_client = monitor.clients;
104
        while (current_client) |client| {
105
            if (client.window == window) return client;
106
            current_client = client.next;
107
        }
108
        current_monitor = monitor.next;
109
    }
110
    return null;
111
}
112
113
/// Returns true if `client` is visible on its monitor's currently selected
114
/// tag set.
115
pub fn is_visible(client: *Client) bool {
116
    if (client.monitor) |monitor| {
117
        return (client.tags & monitor.tagset[monitor.sel_tags]) != 0;
118
    }
119
    return false;
120
}
121
122
/// Returns true if `client` is visible on the given tag bitmask.
123
pub fn is_visible_on_tag(client: *Client, tags: u32) bool {
124
    return (client.tags & tags) != 0;
125
}
126
127
/// Returns the first non-floating, visible client at or after `client`.
128
pub fn next_tiled(client: ?*Client) ?*Client {
129
    var current = client;
130
    while (current) |iter| {
131
        if (!iter.is_floating and is_visible(iter)) return iter;
132
        current = iter.next;
133
    }
134
    return null;
135
}
136
137
/// Returns the first non-floating client on `client`'s monitor that shares
138
/// any tag with `client`. Used for `attach_aside` ordering.
139
pub fn next_tagged(client: *Client) ?*Client {
140
    const monitor = client.monitor orelse return null;
141
    var walked = monitor.clients;
142
    while (walked) |iter| {
143
        if (!iter.is_floating and is_visible_on_tag(iter, client.tags)) return iter;
144
        walked = iter.next;
145
    }
146
    return null;
147
}
148
149
/// Inserts `client` just after the first client that shares its tags,
150
/// falling back to prepend if none exists.
151
pub fn attach_aside(client: *Client) void {
152
    const at = next_tagged(client);
153
    if (at == null) {
154
        attach(client);
155
        return;
156
    }
157
    client.next = at.?.next;
158
    at.?.next = client;
159
}
160
161
/// Counts non-floating, visible clients on `monitor`.
162
pub fn count_tiled(monitor: *Monitor) u32 {
163
    var count: u32 = 0;
164
    var current = next_tiled(monitor.clients);
165
    while (current) |client| {
166
        count += 1;
167
        current = next_tiled(client.next);
168
    }
169
    return count;
170
}
171
172
/// Returns the tiled client on `monitor` whose bounds contain (`point_x`,
173
/// `point_y`), excluding `exclude`.  Returns null if none found.
174
pub fn tiled_window_at(exclude: *Client, monitor: *Monitor, point_x: i32, point_y: i32) ?*Client {
175
    const tags = monitor.tagset[monitor.sel_tags];
176
    var current = monitor.clients;
177
178
    while (current) |client| {
179
        if (client != exclude and !client.is_floating and (client.tags & tags) != 0) {
180
            const client_w = client.width + client.border_width * 2;
181
            const client_h = client.height + client.border_width * 2;
182
183
            if (point_x >= client.x and point_x < client.x + client_w and
184
                point_y >= client.y and point_y < client.y + client_h)
185
            {
186
                return client;
187
            }
188
        }
189
        current = client.next;
190
    }
191
    return null;
192
}
193
194
/// Moves `client` to just before `target` in the monitor's client list.
195
/// Does nothing if they are on different monitors.
196
pub fn insert_before(client: *Client, target: *Client) void {
197
    const monitor = target.monitor orelse return;
198
    if (client.monitor != monitor) return;
199
200
    detach(client);
201
202
    if (monitor.clients == target) {
203
        client.next = target;
204
        monitor.clients = client;
205
        return;
206
    }
207
208
    var current = monitor.clients;
209
    while (current) |iter| {
210
        if (iter.next == target) {
211
            client.next = target;
212
            iter.next = client;
213
            return;
214
        }
215
        current = iter.next;
216
    }
217
}
218
219
/// Swaps the positions of `client_a` and `client_b` in their shared monitor's
220
/// client list.  Does nothing if they are on different monitors.
221
pub fn swap_clients(client_a: *Client, client_b: *Client) void {
222
    const monitor = client_a.monitor orelse return;
223
    if (client_b.monitor != monitor) return;
224
225
    var prev_a: ?*Client = null;
226
    var prev_b: ?*Client = null;
227
    var iter = monitor.clients;
228
229
    while (iter) |client| {
230
        if (client.next == client_a) prev_a = client;
231
        if (client.next == client_b) prev_b = client;
232
        iter = client.next;
233
    }
234
235
    const next_a = client_a.next;
236
    const next_b = client_b.next;
237
238
    if (next_a == client_b) {
239
        client_a.next = next_b;
240
        client_b.next = client_a;
241
        if (prev_a) |prev| prev.next = client_b else monitor.clients = client_b;
242
    } else if (next_b == client_a) {
243
        client_b.next = next_a;
244
        client_a.next = client_b;
245
        if (prev_b) |prev| prev.next = client_a else monitor.clients = client_a;
246
    } else {
247
        client_a.next = next_b;
248
        client_b.next = next_a;
249
        if (prev_a) |prev| prev.next = client_b else monitor.clients = client_b;
250
        if (prev_b) |prev| prev.next = client_a else monitor.clients = client_a;
251
    }
252
}