+ 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);
+}
+
+
+#ifdef DEBUG_TIMING
+
+static void
+check_timing (unsigned long delay)
+{
+ static unsigned long frame_count = 0;
+ static unsigned long delay_sum = 0;
+ static struct timeval prev1 = { 0, };
+ static struct timeval prev2 = { 0, };
+ struct timeval now;
+ double uprev1, uprev2, unow;
+
+# ifdef GETTIMEOFDAY_TWO_ARGS
+ gettimeofday (&now, 0);
+# else
+ gettimeofday (&now);
+# endif
+
+ if (prev1.tv_sec == 0)
+ prev1 = prev2 = now;
+
+ frame_count++;
+
+ uprev1 = prev1.tv_sec + ((double) prev1.tv_usec * 0.000001);
+ uprev2 = prev2.tv_sec + ((double) prev2.tv_usec * 0.000001);
+ unow = now.tv_sec + ((double) now.tv_usec * 0.000001);
+
+ if (unow >= uprev1 + 1.5)
+ fprintf (stderr,
+ "%s: warning: blocked event processing for %.1f secs!\n",
+ progname, unow - uprev1);
+ prev1 = now;
+
+ if (unow >= uprev2 + 10.0)
+ {
+ double fps = frame_count / (unow - uprev2);
+ double elapsed = unow - uprev2;
+ double slept = (delay_sum * 0.000001);
+ double sleep_ratio = slept / elapsed;
+
+ if (sleep_ratio < 0.10) {
+ fprintf (stderr,
+ "%s: warning: only %.0f%% idle over the"
+ " last %2.0f secs (at %.1f FPS)\n",
+ progname, 100 * sleep_ratio, elapsed, fps);
+ }
+
+ prev2 = now;
+ frame_count = 0;
+ delay_sum = 0;
+ }
+
+ delay_sum += delay;
+}
+
+#else /* !DEBUG_TIMING */
+# define check_timing(delay) /**/
+#endif /* !DEBUG_TIMING */
+
+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 (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
+ : 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, void *closure, unsigned long delay
+#ifdef DEBUG_PAIR
+ , Window window2, void *closure2, unsigned long delay2
+#endif
+ )
+{
+ do {
+ unsigned long quantum = 100000; /* 1/10th second */
+ if (quantum > delay)
+ quantum = delay;
+ delay -= quantum;
+
+ XSync (dpy, False);
+ if (quantum > 0)
+ usleep (quantum);
+
+ check_timing (quantum);
+
+ /* The above isn't quite right in pair-mode: we always run both windows
+ with the timing of window 2. But, it's just a debugging hack, so
+ that doesn't really matter that much... */
+
+ if (! screenhack_table_handle_events (dpy, ft, window, closure
+#ifdef DEBUG_PAIR
+ , window2, closure2
+#endif
+ ))
+ return False;
+ } while (delay > 0);
+
+ return True;
+}
+
+
+static void
+run_screenhack_table (Display *dpy,
+ Window window,
+# ifdef DEBUG_PAIR
+ Window window2,
+# 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 *closure = init_cb (dpy, window, ft->setup_arg);
+
+#ifdef DEBUG_PAIR
+ void *closure2 = 0;
+ if (window2) closure2 = init_cb (dpy, window2, ft->setup_arg);
+#endif
+
+ if (! closure) /* if it returns nothing, it can't possibly be re-entrant. */
+ abort();
+
+ while (1)
+ {
+ unsigned long delay = ft->draw_cb (dpy, window, closure);
+#ifdef DEBUG_PAIR
+ unsigned long delay2;
+ if (window2) delay2 = ft->draw_cb (dpy, window2, closure2);
+#endif
+
+ if (! usleep_and_process_events (dpy, ft,
+ window, closure, delay
+#ifdef DEBUG_PAIR
+ , window2, closure2, delay2
+#endif
+ ))
+ break;
+ }
+
+ ft->free_cb (dpy, window, closure);
+
+#ifdef DEBUG_PAIR
+ if (window2)
+ ft->free_cb (dpy, window2, closure2);
+#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;
+