1+ #include " ../window_manager.h"
2+ #import < AppKit/AppKit.h>
3+ #import < AppKit/NSAccessibility.h>
4+ #import < ApplicationServices/ApplicationServices.h>
15#include < CoreGraphics/CGWindow.h>
26#import < Foundation/Foundation.h>
3- #import < AppKit/AppKit.h>
4- #include " ../window_manager.h"
57
6- NSDictionary * getWindowInfo (int64_t windowHandle) {
7- CGWindowListOption listOptions = kCGWindowListOptionOnScreenOnly | kCGWindowListExcludeDesktopElements ;
8- CFArrayRef windowList = CGWindowListCopyWindowInfo (listOptions, kCGNullWindowID );
8+ NSDictionary *getWindowInfo (int64_t windowHandle) {
9+ CGWindowListOption listOptions =
10+ kCGWindowListOptionOnScreenOnly | kCGWindowListExcludeDesktopElements ;
11+ CFArrayRef windowList =
12+ CGWindowListCopyWindowInfo (listOptions, kCGNullWindowID );
913
1014 for (NSDictionary *info in (NSArray *)windowList) {
1115 NSNumber *windowNumber = info[(id )kCGWindowNumber ];
2529}
2630
2731WindowHandle getActiveWindow () {
28- CGWindowListOption listOptions = kCGWindowListOptionOnScreenOnly | kCGWindowListExcludeDesktopElements ;
29- CFArrayRef windowList = CGWindowListCopyWindowInfo (listOptions, kCGNullWindowID );
32+ CGWindowListOption listOptions =
33+ kCGWindowListOptionOnScreenOnly | kCGWindowListExcludeDesktopElements ;
34+ CFArrayRef windowList =
35+ CGWindowListCopyWindowInfo (listOptions, kCGNullWindowID );
3036
3137 for (NSDictionary *info in (NSArray *)windowList) {
3238 NSNumber *ownerPid = info[(id )kCGWindowOwnerPID ];
3339 NSNumber *windowNumber = info[(id )kCGWindowNumber ];
3440
35- auto app = [NSRunningApplication runningApplicationWithProcessIdentifier: [ownerPid intValue ]];
41+ auto app = [NSRunningApplication
42+ runningApplicationWithProcessIdentifier: [ownerPid intValue ]];
3643
3744 if (![app isActive ]) {
3845 continue ;
@@ -49,16 +56,19 @@ WindowHandle getActiveWindow() {
4956}
5057
5158std::vector<WindowHandle> getWindows () {
52- CGWindowListOption listOptions = kCGWindowListOptionOnScreenOnly | kCGWindowListExcludeDesktopElements ;
53- CFArrayRef windowList = CGWindowListCopyWindowInfo (listOptions, kCGNullWindowID );
59+ CGWindowListOption listOptions =
60+ kCGWindowListOptionOnScreenOnly | kCGWindowListExcludeDesktopElements ;
61+ CFArrayRef windowList =
62+ CGWindowListCopyWindowInfo (listOptions, kCGNullWindowID );
5463
5564 std::vector<WindowHandle> windowHandles;
5665
5766 for (NSDictionary *info in (NSArray *)windowList) {
5867 NSNumber *ownerPid = info[(id )kCGWindowOwnerPID ];
5968 NSNumber *windowNumber = info[(id )kCGWindowNumber ];
6069
61- auto app = [NSRunningApplication runningApplicationWithProcessIdentifier: [ownerPid intValue ]];
70+ auto app = [NSRunningApplication
71+ runningApplicationWithProcessIdentifier: [ownerPid intValue ]];
6272 auto path = app ? [app.bundleURL.path UTF8String ] : " " ;
6373
6474 if (app && path != " " ) {
@@ -77,8 +87,10 @@ MMRect getWindowRect(const WindowHandle windowHandle) {
7787 auto windowInfo = getWindowInfo (windowHandle);
7888 if (windowInfo != nullptr && windowHandle >= 0 ) {
7989 CGRect windowRect;
80- if (CGRectMakeWithDictionaryRepresentation ((CFDictionaryRef)windowInfo[(id )kCGWindowBounds ], &windowRect)) {
81- return MMRectMake (windowRect.origin .x , windowRect.origin .y , windowRect.size .width , windowRect.size .height );
90+ if (CGRectMakeWithDictionaryRepresentation (
91+ (CFDictionaryRef)windowInfo[(id )kCGWindowBounds ], &windowRect)) {
92+ return MMRectMake (windowRect.origin .x , windowRect.origin .y ,
93+ windowRect.size .width , windowRect.size .height );
8294 }
8395 }
8496 return MMRectMake (0 , 0 , 0 , 0 );
@@ -88,7 +100,172 @@ MMRect getWindowRect(const WindowHandle windowHandle) {
88100 auto windowInfo = getWindowInfo (windowHandle);
89101 if (windowInfo != nullptr && windowHandle >= 0 ) {
90102 NSString *windowName = windowInfo[(id )kCGWindowName ];
91- return std::string ([windowName UTF8String ], [windowName lengthOfBytesUsingEncoding: NSUTF8StringEncoding]);
103+ return std::string (
104+ [windowName UTF8String ],
105+ [windowName lengthOfBytesUsingEncoding: NSUTF8StringEncoding]);
92106 }
93107 return " " ;
94108}
109+
110+ /* *
111+ * Focuses on the window provided via its handle.
112+ *
113+ * This function collects a list of on-screen windows and matches the
114+ * windowHandle with their window numbers. If found, the corresponding
115+ * application is brought to foreground. The function then uses accessibility
116+ * APIs to specifically focus the target window using its title.
117+ *
118+ * @param windowHandle Handle to the window that needs to be focused.
119+ *
120+ * @return bool If the function executes without any errors, it returns true.
121+ * If it can't retrieve window information or windowHandle is
122+ * invalid, it returns false.
123+ */
124+ bool focusWindow (const WindowHandle windowHandle) {
125+
126+ // Collect list of on-screen windows
127+ CGWindowListOption listOptions =
128+ kCGWindowListOptionOnScreenOnly | kCGWindowListExcludeDesktopElements ;
129+ CFArrayRef windowList =
130+ CGWindowListCopyWindowInfo (listOptions, kCGNullWindowID );
131+ bool activated = false ;
132+
133+ // Look for matching window and bring application to foreground
134+ for (NSDictionary *info in (NSArray *)windowList) {
135+ NSNumber *ownerPid = info[(id )kCGWindowOwnerPID ];
136+ NSNumber *windowNumber = info[(id )kCGWindowNumber ];
137+ if ([windowNumber intValue ] == windowHandle) {
138+ NSRunningApplication *app = [NSRunningApplication
139+ runningApplicationWithProcessIdentifier: [ownerPid intValue ]];
140+ [app activateWithOptions: NSApplicationActivateIgnoringOtherApps];
141+ activated = true ;
142+ }
143+ }
144+
145+ // Clean up window list
146+ if (windowList) {
147+ CFRelease (windowList);
148+ }
149+
150+ // Retrieve window info
151+ NSDictionary *windowInfo = getWindowInfo (windowHandle);
152+ if (windowInfo == nullptr || windowHandle < 0 ) {
153+ // NSLog(@"Could not find window info for window handle %lld", windowHandle);
154+ return false ;
155+ }
156+
157+ // Create application object for accessibility
158+ pid_t pid = [[windowInfo objectForKey: (id )kCGWindowOwnerPID ] intValue ];
159+ AXUIElementRef app = AXUIElementCreateApplication (pid);
160+
161+ // Get target window title
162+ NSString *targetWindowTitle = [windowInfo objectForKey: (id )kCGWindowName ];
163+
164+ CFArrayRef windowArray;
165+ AXError error = AXUIElementCopyAttributeValue (app, kAXWindowsAttribute ,
166+ (CFTypeRef *)&windowArray);
167+
168+ // Iterate through windows to find target and bring it to front
169+ if (error == kAXErrorSuccess ) {
170+ CFIndex count = CFArrayGetCount (windowArray);
171+ for (CFIndex i = 0 ; i < count; i++) {
172+ AXUIElementRef window =
173+ (AXUIElementRef)CFArrayGetValueAtIndex (windowArray, i);
174+
175+ CFTypeRef windowTitle;
176+ AXUIElementCopyAttributeValue (window, kAXTitleAttribute , &windowTitle);
177+ if (windowTitle && CFGetTypeID (windowTitle) == CFStringGetTypeID ()) {
178+ NSString *title = (__bridge NSString *)windowTitle;
179+ if ([title isEqualToString: targetWindowTitle]) {
180+ AXError error = AXUIElementPerformAction (window, kAXRaiseAction );
181+ if (error == kAXErrorSuccess ) {
182+ // NSLog(@"Successfully brought the window to front.");
183+ } else {
184+ // NSLog(@"Failed to bring the window to front.");
185+ // NSLog(@"AXUIElementSetAttributeValue error: %d", error);
186+ }
187+ break ;
188+ }
189+ }
190+
191+ // Clean up window title
192+ if (windowTitle) {
193+ CFRelease (windowTitle);
194+ }
195+ }
196+
197+ // Clean up window array
198+ CFRelease (windowArray);
199+ } else {
200+ // NSLog(@"Failed to retrieve the window array.");
201+ }
202+
203+ // Clean up application object
204+ CFRelease (app);
205+
206+ // Successfully executed
207+ return true ;
208+ }
209+
210+ /* *
211+ * Resizes and repositions the window provided via its handle to the specified rectangle.
212+ *
213+ * This function retrieves window information using the provided window handle, then uses
214+ * macOS Accessibility APIs to resize and reposition the window to fit within the provided
215+ * rectangle dimensions and location.
216+ *
217+ * @param windowHandle Handle to the window that needs to be resized.
218+ * @param rect The rectangle area to which the window should be resized and repositioned.
219+ *
220+ * @return bool If the function executes without any errors and successfully resizes the
221+ * window, it returns true. If it can't retrieve window information or
222+ * windowHandle is invalid, or the window resizing operation fails, it returns false.
223+ */
224+ bool resizeWindow (const WindowHandle windowHandle, const MMRect rect) {
225+
226+ // Retrieve window info
227+ NSDictionary *windowInfo = getWindowInfo (windowHandle);
228+ if (windowInfo == nullptr || windowHandle < 0 ) {
229+ // NSLog(@"Could not find window info for window handle %lld", windowHandle);
230+ return false ;
231+ }
232+
233+ // Create application object for accessibility
234+ pid_t pid = [[windowInfo objectForKey: (id )kCGWindowOwnerPID ] intValue ];
235+ AXUIElementRef app = AXUIElementCreateApplication (pid);
236+ AXUIElementRef window;
237+
238+ AXError error = AXUIElementCopyAttributeValue (app, kAXFocusedWindowAttribute ,
239+ (CFTypeRef *)&window);
240+
241+ // If no error occurred, proceed with the resize and reposition operations
242+ if (error == kAXErrorSuccess ) {
243+
244+ // Create AXValue objects for position and size
245+ AXValueRef positionValue = AXValueCreate ((AXValueType)kAXValueCGPointType ,
246+ (const void *)&rect.origin );
247+ CGSize size = CGSizeMake (rect.size .width , rect.size .height );
248+ AXValueRef sizeValue =
249+ AXValueCreate ((AXValueType)kAXValueCGSizeType , (const void *)&size);
250+
251+ // Set new position and size
252+ AXUIElementSetAttributeValue (window, kAXPositionAttribute , positionValue);
253+ AXUIElementSetAttributeValue (window, kAXSizeAttribute , sizeValue);
254+
255+ // Clean up AXValue and AXUIElement objects
256+ CFRelease (positionValue);
257+ CFRelease (sizeValue);
258+ CFRelease (window);
259+ CFRelease (app);
260+
261+ // Return true to indicate successful resize
262+ return true ;
263+ } else {
264+ // NSLog(@"Could not resize window with window handle %lld", windowHandle);
265+ CFRelease (app);
266+ return false ;
267+ }
268+
269+ return YES ;
270+ }
271+
0 commit comments