x11perf.c revision 6f13e4f1
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 const 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    if(usecs != 0.0)
293    {
294        double msecsperobj = usecs / (1000.0 * (double)n);
295        double objspersec = (double) n * 1000000.0 / usecs;
296
297        /* Round obj/sec to 3 significant digits.  Leave msec untouched, to
298	   allow averaging results from several repetitions. */
299        objspersec =  RoundTo3Digits(objspersec);
300
301        if (average) {
302	    printf("%11lld trep @ %8.4f msec (%8.1f/sec): %s\n",
303                   (long long) n, msecsperobj, objspersec, str);
304	} else {
305	    printf("%11lld reps @ %8.4f msec (%8.1f/sec): %s\n",
306                   (long long) n, msecsperobj, objspersec, str);
307	}
308    } else {
309	printf("%6lld %sreps @ 0.0 msec (unmeasurably fast): %s\n",
310               (long long) n, average ? "t" : "", str);
311    }
312
313}
314
315
316
317/************************************************
318*		Generic X stuff			*
319************************************************/
320
321static char *program_name;
322static void usage(void) _X_NORETURN;
323
324/*
325 * Get_Display_Name (argc, argv) Look for -display, -d, or host:dpy (obsolete)
326 * If found, remove it from command line.  Don't go past a lone -.
327 */
328static char *
329Get_Display_Name(int *pargc, /* MODIFIED */
330		 char **argv) /* MODIFIED */
331{
332    int     argc = *pargc;
333    char    **pargv = argv+1;
334    char    *displayname = NULL;
335
336    for (int i = 1; i != argc; i++) {
337	char *arg = argv[i];
338
339	if (!strcmp (arg, "-display") || !strcmp (arg, "-d")) {
340	    if (++i >= argc) usage ();
341
342	    displayname = argv[i];
343	    *pargc -= 2;
344	    continue;
345	}
346	if (!strcmp(arg,"-")) {
347	    while (i<argc)  *pargv++ = argv[i++];
348	    break;
349	}
350	*pargv++ = arg;
351    }
352
353    *pargv = NULL;
354    return (displayname);
355}
356
357
358/*
359 * GetVersion (argc, argv) Look for -v1.2, -v1.3, or -v1.4.
360 * If found remove it from command line.  Don't go past a lone -.
361 */
362
363static Version
364GetVersion(int *pargc, /* MODIFIED */
365	   char **argv)  /* MODIFIED */
366{
367    int     argc = *pargc;
368    char    **pargv = argv+1;
369    Version version = VERSION1_6;
370    Bool    found = False;
371
372    for (int i = 1; i != argc; i++) {
373	char *arg = argv[i];
374
375	if (!strcmp (arg, "-v1.2")) {
376	    version = VERSION1_2;
377	    *pargc -= 1;
378	    if (found) {
379		fprintf(stderr, "Warning: multiple version specifications\n");
380	    }
381	    found = True;
382	    continue;
383	}
384	if (!strcmp (arg, "-v1.3")) {
385	    version = VERSION1_3;
386	    *pargc -= 1;
387	    if (found) {
388		fprintf(stderr, "Warning: multiple version specifications\n");
389	    }
390	    found = True;
391	    continue;
392	}
393	if (!strcmp (arg, "-v1.4")) {
394	    version = VERSION1_4;
395	    *pargc -= 1;
396	    if (found) {
397		fprintf(stderr, "Warning: multiple version specifications\n");
398	    }
399	    found = True;
400	    continue;
401	}
402	if (!strcmp (arg, "-v1.5")) {
403	    version = VERSION1_5;
404	    *pargc -= 1;
405	    if (found) {
406		fprintf(stderr, "Warning: multiple version specifications\n");
407	    }
408	    found = True;
409	    continue;
410	}
411	if (!strcmp(arg,"-")) {
412	    while (i<argc)  *pargv++ = argv[i++];
413	    break;
414	}
415	*pargv++ = arg;
416    }
417
418    *pargv = NULL;
419    return (version);
420}
421
422
423
424/*
425 * Open_Display: Routine to open a display with correct error handling.
426 */
427static Display *
428Open_Display(char *display_name)
429{
430    Display *d;
431
432    d = XOpenDisplay(display_name);
433    if (d == NULL) {
434	fprintf (stderr, "%s:  unable to open display '%s'\n",
435		 program_name, XDisplayName (display_name));
436	exit(1);
437    }
438
439    return(d);
440}
441
442static void
443Cleanup(int sig)
444{
445    abortTest = sig;
446}
447
448void
449AbortTest(void)
450{
451    fflush(stdout);
452
453    XSetScreenSaver(xparms.d, ssTimeout, ssInterval, ssPreferBlanking,
454	ssAllowExposures);
455    XFlush(xparms.d);
456    exit (abortTest);
457}
458
459/************************************************
460*		Performance stuff		*
461************************************************/
462
463
464static void
465usage(void)
466{
467    int     i = 0;
468    static const char *help_message =
469"where options include:\n"
470"    -display <host:display>   the X server to contact\n"
471"    -sync                     do the tests in synchronous mode\n"
472"    -pack                     pack rectangles right next to each other\n"
473"    -repeat <n>               do tests <n> times (default = 5)\n"
474"    -time <s>                 do tests for <s> seconds each (default = 5)\n"
475"    -pause <s>                pause for <s> seconds between each run\n"
476/*
477"    -draw                     draw after each test -- pmax only\n"
478*/
479"    -all                      do all tests\n"
480"    -range <test1>[,<test2>]  like all, but do <test1> to <test2>\n"
481"    -labels                   generate test labels for use by fillblnk\n"
482"    -fg                       the foreground color to use\n"
483"    -bg                       the background color to use\n"
484"    -clips <default>          default number of clip windows per test\n"
485"    -ddbg                     the background color to use for DoubleDash\n"
486"    -rop <rop0 rop1 ...>      use the given rops to draw (default = GXcopy)\n"
487"    -pm <pm0 pm1 ...>         use the given planemasks to draw (default = ~0)\n"
488"    -depth <depth>            use a visual with <depth> planes per pixel\n"
489"    -vclass <class>           the visual class to use (default = root)\n"
490"    -reps <n>                 fix the rep count (default = auto scale)\n"
491"    -subs <s0 s1 ...>         a list of the number of sub-windows to use\n"
492"    -v1.2                     perform only v1.2 tests using old semantics\n"
493"    -v1.3                     perform only v1.3 tests using old semantics\n"
494"    -su                       request save unders on windows\n"
495"    -bs <backing_store_hint>  WhenMapped or Always (default = NotUseful)\n"
496;
497
498    fflush(stdout);
499    fprintf(stderr, "usage: %s [-options ...]\n%s", program_name, help_message);
500    while (test[i].option != NULL) {
501	if (test[i].versions & xparms.version ) {
502	    fprintf(stderr, "    %-24s   %s\n",
503		test[i].option,
504		test[i].label14 ? test[i].label14 : test[i].label);
505	}
506        i++;
507    }
508    fprintf(stderr, "\n");
509
510    /* Print out original command line as the above usage message is so long */
511    for (i = 0; i != saveargc; i++) {
512	fprintf(stderr, "%s ", saveargv[i]);
513    }
514    fprintf(stderr, "\n\n");
515    exit (1);
516}
517
518void
519NullProc(XParms xp, Parms p)
520{
521}
522
523int
524NullInitProc(XParms xp, Parms p, int64_t reps)
525{
526    return reps;
527}
528
529static void
530HardwareSync(XParms xp)
531{
532    /*
533     * Some graphics hardware allows the server to claim it is done,
534     * while in reality the hardware is busily working away.  So fetch
535     * a pixel from the drawable that was drawn to, which should be
536     * enough to make the server wait for the graphics hardware.
537     */
538    XImage *image;
539
540    image = XGetImage(xp->d, xp->p ? xp->p : xp->w, HSx, HSy,
541		      1, 1, ~0, ZPixmap);
542    if (image) XDestroyImage(image);
543}
544
545static void
546DoHardwareSync(XParms xp, Parms p, int64_t reps)
547{
548    for (int i = 0; i != reps; i++) {
549	HardwareSync(xp);
550	CheckAbort ();
551    }
552}
553
554static Test syncTest = {
555    "syncTime", "Internal test for finding how long HardwareSync takes", NULL,
556    NullInitProc, DoHardwareSync, NullProc, NullProc,
557    V1_2FEATURE, NONROP, 0,
558    {1}
559};
560
561
562static Window
563CreatePerfWindow(XParms xp, int x, int y, int width, int height)
564{
565    XSetWindowAttributes xswa;
566    Window w;
567/*
568    Screen *s;
569    int su;
570
571    s = DefaultScreenOfDisplay(xp->d);
572    su = XDoesBackingStore(s);
573    printf("Backing store of screen returns %d\n", su);
574    su = XDoesSaveUnders(s);
575    printf("Save unders of screen returns %d\n", su);
576    su = XPlanesOfScreen(s);
577    printf("Planes of screen returns %d\n", su);
578*/
579    xswa.background_pixel = xp->background;
580    xswa.border_pixel = xp->foreground;
581    xswa.colormap = cmap;
582    xswa.override_redirect = True;
583    xswa.backing_store = xp->backing_store;
584    xswa.save_under = xp->save_under;
585    w = XCreateWindow(xp->d, DefaultRootWindow(xp->d), x, y, width, height, 1,
586        xp->vinfo.depth, CopyFromParent, xp->vinfo.visual,
587	CWBackPixel | CWBorderPixel | CWColormap | CWOverrideRedirect
588	| CWBackingStore | CWSaveUnder, &xswa);
589    XMapWindow (xp->d, w);
590    return w;
591}
592
593
594static void
595CreateClipWindows(XParms xp, int clips)
596{
597    XWindowAttributes    xwa;
598
599    (void) XGetWindowAttributes(xp->d, xp->w, &xwa);
600    if (clips > MAXCLIP) clips = MAXCLIP;
601    for (int j = 0; j != clips; j++) {
602	clipWindows[j] = CreatePerfWindow(xp,
603	    xwa.x + ws[j].x, xwa.y + ws[j].y, ws[j].width, ws[j].height);
604    }
605} /* CreateClipWindows */
606
607
608static void
609DestroyClipWindows(XParms xp, int clips)
610{
611    if (clips > MAXCLIP) clips = MAXCLIP;
612    for (int j = 0; j != clips; j++) {
613	XDestroyWindow(xp->d, clipWindows[j]);
614    }
615} /* DestroyClipWindows */
616
617
618static double
619DoTest(XParms xp, Test *test, int64_t reps)
620{
621    double  time;
622    unsigned int ret_width, ret_height;
623
624    /* Tell screen-saver to restart counting again.  See comments below for the
625       XSetScreenSaver call. */
626    XForceScreenSaver(xp->d, ScreenSaverReset);
627    HardwareSync (xp);
628    InitTimes ();
629    (*test->proc) (xp, &test->parms, reps);
630    HardwareSync(xp);
631
632    time = ElapsedTime(syncTime);
633    if (time < 0.0) time = 0.0;
634    CheckAbort ();
635    if (drawToFakeServer)
636        XQueryBestSize(xp->d, TileShape, tileToQuery,
637		       32, 32, &ret_width, &ret_height);
638    (*test->passCleanup) (xp, &test->parms);
639    return time;
640}
641
642
643static int64_t
644CalibrateTest(XParms xp, Test *test, int seconds, double *usecperobj)
645{
646#define goal    2500000.0   /* Try to get up to 2.5 seconds		    */
647#define enough  2000000.0   /* But settle for 2.0 seconds		    */
648#define tick      10000.0   /* Assume clock not faster than .01 seconds     */
649
650    double  usecs;
651    int64_t reps, didreps;  /* Reps desired, reps performed		    */
652    int     exponent;
653
654    /* Attempt to get an idea how long each rep lasts by getting enough
655       reps to last more tan enough.  Then scale that up to the number of
656       seconds desired.
657
658       If init call to test ever fails, return False and test will be skipped.
659    */
660
661    if (fixedReps != 0) {
662	return fixedReps;
663    }
664    reps = 1;
665    for (;;) {
666	XDestroySubwindows(xp->d, xp->w);
667	XClearWindow(xp->d, xp->w);
668	didreps = (*test->init) (xp, &test->parms, reps);
669	CheckAbort ();
670	if (didreps == 0) {
671	    return 0;
672	}
673	if ( test->clips < clips )
674	  test->clips = clips ;
675	/* Create clip windows if requested */
676	CreateClipWindows(xp, test->clips);
677	HardwareSync(xp);
678	InitTimes();
679	(*test->proc) (xp, &test->parms, reps);
680	HardwareSync(xp);
681	usecs = ElapsedTime(syncTime);
682	(*test->passCleanup) (xp, &test->parms);
683	(*test->cleanup) (xp, &test->parms);
684	DestroyClipWindows(xp, test->clips);
685	CheckAbort ();
686
687	if (didreps != reps) {
688	    /* The test can't do the number of reps as we asked for.
689	       Give up */
690	    *usecperobj =
691		usecs / (double)(didreps * test->parms.objects);
692	    return didreps;
693	}
694	/* Did we go long enough? */
695	if (usecs >= enough) break;
696
697	/* Don't let too short a clock make new reps wildly high */
698	if (usecs <= tick)reps = reps*10;
699	else{
700	    /* Try to get up to goal seconds. */
701	    reps = (int) (goal * (double)reps / usecs) + 1;
702	}
703    }
704
705    *usecperobj = usecs / (double) (reps * test->parms.objects);
706    reps = (int) ((double)seconds * 1000000.0 * (double)reps / usecs) + 1;
707
708    /* Now round reps up to 1 digit accuracy, so we don't get stupid-looking
709       numbers of repetitions. */
710    reps--;
711    exponent = 1;
712    while (reps > 9) {
713	reps /= 10;
714	exponent *= 10;
715    }
716    reps = (reps + 1) * exponent;
717    return reps;
718} /* CalibrateTest */
719
720static void
721CreatePerfGCs(XParms xp, int func, unsigned long pm)
722{
723    XGCValues gcvfg, gcvbg, gcvddbg,gcvddfg;
724    unsigned long	fg, bg, ddbg;
725
726    fg = xp->foreground;
727    bg = xp->background;
728    ddbg = xp->ddbackground;
729    gcvfg.graphics_exposures = False;
730    gcvbg.graphics_exposures = False;
731    gcvddfg.graphics_exposures = False;
732    gcvddbg.graphics_exposures = False;
733    gcvfg.plane_mask = pm;
734    gcvbg.plane_mask = pm;
735    gcvddfg.plane_mask = pm;
736    gcvddbg.plane_mask = pm;
737    gcvfg.function = func;
738    gcvbg.function = func;
739    gcvddfg.function = func;
740    gcvddbg.function = func;
741
742    if (func == GXxor) {
743	/* Make test look good visually if possible */
744	gcvbg.foreground = gcvfg.foreground = bg ^ fg;
745	gcvbg.background = gcvfg.background = bg;
746	/* Double Dash GCs (This doesn't make a huge amount of sense) */
747	gcvddbg.foreground = gcvddfg.foreground = bg ^ fg;
748	gcvddbg.background = gcvddfg.foreground = bg ^ ddbg;
749    } else {
750	gcvfg.foreground = fg;
751	gcvfg.background = bg;
752	gcvbg.foreground = bg;
753	gcvbg.background = fg;
754	gcvddfg.foreground = fg;
755	gcvddfg.background = ddbg;
756	gcvddbg.foreground = ddbg;
757	gcvddbg.background = fg;
758    }
759    xp->fggc = XCreateGC(xp->d, xp->w,
760	GCForeground | GCBackground | GCGraphicsExposures
761      | GCFunction | GCPlaneMask, &gcvfg);
762    xp->bggc = XCreateGC(xp->d, xp->w,
763	GCForeground | GCBackground | GCGraphicsExposures
764      | GCFunction | GCPlaneMask, &gcvbg);
765    xp->ddfggc = XCreateGC(xp->d, xp->w,
766	GCForeground | GCBackground | GCGraphicsExposures
767      | GCFunction | GCPlaneMask, &gcvddfg);
768    xp->ddbggc = XCreateGC(xp->d, xp->w,
769	GCForeground | GCBackground | GCGraphicsExposures
770      | GCFunction | GCPlaneMask, &gcvddbg);
771}
772
773
774static void
775DestroyPerfGCs(XParms xp)
776{
777    XFreeGC(xp->d, xp->fggc);
778    XFreeGC(xp->d, xp->bggc);
779    XFreeGC(xp->d, xp->ddfggc);
780    XFreeGC(xp->d, xp->ddbggc);
781}
782
783static unsigned long
784AllocateColor(Display *display, const char *name, unsigned long pixel)
785{
786    XColor      color;
787
788    if (name != NULL) {
789	/* Try to parse color name */
790	if (XParseColor(display, cmap, name, &color)) {
791	    if (XAllocColor(display, cmap, &color)) {
792		pixel = color.pixel;
793	    } else {
794		(void) fprintf(stderr,
795		    "Can't allocate colormap entry for color %s\n", name);
796	    }
797	} else {
798	    if(*name >= '0' && *name <= '9')
799		pixel = atoi(name);
800	    else
801		(void) fprintf(stderr, "Can't parse color name %s\n", name);
802	}
803    }
804    return pixel;
805} /* AllocateColor */
806
807
808static void
809DisplayStatus(Display *d, const char *message, const char *test, int try)
810{
811    char    s[500];
812
813    XClearWindow(d, status);
814    sprintf(s, "%d %s %s", try, message, test);
815    /* We should really look at the height, descent of the font, etc. but
816       who cares.  This works. */
817    XDrawString(d, status, tgc, 10, 13, s, strlen(s));
818}
819
820
821static void
822ProcessTest(XParms xp, Test *test, int func, unsigned long pm, char *label)
823{
824    double  time, totalTime;
825    long long reps;
826
827    xp->planemask = pm;
828    xp->func = func;
829    if (test->testType == COMP)
830    {
831	func = GXcopy;
832	pm = ~0L;
833    }
834    CreatePerfGCs(xp, func, pm);
835    DisplayStatus(xp->d, "Calibrating", label, 0);
836    reps = CalibrateTest(xp, test, seconds, &time);
837    if (reps != 0) {
838	XDestroySubwindows(xp->d, xp->w);
839	XClearWindow(xp->d, xp->w);
840	reps = (*test->init) (xp, &test->parms, reps);
841	if (abortTest)
842	    AbortTest ();
843	/*
844	 * if using fixedReps then will not have done CalibrateTest so must
845	 * check result of init for 0 here
846	 */
847	if(reps == 0){
848	    DestroyPerfGCs(xp);
849	    return;
850	}
851	/* Create clip windows if requested */
852	CreateClipWindows(xp, test->clips);
853
854	totalTime = 0.0;
855	for (int j = 0; j != repeat; j++) {
856	    DisplayStatus(xp->d, "Testing", label, j+1);
857	    time = DoTest(xp, test, reps);
858	    if (abortTest)
859		AbortTest ();
860	    totalTime += time;
861	    ReportTimes (time, reps * test->parms.objects,
862		    label, False);
863            if (delay)
864                sleep(delay);
865	}
866	if (repeat > 1) {
867	    ReportTimes(totalTime,
868                        repeat * reps * test->parms.objects,
869                        label, True);
870	}
871	(*test->cleanup) (xp, &test->parms);
872	DestroyClipWindows(xp, test->clips);
873    } else {
874	/* Test failed to initialize properly */
875    }
876    printf ("\n");
877    fflush(stdout);
878    DestroyPerfGCs(xp);
879} /* ProcessTest */
880
881#define Strstr strstr
882
883#define LABELP(i) (test[i].label14 && (xparms.version >= VERSION1_4) \
884		        ? test[i].label14 : test[i].label)
885
886int
887main(int argc, char *argv[])
888{
889    int		i, j, n, skip;
890    int		numTests;       /* Even though the linker knows, we don't. */
891    char	hostname[100];
892    Bool	foundOne = False;
893    Bool	synchronous = False;
894    XGCValues	tgcv;
895    int		screen;
896    int		rop, pm;
897    int		pop, format;
898    int		window_y, window_x;
899    XVisualInfo *vinfolist, vinfotempl;
900    unsigned long vmask;
901
902    /* Save away argv, argc, for usage to print out */
903    saveargc = argc;
904    saveargv = malloc(argc * sizeof(char *));
905    for (i = 0; i != argc; i++) {
906	saveargv[i] = argv[i];
907    }
908
909    xparms.pack = False;
910    xparms.save_under = False;
911    xparms.backing_store = NotUseful;
912
913    /* Count number of tests */
914    ForEachTest(numTests);
915    doit = calloc(numTests, sizeof(Bool));
916
917    /* Parse arguments */
918    program_name = argv[0];
919    displayName = Get_Display_Name (&argc, argv);
920    xparms.version = GetVersion(&argc, argv);
921    for (i = 1; i != argc; i++) {
922	if (strcmp (argv[i], "-all") == 0) {
923	    ForEachTest (j)
924		doit[j] = test[j].versions & xparms.version;
925	    foundOne = True;
926	} else if (strcmp (argv[i], "-labels") == 0) {
927	    labels = True;
928	} else if (strcmp(argv[i], "-range") == 0) {
929	    char *cp1;
930	    char *cp2;
931
932	    if (argc <= ++i)
933		usage();
934	    cp1 = argv[i];
935	    if (*cp1 == '-')
936		cp1++;
937	    for (cp2 = cp1; *cp2 != '\0' && *cp2 != ','; cp2++) {};
938	    if (*cp2 == ',') {
939		*cp2++ = '\0';
940		if (*cp2 == '-')
941		    cp2++;
942	    } else {
943		cp2 = "-";
944	    }
945	    ForEachTest (j) {
946		if (strcmp (cp1, (test[j].option) + 1) == 0 &&
947		    (test[j].versions & xparms.version)) {
948		    int k = j;
949		    do {
950			doit[k] = test[j].versions & xparms.version;
951		    } while (!(strcmp(cp2, (test[k].option + 1)) == 0 &&
952			       (test[k].versions & xparms.version)) &&
953			     test[++k].option != NULL);
954		    if (*cp2 != '-' && test[k].option == NULL)
955			usage();
956		    break;
957		}
958	    }
959	    if (test[j].option == NULL)
960		usage();
961	    foundOne = True;
962	} else if (strcmp (argv[i], "-sync") == 0) {
963	    synchronous = True;
964	} else if (strcmp (argv[i], "-pack") == 0) {
965	    xparms.pack = True;
966	} else if (strcmp (argv[i], "-draw") == 0) {
967	    drawToFakeServer = True;
968        } else if (strcmp (argv[i], "-falseprecision") == 0) {
969            falsePrecision = True;
970	} else if (strcmp (argv[i], "-repeat") == 0) {
971	    i++;
972	    if (argc <= i)
973		usage ();
974	    repeat = atoi (argv[i]);
975	    if (repeat <= 0)
976	       usage ();
977	} else if (strcmp (argv[i], "-time") == 0) {
978	    i++;
979	    if (argc <= i)
980		usage ();
981	    seconds = atoi (argv[i]);
982	    if (seconds <= 0)
983	       usage ();
984        } else if (strcmp (argv[i], "-pause") == 0) {
985            ++i;
986	    if (argc <= i)
987		usage ();
988	    delay = atoi (argv[i]);
989	    if (delay < 0)
990	       usage ();
991	} else if (strcmp(argv[i], "-fg") == 0) {
992	    i++;
993	    if (argc <= i)
994		usage ();
995	    foreground = argv[i];
996        } else if (strcmp(argv[i], "-bg") == 0) {
997	    i++;
998	    if (argc <= i)
999		usage ();
1000	    background = argv[i];
1001	    if(ddbackground == NULL)
1002		ddbackground = argv[i];
1003	} else if (strcmp(argv[i], "-clips") == 0 ) {
1004	    i++;
1005	    if (argc <= i)
1006		usage ();
1007	    clips = atoi( argv[i] );
1008	} else if (strcmp(argv[i], "-ddbg") == 0) {
1009	    if (argc <= i)
1010		usage ();
1011	    i++;
1012	    ddbackground = argv[i];
1013	} else if (strcmp(argv[i], "-rop") == 0) {
1014	    skip = GetRops (i+1, argc, argv, rops, &numRops);
1015	    i += skip;
1016	} else if (strcmp(argv[i], "-pop") == 0) {
1017	    skip = GetPops (i+1, argc, argv, pops, &numPops);
1018	    i += skip;
1019	} else if (strcmp(argv[i], "-format") == 0) {
1020	    skip = GetFormats (i+1, argc, argv, formats, &numFormats);
1021	    i += skip;
1022	} else if (strcmp(argv[i], "-pm") == 0) {
1023	    skip = GetNumbers (i+1, argc, argv, planemasks, &numPlanemasks);
1024	    i += skip;
1025	} else if (strcmp(argv[i], "-xor") == 0) {
1026	    numRops = 1;
1027	    rops[0] = GXxor;
1028	} else if (strcmp (argv[i], "-both") == 0) {
1029	    numRops = 2;
1030	    rops[0] = GXcopy;
1031	    rops[1] = GXxor;
1032	} else if (strcmp(argv[i], "-reps") == 0) {
1033	    i++;
1034	    if (argc <= i)
1035		usage ();
1036	    fixedReps = atoi (argv[i]);
1037	    if (fixedReps <= 0)
1038		usage ();
1039        } else if (strcmp(argv[i], "-depth") == 0) {
1040	    i++;
1041	    if (argc <= i)
1042                usage ();
1043            depth = atoi(argv[i]);
1044            if (depth <= 0)
1045		usage ();
1046        } else if (strcmp(argv[i], "-vclass") == 0) {
1047	    i++;
1048	    if (argc <= i)
1049                usage ();
1050	    for (j = StaticGray; j <= DirectColor; j++) {
1051		if (strcmp(argv[i], visualClassNames[j]) == 0) {
1052		    vclass = j;
1053		    break;
1054		}
1055	    }
1056            if (vclass < 0)
1057		usage ();
1058	} else if (strcmp(argv[i], "-subs") == 0) {
1059	    skip = GetNumbers (i+1, argc, argv, subWindows, &numSubWindows);
1060	    i += skip;
1061	} else if (strcmp(argv[i], "-v1.2") == 0) {
1062	    xparms.version = VERSION1_2;
1063	} else if (strcmp(argv[i], "-v1.3") == 0) {
1064	    xparms.version = VERSION1_3;
1065	} else if (strcmp(argv[i], "-su") == 0) {
1066	    xparms.save_under = True;
1067	} else if (strcmp(argv[i], "-bs") == 0) {
1068	    i++;
1069	    if (argc <= i)
1070		usage ();
1071	    if (strcmp(argv[i], "WhenMapped") == 0) {
1072	      xparms.backing_store = WhenMapped;
1073	    } else if (strcmp(argv[i], "Always") == 0) {
1074	      xparms.backing_store = Always;
1075	    } else usage ();
1076	} else {
1077	    int len,found;
1078	    ForEachTest (j) {
1079		if (strcmp (argv[i], test[j].option) == 0 &&
1080		    (test[j].versions & xparms.version)) {
1081		    doit[j] = True;
1082		    goto LegalOption;
1083		}
1084	    }
1085	    found = False;
1086	    len = strlen(argv[i]);
1087	    if(len>=3)
1088	    ForEachTest (j) {
1089		if (Strstr (test[j].option, argv[i]+1) != NULL) {
1090		    fprintf(stderr,"	-> %s	%s\n", test[j].option, LABELP(j));
1091		    doit[j] = found = True;
1092		}
1093	    }
1094	    if(!found)
1095	    ForEachTest (j) {
1096		if (Strstr (LABELP(j), argv[i]+1) != NULL) {
1097		    fprintf(stderr,"	-> %s	%s\n", test[j].option, LABELP(j));
1098		    doit[j] = found = True;
1099		}
1100	    }
1101	    if(!found)
1102		usage ();
1103	LegalOption:
1104		foundOne = True;
1105	}
1106    }
1107
1108    if (labels) {
1109	/* Just print out list of tests for use with .sh programs that
1110	   assemble data from different x11perf runs into a nice format */
1111	ForEachTest (i) {
1112	    if (doit[i]) {
1113		switch (test[i].testType) {
1114		    case NONROP:
1115			printf ("%s\n", LABELP(i));
1116			break;
1117
1118		    case ROP:
1119			/* Run it through all specified rops and planemasks */
1120			for (rop = 0; rop < numRops; rop++) {
1121			    for (pm = 0; pm < numPlanemasks; pm++) {
1122				if (planemasks[pm] == ~0) {
1123				    if (rops[rop] == GXcopy) {
1124					printf ("%s\n", LABELP(i));
1125				    } else {
1126					printf ("(%s) %s\n",
1127					    ropNames[rops[rop]].name,
1128					    LABELP(i));
1129				    }
1130				} else {
1131				    printf ("(%s 0x%lx) %s\n",
1132					    ropNames[rops[rop]].name,
1133					    planemasks[pm],
1134					    LABELP(i));
1135				}
1136			    } /* for pm */
1137			} /* for rop */
1138			break;
1139
1140		    case PLANEMASK:
1141			/* Run it through all specified planemasks */
1142			for (pm = 0; pm < numPlanemasks; pm++) {
1143			    if (planemasks[pm] == ~0) {
1144				printf ("%s\n", LABELP(i));
1145			    } else {
1146				printf ("(0x%lx) %s\n",
1147					planemasks[pm],
1148					LABELP(i));
1149			    }
1150			} /* for pm */
1151			break;
1152
1153		    case WINDOW:
1154			for (int child = 0; child != numSubWindows; child++) {
1155			    printf ("%s (%ld kids)\n",
1156				LABELP(i), subWindows[child]);
1157			}
1158			break;
1159		    case COMP:
1160			/* Run it through all specified pops */
1161			for (pop = 0; pop < numPops; pop++) {
1162			    if (pops[pop] == PictOpOver) {
1163				printf ("%s\n", LABELP(i));
1164			    } else {
1165				printf ("(%s) %s\n",
1166					popNames[pops[pop]].name,
1167					LABELP(i));
1168			    }
1169			} /* for pop */
1170			break;
1171		} /* switch */
1172	    }
1173	}
1174	exit(0);
1175    }
1176
1177    if (!foundOne)
1178	usage ();
1179    xparms.d = Open_Display (displayName);
1180    screen = DefaultScreen(xparms.d);
1181
1182    /* get visual info of default visual */
1183    vmask = VisualIDMask | VisualScreenMask;
1184    vinfotempl.visualid = XVisualIDFromVisual(XDefaultVisual(xparms.d, screen));
1185    vinfotempl.screen = screen;
1186    vinfolist = XGetVisualInfo(xparms.d, vmask, &vinfotempl, &n);
1187    if (!vinfolist || n != 1) {
1188	fprintf (stderr, "%s: can't get visual info of default visual\n",
1189	    program_name);
1190	exit(1);
1191    }
1192
1193    if (depth == -1 && vclass == -1) {
1194	/* use the default visual and colormap */
1195	xparms.vinfo = *vinfolist;
1196	cmap = XDefaultColormap(xparms.d, screen);
1197    } else {
1198	/* find the specified visual */
1199	int errorDepth = vinfolist[0].depth;
1200	int errorClass = vinfolist[0].class;
1201
1202	vmask = VisualScreenMask;
1203	vinfotempl.screen = screen;
1204	if (depth >= 0) {
1205	    vinfotempl.depth = depth;
1206	    vmask |= VisualDepthMask;
1207	    errorDepth = depth;
1208	}
1209	if (vclass >= 0) {
1210	    vinfotempl.class = vclass;
1211	    vmask |= VisualClassMask;
1212	    errorClass = vclass;
1213	}
1214	vinfolist = XGetVisualInfo(xparms.d, vmask, &vinfotempl, &n);
1215	if (!vinfolist) {
1216	    fprintf (stderr,
1217		"%s: can't find a visual of depth %d and class %s\n",
1218		program_name, errorDepth, visualClassNames[errorClass]);
1219	    exit(1);
1220	}
1221	xparms.vinfo = *vinfolist;  /* use the first one in list */
1222	if (xparms.vinfo.visualid ==
1223	    XVisualIDFromVisual(XDefaultVisual(xparms.d, screen))) {
1224	    /* matched visual is same as default visual */
1225	    cmap = XDefaultColormap(xparms.d, screen);
1226	} else {
1227	    cmap = XCreateColormap(xparms.d, DefaultRootWindow(xparms.d),
1228		xparms.vinfo.visual, AllocNone);
1229	    /* since this is not default cmap, must force color allocation */
1230	    if (!foreground) foreground = "Black";
1231	    if (!background) background = "White";
1232	    XInstallColormap(xparms.d, cmap);
1233	}
1234    }
1235    xparms.cmap = cmap;
1236
1237    printf("x11perf - X11 performance program, version %s\n",
1238	   xparms.version & VERSION1_5 ? "1.5" :
1239	   xparms.version & VERSION1_4 ? "1.4" :
1240	   xparms.version & VERSION1_3 ? "1.3" :
1241	   "1.2"
1242	   );
1243    XmuGetHostname(hostname, 100);
1244    printf ("%s server version %d on %s\nfrom %s\n",
1245	    ServerVendor (xparms.d), VendorRelease (xparms.d),
1246	    DisplayString (xparms.d), hostname);
1247    PrintTime ();
1248
1249    /* Force screen out of screen-saver mode, grab current data, and set
1250       time to blank to 8 hours.  We should just be able to turn the screen-
1251       saver off, but this causes problems on some servers.  We also reset
1252       the screen-saver timer each test, as 8 hours is about the maximum time
1253       we can use, and that isn't long enough for some X terminals using a
1254       serial protocol to finish all the tests.  As long as the tests run to
1255       completion, the old screen-saver values are restored. */
1256    XForceScreenSaver(xparms.d, ScreenSaverReset);
1257    XGetScreenSaver(xparms.d, &ssTimeout, &ssInterval, &ssPreferBlanking,
1258	&ssAllowExposures);
1259    (void) signal(SIGINT, Cleanup); /* ^C */
1260#ifdef SIGQUIT
1261    (void) signal(SIGQUIT, Cleanup);
1262#endif
1263    (void) signal(SIGTERM, Cleanup);
1264#ifdef SIGHUP
1265    (void) signal(SIGHUP, Cleanup);
1266#endif
1267    XSetScreenSaver(xparms.d, 8 * 3600, ssInterval, ssPreferBlanking,
1268	ssAllowExposures);
1269
1270    if (drawToFakeServer) {
1271        tileToQuery =
1272	    XCreatePixmap(xparms.d, DefaultRootWindow (xparms.d), 32, 32, 1);
1273    }
1274
1275
1276    xparms.foreground =
1277	AllocateColor(xparms.d, foreground, BlackPixel(xparms.d, screen));
1278    xparms.background =
1279	AllocateColor(xparms.d, background, WhitePixel(xparms.d, screen));
1280    xparms.ddbackground =
1281	AllocateColor(xparms.d, ddbackground, WhitePixel(xparms.d, screen));
1282    window_x = 2;
1283    if (DisplayWidth(xparms.d, screen) < WIDTH + window_x + 1)
1284	window_x = -1;
1285    window_y = 2;
1286    if (DisplayHeight(xparms.d, screen) < HEIGHT + window_y + 1)
1287	window_y = -1;
1288    xparms.w = CreatePerfWindow(&xparms, window_x, window_y, WIDTH, HEIGHT);
1289    HSx = WIDTH-1;
1290    if (window_x + 1 + WIDTH > DisplayWidth(xparms.d, screen))
1291	HSx = DisplayWidth(xparms.d, screen) - (1 + window_x + 1);
1292    HSy = HEIGHT-1;
1293    if (window_y + 1 + HEIGHT > DisplayHeight(xparms.d, screen))
1294	HSy = DisplayHeight(xparms.d, screen) - (1 + window_y + 1);
1295    status = CreatePerfWindow(&xparms, window_x, HEIGHT+5, WIDTH, 20);
1296    tgcv.foreground =
1297	AllocateColor(xparms.d, "black", BlackPixel(xparms.d, screen));
1298    tgcv.background =
1299	AllocateColor(xparms.d, "white", WhitePixel(xparms.d, screen));
1300    tgc = XCreateGC(xparms.d, status, GCForeground | GCBackground, &tgcv);
1301
1302    xparms.p = (Pixmap)0;
1303
1304    if (synchronous)
1305	XSynchronize (xparms.d, True);
1306
1307    /* Get mouse pointer out of the way of the performance window.  On
1308       software cursor machines it will slow graphics performance.  On
1309       all current MIT-derived servers it will slow window
1310       creation/configuration performance. */
1311    XWarpPointer(xparms.d, None, status, 0, 0, 0, 0, WIDTH+32, 20+32);
1312
1313    /* Figure out how long to call HardwareSync, so we can adjust for that
1314       in our total elapsed time */
1315    (void) CalibrateTest(&xparms, &syncTest, 1, &syncTime);
1316    printf("Sync time adjustment is %6.4f msecs.\n\n", syncTime/1000);
1317
1318    ForEachTest (i) {
1319	char label[200];
1320
1321	if (doit[i] && (test[i].versions & xparms.version)) {
1322	    switch (test[i].testType) {
1323	        case NONROP:
1324		    /* Simplest...just run it once */
1325		    strcpy (label, LABELP(i));
1326		    ProcessTest(&xparms, &test[i], GXcopy, ~0L, label);
1327		    break;
1328
1329		case ROP:
1330		    /* Run it through all specified rops and planemasks */
1331		    for (rop = 0; rop < numRops; rop++) {
1332			for (pm = 0; pm < numPlanemasks; pm++) {
1333			    if (planemasks[pm] == ~0) {
1334				if (rops[rop] == GXcopy) {
1335				    sprintf (label, "%s", LABELP(i));
1336				} else {
1337				    sprintf (label, "(%s) %s",
1338					ropNames[rops[rop]].name,
1339					LABELP(i));
1340				}
1341			    } else {
1342				sprintf (label, "(%s 0x%lx) %s",
1343					ropNames[rops[rop]].name,
1344					planemasks[pm],
1345					LABELP(i));
1346			    }
1347			    ProcessTest(&xparms, &test[i], rops[rop],
1348				        planemasks[pm], label);
1349			} /* for pm */
1350		    } /* for rop */
1351		    break;
1352
1353		case PLANEMASK:
1354		    /* Run it through all specified planemasks */
1355		    for (pm = 0; pm < numPlanemasks; pm++) {
1356			if (planemasks[pm] == ~0) {
1357			    sprintf (label, "%s", LABELP(i));
1358			} else {
1359			    sprintf (label, "(0x%lx) %s",
1360				    planemasks[pm],
1361				    LABELP(i));
1362			}
1363			ProcessTest(&xparms, &test[i], GXcopy,
1364				    planemasks[pm], label);
1365		    } /* for pm */
1366		    break;
1367
1368		case WINDOW:
1369		    /* Loop through number of children array */
1370		    for (int child = 0; child != numSubWindows; child++) {
1371			test[i].parms.objects = subWindows[child];
1372			sprintf(label, "%s (%d kids)",
1373			    LABELP(i), test[i].parms.objects);
1374			ProcessTest(&xparms, &test[i], GXcopy, ~0L, label);
1375		    }
1376		    break;
1377	        case COMP:
1378		    /* Loop through the composite operands */
1379		    for (pop = 0; pop < numPops; pop++) {
1380			for (format = 0; format < numFormats; format++) {
1381			    if (formats[format] == PictStandardNative) {
1382				if (pops[pop] == PictOpOver) {
1383				    sprintf (label, "%s", LABELP(i));
1384				} else {
1385				    sprintf (label, "(%s) %s",
1386					     popNames[pops[pop]].name,
1387					     LABELP(i));
1388				}
1389			    } else {
1390				const char *name = NameFromFormat (formats[format]);
1391				sprintf (label, "(%s %s) %s",
1392					 popNames[pops[pop]].name,
1393					 name,
1394					 LABELP(i));
1395			    }
1396			    ProcessTest (&xparms, &test[i], pops[pop], formats[format], label);
1397			}
1398		    }
1399		    break;
1400	    } /* switch */
1401	} /* if doit */
1402    } /* ForEachTest */
1403
1404    XFreeGC(xparms.d, tgc);
1405    XDestroyWindow(xparms.d, xparms.w);
1406    XFree(vinfolist);
1407    if (drawToFakeServer)
1408      XFreePixmap(xparms.d, tileToQuery);
1409    /* Restore ScreenSaver to original state. */
1410    XSetScreenSaver(xparms.d, ssTimeout, ssInterval, ssPreferBlanking,
1411	ssAllowExposures);
1412    XCloseDisplay(xparms.d);
1413    free(saveargv);
1414    free(doit);
1415    exit(0);
1416}
1417
1418static int
1419GetWords (int argi, int argc, char **argv, char **wordsp, int *nump)
1420{
1421    int	    count;
1422
1423    if (argc <= argi)
1424	usage();
1425    count = 0;
1426    while (argv[argi] && *(argv[argi]) != '-') {
1427	*wordsp++ = argv[argi];
1428	++argi;
1429	count++;
1430    }
1431    *nump = count;
1432    return count;
1433}
1434
1435static long
1436atox (char *s)
1437{
1438    long   v, c = 0;
1439
1440    v = 0;
1441    while (*s) {
1442	if ('0' <= *s && *s <= '9')
1443	    c = *s - '0';
1444	else if ('a' <= *s && *s <= 'f')
1445	    c = *s - 'a' + 10;
1446	else if ('A' <= *s && *s <= 'F')
1447	    c = *s - 'A' + 10;
1448	v = v * 16 + c;
1449	s++;
1450    }
1451    return v;
1452}
1453
1454static int
1455GetNumbers (int argi, int argc, char **argv, unsigned long *intsp, int *nump)
1456{
1457    char    *words[256];
1458    int	    count;
1459
1460    count = GetWords (argi, argc, argv, words, nump);
1461    for (int i = 0; i < count; i++) {
1462	int flip = 0;
1463	if (!strncmp (words[i], "~", 1)) {
1464	    words[i]++;
1465	    flip = ~0;
1466	}
1467	if (!strncmp (words[i], "0x", 2))
1468	    intsp[i] = atox(words[i] + 2) ^ flip;
1469	else
1470	    intsp[i] = atoi (words[i]) ^ flip;
1471    }
1472    return count;
1473}
1474
1475static int
1476GetRops (int argi, int argc, char **argv, int *ropsp, int *nump)
1477{
1478    char    *words[256];
1479    int	    count;
1480    int	    rop;
1481
1482    count = GetWords (argi, argc, argv, words, nump);
1483    for (int i = 0; i < count; i++) {
1484	if (!strncmp (words[i], "GX", 2))
1485	    words[i] += 2;
1486	if (!strcmp (words[i], "all")) {
1487	    for (i = 0; i < NUM_ROPS; i++)
1488		ropsp[i] = ropNames[i].rop;
1489	    *nump = NUM_ROPS;
1490	    break;
1491	}
1492	for (rop = 0; rop < NUM_ROPS; rop++) {
1493	    if (!strcmp (words[i], ropNames[rop].name)) {
1494		ropsp[i] = ropNames[rop].rop;
1495		break;
1496	    }
1497	}
1498	if (rop == NUM_ROPS) {
1499	    usage ();
1500	    fprintf (stderr, "unknown rop name %s\n", words[i]);
1501	}
1502    }
1503    return count;
1504}
1505
1506static int
1507GetPops (int argi, int argc, char **argv, int *popsp, int *nump)
1508{
1509    char    *words[256];
1510    int	    count;
1511    int	    i;
1512    int	    pop;
1513
1514    count = GetWords (argi, argc, argv, words, nump);
1515    for (i = 0; i < count; i++) {
1516	if (!strncmp (words[i], "PictOp", 6))
1517	    words[i] += 6;
1518	if (!strcmp (words[i], "all")) {
1519	    for (i = 0; i < NUM_POPS; i++)
1520		popsp[i] = popNames[i].rop;
1521	    *nump = NUM_POPS;
1522	    break;
1523	}
1524	for (pop = 0; pop < NUM_POPS; pop++) {
1525	    if (!strcmp (words[i], popNames[pop].name)) {
1526		popsp[i] = popNames[pop].rop;
1527		break;
1528	    }
1529	}
1530	if (pop == NUM_POPS) {
1531	    usage ();
1532	    fprintf (stderr, "unknown picture op name %s\n", words[i]);
1533	}
1534    }
1535    return count;
1536}
1537
1538static int
1539FormatFromName (char *name)
1540{
1541    int i;
1542    for (i = 0; i < NUM_FORMATS; i++)
1543	if (!strcmp (name, formatNames[i].name))
1544	    return formatNames[i].rop;
1545    return -1;
1546}
1547
1548static const char *
1549NameFromFormat (int format)
1550{
1551    for (int i = 0; i < NUM_FORMATS; i++)
1552	if (formatNames[i].rop == format)
1553	    return formatNames[i].name;
1554    return NULL;
1555}
1556
1557static int
1558GetFormats (int argi, int argc, char **argv, int *formatsp, int *nump)
1559{
1560    char    *words[256];
1561    int	    count;
1562    int	    i;
1563
1564    count = GetWords (argi, argc, argv, words, nump);
1565    for (i = 0; i < count; i++) {
1566        int format;
1567
1568	if (!strcmp (words[i], "all")) {
1569	    for (i = 0; i < NUM_FORMATS; i++)
1570		formatsp[i] = formatNames[i].rop;
1571	    *nump = NUM_FORMATS;
1572	    break;
1573	}
1574	format = FormatFromName (words[i]);
1575	if (format < 0) {
1576	    usage ();
1577	    fprintf (stderr, "unknown format name %s\n", words[i]);
1578	}
1579	formatsp[i] = format;
1580    }
1581    return count;
1582}
1583