windows - OS X 中的窗口移动和调整 API

标签 windows macos layout

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

请注意,我想从外部进程执行此操作 - 我不是在询问如何只控制我自己的应用程序的窗口大小和位置。

最佳答案

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

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

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

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

gcc -framework Carbon -o test test.c

请注意,为简单起见,我没有在代码中执行任何错误检查(如果出现问题,有很多地方可能会导致程序崩溃,并且某些事情可能/可能会出错)。这是代码:

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

对于 AXUIElementCopyAttributeValue 函数,您使用 kAXWindowsAttribute 而不是 kAXFocusedWindowAttribute。结果不是 AXUIElementRef,而是 AXUIElementRef 元素的 CFArray,该应用程序的每个窗口一个。

关于windows - OS X 中的窗口移动和调整 API,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/614185/

相关文章:

windows - 用于经典 ASP 开发的最新环境

c++ - DirectX11 CreateSharedHandle - 这是跨设备的吗?

linux - flutter 桌面 : how to hide a window (and keep the process alive)?

Python 和 Homebrew Vim

java - 平等地抢占多余空间

c++ - 适用于 Windows 和 Linux 的 Visual Studio 2010 项目

macos - Vim grep 导致字符暂时消失

objective-c - 之间的实际差异 - (IBAction)pushClear :(id)sender and - (IBAction)pushClear:(NSButton)sender

java - GridLayout 错误

CSS 全高侧边栏,内容响应