import AppKit final class OverlayWindow: NSPanel { init(screen: NSScreen, onComplete: @escaping (NSRect) -> Void, onCancel: @escaping () -> Void, onWindowPick: @escaping (NSRect) -> Void) { let content = OverlayView(onComplete: onComplete, onCancel: onCancel, onWindowPick: onWindowPick) super.init( contentRect: screen.frame, styleMask: [.borderless, .nonactivatingPanel], backing: .buffered, defer: false ) contentView = content level = .screenSaver collectionBehavior = [.canJoinAllSpaces, .fullScreenAuxiliary, .stationary, .transient] backgroundColor = .clear isOpaque = false hasShadow = false isReleasedWhenClosed = false acceptsMouseMovedEvents = true } override var canBecomeKey: Bool { true } } final class OverlayView: NSView { private let onComplete: (NSRect) -> Void private let onCancel: () -> Void private let onWindowPick: (NSRect) -> Void private var startPoint: NSPoint? private(set) var selection: NSRect? private var isDragging = false // Window detection private var trackedWindows: [(id: CGWindowID, frame: NSRect)] = [] private var hoveredWindowFrame: NSRect? private var excludedIDs: [Int] = [] init(onComplete: @escaping (NSRect) -> Void, onCancel: @escaping () -> Void, onWindowPick: @escaping (NSRect) -> Void) { self.onComplete = onComplete self.onCancel = onCancel self.onWindowPick = onWindowPick super.init(frame: .zero) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } override var acceptsFirstResponder: Bool { true } override func acceptsFirstMouse(for event: NSEvent?) -> Bool { true } override func resetCursorRects() { addCursorRect(bounds, cursor: .crosshair) } // MARK: - Window detection /// Rebuild the list of capturable windows intersecting this screen. /// Called lazily on mouse moves; cheap enough for interactive hover. private func refreshTrackedWindows() { guard let window = window else { return } let screenFrame = window.convertToScreen(NSRect(origin: .zero, size: bounds.size)) // Exclude this overlay's own window (the other overlays don't intersect // this screen, so the on-screen filter handles them). let ownID = window.windowNumber guard let list = CGWindowListCopyWindowInfo( [ .optionOnScreenOnly, .excludeDesktopElements ], kCGNullWindowID ) as? [[String: Any]] else { trackedWindows = []; return } var found: [(CGWindowID, NSRect)] = [] for info in list { guard let cid = info[kCGWindowNumber as String] as? CGWindowID, let bounds = info[kCGWindowBounds as String] as? [String: CGFloat], let layer = info[kCGWindowLayer as String] as? Int, layer == 0 else { continue } // Skip our own overlays if Int(cid) == ownID { continue } let rect = NSRect( x: bounds["X"] ?? 0, y: bounds["Y"] ?? 0, width: bounds["Width"] ?? 0, height: bounds["Height"] ?? 0 ) guard rect.width >= 80, rect.height >= 60 else { continue } // Skip tiny/utility windows guard rect.width < screenFrame.width || rect.height < screenFrame.height else { continue } // Only windows intersecting this screen guard rect.intersects(screenFrame) else { continue } found.append((cid, rect)) } trackedWindows = found } private func windowFrame(at point: NSPoint) -> NSRect? { guard let window = window else { return nil } // convertToScreen gives AppKit-global coordinates; convert tracked // CG frames to the same space and pick the frontmost containing it. let screenPoint = window.convertToScreen(NSRect(origin: point, size: .zero)).origin for (_, frame) in trackedWindows { if Self.cgToAppKit(rect: frame).contains(screenPoint) { return frame } } return nil } /// Convert a CGWindowList (top-left origin, CG global space) frame into /// AppKit screen coordinates (bottom-left origin) for capture. private static func cgToAppKit(rect: NSRect) -> NSRect { guard let mainScreen = NSScreen.screens.first else { return rect } let y = mainScreen.frame.height - rect.origin.y - rect.height return NSRect(x: rect.origin.x, y: y, width: rect.width, height: rect.height) } // MARK: - Events override func mouseMoved(with event: NSEvent) { guard !isDragging else { return } refreshTrackedWindows() let p = convert(event.locationInWindow, from: nil) let frame = windowFrame(at: p) if frame != hoveredWindowFrame { hoveredWindowFrame = frame needsDisplay = true } } override func mouseDown(with event: NSEvent) { let p = convert(event.locationInWindow, from: nil) startPoint = p selection = NSRect(origin: p, size: .zero) needsDisplay = true } override func mouseDragged(with event: NSEvent) { guard let start = startPoint else { return } isDragging = true hoveredWindowFrame = nil let p = convert(event.locationInWindow, from: nil) selection = NSRect( x: min(start.x, p.x), y: min(start.y, p.y), width: abs(p.x - start.x), height: abs(p.y - start.y) ) needsDisplay = true } override func mouseUp(with event: NSEvent) { if isDragging { guard let sel = selection else { return } if sel.width >= 2, sel.height >= 2 { onComplete(window!.convertToScreen(sel)) } else { onCancel() } return } // No drag: it was a click. If a window is hovered, capture it with // rounded corners; otherwise treat as an empty marquee (cancel). if let frame = hoveredWindowFrame { onWindowPick(Self.cgToAppKit(rect: frame)) } else { onCancel() } } override func keyDown(with event: NSEvent) { if event.keyCode == 53 { onCancel() } else { super.keyDown(with: event) } } override func draw(_ dirtyRect: NSRect) { NSColor.black.withAlphaComponent(0.35).setFill() NSBezierPath(rect: bounds).fill() // Highlight the hovered window (rounded, like the real window corners) if let frame = hoveredWindowFrame { // Convert the CG frame to AppKit-global, then to this view's space. guard let window = window else { return } let screenFrame = window.convertToScreen(NSRect(origin: .zero, size: bounds.size)) let appKitFrame = Self.cgToAppKit(rect: frame) let viewFrame = NSRect( x: appKitFrame.origin.x - screenFrame.origin.x, y: appKitFrame.origin.y - screenFrame.origin.y, width: frame.width, height: frame.height ) NSGraphicsContext.current?.saveGraphicsState() NSGraphicsContext.current?.compositingOperation = .clear NSColor.black.setFill() NSBezierPath(roundedRect: viewFrame, xRadius: 10, yRadius: 10).fill() NSGraphicsContext.current?.restoreGraphicsState() let border = NSBezierPath(roundedRect: viewFrame, xRadius: 10, yRadius: 10) border.lineWidth = 2 NSColor.controlAccentColor.setStroke() border.stroke() return } guard let sel = selection, sel.width > 0, sel.height > 0 else { return } NSGraphicsContext.current?.saveGraphicsState() NSGraphicsContext.current?.compositingOperation = .clear NSColor.black.setFill() NSBezierPath(rect: sel).fill() NSGraphicsContext.current?.restoreGraphicsState() let border = NSBezierPath(rect: sel) border.lineWidth = 1.5 NSColor.controlAccentColor.setStroke() border.stroke() let label = "\(Int(sel.width)) × \(Int(sel.height))" let attrs: [NSAttributedString.Key: Any] = [ .font: NSFont.monospacedDigitSystemFont(ofSize: 12, weight: .medium), .foregroundColor: NSColor.white, ] let labelSize = label.size(withAttributes: attrs) let labelRect = NSRect( x: sel.maxX - labelSize.width - 8, y: sel.minY - labelSize.height - 10, width: labelSize.width + 8, height: labelSize.height + 6 ) NSColor.black.withAlphaComponent(0.6).setFill() NSBezierPath(roundedRect: labelRect, xRadius: 4, yRadius: 4).fill() label.draw( at: NSPoint(x: labelRect.minX + 4, y: labelRect.minY + 3), withAttributes: attrs ) } }