Class VirtualScreen() Foundation
The VirtualScreen() class helps to handle a multi-screen environment
The VirtualScreen class provides interfaces for getting information about and for working with the physical screens connected to the computer. With the exception of special-purpose screens, these screens work together and form the virtual screen (desktop).
The virtual screen
By default, the virtual screen is comprised only of the primary screen. This usually is the principal display hardware which came with the computer, for example, the LCD screen built into a laptop. The virtual screen, however, can be extended by connecting additional physical screen to the computer and these screens can be arranged next to each other in all possible manners. Examples for such an arrangement are shown in the following picture:

(1) is the primary (built-in) screen, whereas (2) denotes a screen connected externally. So in these examples, the virtual screen is comprised of two physical screens. This is where Windows displays its desktop, and where applications can position their windows. Note how the secondary screen is arranged differently in relation to the primary screen in the examples above. Note also the unused space on the virtual screen which is not assigned to any physical screen. Windows positioned in this area of the virtual screen would not be visible. More information about each physical screen, the screen area it occupies on the virtual screen and its position in the screen arrangement is provided via the PhysicalScreen class.
Role of the primary screen
The primary screen has a special role in multi-screen environments. It is the default area where windows are displayed in. If a window is created without specifying a position, for example, it is opened on the primary screen. Also, if a physical screen is disconnected, the windows which are currently displayed on it are relocated to the primary screen. Finally, the origin of the coordinate system ({0,0}) is always on the primary screen.
Coordinate system
The coordinate system spans the whole virtual screen, and therefore may span several physical screens. The origin of the coordinate system is located on the primary screen. In order to make a window appear on a certain screen, the window's position must be adjusted so that it is located in the respective display rectangle. The PhysicalScreen class can be used to determine this rectangle. The class also features a dedicated method for this purpose: :setWindow().
Scale factor (DPI), logical points and rectangles versus device units (pixels)
On modern operating systems, each physical screen may use a dedicated scale factor (DPI).This scale factor often needs to be adjusted by the user, especially on high-resolution screens. Otherwise, content displayed on these screens becomes illegible because high-density pixels simply are too small to be discernible. For this reason, on such screens the content must be scaled to the user's preferred proportions. The scale factor (DPI setting) used on a certain physical screen can be determined via the screen's scale factor member variable.
Because the scale factor may differ between screens, the ratio between logical (application) and device units (pixels) also differs. For this reason, the display rectangles as seen by the application may not be consecutive. In addition, depending on how the screens are arranged next to each other on the virtual screen, some of the coordinates may be negative. Therefore, instead of making assumptions such as "adding one to the x position will move the window to the screen on the right", applications should use the interfaces provided by the VirtualScreen class to determine the required window position on the virtual screen.
On screens using a different scale factor than the system default (100%, 96 DPI), the units used by the application, for example, for specifying window positions, differ from those used by the operating system. Whereas the application uses logical units (points), device units (pixels) are used on the API-level. The VirtualScreen class provides the methods :pointToPixel() and :pixelToPoint() for converting between these two coordinate spaces.
// Demonstrates using the VirtualScreen class for displaying a window on the primary and a
// non primary screen.
#include "xbp.ch"
PROCEDURE Main
LOCAL oMain
LOCAL oExt
LOCAL oScreen
LOCAL aScreens
LOCAL nIdx
// Create the main form of the app. This form automatically appears on the primary screen
oMain := CreateForm( "Application's main window" )
oMain:setTitle( "Virtual Screen Example" )
CenterControl( oMain )
/* Get all screens and identify the first non primary screen, then set
* the window to it
*/
aScreens := VirtualScreen():getPhysicalScreens()
nIdx := AScan( aScreens, {|o| !o:IsPrimary()} )
IF nIdx == 0
MsgBox( "Error - no external display!" )
QUIT
ENDIF
oScreen := aScreens[nIdx]
oExt := CreateForm( "Window on external screen" )
oScreen:setwindow( oExt )
oExt:setFrameState( XBPDLG_FRAMESTAT_KIOSK )
oMain:showModal()
RETURN
// Create a form with a label control displaying a specified text
FUNCTION CreateForm( cLabel )
LOCAL oForm
LOCAL oXbp
oForm := XbpDialog():new( AppDesktop() )
oForm:tasklist := .T.
oForm:visible := .F.
oForm:create( ,,, {400,400} )
oXbp := XbpStatic():new( oForm:drawingArea )
oXbp:caption := cLabel
oXbp:autoSize := .T.
oXbp:create( ,, {10,10} )
RETURN oForm
PROCEDURE AppSys()
// Prevent creation of default Crt window
RETURN
// The example demonstrates making a window cover the virtual screen.
#include "xbp.ch"
PROCEDURE Main
LOCAL oDlg
LOCAL oScreen
LOCAL aPos
LOCAL aSize
LOCAL oBmp
LOCAL aRect
// Load a bitmap for display in the form's background
oBmp := XbpBitmap():new():create()
oBmp:loadFile( GetBitmapPath() + "\ablue.bmp" )
// Create the form to display on the virtual screen. Note that
// the window is created borderless so that it covers everything,
// including the task bar(s)
oDlg := XbpDialog():new( Appdesktop() )
oDlg:tasklist := .T.
oDlg:titleBar := .F.
oDlg:border := XBPDLG_NO_BORDER
oDlg:drawingArea:bitmap := oBmp
oDlg:drawingArea:options:= XBP_IMAGE_SCALED
oDlg:visible := .F.
oDlg:create(,,, {200,200})
// Get the dimensions of the virtual screen and use this to
// position and size the form
aRect := VirtualScreen():getRect()
aRect := VirtualScreen():pixelToPoint( aRect )
aPos := {aRect[1],aRect[2]}
aSize := {aRect[3]-aRect[1],aRect[4]-aRect[2]}
oDlg:setPos( aPos )
oDlg:setSize( aSize )
oDlg:showModal()
RETURN
PROCEDURE AppSys()
// Prevent creation of default Crt window
RETURN
// Get path to bitmap resources from "xppresource" environment variable
FUNCTION GetBitmapPath()
LOCAL nStrt
LOCAL nEnd
LOCAL cPath
cPath = GetEnv( "xppresource" )
nEnd := At( "\bitmap", cPath )
IF nEnd == 0
RETURN ""
ENDIF
nStrt := RAt( ";", cPath, nEnd )
IF nStrt == 0
RETURN SubStr( cPath, 1, nEnd +6 )
ENDIF
RETURN SubStr(cPath, nStrt+1, nEnd +6 - nStrt)
If you see anything in the documentation that is not correct, does not match your experience with the particular feature or requires further clarification, please use this form to report a documentation issue.