/* Xorg: xdbedizzy.c /main/3 2004/10/18 14:21:35 gisburn $ */
/******************************************************************************
 * 
 * Copyright (c) 1994, 1995  Silicon Graphics Inc.
 * Copyright (c) 2004  Roland Mainz <roland.mainz@nrubsig.org>
 *
 * Permission to use, copy, modify, and distribute this
 * software and its documentation for any purpose and without
 * fee is hereby granted, 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 Silicon Graphics not be
 * used in advertising or publicity pertaining to distribution
 * of the software without specific prior written permission.
 * Silicon Graphics makes no representation about the suitability
 * of this software for any purpose. It is provided "as is"
 * without any express or implied warranty.
 *
 * SILICON GRAPHICS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
 * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SILICON
 * GRAPHICS 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.
 * 
 *****************************************************************************/

/*
 * xdbedizzy - demo of DBE creating a double buffered spinning scene
 *
 * Original dizzy program written by Mark Kilgard.
 *
 * Adapted to use DBE for double buffering by Allen Leinwand, 2/24/1995 .
 * Print support added by Roland Mainz, 10/18/2004
 *
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/extensions/Xdbe.h>
#ifdef USE_XPRINT
#include <X11/XprintUtil/xprintutil.h>
#endif
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <errno.h>
#include <unistd.h>
#include <X11/Xpoll.h>

/* Turn a NULL pointer string into an empty string */
#define NULLSTR(x) (((x)!=NULL)?(x):(""))
#define Log(x) { if(verbose) printf x; }
#define Msg(x) { printf x; }

/* Global variables */
static char             *ProgramName   = NULL;
static Display          *dpy           = NULL;
static Screen           *screen        = NULL;
static int               screennum     = -1;
#ifdef USE_XPRINT
static XPContext         pcontext      = None; /* Xprint context */
#endif
static XRectangle        winrect       = { 0 };
static unsigned long     c_black, c_pink, c_green, c_orange, c_blue;
static Window            win           = None;
static XID               buf           = None;
static XdbeSwapInfo      swapInfo      = { 0 };
static GC                gc_black, gc_pink, gc_green, gc_orange, gc_blue;
static float             rotation      = 0.0;
static float             delta         = 0.05;
static float             speed         = 20.0;
static Bool              paused        = False;
static Bool              manual_paused = False;
#ifdef USE_XPRINT
static int               xp_event_base,         /* XpExtension even base   */
                         xp_error_base;         /* XpExtension error base  */
static long              dpi_x         = 0L,    /* Current page resolution */
                         dpi_y         = 0L;    
static int               numPages      = 5,     /* Numer of pages to print */
                         currNumPages  = 0;     /* Current page number */
static Bool              doPrint       = False; /* Print to printer ? */
#endif

/* Default values for unspecified command line arguments */
static char             *display_name  = NULL;
static int               visclass      = PseudoColor;
static int               depth         = 0;
static Bool              listVis       = False;
static int               spokes        = 12;
static Bool              do_db         = True;
static Bool              verbose       = False;
static Bool              synchronous   = False;
static VisualID          visid         = 0;

static const char *help_message[] = {
"  where options include:",
"    -display host:dpy       X server connection to use.",
#ifdef USE_XPRINT
"    -print                  Use printer instead of video card for output.",
"    -printer printername    Name of printer to use.",
"    -printfile printername  Output file for print job.",
"    -numpages count         Number of pages to print.",
#endif
"    -delta dlt              Rotate <dlt> per frame (video) or page (printer).",
"    -class classname        Class of visual to use.",
"    -depth n                Depth of visual to use.",
"    -visid [nn,0xnn]        Visual ID to use (ignore -class, -depth).",
"    -list                   List double buffer capable visuals.",
"    -nodb                   Single buffer (ignore -class, -depth, -visid).",
"    -help                   Print this message.",
"    -speed val              Floating-point value to set the speed.",
"    -sync                   Use synchronous X connection.",
"    -spokes n               Specify number of spokes to draw.",
"    -verbose                Produce chatty messages while running.",
NULL};

static
void usage(void)
{
    const char **cpp;

    fprintf (stderr, "\nusage:  %s [-options ...]\n", ProgramName);
    for (cpp = help_message; *cpp; cpp++) {
        fprintf (stderr, "%s\n", *cpp);
    }
    fprintf (stderr, "\n");
    exit(EXIT_FAILURE);
}


static
unsigned long getColor(Colormap cmap, const char *color_name)
{
    XColor color;
    XColor exact;
    int    status;

    status = XAllocNamedColor(dpy, cmap, color_name, &color, &exact);
    if (status == 0) {
        fprintf(stderr, "%s: Couldn't get color: %s\n", ProgramName, color_name);
        exit(EXIT_FAILURE);
    }
    return (color.pixel);
}



#define RATIO1 0.4
#define RATIO2 0.7
#define RATIO3 0.95

#ifndef M_PI
#define M_PI 3.1415927
#endif

#define S_ANGLE(s) (M_PI*2./(s))

static
void redraw(void)
{
    int     i;
    int     x, y;
    XPoint  pnt[4];

    Log(("redraw.\n"));

    /* the double-buffer extension will clear the buffer itself */
    if (!do_db) {
        XClearWindow(dpy, win);
    }
    x = winrect.width / 2;
    x += (int) (sin(rotation * 2) * 20);
    y = winrect.height / 2;
    y += (int) (cos(rotation * 2) * 20);
    for (i = 5; i < 26; i += 3) {
        XDrawArc(dpy, buf, gc_orange, x - i * 10,      y - i * 10,      i * 20,      i * 20,      0, 360 * 64);
        XDrawArc(dpy, buf, gc_green,  x - i * 10 - 5,  y - i * 10 - 5,  i * 20 + 10, i * 20 + 10, 0, 360 * 64);
        XDrawArc(dpy, buf, gc_blue,   x - i * 10 - 10, y - i * 10 - 10, i * 20 + 20, i * 20 + 20, 0, 360 * 64);
    }

    x = winrect.width  / 2;
    y = winrect.height / 2;
    pnt[0].x = x;
    pnt[0].y = y;
    for (i = 0; i < spokes; i++) {
        pnt[1].x = (int) (cos(i * S_ANGLE(spokes) + rotation)       * (RATIO1 * x)) + x;
        pnt[1].y = (int) (sin(i * S_ANGLE(spokes) + rotation)       * (RATIO1 * y)) + y;
        pnt[2].x = (int) (cos(i * S_ANGLE(spokes) + rotation - 0.1) * (RATIO2 * x)) + x;
        pnt[2].y = (int) (sin(i * S_ANGLE(spokes) + rotation - 0.1) * (RATIO2 * y)) + y;
        pnt[3].x = (int) (cos(i * S_ANGLE(spokes) + rotation - 0.2) * (RATIO3 * x)) + x;
        pnt[3].y = (int) (sin(i * S_ANGLE(spokes) + rotation - 0.2) * (RATIO3 * y)) + y;
        XDrawLines(dpy, buf, gc_pink, pnt, 4, CoordModeOrigin);
    }

    if (do_db) {
        XdbeSwapBuffers(dpy, &swapInfo, 1);
    }
}


static
Visual *
choose_DB_visual( /* Input */         Display *dpy, Bool listVis, int visclass,
                  /* Input, Output */ int *pDepth)
{
    Drawable               screen_list[1];
    int                    num_screens;
    XdbeScreenVisualInfo  *DBEvisInfo;
    int                    i, nitems;
    int                    chosenDepth = 0;
    Visual                *chosenVisual = NULL;
    XVisualInfo            vinfo_template, *XvisInfo;

    screen_list[0] = XRootWindowOfScreen(screen);
    num_screens = 1;
    DBEvisInfo = XdbeGetVisualInfo(dpy, screen_list, &num_screens);
    if (DBEvisInfo == NULL) {
        fprintf(stderr, "XdbeGetVisualInfo returned NULL\n");
        return (NULL);
    }

    if (listVis) {
        printf("\nThe double buffer capable visuals are:\n");
        printf("      visual ID    depth    class\n");
    }
    for (i = 0; i < DBEvisInfo->count; i++) {

        vinfo_template.visualid = DBEvisInfo->visinfo[i].visual;
        XvisInfo = XGetVisualInfo(dpy, VisualIDMask,
                                   &vinfo_template, &nitems);
        if (XvisInfo == NULL) {
            fprintf(stderr,
                    "%s: XGetVisualInfo returned NULL for visual %d\n",
                    ProgramName, (int)vinfo_template.visualid);
            return (NULL);
        }
        if (listVis) {
            char visualClassName[64];

            switch( XvisInfo->class ) {
                case TrueColor:   strcpy(visualClassName, "TrueColor");    break;
                case DirectColor: strcpy(visualClassName, "DirectColor");  break;
                case PseudoColor: strcpy(visualClassName, "PseudoColor");  break;
                case StaticColor: strcpy(visualClassName, "StaticColor");  break;
                case GrayScale:   strcpy(visualClassName, "GrayScale");    break;
                case StaticGray:  strcpy(visualClassName, "StaticGray");   break;
                default:
                    sprintf(visualClassName, "unknown_visual_class_%x", (int)XvisInfo->class);
                    break;
            }
 
            printf("        %#4x      %4d    %s\n",
                   (int)DBEvisInfo->visinfo[i].visual,
                   (int)DBEvisInfo->visinfo[i].depth,
                   visualClassName);
        }

        if (visid) {
            if (XvisInfo->visualid == visid) {
                chosenVisual = XvisInfo->visual;
                chosenDepth = XvisInfo->depth;
            }
        }
        else if (XvisInfo->class == visclass) {
            if (*pDepth == 0) {
                /* Choose first deepest visual of matching class. */
                if (DBEvisInfo->visinfo[i].depth > chosenDepth) {
                    chosenVisual = XvisInfo->visual;
                    chosenDepth  = XvisInfo->depth;
                }
            }
            else {
                /* Choose last visual of matching depth and class. */
                if (DBEvisInfo->visinfo[i].depth == *pDepth) {
                    chosenVisual = XvisInfo->visual;
                    chosenDepth  = XvisInfo->depth;
                }
            }
        }
    }

    if (chosenVisual) {
        if (listVis) {
            printf("\n");
        }
        *pDepth = chosenDepth;
        return (chosenVisual);
    }
    else {
        return (NULL);
    }
}

static
void main_loop(void)
{
    fd_set         select_mask;
    int            fd;
    struct timeval timeout;
    int            new_event;
    int            pending;
    Bool           done = False;

    fd = XConnectionNumber(dpy);
    FD_ZERO(&select_mask);
    FD_SET(fd, &select_mask);

    while (!done) {
        XEvent event;

        /* When we print we only render on Expose events and bump
         * |rotation| when the page number changes */                 
        if (!paused && !manual_paused
#ifdef USE_XPRINT
	    && !doPrint
#endif
	    ) {
            pending = XEventsQueued(dpy, QueuedAfterFlush);
            if (pending == 0) {
                do {
                    FD_ZERO(&select_mask);
                    FD_SET(fd, &select_mask);
                    timeout.tv_sec  = 0;
                    timeout.tv_usec = 2000000./speed;
                    new_event = select(fd + 1, &select_mask, NULL, NULL, &timeout);

                    /* This isn't good - we should check the time stamps
                     * between two frames to get a stable frame rate */
                    if (new_event == 0) {
                      rotation = rotation + delta;
                      redraw();
                      XFlush(dpy);
                    }
                } while (new_event == 0);
            }
        }

        XNextEvent(dpy, &event);

#ifdef USE_XPRINT
        /* XpExtension event ? */
        if( doPrint && 
            (event.type == xp_event_base+XPPrintNotify) )
        {
            XPPrintEvent *pev = (XPPrintEvent *)&event;

            switch( pev->detail ) {
                case XPStartJobNotify:
                    Log(("XPStartJobNotify: Starting first page...\n"));
                    XpStartPage(dpy, win);
                    break;
                case XPEndJobNotify:
                    Log(("XPEndJobNotify: Job done..."));
                    /* Job done... */
                    done = True;
                    break;
                case XPStartDocNotify:
                    Log(("XPStartJobNotify: Nop\n"));
                    break;
                case XPEndDocNotify:
                    Log(("XPEndDocNotify: Nop\n"));
                    break;
                case XPStartPageNotify:
                    /* XpStartPage() will automatically trigger an Expose event */

                    Log(("XPStartPageNotify: Page end reached.\n"));
                    XpEndPage(dpy);
                    break;
                case XPEndPageNotify:
                    /* next page or exit */
                    currNumPages++;
                    
                    rotation = rotation + delta;

                    if( currNumPages < numPages ) {
                      Log(("Starting next page (%d)...\n", currNumPages));
                      XpStartPage(dpy, win);
                    }
                    else
                    {
                      Log(("XPEndPageNotify: Finishing job...\n"));
                      XpEndJob(dpy);
                    }
                    break;
                default:
                    Log(("--> other XPPrintEvent event, detail=%x\n", (int)pev->detail));
                    break;
            }
        }
        else
#endif
        {
            switch (event.type) {
                case MapNotify:
                    Log(("MapNotify: resuming...\n"));
                    paused = False;
                    break;
                case UnmapNotify:
                    Log(("UnmapNotify: pausing...\n"));
                    paused = True;
                    break;
                case VisibilityNotify:
                    switch (event.xvisibility.state) {
                    case VisibilityUnobscured:
                        Log(("VisibilityUnobscured: resuming...\n"));
                        paused = False;
                        break;
                    case VisibilityPartiallyObscured:
                        Log(("VisibilityPartiallyObscured: resuming...\n"));
                        paused = False;
                        break;
                    case VisibilityFullyObscured:
                        Log(("VisibilityFullyObscured: pausing...\n"));
                        paused = True;
                        break;
                    }
                    break;
                case Expose:
                    Log(("Expose: rendering.\n"));

                    /* Swallow any extra Expose events (only needed for video
                     * display, the Xprint server is non-interactive and
                     * therefore cannot create extra Expose events caused
                     * by user input) */
#ifdef USE_XPRINT
                    if (!doPrint)
#endif
                      while (XCheckTypedEvent(dpy, Expose, &event))
                        ;

                    redraw();
                    break;
                case ButtonPress:
                    switch (event.xbutton.button) {
                        case 1:
                            Msg(("ButtonPress: faster: %g\n", delta));
                            delta += 0.005;
                            break;
                        case 2:
                            Msg(("ButtonPress: slower: %g\n", delta));
                            delta += -0.005;
                            break;
                        case 3:
                            if (manual_paused) {
                                Msg(("ButtonPress: manual resume.\n"));
                                manual_paused = False;
                            } else {
                                Msg(("ButtonPress: manual pause.\n"));
                                manual_paused = True;
                            }
                    }
                    break;
                case KeyPress:
                    Msg(("KeyPress: done.\n"));
                    done = True;
                    break;
                case ConfigureNotify:
                    Log(("ConfigureNotify: resizing.\n"));
                    winrect.width  = event.xconfigure.width;
                    winrect.height = event.xconfigure.height;
                    break;
            }
        }
    }
}


int main(int argc, char *argv[])
{
    int                  i;
    XSetWindowAttributes attrs;
    Visual              *visual;
    Colormap             cmap;
    XGCValues            gcvals;
    void                *printtofile_handle = NULL; /* "context" when printing to file */
    const char          *printername        = NULL;  /* printer to query */
    const char          *toFile             = NULL;  /* output file (instead of printer) */
#ifdef USE_XPRINT
    XPPrinterList        plist              = NULL;  /* list of printers */
    int                  plist_count;                /* number of entries in |plist|-array */
#endif
    unsigned short       dummy;
    Bool                 use_threadsafe_api = True;

    ProgramName = argv[0];

    for (i = 1; i < argc; i++) {
        char *arg;

        arg = argv[i];
        if (!strcmp(arg, "-display")) {
            if (++i >= argc) {
                fprintf(stderr, "%s: Missing argument to -display\n", ProgramName);
                exit(EXIT_FAILURE);
            }
            display_name = argv[i];
        }
#ifdef USE_XPRINT
        else if (!strcmp(arg, "-print")) {
            doPrint = True;
        }
        else if (!strcmp(arg, "-printer")) {
            if (++i >= argc)
               usage();
            printername = argv[i];
            doPrint = True;
        }
        else if (!strcmp(arg, "-printfile")) {
            if (++i >= argc)
                usage();
            toFile = argv[i];
            doPrint = True;
        }
        else if (!strcmp(arg, "-numpages")) {
            if (++i >= argc)
                usage();
            errno = 0; /* reset errno to catch |atoi()|-errors */
            numPages = atoi(argv[i]);
            if ((numPages <= 0) || (errno != 0))
                usage();
            doPrint = True;
        }
#endif
        else if (!strcmp(arg, "-delta")) {
            if (++i >= argc)
                usage();
            errno = 0; /* reset errno to catch |atof()|-errors */
            delta = atof(argv[i]);
            if (errno != 0)
                usage();
        }
        else if (!strcmp(arg, "-class")) {
            arg = argv[++i];
            if (arg == NULL) {
                fprintf(stderr, "%s: Missing argument to -class\n", ProgramName);
                exit(EXIT_FAILURE);
            }
            if ((!strcmp(arg, "TrueColor")) || (!strcmp(arg, "True")))
                visclass = TrueColor;
            else if (!strcmp(arg, "DirectColor"))
                visclass = DirectColor;
            else if ((!strcmp(arg, "PseudoColor")) || (!strcmp(arg, "Pseudo")))
                visclass = PseudoColor;
            else if (!strcmp(arg, "StaticColor"))
                visclass = StaticColor;
            else if (!strcmp(arg, "GrayScale"))
                visclass = GrayScale;
            else if (!strcmp(arg, "StaticGray"))
                visclass = StaticGray;
            else {
                fprintf(stderr, "%s: Wrong argument %s for -class\n", ProgramName, arg);
                exit(EXIT_FAILURE);
            }
        } else if (!strcmp(arg, "-depth")) {
            arg = argv[++i];
            if (arg == NULL) {
                fprintf(stderr, "%s: Missing argument to -depth\n", ProgramName);
                exit(EXIT_FAILURE);
            }
            errno = 0; /* reset errno to catch |atoi()|-errors */
            depth = atoi(arg);
            if (errno != 0)
                usage();
        } else if (!strcmp(arg, "-help")) {
            usage();
        } else if (!strcmp(arg, "-list")) {
            listVis = True;
        } else if (!strcmp(arg, "-speed")) {
            if (++i >= argc)
                usage();
            errno = 0; /* reset errno to catch |atof()|-errors */
            speed = atof(argv[i]);
            if (errno != 0)
                usage();
        } else if (!strcmp(arg, "-spokes")) {
            arg = argv[++i];
            if (arg == NULL) {
                fprintf(stderr, "%s: Missing argument to -spokes\n", ProgramName);
                exit(EXIT_FAILURE);
            }
            errno = 0; /* reset errno to catch |atoi()|-errors */
            spokes = atoi(arg);
            if (errno != 0)
                usage();
        } else if (!strcmp(arg, "-nodb")) {
            do_db = False;
        } else if (!strcmp(arg, "-visid")) {
            arg = argv[++i];
            if (arg == NULL) {
                fprintf(stderr, "%s: Missing argument to -visid\n", ProgramName);
                exit(EXIT_FAILURE);
            }
            /* |atol()| only uses base10, |strtol(..., ..., 0)| takes any base */
            visid = (int) strtol(arg, (char **)NULL, 0);
        } else if (!strcmp(arg, "-verbose")) {
            verbose = True;
        } else if (!strcmp(arg, "-sync")) {
            synchronous = True;
        } else if (!strcmp(arg, "-debug_use_threadsafe_api")) {
            use_threadsafe_api = True;
        }
        else {
            fprintf(stderr, "%s: Unrecognized option %s\n", ProgramName, arg);
            usage();
        }
    }

#ifdef USE_XPRINT
    /* Display and printing at the same time not implemented */
    if (doPrint && display_name) {
        usage();
    }
#endif

    if (use_threadsafe_api) {
        if (!XInitThreads()) {
            fprintf(stderr, "%s: XInitThreads() failure.\n", ProgramName);
            exit(EXIT_FAILURE);
        }
    }

#ifdef USE_XPRINT
    if (doPrint) {
        plist = XpuGetPrinterList(printername, &plist_count);

        if (!plist) {
            fprintf(stderr, "%s:  no printers found for printer spec \"%s\".\n",
                    ProgramName, NULLSTR(printername));
            return EXIT_FAILURE;
        }

        printername = plist[0].name;

        Log(("Using printer '%s'\n", printername));

        if (XpuGetPrinter(printername, &dpy, &pcontext) != 1) {
            fprintf(stderr, "%s: Cannot open printer '%s'\n", ProgramName, printername);
            return EXIT_FAILURE;
        }
        
        if (synchronous) {
            Log(("Running in synchronous X mode.\n"));
            XSynchronize(dpy, True);
        }

        if (XpQueryExtension(dpy, &xp_event_base, &xp_error_base) == False) {
            fprintf(stderr, "%s: XpQueryExtension() failed.\n", ProgramName);
            XpuClosePrinterDisplay(dpy, pcontext);
            return EXIT_FAILURE;
        }

        /* Listen to XP(Start|End)(Job|Doc|Page)Notify events).
         * This is mandatory as Xp(Start|End)(Job|Doc|Page) functions are _not_ 
         * syncronous !!
         * Not waiting for such events may cause that subsequent data may be 
         * destroyed/corrupted!!
         */
        XpSelectInput(dpy, pcontext, XPPrintMask);

        /* Set job title */
        XpuSetJobTitle(dpy, pcontext, "xdbedizzy for Xprint");

        /* Set print context
         * Note that this modifies the available fonts, including builtin printer prints.
         * All XListFonts()/XLoadFont() stuff should be done _after_ setting the print 
         * context to obtain the proper fonts.
         */ 
        XpSetContext(dpy, pcontext);

        /* Get default printer reolution */   
        if (XpuGetResolution(dpy, pcontext, &dpi_x, &dpi_y) != 1) {
            fprintf(stderr, "%s: No default resolution for printer '%s'.\n",
            ProgramName, printername);
            XpuClosePrinterDisplay(dpy, pcontext);
            return EXIT_FAILURE;
        }

        if (toFile) {
            Log(("starting job (to file '%s').\n", toFile));
            printtofile_handle = XpuStartJobToFile(dpy, pcontext, toFile);
            if( !printtofile_handle ) {
               fprintf(stderr, "%s: Error: %s while trying to print to file.\n", 
                       ProgramName, strerror(errno));
               XpuClosePrinterDisplay(dpy, pcontext);
               return EXIT_FAILURE;
            }
        }
        else
        {
            Log(("starting job.\n"));
            XpuStartJobToSpooler(dpy);    
        }

        screen = XpGetScreenOfContext(dpy, pcontext);
        screennum = XScreenNumberOfScreen(screen);

        /* Obtain some info about page geometry */
        XpGetPageDimensions(dpy, pcontext, &dummy, &dummy, &winrect);
    }
    else
#endif
    {
        dpy = XOpenDisplay(display_name);
        if (dpy == NULL) {
            fprintf(stderr, "%s: Cannot open display %s\n",
                    ProgramName, XDisplayName(display_name));
            exit(EXIT_FAILURE);
        }

        if (synchronous) {
            Log(("Running in synchronous X mode.\n"));
            XSynchronize(dpy, True);
        }

        screen = XDefaultScreenOfDisplay(dpy);
        screennum = XScreenNumberOfScreen(screen);
#ifdef USE_XPRINT
        pcontext = None;
#endif

        winrect.x      = 10;
        winrect.y      = 10;
        winrect.width  = 400;
        winrect.height = 400;

#ifdef USE_XPRINT
        dpi_x = dpi_y = 100L; /* hack-style - but enougth for our needs */
#endif
    }
    
    if (do_db) {
        int dbeMajorVersion,
            dbeMinorVersion;

        if (!XdbeQueryExtension (dpy, &dbeMajorVersion, &dbeMinorVersion)) {
            fprintf(stderr, "%s: XdbeQueryExtension() failed.\n", ProgramName);
            exit(EXIT_FAILURE);
        }

        if ((visual = choose_DB_visual(dpy, listVis, visclass, &depth)) == NULL) {
            fprintf(stderr, "%s: Failed to find matching double buffer capable visual.\n", ProgramName);
            exit(EXIT_FAILURE);
        }
        fprintf(stdout, "%s: Chose visual ID: %#4x depth: %d\n\n",
                ProgramName, (int)visual->visualid, depth);
    }
    else {
        /* No double buffering: ignore class, depth; use default visual. */
        visual = XDefaultVisual(dpy, screennum);
        depth  = XDefaultDepth(dpy,  screennum);
    }

    cmap = XCreateColormap(dpy, XRootWindowOfScreen(screen), visual, AllocNone);
    c_black  = getColor(cmap, "black");
    c_pink   = getColor(cmap, "pink");
    c_green  = getColor(cmap, "green");
    c_orange = getColor(cmap, "orange");
    c_blue   = getColor(cmap, "blue");
    attrs.colormap         = cmap;
    attrs.background_pixel = c_black;
    attrs.border_pixel     = c_black;
    win = XCreateWindow(dpy, XRootWindowOfScreen(screen), 
                        winrect.x, winrect.y, winrect.width, winrect.height,
                        0, depth, InputOutput, visual,
                        CWBorderPixel | CWColormap | CWBackPixel, &attrs);
    if (win == None) {
        fprintf(stderr, "%s: Couldn't window.\n", ProgramName);
        exit(EXIT_FAILURE);
    }

    XSetStandardProperties(dpy, win, "DBE dizzy demo", ProgramName, None,
                           argv, argc, NULL);
    XSelectInput(dpy, win,
                 VisibilityChangeMask | ExposureMask | ButtonPressMask | KeyPressMask |
                 StructureNotifyMask);
    if (do_db) {
        swapInfo.swap_action = XdbeBackground;
        buf = XdbeAllocateBackBufferName (dpy, win, swapInfo.swap_action);
        if (buf == None) {
            fprintf(stderr, "%s: Couldn't create buffers\n", ProgramName);
            exit(EXIT_FAILURE);
        }
        else {
            swapInfo.swap_window = win;
        }
    }
    else {
        buf = win; /* No double buffering. */
    }

    /* Create GCs, one per color (to avoid pipeline flushing
     * when the GC is changed) */
#ifdef USE_XPRINT
    gcvals.line_width = (8L * ((dpi_x+dpi_y)/2L)) / 100L; /* scale line with DPI */
#else
    gcvals.line_width = 8L;
#endif
    
    gcvals.cap_style  = CapRound;
#define CREATECOLORGC(cl) (gcvals.foreground = (cl), \
                           XCreateGC(dpy, win, GCForeground | GCLineWidth | GCCapStyle, &gcvals))
    gc_black  = CREATECOLORGC(c_black);
    gc_pink   = CREATECOLORGC(c_pink);
    gc_green  = CREATECOLORGC(c_green);
    gc_orange = CREATECOLORGC(c_orange);
    gc_blue   = CREATECOLORGC(c_blue);
#undef CREATECOLORGC

    XMapWindow(dpy, win);

    main_loop();

#ifdef USE_XPRINT
    if (doPrint) {
        char *scr;
        
        /* End the print job - the final results are sent by the X print
         * server to the spooler sub system.
         */
        Log(("finishing print job.\n"));

        /* Job completed, check if there are any messages from the spooler command */
        scr = XpGetOneAttribute(dpy, pcontext, XPJobAttr, "xp-spooler-command-results");
        if( scr )
        {
          if( strlen(scr) > 0 )
          {
            const char *msg = XpuCompoundTextToXmb(dpy, scr);
            if( msg )
            {
              Msg(("Spooler command returned:\n%s", msg));
              XpuFreeXmbString(msg);
            }
            else
            {
              Msg(("Spooler command returned (unconverted):\n%s", scr));
            }
          }

          XFree((void *)scr);
        }

        if (toFile) {
           if (XpuWaitForPrintFileChild(printtofile_handle) != XPGetDocFinished) {
              fprintf(stderr, "%s: Error while printing to file.\n", ProgramName);
              XpuClosePrinterDisplay(dpy, pcontext);
              return EXIT_FAILURE;
           }
        }

        XDestroyWindow(dpy, win);
        XpuClosePrinterDisplay(dpy, pcontext);

        XpuFreePrinterList(plist);
    }
    else
#endif
    {
        XDestroyWindow(dpy, win);
        XCloseDisplay(dpy);
    }
    
    Log(("Done."));

    return EXIT_SUCCESS;
}