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);
558 unsigned long delay = 0;
562 fps_state *fpst2 = 0;
563 unsigned long delay2 = 0;
564 if (window2) closure2 = init_cb (dpy, window2, ft->setup_arg);
565 if (window2) fpst2 = fps_init (dpy, window2);
568 if (! closure) /* if it returns nothing, it can't possibly be re-entrant. */
571 if (! fps_cb) fps_cb = screenhack_do_fps;
575 if (! usleep_and_process_events (dpy, ft,
576 window, fpst, closure, delay
578 , window2, fpst2, closure2, delay2
580 #ifdef HAVE_RECORD_ANIM
586 delay = ft->draw_cb (dpy, window, closure);
589 if (window2) delay2 = ft->draw_cb (dpy, window2, closure2);
592 if (fpst) fps_cb (dpy, window, fpst, closure);
594 if (fpst2) fps_cb (dpy, window2, fpst2, closure2);
598 #ifdef HAVE_RECORD_ANIM
599 /* Exiting before target frames hit: write the video anyway. */
600 if (anim_state) screenhack_record_anim_free (anim_state);
603 ft->free_cb (dpy, window, closure);
604 if (fpst) fps_free (fpst);
607 if (window2) ft->free_cb (dpy, window2, closure2);
608 if (fpst2) fps_free (fpst2);
614 make_shell (Screen *screen, Widget toplevel, int width, int height)
616 Display *dpy = DisplayOfScreen (screen);
617 Visual *visual = pick_visual (screen);
618 Boolean def_visual_p = (toplevel &&
619 visual == DefaultVisualOfScreen (screen));
621 if (width <= 0) width = 600;
622 if (height <= 0) height = 480;
627 XtVaSetValues (toplevel,
628 XtNmappedWhenManaged, False,
631 XtNinput, True, /* for WM_HINTS */
633 XtRealizeWidget (toplevel);
634 window = XtWindow (toplevel);
636 if (get_boolean_resource (dpy, "installColormap", "InstallColormap"))
639 XCreateColormap (dpy, window, DefaultVisualOfScreen (screen),
641 XSetWindowColormap (dpy, window, cmap);
648 Colormap cmap = XCreateColormap (dpy, VirtualRootWindowOfScreen(screen),
650 bg = get_pixel_resource (dpy, cmap, "background", "Background");
651 bd = get_pixel_resource (dpy, cmap, "borderColor", "Foreground");
653 new = XtVaAppCreateShell (progname, progclass,
654 topLevelShellWidgetClass, dpy,
655 XtNmappedWhenManaged, False,
657 XtNdepth, visual_depth (screen, visual),
661 XtNbackground, (Pixel) bg,
662 XtNborderColor, (Pixel) bd,
663 XtNinput, True, /* for WM_HINTS */
666 if (!toplevel) /* kludge for the second window in -pair mode... */
667 XtVaSetValues (new, XtNx, 0, XtNy, 550, NULL);
669 XtRealizeWidget (new);
677 init_window (Display *dpy, Widget toplevel, const char *title)
680 XWindowAttributes xgwa;
681 XtPopup (toplevel, XtGrabNone);
682 XtVaSetValues (toplevel, XtNtitle, title, NULL);
684 /* Select KeyPress, and announce that we accept WM_DELETE_WINDOW.
686 window = XtWindow (toplevel);
687 XGetWindowAttributes (dpy, window, &xgwa);
688 XSelectInput (dpy, window,
689 (xgwa.your_event_mask | KeyPressMask | KeyReleaseMask |
690 ButtonPressMask | ButtonReleaseMask));
691 XChangeProperty (dpy, window, XA_WM_PROTOCOLS, XA_ATOM, 32,
693 (unsigned char *) &XA_WM_DELETE_WINDOW, 1);
698 main (int argc, char **argv)
700 struct xscreensaver_function_table *ft = xscreensaver_function_table;
702 XWindowAttributes xgwa;
708 Widget toplevel2 = 0;
710 #ifdef HAVE_RECORD_ANIM
711 record_anim_state *anim_state = 0;
715 Window on_window = 0;
722 progname = argv[0]; /* reset later */
723 progclass = ft->progclass;
726 ft->setup_cb (ft, ft->setup_arg);
731 /* We have to do this on SGI to prevent the background color from being
732 overridden by the current desktop color scheme (we'd like our backgrounds
733 to be black, thanks.) This should be the same as setting the
734 "*useSchemes: none" resource, but it's not -- if that resource is
735 present in the `default_defaults' above, it doesn't work, though it
736 does work when passed as an -xrm arg on the command line. So screw it,
737 turn them off from C instead.
739 SgiUseSchemes ("none");
742 toplevel = XtAppInitialize (&app, progclass, merged_options,
743 merged_options_size, &argc, argv,
744 merged_defaults, 0, 0);
746 dpy = XtDisplay (toplevel);
748 XtGetApplicationNameAndClass (dpy,
750 (char **) &progclass);
752 /* half-assed way of avoiding buffer-overrun attacks. */
753 if (strlen (progname) >= 100) ((char *) progname)[100] = 0;
755 XSetErrorHandler (screenhack_ehandler);
757 XA_WM_PROTOCOLS = XInternAtom (dpy, "WM_PROTOCOLS", False);
758 XA_WM_DELETE_WINDOW = XInternAtom (dpy, "WM_DELETE_WINDOW", False);
761 char *v = (char *) strdup(strchr(screensaver_id, ' '));
762 char *s1, *s2, *s3, *s4;
763 const char *ot = get_string_resource (dpy, "title", "Title");
764 s1 = (char *) strchr(v, ' '); s1++;
765 s2 = (char *) strchr(s1, ' ');
766 s3 = (char *) strchr(v, '('); s3++;
767 s4 = (char *) strchr(s3, ')');
770 if (ot && !*ot) ot = 0;
771 sprintf (version, "%.50s%s%s: from the XScreenSaver %s distribution (%s)",
784 Bool help_p = (!strcmp(argv[1], "-help") ||
785 !strcmp(argv[1], "--help"));
786 fprintf (stderr, "%s\n", version);
787 for (s = progclass; *s; s++) fprintf(stderr, " ");
788 fprintf (stderr, " https://www.jwz.org/xscreensaver/\n\n");
791 fprintf(stderr, "Unrecognised option: %s\n", argv[1]);
792 fprintf (stderr, "Options include: ");
793 for (i = 0; i < merged_options_size; i++)
795 char *sw = merged_options [i].option;
796 Bool argp = (merged_options [i].argKind == XrmoptionSepArg);
797 int size = strlen (sw) + (argp ? 6 : 0) + 2;
800 fprintf (stderr, "\n\t\t ");
804 fprintf (stderr, "%s", sw);
805 if (argp) fprintf (stderr, " <arg>");
806 if (i != merged_options_size - 1) fprintf (stderr, ", ");
809 fprintf (stderr, ".\n");
814 fprintf (stderr, "\nResources:\n\n");
815 for (i = 0; i < merged_options_size; i++)
817 const char *opt = merged_options [i].option;
818 const char *res = merged_options [i].specifier + 1;
819 const char *val = merged_options [i].value;
820 char *s = get_string_resource (dpy, (char *) res, (char *) res);
825 while (L > 0 && (s[L-1] == ' ' || s[L-1] == '\t'))
829 fprintf (stderr, " %-16s %-18s ", opt, res);
830 if (merged_options [i].argKind == XrmoptionSepArg)
832 fprintf (stderr, "[%s]", (s ? s : "?"));
836 fprintf (stderr, "%s", (val ? val : "(null)"));
837 if (val && s && !strcasecmp (val, s))
838 fprintf (stderr, " [default]");
840 fprintf (stderr, "\n");
842 fprintf (stderr, "\n");
846 exit (help_p ? 0 : 1);
851 for (s = merged_defaults; *s; s++)
855 free (merged_options);
856 free (merged_defaults);
860 dont_clear = get_boolean_resource (dpy, "dontClearRoot", "Boolean");
861 mono_p = get_boolean_resource (dpy, "mono", "Boolean");
862 if (CellsOfScreen (DefaultScreenOfDisplay (dpy)) <= 2)
865 root_p = get_boolean_resource (dpy, "root", "Boolean");
868 char *s = get_string_resource (dpy, "windowID", "WindowID");
870 on_window = get_integer_resource (dpy, "windowID", "WindowID");
876 window = (Window) on_window;
877 XtDestroyWidget (toplevel);
878 XGetWindowAttributes (dpy, window, &xgwa);
879 visual_warning (xgwa.screen, window, xgwa.visual, xgwa.colormap, True);
881 /* Select KeyPress and resize events on the external window.
883 xgwa.your_event_mask |= KeyPressMask | StructureNotifyMask;
884 XSelectInput (dpy, window, xgwa.your_event_mask);
886 /* Select ButtonPress and ButtonRelease events on the external window,
887 if no other app has already selected them (only one app can select
888 ButtonPress at a time: BadAccess results.)
890 if (! (xgwa.all_event_masks & (ButtonPressMask | ButtonReleaseMask)))
891 XSelectInput (dpy, window,
892 (xgwa.your_event_mask |
893 ButtonPressMask | ButtonReleaseMask));
897 window = VirtualRootWindowOfScreen (XtScreen (toplevel));
898 XtDestroyWidget (toplevel);
899 XGetWindowAttributes (dpy, window, &xgwa);
900 /* With RANDR, the root window can resize! */
901 XSelectInput (dpy, window, xgwa.your_event_mask | StructureNotifyMask);
902 visual_warning (xgwa.screen, window, xgwa.visual, xgwa.colormap, False);
906 Widget new = make_shell (XtScreen (toplevel), toplevel,
907 toplevel->core.width,
908 toplevel->core.height);
911 XtDestroyWidget (toplevel);
915 init_window (dpy, toplevel, version);
916 window = XtWindow (toplevel);
917 XGetWindowAttributes (dpy, window, &xgwa);
920 if (get_boolean_resource (dpy, "pair", "Boolean"))
922 toplevel2 = make_shell (xgwa.screen, 0,
923 toplevel->core.width,
924 toplevel->core.height);
925 init_window (dpy, toplevel2, version);
926 window2 = XtWindow (toplevel2);
928 # endif /* DEBUG_PAIR */
933 unsigned int bg = get_pixel_resource (dpy, xgwa.colormap,
934 "background", "Background");
935 XSetWindowBackground (dpy, window, bg);
936 XClearWindow (dpy, window);
940 XSetWindowBackground (dpy, window2, bg);
941 XClearWindow (dpy, window2);
946 if (!root_p && !on_window)
947 /* wait for it to be mapped */
948 XIfEvent (dpy, &event, MapNotify_event_p, (XPointer) window);
952 /* This is the one and only place that the random-number generator is
953 seeded in any screenhack. You do not need to seed the RNG again,
954 it is done for you before your code is invoked. */
959 #ifdef HAVE_RECORD_ANIM
961 int frames = get_integer_resource (dpy, "recordAnim", "Integer");
963 anim_state = screenhack_record_anim_init (xgwa.screen, window, frames);
967 run_screenhack_table (dpy, window,
971 # ifdef HAVE_RECORD_ANIM
976 #ifdef HAVE_RECORD_ANIM
977 if (anim_state) screenhack_record_anim_free (anim_state);
980 XtDestroyWidget (toplevel);
981 XtDestroyApplicationContext (app);