displays cat! 🤯
This commit is contained in:
parent
afabc727e4
commit
24796ba50d
543
main.zig
543
main.zig
|
@ -16,14 +16,14 @@ const cairo = @cImport({
|
||||||
});
|
});
|
||||||
|
|
||||||
const Point = struct {
|
const Point = struct {
|
||||||
x: i32,
|
x: i32 = 0,
|
||||||
y: i32,
|
y: i32 = 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
const Size = struct {
|
const Size = struct {
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
width: u32,
|
width: u32 = 0,
|
||||||
height: u32,
|
height: u32 = 0,
|
||||||
|
|
||||||
fn size(self: *Self) usize {
|
fn size(self: *Self) usize {
|
||||||
return self.width * self.height;
|
return self.width * self.height;
|
||||||
|
@ -33,19 +33,27 @@ const Size = struct {
|
||||||
const Box = struct {
|
const Box = struct {
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
|
|
||||||
position: Point,
|
position: Point = .{.x = 0, .y = 0,},
|
||||||
extents: Size,
|
extents: Size = .{.width = 0, .height = 0,},
|
||||||
|
|
||||||
|
fn default() Self {
|
||||||
|
return Self {
|
||||||
|
.position = .{.x = 0, .y = 0,},
|
||||||
|
.extents = .{.width = 0, .height = 0,},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
fn intersects(self: *const Self, other: *const Self) bool {
|
fn intersects(self: *const Self, other: *const Self) bool {
|
||||||
const x1 = self.position.x;
|
const x1 = self.position.x;
|
||||||
const y1 = self.position.y;
|
const y1 = self.position.y;
|
||||||
const w1 = self.extents.width;
|
const w1 = @intCast(isize, self.extents.width);
|
||||||
const h1 = self.extents.height;
|
const h1 = @intCast(isize, self.extents.height);
|
||||||
|
|
||||||
const x2 = other.position.x;
|
const x2 = other.position.x;
|
||||||
const y2 = other.position.y;
|
const y2 = other.position.y;
|
||||||
const w2 = other.extents.width;
|
|
||||||
const h2 = other.extents.height;
|
const w2 = @intCast(isize, other.extents.width);
|
||||||
|
const h2 = @intCast(isize, other.extents.height);
|
||||||
|
|
||||||
// return self.x < other.x + other.width and
|
// return self.x < other.x + other.width and
|
||||||
// self.x + self.width > other.x and
|
// self.x + self.width > other.x and
|
||||||
|
@ -58,11 +66,29 @@ const Box = struct {
|
||||||
y1 + h1 > y2;
|
y1 + h1 > y2;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn contains(self: *Self, point: Point) bool {
|
fn fromCorners(a: Point, b: Point) Self {
|
||||||
|
const top = @min(a.y, b.y);
|
||||||
|
const bot = @max(a.y, b.y);
|
||||||
|
const left = @min(a.x, b.x);
|
||||||
|
const right = @max(a.x, b.x);
|
||||||
|
|
||||||
|
const pos = Point{.x = left, .y = top,};
|
||||||
|
const extents = Size{
|
||||||
|
.width = right - left,
|
||||||
|
.height = bot - top,
|
||||||
|
};
|
||||||
|
|
||||||
|
return Self {
|
||||||
|
.position = pos,
|
||||||
|
.extents = extents,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn contains(self: *const Self, point: Point) bool {
|
||||||
const x1 = self.position.x;
|
const x1 = self.position.x;
|
||||||
const y1 = self.position.y;
|
const y1 = self.position.y;
|
||||||
const w1 = self.extents.width;
|
const w1 = @intCast(isize, self.extents.width);
|
||||||
const h1 = self.extents.height;
|
const h1 = @intCast(isize, self.extents.height);
|
||||||
|
|
||||||
const x2 = point.x;
|
const x2 = point.x;
|
||||||
const y2 = point.y;
|
const y2 = point.y;
|
||||||
|
@ -79,12 +105,50 @@ const Box = struct {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
test "box" {
|
||||||
|
const a = Box.default();
|
||||||
|
const a2 = Box{.extents = .{.width = 1, .height = 1,}};
|
||||||
|
const b = Box.default();
|
||||||
|
const b2 = Box{.extents = .{.width = 1, .height = 1,}};
|
||||||
|
|
||||||
|
std.debug.assert(!a.contains(.{.x = 0, .y = 0}));
|
||||||
|
std.debug.assert(!a.intersects(&b));
|
||||||
|
|
||||||
|
std.debug.assert(a2.contains(.{.x = 0, .y = 0}));
|
||||||
|
std.debug.assert(a2.intersects(&b2));
|
||||||
|
|
||||||
|
const c1 = Box{
|
||||||
|
.position = .{.x = 1, .y = 1,},
|
||||||
|
.extents = .{.width = 2, .height = 2,},
|
||||||
|
};
|
||||||
|
|
||||||
|
const c2 = Box{
|
||||||
|
.position = .{.x = 1, .y = 1,},
|
||||||
|
.extents = .{.width = 1, .height = 1,},
|
||||||
|
};
|
||||||
|
|
||||||
|
std.debug.assert(!c1.intersects(&a2));
|
||||||
|
std.debug.assert(c1.intersects(&c2));
|
||||||
|
}
|
||||||
|
|
||||||
fn ListItem(comptime T: type) type {
|
fn ListItem(comptime T: type) type {
|
||||||
return struct {
|
return struct {
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
|
|
||||||
link: list.Link,
|
link: list.Link,
|
||||||
value: T,
|
value: T = undefined,
|
||||||
|
|
||||||
|
fn create(ally: std.mem.Allocator, value: T) !*Self {
|
||||||
|
const self = try ally.create(Self);
|
||||||
|
|
||||||
|
self.init(value);
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init(self: *Self, value: T) void {
|
||||||
|
self.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
fn Head() type {
|
fn Head() type {
|
||||||
return list.Head(Self, "link");
|
return list.Head(Self, "link");
|
||||||
|
@ -94,7 +158,12 @@ fn ListItem(comptime T: type) type {
|
||||||
return @fieldParentPtr(Self, "link", link);
|
return @fieldParentPtr(Self, "link", link);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// automatically calls `deinit()` on `data` if a function with that name exists
|
||||||
fn remove(self: *Self) void {
|
fn remove(self: *Self) void {
|
||||||
|
if (@hasDecl(T, "deinit")) {
|
||||||
|
self.value.deinit();
|
||||||
|
}
|
||||||
|
|
||||||
self.link.remove();
|
self.link.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -166,12 +235,252 @@ const Options = struct {
|
||||||
const Output = struct {
|
const Output = struct {
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
|
|
||||||
state: *State,
|
state: *State.Init = undefined,
|
||||||
output: *wl.Output,
|
output: *wl.Output,
|
||||||
|
|
||||||
geometry: Box,
|
surface: *wl.Surface = undefined,
|
||||||
logical_geometry: Box,
|
layer_surface: *zwlr.LayerSurfaceV1 = undefined,
|
||||||
scale: i32,
|
xdg_output: *zxdg.OutputV1 = undefined,
|
||||||
|
|
||||||
|
geometry: Box = Box.default(),
|
||||||
|
logical_geometry: Box = Box.default(),
|
||||||
|
scale: i32 = 1,
|
||||||
|
|
||||||
|
size: Size = Size{},
|
||||||
|
|
||||||
|
configured: bool = false,
|
||||||
|
|
||||||
|
fn init(self: *Self, state: *State.Init) !void {
|
||||||
|
std.debug.print("output.init()\n", .{});
|
||||||
|
self.state = state;
|
||||||
|
|
||||||
|
self.output.setListener(*Self, outputListener, self);
|
||||||
|
|
||||||
|
self.surface = try state.compositor.createSurface();
|
||||||
|
|
||||||
|
self.layer_surface = try state.layer_shell.getLayerSurface(
|
||||||
|
self.surface,
|
||||||
|
self.output,
|
||||||
|
.overlay,
|
||||||
|
"selection",
|
||||||
|
);
|
||||||
|
self.layer_surface.setListener(*Self, layerSurfaceListener, self);
|
||||||
|
|
||||||
|
self.xdg_output = try state.xdg_output_manager.getXdgOutput(self.output);
|
||||||
|
self.xdg_output.setListener(*Self, xdgOutputListener, self);
|
||||||
|
|
||||||
|
self.layer_surface.setAnchor(.{.top = true, .left = true, .right = true, .bottom = true,});
|
||||||
|
self.layer_surface.setKeyboardInteractivity(1);
|
||||||
|
self.layer_surface.setExclusiveZone(-1);
|
||||||
|
self.surface.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deinit(self: *Self) void {
|
||||||
|
self.output.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn xdgOutputListener(output: *zxdg.OutputV1, event: zxdg.OutputV1.Event, self: *Self) void {
|
||||||
|
_ = output;
|
||||||
|
|
||||||
|
std.debug.print("zxdg_output listener: {}\n", .{event});
|
||||||
|
|
||||||
|
switch (event) {
|
||||||
|
.logical_position => |pos| {
|
||||||
|
self.logical_geometry.position = .{.x = pos.x, .y = pos.y,};
|
||||||
|
},
|
||||||
|
.logical_size => |size| {
|
||||||
|
self.logical_geometry.extents = .{
|
||||||
|
.width = @intCast(u32, size.width),
|
||||||
|
.height = @intCast(u32, size.height),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
else => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn layerSurfaceListener(
|
||||||
|
layer: *zwlr.LayerSurfaceV1,
|
||||||
|
event: zwlr.LayerSurfaceV1.Event,
|
||||||
|
self: *Self,
|
||||||
|
) void {
|
||||||
|
std.debug.print("zwlr_layer_surface listener: {}\n", .{event});
|
||||||
|
|
||||||
|
switch (event) {
|
||||||
|
.configure => |cfg|{
|
||||||
|
self.configured = true;
|
||||||
|
self.size = .{
|
||||||
|
.width = cfg.width,
|
||||||
|
.height = cfg.height,
|
||||||
|
};
|
||||||
|
|
||||||
|
layer.ackConfigure(cfg.serial);
|
||||||
|
// TODO: send frame
|
||||||
|
},
|
||||||
|
else => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn outputListener(output: *wl.Output, event: wl.Output.Event, self: *Self) void {
|
||||||
|
_ = output;
|
||||||
|
|
||||||
|
std.debug.print("wl_output listener: {}\n", .{event});
|
||||||
|
|
||||||
|
switch (event) {
|
||||||
|
.geometry => |geom| {
|
||||||
|
self.geometry.position = .{.x = geom.x, .y = geom.y};
|
||||||
|
},
|
||||||
|
.mode => |mode| {
|
||||||
|
if (!mode.flags.current) {
|
||||||
|
self.geometry.extents = .{
|
||||||
|
.width = @intCast(u32, mode.width),
|
||||||
|
.height = @intCast(u32, mode.height),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
.scale => |scale| {
|
||||||
|
self.scale = scale.factor;
|
||||||
|
},
|
||||||
|
else => {
|
||||||
|
std.debug.print("wl_output listener: unhandled\n", .{});
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const Seat = struct {
|
||||||
|
const Self = @This();
|
||||||
|
|
||||||
|
state: *State.Init = undefined,
|
||||||
|
seat: *wl.Seat,
|
||||||
|
|
||||||
|
keyboard: ?struct {
|
||||||
|
keyboard: *wl.Keyboard,
|
||||||
|
xkb: ?struct {
|
||||||
|
keymap: *xkb.xkb_keymap,
|
||||||
|
state: *xkb.xkb_state,
|
||||||
|
} = null,
|
||||||
|
} = null,
|
||||||
|
|
||||||
|
pointer: ?struct {
|
||||||
|
pointer: *wl.Pointer,
|
||||||
|
button_state: ?wl.Pointer.ButtonState = null,
|
||||||
|
} = null,
|
||||||
|
|
||||||
|
fn init(self: *Self, state: *State.Init) void {
|
||||||
|
self.state = state;
|
||||||
|
|
||||||
|
std.debug.print("seat.init()\n", .{});
|
||||||
|
self.seat.setListener(*Self, seatListener, self);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pointerListener(pointer: *wl.Pointer, event: wl.Pointer.Event, self: *Self) void {
|
||||||
|
_ = pointer;
|
||||||
|
_ = self;
|
||||||
|
_ = event;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fn keyboardListener(keyboard: *wl.Keyboard, event: wl.Keyboard.Event, self: *Self) void {
|
||||||
|
_ = keyboard;
|
||||||
|
|
||||||
|
switch (event) {
|
||||||
|
.keymap => |keymap_event| {
|
||||||
|
const keymap = blk: {
|
||||||
|
switch (keymap_event.format) {
|
||||||
|
.no_keymap => {
|
||||||
|
break :blk xkb.xkb_keymap_new_from_names(
|
||||||
|
self.state.xkb_context,
|
||||||
|
null,
|
||||||
|
xkb.XKB_KEYMAP_COMPILE_NO_FLAGS,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
.xkb_v1 => {
|
||||||
|
const buffer = std.os.mmap(
|
||||||
|
null,
|
||||||
|
keymap_event.size,
|
||||||
|
std.os.PROT.READ,
|
||||||
|
std.os.MAP.PRIVATE,
|
||||||
|
keymap_event.fd,
|
||||||
|
0,
|
||||||
|
) catch break :blk null;
|
||||||
|
defer std.os.munmap(buffer);
|
||||||
|
defer std.os.close(keymap_event.fd);
|
||||||
|
|
||||||
|
break :blk xkb.xkb_keymap_new_from_buffer(
|
||||||
|
self.state.xkb_context,
|
||||||
|
@ptrCast([*c]const u8, buffer),
|
||||||
|
keymap_event.size - 1,
|
||||||
|
xkb.XKB_KEYMAP_FORMAT_TEXT_V1,
|
||||||
|
xkb.XKB_KEYMAP_COMPILE_NO_FLAGS,
|
||||||
|
);
|
||||||
|
|
||||||
|
},
|
||||||
|
else => unreachable,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (keymap) |map| {
|
||||||
|
const state = xkb.xkb_state_new(map);
|
||||||
|
if (state) |state_| {
|
||||||
|
// SAFETY: keyboard cant be null because we are in the keyboard listener
|
||||||
|
self.keyboard.?.xkb = .{
|
||||||
|
.keymap = map,
|
||||||
|
.state = state_,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
.key => |key| {
|
||||||
|
std.debug.print("key: {} {s}", .{key.key, if (key.state == .pressed) "pressed" else "released"});
|
||||||
|
},
|
||||||
|
else => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn seatListener(seat: *wl.Seat, event: wl.Seat.Event, self: *Self) void {
|
||||||
|
switch (event) {
|
||||||
|
.capabilities => |value| {
|
||||||
|
std.debug.print("seat capabilities: {}\n", .{value.capabilities});
|
||||||
|
const capabilities = value.capabilities;
|
||||||
|
if (capabilities.keyboard) {
|
||||||
|
const keyboard = seat.getKeyboard() catch return;
|
||||||
|
self.keyboard = .{
|
||||||
|
.keyboard = keyboard,
|
||||||
|
};
|
||||||
|
|
||||||
|
keyboard.setListener(*Self, keyboardListener, self);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (capabilities.pointer) {
|
||||||
|
const pointer = seat.getPointer() catch return;
|
||||||
|
self.pointer = .{
|
||||||
|
.pointer = pointer,
|
||||||
|
};
|
||||||
|
|
||||||
|
pointer.setListener(*Self, pointerListener, self);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
else => {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deinit(self: *Self) void {
|
||||||
|
if (self.keyboard) |keyboard| {
|
||||||
|
keyboard.keyboard.destroy();
|
||||||
|
|
||||||
|
if (keyboard.xkb) |kb| {
|
||||||
|
xkb.xkb_state_unref(kb.state);
|
||||||
|
xkb.xkb_keymap_unref(kb.keymap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self.pointer) |pointer| {
|
||||||
|
pointer.pointer.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
self.seat.destroy();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/// helper type to handle dynamic dispatch listeners whatever they're called
|
/// helper type to handle dynamic dispatch listeners whatever they're called
|
||||||
|
@ -202,10 +511,10 @@ fn Listener(comptime T: type, comptime D: type) type {
|
||||||
self: *Self,
|
self: *Self,
|
||||||
comptime D2: type,
|
comptime D2: type,
|
||||||
new_data: *D2,
|
new_data: *D2,
|
||||||
new_callback: @TypeOf(Listener(T, D2)).Fn,
|
new_callback: Listener(T, D2).Fn,
|
||||||
) @TypeOf(Listener(T, D2)) {
|
) *Listener(T, D2) {
|
||||||
const other = @ptrCast(Listener(T, D2), self);
|
const other = @ptrCast(*Listener(T, D2), self);
|
||||||
other.new_data = new_data;
|
other.data = new_data;
|
||||||
other.callback = new_callback;
|
other.callback = new_callback;
|
||||||
return other;
|
return other;
|
||||||
}
|
}
|
||||||
|
@ -221,13 +530,16 @@ const State = struct {
|
||||||
dpy: *wl.Display = undefined,
|
dpy: *wl.Display = undefined,
|
||||||
registry: *wl.Registry = undefined,
|
registry: *wl.Registry = undefined,
|
||||||
|
|
||||||
registry_listener: Listener(wl.Registry, Self) = undefined,
|
registry_listener: *Listener(wl.Registry, Self) = undefined,
|
||||||
|
|
||||||
shm: ?*wl.Shm = null,
|
shm: ?*wl.Shm = null,
|
||||||
compositor: ?*wl.Compositor = null,
|
compositor: ?*wl.Compositor = null,
|
||||||
layer_shell: ?*zwlr.LayerShellV1 = null,
|
layer_shell: ?*zwlr.LayerShellV1 = null,
|
||||||
xdg_output_manager: ?*zxdg.OutputManagerV1 = null,
|
xdg_output_manager: ?*zxdg.OutputManagerV1 = null,
|
||||||
|
|
||||||
|
seats: *ListItem(Seat).Head() = undefined,
|
||||||
|
outputs: *ListItem(Output).Head() = undefined,
|
||||||
|
|
||||||
fn create() !*Self {
|
fn create() !*Self {
|
||||||
const ally = std.heap.c_allocator;
|
const ally = std.heap.c_allocator;
|
||||||
|
|
||||||
|
@ -236,7 +548,9 @@ const State = struct {
|
||||||
|
|
||||||
const self = try ally.create(Self);
|
const self = try ally.create(Self);
|
||||||
|
|
||||||
const listener = .{
|
const listener = try ally.create(Listener(wl.Registry, Self));
|
||||||
|
|
||||||
|
listener.* = .{
|
||||||
.data = self,
|
.data = self,
|
||||||
.callback = registryListener,
|
.callback = registryListener,
|
||||||
};
|
};
|
||||||
|
@ -246,16 +560,25 @@ const State = struct {
|
||||||
.dpy = dpy,
|
.dpy = dpy,
|
||||||
.registry = registry,
|
.registry = registry,
|
||||||
.registry_listener = listener,
|
.registry_listener = listener,
|
||||||
|
.seats = try ally.create(ListItem(Seat).Head()),
|
||||||
|
.outputs = try ally.create(ListItem(Output).Head()),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
self.seats.init();
|
||||||
|
self.outputs.init();
|
||||||
|
|
||||||
self.registry_listener.set(self.registry);
|
self.registry_listener.set(self.registry);
|
||||||
if (dpy.roundtrip() != .SUCCESS) return error.RoundtripFailed;
|
if (dpy.roundtrip() != .SUCCESS) return error.RoundtripFailed;
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deinit(self: *Self) !void {
|
fn deinit(self: *Self) void {
|
||||||
|
self.ally.destroy(self.seats);
|
||||||
|
self.ally.destroy(self.outputs);
|
||||||
self.ally.destroy(self);
|
self.ally.destroy(self);
|
||||||
|
|
||||||
|
// TODO: clean up wayland state
|
||||||
}
|
}
|
||||||
|
|
||||||
fn registryListener(registry: *wl.Registry, event: wl.Registry.Event, self: *Self) void {
|
fn registryListener(registry: *wl.Registry, event: wl.Registry.Event, self: *Self) void {
|
||||||
|
@ -270,14 +593,22 @@ const State = struct {
|
||||||
} else if (std.cstr.cmp(global.interface, zwlr.LayerShellV1.getInterface().name) == 0) {
|
} else if (std.cstr.cmp(global.interface, zwlr.LayerShellV1.getInterface().name) == 0) {
|
||||||
self.layer_shell = registry.bind(global.name, zwlr.LayerShellV1, 1) catch return;
|
self.layer_shell = registry.bind(global.name, zwlr.LayerShellV1, 1) catch return;
|
||||||
} else if (std.cstr.cmp(global.interface, zxdg.OutputManagerV1.getInterface().name) == 0) {
|
} else if (std.cstr.cmp(global.interface, zxdg.OutputManagerV1.getInterface().name) == 0) {
|
||||||
self.xdg_output_manager = registry.bind(global.name, zxdg.OutputManagerV1, 2) catch
|
self.xdg_output_manager =
|
||||||
|
registry.bind(global.name, zxdg.OutputManagerV1, 2) catch
|
||||||
return;
|
return;
|
||||||
|
|
||||||
} else if (std.cstr.cmp(global.interface, wl.Seat.getInterface().name) == 0) {
|
} else if (std.cstr.cmp(global.interface, wl.Seat.getInterface().name) == 0) {
|
||||||
const seat = registry.bind(global.name, wl.Seat, 1) catch return;
|
const seat = registry.bind(global.name, wl.Seat, 1) catch return;
|
||||||
_ = seat;
|
|
||||||
|
if (ListItem(Seat).create(self.ally, Seat{.seat = seat}) catch null) |ele| {
|
||||||
|
self.seats.append(ele);
|
||||||
|
}
|
||||||
} else if (std.cstr.cmp(global.interface, wl.Output.getInterface().name) == 0) {
|
} else if (std.cstr.cmp(global.interface, wl.Output.getInterface().name) == 0) {
|
||||||
const output = registry.bind(global.name, wl.Output, 3) catch return;
|
const output = registry.bind(global.name, wl.Output, 3) catch return;
|
||||||
_ = output;
|
|
||||||
|
if (ListItem(Output).create(self.ally, Output{.output = output}) catch null) |ele| {
|
||||||
|
self.outputs.append(ele);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.global_remove => {},
|
.global_remove => {},
|
||||||
|
@ -292,42 +623,7 @@ const State = struct {
|
||||||
// free allocated memory if not returning it
|
// free allocated memory if not returning it
|
||||||
errdefer self.ally.destroy(init);
|
errdefer self.ally.destroy(init);
|
||||||
|
|
||||||
const xkb_context = xkb.xkb_context_new(xkb.XKB_CONTEXT_NO_FLAGS) orelse
|
try init.init(self);
|
||||||
return error.NoXkbContext;
|
|
||||||
// free xkb_context if we fail to initialize the state
|
|
||||||
errdefer xkb.xkb_context_unref(xkb_context);
|
|
||||||
|
|
||||||
const cursor_theme = std.process.getEnvVarOwned(self.ally, "XCURSOR_THEME") catch "";
|
|
||||||
// only free cursor_theme if bailing
|
|
||||||
errdefer self.ally.free(cursor_theme);
|
|
||||||
|
|
||||||
const buf = try std.process.getEnvVarOwned(self.ally, "XCURSOR_SIZE");
|
|
||||||
defer self.ally.free(buf);
|
|
||||||
|
|
||||||
const cursor_size = std.fmt.parseInt(u32, buf, 10) catch return error.InvalidXCursorSize;
|
|
||||||
|
|
||||||
init.* = Init{
|
|
||||||
.ally = self.ally,
|
|
||||||
.dpy = self.dpy,
|
|
||||||
.registry = self.registry,
|
|
||||||
|
|
||||||
// these 4 fields are set by the registry listener so they may be null at this point
|
|
||||||
// treat that as a hard error tho.
|
|
||||||
.shm = self.shm orelse return error.NoWlShm,
|
|
||||||
.compositor = self.compositor orelse return error.NoWlCompositor,
|
|
||||||
.layer_shell = self.layer_shell orelse return error.NoWlrLayerShell,
|
|
||||||
.xdg_output_manager = self.xdg_output_manager orelse return error.NoXdgOutputManager,
|
|
||||||
|
|
||||||
// these 3 fields should never be null because init() will bail if these functions fail
|
|
||||||
.xkb_context = xkb_context,
|
|
||||||
.cursor_theme = cursor_theme,
|
|
||||||
.cursor_size = cursor_size,
|
|
||||||
};
|
|
||||||
|
|
||||||
init.outputs.init();
|
|
||||||
|
|
||||||
// i hope this is fine but appears to be requried to reset the listener on the registry
|
|
||||||
init.registry_listener = self.registry_listener.cast(Init, init, Init.registryListener);
|
|
||||||
|
|
||||||
return init;
|
return init;
|
||||||
}
|
}
|
||||||
|
@ -341,7 +637,7 @@ const State = struct {
|
||||||
dpy: *wl.Display = undefined,
|
dpy: *wl.Display = undefined,
|
||||||
registry: *wl.Registry = undefined,
|
registry: *wl.Registry = undefined,
|
||||||
|
|
||||||
registry_listener: Listener(wl.Registry, Self) = undefined,
|
registry_listener: *Listener(wl.Registry, Self) = undefined,
|
||||||
|
|
||||||
shm: *wl.Shm = undefined,
|
shm: *wl.Shm = undefined,
|
||||||
compositor: *wl.Compositor = undefined,
|
compositor: *wl.Compositor = undefined,
|
||||||
|
@ -350,11 +646,70 @@ const State = struct {
|
||||||
|
|
||||||
xkb_context: *xkb.xkb_context = undefined,
|
xkb_context: *xkb.xkb_context = undefined,
|
||||||
|
|
||||||
outputs: ListItem(Output).Head() = undefined,
|
|
||||||
|
|
||||||
cursor_theme: []const u8 = undefined,
|
cursor_theme: []const u8 = undefined,
|
||||||
cursor_size: u32 = undefined,
|
cursor_size: u32 = undefined,
|
||||||
|
|
||||||
|
seats: *ListItem(Seat).Head() = undefined,
|
||||||
|
outputs: *ListItem(Output).Head() = undefined,
|
||||||
|
|
||||||
|
fn init(self: *Self, uninit: *Uninit) !void {
|
||||||
|
const xkb_context = xkb.xkb_context_new(xkb.XKB_CONTEXT_NO_FLAGS) orelse
|
||||||
|
return error.NoXkbContext;
|
||||||
|
// free xkb_context if we fail to initialize the state
|
||||||
|
errdefer xkb.xkb_context_unref(xkb_context);
|
||||||
|
|
||||||
|
const cursor_theme = std.process.getEnvVarOwned(uninit.ally, "XCURSOR_THEME") catch "";
|
||||||
|
// only free cursor_theme if bailing
|
||||||
|
errdefer uninit.ally.free(cursor_theme);
|
||||||
|
|
||||||
|
const buf = try std.process.getEnvVarOwned(uninit.ally, "XCURSOR_SIZE");
|
||||||
|
defer uninit.ally.free(buf);
|
||||||
|
|
||||||
|
const cursor_size = std.fmt.parseInt(u32, buf, 10) catch return error.InvalidXCursorSize;
|
||||||
|
|
||||||
|
self.* = Self{
|
||||||
|
.ally = uninit.ally,
|
||||||
|
.dpy = uninit.dpy,
|
||||||
|
.registry = uninit.registry,
|
||||||
|
|
||||||
|
// these 4 fields are set by the registry listener so they may be null at this point
|
||||||
|
// treat that as a hard error tho.
|
||||||
|
.shm = uninit.shm orelse return error.NoWlShm,
|
||||||
|
.compositor = uninit.compositor orelse return error.NoWlCompositor,
|
||||||
|
.layer_shell = uninit.layer_shell orelse return error.NoWlrLayerShell,
|
||||||
|
.xdg_output_manager = uninit.xdg_output_manager orelse return error.NoXdgOutputManager,
|
||||||
|
|
||||||
|
// these 3 fields should never be null because init() will bail if these functions fail
|
||||||
|
.xkb_context = xkb_context,
|
||||||
|
.cursor_theme = cursor_theme,
|
||||||
|
.cursor_size = cursor_size,
|
||||||
|
|
||||||
|
.seats = uninit.seats,
|
||||||
|
.outputs = uninit.outputs,
|
||||||
|
};
|
||||||
|
|
||||||
|
// i hope this is fine but appears to be requried to reset the listener on the registry
|
||||||
|
self.registry_listener = uninit.registry_listener.cast(Self, self, Self.registryListener);
|
||||||
|
|
||||||
|
// init seats
|
||||||
|
|
||||||
|
var seats_iter = self.seats.safeIterator(.forward);
|
||||||
|
while (seats_iter.next()) |item| {
|
||||||
|
const seat = item.get();
|
||||||
|
seat.init(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
// init outputs
|
||||||
|
|
||||||
|
var output_iter = self.outputs.safeIterator(.forward);
|
||||||
|
while (output_iter.next()) |item| {
|
||||||
|
const output = item.get();
|
||||||
|
try output.init(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self.dpy.roundtrip() != .SUCCESS) return error.RoundtripFailed;
|
||||||
|
}
|
||||||
|
|
||||||
fn registryListener(registry: *wl.Registry, event: wl.Registry.Event, self: *Self) void {
|
fn registryListener(registry: *wl.Registry, event: wl.Registry.Event, self: *Self) void {
|
||||||
_ = registry;
|
_ = registry;
|
||||||
_ = self;
|
_ = self;
|
||||||
|
@ -365,9 +720,58 @@ const State = struct {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn stuff(self: *Self) !void {
|
||||||
|
var it = self.outputs.iterator(.forward);
|
||||||
|
if (it.next()) |link| {
|
||||||
|
const output = link.get();
|
||||||
|
const os = std.os;
|
||||||
|
|
||||||
|
const buffer = blk: {
|
||||||
|
const width = 128;
|
||||||
|
const height = 128;
|
||||||
|
const stride = width * 4;
|
||||||
|
const size = stride * height;
|
||||||
|
|
||||||
|
const fd = try os.memfd_create("hello-zig-wayland", 0);
|
||||||
|
try os.ftruncate(fd, size);
|
||||||
|
const data = try os.mmap(null, size, os.PROT.READ | os.PROT.WRITE, os.MAP.SHARED, fd, 0);
|
||||||
|
std.mem.copy(u8, data, @embedFile("cat.bgra"));
|
||||||
|
|
||||||
|
const pool = try self.shm.createPool(fd, size);
|
||||||
|
defer pool.destroy();
|
||||||
|
|
||||||
|
break :blk try pool.createBuffer(0, width, height, stride, wl.Shm.Format.argb8888);
|
||||||
|
};
|
||||||
|
defer buffer.destroy();
|
||||||
|
|
||||||
|
output.surface.attach(buffer, 0, 0);
|
||||||
|
output.surface.commit();
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
if (self.dpy.dispatch() != .SUCCESS) return error.DispatchFailed;
|
||||||
|
std.debug.print("asdf\n", .{});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn deinit(self: *Self) void {
|
fn deinit(self: *Self) void {
|
||||||
defer self.ally.destroy(self);
|
defer self.ally.destroy(self);
|
||||||
|
|
||||||
|
var seats_iter = self.seats.safeIterator(.forward);
|
||||||
|
while (seats_iter.next()) |next| {
|
||||||
|
next.remove();
|
||||||
|
self.ally.destroy(next);
|
||||||
|
}
|
||||||
|
self.ally.destroy(self.seats);
|
||||||
|
|
||||||
|
|
||||||
|
var outputs_iter = self.outputs.safeIterator(.forward);
|
||||||
|
while (outputs_iter.next()) |next| {
|
||||||
|
next.remove();
|
||||||
|
self.ally.destroy(next);
|
||||||
|
}
|
||||||
|
self.ally.destroy(self.outputs);
|
||||||
|
|
||||||
self.shm.destroy();
|
self.shm.destroy();
|
||||||
self.compositor.destroy();
|
self.compositor.destroy();
|
||||||
self.layer_shell.destroy();
|
self.layer_shell.destroy();
|
||||||
|
@ -386,8 +790,7 @@ pub fn main() !void {
|
||||||
|
|
||||||
const state = try State.Uninit.create();
|
const state = try State.Uninit.create();
|
||||||
const init = try state.intoInit();
|
const init = try state.intoInit();
|
||||||
|
|
||||||
_ = try std.io.getStdIn().reader().readByte();
|
|
||||||
|
|
||||||
defer init.deinit();
|
defer init.deinit();
|
||||||
|
|
||||||
|
try init.stuff();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue