OS X 中的窗口移动和调整 API 大小 [英] Window move and resize APIs in OS X

查看:24
本文介绍了OS X 中的窗口移动和调整 API 大小的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试在 OS X 上查找已记录(或未记录,如果这是我唯一的选择)的 API,以从窗口服务器查询窗口列表,然后使窗口移动和调整大小.任何人都可以指出我正确的方向吗?我想我会从 Win32 下的 FindWindowEx 和 MoveWindow 之类的东西开始.

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.

推荐答案

使用辅助功能 API.使用此 API,您可以连接到一个进程,获取窗口列表(实际上是一个数组),获取每个窗口的位置和大小,还可以根据需要更改窗口属性.

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.

但是,只有当用户在其首选项(系统首选项 -> 通用访问)中启用了辅助设备的访问权限时,应用程序才能使用此 API,在这种情况下,所有应用程序都可以使用此 API,或者如果您的应用程序是受信任的辅助应用程序(当它受信任时,它可能会使用 API,即使未选中此选项).Accessibility API 本身提供了必要的功能来使您的应用程序受信任 - 基本上您必须成为 root(使用安全服务来请求用户的 root 权限),然后将您的进程标记为受信任.一旦您的应用程序被标记为受信任,它必须重新启动,因为受信任状态仅在启动时检查并且在应用程序运行时不能更改.信任状态是永久性的,除非用户将应用程序移到其他地方或应用程序二进制文件的哈希值发生变化(例如更新后).如果用户在他的首选项中启用了辅助设备,则所有应用程序都被视为受信任.通常您的应用程序会检查此选项是否已启用,如果已启用,请继续执行您的操作.如果不是,它会检查它是否已经被信任,如果是,再次做你的事情.如果没有尝试让自己受信任,然后重新启动应用程序,除非用户拒绝 root 授权.API 提供了所有必要的功能来检查所有这些.

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.

存在使用 Mac OS 窗口管理器执行相同操作的私有函数,但唯一能给您带来好处的是您不需要成为受信任的辅助功能应用程序(这是在首次启动时的一次性操作)大多数情况下).缺点是这个 API 可能随时更改(它过去已经更改),它都没有记录,功能只能通过逆向工程知道.然而,可访问性是公开的,它被记录在案,并且自引入它的第一个 OS X 版本以来并没有太大变化(一些新功能在 10.4 和 10.5 中添加,但没有太大变化).

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).

这是一个代码示例.它将等待 5 秒钟,因此您可以在执行任何其他操作之前切换到不同的窗口(否则它将始终与终端窗口一起工作,而测试时会很无聊).然后它会得到最前面的进程,这个进程的最前面的窗口,打印它的位置和大小,最后向右移动25个像素.你像这样在命令行上编译它(假设它被命名为 test.c)

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!
");
        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("
");
        }
    }
    
    /* 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("
");
    CFShow(windowTitle);
    printf(
        "Window is at (%f, %f) and has dimension of (%f, %f)
",
        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 问你如何在评论中获得所有窗口的列表,方法如下:

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

您将 kAXWindowsAttribute 用于 AXUIElementCopyAttributeValue 函数,而不是 kAXFocusedWindowAttribute.结果不是 AXUIElementRef,而是 AXUIElementRef 元素的 CFArray,这个应用程序的每个窗口一个.

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.

这篇关于OS X 中的窗口移动和调整 API 大小的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆