1 /* xscreensaver, Copyright (c) 1992-2006 Jamie Zawinski <jwz@jwz.org>
3 * Permission to use, copy, modify, distribute, and sell this software and its
4 * documentation for any purpose is hereby granted without fee, provided that
5 * the above copyright notice appear in all copies and that both that
6 * copyright notice and this permission notice appear in supporting
7 * documentation. No representations are made about the suitability of this
8 * software for any purpose. It is provided "as is" without express or
11 * And remember: X Windows is to graphics hacking as roman numerals are to
12 * the square root of pi.
15 /* This file contains simple code to open a window or draw on the root.
16 The idea being that, when writing a graphics hack, you can just link
17 with this .o to get all of the uninteresting junk out of the way.
19 Create a few static global procedures and variables:
21 static void *YOURNAME_init (Display *, Window);
23 Return an opaque structure representing your drawing state.
25 static unsigned long YOURNAME_draw (Display *, Window, void *closure);
28 The `closure' arg is your drawing state, that you created in `init'.
29 Return the number of microseconds to wait until the next frame.
31 This should return in some small fraction of a second.
32 Do not call `usleep' or loop excessively. For long loops, use a
35 static void YOURNAME_reshape (Display *, Window, void *closure,
36 unsigned int width, unsigned int height);
38 Called when the size of the window changes with the new size.
40 static Bool YOURNAME_event (Display *, Window, void *closure,
43 Called when a keyboard or mouse event arrives.
44 Return True if you handle it in some way, False otherwise.
46 static void YOURNAME_event (Display *, Window, void *closure);
48 Called when you are done: free everything you've allocated,
49 including your private `state' structure.
51 static char YOURNAME_defaults [] = { "...", "...", ... , 0 };
53 This variable is an array of strings, your default resources.
54 Null-terminate the list.
56 static XrmOptionDescRec YOURNAME_options[] = { { ... }, ... { 0,0,0,0 } }
58 This variable describes your command-line options.
59 Null-terminate the list.
61 Finally , invoke the XSCREENSAVER_MODULE() macro to tie it all together.
65 - Make sure that all functions in your module are static (check this
66 by running "nm -g" on the .o file).
68 - Do not use global variables: all such info must be stored in the
69 private `state' structure.
71 - Do not static function-local variables, either. Put it in `state'.
73 Assume that there are N independent runs of this code going in the
74 same address space at the same time: they must not affect each other.
76 - Don't forget to write an XML file to describe the user interface
77 of your screen saver module. See .../hacks/config/README for details.
84 #include <X11/Intrinsic.h>
85 #include <X11/IntrinsicP.h>
86 #include <X11/CoreP.h>
87 #include <X11/Shell.h>
88 #include <X11/StringDefs.h>
89 #include <X11/keysym.h>
92 # include <X11/SGIScheme.h> /* for SgiUseSchemes() */
97 # include <X11/Xmu/Error.h>
99 # include <Xmu/Error.h>
105 #include "screenhackI.h"
108 #include "change_locale.h"
113 #ifndef _XSCREENSAVER_VROOT_H_
114 # error Error! You have an old version of vroot.h! Check -I args.
115 #endif /* _XSCREENSAVER_VROOT_H_ */
118 # define isupper(c) ((c) >= 'A' && (c) <= 'Z')
121 # define _tolower(c) ((c) - 'A' + 'a')
125 /* This is defined by the SCREENHACK_MAIN() macro via screenhack.h.
127 extern struct xscreensaver_function_table *xscreensaver_function_table;
130 const char *progname; /* used by hacks in error messages */
131 const char *progclass; /* used by ../utils/resources.c */
132 Bool mono_p; /* used by hacks */
135 static XrmOptionDescRec default_options [] = {
136 { "-root", ".root", XrmoptionNoArg, "True" },
137 { "-window", ".root", XrmoptionNoArg, "False" },
138 { "-mono", ".mono", XrmoptionNoArg, "True" },
139 { "-install", ".installColormap", XrmoptionNoArg, "True" },
140 { "-noinstall",".installColormap", XrmoptionNoArg, "False" },
141 { "-visual", ".visualID", XrmoptionSepArg, 0 },
142 { "-window-id", ".windowID", XrmoptionSepArg, 0 },
145 { "-pair", ".pair", XrmoptionNoArg, "True" },
146 # endif /* DEBUG_PAIR */
150 static char *default_defaults[] = {
152 "*geometry: 600x480", /* this should be .geometry, but nooooo... */
154 "*installColormap: false",
155 "*visualID: default",
157 "*desktopGrabber: xscreensaver-getimage %s",
161 static XrmOptionDescRec *merged_options;
162 static int merged_options_size;
163 static char **merged_defaults;
168 #ifdef HAVE_SETLOCALE
169 const char *current_locale = setlocale(LC_ALL, "");
170 const char *cmp_locale;
173 while ((cmp_locale = change_locale[j]))
175 if (!strncmp(current_locale, cmp_locale, strlen(cmp_locale)))
177 setlocale(LC_ALL, "C");
182 #endif /* HAVE_SETLOCALE */
189 struct xscreensaver_function_table *ft = xscreensaver_function_table;
191 const XrmOptionDescRec *options = ft->options;
192 const char * const *defaults = ft->defaults;
193 const char *progclass = ft->progclass;
195 int def_opts_size, opts_size;
196 int def_defaults_size, defaults_size;
198 for (def_opts_size = 0; default_options[def_opts_size].option;
201 for (opts_size = 0; options[opts_size].option; opts_size++)
204 merged_options_size = def_opts_size + opts_size;
205 merged_options = (XrmOptionDescRec *)
206 malloc ((merged_options_size + 1) * sizeof(*default_options));
207 memcpy (merged_options, default_options,
208 (def_opts_size * sizeof(*default_options)));
209 memcpy (merged_options + def_opts_size, options,
210 ((opts_size + 1) * sizeof(*default_options)));
212 for (def_defaults_size = 0; default_defaults[def_defaults_size];
215 for (defaults_size = 0; defaults[defaults_size]; defaults_size++)
217 merged_defaults = (char **)
218 malloc ((def_defaults_size + defaults_size + 1) * sizeof (*defaults));;
219 memcpy (merged_defaults, default_defaults,
220 def_defaults_size * sizeof(*defaults));
221 memcpy (merged_defaults + def_defaults_size, defaults,
222 (defaults_size + 1) * sizeof(*defaults));
224 /* This totally sucks. Xt should behave like this by default.
225 If the string in `defaults' looks like ".foo", change that
230 for (s = merged_defaults; *s; s++)
233 const char *oldr = *s;
234 char *newr = (char *) malloc(strlen(oldr) + strlen(progclass) + 3);
235 strcpy (newr, progclass);
243 /* Make the X errors print out the name of this program, so we have some
244 clue which one has a bug when they die under the screensaver.
248 screenhack_ehandler (Display *dpy, XErrorEvent *error)
250 fprintf (stderr, "\nX error in %s:\n", progname);
251 if (XmuPrintDefaultErrorMessage (dpy, error, stderr))
254 fprintf (stderr, " (nonfatal.)\n");
259 MapNotify_event_p (Display *dpy, XEvent *event, XPointer window)
261 return (event->xany.type == MapNotify &&
262 event->xvisibility.window == (Window) window);
266 static Atom XA_WM_PROTOCOLS, XA_WM_DELETE_WINDOW;
268 /* Dead-trivial event handling: exits if "q" or "ESC" are typed.
269 Exit if the WM_PROTOCOLS WM_DELETE_WINDOW ClientMessage is received.
270 Returns False if the screen saver should now terminate.
273 screenhack_handle_event_1 (Display *dpy, XEvent *event)
275 switch (event->xany.type)
281 XLookupString (&event->xkey, &c, 1, &keysym, 0);
286 return False; /* exit */
287 else if (! (keysym >= XK_Shift_L && keysym <= XK_Hyper_R))
288 XBell (dpy, 0); /* beep for non-chord keys */
296 if (event->xclient.message_type != XA_WM_PROTOCOLS)
298 char *s = XGetAtomName(dpy, event->xclient.message_type);
299 if (!s) s = "(null)";
300 fprintf (stderr, "%s: unknown ClientMessage %s received!\n",
303 else if (event->xclient.data.l[0] != XA_WM_DELETE_WINDOW)
305 char *s1 = XGetAtomName(dpy, event->xclient.message_type);
306 char *s2 = XGetAtomName(dpy, event->xclient.data.l[0]);
307 if (!s1) s1 = "(null)";
308 if (!s2) s2 = "(null)";
309 fprintf (stderr, "%s: unknown ClientMessage %s[%s] received!\n",
314 return False; /* exit */
324 pick_visual (Screen *screen)
326 struct xscreensaver_function_table *ft = xscreensaver_function_table;
328 if (ft->pick_visual_hook)
330 Visual *v = ft->pick_visual_hook (screen);
334 return get_visual_resource (screen, "visualID", "VisualID", False);
338 /* Notice when the user has requested a different visual or colormap
339 on a pre-existing window (e.g., "-root -visual truecolor" or
340 "-window-id 0x2c00001 -install") and complain, since when drawing
341 on an existing window, we have no choice about these things.
344 visual_warning (Screen *screen, Window window, Visual *visual, Colormap cmap,
347 struct xscreensaver_function_table *ft = xscreensaver_function_table;
349 char *visual_string = get_string_resource (DisplayOfScreen (screen),
350 "visualID", "VisualID");
351 Visual *desired_visual = pick_visual (screen);
355 if (window == RootWindowOfScreen (screen))
356 strcpy (win, "root window");
358 sprintf (win, "window 0x%lx", (unsigned long) window);
361 sprintf (why, "-window-id 0x%lx", (unsigned long) window);
363 strcpy (why, "-root");
365 if (visual_string && *visual_string)
368 for (s = visual_string; *s; s++)
369 if (isupper (*s)) *s = _tolower (*s);
371 if (!strcmp (visual_string, "default") ||
372 !strcmp (visual_string, "default") ||
373 !strcmp (visual_string, "best"))
374 /* don't warn about these, just silently DWIM. */
376 else if (visual != desired_visual)
378 fprintf (stderr, "%s: ignoring `-visual %s' because of `%s'.\n",
379 progname, visual_string, why);
380 fprintf (stderr, "%s: using %s's visual 0x%lx.\n",
381 progname, win, XVisualIDFromVisual (visual));
383 free (visual_string);
386 if (visual == DefaultVisualOfScreen (screen) &&
387 has_writable_cells (screen, visual) &&
388 get_boolean_resource (DisplayOfScreen (screen),
389 "installColormap", "InstallColormap"))
391 fprintf (stderr, "%s: ignoring `-install' because of `%s'.\n",
393 fprintf (stderr, "%s: using %s's colormap 0x%lx.\n",
394 progname, win, (unsigned long) cmap);
397 if (ft->validate_visual_hook)
399 if (! ft->validate_visual_hook (screen, win, visual))
408 /* Bad Things Happen if stdin, stdout, and stderr have been closed
409 (as by the `sh incantation "attraction >&- 2>&-"). When you do
410 that, the X connection gets allocated to one of these fds, and
411 then some random library writes to stderr, and random bits get
412 stuffed down the X pipe, causing "Xlib: sequence lost" errors.
413 So, we cause the first three file descriptors to be open to
414 /dev/null if they aren't open to something else already. This
415 must be done before any other files are opened (or the closing
416 of that other file will again free up one of the "magic" first
419 We do this by opening /dev/null three times, and then closing
420 those fds, *unless* any of them got allocated as #0, #1, or #2,
421 in which case we leave them open. Gag.
423 Really, this crap is technically required of *every* X program,
424 if you want it to be robust in the face of "2>&-".
426 int fd0 = open ("/dev/null", O_RDWR);
427 int fd1 = open ("/dev/null", O_RDWR);
428 int fd2 = open ("/dev/null", O_RDWR);
429 if (fd0 > 2) close (fd0);
430 if (fd1 > 2) close (fd1);
431 if (fd2 > 2) close (fd2);
438 check_timing (unsigned long delay)
440 static unsigned long frame_count = 0;
441 static unsigned long delay_sum = 0;
442 static struct timeval prev1 = { 0, };
443 static struct timeval prev2 = { 0, };
445 double uprev1, uprev2, unow;
447 # ifdef GETTIMEOFDAY_TWO_ARGS
448 gettimeofday (&now, 0);
453 if (prev1.tv_sec == 0)
458 uprev1 = prev1.tv_sec + ((double) prev1.tv_usec * 0.000001);
459 uprev2 = prev2.tv_sec + ((double) prev2.tv_usec * 0.000001);
460 unow = now.tv_sec + ((double) now.tv_usec * 0.000001);
462 if (unow >= uprev1 + 1.5)
464 "%s: warning: blocked event processing for %.1f secs!\n",
465 progname, unow - uprev1);
468 if (unow >= uprev2 + 10.0)
470 double fps = frame_count / (unow - uprev2);
471 double elapsed = unow - uprev2;
472 double slept = (delay_sum * 0.000001);
473 double sleep_ratio = slept / elapsed;
475 if (sleep_ratio < 0.10) {
477 "%s: warning: only %.0f%% idle over the"
478 " last %2.0f secs (at %.1f FPS)\n",
479 progname, 100 * sleep_ratio, elapsed, fps);
490 #else /* !DEBUG_TIMING */
491 # define check_timing(delay) /**/
492 #endif /* !DEBUG_TIMING */
495 screenhack_table_handle_events (Display *dpy,
496 const struct xscreensaver_function_table *ft,
497 Window window, void *closure
499 , Window window2, void *closure2
503 XtAppContext app = XtDisplayToApplicationContext (dpy);
505 if (XtAppPending (app) & (XtIMTimer|XtIMAlternateInput))
506 XtAppProcessEvent (app, XtIMTimer|XtIMAlternateInput);
508 while (XPending (dpy))
511 XNextEvent (dpy, &event);
513 if (event.xany.type == ConfigureNotify)
515 if (event.xany.window == window)
516 ft->reshape_cb (dpy, window, closure,
517 event.xconfigure.width, event.xconfigure.height);
519 if (event.xany.window == window2)
520 ft->reshape_cb (dpy, window2, closure2,
521 event.xconfigure.width, event.xconfigure.height);
524 else if (event.xany.type == ClientMessage ||
525 (! (event.xany.window == window
526 ? ft->event_cb (dpy, window, closure, &event)
528 : event.xany.window == window2
529 ? ft->event_cb (dpy, window2, closure2, &event)
532 if (! screenhack_handle_event_1 (dpy, &event))
535 if (XtAppPending (app) & (XtIMTimer|XtIMAlternateInput))
536 XtAppProcessEvent (app, XtIMTimer|XtIMAlternateInput);
543 usleep_and_process_events (Display *dpy,
544 const struct xscreensaver_function_table *ft,
545 Window window, void *closure, unsigned long delay
547 , Window window2, void *closure2, unsigned long delay2
552 unsigned long quantum = 100000; /* 1/10th second */
561 check_timing (quantum);
563 /* The above isn't quite right in pair-mode: we always run both windows
564 with the timing of window 2. But, it's just a debugging hack, so
565 that doesn't really matter that much... */
567 if (! screenhack_table_handle_events (dpy, ft, window, closure
580 run_screenhack_table (Display *dpy,
585 const struct xscreensaver_function_table *ft)
588 /* Kludge: even though the init_cb functions are declared to take 2 args,
589 actually call them with 3, for the benefit of xlockmore_init() and
592 void *(*init_cb) (Display *, Window, void *) =
593 (void *(*) (Display *, Window, void *)) ft->init_cb;
595 void *closure = init_cb (dpy, window, ft->setup_arg);
599 if (window2) closure2 = init_cb (dpy, window2, ft->setup_arg);
602 if (! closure) /* if it returns nothing, it can't possibly be re-entrant. */
607 unsigned long delay = ft->draw_cb (dpy, window, closure);
609 unsigned long delay2;
610 if (window2) delay2 = ft->draw_cb (dpy, window2, closure2);
613 if (! usleep_and_process_events (dpy, ft,
614 window, closure, delay
616 , window2, closure2, delay2
622 ft->free_cb (dpy, window, closure);
626 ft->free_cb (dpy, window2, closure2);
632 make_shell (Screen *screen, Widget toplevel, int width, int height)
634 Display *dpy = DisplayOfScreen (screen);
635 Visual *visual = pick_visual (screen);
636 Boolean def_visual_p = (toplevel &&
637 visual == DefaultVisualOfScreen (screen));
639 if (width <= 0) width = 600;
640 if (height <= 0) height = 480;
643 if (!validate_gl_visual (stderr, screen, "window", visual))
650 XtVaSetValues (toplevel,
651 XtNmappedWhenManaged, False,
654 XtNinput, True, /* for WM_HINTS */
656 XtRealizeWidget (toplevel);
657 window = XtWindow (toplevel);
659 if (get_boolean_resource (dpy, "installColormap", "InstallColormap"))
662 XCreateColormap (dpy, window, DefaultVisualOfScreen (screen),
664 XSetWindowColormap (dpy, window, cmap);
671 Colormap cmap = XCreateColormap (dpy, VirtualRootWindowOfScreen(screen),
673 bg = get_pixel_resource (dpy, cmap, "background", "Background");
674 bd = get_pixel_resource (dpy, cmap, "borderColor", "Foreground");
676 new = XtVaAppCreateShell (progname, progclass,
677 topLevelShellWidgetClass, dpy,
678 XtNmappedWhenManaged, False,
680 XtNdepth, visual_depth (screen, visual),
684 XtNbackground, (Pixel) bg,
685 XtNborderColor, (Pixel) bd,
686 XtNinput, True, /* for WM_HINTS */
689 if (!toplevel) /* kludge for the second window in -pair mode... */
690 XtVaSetValues (new, XtNx, 0, XtNy, 550, NULL);
692 XtRealizeWidget (new);
700 init_window (Display *dpy, Widget toplevel, const char *title)
703 XWindowAttributes xgwa;
704 XtPopup (toplevel, XtGrabNone);
705 XtVaSetValues (toplevel, XtNtitle, title, NULL);
707 /* Select KeyPress, and announce that we accept WM_DELETE_WINDOW.
709 window = XtWindow (toplevel);
710 XGetWindowAttributes (dpy, window, &xgwa);
711 XSelectInput (dpy, window,
712 (xgwa.your_event_mask | KeyPressMask |
713 ButtonPressMask | ButtonReleaseMask));
714 XChangeProperty (dpy, window, XA_WM_PROTOCOLS, XA_ATOM, 32,
716 (unsigned char *) &XA_WM_DELETE_WINDOW, 1);
721 main (int argc, char **argv)
723 struct xscreensaver_function_table *ft = xscreensaver_function_table;
725 XWindowAttributes xgwa;
731 Widget toplevel2 = 0;
735 Window on_window = 0;
743 progname = argv[0]; /* reset later */
744 progclass = ft->progclass;
747 ft->setup_cb (ft, ft->setup_arg);
752 /* We have to do this on SGI to prevent the background color from being
753 overridden by the current desktop color scheme (we'd like our backgrounds
754 to be black, thanks.) This should be the same as setting the
755 "*useSchemes: none" resource, but it's not -- if that resource is
756 present in the `default_defaults' above, it doesn't work, though it
757 does work when passed as an -xrm arg on the command line. So screw it,
758 turn them off from C instead.
760 SgiUseSchemes ("none");
763 toplevel = XtAppInitialize (&app, progclass, merged_options,
764 merged_options_size, &argc, argv,
765 merged_defaults, 0, 0);
767 dpy = XtDisplay (toplevel);
769 XtGetApplicationNameAndClass (dpy,
771 (char **) &progclass);
773 /* half-assed way of avoiding buffer-overrun attacks. */
774 if (strlen (progname) >= 100) ((char *) progname)[100] = 0;
776 XSetErrorHandler (screenhack_ehandler);
778 XA_WM_PROTOCOLS = XInternAtom (dpy, "WM_PROTOCOLS", False);
779 XA_WM_DELETE_WINDOW = XInternAtom (dpy, "WM_DELETE_WINDOW", False);
782 char *v = (char *) strdup(strchr(screensaver_id, ' '));
783 char *s1, *s2, *s3, *s4;
784 s1 = (char *) strchr(v, ' '); s1++;
785 s2 = (char *) strchr(s1, ' ');
786 s3 = (char *) strchr(v, '('); s3++;
787 s4 = (char *) strchr(s3, ')');
790 sprintf (version, "%s: from the XScreenSaver %s distribution (%s.)",
801 Bool help_p = (!strcmp(argv[1], "-help") ||
802 !strcmp(argv[1], "--help"));
803 fprintf (stderr, "%s\n", version);
804 for (s = progclass; *s; s++) fprintf(stderr, " ");
805 fprintf (stderr, " http://www.jwz.org/xscreensaver/\n\n");
808 fprintf(stderr, "Unrecognised option: %s\n", argv[1]);
809 fprintf (stderr, "Options include: ");
810 for (i = 0; i < merged_options_size; i++)
812 char *sw = merged_options [i].option;
813 Bool argp = (merged_options [i].argKind == XrmoptionSepArg);
814 int size = strlen (sw) + (argp ? 6 : 0) + 2;
817 fprintf (stderr, "\n\t\t ");
821 fprintf (stderr, "%s", sw);
822 if (argp) fprintf (stderr, " <arg>");
823 if (i != merged_options_size - 1) fprintf (stderr, ", ");
826 fprintf (stderr, ".\n");
831 fprintf (stderr, "\nResources:\n\n");
832 for (i = 0; i < merged_options_size; i++)
834 const char *opt = merged_options [i].option;
835 const char *res = merged_options [i].specifier + 1;
836 const char *val = merged_options [i].value;
837 char *s = get_string_resource (dpy, (char *) res, (char *) res);
842 while (L > 0 && (s[L-1] == ' ' || s[L-1] == '\t'))
846 fprintf (stderr, " %-16s %-18s ", opt, res);
847 if (merged_options [i].argKind == XrmoptionSepArg)
849 fprintf (stderr, "[%s]", (s ? s : "?"));
853 fprintf (stderr, "%s", (val ? val : "(null)"));
854 if (val && s && !strcasecmp (val, s))
855 fprintf (stderr, " [default]");
857 fprintf (stderr, "\n");
859 fprintf (stderr, "\n");
863 exit (help_p ? 0 : 1);
866 free (merged_options);
867 free (merged_defaults);
871 dont_clear = get_boolean_resource (dpy, "dontClearRoot", "Boolean");
872 mono_p = get_boolean_resource (dpy, "mono", "Boolean");
873 if (CellsOfScreen (DefaultScreenOfDisplay (dpy)) <= 2)
876 root_p = get_boolean_resource (dpy, "root", "Boolean");
879 char *s = get_string_resource (dpy, "windowID", "WindowID");
881 on_window = get_integer_resource (dpy, "windowID", "WindowID");
887 window = (Window) on_window;
888 XtDestroyWidget (toplevel);
889 XGetWindowAttributes (dpy, window, &xgwa);
890 visual_warning (xgwa.screen, window, xgwa.visual, xgwa.colormap, True);
892 /* Select KeyPress and resize events on the external window.
894 xgwa.your_event_mask |= KeyPressMask | StructureNotifyMask;
895 XSelectInput (dpy, window, xgwa.your_event_mask);
897 /* Select ButtonPress and ButtonRelease events on the external window,
898 if no other app has already selected them (only one app can select
899 ButtonPress at a time: BadAccess results.)
901 if (! (xgwa.all_event_masks & (ButtonPressMask | ButtonReleaseMask)))
902 XSelectInput (dpy, window,
903 (xgwa.your_event_mask |
904 ButtonPressMask | ButtonReleaseMask));
908 window = VirtualRootWindowOfScreen (XtScreen (toplevel));
909 XtDestroyWidget (toplevel);
910 XGetWindowAttributes (dpy, window, &xgwa);
911 visual_warning (xgwa.screen, window, xgwa.visual, xgwa.colormap, False);
915 Widget new = make_shell (XtScreen (toplevel), toplevel,
916 toplevel->core.width,
917 toplevel->core.height);
920 XtDestroyWidget (toplevel);
924 init_window (dpy, toplevel, version);
925 window = XtWindow (toplevel);
926 XGetWindowAttributes (dpy, window, &xgwa);
929 if (get_boolean_resource (dpy, "pair", "Boolean"))
931 toplevel2 = make_shell (xgwa.screen, 0,
932 toplevel->core.width,
933 toplevel->core.height);
934 init_window (dpy, toplevel2, version);
935 window2 = XtWindow (toplevel2);
937 # endif /* DEBUG_PAIR */
942 unsigned int bg = get_pixel_resource (dpy, xgwa.colormap,
943 "background", "Background");
944 XSetWindowBackground (dpy, window, bg);
945 XClearWindow (dpy, window);
949 XSetWindowBackground (dpy, window2, bg);
950 XClearWindow (dpy, window2);
955 if (!root_p && !on_window)
956 /* wait for it to be mapped */
957 XIfEvent (dpy, &event, MapNotify_event_p, (XPointer) window);
961 /* This is the one and only place that the random-number generator is
962 seeded in any screenhack. You do not need to seed the RNG again,
963 it is done for you before your code is invoked. */
967 run_screenhack_table (dpy, window,
973 XtDestroyWidget (toplevel);
974 XtDestroyApplicationContext (app);