Skip to content

Instantly share code, notes, and snippets.

@stephancasas
Last active June 16, 2024 09:51
Show Gist options
  • Save stephancasas/d230ac93d5bc1b0130555e2bc7203fce to your computer and use it in GitHub Desktop.
Save stephancasas/d230ac93d5bc1b0130555e2bc7203fce to your computer and use it in GitHub Desktop.
A convenience type for working with the CFDictionary values returned by CGWindowListCopyWindowInfo(_:, _:)
//
// CGWindowDictionary.swift
//
// Created by Stephan Casas on 11/2/23.
//
import Foundation;
import CoreGraphics;
import AppKit;
typealias CGWindowDictionary = [CFString: Any];
// MARK: - Utilities
/// A convenience type for working with the CFDictionary values returned by `CGWindowListCopyWindowInfo(_:, _:)`.
extension CGWindowDictionary {
/// Get a list of modernized window representations as `[CGWindowDictionary]` using
/// the given constraints.
/// - Parameters:
/// - option: The native window list filter option.
/// - window: The relative window when considering ordered window list filters.
/// - Returns: An array of `CGWindowDictionary` types which match the given parameters.
static func list(
matching option: CGWindowListOption = .optionAll,
relativeTo window: CGWindowID = kCGNullWindowID,
where isIncluded: ((CGWindowDictionary) -> Bool)? = nil
) -> [CGWindowDictionary] {
let windowList = CGWindowListCopyWindowInfo(
option, window
) as? [CGWindowDictionary] ?? [];
guard let isIncluded = isIncluded else {
return windowList;
}
return windowList.filter(isIncluded);
}
/// Get the frontmost window dictionary for the window which is both on-screen and the
/// frontmost window owned by the given process identifier.
/// - Parameter owner: The window-owning process for which the frontmost window should be resolved.
/// - Returns: The dictionary representation of the frontmost window of the given process.
static func frontmost(for owner: pid_t) -> CGWindowDictionary? {
var windowList = CGWindowDictionary.list(
matching: .optionOnScreenOnly,
where: { $0.processIdentifier == owner });
var frontmost: CGWindowDictionary?;
while windowList.count > 1 {
guard let _frontmost = windowList.first else {
return frontmost;
}
frontmost = _frontmost;
windowList = CGWindowDictionary.list(
matching: .optionOnScreenAboveWindow,
relativeTo: _frontmost.id,
where: { $0.processIdentifier == owner });
}
return frontmost;
}
}
// MARK: - Typecast Properties
extension CGWindowDictionary {
/// The window server-tracked window ID for this window.
var id: CGWindowID {
self[kCGWindowNumber] as! CGWindowID
}
/// The process name for the process which owns this window.
var processName: String {
self[kCGWindowOwnerName] as! String;
}
/// The process identifier for the process which owns this window.
var processIdentifier: pid_t {
self[kCGWindowOwnerPID] as! pid_t
}
/// The title of this window.
var name: String {
(self[kCGWindowName] as? String) ?? ""
}
/// The window's bounds in the Quartz/CoreGraphics coordinate space — where `(0, 0)` is at the leftmost-lowermost point of the display which owns the space with the lowest ordered index value.
var bounds: CGRect {
.init(dictionaryRepresentation: self[kCGWindowBounds] as! CFDictionary)!
}
/// The layer at which this window draws.
var layer: Int32 {
self[kCGWindowLayer] as! Int32
}
/// The window's bounds in the AppKit coordinate space — where `(0, 0)` is at the leftmost-uppermost point of the display which owns the space with the lowest ordered index value.
var appKitBounds: CGRect {
guard
let NSCGSWindow = objc_getClass(
"NSCGSWindow") as? AnyClass,
let convertRectFromCGCoordinatesPtr = class_getClassMethod(
NSCGSWindow, Selector(("convertRectFromCGCoordinates:")))
else { fatalError(
"Could not load private class method +[NSCGSWindow convertRectFromCGCoordinates:]."
) }
return unsafeBitCast(
method_getImplementation(convertRectFromCGCoordinatesPtr),
to: (@convention(c) (CGRect) -> CGRect).self
)(self.bounds);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment