This post is part of a larger article, found here: https://andrewchidden.com/long-live-the-macbook-pro-with-touch-bar/
Goals
In designing for my needs as a power user, I kept these goals in mind:
- Enhance a development-oriented workflow.
- Remain un-distracting and aesthetically pleasing.
- Keep the power consumption as low as possible.
I believe my setup meets all three goals, turning the MacBook Pro with Touch Bar into my preferred development environment.
App Region
Git Diff Statistics
When working on a project, I want to know whether the working directory is clean (so I can rebase or check out safely), or the size of my next pull request.
Text Label: Displays the number of files changed and the lines added / removed for both staged and unstaged files.
Single Press: Opens GitUp, an open-source Git interface for macOS.
Long Press: Pushes the current branch and shows the page for creating a new pull request on the branch.
Implementation: The widget uses caching to reduce locking on the Git index; it first checks git --no-optional-locks status
before performing git diff HEAD
. See also the Git documentation on best practices for background status refresh.
Xcode Build Status
Large Xcode projects can take a long time to build, especially after cleaning intermediaries. (Longer than just grabbing a cup of coffee.) I need a way of checking on the progress of builds and tests, and the status whether it failed or succeeded.
Text Label: If building, shows the name of the last intermediate product built, such as a static library. If the build / test failed, it shows the number of errors generated or a readable error message.
Single Press: Opens or focuses the main project workspace in Xcode.
Long Press: Reveals the main project folder in Finder.
Implementation: Xcode doesn’t provide any useful AppleScript hooks for determining current build progress. Rather than using xcodebuild
which would break my existing workflow around Xcode, to show progress I instead monitor changes to the intermediary build products in the derived data directory.
Once a build completes or a test suite finishes, the script takes the latest gzipped build log and analyzes it for error messages. I built the system to match my own build pipeline so it is unlikely to work or add value for most developers; I won’t be including this widget in the public preset.
Next Calendar Event
Seeing the next event and time left allows me to better plan tasks throughout the day. Plus, it gives me reassurance against inadvertently missing a meeting.
Text Label: Shows the name of the next calendar event and a readable relative time: the number of hours if more than one hour, the number of minutes if less than an hour. If the event already started, it shows the remaining time left.
Single Press: Opens the native Calendar application.
Long Press: Opens the Google Calendar webpage. I find this useful because despite its nice UX, Apple Calendar lacks certain core functionality offered by Google, such as booking meeting rooms.
Implementation: I wanted the text label to update within seconds whenever the next calendar event changes. Unfortunately, the open-source command line utility icalBuddy which is often used in BTT presets suffers from poor performance, lack of maintenance (last commit was almost five years ago), and must poll the Calendar Agent for changes.
In order to push updates to BTT and reduce overall energy impact, I wrote a custom command line utility that acts as a persistent service between EventKit
and BTT. The utility listens for calendar changes and pushes a refresh_widget
request to BTT’s local web server where the widget’s controller script handles the appearance update.
Control Strip
Brightness
Changing the screen brightness is a standard system-level control. To maximize usefulness, the buttons reproduce the native handling of modifier keys and incorporate additional functionality through long press gestures.
Single Press: Turns screen brightness up / down.
Long Press Brightness Down: Locks the screen.
Shift Press: Turns keyboard illumination up / down.
Option Press: Opens the Displays preference pane in System Preferences.
Option + Shift Press: Uses small steps to turn screen brightness up / down.
Implementation: In its default configuration, BTT cannot handle such a complex gesture set. (To be precise, there’s no way to not perform a default button action but also handle modifier keys.) Instead, I wrote a very small (~100 LOC) command line utility that handles modifier key logic and synthesizes the corresponding NSEvent
objects.
Here be Dragons: Events created with type NSEventTypeSystemDefined
and posted using CGEventPost(kCGHIDEventTap, ...)
must have a magic delay after both the key down and key up events. Not doing so often results in a race condition between the two events.
Volume
The volume control works similarly to brightness, but also changes appearance based on current volume and mute status. Increasing the volume activates more “sound waves” in the icon:
Unlike the system control, the muted appearance preserves the last icon state; this is particularly useful in indicating if the volume will be loud or soft when unmuting.
Single Press: Turns volume up / down.
Long Press Volume Down: Toggles mute / unmute.
Shift Press: Plays the system feedback sound effect.
Option Press: Opens the Volume preference pane in System Preferences.
Option + Shift Press: Uses small steps to turn volume up / down.
Implementation: The volume control doesn’t just synthesize events, but also updates the icon appearance in real time. It uses the same command line utility as brightness for key presses but also a custom push-based service similar to calendar for volume change events.
Here be Dragons: To make the control feel responsive, it’s necessary to keep the latency low between a volume change event and the Touch Bar icon updating. I found that sending HTTPS requests to BTT incurred a non-negligible latency overhead compared to HTTP.
Clock App
Hiding the system menu bar frees up valuable vertical screen space, but also hides the time. Moving the clock to the Touch Bar fixes the issue and provides a convenient way to access additional features.
World Clock
I often want to know what time it is in Stockholm or New York City. By tapping the clock, I can immediately see the day of the week, date, and times for points of interest around the world. Tapping the local time again dismisses the modal.
Countdown Timer
Timers can provide utility in many contexts, ranging from Pomodoro timers to simple mental reminders. Unfortunately, macOS doesn’t come with a built-in timer application, and certainly not one for the Touch Bar.
Long pressing the clock reveals a minimal set of options to start a timer or cancel the existing one.
Implementation: I built the timer with simple source-embedded shell scripts to avoid the need to compile yet another custom command line utility.
Secondary Widgets
Workflow macros can offer power users a great deal of utility with very little work or setup. I’ve created a handful of destructive / not undoable macros (in red) as well as a few general-purpose ones (in gray). These macros only show while I hold down the three bottom-row modifier keys (command + option + control).
Each macro simply opens a Terminal window with the pre-defined commands. Macros execute in a Terminal window rather than in the background. Visual execution of commands allows error states or those that require user interaction (such as entering an RSA key passphrase) to work seamlessly.
Using the Preset
Caveats
I designed this preset for my own use-cases and workflow. Power users who have non-development workflows, don’t use trackpad gestures for Exposé, or keep the MacBook Pro in clamshell mode / on a stand may not find much utility in this preset. If you find this to be the case, consider perusing other presets created by the BetterTouchTool community.
Installation
- If not already installed, download and install BetterTouchTool.
-
UPDATE (Aug 28): The preset was exported and tested using BTT v2.536 and may not work for the recently released BTT v2.6xx. You can download
btt2.536.zip
here: https://bettertouchtool.net/releases/
-
UPDATE (Aug 28): The preset was exported and tested using BTT v2.536 and may not work for the recently released BTT v2.6xx. You can download
- Download the latest controller scripts and compiled service binaries for the preset: andrewchidden/btt-controllers.
-
git clone
the repository to have the path~/bettertouchtool
. - Alternatively, download the zip archive, decompress, and move + rename as
~/bettertouchtool
.
-
- Decide on a preset flavor:
- andrewchidden-with-settings.bttpreset if new to BetterTouchTool. Contains everything including BetterTouchTool settings to allow the preset to function as intended.
- andrewchidden.bttpreset if already using BetterTouchTool. Only contains controls and triggers. You will need to manually configure parts of the preset and/or BTT after installation.
- Download from a mirror:
- BetterTouchTool (share.folivora.ai)
- andrewchidden-with-settings.bttpreset (v1.0.1) Direct Import or Source Viewer
- andrewchidden.bttpreset (v1.0.1) Direct Import or Source Viewer
- andrewchidden/btt-presets (github.com)
- BetterTouchTool (share.folivora.ai)
- Configure the preset—please look at the next section for instructions. This is required for the preset to work!
What BetterTouchTool should look like after importing the preset, minus the red entries which are private widgets:
Configuration
For complete environment variable documentation, please see the README at andrewchidden/btt-presets.
Note: To set some environment variable <env_var>
to value <value>
, modify (or create) the file ~/.bash_profile
to include the line export <env_var>=<value>
.
- Identify where you downloaded the controller scripts. Ideally they already have the path
~/bettertouchtool
.- If this is not the case, set the environment variable
BTT_USR_ROOT
to the controllers directory.
- If this is not the case, set the environment variable
- Set the environment variables
BTT_WEBSERVER_URL
andBTT_WEBSERVER_SHAREDSECRET
corresponding to the BetterTouchTool web server URL and shared secret. Can be found by enabling “Advanced” mode, opening “Advanced Settings,” and then navigating to the “Webserver” tab.- The URL should be in the format
protocol://address:port
and will default tohttp://127.0.0.1:64875
if not specified.
- The URL should be in the format
- Set the environment variable
BTT_EVENTKIT_CALENDAR_NAMES
to a comma-delimited, case-sensitive list of calendar names that should be checked for upcoming events. - Set the environment variable
BTT_GIT_WORKING_DIR
to the primary working directory for Git diff statistics and macro-triggered Git operations. - If necessary, modify the “General Touch Bar Settings” to match those below:
Source
The project is separated into three repositories:
- btt-presets contains the preset JSON configurations.
- btt-controllers contains the required controllers and pre-built services for the preset.
- btt-services contains the source of services used by the controllers.
The setup loosely follows the model-view-controller pattern, with the services as the model and presets as the view.
Both services and controllers employ good test coverage with OCMockito used to perform unit tests on services and Bats used to perform unit and integration tests on controllers.
Contributions are welcomed
BetterTouchTool Development Tips
If you currently develop or are considering developing presets for BetterTouchTool, the article also contains a set of tips for working with BTT, found here: https://andrewchidden.com/long-live-the-macbook-pro-with-touch-bar/#bettertouchtooldevelopmenttips