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 - [email protected]
_______________________________________________
Cocoa-dev mailing list ([email protected])
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 [email protected]