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>
43 # include <X11/SGIScheme.h> /* for SgiUseSchemes() */
48 # include <X11/Xmu/Error.h>
50 # include <Xmu/Error.h>
55 #include "screenhack.h"
63 static XrmOptionDescRec default_options [] = {
64 { "-root", ".root", XrmoptionNoArg, "True" },
65 { "-window", ".root", XrmoptionNoArg, "False" },
66 { "-mono", ".mono", XrmoptionNoArg, "True" },
67 { "-install", ".installColormap", XrmoptionNoArg, "True" },
68 { "-noinstall",".installColormap", XrmoptionNoArg, "False" },
69 { "-visual", ".visualID", XrmoptionSepArg, 0 },
70 { "-window-id", ".windowID", XrmoptionSepArg, 0 },
74 static char *default_defaults[] = {
76 "*geometry: 600x480", /* this should be .geometry, but nooooo... */
78 "*installColormap: false",
84 static XrmOptionDescRec *merged_options;
85 static int merged_options_size;
86 static char **merged_defaults;
91 int def_opts_size, opts_size;
92 int def_defaults_size, defaults_size;
94 for (def_opts_size = 0; default_options[def_opts_size].option;
97 for (opts_size = 0; options[opts_size].option; opts_size++)
100 merged_options_size = def_opts_size + opts_size;
101 merged_options = (XrmOptionDescRec *)
102 malloc ((merged_options_size + 1) * sizeof(*default_options));
103 memcpy (merged_options, default_options,
104 (def_opts_size * sizeof(*default_options)));
105 memcpy (merged_options + def_opts_size, options,
106 ((opts_size + 1) * sizeof(*default_options)));
108 for (def_defaults_size = 0; default_defaults[def_defaults_size];
111 for (defaults_size = 0; defaults[defaults_size]; defaults_size++)
113 merged_defaults = (char **)
114 malloc ((def_defaults_size + defaults_size + 1) * sizeof (*defaults));;
115 memcpy (merged_defaults, default_defaults,
116 def_defaults_size * sizeof(*defaults));
117 memcpy (merged_defaults + def_defaults_size, defaults,
118 (defaults_size + 1) * sizeof(*defaults));
120 /* This totally sucks. Xt should behave like this by default.
121 If the string in `defaults' looks like ".foo", change that
126 for (s = merged_defaults; *s; s++)
129 const char *oldr = *s;
130 char *newr = (char *) malloc(strlen(oldr) + strlen(progclass) + 3);
131 strcpy (newr, progclass);
139 /* Make the X errors print out the name of this program, so we have some
140 clue which one has a bug when they die under the screensaver.
144 screenhack_ehandler (Display *dpy, XErrorEvent *error)
146 fprintf (stderr, "\nX error in %s:\n", progname);
147 if (XmuPrintDefaultErrorMessage (dpy, error, stderr))
150 fprintf (stderr, " (nonfatal.)\n");
155 MapNotify_event_p (Display *dpy, XEvent *event, XPointer window)
157 return (event->xany.type == MapNotify &&
158 event->xvisibility.window == (Window) window);
163 extern Visual *get_gl_visual (Screen *, const char *, const char *);
167 extern void pre_merge_options (void);
171 static Atom XA_WM_PROTOCOLS, XA_WM_DELETE_WINDOW;
173 /* Dead-trivial event handling: exits if "q" or "ESC" are typed.
174 Exit if the WM_PROTOCOLS WM_DELETE_WINDOW ClientMessage is received.
177 screenhack_handle_event (Display *dpy, XEvent *event)
179 switch (event->xany.type)
185 XLookupString (&event->xkey, &c, 1, &keysym, 0);
197 if (event->xclient.message_type != XA_WM_PROTOCOLS)
199 char *s = XGetAtomName(dpy, event->xclient.message_type);
200 if (!s) s = "(null)";
201 fprintf (stderr, "%s: unknown ClientMessage %s received!\n",
204 else if (event->xclient.data.l[0] != XA_WM_DELETE_WINDOW)
206 char *s1 = XGetAtomName(dpy, event->xclient.message_type);
207 char *s2 = XGetAtomName(dpy, event->xclient.data.l[0]);
208 if (!s1) s1 = "(null)";
209 if (!s2) s2 = "(null)";
210 fprintf (stderr, "%s: unknown ClientMessage %s[%s] received!\n",
224 screenhack_handle_events (Display *dpy)
226 while (XPending (dpy))
229 XNextEvent (dpy, &event);
230 screenhack_handle_event (dpy, &event);
237 main (int argc, char **argv)
246 Window on_window = 0;
248 Boolean dont_clear /*, dont_map */;
252 pre_merge_options ();
257 /* We have to do this on SGI to prevent the background color from being
258 overridden by the current desktop color scheme (we'd like our backgrounds
259 to be black, thanks.) This should be the same as setting the
260 "*useSchemes: none" resource, but it's not -- if that resource is
261 present in the `default_defaults' above, it doesn't work, though it
262 does work when passed as an -xrm arg on the command line. So screw it,
263 turn them off from C instead.
265 SgiUseSchemes ("none");
268 toplevel = XtAppInitialize (&app, progclass, merged_options,
269 merged_options_size, &argc, argv,
270 merged_defaults, 0, 0);
271 dpy = XtDisplay (toplevel);
272 db = XtDatabase (dpy);
273 XtGetApplicationNameAndClass (dpy, &progname, &progclass);
274 XSetErrorHandler (screenhack_ehandler);
276 XA_WM_PROTOCOLS = XInternAtom (dpy, "WM_PROTOCOLS", False);
277 XA_WM_DELETE_WINDOW = XInternAtom (dpy, "WM_DELETE_WINDOW", False);
280 char *v = (char *) strdup(strchr(screensaver_id, ' '));
281 char *s1, *s2, *s3, *s4;
282 s1 = (char *) strchr(v, ' '); s1++;
283 s2 = (char *) strchr(s1, ' ');
284 s3 = (char *) strchr(v, '('); s3++;
285 s4 = (char *) strchr(s3, ')');
288 sprintf (version, "%s: from the XScreenSaver %s distribution (%s.)",
299 Bool help_p = !strcmp(argv[1], "-help");
300 fprintf (stderr, "%s\n", version);
301 for (s = progclass; *s; s++) fprintf(stderr, " ");
302 fprintf (stderr, " http://www.jwz.org/xscreensaver/\n\n");
305 fprintf(stderr, "Unrecognised option: %s\n", argv[1]);
306 fprintf (stderr, "Options include: ");
307 for (i = 0; i < merged_options_size; i++)
309 char *sw = merged_options [i].option;
310 Bool argp = (merged_options [i].argKind == XrmoptionSepArg);
311 int size = strlen (sw) + (argp ? 6 : 0) + 2;
314 fprintf (stderr, "\n\t\t ");
318 fprintf (stderr, "%s", sw);
319 if (argp) fprintf (stderr, " <arg>");
320 if (i != merged_options_size - 1) fprintf (stderr, ", ");
322 fprintf (stderr, ".\n");
323 exit (help_p ? 0 : 1);
326 dont_clear = get_boolean_resource ("dontClearRoot", "Boolean");
327 /*dont_map = get_boolean_resource ("dontMapWindow", "Boolean"); */
328 mono_p = get_boolean_resource ("mono", "Boolean");
329 if (CellsOfScreen (DefaultScreenOfDisplay (dpy)) <= 2)
332 root_p = get_boolean_resource ("root", "Boolean");
335 char *s = get_string_resource ("windowID", "WindowID");
337 on_window = get_integer_resource ("windowID", "WindowID");
343 XWindowAttributes xgwa;
344 window = (Window) on_window;
345 XtDestroyWidget (toplevel);
346 XGetWindowAttributes (dpy, window, &xgwa);
347 cmap = xgwa.colormap;
348 visual = xgwa.visual;
352 XWindowAttributes xgwa;
353 window = RootWindowOfScreen (XtScreen (toplevel));
354 XtDestroyWidget (toplevel);
355 XGetWindowAttributes (dpy, window, &xgwa);
356 cmap = xgwa.colormap;
357 visual = xgwa.visual;
361 Boolean def_visual_p;
362 Screen *screen = XtScreen (toplevel);
365 visual = get_gl_visual (screen, "visualID", "VisualID");
367 visual = get_visual_resource (screen, "visualID", "VisualID", False);
370 if (toplevel->core.width <= 0)
371 toplevel->core.width = 600;
372 if (toplevel->core.height <= 0)
373 toplevel->core.height = 480;
375 def_visual_p = (visual == DefaultVisualOfScreen (screen));
382 cmap = XCreateColormap (dpy, RootWindowOfScreen(screen),
384 bg = get_pixel_resource ("background", "Background", dpy, cmap);
385 bd = get_pixel_resource ("borderColor", "Foreground", dpy, cmap);
387 new = XtVaAppCreateShell (progname, progclass,
388 topLevelShellWidgetClass, dpy,
389 XtNmappedWhenManaged, False,
391 XtNdepth, visual_depth (screen, visual),
392 XtNwidth, toplevel->core.width,
393 XtNheight, toplevel->core.height,
395 XtNbackground, (Pixel) bg,
396 XtNborderColor, (Pixel) bd,
398 XtDestroyWidget (toplevel);
400 XtRealizeWidget (toplevel);
401 window = XtWindow (toplevel);
405 XtVaSetValues (toplevel, XtNmappedWhenManaged, False, 0);
406 XtRealizeWidget (toplevel);
407 window = XtWindow (toplevel);
409 if (get_boolean_resource ("installColormap", "InstallColormap"))
411 cmap = XCreateColormap (dpy, window,
412 DefaultVisualOfScreen (XtScreen (toplevel)),
414 XSetWindowColormap (dpy, window, cmap);
418 cmap = DefaultColormap (dpy, DefaultScreen (dpy));
425 XtVaSetValues (toplevel, XtNmappedWhenManaged, False, 0);
426 XtRealizeWidget (toplevel);
431 XtPopup (toplevel, XtGrabNone);
434 XtVaSetValues(toplevel, XtNtitle, version, 0);
436 /* For screenhack_handle_events(): select KeyPress, and
437 announce that we accept WM_DELETE_WINDOW. */
439 XWindowAttributes xgwa;
440 XGetWindowAttributes (dpy, window, &xgwa);
441 XSelectInput (dpy, window,
442 xgwa.your_event_mask | KeyPressMask | ButtonPressMask);
443 XChangeProperty (dpy, window, XA_WM_PROTOCOLS, XA_ATOM, 32,
445 (unsigned char *) &XA_WM_DELETE_WINDOW, 1);
451 XSetWindowBackground (dpy, window,
452 get_pixel_resource ("background", "Background",
454 XClearWindow (dpy, window);
457 if (!root_p && !on_window)
458 /* wait for it to be mapped */
459 XIfEvent (dpy, &event, MapNotify_event_p, (XPointer) window);
462 srandom ((int) time ((time_t *) 0));
463 screenhack (dpy, window); /* doesn't return */