From http://www.jwz.org/xscreensaver/xscreensaver-5.29.tar.gz
[xscreensaver] / hacks / xlockmore.c
1 /* xlockmore.c --- xscreensaver compatibility layer for xlockmore modules.
2  * xscreensaver, Copyright (c) 1997-2014 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   Bool root_p;
286
287   if (! xlmft)
288     abort();
289   mi->xlmft = xlmft;
290
291   mi->dpy = dpy;
292   mi->window = window;
293   XGetWindowAttributes (dpy, window, &mi->xgwa);
294   
295 #ifdef HAVE_COCOA
296   
297   /* In Cocoa-based xscreensaver, all hacks run in the same address space,
298      so each one needs to get its own screen number.  Believe what jwxyz
299      says about screen counts and numbers.
300    */
301   mi->num_screens = ScreenCount (dpy);
302   mi->screen_number = XScreenNumberOfScreen (mi->xgwa.screen);
303   root_p = True;
304
305 #else /* !HAVE_COCOA -- real Xlib */
306   
307   /* In Xlib-based xscreensaver, each hack runs in its own address space,
308      so each one only needs to be aware of one screen.
309    */
310   mi->num_screens = 1;
311   mi->screen_number = 0;
312   
313   {  /* kludge for DEBUG_PAIR */
314     static int screen_tick = 0;
315     mi->num_screens++;
316     if (screen_tick)
317       mi->screen_number++;
318     screen_tick++;
319   }
320
321   root_p = (window == RootWindowOfScreen (mi->xgwa.screen));
322
323   /* Everybody gets motion events, just in case. */
324   XSelectInput (dpy, window, (mi->xgwa.your_event_mask | PointerMotionMask));
325
326 #endif /* !HAVE_COCOA */
327   
328   color.flags = DoRed|DoGreen|DoBlue;
329   color.red = color.green = color.blue = 0;
330   if (!XAllocColor(dpy, mi->xgwa.colormap, &color))
331     abort();
332   mi->black = color.pixel;
333   color.red = color.green = color.blue = 0xFFFF;
334   if (!XAllocColor(dpy, mi->xgwa.colormap, &color))
335     abort();
336   mi->white = color.pixel;
337
338   if (mono_p)
339     {
340       static unsigned long pixels[2];
341       static XColor colors[2];
342     MONO:
343       mi->npixels = 2;
344       if (! mi->pixels)
345         mi->pixels = (unsigned long *) 
346           calloc (mi->npixels, sizeof (*mi->pixels));
347       if (!mi->colors)
348         mi->colors = (XColor *) 
349           calloc (mi->npixels, sizeof (*mi->colors));
350       pixels[0] = mi->black;
351       pixels[1] = mi->white;
352       colors[0].flags = DoRed|DoGreen|DoBlue;
353       colors[1].flags = DoRed|DoGreen|DoBlue;
354       colors[0].red = colors[0].green = colors[0].blue = 0;
355       colors[1].red = colors[1].green = colors[1].blue = 0xFFFF;
356       mi->writable_p = False;
357     }
358   else
359     {
360       mi->npixels = get_integer_resource (dpy, "ncolors", "Integer");
361       if (mi->npixels <= 0)
362         mi->npixels = 64;
363       else if (mi->npixels > MAX_COLORS)
364         mi->npixels = MAX_COLORS;
365
366       mi->colors = (XColor *) calloc (mi->npixels, sizeof (*mi->colors));
367
368       mi->writable_p = mi->xlmft->want_writable_colors;
369
370       switch (mi->xlmft->desired_color_scheme)
371         {
372         case color_scheme_uniform:
373           make_uniform_colormap (mi->xgwa.screen, mi->xgwa.visual,
374                                  mi->xgwa.colormap,
375                                  mi->colors, &mi->npixels,
376                                  True, &mi->writable_p, True);
377           break;
378         case color_scheme_smooth:
379           make_smooth_colormap (mi->xgwa.screen, mi->xgwa.visual,
380                                 mi->xgwa.colormap,
381                                 mi->colors, &mi->npixels,
382                                 True, &mi->writable_p, True);
383           break;
384         case color_scheme_bright:
385         case color_scheme_default:
386           make_random_colormap (mi->xgwa.screen, mi->xgwa.visual,
387                                 mi->xgwa.colormap,
388                                 mi->colors, &mi->npixels,
389                                 (mi->xlmft->desired_color_scheme ==
390                                  color_scheme_bright),
391                                 True, &mi->writable_p, True);
392           break;
393         default:
394           abort();
395         }
396
397       if (mi->npixels <= 2)
398         goto MONO;
399       else
400         {
401           mi->pixels = (unsigned long *)
402             calloc (mi->npixels, sizeof (*mi->pixels));
403           for (i = 0; i < mi->npixels; i++)
404             mi->pixels[i] = mi->colors[i].pixel;
405         }
406     }
407
408   gcv.foreground = mi->white;
409   gcv.background = mi->black;
410   mi->gc = XCreateGC (dpy, window, GCForeground|GCBackground, &gcv);
411
412   mi->fullrandom = True;
413
414   mi->pause      = get_integer_resource (dpy, "delay", "Usecs");
415
416   mi->cycles     = get_integer_resource (dpy, "cycles", "Int");
417   mi->batchcount = get_integer_resource (dpy, "count", "Int");
418   mi->size       = get_integer_resource (dpy, "size", "Int");
419
420   mi->threed = get_boolean_resource (dpy, "use3d", "Boolean");
421   mi->threed_delta = get_float_resource (dpy, "delta3d", "Float");
422   mi->threed_right_color = get_pixel_resource (dpy,
423                                                mi->xgwa.colormap, "right3d", "Color");
424   mi->threed_left_color = get_pixel_resource (dpy,
425                                               mi->xgwa.colormap, "left3d", "Color");
426   mi->threed_both_color = get_pixel_resource (dpy,
427                                               mi->xgwa.colormap, "both3d", "Color");
428   mi->threed_none_color = get_pixel_resource (dpy,
429                                               mi->xgwa.colormap, "none3d", "Color");
430
431   mi->wireframe_p = get_boolean_resource (dpy, "wireframe", "Boolean");
432   mi->root_p = root_p;
433 #ifdef HAVE_XSHM_EXTENSION
434   mi->use_shm = get_boolean_resource (dpy, "useSHM", "Boolean");
435 #endif /* !HAVE_XSHM_EXTENSION */
436   mi->fps_p = get_boolean_resource (dpy, "doFPS", "DoFPS");
437   mi->recursion_depth = -1;  /* see fps.c */
438
439   if (mi->pause < 0)
440     mi->pause = 0;
441   else if (mi->pause > 100000000)
442     mi->pause = 100000000;
443
444   /* If this hack uses fonts (meaning, mentioned "font" in DEFAULTS)
445      then load it. */
446   {
447     char *name = get_string_resource (dpy, "font", "Font");
448     if (name)
449       {
450         XFontStruct *f = XLoadQueryFont (dpy, name);
451         const char *def1 = "-*-helvetica-bold-r-normal-*-180-*";
452         const char *def2 = "fixed";
453         if (!f)
454           {
455             fprintf (stderr, "%s: font %s does not exist, using %s\n",
456                      progname, name, def1);
457             f = XLoadQueryFont (dpy, def1);
458           }
459         if (!f)
460           {
461             fprintf (stderr, "%s: font %s does not exist, using %s\n",
462                      progname, def1, def2);
463             f = XLoadQueryFont (dpy, def2);
464           }
465         if (f) XSetFont (dpy, mi->gc, f->fid);
466         if (f) XFreeFont (dpy, f);
467         free (name);
468       }
469   }
470
471   xlockmore_read_resources (mi);
472
473   XClearWindow (dpy, window);
474
475   mi->xlmft->hack_init (mi);
476
477   return mi;
478 }
479
480 static unsigned long
481 xlockmore_draw (Display *dpy, Window window, void *closure)
482 {
483   ModeInfo *mi = (ModeInfo *) closure;
484
485   unsigned long orig_pause = mi->pause;
486   unsigned long this_pause;
487
488   mi->xlmft->hack_draw (mi);
489
490   this_pause = mi->pause;
491   mi->pause  = orig_pause;
492   return this_pause;
493 }
494
495
496 static void
497 xlockmore_reshape (Display *dpy, Window window, void *closure, 
498                  unsigned int w, unsigned int h)
499 {
500   ModeInfo *mi = (ModeInfo *) closure;
501   if (mi && mi->xlmft->hack_reshape)
502     {
503       XGetWindowAttributes (dpy, window, &mi->xgwa);
504       mi->xlmft->hack_reshape (mi, mi->xgwa.width, mi->xgwa.height);
505     }
506 }
507
508 static Bool
509 xlockmore_event (Display *dpy, Window window, void *closure, XEvent *event)
510 {
511   ModeInfo *mi = (ModeInfo *) closure;
512   if (mi && mi->xlmft->hack_handle_events)
513     return mi->xlmft->hack_handle_events (mi, event);
514   else
515     return False;
516 }
517
518 void
519 xlockmore_do_fps (Display *dpy, Window w, fps_state *fpst, void *closure)
520 {
521   ModeInfo *mi = (ModeInfo *) closure;
522   fps_compute (fpst, 0, mi ? mi->recursion_depth : -1);
523   fps_draw (fpst);
524 }
525
526
527 static void
528 xlockmore_free (Display *dpy, Window window, void *closure)
529 {
530   /* Most of the xlockmore/GL hacks don't have `free' functions, and of
531      those that do have them, they're incomplete or buggy.  So, fuck it.
532      Under X11, we're about to exit anyway, and it doesn't matter.
533      On OSX, we'll leak a little.  Beats crashing.
534    */
535 #if 0
536   ModeInfo *mi = (ModeInfo *) closure;
537   if (mi->xlmft->hack_free)
538     mi->xlmft->hack_free (mi);
539
540   XFreeGC (dpy, mi->gc);
541   free_colors (dpy, mi->xgwa.colormap, mi->colors, mi->npixels);
542   free (mi->colors);
543   free (mi->pixels);
544
545   free (mi);
546 #endif
547 }
548