http://www.tienza.es/crux/src/www.jwz.org/xscreensaver/xscreensaver-5.06.tar.gz
[xscreensaver] / hacks / xlockmore.c
1 /* xlockmore.c --- xscreensaver compatibility layer for xlockmore modules.
2  * xscreensaver, Copyright (c) 1997-2008 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 "xlockmoreI.h"
20 #include "screenhack.h"
21
22 #ifndef HAVE_COCOA
23 # include <X11/Intrinsic.h>
24 #endif /* !HAVE_COCOA */
25
26 #define countof(x) (sizeof((x))/sizeof(*(x)))
27
28 #define MAX_COLORS (1L<<13)
29
30 extern struct xscreensaver_function_table *xscreensaver_function_table;
31
32 extern const char *progclass;
33
34 extern struct xlockmore_function_table xlockmore_function_table;
35
36 static void *xlockmore_init (Display *, Window, 
37                              struct xlockmore_function_table *);
38 static unsigned long xlockmore_draw (Display *, Window, void *);
39 static void xlockmore_reshape (Display *, Window, void *, 
40                                unsigned int w, unsigned int h);
41 static Bool xlockmore_event (Display *, Window, void *, XEvent *);
42 static void xlockmore_free (Display *, Window, void *);
43
44
45 void
46 xlockmore_setup (struct xscreensaver_function_table *xsft, void *arg)
47 {
48   struct xlockmore_function_table *xlmft = 
49     (struct xlockmore_function_table *) arg;
50   int i, j;
51   char *s;
52   XrmOptionDescRec *new_options;
53   char **new_defaults;
54   const char *xlockmore_defaults;
55   ModeSpecOpt *xlockmore_opts = xlmft->opts;
56
57 # undef ya_rand_init
58   ya_rand_init (0);
59
60   xsft->init_cb    = (void *(*) (Display *, Window)) xlockmore_init;
61   xsft->draw_cb    = xlockmore_draw;
62   xsft->reshape_cb = xlockmore_reshape;
63   xsft->event_cb   = xlockmore_event;
64   xsft->free_cb    = xlockmore_free;
65
66   progclass = xlmft->progclass;
67   xlockmore_defaults = xlmft->defaults;
68
69   /* Translate the xlockmore `opts[]' argument to a form that
70      screenhack.c expects.
71    */
72   new_options = (XrmOptionDescRec *) 
73     calloc (xlockmore_opts->numopts*3 + 100, sizeof (*new_options));
74
75   for (i = 0; i < xlockmore_opts->numopts; i++)
76     {
77       XrmOptionDescRec *old = &xlockmore_opts->opts[i];
78       XrmOptionDescRec *new = &new_options[i];
79
80       if (old->option[0] == '-')
81         new->option = old->option;
82       else
83         {
84           /* Convert "+foo" to "-no-foo". */
85           new->option = (char *) malloc (strlen(old->option) + 5);
86           strcpy (new->option, "-no-");
87           strcat (new->option, old->option + 1);
88         }
89
90       new->specifier = strrchr (old->specifier, '.');
91       if (!new->specifier) abort();
92
93       new->argKind = old->argKind;
94       new->value = old->value;
95     }
96
97   /* Add extra args, if they're mentioned in the defaults... */
98   {
99     char *args[] = { "-count", "-cycles", "-delay", "-ncolors",
100                      "-size", "-font", "-wireframe", "-use3d", "-useSHM",
101                      "-showFPS" };
102     for (j = 0; j < countof(args); j++)
103       if (strstr(xlockmore_defaults, args[j]+1))
104         {
105           XrmOptionDescRec *new = &new_options[i++];
106           new->option = args[j];
107           new->specifier = strdup(args[j]);
108           new->specifier[0] = '.';
109           if (!strcmp(new->option, "-wireframe"))
110             {
111               new->argKind = XrmoptionNoArg;
112               new->value = "True";
113               new = &new_options[i++];
114               new->option = "-no-wireframe";
115               new->specifier = new_options[i-2].specifier;
116               new->argKind = XrmoptionNoArg;
117               new->value = "False";
118             }
119           else if (!strcmp(new->option, "-use3d"))
120             {
121               new->option = "-3d";
122               new->argKind = XrmoptionNoArg;
123               new->value = "True";
124               new = &new_options[i++];
125               new->option = "-no-3d";
126               new->specifier = new_options[i-2].specifier;
127               new->argKind = XrmoptionNoArg;
128               new->value = "False";
129             }
130           else if (!strcmp(new->option, "-useSHM"))
131             {
132               new->option = "-shm";
133               new->argKind = XrmoptionNoArg;
134               new->value = "True";
135               new = &new_options[i++];
136               new->option = "-no-shm";
137               new->specifier = new_options[i-2].specifier;
138               new->argKind = XrmoptionNoArg;
139               new->value = "False";
140             }
141           else if (!strcmp(new->option, "-showFPS"))
142             {
143               new->option = "-fps";
144               new->argKind = XrmoptionNoArg;
145               new->value = "True";
146               new = &new_options[i++];
147               new->option = "-no-fps";
148               new->specifier = new_options[i-2].specifier;
149               new->argKind = XrmoptionNoArg;
150               new->value = "False";
151             }
152           else
153             {
154               new->argKind = XrmoptionSepArg;
155               new->value = 0;
156             }
157         }
158   }
159
160
161
162   /* Construct the kind of `defaults' that screenhack.c expects from
163      the xlockmore `vars[]' argument.
164    */
165   i = 0;
166
167   new_defaults = (char **) calloc (1, xlockmore_opts->numvarsdesc * 10 + 100);
168
169   /* Put on the PROGCLASS.background/foreground resources. */
170   s = (char *) malloc(50);
171   *s = 0;
172 # ifndef HAVE_COCOA
173   strcpy (s, progclass);
174 # endif
175   strcat (s, ".background: black");
176   new_defaults [i++] = s;
177
178   s = (char *) malloc(50);
179   *s = 0;
180 # ifndef HAVE_COCOA
181   strcpy (s, progclass);
182 # endif
183   strcat (s, ".foreground: white");
184   new_defaults [i++] = s;
185
186   /* Copy the lines out of the `xlockmore_defaults' var and into this array. */
187   s = strdup (xlockmore_defaults);
188   while (s && *s)
189     {
190       new_defaults [i++] = s;
191       s = strchr(s, '\n');
192       if (s)
193         *s++ = 0;
194     }
195
196   /* Copy the defaults out of the `xlockmore_opts->' variable. */
197   for (j = 0; j < xlockmore_opts->numvarsdesc; j++)
198     {
199       const char *def = xlockmore_opts->vars[j].def;
200
201       if (!def) abort();
202       if (!*def) abort();
203       if (strlen(def) > 1000) abort();
204
205       s = (char *) malloc (strlen (xlockmore_opts->vars[j].name) +
206                            strlen (def) + 10);
207       strcpy (s, "*");
208       strcat (s, xlockmore_opts->vars[j].name);
209       strcat (s, ": ");
210       strcat (s, def);
211       new_defaults [i++] = s;
212
213       /* Go through the list of resources and print a warning if there
214          are any duplicates.
215        */
216       {
217         char *onew = strdup (xlockmore_opts->vars[j].name);
218         const char *new = onew;
219         int k;
220         if ((s = strrchr (new, '.'))) new = s+1;
221         if ((s = strrchr (new, '*'))) new = s+1;
222         for (k = 0; k < i-1; k++)
223           {
224             char *oold = strdup (new_defaults[k]);
225             const char *old = oold;
226             if ((s = strchr (oold, ':'))) *s = 0;
227             if ((s = strrchr (old, '.'))) old = s+1;
228             if ((s = strrchr (old, '*'))) old = s+1;
229             if (!strcasecmp (old, new))
230               {
231                 fprintf (stderr,
232                          "%s: duplicate resource \"%s\": "
233                          "set in both DEFAULTS and vars[]\n",
234                          progname, old);
235               }
236             free (oold);
237           }
238         free (onew);
239       }
240     }
241
242   new_defaults [i] = 0;
243
244   xsft->progclass = progclass;
245   xsft->options   = new_options;
246   xsft->defaults  = (const char * const *) new_defaults;
247 }
248
249
250 static void
251 xlockmore_read_resources (ModeInfo *mi)
252 {
253   Display *dpy = mi->dpy;
254   ModeSpecOpt *xlockmore_opts = mi->xlmft->opts;
255   int i;
256   for (i = 0; i < xlockmore_opts->numvarsdesc; i++)
257     {
258       void  *var   = xlockmore_opts->vars[i].var;
259       Bool  *var_b = (Bool *)  var;
260       char **var_c = (char **) var;
261       int   *var_i = (int *) var;
262       float *var_f = (float *) var;
263
264       switch (xlockmore_opts->vars[i].type)
265         {
266         case t_String:
267           *var_c = get_string_resource (dpy, xlockmore_opts->vars[i].name,
268                                         xlockmore_opts->vars[i].classname);
269           break;
270         case t_Float:
271           *var_f = get_float_resource (dpy, xlockmore_opts->vars[i].name,
272                                        xlockmore_opts->vars[i].classname);
273           break;
274         case t_Int:
275           *var_i = get_integer_resource (dpy, xlockmore_opts->vars[i].name,
276                                          xlockmore_opts->vars[i].classname);
277           break;
278         case t_Bool:
279           *var_b = get_boolean_resource (dpy, xlockmore_opts->vars[i].name,
280                                          xlockmore_opts->vars[i].classname);
281           break;
282         default:
283           abort ();
284         }
285     }
286 }
287
288
289 static void *
290 xlockmore_init (Display *dpy, Window window, 
291                 struct xlockmore_function_table *xlmft)
292 {
293   ModeInfo *mi = (ModeInfo *) calloc (1, sizeof(*mi));
294   XGCValues gcv;
295   XColor color;
296   int i;
297   int orig_pause;
298   Bool root_p;
299
300   if (! xlmft)
301     abort();
302   mi->xlmft = xlmft;
303
304   mi->dpy = dpy;
305   mi->window = window;
306   XGetWindowAttributes (dpy, window, &mi->xgwa);
307   
308 #ifdef HAVE_COCOA
309   
310   /* In Cocoa-based xscreensaver, all hacks run in the same address space,
311      so each one needs to get its own screen number.  We just use a global
312      counter for that, instead of actually trying to figure out which
313      monitor each window is on.  Also, the embedded "preview" view counts
314      as a screen as well.
315     
316      Note that the screen number will increase each time the saver is
317      restarted (e.g., each time preferences are changed!)  So we just
318      keep pushing the num_screens number up as needed, and assume that
319      no more than 10 simultanious copies will be running at once...
320    */
321   {
322     static int screen_tick = 0;
323     mi->num_screens = 10;
324     mi->screen_number = screen_tick++;
325     if (screen_tick >= mi->num_screens)
326       mi->num_screens += 10;
327   }
328   
329   root_p = True;
330 #else /* !HAVE_COCOA -- real Xlib */
331   
332   /* In Xlib-based xscreensaver, each hack runs in its own address space,
333      so each one only needs to be aware of one screen.
334    */
335   mi->num_screens = 1;
336   mi->screen_number = 0;
337   
338   {  /* kludge for DEBUG_PAIR */
339     static int screen_tick = 0;
340     mi->num_screens++;
341     if (screen_tick)
342       mi->screen_number++;
343     screen_tick++;
344   }
345
346   root_p = (window == RootWindowOfScreen (mi->xgwa.screen));
347
348   /* Everybody gets motion events, just in case. */
349   XSelectInput (dpy, window, (mi->xgwa.your_event_mask | PointerMotionMask));
350
351 #endif /* !HAVE_COCOA */
352   
353   color.flags = DoRed|DoGreen|DoBlue;
354   color.red = color.green = color.blue = 0;
355   if (!XAllocColor(dpy, mi->xgwa.colormap, &color))
356     abort();
357   mi->black = color.pixel;
358   color.red = color.green = color.blue = 0xFFFF;
359   if (!XAllocColor(dpy, mi->xgwa.colormap, &color))
360     abort();
361   mi->white = color.pixel;
362
363   if (mono_p)
364     {
365       static unsigned long pixels[2];
366       static XColor colors[2];
367     MONO:
368       mi->npixels = 2;
369       if (! mi->pixels)
370         mi->pixels = (unsigned long *) 
371           calloc (mi->npixels, sizeof (*mi->pixels));
372       if (!mi->colors)
373         mi->colors = (XColor *) 
374           calloc (mi->npixels, sizeof (*mi->colors));
375       pixels[0] = mi->black;
376       pixels[1] = mi->white;
377       colors[0].flags = DoRed|DoGreen|DoBlue;
378       colors[1].flags = DoRed|DoGreen|DoBlue;
379       colors[0].red = colors[0].green = colors[0].blue = 0;
380       colors[1].red = colors[1].green = colors[1].blue = 0xFFFF;
381       mi->writable_p = False;
382     }
383   else
384     {
385       mi->npixels = get_integer_resource (dpy, "ncolors", "Integer");
386       if (mi->npixels <= 0)
387         mi->npixels = 64;
388       else if (mi->npixels > MAX_COLORS)
389         mi->npixels = MAX_COLORS;
390
391       mi->colors = (XColor *) calloc (mi->npixels, sizeof (*mi->colors));
392
393       mi->writable_p = mi->xlmft->want_writable_colors;
394
395       switch (mi->xlmft->desired_color_scheme)
396         {
397         case color_scheme_uniform:
398           make_uniform_colormap (dpy, mi->xgwa.visual, mi->xgwa.colormap,
399                                  mi->colors, &mi->npixels,
400                                  True, &mi->writable_p, True);
401           break;
402         case color_scheme_smooth:
403           make_smooth_colormap (dpy, mi->xgwa.visual, mi->xgwa.colormap,
404                                 mi->colors, &mi->npixels,
405                                 True, &mi->writable_p, True);
406           break;
407         case color_scheme_bright:
408         case color_scheme_default:
409           make_random_colormap (dpy, mi->xgwa.visual, mi->xgwa.colormap,
410                                 mi->colors, &mi->npixels,
411                                 (mi->xlmft->desired_color_scheme ==
412                                  color_scheme_bright),
413                                 True, &mi->writable_p, True);
414           break;
415         default:
416           abort();
417         }
418
419       if (mi->npixels <= 2)
420         goto MONO;
421       else
422         {
423           mi->pixels = (unsigned long *)
424             calloc (mi->npixels, sizeof (*mi->pixels));
425           for (i = 0; i < mi->npixels; i++)
426             mi->pixels[i] = mi->colors[i].pixel;
427         }
428     }
429
430   gcv.foreground = mi->white;
431   gcv.background = mi->black;
432   mi->gc = XCreateGC (dpy, window, GCForeground|GCBackground, &gcv);
433
434   mi->fullrandom = True;
435
436   mi->pause      = get_integer_resource (dpy, "delay", "Usecs");
437
438   mi->cycles     = get_integer_resource (dpy, "cycles", "Int");
439   mi->batchcount = get_integer_resource (dpy, "count", "Int");
440   mi->size       = get_integer_resource (dpy, "size", "Int");
441
442   mi->threed = get_boolean_resource (dpy, "use3d", "Boolean");
443   mi->threed_delta = get_float_resource (dpy, "delta3d", "Float");
444   mi->threed_right_color = get_pixel_resource (dpy,
445                                                mi->xgwa.colormap, "right3d", "Color");
446   mi->threed_left_color = get_pixel_resource (dpy,
447                                               mi->xgwa.colormap, "left3d", "Color");
448   mi->threed_both_color = get_pixel_resource (dpy,
449                                               mi->xgwa.colormap, "both3d", "Color");
450   mi->threed_none_color = get_pixel_resource (dpy,
451                                               mi->xgwa.colormap, "none3d", "Color");
452
453   mi->wireframe_p = get_boolean_resource (dpy, "wireframe", "Boolean");
454   mi->root_p = root_p;
455   mi->fps_p = get_boolean_resource (dpy, "showFPS", "Boolean");
456 #ifdef HAVE_XSHM_EXTENSION
457   mi->use_shm = get_boolean_resource (dpy, "useSHM", "Boolean");
458 #endif /* !HAVE_XSHM_EXTENSION */
459
460   if (mi->pause < 0)
461     mi->pause = 0;
462   else if (mi->pause > 100000000)
463     mi->pause = 100000000;
464   orig_pause = mi->pause;
465
466   /* If this hack uses fonts (meaning, mentioned "font" in DEFAULTS)
467      then load it. */
468   {
469     char *name = get_string_resource (dpy, "font", "Font");
470     if (name)
471       {
472         XFontStruct *f = XLoadQueryFont (dpy, name);
473         const char *def1 = "-*-times-bold-r-normal-*-180-*";
474         const char *def2 = "fixed";
475         if (!f)
476           {
477             fprintf (stderr, "%s: font %s does not exist, using %s\n",
478                      progname, name, def1);
479             f = XLoadQueryFont (dpy, def1);
480           }
481         if (!f)
482           {
483             fprintf (stderr, "%s: font %s does not exist, using %s\n",
484                      progname, def1, def2);
485             f = XLoadQueryFont (dpy, def2);
486           }
487         if (f) XSetFont (dpy, mi->gc, f->fid);
488         if (f) XFreeFont (dpy, f);
489         free (name);
490       }
491   }
492
493   xlockmore_read_resources (mi);
494
495   XClearWindow (dpy, window);
496
497   mi->xlmft->hack_init (mi);
498
499   return mi;
500 }
501
502 static unsigned long
503 xlockmore_draw (Display *dpy, Window window, void *closure)
504 {
505   ModeInfo *mi = (ModeInfo *) closure;
506
507   unsigned long orig_pause = mi->pause;
508   unsigned long this_pause;
509
510   mi->xlmft->hack_draw (mi);
511
512   this_pause = mi->pause;
513   mi->pause  = orig_pause;
514   return this_pause;
515 }
516
517
518 static void
519 xlockmore_reshape (Display *dpy, Window window, void *closure, 
520                  unsigned int w, unsigned int h)
521 {
522   ModeInfo *mi = (ModeInfo *) closure;
523   if (mi && mi->xlmft->hack_reshape)
524     {
525       XGetWindowAttributes (dpy, window, &mi->xgwa);
526       mi->xlmft->hack_reshape (mi, mi->xgwa.width, mi->xgwa.height);
527     }
528 }
529
530 static Bool
531 xlockmore_event (Display *dpy, Window window, void *closure, XEvent *event)
532 {
533   ModeInfo *mi = (ModeInfo *) closure;
534   if (mi && mi->xlmft->hack_handle_events)
535     {
536       mi->xlmft->hack_handle_events (mi, event);
537       /* Since xlockmore hacks don't tell us whether they handled the
538          event, assume they handled buttons (so we don't beep) but that
539          they didn't handle anything else (so that "q" still quits).
540        */
541       return (event->xany.type == ButtonPress);
542     }
543   else
544     return False;
545 }
546
547 static void
548 xlockmore_free (Display *dpy, Window window, void *closure)
549 {
550   ModeInfo *mi = (ModeInfo *) closure;
551   if (mi->xlmft->hack_free)
552     mi->xlmft->hack_free (mi);
553 #ifdef USE_GL
554 /* ####
555   if (mi->fps_state)
556     fps_free (mi);
557  */
558 #endif /* USE_GL */
559
560   XFreeGC (dpy, mi->gc);
561   free_colors (dpy, mi->xgwa.colormap, mi->colors, mi->npixels);
562   free (mi->colors);
563   free (mi->pixels);
564
565   free (mi);
566 }
567