1 /* xscreensaver, Copyright (c) 1992-2008 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"
109 #ifndef _XSCREENSAVER_VROOT_H_
110 # error Error! You have an old version of vroot.h! Check -I args.
111 #endif /* _XSCREENSAVER_VROOT_H_ */
114 # define isupper(c) ((c) >= 'A' && (c) <= 'Z')
117 # define _tolower(c) ((c) - 'A' + 'a')
121 /* This is defined by the SCREENHACK_MAIN() macro via screenhack.h.
123 extern struct xscreensaver_function_table *xscreensaver_function_table;
126 const char *progname; /* used by hacks in error messages */
127 const char *progclass; /* used by ../utils/resources.c */
128 Bool mono_p; /* used by hacks */
131 static XrmOptionDescRec default_options [] = {
132 { "-root", ".root", XrmoptionNoArg, "True" },
133 { "-window", ".root", XrmoptionNoArg, "False" },
134 { "-mono", ".mono", XrmoptionNoArg, "True" },
135 { "-install", ".installColormap", XrmoptionNoArg, "True" },
136 { "-noinstall",".installColormap", XrmoptionNoArg, "False" },
137 { "-visual", ".visualID", XrmoptionSepArg, 0 },
138 { "-window-id", ".windowID", XrmoptionSepArg, 0 },
141 { "-pair", ".pair", XrmoptionNoArg, "True" },
142 # endif /* DEBUG_PAIR */
146 static char *default_defaults[] = {
148 "*geometry: 600x480", /* this should be .geometry, but nooooo... */
150 "*installColormap: false",
151 "*visualID: default",
153 "*desktopGrabber: xscreensaver-getimage %s",
157 static XrmOptionDescRec *merged_options;
158 static int merged_options_size;
159 static char **merged_defaults;
164 struct xscreensaver_function_table *ft = xscreensaver_function_table;
166 const XrmOptionDescRec *options = ft->options;
167 const char * const *defaults = ft->defaults;
168 const char *progclass = ft->progclass;
170 int def_opts_size, opts_size;
171 int def_defaults_size, defaults_size;
173 for (def_opts_size = 0; default_options[def_opts_size].option;
176 for (opts_size = 0; options[opts_size].option; opts_size++)
179 merged_options_size = def_opts_size + opts_size;
180 merged_options = (XrmOptionDescRec *)
181 malloc ((merged_options_size + 1) * sizeof(*default_options));
182 memcpy (merged_options, default_options,
183 (def_opts_size * sizeof(*default_options)));
184 memcpy (merged_options + def_opts_size, options,
185 ((opts_size + 1) * sizeof(*default_options)));
187 for (def_defaults_size = 0; default_defaults[def_defaults_size];
190 for (defaults_size = 0; defaults[defaults_size]; defaults_size++)
192 merged_defaults = (char **)
193 malloc ((def_defaults_size + defaults_size + 1) * sizeof (*defaults));;
194 memcpy (merged_defaults, default_defaults,
195 def_defaults_size * sizeof(*defaults));
196 memcpy (merged_defaults + def_defaults_size, defaults,
197 (defaults_size + 1) * sizeof(*defaults));
199 /* This totally sucks. Xt should behave like this by default.
200 If the string in `defaults' looks like ".foo", change that
205 for (s = merged_defaults; *s; s++)
208 const char *oldr = *s;
209 char *newr = (char *) malloc(strlen(oldr) + strlen(progclass) + 3);
210 strcpy (newr, progclass);
218 /* Make the X errors print out the name of this program, so we have some
219 clue which one has a bug when they die under the screensaver.
223 screenhack_ehandler (Display *dpy, XErrorEvent *error)
225 fprintf (stderr, "\nX error in %s:\n", progname);
226 if (XmuPrintDefaultErrorMessage (dpy, error, stderr))
229 fprintf (stderr, " (nonfatal.)\n");
234 MapNotify_event_p (Display *dpy, XEvent *event, XPointer window)
236 return (event->xany.type == MapNotify &&
237 event->xvisibility.window == (Window) window);
241 static Atom XA_WM_PROTOCOLS, XA_WM_DELETE_WINDOW;
243 /* Dead-trivial event handling: exits if "q" or "ESC" are typed.
244 Exit if the WM_PROTOCOLS WM_DELETE_WINDOW ClientMessage is received.
245 Returns False if the screen saver should now terminate.
248 screenhack_handle_event_1 (Display *dpy, XEvent *event)
250 switch (event->xany.type)
256 XLookupString (&event->xkey, &c, 1, &keysym, 0);
261 return False; /* exit */
262 else if (! (keysym >= XK_Shift_L && keysym <= XK_Hyper_R))
263 XBell (dpy, 0); /* beep for non-chord keys */
271 if (event->xclient.message_type != XA_WM_PROTOCOLS)
273 char *s = XGetAtomName(dpy, event->xclient.message_type);
274 if (!s) s = "(null)";
275 fprintf (stderr, "%s: unknown ClientMessage %s received!\n",
278 else if (event->xclient.data.l[0] != XA_WM_DELETE_WINDOW)
280 char *s1 = XGetAtomName(dpy, event->xclient.message_type);
281 char *s2 = XGetAtomName(dpy, event->xclient.data.l[0]);
282 if (!s1) s1 = "(null)";
283 if (!s2) s2 = "(null)";
284 fprintf (stderr, "%s: unknown ClientMessage %s[%s] received!\n",
289 return False; /* exit */
299 pick_visual (Screen *screen)
301 struct xscreensaver_function_table *ft = xscreensaver_function_table;
303 if (ft->pick_visual_hook)
305 Visual *v = ft->pick_visual_hook (screen);
309 return get_visual_resource (screen, "visualID", "VisualID", False);
313 /* Notice when the user has requested a different visual or colormap
314 on a pre-existing window (e.g., "-root -visual truecolor" or
315 "-window-id 0x2c00001 -install") and complain, since when drawing
316 on an existing window, we have no choice about these things.
319 visual_warning (Screen *screen, Window window, Visual *visual, Colormap cmap,
322 struct xscreensaver_function_table *ft = xscreensaver_function_table;
324 char *visual_string = get_string_resource (DisplayOfScreen (screen),
325 "visualID", "VisualID");
326 Visual *desired_visual = pick_visual (screen);
330 if (window == RootWindowOfScreen (screen))
331 strcpy (win, "root window");
333 sprintf (win, "window 0x%lx", (unsigned long) window);
336 sprintf (why, "-window-id 0x%lx", (unsigned long) window);
338 strcpy (why, "-root");
340 if (visual_string && *visual_string)
343 for (s = visual_string; *s; s++)
344 if (isupper (*s)) *s = _tolower (*s);
346 if (!strcmp (visual_string, "default") ||
347 !strcmp (visual_string, "default") ||
348 !strcmp (visual_string, "best"))
349 /* don't warn about these, just silently DWIM. */
351 else if (visual != desired_visual)
353 fprintf (stderr, "%s: ignoring `-visual %s' because of `%s'.\n",
354 progname, visual_string, why);
355 fprintf (stderr, "%s: using %s's visual 0x%lx.\n",
356 progname, win, XVisualIDFromVisual (visual));
358 free (visual_string);
361 if (visual == DefaultVisualOfScreen (screen) &&
362 has_writable_cells (screen, visual) &&
363 get_boolean_resource (DisplayOfScreen (screen),
364 "installColormap", "InstallColormap"))
366 fprintf (stderr, "%s: ignoring `-install' because of `%s'.\n",
368 fprintf (stderr, "%s: using %s's colormap 0x%lx.\n",
369 progname, win, (unsigned long) cmap);
372 if (ft->validate_visual_hook)
374 if (! ft->validate_visual_hook (screen, win, visual))
383 /* Bad Things Happen if stdin, stdout, and stderr have been closed
384 (as by the `sh incantation "attraction >&- 2>&-"). When you do
385 that, the X connection gets allocated to one of these fds, and
386 then some random library writes to stderr, and random bits get
387 stuffed down the X pipe, causing "Xlib: sequence lost" errors.
388 So, we cause the first three file descriptors to be open to
389 /dev/null if they aren't open to something else already. This
390 must be done before any other files are opened (or the closing
391 of that other file will again free up one of the "magic" first
394 We do this by opening /dev/null three times, and then closing
395 those fds, *unless* any of them got allocated as #0, #1, or #2,
396 in which case we leave them open. Gag.
398 Really, this crap is technically required of *every* X program,
399 if you want it to be robust in the face of "2>&-".
401 int fd0 = open ("/dev/null", O_RDWR);
402 int fd1 = open ("/dev/null", O_RDWR);
403 int fd2 = open ("/dev/null", O_RDWR);
404 if (fd0 > 2) close (fd0);
405 if (fd1 > 2) close (fd1);
406 if (fd2 > 2) close (fd2);
413 check_timing (unsigned long delay)
415 static unsigned long frame_count = 0;
416 static unsigned long delay_sum = 0;
417 static struct timeval prev1 = { 0, };
418 static struct timeval prev2 = { 0, };
420 double uprev1, uprev2, unow;
422 # ifdef GETTIMEOFDAY_TWO_ARGS
423 gettimeofday (&now, 0);
428 if (prev1.tv_sec == 0)
433 uprev1 = prev1.tv_sec + ((double) prev1.tv_usec * 0.000001);
434 uprev2 = prev2.tv_sec + ((double) prev2.tv_usec * 0.000001);
435 unow = now.tv_sec + ((double) now.tv_usec * 0.000001);
437 if (unow >= uprev1 + 1.5)
439 "%s: warning: blocked event processing for %.1f secs!\n",
440 progname, unow - uprev1);
443 if (unow >= uprev2 + 10.0)
445 double fps = frame_count / (unow - uprev2);
446 double elapsed = unow - uprev2;
447 double slept = (delay_sum * 0.000001);
448 double sleep_ratio = slept / elapsed;
450 if (sleep_ratio < 0.10) {
452 "%s: warning: only %.0f%% idle over the"
453 " last %2.0f secs (at %.1f FPS)\n",
454 progname, 100 * sleep_ratio, elapsed, fps);
465 #else /* !DEBUG_TIMING */
466 # define check_timing(delay) /**/
467 #endif /* !DEBUG_TIMING */
470 screenhack_table_handle_events (Display *dpy,
471 const struct xscreensaver_function_table *ft,
472 Window window, void *closure
474 , Window window2, void *closure2
478 XtAppContext app = XtDisplayToApplicationContext (dpy);
480 if (XtAppPending (app) & (XtIMTimer|XtIMAlternateInput))
481 XtAppProcessEvent (app, XtIMTimer|XtIMAlternateInput);
483 while (XPending (dpy))
486 XNextEvent (dpy, &event);
488 if (event.xany.type == ConfigureNotify)
490 if (event.xany.window == window)
491 ft->reshape_cb (dpy, window, closure,
492 event.xconfigure.width, event.xconfigure.height);
494 if (event.xany.window == window2)
495 ft->reshape_cb (dpy, window2, closure2,
496 event.xconfigure.width, event.xconfigure.height);
499 else if (event.xany.type == ClientMessage ||
500 (! (event.xany.window == window
501 ? ft->event_cb (dpy, window, closure, &event)
503 : event.xany.window == window2
504 ? ft->event_cb (dpy, window2, closure2, &event)
507 if (! screenhack_handle_event_1 (dpy, &event))
510 if (XtAppPending (app) & (XtIMTimer|XtIMAlternateInput))
511 XtAppProcessEvent (app, XtIMTimer|XtIMAlternateInput);
518 usleep_and_process_events (Display *dpy,
519 const struct xscreensaver_function_table *ft,
520 Window window, void *closure, unsigned long delay
522 , Window window2, void *closure2, unsigned long delay2
527 unsigned long quantum = 100000; /* 1/10th second */
536 check_timing (quantum);
538 /* The above isn't quite right in pair-mode: we always run both windows
539 with the timing of window 2. But, it's just a debugging hack, so
540 that doesn't really matter that much... */
542 if (! screenhack_table_handle_events (dpy, ft, window, closure
555 run_screenhack_table (Display *dpy,
560 const struct xscreensaver_function_table *ft)
563 /* Kludge: even though the init_cb functions are declared to take 2 args,
564 actually call them with 3, for the benefit of xlockmore_init() and
567 void *(*init_cb) (Display *, Window, void *) =
568 (void *(*) (Display *, Window, void *)) ft->init_cb;
570 void *closure = init_cb (dpy, window, ft->setup_arg);
574 if (window2) closure2 = init_cb (dpy, window2, ft->setup_arg);
577 if (! closure) /* if it returns nothing, it can't possibly be re-entrant. */
582 unsigned long delay = ft->draw_cb (dpy, window, closure);
584 unsigned long delay2 = 0;
585 if (window2) delay2 = ft->draw_cb (dpy, window2, closure2);
588 if (! usleep_and_process_events (dpy, ft,
589 window, closure, delay
591 , window2, closure2, delay2
597 ft->free_cb (dpy, window, closure);
601 ft->free_cb (dpy, window2, closure2);
607 make_shell (Screen *screen, Widget toplevel, int width, int height)
609 Display *dpy = DisplayOfScreen (screen);
610 Visual *visual = pick_visual (screen);
611 Boolean def_visual_p = (toplevel &&
612 visual == DefaultVisualOfScreen (screen));
614 if (width <= 0) width = 600;
615 if (height <= 0) height = 480;
618 if (!validate_gl_visual (stderr, screen, "window", visual))
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;
710 Window on_window = 0;
717 progname = argv[0]; /* reset later */
718 progclass = ft->progclass;
721 ft->setup_cb (ft, ft->setup_arg);
726 /* We have to do this on SGI to prevent the background color from being
727 overridden by the current desktop color scheme (we'd like our backgrounds
728 to be black, thanks.) This should be the same as setting the
729 "*useSchemes: none" resource, but it's not -- if that resource is
730 present in the `default_defaults' above, it doesn't work, though it
731 does work when passed as an -xrm arg on the command line. So screw it,
732 turn them off from C instead.
734 SgiUseSchemes ("none");
737 toplevel = XtAppInitialize (&app, progclass, merged_options,
738 merged_options_size, &argc, argv,
739 merged_defaults, 0, 0);
741 dpy = XtDisplay (toplevel);
743 XtGetApplicationNameAndClass (dpy,
745 (char **) &progclass);
747 /* half-assed way of avoiding buffer-overrun attacks. */
748 if (strlen (progname) >= 100) ((char *) progname)[100] = 0;
750 XSetErrorHandler (screenhack_ehandler);
752 XA_WM_PROTOCOLS = XInternAtom (dpy, "WM_PROTOCOLS", False);
753 XA_WM_DELETE_WINDOW = XInternAtom (dpy, "WM_DELETE_WINDOW", False);
756 char *v = (char *) strdup(strchr(screensaver_id, ' '));
757 char *s1, *s2, *s3, *s4;
758 s1 = (char *) strchr(v, ' '); s1++;
759 s2 = (char *) strchr(s1, ' ');
760 s3 = (char *) strchr(v, '('); s3++;
761 s4 = (char *) strchr(s3, ')');
764 sprintf (version, "%s: from the XScreenSaver %s distribution (%s.)",
775 Bool help_p = (!strcmp(argv[1], "-help") ||
776 !strcmp(argv[1], "--help"));
777 fprintf (stderr, "%s\n", version);
778 for (s = progclass; *s; s++) fprintf(stderr, " ");
779 fprintf (stderr, " http://www.jwz.org/xscreensaver/\n\n");
782 fprintf(stderr, "Unrecognised option: %s\n", argv[1]);
783 fprintf (stderr, "Options include: ");
784 for (i = 0; i < merged_options_size; i++)
786 char *sw = merged_options [i].option;
787 Bool argp = (merged_options [i].argKind == XrmoptionSepArg);
788 int size = strlen (sw) + (argp ? 6 : 0) + 2;
791 fprintf (stderr, "\n\t\t ");
795 fprintf (stderr, "%s", sw);
796 if (argp) fprintf (stderr, " <arg>");
797 if (i != merged_options_size - 1) fprintf (stderr, ", ");
800 fprintf (stderr, ".\n");
805 fprintf (stderr, "\nResources:\n\n");
806 for (i = 0; i < merged_options_size; i++)
808 const char *opt = merged_options [i].option;
809 const char *res = merged_options [i].specifier + 1;
810 const char *val = merged_options [i].value;
811 char *s = get_string_resource (dpy, (char *) res, (char *) res);
816 while (L > 0 && (s[L-1] == ' ' || s[L-1] == '\t'))
820 fprintf (stderr, " %-16s %-18s ", opt, res);
821 if (merged_options [i].argKind == XrmoptionSepArg)
823 fprintf (stderr, "[%s]", (s ? s : "?"));
827 fprintf (stderr, "%s", (val ? val : "(null)"));
828 if (val && s && !strcasecmp (val, s))
829 fprintf (stderr, " [default]");
831 fprintf (stderr, "\n");
833 fprintf (stderr, "\n");
837 exit (help_p ? 0 : 1);
840 free (merged_options);
841 free (merged_defaults);
845 dont_clear = get_boolean_resource (dpy, "dontClearRoot", "Boolean");
846 mono_p = get_boolean_resource (dpy, "mono", "Boolean");
847 if (CellsOfScreen (DefaultScreenOfDisplay (dpy)) <= 2)
850 root_p = get_boolean_resource (dpy, "root", "Boolean");
853 char *s = get_string_resource (dpy, "windowID", "WindowID");
855 on_window = get_integer_resource (dpy, "windowID", "WindowID");
861 window = (Window) on_window;
862 XtDestroyWidget (toplevel);
863 XGetWindowAttributes (dpy, window, &xgwa);
864 visual_warning (xgwa.screen, window, xgwa.visual, xgwa.colormap, True);
866 /* Select KeyPress and resize events on the external window.
868 xgwa.your_event_mask |= KeyPressMask | StructureNotifyMask;
869 XSelectInput (dpy, window, xgwa.your_event_mask);
871 /* Select ButtonPress and ButtonRelease events on the external window,
872 if no other app has already selected them (only one app can select
873 ButtonPress at a time: BadAccess results.)
875 if (! (xgwa.all_event_masks & (ButtonPressMask | ButtonReleaseMask)))
876 XSelectInput (dpy, window,
877 (xgwa.your_event_mask |
878 ButtonPressMask | ButtonReleaseMask));
882 window = VirtualRootWindowOfScreen (XtScreen (toplevel));
883 XtDestroyWidget (toplevel);
884 XGetWindowAttributes (dpy, window, &xgwa);
885 /* With RANDR, the root window can resize! */
886 XSelectInput (dpy, window, xgwa.your_event_mask | StructureNotifyMask);
887 visual_warning (xgwa.screen, window, xgwa.visual, xgwa.colormap, False);
891 Widget new = make_shell (XtScreen (toplevel), toplevel,
892 toplevel->core.width,
893 toplevel->core.height);
896 XtDestroyWidget (toplevel);
900 init_window (dpy, toplevel, version);
901 window = XtWindow (toplevel);
902 XGetWindowAttributes (dpy, window, &xgwa);
905 if (get_boolean_resource (dpy, "pair", "Boolean"))
907 toplevel2 = make_shell (xgwa.screen, 0,
908 toplevel->core.width,
909 toplevel->core.height);
910 init_window (dpy, toplevel2, version);
911 window2 = XtWindow (toplevel2);
913 # endif /* DEBUG_PAIR */
918 unsigned int bg = get_pixel_resource (dpy, xgwa.colormap,
919 "background", "Background");
920 XSetWindowBackground (dpy, window, bg);
921 XClearWindow (dpy, window);
925 XSetWindowBackground (dpy, window2, bg);
926 XClearWindow (dpy, window2);
931 if (!root_p && !on_window)
932 /* wait for it to be mapped */
933 XIfEvent (dpy, &event, MapNotify_event_p, (XPointer) window);
937 /* This is the one and only place that the random-number generator is
938 seeded in any screenhack. You do not need to seed the RNG again,
939 it is done for you before your code is invoked. */
943 run_screenhack_table (dpy, window,
949 XtDestroyWidget (toplevel);
950 XtDestroyApplicationContext (app);