Change display arrangement in OS X/macOS programmatically
I periodically have to move my display around (physically1) and this requires also rearranging my display arrangement in System Preferences. Manually doing this all the time is a pain in the neck. Is there any way to do it programmatically?
1 I work on one side of a table when I'm working solo, but when I want to pair with someone I flip to the other side of the table because it has more unused space available for a second person. Flipping the display around to face the other side of the table means the display goes from being to the right of my laptop to being to the left, since the laptop goes on a stand either way.
Solution 1:
Sorry for the late answer. You can use displayplacer to set profiles for both of your arrangements. Save the profiles in a script or use something like BetterTouchTool for a hotkey to change your monitor layout.
https://github.com/jakehilborn/displayplacer
Solution 2:
Using my MacBook Pro, which normally sits to the left of my Thunderbolt Display, and my Thunderbolt Display, the following will show how to use hsscreens and AppleScript to toggle the two arrangements programmatically based on how it's currently arranged. The end result is an AppleScript application that when run simply toggles back and forth between the two arrangements. This app can be run in all the normal ways any other application can be run.
In the image below, you see the normal and flipped arrangements of the Displays and Menu bar showing the difference relationships between my MacBook Pro and Thunderbolt Display.
This AppleScript application, along with the hsscreens
binary executable file, programmatically toggles between the two arrangements shown in the image above, shifting the MacBook Pro's Display to the right or left of the Thunderbolt Display maintaining the Menu bar on the MacBook Pro's Display.
To code this, I needed to determine what the current arrangement was and then act accordingly. To do this I used hmscreens -info
in a Terminal twice, once while in each arrangement.
Looking at the outputted information I saw a piece of info that I could grep
for, in order to the set a flag to toggle against.
Output of hmscreens -info
in a normal arrangement of the Displays:
$ hmscreens -info
Screen ID: 68822100
Size: {1440, 900}
Global Position: {{0, 0}, {1440, 900}}
Color Space: NSCalibratedRGBColorSpace
BitsPerPixel: 32
Resolution(dpi): {144, 144}
Refresh Rate: 0
Uses Quartz Extreme: YES
Screen ID: 68695180
Size: {2560, 1440}
Global Position: {{1440, -540}, {4000, 900}}
Color Space: NSCalibratedRGBColorSpace
BitsPerPixel: 32
Resolution(dpi): {72, 72}
Refresh Rate: 0
Uses Quartz Extreme: YES
$
Output of hmscreens -info
in a flipped arrangement of the Displays:
$ hmscreens -info
Screen ID: 68822100
Size: {1440, 900}
Global Position: {{0, 0}, {1440, 900}}
Color Space: NSCalibratedRGBColorSpace
BitsPerPixel: 32
Resolution(dpi): {144, 144}
Refresh Rate: 0
Uses Quartz Extreme: YES
Screen ID: 68695180
Size: {2560, 1440}
Global Position: {{-2560, -540}, {0, 900}}
Color Space: NSCalibratedRGBColorSpace
BitsPerPixel: 32
Resolution(dpi): {72, 72}
Refresh Rate: 0
Uses Quartz Extreme: YES
$
If was obvious, to me, I needed to use info from Global Position:
to determine what the current arrangement of the Displays were. I immediately saw -2560
as unique between the output and a way of determining the current arrangement. If I grep
ed for -2560
and found it then I was in the flipped arrangement, not the normal arrangement. So this is what I used to determine the current arrangement and thus toggle between the two arrangements. (Note: -2560
was not the only thing unique, it's just what I focused on first and felt an easy way to parse the information to set a toggle flag with.)
Save the AppleScript code below as an application and place a copy of the hmscreens
binary executable file within the <appname>.app/Contents/Resources
folder. This way the app, e.g. /Applications/Toggle Displays Arrangements.app
is self-contained and not dependent on an external location of the hmscreens
binary executable file.
Obviously you'll need to determine what to grep
for or use a different method to set the toggle flag and modify the code below accordingly to suite your particular needs (if other then this use case or a different use case).
AppleScript code:
set hms to (path to me as text) & "Contents:Resources:hmscreens"
set screenIDs to paragraphs of (do shell script quoted form of POSIX path of hms & " -screenIDs")
if (count of screenIDs) is greater than 1 then
set normalOrentation to "1"
try
set normalOrentation to do shell script quoted form of POSIX path of hms & " -info | grep '\\-2560'"
end try
if normalOrentation is "1" then
set othersPosition to "left"
else
set othersPosition to "right"
end if
set secondScreen to item 1 of screenIDs
do shell script quoted form of POSIX path of hms & " -setMainID " & secondScreen & " -othersStartingPosition " & othersPosition
else
tell me
activate
display dialog "Only one screen is attached to your computer!" buttons {"OK"} default button 1 with icon note
end tell
end if
The image below shows syntactical highlighting of the AppleScript code.
Solution 3:
I figured out how to automate the display switching using a keyboard shortcut with displayplacer
and it works like a charm!😎
I setup a macro using Keyboard Maestro to automatically cycle the desktop arrangement using a keyboard shortcut.
Instructions to setup Keyboard Shortcut
- Install displayplacer using Homebrew
- Use Keyboard Maestro and create a Macro
- Add the Execute Shell Script Action for each display you have (I have 3).
- Open System Preferences > Displays and change your arrangement by placing the menu bar to the right by 1 screen (right-to-left type direction)
- Open your terminal and run the
displayplacer list
command - Copy the bottom output, e.g.
displayplacer "id:<screenId> res:<width>x<height> hz:<num> color_depth:<num> scaling:<on/off> origin:(<x>,<y>) degree:<0/90/180/270>"
- Paste the output into your Macro Action.
- Repeat steps 4-7 and stop after you've done the main screen you keep your menu bar on.
- Setup a keyboard shortcut for your Macro.
- Run your Macro with your new shortcut and watch the magic happen right before your eyes.
Helpful Tips
If you have an issue with running
displayplacer
from Keyboard Maestro, it's likely that you're using a different bash in your terminal/iTerm like oh-my-zsh when you installed it using Homebrew. If thats the case, follow these instructions from my other post to fix yourdisplayplacer
binary reference.Here's a screenshot to view my working Keyboard Maestro setup with
displayplacer
.You may need to run your macro a second time if you accidentally hit your shortcut.
Solution 4:
This isn't AppleScript, but here is a python library that that interfaces with PyObjC bindings that provides in-depth control of Mac display settings called "Display Manager". We have updated Display Manager, which is an open-source python library which can modify a individual or fleet of Mac's display settings manually or automatically. It programmatically manages Mac displays, including display resolution, refresh rate, rotation, brightness, screen mirroring, and HDMI underscan. Its primary intended purpose is to allow system administrators and developers to automatically configure any number of Mac displays, by use of the command-line scripts and the Display Manager Python library. It can easily be integrated in Jamf Pro, Outset or Munki or other client management systems.
We use Display Manager in our environment for multiple use cases like on our digital display systems to rotate the display 90 degree's and exactly set the HDMI underscan, on podiums setting video mirroring and allow the instructor quickly toggle mirroring on or off, set default resolution & brightness on shared systems like student labs or staff/faculty systems and reset the default after at times like user login/logout or on demand, etc.
If you are interested in reviewing the code or trying it out see the following GitHub repository...
https://github.com/univ-of-utah-marriott-library-apple/display_manager
Please let us know if you have an question, problems or have feature requests.
Solution 5:
I used the examples above to create a simple apple script "Swap Screens".
display dialog "Swap Screens?" buttons {"Cancel", "OK"} default button "OK" cancel button "Cancel" giving up after 15
set hms to "Applications:hmscreens"
set screenIDs to paragraphs of (do shell script quoted form of POSIX path of hms & " -screenIDs")
if (count of screenIDs) is greater than 1 then
set firstScreen to item 1 of screenIDs
set secondScreen to item 2 of screenIDs
set othersPosition to "right"
do shell script quoted form of POSIX path of hms & " -setMainID " & secondScreen & " -othersStartingPosition " & othersPosition
else
display dialog "Only See One Screen"
end if
It worked like a champ ... I can click one icon and my screens align just like I want them to.
Enjoy! E-