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"
64 static XrmOptionDescRec default_options [] = {
65 { "-root", ".root", XrmoptionNoArg, "True" },
66 { "-window", ".root", XrmoptionNoArg, "False" },
67 { "-mono", ".mono", XrmoptionNoArg, "True" },
68 { "-install", ".installColormap", XrmoptionNoArg, "True" },
69 { "-noinstall",".installColormap", XrmoptionNoArg, "False" },
70 { "-visual", ".visualID", XrmoptionSepArg, 0 },
71 { "-window-id", ".windowID", XrmoptionSepArg, 0 },
75 static char *default_defaults[] = {
77 "*geometry: 600x480", /* this should be .geometry, but nooooo... */
79 "*installColormap: false",
85 static XrmOptionDescRec *merged_options;
86 static int merged_options_size;
87 static char **merged_defaults;
92 int def_opts_size, opts_size;
93 int def_defaults_size, defaults_size;
95 for (def_opts_size = 0; default_options[def_opts_size].option;
98 for (opts_size = 0; options[opts_size].option; opts_size++)
101 merged_options_size = def_opts_size + opts_size;
102 merged_options = (XrmOptionDescRec *)
103 malloc ((merged_options_size + 1) * sizeof(*default_options));
104 memcpy (merged_options, default_options,
105 (def_opts_size * sizeof(*default_options)));
106 memcpy (merged_options + def_opts_size, options,
107 ((opts_size + 1) * sizeof(*default_options)));
109 for (def_defaults_size = 0; default_defaults[def_defaults_size];
112 for (defaults_size = 0; defaults[defaults_size]; defaults_size++)
114 merged_defaults = (char **)
115 malloc ((def_defaults_size + defaults_size + 1) * sizeof (*defaults));;
116 memcpy (merged_defaults, default_defaults,
117 def_defaults_size * sizeof(*defaults));
118 memcpy (merged_defaults + def_defaults_size, defaults,
119 (defaults_size + 1) * sizeof(*defaults));
121 /* This totally sucks. Xt should behave like this by default.
122 If the string in `defaults' looks like ".foo", change that
127 for (s = merged_defaults; *s; s++)
130 const char *oldr = *s;
131 char *newr = (char *) malloc(strlen(oldr) + strlen(progclass) + 3);
132 strcpy (newr, progclass);
140 /* Make the X errors print out the name of this program, so we have some
141 clue which one has a bug when they die under the screensaver.
145 screenhack_ehandler (Display *dpy, XErrorEvent *error)
147 fprintf (stderr, "\nX error in %s:\n", progname);
148 if (XmuPrintDefaultErrorMessage (dpy, error, stderr))
151 fprintf (stderr, " (nonfatal.)\n");
156 MapNotify_event_p (Display *dpy, XEvent *event, XPointer window)
158 return (event->xany.type == MapNotify &&
159 event->xvisibility.window == (Window) window);
164 extern Visual *get_gl_visual (Screen *, const char *, const char *);
168 extern void pre_merge_options (void);
172 static Atom XA_WM_PROTOCOLS, XA_WM_DELETE_WINDOW;
174 /* Dead-trivial event handling: exits if "q" or "ESC" are typed.
175 Exit if the WM_PROTOCOLS WM_DELETE_WINDOW ClientMessage is received.
178 screenhack_handle_event (Display *dpy, XEvent *event)
180 switch (event->xany.type)
186 XLookupString (&event->xkey, &c, 1, &keysym, 0);
192 else if (! (keysym >= XK_Shift_L && keysym <= XK_Hyper_R))
193 XBell (dpy, 0); /* beep for non-chord keys */
201 if (event->xclient.message_type != XA_WM_PROTOCOLS)
203 char *s = XGetAtomName(dpy, event->xclient.message_type);
204 if (!s) s = "(null)";
205 fprintf (stderr, "%s: unknown ClientMessage %s received!\n",
208 else if (event->xclient.data.l[0] != XA_WM_DELETE_WINDOW)
210 char *s1 = XGetAtomName(dpy, event->xclient.message_type);
211 char *s2 = XGetAtomName(dpy, event->xclient.data.l[0]);
212 if (!s1) s1 = "(null)";
213 if (!s2) s2 = "(null)";
214 fprintf (stderr, "%s: unknown ClientMessage %s[%s] received!\n",
228 screenhack_handle_events (Display *dpy)
230 while (XPending (dpy))
233 XNextEvent (dpy, &event);
234 screenhack_handle_event (dpy, &event);
241 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 */