The MacOS X Aqua environment allows you to specify dual displays (possibly more) in an arbitrary arrangement - for example, side-by-side with the origin on one monitor offset from that of the other.
This presents a potential problem if a Tk program saves and restores window geometry - the user may disconnect a monitor or the saved geometry might apply to a different computer than the current one.
The following code uses the MacOS X /usr/sbin/system_profiler command to detect the number of monitors, and whether they are mirrored or not, and reads the system preferences (i.e. the com.apple.windowserver domain) to find the arrangement for each attached monitor.
The check_geom_aqua procedure is passed a window geometry (as returned by the "wm geometry" command, and checks that returns at least part of the top of the window is visible on one of the displays (thus allowing a window to be moved using the window manager controls). If none of the window is visible, the geometry is adjusted so that the window is centered on the primary display.
Tested on OSX 10.4.8 - YMMV
I'd appreciate knowing if anyone has a simpler way of achieving this functionality stevel - Jan 07
proc check_geom {win geom {yoffset 0}} { # yoffset can be used to allow for a menubar (as in MacOS X Aqua) # or space at the bottom for window manager controls scan $geom "%ix%i+%i+%i" w h x y set sw [winfo screenwidth $win] set sh [winfo screenheight $win] if {$w + $x > $sw} { set x [expr {($sw - $w)/2}] set geom "" } if {$h + $y > $sh} { set y [expr {($sh - $y)/2}] set geom "" } if {$y < $yoffset} { set y $yoffset set geom "" } if {$h + $yoffset*2 > $sh} { set h [expr {$sh - $yoffset*2}] set geom "" } if {$geom eq ""} { set geom =${w}x${h}+${x}+$y } return $geom } proc check_geom_aqua {win geom} { scan $geom "%ix%i+%i+%i" w h x y set cmd /usr/sbin/system_profiler if {[auto_execok $cmd] ne ""} { set fd [open "| $cmd SPDisplaysDataType"] set displaynum 0 foreach line [split [read $fd] \n] { set fields [split [string trim $line] :] set arg [string trim [lindex $fields 1]] switch -- [lindex $fields 0] { Resolution { incr displaynum } Mirror { set mirror [expr {$arg ne "Off"}] } } } close $fd if {$displaynum == 1 || $mirror} { # if a single display or mirrored we can trust Tk to # tell us the screen size return [check_geom $win $geom 20] } set prefs /Library/Preferences/com.apple.windowserver if {![catch { set fd [open "| defaults read $prefs DisplaySets"] }]} { set lines [read $fd] set num -1 foreach line [split [lindex [split $lines "()"] 2] \n] { set fields [split [string trim $line " ;"] =] set var [string trim [lindex $fields 0]] set val [string trim [lindex $fields 1]] switch -- $var { Active { incr num } OriginX - OriginY - Height { set $var $val } Width { if {$num == 0} { # allow for menubar incr OriginY 20 } # note the window must have at least 20 pixels # overlapping with the display (enough to move # via the window manager) lappend displays $num $OriginX $OriginY \ [expr {$OriginX + $val - 20}] \ [expr {$OriginY + $Height - 1}] } } } close $fd # look for top left on a display set found "" foreach {num x1 y1 x2 y2} $displays { if {$x >= $x1 && $x < $x2 && $y >= $y1 && $y < $y2} { set found $num break } } if {$found eq ""} { # look for top right on a display set ex [expr {$x + $w - 1}] foreach {num x1 y1 x2 y2} $displays { if {$ex >= $x1 && $ex < $x2 && $y >= $y1 && $y < $y2} { set found $num break } } } if {$found eq ""} { # not on any display, so we center on primary display set geom [check_geom $win $geom 20] } } } return $geom } if 0 { proc check {} { set geom [check_aqua_geom . [winfo geom .]] puts "geom = $geom" wm geom . $geom } package require Tk if 1 { # play with geom interactively bind . <Configure> [list check] } else { # specify geom well outside most people's displays wm geom . =200x2000+4000+4000 puts "waiting" after 3000 check } }