【问题标题】:Move other windows on Mac OS X using Accessibility API使用 Accessibility API 在 Mac OS X 上移动其他窗口
【发布时间】:2014-01-11 23:07:42
【问题描述】:

我正在尝试使用 Accessibility API 来更改其他应用程序窗口的位置。我想做的是从所有应用程序中获取屏幕上的所有窗口,然后将它们全部移动给定的偏移量(假设5 或 10 或任何值)。我很难做到这一点,因为这是我使用 Objective-C 编程的第一天。

这就是我现在正在做的事情。首先,我使用CGWindowListCopyWindowInfo 找到窗口列表及其PID。然后,对于每个窗口,我使用AXUIElementCreateApplication 来获取窗口的AXUIElementRef。之后,我应该使用 AXUIElementCopyAttributeValue 和属性 kAXPositionAttribute (我未能获得正确的位置,总是得到零)。最后,我应该将想要的偏移量添加到位置并使用 AXUIElementSetAttributeValue 与属性 kAXPositionAttribute 和新的位置点(即使我设置绝对值如 0,0,也会出现运行时错误)。

有人可以帮我做我上面描述的sn-p,因为我尝试了很多事情都没有运气。此外,它不应该完全像我在上面决定实现的那样。如果有更好的方法来做到这一点,那么我很乐意改变它。

更新: 根据评论中的要求,这是其中一种尝试的代码 sn-p:

// 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);
}

【问题讨论】:

  • 发布您的一次尝试中的代码,以便人们可以看到您从什么开始。
  • 它是分步解释的,我所拥有的不起作用,所以我认为不需要一个不起作用的。我已经用 sn-p 更新了它。

标签: objective-c macos accessibility-api window-position


【解决方案1】:

根据您的示例代码(稍作修改,因为您发布的内容无法编译并且未经修改会崩溃),我做了一些实验。

这里有一些注意事项:

  • 您正在通过 PID 检索 应用程序,但随后对其进行操作,就好像它是一个窗口一样。这是您问题的核心,但这只是解决方案的开始。
  • 您需要遍历辅助功能应用程序对象的窗口列表,才能找到可以使用辅助功能框架移动的可重定位窗口。
  • CGWindowListCopyWindowInfo 将在被问及您调用它的方式时返回“屏幕上的所有”窗口,但它不能保证这些是“用户窗口”或具有可访问性的窗口。大多数菜单栏项目都有一个“在屏幕上”的根窗口,并且其中大部分都无法访问(当您尝试遍历检索到的 PID 的可访问性树时会显示)。
  • 您可能会发现 AXRole 测试很有帮助,或者您可能会发现其他窗口可访问性属性在确定是否移动窗口时更有用。

我在这里包含了对您的代码的修改(这将运行而不会崩溃),它将从您通过 PID 检索的应用程序中获取相关的窗口信息,然后移动窗口。我有一个 sleep 语句,以便我可以停止执行,因为我只是在测试运动的效果:

#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);
    }       
    }
}

【讨论】:

  • 很好的答案,谢谢!投票赞成,并被选中。但是,您能否详细说明一下 AXRole 的使用,在这种情况下它会有什么用处?
  • @tria AXRole(取决于其窗口可用的应用程序对它的使用)可以帮助您决定要移动哪些窗口,例如kAXSystemWideRolekAXSheetRole。您可以使用Accessibility Inspector(现在作为 Xcode 的一部分安装)环顾您自己的系统。
  • @gaige 似乎macOS Sierra 中的AXUIElementCopyAttributeValue(appRef, kAXWindowsAttribute, (CFTypeRef *)&amp;windowList); 不会返回任何窗口。
  • @loretoparisi 我也看到重新运行我的测试夹具。我猜这与权限模型有关,因为我还没有在可以授予辅助功能权限的完整应用程序中尝试过。
  • @mica 我真的没怎么玩过 Swift 和 AX。您可以尝试将上述内容拆分为方法,然后使用 Xcode 并排使用 Swift 和 Objective-C 的能力来访问这些方法。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-11
  • 1970-01-01
  • 2012-03-16
  • 2020-10-22
  • 1970-01-01
  • 2014-07-23
相关资源
最近更新 更多