aced3efc966f335bec2f363a95b713cb0e7c92ad
[xscreensaver] / hacks / deluxe.c
1 /* xscreensaver, Copyright (c) 1999 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
15 #define countof(x) (sizeof(x)/sizeof(*(x)))
16 #define ABS(x) ((x)<0?-(x):(x))
17
18 struct throbber {
19   int x, y;
20   int size;
21   int max_size;
22   int thickness;
23   int speed;
24   int fuse;
25   GC gc;
26   void (*draw) (Display *, Drawable, struct throbber *);
27 };
28
29 static void
30 draw_star (Display *dpy, Drawable w, struct throbber *t)
31 {
32   XPoint points[11];
33   int x = t->x;
34   int y = t->y;
35   int s = t->size / 0.383;  /* trial and error, I forget how to derive this */
36   int s2 = t->size;
37   double c = M_PI * 2;
38   double o = -M_PI / 2;
39
40   points[0].x = x + s  * cos(o + 0.0*c); points[0].y = y + s  * sin(o + 0.0*c);
41   points[1].x = x + s2 * cos(o + 0.1*c); points[1].y = y + s2 * sin(o + 0.1*c);
42   points[2].x = x + s  * cos(o + 0.2*c); points[2].y = y + s  * sin(o + 0.2*c);
43   points[3].x = x + s2 * cos(o + 0.3*c); points[3].y = y + s2 * sin(o + 0.3*c);
44   points[4].x = x + s  * cos(o + 0.4*c); points[4].y = y + s  * sin(o + 0.4*c);
45   points[5].x = x + s2 * cos(o + 0.5*c); points[5].y = y + s2 * sin(o + 0.5*c);
46   points[6].x = x + s  * cos(o + 0.6*c); points[6].y = y + s  * sin(o + 0.6*c);
47   points[7].x = x + s2 * cos(o + 0.7*c); points[7].y = y + s2 * sin(o + 0.7*c);
48   points[8].x = x + s  * cos(o + 0.8*c); points[8].y = y + s  * sin(o + 0.8*c);
49   points[9].x = x + s2 * cos(o + 0.9*c); points[9].y = y + s2 * sin(o + 0.9*c);
50   points[10] = points[0];
51
52   XDrawLines (dpy, w, t->gc, points, countof(points), CoordModeOrigin);
53 }
54
55 static void
56 draw_circle (Display *dpy, Drawable w, struct throbber *t)
57 {
58   XDrawArc (dpy, w, t->gc,
59             t->x - t->size / 2,
60             t->y - t->size / 2,
61             t->size, t->size,
62             0, 360*64);
63 }
64
65 static void
66 draw_hlines (Display *dpy, Drawable w, struct throbber *t)
67 {
68   XDrawLine (dpy, w, t->gc, 0,
69              t->y - t->size, t->max_size,
70              t->y - t->size);
71   XDrawLine (dpy, w, t->gc, 0,
72              t->y + t->size, t->max_size,
73              t->y + t->size);
74 }
75
76 static void
77 draw_vlines (Display *dpy, Drawable w, struct throbber *t)
78 {
79   XDrawLine (dpy, w, t->gc,
80              t->x - t->size, 0,
81              t->x - t->size, t->max_size);
82   XDrawLine (dpy, w, t->gc,
83              t->x + t->size, 0,
84              t->x + t->size, t->max_size);
85 }
86
87 static void
88 draw_corners (Display *dpy, Drawable w, struct throbber *t)
89 {
90   int s = (t->size + t->thickness) / 2;
91   XPoint points[3];
92
93   points[0].x = 0;        points[0].y = t->y - s;
94   points[1].x = t->x - s; points[1].y = t->y - s;
95   points[2].x = t->x - s; points[2].y = 0;
96   XDrawLines (dpy, w, t->gc, points, countof(points), CoordModeOrigin);
97
98   points[0].x = 0;        points[0].y = t->y + s;
99   points[1].x = t->x - s; points[1].y = t->y + s;
100   points[2].x = t->x - s; points[2].y = t->max_size;
101   XDrawLines (dpy, w, t->gc, points, countof(points), CoordModeOrigin);
102
103   points[0].x = t->x + s;    points[0].y = 0;
104   points[1].x = t->x + s;    points[1].y = t->y - s;
105   points[2].x = t->max_size; points[2].y = t->y - s;
106   XDrawLines (dpy, w, t->gc, points, countof(points), CoordModeOrigin);
107
108   points[0].x = t->x + s;    points[0].y = t->max_size;
109   points[1].x = t->x + s;    points[1].y = t->y + s;
110   points[2].x = t->max_size; points[2].y = t->y + s;
111   XDrawLines (dpy, w, t->gc, points, countof(points), CoordModeOrigin);
112 }
113
114
115 static struct throbber *
116 make_throbber (Display *dpy, Drawable d, int w, int h, unsigned long pixel)
117 {
118   XGCValues gcv;
119   struct throbber *t = (struct throbber *) malloc (sizeof (*t));
120   t->x = w / 2;
121   t->y = h / 2;
122   t->max_size = w;
123   t->speed = get_integer_resource ("speed", "Speed");
124   t->fuse = 1 + (random() % 4);
125   t->thickness = get_integer_resource ("thickness", "Thickness");
126
127   if (t->speed < 0) t->speed = -t->speed;
128   t->speed += (((random() % t->speed) / 2) - (t->speed / 2));
129   if (t->speed > 0) t->speed = -t->speed;
130
131   if (random() % 4)
132     t->size = t->max_size;
133   else
134     t->size = t->thickness, t->speed = -t->speed;
135
136   gcv.foreground = pixel;
137   gcv.line_width = t->thickness;
138   gcv.line_style = LineSolid;
139   gcv.cap_style = CapProjecting;
140   gcv.join_style = JoinMiter;
141   t->gc = XCreateGC (dpy, d,
142                      (GCForeground|GCLineWidth|GCLineStyle|
143                       GCCapStyle|GCJoinStyle),
144                      &gcv);
145
146   switch (random() % 11) {
147   case 0: case 1: case 2: case 3: t->draw = draw_star; break;
148   case 4: case 5: case 6: case 7: t->draw = draw_circle; break;
149   case 8: t->draw = draw_hlines; break;
150   case 9: t->draw = draw_vlines; break;
151   case 10: t->draw = draw_corners; break;
152   default: abort(); break;
153   }
154
155   return t;
156 }
157
158 static int
159 throb (Display *dpy, Drawable window, struct throbber *t)
160 {
161   t->size += t->speed;
162   if (t->size <= (t->thickness / 2))
163     {
164       t->speed = -t->speed;
165       t->size += (t->speed * 2);
166     }
167   else if (t->size > t->max_size)
168     {
169       t->speed = -t->speed;
170       t->size += (t->speed * 2);
171       t->fuse--;
172     }
173
174   if (t->fuse <= 0)
175     {
176       XFreeGC (dpy, t->gc);
177       memset (t, 0, sizeof(*t));
178       free (t);
179       return -1;
180     }
181   else
182     {
183       t->draw (dpy, window, t);
184       return 0;
185     }
186 }
187
188
189 \f
190 char *progclass = "Deluxe";
191
192 char *defaults [] = {
193   ".background:         black",
194   ".foreground:         white",
195   "*delay:              5000",
196   "*count:              5",
197   "*thickness:          50",
198   "*speed:              15",
199   "*ncolors:            20",
200   "*doubleBuffer:       True",
201   0
202 };
203
204 XrmOptionDescRec options [] = {
205   { "-delay",           ".delay",       XrmoptionSepArg, 0 },
206   { "-thickness",       ".thickness",   XrmoptionSepArg, 0 },
207   { "-count",           ".count",       XrmoptionSepArg, 0 },
208   { "-ncolors",         ".ncolors",     XrmoptionSepArg, 0 },
209   { "-speed",           ".speed",       XrmoptionSepArg, 0 },
210   { 0, 0, 0, 0 }
211 };
212
213 void
214 screenhack (Display *dpy, Window window)
215 {
216   int count = get_integer_resource ("count", "Integer");
217   int delay = get_integer_resource ("delay", "Integer");
218   int ncolors = get_integer_resource ("ncolors", "Integer");
219   Bool dbuf = get_boolean_resource ("doubleBuffer", "Boolean");
220   XColor colors[255];
221   XGCValues gcv;
222   GC erase_gc = 0;
223   int i;
224   struct throbber **throbbers;
225   XWindowAttributes xgwa;
226   Pixmap b=0, ba=0, bb=0;       /* double-buffer to reduce flicker */
227
228   XGetWindowAttributes (dpy, window, &xgwa);
229   make_random_colormap (dpy, xgwa.visual, xgwa.colormap,
230                         colors, &ncolors, True, True, 0, True);
231
232   if (dbuf)
233     {
234       ba = XCreatePixmap (dpy, window, xgwa.width, xgwa.height, xgwa.depth);
235       bb = XCreatePixmap (dpy, window, xgwa.width, xgwa.height, xgwa.depth);
236       b = ba;
237     }
238   else
239     {
240       b = window;
241     }
242
243   throbbers = (struct throbber **) calloc (count, sizeof(struct throbber *));
244   for (i = 0; i < count; i++)
245     throbbers[i] = make_throbber (dpy, b, xgwa.width, xgwa.height,
246                                   colors[random() % ncolors].pixel);
247
248   if (dbuf)
249     {
250       gcv.foreground = get_pixel_resource ("background", "Background",
251                                            dpy, xgwa.colormap);
252       erase_gc = XCreateGC (dpy, b, GCForeground, &gcv);
253       XFillRectangle (dpy, ba, erase_gc, 0, 0, xgwa.width, xgwa.height);
254       XFillRectangle (dpy, bb, erase_gc, 0, 0, xgwa.width, xgwa.height);
255     }
256
257   while (1)
258     {
259       if (dbuf)
260         XFillRectangle (dpy, b, erase_gc, 0, 0, xgwa.width, xgwa.height);
261       else
262         XClearWindow (dpy, b);
263
264       for (i = 0; i < count; i++)
265         if (throb (dpy, b, throbbers[i]) < 0)
266           throbbers[i] = make_throbber (dpy, b, xgwa.width, xgwa.height,
267                                         colors[random() % ncolors].pixel);
268       if (dbuf)
269         {
270           XCopyArea (dpy, b, window, erase_gc, 0, 0,
271                      xgwa.width, xgwa.height, 0, 0);
272           b = (b == ba ? bb : ba);
273         }
274
275       XSync (dpy, False);
276       screenhack_handle_events (dpy);
277       if (delay)
278         usleep (delay);
279     }
280 }