From http://www.jwz.org/xscreensaver/xscreensaver-5.37.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
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, 24, 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       XPutPixel(image, 2, 0, 0x00000000);
694       XPutImage (dpy, t, st->copy_gc, image, 0, 0, 88, 55, 15, 15);
695       XDestroyImage(image);
696     }
697
698     /* Box 3 */
699
700     {
701       XCopyArea (dpy, t, st->primitives_mini_pix, st->copy_gc,
702                  104, 55, 16, 16, 0, 0);
703       /* XCopyArea (dpy, t, st->primitives_mini_pix, st->copy_gc,
704                     105, 56, 14, 14, 1, 1);
705          XCopyArea (dpy, t, st->primitives_mini_pix, st->copy_gc,
706                     1, 1, 14, 14, 1, 1);
707        */
708
709       /* This point gets hidden. */
710       XDrawPoint (dpy, t, st->copy_gc, 104 + 8, 55 + 8);
711
712       XDrawPoint (dpy, st->primitives_mini_pix, st->copy_gc, 0, 0);
713       XDrawPoint (dpy, st->primitives_mini_pix, st->copy_gc, 1, 0);
714       XDrawPoint (dpy, st->primitives_mini_pix, st->copy_gc, 15, 15);
715       XDrawRectangle (dpy, st->primitives_mini_pix, st->copy_gc,
716                       1, 1, 13, 13);
717       XCopyArea (dpy, st->primitives_mini_pix, t, st->copy_gc,
718                  0, 0, 16, 16, 104, 55);
719     }
720
721     {
722       XDrawLine (dpy, t, st->copy_gc, 11, 28, 22, 28);
723       XDrawLine (dpy, t, st->copy_gc, 12, 27, 12, 46);
724       XDrawLine (dpy, t, st->copy_gc, 14, 30, 14, 30);
725     }
726
727     XDrawArc (dpy, t, st->copy_gc, 27, 11, 19, 11, 0, 360 * 64);
728
729     XDrawRectangle (dpy, t, st->copy_gc, 54, 73, 64, 64);
730     XFillArc (dpy, t, st->copy_gc, 56, 75, 60, 60, 0, 360 * 64);
731     /* XDrawArc (dpy, t, st->thick_line_gc, 56, 75, 60, 60, 0, 360 * 64); */
732
733     XClearArea (dpy, win, 121, 55, 16, 16, False);
734     break;
735
736   case mode_images:
737     backdrop (st, t);
738
739     /* if(w >= 9 && h >= 10) */
740     {
741 #ifdef HAVE_ANDROID
742       /* Draw below the status bar. */
743       const unsigned y = 64;
744 #else
745       const unsigned y = 0;
746 #endif
747
748       Screen *screen = st->xgwa.screen;
749       Visual *visual = st->xgwa.visual;
750       Pixmap pixmap = XCreatePixmap (dpy, t, 3, 10,
751                                      visual_depth (screen, visual));
752       unsigned long cells = cells = st->rgb[0] | st->rgb[1] | st->rgb[2];
753
754       {
755         XSetForeground (dpy, st->images_point_gc, st->salmon);
756         XDrawPoint (dpy, t, st->images_point_gc, 0, h - 1);
757         XDrawLine (dpy, t, st->images_point_gc, 0, y - 1, 8, y - 1);
758       }
759
760       images_pattern (st, t, y);
761       images_pattern (st, pixmap, 0);
762       /* Here's a good spot to verify that the pixmap contains the right colors
763          at the top.
764        */
765       images_copy_test (dpy, t, pixmap, st->copy_gc, y, 0, 6, cells);
766
767       XCopyArea (dpy, pixmap, t, st->copy_gc, 0, 0, 3, 10, 3, y);
768
769       {
770         XImage *image = XGetImage (dpy, pixmap, 0, 0, 3, 10, cells, ZPixmap);
771         XPutImage (dpy, t, st->copy_gc, image, 0, 0, 6, y, 3, 10);
772         XDestroyImage (image);
773       }
774
775       XFreePixmap (dpy, pixmap);
776       XSync (dpy, False);
777     }
778     break;
779
780   case mode_copy:
781     backdrop (st, win);
782
783     /* X.org isn't making a whole lot of sense here. */
784
785     Bool use_copy = (st->frame / 20) & 1;
786
787     {
788       GC gc = use_copy ? st->copy_gc : st->xor_gc;
789
790       XSetWindowBackground (dpy, t, st->magenta);
791       XCopyArea (dpy, t, t, gc, -2, -2, 40, 40, 20, 20);
792
793       if (!use_copy)
794         XCopyArea (st->dpy, t, t, gc, -20, h - 20, 40, 40, 20, h - 60);
795       XCopyArea (dpy, t, t, gc, w - 38, h - 38, 40, 40, w - 60, h - 60);
796       XCopyArea (dpy, t, t, gc, w - 20, -20, 40, 40, w - 60, 20);
797
798       XSetWindowBackground (dpy, t, st->gray50);
799       XCopyArea (st->dpy, t, t, gc, -20, 64, 40, 40, 20, 64);
800
801       XSetWindowBackground (dpy, t, st->dark_slate_gray1);
802       XCopyArea (st->dpy, t, t, gc, -20, 112, 40, 40, 20, 112);
803     }
804
805     if (use_copy)
806     {
807       GC gc = st->copy_gc;
808       XCopyArea (st->dpy, t, st->copy_pix64, gc, 0, h - 64, 64, 64, 0, 0);
809
810       XSetForeground (st->dpy, st->xor_gc, st->rgb[1]);
811       XSetBackground (st->dpy, st->xor_gc, st->cyan);
812
813       /* XCopyArea (st->dpy, st->copy_pix64, st->copy_pix64, gc,
814                     32, 32, 64, 64, 0, 0);
815          XCopyArea (st->dpy, st->copy_pix64, t, gc, 0, 0, 64, 64, 4, h - 68);
816        */
817       XCopyArea (st->dpy, st->copy_pix64, t, gc, 32, 32, 128, 64, 0, h - 64);
818     }
819
820     break;
821
822   case mode_preserve:
823     backdrop (st, t);
824
825     if(!(st->frame % 10)) {
826       const unsigned r = 16;
827       unsigned n = st->frame / 10;
828       unsigned m = n >> 1;
829       XFillArc (st->dpy, st->preserve[n & 1],
830                 m & 1 ? st->copy_gc : st->thick_line_gc,
831                 NRAND(preserve_size) - r, NRAND(preserve_size) - r,
832                 r * 2, r * 2, 0, 360 * 64);
833     }
834
835     XCopyArea (st->dpy, st->preserve[0], t, st->copy_gc, 0, 0,
836                preserve_size, preserve_size, 0, 0);
837     XCopyArea (st->dpy, st->preserve[1], t, st->copy_gc, 0, 0,
838                preserve_size, preserve_size, preserve_size, 0);
839     XCopyArea (st->dpy, st->preserve[1], t, st->copy_gc, 0, 0,
840                preserve_size, preserve_size,
841                w - preserve_size / 2, preserve_size);
842     break;
843   }
844
845   /* Mode toggle buttons */
846   for (i = 1; i != mode_count; ++i) {
847     unsigned i0 = i - 1;
848     char str[32];
849     XRectangle button_dims;
850     button_dims.x = i0 * (button_pad + button_size) + button_pad;
851     button_dims.y = h - button_pad - button_size;
852     button_dims.width = button_size;
853     button_dims.height = button_size;
854     if (!st->mode)
855       XFillRectangles (dpy, t, st->backdrop_black_gc, &button_dims, 1);
856     XDrawRectangle (dpy, t, st->copy_gc, button_dims.x, button_dims.y,
857                     button_dims.width, button_dims.height);
858
859     XDrawString (dpy, t, st->copy_gc,
860                  button_dims.x + button_size / 2 - 3,
861                  h - button_pad - button_size / 2 + 13 / 2,
862                  str, sprintf(str, "%u", i));
863   }
864
865   if (t != win)
866     XCopyArea (dpy, t, win, st->copy_gc, 0, 0, w, h, 0, 0);
867
868   testx11_graph_rotator (st);
869   testx11_show_orientation (st);
870
871   ++st->frame;
872   return 1000000 / 20;
873 }
874
875 static void
876 testx11_reshape (Display *dpy, Window window, void *st_raw,
877                  unsigned int w, unsigned int h)
878 {
879   struct testx11 *st = (struct testx11 *)st_raw;
880   st->xgwa.width = w;
881   st->xgwa.height = h;
882 # ifndef HAVE_JWXYZ
883   XFreePixmap (st->dpy, st->backbuffer);
884 # endif
885   create_backbuffer (st);
886   make_clip_mask (st);
887 }
888
889 static Bool
890 testx11_event (Display *dpy, Window window, void *st_raw, XEvent *event)
891 {
892   struct testx11 *st = (struct testx11 *) st_raw;
893
894   Bool handled = False;
895
896   switch (event->xany.type)
897   {
898   case KeyPress:
899     {
900       KeySym keysym;
901       char c = 0;
902       XLookupString (&event->xkey, &c, 1, &keysym, 0);
903       if (c == ' ')
904         handled = toggle_antialiasing (st);
905
906       if (c >= '0' && c <= '9' && c < '0' + mode_count) {
907         st->mode = c - '0';
908         handled = True;
909       }
910     }
911     break;
912
913   case ButtonPress:
914     if (event->xbutton.y >= st->xgwa.height - button_pad * 2 - button_size) {
915       int i = (event->xbutton.x - button_pad / 2) / (button_pad + button_size) + 1;
916       if (i && i < mode_count) {
917         st->mode = i;
918         handled = True;
919       }
920     }
921
922     if (!handled)
923       handled = toggle_antialiasing (st);
924     break;
925   }
926
927   return handled;
928 }
929
930 static void
931 testx11_free (Display *dpy, Window window, void *st_raw)
932 {
933   /* Omitted for the sake of brevity. */
934 }
935
936 XSCREENSAVER_MODULE_2 ("TestX11", testx11, testx11)