From http://www.jwz.org/xscreensaver/xscreensaver-5.30.tar.gz
[xscreensaver] / hacks / deluxe.c
1 /* xscreensaver, Copyright (c) 1999-2013 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 (t->speed < 0) t->speed = -t->speed;
172   t->speed += (((random() % t->speed) / 2) - (t->speed / 2));
173   if (t->speed > 0) t->speed = -t->speed;
174
175   flags = GCForeground;
176 # ifndef HAVE_COCOA
177   if (st->transparent_p)
178     {
179       gcv.foreground = ~0L;
180       gcv.plane_mask = st->base_pixel | st->plane_masks[random() % st->nplanes];
181       flags |= GCPlaneMask;
182     }
183   else
184 # endif /* !HAVE_COCOA */
185     {
186       gcv.foreground = pixel;
187     }
188
189   gcv.line_width = t->thickness;
190   gcv.cap_style = CapProjecting;
191   gcv.join_style = JoinMiter;
192
193   flags |= (GCLineWidth | GCCapStyle | GCJoinStyle);
194   t->gc = XCreateGC (st->dpy, d, flags, &gcv);
195
196 # ifdef HAVE_COCOA
197   if (st->transparent_p)
198     {
199       /* give a non-opaque alpha to the color */
200       unsigned long pixel = gcv.foreground;
201       unsigned long amask = BlackPixelOfScreen (st->xgwa.screen);
202       unsigned long a = (0xCCCCCCCC & amask);
203       pixel = (pixel & (~amask)) | a;
204
205       jwxyz_XSetAlphaAllowed (st->dpy, t->gc, True);
206       XSetForeground (st->dpy, t->gc, pixel);
207     }
208 # endif /* HAVE_COCOA */
209
210   switch (random() % 11) {
211   case 0: case 1: case 2: case 3: t->draw = draw_star; break;
212   case 4: case 5: case 6: case 7: t->draw = draw_circle; break;
213   case 8: t->draw = draw_hlines; break;
214   case 9: t->draw = draw_vlines; break;
215   case 10: t->draw = draw_corners; break;
216   default: abort(); break;
217   }
218
219   if (t->draw == draw_circle) 
220     t->max_size *= 1.5;
221
222   if (random() % 4)
223     t->size = t->max_size;
224   else
225     t->size = t->thickness, t->speed = -t->speed;
226
227   return t;
228 }
229
230 static int
231 throb (struct state *st, Drawable window, struct throbber *t)
232 {
233   t->size += t->speed;
234   if (t->size <= (t->thickness / 2))
235     {
236       t->speed = -t->speed;
237       t->size += (t->speed * 2);
238     }
239   else if (t->size > t->max_size)
240     {
241       t->speed = -t->speed;
242       t->size += (t->speed * 2);
243       t->fuse--;
244     }
245
246   if (t->fuse <= 0)
247     {
248       XFreeGC (st->dpy, t->gc);
249       memset (t, 0, sizeof(*t));
250       free (t);
251       return -1;
252     }
253   else
254     {
255       t->draw (st, window, t);
256       return 0;
257     }
258 }
259
260
261 static void *
262 deluxe_init (Display *dpy, Window window)
263 {
264   struct state *st = (struct state *) calloc (1, sizeof(*st));
265   XGCValues gcv;
266   int i;
267   st->dpy = dpy;
268   st->window = window;
269   st->count = get_integer_resource (st->dpy, "count", "Integer");
270   st->delay = get_integer_resource (st->dpy, "delay", "Integer");
271   st->ncolors = get_integer_resource (st->dpy, "ncolors", "Integer");
272   st->dbuf = get_boolean_resource (st->dpy, "doubleBuffer", "Boolean");
273
274 # ifdef HAVE_DOUBLE_BUFFER_EXTENSION
275   st->dbeclear_p = get_boolean_resource (st->dpy, "useDBEClear", "Boolean");
276 #endif
277
278 # ifdef HAVE_COCOA      /* Don't second-guess Quartz's double-buffering */
279   st->dbuf = False;
280 # endif
281
282   XGetWindowAttributes (st->dpy, st->window, &st->xgwa);
283
284   st->transparent_p = get_boolean_resource(st->dpy, "transparent", "Transparent");
285
286   st->colors = (XColor *) calloc (sizeof(*st->colors), st->ncolors);
287
288   if (get_boolean_resource(st->dpy, "mono", "Boolean"))
289     {
290     MONO:
291       st->ncolors = 1;
292       st->colors[0].pixel = get_pixel_resource(st->dpy, st->xgwa.colormap,
293                                            "foreground", "Foreground");
294     }
295 #ifndef HAVE_COCOA
296   else if (st->transparent_p)
297     {
298       st->nplanes = get_integer_resource (st->dpy, "planes", "Planes");
299       if (st->nplanes <= 0)
300         st->nplanes = (random() % (st->xgwa.depth-2)) + 2;
301
302       allocate_alpha_colors (st->xgwa.screen, st->xgwa.visual, st->xgwa.colormap,
303                              &st->nplanes, True, &st->plane_masks,
304                              &st->base_pixel);
305       if (st->nplanes <= 1)
306         {
307 # if 0
308           fprintf (stderr,
309          "%s: couldn't allocate any color planes; turning transparency off.\n",
310                    progname);
311 # endif
312           st->transparent_p = False;
313           goto COLOR;
314         }
315     }
316 #endif /* !HAVE_COCOA */
317   else
318     {
319 #ifndef HAVE_COCOA
320     COLOR:
321 #endif
322       make_random_colormap (st->xgwa.screen, st->xgwa.visual, st->xgwa.colormap,
323                             st->colors, &st->ncolors, True, True, 0, True);
324       if (st->ncolors < 2)
325         goto MONO;
326     }
327
328   if (st->dbuf)
329     {
330 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
331       if (st->dbeclear_p)
332         st->b = xdbe_get_backbuffer (st->dpy, st->window, XdbeBackground);
333       else
334         st->b = xdbe_get_backbuffer (st->dpy, st->window, XdbeUndefined);
335       st->backb = st->b;
336 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
337
338       if (!st->b)
339         {
340           st->ba = XCreatePixmap (st->dpy, st->window, st->xgwa.width, st->xgwa.height,st->xgwa.depth);
341           st->bb = XCreatePixmap (st->dpy, st->window, st->xgwa.width, st->xgwa.height,st->xgwa.depth);
342           st->b = st->ba;
343         }
344     }
345   else
346     {
347       st->b = st->window;
348     }
349
350   st->throbbers = (struct throbber **) calloc (st->count, sizeof(struct throbber *));
351   for (i = 0; i < st->count; i++)
352     st->throbbers[i] = make_throbber (st, st->b, st->xgwa.width, st->xgwa.height,
353                                   st->colors[random() % st->ncolors].pixel);
354
355   gcv.foreground = get_pixel_resource (st->dpy, st->xgwa.colormap,
356                                        "background", "Background");
357   st->erase_gc = XCreateGC (st->dpy, st->b, GCForeground, &gcv);
358
359   if (st->ba) XFillRectangle (st->dpy, st->ba, st->erase_gc, 0, 0, st->xgwa.width, st->xgwa.height);
360   if (st->bb) XFillRectangle (st->dpy, st->bb, st->erase_gc, 0, 0, st->xgwa.width, st->xgwa.height);
361
362   return st;
363 }
364
365 static unsigned long
366 deluxe_draw (Display *dpy, Window window, void *closure)
367 {
368   struct state *st = (struct state *) closure;
369   int i;
370 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
371   if (!st->dbeclear_p || !st->backb)
372 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
373     XFillRectangle (st->dpy, st->b, st->erase_gc, 0, 0, st->xgwa.width, st->xgwa.height);
374
375   for (i = 0; i < st->count; i++)
376     if (throb (st, st->b, st->throbbers[i]) < 0)
377       st->throbbers[i] = make_throbber (st, st->b, st->xgwa.width, st->xgwa.height,
378                                     st->colors[random() % st->ncolors].pixel);
379
380 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
381   if (st->backb)
382     {
383       XdbeSwapInfo info[1];
384       info[0].swap_window = st->window;
385       info[0].swap_action = (st->dbeclear_p ? XdbeBackground : XdbeUndefined);
386       XdbeSwapBuffers (st->dpy, info, 1);
387     }
388   else
389 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
390     if (st->dbuf)
391       {
392         XCopyArea (st->dpy, st->b, st->window, st->erase_gc, 0, 0,
393                    st->xgwa.width, st->xgwa.height, 0, 0);
394         st->b = (st->b == st->ba ? st->bb : st->ba);
395       }
396
397   return st->delay;
398 }
399
400 static void
401 deluxe_reshape (Display *dpy, Window window, void *closure, 
402                  unsigned int w, unsigned int h)
403 {
404   struct state *st = (struct state *) closure;
405   if (! st->dbuf) {   /* #### more complicated if we have a back buffer... */
406     int i;
407     XGetWindowAttributes (st->dpy, st->window, &st->xgwa);
408     XClearWindow (dpy, window);
409     for (i = 0; i < st->count; i++)
410       if (st->throbbers[i])
411         st->throbbers[i]->fuse = 0;
412   }
413 }
414
415 static Bool
416 deluxe_event (Display *dpy, Window window, void *closure, XEvent *event)
417 {
418   return False;
419 }
420
421 static void
422 deluxe_free (Display *dpy, Window window, void *closure)
423 {
424 }
425
426
427 static const char *deluxe_defaults [] = {
428   ".background:         black",
429   ".foreground:         white",
430   "*delay:              10000",
431   "*count:              5",
432   "*thickness:          50",
433   "*speed:              15",
434   "*ncolors:            20",
435   "*transparent:        True",
436   "*doubleBuffer:       True",
437 #ifdef HAVE_DOUBLE_BUFFER_EXTENSION
438   "*useDBE:             True",
439   "*useDBEClear:        True",
440 #endif /* HAVE_DOUBLE_BUFFER_EXTENSION */
441 #ifdef USE_IPHONE
442   "*ignoreRotation:     True",
443 #endif
444   0
445 };
446
447 static XrmOptionDescRec deluxe_options [] = {
448   { "-delay",           ".delay",       XrmoptionSepArg, 0 },
449   { "-thickness",       ".thickness",   XrmoptionSepArg, 0 },
450   { "-count",           ".count",       XrmoptionSepArg, 0 },
451   { "-ncolors",         ".ncolors",     XrmoptionSepArg, 0 },
452   { "-speed",           ".speed",       XrmoptionSepArg, 0 },
453   { "-transparent",     ".transparent",  XrmoptionNoArg,  "True"  },
454   { "-no-transparent",  ".transparent",  XrmoptionNoArg,  "False" },
455   { "-opaque",          ".transparent",  XrmoptionNoArg,  "False" },
456   { "-no-opaque",       ".transparent",  XrmoptionNoArg,  "True"  },
457   { "-db",              ".doubleBuffer", XrmoptionNoArg,  "True"  },
458   { "-no-db",           ".doubleBuffer", XrmoptionNoArg,  "False" },
459   { 0, 0, 0, 0 }
460 };
461
462
463 XSCREENSAVER_MODULE ("Deluxe", deluxe)