33#define DEFAULT_LINES 3
44#define SIGN (x ) (((x) > 0) - ((x) < 0))
55
6+ static const CFStringRef AX_NOTIFICATION = CFSTR ("com.apple.accessibility.api" );
7+ static bool TRUSTED ;
8+
9+ static CFMachPortRef TAP ;
10+ static CFRunLoopSourceRef SOURCE ;
11+
612static int LINES ;
713
8- CGEventRef callback (CGEventTapProxy proxy , CGEventType type , CGEventRef event , void * userInfo )
14+ static CGEventRef tapCallback (CGEventTapProxy proxy ,
15+ CGEventType type , CGEventRef event , void * userInfo )
916{
1017 if (CGEventGetIntegerValueField (event , kCGScrollWheelEventIsContinuous ) == 0 ) {
1118 int delta = (int )CGEventGetIntegerValueField (event , kCGScrollWheelEventPointDeltaAxis1 );
@@ -15,7 +22,7 @@ CGEventRef callback(CGEventTapProxy proxy, CGEventType type, CGEventRef event, v
1522 return event ;
1623}
1724
18- void displayNoticeAndExit (CFStringRef alertHeader )
25+ static void displayNoticeAndExit (CFStringRef alertHeader )
1926{
2027 CFUserNotificationDisplayNotice (
2128 0 , kCFUserNotificationCautionAlertLevel ,
@@ -26,44 +33,74 @@ void displayNoticeAndExit(CFStringRef alertHeader)
2633 exit (EXIT_FAILURE );
2734}
2835
36+ static void notificationCallback (CFNotificationCenterRef center , void * observer ,
37+ CFNotificationName name , const void * object ,
38+ CFDictionaryRef userInfo )
39+ {
40+ if (CFStringCompare (name , AX_NOTIFICATION , 0 ) == kCFCompareEqualTo ) {
41+ CFRunLoopRef runLoop = CFRunLoopGetCurrent ();
42+ CFRunLoopPerformBlock (
43+ runLoop , kCFRunLoopDefaultMode , ^{
44+ bool previouslyTrusted = TRUSTED ;
45+ if ((TRUSTED = AXIsProcessTrusted ()) != previouslyTrusted ) {
46+ CFRunLoopStop (runLoop );
47+ if (SOURCE && CFRunLoopContainsSource (runLoop , SOURCE , kCFRunLoopDefaultMode )) {
48+ CGEventTapEnable (TAP , TRUSTED );
49+ CFRunLoopRun ();
50+ } else if (!TRUSTED ) {
51+ CFRunLoopRun ();
52+ }
53+ }
54+ }
55+ );
56+ }
57+ }
58+
59+ static bool getIntPreference (CFStringRef key , int * valuePtr )
60+ {
61+ CFNumberRef number = (CFNumberRef )CFPreferencesCopyAppValue (
62+ key , kCFPreferencesCurrentApplication
63+ );
64+ bool got = false;
65+ if (number ) {
66+ if (CFGetTypeID (number ) == CFNumberGetTypeID ())
67+ got = CFNumberGetValue (number , kCFNumberIntType , valuePtr );
68+ CFRelease (number );
69+ }
70+
71+ return got ;
72+ }
73+
2974int main (void )
3075{
76+ CFNotificationCenterAddObserver (
77+ CFNotificationCenterGetDistributedCenter (), NULL ,
78+ notificationCallback , AX_NOTIFICATION , NULL ,
79+ CFNotificationSuspensionBehaviorDeliverImmediately
80+ );
3181 CFDictionaryRef options = CFDictionaryCreate (
3282 kCFAllocatorDefault ,
3383 (const void * * )& kAXTrustedCheckOptionPrompt , (const void * * )& kCFBooleanTrue , 1 ,
3484 & kCFTypeDictionaryKeyCallBacks , & kCFTypeDictionaryValueCallBacks
3585 );
36- bool trusted = AXIsProcessTrustedWithOptions (options );
86+ TRUSTED = AXIsProcessTrustedWithOptions (options );
3787 CFRelease (options );
38- if (!trusted )
39- displayNoticeAndExit (
40- CFSTR ("Restart DiscreteScroll after granting it access to accessibility features." )
41- );
88+ if (!TRUSTED )
89+ CFRunLoopRun ();
4290
43- CFNumberRef value = (CFNumberRef )CFPreferencesCopyAppValue (
44- CFSTR ("lines" ), kCFPreferencesCurrentApplication
45- );
46- bool got = false;
47- if (value ) {
48- if (CFGetTypeID (value ) == CFNumberGetTypeID ())
49- got = CFNumberGetValue (value , kCFNumberIntType , & LINES );
50- CFRelease (value );
51- }
52- if (!got )
91+ if (!getIntPreference (CFSTR ("lines" ), & LINES ))
5392 LINES = DEFAULT_LINES ;
5493
55- CFMachPortRef tap = CGEventTapCreate (
94+ TAP = CGEventTapCreate (
5695 kCGSessionEventTap , kCGHeadInsertEventTap , kCGEventTapOptionDefault ,
57- CGEventMaskBit (kCGEventScrollWheel ), callback , NULL
96+ CGEventMaskBit (kCGEventScrollWheel ), tapCallback , NULL
5897 );
59- if (!tap )
98+ if (!TAP )
6099 displayNoticeAndExit (CFSTR ("DiscreteScroll could not create an event tap." ));
61- CFRunLoopSourceRef source = CFMachPortCreateRunLoopSource (kCFAllocatorDefault , tap , 0 );
62- if (!source )
100+ SOURCE = CFMachPortCreateRunLoopSource (kCFAllocatorDefault , TAP , 0 );
101+ if (!SOURCE )
63102 displayNoticeAndExit (CFSTR ("DiscreteScroll could not create a run loop source." ));
64- CFRunLoopAddSource (CFRunLoopGetCurrent (), source , kCFRunLoopDefaultMode );
65- CFRelease (tap );
66- CFRelease (source );
103+ CFRunLoopAddSource (CFRunLoopGetCurrent (), SOURCE , kCFRunLoopDefaultMode );
67104 CFRunLoopRun ();
68105
69106 return EXIT_SUCCESS ;
0 commit comments