FFMPEG录屏(20)--- 枚举macOS下的窗口和屏幕列表,并获取名称缩略图等信息

在 macOS 下获取可屏幕共享的窗口和屏幕

在 macOS 下,我们可以通过使用 Core Graphics 和 Cocoa 框架来获取当前系统中可屏幕共享的窗口和屏幕信息。本文将详细介绍如何获取窗口和屏幕的 ID、标题、坐标、进程图标和缩略图等信息。

前提条件

在开始之前,请确保您的开发环境满足以下条件:

  1. macOS 系统
  2. 安装了 Xcode 或其他支持 Objective-C++ 开发的编译器

步骤

1. 包含必要的头文件

首先,包含必要的头文件:

#include "base/devices/screen/enumerator.h"
#include "base/devices/screen/darwin/desktop_configuration.h"
#include "base/devices/screen/desktop_geometry.h"
#include "base/devices/screen/utils.h"
#include "base/folder/folder.h"
#include "base/log/logger.h"#include <vector>
#include <functional>
#include <stdlib.h>
#include <string.h>#import <Cocoa/Cocoa.h>
#import <Foundation/Foundation.h>
2. 获取窗口信息

我们可以使用 CGWindowListCopyWindowInfo 函数获取当前系统中的窗口信息。以下是获取窗口信息的函数:

int enum_windows(const traa_size icon_size, const traa_size thumbnail_size,const unsigned int external_flags, std::vector<traa_screen_source_info> &infos) {CFArrayRef window_array = CGWindowListCopyWindowInfo(kCGWindowListOptionAll | kCGWindowListExcludeDesktopElements, kCGNullWindowID);if (!window_array)return false;MacDesktopConfiguration desktop_config =MacDesktopConfiguration::GetCurrent(MacDesktopConfiguration::TopLeftOrigin);CFIndex count = CFArrayGetCount(window_array);for (CFIndex i = 0; i < count; i++) {CFDictionaryRef window =reinterpret_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(window_array, i));if (!window) {continue;}CFNumberRef window_id =reinterpret_cast<CFNumberRef>(CFDictionaryGetValue(window, kCGWindowNumber));if (!window_id) {continue;}traa_screen_source_info window_info;window_info.is_window = true;if (!CFNumberGetValue(window_id, kCFNumberSInt64Type, &window_info.id) || window_info.id <= 0) {continue;}CFNumberRef window_layer =reinterpret_cast<CFNumberRef>(CFDictionaryGetValue(window, kCGWindowLayer));if (!window_layer) {continue;}int layer;if (!CFNumberGetValue(window_layer, kCFNumberIntType, &layer)|| layer < kCGNormalWindowLevel || layer == kCGDockWindowLevel) {continue;}window_info.is_minimized = !is_window_on_screen(window);window_info.is_maximized = is_window_full_screen(desktop_config, window);if ((external_flags & TRAA_SCREEN_SOURCE_FLAG_IGNORE_MINIMIZED) && window_info.is_minimized) {continue;}CFNumberRef window_alpha =reinterpret_cast<CFNumberRef>(CFDictionaryGetValue(window, kCGWindowAlpha));if (!window_alpha) {continue;} else {int alpha;if (!CFNumberGetValue(window_alpha, kCFNumberIntType, &alpha) || alpha <= 0) {continue;}}CFNumberRef window_sharing_state =reinterpret_cast<CFNumberRef>(CFDictionaryGetValue(window, kCGWindowSharingState));if (!window_sharing_state) {continue;} else {int sharing_state;if (!CFNumberGetValue(window_sharing_state, kCFNumberIntType, &sharing_state) ||sharing_state != 1) {continue;}}CFNumberRef window_pid =reinterpret_cast<CFNumberRef>(CFDictionaryGetValue(window, kCGWindowOwnerPID));if (!window_pid) {continue;}std::string str_title = get_window_title(window);std::string str_owner_name = get_window_owner_name(window);if (str_title.empty() && str_owner_name.empty() &&!(external_flags & TRAA_SCREEN_SOURCE_FLAG_NOT_IGNORE_UNTITLED)) {continue;}if (str_title.empty()) {str_title = std::move(str_owner_name);}if (!str_title.empty()) {strncpy(const_cast<char *>(window_info.title), str_title.c_str(),std::min(sizeof(window_info.title) - 1, str_title.size()));}CGRect window_bounds = CGRectZero;if (CFDictionaryContainsKey(window, kCGWindowBounds)) {CFDictionaryRef rect = (CFDictionaryRef)CFDictionaryGetValue(window, kCGWindowBounds);if (rect) {CGRectMakeWithDictionaryRepresentation(rect, &window_bounds);}}if (window_bounds.size.width <= 96 || window_bounds.size.height <= 96) {continue;}window_info.rect = desktop_rect::make_xywh(window_bounds.origin.x, window_bounds.origin.y,window_bounds.size.width, window_bounds.size.height).to_traa_rect();NSRunningApplication *running_app = [NSRunningApplicationrunningApplicationWithProcessIdentifier:((__bridge NSNumber *)window_pid).intValue];if (running_app) {std::string process_path = [[running_app bundleURL] fileSystemRepresentation];if (!process_path.empty()) {strncpy(const_cast<char *>(window_info.process_path), process_path.c_str(),std::min(sizeof(window_info.process_path) - 1, process_path.size()));} else if (external_flags & TRAA_SCREEN_SOURCE_FLAG_IGNORE_NOPROCESS_PATH) {continue;}if (icon_size.width > 0 && icon_size.height > 0 && running_app.icon &&running_app.icon.representations.count > 0) {NSImage *icon = running_app.icon;NSBitmapImageRep *icon_rep =[[NSBitmapImageRep alloc] initWithData:[icon TIFFRepresentation]];if (icon_rep) {desktop_size icon_scaled_size =calc_scaled_size(desktop_size(icon_rep.size.width, icon_rep.size.height), icon_size);CGSize icon_scaled_ns_size =CGSizeMake(icon_scaled_size.width(), icon_scaled_size.height());NSBitmapImageRep *icon_scaled_image_rep = scale_image(icon_rep, icon_scaled_ns_size);if (icon_scaled_image_rep) {size_t icon_data_size = [icon_scaled_image_rep pixelsWide] *[icon_scaled_image_rep pixelsHigh] *[icon_scaled_image_rep samplesPerPixel] * sizeof(unsigned char);window_info.icon_data = new uint8_t[icon_data_size];window_info.icon_size = icon_scaled_size.to_traa_size();if (!window_info.icon_data) {LOG_ERROR("alloc memory for icon data failed");continue;}memcpy(const_cast<uint8_t *>(window_info.icon_data), [icon_scaled_image_rep bitmapData],icon_data_size);}}}}NSBitmapImageRep *origin_image_rep =get_image_rep(true, static_cast<CGWindowID>(window_info.id));if (!origin_image_rep) {continue;}if (thumbnail_size.width > 0 && thumbnail_size.height > 0) {NSImage *origin_image = [NSImage new];[origin_image addRepresentation:origin_image_rep];if (origin_image.size.width <= 1 || origin_image.size.height <= 1) {continue;}desktop_size scaled_size = calc_scaled_size(desktop_size(origin_image.size.width, origin_image.size.height), thumbnail_size);CGSize scaled_ns_size = CGSizeMake(scaled_size.width(), scaled_size.height());NSBitmapImageRep *scaled_image_rep = scale_image(origin_image_rep, scaled_ns_size);if (!scaled_image_rep) {continue;}size_t thumbnail_data_size = [scaled_image_rep pixelsWide] * [scaled_image_rep pixelsHigh] *[scaled_image_rep samplesPerPixel] * sizeof(unsigned char);window_info.thumbnail_data = new uint8_t[thumbnail_data_size];window_info.thumbnail_size = scaled_size.to_traa_size();if (!window_info.thumbnail_data) {LOG_ERROR("alloc memory for thumbnail data failed");continue;}memcpy(const_cast<uint8_t *>(window_info.thumbnail_data), [scaled_image_rep bitmapData],thumbnail_data_size);}infos.push_back(window_info);}CFRelease(window_array);return TRAA_ERROR_NONE;
}
3. 获取屏幕信息

我们可以使用 NSScreen 类获取当前系统中的屏幕信息。以下是获取屏幕信息的函数:

int enum_screens(const traa_size icon_size, const traa_size thumbnail_size,const unsigned int external_flags, std::vector<traa_screen_source_info> &infos) {NSArray *screen_array = NSScreen.screens;if (!screen_array) {return TRAA_ERROR_NONE;}for (size_t i = 0; i < screen_array.count; i++) {NSScreen *screen = screen_array[i];if (!screen) {continue;}CGDirectDisplayID screen_id = static_cast<CGDirectDisplayID>([[screen.deviceDescription objectForKey:@"NSScreenNumber"] unsignedIntValue]);CGRect screen_bounds = CGDisplayBounds(screen_id);NSBitmapImageRep *screen_image_rep = get_image_rep(false, screen_id);if (!screen_image_rep) {continue;}traa_screen_source_info screen_info;screen_info.is_window = false;screen_info.is_primary = CGDisplayIsMain(screen_id);screen_info.id = static_cast<int64_t>(screen_id);snprintf(const_cast<char *>(screen_info.title), sizeof(screen_info.title), "Display %d",static_cast<int>(i));screen_info.rect = desktop_rect::make_xywh(screen_bounds.origin.x, screen_bounds.origin.y,screen_bounds.size.width, screen_bounds.size.height).to_traa_rect();if (thumbnail_size.width > 0 && thumbnail_size.height > 0) {NSImage *screen_image = [NSImage new];[screen_image addRepresentation:screen_image_rep];desktop_size scaled_size = calc_scaled_size(desktop_size(screen_image.size.width, screen_image.size.height), thumbnail_size);CGSize scaled_ns_size = CGSizeMake(scaled_size.width(), scaled_size.height());NSBitmapImageRep *scaled_image_rep = scale_image(screen_image_rep, scaled_ns_size);if (!scaled_image_rep) {continue;}size_t thumbnail_data_size = [scaled_image_rep pixelsWide] * [scaled_image_rep pixelsHigh] *[scaled_image_rep samplesPerPixel] * sizeof(unsigned char);screen_info.thumbnail_data = new uint8_t[thumbnail_data_size];screen_info.thumbnail_size = scaled_size.to_traa_size();if (!screen_info.thumbnail_data) {LOG_ERROR("alloc memory for thumbnail data failed");continue;}memcpy(const_cast<uint8_t *>(screen_info.thumbnail_data), [scaled_image_rep bitmapData],thumbnail_data_size);}infos.push_back(screen_info);}return TRAA_ERROR_NONE;
}
4. 枚举屏幕和窗口信息

最后,我们可以通过调用 enum_windowsenum_screens 函数来枚举当前系统中的窗口和屏幕信息:

int screen_source_info_enumerator::enum_screen_source_info(const traa_size icon_size,const traa_size thumbnail_size,const unsigned int external_flags,traa_screen_source_info **infos,int *count) {if (__builtin_available(macOS 11, *)) {if (!CGPreflightScreenCaptureAccess()) {CGRequestScreenCaptureAccess();return TRAA_ERROR_PERMISSION_DENIED;}}std::vector<traa_screen_source_info> sources;if (!(external_flags & TRAA_SCREEN_SOURCE_FLAG_IGNORE_WINDOW)) {enum_windows(icon_size, thumbnail_size, external_flags, sources);}if (!(external_flags & TRAA_SCREEN_SOURCE_FLAG_IGNORE_SCREEN)) {enum_screens(icon_size, thumbnail_size, external_flags, sources);}if (sources.size() == 0) {return traa_error::TRAA_ERROR_NONE;}*count = static_cast<int>(sources.size());*infos = reinterpret_cast<traa_screen_source_info *>(new traa_screen_source_info[sources.size()]);if (*infos == nullptr) {LOG_ERROR("alloca memroy for infos failed");return traa_error::TRAA_ERROR_OUT_OF_MEMORY;}for (size_t i = 0; i < sources.size(); ++i) {auto &source_info = sources[i];auto &dest_info = (*infos)[i];memcpy(&dest_info, &source_info, sizeof(traa_screen_source_info));if (std::strlen(source_info.title) > 0) {strncpy(const_cast<char *>(dest_info.title), source_info.title,std::min(sizeof(dest_info.title) - 1, std::strlen(source_info.title)));}if (std::strlen(source_info.process_path) > 0) {strncpy(const_cast<char *>(dest_info.process_path), source_info.process_path,std::min(sizeof(dest_info.process_path) - 1, std::strlen(source_info.process_path)));}}return TRAA_ERROR_NONE;
}

总结

通过上述步骤,我们可以在 macOS 下获取当前系统中可屏幕共享的窗口和屏幕信息,包括窗口和屏幕的 ID、标题、坐标、进程图标和缩略图等信息。希望这篇文章对您有所帮助。

源码传送

traa

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/456292.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[C++进阶数据结构]红黑树(半成品)

我们讲完了AVL树,它追求绝对平衡&#xff0c;从而导致插入和删除性能较差。今天我们来讲讲&#xff0c;红黑树&#xff0c;它是另一种平衡二叉搜索树&#xff0c;它追求相对平衡&#xff0c;使得增删查改的性能都极佳&#xff0c;时间复杂度皆为O(log2N)。 一、红黑树的概念 …

CSS3 动画相关属性实例大全(三)(columns、filter、flex、flex-basis 、flex-grow、flex-shrink属性)

CSS3 动画相关属性实例大全&#xff08;三) &#xff08;columns、filter、flex、flex-basis 、flex-grow、flex-shrink属性&#xff09; 本文目录&#xff1a; 一、columns属性&#xff08;设置元素的列宽和列数&#xff09; 二、filter属性&#xff08;调整图像、背景和边…

Ribbon客户端负载均衡策略测试及其改进

文章目录 一、目的概述二、验证步骤1、源码下载2、导入IDE3、运行前修改配置4、策略说明5、修改策略 三、最终结论四、改进措施1. 思路分析2. 核心代码3. 测试页面 一、目的概述 为了验证Ribbon客户端负载均衡策略在负载节点失效的情况下&#xff0c;是否具有故障转移的功能&a…

【逆向基础】十七、PE文件格式(二)

一、简介 本篇章主要PE文件组成部分中使用的结构体&#xff1b;根据结构体的成员变量去了解各个字节的含义。&#xff08;ps:我们依旧以”cmd.exe“为例展开解析&#xff1b;) 二、DOS Header 1、结构体&#xff1a;IMAGE_DOS_HEADER IMAGE_DOS_HEADER结构体的背景是为了兼…

忘记7-zip文件7-zip文件,还可以解压zip文件吗?

文件压缩与解压已成为我们日常处理数据和存储信息的常规操作。7-Zip&#xff0c;作为一款开源且功能强大的文件压缩工具&#xff0c;凭借其高压缩率、支持多种格式以及免费使用的特点&#xff0c;赢得了广大用户的青睐。然而&#xff0c;出于保护文件内容安全的考虑&#xff0c…

基于NVIDIA NIM平台—生成属于自己的DIY食谱

目录 一、介绍NVIDIA NIM平台 二、生成DIY食谱Demo 三、小结 一、介绍NVIDIA NIM平台 NVIDIA NIM&#xff08;Nvidia Inference Microservices&#xff09;平台是NVIDIA推出的一个微服务套件&#xff0c;旨在加速生成式AI模型在云端、数据中心和工作站上的部署和使用。以下是…

怎么区分主谓宾I love you与主系表I am fine? 去掉宾语看句子完整性 主系表结构则侧重于描述主语的状态、特征或性质

主谓宾与主系表是英语句子结构中的两种基本类型&#xff0c;它们在关注点、动词分类以及句子完整性方面有所区别。具体分析如下&#xff1a; 关注点 主谓宾I love you&#xff1a;主谓宾结构主要关注动作和影响对象之间的关系[1]。这种结构强调的是动态和行为&#xff0c;通常描…

4K双模显示器7款评测报告

4K双模显示器7款评测报告 HKC G27H7Pro 4K双模显示器 ROG华硕 XG27UCG 4K双模显示器 雷神 ZU27F160L 4K双模显示器 泰坦军团 P275MV PLUS 4K双模显示器 外星人&#xff08;Alienware&#xff09;AW2725QF 4K双模显示器 SANC盛色 D73uPro 4K双模显示器 ANTGAMER蚂蚁电竞 …

MySql中表的约束

​ 本篇中将会介绍关于 MySql 数据库中的表的约束&#xff0c;关于表的约束其实约束的是表中的数据类型&#xff0c;因为有的数据类型很单一&#xff0c;需要我们添加一些额外的约束&#xff0c;才能更好的保证数据的合法性&#xff0c;从业务逻辑角度保证数据的正确性&#xf…

Notepad++通过自定义语言实现日志按照不同级别高亮

借助Notepad的自定义语言可以实现日志的按照不同级别的高亮&#xff1b; 参考&#xff1a; https://blog.csdn.net/commshare/article/details/131208656 在此基础上做了一点修改效果如下&#xff1a; xml文件&#xff1a; <NotepadPlus><UserLang name"Ansibl…

leetCode算法题爬楼梯递归写法

题目&#xff1a; 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢&#xff1f; 示例 1&#xff1a; 输入&#xff1a;n 2输出&#xff1a;2解释&#xff1a;有两种方法可以爬到楼顶。1. 1 阶 1 阶2. 2 阶 …

GPIO输入和输出

参考视频&#xff1a;2.1 [GPIO]4种输出模式_哔哩哔哩_bilibili 输出&#xff1a;通过写0或者写1&#xff0c;控制引脚输出低电压或高电压。 输入&#xff1a;通过读取引脚是0还是1&#xff0c;判断引脚输入的是高电压还是低电压。 输出 推挽开漏通用通用输出推挽通用输出开漏…

Asp.net Core MVC 动态路由

动态路由 asp.net core 3.0 就支持了 // 映射关系public class TranslationDatabase{private static Dictionary<string, Dictionary<string, string>> Translations new Dictionary<string, Dictionary<string, string>>{{"en", new Dictio…

yolo自动化项目实例解析(八)自建UI-键鼠录制回放

项目中关于键鼠的操作&#xff0c;不像我们之前自动化那样一步一步去定义的&#xff0c;而是用C写了一个记录键鼠的操作&#xff0c;通过回放的方法来实现的 一、通讯系统 1、创建websocket服务器 首先通过事件循环asyncio 和websockets&#xff0c;创建一个持久化的服务端进程…

通过页面添加国际化数据,实现vue的国际化

element ui 写在前面1. 原有的vue的国际化处理1.1 语言文件1.2 lang的index.js1.3 入口文件导入1.3 应用 2. 通过页面添加国际化数据2.1 做法2.2 lang的index.js文件修改2.3 需要注意的点 总结写在最后 写在前面 需求&#xff1a;在系统的国际化管理页面添加国际化数据&#x…

我想电脑批量管理 30 台苹果手机,怎么操作更简单方便呢?

在如今的数字化时代&#xff0c;手机已经成为了我们日常生活中不可或缺的一部分。无论是工作还是娱乐&#xff0c;我们都需要使用各种各样的应用软件来满足自己的需求。 而对于那些需要管理大量苹果手机设备的企业来说&#xff0c;如何高效地完成这些任务就成了一个重要问题。…

三款计算服务器配置→如何选择科学计算服务器?

科学计算在众多领域都扮演着关键角色&#xff0c;无论是基础科学研究还是实际工程应用&#xff0c;强大的计算能力都是不可或缺的。而选择一台合适的科学计算服务器&#xff0c;对于确保科研和工作的顺利进行至关重要。 首先&#xff0c;明确自身需求是重中之重。要仔细考虑计算…

六个方向比较分析:ChatGPT-o1-preview与 ChatGPT-4o在论文写作辅助上的差异

学境思源&#xff0c;一键生成论文初稿&#xff1a; AcademicIdeas - 学境思源AI论文写作 在学术研究和论文撰写的领域&#xff0c;人工智能助手正变得越来越重要。随着技术的不断进步&#xff0c;ChatGPT-o1-preview和ChatGPT-4o作为两个先进的语言模型&#xff0c;在辅助论文…

文件上传漏洞及安全

文件上传 文件上传安全指的是攻击者通过利用上传实现后门的写入连接后门进行权限控制的安全问题&#xff0c;对于如何确保这类安全问题&#xff0c;一般会从原生态功能中的文件内容&#xff0c;文件后缀&#xff0c;文件类型等方面判断&#xff0c;但是漏洞可能不仅在本身的代码…

C++学习路线(二十二)

构造函数 构造函数作用 在创建一个新的对象时&#xff0c;自动调用的函数&#xff0c;用来进行“初始化”工作:对这个对象内部的数据成员进行初始化。 构造函数特点 1.自动调用(在创建新对象时&#xff0c;自动调用) 2.构造函数的函数名&#xff0c;和类名相同 3.构造函数…