http://ftp.x.org/contrib/applications/xscreensaver-3.10.tar.gz
[xscreensaver] / driver / windows.c
index 4daa0607ea902bac5e35fd1d2ffa640a10e95e29..4945202a1f228690bb8b4abe2e6b0f6ffe0659bb 100644 (file)
 #ifdef VMS
 # include <unixlib.h>          /* for getpid() */
 # include "vms-gtod.h"         /* for gettimeofday() */
-# if !defined(HAVE_UNAME) && (__VMS_VER >= 70000000)
-#  define HAVE_UNAME 1
-# endif /* !HAVE_UNAME */
 #endif /* VMS */
 
-# ifdef HAVE_UNAME
-#  include <sys/utsname.h>     /* for uname() */
-# endif /* HAVE_UNAME */
+#ifndef VMS
+# include <pwd.h>              /* for getpwuid() */
+#else /* VMS */
+# include "vms-pwd.h"
+#endif /* VMS */
+
+#ifdef HAVE_UNAME
+# include <sys/utsname.h>      /* for uname() */
+#endif /* HAVE_UNAME */
 
 #include <stdio.h>
 #include <X11/Xproto.h>                /* for CARD32 */
 #include "visual.h"
 #include "fade.h"
 
+
+#ifdef HAVE_VT_LOCKSWITCH
+# include <fcntl.h>
+# include <sys/ioctl.h>
+# include <sys/vt.h>
+  static void lock_vt (saver_info *si, Bool lock_p);
+#endif /* HAVE_VT_LOCKSWITCH */
+
+
 extern int kill (pid_t, int);          /* signal() is in sys/signal.h... */
 
 Atom XA_VROOT, XA_XSETROOT_ID;
@@ -81,6 +93,25 @@ static void store_activate_time (saver_info *si, Bool use_last_p);
         Button4MotionMask | Button5MotionMask | ButtonMotionMask)
 
 
+static const char *
+grab_string(int status)
+{
+  switch (status)
+    {
+    case GrabSuccess:     return "GrabSuccess";
+    case AlreadyGrabbed:  return "AlreadyGrabbed";
+    case GrabInvalidTime: return "GrabInvalidTime";
+    case GrabNotViewable: return "GrabNotViewable";
+    case GrabFrozen:      return "GrabFrozen";
+    default:
+      {
+       static char foo[255];
+       sprintf(foo, "unknown status: %d", status);
+       return foo;
+      }
+    }
+}
+
 static int
 grab_kbd(saver_info *si, Window w)
 {
@@ -95,36 +126,10 @@ grab_kbd(saver_info *si, Window w)
 
   if (p->verbose_p)
     fprintf(stderr, "%s: grabbing keyboard on 0x%x... %s.\n",
-           blurb(), (unsigned long) w,
-           (status == GrabSuccess ? "GrabSuccess" :
-            status == AlreadyGrabbed ? "AlreadyGrabbed" :
-            status == GrabInvalidTime ? "GrabInvalidTime" :
-            status == GrabNotViewable ? "GrabNotViewable" :
-            status == GrabFrozen ? "GrabFrozen" :
-            "???"));
-
+           blurb(), (unsigned long) w, grab_string(status));
   return status;
 }
 
-static const char *
-grab_string(int status)
-{
-  switch (status)
-    {
-    case GrabSuccess:     return "GrabSuccess";     break;
-    case AlreadyGrabbed:  return "AlreadyGrabbed";  break;
-    case GrabInvalidTime: return "GrabInvalidTime"; break;
-    case GrabNotViewable: return "GrabNotViewable"; break;
-    case GrabFrozen:      return "GrabFrozen";      break;
-    default:
-      {
-       static char foo[255];
-       sprintf(foo, "unknown status: %d", status);
-       return foo;
-      }
-    }
-}
-
 
 static int
 grab_mouse (saver_info *si, Window w, Cursor cursor)
@@ -167,34 +172,37 @@ ungrab_mouse(saver_info *si)
 }
 
 
-void
+static Bool
 grab_keyboard_and_mouse (saver_info *si, Window window, Cursor cursor)
 {
-  Status status;
+  Status mstatus, kstatus;
   XSync (si->dpy, False);
 
-  status = grab_kbd (si, window);
-  if (status != GrabSuccess)
+  kstatus = grab_kbd (si, window);
+  if (kstatus != GrabSuccess)
     {  /* try again in a second */
       sleep (1);
-      status = grab_kbd (si, window);
-      if (status != GrabSuccess)
+      kstatus = grab_kbd (si, window);
+      if (kstatus != GrabSuccess)
        fprintf (stderr, "%s: couldn't grab keyboard!  (%s)\n",
-                blurb(), grab_string(status));
+                blurb(), grab_string(kstatus));
     }
 
-  status = grab_mouse (si, window, cursor);
-  if (status != GrabSuccess)
+  mstatus = grab_mouse (si, window, cursor);
+  if (mstatus != GrabSuccess)
     {  /* try again in a second */
       sleep (1);
-      status = grab_mouse (si, window, cursor);
-      if (status != GrabSuccess)
+      mstatus = grab_mouse (si, window, cursor);
+      if (mstatus != GrabSuccess)
        fprintf (stderr, "%s: couldn't grab pointer!  (%s)\n",
-                blurb(), grab_string(status));
+                blurb(), grab_string(mstatus));
     }
+
+  return (kstatus == GrabSuccess ||
+         mstatus == GrabSuccess);
 }
 
-void
+static void
 ungrab_keyboard_and_mouse (saver_info *si)
 {
   ungrab_mouse (si);
@@ -351,6 +359,16 @@ save_real_vroot (saver_screen_info *ssi)
   Window root = RootWindowOfScreen (screen);
   Window root2, parent, *kids;
   unsigned int nkids;
+  XErrorHandler old_handler;
+
+  /* It's possible that a window might be deleted between our call to
+     XQueryTree() and our call to XGetWindowProperty().  Don't die if
+     that happens (but just ignore that window, it's not the one we're
+     interested in anyway.)
+   */
+  XSync (dpy, False);
+  old_handler = XSetErrorHandler (BadWindow_ehandler);
+  XSync (dpy, False);
 
   ssi->real_vroot = 0;
   ssi->real_vroot_value = 0;
@@ -386,6 +404,10 @@ save_real_vroot (saver_screen_info *ssi)
       ssi->real_vroot_value = *vrootP;
     }
 
+  XSync (dpy, False);
+  XSetErrorHandler (old_handler);
+  XSync (dpy, False);
+
   if (ssi->real_vroot)
     {
       handle_signals (si, True);
@@ -592,6 +614,7 @@ saver_exit (saver_info *si, int status, const char *dump_core_reason)
 {
   saver_preferences *p = &si->prefs;
   static Bool exiting = False;
+  Bool bugp;
   Bool vrs;
 
   if (exiting)
@@ -614,49 +637,40 @@ saver_exit (saver_info *si, int status, const char *dump_core_reason)
   else if (status == 1) status = -1;
 #endif
 
+  bugp = !!dump_core_reason;
+
   if (si->prefs.debug_p && !dump_core_reason)
     dump_core_reason = "because of -debug";
 
   if (dump_core_reason)
     {
-#if 0
-      if (si->locking_disabled_p &&
-         si->nolock_reason &&
-         *si->nolock_reason)
-       {
-         /* If locking is disabled, it's because xscreensaver was launched
-            by root, and has relinquished its user id (most likely we are
-            now running as "nobody".)  This means we won't be able to dump
-            core, since "nobody" can't write files; so don't even try.
-          */
-         fprintf(real_stderr, "%s: NOT dumping core (%s)\n", blurb(),
-                 si->nolock_reason);
-       }
-      else
-#endif
-       {
-         /* Note that the Linux man page for setuid() says If uid is
-            different from the old effective uid, the process will be
-            forbidden from leaving core dumps.
-          */
-
-         char cwd[4096]; /* should really be PATH_MAX, but who cares. */
-         fprintf(real_stderr, "%s: dumping core (%s)\n", blurb(),
-                 dump_core_reason);
+      /* Note that the Linux man page for setuid() says If uid is
+        different from the old effective uid, the process will be
+        forbidden from leaving core dumps.
+      */
+      char cwd[4096]; /* should really be PATH_MAX, but who cares. */
+      cwd[0] = 0;
+      fprintf(real_stderr, "%s: dumping core (%s)\n", blurb(),
+             dump_core_reason);
+
+      if (bugp)
+       fprintf(real_stderr,
+               "%s: see http://www.jwz.org/xscreensaver/bugs.html\n"
+               "\t\tfor bug reporting information.\n\n",
+               blurb());
 
 # if defined(HAVE_GETCWD)
-         getcwd (cwd, sizeof(cwd));
+      if (!getcwd (cwd, sizeof(cwd)))
 # elif defined(HAVE_GETWD)
-         getwd (cwd);
-# else
-         strcpy(cwd, "unknown.");
+      if (!getwd (cwd))
 # endif
-         fprintf (real_stderr, "%s: current directory is %s\n", blurb(), cwd);
-         describe_uids (si, real_stderr);
+        strcpy(cwd, "unknown.");
 
-         /* Do this to drop a core file, so that we can get a stack trace. */
-         abort();
-       }
+      fprintf (real_stderr, "%s: current directory is %s\n", blurb(), cwd);
+      describe_uids (si, real_stderr);
+
+      /* Do this to drop a core file, so that we can get a stack trace. */
+      abort();
     }
 
   exit (status);
@@ -678,12 +692,77 @@ window_exists_p (Display *dpy, Window window)
   return (xgwa.screen != 0);
 }
 
+static void
+store_saver_id (saver_screen_info *ssi)
+{
+  XClassHint class_hints;
+  saver_info *si = ssi->global;
+  unsigned long pid = (unsigned long) getpid ();
+  char buf[20];
+  struct passwd *p = getpwuid (getuid ());
+  const char *name, *host;
+  char *id;
+  
+  /* First store the name and class on the window.
+   */
+  class_hints.res_name = progname;
+  class_hints.res_class = progclass;
+  XSetClassHint (si->dpy, ssi->screensaver_window, &class_hints);
+  XStoreName (si->dpy, ssi->screensaver_window, "screensaver");
+
+  /* Then store the xscreensaver version number.
+   */
+  XChangeProperty (si->dpy, ssi->screensaver_window,
+                  XA_SCREENSAVER_VERSION,
+                  XA_STRING, 8, PropModeReplace,
+                  (unsigned char *) si->version,
+                  strlen (si->version));
+
+  /* Now store the XSCREENSAVER_ID property, that says what user and host
+     xscreensaver is running as.
+   */
+
+  if (p && p->pw_name && *p->pw_name)
+    name = p->pw_name;
+  else if (p)
+    {
+      sprintf (buf, "%lu", (unsigned long) p->pw_uid);
+      name = buf;
+    }
+  else
+    name = "???";
+
+# if defined(HAVE_UNAME)
+  {
+    struct utsname uts;
+    if (uname (&uts) < 0)
+      host = "???";
+    else
+      host = uts.nodename;
+  }
+# elif defined(VMS)
+  host = getenv("SYS$NODE");
+# else  /* !HAVE_UNAME && !VMS */
+  host = "???";
+# endif /* !HAVE_UNAME && !VMS */
+
+  id = (char *) malloc (strlen(name) + strlen(host) + 50);
+  sprintf (id, "%lu (%s@%s)", pid, name, host);
+
+  XChangeProperty (si->dpy, ssi->screensaver_window,
+                  XA_SCREENSAVER_ID, XA_STRING,
+                  8, PropModeReplace,
+                  (unsigned char *) id, strlen (id));
+  free (id);
+}
+
+
 static void
 initialize_screensaver_window_1 (saver_screen_info *ssi)
 {
   saver_info *si = ssi->global;
   saver_preferences *p = &si->prefs;
-  Bool install_cmap_p = (ssi->install_cmap_p || p->install_cmap_p);
+  Bool install_cmap_p = ssi->install_cmap_p;   /* not p->install_cmap_p */
 
   /* This resets the screensaver window as fully as possible, since there's
      no way of knowing what some random client may have done to us in the
@@ -691,12 +770,10 @@ initialize_screensaver_window_1 (saver_screen_info *ssi)
      its own set of problems...
    */
   XColor black;
-  XClassHint class_hints;
   XSetWindowAttributes attrs;
   unsigned long attrmask;
   int width = WidthOfScreen (ssi->screen);
   int height = HeightOfScreen (ssi->screen);
-  char id [2048];
   static Bool printed_visual_info = False;  /* only print the message once. */
 
   black.red = black.green = black.blue = 0;
@@ -705,13 +782,15 @@ initialize_screensaver_window_1 (saver_screen_info *ssi)
     ssi->cmap = 0;
 
   if (ssi->current_visual != DefaultVisualOfScreen (ssi->screen))
+    /* It's not the default visual, so we have no choice but to install. */
     install_cmap_p = True;
 
   if (install_cmap_p)
     {
       if (! ssi->cmap)
        {
-         ssi->cmap = XCreateColormap (si->dpy, RootWindowOfScreen (ssi->screen),
+         ssi->cmap = XCreateColormap (si->dpy,
+                                      RootWindowOfScreen (ssi->screen),
                                      ssi->current_visual, AllocNone);
          if (! XAllocColor (si->dpy, ssi->cmap, &black)) abort ();
          ssi->black_pixel = black.pixel;
@@ -772,7 +851,7 @@ initialize_screensaver_window_1 (saver_screen_info *ssi)
   printed_visual_info = True;
 
 #ifdef HAVE_MIT_SAVER_EXTENSION
-  if (p->use_mit_saver_extension)
+  if (si->using_mit_saver_extension)
     {
       XScreenSaverInfo *info;
       Window root = RootWindowOfScreen (ssi->screen);
@@ -850,68 +929,28 @@ initialize_screensaver_window_1 (saver_screen_info *ssi)
                 blurb(), (unsigned long) ssi->screensaver_window);
     }
 
-#ifdef HAVE_MIT_SAVER_EXTENSION
-  if (!p->use_mit_saver_extension ||
-      window_exists_p (si->dpy, ssi->screensaver_window))
-    /* When using the MIT-SCREEN-SAVER extension, the window pointed to
-       by screensaver_window only exists while the saver is active.
-       So we must be careful to only try and manipulate it while it
-       exists...
-       (#### The above comment would be true if the MIT extension actually
-       worked, but it's not true today -- see `server_mit_saver_window'.)
-     */
-#endif /* HAVE_MIT_SAVER_EXTENSION */
-    {
-      class_hints.res_name = progname;
-      class_hints.res_class = progclass;
-      XSetClassHint (si->dpy, ssi->screensaver_window, &class_hints);
-      XStoreName (si->dpy, ssi->screensaver_window, "screensaver");
-      XChangeProperty (si->dpy, ssi->screensaver_window,
-                      XA_SCREENSAVER_VERSION,
-                      XA_STRING, 8, PropModeReplace,
-                      (unsigned char *) si->version,
-                      strlen (si->version));
-
-      sprintf (id, "%lu on host ", (unsigned long) getpid ());
 
-# if defined(HAVE_UNAME)
-      {
-       struct utsname uts;
-       if (uname (&uts) < 0)
-         strcat (id, "???");
-       else
-         strcat (id, uts.nodename);
-      }
-# elif defined(VMS)
-      strcat (id, getenv("SYS$NODE"));
-# else  /* !HAVE_UNAME && !VMS */
-      strcat (id, "???");
-# endif /* !HAVE_UNAME && !VMS */
+  store_saver_id (ssi);
 
-      XChangeProperty (si->dpy, ssi->screensaver_window,
-                      XA_SCREENSAVER_ID, XA_STRING,
-                      8, PropModeReplace, (unsigned char *) id, strlen (id));
+  if (!ssi->cursor)
+    {
+      Pixmap bit;
+      bit = XCreatePixmapFromBitmapData (si->dpy, ssi->screensaver_window,
+                                        "\000", 1, 1,
+                                        BlackPixelOfScreen (ssi->screen),
+                                        BlackPixelOfScreen (ssi->screen),
+                                        1);
+      ssi->cursor = XCreatePixmapCursor (si->dpy, bit, bit, &black, &black,
+                                        0, 0);
+      XFreePixmap (si->dpy, bit);
+    }
 
-      if (!ssi->cursor)
-       {
-         Pixmap bit;
-         bit = XCreatePixmapFromBitmapData (si->dpy, ssi->screensaver_window,
-                                            "\000", 1, 1,
-                                            BlackPixelOfScreen (ssi->screen),
-                                            BlackPixelOfScreen (ssi->screen),
-                                            1);
-         ssi->cursor = XCreatePixmapCursor (si->dpy, bit, bit, &black, &black,
-                                            0, 0);
-         XFreePixmap (si->dpy, bit);
-       }
+  XSetWindowBackground (si->dpy, ssi->screensaver_window, ssi->black_pixel);
 
-      XSetWindowBackground (si->dpy, ssi->screensaver_window,
-                           ssi->black_pixel);
-      if (si->demo_mode_p)
-       XUndefineCursor (si->dpy, ssi->screensaver_window);
-      else
-       XDefineCursor (si->dpy, ssi->screensaver_window, ssi->cursor);
-    }
+  if (si->demoing_p)
+    XUndefineCursor (si->dpy, ssi->screensaver_window);
+  else
+    XDefineCursor (si->dpy, ssi->screensaver_window, ssi->cursor);
 }
 
 void
@@ -930,10 +969,13 @@ raise_window (saver_info *si,
   saver_preferences *p = &si->prefs;
   int i;
 
+  if (si->demoing_p)
+    inhibit_fade = True;
+
   initialize_screensaver_window (si);
   reset_watchdog_timer (si, True);
 
-  if (p->fade_p && !inhibit_fade && !si->demo_mode_p)
+  if (p->fade_p && p->fading_possible_p && !inhibit_fade)
     {
       Window *current_windows = (Window *)
        calloc(sizeof(Window), si->nscreens);
@@ -972,7 +1014,7 @@ raise_window (saver_info *si,
       /* Note!  The server is grabbed, and this will take several seconds
         to complete! */
       fade_screens (si->dpy, current_maps, current_windows,
-                   p->fade_seconds, p->fade_ticks, True, !dont_clear);
+                   p->fade_seconds/1000, p->fade_ticks, True, !dont_clear);
 
       free(current_maps);
       free(current_windows);
@@ -1020,19 +1062,33 @@ raise_window (saver_info *si,
     }
 }
 
-void
+Bool
 blank_screen (saver_info *si)
 {
   int i;
+  Bool ok;
 
   /* Note: we do our grabs on the root window, not on the screensaver window.
      If we grabbed on the saver window, then the demo mode and lock dialog
      boxes wouldn't get any events.
    */
-  grab_keyboard_and_mouse (si,
-                          /*si->screens[0].screensaver_window,*/
-                          RootWindowOfScreen(si->screens[0].screen),
-                          (si->demo_mode_p ? 0 : si->screens[0].cursor));
+  ok = grab_keyboard_and_mouse (si,
+                                /*si->screens[0].screensaver_window,*/
+                                RootWindowOfScreen(si->screens[0].screen),
+                                (si->demoing_p
+                                 ? 0
+                                 : si->screens[0].cursor));
+
+
+  if (si->using_mit_saver_extension || si->using_sgi_saver_extension)
+    /* If we're using a server extension, then failure to get a grab is
+       not a big deal -- even without the grab, we will still be able
+       to un-blank when there is user activity, since the server will
+       tell us. */
+    ok = True;
+
+  if (!ok)
+    return False;
 
   for (i = 0; i < si->nscreens; i++)
     {
@@ -1054,13 +1110,21 @@ blank_screen (saver_info *si)
     }
 #endif
 
+#ifdef HAVE_VT_LOCKSWITCH
+  if (si->locked_p)
+      lock_vt (si, True);              /* turn off C-Alt-Fn */
+#endif
+
   si->screen_blanked_p = True;
+
+  return True;
 }
 
 void
 unblank_screen (saver_info *si)
 {
   saver_preferences *p = &si->prefs;
+  Bool unfade_p = (p->fading_possible_p && p->unfade_p);
   int i;
 
   monitor_power_on (si);
@@ -1068,7 +1132,10 @@ unblank_screen (saver_info *si)
   store_activate_time (si, True);
   reset_watchdog_timer (si, False);
 
-  if (p->unfade_p && !si->demo_mode_p)
+  if (si->demoing_p)
+    unfade_p = False;
+
+  if (unfade_p)
     {
       Window *current_windows = (Window *)
        calloc(sizeof(Window), si->nscreens);
@@ -1103,7 +1170,7 @@ unblank_screen (saver_info *si)
 
 
       fade_screens (si->dpy, 0, current_windows,
-                   p->fade_seconds, p->fade_ticks,
+                   p->fade_seconds/1000, p->fade_ticks,
                    False, False);
 
       free(current_windows);
@@ -1169,6 +1236,10 @@ unblank_screen (saver_info *si)
     }
 #endif
 
+#ifdef HAVE_VT_LOCKSWITCH
+  lock_vt (si, False);         /* turn C-Alt-Fn back on */
+#endif
+
   /* Unmap the windows a second time, dammit -- just to avoid a race
      with the screen-grabbing hacks.  (I'm not sure if this is really
      necessary; I'm stabbing in the dark now.)
@@ -1232,6 +1303,7 @@ select_visual (saver_screen_info *ssi, const char *visual_name)
   got_it = !!new_v;
 
   if (new_v && new_v != DefaultVisualOfScreen(ssi->screen))
+    /* It's not the default visual, so we have no choice but to install. */
     install_cmap_p = True;
 
   ssi->install_cmap_p = install_cmap_p;
@@ -1289,9 +1361,11 @@ select_visual (saver_screen_info *ssi, const char *visual_name)
       if (old_w == si->mouse_grab_window)
        {
          XGrabServer (si->dpy);                /* ############ DANGER! */
-         ungrab_mouse(si);
-         grab_mouse(si, ssi->screensaver_window,
-                    (si->demo_mode_p ? 0 : ssi->cursor));
+         ungrab_mouse (si);
+         grab_mouse (si, ssi->screensaver_window,
+                     (si->demoing_p
+                      ? 0
+                      : ssi->cursor));
          XUngrabServer (si->dpy);
          XSync (si->dpy, False);               /* ###### (danger over) */
        }
@@ -1325,3 +1399,56 @@ select_visual (saver_screen_info *ssi, const char *visual_name)
 
   return got_it;
 }
+
+\f
+/* VT locking */
+
+#ifdef HAVE_VT_LOCKSWITCH
+static void
+lock_vt (saver_info *si, Bool lock_p)
+{
+  saver_preferences *p = &si->prefs;
+  static Bool locked_p = False;
+  const char *dev_console = "/dev/console";
+  int fd;
+
+  if (lock_p == locked_p)
+    return;
+
+  if (lock_p && !p->lock_vt_p)
+    return;
+
+  fd = open (dev_console, O_RDWR);
+  if (fd < 0)
+    {
+      char buf [255];
+      sprintf (buf, "%s: couldn't %s VTs: %s", blurb(),
+              (lock_p ? "lock" : "unlock"),
+              dev_console);
+#if 0 /* #### doesn't work yet, so don't bother complaining */
+      perror (buf);
+#endif
+      return;
+    }
+
+  if (ioctl (fd, (lock_p ? VT_LOCKSWITCH : VT_UNLOCKSWITCH)) == 0)
+    {
+      locked_p = lock_p;
+
+      if (p->verbose_p)
+       fprintf (stderr, "%s: %s VTs\n", blurb(),
+                (lock_p ? "locked" : "unlocked"));
+    }
+  else
+    {
+      char buf [255];
+      sprintf (buf, "%s: couldn't %s VTs: ioctl", blurb(),
+              (lock_p ? "lock" : "unlock"));
+#if 0 /* #### doesn't work yet, so don't bother complaining */
+      perror (buf);
+#endif
+    }
+
+  close (fd);
+}
+#endif /* HAVE_VT_LOCKSWITCH */