概要
Macの背景画像をどうしても抜いてきたい状況に使えるコード.CGWindowの座標系とNSScreenの座標系が異なるため,マルチディスプレイ対応するのは困難だった.
ソース
Extension
import Cocoa
extension NSScreen {
static var mainHeight: CGFloat {
return main?.frame.height ?? CGFloat.zero
}
static var totalRect: CGRect {
return screens.reduce(CGRect.zero) { (result, screen) -> CGRect in
return result.union(screen.frame)
}
}
}
extension NSImage {
static func background(_ frame: CGRect) -> NSImage? {
guard var list = CGWindowListCopyWindowInfo(.optionOnScreenOnly, kCGNullWindowID) as? [NSDictionary] else {
return nil
}
let origin = CGPoint(x: frame.minX, y: NSScreen.mainHeight - frame.maxY)
list = list.compactMap({ (dict) -> NSDictionary? in
guard let name = dict[kCGWindowName] as? String, name.contains("Desktop Picture") else { return nil }
let bounds = dict[kCGWindowBounds] as! NSDictionary
guard CGPoint(x: bounds["X"] as! CGFloat, y: bounds["Y"] as! CGFloat).equalTo(origin) else { return nil }
return dict
})
guard
let dict = list.first, let id = dict[kCGWindowNumber] as? CGWindowID,
let cgImage = CGWindowListCreateImage(CGRect.null, .optionIncludingWindow, id, .boundsIgnoreFraming)
else { return nil }
return NSImage(cgImage: cgImage, size: frame.size)
}
}
使用例
func getMainScreenBackground() -> NSImage? {
guard let screen = NSScreen.main else { return nil }
return NSImage.background(screen.frame)
}
func getAllScreenBackgrounds() -> [NSImage] {
return NSScreen.screens.compactMap { (screen) -> NSImage? in
return NSImage.background(screen.frame)
}
}
おまけ
CGWindowの座標系とNSScreenの座標系とCGImageの座標系の差.
座標変換がかなり厄介なことがわかっていただけると思う.
さらにもう一歩先へ
もしも動作環境にApp Sand Boxが必要ないのであれば,
let url = NSWorkspace.shared.desktopImageURL(for: NSScreen.main!)!
let image = NSImage(contentsOf: url)
このようにNSWorkspaceを経由して画像のソースを直接取得できる.