17732d7b6fe9552a3378f44f2382125be135098f
[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       unsigned c = ((sin ((x + st->frame / 8.0) * s0) * y_fac) - 1) / 2 * st->backdrop_ncolors / 2;
374       c = (c + st->frame) % st->backdrop_ncolors;
375       XSetBackground (st->dpy, st->backdrop_black_gc,
376                       st->backdrop_colors[c].pixel);
377       XCopyPlane (st->dpy, st->backdrop_tile, st->backdrop_scratch,
378                   st->backdrop_black_gc, 0, st->frame % tile_size,
379                   tile_size, tile_size, x * tile_size, y * tile_size, 1);
380     }
381   }
382
383   /* XFillRectangle (st->dpy, t, st->backdrop_black_gc, 0, 0, w, h); */
384
385   for (y = 0; y < h; y += tile_count * tile_size) {
386     for (x = 0; x < w; x += tile_count * tile_size) {
387       XCopyArea (st->dpy, st->backdrop_scratch, t, st->copy_gc, 0, 0,
388                  tile_count * tile_size, tile_count * tile_size, x, y);
389     }
390   }
391 }
392
393 static const unsigned button_pad = 16;
394 static const unsigned button_size = 64;
395
396
397 static void
398 testx11_graph_rotator (struct testx11 *st)
399 {
400   double x, y, z;
401
402   int boxw = st->xgwa.width / 3;
403   int boxh = (st->xgwa.height - (22 * 5)) / 4;
404   int boxx = st->xgwa.width - boxw - 20;
405   int boxy = st->xgwa.height - boxh - 20;
406   
407   /* position */
408
409   get_position (st->rot, &x, &y, &z, True);
410   if (x < 0 || x >= 1 || y < 0 || y >= 1 || z < 0 || z >= 1) abort();
411       
412
413   XSetForeground (st->dpy, st->graph_gc, st->dark_slate_gray1);
414   XDrawRectangle (st->dpy, st->win, st->graph_gc,
415                   boxx-1, boxy-1, boxw+2, boxh+2);
416
417   XCopyArea (st->dpy, st->win, st->win, st->graph_gc,
418              boxx+1, boxy, boxw-1, boxh, boxx, boxy);
419
420   XSetForeground (st->dpy, st->graph_gc, BlackPixelOfScreen (st->xgwa.screen));
421   XDrawLine (st->dpy, st->win, st->graph_gc,
422              boxx + boxw - 1, boxy,
423              boxx + boxw - 1, boxy + boxh);
424
425   XSetForeground (st->dpy, st->graph_gc, st->salmon);
426   XDrawPoint (st->dpy, st->win, st->graph_gc,
427               boxx + boxw - 1,
428               boxy + boxh - 1 - (int) (x * (boxh - 1)));
429
430   XSetForeground (st->dpy, st->graph_gc, st->magenta);
431   XDrawPoint (st->dpy, st->win, st->graph_gc,
432               boxx + boxw - 1,
433               boxy + boxh - 1 - (int) (y * (boxh - 1)));
434
435   XSetForeground (st->dpy, st->graph_gc, st->gray50);
436   XDrawPoint (st->dpy, st->win, st->graph_gc,
437               boxx + boxw - 1,
438               boxy + boxh - 1 - (int) (z * (boxh - 1)));
439
440   /* spin */
441
442   get_rotation (st->rot, &x, &y, &z, True);
443   if (x < 0 || x >= 1 || y < 0 || y >= 1 || z < 0 || z >= 1) abort();
444
445   /* put 0 in the middle */
446   x += 0.5; if (x > 1) x--;
447   y += 0.5; if (y > 1) y--;
448   z += 0.5; if (z > 1) z--;
449
450
451   boxy -= boxh + 20;
452
453   XSetForeground (st->dpy, st->graph_gc, st->dark_slate_gray1);
454   XDrawRectangle (st->dpy, st->win, st->graph_gc,
455                   boxx-1, boxy-1, boxw+2, boxh+2);
456
457   XCopyArea (st->dpy, st->win, st->win, st->graph_gc,
458              boxx+1, boxy, boxw-1, boxh, boxx, boxy);
459
460   XSetForeground (st->dpy, st->graph_gc, BlackPixelOfScreen (st->xgwa.screen));
461   XDrawLine (st->dpy, st->win, st->graph_gc,
462              boxx + boxw - 1, boxy,
463              boxx + boxw - 1, boxy + boxh);
464
465   XSetForeground (st->dpy, st->graph_gc, st->magenta);
466   XDrawPoint (st->dpy, st->win, st->graph_gc,
467               boxx + boxw - 1,
468               boxy + boxh - 1 - (int) (x * (boxh - 1)));
469
470
471   boxy -= boxh + 20;
472
473   XSetForeground (st->dpy, st->graph_gc, st->dark_slate_gray1);
474   XDrawRectangle (st->dpy, st->win, st->graph_gc,
475                   boxx-1, boxy-1, boxw+2, boxh+2);
476
477   XCopyArea (st->dpy, st->win, st->win, st->graph_gc,
478              boxx+1, boxy, boxw-1, boxh, boxx, boxy);
479
480   XSetForeground (st->dpy, st->graph_gc, BlackPixelOfScreen (st->xgwa.screen));
481   XDrawLine (st->dpy, st->win, st->graph_gc,
482              boxx + boxw - 1, boxy,
483              boxx + boxw - 1, boxy + boxh);
484
485   XSetForeground (st->dpy, st->graph_gc, st->magenta);
486   XDrawPoint (st->dpy, st->win, st->graph_gc,
487               boxx + boxw - 1,
488               boxy + boxh - 1 - (int) (y * (boxh - 1)));
489
490
491   boxy -= boxh + 20;
492
493   XSetForeground (st->dpy, st->graph_gc, st->dark_slate_gray1);
494   XDrawRectangle (st->dpy, st->win, st->graph_gc,
495                   boxx-1, boxy-1, boxw+2, boxh+2);
496
497   XCopyArea (st->dpy, st->win, st->win, st->graph_gc,
498              boxx+1, boxy, boxw-1, boxh, boxx, boxy);
499
500   XSetForeground (st->dpy, st->graph_gc, BlackPixelOfScreen (st->xgwa.screen));
501   XDrawLine (st->dpy, st->win, st->graph_gc,
502              boxx + boxw - 1, boxy,
503              boxx + boxw - 1, boxy + boxh);
504
505   XSetForeground (st->dpy, st->graph_gc, st->magenta);
506   XDrawPoint (st->dpy, st->win, st->graph_gc,
507               boxx + boxw - 1,
508               boxy + boxh - 1 - (int) (z * (boxh - 1)));
509 }
510
511
512 /* Draws a blinking box in what should be the upper left corner of the
513    device, as physically oriented. The box is taller than it is wide.
514  */
515 static void
516 testx11_show_orientation (struct testx11 *st)
517 {
518 #ifdef HAVE_MOBILE
519   int x, y;
520   int w = st->xgwa.width;
521   int h = st->xgwa.height;
522   int ww = 15;
523   int hh = ww*2;
524   static int tick = 0;
525   static int oo = -1;
526   int o = (int) current_device_rotation ();
527
528   if (o != oo) {
529 //    fprintf (stderr,"ROT %d -> %d\n", oo, o);
530     oo = o;
531   }
532
533   switch (o) {
534   case    0: case  360: x = 0;    y = 0;    w = ww; h = hh; break;
535   case   90: case -270: x = 0;    y = h-ww; w = hh; h = ww; break;
536   case  -90: case  270: x = w-hh; y = 0;    w = hh; h = ww; break;
537   case  180: case -180: x = w-ww; y = h-hh; w = ww; h = hh; break;
538   default: return;
539   }
540
541   if (++tick > 20) tick = 0;
542
543   XSetForeground (st->dpy, st->graph_gc, 
544                   (tick > 10
545                    ? WhitePixelOfScreen (st->xgwa.screen)
546                    : BlackPixelOfScreen (st->xgwa.screen)));
547   XFillRectangle (st->dpy, st->win, st->graph_gc,
548                   x, y, w, h);
549 #endif
550 }
551
552
553 static unsigned long
554 testx11_draw (Display *dpy, Window win, void *st_raw)
555 {
556   struct testx11 *st = (struct testx11 *) st_raw;
557   unsigned w = st->xgwa.width, h = st->xgwa.height;
558 # ifdef HAVE_JWXYZ
559   Drawable t = win;
560 # else
561   Drawable t = st->mode == mode_primitives ? st->backbuffer : win;
562 # endif
563   unsigned i;
564
565   assert (dpy == st->dpy);
566   assert (win == st->win);
567
568   jwxyz_assert_display (dpy);
569
570   XSetWindowBackground (dpy, win, st->gray50);
571
572   switch (st->mode)
573   {
574   case mode_primitives:
575     backdrop (st, t);
576
577     XDrawPoint (dpy, t, st->thick_line_gc, 0, 0);
578     XDrawPoint (dpy, t, st->thick_line_gc, 0, 1);
579     XDrawPoint (dpy, t, st->thick_line_gc, 1, 0);
580     XDrawPoint (dpy, t, st->thick_line_gc, 1, 1);
581
582     primitives_mini_rect (st, t, 2, 0);
583     primitives_mini_rect (st, t, 0, 2);
584     primitives_mini_rect (st, t, 4, 2);
585     primitives_mini_rect (st, t, 2, 4);
586
587     primitives_mini_rect (st, t, 30, -2);
588     primitives_mini_rect (st, t, 32, 0);
589
590     primitives_mini_rect (st, t, 30, h - 2);
591     primitives_mini_rect (st, t, 32, h);
592
593     primitives_mini_rect (st, t, -2, 30);
594     primitives_mini_rect (st, t, 0, 32);
595     primitives_mini_rect (st, t, w - 2, 30);
596     primitives_mini_rect (st, t, w, 32);
597
598     primitives_mini_rect (st, t, w / 2 + 4, h / 2 + 4);
599
600     XFillArc (dpy, t, st->copy_gc, 16, 16, 256, 256, 45 * 64, -135 * 64);
601     /* XCopyArea(dpy, t, t, st->copy_gc, 48, 48, 128, 128, 64, 64); */
602
603     /* Center */
604     XDrawPoint (dpy, t, st->copy_gc, w / 2, h / 2);
605
606     /* Top/bottom */
607     XDrawPoint (dpy, t, st->copy_gc, w / 2, 0);
608     XDrawPoint (dpy, t, st->copy_gc, w / 2, h - 1);
609
610     XDrawPoint (dpy, t, st->copy_gc, 16, -1);
611     XDrawPoint (dpy, t, st->copy_gc, 17, 0);
612
613     XDrawPoint (dpy, t, st->copy_gc, 15, h - 2);
614     XDrawPoint (dpy, t, st->copy_gc, 16, h - 1);
615     XDrawPoint (dpy, t, st->copy_gc, 17, h);
616
617     {
618       XPoint *points = malloc (sizeof(XPoint) * h);
619       XSegment *lines = malloc (sizeof(XSegment) * h);
620       XPoint *points2 = malloc (sizeof(XPoint) * h);
621       for(i = 0; i != h; ++i)
622       {
623         points[i].x = 8 + (i & 1);
624         points[i].y = i;
625
626         lines[i].x1 = 48 + (i & 1) * 4;
627         lines[i].y1 = i;
628         lines[i].x2 = 50;
629         lines[i].y2 = i;
630
631         points2[i].x = 24 + (i & 1);
632         points2[i].y = i;
633         /* XFillRectangle(st->dpy, t, st->copy_gc, 24 + (i & 1), i, 1, 1); */
634       }
635
636       XDrawPoints (dpy, t, st->copy_gc, points, h, CoordModeOrigin);
637       XDrawSegments (dpy, t, st->copy_gc, lines, h);
638       XDrawPoints (dpy, t, st->copy_gc, points2, h, CoordModeOrigin);
639       free (lines);
640       free (points);
641       free (points2);
642     }
643
644     /* Left/right */
645     XDrawPoint (dpy, t, st->copy_gc, -1, 16);
646     XDrawPoint (dpy, t, st->copy_gc, 0, 17);
647     XDrawPoint (dpy, t, st->copy_gc, w - 1, 16);
648     XDrawPoint (dpy, t, st->copy_gc, w, 17);
649
650     {
651       XPoint *points = malloc(sizeof(XPoint) * w);
652       XSegment *lines = malloc(sizeof(XSegment) * w);
653       XPoint *points2 = malloc(sizeof(XPoint) * w);
654       for(i = 0; i != w; ++i)
655       {
656         points[i].x = i;
657         points[i].y = 8 + (i & 1);
658         lines[i].x1 = i;
659         lines[i].y1 = 48 + (i & 1) * 4;
660         lines[i].x2 = i;
661         lines[i].y2 = 50;
662
663         points2[i].x = i;
664         points2[i].y = 24 + (i & 1);
665         /* XFillRectangle(dpy, t, st->copy_gc, i, 24 + (i & 1), 1, 1); */
666       }
667
668       XDrawPoints (dpy, t, st->copy_gc, points, w, CoordModeOrigin);
669       XDrawSegments (dpy, t, st->copy_gc, lines, w);
670       {
671         /* Thick purple lines */
672         XSegment seg[2] =
673         {
674           {31, 31, 31, 31}, /* TODO: This should not be rotated in Cocoa. */
675           {42, 31, 42, 42}
676         };
677         XDrawSegments (dpy, t, st->thick_line_gc, seg, 2);
678
679         XDrawLine (dpy, t, st->thick_line_gc, 58, 43, 64, 43);
680         XDrawLine (dpy, t, st->thick_line_gc, 73, 43, 80, 43);
681       }
682
683       XDrawPoints (dpy, t, st->copy_gc, points2, w, CoordModeOrigin);
684       free (points);
685       free (points2);
686       free (lines);
687     }
688
689     XDrawLine (dpy, t, st->copy_gc, 54, 11, 72, 22);
690
691     {
692       XPoint vertices[] = {{5, 5}, {5, 8}, {8, 8}, {8, 5}};
693       XFillPolygon (dpy, t, st->copy_gc, vertices, 4, Convex, CoordModeOrigin);
694     }
695
696     {
697       XDrawRectangle (dpy, t, st->copy_gc, 11, 11, 11, 11);
698       XFillRectangle (dpy, t, st->copy_gc, 13, 13, 8, 8);
699     }
700
701     /* Several ~16 pixel boxes in a row. */
702
703     /* Box 0 */
704     {
705       XDrawRectangle (dpy, t, st->copy_gc, 54, 54, 16, 16);
706       XDrawRectangle (dpy, t, st->copy_gc, 55, 55, 14, 14);
707
708       XDrawPoint (dpy, t, st->thick_line_gc, 56, 56);
709       XDrawPoint (dpy, t, st->copy_gc, 57, 56);
710     }
711
712     /* Box 1 */
713     XCopyArea (dpy, t, t, st->copy_gc, 55, 55, 15, 15, 72, 55);
714
715     /* Box 2 */
716     {
717       XImage *image = XGetImage(st->dpy, t, 55, 55, 15, 15, 0xffffff, ZPixmap);
718       XPutPixel(image, 2, 0, 0x00000000);
719       XPutImage (dpy, t, st->copy_gc, image, 0, 0, 88, 55, 15, 15);
720       XDestroyImage(image);
721     }
722
723     /* Box 3 */
724
725     {
726       XCopyArea (dpy, t, st->primitives_mini_pix, st->copy_gc,
727                  104, 55, 16, 16, 0, 0);
728       /* XCopyArea (dpy, t, st->primitives_mini_pix, st->copy_gc,
729                     105, 56, 14, 14, 1, 1);
730          XCopyArea (dpy, t, st->primitives_mini_pix, st->copy_gc,
731                     1, 1, 14, 14, 1, 1);
732        */
733
734       /* This point gets hidden. */
735       XDrawPoint (dpy, t, st->copy_gc, 104 + 8, 55 + 8);
736
737       XDrawPoint (dpy, st->primitives_mini_pix, st->copy_gc, 0, 0);
738       XDrawPoint (dpy, st->primitives_mini_pix, st->copy_gc, 1, 0);
739       XDrawPoint (dpy, st->primitives_mini_pix, st->copy_gc, 15, 15);
740       XDrawRectangle (dpy, st->primitives_mini_pix, st->copy_gc,
741                       1, 1, 13, 13);
742       XCopyArea (dpy, st->primitives_mini_pix, t, st->copy_gc,
743                  0, 0, 16, 16, 104, 55);
744     }
745
746     {
747       XDrawLine (dpy, t, st->copy_gc, 11, 28, 22, 28);
748       XDrawLine (dpy, t, st->copy_gc, 12, 27, 12, 46);
749       XDrawLine (dpy, t, st->copy_gc, 14, 30, 14, 30);
750     }
751
752     XDrawArc (dpy, t, st->copy_gc, 27, 11, 19, 11, 0, 360 * 64);
753
754     XDrawRectangle (dpy, t, st->copy_gc, 54, 73, 64, 64);
755     XFillArc (dpy, t, st->copy_gc, 56, 75, 60, 60, 0, 360 * 64);
756     /* XDrawArc (dpy, t, st->thick_line_gc, 56, 75, 60, 60, 0, 360 * 64); */
757
758     XClearArea (dpy, win, 121, 55, 16, 16, False);
759     break;
760
761   case mode_images:
762     backdrop (st, t);
763
764     /* if(w >= 9 && h >= 10) */
765     {
766 #ifdef HAVE_ANDROID
767       /* Draw below the status bar. */
768       const unsigned y = 64;
769 #else
770       const unsigned y = 0;
771 #endif
772
773       Screen *screen = st->xgwa.screen;
774       Visual *visual = st->xgwa.visual;
775       Pixmap pixmap = XCreatePixmap (dpy, t, 3, 10,
776                                      visual_depth (screen, visual));
777       unsigned long cells = cells = st->rgb[0] | st->rgb[1] | st->rgb[2];
778
779       {
780         XSetForeground (dpy, st->images_point_gc, st->salmon);
781         XDrawPoint (dpy, t, st->images_point_gc, 0, h - 1);
782         XDrawLine (dpy, t, st->images_point_gc, 0, y - 1, 8, y - 1);
783       }
784
785       images_pattern (st, t, y);
786       images_pattern (st, pixmap, 0);
787       /* Here's a good spot to verify that the pixmap contains the right colors
788          at the top.
789        */
790       images_copy_test (dpy, t, pixmap, st->copy_gc, y, 0, 6, cells);
791
792       XCopyArea (dpy, pixmap, t, st->copy_gc, 0, 0, 3, 10, 3, y);
793
794       {
795         XImage *image = XGetImage (dpy, pixmap, 0, 0, 3, 10, cells, ZPixmap);
796         XPutImage (dpy, t, st->copy_gc, image, 0, 0, 6, y, 3, 10);
797         XDestroyImage (image);
798       }
799
800       XFreePixmap (dpy, pixmap);
801       XSync (dpy, False);
802     }
803     break;
804
805   case mode_copy:
806     backdrop (st, win);
807
808     /* X.org isn't making a whole lot of sense here. */
809
810     Bool use_copy = (st->frame / 20) & 1;
811
812     {
813       GC gc = use_copy ? st->copy_gc : st->xor_gc;
814
815       XSetWindowBackground (dpy, t, st->magenta);
816       XCopyArea (dpy, t, t, gc, -2, -2, 40, 40, 20, 20);
817
818       if (!use_copy)
819         XCopyArea (st->dpy, t, t, gc, -20, h - 20, 40, 40, 20, h - 60);
820       XCopyArea (dpy, t, t, gc, w - 38, h - 38, 40, 40, w - 60, h - 60);
821       XCopyArea (dpy, t, t, gc, w - 20, -20, 40, 40, w - 60, 20);
822
823       XSetWindowBackground (dpy, t, st->gray50);
824       XCopyArea (st->dpy, t, t, gc, -20, 64, 40, 40, 20, 64);
825
826       XSetWindowBackground (dpy, t, st->dark_slate_gray1);
827       XCopyArea (st->dpy, t, t, gc, -20, 112, 40, 40, 20, 112);
828     }
829
830     if (use_copy)
831     {
832       GC gc = st->copy_gc;
833       XCopyArea (st->dpy, t, st->copy_pix64, gc, 0, h - 64, 64, 64, 0, 0);
834
835       XSetForeground (st->dpy, st->xor_gc, st->rgb[1]);
836       XSetBackground (st->dpy, st->xor_gc, st->cyan);
837
838       /* XCopyArea (st->dpy, st->copy_pix64, st->copy_pix64, gc,
839                     32, 32, 64, 64, 0, 0);
840          XCopyArea (st->dpy, st->copy_pix64, t, gc, 0, 0, 64, 64, 4, h - 68);
841        */
842       XCopyArea (st->dpy, st->copy_pix64, t, gc, 32, 32, 128, 64, 0, h - 64);
843     }
844
845     break;
846
847   case mode_preserve:
848     backdrop (st, t);
849
850     if(!(st->frame % 10)) {
851       const unsigned r = 16;
852       unsigned n = st->frame / 10;
853       unsigned m = n >> 1;
854       XFillArc (st->dpy, st->preserve[n & 1],
855                 m & 1 ? st->copy_gc : st->thick_line_gc,
856                 NRAND(preserve_size) - r, NRAND(preserve_size) - r,
857                 r * 2, r * 2, 0, 360 * 64);
858     }
859
860     XCopyArea (st->dpy, st->preserve[0], t, st->copy_gc, 0, 0,
861                preserve_size, preserve_size, 0, 0);
862     XCopyArea (st->dpy, st->preserve[1], t, st->copy_gc, 0, 0,
863                preserve_size, preserve_size, preserve_size, 0);
864     XCopyArea (st->dpy, st->preserve[1], t, st->copy_gc, 0, 0,
865                preserve_size, preserve_size,
866                w - preserve_size / 2, preserve_size);
867     break;
868
869   case mode_erase:
870     if (!st->erase)
871       colorbars (st);
872     st->erase = erase_window(st->dpy, st->win, st->erase);
873     break;
874   }
875
876   /* Mode toggle buttons */
877   for (i = 1; i != mode_count; ++i) {
878     unsigned i0 = i - 1;
879     char str[32];
880     XRectangle button_dims;
881     button_dims.x = i0 * (button_pad + button_size) + button_pad;
882     button_dims.y = h - button_pad - button_size;
883     button_dims.width = button_size;
884     button_dims.height = button_size;
885     if (!st->mode)
886       XFillRectangles (dpy, t, st->backdrop_black_gc, &button_dims, 1);
887     XDrawRectangle (dpy, t, st->copy_gc, button_dims.x, button_dims.y,
888                     button_dims.width, button_dims.height);
889
890     XDrawString (dpy, t, st->copy_gc,
891                  button_dims.x + button_size / 2 - 3,
892                  h - button_pad - button_size / 2 + 13 / 2,
893                  str, sprintf(str, "%u", i));
894   }
895
896   if (t != win)
897     XCopyArea (dpy, t, win, st->copy_gc, 0, 0, w, h, 0, 0);
898
899   testx11_graph_rotator (st);
900   testx11_show_orientation (st);
901
902   ++st->frame;
903   return 1000000 / 20;
904 }
905
906 static void
907 testx11_reshape (Display *dpy, Window window, void *st_raw,
908                  unsigned int w, unsigned int h)
909 {
910   struct testx11 *st = (struct testx11 *)st_raw;
911   st->xgwa.width = w;
912   st->xgwa.height = h;
913 # ifndef HAVE_JWXYZ
914   XFreePixmap (st->dpy, st->backbuffer);
915 # endif
916   create_backbuffer (st);
917   make_clip_mask (st);
918 }
919
920 static Bool
921 testx11_event (Display *dpy, Window window, void *st_raw, XEvent *event)
922 {
923   struct testx11 *st = (struct testx11 *) st_raw;
924
925   Bool handled = False;
926
927   switch (event->xany.type)
928   {
929   case KeyPress:
930     {
931       KeySym keysym;
932       char c = 0;
933       XLookupString (&event->xkey, &c, 1, &keysym, 0);
934       if (c == ' ')
935         handled = toggle_antialiasing (st);
936
937       if (c >= '0' && c <= '9' && c < '0' + mode_count) {
938         st->mode = c - '0';
939         handled = True;
940       }
941     }
942     break;
943
944   case ButtonPress:
945     if (event->xbutton.y >= st->xgwa.height - button_pad * 2 - button_size) {
946       int i = (event->xbutton.x - button_pad / 2) / (button_pad + button_size) + 1;
947       if (i && i < mode_count) {
948         st->mode = i;
949         handled = True;
950       }
951     }
952
953     if (!handled)
954       handled = toggle_antialiasing (st);
955     break;
956   }
957
958   return handled;
959 }
960
961 static void
962 testx11_free (Display *dpy, Window window, void *st_raw)
963 {
964   /* Omitted for the sake of brevity. */
965 }
966
967 XSCREENSAVER_MODULE_2 ("TestX11", testx11, testx11)