From http://www.jwz.org/xscreensaver/xscreensaver-5.40.tar.gz
[xscreensaver] / hacks / testx11.c
1 /* testx11.c, Copyright (c) 2015-2017 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 #include "glx/rotator.h"
19 #include "colorbars.h"
20 #include "erase.h"
21
22 #include "ximage-loader.h"
23 #include "images/gen/logo-180_png.h"
24
25 #include <assert.h>
26 #include <errno.h>
27
28 #ifndef HAVE_JWXYZ
29 # define jwxyz_XSetAntiAliasing(dpy, gc, p)
30 #endif
31
32 #ifndef jwxyz_assert_display
33 # define jwxyz_assert_display(dpy)
34 #endif
35
36 #define countof(a) (sizeof(a) / sizeof(*(a)))
37
38 static const char *testx11_defaults [] = {
39   ".background: #a020f0", /* purple */
40   ".foreground: white",
41 #ifdef HAVE_MOBILE
42   "*ignoreRotation: True",
43 #endif
44   0
45 };
46
47 static XrmOptionDescRec testx11_options [] = {
48   { 0, 0, 0, 0 }
49 };
50
51 enum
52 {
53   mode_welcome,
54   mode_primitives,
55   mode_images,
56   mode_copy,
57   mode_preserve,
58   mode_erase,
59
60   mode_count
61 };
62
63 struct testx11 {
64   Display *dpy;
65   Window win;
66
67   XWindowAttributes xgwa;
68
69 # ifndef HAVE_JWXYZ
70   Pixmap backbuffer;
71 # endif
72
73   unsigned mode;
74
75   /* Pixels from XAllocPixel. */
76   unsigned long rgb[3], salmon, magenta, gray50, dark_slate_gray1, cyan;
77
78   unsigned frame;
79
80   Bool anti_alias_p;
81
82   /* These X11 objects aren't altered after creation, except for:
83      - copy_gc and thick_line_gc get anti-aliasing toggled.
84      - xor_gc's clip mask (if it's turned on) always covers the entire window.
85    */
86   GC copy_gc, mono_gc, thick_line_gc, xor_gc, graph_gc;
87   Pixmap clip_mask_tile;
88
89   /* Backdrop stuff, naturally. */
90   GC backdrop_black_gc;
91   Pixmap backdrop_tile, backdrop_scratch;
92   XColor backdrop_colors[64];
93   int backdrop_ncolors;
94
95   /* The following items may be modified by various test modes. */
96   Pixmap primitives_mini_pix;
97
98   GC images_point_gc;
99
100   Pixmap copy_pix64;
101
102   Pixmap preserve[2];
103
104   eraser_state *erase;
105
106   rotator *rot;
107 };
108
109
110 static void
111 abort_no_mem (void)
112 {
113   fprintf(stderr, "%s: %s\n", progname, strerror(ENOMEM));
114   abort();
115 }
116
117 static void
118 check_no_mem (void *ptr)
119 {
120   if(!ptr)
121     abort_no_mem();
122 }
123
124
125 static unsigned long
126 alloc_color (struct testx11 *st,
127              unsigned short r, unsigned short g, unsigned short b)
128 {
129   XColor color;
130   color.red = r;
131   color.green = g;
132   color.blue = b;
133   color.flags = DoRed | DoGreen | DoBlue;
134   XAllocColor (st->dpy, st->xgwa.colormap, &color);
135   return color.pixel;
136 }
137
138 static void
139 create_backbuffer(struct testx11 *st)
140 {
141 # ifndef HAVE_JWXYZ
142   st->backbuffer = XCreatePixmap (st->dpy, st->win,
143                                   st->xgwa.width, st->xgwa.height,
144                                   st->xgwa.depth);
145 # endif
146 }
147
148 static Bool
149 toggle_antialiasing (struct testx11 *st)
150 {
151   st->anti_alias_p ^= True;
152   jwxyz_XSetAntiAliasing (st->dpy, st->copy_gc, st->anti_alias_p);
153   jwxyz_XSetAntiAliasing (st->dpy, st->thick_line_gc, st->anti_alias_p);
154   return True;
155 }
156
157
158 static void
159 primitives_mini_rect(struct testx11 *st, Drawable t, int x, int y)
160 {
161   XFillRectangle(st->dpy, t, st->copy_gc, x, y, 2, 2);
162 }
163
164 static void
165 images_copy_test (Display *dpy, Drawable src, Drawable dst, GC gc,
166                   int src_y, int dst_x, int dst_y, unsigned long cells)
167 {
168   XCopyArea(dpy, src, dst, gc, 0, src_y, 3, 2, dst_x, dst_y);
169
170   {
171     XImage *image = XGetImage(dpy, src, 0, src_y, 3, 2, cells, ZPixmap);
172     XPutImage(dpy, dst, gc, image, 0, 0, dst_x, dst_y + 2, 3, 2);
173     XDestroyImage(image);
174   }
175 }
176
177 static void
178 images_pattern (struct testx11 *st, Drawable d, unsigned y)
179 {
180   unsigned x;
181   for (x = 0; x != 3; ++x) {
182     XSetForeground(st->dpy, st->images_point_gc, st->rgb[x]);
183     XDrawPoint(st->dpy, d, st->images_point_gc, x, y);
184     XSetForeground(st->dpy, st->images_point_gc, st->rgb[2 - x]);
185     XFillRectangle(st->dpy, d, st->images_point_gc, x, y + 1, 1, 1);
186   }
187
188   images_copy_test (st->dpy, d, d, st->images_point_gc, y, 0, y + 2,
189                     st->rgb[0] | st->rgb[1] | st->rgb[2]);
190 }
191
192 static const unsigned tile_size = 16;
193 static const unsigned tile_count = 8;
194
195 static const unsigned preserve_size = 128;
196
197 static void
198 make_clip_mask (struct testx11 *st)
199 {
200   /* Activate this for extra Fun! */
201 # if 0
202   /* This is kind of slow, but that's OK, because this only happens on int
203      or during a resize.
204    */
205   unsigned w = st->xgwa.width, h = st->xgwa.height;
206   unsigned x, y;
207   Pixmap mask = XCreatePixmap (st->dpy, st->clip_mask_tile, w, h, 1);
208
209   for (y = 0; y < h; y += 8) {
210     for (x = 0; x < w; x += 8) {
211       XCopyArea (st->dpy, st->clip_mask_tile, mask, st->mono_gc,
212                  0, 0, 8, 8, x, y);
213     }
214   }
215
216   XSetClipMask (st->dpy, st->xor_gc, mask);
217   XFreePixmap (st->dpy, mask);
218 # endif
219 }
220
221
222 static void
223 colorbars (struct testx11 *st)
224 {
225   Pixmap logo_mask = 0;
226   Pixmap logo = image_data_to_pixmap (st->dpy, st->win,
227                                       logo_180_png, sizeof(logo_180_png),
228                                       0, 0, &logo_mask);
229   draw_colorbars (st->xgwa.screen, st->xgwa.visual, st->win,
230                   st->xgwa.colormap, 0, 0, st->xgwa.width, st->xgwa.height,
231                   logo, logo_mask);
232   XFreePixmap (st->dpy, logo);
233   XFreePixmap (st->dpy, logo_mask);
234 }
235
236
237 static void *
238 testx11_init (Display *dpy, Window win)
239 {
240   static const char backdrop_pattern[] = {
241     '\xff', '\x00', '\xfe', '\x01', '\xfc', '\x03', '\xf8', '\x07', /* Zig */
242     '\xf0', '\x0f', '\xf8', '\x07', '\xfc', '\x03', '\xfe', '\x01', /* Zag */
243     '\xff', '\x00', '\xfe', '\x01', '\xfc', '\x03', '\xf8', '\x07', /* etc. */
244     '\xf0', '\x0f', '\xf8', '\x07', '\xfc', '\x03', '\xfe', '\x01',
245     '\xff', '\x00', '\xfe', '\x01', '\xfc', '\x03', '\xf8', '\x07',
246     '\xf0', '\x0f', '\xf8', '\x07', '\xfc', '\x03', '\xfe', '\x01',
247     '\xff', '\x00', '\xfe', '\x01', '\xfc', '\x03', '\xf8', '\x07',
248     '\xf0', '\x0f', '\xf8', '\x07', '\xfc', '\x03', '\xfe', '\x01',
249   };
250
251   static const char clip_bits[] = {
252     '\x33', '\x33', '\xcc', '\xcc', '\x33', '\x33', '\xcc', '\xcc'
253   };
254
255   struct testx11 *st = (struct testx11 *) malloc (sizeof(*st));
256   XGCValues gcv;
257
258   check_no_mem (st);
259
260   st->dpy = dpy;
261   st->win = win;
262
263   XGetWindowAttributes (dpy, win, &st->xgwa);
264
265   create_backbuffer (st);
266
267   st->rgb[0]           = alloc_color (st, 0xffff, 0x0000, 0x0000);
268   st->rgb[1]           = alloc_color (st, 0x0000, 0xffff, 0x0000);
269   st->rgb[2]           = alloc_color (st, 0x0000, 0x0000, 0xffff);
270   st->salmon           = alloc_color (st, 0xffff, 0x7fff, 0x7fff);
271   st->magenta          = alloc_color (st, 0xffff, 0x0000, 0xffff);
272   st->gray50           = alloc_color (st, 0x8000, 0x8000, 0x8000);
273   st->dark_slate_gray1 = alloc_color (st, 0x8000, 0xffff, 0xffff);
274   st->cyan             = alloc_color (st, 0x0000, 0xffff, 0xffff);
275
276   st->backdrop_tile = XCreatePixmapFromBitmapData (dpy, win,
277                                                    (char *) backdrop_pattern,
278                                                    tile_size, tile_size * 2,
279                                                    1, 0, 1);
280
281   st->clip_mask_tile = XCreatePixmapFromBitmapData (dpy, win,
282                                                     (char *) clip_bits, 8, 8,
283                                                     1, 0, 1);
284
285   {
286     unsigned s = tile_size * tile_count;
287     st->backdrop_scratch = XCreatePixmap (dpy, win, s, s, st->xgwa.depth);
288   }
289
290   st->backdrop_ncolors = countof (st->backdrop_colors);
291   make_color_loop (st->xgwa.screen, st->xgwa.visual, st->xgwa.colormap,
292                    180, 1, 0.5, 210, 1, 0.5, 240, 1, 0.5,
293                    st->backdrop_colors, &st->backdrop_ncolors, True, NULL);
294
295   st->frame = 0;
296
297 # ifdef HAVE_ANDROID
298   st->mode = mode_primitives;
299 # else
300   st->mode = mode_welcome;
301 # endif
302
303   st->anti_alias_p = False;
304
305   gcv.function = GXcopy;
306   gcv.foreground = st->cyan;
307   gcv.background = st->magenta;
308   gcv.line_width = 0;
309   gcv.cap_style = CapRound;
310   /* TODO: Real X11 uses fixed by default. */
311   gcv.font = XLoadFont (dpy, "fixed");
312   st->copy_gc = XCreateGC (dpy, win,
313                            GCFunction | GCForeground | GCBackground
314                              | GCLineWidth | GCCapStyle | GCFont, &gcv);
315
316   gcv.foreground = BlackPixelOfScreen (st->xgwa.screen);
317   st->backdrop_black_gc = XCreateGC (dpy, win, GCForeground, &gcv);
318
319   gcv.foreground = alloc_color (st, 0x8000, 0x4000, 0xffff);
320   gcv.line_width = 8;
321   gcv.cap_style = CapProjecting;
322   st->thick_line_gc = XCreateGC (dpy, win,
323                                  GCForeground | GCLineWidth | GCCapStyle,
324                                  &gcv);
325
326   gcv.function = GXxor;
327   st->xor_gc = XCreateGC (dpy, win, GCFunction, &gcv);
328
329   st->images_point_gc = XCreateGC (dpy, win, 0, NULL);
330
331   st->graph_gc = XCreateGC (dpy, win, 0, &gcv);
332
333   st->mono_gc = XCreateGC (dpy, st->clip_mask_tile, 0, &gcv);
334
335   st->copy_pix64 = XCreatePixmap(dpy, win, 64, 64, st->xgwa.depth);
336
337   st->primitives_mini_pix = XCreatePixmap (dpy, win, 16, 24, st->xgwa.depth);
338
339   {
340     static const char text[] = "Welcome from testx11_init().";
341     colorbars (st);
342     XDrawString (dpy, win, st->copy_gc, 16, 16, text, countof (text) - 1);
343   }
344
345   make_clip_mask (st);
346
347   st->preserve[0] =
348     XCreatePixmap(dpy, win, preserve_size, preserve_size, st->xgwa.depth);
349   st->preserve[1] =
350     XCreatePixmap(dpy, win, preserve_size, preserve_size, st->xgwa.depth);
351
352   toggle_antialiasing (st);
353
354   st->erase = NULL;
355
356   jwxyz_assert_display (dpy);
357
358   st->rot = make_rotator (2, 2, 2, 2, 0.01, False);
359   return st;
360 }
361
362
363 static void
364 backdrop (struct testx11 *st, Drawable t)
365 {
366   unsigned w = st->xgwa.width, h = st->xgwa.height;
367   unsigned x, y;
368
369   for (y = 0; y != tile_count; ++y) {
370     const float s0 = 2 * M_PI / tile_count;
371     float y_fac = sin ((y + st->frame / 16.0) * s0);
372     for (x = 0; x != tile_count; ++x) {
373       int c = ((sin ((x + st->frame / 8.0) * s0) * y_fac) - 1) / 2 *
374               st->backdrop_ncolors / 2;
375       c = (c + st->frame) % st->backdrop_ncolors;
376       XSetBackground (st->dpy, st->backdrop_black_gc,
377                       st->backdrop_colors[c].pixel);
378       XCopyPlane (st->dpy, st->backdrop_tile, st->backdrop_scratch,
379                   st->backdrop_black_gc, 0, st->frame % tile_size,
380                   tile_size, tile_size, x * tile_size, y * tile_size, 1);
381     }
382   }
383
384   /* XFillRectangle (st->dpy, t, st->backdrop_black_gc, 0, 0, w, h); */
385
386   for (y = 0; y < h; y += tile_count * tile_size) {
387     for (x = 0; x < w; x += tile_count * tile_size) {
388       XCopyArea (st->dpy, st->backdrop_scratch, t, st->copy_gc, 0, 0,
389                  tile_count * tile_size, tile_count * tile_size, x, y);
390     }
391   }
392 }
393
394 static const unsigned button_pad = 16;
395 static const unsigned button_size = 64;
396
397
398 static void
399 testx11_graph_rotator (struct testx11 *st)
400 {
401   double x, y, z;
402
403   int boxw = st->xgwa.width / 3;
404   int boxh = (st->xgwa.height - (22 * 5)) / 4;
405   int boxx = st->xgwa.width - boxw - 20;
406   int boxy = st->xgwa.height - boxh - 20;
407   
408   /* position */
409
410   get_position (st->rot, &x, &y, &z, True);
411   if (x < 0 || x >= 1 || y < 0 || y >= 1 || z < 0 || z >= 1) abort();
412       
413
414   XSetForeground (st->dpy, st->graph_gc, st->dark_slate_gray1);
415   XDrawRectangle (st->dpy, st->win, st->graph_gc,
416                   boxx-1, boxy-1, boxw+2, boxh+2);
417
418   XCopyArea (st->dpy, st->win, st->win, st->graph_gc,
419              boxx+1, boxy, boxw-1, boxh, boxx, boxy);
420
421   XSetForeground (st->dpy, st->graph_gc, BlackPixelOfScreen (st->xgwa.screen));
422   XDrawLine (st->dpy, st->win, st->graph_gc,
423              boxx + boxw - 1, boxy,
424              boxx + boxw - 1, boxy + boxh);
425
426   XSetForeground (st->dpy, st->graph_gc, st->salmon);
427   XDrawPoint (st->dpy, st->win, st->graph_gc,
428               boxx + boxw - 1,
429               boxy + boxh - 1 - (int) (x * (boxh - 1)));
430
431   XSetForeground (st->dpy, st->graph_gc, st->magenta);
432   XDrawPoint (st->dpy, st->win, st->graph_gc,
433               boxx + boxw - 1,
434               boxy + boxh - 1 - (int) (y * (boxh - 1)));
435
436   XSetForeground (st->dpy, st->graph_gc, st->gray50);
437   XDrawPoint (st->dpy, st->win, st->graph_gc,
438               boxx + boxw - 1,
439               boxy + boxh - 1 - (int) (z * (boxh - 1)));
440
441   /* spin */
442
443   get_rotation (st->rot, &x, &y, &z, True);
444   if (x < 0 || x >= 1 || y < 0 || y >= 1 || z < 0 || z >= 1) abort();
445
446   /* put 0 in the middle */
447   x += 0.5; if (x > 1) x--;
448   y += 0.5; if (y > 1) y--;
449   z += 0.5; if (z > 1) z--;
450
451
452   boxy -= boxh + 20;
453
454   XSetForeground (st->dpy, st->graph_gc, st->dark_slate_gray1);
455   XDrawRectangle (st->dpy, st->win, st->graph_gc,
456                   boxx-1, boxy-1, boxw+2, boxh+2);
457
458   XCopyArea (st->dpy, st->win, st->win, st->graph_gc,
459              boxx+1, boxy, boxw-1, boxh, boxx, boxy);
460
461   XSetForeground (st->dpy, st->graph_gc, BlackPixelOfScreen (st->xgwa.screen));
462   XDrawLine (st->dpy, st->win, st->graph_gc,
463              boxx + boxw - 1, boxy,
464              boxx + boxw - 1, boxy + boxh);
465
466   XSetForeground (st->dpy, st->graph_gc, st->magenta);
467   XDrawPoint (st->dpy, st->win, st->graph_gc,
468               boxx + boxw - 1,
469               boxy + boxh - 1 - (int) (x * (boxh - 1)));
470
471
472   boxy -= boxh + 20;
473
474   XSetForeground (st->dpy, st->graph_gc, st->dark_slate_gray1);
475   XDrawRectangle (st->dpy, st->win, st->graph_gc,
476                   boxx-1, boxy-1, boxw+2, boxh+2);
477
478   XCopyArea (st->dpy, st->win, st->win, st->graph_gc,
479              boxx+1, boxy, boxw-1, boxh, boxx, boxy);
480
481   XSetForeground (st->dpy, st->graph_gc, BlackPixelOfScreen (st->xgwa.screen));
482   XDrawLine (st->dpy, st->win, st->graph_gc,
483              boxx + boxw - 1, boxy,
484              boxx + boxw - 1, boxy + boxh);
485
486   XSetForeground (st->dpy, st->graph_gc, st->magenta);
487   XDrawPoint (st->dpy, st->win, st->graph_gc,
488               boxx + boxw - 1,
489               boxy + boxh - 1 - (int) (y * (boxh - 1)));
490
491
492   boxy -= boxh + 20;
493
494   XSetForeground (st->dpy, st->graph_gc, st->dark_slate_gray1);
495   XDrawRectangle (st->dpy, st->win, st->graph_gc,
496                   boxx-1, boxy-1, boxw+2, boxh+2);
497
498   XCopyArea (st->dpy, st->win, st->win, st->graph_gc,
499              boxx+1, boxy, boxw-1, boxh, boxx, boxy);
500
501   XSetForeground (st->dpy, st->graph_gc, BlackPixelOfScreen (st->xgwa.screen));
502   XDrawLine (st->dpy, st->win, st->graph_gc,
503              boxx + boxw - 1, boxy,
504              boxx + boxw - 1, boxy + boxh);
505
506   XSetForeground (st->dpy, st->graph_gc, st->magenta);
507   XDrawPoint (st->dpy, st->win, st->graph_gc,
508               boxx + boxw - 1,
509               boxy + boxh - 1 - (int) (z * (boxh - 1)));
510 }
511
512
513 /* Draws a blinking box in what should be the upper left corner of the
514    device, as physically oriented. The box is taller than it is wide.
515  */
516 static void
517 testx11_show_orientation (struct testx11 *st)
518 {
519 #ifdef HAVE_MOBILE
520   int x, y;
521   int w = st->xgwa.width;
522   int h = st->xgwa.height;
523   int ww = 15;
524   int hh = ww*2;
525   static int tick = 0;
526   static int oo = -1;
527   int o = (int) current_device_rotation ();
528
529   if (o != oo) {
530 //    fprintf (stderr,"ROT %d -> %d\n", oo, o);
531     oo = o;
532   }
533
534   switch (o) {
535   case    0: case  360: x = 0;    y = 0;    w = ww; h = hh; break;
536   case   90: case -270: x = 0;    y = h-ww; w = hh; h = ww; break;
537   case  -90: case  270: x = w-hh; y = 0;    w = hh; h = ww; break;
538   case  180: case -180: x = w-ww; y = h-hh; w = ww; h = hh; break;
539   default: return;
540   }
541
542   if (++tick > 20) tick = 0;
543
544   XSetForeground (st->dpy, st->graph_gc, 
545                   (tick > 10
546                    ? WhitePixelOfScreen (st->xgwa.screen)
547                    : BlackPixelOfScreen (st->xgwa.screen)));
548   XFillRectangle (st->dpy, st->win, st->graph_gc,
549                   x, y, w, h);
550 #endif
551 }
552
553
554 static unsigned long
555 testx11_draw (Display *dpy, Window win, void *st_raw)
556 {
557   struct testx11 *st = (struct testx11 *) st_raw;
558   unsigned w = st->xgwa.width, h = st->xgwa.height;
559 # ifdef HAVE_JWXYZ
560   Drawable t = win;
561 # else
562   Drawable t = st->mode == mode_primitives ? st->backbuffer : win;
563 # endif
564   unsigned i;
565
566   assert (dpy == st->dpy);
567   assert (win == st->win);
568
569   jwxyz_assert_display (dpy);
570
571   XSetWindowBackground (dpy, win, st->gray50);
572
573   switch (st->mode)
574   {
575   case mode_primitives:
576     backdrop (st, t);
577
578     XDrawPoint (dpy, t, st->thick_line_gc, 0, 0);
579     XDrawPoint (dpy, t, st->thick_line_gc, 0, 1);
580     XDrawPoint (dpy, t, st->thick_line_gc, 1, 0);
581     XDrawPoint (dpy, t, st->thick_line_gc, 1, 1);
582
583     primitives_mini_rect (st, t, 2, 0);
584     primitives_mini_rect (st, t, 0, 2);
585     primitives_mini_rect (st, t, 4, 2);
586     primitives_mini_rect (st, t, 2, 4);
587
588     primitives_mini_rect (st, t, 30, -2);
589     primitives_mini_rect (st, t, 32, 0);
590
591     primitives_mini_rect (st, t, 30, h - 2);
592     primitives_mini_rect (st, t, 32, h);
593
594     primitives_mini_rect (st, t, -2, 30);
595     primitives_mini_rect (st, t, 0, 32);
596     primitives_mini_rect (st, t, w - 2, 30);
597     primitives_mini_rect (st, t, w, 32);
598
599     primitives_mini_rect (st, t, w / 2 + 4, h / 2 + 4);
600
601     XFillArc (dpy, t, st->copy_gc, 16, 16, 256, 256, 45 * 64, -135 * 64);
602     /* XCopyArea(dpy, t, t, st->copy_gc, 48, 48, 128, 128, 64, 64); */
603
604     /* Center */
605     XDrawPoint (dpy, t, st->copy_gc, w / 2, h / 2);
606
607     /* Top/bottom */
608     XDrawPoint (dpy, t, st->copy_gc, w / 2, 0);
609     XDrawPoint (dpy, t, st->copy_gc, w / 2, h - 1);
610
611     XDrawPoint (dpy, t, st->copy_gc, 16, -1);
612     XDrawPoint (dpy, t, st->copy_gc, 17, 0);
613
614     XDrawPoint (dpy, t, st->copy_gc, 15, h - 2);
615     XDrawPoint (dpy, t, st->copy_gc, 16, h - 1);
616     XDrawPoint (dpy, t, st->copy_gc, 17, h);
617
618     {
619       XPoint *points = malloc (sizeof(XPoint) * h);
620       XSegment *lines = malloc (sizeof(XSegment) * h);
621       XPoint *points2 = malloc (sizeof(XPoint) * h);
622       for(i = 0; i != h; ++i)
623       {
624         points[i].x = 8 + (i & 1);
625         points[i].y = i;
626
627         lines[i].x1 = 48 + (i & 1) * 4;
628         lines[i].y1 = i;
629         lines[i].x2 = 50;
630         lines[i].y2 = i;
631
632         points2[i].x = 24 + (i & 1);
633         points2[i].y = i;
634         /* XFillRectangle(st->dpy, t, st->copy_gc, 24 + (i & 1), i, 1, 1); */
635       }
636
637       XDrawPoints (dpy, t, st->copy_gc, points, h, CoordModeOrigin);
638       XDrawSegments (dpy, t, st->copy_gc, lines, h);
639       XDrawPoints (dpy, t, st->copy_gc, points2, h, CoordModeOrigin);
640       free (lines);
641       free (points);
642       free (points2);
643     }
644
645     /* Left/right */
646     XDrawPoint (dpy, t, st->copy_gc, -1, 16);
647     XDrawPoint (dpy, t, st->copy_gc, 0, 17);
648     XDrawPoint (dpy, t, st->copy_gc, w - 1, 16);
649     XDrawPoint (dpy, t, st->copy_gc, w, 17);
650
651     {
652       XPoint *points = malloc(sizeof(XPoint) * w);
653       XSegment *lines = malloc(sizeof(XSegment) * w);
654       XPoint *points2 = malloc(sizeof(XPoint) * w);
655       for(i = 0; i != w; ++i)
656       {
657         points[i].x = i;
658         points[i].y = 8 + (i & 1);
659         lines[i].x1 = i;
660         lines[i].y1 = 48 + (i & 1) * 4;
661         lines[i].x2 = i;
662         lines[i].y2 = 50;
663
664         points2[i].x = i;
665         points2[i].y = 24 + (i & 1);
666         /* XFillRectangle(dpy, t, st->copy_gc, i, 24 + (i & 1), 1, 1); */
667       }
668
669       XDrawPoints (dpy, t, st->copy_gc, points, w, CoordModeOrigin);
670       XDrawSegments (dpy, t, st->copy_gc, lines, w);
671       {
672         /* Thick purple lines */
673         XSegment seg[2] =
674         {
675           {31, 31, 31, 31}, /* TODO: This should not be rotated in Cocoa. */
676           {42, 31, 42, 42}
677         };
678         XDrawSegments (dpy, t, st->thick_line_gc, seg, 2);
679
680         XDrawLine (dpy, t, st->thick_line_gc, 58, 43, 64, 43);
681         XDrawLine (dpy, t, st->thick_line_gc, 73, 43, 80, 43);
682       }
683
684       XDrawPoints (dpy, t, st->copy_gc, points2, w, CoordModeOrigin);
685       free (points);
686       free (points2);
687       free (lines);
688     }
689
690     XDrawLine (dpy, t, st->copy_gc, 54, 11, 72, 22);
691
692     {
693       XPoint vertices[] = {{5, 5}, {5, 8}, {8, 8}, {8, 5}};
694       XFillPolygon (dpy, t, st->copy_gc, vertices, 4, Convex, CoordModeOrigin);
695     }
696
697     {
698       XDrawRectangle (dpy, t, st->copy_gc, 11, 11, 11, 11);
699       XFillRectangle (dpy, t, st->copy_gc, 13, 13, 8, 8);
700     }
701
702     /* Several ~16 pixel boxes in a row. */
703
704     /* Box 0 */
705     {
706       XDrawRectangle (dpy, t, st->copy_gc, 54, 54, 16, 16);
707       XDrawRectangle (dpy, t, st->copy_gc, 55, 55, 14, 14);
708
709       XDrawPoint (dpy, t, st->thick_line_gc, 56, 56);
710       XDrawPoint (dpy, t, st->copy_gc, 57, 56);
711     }
712
713     /* Box 1 */
714     XCopyArea (dpy, t, t, st->copy_gc, 55, 55, 15, 15, 72, 55);
715
716     /* Box 2 */
717     {
718       XImage *image = XGetImage(st->dpy, t, 55, 55, 15, 15, 0xffffff, ZPixmap);
719       XPutPixel(image, 2, 0, 0x00000000);
720       XPutImage (dpy, t, st->copy_gc, image, 0, 0, 88, 55, 15, 15);
721       XDestroyImage(image);
722     }
723
724     /* Box 3 */
725
726     {
727       XCopyArea (dpy, t, st->primitives_mini_pix, st->copy_gc,
728                  104, 55, 16, 16, 0, 0);
729       /* XCopyArea (dpy, t, st->primitives_mini_pix, st->copy_gc,
730                     105, 56, 14, 14, 1, 1);
731          XCopyArea (dpy, t, st->primitives_mini_pix, st->copy_gc,
732                     1, 1, 14, 14, 1, 1);
733        */
734
735       /* This point gets hidden. */
736       XDrawPoint (dpy, t, st->copy_gc, 104 + 8, 55 + 8);
737
738       XDrawPoint (dpy, st->primitives_mini_pix, st->copy_gc, 0, 0);
739       XDrawPoint (dpy, st->primitives_mini_pix, st->copy_gc, 1, 0);
740       XDrawPoint (dpy, st->primitives_mini_pix, st->copy_gc, 15, 15);
741       XDrawRectangle (dpy, st->primitives_mini_pix, st->copy_gc,
742                       1, 1, 13, 13);
743       XCopyArea (dpy, st->primitives_mini_pix, t, st->copy_gc,
744                  0, 0, 16, 16, 104, 55);
745     }
746
747     {
748       XDrawLine (dpy, t, st->copy_gc, 11, 28, 22, 28);
749       XDrawLine (dpy, t, st->copy_gc, 12, 27, 12, 46);
750       XDrawLine (dpy, t, st->copy_gc, 14, 30, 14, 30);
751     }
752
753     XDrawArc (dpy, t, st->copy_gc, 27, 11, 19, 11, 0, 360 * 64);
754
755     XDrawRectangle (dpy, t, st->copy_gc, 54, 73, 64, 64);
756     XFillArc (dpy, t, st->copy_gc, 56, 75, 60, 60, 0, 360 * 64);
757     /* XDrawArc (dpy, t, st->thick_line_gc, 56, 75, 60, 60, 0, 360 * 64); */
758
759     XClearArea (dpy, win, 121, 55, 16, 16, False);
760     break;
761
762   case mode_images:
763     backdrop (st, t);
764
765     /* if(w >= 9 && h >= 10) */
766     {
767 #ifdef HAVE_ANDROID
768       /* Draw below the status bar. */
769       const unsigned y = 64;
770 #else
771       const unsigned y = 0;
772 #endif
773
774       Screen *screen = st->xgwa.screen;
775       Visual *visual = st->xgwa.visual;
776       Pixmap pixmap = XCreatePixmap (dpy, t, 3, 10,
777                                      visual_depth (screen, visual));
778       unsigned long cells = cells = st->rgb[0] | st->rgb[1] | st->rgb[2];
779
780       {
781         XSetForeground (dpy, st->images_point_gc, st->salmon);
782         XDrawPoint (dpy, t, st->images_point_gc, 0, h - 1);
783         XDrawLine (dpy, t, st->images_point_gc, 0, y - 1, 8, y - 1);
784       }
785
786       images_pattern (st, t, y);
787       images_pattern (st, pixmap, 0);
788       /* Here's a good spot to verify that the pixmap contains the right colors
789          at the top.
790        */
791       images_copy_test (dpy, t, pixmap, st->copy_gc, y, 0, 6, cells);
792
793       XCopyArea (dpy, pixmap, t, st->copy_gc, 0, 0, 3, 10, 3, y);
794
795       {
796         XImage *image = XGetImage (dpy, pixmap, 0, 0, 3, 10, cells, ZPixmap);
797         XPutImage (dpy, t, st->copy_gc, image, 0, 0, 6, y, 3, 10);
798         XDestroyImage (image);
799       }
800
801       XFreePixmap (dpy, pixmap);
802       XSync (dpy, False);
803     }
804     break;
805
806   case mode_copy:
807     backdrop (st, win);
808
809     /* X.org isn't making a whole lot of sense here. */
810
811     Bool use_copy = (st->frame / 20) & 1;
812
813     {
814       GC gc = use_copy ? st->copy_gc : st->xor_gc;
815
816       XSetWindowBackground (dpy, t, st->magenta);
817       XCopyArea (dpy, t, t, gc, -2, -2, 40, 40, 20, 20);
818
819       if (!use_copy)
820         XCopyArea (st->dpy, t, t, gc, -20, h - 20, 40, 40, 20, h - 60);
821       XCopyArea (dpy, t, t, gc, w - 38, h - 38, 40, 40, w - 60, h - 60);
822       XCopyArea (dpy, t, t, gc, w - 20, -20, 40, 40, w - 60, 20);
823
824       XSetWindowBackground (dpy, t, st->gray50);
825       XCopyArea (st->dpy, t, t, gc, -20, 64, 40, 40, 20, 64);
826
827       XSetWindowBackground (dpy, t, st->dark_slate_gray1);
828       XCopyArea (st->dpy, t, t, gc, -20, 112, 40, 40, 20, 112);
829     }
830
831     if (use_copy)
832     {
833       GC gc = st->copy_gc;
834       XCopyArea (st->dpy, t, st->copy_pix64, gc, 0, h - 64, 64, 64, 0, 0);
835
836       XSetForeground (st->dpy, st->xor_gc, st->rgb[1]);
837       XSetBackground (st->dpy, st->xor_gc, st->cyan);
838
839       /* XCopyArea (st->dpy, st->copy_pix64, st->copy_pix64, gc,
840                     32, 32, 64, 64, 0, 0);
841          XCopyArea (st->dpy, st->copy_pix64, t, gc, 0, 0, 64, 64, 4, h - 68);
842        */
843       XCopyArea (st->dpy, st->copy_pix64, t, gc, 32, 32, 128, 64, 0, h - 64);
844     }
845
846     break;
847
848   case mode_preserve:
849     backdrop (st, t);
850
851     if(!(st->frame % 10)) {
852       const unsigned r = 16;
853       unsigned n = st->frame / 10;
854       unsigned m = n >> 1;
855       XFillArc (st->dpy, st->preserve[n & 1],
856                 m & 1 ? st->copy_gc : st->thick_line_gc,
857                 NRAND(preserve_size) - r, NRAND(preserve_size) - r,
858                 r * 2, r * 2, 0, 360 * 64);
859     }
860
861     XCopyArea (st->dpy, st->preserve[0], t, st->copy_gc, 0, 0,
862                preserve_size, preserve_size, 0, 0);
863     XCopyArea (st->dpy, st->preserve[1], t, st->copy_gc, 0, 0,
864                preserve_size, preserve_size, preserve_size, 0);
865     XCopyArea (st->dpy, st->preserve[1], t, st->copy_gc, 0, 0,
866                preserve_size, preserve_size,
867                w - preserve_size / 2, preserve_size);
868     break;
869
870   case mode_erase:
871     if (!st->erase)
872       colorbars (st);
873     st->erase = erase_window(st->dpy, st->win, st->erase);
874     break;
875   }
876
877   /* Mode toggle buttons */
878   for (i = 1; i != mode_count; ++i) {
879     unsigned i0 = i - 1;
880     char str[32];
881     XRectangle button_dims;
882     button_dims.x = i0 * (button_pad + button_size) + button_pad;
883     button_dims.y = h - button_pad - button_size;
884     button_dims.width = button_size;
885     button_dims.height = button_size;
886     if (!st->mode)
887       XFillRectangles (dpy, t, st->backdrop_black_gc, &button_dims, 1);
888     XDrawRectangle (dpy, t, st->copy_gc, button_dims.x, button_dims.y,
889                     button_dims.width, button_dims.height);
890
891     XDrawString (dpy, t, st->copy_gc,
892                  button_dims.x + button_size / 2 - 3,
893                  h - button_pad - button_size / 2 + 13 / 2,
894                  str, sprintf(str, "%u", i));
895   }
896
897   if (t != win)
898     XCopyArea (dpy, t, win, st->copy_gc, 0, 0, w, h, 0, 0);
899
900   testx11_graph_rotator (st);
901   testx11_show_orientation (st);
902
903   ++st->frame;
904   return 1000000 / 20;
905 }
906
907 static void
908 testx11_reshape (Display *dpy, Window window, void *st_raw,
909                  unsigned int w, unsigned int h)
910 {
911   struct testx11 *st = (struct testx11 *)st_raw;
912   st->xgwa.width = w;
913   st->xgwa.height = h;
914 # ifndef HAVE_JWXYZ
915   XFreePixmap (st->dpy, st->backbuffer);
916 # endif
917   create_backbuffer (st);
918   make_clip_mask (st);
919 }
920
921 static Bool
922 testx11_event (Display *dpy, Window window, void *st_raw, XEvent *event)
923 {
924   struct testx11 *st = (struct testx11 *) st_raw;
925
926   Bool handled = False;
927
928   switch (event->xany.type)
929   {
930   case KeyPress:
931     {
932       KeySym keysym;
933       char c = 0;
934       XLookupString (&event->xkey, &c, 1, &keysym, 0);
935       if (c == ' ')
936         handled = toggle_antialiasing (st);
937
938       if (c >= '0' && c <= '9' && c < '0' + mode_count) {
939         st->mode = c - '0';
940         handled = True;
941       }
942     }
943     break;
944
945   case ButtonPress:
946     if (event->xbutton.y >= st->xgwa.height - button_pad * 2 - button_size) {
947       int i = (event->xbutton.x - button_pad / 2) / (button_pad + button_size) + 1;
948       if (i && i < mode_count) {
949         st->mode = i;
950         handled = True;
951       }
952     }
953
954     if (!handled)
955       handled = toggle_antialiasing (st);
956     break;
957   }
958
959   return handled;
960 }
961
962 static void
963 testx11_free (Display *dpy, Window window, void *st_raw)
964 {
965   /* Omitted for the sake of brevity. */
966 }
967
968 XSCREENSAVER_MODULE_2 ("TestX11", testx11, testx11)