From c1e6c7428a8d2c1b60ffac7df7a3f56c300fa983 Mon Sep 17 00:00:00 2001 From: marha Date: Thu, 22 Sep 2011 15:20:09 +0200 Subject: libxtrans libX11 libX11 libXext mesa xserver git update 22 sep 2011 --- xorg-server/hw/dmx/config/xdmxconfig.c | 23 +- xorg-server/hw/dmx/dmx.c | 2165 +++++---- xorg-server/hw/dmx/dmxclient.h | 1 - xorg-server/hw/dmx/dmxcursor.h | 4 +- xorg-server/hw/dmx/dmxextension.c | 2 +- xorg-server/hw/dmx/dmxinit.c | 11 +- xorg-server/hw/dmx/dmxprop.c | 695 +-- xorg-server/hw/dmx/doc/dmx.xml | 6860 ++++++++++++++--------------- xorg-server/hw/dmx/examples/dmxwininfo.c | 3 +- xorg-server/hw/dmx/glxProxy/glxcmds.c | 41 +- xorg-server/hw/dmx/glxProxy/glxscreens.c | 703 ++- xorg-server/hw/dmx/glxProxy/glxserver.h | 612 ++- xorg-server/hw/dmx/glxProxy/render2swap.c | 2 +- xorg-server/hw/dmx/input/dmxinputinit.c | 6 +- xorg-server/hw/dmx/input/lnx-keyboard.c | 1980 ++++----- xorg-server/hw/dmx/input/usb-common.c | 762 ++-- 16 files changed, 6915 insertions(+), 6955 deletions(-) (limited to 'xorg-server/hw/dmx') diff --git a/xorg-server/hw/dmx/config/xdmxconfig.c b/xorg-server/hw/dmx/config/xdmxconfig.c index 2de7f2b85..3165ba000 100644 --- a/xorg-server/hw/dmx/config/xdmxconfig.c +++ b/xorg-server/hw/dmx/config/xdmxconfig.c @@ -49,7 +49,6 @@ #include #include #include -#include #include "Canvas.h" #include "dmxparse.h" @@ -199,8 +198,8 @@ static void dmxConfigDataUpdate(void) XtVaSetValues(ndbutton1, XtNsensitive, False, NULL); } else { name = dmxConfigCurrent->name; - XmuSnprintf(cnambuf, sizeof(cnambuf), "%s", name ? name : ""); - XmuSnprintf(cdimbuf, sizeof(cdimbuf), "%dx%d", + snprintf(cnambuf, sizeof(cnambuf), "%s", name ? name : ""); + snprintf(cdimbuf, sizeof(cdimbuf), "%dx%d", dmxConfigWallWidth, dmxConfigWallHeight); XtVaSetValues(cnamebox, XtNlabel, cnambuf, XtNsensitive, True, NULL); XtVaSetValues(cdimbox, XtNlabel, cdimbuf, XtNsensitive, True, NULL); @@ -219,22 +218,22 @@ static void dmxConfigDataUpdate(void) XtVaSetValues(ddbutton, XtNsensitive, False, NULL); } else { name = dmxConfigCurrentDisplay->name; - XmuSnprintf(nambuf, sizeof(nambuf), "%s", name ? name : ""); - XmuSnprintf(dimbuf, sizeof(dimbuf), "%dx%d%c%d%c%d", + snprintf(nambuf, sizeof(nambuf), "%s", name ? name : ""); + snprintf(dimbuf, sizeof(dimbuf), "%dx%d%c%d%c%d", dmxConfigCurrentDisplay->scrnWidth, dmxConfigCurrentDisplay->scrnHeight, dmxConfigCurrentDisplay->scrnXSign < 0 ? '-' : '+', dmxConfigCurrentDisplay->scrnX, dmxConfigCurrentDisplay->scrnYSign < 0 ? '-' : '+', dmxConfigCurrentDisplay->scrnY); - XmuSnprintf(rtbuf, sizeof(dimbuf), "%dx%d%c%d%c%d", + snprintf(rtbuf, sizeof(dimbuf), "%dx%d%c%d%c%d", dmxConfigCurrentDisplay->rootWidth, dmxConfigCurrentDisplay->rootHeight, dmxConfigCurrentDisplay->rootXSign < 0 ? '-' : '+', dmxConfigCurrentDisplay->rootX, dmxConfigCurrentDisplay->rootYSign < 0 ? '-' : '+', dmxConfigCurrentDisplay->rootY); - XmuSnprintf(offbuf, sizeof(offbuf), "@%dx%d", + snprintf(offbuf, sizeof(offbuf), "@%dx%d", dmxConfigCurrentDisplay->rootXOrigin, dmxConfigCurrentDisplay->rootYOrigin); XtVaSetValues(namebox, XtNlabel, nambuf, XtNsensitive, True, NULL); @@ -596,14 +595,14 @@ static void dmxConfigCanCallback(Widget w, XtPointer closure, static void dmxConfigECCallback(Widget w, XtPointer closure, XtPointer callData) { - char buf[256]; /* RATS: Only used in XmuSnprintf */ + char buf[256]; /* RATS: Only used in snprintf */ if (!dmxConfigCurrent) return; dmxConfigSetPopupPosition(ecpopup); XtVaSetValues(ecdialog0, XtNvalue, dmxConfigCurrent->name ? dmxConfigCurrent->name : "", NULL); - XmuSnprintf(buf, sizeof(buf), "%dx%d", + snprintf(buf, sizeof(buf), "%dx%d", dmxConfigCurrent->width, dmxConfigCurrent->height); XtVaSetValues(ecdialog1, XtNvalue, buf, NULL); XtPopup(ecpopup, XtGrabExclusive); @@ -692,7 +691,7 @@ static void dmxConfigECCanCallback(Widget w, XtPointer closure, static void dmxConfigEDCallback(Widget w, XtPointer closure, XtPointer callData) { - char buf[256]; /* RATS: Only used in XmuSnprintf */ + char buf[256]; /* RATS: Only used in snprintf */ if (!dmxConfigCurrent || !dmxConfigCurrentDisplay) return; dmxConfigSetPopupPosition(edpopup); @@ -701,7 +700,7 @@ static void dmxConfigEDCallback(Widget w, XtPointer closure, ? dmxConfigCurrentDisplay->name : "", NULL); - XmuSnprintf(buf, sizeof(buf), "%dx%d%c%d%c%d", + snprintf(buf, sizeof(buf), "%dx%d%c%d%c%d", dmxConfigCurrentDisplay->scrnWidth, dmxConfigCurrentDisplay->scrnHeight, dmxConfigCurrentDisplay->scrnXSign < 0 ? '-' : '+', @@ -709,7 +708,7 @@ static void dmxConfigEDCallback(Widget w, XtPointer closure, dmxConfigCurrentDisplay->scrnYSign < 0 ? '-' : '+', dmxConfigCurrentDisplay->scrnY); XtVaSetValues(eddialog1, XtNvalue, buf, NULL); - XmuSnprintf(buf, sizeof(buf), "@%dx%d", + snprintf(buf, sizeof(buf), "@%dx%d", dmxConfigCurrentDisplay->rootXOrigin, dmxConfigCurrentDisplay->rootYOrigin); XtVaSetValues(eddialog2, XtNvalue, buf, NULL); diff --git a/xorg-server/hw/dmx/dmx.c b/xorg-server/hw/dmx/dmx.c index 5de565f85..01a744849 100644 --- a/xorg-server/hw/dmx/dmx.c +++ b/xorg-server/hw/dmx/dmx.c @@ -1,1097 +1,1068 @@ -/* - * Copyright 2002-2004 Red Hat Inc., Durham, North Carolina. - * - * All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation on the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial - * portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NON-INFRINGEMENT. IN NO EVENT SHALL RED HAT AND/OR THEIR SUPPLIERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/* - * Authors: - * Rickard E. (Rik) Faith - * - */ - -/** \file - * This file implements the server-side part of the DMX protocol. A - * vector of fucntions is provided at extension initialization time, so - * most all of the useful functions in this file are declared static and - * do not appear in the doxygen documentation. - * - * Much of the low-level work is done by functions in \a dmxextension.c - * - * Please see the Client-to-Server DMX Extension to the X Protocol - * document for details about the protocol. */ - -#ifdef HAVE_DMX_CONFIG_H -#include -#endif - -#include -#include -#include "misc.h" -#include "os.h" -#include "dixstruct.h" -#include "extnsionst.h" -#include "opaque.h" - -#include "dmxextension.h" -#include -#include -#include "protocol-versions.h" - -#ifdef PANORAMIX -#include "panoramiX.h" -extern unsigned long XRT_WINDOW; -extern int PanoramiXNumScreens; -#endif - -extern void DMXExtensionInit(void); - -static unsigned char DMXCode; - - - -static int _DMXXineramaActive(void) -{ -#ifdef PANORAMIX - return !noPanoramiXExtension; -#endif - return 0; -} - -static void dmxSetScreenAttribute(int bit, DMXScreenAttributesPtr attr, - CARD32 value) -{ - switch (1 << bit) { - case DMXScreenWindowWidth: attr->screenWindowWidth = value; break; - case DMXScreenWindowHeight: attr->screenWindowHeight = value; break; - case DMXScreenWindowXoffset: attr->screenWindowXoffset = value; break; - case DMXScreenWindowYoffset: attr->screenWindowYoffset = value; break; - case DMXRootWindowWidth: attr->rootWindowWidth = value; break; - case DMXRootWindowHeight: attr->rootWindowHeight = value; break; - case DMXRootWindowXoffset: attr->rootWindowXoffset = value; break; - case DMXRootWindowYoffset: attr->rootWindowYoffset = value; break; - case DMXRootWindowXorigin: attr->rootWindowXorigin = value; break; - case DMXRootWindowYorigin: attr->rootWindowYorigin = value; break; - } -} - -static int dmxFetchScreenAttributes(unsigned int mask, - DMXScreenAttributesPtr attr, - CARD32 *value_list) -{ - int i; - CARD32 *value = value_list; - int count = 0; - - for (i = 0; i < 32; i++) { - if (mask & (1 << i)) { - dmxSetScreenAttribute(i, attr, *value); - ++value; - ++count; - } - } - return count; -} - -static void dmxSetDesktopAttribute(int bit, DMXDesktopAttributesPtr attr, - CARD32 value) -{ - switch (1 << bit) { - case DMXDesktopWidth: attr->width = value; break; - case DMXDesktopHeight: attr->height = value; break; - case DMXDesktopShiftX: attr->shiftX = value; break; - case DMXDesktopShiftY: attr->shiftY = value; break; - } -} - -static int dmxFetchDesktopAttributes(unsigned int mask, - DMXDesktopAttributesPtr attr, - CARD32 *value_list) -{ - int i; - CARD32 *value = value_list; - int count = 0; - - for (i = 0; i < 32; i++) { - if (mask & (1 << i)) { - dmxSetDesktopAttribute(i, attr, *value); - ++value; - ++count; - } - } - return count; -} - -static void dmxSetInputAttribute(int bit, DMXInputAttributesPtr attr, - CARD32 value) -{ - switch (1 << bit) { - case DMXInputType: attr->inputType = value; break; - case DMXInputPhysicalScreen: attr->physicalScreen = value; break; - case DMXInputSendsCore: attr->sendsCore = !!value; break; - } -} - -static int dmxFetchInputAttributes(unsigned int mask, - DMXInputAttributesPtr attr, - CARD32 *value_list) -{ - int i; - CARD32 *value = value_list; - int count = 0; - - for (i = 0; i < 32; i++) { - if (mask & (1 << i)) { - dmxSetInputAttribute(i, attr, *value); - ++value; - ++count; - } - } - return count; -} - -static int ProcDMXQueryVersion(ClientPtr client) -{ - xDMXQueryVersionReply rep; - int n; - - REQUEST_SIZE_MATCH(xDMXQueryVersionReq); - - rep.type = X_Reply; - rep.sequenceNumber = client->sequence; - rep.length = 0; - rep.majorVersion = SERVER_DMX_MAJOR_VERSION; - rep.minorVersion = SERVER_DMX_MINOR_VERSION; - rep.patchVersion = SERVER_DMX_PATCH_VERSION; - if (client->swapped) { - swaps(&rep.sequenceNumber, n); - swapl(&rep.length, n); - swapl(&rep.majorVersion, n); - swapl(&rep.minorVersion, n); - swapl(&rep.patchVersion, n); - } - WriteToClient(client, sizeof(xDMXQueryVersionReply), (char *)&rep); - return Success; -} - -static int ProcDMXSync(ClientPtr client) -{ - xDMXSyncReply rep; - int n; - - REQUEST_SIZE_MATCH(xDMXSyncReq); - - dmxFlushPendingSyncs(); - - rep.type = X_Reply; - rep.sequenceNumber = client->sequence; - rep.length = 0; - rep.status = 0; - if (client->swapped) { - swaps(&rep.sequenceNumber, n); - swapl(&rep.length, n); - swapl(&rep.status, n); - } - WriteToClient(client, sizeof(xDMXSyncReply), (char *)&rep); - return Success; -} - -static int ProcDMXForceWindowCreation(ClientPtr client) -{ - xDMXForceWindowCreationReply rep; - REQUEST(xDMXForceWindowCreationReq); - WindowPtr pWin; - int n; - - REQUEST_SIZE_MATCH(xDMXForceWindowCreationReq); - -#ifdef PANORAMIX - if (!noPanoramiXExtension) { - PanoramiXRes *win; - int i; - - if (Success != dixLookupResourceByType((pointer*) &win, - stuff->window, XRT_WINDOW, - client, DixReadAccess)) - return -1; /* BadWindow */ - - FOR_NSCREENS(i) { - if (Success != dixLookupWindow(&pWin, win->info[i].id, client, - DixReadAccess)) - return -1; /* BadWindow */ - - dmxForceWindowCreation(pWin); - } - goto doreply; - } -#endif - - if (Success != dixLookupWindow(&pWin, stuff->window, client, - DixReadAccess)) - return -1; /* BadWindow */ - - dmxForceWindowCreation(pWin); - doreply: - dmxFlushPendingSyncs(); - rep.type = X_Reply; - rep.sequenceNumber = client->sequence; - rep.length = 0; - rep.status = 0; - if (client->swapped) { - swaps(&rep.sequenceNumber, n); - swapl(&rep.length, n); - swapl(&rep.status, n); - } - WriteToClient(client, sizeof(xDMXForceWindowCreationReply), (char *)&rep); - return Success; -} - -static int ProcDMXGetScreenCount(ClientPtr client) -{ - xDMXGetScreenCountReply rep; - int n; - - REQUEST_SIZE_MATCH(xDMXGetScreenCountReq); - - rep.type = X_Reply; - rep.sequenceNumber = client->sequence; - rep.length = 0; - rep.screenCount = dmxGetNumScreens(); - if (client->swapped) { - swaps(&rep.sequenceNumber, n); - swapl(&rep.length, n); - swapl(&rep.screenCount, n); - } - WriteToClient(client, sizeof(xDMXGetScreenCountReply), (char *)&rep); - return Success; -} - -static int ProcDMXGetScreenAttributes(ClientPtr client) -{ - REQUEST(xDMXGetScreenAttributesReq); - xDMXGetScreenAttributesReply rep; - int n; - int length; - int paddedLength; - DMXScreenAttributesRec attr; - - REQUEST_SIZE_MATCH(xDMXGetScreenAttributesReq); - - if (stuff->physicalScreen < 0 - || stuff->physicalScreen >= dmxGetNumScreens()) return BadValue; - - if (!dmxGetScreenAttributes(stuff->physicalScreen, &attr)) - return BadValue; - - rep.logicalScreen = attr.logicalScreen; - rep.screenWindowWidth = attr.screenWindowWidth; - rep.screenWindowHeight = attr.screenWindowHeight; - rep.screenWindowXoffset = attr.screenWindowXoffset; - rep.screenWindowYoffset = attr.screenWindowYoffset; - rep.rootWindowWidth = attr.rootWindowWidth; - rep.rootWindowHeight = attr.rootWindowHeight; - rep.rootWindowXoffset = attr.rootWindowXoffset; - rep.rootWindowYoffset = attr.rootWindowYoffset; - rep.rootWindowXorigin = attr.rootWindowXorigin; - rep.rootWindowYorigin = attr.rootWindowYorigin; - - length = attr.displayName ? strlen(attr.displayName) : 0; - paddedLength = pad_to_int32(length); - rep.type = X_Reply; - rep.sequenceNumber = client->sequence; - rep.length = bytes_to_int32((sizeof(xDMXGetScreenAttributesReply) - sizeof(xGenericReply)) - + paddedLength); - rep.displayNameLength = length; - - if (client->swapped) { - swaps(&rep.sequenceNumber, n); - swapl(&rep.length, n); - swapl(&rep.displayNameLength, n); - swapl(&rep.logicalScreen, n); - swaps(&rep.screenWindowWidth, n); - swaps(&rep.screenWindowHeight, n); - swaps(&rep.screenWindowXoffset, n); - swaps(&rep.screenWindowYoffset, n); - swaps(&rep.rootWindowWidth, n); - swaps(&rep.rootWindowHeight, n); - swaps(&rep.rootWindowXoffset, n); - swaps(&rep.rootWindowYoffset, n); - swaps(&rep.rootWindowXorigin, n); - swaps(&rep.rootWindowYorigin, n); - } - WriteToClient(client, sizeof(xDMXGetScreenAttributesReply), (char *)&rep); - if (length) WriteToClient(client, length, (char *)attr.displayName); - return Success; -} - -static int ProcDMXChangeScreensAttributes(ClientPtr client) -{ - REQUEST(xDMXChangeScreensAttributesReq); - xDMXChangeScreensAttributesReply rep; - int n; - int status = DMX_BAD_XINERAMA; - unsigned int mask = 0; - unsigned int i; - CARD32 *screen_list; - CARD32 *mask_list; - CARD32 *value_list; - DMXScreenAttributesPtr attribs; - int errorScreen = 0; - unsigned int len; - int ones = 0; - - - REQUEST_AT_LEAST_SIZE(xDMXChangeScreensAttributesReq); - len = client->req_len - bytes_to_int32(sizeof(xDMXChangeScreensAttributesReq)); - if (len < stuff->screenCount + stuff->maskCount) - return BadLength; - - screen_list = (CARD32 *)(stuff + 1); - mask_list = &screen_list[stuff->screenCount]; - value_list = &mask_list[stuff->maskCount]; - - for (i = 0; i < stuff->maskCount; i++) ones += Ones(mask_list[i]); - if (len != stuff->screenCount + stuff->maskCount + ones) - return BadLength; - - if (!_DMXXineramaActive()) goto noxinerama; - - if (!(attribs = malloc(stuff->screenCount * sizeof(*attribs)))) - return BadAlloc; - - for (i = 0; i < stuff->screenCount; i++) { - int count; - - if (i < stuff->maskCount) mask = mask_list[i]; - dmxGetScreenAttributes(screen_list[i], &attribs[i]); - count = dmxFetchScreenAttributes(mask, &attribs[i], value_list); - value_list += count; - } - -#if PANORAMIX - status = dmxConfigureScreenWindows(stuff->screenCount, - screen_list, - attribs, - &errorScreen); -#endif - - free(attribs); - - if (status == BadValue) return status; - - noxinerama: - rep.type = X_Reply; - rep.sequenceNumber = client->sequence; - rep.length = 0; - rep.status = status; - rep.errorScreen = errorScreen; - if (client->swapped) { - swaps(&rep.sequenceNumber, n); - swapl(&rep.length, n); - swapl(&rep.status, n); - swapl(&rep.errorScreen, n); - } - WriteToClient(client, - sizeof(xDMXChangeScreensAttributesReply), - (char *)&rep); - return Success; -} - -static int ProcDMXAddScreen(ClientPtr client) -{ - REQUEST(xDMXAddScreenReq); - xDMXAddScreenReply rep; - int n; - int status = 0; - CARD32 *value_list; - DMXScreenAttributesRec attr; - int count; - char *name; - int len; - int paddedLength; - - REQUEST_AT_LEAST_SIZE(xDMXAddScreenReq); - paddedLength = pad_to_int32(stuff->displayNameLength); - len = client->req_len - bytes_to_int32(sizeof(xDMXAddScreenReq)); - if (len != Ones(stuff->valueMask) + paddedLength/4) - return BadLength; - - memset(&attr, 0, sizeof(attr)); - dmxGetScreenAttributes(stuff->physicalScreen, &attr); - value_list = (CARD32 *)(stuff + 1); - count = dmxFetchScreenAttributes(stuff->valueMask, &attr, value_list); - - if (!(name = malloc(stuff->displayNameLength + 1 + 4))) - return BadAlloc; - memcpy(name, &value_list[count], stuff->displayNameLength); - name[stuff->displayNameLength] = '\0'; - attr.displayName = name; - - status = dmxAttachScreen(stuff->physicalScreen, &attr); - - free(name); - - rep.type = X_Reply; - rep.sequenceNumber = client->sequence; - rep.length = 0; - rep.status = status; - rep.physicalScreen = stuff->physicalScreen; - if (client->swapped) { - swaps(&rep.sequenceNumber, n); - swapl(&rep.length, n); - swapl(&rep.status, n); - swapl(&rep.physicalScreen, n); - } - WriteToClient(client, - sizeof(xDMXAddScreenReply), - (char *)&rep); - return Success; -} - -static int ProcDMXRemoveScreen(ClientPtr client) -{ - REQUEST(xDMXRemoveScreenReq); - xDMXRemoveScreenReply rep; - int n; - int status = 0; - - REQUEST_SIZE_MATCH(xDMXRemoveScreenReq); - - status = dmxDetachScreen(stuff->physicalScreen); - - rep.type = X_Reply; - rep.sequenceNumber = client->sequence; - rep.length = 0; - rep.status = status; - if (client->swapped) { - swaps(&rep.sequenceNumber, n); - swapl(&rep.length, n); - swapl(&rep.status, n); - } - WriteToClient(client, - sizeof(xDMXRemoveScreenReply), - (char *)&rep); - return Success; -} - - -#ifdef PANORAMIX -static int dmxPopulatePanoramiX(ClientPtr client, Window window, - CARD32 *screens, CARD32 *windows, - xRectangle *pos, xRectangle *vis) -{ - WindowPtr pWin; - PanoramiXRes *win; - int i; - int count = 0; - DMXWindowAttributesRec attr; - - if (Success != dixLookupResourceByType((pointer*) &win, - window, XRT_WINDOW, - client, DixReadAccess)) - return -1; /* BadWindow */ - - FOR_NSCREENS(i) { - if (Success != dixLookupWindow(&pWin, win->info[i].id, client, - DixReadAccess)) - return -1; /* BadWindow */ - if (dmxGetWindowAttributes(pWin, &attr)) { - screens[count] = attr.screen; - windows[count] = attr.window; - pos[count] = attr.pos; - vis[count] = attr.vis; - ++count; /* Only count existing windows */ - } - } - return count; -} -#endif - -static int dmxPopulate(ClientPtr client, Window window, CARD32 *screens, - CARD32 *windows, xRectangle *pos, xRectangle *vis) -{ - WindowPtr pWin; - DMXWindowAttributesRec attr; - -#ifdef PANORAMIX - if (!noPanoramiXExtension) - return dmxPopulatePanoramiX(client, window, screens, windows, - pos, vis); -#endif - - if (Success != dixLookupWindow(&pWin, window, client, DixReadAccess)) - return -1; /* BadWindow */ - - dmxGetWindowAttributes(pWin, &attr); - *screens = attr.screen; - *windows = attr.window; - *pos = attr.pos; - *vis = attr.vis; - return 1; -} - -static int dmxMaxNumScreens(void) -{ -#ifdef PANORAMIX - if (!noPanoramiXExtension) return PanoramiXNumScreens; -#endif - return 1; -} - -static int ProcDMXGetWindowAttributes(ClientPtr client) -{ - REQUEST(xDMXGetWindowAttributesReq); - xDMXGetWindowAttributesReply rep; - int i, n; - CARD32 *screens; - CARD32 *windows; - xRectangle *pos, *vis; - int count = dmxMaxNumScreens(); - - REQUEST_SIZE_MATCH(xDMXGetWindowAttributesReq); - - if (!(screens = malloc(count * sizeof(*screens)))) - return BadAlloc; - if (!(windows = malloc(count * sizeof(*windows)))) { - free(screens); - return BadAlloc; - } - if (!(pos = malloc(count * sizeof(*pos)))) { - free(windows); - free(screens); - return BadAlloc; - } - if (!(vis = malloc(count * sizeof(*vis)))) { - free(pos); - free(windows); - free(screens); - return BadAlloc; - } - - if ((count = dmxPopulate(client, stuff->window, screens, windows, - pos, vis)) < 0) { - free(vis); - free(pos); - free(windows); - free(screens); - return BadWindow; - } - - rep.type = X_Reply; - rep.sequenceNumber = client->sequence; - rep.length = count * 6; - rep.screenCount = count; - if (client->swapped) { - swaps(&rep.sequenceNumber, n); - swapl(&rep.length, n); - swapl(&rep.screenCount, n); - for (i = 0; i < count; i++) { - swapl(&screens[i], n); - swapl(&windows[i], n); - - swaps(&pos[i].x, n); - swaps(&pos[i].y, n); - swaps(&pos[i].width, n); - swaps(&pos[i].height, n); - - swaps(&vis[i].x, n); - swaps(&vis[i].y, n); - swaps(&vis[i].width, n); - swaps(&vis[i].height, n); - } - } - - dmxFlushPendingSyncs(); - - WriteToClient(client, sizeof(xDMXGetWindowAttributesReply), (char *)&rep); - if (count) { - WriteToClient(client, count * sizeof(*screens), (char *)screens); - WriteToClient(client, count * sizeof(*windows), (char *)windows); - WriteToClient(client, count * sizeof(*pos), (char *)pos); - WriteToClient(client, count * sizeof(*vis), (char *)vis); - } - - free(vis); - free(pos); - free(windows); - free(screens); - - return Success; -} - -static int ProcDMXGetDesktopAttributes(ClientPtr client) -{ - xDMXGetDesktopAttributesReply rep; - int n; - DMXDesktopAttributesRec attr; - - REQUEST_SIZE_MATCH(xDMXGetDesktopAttributesReq); - - dmxGetDesktopAttributes(&attr); - - rep.width = attr.width; - rep.height = attr.height; - rep.shiftX = attr.shiftX; - rep.shiftY = attr.shiftY; - - rep.type = X_Reply; - rep.sequenceNumber = client->sequence; - rep.length = 0; - - if (client->swapped) { - swaps(&rep.sequenceNumber, n); - swapl(&rep.length, n); - swapl(&rep.width, n); - swapl(&rep.height, n); - swapl(&rep.shiftX, n); - swapl(&rep.shiftY, n); - } - WriteToClient(client, sizeof(xDMXGetDesktopAttributesReply), (char *)&rep); - return Success; -} - -static int ProcDMXChangeDesktopAttributes(ClientPtr client) -{ - REQUEST(xDMXChangeDesktopAttributesReq); - xDMXChangeDesktopAttributesReply rep; - int n; - int status = DMX_BAD_XINERAMA; - CARD32 *value_list; - DMXDesktopAttributesRec attr; - int len; - - REQUEST_AT_LEAST_SIZE(xDMXChangeDesktopAttributesReq); - len = client->req_len - (sizeof(xDMXChangeDesktopAttributesReq) >> 2); - if (len != Ones(stuff->valueMask)) - return BadLength; - - if (!_DMXXineramaActive()) goto noxinerama; - - value_list = (CARD32 *)(stuff + 1); - - dmxGetDesktopAttributes(&attr); - dmxFetchDesktopAttributes(stuff->valueMask, &attr, value_list); - -#if PANORAMIX - status = dmxConfigureDesktop(&attr); -#endif - if (status == BadValue) return status; - - noxinerama: - rep.type = X_Reply; - rep.sequenceNumber = client->sequence; - rep.length = 0; - rep.status = status; - if (client->swapped) { - swaps(&rep.sequenceNumber, n); - swapl(&rep.length, n); - swapl(&rep.status, n); - } - WriteToClient(client, - sizeof(xDMXChangeDesktopAttributesReply), - (char *)&rep); - return Success; -} - -static int ProcDMXGetInputCount(ClientPtr client) -{ - xDMXGetInputCountReply rep; - int n; - - REQUEST_SIZE_MATCH(xDMXGetInputCountReq); - - rep.type = X_Reply; - rep.sequenceNumber = client->sequence; - rep.length = 0; - rep.inputCount = dmxGetInputCount(); - if (client->swapped) { - swaps(&rep.sequenceNumber, n); - swapl(&rep.length, n); - swapl(&rep.inputCount, n); - } - WriteToClient(client, sizeof(xDMXGetInputCountReply), (char *)&rep); - return Success; -} - -static int ProcDMXGetInputAttributes(ClientPtr client) -{ - REQUEST(xDMXGetInputAttributesReq); - xDMXGetInputAttributesReply rep; - int n; - int length; - int paddedLength; - DMXInputAttributesRec attr; - - REQUEST_SIZE_MATCH(xDMXGetInputAttributesReq); - - if (dmxGetInputAttributes(stuff->deviceId, &attr)) return BadValue; - rep.inputType = attr.inputType; - rep.physicalScreen = attr.physicalScreen; - rep.physicalId = attr.physicalId; - rep.isCore = attr.isCore; - rep.sendsCore = attr.sendsCore; - rep.detached = attr.detached; - - length = attr.name ? strlen(attr.name) : 0; - paddedLength = pad_to_int32(length); - rep.type = X_Reply; - rep.sequenceNumber = client->sequence; - rep.length = bytes_to_int32(paddedLength); - rep.nameLength = length; - if (client->swapped) { - swaps(&rep.sequenceNumber, n); - swapl(&rep.length, n); - swapl(&rep.inputType, n); - swapl(&rep.physicalScreen, n); - swapl(&rep.physicalId, n); - swapl(&rep.nameLength, n); - } - WriteToClient(client, sizeof(xDMXGetInputAttributesReply), (char *)&rep); - if (length) WriteToClient(client, length, (char *)attr.name); - return Success; -} - -static int ProcDMXAddInput(ClientPtr client) -{ - REQUEST(xDMXAddInputReq); - xDMXAddInputReply rep; - int n; - int status = 0; - CARD32 *value_list; - DMXInputAttributesRec attr; - int count; - char *name; - int len; - int paddedLength; - int id = -1; - - REQUEST_AT_LEAST_SIZE(xDMXAddInputReq); - paddedLength = pad_to_int32(stuff->displayNameLength); - len = client->req_len - (sizeof(xDMXAddInputReq) >> 2); - if (len != Ones(stuff->valueMask) + paddedLength/4) - return BadLength; - - memset(&attr, 0, sizeof(attr)); - value_list = (CARD32 *)(stuff + 1); - count = dmxFetchInputAttributes(stuff->valueMask, &attr, value_list); - - if (!(name = malloc(stuff->displayNameLength + 1 + 4))) - return BadAlloc; - memcpy(name, &value_list[count], stuff->displayNameLength); - name[stuff->displayNameLength] = '\0'; - attr.name = name; - - status = dmxAddInput(&attr, &id); - - free(name); - - if (status) return status; - - rep.type = X_Reply; - rep.sequenceNumber = client->sequence; - rep.length = 0; - rep.status = status; - rep.physicalId = id; - if (client->swapped) { - swaps(&rep.sequenceNumber, n); - swapl(&rep.length, n); - swapl(&rep.status, n); - swapl(&rep.physicalId, n); - } - WriteToClient(client, sizeof(xDMXAddInputReply), (char *)&rep); - return Success; -} - -static int ProcDMXRemoveInput(ClientPtr client) -{ - REQUEST(xDMXRemoveInputReq); - xDMXRemoveInputReply rep; - int n; - int status = 0; - - REQUEST_SIZE_MATCH(xDMXRemoveInputReq); - - status = dmxRemoveInput(stuff->physicalId); - - if (status) return status; - - rep.type = X_Reply; - rep.sequenceNumber = client->sequence; - rep.length = 0; - rep.status = status; - if (client->swapped) { - swaps(&rep.sequenceNumber, n); - swapl(&rep.length, n); - swapl(&rep.status, n); - } - WriteToClient(client, sizeof(xDMXRemoveInputReply), (char *)&rep); - return Success; -} - -static int ProcDMXDispatch(ClientPtr client) -{ - REQUEST(xReq); - - switch (stuff->data) { - case X_DMXQueryVersion: return ProcDMXQueryVersion(client); - case X_DMXSync: return ProcDMXSync(client); - case X_DMXForceWindowCreation: return ProcDMXForceWindowCreation(client); - case X_DMXGetScreenCount: return ProcDMXGetScreenCount(client); - case X_DMXGetScreenAttributes: return ProcDMXGetScreenAttributes(client); - case X_DMXChangeScreensAttributes: - return ProcDMXChangeScreensAttributes(client); - case X_DMXAddScreen: return ProcDMXAddScreen(client); - case X_DMXRemoveScreen: return ProcDMXRemoveScreen(client); - case X_DMXGetWindowAttributes: return ProcDMXGetWindowAttributes(client); - case X_DMXGetDesktopAttributes: return ProcDMXGetDesktopAttributes(client); - case X_DMXChangeDesktopAttributes: - return ProcDMXChangeDesktopAttributes(client); - case X_DMXGetInputCount: return ProcDMXGetInputCount(client); - case X_DMXGetInputAttributes: return ProcDMXGetInputAttributes(client); - case X_DMXAddInput: return ProcDMXAddInput(client); - case X_DMXRemoveInput: return ProcDMXRemoveInput(client); - - case X_DMXGetScreenInformationDEPRECATED: - case X_DMXForceWindowCreationDEPRECATED: - case X_DMXReconfigureScreenDEPRECATED: - return BadImplementation; - - default: return BadRequest; - } -} - -static int SProcDMXQueryVersion(ClientPtr client) -{ - int n; - REQUEST(xDMXQueryVersionReq); - - swaps(&stuff->length, n); - REQUEST_SIZE_MATCH(xDMXQueryVersionReq); - return ProcDMXQueryVersion(client); -} - -static int SProcDMXSync(ClientPtr client) -{ - int n; - REQUEST(xDMXSyncReq); - - swaps(&stuff->length, n); - REQUEST_SIZE_MATCH(xDMXSyncReq); - return ProcDMXSync(client); -} - -static int SProcDMXForceWindowCreation(ClientPtr client) -{ - int n; - REQUEST(xDMXForceWindowCreationReq); - - swaps(&stuff->length, n); - REQUEST_SIZE_MATCH(xDMXForceWindowCreationReq); - swaps(&stuff->window, n); - return ProcDMXForceWindowCreation(client); -} - -static int SProcDMXGetScreenCount(ClientPtr client) -{ - int n; - REQUEST(xDMXGetScreenCountReq); - - swaps(&stuff->length, n); - REQUEST_SIZE_MATCH(xDMXGetScreenCountReq); - return ProcDMXGetScreenCount(client); -} - -static int SProcDMXGetScreenAttributes(ClientPtr client) -{ - int n; - REQUEST(xDMXGetScreenAttributesReq); - - swaps(&stuff->length, n); - REQUEST_SIZE_MATCH(xDMXGetScreenAttributesReq); - swapl(&stuff->physicalScreen, n); - return ProcDMXGetScreenAttributes(client); -} - -static int SProcDMXChangeScreensAttributes(ClientPtr client) -{ - int n; - REQUEST(xDMXChangeScreensAttributesReq); - - swaps(&stuff->length, n); - REQUEST_AT_LEAST_SIZE(xDMXGetScreenAttributesReq); - swapl(&stuff->screenCount, n); - swapl(&stuff->maskCount, n); - SwapRestL(stuff); - return ProcDMXGetScreenAttributes(client); -} - -static int SProcDMXAddScreen(ClientPtr client) -{ - int n; - int paddedLength; - REQUEST(xDMXAddScreenReq); - - swaps(&stuff->length, n); - REQUEST_AT_LEAST_SIZE(xDMXAddScreenReq); - swapl(&stuff->displayNameLength, n); - swapl(&stuff->valueMask, n); - paddedLength = pad_to_int32(stuff->displayNameLength); - SwapLongs((CARD32 *)(stuff+1), LengthRestL(stuff) - paddedLength/4); - return ProcDMXAddScreen(client); -} - -static int SProcDMXRemoveScreen(ClientPtr client) -{ - int n; - REQUEST(xDMXRemoveScreenReq); - - swaps(&stuff->length, n); - REQUEST_SIZE_MATCH(xDMXRemoveScreenReq); - swapl(&stuff->physicalScreen, n); - return ProcDMXRemoveScreen(client); -} - -static int SProcDMXGetWindowAttributes(ClientPtr client) -{ - int n; - REQUEST(xDMXGetWindowAttributesReq); - - swaps(&stuff->length, n); - REQUEST_SIZE_MATCH(xDMXGetWindowAttributesReq); - swapl(&stuff->window, n); - return ProcDMXGetWindowAttributes(client); -} - -static int SProcDMXGetDesktopAttributes(ClientPtr client) -{ - int n; - REQUEST(xDMXGetDesktopAttributesReq); - - swaps(&stuff->length, n); - REQUEST_SIZE_MATCH(xDMXGetDesktopAttributesReq); - return ProcDMXGetDesktopAttributes(client); -} - -static int SProcDMXChangeDesktopAttributes(ClientPtr client) -{ - int n; - REQUEST(xDMXChangeDesktopAttributesReq); - - swaps(&stuff->length, n); - REQUEST_AT_LEAST_SIZE(xDMXChangeDesktopAttributesReq); - swapl(&stuff->valueMask, n); - SwapRestL(stuff); - return ProcDMXChangeDesktopAttributes(client); -} - -static int SProcDMXGetInputCount(ClientPtr client) -{ - int n; - REQUEST(xDMXGetInputCountReq); - - swaps(&stuff->length, n); - REQUEST_SIZE_MATCH(xDMXGetInputCountReq); - return ProcDMXGetInputCount(client); -} - -static int SProcDMXGetInputAttributes(ClientPtr client) -{ - int n; - REQUEST(xDMXGetInputAttributesReq); - - swaps(&stuff->length, n); - REQUEST_SIZE_MATCH(xDMXGetInputAttributesReq); - swapl(&stuff->deviceId, n); - return ProcDMXGetInputAttributes(client); -} - -static int SProcDMXAddInput(ClientPtr client) -{ - int n; - int paddedLength; - REQUEST(xDMXAddInputReq); - - swaps(&stuff->length, n); - REQUEST_AT_LEAST_SIZE(xDMXAddInputReq); - swapl(&stuff->displayNameLength, n); - swapl(&stuff->valueMask, n); - paddedLength = pad_to_int32(stuff->displayNameLength); - SwapLongs((CARD32 *)(stuff+1), LengthRestL(stuff) - paddedLength/4); - return ProcDMXAddInput(client); -} - -static int SProcDMXRemoveInput(ClientPtr client) -{ - int n; - REQUEST(xDMXRemoveInputReq); - - swaps(&stuff->length, n); - REQUEST_SIZE_MATCH(xDMXRemoveInputReq); - swapl(&stuff->physicalId, n); - return ProcDMXRemoveInput(client); -} - -static int SProcDMXDispatch (ClientPtr client) -{ - REQUEST(xReq); - - switch (stuff->data) { - case X_DMXQueryVersion: return SProcDMXQueryVersion(client); - case X_DMXSync: return SProcDMXSync(client); - case X_DMXForceWindowCreation: return SProcDMXForceWindowCreation(client); - case X_DMXGetScreenCount: return SProcDMXGetScreenCount(client); - case X_DMXGetScreenAttributes: return SProcDMXGetScreenAttributes(client); - case X_DMXChangeScreensAttributes: - return SProcDMXChangeScreensAttributes(client); - case X_DMXAddScreen: return SProcDMXAddScreen(client); - case X_DMXRemoveScreen: return SProcDMXRemoveScreen(client); - case X_DMXGetWindowAttributes: return SProcDMXGetWindowAttributes(client); - case X_DMXGetDesktopAttributes: - return SProcDMXGetDesktopAttributes(client); - case X_DMXChangeDesktopAttributes: - return SProcDMXChangeDesktopAttributes(client); - case X_DMXGetInputCount: return SProcDMXGetInputCount(client); - case X_DMXGetInputAttributes: return SProcDMXGetInputAttributes(client); - case X_DMXAddInput: return SProcDMXAddInput(client); - case X_DMXRemoveInput: return SProcDMXRemoveInput(client); - - case X_DMXGetScreenInformationDEPRECATED: - case X_DMXForceWindowCreationDEPRECATED: - case X_DMXReconfigureScreenDEPRECATED: - return BadImplementation; - - default: return BadRequest; - } -} - -/** Initialize the extension. */ -void DMXExtensionInit(void) -{ - ExtensionEntry *extEntry; - - if ((extEntry = AddExtension(DMX_EXTENSION_NAME, 0, 0, - ProcDMXDispatch, SProcDMXDispatch, - NULL, StandardMinorOpcode))) - DMXCode = extEntry->base; -} +/* + * Copyright 2002-2004 Red Hat Inc., Durham, North Carolina. + * + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation on the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NON-INFRINGEMENT. IN NO EVENT SHALL RED HAT AND/OR THEIR SUPPLIERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* + * Authors: + * Rickard E. (Rik) Faith + * + */ + +/** \file + * This file implements the server-side part of the DMX protocol. A + * vector of fucntions is provided at extension initialization time, so + * most all of the useful functions in this file are declared static and + * do not appear in the doxygen documentation. + * + * Much of the low-level work is done by functions in \a dmxextension.c + * + * Please see the Client-to-Server DMX Extension to the X Protocol + * document for details about the protocol. */ + +#ifdef HAVE_DMX_CONFIG_H +#include +#endif + +#include +#include +#include "misc.h" +#include "os.h" +#include "dixstruct.h" +#include "extnsionst.h" +#include "opaque.h" + +#include "dmxextension.h" +#include +#include +#include "protocol-versions.h" + +#ifdef PANORAMIX +#include "panoramiX.h" +extern unsigned long XRT_WINDOW; +extern int PanoramiXNumScreens; +#endif + +extern void DMXExtensionInit(void); + +static unsigned char DMXCode; + + + +static int _DMXXineramaActive(void) +{ +#ifdef PANORAMIX + return !noPanoramiXExtension; +#endif + return 0; +} + +static void dmxSetScreenAttribute(int bit, DMXScreenAttributesPtr attr, + CARD32 value) +{ + switch (1 << bit) { + case DMXScreenWindowWidth: attr->screenWindowWidth = value; break; + case DMXScreenWindowHeight: attr->screenWindowHeight = value; break; + case DMXScreenWindowXoffset: attr->screenWindowXoffset = value; break; + case DMXScreenWindowYoffset: attr->screenWindowYoffset = value; break; + case DMXRootWindowWidth: attr->rootWindowWidth = value; break; + case DMXRootWindowHeight: attr->rootWindowHeight = value; break; + case DMXRootWindowXoffset: attr->rootWindowXoffset = value; break; + case DMXRootWindowYoffset: attr->rootWindowYoffset = value; break; + case DMXRootWindowXorigin: attr->rootWindowXorigin = value; break; + case DMXRootWindowYorigin: attr->rootWindowYorigin = value; break; + } +} + +static int dmxFetchScreenAttributes(unsigned int mask, + DMXScreenAttributesPtr attr, + CARD32 *value_list) +{ + int i; + CARD32 *value = value_list; + int count = 0; + + for (i = 0; i < 32; i++) { + if (mask & (1 << i)) { + dmxSetScreenAttribute(i, attr, *value); + ++value; + ++count; + } + } + return count; +} + +static void dmxSetDesktopAttribute(int bit, DMXDesktopAttributesPtr attr, + CARD32 value) +{ + switch (1 << bit) { + case DMXDesktopWidth: attr->width = value; break; + case DMXDesktopHeight: attr->height = value; break; + case DMXDesktopShiftX: attr->shiftX = value; break; + case DMXDesktopShiftY: attr->shiftY = value; break; + } +} + +static int dmxFetchDesktopAttributes(unsigned int mask, + DMXDesktopAttributesPtr attr, + CARD32 *value_list) +{ + int i; + CARD32 *value = value_list; + int count = 0; + + for (i = 0; i < 32; i++) { + if (mask & (1 << i)) { + dmxSetDesktopAttribute(i, attr, *value); + ++value; + ++count; + } + } + return count; +} + +static void dmxSetInputAttribute(int bit, DMXInputAttributesPtr attr, + CARD32 value) +{ + switch (1 << bit) { + case DMXInputType: attr->inputType = value; break; + case DMXInputPhysicalScreen: attr->physicalScreen = value; break; + case DMXInputSendsCore: attr->sendsCore = !!value; break; + } +} + +static int dmxFetchInputAttributes(unsigned int mask, + DMXInputAttributesPtr attr, + CARD32 *value_list) +{ + int i; + CARD32 *value = value_list; + int count = 0; + + for (i = 0; i < 32; i++) { + if (mask & (1 << i)) { + dmxSetInputAttribute(i, attr, *value); + ++value; + ++count; + } + } + return count; +} + +static int ProcDMXQueryVersion(ClientPtr client) +{ + xDMXQueryVersionReply rep; + + REQUEST_SIZE_MATCH(xDMXQueryVersionReq); + + rep.type = X_Reply; + rep.sequenceNumber = client->sequence; + rep.length = 0; + rep.majorVersion = SERVER_DMX_MAJOR_VERSION; + rep.minorVersion = SERVER_DMX_MINOR_VERSION; + rep.patchVersion = SERVER_DMX_PATCH_VERSION; + if (client->swapped) { + swaps(&rep.sequenceNumber); + swapl(&rep.length); + swapl(&rep.majorVersion); + swapl(&rep.minorVersion); + swapl(&rep.patchVersion); + } + WriteToClient(client, sizeof(xDMXQueryVersionReply), (char *)&rep); + return Success; +} + +static int ProcDMXSync(ClientPtr client) +{ + xDMXSyncReply rep; + + REQUEST_SIZE_MATCH(xDMXSyncReq); + + dmxFlushPendingSyncs(); + + rep.type = X_Reply; + rep.sequenceNumber = client->sequence; + rep.length = 0; + rep.status = 0; + if (client->swapped) { + swaps(&rep.sequenceNumber); + swapl(&rep.length); + swapl(&rep.status); + } + WriteToClient(client, sizeof(xDMXSyncReply), (char *)&rep); + return Success; +} + +static int ProcDMXForceWindowCreation(ClientPtr client) +{ + xDMXForceWindowCreationReply rep; + REQUEST(xDMXForceWindowCreationReq); + WindowPtr pWin; + + REQUEST_SIZE_MATCH(xDMXForceWindowCreationReq); + +#ifdef PANORAMIX + if (!noPanoramiXExtension) { + PanoramiXRes *win; + int i; + + if (Success != dixLookupResourceByType((pointer*) &win, + stuff->window, XRT_WINDOW, + client, DixReadAccess)) + return -1; /* BadWindow */ + + FOR_NSCREENS(i) { + if (Success != dixLookupWindow(&pWin, win->info[i].id, client, + DixReadAccess)) + return -1; /* BadWindow */ + + dmxForceWindowCreation(pWin); + } + goto doreply; + } +#endif + + if (Success != dixLookupWindow(&pWin, stuff->window, client, + DixReadAccess)) + return -1; /* BadWindow */ + + dmxForceWindowCreation(pWin); + doreply: + dmxFlushPendingSyncs(); + rep.type = X_Reply; + rep.sequenceNumber = client->sequence; + rep.length = 0; + rep.status = 0; + if (client->swapped) { + swaps(&rep.sequenceNumber); + swapl(&rep.length); + swapl(&rep.status); + } + WriteToClient(client, sizeof(xDMXForceWindowCreationReply), (char *)&rep); + return Success; +} + +static int ProcDMXGetScreenCount(ClientPtr client) +{ + xDMXGetScreenCountReply rep; + + REQUEST_SIZE_MATCH(xDMXGetScreenCountReq); + + rep.type = X_Reply; + rep.sequenceNumber = client->sequence; + rep.length = 0; + rep.screenCount = dmxGetNumScreens(); + if (client->swapped) { + swaps(&rep.sequenceNumber); + swapl(&rep.length); + swapl(&rep.screenCount); + } + WriteToClient(client, sizeof(xDMXGetScreenCountReply), (char *)&rep); + return Success; +} + +static int ProcDMXGetScreenAttributes(ClientPtr client) +{ + REQUEST(xDMXGetScreenAttributesReq); + xDMXGetScreenAttributesReply rep; + int length; + int paddedLength; + DMXScreenAttributesRec attr; + + REQUEST_SIZE_MATCH(xDMXGetScreenAttributesReq); + + if (stuff->physicalScreen < 0 + || stuff->physicalScreen >= dmxGetNumScreens()) return BadValue; + + if (!dmxGetScreenAttributes(stuff->physicalScreen, &attr)) + return BadValue; + + rep.logicalScreen = attr.logicalScreen; + rep.screenWindowWidth = attr.screenWindowWidth; + rep.screenWindowHeight = attr.screenWindowHeight; + rep.screenWindowXoffset = attr.screenWindowXoffset; + rep.screenWindowYoffset = attr.screenWindowYoffset; + rep.rootWindowWidth = attr.rootWindowWidth; + rep.rootWindowHeight = attr.rootWindowHeight; + rep.rootWindowXoffset = attr.rootWindowXoffset; + rep.rootWindowYoffset = attr.rootWindowYoffset; + rep.rootWindowXorigin = attr.rootWindowXorigin; + rep.rootWindowYorigin = attr.rootWindowYorigin; + + length = attr.displayName ? strlen(attr.displayName) : 0; + paddedLength = pad_to_int32(length); + rep.type = X_Reply; + rep.sequenceNumber = client->sequence; + rep.length = bytes_to_int32((sizeof(xDMXGetScreenAttributesReply) - sizeof(xGenericReply)) + + paddedLength); + rep.displayNameLength = length; + + if (client->swapped) { + swaps(&rep.sequenceNumber); + swapl(&rep.length); + swapl(&rep.displayNameLength); + swapl(&rep.logicalScreen); + swaps(&rep.screenWindowWidth); + swaps(&rep.screenWindowHeight); + swaps(&rep.screenWindowXoffset); + swaps(&rep.screenWindowYoffset); + swaps(&rep.rootWindowWidth); + swaps(&rep.rootWindowHeight); + swaps(&rep.rootWindowXoffset); + swaps(&rep.rootWindowYoffset); + swaps(&rep.rootWindowXorigin); + swaps(&rep.rootWindowYorigin); + } + WriteToClient(client, sizeof(xDMXGetScreenAttributesReply), (char *)&rep); + if (length) WriteToClient(client, length, (char *)attr.displayName); + return Success; +} + +static int ProcDMXChangeScreensAttributes(ClientPtr client) +{ + REQUEST(xDMXChangeScreensAttributesReq); + xDMXChangeScreensAttributesReply rep; + int status = DMX_BAD_XINERAMA; + unsigned int mask = 0; + unsigned int i; + CARD32 *screen_list; + CARD32 *mask_list; + CARD32 *value_list; + DMXScreenAttributesPtr attribs; + int errorScreen = 0; + unsigned int len; + int ones = 0; + + + REQUEST_AT_LEAST_SIZE(xDMXChangeScreensAttributesReq); + len = client->req_len - bytes_to_int32(sizeof(xDMXChangeScreensAttributesReq)); + if (len < stuff->screenCount + stuff->maskCount) + return BadLength; + + screen_list = (CARD32 *)(stuff + 1); + mask_list = &screen_list[stuff->screenCount]; + value_list = &mask_list[stuff->maskCount]; + + for (i = 0; i < stuff->maskCount; i++) ones += Ones(mask_list[i]); + if (len != stuff->screenCount + stuff->maskCount + ones) + return BadLength; + + if (!_DMXXineramaActive()) goto noxinerama; + + if (!(attribs = malloc(stuff->screenCount * sizeof(*attribs)))) + return BadAlloc; + + for (i = 0; i < stuff->screenCount; i++) { + int count; + + if (i < stuff->maskCount) mask = mask_list[i]; + dmxGetScreenAttributes(screen_list[i], &attribs[i]); + count = dmxFetchScreenAttributes(mask, &attribs[i], value_list); + value_list += count; + } + +#if PANORAMIX + status = dmxConfigureScreenWindows(stuff->screenCount, + screen_list, + attribs, + &errorScreen); +#endif + + free(attribs); + + if (status == BadValue) return status; + + noxinerama: + rep.type = X_Reply; + rep.sequenceNumber = client->sequence; + rep.length = 0; + rep.status = status; + rep.errorScreen = errorScreen; + if (client->swapped) { + swaps(&rep.sequenceNumber); + swapl(&rep.length); + swapl(&rep.status); + swapl(&rep.errorScreen); + } + WriteToClient(client, + sizeof(xDMXChangeScreensAttributesReply), + (char *)&rep); + return Success; +} + +static int ProcDMXAddScreen(ClientPtr client) +{ + REQUEST(xDMXAddScreenReq); + xDMXAddScreenReply rep; + int status = 0; + CARD32 *value_list; + DMXScreenAttributesRec attr; + int count; + char *name; + int len; + int paddedLength; + + REQUEST_AT_LEAST_SIZE(xDMXAddScreenReq); + paddedLength = pad_to_int32(stuff->displayNameLength); + len = client->req_len - bytes_to_int32(sizeof(xDMXAddScreenReq)); + if (len != Ones(stuff->valueMask) + paddedLength/4) + return BadLength; + + memset(&attr, 0, sizeof(attr)); + dmxGetScreenAttributes(stuff->physicalScreen, &attr); + value_list = (CARD32 *)(stuff + 1); + count = dmxFetchScreenAttributes(stuff->valueMask, &attr, value_list); + + if (!(name = malloc(stuff->displayNameLength + 1 + 4))) + return BadAlloc; + memcpy(name, &value_list[count], stuff->displayNameLength); + name[stuff->displayNameLength] = '\0'; + attr.displayName = name; + + status = dmxAttachScreen(stuff->physicalScreen, &attr); + + free(name); + + rep.type = X_Reply; + rep.sequenceNumber = client->sequence; + rep.length = 0; + rep.status = status; + rep.physicalScreen = stuff->physicalScreen; + if (client->swapped) { + swaps(&rep.sequenceNumber); + swapl(&rep.length); + swapl(&rep.status); + swapl(&rep.physicalScreen); + } + WriteToClient(client, + sizeof(xDMXAddScreenReply), + (char *)&rep); + return Success; +} + +static int ProcDMXRemoveScreen(ClientPtr client) +{ + REQUEST(xDMXRemoveScreenReq); + xDMXRemoveScreenReply rep; + int status = 0; + + REQUEST_SIZE_MATCH(xDMXRemoveScreenReq); + + status = dmxDetachScreen(stuff->physicalScreen); + + rep.type = X_Reply; + rep.sequenceNumber = client->sequence; + rep.length = 0; + rep.status = status; + if (client->swapped) { + swaps(&rep.sequenceNumber); + swapl(&rep.length); + swapl(&rep.status); + } + WriteToClient(client, + sizeof(xDMXRemoveScreenReply), + (char *)&rep); + return Success; +} + + +#ifdef PANORAMIX +static int dmxPopulatePanoramiX(ClientPtr client, Window window, + CARD32 *screens, CARD32 *windows, + xRectangle *pos, xRectangle *vis) +{ + WindowPtr pWin; + PanoramiXRes *win; + int i; + int count = 0; + DMXWindowAttributesRec attr; + + if (Success != dixLookupResourceByType((pointer*) &win, + window, XRT_WINDOW, + client, DixReadAccess)) + return -1; /* BadWindow */ + + FOR_NSCREENS(i) { + if (Success != dixLookupWindow(&pWin, win->info[i].id, client, + DixReadAccess)) + return -1; /* BadWindow */ + if (dmxGetWindowAttributes(pWin, &attr)) { + screens[count] = attr.screen; + windows[count] = attr.window; + pos[count] = attr.pos; + vis[count] = attr.vis; + ++count; /* Only count existing windows */ + } + } + return count; +} +#endif + +static int dmxPopulate(ClientPtr client, Window window, CARD32 *screens, + CARD32 *windows, xRectangle *pos, xRectangle *vis) +{ + WindowPtr pWin; + DMXWindowAttributesRec attr; + +#ifdef PANORAMIX + if (!noPanoramiXExtension) + return dmxPopulatePanoramiX(client, window, screens, windows, + pos, vis); +#endif + + if (Success != dixLookupWindow(&pWin, window, client, DixReadAccess)) + return -1; /* BadWindow */ + + dmxGetWindowAttributes(pWin, &attr); + *screens = attr.screen; + *windows = attr.window; + *pos = attr.pos; + *vis = attr.vis; + return 1; +} + +static int dmxMaxNumScreens(void) +{ +#ifdef PANORAMIX + if (!noPanoramiXExtension) return PanoramiXNumScreens; +#endif + return 1; +} + +static int ProcDMXGetWindowAttributes(ClientPtr client) +{ + REQUEST(xDMXGetWindowAttributesReq); + xDMXGetWindowAttributesReply rep; + int i; + CARD32 *screens; + CARD32 *windows; + xRectangle *pos, *vis; + int count = dmxMaxNumScreens(); + + REQUEST_SIZE_MATCH(xDMXGetWindowAttributesReq); + + if (!(screens = malloc(count * sizeof(*screens)))) + return BadAlloc; + if (!(windows = malloc(count * sizeof(*windows)))) { + free(screens); + return BadAlloc; + } + if (!(pos = malloc(count * sizeof(*pos)))) { + free(windows); + free(screens); + return BadAlloc; + } + if (!(vis = malloc(count * sizeof(*vis)))) { + free(pos); + free(windows); + free(screens); + return BadAlloc; + } + + if ((count = dmxPopulate(client, stuff->window, screens, windows, + pos, vis)) < 0) { + free(vis); + free(pos); + free(windows); + free(screens); + return BadWindow; + } + + rep.type = X_Reply; + rep.sequenceNumber = client->sequence; + rep.length = count * 6; + rep.screenCount = count; + if (client->swapped) { + swaps(&rep.sequenceNumber); + swapl(&rep.length); + swapl(&rep.screenCount); + for (i = 0; i < count; i++) { + swapl(&screens[i]); + swapl(&windows[i]); + + swaps(&pos[i].x); + swaps(&pos[i].y); + swaps(&pos[i].width); + swaps(&pos[i].height); + + swaps(&vis[i].x); + swaps(&vis[i].y); + swaps(&vis[i].width); + swaps(&vis[i].height); + } + } + + dmxFlushPendingSyncs(); + + WriteToClient(client, sizeof(xDMXGetWindowAttributesReply), (char *)&rep); + if (count) { + WriteToClient(client, count * sizeof(*screens), (char *)screens); + WriteToClient(client, count * sizeof(*windows), (char *)windows); + WriteToClient(client, count * sizeof(*pos), (char *)pos); + WriteToClient(client, count * sizeof(*vis), (char *)vis); + } + + free(vis); + free(pos); + free(windows); + free(screens); + + return Success; +} + +static int ProcDMXGetDesktopAttributes(ClientPtr client) +{ + xDMXGetDesktopAttributesReply rep; + DMXDesktopAttributesRec attr; + + REQUEST_SIZE_MATCH(xDMXGetDesktopAttributesReq); + + dmxGetDesktopAttributes(&attr); + + rep.width = attr.width; + rep.height = attr.height; + rep.shiftX = attr.shiftX; + rep.shiftY = attr.shiftY; + + rep.type = X_Reply; + rep.sequenceNumber = client->sequence; + rep.length = 0; + + if (client->swapped) { + swaps(&rep.sequenceNumber); + swapl(&rep.length); + swaps(&rep.width); + swaps(&rep.height); + swaps(&rep.shiftX); + swaps(&rep.shiftY); + } + WriteToClient(client, sizeof(xDMXGetDesktopAttributesReply), (char *)&rep); + return Success; +} + +static int ProcDMXChangeDesktopAttributes(ClientPtr client) +{ + REQUEST(xDMXChangeDesktopAttributesReq); + xDMXChangeDesktopAttributesReply rep; + int status = DMX_BAD_XINERAMA; + CARD32 *value_list; + DMXDesktopAttributesRec attr; + int len; + + REQUEST_AT_LEAST_SIZE(xDMXChangeDesktopAttributesReq); + len = client->req_len - (sizeof(xDMXChangeDesktopAttributesReq) >> 2); + if (len != Ones(stuff->valueMask)) + return BadLength; + + if (!_DMXXineramaActive()) goto noxinerama; + + value_list = (CARD32 *)(stuff + 1); + + dmxGetDesktopAttributes(&attr); + dmxFetchDesktopAttributes(stuff->valueMask, &attr, value_list); + +#if PANORAMIX + status = dmxConfigureDesktop(&attr); +#endif + if (status == BadValue) return status; + + noxinerama: + rep.type = X_Reply; + rep.sequenceNumber = client->sequence; + rep.length = 0; + rep.status = status; + if (client->swapped) { + swaps(&rep.sequenceNumber); + swapl(&rep.length); + swapl(&rep.status); + } + WriteToClient(client, + sizeof(xDMXChangeDesktopAttributesReply), + (char *)&rep); + return Success; +} + +static int ProcDMXGetInputCount(ClientPtr client) +{ + xDMXGetInputCountReply rep; + + REQUEST_SIZE_MATCH(xDMXGetInputCountReq); + + rep.type = X_Reply; + rep.sequenceNumber = client->sequence; + rep.length = 0; + rep.inputCount = dmxGetInputCount(); + if (client->swapped) { + swaps(&rep.sequenceNumber); + swapl(&rep.length); + swapl(&rep.inputCount); + } + WriteToClient(client, sizeof(xDMXGetInputCountReply), (char *)&rep); + return Success; +} + +static int ProcDMXGetInputAttributes(ClientPtr client) +{ + REQUEST(xDMXGetInputAttributesReq); + xDMXGetInputAttributesReply rep; + int length; + int paddedLength; + DMXInputAttributesRec attr; + + REQUEST_SIZE_MATCH(xDMXGetInputAttributesReq); + + if (dmxGetInputAttributes(stuff->deviceId, &attr)) return BadValue; + rep.inputType = attr.inputType; + rep.physicalScreen = attr.physicalScreen; + rep.physicalId = attr.physicalId; + rep.isCore = attr.isCore; + rep.sendsCore = attr.sendsCore; + rep.detached = attr.detached; + + length = attr.name ? strlen(attr.name) : 0; + paddedLength = pad_to_int32(length); + rep.type = X_Reply; + rep.sequenceNumber = client->sequence; + rep.length = bytes_to_int32(paddedLength); + rep.nameLength = length; + if (client->swapped) { + swaps(&rep.sequenceNumber); + swapl(&rep.length); + swapl(&rep.inputType); + swapl(&rep.physicalScreen); + swapl(&rep.physicalId); + swapl(&rep.nameLength); + } + WriteToClient(client, sizeof(xDMXGetInputAttributesReply), (char *)&rep); + if (length) WriteToClient(client, length, (char *)attr.name); + return Success; +} + +static int ProcDMXAddInput(ClientPtr client) +{ + REQUEST(xDMXAddInputReq); + xDMXAddInputReply rep; + int status = 0; + CARD32 *value_list; + DMXInputAttributesRec attr; + int count; + char *name; + int len; + int paddedLength; + int id = -1; + + REQUEST_AT_LEAST_SIZE(xDMXAddInputReq); + paddedLength = pad_to_int32(stuff->displayNameLength); + len = client->req_len - (sizeof(xDMXAddInputReq) >> 2); + if (len != Ones(stuff->valueMask) + paddedLength/4) + return BadLength; + + memset(&attr, 0, sizeof(attr)); + value_list = (CARD32 *)(stuff + 1); + count = dmxFetchInputAttributes(stuff->valueMask, &attr, value_list); + + if (!(name = malloc(stuff->displayNameLength + 1 + 4))) + return BadAlloc; + memcpy(name, &value_list[count], stuff->displayNameLength); + name[stuff->displayNameLength] = '\0'; + attr.name = name; + + status = dmxAddInput(&attr, &id); + + free(name); + + if (status) return status; + + rep.type = X_Reply; + rep.sequenceNumber = client->sequence; + rep.length = 0; + rep.status = status; + rep.physicalId = id; + if (client->swapped) { + swaps(&rep.sequenceNumber); + swapl(&rep.length); + swapl(&rep.status); + swapl(&rep.physicalId); + } + WriteToClient(client, sizeof(xDMXAddInputReply), (char *)&rep); + return Success; +} + +static int ProcDMXRemoveInput(ClientPtr client) +{ + REQUEST(xDMXRemoveInputReq); + xDMXRemoveInputReply rep; + int status = 0; + + REQUEST_SIZE_MATCH(xDMXRemoveInputReq); + + status = dmxRemoveInput(stuff->physicalId); + + if (status) return status; + + rep.type = X_Reply; + rep.sequenceNumber = client->sequence; + rep.length = 0; + rep.status = status; + if (client->swapped) { + swaps(&rep.sequenceNumber); + swapl(&rep.length); + swapl(&rep.status); + } + WriteToClient(client, sizeof(xDMXRemoveInputReply), (char *)&rep); + return Success; +} + +static int ProcDMXDispatch(ClientPtr client) +{ + REQUEST(xReq); + + switch (stuff->data) { + case X_DMXQueryVersion: return ProcDMXQueryVersion(client); + case X_DMXSync: return ProcDMXSync(client); + case X_DMXForceWindowCreation: return ProcDMXForceWindowCreation(client); + case X_DMXGetScreenCount: return ProcDMXGetScreenCount(client); + case X_DMXGetScreenAttributes: return ProcDMXGetScreenAttributes(client); + case X_DMXChangeScreensAttributes: + return ProcDMXChangeScreensAttributes(client); + case X_DMXAddScreen: return ProcDMXAddScreen(client); + case X_DMXRemoveScreen: return ProcDMXRemoveScreen(client); + case X_DMXGetWindowAttributes: return ProcDMXGetWindowAttributes(client); + case X_DMXGetDesktopAttributes: return ProcDMXGetDesktopAttributes(client); + case X_DMXChangeDesktopAttributes: + return ProcDMXChangeDesktopAttributes(client); + case X_DMXGetInputCount: return ProcDMXGetInputCount(client); + case X_DMXGetInputAttributes: return ProcDMXGetInputAttributes(client); + case X_DMXAddInput: return ProcDMXAddInput(client); + case X_DMXRemoveInput: return ProcDMXRemoveInput(client); + + case X_DMXGetScreenInformationDEPRECATED: + case X_DMXForceWindowCreationDEPRECATED: + case X_DMXReconfigureScreenDEPRECATED: + return BadImplementation; + + default: return BadRequest; + } +} + +static int SProcDMXQueryVersion(ClientPtr client) +{ + REQUEST(xDMXQueryVersionReq); + + swaps(&stuff->length); + REQUEST_SIZE_MATCH(xDMXQueryVersionReq); + return ProcDMXQueryVersion(client); +} + +static int SProcDMXSync(ClientPtr client) +{ + REQUEST(xDMXSyncReq); + + swaps(&stuff->length); + REQUEST_SIZE_MATCH(xDMXSyncReq); + return ProcDMXSync(client); +} + +static int SProcDMXForceWindowCreation(ClientPtr client) +{ + REQUEST(xDMXForceWindowCreationReq); + + swaps(&stuff->length); + REQUEST_SIZE_MATCH(xDMXForceWindowCreationReq); + swapl(&stuff->window); + return ProcDMXForceWindowCreation(client); +} + +static int SProcDMXGetScreenCount(ClientPtr client) +{ + REQUEST(xDMXGetScreenCountReq); + + swaps(&stuff->length); + REQUEST_SIZE_MATCH(xDMXGetScreenCountReq); + return ProcDMXGetScreenCount(client); +} + +static int SProcDMXGetScreenAttributes(ClientPtr client) +{ + REQUEST(xDMXGetScreenAttributesReq); + + swaps(&stuff->length); + REQUEST_SIZE_MATCH(xDMXGetScreenAttributesReq); + swapl(&stuff->physicalScreen); + return ProcDMXGetScreenAttributes(client); +} + +static int SProcDMXChangeScreensAttributes(ClientPtr client) +{ + REQUEST(xDMXChangeScreensAttributesReq); + + swaps(&stuff->length); + REQUEST_AT_LEAST_SIZE(xDMXGetScreenAttributesReq); + swapl(&stuff->screenCount); + swapl(&stuff->maskCount); + SwapRestL(stuff); + return ProcDMXGetScreenAttributes(client); +} + +static int SProcDMXAddScreen(ClientPtr client) +{ + int paddedLength; + REQUEST(xDMXAddScreenReq); + + swaps(&stuff->length); + REQUEST_AT_LEAST_SIZE(xDMXAddScreenReq); + swapl(&stuff->displayNameLength); + swapl(&stuff->valueMask); + paddedLength = pad_to_int32(stuff->displayNameLength); + SwapLongs((CARD32 *)(stuff+1), LengthRestL(stuff) - paddedLength/4); + return ProcDMXAddScreen(client); +} + +static int SProcDMXRemoveScreen(ClientPtr client) +{ + REQUEST(xDMXRemoveScreenReq); + + swaps(&stuff->length); + REQUEST_SIZE_MATCH(xDMXRemoveScreenReq); + swapl(&stuff->physicalScreen); + return ProcDMXRemoveScreen(client); +} + +static int SProcDMXGetWindowAttributes(ClientPtr client) +{ + REQUEST(xDMXGetWindowAttributesReq); + + swaps(&stuff->length); + REQUEST_SIZE_MATCH(xDMXGetWindowAttributesReq); + swapl(&stuff->window); + return ProcDMXGetWindowAttributes(client); +} + +static int SProcDMXGetDesktopAttributes(ClientPtr client) +{ + REQUEST(xDMXGetDesktopAttributesReq); + + swaps(&stuff->length); + REQUEST_SIZE_MATCH(xDMXGetDesktopAttributesReq); + return ProcDMXGetDesktopAttributes(client); +} + +static int SProcDMXChangeDesktopAttributes(ClientPtr client) +{ + REQUEST(xDMXChangeDesktopAttributesReq); + + swaps(&stuff->length); + REQUEST_AT_LEAST_SIZE(xDMXChangeDesktopAttributesReq); + swapl(&stuff->valueMask); + SwapRestL(stuff); + return ProcDMXChangeDesktopAttributes(client); +} + +static int SProcDMXGetInputCount(ClientPtr client) +{ + REQUEST(xDMXGetInputCountReq); + + swaps(&stuff->length); + REQUEST_SIZE_MATCH(xDMXGetInputCountReq); + return ProcDMXGetInputCount(client); +} + +static int SProcDMXGetInputAttributes(ClientPtr client) +{ + REQUEST(xDMXGetInputAttributesReq); + + swaps(&stuff->length); + REQUEST_SIZE_MATCH(xDMXGetInputAttributesReq); + swapl(&stuff->deviceId); + return ProcDMXGetInputAttributes(client); +} + +static int SProcDMXAddInput(ClientPtr client) +{ + int paddedLength; + REQUEST(xDMXAddInputReq); + + swaps(&stuff->length); + REQUEST_AT_LEAST_SIZE(xDMXAddInputReq); + swapl(&stuff->displayNameLength); + swapl(&stuff->valueMask); + paddedLength = pad_to_int32(stuff->displayNameLength); + SwapLongs((CARD32 *)(stuff+1), LengthRestL(stuff) - paddedLength/4); + return ProcDMXAddInput(client); +} + +static int SProcDMXRemoveInput(ClientPtr client) +{ + REQUEST(xDMXRemoveInputReq); + + swaps(&stuff->length); + REQUEST_SIZE_MATCH(xDMXRemoveInputReq); + swapl(&stuff->physicalId); + return ProcDMXRemoveInput(client); +} + +static int SProcDMXDispatch (ClientPtr client) +{ + REQUEST(xReq); + + switch (stuff->data) { + case X_DMXQueryVersion: return SProcDMXQueryVersion(client); + case X_DMXSync: return SProcDMXSync(client); + case X_DMXForceWindowCreation: return SProcDMXForceWindowCreation(client); + case X_DMXGetScreenCount: return SProcDMXGetScreenCount(client); + case X_DMXGetScreenAttributes: return SProcDMXGetScreenAttributes(client); + case X_DMXChangeScreensAttributes: + return SProcDMXChangeScreensAttributes(client); + case X_DMXAddScreen: return SProcDMXAddScreen(client); + case X_DMXRemoveScreen: return SProcDMXRemoveScreen(client); + case X_DMXGetWindowAttributes: return SProcDMXGetWindowAttributes(client); + case X_DMXGetDesktopAttributes: + return SProcDMXGetDesktopAttributes(client); + case X_DMXChangeDesktopAttributes: + return SProcDMXChangeDesktopAttributes(client); + case X_DMXGetInputCount: return SProcDMXGetInputCount(client); + case X_DMXGetInputAttributes: return SProcDMXGetInputAttributes(client); + case X_DMXAddInput: return SProcDMXAddInput(client); + case X_DMXRemoveInput: return SProcDMXRemoveInput(client); + + case X_DMXGetScreenInformationDEPRECATED: + case X_DMXForceWindowCreationDEPRECATED: + case X_DMXReconfigureScreenDEPRECATED: + return BadImplementation; + + default: return BadRequest; + } +} + +/** Initialize the extension. */ +void DMXExtensionInit(void) +{ + ExtensionEntry *extEntry; + + if ((extEntry = AddExtension(DMX_EXTENSION_NAME, 0, 0, + ProcDMXDispatch, SProcDMXDispatch, + NULL, StandardMinorOpcode))) + DMXCode = extEntry->base; +} diff --git a/xorg-server/hw/dmx/dmxclient.h b/xorg-server/hw/dmx/dmxclient.h index c45f71fc0..f0f235f61 100644 --- a/xorg-server/hw/dmx/dmxclient.h +++ b/xorg-server/hw/dmx/dmxclient.h @@ -82,7 +82,6 @@ typedef XID KeySym64; #include #include #include -#include /* For XmuSnprintf */ #include diff --git a/xorg-server/hw/dmx/dmxcursor.h b/xorg-server/hw/dmx/dmxcursor.h index 5242268c1..fc2e118e0 100644 --- a/xorg-server/hw/dmx/dmxcursor.h +++ b/xorg-server/hw/dmx/dmxcursor.h @@ -64,9 +64,9 @@ extern void dmxBECreateCursor(ScreenPtr pScreen, CursorPtr pCursor); extern Bool dmxBEFreeCursor(ScreenPtr pScreen, CursorPtr pCursor); #define DMX_GET_CURSOR_PRIV(_pCursor, _pScreen) ((dmxCursorPrivPtr) \ - dixLookupPrivate(&(_pCursor)->devPrivates, CursorScreenKey(_pScreen))) + dixLookupScreenPrivate(&(_pCursor)->devPrivates, CursorScreenKey, _pScreen)) #define DMX_SET_CURSOR_PRIV(_pCursor, _pScreen, v) \ - dixSetPrivate(&(_pCursor)->devPrivates, CursorScreenKey(_pScreen), v) + dixSetScreenPrivate(&(_pCursor)->devPrivates, CursorScreenKey, _pScreen, v) #endif /* DMXCURSOR_H */ diff --git a/xorg-server/hw/dmx/dmxextension.c b/xorg-server/hw/dmx/dmxextension.c index db5709ee6..80d11ee89 100644 --- a/xorg-server/hw/dmx/dmxextension.c +++ b/xorg-server/hw/dmx/dmxextension.c @@ -455,7 +455,7 @@ static void dmxSetRootWindowOrigin(int idx, int x, int y) pScreen->y = dmxScreen->rootYOrigin; /* Recalculate the Xinerama regions and data structs */ - XineramaReinitData(pScreen); + XineramaReinitData(); /* Adjust each of the root window's children */ if (!idx) ReinitializeRootWindow(screenInfo.screens[0]->root, xoff, yoff); diff --git a/xorg-server/hw/dmx/dmxinit.c b/xorg-server/hw/dmx/dmxinit.c index bc1509b35..165476c5f 100644 --- a/xorg-server/hw/dmx/dmxinit.c +++ b/xorg-server/hw/dmx/dmxinit.c @@ -56,6 +56,7 @@ #include "dmxpict.h" #include /* For gettimeofday */ +#include /* For XmuGetHostname */ #include "dixstruct.h" #ifdef PANORAMIX #include "panoramiXsrv.h" @@ -131,7 +132,7 @@ static int dmxErrorHandler(Display *dpy, XErrorEvent *ev) /* Find major opcode name */ if (ev->request_code < 128) { - XmuSnprintf(request, sizeof(request), "%d", ev->request_code); + snprintf(request, sizeof(request), "%d", ev->request_code); XGetErrorDatabaseText(dpy, "XRequest", request, "", buf, sizeof(buf)); } else { for (ext = dpy->ext_procs; @@ -145,8 +146,8 @@ static int dmxErrorHandler(Display *dpy, XErrorEvent *ev) /* Find minor opcode name */ if (ev->request_code >= 128 && ext) { - XmuSnprintf(request, sizeof(request), "%d", ev->request_code); - XmuSnprintf(request, sizeof(request), "%s.%d", + snprintf(request, sizeof(request), "%d", ev->request_code); + snprintf(request, sizeof(request), "%s.%d", ext->name, ev->minor_code); XGetErrorDatabaseText(dpy, "XRequest", request, "", buf, sizeof(buf)); dmxLog(dmxWarning, " Minor opcode: %d (%s)\n", @@ -515,7 +516,7 @@ static const char *dmxExecOS(void) if (!initialized++) { memset(buffer, 0, sizeof(buffer)); uname(&u); - XmuSnprintf(buffer, sizeof(buffer)-1, "%s %s %s", + snprintf(buffer, sizeof(buffer)-1, "%s %s %s", u.sysname, u.release, u.version); } return buffer; @@ -530,7 +531,7 @@ static const char *dmxBuildCompiler(void) if (!initialized++) { memset(buffer, 0, sizeof(buffer)); #if defined(__GNUC__) && defined(__GNUC_MINOR__) &&defined(__GNUC_PATCHLEVEL__) - XmuSnprintf(buffer, sizeof(buffer)-1, "gcc %d.%d.%d", + snprintf(buffer, sizeof(buffer)-1, "gcc %d.%d.%d", __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__); #endif } diff --git a/xorg-server/hw/dmx/dmxprop.c b/xorg-server/hw/dmx/dmxprop.c index 95efcb0e0..b4695dd5d 100644 --- a/xorg-server/hw/dmx/dmxprop.c +++ b/xorg-server/hw/dmx/dmxprop.c @@ -1,347 +1,348 @@ -/* - * Copyright 2002-2003 Red Hat Inc., Durham, North Carolina. - * - * All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation on the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial - * portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NON-INFRINGEMENT. IN NO EVENT SHALL RED HAT AND/OR THEIR SUPPLIERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/* - * Authors: - * Rickard E. (Rik) Faith - * - */ - -/** \file - * - * It is possible for one of the DMX "backend displays" to actually be - * smaller than the dimensions of the backend X server. Therefore, it - * is possible for more than one of the DMX "backend displays" to be - * physically located on the same backend X server. This situation must - * be detected so that cursor motion can be handled in an expected - * fashion. - * - * We could analyze the names used for the DMX "backend displays" (e.g., - * the names passed to the -display command-line parameter), but there - * are many possible names for a single X display, and failing to detect - * sameness leads to very unexpected results. Therefore, whenever the - * DMX server opens a window on a backend X server, a property value is - * queried and set on that backend to detect when another window is - * already open on that server. - * - * Further, it is possible that two different DMX server instantiations - * both have windows on the same physical backend X server. This case - * is also detected so that pointer input is not taken from that - * particular backend X server. - * - * The routines in this file handle the property management. */ - -#ifdef HAVE_DMX_CONFIG_H -#include -#endif - -#include "dmx.h" -#include "dmxprop.h" -#include "dmxlog.h" - -/** Holds the window id of all DMX windows on the backend X server. */ -#define DMX_ATOMNAME "DMX_NAME" - -/** The identification string of this DMX server */ -#define DMX_IDENT "Xdmx" - -extern char *display; - -static int dmxPropertyErrorHandler(Display *dpy, XErrorEvent *ev) -{ - return 0; -} - -static const unsigned char *dmxPropertyIdentifier(void) -{ - /* RATS: These buffers are only used in - * length-limited calls. */ - char hostname[256]; - static char buf[128]; - static int initialized = 0; - - if (initialized++) return (unsigned char *)buf; - - XmuGetHostname(hostname, sizeof(hostname)); - XmuSnprintf(buf, sizeof(buf), "%s:%s:%s", DMX_IDENT, hostname, display); - return (unsigned char *)buf; -} - -/** Starting with the \a start screen, iterate over all of the screens - * on the same physical X server as \a start, calling \a f with the - * screen and the \a closure. (The common case is that \a start is the - * only DMX window on the backend X server.) */ -void *dmxPropertyIterate(DMXScreenInfo *start, - void *(*f)(DMXScreenInfo *dmxScreen, void *), - void *closure) -{ - DMXScreenInfo *pt; - - if (!start->next) { - if (!start->beDisplay) return NULL; - return f(start, closure); - } - - for (pt = start->next; /* condition at end of loop */; pt = pt->next) { - void *retval; - /* beDisplay ban be NULL if a screen was detached */ - dmxLog(dmxDebug, "pt = %p\n", pt); - dmxLog(dmxDebug, "pt->beDisplay = %p\n", pt->beDisplay); - if (pt->beDisplay && (retval = f(pt, closure))) return retval; - if (pt == start) break; - } - return NULL; -} - -/** Returns 0 if this is the only Xdmx session on the display; 1 - * otherwise. */ -static int dmxPropertyCheckOtherServers(DMXScreenInfo *dmxScreen, Atom atom) -{ - Display *dpy = dmxScreen->beDisplay; - XTextProperty tp; - XTextProperty tproot; - const char *pt; - int retcode = 0; - char **list = NULL; - int count = 0; - int i; - int (*dmxOldHandler)(Display *, XErrorEvent *); - - if (!dpy) - return 0; - - if (!XGetTextProperty(dpy, RootWindow(dpy,0), &tproot, atom) - || !tproot.nitems) return 0; - - /* Ignore BadWindow errors for this - * routine because the window id stored - * in the property might be old */ - dmxOldHandler = XSetErrorHandler(dmxPropertyErrorHandler); - for (pt = (const char *)tproot.value; pt && *pt; pt = pt ? pt + 1 : NULL) { - if ((pt = strchr(pt, ','))) { - Window win = strtol(pt+1, NULL, 10); - if (XGetTextProperty(dpy, win, &tp, atom) && tp.nitems) { - if (!strncmp((char *)tp.value, DMX_IDENT, strlen(DMX_IDENT))) { - int flag = 0; - for (i = 0; i < count; i++) - if (!strcmp(list[i], (char *)tp.value)) { - ++flag; - break; - } - if (flag) continue; - ++retcode; - dmxLogOutputWarning(dmxScreen, - "%s also running on %s\n", - tp.value, dmxScreen->name); - list = realloc(list, ++count * sizeof(*list)); - list[count-1] = malloc(tp.nitems + 2); - strncpy(list[count-1], (char *)tp.value, tp.nitems + 1); - } - XFree(tp.value); - } - } - } - XSetErrorHandler(dmxOldHandler); - - for (i = 0; i < count; i++) free(list[i]); - free(list); - XFree(tproot.value); - if (!retcode) - dmxLogOutput(dmxScreen, "No Xdmx server running on backend\n"); - return retcode; -} - -/** Returns NULL if this is the only Xdmx window on the display. - * Otherwise, returns a pointer to the dmxScreen of the other windows on - * the display. */ -static DMXScreenInfo *dmxPropertyCheckOtherWindows(DMXScreenInfo *dmxScreen, - Atom atom) -{ - Display *dpy = dmxScreen->beDisplay; - const unsigned char *id = dmxPropertyIdentifier(); - XTextProperty tproot; - XTextProperty tp; - const char *pt; - int (*dmxOldHandler)(Display *, XErrorEvent *); - - if (!dpy) - return NULL; - - if (!XGetTextProperty(dpy, RootWindow(dpy,0), &tproot, atom) - || !tproot.nitems) return 0; - - /* Ignore BadWindow errors for this - * routine because the window id stored - * in the property might be old */ - dmxOldHandler = XSetErrorHandler(dmxPropertyErrorHandler); - for (pt = (const char *)tproot.value; pt && *pt; pt = pt ? pt + 1 : NULL) { - if ((pt = strchr(pt, ','))) { - Window win = strtol(pt+1, NULL, 10); - if (XGetTextProperty(dpy, win, &tp, atom) && tp.nitems) { - dmxLog(dmxDebug,"On %s/%lu: %s\n", - dmxScreen->name, win, tp.value); - if (!strncmp((char *)tp.value, (char *)id, - strlen((char *)id))) { - int idx; - - if (!(pt = strchr((char *)tp.value, ','))) continue; - idx = strtol(pt+1, NULL, 10); - if (idx < 0 || idx >= dmxNumScreens) continue; - if (dmxScreens[idx].scrnWin != win) continue; - XSetErrorHandler(dmxOldHandler); - return &dmxScreens[idx]; - } - XFree(tp.value); - } - } - } - XSetErrorHandler(dmxOldHandler); - XFree(tproot.value); - return 0; -} - -/** Returns 0 if this is the only Xdmx session on the display; 1 - * otherwise. */ -int dmxPropertyDisplay(DMXScreenInfo *dmxScreen) -{ - Atom atom; - const unsigned char *id = dmxPropertyIdentifier(); - Display *dpy = dmxScreen->beDisplay; - - if (!dpy) - return 0; - - atom = XInternAtom(dpy, DMX_ATOMNAME, False); - if (dmxPropertyCheckOtherServers(dmxScreen, atom)) { - dmxScreen->shared = 1; - return 1; - } - XChangeProperty(dpy, RootWindow(dpy,0), atom, XA_STRING, 8, - PropModeReplace, id, strlen((char *)id)); - return 0; -} - -/** Returns 1 if the dmxScreen and the display in \a name are on the - * same display, or 0 otherwise. We can't just compare the display - * names because there can be multiple synonyms for the same display, - * some of which cannot be determined without accessing the display - * itself (e.g., domain aliases or machines with multiple NICs). */ -int dmxPropertySameDisplay(DMXScreenInfo *dmxScreen, const char *name) -{ - Display *dpy0 = dmxScreen->beDisplay; - Atom atom0; - XTextProperty tp0; - Display *dpy1 = NULL; - Atom atom1; - XTextProperty tp1; - int retval = 0; - - if (!dpy0) - return 0; - - tp0.nitems = 0; - tp1.nitems = 0; - - if ((atom0 = XInternAtom(dpy0, DMX_ATOMNAME, True)) == None) { - dmxLog(dmxWarning, "No atom on %s\n", dmxScreen->name); - return 0; - } - if (!XGetTextProperty(dpy0, RootWindow(dpy0,0), &tp0, atom0) - || !tp0.nitems) { - dmxLog(dmxWarning, "No text property on %s\n", dmxScreen->name); - return 0; - } - - if (!(dpy1 = XOpenDisplay(name))) { - dmxLog(dmxWarning, "Cannot open %s\n", name); - goto cleanup; - } - atom1 = XInternAtom(dpy1, DMX_ATOMNAME, True); - if (atom1 == None) { - dmxLog(dmxDebug, "No atom on %s\n", name); - goto cleanup; - } - if (!XGetTextProperty(dpy1, RootWindow(dpy1,0), &tp1, atom1) - || !tp1.nitems) { - dmxLog(dmxDebug, "No text property on %s\n", name); - goto cleanup; - } - if (!strcmp((char *)tp0.value, (char *)tp1.value)) retval = 1; - - cleanup: - if (tp0.nitems) XFree(tp0.value); - if (tp1.nitems) XFree(tp1.value); - if (dpy1) XCloseDisplay(dpy1); - return retval; -} - -/** Prints a log message if \a dmxScreen is on the same backend X server - * as some other DMX backend (output) screen. Modifies the property - * (#DMX_ATOMNAME) on the backend X server to reflect the creation of \a - * dmxScreen. - * - * The root window of the backend X server holds a list of window ids - * for all DMX windows (on this DMX server or some other DMX server). - * - * This list can then be iterated, and the property for each window can - * be examined. This property contains the following tuple (no quotes): - * - * "#DMX_IDENT::," - */ -void dmxPropertyWindow(DMXScreenInfo *dmxScreen) -{ - Atom atom; - const unsigned char *id = dmxPropertyIdentifier(); - Display *dpy = dmxScreen->beDisplay; - Window win = dmxScreen->scrnWin; - DMXScreenInfo *other; - char buf[128]; /* RATS: only used with XmuSnprintf */ - - if (!dpy) - return; /* FIXME: What should be done here if Xdmx is started - * with this screen initially detached? - */ - - atom = XInternAtom(dpy, DMX_ATOMNAME, False); - if ((other = dmxPropertyCheckOtherWindows(dmxScreen, atom))) { - DMXScreenInfo *tmp = dmxScreen->next; - dmxScreen->next = (other->next ? other->next : other); - other->next = (tmp ? tmp : dmxScreen); - dmxLog(dmxDebug, "%d/%s/%lu and %d/%s/%lu are on the same backend\n", - dmxScreen->index, dmxScreen->name, dmxScreen->scrnWin, - other->index, other->name, other->scrnWin); - } - - XmuSnprintf(buf, sizeof(buf), ".%d,%lu", dmxScreen->index, - (long unsigned)win); - XChangeProperty(dpy, RootWindow(dpy,0), atom, XA_STRING, 8, - PropModeAppend, (unsigned char *)buf, strlen(buf)); - - XmuSnprintf(buf, sizeof(buf), "%s,%d", id, dmxScreen->index); - XChangeProperty(dpy, win, atom, XA_STRING, 8, - PropModeAppend, (unsigned char *)buf, strlen(buf)); -} +/* + * Copyright 2002-2003 Red Hat Inc., Durham, North Carolina. + * + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation on the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NON-INFRINGEMENT. IN NO EVENT SHALL RED HAT AND/OR THEIR SUPPLIERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* + * Authors: + * Rickard E. (Rik) Faith + * + */ + +/** \file + * + * It is possible for one of the DMX "backend displays" to actually be + * smaller than the dimensions of the backend X server. Therefore, it + * is possible for more than one of the DMX "backend displays" to be + * physically located on the same backend X server. This situation must + * be detected so that cursor motion can be handled in an expected + * fashion. + * + * We could analyze the names used for the DMX "backend displays" (e.g., + * the names passed to the -display command-line parameter), but there + * are many possible names for a single X display, and failing to detect + * sameness leads to very unexpected results. Therefore, whenever the + * DMX server opens a window on a backend X server, a property value is + * queried and set on that backend to detect when another window is + * already open on that server. + * + * Further, it is possible that two different DMX server instantiations + * both have windows on the same physical backend X server. This case + * is also detected so that pointer input is not taken from that + * particular backend X server. + * + * The routines in this file handle the property management. */ + +#ifdef HAVE_DMX_CONFIG_H +#include +#endif + +#include "dmx.h" +#include "dmxprop.h" +#include "dmxlog.h" +#include /* For XmuGetHostname */ + +/** Holds the window id of all DMX windows on the backend X server. */ +#define DMX_ATOMNAME "DMX_NAME" + +/** The identification string of this DMX server */ +#define DMX_IDENT "Xdmx" + +extern char *display; + +static int dmxPropertyErrorHandler(Display *dpy, XErrorEvent *ev) +{ + return 0; +} + +static const unsigned char *dmxPropertyIdentifier(void) +{ + /* RATS: These buffers are only used in + * length-limited calls. */ + char hostname[256]; + static char buf[128]; + static int initialized = 0; + + if (initialized++) return (unsigned char *)buf; + + XmuGetHostname(hostname, sizeof(hostname)); + snprintf(buf, sizeof(buf), "%s:%s:%s", DMX_IDENT, hostname, display); + return (unsigned char *)buf; +} + +/** Starting with the \a start screen, iterate over all of the screens + * on the same physical X server as \a start, calling \a f with the + * screen and the \a closure. (The common case is that \a start is the + * only DMX window on the backend X server.) */ +void *dmxPropertyIterate(DMXScreenInfo *start, + void *(*f)(DMXScreenInfo *dmxScreen, void *), + void *closure) +{ + DMXScreenInfo *pt; + + if (!start->next) { + if (!start->beDisplay) return NULL; + return f(start, closure); + } + + for (pt = start->next; /* condition at end of loop */; pt = pt->next) { + void *retval; + /* beDisplay ban be NULL if a screen was detached */ + dmxLog(dmxDebug, "pt = %p\n", pt); + dmxLog(dmxDebug, "pt->beDisplay = %p\n", pt->beDisplay); + if (pt->beDisplay && (retval = f(pt, closure))) return retval; + if (pt == start) break; + } + return NULL; +} + +/** Returns 0 if this is the only Xdmx session on the display; 1 + * otherwise. */ +static int dmxPropertyCheckOtherServers(DMXScreenInfo *dmxScreen, Atom atom) +{ + Display *dpy = dmxScreen->beDisplay; + XTextProperty tp; + XTextProperty tproot; + const char *pt; + int retcode = 0; + char **list = NULL; + int count = 0; + int i; + int (*dmxOldHandler)(Display *, XErrorEvent *); + + if (!dpy) + return 0; + + if (!XGetTextProperty(dpy, RootWindow(dpy,0), &tproot, atom) + || !tproot.nitems) return 0; + + /* Ignore BadWindow errors for this + * routine because the window id stored + * in the property might be old */ + dmxOldHandler = XSetErrorHandler(dmxPropertyErrorHandler); + for (pt = (const char *)tproot.value; pt && *pt; pt = pt ? pt + 1 : NULL) { + if ((pt = strchr(pt, ','))) { + Window win = strtol(pt+1, NULL, 10); + if (XGetTextProperty(dpy, win, &tp, atom) && tp.nitems) { + if (!strncmp((char *)tp.value, DMX_IDENT, strlen(DMX_IDENT))) { + int flag = 0; + for (i = 0; i < count; i++) + if (!strcmp(list[i], (char *)tp.value)) { + ++flag; + break; + } + if (flag) continue; + ++retcode; + dmxLogOutputWarning(dmxScreen, + "%s also running on %s\n", + tp.value, dmxScreen->name); + list = realloc(list, ++count * sizeof(*list)); + list[count-1] = malloc(tp.nitems + 2); + strncpy(list[count-1], (char *)tp.value, tp.nitems + 1); + } + XFree(tp.value); + } + } + } + XSetErrorHandler(dmxOldHandler); + + for (i = 0; i < count; i++) free(list[i]); + free(list); + XFree(tproot.value); + if (!retcode) + dmxLogOutput(dmxScreen, "No Xdmx server running on backend\n"); + return retcode; +} + +/** Returns NULL if this is the only Xdmx window on the display. + * Otherwise, returns a pointer to the dmxScreen of the other windows on + * the display. */ +static DMXScreenInfo *dmxPropertyCheckOtherWindows(DMXScreenInfo *dmxScreen, + Atom atom) +{ + Display *dpy = dmxScreen->beDisplay; + const unsigned char *id = dmxPropertyIdentifier(); + XTextProperty tproot; + XTextProperty tp; + const char *pt; + int (*dmxOldHandler)(Display *, XErrorEvent *); + + if (!dpy) + return NULL; + + if (!XGetTextProperty(dpy, RootWindow(dpy,0), &tproot, atom) + || !tproot.nitems) return 0; + + /* Ignore BadWindow errors for this + * routine because the window id stored + * in the property might be old */ + dmxOldHandler = XSetErrorHandler(dmxPropertyErrorHandler); + for (pt = (const char *)tproot.value; pt && *pt; pt = pt ? pt + 1 : NULL) { + if ((pt = strchr(pt, ','))) { + Window win = strtol(pt+1, NULL, 10); + if (XGetTextProperty(dpy, win, &tp, atom) && tp.nitems) { + dmxLog(dmxDebug,"On %s/%lu: %s\n", + dmxScreen->name, win, tp.value); + if (!strncmp((char *)tp.value, (char *)id, + strlen((char *)id))) { + int idx; + + if (!(pt = strchr((char *)tp.value, ','))) continue; + idx = strtol(pt+1, NULL, 10); + if (idx < 0 || idx >= dmxNumScreens) continue; + if (dmxScreens[idx].scrnWin != win) continue; + XSetErrorHandler(dmxOldHandler); + return &dmxScreens[idx]; + } + XFree(tp.value); + } + } + } + XSetErrorHandler(dmxOldHandler); + XFree(tproot.value); + return 0; +} + +/** Returns 0 if this is the only Xdmx session on the display; 1 + * otherwise. */ +int dmxPropertyDisplay(DMXScreenInfo *dmxScreen) +{ + Atom atom; + const unsigned char *id = dmxPropertyIdentifier(); + Display *dpy = dmxScreen->beDisplay; + + if (!dpy) + return 0; + + atom = XInternAtom(dpy, DMX_ATOMNAME, False); + if (dmxPropertyCheckOtherServers(dmxScreen, atom)) { + dmxScreen->shared = 1; + return 1; + } + XChangeProperty(dpy, RootWindow(dpy,0), atom, XA_STRING, 8, + PropModeReplace, id, strlen((char *)id)); + return 0; +} + +/** Returns 1 if the dmxScreen and the display in \a name are on the + * same display, or 0 otherwise. We can't just compare the display + * names because there can be multiple synonyms for the same display, + * some of which cannot be determined without accessing the display + * itself (e.g., domain aliases or machines with multiple NICs). */ +int dmxPropertySameDisplay(DMXScreenInfo *dmxScreen, const char *name) +{ + Display *dpy0 = dmxScreen->beDisplay; + Atom atom0; + XTextProperty tp0; + Display *dpy1 = NULL; + Atom atom1; + XTextProperty tp1; + int retval = 0; + + if (!dpy0) + return 0; + + tp0.nitems = 0; + tp1.nitems = 0; + + if ((atom0 = XInternAtom(dpy0, DMX_ATOMNAME, True)) == None) { + dmxLog(dmxWarning, "No atom on %s\n", dmxScreen->name); + return 0; + } + if (!XGetTextProperty(dpy0, RootWindow(dpy0,0), &tp0, atom0) + || !tp0.nitems) { + dmxLog(dmxWarning, "No text property on %s\n", dmxScreen->name); + return 0; + } + + if (!(dpy1 = XOpenDisplay(name))) { + dmxLog(dmxWarning, "Cannot open %s\n", name); + goto cleanup; + } + atom1 = XInternAtom(dpy1, DMX_ATOMNAME, True); + if (atom1 == None) { + dmxLog(dmxDebug, "No atom on %s\n", name); + goto cleanup; + } + if (!XGetTextProperty(dpy1, RootWindow(dpy1,0), &tp1, atom1) + || !tp1.nitems) { + dmxLog(dmxDebug, "No text property on %s\n", name); + goto cleanup; + } + if (!strcmp((char *)tp0.value, (char *)tp1.value)) retval = 1; + + cleanup: + if (tp0.nitems) XFree(tp0.value); + if (tp1.nitems) XFree(tp1.value); + if (dpy1) XCloseDisplay(dpy1); + return retval; +} + +/** Prints a log message if \a dmxScreen is on the same backend X server + * as some other DMX backend (output) screen. Modifies the property + * (#DMX_ATOMNAME) on the backend X server to reflect the creation of \a + * dmxScreen. + * + * The root window of the backend X server holds a list of window ids + * for all DMX windows (on this DMX server or some other DMX server). + * + * This list can then be iterated, and the property for each window can + * be examined. This property contains the following tuple (no quotes): + * + * "#DMX_IDENT::," + */ +void dmxPropertyWindow(DMXScreenInfo *dmxScreen) +{ + Atom atom; + const unsigned char *id = dmxPropertyIdentifier(); + Display *dpy = dmxScreen->beDisplay; + Window win = dmxScreen->scrnWin; + DMXScreenInfo *other; + char buf[128]; /* RATS: only used with snprintf */ + + if (!dpy) + return; /* FIXME: What should be done here if Xdmx is started + * with this screen initially detached? + */ + + atom = XInternAtom(dpy, DMX_ATOMNAME, False); + if ((other = dmxPropertyCheckOtherWindows(dmxScreen, atom))) { + DMXScreenInfo *tmp = dmxScreen->next; + dmxScreen->next = (other->next ? other->next : other); + other->next = (tmp ? tmp : dmxScreen); + dmxLog(dmxDebug, "%d/%s/%lu and %d/%s/%lu are on the same backend\n", + dmxScreen->index, dmxScreen->name, dmxScreen->scrnWin, + other->index, other->name, other->scrnWin); + } + + snprintf(buf, sizeof(buf), ".%d,%lu", dmxScreen->index, + (long unsigned)win); + XChangeProperty(dpy, RootWindow(dpy,0), atom, XA_STRING, 8, + PropModeAppend, (unsigned char *)buf, strlen(buf)); + + snprintf(buf, sizeof(buf), "%s,%d", id, dmxScreen->index); + XChangeProperty(dpy, win, atom, XA_STRING, 8, + PropModeAppend, (unsigned char *)buf, strlen(buf)); +} diff --git a/xorg-server/hw/dmx/doc/dmx.xml b/xorg-server/hw/dmx/doc/dmx.xml index c998485bc..ce472c2bf 100644 --- a/xorg-server/hw/dmx/doc/dmx.xml +++ b/xorg-server/hw/dmx/doc/dmx.xml @@ -1,3430 +1,3430 @@ - - - -
- - - - Distributed Multihead X design - - Kevin E.Martin - David H.Dawes - Rickard E.Faith - - 29 June 2004 (created 25 July 2001) - - This document covers the motivation, background, design, and - implementation of the distributed multihead X (DMX) system. It - is a living document and describes the current design and - implementation details of the DMX system. As the project - progresses, this document will be continually updated to reflect - the changes in the code and/or design. Copyright 2001 by VA - Linux Systems, Inc., Fremont, California. Copyright 2001-2004 - by Red Hat, Inc., Raleigh, North Carolina - - - - - -Introduction - - -The Distributed Multihead X Server - -Current Open Source multihead solutions are limited to a single -physical machine. A single X server controls multiple display devices, -which can be arranged as independent heads or unified into a single -desktop (with Xinerama). These solutions are limited to the number of -physical devices that can co-exist in a single machine (e.g., due to the -number of AGP/PCI slots available for graphics cards). Thus, large -tiled displays are not currently possible. The work described in this -paper will eliminate the requirement that the display devices reside in -the same physical machine. This will be accomplished by developing a -front-end proxy X server that will control multiple back-end X servers -that make up the large display. - - -The overall structure of the distributed multihead X (DMX) project is -as follows: A single front-end X server will act as a proxy to a set of -back-end X servers, which handle all of the visible rendering. X -clients will connect to the front-end server just as they normally would -to a regular X server. The front-end server will present an abstracted -view to the client of a single large display. This will ensure that all -standard X clients will continue to operate without modification -(limited, as always, by the visuals and extensions provided by the X -server). Clients that are DMX-aware will be able to use an extension to -obtain information about the back-end servers (e.g., for placement of -pop-up windows, window alignments by the window manager, etc.). - - -The architecture of the DMX server is divided into two main sections: -input (e.g., mouse and keyboard events) and output (e.g., rendering and -windowing requests). Each of these are describe briefly below, and the -rest of this design document will describe them in greater detail. - - -The DMX server can receive input from three general types of input -devices: "local" devices that are physically attached to the machine on -which DMX is running, "backend" devices that are physically attached to -one or more of the back-end X servers (and that generate events via the -X protocol stream from the backend), and "console" devices that can be -abstracted from any non-back-end X server. Backend and console devices -are treated differently because the pointer device on the back-end X -server also controls the location of the hardware X cursor. Full -support for XInput extension devices is provided. - - -Rendering requests will be accepted by the front-end server; however, -rendering to visible windows will be broken down as needed and sent to -the appropriate back-end server(s) via X11 library calls for actual -rendering. The basic framework will follow a Xnest-style approach. GC -state will be managed in the front-end server and sent to the -appropriate back-end server(s) as required. Pixmap rendering will (at -least initially) be handled by the front-end X server. Windowing -requests (e.g., ordering, mapping, moving, etc.) will handled in the -front-end server. If the request requires a visible change, the -windowing operation will be translated into requests for the appropriate -back-end server(s). Window state will be mirrored in the back-end -server(s) as needed. - - - - -Layout of Paper - -The next section describes the general development plan that was -actually used for implementation. The final section discusses -outstanding issues at the conclusion of development. The first appendix -provides low-level technical detail that may be of interest to those -intimately familiar with the X server architecture. The final appendix -describes the four phases of development that were performed during the -first two years of development. - - -The final year of work was divided into 9 tasks that are not -described in specific sections of this document. The major tasks during -that time were the enhancement of the reconfiguration ability added in -Phase IV, addition of support for a dynamic number of back-end displays -(instead of a hard-coded limit), and the support for back-end display -and input removal and addition. This work is mentioned in this paper, -but is not covered in detail. - - - - - - -Development plan - -This section describes the development plan from approximately June -2001 through July 2003. - - - -Bootstrap code - -To allow for rapid development of the DMX server by multiple -developers during the first development stage, the problem will be -broken down into three tasks: the overall DMX framework, back-end -rendering services and input device handling services. However, before -the work begins on these tasks, a simple framework that each developer -could use was implemented to bootstrap the development effort. This -framework renders to a single back-end server and provides dummy input -devices (i.e., the keyboard and mouse). The simple back-end rendering -service was implemented using the shadow framebuffer support currently -available in the XFree86 environment. - - -Using this bootstrapping framework, each developer has been able to -work on each of the tasks listed above independently as follows: the -framework will be extended to handle arbitrary back-end server -configurations; the back-end rendering services will be transitioned to -the more efficient Xnest-style implementation; and, an input device -framework to handle various input devices via the input extension will -be developed. - - -Status: The boot strap code is complete. - - - - - -Input device handling - -An X server (including the front-end X server) requires two core -input devices -- a keyboard and a pointer (mouse). These core devices -are handled and required by the core X11 protocol. Additional types of -input devices may be attached and utilized via the XInput extension. -These are usually referred to as ``XInput extension devices'', - - -There are some options as to how the front-end X server gets its core -input devices: - - - - Local Input. The physical input devices (e.g., keyboard and - mouse) can be attached directly to the front-end X server. In this - case, the keyboard and mouse on the machine running the front-end X - server will be used. The front-end will have drivers to read the - raw input from those devices and convert it into the required X - input events (e.g., key press/release, pointer button press/release, - pointer motion). The front-end keyboard driver will keep track of - keyboard properties such as key and modifier mappings, autorepeat - state, keyboard sound and led state. Similarly the front-end - pointer driver will keep track if pointer properties such as the - button mapping and movement acceleration parameters. With this - option, input is handled fully in the front-end X server, and the - back-end X servers are used in a display-only mode. This option was - implemented and works for a limited number of Linux-specific - devices. Adding additional local input devices for other - architectures is expected to be relatively simple. - - - The following options are available for implementing local input - devices: - - - - The XFree86 X server has modular input drivers that could - be adapted for this purpose. The mouse driver supports a wide - range of mouse types and interfaces, as well as a range of - Operating System platforms. The keyboard driver in XFree86 is - not currently as modular as the mouse driver, but could be made - so. The XFree86 X server also has a range of other input - drivers for extended input devices such as tablets and touch - screens. Unfortunately, the XFree86 drivers are generally - complex, often simultaneously providing support for multiple - devices across multiple architectures; and rely so heavily on - XFree86-specific helper-functions, that this option was not - pursued. - - - - - The kdrive X server in XFree86 has built-in drivers that - support PS/2 mice and keyboard under Linux. The mouse driver - can indirectly handle other mouse types if the Linux utility - gpm is used as to translate the native mouse protocol into - PS/2 mouse format. These drivers could be adapted and built in - to the front-end X server if this range of hardware and OS - support is sufficient. While much simpler than the XFree86 - drivers, the kdrive drivers were not used for the DMX - implementation. - - - - - Reimplementation of keyboard and mouse drivers from - scratch for the DMX framework. Because keyboard and mouse - drivers are relatively trivial to implement, this pathway was - selected. Other drivers in the X source tree were referenced, - and significant contributions from other drivers are noted in - the DMX source code. - - - - - - - - Backend Input. The front-end can make use of the core input - devices attached to one or more of the back-end X servers. Core - input events from multiple back-ends are merged into a single input - event stream. This can work sanely when only a single set of input - devices is used at any given time. The keyboard and pointer state - will be handled in the front-end, with changes propagated to the - back-end servers as needed. This option was implemented and works - well. Because the core pointer on a back-end controls the hardware - mouse on that back-end, core pointers cannot be treated as XInput - extension devices. However, all back-end XInput extensions devices - can be mapped to either DMX core or DMX XInput extension devices. - - - - - Console Input. The front-end server could create a console - window that is displayed on an X server independent of the back-end - X servers. This console window could display things like the - physical screen layout, and the front-end could get its core input - events from events delivered to the console window. This option was - implemented and works well. To help the human navigate, window - outlines are also displayed in the console window. Further, console - windows can be used as either core or XInput extension devices. - - - - - Other options were initially explored, but they were all - partial subsets of the options listed above and, hence, are - irrelevant. - - - - - - -Although extended input devices are not specifically mentioned in the -Distributed X requirements, the options above were all implemented so -that XInput extension devices were supported. - - -The bootstrap code (Xdmx) had dummy input devices, and these are -still supported in the final version. These do the necessary -initialization to satisfy the X server's requirements for core pointer -and keyboard devices, but no input events are ever generated. - - -Status: The input code is complete. Because of the complexity of the -XFree86 input device drivers (and their heavy reliance on XFree86 -infrastructure), separate low-level device drivers were implemented for -Xdmx. The following kinds of drivers are supported (in general, the -devices can be treated arbitrarily as "core" input devices or as XInput -"extension" devices; and multiple instances of different kinds of -devices can be simultaneously available): - - - A "dummy" device drive that never generates events. - - - - - "Local" input is from the low-level hardware on which the - Xdmx binary is running. This is the only area where using the - XFree86 driver infrastructure would have been helpful, and then - only partially, since good support for generic USB devices does - not yet exist in XFree86 (in any case, XFree86 and kdrive driver - code was used where possible). Currently, the following local - devices are supported under Linux (porting to other operating - systems should be fairly straightforward): - - Linux keyboard - Linux serial mouse (MS) - Linux PS/2 mouse - USB keyboard - USB mouse - USB generic device (e.g., joystick, gamepad, etc.) - - - - - - "Backend" input is taken from one or more of the back-end - displays. In this case, events are taken from the back-end X - server and are converted to Xdmx events. Care must be taken so - that the sprite moves properly on the display from which input - is being taken. - - - - - "Console" input is taken from an X window that Xdmx - creates on the operator's display (i.e., on the machine running - the Xdmx binary). When the operator's mouse is inside the - console window, then those events are converted to Xdmx events. - Several special features are available: the console can display - outlines of windows that are on the Xdmx display (to facilitate - navigation), the cursor can be confined to the console, and a - "fine" mode can be activated to allow very precise cursor - positioning. - - - - - - - - - - - -Output device handling - -The output of the DMX system displays rendering and windowing -requests across multiple screens. The screens are typically arranged in -a grid such that together they represent a single large display. - - -The output section of the DMX code consists of two parts. The first -is in the front-end proxy X server (Xdmx), which accepts client -connections, manages the windows, and potentially renders primitives but -does not actually display any of the drawing primitives. The second -part is the back-end X server(s), which accept commands from the -front-end server and display the results on their screens. - - - -Initialization - -The DMX front-end must first initialize its screens by connecting to -each of the back-end X servers and collecting information about each of -these screens. However, the information collected from the back-end X -servers might be inconsistent. Handling these cases can be difficult -and/or inefficient. For example, a two screen system has one back-end X -server running at 16bpp while the second is running at 32bpp. -Converting rendering requests (e.g., XPutImage() or XGetImage() -requests) to the appropriate bit depth can be very time consuming. -Analyzing these cases to determine how or even if it is possible to -handle them is required. The current Xinerama code handles many of -these cases (e.g., in PanoramiXConsolidate()) and will be used as a -starting point. In general, the best solution is to use homogeneous X -servers and display devices. Using back-end servers with the same depth -is a requirement of the final DMX implementation. - - -Once this screen consolidation is finished, the relative position of -each back-end X server's screen in the unified screen is initialized. A -full-screen window is opened on each of the back-end X servers, and the -cursor on each screen is turned off. The final DMX implementation can -also make use of a partial-screen window, or multiple windows per -back-end screen. - - - - -Handling rendering requests - -After initialization, X applications connect to the front-end server. -There are two possible implementations of how rendering and windowing -requests are handled in the DMX system: - - - - A shadow framebuffer is used in the front-end server as the - render target. In this option, all protocol requests are completely - handled in the front-end server. All state and resources are - maintained in the front-end including a shadow copy of the entire - framebuffer. The framebuffers attached to the back-end servers are - updated by XPutImage() calls with data taken directly from the - shadow framebuffer. - - - This solution suffers from two main problems. First, it does not - take advantage of any accelerated hardware available in the system. - Second, the size of the XPutImage() calls can be quite large and - thus will be limited by the bandwidth available. - - - The initial DMX implementation used a shadow framebuffer by - default. - - - - - Rendering requests are sent to each back-end server for - handling (as is done in the Xnest server described above). In this - option, certain protocol requests are handled in the front-end - server and certain requests are repackaged and then sent to the - back-end servers. The framebuffer is distributed across the - multiple back-end servers. Rendering to the framebuffer is handled - on each back-end and can take advantage of any acceleration - available on the back-end servers' graphics display device. State - is maintained both in the front and back-end servers. - - - This solution suffers from two main drawbacks. First, protocol - requests are sent to all back-end servers -- even those that will - completely clip the rendering primitive -- which wastes bandwidth - and processing time. Second, state is maintained both in the front- - and back-end servers. These drawbacks are not as severe as in - option 1 (above) and can either be overcome through optimizations or - are acceptable. Therefore, this option will be used in the final - implementation. - - - The final DMX implementation defaults to this mechanism, but also - supports the shadow framebuffer mechanism. Several optimizations - were implemented to eliminate the drawbacks of the default - mechanism. These optimizations are described the section below and - in Phase II of the Development Results (see appendix). - - - - - - -Status: Both the shadow framebuffer and Xnest-style code is complete. - - - - - - - -Optimizing DMX - -Initially, the Xnest-style solution's performance will be measured -and analyzed to determine where the performance bottlenecks exist. -There are four main areas that will be addressed. - - -First, to obtain reasonable interactivity with the first development -phase, XSync() was called after each protocol request. The XSync() -function flushes any pending protocol requests. It then waits for the -back-end to process the request and send a reply that the request has -completed. This happens with each back-end server and performance -greatly suffers. As a result of the way XSync() is called in the first -development phase, the batching that the X11 library performs is -effectively defeated. The XSync() call usage will be analyzed and -optimized by batching calls and performing them at regular intervals, -except where interactivity will suffer (e.g., on cursor movements). - - -Second, the initial Xnest-style solution described above sends the -repackaged protocol requests to all back-end servers regardless of -whether or not they would be completely clipped out. The requests that -are trivially rejected on the back-end server wastes the limited -bandwidth available. By tracking clipping changes in the DMX X server's -windowing code (e.g., by opening, closing, moving or resizing windows), -we can determine whether or not back-end windows are visible so that -trivial tests in the front-end server's GC ops drawing functions can -eliminate these unnecessary protocol requests. - - -Third, each protocol request will be analyzed to determine if it is -possible to break the request into smaller pieces at display boundaries. -The initial ones to be analyzed are put and get image requests since -they will require the greatest bandwidth to transmit data between the -front and back-end servers. Other protocol requests will be analyzed -and those that will benefit from breaking them into smaller requests -will be implemented. - - -Fourth, an extension is being considered that will allow font glyphs to -be transferred from the front-end DMX X server to each back-end server. -This extension will permit the front-end to handle all font requests and -eliminate the requirement that all back-end X servers share the exact -same fonts as the front-end server. We are investigating the -feasibility of this extension during this development phase. - - -Other potential optimizations will be determined from the performance -analysis. - - -Please note that in our initial design, we proposed optimizing BLT -operations (e.g., XCopyArea() and window moves) by developing an -extension that would allow individual back-end servers to directly copy -pixel data to other back-end servers. This potential optimization was -in response to the simple image movement implementation that required -potentially many calls to GetImage() and PutImage(). However, the -current Xinerama implementation handles these BLT operations -differently. Instead of copying data to and from screens, they generate -expose events -- just as happens in the case when a window is moved from -off a screen to on screen. This approach saves the limited bandwidth -available between front and back-end servers and is being standardized -with Xinerama. It also eliminates the potential setup problems and -security issues resulting from having each back-end server open -connections to all other back-end servers. Therefore, we suggest -accepting Xinerama's expose event solution. - - -Also note that the approach proposed in the second and third -optimizations might cause backing store algorithms in the back-end to be -defeated, so a DMX X server configuration flag will be added to disable -these optimizations. - - -Status: The optimizations proposed above are complete. It was -determined that the using the xfs font server was sufficient and -creating a new mechanism to pass glyphs was redundant; therefore, the -fourth optimization proposed above was not included in DMX. - - - - - - -DMX X extension support - -The DMX X server keeps track of all the windowing information on the -back-end X servers, but does not currently export this information to -any client applications. An extension will be developed to pass the -screen information and back-end window IDs to DMX-aware clients. These -clients can then use this information to directly connect to and render -to the back-end windows. Bypassing the DMX X server allows DMX-aware -clients to break up complex rendering requests on their own and send -them directly to the windows on the back-end server's screens. An -example of a client that can make effective use of this extension is -Chromium. - - -Status: The extension, as implemented, is fully documented in -"Client-to-Server DMX Extension to the X Protocol". Future changes -might be required based on feedback and other proposed enhancements to -DMX. Currently, the following facilities are supported: - - - Screen information (clipping rectangle for each screen relative - to the virtual screen) - - - Window information (window IDs and clipping information for each - back-end window that corresponds to each DMX window) - - - Input device information (mappings from DMX device IDs to - back-end device IDs) - - - Force window creation (so that a client can override the - server-side lazy window creation optimization) - - - Reconfiguration (so that a client can request that a screen - position be changed) - - - Addition and removal of back-end servers and back-end and - console inputs. - - - - - - - - -Common X extension support - -The XInput, XKeyboard and Shape extensions are commonly used -extensions to the base X11 protocol. XInput allows multiple and -non-standard input devices to be accessed simultaneously. These input -devices can be connected to either the front-end or back-end servers. -XKeyboard allows much better keyboard mappings control. Shape adds -support for arbitrarily shaped windows and is used by various window -managers. Nearly all potential back-end X servers make these extensions -available, and support for each one will be added to the DMX system. - - -In addition to the extensions listed above, support for the X -Rendering extension (Render) is being developed. Render adds digital -image composition to the rendering model used by the X Window System. -While this extension is still under development by Keith Packard of HP, -support for the current version will be added to the DMX system. - - -Support for the XTest extension was added during the first -development phase. - - - -Status: The following extensions are supported and are discussed in -more detail in Phase IV of the Development Results (see appendix): - BIG-REQUESTS, - DEC-XTRAP, - DMX, - DPMS, - Extended-Visual-Information, - GLX, - LBX, - RECORD, - RENDER, - SECURITY, - SHAPE, - SYNC, - X-Resource, - XC-APPGROUP, - XC-MISC, - XFree86-Bigfont, - XINERAMA, - XInputExtension, - XKEYBOARD, and - XTEST. - - - - - -OpenGL support - -OpenGL support using the Mesa code base exists in XFree86 release 4 -and later. Currently, the direct rendering infrastructure (DRI) -provides accelerated OpenGL support for local clients and unaccelerated -OpenGL support (i.e., software rendering) is provided for non-local -clients. - - -The single head OpenGL support in XFree86 4.x will be extended to use -the DMX system. When the front and back-end servers are on the same -physical hardware, it is possible to use the DRI to directly render to -the back-end servers. First, the existing DRI will be extended to -support multiple display heads, and then to support the DMX system. -OpenGL rendering requests will be direct rendering to each back-end X -server. The DRI will request the screen layout (either from the -existing Xinerama extension or a DMX-specific extension). Support for -synchronized swap buffers will also be added (on hardware that supports -it). Note that a single front-end server with a single back-end server -on the same physical machine can emulate accelerated indirect rendering. - - -When the front and back-end servers are on different physical -hardware or are using non-XFree86 4.x X servers, a mechanism to render -primitives across the back-end servers will be provided. There are -several options as to how this can be implemented. - - - - - The existing OpenGL support in each back-end server can be - used by repackaging rendering primitives and sending them to each - back-end server. This option is similar to the unoptimized - Xnest-style approach mentioned above. Optimization of this solution - is beyond the scope of this project and is better suited to other - distributed rendering systems. - - - - Rendering to a pixmap in the front-end server using the - current XFree86 4.x code, and then displaying to the back-ends via - calls to XPutImage() is another option. This option is similar to - the shadow frame buffer approach mentioned above. It is slower and - bandwidth intensive, but has the advantage that the back-end servers - are not required to have OpenGL support. - - - -These, and other, options will be investigated in this phase of the -work. - - -Work by others have made Chromium DMX-aware. Chromium will use the -DMX X protocol extension to obtain information about the back-end -servers and will render directly to those servers, bypassing DMX. - - -Status: OpenGL support by the glxProxy extension was implemented by -SGI and has been integrated into the DMX code base. - - - - - - - - -Current issues - -In this sections the current issues are outlined that require further -investigation. - - - -Fonts - -The font path and glyphs need to be the same for the front-end and -each of the back-end servers. Font glyphs could be sent to the back-end -servers as necessary but this would consume a significant amount of -available bandwidth during font rendering for clients that use many -different fonts (e.g., Netscape). Initially, the font server (xfs) will -be used to provide the fonts to both the front-end and back-end servers. -Other possibilities will be investigated during development. - - - - -Zero width rendering primitives - -To allow pixmap and on-screen rendering to be pixel perfect, all -back-end servers must render zero width primitives exactly the same as -the front-end renders the primitives to pixmaps. For those back-end -servers that do not exactly match, zero width primitives will be -automatically converted to one width primitives. This can be handled in -the front-end server via the GC state. - - - - -Output scaling - -With very large tiled displays, it might be difficult to read the -information on the standard X desktop. In particular, the cursor can be -easily lost and fonts could be difficult to read. Automatic primitive -scaling might prove to be very useful. We will investigate the -possibility of scaling the cursor and providing a set of alternate -pre-scaled fonts to replace the standard fonts that many applications -use (e.g., fixed). Other options for automatic scaling will also be -investigated. - - - - -Per-screen colormaps - -Each screen's default colormap in the set of back-end X servers -should be able to be adjusted via a configuration utility. This support -is would allow the back-end screens to be calibrated via custom gamma -tables. On 24-bit systems that support a DirectColor visual, this type -of correction can be accommodated. One possible implementation would be -to advertise to X client of the DMX server a TrueColor visual while -using DirectColor visuals on the back-end servers to implement this type -of color correction. Other options will be investigated. - - - - - - -Appendix - - -Background - -This section describes the existing Open Source architectures that -can be used to handle multiple screens and upon which this development -project is based. This section was written before the implementation -was finished, and may not reflect actual details of the implementation. -It is left for historical interest only. - - - -Core input device handling - -The following is a description of how core input devices are handled -by an X server. - - - -InitInput() - -InitInput() is a DDX function that is called at the start of each -server generation from the X server's main() function. Its purpose is -to determine what input devices are connected to the X server, register -them with the DIX and MI layers, and initialize the input event queue. -InitInput() does not have a return value, but the X server will abort if -either a core keyboard device or a core pointer device are not -registered. Extended input (XInput) devices can also be registered in -InitInput(). - - -InitInput() usually has implementation specific code to determine -which input devices are available. For each input device it will be -using, it calls AddInputDevice(): - - - -AddInputDevice() -This DIX function allocates the device structure, -registers a callback function (which handles device init, close, on and -off), and returns the input handle, which can be treated as opaque. It -is called once for each input device. - - - - - -Once input handles for core keyboard and core pointer devices have -been obtained from AddInputDevice(). If both core devices are not -registered, then the X server will exit with a fatal error when it -attempts to start the input devices in InitAndStartDevices(), which is -called directly after InitInput() (see below). - - -The core pointer device is then registered with the miPointer code -(which does the high level cursor handling). While this registration -is not necessary for correct miPointer operation in the current XFree86 -code, it is still done mostly for compatibility reasons. - - - - - -miRegisterPointerDevice() -This MI function registers the core -pointer's input handle with with the miPointer code. - - - - -The final part of InitInput() is the initialization of the input -event queue handling. In most cases, the event queue handling provided -in the MI layer is used. The primary XFree86 X server uses its own -event queue handling to support some special cases related to the XInput -extension and the XFree86-specific DGA extension. For our purposes, the -MI event queue handling should be suitable. It is initialized by -calling mieqInit(): - - - -mieqInit() -This MI function initializes the MI event queue for the -core devices, and is passed the public component of the input handles -for the two core devices. - - - - -If a wakeup handler is required to deliver synchronous input -events, it can be registered here by calling the DIX function -RegisterBlockAndWakeupHandlers(). (See the devReadInput() description -below.) - - - - -InitAndStartDevices() - -InitAndStartDevices() is a DIX function that is called immediately -after InitInput() from the X server's main() function. Its purpose is -to initialize each input device that was registered with -AddInputDevice(), enable each input device that was successfully -initialized, and create the list of enabled input devices. Once each -registered device is processed in this way, the list of enabled input -devices is checked to make sure that both a core keyboard device and -core pointer device were registered and successfully enabled. If not, -InitAndStartDevices() returns failure, and results in the the X server -exiting with a fatal error. - - -Each registered device is initialized by calling its callback -(dev->deviceProc) with the DEVICE_INIT argument: - - - -(*dev->deviceProc)(dev, DEVICE_INIT) - -This function initializes the -device structs with core information relevant to the device. - - -For pointer devices, this means specifying the number of buttons, -default button mapping, the function used to get motion events (usually -miPointerGetMotionEvents()), the function used to change/control the -core pointer motion parameters (acceleration and threshold), and the -motion buffer size. - - -For keyboard devices, this means specifying the keycode range, -default keycode to keysym mapping, default modifier mapping, and the -functions used to sound the keyboard bell and modify/control the -keyboard parameters (LEDs, bell pitch and duration, key click, which -keys are auto-repeating, etc). - - - - -Each initialized device is enabled by calling EnableDevice(): - - - -EnableDevice() - -EnableDevice() calls the device callback with -DEVICE_ON: - - - (*dev->deviceProc)(dev, DEVICE_ON) - - This typically opens and - initializes the relevant physical device, and when appropriate, - registers the device's file descriptor (or equivalent) as a valid - input source. - - - - - EnableDevice() then adds the device handle to the X server's - global list of enabled devices. - - - - -InitAndStartDevices() then verifies that a valid core keyboard and -pointer has been initialized and enabled. It returns failure if either -are missing. - - - - -devReadInput() - -Each device will have some function that gets called to read its -physical input. These may be called in a number of different ways. In -the case of synchronous I/O, they will be called from a DDX -wakeup-handler that gets called after the server detects that new input is -available. In the case of asynchronous I/O, they will be called from a -(SIGIO) signal handler triggered when new input is available. This -function should do at least two things: make sure that input events get -enqueued, and make sure that the cursor gets moved for motion events -(except if these are handled later by the driver's own event queue -processing function, which cannot be done when using the MI event queue -handling). - - -Events are queued by calling mieqEnqueue(): - - - -mieqEnqueue() - -This MI function is used to add input events to the -event queue. It is simply passed the event to be queued. - - - - -The cursor position should be updated when motion events are -enqueued, by calling either miPointerAbsoluteCursor() or -miPointerDeltaCursor(): - - - -miPointerAbsoluteCursor() - -This MI function is used to move the -cursor to the absolute coordinates provided. - - -miPointerDeltaCursor() - -This MI function is used to move the cursor -relative to its current position. - - - - - - -ProcessInputEvents() - -ProcessInputEvents() is a DDX function that is called from the X -server's main dispatch loop when new events are available in the input -event queue. It typically processes the enqueued events, and updates -the cursor/pointer position. It may also do other DDX-specific event -processing. - - -Enqueued events are processed by mieqProcessInputEvents() and passed -to the DIX layer for transmission to clients: - - - -mieqProcessInputEvents() - -This function processes each event in the -event queue, and passes it to the device's input processing function. -The DIX layer provides default functions to do this processing, and they -handle the task of getting the events passed back to the relevant -clients. - - -miPointerUpdate() - -This function resynchronized the cursor position -with the new pointer position. It also takes care of moving the cursor -between screens when needed in multi-head configurations. - - - - - - - -DisableDevice() - -DisableDevice is a DIX function that removes an input device from the -list of enabled devices. The result of this is that the device no -longer generates input events. The device's data structures are kept in -place, and disabling a device like this can be reversed by calling -EnableDevice(). DisableDevice() may be called from the DDX when it is -desirable to do so (e.g., the XFree86 server does this when VT -switching). Except for special cases, this is not normally called for -core input devices. - - -DisableDevice() calls the device's callback function with -DEVICE_OFF: - - - -(*dev->deviceProc)(dev, DEVICE_OFF) - -This typically closes the -relevant physical device, and when appropriate, unregisters the device's -file descriptor (or equivalent) as a valid input source. - - - - -DisableDevice() then removes the device handle from the X server's -global list of enabled devices. - - - - - -CloseDevice() - -CloseDevice is a DIX function that removes an input device from the -list of available devices. It disables input from the device and frees -all data structures associated with the device. This function is -usually called from CloseDownDevices(), which is called from main() at -the end of each server generation to close all input devices. - - -CloseDevice() calls the device's callback function with -DEVICE_CLOSE: - - - -(*dev->deviceProc)(dev, DEVICE_CLOSE) - -This typically closes the -relevant physical device, and when appropriate, unregisters the device's -file descriptor (or equivalent) as a valid input source. If any device -specific data structures were allocated when the device was initialized, -they are freed here. - - - - -CloseDevice() then frees the data structures that were allocated -for the device when it was registered/initialized. - - - - - -LegalModifier() - -LegalModifier() is a required DDX function that can be used to -restrict which keys may be modifier keys. This seems to be present for -historical reasons, so this function should simply return TRUE -unconditionally. - - - - - - -Output handling - -The following sections describe the main functions required to -initialize, use and close the output device(s) for each screen in the X -server. - - - -InitOutput() - -This DDX function is called near the start of each server generation -from the X server's main() function. InitOutput()'s main purpose is to -initialize each screen and fill in the global screenInfo structure for -each screen. It is passed three arguments: a pointer to the screenInfo -struct, which it is to initialize, and argc and argv from main(), which -can be used to determine additional configuration information. - - -The primary tasks for this function are outlined below: - - - - Parse configuration info: The first task of InitOutput() - is to parses any configuration information from the configuration - file. In addition to the XF86Config file, other configuration - information can be taken from the command line. The command line - options can be gathered either in InitOutput() or earlier in the - ddxProcessArgument() function, which is called by - ProcessCommandLine(). The configuration information determines the - characteristics of the screen(s). For example, in the XFree86 X - server, the XF86Config file specifies the monitor information, the - screen resolution, the graphics devices and slots in which they are - located, and, for Xinerama, the screens' layout. - - - - - Initialize screen info: The next task is to initialize - the screen-dependent internal data structures. For example, part of - what the XFree86 X server does is to allocate its screen and pixmap - private indices, probe for graphics devices, compare the probed - devices to the ones listed in the XF86Config file, and add the ones that - match to the internal xf86Screens[] structure. - - - - - Set pixmap formats: The next task is to initialize the - screenInfo's image byte order, bitmap bit order and bitmap scanline - unit/pad. The screenInfo's pixmap format's depth, bits per pixel - and scanline padding is also initialized at this stage. - - - - - Unify screen info: An optional task that might be done at - this stage is to compare all of the information from the various - screens and determines if they are compatible (i.e., if the set of - screens can be unified into a single desktop). This task has - potential to be useful to the DMX front-end server, if Xinerama's - PanoramiXConsolidate() function is not sufficient. - - - - - -Once these tasks are complete, the valid screens are known and each -of these screens can be initialized by calling AddScreen(). - - - - -AddScreen() - -This DIX function is called from InitOutput(), in the DDX layer, to -add each new screen to the screenInfo structure. The DDX screen -initialization function and command line arguments (i.e., argc and argv) -are passed to it as arguments. - - -This function first allocates a new Screen structure and any privates -that are required. It then initializes some of the fields in the Screen -struct and sets up the pixmap padding information. Finally, it calls -the DDX screen initialization function ScreenInit(), which is described -below. It returns the number of the screen that were just added, or -1 -if there is insufficient memory to add the screen or if the DDX screen -initialization fails. - - - - -ScreenInit() - -This DDX function initializes the rest of the Screen structure with -either generic or screen-specific functions (as necessary). It also -fills in various screen attributes (e.g., width and height in -millimeters, black and white pixel values). - - -The screen init function usually calls several functions to perform -certain screen initialization functions. They are described below: - - - -{mi,*fb}ScreenInit() - -The DDX layer's ScreenInit() function usually -calls another layer's ScreenInit() function (e.g., miScreenInit() or -fbScreenInit()) to initialize the fallbacks that the DDX driver does not -specifically handle. - - -After calling another layer's ScreenInit() function, any -screen-specific functions either wrap or replace the other layer's -function pointers. If a function is to be wrapped, each of the old -function pointers from the other layer are stored in a screen private -area. Common functions to wrap are CloseScreen() and SaveScreen(). - - - -miInitializeBackingStore() - -This MI function initializes the -screen's backing storage functions, which are used to save areas of -windows that are currently covered by other windows. - - - -miDCInitialize() - -This MI function initializes the MI cursor -display structures and function pointers. If a hardware cursor is used, -the DDX layer's ScreenInit() function will wrap additional screen and -the MI cursor display function pointers. - - - - -Another common task for ScreenInit() function is to initialize the -output device state. For example, in the XFree86 X server, the -ScreenInit() function saves the original state of the video card and -then initializes the video mode of the graphics device. - - - - -CloseScreen() - -This function restores any wrapped screen functions (and in -particular the wrapped CloseScreen() function) and restores the state of -the output device to its original state. It should also free any -private data it created during the screen initialization. - - - - -GC operations - -When the X server is requested to render drawing primitives, it does -so by calling drawing functions through the graphics context's operation -function pointer table (i.e., the GCOps functions). These functions -render the basic graphics operations such as drawing rectangles, lines, -text or copying pixmaps. Default routines are provided either by the MI -layer, which draws indirectly through a simple span interface, or by the -framebuffer layers (e.g., CFB, MFB, FB), which draw directly to a -linearly mapped frame buffer. - - -To take advantage of special hardware on the graphics device, -specific GCOps functions can be replaced by device specific code. -However, many times the graphics devices can handle only a subset of the -possible states of the GC, so during graphics context validation, -appropriate routines are selected based on the state and capabilities of -the hardware. For example, some graphics hardware can accelerate single -pixel width lines with certain dash patterns. Thus, for dash patterns -that are not supported by hardware or for width 2 or greater lines, the -default routine is chosen during GC validation. - - -Note that some pointers to functions that draw to the screen are -stored in the Screen structure. They include GetImage(), GetSpans(), -CopyWindow() and RestoreAreas(). - - - - -Xnest - -The Xnest X server is a special proxy X server that relays the X -protocol requests that it receives to a ``real'' X server that then -processes the requests and displays the results, if applicable. To the X -applications, Xnest appears as if it is a regular X server. However, -Xnest is both server to the X application and client of the real X -server, which will actually handle the requests. - - -The Xnest server implements all of the standard input and output -initialization steps outlined above. - - - - -InitOutput() - -Xnest takes its configuration information from -command line arguments via ddxProcessArguments(). This information -includes the real X server display to connect to, its default visual -class, the screen depth, the Xnest window's geometry, etc. Xnest then -connects to the real X server and gathers visual, colormap, depth and -pixmap information about that server's display, creates a window on that -server, which will be used as the root window for Xnest. - - -Next, Xnest initializes its internal data structures and uses the -data from the real X server's pixmaps to initialize its own pixmap -formats. Finally, it calls AddScreen(xnestOpenScreen, argc, argv) to -initialize each of its screens. - - - -ScreenInit() - -Xnest's ScreenInit() function is called -xnestOpenScreen(). This function initializes its screen's depth and -visual information, and then calls miScreenInit() to set up the default -screen functions. It then calls miInitializeBackingStore() and -miDCInitialize() to initialize backing store and the software cursor. -Finally, it replaces many of the screen functions with its own -functions that repackage and send the requests to the real X server to -which Xnest is attached. - - - -CloseScreen() - -This function frees its internal data structure -allocations. Since it replaces instead of wrapping screen functions, -there are no function pointers to unwrap. This can potentially lead to -problems during server regeneration. - - - -GC operations - -The GC operations in Xnest are very simple since -they leave all of the drawing to the real X server to which Xnest is -attached. Each of the GCOps takes the request and sends it to the -real X server using standard Xlib calls. For example, the X -application issues a XDrawLines() call. This function turns into a -protocol request to Xnest, which calls the xnestPolylines() function -through Xnest's GCOps function pointer table. The xnestPolylines() -function is only a single line, which calls XDrawLines() using the same -arguments that were passed into it. Other GCOps functions are very -similar. Two exceptions to the simple GCOps functions described above -are the image functions and the BLT operations. - - -The image functions, GetImage() and PutImage(), must use a temporary -image to hold the image to be put of the image that was just grabbed -from the screen while it is in transit to the real X server or the -client. When the image has been transmitted, the temporary image is -destroyed. - - -The BLT operations, CopyArea() and CopyPlane(), handle not only the -copy function, which is the same as the simple cases described above, -but also the graphics exposures that result when the GC's graphics -exposure bit is set to True. Graphics exposures are handled in a helper -function, xnestBitBlitHelper(). This function collects the exposure -events from the real X server and, if any resulting in regions being -exposed, then those regions are passed back to the MI layer so that it -can generate exposure events for the X application. - - - - -The Xnest server takes its input from the X server to which it is -connected. When the mouse is in the Xnest server's window, keyboard and -mouse events are received by the Xnest server, repackaged and sent back -to any client that requests those events. - - - - -Shadow framebuffer - -The most common type of framebuffer is a linear array memory that -maps to the video memory on the graphics device. However, accessing -that video memory over an I/O bus (e.g., ISA or PCI) can be slow. The -shadow framebuffer layer allows the developer to keep the entire -framebuffer in main memory and copy it back to video memory at regular -intervals. It also has been extended to handle planar video memory and -rotated framebuffers. - - -There are two main entry points to the shadow framebuffer code: - - - -shadowAlloc(width, height, bpp) - -This function allocates the in -memory copy of the framebuffer of size width*height*bpp. It returns a -pointer to that memory, which will be used by the framebuffer -ScreenInit() code during the screen's initialization. - - - -shadowInit(pScreen, updateProc, windowProc) - -This function -initializes the shadow framebuffer layer. It wraps several screen -drawing functions, and registers a block handler that will update the -screen. The updateProc is a function that will copy the damaged regions -to the screen, and the windowProc is a function that is used when the -entire linear video memory range cannot be accessed simultaneously so -that only a window into that memory is available (e.g., when using the -VGA aperture). - - - - -The shadow framebuffer code keeps track of the damaged area of each -screen by calculating the bounding box of all drawing operations that -have occurred since the last screen update. Then, when the block handler -is next called, only the damaged portion of the screen is updated. - - -Note that since the shadow framebuffer is kept in main memory, all -drawing operations are performed by the CPU and, thus, no accelerated -hardware drawing operations are possible. - - - - - - -Xinerama - -Xinerama is an X extension that allows multiple physical screens -controlled by a single X server to appear as a single screen. Although -the extension allows clients to find the physical screen layout via -extension requests, it is completely transparent to clients at the core -X11 protocol level. The original public implementation of Xinerama came -from Digital/Compaq. XFree86 rewrote it, filling in some missing pieces -and improving both X11 core protocol compliance and performance. The -Xinerama extension will be passing through X.Org's standardization -process in the near future, and the sample implementation will be based -on this rewritten version. - - -The current implementation of Xinerama is based primarily in the DIX -(device independent) and MI (machine independent) layers of the X -server. With few exceptions the DDX layers do not need any changes to -support Xinerama. X server extensions often do need modifications to -provide full Xinerama functionality. - - -The following is a code-level description of how Xinerama functions. - - -Note: Because the Xinerama extension was originally called the -PanoramiX extension, many of the Xinerama functions still have the -PanoramiX prefix. - - - - -PanoramiXExtensionInit() - - PanoramiXExtensionInit() is a - device-independent extension function that is called at the start of - each server generation from InitExtensions(), which is called from - the X server's main() function after all output devices have been - initialized, but before any input devices have been initialized. - - - PanoramiXNumScreens is set to the number of physical screens. If - only one physical screen is present, the extension is disabled, and - PanoramiXExtensionInit() returns without doing anything else. - - - The Xinerama extension is registered by calling AddExtension(). - - - GC and Screen private - indexes are allocated, and both GC and Screen private areas are - allocated for each physical screen. These hold Xinerama-specific - per-GC and per-Screen data. Each screen's CreateGC and CloseScreen - functions are wrapped by XineramaCreateGC() and - XineramaCloseScreen() respectively. Some new resource classes are - created for Xinerama drawables and GCs, and resource types for - Xinerama windows, pixmaps and colormaps. - - - A region (PanoramiXScreenRegion) is - initialized to be the union of the screen regions. - The relative positioning information for the - physical screens is taken from the ScreenRec x and y members, which - the DDX layer must initialize in InitOutput(). The bounds of the - combined screen is also calculated (PanoramiXPixWidth and - PanoramiXPixHeight). - - - The DIX layer has a list of function pointers - (ProcVector[]) that - holds the entry points for the functions that process core protocol - requests. The requests that Xinerama must intercept and break up - into physical screen-specific requests are wrapped. The original - set is copied to SavedProcVector[]. The types of requests - intercepted are Window requests, GC requests, colormap requests, - drawing requests, and some geometry-related requests. This wrapping - allows the bulk of the protocol request processing to be handled - transparently to the DIX layer. Some operations cannot be dealt with - in this way and are handled with Xinerama-specific code within the - DIX layer. - - - - -PanoramiXConsolidate() - - PanoramiXConsolidate() is a - device-independent extension function that is called directly from - the X server's main() function after extensions and input/output - devices have been initialized, and before the root windows are - defined and initialized. - - - This function finds the set of depths (PanoramiXDepths[]) and - visuals (PanoramiXVisuals[]) - common to all of the physical screens. - PanoramiXNumDepths is set to the number of common depths, and - PanoramiXNumVisuals is set to the number of common visuals. - Resources are created for the single root window and the default - colormap. Each of these resources has per-physical screen entries. - - - - -PanoramiXCreateConnectionBlock() - - PanoramiXConsolidate() is a - device-independent extension function that is called directly from - the X server's main() function after the per-physical screen root - windows are created. It is called instead of the standard DIX - CreateConnectionBlock() function. If this function returns FALSE, - the X server exits with a fatal error. This function will return - FALSE if no common depths were found in PanoramiXConsolidate(). - With no common depths, Xinerama mode is not possible. - - - The connection block holds the information that clients get when - they open a connection to the X server. It includes information - such as the supported pixmap formats, number of screens and the - sizes, depths, visuals, default colormap information, etc, for each - of the screens (much of information that xdpyinfo shows). The - connection block is initialized with the combined single screen - values that were calculated in the above two functions. - - - The Xinerama extension allows the registration of connection - block callback functions. The purpose of these is to allow other - extensions to do processing at this point. These callbacks can be - registered by calling XineramaRegisterConnectionBlockCallback() from - the other extension's ExtensionInit() function. Each registered - connection block callback is called at the end of - PanoramiXCreateConnectionBlock(). - - - - - -Xinerama-specific changes to the DIX code - -There are a few types of Xinerama-specific changes within the DIX -code. The main ones are described here. - - -Functions that deal with colormap or GC -related operations outside of -the intercepted protocol requests have a test added to only do the -processing for screen numbers > 0. This is because they are handled for -the single Xinerama screen and the processing is done once for screen 0. - - -The handling of motion events does some coordinate translation between -the physical screen's origin and screen zero's origin. Also, motion -events must be reported relative to the composite screen origin rather -than the physical screen origins. - - -There is some special handling for cursor, window and event processing -that cannot (either not at all or not conveniently) be done via the -intercepted protocol requests. A particular case is the handling of -pointers moving between physical screens. - - - - -Xinerama-specific changes to the MI code - -The only Xinerama-specific change to the MI code is in miSendExposures() -to handle the coordinate (and window ID) translation for expose events. - - - - -Intercepted DIX core requests - -Xinerama breaks up drawing requests for dispatch to each physical -screen. It also breaks up windows into pieces for each physical screen. -GCs are translated into per-screen GCs. Colormaps are replicated on -each physical screen. The functions handling the intercepted requests -take care of breaking the requests and repackaging them so that they can -be passed to the standard request handling functions for each screen in -turn. In addition, and to aid the repackaging, the information from -many of the intercepted requests is used to keep up to date the -necessary state information for the single composite screen. Requests -(usually those with replies) that can be satisfied completely from this -stored state information do not call the standard request handling -functions. - - - - - - - - - - - -Development Results - -In this section the results of each phase of development are -discussed. This development took place between approximately June 2001 -and July 2003. - - - -Phase I - -The initial development phase dealt with the basic implementation -including the bootstrap code, which used the shadow framebuffer, and the -unoptimized implementation, based on an Xnest-style implementation. - - - -Scope - -The goal of Phase I is to provide fundamental functionality that can -act as a foundation for ongoing work: - - - Develop the proxy X server - - - The proxy X server will operate on the X11 protocol and - relay requests as necessary to correctly perform the request. - - - Work will be based on the existing work for Xinerama and - Xnest. - - - Input events and windowing operations are handled in the - proxy server and rendering requests are repackaged and sent to - each of the back-end servers for display. - - - The multiple screen layout (including support for - overlapping screens) will be user configurable via a - configuration file or through the configuration tool. - - - - - Develop graphical configuration tool - - - There will be potentially a large number of X servers to - configure into a single display. The tool will allow the user - to specify which servers are involved in the configuration and - how they should be laid out. - - - - - Pass the X Test Suite - - - The X Test Suite covers the basic X11 operations. All - tests known to succeed must correctly operate in the distributed - X environment. - - - - - - - -For this phase, the back-end X servers are assumed to be unmodified X -servers that do not support any DMX-related protocol extensions; future -optimization pathways are considered, but are not implemented; and the -configuration tool is assumed to rely only on libraries in the X source -tree (e.g., Xt). - - - - -Results - -The proxy X server, Xdmx, was developed to distribute X11 protocol -requests to the set of back-end X servers. It opens a window on each -back-end server, which represents the part of the front-end's root -window that is visible on that screen. It mirrors window, pixmap and -other state in each back-end server. Drawing requests are sent to -either windows or pixmaps on each back-end server. This code is based -on Xnest and uses the existing Xinerama extension. - - -Input events can be taken from (1) devices attached to the back-end -server, (2) core devices attached directly to the Xdmx server, or (3) -from a ``console'' window on another X server. Events for these devices -are gathered, processed and delivered to clients attached to the Xdmx -server. - - -An intuitive configuration format was developed to help the user -easily configure the multiple back-end X servers. It was defined (see -grammar in Xdmx man page) and a parser was implemented that is used by -the Xdmx server and by a standalone xdmxconfig utility. The parsing -support was implemented such that it can be easily factored out of the X -source tree for use with other tools (e.g., vdl). Support for -converting legacy vdl-format configuration files to the DMX format is -provided by the vdltodmx utility. - - -Originally, the configuration file was going to be a subsection of -XFree86's XF86Config file, but that was not possible since Xdmx is a -completely separate X server. Thus, a separate config file format was -developed. In addition, a graphical configuration -tool, xdmxconfig, was developed to allow the user to create and arrange -the screens in the configuration file. The -configfile and -config -command-line options can be used to start Xdmx using a configuration -file. - - -An extension that enables remote input testing is required for the X -Test Suite to function. During this phase, this extension (XTEST) was -implemented in the Xdmx server. The results from running the X Test -Suite are described in detail below. - - - - -X Test Suite - - - Introduction - - The X Test Suite contains tests that verify Xlib functions - operate correctly. The test suite is designed to run on a - single X server; however, since X applications will not be - able to tell the difference between the DMX server and a - standard X server, the X Test Suite should also run on the - DMX server. - - - The Xdmx server was tested with the X Test Suite, and the - existing failures are noted in this section. To put these - results in perspective, we first discuss expected X Test - failures and how errors in underlying systems can impact - Xdmx test results. - - - - - Expected Failures for a Single Head - - A correctly implemented X server with a single screen is - expected to fail certain X Test tests. The following - well-known errors occur because of rounding error in the X - server code: - -XDrawArc: Tests 42, 63, 66, 73 -XDrawArcs: Tests 45, 66, 69, 76 - - - - The following failures occur because of the high-level X - server implementation: - -XLoadQueryFont: Test 1 -XListFontsWithInfo: Tests 3, 4 -XQueryFont: Tests 1, 2 - - - - The following test fails when running the X server as root - under Linux because of the way directory modes are - interpreted: - -XWriteBitmapFile: Test 3 - - - - Depending on the video card used for the back-end, other - failures may also occur because of bugs in the low-level - driver implementation. Over time, failures of this kind - are usually fixed by XFree86, but will show up in Xdmx - testing until then. - - - - - Expected Failures for Xinerama - - Xinerama fails several X Test Suite tests because of - design decisions made for the current implementation of - Xinerama. Over time, many of these errors will be - corrected by XFree86 and the group working on a new - Xinerama implementation. Therefore, Xdmx will also share - X Suite Test failures with Xinerama. - - - - We may be able to fix or work-around some of these - failures at the Xdmx level, but this will require - additional exploration that was not part of Phase I. - - - - Xinerama is constantly improving, and the list of - Xinerama-related failures depends on XFree86 version and - the underlying graphics hardware. We tested with a - variety of hardware, including nVidia, S3, ATI Radeon, - and Matrox G400 (in dual-head mode). The list below - includes only those failures that appear to be from the - Xinerama layer, and does not include failures listed in - the previous section, or failures that appear to be from - the low-level graphics driver itself: - - - - These failures were noted with multiple Xinerama - configurations: - -XCopyPlane: Tests 13, 22, 31 (well-known Xinerama implementation issue) -XSetFontPath: Test 4 -XGetDefault: Test 5 -XMatchVisualInfo: Test 1 - - - - These failures were noted only when using one dual-head - video card with a 4.2.99.x XFree86 server: - -XListPixmapFormats: Test 1 -XDrawRectangles: Test 45 - - - - These failures were noted only when using two video cards - from different vendors with a 4.1.99.x XFree86 server: - -XChangeWindowAttributes: Test 32 -XCreateWindow: Test 30 -XDrawLine: Test 22 -XFillArc: Test 22 -XChangeKeyboardControl: Tests 9, 10 -XRebindKeysym: Test 1 - - - - - - Additional Failures from Xdmx - - - When running Xdmx, no unexpected failures were noted. - Since the Xdmx server is based on Xinerama, we expect to - have most of the Xinerama failures present in the Xdmx - server. Similarly, since the Xdmx server must rely on the - low-level device drivers on each back-end server, we also - expect that Xdmx will exhibit most of the back-end - failures. Here is a summary: - -XListPixmapFormats: Test 1 (configuration dependent) -XChangeWindowAttributes: Test 32 -XCreateWindow: Test 30 -XCopyPlane: Test 13, 22, 31 -XSetFontPath: Test 4 -XGetDefault: Test 5 (configuration dependent) -XMatchVisualInfo: Test 1 -XRebindKeysym: Test 1 (configuration dependent) - - - - Note that this list is shorter than the combined list for - Xinerama because Xdmx uses different code paths to perform - some Xinerama operations. Further, some Xinerama failures - have been fixed in the XFree86 4.2.99.x CVS repository. - - - - - Summary and Future Work - - - Running the X Test Suite on Xdmx does not produce any - failures that cannot be accounted for by the underlying - Xinerama subsystem used by the front-end or by the - low-level device-driver code running on the back-end X - servers. The Xdmx server therefore is as ``correct'' as - possible with respect to the standard set of X Test Suite - tests. - - - - During the following phases, we will continue to verify - Xdmx correctness using the X Test Suite. We may also use - other tests suites or write additional tests that run - under the X Test Suite that specifically verify the - expected behavior of DMX. - - - - - -Fonts - -In Phase I, fonts are handled directly by both the front-end and the -back-end servers, which is required since we must treat each back-end -server during this phase as a ``black box''. What this requires is that -the front- and back-end servers must share the exact same font -path. There are two ways to help make sure that all servers share the -same font path: - - - - First, each server can be configured to use the same font - server. The font server, xfs, can be configured to serve fonts to - multiple X servers via TCP. - - - - Second, each server can be configured to use the same font - path and either those font paths can be copied to each back-end - machine or they can be mounted (e.g., via NFS) on each back-end - machine. - - - - -One additional concern is that a client program can set its own font -path, and if it does so, then that font path must be available on each -back-end machine. - - -The -fontpath command line option was added to allow users to -initialize the font path of the front end server. This font path is -propagated to each back-end server when the default font is loaded. If -there are any problems, an error message is printed, which will describe -the problem and list the current font path. For more information about -setting the font path, see the -fontpath option description in the man -page. - - - - -Performance - -Phase I of development was not intended to optimize performance. Its -focus was on completely and correctly handling the base X11 protocol in -the Xdmx server. However, several insights were gained during Phase I, -which are listed here for reference during the next phase of -development. - - - - - Calls to XSync() can slow down rendering since it requires a - complete round trip to and from a back-end server. This is - especially problematic when communicating over long haul networks. - - - - Sending drawing requests to only the screens that they overlap - should improve performance. - - - - - -Pixmaps - -Pixmaps were originally expected to be handled entirely in the -front-end X server; however, it was found that this overly complicated -the rendering code and would have required sending potentially large -images to each back server that required them when copying from pixmap -to screen. Thus, pixmap state is mirrored in the back-end server just -as it is with regular window state. With this implementation, the same -rendering code that draws to windows can be used to draw to pixmaps on -the back-end server, and no large image transfers are required to copy -from pixmap to window. - - - - - - - - -Phase II - -The second phase of development concentrates on performance -optimizations. These optimizations are documented here, with -x11perf data to show how the optimizations improve performance. - - -All benchmarks were performed by running Xdmx on a dual processor -1.4GHz AMD Athlon machine with 1GB of RAM connecting over 100baseT to -two single-processor 1GHz Pentium III machines with 256MB of RAM and ATI -Rage 128 (RF) video cards. The front end was running Linux -2.4.20-pre1-ac1 and the back ends were running Linux 2.4.7-10 and -version 4.2.99.1 of XFree86 pulled from the XFree86 CVS repository on -August 7, 2002. All systems were running Red Hat Linux 7.2. - - - -Moving from XFree86 4.1.99.1 to 4.2.0.0 - -For phase II, the working source tree was moved to the branch tagged -with dmx-1-0-branch and was updated from version 4.1.99.1 (20 August -2001) of the XFree86 sources to version 4.2.0.0 (18 January 2002). -After this update, the following tests were noted to be more than 10% -faster: - -1.13 Fill 300x300 opaque stippled trapezoid (161x145 stipple) -1.16 Fill 1x1 tiled trapezoid (161x145 tile) -1.13 Fill 10x10 tiled trapezoid (161x145 tile) -1.17 Fill 100x100 tiled trapezoid (161x145 tile) -1.16 Fill 1x1 tiled trapezoid (216x208 tile) -1.20 Fill 10x10 tiled trapezoid (216x208 tile) -1.15 Fill 100x100 tiled trapezoid (216x208 tile) -1.37 Circulate Unmapped window (200 kids) - -And the following tests were noted to be more than 10% slower: - -0.88 Unmap window via parent (25 kids) -0.75 Circulate Unmapped window (4 kids) -0.79 Circulate Unmapped window (16 kids) -0.80 Circulate Unmapped window (25 kids) -0.82 Circulate Unmapped window (50 kids) -0.85 Circulate Unmapped window (75 kids) - - - -These changes were not caused by any changes in the DMX system, and -may point to changes in the XFree86 tree or to tests that have more -"jitter" than most other x11perf tests. - - - - -Global changes - -During the development of the Phase II DMX server, several global -changes were made. These changes were also compared with the Phase I -server. The following tests were noted to be more than 10% faster: - -1.13 Fill 300x300 opaque stippled trapezoid (161x145 stipple) -1.15 Fill 1x1 tiled trapezoid (161x145 tile) -1.13 Fill 10x10 tiled trapezoid (161x145 tile) -1.17 Fill 100x100 tiled trapezoid (161x145 tile) -1.16 Fill 1x1 tiled trapezoid (216x208 tile) -1.19 Fill 10x10 tiled trapezoid (216x208 tile) -1.15 Fill 100x100 tiled trapezoid (216x208 tile) -1.15 Circulate Unmapped window (4 kids) - - - -The following tests were noted to be more than 10% slower: - -0.69 Scroll 10x10 pixels -0.68 Scroll 100x100 pixels -0.68 Copy 10x10 from window to window -0.68 Copy 100x100 from window to window -0.76 Circulate Unmapped window (75 kids) -0.83 Circulate Unmapped window (100 kids) - - - -For the remainder of this analysis, the baseline of comparison will -be the Phase II deliverable with all optimizations disabled (unless -otherwise noted). This will highlight how the optimizations in -isolation impact performance. - - - - -XSync() Batching - -During the Phase I implementation, XSync() was called after every -protocol request made by the DMX server. This provided the DMX server -with an interactive feel, but defeated X11's protocol buffering system -and introduced round-trip wire latency into every operation. During -Phase II, DMX was changed so that protocol requests are no longer -followed by calls to XSync(). Instead, the need for an XSync() is -noted, and XSync() calls are only made every 100mS or when the DMX -server specifically needs to make a call to guarantee interactivity. -With this new system, X11 buffers protocol as much as possible during a -100mS interval, and many unnecessary XSync() calls are avoided. - - -Out of more than 300 x11perf tests, 8 tests became more than 100 -times faster, with 68 more than 50X faster, 114 more than 10X faster, -and 181 more than 2X faster. See table below for summary. - - -The following tests were noted to be more than 10% slower with -XSync() batching on: - -0.88 500x500 tiled rectangle (161x145 tile) -0.89 Copy 500x500 from window to window - - - - - -Offscreen Optimization - -Windows span one or more of the back-end servers' screens; however, -during Phase I development, windows were created on every back-end -server and every rendering request was sent to every window regardless -of whether or not that window was visible. With the offscreen -optimization, the DMX server tracks when a window is completely off of a -back-end server's screen and, in that case, it does not send rendering -requests to those back-end windows. This optimization saves bandwidth -between the front and back-end servers, and it reduces the number of -XSync() calls. The performance tests were run on a DMX system with only -two back-end servers. Greater performance gains will be had as the -number of back-end servers increases. - - -Out of more than 300 x11perf tests, 3 tests were at least twice as -fast, and 146 tests were at least 10% faster. Two tests were more than -10% slower with the offscreen optimization: - -0.88 Hide/expose window via popup (4 kids) -0.89 Resize unmapped window (75 kids) - - - - - -Lazy Window Creation Optimization - -As mentioned above, during Phase I, windows were created on every -back-end server even if they were not visible on that back-end. With -the lazy window creation optimization, the DMX server does not create -windows on a back-end server until they are either visible or they -become the parents of a visible window. This optimization builds on the -offscreen optimization (described above) and requires it to be enabled. - - -The lazy window creation optimization works by creating the window -data structures in the front-end server when a client creates a window, -but delays creation of the window on the back-end server(s). A private -window structure in the DMX server saves the relevant window data and -tracks changes to the window's attributes and stacking order for later -use. The only times a window is created on a back-end server are (1) -when it is mapped and is at least partially overlapping the back-end -server's screen (tracked by the offscreen optimization), or (2) when the -window becomes the parent of a previously visible window. The first -case occurs when a window is mapped or when a visible window is copied, -moved or resized and now overlaps the back-end server's screen. The -second case occurs when starting a window manager after having created -windows to which the window manager needs to add decorations. - - -When either case occurs, a window on the back-end server is created -using the data saved in the DMX server's window private data structure. -The stacking order is then adjusted to correctly place the window on the -back-end and lastly the window is mapped. From this time forward, the -window is handled exactly as if the window had been created at the time -of the client's request. - - -Note that when a window is no longer visible on a back-end server's -screen (e.g., it is moved offscreen), the window is not destroyed; -rather, it is kept and reused later if the window once again becomes -visible on the back-end server's screen. Originally with this -optimization, destroying windows was implemented but was later rejected -because it increased bandwidth when windows were opaquely moved or -resized, which is common in many window managers. - - -The performance tests were run on a DMX system with only two back-end -servers. Greater performance gains will be had as the number of -back-end servers increases. - - -This optimization improved the following x11perf tests by more -than 10%: - -1.10 500x500 rectangle outline -1.12 Fill 100x100 stippled trapezoid (161x145 stipple) -1.20 Circulate Unmapped window (50 kids) -1.19 Circulate Unmapped window (75 kids) - - - - - -Subdividing Rendering Primitives - -X11 imaging requests transfer significant data between the client and -the X server. During Phase I, the DMX server would then transfer the -image data to each back-end server. Even with the offscreen -optimization (above), these requests still required transferring -significant data to each back-end server that contained a visible -portion of the window. For example, if the client uses XPutImage() to -copy an image to a window that overlaps the entire DMX screen, then the -entire image is copied by the DMX server to every back-end server. - - -To reduce the amount of data transferred between the DMX server and -the back-end servers when XPutImage() is called, the image data is -subdivided and only the data that will be visible on a back-end server's -screen is sent to that back-end server. Xinerama already implements a -subdivision algorithm for XGetImage() and no further optimization was -needed. - - -Other rendering primitives were analyzed, but the time required to -subdivide these primitives was a significant proportion of the time -required to send the entire rendering request to the back-end server, so -this optimization was rejected for the other rendering primitives. - - -Again, the performance tests were run on a DMX system with only two -back-end servers. Greater performance gains will be had as the number -of back-end servers increases. - - -This optimization improved the following x11perf tests by more -than 10%: - -1.12 Fill 100x100 stippled trapezoid (161x145 stipple) -1.26 PutImage 10x10 square -1.83 PutImage 100x100 square -1.91 PutImage 500x500 square -1.40 PutImage XY 10x10 square -1.48 PutImage XY 100x100 square -1.50 PutImage XY 500x500 square -1.45 Circulate Unmapped window (75 kids) -1.74 Circulate Unmapped window (100 kids) - - - -The following test was noted to be more than 10% slower with this -optimization: - -0.88 10-pixel fill chord partial circle - - - - - -Summary of x11perf Data - -With all of the optimizations on, 53 x11perf tests are more than -100X faster than the unoptimized Phase II deliverable, with 69 more than -50X faster, 73 more than 10X faster, and 199 more than twice as fast. -No tests were more than 10% slower than the unoptimized Phase II -deliverable. (Compared with the Phase I deliverable, only Circulate -Unmapped window (100 kids) was more than 10% slower than the Phase II -deliverable. As noted above, this test seems to have wider variability -than other x11perf tests.) - - -The following table summarizes relative x11perf test changes for -all optimizations individually and collectively. Note that some of the -optimizations have a synergistic effect when used together. - - -1: XSync() batching only -2: Off screen optimizations only -3: Window optimizations only -4: Subdivprims only -5: All optimizations - - 1 2 3 4 5 Operation ------- ---- ---- ---- ------ --------- - 2.14 1.85 1.00 1.00 4.13 Dot - 1.67 1.80 1.00 1.00 3.31 1x1 rectangle - 2.38 1.43 1.00 1.00 2.44 10x10 rectangle - 1.00 1.00 0.92 0.98 1.00 100x100 rectangle - 1.00 1.00 1.00 1.00 1.00 500x500 rectangle - 1.83 1.85 1.05 1.06 3.54 1x1 stippled rectangle (8x8 stipple) - 2.43 1.43 1.00 1.00 2.41 10x10 stippled rectangle (8x8 stipple) - 0.98 1.00 1.00 1.00 1.00 100x100 stippled rectangle (8x8 stipple) - 1.00 1.00 1.00 1.00 0.98 500x500 stippled rectangle (8x8 stipple) - 1.75 1.75 1.00 1.00 3.40 1x1 opaque stippled rectangle (8x8 stipple) - 2.38 1.42 1.00 1.00 2.34 10x10 opaque stippled rectangle (8x8 stipple) - 1.00 1.00 0.97 0.97 1.00 100x100 opaque stippled rectangle (8x8 stipple) - 1.00 1.00 1.00 1.00 0.99 500x500 opaque stippled rectangle (8x8 stipple) - 1.82 1.82 1.04 1.04 3.56 1x1 tiled rectangle (4x4 tile) - 2.33 1.42 1.00 1.00 2.37 10x10 tiled rectangle (4x4 tile) - 1.00 0.92 1.00 1.00 1.00 100x100 tiled rectangle (4x4 tile) - 1.00 1.00 1.00 1.00 1.00 500x500 tiled rectangle (4x4 tile) - 1.94 1.62 1.00 1.00 3.66 1x1 stippled rectangle (17x15 stipple) - 1.74 1.28 1.00 1.00 1.73 10x10 stippled rectangle (17x15 stipple) - 1.00 1.00 1.00 0.89 0.98 100x100 stippled rectangle (17x15 stipple) - 1.00 1.00 1.00 1.00 0.98 500x500 stippled rectangle (17x15 stipple) - 1.94 1.62 1.00 1.00 3.67 1x1 opaque stippled rectangle (17x15 stipple) - 1.69 1.26 1.00 1.00 1.66 10x10 opaque stippled rectangle (17x15 stipple) - 1.00 0.95 1.00 1.00 1.00 100x100 opaque stippled rectangle (17x15 stipple) - 1.00 1.00 1.00 1.00 0.97 500x500 opaque stippled rectangle (17x15 stipple) - 1.93 1.61 0.99 0.99 3.69 1x1 tiled rectangle (17x15 tile) - 1.73 1.27 1.00 1.00 1.72 10x10 tiled rectangle (17x15 tile) - 1.00 1.00 1.00 1.00 0.98 100x100 tiled rectangle (17x15 tile) - 1.00 1.00 0.97 0.97 1.00 500x500 tiled rectangle (17x15 tile) - 1.95 1.63 1.00 1.00 3.83 1x1 stippled rectangle (161x145 stipple) - 1.80 1.30 1.00 1.00 1.83 10x10 stippled rectangle (161x145 stipple) - 0.97 1.00 1.00 1.00 1.01 100x100 stippled rectangle (161x145 stipple) - 1.00 1.00 1.00 1.00 0.98 500x500 stippled rectangle (161x145 stipple) - 1.95 1.63 1.00 1.00 3.56 1x1 opaque stippled rectangle (161x145 stipple) - 1.65 1.25 1.00 1.00 1.68 10x10 opaque stippled rectangle (161x145 stipple) - 1.00 1.00 1.00 1.00 1.01 100x100 opaque stippled rectangle (161x145... - 1.00 1.00 1.00 1.00 0.97 500x500 opaque stippled rectangle (161x145... - 1.95 1.63 0.98 0.99 3.80 1x1 tiled rectangle (161x145 tile) - 1.67 1.26 1.00 1.00 1.67 10x10 tiled rectangle (161x145 tile) - 1.13 1.14 1.14 1.14 1.14 100x100 tiled rectangle (161x145 tile) - 0.88 1.00 1.00 1.00 0.99 500x500 tiled rectangle (161x145 tile) - 1.93 1.63 1.00 1.00 3.53 1x1 tiled rectangle (216x208 tile) - 1.69 1.26 1.00 1.00 1.66 10x10 tiled rectangle (216x208 tile) - 1.00 1.00 1.00 1.00 1.00 100x100 tiled rectangle (216x208 tile) - 1.00 1.00 1.00 1.00 1.00 500x500 tiled rectangle (216x208 tile) - 1.82 1.70 1.00 1.00 3.38 1-pixel line segment - 2.07 1.56 0.90 1.00 3.31 10-pixel line segment - 1.29 1.10 1.00 1.00 1.27 100-pixel line segment - 1.05 1.06 1.03 1.03 1.09 500-pixel line segment - 1.30 1.13 1.00 1.00 1.29 100-pixel line segment (1 kid) - 1.32 1.15 1.00 1.00 1.32 100-pixel line segment (2 kids) - 1.33 1.16 1.00 1.00 1.33 100-pixel line segment (3 kids) - 1.92 1.64 1.00 1.00 3.73 10-pixel dashed segment - 1.34 1.16 1.00 1.00 1.34 100-pixel dashed segment - 1.24 1.11 0.99 0.97 1.23 100-pixel double-dashed segment - 1.72 1.77 1.00 1.00 3.25 10-pixel horizontal line segment - 1.83 1.66 1.01 1.00 3.54 100-pixel horizontal line segment - 1.86 1.30 1.00 1.00 1.84 500-pixel horizontal line segment - 2.11 1.52 1.00 0.99 3.02 10-pixel vertical line segment - 1.21 1.10 1.00 1.00 1.20 100-pixel vertical line segment - 1.03 1.03 1.00 1.00 1.02 500-pixel vertical line segment - 4.42 1.68 1.00 1.01 4.64 10x1 wide horizontal line segment - 1.83 1.31 1.00 1.00 1.83 100x10 wide horizontal line segment - 1.07 1.00 0.96 1.00 1.07 500x50 wide horizontal line segment - 4.10 1.67 1.00 1.00 4.62 10x1 wide vertical line segment - 1.50 1.24 1.06 1.06 1.48 100x10 wide vertical line segment - 1.06 1.03 1.00 1.00 1.05 500x50 wide vertical line segment - 2.54 1.61 1.00 1.00 3.61 1-pixel line - 2.71 1.48 1.00 1.00 2.67 10-pixel line - 1.19 1.09 1.00 1.00 1.19 100-pixel line - 1.04 1.02 1.00 1.00 1.03 500-pixel line - 2.68 1.51 0.98 1.00 3.17 10-pixel dashed line - 1.23 1.11 0.99 0.99 1.23 100-pixel dashed line - 1.15 1.08 1.00 1.00 1.15 100-pixel double-dashed line - 2.27 1.39 1.00 1.00 2.23 10x1 wide line - 1.20 1.09 1.00 1.00 1.20 100x10 wide line - 1.04 1.02 1.00 1.00 1.04 500x50 wide line - 1.52 1.45 1.00 1.00 1.52 100x10 wide dashed line - 1.54 1.47 1.00 1.00 1.54 100x10 wide double-dashed line - 1.97 1.30 0.96 0.95 1.95 10x10 rectangle outline - 1.44 1.27 1.00 1.00 1.43 100x100 rectangle outline - 3.22 2.16 1.10 1.09 3.61 500x500 rectangle outline - 1.95 1.34 1.00 1.00 1.90 10x10 wide rectangle outline - 1.14 1.14 1.00 1.00 1.13 100x100 wide rectangle outline - 1.00 1.00 1.00 1.00 1.00 500x500 wide rectangle outline - 1.57 1.72 1.00 1.00 3.03 1-pixel circle - 1.96 1.35 1.00 1.00 1.92 10-pixel circle - 1.21 1.07 0.86 0.97 1.20 100-pixel circle - 1.08 1.04 1.00 1.00 1.08 500-pixel circle - 1.39 1.19 1.03 1.03 1.38 100-pixel dashed circle - 1.21 1.11 1.00 1.00 1.23 100-pixel double-dashed circle - 1.59 1.28 1.00 1.00 1.58 10-pixel wide circle - 1.22 1.12 0.99 1.00 1.22 100-pixel wide circle - 1.06 1.04 1.00 1.00 1.05 500-pixel wide circle - 1.87 1.84 1.00 1.00 1.85 100-pixel wide dashed circle - 1.90 1.93 1.01 1.01 1.90 100-pixel wide double-dashed circle - 2.13 1.43 1.00 1.00 2.32 10-pixel partial circle - 1.42 1.18 1.00 1.00 1.42 100-pixel partial circle - 1.92 1.85 1.01 1.01 1.89 10-pixel wide partial circle - 1.73 1.67 1.00 1.00 1.73 100-pixel wide partial circle - 1.36 1.95 1.00 1.00 2.64 1-pixel solid circle - 2.02 1.37 1.00 1.00 2.03 10-pixel solid circle - 1.19 1.09 1.00 1.00 1.19 100-pixel solid circle - 1.02 0.99 1.00 1.00 1.01 500-pixel solid circle - 1.74 1.28 1.00 0.88 1.73 10-pixel fill chord partial circle - 1.31 1.13 1.00 1.00 1.31 100-pixel fill chord partial circle - 1.67 1.31 1.03 1.03 1.72 10-pixel fill slice partial circle - 1.30 1.13 1.00 1.00 1.28 100-pixel fill slice partial circle - 2.45 1.49 1.01 1.00 2.71 10-pixel ellipse - 1.22 1.10 1.00 1.00 1.22 100-pixel ellipse - 1.09 1.04 1.00 1.00 1.09 500-pixel ellipse - 1.90 1.28 1.00 1.00 1.89 100-pixel dashed ellipse - 1.62 1.24 0.96 0.97 1.61 100-pixel double-dashed ellipse - 2.43 1.50 1.00 1.00 2.42 10-pixel wide ellipse - 1.61 1.28 1.03 1.03 1.60 100-pixel wide ellipse - 1.08 1.05 1.00 1.00 1.08 500-pixel wide ellipse - 1.93 1.88 1.00 1.00 1.88 100-pixel wide dashed ellipse - 1.94 1.89 1.01 1.00 1.94 100-pixel wide double-dashed ellipse - 2.31 1.48 1.00 1.00 2.67 10-pixel partial ellipse - 1.38 1.17 1.00 1.00 1.38 100-pixel partial ellipse - 2.00 1.85 0.98 0.97 1.98 10-pixel wide partial ellipse - 1.89 1.86 1.00 1.00 1.89 100-pixel wide partial ellipse - 3.49 1.60 1.00 1.00 3.65 10-pixel filled ellipse - 1.67 1.26 1.00 1.00 1.67 100-pixel filled ellipse - 1.06 1.04 1.00 1.00 1.06 500-pixel filled ellipse - 2.38 1.43 1.01 1.00 2.32 10-pixel fill chord partial ellipse - 2.06 1.30 1.00 1.00 2.05 100-pixel fill chord partial ellipse - 2.27 1.41 1.00 1.00 2.27 10-pixel fill slice partial ellipse - 1.98 1.33 1.00 0.97 1.97 100-pixel fill slice partial ellipse - 57.46 1.99 1.01 1.00 114.92 Fill 1x1 equivalent triangle - 56.94 1.98 1.01 1.00 73.89 Fill 10x10 equivalent triangle - 6.07 1.75 1.00 1.00 6.07 Fill 100x100 equivalent triangle - 51.12 1.98 1.00 1.00 102.81 Fill 1x1 trapezoid - 51.42 1.82 1.01 1.00 94.89 Fill 10x10 trapezoid - 6.47 1.80 1.00 1.00 6.44 Fill 100x100 trapezoid - 1.56 1.28 1.00 0.99 1.56 Fill 300x300 trapezoid - 51.27 1.97 0.96 0.97 102.54 Fill 1x1 stippled trapezoid (8x8 stipple) - 51.73 2.00 1.02 1.02 67.92 Fill 10x10 stippled trapezoid (8x8 stipple) - 5.36 1.72 1.00 1.00 5.36 Fill 100x100 stippled trapezoid (8x8 stipple) - 1.54 1.26 1.00 1.00 1.59 Fill 300x300 stippled trapezoid (8x8 stipple) - 51.41 1.94 1.01 1.00 102.82 Fill 1x1 opaque stippled trapezoid (8x8 stipple) - 50.71 1.95 0.99 1.00 65.44 Fill 10x10 opaque stippled trapezoid (8x8... - 5.33 1.73 1.00 1.00 5.36 Fill 100x100 opaque stippled trapezoid (8x8... - 1.58 1.25 1.00 1.00 1.58 Fill 300x300 opaque stippled trapezoid (8x8... - 51.56 1.96 0.99 0.90 103.68 Fill 1x1 tiled trapezoid (4x4 tile) - 51.59 1.99 1.01 1.01 62.25 Fill 10x10 tiled trapezoid (4x4 tile) - 5.38 1.72 1.00 1.00 5.38 Fill 100x100 tiled trapezoid (4x4 tile) - 1.54 1.25 1.00 0.99 1.58 Fill 300x300 tiled trapezoid (4x4 tile) - 51.70 1.98 1.01 1.01 103.98 Fill 1x1 stippled trapezoid (17x15 stipple) - 44.86 1.97 1.00 1.00 44.86 Fill 10x10 stippled trapezoid (17x15 stipple) - 2.74 1.56 1.00 1.00 2.73 Fill 100x100 stippled trapezoid (17x15 stipple) - 1.29 1.14 1.00 1.00 1.27 Fill 300x300 stippled trapezoid (17x15 stipple) - 51.41 1.96 0.96 0.95 103.39 Fill 1x1 opaque stippled trapezoid (17x15... - 45.14 1.96 1.01 1.00 45.14 Fill 10x10 opaque stippled trapezoid (17x15... - 2.68 1.56 1.00 1.00 2.68 Fill 100x100 opaque stippled trapezoid (17x15... - 1.26 1.10 1.00 1.00 1.28 Fill 300x300 opaque stippled trapezoid (17x15... - 51.13 1.97 1.00 0.99 103.39 Fill 1x1 tiled trapezoid (17x15 tile) - 47.58 1.96 1.00 1.00 47.86 Fill 10x10 tiled trapezoid (17x15 tile) - 2.74 1.56 1.00 1.00 2.74 Fill 100x100 tiled trapezoid (17x15 tile) - 1.29 1.14 1.00 1.00 1.28 Fill 300x300 tiled trapezoid (17x15 tile) - 51.13 1.97 0.99 0.97 103.39 Fill 1x1 stippled trapezoid (161x145 stipple) - 45.14 1.97 1.00 1.00 44.29 Fill 10x10 stippled trapezoid (161x145 stipple) - 3.02 1.77 1.12 1.12 3.38 Fill 100x100 stippled trapezoid (161x145 stipple) - 1.31 1.13 1.00 1.00 1.30 Fill 300x300 stippled trapezoid (161x145 stipple) - 51.27 1.97 1.00 1.00 103.10 Fill 1x1 opaque stippled trapezoid (161x145... - 45.01 1.97 1.00 1.00 45.01 Fill 10x10 opaque stippled trapezoid (161x145... - 2.67 1.56 1.00 1.00 2.69 Fill 100x100 opaque stippled trapezoid (161x145.. - 1.29 1.13 1.00 1.01 1.27 Fill 300x300 opaque stippled trapezoid (161x145.. - 51.41 1.96 1.00 0.99 103.39 Fill 1x1 tiled trapezoid (161x145 tile) - 45.01 1.96 0.98 1.00 45.01 Fill 10x10 tiled trapezoid (161x145 tile) - 2.62 1.36 1.00 1.00 2.69 Fill 100x100 tiled trapezoid (161x145 tile) - 1.27 1.13 1.00 1.00 1.22 Fill 300x300 tiled trapezoid (161x145 tile) - 51.13 1.98 1.00 1.00 103.39 Fill 1x1 tiled trapezoid (216x208 tile) - 45.14 1.97 1.01 0.99 45.14 Fill 10x10 tiled trapezoid (216x208 tile) - 2.62 1.55 1.00 1.00 2.71 Fill 100x100 tiled trapezoid (216x208 tile) - 1.28 1.13 1.00 1.00 1.20 Fill 300x300 tiled trapezoid (216x208 tile) - 50.71 1.95 1.00 1.00 54.70 Fill 10x10 equivalent complex polygon - 5.51 1.71 0.96 0.98 5.47 Fill 100x100 equivalent complex polygons - 8.39 1.97 1.00 1.00 16.75 Fill 10x10 64-gon (Convex) - 8.38 1.83 1.00 1.00 8.43 Fill 100x100 64-gon (Convex) - 8.50 1.96 1.00 1.00 16.64 Fill 10x10 64-gon (Complex) - 8.26 1.83 1.00 1.00 8.35 Fill 100x100 64-gon (Complex) - 14.09 1.87 1.00 1.00 14.05 Char in 80-char line (6x13) - 11.91 1.87 1.00 1.00 11.95 Char in 70-char line (8x13) - 11.16 1.85 1.01 1.00 11.10 Char in 60-char line (9x15) - 10.09 1.78 1.00 1.00 10.09 Char16 in 40-char line (k14) - 6.15 1.75 1.00 1.00 6.31 Char16 in 23-char line (k24) - 11.92 1.90 1.03 1.03 11.88 Char in 80-char line (TR 10) - 8.18 1.78 1.00 0.99 8.17 Char in 30-char line (TR 24) - 42.83 1.44 1.01 1.00 42.11 Char in 20/40/20 line (6x13, TR 10) - 27.45 1.43 1.01 1.01 27.45 Char16 in 7/14/7 line (k14, k24) - 12.13 1.85 1.00 1.00 12.05 Char in 80-char image line (6x13) - 10.00 1.84 1.00 1.00 10.00 Char in 70-char image line (8x13) - 9.18 1.83 1.00 1.00 9.12 Char in 60-char image line (9x15) - 9.66 1.82 0.98 0.95 9.66 Char16 in 40-char image line (k14) - 5.82 1.72 1.00 1.00 5.99 Char16 in 23-char image line (k24) - 8.70 1.80 1.00 1.00 8.65 Char in 80-char image line (TR 10) - 4.67 1.66 1.00 1.00 4.67 Char in 30-char image line (TR 24) - 84.43 1.47 1.00 1.00 124.18 Scroll 10x10 pixels - 3.73 1.50 1.00 0.98 3.73 Scroll 100x100 pixels - 1.00 1.00 1.00 1.00 1.00 Scroll 500x500 pixels - 84.43 1.51 1.00 1.00 134.02 Copy 10x10 from window to window - 3.62 1.51 0.98 0.98 3.62 Copy 100x100 from window to window - 0.89 1.00 1.00 1.00 1.00 Copy 500x500 from window to window - 57.06 1.99 1.00 1.00 88.64 Copy 10x10 from pixmap to window - 2.49 2.00 1.00 1.00 2.48 Copy 100x100 from pixmap to window - 1.00 0.91 1.00 1.00 0.98 Copy 500x500 from pixmap to window - 2.04 1.01 1.00 1.00 2.03 Copy 10x10 from window to pixmap - 1.05 1.00 1.00 1.00 1.05 Copy 100x100 from window to pixmap - 1.00 1.00 0.93 1.00 1.04 Copy 500x500 from window to pixmap - 58.52 1.03 1.03 1.02 57.95 Copy 10x10 from pixmap to pixmap - 2.40 1.00 1.00 1.00 2.45 Copy 100x100 from pixmap to pixmap - 1.00 1.00 1.00 1.00 1.00 Copy 500x500 from pixmap to pixmap - 51.57 1.92 1.00 1.00 85.75 Copy 10x10 1-bit deep plane - 6.37 1.75 1.01 1.01 6.37 Copy 100x100 1-bit deep plane - 1.26 1.11 1.00 1.00 1.24 Copy 500x500 1-bit deep plane - 4.23 1.63 0.98 0.97 4.38 Copy 10x10 n-bit deep plane - 1.04 1.02 1.00 1.00 1.04 Copy 100x100 n-bit deep plane - 1.00 1.00 1.00 1.00 1.00 Copy 500x500 n-bit deep plane - 6.45 1.98 1.00 1.26 12.80 PutImage 10x10 square - 1.10 1.87 1.00 1.83 2.11 PutImage 100x100 square - 1.02 1.93 1.00 1.91 1.91 PutImage 500x500 square - 4.17 1.78 1.00 1.40 7.18 PutImage XY 10x10 square - 1.27 1.49 0.97 1.48 2.10 PutImage XY 100x100 square - 1.00 1.50 1.00 1.50 1.52 PutImage XY 500x500 square - 1.07 1.01 1.00 1.00 1.06 GetImage 10x10 square - 1.01 1.00 1.00 1.00 1.01 GetImage 100x100 square - 1.00 1.00 1.00 1.00 1.00 GetImage 500x500 square - 1.56 1.00 0.99 0.97 1.56 GetImage XY 10x10 square - 1.02 1.00 1.00 1.00 1.02 GetImage XY 100x100 square - 1.00 1.00 1.00 1.00 1.00 GetImage XY 500x500 square - 1.00 1.00 1.01 0.98 0.95 X protocol NoOperation - 1.02 1.03 1.04 1.03 1.00 QueryPointer - 1.03 1.02 1.04 1.03 1.00 GetProperty -100.41 1.51 1.00 1.00 198.76 Change graphics context - 45.81 1.00 0.99 0.97 57.10 Create and map subwindows (4 kids) - 78.45 1.01 1.02 1.02 63.07 Create and map subwindows (16 kids) - 73.91 1.01 1.00 1.00 56.37 Create and map subwindows (25 kids) - 73.22 1.00 1.00 1.00 49.07 Create and map subwindows (50 kids) - 72.36 1.01 0.99 1.00 32.14 Create and map subwindows (75 kids) - 70.34 1.00 1.00 1.00 30.12 Create and map subwindows (100 kids) - 55.00 1.00 1.00 0.99 23.75 Create and map subwindows (200 kids) - 55.30 1.01 1.00 1.00 141.03 Create unmapped window (4 kids) - 55.38 1.01 1.01 1.00 163.25 Create unmapped window (16 kids) - 54.75 0.96 1.00 0.99 166.95 Create unmapped window (25 kids) - 54.83 1.00 1.00 0.99 178.81 Create unmapped window (50 kids) - 55.38 1.01 1.01 1.00 181.20 Create unmapped window (75 kids) - 55.38 1.01 1.01 1.00 181.20 Create unmapped window (100 kids) - 54.87 1.01 1.01 1.00 182.05 Create unmapped window (200 kids) - 28.13 1.00 1.00 1.00 30.75 Map window via parent (4 kids) - 36.14 1.01 1.01 1.01 32.58 Map window via parent (16 kids) - 26.13 1.00 0.98 0.95 29.85 Map window via parent (25 kids) - 40.07 1.00 1.01 1.00 27.57 Map window via parent (50 kids) - 23.26 0.99 1.00 1.00 18.23 Map window via parent (75 kids) - 22.91 0.99 1.00 0.99 16.52 Map window via parent (100 kids) - 27.79 1.00 1.00 0.99 12.50 Map window via parent (200 kids) - 22.35 1.00 1.00 1.00 56.19 Unmap window via parent (4 kids) - 9.57 1.00 0.99 1.00 89.78 Unmap window via parent (16 kids) - 80.77 1.01 1.00 1.00 103.85 Unmap window via parent (25 kids) - 96.34 1.00 1.00 1.00 116.06 Unmap window via parent (50 kids) - 99.72 1.00 1.00 1.00 124.93 Unmap window via parent (75 kids) -112.36 1.00 1.00 1.00 125.27 Unmap window via parent (100 kids) -105.41 1.00 1.00 0.99 120.00 Unmap window via parent (200 kids) - 51.29 1.03 1.02 1.02 74.19 Destroy window via parent (4 kids) - 86.75 0.99 0.99 0.99 116.87 Destroy window via parent (16 kids) -106.43 1.01 1.01 1.01 127.49 Destroy window via parent (25 kids) -120.34 1.01 1.01 1.00 140.11 Destroy window via parent (50 kids) -126.67 1.00 0.99 0.99 145.00 Destroy window via parent (75 kids) -126.11 1.01 1.01 1.00 140.56 Destroy window via parent (100 kids) -128.57 1.01 1.00 1.00 137.91 Destroy window via parent (200 kids) - 16.04 0.88 1.00 1.00 20.36 Hide/expose window via popup (4 kids) - 19.04 1.01 1.00 1.00 23.48 Hide/expose window via popup (16 kids) - 19.22 1.00 1.00 1.00 20.44 Hide/expose window via popup (25 kids) - 17.41 1.00 0.91 0.97 17.68 Hide/expose window via popup (50 kids) - 17.29 1.01 1.00 1.01 17.07 Hide/expose window via popup (75 kids) - 16.74 1.00 1.00 1.00 16.17 Hide/expose window via popup (100 kids) - 10.30 1.00 1.00 1.00 10.51 Hide/expose window via popup (200 kids) - 16.48 1.01 1.00 1.00 26.05 Move window (4 kids) - 17.01 0.95 1.00 1.00 23.97 Move window (16 kids) - 16.95 1.00 1.00 1.00 22.90 Move window (25 kids) - 16.05 1.01 1.00 1.00 21.32 Move window (50 kids) - 15.58 1.00 0.98 0.98 19.44 Move window (75 kids) - 14.98 1.02 1.03 1.03 18.17 Move window (100 kids) - 10.90 1.01 1.01 1.00 12.68 Move window (200 kids) - 49.42 1.00 1.00 1.00 198.27 Moved unmapped window (4 kids) - 50.72 0.97 1.00 1.00 193.66 Moved unmapped window (16 kids) - 50.87 1.00 0.99 1.00 195.09 Moved unmapped window (25 kids) - 50.72 1.00 1.00 1.00 189.34 Moved unmapped window (50 kids) - 50.87 1.00 1.00 1.00 191.33 Moved unmapped window (75 kids) - 50.87 1.00 1.00 0.90 186.71 Moved unmapped window (100 kids) - 50.87 1.00 1.00 1.00 179.19 Moved unmapped window (200 kids) - 41.04 1.00 1.00 1.00 56.61 Move window via parent (4 kids) - 69.81 1.00 1.00 1.00 130.82 Move window via parent (16 kids) - 95.81 1.00 1.00 1.00 141.92 Move window via parent (25 kids) - 95.98 1.00 1.00 1.00 149.43 Move window via parent (50 kids) - 96.59 1.01 1.01 1.00 153.98 Move window via parent (75 kids) - 97.19 1.00 1.00 1.00 157.30 Move window via parent (100 kids) - 96.67 1.00 0.99 0.96 159.44 Move window via parent (200 kids) - 17.75 1.01 1.00 1.00 27.61 Resize window (4 kids) - 17.94 1.00 1.00 0.99 25.42 Resize window (16 kids) - 17.92 1.01 1.00 1.00 24.47 Resize window (25 kids) - 17.24 0.97 1.00 1.00 24.14 Resize window (50 kids) - 16.81 1.00 1.00 0.99 22.75 Resize window (75 kids) - 16.08 1.00 1.00 1.00 21.20 Resize window (100 kids) - 12.92 1.00 0.99 1.00 16.26 Resize window (200 kids) - 52.94 1.01 1.00 1.00 327.12 Resize unmapped window (4 kids) - 53.60 1.01 1.01 1.01 333.71 Resize unmapped window (16 kids) - 52.99 1.00 1.00 1.00 337.29 Resize unmapped window (25 kids) - 51.98 1.00 1.00 1.00 329.38 Resize unmapped window (50 kids) - 53.05 0.89 1.00 1.00 322.60 Resize unmapped window (75 kids) - 53.05 1.00 1.00 1.00 318.08 Resize unmapped window (100 kids) - 53.11 1.00 1.00 0.99 306.21 Resize unmapped window (200 kids) - 16.76 1.00 0.96 1.00 19.46 Circulate window (4 kids) - 17.24 1.00 1.00 0.97 16.24 Circulate window (16 kids) - 16.30 1.03 1.03 1.03 15.85 Circulate window (25 kids) - 13.45 1.00 1.00 1.00 14.90 Circulate window (50 kids) - 12.91 1.00 1.00 1.00 13.06 Circulate window (75 kids) - 11.30 0.98 1.00 1.00 11.03 Circulate window (100 kids) - 7.58 1.01 1.01 0.99 7.47 Circulate window (200 kids) - 1.01 1.01 0.98 1.00 0.95 Circulate Unmapped window (4 kids) - 1.07 1.07 1.01 1.07 1.02 Circulate Unmapped window (16 kids) - 1.04 1.09 1.06 1.05 0.97 Circulate Unmapped window (25 kids) - 1.04 1.23 1.20 1.18 1.05 Circulate Unmapped window (50 kids) - 1.18 1.53 1.19 1.45 1.24 Circulate Unmapped window (75 kids) - 1.08 1.02 1.01 1.74 1.01 Circulate Unmapped window (100 kids) - 1.01 1.12 0.98 0.91 0.97 Circulate Unmapped window (200 kids) - - - - - -Profiling with OProfile - -OProfile (available from http://oprofile.sourceforge.net/) is a -system-wide profiler for Linux systems that uses processor-level -counters to collect sampling data. OProfile can provide information -that is similar to that provided by gprof, but without the -necessity of recompiling the program with special instrumentation (i.e., -OProfile can collect statistical profiling information about optimized -programs). A test harness was developed to collect OProfile data for -each x11perf test individually. - - -Test runs were performed using the RETIRED_INSNS counter on the AMD -Athlon and the CPU_CLK_HALTED counter on the Intel Pentium III (with a -test configuration different from the one described above). We have -examined OProfile output and have compared it with gprof output. -This investigation has not produced results that yield performance -increases in x11perf numbers. - - - - - - - - - -X Test Suite - -The X Test Suite was run on the fully optimized DMX server using the -configuration described above. The following failures were noted: - -XListPixmapFormats: Test 1 [1] -XChangeWindowAttributes: Test 32 [1] -XCreateWindow: Test 30 [1] -XFreeColors: Test 4 [3] -XCopyArea: Test 13, 17, 21, 25, 30 [2] -XCopyPlane: Test 11, 15, 27, 31 [2] -XSetFontPath: Test 4 [1] -XChangeKeyboardControl: Test 9, 10 [1] - -[1] Previously documented errors expected from the Xinerama - implementation (see Phase I discussion). -[2] Newly noted errors that have been verified as expected - behavior of the Xinerama implementation. -[3] Newly noted error that has been verified as a Xinerama - implementation bug. - - - - - - - - - -Phase III - -During the third phase of development, support was provided for the -following extensions: SHAPE, RENDER, XKEYBOARD, XInput. - - - -SHAPE - -The SHAPE extension is supported. Test applications (e.g., xeyes and -oclock) and window managers that make use of the SHAPE extension will -work as expected. - - - - -RENDER - -The RENDER extension is supported. The version included in the DMX -CVS tree is version 0.2, and this version is fully supported by Xdmx. -Applications using only version 0.2 functions will work correctly; -however, some apps that make use of functions from later versions do not -properly check the extension's major/minor version numbers. These apps -will fail with a Bad Implementation error when using post-version 0.2 -functions. This is expected behavior. When the DMX CVS tree is updated -to include newer versions of RENDER, support for these newer functions -will be added to the DMX X server. - - - - -XKEYBOARD - -The XKEYBOARD extension is supported. If present on the back-end X -servers, the XKEYBOARD extension will be used to obtain information -about the type of the keyboard for initialization. Otherwise, the -keyboard will be initialized using defaults. Note that this departs -from older behavior: when Xdmx is compiled without XKEYBOARD support, -the map from the back-end X server will be preserved. With XKEYBOARD -support, the map is not preserved because better information and control -of the keyboard is available. - - - - -XInput - -The XInput extension is supported. Any device can be used as a core -device and be used as an XInput extension device, with the exception of -core devices on the back-end servers. This limitation is present -because cursor handling on the back-end requires that the back-end -cursor sometimes track the Xdmx core cursor -- behavior that is -incompatible with using the back-end pointer as a non-core device. - - -Currently, back-end extension devices are not available as Xdmx -extension devices, but this limitation should be removed in the future. - - -To demonstrate the XInput extension, and to provide more examples for -low-level input device driver writers, USB device drivers have been -written for mice (usb-mou), keyboards (usb-kbd), and -non-mouse/non-keyboard USB devices (usb-oth). Please see the man page -for information on Linux kernel drivers that are required for using -these Xdmx drivers. - - - - -DPMS - -The DPMS extension is exported but does not do anything at this time. - - - - - -Other Extensions - -The LBX, - SECURITY, - XC-APPGROUP, and - XFree86-Bigfont -extensions do not require any special Xdmx support and have been exported. - - -The - BIG-REQUESTS, - DEC-XTRAP, - DOUBLE-BUFFER, - Extended-Visual-Information, - FontCache, - GLX, - MIT-SCREEN-SAVER, - MIT-SHM, - MIT-SUNDRY-NONSTANDARD, - RECORD, - SECURITY, - SGI-GLX, - SYNC, - TOG-CUP, - X-Resource, - XC-MISC, - XFree86-DGA, - XFree86-DRI, - XFree86-Misc, - XFree86-VidModeExtension, and - XVideo -extensions are not supported at this time, but will be evaluated -for inclusion in future DMX releases. See below for additional work -on extensions after Phase III. - - - - - -Phase IV - - -Moving to XFree86 4.3.0 - -For Phase IV, the recent release of XFree86 4.3.0 (27 February 2003) -was merged onto the dmx.sourceforge.net CVS trunk and all work is -proceeding using this tree. - - - - -Extensions - - -XC-MISC (supported) - -XC-MISC is used internally by the X library to recycle XIDs from the -X server. This is important for long-running X server sessions. Xdmx -supports this extension. The X Test Suite passed and failed the exact -same tests before and after this extension was enabled. - - - - - -Extended-Visual-Information (supported) - -The Extended-Visual-Information extension provides a method for an X -client to obtain detailed visual information. Xdmx supports this -extension. It was tested using the hw/dmx/examples/evi example -program. Note that this extension is not Xinerama-aware -- it will -return visual information for each screen even though Xinerama is -causing the X server to export a single logical screen. - - - - - -RES (supported) - -The X-Resource extension provides a mechanism for a client to obtain -detailed information about the resources used by other clients. This -extension was tested with the hw/dmx/examples/res program. The -X Test Suite passed and failed the exact same tests before and after -this extension was enabled. - - - - - -BIG-REQUESTS (supported) - -This extension enables the X11 protocol to handle requests longer -than 262140 bytes. The X Test Suite passed and failed the exact same -tests before and after this extension was enabled. - - - - - -XSYNC (supported) - -This extension provides facilities for two different X clients to -synchronize their requests. This extension was minimally tested with -xdpyinfo and the X Test Suite passed and failed the exact same -tests before and after this extension was enabled. - - - - - -XTEST, RECORD, DEC-XTRAP (supported) and XTestExtension1 (not supported) - -The XTEST and RECORD extension were developed by the X Consortium for -use in the X Test Suite and are supported as a standard in the X11R6 -tree. They are also supported in Xdmx. When X Test Suite tests that -make use of the XTEST extension are run, Xdmx passes and fails exactly -the same tests as does a standard XFree86 X server. When the -rcrdtest test (a part of the X Test Suite that verifies the RECORD -extension) is run, Xdmx passes and fails exactly the same tests as does -a standard XFree86 X server. - - -There are two older XTEST-like extensions: DEC-XTRAP and -XTestExtension1. The XTestExtension1 extension was developed for use by -the X Testing Consortium for use with a test suite that eventually -became (part of?) the X Test Suite. Unlike XTEST, which only allows -events to be sent to the server, the XTestExtension1 extension also -allowed events to be recorded (similar to the RECORD extension). The -second is the DEC-XTRAP extension that was developed by the Digital -Equipment Corporation. - - -The DEC-XTRAP extension is available from Xdmx and has been tested -with the xtrap* tools which are distributed as standard X11R6 -clients. - - -The XTestExtension1 is not supported because it does not appear -to be used by any modern X clients (the few that support it also support -XTEST) and because there are no good methods available for testing that -it functions correctly (unlike XTEST and DEC-XTRAP, the code for -XTestExtension1 is not part of the standard X server source tree, so -additional testing is important). - - -Most of these extensions are documented in the X11R6 source tree. -Further, several original papers exist that this author was unable to -locate -- for completeness and historical interest, citations are -provide: - - -XRECORD - -Martha Zimet. Extending X For Recording. 8th Annual X -Technical Conference Boston, MA January 24-26, 1994. - - -DEC-XTRAP - -Dick Annicchiarico, Robert Chesler, Alan Jamison. XTrap -Architecture. Digital Equipment Corporation, July 1991. - - -XTestExtension1 - -Larry Woestman. X11 Input Synthesis Extension -Proposal. Hewlett Packard, November 1991. - - - - - - -MIT-MISC (not supported) - -The MIT-MISC extension is used to control a bug-compatibility flag -that provides compatibility with xterm programs from X11R1 and X11R2. -There does not appear to be a single client available that makes use of -this extension and there is not way to verify that it works correctly. -The Xdmx server does not support MIT-MISC. - - - - -SCREENSAVER (not supported) - -This extension provides special support for the X screen saver. It -was tested with beforelight, which appears to be the only client that -works with it. When Xinerama was not active, beforelight behaved -as expected. However, when Xinerama was active, beforelight did -not behave as expected. Further, when this extension is not active, -xscreensaver (a widely-used X screen saver program) did not behave -as expected. Since this extension is not Xinerama-aware and is not -commonly used with expected results by clients, we have left this -extension disabled at this time. - - - - -GLX (supported) - -The GLX extension provides OpenGL and GLX windowing support. In -Xdmx, the extension is called glxProxy, and it is Xinerama aware. It -works by either feeding requests forward through Xdmx to each of the -back-end servers or handling them locally. All rendering requests are -handled on the back-end X servers. This code was donated to the DMX -project by SGI. For the X Test Suite results comparison, see below. - - - - -RENDER (supported) - -The X Rendering Extension (RENDER) provides support for digital image -composition. Geometric and text rendering are supported. RENDER is -partially Xinerama-aware, with text and the most basic compositing -operator; however, its higher level primitives (triangles, triangle -strips, and triangle fans) are not yet Xinerama-aware. The RENDER -extension is still under development, and is currently at version 0.8. -Additional support will be required in DMX as more primitives and/or -requests are added to the extension. - - -There is currently no test suite for the X Rendering Extension; -however, there has been discussion of developing a test suite as the -extension matures. When that test suite becomes available, additional -testing can be performed with Xdmx. The X Test Suite passed and failed -the exact same tests before and after this extension was enabled. - - - - -Summary - - -To summarize, the following extensions are currently supported: - BIG-REQUESTS, - DEC-XTRAP, - DMX, - DPMS, - Extended-Visual-Information, - GLX, - LBX, - RECORD, - RENDER, - SECURITY, - SHAPE, - SYNC, - X-Resource, - XC-APPGROUP, - XC-MISC, - XFree86-Bigfont, - XINERAMA, - XInputExtension, - XKEYBOARD, and - XTEST. - - -The following extensions are not supported at this time: - DOUBLE-BUFFER, - FontCache, - MIT-SCREEN-SAVER, - MIT-SHM, - MIT-SUNDRY-NONSTANDARD, - TOG-CUP, - XFree86-DGA, - XFree86-Misc, - XFree86-VidModeExtension, - XTestExtensionExt1, and - XVideo. - - - - - -Additional Testing with the X Test Suite - - -XFree86 without XTEST - -After the release of XFree86 4.3.0, we retested the XFree86 X server -with and without using the XTEST extension. When the XTEST extension -was not used for testing, the XFree86 4.3.0 server running on our -usual test system with a Radeon VE card reported unexpected failures in -the following tests: - -XListPixmapFormats: Test 1 -XChangeKeyboardControl: Tests 9, 10 -XGetDefault: Test 5 -XRebindKeysym: Test 1 - - - - - -XFree86 with XTEST - -When using the XTEST extension, the XFree86 4.3.0 server reported the -following errors: - -XListPixmapFormats: Test 1 -XChangeKeyboardControl: Tests 9, 10 -XGetDefault: Test 5 -XRebindKeysym: Test 1 - -XAllowEvents: Tests 20, 21, 24 -XGrabButton: Tests 5, 9-12, 14, 16, 19, 21-25 -XGrabKey: Test 8 -XSetPointerMapping: Test 3 -XUngrabButton: Test 4 - - - -While these errors may be important, they will probably be fixed -eventually in the XFree86 source tree. We are particularly interested -in demonstrating that the Xdmx server does not introduce additional -failures that are not known Xinerama failures. - - - - -Xdmx with XTEST, without Xinerama, without GLX - -Without Xinerama, but using the XTEST extension, the following errors -were reported from Xdmx (note that these are the same as for the XFree86 -4.3.0, except that XGetDefault no longer fails): - -XListPixmapFormats: Test 1 -XChangeKeyboardControl: Tests 9, 10 -XRebindKeysym: Test 1 - -XAllowEvents: Tests 20, 21, 24 -XGrabButton: Tests 5, 9-12, 14, 16, 19, 21-25 -XGrabKey: Test 8 -XSetPointerMapping: Test 3 -XUngrabButton: Test 4 - - - - - -Xdmx with XTEST, with Xinerama, without GLX - -With Xinerama, using the XTEST extension, the following errors -were reported from Xdmx: - -XListPixmapFormats: Test 1 -XChangeKeyboardControl: Tests 9, 10 -XRebindKeysym: Test 1 - -XAllowEvents: Tests 20, 21, 24 -XGrabButton: Tests 5, 9-12, 14, 16, 19, 21-25 -XGrabKey: Test 8 -XSetPointerMapping: Test 3 -XUngrabButton: Test 4 - -XCopyPlane: Tests 13, 22, 31 (well-known XTEST/Xinerama interaction issue) -XDrawLine: Test 67 -XDrawLines: Test 91 -XDrawSegments: Test 68 - -Note that the first two sets of errors are the same as for the XFree86 -4.3.0 server, and that the XCopyPlane error is a well-known error -resulting from an XTEST/Xinerama interaction when the request crosses a -screen boundary. The XDraw* errors are resolved when the tests are run -individually and they do not cross a screen boundary. We will -investigate these errors further to determine their cause. - - - - -Xdmx with XTEST, with Xinerama, with GLX - -With GLX enabled, using the XTEST extension, the following errors -were reported from Xdmx (these results are from early during the Phase -IV development, but were confirmed with a late Phase IV snapshot): - -XListPixmapFormats: Test 1 -XChangeKeyboardControl: Tests 9, 10 -XRebindKeysym: Test 1 - -XAllowEvents: Tests 20, 21, 24 -XGrabButton: Tests 5, 9-12, 14, 16, 19, 21-25 -XGrabKey: Test 8 -XSetPointerMapping: Test 3 -XUngrabButton: Test 4 - -XClearArea: Test 8 -XCopyArea: Tests 4, 5, 11, 14, 17, 23, 25, 27, 30 -XCopyPlane: Tests 6, 7, 10, 19, 22, 31 -XDrawArcs: Tests 89, 100, 102 -XDrawLine: Test 67 -XDrawSegments: Test 68 - -Note that the first two sets of errors are the same as for the XFree86 -4.3.0 server, and that the third set has different failures than when -Xdmx does not include GLX support. Since the GLX extension adds new -visuals to support GLX's visual configs and the X Test Suite runs tests -over the entire set of visuals, additional rendering tests were run and -presumably more of them crossed a screen boundary. This conclusion is -supported by the fact that nearly all of the rendering errors reported -are resolved when the tests are run individually and they do no cross a -screen boundary. - - -Further, when hardware rendering is disabled on the back-end displays, -many of the errors in the third set are eliminated, leaving only: - -XClearArea: Test 8 -XCopyArea: Test 4, 5, 11, 14, 17, 23, 25, 27, 30 -XCopyPlane: Test 6, 7, 10, 19, 22, 31 - - - - - -Conclusion - -We conclude that all of the X Test Suite errors reported for Xdmx are -the result of errors in the back-end X server or the Xinerama -implementation. Further, all of these errors that can be reasonably -fixed at the Xdmx layer have been. (Where appropriate, we have -submitted patches to the XFree86 and Xinerama upstream maintainers.) - - - - - -Dynamic Reconfiguration - -During this development phase, dynamic reconfiguration support was -added to DMX. This support allows an application to change the position -and offset of a back-end server's screen. For example, if the -application would like to shift a screen slightly to the left, it could -query Xdmx for the screen's <x,y> position and then dynamically -reconfigure that screen to be at position <x+10,y>. When a screen -is dynamically reconfigured, input handling and a screen's root window -dimensions are adjusted as needed. These adjustments are transparent to -the user. - - - -Dynamic reconfiguration extension - -The application interface to DMX's dynamic reconfiguration is through -a function in the DMX extension library: - -Bool DMXReconfigureScreen(Display *dpy, int screen, int x, int y) - -where dpy is DMX server's display, screen is the number of the -screen to be reconfigured, and x and y are the new upper, -left-hand coordinates of the screen to be reconfigured. - - -The coordinates are not limited other than as required by the X -protocol, which limits all coordinates to a signed 16 bit number. In -addition, all coordinates within a screen must also be legal values. -Therefore, setting a screen's upper, left-hand coordinates such that the -right or bottom edges of the screen is greater than 32,767 is illegal. - - - - -Bounding box - -When the Xdmx server is started, a bounding box is calculated from -the screens' layout given either on the command line or in the -configuration file. This bounding box is currently fixed for the -lifetime of the Xdmx server. - - -While it is possible to move a screen outside of the bounding box, it -is currently not possible to change the dimensions of the bounding box. -For example, it is possible to specify coordinates of <-100,-100> -for the upper, left-hand corner of the bounding box, which was -previously at coordinates <0,0>. As expected, the screen is moved -down and to the right; however, since the bounding box is fixed, the -left side and upper portions of the screen exposed by the -reconfiguration are no longer accessible on that screen. Those -inaccessible regions are filled with black. - - -This fixed bounding box limitation will be addressed in a future -development phase. - - - - -Sample applications - -An example of where this extension is useful is in setting up a video -wall. It is not always possible to get everything perfectly aligned, -and sometimes the positions are changed (e.g., someone might bump into a -projector). Instead of physically moving projectors or monitors, it is -now possible to adjust the positions of the back-end server's screens -using the dynamic reconfiguration support in DMX. - - -Other applications, such as automatic setup and calibration tools, -can make use of dynamic reconfiguration to correct for projector -alignment problems, as long as the projectors are still arranged -rectilinearly. Horizontal and vertical keystone correction could be -applied to projectors to correct for non-rectilinear alignment problems; -however, this must be done external to Xdmx. - - -A sample test program is included in the DMX server's examples -directory to demonstrate the interface and how an application might use -dynamic reconfiguration. See dmxreconfig.c for details. - - - - -Additional notes - -In the original development plan, Phase IV was primarily devoted to -adding OpenGL support to DMX; however, SGI became interested in the DMX -project and developed code to support OpenGL/GLX. This code was later -donated to the DMX project and integrated into the DMX code base, which -freed the DMX developers to concentrate on dynamic reconfiguration (as -described above). - - - - - -Doxygen documentation - -Doxygen is an open-source (GPL) documentation system for generating -browseable documentation from stylized comments in the source code. We -have placed all of the Xdmx server and DMX protocol source code files -under Doxygen so that comprehensive documentation for the Xdmx source -code is available in an easily browseable format. - - - - -Valgrind - -Valgrind, an open-source (GPL) memory debugger for Linux, was used to -search for memory management errors. Several memory leaks were detected -and repaired. The following errors were not addressed: - - - When the X11 transport layer sends a reply to the client, only - those fields that are required by the protocol are filled in -- - unused fields are left as uninitialized memory and are therefore - noted by valgrind. These instances are not errors and were not - repaired. - - - At each server generation, glxInitVisuals allocates memory that - is never freed. The amount of memory lost each generation - approximately equal to 128 bytes for each back-end visual. - Because the code involved is automatically generated, this bug - has not been fixed and will be referred to SGI. - - - At each server generation, dmxRealizeFont calls XLoadQueryFont, - which allocates a font structure that is not freed. - dmxUnrealizeFont can free the font structure for the first - screen, but cannot free it for the other screens since they are - already closed by the time dmxUnrealizeFont could free them. - The amount of memory lost each generation is approximately equal - to 80 bytes per font per back-end. When this bug is fixed in - the the X server's device-independent (dix) code, DMX will be - able to properly free the memory allocated by XLoadQueryFont. - - - - - - -RATS - -RATS (Rough Auditing Tool for Security) is an open-source (GPL) -security analysis tool that scans source code for common -security-related programming errors (e.g., buffer overflows and TOCTOU -races). RATS was used to audit all of the code in the hw/dmx directory -and all "High" notations were checked manually. The code was either -re-written to eliminate the warning, or a comment containing "RATS" was -inserted on the line to indicate that a human had checked the code. -Unrepaired warnings are as follows: - - - Fixed-size buffers are used in many areas, but code has been - added to protect against buffer overflows (e.g., XmuSnprint). - The only instances that have not yet been fixed are in - config/xdmxconfig.c (which is not part of the Xdmx server) and - input/usb-common.c. - - - vprintf and vfprintf are used in the logging routines. In - general, all uses of these functions (e.g., dmxLog) provide a - constant format string from a trusted source, so the use is - relatively benign. - - - glxProxy/glxscreens.c uses getenv and strcat. The use of these - functions is safe and will remain safe as long as - ExtensionsString is longer then GLXServerExtensions (ensuring - this may not be ovious to the casual programmer, but this is in - automatically generated code, so we hope that the generator - enforces this constraint). - - - - - - - - - - - - - -
- - - - + + + +
+ + + + Distributed Multihead X design + + Kevin E.Martin + David H.Dawes + Rickard E.Faith + + 29 June 2004 (created 25 July 2001) + + This document covers the motivation, background, design, and + implementation of the distributed multihead X (DMX) system. It + is a living document and describes the current design and + implementation details of the DMX system. As the project + progresses, this document will be continually updated to reflect + the changes in the code and/or design. Copyright 2001 by VA + Linux Systems, Inc., Fremont, California. Copyright 2001-2004 + by Red Hat, Inc., Raleigh, North Carolina + + + + + +Introduction + + +The Distributed Multihead X Server + +Current Open Source multihead solutions are limited to a single +physical machine. A single X server controls multiple display devices, +which can be arranged as independent heads or unified into a single +desktop (with Xinerama). These solutions are limited to the number of +physical devices that can co-exist in a single machine (e.g., due to the +number of AGP/PCI slots available for graphics cards). Thus, large +tiled displays are not currently possible. The work described in this +paper will eliminate the requirement that the display devices reside in +the same physical machine. This will be accomplished by developing a +front-end proxy X server that will control multiple back-end X servers +that make up the large display. + + +The overall structure of the distributed multihead X (DMX) project is +as follows: A single front-end X server will act as a proxy to a set of +back-end X servers, which handle all of the visible rendering. X +clients will connect to the front-end server just as they normally would +to a regular X server. The front-end server will present an abstracted +view to the client of a single large display. This will ensure that all +standard X clients will continue to operate without modification +(limited, as always, by the visuals and extensions provided by the X +server). Clients that are DMX-aware will be able to use an extension to +obtain information about the back-end servers (e.g., for placement of +pop-up windows, window alignments by the window manager, etc.). + + +The architecture of the DMX server is divided into two main sections: +input (e.g., mouse and keyboard events) and output (e.g., rendering and +windowing requests). Each of these are describe briefly below, and the +rest of this design document will describe them in greater detail. + + +The DMX server can receive input from three general types of input +devices: "local" devices that are physically attached to the machine on +which DMX is running, "backend" devices that are physically attached to +one or more of the back-end X servers (and that generate events via the +X protocol stream from the backend), and "console" devices that can be +abstracted from any non-back-end X server. Backend and console devices +are treated differently because the pointer device on the back-end X +server also controls the location of the hardware X cursor. Full +support for XInput extension devices is provided. + + +Rendering requests will be accepted by the front-end server; however, +rendering to visible windows will be broken down as needed and sent to +the appropriate back-end server(s) via X11 library calls for actual +rendering. The basic framework will follow a Xnest-style approach. GC +state will be managed in the front-end server and sent to the +appropriate back-end server(s) as required. Pixmap rendering will (at +least initially) be handled by the front-end X server. Windowing +requests (e.g., ordering, mapping, moving, etc.) will handled in the +front-end server. If the request requires a visible change, the +windowing operation will be translated into requests for the appropriate +back-end server(s). Window state will be mirrored in the back-end +server(s) as needed. + + + + +Layout of Paper + +The next section describes the general development plan that was +actually used for implementation. The final section discusses +outstanding issues at the conclusion of development. The first appendix +provides low-level technical detail that may be of interest to those +intimately familiar with the X server architecture. The final appendix +describes the four phases of development that were performed during the +first two years of development. + + +The final year of work was divided into 9 tasks that are not +described in specific sections of this document. The major tasks during +that time were the enhancement of the reconfiguration ability added in +Phase IV, addition of support for a dynamic number of back-end displays +(instead of a hard-coded limit), and the support for back-end display +and input removal and addition. This work is mentioned in this paper, +but is not covered in detail. + + + + + + +Development plan + +This section describes the development plan from approximately June +2001 through July 2003. + + + +Bootstrap code + +To allow for rapid development of the DMX server by multiple +developers during the first development stage, the problem will be +broken down into three tasks: the overall DMX framework, back-end +rendering services and input device handling services. However, before +the work begins on these tasks, a simple framework that each developer +could use was implemented to bootstrap the development effort. This +framework renders to a single back-end server and provides dummy input +devices (i.e., the keyboard and mouse). The simple back-end rendering +service was implemented using the shadow framebuffer support currently +available in the XFree86 environment. + + +Using this bootstrapping framework, each developer has been able to +work on each of the tasks listed above independently as follows: the +framework will be extended to handle arbitrary back-end server +configurations; the back-end rendering services will be transitioned to +the more efficient Xnest-style implementation; and, an input device +framework to handle various input devices via the input extension will +be developed. + + +Status: The boot strap code is complete. + + + + + +Input device handling + +An X server (including the front-end X server) requires two core +input devices -- a keyboard and a pointer (mouse). These core devices +are handled and required by the core X11 protocol. Additional types of +input devices may be attached and utilized via the XInput extension. +These are usually referred to as ``XInput extension devices'', + + +There are some options as to how the front-end X server gets its core +input devices: + + + + Local Input. The physical input devices (e.g., keyboard and + mouse) can be attached directly to the front-end X server. In this + case, the keyboard and mouse on the machine running the front-end X + server will be used. The front-end will have drivers to read the + raw input from those devices and convert it into the required X + input events (e.g., key press/release, pointer button press/release, + pointer motion). The front-end keyboard driver will keep track of + keyboard properties such as key and modifier mappings, autorepeat + state, keyboard sound and led state. Similarly the front-end + pointer driver will keep track if pointer properties such as the + button mapping and movement acceleration parameters. With this + option, input is handled fully in the front-end X server, and the + back-end X servers are used in a display-only mode. This option was + implemented and works for a limited number of Linux-specific + devices. Adding additional local input devices for other + architectures is expected to be relatively simple. + + + The following options are available for implementing local input + devices: + + + + The XFree86 X server has modular input drivers that could + be adapted for this purpose. The mouse driver supports a wide + range of mouse types and interfaces, as well as a range of + Operating System platforms. The keyboard driver in XFree86 is + not currently as modular as the mouse driver, but could be made + so. The XFree86 X server also has a range of other input + drivers for extended input devices such as tablets and touch + screens. Unfortunately, the XFree86 drivers are generally + complex, often simultaneously providing support for multiple + devices across multiple architectures; and rely so heavily on + XFree86-specific helper-functions, that this option was not + pursued. + + + + + The kdrive X server in XFree86 has built-in drivers that + support PS/2 mice and keyboard under Linux. The mouse driver + can indirectly handle other mouse types if the Linux utility + gpm is used as to translate the native mouse protocol into + PS/2 mouse format. These drivers could be adapted and built in + to the front-end X server if this range of hardware and OS + support is sufficient. While much simpler than the XFree86 + drivers, the kdrive drivers were not used for the DMX + implementation. + + + + + Reimplementation of keyboard and mouse drivers from + scratch for the DMX framework. Because keyboard and mouse + drivers are relatively trivial to implement, this pathway was + selected. Other drivers in the X source tree were referenced, + and significant contributions from other drivers are noted in + the DMX source code. + + + + + + + + Backend Input. The front-end can make use of the core input + devices attached to one or more of the back-end X servers. Core + input events from multiple back-ends are merged into a single input + event stream. This can work sanely when only a single set of input + devices is used at any given time. The keyboard and pointer state + will be handled in the front-end, with changes propagated to the + back-end servers as needed. This option was implemented and works + well. Because the core pointer on a back-end controls the hardware + mouse on that back-end, core pointers cannot be treated as XInput + extension devices. However, all back-end XInput extensions devices + can be mapped to either DMX core or DMX XInput extension devices. + + + + + Console Input. The front-end server could create a console + window that is displayed on an X server independent of the back-end + X servers. This console window could display things like the + physical screen layout, and the front-end could get its core input + events from events delivered to the console window. This option was + implemented and works well. To help the human navigate, window + outlines are also displayed in the console window. Further, console + windows can be used as either core or XInput extension devices. + + + + + Other options were initially explored, but they were all + partial subsets of the options listed above and, hence, are + irrelevant. + + + + + + +Although extended input devices are not specifically mentioned in the +Distributed X requirements, the options above were all implemented so +that XInput extension devices were supported. + + +The bootstrap code (Xdmx) had dummy input devices, and these are +still supported in the final version. These do the necessary +initialization to satisfy the X server's requirements for core pointer +and keyboard devices, but no input events are ever generated. + + +Status: The input code is complete. Because of the complexity of the +XFree86 input device drivers (and their heavy reliance on XFree86 +infrastructure), separate low-level device drivers were implemented for +Xdmx. The following kinds of drivers are supported (in general, the +devices can be treated arbitrarily as "core" input devices or as XInput +"extension" devices; and multiple instances of different kinds of +devices can be simultaneously available): + + + A "dummy" device drive that never generates events. + + + + + "Local" input is from the low-level hardware on which the + Xdmx binary is running. This is the only area where using the + XFree86 driver infrastructure would have been helpful, and then + only partially, since good support for generic USB devices does + not yet exist in XFree86 (in any case, XFree86 and kdrive driver + code was used where possible). Currently, the following local + devices are supported under Linux (porting to other operating + systems should be fairly straightforward): + + Linux keyboard + Linux serial mouse (MS) + Linux PS/2 mouse + USB keyboard + USB mouse + USB generic device (e.g., joystick, gamepad, etc.) + + + + + + "Backend" input is taken from one or more of the back-end + displays. In this case, events are taken from the back-end X + server and are converted to Xdmx events. Care must be taken so + that the sprite moves properly on the display from which input + is being taken. + + + + + "Console" input is taken from an X window that Xdmx + creates on the operator's display (i.e., on the machine running + the Xdmx binary). When the operator's mouse is inside the + console window, then those events are converted to Xdmx events. + Several special features are available: the console can display + outlines of windows that are on the Xdmx display (to facilitate + navigation), the cursor can be confined to the console, and a + "fine" mode can be activated to allow very precise cursor + positioning. + + + + + + + + + + + +Output device handling + +The output of the DMX system displays rendering and windowing +requests across multiple screens. The screens are typically arranged in +a grid such that together they represent a single large display. + + +The output section of the DMX code consists of two parts. The first +is in the front-end proxy X server (Xdmx), which accepts client +connections, manages the windows, and potentially renders primitives but +does not actually display any of the drawing primitives. The second +part is the back-end X server(s), which accept commands from the +front-end server and display the results on their screens. + + + +Initialization + +The DMX front-end must first initialize its screens by connecting to +each of the back-end X servers and collecting information about each of +these screens. However, the information collected from the back-end X +servers might be inconsistent. Handling these cases can be difficult +and/or inefficient. For example, a two screen system has one back-end X +server running at 16bpp while the second is running at 32bpp. +Converting rendering requests (e.g., XPutImage() or XGetImage() +requests) to the appropriate bit depth can be very time consuming. +Analyzing these cases to determine how or even if it is possible to +handle them is required. The current Xinerama code handles many of +these cases (e.g., in PanoramiXConsolidate()) and will be used as a +starting point. In general, the best solution is to use homogeneous X +servers and display devices. Using back-end servers with the same depth +is a requirement of the final DMX implementation. + + +Once this screen consolidation is finished, the relative position of +each back-end X server's screen in the unified screen is initialized. A +full-screen window is opened on each of the back-end X servers, and the +cursor on each screen is turned off. The final DMX implementation can +also make use of a partial-screen window, or multiple windows per +back-end screen. + + + + +Handling rendering requests + +After initialization, X applications connect to the front-end server. +There are two possible implementations of how rendering and windowing +requests are handled in the DMX system: + + + + A shadow framebuffer is used in the front-end server as the + render target. In this option, all protocol requests are completely + handled in the front-end server. All state and resources are + maintained in the front-end including a shadow copy of the entire + framebuffer. The framebuffers attached to the back-end servers are + updated by XPutImage() calls with data taken directly from the + shadow framebuffer. + + + This solution suffers from two main problems. First, it does not + take advantage of any accelerated hardware available in the system. + Second, the size of the XPutImage() calls can be quite large and + thus will be limited by the bandwidth available. + + + The initial DMX implementation used a shadow framebuffer by + default. + + + + + Rendering requests are sent to each back-end server for + handling (as is done in the Xnest server described above). In this + option, certain protocol requests are handled in the front-end + server and certain requests are repackaged and then sent to the + back-end servers. The framebuffer is distributed across the + multiple back-end servers. Rendering to the framebuffer is handled + on each back-end and can take advantage of any acceleration + available on the back-end servers' graphics display device. State + is maintained both in the front and back-end servers. + + + This solution suffers from two main drawbacks. First, protocol + requests are sent to all back-end servers -- even those that will + completely clip the rendering primitive -- which wastes bandwidth + and processing time. Second, state is maintained both in the front- + and back-end servers. These drawbacks are not as severe as in + option 1 (above) and can either be overcome through optimizations or + are acceptable. Therefore, this option will be used in the final + implementation. + + + The final DMX implementation defaults to this mechanism, but also + supports the shadow framebuffer mechanism. Several optimizations + were implemented to eliminate the drawbacks of the default + mechanism. These optimizations are described the section below and + in Phase II of the Development Results (see appendix). + + + + + + +Status: Both the shadow framebuffer and Xnest-style code is complete. + + + + + + + +Optimizing DMX + +Initially, the Xnest-style solution's performance will be measured +and analyzed to determine where the performance bottlenecks exist. +There are four main areas that will be addressed. + + +First, to obtain reasonable interactivity with the first development +phase, XSync() was called after each protocol request. The XSync() +function flushes any pending protocol requests. It then waits for the +back-end to process the request and send a reply that the request has +completed. This happens with each back-end server and performance +greatly suffers. As a result of the way XSync() is called in the first +development phase, the batching that the X11 library performs is +effectively defeated. The XSync() call usage will be analyzed and +optimized by batching calls and performing them at regular intervals, +except where interactivity will suffer (e.g., on cursor movements). + + +Second, the initial Xnest-style solution described above sends the +repackaged protocol requests to all back-end servers regardless of +whether or not they would be completely clipped out. The requests that +are trivially rejected on the back-end server wastes the limited +bandwidth available. By tracking clipping changes in the DMX X server's +windowing code (e.g., by opening, closing, moving or resizing windows), +we can determine whether or not back-end windows are visible so that +trivial tests in the front-end server's GC ops drawing functions can +eliminate these unnecessary protocol requests. + + +Third, each protocol request will be analyzed to determine if it is +possible to break the request into smaller pieces at display boundaries. +The initial ones to be analyzed are put and get image requests since +they will require the greatest bandwidth to transmit data between the +front and back-end servers. Other protocol requests will be analyzed +and those that will benefit from breaking them into smaller requests +will be implemented. + + +Fourth, an extension is being considered that will allow font glyphs to +be transferred from the front-end DMX X server to each back-end server. +This extension will permit the front-end to handle all font requests and +eliminate the requirement that all back-end X servers share the exact +same fonts as the front-end server. We are investigating the +feasibility of this extension during this development phase. + + +Other potential optimizations will be determined from the performance +analysis. + + +Please note that in our initial design, we proposed optimizing BLT +operations (e.g., XCopyArea() and window moves) by developing an +extension that would allow individual back-end servers to directly copy +pixel data to other back-end servers. This potential optimization was +in response to the simple image movement implementation that required +potentially many calls to GetImage() and PutImage(). However, the +current Xinerama implementation handles these BLT operations +differently. Instead of copying data to and from screens, they generate +expose events -- just as happens in the case when a window is moved from +off a screen to on screen. This approach saves the limited bandwidth +available between front and back-end servers and is being standardized +with Xinerama. It also eliminates the potential setup problems and +security issues resulting from having each back-end server open +connections to all other back-end servers. Therefore, we suggest +accepting Xinerama's expose event solution. + + +Also note that the approach proposed in the second and third +optimizations might cause backing store algorithms in the back-end to be +defeated, so a DMX X server configuration flag will be added to disable +these optimizations. + + +Status: The optimizations proposed above are complete. It was +determined that the using the xfs font server was sufficient and +creating a new mechanism to pass glyphs was redundant; therefore, the +fourth optimization proposed above was not included in DMX. + + + + + + +DMX X extension support + +The DMX X server keeps track of all the windowing information on the +back-end X servers, but does not currently export this information to +any client applications. An extension will be developed to pass the +screen information and back-end window IDs to DMX-aware clients. These +clients can then use this information to directly connect to and render +to the back-end windows. Bypassing the DMX X server allows DMX-aware +clients to break up complex rendering requests on their own and send +them directly to the windows on the back-end server's screens. An +example of a client that can make effective use of this extension is +Chromium. + + +Status: The extension, as implemented, is fully documented in +"Client-to-Server DMX Extension to the X Protocol". Future changes +might be required based on feedback and other proposed enhancements to +DMX. Currently, the following facilities are supported: + + + Screen information (clipping rectangle for each screen relative + to the virtual screen) + + + Window information (window IDs and clipping information for each + back-end window that corresponds to each DMX window) + + + Input device information (mappings from DMX device IDs to + back-end device IDs) + + + Force window creation (so that a client can override the + server-side lazy window creation optimization) + + + Reconfiguration (so that a client can request that a screen + position be changed) + + + Addition and removal of back-end servers and back-end and + console inputs. + + + + + + + + +Common X extension support + +The XInput, XKeyboard and Shape extensions are commonly used +extensions to the base X11 protocol. XInput allows multiple and +non-standard input devices to be accessed simultaneously. These input +devices can be connected to either the front-end or back-end servers. +XKeyboard allows much better keyboard mappings control. Shape adds +support for arbitrarily shaped windows and is used by various window +managers. Nearly all potential back-end X servers make these extensions +available, and support for each one will be added to the DMX system. + + +In addition to the extensions listed above, support for the X +Rendering extension (Render) is being developed. Render adds digital +image composition to the rendering model used by the X Window System. +While this extension is still under development by Keith Packard of HP, +support for the current version will be added to the DMX system. + + +Support for the XTest extension was added during the first +development phase. + + + +Status: The following extensions are supported and are discussed in +more detail in Phase IV of the Development Results (see appendix): + BIG-REQUESTS, + DEC-XTRAP, + DMX, + DPMS, + Extended-Visual-Information, + GLX, + LBX, + RECORD, + RENDER, + SECURITY, + SHAPE, + SYNC, + X-Resource, + XC-APPGROUP, + XC-MISC, + XFree86-Bigfont, + XINERAMA, + XInputExtension, + XKEYBOARD, and + XTEST. + + + + + +OpenGL support + +OpenGL support using the Mesa code base exists in XFree86 release 4 +and later. Currently, the direct rendering infrastructure (DRI) +provides accelerated OpenGL support for local clients and unaccelerated +OpenGL support (i.e., software rendering) is provided for non-local +clients. + + +The single head OpenGL support in XFree86 4.x will be extended to use +the DMX system. When the front and back-end servers are on the same +physical hardware, it is possible to use the DRI to directly render to +the back-end servers. First, the existing DRI will be extended to +support multiple display heads, and then to support the DMX system. +OpenGL rendering requests will be direct rendering to each back-end X +server. The DRI will request the screen layout (either from the +existing Xinerama extension or a DMX-specific extension). Support for +synchronized swap buffers will also be added (on hardware that supports +it). Note that a single front-end server with a single back-end server +on the same physical machine can emulate accelerated indirect rendering. + + +When the front and back-end servers are on different physical +hardware or are using non-XFree86 4.x X servers, a mechanism to render +primitives across the back-end servers will be provided. There are +several options as to how this can be implemented. + + + + + The existing OpenGL support in each back-end server can be + used by repackaging rendering primitives and sending them to each + back-end server. This option is similar to the unoptimized + Xnest-style approach mentioned above. Optimization of this solution + is beyond the scope of this project and is better suited to other + distributed rendering systems. + + + + Rendering to a pixmap in the front-end server using the + current XFree86 4.x code, and then displaying to the back-ends via + calls to XPutImage() is another option. This option is similar to + the shadow frame buffer approach mentioned above. It is slower and + bandwidth intensive, but has the advantage that the back-end servers + are not required to have OpenGL support. + + + +These, and other, options will be investigated in this phase of the +work. + + +Work by others have made Chromium DMX-aware. Chromium will use the +DMX X protocol extension to obtain information about the back-end +servers and will render directly to those servers, bypassing DMX. + + +Status: OpenGL support by the glxProxy extension was implemented by +SGI and has been integrated into the DMX code base. + + + + + + + + +Current issues + +In this sections the current issues are outlined that require further +investigation. + + + +Fonts + +The font path and glyphs need to be the same for the front-end and +each of the back-end servers. Font glyphs could be sent to the back-end +servers as necessary but this would consume a significant amount of +available bandwidth during font rendering for clients that use many +different fonts (e.g., Netscape). Initially, the font server (xfs) will +be used to provide the fonts to both the front-end and back-end servers. +Other possibilities will be investigated during development. + + + + +Zero width rendering primitives + +To allow pixmap and on-screen rendering to be pixel perfect, all +back-end servers must render zero width primitives exactly the same as +the front-end renders the primitives to pixmaps. For those back-end +servers that do not exactly match, zero width primitives will be +automatically converted to one width primitives. This can be handled in +the front-end server via the GC state. + + + + +Output scaling + +With very large tiled displays, it might be difficult to read the +information on the standard X desktop. In particular, the cursor can be +easily lost and fonts could be difficult to read. Automatic primitive +scaling might prove to be very useful. We will investigate the +possibility of scaling the cursor and providing a set of alternate +pre-scaled fonts to replace the standard fonts that many applications +use (e.g., fixed). Other options for automatic scaling will also be +investigated. + + + + +Per-screen colormaps + +Each screen's default colormap in the set of back-end X servers +should be able to be adjusted via a configuration utility. This support +is would allow the back-end screens to be calibrated via custom gamma +tables. On 24-bit systems that support a DirectColor visual, this type +of correction can be accommodated. One possible implementation would be +to advertise to X client of the DMX server a TrueColor visual while +using DirectColor visuals on the back-end servers to implement this type +of color correction. Other options will be investigated. + + + + + + +Appendix + + +Background + +This section describes the existing Open Source architectures that +can be used to handle multiple screens and upon which this development +project is based. This section was written before the implementation +was finished, and may not reflect actual details of the implementation. +It is left for historical interest only. + + + +Core input device handling + +The following is a description of how core input devices are handled +by an X server. + + + +InitInput() + +InitInput() is a DDX function that is called at the start of each +server generation from the X server's main() function. Its purpose is +to determine what input devices are connected to the X server, register +them with the DIX and MI layers, and initialize the input event queue. +InitInput() does not have a return value, but the X server will abort if +either a core keyboard device or a core pointer device are not +registered. Extended input (XInput) devices can also be registered in +InitInput(). + + +InitInput() usually has implementation specific code to determine +which input devices are available. For each input device it will be +using, it calls AddInputDevice(): + + + +AddInputDevice() +This DIX function allocates the device structure, +registers a callback function (which handles device init, close, on and +off), and returns the input handle, which can be treated as opaque. It +is called once for each input device. + + + + + +Once input handles for core keyboard and core pointer devices have +been obtained from AddInputDevice(). If both core devices are not +registered, then the X server will exit with a fatal error when it +attempts to start the input devices in InitAndStartDevices(), which is +called directly after InitInput() (see below). + + +The core pointer device is then registered with the miPointer code +(which does the high level cursor handling). While this registration +is not necessary for correct miPointer operation in the current XFree86 +code, it is still done mostly for compatibility reasons. + + + + + +miRegisterPointerDevice() +This MI function registers the core +pointer's input handle with with the miPointer code. + + + + +The final part of InitInput() is the initialization of the input +event queue handling. In most cases, the event queue handling provided +in the MI layer is used. The primary XFree86 X server uses its own +event queue handling to support some special cases related to the XInput +extension and the XFree86-specific DGA extension. For our purposes, the +MI event queue handling should be suitable. It is initialized by +calling mieqInit(): + + + +mieqInit() +This MI function initializes the MI event queue for the +core devices, and is passed the public component of the input handles +for the two core devices. + + + + +If a wakeup handler is required to deliver synchronous input +events, it can be registered here by calling the DIX function +RegisterBlockAndWakeupHandlers(). (See the devReadInput() description +below.) + + + + +InitAndStartDevices() + +InitAndStartDevices() is a DIX function that is called immediately +after InitInput() from the X server's main() function. Its purpose is +to initialize each input device that was registered with +AddInputDevice(), enable each input device that was successfully +initialized, and create the list of enabled input devices. Once each +registered device is processed in this way, the list of enabled input +devices is checked to make sure that both a core keyboard device and +core pointer device were registered and successfully enabled. If not, +InitAndStartDevices() returns failure, and results in the the X server +exiting with a fatal error. + + +Each registered device is initialized by calling its callback +(dev->deviceProc) with the DEVICE_INIT argument: + + + +(*dev->deviceProc)(dev, DEVICE_INIT) + +This function initializes the +device structs with core information relevant to the device. + + +For pointer devices, this means specifying the number of buttons, +default button mapping, the function used to get motion events (usually +miPointerGetMotionEvents()), the function used to change/control the +core pointer motion parameters (acceleration and threshold), and the +motion buffer size. + + +For keyboard devices, this means specifying the keycode range, +default keycode to keysym mapping, default modifier mapping, and the +functions used to sound the keyboard bell and modify/control the +keyboard parameters (LEDs, bell pitch and duration, key click, which +keys are auto-repeating, etc). + + + + +Each initialized device is enabled by calling EnableDevice(): + + + +EnableDevice() + +EnableDevice() calls the device callback with +DEVICE_ON: + + + (*dev->deviceProc)(dev, DEVICE_ON) + + This typically opens and + initializes the relevant physical device, and when appropriate, + registers the device's file descriptor (or equivalent) as a valid + input source. + + + + + EnableDevice() then adds the device handle to the X server's + global list of enabled devices. + + + + +InitAndStartDevices() then verifies that a valid core keyboard and +pointer has been initialized and enabled. It returns failure if either +are missing. + + + + +devReadInput() + +Each device will have some function that gets called to read its +physical input. These may be called in a number of different ways. In +the case of synchronous I/O, they will be called from a DDX +wakeup-handler that gets called after the server detects that new input is +available. In the case of asynchronous I/O, they will be called from a +(SIGIO) signal handler triggered when new input is available. This +function should do at least two things: make sure that input events get +enqueued, and make sure that the cursor gets moved for motion events +(except if these are handled later by the driver's own event queue +processing function, which cannot be done when using the MI event queue +handling). + + +Events are queued by calling mieqEnqueue(): + + + +mieqEnqueue() + +This MI function is used to add input events to the +event queue. It is simply passed the event to be queued. + + + + +The cursor position should be updated when motion events are +enqueued, by calling either miPointerAbsoluteCursor() or +miPointerDeltaCursor(): + + + +miPointerAbsoluteCursor() + +This MI function is used to move the +cursor to the absolute coordinates provided. + + +miPointerDeltaCursor() + +This MI function is used to move the cursor +relative to its current position. + + + + + + +ProcessInputEvents() + +ProcessInputEvents() is a DDX function that is called from the X +server's main dispatch loop when new events are available in the input +event queue. It typically processes the enqueued events, and updates +the cursor/pointer position. It may also do other DDX-specific event +processing. + + +Enqueued events are processed by mieqProcessInputEvents() and passed +to the DIX layer for transmission to clients: + + + +mieqProcessInputEvents() + +This function processes each event in the +event queue, and passes it to the device's input processing function. +The DIX layer provides default functions to do this processing, and they +handle the task of getting the events passed back to the relevant +clients. + + +miPointerUpdate() + +This function resynchronized the cursor position +with the new pointer position. It also takes care of moving the cursor +between screens when needed in multi-head configurations. + + + + + + + +DisableDevice() + +DisableDevice is a DIX function that removes an input device from the +list of enabled devices. The result of this is that the device no +longer generates input events. The device's data structures are kept in +place, and disabling a device like this can be reversed by calling +EnableDevice(). DisableDevice() may be called from the DDX when it is +desirable to do so (e.g., the XFree86 server does this when VT +switching). Except for special cases, this is not normally called for +core input devices. + + +DisableDevice() calls the device's callback function with +DEVICE_OFF: + + + +(*dev->deviceProc)(dev, DEVICE_OFF) + +This typically closes the +relevant physical device, and when appropriate, unregisters the device's +file descriptor (or equivalent) as a valid input source. + + + + +DisableDevice() then removes the device handle from the X server's +global list of enabled devices. + + + + + +CloseDevice() + +CloseDevice is a DIX function that removes an input device from the +list of available devices. It disables input from the device and frees +all data structures associated with the device. This function is +usually called from CloseDownDevices(), which is called from main() at +the end of each server generation to close all input devices. + + +CloseDevice() calls the device's callback function with +DEVICE_CLOSE: + + + +(*dev->deviceProc)(dev, DEVICE_CLOSE) + +This typically closes the +relevant physical device, and when appropriate, unregisters the device's +file descriptor (or equivalent) as a valid input source. If any device +specific data structures were allocated when the device was initialized, +they are freed here. + + + + +CloseDevice() then frees the data structures that were allocated +for the device when it was registered/initialized. + + + + + +LegalModifier() + +LegalModifier() is a required DDX function that can be used to +restrict which keys may be modifier keys. This seems to be present for +historical reasons, so this function should simply return TRUE +unconditionally. + + + + + + +Output handling + +The following sections describe the main functions required to +initialize, use and close the output device(s) for each screen in the X +server. + + + +InitOutput() + +This DDX function is called near the start of each server generation +from the X server's main() function. InitOutput()'s main purpose is to +initialize each screen and fill in the global screenInfo structure for +each screen. It is passed three arguments: a pointer to the screenInfo +struct, which it is to initialize, and argc and argv from main(), which +can be used to determine additional configuration information. + + +The primary tasks for this function are outlined below: + + + + Parse configuration info: The first task of InitOutput() + is to parses any configuration information from the configuration + file. In addition to the XF86Config file, other configuration + information can be taken from the command line. The command line + options can be gathered either in InitOutput() or earlier in the + ddxProcessArgument() function, which is called by + ProcessCommandLine(). The configuration information determines the + characteristics of the screen(s). For example, in the XFree86 X + server, the XF86Config file specifies the monitor information, the + screen resolution, the graphics devices and slots in which they are + located, and, for Xinerama, the screens' layout. + + + + + Initialize screen info: The next task is to initialize + the screen-dependent internal data structures. For example, part of + what the XFree86 X server does is to allocate its screen and pixmap + private indices, probe for graphics devices, compare the probed + devices to the ones listed in the XF86Config file, and add the ones that + match to the internal xf86Screens[] structure. + + + + + Set pixmap formats: The next task is to initialize the + screenInfo's image byte order, bitmap bit order and bitmap scanline + unit/pad. The screenInfo's pixmap format's depth, bits per pixel + and scanline padding is also initialized at this stage. + + + + + Unify screen info: An optional task that might be done at + this stage is to compare all of the information from the various + screens and determines if they are compatible (i.e., if the set of + screens can be unified into a single desktop). This task has + potential to be useful to the DMX front-end server, if Xinerama's + PanoramiXConsolidate() function is not sufficient. + + + + + +Once these tasks are complete, the valid screens are known and each +of these screens can be initialized by calling AddScreen(). + + + + +AddScreen() + +This DIX function is called from InitOutput(), in the DDX layer, to +add each new screen to the screenInfo structure. The DDX screen +initialization function and command line arguments (i.e., argc and argv) +are passed to it as arguments. + + +This function first allocates a new Screen structure and any privates +that are required. It then initializes some of the fields in the Screen +struct and sets up the pixmap padding information. Finally, it calls +the DDX screen initialization function ScreenInit(), which is described +below. It returns the number of the screen that were just added, or -1 +if there is insufficient memory to add the screen or if the DDX screen +initialization fails. + + + + +ScreenInit() + +This DDX function initializes the rest of the Screen structure with +either generic or screen-specific functions (as necessary). It also +fills in various screen attributes (e.g., width and height in +millimeters, black and white pixel values). + + +The screen init function usually calls several functions to perform +certain screen initialization functions. They are described below: + + + +{mi,*fb}ScreenInit() + +The DDX layer's ScreenInit() function usually +calls another layer's ScreenInit() function (e.g., miScreenInit() or +fbScreenInit()) to initialize the fallbacks that the DDX driver does not +specifically handle. + + +After calling another layer's ScreenInit() function, any +screen-specific functions either wrap or replace the other layer's +function pointers. If a function is to be wrapped, each of the old +function pointers from the other layer are stored in a screen private +area. Common functions to wrap are CloseScreen() and SaveScreen(). + + + +miInitializeBackingStore() + +This MI function initializes the +screen's backing storage functions, which are used to save areas of +windows that are currently covered by other windows. + + + +miDCInitialize() + +This MI function initializes the MI cursor +display structures and function pointers. If a hardware cursor is used, +the DDX layer's ScreenInit() function will wrap additional screen and +the MI cursor display function pointers. + + + + +Another common task for ScreenInit() function is to initialize the +output device state. For example, in the XFree86 X server, the +ScreenInit() function saves the original state of the video card and +then initializes the video mode of the graphics device. + + + + +CloseScreen() + +This function restores any wrapped screen functions (and in +particular the wrapped CloseScreen() function) and restores the state of +the output device to its original state. It should also free any +private data it created during the screen initialization. + + + + +GC operations + +When the X server is requested to render drawing primitives, it does +so by calling drawing functions through the graphics context's operation +function pointer table (i.e., the GCOps functions). These functions +render the basic graphics operations such as drawing rectangles, lines, +text or copying pixmaps. Default routines are provided either by the MI +layer, which draws indirectly through a simple span interface, or by the +framebuffer layers (e.g., CFB, MFB, FB), which draw directly to a +linearly mapped frame buffer. + + +To take advantage of special hardware on the graphics device, +specific GCOps functions can be replaced by device specific code. +However, many times the graphics devices can handle only a subset of the +possible states of the GC, so during graphics context validation, +appropriate routines are selected based on the state and capabilities of +the hardware. For example, some graphics hardware can accelerate single +pixel width lines with certain dash patterns. Thus, for dash patterns +that are not supported by hardware or for width 2 or greater lines, the +default routine is chosen during GC validation. + + +Note that some pointers to functions that draw to the screen are +stored in the Screen structure. They include GetImage(), GetSpans(), +CopyWindow() and RestoreAreas(). + + + + +Xnest + +The Xnest X server is a special proxy X server that relays the X +protocol requests that it receives to a ``real'' X server that then +processes the requests and displays the results, if applicable. To the X +applications, Xnest appears as if it is a regular X server. However, +Xnest is both server to the X application and client of the real X +server, which will actually handle the requests. + + +The Xnest server implements all of the standard input and output +initialization steps outlined above. + + + + +InitOutput() + +Xnest takes its configuration information from +command line arguments via ddxProcessArguments(). This information +includes the real X server display to connect to, its default visual +class, the screen depth, the Xnest window's geometry, etc. Xnest then +connects to the real X server and gathers visual, colormap, depth and +pixmap information about that server's display, creates a window on that +server, which will be used as the root window for Xnest. + + +Next, Xnest initializes its internal data structures and uses the +data from the real X server's pixmaps to initialize its own pixmap +formats. Finally, it calls AddScreen(xnestOpenScreen, argc, argv) to +initialize each of its screens. + + + +ScreenInit() + +Xnest's ScreenInit() function is called +xnestOpenScreen(). This function initializes its screen's depth and +visual information, and then calls miScreenInit() to set up the default +screen functions. It then calls miInitializeBackingStore() and +miDCInitialize() to initialize backing store and the software cursor. +Finally, it replaces many of the screen functions with its own +functions that repackage and send the requests to the real X server to +which Xnest is attached. + + + +CloseScreen() + +This function frees its internal data structure +allocations. Since it replaces instead of wrapping screen functions, +there are no function pointers to unwrap. This can potentially lead to +problems during server regeneration. + + + +GC operations + +The GC operations in Xnest are very simple since +they leave all of the drawing to the real X server to which Xnest is +attached. Each of the GCOps takes the request and sends it to the +real X server using standard Xlib calls. For example, the X +application issues a XDrawLines() call. This function turns into a +protocol request to Xnest, which calls the xnestPolylines() function +through Xnest's GCOps function pointer table. The xnestPolylines() +function is only a single line, which calls XDrawLines() using the same +arguments that were passed into it. Other GCOps functions are very +similar. Two exceptions to the simple GCOps functions described above +are the image functions and the BLT operations. + + +The image functions, GetImage() and PutImage(), must use a temporary +image to hold the image to be put of the image that was just grabbed +from the screen while it is in transit to the real X server or the +client. When the image has been transmitted, the temporary image is +destroyed. + + +The BLT operations, CopyArea() and CopyPlane(), handle not only the +copy function, which is the same as the simple cases described above, +but also the graphics exposures that result when the GC's graphics +exposure bit is set to True. Graphics exposures are handled in a helper +function, xnestBitBlitHelper(). This function collects the exposure +events from the real X server and, if any resulting in regions being +exposed, then those regions are passed back to the MI layer so that it +can generate exposure events for the X application. + + + + +The Xnest server takes its input from the X server to which it is +connected. When the mouse is in the Xnest server's window, keyboard and +mouse events are received by the Xnest server, repackaged and sent back +to any client that requests those events. + + + + +Shadow framebuffer + +The most common type of framebuffer is a linear array memory that +maps to the video memory on the graphics device. However, accessing +that video memory over an I/O bus (e.g., ISA or PCI) can be slow. The +shadow framebuffer layer allows the developer to keep the entire +framebuffer in main memory and copy it back to video memory at regular +intervals. It also has been extended to handle planar video memory and +rotated framebuffers. + + +There are two main entry points to the shadow framebuffer code: + + + +shadowAlloc(width, height, bpp) + +This function allocates the in +memory copy of the framebuffer of size width*height*bpp. It returns a +pointer to that memory, which will be used by the framebuffer +ScreenInit() code during the screen's initialization. + + + +shadowInit(pScreen, updateProc, windowProc) + +This function +initializes the shadow framebuffer layer. It wraps several screen +drawing functions, and registers a block handler that will update the +screen. The updateProc is a function that will copy the damaged regions +to the screen, and the windowProc is a function that is used when the +entire linear video memory range cannot be accessed simultaneously so +that only a window into that memory is available (e.g., when using the +VGA aperture). + + + + +The shadow framebuffer code keeps track of the damaged area of each +screen by calculating the bounding box of all drawing operations that +have occurred since the last screen update. Then, when the block handler +is next called, only the damaged portion of the screen is updated. + + +Note that since the shadow framebuffer is kept in main memory, all +drawing operations are performed by the CPU and, thus, no accelerated +hardware drawing operations are possible. + + + + + + +Xinerama + +Xinerama is an X extension that allows multiple physical screens +controlled by a single X server to appear as a single screen. Although +the extension allows clients to find the physical screen layout via +extension requests, it is completely transparent to clients at the core +X11 protocol level. The original public implementation of Xinerama came +from Digital/Compaq. XFree86 rewrote it, filling in some missing pieces +and improving both X11 core protocol compliance and performance. The +Xinerama extension will be passing through X.Org's standardization +process in the near future, and the sample implementation will be based +on this rewritten version. + + +The current implementation of Xinerama is based primarily in the DIX +(device independent) and MI (machine independent) layers of the X +server. With few exceptions the DDX layers do not need any changes to +support Xinerama. X server extensions often do need modifications to +provide full Xinerama functionality. + + +The following is a code-level description of how Xinerama functions. + + +Note: Because the Xinerama extension was originally called the +PanoramiX extension, many of the Xinerama functions still have the +PanoramiX prefix. + + + + +PanoramiXExtensionInit() + + PanoramiXExtensionInit() is a + device-independent extension function that is called at the start of + each server generation from InitExtensions(), which is called from + the X server's main() function after all output devices have been + initialized, but before any input devices have been initialized. + + + PanoramiXNumScreens is set to the number of physical screens. If + only one physical screen is present, the extension is disabled, and + PanoramiXExtensionInit() returns without doing anything else. + + + The Xinerama extension is registered by calling AddExtension(). + + + GC and Screen private + indexes are allocated, and both GC and Screen private areas are + allocated for each physical screen. These hold Xinerama-specific + per-GC and per-Screen data. Each screen's CreateGC and CloseScreen + functions are wrapped by XineramaCreateGC() and + XineramaCloseScreen() respectively. Some new resource classes are + created for Xinerama drawables and GCs, and resource types for + Xinerama windows, pixmaps and colormaps. + + + A region (PanoramiXScreenRegion) is + initialized to be the union of the screen regions. + The relative positioning information for the + physical screens is taken from the ScreenRec x and y members, which + the DDX layer must initialize in InitOutput(). The bounds of the + combined screen is also calculated (PanoramiXPixWidth and + PanoramiXPixHeight). + + + The DIX layer has a list of function pointers + (ProcVector[]) that + holds the entry points for the functions that process core protocol + requests. The requests that Xinerama must intercept and break up + into physical screen-specific requests are wrapped. The original + set is copied to SavedProcVector[]. The types of requests + intercepted are Window requests, GC requests, colormap requests, + drawing requests, and some geometry-related requests. This wrapping + allows the bulk of the protocol request processing to be handled + transparently to the DIX layer. Some operations cannot be dealt with + in this way and are handled with Xinerama-specific code within the + DIX layer. + + + + +PanoramiXConsolidate() + + PanoramiXConsolidate() is a + device-independent extension function that is called directly from + the X server's main() function after extensions and input/output + devices have been initialized, and before the root windows are + defined and initialized. + + + This function finds the set of depths (PanoramiXDepths[]) and + visuals (PanoramiXVisuals[]) + common to all of the physical screens. + PanoramiXNumDepths is set to the number of common depths, and + PanoramiXNumVisuals is set to the number of common visuals. + Resources are created for the single root window and the default + colormap. Each of these resources has per-physical screen entries. + + + + +PanoramiXCreateConnectionBlock() + + PanoramiXConsolidate() is a + device-independent extension function that is called directly from + the X server's main() function after the per-physical screen root + windows are created. It is called instead of the standard DIX + CreateConnectionBlock() function. If this function returns FALSE, + the X server exits with a fatal error. This function will return + FALSE if no common depths were found in PanoramiXConsolidate(). + With no common depths, Xinerama mode is not possible. + + + The connection block holds the information that clients get when + they open a connection to the X server. It includes information + such as the supported pixmap formats, number of screens and the + sizes, depths, visuals, default colormap information, etc, for each + of the screens (much of information that xdpyinfo shows). The + connection block is initialized with the combined single screen + values that were calculated in the above two functions. + + + The Xinerama extension allows the registration of connection + block callback functions. The purpose of these is to allow other + extensions to do processing at this point. These callbacks can be + registered by calling XineramaRegisterConnectionBlockCallback() from + the other extension's ExtensionInit() function. Each registered + connection block callback is called at the end of + PanoramiXCreateConnectionBlock(). + + + + + +Xinerama-specific changes to the DIX code + +There are a few types of Xinerama-specific changes within the DIX +code. The main ones are described here. + + +Functions that deal with colormap or GC -related operations outside of +the intercepted protocol requests have a test added to only do the +processing for screen numbers > 0. This is because they are handled for +the single Xinerama screen and the processing is done once for screen 0. + + +The handling of motion events does some coordinate translation between +the physical screen's origin and screen zero's origin. Also, motion +events must be reported relative to the composite screen origin rather +than the physical screen origins. + + +There is some special handling for cursor, window and event processing +that cannot (either not at all or not conveniently) be done via the +intercepted protocol requests. A particular case is the handling of +pointers moving between physical screens. + + + + +Xinerama-specific changes to the MI code + +The only Xinerama-specific change to the MI code is in miSendExposures() +to handle the coordinate (and window ID) translation for expose events. + + + + +Intercepted DIX core requests + +Xinerama breaks up drawing requests for dispatch to each physical +screen. It also breaks up windows into pieces for each physical screen. +GCs are translated into per-screen GCs. Colormaps are replicated on +each physical screen. The functions handling the intercepted requests +take care of breaking the requests and repackaging them so that they can +be passed to the standard request handling functions for each screen in +turn. In addition, and to aid the repackaging, the information from +many of the intercepted requests is used to keep up to date the +necessary state information for the single composite screen. Requests +(usually those with replies) that can be satisfied completely from this +stored state information do not call the standard request handling +functions. + + + + + + + + + + + +Development Results + +In this section the results of each phase of development are +discussed. This development took place between approximately June 2001 +and July 2003. + + + +Phase I + +The initial development phase dealt with the basic implementation +including the bootstrap code, which used the shadow framebuffer, and the +unoptimized implementation, based on an Xnest-style implementation. + + + +Scope + +The goal of Phase I is to provide fundamental functionality that can +act as a foundation for ongoing work: + + + Develop the proxy X server + + + The proxy X server will operate on the X11 protocol and + relay requests as necessary to correctly perform the request. + + + Work will be based on the existing work for Xinerama and + Xnest. + + + Input events and windowing operations are handled in the + proxy server and rendering requests are repackaged and sent to + each of the back-end servers for display. + + + The multiple screen layout (including support for + overlapping screens) will be user configurable via a + configuration file or through the configuration tool. + + + + + Develop graphical configuration tool + + + There will be potentially a large number of X servers to + configure into a single display. The tool will allow the user + to specify which servers are involved in the configuration and + how they should be laid out. + + + + + Pass the X Test Suite + + + The X Test Suite covers the basic X11 operations. All + tests known to succeed must correctly operate in the distributed + X environment. + + + + + + + +For this phase, the back-end X servers are assumed to be unmodified X +servers that do not support any DMX-related protocol extensions; future +optimization pathways are considered, but are not implemented; and the +configuration tool is assumed to rely only on libraries in the X source +tree (e.g., Xt). + + + + +Results + +The proxy X server, Xdmx, was developed to distribute X11 protocol +requests to the set of back-end X servers. It opens a window on each +back-end server, which represents the part of the front-end's root +window that is visible on that screen. It mirrors window, pixmap and +other state in each back-end server. Drawing requests are sent to +either windows or pixmaps on each back-end server. This code is based +on Xnest and uses the existing Xinerama extension. + + +Input events can be taken from (1) devices attached to the back-end +server, (2) core devices attached directly to the Xdmx server, or (3) +from a ``console'' window on another X server. Events for these devices +are gathered, processed and delivered to clients attached to the Xdmx +server. + + +An intuitive configuration format was developed to help the user +easily configure the multiple back-end X servers. It was defined (see +grammar in Xdmx man page) and a parser was implemented that is used by +the Xdmx server and by a standalone xdmxconfig utility. The parsing +support was implemented such that it can be easily factored out of the X +source tree for use with other tools (e.g., vdl). Support for +converting legacy vdl-format configuration files to the DMX format is +provided by the vdltodmx utility. + + +Originally, the configuration file was going to be a subsection of +XFree86's XF86Config file, but that was not possible since Xdmx is a +completely separate X server. Thus, a separate config file format was +developed. In addition, a graphical configuration +tool, xdmxconfig, was developed to allow the user to create and arrange +the screens in the configuration file. The -configfile and -config +command-line options can be used to start Xdmx using a configuration +file. + + +An extension that enables remote input testing is required for the X +Test Suite to function. During this phase, this extension (XTEST) was +implemented in the Xdmx server. The results from running the X Test +Suite are described in detail below. + + + + +X Test Suite + + + Introduction + + The X Test Suite contains tests that verify Xlib functions + operate correctly. The test suite is designed to run on a + single X server; however, since X applications will not be + able to tell the difference between the DMX server and a + standard X server, the X Test Suite should also run on the + DMX server. + + + The Xdmx server was tested with the X Test Suite, and the + existing failures are noted in this section. To put these + results in perspective, we first discuss expected X Test + failures and how errors in underlying systems can impact + Xdmx test results. + + + + + Expected Failures for a Single Head + + A correctly implemented X server with a single screen is + expected to fail certain X Test tests. The following + well-known errors occur because of rounding error in the X + server code: + +XDrawArc: Tests 42, 63, 66, 73 +XDrawArcs: Tests 45, 66, 69, 76 + + + + The following failures occur because of the high-level X + server implementation: + +XLoadQueryFont: Test 1 +XListFontsWithInfo: Tests 3, 4 +XQueryFont: Tests 1, 2 + + + + The following test fails when running the X server as root + under Linux because of the way directory modes are + interpreted: + +XWriteBitmapFile: Test 3 + + + + Depending on the video card used for the back-end, other + failures may also occur because of bugs in the low-level + driver implementation. Over time, failures of this kind + are usually fixed by XFree86, but will show up in Xdmx + testing until then. + + + + + Expected Failures for Xinerama + + Xinerama fails several X Test Suite tests because of + design decisions made for the current implementation of + Xinerama. Over time, many of these errors will be + corrected by XFree86 and the group working on a new + Xinerama implementation. Therefore, Xdmx will also share + X Suite Test failures with Xinerama. + + + + We may be able to fix or work-around some of these + failures at the Xdmx level, but this will require + additional exploration that was not part of Phase I. + + + + Xinerama is constantly improving, and the list of + Xinerama-related failures depends on XFree86 version and + the underlying graphics hardware. We tested with a + variety of hardware, including nVidia, S3, ATI Radeon, + and Matrox G400 (in dual-head mode). The list below + includes only those failures that appear to be from the + Xinerama layer, and does not include failures listed in + the previous section, or failures that appear to be from + the low-level graphics driver itself: + + + + These failures were noted with multiple Xinerama + configurations: + +XCopyPlane: Tests 13, 22, 31 (well-known Xinerama implementation issue) +XSetFontPath: Test 4 +XGetDefault: Test 5 +XMatchVisualInfo: Test 1 + + + + These failures were noted only when using one dual-head + video card with a 4.2.99.x XFree86 server: + +XListPixmapFormats: Test 1 +XDrawRectangles: Test 45 + + + + These failures were noted only when using two video cards + from different vendors with a 4.1.99.x XFree86 server: + +XChangeWindowAttributes: Test 32 +XCreateWindow: Test 30 +XDrawLine: Test 22 +XFillArc: Test 22 +XChangeKeyboardControl: Tests 9, 10 +XRebindKeysym: Test 1 + + + + + + Additional Failures from Xdmx + + + When running Xdmx, no unexpected failures were noted. + Since the Xdmx server is based on Xinerama, we expect to + have most of the Xinerama failures present in the Xdmx + server. Similarly, since the Xdmx server must rely on the + low-level device drivers on each back-end server, we also + expect that Xdmx will exhibit most of the back-end + failures. Here is a summary: + +XListPixmapFormats: Test 1 (configuration dependent) +XChangeWindowAttributes: Test 32 +XCreateWindow: Test 30 +XCopyPlane: Test 13, 22, 31 +XSetFontPath: Test 4 +XGetDefault: Test 5 (configuration dependent) +XMatchVisualInfo: Test 1 +XRebindKeysym: Test 1 (configuration dependent) + + + + Note that this list is shorter than the combined list for + Xinerama because Xdmx uses different code paths to perform + some Xinerama operations. Further, some Xinerama failures + have been fixed in the XFree86 4.2.99.x CVS repository. + + + + + Summary and Future Work + + + Running the X Test Suite on Xdmx does not produce any + failures that cannot be accounted for by the underlying + Xinerama subsystem used by the front-end or by the + low-level device-driver code running on the back-end X + servers. The Xdmx server therefore is as ``correct'' as + possible with respect to the standard set of X Test Suite + tests. + + + + During the following phases, we will continue to verify + Xdmx correctness using the X Test Suite. We may also use + other tests suites or write additional tests that run + under the X Test Suite that specifically verify the + expected behavior of DMX. + + + + + +Fonts + +In Phase I, fonts are handled directly by both the front-end and the +back-end servers, which is required since we must treat each back-end +server during this phase as a ``black box''. What this requires is that +the front- and back-end servers must share the exact same font +path. There are two ways to help make sure that all servers share the +same font path: + + + + First, each server can be configured to use the same font + server. The font server, xfs, can be configured to serve fonts to + multiple X servers via TCP. + + + + Second, each server can be configured to use the same font + path and either those font paths can be copied to each back-end + machine or they can be mounted (e.g., via NFS) on each back-end + machine. + + + + +One additional concern is that a client program can set its own font +path, and if it does so, then that font path must be available on each +back-end machine. + + +The -fontpath command line option was added to allow users to +initialize the font path of the front end server. This font path is +propagated to each back-end server when the default font is loaded. If +there are any problems, an error message is printed, which will describe +the problem and list the current font path. For more information about +setting the font path, see the -fontpath option description in the man +page. + + + + +Performance + +Phase I of development was not intended to optimize performance. Its +focus was on completely and correctly handling the base X11 protocol in +the Xdmx server. However, several insights were gained during Phase I, +which are listed here for reference during the next phase of +development. + + + + + Calls to XSync() can slow down rendering since it requires a + complete round trip to and from a back-end server. This is + especially problematic when communicating over long haul networks. + + + + Sending drawing requests to only the screens that they overlap + should improve performance. + + + + + +Pixmaps + +Pixmaps were originally expected to be handled entirely in the +front-end X server; however, it was found that this overly complicated +the rendering code and would have required sending potentially large +images to each back server that required them when copying from pixmap +to screen. Thus, pixmap state is mirrored in the back-end server just +as it is with regular window state. With this implementation, the same +rendering code that draws to windows can be used to draw to pixmaps on +the back-end server, and no large image transfers are required to copy +from pixmap to window. + + + + + + + + +Phase II + +The second phase of development concentrates on performance +optimizations. These optimizations are documented here, with +x11perf data to show how the optimizations improve performance. + + +All benchmarks were performed by running Xdmx on a dual processor +1.4GHz AMD Athlon machine with 1GB of RAM connecting over 100baseT to +two single-processor 1GHz Pentium III machines with 256MB of RAM and ATI +Rage 128 (RF) video cards. The front end was running Linux +2.4.20-pre1-ac1 and the back ends were running Linux 2.4.7-10 and +version 4.2.99.1 of XFree86 pulled from the XFree86 CVS repository on +August 7, 2002. All systems were running Red Hat Linux 7.2. + + + +Moving from XFree86 4.1.99.1 to 4.2.0.0 + +For phase II, the working source tree was moved to the branch tagged +with dmx-1-0-branch and was updated from version 4.1.99.1 (20 August +2001) of the XFree86 sources to version 4.2.0.0 (18 January 2002). +After this update, the following tests were noted to be more than 10% +faster: + +1.13 Fill 300x300 opaque stippled trapezoid (161x145 stipple) +1.16 Fill 1x1 tiled trapezoid (161x145 tile) +1.13 Fill 10x10 tiled trapezoid (161x145 tile) +1.17 Fill 100x100 tiled trapezoid (161x145 tile) +1.16 Fill 1x1 tiled trapezoid (216x208 tile) +1.20 Fill 10x10 tiled trapezoid (216x208 tile) +1.15 Fill 100x100 tiled trapezoid (216x208 tile) +1.37 Circulate Unmapped window (200 kids) + +And the following tests were noted to be more than 10% slower: + +0.88 Unmap window via parent (25 kids) +0.75 Circulate Unmapped window (4 kids) +0.79 Circulate Unmapped window (16 kids) +0.80 Circulate Unmapped window (25 kids) +0.82 Circulate Unmapped window (50 kids) +0.85 Circulate Unmapped window (75 kids) + + + +These changes were not caused by any changes in the DMX system, and +may point to changes in the XFree86 tree or to tests that have more +"jitter" than most other x11perf tests. + + + + +Global changes + +During the development of the Phase II DMX server, several global +changes were made. These changes were also compared with the Phase I +server. The following tests were noted to be more than 10% faster: + +1.13 Fill 300x300 opaque stippled trapezoid (161x145 stipple) +1.15 Fill 1x1 tiled trapezoid (161x145 tile) +1.13 Fill 10x10 tiled trapezoid (161x145 tile) +1.17 Fill 100x100 tiled trapezoid (161x145 tile) +1.16 Fill 1x1 tiled trapezoid (216x208 tile) +1.19 Fill 10x10 tiled trapezoid (216x208 tile) +1.15 Fill 100x100 tiled trapezoid (216x208 tile) +1.15 Circulate Unmapped window (4 kids) + + + +The following tests were noted to be more than 10% slower: + +0.69 Scroll 10x10 pixels +0.68 Scroll 100x100 pixels +0.68 Copy 10x10 from window to window +0.68 Copy 100x100 from window to window +0.76 Circulate Unmapped window (75 kids) +0.83 Circulate Unmapped window (100 kids) + + + +For the remainder of this analysis, the baseline of comparison will +be the Phase II deliverable with all optimizations disabled (unless +otherwise noted). This will highlight how the optimizations in +isolation impact performance. + + + + +XSync() Batching + +During the Phase I implementation, XSync() was called after every +protocol request made by the DMX server. This provided the DMX server +with an interactive feel, but defeated X11's protocol buffering system +and introduced round-trip wire latency into every operation. During +Phase II, DMX was changed so that protocol requests are no longer +followed by calls to XSync(). Instead, the need for an XSync() is +noted, and XSync() calls are only made every 100mS or when the DMX +server specifically needs to make a call to guarantee interactivity. +With this new system, X11 buffers protocol as much as possible during a +100mS interval, and many unnecessary XSync() calls are avoided. + + +Out of more than 300 x11perf tests, 8 tests became more than 100 +times faster, with 68 more than 50X faster, 114 more than 10X faster, +and 181 more than 2X faster. See table below for summary. + + +The following tests were noted to be more than 10% slower with +XSync() batching on: + +0.88 500x500 tiled rectangle (161x145 tile) +0.89 Copy 500x500 from window to window + + + + + +Offscreen Optimization + +Windows span one or more of the back-end servers' screens; however, +during Phase I development, windows were created on every back-end +server and every rendering request was sent to every window regardless +of whether or not that window was visible. With the offscreen +optimization, the DMX server tracks when a window is completely off of a +back-end server's screen and, in that case, it does not send rendering +requests to those back-end windows. This optimization saves bandwidth +between the front and back-end servers, and it reduces the number of +XSync() calls. The performance tests were run on a DMX system with only +two back-end servers. Greater performance gains will be had as the +number of back-end servers increases. + + +Out of more than 300 x11perf tests, 3 tests were at least twice as +fast, and 146 tests were at least 10% faster. Two tests were more than +10% slower with the offscreen optimization: + +0.88 Hide/expose window via popup (4 kids) +0.89 Resize unmapped window (75 kids) + + + + + +Lazy Window Creation Optimization + +As mentioned above, during Phase I, windows were created on every +back-end server even if they were not visible on that back-end. With +the lazy window creation optimization, the DMX server does not create +windows on a back-end server until they are either visible or they +become the parents of a visible window. This optimization builds on the +offscreen optimization (described above) and requires it to be enabled. + + +The lazy window creation optimization works by creating the window +data structures in the front-end server when a client creates a window, +but delays creation of the window on the back-end server(s). A private +window structure in the DMX server saves the relevant window data and +tracks changes to the window's attributes and stacking order for later +use. The only times a window is created on a back-end server are (1) +when it is mapped and is at least partially overlapping the back-end +server's screen (tracked by the offscreen optimization), or (2) when the +window becomes the parent of a previously visible window. The first +case occurs when a window is mapped or when a visible window is copied, +moved or resized and now overlaps the back-end server's screen. The +second case occurs when starting a window manager after having created +windows to which the window manager needs to add decorations. + + +When either case occurs, a window on the back-end server is created +using the data saved in the DMX server's window private data structure. +The stacking order is then adjusted to correctly place the window on the +back-end and lastly the window is mapped. From this time forward, the +window is handled exactly as if the window had been created at the time +of the client's request. + + +Note that when a window is no longer visible on a back-end server's +screen (e.g., it is moved offscreen), the window is not destroyed; +rather, it is kept and reused later if the window once again becomes +visible on the back-end server's screen. Originally with this +optimization, destroying windows was implemented but was later rejected +because it increased bandwidth when windows were opaquely moved or +resized, which is common in many window managers. + + +The performance tests were run on a DMX system with only two back-end +servers. Greater performance gains will be had as the number of +back-end servers increases. + + +This optimization improved the following x11perf tests by more +than 10%: + +1.10 500x500 rectangle outline +1.12 Fill 100x100 stippled trapezoid (161x145 stipple) +1.20 Circulate Unmapped window (50 kids) +1.19 Circulate Unmapped window (75 kids) + + + + + +Subdividing Rendering Primitives + +X11 imaging requests transfer significant data between the client and +the X server. During Phase I, the DMX server would then transfer the +image data to each back-end server. Even with the offscreen +optimization (above), these requests still required transferring +significant data to each back-end server that contained a visible +portion of the window. For example, if the client uses XPutImage() to +copy an image to a window that overlaps the entire DMX screen, then the +entire image is copied by the DMX server to every back-end server. + + +To reduce the amount of data transferred between the DMX server and +the back-end servers when XPutImage() is called, the image data is +subdivided and only the data that will be visible on a back-end server's +screen is sent to that back-end server. Xinerama already implements a +subdivision algorithm for XGetImage() and no further optimization was +needed. + + +Other rendering primitives were analyzed, but the time required to +subdivide these primitives was a significant proportion of the time +required to send the entire rendering request to the back-end server, so +this optimization was rejected for the other rendering primitives. + + +Again, the performance tests were run on a DMX system with only two +back-end servers. Greater performance gains will be had as the number +of back-end servers increases. + + +This optimization improved the following x11perf tests by more +than 10%: + +1.12 Fill 100x100 stippled trapezoid (161x145 stipple) +1.26 PutImage 10x10 square +1.83 PutImage 100x100 square +1.91 PutImage 500x500 square +1.40 PutImage XY 10x10 square +1.48 PutImage XY 100x100 square +1.50 PutImage XY 500x500 square +1.45 Circulate Unmapped window (75 kids) +1.74 Circulate Unmapped window (100 kids) + + + +The following test was noted to be more than 10% slower with this +optimization: + +0.88 10-pixel fill chord partial circle + + + + + +Summary of x11perf Data + +With all of the optimizations on, 53 x11perf tests are more than +100X faster than the unoptimized Phase II deliverable, with 69 more than +50X faster, 73 more than 10X faster, and 199 more than twice as fast. +No tests were more than 10% slower than the unoptimized Phase II +deliverable. (Compared with the Phase I deliverable, only Circulate +Unmapped window (100 kids) was more than 10% slower than the Phase II +deliverable. As noted above, this test seems to have wider variability +than other x11perf tests.) + + +The following table summarizes relative x11perf test changes for +all optimizations individually and collectively. Note that some of the +optimizations have a synergistic effect when used together. + + +1: XSync() batching only +2: Off screen optimizations only +3: Window optimizations only +4: Subdivprims only +5: All optimizations + + 1 2 3 4 5 Operation +------ ---- ---- ---- ------ --------- + 2.14 1.85 1.00 1.00 4.13 Dot + 1.67 1.80 1.00 1.00 3.31 1x1 rectangle + 2.38 1.43 1.00 1.00 2.44 10x10 rectangle + 1.00 1.00 0.92 0.98 1.00 100x100 rectangle + 1.00 1.00 1.00 1.00 1.00 500x500 rectangle + 1.83 1.85 1.05 1.06 3.54 1x1 stippled rectangle (8x8 stipple) + 2.43 1.43 1.00 1.00 2.41 10x10 stippled rectangle (8x8 stipple) + 0.98 1.00 1.00 1.00 1.00 100x100 stippled rectangle (8x8 stipple) + 1.00 1.00 1.00 1.00 0.98 500x500 stippled rectangle (8x8 stipple) + 1.75 1.75 1.00 1.00 3.40 1x1 opaque stippled rectangle (8x8 stipple) + 2.38 1.42 1.00 1.00 2.34 10x10 opaque stippled rectangle (8x8 stipple) + 1.00 1.00 0.97 0.97 1.00 100x100 opaque stippled rectangle (8x8 stipple) + 1.00 1.00 1.00 1.00 0.99 500x500 opaque stippled rectangle (8x8 stipple) + 1.82 1.82 1.04 1.04 3.56 1x1 tiled rectangle (4x4 tile) + 2.33 1.42 1.00 1.00 2.37 10x10 tiled rectangle (4x4 tile) + 1.00 0.92 1.00 1.00 1.00 100x100 tiled rectangle (4x4 tile) + 1.00 1.00 1.00 1.00 1.00 500x500 tiled rectangle (4x4 tile) + 1.94 1.62 1.00 1.00 3.66 1x1 stippled rectangle (17x15 stipple) + 1.74 1.28 1.00 1.00 1.73 10x10 stippled rectangle (17x15 stipple) + 1.00 1.00 1.00 0.89 0.98 100x100 stippled rectangle (17x15 stipple) + 1.00 1.00 1.00 1.00 0.98 500x500 stippled rectangle (17x15 stipple) + 1.94 1.62 1.00 1.00 3.67 1x1 opaque stippled rectangle (17x15 stipple) + 1.69 1.26 1.00 1.00 1.66 10x10 opaque stippled rectangle (17x15 stipple) + 1.00 0.95 1.00 1.00 1.00 100x100 opaque stippled rectangle (17x15 stipple) + 1.00 1.00 1.00 1.00 0.97 500x500 opaque stippled rectangle (17x15 stipple) + 1.93 1.61 0.99 0.99 3.69 1x1 tiled rectangle (17x15 tile) + 1.73 1.27 1.00 1.00 1.72 10x10 tiled rectangle (17x15 tile) + 1.00 1.00 1.00 1.00 0.98 100x100 tiled rectangle (17x15 tile) + 1.00 1.00 0.97 0.97 1.00 500x500 tiled rectangle (17x15 tile) + 1.95 1.63 1.00 1.00 3.83 1x1 stippled rectangle (161x145 stipple) + 1.80 1.30 1.00 1.00 1.83 10x10 stippled rectangle (161x145 stipple) + 0.97 1.00 1.00 1.00 1.01 100x100 stippled rectangle (161x145 stipple) + 1.00 1.00 1.00 1.00 0.98 500x500 stippled rectangle (161x145 stipple) + 1.95 1.63 1.00 1.00 3.56 1x1 opaque stippled rectangle (161x145 stipple) + 1.65 1.25 1.00 1.00 1.68 10x10 opaque stippled rectangle (161x145 stipple) + 1.00 1.00 1.00 1.00 1.01 100x100 opaque stippled rectangle (161x145... + 1.00 1.00 1.00 1.00 0.97 500x500 opaque stippled rectangle (161x145... + 1.95 1.63 0.98 0.99 3.80 1x1 tiled rectangle (161x145 tile) + 1.67 1.26 1.00 1.00 1.67 10x10 tiled rectangle (161x145 tile) + 1.13 1.14 1.14 1.14 1.14 100x100 tiled rectangle (161x145 tile) + 0.88 1.00 1.00 1.00 0.99 500x500 tiled rectangle (161x145 tile) + 1.93 1.63 1.00 1.00 3.53 1x1 tiled rectangle (216x208 tile) + 1.69 1.26 1.00 1.00 1.66 10x10 tiled rectangle (216x208 tile) + 1.00 1.00 1.00 1.00 1.00 100x100 tiled rectangle (216x208 tile) + 1.00 1.00 1.00 1.00 1.00 500x500 tiled rectangle (216x208 tile) + 1.82 1.70 1.00 1.00 3.38 1-pixel line segment + 2.07 1.56 0.90 1.00 3.31 10-pixel line segment + 1.29 1.10 1.00 1.00 1.27 100-pixel line segment + 1.05 1.06 1.03 1.03 1.09 500-pixel line segment + 1.30 1.13 1.00 1.00 1.29 100-pixel line segment (1 kid) + 1.32 1.15 1.00 1.00 1.32 100-pixel line segment (2 kids) + 1.33 1.16 1.00 1.00 1.33 100-pixel line segment (3 kids) + 1.92 1.64 1.00 1.00 3.73 10-pixel dashed segment + 1.34 1.16 1.00 1.00 1.34 100-pixel dashed segment + 1.24 1.11 0.99 0.97 1.23 100-pixel double-dashed segment + 1.72 1.77 1.00 1.00 3.25 10-pixel horizontal line segment + 1.83 1.66 1.01 1.00 3.54 100-pixel horizontal line segment + 1.86 1.30 1.00 1.00 1.84 500-pixel horizontal line segment + 2.11 1.52 1.00 0.99 3.02 10-pixel vertical line segment + 1.21 1.10 1.00 1.00 1.20 100-pixel vertical line segment + 1.03 1.03 1.00 1.00 1.02 500-pixel vertical line segment + 4.42 1.68 1.00 1.01 4.64 10x1 wide horizontal line segment + 1.83 1.31 1.00 1.00 1.83 100x10 wide horizontal line segment + 1.07 1.00 0.96 1.00 1.07 500x50 wide horizontal line segment + 4.10 1.67 1.00 1.00 4.62 10x1 wide vertical line segment + 1.50 1.24 1.06 1.06 1.48 100x10 wide vertical line segment + 1.06 1.03 1.00 1.00 1.05 500x50 wide vertical line segment + 2.54 1.61 1.00 1.00 3.61 1-pixel line + 2.71 1.48 1.00 1.00 2.67 10-pixel line + 1.19 1.09 1.00 1.00 1.19 100-pixel line + 1.04 1.02 1.00 1.00 1.03 500-pixel line + 2.68 1.51 0.98 1.00 3.17 10-pixel dashed line + 1.23 1.11 0.99 0.99 1.23 100-pixel dashed line + 1.15 1.08 1.00 1.00 1.15 100-pixel double-dashed line + 2.27 1.39 1.00 1.00 2.23 10x1 wide line + 1.20 1.09 1.00 1.00 1.20 100x10 wide line + 1.04 1.02 1.00 1.00 1.04 500x50 wide line + 1.52 1.45 1.00 1.00 1.52 100x10 wide dashed line + 1.54 1.47 1.00 1.00 1.54 100x10 wide double-dashed line + 1.97 1.30 0.96 0.95 1.95 10x10 rectangle outline + 1.44 1.27 1.00 1.00 1.43 100x100 rectangle outline + 3.22 2.16 1.10 1.09 3.61 500x500 rectangle outline + 1.95 1.34 1.00 1.00 1.90 10x10 wide rectangle outline + 1.14 1.14 1.00 1.00 1.13 100x100 wide rectangle outline + 1.00 1.00 1.00 1.00 1.00 500x500 wide rectangle outline + 1.57 1.72 1.00 1.00 3.03 1-pixel circle + 1.96 1.35 1.00 1.00 1.92 10-pixel circle + 1.21 1.07 0.86 0.97 1.20 100-pixel circle + 1.08 1.04 1.00 1.00 1.08 500-pixel circle + 1.39 1.19 1.03 1.03 1.38 100-pixel dashed circle + 1.21 1.11 1.00 1.00 1.23 100-pixel double-dashed circle + 1.59 1.28 1.00 1.00 1.58 10-pixel wide circle + 1.22 1.12 0.99 1.00 1.22 100-pixel wide circle + 1.06 1.04 1.00 1.00 1.05 500-pixel wide circle + 1.87 1.84 1.00 1.00 1.85 100-pixel wide dashed circle + 1.90 1.93 1.01 1.01 1.90 100-pixel wide double-dashed circle + 2.13 1.43 1.00 1.00 2.32 10-pixel partial circle + 1.42 1.18 1.00 1.00 1.42 100-pixel partial circle + 1.92 1.85 1.01 1.01 1.89 10-pixel wide partial circle + 1.73 1.67 1.00 1.00 1.73 100-pixel wide partial circle + 1.36 1.95 1.00 1.00 2.64 1-pixel solid circle + 2.02 1.37 1.00 1.00 2.03 10-pixel solid circle + 1.19 1.09 1.00 1.00 1.19 100-pixel solid circle + 1.02 0.99 1.00 1.00 1.01 500-pixel solid circle + 1.74 1.28 1.00 0.88 1.73 10-pixel fill chord partial circle + 1.31 1.13 1.00 1.00 1.31 100-pixel fill chord partial circle + 1.67 1.31 1.03 1.03 1.72 10-pixel fill slice partial circle + 1.30 1.13 1.00 1.00 1.28 100-pixel fill slice partial circle + 2.45 1.49 1.01 1.00 2.71 10-pixel ellipse + 1.22 1.10 1.00 1.00 1.22 100-pixel ellipse + 1.09 1.04 1.00 1.00 1.09 500-pixel ellipse + 1.90 1.28 1.00 1.00 1.89 100-pixel dashed ellipse + 1.62 1.24 0.96 0.97 1.61 100-pixel double-dashed ellipse + 2.43 1.50 1.00 1.00 2.42 10-pixel wide ellipse + 1.61 1.28 1.03 1.03 1.60 100-pixel wide ellipse + 1.08 1.05 1.00 1.00 1.08 500-pixel wide ellipse + 1.93 1.88 1.00 1.00 1.88 100-pixel wide dashed ellipse + 1.94 1.89 1.01 1.00 1.94 100-pixel wide double-dashed ellipse + 2.31 1.48 1.00 1.00 2.67 10-pixel partial ellipse + 1.38 1.17 1.00 1.00 1.38 100-pixel partial ellipse + 2.00 1.85 0.98 0.97 1.98 10-pixel wide partial ellipse + 1.89 1.86 1.00 1.00 1.89 100-pixel wide partial ellipse + 3.49 1.60 1.00 1.00 3.65 10-pixel filled ellipse + 1.67 1.26 1.00 1.00 1.67 100-pixel filled ellipse + 1.06 1.04 1.00 1.00 1.06 500-pixel filled ellipse + 2.38 1.43 1.01 1.00 2.32 10-pixel fill chord partial ellipse + 2.06 1.30 1.00 1.00 2.05 100-pixel fill chord partial ellipse + 2.27 1.41 1.00 1.00 2.27 10-pixel fill slice partial ellipse + 1.98 1.33 1.00 0.97 1.97 100-pixel fill slice partial ellipse + 57.46 1.99 1.01 1.00 114.92 Fill 1x1 equivalent triangle + 56.94 1.98 1.01 1.00 73.89 Fill 10x10 equivalent triangle + 6.07 1.75 1.00 1.00 6.07 Fill 100x100 equivalent triangle + 51.12 1.98 1.00 1.00 102.81 Fill 1x1 trapezoid + 51.42 1.82 1.01 1.00 94.89 Fill 10x10 trapezoid + 6.47 1.80 1.00 1.00 6.44 Fill 100x100 trapezoid + 1.56 1.28 1.00 0.99 1.56 Fill 300x300 trapezoid + 51.27 1.97 0.96 0.97 102.54 Fill 1x1 stippled trapezoid (8x8 stipple) + 51.73 2.00 1.02 1.02 67.92 Fill 10x10 stippled trapezoid (8x8 stipple) + 5.36 1.72 1.00 1.00 5.36 Fill 100x100 stippled trapezoid (8x8 stipple) + 1.54 1.26 1.00 1.00 1.59 Fill 300x300 stippled trapezoid (8x8 stipple) + 51.41 1.94 1.01 1.00 102.82 Fill 1x1 opaque stippled trapezoid (8x8 stipple) + 50.71 1.95 0.99 1.00 65.44 Fill 10x10 opaque stippled trapezoid (8x8... + 5.33 1.73 1.00 1.00 5.36 Fill 100x100 opaque stippled trapezoid (8x8... + 1.58 1.25 1.00 1.00 1.58 Fill 300x300 opaque stippled trapezoid (8x8... + 51.56 1.96 0.99 0.90 103.68 Fill 1x1 tiled trapezoid (4x4 tile) + 51.59 1.99 1.01 1.01 62.25 Fill 10x10 tiled trapezoid (4x4 tile) + 5.38 1.72 1.00 1.00 5.38 Fill 100x100 tiled trapezoid (4x4 tile) + 1.54 1.25 1.00 0.99 1.58 Fill 300x300 tiled trapezoid (4x4 tile) + 51.70 1.98 1.01 1.01 103.98 Fill 1x1 stippled trapezoid (17x15 stipple) + 44.86 1.97 1.00 1.00 44.86 Fill 10x10 stippled trapezoid (17x15 stipple) + 2.74 1.56 1.00 1.00 2.73 Fill 100x100 stippled trapezoid (17x15 stipple) + 1.29 1.14 1.00 1.00 1.27 Fill 300x300 stippled trapezoid (17x15 stipple) + 51.41 1.96 0.96 0.95 103.39 Fill 1x1 opaque stippled trapezoid (17x15... + 45.14 1.96 1.01 1.00 45.14 Fill 10x10 opaque stippled trapezoid (17x15... + 2.68 1.56 1.00 1.00 2.68 Fill 100x100 opaque stippled trapezoid (17x15... + 1.26 1.10 1.00 1.00 1.28 Fill 300x300 opaque stippled trapezoid (17x15... + 51.13 1.97 1.00 0.99 103.39 Fill 1x1 tiled trapezoid (17x15 tile) + 47.58 1.96 1.00 1.00 47.86 Fill 10x10 tiled trapezoid (17x15 tile) + 2.74 1.56 1.00 1.00 2.74 Fill 100x100 tiled trapezoid (17x15 tile) + 1.29 1.14 1.00 1.00 1.28 Fill 300x300 tiled trapezoid (17x15 tile) + 51.13 1.97 0.99 0.97 103.39 Fill 1x1 stippled trapezoid (161x145 stipple) + 45.14 1.97 1.00 1.00 44.29 Fill 10x10 stippled trapezoid (161x145 stipple) + 3.02 1.77 1.12 1.12 3.38 Fill 100x100 stippled trapezoid (161x145 stipple) + 1.31 1.13 1.00 1.00 1.30 Fill 300x300 stippled trapezoid (161x145 stipple) + 51.27 1.97 1.00 1.00 103.10 Fill 1x1 opaque stippled trapezoid (161x145... + 45.01 1.97 1.00 1.00 45.01 Fill 10x10 opaque stippled trapezoid (161x145... + 2.67 1.56 1.00 1.00 2.69 Fill 100x100 opaque stippled trapezoid (161x145.. + 1.29 1.13 1.00 1.01 1.27 Fill 300x300 opaque stippled trapezoid (161x145.. + 51.41 1.96 1.00 0.99 103.39 Fill 1x1 tiled trapezoid (161x145 tile) + 45.01 1.96 0.98 1.00 45.01 Fill 10x10 tiled trapezoid (161x145 tile) + 2.62 1.36 1.00 1.00 2.69 Fill 100x100 tiled trapezoid (161x145 tile) + 1.27 1.13 1.00 1.00 1.22 Fill 300x300 tiled trapezoid (161x145 tile) + 51.13 1.98 1.00 1.00 103.39 Fill 1x1 tiled trapezoid (216x208 tile) + 45.14 1.97 1.01 0.99 45.14 Fill 10x10 tiled trapezoid (216x208 tile) + 2.62 1.55 1.00 1.00 2.71 Fill 100x100 tiled trapezoid (216x208 tile) + 1.28 1.13 1.00 1.00 1.20 Fill 300x300 tiled trapezoid (216x208 tile) + 50.71 1.95 1.00 1.00 54.70 Fill 10x10 equivalent complex polygon + 5.51 1.71 0.96 0.98 5.47 Fill 100x100 equivalent complex polygons + 8.39 1.97 1.00 1.00 16.75 Fill 10x10 64-gon (Convex) + 8.38 1.83 1.00 1.00 8.43 Fill 100x100 64-gon (Convex) + 8.50 1.96 1.00 1.00 16.64 Fill 10x10 64-gon (Complex) + 8.26 1.83 1.00 1.00 8.35 Fill 100x100 64-gon (Complex) + 14.09 1.87 1.00 1.00 14.05 Char in 80-char line (6x13) + 11.91 1.87 1.00 1.00 11.95 Char in 70-char line (8x13) + 11.16 1.85 1.01 1.00 11.10 Char in 60-char line (9x15) + 10.09 1.78 1.00 1.00 10.09 Char16 in 40-char line (k14) + 6.15 1.75 1.00 1.00 6.31 Char16 in 23-char line (k24) + 11.92 1.90 1.03 1.03 11.88 Char in 80-char line (TR 10) + 8.18 1.78 1.00 0.99 8.17 Char in 30-char line (TR 24) + 42.83 1.44 1.01 1.00 42.11 Char in 20/40/20 line (6x13, TR 10) + 27.45 1.43 1.01 1.01 27.45 Char16 in 7/14/7 line (k14, k24) + 12.13 1.85 1.00 1.00 12.05 Char in 80-char image line (6x13) + 10.00 1.84 1.00 1.00 10.00 Char in 70-char image line (8x13) + 9.18 1.83 1.00 1.00 9.12 Char in 60-char image line (9x15) + 9.66 1.82 0.98 0.95 9.66 Char16 in 40-char image line (k14) + 5.82 1.72 1.00 1.00 5.99 Char16 in 23-char image line (k24) + 8.70 1.80 1.00 1.00 8.65 Char in 80-char image line (TR 10) + 4.67 1.66 1.00 1.00 4.67 Char in 30-char image line (TR 24) + 84.43 1.47 1.00 1.00 124.18 Scroll 10x10 pixels + 3.73 1.50 1.00 0.98 3.73 Scroll 100x100 pixels + 1.00 1.00 1.00 1.00 1.00 Scroll 500x500 pixels + 84.43 1.51 1.00 1.00 134.02 Copy 10x10 from window to window + 3.62 1.51 0.98 0.98 3.62 Copy 100x100 from window to window + 0.89 1.00 1.00 1.00 1.00 Copy 500x500 from window to window + 57.06 1.99 1.00 1.00 88.64 Copy 10x10 from pixmap to window + 2.49 2.00 1.00 1.00 2.48 Copy 100x100 from pixmap to window + 1.00 0.91 1.00 1.00 0.98 Copy 500x500 from pixmap to window + 2.04 1.01 1.00 1.00 2.03 Copy 10x10 from window to pixmap + 1.05 1.00 1.00 1.00 1.05 Copy 100x100 from window to pixmap + 1.00 1.00 0.93 1.00 1.04 Copy 500x500 from window to pixmap + 58.52 1.03 1.03 1.02 57.95 Copy 10x10 from pixmap to pixmap + 2.40 1.00 1.00 1.00 2.45 Copy 100x100 from pixmap to pixmap + 1.00 1.00 1.00 1.00 1.00 Copy 500x500 from pixmap to pixmap + 51.57 1.92 1.00 1.00 85.75 Copy 10x10 1-bit deep plane + 6.37 1.75 1.01 1.01 6.37 Copy 100x100 1-bit deep plane + 1.26 1.11 1.00 1.00 1.24 Copy 500x500 1-bit deep plane + 4.23 1.63 0.98 0.97 4.38 Copy 10x10 n-bit deep plane + 1.04 1.02 1.00 1.00 1.04 Copy 100x100 n-bit deep plane + 1.00 1.00 1.00 1.00 1.00 Copy 500x500 n-bit deep plane + 6.45 1.98 1.00 1.26 12.80 PutImage 10x10 square + 1.10 1.87 1.00 1.83 2.11 PutImage 100x100 square + 1.02 1.93 1.00 1.91 1.91 PutImage 500x500 square + 4.17 1.78 1.00 1.40 7.18 PutImage XY 10x10 square + 1.27 1.49 0.97 1.48 2.10 PutImage XY 100x100 square + 1.00 1.50 1.00 1.50 1.52 PutImage XY 500x500 square + 1.07 1.01 1.00 1.00 1.06 GetImage 10x10 square + 1.01 1.00 1.00 1.00 1.01 GetImage 100x100 square + 1.00 1.00 1.00 1.00 1.00 GetImage 500x500 square + 1.56 1.00 0.99 0.97 1.56 GetImage XY 10x10 square + 1.02 1.00 1.00 1.00 1.02 GetImage XY 100x100 square + 1.00 1.00 1.00 1.00 1.00 GetImage XY 500x500 square + 1.00 1.00 1.01 0.98 0.95 X protocol NoOperation + 1.02 1.03 1.04 1.03 1.00 QueryPointer + 1.03 1.02 1.04 1.03 1.00 GetProperty +100.41 1.51 1.00 1.00 198.76 Change graphics context + 45.81 1.00 0.99 0.97 57.10 Create and map subwindows (4 kids) + 78.45 1.01 1.02 1.02 63.07 Create and map subwindows (16 kids) + 73.91 1.01 1.00 1.00 56.37 Create and map subwindows (25 kids) + 73.22 1.00 1.00 1.00 49.07 Create and map subwindows (50 kids) + 72.36 1.01 0.99 1.00 32.14 Create and map subwindows (75 kids) + 70.34 1.00 1.00 1.00 30.12 Create and map subwindows (100 kids) + 55.00 1.00 1.00 0.99 23.75 Create and map subwindows (200 kids) + 55.30 1.01 1.00 1.00 141.03 Create unmapped window (4 kids) + 55.38 1.01 1.01 1.00 163.25 Create unmapped window (16 kids) + 54.75 0.96 1.00 0.99 166.95 Create unmapped window (25 kids) + 54.83 1.00 1.00 0.99 178.81 Create unmapped window (50 kids) + 55.38 1.01 1.01 1.00 181.20 Create unmapped window (75 kids) + 55.38 1.01 1.01 1.00 181.20 Create unmapped window (100 kids) + 54.87 1.01 1.01 1.00 182.05 Create unmapped window (200 kids) + 28.13 1.00 1.00 1.00 30.75 Map window via parent (4 kids) + 36.14 1.01 1.01 1.01 32.58 Map window via parent (16 kids) + 26.13 1.00 0.98 0.95 29.85 Map window via parent (25 kids) + 40.07 1.00 1.01 1.00 27.57 Map window via parent (50 kids) + 23.26 0.99 1.00 1.00 18.23 Map window via parent (75 kids) + 22.91 0.99 1.00 0.99 16.52 Map window via parent (100 kids) + 27.79 1.00 1.00 0.99 12.50 Map window via parent (200 kids) + 22.35 1.00 1.00 1.00 56.19 Unmap window via parent (4 kids) + 9.57 1.00 0.99 1.00 89.78 Unmap window via parent (16 kids) + 80.77 1.01 1.00 1.00 103.85 Unmap window via parent (25 kids) + 96.34 1.00 1.00 1.00 116.06 Unmap window via parent (50 kids) + 99.72 1.00 1.00 1.00 124.93 Unmap window via parent (75 kids) +112.36 1.00 1.00 1.00 125.27 Unmap window via parent (100 kids) +105.41 1.00 1.00 0.99 120.00 Unmap window via parent (200 kids) + 51.29 1.03 1.02 1.02 74.19 Destroy window via parent (4 kids) + 86.75 0.99 0.99 0.99 116.87 Destroy window via parent (16 kids) +106.43 1.01 1.01 1.01 127.49 Destroy window via parent (25 kids) +120.34 1.01 1.01 1.00 140.11 Destroy window via parent (50 kids) +126.67 1.00 0.99 0.99 145.00 Destroy window via parent (75 kids) +126.11 1.01 1.01 1.00 140.56 Destroy window via parent (100 kids) +128.57 1.01 1.00 1.00 137.91 Destroy window via parent (200 kids) + 16.04 0.88 1.00 1.00 20.36 Hide/expose window via popup (4 kids) + 19.04 1.01 1.00 1.00 23.48 Hide/expose window via popup (16 kids) + 19.22 1.00 1.00 1.00 20.44 Hide/expose window via popup (25 kids) + 17.41 1.00 0.91 0.97 17.68 Hide/expose window via popup (50 kids) + 17.29 1.01 1.00 1.01 17.07 Hide/expose window via popup (75 kids) + 16.74 1.00 1.00 1.00 16.17 Hide/expose window via popup (100 kids) + 10.30 1.00 1.00 1.00 10.51 Hide/expose window via popup (200 kids) + 16.48 1.01 1.00 1.00 26.05 Move window (4 kids) + 17.01 0.95 1.00 1.00 23.97 Move window (16 kids) + 16.95 1.00 1.00 1.00 22.90 Move window (25 kids) + 16.05 1.01 1.00 1.00 21.32 Move window (50 kids) + 15.58 1.00 0.98 0.98 19.44 Move window (75 kids) + 14.98 1.02 1.03 1.03 18.17 Move window (100 kids) + 10.90 1.01 1.01 1.00 12.68 Move window (200 kids) + 49.42 1.00 1.00 1.00 198.27 Moved unmapped window (4 kids) + 50.72 0.97 1.00 1.00 193.66 Moved unmapped window (16 kids) + 50.87 1.00 0.99 1.00 195.09 Moved unmapped window (25 kids) + 50.72 1.00 1.00 1.00 189.34 Moved unmapped window (50 kids) + 50.87 1.00 1.00 1.00 191.33 Moved unmapped window (75 kids) + 50.87 1.00 1.00 0.90 186.71 Moved unmapped window (100 kids) + 50.87 1.00 1.00 1.00 179.19 Moved unmapped window (200 kids) + 41.04 1.00 1.00 1.00 56.61 Move window via parent (4 kids) + 69.81 1.00 1.00 1.00 130.82 Move window via parent (16 kids) + 95.81 1.00 1.00 1.00 141.92 Move window via parent (25 kids) + 95.98 1.00 1.00 1.00 149.43 Move window via parent (50 kids) + 96.59 1.01 1.01 1.00 153.98 Move window via parent (75 kids) + 97.19 1.00 1.00 1.00 157.30 Move window via parent (100 kids) + 96.67 1.00 0.99 0.96 159.44 Move window via parent (200 kids) + 17.75 1.01 1.00 1.00 27.61 Resize window (4 kids) + 17.94 1.00 1.00 0.99 25.42 Resize window (16 kids) + 17.92 1.01 1.00 1.00 24.47 Resize window (25 kids) + 17.24 0.97 1.00 1.00 24.14 Resize window (50 kids) + 16.81 1.00 1.00 0.99 22.75 Resize window (75 kids) + 16.08 1.00 1.00 1.00 21.20 Resize window (100 kids) + 12.92 1.00 0.99 1.00 16.26 Resize window (200 kids) + 52.94 1.01 1.00 1.00 327.12 Resize unmapped window (4 kids) + 53.60 1.01 1.01 1.01 333.71 Resize unmapped window (16 kids) + 52.99 1.00 1.00 1.00 337.29 Resize unmapped window (25 kids) + 51.98 1.00 1.00 1.00 329.38 Resize unmapped window (50 kids) + 53.05 0.89 1.00 1.00 322.60 Resize unmapped window (75 kids) + 53.05 1.00 1.00 1.00 318.08 Resize unmapped window (100 kids) + 53.11 1.00 1.00 0.99 306.21 Resize unmapped window (200 kids) + 16.76 1.00 0.96 1.00 19.46 Circulate window (4 kids) + 17.24 1.00 1.00 0.97 16.24 Circulate window (16 kids) + 16.30 1.03 1.03 1.03 15.85 Circulate window (25 kids) + 13.45 1.00 1.00 1.00 14.90 Circulate window (50 kids) + 12.91 1.00 1.00 1.00 13.06 Circulate window (75 kids) + 11.30 0.98 1.00 1.00 11.03 Circulate window (100 kids) + 7.58 1.01 1.01 0.99 7.47 Circulate window (200 kids) + 1.01 1.01 0.98 1.00 0.95 Circulate Unmapped window (4 kids) + 1.07 1.07 1.01 1.07 1.02 Circulate Unmapped window (16 kids) + 1.04 1.09 1.06 1.05 0.97 Circulate Unmapped window (25 kids) + 1.04 1.23 1.20 1.18 1.05 Circulate Unmapped window (50 kids) + 1.18 1.53 1.19 1.45 1.24 Circulate Unmapped window (75 kids) + 1.08 1.02 1.01 1.74 1.01 Circulate Unmapped window (100 kids) + 1.01 1.12 0.98 0.91 0.97 Circulate Unmapped window (200 kids) + + + + + +Profiling with OProfile + +OProfile (available from http://oprofile.sourceforge.net/) is a +system-wide profiler for Linux systems that uses processor-level +counters to collect sampling data. OProfile can provide information +that is similar to that provided by gprof, but without the +necessity of recompiling the program with special instrumentation (i.e., +OProfile can collect statistical profiling information about optimized +programs). A test harness was developed to collect OProfile data for +each x11perf test individually. + + +Test runs were performed using the RETIRED_INSNS counter on the AMD +Athlon and the CPU_CLK_HALTED counter on the Intel Pentium III (with a +test configuration different from the one described above). We have +examined OProfile output and have compared it with gprof output. +This investigation has not produced results that yield performance +increases in x11perf numbers. + + + + + + + + + +X Test Suite + +The X Test Suite was run on the fully optimized DMX server using the +configuration described above. The following failures were noted: + +XListPixmapFormats: Test 1 [1] +XChangeWindowAttributes: Test 32 [1] +XCreateWindow: Test 30 [1] +XFreeColors: Test 4 [3] +XCopyArea: Test 13, 17, 21, 25, 30 [2] +XCopyPlane: Test 11, 15, 27, 31 [2] +XSetFontPath: Test 4 [1] +XChangeKeyboardControl: Test 9, 10 [1] + +[1] Previously documented errors expected from the Xinerama + implementation (see Phase I discussion). +[2] Newly noted errors that have been verified as expected + behavior of the Xinerama implementation. +[3] Newly noted error that has been verified as a Xinerama + implementation bug. + + + + + + + + + +Phase III + +During the third phase of development, support was provided for the +following extensions: SHAPE, RENDER, XKEYBOARD, XInput. + + + +SHAPE + +The SHAPE extension is supported. Test applications (e.g., xeyes and +oclock) and window managers that make use of the SHAPE extension will +work as expected. + + + + +RENDER + +The RENDER extension is supported. The version included in the DMX +CVS tree is version 0.2, and this version is fully supported by Xdmx. +Applications using only version 0.2 functions will work correctly; +however, some apps that make use of functions from later versions do not +properly check the extension's major/minor version numbers. These apps +will fail with a Bad Implementation error when using post-version 0.2 +functions. This is expected behavior. When the DMX CVS tree is updated +to include newer versions of RENDER, support for these newer functions +will be added to the DMX X server. + + + + +XKEYBOARD + +The XKEYBOARD extension is supported. If present on the back-end X +servers, the XKEYBOARD extension will be used to obtain information +about the type of the keyboard for initialization. Otherwise, the +keyboard will be initialized using defaults. Note that this departs +from older behavior: when Xdmx is compiled without XKEYBOARD support, +the map from the back-end X server will be preserved. With XKEYBOARD +support, the map is not preserved because better information and control +of the keyboard is available. + + + + +XInput + +The XInput extension is supported. Any device can be used as a core +device and be used as an XInput extension device, with the exception of +core devices on the back-end servers. This limitation is present +because cursor handling on the back-end requires that the back-end +cursor sometimes track the Xdmx core cursor -- behavior that is +incompatible with using the back-end pointer as a non-core device. + + +Currently, back-end extension devices are not available as Xdmx +extension devices, but this limitation should be removed in the future. + + +To demonstrate the XInput extension, and to provide more examples for +low-level input device driver writers, USB device drivers have been +written for mice (usb-mou), keyboards (usb-kbd), and +non-mouse/non-keyboard USB devices (usb-oth). Please see the man page +for information on Linux kernel drivers that are required for using +these Xdmx drivers. + + + + +DPMS + +The DPMS extension is exported but does not do anything at this time. + + + + + +Other Extensions + +The LBX, + SECURITY, + XC-APPGROUP, and + XFree86-Bigfont +extensions do not require any special Xdmx support and have been exported. + + +The + BIG-REQUESTS, + DEC-XTRAP, + DOUBLE-BUFFER, + Extended-Visual-Information, + FontCache, + GLX, + MIT-SCREEN-SAVER, + MIT-SHM, + MIT-SUNDRY-NONSTANDARD, + RECORD, + SECURITY, + SGI-GLX, + SYNC, + TOG-CUP, + X-Resource, + XC-MISC, + XFree86-DGA, + XFree86-DRI, + XFree86-Misc, + XFree86-VidModeExtension, and + XVideo +extensions are not supported at this time, but will be evaluated +for inclusion in future DMX releases. See below for additional work +on extensions after Phase III. + + + + + +Phase IV + + +Moving to XFree86 4.3.0 + +For Phase IV, the recent release of XFree86 4.3.0 (27 February 2003) +was merged onto the dmx.sourceforge.net CVS trunk and all work is +proceeding using this tree. + + + + +Extensions + + +XC-MISC (supported) + +XC-MISC is used internally by the X library to recycle XIDs from the +X server. This is important for long-running X server sessions. Xdmx +supports this extension. The X Test Suite passed and failed the exact +same tests before and after this extension was enabled. + + + + + +Extended-Visual-Information (supported) + +The Extended-Visual-Information extension provides a method for an X +client to obtain detailed visual information. Xdmx supports this +extension. It was tested using the hw/dmx/examples/evi example +program. Note that this extension is not Xinerama-aware -- it will +return visual information for each screen even though Xinerama is +causing the X server to export a single logical screen. + + + + + +RES (supported) + +The X-Resource extension provides a mechanism for a client to obtain +detailed information about the resources used by other clients. This +extension was tested with the hw/dmx/examples/res program. The +X Test Suite passed and failed the exact same tests before and after +this extension was enabled. + + + + + +BIG-REQUESTS (supported) + +This extension enables the X11 protocol to handle requests longer +than 262140 bytes. The X Test Suite passed and failed the exact same +tests before and after this extension was enabled. + + + + + +XSYNC (supported) + +This extension provides facilities for two different X clients to +synchronize their requests. This extension was minimally tested with +xdpyinfo and the X Test Suite passed and failed the exact same +tests before and after this extension was enabled. + + + + + +XTEST, RECORD, DEC-XTRAP (supported) and XTestExtension1 (not supported) + +The XTEST and RECORD extension were developed by the X Consortium for +use in the X Test Suite and are supported as a standard in the X11R6 +tree. They are also supported in Xdmx. When X Test Suite tests that +make use of the XTEST extension are run, Xdmx passes and fails exactly +the same tests as does a standard XFree86 X server. When the +rcrdtest test (a part of the X Test Suite that verifies the RECORD +extension) is run, Xdmx passes and fails exactly the same tests as does +a standard XFree86 X server. + + +There are two older XTEST-like extensions: DEC-XTRAP and +XTestExtension1. The XTestExtension1 extension was developed for use by +the X Testing Consortium for use with a test suite that eventually +became (part of?) the X Test Suite. Unlike XTEST, which only allows +events to be sent to the server, the XTestExtension1 extension also +allowed events to be recorded (similar to the RECORD extension). The +second is the DEC-XTRAP extension that was developed by the Digital +Equipment Corporation. + + +The DEC-XTRAP extension is available from Xdmx and has been tested +with the xtrap* tools which are distributed as standard X11R6 +clients. + + +The XTestExtension1 is not supported because it does not appear +to be used by any modern X clients (the few that support it also support +XTEST) and because there are no good methods available for testing that +it functions correctly (unlike XTEST and DEC-XTRAP, the code for +XTestExtension1 is not part of the standard X server source tree, so +additional testing is important). + + +Most of these extensions are documented in the X11R6 source tree. +Further, several original papers exist that this author was unable to +locate -- for completeness and historical interest, citations are +provide: + + +XRECORD + +Martha Zimet. Extending X For Recording. 8th Annual X +Technical Conference Boston, MA January 24-26, 1994. + + +DEC-XTRAP + +Dick Annicchiarico, Robert Chesler, Alan Jamison. XTrap +Architecture. Digital Equipment Corporation, July 1991. + + +XTestExtension1 + +Larry Woestman. X11 Input Synthesis Extension +Proposal. Hewlett Packard, November 1991. + + + + + + +MIT-MISC (not supported) + +The MIT-MISC extension is used to control a bug-compatibility flag +that provides compatibility with xterm programs from X11R1 and X11R2. +There does not appear to be a single client available that makes use of +this extension and there is not way to verify that it works correctly. +The Xdmx server does not support MIT-MISC. + + + + +SCREENSAVER (not supported) + +This extension provides special support for the X screen saver. It +was tested with beforelight, which appears to be the only client that +works with it. When Xinerama was not active, beforelight behaved +as expected. However, when Xinerama was active, beforelight did +not behave as expected. Further, when this extension is not active, +xscreensaver (a widely-used X screen saver program) did not behave +as expected. Since this extension is not Xinerama-aware and is not +commonly used with expected results by clients, we have left this +extension disabled at this time. + + + + +GLX (supported) + +The GLX extension provides OpenGL and GLX windowing support. In +Xdmx, the extension is called glxProxy, and it is Xinerama aware. It +works by either feeding requests forward through Xdmx to each of the +back-end servers or handling them locally. All rendering requests are +handled on the back-end X servers. This code was donated to the DMX +project by SGI. For the X Test Suite results comparison, see below. + + + + +RENDER (supported) + +The X Rendering Extension (RENDER) provides support for digital image +composition. Geometric and text rendering are supported. RENDER is +partially Xinerama-aware, with text and the most basic compositing +operator; however, its higher level primitives (triangles, triangle +strips, and triangle fans) are not yet Xinerama-aware. The RENDER +extension is still under development, and is currently at version 0.8. +Additional support will be required in DMX as more primitives and/or +requests are added to the extension. + + +There is currently no test suite for the X Rendering Extension; +however, there has been discussion of developing a test suite as the +extension matures. When that test suite becomes available, additional +testing can be performed with Xdmx. The X Test Suite passed and failed +the exact same tests before and after this extension was enabled. + + + + +Summary + + +To summarize, the following extensions are currently supported: + BIG-REQUESTS, + DEC-XTRAP, + DMX, + DPMS, + Extended-Visual-Information, + GLX, + LBX, + RECORD, + RENDER, + SECURITY, + SHAPE, + SYNC, + X-Resource, + XC-APPGROUP, + XC-MISC, + XFree86-Bigfont, + XINERAMA, + XInputExtension, + XKEYBOARD, and + XTEST. + + +The following extensions are not supported at this time: + DOUBLE-BUFFER, + FontCache, + MIT-SCREEN-SAVER, + MIT-SHM, + MIT-SUNDRY-NONSTANDARD, + TOG-CUP, + XFree86-DGA, + XFree86-Misc, + XFree86-VidModeExtension, + XTestExtensionExt1, and + XVideo. + + + + + +Additional Testing with the X Test Suite + + +XFree86 without XTEST + +After the release of XFree86 4.3.0, we retested the XFree86 X server +with and without using the XTEST extension. When the XTEST extension +was not used for testing, the XFree86 4.3.0 server running on our +usual test system with a Radeon VE card reported unexpected failures in +the following tests: + +XListPixmapFormats: Test 1 +XChangeKeyboardControl: Tests 9, 10 +XGetDefault: Test 5 +XRebindKeysym: Test 1 + + + + + +XFree86 with XTEST + +When using the XTEST extension, the XFree86 4.3.0 server reported the +following errors: + +XListPixmapFormats: Test 1 +XChangeKeyboardControl: Tests 9, 10 +XGetDefault: Test 5 +XRebindKeysym: Test 1 + +XAllowEvents: Tests 20, 21, 24 +XGrabButton: Tests 5, 9-12, 14, 16, 19, 21-25 +XGrabKey: Test 8 +XSetPointerMapping: Test 3 +XUngrabButton: Test 4 + + + +While these errors may be important, they will probably be fixed +eventually in the XFree86 source tree. We are particularly interested +in demonstrating that the Xdmx server does not introduce additional +failures that are not known Xinerama failures. + + + + +Xdmx with XTEST, without Xinerama, without GLX + +Without Xinerama, but using the XTEST extension, the following errors +were reported from Xdmx (note that these are the same as for the XFree86 +4.3.0, except that XGetDefault no longer fails): + +XListPixmapFormats: Test 1 +XChangeKeyboardControl: Tests 9, 10 +XRebindKeysym: Test 1 + +XAllowEvents: Tests 20, 21, 24 +XGrabButton: Tests 5, 9-12, 14, 16, 19, 21-25 +XGrabKey: Test 8 +XSetPointerMapping: Test 3 +XUngrabButton: Test 4 + + + + + +Xdmx with XTEST, with Xinerama, without GLX + +With Xinerama, using the XTEST extension, the following errors +were reported from Xdmx: + +XListPixmapFormats: Test 1 +XChangeKeyboardControl: Tests 9, 10 +XRebindKeysym: Test 1 + +XAllowEvents: Tests 20, 21, 24 +XGrabButton: Tests 5, 9-12, 14, 16, 19, 21-25 +XGrabKey: Test 8 +XSetPointerMapping: Test 3 +XUngrabButton: Test 4 + +XCopyPlane: Tests 13, 22, 31 (well-known XTEST/Xinerama interaction issue) +XDrawLine: Test 67 +XDrawLines: Test 91 +XDrawSegments: Test 68 + +Note that the first two sets of errors are the same as for the XFree86 +4.3.0 server, and that the XCopyPlane error is a well-known error +resulting from an XTEST/Xinerama interaction when the request crosses a +screen boundary. The XDraw* errors are resolved when the tests are run +individually and they do not cross a screen boundary. We will +investigate these errors further to determine their cause. + + + + +Xdmx with XTEST, with Xinerama, with GLX + +With GLX enabled, using the XTEST extension, the following errors +were reported from Xdmx (these results are from early during the Phase +IV development, but were confirmed with a late Phase IV snapshot): + +XListPixmapFormats: Test 1 +XChangeKeyboardControl: Tests 9, 10 +XRebindKeysym: Test 1 + +XAllowEvents: Tests 20, 21, 24 +XGrabButton: Tests 5, 9-12, 14, 16, 19, 21-25 +XGrabKey: Test 8 +XSetPointerMapping: Test 3 +XUngrabButton: Test 4 + +XClearArea: Test 8 +XCopyArea: Tests 4, 5, 11, 14, 17, 23, 25, 27, 30 +XCopyPlane: Tests 6, 7, 10, 19, 22, 31 +XDrawArcs: Tests 89, 100, 102 +XDrawLine: Test 67 +XDrawSegments: Test 68 + +Note that the first two sets of errors are the same as for the XFree86 +4.3.0 server, and that the third set has different failures than when +Xdmx does not include GLX support. Since the GLX extension adds new +visuals to support GLX's visual configs and the X Test Suite runs tests +over the entire set of visuals, additional rendering tests were run and +presumably more of them crossed a screen boundary. This conclusion is +supported by the fact that nearly all of the rendering errors reported +are resolved when the tests are run individually and they do no cross a +screen boundary. + + +Further, when hardware rendering is disabled on the back-end displays, +many of the errors in the third set are eliminated, leaving only: + +XClearArea: Test 8 +XCopyArea: Test 4, 5, 11, 14, 17, 23, 25, 27, 30 +XCopyPlane: Test 6, 7, 10, 19, 22, 31 + + + + + +Conclusion + +We conclude that all of the X Test Suite errors reported for Xdmx are +the result of errors in the back-end X server or the Xinerama +implementation. Further, all of these errors that can be reasonably +fixed at the Xdmx layer have been. (Where appropriate, we have +submitted patches to the XFree86 and Xinerama upstream maintainers.) + + + + + +Dynamic Reconfiguration + +During this development phase, dynamic reconfiguration support was +added to DMX. This support allows an application to change the position +and offset of a back-end server's screen. For example, if the +application would like to shift a screen slightly to the left, it could +query Xdmx for the screen's <x,y> position and then dynamically +reconfigure that screen to be at position <x+10,y>. When a screen +is dynamically reconfigured, input handling and a screen's root window +dimensions are adjusted as needed. These adjustments are transparent to +the user. + + + +Dynamic reconfiguration extension + +The application interface to DMX's dynamic reconfiguration is through +a function in the DMX extension library: + +Bool DMXReconfigureScreen(Display *dpy, int screen, int x, int y) + +where dpy is DMX server's display, screen is the number of the +screen to be reconfigured, and x and y are the new upper, +left-hand coordinates of the screen to be reconfigured. + + +The coordinates are not limited other than as required by the X +protocol, which limits all coordinates to a signed 16 bit number. In +addition, all coordinates within a screen must also be legal values. +Therefore, setting a screen's upper, left-hand coordinates such that the +right or bottom edges of the screen is greater than 32,767 is illegal. + + + + +Bounding box + +When the Xdmx server is started, a bounding box is calculated from +the screens' layout given either on the command line or in the +configuration file. This bounding box is currently fixed for the +lifetime of the Xdmx server. + + +While it is possible to move a screen outside of the bounding box, it +is currently not possible to change the dimensions of the bounding box. +For example, it is possible to specify coordinates of <-100,-100> +for the upper, left-hand corner of the bounding box, which was +previously at coordinates <0,0>. As expected, the screen is moved +down and to the right; however, since the bounding box is fixed, the +left side and upper portions of the screen exposed by the +reconfiguration are no longer accessible on that screen. Those +inaccessible regions are filled with black. + + +This fixed bounding box limitation will be addressed in a future +development phase. + + + + +Sample applications + +An example of where this extension is useful is in setting up a video +wall. It is not always possible to get everything perfectly aligned, +and sometimes the positions are changed (e.g., someone might bump into a +projector). Instead of physically moving projectors or monitors, it is +now possible to adjust the positions of the back-end server's screens +using the dynamic reconfiguration support in DMX. + + +Other applications, such as automatic setup and calibration tools, +can make use of dynamic reconfiguration to correct for projector +alignment problems, as long as the projectors are still arranged +rectilinearly. Horizontal and vertical keystone correction could be +applied to projectors to correct for non-rectilinear alignment problems; +however, this must be done external to Xdmx. + + +A sample test program is included in the DMX server's examples +directory to demonstrate the interface and how an application might use +dynamic reconfiguration. See dmxreconfig.c for details. + + + + +Additional notes + +In the original development plan, Phase IV was primarily devoted to +adding OpenGL support to DMX; however, SGI became interested in the DMX +project and developed code to support OpenGL/GLX. This code was later +donated to the DMX project and integrated into the DMX code base, which +freed the DMX developers to concentrate on dynamic reconfiguration (as +described above). + + + + + +Doxygen documentation + +Doxygen is an open-source (GPL) documentation system for generating +browseable documentation from stylized comments in the source code. We +have placed all of the Xdmx server and DMX protocol source code files +under Doxygen so that comprehensive documentation for the Xdmx source +code is available in an easily browseable format. + + + + +Valgrind + +Valgrind, an open-source (GPL) memory debugger for Linux, was used to +search for memory management errors. Several memory leaks were detected +and repaired. The following errors were not addressed: + + + When the X11 transport layer sends a reply to the client, only + those fields that are required by the protocol are filled in -- + unused fields are left as uninitialized memory and are therefore + noted by valgrind. These instances are not errors and were not + repaired. + + + At each server generation, glxInitVisuals allocates memory that + is never freed. The amount of memory lost each generation + approximately equal to 128 bytes for each back-end visual. + Because the code involved is automatically generated, this bug + has not been fixed and will be referred to SGI. + + + At each server generation, dmxRealizeFont calls XLoadQueryFont, + which allocates a font structure that is not freed. + dmxUnrealizeFont can free the font structure for the first + screen, but cannot free it for the other screens since they are + already closed by the time dmxUnrealizeFont could free them. + The amount of memory lost each generation is approximately equal + to 80 bytes per font per back-end. When this bug is fixed in + the the X server's device-independent (dix) code, DMX will be + able to properly free the memory allocated by XLoadQueryFont. + + + + + + +RATS + +RATS (Rough Auditing Tool for Security) is an open-source (GPL) +security analysis tool that scans source code for common +security-related programming errors (e.g., buffer overflows and TOCTOU +races). RATS was used to audit all of the code in the hw/dmx directory +and all "High" notations were checked manually. The code was either +re-written to eliminate the warning, or a comment containing "RATS" was +inserted on the line to indicate that a human had checked the code. +Unrepaired warnings are as follows: + + + Fixed-size buffers are used in many areas, but code has been + added to protect against buffer overflows (e.g., snprintf). + The only instances that have not yet been fixed are in + config/xdmxconfig.c (which is not part of the Xdmx server) and + input/usb-common.c. + + + vprintf and vfprintf are used in the logging routines. In + general, all uses of these functions (e.g., dmxLog) provide a + constant format string from a trusted source, so the use is + relatively benign. + + + glxProxy/glxscreens.c uses getenv and strcat. The use of these + functions is safe and will remain safe as long as + ExtensionsString is longer then GLXServerExtensions (ensuring + this may not be ovious to the casual programmer, but this is in + automatically generated code, so we hope that the generator + enforces this constraint). + + + + + + + + + + + + + +
+ + + + diff --git a/xorg-server/hw/dmx/examples/dmxwininfo.c b/xorg-server/hw/dmx/examples/dmxwininfo.c index 6cf1d411e..3d027d530 100644 --- a/xorg-server/hw/dmx/examples/dmxwininfo.c +++ b/xorg-server/hw/dmx/examples/dmxwininfo.c @@ -39,7 +39,6 @@ #include #include #include -#include #include static const char *FontName = "fixed"; @@ -80,7 +79,7 @@ EventLoop(Display *dpy, Window win, GC gc) y += 20; for (i = 0; i < count; i++) { char str[500]; - XmuSnprintf(str, sizeof(str), + snprintf(str, sizeof(str), "screen %d: pos: %dx%d+%d+%d visible: %dx%d+%d+%d", winInfo[i].screen, winInfo[i].pos.width, winInfo[i].pos.height, diff --git a/xorg-server/hw/dmx/glxProxy/glxcmds.c b/xorg-server/hw/dmx/glxProxy/glxcmds.c index f79264ea9..a76201da0 100644 --- a/xorg-server/hw/dmx/glxProxy/glxcmds.c +++ b/xorg-server/hw/dmx/glxProxy/glxcmds.c @@ -59,9 +59,6 @@ extern __GLXFBConfig **__glXFBConfigs; extern int __glXNumFBConfigs; -extern __GLXFBConfig *glxLookupFBConfig( GLXFBConfigID id ); -extern __GLXFBConfig *glxLookupFBConfigByVID( VisualID vid ); -extern __GLXFBConfig *glxLookupBackEndFBConfig( GLXFBConfigID id, int screen ); extern int glxIsExtensionSupported( char *ext ); extern int __glXGetFBConfigsSGIX(__GLXclientState *cl, GLbyte *pc); @@ -70,6 +67,44 @@ extern int __glXGetFBConfigsSGIX(__GLXclientState *cl, GLbyte *pc); (x) - dmxScreen->glxErrorBase + __glXerrorBase \ : (x) ) +static __GLXFBConfig *glxLookupFBConfig( GLXFBConfigID id ) +{ + int i,j; + + for (i=0, j=0; i<__glXNumFBConfigs; i++,j+=(__glXNumActiveScreens+1) ) { + if ( __glXFBConfigs[j]->id == id) + return __glXFBConfigs[j]; + } + + return NULL; +} + +static __GLXFBConfig *glxLookupFBConfigByVID( VisualID vid ) +{ + int i,j; + + for (i=0, j=0; i<__glXNumFBConfigs; i++,j+=(__glXNumActiveScreens+1) ) { + if ( __glXFBConfigs[j]->associatedVisualId == vid) + return __glXFBConfigs[j]; + } + + return NULL; +} + +static __GLXFBConfig *glxLookupBackEndFBConfig( GLXFBConfigID id, int screen ) +{ + int i; + int j; + + for (i=0, j=0; i<__glXNumFBConfigs; i++,j+=(__glXNumActiveScreens+1) ) { + if ( __glXFBConfigs[j]->id == id) + return __glXFBConfigs[j+screen+1]; + } + + return NULL; + +} + Display *GetBackEndDisplay( __GLXclientState *cl, int s ) { if (! cl->be_displays[s] ) { diff --git a/xorg-server/hw/dmx/glxProxy/glxscreens.c b/xorg-server/hw/dmx/glxProxy/glxscreens.c index df49fe50d..01e041c8f 100644 --- a/xorg-server/hw/dmx/glxProxy/glxscreens.c +++ b/xorg-server/hw/dmx/glxProxy/glxscreens.c @@ -1,371 +1,332 @@ -/* - * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) - * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice including the dates of first publication and - * either this permission notice or a reference to - * http://oss.sgi.com/projects/FreeB/ - * shall be included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF - * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - * Except as contained in this notice, the name of Silicon Graphics, Inc. - * shall not be used in advertising or otherwise to promote the sale, use or - * other dealings in this Software without prior written authorization from - * Silicon Graphics, Inc. - */ - -#ifdef HAVE_DMX_CONFIG_H -#include -#endif - -#include "dmx.h" -#include "dmxlog.h" - -#include "glxserver.h" - -#include - -#include "glxfbconfig.h" - -#ifdef PANORAMIX -#include "panoramiXsrv.h" -#endif - -__GLXscreenInfo *__glXActiveScreens; -GLint __glXNumActiveScreens; - -__GLXFBConfig **__glXFBConfigs; -int __glXNumFBConfigs; - -static char GLXServerVendorName[] = "SGI DMX/glxProxy"; -static char GLXServerVersion[64]; -static char GLXServerExtensions[] = - "GLX_EXT_visual_info " - "GLX_EXT_visual_rating " - "GLX_EXT_import_context " - "GLX_SGIX_fbconfig " - "GLX_SGI_make_current_read " - "GLX_SGI_swap_control " - ; - -static char ExtensionsString[1024]; - -static void CalcServerVersionAndExtensions( void ) -{ - int s; - xGLXQueryVersionReq *req; - xGLXQueryVersionReply reply; - char **be_extensions; - char *ext; - char *denied_extensions; - - /* - * set the server glx version to be the minimum version - * supported by all back-end servers - */ - __glXVersionMajor = 0; - __glXVersionMinor = 0; - for (s=0; s<__glXNumActiveScreens; s++) { - DMXScreenInfo *dmxScreen = &dmxScreens[s]; - Display *dpy = dmxScreen->beDisplay; - - /* Send the glXQueryVersion request */ - LockDisplay(dpy); - GetReq(GLXQueryVersion,req); - req->reqType = dmxScreen->glxMajorOpcode; - req->glxCode = X_GLXQueryVersion; - req->majorVersion = GLX_SERVER_MAJOR_VERSION; - req->minorVersion = GLX_SERVER_MINOR_VERSION; - _XReply(dpy, (xReply*) &reply, 0, False); - UnlockDisplay(dpy); - SyncHandle(); - - if (s == 0) { - __glXVersionMajor = reply.majorVersion; - __glXVersionMinor = reply.minorVersion; - } - else { - if (reply.majorVersion < __glXVersionMajor) { - __glXVersionMajor = reply.majorVersion; - __glXVersionMinor = reply.minorVersion; - } - else if ( (reply.majorVersion == __glXVersionMajor) && - (reply.minorVersion < __glXVersionMinor) ) { - __glXVersionMinor = reply.minorVersion; - } - } - - } - - if (GLX_SERVER_MAJOR_VERSION < __glXVersionMajor) { - __glXVersionMajor = GLX_SERVER_MAJOR_VERSION; - __glXVersionMinor = GLX_SERVER_MINOR_VERSION; - } - else if ( (GLX_SERVER_MAJOR_VERSION == __glXVersionMajor) && - (GLX_SERVER_MINOR_VERSION < __glXVersionMinor) ) { - __glXVersionMinor = GLX_SERVER_MINOR_VERSION; - } - - sprintf(GLXServerVersion, "%d.%d DMX %d back-end server(s)", - __glXVersionMajor, __glXVersionMinor, __glXNumActiveScreens ); - /* - * set the ExtensionsString to the minimum extensions string - */ - ExtensionsString[0] = '\0'; - - /* - * read extensions strings of all back-end servers - */ - be_extensions = (char **)malloc( __glXNumActiveScreens * sizeof(char *) ); - if (!be_extensions) - return; - - for (s=0; s<__glXNumActiveScreens; s++) { - DMXScreenInfo *dmxScreen = &dmxScreens[s]; - Display *dpy = dmxScreen->beDisplay; - xGLXQueryServerStringReq *req; - xGLXQueryServerStringReply reply; - int length, numbytes, slop; - - /* Send the glXQueryServerString request */ - LockDisplay(dpy); - GetReq(GLXQueryServerString,req); - req->reqType = dmxScreen->glxMajorOpcode; - req->glxCode = X_GLXQueryServerString; - req->screen = DefaultScreen(dpy); - req->name = GLX_EXTENSIONS; - _XReply(dpy, (xReply*) &reply, 0, False); - - length = (int)reply.length; - numbytes = (int)reply.n; - slop = numbytes * __GLX_SIZE_INT8 & 3; - be_extensions[s] = (char *)malloc(numbytes); - if (!be_extensions[s]) { - /* Throw data on the floor */ - _XEatData(dpy, length); - } else { - _XRead(dpy, (char *)be_extensions[s], numbytes); - if (slop) _XEatData(dpy,4-slop); - } - UnlockDisplay(dpy); - SyncHandle(); - } - - /* - * extensions string will include only extensions that our - * server supports as well as all back-end servers supports. - * extensions that are in the DMX_DENY_EXTENSIONS string will - * not be supported. - */ - denied_extensions = getenv("DMX_DENY_GLX_EXTENSIONS"); - ext = strtok(GLXServerExtensions, " "); - while( ext ) { - int supported = 1; - - if (denied_extensions && strstr(denied_extensions, ext)) { - supported = 0; - } - else { - for (s=0; s<__glXNumActiveScreens && supported; s++) { - if ( !strstr(be_extensions[s], ext) ) { - supported = 0; - } - } - } - - if (supported) { - strcat(ExtensionsString, ext); - strcat(ExtensionsString, " "); - } - - ext = strtok(NULL, " "); - } - - /* - * release temporary storage - */ - for (s=0; s<__glXNumActiveScreens; s++) { - free(be_extensions[s]); - } - free( be_extensions ); - - if (dmxGLXSwapGroupSupport) { - if (!denied_extensions || - !strstr(denied_extensions, "GLX_SGIX_swap_group")) { - strcat(ExtensionsString, "GLX_SGIX_swap_group"); - if (!denied_extensions || - !strstr(denied_extensions, "GLX_SGIX_swap_barrier")) { - strcat(ExtensionsString, " GLX_SGIX_swap_barrier"); - } - } - } - -} - -void __glXScreenInit(GLint numscreens) -{ - int s; - int c; - DMXScreenInfo *dmxScreen0 = &dmxScreens[0]; - __glXNumActiveScreens = numscreens; - - - CalcServerVersionAndExtensions(); - - - __glXFBConfigs = NULL; - __glXNumFBConfigs = 0; - - if ( (__glXVersionMajor == 1 && __glXVersionMinor >= 3) || - (__glXVersionMajor > 1) || - ( strstr(ExtensionsString, "GLX_SGIX_fbconfig") ) ) { - - /* - // Initialize FBConfig info. - // find the set of FBConfigs that are present on all back-end - // servers - only those configs will be supported - */ - __glXFBConfigs = (__GLXFBConfig **)malloc( dmxScreen0->numFBConfigs * - (numscreens+1) * sizeof(__GLXFBConfig *) ); - __glXNumFBConfigs = 0; - - for (c=0; cnumFBConfigs; c++) { - __GLXFBConfig *cfg = NULL; - - if (numscreens > 1) { - for (s=1; sfbconfigs[c], - dmxScreen->fbconfigs, - dmxScreen->numFBConfigs ); - __glXFBConfigs[ __glXNumFBConfigs * (numscreens+1) + s + 1 ] = cfg; - if (!cfg) { - dmxLog(dmxInfo,"screen0 FBConfig 0x%x is missing on screen#%d\n", dmxScreen0->fbconfigs[c].id, s); - break; - } - else { - dmxLog(dmxInfo,"screen0 FBConfig 0x%x matched to 0x%x on screen#%d\n", dmxScreen0->fbconfigs[c].id, cfg->id, s); - } - } - } - else { - cfg = &dmxScreen0->fbconfigs[c]; - } - - if (cfg) { - - /* filter out overlay visuals */ - if (cfg->level == 0) { - __GLXFBConfig *proxy_cfg; - - __glXFBConfigs[ __glXNumFBConfigs * (numscreens+1) + 1 ] = - &dmxScreen0->fbconfigs[c]; - - proxy_cfg = malloc( sizeof(__GLXFBConfig) ); - memcpy( proxy_cfg, cfg, sizeof(__GLXFBConfig) ); - proxy_cfg->id = FakeClientID(0); - /* visual will be associated later in __glXGetFBConfigs */ - proxy_cfg->associatedVisualId = (unsigned int)-1; - - __glXFBConfigs[ __glXNumFBConfigs * (numscreens+1) + 0 ] = proxy_cfg; - - __glXNumFBConfigs++; - } - - } - - } - - } - -} - -void __glXScreenReset(void) -{ - __glXNumActiveScreens = 0; -} - -char *__glXGetServerString( unsigned int name ) -{ - char *ret = NULL; - - switch( name) { - - case GLX_VENDOR: - ret = GLXServerVendorName; - break; - - case GLX_VERSION: - ret = GLXServerVersion; - break; - - case GLX_EXTENSIONS: - ret = ExtensionsString; - break; - - default: - break; - } - - return ret; - -} - - -__GLXFBConfig *glxLookupFBConfig( GLXFBConfigID id ) -{ - int i,j; - - for (i=0, j=0; i<__glXNumFBConfigs; i++,j+=(__glXNumActiveScreens+1) ) { - if ( __glXFBConfigs[j]->id == id) - return __glXFBConfigs[j]; - } - - return NULL; -} - -__GLXFBConfig *glxLookupFBConfigByVID( VisualID vid ) -{ - int i,j; - - for (i=0, j=0; i<__glXNumFBConfigs; i++,j+=(__glXNumActiveScreens+1) ) { - if ( __glXFBConfigs[j]->associatedVisualId == vid) - return __glXFBConfigs[j]; - } - - return NULL; -} - -__GLXFBConfig *glxLookupBackEndFBConfig( GLXFBConfigID id, int screen ) -{ - int i; - int j; - - for (i=0, j=0; i<__glXNumFBConfigs; i++,j+=(__glXNumActiveScreens+1) ) { - if ( __glXFBConfigs[j]->id == id) - return __glXFBConfigs[j+screen+1]; - } - - return NULL; - -} - -int glxIsExtensionSupported( char *ext ) -{ - return( strstr(ExtensionsString, ext) != NULL ); -} +/* + * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) + * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice including the dates of first publication and + * either this permission notice or a reference to + * http://oss.sgi.com/projects/FreeB/ + * shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Except as contained in this notice, the name of Silicon Graphics, Inc. + * shall not be used in advertising or otherwise to promote the sale, use or + * other dealings in this Software without prior written authorization from + * Silicon Graphics, Inc. + */ + +#ifdef HAVE_DMX_CONFIG_H +#include +#endif + +#include "dmx.h" +#include "dmxlog.h" + +#include "glxserver.h" + +#include + +#include "glxfbconfig.h" + +#ifdef PANORAMIX +#include "panoramiXsrv.h" +#endif + +__GLXscreenInfo *__glXActiveScreens; +GLint __glXNumActiveScreens; + +__GLXFBConfig **__glXFBConfigs; +int __glXNumFBConfigs; + +static char GLXServerVendorName[] = "SGI DMX/glxProxy"; +static char GLXServerVersion[64]; +static char GLXServerExtensions[] = + "GLX_EXT_visual_info " + "GLX_EXT_visual_rating " + "GLX_EXT_import_context " + "GLX_SGIX_fbconfig " + "GLX_SGI_make_current_read " + "GLX_SGI_swap_control " + ; + +static char ExtensionsString[1024]; + +static void CalcServerVersionAndExtensions( void ) +{ + int s; + xGLXQueryVersionReq *req; + xGLXQueryVersionReply reply; + char **be_extensions; + char *ext; + char *denied_extensions; + + /* + * set the server glx version to be the minimum version + * supported by all back-end servers + */ + __glXVersionMajor = 0; + __glXVersionMinor = 0; + for (s=0; s<__glXNumActiveScreens; s++) { + DMXScreenInfo *dmxScreen = &dmxScreens[s]; + Display *dpy = dmxScreen->beDisplay; + + /* Send the glXQueryVersion request */ + LockDisplay(dpy); + GetReq(GLXQueryVersion,req); + req->reqType = dmxScreen->glxMajorOpcode; + req->glxCode = X_GLXQueryVersion; + req->majorVersion = GLX_SERVER_MAJOR_VERSION; + req->minorVersion = GLX_SERVER_MINOR_VERSION; + _XReply(dpy, (xReply*) &reply, 0, False); + UnlockDisplay(dpy); + SyncHandle(); + + if (s == 0) { + __glXVersionMajor = reply.majorVersion; + __glXVersionMinor = reply.minorVersion; + } + else { + if (reply.majorVersion < __glXVersionMajor) { + __glXVersionMajor = reply.majorVersion; + __glXVersionMinor = reply.minorVersion; + } + else if ( (reply.majorVersion == __glXVersionMajor) && + (reply.minorVersion < __glXVersionMinor) ) { + __glXVersionMinor = reply.minorVersion; + } + } + + } + + if (GLX_SERVER_MAJOR_VERSION < __glXVersionMajor) { + __glXVersionMajor = GLX_SERVER_MAJOR_VERSION; + __glXVersionMinor = GLX_SERVER_MINOR_VERSION; + } + else if ( (GLX_SERVER_MAJOR_VERSION == __glXVersionMajor) && + (GLX_SERVER_MINOR_VERSION < __glXVersionMinor) ) { + __glXVersionMinor = GLX_SERVER_MINOR_VERSION; + } + + sprintf(GLXServerVersion, "%d.%d DMX %d back-end server(s)", + __glXVersionMajor, __glXVersionMinor, __glXNumActiveScreens ); + /* + * set the ExtensionsString to the minimum extensions string + */ + ExtensionsString[0] = '\0'; + + /* + * read extensions strings of all back-end servers + */ + be_extensions = (char **)malloc( __glXNumActiveScreens * sizeof(char *) ); + if (!be_extensions) + return; + + for (s=0; s<__glXNumActiveScreens; s++) { + DMXScreenInfo *dmxScreen = &dmxScreens[s]; + Display *dpy = dmxScreen->beDisplay; + xGLXQueryServerStringReq *req; + xGLXQueryServerStringReply reply; + int length, numbytes, slop; + + /* Send the glXQueryServerString request */ + LockDisplay(dpy); + GetReq(GLXQueryServerString,req); + req->reqType = dmxScreen->glxMajorOpcode; + req->glxCode = X_GLXQueryServerString; + req->screen = DefaultScreen(dpy); + req->name = GLX_EXTENSIONS; + _XReply(dpy, (xReply*) &reply, 0, False); + + length = (int)reply.length; + numbytes = (int)reply.n; + slop = numbytes * __GLX_SIZE_INT8 & 3; + be_extensions[s] = (char *)malloc(numbytes); + if (!be_extensions[s]) { + /* Throw data on the floor */ + _XEatData(dpy, length); + } else { + _XRead(dpy, (char *)be_extensions[s], numbytes); + if (slop) _XEatData(dpy,4-slop); + } + UnlockDisplay(dpy); + SyncHandle(); + } + + /* + * extensions string will include only extensions that our + * server supports as well as all back-end servers supports. + * extensions that are in the DMX_DENY_EXTENSIONS string will + * not be supported. + */ + denied_extensions = getenv("DMX_DENY_GLX_EXTENSIONS"); + ext = strtok(GLXServerExtensions, " "); + while( ext ) { + int supported = 1; + + if (denied_extensions && strstr(denied_extensions, ext)) { + supported = 0; + } + else { + for (s=0; s<__glXNumActiveScreens && supported; s++) { + if ( !strstr(be_extensions[s], ext) ) { + supported = 0; + } + } + } + + if (supported) { + strcat(ExtensionsString, ext); + strcat(ExtensionsString, " "); + } + + ext = strtok(NULL, " "); + } + + /* + * release temporary storage + */ + for (s=0; s<__glXNumActiveScreens; s++) { + free(be_extensions[s]); + } + free( be_extensions ); + + if (dmxGLXSwapGroupSupport) { + if (!denied_extensions || + !strstr(denied_extensions, "GLX_SGIX_swap_group")) { + strcat(ExtensionsString, "GLX_SGIX_swap_group"); + if (!denied_extensions || + !strstr(denied_extensions, "GLX_SGIX_swap_barrier")) { + strcat(ExtensionsString, " GLX_SGIX_swap_barrier"); + } + } + } + +} + +void __glXScreenInit(GLint numscreens) +{ + int s; + int c; + DMXScreenInfo *dmxScreen0 = &dmxScreens[0]; + __glXNumActiveScreens = numscreens; + + + CalcServerVersionAndExtensions(); + + + __glXFBConfigs = NULL; + __glXNumFBConfigs = 0; + + if ( (__glXVersionMajor == 1 && __glXVersionMinor >= 3) || + (__glXVersionMajor > 1) || + ( strstr(ExtensionsString, "GLX_SGIX_fbconfig") ) ) { + + /* + // Initialize FBConfig info. + // find the set of FBConfigs that are present on all back-end + // servers - only those configs will be supported + */ + __glXFBConfigs = (__GLXFBConfig **)malloc( dmxScreen0->numFBConfigs * + (numscreens+1) * sizeof(__GLXFBConfig *) ); + __glXNumFBConfigs = 0; + + for (c=0; cnumFBConfigs; c++) { + __GLXFBConfig *cfg = NULL; + + if (numscreens > 1) { + for (s=1; sfbconfigs[c], + dmxScreen->fbconfigs, + dmxScreen->numFBConfigs ); + __glXFBConfigs[ __glXNumFBConfigs * (numscreens+1) + s + 1 ] = cfg; + if (!cfg) { + dmxLog(dmxInfo,"screen0 FBConfig 0x%x is missing on screen#%d\n", dmxScreen0->fbconfigs[c].id, s); + break; + } + else { + dmxLog(dmxInfo,"screen0 FBConfig 0x%x matched to 0x%x on screen#%d\n", dmxScreen0->fbconfigs[c].id, cfg->id, s); + } + } + } + else { + cfg = &dmxScreen0->fbconfigs[c]; + } + + if (cfg) { + + /* filter out overlay visuals */ + if (cfg->level == 0) { + __GLXFBConfig *proxy_cfg; + + __glXFBConfigs[ __glXNumFBConfigs * (numscreens+1) + 1 ] = + &dmxScreen0->fbconfigs[c]; + + proxy_cfg = malloc( sizeof(__GLXFBConfig) ); + memcpy( proxy_cfg, cfg, sizeof(__GLXFBConfig) ); + proxy_cfg->id = FakeClientID(0); + /* visual will be associated later in __glXGetFBConfigs */ + proxy_cfg->associatedVisualId = (unsigned int)-1; + + __glXFBConfigs[ __glXNumFBConfigs * (numscreens+1) + 0 ] = proxy_cfg; + + __glXNumFBConfigs++; + } + + } + + } + + } + +} + +void __glXScreenReset(void) +{ + __glXNumActiveScreens = 0; +} + +char *__glXGetServerString( unsigned int name ) +{ + char *ret = NULL; + + switch( name) { + + case GLX_VENDOR: + ret = GLXServerVendorName; + break; + + case GLX_VERSION: + ret = GLXServerVersion; + break; + + case GLX_EXTENSIONS: + ret = ExtensionsString; + break; + + default: + break; + } + + return ret; + +} + +int glxIsExtensionSupported( char *ext ) +{ + return( strstr(ExtensionsString, ext) != NULL ); +} diff --git a/xorg-server/hw/dmx/glxProxy/glxserver.h b/xorg-server/hw/dmx/glxProxy/glxserver.h index b2be58219..03e95b865 100644 --- a/xorg-server/hw/dmx/glxProxy/glxserver.h +++ b/xorg-server/hw/dmx/glxProxy/glxserver.h @@ -1,309 +1,303 @@ -#ifndef _GLX_server_h_ -#define _GLX_server_h_ - -/* - * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) - * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice including the dates of first publication and - * either this permission notice or a reference to - * http://oss.sgi.com/projects/FreeB/ - * shall be included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF - * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - * Except as contained in this notice, the name of Silicon Graphics, Inc. - * shall not be used in advertising or otherwise to promote the sale, use or - * other dealings in this Software without prior written authorization from - * Silicon Graphics, Inc. - */ - -#include "dmx.h" - -#include -#include -#include -#include -#include -#include -#include - - -/* -** The X header misc.h defines these math functions. -*/ -#undef abs -#undef fabs - -#define GL_GLEXT_PROTOTYPES /* we want prototypes */ -#include -#include -#include - -#include "glxscreens.h" -#include "glxdrawable.h" -#include "glxcontext.h" -#include "glxerror.h" - - -#define GLX_SERVER_MAJOR_VERSION 1 -#define GLX_SERVER_MINOR_VERSION 3 - -#ifndef True -#define True 1 -#endif -#ifndef False -#define False 0 -#endif - -/* -** GLX resources. -typedef XID GLXContextID; -typedef XID GLXPixmap; -typedef XID GLXDrawable; -typedef XID GLXWindow; -typedef XID GLXPbuffer; - -typedef struct __GLXcontextRec *GLXContext; -*/ -typedef struct __GLXclientStateRec __GLXclientState; - -extern __GLXscreenInfo *__glXActiveScreens; -extern GLint __glXNumActiveScreens; - -/************************************************************************/ - -/* -** The last context used (from the server's persective) is cached. -*/ -extern __GLXcontext *__glXLastContext; -extern __GLXcontext *__glXForceCurrent(__GLXclientState*, GLXContextTag, int*); - -/************************************************************************/ - -typedef struct { - int elem_size; /* element size in bytes */ - int nelems; /* number of elements to swap */ - void (*swapfunc)(GLbyte *pc); -} __GLXRenderSwapInfo; - -/* -** State kept per client. -*/ -struct __GLXclientStateRec { - /* - ** Whether this structure is currently being used to support a client. - */ - Bool inUse; - - /* - ** Buffer for returned data. - */ - GLbyte *returnBuf; - GLint returnBufSize; - - /* - ** Keep a list of all the contexts that are current for this client's - ** threads. - */ - __GLXcontext **currentContexts; - DrawablePtr *currentDrawables; - GLint numCurrentContexts; - - /* Back pointer to X client record */ - ClientPtr client; - - int GLClientmajorVersion; - int GLClientminorVersion; - char *GLClientextensions; - - GLXContextTag *be_currentCTag; - Display **be_displays; - - /* - ** Keep track of large rendering commands, which span multiple requests. - */ - GLint largeCmdBytesSoFar; /* bytes received so far */ - GLint largeCmdBytesTotal; /* total bytes expected */ - GLint largeCmdRequestsSoFar; /* requests received so far */ - GLint largeCmdRequestsTotal; /* total requests expected */ - void (*largeCmdRequestsSwapProc)(GLbyte *); - __GLXRenderSwapInfo *largeCmdRequestsSwap_info; - GLbyte *largeCmdBuf; - GLint largeCmdBufSize; - GLint largeCmdMaxReqDataSize; - -}; - -extern __GLXclientState *__glXClients[]; - -/************************************************************************/ - -/* -** Dispatch tables. -*/ -typedef void (*__GLXdispatchRenderProcPtr)(GLbyte *); -typedef int (*__GLXdispatchSingleProcPtr)(__GLXclientState *, GLbyte *); -typedef int (*__GLXdispatchVendorPrivProcPtr)(__GLXclientState *, GLbyte *); -extern __GLXdispatchSingleProcPtr __glXSingleTable[]; -extern __GLXdispatchVendorPrivProcPtr __glXVendorPrivTable_EXT[]; -extern __GLXdispatchSingleProcPtr __glXSwapSingleTable[]; -extern __GLXdispatchVendorPrivProcPtr __glXSwapVendorPrivTable_EXT[]; -extern __GLXdispatchRenderProcPtr __glXSwapRenderTable[]; - -extern __GLXRenderSwapInfo __glXSwapRenderTable_EXT[]; - -/* - * Dispatch for GLX commands. - */ -typedef int (*__GLXprocPtr)(__GLXclientState *, char *pc); -extern __GLXprocPtr __glXProcTable[]; - -/* - * Tables for computing the size of each rendering command. - */ -typedef struct { - int bytes; - int (*varsize)(GLbyte *pc, Bool swap); -} __GLXrenderSizeData; -extern __GLXrenderSizeData __glXRenderSizeTable[]; -extern __GLXrenderSizeData __glXRenderSizeTable_EXT[]; - -/************************************************************************/ - -/* -** X resources. -*/ -extern RESTYPE __glXContextRes; -extern RESTYPE __glXClientRes; -extern RESTYPE __glXPixmapRes; -extern RESTYPE __glXDrawableRes; -extern RESTYPE __glXWindowRes; -extern RESTYPE __glXPbufferRes; - -/************************************************************************/ - -/* -** Prototypes. -*/ - - -extern char *__glXcombine_strings(const char *, const char *); - -extern void __glXDisp_DrawArrays(GLbyte*); -extern void __glXDispSwap_DrawArrays(GLbyte*); - - -/* -** Routines for sending swapped replies. -*/ - -extern void __glXSwapMakeCurrentReply(ClientPtr client, - xGLXMakeCurrentReadSGIReply *reply); - -extern void __glXSwapIsDirectReply(ClientPtr client, - xGLXIsDirectReply *reply); -extern void __glXSwapQueryVersionReply(ClientPtr client, - xGLXQueryVersionReply *reply); -extern void __glXSwapQueryContextInfoEXTReply(ClientPtr client, - xGLXQueryContextInfoEXTReply *reply, - int *buf); -extern void glxSwapQueryExtensionsStringReply(ClientPtr client, - xGLXQueryExtensionsStringReply *reply, char *buf); -extern void glxSwapQueryServerStringReply(ClientPtr client, - xGLXQueryServerStringReply *reply, char *buf); -extern void __glXSwapQueryContextReply(ClientPtr client, - xGLXQueryContextReply *reply, int *buf); -extern void __glXSwapGetDrawableAttributesReply(ClientPtr client, - xGLXGetDrawableAttributesReply *reply, int *buf); -extern void __glXSwapQueryMaxSwapBarriersSGIXReply(ClientPtr client, - xGLXQueryMaxSwapBarriersSGIXReply *reply); - -/* - * Routines for computing the size of variably-sized rendering commands. - */ - -extern int __glXTypeSize(GLenum enm); -extern int __glXImageSize(GLenum format, GLenum type, GLsizei w, GLsizei h, - GLint rowLength, GLint skipRows, GLint alignment); -extern int __glXImage3DSize(GLenum format, GLenum type, - GLsizei w, GLsizei h, GLsizei d, - GLint imageHeight, GLint rowLength, - GLint skipImages, GLint skipRows, - GLint alignment); - -extern int __glXCallListsReqSize(GLbyte *pc, Bool swap); -extern int __glXBitmapReqSize(GLbyte *pc, Bool swap); -extern int __glXFogfvReqSize(GLbyte *pc, Bool swap); -extern int __glXFogivReqSize(GLbyte *pc, Bool swap); -extern int __glXLightfvReqSize(GLbyte *pc, Bool swap); -extern int __glXLightivReqSize(GLbyte *pc, Bool swap); -extern int __glXLightModelfvReqSize(GLbyte *pc, Bool swap); -extern int __glXLightModelivReqSize(GLbyte *pc, Bool swap); -extern int __glXMaterialfvReqSize(GLbyte *pc, Bool swap); -extern int __glXMaterialivReqSize(GLbyte *pc, Bool swap); -extern int __glXTexParameterfvReqSize(GLbyte *pc, Bool swap); -extern int __glXTexParameterivReqSize(GLbyte *pc, Bool swap); -extern int __glXTexImage1DReqSize(GLbyte *pc, Bool swap); -extern int __glXTexImage2DReqSize(GLbyte *pc, Bool swap); -extern int __glXTexEnvfvReqSize(GLbyte *pc, Bool swap); -extern int __glXTexEnvivReqSize(GLbyte *pc, Bool swap); -extern int __glXTexGendvReqSize(GLbyte *pc, Bool swap); -extern int __glXTexGenfvReqSize(GLbyte *pc, Bool swap); -extern int __glXTexGenivReqSize(GLbyte *pc, Bool swap); -extern int __glXMap1dReqSize(GLbyte *pc, Bool swap); -extern int __glXMap1fReqSize(GLbyte *pc, Bool swap); -extern int __glXMap2dReqSize(GLbyte *pc, Bool swap); -extern int __glXMap2fReqSize(GLbyte *pc, Bool swap); -extern int __glXPixelMapfvReqSize(GLbyte *pc, Bool swap); -extern int __glXPixelMapuivReqSize(GLbyte *pc, Bool swap); -extern int __glXPixelMapusvReqSize(GLbyte *pc, Bool swap); -extern int __glXDrawPixelsReqSize(GLbyte *pc, Bool swap); -extern int __glXDrawArraysSize(GLbyte *pc, Bool swap); -extern int __glXPrioritizeTexturesReqSize(GLbyte *pc, Bool swap); -extern int __glXTexSubImage1DReqSize(GLbyte *pc, Bool swap); -extern int __glXTexSubImage2DReqSize(GLbyte *pc, Bool swap); -extern int __glXTexImage3DReqSize(GLbyte *pc, Bool swap ); -extern int __glXTexSubImage3DReqSize(GLbyte *pc, Bool swap); -extern int __glXConvolutionFilter1DReqSize(GLbyte *pc, Bool swap); -extern int __glXConvolutionFilter2DReqSize(GLbyte *pc, Bool swap); -extern int __glXConvolutionParameterivReqSize(GLbyte *pc, Bool swap); -extern int __glXConvolutionParameterfvReqSize(GLbyte *pc, Bool swap); -extern int __glXSeparableFilter2DReqSize(GLbyte *pc, Bool swap); -extern int __glXColorTableReqSize(GLbyte *pc, Bool swap); -extern int __glXColorSubTableReqSize(GLbyte *pc, Bool swap); -extern int __glXColorTableParameterfvReqSize(GLbyte *pc, Bool swap); -extern int __glXColorTableParameterivReqSize(GLbyte *pc, Bool swap); - -/* - * Routines for computing the size of returned data. - */ -extern int __glXConvolutionParameterivSize(GLenum pname); -extern int __glXConvolutionParameterfvSize(GLenum pname); -extern int __glXColorTableParameterfvSize(GLenum pname); -extern int __glXColorTableParameterivSize(GLenum pname); - -extern void __glXFreeGLXWindow(__glXWindow *pGlxWindow); -extern void __glXFreeGLXPbuffer(__glXPbuffer *pGlxPbuffer); - -extern int __glXVersionMajor; -extern int __glXVersionMinor; - -#define __GLX_IS_VERSION_SUPPORTED(major,minor) \ - ( (__glXVersionMajor > (major)) || \ - ((__glXVersionMajor == (major)) && (__glXVersionMinor >= (minor))) ) - -#endif /* !__GLX_server_h__ */ +#ifndef _GLX_server_h_ +#define _GLX_server_h_ + +/* + * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) + * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice including the dates of first publication and + * either this permission notice or a reference to + * http://oss.sgi.com/projects/FreeB/ + * shall be included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Except as contained in this notice, the name of Silicon Graphics, Inc. + * shall not be used in advertising or otherwise to promote the sale, use or + * other dealings in this Software without prior written authorization from + * Silicon Graphics, Inc. + */ + +#include "dmx.h" + +#include +#include +#include +#include +#include +#include +#include + + +#define GL_GLEXT_PROTOTYPES /* we want prototypes */ +#include +#include +#include + +#include "glxscreens.h" +#include "glxdrawable.h" +#include "glxcontext.h" +#include "glxerror.h" + + +#define GLX_SERVER_MAJOR_VERSION 1 +#define GLX_SERVER_MINOR_VERSION 3 + +#ifndef True +#define True 1 +#endif +#ifndef False +#define False 0 +#endif + +/* +** GLX resources. +typedef XID GLXContextID; +typedef XID GLXPixmap; +typedef XID GLXDrawable; +typedef XID GLXWindow; +typedef XID GLXPbuffer; + +typedef struct __GLXcontextRec *GLXContext; +*/ +typedef struct __GLXclientStateRec __GLXclientState; + +extern __GLXscreenInfo *__glXActiveScreens; +extern GLint __glXNumActiveScreens; + +/************************************************************************/ + +/* +** The last context used (from the server's persective) is cached. +*/ +extern __GLXcontext *__glXLastContext; +extern __GLXcontext *__glXForceCurrent(__GLXclientState*, GLXContextTag, int*); + +/************************************************************************/ + +typedef struct { + int elem_size; /* element size in bytes */ + int nelems; /* number of elements to swap */ + void (*swapfunc)(GLbyte *pc); +} __GLXRenderSwapInfo; + +/* +** State kept per client. +*/ +struct __GLXclientStateRec { + /* + ** Whether this structure is currently being used to support a client. + */ + Bool inUse; + + /* + ** Buffer for returned data. + */ + GLbyte *returnBuf; + GLint returnBufSize; + + /* + ** Keep a list of all the contexts that are current for this client's + ** threads. + */ + __GLXcontext **currentContexts; + DrawablePtr *currentDrawables; + GLint numCurrentContexts; + + /* Back pointer to X client record */ + ClientPtr client; + + int GLClientmajorVersion; + int GLClientminorVersion; + char *GLClientextensions; + + GLXContextTag *be_currentCTag; + Display **be_displays; + + /* + ** Keep track of large rendering commands, which span multiple requests. + */ + GLint largeCmdBytesSoFar; /* bytes received so far */ + GLint largeCmdBytesTotal; /* total bytes expected */ + GLint largeCmdRequestsSoFar; /* requests received so far */ + GLint largeCmdRequestsTotal; /* total requests expected */ + void (*largeCmdRequestsSwapProc)(GLbyte *); + __GLXRenderSwapInfo *largeCmdRequestsSwap_info; + GLbyte *largeCmdBuf; + GLint largeCmdBufSize; + GLint largeCmdMaxReqDataSize; + +}; + +extern __GLXclientState *__glXClients[]; + +/************************************************************************/ + +/* +** Dispatch tables. +*/ +typedef void (*__GLXdispatchRenderProcPtr)(GLbyte *); +typedef int (*__GLXdispatchSingleProcPtr)(__GLXclientState *, GLbyte *); +typedef int (*__GLXdispatchVendorPrivProcPtr)(__GLXclientState *, GLbyte *); +extern __GLXdispatchSingleProcPtr __glXSingleTable[]; +extern __GLXdispatchVendorPrivProcPtr __glXVendorPrivTable_EXT[]; +extern __GLXdispatchSingleProcPtr __glXSwapSingleTable[]; +extern __GLXdispatchVendorPrivProcPtr __glXSwapVendorPrivTable_EXT[]; +extern __GLXdispatchRenderProcPtr __glXSwapRenderTable[]; + +extern __GLXRenderSwapInfo __glXSwapRenderTable_EXT[]; + +/* + * Dispatch for GLX commands. + */ +typedef int (*__GLXprocPtr)(__GLXclientState *, char *pc); +extern __GLXprocPtr __glXProcTable[]; + +/* + * Tables for computing the size of each rendering command. + */ +typedef struct { + int bytes; + int (*varsize)(GLbyte *pc, Bool swap); +} __GLXrenderSizeData; +extern __GLXrenderSizeData __glXRenderSizeTable[]; +extern __GLXrenderSizeData __glXRenderSizeTable_EXT[]; + +/************************************************************************/ + +/* +** X resources. +*/ +extern RESTYPE __glXContextRes; +extern RESTYPE __glXClientRes; +extern RESTYPE __glXPixmapRes; +extern RESTYPE __glXDrawableRes; +extern RESTYPE __glXWindowRes; +extern RESTYPE __glXPbufferRes; + +/************************************************************************/ + +/* +** Prototypes. +*/ + + +extern char *__glXcombine_strings(const char *, const char *); + +extern void __glXDisp_DrawArrays(GLbyte*); +extern void __glXDispSwap_DrawArrays(GLbyte*); + + +/* +** Routines for sending swapped replies. +*/ + +extern void __glXSwapMakeCurrentReply(ClientPtr client, + xGLXMakeCurrentReadSGIReply *reply); + +extern void __glXSwapIsDirectReply(ClientPtr client, + xGLXIsDirectReply *reply); +extern void __glXSwapQueryVersionReply(ClientPtr client, + xGLXQueryVersionReply *reply); +extern void __glXSwapQueryContextInfoEXTReply(ClientPtr client, + xGLXQueryContextInfoEXTReply *reply, + int *buf); +extern void glxSwapQueryExtensionsStringReply(ClientPtr client, + xGLXQueryExtensionsStringReply *reply, char *buf); +extern void glxSwapQueryServerStringReply(ClientPtr client, + xGLXQueryServerStringReply *reply, char *buf); +extern void __glXSwapQueryContextReply(ClientPtr client, + xGLXQueryContextReply *reply, int *buf); +extern void __glXSwapGetDrawableAttributesReply(ClientPtr client, + xGLXGetDrawableAttributesReply *reply, int *buf); +extern void __glXSwapQueryMaxSwapBarriersSGIXReply(ClientPtr client, + xGLXQueryMaxSwapBarriersSGIXReply *reply); + +/* + * Routines for computing the size of variably-sized rendering commands. + */ + +extern int __glXTypeSize(GLenum enm); +extern int __glXImageSize(GLenum format, GLenum type, GLsizei w, GLsizei h, + GLint rowLength, GLint skipRows, GLint alignment); +extern int __glXImage3DSize(GLenum format, GLenum type, + GLsizei w, GLsizei h, GLsizei d, + GLint imageHeight, GLint rowLength, + GLint skipImages, GLint skipRows, + GLint alignment); + +extern int __glXCallListsReqSize(GLbyte *pc, Bool swap); +extern int __glXBitmapReqSize(GLbyte *pc, Bool swap); +extern int __glXFogfvReqSize(GLbyte *pc, Bool swap); +extern int __glXFogivReqSize(GLbyte *pc, Bool swap); +extern int __glXLightfvReqSize(GLbyte *pc, Bool swap); +extern int __glXLightivReqSize(GLbyte *pc, Bool swap); +extern int __glXLightModelfvReqSize(GLbyte *pc, Bool swap); +extern int __glXLightModelivReqSize(GLbyte *pc, Bool swap); +extern int __glXMaterialfvReqSize(GLbyte *pc, Bool swap); +extern int __glXMaterialivReqSize(GLbyte *pc, Bool swap); +extern int __glXTexParameterfvReqSize(GLbyte *pc, Bool swap); +extern int __glXTexParameterivReqSize(GLbyte *pc, Bool swap); +extern int __glXTexImage1DReqSize(GLbyte *pc, Bool swap); +extern int __glXTexImage2DReqSize(GLbyte *pc, Bool swap); +extern int __glXTexEnvfvReqSize(GLbyte *pc, Bool swap); +extern int __glXTexEnvivReqSize(GLbyte *pc, Bool swap); +extern int __glXTexGendvReqSize(GLbyte *pc, Bool swap); +extern int __glXTexGenfvReqSize(GLbyte *pc, Bool swap); +extern int __glXTexGenivReqSize(GLbyte *pc, Bool swap); +extern int __glXMap1dReqSize(GLbyte *pc, Bool swap); +extern int __glXMap1fReqSize(GLbyte *pc, Bool swap); +extern int __glXMap2dReqSize(GLbyte *pc, Bool swap); +extern int __glXMap2fReqSize(GLbyte *pc, Bool swap); +extern int __glXPixelMapfvReqSize(GLbyte *pc, Bool swap); +extern int __glXPixelMapuivReqSize(GLbyte *pc, Bool swap); +extern int __glXPixelMapusvReqSize(GLbyte *pc, Bool swap); +extern int __glXDrawPixelsReqSize(GLbyte *pc, Bool swap); +extern int __glXDrawArraysSize(GLbyte *pc, Bool swap); +extern int __glXPrioritizeTexturesReqSize(GLbyte *pc, Bool swap); +extern int __glXTexSubImage1DReqSize(GLbyte *pc, Bool swap); +extern int __glXTexSubImage2DReqSize(GLbyte *pc, Bool swap); +extern int __glXTexImage3DReqSize(GLbyte *pc, Bool swap ); +extern int __glXTexSubImage3DReqSize(GLbyte *pc, Bool swap); +extern int __glXConvolutionFilter1DReqSize(GLbyte *pc, Bool swap); +extern int __glXConvolutionFilter2DReqSize(GLbyte *pc, Bool swap); +extern int __glXConvolutionParameterivReqSize(GLbyte *pc, Bool swap); +extern int __glXConvolutionParameterfvReqSize(GLbyte *pc, Bool swap); +extern int __glXSeparableFilter2DReqSize(GLbyte *pc, Bool swap); +extern int __glXColorTableReqSize(GLbyte *pc, Bool swap); +extern int __glXColorSubTableReqSize(GLbyte *pc, Bool swap); +extern int __glXColorTableParameterfvReqSize(GLbyte *pc, Bool swap); +extern int __glXColorTableParameterivReqSize(GLbyte *pc, Bool swap); + +/* + * Routines for computing the size of returned data. + */ +extern int __glXConvolutionParameterivSize(GLenum pname); +extern int __glXConvolutionParameterfvSize(GLenum pname); +extern int __glXColorTableParameterfvSize(GLenum pname); +extern int __glXColorTableParameterivSize(GLenum pname); + +extern void __glXFreeGLXWindow(__glXWindow *pGlxWindow); +extern void __glXFreeGLXPbuffer(__glXPbuffer *pGlxPbuffer); + +extern int __glXVersionMajor; +extern int __glXVersionMinor; + +#define __GLX_IS_VERSION_SUPPORTED(major,minor) \ + ( (__glXVersionMajor > (major)) || \ + ((__glXVersionMajor == (major)) && (__glXVersionMinor >= (minor))) ) + +#endif /* !__GLX_server_h__ */ diff --git a/xorg-server/hw/dmx/glxProxy/render2swap.c b/xorg-server/hw/dmx/glxProxy/render2swap.c index 81bb501ea..da9b565de 100644 --- a/xorg-server/hw/dmx/glxProxy/render2swap.c +++ b/xorg-server/hw/dmx/glxProxy/render2swap.c @@ -32,7 +32,7 @@ #include "unpack.h" #include "g_disptab.h" -GLint __glEvalComputeK(GLenum target) +static GLint __glEvalComputeK(GLenum target) { switch (target) { case GL_MAP1_VERTEX_4: diff --git a/xorg-server/hw/dmx/input/dmxinputinit.c b/xorg-server/hw/dmx/input/dmxinputinit.c index 1b067c725..16ecae38d 100644 --- a/xorg-server/hw/dmx/input/dmxinputinit.c +++ b/xorg-server/hw/dmx/input/dmxinputinit.c @@ -671,9 +671,9 @@ static char *dmxMakeUniqueDeviceName(DMXLocalInputInfoPtr dmxLocal) } switch (dmxLocal->type) { - case DMX_LOCAL_KEYBOARD: XmuSnprintf(buf, LEN, "Keyboard%d", k++); break; - case DMX_LOCAL_MOUSE: XmuSnprintf(buf, LEN, "Mouse%d", m++); break; - default: XmuSnprintf(buf, LEN, "Other%d", o++); break; + case DMX_LOCAL_KEYBOARD: snprintf(buf, LEN, "Keyboard%d", k++); break; + case DMX_LOCAL_MOUSE: snprintf(buf, LEN, "Mouse%d", m++); break; + default: snprintf(buf, LEN, "Other%d", o++); break; } return buf; diff --git a/xorg-server/hw/dmx/input/lnx-keyboard.c b/xorg-server/hw/dmx/input/lnx-keyboard.c index 269e84435..06e9ec722 100644 --- a/xorg-server/hw/dmx/input/lnx-keyboard.c +++ b/xorg-server/hw/dmx/input/lnx-keyboard.c @@ -1,990 +1,990 @@ -/* Portions of this file were derived from the following files: - * - ********************************************************************** - * - * xfree86/common/{xf86Io.c,xf86Kbd.c,xf86Events.c} - * - * Copyright 1990,91 by Thomas Roell, Dinkelscherben, Germany. - * - * Permission to use, copy, modify, distribute, and sell this software and its - * documentation for any purpose is hereby granted without fee, provided that - * the above copyright notice appear in all copies and that both that - * copyright notice and this permission notice appear in supporting - * documentation, and that the name of Thomas Roell not be used in - * advertising or publicity pertaining to distribution of the software without - * specific, written prior permission. Thomas Roell makes no representations - * about the suitability of this software for any purpose. It is provided - * "as is" without express or implied warranty. - * - * THOMAS ROELL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, - * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO - * EVENT SHALL THOMAS ROELL BE LIABLE FOR ANY SPECIAL, INDIRECT OR - * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, - * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER - * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. - * - ********************************************************************** - * - * xfree86/common/xf86KbdLnx.c - * - * Linux version of keymapping setup. The kernel (since 0.99.14) has support - * for fully remapping the keyboard, but there are some differences between - * the Linux map and the SVR4 map (esp. in the extended keycodes). We also - * remove the restriction on what keycodes can be remapped. - * Orest Zborowski. - * - * Copyright 1990,91 by Thomas Roell, Dinkelscherben, Germany. - * - * Permission to use, copy, modify, distribute, and sell this software and its - * documentation for any purpose is hereby granted without fee, provided that - * the above copyright notice appear in all copies and that both that - * copyright notice and this permission notice appear in supporting - * documentation, and that the name of Thomas Roell not be used in - * advertising or publicity pertaining to distribution of the software without - * specific, written prior permission. Thomas Roell makes no representations - * about the suitability of this software for any purpose. It is provided - * "as is" without express or implied warranty. - * - * THOMAS ROELL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, - * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO - * EVENT SHALL THOMAS ROELL BE LIABLE FOR ANY SPECIAL, INDIRECT OR - * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, - * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER - * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. - * - ********************************************************************** - * - * xfree86/os-support/linux/lnx_io.c - * - * Copyright 1992 by Orest Zborowski - * Copyright 1993 by David Dawes - * - * Permission to use, copy, modify, distribute, and sell this software and its - * documentation for any purpose is hereby granted without fee, provided that - * the above copyright notice appear in all copies and that both that - * copyright notice and this permission notice appear in supporting - * documentation, and that the names of Orest Zborowski and David Dawes - * not be used in advertising or publicity pertaining to distribution of - * the software without specific, written prior permission. Orest Zborowski - * and David Dawes make no representations about the suitability of this - * software for any purpose. It is provided "as is" without express or - * implied warranty. - * - * OREST ZBOROWSKI AND DAVID DAWES DISCLAIMS ALL WARRANTIES WITH REGARD - * TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS, IN NO EVENT SHALL OREST ZBOROWSKI OR DAVID DAWES BE LIABLE - * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - */ - -/* - * Copyright 2001-2003 Red Hat Inc., Durham, North Carolina. - * - * All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation on the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial - * portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NON-INFRINGEMENT. IN NO EVENT SHALL RED HAT AND/OR THEIR SUPPLIERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/* - * Authors: - * Rickard E. (Rik) Faith - * - */ - -/** \file - * - * This code implements a low-level device driver for the Linux - * keyboard. The code is derived from code by Thomas Roell, Orest - * Zborowski, and David Dawes (see the source code for complete - * references). */ - -#ifdef HAVE_DMX_CONFIG_H -#include -#endif - -/*****************************************************************************/ -/* Define some macros to make it easier to move this file to another - * part of the Xserver tree. All calls to the dmx* layer are #defined - * here for the .c file. The .h file will also have to be edited. */ -#include "dmxinputinit.h" -#include "lnx-keyboard.h" - -#define GETPRIV myPrivate *priv \ - = ((DMXLocalInputInfoPtr)(pDev->devicePrivate))->private - -#define LOG0(f) dmxLog(dmxDebug,f) -#define LOG1(f,a) dmxLog(dmxDebug,f,a) -#define LOG2(f,a,b) dmxLog(dmxDebug,f,a,b) -#define LOG3(f,a,b,c) dmxLog(dmxDebug,f,a,b,c) -#define FATAL0(f) dmxLog(dmxFatal,f) -#define FATAL1(f,a) dmxLog(dmxFatal,f,a) -#define FATAL2(f,a,b) dmxLog(dmxFatal,f,a,b) -#define MOTIONPROC dmxMotionProcPtr -#define ENQUEUEPROC dmxEnqueueProcPtr -#define CHECKPROC dmxCheckSpecialProcPtr -#define SWITCHRETPROC dmxVTSwitchReturnProcPtr -#define BLOCK DMXBlockType -#define MESSAGE "\033c\n\n\nDMX taking input from this console..." -#define FINALMESSAGE "\033cDMX terminated." - -/* End of interface definitions. */ -/*****************************************************************************/ - -#include "inputstr.h" -#include -#include -#include -#include -#include -#include -#include -#include "atKeynames.h" -#if 00 -#include "xf86Keymap.h" -#endif -#include -#include - -#define NUM_AT2LNX (sizeof(at2lnx) / sizeof(at2lnx[0])) -#define NUM_STATE_ENTRIES (256/32) - - -/* Private area for Linux-style keyboards. */ -typedef struct _myPrivate { - int fd; - int vtno; - int vtcurrent; - int kbdtrans; - struct termios kbdtty; - int kbdType; - CARD32 kbdState[NUM_STATE_ENTRIES]; - DeviceIntPtr pKeyboard; - unsigned char prefix; - - int switched; - SWITCHRETPROC switch_return; - void *switch_return_data; - - /* For bell */ - int pitch; - unsigned long duration; -} myPrivate; - -static myPrivate *PRIV = NULL; - -#undef SYSCALL -#define SYSCALL(call) while(((call) == -1) && (errno == EINTR)) - -static int kbdLinuxKeyDown(myPrivate *priv, int keyCode) -{ - CARD8 byte = keyCode >> 5; - CARD32 bit = 1 << (keyCode & 0x1f); - - if (byte > NUM_STATE_ENTRIES) return 0; - return priv->kbdState[byte] & bit; -} - -static void kbdLinuxKeyState(myPrivate *priv, int type, int keyCode) -{ - CARD8 byte = keyCode >> 5; - CARD32 bit = 1 << (keyCode & 0x1f); - - if (byte > NUM_STATE_ENTRIES) return; - if (type == KeyPress) priv->kbdState[byte] |= bit; - else priv->kbdState[byte] &= ~bit; -} - -static KeySym linux_to_x[256] = { - NoSymbol, NoSymbol, NoSymbol, NoSymbol, - NoSymbol, NoSymbol, NoSymbol, NoSymbol, - XK_BackSpace, XK_Tab, XK_Linefeed, NoSymbol, - NoSymbol, NoSymbol, NoSymbol, NoSymbol, - NoSymbol, NoSymbol, NoSymbol, NoSymbol, - NoSymbol, NoSymbol, NoSymbol, NoSymbol, - NoSymbol, NoSymbol, NoSymbol, XK_Escape, - NoSymbol, NoSymbol, NoSymbol, NoSymbol, - XK_space, XK_exclam, XK_quotedbl, XK_numbersign, - XK_dollar, XK_percent, XK_ampersand, XK_apostrophe, - XK_parenleft, XK_parenright, XK_asterisk, XK_plus, - XK_comma, XK_minus, XK_period, XK_slash, - XK_0, XK_1, XK_2, XK_3, - XK_4, XK_5, XK_6, XK_7, - XK_8, XK_9, XK_colon, XK_semicolon, - XK_less, XK_equal, XK_greater, XK_question, - XK_at, XK_A, XK_B, XK_C, - XK_D, XK_E, XK_F, XK_G, - XK_H, XK_I, XK_J, XK_K, - XK_L, XK_M, XK_N, XK_O, - XK_P, XK_Q, XK_R, XK_S, - XK_T, XK_U, XK_V, XK_W, - XK_X, XK_Y, XK_Z, XK_bracketleft, - XK_backslash, XK_bracketright,XK_asciicircum, XK_underscore, - XK_grave, XK_a, XK_b, XK_c, - XK_d, XK_e, XK_f, XK_g, - XK_h, XK_i, XK_j, XK_k, - XK_l, XK_m, XK_n, XK_o, - XK_p, XK_q, XK_r, XK_s, - XK_t, XK_u, XK_v, XK_w, - XK_x, XK_y, XK_z, XK_braceleft, - XK_bar, XK_braceright, XK_asciitilde, XK_BackSpace, - NoSymbol, NoSymbol, NoSymbol, NoSymbol, - NoSymbol, NoSymbol, NoSymbol, NoSymbol, - NoSymbol, NoSymbol, NoSymbol, NoSymbol, - NoSymbol, NoSymbol, NoSymbol, NoSymbol, - NoSymbol, NoSymbol, NoSymbol, NoSymbol, - NoSymbol, NoSymbol, NoSymbol, NoSymbol, - NoSymbol, NoSymbol, NoSymbol, NoSymbol, - NoSymbol, NoSymbol, NoSymbol, NoSymbol, - XK_nobreakspace,XK_exclamdown, XK_cent, XK_sterling, - XK_currency, XK_yen, XK_brokenbar, XK_section, - XK_diaeresis, XK_copyright, XK_ordfeminine, XK_guillemotleft, - XK_notsign, XK_hyphen, XK_registered, XK_macron, - XK_degree, XK_plusminus, XK_twosuperior, XK_threesuperior, - XK_acute, XK_mu, XK_paragraph, XK_periodcentered, - XK_cedilla, XK_onesuperior, XK_masculine, XK_guillemotright, - XK_onequarter, XK_onehalf, XK_threequarters,XK_questiondown, - XK_Agrave, XK_Aacute, XK_Acircumflex, XK_Atilde, - XK_Adiaeresis, XK_Aring, XK_AE, XK_Ccedilla, - XK_Egrave, XK_Eacute, XK_Ecircumflex, XK_Ediaeresis, - XK_Igrave, XK_Iacute, XK_Icircumflex, XK_Idiaeresis, - XK_ETH, XK_Ntilde, XK_Ograve, XK_Oacute, - XK_Ocircumflex, XK_Otilde, XK_Odiaeresis, XK_multiply, - XK_Ooblique, XK_Ugrave, XK_Uacute, XK_Ucircumflex, - XK_Udiaeresis, XK_Yacute, XK_THORN, XK_ssharp, - XK_agrave, XK_aacute, XK_acircumflex, XK_atilde, - XK_adiaeresis, XK_aring, XK_ae, XK_ccedilla, - XK_egrave, XK_eacute, XK_ecircumflex, XK_ediaeresis, - XK_igrave, XK_iacute, XK_icircumflex, XK_idiaeresis, - XK_eth, XK_ntilde, XK_ograve, XK_oacute, - XK_ocircumflex, XK_otilde, XK_odiaeresis, XK_division, - XK_oslash, XK_ugrave, XK_uacute, XK_ucircumflex, - XK_udiaeresis, XK_yacute, XK_thorn, XK_ydiaeresis -}; - -/* - * Maps the AT keycodes to Linux keycodes - */ -static unsigned char at2lnx[NUM_KEYCODES] = -{ - 0x01, /* KEY_Escape */ 0x02, /* KEY_1 */ - 0x03, /* KEY_2 */ 0x04, /* KEY_3 */ - 0x05, /* KEY_4 */ 0x06, /* KEY_5 */ - 0x07, /* KEY_6 */ 0x08, /* KEY_7 */ - 0x09, /* KEY_8 */ 0x0a, /* KEY_9 */ - 0x0b, /* KEY_0 */ 0x0c, /* KEY_Minus */ - 0x0d, /* KEY_Equal */ 0x0e, /* KEY_BackSpace */ - 0x0f, /* KEY_Tab */ 0x10, /* KEY_Q */ - 0x11, /* KEY_W */ 0x12, /* KEY_E */ - 0x13, /* KEY_R */ 0x14, /* KEY_T */ - 0x15, /* KEY_Y */ 0x16, /* KEY_U */ - 0x17, /* KEY_I */ 0x18, /* KEY_O */ - 0x19, /* KEY_P */ 0x1a, /* KEY_LBrace */ - 0x1b, /* KEY_RBrace */ 0x1c, /* KEY_Enter */ - 0x1d, /* KEY_LCtrl */ 0x1e, /* KEY_A */ - 0x1f, /* KEY_S */ 0x20, /* KEY_D */ - 0x21, /* KEY_F */ 0x22, /* KEY_G */ - 0x23, /* KEY_H */ 0x24, /* KEY_J */ - 0x25, /* KEY_K */ 0x26, /* KEY_L */ - 0x27, /* KEY_SemiColon */ 0x28, /* KEY_Quote */ - 0x29, /* KEY_Tilde */ 0x2a, /* KEY_ShiftL */ - 0x2b, /* KEY_BSlash */ 0x2c, /* KEY_Z */ - 0x2d, /* KEY_X */ 0x2e, /* KEY_C */ - 0x2f, /* KEY_V */ 0x30, /* KEY_B */ - 0x31, /* KEY_N */ 0x32, /* KEY_M */ - 0x33, /* KEY_Comma */ 0x34, /* KEY_Period */ - 0x35, /* KEY_Slash */ 0x36, /* KEY_ShiftR */ - 0x37, /* KEY_KP_Multiply */ 0x38, /* KEY_Alt */ - 0x39, /* KEY_Space */ 0x3a, /* KEY_CapsLock */ - 0x3b, /* KEY_F1 */ 0x3c, /* KEY_F2 */ - 0x3d, /* KEY_F3 */ 0x3e, /* KEY_F4 */ - 0x3f, /* KEY_F5 */ 0x40, /* KEY_F6 */ - 0x41, /* KEY_F7 */ 0x42, /* KEY_F8 */ - 0x43, /* KEY_F9 */ 0x44, /* KEY_F10 */ - 0x45, /* KEY_NumLock */ 0x46, /* KEY_ScrollLock */ - 0x47, /* KEY_KP_7 */ 0x48, /* KEY_KP_8 */ - 0x49, /* KEY_KP_9 */ 0x4a, /* KEY_KP_Minus */ - 0x4b, /* KEY_KP_4 */ 0x4c, /* KEY_KP_5 */ - 0x4d, /* KEY_KP_6 */ 0x4e, /* KEY_KP_Plus */ - 0x4f, /* KEY_KP_1 */ 0x50, /* KEY_KP_2 */ - 0x51, /* KEY_KP_3 */ 0x52, /* KEY_KP_0 */ - 0x53, /* KEY_KP_Decimal */ 0x54, /* KEY_SysReqest */ - 0x00, /* 0x55 */ 0x56, /* KEY_Less */ - 0x57, /* KEY_F11 */ 0x58, /* KEY_F12 */ - 0x66, /* KEY_Home */ 0x67, /* KEY_Up */ - 0x68, /* KEY_PgUp */ 0x69, /* KEY_Left */ - 0x5d, /* KEY_Begin */ 0x6a, /* KEY_Right */ - 0x6b, /* KEY_End */ 0x6c, /* KEY_Down */ - 0x6d, /* KEY_PgDown */ 0x6e, /* KEY_Insert */ - 0x6f, /* KEY_Delete */ 0x60, /* KEY_KP_Enter */ - 0x61, /* KEY_RCtrl */ 0x77, /* KEY_Pause */ - 0x63, /* KEY_Print */ 0x62, /* KEY_KP_Divide */ - 0x64, /* KEY_AltLang */ 0x65, /* KEY_Break */ - 0x00, /* KEY_LMeta */ 0x00, /* KEY_RMeta */ - 0x7A, /* KEY_Menu/FOCUS_PF11*/0x00, /* 0x6e */ - 0x7B, /* FOCUS_PF12 */ 0x00, /* 0x70 */ - 0x00, /* 0x71 */ 0x00, /* 0x72 */ - 0x59, /* FOCUS_PF2 */ 0x78, /* FOCUS_PF9 */ - 0x00, /* 0x75 */ 0x00, /* 0x76 */ - 0x5A, /* FOCUS_PF3 */ 0x5B, /* FOCUS_PF4 */ - 0x5C, /* FOCUS_PF5 */ 0x5D, /* FOCUS_PF6 */ - 0x5E, /* FOCUS_PF7 */ 0x5F, /* FOCUS_PF8 */ - 0x7C, /* JAP_86 */ 0x79, /* FOCUS_PF10 */ - 0x00, /* 0x7f */ -}; - -/** Create a private structure for use within this file. */ -pointer kbdLinuxCreatePrivate(DeviceIntPtr pKeyboard) -{ - myPrivate *priv = calloc(1, sizeof(*priv)); - priv->fd = -1; - priv->pKeyboard = pKeyboard; - return priv; -} - -/** Destroy a private structure. */ -void kbdLinuxDestroyPrivate(pointer priv) -{ - free(priv); -} - -/** Ring the bell. - * - * Note: we completely ignore the \a volume, since Linux's ioctl() - * interface does not provide a way to control it. If it did, the XBell - * manpage tells how the actual volume is a function of the percent and - * the (base) volume. - * - * Note that most of the other PC-based bell drivers compute the - * duration for KDMKTONE as a function of the volume and the duration. - * For some drivers, the duration is only measured in mS if the volume - * is 50, and is scaled by the volume for other values. This seems - * confusing and possibly incorrect (the xset man page says that the - * bell will be "as closely as it can to the user's specifications" -- - * if we ignore the volume and set the duration correctly, then we'll - * get one parameter "wrong" -- but if we use the volume to scale the - * duration, then we'll get both parameters "wrong"). */ -void kbdLinuxBell(DevicePtr pDev, int percent, - int volume, int pitch, int duration) -{ - GETPRIV; - - if (duration && pitch) { - ioctl(priv->fd, - KDMKTONE, - ((1193190 / pitch) & 0xffff) /* Low bits specify cycle time */ - | (duration << 16)); /* High bits are duration in msec */ - } -} - -/** Set the LEDs. */ -void kbdLinuxCtrl(DevicePtr pDev, KeybdCtrl *ctrl) -{ - GETPRIV; - - ioctl(priv->fd, KDSETLED, ctrl->leds & 0x07); -} - -static int kbdLinuxGetFreeVTNumber(void) -{ - int fd = -1; - int vtno; - int i; - const char *tty0[] = { "/dev/tty0", "/dev/vc/0", NULL }; - - for (i = 0; tty0[i]; i++) - if ((fd = open(tty0[i], O_WRONLY, 0)) >= 0) break; - if (fd < 0) - FATAL1("kbdLinuxGetFreeVTNumber: Cannot open tty0 (%s)\n", - strerror(errno)); - if (ioctl(fd, VT_OPENQRY, &vtno) < 0 || vtno < 0) - FATAL0("kbdLinuxGetFreeVTNumber: Cannot find a free VT\n"); - return vtno; -} - -static int kbdLinuxOpenVT(int vtno) -{ - int fd = -1; - int i; - const char *vcs[] = { "/dev/vc/%d", "/dev/tty%d", NULL }; - char name[64]; /* RATS: Only used in XmuSnprintf */ - - for (i = 0; vcs[i]; i++) { - XmuSnprintf(name, sizeof(name), vcs[i], vtno); - if ((fd = open(name, O_RDWR | O_NONBLOCK, 0)) >= 0) break; - } - if (fd < 0) - FATAL2("kbdLinuxOpenVT: Cannot open VT %d (%s)\n", - vtno, strerror(errno)); - return fd; -} - -static int kbdLinuxGetCurrentVTNumber(int fd) -{ - struct vt_stat vts; - - if (!ioctl(fd, VT_GETSTATE, &vts)) return vts.v_active; - return -1; -} - -static int kbdLinuxActivate(int fd, int vtno, int setSig); - -/** Currently unused hook called prior to an VT switch. */ -void kbdLinuxVTPreSwitch(pointer p) -{ -} - -/** Currently unused hook called after returning from a VT switch. */ -void kbdLinuxVTPostSwitch(pointer p) -{ -} - -/** Tell the operating system to switch to \a vt. The \a switch_return - * function is called with the \a switch_return_data when the VT is - * switched back to the pre-switch VT (i.e., the user returns to the DMX - * session). */ -int kbdLinuxVTSwitch(pointer p, int vt, - void (*switch_return)(pointer), - pointer switch_return_data) -{ - myPrivate *priv = p; - - if (priv->switched) FATAL0("kbdLinuxVTSwitch: already switched...\n"); - if (priv->vtno == vt) return 0; - - PRIV = priv; - priv->switched = 0; /* Will switch to 1 in handler */ - priv->switch_return = switch_return; - priv->switch_return_data = switch_return_data; - kbdLinuxActivate(priv->fd, vt, 0); - return 1; -} - -/* RATS: This function is only ever used to handle SIGUSR1. */ -static void kbdLinuxVTSignalHandler(int sig) -{ - myPrivate *priv = PRIV; - - signal(sig, kbdLinuxVTSignalHandler); - if (priv) { - ioctl(priv->fd, VT_RELDISP, VT_ACKACQ); - priv->switched = !priv->switched; - LOG2("kbdLinuxVTSignalHandler: got signal %d, switched = %d\n", - sig, priv->switched); - if (!priv->switched && priv->switch_return) - priv->switch_return(priv->switch_return_data); - } -} - -static int kbdLinuxActivate(int fd, int vtno, int setSig) -{ - int result; - struct vt_mode VT; - - SYSCALL(result = ioctl(fd, VT_ACTIVATE, vtno)); - if (result) FATAL0("kbdLinuxActivate: VT_ACTIVATE failed\n"); - SYSCALL(result = ioctl(fd, VT_WAITACTIVE, vtno)); - if (result) FATAL0("kbdLinuxActivate: VT_WAITACTIVE failed\n"); - if (setSig) { - SYSCALL(result = ioctl(fd, VT_GETMODE, &VT)); - if (result < 0) FATAL0("kbdLinuxActivate: VT_GETMODE failed\n"); - VT.mode = VT_PROCESS; - VT.relsig = SIGUSR1; - VT.acqsig = SIGUSR1; - if (ioctl(fd, VT_SETMODE, &VT)) - FATAL0("kbdLinuxActivate: VT_SETMODE VT_PROCESS failed\n"); - signal(SIGUSR1, kbdLinuxVTSignalHandler); - } - return Success; -} - -static void kbdLinuxOpenConsole(DevicePtr pDev) -{ - GETPRIV; - const char *msg = MESSAGE; - - if (priv->fd >= 0) return; - priv->vtno = kbdLinuxGetFreeVTNumber(); - priv->fd = kbdLinuxOpenVT(priv->vtno); - priv->vtcurrent = kbdLinuxGetCurrentVTNumber(priv->fd); - LOG2("kbdLinuxOpenConsole: current VT %d; using free VT %d\n", - priv->vtcurrent, priv->vtno); - kbdLinuxActivate(priv->fd, priv->vtno, 1); - ioctl(priv->fd, KDSETMODE, KD_GRAPHICS); /* To turn off gpm */ - if (msg) write(priv->fd, msg, strlen(msg)); -} - -static void kbdLinuxCloseConsole(DevicePtr pDev) -{ - GETPRIV; - struct vt_mode VT; - const char *msg = FINALMESSAGE; - - if (priv->fd < 0) return; - - ioctl(priv->fd, KDSETMODE, KD_TEXT); - if (msg) write(priv->fd, msg, strlen(msg)); - if (ioctl(priv->fd, VT_GETMODE, &VT) != -1) { - VT.mode = VT_AUTO; - ioctl(priv->fd, VT_SETMODE, &VT); - } - - LOG1("kbdLinuxCloseConsole: switching to VT %d\n", priv->vtcurrent); - if (priv->vtcurrent >= 0) kbdLinuxActivate(priv->fd, priv->vtcurrent, 0); - - close(priv->fd); - priv->fd = -1; -} - -/** Initialize the \a pDev as a Linux keyboard. */ -void kbdLinuxInit(DevicePtr pDev) -{ - GETPRIV; - - if (priv->fd <= 0) kbdLinuxOpenConsole(pDev); - - ioctl(priv->fd, KDGKBMODE, &priv->kbdtrans); - if (tcgetattr(priv->fd, &priv->kbdtty) < 0) - FATAL1("kbdLinuxInit: tcgetattr failed (%s)\n", strerror(errno)); -} - -static int kbdLinuxPrefix0Mapping(unsigned char *scanCode) -{ - /* Table from xfree86/common/xf86Events.c */ - switch (*scanCode) { - case KEY_KP_7: *scanCode = KEY_Home; break; /* curs home */ - case KEY_KP_8: *scanCode = KEY_Up; break; /* curs up */ - case KEY_KP_9: *scanCode = KEY_PgUp; break; /* curs pgup */ - case KEY_KP_4: *scanCode = KEY_Left; break; /* curs left */ - case KEY_KP_5: *scanCode = KEY_Begin; break; /* curs begin */ - case KEY_KP_6: *scanCode = KEY_Right; break; /* curs right */ - case KEY_KP_1: *scanCode = KEY_End; break; /* curs end */ - case KEY_KP_2: *scanCode = KEY_Down; break; /* curs down */ - case KEY_KP_3: *scanCode = KEY_PgDown; break; /* curs pgdown */ - case KEY_KP_0: *scanCode = KEY_Insert; break; /* curs insert */ - case KEY_KP_Decimal: *scanCode = KEY_Delete; break; /* curs delete */ - case KEY_Enter: *scanCode = KEY_KP_Enter; break; /* keypad enter */ - case KEY_LCtrl: *scanCode = KEY_RCtrl; break; /* right ctrl */ - case KEY_KP_Multiply: *scanCode = KEY_Print; break; /* print */ - case KEY_Slash: *scanCode = KEY_KP_Divide; break; /* keyp divide */ - case KEY_Alt: *scanCode = KEY_AltLang; break; /* right alt */ - case KEY_ScrollLock: *scanCode = KEY_Break; break; /* curs break */ - case 0x5b: *scanCode = KEY_LMeta; break; - case 0x5c: *scanCode = KEY_RMeta; break; - case 0x5d: *scanCode = KEY_Menu; break; - case KEY_F3: *scanCode = KEY_F13; break; - case KEY_F4: *scanCode = KEY_F14; break; - case KEY_F5: *scanCode = KEY_F15; break; - case KEY_F6: *scanCode = KEY_F16; break; - case KEY_F7: *scanCode = KEY_F17; break; - case KEY_KP_Plus: *scanCode = KEY_KP_DEC; break; - /* - * Ignore virtual shifts (E0 2A, E0 AA, E0 36, E0 B6) - */ - case 0x2A: - case 0x36: - return 1; - default: - /* - * "Internet" keyboards are generating lots of new codes. - * Let them pass. There is little consistency between them, - * so don't bother with symbolic names at this level. - */ - scanCode += 0x78; - } - return 0; -} - -static int kbdLinuxPrefixMapping(myPrivate *priv, unsigned char *scanCode) -{ - int pressed = *scanCode & 0x80; - unsigned char code = *scanCode & 0x7f; - - /* If we don't have a prefix, check for one */ - if (!priv->prefix) { - switch (code) { - case KEY_Prefix0: - case KEY_Prefix1: - priv->prefix = code; - return 1; - } - return 0; /* No change */ - } - - /* We have a prefix from the last scanCode */ - switch (priv->prefix) { - case KEY_Prefix0: - priv->prefix = 0; - if (kbdLinuxPrefix0Mapping(&code)) return 1; /* Skip sequence */ - break; - case KEY_Prefix1: - priv->prefix = (code = KEY_LCtrl) ? KEY_LCtrl : 0; - return 1; /* Use new prefix */ - case KEY_LCtrl: - priv->prefix = 0; - if (code != KEY_NumLock) return 1; /* Skip sequence*/ - code = KEY_Pause; - break; - } - - *scanCode = code | (pressed ? 0x80 : 0x00); - return 0; /* Use old scanCode */ -} - -static void kbdLinuxConvert(DevicePtr pDev, - unsigned char scanCode, - ENQUEUEPROC enqueue, - CHECKPROC checkspecial, - BLOCK block) -{ - GETPRIV; - XkbSrvInfoPtr xkbi = priv->pKeyboard->key->xkbInfo; - int type; - KeySym keySym = NoSymbol; - int keyCode; - int switching; - - /* Do special PC/AT prefix mapping -- may change scanCode! */ - if (kbdLinuxPrefixMapping(priv, &scanCode)) return; - - type = (scanCode & 0x80) ? KeyRelease : KeyPress; - keyCode = (scanCode & 0x7f) + MIN_KEYCODE; - - /* Handle repeats */ - - if (keyCode >= xkbi->desc->min_key_code && - keyCode <= xkbi->desc->max_key_code) { - - int effectiveGroup = XkbGetEffectiveGroup(xkbi, - &xkbi->state, - scanCode); - keySym = XkbKeySym(xkbi->desc, scanCode, effectiveGroup); -#if 0 - switch (keySym) { - case XK_Num_Lock: - case XK_Scroll_Lock: - case XK_Shift_Lock: - case XK_Caps_Lock: - /* Ignore releases and all but first press */ - if (kbdLinuxModIgnore(priv, &xE, keySym)) return; - if (kbdLinuxKeyDown(priv, &xE)) xE.u.u.type = KeyRelease; - else xE.u.u.type = KeyPress; - break; - } -#endif - - /* If key is already down, ignore or autorepeat */ - if (type == KeyPress && kbdLinuxKeyDown(priv, keyCode)) { - KbdFeedbackClassRec *feed = priv->pKeyboard->kbdfeed; - - /* No auto-repeat? */ - if ((feed && !feed->ctrl.autoRepeat) - || priv->pKeyboard->key->xkbInfo->desc->map->modmap[keyCode] - || (feed - && !(feed->ctrl.autoRepeats[keyCode >> 3] - & (1 << (keyCode & 7))))) return; /* Ignore */ - - /* Do auto-repeat */ - enqueue(pDev, KeyRelease, keyCode, keySym, NULL, block); - type = KeyPress; - } - - /* If key is already up, ignore */ - if (type == KeyRelease && !kbdLinuxKeyDown(priv, keyCode)) return; - } - - switching = 0; - if (checkspecial && type == KeyPress) - switching = checkspecial(pDev, keySym); - if (!switching) { - if (enqueue) - enqueue(pDev, type, keyCode, keySym, NULL, block); - kbdLinuxKeyState(priv, type, keyCode); /* Update our state bitmap */ - } -} - -/** Read an event from the \a pDev device. If the event is a motion - * event, enqueue it with the \a motion function. Otherwise, check for - * special keys with the \a checkspecial function and enqueue the event - * with the \a enqueue function. The \a block type is passed to the - * functions so that they may block SIGIO handling as appropriate to the - * caller of this function. */ -void kbdLinuxRead(DevicePtr pDev, - MOTIONPROC motion, - ENQUEUEPROC enqueue, - CHECKPROC checkspecial, - BLOCK block) -{ - GETPRIV; - unsigned char buf[256]; /* RATS: Only used in length-limited call */ - unsigned char *pt; - int n; - - while ((n = read(priv->fd, buf, sizeof(buf))) > 0) - for (pt = buf; n; --n, ++pt) - kbdLinuxConvert(pDev, *pt, enqueue, checkspecial, block); -} - -/** Turn \a pDev on (i.e., take input from \a pDev). */ -int kbdLinuxOn(DevicePtr pDev) -{ - GETPRIV; - struct termios nTty; - - ioctl(priv->fd, KDSKBMODE, K_RAW); - - nTty = priv->kbdtty; - nTty.c_iflag = (IGNPAR | IGNBRK) & (~PARMRK) & (~ISTRIP); - nTty.c_oflag = 0; - nTty.c_cflag = CREAD | CS8; - nTty.c_lflag = 0; - nTty.c_cc[VTIME] = 0; - nTty.c_cc[VMIN] = 1; - cfsetispeed(&nTty, B9600); - cfsetospeed(&nTty, B9600); - if (tcsetattr(priv->fd, TCSANOW, &nTty) < 0) - FATAL1("kbdLinuxOn: tcsetattr failed (%s)\n", strerror(errno)); - return priv->fd; -} - -/** Turn \a pDev off (i.e., stop taking input from \a pDev). */ -void kbdLinuxOff(DevicePtr pDev) -{ - GETPRIV; - - ioctl(priv->fd, KDSKBMODE, priv->kbdtrans); - tcsetattr(priv->fd, TCSANOW, &priv->kbdtty); - kbdLinuxCloseConsole(pDev); -} - - -static void kbdLinuxReadKernelMapping(int fd, KeySymsPtr pKeySyms) -{ - KeySym *k; - int i; - int maxkey; - static unsigned char tbl[GLYPHS_PER_KEY] = { /* RATS: Use ok */ - 0, /* unshifted */ - 1, /* shifted */ - 0, /* modeswitch unshifted */ - 0 /* modeswitch shifted */ - }; - - /* - * Read the mapping from the kernel. - * Since we're still using the XFree86 scancode->AT keycode mapping - * routines, we need to convert the AT keycodes to Linux keycodes, - * then translate the Linux keysyms into X keysyms. - * - * First, figure out which tables to use for the modeswitch columns - * above, from the XF86Config fields. - */ - tbl[2] = 8; /* alt */ - tbl[3] = tbl[2] | 1; - -#if 00/*BP*/ - k = map+GLYPHS_PER_KEY; -#else - ErrorF("kbdLinuxReadKernelMapping() is broken/no-op'd\n"); - return; -#endif - maxkey = NUM_AT2LNX; - - for (i = 0; i < maxkey; ++i) { - struct kbentry kbe; - int j; - - kbe.kb_index = at2lnx[i]; - - for (j = 0; j < GLYPHS_PER_KEY; ++j, ++k) { - unsigned short kval; - - *k = NoSymbol; - - kbe.kb_table = tbl[j]; - if (kbe.kb_index == 0 || ioctl(fd, KDGKBENT, &kbe)) continue; - - kval = KVAL(kbe.kb_value); - switch (KTYP(kbe.kb_value)) { - case KT_LATIN: - case KT_LETTER: *k = linux_to_x[kval]; break; - case KT_FN: - if (kval <= 19) *k = XK_F1 + kval; - else switch (kbe.kb_value) { - case K_FIND: *k = XK_Home; /* or XK_Find */ break; - case K_INSERT: *k = XK_Insert; break; - case K_REMOVE: *k = XK_Delete; break; - case K_SELECT: *k = XK_End; /* or XK_Select */ break; - case K_PGUP: *k = XK_Prior; break; - case K_PGDN: *k = XK_Next; break; - case K_HELP: *k = XK_Help; break; - case K_DO: *k = XK_Execute; break; - case K_PAUSE: *k = XK_Pause; break; - case K_MACRO: *k = XK_Menu; break; - default: break; - } - break; - case KT_SPEC: - switch (kbe.kb_value) { - case K_ENTER: *k = XK_Return; break; - case K_BREAK: *k = XK_Break; break; - case K_CAPS: *k = XK_Caps_Lock; break; - case K_NUM: *k = XK_Num_Lock; break; - case K_HOLD: *k = XK_Scroll_Lock; break; - case K_COMPOSE: *k = XK_Multi_key; break; - default: break; - } - break; - case KT_PAD: - switch (kbe.kb_value) { - case K_PPLUS: *k = XK_KP_Add; break; - case K_PMINUS: *k = XK_KP_Subtract; break; - case K_PSTAR: *k = XK_KP_Multiply; break; - case K_PSLASH: *k = XK_KP_Divide; break; - case K_PENTER: *k = XK_KP_Enter; break; - case K_PCOMMA: *k = XK_KP_Separator; break; - case K_PDOT: *k = XK_KP_Decimal; break; - case K_PPLUSMINUS: *k = XK_KP_Subtract; break; - default: if (kval <= 9) *k = XK_KP_0 + kval; break; - } - break; - case KT_DEAD: - /* KT_DEAD keys are for accelerated diacritical creation. */ - switch (kbe.kb_value) { - case K_DGRAVE: *k = XK_dead_grave; break; - case K_DACUTE: *k = XK_dead_acute; break; - case K_DCIRCM: *k = XK_dead_circumflex; break; - case K_DTILDE: *k = XK_dead_tilde; break; - case K_DDIERE: *k = XK_dead_diaeresis; break; - } - break; - case KT_CUR: - switch (kbe.kb_value) { - case K_DOWN: *k = XK_Down; break; - case K_LEFT: *k = XK_Left; break; - case K_RIGHT: *k = XK_Right; break; - case K_UP: *k = XK_Up; break; - } - break; - case KT_SHIFT: - switch (kbe.kb_value) { - case K_ALTGR: *k = XK_Alt_R; break; - case K_ALT: - *k = (kbe.kb_index == 0x64 ? XK_Alt_R : XK_Alt_L); - break; - case K_CTRL: - *k = (kbe.kb_index == 0x61 ? XK_Control_R : XK_Control_L); - break; - case K_CTRLL: *k = XK_Control_L; break; - case K_CTRLR: *k = XK_Control_R; break; - case K_SHIFT: - *k = (kbe.kb_index == 0x36 ? XK_Shift_R : XK_Shift_L); - break; - case K_SHIFTL: *k = XK_Shift_L; break; - case K_SHIFTR: *k = XK_Shift_R; break; - default: break; - } - break; - case KT_ASCII: - /* KT_ASCII keys accumulate a 3 digit decimal number that - * gets emitted when the shift state changes. We can't - * emulate that. - */ - break; - case KT_LOCK: - if (kbe.kb_value == K_SHIFTLOCK) *k = XK_Shift_Lock; - break; - default: break; - } - } - - if (k[-1] == k[-2]) k[-1] = NoSymbol; - if (k[-2] == k[-3]) k[-2] = NoSymbol; - if (k[-3] == k[-4]) k[-3] = NoSymbol; - if (k[-4] == k[-2] && k[-3] == k[-1]) k[-2] = k[-1] = NoSymbol; - if (k[-1] == k[-4] && k[-2] == k[-3] - && k[-2] == NoSymbol) k[-1] = NoSymbol; - } -} - -static void kbdLinuxGetMap(DevicePtr pDev, KeySymsPtr pKeySyms, CARD8 *pModMap) -{ - GETPRIV; - KeySym *k, *mapCopy; - char type; - int i; - -#if 00/*BP*/ - mapCopy = malloc(sizeof(map)); - memcpy(mapCopy, map, sizeof(map)); -#else - ErrorF("kbdLinuxGetMap() is broken/no-op'd\n"); - return; -#endif - - kbdLinuxReadKernelMapping(priv->fd, pKeySyms); - - /* compute the modifier map */ - for (i = 0; i < MAP_LENGTH; i++) - pModMap[i] = NoSymbol; /* make sure it is restored */ - - for (k = mapCopy, i = MIN_KEYCODE; - i < NUM_KEYCODES + MIN_KEYCODE; - i++, k += 4) { - switch(*k) { - case XK_Shift_L: - case XK_Shift_R: pModMap[i] = ShiftMask; break; - case XK_Control_L: - case XK_Control_R: pModMap[i] = ControlMask; break; - case XK_Caps_Lock: pModMap[i] = LockMask; break; - case XK_Alt_L: - case XK_Alt_R: pModMap[i] = AltMask; break; - case XK_Num_Lock: pModMap[i] = NumLockMask; break; - case XK_Scroll_Lock: pModMap[i] = ScrollLockMask; break; - case XK_Kana_Lock: - case XK_Kana_Shift: pModMap[i] = KanaMask; break; - case XK_Mode_switch: pModMap[i] = AltLangMask; break; - } - } - - priv->kbdType = (ioctl(priv->fd, KDGKBTYPE, &type) < 0) ? KB_101 : type; - - pKeySyms->map = mapCopy; /* Must be XFree'able */ - pKeySyms->mapWidth = GLYPHS_PER_KEY; - pKeySyms->minKeyCode = MIN_KEYCODE; - pKeySyms->maxKeyCode = MAX_KEYCODE; -} - -/** Fill the \a info structure with information needed to initialize \a - * pDev. */ -void kbdLinuxGetInfo(DevicePtr pDev, DMXLocalInitInfoPtr info) -{ - info->keyboard = 1; - info->keyClass = 1; - kbdLinuxGetMap(pDev, &info->keySyms, info->modMap); - info->focusClass = 1; - info->kbdFeedbackClass = 1; -} +/* Portions of this file were derived from the following files: + * + ********************************************************************** + * + * xfree86/common/{xf86Io.c,xf86Kbd.c,xf86Events.c} + * + * Copyright 1990,91 by Thomas Roell, Dinkelscherben, Germany. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of Thomas Roell not be used in + * advertising or publicity pertaining to distribution of the software without + * specific, written prior permission. Thomas Roell makes no representations + * about the suitability of this software for any purpose. It is provided + * "as is" without express or implied warranty. + * + * THOMAS ROELL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THOMAS ROELL BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + * + ********************************************************************** + * + * xfree86/common/xf86KbdLnx.c + * + * Linux version of keymapping setup. The kernel (since 0.99.14) has support + * for fully remapping the keyboard, but there are some differences between + * the Linux map and the SVR4 map (esp. in the extended keycodes). We also + * remove the restriction on what keycodes can be remapped. + * Orest Zborowski. + * + * Copyright 1990,91 by Thomas Roell, Dinkelscherben, Germany. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of Thomas Roell not be used in + * advertising or publicity pertaining to distribution of the software without + * specific, written prior permission. Thomas Roell makes no representations + * about the suitability of this software for any purpose. It is provided + * "as is" without express or implied warranty. + * + * THOMAS ROELL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THOMAS ROELL BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + * + ********************************************************************** + * + * xfree86/os-support/linux/lnx_io.c + * + * Copyright 1992 by Orest Zborowski + * Copyright 1993 by David Dawes + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the names of Orest Zborowski and David Dawes + * not be used in advertising or publicity pertaining to distribution of + * the software without specific, written prior permission. Orest Zborowski + * and David Dawes make no representations about the suitability of this + * software for any purpose. It is provided "as is" without express or + * implied warranty. + * + * OREST ZBOROWSKI AND DAVID DAWES DISCLAIMS ALL WARRANTIES WITH REGARD + * TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS, IN NO EVENT SHALL OREST ZBOROWSKI OR DAVID DAWES BE LIABLE + * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ + +/* + * Copyright 2001-2003 Red Hat Inc., Durham, North Carolina. + * + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation on the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NON-INFRINGEMENT. IN NO EVENT SHALL RED HAT AND/OR THEIR SUPPLIERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* + * Authors: + * Rickard E. (Rik) Faith + * + */ + +/** \file + * + * This code implements a low-level device driver for the Linux + * keyboard. The code is derived from code by Thomas Roell, Orest + * Zborowski, and David Dawes (see the source code for complete + * references). */ + +#ifdef HAVE_DMX_CONFIG_H +#include +#endif + +/*****************************************************************************/ +/* Define some macros to make it easier to move this file to another + * part of the Xserver tree. All calls to the dmx* layer are #defined + * here for the .c file. The .h file will also have to be edited. */ +#include "dmxinputinit.h" +#include "lnx-keyboard.h" + +#define GETPRIV myPrivate *priv \ + = ((DMXLocalInputInfoPtr)(pDev->devicePrivate))->private + +#define LOG0(f) dmxLog(dmxDebug,f) +#define LOG1(f,a) dmxLog(dmxDebug,f,a) +#define LOG2(f,a,b) dmxLog(dmxDebug,f,a,b) +#define LOG3(f,a,b,c) dmxLog(dmxDebug,f,a,b,c) +#define FATAL0(f) dmxLog(dmxFatal,f) +#define FATAL1(f,a) dmxLog(dmxFatal,f,a) +#define FATAL2(f,a,b) dmxLog(dmxFatal,f,a,b) +#define MOTIONPROC dmxMotionProcPtr +#define ENQUEUEPROC dmxEnqueueProcPtr +#define CHECKPROC dmxCheckSpecialProcPtr +#define SWITCHRETPROC dmxVTSwitchReturnProcPtr +#define BLOCK DMXBlockType +#define MESSAGE "\033c\n\n\nDMX taking input from this console..." +#define FINALMESSAGE "\033cDMX terminated." + +/* End of interface definitions. */ +/*****************************************************************************/ + +#include "inputstr.h" +#include +#include +#include +#include +#include +#include +#include +#include "atKeynames.h" +#if 00 +#include "xf86Keymap.h" +#endif +#include +#include + +#define NUM_AT2LNX (sizeof(at2lnx) / sizeof(at2lnx[0])) +#define NUM_STATE_ENTRIES (256/32) + + +/* Private area for Linux-style keyboards. */ +typedef struct _myPrivate { + int fd; + int vtno; + int vtcurrent; + int kbdtrans; + struct termios kbdtty; + int kbdType; + CARD32 kbdState[NUM_STATE_ENTRIES]; + DeviceIntPtr pKeyboard; + unsigned char prefix; + + int switched; + SWITCHRETPROC switch_return; + void *switch_return_data; + + /* For bell */ + int pitch; + unsigned long duration; +} myPrivate; + +static myPrivate *PRIV = NULL; + +#undef SYSCALL +#define SYSCALL(call) while(((call) == -1) && (errno == EINTR)) + +static int kbdLinuxKeyDown(myPrivate *priv, int keyCode) +{ + CARD8 byte = keyCode >> 5; + CARD32 bit = 1 << (keyCode & 0x1f); + + if (byte > NUM_STATE_ENTRIES) return 0; + return priv->kbdState[byte] & bit; +} + +static void kbdLinuxKeyState(myPrivate *priv, int type, int keyCode) +{ + CARD8 byte = keyCode >> 5; + CARD32 bit = 1 << (keyCode & 0x1f); + + if (byte > NUM_STATE_ENTRIES) return; + if (type == KeyPress) priv->kbdState[byte] |= bit; + else priv->kbdState[byte] &= ~bit; +} + +static KeySym linux_to_x[256] = { + NoSymbol, NoSymbol, NoSymbol, NoSymbol, + NoSymbol, NoSymbol, NoSymbol, NoSymbol, + XK_BackSpace, XK_Tab, XK_Linefeed, NoSymbol, + NoSymbol, NoSymbol, NoSymbol, NoSymbol, + NoSymbol, NoSymbol, NoSymbol, NoSymbol, + NoSymbol, NoSymbol, NoSymbol, NoSymbol, + NoSymbol, NoSymbol, NoSymbol, XK_Escape, + NoSymbol, NoSymbol, NoSymbol, NoSymbol, + XK_space, XK_exclam, XK_quotedbl, XK_numbersign, + XK_dollar, XK_percent, XK_ampersand, XK_apostrophe, + XK_parenleft, XK_parenright, XK_asterisk, XK_plus, + XK_comma, XK_minus, XK_period, XK_slash, + XK_0, XK_1, XK_2, XK_3, + XK_4, XK_5, XK_6, XK_7, + XK_8, XK_9, XK_colon, XK_semicolon, + XK_less, XK_equal, XK_greater, XK_question, + XK_at, XK_A, XK_B, XK_C, + XK_D, XK_E, XK_F, XK_G, + XK_H, XK_I, XK_J, XK_K, + XK_L, XK_M, XK_N, XK_O, + XK_P, XK_Q, XK_R, XK_S, + XK_T, XK_U, XK_V, XK_W, + XK_X, XK_Y, XK_Z, XK_bracketleft, + XK_backslash, XK_bracketright,XK_asciicircum, XK_underscore, + XK_grave, XK_a, XK_b, XK_c, + XK_d, XK_e, XK_f, XK_g, + XK_h, XK_i, XK_j, XK_k, + XK_l, XK_m, XK_n, XK_o, + XK_p, XK_q, XK_r, XK_s, + XK_t, XK_u, XK_v, XK_w, + XK_x, XK_y, XK_z, XK_braceleft, + XK_bar, XK_braceright, XK_asciitilde, XK_BackSpace, + NoSymbol, NoSymbol, NoSymbol, NoSymbol, + NoSymbol, NoSymbol, NoSymbol, NoSymbol, + NoSymbol, NoSymbol, NoSymbol, NoSymbol, + NoSymbol, NoSymbol, NoSymbol, NoSymbol, + NoSymbol, NoSymbol, NoSymbol, NoSymbol, + NoSymbol, NoSymbol, NoSymbol, NoSymbol, + NoSymbol, NoSymbol, NoSymbol, NoSymbol, + NoSymbol, NoSymbol, NoSymbol, NoSymbol, + XK_nobreakspace,XK_exclamdown, XK_cent, XK_sterling, + XK_currency, XK_yen, XK_brokenbar, XK_section, + XK_diaeresis, XK_copyright, XK_ordfeminine, XK_guillemotleft, + XK_notsign, XK_hyphen, XK_registered, XK_macron, + XK_degree, XK_plusminus, XK_twosuperior, XK_threesuperior, + XK_acute, XK_mu, XK_paragraph, XK_periodcentered, + XK_cedilla, XK_onesuperior, XK_masculine, XK_guillemotright, + XK_onequarter, XK_onehalf, XK_threequarters,XK_questiondown, + XK_Agrave, XK_Aacute, XK_Acircumflex, XK_Atilde, + XK_Adiaeresis, XK_Aring, XK_AE, XK_Ccedilla, + XK_Egrave, XK_Eacute, XK_Ecircumflex, XK_Ediaeresis, + XK_Igrave, XK_Iacute, XK_Icircumflex, XK_Idiaeresis, + XK_ETH, XK_Ntilde, XK_Ograve, XK_Oacute, + XK_Ocircumflex, XK_Otilde, XK_Odiaeresis, XK_multiply, + XK_Ooblique, XK_Ugrave, XK_Uacute, XK_Ucircumflex, + XK_Udiaeresis, XK_Yacute, XK_THORN, XK_ssharp, + XK_agrave, XK_aacute, XK_acircumflex, XK_atilde, + XK_adiaeresis, XK_aring, XK_ae, XK_ccedilla, + XK_egrave, XK_eacute, XK_ecircumflex, XK_ediaeresis, + XK_igrave, XK_iacute, XK_icircumflex, XK_idiaeresis, + XK_eth, XK_ntilde, XK_ograve, XK_oacute, + XK_ocircumflex, XK_otilde, XK_odiaeresis, XK_division, + XK_oslash, XK_ugrave, XK_uacute, XK_ucircumflex, + XK_udiaeresis, XK_yacute, XK_thorn, XK_ydiaeresis +}; + +/* + * Maps the AT keycodes to Linux keycodes + */ +static unsigned char at2lnx[NUM_KEYCODES] = +{ + 0x01, /* KEY_Escape */ 0x02, /* KEY_1 */ + 0x03, /* KEY_2 */ 0x04, /* KEY_3 */ + 0x05, /* KEY_4 */ 0x06, /* KEY_5 */ + 0x07, /* KEY_6 */ 0x08, /* KEY_7 */ + 0x09, /* KEY_8 */ 0x0a, /* KEY_9 */ + 0x0b, /* KEY_0 */ 0x0c, /* KEY_Minus */ + 0x0d, /* KEY_Equal */ 0x0e, /* KEY_BackSpace */ + 0x0f, /* KEY_Tab */ 0x10, /* KEY_Q */ + 0x11, /* KEY_W */ 0x12, /* KEY_E */ + 0x13, /* KEY_R */ 0x14, /* KEY_T */ + 0x15, /* KEY_Y */ 0x16, /* KEY_U */ + 0x17, /* KEY_I */ 0x18, /* KEY_O */ + 0x19, /* KEY_P */ 0x1a, /* KEY_LBrace */ + 0x1b, /* KEY_RBrace */ 0x1c, /* KEY_Enter */ + 0x1d, /* KEY_LCtrl */ 0x1e, /* KEY_A */ + 0x1f, /* KEY_S */ 0x20, /* KEY_D */ + 0x21, /* KEY_F */ 0x22, /* KEY_G */ + 0x23, /* KEY_H */ 0x24, /* KEY_J */ + 0x25, /* KEY_K */ 0x26, /* KEY_L */ + 0x27, /* KEY_SemiColon */ 0x28, /* KEY_Quote */ + 0x29, /* KEY_Tilde */ 0x2a, /* KEY_ShiftL */ + 0x2b, /* KEY_BSlash */ 0x2c, /* KEY_Z */ + 0x2d, /* KEY_X */ 0x2e, /* KEY_C */ + 0x2f, /* KEY_V */ 0x30, /* KEY_B */ + 0x31, /* KEY_N */ 0x32, /* KEY_M */ + 0x33, /* KEY_Comma */ 0x34, /* KEY_Period */ + 0x35, /* KEY_Slash */ 0x36, /* KEY_ShiftR */ + 0x37, /* KEY_KP_Multiply */ 0x38, /* KEY_Alt */ + 0x39, /* KEY_Space */ 0x3a, /* KEY_CapsLock */ + 0x3b, /* KEY_F1 */ 0x3c, /* KEY_F2 */ + 0x3d, /* KEY_F3 */ 0x3e, /* KEY_F4 */ + 0x3f, /* KEY_F5 */ 0x40, /* KEY_F6 */ + 0x41, /* KEY_F7 */ 0x42, /* KEY_F8 */ + 0x43, /* KEY_F9 */ 0x44, /* KEY_F10 */ + 0x45, /* KEY_NumLock */ 0x46, /* KEY_ScrollLock */ + 0x47, /* KEY_KP_7 */ 0x48, /* KEY_KP_8 */ + 0x49, /* KEY_KP_9 */ 0x4a, /* KEY_KP_Minus */ + 0x4b, /* KEY_KP_4 */ 0x4c, /* KEY_KP_5 */ + 0x4d, /* KEY_KP_6 */ 0x4e, /* KEY_KP_Plus */ + 0x4f, /* KEY_KP_1 */ 0x50, /* KEY_KP_2 */ + 0x51, /* KEY_KP_3 */ 0x52, /* KEY_KP_0 */ + 0x53, /* KEY_KP_Decimal */ 0x54, /* KEY_SysReqest */ + 0x00, /* 0x55 */ 0x56, /* KEY_Less */ + 0x57, /* KEY_F11 */ 0x58, /* KEY_F12 */ + 0x66, /* KEY_Home */ 0x67, /* KEY_Up */ + 0x68, /* KEY_PgUp */ 0x69, /* KEY_Left */ + 0x5d, /* KEY_Begin */ 0x6a, /* KEY_Right */ + 0x6b, /* KEY_End */ 0x6c, /* KEY_Down */ + 0x6d, /* KEY_PgDown */ 0x6e, /* KEY_Insert */ + 0x6f, /* KEY_Delete */ 0x60, /* KEY_KP_Enter */ + 0x61, /* KEY_RCtrl */ 0x77, /* KEY_Pause */ + 0x63, /* KEY_Print */ 0x62, /* KEY_KP_Divide */ + 0x64, /* KEY_AltLang */ 0x65, /* KEY_Break */ + 0x00, /* KEY_LMeta */ 0x00, /* KEY_RMeta */ + 0x7A, /* KEY_Menu/FOCUS_PF11*/0x00, /* 0x6e */ + 0x7B, /* FOCUS_PF12 */ 0x00, /* 0x70 */ + 0x00, /* 0x71 */ 0x00, /* 0x72 */ + 0x59, /* FOCUS_PF2 */ 0x78, /* FOCUS_PF9 */ + 0x00, /* 0x75 */ 0x00, /* 0x76 */ + 0x5A, /* FOCUS_PF3 */ 0x5B, /* FOCUS_PF4 */ + 0x5C, /* FOCUS_PF5 */ 0x5D, /* FOCUS_PF6 */ + 0x5E, /* FOCUS_PF7 */ 0x5F, /* FOCUS_PF8 */ + 0x7C, /* JAP_86 */ 0x79, /* FOCUS_PF10 */ + 0x00, /* 0x7f */ +}; + +/** Create a private structure for use within this file. */ +pointer kbdLinuxCreatePrivate(DeviceIntPtr pKeyboard) +{ + myPrivate *priv = calloc(1, sizeof(*priv)); + priv->fd = -1; + priv->pKeyboard = pKeyboard; + return priv; +} + +/** Destroy a private structure. */ +void kbdLinuxDestroyPrivate(pointer priv) +{ + free(priv); +} + +/** Ring the bell. + * + * Note: we completely ignore the \a volume, since Linux's ioctl() + * interface does not provide a way to control it. If it did, the XBell + * manpage tells how the actual volume is a function of the percent and + * the (base) volume. + * + * Note that most of the other PC-based bell drivers compute the + * duration for KDMKTONE as a function of the volume and the duration. + * For some drivers, the duration is only measured in mS if the volume + * is 50, and is scaled by the volume for other values. This seems + * confusing and possibly incorrect (the xset man page says that the + * bell will be "as closely as it can to the user's specifications" -- + * if we ignore the volume and set the duration correctly, then we'll + * get one parameter "wrong" -- but if we use the volume to scale the + * duration, then we'll get both parameters "wrong"). */ +void kbdLinuxBell(DevicePtr pDev, int percent, + int volume, int pitch, int duration) +{ + GETPRIV; + + if (duration && pitch) { + ioctl(priv->fd, + KDMKTONE, + ((1193190 / pitch) & 0xffff) /* Low bits specify cycle time */ + | (duration << 16)); /* High bits are duration in msec */ + } +} + +/** Set the LEDs. */ +void kbdLinuxCtrl(DevicePtr pDev, KeybdCtrl *ctrl) +{ + GETPRIV; + + ioctl(priv->fd, KDSETLED, ctrl->leds & 0x07); +} + +static int kbdLinuxGetFreeVTNumber(void) +{ + int fd = -1; + int vtno; + int i; + const char *tty0[] = { "/dev/tty0", "/dev/vc/0", NULL }; + + for (i = 0; tty0[i]; i++) + if ((fd = open(tty0[i], O_WRONLY, 0)) >= 0) break; + if (fd < 0) + FATAL1("kbdLinuxGetFreeVTNumber: Cannot open tty0 (%s)\n", + strerror(errno)); + if (ioctl(fd, VT_OPENQRY, &vtno) < 0 || vtno < 0) + FATAL0("kbdLinuxGetFreeVTNumber: Cannot find a free VT\n"); + return vtno; +} + +static int kbdLinuxOpenVT(int vtno) +{ + int fd = -1; + int i; + const char *vcs[] = { "/dev/vc/", "/dev/tty", NULL }; + char name[64]; /* RATS: Only used in snprintf */ + + for (i = 0; vcs[i]; i++) { + snprintf(name, sizeof(name), "%s%d", vcs[i], vtno); + if ((fd = open(name, O_RDWR | O_NONBLOCK, 0)) >= 0) break; + } + if (fd < 0) + FATAL2("kbdLinuxOpenVT: Cannot open VT %d (%s)\n", + vtno, strerror(errno)); + return fd; +} + +static int kbdLinuxGetCurrentVTNumber(int fd) +{ + struct vt_stat vts; + + if (!ioctl(fd, VT_GETSTATE, &vts)) return vts.v_active; + return -1; +} + +static int kbdLinuxActivate(int fd, int vtno, int setSig); + +/** Currently unused hook called prior to an VT switch. */ +void kbdLinuxVTPreSwitch(pointer p) +{ +} + +/** Currently unused hook called after returning from a VT switch. */ +void kbdLinuxVTPostSwitch(pointer p) +{ +} + +/** Tell the operating system to switch to \a vt. The \a switch_return + * function is called with the \a switch_return_data when the VT is + * switched back to the pre-switch VT (i.e., the user returns to the DMX + * session). */ +int kbdLinuxVTSwitch(pointer p, int vt, + void (*switch_return)(pointer), + pointer switch_return_data) +{ + myPrivate *priv = p; + + if (priv->switched) FATAL0("kbdLinuxVTSwitch: already switched...\n"); + if (priv->vtno == vt) return 0; + + PRIV = priv; + priv->switched = 0; /* Will switch to 1 in handler */ + priv->switch_return = switch_return; + priv->switch_return_data = switch_return_data; + kbdLinuxActivate(priv->fd, vt, 0); + return 1; +} + +/* RATS: This function is only ever used to handle SIGUSR1. */ +static void kbdLinuxVTSignalHandler(int sig) +{ + myPrivate *priv = PRIV; + + signal(sig, kbdLinuxVTSignalHandler); + if (priv) { + ioctl(priv->fd, VT_RELDISP, VT_ACKACQ); + priv->switched = !priv->switched; + LOG2("kbdLinuxVTSignalHandler: got signal %d, switched = %d\n", + sig, priv->switched); + if (!priv->switched && priv->switch_return) + priv->switch_return(priv->switch_return_data); + } +} + +static int kbdLinuxActivate(int fd, int vtno, int setSig) +{ + int result; + struct vt_mode VT; + + SYSCALL(result = ioctl(fd, VT_ACTIVATE, vtno)); + if (result) FATAL0("kbdLinuxActivate: VT_ACTIVATE failed\n"); + SYSCALL(result = ioctl(fd, VT_WAITACTIVE, vtno)); + if (result) FATAL0("kbdLinuxActivate: VT_WAITACTIVE failed\n"); + if (setSig) { + SYSCALL(result = ioctl(fd, VT_GETMODE, &VT)); + if (result < 0) FATAL0("kbdLinuxActivate: VT_GETMODE failed\n"); + VT.mode = VT_PROCESS; + VT.relsig = SIGUSR1; + VT.acqsig = SIGUSR1; + if (ioctl(fd, VT_SETMODE, &VT)) + FATAL0("kbdLinuxActivate: VT_SETMODE VT_PROCESS failed\n"); + signal(SIGUSR1, kbdLinuxVTSignalHandler); + } + return Success; +} + +static void kbdLinuxOpenConsole(DevicePtr pDev) +{ + GETPRIV; + const char *msg = MESSAGE; + + if (priv->fd >= 0) return; + priv->vtno = kbdLinuxGetFreeVTNumber(); + priv->fd = kbdLinuxOpenVT(priv->vtno); + priv->vtcurrent = kbdLinuxGetCurrentVTNumber(priv->fd); + LOG2("kbdLinuxOpenConsole: current VT %d; using free VT %d\n", + priv->vtcurrent, priv->vtno); + kbdLinuxActivate(priv->fd, priv->vtno, 1); + ioctl(priv->fd, KDSETMODE, KD_GRAPHICS); /* To turn off gpm */ + if (msg) write(priv->fd, msg, strlen(msg)); +} + +static void kbdLinuxCloseConsole(DevicePtr pDev) +{ + GETPRIV; + struct vt_mode VT; + const char *msg = FINALMESSAGE; + + if (priv->fd < 0) return; + + ioctl(priv->fd, KDSETMODE, KD_TEXT); + if (msg) write(priv->fd, msg, strlen(msg)); + if (ioctl(priv->fd, VT_GETMODE, &VT) != -1) { + VT.mode = VT_AUTO; + ioctl(priv->fd, VT_SETMODE, &VT); + } + + LOG1("kbdLinuxCloseConsole: switching to VT %d\n", priv->vtcurrent); + if (priv->vtcurrent >= 0) kbdLinuxActivate(priv->fd, priv->vtcurrent, 0); + + close(priv->fd); + priv->fd = -1; +} + +/** Initialize the \a pDev as a Linux keyboard. */ +void kbdLinuxInit(DevicePtr pDev) +{ + GETPRIV; + + if (priv->fd <= 0) kbdLinuxOpenConsole(pDev); + + ioctl(priv->fd, KDGKBMODE, &priv->kbdtrans); + if (tcgetattr(priv->fd, &priv->kbdtty) < 0) + FATAL1("kbdLinuxInit: tcgetattr failed (%s)\n", strerror(errno)); +} + +static int kbdLinuxPrefix0Mapping(unsigned char *scanCode) +{ + /* Table from xfree86/common/xf86Events.c */ + switch (*scanCode) { + case KEY_KP_7: *scanCode = KEY_Home; break; /* curs home */ + case KEY_KP_8: *scanCode = KEY_Up; break; /* curs up */ + case KEY_KP_9: *scanCode = KEY_PgUp; break; /* curs pgup */ + case KEY_KP_4: *scanCode = KEY_Left; break; /* curs left */ + case KEY_KP_5: *scanCode = KEY_Begin; break; /* curs begin */ + case KEY_KP_6: *scanCode = KEY_Right; break; /* curs right */ + case KEY_KP_1: *scanCode = KEY_End; break; /* curs end */ + case KEY_KP_2: *scanCode = KEY_Down; break; /* curs down */ + case KEY_KP_3: *scanCode = KEY_PgDown; break; /* curs pgdown */ + case KEY_KP_0: *scanCode = KEY_Insert; break; /* curs insert */ + case KEY_KP_Decimal: *scanCode = KEY_Delete; break; /* curs delete */ + case KEY_Enter: *scanCode = KEY_KP_Enter; break; /* keypad enter */ + case KEY_LCtrl: *scanCode = KEY_RCtrl; break; /* right ctrl */ + case KEY_KP_Multiply: *scanCode = KEY_Print; break; /* print */ + case KEY_Slash: *scanCode = KEY_KP_Divide; break; /* keyp divide */ + case KEY_Alt: *scanCode = KEY_AltLang; break; /* right alt */ + case KEY_ScrollLock: *scanCode = KEY_Break; break; /* curs break */ + case 0x5b: *scanCode = KEY_LMeta; break; + case 0x5c: *scanCode = KEY_RMeta; break; + case 0x5d: *scanCode = KEY_Menu; break; + case KEY_F3: *scanCode = KEY_F13; break; + case KEY_F4: *scanCode = KEY_F14; break; + case KEY_F5: *scanCode = KEY_F15; break; + case KEY_F6: *scanCode = KEY_F16; break; + case KEY_F7: *scanCode = KEY_F17; break; + case KEY_KP_Plus: *scanCode = KEY_KP_DEC; break; + /* + * Ignore virtual shifts (E0 2A, E0 AA, E0 36, E0 B6) + */ + case 0x2A: + case 0x36: + return 1; + default: + /* + * "Internet" keyboards are generating lots of new codes. + * Let them pass. There is little consistency between them, + * so don't bother with symbolic names at this level. + */ + scanCode += 0x78; + } + return 0; +} + +static int kbdLinuxPrefixMapping(myPrivate *priv, unsigned char *scanCode) +{ + int pressed = *scanCode & 0x80; + unsigned char code = *scanCode & 0x7f; + + /* If we don't have a prefix, check for one */ + if (!priv->prefix) { + switch (code) { + case KEY_Prefix0: + case KEY_Prefix1: + priv->prefix = code; + return 1; + } + return 0; /* No change */ + } + + /* We have a prefix from the last scanCode */ + switch (priv->prefix) { + case KEY_Prefix0: + priv->prefix = 0; + if (kbdLinuxPrefix0Mapping(&code)) return 1; /* Skip sequence */ + break; + case KEY_Prefix1: + priv->prefix = (code = KEY_LCtrl) ? KEY_LCtrl : 0; + return 1; /* Use new prefix */ + case KEY_LCtrl: + priv->prefix = 0; + if (code != KEY_NumLock) return 1; /* Skip sequence*/ + code = KEY_Pause; + break; + } + + *scanCode = code | (pressed ? 0x80 : 0x00); + return 0; /* Use old scanCode */ +} + +static void kbdLinuxConvert(DevicePtr pDev, + unsigned char scanCode, + ENQUEUEPROC enqueue, + CHECKPROC checkspecial, + BLOCK block) +{ + GETPRIV; + XkbSrvInfoPtr xkbi = priv->pKeyboard->key->xkbInfo; + int type; + KeySym keySym = NoSymbol; + int keyCode; + int switching; + + /* Do special PC/AT prefix mapping -- may change scanCode! */ + if (kbdLinuxPrefixMapping(priv, &scanCode)) return; + + type = (scanCode & 0x80) ? KeyRelease : KeyPress; + keyCode = (scanCode & 0x7f) + MIN_KEYCODE; + + /* Handle repeats */ + + if (keyCode >= xkbi->desc->min_key_code && + keyCode <= xkbi->desc->max_key_code) { + + int effectiveGroup = XkbGetEffectiveGroup(xkbi, + &xkbi->state, + scanCode); + keySym = XkbKeySym(xkbi->desc, scanCode, effectiveGroup); +#if 0 + switch (keySym) { + case XK_Num_Lock: + case XK_Scroll_Lock: + case XK_Shift_Lock: + case XK_Caps_Lock: + /* Ignore releases and all but first press */ + if (kbdLinuxModIgnore(priv, &xE, keySym)) return; + if (kbdLinuxKeyDown(priv, &xE)) xE.u.u.type = KeyRelease; + else xE.u.u.type = KeyPress; + break; + } +#endif + + /* If key is already down, ignore or autorepeat */ + if (type == KeyPress && kbdLinuxKeyDown(priv, keyCode)) { + KbdFeedbackClassRec *feed = priv->pKeyboard->kbdfeed; + + /* No auto-repeat? */ + if ((feed && !feed->ctrl.autoRepeat) + || priv->pKeyboard->key->xkbInfo->desc->map->modmap[keyCode] + || (feed + && !(feed->ctrl.autoRepeats[keyCode >> 3] + & (1 << (keyCode & 7))))) return; /* Ignore */ + + /* Do auto-repeat */ + enqueue(pDev, KeyRelease, keyCode, keySym, NULL, block); + type = KeyPress; + } + + /* If key is already up, ignore */ + if (type == KeyRelease && !kbdLinuxKeyDown(priv, keyCode)) return; + } + + switching = 0; + if (checkspecial && type == KeyPress) + switching = checkspecial(pDev, keySym); + if (!switching) { + if (enqueue) + enqueue(pDev, type, keyCode, keySym, NULL, block); + kbdLinuxKeyState(priv, type, keyCode); /* Update our state bitmap */ + } +} + +/** Read an event from the \a pDev device. If the event is a motion + * event, enqueue it with the \a motion function. Otherwise, check for + * special keys with the \a checkspecial function and enqueue the event + * with the \a enqueue function. The \a block type is passed to the + * functions so that they may block SIGIO handling as appropriate to the + * caller of this function. */ +void kbdLinuxRead(DevicePtr pDev, + MOTIONPROC motion, + ENQUEUEPROC enqueue, + CHECKPROC checkspecial, + BLOCK block) +{ + GETPRIV; + unsigned char buf[256]; /* RATS: Only used in length-limited call */ + unsigned char *pt; + int n; + + while ((n = read(priv->fd, buf, sizeof(buf))) > 0) + for (pt = buf; n; --n, ++pt) + kbdLinuxConvert(pDev, *pt, enqueue, checkspecial, block); +} + +/** Turn \a pDev on (i.e., take input from \a pDev). */ +int kbdLinuxOn(DevicePtr pDev) +{ + GETPRIV; + struct termios nTty; + + ioctl(priv->fd, KDSKBMODE, K_RAW); + + nTty = priv->kbdtty; + nTty.c_iflag = (IGNPAR | IGNBRK) & (~PARMRK) & (~ISTRIP); + nTty.c_oflag = 0; + nTty.c_cflag = CREAD | CS8; + nTty.c_lflag = 0; + nTty.c_cc[VTIME] = 0; + nTty.c_cc[VMIN] = 1; + cfsetispeed(&nTty, B9600); + cfsetospeed(&nTty, B9600); + if (tcsetattr(priv->fd, TCSANOW, &nTty) < 0) + FATAL1("kbdLinuxOn: tcsetattr failed (%s)\n", strerror(errno)); + return priv->fd; +} + +/** Turn \a pDev off (i.e., stop taking input from \a pDev). */ +void kbdLinuxOff(DevicePtr pDev) +{ + GETPRIV; + + ioctl(priv->fd, KDSKBMODE, priv->kbdtrans); + tcsetattr(priv->fd, TCSANOW, &priv->kbdtty); + kbdLinuxCloseConsole(pDev); +} + + +static void kbdLinuxReadKernelMapping(int fd, KeySymsPtr pKeySyms) +{ + KeySym *k; + int i; + int maxkey; + static unsigned char tbl[GLYPHS_PER_KEY] = { /* RATS: Use ok */ + 0, /* unshifted */ + 1, /* shifted */ + 0, /* modeswitch unshifted */ + 0 /* modeswitch shifted */ + }; + + /* + * Read the mapping from the kernel. + * Since we're still using the XFree86 scancode->AT keycode mapping + * routines, we need to convert the AT keycodes to Linux keycodes, + * then translate the Linux keysyms into X keysyms. + * + * First, figure out which tables to use for the modeswitch columns + * above, from the XF86Config fields. + */ + tbl[2] = 8; /* alt */ + tbl[3] = tbl[2] | 1; + +#if 00/*BP*/ + k = map+GLYPHS_PER_KEY; +#else + ErrorF("kbdLinuxReadKernelMapping() is broken/no-op'd\n"); + return; +#endif + maxkey = NUM_AT2LNX; + + for (i = 0; i < maxkey; ++i) { + struct kbentry kbe; + int j; + + kbe.kb_index = at2lnx[i]; + + for (j = 0; j < GLYPHS_PER_KEY; ++j, ++k) { + unsigned short kval; + + *k = NoSymbol; + + kbe.kb_table = tbl[j]; + if (kbe.kb_index == 0 || ioctl(fd, KDGKBENT, &kbe)) continue; + + kval = KVAL(kbe.kb_value); + switch (KTYP(kbe.kb_value)) { + case KT_LATIN: + case KT_LETTER: *k = linux_to_x[kval]; break; + case KT_FN: + if (kval <= 19) *k = XK_F1 + kval; + else switch (kbe.kb_value) { + case K_FIND: *k = XK_Home; /* or XK_Find */ break; + case K_INSERT: *k = XK_Insert; break; + case K_REMOVE: *k = XK_Delete; break; + case K_SELECT: *k = XK_End; /* or XK_Select */ break; + case K_PGUP: *k = XK_Prior; break; + case K_PGDN: *k = XK_Next; break; + case K_HELP: *k = XK_Help; break; + case K_DO: *k = XK_Execute; break; + case K_PAUSE: *k = XK_Pause; break; + case K_MACRO: *k = XK_Menu; break; + default: break; + } + break; + case KT_SPEC: + switch (kbe.kb_value) { + case K_ENTER: *k = XK_Return; break; + case K_BREAK: *k = XK_Break; break; + case K_CAPS: *k = XK_Caps_Lock; break; + case K_NUM: *k = XK_Num_Lock; break; + case K_HOLD: *k = XK_Scroll_Lock; break; + case K_COMPOSE: *k = XK_Multi_key; break; + default: break; + } + break; + case KT_PAD: + switch (kbe.kb_value) { + case K_PPLUS: *k = XK_KP_Add; break; + case K_PMINUS: *k = XK_KP_Subtract; break; + case K_PSTAR: *k = XK_KP_Multiply; break; + case K_PSLASH: *k = XK_KP_Divide; break; + case K_PENTER: *k = XK_KP_Enter; break; + case K_PCOMMA: *k = XK_KP_Separator; break; + case K_PDOT: *k = XK_KP_Decimal; break; + case K_PPLUSMINUS: *k = XK_KP_Subtract; break; + default: if (kval <= 9) *k = XK_KP_0 + kval; break; + } + break; + case KT_DEAD: + /* KT_DEAD keys are for accelerated diacritical creation. */ + switch (kbe.kb_value) { + case K_DGRAVE: *k = XK_dead_grave; break; + case K_DACUTE: *k = XK_dead_acute; break; + case K_DCIRCM: *k = XK_dead_circumflex; break; + case K_DTILDE: *k = XK_dead_tilde; break; + case K_DDIERE: *k = XK_dead_diaeresis; break; + } + break; + case KT_CUR: + switch (kbe.kb_value) { + case K_DOWN: *k = XK_Down; break; + case K_LEFT: *k = XK_Left; break; + case K_RIGHT: *k = XK_Right; break; + case K_UP: *k = XK_Up; break; + } + break; + case KT_SHIFT: + switch (kbe.kb_value) { + case K_ALTGR: *k = XK_Alt_R; break; + case K_ALT: + *k = (kbe.kb_index == 0x64 ? XK_Alt_R : XK_Alt_L); + break; + case K_CTRL: + *k = (kbe.kb_index == 0x61 ? XK_Control_R : XK_Control_L); + break; + case K_CTRLL: *k = XK_Control_L; break; + case K_CTRLR: *k = XK_Control_R; break; + case K_SHIFT: + *k = (kbe.kb_index == 0x36 ? XK_Shift_R : XK_Shift_L); + break; + case K_SHIFTL: *k = XK_Shift_L; break; + case K_SHIFTR: *k = XK_Shift_R; break; + default: break; + } + break; + case KT_ASCII: + /* KT_ASCII keys accumulate a 3 digit decimal number that + * gets emitted when the shift state changes. We can't + * emulate that. + */ + break; + case KT_LOCK: + if (kbe.kb_value == K_SHIFTLOCK) *k = XK_Shift_Lock; + break; + default: break; + } + } + + if (k[-1] == k[-2]) k[-1] = NoSymbol; + if (k[-2] == k[-3]) k[-2] = NoSymbol; + if (k[-3] == k[-4]) k[-3] = NoSymbol; + if (k[-4] == k[-2] && k[-3] == k[-1]) k[-2] = k[-1] = NoSymbol; + if (k[-1] == k[-4] && k[-2] == k[-3] + && k[-2] == NoSymbol) k[-1] = NoSymbol; + } +} + +static void kbdLinuxGetMap(DevicePtr pDev, KeySymsPtr pKeySyms, CARD8 *pModMap) +{ + GETPRIV; + KeySym *k, *mapCopy; + char type; + int i; + +#if 00/*BP*/ + mapCopy = malloc(sizeof(map)); + memcpy(mapCopy, map, sizeof(map)); +#else + ErrorF("kbdLinuxGetMap() is broken/no-op'd\n"); + return; +#endif + + kbdLinuxReadKernelMapping(priv->fd, pKeySyms); + + /* compute the modifier map */ + for (i = 0; i < MAP_LENGTH; i++) + pModMap[i] = NoSymbol; /* make sure it is restored */ + + for (k = mapCopy, i = MIN_KEYCODE; + i < NUM_KEYCODES + MIN_KEYCODE; + i++, k += 4) { + switch(*k) { + case XK_Shift_L: + case XK_Shift_R: pModMap[i] = ShiftMask; break; + case XK_Control_L: + case XK_Control_R: pModMap[i] = ControlMask; break; + case XK_Caps_Lock: pModMap[i] = LockMask; break; + case XK_Alt_L: + case XK_Alt_R: pModMap[i] = AltMask; break; + case XK_Num_Lock: pModMap[i] = NumLockMask; break; + case XK_Scroll_Lock: pModMap[i] = ScrollLockMask; break; + case XK_Kana_Lock: + case XK_Kana_Shift: pModMap[i] = KanaMask; break; + case XK_Mode_switch: pModMap[i] = AltLangMask; break; + } + } + + priv->kbdType = (ioctl(priv->fd, KDGKBTYPE, &type) < 0) ? KB_101 : type; + + pKeySyms->map = mapCopy; /* Must be XFree'able */ + pKeySyms->mapWidth = GLYPHS_PER_KEY; + pKeySyms->minKeyCode = MIN_KEYCODE; + pKeySyms->maxKeyCode = MAX_KEYCODE; +} + +/** Fill the \a info structure with information needed to initialize \a + * pDev. */ +void kbdLinuxGetInfo(DevicePtr pDev, DMXLocalInitInfoPtr info) +{ + info->keyboard = 1; + info->keyClass = 1; + kbdLinuxGetMap(pDev, &info->keySyms, info->modMap); + info->focusClass = 1; + info->kbdFeedbackClass = 1; +} diff --git a/xorg-server/hw/dmx/input/usb-common.c b/xorg-server/hw/dmx/input/usb-common.c index c655a2984..944033eba 100644 --- a/xorg-server/hw/dmx/input/usb-common.c +++ b/xorg-server/hw/dmx/input/usb-common.c @@ -1,381 +1,381 @@ -/* - * Copyright 2002-2003 Red Hat Inc., Durham, North Carolina. - * - * All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation on the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial - * portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NON-INFRINGEMENT. IN NO EVENT SHALL RED HAT AND/OR THEIR SUPPLIERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/* - * Authors: - * Rickard E. (Rik) Faith - * - */ - -/** \file - * - * Routines that are common between \a usb-keyboard.c, \a usb-mouse.c, and - * \a usb-other.c */ - -#ifdef HAVE_DMX_CONFIG_H -#include -#endif - -#include "usb-private.h" - -#define USB_COMMON_DEBUG 1 - -/*****************************************************************************/ -/* Define some macros to make it easier to move this file to another - * part of the Xserver tree. All calls to the dmx* layer are #defined - * here for the .c file. The .h file will also have to be edited. */ -#include "usb-mouse.h" - -#define GETPRIV myPrivate *priv \ - = ((DMXLocalInputInfoPtr)(pDev->devicePrivate))->private - -#define GETNAME ((DMXLocalInputInfoPtr)(pDevice->public.devicePrivate)) \ - ->name - -#define LOG0(f) dmxLog(dmxDebug,f) -#define LOG1(f,a) dmxLog(dmxDebug,f,a) -#define LOG2(f,a,b) dmxLog(dmxDebug,f,a,b) -#define LOG3(f,a,b,c) dmxLog(dmxDebug,f,a,b,c) -#define LOG1INPUT(p,f,a) dmxLogInput(p->dmxInput,f,a) -#define LOG3INPUT(p,f,a,b,c) dmxLogInput(p->dmxInput,f,a,b,c) -#define LOG5INPUT(p,f,a,b,c,d,e) dmxLogInput(p->dmxInput,f,a,b,c,d,e) -#define FATAL0(f) dmxLog(dmxFatal,f) -#define FATAL1(f,a) dmxLog(dmxFatal,f,a) -#define FATAL2(f,a,b) dmxLog(dmxFatal,f,a,b) -#define MOTIONPROC dmxMotionProcPtr -#define ENQUEUEPROC dmxEnqueueProcPtr -#define CHECKPROC dmxCheckSpecialProcPtr -#define BLOCK DMXBlockType - -/* End of interface definitions. */ -/*****************************************************************************/ - - -/** Read an event from the \a pDev device. If the event is a motion - * event, enqueue it with the \a motion function. Otherwise, enqueue - * the event with the \a enqueue function. The \a block type is passed - * to the functions so that they may block SIGIO handling as appropriate - * to the caller of this function. - * - * Since USB devices return EV_KEY events for buttons and keys, \a - * minButton is used to decide if a Button or Key event should be - * queued.*/ -void usbRead(DevicePtr pDev, - MOTIONPROC motion, - ENQUEUEPROC enqueue, - int minButton, - BLOCK block) -{ - GETPRIV; - struct input_event raw; - int v[DMX_MAX_AXES]; - int axis; - -#define PRESS(b) \ - do { \ - enqueue(pDev, ButtonPress, 0, 0, NULL, block); \ - } while (0) - -#define RELEASE(b) \ - do { \ - enqueue(pDev, ButtonRelease, 0, 0, NULL, block); \ - } while (0) - - while (read(priv->fd, &raw, sizeof(raw)) > 0) { -#if USB_COMMON_DEBUG > 1 - LOG3("USB: type = %d, code = 0x%02x, value = %d\n", - raw.type, raw.code, raw.value); -#endif - switch (raw.type) { - case EV_KEY: - /* raw.value = 1 for first, 2 for repeat */ - if (raw.code > minButton) { - if (raw.value) PRESS((raw.code & 0x0f) + 1); - else RELEASE((raw.code & 0x0f) + 1); - } else { - enqueue(pDev, raw.value ? KeyPress : KeyRelease, - 0, 0, NULL, block); - } - break; - case EV_REL: - switch (raw.code) { - case REL_X: - v[0] = -raw.value; - v[1] = 0; - motion(pDev, v, 0, 2, DMX_RELATIVE, block); - break; - case REL_Y: - v[0] = 0; - v[1] = -raw.value; - motion(pDev, v, 0, 2, DMX_RELATIVE, block); - break; - case REL_WHEEL: - if ((int)raw.value > 0) { - PRESS(4); - RELEASE(4); - } else if ((int)raw.value < 0) { - PRESS(5); - RELEASE(5); - } - break; - default: - memset(v, 0, sizeof(v)); - axis = priv->relmap[raw.code]; - v[axis] = raw.value; - motion(pDev, v, axis, 1, DMX_RELATIVE, block); - } - break; - case EV_ABS: - memset(v, 0, sizeof(v)); - axis = priv->absmap[raw.code]; - v[axis] = raw.value; - motion(pDev, v, axis, 1, DMX_ABSOLUTE, block); - break; - } - } -} - -#define test_bit(bit) (priv->mask[(bit)/8] & (1 << ((bit)%8))) -#define test_bits(bit) (bits[(bit)/8] & (1 << ((bit)%8))) - -static void usbPrint(myPrivate *priv, - const char *filename, const char *devname, int fd) -{ - int j, k; - DeviceIntPtr pDevice = priv->pDevice; - unsigned char bits[KEY_MAX/8 + 1]; /* RATS: Use ok assuming that - * KEY_MAX is greater than - * REL_MAX, ABS_MAX, SND_MAX, and - * LED_MAX. */ - - LOG3INPUT(priv, "%s (%s) using %s\n", pDevice->name, GETNAME, filename); - LOG1INPUT(priv, " %s\n", devname); - for (j = 0; j < EV_MAX; j++) { - if (test_bit(j)) { - const char *type = "unknown"; - char extra[256]; /* FIXME: may cause buffer overflow */ - extra[0] = '\0'; - switch(j) { - case EV_KEY: type = "keys/buttons"; break; - case EV_REL: type = "relative"; - memset(bits, 0, sizeof(bits)); - ioctl(priv->fd, EVIOCGBIT(EV_REL, sizeof(bits)), bits); - for (k = 0; k < REL_MAX; k++) { - if (test_bits(k)) switch (k) { - case REL_X: strcat(extra, " X"); break; - case REL_Y: strcat(extra, " Y"); break; - case REL_Z: strcat(extra, " Z"); break; - case REL_HWHEEL: strcat(extra, " HWheel"); break; - case REL_DIAL: strcat(extra, " Dial"); break; - case REL_WHEEL: strcat(extra, " Wheel"); break; - case REL_MISC: strcat(extra, " Misc"); break; - } - } - break; - case EV_ABS: type = "absolute"; - memset(bits, 0, sizeof(bits)); - ioctl(priv->fd, EVIOCGBIT(EV_ABS, sizeof(bits)), bits); - for (k = 0; k < ABS_MAX; k++) { - if (test_bits(k)) switch (k) { - case ABS_X: strcat(extra," X"); break; - case ABS_Y: strcat(extra," Y"); break; - case ABS_Z: strcat(extra," Z"); break; - case ABS_RX: strcat(extra," RX"); break; - case ABS_RY: strcat(extra," RY"); break; - case ABS_RZ: strcat(extra," RZ"); break; - case ABS_THROTTLE: strcat(extra," Throttle");break; - case ABS_RUDDER: strcat(extra," Rudder"); break; - case ABS_WHEEL: strcat(extra," Wheel"); break; - case ABS_GAS: strcat(extra," Gas"); break; - case ABS_BRAKE: strcat(extra," Break"); break; - case ABS_HAT0X: strcat(extra," Hat0X"); break; - case ABS_HAT0Y: strcat(extra," Hat0Y"); break; - case ABS_HAT1X: strcat(extra," Hat1X"); break; - case ABS_HAT1Y: strcat(extra," Hat1Y"); break; - case ABS_HAT2X: strcat(extra," Hat2X"); break; - case ABS_HAT2Y: strcat(extra," Hat2Y"); break; - case ABS_HAT3X: strcat(extra," Hat3X"); break; - case ABS_HAT3Y: strcat(extra," Hat3Y"); break; - case ABS_PRESSURE: strcat(extra," Pressure");break; - case ABS_DISTANCE: strcat(extra," Distance");break; - case ABS_TILT_X: strcat(extra," TiltX"); break; - case ABS_TILT_Y: strcat(extra," TiltY"); break; - case ABS_MISC: strcat(extra," Misc"); break; - } - } - break; - case EV_MSC: type = "reserved"; break; - case EV_LED: type = "leds"; - memset(bits, 0, sizeof(bits)); - ioctl(priv->fd, EVIOCGBIT(EV_LED, sizeof(bits)), bits); - for (k = 0; k < LED_MAX; k++) { - if (test_bits(k)) switch (k) { - case LED_NUML: strcat(extra," NumLock"); break; - case LED_CAPSL: strcat(extra," CapsLock"); break; - case LED_SCROLLL: strcat(extra," ScrlLock"); break; - case LED_COMPOSE: strcat(extra," Compose"); break; - case LED_KANA: strcat(extra," Kana"); break; - case LED_SLEEP: strcat(extra," Sleep"); break; - case LED_SUSPEND: strcat(extra," Suspend"); break; - case LED_MUTE: strcat(extra," Mute"); break; - case LED_MISC: strcat(extra," Misc"); break; - } - } - break; - case EV_SND: type = "sound"; - memset(bits, 0, sizeof(bits)); - ioctl(priv->fd, EVIOCGBIT(EV_SND, sizeof(bits)), bits); - for (k = 0; k < SND_MAX; k++) { - if (test_bits(k)) switch (k) { - case SND_CLICK: strcat(extra," Click"); break; - case SND_BELL: strcat(extra," Bell"); break; - } - } - break; - case EV_REP: type = "repeat"; break; - case EV_FF: type = "feedback"; break; - } - LOG5INPUT(priv, " Feature 0x%02x = %s%s%s%s\n", j, type, - extra[0] ? " [" : "", - extra[0] ? extra+1 : "", - extra[0] ? "]" : ""); - } - } -} - -/** Initialized \a pDev as a \a usbMouse, \a usbKeyboard, or \a usbOther -device. */ -void usbInit(DevicePtr pDev, usbType type) -{ - GETPRIV; - char name[64]; /* RATS: Only used in XmuSnprintf */ - int i, j, k; - char buf[256] = { 0, }; /* RATS: Use ok */ - int version; - unsigned char bits[KEY_MAX/8 + 1]; /* RATS: Use ok assuming that - * KEY_MAX is greater than - * REL_MAX, ABS_MAX, SND_MAX, and - * LED_MAX. */ - - if (priv->fd >=0) return; - - for (i = 0; i < 32; i++) { - XmuSnprintf(name, sizeof(name), "/dev/input/event%d", i); - if ((priv->fd = open(name, O_RDWR | O_NONBLOCK, 0)) >= 0) { - ioctl(priv->fd, EVIOCGVERSION, &version); - ioctl(priv->fd, EVIOCGNAME(sizeof(buf)), buf); - memset(priv->mask, 0, sizeof(priv->mask)); - ioctl(priv->fd, EVIOCGBIT(0, sizeof(priv->mask)), priv->mask); - - for (j = 0; j < EV_MAX; j++) { - if (test_bit(j)) { - switch(j) { - case EV_REL: - memset(bits, 0, sizeof(bits)); - ioctl(priv->fd, EVIOCGBIT(EV_REL, sizeof(bits)), bits); - for (k = 0; k < REL_MAX; k++) { - if (test_bits(k)) { - if (k == REL_X) priv->relmap[k] = 0; - else if (k == REL_Y) priv->relmap[k] = 1; - else priv->relmap[k] = 2 + priv->numAbs; - ++priv->numRel; - } - } - break; - case EV_ABS: - memset(bits, 0, sizeof(bits)); - ioctl(priv->fd, EVIOCGBIT(EV_ABS, sizeof(bits)), bits); - for (k = 0; k < ABS_MAX; k++) { - if (test_bits(k)) { - priv->absmap[k] = priv->numAbs; - ++priv->numAbs; - } - } - break; - case EV_LED: - memset(bits, 0, sizeof(bits)); - ioctl(priv->fd, EVIOCGBIT(EV_LED, sizeof(bits)), bits); - for (k = 0; k < LED_MAX; k++) { - if (test_bits(k)) ++priv->numLeds; - } - break; - } - } - } - switch (type) { - case usbMouse: - if (test_bit(EV_REL) && test_bit(EV_KEY)) - goto found; - break; - case usbKeyboard: - if (test_bit(EV_KEY) && test_bit(EV_LED) && !test_bit(EV_ABS)) - goto found; - break; - case usbOther: - if (!(test_bit(EV_REL) && test_bit(EV_KEY)) - && !(test_bit(EV_KEY) && test_bit(EV_LED) - && !test_bit(EV_ABS))) - goto found; - break; - } - close(priv->fd); - priv->fd = -1; - } - } - if (priv->fd < 0) - FATAL1("usbInit: Cannot open /dev/input/event* port (%s)\n" - " If you have not done so, you may need to:\n" - " rmmod mousedev; rmmod keybdev\n" - " modprobe evdev\n", - strerror(errno)); - found: - usbPrint(priv, name, buf, priv->fd); -} - -/** Turn \a pDev off (i.e., stop taking input from \a pDev). */ -void usbOff(DevicePtr pDev) -{ - GETPRIV; - - if (priv->fd >= 0) close(priv->fd); - priv->fd = -1; -} - -/** Create a private structure for use within this file. */ -pointer usbCreatePrivate(DeviceIntPtr pDevice) -{ - myPrivate *priv = calloc(1, sizeof(*priv)); - priv->fd = -1; - priv->pDevice = pDevice; - return priv; -} - -/** Destroy a private structure. */ -void usbDestroyPrivate(pointer priv) -{ - free(priv); -} +/* + * Copyright 2002-2003 Red Hat Inc., Durham, North Carolina. + * + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation on the rights to use, copy, modify, merge, + * publish, distribute, sublicense, and/or sell copies of the Software, + * and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NON-INFRINGEMENT. IN NO EVENT SHALL RED HAT AND/OR THEIR SUPPLIERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* + * Authors: + * Rickard E. (Rik) Faith + * + */ + +/** \file + * + * Routines that are common between \a usb-keyboard.c, \a usb-mouse.c, and + * \a usb-other.c */ + +#ifdef HAVE_DMX_CONFIG_H +#include +#endif + +#include "usb-private.h" + +#define USB_COMMON_DEBUG 1 + +/*****************************************************************************/ +/* Define some macros to make it easier to move this file to another + * part of the Xserver tree. All calls to the dmx* layer are #defined + * here for the .c file. The .h file will also have to be edited. */ +#include "usb-mouse.h" + +#define GETPRIV myPrivate *priv \ + = ((DMXLocalInputInfoPtr)(pDev->devicePrivate))->private + +#define GETNAME ((DMXLocalInputInfoPtr)(pDevice->public.devicePrivate)) \ + ->name + +#define LOG0(f) dmxLog(dmxDebug,f) +#define LOG1(f,a) dmxLog(dmxDebug,f,a) +#define LOG2(f,a,b) dmxLog(dmxDebug,f,a,b) +#define LOG3(f,a,b,c) dmxLog(dmxDebug,f,a,b,c) +#define LOG1INPUT(p,f,a) dmxLogInput(p->dmxInput,f,a) +#define LOG3INPUT(p,f,a,b,c) dmxLogInput(p->dmxInput,f,a,b,c) +#define LOG5INPUT(p,f,a,b,c,d,e) dmxLogInput(p->dmxInput,f,a,b,c,d,e) +#define FATAL0(f) dmxLog(dmxFatal,f) +#define FATAL1(f,a) dmxLog(dmxFatal,f,a) +#define FATAL2(f,a,b) dmxLog(dmxFatal,f,a,b) +#define MOTIONPROC dmxMotionProcPtr +#define ENQUEUEPROC dmxEnqueueProcPtr +#define CHECKPROC dmxCheckSpecialProcPtr +#define BLOCK DMXBlockType + +/* End of interface definitions. */ +/*****************************************************************************/ + + +/** Read an event from the \a pDev device. If the event is a motion + * event, enqueue it with the \a motion function. Otherwise, enqueue + * the event with the \a enqueue function. The \a block type is passed + * to the functions so that they may block SIGIO handling as appropriate + * to the caller of this function. + * + * Since USB devices return EV_KEY events for buttons and keys, \a + * minButton is used to decide if a Button or Key event should be + * queued.*/ +void usbRead(DevicePtr pDev, + MOTIONPROC motion, + ENQUEUEPROC enqueue, + int minButton, + BLOCK block) +{ + GETPRIV; + struct input_event raw; + int v[DMX_MAX_AXES]; + int axis; + +#define PRESS(b) \ + do { \ + enqueue(pDev, ButtonPress, 0, 0, NULL, block); \ + } while (0) + +#define RELEASE(b) \ + do { \ + enqueue(pDev, ButtonRelease, 0, 0, NULL, block); \ + } while (0) + + while (read(priv->fd, &raw, sizeof(raw)) > 0) { +#if USB_COMMON_DEBUG > 1 + LOG3("USB: type = %d, code = 0x%02x, value = %d\n", + raw.type, raw.code, raw.value); +#endif + switch (raw.type) { + case EV_KEY: + /* raw.value = 1 for first, 2 for repeat */ + if (raw.code > minButton) { + if (raw.value) PRESS((raw.code & 0x0f) + 1); + else RELEASE((raw.code & 0x0f) + 1); + } else { + enqueue(pDev, raw.value ? KeyPress : KeyRelease, + 0, 0, NULL, block); + } + break; + case EV_REL: + switch (raw.code) { + case REL_X: + v[0] = -raw.value; + v[1] = 0; + motion(pDev, v, 0, 2, DMX_RELATIVE, block); + break; + case REL_Y: + v[0] = 0; + v[1] = -raw.value; + motion(pDev, v, 0, 2, DMX_RELATIVE, block); + break; + case REL_WHEEL: + if ((int)raw.value > 0) { + PRESS(4); + RELEASE(4); + } else if ((int)raw.value < 0) { + PRESS(5); + RELEASE(5); + } + break; + default: + memset(v, 0, sizeof(v)); + axis = priv->relmap[raw.code]; + v[axis] = raw.value; + motion(pDev, v, axis, 1, DMX_RELATIVE, block); + } + break; + case EV_ABS: + memset(v, 0, sizeof(v)); + axis = priv->absmap[raw.code]; + v[axis] = raw.value; + motion(pDev, v, axis, 1, DMX_ABSOLUTE, block); + break; + } + } +} + +#define test_bit(bit) (priv->mask[(bit)/8] & (1 << ((bit)%8))) +#define test_bits(bit) (bits[(bit)/8] & (1 << ((bit)%8))) + +static void usbPrint(myPrivate *priv, + const char *filename, const char *devname, int fd) +{ + int j, k; + DeviceIntPtr pDevice = priv->pDevice; + unsigned char bits[KEY_MAX/8 + 1]; /* RATS: Use ok assuming that + * KEY_MAX is greater than + * REL_MAX, ABS_MAX, SND_MAX, and + * LED_MAX. */ + + LOG3INPUT(priv, "%s (%s) using %s\n", pDevice->name, GETNAME, filename); + LOG1INPUT(priv, " %s\n", devname); + for (j = 0; j < EV_MAX; j++) { + if (test_bit(j)) { + const char *type = "unknown"; + char extra[256]; /* FIXME: may cause buffer overflow */ + extra[0] = '\0'; + switch(j) { + case EV_KEY: type = "keys/buttons"; break; + case EV_REL: type = "relative"; + memset(bits, 0, sizeof(bits)); + ioctl(priv->fd, EVIOCGBIT(EV_REL, sizeof(bits)), bits); + for (k = 0; k < REL_MAX; k++) { + if (test_bits(k)) switch (k) { + case REL_X: strcat(extra, " X"); break; + case REL_Y: strcat(extra, " Y"); break; + case REL_Z: strcat(extra, " Z"); break; + case REL_HWHEEL: strcat(extra, " HWheel"); break; + case REL_DIAL: strcat(extra, " Dial"); break; + case REL_WHEEL: strcat(extra, " Wheel"); break; + case REL_MISC: strcat(extra, " Misc"); break; + } + } + break; + case EV_ABS: type = "absolute"; + memset(bits, 0, sizeof(bits)); + ioctl(priv->fd, EVIOCGBIT(EV_ABS, sizeof(bits)), bits); + for (k = 0; k < ABS_MAX; k++) { + if (test_bits(k)) switch (k) { + case ABS_X: strcat(extra," X"); break; + case ABS_Y: strcat(extra," Y"); break; + case ABS_Z: strcat(extra," Z"); break; + case ABS_RX: strcat(extra," RX"); break; + case ABS_RY: strcat(extra," RY"); break; + case ABS_RZ: strcat(extra," RZ"); break; + case ABS_THROTTLE: strcat(extra," Throttle");break; + case ABS_RUDDER: strcat(extra," Rudder"); break; + case ABS_WHEEL: strcat(extra," Wheel"); break; + case ABS_GAS: strcat(extra," Gas"); break; + case ABS_BRAKE: strcat(extra," Break"); break; + case ABS_HAT0X: strcat(extra," Hat0X"); break; + case ABS_HAT0Y: strcat(extra," Hat0Y"); break; + case ABS_HAT1X: strcat(extra," Hat1X"); break; + case ABS_HAT1Y: strcat(extra," Hat1Y"); break; + case ABS_HAT2X: strcat(extra," Hat2X"); break; + case ABS_HAT2Y: strcat(extra," Hat2Y"); break; + case ABS_HAT3X: strcat(extra," Hat3X"); break; + case ABS_HAT3Y: strcat(extra," Hat3Y"); break; + case ABS_PRESSURE: strcat(extra," Pressure");break; + case ABS_DISTANCE: strcat(extra," Distance");break; + case ABS_TILT_X: strcat(extra," TiltX"); break; + case ABS_TILT_Y: strcat(extra," TiltY"); break; + case ABS_MISC: strcat(extra," Misc"); break; + } + } + break; + case EV_MSC: type = "reserved"; break; + case EV_LED: type = "leds"; + memset(bits, 0, sizeof(bits)); + ioctl(priv->fd, EVIOCGBIT(EV_LED, sizeof(bits)), bits); + for (k = 0; k < LED_MAX; k++) { + if (test_bits(k)) switch (k) { + case LED_NUML: strcat(extra," NumLock"); break; + case LED_CAPSL: strcat(extra," CapsLock"); break; + case LED_SCROLLL: strcat(extra," ScrlLock"); break; + case LED_COMPOSE: strcat(extra," Compose"); break; + case LED_KANA: strcat(extra," Kana"); break; + case LED_SLEEP: strcat(extra," Sleep"); break; + case LED_SUSPEND: strcat(extra," Suspend"); break; + case LED_MUTE: strcat(extra," Mute"); break; + case LED_MISC: strcat(extra," Misc"); break; + } + } + break; + case EV_SND: type = "sound"; + memset(bits, 0, sizeof(bits)); + ioctl(priv->fd, EVIOCGBIT(EV_SND, sizeof(bits)), bits); + for (k = 0; k < SND_MAX; k++) { + if (test_bits(k)) switch (k) { + case SND_CLICK: strcat(extra," Click"); break; + case SND_BELL: strcat(extra," Bell"); break; + } + } + break; + case EV_REP: type = "repeat"; break; + case EV_FF: type = "feedback"; break; + } + LOG5INPUT(priv, " Feature 0x%02x = %s%s%s%s\n", j, type, + extra[0] ? " [" : "", + extra[0] ? extra+1 : "", + extra[0] ? "]" : ""); + } + } +} + +/** Initialized \a pDev as a \a usbMouse, \a usbKeyboard, or \a usbOther +device. */ +void usbInit(DevicePtr pDev, usbType type) +{ + GETPRIV; + char name[64]; /* RATS: Only used in snprintf */ + int i, j, k; + char buf[256] = { 0, }; /* RATS: Use ok */ + int version; + unsigned char bits[KEY_MAX/8 + 1]; /* RATS: Use ok assuming that + * KEY_MAX is greater than + * REL_MAX, ABS_MAX, SND_MAX, and + * LED_MAX. */ + + if (priv->fd >=0) return; + + for (i = 0; i < 32; i++) { + snprintf(name, sizeof(name), "/dev/input/event%d", i); + if ((priv->fd = open(name, O_RDWR | O_NONBLOCK, 0)) >= 0) { + ioctl(priv->fd, EVIOCGVERSION, &version); + ioctl(priv->fd, EVIOCGNAME(sizeof(buf)), buf); + memset(priv->mask, 0, sizeof(priv->mask)); + ioctl(priv->fd, EVIOCGBIT(0, sizeof(priv->mask)), priv->mask); + + for (j = 0; j < EV_MAX; j++) { + if (test_bit(j)) { + switch(j) { + case EV_REL: + memset(bits, 0, sizeof(bits)); + ioctl(priv->fd, EVIOCGBIT(EV_REL, sizeof(bits)), bits); + for (k = 0; k < REL_MAX; k++) { + if (test_bits(k)) { + if (k == REL_X) priv->relmap[k] = 0; + else if (k == REL_Y) priv->relmap[k] = 1; + else priv->relmap[k] = 2 + priv->numAbs; + ++priv->numRel; + } + } + break; + case EV_ABS: + memset(bits, 0, sizeof(bits)); + ioctl(priv->fd, EVIOCGBIT(EV_ABS, sizeof(bits)), bits); + for (k = 0; k < ABS_MAX; k++) { + if (test_bits(k)) { + priv->absmap[k] = priv->numAbs; + ++priv->numAbs; + } + } + break; + case EV_LED: + memset(bits, 0, sizeof(bits)); + ioctl(priv->fd, EVIOCGBIT(EV_LED, sizeof(bits)), bits); + for (k = 0; k < LED_MAX; k++) { + if (test_bits(k)) ++priv->numLeds; + } + break; + } + } + } + switch (type) { + case usbMouse: + if (test_bit(EV_REL) && test_bit(EV_KEY)) + goto found; + break; + case usbKeyboard: + if (test_bit(EV_KEY) && test_bit(EV_LED) && !test_bit(EV_ABS)) + goto found; + break; + case usbOther: + if (!(test_bit(EV_REL) && test_bit(EV_KEY)) + && !(test_bit(EV_KEY) && test_bit(EV_LED) + && !test_bit(EV_ABS))) + goto found; + break; + } + close(priv->fd); + priv->fd = -1; + } + } + if (priv->fd < 0) + FATAL1("usbInit: Cannot open /dev/input/event* port (%s)\n" + " If you have not done so, you may need to:\n" + " rmmod mousedev; rmmod keybdev\n" + " modprobe evdev\n", + strerror(errno)); + found: + usbPrint(priv, name, buf, priv->fd); +} + +/** Turn \a pDev off (i.e., stop taking input from \a pDev). */ +void usbOff(DevicePtr pDev) +{ + GETPRIV; + + if (priv->fd >= 0) close(priv->fd); + priv->fd = -1; +} + +/** Create a private structure for use within this file. */ +pointer usbCreatePrivate(DeviceIntPtr pDevice) +{ + myPrivate *priv = calloc(1, sizeof(*priv)); + priv->fd = -1; + priv->pDevice = pDevice; + return priv; +} + +/** Destroy a private structure. */ +void usbDestroyPrivate(pointer priv) +{ + free(priv); +} -- cgit v1.2.3