1 /* xscreensaver, Copyright (c) 1992, 1995, 1997, 1998
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"
65 static XrmOptionDescRec default_options [] = {
66 { "-root", ".root", XrmoptionNoArg, "True" },
67 { "-window", ".root", XrmoptionNoArg, "False" },
68 { "-mono", ".mono", XrmoptionNoArg, "True" },
69 { "-install", ".installColormap", XrmoptionNoArg, "True" },
70 { "-noinstall",".installColormap", XrmoptionNoArg, "False" },
71 { "-visual", ".visualID", XrmoptionSepArg, 0 },
72 { "-window-id", ".windowID", XrmoptionSepArg, 0 },
76 static char *default_defaults[] = {
78 "*geometry: 600x480", /* this should be .geometry, but nooooo... */
80 "*installColormap: false",
86 static XrmOptionDescRec *merged_options;
87 static int merged_options_size;
88 static char **merged_defaults;
93 int def_opts_size, opts_size;
94 int def_defaults_size, defaults_size;
96 for (def_opts_size = 0; default_options[def_opts_size].option;
99 for (opts_size = 0; options[opts_size].option; opts_size++)
102 merged_options_size = def_opts_size + opts_size;
103 merged_options = (XrmOptionDescRec *)
104 malloc ((merged_options_size + 1) * sizeof(*default_options));
105 memcpy (merged_options, default_options,
106 (def_opts_size * sizeof(*default_options)));
107 memcpy (merged_options + def_opts_size, options,
108 ((opts_size + 1) * sizeof(*default_options)));
110 for (def_defaults_size = 0; default_defaults[def_defaults_size];
113 for (defaults_size = 0; defaults[defaults_size]; defaults_size++)
115 merged_defaults = (char **)
116 malloc ((def_defaults_size + defaults_size + 1) * sizeof (*defaults));;
117 memcpy (merged_defaults, default_defaults,
118 def_defaults_size * sizeof(*defaults));
119 memcpy (merged_defaults + def_defaults_size, defaults,
120 (defaults_size + 1) * sizeof(*defaults));
122 /* This totally sucks. Xt should behave like this by default.
123 If the string in `defaults' looks like ".foo", change that
128 for (s = merged_defaults; *s; s++)
131 const char *oldr = *s;
132 char *newr = (char *) malloc(strlen(oldr) + strlen(progclass) + 3);
133 strcpy (newr, progclass);
141 /* Make the X errors print out the name of this program, so we have some
142 clue which one has a bug when they die under the screensaver.
146 screenhack_ehandler (Display *dpy, XErrorEvent *error)
148 fprintf (stderr, "\nX error in %s:\n", progname);
149 if (XmuPrintDefaultErrorMessage (dpy, error, stderr))
152 fprintf (stderr, " (nonfatal.)\n");
157 MapNotify_event_p (Display *dpy, XEvent *event, XPointer window)
159 return (event->xany.type == MapNotify &&
160 event->xvisibility.window == (Window) window);
165 extern void pre_merge_options (void);
169 static Atom XA_WM_PROTOCOLS, XA_WM_DELETE_WINDOW;
171 /* Dead-trivial event handling: exits if "q" or "ESC" are typed.
172 Exit if the WM_PROTOCOLS WM_DELETE_WINDOW ClientMessage is received.
175 screenhack_handle_event (Display *dpy, XEvent *event)
177 switch (event->xany.type)
183 XLookupString (&event->xkey, &c, 1, &keysym, 0);
189 else if (! (keysym >= XK_Shift_L && keysym <= XK_Hyper_R))
190 XBell (dpy, 0); /* beep for non-chord keys */
198 if (event->xclient.message_type != XA_WM_PROTOCOLS)
200 char *s = XGetAtomName(dpy, event->xclient.message_type);
201 if (!s) s = "(null)";
202 fprintf (stderr, "%s: unknown ClientMessage %s received!\n",
205 else if (event->xclient.data.l[0] != XA_WM_DELETE_WINDOW)
207 char *s1 = XGetAtomName(dpy, event->xclient.message_type);
208 char *s2 = XGetAtomName(dpy, event->xclient.data.l[0]);
209 if (!s1) s1 = "(null)";
210 if (!s2) s2 = "(null)";
211 fprintf (stderr, "%s: unknown ClientMessage %s[%s] received!\n",
225 screenhack_handle_events (Display *dpy)
227 while (XPending (dpy))
230 XNextEvent (dpy, &event);
231 screenhack_handle_event (dpy, &event);
237 pick_visual (Screen *screen)
240 /* If we're linking against GL (that is, this is the version of screenhack.o
241 that the GL hacks will use, which is different from the one that the
242 non-GL hacks will use) then try to pick the "best" visual by interrogating
243 the GL library instead of by asking Xlib. GL knows better.
246 char *string = get_string_resource ("visualID", "VisualID");
248 if (!string || !*string ||
249 !strcmp (string, "gl") ||
250 !strcmp (string, "best") ||
251 !strcmp (string, "color") ||
252 !strcmp (string, "default"))
253 v = get_gl_visual (screen); /* from ../utils/visual-gl.c */
261 return get_visual_resource (screen, "visualID", "VisualID", False);
266 main (int argc, char **argv)
274 Window on_window = 0;
276 Boolean dont_clear /*, dont_map */;
280 pre_merge_options ();
285 /* We have to do this on SGI to prevent the background color from being
286 overridden by the current desktop color scheme (we'd like our backgrounds
287 to be black, thanks.) This should be the same as setting the
288 "*useSchemes: none" resource, but it's not -- if that resource is
289 present in the `default_defaults' above, it doesn't work, though it
290 does work when passed as an -xrm arg on the command line. So screw it,
291 turn them off from C instead.
293 SgiUseSchemes ("none");
296 toplevel = XtAppInitialize (&app, progclass, merged_options,
297 merged_options_size, &argc, argv,
298 merged_defaults, 0, 0);
299 dpy = XtDisplay (toplevel);
300 db = XtDatabase (dpy);
301 XtGetApplicationNameAndClass (dpy, &progname, &progclass);
303 /* half-assed way of avoiding buffer-overrun attacks. */
304 if (strlen (progname) >= 100) progname[100] = 0;
306 XSetErrorHandler (screenhack_ehandler);
308 XA_WM_PROTOCOLS = XInternAtom (dpy, "WM_PROTOCOLS", False);
309 XA_WM_DELETE_WINDOW = XInternAtom (dpy, "WM_DELETE_WINDOW", False);
312 char *v = (char *) strdup(strchr(screensaver_id, ' '));
313 char *s1, *s2, *s3, *s4;
314 s1 = (char *) strchr(v, ' '); s1++;
315 s2 = (char *) strchr(s1, ' ');
316 s3 = (char *) strchr(v, '('); s3++;
317 s4 = (char *) strchr(s3, ')');
320 sprintf (version, "%s: from the XScreenSaver %s distribution (%s.)",
331 Bool help_p = !strcmp(argv[1], "-help");
332 fprintf (stderr, "%s\n", version);
333 for (s = progclass; *s; s++) fprintf(stderr, " ");
334 fprintf (stderr, " http://www.jwz.org/xscreensaver/\n\n");
337 fprintf(stderr, "Unrecognised option: %s\n", argv[1]);
338 fprintf (stderr, "Options include: ");
339 for (i = 0; i < merged_options_size; i++)
341 char *sw = merged_options [i].option;
342 Bool argp = (merged_options [i].argKind == XrmoptionSepArg);
343 int size = strlen (sw) + (argp ? 6 : 0) + 2;
346 fprintf (stderr, "\n\t\t ");
350 fprintf (stderr, "%s", sw);
351 if (argp) fprintf (stderr, " <arg>");
352 if (i != merged_options_size - 1) fprintf (stderr, ", ");
354 fprintf (stderr, ".\n");
355 exit (help_p ? 0 : 1);
358 dont_clear = get_boolean_resource ("dontClearRoot", "Boolean");
359 /*dont_map = get_boolean_resource ("dontMapWindow", "Boolean"); */
360 mono_p = get_boolean_resource ("mono", "Boolean");
361 if (CellsOfScreen (DefaultScreenOfDisplay (dpy)) <= 2)
364 root_p = get_boolean_resource ("root", "Boolean");
367 char *s = get_string_resource ("windowID", "WindowID");
369 on_window = get_integer_resource ("windowID", "WindowID");
375 XWindowAttributes xgwa;
376 window = (Window) on_window;
377 XtDestroyWidget (toplevel);
378 XGetWindowAttributes (dpy, window, &xgwa);
379 cmap = xgwa.colormap;
380 visual = xgwa.visual;
384 XWindowAttributes xgwa;
385 window = RootWindowOfScreen (XtScreen (toplevel));
386 XtDestroyWidget (toplevel);
387 XGetWindowAttributes (dpy, window, &xgwa);
388 cmap = xgwa.colormap;
389 visual = xgwa.visual;
393 Boolean def_visual_p;
394 Screen *screen = XtScreen (toplevel);
395 visual = pick_visual (screen);
397 if (toplevel->core.width <= 0)
398 toplevel->core.width = 600;
399 if (toplevel->core.height <= 0)
400 toplevel->core.height = 480;
402 def_visual_p = (visual == DefaultVisualOfScreen (screen));
409 cmap = XCreateColormap (dpy, RootWindowOfScreen(screen),
411 bg = get_pixel_resource ("background", "Background", dpy, cmap);
412 bd = get_pixel_resource ("borderColor", "Foreground", dpy, cmap);
414 new = XtVaAppCreateShell (progname, progclass,
415 topLevelShellWidgetClass, dpy,
416 XtNmappedWhenManaged, False,
418 XtNdepth, visual_depth (screen, visual),
419 XtNwidth, toplevel->core.width,
420 XtNheight, toplevel->core.height,
422 XtNbackground, (Pixel) bg,
423 XtNborderColor, (Pixel) bd,
425 XtDestroyWidget (toplevel);
427 XtRealizeWidget (toplevel);
428 window = XtWindow (toplevel);
432 XtVaSetValues (toplevel, XtNmappedWhenManaged, False, 0);
433 XtRealizeWidget (toplevel);
434 window = XtWindow (toplevel);
436 if (get_boolean_resource ("installColormap", "InstallColormap"))
438 cmap = XCreateColormap (dpy, window,
439 DefaultVisualOfScreen (XtScreen (toplevel)),
441 XSetWindowColormap (dpy, window, cmap);
445 cmap = DefaultColormap (dpy, DefaultScreen (dpy));
452 XtVaSetValues (toplevel, XtNmappedWhenManaged, False, 0);
453 XtRealizeWidget (toplevel);
458 XtPopup (toplevel, XtGrabNone);
461 XtVaSetValues(toplevel, XtNtitle, version, 0);
463 /* For screenhack_handle_events(): select KeyPress, and
464 announce that we accept WM_DELETE_WINDOW. */
466 XWindowAttributes xgwa;
467 XGetWindowAttributes (dpy, window, &xgwa);
468 XSelectInput (dpy, window,
469 xgwa.your_event_mask | KeyPressMask | ButtonPressMask);
470 XChangeProperty (dpy, window, XA_WM_PROTOCOLS, XA_ATOM, 32,
472 (unsigned char *) &XA_WM_DELETE_WINDOW, 1);
478 XSetWindowBackground (dpy, window,
479 get_pixel_resource ("background", "Background",
481 XClearWindow (dpy, window);
484 if (!root_p && !on_window)
485 /* wait for it to be mapped */
486 XIfEvent (dpy, &event, MapNotify_event_p, (XPointer) window);
489 srandom ((int) time ((time_t *) 0));
490 screenhack (dpy, window); /* doesn't return */