diff --git a/include/ghostty.h b/include/ghostty.h index 1254f71bd..6638caaa6 100644 --- a/include/ghostty.h +++ b/include/ghostty.h @@ -513,7 +513,6 @@ ghostty_surface_config_s ghostty_surface_config_new(); ghostty_surface_t ghostty_surface_new(ghostty_app_t, ghostty_surface_config_s*); void ghostty_surface_free(ghostty_surface_t); ghostty_app_t ghostty_surface_app(ghostty_surface_t); -bool ghostty_surface_transparent(ghostty_surface_t); bool ghostty_surface_needs_confirm_quit(ghostty_surface_t); void ghostty_surface_refresh(ghostty_surface_t); void ghostty_surface_draw(ghostty_surface_t); @@ -586,7 +585,7 @@ bool ghostty_inspector_metal_shutdown(ghostty_inspector_t); // 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*); +void ghostty_set_window_background_blur(ghostty_app_t, void*); #ifdef __cplusplus } diff --git a/macos/Sources/Features/Terminal/TerminalController.swift b/macos/Sources/Features/Terminal/TerminalController.swift index b0c2df1ca..d1624a8e4 100644 --- a/macos/Sources/Features/Terminal/TerminalController.swift +++ b/macos/Sources/Features/Terminal/TerminalController.swift @@ -166,6 +166,16 @@ class TerminalController: NSWindowController, NSWindowDelegate, private func syncAppearance() { guard let window = self.window as? TerminalWindow else { return } + // If our window is not visible, then delay this. This is possible specifically + // during state restoration but probably in other scenarios as well. To delay, + // we just loop directly on the dispatch queue. We have to delay because some + // APIs such as window blur have no effect unless the window is visible. + guard window.isVisible else { + // Weak window so that if the window changes or is destroyed we aren't holding a ref + DispatchQueue.main.async { [weak self] in self?.syncAppearance() } + return + } + // Set the font for the window and tab titles. if let titleFontName = ghostty.config.windowTitleFontFamily { window.titlebarFont = NSFont(name: titleFontName, size: NSFont.systemFontSize) @@ -173,6 +183,23 @@ class TerminalController: NSWindowController, NSWindowDelegate, window.titlebarFont = nil } + // If we have window transparency then set it transparent. Otherwise set it opaque. + if (ghostty.config.backgroundOpacity < 1) { + window.isOpaque = false + + // This is weird, but we don't use ".clear" because this creates a look that + // matches Terminal.app much more closer. This lets users transition from + // Terminal.app more easily. + window.backgroundColor = .white.withAlphaComponent(0.001) + + ghostty_set_window_background_blur(ghostty.app, Unmanaged.passUnretained(window).toOpaque()) + } else { + window.isOpaque = true + window.backgroundColor = .windowBackgroundColor + } + + window.hasShadow = ghostty.config.macosWindowShadow + guard window.hasStyledTabs else { return } // The titlebar is always updated. We don't need to worry about opacity diff --git a/macos/Sources/Ghostty/Ghostty.Config.swift b/macos/Sources/Ghostty/Ghostty.Config.swift index 2f455e578..e2baa3478 100644 --- a/macos/Sources/Ghostty/Ghostty.Config.swift +++ b/macos/Sources/Ghostty/Ghostty.Config.swift @@ -236,6 +236,14 @@ extension Ghostty { return v } + var macosWindowShadow: Bool { + guard let config = self.config else { return false } + var v = false; + let key = "macos-window-shadow" + _ = ghostty_config_get(config, &v, key, UInt(key.count)) + return v + } + var backgroundColor: Color { var rgb: UInt32 = 0 let bg_key = "background" @@ -268,6 +276,14 @@ extension Ghostty { return v; } + var backgroundBlurRadius: Int { + guard let config = self.config else { return 1 } + var v: Int = 0 + let key = "background-blur-radius" + _ = ghostty_config_get(config, &v, key, UInt(key.count)) + return v; + } + var unfocusedSplitOpacity: Double { guard let config = self.config else { return 1 } var opacity: Double = 0.85 diff --git a/macos/Sources/Ghostty/SurfaceView_AppKit.swift b/macos/Sources/Ghostty/SurfaceView_AppKit.swift index 255248c61..a5347e1ad 100644 --- a/macos/Sources/Ghostty/SurfaceView_AppKit.swift +++ b/macos/Sources/Ghostty/SurfaceView_AppKit.swift @@ -342,40 +342,6 @@ extension Ghostty { // MARK: - NSView - override func viewDidMoveToWindow() { - // Set our background blur if requested - setWindowBackgroundBlur(window) - } - - /// This function sets the window background to blur if it is configured on the surface. - private func setWindowBackgroundBlur(_ targetWindow: NSWindow?) { - // Surface must desire transparency - guard let surface = self.surface, - ghostty_surface_transparent(surface) else { return } - - // Our target should always be our own view window - guard let target = targetWindow, - let window = self.window, - target == window else { return } - - // If our window is not visible, then delay this. This is possible specifically - // during state restoration but probably in other scenarios as well. To delay, - // we just loop directly on the dispatch queue. - guard window.isVisible else { - // Weak window so that if the window changes or is destroyed we aren't holding a ref - DispatchQueue.main.async { [weak self, weak window] in self?.setWindowBackgroundBlur(window) } - 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 becomeFirstResponder() -> Bool { let result = super.becomeFirstResponder() if (result) { focusDidChange(true) } diff --git a/src/apprt/embedded.zig b/src/apprt/embedded.zig index d2cc493b4..57fc26aea 100644 --- a/src/apprt/embedded.zig +++ b/src/apprt/embedded.zig @@ -1474,11 +1474,6 @@ pub const CAPI = struct { return surface.app; } - /// Returns true if the surface has transparency set. - export fn ghostty_surface_transparent(surface: *Surface) bool { - return surface.app.config.@"background-opacity" < 1.0; - } - /// Returns true if the surface needs to confirm quitting. export fn ghostty_surface_needs_confirm_quit(surface: *Surface) bool { return surface.core_surface.needsConfirmQuit(); @@ -1850,13 +1845,13 @@ pub const CAPI = struct { /// 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, + app: *App, window: *anyopaque, ) void { // This is only supported on macOS if (comptime builtin.target.os.tag != .macos) return; - const config = ptr.app.config; + const config = app.config; // Do nothing if we don't have background transparency enabled if (config.@"background-opacity" >= 1.0) return; diff --git a/src/config/Config.zig b/src/config/Config.zig index cf38788cb..10b874ed2 100644 --- a/src/config/Config.zig +++ b/src/config/Config.zig @@ -368,9 +368,6 @@ palette: Palette = .{}, /// The opacity level (opposite of transparency) of the background. A value of /// 1 is fully opaque and a value of 0 is fully transparent. A value less than 0 /// or greater than 1 will be clamped to the nearest valid value. -/// -/// Changing this value at runtime (and reloading config) will only affect new -/// windows, tabs, and splits. @"background-opacity": f64 = 1.0, /// A positive value enables blurring of the background when background-opacity @@ -988,6 +985,11 @@ keybind: Keybinds = .{}, /// This does not work with GLFW builds. @"macos-option-as-alt": OptionAsAlt = .false, +/// Whether to enable the macOS window shadow. The default value is true. +/// With some window managers and window transparency settings, you may +/// find false more visually appealing. +@"macos-window-shadow": bool = true, + /// Put every surface (tab, split, window) into a dedicated Linux cgroup. /// /// This makes it so that resource management can be done on a per-surface