Use screen crop for owned popup windows

desktopIndependentWindow composites owned popups together with their
parent window and scales the group to the popup's size. Detect the case
at pick time — another window from the same app overlapping the picked
one — and capture the screen crop with a rounded mask instead, which
contains only the popup.

💘 Generated with Crush

Assisted-by: Crush:deepseek-v4-flash-0731
This commit is contained in:
2026-09-09 15:05:34 +08:00
parent 265571d7e2
commit 3078713f8f
2 changed files with 22 additions and 11 deletions
+20 -9
View File
@@ -14,9 +14,9 @@ final class CaptureController: NSObject {
// Selection state (driven by the event tap; AppKit global coordinates)
private var marqueeStart: NSPoint?
private(set) var marqueeRect: NSRect?
private(set) var hoverWindow: (id: CGWindowID, frame: NSRect)?
private(set) var hoverWindow: (id: CGWindowID, frame: NSRect, pid: Int)?
var hoverFrame: NSRect? { hoverWindow?.frame }
private var trackedWindows: [(id: CGWindowID, frame: NSRect)] = []
private var trackedWindows: [(id: CGWindowID, frame: NSRect, pid: Int)] = []
private var lastWindowScan = Date.distantPast
private var eventTap: CFMachPort?
@@ -105,13 +105,17 @@ final class CaptureController: NSObject {
/// A window was picked from hover — capture it directly (real rounded
/// corners come from the window's own alpha).
private func finishWindowPick(_ windowID: CGWindowID, _ rect: NSRect) {
private func finishWindowPick(_ windowID: CGWindowID, _ rect: NSRect, ownedRisk: Bool) {
let ids = overlayIDs
hideOverlays()
isSelecting = false
restorePreviousApp()
DispatchQueue.main.async { [self] in
capture(rect, excludingWindowIDs: ids, windowID: windowID)
if ownedRisk {
capture(rect, excludingWindowIDs: ids, roundedCorners: true)
} else {
capture(rect, excludingWindowIDs: ids, windowID: windowID)
}
}
}
@@ -219,11 +223,12 @@ final class CaptureController: NSObject {
) as? [[String: Any]] else { trackedWindows = []; return }
let ownIDs = Set(overlayIDs)
var found: [(id: CGWindowID, frame: NSRect)] = []
var found: [(id: CGWindowID, frame: NSRect, pid: Int)] = []
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,
let pid = info[kCGWindowOwnerPID as String] as? Int,
layer == 0
else { continue }
if ownIDs.contains(Int(cid)) { continue }
@@ -235,7 +240,7 @@ final class CaptureController: NSObject {
)
guard cgRect.width >= 80, cgRect.height >= 60 else { continue }
let rect = Self.cgToAppKit(rect: cgRect)
found.append((cid, rect))
found.append((id: cid, frame: rect, pid: pid))
}
trackedWindows = found
}
@@ -248,7 +253,7 @@ final class CaptureController: NSObject {
return NSRect(x: rect.origin.x, y: y, width: rect.width, height: rect.height)
}
private func topmostWindowContaining(_ appKitPoint: NSPoint) -> (id: CGWindowID, frame: NSRect)? {
private func topmostWindowContaining(_ appKitPoint: NSPoint) -> (id: CGWindowID, frame: NSRect, pid: Int)? {
for entry in trackedWindows {
if entry.frame.contains(appKitPoint) {
return entry
@@ -321,8 +326,14 @@ final class CaptureController: NSObject {
CaptureController.log("tap: marquee complete \(rect)")
finishSelection(rect)
} else if let hover = hoverWindow {
CaptureController.log("tap: window pick id=\(hover.id) \(hover.frame)")
finishWindowPick(hover.id, hover.frame)
// An overlapping window from the same app means this is an
// owned popup — desktopIndependentWindow would composite the
// parent too, so capture the screen crop instead.
let ownedRisk = trackedWindows.contains {
$0.id != hover.id && $0.pid == hover.pid && $0.frame.intersects(hover.frame)
}
CaptureController.log("tap: window pick id=\(hover.id) pid=\(hover.pid) ownedRisk=\(ownedRisk) \(hover.frame)")
finishWindowPick(hover.id, hover.frame, ownedRisk: ownedRisk)
} else {
CaptureController.log("tap: click with no hover/marquee — cancel")
cancelSelection()
+2 -2
View File
@@ -108,9 +108,9 @@ cat > "$APP/Contents/Info.plist" <<'PLIST'
<key>CFBundleIconFile</key>
<string>Icon</string>
<key>CFBundleShortVersionString</key>
<string>1.8.1</string>
<string>1.8.2</string>
<key>CFBundleVersion</key>
<string>81</string>
<string>82</string>
<key>LSUIElement</key>
<true/>
<key>NSHighResolutionCapable</key>