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 },
83 static char *default_defaults[] = {
85 "*geometry: 600x480", /* this should be .geometry, but nooooo... */
87 "*installColormap: false",
92 static XrmOptionDescRec *merged_options;
93 static int merged_options_size;
94 static char **merged_defaults;
99 int def_opts_size, opts_size;
100 int def_defaults_size, defaults_size;
102 for (def_opts_size = 0; default_options[def_opts_size].option;
105 for (opts_size = 0; options[opts_size].option; opts_size++)
108 merged_options_size = def_opts_size + opts_size;
109 merged_options = (XrmOptionDescRec *)
110 malloc ((merged_options_size + 1) * sizeof(*default_options));
111 memcpy (merged_options, default_options,
112 (def_opts_size * sizeof(*default_options)));
113 memcpy (merged_options + def_opts_size, options,
114 ((opts_size + 1) * sizeof(*default_options)));
116 for (def_defaults_size = 0; default_defaults[def_defaults_size];
119 for (defaults_size = 0; defaults[defaults_size]; defaults_size++)
121 merged_defaults = (char **)
122 malloc ((def_defaults_size + defaults_size + 1) * sizeof (*defaults));;
123 memcpy (merged_defaults, default_defaults,
124 def_defaults_size * sizeof(*defaults));
125 memcpy (merged_defaults + def_defaults_size, defaults,
126 (defaults_size + 1) * sizeof(*defaults));
128 /* This totally sucks. Xt should behave like this by default.
129 If the string in `defaults' looks like ".foo", change that
134 for (s = merged_defaults; *s; s++)
137 const char *oldr = *s;
138 char *newr = (char *) malloc(strlen(oldr) + strlen(progclass) + 3);
139 strcpy (newr, progclass);
147 /* Make the X errors print out the name of this program, so we have some
148 clue which one has a bug when they die under the screensaver.
152 screenhack_ehandler (Display *dpy, XErrorEvent *error)
154 fprintf (stderr, "\nX error in %s:\n", progname);
155 if (XmuPrintDefaultErrorMessage (dpy, error, stderr))
158 fprintf (stderr, " (nonfatal.)\n");
163 MapNotify_event_p (Display *dpy, XEvent *event, XPointer window)
165 return (event->xany.type == MapNotify &&
166 event->xvisibility.window == (Window) window);
171 extern void pre_merge_options (void);
175 static Atom XA_WM_PROTOCOLS, XA_WM_DELETE_WINDOW;
177 /* Dead-trivial event handling: exits if "q" or "ESC" are typed.
178 Exit if the WM_PROTOCOLS WM_DELETE_WINDOW ClientMessage is received.
181 screenhack_handle_event (Display *dpy, XEvent *event)
183 switch (event->xany.type)
189 XLookupString (&event->xkey, &c, 1, &keysym, 0);
195 else if (! (keysym >= XK_Shift_L && keysym <= XK_Hyper_R))
196 XBell (dpy, 0); /* beep for non-chord keys */
204 if (event->xclient.message_type != XA_WM_PROTOCOLS)
206 char *s = XGetAtomName(dpy, event->xclient.message_type);
207 if (!s) s = "(null)";
208 fprintf (stderr, "%s: unknown ClientMessage %s received!\n",
211 else if (event->xclient.data.l[0] != XA_WM_DELETE_WINDOW)
213 char *s1 = XGetAtomName(dpy, event->xclient.message_type);
214 char *s2 = XGetAtomName(dpy, event->xclient.data.l[0]);
215 if (!s1) s1 = "(null)";
216 if (!s2) s2 = "(null)";
217 fprintf (stderr, "%s: unknown ClientMessage %s[%s] received!\n",
231 screenhack_handle_events (Display *dpy)
233 while (XPending (dpy))
236 XNextEvent (dpy, &event);
237 screenhack_handle_event (dpy, &event);
243 pick_visual (Screen *screen)
246 /* If we're linking against GL (that is, this is the version of screenhack.o
247 that the GL hacks will use, which is different from the one that the
248 non-GL hacks will use) then try to pick the "best" visual by interrogating
249 the GL library instead of by asking Xlib. GL knows better.
252 char *string = get_string_resource ("visualID", "VisualID");
256 for (s = string; *s; s++)
257 if (isupper (*s)) *s = _tolower (*s);
259 if (!string || !*string ||
260 !strcmp (string, "gl") ||
261 !strcmp (string, "best") ||
262 !strcmp (string, "color") ||
263 !strcmp (string, "default"))
264 v = get_gl_visual (screen); /* from ../utils/visual-gl.c */
272 return get_visual_resource (screen, "visualID", "VisualID", False);
276 /* Notice when the user has requested a different visual or colormap
277 on a pre-existing window (e.g., "-root -visual truecolor") and
278 complain, since when drawing on an existing window, we have no
279 choice about these things.
282 visual_warning (Screen *screen, Window window, Visual *visual, Colormap cmap,
285 char *visual_string = get_string_resource ("visualID", "VisualID");
286 Visual *desired_visual = pick_visual (screen);
290 if (window == RootWindowOfScreen (screen))
291 strcpy (win, "root window");
293 sprintf (win, "window 0x%x", (unsigned long) window);
296 sprintf (why, "-window-id 0x%x", (unsigned long) window);
298 strcpy (why, "-root");
300 if (visual_string && *visual_string)
303 for (s = visual_string; *s; s++)
304 if (isupper (*s)) *s = _tolower (*s);
306 if (!strcmp (visual_string, "default") ||
307 !strcmp (visual_string, "default") ||
308 !strcmp (visual_string, "best"))
309 /* don't warn about these, just silently DWIM. */
311 else if (visual != desired_visual)
313 fprintf (stderr, "%s: ignoring `-visual %s' because of `%s'.\n",
314 progname, visual_string, why);
315 fprintf (stderr, "%s: using %s's visual 0x%x.\n",
316 progname, win, XVisualIDFromVisual (visual));
318 free (visual_string);
321 if (visual == DefaultVisualOfScreen (screen) &&
322 has_writable_cells (screen, visual) &&
323 get_boolean_resource ("installColormap", "InstallColormap"))
325 fprintf (stderr, "%s: ignoring `-install' because of `%s'.\n",
327 fprintf (stderr, "%s: using %s's colormap 0x%x.\n",
328 progname, win, (unsigned long) cmap);
332 if (!validate_gl_visual (stderr, screen, win, visual))
339 main (int argc, char **argv)
349 Boolean dont_clear /*, dont_map */;
353 pre_merge_options ();
358 /* We have to do this on SGI to prevent the background color from being
359 overridden by the current desktop color scheme (we'd like our backgrounds
360 to be black, thanks.) This should be the same as setting the
361 "*useSchemes: none" resource, but it's not -- if that resource is
362 present in the `default_defaults' above, it doesn't work, though it
363 does work when passed as an -xrm arg on the command line. So screw it,
364 turn them off from C instead.
366 SgiUseSchemes ("none");
369 toplevel = XtAppInitialize (&app, progclass, merged_options,
370 merged_options_size, &argc, argv,
371 merged_defaults, 0, 0);
372 dpy = XtDisplay (toplevel);
373 screen = XtScreen (toplevel);
374 db = XtDatabase (dpy);
376 XtGetApplicationNameAndClass (dpy, &progname, &progclass);
378 /* half-assed way of avoiding buffer-overrun attacks. */
379 if (strlen (progname) >= 100) progname[100] = 0;
381 XSetErrorHandler (screenhack_ehandler);
383 XA_WM_PROTOCOLS = XInternAtom (dpy, "WM_PROTOCOLS", False);
384 XA_WM_DELETE_WINDOW = XInternAtom (dpy, "WM_DELETE_WINDOW", False);
387 char *v = (char *) strdup(strchr(screensaver_id, ' '));
388 char *s1, *s2, *s3, *s4;
389 s1 = (char *) strchr(v, ' '); s1++;
390 s2 = (char *) strchr(s1, ' ');
391 s3 = (char *) strchr(v, '('); s3++;
392 s4 = (char *) strchr(s3, ')');
395 sprintf (version, "%s: from the XScreenSaver %s distribution (%s.)",
406 Bool help_p = !strcmp(argv[1], "-help");
407 fprintf (stderr, "%s\n", version);
408 for (s = progclass; *s; s++) fprintf(stderr, " ");
409 fprintf (stderr, " http://www.jwz.org/xscreensaver/\n\n");
412 fprintf(stderr, "Unrecognised option: %s\n", argv[1]);
413 fprintf (stderr, "Options include: ");
414 for (i = 0; i < merged_options_size; i++)
416 char *sw = merged_options [i].option;
417 Bool argp = (merged_options [i].argKind == XrmoptionSepArg);
418 int size = strlen (sw) + (argp ? 6 : 0) + 2;
421 fprintf (stderr, "\n\t\t ");
425 fprintf (stderr, "%s", sw);
426 if (argp) fprintf (stderr, " <arg>");
427 if (i != merged_options_size - 1) fprintf (stderr, ", ");
429 fprintf (stderr, ".\n");
430 exit (help_p ? 0 : 1);
433 dont_clear = get_boolean_resource ("dontClearRoot", "Boolean");
434 /*dont_map = get_boolean_resource ("dontMapWindow", "Boolean"); */
435 mono_p = get_boolean_resource ("mono", "Boolean");
436 if (CellsOfScreen (DefaultScreenOfDisplay (dpy)) <= 2)
439 root_p = get_boolean_resource ("root", "Boolean");
443 XWindowAttributes xgwa;
444 window = RootWindowOfScreen (XtScreen (toplevel));
445 XtDestroyWidget (toplevel);
446 XGetWindowAttributes (dpy, window, &xgwa);
447 cmap = xgwa.colormap;
448 visual = xgwa.visual;
449 visual_warning (screen, window, visual, cmap, False);
453 Boolean def_visual_p;
454 visual = pick_visual (screen);
457 if (!validate_gl_visual (stderr, screen, "window", visual))
461 if (toplevel->core.width <= 0)
462 toplevel->core.width = 600;
463 if (toplevel->core.height <= 0)
464 toplevel->core.height = 480;
466 def_visual_p = (visual == DefaultVisualOfScreen (screen));
473 cmap = XCreateColormap (dpy, RootWindowOfScreen(screen),
475 bg = get_pixel_resource ("background", "Background", dpy, cmap);
476 bd = get_pixel_resource ("borderColor", "Foreground", dpy, cmap);
478 new = XtVaAppCreateShell (progname, progclass,
479 topLevelShellWidgetClass, dpy,
480 XtNmappedWhenManaged, False,
482 XtNdepth, visual_depth (screen, visual),
483 XtNwidth, toplevel->core.width,
484 XtNheight, toplevel->core.height,
486 XtNbackground, (Pixel) bg,
487 XtNborderColor, (Pixel) bd,
488 XtNinput, True, /* for WM_HINTS */
490 XtDestroyWidget (toplevel);
492 XtRealizeWidget (toplevel);
493 window = XtWindow (toplevel);
497 XtVaSetValues (toplevel,
498 XtNmappedWhenManaged, False,
499 XtNinput, True, /* for WM_HINTS */
501 XtRealizeWidget (toplevel);
502 window = XtWindow (toplevel);
504 if (get_boolean_resource ("installColormap", "InstallColormap"))
506 cmap = XCreateColormap (dpy, window,
507 DefaultVisualOfScreen (XtScreen (toplevel)),
509 XSetWindowColormap (dpy, window, cmap);
513 cmap = DefaultColormap (dpy, DefaultScreen (dpy));
520 XtVaSetValues (toplevel, XtNmappedWhenManaged, False, 0);
521 XtRealizeWidget (toplevel);
526 XtPopup (toplevel, XtGrabNone);
529 XtVaSetValues(toplevel, XtNtitle, version, 0);
531 /* For screenhack_handle_events(): select KeyPress, and
532 announce that we accept WM_DELETE_WINDOW. */
534 XWindowAttributes xgwa;
535 XGetWindowAttributes (dpy, window, &xgwa);
536 XSelectInput (dpy, window,
537 xgwa.your_event_mask | KeyPressMask | ButtonPressMask);
538 XChangeProperty (dpy, window, XA_WM_PROTOCOLS, XA_ATOM, 32,
540 (unsigned char *) &XA_WM_DELETE_WINDOW, 1);
546 XSetWindowBackground (dpy, window,
547 get_pixel_resource ("background", "Background",
549 XClearWindow (dpy, window);
553 /* wait for it to be mapped */
554 XIfEvent (dpy, &event, MapNotify_event_p, (XPointer) window);
558 /* This is the one and only place that the random-number generator is
559 seeded in any screenhack. You do not need to seed the RNG again,
560 it is done for you before your code is invoked. */
564 screenhack (dpy, window); /* doesn't return */