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 { | ||||
|     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(); | ||||
| } | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue