Handling Multiple Monitors in Omnis

This example demonstrates how to detect all available monitors on a user’s computer and correctly position Omnis windows across them.

Although Windows currently uses an MDI Frame (so it effectively reports a single screen), the same code works seamlessly on both macOS and Windows.


What This Code Does

  1. Detects monitor layout --- ensures that if someone re-orients monitors in macOS System Preferences, the Omnis window still opens entirely on a visible screen.\
  2. Moves a window to a chosen monitor --- via a menu built from the detected monitor list:
    • Move to main monitor (1600×1600)
    • Move to secondary monitor (800×800)
  3. Ensures proper sizing --- the window will not open larger than the monitor it’s on, accounting for the Omnis toolbar position and menu bar height.\
  4. Snaps window to monitor size --- resizes windows that are too wide or tall to fit.

This requirement has existed for a long time --- but became much easier once the new APIs arrived in Omnis 11.x.


Parameters & Local Variables


Name Type Purpose


pCacheHeight Boolean If true, forces Omnis to re-query the menu bar height (macOS only).

delimiter Character Used in token parsing.

height, width Integer (64-bit) Monitor dimensions.

mainScreenHeight Integer (64-bit) Tracks main screen height.

menuBarHeight Integer (default macOS menu bar height. 24)

offsetX, Integer (64-bit) Screen offsets and adjusted Y offsetY, omnisY coordinate.

screenList List List of all monitors.

screenString Character Raw string from sys(240).


Getting the List of Screens

# Gets a list of screens on the user's computer.
# On Windows (MDI), there will only be one.

Do $cinst.$getMenuBarHeight(pCacheHeight) Returns menuBarHeight
Do screenList.$define(width,height,offsetX,offSetY,omnisY)

Calculate screenString as sys(240)
Repeat
  Do method getNextMonitorToken (screenString,'[cxy_]',width.$ref,delimiter.$ref) Returns screenString
  Do method getNextMonitorToken (screenString,'[cxy_]',height.$ref,delimiter.$ref) Returns screenString
  Do method getNextMonitorToken (screenString,'[cxy_]',offsetX.$ref,delimiter.$ref) Returns screenString
  If delimiter='y'
    Calculate offsetX as -offsetX
  End If
  Do method getNextMonitorToken (screenString,'[cxy_]',offSetY.$ref,delimiter.$ref) Returns screenString
  If delimiter='y'
    Calculate offSetY as -offSetY
  End If

  If mainScreenHeight<=0
    Calculate mainScreenHeight as height-menuBarHeight
  Else
    Calculate omnisY as offSetY+height-menuBarHeight
    Calculate omnisY as omnisY-mainScreenHeight
    Calculate omnisY as omnisY*-1
  End If

  Do screenList.$add(width,height,offsetX,offSetY,omnisY)
Until screenString=""
Quit method screenList

The getNextMonitorToken Method

# Parses sys(240) into monitor values

Parameters:
  pString (Character)
  pRegExp (Character)
  pValue (Item Ref)
  pDelimiter (Character)

Calculate returnString as pString
Calculate length as len(returnString)
Calculate startToken as rxpos(pRegExp,returnString,kTrue,kFalse,matchLen)
Calculate pDelimiter as charat(returnString,startToken)
Calculate returnString as right(returnString,length-startToken)
Calculate endToken as rxpos(pRegExp,returnString,kTrue,kFalse,matchLen)

If endToken=0
  Calculate pValue as returnString
  Calculate returnString as ''
Else
  Calculate pValue as mid(returnString,1,endToken-1)
  Calculate returnString as right(returnString,length-endToken)
End If

Quit method returnString

Getting the macOS Menu Bar Height

Do $cinst.$getMenuBarHeight(pCacheHeight) Returns menuBarHeight

If not(pCacheHeight)
  If not(I_DOS_MACHINE)
    Begin text block
      Line:tell application "System Events"
      Line:tell process "Omnis"
      Line:set menuBar to menu bar 1
      Line:set menuBarSize to size of menuBar
      Line:set height to item 2 of menuBarSize
      Line:return height
      Line:end tell
      Line:end tell
    End text block
    Get text block applescript
    Do code method Common.$doApplescript (applescript,result) Returns #F
    If flag true
      If len(result)
        Calculate cMenuBarHeight as result
      End If
    End If
  Else
    Breakpoint    # TODO: Handle Windows equivalent
  End If
End If
Quit method cMenuBarHeight

Moving a Window to a Selected Monitor

# Moves a selected window to another monitor and resizes it to fit

Parameters:
  pMonitorList (List) -> Monitor to move to
  pWindowRef (Ref) -> Window reference
  pTopBorder (Int)
  pLeftBorder (Int)

If pMonitorList.$colcount=0
  Breakpoint "Missing monitor row"
  Quit method kFalse
End If

If pWindowRef=''
  Breakpoint "Missing window reference"
  Quit method kFalse
End If

If isclear(pTopBorder)|isclear(pLeftBorder)
  Calculate topBorder as 5
  Calculate leftBorder as 5
Else
  Calculate topBorder as pTopBorder
  Calculate leftBorder as pLeftBorder
End If

Calculate pWindowRef.$top as pMonitorList.omnisY+topBorder
Calculate pWindowRef.$left as pMonitorList.offsetX+leftBorder

If pWindowRef.$width+rightBorderMin+leftBorder>pMonitorList.width
  Calculate pWindowRef.$width as pMonitorList.width-rightBorderMin-leftBorder
End If

If pWindowRef.$height+bottomBorderMin+topBorder>pMonitorList.height
  Calculate pWindowRef.$height as pMonitorList.height-bottomBorderMin-topBorder
End If

Example: Cascading Windows on a Monitor

If pMonitorList.$colcount<=0
  Calculate list as $cinst.$getMonitorlist
  Calculate pMonitorList as list.1
End If

Do $cinst.$buildOpenWindowList('Top',kFalse,kTrue) Returns windowList
If windowList.$linecount=0
  Quit method kTrue
End If

Do windowList.$first(kFalse,kTrue) Returns #F
While flag true
  Set reference windowRef to windowList.iWindowRef

  If windowRef.$minimized
    Calculate windowRef.$maximized as kTrue
  End If

  If $cinst.$moveWindowToSelectedMonitor.$cando
    Do $cinst.$moveWindowToSelectedMonitor(pMonitorList,windowRef,topBorder,leftBorder)
  End If

  Calculate topBorder as topBorder+22   ## space to see title
  Calculate leftBorder as leftBorder+15

  Do windowList.$next(0,kFalse,kTrue) Returns #F
End While

Quit method kTrue

Summary

Once Omnis can identify available monitors and their coordinates, you can:

This logic makes Omnis far more user-friendly in multi-monitor environments --- and might make a good EuroOmnis topic.


Contributed by: []\Doug Easterbrook --- Omnis User Group]
Version: Omnis 11.x compatible