@@ -119,9 +119,14 @@ final class HotkeyService {
119119 private var fallbackKeyUpMonitors : [ Any ] = [ ]
120120 private var eventTap : CFMachPort ?
121121 private var eventTapRunLoopSource : CFRunLoopSource ?
122+ private var eventTapRetryTimer : Timer ?
122123 private var activeCombo : WorkflowType ? // Which combo is currently held
123124 private var activeKeyCode : UInt16 ? // Set when the active combo includes a regular key
124125
126+ /// false, solange der CGEventTap mangels Accessibility-Berechtigung nicht
127+ /// laeuft -- Kombos mit normaler Taste funktionieren dann nicht.
128+ private( set) var keyEventTapActive = false
129+
125130 var combos : [ WorkflowType : HotkeyCombo ] = HotkeyCombo . defaults
126131
127132 /// Waehrend der Hotkey-Aufnahme in den Einstellungen pausiert,
@@ -167,10 +172,9 @@ final class HotkeyService {
167172 globalMonitor = nil
168173 localMonitor = nil
169174 keyMonitor = nil
170- fallbackKeyDownMonitors. forEach { NSEvent . removeMonitor ( $0) }
171- fallbackKeyUpMonitors. forEach { NSEvent . removeMonitor ( $0) }
172- fallbackKeyDownMonitors = [ ]
173- fallbackKeyUpMonitors = [ ]
175+ stopFallbackKeyMonitors ( )
176+ eventTapRetryTimer? . invalidate ( )
177+ eventTapRetryTimer = nil
174178 if let eventTapRunLoopSource {
175179 CFRunLoopRemoveSource ( CFRunLoopGetMain ( ) , eventTapRunLoopSource, . commonModes)
176180 }
@@ -179,14 +183,34 @@ final class HotkeyService {
179183 }
180184 eventTap = nil
181185 eventTapRunLoopSource = nil
186+ keyEventTapActive = false
182187 }
183188
184189 // MARK: - Key Event Tap (fuer Kombos mit normaler Taste, z.B. fn + R)
185190
186191 /// Ein CGEventTap schluckt die normale Taste beim Ausloesen, damit sie nicht
187192 /// zusaetzlich in das fokussierte Textfeld getippt wird. Ohne Accessibility-
188- /// Berechtigung faellt der Service auf passive NSEvent-Monitore zurueck.
193+ /// Berechtigung faellt der Service auf passive NSEvent-Monitore zurueck und
194+ /// versucht periodisch erneut, den Tap zu erstellen -- sonst blieben
195+ /// Tasten-Kombos nach spaeter erteilter Berechtigung bis zum Neustart tot.
189196 private func startKeyEventTap( ) {
197+ guard !createKeyEventTap( ) else { return }
198+ startFallbackKeyMonitors ( )
199+ eventTapRetryTimer = Timer . scheduledTimer ( withTimeInterval: 3.0 , repeats: true ) { [ weak self] _ in
200+ Task { @MainActor in
201+ self ? . retryKeyEventTap ( )
202+ }
203+ }
204+ }
205+
206+ private func retryKeyEventTap( ) {
207+ guard createKeyEventTap ( ) else { return }
208+ eventTapRetryTimer? . invalidate ( )
209+ eventTapRetryTimer = nil
210+ stopFallbackKeyMonitors ( )
211+ }
212+
213+ private func createKeyEventTap( ) -> Bool {
190214 let mask = ( 1 << CGEventType . keyDown. rawValue) | ( 1 << CGEventType . keyUp. rawValue)
191215 let callback : CGEventTapCallBack = { _, type, cgEvent, userInfo in
192216 guard let userInfo else { return Unmanaged . passUnretained ( cgEvent) }
@@ -207,13 +231,15 @@ final class HotkeyService {
207231 )
208232
209233 guard let eventTap else {
210- startFallbackKeyMonitors ( )
211- return
234+ keyEventTapActive = false
235+ return false
212236 }
213237
214238 eventTapRunLoopSource = CFMachPortCreateRunLoopSource ( kCFAllocatorDefault, eventTap, 0 )
215239 CFRunLoopAddSource ( CFRunLoopGetMain ( ) , eventTapRunLoopSource, . commonModes)
216240 CGEvent . tapEnable ( tap: eventTap, enable: true )
241+ keyEventTapActive = true
242+ return true
217243 }
218244
219245 private func startFallbackKeyMonitors( ) {
@@ -241,6 +267,13 @@ final class HotkeyService {
241267 } as Any )
242268 }
243269
270+ private func stopFallbackKeyMonitors( ) {
271+ fallbackKeyDownMonitors. forEach { NSEvent . removeMonitor ( $0) }
272+ fallbackKeyUpMonitors. forEach { NSEvent . removeMonitor ( $0) }
273+ fallbackKeyDownMonitors = [ ]
274+ fallbackKeyUpMonitors = [ ]
275+ }
276+
244277 private func handleKeyTapEvent( type: CGEventType , event: CGEvent ) -> Unmanaged < CGEvent > ? {
245278 switch type {
246279 case . tapDisabledByTimeout, . tapDisabledByUserInput:
0 commit comments