6ca7164f6c79ea73ad7689f5a2c9dae8156c00d9
[xscreensaver] / hacks / xlockmore.c
1 /* xlockmore.c --- xscreensaver compatibility layer for xlockmore modules.
2  * xscreensaver, Copyright (c) 1997, 1998, 2001 Jamie Zawinski <jwz@jwz.org>
3  *
4  * Permission to use, copy, modify, distribute, and sell this software and its
5  * documentation for any purpose is hereby granted without fee, provided that
6  * the above copyright notice appear in all copies and that both that
7  * copyright notice and this permission notice appear in supporting
8  * documentation.  No representations are made about the suitability of this
9  * software for any purpose.  It is provided "as is" without express or 
10  * implied warranty.
11  *
12  * This file, along with xlockmore.h, make it possible to compile an xlockmore
13  * module into a standalone program, and thus use it with xscreensaver.
14  * By Jamie Zawinski <jwz@jwz.org> on 10-May-97; based on the ideas
15  * in the older xlock.h by Charles Hannum <mycroft@ai.mit.edu>.  (I had
16  * to redo it, since xlockmore has diverged so far from xlock...)
17  */
18
19 #include <stdio.h>
20 #include <math.h>
21 #include <string.h>
22 #include "screenhack.h"
23 #include "xlockmoreI.h"
24
25 #define countof(x) (sizeof((x))/sizeof(*(x)))
26
27 #define MAX_COLORS (1L<<13)
28
29 extern ModeSpecOpt xlockmore_opts[];
30 extern const char *app_defaults;
31
32 void
33 pre_merge_options (void)
34 {
35   int i, j;
36   char *s;
37
38   /* Translate the xlockmore `opts[]' argument to a form that
39      screenhack.c expects.
40    */
41   for (i = 0; i < xlockmore_opts->numopts; i++)
42     {
43       XrmOptionDescRec *old = &xlockmore_opts->opts[i];
44       XrmOptionDescRec *new = &options[i];
45
46       if (old->option[0] == '-')
47         new->option = old->option;
48       else
49         {
50           /* Convert "+foo" to "-no-foo". */
51           new->option = (char *) malloc (strlen(old->option) + 5);
52           strcpy (new->option, "-no-");
53           strcat (new->option, old->option + 1);
54         }
55
56       new->specifier = strrchr (old->specifier, '.');
57       if (!new->specifier) abort();
58
59       new->argKind = old->argKind;
60       new->value = old->value;
61     }
62
63   /* Add extra args, if they're mentioned in the defaults... */
64   {
65     char *args[] = { "-count", "-cycles", "-delay", "-ncolors",
66                      "-size", "-wireframe", "-use3d", "-useSHM",
67                      "-showFPS" };
68     for (j = 0; j < countof(args); j++)
69       if (strstr(app_defaults, args[j]+1))
70         {
71           XrmOptionDescRec *new = &options[i++];
72           new->option = args[j];
73           new->specifier = strdup(args[j]);
74           new->specifier[0] = '.';
75           if (!strcmp(new->option, "-wireframe"))
76             {
77               new->argKind = XrmoptionNoArg;
78               new->value = "True";
79               new = &options[i++];
80               new->option = "-no-wireframe";
81               new->specifier = options[i-2].specifier;
82               new->argKind = XrmoptionNoArg;
83               new->value = "False";
84             }
85           else if (!strcmp(new->option, "-use3d"))
86             {
87               new->option = "-3d";
88               new->argKind = XrmoptionNoArg;
89               new->value = "True";
90               new = &options[i++];
91               new->option = "-no-3d";
92               new->specifier = options[i-2].specifier;
93               new->argKind = XrmoptionNoArg;
94               new->value = "False";
95             }
96           else if (!strcmp(new->option, "-useSHM"))
97             {
98               new->option = "-shm";
99               new->argKind = XrmoptionNoArg;
100               new->value = "True";
101               new = &options[i++];
102               new->option = "-no-shm";
103               new->specifier = options[i-2].specifier;
104               new->argKind = XrmoptionNoArg;
105               new->value = "False";
106             }
107           else if (!strcmp(new->option, "-showFPS"))
108             {
109               new->option = "-fps";
110               new->argKind = XrmoptionNoArg;
111               new->value = "True";
112               new = &options[i++];
113               new->option = "-no-fps";
114               new->specifier = options[i-2].specifier;
115               new->argKind = XrmoptionNoArg;
116               new->value = "False";
117             }
118           else
119             {
120               new->argKind = XrmoptionSepArg;
121               new->value = 0;
122             }
123         }
124   }
125
126
127   /* Construct the kind of `defaults' that screenhack.c expects from
128      the xlockmore `vars[]' argument.
129    */
130   i = 0;
131
132   /* Put on the PROGCLASS.background/foreground resources. */
133   s = (char *) malloc(50);
134   strcpy (s, progclass);
135   strcat (s, ".background: black");
136   defaults [i++] = s;
137
138   s = (char *) malloc(50);
139   strcpy (s, progclass);
140   strcat (s, ".foreground: white");
141   defaults [i++] = s;
142
143   /* Copy the lines out of the `app_defaults' var and into this array. */
144   s = strdup (app_defaults);
145   while (s && *s)
146     {
147       defaults [i++] = s;
148       s = strchr(s, '\n');
149       if (s)
150         *s++ = 0;
151     }
152
153   /* Copy the defaults out of the `xlockmore_opts->' variable. */
154   for (j = 0; j < xlockmore_opts->numvarsdesc; j++)
155     {
156       const char *def = xlockmore_opts->vars[j].def;
157
158       if (!def) abort();
159       if (!*def) abort();
160       if (strlen(def) > 1000) abort();
161
162       s = (char *) malloc (strlen (xlockmore_opts->vars[j].name) +
163                            strlen (def) + 10);
164       strcpy (s, "*");
165       strcat (s, xlockmore_opts->vars[j].name);
166       strcat (s, ": ");
167       strcat (s, def);
168       defaults [i++] = s;
169     }
170
171   defaults [i] = 0;
172 }
173
174
175 static void
176 xlockmore_read_resources (void)
177 {
178   int i;
179   for (i = 0; i < xlockmore_opts->numvarsdesc; i++)
180     {
181       void  *var   = xlockmore_opts->vars[i].var;
182       Bool  *var_b = (Bool *)  var;
183       char **var_c = (char **) var;
184       int   *var_i = (int *) var;
185       float *var_f = (float *) var;
186
187       switch (xlockmore_opts->vars[i].type)
188         {
189         case t_String:
190           *var_c = get_string_resource (xlockmore_opts->vars[i].name,
191                                         xlockmore_opts->vars[i].classname);
192           break;
193         case t_Float:
194           *var_f = get_float_resource (xlockmore_opts->vars[i].name,
195                                        xlockmore_opts->vars[i].classname);
196           break;
197         case t_Int:
198           *var_i = get_integer_resource (xlockmore_opts->vars[i].name,
199                                          xlockmore_opts->vars[i].classname);
200           break;
201         case t_Bool:
202           *var_b = get_boolean_resource (xlockmore_opts->vars[i].name,
203                                          xlockmore_opts->vars[i].classname);
204           break;
205         default:
206           abort ();
207         }
208     }
209 }
210
211
212 static void
213 xlockmore_handle_events (ModeInfo *mi, 
214                          void (*reshape) (ModeInfo *, int, int))
215 {
216   while (XPending (mi->dpy))
217     {
218       XEvent event;
219       XNextEvent (mi->dpy, &event);
220       if (reshape && event.xany.type == ConfigureNotify)
221         {
222           XGetWindowAttributes (mi->dpy, mi->window, &mi->xgwa);
223           reshape (mi, mi->xgwa.width, mi->xgwa.height);
224         }
225       else
226         {
227           screenhack_handle_event (mi->dpy, &event);
228         }
229     }
230 }
231
232
233 void 
234 xlockmore_screenhack (Display *dpy, Window window,
235                       Bool want_writable_colors,
236                       Bool want_uniform_colors,
237                       Bool want_smooth_colors,
238                       Bool want_bright_colors,
239                       void (*hack_init) (ModeInfo *),
240                       void (*hack_draw) (ModeInfo *),
241                       void (*hack_reshape) (ModeInfo *, int, int),
242                       void (*hack_free) (ModeInfo *))
243 {
244   ModeInfo mi;
245   XGCValues gcv;
246   XColor color;
247   int i;
248   time_t start, now;
249   int orig_pause;
250
251   memset(&mi, 0, sizeof(mi));
252   mi.dpy = dpy;
253   mi.window = window;
254   XGetWindowAttributes (dpy, window, &mi.xgwa);
255
256   color.flags = DoRed|DoGreen|DoBlue;
257   color.red = color.green = color.blue = 0;
258   if (!XAllocColor(dpy, mi.xgwa.colormap, &color))
259     abort();
260   mi.black = color.pixel;
261   color.red = color.green = color.blue = 0xFFFF;
262   if (!XAllocColor(dpy, mi.xgwa.colormap, &color))
263     abort();
264   mi.white = color.pixel;
265
266   if (mono_p)
267     {
268       static unsigned long pixels[2];
269       static XColor colors[2];
270     MONO:
271       mi.npixels = 2;
272       mi.pixels = pixels;
273       mi.colors = colors;
274       pixels[0] = mi.black;
275       pixels[1] = mi.white;
276       colors[0].flags = DoRed|DoGreen|DoBlue;
277       colors[1].flags = DoRed|DoGreen|DoBlue;
278       colors[0].red = colors[0].green = colors[0].blue = 0;
279       colors[1].red = colors[1].green = colors[1].blue = 0xFFFF;
280       mi.writable_p = False;
281     }
282   else
283     {
284       mi.npixels = get_integer_resource ("ncolors", "Integer");
285       if (mi.npixels <= 0)
286         mi.npixels = 64;
287       else if (mi.npixels > MAX_COLORS)
288         mi.npixels = MAX_COLORS;
289
290       mi.colors = (XColor *) calloc (mi.npixels, sizeof (*mi.colors));
291
292       mi.writable_p = want_writable_colors;
293
294       if (want_uniform_colors)
295         make_uniform_colormap (dpy, mi.xgwa.visual, mi.xgwa.colormap,
296                                mi.colors, &mi.npixels,
297                                True, &mi.writable_p, True);
298       else if (want_smooth_colors)
299         make_smooth_colormap (dpy, mi.xgwa.visual, mi.xgwa.colormap,
300                               mi.colors, &mi.npixels,
301                               True, &mi.writable_p, True);
302       else
303         make_random_colormap (dpy, mi.xgwa.visual, mi.xgwa.colormap,
304                               mi.colors, &mi.npixels,
305                               want_bright_colors,
306                               True, &mi.writable_p, True);
307
308       if (mi.npixels <= 2)
309         goto MONO;
310       else
311         {
312           int i;
313           mi.pixels = (unsigned long *)
314             calloc (mi.npixels, sizeof (*mi.pixels));
315           for (i = 0; i < mi.npixels; i++)
316             mi.pixels[i] = mi.colors[i].pixel;
317         }
318     }
319
320   gcv.foreground = mi.white;
321   gcv.background = mi.black;
322   mi.gc = XCreateGC(dpy, window, GCForeground|GCBackground, &gcv);
323
324   mi.fullrandom = True;
325
326   mi.pause      = get_integer_resource ("delay", "Usecs");
327
328   mi.cycles     = get_integer_resource ("cycles", "Int");
329   mi.batchcount = get_integer_resource ("count", "Int");
330   mi.size       = get_integer_resource ("size", "Int");
331
332   mi.threed = get_boolean_resource ("use3d", "Boolean");
333   mi.threed_delta = get_float_resource ("delta3d", "Boolean");
334   mi.threed_right_color = get_pixel_resource ("right3d", "Color", dpy,
335                                               mi.xgwa.colormap);
336   mi.threed_left_color = get_pixel_resource ("left3d", "Color", dpy,
337                                              mi.xgwa.colormap);
338   mi.threed_both_color = get_pixel_resource ("both3d", "Color", dpy,
339                                              mi.xgwa.colormap);
340   mi.threed_none_color = get_pixel_resource ("none3d", "Color", dpy,
341                                              mi.xgwa.colormap);
342
343   mi.wireframe_p = get_boolean_resource ("wireframe", "Boolean");
344   mi.root_p = (window == RootWindowOfScreen (mi.xgwa.screen));
345   mi.fps_p = get_boolean_resource ("showFPS", "Boolean");
346 #ifdef HAVE_XSHM_EXTENSION
347   mi.use_shm = get_boolean_resource ("useSHM", "Boolean");
348 #endif /* !HAVE_XSHM_EXTENSION */
349
350   if (mi.pause < 0)
351     mi.pause = 0;
352   else if (mi.pause > 100000000)
353     mi.pause = 100000000;
354   orig_pause = mi.pause;
355
356   xlockmore_read_resources ();
357
358   XClearWindow (dpy, window);
359
360   i = 0;
361   start = time((time_t) 0);
362
363   hack_init (&mi);
364   do {
365     hack_draw (&mi);
366     XSync(dpy, False);
367     xlockmore_handle_events (&mi, hack_reshape);
368     if (mi.pause)
369       usleep(mi.pause);
370     mi.pause = orig_pause;
371
372     if (hack_free)
373       {
374         if (i++ > (mi.batchcount / 4) &&
375             (start + 5) < (now = time((time_t) 0)))
376           {
377             i = 0;
378             start = now;
379             hack_free (&mi);
380             hack_init (&mi);
381             XSync(dpy, False);
382           }
383       }
384
385   } while (1);
386 }