From 24796ba50d96fd96e2b4ebf2ec8b1502d040b85a Mon Sep 17 00:00:00 2001 From: Janis Date: Fri, 16 Dec 2022 12:43:42 +0100 Subject: [PATCH] =?UTF-8?q?displays=20cat!=20=F0=9F=A4=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cat.bgra | Bin 0 -> 65536 bytes main.zig | 543 ++++++++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 473 insertions(+), 70 deletions(-) create mode 100644 cat.bgra diff --git a/cat.bgra b/cat.bgra new file mode 100644 index 0000000000000000000000000000000000000000..1ca94bb36757708353504a7509a99d85b81ce4cf GIT binary patch literal 65536 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 literal 0 HcmV?d00001 diff --git a/main.zig b/main.zig index d32cd7d..b5cb6c4 100644 --- a/main.zig +++ b/main.zig @@ -16,14 +16,14 @@ const cairo = @cImport({ }); const Point = struct { - x: i32, - y: i32, + x: i32 = 0, + y: i32 = 0, }; const Size = struct { const Self = @This(); - width: u32, - height: u32, + width: u32 = 0, + height: u32 = 0, fn size(self: *Self) usize { return self.width * self.height; @@ -33,19 +33,27 @@ const Size = struct { const Box = struct { const Self = @This(); - position: Point, - extents: Size, + position: Point = .{.x = 0, .y = 0,}, + 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 { const x1 = self.position.x; const y1 = self.position.y; - const w1 = self.extents.width; - const h1 = self.extents.height; + const w1 = @intCast(isize, self.extents.width); + const h1 = @intCast(isize, self.extents.height); const x2 = other.position.x; 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 // self.x + self.width > other.x and @@ -58,11 +66,29 @@ const Box = struct { 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 y1 = self.position.y; - const w1 = self.extents.width; - const h1 = self.extents.height; + const w1 = @intCast(isize, self.extents.width); + const h1 = @intCast(isize, self.extents.height); const x2 = point.x; 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 { return struct { const Self = @This(); 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 { return list.Head(Self, "link"); @@ -94,7 +158,12 @@ fn ListItem(comptime T: type) type { return @fieldParentPtr(Self, "link", link); } + /// automatically calls `deinit()` on `data` if a function with that name exists fn remove(self: *Self) void { + if (@hasDecl(T, "deinit")) { + self.value.deinit(); + } + self.link.remove(); } @@ -166,12 +235,252 @@ const Options = struct { const Output = struct { const Self = @This(); - state: *State, + state: *State.Init = undefined, output: *wl.Output, - geometry: Box, - logical_geometry: Box, - scale: i32, + surface: *wl.Surface = undefined, + layer_surface: *zwlr.LayerSurfaceV1 = undefined, + 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 @@ -202,10 +511,10 @@ fn Listener(comptime T: type, comptime D: type) type { self: *Self, comptime D2: type, new_data: *D2, - new_callback: @TypeOf(Listener(T, D2)).Fn, - ) @TypeOf(Listener(T, D2)) { - const other = @ptrCast(Listener(T, D2), self); - other.new_data = new_data; + new_callback: Listener(T, D2).Fn, + ) *Listener(T, D2) { + const other = @ptrCast(*Listener(T, D2), self); + other.data = new_data; other.callback = new_callback; return other; } @@ -221,13 +530,16 @@ const State = struct { dpy: *wl.Display = undefined, registry: *wl.Registry = undefined, - registry_listener: Listener(wl.Registry, Self) = undefined, + registry_listener: *Listener(wl.Registry, Self) = undefined, shm: ?*wl.Shm = null, compositor: ?*wl.Compositor = null, layer_shell: ?*zwlr.LayerShellV1 = null, xdg_output_manager: ?*zxdg.OutputManagerV1 = null, + seats: *ListItem(Seat).Head() = undefined, + outputs: *ListItem(Output).Head() = undefined, + fn create() !*Self { const ally = std.heap.c_allocator; @@ -236,7 +548,9 @@ const State = struct { const self = try ally.create(Self); - const listener = .{ + const listener = try ally.create(Listener(wl.Registry, Self)); + + listener.* = .{ .data = self, .callback = registryListener, }; @@ -246,16 +560,25 @@ const State = struct { .dpy = dpy, .registry = registry, .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); if (dpy.roundtrip() != .SUCCESS) return error.RoundtripFailed; 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); + + // TODO: clean up wayland state } 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) { self.layer_shell = registry.bind(global.name, zwlr.LayerShellV1, 1) catch return; } 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; + } else if (std.cstr.cmp(global.interface, wl.Seat.getInterface().name) == 0) { 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) { 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 => {}, @@ -292,42 +623,7 @@ const State = struct { // free allocated memory if not returning it errdefer self.ally.destroy(init); - 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(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); + try init.init(self); return init; } @@ -341,7 +637,7 @@ const State = struct { dpy: *wl.Display = undefined, registry: *wl.Registry = undefined, - registry_listener: Listener(wl.Registry, Self) = undefined, + registry_listener: *Listener(wl.Registry, Self) = undefined, shm: *wl.Shm = undefined, compositor: *wl.Compositor = undefined, @@ -350,11 +646,70 @@ const State = struct { xkb_context: *xkb.xkb_context = undefined, - outputs: ListItem(Output).Head() = undefined, - cursor_theme: []const u8 = 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 { _ = registry; _ = 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 { 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.compositor.destroy(); self.layer_shell.destroy(); @@ -386,8 +790,7 @@ pub fn main() !void { const state = try State.Uninit.create(); const init = try state.intoInit(); - - _ = try std.io.getStdIn().reader().readByte(); - defer init.deinit(); + + try init.stuff(); }