/* Xorg: xdbedizzy.c /main/3 2004/10/18 14:21:35 gisburn $ */ /****************************************************************************** * * Copyright (c) 1994, 1995 Silicon Graphics Inc. * Copyright (c) 2004 Roland Mainz * * 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 * */ #include #include #include #ifdef USE_XPRINT #include #endif #include #include #include #include #include #include #include /* 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 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); } #endif else { 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; }