1
+ #include " ../window_manager.h"
2
+ #import < AppKit/AppKit.h>
3
+ #import < AppKit/NSAccessibility.h>
4
+ #import < ApplicationServices/ApplicationServices.h>
1
5
#include < CoreGraphics/CGWindow.h>
2
6
#import < Foundation/Foundation.h>
3
- #import < AppKit/AppKit.h>
4
- #include " ../window_manager.h"
5
7
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 );
9
13
10
14
for (NSDictionary *info in (NSArray *)windowList) {
11
15
NSNumber *windowNumber = info[(id )kCGWindowNumber ];
25
29
}
26
30
27
31
WindowHandle 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 );
30
36
31
37
for (NSDictionary *info in (NSArray *)windowList) {
32
38
NSNumber *ownerPid = info[(id )kCGWindowOwnerPID ];
33
39
NSNumber *windowNumber = info[(id )kCGWindowNumber ];
34
40
35
- auto app = [NSRunningApplication runningApplicationWithProcessIdentifier: [ownerPid intValue ]];
41
+ auto app = [NSRunningApplication
42
+ runningApplicationWithProcessIdentifier: [ownerPid intValue ]];
36
43
37
44
if (![app isActive ]) {
38
45
continue ;
@@ -49,16 +56,19 @@ WindowHandle getActiveWindow() {
49
56
}
50
57
51
58
std::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 );
54
63
55
64
std::vector<WindowHandle> windowHandles;
56
65
57
66
for (NSDictionary *info in (NSArray *)windowList) {
58
67
NSNumber *ownerPid = info[(id )kCGWindowOwnerPID ];
59
68
NSNumber *windowNumber = info[(id )kCGWindowNumber ];
60
69
61
- auto app = [NSRunningApplication runningApplicationWithProcessIdentifier: [ownerPid intValue ]];
70
+ auto app = [NSRunningApplication
71
+ runningApplicationWithProcessIdentifier: [ownerPid intValue ]];
62
72
auto path = app ? [app.bundleURL.path UTF8String ] : " " ;
63
73
64
74
if (app && path != " " ) {
@@ -77,8 +87,10 @@ MMRect getWindowRect(const WindowHandle windowHandle) {
77
87
auto windowInfo = getWindowInfo (windowHandle);
78
88
if (windowInfo != nullptr && windowHandle >= 0 ) {
79
89
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 );
82
94
}
83
95
}
84
96
return MMRectMake (0 , 0 , 0 , 0 );
@@ -88,7 +100,172 @@ MMRect getWindowRect(const WindowHandle windowHandle) {
88
100
auto windowInfo = getWindowInfo (windowHandle);
89
101
if (windowInfo != nullptr && windowHandle >= 0 ) {
90
102
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]);
92
106
}
93
107
return " " ;
94
108
}
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