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.

Screen Arrangements

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

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

  1. Install displayplacer using Homebrew
  2. Use Keyboard Maestro and create a Macro
  3. Add the Execute Shell Script Action for each display you have (I have 3).
  4. Open System Preferences > Displays and change your arrangement by placing the menu bar to the right by 1 screen (right-to-left type direction)
  5. Open your terminal and run the displayplacer list command
  6. 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>"
  7. Paste the output into your Macro Action.
  8. Repeat steps 4-7 and stop after you've done the main screen you keep your menu bar on.
  9. Setup a keyboard shortcut for your Macro.
  10. Run your Macro with your new shortcut and watch the magic happen right before your eyes.

Helpful Tips

  1. 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 your displayplacer binary reference.

  2. Here's a screenshot to view my working Keyboard Maestro setup with displayplacer.

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