Skip to content

Commit

Permalink
Handle kCGEventTapDisabledByTimeout and kCGEventTapDisabledByUserInput.
Browse files Browse the repository at this point in the history
Refs #114
  • Loading branch information
Kentzo committed Apr 7, 2020
1 parent 8ceb3ba commit ff95668
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 19 deletions.
10 changes: 10 additions & 0 deletions Library/SRShortcutAction.h
Original file line number Diff line number Diff line change
Expand Up @@ -382,15 +382,23 @@ NS_SWIFT_NAME(GlobalShortcutMonitor)
security implications as this API requires the app to either run under the root user or been allowed
the Accessibility permission.
The monitor automatically enables and disables the tap when needed.
@see SRGlobalShortcutMonitor
@see AXIsProcessTrustedWithOptions
@see NSAppleEventsUsageDescription
*/
@interface SRAXGlobalShortcutMonitor : SRShortcutMonitor

/*!
Mach port that corresponds to the event tap used under the hood.
*/
@property (readonly) CFMachPortRef eventTap;
- (CFMachPortRef)eventTap NS_RETURNS_INNER_POINTER CF_RETURNS_NOT_RETAINED;

@property (readonly) CFRunLoopSourceRef eventTapSource;
- (CFRunLoopSourceRef)eventTapSource NS_RETURNS_INNER_POINTER CF_RETURNS_NOT_RETAINED;

/*!
@discussion
Initialization may fail if it's impossible to create an event tap.
Expand All @@ -399,6 +407,8 @@ NS_SWIFT_NAME(GlobalShortcutMonitor)
*/
- (nullable instancetype)init;

- (nullable instancetype)initWithRunLoop:(nullable NSRunLoop *)aRunLoop NS_DESIGNATED_INITIALIZER;

/*!
Perform the action associated with a given event.
Expand Down
71 changes: 52 additions & 19 deletions Library/SRShortcutAction.m
Original file line number Diff line number Diff line change
Expand Up @@ -1110,10 +1110,23 @@ @implementation SRAXGlobalShortcutMonitor
CGEventRef _Nullable TapCallback(CGEventTapProxy aProxy, CGEventType aType, CGEventRef anEvent, void * _Nullable aUserInfo)
{
__auto_type self = (__bridge SRAXGlobalShortcutMonitor *)aUserInfo;
return [self handleEvent:anEvent];

if (aType == kCGEventTapDisabledByTimeout || aType == kCGEventTapDisabledByUserInput)
{
os_trace("#Error #Developer The system disabled event tap due to %u", aType);
CGEventTapEnable(self.eventTap, true);
return anEvent;
}
else
return [self handleEvent:anEvent];
}

- (instancetype)init
{
return [self initWithRunLoop:nil];
}

- (instancetype)initWithRunLoop:(NSRunLoop *)aRunLoop
{
static const CGEventMask Mask = (CGEventMaskBit(kCGEventKeyDown) |
CGEventMaskBit(kCGEventKeyUp) |
Expand All @@ -1135,6 +1148,12 @@ - (instancetype)init
if (self)
{
_eventTap = eventTap;
_eventTapSource = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, eventTap, 0);

if (aRunLoop)
CFRunLoopAddSource(aRunLoop.getCFRunLoop, _eventTapSource, kCFRunLoopDefaultMode);
else
CFRunLoopAddSource(CFRunLoopGetCurrent(), _eventTapSource, kCFRunLoopDefaultMode);
}

return self;
Expand All @@ -1144,33 +1163,47 @@ - (void)dealloc
{
if (_eventTap)
CFRelease(_eventTap);

CFRelease(_eventTapSource);
}

#pragma mark Methods

- (CGEventRef)handleEvent:(CGEventRef)anEvent
{
__auto_type nsEvent = [NSEvent eventWithCGEvent:anEvent];
__auto_type shortcut = [SRShortcut shortcutWithEvent:nsEvent];
if (!shortcut)
{
os_trace_error("#Error Not a keyboard event");
return anEvent;
}
__block __auto_type result = anEvent;

SRKeyEventType eventType = nsEvent.SR_keyEventType;
if (eventType == 0)
return anEvent;
os_activity_initiate("-[SRAXGlobalShortcutMonitor handleEvent:]", OS_ACTIVITY_FLAG_DETACHED, ^{
__auto_type nsEvent = [NSEvent eventWithCGEvent:anEvent];
if (!nsEvent)
{
os_trace_error("#Error Unexpected event");
return;
}

__auto_type actions = [self actionsForShortcut:shortcut keyEvent:eventType];
__block BOOL isHandled = NO;
[actions enumerateObjectsWithOptions:NSEnumerationReverse
usingBlock:^(SRShortcutAction *obj, NSUInteger idx, BOOL *stop)
{
*stop = isHandled = [obj performActionOnTarget:nil];
}];
__auto_type shortcut = [SRShortcut shortcutWithEvent:nsEvent];
if (!shortcut)
{
os_trace_error("#Error Not a keyboard event");
return;
}

SRKeyEventType eventType = nsEvent.SR_keyEventType;
if (eventType == 0)
return;

__auto_type actions = [self actionsForShortcut:shortcut keyEvent:eventType];
__block BOOL isHandled = NO;
[actions enumerateObjectsWithOptions:NSEnumerationReverse
usingBlock:^(SRShortcutAction *obj, NSUInteger idx, BOOL *stop)
{
*stop = isHandled = [obj performActionOnTarget:nil];
}];

result = isHandled ? nil : anEvent;
});

return isHandled ? nil : anEvent;
return result;
}

#pragma mark SRShortcutMonitor
Expand Down

0 comments on commit ff95668

Please sign in to comment.