From 307d1ed9caac96c9688234693db02cdeac2018d7 Mon Sep 17 00:00:00 2001 From: Yvette Carlisle Date: Thu, 7 May 2026 14:25:56 +0800 Subject: [PATCH] {"schema":"maestro/commit/1","summary":"Trim native permissions to screen recording","authority":"manual"} --- README.md | 8 +- .../Sources/RsnapHostBridge/HostFFI.swift | 8 - .../GlobalHotKeyCenter.swift | 70 +------- .../HotKeyBindingCoordinator.swift | 20 +-- .../RsnapNativeHostKit/NativeHostApp.swift | 159 ++---------------- .../RsnapNativeHostKit/NativeHostPanels.swift | 50 +----- .../NativeHostSettingsView.swift | 9 +- .../PermissionRecoveryGuideWindow.swift | 11 +- packages/rsnap-capture-core/src/protocol.rs | 4 - packages/rsnap-capture-core/src/session.rs | 2 - .../rsnap-host-ffi/include/rsnap_host_ffi.h | 6 +- packages/rsnap-host-ffi/src/lib.rs | 28 +-- 12 files changed, 38 insertions(+), 337 deletions(-) diff --git a/README.md b/README.md index 526c03d4..4198012d 100644 --- a/README.md +++ b/README.md @@ -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. diff --git a/native/macos-host/Sources/RsnapHostBridge/HostFFI.swift b/native/macos-host/Sources/RsnapHostBridge/HostFFI.swift index b51092ef..c2b14334 100644 --- a/native/macos-host/Sources/RsnapHostBridge/HostFFI.swift +++ b/native/macos-host/Sources/RsnapHostBridge/HostFFI.swift @@ -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 { @@ -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 { @@ -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: diff --git a/native/macos-host/Sources/RsnapNativeHostKit/GlobalHotKeyCenter.swift b/native/macos-host/Sources/RsnapNativeHostKit/GlobalHotKeyCenter.swift index a32b875b..5163dd8e 100644 --- a/native/macos-host/Sources/RsnapNativeHostKit/GlobalHotKeyCenter.swift +++ b/native/macos-host/Sources/RsnapNativeHostKit/GlobalHotKeyCenter.swift @@ -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( @@ -55,7 +51,6 @@ final class GlobalHotKeyCenter { } func invalidate() { - removePlainFrozenShortcutMonitors() for binding in Binding.allCases { unregister(binding) } @@ -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 @@ -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))) @@ -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) diff --git a/native/macos-host/Sources/RsnapNativeHostKit/HotKeyBindingCoordinator.swift b/native/macos-host/Sources/RsnapNativeHostKit/HotKeyBindingCoordinator.swift index 8eedfd37..ae35c389 100644 --- a/native/macos-host/Sources/RsnapNativeHostKit/HotKeyBindingCoordinator.swift +++ b/native/macos-host/Sources/RsnapNativeHostKit/HotKeyBindingCoordinator.swift @@ -5,7 +5,6 @@ final class HotKeyBindingCoordinator { private struct BindingState: Equatable { let captureHotKey: String let sceneMode: SceneKind - let plainFrozenShortcutsEnabled: Bool } var onCaptureRequested: (() -> Void)? { @@ -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 } @@ -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 } diff --git a/native/macos-host/Sources/RsnapNativeHostKit/NativeHostApp.swift b/native/macos-host/Sources/RsnapNativeHostKit/NativeHostApp.swift index d8edcfbc..5f924c15 100644 --- a/native/macos-host/Sources/RsnapNativeHostKit/NativeHostApp.swift +++ b/native/macos-host/Sources/RsnapNativeHostKit/NativeHostApp.swift @@ -449,7 +449,7 @@ public final class NativeHostApplicationController: NSObject, NSApplicationDeleg source: String, oncePerLaunch: Bool = false ) -> Bool { - guard !NativePermissions.status(for: .screenRecording) else { + guard !NativePermissions.screenRecordingGranted else { permissionRecoveryWindowController.close() return false } @@ -459,7 +459,7 @@ public final class NativeHostApplicationController: NSObject, NSApplicationDeleg } didPresentLaunchPermissionOnboarding = true } - permissionRecoveryWindowController.present(kind: .screenRecording) + permissionRecoveryWindowController.present() NativeHostTelemetry.lifecycleEvent( "native_host.permission_recovery_presented", detail: "source=\(source)" @@ -542,12 +542,6 @@ public final class NativeHostApplicationController: NSObject, NSApplicationDeleg hotKeyCoordinator.onToggleLoupeRequested = { [weak self] in self?.sessionController.toggleLoupe() } - hotKeyCoordinator.onCopyRequested = { [weak self] in - self?.sessionController.copySelection() - } - hotKeyCoordinator.onAutoCenterRequested = { [weak self] in - self?.sessionController.performFrozenAutoCenter() - } hotKeyCoordinator.onSaveRequested = { [weak self] in self?.sessionController.saveSelection() } @@ -571,8 +565,7 @@ public final class NativeHostApplicationController: NSObject, NSApplicationDeleg private func refreshHotKeyBindings(for mode: SceneKind) { hotKeyCoordinator.update( captureHotKey: settingsStore.settings.captureHotkey, - sceneMode: mode, - plainFrozenShortcutsEnabled: sessionController.plainFrozenShortcutHotkeysEnabled + sceneMode: mode ) } @@ -746,7 +739,7 @@ final class CaptureSessionController: NSObject { } return } - guard NativePermissions.status(for: .screenRecording) else { + guard NativePermissions.screenRecordingGranted else { return } frozenFrameAuthority.refreshShareableContentCache( @@ -770,7 +763,7 @@ final class CaptureSessionController: NSObject { ) -> LiveChromeSample? { let warmStartedAt = ProcessInfo.processInfo.systemUptime let screenCount = NSScreen.screens.count - guard NativePermissions.status(for: .screenRecording) else { + guard NativePermissions.screenRecordingGranted else { NativeHostTelemetry.liveSamplingWarmTiming( captureID: captureID, source: source, @@ -972,11 +965,10 @@ final class CaptureSessionController: NSObject { } private func ensureCapturePermissions() -> Bool { - let granted = NativePermissions.status(for: .screenRecording) - guard !granted else { + guard !NativePermissions.screenRecordingGranted else { return true } - return NativePermissions.request(.screenRecording) + return NativePermissions.requestScreenRecording() } func backgroundPatch(in rect: CGRect) -> CGImage? { @@ -1774,39 +1766,11 @@ final class CaptureSessionController: NSObject { case .recognizeText: try performRecognizeText() case .requestScreenRecordingPermission: - let granted = NativePermissions.request(.screenRecording) + let granted = NativePermissions.requestScreenRecording() try session?.send(report: .permissionChanged(.screenRecording, granted: granted)) if !granted { try sendHostStatusMessage("Screen recording permission is required.") } - case .requestAccessibilityPermission: - guard NativePermissions.requiredForCurrentNativeHost(.accessibility) else { - try session?.send( - report: .permissionChanged( - .accessibility, granted: NativePermissions.status(for: .accessibility))) - try sendHostStatusMessage( - "Accessibility is not required by the current native host.") - return - } - let granted = NativePermissions.request(.accessibility) - try session?.send(report: .permissionChanged(.accessibility, granted: granted)) - if !granted { - try sendHostStatusMessage("Accessibility permission is required.") - } - case .requestInputMonitoringPermission: - guard NativePermissions.requiredForCurrentNativeHost(.inputMonitoring) else { - try session?.send( - report: .permissionChanged( - .inputMonitoring, granted: NativePermissions.status(for: .inputMonitoring))) - try sendHostStatusMessage( - "Input Monitoring is not required by the current native host.") - return - } - let granted = NativePermissions.request(.inputMonitoring) - try session?.send(report: .permissionChanged(.inputMonitoring, granted: granted)) - if !granted { - try sendHostStatusMessage("Input monitoring permission is required.") - } } } @@ -2089,12 +2053,6 @@ final class CaptureSessionController: NSObject { && currentFrozenSelection() != nil } - var plainFrozenShortcutHotkeysEnabled: Bool { - scene.mode == .frozen - && scrollCaptureState == nil - && chromeState.frozenOverlay.activeTextEdit == nil - } - func handleScrollCaptureWheel(_ event: NSEvent, at point: CGPoint) -> Bool { guard Self.scrollCaptureEnabled else { return false @@ -4413,9 +4371,6 @@ final class CaptureHostView: NSView { private var liveDragExceededThreshold = false private var livePrimaryCompletionInFlight = false private var liveMouseUpMonitor: Any? - private var liveGlobalMouseUpMonitor: Any? - private var liveMouseUpEventTap: CFMachPort? - private var liveMouseUpEventTapRunLoopSource: CFRunLoopSource? private var liveMouseReleaseWatchdog: DispatchWorkItem? private var livePointerPreviewGlobal: CGPoint? private var livePointerPreviewInputUptime: TimeInterval? @@ -5360,21 +5315,9 @@ final class CaptureHostView: NSView { removeLiveMouseUpMonitor() liveMouseUpMonitor = NSEvent.addLocalMonitorForEvents(matching: [.leftMouseUp]) { [weak self] event in - self?.completeLivePrimaryInteractionFromMouseUp(event, source: "local") + self?.completeLivePrimaryInteractionFromMouseUp(event) return event } - liveGlobalMouseUpMonitor = NSEvent.addGlobalMonitorForEvents(matching: [.leftMouseUp]) { - [weak self] event in - let point = NSEvent.mouseLocation - DispatchQueue.main.async { - self?.completeLivePrimaryInteractionFromMouseUp( - event, - source: "global", - fallbackPoint: point - ) - } - } - installLiveMouseUpEventTap() } private func removeLiveMouseUpMonitor() { @@ -5383,21 +5326,12 @@ final class CaptureHostView: NSView { NSEvent.removeMonitor(liveMouseUpMonitor) self.liveMouseUpMonitor = nil } - if let liveGlobalMouseUpMonitor { - NSEvent.removeMonitor(liveGlobalMouseUpMonitor) - self.liveGlobalMouseUpMonitor = nil - } - removeLiveMouseUpEventTap() } - private func completeLivePrimaryInteractionFromMouseUp( - _ event: NSEvent, - source: String, - fallbackPoint: CGPoint? = nil - ) { + private func completeLivePrimaryInteractionFromMouseUp(_ event: NSEvent) { completeLivePrimaryInteractionFromSystemMouseUp( - at: fallbackPoint ?? globalPoint(fromAnyEvent: event), - source: source + at: globalPoint(from: event), + source: "local" ) } @@ -5423,68 +5357,6 @@ final class CaptureHostView: NSView { ) } - private func installLiveMouseUpEventTap() { - guard liveMouseUpEventTap == nil else { - return - } - let mask = CGEventMask(1 << CGEventType.leftMouseUp.rawValue) - let refcon = UnsafeMutableRawPointer(Unmanaged.passUnretained(self).toOpaque()) - guard - let eventTap = CGEvent.tapCreate( - tap: .cgSessionEventTap, - place: .headInsertEventTap, - options: .listenOnly, - eventsOfInterest: mask, - callback: Self.liveMouseUpEventTapCallback, - userInfo: refcon - ), - let source = CFMachPortCreateRunLoopSource(nil, eventTap, 0) - else { - NativeHostTelemetry.captureEvent( - "capture.live_primary_mouse_up_event_tap", - captureID: controller?.activeTelemetryCaptureID ?? 0, - outcome: "unavailable" - ) - return - } - liveMouseUpEventTap = eventTap - liveMouseUpEventTapRunLoopSource = source - CFRunLoopAddSource(CFRunLoopGetMain(), source, .commonModes) - CGEvent.tapEnable(tap: eventTap, enable: true) - } - - private func removeLiveMouseUpEventTap() { - if let source = liveMouseUpEventTapRunLoopSource { - CFRunLoopRemoveSource(CFRunLoopGetMain(), source, .commonModes) - liveMouseUpEventTapRunLoopSource = nil - } - if let eventTap = liveMouseUpEventTap { - CFMachPortInvalidate(eventTap) - liveMouseUpEventTap = nil - } - } - - private static let liveMouseUpEventTapCallback: CGEventTapCallBack = { - _, type, event, userInfo in - guard type == .leftMouseUp, let userInfo else { - return Unmanaged.passUnretained(event) - } - let view = Unmanaged.fromOpaque(userInfo).takeUnretainedValue() - let point = view.appKitPoint(fromQuartzPoint: event.location) - DispatchQueue.main.async { - view.completeLivePrimaryInteractionFromSystemMouseUp( - at: point, - source: "event_tap" - ) - } - return Unmanaged.passUnretained(event) - } - - private func appKitPoint(fromQuartzPoint point: CGPoint) -> CGPoint { - let desktopFrame = CaptureOverlayController.desktopFrame - return CGPoint(x: point.x, y: desktopFrame.maxY - point.y) - } - private func installLiveMouseReleaseWatchdog() { cancelLiveMouseReleaseWatchdog() scheduleLiveMouseReleaseWatchdog() @@ -5795,13 +5667,6 @@ final class CaptureHostView: NSView { return window.convertPoint(toScreen: event.locationInWindow) } - private func globalPoint(fromAnyEvent event: NSEvent) -> CGPoint { - guard let eventWindow = event.window else { - return NSEvent.mouseLocation - } - return eventWindow.convertPoint(toScreen: event.locationInWindow) - } - private func currentGlobalMousePoint() -> CGPoint? { guard let window else { return NSEvent.mouseLocation diff --git a/native/macos-host/Sources/RsnapNativeHostKit/NativeHostPanels.swift b/native/macos-host/Sources/RsnapNativeHostKit/NativeHostPanels.swift index 6abffb06..4b7cde71 100644 --- a/native/macos-host/Sources/RsnapNativeHostKit/NativeHostPanels.swift +++ b/native/macos-host/Sources/RsnapNativeHostKit/NativeHostPanels.swift @@ -1,5 +1,4 @@ import AppKit -import ApplicationServices import CoreGraphics import Foundation import RsnapHostBridge @@ -130,56 +129,21 @@ final class SettingsWindowController: NSWindowController, NSWindowDelegate { @MainActor enum NativePermissions { - static func requiredForCurrentNativeHost(_ kind: PermissionKind) -> Bool { - switch kind { - case .screenRecording: - return true - case .accessibility, .inputMonitoring: - return false - } + static var screenRecordingGranted: Bool { + CGPreflightScreenCaptureAccess() } - static func status(for kind: PermissionKind) -> Bool { - switch kind { - case .screenRecording: - return CGPreflightScreenCaptureAccess() - case .accessibility: - return AXIsProcessTrusted() - case .inputMonitoring: - return CGPreflightListenEventAccess() - } - } - - static func request(_ kind: PermissionKind) -> Bool { - let granted: Bool - switch kind { - case .screenRecording: - granted = CGPreflightScreenCaptureAccess() || CGRequestScreenCaptureAccess() - case .accessibility: - let promptKey = "AXTrustedCheckOptionPrompt" - let options = [promptKey: true] as CFDictionary - granted = AXIsProcessTrustedWithOptions(options) - case .inputMonitoring: - granted = CGPreflightListenEventAccess() || CGRequestListenEventAccess() - } + static func requestScreenRecording() -> Bool { + let granted = screenRecordingGranted || CGRequestScreenCaptureAccess() if !granted { - openSystemSettings(for: kind) + openScreenRecordingSettings() } return granted } @discardableResult - static func openSystemSettings(for kind: PermissionKind) -> Bool { - let privacyQuery: String - switch kind { - case .screenRecording: - privacyQuery = "Privacy_ScreenCapture" - case .accessibility: - privacyQuery = "Privacy_Accessibility" - case .inputMonitoring: - privacyQuery = "Privacy_ListenEvent" - } - + static func openScreenRecordingSettings() -> Bool { + let privacyQuery = "Privacy_ScreenCapture" let modernURLString = "x-apple.systempreferences:com.apple.settings.PrivacySecurity.extension?\(privacyQuery)" if let modernURL = URL(string: modernURLString), NSWorkspace.shared.open(modernURL) { diff --git a/native/macos-host/Sources/RsnapNativeHostKit/NativeHostSettingsView.swift b/native/macos-host/Sources/RsnapNativeHostKit/NativeHostSettingsView.swift index d636a9b8..866f22f6 100644 --- a/native/macos-host/Sources/RsnapNativeHostKit/NativeHostSettingsView.swift +++ b/native/macos-host/Sources/RsnapNativeHostKit/NativeHostSettingsView.swift @@ -498,7 +498,7 @@ private struct OutputInspector: View { private struct PermissionsInspector: View { var body: some View { - let granted = NativePermissions.status(for: .screenRecording) ? 1 : 0 + let granted = NativePermissions.screenRecordingGranted ? 1 : 0 VStack(alignment: .leading, spacing: 12) { PermissionProgressBadge(granted: granted, total: 1) @@ -1459,18 +1459,16 @@ private struct CaptureHotKeyField: View { private struct PermissionsSettingsPanel: View { @ObservedObject var model: NativeHostSettingsViewModel @State private var refreshID = 0 - private let primaryKind = PermissionKind.screenRecording var body: some View { VStack(spacing: 8) { VStack(spacing: 0) { PermissionGrantCard( - kind: primaryKind, refreshID: refreshID, bundleURL: Self.appBundleURL, appIcon: Self.appIcon, openSettings: { - NativePermissions.openSystemSettings(for: primaryKind) + NativePermissions.openScreenRecordingSettings() }, refresh: { refreshID += 1 @@ -1499,7 +1497,6 @@ private struct PermissionsSettingsPanel: View { } private struct PermissionGrantCard: View { - let kind: PermissionKind let refreshID: Int let bundleURL: URL let appIcon: NSImage @@ -1580,7 +1577,7 @@ private struct PermissionGrantCard: View { private var isGranted: Bool { _ = refreshID - return NativePermissions.status(for: kind) + return NativePermissions.screenRecordingGranted } private var title: String { diff --git a/native/macos-host/Sources/RsnapNativeHostKit/PermissionRecoveryGuideWindow.swift b/native/macos-host/Sources/RsnapNativeHostKit/PermissionRecoveryGuideWindow.swift index e105c0cb..d16b380c 100644 --- a/native/macos-host/Sources/RsnapNativeHostKit/PermissionRecoveryGuideWindow.swift +++ b/native/macos-host/Sources/RsnapNativeHostKit/PermissionRecoveryGuideWindow.swift @@ -1,6 +1,5 @@ import AppKit import CoreGraphics -import RsnapHostBridge import SwiftUI @MainActor @@ -22,7 +21,6 @@ final class PermissionRecoveryGuideWindowController: NSWindowController { private static let windowSize = NSSize(width: 318, height: 50) private static let cornerRadius: CGFloat = 17 private static let windowGap: CGFloat = 14 - private var kind: PermissionKind = .screenRecording private var positionWorkItem: DispatchWorkItem? private var statusPollWorkItem: DispatchWorkItem? private var guideDirection: GuideDirection = .left @@ -55,9 +53,8 @@ final class PermissionRecoveryGuideWindowController: NSWindowController { fatalError("init(coder:) has not been implemented") } - func present(kind: PermissionKind) { - self.kind = kind - NativePermissions.openSystemSettings(for: kind) + func present() { + NativePermissions.openScreenRecordingSettings() updateRootView() window?.orderOut(nil) scheduleSystemSettingsPositioning() @@ -83,7 +80,7 @@ final class PermissionRecoveryGuideWindowController: NSWindowController { guard let self else { return } - NativePermissions.openSystemSettings(for: self.kind) + NativePermissions.openScreenRecordingSettings() self.scheduleSystemSettingsPositioning() } ) @@ -177,7 +174,7 @@ final class PermissionRecoveryGuideWindowController: NSWindowController { } private func pollPermissionStatus(remainingAttempts: Int) { - if NativePermissions.status(for: kind) { + if NativePermissions.screenRecordingGranted { close() return } diff --git a/packages/rsnap-capture-core/src/protocol.rs b/packages/rsnap-capture-core/src/protocol.rs index 3de48ff7..6d9edc18 100644 --- a/packages/rsnap-capture-core/src/protocol.rs +++ b/packages/rsnap-capture-core/src/protocol.rs @@ -71,10 +71,6 @@ pub enum CursorIntent { pub enum PermissionKind { /// Screen recording or equivalent display-capture access. ScreenRecording, - /// Accessibility APIs for synthetic input or focused automation. - Accessibility, - /// Input-monitoring or global event tap access. - InputMonitoring, } /// Host-owned effect surface that remains outside the Rust core. diff --git a/packages/rsnap-capture-core/src/session.rs b/packages/rsnap-capture-core/src/session.rs index 007397f5..2c8a04ca 100644 --- a/packages/rsnap-capture-core/src/session.rs +++ b/packages/rsnap-capture-core/src/session.rs @@ -173,8 +173,6 @@ impl CaptureSessionCore { HostReport::PermissionChanged { kind, granted } => { let permission = match kind { PermissionKind::ScreenRecording => "Screen Recording", - PermissionKind::Accessibility => "Accessibility", - PermissionKind::InputMonitoring => "Input Monitoring", }; let verb = if granted { "granted" } else { "revoked" }; diff --git a/packages/rsnap-host-ffi/include/rsnap_host_ffi.h b/packages/rsnap-host-ffi/include/rsnap_host_ffi.h index f0126958..79e065c0 100644 --- a/packages/rsnap-host-ffi/include/rsnap_host_ffi.h +++ b/packages/rsnap-host-ffi/include/rsnap_host_ffi.h @@ -8,7 +8,7 @@ extern "C" { #endif -#define RSNAP_HOST_FFI_ABI_VERSION 17u +#define RSNAP_HOST_FFI_ABI_VERSION 18u #define RSNAP_TOOLBAR_ITEM_CAPACITY 16u #define RSNAP_STATUS_MESSAGE_CAPACITY 256u #define RSNAP_LIVE_SAMPLE_PATCH_CAPACITY 4096u @@ -131,8 +131,6 @@ typedef enum RsnapHostEffectKind { typedef enum RsnapPermissionKind { RSNAP_PERMISSION_SCREEN_RECORDING = 0, - RSNAP_PERMISSION_ACCESSIBILITY = 1, - RSNAP_PERMISSION_INPUT_MONITORING = 2, } RsnapPermissionKind; typedef struct RsnapHostReport { @@ -205,8 +203,6 @@ typedef enum RsnapHostRequestKind { RSNAP_HOST_REQUEST_SAVE_CAPTURE = 4, RSNAP_HOST_REQUEST_RECOGNIZE_TEXT = 5, RSNAP_HOST_REQUEST_REQUEST_SCREEN_RECORDING_PERMISSION = 6, - RSNAP_HOST_REQUEST_REQUEST_ACCESSIBILITY_PERMISSION = 7, - RSNAP_HOST_REQUEST_REQUEST_INPUT_MONITORING_PERMISSION = 8, RSNAP_HOST_REQUEST_START_SCROLL_CAPTURE = 9, } RsnapHostRequestKind; diff --git a/packages/rsnap-host-ffi/src/lib.rs b/packages/rsnap-host-ffi/src/lib.rs index ca1b14c0..f230e568 100644 --- a/packages/rsnap-host-ffi/src/lib.rs +++ b/packages/rsnap-host-ffi/src/lib.rs @@ -24,7 +24,7 @@ use rsnap_overlay::scroll_stitching::{ }; /// ABI version exported by the thin C host bridge. -pub const RSNAP_HOST_FFI_ABI_VERSION: u32 = 17; +pub const RSNAP_HOST_FFI_ABI_VERSION: u32 = 18; const RSNAP_TOOLBAR_ITEM_CAPACITY: usize = 16; const RSNAP_STATUS_MESSAGE_CAPACITY: usize = 256; @@ -401,10 +401,6 @@ pub enum RsnapHostEffectKind { pub enum RsnapPermissionKind { /// Screen recording or equivalent display capture access. ScreenRecording = 0, - /// Accessibility access. - Accessibility = 1, - /// Input monitoring access. - InputMonitoring = 2, } /// FFI-safe host report payload. @@ -574,10 +570,6 @@ pub enum RsnapHostRequestKind { RecognizeText = 5, /// Request screen recording permission. RequestScreenRecordingPermission = 6, - /// Request accessibility permission. - RequestAccessibilityPermission = 7, - /// Request input monitoring permission. - RequestInputMonitoringPermission = 8, /// Start native scroll capture. StartScrollCapture = 9, } @@ -1592,18 +1584,8 @@ fn encode_host_request(request: HostRequest) -> RsnapHostRequestValue { } as u32, ..RsnapHostRequestValue::default() }, - HostRequest::RequestPermission(permission) => RsnapHostRequestValue { - kind: match permission { - PermissionKind::ScreenRecording => { - RsnapHostRequestKind::RequestScreenRecordingPermission - }, - PermissionKind::Accessibility => { - RsnapHostRequestKind::RequestAccessibilityPermission - }, - PermissionKind::InputMonitoring => { - RsnapHostRequestKind::RequestInputMonitoringPermission - }, - } as u32, + HostRequest::RequestPermission(PermissionKind::ScreenRecording) => RsnapHostRequestValue { + kind: RsnapHostRequestKind::RequestScreenRecordingPermission as u32, ..RsnapHostRequestValue::default() }, } @@ -1665,10 +1647,6 @@ fn decode_permission_kind(permission_kind: u32) -> PermissionKind { kind if kind == RsnapPermissionKind::ScreenRecording as u32 => { PermissionKind::ScreenRecording }, - kind if kind == RsnapPermissionKind::Accessibility as u32 => PermissionKind::Accessibility, - kind if kind == RsnapPermissionKind::InputMonitoring as u32 => { - PermissionKind::InputMonitoring - }, _ => PermissionKind::ScreenRecording, } }