Compare commits

...
4 Commits
Author SHA1 Message Date
TZGyn 83b127cf44 Detect maximized windows on secondary displays
A leftover heuristic skipped any window matching its screen's dimensions,
which made window detection silently fail on secondary monitors where the
working window is typically maximized.

💘 Generated with Crush

Assisted-by: Crush:deepseek-v4-flash-0731
2026-09-09 14:03:52 +08:00
TZGyn 8332661504 Guard against event-tap teardown crashes; log result-window display
A tap-disabled callback force-unwrapped the tap port, which could crash
the app mid-capture (no result window, dead hotkey). Guard it and log
re-enables. Result window display is now logged with the image size.

💘 Generated with Crush

Assisted-by: Crush:deepseek-v4-flash-0731
2026-09-09 12:29:53 +08:00
TZGyn 832a18b06c Exclude the cursor from captures
💘 Generated with Crush

Assisted-by: Crush:deepseek-v4-flash-0731
2026-09-09 12:26:12 +08:00
TZGyn 6e5796e16d Check Accessibility before starting a capture; log capture geometry
The first capture used to start the overlay and the system permission
prompt at once, making both uninteractive. begin() now requires
Accessibility up front, shows an alert with a direct link to the setting,
and only starts capturing once granted. Captures log their display,
backing scale, and rect mapping for diagnosing multi-display offsets.

💘 Generated with Crush

Assisted-by: Crush:deepseek-v4-flash-0731
2026-09-09 12:24:47 +08:00
2 changed files with 11 additions and 13 deletions
+9 -11
View File
@@ -233,11 +233,6 @@ final class CaptureController: NSObject {
) )
guard cgRect.width >= 80, cgRect.height >= 60 else { continue } guard cgRect.width >= 80, cgRect.height >= 60 else { continue }
let rect = Self.cgToAppKit(rect: cgRect) let rect = Self.cgToAppKit(rect: cgRect)
// Skip windows filling an entire screen (fullscreen apps)
let coversWholeScreen = NSScreen.screens.contains { screen in
abs(screen.frame.width - rect.width) < 2 && abs(screen.frame.height - rect.height) < 2
}
guard !coversWholeScreen else { continue }
found.append((cid, rect)) found.append((cid, rect))
} }
trackedWindows = found trackedWindows = found
@@ -265,11 +260,12 @@ final class CaptureController: NSObject {
guard isSelecting else { return event } guard isSelecting else { return event }
switch type { switch type {
case .tapDisabledByUserInput: case .tapDisabledByUserInput, .tapDisabledByTimeout:
CGEvent.tapEnable(tap: eventTap!, enable: true) // A timed-out tap must be re-enabled; guard against a torn-down tap.
return event if let tap = eventTap {
case .tapDisabledByTimeout: CGEvent.tapEnable(tap: tap, enable: true)
CGEvent.tapEnable(tap: eventTap!, enable: true) CaptureController.log("tap: re-enabled after \(type)")
}
return event return event
case .keyDown: case .keyDown:
if event.getIntegerValueField(.keyboardEventKeycode) == 53 { if event.getIntegerValueField(.keyboardEventKeycode) == 53 {
@@ -342,6 +338,7 @@ final class CaptureController: NSObject {
ResultWindow.shared.show(image: ResultWindow.failureImage("Capture failed. Check Screen Recording permission.")) ResultWindow.shared.show(image: ResultWindow.failureImage("Capture failed. Check Screen Recording permission."))
return return
} }
CaptureController.log("capture done: \(image.width)x\(image.height) — showing result window")
ResultWindow.shared.show(image: image) ResultWindow.shared.show(image: image)
} }
} }
@@ -440,7 +437,7 @@ final class ScreenCapture {
let backingScale = screen.backingScaleFactor let backingScale = screen.backingScaleFactor
config.width = Int(CGFloat(display.width) * backingScale) config.width = Int(CGFloat(display.width) * backingScale)
config.height = Int(CGFloat(display.height) * backingScale) config.height = Int(CGFloat(display.height) * backingScale)
config.showsCursor = true config.showsCursor = false
// ScreenCaptureKit uses a different origin than AppKit for // ScreenCaptureKit uses a different origin than AppKit for
// screens stacked around the main display: per-display, // screens stacked around the main display: per-display,
@@ -456,6 +453,7 @@ final class ScreenCapture {
completion(nil) completion(nil)
return return
} }
CaptureController.log("capture: rect=\(rect) screen=\(screen.frame) display=\(display.frame) backing=\(backingScale) image=\(image.width)x\(image.height) scRect=\(scRect)")
completion(self.crop(image, to: scRect, display: display, roundedCorners: roundedCorners)) completion(self.crop(image, to: scRect, display: display, roundedCorners: roundedCorners))
} }
} else { } else {
+2 -2
View File
@@ -108,9 +108,9 @@ cat > "$APP/Contents/Info.plist" <<'PLIST'
<key>CFBundleIconFile</key> <key>CFBundleIconFile</key>
<string>Icon</string> <string>Icon</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>1.7.0</string> <string>1.7.4</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>70</string> <string>74</string>
<key>LSUIElement</key> <key>LSUIElement</key>
<true/> <true/>
<key>NSHighResolutionCapable</key> <key>NSHighResolutionCapable</key>