Xinerama Extension to the X Window System

By Irene McCartney and Madeline Asmus

Compaq Computer

 

 

The Xinerama extension allows a system configured with multiple graphics adapters and video monitors (a multi-headed system) to operate the screens as a single large screen. Windows can span multiple screens and can move from on screen to another. This extension is supported with graphics that have common visuals, depths and resolution.

 

 

 

 

 

 

 

 

Introduction

Xinerama is an X Server Extension, which allows existing client applications to operate as if there is a single screen when in fact there is more than 1 screen. Client applications can be modified to use the Xinerama API, allowing an application to get real X Server screen information related to the real number of physical screen, and their corresponding screen widths and heights. While modifications are not required, they are suggested if the application centers windows (such as dialog boxes) which, with an even numbered of screens, would cause the window to be split between screens.

In the advent of window managers such as CDE which do not always handle multiscreen configurations in a flexible manner, Xinerama allows the user to take full advantage of their total screen real estate. For software developers, this extension makes it easy to maintain a debug window and an editor window that do not overlap. They can also have a full screen application window and a debug window side by side for easy visibility. For a web-page developer, they can display a full screen viewer and a page editor without them overlapping. For anyone editing files, you can simultaneously see the current version of the file and edit it without having to constantly switch screen ordering. In all of these cases, windows may be moved between screens at any time by simply clicking and dragging.

The Xinerama extension is implemented at the device independent level of the X Window System server. This allows new graphics adapters to take advantage of the extension without any additional work. This was considered a major design consideration since graphics adapters currently tend to have a relatively short marketable lifetime. A description of the implementation follows.

 

Implementation

The implementation of Xinerama is described in terms of 6 areas. They are: Xinerama protocol, X Server protocol, screen initialization, screen configuration, screen resource, and graphics events.

 

Xinerama Protocol

The Xinerama extension protocol provides a public interface for querying the Xserver about Xinerama. It is important to note that even if a client application is not modified to use the extension protocol, applications will still work with a Xserver running with Xinerama enabled. Using the protocol allows applications to avoid displaying windows split between screens. The interface provides the following:

QueryExtension - indicates support of Xinerama. Also returns the first error and event code associated with the extension.

QueryVersion - returns major and minor version numbers of the extension

GetStat - indicates if Xinerama is enabled or disabled.

GetScreenCount - returns number of physical screens.

GetScreenSize`- returns resolution of a physical screen queried.

The following data structure is used for storing the extension information
that is then used when replying to extension requests.

typedef struct {
Window window; /* PanoramiX window - may not exist */
int screen;
int State; /* PanroamiXOff, PanoramiXOn */
int width; /* width of this screen */
int height; /* height of this screen */
int ScreenCount; /* real physical number of screens */
XID eventMask; /* selected events for this client */
} XPanoramiXInfo;

X Server Protocol

A number of X Server functions are replaced such that dispatch calls the alternate function. The alternate function is responsible for validating the request packet, locating and substituting per screen any needed resources, making necessary coordinate adjustments and then calling the original X Server function.

The following code segment is an example for the X Server protocol function PolyText16.

int
PanoramiXPolyText16(register ClientPtr client)
{
int result, j;

PanoramiXWindow *pPanoramiXRoot;
PanoramiXWindow *pPanoramiXWin;
PanoramiXGC *pPanoramiXGC = PanoramiXGCRoot;
DrawablePtr pDraw;
GC *pGC;
int orig_x, orig_y;
REQUEST(xPolyTextReq);

VALIDATE_DRAWABLE_AND_GC(stuff->drawable, pDraw, pGC, client);
pPanoramiXRoot = (pDraw->type == DRAWABLE_PIXMAP)
? PanoramiXPmapRoot : PanoramiXWinRoot;
pPanoramiXWin = pPanoramiXRoot;
PANORAMIXFIND_ID(pPanoramiXWin, stuff->drawable);
/* In the case of Multibuffering, we need to make sure the drawable
isn't really a pixmap associated to a drawable */
if (!pPanoramiXWin && (stuff->drawable != pDraw->id))
{
pPanoramiXWin = PanoramiXPmapRoot;
PANORAMIXFIND_ID(pPanoramiXWin, stuff->drawable);
}
IF_RETURN(!pPanoramiXWin, BadDrawable);
PANORAMIXFIND_ID(pPanoramiXGC, stuff->gc);
IF_RETURN(!pPanoramiXGC, BadGC);
orig_x = stuff->x;
orig_y = stuff->y;
FOR_NSCREENS_OR_ONCE((pPanoramiXWin && pPanoramiXGC), j) {
stuff->drawable = pPanoramiXWin->info[j].id;
stuff->gc = pPanoramiXGC->info[j].id;
stuff->x = orig_x;
stuff->y = orig_y;
if (pPanoramiXWin == pPanoramiXRoot) {
stuff->x = orig_x - panoramiXdataPtr[j].x;
stuff->y = orig_y - panoramiXdataPtr[j].y;
} else {
if ( (pDraw->type == DRAWABLE_PIXMAP)
&& /* add special case check for text on root window */
(pDraw->width == (panoramiXdataPtr[PanoramiXNumScreens-1].x +
panoramiXdataPtr[PanoramiXNumScreens-1].width)) )
{
stuff->x = orig_x - panoramiXdataPtr[j].x;
stuff->y = orig_y - panoramiXdataPtr[j].y;
}
}
if (!j)
noPanoramiXExtension = TRUE;
result = (*SavedProcVector[X_PolyText16])(client);
noPanoramiXExtension = FALSE;
BREAK_IF(result != Success);
}
return (result);

}

Screen Initialization

The Xinerama extension is initialized after all of the graphics devices are initialized. During the extension initialization, the following is done:

 

Xinerama is added to the list of available server extensions

Memory for extension resources and linked lists are allocated

Screen functions are wrapped

Dispatch functions are replaced

The initialization involves calling the standard CreateConnectionBlock routine, after which it then modifies the connection block to represent one screen with a composite width and height of all combined physical displays. At this point, the X Server appears to client requests as a single screen display.

Screen Configuration

Screen information is maintained such that the core Xserver can easily determine each physical screen resolution and position. The GCOps, GCFunc, and screen functions in ScreenPtr are called per screen, which requires coordinate adjustments based on screen positioning. For instance, the process of grabbing a window, and moving it across screens. The mouse position and window get their coordinates adjusted relating to the physical screen information, thus allowing normal clipping logic to draw the correct pixels per screen. Similarly, application windows can be enlarged to the size of the entire single screen. If for instance there are two physical screens and on screen 1, an application window is displayed. Internal to the X Server, there are really 2 application windows, and the only difference is how the coordinates relate to a physical screen. In this case for instance, the window is out of physical screen boundaries, and considered clipped. As the window is moved, and the coordinates change, sections of the application may become visible on screen 0.

The following code segment is used by ConfigureWindow to prevent
duplicate configureNotify events from being generated when a window is
split between screens.

#ifdef PANORAMIX
Bool
PanoramiXWindowOffScreen(pWin,w, h)
register WindowPtr pWin;
unsigned short w, h;
{
register ScreenPtr pScreen = pWin->drawable.pScreen;
int Scr;

Scr = (pWin->drawable.pScreen)->myNum;

if ((pWin->drawable.x < 0) &&
((pWin->drawable.x + w) < 0))
return TRUE;
if ((pWin->drawable.x > panoramiXdataPtr[Scr].width) &&
((pWin->drawable.x + w) > panoramiXdataPtr[Scr].width))
return TRUE;
if ((pWin->drawable.y < 0) &&
((pWin->drawable.y + h) < 0))
return TRUE;
if ((pWin->drawable.y > panoramiXdataPtr[Scr].height) &&
((pWin->drawable.y + h) > panoramiXdataPtr[Scr].height) )
return TRUE;

return FALSE;
}
#endif

Screen Resources

There are four types of resources identified by Xinerama, which require unique instances per screen, they are: Window, GC, Colormap and Pixmap. The resources of physical Screen 0 always represents the resource identifier known by client requests. The core Xserver creates all resources, using FakeClientID, for all physical screens, other than 0. These resources are easily mapped to its corresponding client request resource. Resource lists are created and maintained by the extension. Resources are added and deleted as needed. Functions, which are replaced by the extension, such as CreateWindow and CreateGC, create instances of the resource for each screen. Functions, which validate resources passed in a request packet, search the extensions resources list to find match based on Screen 0. When a match is found, the appropriate resource per screen gets used.

The following macros are used throughout the extension for locating resources in the extensions resource lists. The code segment in the X Server Protocol Modifications section shows example usage of the first macro listed here.

#define PANORAMIXFIND_ID(a, b) \
for (; (a) && ((a)->info[0].id != (b)); (a) = (a)->next)

#define PANORAMIXFIND_ID_BY_SCRNUM(a, b ,i) \
for (; (a) && ((a)->info[i].id != (b)); (a) = (a)->next)

#define PANORAMIXFIND_LAST(a,b) \
for ((a) = (b); (a)->next; (a) = (a)->next)

Graphics Events

Graphic events are generated from the low level graphics engine, which can be evident on any screen. Relaying graphics events back to a client in terms of screens other than 0 can not be done. In these cases, reply packets are modified such that the resources ids are returned in terms of screen 0, and any associated coordinate information is modified in terms of the single screen. Event code in the device independent layer of the core X server was modified. A summary of the changes that were necessary is as follows:

Return the pointer location on a single screen instead of the physical screen. The screen id returned must also be the client known resource id.

Derive the window pointers from the client-known window id.

The cursor location is calculated by using the single screen width and height.

Values for rootX and rootY are determined based on the physical location of the cursor (screen and coordinates) and the screens X and Y offset from the origin of the single combined window.

The window value used for the confineTo window is the extension window id if it is the root window.

The routine replies with adjusted winX, winY, rootX and rootY values based on the single, combined screen, the cursor location on it and the extensionís combined physical screen data.

The routine was changed such that CheckPassiveGrabsOnWindow is called for each screen with the appropriate extension window ID.

The physical screen number for the cursor is determined and the corresponding extension window id is passed to the standard SetInputFocus/GetInputFocus.

This routine was modified such that events that are necessary per screen, such as the LeaveNotify event, send an event with a client-known id. Events that would get generated per screen but are not useful are skipped.

 

Conclusion

Xinerama is a single-logical screen extension which shipped as part of the X Version 11, Release 6.4 (X11R6.4). It is implemented completely at the device independent layer of the Xserver. In general, no client changes are required to use this extension. However, a programming interface is available for those clients that wish to obtain the information.