1 /* xlockmore.c --- xscreensaver compatibility layer for xlockmore modules.
2 * xscreensaver, Copyright (c) 1997-2014 Jamie Zawinski <jwz@jwz.org>
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
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...)
19 #include "xlockmoreI.h"
20 #include "screenhack.h"
22 #if !defined(HAVE_COCOA) && !defined(HAVE_ANDROID)
23 # include <X11/Intrinsic.h>
24 #endif /* !HAVE_COCOA */
26 #define countof(x) (sizeof((x))/sizeof(*(x)))
28 #define MAX_COLORS (1L<<13)
30 extern struct xscreensaver_function_table *xscreensaver_function_table;
32 extern const char *progclass;
34 extern struct xlockmore_function_table xlockmore_function_table;
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 *);
46 xlockmore_setup (struct xscreensaver_function_table *xsft, void *arg)
48 struct xlockmore_function_table *xlmft =
49 (struct xlockmore_function_table *) arg;
52 XrmOptionDescRec *new_options;
54 const char *xlockmore_defaults;
55 ModeSpecOpt *xlockmore_opts = xlmft->opts;
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;
66 progclass = xlmft->progclass;
67 xlockmore_defaults = xlmft->defaults;
69 /* Translate the xlockmore `opts[]' argument to a form that
72 new_options = (XrmOptionDescRec *)
73 calloc (xlockmore_opts->numopts*3 + 100, sizeof (*new_options));
75 for (i = 0; i < xlockmore_opts->numopts; i++)
77 XrmOptionDescRec *old = &xlockmore_opts->opts[i];
78 XrmOptionDescRec *new = &new_options[i];
80 if (old->option[0] == '-')
81 new->option = old->option;
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);
90 new->specifier = strrchr (old->specifier, '.');
91 if (!new->specifier) abort();
93 new->argKind = old->argKind;
94 new->value = old->value;
97 /* Add extra args, if they're mentioned in the defaults... */
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))
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"))
110 new->argKind = XrmoptionNoArg;
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";
118 else if (!strcmp(new->option, "-use3d"))
121 new->argKind = XrmoptionNoArg;
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";
129 else if (!strcmp(new->option, "-useSHM"))
131 new->option = "-shm";
132 new->argKind = XrmoptionNoArg;
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";
142 new->argKind = XrmoptionSepArg;
150 /* Construct the kind of `defaults' that screenhack.c expects from
151 the xlockmore `vars[]' argument.
155 new_defaults = (char **) calloc (1, xlockmore_opts->numvarsdesc * 10 + 1000);
157 /* Put on the PROGCLASS.background/foreground resources. */
158 s = (char *) malloc(50);
161 strcpy (s, progclass);
163 strcat (s, ".background: black");
164 new_defaults [i++] = s;
166 s = (char *) malloc(50);
169 strcpy (s, progclass);
171 strcat (s, ".foreground: white");
172 new_defaults [i++] = s;
174 /* Copy the lines out of the `xlockmore_defaults' var and into this array. */
175 s = strdup (xlockmore_defaults);
178 new_defaults [i++] = s;
184 /* Copy the defaults out of the `xlockmore_opts->' variable. */
185 for (j = 0; j < xlockmore_opts->numvarsdesc; j++)
187 const char *def = xlockmore_opts->vars[j].def;
191 if (strlen(def) > 1000) abort();
193 s = (char *) malloc (strlen (xlockmore_opts->vars[j].name) +
196 strcat (s, xlockmore_opts->vars[j].name);
199 new_defaults [i++] = s;
201 /* Go through the list of resources and print a warning if there
205 char *onew = strdup (xlockmore_opts->vars[j].name);
206 const char *new = onew;
208 if ((s = strrchr (new, '.'))) new = s+1;
209 if ((s = strrchr (new, '*'))) new = s+1;
210 for (k = 0; k < i-1; k++)
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))
220 "%s: duplicate resource \"%s\": "
221 "set in both DEFAULTS and vars[]\n",
230 new_defaults [i] = 0;
232 xsft->progclass = progclass;
233 xsft->options = new_options;
234 xsft->defaults = (const char * const *) new_defaults;
239 xlockmore_read_resources (ModeInfo *mi)
241 Display *dpy = mi->dpy;
242 ModeSpecOpt *xlockmore_opts = mi->xlmft->opts;
244 for (i = 0; i < xlockmore_opts->numvarsdesc; i++)
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;
252 switch (xlockmore_opts->vars[i].type)
255 *var_c = get_string_resource (dpy, xlockmore_opts->vars[i].name,
256 xlockmore_opts->vars[i].classname);
259 *var_f = get_float_resource (dpy, xlockmore_opts->vars[i].name,
260 xlockmore_opts->vars[i].classname);
263 *var_i = get_integer_resource (dpy, xlockmore_opts->vars[i].name,
264 xlockmore_opts->vars[i].classname);
267 *var_b = get_boolean_resource (dpy, xlockmore_opts->vars[i].name,
268 xlockmore_opts->vars[i].classname);
278 xlockmore_init (Display *dpy, Window window,
279 struct xlockmore_function_table *xlmft)
281 ModeInfo *mi = (ModeInfo *) calloc (1, sizeof(*mi));
293 XGetWindowAttributes (dpy, window, &mi->xgwa);
295 #if defined(HAVE_COCOA) || defined(HAVE_ANDROID)
298 /* In Cocoa and Android-based xscreensaver, all hacks run in the
299 same address space, so each one needs to get its own screen
300 number. Believe what jwxyz says about screen counts and numbers.
302 mi->num_screens = ScreenCount (dpy);
303 mi->screen_number = XScreenNumberOfScreen (mi->xgwa.screen);
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.
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++;
319 #else /* real Xlib */
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.
325 mi->screen_number = 0;
327 { /* kludge for DEBUG_PAIR */
328 static int screen_tick = 0;
335 root_p = (window == RootWindowOfScreen (mi->xgwa.screen));
337 /* Everybody gets motion events, just in case. */
338 XSelectInput (dpy, window, (mi->xgwa.your_event_mask | PointerMotionMask));
340 #endif /* !HAVE_COCOA */
342 color.flags = DoRed|DoGreen|DoBlue;
343 color.red = color.green = color.blue = 0;
344 if (!XAllocColor(dpy, mi->xgwa.colormap, &color))
346 mi->black = color.pixel;
347 color.red = color.green = color.blue = 0xFFFF;
348 if (!XAllocColor(dpy, mi->xgwa.colormap, &color))
350 mi->white = color.pixel;
354 static unsigned long pixels[2];
355 static XColor colors[2];
359 mi->pixels = (unsigned long *)
360 calloc (mi->npixels, sizeof (*mi->pixels));
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;
374 mi->npixels = get_integer_resource (dpy, "ncolors", "Integer");
375 if (mi->npixels <= 0)
377 else if (mi->npixels > MAX_COLORS)
378 mi->npixels = MAX_COLORS;
380 mi->colors = (XColor *) calloc (mi->npixels, sizeof (*mi->colors));
382 mi->writable_p = mi->xlmft->want_writable_colors;
384 switch (mi->xlmft->desired_color_scheme)
386 case color_scheme_uniform:
387 make_uniform_colormap (mi->xgwa.screen, mi->xgwa.visual,
389 mi->colors, &mi->npixels,
390 True, &mi->writable_p, True);
392 case color_scheme_smooth:
393 make_smooth_colormap (mi->xgwa.screen, mi->xgwa.visual,
395 mi->colors, &mi->npixels,
396 True, &mi->writable_p, True);
398 case color_scheme_bright:
399 case color_scheme_default:
400 make_random_colormap (mi->xgwa.screen, mi->xgwa.visual,
402 mi->colors, &mi->npixels,
403 (mi->xlmft->desired_color_scheme ==
404 color_scheme_bright),
405 True, &mi->writable_p, True);
411 if (mi->npixels <= 2)
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;
422 gcv.foreground = mi->white;
423 gcv.background = mi->black;
424 mi->gc = XCreateGC (dpy, window, GCForeground|GCBackground, &gcv);
426 mi->fullrandom = True;
428 mi->pause = get_integer_resource (dpy, "delay", "Usecs");
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");
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");
445 mi->wireframe_p = get_boolean_resource (dpy, "wireframe", "Boolean");
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 */
455 else if (mi->pause > 100000000)
456 mi->pause = 100000000;
458 /* If this hack uses fonts (meaning, mentioned "font" in DEFAULTS)
461 char *name = get_string_resource (dpy, "font", "Font");
464 XFontStruct *f = XLoadQueryFont (dpy, name);
465 const char *def1 = "-*-helvetica-bold-r-normal-*-180-*";
466 const char *def2 = "fixed";
469 fprintf (stderr, "%s: font %s does not exist, using %s\n",
470 progname, name, def1);
471 f = XLoadQueryFont (dpy, def1);
475 fprintf (stderr, "%s: font %s does not exist, using %s\n",
476 progname, def1, def2);
477 f = XLoadQueryFont (dpy, def2);
479 if (f) XSetFont (dpy, mi->gc, f->fid);
480 if (f) XFreeFont (dpy, f);
485 xlockmore_read_resources (mi);
487 XClearWindow (dpy, window);
489 mi->xlmft->hack_init (mi);
495 xlockmore_draw (Display *dpy, Window window, void *closure)
497 ModeInfo *mi = (ModeInfo *) closure;
499 unsigned long orig_pause = mi->pause;
500 unsigned long this_pause;
502 mi->xlmft->hack_draw (mi);
504 this_pause = mi->pause;
505 mi->pause = orig_pause;
511 xlockmore_reshape (Display *dpy, Window window, void *closure,
512 unsigned int w, unsigned int h)
514 ModeInfo *mi = (ModeInfo *) closure;
515 if (mi && mi->xlmft->hack_reshape)
517 XGetWindowAttributes (dpy, window, &mi->xgwa);
518 mi->xlmft->hack_reshape (mi, mi->xgwa.width, mi->xgwa.height);
523 xlockmore_event (Display *dpy, Window window, void *closure, XEvent *event)
525 ModeInfo *mi = (ModeInfo *) closure;
526 if (mi && mi->xlmft->hack_handle_events)
527 return mi->xlmft->hack_handle_events (mi, event);
533 xlockmore_do_fps (Display *dpy, Window w, fps_state *fpst, void *closure)
535 ModeInfo *mi = (ModeInfo *) closure;
536 fps_compute (fpst, 0, mi ? mi->recursion_depth : -1);
542 xlockmore_free (Display *dpy, Window window, void *closure)
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.
550 ModeInfo *mi = (ModeInfo *) closure;
551 if (mi->xlmft->hack_free)
552 mi->xlmft->hack_free (mi);
554 XFreeGC (dpy, mi->gc);
555 free_colors (dpy, mi->xgwa.colormap, mi->colors, mi->npixels);