a73x

aba50517

Bump xdg_wm_base to v6 and honor toplevel suspended state

a73x   2026-04-16 10:53

Commit message
Bump xdg_wm_base to v6 and honor toplevel suspended state

The scanner already generates v6 bindings (build.zig:31); this commit
bumps the runtime bind version and hooks xdg_toplevel.configure.states
into SurfaceState.suspended. Compositors that don't send .suspended
(pre-v6 impls) see no behavior change.

src/wayland.zig
Old New
@@ -1078,11 +1078,27 @@ fn surfaceListener(_: *wl.Surface, event: wl.Surface.Event, window: *Window) voi
1078 } 1078 }
1079 } 1079 }
1080 1080
1081 fn applyToplevelStates(state: *SurfaceState, state_bytes: []const u8) void {
1082 // xdg_toplevel.configure delivers the state array as a wl_array of u32.
1083 // Re-interpret to u32 slice and scan for `.suspended`.
1084 const u32_count = state_bytes.len / @sizeOf(u32);
1085 const states = std.mem.bytesAsSlice(u32, state_bytes[0 .. u32_count * @sizeOf(u32)]);
1086 var suspended = false;
1087 for (states) |raw| {
1088 if (raw == @intFromEnum(xdg.Toplevel.State.suspended)) {
1089 suspended = true;
1090 break;
1091 }
1092 }
1093 state.suspended = suspended;
1094 }
1095
1081 fn xdgToplevelListener(_: *xdg.Toplevel, event: xdg.Toplevel.Event, window: *Window) void { 1096 fn xdgToplevelListener(_: *xdg.Toplevel, event: xdg.Toplevel.Event, window: *Window) void {
1082 switch (event) { 1097 switch (event) {
1083 .configure => |cfg| { 1098 .configure => |cfg| {
1084 if (cfg.width > 0) window.width = @intCast(cfg.width); 1099 if (cfg.width > 0) window.width = @intCast(cfg.width);
1085 if (cfg.height > 0) window.height = @intCast(cfg.height); 1100 if (cfg.height > 0) window.height = @intCast(cfg.height);
1101 applyToplevelStates(&window.state, std.mem.sliceAsBytes(cfg.states.slice(u32)));
1086 }, 1102 },
1087 .close => window.should_close = true, 1103 .close => window.should_close = true,
1088 .configure_bounds => {}, 1104 .configure_bounds => {},
@@ -1103,7 +1119,7 @@ fn registryListener(
1103 } else if (std.mem.eql(u8, iface, std.mem.span(wl.DataDeviceManager.interface.name))) { 1119 } else if (std.mem.eql(u8, iface, std.mem.span(wl.DataDeviceManager.interface.name))) {
1104 conn.globals.data_device_manager = registry.bind(g.name, wl.DataDeviceManager, 3) catch return; 1120 conn.globals.data_device_manager = registry.bind(g.name, wl.DataDeviceManager, 3) catch return;
1105 } else if (std.mem.eql(u8, iface, std.mem.span(xdg.WmBase.interface.name))) { 1121 } else if (std.mem.eql(u8, iface, std.mem.span(xdg.WmBase.interface.name))) {
1106 conn.globals.wm_base = registry.bind(g.name, xdg.WmBase, 5) catch return; 1122 conn.globals.wm_base = registry.bind(g.name, xdg.WmBase, 6) catch return;
1107 } else if (std.mem.eql(u8, iface, std.mem.span(wl.Seat.interface.name))) { 1123 } else if (std.mem.eql(u8, iface, std.mem.span(wl.Seat.interface.name))) {
1108 conn.globals.seat = registry.bind(g.name, wl.Seat, 9) catch return; 1124 conn.globals.seat = registry.bind(g.name, wl.Seat, 9) catch return;
1109 } else if (std.mem.eql(u8, iface, std.mem.span(wl.Output.interface.name))) { 1125 } else if (std.mem.eql(u8, iface, std.mem.span(wl.Output.interface.name))) {
@@ -1257,3 +1273,24 @@ test "SurfaceState.visible requires configured && !suspended && enteredCount > 0
1257 tracker.leaveOutput(1); 1273 tracker.leaveOutput(1);
1258 try std.testing.expect(!state.visible()); 1274 try std.testing.expect(!state.visible());
1259 } 1275 }
1276
1277 test "SurfaceState.suspended toggles from xdg_toplevel.configure.states" {
1278 var tracker = ScaleTracker.init(std.testing.allocator);
1279 defer tracker.deinit();
1280 try tracker.addOutput(1);
1281 try tracker.enterOutput(1);
1282
1283 var state = SurfaceState{ .tracker = &tracker, .configured = true };
1284 try std.testing.expect(state.visible());
1285
1286 // Helper function under test — applies xdg_toplevel state array to SurfaceState.
1287 const states_suspended = [_]u32{@intFromEnum(xdg.Toplevel.State.suspended)};
1288 applyToplevelStates(&state, std.mem.sliceAsBytes(&states_suspended));
1289 try std.testing.expect(state.suspended);
1290 try std.testing.expect(!state.visible());
1291
1292 const states_none = [_]u32{};
1293 applyToplevelStates(&state, std.mem.sliceAsBytes(&states_none));
1294 try std.testing.expect(!state.suspended);
1295 try std.testing.expect(state.visible());
1296 }