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