oxwm

https://git.tonybtw.com/oxwm.git git://git.tonybtw.com/oxwm.git
5,397 bytes raw
1
const std = @import("std");
2
const xlib = @import("x11/xlib.zig");
3
const Client = @import("client.zig").Client;
4
5
pub const Layout = struct {
6
    symbol: []const u8,
7
    arrange_fn: ?*const fn (*Monitor) void,
8
};
9
10
// TODO: Make clearer, document? refactor?
11
pub const Pertag = struct {
12
    curtag: u32 = 1,
13
    prevtag: u32 = 1,
14
    nmasters: [10]i32 = [_]i32{1} ** 10,
15
    mfacts: [10]f32 = [_]f32{0.55} ** 10,
16
    sellts: [10]u32 = [_]u32{0} ** 10,
17
    ltidxs: [10][5]?*const Layout = [_][5]?*const Layout{.{ null, null, null, null, null }} ** 10,
18
    showbars: [10]bool = [_]bool{true} ** 10,
19
};
20
21
// TODO: Make clearer, document? refactor?
22
pub const Monitor = struct {
23
    lt_symbol: [16]u8 = std.mem.zeroes([16]u8),
24
    mfact: f32 = 0.55,
25
    nmaster: i32 = 1,
26
    num: i32 = 0,
27
    bar_y: i32 = 0,
28
    mon_x: i32 = 0,
29
    mon_y: i32 = 0,
30
    mon_w: i32 = 0,
31
    mon_h: i32 = 0,
32
    win_x: i32 = 0,
33
    win_y: i32 = 0,
34
    win_w: i32 = 0,
35
    win_h: i32 = 0,
36
    gap_inner_h: i32 = 0,
37
    gap_inner_v: i32 = 0,
38
    gap_outer_h: i32 = 0,
39
    gap_outer_v: i32 = 0,
40
    scroll_offset: i32 = 0,
41
    sel_tags: u32 = 0,
42
    sel_lt: u32 = 0,
43
    tagset: [2]u32 = .{ 1, 1 },
44
    show_bar: bool = true,
45
    top_bar: bool = true,
46
    clients: ?*Client = null,
47
    sel: ?*Client = null,
48
    stack: ?*Client = null,
49
    next: ?*Monitor = null,
50
    bar_win: xlib.Window = 0,
51
    lt: [5]?*const Layout = .{null} ** 5,
52
    pertag: Pertag = .{},
53
};
54
55
// NOTE: `monitors` and `selected_monitor` will soon be removed, they will
56
// move to `WindowManager` fields in a future refactor step.  All new
57
// code should prefer receiving a `*Monitor` or `?*Monitor` as a parameter
58
// rather than reaching into these globals directly.
59
60
pub var monitors: ?*Monitor = null;
61
pub var selected_monitor: ?*Monitor = null;
62
63
var allocator: std.mem.Allocator = undefined;
64
65
/// Must be called once before any other function in this module.
66
pub fn init(alloc: std.mem.Allocator) void {
67
    allocator = alloc;
68
}
69
70
/// Allocates and zero-initialises a new `Monitor`.
71
pub fn create() ?*Monitor {
72
    const mon = allocator.create(Monitor) catch return null;
73
    mon.* = Monitor{};
74
    return mon;
75
}
76
77
/// Frees a monitor previously returned by `create`.
78
pub fn destroy(mon: *Monitor) void {
79
    allocator.destroy(mon);
80
}
81
82
/// Returns the monitor whose bar window or client matches `win`, falling
83
/// back to a pointer-position query when `win` is the root window.
84
///
85
/// `display` and `root` are passed explicitly rather than cached in module
86
/// state, this keeps ownership clear and avoids a stale-pointer hazard.
87
pub fn window_to_monitor(display: *xlib.Display, root: xlib.Window, win: xlib.Window) ?*Monitor {
88
    if (win == root) {
89
        var root_x: c_int = undefined;
90
        var root_y: c_int = undefined;
91
        var dummy_win: xlib.Window = undefined;
92
        var dummy_int: c_int = undefined;
93
        var dummy_uint: c_uint = undefined;
94
        if (xlib.XQueryPointer(display, root, &dummy_win, &dummy_win, &root_x, &root_y, &dummy_int, &dummy_int, &dummy_uint) != 0) {
95
            return rect_to_monitor(root_x, root_y, 1, 1);
96
        }
97
    }
98
99
    var current = monitors;
100
    while (current) |monitor| {
101
        if (monitor.bar_win == win) return monitor;
102
        current = monitor.next;
103
    }
104
105
    const client = @import("client.zig").window_to_client(monitors, win);
106
    if (client) |found_client| {
107
        return found_client.monitor;
108
    }
109
110
    return selected_monitor;
111
}
112
113
/// Returns the monitor with the greatest intersection area with the given
114
/// rectangle, or `selected_monitor` if no intersection is found.
115
pub fn rect_to_monitor(x: i32, y: i32, width: i32, height: i32) ?*Monitor {
116
    var result = selected_monitor;
117
    var max_area: i32 = 0;
118
119
    var current = monitors;
120
    while (current) |monitor| {
121
        const intersect_x = @max(0, @min(x + width, monitor.win_x + monitor.win_w) - @max(x, monitor.win_x));
122
        const intersect_y = @max(0, @min(y + height, monitor.win_y + monitor.win_h) - @max(y, monitor.win_y));
123
        const area = intersect_x * intersect_y;
124
        if (area > max_area) {
125
            max_area = area;
126
            result = monitor;
127
        }
128
        current = monitor.next;
129
    }
130
    return result;
131
}
132
133
/// Returns the next or previous monitor relative to `selected_monitor`.
134
///
135
/// Positive `direction` moves forward through the linked list (wrapping to
136
/// the head); negative moves backward (wrapping to the tail).
137
///
138
// TODO:
139
// - Change direction to an enum/enum_literal
140
// - Rename function
141
pub fn dir_to_monitor(direction: i32) ?*Monitor {
142
    var target: ?*Monitor = null;
143
144
    if (direction > 0) {
145
        target = if (selected_monitor) |current| current.next else null;
146
        if (target == null) {
147
            target = monitors;
148
        }
149
    } else if (selected_monitor == monitors) {
150
        // Already at head, walk to tail.
151
        var last = monitors;
152
        while (last) |iter| {
153
            if (iter.next == null) {
154
                target = iter;
155
                break;
156
            }
157
            last = iter.next;
158
        }
159
    } else {
160
        // Walk until we find the node just before selected_monitor.
161
        var previous = monitors;
162
        while (previous) |iter| {
163
            if (iter.next == selected_monitor) {
164
                target = iter;
165
                break;
166
            }
167
            previous = iter.next;
168
        }
169
    }
170
    return target;
171
}