11import Cocoa
22
33class Window {
4- static var globalCreationCounter = Int . zero
4+ private static let notifications = [
5+ kAXUIElementDestroyedNotification,
6+ kAXTitleChangedNotification,
7+ kAXWindowMiniaturizedNotification,
8+ kAXWindowDeminiaturizedNotification,
9+ kAXWindowResizedNotification,
10+ kAXWindowMovedNotification,
11+ ]
12+ private static var globalCreationCounter = Int . zero
13+
514 var cgWindowId : CGWindowID ?
615 var lastFocusOrder = Int . zero
716 var creationOrder = Int . zero
@@ -26,15 +35,6 @@ class Window {
2635 var rowIndex : Int ?
2736 var debugId : String { " (wid: \( cgWindowId. map { String ( describing: $0) } ?? " nil " ) ) \( title ?? " nil " ) ) \( application. debugId) " }
2837
29- static let notifications = [
30- kAXUIElementDestroyedNotification,
31- kAXTitleChangedNotification,
32- kAXWindowMiniaturizedNotification,
33- kAXWindowDeminiaturizedNotification,
34- kAXWindowResizedNotification,
35- kAXWindowMovedNotification,
36- ]
37-
3838 init ( _ axUiElement: AXUIElement , _ application: Application , _ wid: CGWindowID , _ axTitle: String ? , _ isFullscreen: Bool , _ isMinimized: Bool , _ position: CGPoint ? , _ size: CGSize ? ) {
3939 self . axUiElement = axUiElement
4040 self . application = application
@@ -68,17 +68,6 @@ class Window {
6868 Logger . debug ( debugId)
6969 }
7070
71- /// some apps will not trigger AXApplicationActivated, where we usually update application.focusedWindow
72- /// workaround: we check and possibly do it here
73- func checkIfFocused( _ application: Application , _ wid: CGWindowID ) {
74- AXUIElement . retryAxCallUntilTimeout ( context: debugId, pid: application. pid, callType: . updateWindow) {
75- let focusedWid = try application. axUiElement? . focusedWindow ( ) ? . cgWindowId ( )
76- if wid == focusedWid {
77- application. focusedWindow = self
78- }
79- }
80- }
81-
8271 func isEqualRobust( _ otherWindowAxUiElement: AXUIElement , _ otherWindowWid: CGWindowID ? ) -> Bool {
8372 // the window can be deallocated by the OS, in which case its `CGWindowID` will be `-1`
8473 // we check for equality both on the AXUIElement, and the CGWindowID, in order to catch all scenarios
@@ -124,6 +113,10 @@ class Window {
124113 NSSound . beep ( )
125114 return
126115 }
116+ if let altTabWindow = altTabWindow ( ) {
117+ altTabWindow. close ( )
118+ return
119+ }
127120 BackgroundWork . accessibilityCommandsQueue. addOperation { [ weak self] in
128121 guard let self else { return }
129122 if self . isFullscreen {
@@ -144,6 +137,10 @@ class Window {
144137 NSSound . beep ( )
145138 return
146139 }
140+ if let altTabWindow = altTabWindow ( ) {
141+ isMinimized ? altTabWindow. deminiaturize ( nil ) : altTabWindow. miniaturize ( nil )
142+ return
143+ }
147144 BackgroundWork . accessibilityCommandsQueue. addOperation { [ weak self] in
148145 guard let self else { return }
149146 if self . isFullscreen {
@@ -164,20 +161,23 @@ class Window {
164161 NSSound . beep ( )
165162 return
166163 }
164+ if let altTabWindow = altTabWindow ( ) {
165+ altTabWindow. toggleFullScreen ( nil )
166+ return
167+ }
167168 BackgroundWork . accessibilityCommandsQueue. addOperation { [ weak self] in
168169 guard let self else { return }
169170 try ? self . axUiElement!. setAttribute ( kAXFullscreenAttribute, !self . isFullscreen)
170171 }
171172 }
172173
173174 func focus( ) {
174- let bundleUrl = application. bundleURL
175- if bundleUrl == App . bundleURL {
175+ if let altTabWindow = altTabWindow ( ) {
176176 App . shared. activate ( ignoringOtherApps: true )
177- App . app . window ( withWindowNumber : Int ( cgWindowId! ) ) ? . makeKeyAndOrderFront ( nil )
177+ altTabWindow . makeKeyAndOrderFront ( nil )
178178 Windows . previewFocusedWindowIfNeeded ( )
179179 } else if isWindowlessApp || cgWindowId == nil || Preferences . onlyShowApplications ( ) {
180- if let bundleUrl, isWindowlessApp {
180+ if let bundleUrl = application . bundleURL , isWindowlessApp {
181181 if ( try ? NSWorkspace . shared. launchApplication ( at: bundleUrl, configuration: [ : ] ) ) == nil {
182182 application. runningApplication. activate ( options: . activateAllWindows)
183183 }
@@ -204,7 +204,7 @@ class Window {
204204 }
205205
206206 /// The following function was ported from https://github.com/Hammerspoon/hammerspoon/issues/370#issuecomment-545545468
207- func makeKeyWindow( _ psn: inout ProcessSerialNumber ) -> Void {
207+ private func makeKeyWindow( _ psn: inout ProcessSerialNumber ) -> Void {
208208 var bytes = [ UInt8] ( repeating: 0 , count: 0xf8 )
209209 bytes [ 0x04 ] = 0xf8
210210 bytes [ 0x3a ] = 0x10
@@ -273,4 +273,22 @@ class Window {
273273 }
274274 return false
275275 }
276+
277+ private func altTabWindow( ) -> NSWindow ? {
278+ if application. bundleURL == App . bundleURL, let cgWindowId {
279+ return App . app. window ( withWindowNumber: Int ( cgWindowId) )
280+ }
281+ return nil
282+ }
283+
284+ /// some apps will not trigger AXApplicationActivated, where we usually update application.focusedWindow
285+ /// workaround: we check and possibly do it here
286+ private func checkIfFocused( _ application: Application , _ wid: CGWindowID ) {
287+ AXUIElement . retryAxCallUntilTimeout ( context: debugId, pid: application. pid, callType: . updateWindow) {
288+ let focusedWid = try application. axUiElement? . focusedWindow ( ) ? . cgWindowId ( )
289+ if wid == focusedWid {
290+ application. focusedWindow = self
291+ }
292+ }
293+ }
276294}
0 commit comments