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