Respuesta
FWH proporciona soporte completo para múltiples monitores a través de la clase TMonitor y varias funciones auxiliares ubicadas en source/function/getsysin.prg. 0-cite-0
Funciones Principales
FW_ActiveMonitors() - Retorna el número de monitores activos en el sistema. 0-cite-1
FW_VirtualScreen() - Retorna un objeto TRect que representa el área total de la pantalla virtual cubierta por todos los monitores. 0-cite-2
FW_GetMonitor() - Función versátil que retorna un objeto TMonitor según diferentes parámetros: 0-cite-3
- Sin parámetros: retorna el monitor primario
- Con número n: retorna el n-ésimo monitor
- Con oWnd/hWnd: retorna el monitor que contiene la ventana
- Con coordenadas nRow, nCol: retorna el monitor que contiene ese punto
FW_GetAllMonitors() - Retorna un array con todos los objetos TMonitor del sistema. 0-cite-4
FW_SetMonitor() - Establece un monitor por defecto para todas las ventanas/diálogos que se abran posteriormente. 0-cite-5 0-cite-6
Clase TMonitor
La clase TMonitor hereda de TRect y proporciona: 0-cite-0
Datos:
- lPrimary - Indica si es el monitor primario
- ClientRect - Área de trabajo del monitor (excluyendo barra de tareas)
- nTop, nLeft, nBottom, nRight, nWidth, nHeight - Coordenadas y dimensiones (heredadas de TRect)
Métodos:
- Row(r) - Convierte coordenada de fila relativa al monitor a coordenada de pantalla virtual 0-cite-7
- Col(c) - Convierte coordenada de columna relativa al monitor a coordenada de pantalla virtual 0-cite-8
- Center(oWnd) - Centra una ventana dentro del monitor 0-cite-9
- Move(oWnd) - Mueve una ventana al monitor manteniendo sus coordenadas relativas 0-cite-10
Integración con Ventanas y Diálogos
Las ventanas y diálogos soportan la cláusula IN para especificar el monitor: 0-cite-11
ACTIVATE WINDOW oWnd IN nMonitor/oMonitor
ACTIVATE DIALOG oDlg IN oWnd/nMonitor/oMonitor
El método Activate() de TWindow procesa el parámetro oMonitor para posicionar la ventana en el monitor especificado. 0-cite-12
Notas
Estas funcionalidades fueron introducidas en diciembre de 2018 según el historial de cambios. 0-cite-13 En septiembre de 2024 se corrigieron los métodos Row() y Col() de la clase TMonitor. 0-cite-14
Wiki pages you might want to explore:
Citations
File: source/function/getsysin.prg (L427-428)
function FW_ActiveMonitors()
return GetSysMetrics( SM_CMONITORS )
File: source/function/getsysin.prg (L432-434)
function FW_VirtualScreen()
return TRect():New( GetSysMetrics( SM_YVIRTUALSCREEN ), GetSysMetrics( SM_XVIRTUALSCREEN ),,, ;
GetSysMetrics( SM_CXVIRTUALSCREEN ), GetSysMetrics( SM_CYVIRTUALSCREEN ) )
File: source/function/getsysin.prg (L438-452)
function FW_SetMonitor(...)
static oMonitor
local oRet
oRet := oMonitor
if PCOUNT() > 0
oMonitor := HB_ExecFromArray( "FW_GETMONITOR", HB_AParams() )
if oMonitor:lPrimary
oMonitor := nil
endif
endif
return oRet
File: source/function/getsysin.prg (L456-486)
function FW_GetMonitor( nRow, nCol ) // POINT r,c [OR] nMonitor, [or] oWnd/hWnd No params: primary monitor
local aInfo, oMonitor, nMax
if nRow == nil .and. nCol == nil
oMonitor := TMonitor():New( MonitorInfoFromRC( 20, 20 ) )
elseif HB_ISNUMERIC( nRow ) .and. HB_ISNUMERIC( nCol )
aInfo := MonitorInfoFromRC( nRow, nCol )
if !Empty( aInfo )
oMonitor := TMonitor():New( aInfo )
endif
elseif PCount() == 1
if HB_ISOBJECT( nRow ) .and. nRow:IsKindOf( "TWINDOW" )
nRow := nRow:hWnd
endif
if ValType( nRow ) != "N"
nRow := 1
endif
if IsWindow( nRow )
aInfo := MonitorInfoFromRC( nRow )
if aInfo != nil
oMonitor := TMonitor():New( aInfo )
endif
else
nMax := Max( 1, Min( nRow, FW_ActiveMonitors() ) )
oMonitor := FW_GetAllMonitors()[ nMax ]
endif
endif
return oMonitor
File: source/function/getsysin.prg (L490-527)
function FW_GetAllMonitors()
static caRect
static aMonitors := {}
local aRect, nRow, nCol, aInfo, caInfo, nMonitors, c, nMonitor := 0
aRect := FW_VirtualScreen()
if ( c := FW_ArrayAsList( aRect ) ) == caRect
return aMonitors
endif
caRect := c
aMonitors := {}
nMonitors := FW_ActiveMonitors()
for nRow := aRect[ 1 ] + 100 to aRect[ 3 ] step 100
for nCol := aRect[ 2 ] + 100 to aRect[ 4 ] step 100
aInfo := MonitorInfoFromRC( nRow, nCol )
IF HB_IsArray( aInfo )
caInfo := Str( aInfo[ 1 ] ) + ", " + Str( aInfo[ 2 ] ) + ", " + Str( aInfo[ 3 ] ) + ", " + Str( aInfo[ 4 ] )
IF AScan( aMonitors, { | x | x[ 2 ] == caInfo } ) == 0
nMonitor ++
AAdd( aMonitors, { nMonitor, caInfo, aInfo, TMonitor():New( aInfo ) } )
ENDIF
ENDIF
IF nMonitor >= nMonitors
EXIT
ENDIF
next nCol
IF nMonitor >= nMonitors
EXIT
ENDIF
next nRow
AEval( aMonitors, { |a,i| aMonitors[ i ] := a[ 4 ] } )
return aMonitors
File: source/function/getsysin.prg (L563-574)
CLASS TMonitor STATIC FROM TRect
DATA ClientRect
DATA lPrimary
METHOD New( aInfo ) CONSTRUCTOR
METHOD Move( oWnd )
METHOD Center( oWnd )
METHOD Row( r ) INLINE ( ::nTop + r )
METHOD Col( c ) INLINE ( ::nLeft + c )
ENDCLASS
File: source/function/getsysin.prg (L588-618)
METHOD Move( oWnd ) CLASS TMonitor
local hWnd, nTop, nLeft, w, h, aRect
local oMonitor
if HB_ISOBJECT( oWnd ) .and. oWnd:IsKindOf( "TWINDOW" )
hWnd := oWnd:hWnd
elseif HB_ISNUMERIC( oWnd )
hWnd := oWnd
endif
if Empty( hWnd ) .or. !IsWindow( hWnd )
return nil
endif
// aRect := GetCoors( hWnd ) // not correct for dialogs
aRect := GetWndRect( hWnd )
nTop := aRect[ 1 ]
nLeft := aRect[ 2 ]
w := aRect[ 4 ] - aRect[ 2 ]
h := aRect[ 3 ] - aRect[ 1 ]
oMonitor := FW_GetMonitor( hWnd )
if !oMonitor:lPrimary
nTop -= oMonitor:nTop
nLeft -= oMonitor:nLeft
endif
nTop += ::nTop
nLeft += ::nLeft
MoveWindow( hWnd, nTop, nLeft, w, h, .t. )
return nil
File: source/function/getsysin.prg (L622-647)
METHOD Center( oWnd ) CLASS TMonitor
local aPt := ::CenterPt
local hWnd, w, h, aRect
if HB_ISNUMERIC( oWnd ) .and. IsWindow( oWnd )
hWnd := oWnd
aRect := GetCoors( hWnd )
w := aRect[ 4 ] - aRect[ 2 ]
h := aRect[ 3 ] - aRect[ 1 ]
aPt[ 1 ] := Max( ::nTop, aPt[ 1 ] - Int( h / 2 ) )
aPt[ 2 ] := Max( ::nLeft, aPt[ 2 ] - Int( w / 2 ) )
MoveWindow( hWnd, aPt[ 1 ], aPt[ 2 ], w, h, .t. )
elseif HB_ISOBJECT( oWnd ) .and. oWnd:IsKindOf( "TWINDOW" )
aPt[ 1 ] -= Int( oWnd:nHeight / 2 )
aPt[ 2 ] -= Int( oWnd:nWidth / 2 )
aPt[ 1 ] := Max( ::nTop, aPt[ 1 ] )
aPt[ 2 ] := Max( ::nLeft, aPt[ 2 ] )
oWnd:Move( aPt[ 1 ], aPt[ 2 ], nil, nil, .t. )
endif
return aPt
File: whatsnew.txt (L591-594)
* Fix: Fixed methods Row,Col of TMonitor class in getsysin.prg by
Modified methods
METHOD Row(r) INLINE ( ::nTop + r )
METHOD Col(c) INLINE ( ::nLeft + c )
File: whatsnew.txt (L596-598)
* Enhanced: Multi-monitor friendly Activate commands for Windows/Dialogs:
ACTIVATE WINDOW [<clauses,...] IN nMonitor/oMonitor
ACTIVATE DIALOG [<clauses,...] IN oWnd/nMonitor/oMonitor
File: whatsnew.txt (L600-602)
* New function FW_SetMonitor(…) -> oPrevMonitor
All windows/dialogs will open in the new monitor set.
Params: nMonitor, oMonitor, oWnd, (nrow,col)
File: whatsnew.txt (L4863-4934)
* Multiple monitor support functions:
- New function FW_GetAllMonitors() --> Array of TMonitor objects
- Fixes and enhancement to FW_GetMonitor( nthMonitor )
DECEMBER 2018
=============
* New methods in TWindow, applicable to dialogs also, but not controls,
mdichild windows and child windows.
- METHOD SaveState() --> cState //text string of 88 chars containing
encoded information of the postion ans state of window, for saving and
later using with RestoreState
- METHOD RestoreState( cState ) //Restores window to the previusly saved
state.
Usage:
ACTIVATE WINDOW oWnd ;
ON INIT oWnd:RestoreState( MemoRead( "wndstate.txt" ) ) ;
VALID ( MemoWrit( "wndstate.txt", oWnd:SaveState() ), .t. )
- METHOD Arrow( r1, c1, r2, c2, [nClr], [nPenSize] )
Draws an arrow from point r1,c1 to r2,c2 with arrow head pointing to
r2,c2.
* Multiple Monitors support functions:
fwh\source\function\getsysin.prg
- FW_ActiveMonitors() --> nMonitors
Number of extended monitors currently active.
- FW_VirtualScreen() --> oRect
TRect object representing the area of virtual screen covered by all the
extended monitors. (Note: The object has datas nTop, nLeft, nBottom,
nRight, nWidth, nHeight)
The total width of the rectangle is the sum of horizontal resolutions of
all the monitors and height is the maximum of the vertical resolutions
of all the monitors.
Recommended reading for better understanding
https://docs.microsoft.com/en-us/windows/desktop/gdi/the-virtual-screen
https://docs.microsoft.com/en-us/windows/desktop/gdi/multiple-display-monitors
- FW_GetMonitor(params) --> oMonitor (TMonitor object)
params:
FW_GetMonitor() --> oPrimaryMonitor
FW_GetMonitor( n ) --> nth Monitor object. Defaults to primary monitor
if there is only one active monitor.
FW_GetMonitor( oWnd/hWnd ) --> oMonitor containing max area of the window
FW_GetMonitor( nRow, nCol ) --> oMonitor containing the coordinates
- TMonitor class datas and methods
DATAS:
lPrimary // Is primary monitor
nTop, nLeft, nBottom, nRight, nWidth, nHeight
CenterPt --> { nRow, nCol }
METHODS:
Row( nMonitorRow ) --> nRow on Virtual Screen
Col( nMonitorCol ) --> nCol on Virtual Screen
Center( oWnd/hWnd ) --> Centers the window inside the monitor
Move( oWnd/hWnd ) --> Sets the window in the monitor with the same
relative coordinates
Example Usage:
ACTIVATE WINDOW oWnd ON INIT FW_GetMonitor( 2 ):Center( oWnd )
ACTIVATE WINDOW oWnd ON INIT FW_GetMonitor( 2 ):Move( oWnd )
If the program is run on a single monitor the display defaults to the
primary monitor
File: source/classes/window.prg (L1052-1059)
DEFAULT oMonitor := FW_SetMonitor()
if oMonitor != nil
if HB_ISNUMERIC( oMonitor )
oMonitor := FW_GetMonitor( oMonitor )
elseif !( HB_ISOBJECT( oMonitor ) .and. ( oMonitor:IsKindOf( "TMONITOR" ) .or. oMonitor:IsKindOf( "TWINDOW" ) ) )
oMonitor := nil
endif
endif