macOS窗口信息CGWindowListCopyWindowInfo
macOS窗口信息CGWindowListCopyWindowInfo

macOS窗口信息CGWindowListCopyWindowInfo

CGWindowListCopyWindowInfo 是 macOS Core Graphics 框架中的一个函数,用于获取当前所有窗口的信息,比如窗口标题、应用名称、窗口位置、层级、是否可见、所属进程等。它返回一个包含窗口信息的数组(CFArray),其中每一项是一个描述窗口的 CFDictionary。

基本用法

func CGWindowListCopyWindowInfo(
    _ option: CGWindowListOption,
    _ relativeToWindow: CGWindowID
) -> CFArray?

1、option: CGWindowListOption类型

这是一个位掩码(OptionSet),用于指定想要列出的窗口类型,可以组合使用多个选项。常用选项包括:

.optionAll:所有窗口(包括后台、隐藏);

.optionOnScreenOnly:仅显示在屏幕上的窗口;

.optionOnScreenAboveWindow:屏幕上,在某个窗口之上的窗口;

.optionOnScreenBelowWindow:屏幕上,在某个窗口之下的窗口;

.optionIncludingWindow:包含指定窗口;

.excludeDesktopElements:排除桌面图标、壁纸等元素。

2、relativeToWindow: CGWindowID

这是参考的窗口 ID,如果用到了 optionOnScreenAboveWindow 或 optionOnScreenBelowWindow,就需要提供该窗口的 ID,否则填 kCGNullWindowID或者0。

kCGNullWindowID实际上就是0,它是一个特殊的窗口 ID,代表「没有特定窗口」或「空窗口引用」。

3、返回一个 CFArray,里面是 CFDictionary 类型的窗口信息,可以转换为 [[String: Any]] 来访问。

使用场景

1、获取所有窗口信息

if let windowInfoList = CGWindowListCopyWindowInfo([.optionOnScreenOnly, .excludeDesktopElements], kCGNullWindowID) as? [[String: Any]] {
    for windowInfo in windowInfoList {
        if let ownerName = windowInfo["kCGWindowOwnerName"] as? String,
           let windowName = windowInfo["kCGWindowName"] as? String,
           let bounds = windowInfo["kCGWindowBounds"] as? [String: CGFloat] {
            print("App: \(ownerName), 标题: \(windowName), 尺寸: \(bounds)")
        }
    }
}

输出:

App: 微信, 标题: Item-0, 尺寸: ["X": 992.0, "Width": 38.0, "Y": 0.0, "Height": 36.0]
App: 轻截图片, 标题: Item-0, 尺寸: ["X": 882.0, "Width": 34.0, "Y": 0.0, "Height": 36.0]
App: Xcode, 标题: SnapSlim — StatusBarController.swift, 尺寸: ["X": 72.0, "Width": 1112.0, "Y": 82.0, "Height": 722.0]
App: Google Chrome, 标题: 方君宇, 尺寸: ["X": 160.0, "Width": 898.0, "Y": 302.0, "Height": 630.0]
App: Microsoft Word, 标题: macOS截取屏幕窗口CGWindowListCreateImage, 尺寸: ["X": 369.0, "Width": 1002.0, "Y": 69.0, "Height": 674.0]

CFArray字段

可提取的信息字段(字典 key):

1、kCGWindowOwnerName:String,应用名称;

2、kCGWindowOwnerPID:Int类型,进程 PID;

3、kCGWindowNumber:Int类型,窗口 ID;

4、kCGWindowName:String类型,窗口标题;

5、kCGWindowLayer:Int类型,窗口层级;

6、kCGWindowAlpha:Float类型,透明度;

7、kCGWindowBounds:[String: CGFloat] 类型,窗口位置和大小;

8、kCGWindowIsOnscreen:Bool类型,是否显示在屏幕上。

总结

CGWindowListCopyWindowInfo用于枚举桌面上所有可见窗口,可以获取指定App的前台窗口信息。

可以配合CGWindowListCreateImage,获取截图时窗口位置,辅助窗口管理器或屏幕录制工具。

扩展知识

窗口位置

通过kCGWindowBounds获取的窗口位置,默认是从左上角开始计算,而不是左下角,例如状态栏中控制中心的声音菜单:

App: 控制中心, 进程PID: 828, 窗口ID:17, 标题: Sound, 层级:25, 透明度:1.0, 尺寸: ["X": 1107.0, "Width": 38.0, "Y": 0.0, "Height": 36.0], 是否在屏幕上: true

它的Y轴为0,X轴为1107,表示从左上角开始计算的“声音”状态栏。

窗口层级

在kCGWindowLayer字段获取窗口层级时,常用的层级有:

0:kCGNormalWindowLevelKey,普通窗口,例如大多数 App 主窗口;

3:kCGFloatingWindowLevelKey,比普通窗口高,HUD、浮动面板;

8:kCGModalPanelWindowLevelKey,模态对话框,比如弹出警告框;

19:kCGUtilityWindowLevelKey,工具类窗口,例如颜色选择器;

20:kCGDockWindowLevelKey,Dock 的窗口层;

24、kCGMainMenuWindowLevelKey,菜单栏(但不是菜单内容);

25、kCGStatusWindowLevelKey,状态栏图标菜单,例如微信展开的菜单项;

101、系统内部使用的高层级窗口,例如系统菜单展开窗口、控制中心、Spotlight 等;

1000+:kCGCursorWindowLevelKey 以上,鼠标、拖拽指示器、屏保等特殊层。

0、3、8都是kCGWindowLayer返回的层级,kCGNormalWindowLevelKey、kCGFloatingWindowLevelKey则是层级对应的名称。

示例:

App: Window Server, 标题: StatusIndicator, 层级:2147483630, 透明度:1.0, 尺寸: ["Height": 18.0, "Width": 10.0, "X": 1278.0, "Y": 9.0]
App: 轻截图片, 标题: , 层级:101, 透明度:0.0, 尺寸: ["Height": 120.0, "Width": 127.0, "Y": 37.0, "X": 910.0]
App: 轻截图片, 进程PID: 77392, 窗口ID:218098, 标题: Item-0, 层级:25, 透明度:1.0, 尺寸: ["Height": 36.0, "Width": 34.0, "Y": 0.0, "X": 914.0]
App: Xcode, 标题: SnapSlim — StatusBarController.swift, 层级:0, 透明度:1.0, 尺寸: ["X": 63.0, "Height": 722.0, "Width": 1112.0, "Y": 152.0]
App: Google Chrome, 标题: macOS窗口信息CGWindowListCopyWindowInf – 方君宇, 层级:0, 透明度:1.0, 尺寸: ["Height": 749.0, "Width": 1148.0, "X": 96.0, "Y": 39.0]

Window Server层级2147483630,表示系统层级。

轻截图片有两个,分别是101层级和25层级:

101层级实际上是状态栏的下拉菜单,在截图时被隐藏,所以透明度是0。

25层级则对应的是状态栏的图标菜单。

Xcode和Google Chrome都是0层级,表示普通窗口。

   

如果您认为这篇文章给您带来了帮助,您可以在此通过支付宝或者微信打赏网站开发者。

欢迎加入我们的 微信交流群QQ交流群,交流更多精彩内容!
微信交流群二维码 QQ交流群二维码

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注