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

news2024/10/25 5:05:01

在 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 = [NSRunningApplication
        runningApplicationWithProcessIdentifier:((__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.coloradmin.cn/o/2222974.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

54万字WORD电力数字化转型智慧电力一体化监管云平台整体解决方案

▲关注智慧方案文库&#xff0c;学习9000多份最新解决方案&#xff0c;其中 PPT、WORD超过7000多份 &#xff0c;覆盖智慧城市多数领域的深度知识社区&#xff0c;稳定更新4年&#xff0c;日积月累&#xff0c;更懂行业需求。 1459页54万字WORD丨电力行业数字化转型 智慧电力…

【ArcGIS Pro实操第4期】绘制三维地图

【ArcGIS Pro实操第4期】绘制三维地图 ArcGIS Pro绘制三维地图-以DEM高程为例参考 如何使用ArcGIS Pro将栅格数据用三维的形式进行表达&#xff1f;在ArcGIS里可以使用ArcScene来实现&#xff0c;ArcGIS Pro实现原理跟ArcScene一致。由于Esri未来将不再对ArcGIS更新&#xff0c…

53页 PPT煤炭行业数字化转型规划方案

▲关注智慧方案文库&#xff0c;学习9000多份最新解决方案&#xff0c;其中 PPT、WORD超过7000多份 &#xff0c;覆盖智慧城市多数领域的深度知识社区&#xff0c;稳定更新4年&#xff0c;日积月累&#xff0c;更懂行业需求。 53页 PPT煤炭行业数字化转型规划方案 通过对煤企高…

手机玩使命召唤21:黑色行动6?GameViewer远程玩使命召唤教程

使命召唤21&#xff1a;黑色行动 6这个第一人称射击游戏&#xff0c;将于10月25号上线&#xff01;如果你是使命召唤的老玩家&#xff0c;是不是也在期待这部新作&#xff1f;其实这个游戏不仅可以用电脑玩&#xff0c;还可以用手机玩&#xff0c;使用网易GameViewer远程就能让…

【Qt6聊天室项目】 主界面功能实现

1. 获取当前用户的个人信息 1.1 前后端逻辑分析&#xff08;主界面功能&#xff09; 主界面上所有的前后端交互逻辑相同&#xff0c;分析到加载会话列表后其余功能仅实现。 核心逻辑总结 异步请求-响应模型 客户端发起请求&#xff0c;向服务器发送包含会话ID的请求服务端处…

python画图|曲线动态输出

【1】引言 前序教程中的曲线动态输出&#xff0c;其实是把曲线按照左右移动的形式输出&#xff08;波的传递形式&#xff09;。 python画图|曲线动态输出基础教程_python 动态曲线-CSDN博客 但有些时候我们更期待的是曲线不移动&#xff0c;随着自变量的增加而输出因变量&am…

信号与系统学习:傅里叶级数

一、基本概念 1. 什么是傅里叶级数&#xff1f; 傅里叶级数是一种数学工具&#xff0c;可以将一个周期函数分解为一系列正弦和余弦函数&#xff08;即三角函数&#xff09;的和。这些正弦和余弦函数的频率是原函数的整数倍。 2. 为什么要使用傅里叶级数&#xff1f; 信号分…

【STM32+HAL】OV2640捕获图像显示

一、准备工作 有关CUBEMX的初始化配置&#xff0c;参见我的另一篇blog&#xff1a;【STM32HAL】CUBEMX初始化配置 二、所用工具 1、芯片&#xff1a; STM32F407ZGT6 2、IDE&#xff1a; MDK-Keil软件 3、库文件&#xff1a;STM32F4xxHAL库 三、实现功能 通过OV2640捕获图像…

Flutter UI组件库(JUI)

Flutter UI组件库 (JUI) 介绍 您是否正在寻找一种方法来简化Flutter开发过程&#xff0c;并创建美观、一致的用户界面&#xff1f;您的搜索到此为止&#xff01;我们的Flutter UI组件库&#xff08;JUI&#xff09;提供了广泛的预构建、可自定义组件&#xff0c;帮助您快速构建…

为什么会配置足够打LOL等网游很卡?12代大小核处理器最典型

卡顿原因及优化建议 大小核调度问题&#xff1a; 调度不当&#xff1a;某些游戏未针对大小核进行优化&#xff0c;可能导致系统将负载分配到效率核&#xff08;小核&#xff09;&#xff0c;而性能核&#xff08;大核&#xff09;未被充分利用。操作系统调度策略&#xff1a;尽…

15.6 JDBC数据库编程6——可滚动和可更新的ResultSet

目录 15.6 引言 15.6.1 可滚动的ResultSet 15.6.1 可更新的ResultSet 15.6 引言 可滚动的ResultSet是指在结果集对象上不但可以向前访问结果集中的记录&#xff0c;还可以向后访问结果集中记录。可更新的ResultSet是指不但可以访问结果集中的记录&#xff0c;还可以更新…

文件操作(1) —— 文件基础知识

目录 1. 为什么使用文件&#xff1f; 2. 文件种类【按功能分】 3. 文件名 4. 数据文件种类【按存储方式细分】 5. 文件的打开和关闭 5.1 流和标准流 5.2 文件指针 5.3 文件的打开和关闭函数 6. 文件缓冲区 1. 为什么使用文件&#xff1f; 如果没有⽂件&#xff0c;我…

Vue笔记-浏览器窗口改变时,重新计算表格高度并设置

当窗口大小改变时&#xff0c;你监听 window 对象的 resize 事件&#xff0c;然后在事件处理程序中重新计算表格的高度。在 Vue 中&#xff0c;可以在组件中通过 created 生命周期钩子来添加事件监听器&#xff0c;然后在组件销毁时移除事件监听器。 如下vue代码&#xff1a; …

Android GPU Inspector分析帧数据快速入门

使用 谷歌官方工具Android GPU Inspector (AGI) 可以对Android 应用进行深入和全面的系统性能分析和帧性能分析 。AGI 是一个非常强大的分析工具&#xff0c;尤其是在需要诊断 GPU 性能问题和优化应用时&#xff0c;可以帮助你精准找到性能瓶颈。本文介绍如何使用该工具对帧数据…

24V转3.3V2A同步降压WT6030

24V转3.3V2A同步降压WT6030 WT6030 是一种高效同步整流降压开关模式转换器&#xff0c;集成内部功率 MOSFET&#xff0c;能在宽输入电源范围内提供较高的输出电流&#xff0c;以下是使用 WT6030 将 24V 降压到 3.3V 输出 2A 电流的相关设计要点&#xff1a; 1. 电路设计 输入电…

零基础Java第九期:一维数组(二)和二维数组

目录 一、数组的练习 1.1. 顺序表查找 1.2. 二分查找 1.3. 冒泡排序 二、二维数组 2.1. 二维数组的性质 2.2. 不规则二维数组 一、数组的练习 1.1. 顺序表查找 题目描述&#xff1a;给定一个数组, 再给定一个元素, 找出该元素在数组中的位置。 利用for循环去遍历数组&am…

听一听语音助手的声音

分享自制树莓派语音助手的博文也有一些日子了&#xff0c;今天咱们来听听语音助手自己的声音。 上图是本次对话的log记录&#xff0c;从图上可以看到&#xff0c;主要的对话耗时是用于录音&#xff08;默认5秒&#xff09;和语音识别&#xff08;平均5秒&#xff09;这两个组件…

【数据结构】包装类简单认识泛型-Java

包装类 在Java中&#xff0c;由于基本类型不是继承自Object&#xff0c;为了在泛型代码中可以支持基本类型&#xff0c;Java给每个基本类型都给了一个包装类型 基本数据类型和对应的包装类 基本数据类型包装类ByteByteshortShortint Integer longLongfloatFloatdoubleDoublec…

洞察前沿趋势!2024深圳国际金融科技大赛——西丽湖金融科技大学生挑战赛技术公开课指南

在当前信息技术与“互联网”深度融合的背景下&#xff0c;金融行业的转型升级是热门话题&#xff0c;创新与发展成为金融科技主旋律。随着区块链技术、人工智能技术、5G通信技术、大数据技术等前沿科技的飞速发展&#xff0c;它们与金融领域的深度融合&#xff0c;正引领着新型…

模型的部署:服务端与客户端建立连接(Flask)

目录 一、服务端部署&#xff08;使用Flask&#xff09; 1.安装Flask 2.加载模型&#xff08;这里以识别图片的类型模型为例&#xff09; 3.定义API端点 4.运行Flask应用 二、客户端请求 1.安装HTTP客户端库 2.发送请求 请求成功示例&#xff1a; 监控与日志 总结 在…