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