1 /* testx11.c, Copyright (c) 2015 Dave Odell <dmo2118@gmail.com>
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
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.
14 * Almost no error checking in here. This is intentional.
17 #include "screenhack.h"
23 # define jwxyz_XSetAntiAliasing(dpy, gc, p)
26 #ifndef jwxyz_assert_display
27 # define jwxyz_assert_display(dpy)
30 #define countof(a) (sizeof(a) / sizeof(*(a)))
32 static const char *testx11_defaults [] = {
33 ".background: #a020f0", /* purple */
38 static XrmOptionDescRec testx11_options [] = {
42 #if defined HAVE_COCOA || defined HAVE_ANDROID
60 XWindowAttributes xgwa;
68 unsigned long rgb[3], salmon, magenta, gray50, dark_slate_gray1, cyan;
70 Pixmap backdrop_tile, clip_mask_tile, backdrop_scratch;
71 XColor backdrop_colors[64];
75 GC copy_gc, black_gc, mono_gc, thick_line_gc, xor_gc, point_gc;
78 Pixmap mini_pix, pix64;
85 fprintf(stderr, "%s: %s\n", progname, strerror(ENOMEM));
90 check_no_mem (void *ptr)
98 alloc_color (struct testx11 *st,
99 unsigned short r, unsigned short g, unsigned short b)
105 color.flags = DoRed | DoGreen | DoBlue;
106 XAllocColor (st->dpy, st->xgwa.colormap, &color);
111 create_backbuffer(struct testx11 *st)
114 st->backbuffer = XCreatePixmap (st->dpy, st->win,
115 st->xgwa.width, st->xgwa.height,
122 copy_test (Display *dpy, Drawable src, Drawable dst, GC gc, int x, int y,
125 XCopyArea(dpy, src, dst, gc, 0, 0, 3, 2, x, y);
128 XImage *image = XGetImage(dpy, src, 0, 0, 3, 2, cells, ZPixmap);
129 XPutImage(dpy, dst, gc, image, 0, 0, x, y + 2, 3, 2);
130 XDestroyImage(image);
135 test_pattern (struct testx11 *st, Drawable d)
138 for (x = 0; x != 3; ++x) {
139 XSetForeground(st->dpy, st->point_gc, st->rgb[x]);
140 XDrawPoint(st->dpy, d, st->point_gc, x, 0);
141 XSetForeground(st->dpy, st->point_gc, st->rgb[2 - x]);
142 XFillRectangle(st->dpy, d, st->point_gc, x, 1, 1, 1);
145 copy_test (st->dpy, d, d, st->point_gc, 0, 2,
146 st->rgb[0] | st->rgb[1] | st->rgb[2]);
150 mini_rect(struct testx11 *st, Drawable t, int x, int y)
152 XFillRectangle(st->dpy, t, st->copy_gc, x, y, 2, 2);
156 toggle_antialiasing (struct testx11 *st)
158 st->anti_alias_p ^= True;
159 jwxyz_XSetAntiAliasing(st->dpy, st->copy_gc, st->anti_alias_p);
163 static const unsigned tile_size = 16;
164 static const unsigned tile_count = 8;
167 make_clip_mask (struct testx11 *st)
169 /* Activate this for extra Fun! */
171 /* This is kind of slow, but that's OK, because this only happens on int
174 unsigned w = st->xgwa.width, h = st->xgwa.height;
176 Pixmap mask = XCreatePixmap (st->dpy, st->clip_mask_tile, w, h, 1);
178 for (y = 0; y < h; y += 8) {
179 for (x = 0; x < w; x += 8) {
180 XCopyArea (st->dpy, st->clip_mask_tile, mask, st->mono_gc,
185 XSetClipMask (st->dpy, st->xor_gc, mask);
186 XFreePixmap (st->dpy, mask);
192 testx11_init (Display *dpy, Window win)
194 static const char backdrop_pattern[] = {
195 '\xff', '\x00', '\xfe', '\x01', '\xfc', '\x03', '\xf8', '\x07', /* Zig */
196 '\xf0', '\x0f', '\xf8', '\x07', '\xfc', '\x03', '\xfe', '\x01', /* Zag */
197 '\xff', '\x00', '\xfe', '\x01', '\xfc', '\x03', '\xf8', '\x07', /* etc. */
198 '\xf0', '\x0f', '\xf8', '\x07', '\xfc', '\x03', '\xfe', '\x01',
199 '\xff', '\x00', '\xfe', '\x01', '\xfc', '\x03', '\xf8', '\x07',
200 '\xf0', '\x0f', '\xf8', '\x07', '\xfc', '\x03', '\xfe', '\x01',
201 '\xff', '\x00', '\xfe', '\x01', '\xfc', '\x03', '\xf8', '\x07',
202 '\xf0', '\x0f', '\xf8', '\x07', '\xfc', '\x03', '\xfe', '\x01',
205 static const char clip_bits[] = {
206 '\x33', '\x33', '\xcc', '\xcc', '\x33', '\x33', '\xcc', '\xcc'
209 struct testx11 *st = (struct testx11 *) malloc (sizeof(*st));
217 XGetWindowAttributes (dpy, win, &st->xgwa);
219 create_backbuffer (st);
221 st->rgb[0] = alloc_color (st, 0xffff, 0x0000, 0x0000);
222 st->rgb[1] = alloc_color (st, 0x0000, 0xffff, 0x0000);
223 st->rgb[2] = alloc_color (st, 0x0000, 0x0000, 0xffff);
224 st->salmon = alloc_color (st, 0xffff, 0x7fff, 0x7fff);
225 st->magenta = alloc_color (st, 0xffff, 0x0000, 0xffff);
226 st->gray50 = alloc_color (st, 0x8000, 0x8000, 0x8000);
227 st->dark_slate_gray1 = alloc_color (st, 0x8000, 0xffff, 0xffff);
228 st->cyan = alloc_color (st, 0x0000, 0xffff, 0xffff);
230 st->backdrop_tile = XCreatePixmapFromBitmapData (dpy, win,
231 (char *) backdrop_pattern,
232 tile_size, tile_size * 2,
235 st->clip_mask_tile = XCreatePixmapFromBitmapData (dpy, win,
236 (char *) clip_bits, 8, 8,
240 unsigned s = tile_size * tile_count;
241 st->backdrop_scratch = XCreatePixmap (dpy, win, s, s, st->xgwa.depth);
244 st->backdrop_ncolors = countof (st->backdrop_colors);
245 make_color_loop (st->xgwa.screen, st->xgwa.visual, st->xgwa.colormap,
246 180, 1, 0.5, 210, 1, 0.5, 240, 1, 0.5,
247 st->backdrop_colors, &st->backdrop_ncolors, True, NULL);
251 st->mode = mode_welcome;
253 gcv.function = GXcopy;
254 gcv.foreground = st->cyan;
255 gcv.background = st->magenta;
257 gcv.cap_style = CapRound;
258 /* TODO: Real X11 uses fixed by default. */
259 gcv.font = XLoadFont (dpy, "fixed");
260 st->copy_gc = XCreateGC (dpy, win, GCFunction | GCForeground | GCBackground | GCLineWidth | GCCapStyle | GCFont, &gcv);
262 gcv.foreground = BlackPixelOfScreen (st->xgwa.screen);
263 st->black_gc = XCreateGC (dpy, win, GCForeground, &gcv);
265 st->anti_alias_p = False;
266 jwxyz_XSetAntiAliasing (dpy, st->copy_gc, False);
268 gcv.foreground = alloc_color (st, 0x8000, 0x4000, 0xffff);
270 gcv.cap_style = CapProjecting;
271 st->thick_line_gc = XCreateGC (dpy, win, GCForeground | GCLineWidth | GCCapStyle, &gcv);
273 gcv.function = GXxor;
274 st->xor_gc = XCreateGC (dpy, win, GCFunction, &gcv);
276 st->point_gc = XCreateGC (dpy, win, 0, NULL);
278 st->mono_gc = XCreateGC (dpy, st->clip_mask_tile, 0, &gcv);
280 st->pix64 = XCreatePixmap(dpy, win, 64, 64, st->xgwa.depth);
282 st->mini_pix = XCreatePixmap (dpy, win, 16, 16, st->xgwa.depth);
285 static const char text[] = "Welcome from testx11_init().";
286 XClearWindow (dpy, win);
287 XDrawString (dpy, win, st->copy_gc, 16, 16, text, countof (text) - 1);
292 jwxyz_assert_display (dpy);
299 backdrop (struct testx11 *st, Drawable t)
301 unsigned w = st->xgwa.width, h = st->xgwa.height;
304 for (y = 0; y != tile_count; ++y) {
305 const float s0 = 2 * M_PI / tile_count;
306 float y_fac = sin ((y + st->frame / 16.0) * s0);
307 for (x = 0; x != tile_count; ++x) {
308 unsigned c = ((sin ((x + st->frame / 8.0) * s0) * y_fac) - 1) / 2 * st->backdrop_ncolors / 2;
309 c = (c + st->frame) % st->backdrop_ncolors;
310 XSetBackground (st->dpy, st->black_gc, st->backdrop_colors[c].pixel);
311 XCopyPlane (st->dpy, st->backdrop_tile, st->backdrop_scratch, st->black_gc,
312 0, st->frame % tile_size, tile_size, tile_size, x * tile_size, y * tile_size, 1);
316 /* XFillRectangle (st->dpy, t, st->black_gc, 0, 0, w, h); */
318 for (y = 0; y < h; y += tile_count * tile_size) {
319 for (x = 0; x < w; x += tile_count * tile_size) {
320 XCopyArea (st->dpy, st->backdrop_scratch, t, st->copy_gc, 0, 0,
321 tile_count * tile_size, tile_count * tile_size, x, y);
326 static const unsigned button_pad = 16;
327 static const unsigned button_size = 64;
331 testx11_draw (Display *dpy, Window win, void *st_raw)
333 struct testx11 *st = (struct testx11 *) st_raw;
334 unsigned w = st->xgwa.width, h = st->xgwa.height;
339 st->mode == mode_welcome ||
340 st->mode == mode_images ||
341 st->mode == mode_copy ? win : st->backbuffer;
345 assert (dpy == st->dpy);
346 assert (win == st->win);
348 jwxyz_assert_display (dpy);
352 case mode_primitives:
355 XDrawPoint (dpy, t, st->thick_line_gc, 0, 0);
356 XDrawPoint (dpy, t, st->thick_line_gc, 0, 1);
357 XDrawPoint (dpy, t, st->thick_line_gc, 1, 0);
358 XDrawPoint (dpy, t, st->thick_line_gc, 1, 1);
360 mini_rect (st, t, 2, 0);
361 mini_rect (st, t, 0, 2);
362 mini_rect (st, t, 4, 2);
363 mini_rect (st, t, 2, 4);
365 mini_rect (st, t, 30, -2);
366 mini_rect (st, t, 32, 0);
368 mini_rect (st, t, 30, h - 2);
369 mini_rect (st, t, 32, h);
371 mini_rect (st, t, -2, 30);
372 mini_rect (st, t, 0, 32);
373 mini_rect (st, t, w - 2, 30);
374 mini_rect (st, t, w, 32);
376 mini_rect (st, t, w / 2 + 4, h / 2 + 4);
378 XFillArc (dpy, t, st->copy_gc, 16, 16, 256, 256, 45 * 64, -135 * 64);
379 /* XCopyArea(dpy, t, t, st->copy_gc, 48, 48, 128, 128, 64, 64); */
382 XDrawPoint (dpy, t, st->copy_gc, w / 2, h / 2);
385 XDrawPoint (dpy, t, st->copy_gc, w / 2, 0);
386 XDrawPoint (dpy, t, st->copy_gc, w / 2, h - 1);
388 XDrawPoint (dpy, t, st->copy_gc, 16, -1);
389 XDrawPoint (dpy, t, st->copy_gc, 17, 0);
391 XDrawPoint (dpy, t, st->copy_gc, 15, h - 2);
392 XDrawPoint (dpy, t, st->copy_gc, 16, h - 1);
393 XDrawPoint (dpy, t, st->copy_gc, 17, h);
396 XPoint *points = malloc (sizeof(XPoint) * h);
397 XSegment *lines = malloc (sizeof(XSegment) * h);
398 XPoint *points2 = malloc (sizeof(XPoint) * h);
399 for(i = 0; i != h; ++i)
401 points[i].x = 8 + (i & 1);
404 lines[i].x1 = 48 + (i & 1) * 4;
409 points2[i].x = 24 + (i & 1);
411 /* XFillRectangle(st->dpy, t, st->copy_gc, 24 + (i & 1), i, 1, 1); */
414 XDrawPoints (dpy, t, st->copy_gc, points, h, CoordModeOrigin);
415 XDrawSegments (dpy, t, st->copy_gc, lines, h);
416 XDrawPoints (dpy, t, st->copy_gc, points2, h, CoordModeOrigin);
423 XDrawPoint (dpy, t, st->copy_gc, -1, 16);
424 XDrawPoint (dpy, t, st->copy_gc, 0, 17);
425 XDrawPoint (dpy, t, st->copy_gc, w - 1, 16);
426 XDrawPoint (dpy, t, st->copy_gc, w, 17);
429 XPoint *points = malloc(sizeof(XPoint) * w);
430 XSegment *lines = malloc(sizeof(XSegment) * w);
431 XPoint *points2 = malloc(sizeof(XPoint) * w);
432 for(i = 0; i != w; ++i)
435 points[i].y = 8 + (i & 1);
437 lines[i].y1 = 48 + (i & 1) * 4;
442 points2[i].y = 24 + (i & 1);
443 /* XFillRectangle(dpy, t, st->copy_gc, i, 24 + (i & 1), 1, 1); */
446 XDrawPoints (dpy, t, st->copy_gc, points, w, CoordModeOrigin);
447 XDrawSegments (dpy, t, st->copy_gc, lines, w);
449 /* Thick purple lines */
452 {31, 31, 31, 31}, /* TODO: This should not be rotated in Cocoa. */
455 XDrawSegments (dpy, t, st->thick_line_gc, seg, 2);
457 XDrawLine (dpy, t, st->thick_line_gc, 58, 43, 64, 43);
458 XDrawLine (dpy, t, st->thick_line_gc, 73, 43, 80, 43);
461 XDrawPoints (dpy, t, st->copy_gc, points2, w, CoordModeOrigin);
467 XDrawLine (dpy, t, st->copy_gc, 54, 11, 72, 22);
470 XPoint vertices[] = {{5, 5}, {5, 8}, {8, 8}, {8, 5}};
471 XFillPolygon (dpy, t, st->copy_gc, vertices, 4, Convex, CoordModeOrigin);
475 XDrawRectangle (dpy, t, st->copy_gc, 11, 11, 11, 11);
476 XFillRectangle (dpy, t, st->copy_gc, 13, 13, 8, 8);
479 /* Several ~16 pixel boxes in a row. */
483 XDrawRectangle (dpy, t, st->copy_gc, 54, 54, 16, 16);
484 XDrawRectangle (dpy, t, st->copy_gc, 55, 55, 14, 14);
486 XDrawPoint (dpy, t, st->thick_line_gc, 56, 56);
487 XDrawPoint (dpy, t, st->copy_gc, 57, 56);
491 XCopyArea (dpy, t, t, st->copy_gc, 55, 55, 15, 15, 72, 55);
495 XImage *image = XGetImage(st->dpy, t, 55, 55, 15, 15, 0xffffff, ZPixmap);
496 XPutImage (dpy, t, st->copy_gc, image, 0, 0, 88, 55, 15, 15);
497 XDestroyImage(image);
503 XCopyArea (dpy, t, st->mini_pix, st->copy_gc, 104, 55, 16, 16, 0, 0);
504 /* XCopyArea (dpy, t, st->mini_pix, st->copy_gc, 105, 56, 14, 14, 1, 1);
505 XCopyArea (dpy, t, st->mini_pix, st->copy_gc, 1, 1, 14, 14, 1, 1);
508 /* This point gets hidden. */
509 XDrawPoint (dpy, t, st->copy_gc, 104 + 8, 55 + 8);
511 XDrawPoint (dpy, st->mini_pix, st->copy_gc, 0, 0);
512 XDrawPoint (dpy, st->mini_pix, st->copy_gc, 15, 15);
513 XDrawRectangle (dpy, st->mini_pix, st->copy_gc, 1, 1, 13, 13);
514 XCopyArea (dpy, st->mini_pix, t, st->copy_gc, 0, 0, 16, 16, 104, 55);
518 XDrawLine (dpy, t, st->copy_gc, 11, 28, 22, 28);
519 XDrawLine (dpy, t, st->copy_gc, 12, 27, 12, 46);
520 XDrawLine (dpy, t, st->copy_gc, 14, 30, 14, 30);
523 XDrawArc (dpy, t, st->copy_gc, 27, 11, 19, 11, 0, 360 * 64);
525 XDrawRectangle (dpy, t, st->copy_gc, 54, 73, 64, 64);
526 XFillArc (dpy, t, st->copy_gc, 56, 75, 60, 60, 0, 360 * 64);
527 /* XDrawArc (dpy, t, st->thick_line_gc, 56, 75, 60, 60, 0, 360 * 64); */
529 XClearArea (dpy, win, 121, 55, 16, 16, False);
535 /* if(w >= 9 && h >= 10) */
537 Screen *screen = st->xgwa.screen;
538 Visual *visual = st->xgwa.visual;
539 Pixmap pixmap = XCreatePixmap (dpy, t, 3, 10,
540 visual_depth (screen, visual));
541 unsigned long cells = cells = st->rgb[0] | st->rgb[1] | st->rgb[2];
544 XSetForeground (dpy, st->point_gc, st->salmon);
545 XDrawPoint(dpy, t, st->point_gc, 0, h - 1);
548 test_pattern (st, t);
549 test_pattern (st, pixmap);
550 /* Here's a good spot to verify that the pixmap contains the right colors
553 copy_test (dpy, t, pixmap, st->copy_gc, 0, 6, cells);
555 XCopyArea (dpy, pixmap, t, st->copy_gc, 0, 0, 3, 10, 3, 0);
557 XImage *image = XGetImage (dpy, pixmap, 0, 0, 3, 10, cells, ZPixmap);
558 XPutImage (dpy, t, st->copy_gc, image, 0, 0, 6, 0, 3, 10);
559 XDestroyImage (image);
562 XFreePixmap (dpy, pixmap);
570 /* X.org isn't making a whole lot of sense here. */
572 Bool use_copy = (st->frame / 20) & 1;
575 GC gc = use_copy ? st->copy_gc : st->xor_gc;
577 XSetWindowBackground (dpy, t, st->magenta);
578 XCopyArea (dpy, t, t, gc, -2, -2, 40, 40, 20, 20);
581 XCopyArea (st->dpy, t, t, gc, -20, h - 20, 40, 40, 20, h - 60);
582 XCopyArea (dpy, t, t, gc, w - 38, h - 38, 40, 40, w - 60, h - 60);
583 XCopyArea (dpy, t, t, gc, w - 20, -20, 40, 40, w - 60, 20);
585 XSetWindowBackground (dpy, t, st->gray50);
586 XCopyArea (st->dpy, t, t, gc, -20, 64, 40, 40, 20, 64);
588 XSetWindowBackground (dpy, t, st->dark_slate_gray1);
589 XCopyArea (st->dpy, t, t, gc, -20, 112, 40, 40, 20, 112);
595 XCopyArea (st->dpy, t, st->pix64, gc, 0, h - 64, 64, 64, 0, 0);
597 XSetForeground (st->dpy, st->xor_gc, st->rgb[1]);
598 XSetBackground (st->dpy, st->xor_gc, st->cyan);
600 /* XCopyArea (st->dpy, st->pix64, st->pix64, gc, 32, 32, 64, 64, 0, 0);
601 XCopyArea (st->dpy, st->pix64, t, gc, 0, 0, 64, 64, 4, h - 68);
603 XCopyArea (st->dpy, st->pix64, t, gc, 32, 32, 128, 64, 0, h - 64);
609 for (i = 1; i != mode_count; ++i) {
612 XRectangle button_dims;
613 button_dims.x = i0 * (button_pad + button_size) + button_pad;
614 button_dims.y = h - button_pad - button_size;
615 button_dims.width = button_size;
616 button_dims.height = button_size;
618 XFillRectangles (dpy, t, st->black_gc, &button_dims, 1);
619 XDrawRectangle (dpy, t, st->copy_gc, button_dims.x, button_dims.y,
620 button_dims.width, button_dims.height);
622 XDrawString (dpy, t, st->copy_gc,
623 button_dims.x + button_size / 2 - 3,
624 h - button_pad - button_size / 2 + 13 / 2,
625 str, sprintf(str, "%u", i));
629 XCopyArea (dpy, t, win, st->copy_gc, 0, 0, w, h, 0, 0);
636 testx11_reshape (Display *dpy, Window window, void *st_raw,
637 unsigned int w, unsigned int h)
639 struct testx11 *st = (struct testx11 *)st_raw;
643 XFreePixmap (st->dpy, st->backbuffer);
645 create_backbuffer (st);
650 testx11_event (Display *dpy, Window window, void *st_raw, XEvent *event)
652 struct testx11 *st = (struct testx11 *) st_raw;
654 Bool handled = False;
656 switch (event->xany.type)
662 XLookupString (&event->xkey, &c, 1, &keysym, 0);
664 handled = toggle_antialiasing (st);
666 if (c >= '0' && c <= '9' && c < '0' + mode_count) {
674 if (event->xbutton.y >= st->xgwa.height - button_pad * 2 - button_size) {
675 int i = (event->xbutton.x - button_pad / 2) / (button_pad + button_size) + 1;
676 if (i && i < mode_count) {
683 handled = toggle_antialiasing (st);
691 testx11_free (Display *dpy, Window window, void *st_raw)
693 /* Omitted for the sake of brevity. */
696 XSCREENSAVER_MODULE_2 ("TestX11", testx11, testx11)