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);
302 XSetErrorHandler (screenhack_ehandler);
304 XA_WM_PROTOCOLS = XInternAtom (dpy, "WM_PROTOCOLS", False);
305 XA_WM_DELETE_WINDOW = XInternAtom (dpy, "WM_DELETE_WINDOW", False);
308 char *v = (char *) strdup(strchr(screensaver_id, ' '));
309 char *s1, *s2, *s3, *s4;
310 s1 = (char *) strchr(v, ' '); s1++;
311 s2 = (char *) strchr(s1, ' ');
312 s3 = (char *) strchr(v, '('); s3++;
313 s4 = (char *) strchr(s3, ')');
316 sprintf (version, "%s: from the XScreenSaver %s distribution (%s.)",
327 Bool help_p = !strcmp(argv[1], "-help");
328 fprintf (stderr, "%s\n", version);
329 for (s = progclass; *s; s++) fprintf(stderr, " ");
330 fprintf (stderr, " http://www.jwz.org/xscreensaver/\n\n");
333 fprintf(stderr, "Unrecognised option: %s\n", argv[1]);
334 fprintf (stderr, "Options include: ");
335 for (i = 0; i < merged_options_size; i++)
337 char *sw = merged_options [i].option;
338 Bool argp = (merged_options [i].argKind == XrmoptionSepArg);
339 int size = strlen (sw) + (argp ? 6 : 0) + 2;
342 fprintf (stderr, "\n\t\t ");
346 fprintf (stderr, "%s", sw);
347 if (argp) fprintf (stderr, " <arg>");
348 if (i != merged_options_size - 1) fprintf (stderr, ", ");
350 fprintf (stderr, ".\n");
351 exit (help_p ? 0 : 1);
354 dont_clear = get_boolean_resource ("dontClearRoot", "Boolean");
355 /*dont_map = get_boolean_resource ("dontMapWindow", "Boolean"); */
356 mono_p = get_boolean_resource ("mono", "Boolean");
357 if (CellsOfScreen (DefaultScreenOfDisplay (dpy)) <= 2)
360 root_p = get_boolean_resource ("root", "Boolean");
363 char *s = get_string_resource ("windowID", "WindowID");
365 on_window = get_integer_resource ("windowID", "WindowID");
371 XWindowAttributes xgwa;
372 window = (Window) on_window;
373 XtDestroyWidget (toplevel);
374 XGetWindowAttributes (dpy, window, &xgwa);
375 cmap = xgwa.colormap;
376 visual = xgwa.visual;
380 XWindowAttributes xgwa;
381 window = RootWindowOfScreen (XtScreen (toplevel));
382 XtDestroyWidget (toplevel);
383 XGetWindowAttributes (dpy, window, &xgwa);
384 cmap = xgwa.colormap;
385 visual = xgwa.visual;
389 Boolean def_visual_p;
390 Screen *screen = XtScreen (toplevel);
391 visual = pick_visual (screen);
393 if (toplevel->core.width <= 0)
394 toplevel->core.width = 600;
395 if (toplevel->core.height <= 0)
396 toplevel->core.height = 480;
398 def_visual_p = (visual == DefaultVisualOfScreen (screen));
405 cmap = XCreateColormap (dpy, RootWindowOfScreen(screen),
407 bg = get_pixel_resource ("background", "Background", dpy, cmap);
408 bd = get_pixel_resource ("borderColor", "Foreground", dpy, cmap);
410 new = XtVaAppCreateShell (progname, progclass,
411 topLevelShellWidgetClass, dpy,
412 XtNmappedWhenManaged, False,
414 XtNdepth, visual_depth (screen, visual),
415 XtNwidth, toplevel->core.width,
416 XtNheight, toplevel->core.height,
418 XtNbackground, (Pixel) bg,
419 XtNborderColor, (Pixel) bd,
421 XtDestroyWidget (toplevel);
423 XtRealizeWidget (toplevel);
424 window = XtWindow (toplevel);
428 XtVaSetValues (toplevel, XtNmappedWhenManaged, False, 0);
429 XtRealizeWidget (toplevel);
430 window = XtWindow (toplevel);
432 if (get_boolean_resource ("installColormap", "InstallColormap"))
434 cmap = XCreateColormap (dpy, window,
435 DefaultVisualOfScreen (XtScreen (toplevel)),
437 XSetWindowColormap (dpy, window, cmap);
441 cmap = DefaultColormap (dpy, DefaultScreen (dpy));
448 XtVaSetValues (toplevel, XtNmappedWhenManaged, False, 0);
449 XtRealizeWidget (toplevel);
454 XtPopup (toplevel, XtGrabNone);
457 XtVaSetValues(toplevel, XtNtitle, version, 0);
459 /* For screenhack_handle_events(): select KeyPress, and
460 announce that we accept WM_DELETE_WINDOW. */
462 XWindowAttributes xgwa;
463 XGetWindowAttributes (dpy, window, &xgwa);
464 XSelectInput (dpy, window,
465 xgwa.your_event_mask | KeyPressMask | ButtonPressMask);
466 XChangeProperty (dpy, window, XA_WM_PROTOCOLS, XA_ATOM, 32,
468 (unsigned char *) &XA_WM_DELETE_WINDOW, 1);
474 XSetWindowBackground (dpy, window,
475 get_pixel_resource ("background", "Background",
477 XClearWindow (dpy, window);
480 if (!root_p && !on_window)
481 /* wait for it to be mapped */
482 XIfEvent (dpy, &event, MapNotify_event_p, (XPointer) window);
485 srandom ((int) time ((time_t *) 0));
486 screenhack (dpy, window); /* doesn't return */