From http://www.jwz.org/xscreensaver/xscreensaver-5.39.tar.gz
[xscreensaver] / hacks / deluxe.c
1 /* xscreensaver, Copyright (c) 1999-2018 Jamie Zawinski <jwz@jwz.org>
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
12 #include <math.h>
13 #include "screenhack.h"
14 #include "alpha.h"
15
16 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
17 # include "xdbe.h"
18 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
19
20 #define countof(x) (sizeof(x)/sizeof(*(x)))
21 #define ABS(x) ((x)<0?-(x):(x))
22
23 struct state {
24   Display *dpy;
25   Window window;
26
27   Bool transparent_p;
28   int nplanes;
29   unsigned long base_pixel, *plane_masks;
30
31   int count;
32   int delay;
33   int ncolors;
34   Bool dbuf;
35   XColor *colors;
36   GC erase_gc;
37   struct throbber **throbbers;
38   XWindowAttributes xgwa;
39   Pixmap b, ba, bb;     /* double-buffer to reduce flicker */
40
41 # ifdef HAVE_DOUBLE_BUFFER_EXTENSION
42   Bool dbeclear_p;
43   XdbeBackBuffer backb;
44 # endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
45 };
46
47 struct throbber {
48   int x, y;
49   int size;
50   int max_size;
51   int thickness;
52   int speed;
53   int fuse;
54   GC gc;
55   void (*draw) (struct state *, Drawable, struct throbber *);
56 };
57
58
59 static void
60 draw_star (struct state *st, Drawable w, struct throbber *t)
61 {
62   XPoint points[11];
63   int x = t->x;
64   int y = t->y;
65
66   /*
67     The following constant is really:
68       sqrt(5 - sqrt(5)) / sqrt(25 - 11 * sqrt(5))
69
70     Reference: http://mathworld.wolfram.com/Pentagram.html
71   */
72   int s = t->size * 2.6180339887498985;
73   int s2 = t->size;
74   double c = M_PI * 2;
75   double o = -M_PI / 2;
76
77   points[0].x = x + s  * cos(o + 0.0*c); points[0].y = y + s  * sin(o + 0.0*c);
78   points[1].x = x + s2 * cos(o + 0.1*c); points[1].y = y + s2 * sin(o + 0.1*c);
79   points[2].x = x + s  * cos(o + 0.2*c); points[2].y = y + s  * sin(o + 0.2*c);
80   points[3].x = x + s2 * cos(o + 0.3*c); points[3].y = y + s2 * sin(o + 0.3*c);
81   points[4].x = x + s  * cos(o + 0.4*c); points[4].y = y + s  * sin(o + 0.4*c);
82   points[5].x = x + s2 * cos(o + 0.5*c); points[5].y = y + s2 * sin(o + 0.5*c);
83   points[6].x = x + s  * cos(o + 0.6*c); points[6].y = y + s  * sin(o + 0.6*c);
84   points[7].x = x + s2 * cos(o + 0.7*c); points[7].y = y + s2 * sin(o + 0.7*c);
85   points[8].x = x + s  * cos(o + 0.8*c); points[8].y = y + s  * sin(o + 0.8*c);
86   points[9].x = x + s2 * cos(o + 0.9*c); points[9].y = y + s2 * sin(o + 0.9*c);
87   points[10] = points[0];
88
89   XDrawLines (st->dpy, w, t->gc, points, countof(points), CoordModeOrigin);
90 }
91
92 static void
93 draw_circle (struct state *st, Drawable w, struct throbber *t)
94 {
95   XDrawArc (st->dpy, w, t->gc,
96             t->x - t->size / 2,
97             t->y - t->size / 2,
98             t->size, t->size,
99             0, 360*64);
100 }
101
102 static void
103 draw_hlines (struct state *st, Drawable w, struct throbber *t)
104 {
105   XDrawLine (st->dpy, w, t->gc, 0,
106              t->y - t->size, t->max_size,
107              t->y - t->size);
108   XDrawLine (st->dpy, w, t->gc, 0,
109              t->y + t->size, t->max_size,
110              t->y + t->size);
111 }
112
113 static void
114 draw_vlines (struct state *st, Drawable w, struct throbber *t)
115 {
116   XDrawLine (st->dpy, w, t->gc,
117              t->x - t->size, 0,
118              t->x - t->size, t->max_size);
119   XDrawLine (st->dpy, w, t->gc,
120              t->x + t->size, 0,
121              t->x + t->size, t->max_size);
122 }
123
124 static void
125 draw_corners (struct state *st, Drawable w, struct throbber *t)
126 {
127   int s = (t->size + t->thickness) / 2;
128   XPoint points[3];
129
130   if (t->y > s)
131     {
132       points[0].x = 0;        points[0].y = t->y - s;
133       points[1].x = t->x - s; points[1].y = t->y - s;
134       points[2].x = t->x - s; points[2].y = 0;
135       XDrawLines (st->dpy, w, t->gc, points, countof(points), CoordModeOrigin);
136
137       points[0].x = t->x + s;    points[0].y = 0;
138       points[1].x = t->x + s;    points[1].y = t->y - s;
139       points[2].x = t->max_size; points[2].y = t->y - s;
140       XDrawLines (st->dpy, w, t->gc, points, countof(points), CoordModeOrigin);
141     }
142
143   if (t->x > s)
144     {
145       points[0].x = 0;        points[0].y = t->y + s;
146       points[1].x = t->x - s; points[1].y = t->y + s;
147       points[2].x = t->x - s; points[2].y = t->max_size;
148       XDrawLines (st->dpy, w, t->gc, points, countof(points), CoordModeOrigin);
149
150       points[0].x = t->x + s;    points[0].y = t->max_size;
151       points[1].x = t->x + s;    points[1].y = t->y + s;
152       points[2].x = t->max_size; points[2].y = t->y + s;
153       XDrawLines (st->dpy, w, t->gc, points, countof(points), CoordModeOrigin);
154     }
155 }
156
157
158 static struct throbber *
159 make_throbber (struct state *st, Drawable d, int w, int h, unsigned long pixel)
160 {
161   XGCValues gcv;
162   unsigned long flags;
163   struct throbber *t = (struct throbber *) malloc (sizeof (*t));
164   t->x = w / 2;
165   t->y = h / 2;
166   t->max_size = (w > h ? w : h);
167   t->speed = get_integer_resource (st->dpy, "speed", "Speed");
168   t->fuse = 1 + (random() % 4);
169   t->thickness = get_integer_resource (st->dpy, "thickness", "Thickness");
170
171   if (st->xgwa.width > 2560) t->thickness *= 3;  /* Retina displays */
172
173   if (t->speed < 0) t->speed = -t->speed;
174   t->speed += (((random() % t->speed) / 2) - (t->speed / 2));
175   if (t->speed > 0) t->speed = -t->speed;
176
177   flags = GCForeground;
178 # ifndef HAVE_JWXYZ
179   if (st->transparent_p)
180     {
181       gcv.foreground = ~0L;
182       gcv.plane_mask = st->base_pixel | st->plane_masks[random() % st->nplanes];
183       flags |= GCPlaneMask;
184     }
185   else
186 # endif /* !HAVE_JWXYZ */
187     {
188       gcv.foreground = pixel;
189     }
190
191   gcv.line_width = t->thickness;
192   gcv.cap_style = CapProjecting;
193   gcv.join_style = JoinMiter;
194
195   flags |= (GCLineWidth | GCCapStyle | GCJoinStyle);
196   t->gc = XCreateGC (st->dpy, d, flags, &gcv);
197
198 # ifdef HAVE_JWXYZ
199   if (st->transparent_p)
200     {
201       /* give a non-opaque alpha to the color */
202       unsigned long pixel = gcv.foreground;
203       unsigned long amask = BlackPixelOfScreen (st->xgwa.screen);
204       unsigned long a = (0xCCCCCCCC & amask);
205       pixel = (pixel & (~amask)) | a;
206
207       jwxyz_XSetAlphaAllowed (st->dpy, t->gc, True);
208       XSetForeground (st->dpy, t->gc, pixel);
209     }
210 # endif /* HAVE_JWXYZ */
211
212   switch (random() % 11) {
213   case 0: case 1: case 2: case 3: t->draw = draw_star; break;
214   case 4: case 5: case 6: case 7: t->draw = draw_circle; break;
215   case 8: t->draw = draw_hlines; break;
216   case 9: t->draw = draw_vlines; break;
217   case 10: t->draw = draw_corners; break;
218   default: abort(); break;
219   }
220
221   if (t->draw == draw_circle) 
222     t->max_size *= 1.5;
223
224   if (random() % 4)
225     t->size = t->max_size;
226   else
227     t->size = t->thickness, t->speed = -t->speed;
228
229   return t;
230 }
231
232 static int
233 throb (struct state *st, Drawable window, struct throbber *t)
234 {
235   t->size += t->speed;
236   if (t->size <= (t->thickness / 2))
237     {
238       t->speed = -t->speed;
239       t->size += (t->speed * 2);
240     }
241   else if (t->size > t->max_size)
242     {
243       t->speed = -t->speed;
244       t->size += (t->speed * 2);
245       t->fuse--;
246     }
247
248   if (t->fuse <= 0)
249     {
250       XFreeGC (st->dpy, t->gc);
251       memset (t, 0, sizeof(*t));
252       free (t);
253       return -1;
254     }
255   else
256     {
257       t->draw (st, window, t);
258       return 0;
259     }
260 }
261
262
263 static void *
264 deluxe_init (Display *dpy, Window window)
265 {
266   struct state *st = (struct state *) calloc (1, sizeof(*st));
267   XGCValues gcv;
268   int i;
269   st->dpy = dpy;
270   st->window = window;
271   st->count = get_integer_resource (st->dpy, "count", "Integer");
272   st->delay = get_integer_resource (st->dpy, "delay", "Integer");
273   st->ncolors = get_integer_resource (st->dpy, "ncolors", "Integer");
274   st->dbuf = get_boolean_resource (st->dpy, "doubleBuffer", "Boolean");
275
276 # ifdef HAVE_DOUBLE_BUFFER_EXTENSION
277   st->dbeclear_p = get_boolean_resource (st->dpy, "useDBEClear", "Boolean");
278 #endif
279
280 # ifdef HAVE_JWXYZ      /* Don't second-guess Quartz's double-buffering */
281   st->dbuf = False;
282 # endif
283
284   XGetWindowAttributes (st->dpy, st->window, &st->xgwa);
285
286   st->transparent_p = get_boolean_resource(st->dpy, "transparent", "Transparent");
287
288   st->colors = (XColor *) calloc (sizeof(*st->colors), st->ncolors);
289
290   if (get_boolean_resource(st->dpy, "mono", "Boolean"))
291     {
292     MONO:
293       st->ncolors = 1;
294       st->colors[0].pixel = get_pixel_resource(st->dpy, st->xgwa.colormap,
295                                            "foreground", "Foreground");
296     }
297 #ifndef HAVE_JWXYZ
298   else if (st->transparent_p)
299     {
300       st->nplanes = get_integer_resource (st->dpy, "planes", "Planes");
301       if (st->nplanes <= 0)
302         st->nplanes = (random() % (st->xgwa.depth-2)) + 2;
303
304       allocate_alpha_colors (st->xgwa.screen, st->xgwa.visual, st->xgwa.colormap,
305                              &st->nplanes, True, &st->plane_masks,
306                              &st->base_pixel);
307       if (st->nplanes <= 1)
308         {
309 # if 0
310           fprintf (stderr,
311          "%s: couldn't allocate any color planes; turning transparency off.\n",
312                    progname);
313 # endif
314           st->transparent_p = False;
315           goto COLOR;
316         }
317     }
318 #endif /* !HAVE_JWXYZ */
319   else
320     {
321 #ifndef HAVE_JWXYZ
322     COLOR:
323 #endif
324       make_random_colormap (st->xgwa.screen, st->xgwa.visual, st->xgwa.colormap,
325                             st->colors, &st->ncolors, True, True, 0, True);
326       if (st->ncolors < 2)
327         goto MONO;
328     }
329
330   if (st->dbuf)
331     {
332 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
333       if (st->dbeclear_p)
334         st->b = xdbe_get_backbuffer (st->dpy, st->window, XdbeBackground);
335       else
336         st->b = xdbe_get_backbuffer (st->dpy, st->window, XdbeUndefined);
337       st->backb = st->b;
338 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
339
340       if (!st->b)
341         {
342           st->ba = XCreatePixmap (st->dpy, st->window, st->xgwa.width, st->xgwa.height,st->xgwa.depth);
343           st->bb = XCreatePixmap (st->dpy, st->window, st->xgwa.width, st->xgwa.height,st->xgwa.depth);
344           st->b = st->ba;
345         }
346     }
347   else
348     {
349       st->b = st->window;
350     }
351
352   st->throbbers = (struct throbber **) calloc (st->count, sizeof(struct throbber *));
353   for (i = 0; i < st->count; i++)
354     st->throbbers[i] = make_throbber (st, st->b, st->xgwa.width, st->xgwa.height,
355                                   st->colors[random() % st->ncolors].pixel);
356
357   gcv.foreground = get_pixel_resource (st->dpy, st->xgwa.colormap,
358                                        "background", "Background");
359   st->erase_gc = XCreateGC (st->dpy, st->b, GCForeground, &gcv);
360
361   if (st->ba) XFillRectangle (st->dpy, st->ba, st->erase_gc, 0, 0, st->xgwa.width, st->xgwa.height);
362   if (st->bb) XFillRectangle (st->dpy, st->bb, st->erase_gc, 0, 0, st->xgwa.width, st->xgwa.height);
363
364   return st;
365 }
366
367 static unsigned long
368 deluxe_draw (Display *dpy, Window window, void *closure)
369 {
370   struct state *st = (struct state *) closure;
371   int i;
372 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
373   if (!st->dbeclear_p || !st->backb)
374 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
375     XFillRectangle (st->dpy, st->b, st->erase_gc, 0, 0, st->xgwa.width, st->xgwa.height);
376
377   for (i = 0; i < st->count; i++)
378     if (throb (st, st->b, st->throbbers[i]) < 0)
379       st->throbbers[i] = make_throbber (st, st->b, st->xgwa.width, st->xgwa.height,
380                                     st->colors[random() % st->ncolors].pixel);
381
382 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
383   if (st->backb)
384     {
385       XdbeSwapInfo info[1];
386       info[0].swap_window = st->window;
387       info[0].swap_action = (st->dbeclear_p ? XdbeBackground : XdbeUndefined);
388       XdbeSwapBuffers (st->dpy, info, 1);
389     }
390   else
391 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
392     if (st->dbuf)
393       {
394         XCopyArea (st->dpy, st->b, st->window, st->erase_gc, 0, 0,
395                    st->xgwa.width, st->xgwa.height, 0, 0);
396         st->b = (st->b == st->ba ? st->bb : st->ba);
397       }
398
399   return st->delay;
400 }
401
402 static void
403 deluxe_reshape (Display *dpy, Window window, void *closure, 
404                  unsigned int w, unsigned int h)
405 {
406   struct state *st = (struct state *) closure;
407   if (! st->dbuf) {   /* #### more complicated if we have a back buffer... */
408     int i;
409     XGetWindowAttributes (st->dpy, st->window, &st->xgwa);
410     XClearWindow (dpy, window);
411     for (i = 0; i < st->count; i++)
412       if (st->throbbers[i])
413         st->throbbers[i]->fuse = 0;
414   }
415 }
416
417 static Bool
418 deluxe_event (Display *dpy, Window window, void *closure, XEvent *event)
419 {
420   return False;
421 }
422
423 static void
424 deluxe_free (Display *dpy, Window window, void *closure)
425 {
426 }
427
428
429 static const char *deluxe_defaults [] = {
430   ".background:         black",
431   ".foreground:         white",
432   "*delay:              10000",
433   "*count:              5",
434   "*thickness:          50",
435   "*speed:              15",
436   "*ncolors:            20",
437   "*transparent:        True",
438   "*doubleBuffer:       True",
439 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
440   "*useDBE:             True",
441   "*useDBEClear:        True",
442 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
443 #ifdef HAVE_MOBILE
444   "*ignoreRotation:     True",
445 #endif
446   0
447 };
448
449 static XrmOptionDescRec deluxe_options [] = {
450   { "-delay",           ".delay",       XrmoptionSepArg, 0 },
451   { "-thickness",       ".thickness",   XrmoptionSepArg, 0 },
452   { "-count",           ".count",       XrmoptionSepArg, 0 },
453   { "-ncolors",         ".ncolors",     XrmoptionSepArg, 0 },
454   { "-speed",           ".speed",       XrmoptionSepArg, 0 },
455   { "-transparent",     ".transparent",  XrmoptionNoArg,  "True"  },
456   { "-no-transparent",  ".transparent",  XrmoptionNoArg,  "False" },
457   { "-opaque",          ".transparent",  XrmoptionNoArg,  "False" },
458   { "-no-opaque",       ".transparent",  XrmoptionNoArg,  "True"  },
459   { "-db",              ".doubleBuffer", XrmoptionNoArg,  "True"  },
460   { "-no-db",           ".doubleBuffer", XrmoptionNoArg,  "False" },
461   { 0, 0, 0, 0 }
462 };
463
464
465 XSCREENSAVER_MODULE ("Deluxe", deluxe)