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