a73x

eb6c0232

Add real-path DisplayOps adapter on Window

a73x   2026-04-16 11:08

Commit message
Add real-path DisplayOps adapter on Window

DisplayAdapter thinly wraps wl.Display + wl.Surface and satisfies the
DisplayOps vtable. frameCallbackListener receives wl_callback.done and
forwards it to FrameLoop.onFrameCallbackDone, which performs the
identity check.

src/wayland.zig
Old New
@@ -11,6 +11,12 @@ const c = @cImport({
11 @cInclude("unistd.h"); 11 @cInclude("unistd.h");
12 }); 12 });
13 13
14 const frame_loop_mod = @import("frame_loop");
15 pub const FrameLoop = frame_loop_mod.FrameLoop;
16 pub const DisplayOps = frame_loop_mod.DisplayOps;
17 pub const SurfaceStateView = frame_loop_mod.SurfaceStateView;
18 pub const CallbackToken = frame_loop_mod.CallbackToken;
19
14 pub const Output = struct { 20 pub const Output = struct {
15 wl_output: *wl.Output, 21 wl_output: *wl.Output,
16 name: u32, 22 name: u32,
@@ -555,6 +561,56 @@ fn choosePreferredMime(mime_types: []const [:0]const u8) ?[:0]const u8 {
555 return null; 561 return null;
556 } 562 }
557 563
564 pub const DisplayAdapter = struct {
565 display: *wl.Display,
566 surface: *wl.Surface,
567 // NOTE: The frame listener receives `loop: *FrameLoop` directly through
568 // zig-wayland's setListener data slot, so the adapter does not need to
569 // carry a reference to the loop itself to dispatch .done events.
570
571 fn flushThunk(ctx: *anyopaque) void {
572 const self: *DisplayAdapter = @ptrCast(@alignCast(ctx));
573 _ = self.display.flush();
574 }
575 fn prepareReadThunk(ctx: *anyopaque) bool {
576 const self: *DisplayAdapter = @ptrCast(@alignCast(ctx));
577 return self.display.prepareRead();
578 }
579 fn readEventsThunk(ctx: *anyopaque) void {
580 const self: *DisplayAdapter = @ptrCast(@alignCast(ctx));
581 _ = self.display.readEvents();
582 }
583 fn dispatchPendingThunk(ctx: *anyopaque) void {
584 const self: *DisplayAdapter = @ptrCast(@alignCast(ctx));
585 _ = self.display.dispatchPending();
586 }
587 fn getFdThunk(ctx: *anyopaque) std.posix.fd_t {
588 const self: *DisplayAdapter = @ptrCast(@alignCast(ctx));
589 return self.display.getFd();
590 }
591
592 fn requestFrameThunk(ctx: *anyopaque, loop: *FrameLoop) anyerror!CallbackToken {
593 const self: *DisplayAdapter = @ptrCast(@alignCast(ctx));
594 const cb = try self.surface.frame();
595 cb.setListener(*FrameLoop, frameCallbackListener, loop);
596 return @ptrCast(cb);
597 }
598
599 fn destroyCallbackThunk(_: *anyopaque, token: CallbackToken) void {
600 const cb: *wl.Callback = @constCast(@ptrCast(@alignCast(token)));
601 cb.destroy();
602 }
603 };
604
605 fn frameCallbackListener(cb: *wl.Callback, event: wl.Callback.Event, loop: *FrameLoop) void {
606 switch (event) {
607 .done => {
608 const tok: CallbackToken = @ptrCast(cb);
609 loop.onFrameCallbackDone(tok);
610 },
611 }
612 }
613
558 pub const Window = struct { 614 pub const Window = struct {
559 alloc: std.mem.Allocator, 615 alloc: std.mem.Allocator,
560 surface: *wl.Surface, 616 surface: *wl.Surface,
@@ -568,6 +624,7 @@ pub const Window = struct {
568 should_close: bool = false, 624 should_close: bool = false,
569 width: u32 = 800, 625 width: u32 = 800,
570 height: u32 = 600, 626 height: u32 = 600,
627 display_adapter: DisplayAdapter = undefined,
571 628
572 pub fn deinit(self: *Window) void { 629 pub fn deinit(self: *Window) void {
573 self.xdg_toplevel.destroy(); 630 self.xdg_toplevel.destroy();
@@ -601,6 +658,33 @@ pub const Window = struct {
601 } 658 }
602 } 659 }
603 } 660 }
661
662 /// Build a DisplayOps vtable that drives the real compositor.
663 pub fn displayOps(self: *Window, display: *wl.Display) DisplayOps {
664 // Carry both display and surface via the owned adapter struct.
665 self.display_adapter = .{
666 .display = display,
667 .surface = self.surface,
668 };
669 return .{
670 .ctx = &self.display_adapter,
671 .flushFn = DisplayAdapter.flushThunk,
672 .prepareReadFn = DisplayAdapter.prepareReadThunk,
673 .readEventsFn = DisplayAdapter.readEventsThunk,
674 .dispatchPendingFn = DisplayAdapter.dispatchPendingThunk,
675 .getFdFn = DisplayAdapter.getFdThunk,
676 .requestFrameFn = DisplayAdapter.requestFrameThunk,
677 .destroyCallbackFn = DisplayAdapter.destroyCallbackThunk,
678 };
679 }
680
681 pub fn surfaceStateView(self: *const Window) SurfaceStateView {
682 return .{
683 .configured_ptr = &self.state.configured,
684 .suspended_ptr = &self.state.suspended,
685 .tracker = self.tracker,
686 };
687 }
604 }; 688 };
605 689
606 pub const Connection = struct { 690 pub const Connection = struct {