Class VirtualScreen() Foundation

The VirtualScreen() class helps to handle a multi-screen environment

Description

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:

The virtual screen

(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.

Scale factor (DPI) in Xbase Part-based applications:Xbase Part-based applications use a fixed unit system. Each logical point on the screen corresponds to a pixel on the respective physical screen and has a resolution of approximately 96 DPI. On physical screens using a different scale factor (DPI), the operating system automatically scales the application UI to the user's preferred scale. For this reason, the scale factor reported to the application always is 1.0.
Coordinate system in Xbase Part-based applications:Unless configured otherwise, the origin ({0,0}) is located at the bottom/left corner of the coordinate system. Windows uses a top/left origin, however. This means that although application units (points) and device units (pixels) are identical in scale, rectangles and positions as seen by the application and by the operating system differ.

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.

Attribute: READONLY
Class methods
:getPhysicalScreenCount()
Get the number of physical screens connected to the computer.
:getPhysicalScreen()
Get a physical screen connected to the computer.
:getPhysicalScreens()
Get the physical screens connected to the computer.
:getPhysicalScreenFromPos()
Get the physical screen containing a certain point.
:getPhysicalScreenFromWindow()
Get the physical screen a certain window is being displayed on.
:getPointerPos()
Get the current position of the mouse pointer
:getRect()
Get the rectangle encompassing the virtual screen.
:getSize()
Get the size of the virtual screen.
:pointToPixel()
Convert a point from logical to system coordinates.
:pixelToPoint()
Convert a point from system to logical coordinates.
Examples
Example for creating windows on the primary and a non primary screen

// 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 
Example for making window cover all of the virtual screen

// 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) 
Feedback

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.