From http://www.jwz.org/xscreensaver/xscreensaver-5.34.tar.gz
[xscreensaver] / hacks / testx11.c
1 /* testx11.c, Copyright (c) 2015 Dave Odell <dmo2118@gmail.com>
2  *
3  * Permission to use, copy, modify, distribute, and sell this software and its
4  * documentation for any purpose is hereby granted without fee, provided that
5  * the above copyright notice appear in all copies and that both that
6  * copyright notice and this permission notice appear in supporting
7  * documentation.  No representations are made about the suitability of this
8  * software for any purpose.  It is provided "as is" without express or
9  * implied warranty.
10  *
11  * This is a test for X11 drawing primitives for the non-X11 ports of
12  * XScreenSaver. It shouldn't normally be installed with the other screenhacks.
13  *
14  * Almost no error checking in here. This is intentional.
15  */
16
17 #include "screenhack.h"
18
19 #include <assert.h>
20 #include <errno.h>
21
22 #ifndef HAVE_COCOA
23 # define jwxyz_XSetAntiAliasing(dpy, gc, p)
24 #endif
25
26 #ifndef jwxyz_assert_display
27 # define jwxyz_assert_display(dpy)
28 #endif
29
30 #define countof(a) (sizeof(a) / sizeof(*(a)))
31
32 static const char *testx11_defaults [] = {
33   ".background: #a020f0", /* purple */
34   ".foreground: white",
35   0
36 };
37
38 static XrmOptionDescRec testx11_options [] = {
39   { 0, 0, 0, 0 }
40 };
41
42 #if defined HAVE_COCOA || defined HAVE_ANDROID
43 # define HAVE_JWXYZ 1
44 #endif
45
46 enum
47 {
48   mode_welcome,
49   mode_primitives,
50   mode_images,
51   mode_copy,
52
53   mode_count
54 };
55
56 struct testx11 {
57   Display *dpy;
58   Window win;
59
60   XWindowAttributes xgwa;
61
62 # ifndef HAVE_JWXYZ
63   Pixmap backbuffer;
64 # endif
65
66   unsigned mode;
67
68   unsigned long rgb[3], salmon, magenta, gray50, dark_slate_gray1, cyan;
69
70   Pixmap backdrop_tile, clip_mask_tile, backdrop_scratch;
71   XColor backdrop_colors[64];
72   int backdrop_ncolors;
73   unsigned frame;
74
75   GC copy_gc, black_gc, mono_gc, thick_line_gc, xor_gc, point_gc;
76   Bool anti_alias_p;
77
78   Pixmap mini_pix, pix64;
79 };
80
81
82 static void
83 abort_no_mem (void)
84 {
85   fprintf(stderr, "%s: %s\n", progname, strerror(ENOMEM));
86   abort();
87 }
88
89 static void
90 check_no_mem (void *ptr)
91 {
92   if(!ptr)
93     abort_no_mem();
94 }
95
96
97 static unsigned long
98 alloc_color (struct testx11 *st,
99              unsigned short r, unsigned short g, unsigned short b)
100 {
101   XColor color;
102   color.red = r;
103   color.green = g;
104   color.blue = b;
105   color.flags = DoRed | DoGreen | DoBlue;
106   XAllocColor (st->dpy, st->xgwa.colormap, &color);
107   return color.pixel;
108 }
109
110 static void
111 create_backbuffer(struct testx11 *st)
112 {
113 # ifndef HAVE_JWXYZ
114   st->backbuffer = XCreatePixmap (st->dpy, st->win,
115                                   st->xgwa.width, st->xgwa.height,
116                                   st->xgwa.depth);
117 # endif
118 }
119
120
121 static void
122 copy_test (Display *dpy, Drawable src, Drawable dst, GC gc, int x, int y,
123            unsigned long cells)
124 {
125   XCopyArea(dpy, src, dst, gc, 0, 0, 3, 2, x, y);
126
127   {
128     XImage *image = XGetImage(dpy, src, 0, 0, 3, 2, cells, ZPixmap);
129     XPutImage(dpy, dst, gc, image, 0, 0, x, y + 2, 3, 2);
130     XDestroyImage(image);
131   }
132 }
133
134 static void
135 test_pattern (struct testx11 *st, Drawable d)
136 {
137   unsigned x;
138   for (x = 0; x != 3; ++x) {
139     XSetForeground(st->dpy, st->point_gc, st->rgb[x]);
140     XDrawPoint(st->dpy, d, st->point_gc, x, 0);
141     XSetForeground(st->dpy, st->point_gc, st->rgb[2 - x]);
142     XFillRectangle(st->dpy, d, st->point_gc, x, 1, 1, 1);
143   }
144
145   copy_test (st->dpy, d, d, st->point_gc, 0, 2,
146              st->rgb[0] | st->rgb[1] | st->rgb[2]);
147 }
148
149 static void
150 mini_rect(struct testx11 *st, Drawable t, int x, int y)
151 {
152   XFillRectangle(st->dpy, t, st->copy_gc, x, y, 2, 2);
153 }
154
155 static Bool
156 toggle_antialiasing (struct testx11 *st)
157 {
158   st->anti_alias_p ^= True;
159   jwxyz_XSetAntiAliasing(st->dpy, st->copy_gc, st->anti_alias_p);
160   return True;
161 }
162
163 static const unsigned tile_size = 16;
164 static const unsigned tile_count = 8;
165
166 static void
167 make_clip_mask (struct testx11 *st)
168 {
169   /* Activate this for extra Fun! */
170 # if 0
171   /* This is kind of slow, but that's OK, because this only happens on int
172      or during a resize.
173    */
174   unsigned w = st->xgwa.width, h = st->xgwa.height;
175   unsigned x, y;
176   Pixmap mask = XCreatePixmap (st->dpy, st->clip_mask_tile, w, h, 1);
177
178   for (y = 0; y < h; y += 8) {
179     for (x = 0; x < w; x += 8) {
180       XCopyArea (st->dpy, st->clip_mask_tile, mask, st->mono_gc,
181                  0, 0, 8, 8, x, y);
182     }
183   }
184
185   XSetClipMask (st->dpy, st->xor_gc, mask);
186   XFreePixmap (st->dpy, mask);
187 # endif
188 }
189
190
191 static void *
192 testx11_init (Display *dpy, Window win)
193 {
194   static const char backdrop_pattern[] = {
195     '\xff', '\x00', '\xfe', '\x01', '\xfc', '\x03', '\xf8', '\x07', /* Zig */
196     '\xf0', '\x0f', '\xf8', '\x07', '\xfc', '\x03', '\xfe', '\x01', /* Zag */
197     '\xff', '\x00', '\xfe', '\x01', '\xfc', '\x03', '\xf8', '\x07', /* etc. */
198     '\xf0', '\x0f', '\xf8', '\x07', '\xfc', '\x03', '\xfe', '\x01',
199     '\xff', '\x00', '\xfe', '\x01', '\xfc', '\x03', '\xf8', '\x07',
200     '\xf0', '\x0f', '\xf8', '\x07', '\xfc', '\x03', '\xfe', '\x01',
201     '\xff', '\x00', '\xfe', '\x01', '\xfc', '\x03', '\xf8', '\x07',
202     '\xf0', '\x0f', '\xf8', '\x07', '\xfc', '\x03', '\xfe', '\x01',
203   };
204
205   static const char clip_bits[] = {
206     '\x33', '\x33', '\xcc', '\xcc', '\x33', '\x33', '\xcc', '\xcc'
207   };
208
209   struct testx11 *st = (struct testx11 *) malloc (sizeof(*st));
210   XGCValues gcv;
211
212   check_no_mem (st);
213
214   st->dpy = dpy;
215   st->win = win;
216
217   XGetWindowAttributes (dpy, win, &st->xgwa);
218
219   create_backbuffer (st);
220
221   st->rgb[0]           = alloc_color (st, 0xffff, 0x0000, 0x0000);
222   st->rgb[1]           = alloc_color (st, 0x0000, 0xffff, 0x0000);
223   st->rgb[2]           = alloc_color (st, 0x0000, 0x0000, 0xffff);
224   st->salmon           = alloc_color (st, 0xffff, 0x7fff, 0x7fff);
225   st->magenta          = alloc_color (st, 0xffff, 0x0000, 0xffff);
226   st->gray50           = alloc_color (st, 0x8000, 0x8000, 0x8000);
227   st->dark_slate_gray1 = alloc_color (st, 0x8000, 0xffff, 0xffff);
228   st->cyan             = alloc_color (st, 0x0000, 0xffff, 0xffff);
229
230   st->backdrop_tile = XCreatePixmapFromBitmapData (dpy, win,
231                                                    (char *) backdrop_pattern,
232                                                    tile_size, tile_size * 2,
233                                                    1, 0, 1);
234
235   st->clip_mask_tile = XCreatePixmapFromBitmapData (dpy, win,
236                                                     (char *) clip_bits, 8, 8,
237                                                     1, 0, 1);
238
239   {
240     unsigned s = tile_size * tile_count;
241     st->backdrop_scratch = XCreatePixmap (dpy, win, s, s, st->xgwa.depth);
242   }
243
244   st->backdrop_ncolors = countof (st->backdrop_colors);
245   make_color_loop (st->xgwa.screen, st->xgwa.visual, st->xgwa.colormap,
246                    180, 1, 0.5, 210, 1, 0.5, 240, 1, 0.5,
247                    st->backdrop_colors, &st->backdrop_ncolors, True, NULL);
248
249   st->frame = 0;
250
251   st->mode = mode_welcome;
252
253   gcv.function = GXcopy;
254   gcv.foreground = st->cyan;
255   gcv.background = st->magenta;
256   gcv.line_width = 0;
257   gcv.cap_style = CapRound;
258   /* TODO: Real X11 uses fixed by default. */
259   gcv.font = XLoadFont (dpy, "fixed");
260   st->copy_gc = XCreateGC (dpy, win, GCFunction | GCForeground | GCBackground | GCLineWidth | GCCapStyle | GCFont, &gcv);
261
262   gcv.foreground = BlackPixelOfScreen (st->xgwa.screen);
263   st->black_gc = XCreateGC (dpy, win, GCForeground, &gcv);
264
265   st->anti_alias_p = False;
266   jwxyz_XSetAntiAliasing (dpy, st->copy_gc, False);
267
268   gcv.foreground = alloc_color (st, 0x8000, 0x4000, 0xffff);
269   gcv.line_width = 8;
270   gcv.cap_style = CapProjecting;
271   st->thick_line_gc = XCreateGC (dpy, win, GCForeground | GCLineWidth | GCCapStyle, &gcv);
272
273   gcv.function = GXxor;
274   st->xor_gc = XCreateGC (dpy, win, GCFunction, &gcv);
275
276   st->point_gc = XCreateGC (dpy, win, 0, NULL);
277
278   st->mono_gc = XCreateGC (dpy, st->clip_mask_tile, 0, &gcv);
279
280   st->pix64 = XCreatePixmap(dpy, win, 64, 64, st->xgwa.depth);
281
282   st->mini_pix = XCreatePixmap (dpy, win, 16, 16, st->xgwa.depth);
283
284   {
285     static const char text[] = "Welcome from testx11_init().";
286     XClearWindow (dpy, win);
287     XDrawString (dpy, win, st->copy_gc, 16, 16, text, countof (text) - 1);
288   }
289
290   make_clip_mask (st);
291
292   jwxyz_assert_display (dpy);
293
294   return st;
295 }
296
297
298 static void
299 backdrop (struct testx11 *st, Drawable t)
300 {
301   unsigned w = st->xgwa.width, h = st->xgwa.height;
302   unsigned x, y;
303
304   for (y = 0; y != tile_count; ++y) {
305     const float s0 = 2 * M_PI / tile_count;
306     float y_fac = sin ((y + st->frame / 16.0) * s0);
307     for (x = 0; x != tile_count; ++x) {
308       unsigned c = ((sin ((x + st->frame / 8.0) * s0) * y_fac) - 1) / 2 * st->backdrop_ncolors / 2;
309       c = (c + st->frame) % st->backdrop_ncolors;
310       XSetBackground (st->dpy, st->black_gc, st->backdrop_colors[c].pixel);
311       XCopyPlane (st->dpy, st->backdrop_tile, st->backdrop_scratch, st->black_gc,
312                   0, st->frame % tile_size, tile_size, tile_size, x * tile_size, y * tile_size, 1);
313     }
314   }
315
316   /* XFillRectangle (st->dpy, t, st->black_gc, 0, 0, w, h); */
317
318   for (y = 0; y < h; y += tile_count * tile_size) {
319     for (x = 0; x < w; x += tile_count * tile_size) {
320       XCopyArea (st->dpy, st->backdrop_scratch, t, st->copy_gc, 0, 0,
321                  tile_count * tile_size, tile_count * tile_size, x, y);
322     }
323   }
324 }
325
326 static const unsigned button_pad = 16;
327 static const unsigned button_size = 64;
328
329
330 static unsigned long
331 testx11_draw (Display *dpy, Window win, void *st_raw)
332 {
333   struct testx11 *st = (struct testx11 *) st_raw;
334   unsigned w = st->xgwa.width, h = st->xgwa.height;
335 # ifdef HAVE_JWXYZ
336   Drawable t = win;
337 # else
338   Drawable t =
339     st->mode == mode_welcome ||
340     st->mode == mode_images ||
341     st->mode == mode_copy ? win : st->backbuffer;
342 # endif
343   unsigned i;
344
345   assert (dpy == st->dpy);
346   assert (win == st->win);
347
348   jwxyz_assert_display (dpy);
349
350   switch (st->mode)
351   {
352   case mode_primitives:
353     backdrop (st, t);
354
355     XDrawPoint (dpy, t, st->thick_line_gc, 0, 0);
356     XDrawPoint (dpy, t, st->thick_line_gc, 0, 1);
357     XDrawPoint (dpy, t, st->thick_line_gc, 1, 0);
358     XDrawPoint (dpy, t, st->thick_line_gc, 1, 1);
359
360     mini_rect (st, t, 2, 0);
361     mini_rect (st, t, 0, 2);
362     mini_rect (st, t, 4, 2);
363     mini_rect (st, t, 2, 4);
364
365     mini_rect (st, t, 30, -2);
366     mini_rect (st, t, 32, 0);
367
368     mini_rect (st, t, 30, h - 2);
369     mini_rect (st, t, 32, h);
370
371     mini_rect (st, t, -2, 30);
372     mini_rect (st, t, 0, 32);
373     mini_rect (st, t, w - 2, 30);
374     mini_rect (st, t, w, 32);
375
376     mini_rect (st, t, w / 2 + 4, h / 2 + 4);
377
378     XFillArc (dpy, t, st->copy_gc, 16, 16, 256, 256, 45 * 64, -135 * 64);
379     /* XCopyArea(dpy, t, t, st->copy_gc, 48, 48, 128, 128, 64, 64); */
380
381     /* Center */
382     XDrawPoint (dpy, t, st->copy_gc, w / 2, h / 2);
383
384     /* Top/bottom */
385     XDrawPoint (dpy, t, st->copy_gc, w / 2, 0);
386     XDrawPoint (dpy, t, st->copy_gc, w / 2, h - 1);
387
388     XDrawPoint (dpy, t, st->copy_gc, 16, -1);
389     XDrawPoint (dpy, t, st->copy_gc, 17, 0);
390
391     XDrawPoint (dpy, t, st->copy_gc, 15, h - 2);
392     XDrawPoint (dpy, t, st->copy_gc, 16, h - 1);
393     XDrawPoint (dpy, t, st->copy_gc, 17, h);
394
395     {
396       XPoint *points = malloc (sizeof(XPoint) * h);
397       XSegment *lines = malloc (sizeof(XSegment) * h);
398       XPoint *points2 = malloc (sizeof(XPoint) * h);
399       for(i = 0; i != h; ++i)
400       {
401         points[i].x = 8 + (i & 1);
402         points[i].y = i;
403
404         lines[i].x1 = 48 + (i & 1) * 4;
405         lines[i].y1 = i;
406         lines[i].x2 = 50;
407         lines[i].y2 = i;
408
409         points2[i].x = 24 + (i & 1);
410         points2[i].y = i;
411         /* XFillRectangle(st->dpy, t, st->copy_gc, 24 + (i & 1), i, 1, 1); */
412       }
413
414       XDrawPoints (dpy, t, st->copy_gc, points, h, CoordModeOrigin);
415       XDrawSegments (dpy, t, st->copy_gc, lines, h);
416       XDrawPoints (dpy, t, st->copy_gc, points2, h, CoordModeOrigin);
417       free (lines);
418       free (points);
419       free (points2);
420     }
421
422     /* Left/right */
423     XDrawPoint (dpy, t, st->copy_gc, -1, 16);
424     XDrawPoint (dpy, t, st->copy_gc, 0, 17);
425     XDrawPoint (dpy, t, st->copy_gc, w - 1, 16);
426     XDrawPoint (dpy, t, st->copy_gc, w, 17);
427
428     {
429       XPoint *points = malloc(sizeof(XPoint) * w);
430       XSegment *lines = malloc(sizeof(XSegment) * w);
431       XPoint *points2 = malloc(sizeof(XPoint) * w);
432       for(i = 0; i != w; ++i)
433       {
434         points[i].x = i;
435         points[i].y = 8 + (i & 1);
436         lines[i].x1 = i;
437         lines[i].y1 = 48 + (i & 1) * 4;
438         lines[i].x2 = i;
439         lines[i].y2 = 50;
440
441         points2[i].x = i;
442         points2[i].y = 24 + (i & 1);
443         /* XFillRectangle(dpy, t, st->copy_gc, i, 24 + (i & 1), 1, 1); */
444       }
445
446       XDrawPoints (dpy, t, st->copy_gc, points, w, CoordModeOrigin);
447       XDrawSegments (dpy, t, st->copy_gc, lines, w);
448       {
449         /* Thick purple lines */
450         XSegment seg[2] =
451         {
452           {31, 31, 31, 31}, /* TODO: This should not be rotated in Cocoa. */
453           {42, 31, 42, 42}
454         };
455         XDrawSegments (dpy, t, st->thick_line_gc, seg, 2);
456
457         XDrawLine (dpy, t, st->thick_line_gc, 58, 43, 64, 43);
458         XDrawLine (dpy, t, st->thick_line_gc, 73, 43, 80, 43);
459       }
460
461       XDrawPoints (dpy, t, st->copy_gc, points2, w, CoordModeOrigin);
462       free (points);
463       free (points2);
464       free (lines);
465     }
466
467     XDrawLine (dpy, t, st->copy_gc, 54, 11, 72, 22);
468
469     {
470       XPoint vertices[] = {{5, 5}, {5, 8}, {8, 8}, {8, 5}};
471       XFillPolygon (dpy, t, st->copy_gc, vertices, 4, Convex, CoordModeOrigin);
472     }
473
474     {
475       XDrawRectangle (dpy, t, st->copy_gc, 11, 11, 11, 11);
476       XFillRectangle (dpy, t, st->copy_gc, 13, 13, 8, 8);
477     }
478
479     /* Several ~16 pixel boxes in a row. */
480
481     /* Box 0 */
482     {
483       XDrawRectangle (dpy, t, st->copy_gc, 54, 54, 16, 16);
484       XDrawRectangle (dpy, t, st->copy_gc, 55, 55, 14, 14);
485
486       XDrawPoint (dpy, t, st->thick_line_gc, 56, 56);
487       XDrawPoint (dpy, t, st->copy_gc, 57, 56);
488     }
489
490     /* Box 1 */
491     XCopyArea (dpy, t, t, st->copy_gc, 55, 55, 15, 15, 72, 55);
492
493     /* Box 2 */
494     {
495       XImage *image = XGetImage(st->dpy, t, 55, 55, 15, 15, 0xffffff, ZPixmap);
496       XPutImage (dpy, t, st->copy_gc, image, 0, 0, 88, 55, 15, 15);
497       XDestroyImage(image);
498     }
499
500     /* Box 3 */
501
502     {
503       XCopyArea (dpy, t, st->mini_pix, st->copy_gc, 104, 55, 16, 16, 0, 0);
504       /* XCopyArea (dpy, t, st->mini_pix, st->copy_gc, 105, 56, 14, 14, 1, 1);
505          XCopyArea (dpy, t, st->mini_pix, st->copy_gc, 1, 1, 14, 14, 1, 1);
506        */
507
508       /* This point gets hidden. */
509       XDrawPoint (dpy, t, st->copy_gc, 104 + 8, 55 + 8);
510
511       XDrawPoint (dpy, st->mini_pix, st->copy_gc, 0, 0);
512       XDrawPoint (dpy, st->mini_pix, st->copy_gc, 15, 15);
513       XDrawRectangle (dpy, st->mini_pix, st->copy_gc, 1, 1, 13, 13);
514       XCopyArea (dpy, st->mini_pix, t, st->copy_gc, 0, 0, 16, 16, 104, 55);
515     }
516
517     {
518       XDrawLine (dpy, t, st->copy_gc, 11, 28, 22, 28);
519       XDrawLine (dpy, t, st->copy_gc, 12, 27, 12, 46);
520       XDrawLine (dpy, t, st->copy_gc, 14, 30, 14, 30);
521     }
522
523     XDrawArc (dpy, t, st->copy_gc, 27, 11, 19, 11, 0, 360 * 64);
524
525     XDrawRectangle (dpy, t, st->copy_gc, 54, 73, 64, 64);
526     XFillArc (dpy, t, st->copy_gc, 56, 75, 60, 60, 0, 360 * 64);
527     /* XDrawArc (dpy, t, st->thick_line_gc, 56, 75, 60, 60, 0, 360 * 64); */
528
529     XClearArea (dpy, win, 121, 55, 16, 16, False);
530     break;
531
532   case mode_images:
533     backdrop (st, t);
534
535     /* if(w >= 9 && h >= 10) */
536     {
537       Screen *screen = st->xgwa.screen;
538       Visual *visual = st->xgwa.visual;
539       Pixmap pixmap = XCreatePixmap (dpy, t, 3, 10,
540                                      visual_depth (screen, visual));
541       unsigned long cells = cells = st->rgb[0] | st->rgb[1] | st->rgb[2];
542
543       {
544         XSetForeground (dpy, st->point_gc, st->salmon);
545         XDrawPoint(dpy, t, st->point_gc, 0, h - 1);
546       }
547
548       test_pattern (st, t);
549       test_pattern (st, pixmap);
550       /* Here's a good spot to verify that the pixmap contains the right colors
551          at the top.
552        */
553       copy_test (dpy, t, pixmap, st->copy_gc, 0, 6, cells);
554
555       XCopyArea (dpy, pixmap, t, st->copy_gc, 0, 0, 3, 10, 3, 0);
556       {
557         XImage *image = XGetImage (dpy, pixmap, 0, 0, 3, 10, cells, ZPixmap);
558         XPutImage (dpy, t, st->copy_gc, image, 0, 0, 6, 0, 3, 10);
559         XDestroyImage (image);
560       }
561
562       XFreePixmap (dpy, pixmap);
563       XSync (dpy, False);
564     }
565     break;
566
567   case mode_copy:
568     backdrop (st, win);
569
570     /* X.org isn't making a whole lot of sense here. */
571
572     Bool use_copy = (st->frame / 20) & 1;
573
574     {
575       GC gc = use_copy ? st->copy_gc : st->xor_gc;
576
577       XSetWindowBackground (dpy, t, st->magenta);
578       XCopyArea (dpy, t, t, gc, -2, -2, 40, 40, 20, 20);
579
580       if (!use_copy)
581         XCopyArea (st->dpy, t, t, gc, -20, h - 20, 40, 40, 20, h - 60);
582       XCopyArea (dpy, t, t, gc, w - 38, h - 38, 40, 40, w - 60, h - 60);
583       XCopyArea (dpy, t, t, gc, w - 20, -20, 40, 40, w - 60, 20);
584
585       XSetWindowBackground (dpy, t, st->gray50);
586       XCopyArea (st->dpy, t, t, gc, -20, 64, 40, 40, 20, 64);
587
588       XSetWindowBackground (dpy, t, st->dark_slate_gray1);
589       XCopyArea (st->dpy, t, t, gc, -20, 112, 40, 40, 20, 112);
590     }
591
592     if (use_copy)
593     {
594       GC gc = st->copy_gc;
595       XCopyArea (st->dpy, t, st->pix64, gc, 0, h - 64, 64, 64, 0, 0);
596
597       XSetForeground (st->dpy, st->xor_gc, st->rgb[1]);
598       XSetBackground (st->dpy, st->xor_gc, st->cyan);
599
600       /* XCopyArea (st->dpy, st->pix64, st->pix64, gc, 32, 32, 64, 64, 0, 0);
601          XCopyArea (st->dpy, st->pix64, t, gc, 0, 0, 64, 64, 4, h - 68);
602        */
603       XCopyArea (st->dpy, st->pix64, t, gc, 32, 32, 128, 64, 0, h - 64);
604     }
605
606     break;
607   }
608
609   for (i = 1; i != mode_count; ++i) {
610     unsigned i0 = i - 1;
611     char str[32];
612     XRectangle button_dims;
613     button_dims.x = i0 * (button_pad + button_size) + button_pad;
614     button_dims.y = h - button_pad - button_size;
615     button_dims.width = button_size;
616     button_dims.height = button_size;
617     if (!st->mode)
618       XFillRectangles (dpy, t, st->black_gc, &button_dims, 1);
619     XDrawRectangle (dpy, t, st->copy_gc, button_dims.x, button_dims.y,
620                     button_dims.width, button_dims.height);
621
622     XDrawString (dpy, t, st->copy_gc,
623                  button_dims.x + button_size / 2 - 3,
624                  h - button_pad - button_size / 2 + 13 / 2,
625                  str, sprintf(str, "%u", i));
626   }
627
628   if (t != win)
629     XCopyArea (dpy, t, win, st->copy_gc, 0, 0, w, h, 0, 0);
630
631   ++st->frame;
632   return 1000000 / 20;
633 }
634
635 static void
636 testx11_reshape (Display *dpy, Window window, void *st_raw,
637                  unsigned int w, unsigned int h)
638 {
639   struct testx11 *st = (struct testx11 *)st_raw;
640   st->xgwa.width = w;
641   st->xgwa.height = h;
642 # ifndef HAVE_JWXYZ
643   XFreePixmap (st->dpy, st->backbuffer);
644 # endif
645   create_backbuffer (st);
646   make_clip_mask (st);
647 }
648
649 static Bool
650 testx11_event (Display *dpy, Window window, void *st_raw, XEvent *event)
651 {
652   struct testx11 *st = (struct testx11 *) st_raw;
653
654   Bool handled = False;
655
656   switch (event->xany.type)
657   {
658   case KeyPress:
659     {
660       KeySym keysym;
661       char c = 0;
662       XLookupString (&event->xkey, &c, 1, &keysym, 0);
663       if (c == ' ')
664         handled = toggle_antialiasing (st);
665
666       if (c >= '0' && c <= '9' && c < '0' + mode_count) {
667         st->mode = c - '0';
668         handled = True;
669       }
670     }
671     break;
672
673   case ButtonPress:
674     if (event->xbutton.y >= st->xgwa.height - button_pad * 2 - button_size) {
675       int i = (event->xbutton.x - button_pad / 2) / (button_pad + button_size) + 1;
676       if (i && i < mode_count) {
677         st->mode = i;
678         handled = True;
679       }
680     }
681
682     if (!handled)
683       handled = toggle_antialiasing (st);
684     break;
685   }
686
687   return handled;
688 }
689
690 static void
691 testx11_free (Display *dpy, Window window, void *st_raw)
692 {
693   /* Omitted for the sake of brevity. */
694 }
695
696 XSCREENSAVER_MODULE_2 ("TestX11", testx11, testx11)