DockDoor项目中的窗口图像缓存机制解析
【免费下载链接】DockDoor Window peeking for macOS 项目地址: https://gitcode.com/gh_mirrors/do/DockDoor
DockDoor作为一款macOS上的Dock预览和窗口管理工具,其核心功能依赖于高效的窗口图像缓存机制。本文将深入解析DockDoor项目中窗口图像缓存的设计原理、实现细节和优化策略。
缓存架构概览
DockDoor的窗口图像缓存采用分层架构,主要由以下几个核心组件构成:
核心数据结构:WindowInfo
WindowInfo结构体是缓存机制的基础单元,包含了窗口的所有必要信息:
struct WindowInfo: Identifiable, Hashable {
let id: CGWindowID
let windowProvider: WindowPropertiesProviding
let app: NSRunningApplication
var windowName: String?
var image: CGImage?
var axElement: AXUIElement
var appAxElement: AXUIElement
var closeButton: AXUIElement?
var isMinimized: Bool
var isHidden: Bool
var lastAccessedTime: Date
}
关键字段说明
| 字段 | 类型 | 描述 |
|---|---|---|
id | CGWindowID | 窗口的唯一标识符 |
image | CGImage? | 缓存的窗口图像 |
lastAccessedTime | Date | 最后访问时间,用于缓存失效策略 |
isMinimized | Bool | 窗口是否最小化 |
isHidden | Bool | 窗口是否隐藏 |
缓存管理:SpaceWindowCacheManager
SpaceWindowCacheManager是缓存的核心管理器,负责窗口数据的存储、检索和更新:
线程安全设计
class SpaceWindowCacheManager {
private var windowCache: [pid_t: Set<WindowInfo>] = [:]
private let cacheLock = NSLock()
func readCache(pid: pid_t) -> Set<WindowInfo> {
cacheLock.lock()
defer { cacheLock.unlock() }
return windowCache[pid] ?? []
}
}
采用NSLock确保多线程环境下的数据一致性,每个操作都通过锁机制保护。
缓存更新策略
图像捕获与缓存流程
图像捕获机制
DockDoor使用ScreenCaptureKit和CGWindowListCreateImage两种方式捕获窗口图像:
static func captureWindowImage(window: SCWindow, forceRefresh: Bool = false) async throws -> CGImage {
// 检查缓存
if !forceRefresh {
if let cachedImage = getCachedImage(window: window) {
return cachedImage
}
}
// 使用ScreenCaptureKit或CGWindowListCreateImage捕获图像
var cgImage: CGImage
if forceRefresh {
cgImage = try captureWithCGSHWCaptureWindowList(window)
} else {
cgImage = try captureWithCGWindowListCreateImage(window)
}
// 图像缩放优化
let previewScale = Int(Defaults[.windowPreviewImageScale])
if previewScale > 1 {
cgImage = scaleImage(cgImage, scale: previewScale)
}
return cgImage
}
缓存生命周期管理
DockDoor实现了智能的缓存失效策略:
// 检查缓存是否过期
let cacheLifespan = Defaults[.screenCaptureCacheLifespan]
if Date().timeIntervalSince(cachedWindow.lastAccessedTime) <= cacheLifespan {
return cachedImage
}
并发处理与性能优化
有限并发任务组
let group = LimitedTaskGroup<Void>(maxConcurrentTasks: 4)
for window in content.windows {
await group.addTask {
try await captureAndCacheWindowInfo(window: window, app: nsApp)
}
}
窗口过滤机制
DockDoor实现了多层过滤策略:
| 过滤类型 | 实现方式 | 目的 |
|---|---|---|
| 系统窗口过滤 | filteredBundleIdentifiers | 过滤桌面小部件等系统窗口 |
| 应用名称过滤 | appNameFilters | 用户自定义应用过滤 |
| 窗口标题过滤 | windowTitleFilters | 用户自定义窗口标题过滤 |
| 尺寸过滤 | width >= 100, height >= 100 | 过滤过小窗口 |
缓存净化与维护
定期缓存清理
static func purifyAppCache(with pid: pid_t, removeAll: Bool) async -> Set<WindowInfo>? {
if removeAll {
desktopSpaceWindowCacheManager.writeCache(pid: pid, windowSet: [])
return nil
} else {
let existingWindowsSet = desktopSpaceWindowCacheManager.readCache(pid: pid)
var purifiedSet = existingWindowsSet
for window in existingWindowsSet {
if !isValidElement(window.axElement) {
purifiedSet.remove(window)
desktopSpaceWindowCacheManager.removeFromCache(pid: pid, windowId: window.id)
}
}
return purifiedSet
}
}
有效性验证
static func isValidElement(_ element: AXUIElement) -> Bool {
do {
let position = try element.position()
let size = try element.size()
return position != nil && size != nil
} catch {
return false
}
}
性能优化策略
1. 图像缩放优化
let previewScale = Int(Defaults[.windowPreviewImageScale])
if previewScale > 1 {
let newWidth = Int(cgImage.width) / previewScale
let newHeight = Int(cgImage.height) / previewScale
// 使用高质量插值进行缩放
context.interpolationQuality = .high
context.draw(cgImage, in: CGRect(x: 0, y: 0, width: newWidth, height: newHeight))
}
2. 智能重试机制
let maxRetries = 3
var retryCount = 0
while retryCount < maxRetries {
if attemptActivation() {
return
}
retryCount += 1
if retryCount < maxRetries {
usleep(50000) // 50ms延迟
}
}
3. 去抖动处理
private let debounceDelay: TimeInterval = 0.1
private var debounceWorkItem: DispatchWorkItem?
func showWindow(...) {
debounceWorkItem?.cancel()
let workItem = DispatchWorkItem { /* 实际显示逻辑 */ }
if let lastShowTime, now.timeIntervalSince(lastShowTime) < debounceDelay {
debounceWorkItem = workItem
DispatchQueue.main.asyncAfter(deadline: .now() + debounceDelay, execute: workItem)
}
}
实际应用场景
1. Dock预览场景
2. Alt+Tab窗口切换
static func getAllWindowsOfAllApps() -> [WindowInfo] {
let windows = desktopSpaceWindowCacheManager.getAllWindows()
let filteredWindows = !Defaults[.includeHiddenWindowsInSwitcher]
? windows.filter { !$0.isHidden && !$0.isMinimized }
: windows
if Defaults[.limitSwitcherToFrontmostApp] {
return getWindowsForFrontmostApp(from: filteredWindows)
}
return filteredWindows
}
总结与最佳实践
DockDoor的窗口图像缓存机制体现了多个优秀的设计原则:
设计亮点
- 线程安全:使用NSLock确保多线程环境下的数据一致性
- 内存效率:按应用PID分组存储,减少内存碎片
- 性能优化:并发捕获、图像缩放、去抖动处理
- 智能失效:基于时间和窗口状态的双重失效策略
- 可扩展性:易于添加新的过滤规则和优化策略
性能指标
| 指标 | 数值 | 说明 |
|---|---|---|
| 最大并发任务数 | 4 | 平衡性能与资源消耗 |
| 默认缓存寿命 | 可配置 | 用户可根据需求调整 |
| 重试次数 | 3 | 确保操作成功率 |
| 去抖动延迟 | 100ms | 优化用户体验 |
DockDoor的缓存机制不仅提供了流畅的用户体验,还为开发者提供了一个优秀的参考实现,展示了如何在macOS环境下高效管理窗口图像缓存。
【免费下载链接】DockDoor Window peeking for macOS 项目地址: https://gitcode.com/gh_mirrors/do/DockDoor
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



