summary history branches tags files
commit:80ac2d0eed54131d40605b777bf51f9ce2f19101
author:Trevor Bentley
committer:Trevor Bentley
date:Mon Jul 3 22:58:11 2017 +0200
parents:
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.
diff --git a/Info.plist b/Info.plist
line changes: +11/-0
index 0000000..23e9964
--- /dev/null
+++ b/Info.plist
@@ -0,0 +1,11 @@
+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";

diff --git a/Makefile b/Makefile
line changes: +29/-0
index 0000000..a84ea63
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,29 @@
+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)"

diff --git a/README.md b/README.md
line changes: +48/-0
index 0000000..623d7d4
--- /dev/null
+++ b/README.md
@@ -0,0 +1,48 @@
+# 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.

diff --git a/build.sh b/build.sh
line changes: +1/-0
index 0000000..f1125c0
--- /dev/null
+++ b/build.sh
@@ -0,0 +1 @@
+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

diff --git a/touchtest.m b/touchtest.m
line changes: +86/-0
index 0000000..77cadc6
--- /dev/null
+++ b/touchtest.m
@@ -0,0 +1,86 @@
+#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;
+}