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