ftp://ftp.linux.ncsu.edu/mirror/ftp.redhat.com/pub/redhat/linux/enterprise/4/en/os...
[xscreensaver] / driver / lock.c
index 7a3ed6a259ffbf3ca1a6a5a6da6fdae625f8c64f..2aa61f637daa8f9178ee7b31521651f7c9c7d57e 100644 (file)
@@ -1,5 +1,5 @@
 /* lock.c --- handling the password dialog for locking-mode.
- * xscreensaver, Copyright (c) 1993-1998 Jamie Zawinski <jwz@jwz.org>
+ * xscreensaver, Copyright (c) 1993-2004 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 "config.h"
 #endif
 
-#ifndef NO_LOCKING   /* whole file */
-
 #include <X11/Intrinsic.h>
+#include <X11/Xos.h>           /* for time() */
+#include <time.h>
+#include <sys/time.h>
 #include "xscreensaver.h"
 #include "resources.h"
 
+#ifndef NO_LOCKING              /* (mostly) whole file */
+
+#ifdef HAVE_SYSLOG
+# include <syslog.h>
+#endif /* HAVE_SYSLOG */
+
+#ifdef HAVE_XHPDISABLERESET
+# include <X11/XHPlib.h>
+  static void hp_lock_reset (saver_info *si, Bool lock_p);
+#endif /* HAVE_XHPDISABLERESET */
+
+#ifdef HAVE_VT_LOCKSWITCH
+# include <fcntl.h>
+# include <sys/ioctl.h>
+# include <sys/vt.h>
+  static void linux_lock_vt_switch (saver_info *si, Bool lock_p);
+#endif /* HAVE_VT_LOCKSWITCH */
+
+#ifdef HAVE_XF86VMODE
+# include <X11/extensions/xf86vmode.h>
+  static void xfree_lock_mode_switch (saver_info *si, Bool lock_p);
+#endif /* HAVE_XF86VMODE */
+
+#ifdef HAVE_XF86MISCSETGRABKEYSSTATE
+# include <X11/extensions/xf86misc.h>
+  static void xfree_lock_grab_smasher (saver_info *si, Bool lock_p);
+#endif /* HAVE_XF86MISCSETGRABKEYSSTATE */
+
+
 #ifdef _VROOT_H_
 ERROR!  You must not include vroot.h in this file.
 #endif
@@ -35,7 +65,7 @@ extern char *getenv(const char *name);
 extern int validate_user(char *name, char *password);
 
 static Bool
-vms_passwd_valid_p(char *pw)
+vms_passwd_valid_p(char *pw, Bool verbose_p)
 {
   return (validate_user (getenv("USER"), typed_passwd) == 1);
 }
@@ -48,23 +78,29 @@ vms_passwd_valid_p(char *pw)
 #undef MAX
 #define MAX(a,b) ((a)>(b)?(a):(b))
 
-enum passwd_state { pw_read, pw_ok, pw_fail, pw_cancel, pw_time };
+enum passwd_state { pw_read, pw_ok, pw_null, pw_fail, pw_cancel, pw_time };
 
 struct passwd_dialog_data {
 
+  saver_screen_info *prompt_screen;
+  int previous_mouse_x, previous_mouse_y;
+
   enum passwd_state state;
   char typed_passwd [80];
   XtIntervalId timer;
   int i_beam;
 
   float ratio;
+  Position x, y;
   Dimension width;
   Dimension height;
+  Dimension border_width;
 
   char *heading_label;
   char *body_label;
   char *user_label;
   char *passwd_label;
+  char *date_label;
   char *user_string;
   char *passwd_string;
 
@@ -72,13 +108,14 @@ struct passwd_dialog_data {
   XFontStruct *body_font;
   XFontStruct *label_font;
   XFontStruct *passwd_font;
+  XFontStruct *date_font;
 
   Pixel foreground;
   Pixel background;
   Pixel passwd_foreground;
   Pixel passwd_background;
-  Pixel logo_foreground;
-  Pixel logo_background;
+  Pixel thermo_foreground;
+  Pixel thermo_background;
   Pixel shadow_top;
   Pixel shadow_bottom;
 
@@ -93,20 +130,40 @@ struct passwd_dialog_data {
 
   Dimension thermo_field_x, thermo_field_y;
   Dimension thermo_field_height;
+
+  Pixmap logo_pixmap;
+  int logo_npixels;
+  unsigned long *logo_pixels;
+
+  Pixmap save_under;
 };
 
+static void draw_passwd_window (saver_info *si);
+static void update_passwd_window (saver_info *si, const char *printed_passwd,
+                                 float ratio);
+static void destroy_passwd_window (saver_info *si);
+static void undo_vp_motion (saver_info *si);
 
-void
+
+static void
 make_passwd_window (saver_info *si)
 {
   struct passwd *p = getpwuid (getuid ());
-  int x, y, bw;
   XSetWindowAttributes attrs;
   unsigned long attrmask = 0;
-  Screen *screen = si->default_screen->screen;
   passwd_dialog_data *pw = (passwd_dialog_data *) calloc (1, sizeof(*pw));
-  Colormap cmap = DefaultColormapOfScreen (screen);
+  Screen *screen;
+  Colormap cmap;
   char *f;
+  saver_screen_info *ssi = &si->screens [mouse_screen (si)];
+
+  pw->prompt_screen = ssi;
+  if (si->prefs.verbose_p)
+    fprintf (stderr, "%s: %d: creating password dialog.\n",
+             blurb(), pw->prompt_screen->number);
+
+  screen = pw->prompt_screen->screen;
+  cmap = DefaultColormapOfScreen (screen);
 
   pw->ratio = 1.0;
 
@@ -118,6 +175,7 @@ make_passwd_window (saver_info *si)
                                        "Dialog.Label.Label");
   pw->passwd_label = get_string_resource ("passwd.passwd.label",
                                          "Dialog.Label.Label");
+  pw->date_label = get_string_resource ("dateFormat", "DateFormat");
 
   if (!pw->heading_label)
     pw->heading_label = strdup("ERROR: REESOURCES NOT INSTALLED CORRECTLY");
@@ -125,6 +183,7 @@ make_passwd_window (saver_info *si)
     pw->body_label = strdup("ERROR: REESOURCES NOT INSTALLED CORRECTLY");
   if (!pw->user_label) pw->user_label = strdup("ERROR");
   if (!pw->passwd_label) pw->passwd_label = strdup("ERROR");
+  if (!pw->date_label) pw->date_label = strdup("ERROR");
 
   /* Put the version number in the label. */
   {
@@ -134,7 +193,7 @@ make_passwd_window (saver_info *si)
     pw->heading_label = s;
   }
 
-  pw->user_string = (p->pw_name ? p->pw_name : "???");
+  pw->user_string = strdup (p && p->pw_name ? p->pw_name : "???");
   pw->passwd_string = strdup("");
 
   f = get_string_resource ("passwd.headingFont", "Dialog.Font");
@@ -157,6 +216,11 @@ make_passwd_window (saver_info *si)
   if (!pw->passwd_font) pw->passwd_font = XLoadQueryFont (si->dpy, "fixed");
   if (f) free (f);
 
+  f = get_string_resource("passwd.dateFont", "Dialog.Font");
+  pw->date_font = XLoadQueryFont (si->dpy, (f ? f : "fixed"));
+  if (!pw->date_font) pw->date_font = XLoadQueryFont (si->dpy, "fixed");
+  if (f) free (f);
+
   pw->foreground = get_pixel_resource ("passwd.foreground",
                                       "Dialog.Foreground",
                                       si->dpy, cmap);
@@ -177,12 +241,12 @@ make_passwd_window (saver_info *si)
   pw->passwd_background = get_pixel_resource ("passwd.text.background",
                                              "Dialog.Text.Background",
                                              si->dpy, cmap);
-  pw->logo_foreground = get_pixel_resource ("passwd.logo.foreground",
-                                           "Dialog.Logo.Foreground",
-                                           si->dpy, cmap);
-  pw->logo_background = get_pixel_resource ("passwd.logo.background",
-                                           "Dialog.Logo.Background",
-                                           si->dpy, cmap);
+  pw->thermo_foreground = get_pixel_resource ("passwd.thermometer.foreground",
+                                             "Dialog.Thermometer.Foreground",
+                                             si->dpy, cmap);
+  pw->thermo_background = get_pixel_resource ("passwd.thermometer.background",
+                                             "Dialog.Thermometer.Background",
+                                             si->dpy, cmap);
   pw->shadow_top = get_pixel_resource ("passwd.topShadowColor",
                                       "Dialog.Foreground",
                                       si->dpy, cmap);
@@ -290,38 +354,111 @@ make_passwd_window (saver_info *si)
   attrmask |= CWOverrideRedirect; attrs.override_redirect = True;
   attrmask |= CWEventMask; attrs.event_mask = ExposureMask|KeyPressMask;
 
+  /* We need to remember the mouse position and restore it afterward, or
+     sometimes (perhaps only with Xinerama?) the mouse gets warped to
+     inside the bounds of the lock dialog window.
+   */
+  {
+    Window pointer_root, pointer_child;
+    int root_x, root_y, win_x, win_y;
+    unsigned int mask;
+    pw->previous_mouse_x = 0;
+    pw->previous_mouse_y = 0;
+    if (XQueryPointer (si->dpy, RootWindowOfScreen (pw->prompt_screen->screen),
+                       &pointer_root, &pointer_child,
+                       &root_x, &root_y, &win_x, &win_y, &mask))
+      {
+        pw->previous_mouse_x = root_x;
+        pw->previous_mouse_y = root_y;
+        if (si->prefs.verbose_p)
+          fprintf (stderr, "%s: %d: mouse is at %d,%d.\n",
+                   blurb(), pw->prompt_screen->number,
+                   pw->previous_mouse_x, pw->previous_mouse_y);
+      }
+    else if (si->prefs.verbose_p)
+      fprintf (stderr, "%s: %d: unable to determine mouse position?\n",
+               blurb(), pw->prompt_screen->number);
+  }
+
+  /* Figure out where on the desktop to place the window so that it will
+     actually be visible; this takes into account virtual viewports as
+     well as Xinerama. */
   {
-    Dimension w = WidthOfScreen(screen);
-    Dimension h = HeightOfScreen(screen);
+    int x, y, w, h;
+    get_screen_viewport (pw->prompt_screen, &x, &y, &w, &h,
+                         pw->previous_mouse_x, pw->previous_mouse_y,
+                         si->prefs.verbose_p);
     if (si->prefs.debug_p) w /= 2;
-    x = ((w + pw->width) / 2) - pw->width;
-    y = ((h + pw->height) / 2) - pw->height;
-    if (x < 0) x = 0;
-    if (y < 0) y = 0;
+    pw->x = x + ((w + pw->width) / 2) - pw->width;
+    pw->y = y + ((h + pw->height) / 2) - pw->height;
+    if (pw->x < x) pw->x = x;
+    if (pw->y < y) pw->y = y;
   }
 
-  bw = get_integer_resource ("passwd.borderWidth", "Dialog.BorderWidth");
+  pw->border_width = get_integer_resource ("passwd.borderWidth",
+                                           "Dialog.BorderWidth");
 
   si->passwd_dialog =
     XCreateWindow (si->dpy,
                   RootWindowOfScreen(screen),
-                  x, y, pw->width, pw->height, bw,
+                  pw->x, pw->y, pw->width, pw->height, pw->border_width,
                   DefaultDepthOfScreen (screen), InputOutput,
                   DefaultVisualOfScreen(screen),
                   attrmask, &attrs);
   XSetWindowBackground (si->dpy, si->passwd_dialog, pw->background);
 
+  /* We use the default visual, not ssi->visual, so that the logo pixmap's
+     visual matches that of the si->passwd_dialog window. */
+  pw->logo_pixmap = xscreensaver_logo (ssi->screen,
+                                       /* ssi->current_visual, */
+                                       DefaultVisualOfScreen(screen),
+                                       si->passwd_dialog, cmap,
+                                       pw->background, 
+                                       &pw->logo_pixels, &pw->logo_npixels,
+                                       0, True);
+
+  /* Before mapping the window, save the bits that are underneath the
+     rectangle the window will occlude.  When we lower the window, we
+     restore these bits.  This works, because the running screenhack
+     has already been sent SIGSTOP, so we know nothing else is drawing
+     right now! */
+  {
+    XGCValues gcv;
+    GC gc;
+    pw->save_under = XCreatePixmap (si->dpy,
+                                    pw->prompt_screen->screensaver_window,
+                                    pw->width + (pw->border_width*2) + 1,
+                                    pw->height + (pw->border_width*2) + 1,
+                                    pw->prompt_screen->current_depth);
+    gcv.function = GXcopy;
+    gc = XCreateGC (si->dpy, pw->save_under, GCFunction, &gcv);
+    XCopyArea (si->dpy, pw->prompt_screen->screensaver_window,
+               pw->save_under, gc,
+               pw->x - pw->border_width, pw->y - pw->border_width,
+               pw->width + (pw->border_width*2) + 1,
+               pw->height + (pw->border_width*2) + 1,
+               0, 0);
+    XFreeGC (si->dpy, gc);
+  }
+
   XMapRaised (si->dpy, si->passwd_dialog);
   XSync (si->dpy, False);
 
+  move_mouse_grab (si, si->passwd_dialog,
+                   pw->prompt_screen->cursor,
+                   pw->prompt_screen->number);
+  undo_vp_motion (si);
+
   si->pw_data = pw;
 
+  if (cmap)
+    XInstallColormap (si->dpy, cmap);
   draw_passwd_window (si);
   XSync (si->dpy, False);
 }
 
 
-void
+static void
 draw_passwd_window (saver_info *si)
 {
   passwd_dialog_data *pw = si->pw_data;
@@ -336,7 +473,9 @@ draw_passwd_window (saver_info *si)
            pw->body_font->ascent + pw->body_font->descent +
            (2 * MAX ((pw->label_font->ascent + pw->label_font->descent),
                      (pw->passwd_font->ascent + pw->passwd_font->descent +
-                      (pw->shadow_width * 4)))));
+                      (pw->shadow_width * 4)))) +
+            pw->date_font->ascent + pw->date_font->descent
+            );
   spacing = ((pw->height - (2 * pw->shadow_width) -
              pw->internal_border - height)) / 8;
   if (spacing < 0) spacing = 0;
@@ -444,38 +583,76 @@ draw_passwd_window (saver_info *si)
                         pw->shadow_width,
                         pw->shadow_bottom, pw->shadow_top);
 
-  /* the logo
+
+  /* The date, below the text fields
    */
-  XSetForeground (si->dpy, gc1, pw->logo_foreground);
-  XSetForeground (si->dpy, gc2, pw->logo_background);
+  {
+    char buf[100];
+    time_t now = time ((time_t *) 0);
+    struct tm *tm = localtime (&now);
+    memset (buf, 0, sizeof(buf));
+    strftime (buf, sizeof(buf)-1, pw->date_label, tm);
+
+    XSetFont (si->dpy, gc1, pw->date_font->fid);
+    y1 += pw->shadow_width;
+    y1 += (spacing + tb_height);
+    y1 += spacing/2;
+    sw = string_width (pw->date_font, buf);
+    x2 = x1 + x2 - sw;
+    XDrawString (si->dpy, si->passwd_dialog, gc1, x2, y1, buf, strlen(buf));
+  }
 
-  x1 = pw->shadow_width * 3;
-  y1 = pw->shadow_width * 3;
-  x2 = pw->logo_width - (pw->shadow_width * 6);
-  y2 = pw->logo_height - (pw->shadow_width * 6);
 
-  XFillRectangle (si->dpy, si->passwd_dialog, gc2, x1, y1, x2, y2);
-  skull (si->dpy, si->passwd_dialog, gc1, gc2,
-        x1 + pw->shadow_width, y1 + pw->shadow_width,
-        x2 - (pw->shadow_width * 2), y2 - (pw->shadow_width * 2));
+  /* The logo
+   */
+  x1 = pw->shadow_width * 6;
+  y1 = pw->shadow_width * 6;
+  x2 = pw->logo_width - (pw->shadow_width * 12);
+  y2 = pw->logo_height - (pw->shadow_width * 12);
+
+  if (pw->logo_pixmap)
+    {
+      Window root;
+      int x, y;
+      unsigned int w, h, bw, d;
+      XGetGeometry (si->dpy, pw->logo_pixmap, &root, &x, &y, &w, &h, &bw, &d);
+      XSetForeground (si->dpy, gc1, pw->foreground);
+      XSetBackground (si->dpy, gc1, pw->background);
+      if (d == 1)
+        XCopyPlane (si->dpy, pw->logo_pixmap, si->passwd_dialog, gc1,
+                    0, 0, w, h,
+                    x1 + ((x2 - (int)w) / 2),
+                    y1 + ((y2 - (int)h) / 2),
+                    1);
+      else
+        XCopyArea (si->dpy, pw->logo_pixmap, si->passwd_dialog, gc1,
+                   0, 0, w, h,
+                   x1 + ((x2 - (int)w) / 2),
+                   y1 + ((y2 - (int)h) / 2));
+    }
 
   /* The thermometer
    */
+  XSetForeground (si->dpy, gc1, pw->thermo_foreground);
+  XSetForeground (si->dpy, gc2, pw->thermo_background);
+
   pw->thermo_field_x = pw->logo_width + pw->shadow_width;
-  pw->thermo_field_y = pw->shadow_width * 3;
-  pw->thermo_field_height = pw->height - (pw->shadow_width * 6);
+  pw->thermo_field_y = pw->shadow_width * 5;
+  pw->thermo_field_height = pw->height - (pw->shadow_width * 10);
 
+#if 0
   /* Solid border inside the logo box. */
   XSetForeground (si->dpy, gc1, pw->foreground);
   XDrawRectangle (si->dpy, si->passwd_dialog, gc1, x1, y1, x2-1, y2-1);
+#endif
 
   /* The shadow around the logo
    */
   draw_shaded_rectangle (si->dpy, si->passwd_dialog,
-                        pw->shadow_width * 2,
-                        pw->shadow_width * 2,
-                        pw->logo_width - (pw->shadow_width * 4),
-                        pw->logo_height - (pw->shadow_width * 4),
+                        pw->shadow_width * 4,
+                        pw->shadow_width * 4,
+                        pw->logo_width - (pw->shadow_width * 8),
+                        pw->logo_height - (pw->shadow_width * 8),
                         pw->shadow_width,
                         pw->shadow_bottom, pw->shadow_top);
 
@@ -483,19 +660,19 @@ draw_passwd_window (saver_info *si)
    */
   draw_shaded_rectangle (si->dpy, si->passwd_dialog,
                         pw->logo_width,
-                        pw->shadow_width * 2,
+                        pw->shadow_width * 4,
                         pw->thermo_width + (pw->shadow_width * 2),
-                        pw->height - (pw->shadow_width * 4),
+                        pw->height - (pw->shadow_width * 8),
                         pw->shadow_width,
                         pw->shadow_bottom, pw->shadow_top);
 
+#if 1
   /* Solid border inside the thermometer. */
   XSetForeground (si->dpy, gc1, pw->foreground);
   XDrawRectangle (si->dpy, si->passwd_dialog, gc1, 
-                 pw->logo_width + pw->shadow_width,
-                 pw->shadow_width * 3,
-                 pw->thermo_width - 1,
-                 pw->height - (pw->shadow_width * 6) - 1);
+                 pw->thermo_field_x, pw->thermo_field_y,
+                  pw->thermo_width - 1, pw->thermo_field_height - 1);
+#endif
 
   /* The shadow around the whole window
    */
@@ -510,13 +687,14 @@ draw_passwd_window (saver_info *si)
 }
 
 
-void
+static void
 update_passwd_window (saver_info *si, const char *printed_passwd, float ratio)
 {
   passwd_dialog_data *pw = si->pw_data;
   XGCValues gcv;
   GC gc1, gc2;
   int x, y;
+  XRectangle rects[1];
 
   pw->ratio = ratio;
   gcv.foreground = pw->passwd_foreground;
@@ -534,22 +712,34 @@ update_passwd_window (saver_info *si, const char *printed_passwd, float ratio)
 
   /* the "password" text field
    */
+  rects[0].x =  pw->passwd_field_x;
+  rects[0].y =  pw->passwd_field_y;
+  rects[0].width = pw->passwd_field_width;
+  rects[0].height = pw->passwd_field_height;
+
   XFillRectangle (si->dpy, si->passwd_dialog, gc2,
-                 pw->passwd_field_x, pw->passwd_field_y,
-                 pw->passwd_field_width, pw->passwd_field_height);
+                  rects[0].x, rects[0].y, rects[0].width, rects[0].height);
+
+  XSetClipRectangles (si->dpy, gc1, 0, 0, rects, 1, Unsorted);
+
   XDrawString (si->dpy, si->passwd_dialog, gc1,
-              pw->passwd_field_x + pw->shadow_width,
-              pw->passwd_field_y + (pw->passwd_font->ascent +
-                                    pw->passwd_font->descent),
-              pw->passwd_string, strlen(pw->passwd_string));
+               rects[0].x + pw->shadow_width,
+               rects[0].y + (pw->passwd_font->ascent +
+                             pw->passwd_font->descent),
+               pw->passwd_string, strlen(pw->passwd_string));
+
+  XSetClipMask (si->dpy, gc1, None);
 
   /* The I-beam
    */
   if (pw->i_beam != 0)
     {
-      x = (pw->passwd_field_x + pw->shadow_width +
+      x = (rects[0].x + pw->shadow_width +
           string_width (pw->passwd_font, pw->passwd_string));
-      y = pw->passwd_field_y + pw->shadow_width;
+      y = rects[0].y + pw->shadow_width;
+
+      if (x > rects[0].x + rects[0].width - 1)
+        x = rects[0].x + rects[0].width - 1;
       XDrawLine (si->dpy, si->passwd_dialog, gc1, 
                 x, y, x, y + pw->passwd_font->ascent);
     }
@@ -559,7 +749,7 @@ update_passwd_window (saver_info *si, const char *printed_passwd, float ratio)
 
   /* the thermometer
    */
-  y = pw->thermo_field_height * (1.0 - pw->ratio);
+  y = (pw->thermo_field_height - 2) * (1.0 - pw->ratio);
   if (y > 0)
     {
       XFillRectangle (si->dpy, si->passwd_dialog, gc2,
@@ -567,7 +757,7 @@ update_passwd_window (saver_info *si, const char *printed_passwd, float ratio)
                      pw->thermo_field_y + 1,
                      pw->thermo_width-2,
                      y);
-      XSetForeground (si->dpy, gc1, pw->logo_foreground);
+      XSetForeground (si->dpy, gc1, pw->thermo_foreground);
       XFillRectangle (si->dpy, si->passwd_dialog, gc1,
                      pw->thermo_field_x + 1,
                      pw->thermo_field_y + 1 + y,
@@ -581,33 +771,76 @@ update_passwd_window (saver_info *si, const char *printed_passwd, float ratio)
 }
 
 
-void
+static void
 destroy_passwd_window (saver_info *si)
 {
+  saver_preferences *p = &si->prefs;
   passwd_dialog_data *pw = si->pw_data;
-  Screen *screen = si->default_screen->screen;
-  Colormap cmap = DefaultColormapOfScreen (screen);
-  Pixel black = BlackPixelOfScreen (screen);
-  Pixel white = WhitePixelOfScreen (screen);
+  saver_screen_info *ssi = pw->prompt_screen;
+  Colormap cmap = DefaultColormapOfScreen (ssi->screen);
+  Pixel black = BlackPixelOfScreen (ssi->screen);
+  Pixel white = WhitePixelOfScreen (ssi->screen);
+  XEvent event;
+
+  memset (pw->typed_passwd, 0, sizeof(pw->typed_passwd));
+  memset (pw->passwd_string, 0, strlen(pw->passwd_string));
 
   if (pw->timer)
     XtRemoveTimeOut (pw->timer);
 
+  move_mouse_grab (si, RootWindowOfScreen (ssi->screen),
+                   ssi->cursor, ssi->number);
+
+  if (p->verbose_p)
+    fprintf (stderr, "%s: %d: moving mouse back to %d,%d.\n",
+             blurb(), ssi->number,
+             pw->previous_mouse_x, pw->previous_mouse_y);
+
+  XWarpPointer (si->dpy, None, RootWindowOfScreen (ssi->screen),
+                0, 0, 0, 0,
+                pw->previous_mouse_x, pw->previous_mouse_y);
+
+  XSync (si->dpy, False);
+  while (XCheckMaskEvent (si->dpy, PointerMotionMask, &event))
+    if (p->verbose_p)
+      fprintf (stderr, "%s: discarding MotionNotify event.\n", blurb());
+
   if (si->passwd_dialog)
     {
       XDestroyWindow (si->dpy, si->passwd_dialog);
       si->passwd_dialog = 0;
     }
   
+  if (pw->save_under)
+    {
+      XGCValues gcv;
+      GC gc;
+      gcv.function = GXcopy;
+      gc = XCreateGC (si->dpy, ssi->screensaver_window, GCFunction, &gcv);
+      XCopyArea (si->dpy, pw->save_under,
+                 ssi->screensaver_window, gc,
+                 0, 0,
+                 pw->width + (pw->border_width*2) + 1,
+                 pw->height + (pw->border_width*2) + 1,
+                 pw->x - pw->border_width, pw->y - pw->border_width);
+      XFreePixmap (si->dpy, pw->save_under);
+      pw->save_under = 0;
+      XFreeGC (si->dpy, gc);
+    }
+
   if (pw->heading_label) free (pw->heading_label);
   if (pw->body_label)    free (pw->body_label);
   if (pw->user_label)    free (pw->user_label);
   if (pw->passwd_label)  free (pw->passwd_label);
+  if (pw->date_label)    free (pw->date_label);
+  if (pw->user_string)   free (pw->user_string);
+  if (pw->passwd_string) free (pw->passwd_string);
 
   if (pw->heading_font) XFreeFont (si->dpy, pw->heading_font);
   if (pw->body_font)    XFreeFont (si->dpy, pw->body_font);
   if (pw->label_font)   XFreeFont (si->dpy, pw->label_font);
   if (pw->passwd_font)  XFreeFont (si->dpy, pw->passwd_font);
+  if (pw->date_font)    XFreeFont (si->dpy, pw->date_font);
 
   if (pw->foreground != black && pw->foreground != white)
     XFreeColors (si->dpy, cmap, &pw->foreground, 1, 0L);
@@ -617,21 +850,290 @@ destroy_passwd_window (saver_info *si)
     XFreeColors (si->dpy, cmap, &pw->passwd_foreground, 1, 0L);
   if (pw->passwd_background != black && pw->passwd_background != white)
     XFreeColors (si->dpy, cmap, &pw->passwd_background, 1, 0L);
-  if (pw->logo_foreground != black && pw->logo_foreground != white)
-    XFreeColors (si->dpy, cmap, &pw->logo_foreground, 1, 0L);
-  if (pw->logo_background != black && pw->logo_background != white)
-    XFreeColors (si->dpy, cmap, &pw->logo_background, 1, 0L);
+  if (pw->thermo_foreground != black && pw->thermo_foreground != white)
+    XFreeColors (si->dpy, cmap, &pw->thermo_foreground, 1, 0L);
+  if (pw->thermo_background != black && pw->thermo_background != white)
+    XFreeColors (si->dpy, cmap, &pw->thermo_background, 1, 0L);
   if (pw->shadow_top != black && pw->shadow_top != white)
     XFreeColors (si->dpy, cmap, &pw->shadow_top, 1, 0L);
   if (pw->shadow_bottom != black && pw->shadow_bottom != white)
     XFreeColors (si->dpy, cmap, &pw->shadow_bottom, 1, 0L);
 
+  if (pw->logo_pixmap)
+    XFreePixmap (si->dpy, pw->logo_pixmap);
+  if (pw->logo_pixels)
+    {
+      if (pw->logo_npixels)
+        XFreeColors (si->dpy, cmap, pw->logo_pixels, pw->logo_npixels, 0L);
+      free (pw->logo_pixels);
+      pw->logo_pixels = 0;
+      pw->logo_npixels = 0;
+    }
+
+  if (pw->save_under)
+    XFreePixmap (si->dpy, pw->save_under);
+
+  if (cmap)
+    XInstallColormap (si->dpy, cmap);
+
   memset (pw, 0, sizeof(*pw));
   free (pw);
-
   si->pw_data = 0;
 }
 
+
+static Bool error_handler_hit_p = False;
+
+static int
+ignore_all_errors_ehandler (Display *dpy, XErrorEvent *error)
+{
+  error_handler_hit_p = True;
+  return 0;
+}
+
+
+#ifdef HAVE_XHPDISABLERESET
+/* This function enables and disables the C-Sh-Reset hot-key, which
+   normally resets the X server (logging out the logged-in user.)
+   We don't want random people to be able to do that while the
+   screen is locked.
+ */
+static void
+hp_lock_reset (saver_info *si, Bool lock_p)
+{
+  static Bool hp_locked_p = False;
+
+  /* Calls to XHPDisableReset and XHPEnableReset must be balanced,
+     or BadAccess errors occur.  (It's ok for this to be global,
+     since it affects the whole machine, not just the current screen.)
+  */
+  if (hp_locked_p == lock_p)
+    return;
+
+  if (lock_p)
+    XHPDisableReset (si->dpy);
+  else
+    XHPEnableReset (si->dpy);
+  hp_locked_p = lock_p;
+}
+#endif /* HAVE_XHPDISABLERESET */
+
+\f
+#ifdef HAVE_XF86MISCSETGRABKEYSSTATE
+
+/* This function enables and disables the Ctrl-Alt-KP_star and 
+   Ctrl-Alt-KP_slash hot-keys, which (in XFree86 4.2) break any
+   grabs and/or kill the grabbing client.  That would effectively
+   unlock the screen, so we don't like that.
+
+   The Ctrl-Alt-KP_star and Ctrl-Alt-KP_slash hot-keys only exist
+   if AllowDeactivateGrabs and/or AllowClosedownGrabs are turned on
+   in XF86Config.  I believe they are disabled by default.
+
+   This does not affect any other keys (specifically Ctrl-Alt-BS or
+   Ctrl-Alt-F1) but I wish it did.  Maybe it will someday.
+ */
+static void
+xfree_lock_grab_smasher (saver_info *si, Bool lock_p)
+{
+  saver_preferences *p = &si->prefs;
+  int status;
+
+  XErrorHandler old_handler;
+  XSync (si->dpy, False);
+  error_handler_hit_p = False;
+  old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
+  XSync (si->dpy, False);
+  status = XF86MiscSetGrabKeysState (si->dpy, !lock_p);
+  XSync (si->dpy, False);
+  if (error_handler_hit_p) status = 666;
+
+  if (!lock_p && status == MiscExtGrabStateAlready)
+    status = MiscExtGrabStateSuccess;  /* shut up, consider this success */
+
+  if (p->verbose_p && status != MiscExtGrabStateSuccess)
+    fprintf (stderr, "%s: error: XF86MiscSetGrabKeysState(%d) returned %s\n",
+             blurb(), !lock_p,
+             (status == MiscExtGrabStateSuccess ? "MiscExtGrabStateSuccess" :
+              status == MiscExtGrabStateLocked  ? "MiscExtGrabStateLocked"  :
+              status == MiscExtGrabStateAlready ? "MiscExtGrabStateAlready" :
+              status == 666 ? "an X error" :
+              "unknown value"));
+
+  XSync (si->dpy, False);
+  XSetErrorHandler (old_handler);
+  XSync (si->dpy, False);
+}
+#endif /* HAVE_XF86MISCSETGRABKEYSSTATE */
+
+
+
+\f
+/* This function enables and disables the C-Sh-F1 ... F12 hot-keys,
+   which, on Linux systems, switches to another virtual console.
+   We'd like the whole screen/keyboard to be locked, not just one
+   virtual console, so this function disables that while the X
+   screen is locked.
+
+   Unfortunately, this doesn't work -- this ioctl only works when
+   called by root, and we have disavowed our privileges long ago.
+ */
+#ifdef HAVE_VT_LOCKSWITCH
+static void
+linux_lock_vt_switch (saver_info *si, Bool lock_p)
+{
+  saver_preferences *p = &si->prefs;
+  static Bool vt_locked_p = False;
+  const char *dev_console = "/dev/console";
+  int fd;
+
+  if (lock_p == vt_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 1 /* #### doesn't work yet, so don't bother complaining */
+      perror (buf);
+#endif
+      return;
+    }
+
+  if (ioctl (fd, (lock_p ? VT_LOCKSWITCH : VT_UNLOCKSWITCH)) == 0)
+    {
+      vt_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 */
+
+\f
+/* This function enables and disables the C-Alt-Plus and C-Alt-Minus
+   hot-keys, which normally change the resolution of the X server.
+   We don't want people to be able to switch the server resolution
+   while the screen is locked, because if they switch to a higher
+   resolution, it could cause part of the underlying desktop to become
+   exposed.
+ */
+#ifdef HAVE_XF86VMODE
+
+static void
+xfree_lock_mode_switch (saver_info *si, Bool lock_p)
+{
+  static Bool any_mode_locked_p = False;
+  saver_preferences *p = &si->prefs;
+  int screen;
+  int event, error;
+  Bool status;
+  XErrorHandler old_handler;
+
+  if (any_mode_locked_p == lock_p)
+    return;
+  if (!XF86VidModeQueryExtension (si->dpy, &event, &error))
+    return;
+
+  for (screen = 0; screen < (si->xinerama_p ? 1 : si->nscreens); screen++)
+    {
+      XSync (si->dpy, False);
+      old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
+      error_handler_hit_p = False;
+      status = XF86VidModeLockModeSwitch (si->dpy, screen, lock_p);
+      XSync (si->dpy, False);
+      XSetErrorHandler (old_handler);
+      if (error_handler_hit_p) status = False;
+
+      if (status)
+        any_mode_locked_p = lock_p;
+
+      if (!status && (p->verbose_p || !lock_p))
+        /* Only print this when verbose, or when we locked but can't unlock.
+           I tried printing this message whenever it comes up, but
+           mode-locking always fails if DontZoom is set in XF86Config. */
+        fprintf (stderr, "%s: %d: unable to %s mode switching!\n",
+                 blurb(), screen, (lock_p ? "lock" : "unlock"));
+      else if (p->verbose_p)
+        fprintf (stderr, "%s: %d: %s mode switching.\n",
+                 blurb(), screen, (lock_p ? "locked" : "unlocked"));
+    }
+}
+#endif /* HAVE_XF86VMODE */
+
+\f
+/* If the viewport has been scrolled since the screen was blanked,
+   then scroll it back to where it belongs.  This function only exists
+   to patch over a very brief race condition.
+ */
+static void
+undo_vp_motion (saver_info *si)
+{
+#ifdef HAVE_XF86VMODE
+  saver_preferences *p = &si->prefs;
+  int screen;
+  int event, error;
+
+  if (!XF86VidModeQueryExtension (si->dpy, &event, &error))
+    return;
+
+  for (screen = 0; screen < si->nscreens; screen++)
+    {
+      saver_screen_info *ssi = &si->screens[screen];
+      int x, y;
+      Bool status;
+
+      if (ssi->blank_vp_x == -1 && ssi->blank_vp_y == -1)
+        break;
+      if (!XF86VidModeGetViewPort (si->dpy, screen, &x, &y))
+        return;
+      if (ssi->blank_vp_x == x && ssi->blank_vp_y == y)
+        return;
+    
+      /* We're going to move the viewport.  The mouse has just been grabbed on
+         (and constrained to, thus warped to) the password window, so it is no
+         longer near the edge of the screen.  However, wait a bit anyway, just
+         to make sure the server drains its last motion event, so that the
+         screen doesn't continue to scroll after we've reset the viewport.
+       */
+      XSync (si->dpy, False);
+      usleep (250000);  /* 1/4 second */
+      XSync (si->dpy, False);
+
+      status = XF86VidModeSetViewPort (si->dpy, screen,
+                                       ssi->blank_vp_x, ssi->blank_vp_y);
+
+      if (!status)
+        fprintf (stderr,
+                 "%s: %d: unable to move vp from (%d,%d) back to (%d,%d)!\n",
+                 blurb(), screen, x, y, ssi->blank_vp_x, ssi->blank_vp_y);
+      else if (p->verbose_p)
+        fprintf (stderr,
+                 "%s: %d: vp moved to (%d,%d); moved it back to (%d,%d).\n",
+                 blurb(), screen, x, y, ssi->blank_vp_x, ssi->blank_vp_y);
+    }
+#endif /* HAVE_XF86VMODE */
+}
+
+
 \f
 /* Interactions
  */
@@ -661,20 +1163,23 @@ passwd_animate_timer (XtPointer closure, XtIntervalId *id)
   else
     pw->timer = 0;
 
-  idle_timer ((XtPointer) si, id);
+  idle_timer ((XtPointer) si, 0);
 }
 
 
+static XComposeStatus *compose_status;
+
 static void
 handle_passwd_key (saver_info *si, XKeyEvent *event)
 {
+  saver_preferences *p = &si->prefs;
   passwd_dialog_data *pw = si->pw_data;
   int pw_size = sizeof (pw->typed_passwd) - 1;
   char *typed_passwd = pw->typed_passwd;
   char s[2];
   char *stars = 0;
   int i;
-  int size = XLookupString (event, s, 1, 0, 0);
+  int size = XLookupString (event, s, 1, 0, compose_status);
 
   if (size != 1) return;
 
@@ -696,10 +1201,18 @@ handle_passwd_key (saver_info *si, XKeyEvent *event)
     case '\012': case '\015':                          /* Enter */
       if (pw->state != pw_read)
        ;  /* already done? */
-      else if (passwd_valid_p (typed_passwd))
-       pw->state = pw_ok;
+      else if (typed_passwd[0] == 0)
+       pw->state = pw_null;
       else
-       pw->state = pw_fail;
+        {
+          update_passwd_window (si, "Checking...", pw->ratio);
+          XSync (si->dpy, False);
+          if (passwd_valid_p (typed_passwd, p->verbose_p))
+            pw->state = pw_ok;
+          else
+            pw->state = pw_fail;
+          update_passwd_window (si, "", pw->ratio);
+        }
       break;
 
     default:
@@ -726,6 +1239,7 @@ handle_passwd_key (saver_info *si, XKeyEvent *event)
 static void
 passwd_event_loop (saver_info *si)
 {
+  saver_preferences *p = &si->prefs;
   char *msg = 0;
   XEvent event;
   passwd_animate_timer ((XtPointer) si, 0);
@@ -744,15 +1258,83 @@ passwd_event_loop (saver_info *si)
   switch (si->pw_data->state)
     {
     case pw_ok:   msg = 0; break;
+    case pw_null: msg = ""; break;
     case pw_time: msg = "Timed out!"; break;
     default:      msg = "Sorry!"; break;
     }
 
+  if (si->pw_data->state == pw_fail)
+    si->unlock_failures++;
+
+  if (p->verbose_p)
+    switch (si->pw_data->state)
+      {
+      case pw_ok:
+       fprintf (stderr, "%s: password correct.\n", blurb()); break;
+      case pw_fail:
+       fprintf (stderr, "%s: password incorrect!\n", blurb()); break;
+      case pw_null:
+      case pw_cancel:
+       fprintf (stderr, "%s: password entry cancelled.\n", blurb()); break;
+      case pw_time:
+       fprintf (stderr, "%s: password entry timed out.\n", blurb()); break;
+      default: break;
+      }
+
+#ifdef HAVE_SYSLOG
+  if (si->pw_data->state == pw_fail)
+    {
+      /* If they typed a password (as opposed to just hitting return) and
+        the password was invalid, log it.
+      */
+      struct passwd *pw = getpwuid (getuid ());
+      char *d = DisplayString (si->dpy);
+      char *u = (pw->pw_name ? pw->pw_name : "???");
+      int opt = 0;
+      int fac = 0;
+
+# ifdef LOG_PID
+      opt = LOG_PID;
+# endif
+
+# if defined(LOG_AUTHPRIV)
+      fac = LOG_AUTHPRIV;
+# elif defined(LOG_AUTH)
+      fac = LOG_AUTH;
+# else
+      fac = LOG_DAEMON;
+# endif
+
+      if (!d) d = "";
+      openlog (progname, opt, fac);
+      syslog (LOG_NOTICE, "FAILED LOGIN %d ON DISPLAY \"%s\", FOR \"%s\"",
+             si->unlock_failures, d, u);
+      closelog ();
+    }
+#endif /* HAVE_SYSLOG */
+
+  if (si->pw_data->state == pw_fail)
+    XBell (si->dpy, False);
+
+  if (si->pw_data->state == pw_ok && si->unlock_failures != 0)
+    {
+      if (si->unlock_failures == 1)
+       fprintf (real_stderr,
+                "%s: WARNING: 1 failed attempt to unlock the screen.\n",
+                blurb());
+      else
+       fprintf (real_stderr,
+                "%s: WARNING: %d failed attempts to unlock the screen.\n",
+                blurb(), si->unlock_failures);
+      fflush (real_stderr);
+
+      si->unlock_failures = 0;
+    }
+
   if (msg)
     {
       si->pw_data->i_beam = 0;
       update_passwd_window (si, msg, 0.0);
-      XBell (si->dpy, False);
       XSync (si->dpy, False);
       sleep (1);
 
@@ -765,28 +1347,90 @@ passwd_event_loop (saver_info *si)
     }
 }
 
+
+static void
+handle_typeahead (saver_info *si)
+{
+  passwd_dialog_data *pw = si->pw_data;
+  int i;
+  if (!si->unlock_typeahead)
+    return;
+
+  i = strlen (si->unlock_typeahead);
+  if (i >= sizeof(pw->typed_passwd) - 1)
+    i = sizeof(pw->typed_passwd) - 1;
+
+  memcpy (pw->typed_passwd, si->unlock_typeahead, i);
+  pw->typed_passwd [i] = 0;
+
+  memset (si->unlock_typeahead, '*', strlen(si->unlock_typeahead));
+  si->unlock_typeahead[i] = 0;
+  update_passwd_window (si, si->unlock_typeahead, pw->ratio);
+
+  free (si->unlock_typeahead);
+  si->unlock_typeahead = 0;
+}
+
+
 Bool
 unlock_p (saver_info *si)
 {
-  Screen *screen = si->default_screen->screen;
-  Colormap cmap = DefaultColormapOfScreen (screen);
+  saver_preferences *p = &si->prefs;
   Bool status;
 
+  raise_window (si, True, True, True);
+
+  if (p->verbose_p)
+    fprintf (stderr, "%s: prompting for password.\n", blurb());
+
   if (si->pw_data || si->passwd_dialog)
     destroy_passwd_window (si);
 
   make_passwd_window (si);
-  if (cmap) XInstallColormap (si->dpy, cmap);
 
+  compose_status = calloc (1, sizeof (*compose_status));
+
+  handle_typeahead (si);
   passwd_event_loop (si);
 
   status = (si->pw_data->state == pw_ok);
   destroy_passwd_window (si);
 
-  cmap = si->default_screen->cmap;
-  if (cmap) XInstallColormap (si->dpy, cmap);
+  free (compose_status);
+  compose_status = 0;
 
   return status;
 }
 
-#endif /* !NO_LOCKING -- whole file */
+
+void
+set_locked_p (saver_info *si, Bool locked_p)
+{
+  si->locked_p = locked_p;
+
+#ifdef HAVE_XHPDISABLERESET
+  hp_lock_reset (si, locked_p);                 /* turn off/on C-Sh-Reset */
+#endif
+#ifdef HAVE_VT_LOCKSWITCH
+  linux_lock_vt_switch (si, locked_p);          /* turn off/on C-Alt-F1 */
+#endif
+#ifdef HAVE_XF86VMODE
+  xfree_lock_mode_switch (si, locked_p);        /* turn off/on C-Alt-Plus */
+#endif
+#ifdef HAVE_XF86MISCSETGRABKEYSSTATE
+  xfree_lock_grab_smasher (si, locked_p);       /* turn off/on C-Alt-KP-*,/ */
+#endif
+
+  store_saver_status (si);                     /* store locked-p */
+}
+
+
+#else  /*  NO_LOCKING -- whole file */
+
+void
+set_locked_p (saver_info *si, Bool locked_p)
+{
+  if (locked_p) abort();
+}
+
+#endif /* !NO_LOCKING */