mirror of
https://github.com/keycastr/keycastr.git
synced 2026-05-15 06:06:06 -06:00
Fix ghost keystrokes after app exits
Instead of using a null EventTap to do a pre-flight check for permissions, using a dedicated EventTap for KeyDown events serves the same purpose and fixes the issue. Also convert KCEventTap to ARC. [fixes #72] [fixes #311]
This commit is contained in:
parent
cf95a5c58f
commit
1025e8fcb4
2 changed files with 75 additions and 80 deletions
|
|
@ -1,5 +1,5 @@
|
|||
// Copyright (c) 2009 Stephen Deken
|
||||
// Copyright (c) 2014-2023 Andrew Kitchen
|
||||
// Copyright (c) 2014-2024 Andrew Kitchen
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
|
|
@ -26,6 +26,9 @@
|
|||
// OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#if !__has_feature(objc_arc)
|
||||
#error "ARC is required for this file -- enable with -fobjc-arc"
|
||||
#endif
|
||||
|
||||
#import "KCEventTap.h"
|
||||
#import "KCKeystroke.h"
|
||||
|
|
@ -33,9 +36,10 @@
|
|||
#import "KCMouseEvent.h"
|
||||
|
||||
@interface KCEventTap () {
|
||||
CFMachPortRef eventTap;
|
||||
CFRunLoopRef eventTapRunLoop;
|
||||
CFRunLoopSourceRef eventTapEventSource;
|
||||
CFMachPortRef keyEventTap;
|
||||
CFMachPortRef mouseAndFlagsEventTap;
|
||||
CFRunLoopSourceRef keyEventTapSource;
|
||||
CFRunLoopSourceRef mouseAndFlagsEventTapSource;
|
||||
}
|
||||
|
||||
- (void)_noteMouseEvent:(CGEventRef)eventRef;
|
||||
|
|
@ -44,22 +48,33 @@
|
|||
|
||||
@end
|
||||
|
||||
CGEventRef nullEventTapCallback(
|
||||
CGEventRef keyEventTapCallback(
|
||||
CGEventTapProxy proxy,
|
||||
CGEventType type,
|
||||
CGEventRef event,
|
||||
void *vp)
|
||||
void *context)
|
||||
{
|
||||
return NULL;
|
||||
KCEventTap *eventTap = (__bridge KCEventTap *)context;
|
||||
switch (type)
|
||||
{
|
||||
case kCGEventKeyDown:
|
||||
[eventTap _noteKeyEvent:event];
|
||||
break;
|
||||
case kCGEventKeyUp:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return event;
|
||||
}
|
||||
|
||||
CGEventRef eventTapCallback(
|
||||
CGEventRef mouseAndFlagsEventTapCallback(
|
||||
CGEventTapProxy proxy,
|
||||
CGEventType type,
|
||||
CGEventRef event,
|
||||
void *vp)
|
||||
void *context)
|
||||
{
|
||||
KCEventTap* keyTap = (KCEventTap*)vp;
|
||||
KCEventTap *eventTap = (__bridge KCEventTap *)context;
|
||||
switch (type)
|
||||
{
|
||||
case kCGEventLeftMouseDown:
|
||||
|
|
@ -71,24 +86,19 @@ CGEventRef eventTapCallback(
|
|||
case kCGEventOtherMouseDown:
|
||||
case kCGEventOtherMouseUp:
|
||||
case kCGEventOtherMouseDragged:
|
||||
[keyTap _noteMouseEvent:event];
|
||||
break;
|
||||
case kCGEventKeyDown:
|
||||
[keyTap _noteKeyEvent:event];
|
||||
[eventTap _noteMouseEvent:event];
|
||||
break;
|
||||
case kCGEventFlagsChanged:
|
||||
[keyTap _noteFlagsChanged:event];
|
||||
[eventTap _noteFlagsChanged:event];
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return NULL;
|
||||
return event;
|
||||
}
|
||||
|
||||
@implementation KCEventTap
|
||||
|
||||
@synthesize delegate = _delegate;
|
||||
|
||||
-(id) init
|
||||
{
|
||||
if (!(self = [super init]))
|
||||
|
|
@ -101,8 +111,6 @@ CGEventRef eventTapCallback(
|
|||
if (_tapInstalled) {
|
||||
[self removeTap];
|
||||
}
|
||||
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
-(NSError*) constructErrorWithDescription:(NSString*)description {
|
||||
|
|
@ -121,69 +129,52 @@ CGEventRef eventTapCallback(
|
|||
// We have to try to tap the keydown event independently because CGEventTapCreate will succeed if it can
|
||||
// install the event tap for the flags changed event, which apparently doesn't require universal access
|
||||
// to be enabled. Thus, the call would succeed but KeyCastr would be, um, useless.
|
||||
CFMachPortRef tapKeyDown = CGEventTapCreate(
|
||||
kCGSessionEventTap,
|
||||
kCGHeadInsertEventTap,
|
||||
kCGEventTapOptionListenOnly,
|
||||
CGEventMaskBit(kCGEventKeyDown),
|
||||
nullEventTapCallback,
|
||||
self
|
||||
);
|
||||
|
||||
if (tapKeyDown == NULL) {
|
||||
keyEventTap = CGEventTapCreate(kCGSessionEventTap,
|
||||
kCGHeadInsertEventTap,
|
||||
kCGEventTapOptionListenOnly,
|
||||
CGEventMaskBit(kCGEventKeyDown)
|
||||
| CGEventMaskBit(kCGEventKeyUp),
|
||||
keyEventTapCallback,
|
||||
(__bridge void *)self
|
||||
);
|
||||
|
||||
if (keyEventTap == NULL) {
|
||||
if (error != NULL) {
|
||||
*error = [self constructErrorWithDescription:@"Could not create keyDown event tap!"];
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
CFRelease( tapKeyDown );
|
||||
|
||||
eventTap = CGEventTapCreate(
|
||||
kCGSessionEventTap,
|
||||
kCGHeadInsertEventTap,
|
||||
kCGEventTapOptionListenOnly,
|
||||
CGEventMaskBit(kCGEventLeftMouseDown)
|
||||
| CGEventMaskBit(kCGEventLeftMouseUp)
|
||||
| CGEventMaskBit(kCGEventRightMouseDown)
|
||||
| CGEventMaskBit(kCGEventRightMouseUp)
|
||||
| CGEventMaskBit(kCGEventLeftMouseDragged)
|
||||
| CGEventMaskBit(kCGEventRightMouseDragged)
|
||||
| CGEventMaskBit(kCGEventKeyDown)
|
||||
| CGEventMaskBit(kCGEventFlagsChanged)
|
||||
| CGEventMaskBit(kCGEventOtherMouseDown)
|
||||
| CGEventMaskBit(kCGEventOtherMouseUp)
|
||||
| CGEventMaskBit(kCGEventOtherMouseDragged),
|
||||
eventTapCallback,
|
||||
self
|
||||
);
|
||||
|
||||
if (eventTap == NULL) {
|
||||
if (error != NULL) {
|
||||
*error = [self constructErrorWithDescription:@"Could not create keyDown|flagsChanged event tap!"];
|
||||
*error = [self constructErrorWithDescription:@"Could not create key event tap! Permissions needed..."];
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
eventTapEventSource = CFMachPortCreateRunLoopSource(NULL, eventTap, 0);
|
||||
if (eventTapEventSource == NULL) {
|
||||
CFRelease(eventTap);
|
||||
mouseAndFlagsEventTap = CGEventTapCreate(kCGSessionEventTap,
|
||||
kCGHeadInsertEventTap,
|
||||
kCGEventTapOptionListenOnly,
|
||||
CGEventMaskBit(kCGEventLeftMouseDown)
|
||||
| CGEventMaskBit(kCGEventLeftMouseUp)
|
||||
| CGEventMaskBit(kCGEventRightMouseDown)
|
||||
| CGEventMaskBit(kCGEventRightMouseUp)
|
||||
| CGEventMaskBit(kCGEventLeftMouseDragged)
|
||||
| CGEventMaskBit(kCGEventRightMouseDragged)
|
||||
| CGEventMaskBit(kCGEventFlagsChanged)
|
||||
| CGEventMaskBit(kCGEventOtherMouseDown)
|
||||
| CGEventMaskBit(kCGEventOtherMouseUp)
|
||||
| CGEventMaskBit(kCGEventOtherMouseDragged),
|
||||
mouseAndFlagsEventTapCallback,
|
||||
(__bridge void *)self
|
||||
);
|
||||
|
||||
if (mouseAndFlagsEventTap == NULL) {
|
||||
if (error != NULL) {
|
||||
*error = [self constructErrorWithDescription:@"Could not create run loop source!"];
|
||||
*error = [self constructErrorWithDescription:@"Could not create mouse and modifiers event tap!"];
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
eventTapRunLoop = CFRunLoopGetCurrent();
|
||||
if (eventTapRunLoop == NULL) {
|
||||
CFRelease(eventTapEventSource);
|
||||
CFRelease(eventTap);
|
||||
if (error != NULL) {
|
||||
*error = [self constructErrorWithDescription:@"Could not get current run loop!"];
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
CFRunLoopAddSource(eventTapRunLoop, eventTapEventSource, kCFRunLoopDefaultMode);
|
||||
keyEventTapSource = CFMachPortCreateRunLoopSource(NULL, keyEventTap, 0);
|
||||
mouseAndFlagsEventTapSource = CFMachPortCreateRunLoopSource(NULL, mouseAndFlagsEventTap, 0);
|
||||
|
||||
CFRunLoopRef runLoop = CFRunLoopGetCurrent();
|
||||
CFRunLoopAddSource(runLoop, keyEventTapSource, kCFRunLoopDefaultMode);
|
||||
CFRunLoopAddSource(runLoop, mouseAndFlagsEventTapSource, kCFRunLoopDefaultMode);
|
||||
|
||||
_tapInstalled = YES;
|
||||
|
||||
|
|
@ -194,10 +185,15 @@ CGEventRef eventTapCallback(
|
|||
if (!_tapInstalled) {
|
||||
return;
|
||||
}
|
||||
|
||||
CFRunLoopRemoveSource(eventTapRunLoop, eventTapEventSource, kCFRunLoopDefaultMode);
|
||||
CFRelease(eventTapEventSource);
|
||||
CFRelease(eventTap);
|
||||
|
||||
CFRunLoopSourceInvalidate(keyEventTapSource);
|
||||
CFRunLoopSourceInvalidate(mouseAndFlagsEventTapSource);
|
||||
|
||||
CFRelease(keyEventTapSource);
|
||||
CFRelease(mouseAndFlagsEventTapSource);
|
||||
|
||||
CFRelease(keyEventTap);
|
||||
CFRelease(mouseAndFlagsEventTap);
|
||||
|
||||
_tapInstalled = NO;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@
|
|||
3D3F52A70F30BF1E001C7272 /* KeyCastr.icns in Resources */ = {isa = PBXBuildFile; fileRef = 3D3F52A60F30BF1E001C7272 /* KeyCastr.icns */; };
|
||||
3D3F52BA0F30C68B001C7272 /* Quartz.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3D3F52B90F30C68B001C7272 /* Quartz.framework */; };
|
||||
3D3F52EB0F30CB13001C7272 /* KCPrefsWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3D3F52EA0F30CB13001C7272 /* KCPrefsWindowController.m */; };
|
||||
3D3F540C0F30E8E7001C7272 /* KCEventTap.m in Sources */ = {isa = PBXBuildFile; fileRef = 3D3F540B0F30E8E7001C7272 /* KCEventTap.m */; };
|
||||
3D3F540C0F30E8E7001C7272 /* KCEventTap.m in Sources */ = {isa = PBXBuildFile; fileRef = 3D3F540B0F30E8E7001C7272 /* KCEventTap.m */; settings = {COMPILER_FLAGS = "-fobjc-arc"; }; };
|
||||
3D3F548A0F30F37E001C7272 /* GeneralIcon.tif in Resources */ = {isa = PBXBuildFile; fileRef = 3D3F54870F30F37E001C7272 /* GeneralIcon.tif */; };
|
||||
3D3F548B0F30F37E001C7272 /* DisplayIcon.tif in Resources */ = {isa = PBXBuildFile; fileRef = 3D3F54880F30F37E001C7272 /* DisplayIcon.tif */; };
|
||||
3D3F548E0F30F3C7001C7272 /* UpdateIcon.tif in Resources */ = {isa = PBXBuildFile; fileRef = 3D3F548D0F30F3C7001C7272 /* UpdateIcon.tif */; };
|
||||
|
|
@ -796,7 +796,6 @@
|
|||
ProvisioningStyle = Automatic;
|
||||
};
|
||||
8D1107260486CEB800E47090 = {
|
||||
DevelopmentTeam = 68TUCLNAPS;
|
||||
ProvisioningStyle = Automatic;
|
||||
};
|
||||
AFC172182377CFF500292155 = {
|
||||
|
|
@ -1521,7 +1520,7 @@
|
|||
PRODUCT_BUNDLE_IDENTIFIER = io.github.keycastr;
|
||||
PRODUCT_NAME = KeyCastr;
|
||||
PROVISIONING_PROFILE_SPECIFIER = "KeyCastr Developer ID";
|
||||
"PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = "KeyCastr Developer ID";
|
||||
"PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = "KeyCastr Developer ID Distribution";
|
||||
WRAPPER_EXTENSION = app;
|
||||
};
|
||||
name = Release;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue