objective-cmacosaccessibility-apiwindow-position

Move other windows on Mac OS X using Accessibility API


I'm trying to use the Accessibility API to change the position of other applications windows.What I wish to do is to get all the windows on the screen from all the applications, then move them all a given offset (lets say 5 or 10 or any value). I am having difficulties doing this since this is day one of programming in Objective-C for me.

Here is what I am doing right now. First, I find the list of windows and their PIDs using CGWindowListCopyWindowInfo. Then, for each window I use AXUIElementCreateApplication to get the AXUIElementRef of the window. After, that I should use AXUIElementCopyAttributeValue with the attribute kAXPositionAttribute (which I fail in getting the proper position, always get zeros). Finally, I should add the wanted offset to the position and use AXUIElementSetAttributeValue with the attribute kAXPositionAttribute and the new position point (which I get runtime errors even if I set absolute values like 0,0).

Can someone help me with a snippet doing what I described above, as I tried many things without any luck. Also, it shouldn't need to be exactly as I decided to implement it above. If there is a better way to go about doing that, then I'll be happy to change it.

Update: As requested in the comment, here is a code snippet of one of the attempts:

// Get all the windows
CFArrayRef windowList = CGWindowListCopyWindowInfo(kCGWindowListOptionOnScreenOnly, kCGNullWindowID);
NSArray* arr = CFBridgingRelease(windowList);
// Loop through the windows
for (NSMutableDictionary* entry in arr)
{
    // Get window PID
    pid_t pid = [[entry objectForKey:(id)kCGWindowOwnerPID] intValue];
    // Get AXUIElement using PID
    AXUIElementRef elementRef = AXUIElementCreateApplication(pid);
    CFTypeRef position;
    CGPoint point;
    // Get the position attribute of the window (maybe something is wrong?)
    AXUIElementCopyAttributeValue(elementRef, kAXPositionAttribute, (CFTypeRef *)&position);
    AXValueGetValue(position, kAXValueCGPointType, &point);
    // Debugging (always zeros?)
    NSLog(@"point=%@", point);
    // Create a point
    NSPoint newPoint;
    newPoint.x = 0;
    newPoint.y = 0;
    position = (CFTypeRef)(AXValueCreate(kAXValueCGPointType, (const void *)&newPoint));
    // Set the position attribute of the window (runtime error over here)
    AXUIElementSetAttributeValue(elementRef, kAXPositionAttribute, (CFTypeRef *)&position);
}

Solution

  • Based on your sample code (slightly modified, as what you posted does not compile and will crash unmodified), I did some experiments.

    Here are a few caveats:

    I've included here modifications to your code (this will run without crashing), which will grab the pertinent window information from the applications you retrieve via PID and then move the windows. I have a sleep statement so that I could stop execution, since I was just testing for the effect of movement:

    #import <Foundation/Foundation.h>
    #import <CoreFoundation/CoreFoundation.h>
    #import <ApplicationServices/ApplicationServices.h>
    
    int main(int argc, char *argv[]) {
        @autoreleasepool {
        // Get all the windows
        CFArrayRef windowList = CGWindowListCopyWindowInfo(kCGWindowListOptionOnScreenOnly, kCGNullWindowID);
        NSArray* arr = CFBridgingRelease(windowList);
        // Loop through the windows
        for (NSMutableDictionary* entry in arr)
        {
            // Get window PID
            pid_t pid = [[entry objectForKey:(id)kCGWindowOwnerPID] intValue];
            // Get AXUIElement using PID
            AXUIElementRef appRef = AXUIElementCreateApplication(pid);
            NSLog(@"Ref = %@",appRef);
    
            // Get the windows
            CFArrayRef windowList;
            AXUIElementCopyAttributeValue(appRef, kAXWindowsAttribute, (CFTypeRef *)&windowList);
            NSLog(@"WindowList = %@", windowList);
            if ((!windowList) || CFArrayGetCount(windowList)<1)
                continue;
    
    
            // get just the first window for now
            AXUIElementRef windowRef = (AXUIElementRef) CFArrayGetValueAtIndex( windowList, 0);
            CFTypeRef role;
            AXUIElementCopyAttributeValue(windowRef, kAXRoleAttribute, (CFTypeRef *)&role);         
            CFTypeRef position;
            CGPoint point;
    
            // Get the position attribute of the window (maybe something is wrong?)
            AXUIElementCopyAttributeValue(windowRef, kAXPositionAttribute, (CFTypeRef *)&position);
            AXValueGetValue(position, kAXValueCGPointType, &point);
            // Debugging (always zeros?)
            NSLog(@"point=%f,%f", point.x,point.y);
            // Create a point
            CGPoint newPoint;
            newPoint.x = 0;
            newPoint.y = 0;
            NSLog(@"Create");
            position = (CFTypeRef)(AXValueCreate(kAXValueCGPointType, (const void *)&newPoint));
            // Set the position attribute of the window (runtime error over here)
            NSLog(@"SetAttribute");
            AXUIElementSetAttributeValue(windowRef, kAXPositionAttribute, position);
            sleep(5);
        }       
        }
    }