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