在Mac OS X中,有没有一种方法可以遍历所有打开的窗口?

3
当您从笔记本电脑上拔下分辨率高于MacBook的外部监视器时,窗口大多会保留其宽度,但大小被剪裁为MacBook屏幕的(较小)高度。当您再次插入监视器时,它们的大小仍然非常小,这很令人沮丧。
我的问题是:是否有任何方法可以遍历所有打开的窗口,保存它们的大小,并在再次插入监视器后恢复它们?
2个回答

10
以下的 AppleScript 显示如何:
  • 循环遍历所有窗口
  • 检索并更改窗口位置
  • 检索并更改窗口大小

代码:

tell application "System Events"
    set theProcesses to application processes
    repeat with theProcess from 1 to count theProcesses
        tell process theProcess
            repeat with x from 1 to (count windows)
                set windowPosition to position of window x
                set windowSize to size of window x
                set position of window x to {0, 0}
                set size of window  to {100, 100}
            end repeat
        end tell
    end repeat
end tell

注意:该脚本需要开启辅助功能设备(AfAD):
"系统偏好设置" → "辅助功能" → "启用辅助功能设备"

编辑(回复评论)

从AppleScipt中启用AfAD可能会改善用户体验,但不要每次执行脚本都这样做,只在AfAD被禁用的情况下启用AfAD。未经通知就启用功能是不好的做法,请提示用户允许启用AfAD。

示例:

set AccesEnables to do shell script "[ -e \"/private/var/db/.AccessibilityAPIEnabled\" ] && echo \"Yes\" || echo \"No\""
if (AccesEnables is equal to "No") then
    set askUser to display dialog "This application requires access for assistive devices. Enable this feature?" default button 2
    set answer to button returned of askUser
    if answer is equal to "OK" then
        do shell script "touch /private/var/db/.AccessibilityAPIEnabled" with administrator privileges
    else
        close
    end if
end if

+1,但是可能值得展示如何通过请求授权(使用管理员权限的 do shell script…)来编程打开辅助访问。 - abarnert
太遗憾了,我不能再+1了...但是“使用管理员权限”已经弹出一个对话框询问是否授予权限,所以你不想弹出一个对话框询问是否弹出要求权限的对话框,除非你是Windows Vista的忠实粉丝。 :) - abarnert
1
这并不完全正确。第一个对话框告知用户需要启用其他功能才能使用应用程序。第二个对话框仅要求输入密码,但没有解释原因。使用这两个对话框,用户知道正在发生什么,并可以选择是否启用该功能。此外,您可以在第一个对话框中提醒用户需要管理员权限,并且下一个窗口将要求输入密码。未经通知即永久更改设置是不好的做法,因此应使用附加对话框。 - Anne
warm == warn && within == without温暖等于警告 && 在内部等于在外部 - Anne

3

对于这个问题,最简单的解决方案是使用AppleScript,详见Anne的回答。

如果你想要把这个方法分享给不太懂如何启用辅助功能的朋友,或者更广泛地分发它,只需添加这一行:

do shell script ¬
  "touch /private/var/db/.AccessibilityAPIEnabled" ¬
  with administrator privileges

这将弹出常规的身份验证对话框,然后使用权限打开辅助功能访问。
实际上,可以在不使用辅助功能的情况下完成此操作,但需要使用CoreGraphics / Quartz Window Services中的私有函数,即CGSPrivate.h
使用公共API,您可以轻松枚举所有窗口:
CFArrayRef windows = 
  CGWindowListCopyWindowInfo(kCGWindowListOptionOnScreenOnly |
                             kCGWindowListExcludeDesktopElements,
                             kCGNullWindowID);

这将返回一个字典数组,每个字典包含一个kCGWindowBounds字典,其中包含Height,Width,X和Y整数值。
但这些公共API是只读的。要实际移动窗口,您必须深入CGSPrivate.h并执行类似以下内容的操作:
CGSConnection conn = _CGSDefaultConnection();
for (NSDictionary *window in windows) {
  CGSWindow wid = (CGSWindow)[[window objectForKey:@"kCGWindowNumber"] intValue];
  CGRect bounds;
  CGRectMakeWithDictionaryRepresentation([window objectForKey:@"kCGWindowBounds"],
                                         &bounds);
  CGSMoveWindow(conn, wid, bounds.origin);
}

显然,这很糟糕,只有在你真正需要分发一个无法请求辅助访问的应用时才应该考虑它。

你也可以反向工程Window Server协议并直接与其通信,但这更加糟糕。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接