http://ftp.ksu.edu.tw/FTP/FreeBSD/distfiles/xscreensaver-4.23.tar.gz
[xscreensaver] / driver / lock.c
index d47c99a0d1d159ab0a0868f54abbf8f97c3d2399..65162a591a2752214b3b86583fa22aca4d69b4e4 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-2005 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
 
+#include <ctype.h>
 #include <X11/Intrinsic.h>
+#include <X11/cursorfont.h>
 #include <X11/Xos.h>           /* for time() */
+#include <time.h>
+#include <sys/time.h>
 #include "xscreensaver.h"
 #include "resources.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.
@@ -75,6 +84,9 @@ 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;
@@ -86,6 +98,9 @@ struct passwd_dialog_data {
   Dimension height;
   Dimension border_width;
 
+  Bool show_stars_p; /* "I regret that I have but one asterisk for my country."
+                        -- Nathan Hale, 1776. */
+
   char *heading_label;
   char *body_label;
   char *user_label;
@@ -93,12 +108,14 @@ struct passwd_dialog_data {
   char *date_label;
   char *user_string;
   char *passwd_string;
+  char *login_label;
 
   XFontStruct *heading_font;
   XFontStruct *body_font;
   XFontStruct *label_font;
   XFontStruct *passwd_font;
   XFontStruct *date_font;
+  XFontStruct *button_font;
 
   Pixel foreground;
   Pixel background;
@@ -108,6 +125,8 @@ struct passwd_dialog_data {
   Pixel thermo_background;
   Pixel shadow_top;
   Pixel shadow_bottom;
+  Pixel button_foreground;
+  Pixel button_background;
 
   Dimension logo_width;
   Dimension logo_height;
@@ -118,6 +137,9 @@ struct passwd_dialog_data {
   Dimension passwd_field_x, passwd_field_y;
   Dimension passwd_field_width, passwd_field_height;
 
+  Dimension login_button_x, login_button_y;
+  Dimension login_button_width, login_button_height;
+
   Dimension thermo_field_x, thermo_field_y;
   Dimension thermo_field_height;
 
@@ -125,6 +147,11 @@ struct passwd_dialog_data {
   int logo_npixels;
   unsigned long *logo_pixels;
 
+  Cursor passwd_cursor;
+  Bool login_button_down_p;
+  Bool login_button_p;
+  Bool login_button_enabled_p;
+
   Pixmap save_under;
 };
 
@@ -133,6 +160,7 @@ 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);
+static void handle_passwd_button (saver_info *si, XEvent *event);
 
 
 static void
@@ -141,13 +169,34 @@ make_passwd_window (saver_info *si)
   struct passwd *p = getpwuid (getuid ());
   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)];
+
+  /* Display the button only if the "newLoginCommand" pref is non-null.
+   */
+  pw->login_button_p = (si->prefs.new_login_command &&
+                        *si->prefs.new_login_command);
+
+  if (pw->login_button_p)
+    pw->passwd_cursor = XCreateFontCursor (si->dpy, XC_top_left_arrow);
+  else
+    pw->passwd_cursor = 0;
+
+  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;
 
+  pw->show_stars_p = get_boolean_resource("passwd.asterisks", "Boolean");
+  
   pw->heading_label = get_string_resource ("passwd.heading.label",
                                           "Dialog.Label.Label");
   pw->body_label = get_string_resource ("passwd.body.label",
@@ -156,15 +205,19 @@ make_passwd_window (saver_info *si)
                                        "Dialog.Label.Label");
   pw->passwd_label = get_string_resource ("passwd.passwd.label",
                                          "Dialog.Label.Label");
+  pw->login_label = get_string_resource ("passwd.login.label",
+                                         "Dialog.Button.Label");
+
   pw->date_label = get_string_resource ("dateFormat", "DateFormat");
 
   if (!pw->heading_label)
-    pw->heading_label = strdup("ERROR: REESOURCES NOT INSTALLED CORRECTLY");
+    pw->heading_label = strdup("ERROR: RESOURCES NOT INSTALLED CORRECTLY");
   if (!pw->body_label)
-    pw->body_label = strdup("ERROR: REESOURCES NOT INSTALLED CORRECTLY");
+    pw->body_label = strdup("ERROR: RESOURCES 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");
+  if (!pw->login_label) pw->login_label = strdup ("ERROR (LOGIN)") ;
 
   /* Put the version number in the label. */
   {
@@ -174,7 +227,7 @@ make_passwd_window (saver_info *si)
     pw->heading_label = s;
   }
 
-  pw->user_string = (p && 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");
@@ -182,6 +235,11 @@ make_passwd_window (saver_info *si)
   if (!pw->heading_font) pw->heading_font = XLoadQueryFont (si->dpy, "fixed");
   if (f) free (f);
 
+  f = get_string_resource ("passwd.buttonFont", "Dialog.Font");
+  pw->button_font = XLoadQueryFont (si->dpy, (f ? f : "fixed"));
+  if (!pw->button_font) pw->button_font = XLoadQueryFont (si->dpy, "fixed");
+  if (f) free (f);
+
   f = get_string_resource("passwd.bodyFont", "Dialog.Font");
   pw->body_font = XLoadQueryFont (si->dpy, (f ? f : "fixed"));
   if (!pw->body_font) pw->body_font = XLoadQueryFont (si->dpy, "fixed");
@@ -222,6 +280,12 @@ make_passwd_window (saver_info *si)
   pw->passwd_background = get_pixel_resource ("passwd.text.background",
                                              "Dialog.Text.Background",
                                              si->dpy, cmap);
+  pw->button_foreground = get_pixel_resource ("splash.Button.foreground",
+                                              "Dialog.Button.Foreground",
+                                              si->dpy, cmap);
+  pw->button_background = get_pixel_resource ("splash.Button.background",
+                                              "Dialog.Button.Background",
+                                              si->dpy, cmap);
   pw->thermo_foreground = get_pixel_resource ("passwd.thermometer.foreground",
                                              "Dialog.Thermometer.Foreground",
                                              si->dpy, cmap);
@@ -274,8 +338,8 @@ make_passwd_window (saver_info *si)
     pw->height += ascent + descent;
 
     {
-      Dimension w2 = 0, w3 = 0;
-      Dimension h2 = 0, h3 = 0;
+      Dimension w2 = 0, w3 = 0, button_w = 0;
+      Dimension h2 = 0, h3 = 0, button_h = 0;
       const char *passwd_string = "MMMMMMMMMMMM";
 
       /* Measure the user_label. */
@@ -313,6 +377,33 @@ make_passwd_window (saver_info *si)
       w2 = w2 + w3 + (pw->shadow_width * 2);
       h2 = MAX (h2, h3);
 
+      pw->login_button_width = 0;
+      pw->login_button_height = 0;
+
+      if (pw->login_button_p)
+        {
+          pw->login_button_enabled_p = True;
+
+          /* Measure the "New Login" button */
+          XTextExtents (pw->button_font, pw->login_label,
+                        strlen (pw->login_label),
+                        &direction, &ascent, &descent, &overall);
+          button_w = overall.width;
+          button_h = ascent + descent;
+
+          /* Add some horizontal padding inside the buttons. */
+          button_w += ascent;
+
+          button_w += ((ascent + descent) / 2) + (pw->shadow_width * 2);
+          button_h += ((ascent + descent) / 2) + (pw->shadow_width * 2);
+
+          pw->login_button_width = button_w;
+          pw->login_button_height = button_h;
+
+          w2 = MAX (w2, button_w);
+          h2 += button_h * 1.5;
+        }
+
       if (w2 > pw->width)  pw->width  = w2;
       pw->height += h2;
     }
@@ -333,11 +424,45 @@ make_passwd_window (saver_info *si)
   }
 
   attrmask |= CWOverrideRedirect; attrs.override_redirect = True;
-  attrmask |= CWEventMask; attrs.event_mask = ExposureMask|KeyPressMask;
 
+  attrmask |= CWEventMask;
+  attrs.event_mask = (ExposureMask | KeyPressMask |
+                      ButtonPressMask | ButtonReleaseMask);
+
+  /* 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. */
   {
     int x, y, w, h;
-    get_screen_viewport (si->default_screen, &x, &y, &w, &h, False);
+    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;
     pw->x = x + ((w + pw->width) / 2) - pw->width;
     pw->y = y + ((h + pw->height) / 2) - pw->height;
@@ -357,10 +482,15 @@ make_passwd_window (saver_info *si)
                   attrmask, &attrs);
   XSetWindowBackground (si->dpy, si->passwd_dialog, pw->background);
 
-  pw->logo_pixmap = xscreensaver_logo (si->dpy, si->passwd_dialog, cmap,
+  /* 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,
-                                       True);
+                                       0, True);
 
   /* Before mapping the window, save the bits that are underneath the
      rectangle the window will occlude.  When we lower the window, we
@@ -371,13 +501,13 @@ make_passwd_window (saver_info *si)
     XGCValues gcv;
     GC gc;
     pw->save_under = XCreatePixmap (si->dpy,
-                                    si->default_screen->screensaver_window,
+                                    pw->prompt_screen->screensaver_window,
                                     pw->width + (pw->border_width*2) + 1,
                                     pw->height + (pw->border_width*2) + 1,
-                                    si->default_screen->current_depth);
+                                    pw->prompt_screen->current_depth);
     gcv.function = GXcopy;
     gc = XCreateGC (si->dpy, pw->save_under, GCFunction, &gcv);
-    XCopyArea (si->dpy, si->default_screen->screensaver_window,
+    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,
@@ -389,11 +519,17 @@ make_passwd_window (saver_info *si)
   XMapRaised (si->dpy, si->passwd_dialog);
   XSync (si->dpy, False);
 
-  move_mouse_grab (si, si->passwd_dialog, si->screens[0].cursor);
+  move_mouse_grab (si, si->passwd_dialog,
+                   (pw->passwd_cursor
+                    ? pw->passwd_cursor
+                    : 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);
 }
@@ -411,14 +547,21 @@ draw_passwd_window (saver_info *si)
   int tb_height;
 
   height = (pw->heading_font->ascent + pw->heading_font->descent +
-           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->date_font->ascent + pw->date_font->descent
-            );
-  spacing = ((pw->height - (2 * pw->shadow_width) -
-             pw->internal_border - height)) / 8;
+            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->date_font->ascent + pw->date_font->descent);
+
+  if (pw->login_button_p)
+    height += ((pw->button_font->ascent + pw->button_font->descent) * 2 +
+               2 * pw->shadow_width);
+
+  spacing = (((pw->height
+               - ((pw->login_button_p ? 4 : 2) * pw->shadow_width)
+               - pw->internal_border - height))
+             / 8);
+
   if (spacing < 0) spacing = 0;
 
   gcv.foreground = pw->foreground;
@@ -462,7 +605,7 @@ draw_passwd_window (saver_info *si)
            string_width (pw->label_font, pw->passwd_label)));
   XDrawString (si->dpy, si->passwd_dialog, gc1,
               x2 - string_width (pw->label_font, pw->user_label),
-              y1,
+              y1 - pw->passwd_font->descent,
               pw->user_label, strlen(pw->user_label));
 
   /* the "Password:" prompt
@@ -470,7 +613,7 @@ draw_passwd_window (saver_info *si)
   y1 += (spacing + tb_height);
   XDrawString (si->dpy, si->passwd_dialog, gc1,
               x2 - string_width (pw->label_font, pw->passwd_label),
-              y1,
+              y1 - pw->passwd_font->descent,
               pw->passwd_label, strlen(pw->passwd_label));
 
 
@@ -493,7 +636,9 @@ draw_passwd_window (saver_info *si)
                  x2 - pw->shadow_width,
                  y1 - (pw->passwd_font->ascent + pw->passwd_font->descent),
                  pw->passwd_field_width, pw->passwd_field_height);
-  XDrawString (si->dpy, si->passwd_dialog, gc1, x2, y1,
+  XDrawString (si->dpy, si->passwd_dialog, gc1,
+               x2,
+               y1 - pw->passwd_font->descent,
               pw->user_string, strlen(pw->user_string));
 
   /* the "password" text field
@@ -543,13 +688,44 @@ draw_passwd_window (saver_info *si)
     XDrawString (si->dpy, si->passwd_dialog, gc1, x2, y1, buf, strlen(buf));
   }
 
+  /* The "New Login" button
+   */
+  if (pw->login_button_p)
+    {
+      XSetForeground (si->dpy, gc1, pw->button_foreground);
+      XSetForeground (si->dpy, gc2, pw->button_background);
+      XSetFont (si->dpy, gc1, pw->button_font->fid);
+
+      sw = string_width (pw->button_font, pw->login_label);
+
+      x2 = pw->width - pw->internal_border - (pw->shadow_width * 2);
+
+      /* right aligned button */
+      /* x1 = x2 - pw->login_button_width;  */
+
+      /* centered button */
+      x1 = (pw->logo_width + pw->thermo_width + (pw->shadow_width * 3) +
+            pw->internal_border);
+      x1 = x1 + (x2 - x1 - pw->login_button_width) / 2;
+
+      y1 = (pw->height - pw->internal_border - pw->login_button_height +
+            spacing);
+      y2 = (y1 +
+            ((pw->login_button_height -
+              (pw->button_font->ascent + pw->button_font->descent))
+             / 2) +
+            pw->button_font->ascent);
+
+      pw->login_button_x = x1;
+      pw->login_button_y = y1;
+    }
 
   /* The logo
    */
-  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);
+  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)
     {
@@ -578,20 +754,22 @@ draw_passwd_window (saver_info *si)
   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);
 
@@ -599,19 +777,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
    */
@@ -663,8 +841,7 @@ update_passwd_window (saver_info *si, const char *printed_passwd, float ratio)
 
   XDrawString (si->dpy, si->passwd_dialog, gc1,
                rects[0].x + pw->shadow_width,
-               rects[0].y + (pw->passwd_font->ascent +
-                             pw->passwd_font->descent),
+               rects[0].y + pw->passwd_font->ascent,
                pw->passwd_string, strlen(pw->passwd_string));
 
   XSetClipMask (si->dpy, gc1, None);
@@ -680,7 +857,8 @@ update_passwd_window (saver_info *si, const char *printed_passwd, float ratio)
       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);
+                x, y,
+                 x, y + pw->passwd_font->ascent + pw->passwd_font->descent-1);
     }
 
   pw->i_beam = (pw->i_beam + 1) % 4;
@@ -688,7 +866,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,
@@ -704,6 +882,45 @@ update_passwd_window (saver_info *si, const char *printed_passwd, float ratio)
                      MAX (0, pw->thermo_field_height - y - 2));
     }
 
+  /* The "New Login" button
+   */
+  if (pw->login_button_p)
+    {
+      int x2, y2, sw;
+      XSetFont (si->dpy, gc1, pw->button_font->fid);
+      XSetForeground (si->dpy, gc1,
+                      (pw->login_button_enabled_p
+                       ? pw->passwd_foreground
+                       : pw->shadow_bottom));
+      XSetForeground (si->dpy, gc2, pw->button_background);
+
+      XFillRectangle (si->dpy, si->passwd_dialog, gc2,
+                      pw->login_button_x, pw->login_button_y,
+                     pw->login_button_width, pw->login_button_height);
+
+      sw = string_width (pw->button_font, pw->login_label);
+      x2 = pw->login_button_x + ((pw->login_button_width - sw) / 2);
+      y2 = (pw->login_button_y +
+            ((pw->login_button_height -
+              (pw->button_font->ascent + pw->button_font->descent))
+             / 2) +
+            pw->button_font->ascent);
+
+      XDrawString (si->dpy, si->passwd_dialog, gc1, x2, y2,
+                  pw->login_label, strlen(pw->login_label));
+
+      draw_shaded_rectangle (si->dpy, si->passwd_dialog,
+                             pw->login_button_x, pw->login_button_y, 
+                             pw->login_button_width, pw->login_button_height,
+                             pw->shadow_width,
+                             (pw->login_button_down_p
+                              ? pw->shadow_bottom
+                              : pw->shadow_top), 
+                             (pw->login_button_down_p
+                              ? pw->shadow_top
+                              : pw->shadow_bottom));
+    }
+
   XFreeGC (si->dpy, gc1);
   XFreeGC (si->dpy, gc2);
   XSync (si->dpy, False);
@@ -713,17 +930,39 @@ update_passwd_window (saver_info *si, const char *printed_passwd, float ratio)
 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(si->screens[0].screen),
-                   si->screens[0].cursor);
+  move_mouse_grab (si, RootWindowOfScreen (ssi->screen),
+                   ssi->cursor, ssi->number);
+
+  if (pw->passwd_cursor)
+    XFreeCursor (si->dpy, pw->passwd_cursor);
+
+  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)
     {
@@ -736,10 +975,9 @@ destroy_passwd_window (saver_info *si)
       XGCValues gcv;
       GC gc;
       gcv.function = GXcopy;
-      gc = XCreateGC (si->dpy, si->default_screen->screensaver_window,
-                      GCFunction, &gcv);
+      gc = XCreateGC (si->dpy, ssi->screensaver_window, GCFunction, &gcv);
       XCopyArea (si->dpy, pw->save_under,
-                 si->default_screen->screensaver_window, gc,
+                 ssi->screensaver_window, gc,
                  0, 0,
                  pw->width + (pw->border_width*2) + 1,
                  pw->height + (pw->border_width*2) + 1,
@@ -753,16 +991,26 @@ destroy_passwd_window (saver_info *si)
   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->login_label)   free (pw->login_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->button_font)  XFreeFont (si->dpy, pw->button_font);
 
   if (pw->foreground != black && pw->foreground != white)
     XFreeColors (si->dpy, cmap, &pw->foreground, 1, 0L);
   if (pw->background != black && pw->background != white)
     XFreeColors (si->dpy, cmap, &pw->background, 1, 0L);
+  if (!(pw->button_foreground == black || pw->button_foreground == white))
+    XFreeColors (si->dpy, cmap, &pw->button_foreground, 1, 0L);
+  if (!(pw->button_background == black || pw->button_background == white))
+    XFreeColors (si->dpy, cmap, &pw->button_background, 1, 0L);
   if (pw->passwd_foreground != black && pw->passwd_foreground != white)
     XFreeColors (si->dpy, cmap, &pw->passwd_foreground, 1, 0L);
   if (pw->passwd_background != black && pw->passwd_background != white)
@@ -778,18 +1026,37 @@ destroy_passwd_window (saver_info *si)
 
   if (pw->logo_pixmap)
     XFreePixmap (si->dpy, pw->logo_pixmap);
-  if (pw->logo_npixels && pw->logo_pixels)
-    XFreeColors (si->dpy, cmap, pw->logo_pixels, pw->logo_npixels, 0L);
   if (pw->logo_pixels)
-    free (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.)
@@ -816,6 +1083,55 @@ hp_lock_reset (saver_info *si, Bool 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,
@@ -887,52 +1203,45 @@ linux_lock_vt_switch (saver_info *si, Bool lock_p)
  */
 #ifdef HAVE_XF86VMODE
 
-static int ignore_all_errors_ehandler (Display *dpy, XErrorEvent *error);
-static Bool vp_got_error = False;
-
 static void
 xfree_lock_mode_switch (saver_info *si, Bool lock_p)
 {
-  static Bool mode_locked_p = False;
+  static Bool any_mode_locked_p = False;
   saver_preferences *p = &si->prefs;
-  int screen = 0;  /* always screen 0 */
+  int screen;
   int event, error;
   Bool status;
   XErrorHandler old_handler;
 
-  if (mode_locked_p == lock_p)
+  if (any_mode_locked_p == lock_p)
     return;
   if (!XF86VidModeQueryExtension (si->dpy, &event, &error))
     return;
 
-  XSync (si->dpy, False);
-  old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
-  status = XF86VidModeLockModeSwitch (si->dpy, screen, lock_p);
-  XSync (si->dpy, False);
-  XSetErrorHandler (old_handler);
-  if (vp_got_error) status = False;
-
-  if (status)
-    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: unable to %s mode switching!\n",
-             blurb(), (lock_p ? "lock" : "unlock"));
-  else if (p->verbose_p)
-    fprintf (stderr, "%s: %s mode switching.\n",
-             blurb(), (lock_p ? "locked" : "unlocked"));
-}
-
-static int
-ignore_all_errors_ehandler (Display *dpy, XErrorEvent *error)
-{
-  vp_got_error = True;
-  return 0;
+  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
@@ -945,40 +1254,47 @@ undo_vp_motion (saver_info *si)
 {
 #ifdef HAVE_XF86VMODE
   saver_preferences *p = &si->prefs;
-  int screen = 0;  /* always screen 0 */
-  saver_screen_info *ssi = &si->screens[screen];
-  int event, error, x, y;
-  Bool status;
+  int screen;
+  int event, error;
 
-  if (ssi->blank_vp_x == -1 && ssi->blank_vp_y == -1)
-    return;
   if (!XF86VidModeQueryExtension (si->dpy, &event, &error))
     return;
-  if (!XF86VidModeGetViewPort (si->dpy, 0, &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: unable to move vp from (%d,%d) back to (%d,%d)!\n",
-             blurb(), x, y, ssi->blank_vp_x, ssi->blank_vp_y);
-  else if (p->verbose_p)
-    fprintf (stderr, "%s: vp moved to (%d,%d); moved it back to (%d,%d).\n",
-             blurb(), x, y, ssi->blank_vp_x, ssi->blank_vp_y);
+  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 */
 }
 
@@ -1012,12 +1328,52 @@ 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_button (saver_info *si, XEvent *event)
+{
+  saver_preferences *p = &si->prefs;
+  Bool mouse_in_box = False;
+  Bool hit_p = False;
+  passwd_dialog_data *pw = si->pw_data;
+  saver_screen_info *ssi = pw->prompt_screen;
+
+  if (! pw->login_button_enabled_p)
+    return;
+
+  mouse_in_box = 
+    (event->xbutton.x >= pw->login_button_x &&
+     event->xbutton.x <= pw->login_button_x + pw->login_button_width &&
+     event->xbutton.y >= pw->login_button_y &&
+     event->xbutton.y <= pw->login_button_y + pw->login_button_height);
+
+  if (ButtonRelease == event->xany.type &&
+      pw->login_button_down_p &&
+      mouse_in_box)
+    {
+      /* Only allow them to press the button once: don't want to
+         accidentally launch a dozen gdm choosers if the machine
+         is being slow.
+       */
+      hit_p = True;
+      pw->login_button_enabled_p = False;
+    }
+
+  pw->login_button_down_p = (mouse_in_box &&
+                             ButtonRelease != event->xany.type);
+
+  update_passwd_window (si, 0, pw->ratio);
+
+  if (hit_p)
+    fork_and_exec (ssi, p->new_login_command);
+}
+
+
 static void
 handle_passwd_key (saver_info *si, XKeyEvent *event)
 {
@@ -1034,6 +1390,10 @@ handle_passwd_key (saver_info *si, XKeyEvent *event)
 
   s[1] = 0;
 
+  /* Add 10% to the time remaining every time a key is pressed. */
+  pw->ratio += 0.1;
+  if (pw->ratio > 1) pw->ratio = 1;
+
   switch (*s)
     {
     case '\010': case '\177':                          /* Backspace */
@@ -1065,23 +1425,41 @@ handle_passwd_key (saver_info *si, XKeyEvent *event)
       break;
 
     default:
-      i = strlen (typed_passwd);
-      if (i >= pw_size-1)
-       XBell (si->dpy, 0);
+      /* Though technically the only illegal characters in Unix passwords
+         are LF and NUL, most GUI programs (e.g., GDM) use regular text-entry
+         fields that only let you type printable characters.  So, people
+         who use funky characters in their passwords are already broken.
+         We follow that precedent.
+       */
+      if (isprint ((unsigned char) *s))
+        {
+          i = strlen (typed_passwd);
+          if (i >= pw_size-1)
+            XBell (si->dpy, 0);
+          else
+            {
+              typed_passwd [i] = *s;
+              typed_passwd [i+1] = 0;
+            }
+        }
       else
-       {
-         typed_passwd [i] = *s;
-         typed_passwd [i+1] = 0;
-       }
+        XBell (si->dpy, 0);
       break;
     }
 
-  i = strlen(typed_passwd);
-  stars = (char *) malloc(i+1);
-  memset (stars, '*', i);
-  stars[i] = 0;
-  update_passwd_window (si, stars, pw->ratio);
-  free (stars);
+  if (pw->show_stars_p)
+    {
+      i = strlen(typed_passwd);
+      stars = (char *) malloc(i+1);
+      memset (stars, '*', i);
+      stars[i] = 0;
+      update_passwd_window (si, stars, pw->ratio);
+      free (stars);
+    }
+  else
+    {
+      update_passwd_window (si, "", pw->ratio);
+    }
 }
 
 
@@ -1091,6 +1469,8 @@ passwd_event_loop (saver_info *si)
   saver_preferences *p = &si->prefs;
   char *msg = 0;
   XEvent event;
+  unsigned int caps_p = 0;
+
   passwd_animate_timer ((XtPointer) si, 0);
 
   while (si->pw_data && si->pw_data->state == pw_read)
@@ -1099,7 +1479,14 @@ passwd_event_loop (saver_info *si)
       if (event.xany.window == si->passwd_dialog && event.xany.type == Expose)
        draw_passwd_window (si);
       else if (event.xany.type == KeyPress)
-       handle_passwd_key (si, &event.xkey);
+        {
+          handle_passwd_key (si, &event.xkey);
+          caps_p = (event.xkey.state & LockMask);
+        }
+      else if ((event.xany.type == ButtonPress || 
+                event.xany.type == ButtonRelease) && 
+               si->pw_data->login_button_p)
+       handle_passwd_button (si, &event);
       else
        XtDispatchEvent (&event);
     }
@@ -1109,7 +1496,7 @@ passwd_event_loop (saver_info *si)
     case pw_ok:   msg = 0; break;
     case pw_null: msg = ""; break;
     case pw_time: msg = "Timed out!"; break;
-    default:      msg = "Sorry!"; break;
+    default:      msg = (caps_p ? "CapsLock?" : "Sorry!"); break;
     }
 
   if (si->pw_data->state == pw_fail)
@@ -1121,7 +1508,9 @@ passwd_event_loop (saver_info *si)
       case pw_ok:
        fprintf (stderr, "%s: password correct.\n", blurb()); break;
       case pw_fail:
-       fprintf (stderr, "%s: password incorrect!\n", blurb()); break;
+       fprintf (stderr, "%s: password incorrect!%s\n", blurb(),
+                 (caps_p ? "  (CapsLock)" : ""));
+        break;
       case pw_null:
       case pw_cancel:
        fprintf (stderr, "%s: password entry cancelled.\n", blurb()); break;
@@ -1225,8 +1614,6 @@ Bool
 unlock_p (saver_info *si)
 {
   saver_preferences *p = &si->prefs;
-  Screen *screen = si->default_screen->screen;
-  Colormap cmap = DefaultColormapOfScreen (screen);
   Bool status;
 
   raise_window (si, True, True, True);
@@ -1238,7 +1625,6 @@ unlock_p (saver_info *si)
     destroy_passwd_window (si);
 
   make_passwd_window (si);
-  if (cmap) XInstallColormap (si->dpy, cmap);
 
   compose_status = calloc (1, sizeof (*compose_status));
 
@@ -1251,9 +1637,6 @@ unlock_p (saver_info *si)
   free (compose_status);
   compose_status = 0;
 
-  cmap = si->default_screen->cmap;
-  if (cmap) XInstallColormap (si->dpy, cmap);
-
   return status;
 }
 
@@ -1272,6 +1655,9 @@ set_locked_p (saver_info *si, Bool locked_p)
 #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 */
 }