Skip to content

Instantly share code, notes, and snippets.

@mpoisot
Created October 29, 2020 15:25
Show Gist options
  • Save mpoisot/5dc29d3aeb2221a8c344240630453bb2 to your computer and use it in GitHub Desktop.
Save mpoisot/5dc29d3aeb2221a8c344240630453bb2 to your computer and use it in GitHub Desktop.
Set multi-monitor display arrangement for a MacBook based on which USB C port the monitor plugs in to
#!/usr/bin/env sh
# Call this script from an EventScripts trigger. https://www.mousedown.net/software/EventScripts.html
# Depends on `displayplacer`. https://github.com/jakehilborn/displayplacer
# On my macbook, when my external screen is plugged into the right USB ports I get one contextual ID running `displayplacer list`. When plugged into the left ports I get a different ID.
# 27" LG info from `displayplacer list`, connected to right USB ports:
# Persistent screen id: 1B2915BF-F01F-DFDA-9605-FB0311DB5E03
# Contextual screen id: 458633946
# EventScripts has an easy GUI to let you add your own script, and set the trigger from a big list of system events. I found both "USB device attached" and "Spaces Changed" work in terms of detecting that my monitor is connected (via a USB C to HDMI dock). The problem is that both events are trigger too often for the simple command line @jakehilborn suggested (`displayplacer [config1] || displayplacer [config2]`).
# When my Anker USB C dock is connected, it registers as 6 different USB devices: ethernet, SD card reader, etc etc. With EventScripts triggering on USB Added events, this resulted in my displayplacer script getting called 6 times in a row, and it took a good 15 seconds of screen flickering before things settled down. Yikes. And I would get a flicker for any other USB stuff I might connect, unrelated to the monitor's dock. Grrr.
# Next I tried triggering EventScripts based on Spaces Changed. This does result in just one event when I connect the monitor. But it also triggers every time I move around spaces - which I do A LOT. These events queue up and result in absurd amounts of screen flickering. Grrr.
# What I needed was for my script to do nothing when the screen is already arranged correctly. I looked at the output of `displayplacer list` and realized I could grep a things and make my script a little smarter.
# Is the external monitor connected at all? Is it connected to left or right ports? Is the config already set? Only at the end of all that set the config, otherwise do nothing. Then it doesn't matter how often the script is called with false positives.
# One gotcha I encountered is that it's hard to make a shell variable that contains double quotes. I ended up just using the full ugly string twice instead of nicer variables.
##########################################################################
# EventScripts requires this script to live in a special folder that doesn't have write
# permission, and I don't see output from this script in the system console. So log to a
# file in my home folder.
LOGFILE=/Users/marcel/tmp/display_config.log
# Your displayplacer binary.
DISPLAY_PLACER=/usr/local/bin/displayplacer
# Info regarding external monitor from `displayplacer list`
# The permanent ID never changes. The contextual ID depends on a number of factors.
# For my macbook with 4 USB C ports, it reports one ID for left ports, another
# ID for rights ports.
SCREEN_PERM_ID=1B2915BF-F01F-DFDA-9605-FB0311DB5E03
SCREEN_RIGHT_ID=458633946
SCREEN_LEFT_ID=458633945
echo "Starting display_config" > $LOGFILE
# EventScripts passes a bunch of extra params.
# echo $@ >> $LOGFILE
if ${DISPLAY_PLACER} list | grep ${SCREEN_PERM_ID}; then
echo "External 27 inch LG screen detected." >> $LOGFILE
if ${DISPLAY_PLACER} list | grep ${SCREEN_RIGHT_ID}; then
echo "Screen plugged into right port." >> $LOGFILE
# Exact command suggested by `displayplacer list` when monitor connected to right ports and arranged correctly.
if ${DISPLAY_PLACER} list | grep '"id:1B2915BF-F01F-DFDA-9605-FB0311DB5E03 res:1920x1080 hz:30 color_depth:8 scaling:on origin:(0,0) degree:0" "id:FC1D8AEF-83A6-B0CC-9673-6E13DCF9CCAC res:1440x900 color_depth:8 scaling:on origin:(-1440,874) degree:0"'; then
echo "Already set to right config. Ignoring." >> $LOGFILE
else
echo "Setting right side config." >> $LOGFILE
# Same exact same string as above
${DISPLAY_PLACER} "id:1B2915BF-F01F-DFDA-9605-FB0311DB5E03 res:1920x1080 hz:30 color_depth:8 scaling:on origin:(0,0) degree:0" "id:FC1D8AEF-83A6-B0CC-9673-6E13DCF9CCAC res:1440x900 color_depth:8 scaling:on origin:(-1440,874) degree:0"
fi
fi
if ${DISPLAY_PLACER} list | grep ${SCREEN_LEFT_ID}; then
echo "Screen plugged into left port." >> $LOGFILE
# Exact command suggested by `displayplacer list` when monitor connected to left ports and arranged correctly.
if ${DISPLAY_PLACER} list | grep '"id:1B2915BF-F01F-DFDA-9605-FB0311DB5E03 res:1920x1080 hz:30 color_depth:8 scaling:on origin:(0,0) degree:0" "id:FC1D8AEF-83A6-B0CC-9673-6E13DCF9CCAC res:1440x900 color_depth:8 scaling:on origin:(259,1080) degree:0"'; then
echo "Already set to left config. Ignoring." >> $LOGFILE
else
echo "Setting left side config." >> $LOGFILE
# Same exact same string as above
${DISPLAY_PLACER} "id:1B2915BF-F01F-DFDA-9605-FB0311DB5E03 res:1920x1080 hz:30 color_depth:8 scaling:on origin:(0,0) degree:0" "id:FC1D8AEF-83A6-B0CC-9673-6E13DCF9CCAC res:1440x900 color_depth:8 scaling:on origin:(259,1080) degree:0"
fi
fi
fi
echo "done" >> $LOGFILE
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment