3

Playing with AppleScript I want to manipulate the location of a few windows with position but I'm having an issue defining the monitor. Currently I have several different size and brand monitors I connect to with my MacBook during my travels. The only repeating occurrence with all four monitors I connect to is that the monitor I want to manipulate includes the menu bar.

When testing with do shell script "system_profiler SPDisplaysDataType I'm not sure if the returned text of Main Display: Yes considers the main to be where the menu bar resides from the extraction:

Graphics/Displays:

    Intel Iris Pro:

      Chipset Model: Intel Iris Pro
      Type: GPU
      Bus: Built-In
      VRAM (Dynamic, Max): 1536 MB
      Metal: Supported
      Displays:
        Color LCD:
          Display Type: Built-In Retina LCD
          Resolution: 2880 x 1800 Retina
          Mirror: Off
          Online: Yes
          Rotation: Supported
          Automatically Adjust Brightness: Yes
          Connection Type: DisplayPort
        Thunderbolt Display:
          Display Type: LCD
          Resolution: 2560 x 1440
          Main Display: Yes
          Mirror: Off
          Online: Yes
          Rotation: Supported
          Automatically Adjust Brightness: No
          Connection Type: DisplayPort

If my assumption of the Main Display is correct how should I properly grep the resolution of the identified main display so I can manipulate the windows?

When I research it would appear some commonly just step into each display:

So I don't know if this would be two questions in one. I'm somewhat confused on how the sequence is determined when connecting to multiple monitors (such as today I might be connected to a thunderbolt display but tomorrow I might be connected to two Dells monitors through my two DisplayPorts) and if there is a way to target a monitor in particular based on the menu bar.

I can already detect the app and it's size with:

tell application "System Events" to tell application process "Notes" to set theSize to get size of window 1
set theWidth to item 1 of theSize
set theHeight to item 2 of theSize

but my issue falls in determining the resolution so I can calculate the position and bounds to move the app window.

3 Answers 3

3

UPDATE

Removed my original answer. Here is a completely different approach which I prefer over my original solution. This version actually uses “Image Events” scripting addition.

This script will get the names of the displays connected to your computer. The returned values of “display "Display 1" will always be “which monitor has the menu bar in AppleScript?”

property activeDesktop : missing value
property activeDesktopResolution : missing value

set displayNames to {}

tell application "Image Events"
    set theDisplays to displays
    repeat with i from 1 to number of items in theDisplays
        set this_item to item i of theDisplays
        set theName to name of display profile of item i of theDisplays
        set end of displayNames to theName
        set activeDesktop to item 1 of displayNames
    end repeat
end tell

tell application "Finder"
    set activeDesktopResolution to bounds of window of desktop
    set activeDesktopResolution to items 3 thru 4 of activeDesktopResolution
end tell

set theResultz to display dialog activeDesktop & "  " & item 1 of activeDesktopResolution & " x " & item 2 of activeDesktopResolution ¬
    buttons ¬
    "OK" with title ¬
    "Your Current Display and Its Resolution" with icon 1 ¬
    giving up after 10

enter image description here enter image description here enter image description here enter image description here enter image description here

2
  • Can you please clarify how this is supposed to work for multiple monitors? On my twin monitor setup the result of bounds of window of desktop is always the total rectangle that includes both displays, so a 1920 x 1080 next to a 1280 x 800 gives 3200 x 1080 every time.
    – Haravikk
    May 30, 2020 at 16:58
  • In this following image, you will see that the menu bar, which is circled in red, can be moved back and forth between connected displays… in System Preferences/Displays, when the option to “Mirror Displays” is deselected. Whichever display contains the menu bar, Will be in the display dimensions that the AppleScript code returns. i.imgur.com/vPD5upS.jpg
    – wch1zpink
    May 30, 2020 at 18:02
2

In my testing, you are correct that the main display is the one with the menu assigned in Displays System Prefs. I tested by moving the menu bar from one display to the other and running your script. The results suggest that wherever the menu bar is, that display will have "Main Display: Yes" in its information.

To parse the resolution, I would use the -xml argument to the system_profiler command to get some consistent text to look for, and then use Apple's text item delimiters to parse the data. I wrote this script, and when I switch the menu back and forth, it returns the resolution of the current main display. You may need to tweak the delimiters depending on what your OS returns for the system profiler report.

I added inline comments in the code below to explain what the script is doing.

set pfl to do shell script "system_profiler -xml SPDisplaysDataType"

set tid to text item delimiters
set text item delimiters to "</data><key>_name</key>"--in my reports, each display section starts with this xml code

ignoring white space --allows us to not worry about returns, tabs, spaces, etc.
    repeat with i from 2 to count of pfl's text items
        set aDisplay to pfl's text item i
        if aDisplay contains "<key>spdisplays_main</key><string>spdisplays_yes</string>" then--check if this display is the main
            set text item delimiters to "<key>_spdisplays_resolution</key><string>"--this is the xml code for the resolution
            set displayRes to aDisplay's text item 2
            set text item delimiters to "</string"--remove the ending xml code
            set displayRes to displayRes's text item 1
        end if
    end repeat
end ignoring
set text item delimiters to tid --always text text item delimiters back to default
return displayRes

EDIT: Because you posted the regular form of the system_profiler above, I re-wrote the script to parse that output. It seems a bit riskier to me than using the xml, but it should compile and run correctly on your system without any tweaks.

set pfl to do shell script "system_profiler SPDisplaysDataType"
set tid to text item delimiters
set text item delimiters to "Displays:"
set pfl to text item 3 of pfl
set text item delimiters to "Mirror:"
repeat with i from 1 to count of pfl's text items
    set aDisplay to pfl's text item i
    ignoring white space
        if aDisplay contains "Main Display: Yes" then
            if aDisplay contains "Resolution:" then
                set text item delimiters to "Resolution: "
                set displayRes to text item 2 of aDisplay
                considering white space
                    set text item delimiters to "\r" --line breaks should be \r
                    set displayRes to text item 1 of displayRes
                    set text item delimiters to "\n" --or they might be \n
                    set displayRes to text item 1 of displayRes
                end considering
            end if
        end if
    end ignoring
    set text item delimiters to "Mirror:"
end repeat
set text item delimiters to tid
return displayRes
5
  • If I copy and paste your code into Script Editor, while it does compile, nonetheless, it errors out. Am I supposed to change something in the code you posted to make it work, if so what? Oct 24, 2017 at 19:45
  • You would probably need to check the xml report it returns to make sure the the things I set to text item delimiters are the same on your system. You can add a line of code "return pfl" right after the first line of code to see what your system is returning. If, for example, "</data><key>_name</key>" doesn't exist (ignoring white space) in your report, you would have to change it to whatever comes right before the display's name. If "<key>_spdisplays_resolution</key><string>" doesn't exist, change it accordingly. I'm running Sierra 10.12.6, it's possible you have a dif version with dif report.
    – Hurston
    Oct 24, 2017 at 20:10
  • If you want to post the xml here (what pfl is in that script), I'll look for the delimiters and revise the script to reflect that.
    – Hurston
    Oct 24, 2017 at 20:16
  • Your update as coded returns with a leading space, e.g. " 2560 x 1440" but if you add a space after Resolution, e.g. set text item delimiters to "Resolution: " it remove it, e.g "2560 x 1440" Oct 24, 2017 at 21:26
  • added the space.
    – Hurston
    Oct 24, 2017 at 21:30
2

Tested on my MacBook Pro with a Thunderbolt Display attached and the Menu Bar on it, the following worked:

set theMainDisplayName to do shell script "system_profiler SPDisplaysDataType | grep -B 5 'Main Display:' | awk '{sub(/^[ \t]+/, \"\"); print $0; exit}'"
return theMainDisplayName

Result:
"Thunderbolt Display:"

If I switch the Menu Bar to the MacBook Pro, it returns the name of the Display of it, instead of the name of the Thunderbolt Display.

For the Resolution of my Thunderbolt Display:

set theMainDisplayResolution to do shell script "system_profiler SPDisplaysDataType | grep -B 3 'Main Display:' | awk '/Resolution:/{print $2,$3,$4}'"
return theMainDisplayResolution

Result:
"2560 x 1440"

If I switch the Menu Bar to the MacBook Pro, it returns the resolution of the Display of it, instead of the resolution of the Thunderbolt Display.


Also, have a look at my answers to, Script to relocate the menu-bar on dual monitors set-up and Change display arrangement in OS X programmatically for a couple of third-party utilities you might also find useful.

Note: I have no affiliation with the developers of the utilities mentioned and used in my answers in the links above.


Understanding a bit about the shell commands used in the do shell script commands:

Command portion of the do shell script command:

system_profiler SPDisplaysDataType | grep -B 5 'Main Display:' | awk '{sub(/^[ \t]+/, \"\"); print $0; exit}'
  • system_profiler - Reports system hardware and software configuration.
    • SPDisplaysDataType - Generates a text report containing only about Graphics and Displays.
  • | Pipe to direct output of proceeding command to input of next command.
  • grep - File pattern searcher. The grep utility searches any given input files, selecting lines that match one or more patterns.
    • −B num - Print num lines of leading context before each match.
    • 'Main Display:' Pattern to match.
  • | Pipe to direct output of proceeding command to input of next command.
  • awk - Pattern-directed scanning and processing language.
    • sub(/^[ \t]+/, \"\") - Substitute String Function - sub(regexp, replacement) - Used in this instance to remove leading whitespace.
      • ^ - Asserts position at start of the string.
      • [ \t] - Match a single character present in the list.
        • space - Matches the space character literally. Note that space is used here to denote a single space character (ASCII 32).
        • \t - Matches a tab character (ASCII 9).
      • + - Matches between one and unlimited times, as many times as possible, giving back as needed (greedy).
      • \"\" - Replacement is with nothing (""). The "" are escaped, \, so the command can be compiled and passed to the shell. If entered directly on the command line the "" would not need to be escaped.
    • print $0 - Prints the full record. - In this case it prints the fifth line above Main Display:, san the leading whitespace.
    • exit - Exits after printing the first record.

Command portion of the do shell script command:

system_profiler SPDisplaysDataType | grep -B 3 'Main Display:' | awk '/Resolution:/{print $2,$3,$4}'

In this do shell script command the awk '/Resolution:/{print $2,$3,$4}' portion does the following:

The output of e.g.:

$ system_profiler SPDisplaysDataType | grep -B 3 'Main Display:'
          Resolution: 2560 x 1440
          Pixel Depth: 32-Bit Color (ARGB8888)
          Display Serial Number: C08Z4120F6FB
          Main Display: Yes
$

Gets piped to awk and it searches the input for a record containing Resolution: which finds the following record match:

          Resolution: 2560 x 1440

The print $2,$3,$4 command, prints the second, third and fourth fields of the record. The , comma between the $n fields is to insert a space. The output of which is:

2560 x 1440
4
  • Is the grep -B 3 for three lines before and is that awk substring? Would plus one for a breakout of the grep awk and print it would help others, too.
    – ʀ2ᴅ2
    Oct 24, 2017 at 21:00
  • @DᴀʀᴛʜVᴀᴅᴇʀ, Yes, grep -B 3 is to include three lines before the match and I removed the substitute function from the awk command for the Resolution: because it really wasn't needed there. I'm headed out for a few hours and will give a break down a bit later. That said, I'd personally probably use cscreen and parse the info from it over system_profiler and I'll add an example of it too, unless you're against using third-party utilities. Oct 24, 2017 at 22:38
  • I'd personally prefer to use built in but others might find it interesting with what third parties can do.
    – ʀ2ᴅ2
    Oct 25, 2017 at 13:22
  • @DᴀʀᴛʜVᴀᴅᴇʀ, I've update the answer to explain a bit about what the shell commands of the do shell script commands are doing. Oct 25, 2017 at 14:29

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .