x11perf.c revision 533545b5
1/****************************************************************************
2Copyright 1988, 1989 by Digital Equipment Corporation, Maynard, Massachusetts.
3
4                        All Rights Reserved
5
6Permission to use, copy, modify, and distribute this software and its
7documentation for any purpose and without fee is hereby granted,
8provided that the above copyright notice appear in all copies and that
9both that copyright notice and this permission notice appear in
10supporting documentation, and that the name of Digital not be
11used in advertising or publicity pertaining to distribution of the
12software without specific, written prior permission.
13
14DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
15ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
16DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
17ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
18WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
19ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
20SOFTWARE.
21
22****************************************************************************/
23
24#include <stdio.h>
25#include <ctype.h>
26#include <signal.h>
27#include <stdint.h>
28
29#ifndef VMS
30#include <X11/Xatom.h>
31#include <X11/Xos.h>
32#else
33#include <decw$include/Xatom.h>
34#endif
35#include "x11perf.h"
36#include <X11/Xmu/SysUtil.h>
37
38#include <time.h>
39#define Time_t time_t
40#include <stdlib.h>
41
42/* Only for working on ``fake'' servers, for hardware that doesn't exist */
43static Bool     drawToFakeServer = False;
44static Bool     falsePrecision  = False;
45static Pixmap   tileToQuery     = None;
46static char *displayName;
47int	abortTest;
48
49typedef struct _RopNames { const char	*name; int  rop; } RopNameRec, *RopNamePtr;
50
51static RopNameRec ropNames[] = {
52	{ "clear",    	  GXclear },		/* 0 */
53	{ "and",	  GXand },		/* src AND dst */
54	{ "andReverse",	  GXandReverse }, 	/* src AND NOT dst */
55	{ "copy",	  GXcopy },		/* src */
56	{ "andInverted",  GXandInverted },	/* NOT src AND dst */
57	{ "noop",	  GXnoop },		/* dst */
58	{ "xor",	  GXxor },		/* src XOR dst */
59	{ "or",		  GXor },		/* src OR dst */
60	{ "nor",	  GXnor },		/* NOT src AND NOT dst */
61	{ "equiv",	  GXequiv },		/* NOT src XOR dst */
62	{ "invert",	  GXinvert },		/* NOT dst */
63	{ "orReverse",	  GXorReverse },	/* src OR NOT dst */
64	{ "copyInverted", GXcopyInverted },	/* NOT src */
65	{ "orInverted",	  GXorInverted },	/* NOT src OR dst */
66	{ "nand",	  GXnand },		/* NOT src OR NOT dst */
67	{ "set",	  GXset }		/* 1 */
68};
69
70static RopNameRec popNames[] = {
71	{ "Clear",    	  PictOpClear },
72	{ "Src",	  PictOpSrc },
73	{ "Dst",	  PictOpDst },
74	{ "Over",	  PictOpOver },
75	{ "OverReverse",  PictOpOverReverse },
76	{ "In",		  PictOpIn },
77	{ "InReverse",	  PictOpInReverse },
78	{ "Out",	  PictOpOut },
79	{ "OutReverse",	  PictOpOutReverse },
80	{ "Atop",	  PictOpAtop },
81	{ "AtopReverse",  PictOpAtopReverse },
82	{ "Xor",	  PictOpXor },
83	{ "Add",	  PictOpAdd },
84	{ "Saturate",	  PictOpSaturate },
85};
86
87static RopNameRec formatNames[] = {
88    { "RGB24",	  PictStandardRGB24 },
89    { "ARGB32",	  PictStandardARGB32 },
90    { "A8",	  PictStandardA8 },
91    { "A4",	  PictStandardA4 },
92    { "A1",	  PictStandardA1 },
93    { "NATIVE",	  PictStandardNative },
94};
95
96static const char *(visualClassNames)[] = {
97    "StaticGray",
98    "GrayScale",
99    "StaticColor",
100    "PseudoColor",
101    "TrueColor",
102    "DirectColor"
103};
104
105static Bool     labels		= False;
106static int      repeat		= 5;
107static int	seconds		= 5;
108static int      delay           = 0;
109
110static Window   status;     /* Status window and GC */
111static GC       tgc;
112static int	HSx, HSy;
113
114static double syncTime = 0.0;
115
116static int saveargc;
117static char **saveargv;
118
119#define NUM_ROPS    16
120static int  numRops = 1;
121static int  rops[NUM_ROPS] = { GXcopy };
122#define NUM_POPS    14
123static int  numPops = 1;
124static int  pops[NUM_POPS] = { PictOpOver };
125#define NUM_FORMATS 6
126static int  numFormats = 1;
127static int  formats[NUM_FORMATS] = { PictStandardNative };
128static int  numPlanemasks = 1;
129static unsigned long planemasks[256] = { (unsigned long)~0 };
130
131static const char *foreground = NULL;
132static const char *background = NULL;
133static const char *ddbackground = NULL;
134static int clips = 0;
135
136static int numSubWindows = 7;
137static unsigned long subWindows[] = {4, 16, 25, 50, 75, 100, 200, 0};
138
139static int  fixedReps = 0;
140
141static Bool *doit;
142
143static XRectangle ws[] = {  /* Clip rectangles */
144    {195, 195, 120, 120},
145    { 45, 145, 120, 120},
146    {345, 245, 120, 120},
147    { 45, 275, 120, 120},
148    {345, 115, 120, 120},
149    {195, 325, 120, 120}
150
151};
152#define MAXCLIP     (sizeof(ws) / sizeof(ws[0]))
153static Window clipWindows[MAXCLIP];
154static Colormap cmap;
155static int depth = -1;  /* -1 means use default depth */
156static int vclass = -1; /* -1 means use CopyFromParent */
157
158/* ScreenSaver state */
159static XParmRec    xparms;
160static int ssTimeout, ssInterval, ssPreferBlanking, ssAllowExposures;
161
162/* Static functions */
163static int GetWords(int argi, int argc, char **argv, char **wordsp, int *nump);
164static int GetNumbers(int argi, int argc, char **argv, unsigned long *intsp,
165		      int *nump);
166static int GetRops(int argi, int argc, char **argv, int *ropsp, int *nump);
167static int GetPops(int argi, int argc, char **argv, int *popsp, int *nump);
168static int GetFormats(int argi, int argc, char **argv, int *formatsp, int *nump);
169static int FormatFromName (char *name);
170static char *NameFromFormat (int format);
171
172
173/************************************************
174*	    time related stuff			*
175************************************************/
176
177#ifdef VMS
178
179typedef struct _vms_time {
180    unsigned long low;
181    unsigned long high;
182}vms_time;
183
184struct timeval {
185    long tv_sec;        /* seconds since Jan. 1, 1970 */
186    long tv_usec;  /* and microseconds */
187};
188
189struct timezone {
190    int  tz_minuteswest;     /* of Greenwich */
191    int  tz_dsttime;    /* type of dst correction to apply */
192};
193
194
195static int firsttime = True;
196static vms_time basetime;
197
198int gettimeofday(tp)
199    struct timeval *tp;
200{
201    vms_time current_time, resultant;
202    unsigned long mumble, foo;
203    int status;
204
205    if (firsttime) {
206        sys$gettim(&basetime);
207        firsttime = False;
208    }
209    sys$gettim(&current_time);
210    resultant.high = current_time.high - basetime.high;
211    resultant.low = current_time.low - basetime.low;
212    if (current_time.low < basetime.low) {
213        resultant.high -= 1;
214    }
215    status = lib$ediv( &(10000000), &resultant, &tp->tv_sec, &tp->tv_usec);
216    tp->tv_usec /= 10;
217    return 0;
218}
219
220#endif
221
222static struct  timeval start;
223
224static void
225PrintTime(void)
226{
227    Time_t t;
228
229    t = time((Time_t *)NULL);
230    printf("%s\n", ctime(&t));
231}
232
233static void
234InitTimes(void)
235{
236    X_GETTIMEOFDAY(&start);
237}
238
239static double
240ElapsedTime(double correction)
241{
242    struct timeval stop;
243
244    X_GETTIMEOFDAY(&stop);
245    if (stop.tv_usec < start.tv_usec) {
246        stop.tv_usec += 1000000;
247	stop.tv_sec -= 1;
248    }
249    return  (double)(stop.tv_usec - start.tv_usec) +
250            (1000000.0 * (double)(stop.tv_sec - start.tv_sec)) - correction;
251}
252
253static double
254RoundTo3Digits(double d)
255{
256    /* It's kind of silly to print out things like ``193658.4/sec'' so just
257       junk all but 3 most significant digits. */
258    double exponent, sign;
259
260    if (falsePrecision)
261        return d;
262
263    exponent = 1.0;
264    /* the code below won't work if d should happen to be non-positive. */
265    if (d < 0.0) {
266	d = -d;
267	sign = -1.0;
268    } else
269	sign = 1.0;
270    if (d >= 1000.0) {
271	do {
272	    exponent *= 10.0;
273	} while (d/exponent >= 1000.0);
274	d = (double)((int) (d/exponent + 0.5));
275	d *= exponent;
276    } else {
277	if (d != 0.0) {
278	    while (d*exponent < 100.0) {
279	        exponent *= 10.0;
280	    }
281	}
282	d = (double)((int) (d*exponent + 0.5));
283	d /= exponent;
284    }
285    return d * sign;
286}
287
288
289static void
290ReportTimes(double usecs, int64_t n, char *str, int average)
291{
292    double msecsperobj, objspersec;
293
294    if(usecs != 0.0)
295    {
296        msecsperobj = usecs / (1000.0 * (double)n);
297        objspersec = (double) n * 1000000.0 / usecs;
298
299        /* Round obj/sec to 3 significant digits.  Leave msec untouched, to
300	   allow averaging results from several repetitions. */
301        objspersec =  RoundTo3Digits(objspersec);
302
303        if (average) {
304	    printf("%11lld trep @ %8.4f msec (%8.1f/sec): %s\n",
305                   (long long) n, msecsperobj, objspersec, str);
306	} else {
307	    printf("%11lld reps @ %8.4f msec (%8.1f/sec): %s\n",
308                   (long long) n, msecsperobj, objspersec, str);
309	}
310    } else {
311	printf("%6lld %sreps @ 0.0 msec (unmeasurably fast): %s\n",
312               (long long) n, average ? "t" : "", str);
313    }
314
315}
316
317
318
319/************************************************
320*		Generic X stuff			*
321************************************************/
322
323static char *program_name;
324static void usage(void) _X_NORETURN;
325
326/*
327 * Get_Display_Name (argc, argv) Look for -display, -d, or host:dpy (obselete)
328 * If found, remove it from command line.  Don't go past a lone -.
329 */
330static char *
331Get_Display_Name(int *pargc, /* MODIFIED */
332		 char **argv) /* MODIFIED */
333{
334    int     argc = *pargc;
335    char    **pargv = argv+1;
336    char    *displayname = NULL;
337    int     i;
338
339    for (i = 1; i != argc; i++) {
340	char *arg = argv[i];
341
342	if (!strcmp (arg, "-display") || !strcmp (arg, "-d")) {
343	    if (++i >= argc) usage ();
344
345	    displayname = argv[i];
346	    *pargc -= 2;
347	    continue;
348	}
349	if (!strcmp(arg,"-")) {
350	    while (i<argc)  *pargv++ = argv[i++];
351	    break;
352	}
353	*pargv++ = arg;
354    }
355
356    *pargv = NULL;
357    return (displayname);
358}
359
360
361/*
362 * GetVersion (argc, argv) Look for -v1.2, -v1.3, or -v1.4.
363 * If found remove it from command line.  Don't go past a lone -.
364 */
365
366static Version
367GetVersion(int *pargc, /* MODIFIED */
368	   char **argv)  /* MODIFIED */
369{
370    int     argc = *pargc;
371    char    **pargv = argv+1;
372    Version version = VERSION1_6;
373    int     i;
374    Bool    found = False;
375
376    for (i = 1; i != argc; i++) {
377	char *arg = argv[i];
378
379	if (!strcmp (arg, "-v1.2")) {
380	    version = VERSION1_2;
381	    *pargc -= 1;
382	    if (found) {
383		fprintf(stderr, "Warning: multiple version specifications\n");
384	    }
385	    found = True;
386	    continue;
387	}
388	if (!strcmp (arg, "-v1.3")) {
389	    version = VERSION1_3;
390	    *pargc -= 1;
391	    if (found) {
392		fprintf(stderr, "Warning: multiple version specifications\n");
393	    }
394	    found = True;
395	    continue;
396	}
397	if (!strcmp (arg, "-v1.4")) {
398	    version = VERSION1_4;
399	    *pargc -= 1;
400	    if (found) {
401		fprintf(stderr, "Warning: multiple version specifications\n");
402	    }
403	    found = True;
404	    continue;
405	}
406	if (!strcmp (arg, "-v1.5")) {
407	    version = VERSION1_5;
408	    *pargc -= 1;
409	    if (found) {
410		fprintf(stderr, "Warning: multiple version specifications\n");
411	    }
412	    found = True;
413	    continue;
414	}
415	if (!strcmp(arg,"-")) {
416	    while (i<argc)  *pargv++ = argv[i++];
417	    break;
418	}
419	*pargv++ = arg;
420    }
421
422    *pargv = NULL;
423    return (version);
424}
425
426
427
428/*
429 * Open_Display: Routine to open a display with correct error handling.
430 */
431static Display *
432Open_Display(char *display_name)
433{
434    Display *d;
435
436    d = XOpenDisplay(display_name);
437    if (d == NULL) {
438	fprintf (stderr, "%s:  unable to open display '%s'\n",
439		 program_name, XDisplayName (display_name));
440	exit(1);
441    }
442
443    return(d);
444}
445
446static void
447Cleanup(int sig)
448{
449    abortTest = sig;
450}
451
452void
453AbortTest(void)
454{
455    fflush(stdout);
456
457    XSetScreenSaver(xparms.d, ssTimeout, ssInterval, ssPreferBlanking,
458	ssAllowExposures);
459    XFlush(xparms.d);
460    exit (abortTest);
461}
462
463/************************************************
464*		Performance stuff		*
465************************************************/
466
467
468static void
469usage(void)
470{
471    int     i = 0;
472    static const char *help_message =
473"where options include:\n"
474"    -display <host:display>   the X server to contact\n"
475"    -sync                     do the tests in synchronous mode\n"
476"    -pack                     pack rectangles right next to each other\n"
477"    -repeat <n>               do tests <n> times (default = 5)\n"
478"    -time <s>                 do tests for <s> seconds each (default = 5)\n"
479"    -pause <s>                pause for <s> seconds between each run\n"
480/*
481"    -draw                     draw after each test -- pmax only\n"
482*/
483"    -all                      do all tests\n"
484"    -range <test1>[,<test2>]  like all, but do <test1> to <test2>\n"
485"    -labels                   generate test labels for use by fillblnk\n"
486"    -fg                       the foreground color to use\n"
487"    -bg                       the background color to use\n"
488"    -clips <default>          default number of clip windows per test\n"
489"    -ddbg                     the background color to use for DoubleDash\n"
490"    -rop <rop0 rop1 ...>      use the given rops to draw (default = GXcopy)\n"
491"    -pm <pm0 pm1 ...>         use the given planemasks to draw (default = ~0)\n"
492"    -depth <depth>            use a visual with <depth> planes per pixel\n"
493"    -vclass <class>           the visual class to use (default = root)\n"
494"    -reps <n>                 fix the rep count (default = auto scale)\n"
495"    -subs <s0 s1 ...>         a list of the number of sub-windows to use\n"
496"    -v1.2                     perform only v1.2 tests using old semantics\n"
497"    -v1.3                     perform only v1.3 tests using old semantics\n"
498"    -su                       request save unders on windows\n"
499"    -bs <backing_store_hint>  WhenMapped or Always (default = NotUseful)\n"
500;
501
502    fflush(stdout);
503    fprintf(stderr, "usage: %s [-options ...]\n%s", program_name, help_message);
504    while (test[i].option != NULL) {
505	if (test[i].versions & xparms.version ) {
506	    fprintf(stderr, "    %-24s   %s\n",
507		test[i].option,
508		test[i].label14 ? test[i].label14 : test[i].label);
509	}
510        i++;
511    }
512    fprintf(stderr, "\n");
513
514    /* Print out original command line as the above usage message is so long */
515    for (i = 0; i != saveargc; i++) {
516	fprintf(stderr, "%s ", saveargv[i]);
517    }
518    fprintf(stderr, "\n\n");
519    exit (1);
520}
521
522void
523NullProc(XParms xp, Parms p)
524{
525}
526
527int
528NullInitProc(XParms xp, Parms p, int64_t reps)
529{
530    return reps;
531}
532
533static void
534HardwareSync(XParms xp)
535{
536    /*
537     * Some graphics hardware allows the server to claim it is done,
538     * while in reality the hardware is busily working away.  So fetch
539     * a pixel from the drawable that was drawn to, which should be
540     * enough to make the server wait for the graphics hardware.
541     */
542    XImage *image;
543
544    image = XGetImage(xp->d, xp->p ? xp->p : xp->w, HSx, HSy,
545		      1, 1, ~0, ZPixmap);
546    if (image) XDestroyImage(image);
547}
548
549static void
550DoHardwareSync(XParms xp, Parms p, int64_t reps)
551{
552    int i;
553
554    for (i = 0; i != reps; i++) {
555	HardwareSync(xp);
556	CheckAbort ();
557    }
558}
559
560static Test syncTest = {
561    "syncTime", "Internal test for finding how long HardwareSync takes", NULL,
562    NullInitProc, DoHardwareSync, NullProc, NullProc,
563    V1_2FEATURE, NONROP, 0,
564    {1}
565};
566
567
568static Window
569CreatePerfWindow(XParms xp, int x, int y, int width, int height)
570{
571    XSetWindowAttributes xswa;
572    Window w;
573/*
574    Screen *s;
575    int su;
576
577    s = DefaultScreenOfDisplay(xp->d);
578    su = XDoesBackingStore(s);
579    printf("Backing store of screen returns %d\n", su);
580    su = XDoesSaveUnders(s);
581    printf("Save unders of screen returns %d\n", su);
582    su = XPlanesOfScreen(s);
583    printf("Planes of screen returns %d\n", su);
584*/
585    xswa.background_pixel = xp->background;
586    xswa.border_pixel = xp->foreground;
587    xswa.colormap = cmap;
588    xswa.override_redirect = True;
589    xswa.backing_store = xp->backing_store;
590    xswa.save_under = xp->save_under;
591    w = XCreateWindow(xp->d, DefaultRootWindow(xp->d), x, y, width, height, 1,
592        xp->vinfo.depth, CopyFromParent, xp->vinfo.visual,
593	CWBackPixel | CWBorderPixel | CWColormap | CWOverrideRedirect
594	| CWBackingStore | CWSaveUnder, &xswa);
595    XMapWindow (xp->d, w);
596    return w;
597}
598
599
600static void
601CreateClipWindows(XParms xp, int clips)
602{
603    int j;
604    XWindowAttributes    xwa;
605
606    (void) XGetWindowAttributes(xp->d, xp->w, &xwa);
607    if (clips > MAXCLIP) clips = MAXCLIP;
608    for (j = 0; j != clips; j++) {
609	clipWindows[j] = CreatePerfWindow(xp,
610	    xwa.x + ws[j].x, xwa.y + ws[j].y, ws[j].width, ws[j].height);
611    }
612} /* CreateClipWindows */
613
614
615static void
616DestroyClipWindows(XParms xp, int clips)
617{
618    int j;
619
620    if (clips > MAXCLIP) clips = MAXCLIP;
621    for (j = 0; j != clips; j++) {
622	XDestroyWindow(xp->d, clipWindows[j]);
623    }
624} /* DestroyClipWindows */
625
626
627static double
628DoTest(XParms xp, Test *test, int64_t reps)
629{
630    double  time;
631    unsigned int ret_width, ret_height;
632
633    /* Tell screen-saver to restart counting again.  See comments below for the
634       XSetScreenSaver call. */
635    XForceScreenSaver(xp->d, ScreenSaverReset);
636    HardwareSync (xp);
637    InitTimes ();
638    (*test->proc) (xp, &test->parms, reps);
639    HardwareSync(xp);
640
641    time = ElapsedTime(syncTime);
642    if (time < 0.0) time = 0.0;
643    CheckAbort ();
644    if (drawToFakeServer)
645        XQueryBestSize(xp->d, TileShape, tileToQuery,
646		       32, 32, &ret_width, &ret_height);
647    (*test->passCleanup) (xp, &test->parms);
648    return time;
649}
650
651
652static int64_t
653CalibrateTest(XParms xp, Test *test, int seconds, double *usecperobj)
654{
655#define goal    2500000.0   /* Try to get up to 2.5 seconds		    */
656#define enough  2000000.0   /* But settle for 2.0 seconds		    */
657#define tick      10000.0   /* Assume clock not faster than .01 seconds     */
658
659    double  usecs;
660    int64_t reps, didreps;  /* Reps desired, reps performed		    */
661    int     exponent;
662
663    /* Attempt to get an idea how long each rep lasts by getting enough
664       reps to last more tan enough.  Then scale that up to the number of
665       seconds desired.
666
667       If init call to test ever fails, return False and test will be skipped.
668    */
669
670    if (fixedReps != 0) {
671	return fixedReps;
672    }
673    reps = 1;
674    for (;;) {
675	XDestroySubwindows(xp->d, xp->w);
676	XClearWindow(xp->d, xp->w);
677	didreps = (*test->init) (xp, &test->parms, reps);
678	CheckAbort ();
679	if (didreps == 0) {
680	    return 0;
681	}
682	if ( test->clips < clips )
683	  test->clips = clips ;
684	/* Create clip windows if requested */
685	CreateClipWindows(xp, test->clips);
686	HardwareSync(xp);
687	InitTimes();
688	(*test->proc) (xp, &test->parms, reps);
689	HardwareSync(xp);
690	usecs = ElapsedTime(syncTime);
691	(*test->passCleanup) (xp, &test->parms);
692	(*test->cleanup) (xp, &test->parms);
693	DestroyClipWindows(xp, test->clips);
694	CheckAbort ();
695
696	if (didreps != reps) {
697	    /* The test can't do the number of reps as we asked for.
698	       Give up */
699	    *usecperobj =
700		usecs / (double)(didreps * test->parms.objects);
701	    return didreps;
702	}
703	/* Did we go long enough? */
704	if (usecs >= enough) break;
705
706	/* Don't let too short a clock make new reps wildly high */
707	if (usecs <= tick)reps = reps*10;
708	else{
709	    /* Try to get up to goal seconds. */
710	    reps = (int) (goal * (double)reps / usecs) + 1;
711	}
712    }
713
714    *usecperobj = usecs / (double) (reps * test->parms.objects);
715    reps = (int) ((double)seconds * 1000000.0 * (double)reps / usecs) + 1;
716
717    /* Now round reps up to 1 digit accuracy, so we don't get stupid-looking
718       numbers of repetitions. */
719    reps--;
720    exponent = 1;
721    while (reps > 9) {
722	reps /= 10;
723	exponent *= 10;
724    }
725    reps = (reps + 1) * exponent;
726    return reps;
727} /* CalibrateTest */
728
729static void
730CreatePerfGCs(XParms xp, int func, unsigned long pm)
731{
732    XGCValues gcvfg, gcvbg, gcvddbg,gcvddfg;
733    unsigned long	fg, bg, ddbg;
734
735    fg = xp->foreground;
736    bg = xp->background;
737    ddbg = xp->ddbackground;
738    gcvfg.graphics_exposures = False;
739    gcvbg.graphics_exposures = False;
740    gcvddfg.graphics_exposures = False;
741    gcvddbg.graphics_exposures = False;
742    gcvfg.plane_mask = pm;
743    gcvbg.plane_mask = pm;
744    gcvddfg.plane_mask = pm;
745    gcvddbg.plane_mask = pm;
746    gcvfg.function = func;
747    gcvbg.function = func;
748    gcvddfg.function = func;
749    gcvddbg.function = func;
750
751    if (func == GXxor) {
752	/* Make test look good visually if possible */
753	gcvbg.foreground = gcvfg.foreground = bg ^ fg;
754	gcvbg.background = gcvfg.background = bg;
755	/* Double Dash GCs (This doesn't make a huge amount of sense) */
756	gcvddbg.foreground = gcvddfg.foreground = bg ^ fg;
757	gcvddbg.background = gcvddfg.foreground = bg ^ ddbg;
758    } else {
759	gcvfg.foreground = fg;
760	gcvfg.background = bg;
761	gcvbg.foreground = bg;
762	gcvbg.background = fg;
763	gcvddfg.foreground = fg;
764	gcvddfg.background = ddbg;
765	gcvddbg.foreground = ddbg;
766	gcvddbg.background = fg;
767    }
768    xp->fggc = XCreateGC(xp->d, xp->w,
769	GCForeground | GCBackground | GCGraphicsExposures
770      | GCFunction | GCPlaneMask, &gcvfg);
771    xp->bggc = XCreateGC(xp->d, xp->w,
772	GCForeground | GCBackground | GCGraphicsExposures
773      | GCFunction | GCPlaneMask, &gcvbg);
774    xp->ddfggc = XCreateGC(xp->d, xp->w,
775	GCForeground | GCBackground | GCGraphicsExposures
776      | GCFunction | GCPlaneMask, &gcvddfg);
777    xp->ddbggc = XCreateGC(xp->d, xp->w,
778	GCForeground | GCBackground | GCGraphicsExposures
779      | GCFunction | GCPlaneMask, &gcvddbg);
780}
781
782
783static void
784DestroyPerfGCs(XParms xp)
785{
786    XFreeGC(xp->d, xp->fggc);
787    XFreeGC(xp->d, xp->bggc);
788    XFreeGC(xp->d, xp->ddfggc);
789    XFreeGC(xp->d, xp->ddbggc);
790}
791
792static unsigned long
793AllocateColor(Display *display, const char *name, unsigned long pixel)
794{
795    XColor      color;
796
797    if (name != NULL) {
798	/* Try to parse color name */
799	if (XParseColor(display, cmap, name, &color)) {
800	    if (XAllocColor(display, cmap, &color)) {
801		pixel = color.pixel;
802	    } else {
803		(void) fprintf(stderr,
804		    "Can't allocate colormap entry for color %s\n", name);
805	    }
806	} else {
807	    if(*name >= '0' && *name <= '9')
808		pixel = atoi(name);
809	    else
810		(void) fprintf(stderr, "Can't parse color name %s\n", name);
811	}
812    }
813    return pixel;
814} /* AllocateColor */
815
816
817static void
818DisplayStatus(Display *d, const char *message, const char *test, int try)
819{
820    char    s[500];
821
822    XClearWindow(d, status);
823    sprintf(s, "%d %s %s", try, message, test);
824    /* We should really look at the height, descent of the font, etc. but
825       who cares.  This works. */
826    XDrawString(d, status, tgc, 10, 13, s, strlen(s));
827}
828
829
830static void
831ProcessTest(XParms xp, Test *test, int func, unsigned long pm, char *label)
832{
833    double  time, totalTime;
834    long long reps;
835    int     j;
836
837    xp->planemask = pm;
838    xp->func = func;
839    if (test->testType == COMP)
840    {
841	func = GXcopy;
842	pm = ~0L;
843    }
844    CreatePerfGCs(xp, func, pm);
845    DisplayStatus(xp->d, "Calibrating", label, 0);
846    reps = CalibrateTest(xp, test, seconds, &time);
847    if (reps != 0) {
848	XDestroySubwindows(xp->d, xp->w);
849	XClearWindow(xp->d, xp->w);
850	reps = (*test->init) (xp, &test->parms, reps);
851	if (abortTest)
852	    AbortTest ();
853	/*
854	 * if using fixedReps then will not have done CalibrateTest so must
855	 * check result of init for 0 here
856	 */
857	if(reps == 0){
858	    DestroyPerfGCs(xp);
859	    return;
860	}
861	/* Create clip windows if requested */
862	CreateClipWindows(xp, test->clips);
863
864	totalTime = 0.0;
865	for (j = 0; j != repeat; j++) {
866	    DisplayStatus(xp->d, "Testing", label, j+1);
867	    time = DoTest(xp, test, reps);
868	    if (abortTest)
869		AbortTest ();
870	    totalTime += time;
871	    ReportTimes (time, reps * test->parms.objects,
872		    label, False);
873            if (delay)
874                sleep(delay);
875	}
876	if (repeat > 1) {
877	    ReportTimes(totalTime,
878                        repeat * reps * test->parms.objects,
879                        label, True);
880	}
881	(*test->cleanup) (xp, &test->parms);
882	DestroyClipWindows(xp, test->clips);
883    } else {
884	/* Test failed to initialize properly */
885    }
886    printf ("\n");
887    fflush(stdout);
888    DestroyPerfGCs(xp);
889} /* ProcessTest */
890
891#define Strstr strstr
892
893#define LABELP(i) (test[i].label14 && (xparms.version >= VERSION1_4) \
894		        ? test[i].label14 : test[i].label)
895
896int
897main(int argc, char *argv[])
898{
899    int		i, j, n, skip;
900    int		numTests;       /* Even though the linker knows, we don't. */
901    char	hostname[100];
902    Bool	foundOne = False;
903    Bool	synchronous = False;
904    XGCValues	tgcv;
905    int		screen;
906    int		rop, pm;
907    int		pop, format;
908    int		window_y, window_x;
909    XVisualInfo *vinfolist, vinfotempl;
910    unsigned long vmask;
911
912    /* Save away argv, argc, for usage to print out */
913    saveargc = argc;
914    saveargv = (char **) malloc(argc * sizeof(char *));
915    for (i = 0; i != argc; i++) {
916	saveargv[i] = argv[i];
917    }
918
919    xparms.pack = False;
920    xparms.save_under = False;
921    xparms.backing_store = NotUseful;
922
923    /* Count number of tests */
924    ForEachTest(numTests);
925    doit = (Bool *)calloc(numTests, sizeof(Bool));
926
927    /* Parse arguments */
928    program_name = argv[0];
929    displayName = Get_Display_Name (&argc, argv);
930    xparms.version = GetVersion(&argc, argv);
931    for (i = 1; i != argc; i++) {
932	if (strcmp (argv[i], "-all") == 0) {
933	    ForEachTest (j)
934		doit[j] = test[j].versions & xparms.version;
935	    foundOne = True;
936	} else if (strcmp (argv[i], "-labels") == 0) {
937	    labels = True;
938	} else if (strcmp(argv[i], "-range") == 0) {
939	    char *cp1;
940	    char *cp2;
941
942	    if (argc <= ++i)
943		usage();
944	    cp1 = argv[i];
945	    if (*cp1 == '-')
946		cp1++;
947	    for (cp2 = cp1; *cp2 != '\0' && *cp2 != ','; cp2++) {};
948	    if (*cp2 == ',') {
949		*cp2++ = '\0';
950		if (*cp2 == '-')
951		    cp2++;
952	    } else {
953		cp2 = "-";
954	    }
955	    ForEachTest (j) {
956		if (strcmp (cp1, (test[j].option) + 1) == 0 &&
957		    (test[j].versions & xparms.version)) {
958		    int k = j;
959		    do {
960			doit[k] = test[j].versions & xparms.version;
961		    } while (!(strcmp(cp2, (test[k].option + 1)) == 0 &&
962			       (test[k].versions & xparms.version)) &&
963			     test[++k].option != NULL);
964		    if (*cp2 != '-' && test[k].option == NULL)
965			usage();
966		    break;
967		}
968	    }
969	    if (test[j].option == NULL)
970		usage();
971	    foundOne = True;
972	} else if (strcmp (argv[i], "-sync") == 0) {
973	    synchronous = True;
974	} else if (strcmp (argv[i], "-pack") == 0) {
975	    xparms.pack = True;
976	} else if (strcmp (argv[i], "-draw") == 0) {
977	    drawToFakeServer = True;
978        } else if (strcmp (argv[i], "-falseprecision") == 0) {
979            falsePrecision = True;
980	} else if (strcmp (argv[i], "-repeat") == 0) {
981	    i++;
982	    if (argc <= i)
983		usage ();
984	    repeat = atoi (argv[i]);
985	    if (repeat <= 0)
986	       usage ();
987	} else if (strcmp (argv[i], "-time") == 0) {
988	    i++;
989	    if (argc <= i)
990		usage ();
991	    seconds = atoi (argv[i]);
992	    if (seconds <= 0)
993	       usage ();
994        } else if (strcmp (argv[i], "-pause") == 0) {
995            ++i;
996	    if (argc <= i)
997		usage ();
998	    delay = atoi (argv[i]);
999	    if (delay < 0)
1000	       usage ();
1001	} else if (strcmp(argv[i], "-fg") == 0) {
1002	    i++;
1003	    if (argc <= i)
1004		usage ();
1005	    foreground = argv[i];
1006        } else if (strcmp(argv[i], "-bg") == 0) {
1007	    i++;
1008	    if (argc <= i)
1009		usage ();
1010	    background = argv[i];
1011	    if(ddbackground == NULL)
1012		ddbackground = argv[i];
1013	} else if (strcmp(argv[i], "-clips") == 0 ) {
1014	    i++;
1015	    if (argc <= i)
1016		usage ();
1017	    clips = atoi( argv[i] );
1018	} else if (strcmp(argv[i], "-ddbg") == 0) {
1019	    if (argc <= i)
1020		usage ();
1021	    i++;
1022	    ddbackground = argv[i];
1023	} else if (strcmp(argv[i], "-rop") == 0) {
1024	    skip = GetRops (i+1, argc, argv, rops, &numRops);
1025	    i += skip;
1026	} else if (strcmp(argv[i], "-pop") == 0) {
1027	    skip = GetPops (i+1, argc, argv, pops, &numPops);
1028	    i += skip;
1029	} else if (strcmp(argv[i], "-format") == 0) {
1030	    skip = GetFormats (i+1, argc, argv, formats, &numFormats);
1031	    i += skip;
1032	} else if (strcmp(argv[i], "-pm") == 0) {
1033	    skip = GetNumbers (i+1, argc, argv, planemasks, &numPlanemasks);
1034	    i += skip;
1035	} else if (strcmp(argv[i], "-xor") == 0) {
1036	    numRops = 1;
1037	    rops[0] = GXxor;
1038	} else if (strcmp (argv[i], "-both") == 0) {
1039	    numRops = 2;
1040	    rops[0] = GXcopy;
1041	    rops[1] = GXxor;
1042	} else if (strcmp(argv[i], "-reps") == 0) {
1043	    i++;
1044	    if (argc <= i)
1045		usage ();
1046	    fixedReps = atoi (argv[i]);
1047	    if (fixedReps <= 0)
1048		usage ();
1049        } else if (strcmp(argv[i], "-depth") == 0) {
1050	    i++;
1051	    if (argc <= i)
1052                usage ();
1053            depth = atoi(argv[i]);
1054            if (depth <= 0)
1055		usage ();
1056        } else if (strcmp(argv[i], "-vclass") == 0) {
1057	    i++;
1058	    if (argc <= i)
1059                usage ();
1060	    for (j = StaticGray; j <= DirectColor; j++) {
1061		if (strcmp(argv[i], visualClassNames[j]) == 0) {
1062		    vclass = j;
1063		    break;
1064		}
1065	    }
1066            if (vclass < 0)
1067		usage ();
1068	} else if (strcmp(argv[i], "-subs") == 0) {
1069	    skip = GetNumbers (i+1, argc, argv, subWindows, &numSubWindows);
1070	    i += skip;
1071	} else if (strcmp(argv[i], "-v1.2") == 0) {
1072	    xparms.version = VERSION1_2;
1073	} else if (strcmp(argv[i], "-v1.3") == 0) {
1074	    xparms.version = VERSION1_3;
1075	} else if (strcmp(argv[i], "-su") == 0) {
1076	    xparms.save_under = True;
1077	} else if (strcmp(argv[i], "-bs") == 0) {
1078	    i++;
1079	    if (argc <= i)
1080		usage ();
1081	    if (strcmp(argv[i], "WhenMapped") == 0) {
1082	      xparms.backing_store = WhenMapped;
1083	    } else if (strcmp(argv[i], "Always") == 0) {
1084	      xparms.backing_store = Always;
1085	    } else usage ();
1086	} else {
1087	    int len,found;
1088	    ForEachTest (j) {
1089		if (strcmp (argv[i], test[j].option) == 0 &&
1090		    (test[j].versions & xparms.version)) {
1091		    doit[j] = True;
1092		    goto LegalOption;
1093		}
1094	    }
1095	    found = False;
1096	    len = strlen(argv[i]);
1097	    if(len>=3)
1098	    ForEachTest (j) {
1099		if (Strstr (test[j].option, argv[i]+1) != NULL) {
1100		    fprintf(stderr,"	-> %s	%s\n", test[j].option, LABELP(j));
1101		    doit[j] = found = True;
1102		}
1103	    }
1104	    if(!found)
1105	    ForEachTest (j) {
1106		if (Strstr (LABELP(j), argv[i]+1) != NULL) {
1107		    fprintf(stderr,"	-> %s	%s\n", test[j].option, LABELP(j));
1108		    doit[j] = found = True;
1109		}
1110	    }
1111	    if(!found)
1112		usage ();
1113	LegalOption:
1114		foundOne = True;
1115	}
1116    }
1117
1118    if (labels) {
1119	/* Just print out list of tests for use with .sh programs that
1120	   assemble data from different x11perf runs into a nice format */
1121	ForEachTest (i) {
1122	    int child;
1123	    if (doit[i]) {
1124		switch (test[i].testType) {
1125		    case NONROP:
1126			printf ("%s\n", LABELP(i));
1127			break;
1128
1129		    case ROP:
1130			/* Run it through all specified rops and planemasks */
1131			for (rop = 0; rop < numRops; rop++) {
1132			    for (pm = 0; pm < numPlanemasks; pm++) {
1133				if (planemasks[pm] == ~0) {
1134				    if (rops[rop] == GXcopy) {
1135					printf ("%s\n", LABELP(i));
1136				    } else {
1137					printf ("(%s) %s\n",
1138					    ropNames[rops[rop]].name,
1139					    LABELP(i));
1140				    }
1141				} else {
1142				    printf ("(%s 0x%lx) %s\n",
1143					    ropNames[rops[rop]].name,
1144					    planemasks[pm],
1145					    LABELP(i));
1146				}
1147			    } /* for pm */
1148			} /* for rop */
1149			break;
1150
1151		    case PLANEMASK:
1152			/* Run it through all specified planemasks */
1153			for (pm = 0; pm < numPlanemasks; pm++) {
1154			    if (planemasks[pm] == ~0) {
1155				printf ("%s\n", LABELP(i));
1156			    } else {
1157				printf ("(0x%lx) %s\n",
1158					planemasks[pm],
1159					LABELP(i));
1160			    }
1161			} /* for pm */
1162			break;
1163
1164		    case WINDOW:
1165			for (child = 0; child != numSubWindows; child++) {
1166			    printf ("%s (%ld kids)\n",
1167				LABELP(i), subWindows[child]);
1168			}
1169			break;
1170		    case COMP:
1171			/* Run it through all specified pops */
1172			for (pop = 0; pop < numPops; pop++) {
1173			    if (pops[pop] == PictOpOver) {
1174				printf ("%s\n", LABELP(i));
1175			    } else {
1176				printf ("(%s) %s\n",
1177					popNames[pops[pop]].name,
1178					LABELP(i));
1179			    }
1180			} /* for pop */
1181			break;
1182		} /* switch */
1183	    }
1184	}
1185	exit(0);
1186    }
1187
1188    if (!foundOne)
1189	usage ();
1190    xparms.d = Open_Display (displayName);
1191    screen = DefaultScreen(xparms.d);
1192
1193    /* get visual info of default visual */
1194    vmask = VisualIDMask | VisualScreenMask;
1195    vinfotempl.visualid = XVisualIDFromVisual(XDefaultVisual(xparms.d, screen));
1196    vinfotempl.screen = screen;
1197    vinfolist = XGetVisualInfo(xparms.d, vmask, &vinfotempl, &n);
1198    if (!vinfolist || n != 1) {
1199	fprintf (stderr, "%s: can't get visual info of default visual\n",
1200	    program_name);
1201	exit(1);
1202    }
1203
1204    if (depth == -1 && vclass == -1) {
1205	/* use the default visual and colormap */
1206	xparms.vinfo = *vinfolist;
1207	cmap = XDefaultColormap(xparms.d, screen);
1208    } else {
1209	/* find the specified visual */
1210	int errorDepth = vinfolist[0].depth;
1211	int errorClass = vinfolist[0].class;
1212
1213	vmask = VisualScreenMask;
1214	vinfotempl.screen = screen;
1215	if (depth >= 0) {
1216	    vinfotempl.depth = depth;
1217	    vmask |= VisualDepthMask;
1218	    errorDepth = depth;
1219	}
1220	if (vclass >= 0) {
1221	    vinfotempl.class = vclass;
1222	    vmask |= VisualClassMask;
1223	    errorClass = vclass;
1224	}
1225	vinfolist = XGetVisualInfo(xparms.d, vmask, &vinfotempl, &n);
1226	if (!vinfolist) {
1227	    fprintf (stderr,
1228		"%s: can't find a visual of depth %d and class %s\n",
1229		program_name, errorDepth, visualClassNames[errorClass]);
1230	    exit(1);
1231	}
1232	xparms.vinfo = *vinfolist;  /* use the first one in list */
1233	if (xparms.vinfo.visualid ==
1234	    XVisualIDFromVisual(XDefaultVisual(xparms.d, screen))) {
1235	    /* matched visual is same as default visual */
1236	    cmap = XDefaultColormap(xparms.d, screen);
1237	} else {
1238	    cmap = XCreateColormap(xparms.d, DefaultRootWindow(xparms.d),
1239		xparms.vinfo.visual, AllocNone);
1240	    /* since this is not default cmap, must force color allocation */
1241	    if (!foreground) foreground = "Black";
1242	    if (!background) background = "White";
1243	    XInstallColormap(xparms.d, cmap);
1244	}
1245    }
1246    xparms.cmap = cmap;
1247
1248    printf("x11perf - X11 performance program, version %s\n",
1249	   xparms.version & VERSION1_5 ? "1.5" :
1250	   xparms.version & VERSION1_4 ? "1.4" :
1251	   xparms.version & VERSION1_3 ? "1.3" :
1252	   "1.2"
1253	   );
1254    XmuGetHostname(hostname, 100);
1255    printf ("%s server version %d on %s\nfrom %s\n",
1256	    ServerVendor (xparms.d), VendorRelease (xparms.d),
1257	    DisplayString (xparms.d), hostname);
1258    PrintTime ();
1259
1260    /* Force screen out of screen-saver mode, grab current data, and set
1261       time to blank to 8 hours.  We should just be able to turn the screen-
1262       saver off, but this causes problems on some servers.  We also reset
1263       the screen-saver timer each test, as 8 hours is about the maximum time
1264       we can use, and that isn't long enough for some X terminals using a
1265       serial protocol to finish all the tests.  As long as the tests run to
1266       completion, the old screen-saver values are restored. */
1267    XForceScreenSaver(xparms.d, ScreenSaverReset);
1268    XGetScreenSaver(xparms.d, &ssTimeout, &ssInterval, &ssPreferBlanking,
1269	&ssAllowExposures);
1270    (void) signal(SIGINT, Cleanup); /* ^C */
1271#ifdef SIGQUIT
1272    (void) signal(SIGQUIT, Cleanup);
1273#endif
1274    (void) signal(SIGTERM, Cleanup);
1275#ifdef SIGHUP
1276    (void) signal(SIGHUP, Cleanup);
1277#endif
1278    XSetScreenSaver(xparms.d, 8 * 3600, ssInterval, ssPreferBlanking,
1279	ssAllowExposures);
1280
1281    if (drawToFakeServer) {
1282        tileToQuery =
1283	    XCreatePixmap(xparms.d, DefaultRootWindow (xparms.d), 32, 32, 1);
1284    }
1285
1286
1287    xparms.foreground =
1288	AllocateColor(xparms.d, foreground, BlackPixel(xparms.d, screen));
1289    xparms.background =
1290	AllocateColor(xparms.d, background, WhitePixel(xparms.d, screen));
1291    xparms.ddbackground =
1292	AllocateColor(xparms.d, ddbackground, WhitePixel(xparms.d, screen));
1293    window_x = 2;
1294    if (DisplayWidth(xparms.d, screen) < WIDTH + window_x + 1)
1295	window_x = -1;
1296    window_y = 2;
1297    if (DisplayHeight(xparms.d, screen) < HEIGHT + window_y + 1)
1298	window_y = -1;
1299    xparms.w = CreatePerfWindow(&xparms, window_x, window_y, WIDTH, HEIGHT);
1300    HSx = WIDTH-1;
1301    if (window_x + 1 + WIDTH > DisplayWidth(xparms.d, screen))
1302	HSx = DisplayWidth(xparms.d, screen) - (1 + window_x + 1);
1303    HSy = HEIGHT-1;
1304    if (window_y + 1 + HEIGHT > DisplayHeight(xparms.d, screen))
1305	HSy = DisplayHeight(xparms.d, screen) - (1 + window_y + 1);
1306    status = CreatePerfWindow(&xparms, window_x, HEIGHT+5, WIDTH, 20);
1307    tgcv.foreground =
1308	AllocateColor(xparms.d, "black", BlackPixel(xparms.d, screen));
1309    tgcv.background =
1310	AllocateColor(xparms.d, "white", WhitePixel(xparms.d, screen));
1311    tgc = XCreateGC(xparms.d, status, GCForeground | GCBackground, &tgcv);
1312
1313    xparms.p = (Pixmap)0;
1314
1315    if (synchronous)
1316	XSynchronize (xparms.d, True);
1317
1318    /* Get mouse pointer out of the way of the performance window.  On
1319       software cursor machines it will slow graphics performance.  On
1320       all current MIT-derived servers it will slow window
1321       creation/configuration performance. */
1322    XWarpPointer(xparms.d, None, status, 0, 0, 0, 0, WIDTH+32, 20+32);
1323
1324    /* Figure out how long to call HardwareSync, so we can adjust for that
1325       in our total elapsed time */
1326    (void) CalibrateTest(&xparms, &syncTest, 1, &syncTime);
1327    printf("Sync time adjustment is %6.4f msecs.\n\n", syncTime/1000);
1328
1329    ForEachTest (i) {
1330	int child;
1331	char label[200];
1332
1333	if (doit[i] && (test[i].versions & xparms.version)) {
1334	    switch (test[i].testType) {
1335	        case NONROP:
1336		    /* Simplest...just run it once */
1337		    strcpy (label, LABELP(i));
1338		    ProcessTest(&xparms, &test[i], GXcopy, ~0L, label);
1339		    break;
1340
1341		case ROP:
1342		    /* Run it through all specified rops and planemasks */
1343		    for (rop = 0; rop < numRops; rop++) {
1344			for (pm = 0; pm < numPlanemasks; pm++) {
1345			    if (planemasks[pm] == ~0) {
1346				if (rops[rop] == GXcopy) {
1347				    sprintf (label, "%s", LABELP(i));
1348				} else {
1349				    sprintf (label, "(%s) %s",
1350					ropNames[rops[rop]].name,
1351					LABELP(i));
1352				}
1353			    } else {
1354				sprintf (label, "(%s 0x%lx) %s",
1355					ropNames[rops[rop]].name,
1356					planemasks[pm],
1357					LABELP(i));
1358			    }
1359			    ProcessTest(&xparms, &test[i], rops[rop],
1360				        planemasks[pm], label);
1361			} /* for pm */
1362		    } /* for rop */
1363		    break;
1364
1365		case PLANEMASK:
1366		    /* Run it through all specified planemasks */
1367		    for (pm = 0; pm < numPlanemasks; pm++) {
1368			if (planemasks[pm] == ~0) {
1369			    sprintf (label, "%s", LABELP(i));
1370			} else {
1371			    sprintf (label, "(0x%lx) %s",
1372				    planemasks[pm],
1373				    LABELP(i));
1374			}
1375			ProcessTest(&xparms, &test[i], GXcopy,
1376				    planemasks[pm], label);
1377		    } /* for pm */
1378		    break;
1379
1380		case WINDOW:
1381		    /* Loop through number of children array */
1382		    for (child = 0; child != numSubWindows; child++) {
1383			test[i].parms.objects = subWindows[child];
1384			sprintf(label, "%s (%d kids)",
1385			    LABELP(i), test[i].parms.objects);
1386			ProcessTest(&xparms, &test[i], GXcopy, ~0L, label);
1387		    }
1388		    break;
1389	        case COMP:
1390		    /* Loop through the composite operands */
1391		    for (pop = 0; pop < numPops; pop++) {
1392			for (format = 0; format < numFormats; format++) {
1393			    if (formats[format] == PictStandardNative) {
1394				if (pops[pop] == PictOpOver) {
1395				    sprintf (label, "%s", LABELP(i));
1396				} else {
1397				    sprintf (label, "(%s) %s",
1398					     popNames[pops[pop]].name,
1399					     LABELP(i));
1400				}
1401			    } else {
1402				char *name = NameFromFormat (formats[format]);
1403				sprintf (label, "(%s %s) %s",
1404					 popNames[pops[pop]].name,
1405					 name,
1406					 LABELP(i));
1407			    }
1408			    ProcessTest (&xparms, &test[i], pops[pop], formats[format], label);
1409			}
1410		    }
1411		    break;
1412	    } /* switch */
1413	} /* if doit */
1414    } /* ForEachTest */
1415
1416    XFreeGC(xparms.d, tgc);
1417    XDestroyWindow(xparms.d, xparms.w);
1418    XFree(vinfolist);
1419    if (drawToFakeServer)
1420      XFreePixmap(xparms.d, tileToQuery);
1421    /* Restore ScreenSaver to original state. */
1422    XSetScreenSaver(xparms.d, ssTimeout, ssInterval, ssPreferBlanking,
1423	ssAllowExposures);
1424    XCloseDisplay(xparms.d);
1425    free(saveargv);
1426    free(doit);
1427    exit(0);
1428}
1429
1430static int
1431GetWords (int argi, int argc, char **argv, char **wordsp, int *nump)
1432{
1433    int	    count;
1434
1435    if (argc <= argi)
1436	usage();
1437    count = 0;
1438    while (argv[argi] && *(argv[argi]) != '-') {
1439	*wordsp++ = argv[argi];
1440	++argi;
1441	count++;
1442    }
1443    *nump = count;
1444    return count;
1445}
1446
1447static long
1448atox (char *s)
1449{
1450    long   v, c = 0;
1451
1452    v = 0;
1453    while (*s) {
1454	if ('0' <= *s && *s <= '9')
1455	    c = *s - '0';
1456	else if ('a' <= *s && *s <= 'f')
1457	    c = *s - 'a' + 10;
1458	else if ('A' <= *s && *s <= 'F')
1459	    c = *s - 'A' + 10;
1460	v = v * 16 + c;
1461	s++;
1462    }
1463    return v;
1464}
1465
1466static int
1467GetNumbers (int argi, int argc, char **argv, unsigned long *intsp, int *nump)
1468{
1469    char    *words[256];
1470    int	    count;
1471    int	    i;
1472    int	    flip;
1473
1474    count = GetWords (argi, argc, argv, words, nump);
1475    for (i = 0; i < count; i++) {
1476	flip = 0;
1477	if (!strncmp (words[i], "~", 1)) {
1478	    words[i]++;
1479	    flip = ~0;
1480	}
1481	if (!strncmp (words[i], "0x", 2))
1482	    intsp[i] = atox(words[i] + 2) ^ flip;
1483	else
1484	    intsp[i] = atoi (words[i]) ^ flip;
1485    }
1486    return count;
1487}
1488
1489static int
1490GetRops (int argi, int argc, char **argv, int *ropsp, int *nump)
1491{
1492    char    *words[256];
1493    int	    count;
1494    int	    i;
1495    int	    rop;
1496
1497    count = GetWords (argi, argc, argv, words, nump);
1498    for (i = 0; i < count; i++) {
1499	if (!strncmp (words[i], "GX", 2))
1500	    words[i] += 2;
1501	if (!strcmp (words[i], "all")) {
1502	    for (i = 0; i < NUM_ROPS; i++)
1503		ropsp[i] = ropNames[i].rop;
1504	    *nump = NUM_ROPS;
1505	    break;
1506	}
1507	for (rop = 0; rop < NUM_ROPS; rop++) {
1508	    if (!strcmp (words[i], ropNames[rop].name)) {
1509		ropsp[i] = ropNames[rop].rop;
1510		break;
1511	    }
1512	}
1513	if (rop == NUM_ROPS) {
1514	    usage ();
1515	    fprintf (stderr, "unknown rop name %s\n", words[i]);
1516	}
1517    }
1518    return count;
1519}
1520
1521static int
1522GetPops (int argi, int argc, char **argv, int *popsp, int *nump)
1523{
1524    char    *words[256];
1525    int	    count;
1526    int	    i;
1527    int	    pop;
1528
1529    count = GetWords (argi, argc, argv, words, nump);
1530    for (i = 0; i < count; i++) {
1531	if (!strncmp (words[i], "PictOp", 6))
1532	    words[i] += 6;
1533	if (!strcmp (words[i], "all")) {
1534	    for (i = 0; i < NUM_POPS; i++)
1535		popsp[i] = popNames[i].rop;
1536	    *nump = NUM_POPS;
1537	    break;
1538	}
1539	for (pop = 0; pop < NUM_POPS; pop++) {
1540	    if (!strcmp (words[i], popNames[pop].name)) {
1541		popsp[i] = popNames[pop].rop;
1542		break;
1543	    }
1544	}
1545	if (pop == NUM_POPS) {
1546	    usage ();
1547	    fprintf (stderr, "unknown picture op name %s\n", words[i]);
1548	}
1549    }
1550    return count;
1551}
1552
1553static int
1554FormatFromName (char *name)
1555{
1556    int i;
1557    for (i = 0; i < NUM_FORMATS; i++)
1558	if (!strcmp (name, formatNames[i].name))
1559	    return formatNames[i].rop;
1560    return -1;
1561}
1562
1563static char *
1564NameFromFormat (int format)
1565{
1566    int i;
1567    for (i = 0; i < NUM_FORMATS; i++)
1568	if (formatNames[i].rop == format)
1569	    return formatNames[i].name;
1570    return NULL;
1571}
1572
1573static int
1574GetFormats (int argi, int argc, char **argv, int *formatsp, int *nump)
1575{
1576    char    *words[256];
1577    int	    count;
1578    int	    i;
1579    int	    format;
1580
1581    count = GetWords (argi, argc, argv, words, nump);
1582    for (i = 0; i < count; i++) {
1583	if (!strcmp (words[i], "all")) {
1584	    for (i = 0; i < NUM_FORMATS; i++)
1585		formatsp[i] = formatNames[i].rop;
1586	    *nump = NUM_FORMATS;
1587	    break;
1588	}
1589	format = FormatFromName (words[i]);
1590	if (format < 0) {
1591	    usage ();
1592	    fprintf (stderr, "unknown format name %s\n", words[i]);
1593	}
1594	formatsp[i] = format;
1595    }
1596    return count;
1597}
1598