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