/*
 * Copyright 2001-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:
 *   Kevin E. Martin <kem@redhat.com>
 *   David H. Dawes <dawes@xfree86.org>
 *   Rickard E. (Rik) Faith <faith@redhat.com>
 *
 */

/** \file
 * Provide expected functions for initialization from the ddx layer and
 * global variables for the DMX server. */

#ifdef HAVE_DMX_CONFIG_H
#include <dmx-config.h>
#endif

#include "dmx.h"
#include "dmxinit.h"
#include "dmxsync.h"
#include "dmxlog.h"
#include "dmxinput.h"
#include "dmxscrinit.h"
#include "dmxcursor.h"
#include "dmxfont.h"
#include "config/dmxconfig.h"
#include "dmxcb.h"
#include "dmxprop.h"
#include "dmxstat.h"
#include "dmxpict.h"

#include <X11/Xos.h>            /* For gettimeofday */
#include <X11/Xmu/SysUtil.h>    /* For XmuGetHostname */
#include "dixstruct.h"
#ifdef PANORAMIX
#include "panoramiXsrv.h"
#endif

#include <signal.h>             /* For SIGQUIT */

#ifdef GLXEXT
#include <GL/glx.h>
#include <GL/glxint.h>
#include "dmx_glxvisuals.h"
#include "glx_extinit.h"
#include <X11/extensions/Xext.h>
#include <X11/extensions/extutil.h>
#endif                          /* GLXEXT */

#include <X11/extensions/dmxproto.h>

/* Global variables available to all Xserver/hw/dmx routines. */
int dmxNumScreens;
DMXScreenInfo *dmxScreens;

int dmxNumInputs;
DMXInputInfo *dmxInputs;

XErrorEvent dmxLastErrorEvent;
Bool dmxErrorOccurred = FALSE;

char *dmxFontPath = NULL;

Bool dmxOffScreenOpt = TRUE;

Bool dmxSubdividePrimitives = TRUE;

Bool dmxLazyWindowCreation = TRUE;

Bool dmxUseXKB = TRUE;

int dmxDepth = 0;

#ifndef GLXEXT
static Bool dmxGLXProxy = FALSE;
#else
Bool dmxGLXProxy = TRUE;

Bool dmxGLXSwapGroupSupport = TRUE;

Bool dmxGLXSyncSwap = FALSE;

Bool dmxGLXFinishSwap = FALSE;
#endif

RESTYPE RRProviderType = 0;

Bool dmxIgnoreBadFontPaths = FALSE;

Bool dmxAddRemoveScreens = FALSE;

/* dmxErrorHandler catches errors that occur when calling one of the
 * back-end servers.  Some of this code is based on _XPrintDefaultError
 * in xc/lib/X11/XlibInt.c */
static int
dmxErrorHandler(Display * dpy, XErrorEvent * ev)
{
#define DMX_ERROR_BUF_SIZE 256
    /* RATS: these buffers are only used in
     * length-limited calls. */
    char buf[DMX_ERROR_BUF_SIZE];
    char request[DMX_ERROR_BUF_SIZE];
    _XExtension *ext = NULL;

    dmxErrorOccurred = TRUE;
    dmxLastErrorEvent = *ev;

    XGetErrorText(dpy, ev->error_code, buf, sizeof(buf));
    dmxLog(dmxWarning, "dmxErrorHandler: %s\n", buf);

    /* Find major opcode name */
    if (ev->request_code < 128) {
        snprintf(request, sizeof(request), "%d", ev->request_code);
        XGetErrorDatabaseText(dpy, "XRequest", request, "", buf, sizeof(buf));
    }
    else {
        for (ext = dpy->ext_procs;
             ext && ext->codes.major_opcode != ev->request_code;
             ext = ext->next);
        if (ext)
            strlcpy(buf, ext->name, sizeof(buf));
        else
            buf[0] = '\0';
    }
    dmxLog(dmxWarning, "                 Major opcode: %d (%s)\n",
           ev->request_code, buf);

    /* Find minor opcode name */
    if (ev->request_code >= 128 && ext) {
        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",
               ev->minor_code, buf);
    }

    /* Provide value information */
    switch (ev->error_code) {
    case BadValue:
        dmxLog(dmxWarning, "                 Value:        0x%x\n",
               (unsigned int) ev->resourceid);
        break;
    case BadAtom:
        dmxLog(dmxWarning, "                 AtomID:       0x%x\n",
               (unsigned int) ev->resourceid);
        break;
    default:
        dmxLog(dmxWarning, "                 ResourceID:   0x%x\n",
               (unsigned int) ev->resourceid);
        break;
    }

    /* Provide serial number information */
    dmxLog(dmxWarning, "                 Failed serial number:  %d\n",
           (unsigned int) ev->serial);
    dmxLog(dmxWarning, "                 Current serial number: %d\n",
           (unsigned int) dpy->request);
    return 0;
}

#ifdef GLXEXT
static int
dmxNOPErrorHandler(Display * dpy, XErrorEvent * ev)
{
    return 0;
}
#endif

Bool
dmxOpenDisplay(DMXScreenInfo * dmxScreen)
{
    if (!(dmxScreen->beDisplay = XOpenDisplay(dmxScreen->name)))
        return FALSE;

    dmxPropertyDisplay(dmxScreen);
    return TRUE;
}

void
dmxSetErrorHandler(DMXScreenInfo * dmxScreen)
{
    XSetErrorHandler(dmxErrorHandler);
}

static void
dmxPrintScreenInfo(DMXScreenInfo * dmxScreen)
{
    XWindowAttributes attribs;
    int ndepths = 0, *depths = NULL;
    int i;
    Display *dpy = dmxScreen->beDisplay;
    Screen *s = DefaultScreenOfDisplay(dpy);
    int scr = DefaultScreen(dpy);

    XGetWindowAttributes(dpy, DefaultRootWindow(dpy), &attribs);
    if (!(depths = XListDepths(dpy, scr, &ndepths)))
        ndepths = 0;

    dmxLogOutput(dmxScreen, "Name of display: %s\n", DisplayString(dpy));
    dmxLogOutput(dmxScreen, "Version number:  %d.%d\n",
                 ProtocolVersion(dpy), ProtocolRevision(dpy));
    dmxLogOutput(dmxScreen, "Vendor string:   %s\n", ServerVendor(dpy));
    if (!strstr(ServerVendor(dpy), "XFree86")) {
        dmxLogOutput(dmxScreen, "Vendor release:  %d\n", VendorRelease(dpy));
    }
    else {
        /* This code based on xdpyinfo.c */
        int v = VendorRelease(dpy);
        int major = -1, minor = -1, patch = -1, subpatch = -1;

        if (v < 336)
            major = v / 100, minor = (v / 10) % 10, patch = v % 10;
        else if (v < 3900) {
            major = v / 1000;
            minor = (v / 100) % 10;
            if (((v / 10) % 10) || (v % 10)) {
                patch = (v / 10) % 10;
                if (v % 10)
                    subpatch = v % 10;
            }
        }
        else if (v < 40000000) {
            major = v / 1000;
            minor = (v / 10) % 10;
            if (v % 10)
                patch = v % 10;
        }
        else {
            major = v / 10000000;
            minor = (v / 100000) % 100;
            patch = (v / 1000) % 100;
            if (v % 1000)
                subpatch = v % 1000;
        }
        dmxLogOutput(dmxScreen, "Vendor release:  %d (XFree86 version: %d.%d",
                     v, major, minor);
        if (patch > 0)
            dmxLogOutputCont(dmxScreen, ".%d", patch);
        if (subpatch > 0)
            dmxLogOutputCont(dmxScreen, ".%d", subpatch);
        dmxLogOutputCont(dmxScreen, ")\n");
    }

    dmxLogOutput(dmxScreen, "Dimensions:      %dx%d pixels\n",
                 attribs.width, attribs.height);
    dmxLogOutput(dmxScreen, "%d depths on screen %d: ", ndepths, scr);
    for (i = 0; i < ndepths; i++)
        dmxLogOutputCont(dmxScreen, "%c%d", i ? ',' : ' ', depths[i]);
    dmxLogOutputCont(dmxScreen, "\n");
    dmxLogOutput(dmxScreen, "Depth of root window:  %d plane%s (%d)\n",
                 attribs.depth, attribs.depth == 1 ? "" : "s",
                 DisplayPlanes(dpy, scr));
    dmxLogOutput(dmxScreen, "Number of colormaps:   %d min, %d max\n",
                 MinCmapsOfScreen(s), MaxCmapsOfScreen(s));
    dmxLogOutput(dmxScreen, "Options: backing-store %s, save-unders %s\n",
                 (DoesBackingStore(s) == NotUseful) ? "no" :
                 ((DoesBackingStore(s) == Always) ? "yes" : "when mapped"),
                 DoesSaveUnders(s) ? "yes" : "no");
    dmxLogOutput(dmxScreen, "Window Manager running: %s\n",
                 (dmxScreen->WMRunningOnBE) ? "yes" : "no");

    if (dmxScreen->WMRunningOnBE) {
        dmxLogOutputWarning(dmxScreen,
                            "Window manager running "
                            "-- colormaps not supported\n");
    }
    XFree(depths);
}

void
dmxGetScreenAttribs(DMXScreenInfo * dmxScreen)
{
    XWindowAttributes attribs;
    Display *dpy = dmxScreen->beDisplay;

#ifdef GLXEXT
    int dummy;
#endif

    XGetWindowAttributes(dpy, DefaultRootWindow(dpy), &attribs);

    dmxScreen->beWidth = attribs.width;
    dmxScreen->beHeight = attribs.height;

    /* Fill in missing geometry information */
    if (dmxScreen->scrnXSign < 0) {
        if (dmxScreen->scrnWidth) {
            dmxScreen->scrnX = (attribs.width - dmxScreen->scrnWidth
                                - dmxScreen->scrnX);
        }
        else {
            dmxScreen->scrnWidth = attribs.width - dmxScreen->scrnX;
            dmxScreen->scrnX = 0;
        }
    }
    if (dmxScreen->scrnYSign < 0) {
        if (dmxScreen->scrnHeight) {
            dmxScreen->scrnY = (attribs.height - dmxScreen->scrnHeight
                                - dmxScreen->scrnY);
        }
        else {
            dmxScreen->scrnHeight = attribs.height - dmxScreen->scrnY;
            dmxScreen->scrnY = 0;
        }
    }
    if (!dmxScreen->scrnWidth)
        dmxScreen->scrnWidth = attribs.width - dmxScreen->scrnX;
    if (!dmxScreen->scrnHeight)
        dmxScreen->scrnHeight = attribs.height - dmxScreen->scrnY;

    if (!dmxScreen->rootWidth)
        dmxScreen->rootWidth = dmxScreen->scrnWidth;
    if (!dmxScreen->rootHeight)
        dmxScreen->rootHeight = dmxScreen->scrnHeight;
    if (dmxScreen->rootWidth + dmxScreen->rootX > dmxScreen->scrnWidth)
        dmxScreen->rootWidth = dmxScreen->scrnWidth - dmxScreen->rootX;
    if (dmxScreen->rootHeight + dmxScreen->rootY > dmxScreen->scrnHeight)
        dmxScreen->rootHeight = dmxScreen->scrnHeight - dmxScreen->rootY;

    /* FIXME: Get these from the back-end server */
    dmxScreen->beXDPI = 75;
    dmxScreen->beYDPI = 75;

    dmxScreen->beDepth = attribs.depth; /* FIXME: verify that this
                                         * works always.  In
                                         * particular, this will work
                                         * well for depth=16, will fail
                                         * because of colormap issues
                                         * at depth 8.  More work needs
                                         * to be done here. */

    if (dmxScreen->beDepth <= 8)
        dmxScreen->beBPP = 8;
    else if (dmxScreen->beDepth <= 16)
        dmxScreen->beBPP = 16;
    else
        dmxScreen->beBPP = 32;

#ifdef GLXEXT
    /* get the majorOpcode for the back-end GLX extension */
    XQueryExtension(dpy, "GLX", &dmxScreen->glxMajorOpcode,
                    &dummy, &dmxScreen->glxErrorBase);
#endif

    dmxPrintScreenInfo(dmxScreen);
    dmxLogOutput(dmxScreen, "%dx%d+%d+%d on %dx%d at depth=%d, bpp=%d\n",
                 dmxScreen->scrnWidth, dmxScreen->scrnHeight,
                 dmxScreen->scrnX, dmxScreen->scrnY,
                 dmxScreen->beWidth, dmxScreen->beHeight,
                 dmxScreen->beDepth, dmxScreen->beBPP);
    if (dmxScreen->beDepth == 8)
        dmxLogOutputWarning(dmxScreen,
                            "Support for depth == 8 is not complete\n");
}

Bool
dmxGetVisualInfo(DMXScreenInfo * dmxScreen)
{
    int i;
    XVisualInfo visinfo;

    visinfo.screen = DefaultScreen(dmxScreen->beDisplay);
    dmxScreen->beVisuals = XGetVisualInfo(dmxScreen->beDisplay,
                                          VisualScreenMask,
                                          &visinfo, &dmxScreen->beNumVisuals);

    dmxScreen->beDefVisualIndex = -1;

    if (defaultColorVisualClass >= 0 || dmxDepth > 0) {
        for (i = 0; i < dmxScreen->beNumVisuals; i++)
            if (defaultColorVisualClass >= 0) {
                if (dmxScreen->beVisuals[i].class == defaultColorVisualClass) {
                    if (dmxDepth > 0) {
                        if (dmxScreen->beVisuals[i].depth == dmxDepth) {
                            dmxScreen->beDefVisualIndex = i;
                            break;
                        }
                    }
                    else {
                        dmxScreen->beDefVisualIndex = i;
                        break;
                    }
                }
            }
            else if (dmxScreen->beVisuals[i].depth == dmxDepth) {
                dmxScreen->beDefVisualIndex = i;
                break;
            }
    }
    else {
        visinfo.visualid =
            XVisualIDFromVisual(DefaultVisual(dmxScreen->beDisplay,
                                              visinfo.screen));

        for (i = 0; i < dmxScreen->beNumVisuals; i++)
            if (visinfo.visualid == dmxScreen->beVisuals[i].visualid) {
                dmxScreen->beDefVisualIndex = i;
                break;
            }
    }

    for (i = 0; i < dmxScreen->beNumVisuals; i++)
        dmxLogVisual(dmxScreen, &dmxScreen->beVisuals[i],
                     (i == dmxScreen->beDefVisualIndex));

    return dmxScreen->beDefVisualIndex >= 0;
}

void
dmxGetColormaps(DMXScreenInfo * dmxScreen)
{
    int i;

    dmxScreen->beNumDefColormaps = dmxScreen->beNumVisuals;
    dmxScreen->beDefColormaps = xallocarray(dmxScreen->beNumDefColormaps,
                                       sizeof(*dmxScreen->beDefColormaps));

    for (i = 0; i < dmxScreen->beNumDefColormaps; i++)
        dmxScreen->beDefColormaps[i] =
            XCreateColormap(dmxScreen->beDisplay,
                            DefaultRootWindow(dmxScreen->beDisplay),
                            dmxScreen->beVisuals[i].visual, AllocNone);

    dmxScreen->beBlackPixel = BlackPixel(dmxScreen->beDisplay,
                                         DefaultScreen(dmxScreen->beDisplay));
    dmxScreen->beWhitePixel = WhitePixel(dmxScreen->beDisplay,
                                         DefaultScreen(dmxScreen->beDisplay));
}

void
dmxGetPixmapFormats(DMXScreenInfo * dmxScreen)
{
    dmxScreen->beDepths =
        XListDepths(dmxScreen->beDisplay, DefaultScreen(dmxScreen->beDisplay),
                    &dmxScreen->beNumDepths);

    dmxScreen->bePixmapFormats =
        XListPixmapFormats(dmxScreen->beDisplay,
                           &dmxScreen->beNumPixmapFormats);
}

static Bool
dmxSetPixmapFormats(ScreenInfo * pScreenInfo, DMXScreenInfo * dmxScreen)
{
    XPixmapFormatValues *bePixmapFormat;
    PixmapFormatRec *format;
    int i, j;

    pScreenInfo->imageByteOrder = ImageByteOrder(dmxScreen->beDisplay);
    pScreenInfo->bitmapScanlineUnit = BitmapUnit(dmxScreen->beDisplay);
    pScreenInfo->bitmapScanlinePad = BitmapPad(dmxScreen->beDisplay);
    pScreenInfo->bitmapBitOrder = BitmapBitOrder(dmxScreen->beDisplay);

    pScreenInfo->numPixmapFormats = 0;
    for (i = 0; i < dmxScreen->beNumPixmapFormats; i++) {
        bePixmapFormat = &dmxScreen->bePixmapFormats[i];
        for (j = 0; j < dmxScreen->beNumDepths; j++)
            if ((bePixmapFormat->depth == 1) ||
                (bePixmapFormat->depth == dmxScreen->beDepths[j])) {
                format = &pScreenInfo->formats[pScreenInfo->numPixmapFormats];

                format->depth = bePixmapFormat->depth;
                format->bitsPerPixel = bePixmapFormat->bits_per_pixel;
                format->scanlinePad = bePixmapFormat->scanline_pad;

                pScreenInfo->numPixmapFormats++;
                break;
            }
    }

    return TRUE;
}

void
dmxCheckForWM(DMXScreenInfo * dmxScreen)
{
    Status status;
    XWindowAttributes xwa;

    status = XGetWindowAttributes(dmxScreen->beDisplay,
                                  DefaultRootWindow(dmxScreen->beDisplay),
                                  &xwa);
    dmxScreen->WMRunningOnBE =
        (status &&
         ((xwa.all_event_masks & SubstructureRedirectMask) ||
          (xwa.all_event_masks & SubstructureNotifyMask)));
}

/** Initialize the display and collect relevant information about the
 *  display properties */
static void
dmxDisplayInit(DMXScreenInfo * dmxScreen)
{
    if (!dmxOpenDisplay(dmxScreen))
        dmxLog(dmxFatal,
               "dmxOpenDisplay: Unable to open display %s\n", dmxScreen->name);

    dmxSetErrorHandler(dmxScreen);
    dmxCheckForWM(dmxScreen);
    dmxGetScreenAttribs(dmxScreen);

    if (!dmxGetVisualInfo(dmxScreen))
        dmxLog(dmxFatal, "dmxGetVisualInfo: No matching visuals found\n");

    dmxGetColormaps(dmxScreen);
    dmxGetPixmapFormats(dmxScreen);
}

static void dmxAddExtensions(void)
{
    const ExtensionModule dmxExtensions[] = {
        { DMXExtensionInit, DMX_EXTENSION_NAME, NULL },
    };

    LoadExtensionList(dmxExtensions, ARRAY_SIZE(dmxExtensions), TRUE);
}

/** This routine is called in Xserver/dix/main.c from \a main(). */
void
InitOutput(ScreenInfo * pScreenInfo, int argc, char *argv[])
{
    int i;
    static unsigned long dmxGeneration = 0;

    if (dmxGeneration != serverGeneration) {
        int vendrel = VENDOR_RELEASE;
        int major, minor, year, month, day;

        dmxGeneration = serverGeneration;

        major = vendrel / 100000000;
        vendrel -= major * 100000000;
        minor = vendrel / 1000000;
        vendrel -= minor * 1000000;
        year = vendrel / 10000;
        vendrel -= year * 10000;
        month = vendrel / 100;
        vendrel -= month * 100;
        day = vendrel;

        /* Add other epoch tests here */
        if (major > 0 && minor > 0)
            year += 2000;

        dmxLog(dmxInfo, "Generation:         %lu\n", dmxGeneration);
        dmxLog(dmxInfo, "DMX version:        %d.%d.%02d%02d%02d (%s)\n",
               major, minor, year, month, day, VENDOR_STRING);

        SetVendorRelease(VENDOR_RELEASE);
        SetVendorString(VENDOR_STRING);

        dmxLog(dmxInfo, "MAXSCREENS:         %d\n", MAXSCREENS);

        for (i = 0; i < dmxNumScreens; i++) {
            if (dmxScreens[i].beDisplay)
                dmxLog(dmxWarning, "Display \"%s\" still open\n",
                       dmxScreens[i].name);
            dmxStatFree(dmxScreens[i].stat);
            dmxScreens[i].stat = NULL;
        }
        for (i = 0; i < dmxNumInputs; i++)
            dmxInputFree(&dmxInputs[i]);
        free(dmxScreens);
        free(dmxInputs);
        dmxScreens = NULL;
        dmxInputs = NULL;
        dmxNumScreens = 0;
        dmxNumInputs = 0;
    }

    /* Make sure that the command-line arguments are sane. */
    if (dmxAddRemoveScreens && dmxGLXProxy) {
        /* Currently it is not possible to support GLX and Render
         * extensions with dynamic screen addition/removal due to the
         * state that each extension keeps, which cannot be restored. */
        dmxLog(dmxWarning,
               "GLX Proxy and Render extensions do not yet support dynamic\n");
        dmxLog(dmxWarning,
               "screen addition and removal.  Please specify -noglxproxy\n");
        dmxLog(dmxWarning,
               "and -norender on the command line or in the configuration\n");
        dmxLog(dmxWarning,
               "file to disable these two extensions if you wish to use\n");
        dmxLog(dmxWarning,
               "the dynamic addition and removal of screens support.\n");
        dmxLog(dmxFatal,
               "Dynamic screen addition/removal error (see above).\n");
    }

    /* ddxProcessArgument has been called at this point, but any data
     * from the configuration file has not been applied.  Do so, and be
     * sure we have at least one back-end display. */
    dmxConfigConfigure();
    if (!dmxNumScreens)
        dmxLog(dmxFatal, "InitOutput: no back-end displays found\n");
    if (!dmxNumInputs)
        dmxLog(dmxInfo, "InitOutput: no inputs found\n");

    /* Disable lazy window creation optimization if offscreen
     * optimization is disabled */
    if (!dmxOffScreenOpt && dmxLazyWindowCreation) {
        dmxLog(dmxInfo,
               "InitOutput: Disabling lazy window creation optimization\n");
        dmxLog(dmxInfo,
               "            since it requires the offscreen optimization\n");
        dmxLog(dmxInfo, "            to function properly.\n");
        dmxLazyWindowCreation = FALSE;
    }

    /* Open each display and gather information about it. */
    for (i = 0; i < dmxNumScreens; i++)
        dmxDisplayInit(&dmxScreens[i]);

#ifdef PANORAMIX
    /* Register a Xinerama callback which will run from within
     * PanoramiXCreateConnectionBlock.  We can use the callback to
     * determine if Xinerama is loaded and to check the visuals
     * determined by PanoramiXConsolidate. */
    XineramaRegisterConnectionBlockCallback(dmxConnectionBlockCallback);
#endif

    /* Since we only have a single screen thus far, we only need to set
       the pixmap formats to match that screen.  FIXME: this isn't true. */
    if (!dmxSetPixmapFormats(pScreenInfo, &dmxScreens[0]))
        return;

    /* Might want to install a signal handler to allow cleaning up after
     * unexpected signals.  The DIX/OS layer already handles SIGINT and
     * SIGTERM, so everything is OK for expected signals. --DD
     *
     * SIGHUP, SIGINT, and SIGTERM are trapped in os/connection.c
     * SIGQUIT is another common signal that is sent from the keyboard.
     * Trap it here, to ensure that the keyboard modifier map and other
     * state for the input devices are restored. (This makes the
     * behavior of SIGQUIT somewhat unexpected, since it will be the
     * same as the behavior of SIGINT.  However, leaving the modifier
     * map of the input devices empty is even more unexpected.) --RF
     */
    OsSignal(SIGQUIT, GiveUp);

#ifdef GLXEXT
    /* Check if GLX extension exists on all back-end servers */
    for (i = 0; i < dmxNumScreens; i++)
        noGlxExtension |= (dmxScreens[i].glxMajorOpcode == 0);
#endif

    if (serverGeneration == 1)
        dmxAddExtensions();

    /* Tell dix layer about the backend displays */
    for (i = 0; i < dmxNumScreens; i++) {

#ifdef GLXEXT
        if (!noGlxExtension) {
            /*
             * Builds GLX configurations from the list of visuals
             * supported by the back-end server, and give that
             * configuration list to the glx layer - so that he will
             * build the visuals accordingly.
             */

            DMXScreenInfo *dmxScreen = &dmxScreens[i];
            __GLXvisualConfig *configs = NULL;
            dmxGlxVisualPrivate **configprivs = NULL;
            int nconfigs = 0;
            int (*oldErrorHandler) (Display *, XErrorEvent *);

            /* Catch errors if when using an older GLX w/o FBconfigs */
            oldErrorHandler = XSetErrorHandler(dmxNOPErrorHandler);

            /* Get FBConfigs of the back-end server */
            dmxScreen->fbconfigs = GetGLXFBConfigs(dmxScreen->beDisplay,
                                                   dmxScreen->glxMajorOpcode,
                                                   &dmxScreen->numFBConfigs);

            XSetErrorHandler(oldErrorHandler);

            dmxScreen->glxVisuals =
                GetGLXVisualConfigs(dmxScreen->beDisplay,
                                    DefaultScreen(dmxScreen->beDisplay),
                                    &dmxScreen->numGlxVisuals);

            if (dmxScreen->fbconfigs) {
                configs =
                    GetGLXVisualConfigsFromFBConfigs(dmxScreen->fbconfigs,
                                                     dmxScreen->numFBConfigs,
                                                     dmxScreen->beVisuals,
                                                     dmxScreen->beNumVisuals,
                                                     dmxScreen->glxVisuals,
                                                     dmxScreen->numGlxVisuals,
                                                     &nconfigs);
            }
            else {
                configs = dmxScreen->glxVisuals;
                nconfigs = dmxScreen->numGlxVisuals;
            }

            configprivs = xallocarray(nconfigs, sizeof(dmxGlxVisualPrivate *));

            if (configs != NULL && configprivs != NULL) {
                int j;

                /* Initialize our private info for each visual
                 * (currently only x_visual_depth and x_visual_class)
                 */
                for (j = 0; j < nconfigs; j++) {

                    configprivs[j] = (dmxGlxVisualPrivate *)
                        malloc(sizeof(dmxGlxVisualPrivate));
                    configprivs[j]->x_visual_depth = 0;
                    configprivs[j]->x_visual_class = 0;

                    /* Find the visual depth */
                    if (configs[j].vid > 0) {
                        int k;

                        for (k = 0; k < dmxScreen->beNumVisuals; k++) {
                            if (dmxScreen->beVisuals[k].visualid ==
                                configs[j].vid) {
                                configprivs[j]->x_visual_depth =
                                    dmxScreen->beVisuals[k].depth;
                                configprivs[j]->x_visual_class =
                                    dmxScreen->beVisuals[k].class;
                                break;
                            }
                        }
                    }
                }

                XFlush(dmxScreen->beDisplay);
            }
        }
#endif                          /* GLXEXT */

        AddScreen(dmxScreenInit, argc, argv);
    }

    /* Compute origin information. */
    dmxInitOrigins();

    /* Compute overlap information. */
    dmxInitOverlap();

    /* Make sure there is a global width/height available */
    dmxComputeWidthHeight(DMX_NO_RECOMPUTE_BOUNDING_BOX);

    /* FIXME: The following is temporarily placed here.  When the DMX
     * extension is available, it will be move there.
     */
    dmxInitFonts();

    /* Initialize the render extension */
    if (!noRenderExtension)
        dmxInitRender();

    /* Initialized things that need timer hooks */
    dmxStatInit();
    dmxSyncInit();              /* Calls RegisterBlockAndWakeupHandlers */
}

/* RATS: Assuming the fp string (which comes from the command-line argv
         vector) is NULL-terminated, the buffer is large enough for the
         strcpy. */
static void
dmxSetDefaultFontPath(const char *fp)
{
    if (dmxFontPath) {
        int fplen = strlen(fp) + 1;
        int len = strlen(dmxFontPath);

        dmxFontPath = realloc(dmxFontPath, len + fplen + 1);
        dmxFontPath[len] = ',';
        strncpy(&dmxFontPath[len + 1], fp, fplen);
    }
    else {
        dmxFontPath = strdup(fp);
    }

    defaultFontPath = dmxFontPath;
}

/** This function is called in Xserver/os/utils.c from \a AbortServer().
 * We must ensure that backend and console state is restored in the
 * event the server shutdown wasn't clean. */
void
AbortDDX(enum ExitCode error)
{
    int i;

    for (i = 0; i < dmxNumScreens; i++) {
        DMXScreenInfo *dmxScreen = &dmxScreens[i];

        if (dmxScreen->beDisplay)
            XCloseDisplay(dmxScreen->beDisplay);
        dmxScreen->beDisplay = NULL;
    }
}

#ifdef DDXBEFORERESET
void
ddxBeforeReset(void)
{
}
#endif

/** This function is called in Xserver/dix/main.c from \a main() when
 * dispatchException & DE_TERMINATE (which is the only way to exit the
 * main loop without an interruption. */
void
ddxGiveUp(enum ExitCode error)
{
    AbortDDX(error);
}

/** This function is called in Xserver/os/osinit.c from \a OsInit(). */
void
OsVendorInit(void)
{
}

/** This function is called in Xserver/os/utils.c from \a FatalError()
 * and \a VFatalError().  (Note that setting the function pointer \a
 * OsVendorVErrorFProc will cause \a VErrorF() (which is called by the
 * two routines mentioned here, as well as by others) to use the
 * referenced routine instead of \a vfprintf().) */
void
OsVendorFatalError(const char *f, va_list args)
{
}

/** Process our command line arguments. */
int
ddxProcessArgument(int argc, char *argv[], int i)
{
    int retval = 0;

    if (!strcmp(argv[i], "-display")) {
        if (++i < argc)
            dmxConfigStoreDisplay(argv[i]);
        retval = 2;
    }
    else if (!strcmp(argv[i], "-inputfrom") || !strcmp(argv[i], "-input")) {
        if (++i < argc)
            dmxConfigStoreInput(argv[i]);
        retval = 2;
    }
    else if (!strcmp(argv[i], "-xinputfrom") || !strcmp(argv[i], "-xinput")) {
        if (++i < argc)
            dmxConfigStoreXInput(argv[i]);
        retval = 2;
    }
    else if (!strcmp(argv[i], "-noshadowfb")) {
        retval = 1;
    }
    else if (!strcmp(argv[i], "-nomulticursor")) {
        dmxCursorNoMulti();
        retval = 1;
    }
    else if (!strcmp(argv[i], "-shadowfb")) {
        retval = 1;
    }
    else if (!strcmp(argv[i], "-configfile")) {
        if (++i < argc)
            dmxConfigStoreFile(argv[i]);
        retval = 2;
    }
    else if (!strcmp(argv[i], "-config")) {
        if (++i < argc)
            dmxConfigStoreConfig(argv[i]);
        retval = 2;
    }
    else if (!strcmp(argv[i], "-fontpath")) {
        if (++i < argc)
            dmxSetDefaultFontPath(argv[i]);
        retval = 2;
    }
    else if (!strcmp(argv[i], "-stat")) {
        if ((i += 2) < argc)
            dmxStatActivate(argv[i - 1], argv[i]);
        retval = 3;
    }
    else if (!strcmp(argv[i], "-syncbatch")) {
        if (++i < argc)
            dmxSyncActivate(argv[i]);
        retval = 2;
    }
    else if (!strcmp(argv[i], "-nooffscreenopt")) {
        dmxOffScreenOpt = FALSE;
        retval = 1;
    }
    else if (!strcmp(argv[i], "-nosubdivprims")) {
        dmxSubdividePrimitives = FALSE;
        retval = 1;
    }
    else if (!strcmp(argv[i], "-nowindowopt")) {
        dmxLazyWindowCreation = FALSE;
        retval = 1;
    }
    else if (!strcmp(argv[i], "-noxkb")) {
        dmxUseXKB = FALSE;
        retval = 1;
    }
    else if (!strcmp(argv[i], "-depth")) {
        if (++i < argc)
            dmxDepth = atoi(argv[i]);
        retval = 2;
    }
    else if (!strcmp(argv[i], "-norender")) {
        noRenderExtension = TRUE;
        retval = 1;
#ifdef GLXEXT
    }
    else if (!strcmp(argv[i], "-noglxproxy")) {
        dmxGLXProxy = FALSE;
        retval = 1;
    }
    else if (!strcmp(argv[i], "-noglxswapgroup")) {
        dmxGLXSwapGroupSupport = FALSE;
        retval = 1;
    }
    else if (!strcmp(argv[i], "-glxsyncswap")) {
        dmxGLXSyncSwap = TRUE;
        retval = 1;
    }
    else if (!strcmp(argv[i], "-glxfinishswap")) {
        dmxGLXFinishSwap = TRUE;
        retval = 1;
#endif
    }
    else if (!strcmp(argv[i], "-ignorebadfontpaths")) {
        dmxIgnoreBadFontPaths = TRUE;
        retval = 1;
    }
    else if (!strcmp(argv[i], "-addremovescreens")) {
        dmxAddRemoveScreens = TRUE;
        retval = 1;
    }
    else if (!strcmp(argv[i], "-param")) {
        if ((i += 2) < argc) {
            if (!strcasecmp(argv[i - 1], "xkbrules"))
                dmxConfigSetXkbRules(argv[i]);
            else if (!strcasecmp(argv[i - 1], "xkbmodel"))
                dmxConfigSetXkbModel(argv[i]);
            else if (!strcasecmp(argv[i - 1], "xkblayout"))
                dmxConfigSetXkbLayout(argv[i]);
            else if (!strcasecmp(argv[i - 1], "xkbvariant"))
                dmxConfigSetXkbVariant(argv[i]);
            else if (!strcasecmp(argv[i - 1], "xkboptions"))
                dmxConfigSetXkbOptions(argv[i]);
            else
                dmxLog(dmxWarning,
                       "-param requires: XkbRules, XkbModel, XkbLayout,"
                       " XkbVariant, or XkbOptions\n");
        }
        retval = 3;
    }
    if (!serverGeneration)
        dmxConfigSetMaxScreens();
    return retval;
}

/** Provide succinct usage information for the DMX server. */
void
ddxUseMsg(void)
{
    ErrorF("\n\nDevice Dependent Usage:\n");
    ErrorF("-display string      Specify the back-end display(s)\n");
    ErrorF("-input string        Specify input source for core device\n");
    ErrorF("-xinput string       Specify input source for XInput device\n");
    ErrorF("-shadowfb            Enable shadow frame buffer\n");
    ErrorF("-configfile file     Read from a configuration file\n");
    ErrorF("-config config       Select a specific configuration\n");
    ErrorF("-nomulticursor       Turn of multiple cursor support\n");
    ErrorF("-fontpath            Sets the default font path\n");
    ErrorF("-stat inter scrns    Print out performance statistics\n");
    ErrorF("-syncbatch inter     Set interval for XSync batching\n");
    ErrorF("-nooffscreenopt      Disable offscreen optimization\n");
    ErrorF("-nosubdivprims       Disable primitive subdivision\n");
    ErrorF("                     optimization\n");
    ErrorF("-nowindowopt         Disable lazy window creation optimization\n");
    ErrorF("-noxkb               Disable use of the XKB extension with\n");
    ErrorF("                     backend displays (cf. -kb).\n");
    ErrorF("-depth               Specify the default root window depth\n");
    ErrorF("-norender            Disable RENDER extension support\n");
#ifdef GLXEXT
    ErrorF("-noglxproxy          Disable GLX Proxy\n");
    ErrorF("-noglxswapgroup      Disable swap group and swap barrier\n");
    ErrorF("                     extensions in GLX proxy\n");
    ErrorF("-glxsyncswap         Force XSync after swap buffers\n");
    ErrorF("-glxfinishswap       Force glFinish after swap buffers\n");
#endif
    ErrorF
        ("-ignorebadfontpaths  Ignore bad font paths during initialization\n");
    ErrorF("-addremovescreens    Enable dynamic screen addition/removal\n");
    ErrorF("-param ...           Specify configuration parameters (e.g.,\n");
    ErrorF("                     XkbRules, XkbModel, XkbLayout, etc.)\n");
    ErrorF("\n");
    ErrorF("    If the -input string matches a -display string, then input\n"
           "    is taken from that backend display.  (XInput cannot be taken\n"
           "    from a backend display.)  Placing \",console\" after the\n"
           "    display name will force a console window to be opened on\n"
           "    that display in addition to the backend input.  This is\n"
           "    useful if the backend window does not cover the whole\n"
           "    physical display.\n\n");

    ErrorF("    Otherwise, if the -input or -xinput string specifies another\n"
           "    X display, then a console window will be created on that\n"
           "    display.  Placing \",windows\" or \",nowindows\" after the\n"
           "    display name will control the display of window outlines in\n"
           "    the console.\n\n");

    ErrorF("    -input or -xinput dummy specifies no input.\n");
    ErrorF("    -input or -xinput local specifies the use of a raw keyboard,\n"
           "    mouse, or other (extension) device:\n"
           "        -input local,kbd,ps2 will use a ps2 mouse\n"
           "        -input local,kbd,ms  will use a serial mouse\n"
           "        -input local,usb-kbd,usb-mou will use USB devices \n"
           "        -xinput local,usb-oth will use a non-mouse and\n"
           "                non-keyboard USB device with XInput\n\n");

    ErrorF("    Special Keys:\n");
    ErrorF("        Ctrl-Alt-g    Server grab/ungrab (console only)\n");
    ErrorF("        Ctrl-Alt-f    Fine (1-pixel) mouse mode (console only)\n");
    ErrorF("        Ctrl-Alt-q    Quit (core devices only)\n");
    ErrorF("        Ctrl-Alt-F*   Switch to VC (local only)\n");
}
