+ switch (event->xany.type)
+ {
+ case KeyPress:
+ {
+ KeySym keysym;
+ char c = 0;
+ XLookupString (&event->xkey, &c, 1, &keysym, 0);
+ if (c == 'q' ||
+ c == 'Q' ||
+ c == 3 || /* ^C */
+ c == 27) /* ESC */
+ return False; /* exit */
+ else if (! (keysym >= XK_Shift_L && keysym <= XK_Hyper_R))
+ XBell (dpy, 0); /* beep for non-chord keys */
+ }
+ break;
+ case ButtonPress:
+ XBell (dpy, 0);
+ break;
+ case ClientMessage:
+ {
+ if (event->xclient.message_type != XA_WM_PROTOCOLS)
+ {
+ char *s = XGetAtomName(dpy, event->xclient.message_type);
+ if (!s) s = "(null)";
+ fprintf (stderr, "%s: unknown ClientMessage %s received!\n",
+ progname, s);
+ }
+ else if (event->xclient.data.l[0] != XA_WM_DELETE_WINDOW)
+ {
+ char *s1 = XGetAtomName(dpy, event->xclient.message_type);
+ char *s2 = XGetAtomName(dpy, event->xclient.data.l[0]);
+ if (!s1) s1 = "(null)";
+ if (!s2) s2 = "(null)";
+ fprintf (stderr, "%s: unknown ClientMessage %s[%s] received!\n",
+ progname, s1, s2);
+ }
+ else
+ {
+ return False; /* exit */
+ }
+ }
+ break;
+ }
+ return True;
+}
+
+
+static Visual *
+pick_visual (Screen *screen)
+{
+ struct xscreensaver_function_table *ft = xscreensaver_function_table;
+
+ if (ft->pick_visual_hook)
+ {
+ Visual *v = ft->pick_visual_hook (screen);
+ if (v) return v;
+ }
+
+ return get_visual_resource (screen, "visualID", "VisualID", False);
+}
+
+
+/* Notice when the user has requested a different visual or colormap
+ on a pre-existing window (e.g., "-root -visual truecolor" or
+ "-window-id 0x2c00001 -install") and complain, since when drawing
+ on an existing window, we have no choice about these things.
+ */
+static void
+visual_warning (Screen *screen, Window window, Visual *visual, Colormap cmap,
+ Bool window_p)
+{
+ struct xscreensaver_function_table *ft = xscreensaver_function_table;
+
+ char *visual_string = get_string_resource (DisplayOfScreen (screen),
+ "visualID", "VisualID");
+ Visual *desired_visual = pick_visual (screen);
+ char win[100];
+ char why[100];
+
+ if (window == RootWindowOfScreen (screen))
+ strcpy (win, "root window");
+ else
+ sprintf (win, "window 0x%lx", (unsigned long) window);
+
+ if (window_p)
+ sprintf (why, "-window-id 0x%lx", (unsigned long) window);
+ else
+ strcpy (why, "-root");
+
+ if (visual_string && *visual_string)
+ {
+ char *s;
+ for (s = visual_string; *s; s++)
+ if (isupper (*s)) *s = _tolower (*s);
+
+ if (!strcmp (visual_string, "default") ||
+ !strcmp (visual_string, "default") ||
+ !strcmp (visual_string, "best"))
+ /* don't warn about these, just silently DWIM. */
+ ;
+ else if (visual != desired_visual)
+ {
+ fprintf (stderr, "%s: ignoring `-visual %s' because of `%s'.\n",
+ progname, visual_string, why);
+ fprintf (stderr, "%s: using %s's visual 0x%lx.\n",
+ progname, win, XVisualIDFromVisual (visual));
+ }
+ free (visual_string);
+ }
+
+ if (visual == DefaultVisualOfScreen (screen) &&
+ has_writable_cells (screen, visual) &&
+ get_boolean_resource (DisplayOfScreen (screen),
+ "installColormap", "InstallColormap"))
+ {
+ fprintf (stderr, "%s: ignoring `-install' because of `%s'.\n",
+ progname, why);
+ fprintf (stderr, "%s: using %s's colormap 0x%lx.\n",
+ progname, win, (unsigned long) cmap);
+ }
+
+ if (ft->validate_visual_hook)
+ {
+ if (! ft->validate_visual_hook (screen, win, visual))
+ exit (1);
+ }
+}
+
+
+static void
+fix_fds (void)
+{
+ /* Bad Things Happen if stdin, stdout, and stderr have been closed
+ (as by the `sh incantation "attraction >&- 2>&-"). When you do
+ that, the X connection gets allocated to one of these fds, and
+ then some random library writes to stderr, and random bits get
+ stuffed down the X pipe, causing "Xlib: sequence lost" errors.
+ So, we cause the first three file descriptors to be open to
+ /dev/null if they aren't open to something else already. This
+ must be done before any other files are opened (or the closing
+ of that other file will again free up one of the "magic" first
+ three FDs.)
+
+ We do this by opening /dev/null three times, and then closing
+ those fds, *unless* any of them got allocated as #0, #1, or #2,
+ in which case we leave them open. Gag.
+
+ Really, this crap is technically required of *every* X program,
+ if you want it to be robust in the face of "2>&-".
+ */
+ int fd0 = open ("/dev/null", O_RDWR);
+ int fd1 = open ("/dev/null", O_RDWR);
+ int fd2 = open ("/dev/null", O_RDWR);
+ if (fd0 > 2) close (fd0);
+ if (fd1 > 2) close (fd1);
+ if (fd2 > 2) close (fd2);
+}
+
+
+static Boolean
+screenhack_table_handle_events (Display *dpy,
+ const struct xscreensaver_function_table *ft,
+ Window window, void *closure
+#ifdef DEBUG_PAIR
+ , Window window2, void *closure2
+#endif
+ )
+{
+ XtAppContext app = XtDisplayToApplicationContext (dpy);
+
+ if (XtAppPending (app) & (XtIMTimer|XtIMAlternateInput))
+ XtAppProcessEvent (app, XtIMTimer|XtIMAlternateInput);
+
+ while (XPending (dpy))
+ {
+ XEvent event;
+ XNextEvent (dpy, &event);
+
+ if (event.xany.type == ConfigureNotify)
+ {
+ if (event.xany.window == window)
+ ft->reshape_cb (dpy, window, closure,
+ event.xconfigure.width, event.xconfigure.height);
+#ifdef DEBUG_PAIR
+ if (window2 && event.xany.window == window2)
+ ft->reshape_cb (dpy, window2, closure2,
+ event.xconfigure.width, event.xconfigure.height);
+#endif
+ }
+ else if (event.xany.type == ClientMessage ||
+ (! (event.xany.window == window
+ ? ft->event_cb (dpy, window, closure, &event)
+#ifdef DEBUG_PAIR
+ : (window2 && event.xany.window == window2)
+ ? ft->event_cb (dpy, window2, closure2, &event)
+#endif
+ : 0)))
+ if (! screenhack_handle_event_1 (dpy, &event))
+ return False;
+
+ if (XtAppPending (app) & (XtIMTimer|XtIMAlternateInput))
+ XtAppProcessEvent (app, XtIMTimer|XtIMAlternateInput);
+ }
+ return True;
+}
+
+
+static Boolean
+usleep_and_process_events (Display *dpy,
+ const struct xscreensaver_function_table *ft,
+ Window window, fps_state *fpst, void *closure,
+ unsigned long delay
+#ifdef DEBUG_PAIR
+ , Window window2, fps_state *fpst2, void *closure2,
+ unsigned long delay2
+#endif
+# ifdef HAVE_RECORD_ANIM
+ , record_anim_state *anim_state
+# endif
+ )
+{
+ do {
+ unsigned long quantum = 33333; /* 30 fps */
+ if (quantum > delay)
+ quantum = delay;
+ delay -= quantum;
+
+ XSync (dpy, False);
+
+#ifdef HAVE_RECORD_ANIM
+ if (anim_state) screenhack_record_anim (anim_state);
+#endif
+
+ if (quantum > 0)
+ {
+ usleep (quantum);
+ if (fpst) fps_slept (fpst, quantum);
+#ifdef DEBUG_PAIR
+ if (fpst2) fps_slept (fpst2, quantum);
+#endif
+ }
+
+ if (! screenhack_table_handle_events (dpy, ft, window, closure
+#ifdef DEBUG_PAIR
+ , window2, closure2
+#endif
+ ))
+ return False;
+ } while (delay > 0);
+
+ return True;
+}
+
+
+static void
+screenhack_do_fps (Display *dpy, Window w, fps_state *fpst, void *closure)
+{
+ fps_compute (fpst, 0, -1);
+ fps_draw (fpst);
+}
+
+
+static void
+run_screenhack_table (Display *dpy,
+ Window window,
+# ifdef DEBUG_PAIR
+ Window window2,
+# endif
+# ifdef HAVE_RECORD_ANIM
+ record_anim_state *anim_state,
+# endif
+ const struct xscreensaver_function_table *ft)
+{
+
+ /* Kludge: even though the init_cb functions are declared to take 2 args,
+ actually call them with 3, for the benefit of xlockmore_init() and
+ xlockmore_setup().
+ */
+ void *(*init_cb) (Display *, Window, void *) =
+ (void *(*) (Display *, Window, void *)) ft->init_cb;
+
+ void (*fps_cb) (Display *, Window, fps_state *, void *) = ft->fps_cb;
+
+ void *closure = init_cb (dpy, window, ft->setup_arg);
+ fps_state *fpst = fps_init (dpy, window);
+
+#ifdef DEBUG_PAIR
+ void *closure2 = 0;
+ fps_state *fpst2 = 0;
+ if (window2) closure2 = init_cb (dpy, window2, ft->setup_arg);
+ if (window2) fpst2 = fps_init (dpy, window2);
+#endif
+
+ if (! closure) /* if it returns nothing, it can't possibly be re-entrant. */
+ abort();
+
+ if (! fps_cb) fps_cb = screenhack_do_fps;
+
+ while (1)
+ {
+ unsigned long delay = ft->draw_cb (dpy, window, closure);
+#ifdef DEBUG_PAIR
+ unsigned long delay2 = 0;
+ if (window2) delay2 = ft->draw_cb (dpy, window2, closure2);
+#endif
+
+ if (fpst) fps_cb (dpy, window, fpst, closure);
+#ifdef DEBUG_PAIR
+ if (fpst2) fps_cb (dpy, window, fpst2, closure);
+#endif
+
+ if (! usleep_and_process_events (dpy, ft,
+ window, fpst, closure, delay
+#ifdef DEBUG_PAIR
+ , window2, fpst2, closure2, delay2
+#endif
+#ifdef HAVE_RECORD_ANIM
+ , anim_state
+#endif
+ ))
+ break;
+ }
+
+#ifdef HAVE_RECORD_ANIM
+ /* Exiting before target frames hit: write the video anyway. */
+ if (anim_state) screenhack_record_anim_free (anim_state);
+#endif
+
+ ft->free_cb (dpy, window, closure);
+ if (fpst) fps_free (fpst);
+
+#ifdef DEBUG_PAIR
+ if (window2) ft->free_cb (dpy, window2, closure2);
+ if (fpst2) fps_free (fpst2);
+#endif
+}
+
+
+static Widget
+make_shell (Screen *screen, Widget toplevel, int width, int height)
+{
+ Display *dpy = DisplayOfScreen (screen);
+ Visual *visual = pick_visual (screen);
+ Boolean def_visual_p = (toplevel &&
+ visual == DefaultVisualOfScreen (screen));
+
+ if (width <= 0) width = 600;
+ if (height <= 0) height = 480;
+
+ if (def_visual_p)
+ {
+ Window window;
+ XtVaSetValues (toplevel,
+ XtNmappedWhenManaged, False,
+ XtNwidth, width,
+ XtNheight, height,
+ XtNinput, True, /* for WM_HINTS */
+ NULL);
+ XtRealizeWidget (toplevel);
+ window = XtWindow (toplevel);
+
+ if (get_boolean_resource (dpy, "installColormap", "InstallColormap"))
+ {
+ Colormap cmap =
+ XCreateColormap (dpy, window, DefaultVisualOfScreen (screen),
+ AllocNone);
+ XSetWindowColormap (dpy, window, cmap);
+ }
+ }
+ else
+ {
+ unsigned int bg, bd;
+ Widget new;
+ Colormap cmap = XCreateColormap (dpy, VirtualRootWindowOfScreen(screen),
+ visual, AllocNone);
+ bg = get_pixel_resource (dpy, cmap, "background", "Background");
+ bd = get_pixel_resource (dpy, cmap, "borderColor", "Foreground");
+
+ new = XtVaAppCreateShell (progname, progclass,
+ topLevelShellWidgetClass, dpy,
+ XtNmappedWhenManaged, False,
+ XtNvisual, visual,
+ XtNdepth, visual_depth (screen, visual),
+ XtNwidth, width,
+ XtNheight, height,
+ XtNcolormap, cmap,
+ XtNbackground, (Pixel) bg,
+ XtNborderColor, (Pixel) bd,
+ XtNinput, True, /* for WM_HINTS */
+ NULL);
+
+ if (!toplevel) /* kludge for the second window in -pair mode... */
+ XtVaSetValues (new, XtNx, 0, XtNy, 550, NULL);
+
+ XtRealizeWidget (new);
+ toplevel = new;
+ }
+
+ return toplevel;
+}
+
+static void
+init_window (Display *dpy, Widget toplevel, const char *title)
+{
+ Window window;
+ XWindowAttributes xgwa;
+ XtPopup (toplevel, XtGrabNone);
+ XtVaSetValues (toplevel, XtNtitle, title, NULL);
+
+ /* Select KeyPress, and announce that we accept WM_DELETE_WINDOW.
+ */
+ window = XtWindow (toplevel);
+ XGetWindowAttributes (dpy, window, &xgwa);
+ XSelectInput (dpy, window,
+ (xgwa.your_event_mask | KeyPressMask | KeyReleaseMask |
+ ButtonPressMask | ButtonReleaseMask));
+ XChangeProperty (dpy, window, XA_WM_PROTOCOLS, XA_ATOM, 32,
+ PropModeReplace,
+ (unsigned char *) &XA_WM_DELETE_WINDOW, 1);
+}
+
+
+int
+main (int argc, char **argv)
+{
+ struct xscreensaver_function_table *ft = xscreensaver_function_table;
+
+ XWindowAttributes xgwa;