Add Touch Bar Control Strip icon example
Add a button to the Touch Bar Control Strip from a Headless macOS app, no Xcode,
no Xib. Uses private framework.
+CFBundleName = touchtest;
+CFBundleDisplayName = TouchTest;
+CFBundleIdentifier = "com.trevorbentley.touchtest";
+CFBundleExecutable = touchtest;
+
+CFBundleVersion = "0.0.2";
+CFBundleInfoDictionaryVersion = "6.0";
+CFBundlePackageType = APPL;
+CFBundleSignature = xxxx;
+
+LSMinimumSystemVersion = "10.12.0";
+CC=clang
+FRAMEWORKS=-framework Cocoa -framework DFRFoundation -F /System/Library/PrivateFrameworks
+CFLAGS=-mmacosx-version-min=10.12 -x objective-c
+LDFLAGS=-fobjc-link-runtime $(FRAMEWORKS)
+SOURCES=touchtest.m
+OBJECTS=touchtest.o
+OUT=touchtest
+BUNDLE=touchtest.app
+
+all: $(SOURCES) $(OUT)
+ @mkdir -p "$(BUNDLE)/Contents/MacOS"
+ @cp "$(OUT)" "$(BUNDLE)/Contents/MacOS/"
+ @cp Info.plist "$(BUNDLE)/Contents"
+
+run: all kill
+ @open "$(BUNDLE)"
+
+$(OUT): $(OBJECTS)
+ $(CC) $(OBJECTS) $(LDFLAGS) -o $(OUT)
+
+.m.o:
+ $(CC) -c $(CFLAGS) $< -o $@
+
+kill:
+ @pkill $(OUT) ; true
+
+clean:
+ @rm -f *~ $(OUT) *.o
+ @rm -rf "$(BUNDLE)"
+# TouchTest - MacOS Touch Bar Control Strip daemon
+
+Example of adding an icon to the Control Strip region of the Mac Touch Bar using
+a headless/UI-less background app. Icon is always displayed, regardless of which
+app is in the foreground.
+
+Uses a private API, not suitable for the App Store.
+
+No XCode project, no Nib/Xib, builds from command-line. Just the minimum
+necessary to add an always-available button to the Control Strip.
+
+Inspired by: https://github.com/a2/touch-baer
+
+## Usage
+
+### Build
+```
+$ make
+```
+
+### Build and launch
+```
+$ make run
+```
+
+### Kill running daemon
+```
+$ make kill
+```
+
+### Cleanup
+```
+$ make clean
+```
+
+### Design Notes
+
+Links against private framework DFRFoundation.
+
+Uses two DFRFoundation functions: ```DFRElementSetControlStripPresenceForIdentifier``` and ```DFRSystemModalShowsCloseBoxWhenFrontMost```
+
+Uses private NSTouchBarItem method: ```+(void)addSystemTrayItem:(NSTouchBarItem *)item;```
+
+Uses private NSTouchBar method: ```+(void)presentSystemModalFunctionBar:(NSTouchBar *)touchBar systemTrayItemIdentifier:(NSString *)identifier;```
+
+Simply creates a windowless NSApplication and an NSApplicationDelegate implementing the private functions.
+
+**MUST** execute from a mac _.app_ bundle. The TouchBar service doesn't seem to communicate with a binary when launched outside of a bundle. I did not investigate why.
+pkill a.out ; MACOSX_DEPLOYMENT_TARGET=10.12 clang -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk -fobjc-link-runtime -mmacosx-version-min=10.12 -framework DFRFoundation -framework Cocoa -F /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk/System/Library/PrivateFrameworks/ -x objective-c touch2.m && cp a.out test.app/Contents/MacOS/ && open test.app
+#import <Cocoa/Cocoa.h>
+
+static const NSTouchBarItemIdentifier kGroupButton = @"com.trevorbentley.group";
+static const NSTouchBarItemIdentifier kButton1 = @"com.trevorbentley.b1";
+static const NSTouchBarItemIdentifier kButton2 = @"com.trevorbentley.b2";
+
+extern void DFRElementSetControlStripPresenceForIdentifier(NSString *, BOOL);
+extern void DFRSystemModalShowsCloseBoxWhenFrontMost(BOOL);
+
+@interface NSTouchBarItem ()
++ (void)addSystemTrayItem:(NSTouchBarItem *)item;
+@end
+
+@interface NSTouchBar ()
++ (void)presentSystemModalFunctionBar:(NSTouchBar *)touchBar systemTrayItemIdentifier:(NSString *)identifier;
+@end
+
+@interface AppDelegate : NSObject <NSApplicationDelegate,NSTouchBarDelegate>
+@end
+
+NSTouchBar *_groupTouchBar;
+
+@implementation AppDelegate
+
+- (NSTouchBar *)groupTouchBar {
+ if (!_groupTouchBar) {
+ NSTouchBar *groupTouchBar = [[NSTouchBar alloc] init];
+ groupTouchBar.defaultItemIdentifiers = @[ kButton1, kButton2 ];
+ groupTouchBar.delegate = self;
+ _groupTouchBar = groupTouchBar;
+ }
+ return _groupTouchBar;
+}
+
+-(void)setGroupTouchBar: (NSTouchBar*)bar {
+ _groupTouchBar = bar;
+}
+
+- (void)button:(id)sender {
+ NSLog(@"button");
+}
+
+- (void)present:(id)sender {
+ [NSTouchBar presentSystemModalFunctionBar:self.groupTouchBar
+ systemTrayItemIdentifier:kGroupButton];
+}
+
+- (NSTouchBarItem *)touchBar:(NSTouchBar *)touchBar
+ makeItemForIdentifier:(NSTouchBarItemIdentifier)identifier
+{
+ NSCustomTouchBarItem *item = nil;
+ if ([identifier isEqualToString:kButton1]) {
+ item = [[NSCustomTouchBarItem alloc] initWithIdentifier:kButton1];
+ item.view = [NSButton buttonWithTitle:@"B1" target:self action:@selector(button:)];
+ }
+ else if ([identifier isEqualToString:kButton2]) {
+ item = [[NSCustomTouchBarItem alloc] initWithIdentifier:kButton2];
+ item.view = [NSButton buttonWithTitle:@"Quit" target:[NSApplication sharedApplication] action:@selector(terminate:)];
+ }
+ return item;
+}
+
+- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
+{
+ DFRSystemModalShowsCloseBoxWhenFrontMost(YES);
+ NSCustomTouchBarItem *item = [[NSCustomTouchBarItem alloc] initWithIdentifier:kGroupButton];
+ item.view = [NSButton buttonWithTitle:@"\U0001F4A9\U0001F4A9" target:self action:@selector(present:)];
+ [NSTouchBarItem addSystemTrayItem:item];
+ DFRElementSetControlStripPresenceForIdentifier(kGroupButton, YES);
+}
+
+- (void)applicationWillTerminate:(NSNotification *)aNotification {
+ [_groupTouchBar release];
+ _groupTouchBar = nil;
+}
+@end
+
+int main(){
+ [NSAutoreleasePool new];
+ [NSApplication sharedApplication];
+ AppDelegate *del = [[AppDelegate alloc] init];
+ [NSApp setDelegate: del];
+ [NSApp setActivationPolicy:NSApplicationActivationPolicyAccessory];
+ [NSApp run];
+ return 0;
+}