1const std = @import("std");
2const log = std.log;
3const mem = std.mem;
4const meta = std.meta;
5const posix = std.posix;
6
7const wl = @import("wayland").client.wl;
8const wp = @import("wayland").client.wp;
9const zwlr = @import("wayland").client.zwlr;
10const zriver = @import("wayland").client.zriver;
11
12const Bar = @import("Bar.zig");
13const Input = @import("Input.zig");
14const Monitor = @import("Monitor.zig");
15const Seat = @import("Seat.zig");
16const Wayland = @This();
17
18const state = &@import("root").state;
19
20display: *wl.Display,
21registry: *wl.Registry,
22fd: posix.fd_t,
23
24compositor: ?*wl.Compositor = null,
25subcompositor: ?*wl.Subcompositor = null,
26seat: ?*wl.Seat = null,
27shm: ?*wl.Shm = null,
28single_pixel_buffer_manager: ?*wp.SinglePixelBufferManagerV1 = null,
29viewporter: ?*wp.Viewporter = null,
30layer_shell: ?*zwlr.LayerShellV1 = null,
31status_manager: ?*zriver.StatusManagerV1 = null,
32control: ?*zriver.ControlV1 = null,
33
34river_seat: ?*Seat = null,
35monitors: std.ArrayList(*Monitor),
36inputs: std.ArrayList(*Input),
37
38pub fn init() !Wayland {
39 const display = try wl.Display.connect(null);
40 const wfd: posix.fd_t = @intCast(display.getFd());
41 const registry = try display.getRegistry();
42
43 return Wayland{
44 .display = display,
45 .registry = registry,
46 .fd = wfd,
47 .monitors = std.ArrayList(*Monitor).init(state.gpa),
48 .inputs = std.ArrayList(*Input).init(state.gpa),
49 };
50}
51
52pub fn deinit(self: *Wayland) void {
53 for (self.monitors.items) |monitor| monitor.destroy();
54 for (self.inputs.items) |input| input.destroy();
55
56 if (self.river_seat) |s| s.destroy();
57 self.monitors.deinit();
58 self.inputs.deinit();
59
60 if (self.compositor) |global| global.destroy();
61 if (self.subcompositor) |global| global.destroy();
62 if (self.shm) |global| global.destroy();
63 if (self.viewporter) |global| global.destroy();
64 if (self.single_pixel_buffer_manager) |global| global.destroy();
65 if (self.layer_shell) |global| global.destroy();
66 if (self.status_manager) |global| global.destroy();
67 if (self.control) |global| global.destroy();
68 // TODO: Do we need to .release() the seat?
69 if (self.seat) |global| global.destroy();
70
71 self.registry.destroy();
72 self.display.disconnect();
73}
74
75pub fn registerGlobals(self: *Wayland) !void {
76 self.registry.setListener(*Wayland, registryListener, self);
77
78 const errno = self.display.roundtrip();
79 if (errno != .SUCCESS) {
80 return error.RoundtripFailed;
81 }
82}
83
84pub fn findBar(self: *Wayland, wlSurface: ?*wl.Surface) ?*Bar {
85 if (wlSurface == null) {
86 return null;
87 }
88 for (self.monitors.items) |monitor| {
89 if (monitor.bar) |bar| {
90 if (bar.background.surface == wlSurface or
91 bar.title.surface == wlSurface or
92 bar.tags.surface == wlSurface or
93 bar.text.surface == wlSurface)
94 {
95 return bar;
96 }
97 }
98 }
99 return null;
100}
101
102fn registryListener(registry: *wl.Registry, event: wl.Registry.Event, self: *Wayland) void {
103 switch (event) {
104 .global => |g| {
105 self.bindGlobal(registry, g.name, g.interface) catch unreachable;
106 },
107 .global_remove => |g| {
108 for (self.monitors.items, 0..) |monitor, i| {
109 if (monitor.globalName == g.name) {
110 self.monitors.swapRemove(i).destroy();
111 break;
112 }
113 }
114 for (self.inputs.items, 0..) |input, i| {
115 if (input.globalName == g.name) {
116 self.inputs.swapRemove(i).destroy();
117 break;
118 }
119 }
120 },
121 }
122}
123
124fn bindGlobal(self: *Wayland, registry: *wl.Registry, name: u32, iface: [*:0]const u8) !void {
125 if (mem.orderZ(u8, iface, wl.Compositor.getInterface().name) == .eq) {
126 self.compositor = try registry.bind(name, wl.Compositor, 4);
127 } else if (mem.orderZ(u8, iface, wl.Subcompositor.getInterface().name) == .eq) {
128 self.subcompositor = try registry.bind(name, wl.Subcompositor, 1);
129 } else if (mem.orderZ(u8, iface, wl.Shm.getInterface().name) == .eq) {
130 self.shm = try registry.bind(name, wl.Shm, 1);
131 } else if (mem.orderZ(u8, iface, wp.Viewporter.getInterface().name) == .eq) {
132 self.viewporter = try registry.bind(name, wp.Viewporter, 1);
133 } else if (mem.orderZ(u8, iface, wp.SinglePixelBufferManagerV1.getInterface().name) == .eq) {
134 self.single_pixel_buffer_manager = try registry.bind(name, wp.SinglePixelBufferManagerV1, 1);
135 } else if (mem.orderZ(u8, iface, zwlr.LayerShellV1.getInterface().name) == .eq) {
136 self.layer_shell = try registry.bind(name, zwlr.LayerShellV1, 1);
137 } else if (mem.orderZ(u8, iface, zriver.StatusManagerV1.getInterface().name) == .eq) {
138 self.status_manager = try registry.bind(name, zriver.StatusManagerV1, 2);
139 self.river_seat = try Seat.create(); // TODO: find a better way to do this
140 } else if (mem.orderZ(u8, iface, zriver.ControlV1.getInterface().name) == .eq) {
141 self.control = try registry.bind(name, zriver.ControlV1, 1);
142 } else if (mem.orderZ(u8, iface, wl.Output.getInterface().name) == .eq) {
143 const monitor = try Monitor.create(registry, name);
144 try self.monitors.append(monitor);
145 } else if (mem.orderZ(u8, iface, wl.Seat.getInterface().name) == .eq) {
146 self.seat = try registry.bind(name, wl.Seat, 5);
147 try self.inputs.append(try Input.create(name));
148 }
149}