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