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