diff --git a/src/apprt/gtk/App.zig b/src/apprt/gtk/App.zig index fa5eb7b9f..ecbe61bce 100644 --- a/src/apprt/gtk/App.zig +++ b/src/apprt/gtk/App.zig @@ -36,8 +36,7 @@ const c = @import("c.zig").c; const version = @import("version.zig"); const inspector = @import("inspector.zig"); const key = @import("key.zig"); -const x11 = @import("x11.zig"); -const wayland = @import("wayland.zig"); +const protocol = @import("protocol.zig"); const testing = std.testing; const log = std.log.scoped(.gtk); @@ -71,11 +70,7 @@ clipboard_confirmation_window: ?*ClipboardConfirmationWindow = null, /// This is set to false when the main loop should exit. running: bool = true, -/// Xkb state (X11 only). Will be null on Wayland. -x11_xkb: ?x11.Xkb = null, - -/// Wayland app state. Will be null on X11. -wayland: ?wayland.AppState = null, +protocol: protocol.App, /// The base path of the transient cgroup used to put all surfaces /// into their own cgroup. This is only set if cgroups are enabled @@ -364,46 +359,7 @@ pub fn init(core_app: *CoreApp, opts: Options) !App { return error.GtkApplicationRegisterFailed; } - // Perform all X11 initialization. This ultimately returns the X11 - // keyboard state but the block does more than that (i.e. setting up - // WM_CLASS). - const x11_xkb: ?x11.Xkb = x11_xkb: { - if (comptime !build_options.x11) break :x11_xkb null; - if (!x11.is_display(display)) break :x11_xkb null; - - // Set the X11 window class property (WM_CLASS) if are are on an X11 - // display. - // - // Note that we also set the program name here using g_set_prgname. - // This is how the instance name field for WM_CLASS is derived when - // calling gdk_x11_display_set_program_class; there does not seem to be - // a way to set it directly. It does not look like this is being set by - // our other app initialization routines currently, but since we're - // currently deriving its value from x11-instance-name effectively, I - // feel like gating it behind an X11 check is better intent. - // - // This makes the property show up like so when using xprop: - // - // WM_CLASS(STRING) = "ghostty", "com.mitchellh.ghostty" - // - // Append "-debug" on both when using the debug build. - // - const prgname = if (config.@"x11-instance-name") |pn| - pn - else if (builtin.mode == .Debug) - "ghostty-debug" - else - "ghostty"; - c.g_set_prgname(prgname); - c.gdk_x11_display_set_program_class(display, app_id); - - // Set up Xkb - break :x11_xkb try x11.Xkb.init(display); - }; - - // Initialize Wayland state - var wl = wayland.AppState.init(display); - if (wl) |*w| try w.register(); + const app_protocol = try protocol.App.init(display, &config, app_id); // This just calls the `activate` signal but its part of the normal startup // routine so we just call it, but only if the config allows it (this allows @@ -429,8 +385,7 @@ pub fn init(core_app: *CoreApp, opts: Options) !App { .config = config, .ctx = ctx, .cursor_none = cursor_none, - .x11_xkb = x11_xkb, - .wayland = wl, + .protocol = app_protocol, .single_instance = single_instance, // If we are NOT the primary instance, then we never want to run. // This means that another instance of the GTK app is running and diff --git a/src/apprt/gtk/Surface.zig b/src/apprt/gtk/Surface.zig index 35932ac5e..97b7bb0f4 100644 --- a/src/apprt/gtk/Surface.zig +++ b/src/apprt/gtk/Surface.zig @@ -25,7 +25,6 @@ const ResizeOverlay = @import("ResizeOverlay.zig"); const inspector = @import("inspector.zig"); const gtk_key = @import("key.zig"); const c = @import("c.zig").c; -const x11 = @import("x11.zig"); const log = std.log.scoped(.gtk_surface); @@ -825,9 +824,6 @@ pub fn getContentScale(self: *const Surface) !apprt.ContentScale { c.g_object_get_property(@ptrCast(@alignCast(settings)), "gtk-xft-dpi", &value); const gtk_xft_dpi = c.g_value_get_int(&value); - // As noted above gtk-xft-dpi is multiplied by 1024, so we divide by - // 1024, then divide by the default value (96) to derive a scale. Note - // gtk-xft-dpi can be fractional, so we use floating point math here. const xft_dpi: f32 = @as(f32, @floatFromInt(gtk_xft_dpi)) / 1024; break :xft_scale xft_dpi / 96; }; @@ -1384,6 +1380,10 @@ fn gtkResize(area: *c.GtkGLArea, width: c.gint, height: c.gint, ud: ?*anyopaque) return; }; + if (self.container.window()) |window| window.protocol.onResize() catch |err| { + log.warn("failed to notify X11/Wayland integration of resize={}", .{err}); + }; + self.resize_overlay.maybeShow(); } } @@ -1699,11 +1699,10 @@ pub fn keyEvent( // Get our modifier for the event const mods: input.Mods = gtk_key.eventMods( - @ptrCast(self.gl_area), event, physical_key, gtk_mods, - if (self.app.x11_xkb) |*xkb| xkb else null, + &self.app.protocol, ); // Get our consumed modifiers diff --git a/src/apprt/gtk/Window.zig b/src/apprt/gtk/Window.zig index 0f44cee7b..fbba22195 100644 --- a/src/apprt/gtk/Window.zig +++ b/src/apprt/gtk/Window.zig @@ -25,7 +25,7 @@ const gtk_key = @import("key.zig"); const Notebook = @import("notebook.zig").Notebook; const HeaderBar = @import("headerbar.zig").HeaderBar; const version = @import("version.zig"); -const wayland = @import("wayland.zig"); +const protocol = @import("protocol.zig"); const log = std.log.scoped(.gtk); @@ -56,7 +56,7 @@ toast_overlay: ?*c.GtkWidget, /// See adwTabOverviewOpen for why we have this. adw_tab_overview_focus_timer: ?c.guint = null, -wayland: ?wayland.SurfaceState, +protocol: protocol.Surface, pub fn create(alloc: Allocator, app: *App) !*Window { // Allocate a fixed pointer for our window. We try to minimize @@ -82,7 +82,7 @@ pub fn init(self: *Window, app: *App) !void { .notebook = undefined, .context_menu = undefined, .toast_overlay = undefined, - .wayland = null, + .protocol = undefined, }; // Create the window @@ -396,14 +396,8 @@ pub fn syncAppearance(self: *Window, config: *const configpkg.Config) !void { c.gtk_widget_add_css_class(@ptrCast(self.window), "background"); } - if (self.wayland) |*wl| { - const blurred = switch (config.@"background-blur-radius") { - .false => false, - .true => true, - .radius => |v| v > 0, - }; - try wl.setBlur(blurred); - } + // Perform protocol-specific config updates + try self.protocol.onConfigUpdate(config); } /// Sets up the GTK actions for the window scope. Actions are how GTK handles @@ -443,7 +437,7 @@ fn initActions(self: *Window) void { pub fn deinit(self: *Window) void { c.gtk_widget_unparent(@ptrCast(self.context_menu)); - if (self.wayland) |*wl| wl.deinit(); + self.protocol.deinit(); if (self.adw_tab_overview_focus_timer) |timer| { _ = c.g_source_remove(timer); @@ -584,9 +578,7 @@ pub fn sendToast(self: *Window, title: [:0]const u8) void { fn gtkRealize(v: *c.GtkWindow, ud: ?*anyopaque) callconv(.C) bool { const self = userdataSelf(ud.?); - if (self.app.wayland) |*wl| { - self.wayland = wayland.SurfaceState.init(v, wl); - } + self.protocol.init(v, &self.app.protocol, &self.app.config); self.syncAppearance(&self.app.config) catch |err| { log.err("failed to initialize appearance={}", .{err}); diff --git a/src/apprt/gtk/key.zig b/src/apprt/gtk/key.zig index 311bff0da..ef460e62c 100644 --- a/src/apprt/gtk/key.zig +++ b/src/apprt/gtk/key.zig @@ -2,7 +2,7 @@ const std = @import("std"); const build_options = @import("build_options"); const input = @import("../../input.zig"); const c = @import("c.zig").c; -const x11 = @import("x11.zig"); +const protocol = @import("protocol.zig"); /// Returns a GTK accelerator string from a trigger. pub fn accelFromTrigger(buf: []u8, trigger: input.Binding.Trigger) !?[:0]const u8 { @@ -105,34 +105,14 @@ pub fn keyvalUnicodeUnshifted( /// This requires a lot of context because the GdkEvent /// doesn't contain enough on its own. pub fn eventMods( - widget: *c.GtkWidget, event: *c.GdkEvent, physical_key: input.Key, gtk_mods: c.GdkModifierType, - x11_xkb: ?*x11.Xkb, + app_protocol: *protocol.App, ) input.Mods { const device = c.gdk_event_get_device(event); - var mods = mods: { - // Add any modifier state events from Xkb if we have them (X11 - // only). Null back from the Xkb call means there was no modifier - // event to read. This likely means that the key event did not - // result in a modifier change and we can safely rely on the GDK - // state. - if (comptime build_options.x11) { - const display = c.gtk_widget_get_display(widget); - if (x11_xkb) |xkb| { - if (xkb.modifier_state_from_notify(display)) |x11_mods| break :mods x11_mods; - break :mods translateMods(gtk_mods); - } - } - - // On Wayland, we have to use the GDK device because the mods sent - // to this event do not have the modifier key applied if it was - // pressed (i.e. left control). - break :mods translateMods(c.gdk_device_get_modifier_state(device)); - }; - + var mods = app_protocol.eventMods(device, gtk_mods); mods.num_lock = c.gdk_device_get_num_lock_state(device) == 1; switch (physical_key) { diff --git a/src/apprt/gtk/protocol.zig b/src/apprt/gtk/protocol.zig new file mode 100644 index 000000000..c7d7247cf --- /dev/null +++ b/src/apprt/gtk/protocol.zig @@ -0,0 +1,149 @@ +const std = @import("std"); +const x11 = @import("protocol/x11.zig"); +const wayland = @import("protocol/wayland.zig"); +const c = @import("c.zig").c; +const build_options = @import("build_options"); +const input = @import("../../input.zig"); +const apprt = @import("../../apprt.zig"); +const Config = @import("../../config.zig").Config; +const adwaita = @import("adwaita.zig"); +const builtin = @import("builtin"); +const key = @import("key.zig"); + +const log = std.log.scoped(.gtk_platform); + +pub const App = struct { + gdk_display: *c.GdkDisplay, + derived_config: DerivedConfig, + + inner: union(enum) { + none, + x11: if (build_options.x11) x11.App else void, + wayland: if (build_options.wayland) wayland.App else void, + }, + + const DerivedConfig = struct { + app_id: [:0]const u8, + x11_program_name: [:0]const u8, + + pub fn init(config: *const Config, app_id: [:0]const u8) DerivedConfig { + return .{ + .app_id = app_id, + .x11_program_name = if (config.@"x11-instance-name") |pn| + pn + else if (builtin.mode == .Debug) + "ghostty-debug" + else + "ghostty", + }; + } + }; + + pub fn init(display: ?*c.GdkDisplay, config: *const Config, app_id: [:0]const u8) !App { + var self: App = .{ + .inner = .none, + .derived_config = DerivedConfig.init(config, app_id), + .gdk_display = display orelse { + // TODO: When does this ever happen...? + std.debug.panic("GDK display is null!", .{}); + }, + }; + + // The X11/Wayland init functions set `self.inner` when successful, + // so we only need to keep trying if `self.inner` stays `.none` + if (self.inner == .none and comptime build_options.wayland) try wayland.App.init(&self); + if (self.inner == .none and comptime build_options.x11) try x11.App.init(&self); + + // Welp, no integration for you + if (self.inner == .none) { + log.warn( + "neither X11 nor Wayland integrations enabled - lots of features would be missing!", + .{}, + ); + } + + return self; + } + + pub fn eventMods(self: *App, device: ?*c.GdkDevice, gtk_mods: c.GdkModifierType) input.Mods { + return switch (self.inner) { + // Add any modifier state events from Xkb if we have them (X11 + // only). Null back from the Xkb call means there was no modifier + // event to read. This likely means that the key event did not + // result in a modifier change and we can safely rely on the GDK + // state. + .x11 => |*x| if (comptime build_options.x11) + x.modifierStateFromNotify() orelse key.translateMods(gtk_mods) + else + unreachable, + + // On Wayland, we have to use the GDK device because the mods sent + // to this event do not have the modifier key applied if it was + // pressed (i.e. left control). + .wayland, .none => key.translateMods(c.gdk_device_get_modifier_state(device)), + }; + } +}; + +pub const Surface = struct { + app: *App, + gtk_window: *c.GtkWindow, + derived_config: DerivedConfig, + + inner: union(enum) { + none, + x11: if (build_options.x11) x11.Surface else void, + wayland: if (build_options.wayland) wayland.Surface else void, + }, + + pub const DerivedConfig = struct { + blur: Config.BackgroundBlur, + adw_enabled: bool, + + pub fn init(config: *const Config) DerivedConfig { + return .{ + .blur = config.@"background-blur-radius", + .adw_enabled = adwaita.enabled(config), + }; + } + }; + + pub fn init(self: *Surface, window: *c.GtkWindow, app: *App, config: *const Config) void { + self.* = .{ + .app = app, + .derived_config = DerivedConfig.init(config), + .gtk_window = window, + .inner = .none, + }; + + switch (app.inner) { + .x11 => if (comptime build_options.x11) x11.Surface.init(self) else unreachable, + .wayland => if (comptime build_options.wayland) wayland.Surface.init(self) else unreachable, + .none => {}, + } + } + + pub fn deinit(self: Surface) void { + switch (self.inner) { + .wayland => |wl| if (comptime build_options.wayland) wl.deinit() else unreachable, + .x11, .none => {}, + } + } + + pub fn onConfigUpdate(self: *Surface, config: *const Config) !void { + self.derived_config = DerivedConfig.init(config); + + switch (self.inner) { + .x11 => |*x| if (comptime build_options.x11) try x.onConfigUpdate() else unreachable, + .wayland => |*wl| if (comptime build_options.wayland) try wl.onConfigUpdate() else unreachable, + .none => {}, + } + } + + pub fn onResize(self: *Surface) !void { + switch (self.inner) { + .x11 => |*x| if (comptime build_options.x11) try x.onResize() else unreachable, + .wayland, .none => {}, + } + } +}; diff --git a/src/apprt/gtk/wayland.zig b/src/apprt/gtk/protocol/wayland.zig similarity index 58% rename from src/apprt/gtk/wayland.zig rename to src/apprt/gtk/protocol/wayland.zig index 92446cc46..985d7c5a8 100644 --- a/src/apprt/gtk/wayland.zig +++ b/src/apprt/gtk/protocol/wayland.zig @@ -1,106 +1,106 @@ const std = @import("std"); -const c = @import("c.zig").c; +const c = @import("../c.zig").c; const wayland = @import("wayland"); +const protocol = @import("../protocol.zig"); +const Config = @import("../../../config.zig").Config; + const wl = wayland.client.wl; const org = wayland.client.org; -const build_options = @import("build_options"); const log = std.log.scoped(.gtk_wayland); /// Wayland state that contains application-wide Wayland objects (e.g. wl_display). -pub const AppState = struct { +pub const App = struct { display: *wl.Display, blur_manager: ?*org.KdeKwinBlurManager = null, - pub fn init(display: ?*c.GdkDisplay) ?AppState { - if (comptime !build_options.wayland) return null; - - // It should really never be null - const display_ = display orelse return null; - + pub fn init(common: *protocol.App) !void { // Check if we're actually on Wayland if (c.g_type_check_instance_is_a( - @ptrCast(@alignCast(display_)), + @ptrCast(@alignCast(common.gdk_display)), c.gdk_wayland_display_get_type(), ) == 0) - return null; + return; - const wl_display: *wl.Display = @ptrCast(c.gdk_wayland_display_get_wl_display(display_) orelse return null); - - return .{ - .display = wl_display, + var self: App = .{ + .display = @ptrCast(c.gdk_wayland_display_get_wl_display(common.gdk_display) orelse return), }; - } - pub fn register(self: *AppState) !void { + log.debug("wayland platform init={}", .{self}); + const registry = try self.display.getRegistry(); - registry.setListener(*AppState, registryListener, self); + registry.setListener(*App, registryListener, &self); if (self.display.roundtrip() != .SUCCESS) return error.RoundtripFailed; - log.debug("app wayland init={}", .{self}); + common.inner = .{ .wayland = self }; } }; /// Wayland state that contains Wayland objects associated with a window (e.g. wl_surface). -pub const SurfaceState = struct { - app_state: *AppState, +pub const Surface = struct { + common: *const protocol.Surface, + app: *App, surface: *wl.Surface, /// A token that, when present, indicates that the window is blurred. blur_token: ?*org.KdeKwinBlur = null, - pub fn init(window: *c.GtkWindow, app_state: *AppState) ?SurfaceState { - if (comptime !build_options.wayland) return null; - - const surface = c.gtk_native_get_surface(@ptrCast(window)) orelse return null; + pub fn init(common: *protocol.Surface) void { + const surface = c.gtk_native_get_surface(@ptrCast(common.gtk_window)) orelse return; // Check if we're actually on Wayland if (c.g_type_check_instance_is_a( @ptrCast(@alignCast(surface)), c.gdk_wayland_surface_get_type(), ) == 0) - return null; + return; - const wl_surface: *wl.Surface = @ptrCast(c.gdk_wayland_surface_get_wl_surface(surface) orelse return null); - - return .{ - .app_state = app_state, - .surface = wl_surface, + const self: Surface = .{ + .common = common, + .app = &common.app.inner.wayland, + .surface = @ptrCast(c.gdk_wayland_surface_get_wl_surface(surface) orelse return), }; + + common.inner = .{ .wayland = self }; } - pub fn deinit(self: *SurfaceState) void { + pub fn deinit(self: Surface) void { if (self.blur_token) |blur| blur.release(); } - pub fn setBlur(self: *SurfaceState, blurred: bool) !void { - log.debug("setting blur={}", .{blurred}); + pub fn onConfigUpdate(self: *Surface) !void { + try self.updateBlur(); + } - const mgr = self.app_state.blur_manager orelse { + fn updateBlur(self: *Surface) !void { + const blur = self.common.derived_config.blur; + log.debug("setting blur={}", .{blur}); + + const mgr = self.app.blur_manager orelse { log.warn("can't set blur: org_kde_kwin_blur_manager protocol unavailable", .{}); return; }; - if (self.blur_token) |blur| { + if (self.blur_token) |tok| { // Only release token when transitioning from blurred -> not blurred - if (!blurred) { + if (!blur.enabled()) { mgr.unset(self.surface); - blur.release(); + tok.release(); self.blur_token = null; } } else { // Only acquire token when transitioning from not blurred -> blurred - if (blurred) { - const blur_token = try mgr.create(self.surface); - blur_token.commit(); - self.blur_token = blur_token; + if (blur.enabled()) { + const tok = try mgr.create(self.surface); + tok.commit(); + self.blur_token = tok; } } } }; -fn registryListener(registry: *wl.Registry, event: wl.Registry.Event, state: *AppState) void { +fn registryListener(registry: *wl.Registry, event: wl.Registry.Event, state: *App) void { switch (event) { .global => |global| { log.debug("got global interface={s}", .{global.interface}); diff --git a/src/apprt/gtk/x11.zig b/src/apprt/gtk/protocol/x11.zig similarity index 50% rename from src/apprt/gtk/x11.zig rename to src/apprt/gtk/protocol/x11.zig index 21ff87b34..be991bcfe 100644 --- a/src/apprt/gtk/x11.zig +++ b/src/apprt/gtk/protocol/x11.zig @@ -1,57 +1,69 @@ /// Utility functions for X11 handling. const std = @import("std"); const build_options = @import("build_options"); -const c = @import("c.zig").c; -const input = @import("../../input.zig"); +const c = @import("../c.zig").c; +const input = @import("../../../input.zig"); +const Config = @import("../../../config.zig").Config; +const protocol = @import("../protocol.zig"); +const adwaita = @import("../adwaita.zig"); const log = std.log.scoped(.gtk_x11); -/// Returns true if the passed in display is an X11 display. -pub fn is_display(display: ?*c.GdkDisplay) bool { - if (comptime !build_options.x11) return false; - return c.g_type_check_instance_is_a( - @ptrCast(@alignCast(display orelse return false)), - c.gdk_x11_display_get_type(), - ) != 0; -} +pub const App = struct { + common: *protocol.App, + display: *c.Display, -/// Returns true if the app is running on X11 -pub fn is_current_display_server() bool { - if (comptime !build_options.x11) return false; - const display = c.gdk_display_get_default(); - return is_display(display); -} - -pub const Xkb = struct { - base_event_code: c_int, + base_event_code: c_int = 0, /// Initialize an Xkb struct for the given GDK display. If the display isn't /// backed by X then this will return null. - pub fn init(display_: ?*c.GdkDisplay) !?Xkb { - if (comptime !build_options.x11) return null; - - // Display should never be null but we just treat that as a non-X11 - // display so that the caller can just ignore it and not unwrap it. - const display = display_ orelse return null; - + pub fn init(common: *protocol.App) !void { // If the display isn't X11, then we don't need to do anything. - if (!is_display(display)) return null; + if (c.g_type_check_instance_is_a( + @ptrCast(@alignCast(common.gdk_display)), + c.gdk_x11_display_get_type(), + ) == 0) + return; - log.debug("Xkb.init: initializing Xkb", .{}); - const xdisplay = c.gdk_x11_display_get_xdisplay(display); - var result: Xkb = .{ - .base_event_code = 0, + var self: App = .{ + .common = common, + .display = c.gdk_x11_display_get_xdisplay(common.gdk_display) orelse return, }; + log.debug("X11 platform init={}", .{self}); + + // Set the X11 window class property (WM_CLASS) if are are on an X11 + // display. + // + // Note that we also set the program name here using g_set_prgname. + // This is how the instance name field for WM_CLASS is derived when + // calling gdk_x11_display_set_program_class; there does not seem to be + // a way to set it directly. It does not look like this is being set by + // our other app initialization routines currently, but since we're + // currently deriving its value from x11-instance-name effectively, I + // feel like gating it behind an X11 check is better intent. + // + // This makes the property show up like so when using xprop: + // + // WM_CLASS(STRING) = "ghostty", "com.mitchellh.ghostty" + // + // Append "-debug" on both when using the debug build. + + c.g_set_prgname(common.derived_config.x11_program_name); + c.gdk_x11_display_set_program_class(common.gdk_display, common.derived_config.app_id); + + // XKB + log.debug("Xkb.init: initializing Xkb", .{}); + log.debug("Xkb.init: running XkbQueryExtension", .{}); var opcode: c_int = 0; var base_error_code: c_int = 0; var major = c.XkbMajorVersion; var minor = c.XkbMinorVersion; if (c.XkbQueryExtension( - xdisplay, + self.display, &opcode, - &result.base_event_code, + &self.base_event_code, &base_error_code, &major, &minor, @@ -62,7 +74,7 @@ pub const Xkb = struct { log.debug("Xkb.init: running XkbSelectEventDetails", .{}); if (c.XkbSelectEventDetails( - xdisplay, + self.display, c.XkbUseCoreKbd, c.XkbStateNotify, c.XkbModifierStateMask, @@ -72,7 +84,7 @@ pub const Xkb = struct { return error.XkbInitializationError; } - return result; + common.inner = .{ .x11 = self }; } /// Checks for an immediate pending XKB state update event, and returns the @@ -85,18 +97,13 @@ pub const Xkb = struct { /// Returns null if there is no event. In this case, the caller should fall /// back to the standard GDK modifier state (this likely means the key /// event did not result in a modifier change). - pub fn modifier_state_from_notify(self: Xkb, display_: ?*c.GdkDisplay) ?input.Mods { - if (comptime !build_options.x11) return null; - - const display = display_ orelse return null; - + pub fn modifierStateFromNotify(self: App) ?input.Mods { // Shoutout to Mozilla for figuring out a clean way to do this, this is // paraphrased from Firefox/Gecko in widget/gtk/nsGtkKeyUtils.cpp. - const xdisplay = c.gdk_x11_display_get_xdisplay(display); - if (c.XEventsQueued(xdisplay, c.QueuedAfterReading) == 0) return null; + if (c.XEventsQueued(self.display, c.QueuedAfterReading) == 0) return null; var nextEvent: c.XEvent = undefined; - _ = c.XPeekEvent(xdisplay, &nextEvent); + _ = c.XPeekEvent(self.display, &nextEvent); if (nextEvent.type != self.base_event_code) return null; const xkb_event: *c.XkbEvent = @ptrCast(&nextEvent); @@ -117,3 +124,38 @@ pub const Xkb = struct { return mods; } }; + +pub const Surface = struct { + common: *protocol.Surface, + app: *App, + window: c.Window, + + pub fn init(common: *protocol.Surface) void { + const surface = c.gtk_native_get_surface(@ptrCast(common.gtk_window)) orelse return; + + // Check if we're actually on X11 + if (c.g_type_check_instance_is_a( + @ptrCast(@alignCast(surface)), + c.gdk_x11_surface_get_type(), + ) == 0) + return; + + common.inner = .{ .x11 = .{ + .common = common, + .app = &common.app.inner.x11, + .window = c.gdk_x11_surface_get_xid(surface), + } }; + } + + pub fn onConfigUpdate(self: *Surface) !void { + _ = self; + } + + pub fn onResize(self: *Surface) !void { + _ = self; + } + + fn updateBlur(self: *Surface) !void { + _ = self; + } +}; diff --git a/src/config/Config.zig b/src/config/Config.zig index eae6541be..e0d25d0fb 100644 --- a/src/config/Config.zig +++ b/src/config/Config.zig @@ -5782,6 +5782,14 @@ pub const BackgroundBlur = union(enum) { ) catch return error.InvalidValue }; } + pub fn enabled(self: BackgroundBlur) bool { + return switch (self) { + .false => false, + .true => true, + .radius => |v| v > 0, + }; + } + pub fn cval(self: BackgroundBlur) u8 { return switch (self) { .false => 0,