Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Sources/AppBundle/config/Config.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ var defaultConfigUrl: URL {
@MainActor var configUrl: URL = defaultConfigUrl

struct Config: ConvenienceCopyable {
var mouseWindowFocus: Bool = false
var afterLoginCommand: [any Command] = []
var afterStartupCommand: [any Command] = []
var _indentForNestedContainersWithTheSameOrientation: Void = ()
Expand Down
10 changes: 10 additions & 0 deletions Sources/AppBundle/config/parseConfig.swift
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ private let configParser: [String: any ParserProtocol<Config>] = [
"after-login-command": Parser(\.afterLoginCommand) { parseCommandOrCommands($0).toParsedToml($1) },
"after-startup-command": Parser(\.afterStartupCommand) { parseCommandOrCommands($0).toParsedToml($1) },

"mouse-window-focus": Parser(\.mouseWindowFocus, parseBool),

"on-focus-changed": Parser(\.onFocusChanged) { parseCommandOrCommands($0).toParsedToml($1) },
"on-focused-monitor-changed": Parser(\.onFocusedMonitorChanged) { parseCommandOrCommands($0).toParsedToml($1) },
// "on-focused-workspace-changed": Parser(\.onFocusedWorkspaceChanged, { parseCommandOrCommands($0).toParsedToml($1) }),
Expand Down Expand Up @@ -167,6 +169,14 @@ func parseCommandOrCommands(_ raw: TOMLValueConvertible) -> Parsed<[any Command]
var errors: [TomlParseError] = []

var config = rawTable.parseTable(Config(), configParser, .root, &errors)
if config.mouseWindowFocus {
config.onFocusChanged.removeAll { cmd in
cmd.info.kind.rawValue.localizedCaseInsensitiveContains("mouse")
}
config.onFocusedMonitorChanged.removeAll { cmd in
cmd.info.kind.rawValue.localizedCaseInsensitiveContains("mouse")
}
}

if let mapping = rawTable[keyMappingConfigRootKey].flatMap({ parseKeyMapping($0, .rootKey(keyMappingConfigRootKey), &errors) }) {
config.keyMapping = mapping
Expand Down
1 change: 1 addition & 0 deletions Sources/AppBundle/initAppBundle.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import Foundation
checkAccessibilityPermissions()
startUnixSocketServer()
GlobalObserver.initObserver()
setupMouseTracking()
Task {
Workspace.garbageCollectUnusedWorkspaces() // init workspaces
_ = Workspace.all.first?.focusWorkspace()
Expand Down
34 changes: 34 additions & 0 deletions Sources/AppBundle/tree/MouseTracking.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import AppKit
import Common

@MainActor
func setupMouseTracking() {
print("✅ Mouse tracking initialized")
NSEvent.addGlobalMonitorForEvents(matching: .mouseMoved) { _ in
Task { @MainActor in
guard config.mouseWindowFocus else { return }
await autoFocusWindowUnderMouse()
}
}
}

@MainActor
func autoFocusWindowUnderMouse() async {
let point = NSEvent.mouseLocation
let workspace = point.monitorApproximation.activeWorkspace
_ = workspace.workspaceMonitor.setActiveWorkspace(workspace)

guard let windowUnderMouse = point.findIn(tree: workspace.rootTilingContainer, virtual: false)
else {
return
}

do {
let focusedWindow = try await getNativeFocusedWindow()
if focusedWindow == windowUnderMouse {
return
}
windowUnderMouse.nativeFocus()
} catch {
}
}
1 change: 1 addition & 0 deletions docs/config-examples/default-config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ after-startup-command = []

# Start AeroSpace at login
start-at-login = false
mouse-window-focus = false

# Normalizations. See: https://nikitabobko.github.io/AeroSpace/guide#normalization
enable-normalization-flatten-containers = true
Expand Down
Loading