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