1 /* xscreensaver, Copyright (c) 1992-2017 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_free (Display *, Window, void *closure);
48 Called when you are done: free everything you've allocated,
49 including your private `state' structure.
51 NOTE: this is called in windowed-mode when the user typed
52 'q' or clicks on the window's close box; but when
53 xscreensaver terminates this screenhack, it does so by
54 killing the process with SIGSTOP. So this callback is
57 static char YOURNAME_defaults [] = { "...", "...", ... , 0 };
59 This variable is an array of strings, your default resources.
60 Null-terminate the list.
62 static XrmOptionDescRec YOURNAME_options[] = { { ... }, ... { 0,0,0,0 } }
64 This variable describes your command-line options.
65 Null-terminate the list.
67 Finally , invoke the XSCREENSAVER_MODULE() macro to tie it all together.
71 - Make sure that all functions in your module are static (check this
72 by running "nm -g" on the .o file).
74 - Do not use global variables: all such info must be stored in the
75 private `state' structure.
77 - Do not use static function-local variables, either. Put it in `state'.
79 Assume that there are N independent runs of this code going in the
80 same address space at the same time: they must not affect each other.
82 - Don't forget to write an XML file to describe the user interface
83 of your screen saver module. See .../hacks/config/README for details.
89 #include <X11/Intrinsic.h>
90 #include <X11/IntrinsicP.h>
91 #include <X11/CoreP.h>
92 #include <X11/Shell.h>
93 #include <X11/StringDefs.h>
94 #include <X11/keysym.h>
97 # include <X11/SGIScheme.h> /* for SgiUseSchemes() */
102 # include <X11/Xmu/Error.h>
104 # include <Xmu/Error.h>
110 #include "screenhackI.h"
115 #ifdef HAVE_RECORD_ANIM
116 # include "recanim.h"
119 #ifndef _XSCREENSAVER_VROOT_H_
120 # error Error! You have an old version of vroot.h! Check -I args.
121 #endif /* _XSCREENSAVER_VROOT_H_ */
124 # define isupper(c) ((c) >= 'A' && (c) <= 'Z')
127 # define _tolower(c) ((c) - 'A' + 'a')
131 /* This is defined by the SCREENHACK_MAIN() macro via screenhack.h.
133 extern struct xscreensaver_function_table *xscreensaver_function_table;
136 const char *progname; /* used by hacks in error messages */
137 const char *progclass; /* used by ../utils/resources.c */
138 Bool mono_p; /* used by hacks */
141 static XrmOptionDescRec default_options [] = {
142 { "-root", ".root", XrmoptionNoArg, "True" },
143 { "-window", ".root", XrmoptionNoArg, "False" },
144 { "-mono", ".mono", XrmoptionNoArg, "True" },
145 { "-install", ".installColormap", XrmoptionNoArg, "True" },
146 { "-noinstall",".installColormap", XrmoptionNoArg, "False" },
147 { "-visual", ".visualID", XrmoptionSepArg, 0 },
148 { "-window-id", ".windowID", XrmoptionSepArg, 0 },
149 { "-fps", ".doFPS", XrmoptionNoArg, "True" },
150 { "-no-fps", ".doFPS", XrmoptionNoArg, "False" },
153 { "-pair", ".pair", XrmoptionNoArg, "True" },
154 # endif /* DEBUG_PAIR */
156 # ifdef HAVE_RECORD_ANIM
157 { "-record-animation", ".recordAnim", XrmoptionSepArg, 0 },
158 # endif /* HAVE_RECORD_ANIM */
163 static char *default_defaults[] = {
165 "*geometry: 1280x720", /* this should be .geometry, but noooo... */
167 "*installColormap: false",
169 "*multiSample: false",
170 "*visualID: default",
172 "*desktopGrabber: xscreensaver-getimage %s",
176 static XrmOptionDescRec *merged_options;
177 static int merged_options_size;
178 static char **merged_defaults;
184 struct xscreensaver_function_table *ft = xscreensaver_function_table;
186 const XrmOptionDescRec *options = ft->options;
187 const char * const *defaults = ft->defaults;
188 const char *progclass = ft->progclass;
190 int def_opts_size, opts_size;
191 int def_defaults_size, defaults_size;
193 for (def_opts_size = 0; default_options[def_opts_size].option;
196 for (opts_size = 0; options[opts_size].option; opts_size++)
199 merged_options_size = def_opts_size + opts_size;
200 merged_options = (XrmOptionDescRec *)
201 malloc ((merged_options_size + 1) * sizeof(*default_options));
202 memcpy (merged_options, default_options,
203 (def_opts_size * sizeof(*default_options)));
204 memcpy (merged_options + def_opts_size, options,
205 ((opts_size + 1) * sizeof(*default_options)));
207 for (def_defaults_size = 0; default_defaults[def_defaults_size];
210 for (defaults_size = 0; defaults[defaults_size]; defaults_size++)
212 merged_defaults = (char **)
213 malloc ((def_defaults_size + defaults_size + 1) * sizeof (*defaults));;
214 memcpy (merged_defaults, default_defaults,
215 def_defaults_size * sizeof(*defaults));
216 memcpy (merged_defaults + def_defaults_size, defaults,
217 (defaults_size + 1) * sizeof(*defaults));
219 /* This totally sucks. Xt should behave like this by default.
220 If the string in `defaults' looks like ".foo", change that
225 for (s = merged_defaults; *s; s++)
228 const char *oldr = *s;
229 char *newr = (char *) malloc(strlen(oldr) + strlen(progclass) + 3);
230 strcpy (newr, progclass);
240 /* Make the X errors print out the name of this program, so we have some
241 clue which one has a bug when they die under the screensaver.
245 screenhack_ehandler (Display *dpy, XErrorEvent *error)
247 fprintf (stderr, "\nX error in %s:\n", progname);
248 if (XmuPrintDefaultErrorMessage (dpy, error, stderr))
251 fprintf (stderr, " (nonfatal.)\n");
256 MapNotify_event_p (Display *dpy, XEvent *event, XPointer window)
258 return (event->xany.type == MapNotify &&
259 event->xvisibility.window == (Window) window);
263 static Atom XA_WM_PROTOCOLS, XA_WM_DELETE_WINDOW;
265 /* Dead-trivial event handling: exits if "q" or "ESC" are typed.
266 Exit if the WM_PROTOCOLS WM_DELETE_WINDOW ClientMessage is received.
267 Returns False if the screen saver should now terminate.
270 screenhack_handle_event_1 (Display *dpy, XEvent *event)
272 switch (event->xany.type)
278 XLookupString (&event->xkey, &c, 1, &keysym, 0);
283 return False; /* exit */
284 else if (! (keysym >= XK_Shift_L && keysym <= XK_Hyper_R))
285 XBell (dpy, 0); /* beep for non-chord keys */
293 if (event->xclient.message_type != XA_WM_PROTOCOLS)
295 char *s = XGetAtomName(dpy, event->xclient.message_type);
296 if (!s) s = "(null)";
297 fprintf (stderr, "%s: unknown ClientMessage %s received!\n",
300 else if (event->xclient.data.l[0] != XA_WM_DELETE_WINDOW)
302 char *s1 = XGetAtomName(dpy, event->xclient.message_type);
303 char *s2 = XGetAtomName(dpy, event->xclient.data.l[0]);
304 if (!s1) s1 = "(null)";
305 if (!s2) s2 = "(null)";
306 fprintf (stderr, "%s: unknown ClientMessage %s[%s] received!\n",
311 return False; /* exit */
321 pick_visual (Screen *screen)
323 struct xscreensaver_function_table *ft = xscreensaver_function_table;
325 if (ft->pick_visual_hook)
327 Visual *v = ft->pick_visual_hook (screen);
331 return get_visual_resource (screen, "visualID", "VisualID", False);
335 /* Notice when the user has requested a different visual or colormap
336 on a pre-existing window (e.g., "-root -visual truecolor" or
337 "-window-id 0x2c00001 -install") and complain, since when drawing
338 on an existing window, we have no choice about these things.
341 visual_warning (Screen *screen, Window window, Visual *visual, Colormap cmap,
344 struct xscreensaver_function_table *ft = xscreensaver_function_table;
346 char *visual_string = get_string_resource (DisplayOfScreen (screen),
347 "visualID", "VisualID");
348 Visual *desired_visual = pick_visual (screen);
352 if (window == RootWindowOfScreen (screen))
353 strcpy (win, "root window");
355 sprintf (win, "window 0x%lx", (unsigned long) window);
358 sprintf (why, "-window-id 0x%lx", (unsigned long) window);
360 strcpy (why, "-root");
362 if (visual_string && *visual_string)
365 for (s = visual_string; *s; s++)
366 if (isupper (*s)) *s = _tolower (*s);
368 if (!strcmp (visual_string, "default") ||
369 !strcmp (visual_string, "default") ||
370 !strcmp (visual_string, "best"))
371 /* don't warn about these, just silently DWIM. */
373 else if (visual != desired_visual)
375 fprintf (stderr, "%s: ignoring `-visual %s' because of `%s'.\n",
376 progname, visual_string, why);
377 fprintf (stderr, "%s: using %s's visual 0x%lx.\n",
378 progname, win, XVisualIDFromVisual (visual));
380 free (visual_string);
383 if (visual == DefaultVisualOfScreen (screen) &&
384 has_writable_cells (screen, visual) &&
385 get_boolean_resource (DisplayOfScreen (screen),
386 "installColormap", "InstallColormap"))
388 fprintf (stderr, "%s: ignoring `-install' because of `%s'.\n",
390 fprintf (stderr, "%s: using %s's colormap 0x%lx.\n",
391 progname, win, (unsigned long) cmap);
394 if (ft->validate_visual_hook)
396 if (! ft->validate_visual_hook (screen, win, visual))
405 /* Bad Things Happen if stdin, stdout, and stderr have been closed
406 (as by the `sh incantation "attraction >&- 2>&-"). When you do
407 that, the X connection gets allocated to one of these fds, and
408 then some random library writes to stderr, and random bits get
409 stuffed down the X pipe, causing "Xlib: sequence lost" errors.
410 So, we cause the first three file descriptors to be open to
411 /dev/null if they aren't open to something else already. This
412 must be done before any other files are opened (or the closing
413 of that other file will again free up one of the "magic" first
416 We do this by opening /dev/null three times, and then closing
417 those fds, *unless* any of them got allocated as #0, #1, or #2,
418 in which case we leave them open. Gag.
420 Really, this crap is technically required of *every* X program,
421 if you want it to be robust in the face of "2>&-".
423 int fd0 = open ("/dev/null", O_RDWR);
424 int fd1 = open ("/dev/null", O_RDWR);
425 int fd2 = open ("/dev/null", O_RDWR);
426 if (fd0 > 2) close (fd0);
427 if (fd1 > 2) close (fd1);
428 if (fd2 > 2) close (fd2);
433 screenhack_table_handle_events (Display *dpy,
434 const struct xscreensaver_function_table *ft,
435 Window window, void *closure
437 , Window window2, void *closure2
441 XtAppContext app = XtDisplayToApplicationContext (dpy);
443 if (XtAppPending (app) & (XtIMTimer|XtIMAlternateInput))
444 XtAppProcessEvent (app, XtIMTimer|XtIMAlternateInput);
446 while (XPending (dpy))
449 XNextEvent (dpy, &event);
451 if (event.xany.type == ConfigureNotify)
453 if (event.xany.window == window)
454 ft->reshape_cb (dpy, window, closure,
455 event.xconfigure.width, event.xconfigure.height);
457 if (window2 && event.xany.window == window2)
458 ft->reshape_cb (dpy, window2, closure2,
459 event.xconfigure.width, event.xconfigure.height);
462 else if (event.xany.type == ClientMessage ||
463 (! (event.xany.window == window
464 ? ft->event_cb (dpy, window, closure, &event)
466 : (window2 && event.xany.window == window2)
467 ? ft->event_cb (dpy, window2, closure2, &event)
470 if (! screenhack_handle_event_1 (dpy, &event))
473 if (XtAppPending (app) & (XtIMTimer|XtIMAlternateInput))
474 XtAppProcessEvent (app, XtIMTimer|XtIMAlternateInput);
481 usleep_and_process_events (Display *dpy,
482 const struct xscreensaver_function_table *ft,
483 Window window, fps_state *fpst, void *closure,
486 , Window window2, fps_state *fpst2, void *closure2,
489 # ifdef HAVE_RECORD_ANIM
490 , record_anim_state *anim_state
495 unsigned long quantum = 33333; /* 30 fps */
502 #ifdef HAVE_RECORD_ANIM
503 if (anim_state) screenhack_record_anim (anim_state);
509 if (fpst) fps_slept (fpst, quantum);
511 if (fpst2) fps_slept (fpst2, quantum);
515 if (! screenhack_table_handle_events (dpy, ft, window, closure
528 screenhack_do_fps (Display *dpy, Window w, fps_state *fpst, void *closure)
530 fps_compute (fpst, 0, -1);
536 run_screenhack_table (Display *dpy,
541 # ifdef HAVE_RECORD_ANIM
542 record_anim_state *anim_state,
544 const struct xscreensaver_function_table *ft)
547 /* Kludge: even though the init_cb functions are declared to take 2 args,
548 actually call them with 3, for the benefit of xlockmore_init() and
551 void *(*init_cb) (Display *, Window, void *) =
552 (void *(*) (Display *, Window, void *)) ft->init_cb;
554 void (*fps_cb) (Display *, Window, fps_state *, void *) = ft->fps_cb;
556 void *closure = init_cb (dpy, window, ft->setup_arg);
557 fps_state *fpst = fps_init (dpy, window);
561 fps_state *fpst2 = 0;
562 if (window2) closure2 = init_cb (dpy, window2, ft->setup_arg);
563 if (window2) fpst2 = fps_init (dpy, window2);
566 if (! closure) /* if it returns nothing, it can't possibly be re-entrant. */
569 if (! fps_cb) fps_cb = screenhack_do_fps;
573 unsigned long delay = ft->draw_cb (dpy, window, closure);
575 unsigned long delay2 = 0;
576 if (window2) delay2 = ft->draw_cb (dpy, window2, closure2);
579 if (fpst) fps_cb (dpy, window, fpst, closure);
581 if (fpst2) fps_cb (dpy, window2, fpst2, closure2);
584 if (! usleep_and_process_events (dpy, ft,
585 window, fpst, closure, delay
587 , window2, fpst2, closure2, delay2
589 #ifdef HAVE_RECORD_ANIM
596 #ifdef HAVE_RECORD_ANIM
597 /* Exiting before target frames hit: write the video anyway. */
598 if (anim_state) screenhack_record_anim_free (anim_state);
601 ft->free_cb (dpy, window, closure);
602 if (fpst) fps_free (fpst);
605 if (window2) ft->free_cb (dpy, window2, closure2);
606 if (fpst2) fps_free (fpst2);
612 make_shell (Screen *screen, Widget toplevel, int width, int height)
614 Display *dpy = DisplayOfScreen (screen);
615 Visual *visual = pick_visual (screen);
616 Boolean def_visual_p = (toplevel &&
617 visual == DefaultVisualOfScreen (screen));
619 if (width <= 0) width = 600;
620 if (height <= 0) height = 480;
625 XtVaSetValues (toplevel,
626 XtNmappedWhenManaged, False,
629 XtNinput, True, /* for WM_HINTS */
631 XtRealizeWidget (toplevel);
632 window = XtWindow (toplevel);
634 if (get_boolean_resource (dpy, "installColormap", "InstallColormap"))
637 XCreateColormap (dpy, window, DefaultVisualOfScreen (screen),
639 XSetWindowColormap (dpy, window, cmap);
646 Colormap cmap = XCreateColormap (dpy, VirtualRootWindowOfScreen(screen),
648 bg = get_pixel_resource (dpy, cmap, "background", "Background");
649 bd = get_pixel_resource (dpy, cmap, "borderColor", "Foreground");
651 new = XtVaAppCreateShell (progname, progclass,
652 topLevelShellWidgetClass, dpy,
653 XtNmappedWhenManaged, False,
655 XtNdepth, visual_depth (screen, visual),
659 XtNbackground, (Pixel) bg,
660 XtNborderColor, (Pixel) bd,
661 XtNinput, True, /* for WM_HINTS */
664 if (!toplevel) /* kludge for the second window in -pair mode... */
665 XtVaSetValues (new, XtNx, 0, XtNy, 550, NULL);
667 XtRealizeWidget (new);
675 init_window (Display *dpy, Widget toplevel, const char *title)
678 XWindowAttributes xgwa;
679 XtPopup (toplevel, XtGrabNone);
680 XtVaSetValues (toplevel, XtNtitle, title, NULL);
682 /* Select KeyPress, and announce that we accept WM_DELETE_WINDOW.
684 window = XtWindow (toplevel);
685 XGetWindowAttributes (dpy, window, &xgwa);
686 XSelectInput (dpy, window,
687 (xgwa.your_event_mask | KeyPressMask | KeyReleaseMask |
688 ButtonPressMask | ButtonReleaseMask));
689 XChangeProperty (dpy, window, XA_WM_PROTOCOLS, XA_ATOM, 32,
691 (unsigned char *) &XA_WM_DELETE_WINDOW, 1);
696 main (int argc, char **argv)
698 struct xscreensaver_function_table *ft = xscreensaver_function_table;
700 XWindowAttributes xgwa;
706 Widget toplevel2 = 0;
708 #ifdef HAVE_RECORD_ANIM
709 record_anim_state *anim_state = 0;
713 Window on_window = 0;
720 progname = argv[0]; /* reset later */
721 progclass = ft->progclass;
724 ft->setup_cb (ft, ft->setup_arg);
729 /* We have to do this on SGI to prevent the background color from being
730 overridden by the current desktop color scheme (we'd like our backgrounds
731 to be black, thanks.) This should be the same as setting the
732 "*useSchemes: none" resource, but it's not -- if that resource is
733 present in the `default_defaults' above, it doesn't work, though it
734 does work when passed as an -xrm arg on the command line. So screw it,
735 turn them off from C instead.
737 SgiUseSchemes ("none");
740 toplevel = XtAppInitialize (&app, progclass, merged_options,
741 merged_options_size, &argc, argv,
742 merged_defaults, 0, 0);
744 dpy = XtDisplay (toplevel);
746 XtGetApplicationNameAndClass (dpy,
748 (char **) &progclass);
750 /* half-assed way of avoiding buffer-overrun attacks. */
751 if (strlen (progname) >= 100) ((char *) progname)[100] = 0;
753 XSetErrorHandler (screenhack_ehandler);
755 XA_WM_PROTOCOLS = XInternAtom (dpy, "WM_PROTOCOLS", False);
756 XA_WM_DELETE_WINDOW = XInternAtom (dpy, "WM_DELETE_WINDOW", False);
759 char *v = (char *) strdup(strchr(screensaver_id, ' '));
760 char *s1, *s2, *s3, *s4;
761 const char *ot = get_string_resource (dpy, "title", "Title");
762 s1 = (char *) strchr(v, ' '); s1++;
763 s2 = (char *) strchr(s1, ' ');
764 s3 = (char *) strchr(v, '('); s3++;
765 s4 = (char *) strchr(s3, ')');
768 if (ot && !*ot) ot = 0;
769 sprintf (version, "%.50s%s%s: from the XScreenSaver %s distribution (%s)",
782 Bool help_p = (!strcmp(argv[1], "-help") ||
783 !strcmp(argv[1], "--help"));
784 fprintf (stderr, "%s\n", version);
785 for (s = progclass; *s; s++) fprintf(stderr, " ");
786 fprintf (stderr, " https://www.jwz.org/xscreensaver/\n\n");
789 fprintf(stderr, "Unrecognised option: %s\n", argv[1]);
790 fprintf (stderr, "Options include: ");
791 for (i = 0; i < merged_options_size; i++)
793 char *sw = merged_options [i].option;
794 Bool argp = (merged_options [i].argKind == XrmoptionSepArg);
795 int size = strlen (sw) + (argp ? 6 : 0) + 2;
798 fprintf (stderr, "\n\t\t ");
802 fprintf (stderr, "%s", sw);
803 if (argp) fprintf (stderr, " <arg>");
804 if (i != merged_options_size - 1) fprintf (stderr, ", ");
807 fprintf (stderr, ".\n");
812 fprintf (stderr, "\nResources:\n\n");
813 for (i = 0; i < merged_options_size; i++)
815 const char *opt = merged_options [i].option;
816 const char *res = merged_options [i].specifier + 1;
817 const char *val = merged_options [i].value;
818 char *s = get_string_resource (dpy, (char *) res, (char *) res);
823 while (L > 0 && (s[L-1] == ' ' || s[L-1] == '\t'))
827 fprintf (stderr, " %-16s %-18s ", opt, res);
828 if (merged_options [i].argKind == XrmoptionSepArg)
830 fprintf (stderr, "[%s]", (s ? s : "?"));
834 fprintf (stderr, "%s", (val ? val : "(null)"));
835 if (val && s && !strcasecmp (val, s))
836 fprintf (stderr, " [default]");
838 fprintf (stderr, "\n");
840 fprintf (stderr, "\n");
844 exit (help_p ? 0 : 1);
849 for (s = merged_defaults; *s; s++)
853 free (merged_options);
854 free (merged_defaults);
858 dont_clear = get_boolean_resource (dpy, "dontClearRoot", "Boolean");
859 mono_p = get_boolean_resource (dpy, "mono", "Boolean");
860 if (CellsOfScreen (DefaultScreenOfDisplay (dpy)) <= 2)
863 root_p = get_boolean_resource (dpy, "root", "Boolean");
866 char *s = get_string_resource (dpy, "windowID", "WindowID");
868 on_window = get_integer_resource (dpy, "windowID", "WindowID");
874 window = (Window) on_window;
875 XtDestroyWidget (toplevel);
876 XGetWindowAttributes (dpy, window, &xgwa);
877 visual_warning (xgwa.screen, window, xgwa.visual, xgwa.colormap, True);
879 /* Select KeyPress and resize events on the external window.
881 xgwa.your_event_mask |= KeyPressMask | StructureNotifyMask;
882 XSelectInput (dpy, window, xgwa.your_event_mask);
884 /* Select ButtonPress and ButtonRelease events on the external window,
885 if no other app has already selected them (only one app can select
886 ButtonPress at a time: BadAccess results.)
888 if (! (xgwa.all_event_masks & (ButtonPressMask | ButtonReleaseMask)))
889 XSelectInput (dpy, window,
890 (xgwa.your_event_mask |
891 ButtonPressMask | ButtonReleaseMask));
895 window = VirtualRootWindowOfScreen (XtScreen (toplevel));
896 XtDestroyWidget (toplevel);
897 XGetWindowAttributes (dpy, window, &xgwa);
898 /* With RANDR, the root window can resize! */
899 XSelectInput (dpy, window, xgwa.your_event_mask | StructureNotifyMask);
900 visual_warning (xgwa.screen, window, xgwa.visual, xgwa.colormap, False);
904 Widget new = make_shell (XtScreen (toplevel), toplevel,
905 toplevel->core.width,
906 toplevel->core.height);
909 XtDestroyWidget (toplevel);
913 init_window (dpy, toplevel, version);
914 window = XtWindow (toplevel);
915 XGetWindowAttributes (dpy, window, &xgwa);
918 if (get_boolean_resource (dpy, "pair", "Boolean"))
920 toplevel2 = make_shell (xgwa.screen, 0,
921 toplevel->core.width,
922 toplevel->core.height);
923 init_window (dpy, toplevel2, version);
924 window2 = XtWindow (toplevel2);
926 # endif /* DEBUG_PAIR */
931 unsigned int bg = get_pixel_resource (dpy, xgwa.colormap,
932 "background", "Background");
933 XSetWindowBackground (dpy, window, bg);
934 XClearWindow (dpy, window);
938 XSetWindowBackground (dpy, window2, bg);
939 XClearWindow (dpy, window2);
944 if (!root_p && !on_window)
945 /* wait for it to be mapped */
946 XIfEvent (dpy, &event, MapNotify_event_p, (XPointer) window);
950 /* This is the one and only place that the random-number generator is
951 seeded in any screenhack. You do not need to seed the RNG again,
952 it is done for you before your code is invoked. */
957 #ifdef HAVE_RECORD_ANIM
959 int frames = get_integer_resource (dpy, "recordAnim", "Integer");
961 anim_state = screenhack_record_anim_init (xgwa.screen, window, frames);
965 run_screenhack_table (dpy, window,
969 # ifdef HAVE_RECORD_ANIM
974 #ifdef HAVE_RECORD_ANIM
975 if (anim_state) screenhack_record_anim_free (anim_state);
978 XtDestroyWidget (toplevel);
979 XtDestroyApplicationContext (app);