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