dotfiles/scripts/wlbg/src/Globals.zig
2024-03-19 22:02:31 +01:00

105 lines
3.2 KiB
Zig

const std = @import("std");
const wayland = @import("wayland");
const wl = wayland.client.wl;
const zxdg = wayland.client.zxdg;
const zwlr = wayland.client.zwlr;
const log = std.log.scoped(.globals);
seat: *wl.Seat,
compositor: *wl.Compositor,
layer_shell: *zwlr.LayerShellV1,
xdg_output_manager: *zxdg.OutputManagerV1,
outputs: std.ArrayList(*wl.Output),
const Globals = @This();
const Collector = col: {
var fields: []const std.builtin.Type.StructField = &.{};
for (std.meta.fields(Globals)) |field| {
const Field = @Type(.{ .Optional = .{ .child = field.type } });
fields = fields ++ [1]std.builtin.Type.StructField{.{
.name = field.name,
.type = Field,
.default_value = @ptrCast(&@as(Field, null)),
.is_comptime = false,
.alignment = @alignOf(Field),
}};
}
break :col @Type(.{ .Struct = .{
.layout = .auto,
.fields = fields,
.decls = &.{},
.is_tuple = false,
} });
};
pub fn collect(dpy: *wl.Display) !Globals {
const registry = try dpy.getRegistry();
defer registry.destroy();
var col = Collector{};
inline for (std.meta.fields(Globals)) |f| {
if (comptime isList(f.type)) {
@field(col, f.name) = f.type.init(std.heap.c_allocator);
}
}
errdefer inline for (std.meta.fields(Globals)) |f| {
if (comptime isList(f.type)) {
@field(col, f.name).?.deinit();
}
};
registry.setListener(*Collector, registryListener, &col);
if (dpy.roundtrip() != .SUCCESS) return error.RoundtipFail;
// TODO don't use undefined. std.mem.zeroInit complains about non-nullable pointers in struct
var self: Globals = undefined;
inline for (std.meta.fields(Globals)) |f| {
@field(self, f.name) = @field(col, f.name) orelse return error.MissingGlobals;
}
return self;
}
fn registryListener(reg: *wl.Registry, ev: wl.Registry.Event, col: *Collector) void {
switch (ev) {
.global => |global| {
inline for (std.meta.fields(Collector)) |f| {
const BaseType = std.meta.Child(f.type);
const Interface = std.meta.Child(if (comptime isList(BaseType))
std.meta.Child(std.meta.FieldType(BaseType, .items))
else
BaseType);
if (std.mem.orderZ(u8, global.interface, Interface.getInterface().name) == .eq) {
log.info("binding global {s}@{}", .{ Interface.getInterface().name, global.name });
const bound = reg.bind(
global.name,
Interface,
Interface.generated_version,
) catch return;
if (comptime isList(BaseType)) {
@field(col, f.name).?.append(bound) catch @panic("OOM");
} else {
@field(col, f.name) = bound;
}
return;
}
}
},
.global_remove => {},
}
}
fn isList(comptime T: type) bool {
return @typeInfo(T) == .Struct and
@hasDecl(T, "init") and
@hasDecl(T, "deinit") and
@hasDecl(T, "append");
}