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 Visual *get_gl_visual (Screen *, const char *, const char *);
169 extern void pre_merge_options (void);
173 static Atom XA_WM_PROTOCOLS, XA_WM_DELETE_WINDOW;
175 /* Dead-trivial event handling: exits if "q" or "ESC" are typed.
176 Exit if the WM_PROTOCOLS WM_DELETE_WINDOW ClientMessage is received.
179 screenhack_handle_event (Display *dpy, XEvent *event)
181 switch (event->xany.type)
187 XLookupString (&event->xkey, &c, 1, &keysym, 0);
193 else if (! (keysym >= XK_Shift_L && keysym <= XK_Hyper_R))
194 XBell (dpy, 0); /* beep for non-chord keys */
202 if (event->xclient.message_type != XA_WM_PROTOCOLS)
204 char *s = XGetAtomName(dpy, event->xclient.message_type);
205 if (!s) s = "(null)";
206 fprintf (stderr, "%s: unknown ClientMessage %s received!\n",
209 else if (event->xclient.data.l[0] != XA_WM_DELETE_WINDOW)
211 char *s1 = XGetAtomName(dpy, event->xclient.message_type);
212 char *s2 = XGetAtomName(dpy, event->xclient.data.l[0]);
213 if (!s1) s1 = "(null)";
214 if (!s2) s2 = "(null)";
215 fprintf (stderr, "%s: unknown ClientMessage %s[%s] received!\n",
229 screenhack_handle_events (Display *dpy)
231 while (XPending (dpy))
234 XNextEvent (dpy, &event);
235 screenhack_handle_event (dpy, &event);
242 main (int argc, char **argv)
250 Window on_window = 0;
252 Boolean dont_clear /*, dont_map */;
256 pre_merge_options ();
261 /* We have to do this on SGI to prevent the background color from being
262 overridden by the current desktop color scheme (we'd like our backgrounds
263 to be black, thanks.) This should be the same as setting the
264 "*useSchemes: none" resource, but it's not -- if that resource is
265 present in the `default_defaults' above, it doesn't work, though it
266 does work when passed as an -xrm arg on the command line. So screw it,
267 turn them off from C instead.
269 SgiUseSchemes ("none");
272 toplevel = XtAppInitialize (&app, progclass, merged_options,
273 merged_options_size, &argc, argv,
274 merged_defaults, 0, 0);
275 dpy = XtDisplay (toplevel);
276 db = XtDatabase (dpy);
277 XtGetApplicationNameAndClass (dpy, &progname, &progclass);
278 XSetErrorHandler (screenhack_ehandler);
280 XA_WM_PROTOCOLS = XInternAtom (dpy, "WM_PROTOCOLS", False);
281 XA_WM_DELETE_WINDOW = XInternAtom (dpy, "WM_DELETE_WINDOW", False);
284 char *v = (char *) strdup(strchr(screensaver_id, ' '));
285 char *s1, *s2, *s3, *s4;
286 s1 = (char *) strchr(v, ' '); s1++;
287 s2 = (char *) strchr(s1, ' ');
288 s3 = (char *) strchr(v, '('); s3++;
289 s4 = (char *) strchr(s3, ')');
292 sprintf (version, "%s: from the XScreenSaver %s distribution (%s.)",
303 Bool help_p = !strcmp(argv[1], "-help");
304 fprintf (stderr, "%s\n", version);
305 for (s = progclass; *s; s++) fprintf(stderr, " ");
306 fprintf (stderr, " http://www.jwz.org/xscreensaver/\n\n");
309 fprintf(stderr, "Unrecognised option: %s\n", argv[1]);
310 fprintf (stderr, "Options include: ");
311 for (i = 0; i < merged_options_size; i++)
313 char *sw = merged_options [i].option;
314 Bool argp = (merged_options [i].argKind == XrmoptionSepArg);
315 int size = strlen (sw) + (argp ? 6 : 0) + 2;
318 fprintf (stderr, "\n\t\t ");
322 fprintf (stderr, "%s", sw);
323 if (argp) fprintf (stderr, " <arg>");
324 if (i != merged_options_size - 1) fprintf (stderr, ", ");
326 fprintf (stderr, ".\n");
327 exit (help_p ? 0 : 1);
330 dont_clear = get_boolean_resource ("dontClearRoot", "Boolean");
331 /*dont_map = get_boolean_resource ("dontMapWindow", "Boolean"); */
332 mono_p = get_boolean_resource ("mono", "Boolean");
333 if (CellsOfScreen (DefaultScreenOfDisplay (dpy)) <= 2)
336 root_p = get_boolean_resource ("root", "Boolean");
339 char *s = get_string_resource ("windowID", "WindowID");
341 on_window = get_integer_resource ("windowID", "WindowID");
347 XWindowAttributes xgwa;
348 window = (Window) on_window;
349 XtDestroyWidget (toplevel);
350 XGetWindowAttributes (dpy, window, &xgwa);
351 cmap = xgwa.colormap;
352 visual = xgwa.visual;
356 XWindowAttributes xgwa;
357 window = RootWindowOfScreen (XtScreen (toplevel));
358 XtDestroyWidget (toplevel);
359 XGetWindowAttributes (dpy, window, &xgwa);
360 cmap = xgwa.colormap;
361 visual = xgwa.visual;
365 Boolean def_visual_p;
366 Screen *screen = XtScreen (toplevel);
369 visual = get_gl_visual (screen, "visualID", "VisualID");
371 visual = get_visual_resource (screen, "visualID", "VisualID", False);
374 if (toplevel->core.width <= 0)
375 toplevel->core.width = 600;
376 if (toplevel->core.height <= 0)
377 toplevel->core.height = 480;
379 def_visual_p = (visual == DefaultVisualOfScreen (screen));
386 cmap = XCreateColormap (dpy, RootWindowOfScreen(screen),
388 bg = get_pixel_resource ("background", "Background", dpy, cmap);
389 bd = get_pixel_resource ("borderColor", "Foreground", dpy, cmap);
391 new = XtVaAppCreateShell (progname, progclass,
392 topLevelShellWidgetClass, dpy,
393 XtNmappedWhenManaged, False,
395 XtNdepth, visual_depth (screen, visual),
396 XtNwidth, toplevel->core.width,
397 XtNheight, toplevel->core.height,
399 XtNbackground, (Pixel) bg,
400 XtNborderColor, (Pixel) bd,
402 XtDestroyWidget (toplevel);
404 XtRealizeWidget (toplevel);
405 window = XtWindow (toplevel);
409 XtVaSetValues (toplevel, XtNmappedWhenManaged, False, 0);
410 XtRealizeWidget (toplevel);
411 window = XtWindow (toplevel);
413 if (get_boolean_resource ("installColormap", "InstallColormap"))
415 cmap = XCreateColormap (dpy, window,
416 DefaultVisualOfScreen (XtScreen (toplevel)),
418 XSetWindowColormap (dpy, window, cmap);
422 cmap = DefaultColormap (dpy, DefaultScreen (dpy));
429 XtVaSetValues (toplevel, XtNmappedWhenManaged, False, 0);
430 XtRealizeWidget (toplevel);
435 XtPopup (toplevel, XtGrabNone);
438 XtVaSetValues(toplevel, XtNtitle, version, 0);
440 /* For screenhack_handle_events(): select KeyPress, and
441 announce that we accept WM_DELETE_WINDOW. */
443 XWindowAttributes xgwa;
444 XGetWindowAttributes (dpy, window, &xgwa);
445 XSelectInput (dpy, window,
446 xgwa.your_event_mask | KeyPressMask | ButtonPressMask);
447 XChangeProperty (dpy, window, XA_WM_PROTOCOLS, XA_ATOM, 32,
449 (unsigned char *) &XA_WM_DELETE_WINDOW, 1);
455 XSetWindowBackground (dpy, window,
456 get_pixel_resource ("background", "Background",
458 XClearWindow (dpy, window);
461 if (!root_p && !on_window)
462 /* wait for it to be mapped */
463 XIfEvent (dpy, &event, MapNotify_event_p, (XPointer) window);
466 srandom ((int) time ((time_t *) 0));
467 screenhack (dpy, window); /* doesn't return */