From 265571d7e2830de22f94c391db99dd921f956a23 Mon Sep 17 00:00:00 2001 From: TZGyn Date: Wed, 9 Sep 2026 14:55:40 +0800 Subject: [PATCH] Reject window captures that composite extra windows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ScreenCaptureKit's desktopIndependentWindow filter can include owned child windows (e.g. an app dialog plus its parent), producing a capture much larger than the picked window. Detect the overflow by comparing the returned image size against the window's frame and fall back to the screen crop with rounded mask. Hover changes are logged with the picked window id and tracked count for diagnosis. 💘 Generated with Crush Assisted-by: Crush:deepseek-v4-flash-0731 --- Sources/Kapture/CaptureController.swift | 23 ++++++++++++++++++++--- build-app.sh | 4 ++-- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/Sources/Kapture/CaptureController.swift b/Sources/Kapture/CaptureController.swift index fa8557f..7c5d81f 100644 --- a/Sources/Kapture/CaptureController.swift +++ b/Sources/Kapture/CaptureController.swift @@ -287,6 +287,11 @@ final class CaptureController: NSObject { let window = topmostWindowContaining(appKitPoint) if window?.frame != hoverFrame { hoverWindow = window + if let w = window { + CaptureController.log("hover: picked id=\(w.id) frame=\(w.frame) (tracked=\(trackedWindows.count))") + } else { + CaptureController.log("hover: none (tracked=\(trackedWindows.count))") + } renderOverlays() } return event @@ -463,9 +468,21 @@ final class ScreenCapture { contentFilter: SCContentFilter(desktopIndependentWindow: scWindow), configuration: windowConfig ) { - CaptureController.log("capture: WINDOW id=\(windowID) image=\(windowImage.width)x\(windowImage.height) (native alpha corners)") - completion(windowImage) - return + let expectedW = CGFloat(scWindow.frame.width) * backingScale + let expectedH = CGFloat(scWindow.frame.height) * backingScale + let overflow = max( + CGFloat(windowImage.width) / max(expectedW, 1), + CGFloat(windowImage.height) / max(expectedH, 1) + ) + if overflow > 1.35 { + // SCKit composited extra windows (owned popups etc.) + // — reject and use the screen crop instead. + CaptureController.log("capture: WINDOW id=\(windowID) overflow=\(overflow) (\(windowImage.width)x\(windowImage.height) vs \(Int(expectedW))x\(Int(expectedH))) — rejecting, using crop+mask") + } else { + CaptureController.log("capture: WINDOW id=\(windowID) image=\(windowImage.width)x\(windowImage.height) (native alpha corners)") + completion(windowImage) + return + } } CaptureController.log("capture: window filter failed for id=\(windowID) — falling back to crop+mask") } diff --git a/build-app.sh b/build-app.sh index a108ddd..ff2c134 100755 --- a/build-app.sh +++ b/build-app.sh @@ -108,9 +108,9 @@ cat > "$APP/Contents/Info.plist" <<'PLIST' CFBundleIconFile Icon CFBundleShortVersionString - 1.8.0 + 1.8.1 CFBundleVersion - 80 + 81 LSUIElement NSHighResolutionCapable