Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,12 +118,12 @@ After Gatekeeper allows the app to open, continue with Screen Recording permissi
Rsnap currently relies on **Screen Recording** permission to capture other apps/windows.
- ScreenCaptureKit live sampling on macOS requires macOS 12.3+ and Screen Recording permission.
- Normal region/window/monitor capture does not require Accessibility or Input Monitoring.
- Scroll capture is available from dragged-region freezes on the current native host and uses
Screen Recording-backed screenshots plus forwarded wheel input.
- The retained scroll-capture path uses Screen Recording-backed screenshots plus forwarded wheel
input, but the v0.1.2 native-host release does not expose scroll capture in the toolbar.
- macOS may describe Screen Recording as `Screen & System Audio Recording` or as direct screen/audio access when Rsnap bypasses the system picker.
- The native menubar host exposes `Permissions…`, which shows Screen Recording, Accessibility, and Input Monitoring status. It marks Accessibility and Input Monitoring as diagnostic/not required for the current native host.
- Settings -> Permissions shows Screen Recording as the only required permission.
- Normal native capture depends on Screen Recording; if access is missing, Rsnap opens the Screen Recording page in System Settings and shows a floating drag-to-grant guide.
- You can reopen `Permissions…` from the tray or menubar menu at any time.
- You can reopen the Permissions section from `Settings…` in the tray or menubar menu at any time.
- Base capture path: `System Settings` -> `Privacy & Security` -> `Screen Recording`.
- Enable `Rsnap.app`, then retry capture. If macOS still keeps capture blocked after changing a permission, relaunch the app.

Expand Down
8 changes: 0 additions & 8 deletions native/macos-host/Sources/RsnapHostBridge/HostFFI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -229,8 +229,6 @@ public enum HostRequest: Equatable, Sendable {
case saveCapture
case recognizeText
case requestScreenRecordingPermission
case requestAccessibilityPermission
case requestInputMonitoringPermission
}

public enum HostEffectKind: UInt32, Equatable, Sendable {
Expand All @@ -241,8 +239,6 @@ public enum HostEffectKind: UInt32, Equatable, Sendable {

public enum PermissionKind: UInt32, Equatable, Sendable {
case screenRecording = 0
case accessibility = 1
case inputMonitoring = 2
}

public enum HostEvent: Sendable {
Expand Down Expand Up @@ -601,10 +597,6 @@ public final class RsnapHostSession {
return .recognizeText
case RSNAP_HOST_REQUEST_REQUEST_SCREEN_RECORDING_PERMISSION.rawValue:
return .requestScreenRecordingPermission
case RSNAP_HOST_REQUEST_REQUEST_ACCESSIBILITY_PERMISSION.rawValue:
return .requestAccessibilityPermission
case RSNAP_HOST_REQUEST_REQUEST_INPUT_MONITORING_PERMISSION.rawValue:
return .requestInputMonitoringPermission
case RSNAP_HOST_REQUEST_START_SCROLL_CAPTURE.rawValue:
return .startScrollCapture
default:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,11 @@ final class GlobalHotKeyCenter {
var onCaptureRequested: (() -> Void)?
var onCancelRequested: (() -> Void)?
var onToggleLoupeRequested: (() -> Void)?
var onCopyRequested: (() -> Void)?
var onAutoCenterRequested: (() -> Void)?
var onSaveRequested: (() -> Void)?

private var handlerRef: EventHandlerRef?
private var hotKeyRefs: [Binding: EventHotKeyRef?] = [:]
private var registeredDefinitions: [Binding: HotKeyDefinition] = [:]
private var plainFrozenLocalMonitor: Any?
private var plainFrozenGlobalMonitor: Any?

init() {
var eventType = EventTypeSpec(
Expand All @@ -55,7 +51,6 @@ final class GlobalHotKeyCenter {
}

func invalidate() {
removePlainFrozenShortcutMonitors()
for binding in Binding.allCases {
unregister(binding)
}
Expand All @@ -67,8 +62,7 @@ final class GlobalHotKeyCenter {

func updateBindings(
captureHotKey: String,
sceneMode: SceneKind,
plainFrozenShortcutsEnabled: Bool
sceneMode: SceneKind
) -> Bool {
var allRequestedBindingsRegistered = true
let captureDefinition = Self.parseCaptureHotKey(captureHotKey) ?? Self.defaultCaptureHotKey
Expand All @@ -95,12 +89,6 @@ final class GlobalHotKeyCenter {
unregister(.loupe)
}

if wantsFrozen && plainFrozenShortcutsEnabled {
installPlainFrozenShortcutMonitors()
} else {
removePlainFrozenShortcutMonitors()
}

if wantsFrozen {
allRequestedBindingsRegistered =
register(.save, definition: HotKeyDefinition(keyCode: 1, modifiers: UInt32(cmdKey)))
Expand Down Expand Up @@ -189,62 +177,6 @@ final class GlobalHotKeyCenter {
return noErr
}

private func installPlainFrozenShortcutMonitors() {
guard plainFrozenLocalMonitor == nil, plainFrozenGlobalMonitor == nil else {
return
}
plainFrozenLocalMonitor = NSEvent.addLocalMonitorForEvents(matching: .keyDown) {
[weak self] event in
self?.handlePlainFrozenShortcut(event) == true ? nil : event
}
plainFrozenGlobalMonitor = NSEvent.addGlobalMonitorForEvents(matching: .keyDown) {
[weak self] event in
DispatchQueue.main.async {
_ = self?.handlePlainFrozenShortcut(event)
}
}
}

private func removePlainFrozenShortcutMonitors() {
if let monitor = plainFrozenLocalMonitor {
NSEvent.removeMonitor(monitor)
plainFrozenLocalMonitor = nil
}
if let monitor = plainFrozenGlobalMonitor {
NSEvent.removeMonitor(monitor)
plainFrozenGlobalMonitor = nil
}
}

private func handlePlainFrozenShortcut(_ event: NSEvent) -> Bool {
guard !event.isARepeat else {
return false
}
let flags = event.modifierFlags.intersection(.deviceIndependentFlagsMask)
guard !flags.contains(.command), !flags.contains(.control), !flags.contains(.option),
!flags.contains(.shift)
else {
return false
}
switch event.keyCode {
case 49:
NativeHostTelemetry.lifecycleEvent(
"native_host.plain_frozen_hotkey",
detail: "keyCode=49,action=copy"
)
onCopyRequested?()
case 8:
NativeHostTelemetry.lifecycleEvent(
"native_host.plain_frozen_hotkey",
detail: "keyCode=8,action=auto_center"
)
onAutoCenterRequested?()
default:
return false
}
return true
}

private static let defaultCaptureHotKey = HotKeyDefinition(
keyCode: 7,
modifiers: UInt32(optionKey)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ final class HotKeyBindingCoordinator {
private struct BindingState: Equatable {
let captureHotKey: String
let sceneMode: SceneKind
let plainFrozenShortcutsEnabled: Bool
}

var onCaptureRequested: (() -> Void)? {
Expand All @@ -23,16 +22,6 @@ final class HotKeyBindingCoordinator {
set { hotKeys.onToggleLoupeRequested = newValue }
}

var onCopyRequested: (() -> Void)? {
get { hotKeys.onCopyRequested }
set { hotKeys.onCopyRequested = newValue }
}

var onAutoCenterRequested: (() -> Void)? {
get { hotKeys.onAutoCenterRequested }
set { hotKeys.onAutoCenterRequested = newValue }
}

var onSaveRequested: (() -> Void)? {
get { hotKeys.onSaveRequested }
set { hotKeys.onSaveRequested = newValue }
Expand All @@ -43,21 +32,18 @@ final class HotKeyBindingCoordinator {

func update(
captureHotKey: String,
sceneMode: SceneKind,
plainFrozenShortcutsEnabled: Bool
sceneMode: SceneKind
) {
let state = BindingState(
captureHotKey: captureHotKey,
sceneMode: sceneMode,
plainFrozenShortcutsEnabled: plainFrozenShortcutsEnabled
sceneMode: sceneMode
)
guard state != appliedState else {
return
}
let didApply = hotKeys.updateBindings(
captureHotKey: state.captureHotKey,
sceneMode: state.sceneMode,
plainFrozenShortcutsEnabled: state.plainFrozenShortcutsEnabled
sceneMode: state.sceneMode
)
appliedState = didApply ? state : nil
}
Expand Down
Loading
Loading