ftp://ftp.linux.ncsu.edu/mirror/ftp.redhat.com/pub/redhat/linux/enterprise/4/en/os...
[xscreensaver] / hacks / screenhack.c
index 7c14734664f522227911b8c2f8d06ca788286f7d..728d18edabbb7b6b4ed25f0899faa8055b2a4a56 100644 (file)
@@ -1,4 +1,4 @@
-/* xscreensaver, Copyright (c) 1992, 1995, 1997, 1998
+/* xscreensaver, Copyright (c) 1992, 1995, 1997, 1998, 2001, 2002, 2003, 2004
  *  Jamie Zawinski <jwz@jwz.org>
  *
  * Permission to use, copy, modify, distribute, and sell this software and its
 #include "version.h"
 #include "vroot.h"
 
+#ifndef _XSCREENSAVER_VROOT_H_
+# error Error!  You have an old version of vroot.h!  Check -I args.
+#endif /* _XSCREENSAVER_VROOT_H_ */
+
+#ifndef isupper
+# define isupper(c)  ((c) >= 'A' && (c) <= 'Z')
+#endif
+#ifndef _tolower
+# define _tolower(c)  ((c) - 'A' + 'a')
+#endif
+
+
 char *progname;
 XrmDatabase db;
+XtAppContext app;
 Bool mono_p;
 
 static XrmOptionDescRec default_options [] = {
@@ -79,6 +92,7 @@ static char *default_defaults[] = {
   "*installColormap:   false",
   "*visualID:          default",
   "*windowID:          ",
+  "*desktopGrabber:    xscreensaver-getimage %s",
   0
 };
 
@@ -160,10 +174,6 @@ MapNotify_event_p (Display *dpy, XEvent *event, XPointer window)
 }
 
 
-#ifdef USE_GL
-extern Visual *get_gl_visual (Screen *, const char *, const char *);
-#endif
-
 #ifdef XLOCKMORE
 extern void pre_merge_options (void);
 #endif
@@ -177,6 +187,9 @@ static Atom XA_WM_PROTOCOLS, XA_WM_DELETE_WINDOW;
 void
 screenhack_handle_event (Display *dpy, XEvent *event)
 {
+  if (XtAppPending (app) & (XtIMTimer|XtIMAlternateInput))
+    XtAppProcessEvent (app, XtIMTimer|XtIMAlternateInput);
+
   switch (event->xany.type)
     {
     case KeyPress:
@@ -236,14 +249,139 @@ screenhack_handle_events (Display *dpy)
 }
 
 
+static Visual *
+pick_visual (Screen *screen)
+{
+#ifdef USE_GL
+  /* If we're linking against GL (that is, this is the version of screenhack.o
+     that the GL hacks will use, which is different from the one that the
+     non-GL hacks will use) then try to pick the "best" visual by interrogating
+     the GL library instead of by asking Xlib.  GL knows better.
+   */
+  Visual *v = 0;
+  char *string = get_string_resource ("visualID", "VisualID");
+  char *s;
+
+  if (string)
+    for (s = string; *s; s++)
+      if (isupper (*s)) *s = _tolower (*s);
+
+  if (!string || !*string ||
+      !strcmp (string, "gl") ||
+      !strcmp (string, "best") ||
+      !strcmp (string, "color") ||
+      !strcmp (string, "default"))
+    v = get_gl_visual (screen);                /* from ../utils/visual-gl.c */
+
+  if (string)
+    free (string);
+  if (v)
+    return v;
+#endif /* USE_GL */
+
+  return get_visual_resource (screen, "visualID", "VisualID", False);
+}
+
+
+/* Notice when the user has requested a different visual or colormap
+   on a pre-existing window (e.g., "-root -visual truecolor" or
+   "-window-id 0x2c00001 -install") and complain, since when drawing
+   on an existing window, we have no choice about these things.
+ */
+static void
+visual_warning (Screen *screen, Window window, Visual *visual, Colormap cmap,
+                Bool window_p)
+{
+  char *visual_string = get_string_resource ("visualID", "VisualID");
+  Visual *desired_visual = pick_visual (screen);
+  char win[100];
+  char why[100];
+
+  if (window == RootWindowOfScreen (screen))
+    strcpy (win, "root window");
+  else
+    sprintf (win, "window 0x%lx", (unsigned long) window);
+
+  if (window_p)
+    sprintf (why, "-window-id 0x%lx", (unsigned long) window);
+  else
+    strcpy (why, "-root");
+
+  if (visual_string && *visual_string)
+    {
+      char *s;
+      for (s = visual_string; *s; s++)
+        if (isupper (*s)) *s = _tolower (*s);
+
+      if (!strcmp (visual_string, "default") ||
+          !strcmp (visual_string, "default") ||
+          !strcmp (visual_string, "best"))
+        /* don't warn about these, just silently DWIM. */
+        ;
+      else if (visual != desired_visual)
+        {
+          fprintf (stderr, "%s: ignoring `-visual %s' because of `%s'.\n",
+                   progname, visual_string, why);
+          fprintf (stderr, "%s: using %s's visual 0x%lx.\n",
+                   progname, win, XVisualIDFromVisual (visual));
+        }
+      free (visual_string);
+    }
+
+  if (visual == DefaultVisualOfScreen (screen) &&
+      has_writable_cells (screen, visual) &&
+      get_boolean_resource ("installColormap", "InstallColormap"))
+    {
+      fprintf (stderr, "%s: ignoring `-install' because of `%s'.\n",
+               progname, why);
+      fprintf (stderr, "%s: using %s's colormap 0x%lx.\n",
+               progname, win, (unsigned long) cmap);
+    }
+
+# ifdef USE_GL
+  if (!validate_gl_visual (stderr, screen, win, visual))
+    exit (1);
+# endif /* USE_GL */
+}
+
+
+static void
+fix_fds (void)
+{
+  /* Bad Things Happen if stdin, stdout, and stderr have been closed
+     (as by the `sh incantation "attraction >&- 2>&-").  When you do
+     that, the X connection gets allocated to one of these fds, and
+     then some random library writes to stderr, and random bits get
+     stuffed down the X pipe, causing "Xlib: sequence lost" errors.
+     So, we cause the first three file descriptors to be open to
+     /dev/null if they aren't open to something else already.  This
+     must be done before any other files are opened (or the closing
+     of that other file will again free up one of the "magic" first
+     three FDs.)
+
+     We do this by opening /dev/null three times, and then closing
+     those fds, *unless* any of them got allocated as #0, #1, or #2,
+     in which case we leave them open.  Gag.
+
+     Really, this crap is technically required of *every* X program,
+     if you want it to be robust in the face of "2>&-".
+   */
+  int fd0 = open ("/dev/null", O_RDWR);
+  int fd1 = open ("/dev/null", O_RDWR);
+  int fd2 = open ("/dev/null", O_RDWR);
+  if (fd0 > 2) close (fd0);
+  if (fd1 > 2) close (fd1);
+  if (fd2 > 2) close (fd2);
+}
+
 
 int
 main (int argc, char **argv)
 {
-  XtAppContext app;
   Widget toplevel;
   Display *dpy;
   Window window;
+  Screen *screen;
   Visual *visual;
   Colormap cmap;
   Bool root_p;
@@ -252,6 +390,8 @@ main (int argc, char **argv)
   Boolean dont_clear /*, dont_map */;
   char version[255];
 
+  fix_fds();
+
 #ifdef XLOCKMORE
   pre_merge_options ();
 #endif
@@ -273,8 +413,14 @@ main (int argc, char **argv)
                              merged_options_size, &argc, argv,
                              merged_defaults, 0, 0);
   dpy = XtDisplay (toplevel);
+  screen = XtScreen (toplevel);
   db = XtDatabase (dpy);
+
   XtGetApplicationNameAndClass (dpy, &progname, &progclass);
+
+  /* half-assed way of avoiding buffer-overrun attacks. */
+  if (strlen (progname) >= 100) progname[100] = 0;
+
   XSetErrorHandler (screenhack_ehandler);
 
   XA_WM_PROTOCOLS = XInternAtom (dpy, "WM_PROTOCOLS", False);
@@ -300,7 +446,8 @@ main (int argc, char **argv)
       int i;
       int x = 18;
       int end = 78;
-      Bool help_p = !strcmp(argv[1], "-help");
+      Bool help_p = (!strcmp(argv[1], "-help") ||
+                     !strcmp(argv[1], "--help"));
       fprintf (stderr, "%s\n", version);
       for (s = progclass; *s; s++) fprintf(stderr, " ");
       fprintf (stderr, "  http://www.jwz.org/xscreensaver/\n\n");
@@ -323,7 +470,44 @@ main (int argc, char **argv)
          if (argp) fprintf (stderr, " <arg>");
          if (i != merged_options_size - 1) fprintf (stderr, ", ");
        }
+
       fprintf (stderr, ".\n");
+
+#if 0
+      if (help_p)
+        {
+          fprintf (stderr, "\nResources:\n\n");
+          for (i = 0; i < merged_options_size; i++)
+            {
+              const char *opt = merged_options [i].option;
+              const char *res = merged_options [i].specifier + 1;
+              const char *val = merged_options [i].value;
+              char *s = get_string_resource ((char *) res, (char *) res);
+
+              if (s)
+                {
+                  int L = strlen(s);
+                while (L > 0 && (s[L-1] == ' ' || s[L-1] == '\t'))
+                  s[--L] = 0;
+                }
+
+              fprintf (stderr, "    %-16s %-18s ", opt, res);
+              if (merged_options [i].argKind == XrmoptionSepArg)
+                {
+                  fprintf (stderr, "[%s]", (s ? s : "?"));
+                }
+              else
+                {
+                  fprintf (stderr, "%s", (val ? val : "(null)"));
+                  if (val && s && !strcasecmp (val, s))
+                    fprintf (stderr, " [default]");
+                }
+              fprintf (stderr, "\n");
+            }
+          fprintf (stderr, "\n");
+        }
+#endif
+
       exit (help_p ? 0 : 1);
     }
 
@@ -350,26 +534,42 @@ main (int argc, char **argv)
       XGetWindowAttributes (dpy, window, &xgwa);
       cmap = xgwa.colormap;
       visual = xgwa.visual;
+      screen = xgwa.screen;
+      visual_warning (screen, window, visual, cmap, True);
+
+      /* Select KeyPress and resize events on the external window.
+       */
+      xgwa.your_event_mask |= KeyPressMask | StructureNotifyMask;
+      XSelectInput (dpy, window, xgwa.your_event_mask);
+
+      /* Select ButtonPress and ButtonRelease events on the external window,
+         if no other app has already selected them (only one app can select
+         ButtonPress at a time: BadAccess results.)
+       */
+      if (! (xgwa.all_event_masks & (ButtonPressMask | ButtonReleaseMask)))
+        XSelectInput (dpy, window,
+                      (xgwa.your_event_mask |
+                       ButtonPressMask | ButtonReleaseMask));
     }
   else if (root_p)
     {
       XWindowAttributes xgwa;
-      window = RootWindowOfScreen (XtScreen (toplevel));
+      window = VirtualRootWindowOfScreen (XtScreen (toplevel));
       XtDestroyWidget (toplevel);
       XGetWindowAttributes (dpy, window, &xgwa);
       cmap = xgwa.colormap;
       visual = xgwa.visual;
+      visual_warning (screen, window, visual, cmap, False);
     }
   else
     {
       Boolean def_visual_p;
-      Screen *screen = XtScreen (toplevel);
+      visual = pick_visual (screen);
 
-#ifdef USE_GL
-      visual = get_gl_visual (screen, "visualID", "VisualID");
-#else
-      visual = get_visual_resource (screen, "visualID", "VisualID", False);
-#endif
+# ifdef USE_GL
+      if (!validate_gl_visual (stderr, screen, "window", visual))
+        exit (1);
+# endif /* USE_GL */
 
       if (toplevel->core.width <= 0)
        toplevel->core.width = 600;
@@ -383,7 +583,7 @@ main (int argc, char **argv)
          unsigned int bg, bd;
          Widget new;
 
-         cmap = XCreateColormap (dpy, RootWindowOfScreen(screen),
+         cmap = XCreateColormap (dpy, VirtualRootWindowOfScreen(screen),
                                  visual, AllocNone);
          bg = get_pixel_resource ("background", "Background", dpy, cmap);
          bd = get_pixel_resource ("borderColor", "Foreground", dpy, cmap);
@@ -398,7 +598,8 @@ main (int argc, char **argv)
                                    XtNcolormap, cmap,
                                    XtNbackground, (Pixel) bg,
                                    XtNborderColor, (Pixel) bd,
-                                   0);
+                                   XtNinput, True,  /* for WM_HINTS */
+                                   NULL);
          XtDestroyWidget (toplevel);
          toplevel = new;
          XtRealizeWidget (toplevel);
@@ -406,7 +607,10 @@ main (int argc, char **argv)
        }
       else
        {
-         XtVaSetValues (toplevel, XtNmappedWhenManaged, False, 0);
+         XtVaSetValues (toplevel,
+                         XtNmappedWhenManaged, False,
+                         XtNinput, True,  /* for WM_HINTS */
+                         NULL);
          XtRealizeWidget (toplevel);
          window = XtWindow (toplevel);
 
@@ -426,7 +630,7 @@ main (int argc, char **argv)
 /*
       if (dont_map)
        {
-         XtVaSetValues (toplevel, XtNmappedWhenManaged, False, 0);
+         XtVaSetValues (toplevel, XtNmappedWhenManaged, False, NULL);
          XtRealizeWidget (toplevel);
        }
       else
@@ -435,7 +639,7 @@ main (int argc, char **argv)
          XtPopup (toplevel, XtGrabNone);
        }
 
-      XtVaSetValues(toplevel, XtNtitle, version, 0);
+      XtVaSetValues(toplevel, XtNtitle, version, NULL);
 
       /* For screenhack_handle_events(): select KeyPress, and
          announce that we accept WM_DELETE_WINDOW. */
@@ -443,7 +647,8 @@ main (int argc, char **argv)
         XWindowAttributes xgwa;
         XGetWindowAttributes (dpy, window, &xgwa);
         XSelectInput (dpy, window,
-                      xgwa.your_event_mask | KeyPressMask | ButtonPressMask);
+                      (xgwa.your_event_mask | KeyPressMask |
+                       ButtonPressMask | ButtonReleaseMask));
         XChangeProperty (dpy, window, XA_WM_PROTOCOLS, XA_ATOM, 32,
                          PropModeReplace,
                          (unsigned char *) &XA_WM_DELETE_WINDOW, 1);
@@ -463,7 +668,13 @@ main (int argc, char **argv)
     XIfEvent (dpy, &event, MapNotify_event_p, (XPointer) window);
 
   XSync (dpy, False);
-  srandom ((int) time ((time_t *) 0));
+
+  /* This is the one and only place that the random-number generator is
+     seeded in any screenhack.  You do not need to seed the RNG again,
+     it is done for you before your code is invoked. */
+# undef ya_rand_init
+  ya_rand_init (0);
+
   screenhack (dpy, window); /* doesn't return */
   return 0;
 }