Reading Jon's screen size of {screen list}?

I’m using screen list from the Jon’s Commands OSAX to get the resolution of the screen, but as an Applescript neophyte, nested lists confuse me to no end. I’ve found a way to do it, but it seems horribly inelegant. I figure there must be a better way. Here’s what I’m doing:

set scrRes to screen size of (item 1 of (item 1 of {screen list}))
set scrWidth to item 1 of scrRes
set scrHeight to item 2 of scrRes

Can someone show me a better way to do this? Here’s the beginning of what screen list returns, per Jon’s docs:

{
    {
        name:"AppleVision 1710AV Display", 
        screen id:1, 
        bounds:{0, 0, 832, 624}, 
        screen size:{832, 624}, 
        color depth:32, 
        in color:true, 
        has menu bar:true, 
        refresh rate:75, 
        resolution index:6, 
        safe resolution:true, 
        supported resolutions:{

Thanks for any suggestions!

Greg Orman
Information Technology
Scripps College

If you don’t mind uisng an other OSAX, you can try XTool that has a command to get the screen size.
http://www.osaxen.com

You could use another OSAX but as to your original question: screen list actually returns a list of records (one for each screen–even if there is only one screen) not lists. You’ve added to the complexity by adding another list simply by placing screen list in brackets instead of parentheses. Fixing that, though, your code is fairly sound although you could simplify the setting of the two variables with a single copy statement:.

copy screen size of (item 1 of (screen list)) to {scrWidth, scrHeight}

Jon (not of Jon’s Commands fame – that’s Pugh, a real godsend to AppleScript for many years)

And if you need a non-osax solution you can use the following handler. This works on multiple monitors and returns correct dimensions immediately after a screen resize. Originally tested to return correct results immediately after second screen connection or disconnection, but I can’t reconfirm that currently. The monitorProperties() handler below returns the count of connected monitors and the dimensions of each. It currently is coded to handle only two monitors due to my laptops connection options.


set MonitorProps to monitorProperties()
--{monitor1:{Width:1440, Height:900, OriginX:0, OriginY:0}, monitor2:{Width:missing value, Height:missing value, OriginX:missing value, OriginY:missing value}, monitorCount:1}
set {monHeight, MonWidth} to {Height of monitor1, Width of monitor1} of MonitorProps
--{900,1440}





on monitorProperties()
	--paulskinner Friday, March 14, 2008 6:11:53 PM
	--system_profiler parsing 
	set SPDisplaysData to (do shell script "system_profiler SPDisplaysDataType")
	set text item delimiters to "Displays:"
	set SPDisplaysData to text item 3 of SPDisplaysData
	set text item delimiters to (word 1 of SPDisplaysData)
	copy text item 1 of SPDisplaysData to text item delimiters
	set SPDisplaysData to text items 2 thru -1 of SPDisplaysData
	set text item delimiters to ""
	repeat with i from 2 to length of SPDisplaysData
		if character 1 of item i of SPDisplaysData is not " " then
			set monitor1 to items 2 thru (i - 1) of SPDisplaysData
			set monitor2 to items (i + 1) thru -1 of SPDisplaysData
			exit repeat
		end if
	end repeat
	--END OF system_profiler parsing 
	
	set {monitorCount, output} to {1, {}}
	repeat with curList in {monitor1, monitor2}
		set mainDisplay to false
		curList
		if item 1 of curList contains "Status: No display connected" then
			return {monitor1:{Width:y as integer, Height:x as integer, OriginX:0, OriginY:0}, monitor2:{Width:missing value, Height:missing value, OriginX:missing value, OriginY:missing value}, monitorCount:monitorCount}
		else
			repeat with i from 1 to length of curList
				set curItem to item i of curList
				if curItem contains "Resolution:" then
					set {y, x} to {word 2 of curItem, word 4 of curItem}
				end if
				if curItem contains "Main Display: Yes" then
					set mainDisplay to true
				end if
			end repeat
		end if
		if mainDisplay then
			set display1 to {Width:y as integer, Height:x as integer, OriginX:missing value, OriginY:missing value}
		else
			set monitorCount to 2
			set display2 to {Width:y as integer, Height:x as integer, OriginX:missing value, OriginY:missing value}
		end if
	end repeat
	
	-- If there are two monitors then use com.apple.windowserver to try to acquire Origins
	set {monitor1, monitor2} to {{}, {Height:missing value, Width:missing value, OriginX:missing value, OriginY:missing value}}
	set PrefFilePath to do shell script "find " & (POSIX path of ((path to preferences from user domain as Unicode text) & "ByHost:")) & " -name 'com.apple.windowserver*'"
	tell application "System Events"
		set wsp to item 1 of (item 1 of (get value of property list items of property list file PrefFilePath))
	end tell
	repeat with mon from 1 to length of wsp
		set monData to item mon of wsp
		if (|OriginX| of monData is 0) and (|OriginY| of monData is 0) then
			set monitor1 to {Height:|Height| of monData, Width:|Width| of monData, OriginX:|OriginX| of monData, OriginY:|OriginY| of monData}
		else
			set monitor2 to {Height:|Height| of monData, Width:|Width| of monData, OriginX:|OriginX| of monData, OriginY:|OriginY| of monData}
		end if
	end repeat
	set WSmp to {monitor1:monitor1, monitor2:monitor2, monitorCount:(length of wsp) as integer}
	--END OF If there are two monitors then use com.apple.windowserver to try to acquire Origins
	
	if monitorCount is monitorCount of WSmp then --If monitor count matches, compare dimensions reported by the two sources.
		if (Height of display1 is (Height of monitor1 of WSmp)) and (Width of display1 is (Width of monitor1 of WSmp)) and (Height of display2 is (Height of monitor2 of WSmp)) and (Width of display2 is (Width of monitor2 of WSmp)) then
			return WSmp -- All match, return windowserver data since it is most complete.
		end if
	end if
	return {monitor1:display1, monitor2:display2, monitorCount:monitorCount} -- Not all match, return system_profiler data.
end monitorProperties



This is the one I use. It returns a list of two lists (for two screens) with the first two terms in each being the pixel dimensions of the screen, and the last two the top left-hand corner of the screen. It will return the dimensions and location of a second screen that has been connected even if it is not currently connected.

set f to (path to preferences from local domain as Unicode text) & "com.apple.windowserver.plist"
tell application "System Events"
	set {{|Width|:w1, |Height|:h1, |OriginX|:OX1, |OriginY|:OY1}, {|Width|:w2, |Height|:h2, |OriginX|:OX2, |OriginY|:OY2}} to value of property list items of property list item 1 of property list item "DisplaySets" of property list file f
end tell
{{w1, h1, OX1, OY1}, {w2, h2, OX2, OY2}}

just to prove that there is yet another way… I’m so grateful for all the help I got here…

These methods serve me well, when it comes to AppleScript and displays:


(*
	screenInfo.scpt
	get basic display(s) information
	Written on 090522 by SwissalpS
	
	Uses a shell script to read display settings from defaults.
	I don't know about OS X 10.5 but on earlier versions the read info was not up-to-date
	more like state-at-boot-time so I wouldn't be surprised if the information returned from this
	script isn't accurate at all times.
	
	usage:
		set screenInfo to getSomeScreenInfo()
		note: I can't figure out on which display the menu bar is (when not on main) so safebounds is allways cropped
	
	returns a list containing a record for each display that was found (in the plist!). Example with 2 screens, the main screen is situated to the right of the second: 
		{
			{id:"69670400", origin:{0, 0}, size:{1440, 900}, bounds:{0, 0, 1440, 900}, safebounds:{0, 44, 1440, 900}, unmirroredSize:{1440, 900}, isMirrored:false},
			{id:"1535231425", origin:{-1280, -52}, size:{1280, 1024}, bounds:{-1280, -52, 0, 972}, safebounds:{-1280, -8, 0, 972}, unmirroredSize:{1280, 1024}, isMirrored:false}
		}
	
	thanks to Julio for posting the shell script on macscripter.net
	http://macscripter.net/profile.php?id=7  
	http://macscripter.net/viewtopic.php?pid=12313#p12313
	
	Important: use at your own risk ;-P
*)
-- demo
{getSomeScreenInfo(), getInfoFromSystemProfiler()}

to getSomeScreenInfo()
	set {iMainScreenIsInternal, iActiveScreens, iScreens, iMirrors, aHeights, aWidths, aNativeHeights, aNativeWidths, aIDs, aOx, aOy, aMirrored} to {0, 0, 0, 0, {}, {}, {}, {}, {}, {}, {}, {}}
	-- main screen stuff
	set iMainScreenIsInternal to (my getGrepFor("DisplayMainOnInternal"))'s last word as number
	-- active screen count
	set iActiveScreens to count of (my getGrepFor("Active"))'s paragraphs
	-- anything mirrored?
	set aTmp to (my getGrepFor("Mirrored"))'s paragraphs
	repeat with sTmp in aTmp
		set i to sTmp's last word as number
		set aMirrored's end to i
		set iMirrors to iMirrors + i
	end repeat
	-- display ids
	set aTmp to (getGrepFor("DisplayID"))'s paragraphs
	repeat with sTmp in aTmp
		set aIDs's end to sTmp's last word as text
	end repeat
	-- display count
	set iScreens to aIDs's length
	-- current widths
	set aTmp to (getGrepFor("Width"))'s paragraphs
	repeat with sTmp in aTmp
		set aWidths's end to sTmp's last word as number
	end repeat
	-- current heights
	set aTmp to (getGrepFor("Height"))'s paragraphs
	repeat with sTmp in aTmp
		set i to sTmp's last word as number
		set aHeights's end to i
	end repeat
	-- native widths
	set aTmp to (getGrepFor("UnmirroredWidth"))'s paragraphs
	repeat with sTmp in aTmp
		set aNativeWidths's end to sTmp's last word as number
	end repeat
	-- native heights
	set aTmp to (getGrepFor("UnmirroredHeight"))'s paragraphs
	repeat with sTmp in aTmp
		set aNativeHeights's end to sTmp's last word as number
	end repeat
	-- origin X
	set aTmp to (getGrepFor("OriginX"))'s paragraphs
	repeat with sTmp in aTmp
		set aOx's end to sTmp's characters ((offset of "=" in sTmp) + 1) thru -2 as text as number
	end repeat
	-- origin Y
	set aTmp to (getGrepFor("OriginY"))'s paragraphs
	repeat with sTmp in aTmp
		set i to sTmp's characters ((offset of "=" in sTmp) + 1) thru -2 as text as number
		set aOy's end to i
	end repeat
	
	-- to return in info in another format, uncomment the following line by removing the preceding '--' 
	--return {mainScreenIsInternal:iMainScreenIsInternal, numberOfScreens:iScreens, numberOfActiveScreens:iActiveScreens, numberOfMirrors:iMirrors, heights:aHeights, widths:aWidths, unmirroredHeights:aNativeHeights, unmirroredWidths:aNativeWidths, originXs:aOx, originYs:aOy, allIDs:aIDs}
	
	-- now we have it all, let's make uasable grouping
	set aScreens to {}
	repeat with i from 1 to aIDs's length
		set {x0, y0, w, h} to {aOx's item i, aOy's item i, aWidths's item i, aHeights's item i}
		set {x1, y1} to {x0 + w, y0 + h}
		set hScreen to {id:aIDs's item i, origin:{x0, y0}, size:{w, h}, bounds:{x0, y0, x1, y1}, safebounds:{x0, y0 + 44, x1, y1}, unmirroredSize:{aNativeWidths's item i, aNativeHeights's item i}, isMirrored:false}
		if 0 ≠ (aMirrored's item i) then set hScreen's isMirrored to true
		set aScreens's end to hScreen
	end repeat
	
	return aScreens
end getSomeScreenInfo

(* used by getSomeScreenInfo() *)
to getGrepFor(sString)
	return do shell script "defaults read /Library/Preferences/com.apple.windowserver | grep -w " & sString
end getGrepFor