A standard set of responsive, energy efficient widgets (volume / brightness / calendar / timer / world clock / git)

touch-bar
webserver

#1

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:

  1. Enhance a development-oriented workflow.
  2. Remain un-distracting and aesthetically pleasing.
  3. 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

new-appregion-optimized

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.

touchbar-screen-appregion-gitdiff-1

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.

touchbar-screen-appregion-xcode-1

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.

touchbar-screen-appregion-calendar-1

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

controlstrip-optimized

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:

touchbar-screen-vol-combined-inv-1

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.

touchbar-screen-worldtimes-2

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.

touchbar-screen-timerapp

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.

touchbar-screen-secondarylayout


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

  1. 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/
  2. 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.
  3. 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.
  4. Download from a mirror:
  5. 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:

bettertouchtool-preset-preview

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>.

  1. 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.
  2. Set the environment variables BTT_WEBSERVER_URL and BTT_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 to http://127.0.0.1:64875 if not specified.
  3. 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.
  4. Set the environment variable BTT_GIT_WORKING_DIR to the primary working directory for Git diff statistics and macro-triggered Git operations.
  5. If necessary, modify the “General Touch Bar Settings” to match those below:

touchbar-settings-1

Source

The project is separated into three repositories:

  1. btt-presets contains the preset JSON configurations.
  2. btt-controllers contains the required controllers and pre-built services for the preset.
  3. 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 :slight_smile:


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


(Updated) Testing the battery life impact of AppleScript widgets
Modifier keys for Volume (Small Step)
AppleScriptRunner causes high CPU, log warnings every few ms
#2

This looks really cool, gonna give it a try later :smile: I wish that volume indicator was possible without a helper.


#3

Thanks! I agree—I wish some of the Control Strip widgets were available out-of-the-box with BetterTouchTool, but I’m thankful BTT has the flexibility to support such widgets regardless.


#4

Check out my latest update, I got the dynamic volume button working with no external helpers :slightly_smiling_face: GoldenChaos-BTT: A complete Touch Bar UI replacement preset


#6

Nice! The the volume service isn’t really for managing the dynamic appearance though—that’s the responsibility of the controller shell script—but instead to instantaneously update the icon whenever there’s a volume event.

I also have grave concerns about the power consumption of running AppleScript routines frequently. I’m planning on doing some empirical tests this weekend to see the actual power draw, but I’d wager (based on nothing whatsoever) that it reduces battery life by at least thirty minutes.


#7

Interesting on the battery life, curious to see what you find!


#8

I also have concerns about continually running scripts.

FWIW I update the volume icons (and all other icons when applicable) by simply doing the 'attach additional action' to run a script that refreshes the UUID through BTT ie:

tell application "BetterTouchTool"
refresh_widget "2D4B88AC-9244-4054-9CA9-33E87B84613C"
end tell

I put the script to run every 0 seconds so it only refreshes on press. It works pretty well. The only problem I've had is when unplugging/plugging in headphones since the volume sometimes changes or mutes in the background.


#9

Compiled some preliminary battery life results here:


#10

I think I am doing something wrong, i am new too BTT but something seems to go wrong. I followed your installation and configuration step carefully. I edited my .bash_profile to include those env variables but it keeps looking weird. The buttons do work but they are heavily overlapping en not positioned correctly.

Any tips? Sorry if this is a stupid question. I am just really intrigued to get it to work and see if it enhances my dev workflow!


#11

Hey, thanks for trying out the preset. I’m guessing you’re using the very recently released 2.6x version of BTT which isn’t 100% backwards compatible. I created the preset on BTT v2.536, which you can find here as btt2.536.zip: https://bettertouchtool.net/releases/

You might also need to manually set the Touch Bar settings if BTT didn’t import/export them properly. This is what mine looks like:


#12

I am using the version you suggested and the layout problems are fixed but it the volume/brightness controls don't seem to work.

Here is a screenshot of the TouchBar. I have hidden the items not part of the control strip for clearance.

As you see above the volume up icon is not there (weird).

So far this is what works:

  • Hold volume down to mute
  • Hold brightness down to lock screen

what does not work:

  • single press to turn volume/brightness up or down (option + shift press same thing)
  • option press brightness buttons to open display preference
  • options press volume buttons to play system feedback sound

So in conclusion only the long press actions seem to work. I hope this feedback can help you fix these bugs.

side note: I am using MacBook Pro 13 inch 2017


#13

It sounds like you didn’t download and/or install the controllers. Can you confirm that you installed the controllers to ~/bettertouchtool?


#14

Sure, I followed your instructions and read a bit trough the bash scripts. Below is a screenshot of my home folder with the bettertouchtool folder inside


#15

What do you get when you run this command in Terminal?

btt_usr_root=${BTT_USR_ROOT:-~/bettertouchtool}; "${btt_usr_root}/control-strip/controlstrip-service" --type=volume --direction=0 --volume-feedback=1


#16

It turns my volume down and when I change direction to 1 it goes up. When I change volume to brightness it turn the brightness up/down depending on direction. So it works for that.


#17

Ok, so when you downloaded BTT v2.536 did you completely re-import the preset, or did you use the previously imported set?

If you select the “Volume Down” widget entry and click the gear next to “Predefined Action”, what shows up in the text field?


#18

This turns up:

It is the same command you send and thus the one that works inside the terminal.

edit: I also re-imported the settings as I moved versions because they were not there.


#19

Figured it out. It’s because you probably misnamed ~/.bash_profile (notice the dot before the bash_profile which makes it a system file). If this is the case, the following Terminal command should work to rename it:

mv ~/bash_profile ~/.bash_profile


#20

That is not the case, I named it correctly

inside of my .bash_profile


#21

Ah that’s your problem. You set BTT_USR_ROOT to the string literal "~/bettertouchtool". If you remove the quotes (or remove the line altogether) it’ll work.

Edit: To start the services now that the controller path is fixed (for calendar, git, etc), you can either toggle the app region visibility by tapping the time twice, or simply restart BTT.