http://www.tienza.es/crux/src/www.jwz.org/xscreensaver/xscreensaver-5.05.tar.gz
[xscreensaver] / driver / xscreensaver.c
index 1badde0719764cd87c84ecfeac50c88d67363689..92b28625c1a6337be6cdfbc178832116194e5861 100644 (file)
@@ -1,4 +1,4 @@
-/* xscreensaver, Copyright (c) 1991-2003 Jamie Zawinski <jwz@jwz.org>
+/* xscreensaver, Copyright (c) 1991-2008 Jamie Zawinski <jwz@jwz.org>
  *
  * Permission to use, copy, modify, distribute, and sell this software and its
  * documentation for any purpose is hereby granted without fee, provided that
 #include <time.h>
 #include <sys/time.h>
 #include <netdb.h>     /* for gethostbyname() */
+#include <sys/types.h>
+#include <pwd.h>
 #ifdef HAVE_XMU
 # ifndef VMS
 #  include <X11/Xmu/Error.h>
 #include "resources.h"
 #include "visual.h"
 #include "usleep.h"
+#include "auth.h"
 
 saver_info *global_si_kludge = 0;      /* I hate C so much... */
 
@@ -202,36 +205,6 @@ static XrmOptionDescRec options [] = {
 
   /* useful for debugging */
   { "-no-capture-stderr",  ".captureStderr",   XrmoptionNoArg, "off" },
-
-  /* There's really no reason to have these command-line args; they just
-     lead to confusion when the .xscreensaver file has conflicting values.
-   */
-#if 0
-  { "-splash",            ".splash",           XrmoptionNoArg, "on" },
-  { "-capture-stderr",    ".captureStderr",    XrmoptionNoArg, "on" },
-  { "-timeout",                   ".timeout",          XrmoptionSepArg, 0 },
-  { "-cycle",             ".cycle",            XrmoptionSepArg, 0 },
-  { "-lock-mode",         ".lock",             XrmoptionNoArg, "on" },
-  { "-no-lock-mode",      ".lock",             XrmoptionNoArg, "off" },
-  { "-no-lock",                   ".lock",             XrmoptionNoArg, "off" },
-  { "-lock-timeout",      ".lockTimeout",      XrmoptionSepArg, 0 },
-  { "-lock-vts",          ".lockVTs",          XrmoptionNoArg, "on" },
-  { "-no-lock-vts",       ".lockVTs",          XrmoptionNoArg, "off" },
-  { "-visual",            ".visualID",         XrmoptionSepArg, 0 },
-  { "-install",                   ".installColormap",  XrmoptionNoArg, "on" },
-  { "-no-install",        ".installColormap",  XrmoptionNoArg, "off" },
-  { "-timestamp",         ".timestamp",        XrmoptionNoArg, "on" },
-  { "-xidle-extension",           ".xidleExtension",   XrmoptionNoArg, "on" },
-  { "-no-xidle-extension", ".xidleExtension",  XrmoptionNoArg, "off" },
-  { "-mit-extension",     ".mitSaverExtension",XrmoptionNoArg, "on" },
-  { "-no-mit-extension",   ".mitSaverExtension",XrmoptionNoArg, "off" },
-  { "-sgi-extension",     ".sgiSaverExtension",XrmoptionNoArg, "on" },
-  { "-no-sgi-extension",   ".sgiSaverExtension",XrmoptionNoArg, "off" },
-  { "-proc-interrupts",           ".procInterrupts",   XrmoptionNoArg, "on" },
-  { "-no-proc-interrupts", ".procInterrupts",  XrmoptionNoArg, "off" },
-  { "-idelay",            ".initialDelay",     XrmoptionSepArg, 0 },
-  { "-nice",              ".nice",             XrmoptionSepArg, 0 },
-#endif /* 0 */
 };
 
 #ifdef __GNUC__
@@ -255,7 +228,7 @@ do_help (saver_info *si)
   fflush (stdout);
   fflush (stderr);
   fprintf (stdout, "\
-xscreensaver %s, copyright (c) 1991-2004 by Jamie Zawinski <jwz@jwz.org>\n\
+xscreensaver %s, copyright (c) 1991-2008 by Jamie Zawinski <jwz@jwz.org>\n\
 \n\
   All xscreensaver configuration is via the `~/.xscreensaver' file.\n\
   Rather than editing that file by hand, just run `xscreensaver-demo':\n\
@@ -712,14 +685,15 @@ print_banner (saver_info *si)
      whether to print the banner (and so that the banner gets printed before
      any resource-database-related error messages.)
    */
-  p->verbose_p = (p->debug_p || get_boolean_resource ("verbose", "Boolean"));
+  p->verbose_p = (p->debug_p || 
+                  get_boolean_resource (si->dpy, "verbose", "Boolean"));
 
   /* Ditto, for the locking_disabled_p message. */
-  p->lock_p = get_boolean_resource ("lock", "Boolean");
+  p->lock_p = get_boolean_resource (si->dpy, "lock", "Boolean");
 
   if (p->verbose_p)
     fprintf (stderr,
-            "%s %s, copyright (c) 1991-2004 "
+            "%s %s, copyright (c) 1991-2008 "
             "by Jamie Zawinski <jwz@jwz.org>.\n",
             progname, si->version);
 
@@ -781,6 +755,111 @@ print_lock_failure_banner (saver_info *si)
 }
 
 
+#ifdef HAVE_XINERAMA
+
+static Bool
+screens_overlap_p (XineramaScreenInfo *a, XineramaScreenInfo *b)
+{
+  /* Two rectangles overlap if the max of the tops is less than the
+     min of the bottoms and the max of the lefts is less than the min
+     of the rights.
+   */
+# undef MAX
+# undef MIN
+# define MAX(A,B) ((A)>(B)?(A):(B))
+# define MIN(A,B) ((A)<(B)?(A):(B))
+
+  int maxleft  = MAX(a->x_org, b->x_org);
+  int maxtop   = MAX(a->y_org, b->y_org);
+  int minright = MIN(a->x_org + a->width  - 1, b->x_org + b->width);
+  int minbot   = MIN(a->y_org + a->height - 1, b->y_org + b->height);
+  return (maxtop < minbot && maxleft < minright);
+}
+
+
+/* Go through the list of Xinerama screen descriptions, and mark the
+   ones that appear to be insane, so that we don't use them.
+ */
+static void
+check_xinerama_sanity (int count, Bool verbose_p, XineramaScreenInfo *xsi)
+{
+  static Bool printed_p = False;
+  int i, j;
+  char err[1024];
+  *err = 0;
+
+# define X1 xsi[i].x_org
+# define X2 xsi[j].x_org
+# define Y1 xsi[i].y_org
+# define Y2 xsi[j].y_org
+# define W1 xsi[i].width
+# define W2 xsi[j].width
+# define H1 xsi[i].height
+# define H2 xsi[j].height
+
+# define WHINE() do {                                                        \
+    if (verbose_p) {                                                         \
+      if (! printed_p) {                                                     \
+        fprintf (stderr, "%s: compensating for Xinerama braindamage:\n",     \
+                 blurb());                                                   \
+        printed_p = True;                                                    \
+      }                                                                      \
+      fprintf (stderr, "%s:   %d: %s\n", blurb(), xsi[i].screen_number,err); \
+    }                                                                        \
+    xsi[i].screen_number = -1;                                               \
+  } while(0)
+
+  /* If a screen is enclosed by any other screen, that's insane.
+   */
+  for (i = 0; i < count; i++)
+    for (j = 0; j < count; j++)
+      if (i != j &&
+          xsi[i].screen_number >= 0 &&
+          xsi[j].screen_number >= 0 &&
+          X1 >= X2 && Y1 >= Y2 && (X1+W1) <= (X2+W2) && (X1+H1) <= (X2+H2))
+        {
+          sprintf (err, "%dx%d+%d+%d enclosed by %dx%d+%d+%d",
+                   W1, H1, X1, Y1,
+                   W2, H2, X2, Y2);
+          WHINE();
+          continue;
+        }
+
+  /* After checking for enclosure, check for other lossage against earlier
+     screens.  We do enclosure first so that we make sure to pick the
+     larger one.
+   */
+  for (i = 0; i < count; i++)
+    for (j = 0; j < i; j++)
+      {
+        if (xsi[i].screen_number < 0) continue; /* already marked */
+
+        *err = 0;
+        if (X1 == X2 && Y1 == Y2 && W1 == W2 && H1 == H2)
+          sprintf (err, "%dx%d+%d+%d duplicated", W1, H1, X1, Y1);
+
+        else if (screens_overlap_p (&xsi[i], &xsi[j]))
+          sprintf (err, "%dx%d+%d+%d overlaps %dx%d+%d+%d",
+                   W1, H1, X1, Y1,
+                   W2, H2, X2, Y2);
+
+        if (*err) WHINE();
+      }
+
+# undef X1
+# undef X2
+# undef Y1
+# undef Y2
+# undef W1
+# undef W2
+# undef H1
+# undef H2
+}
+
+#endif /* HAVE_XINERAMA */
+
+
+
 /* Examine all of the display's screens, and populate the `saver_screen_info'
    structures.  Make sure this is called after hack_environment() sets $PATH.
  */
@@ -808,20 +887,27 @@ initialize_per_screen_info (saver_info *si, Widget toplevel_shell)
 
   if (si->xinerama_p)
     {
-      XineramaScreenInfo *xsi = XineramaQueryScreens (si->dpy, &si->nscreens);
+      int nscreens = 0;
+      XineramaScreenInfo *xsi = XineramaQueryScreens (si->dpy, &nscreens);
       if (!xsi)
         si->xinerama_p = False;
       else
         {
+          int j = 0;
           si->screens = (saver_screen_info *)
-            calloc(sizeof(saver_screen_info), si->nscreens);
-          for (i = 0; i < si->nscreens; i++)
+            calloc(sizeof(saver_screen_info), nscreens);
+          check_xinerama_sanity (nscreens, si->prefs.verbose_p, xsi);
+          for (i = 0; i < nscreens; i++)
             {
-              si->screens[i].x      = xsi[i].x_org;
-              si->screens[i].y      = xsi[i].y_org;
-              si->screens[i].width  = xsi[i].width;
-              si->screens[i].height = xsi[i].height;
+              if (xsi[i].screen_number < 0)  /* deemed insane */
+                continue;
+              si->screens[j].x      = xsi[i].x_org;
+              si->screens[j].y      = xsi[i].y_org;
+              si->screens[j].width  = xsi[i].width;
+              si->screens[j].height = xsi[i].height;
+              j++;
             }
+          si->nscreens = j;
           XFree (xsi);
         }
       si->default_screen = &si->screens[0];
@@ -847,6 +933,7 @@ initialize_per_screen_info (saver_info *si, Widget toplevel_shell)
     }
 
 
+# ifdef QUAD_MODE
   /* In "quad mode", we use the Xinerama code to pretend that there are 4
      screens for every physical screen, and run four times as many hacks...
    */
@@ -894,6 +981,7 @@ initialize_per_screen_info (saver_info *si, Widget toplevel_shell)
       si->default_screen = &si->screens[DefaultScreen(si->dpy) * 4];
       si->xinerama_p = True;
     }
+# endif /* QUAD_MODE */
 
   /* finish initializing the screens.
    */
@@ -904,6 +992,8 @@ initialize_per_screen_info (saver_info *si, Widget toplevel_shell)
 
       ssi->number = i;
       ssi->screen = ScreenOfDisplay (si->dpy, ssi->real_screen_number);
+      ssi->poll_mouse_last_root_x = -1;
+      ssi->poll_mouse_last_root_y = -1;
 
       if (!si->xinerama_p)
         {
@@ -1124,7 +1214,7 @@ maybe_reload_init_file (saver_info *si)
        fprintf (stderr, "%s: file \"%s\" has changed, reloading.\n",
                 blurb(), init_file_name());
 
-      load_init_file (p);
+      load_init_file (si->dpy, p);
 
       /* If a server extension is in use, and p->timeout has changed,
         we need to inform the server of the new timeout. */
@@ -1190,8 +1280,13 @@ main_loop (saver_info *si)
           /* Go around the loop and wait for the next bout of idleness,
              or for the init file to change, or for a remote command to
              come in, or something.
+
+             But, if locked_p is true, go ahead.  This can only happen
+             if we're in "disabled" mode but a "lock" clientmessage came
+             in: in that case, we should go ahead and blank/lock the screen.
            */
-          continue;
+          if (!si->locked_p)
+            continue;
         }
 
       /* Since we're about to blank the screen, kill the de-race timer,
@@ -1403,6 +1498,7 @@ main (int argc, char **argv)
   saver_info the_si;
   saver_info *si = &the_si;
   saver_preferences *p = &si->prefs;
+  struct passwd *spasswd;
   int i;
 
   memset(si, 0, sizeof(*si));
@@ -1418,11 +1514,25 @@ main (int argc, char **argv)
   privileged_initialization (si, &argc, argv);
   hack_environment (si);
 
+  spasswd = getpwuid(getuid());
+  if (!spasswd)
+    {
+      fprintf(stderr, "Could not figure out who the current user is!\n");
+      return 1;
+    }
+
+  si->user = strdup(spasswd->pw_name ? spasswd->pw_name : "(unknown)");
+
+# ifndef NO_LOCKING
+  si->unlock_cb = gui_auth_conv;
+  si->auth_finished_cb = auth_finished_cb;
+# endif /* !NO_LOCKING */
+
   shell = connect_to_server (si, &argc, argv);
   process_command_line (si, &argc, argv);
   print_banner (si);
 
-  load_init_file (p);  /* must be before initialize_per_screen_info() */
+  load_init_file(si->dpy, p); /* must be before initialize_per_screen_info() */
   blurb_timestamp_p = p->timestamp_p;  /* kludge */
   initialize_per_screen_info (si, shell); /* also sets si->fading_possible_p */
 
@@ -1591,10 +1701,23 @@ clientmessage_response (saver_info *si, Window w, Bool error,
 static void
 bogus_clientmessage_warning (saver_info *si, XEvent *event)
 {
+#if 0  /* Oh, fuck it.  GNOME likes to spew random ClientMessages at us
+          all the time.  This is presumably indicative of an error in
+          the sender of that ClientMessage: if we're getting it and 
+          ignoring it, then it's not reaching the intended recipient.
+          But people complain to me about this all the time ("waaah!
+          xscreensaver is printing to it's stderr and that gets my
+          panties all in a bunch!")  And I'm sick of hearing about it.
+          So we'll just ignore these messages and let GNOME go right
+          ahead and continue to stumble along in its malfunction.
+        */
+
+  saver_preferences *p = &si->prefs;
   char *str = XGetAtomName_safe (si->dpy, event->xclient.message_type);
   Window w = event->xclient.window;
   char wdesc[255];
   int screen = 0;
+  Bool root_p = False;
 
   *wdesc = 0;
   for (screen = 0; screen < si->nscreens; screen++)
@@ -1606,9 +1729,19 @@ bogus_clientmessage_warning (saver_info *si, XEvent *event)
     else if (w == RootWindow (si->dpy, screen))
       {
         strcpy (wdesc, "root");
+        root_p = True;
         break;
       }
 
+  /* If this ClientMessage was sent to the real root window instead of to the
+     xscreensaver window, then it might be intended for someone else who is
+     listening on the root window (e.g., the window manager).  So only print
+     the warning if: we are in debug mode; or if the bogus message was
+     actually sent to one of the xscreensaver-created windows.
+   */
+  if (root_p && !p->debug_p)
+    return;
+
   if (!*wdesc)
     {
       XErrorHandler old_handler;
@@ -1639,8 +1772,11 @@ bogus_clientmessage_warning (saver_info *si, XEvent *event)
   fprintf (stderr, "%s: %d: for window 0x%lx (%s)\n",
            blurb(), screen, (unsigned long) w, wdesc);
   if (str) XFree (str);
+
+#endif /* 0 */
 }
 
+
 Bool
 handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p)
 {
@@ -1854,7 +1990,7 @@ handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p)
   else if (type == XA_DEMO)
     {
       long arg = event->xclient.data.l[1];
-      Bool demo_one_hack_p = (arg == 300);
+      Bool demo_one_hack_p = (arg == 5000);
 
       if (demo_one_hack_p)
        {
@@ -1902,11 +2038,7 @@ handle_clientmessage (saver_info *si, XEvent *event, Bool until_idle_p)
                              "not compiled with support for locking.",
                              "locking not enabled.");
 #else /* !NO_LOCKING */
-      if (p->mode == DONT_BLANK)
-        clientmessage_response(si, window, True,
-                             "LOCK ClientMessage received in DONT_BLANK mode.",
-                               "screen blanking is currently disabled.");
-      else if (si->locking_disabled_p)
+      if (si->locking_disabled_p)
        clientmessage_response (si, window, True,
                      "LOCK ClientMessage received, but locking is disabled.",
                              "locking not enabled.");
@@ -2148,11 +2280,7 @@ analyze_display (saver_info *si)
       j = strlen (buf);
       strcat (buf, exts[i].desc);
       if (!exts[i].useful_p)
-        {
-          int k = j + 18;
-          while (strlen (buf) < k) strcat (buf, " ");
-          strcat (buf, "<-- not supported at compile time!");
-        }
+        strcat (buf, " (disabled at compile time)");
       fprintf (stderr, "%s\n", buf);
     }
 
@@ -2230,6 +2358,8 @@ display_is_on_console_p (saver_info *si)
        not_on_console = False;
       else if (gethostname (localname, sizeof (localname)))
        not_on_console = True;  /* can't find hostname? */
+      else if (!strncmp (dpyname, "/tmp/launch-", 12))  /* MacOS X launchd */
+       not_on_console = False;
       else
        {
          /* We have to call gethostbyname() on the result of gethostname()
@@ -2264,7 +2394,7 @@ display_is_on_console_p (saver_info *si)
 void
 check_for_leaks (const char *where)
 {
-#ifdef HAVE_SBRK
+#if defined(HAVE_SBRK) && defined(LEAK_PARANOIA)
   static unsigned long last_brk = 0;
   int b = (unsigned long) sbrk(0);
   if (last_brk && last_brk < b)