1 /* xscreensaver, Copyright (c) 1992, 1995, 1997, 1998, 2001
2 * 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 * And remember: X Windows is to graphics hacking as roman numerals are to
13 * the square root of pi.
16 /* This file contains simple code to open a window or draw on the root.
17 The idea being that, when writing a graphics hack, you can just link
18 with this .o to get all of the uninteresting junk out of the way.
20 - create a procedure `screenhack(dpy, window)'
22 - create a variable `char *progclass' which names this program's
25 - create a variable `char defaults []' for the default resources, and
28 - create a variable `XrmOptionDescRec options[]' for the command-line,
29 and null-terminate it.
35 #include <X11/Intrinsic.h>
36 #include <X11/IntrinsicP.h>
37 #include <X11/CoreP.h>
38 #include <X11/Shell.h>
39 #include <X11/StringDefs.h>
40 #include <X11/Xutil.h>
41 #include <X11/keysym.h>
44 # include <X11/SGIScheme.h> /* for SgiUseSchemes() */
49 # include <X11/Xmu/Error.h>
51 # include <Xmu/Error.h>
56 #include "screenhack.h"
61 # define isupper(c) ((c) >= 'A' && (c) <= 'Z')
64 # define _tolower(c) ((c) - 'A' + 'a')
73 static XrmOptionDescRec default_options [] = {
74 { "-root", ".root", XrmoptionNoArg, "True" },
75 { "-window", ".root", XrmoptionNoArg, "False" },
76 { "-mono", ".mono", XrmoptionNoArg, "True" },
77 { "-install", ".installColormap", XrmoptionNoArg, "True" },
78 { "-noinstall",".installColormap", XrmoptionNoArg, "False" },
79 { "-visual", ".visualID", XrmoptionSepArg, 0 },
80 { "-window-id", ".windowID", XrmoptionSepArg, 0 },
84 static char *default_defaults[] = {
86 "*geometry: 600x480", /* this should be .geometry, but nooooo... */
88 "*installColormap: false",
91 "*desktopGrabber: xscreensaver-getimage %s",
95 static XrmOptionDescRec *merged_options;
96 static int merged_options_size;
97 static char **merged_defaults;
102 int def_opts_size, opts_size;
103 int def_defaults_size, defaults_size;
105 for (def_opts_size = 0; default_options[def_opts_size].option;
108 for (opts_size = 0; options[opts_size].option; opts_size++)
111 merged_options_size = def_opts_size + opts_size;
112 merged_options = (XrmOptionDescRec *)
113 malloc ((merged_options_size + 1) * sizeof(*default_options));
114 memcpy (merged_options, default_options,
115 (def_opts_size * sizeof(*default_options)));
116 memcpy (merged_options + def_opts_size, options,
117 ((opts_size + 1) * sizeof(*default_options)));
119 for (def_defaults_size = 0; default_defaults[def_defaults_size];
122 for (defaults_size = 0; defaults[defaults_size]; defaults_size++)
124 merged_defaults = (char **)
125 malloc ((def_defaults_size + defaults_size + 1) * sizeof (*defaults));;
126 memcpy (merged_defaults, default_defaults,
127 def_defaults_size * sizeof(*defaults));
128 memcpy (merged_defaults + def_defaults_size, defaults,
129 (defaults_size + 1) * sizeof(*defaults));
131 /* This totally sucks. Xt should behave like this by default.
132 If the string in `defaults' looks like ".foo", change that
137 for (s = merged_defaults; *s; s++)
140 const char *oldr = *s;
141 char *newr = (char *) malloc(strlen(oldr) + strlen(progclass) + 3);
142 strcpy (newr, progclass);
150 /* Make the X errors print out the name of this program, so we have some
151 clue which one has a bug when they die under the screensaver.
155 screenhack_ehandler (Display *dpy, XErrorEvent *error)
157 fprintf (stderr, "\nX error in %s:\n", progname);
158 if (XmuPrintDefaultErrorMessage (dpy, error, stderr))
161 fprintf (stderr, " (nonfatal.)\n");
166 MapNotify_event_p (Display *dpy, XEvent *event, XPointer window)
168 return (event->xany.type == MapNotify &&
169 event->xvisibility.window == (Window) window);
174 extern void pre_merge_options (void);
178 static Atom XA_WM_PROTOCOLS, XA_WM_DELETE_WINDOW;
180 /* Dead-trivial event handling: exits if "q" or "ESC" are typed.
181 Exit if the WM_PROTOCOLS WM_DELETE_WINDOW ClientMessage is received.
184 screenhack_handle_event (Display *dpy, XEvent *event)
186 switch (event->xany.type)
192 XLookupString (&event->xkey, &c, 1, &keysym, 0);
198 else if (! (keysym >= XK_Shift_L && keysym <= XK_Hyper_R))
199 XBell (dpy, 0); /* beep for non-chord keys */
207 if (event->xclient.message_type != XA_WM_PROTOCOLS)
209 char *s = XGetAtomName(dpy, event->xclient.message_type);
210 if (!s) s = "(null)";
211 fprintf (stderr, "%s: unknown ClientMessage %s received!\n",
214 else if (event->xclient.data.l[0] != XA_WM_DELETE_WINDOW)
216 char *s1 = XGetAtomName(dpy, event->xclient.message_type);
217 char *s2 = XGetAtomName(dpy, event->xclient.data.l[0]);
218 if (!s1) s1 = "(null)";
219 if (!s2) s2 = "(null)";
220 fprintf (stderr, "%s: unknown ClientMessage %s[%s] received!\n",
234 screenhack_handle_events (Display *dpy)
236 while (XPending (dpy))
239 XNextEvent (dpy, &event);
240 screenhack_handle_event (dpy, &event);
246 pick_visual (Screen *screen)
249 /* If we're linking against GL (that is, this is the version of screenhack.o
250 that the GL hacks will use, which is different from the one that the
251 non-GL hacks will use) then try to pick the "best" visual by interrogating
252 the GL library instead of by asking Xlib. GL knows better.
255 char *string = get_string_resource ("visualID", "VisualID");
259 for (s = string; *s; s++)
260 if (isupper (*s)) *s = _tolower (*s);
262 if (!string || !*string ||
263 !strcmp (string, "gl") ||
264 !strcmp (string, "best") ||
265 !strcmp (string, "color") ||
266 !strcmp (string, "default"))
267 v = get_gl_visual (screen); /* from ../utils/visual-gl.c */
275 return get_visual_resource (screen, "visualID", "VisualID", False);
279 /* Notice when the user has requested a different visual or colormap
280 on a pre-existing window (e.g., "-root -visual truecolor" or
281 "-window-id 0x2c00001 -install") and complain, since when drawing
282 on an existing window, we have no choice about these things.
285 visual_warning (Screen *screen, Window window, Visual *visual, Colormap cmap,
288 char *visual_string = get_string_resource ("visualID", "VisualID");
289 Visual *desired_visual = pick_visual (screen);
293 if (window == RootWindowOfScreen (screen))
294 strcpy (win, "root window");
296 sprintf (win, "window 0x%x", (unsigned long) window);
299 sprintf (why, "-window-id 0x%x", (unsigned long) window);
301 strcpy (why, "-root");
303 if (visual_string && *visual_string)
306 for (s = visual_string; *s; s++)
307 if (isupper (*s)) *s = _tolower (*s);
309 if (!strcmp (visual_string, "default") ||
310 !strcmp (visual_string, "default") ||
311 !strcmp (visual_string, "best"))
312 /* don't warn about these, just silently DWIM. */
314 else if (visual != desired_visual)
316 fprintf (stderr, "%s: ignoring `-visual %s' because of `%s'.\n",
317 progname, visual_string, why);
318 fprintf (stderr, "%s: using %s's visual 0x%x.\n",
319 progname, win, XVisualIDFromVisual (visual));
321 free (visual_string);
324 if (visual == DefaultVisualOfScreen (screen) &&
325 has_writable_cells (screen, visual) &&
326 get_boolean_resource ("installColormap", "InstallColormap"))
328 fprintf (stderr, "%s: ignoring `-install' because of `%s'.\n",
330 fprintf (stderr, "%s: using %s's colormap 0x%x.\n",
331 progname, win, (unsigned long) cmap);
335 if (!validate_gl_visual (stderr, screen, win, visual))
342 main (int argc, char **argv)
351 Window on_window = 0;
353 Boolean dont_clear /*, dont_map */;
357 pre_merge_options ();
362 /* We have to do this on SGI to prevent the background color from being
363 overridden by the current desktop color scheme (we'd like our backgrounds
364 to be black, thanks.) This should be the same as setting the
365 "*useSchemes: none" resource, but it's not -- if that resource is
366 present in the `default_defaults' above, it doesn't work, though it
367 does work when passed as an -xrm arg on the command line. So screw it,
368 turn them off from C instead.
370 SgiUseSchemes ("none");
373 toplevel = XtAppInitialize (&app, progclass, merged_options,
374 merged_options_size, &argc, argv,
375 merged_defaults, 0, 0);
376 dpy = XtDisplay (toplevel);
377 screen = XtScreen (toplevel);
378 db = XtDatabase (dpy);
380 XtGetApplicationNameAndClass (dpy, &progname, &progclass);
382 /* half-assed way of avoiding buffer-overrun attacks. */
383 if (strlen (progname) >= 100) progname[100] = 0;
385 XSetErrorHandler (screenhack_ehandler);
387 XA_WM_PROTOCOLS = XInternAtom (dpy, "WM_PROTOCOLS", False);
388 XA_WM_DELETE_WINDOW = XInternAtom (dpy, "WM_DELETE_WINDOW", False);
391 char *v = (char *) strdup(strchr(screensaver_id, ' '));
392 char *s1, *s2, *s3, *s4;
393 s1 = (char *) strchr(v, ' '); s1++;
394 s2 = (char *) strchr(s1, ' ');
395 s3 = (char *) strchr(v, '('); s3++;
396 s4 = (char *) strchr(s3, ')');
399 sprintf (version, "%s: from the XScreenSaver %s distribution (%s.)",
410 Bool help_p = (!strcmp(argv[1], "-help") ||
411 !strcmp(argv[1], "--help"));
412 fprintf (stderr, "%s\n", version);
413 for (s = progclass; *s; s++) fprintf(stderr, " ");
414 fprintf (stderr, " http://www.jwz.org/xscreensaver/\n\n");
417 fprintf(stderr, "Unrecognised option: %s\n", argv[1]);
418 fprintf (stderr, "Options include: ");
419 for (i = 0; i < merged_options_size; i++)
421 char *sw = merged_options [i].option;
422 Bool argp = (merged_options [i].argKind == XrmoptionSepArg);
423 int size = strlen (sw) + (argp ? 6 : 0) + 2;
426 fprintf (stderr, "\n\t\t ");
430 fprintf (stderr, "%s", sw);
431 if (argp) fprintf (stderr, " <arg>");
432 if (i != merged_options_size - 1) fprintf (stderr, ", ");
435 fprintf (stderr, ".\n");
440 fprintf (stderr, "\nResources:\n\n");
441 for (i = 0; i < merged_options_size; i++)
443 const char *opt = merged_options [i].option;
444 const char *res = merged_options [i].specifier + 1;
445 const char *val = merged_options [i].value;
446 char *s = get_string_resource ((char *) res, (char *) res);
451 while (L > 0 && (s[L-1] == ' ' || s[L-1] == '\t'))
455 fprintf (stderr, " %-16s %-18s ", opt, res);
456 if (merged_options [i].argKind == XrmoptionSepArg)
458 fprintf (stderr, "[%s]", (s ? s : "?"));
462 fprintf (stderr, "%s", (val ? val : "(null)"));
463 if (val && s && !strcasecmp (val, s))
464 fprintf (stderr, " [default]");
466 fprintf (stderr, "\n");
468 fprintf (stderr, "\n");
472 exit (help_p ? 0 : 1);
475 dont_clear = get_boolean_resource ("dontClearRoot", "Boolean");
476 /*dont_map = get_boolean_resource ("dontMapWindow", "Boolean"); */
477 mono_p = get_boolean_resource ("mono", "Boolean");
478 if (CellsOfScreen (DefaultScreenOfDisplay (dpy)) <= 2)
481 root_p = get_boolean_resource ("root", "Boolean");
484 char *s = get_string_resource ("windowID", "WindowID");
486 on_window = get_integer_resource ("windowID", "WindowID");
492 XWindowAttributes xgwa;
493 window = (Window) on_window;
494 XtDestroyWidget (toplevel);
495 XGetWindowAttributes (dpy, window, &xgwa);
496 cmap = xgwa.colormap;
497 visual = xgwa.visual;
498 visual_warning (screen, window, visual, cmap, True);
502 XWindowAttributes xgwa;
503 window = RootWindowOfScreen (XtScreen (toplevel));
504 XtDestroyWidget (toplevel);
505 XGetWindowAttributes (dpy, window, &xgwa);
506 cmap = xgwa.colormap;
507 visual = xgwa.visual;
508 visual_warning (screen, window, visual, cmap, False);
512 Boolean def_visual_p;
513 visual = pick_visual (screen);
516 if (!validate_gl_visual (stderr, screen, "window", visual))
520 if (toplevel->core.width <= 0)
521 toplevel->core.width = 600;
522 if (toplevel->core.height <= 0)
523 toplevel->core.height = 480;
525 def_visual_p = (visual == DefaultVisualOfScreen (screen));
532 cmap = XCreateColormap (dpy, RootWindowOfScreen(screen),
534 bg = get_pixel_resource ("background", "Background", dpy, cmap);
535 bd = get_pixel_resource ("borderColor", "Foreground", dpy, cmap);
537 new = XtVaAppCreateShell (progname, progclass,
538 topLevelShellWidgetClass, dpy,
539 XtNmappedWhenManaged, False,
541 XtNdepth, visual_depth (screen, visual),
542 XtNwidth, toplevel->core.width,
543 XtNheight, toplevel->core.height,
545 XtNbackground, (Pixel) bg,
546 XtNborderColor, (Pixel) bd,
547 XtNinput, True, /* for WM_HINTS */
549 XtDestroyWidget (toplevel);
551 XtRealizeWidget (toplevel);
552 window = XtWindow (toplevel);
556 XtVaSetValues (toplevel,
557 XtNmappedWhenManaged, False,
558 XtNinput, True, /* for WM_HINTS */
560 XtRealizeWidget (toplevel);
561 window = XtWindow (toplevel);
563 if (get_boolean_resource ("installColormap", "InstallColormap"))
565 cmap = XCreateColormap (dpy, window,
566 DefaultVisualOfScreen (XtScreen (toplevel)),
568 XSetWindowColormap (dpy, window, cmap);
572 cmap = DefaultColormap (dpy, DefaultScreen (dpy));
579 XtVaSetValues (toplevel, XtNmappedWhenManaged, False, 0);
580 XtRealizeWidget (toplevel);
585 XtPopup (toplevel, XtGrabNone);
588 XtVaSetValues(toplevel, XtNtitle, version, 0);
590 /* For screenhack_handle_events(): select KeyPress, and
591 announce that we accept WM_DELETE_WINDOW. */
593 XWindowAttributes xgwa;
594 XGetWindowAttributes (dpy, window, &xgwa);
595 XSelectInput (dpy, window,
596 xgwa.your_event_mask | KeyPressMask | ButtonPressMask);
597 XChangeProperty (dpy, window, XA_WM_PROTOCOLS, XA_ATOM, 32,
599 (unsigned char *) &XA_WM_DELETE_WINDOW, 1);
605 XSetWindowBackground (dpy, window,
606 get_pixel_resource ("background", "Background",
608 XClearWindow (dpy, window);
611 if (!root_p && !on_window)
612 /* wait for it to be mapped */
613 XIfEvent (dpy, &event, MapNotify_event_p, (XPointer) window);
617 /* This is the one and only place that the random-number generator is
618 seeded in any screenhack. You do not need to seed the RNG again,
619 it is done for you before your code is invoked. */
623 screenhack (dpy, window); /* doesn't return */