diff --git a/macos/Sources/Features/Terminal/TerminalController.swift b/macos/Sources/Features/Terminal/TerminalController.swift index 86fb02442..81b86a215 100644 --- a/macos/Sources/Features/Terminal/TerminalController.swift +++ b/macos/Sources/Features/Terminal/TerminalController.swift @@ -111,6 +111,8 @@ class TerminalController: NSWindowController, NSWindowDelegate, //MARK: - Methods func configDidReload() { + guard let window = window as? TerminalWindow else { return } + window.focusFollowsMouse = ghostty.config.focusFollowsMouse syncAppearance() } @@ -197,9 +199,9 @@ class TerminalController: NSWindowController, NSWindowDelegate, 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 @@ -342,6 +344,8 @@ class TerminalController: NSWindowController, NSWindowDelegate, } } + window.focusFollowsMouse = ghostty.config.focusFollowsMouse + // Apply any additional appearance-related properties to the new window. syncAppearance() } diff --git a/macos/Sources/Features/Terminal/TerminalWindow.swift b/macos/Sources/Features/Terminal/TerminalWindow.swift index 9e4d3600e..2d54db321 100644 --- a/macos/Sources/Features/Terminal/TerminalWindow.swift +++ b/macos/Sources/Features/Terminal/TerminalWindow.swift @@ -369,6 +369,8 @@ class TerminalWindow: NSWindow { } } + var focusFollowsMouse: Bool = false + // Find the NSTextField responsible for displaying the titlebar's title. private var titlebarTextField: NSTextField? { guard let titlebarContainer = contentView?.superview?.subviews diff --git a/macos/Sources/Ghostty/Ghostty.Config.swift b/macos/Sources/Ghostty/Ghostty.Config.swift index dcee797c8..ab583e956 100644 --- a/macos/Sources/Ghostty/Ghostty.Config.swift +++ b/macos/Sources/Ghostty/Ghostty.Config.swift @@ -245,7 +245,15 @@ extension Ghostty { _ = ghostty_config_get(config, &v, key, UInt(key.count)) return v } - + + var focusFollowsMouse : Bool { + guard let config = self.config else { return false } + var v = false; + let key = "focus-follows-mouse" + _ = ghostty_config_get(config, &v, key, UInt(key.count)) + return v + } + var backgroundColor: Color { var rgb: UInt32 = 0 let bg_key = "background" diff --git a/macos/Sources/Ghostty/SurfaceView_AppKit.swift b/macos/Sources/Ghostty/SurfaceView_AppKit.swift index a5347e1ad..b3c002d64 100644 --- a/macos/Sources/Ghostty/SurfaceView_AppKit.swift +++ b/macos/Sources/Ghostty/SurfaceView_AppKit.swift @@ -480,6 +480,14 @@ extension Ghostty { let pos = self.convert(event.locationInWindow, from: nil) ghostty_surface_mouse_pos(surface, pos.x, frame.height - pos.y) + // If focus follows mouse is enabled then move focus to this surface. + if let window = self.window as? TerminalWindow, + window.isKeyWindow && + window.focusFollowsMouse && + !self.focused + { + Ghostty.moveFocus(to: self) + } } override func mouseDragged(with event: NSEvent) { diff --git a/src/apprt/gtk/Surface.zig b/src/apprt/gtk/Surface.zig index d696d8b38..57bbf0d38 100644 --- a/src/apprt/gtk/Surface.zig +++ b/src/apprt/gtk/Surface.zig @@ -1204,6 +1204,12 @@ fn gtkMouseMotion( .y = @floatCast(scaled.y), }; + // If we don't have focus, and we want it, grab it. + const gl_widget = @as(*c.GtkWidget, @ptrCast(self.gl_area)); + if (c.gtk_widget_has_focus(gl_widget) == 0 and self.app.config.@"focus-follows-mouse") { + self.grabFocus(); + } + self.core_surface.cursorPosCallback(self.cursor_pos) catch |err| { log.err("error in cursor pos callback err={}", .{err}); return; diff --git a/src/config/Config.zig b/src/config/Config.zig index 42e6cd584..6e10e63fb 100644 --- a/src/config/Config.zig +++ b/src/config/Config.zig @@ -747,6 +747,14 @@ keybind: Keybinds = .{}, /// This configuration currently only works with GTK. @"window-new-tab-position": WindowNewTabPosition = .current, +// If true, when there are multiple split panes, the mouse selects the pane +// that is focused. This only applies to the currently focused window; i.e. +// mousing over a split in an unfocused window will now focus that split +// and bring the window to front. +// +// Default is false. +@"focus-follows-mouse": bool = false, + /// When enabled, the full GTK titlebar is displayed instead of your window /// manager's simple titlebar. The behavior of this option will vary with your /// window manager.