Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Window move and resize APIs in OS X

I'm trying to find documented (or, undocumented, if that's my only option) APIs on OS X to query a list of windows from the window server and then cause the windows to move and resize. Can anyone point me in the right direction? I guess I'd be starting with something like FindWindowEx and MoveWindow under Win32.

Note that I want to do this from an external process - I'm not asking how to control just my own app's window size and position.

like image 290
Le Dude Avatar asked Mar 05 '09 10:03

Le Dude


People also ask

How do I force a window to resize on a Mac?

A Mac window can be resized using the mouse and the buttons in the upper-left corner of the desktop. Minimize - Clicking this button (yellow circle), which looks like a dash, collapses the window to the Dock.

How do you move a window on a Mac?

Move a window to one side of the screen: Press and hold the Option key while you move the pointer over the green button in the top-left corner of the window, then choose Move Window to Left Side of Screen or Move Window to Right Side of Screen from the menu that appears.

How do you resize a window that is too big Mac?

Move your cursor to any side of a window—top, bottom, left, or right. As the cursor nears the edge of the window, it changes to a double-ended arrow. When you see the double-ended arrow, click and drag to resize the window.


Video Answer


2 Answers

Use the Accessibility API. Using this API you can connect to a process, obtain a list of windows (actually an array), get the positions and sizes of each window and also change window properties if you like.

However, an application can only be using this API if the user has enabled access for assistive devices in his preferences (System Prefs -> Universal Access), in which case all applications may use this API, or if your application is a trusted assitive application (when it is trusted, it may use the API, even if this option is not checked). The Accessibility API itself offers the necessary functions to make your application trusted - basically you must become root (using security services to request root permissions of the user) and then mark your process as trusted. Once your application has been marked trusted, it must be restarted as the trusted state is only checked on start-up and can't change while the app is running. The trust state is permanent, unless the user moves the application somewhere else or the hash of the application binary changes (e.g. after an update). If the user has assistive devices enabled in his prefs, all applications are treated as if they were trusted. Usually your app would check if this option is enabled, if it is, go on and do your stuff. If not, it would check if it is already trusted, if it is, again just do your stuff. If not try to make itself trusted and then restart the application unless the user declined root authorization. The API offers all necessary functions to check all this.

There exist private functions to do the same using the Mac OS window manager, but the only advantage that would buy you is that you don't need to be a trusted Accessibility application (which is a one time operation on first launch in most cases). The disadvantages are that this API may change any time (it has already changed in the past), it's all undocumented and functions are only known through reverse engineering. The Accessibility however is public, it is documented and it hasn't change much since the first OS X version that introduced it (some new functions were added in 10.4 and again in 10.5, but not much else has changed).

Here's a code example. It will wait 5 seconds, so you can switch to a different window before it does anything else (otherwise it will always work with the terminal window, rather boring for testing). Then it will get the front most process, the front most window of this process, print it's position and size and finally move it by 25 pixels to the right. You compile it on command line like that (assuming it is named test.c)

gcc -framework Carbon -o test test.c 

Please note that I do not perform any error checking in the code for simplicity (there are various places that could cause the program to crash if something goes wrong and certain things may/can go wrong). Here's the code:

/* Carbon includes everything necessary for Accessibilty API */ #include <Carbon/Carbon.h>  static bool amIAuthorized () {     if (AXAPIEnabled() != 0) {         /* Yehaa, all apps are authorized */         return true;     }     /* Bummer, it's not activated, maybe we are trusted */     if (AXIsProcessTrusted() != 0) {         /* Good news, we are already trusted */         return true;     }     /* Crap, we are not trusted...      * correct behavior would now be to become a root process using      * authorization services and then call AXMakeProcessTrusted() to make      * ourselves trusted, then restart... I'll skip this here for      * simplicity.      */     return false; }   static AXUIElementRef getFrontMostApp () {     pid_t pid;     ProcessSerialNumber psn;          GetFrontProcess(&psn);     GetProcessPID(&psn, &pid);     return AXUIElementCreateApplication(pid); }       int main (     int argc,     char ** argv ) {     int i;     AXValueRef temp;     CGSize windowSize;     CGPoint windowPosition;     CFStringRef windowTitle;     AXUIElementRef frontMostApp;     AXUIElementRef frontMostWindow;              if (!amIAuthorized()) {         printf("Can't use accessibility API!\n");         return 1;     }          /* Give the user 5 seconds to switch to another window, otherwise      * only the terminal window will be used      */     for (i = 0; i < 5; i++) {         sleep(1);         printf("%d", i + 1);         if (i < 4) {             printf("...");             fflush(stdout);         } else {             printf("\n");         }     }          /* Here we go. Find out which process is front-most */     frontMostApp = getFrontMostApp();          /* Get the front most window. We could also get an array of all windows      * of this process and ask each window if it is front most, but that is      * quite inefficient if we only need the front most window.      */     AXUIElementCopyAttributeValue(         frontMostApp, kAXFocusedWindowAttribute, (CFTypeRef *)&frontMostWindow     );          /* Get the title of the window */     AXUIElementCopyAttributeValue(         frontMostWindow, kAXTitleAttribute, (CFTypeRef *)&windowTitle     );          /* Get the window size and position */     AXUIElementCopyAttributeValue(         frontMostWindow, kAXSizeAttribute, (CFTypeRef *)&temp     );     AXValueGetValue(temp, kAXValueCGSizeType, &windowSize);     CFRelease(temp);          AXUIElementCopyAttributeValue(         frontMostWindow, kAXPositionAttribute, (CFTypeRef *)&temp     );     AXValueGetValue(temp, kAXValueCGPointType, &windowPosition);     CFRelease(temp);      /* Print everything */     printf("\n");     CFShow(windowTitle);     printf(         "Window is at (%f, %f) and has dimension of (%f, %f)\n",         windowPosition.x,         windowPosition.y,         windowSize.width,         windowSize.height     );          /* Move the window to the right by 25 pixels */     windowPosition.x += 25;     temp = AXValueCreate(kAXValueCGPointType, &windowPosition);     AXUIElementSetAttributeValue(frontMostWindow, kAXPositionAttribute, temp);     CFRelease(temp);          /* Clean up */     CFRelease(frontMostWindow);     CFRelease(frontMostApp);     return 0; } 

Sine Ben asked how you get a list of all windows in the comments, here's how:

Instead of kAXFocusedWindowAttribute you use kAXWindowsAttribute for the AXUIElementCopyAttributeValue function. The result is then no AXUIElementRef, but a CFArray of AXUIElementRef elements, one for each window of this application.

like image 94
Mecki Avatar answered Sep 20 '22 09:09

Mecki


I agree that Accessibility is the best way forward. But if you want quick-and-dirty, AppleScript will work as well.

like image 22
vasi Avatar answered Sep 20 '22 09:09

vasi