使用辅助功能 API 在 Mac OS X 上移动其他窗口
-
02-01-2020 - |
题
我正在尝试使用 Accessibility API 来更改其他应用程序窗口的位置。我想要做的是从所有应用程序获取屏幕上的所有窗口,然后将它们全部移动给定的偏移量(比如说 5 或 10或任何值)。我在做这件事时遇到了困难,因为这是我使用 Objective-C 编程的第一天。
这就是我现在正在做的事情。首先,我使用以下命令找到窗口列表及其 PID CGWindowListCopyWindowInfo
. 。然后,对于我使用的每个窗口 AXUIElementCreateApplication
得到 AXUIElementRef
窗户的。之后,我应该使用 AXUIElementCopyAttributeValue
与属性 kAXPositionAttribute
(我未能获得正确的位置,总是得到零)。最后,我应该将想要的偏移量添加到位置并使用 AXUIElementSetAttributeValue
与属性 kAXPositionAttribute
和新的位置点(即使我设置了像 0,0 这样的绝对值,我也会得到运行时错误)。
有人可以帮我做一个片段,做我上面描述的事情,因为我尝试了很多事情,但没有任何运气。另外,它不需要完全像我上面决定实现的那样。如果有更好的方法来做到这一点,那么我很乐意改变它。
更新:根据评论中的要求,以下是其中一项尝试的代码片段:
// 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);
}
解决方案
根据您的示例代码(稍作修改,因为您发布的内容无法编译并且未经修改就会崩溃),我做了一些实验。
以下是一些注意事项:
- 您正在检索 应用 通过 PID,然后对其进行操作,就好像它是一个窗口一样。这是问题的核心,但这只是解决方案的开始。
- 您将需要遍历辅助功能应用程序对象的窗口列表,以便找到可以使用辅助功能框架移动的可重定位窗口。
CGWindowListCopyWindowInfo
当询问您的调用方式时,将返回“所有屏幕上”窗口,但它不能保证这些窗口是“用户窗口”或具有可访问性的窗口。大多数菜单栏项目都有一个“在屏幕上”的根窗口,并且其中大多数是不可访问的(当您尝试遍历所检索的 PID 的可访问性树时,会显示该根窗口)。- 您可能会发现 AXRole 测试很有帮助,或者您可能会发现其他窗口可访问性属性在确定是否移动窗口时更有用。
我在这里对您的代码进行了修改(这将运行而不会崩溃),它将从您通过 PID 检索的应用程序中获取相关的窗口信息,然后移动窗口。我有一个睡眠语句,以便我可以停止执行,因为我只是测试运动的效果:
#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);
}
}
}