ftp://ftp.krokus.ru/pub/OpenBSD/distfiles/xscreensaver-4.21.tar.gz
[xscreensaver] / hacks / xlockmore.c
1 /* xlockmore.c --- xscreensaver compatibility layer for xlockmore modules.
2  * xscreensaver, Copyright (c) 1997, 1998, 2001, 2002, 2004
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 #include <X11/Intrinsic.h>
28
29 #define countof(x) (sizeof((x))/sizeof(*(x)))
30
31 #define MAX_COLORS (1L<<13)
32
33 extern XtAppContext app;
34 extern ModeSpecOpt xlockmore_opts[];
35 extern const char *app_defaults;
36
37 void
38 pre_merge_options (void)
39 {
40   int i, j;
41   char *s;
42
43   /* Translate the xlockmore `opts[]' argument to a form that
44      screenhack.c expects.
45    */
46   for (i = 0; i < xlockmore_opts->numopts; i++)
47     {
48       XrmOptionDescRec *old = &xlockmore_opts->opts[i];
49       XrmOptionDescRec *new = &options[i];
50
51       if (old->option[0] == '-')
52         new->option = old->option;
53       else
54         {
55           /* Convert "+foo" to "-no-foo". */
56           new->option = (char *) malloc (strlen(old->option) + 5);
57           strcpy (new->option, "-no-");
58           strcat (new->option, old->option + 1);
59         }
60
61       new->specifier = strrchr (old->specifier, '.');
62       if (!new->specifier) abort();
63
64       new->argKind = old->argKind;
65       new->value = old->value;
66     }
67
68   /* Add extra args, if they're mentioned in the defaults... */
69   {
70     char *args[] = { "-count", "-cycles", "-delay", "-ncolors",
71                      "-size", "-font", "-wireframe", "-use3d", "-useSHM",
72                      "-showFPS" };
73     for (j = 0; j < countof(args); j++)
74       if (strstr(app_defaults, args[j]+1))
75         {
76           XrmOptionDescRec *new = &options[i++];
77           new->option = args[j];
78           new->specifier = strdup(args[j]);
79           new->specifier[0] = '.';
80           if (!strcmp(new->option, "-wireframe"))
81             {
82               new->argKind = XrmoptionNoArg;
83               new->value = "True";
84               new = &options[i++];
85               new->option = "-no-wireframe";
86               new->specifier = options[i-2].specifier;
87               new->argKind = XrmoptionNoArg;
88               new->value = "False";
89             }
90           else if (!strcmp(new->option, "-use3d"))
91             {
92               new->option = "-3d";
93               new->argKind = XrmoptionNoArg;
94               new->value = "True";
95               new = &options[i++];
96               new->option = "-no-3d";
97               new->specifier = options[i-2].specifier;
98               new->argKind = XrmoptionNoArg;
99               new->value = "False";
100             }
101           else if (!strcmp(new->option, "-useSHM"))
102             {
103               new->option = "-shm";
104               new->argKind = XrmoptionNoArg;
105               new->value = "True";
106               new = &options[i++];
107               new->option = "-no-shm";
108               new->specifier = options[i-2].specifier;
109               new->argKind = XrmoptionNoArg;
110               new->value = "False";
111             }
112           else if (!strcmp(new->option, "-showFPS"))
113             {
114               new->option = "-fps";
115               new->argKind = XrmoptionNoArg;
116               new->value = "True";
117               new = &options[i++];
118               new->option = "-no-fps";
119               new->specifier = options[i-2].specifier;
120               new->argKind = XrmoptionNoArg;
121               new->value = "False";
122             }
123           else
124             {
125               new->argKind = XrmoptionSepArg;
126               new->value = 0;
127             }
128         }
129   }
130
131
132   /* Construct the kind of `defaults' that screenhack.c expects from
133      the xlockmore `vars[]' argument.
134    */
135   i = 0;
136
137   /* Put on the PROGCLASS.background/foreground resources. */
138   s = (char *) malloc(50);
139   strcpy (s, progclass);
140   strcat (s, ".background: black");
141   defaults [i++] = s;
142
143   s = (char *) malloc(50);
144   strcpy (s, progclass);
145   strcat (s, ".foreground: white");
146   defaults [i++] = s;
147
148   /* Copy the lines out of the `app_defaults' var and into this array. */
149   s = strdup (app_defaults);
150   while (s && *s)
151     {
152       defaults [i++] = s;
153       s = strchr(s, '\n');
154       if (s)
155         *s++ = 0;
156     }
157
158   /* Copy the defaults out of the `xlockmore_opts->' variable. */
159   for (j = 0; j < xlockmore_opts->numvarsdesc; j++)
160     {
161       const char *def = xlockmore_opts->vars[j].def;
162
163       if (!def) abort();
164       if (!*def) abort();
165       if (strlen(def) > 1000) abort();
166
167       s = (char *) malloc (strlen (xlockmore_opts->vars[j].name) +
168                            strlen (def) + 10);
169       strcpy (s, "*");
170       strcat (s, xlockmore_opts->vars[j].name);
171       strcat (s, ": ");
172       strcat (s, def);
173       defaults [i++] = s;
174
175       /* Go through the list of resources and print a warning if there
176          are any duplicates.
177        */
178       {
179         char *onew = strdup (xlockmore_opts->vars[j].name);
180         const char *new = onew;
181         char *s;
182         int k;
183         if ((s = strrchr (new, '.'))) new = s+1;
184         if ((s = strrchr (new, '*'))) new = s+1;
185         for (k = 0; k < i-1; k++)
186           {
187             char *oold = strdup (defaults[k]);
188             const char *old = oold;
189             if ((s = strchr (oold, ':'))) *s = 0;
190             if ((s = strrchr (old, '.'))) old = s+1;
191             if ((s = strrchr (old, '*'))) old = s+1;
192             if (!strcasecmp (old, new))
193               {
194                 fprintf (stderr,
195                          "%s: duplicate resource \"%s\": "
196                          "set in both DEFAULTS and vars[]\n",
197                          progname, old);
198               }
199             free (oold);
200           }
201         free (onew);
202       }
203     }
204
205   defaults [i] = 0;
206 }
207
208
209 static void
210 xlockmore_read_resources (void)
211 {
212   int i;
213   for (i = 0; i < xlockmore_opts->numvarsdesc; i++)
214     {
215       void  *var   = xlockmore_opts->vars[i].var;
216       Bool  *var_b = (Bool *)  var;
217       char **var_c = (char **) var;
218       int   *var_i = (int *) var;
219       float *var_f = (float *) var;
220
221       switch (xlockmore_opts->vars[i].type)
222         {
223         case t_String:
224           *var_c = get_string_resource (xlockmore_opts->vars[i].name,
225                                         xlockmore_opts->vars[i].classname);
226           break;
227         case t_Float:
228           *var_f = get_float_resource (xlockmore_opts->vars[i].name,
229                                        xlockmore_opts->vars[i].classname);
230           break;
231         case t_Int:
232           *var_i = get_integer_resource (xlockmore_opts->vars[i].name,
233                                          xlockmore_opts->vars[i].classname);
234           break;
235         case t_Bool:
236           *var_b = get_boolean_resource (xlockmore_opts->vars[i].name,
237                                          xlockmore_opts->vars[i].classname);
238           break;
239         default:
240           abort ();
241         }
242     }
243 }
244
245
246 static void
247 xlockmore_handle_events (ModeInfo *mi, 
248                          void (*reshape) (ModeInfo *, int, int),
249                          Bool (*hook) (ModeInfo *, XEvent *))
250 {
251   if (XtAppPending (app) & (XtIMTimer|XtIMAlternateInput))
252     XtAppProcessEvent (app, XtIMTimer|XtIMAlternateInput);
253
254   while (XPending (mi->dpy))
255     {
256       XEvent event;
257       XNextEvent (mi->dpy, &event);
258       if (reshape && event.xany.type == ConfigureNotify)
259         {
260           XGetWindowAttributes (mi->dpy, mi->window, &mi->xgwa);
261           reshape (mi, mi->xgwa.width, mi->xgwa.height);
262         }
263       else if (hook && hook (mi, &event))
264         {
265         }
266       else
267         {
268           screenhack_handle_event (mi->dpy, &event);
269         }
270     }
271 }
272
273
274 void 
275 xlockmore_screenhack (Display *dpy, Window window,
276                       Bool want_writable_colors,
277                       Bool want_uniform_colors,
278                       Bool want_smooth_colors,
279                       Bool want_bright_colors,
280                       unsigned long event_mask,
281                       void (*hack_init) (ModeInfo *),
282                       void (*hack_draw) (ModeInfo *),
283                       void (*hack_reshape) (ModeInfo *, int, int),
284                       Bool (*hack_handle_events) (ModeInfo *, XEvent *),
285                       void (*hack_free) (ModeInfo *))
286 {
287   ModeInfo mi;
288   XGCValues gcv;
289   XColor color;
290   int i;
291   time_t start, now;
292   int orig_pause;
293   Bool root_p;
294
295   memset(&mi, 0, sizeof(mi));
296   mi.dpy = dpy;
297   mi.window = window;
298   XGetWindowAttributes (dpy, window, &mi.xgwa);
299   root_p = (window == RootWindowOfScreen (mi.xgwa.screen));
300
301   /* If this is the root window, don't allow ButtonPress to be selected.
302      Bad Things Happen. */
303   if (root_p)
304     event_mask &= (~(ButtonPressMask|ButtonReleaseMask));
305
306   /* If this hack wants additional events, select them. */
307   if (event_mask && ! (mi.xgwa.your_event_mask & event_mask))
308     XSelectInput (dpy, window, (mi.xgwa.your_event_mask | event_mask));
309
310   color.flags = DoRed|DoGreen|DoBlue;
311   color.red = color.green = color.blue = 0;
312   if (!XAllocColor(dpy, mi.xgwa.colormap, &color))
313     abort();
314   mi.black = color.pixel;
315   color.red = color.green = color.blue = 0xFFFF;
316   if (!XAllocColor(dpy, mi.xgwa.colormap, &color))
317     abort();
318   mi.white = color.pixel;
319
320   if (mono_p)
321     {
322       static unsigned long pixels[2];
323       static XColor colors[2];
324     MONO:
325       mi.npixels = 2;
326       mi.pixels = pixels;
327       mi.colors = colors;
328       pixels[0] = mi.black;
329       pixels[1] = mi.white;
330       colors[0].flags = DoRed|DoGreen|DoBlue;
331       colors[1].flags = DoRed|DoGreen|DoBlue;
332       colors[0].red = colors[0].green = colors[0].blue = 0;
333       colors[1].red = colors[1].green = colors[1].blue = 0xFFFF;
334       mi.writable_p = False;
335     }
336   else
337     {
338       mi.npixels = get_integer_resource ("ncolors", "Integer");
339       if (mi.npixels <= 0)
340         mi.npixels = 64;
341       else if (mi.npixels > MAX_COLORS)
342         mi.npixels = MAX_COLORS;
343
344       mi.colors = (XColor *) calloc (mi.npixels, sizeof (*mi.colors));
345
346       mi.writable_p = want_writable_colors;
347
348       if (want_uniform_colors)
349         make_uniform_colormap (dpy, mi.xgwa.visual, mi.xgwa.colormap,
350                                mi.colors, &mi.npixels,
351                                True, &mi.writable_p, True);
352       else if (want_smooth_colors)
353         make_smooth_colormap (dpy, mi.xgwa.visual, mi.xgwa.colormap,
354                               mi.colors, &mi.npixels,
355                               True, &mi.writable_p, True);
356       else
357         make_random_colormap (dpy, mi.xgwa.visual, mi.xgwa.colormap,
358                               mi.colors, &mi.npixels,
359                               want_bright_colors,
360                               True, &mi.writable_p, True);
361
362       if (mi.npixels <= 2)
363         goto MONO;
364       else
365         {
366           mi.pixels = (unsigned long *)
367             calloc (mi.npixels, sizeof (*mi.pixels));
368           for (i = 0; i < mi.npixels; i++)
369             mi.pixels[i] = mi.colors[i].pixel;
370         }
371     }
372
373   gcv.foreground = mi.white;
374   gcv.background = mi.black;
375   mi.gc = XCreateGC(dpy, window, GCForeground|GCBackground, &gcv);
376
377   mi.fullrandom = True;
378
379   mi.pause      = get_integer_resource ("delay", "Usecs");
380
381   mi.cycles     = get_integer_resource ("cycles", "Int");
382   mi.batchcount = get_integer_resource ("count", "Int");
383   mi.size       = get_integer_resource ("size", "Int");
384
385   mi.threed = get_boolean_resource ("use3d", "Boolean");
386   mi.threed_delta = get_float_resource ("delta3d", "Boolean");
387   mi.threed_right_color = get_pixel_resource ("right3d", "Color", dpy,
388                                               mi.xgwa.colormap);
389   mi.threed_left_color = get_pixel_resource ("left3d", "Color", dpy,
390                                              mi.xgwa.colormap);
391   mi.threed_both_color = get_pixel_resource ("both3d", "Color", dpy,
392                                              mi.xgwa.colormap);
393   mi.threed_none_color = get_pixel_resource ("none3d", "Color", dpy,
394                                              mi.xgwa.colormap);
395
396   mi.wireframe_p = get_boolean_resource ("wireframe", "Boolean");
397   mi.root_p = root_p;
398   mi.fps_p = get_boolean_resource ("showFPS", "Boolean");
399 #ifdef HAVE_XSHM_EXTENSION
400   mi.use_shm = get_boolean_resource ("useSHM", "Boolean");
401 #endif /* !HAVE_XSHM_EXTENSION */
402
403   if (mi.pause < 0)
404     mi.pause = 0;
405   else if (mi.pause > 100000000)
406     mi.pause = 100000000;
407   orig_pause = mi.pause;
408
409   /* If this hack uses fonts (meaning, mentioned "font" in DEFAULTS)
410      then load it. */
411   {
412     char *name = get_string_resource ("font", "Font");
413     if (name)
414       {
415         XFontStruct *f = XLoadQueryFont (dpy, name);
416         const char *def1 = "-*-times-bold-r-normal-*-180-*";
417         const char *def2 = "fixed";
418         if (!f)
419           {
420             fprintf (stderr, "%s: font %s does not exist, using %s\n",
421                      progname, name, def1);
422             f = XLoadQueryFont (dpy, def1);
423           }
424         if (!f)
425           {
426             fprintf (stderr, "%s: font %s does not exist, using %s\n",
427                      progname, def1, def2);
428             f = XLoadQueryFont (dpy, def2);
429           }
430         if (f) XSetFont (dpy, mi.gc, f->fid);
431         if (f) XFreeFont (dpy, f);
432       }
433   }
434
435   xlockmore_read_resources ();
436
437   XClearWindow (dpy, window);
438
439   i = 0;
440   start = time((time_t) 0);
441
442   hack_init (&mi);
443   do {
444     hack_draw (&mi);
445     XSync(dpy, False);
446     xlockmore_handle_events (&mi, hack_reshape, hack_handle_events);
447     if (mi.pause)
448       usleep(mi.pause);
449     mi.pause = orig_pause;
450
451     if (hack_free)
452       {
453         if (i++ > (mi.batchcount / 4) &&
454             (start + 5) < (now = time((time_t) 0)))
455           {
456             i = 0;
457             start = now;
458             hack_free (&mi);
459             hack_init (&mi);
460             XSync(dpy, False);
461           }
462       }
463
464   } while (1);
465 }