Exploring Dual Screens

In an effort to locate things on my dual-screen setup without shoving them up against the menu bar or under the dock on my primary screen, I put this together. It returns nearly everything you might want to know about dual screens - errors if you have only one [This was fixed to allow a primary screen on either port of a dual-screen card and the menu bar to be on either screen - thanks to Rick Smykla for pointing out the flaws and testing].

to GetScreenParameters()
	tell (do shell script " defaults read /Library/Preferences/com.apple.windowserver | grep -w -m 2 Unit") to if word 3 = word 6 then return display dialog "There is only one screen attached to this computer!" buttons {"Oops"} default button 1 giving up after 5 with icon 2
	tell (do shell script "echo `defaults read /Library/Preferences/com.apple.windowserver | grep -wA 1 -m 2 OriginX``defaults read /Library/Preferences/com.apple.windowserver | grep -w -m 2 Height``defaults read /Library/Preferences/com.apple.windowserver | grep -w -m 2 Width``defaults read /Library/Preferences/com.apple.windowserver | grep   DisplayLayoutToRight``defaults read com.apple.dock | grep  orientation``defaults read com.apple.dock | grep tilesize`") to set details to {word 3, word 6, word 9, word 12, word 15, word 18, word 21, word 24, word 27, word 30, word 33}
	
	--> Origin1 x, y; Origin2 x, y; Height 1, 2; Width 1, 2; DisplayLayout 1 = Right; DockPosition 0 = Left; DockSize; Extent x, y. The screen with origin 0, 0 has the menu bar, or if Display2toRight = 1, then the menu bar is on the left.
	
	repeat with anItem in details -- not really necessary, but I wanted numbers.
		try
			set contents of anItem to contents of anItem as number
		end try
	end repeat
	
	tell application "Finder" to tell (bounds of desktop's window) as list to set details to details & {(item 3) - (item 1), (item 4) - (item 2)}
	tell details to return {OriginPrimaryX:item 1, OriginPrimaryY:item 2, Origin2X:item 3, Origin2Y:item 4, HeightPrimary:item 5, Height2:item 6, WidthPrimary:item 7, Width2:item 8, Display2toRight:item 9, DockPosition:item 10, DockSize:item 11, WidthDesk:item 12, HeightDesk:item 13}
end GetScreenParameters

GetScreenParameters()
--> {OriginPrimaryX:0, OriginPrimaryY:0, Origin2X:1152, Origin2Y:156, HeightPrimary:870, Height2:768, WidthPrimary:1152, Width2:1024, Display2toRight:1, DockPosition:"left", DockSize:36, WidthDesk:2176, HeightDesk:924}

Very cool function. Do you know of any way to set the desktop picture of the secondary display?

Thanks.

can you get the desktop to be the itunes screensaver?

Just asking.

Kc

Hi Adam, I just posted a similar script to code exchange and then I came across your script. I sure wish I would have found your script before I went through the trouble of creating mine from scratch. We took very similar approaches. You can see mine here: http://bbs.applescript.net/viewtopic.php?pid=79207#p79207

Anyway, as I was looking at my script and comparing it to yours I found an error in your script. I only noticed it because when testing my script I stumbled across a tricky part. I found that when getting the values from the plist file by using “word 3” (for example) that a negative sign is dropped (if there are any negative signs). So if you have your second display situated above or to the left of your main display, then either the OriginX or OriginY values for the second display will have a negative value. I had to format the origin values specially (not using “words” to coerce to a list) to retain the negative value. Your script displays those values as positive numbers.

I just thought you’d like to know.

Hi, Reg;

Just back from 2 weeks away. Thanks for the heads up, but one of the returned values is “Display2toRight” where 1 is true and 0 is false. Did that not work for you?

Using System Events can give quite a simple and legible reading of the sizes/positions of the two screens.

Here is an example which resizes and repositions the selected window, making it occupy the rightmost one third of whichever of two screens it happens to be on.

(Can also be called, with proportional parameters [OriginX OriginY Width Depth] as an osascript shell command. Repeated calls with the same parameters will cycle through complementary screen-tiling transforms of the primary rectangle. This makes it possible to quickly tile the screen with a smaller number of assigned key-strokes [a smaller set of arguments for osascript calls])

property pVer : "1.9"
property pTitle : "Place and size window by % of screen" & tab & pVer

-- SET SIZE AND POSITION OF SELECTED WINDOW (ANY APPLICATION)
-- (WORKS WITH SINGLE OR DUAL DISPLAY SYSTEMS)

-- NAME
-- 	PlaceWin
-- 
-- SYNOPSIS
--     OS X terminal command line, in a shell script
--         (Assigned to a key by something like Keyboard Maestro or FastScripts)

--             Toggle window between full screen and last remembered size
--              and position:
--                 osascript ~/Library/Scripts/PlaceWin.scpt --zoom

--             Cycle between left, middle and right-hand vertical third
--              of the screen:
--                 osascript ~/Library/Scripts/PlaceWin.scpt 0 0 .333 1

--             Cycle between 4 vertical quarters of the screen:
--                 osascript ~/Library/Scripts/PlaceWin.scpt 0 0 .25 1

--             Toggle  between left half and right half of the screen:
--                 osascript ~/Library/Scripts/PlaceWin.scpt 0 0 .5 1

--             Toggle between top and bottom half of screen:
--                 osascript ~/Library/Scripts/PlaceWin.scpt 0 1 1 .5

--             Toggle between left 2/3 and right 1/3 of the screen:
--                 osascript ~/Library/Scripts/PlaceWin.scpt 0 0 .667 1

--             Cycle between top and bottom half of right-hand 1/3 of the
--              screen, and top and bottom half of left-hand 2/3 of the screen:
--                 osascript ~/Library/Scripts/PlaceWin.scpt .666 0 .333 .5

--             Clear window placing history, or save recent history to an
--              executable shell script for restoring work-spaces:
--                 osascript ~/Library/Scripts/PlaceWin.scpt --history
--
--             Example of a shell script generated by the history switch
--                 Places Textedit in the top right-hand corner of the
--                  screen (1/3 of screen width, half of screen depth),
--                  and Dictionary in the lower right-hand corner of the
--                  screen (1/3 of screen width, half of screen depth):

--                 open -a '/Applications/TextEdit.app'
--                 osascript ~/Library/Scripts/PlaceWin.scpt 0.666 0.0 0.333 0.5 --screen=1 --tile=1               
--                 open -a '/Applications/Dictionary.app'
--                 osascript ~/Library/Scripts/PlaceWin.scpt 0.666 0.0 0.333 0.5  --screen=1 --tile=2



-- Ver 1.4 Accepts tile argument in form of bare integer or as --Tile=N.  
--			(Providing a Tile argument suppresses cycles through complementary tiles, and gives an index to a particular tile)
-- Ver 1.6 In the absence of an argument, saves a restorable history to a clickable .command file on the Desktop
-- Ver 1.7 The dialog for naming the saved history shell script (restore work-space commands) offers to clear the history
-- Ver 1.71 Correctly sizes OmniFocus windows
-- Ver 1.72 Fixes a bug arising when history is empty and a history list is requested
-- Ver 1.8 Remembers the preZoom state of different zoomed applications
-- Ver 1.9 half-half splits toggle horizontally or vertically, rather than cycling between all four possibilities


-- DEFAULT: A full-height window, rightmost third of the screen:
-- NB (Not all windows can be arbitrarily resized)

-- UNITS - proportion of whichever screen is displaying the selected window
-- POSITION - (origin: top left)
property pPosnAcross : 0
property pPosnDown : 0

-- SIZE 
property pSizeAcross : 2 / 3
property pSizeDown : 1

property pblnMenuWindow : false
property pMenuHeight : 22

property plngMSOfficeOffset : 22

property pstrWinPrefsPath : "/Library/Preferences/com.apple.windowserver.plist"
property pTimeout : 10 -- Maximum number of seconds to wait for a loading app to present a window

property plstDisplays : {}
property piBox : 0
property piTile : missing value -- cycle to next complementary tile? or choose specific tile ?
property piScreen : missing value -- screen on which selected windows top left corner falls ? or specific screen ?
property pblnRepeat : false

property plstHistory : {} -- {App, Screen, PosX, PosY, SizeX, SizeY}
property plstPreZoom : {}
property plngMaxHistory : 10
property pstrWorkSpaceFolder : "~/Desktop/"

property pstrSave : "Save"
property pstrClear : "Clear history"

on run argv
	-- PROCESS ANY ARGUMENTS PASSED BY AN OSASCRIPT SHELL COMMAND
	-- set argv to {"0", "0", ".666", "1"}
	--set argv to {"zoom"}
	
	try
		set blnZoom to false
		if class of argv is list then
			set lngArgs to length of argv
			if lngArgs < 4 then
				-- return history in form of alternating open and placewin shell lines
				if lngArgs > 0 then
					set strFlag to first item of argv
					if strFlag contains "history" then
						set strHistory to HistoryCmds()
						if strHistory is "" then
							tell application id "com.apple.systemevents"
								activate
								display dialog "No window positioning history found." buttons {"OK"} default button 1 with title pTitle
							end tell
						else
							MakeCommand(strHistory)
							return
						end if
					else if strFlag contains "zoom" then
						set blnZoom to true
					end if
				end if
			else
				-- FIRST FOUR ARGUMENTS REQUIRED: OriginX, OriginY, Width, Height
				set lstPosnSize to items 1 thru 4 of argv
				repeat with i from 1 to 4
					set item i of lstPosnSize to (item i of lstPosnSize) as real
				end repeat
				
				-- RECORD WHETHER THESE ARGUMENTS MATCH THOSE OF THE PREVIOUS CALL 
				--		(WE MAY NEED TO CYCLE TO THE NEXT COMPLEMENTARY TRANSFORM)
				set pblnRepeat to ((lstPosnSize) = {pPosnAcross, pPosnDown, pSizeAcross, pSizeDown})
				
				-- AND UPDATE THE PERSISTENT PARAMETERS IF THEY HAVE CHANGED
				if not pblnRepeat then set {pPosnAcross, pPosnDown, pSizeAcross, pSizeDown} to lstPosnSize
				
				-- HANDLE THE OPTIONAL ADDITIONAL ARGUMENTS: --TILE=N --SCREEN=M
				--   (TILE: AN INDEX TO A SPECIFIC COMPLEMENTARY TRANSFORM (LEFT/RIGHT OR TOP/DOWN) OF THE BASE RECTANGLE)
				-- 	USEFUL IF THE OSASCRIPT CALL FOLLOWS AN OPEN -A CALL 
				--  WHICH AIMS TO OPEN A NAMED APP TO A SPECIFIC WINDOW SIZE/POSN
				-- SCREEN: PLACE THE WINDOW ON A SPECIFIC DISPLAY USING ITS INDEX IN THE PLIST
				-- (IN THE ABSENCE OF THIS FLAG, THE WINDOWS IS REPOSITIONED AND RESIZED ON THE SCREEN WHICH CONTAINS ITS
				-- UPPER LEFT CORNER
				set {piTile, piScreen} to {missing value, missing value}
				if length of argv > 4 then
					set lstFlags to items 5 thru end of argv
					repeat with oFlag in lstFlags
						if oFlag contains "=" then
							set my text item delimiters to "="
							set lstParts to text items of oFlag
							set strFlag to first item of lstParts
							if strFlag contains "screen" then
								try
									set piScreen to (item 2 of lstParts as integer)
								on error strMsg
									return strMsg
								end try
							else if strFlag contains "tile" then
								try
									set piTile to (item 2 of lstParts as integer)
								on error strMsg
									return strMsg
								end try
							else
								return "Unknown argument: " & oFlag
							end if
							
							set my text item delimiters to space
						else -- if this additional argument is a bare integer, interpret it as --Tile=N
							try
								set piTile to (oFlag as integer)
							on error strMsg
								return strMsg
							end try
						end if
					end repeat
				end if
				
				-- IF THIS IS NOT A REPEATED CALL WITH THE SAME ARGUMENTS, 
				-- (AND NO TRANSFORM HAS BEEN SPECIFIED),
				-- THEN DEFAULT TO THE BASE RECTANGLE (TRANSFORM 1)
				if (not pblnRepeat) and (piTile is missing value) then set piTile to 1
			end if
			-- NOW REPOSITION AND RESIZE THE FRONT WINDOW, 
			-- USING THE GIVEN PROPORTIONS OF THE CURRENT SCREEN SIZE
			ReSizePosn(blnZoom)
			
		else -- Class of argv not list - not launched at command line. Try to generate a restore workspace command
			set strHistory to HistoryCmds()
			if strHistory is "" then
				ReSizePosn(false)
			else
				MakeCommand(strHistory)
			end if
		end if
	on error strError
		return strError
	end try
end run

on MakeCommand(strHistory)
	tell application id "com.apple.systemevents"
		activate
		try
			set varResponse to (display dialog strHistory & return & "Enter name for this workspace:" default answer ¬
				"" buttons {"Esc", pstrClear, pstrSave} cancel button 1 default button 3 with title pTitle)
		on error
			return
		end try
		set strBtn to button returned of varResponse
		if strBtn = pstrSave then
			set strSpaceName to text returned of varResponse
			set my text item delimiters to space
			set lstParts to text items of strSpaceName
			set my text item delimiters to "_"
			set strSpaceName to lstParts as text
			set my text item delimiters to space
			set strFile to pstrWorkSpaceFolder & strSpaceName & ".command"
			set strCmd to "echo " & quoted form of strHistory & " > " & strFile
			do shell script strCmd
			do shell script "chmod +x " & strFile
			-- 	set strScript to "do shell script \"" & strHistory & "\""
			-- 	set strCmd to "echo " & quoted form of strScript & " | osacompile -o ~/DeskTop/sample.scpt"
			-- 	do shell script strCmd
			set plstHistory to {}
			display dialog "Set of window positions saved in:" & return & return & strFile & ¬
				return & return & "Dbl-Click the command file to restore the workspace." buttons {"OK"} default button 1 with title pTitle
		else if strBtn = pstrClear then
			set plstHistory to {}
		else
			display dialog "Unknown button: " & strBtn with title pTitle
		end if
	end tell
end MakeCommand


-- GET THE CURRENT SCREEN SIZE, 
-- AND MOVE AND RESIZE THE FRONT WINDOW USING PROPORTIONAL DIMENSIONS
on ReSizePosn(blnZoom)
	-- GET THE PROPERTIES OF THE REMEMBERED DISPLAYS
	tell application id "com.apple.systemevents"
		tell contents of property list file pstrWinPrefsPath
			set plstDisplays to (first item of ((value of (property list item "DisplaySets")) as list))
		end tell
		
		-- GET THE FRONT WINDOW AND ITS POSITION
		set oWin to missing value
		tell (first application process where frontmost = true)
			repeat with i from 1 to pTimeout * 2
				if (count of windows) > 0 then exit repeat
				do shell script "sleep 0.5"
			end repeat
			set strAppFile to application file
			set strAppFile to (POSIX path of strAppFile)
			set oWin to front window
		end tell
		if oWin is missing value then return
		
		set {lngWinX, lngWinY} to position of oWin
		
		
		
		-- FIND THE DISPLAY CONTAINING THE TOP LEFT CORNER OF THIS WINDOW
		set blnFound to false
		repeat with iDisplay from 1 to length of plstDisplays
			set {lngScreenX, lngScreenY, lngScreenAcross, lngScreenDown} to {OriginX, OriginY, Width, Height} of (item iDisplay of plstDisplays)
			if ((lngWinX ≥ lngScreenX) and (lngWinX < (lngScreenX + lngScreenAcross)) and ¬
				((lngWinY ≥ lngScreenY) and (lngWinY < (lngScreenY + lngScreenDown)))) then
				set pblnMenuWindow to {lngScreenX, lngScreenY} = {0, 0}
				set blnFound to true
				exit repeat
			end if
		end repeat
		if not blnFound then return
		
		if blnZoom then
			set {lngWinWidth, lngWinHeight} to size of oWin
			set lngMargin to pMenuHeight * 2
			if lngWinX > lngScreenX or lngWinY > (lngScreenY + lngMargin) or lngWinWidth < lngScreenAcross or lngWinHeight < (lngScreenDown - lngMargin) then
				set lngAdjustedY to lngWinY
				set lngAdjustedHeight to lngWinHeight
				if pblnMenuWindow then set lngAdjustedY to lngAdjustedY - pMenuHeight
				if strAppFile contains "Microsoft Word" then
					set lngAdjustedY to lngAdjustedY - plngMSOfficeOffset
					set lngAdjustedHeight to lngAdjustedHeight + plngMSOfficeOffset
				end if
				
				my StoreZoom({strAppFile, lngWinX, lngAdjustedY, lngWinWidth, lngAdjustedHeight})
				-- set plstPreZoom to {lngWinX, lngAdjustedY, lngWinWidth, lngAdjustedHeight}
				
				set {lngWinX, lngWinY, lngWinWidth, lngWinHeight} to {lngScreenX, lngScreenY, lngScreenAcross, lngScreenDown}
			else
				set lstOldZoom to my GetZoom(strAppFile)
				if lstOldZoom = {} then
					set {lngWinX, lngWinY, lngWinWidth, lngWinHeight} to {lngScreenX + (lngScreenAcross / 4), lngScreenY + (lngScreenDown / 4), lngScreenAcross / 2, lngScreenDown / 2}
				else
					set {lngWinX, lngWinY, lngWinWidth, lngWinHeight} to (items 2 thru end of lstOldZoom)
				end if
			end if
		else
			set lstBoxes to my GetTransforms({pPosnAcross, pPosnDown, pSizeAcross, pSizeDown}, {lngScreenX, lngScreenY, lngScreenAcross, lngScreenDown})
			if piTile is missing value then
				set piBox to piBox + 1
				if piBox > length of lstBoxes then set piBox to 1
			else
				try
					set piBox to (piTile as integer) -- specified as an argument on the command line
				on error
					set piBox to 1
				end try
			end if
			set {lngWinX, lngWinY, lngWinWidth, lngWinHeight} to item piBox of lstBoxes
		end if
		
		if pblnMenuWindow then set lngWinY to lngWinY + pMenuHeight
		tell oWin
			if strAppFile contains "Microsoft Word" then
				tell application id "com.Microsoft.Word"
					version
					tell front window of active document
						set position to {lngWinX, lngWinY + plngMSOfficeOffset}
						set width to lngWinWidth
						set height to lngWinHeight
					end tell
				end tell
			else if strAppFile contains "OmniFocus" then
				tell application id "com.omnigroup.omnifocus"
					tell front document window of front document
						if lngWinWidth = lngScreenAcross / 2 then
							set bounds to {lngWinX, lngWinY, lngWinX + lngWinWidth, lngWinY + lngWinHeight * 0.95}
						end if
						set bounds to {lngWinX, lngWinY, lngWinX + lngWinWidth, lngWinY + lngWinHeight}
					end tell
				end tell
			else if strAppFile contains "Accordance" then
				set {lngAccX, lngAccY} to {lngWinX, lngWinY}
				if lngAccX ≤ 23 then set lngAccX to 30
				if lngAccY < 23 then set lngAccY to 95
				set {position, size} to {{lngAccX, lngAccY}, {lngWinWidth, lngWinHeight}}
			else
				set {position, size} to {{lngWinX, lngWinY}, {lngWinWidth, lngWinHeight}}
			end if
		end tell
	end tell
	
	set lstEvent to {strAppFile, iDisplay, pPosnAcross, pPosnDown, pSizeAcross, pSizeDown, piBox}
	if length of plstHistory > 0 then
		set strLastApp to first item of item -1 of plstHistory
		if (strAppFile = strLastApp) then
			set item -1 of plstHistory to lstEvent
		else
			set end of plstHistory to lstEvent
			if length of plstHistory > plngMaxHistory then set plstHistory to (items 2 thru end of plstHistory)
		end if
	else
		set plstHistory to {lstEvent}
	end if
	
	return
end ReSizePosn

on HistoryCmds()
	set str to ""
	
	set text item delimiters to (POSIX path of (path to home folder))
	set strScriptPath to "~/" & last text item of (POSIX path of (path to me))
	
	set text item delimiters to space
	if length of plstHistory > 0 then
		repeat with i from 1 to length of plstHistory
			set {strAppFile, iDisplay, pPosnAcross, pPosnDown, pSizeAcross, pSizeDown, piBox} to (item i of plstHistory)
			set str to ((str & "open -a " & (quoted form of strAppFile) & "
" & "osascript " & strScriptPath & space & ({pPosnAcross, pPosnDown, pSizeAcross, pSizeDown} as text) & ¬
				" --tile=" & piBox as string) & " --screen=" & iDisplay as string) & "
"
		end repeat
	end if
	
	if str ≠ "" then tell application id "com.apple.finder" to set the clipboard to str
	set plstHistory to {}
	return str
end HistoryCmds


-- FOR RECTANGLES WHICH INCLUDE ONE OR TWO CORNERS OF THE SCREEN,
-- OR TOUCH OPPOSITE EDGES OF THE SCREEN, AND HAVE A HEIGHT OR WIDTH 
-- WHICH IS A SIMPLE FRACTION OF THE CORRESPONDING SCREEN DIMENSION
-- GENERATE THE SCREEN-TILING SET OF COMPLEMENTARY RECTANGLES.

-- (REPEATED CALLS OF THE SCRIPT WITH THE SAME PARAMETERS CYCLE THROUGH THESE COMPLEMENTARY TRANSFORMS,
-- ALLOWING SIMPLE TILING OF THE SCREEN WITH A SMALL NUMBER OF ASSIGNED KEYSTROKES)
on GetTransforms({pPosnAcross, pPosnDown, pSizeAcross, pSizeDown}, {lngScreenX, lngScreenY, lngScreenAcross, lngScreenDown})
	if pblnMenuWindow then
		set lngScreenDown to lngScreenDown - pMenuHeight
		--set lngScreenY to lngScreenY + pMenuHeight
	end if
	set {lngX, lngY, lngWidth, lngHeight} to {(pPosnAcross * lngScreenAcross) + lngScreenX, (pPosnDown * lngScreenDown) + lngScreenY, pSizeAcross * lngScreenAcross, pSizeDown * lngScreenDown}
	set {pSizeAcross, pSizeDown} to {pSizeAcross as real, pSizeDown as real}
	
	set {lngDeltaX, lngDeltaY} to {0, 0}
	if lngX = 0 then set lngDeltaX to lngWidth
	if lngY = 0 then set lngDeltaY to lngHeight
	
	if (pSizeAcross = 1.0) or (pSizeDown = 1.0) then -- Single split left/right or up/down
		if pSizeAcross = 1.0 then
			-- UP DOWN
			if pSizeDown = 0.5 then -- (horizontal splits)
				{{0, 0, lngWidth, lngHeight}, {0, lngHeight, lngWidth, lngScreenDown - lngHeight}}
			else if pSizeDown ≥ (1 / 6) and IsSimpleFraction(pSizeDown) then
				set lst to {}
				set lngDeltaY to 0
				repeat with i from 1 to (1 / pSizeDown) as integer
					set end of lst to {lngX, lngDeltaY, lngWidth, lngHeight}
					set lngDeltaY to lngDeltaY + lngHeight
				end repeat
				lst
			else
				{{lngX, lngY, lngWidth, lngHeight}, {lngX, lngDeltaY, lngWidth, lngScreenDown - lngHeight}}
			end if
		else
			-- LEFT RIGHT
			if pSizeAcross = 0.5 then -- vertical splits
				{{lngX, lngY, lngWidth, lngHeight}, {lngDeltaX, lngY, lngScreenAcross - lngWidth, lngHeight}}
			else if pSizeAcross ≥ (1 / 6) and IsSimpleFraction(pSizeAcross) then
				set lst to {}
				set lngDeltaX to 0
				repeat with i from 1 to (1 / pSizeAcross) as integer
					set end of lst to {lngDeltaX, lngY, lngWidth, lngHeight}
					set lngDeltaX to lngDeltaX + lngWidth
				end repeat
				lst
			else
				{{lngX, lngY, lngWidth, lngHeight}, {lngDeltaX, lngY, lngScreenAcross - lngWidth, lngHeight}}
			end if
		end if
	else -- does this have at least one corner ?  (at {0,0} or (height+y)=1) or ((width+x)=1)
		if ({lngX, lngY} = {0, 0}) or AlmostOne(pSizeDown + pPosnDown) or AlmostOne(pSizeAcross + pPosnAcross) then
			
			{{lngX, lngY, lngWidth, lngHeight}, ¬
				{lngX, lngDeltaY, lngWidth, lngScreenDown - lngHeight}, ¬
				{lngDeltaX, lngDeltaY, lngScreenAcross - lngWidth, lngScreenDown - lngHeight}, ¬
				{lngDeltaX, lngY, lngScreenAcross - lngWidth, lngHeight}}
		else
			{{lngX, lngY, lngWidth, lngHeight}}
		end if
	end if
end GetTransforms

on AlmostOne(rNumber)
	(1 - rNumber) < 0.01
end AlmostOne

on IsSimpleFraction(nNumber)
	if nNumber > 0.5 then
		return false
	else
		set N to 1 / nNumber
		Abs((N as real) - (N as integer)) < 0.01
	end if
end IsSimpleFraction

on Abs(N)
	if N < 0 then
		-N
	else
		N
	end if
end Abs

on StoreZoom({strAppFile, lngWinX, lngAdjustedY, lngWinWidth, lngAdjustedHeight})
	set lngZoom to length of plstPreZoom
	if lngZoom > 0 then
		repeat with i from 1 to lngZoom
			if first item of (item i of plstPreZoom) = strAppFile then
				set item i of plstPreZoom to {strAppFile, lngWinX, lngAdjustedY, lngWinWidth, lngAdjustedHeight}
				return
			end if
		end repeat
		set end of plstPreZoom to {strAppFile, lngWinX, lngAdjustedY, lngWinWidth, lngAdjustedHeight}
	else
		set end of plstPreZoom to {strAppFile, lngWinX, lngAdjustedY, lngWinWidth, lngAdjustedHeight}
	end if
end StoreZoom

on GetZoom(strAppFile)
	set lngZoom to length of plstPreZoom
	if lngZoom > 0 then
		repeat with lstZoom in plstPreZoom
			if first item of lstZoom = strAppFile then return lstZoom
		end repeat
		{}
	else
		{}
	end if
end GetZoom

… and a briefer summary of the basic method:

property pVer : "0.1"
property pTitle : "Get Origin, Size of current screen   Ver " & pVer

property pstrWinPrefsPath : "/Library/Preferences/com.apple.windowserver.plist"

-- READ THE DETAILS OF THE REMEMBERED DISPLAYS
tell application id "com.apple.systemevents"
	tell contents of property list file pstrWinPrefsPath
		set lstDisplays to (first item of ((value of (property list item "DisplaySets")) as list))
	end tell
	
	-- GET THE SELECTED WINDOW AND ITS POSITION
	set {lngWinX, lngWinY} to position of (front window of (first application process where frontmost = true))
	
	-- FIND THE DIMENSIONS OF THE SCREEN CONTAINING THE TOP LEFT CORNER OF THE SELECTED WINDOW
	repeat with recDisplay in lstDisplays
		set {lngScreenX, lngScreenY, lngScreenAcross, lngScreenDown} to {OriginX, OriginY, Width, Height} of recDisplay
		if ((lngWinX ≥ lngScreenX) and (lngWinX < (lngScreenX + lngScreenAcross)) and ¬
			((lngWinY ≥ lngScreenY) and (lngWinY < (lngScreenY + lngScreenDown)))) then
			exit repeat
		end if
	end repeat
end tell

-- REPORT ORIGIN AND SIZE OF THIS SCREEN
set text item delimiters to return
display dialog {lngScreenX, lngScreenY, lngScreenAcross, lngScreenDown} as string buttons {"OK"} with title pTitle
set text item delimiters to space

Hi, yiam-jin-qui.

Thanks for that. I think you mean “” or {“”} in the last line rather than ‘space’.

A personal house style, I guess :cool:

Not sure why, but I prefer to default to something like a word-level segmentation).

( And, of course, text 2 thru 3 of “hello” is still “el” )

I just wanted to point out my experience. I have found that the plist file (/Library/Preferences/com.apple.windowserver.plist) is not always accurate. If you’re screen configuration is stable then it seems to work well but if you add or remove a screen periodically, or change it’s position, then the actual configuration can be different than the configuration you find in the plist. As such I wrote a command line tool which will always give accurate information. It uses NSScreen and other cocoa classes to get the information from the window server (rather than its plist) at the time of the query. If you’re interested you can find it here.

Your hmscreens tool looks good, and I have certainly noticed that run-time display-switching and menu swapping can occasionally leave a disjunction between hardware and plist.

Empirically, explicitly setting a value in System Preferences > Displays > Resolutions seems to force a write to the plist. The problem seems to come when a non-manual change in resolution occurs, for example after a lower resolution mirrored device is disconnected.

It certainly helps to be able to write:

set {{OriginX, OriginY}, {Width, Height}} to (run script text 18 thru end of (paragraph 3 of (do shell script "/usr/local/bin/hmscreens -info")))

Adam, your code works fine on my machine at home (Mac Pro running 10.6), but when I tried it at work (PMG5 running 10.4) earlier today it would error out at the line

to set details to {word 3, word 6, word 9, word 12, word 15, word 18, word 21, word 24, word 27, word 30, word 33}

I can’t remember exactly what it said, but I seem to remember it didn’t like “word 3”

And, Adam, my single-screen MacBook Pro with OS 10.6 doesn’t like the first line. (“" Unit = 0;
Unit = 0;" doesn’t understand the display dialog message.”) It needs a ‘tell me’ or something neater:

tell (do shell script " defaults read /Library/Preferences/com.apple.windowserver | grep -w -m 2 Unit") to if word 3 = word 6 then tell me to return (display dialog "There is only one screen attached to this computer!" buttons {"Oops"} default button 1 giving up after 5 with icon 2)

I think things get easier if one simply writes:

property pstrWinPrefsPath : "/Library/Preferences/com.apple.windowserver.plist"

tell application id "com.apple.systemevents"
	tell contents of property list file pstrWinPrefsPath
		set lstDisplays to (first item of ((value of (property list item "DisplaySets")) as list))
	end tell
end tell