From aa48290db3966330ccf00a3f22984cbb4ece64c6 Mon Sep 17 00:00:00 2001 From: Lurkie Date: Sat, 16 Aug 2025 19:24:55 -0600 Subject: [PATCH] feat: add XDG decoration support --- build.zig | 3 +++ src/wayland.zig | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/build.zig b/build.zig index c176e64..0920089 100644 --- a/build.zig +++ b/build.zig @@ -70,6 +70,9 @@ pub fn build(b: *std.Build) !void { scanner.addSystemProtocol("stable/xdg-shell/xdg-shell.xml"); scanner.generate("xdg_wm_base", 1); + scanner.addSystemProtocol("unstable/xdg-decoration/xdg-decoration-unstable-v1.xml"); + scanner.generate("zxdg_decoration_manager_v1", 1); + exe.root_module.addImport("wayland", wayland); exe.linkLibC(); exe.linkSystemLibrary("wayland-client"); diff --git a/src/wayland.zig b/src/wayland.zig index 62fa8fd..c2a316c 100644 --- a/src/wayland.zig +++ b/src/wayland.zig @@ -19,6 +19,7 @@ const Config = configpkg.Config; const wayland = @import("wayland"); const wl = wayland.client.wl; const xdg = wayland.client.xdg; +const zxdg = wayland.client.zxdg; const zwp = wayland.client.zwp; const wp = wayland.client.wp; @@ -42,6 +43,7 @@ const Context = struct { compositor: ?*wl.Compositor, wm_base: ?*xdg.WmBase, data_device_manager: ?*wl.DataDeviceManager, + decoration_manager: ?*zxdg.DecorationManagerV1, selection_device_manager: ?*zwp.PrimarySelectionDeviceManagerV1, cursor_shape_manager: ?*wp.CursorShapeManagerV1, surface_map: *SurfaceMap, @@ -684,6 +686,8 @@ fn registryListener(registry: *wl.Registry, event: wl.Registry.Event, context: * context.selection_device_manager = registry.bind(global.name, zwp.PrimarySelectionDeviceManagerV1, 1) catch return; } else if (std.mem.orderZ(u8, global.interface, wp.CursorShapeManagerV1.interface.name) == .eq) { context.cursor_shape_manager = registry.bind(global.name, wp.CursorShapeManagerV1, 1) catch return; + } else if (std.mem.orderZ(u8, global.interface, zxdg.DecorationManagerV1.interface.name) == .eq) { + context.decoration_manager = registry.bind(global.name, zxdg.DecorationManagerV1, 1) catch return; } }, .global_remove => {}, @@ -697,6 +701,7 @@ pub const App = struct { wm_base: *xdg.WmBase, registry: *wl.Registry, data_device_manager: *wl.DataDeviceManager, + decoration_manager: ?*zxdg.DecorationManagerV1, selection_device_manager: *zwp.PrimarySelectionDeviceManagerV1, data_device: *wl.DataDevice, selection_device: *zwp.PrimarySelectionDeviceV1, @@ -733,6 +738,7 @@ pub const App = struct { .compositor = null, .wm_base = null, .data_device_manager = null, + .decoration_manager = null, .selection_device_manager = null, .cursor_shape_manager = null, .surface_map = surface_map, @@ -839,6 +845,7 @@ pub const App = struct { .wm_base = wm_base, .data_device_manager = data_device_manager, .data_device = data_device, + .decoration_manager = context.decoration_manager, .selection_device_manager = selection_device_manager, .selection_device = selection_device, .cursor_shape_manager = cursor_shape_manager, @@ -1278,6 +1285,7 @@ pub const Surface = struct { wl_surface: *wl.Surface, xdg_surface: *xdg.Surface, xdg_toplevel: *xdg.Toplevel, + xdg_decoration: ?*zxdg.ToplevelDecorationV1, data_offer: ?*wl.DataOffer, selection_offer: ?*zwp.PrimarySelectionOfferV1, @@ -1338,6 +1346,7 @@ pub const Surface = struct { self.pressed_key = null; self.repeat_rate = 0; self.repeat_delay = 0; + self.xdg_decoration = null; self.wl_surface = try app.compositor.createSurface(); errdefer self.wl_surface.destroy(); @@ -1348,6 +1357,11 @@ pub const Surface = struct { self.xdg_surface.setListener(*Surface, xdgSurfaceListener, self); self.xdg_toplevel.setListener(*Surface, xdgToplevelListener, self); + if (app.decoration_manager) |decoration_manager| { + self.xdg_decoration = try decoration_manager.getToplevelDecoration(self.xdg_toplevel); + self.xdg_decoration.?.setMode(.server_side); + } + const app_id = app.config.class orelse "com.mitchellh.ghostty"; self.xdg_toplevel.setAppId(app_id); @@ -1416,6 +1430,10 @@ pub const Surface = struct { // Clean up our core surface so that all the rendering and IO stop. self.core_surface.deinit(); + if (self.xdg_decoration) |decor| { + decor.destroy(); + } + self.xdg_toplevel.destroy(); self.xdg_surface.destroy(); self.wl_surface.destroy();