+#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;
+
+# ifdef USE_GL
+ if (!validate_gl_visual (stderr, screen, "window", visual))
+ exit (1);
+# endif /* USE_GL */
+
+ 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);
+}
+
+