diff --git a/Sources/Kapture/CaptureController.swift b/Sources/Kapture/CaptureController.swift
index 7c5d81f..b828a87 100644
--- a/Sources/Kapture/CaptureController.swift
+++ b/Sources/Kapture/CaptureController.swift
@@ -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()
diff --git a/build-app.sh b/build-app.sh
index ff2c134..af2e2d5 100755
--- a/build-app.sh
+++ b/build-app.sh
@@ -108,9 +108,9 @@ cat > "$APP/Contents/Info.plist" <<'PLIST'
CFBundleIconFile
Icon
CFBundleShortVersionString
- 1.8.1
+ 1.8.2
CFBundleVersion
- 81
+ 82
LSUIElement
NSHighResolutionCapable