Here's the first half of the AppDelegate.m file referred to in my previous 
message. The second half, the -logEvent: method, will follow in my next message.

//
//  AppDelegate.m
//  Touch Bar Monitor
//
//  Created by Bill Cheeseman on 2017-04-15.
//  Copyright © 2017 PFiddlesoft. All rights reserved.
//

#import "AppDelegate.h"

// Apple's macOS Sierra 10.12.2 AppKit Release Notes indicate that the Touch 
Bar API is now considered to have become available in Sierra 10.12.2, and 
that's what the header files say. It was actually introduced in the second 
release of Sierra 10.12.1, but there is no convenient way to distinguish 
between the two 10.12.1 releases.

@interface AppDelegate ()

@property CFRunLoopSourceRef runLoopSource;
@property CFMachPortRef eventTap;

@end

@implementation AppDelegate

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
    // Insert code here to initialize your application
    
    // Install a Quartz Event Taps event tap to monitor all input events, 
including system events and events addressed to the active application (even if 
Touch Bar Monitor itself is the active application.) Experimentation shows that 
passing kCGEventMaskForAllEvents causes all input events to be tapped, 
including all events posted by the Touch Bar -- events of type 
NSEventTypeDirectTouch (37). Experimentation also shows that you cannot limit 
tapping to Touch Bar events alone by passing NSEventMaskDirectTouch instead of 
kCGEventMaskForAllEvents. In the callback function myCGEventCallback(), below, 
logging is nevertheless restricted to NSEventTypeDirectTouch so as to report 
information about Touch Bar events only.
    // The behavior of Quartz Event Taps with respect to Touch Bar events is 
very different from that of Cocoa's 
-addGlobalMonitorForEventsMatchingMask:handler: method, in that the latter does 
not monitor most Touch Bar events. This difference is apparently the result of 
an Apple policy to discourage developers of normal Cocoa applications from 
handling users' Touch Bar touches specially. Apple's macOS Sierra 10.12.2 
AppKit Release Notes state that "Direct touch events are noted by the new event 
type, NSEventTypeDirectTouch. While there is a corresponding 
NSEventMaskDirectTouch, you cannot acquire direct touch events via 
-nextEventMatchingMask: or similar tracking methods." The Cocoa global event 
monitoring method is apparently one of the "-nextEventMatchingMask: or similar 
tracking methods" referred to in the Release Notes.
    // Another difference between Quartz Event Taps and the Cocoa global event 
monitoring method is that the latter does monitor some Touch Bar events and 
reports them to be traditional Key Down-Key Up events. The Touch Bar items that 
it reports as traditional keyboard events are the Escape ("esc") and Function 
(e.g., "F1", "F2") items, which are designed to be treated exactly like the 
equivalent hardware keys on a traditional Mac keyboard which the Touch Bar 
replaces.
    // The marked differences in behavior between Quartz Event Taps and the 
Cocoa global event monitoring method makes clear that Apple's Touch Bar 
documentation is targeted at developers of non-assistive applications. Apple's 
documentation suggests that there is no API for the Touch Bar and that 
developers should treat Touch Bar input the same way they treat all user input: 
"There is no need, and no API, for your app to know whether or not there is a 
Touch Bar available. Whether your app is running on a machine that supports the 
Touch Bar or not, your app’s onscreen user interface (UI) appears and behaves 
the same way." In fact, developers of assistive applications are able to 
monitor, modify and intercept Touch Bar events by using Quartz Event Taps.
    _eventTap = CGEventTapCreate(kCGHIDEventTap, kCGHeadInsertEventTap, 
kCGEventTapOptionDefault, kCGEventMaskForAllEvents, myCGEventCallback, 
(__bridge void * _Nullable)(self));
    if (!_eventTap) {
        NSLog(@"Failed to create event tap!");
        return;
    }
    _runLoopSource = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, 
_eventTap, 0);
    CFRunLoopAddSource(CFRunLoopGetCurrent(), _runLoopSource, 
kCFRunLoopCommonModes);
    CGEventTapEnable(_eventTap, true);
    CFRunLoopRun();
}

- (void)applicationWillTerminate:(NSNotification *)aNotification {
    // Insert code here to tear down your application
    CFRelease(_eventTap);
    CFRelease(_runLoopSource);
}

-(BOOL) applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)app {
    return YES;
}

CGEventRef myCGEventCallback(CGEventTapProxy proxy, CGEventType type, 
CGEventRef cgEvent, void *refcon) {
    // This asynchronous callback function reports information only about Touch 
Bar events by limiting its logging to events of type NSEventTypeDirectTouch.
    // Touch Bar events provide very little information that might be used to 
identify the Touch Bar items that generate them. The -[NSTouch locationInView:] 
method is probably the most useful, because its horizontal, or x, value is the 
horizontal location of the touch in the Touch Bar.
    // The NSEvent description by itself distinguishes between different Touch 
Bar events only by the location of the cursor onscreen at the time of the event 
and the event's timestamp. The only other traditional NSEvent information that 
might be used to distinguish between any two Touch Bar events is the target 
application's process identifier (PID). All other information about a Touch Bar 
event is contained in a set of NSTouch objects associated with the Touch Bar 
event, obtained through the event's allTouches() function. This information is 
limited to the touch and describes very little about the Touch Bar item the 
user touched to generate the event. The one piece of Touch Bar information that 
is useful to identify the Touch Bar item is its -locationInView: method 
providing the horizontal distance of the touch from the left end of the Touch 
Bar.
    // Note that the Cocoa log description of a Touch Bar event shows obsolete 
information, in that the type of the event is reported as "Reserved2" instead 
of "Direct Touch". The event type value by itself is correctly logged as 
NSEventTypeDirectTouch (37).
    
    id self = (__bridge id)(refcon);
    
    NSEvent *cocoaEvent = [NSEvent eventWithCGEvent:cgEvent];
    NSUInteger eventType = [cocoaEvent type];
    if (eventType == NSEventTypeDirectTouch) {
        
        // Log interesting information about the event.
        [self logEvent:cocoaEvent];

        // Test intercepting and blocking Touch Bar events.
        int64_t targetPID = CGEventGetIntegerValueField(cgEvent, 
kCGEventTargetUnixProcessID);
        NSString *targetAppName = [[NSRunningApplication 
runningApplicationWithProcessIdentifier:(pid_t)targetPID] localizedName];
        if ([targetAppName isEqualToString:@"Microsoft Word"]) {
            // To intercept and block all Touch Bar events targeted at MS Word, 
uncomment the 'return nil' statement.
            // To block only those events that relate to a specific MS Word 
Touch Bar item, you should block MS Word events whose locationInView 
x-coordinates are within the width of the affected item. I have not yet worked 
out a way to detect the controls in the Touch Bar so as to be able to do this.
            // To send modified events, you should edit the incoming events of 
interest or create suitable replacement events and return them instead of the 
incoming event. I have not yet worked out how to do this.
            // return nil;
        }
    }

    // Return the incoming event. WARNING: If you return nil here, your 
computer will receive no input from any device and you will have to restart it 
with the power switch in order to recover control.
    return cgEvent;
}

-- 

Bill Cheeseman - wjcheese...@comcast.net

_______________________________________________

Cocoa-dev mailing list (Cocoa-dev@lists.apple.com)

Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com

Help/Unsubscribe/Update your Subscription:
https://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com

This email sent to arch...@mail-archive.com

Reply via email to