@@ -238,6 +238,15 @@ EventTap createEventTap(EventTapCallback callback, void* userData) {
238238
239239 context->runLoopSource = CFMachPortCreateRunLoopSource (kCFAllocatorDefault , context->eventTap , 0 );
240240
241+ // Add to main run loop once during creation to avoid re-entry on enable
242+ if ([NSThread isMainThread ]) {
243+ CFRunLoopAddSource (CFRunLoopGetMain (), context->runLoopSource , kCFRunLoopCommonModes );
244+ } else {
245+ dispatch_async (dispatch_get_main_queue (), ^{
246+ CFRunLoopAddSource (CFRunLoopGetMain (), context->runLoopSource , kCFRunLoopCommonModes );
247+ });
248+ }
249+
241250 return (EventTap)context;
242251}
243252
@@ -268,33 +277,24 @@ void enableEventTap(EventTap tap) {
268277
269278 EventTapContext* context = (EventTapContext*)tap;
270279
271- // Must run on main thread since we're modifying the main run loop
272- if ([NSThread isMainThread ]) {
273- CFRunLoopAddSource (CFRunLoopGetMain (), context->runLoopSource , kCFRunLoopCommonModes );
274- CGEventTapEnable (context->eventTap , true );
275- } else {
276- dispatch_sync (dispatch_get_main_queue (), ^{
277- CFRunLoopAddSource (CFRunLoopGetMain (), context->runLoopSource , kCFRunLoopCommonModes );
280+ // Always enable asynchronously to avoid overlap with disable/destroy
281+ // Use a short delay to ensure prior disable completes first
282+ dispatch_async (dispatch_get_main_queue (), ^{
283+ dispatch_after (dispatch_time (DISPATCH_TIME_NOW , (int64_t )(0.15 * NSEC_PER_SEC )), dispatch_get_main_queue (), ^{
278284 CGEventTapEnable (context->eventTap , true );
279285 });
280- }
286+ });
281287}
282288
283289void disableEventTap (EventTap tap) {
284290 if (!tap) return ;
285291
286292 EventTapContext* context = (EventTapContext*)tap;
287293
288- // Must run on main thread since we're modifying the main run loop
289- if ([ NSThread isMainThread ]) {
294+ // Always disable asynchronously to avoid overlap with enable/destroy
295+ dispatch_async ( dispatch_get_main_queue (), ^ {
290296 CGEventTapEnable (context->eventTap , false );
291- CFRunLoopRemoveSource (CFRunLoopGetMain (), context->runLoopSource , kCFRunLoopCommonModes );
292- } else {
293- dispatch_sync (dispatch_get_main_queue (), ^{
294- CGEventTapEnable (context->eventTap , false );
295- CFRunLoopRemoveSource (CFRunLoopGetMain (), context->runLoopSource , kCFRunLoopCommonModes );
296- });
297- }
297+ });
298298}
299299
300300void destroyEventTap (EventTap tap) {
@@ -311,7 +311,7 @@ void destroyEventTap(EventTap tap) {
311311 CFRunLoopRemoveSource (CFRunLoopGetMain (), context->runLoopSource , kCFRunLoopCommonModes );
312312 }
313313 } else {
314- dispatch_sync (dispatch_get_main_queue (), ^{
314+ dispatch_async (dispatch_get_main_queue (), ^{
315315 if (context->eventTap ) {
316316 CGEventTapEnable (context->eventTap , false );
317317 }
0 commit comments