diff --git a/include/ghostty.h b/include/ghostty.h index 9b3c6b904..2cb915ddd 100644 --- a/include/ghostty.h +++ b/include/ghostty.h @@ -291,6 +291,10 @@ void ghostty_surface_request_close(ghostty_surface_t); void ghostty_surface_split(ghostty_surface_t, ghostty_split_direction_e); void ghostty_surface_split_focus(ghostty_surface_t, ghostty_split_focus_direction_e); +// APIs I'd like to get rid of eventually but are still needed for now. +// Don't use these unless you know what you're doing. +void ghostty_set_window_background_blur(ghostty_surface_t, void *); + #ifdef __cplusplus } #endif diff --git a/macos/Sources/Ghostty/SurfaceView.swift b/macos/Sources/Ghostty/SurfaceView.swift index bb3db5fa1..25c74b3dc 100644 --- a/macos/Sources/Ghostty/SurfaceView.swift +++ b/macos/Sources/Ghostty/SurfaceView.swift @@ -201,9 +201,14 @@ extension Ghostty { guard let window = self.window else { return } guard let surface = self.surface else { return } guard ghostty_surface_transparent(surface) else { return } + + // Set the window transparency settings window.isOpaque = false window.hasShadow = false window.backgroundColor = .clear + + // If we have a blur, set the blur + ghostty_set_window_background_blur(surface, Unmanaged.passUnretained(window).toOpaque()) } override func resignFirstResponder() -> Bool { diff --git a/src/apprt/embedded.zig b/src/apprt/embedded.zig index a90cef4e5..1b5c3c4b8 100644 --- a/src/apprt/embedded.zig +++ b/src/apprt/embedded.zig @@ -570,4 +570,34 @@ pub const CAPI = struct { export fn ghostty_surface_split_focus(ptr: *Surface, direction: input.SplitFocusDirection) void { ptr.gotoSplit(direction); } + + /// Sets the window background blur on macOS to the desired value. + /// I do this in Zig as an extern function because I don't know how to + /// call these functions in Swift. + /// + /// This uses an undocumented, non-public API because this is what + /// every terminal appears to use, including Terminal.app. + export fn ghostty_set_window_background_blur( + ptr: *Surface, + window: *anyopaque, + ) void { + const config = ptr.app.config; + + // Do nothing if we don't have background transparency enabled + if (config.@"background-opacity" >= 1.0) return; + + // Do nothing if our blur value is zero + if (config.@"background-blur-radius" == 0) return; + + const nswindow = objc.Object.fromId(window); + _ = CGSSetWindowBackgroundBlurRadius( + CGSDefaultConnectionForThread(), + nswindow.msgSend(usize, objc.sel("windowNumber"), .{}), + @intCast(config.@"background-blur-radius"), + ); + } + + /// See ghostty_set_window_background_blur + extern "c" fn CGSSetWindowBackgroundBlurRadius(*anyopaque, usize, c_int) i32; + extern "c" fn CGSDefaultConnectionForThread() *anyopaque; }; diff --git a/src/config.zig b/src/config.zig index ca4d8ff9a..cd92cbbf4 100644 --- a/src/config.zig +++ b/src/config.zig @@ -72,6 +72,15 @@ pub const Config = struct { /// affect new windows, tabs, and splits. @"background-opacity": f64 = 1.0, + /// A positive value enables blurring of the background when + /// background-opacity is less than 1. The value is the blur radius to + /// apply. A value of 20 is reasonable for a good looking blur. + /// Higher values will cause strange rendering issues as well as + /// performance issues. + /// + /// This is only supported on macOS. + @"background-blur-radius": u8 = 0, + /// The command to run, usually a shell. If this is not an absolute path, /// it'll be looked up in the PATH. If this is not set, a default will /// be looked up from your system. The rules for the default lookup are: