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);
}
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:
CGWindowListCopyWindowInfo
will return "all on screen" windows when asked the way you're calling it, but it doesn't guarantee that these are either "user windows" or windows with accessibility. Most menu bar items have a root window which is "on screen" and most of them aren't accessible (which shows up when you try to walk the accessibility tree for the PIDs you retrieve).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);
}
}
}