Skip to content

Commit

Permalink
Try to check and recover the eventTap if needed when user clicks the …
Browse files Browse the repository at this point in the history
…status bar item, #39
  • Loading branch information
aahung committed Apr 1, 2019
1 parent ddd536d commit de9cb45
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 14 deletions.
50 changes: 39 additions & 11 deletions Unshaky/AppDelegate.swift
Expand Up @@ -42,6 +42,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {

statusItem.menu = menu
statusItem.behavior = .removalAllowed
statusItem.menu?.delegate = self

dismissCount = defaults.integer(forKey: "DISMISS_COUNT")
updateDismissCountLabel()
Expand All @@ -50,26 +51,18 @@ class AppDelegate: NSObject, NSApplicationDelegate {
let version = Bundle.main.infoDictionary!["CFBundleShortVersionString"] as! String
versionMenuItem.title = String(format: NSLocalizedString("Version", comment: ""), version)

// this following lines will add Unshaky.app to privacy->accessibility panel, unchecked
let checkOptPrompt = kAXTrustedCheckOptionPrompt.takeUnretainedValue() as NSString
let accessEnabled = AXIsProcessTrustedWithOptions([checkOptPrompt: false] as CFDictionary?)

if (!shakyPressPreventer.setupInputDeviceListener() || !accessEnabled) {
let alert = NSAlert()
alert.messageText = NSLocalizedString("Accessibility Help", comment: "")
alert.runModal()
NSWorkspace.shared.open(URL(string: "x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility")!)
NSApplication.shared.terminate(self)
}
shakyPressPreventer.shakyPressDismissed {
self.dismissCount += 1
OperationQueue.main.addOperation {
self.updateDismissCountLabel()
}
}

setup()
}

func applicationWillTerminate(_ aNotification: Notification) {
shakyPressPreventer.removeEventTap()
defaults.set(dismissCount, forKey: "DISMISS_COUNT")
defaults.synchronize()
}
Expand All @@ -80,6 +73,35 @@ class AppDelegate: NSObject, NSApplicationDelegate {
func applicationDidBecomeActive(_ notification: Notification) {
statusItem.isVisible = true
}

//
// Basic
//
func setup() {
// this following lines will add Unshaky.app to privacy->accessibility panel, unchecked
let checkOptPrompt = kAXTrustedCheckOptionPrompt.takeUnretainedValue() as NSString
let accessEnabled = AXIsProcessTrustedWithOptions([checkOptPrompt: false] as CFDictionary?)

if (!shakyPressPreventer.setupEventTap() || !accessEnabled) {
let alert = NSAlert()
alert.messageText = NSLocalizedString("Accessibility Help", comment: "")
alert.runModal()
NSWorkspace.shared.open(URL(string: "x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility")!)
NSApplication.shared.terminate(self)
}
}

func recover() {
shakyPressPreventer.removeEventTap()
setup()
}

func checkAndRecoverIfNeeded() {
if !shakyPressPreventer.eventTapEnabled() {
print("Event tap is not enable, try to recover.")
recover()
}
}

//
// DEBUG Function
Expand Down Expand Up @@ -139,3 +161,9 @@ extension AppDelegate: NSWindowDelegate {
debugWindow = nil
}
}

extension AppDelegate: NSMenuDelegate {
func menuWillOpen(_ menu: NSMenu) {
checkAndRecoverIfNeeded()
}
}
4 changes: 3 additions & 1 deletion Unshaky/ShakyPressPreventer.h
Expand Up @@ -22,7 +22,9 @@ typedef void (^Handler)(void);
@property DebugViewController *debugViewController;

+ (ShakyPressPreventer *)sharedInstance;
- (BOOL)setupInputDeviceListener;
- (BOOL)setupEventTap;
- (void)removeEventTap;
- (BOOL)eventTapEnabled;
- (CGEventRef)filterShakyPressEvent:(CGEventRef)event;
- (void)shakyPressDismissed:(Handler)handler;
- (void)loadKeyDelays;
Expand Down
22 changes: 20 additions & 2 deletions Unshaky/ShakyPressPreventer.m
Expand Up @@ -189,7 +189,7 @@ - (CGEventRef)filterShakyPressEvent:(CGEventRef)event {
return event;
}

- (BOOL)setupInputDeviceListener {
- (BOOL)setupEventTap {

CGEventMask eventMask = ((1 << kCGEventKeyDown) | (1 << kCGEventKeyUp) | (1 << kCGEventFlagsChanged));
eventTap = CGEventTapCreate(kCGSessionEventTap, kCGHeadInsertEventTap, 0,
Expand All @@ -207,7 +207,25 @@ - (BOOL)setupInputDeviceListener {
return YES;
}

CGEventRef myCGEventCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon) {
- (void)removeEventTap {
if (eventTap == NULL) return;
@try {
CFMachPortInvalidate(eventTap);
CFRelease(eventTap);
eventTap = NULL;
}
@catch(NSException *exception) {
NSLog(@"Fail to remove event tap.");
}
}

- (BOOL)eventTapEnabled {
if (eventTap == NULL) return false;
if (CGEventTapIsEnabled(eventTap) == false) return false;
return true;
}

CGEventRef eventTapCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon) {
ShakyPressPreventer *kc = (__bridge ShakyPressPreventer*)refcon;
return [kc filterShakyPressEvent: event];
}
Expand Down

0 comments on commit de9cb45

Please sign in to comment.