[vlc-devel] [VLC 3.x 2/4] macosx: SPMediaKeyTap: Move to main thread

David Fuhrmann david.fuhrmann at gmail.com
Thu Apr 22 16:04:12 UTC 2021



> Am 22.04.2021 um 17:39 schrieb Marvin Scholz <epirat07 at gmail.com>:
> 
> Move the event tap to the main thread, as the event tap callback
> makes use of NSEvent APIs which apparently crash at least on M1
> machines running Big Sur when called from a non-main thread.
> 
> Additionally remove the now unnecessary @synchronized recursive lock.
> 
> Fix videolan/vlc#25650
> ---
> modules/gui/macosx/SPMediaKeyTap.m | 62 +++++++-----------------------
> 1 file changed, 13 insertions(+), 49 deletions(-)
> 
> diff --git a/modules/gui/macosx/SPMediaKeyTap.m b/modules/gui/macosx/SPMediaKeyTap.m
> index 1981b45775..03e8e67920 100644
> --- a/modules/gui/macosx/SPMediaKeyTap.m
> +++ b/modules/gui/macosx/SPMediaKeyTap.m
> @@ -32,19 +32,15 @@ NSString *kIgnoreMediaKeysDefaultsKey = @"SPIgnoreMediaKeys";
> @interface SPMediaKeyTap () {
>     CFMachPortRef _eventPort;
>     CFRunLoopSourceRef _eventPortSource;
> -    CFRunLoopRef _tapThreadRL;
> -    NSThread *_tapThread;
>     BOOL _shouldInterceptMediaKeyEvents;
>     id _delegate;
>     // The app that is frontmost in this list owns media keys
>     NSMutableArray<NSRunningApplication *> *_mediaKeyAppList;
> }
> 
> -- (BOOL)shouldInterceptMediaKeyEvents;
> - (void)setShouldInterceptMediaKeyEvents:(BOOL)newSetting;
> - (void)startWatchingAppSwitching;
> - (void)stopWatchingAppSwitching;
> -- (void)eventTapThread;
> @end
> 
> static CGEventRef tapEventCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon);
> @@ -121,21 +117,16 @@ static CGEventRef tapEventCallback(CGEventTapProxy proxy, CGEventType type, CGEv
>     if (_eventPortSource == NULL)
>         return NO;
> 
> -    // Let's do this in a separate thread so that a slow app doesn't lag the event tap
> -    _tapThread = [[NSThread alloc] initWithTarget:self
> -                                         selector:@selector(eventTapThread)
> -                                           object:nil];
> -    [_tapThread start];
> +    CFRunLoopAddSource(CFRunLoopGetCurrent(), _eventPortSource, kCFRunLoopCommonModes);
> 
>     return YES;
> }
> 
> - (void)stopWatchingMediaKeys
> {
> -    // Shut down tap thread
> -    if(_tapThreadRL){
> -        CFRunLoopStop(_tapThreadRL);
> -        _tapThreadRL = nil;
> +    // Remove runloop source
> +    if(_eventPortSource){
> +        CFRunLoopRemoveSource(CFRunLoopGetCurrent(), _eventPortSource, kCFRunLoopCommonModes);
>     }
> 
>     // Remove tap port
> @@ -199,35 +190,15 @@ static CGEventRef tapEventCallback(CGEventTapProxy proxy, CGEventType type, CGEv
>     return bundleIdentifiers;
> }
> 
> -
> -- (BOOL)shouldInterceptMediaKeyEvents
> -{
> -    BOOL shouldIntercept = NO;
> -    @synchronized(self) {
> -        shouldIntercept = _shouldInterceptMediaKeyEvents;
> -    }
> -    return shouldIntercept;
> -}
> -
> -- (void)pauseTapOnTapThread:(NSNumber *)yeahno
> -{
> -    CGEventTapEnable(self->_eventPort, [yeahno boolValue]);
> -}
> -
> - (void)setShouldInterceptMediaKeyEvents:(BOOL)newSetting
> {
> -    BOOL oldSetting;
> -    @synchronized(self) {
> -        oldSetting = _shouldInterceptMediaKeyEvents;
> -        _shouldInterceptMediaKeyEvents = newSetting;
> -    }
> -    if(_tapThreadRL && oldSetting != newSetting) {
> -        [self performSelector:@selector(pauseTapOnTapThread:)
> -                     onThread:_tapThread
> -                   withObject:@(newSetting)
> -                waitUntilDone:NO];
> +    if (_eventPort == NULL)
> +        return;
> +    if (_shouldInterceptMediaKeyEvents == newSetting)
> +        return;
> +    _shouldInterceptMediaKeyEvents = newSetting;

Can you please move this code (setting _shouldInterceptMediaKeyEvents), before the "_eventPort == NULL“ check, to reinstate the old behaviour here?

In fact this case is used when inside the enabling media keys method (I need to investigate a bit whether we need to call CGEventTapEnable also for enabling the key-watch or not).

> 
> -    }
> +    CGEventTapEnable(_eventPort, newSetting);
> }
> 
> 
> @@ -265,9 +236,6 @@ static CGEventRef tapEventCallback2(CGEventTapProxy proxy, CGEventType type, CGE
>     if (keyCode != NX_KEYTYPE_PLAY && keyCode != NX_KEYTYPE_FAST && keyCode != NX_KEYTYPE_REWIND && keyCode != NX_KEYTYPE_PREVIOUS && keyCode != NX_KEYTYPE_NEXT)
>         return event;
> 
> -    if (![self shouldInterceptMediaKeyEvents])
> -        return event;

From my understanding if we do not handle the event we should return it here in this callback to put it back to the event loop. Therefore I would propose to keep this code path here, and as everything is on the main thread it should be ok here without additional locking.

> -
>     [self performSelectorOnMainThread:@selector(handleAndReleaseMediaKeyEvent:) withObject:nsEvent waitUntilDone:NO];
> 
>     return NULL;
> @@ -283,14 +251,10 @@ static CGEventRef tapEventCallback(CGEventTapProxy proxy, CGEventType type, CGEv
> 
> - (void)handleAndReleaseMediaKeyEvent:(NSEvent *)event
> {
> -    [_delegate mediaKeyTap:self receivedMediaKeyEvent:event];
> -}
> +    if (!_shouldInterceptMediaKeyEvents)
> +        return;
> 
> -- (void)eventTapThread
> -{
> -    _tapThreadRL = CFRunLoopGetCurrent();
> -    CFRunLoopAddSource(_tapThreadRL, _eventPortSource, kCFRunLoopCommonModes);
> -    CFRunLoopRun();
> +    [_delegate mediaKeyTap:self receivedMediaKeyEvent:event];
> }
> 
> 
> -- 
> 2.30.1
> 
> _______________________________________________
> vlc-devel mailing list
> To unsubscribe or modify your subscription options:
> https://mailman.videolan.org/listinfo/vlc-devel



More information about the vlc-devel mailing list