ftp://ftp.linux.ncsu.edu/mirror/ftp.redhat.com/pub/redhat/linux/enterprise/4/en/os...
[xscreensaver] / driver / lock.c
index 0d4c43e3ff663f5c393dad71073d48f1675ca93f..2aa61f637daa8f9178ee7b31521651f7c9c7d57e 100644 (file)
@@ -1,5 +1,5 @@
 /* lock.c --- handling the password dialog for locking-mode.
- * xscreensaver, Copyright (c) 1993-1997 Jamie Zawinski <jwz@netscape.com>
+ * 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/StringDefs.h>
 #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
 
 #ifndef VMS
 # include <pwd.h>
 #else /* VMS */
+
 extern char *getenv(const char *name);
 extern int validate_user(char *name, char *password);
-static char * user_vms;
+
+static Bool
+vms_passwd_valid_p(char *pw, Bool verbose_p)
+{
+  return (validate_user (getenv("USER"), typed_passwd) == 1);
+}
+# undef passwd_valid_p
+# define passwd_valid_p vms_passwd_valid_p
+
 #endif /* VMS */
 
 
-#ifdef HAVE_ATHENA
+#undef MAX
+#define MAX(a,b) ((a)>(b)?(a):(b))
 
-# include <X11/Shell.h>
-# include <X11/StringDefs.h>
-# include <X11/Xaw/Text.h>
-# include <X11/Xaw/Label.h>
-# include <X11/Xaw/Dialog.h>
+enum passwd_state { pw_read, pw_ok, pw_null, pw_fail, pw_cancel, pw_time };
 
-static void passwd_done_cb (Widget, XtPointer, XtPointer);
-static XtActionsRec actionsList[] =
-{
-    {"passwdentered", (XtActionProc) passwd_done_cb},
-};
+struct passwd_dialog_data {
 
-static char Translations[] =
-"\
-<Key>Return:   passwdentered()\
-";
+  saver_screen_info *prompt_screen;
+  int previous_mouse_x, previous_mouse_y;
 
-#else  /* HAVE_MOTIF */
+  enum passwd_state state;
+  char typed_passwd [80];
+  XtIntervalId timer;
+  int i_beam;
 
-# include <Xm/Xm.h>
-# include <Xm/List.h>
-# include <Xm/TextF.h>
+  float ratio;
+  Position x, y;
+  Dimension width;
+  Dimension height;
+  Dimension border_width;
 
-#endif /* HAVE_MOTIF */
+  char *heading_label;
+  char *body_label;
+  char *user_label;
+  char *passwd_label;
+  char *date_label;
+  char *user_string;
+  char *passwd_string;
 
-extern Widget passwd_dialog;
-extern Widget passwd_form;
-extern Widget roger_label;
-extern Widget passwd_label1;
-extern Widget passwd_label3;
-extern Widget passwd_cancel;
+  XFontStruct *heading_font;
+  XFontStruct *body_font;
+  XFontStruct *label_font;
+  XFontStruct *passwd_font;
+  XFontStruct *date_font;
 
-#ifdef HAVE_MOTIF
-extern Widget passwd_text;
-extern Widget passwd_done;
-#else  /* HAVE_ATHENA */
-static Widget passwd_text = 0; /* gag... */
-static Widget passwd_done = 0;
-#endif /* HAVE_ATHENA */
+  Pixel foreground;
+  Pixel background;
+  Pixel passwd_foreground;
+  Pixel passwd_background;
+  Pixel thermo_foreground;
+  Pixel thermo_background;
+  Pixel shadow_top;
+  Pixel shadow_bottom;
 
+  Dimension logo_width;
+  Dimension logo_height;
+  Dimension thermo_width;
+  Dimension internal_border;
+  Dimension shadow_width;
 
+  Dimension passwd_field_x, passwd_field_y;
+  Dimension passwd_field_width, passwd_field_height;
 
-static enum { pw_read, pw_ok, pw_fail, pw_cancel, pw_time } passwd_state;
-static char typed_passwd [80];
+  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);
 
-\f
-#if defined(HAVE_ATHENA) || (XmVersion >= 1002)
-   /* The `destroy' bug apears to be fixed as of Motif 1.2.1, but
-      the `verify-callback' bug is still present. */
-# define DESTROY_WORKS
-#endif
 
 static void
-passwd_cancel_cb (Widget button, XtPointer client_data, XtPointer call_data)
+make_passwd_window (saver_info *si)
 {
-  passwd_state = pw_cancel;
+  struct passwd *p = getpwuid (getuid ());
+  XSetWindowAttributes attrs;
+  unsigned long attrmask = 0;
+  passwd_dialog_data *pw = (passwd_dialog_data *) calloc (1, sizeof(*pw));
+  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;
+
+  pw->heading_label = get_string_resource ("passwd.heading.label",
+                                          "Dialog.Label.Label");
+  pw->body_label = get_string_resource ("passwd.body.label",
+                                       "Dialog.Label.Label");
+  pw->user_label = get_string_resource ("passwd.user.label",
+                                       "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");
+  if (!pw->body_label)
+    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. */
+  {
+    char *s = (char *) malloc (strlen(pw->heading_label) + 20);
+    sprintf(s, pw->heading_label, si->version);
+    free (pw->heading_label);
+    pw->heading_label = s;
+  }
+
+  pw->user_string = strdup (p && p->pw_name ? p->pw_name : "???");
+  pw->passwd_string = strdup("");
+
+  f = get_string_resource ("passwd.headingFont", "Dialog.Font");
+  pw->heading_font = XLoadQueryFont (si->dpy, (f ? f : "fixed"));
+  if (!pw->heading_font) pw->heading_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");
+  if (f) free (f);
+
+  f = get_string_resource("passwd.labelFont", "Dialog.Font");
+  pw->label_font = XLoadQueryFont (si->dpy, (f ? f : "fixed"));
+  if (!pw->label_font) pw->label_font = XLoadQueryFont (si->dpy, "fixed");
+  if (f) free (f);
+
+  f = get_string_resource("passwd.passwdFont", "Dialog.Font");
+  pw->passwd_font = XLoadQueryFont (si->dpy, (f ? f : "fixed"));
+  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);
+  pw->background = get_pixel_resource ("passwd.background",
+                                      "Dialog.Background",
+                                      si->dpy, cmap);
+
+  if (pw->foreground == pw->background)
+    {
+      /* Make sure the error messages show up. */
+      pw->foreground = BlackPixelOfScreen (screen);
+      pw->background = WhitePixelOfScreen (screen);
+    }
+
+  pw->passwd_foreground = get_pixel_resource ("passwd.text.foreground",
+                                             "Dialog.Text.Foreground",
+                                             si->dpy, cmap);
+  pw->passwd_background = get_pixel_resource ("passwd.text.background",
+                                             "Dialog.Text.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);
+  pw->shadow_bottom = get_pixel_resource ("passwd.bottomShadowColor",
+                                         "Dialog.Background",
+                                         si->dpy, cmap);
+
+  pw->logo_width = get_integer_resource ("passwd.logo.width",
+                                        "Dialog.Logo.Width");
+  pw->logo_height = get_integer_resource ("passwd.logo.height",
+                                         "Dialog.Logo.Height");
+  pw->thermo_width = get_integer_resource ("passwd.thermometer.width",
+                                          "Dialog.Thermometer.Width");
+  pw->internal_border = get_integer_resource ("passwd.internalBorderWidth",
+                                             "Dialog.InternalBorderWidth");
+  pw->shadow_width = get_integer_resource ("passwd.shadowThickness",
+                                          "Dialog.ShadowThickness");
+
+  if (pw->logo_width == 0)  pw->logo_width = 150;
+  if (pw->logo_height == 0) pw->logo_height = 150;
+  if (pw->internal_border == 0) pw->internal_border = 15;
+  if (pw->shadow_width == 0) pw->shadow_width = 4;
+  if (pw->thermo_width == 0) pw->thermo_width = pw->shadow_width;
+
+  {
+    int direction, ascent, descent;
+    XCharStruct overall;
+
+    pw->width = 0;
+    pw->height = 0;
+
+    /* Measure the heading_label. */
+    XTextExtents (pw->heading_font,
+                 pw->heading_label, strlen(pw->heading_label),
+                 &direction, &ascent, &descent, &overall);
+    if (overall.width > pw->width) pw->width = overall.width;
+    pw->height += ascent + descent;
+
+    /* Measure the body_label. */
+    XTextExtents (pw->body_font,
+                 pw->body_label, strlen(pw->body_label),
+                 &direction, &ascent, &descent, &overall);
+    if (overall.width > pw->width) pw->width = overall.width;
+    pw->height += ascent + descent;
+
+    {
+      Dimension w2 = 0, w3 = 0;
+      Dimension h2 = 0, h3 = 0;
+      const char *passwd_string = "MMMMMMMMMMMM";
+
+      /* Measure the user_label. */
+      XTextExtents (pw->label_font,
+                   pw->user_label, strlen(pw->user_label),
+                   &direction, &ascent, &descent, &overall);
+      if (overall.width > w2)  w2 = overall.width;
+      h2 += ascent + descent;
+
+      /* Measure the passwd_label. */
+      XTextExtents (pw->label_font,
+                   pw->passwd_label, strlen(pw->passwd_label),
+                   &direction, &ascent, &descent, &overall);
+      if (overall.width > w2)  w2 = overall.width;
+      h2 += ascent + descent;
+
+      /* Measure the user_string. */
+      XTextExtents (pw->passwd_font,
+                   pw->user_string, strlen(pw->user_string),
+                   &direction, &ascent, &descent, &overall);
+      overall.width += (pw->shadow_width * 4);
+      ascent += (pw->shadow_width * 4);
+      if (overall.width > w3)  w3 = overall.width;
+      h3 += ascent + descent;
+
+      /* Measure the (maximally-sized, dummy) passwd_string. */
+      XTextExtents (pw->passwd_font,
+                   passwd_string, strlen(passwd_string),
+                   &direction, &ascent, &descent, &overall);
+      overall.width += (pw->shadow_width * 4);
+      ascent += (pw->shadow_width * 4);
+      if (overall.width > w3)  w3 = overall.width;
+      h3 += ascent + descent;
+
+      w2 = w2 + w3 + (pw->shadow_width * 2);
+      h2 = MAX (h2, h3);
+
+      if (w2 > pw->width)  pw->width  = w2;
+      pw->height += h2;
+    }
+
+    pw->width  += (pw->internal_border * 2);
+    pw->height += (pw->internal_border * 4);
+
+    pw->width += pw->thermo_width + (pw->shadow_width * 3);
+
+    if (pw->logo_height > pw->height)
+      pw->height = pw->logo_height;
+    else if (pw->height > pw->logo_height)
+      pw->logo_height = pw->height;
+
+    pw->logo_width = pw->logo_height;
+
+    pw->width += pw->logo_width;
+  }
+
+  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. */
+  {
+    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;
+    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;
+  }
+
+  pw->border_width = get_integer_resource ("passwd.borderWidth",
+                                           "Dialog.BorderWidth");
+
+  si->passwd_dialog =
+    XCreateWindow (si->dpy,
+                  RootWindowOfScreen(screen),
+                  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);
 }
 
+
 static void
-passwd_done_cb (Widget button, XtPointer client_data, XtPointer call_data)
+draw_passwd_window (saver_info *si)
 {
-  if (passwd_state != pw_read) return; /* already done */
-#ifndef VMS
+  passwd_dialog_data *pw = si->pw_data;
+  XGCValues gcv;
+  GC gc1, gc2;
+  int spacing, height;
+  int x1, x2, x3, y1, y2;
+  int sw;
+  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;
+  if (spacing < 0) spacing = 0;
+
+  gcv.foreground = pw->foreground;
+  gc1 = XCreateGC (si->dpy, si->passwd_dialog, GCForeground, &gcv);
+  gc2 = XCreateGC (si->dpy, si->passwd_dialog, GCForeground, &gcv);
+  x1 = pw->logo_width + pw->thermo_width + (pw->shadow_width * 3);
+  x3 = pw->width - (pw->shadow_width * 2);
+  y1 = (pw->shadow_width * 2) + spacing + spacing;
+
+  /* top heading
+   */
+  XSetFont (si->dpy, gc1, pw->heading_font->fid);
+  sw = string_width (pw->heading_font, pw->heading_label);
+  x2 = (x1 + ((x3 - x1 - sw) / 2));
+  y1 += spacing + pw->heading_font->ascent + pw->heading_font->descent;
+  XDrawString (si->dpy, si->passwd_dialog, gc1, x2, y1,
+              pw->heading_label, strlen(pw->heading_label));
+
+  /* text below top heading
+   */
+  XSetFont (si->dpy, gc1, pw->body_font->fid);
+  y1 += spacing + pw->body_font->ascent + pw->body_font->descent;
+  sw = string_width (pw->body_font, pw->body_label);
+  x2 = (x1 + ((x3 - x1 - sw) / 2));
+  XDrawString (si->dpy, si->passwd_dialog, gc1, x2, y1,
+              pw->body_label, strlen(pw->body_label));
 
-# ifdef HAVE_ATHENA
-  strncpy(typed_passwd, XawDialogGetValueString(passwd_form),
-         sizeof(typed_passwd)-1);
-  typed_passwd[sizeof(typed_passwd)-1] = 0;
-# endif /* HAVE_ATHENA */
-  if (passwd_valid_p (typed_passwd))
-    passwd_state = pw_ok;
-  else
-    passwd_state = pw_fail;
-
-#else  /* VMS */
-  user_vms = getenv("USER");
-  if (validate_user(user_vms,typed_passwd) == 1) 
-    passwd_state = pw_ok;
-  else 
-    passwd_state = pw_fail;
-#endif /* VMS */
-}
 
-#if defined(HAVE_MOTIF) && defined(VERIFY_CALLBACK_WORKS)
+  tb_height = (pw->passwd_font->ascent + pw->passwd_font->descent +
+              (pw->shadow_width * 4));
+
+  /* the "User:" prompt
+   */
+  y1 += spacing;
+  y2 = y1;
+  XSetForeground (si->dpy, gc1, pw->foreground);
+  XSetFont (si->dpy, gc1, pw->label_font->fid);
+  y1 += (spacing + tb_height);
+  x2 = (x1 + pw->internal_border +
+       MAX(string_width (pw->label_font, pw->user_label),
+           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,
+              pw->user_label, strlen(pw->user_label));
+
+  /* the "Password:" prompt
+   */
+  y1 += (spacing + tb_height);
+  XDrawString (si->dpy, si->passwd_dialog, gc1,
+              x2 - string_width (pw->label_font, pw->passwd_label),
+              y1,
+              pw->passwd_label, strlen(pw->passwd_label));
 
-  /* It looks to me like adding any modifyVerify callback causes
-     Motif 1.1.4 to free the the TextF_Value() twice.  I can't see
-     the bug in the Motif source, but Purify complains, even if
-     check_passwd_cb() is a no-op.
 
-     Update: Motif 1.2.1 also loses, but in a different way: it
-     writes beyond the end of a malloc'ed block in ModifyVerify().
-     Probably this block is the text field's text.
+  XSetForeground (si->dpy, gc2, pw->passwd_background);
+
+  /* the "user name" text field
+   */
+  y1 = y2;
+  XSetForeground (si->dpy, gc1, pw->passwd_foreground);
+  XSetFont (si->dpy, gc1, pw->passwd_font->fid);
+  y1 += (spacing + tb_height);
+  x2 += (pw->shadow_width * 4);
+
+  pw->passwd_field_width = x3 - x2 - pw->internal_border;
+  pw->passwd_field_height = (pw->passwd_font->ascent +
+                            pw->passwd_font->descent +
+                            pw->shadow_width);
+
+  XFillRectangle (si->dpy, si->passwd_dialog, gc2,
+                 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,
+              pw->user_string, strlen(pw->user_string));
+
+  /* the "password" text field
    */
+  y1 += (spacing + tb_height);
 
-static void 
-check_passwd_cb (Widget button, XtPointer client_data, XtPointer call_data)
-{
-  XmTextVerifyCallbackStruct *vcb = (XmTextVerifyCallbackStruct *) call_data;
+  pw->passwd_field_x = x2 - pw->shadow_width;
+  pw->passwd_field_y = y1 - (pw->passwd_font->ascent +
+                            pw->passwd_font->descent);
 
-  if (passwd_state != pw_read)
-    return;
-  else if (vcb->reason == XmCR_ACTIVATE)
+  /* The shadow around the text fields
+   */
+  y1 = y2;
+  y1 += (spacing + (pw->shadow_width * 3));
+  x1 = x2 - (pw->shadow_width * 2);
+  x2 = pw->passwd_field_width + (pw->shadow_width * 2);
+  y2 = pw->passwd_field_height + (pw->shadow_width * 2);
+
+  draw_shaded_rectangle (si->dpy, si->passwd_dialog,
+                        x1, y1, x2, y2,
+                        pw->shadow_width,
+                        pw->shadow_bottom, pw->shadow_top);
+
+  y1 += (spacing + pw->passwd_font->ascent + pw->passwd_font->descent +
+        (pw->shadow_width * 4));
+  draw_shaded_rectangle (si->dpy, si->passwd_dialog,
+                        x1, y1, x2, y2,
+                        pw->shadow_width,
+                        pw->shadow_bottom, pw->shadow_top);
+
+
+  /* The date, below the text fields
+   */
+  {
+    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));
+  }
+
+
+  /* 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)
     {
-      passwd_done_cb (0, 0, 0);
+      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));
     }
-  else if (vcb->text->length > 1)      /* don't allow "paste" operations */
+
+  /* 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 * 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 * 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);
+
+  /* The shadow around the thermometer
+   */
+  draw_shaded_rectangle (si->dpy, si->passwd_dialog,
+                        pw->logo_width,
+                        pw->shadow_width * 4,
+                        pw->thermo_width + (pw->shadow_width * 2),
+                        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->thermo_field_x, pw->thermo_field_y,
+                  pw->thermo_width - 1, pw->thermo_field_height - 1);
+#endif
+
+  /* The shadow around the whole window
+   */
+  draw_shaded_rectangle (si->dpy, si->passwd_dialog,
+                        0, 0, pw->width, pw->height, pw->shadow_width,
+                        pw->shadow_top, pw->shadow_bottom);
+
+  XFreeGC (si->dpy, gc1);
+  XFreeGC (si->dpy, gc2);
+
+  update_passwd_window (si, pw->passwd_string, pw->ratio);
+}
+
+
+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;
+  gcv.font = pw->passwd_font->fid;
+  gc1 = XCreateGC (si->dpy, si->passwd_dialog, GCForeground|GCFont, &gcv);
+  gcv.foreground = pw->passwd_background;
+  gc2 = XCreateGC (si->dpy, si->passwd_dialog, GCForeground, &gcv);
+
+  if (printed_passwd)
     {
-      vcb->doit = False;
+      char *s = strdup (printed_passwd);
+      if (pw->passwd_string) free (pw->passwd_string);
+      pw->passwd_string = s;
     }
-  else if (vcb->text->ptr != 0)
+
+  /* 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,
+                  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,
+               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)
     {
-      int i;
-      int L = vcb->text->length;
-      if (L >= sizeof(typed_passwd))
-       L = sizeof(typed_passwd)-1;
-      strncat (typed_passwd, vcb->text->ptr, L);
-      typed_passwd [vcb->endPos + L] = 0;
-      for (i = 0; i < vcb->text->length; i++)
-       vcb->text->ptr [i] = '*';
+      x = (rects[0].x + pw->shadow_width +
+          string_width (pw->passwd_font, pw->passwd_string));
+      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);
     }
-}
 
-# else /* HAVE_ATHENA || !VERIFY_CALLBACK_WORKS */
-
-static void keypress (Widget w, XEvent *event, String *av, Cardinal *ac);
-static void backspace (Widget w, XEvent *event, String *av, Cardinal *ac);
-static void kill_line (Widget w, XEvent *event, String *av, Cardinal *ac);
-static void done (Widget w, XEvent *event, String *av, Cardinal *ac);
-
-static XtActionsRec actions[] = {{"keypress",  keypress},
-                                {"backspace", backspace},
-                                {"kill_line", kill_line},
-                                {"done",      done}
-                               };
-
-# ifdef HAVE_MOTIF
-#  if 0  /* oh fuck, why doesn't this work? */
-static char translations[] = "\
-<Key>BackSpace:                backspace()\n\
-<Key>Delete:           backspace()\n\
-Ctrl<Key>H:            backspace()\n\
-Ctrl<Key>U:            kill_line()\n\
-Ctrl<Key>X:            kill_line()\n\
-Ctrl<Key>J:            done()\n\
-Ctrl<Key>M:            done()\n\
-<Key>:                 keypress()\n\
-";
-#  else  /* !0 */
-static char translations[] = "<Key>:keypress()";
-#  endif /* !0 */
-# endif /* HAVE_MOTIF */
+  pw->i_beam = (pw->i_beam + 1) % 4;
 
 
-static void
-text_field_set_string (Widget widget, char *text, int position)
-{
-#ifdef HAVE_MOTIF
-  XmTextFieldSetString (widget, text);
-  XmTextFieldSetInsertionPosition (widget, position);
-
-#else /* HAVE_ATHENA */
-  char *buf;
-  int end_pos;
-
-  XawTextBlock block;
-  block.firstPos = 0;
-  block.length = strlen (text);
-  block.ptr = text;
-  block.format = 0;
-  if (block.length == 0)
+  /* the thermometer
+   */
+  y = (pw->thermo_field_height - 2) * (1.0 - pw->ratio);
+  if (y > 0)
     {
-      buf = XawDialogGetValueString(passwd_form);
-      if (buf)
-       end_pos = strlen(buf);
-      else
-       end_pos = -1;
+      XFillRectangle (si->dpy, si->passwd_dialog, gc2,
+                     pw->thermo_field_x + 1,
+                     pw->thermo_field_y + 1,
+                     pw->thermo_width-2,
+                     y);
+      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,
+                     pw->thermo_width-2,
+                     MAX (0, pw->thermo_field_height - y - 2));
     }
-  XawTextReplace (widget, 0, end_pos, &block);
-  XawTextSetInsertionPoint (widget, position);
-#endif /* HAVE_ATHENA */
+
+  XFreeGC (si->dpy, gc1);
+  XFreeGC (si->dpy, gc2);
+  XSync (si->dpy, False);
 }
 
 
 static void
-keypress (Widget w, XEvent *event, String *argv, Cardinal *argc)
+destroy_passwd_window (saver_info *si)
 {
-  int i, j;
-  char s [sizeof(typed_passwd)];
-  int size = XLookupString ((XKeyEvent *) event, s, sizeof(s)-1, 0, 0);
-  if (size != 1) return;
+  saver_preferences *p = &si->prefs;
+  passwd_dialog_data *pw = si->pw_data;
+  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));
 
-  /* hack because I can't get translations to dance to my tune... */
-  if (*s == '\010') { backspace (w, event, argv, argc); return; }
-  if (*s == '\177') { backspace (w, event, argv, argc); return; }
-  if (*s == '\025') { kill_line (w, event, argv, argc); return; }
-  if (*s == '\030') { kill_line (w, event, argv, argc); return; }
-  if (*s == '\012') { done (w, event, argv, argc); return; }
-  if (*s == '\015') { done (w, event, argv, argc); return; }
+  if (pw->timer)
+    XtRemoveTimeOut (pw->timer);
 
-  i = j = strlen (typed_passwd);
+  move_mouse_grab (si, RootWindowOfScreen (ssi->screen),
+                   ssi->cursor, ssi->number);
 
-  if (i >= (sizeof(typed_passwd)-1))
+  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)
     {
-      XBell(XtDisplay(w), 0);
-      return;
+      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);
+  if (pw->background != black && pw->background != white)
+    XFreeColors (si->dpy, cmap, &pw->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)
+    XFreeColors (si->dpy, cmap, &pw->passwd_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;
     }
 
-  typed_passwd [i] = *s;
-  s [++i] = 0;
-  while (i--)
-    s [i] = '*';
+  if (pw->save_under)
+    XFreePixmap (si->dpy, pw->save_under);
+
+  if (cmap)
+    XInstallColormap (si->dpy, cmap);
 
-  text_field_set_string (passwd_text, s, j + 1);
+  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
-backspace (Widget w, XEvent *event, String *argv, Cardinal *argc)
+hp_lock_reset (saver_info *si, Bool lock_p)
 {
-  char s [sizeof(typed_passwd)];
-  int i = strlen (typed_passwd);
-  int j = i;
-  if (i == 0)
+  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;
-  typed_passwd [--i] = 0;
-  s [i] = 0;
-  while (i--)
-    s [i] = '*';
 
-  text_field_set_string (passwd_text, s, j + 1);
+  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
-kill_line (Widget w, XEvent *event, String *argv, Cardinal *argc)
+xfree_lock_grab_smasher (saver_info *si, Bool lock_p)
 {
-  memset (typed_passwd, 0, sizeof(typed_passwd));
-  text_field_set_string (passwd_text, "", 0);
+  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
-done (Widget w, XEvent *event, String *argv, Cardinal *argc)
+linux_lock_vt_switch (saver_info *si, Bool lock_p)
 {
-  passwd_done_cb (w, 0, 0);
-}
+  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;
 
-#endif /* HAVE_ATHENA || !VERIFY_CALLBACK_WORKS */
+  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 */
 
-extern void skull (Display *, Window, GC, GC, int, int, int, int);
+\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
-roger (Widget button, XtPointer client_data, XtPointer call_data)
+xfree_lock_mode_switch (saver_info *si, Bool lock_p)
 {
-  Display *dpy = XtDisplay (button);
-  Screen *screen = XtScreen (button);
-  Window window = XtWindow (button);
-  Arg av [10];
-  int ac = 0;
-  XGCValues gcv;
-  Colormap cmap;
-  GC draw_gc, erase_gc;
-  unsigned int fg, bg;
-  int x, y, size;
-  XWindowAttributes xgwa;
-  XGetWindowAttributes (dpy, window, &xgwa);
-  cmap = xgwa.colormap;
-  if (xgwa.width > xgwa.height) size = xgwa.height;
-  else size = xgwa.width;
-  if (size > 40) size -= 30;
-  x = (xgwa.width - size) / 2;
-  y = (xgwa.height - size) / 2;
-  XtSetArg (av [ac], XtNforeground, &fg); ac++;
-  XtSetArg (av [ac], XtNbackground, &bg); ac++;
-  XtGetValues (button, av, ac);
-  /* if it's black on white, swap it cause it looks better (hack hack) */
-  if (fg == BlackPixelOfScreen (screen) && bg == WhitePixelOfScreen (screen))
-    fg = WhitePixelOfScreen (screen), bg = BlackPixelOfScreen (screen);
-  gcv.foreground = bg;
-  erase_gc = XCreateGC (dpy, window, GCForeground, &gcv);
-  gcv.foreground = fg;
-  draw_gc = XCreateGC (dpy, window, GCForeground, &gcv);
-  XFillRectangle (dpy, window, erase_gc, 0, 0, xgwa.width, xgwa.height);
-  skull (dpy, window, draw_gc, erase_gc, x, y, size, size);
-  XFreeGC (dpy, draw_gc);
-  XFreeGC (dpy, erase_gc);
+  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
-make_passwd_dialog (saver_info *si)
+undo_vp_motion (saver_info *si)
 {
-  char *username = 0;
-  saver_screen_info *ssi = si->default_screen;
-  Widget parent = ssi->toplevel_shell;
+#ifdef HAVE_XF86VMODE
+  saver_preferences *p = &si->prefs;
+  int screen;
+  int event, error;
 
-  if (ssi->demo_cmap &&
-      ssi->demo_cmap != ssi->cmap &&
-      ssi->demo_cmap != DefaultColormapOfScreen (ssi->screen))
+  if (!XF86VidModeQueryExtension (si->dpy, &event, &error))
+    return;
+
+  for (screen = 0; screen < si->nscreens; screen++)
     {
-      XFreeColormap (si->dpy, ssi->demo_cmap);
-      ssi->demo_cmap = 0;
+      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 */
+}
 
-  if (ssi->default_visual == DefaultVisualOfScreen (ssi->screen))
-    ssi->demo_cmap = DefaultColormapOfScreen (ssi->screen);
-  else
-    ssi->demo_cmap = XCreateColormap (si->dpy,
-                                    RootWindowOfScreen (ssi->screen),
-                                    ssi->default_visual, AllocNone);
 
-  create_passwd_dialog (parent, ssi->default_visual, ssi->demo_cmap);
+\f
+/* Interactions
+ */
+
+static void
+passwd_animate_timer (XtPointer closure, XtIntervalId *id)
+{
+  saver_info *si = (saver_info *) closure;
+  int tick = 166;
+  passwd_dialog_data *pw = si->pw_data;
 
-#ifdef HAVE_ATHENA
+  if (!pw) return;
 
-  XtVaSetValues(passwd_form, XtNvalue, typed_passwd, 0);
+  pw->ratio -= (1.0 / ((double) si->prefs.passwd_timeout / (double) tick));
+  if (pw->ratio < 0)
+    {
+      pw->ratio = 0;
+      if (pw->state == pw_read)
+       pw->state = pw_time;
+    }
 
-  XawDialogAddButton(passwd_form,"ok", passwd_done_cb, 0);
-  XawDialogAddButton(passwd_form,"cancel", passwd_cancel_cb, 0);
-  passwd_done = XtNameToWidget(passwd_form,"ok");
-  passwd_text = XtNameToWidget(passwd_form,"value");
+  update_passwd_window (si, 0, pw->ratio);
 
-  XtAppAddActions(XtWidgetToApplicationContext(passwd_text), 
-                 actionsList, XtNumber(actionsList));
-  XtOverrideTranslations(passwd_text, XtParseTranslationTable(Translations));
+  if (pw->state == pw_read)
+    pw->timer = XtAppAddTimeOut (si->app, tick, passwd_animate_timer,
+                                (XtPointer) si);
+  else
+    pw->timer = 0;
 
-#else  /* HAVE_MOTIF */
+  idle_timer ((XtPointer) si, 0);
+}
 
-  XtAddCallback (passwd_done, XmNactivateCallback, passwd_done_cb, 0);
-  XtAddCallback (passwd_cancel, XmNactivateCallback, passwd_cancel_cb, 0);
-  XtAddCallback (roger_label, XmNexposeCallback, roger, 0);
 
-# ifdef VERIFY_CALLBACK_WORKS
-  XtAddCallback (passwd_text, XmNmodifyVerifyCallback, check_passwd_cb, 0);
-  XtAddCallback (passwd_text, XmNactivateCallback, check_passwd_cb, 0);
-# else
-  XtAddCallback (passwd_text, XmNactivateCallback, passwd_done_cb, 0);
-  XtOverrideTranslations (passwd_text, XtParseTranslationTable (translations));
-# endif
+static XComposeStatus *compose_status;
 
-# if defined(HAVE_MOTIF) && (XmVersion >= 1002)
-  /* The focus stuff changed around; this didn't exist in 1.1.5. */
-  XtVaSetValues (passwd_form, XmNinitialFocus, passwd_text, 0);
-# endif
+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, compose_status);
 
-  /* Another random thing necessary in 1.2.1 but not 1.1.5... */
-  XtVaSetValues (roger_label, XmNborderWidth, 2, 0);
+  if (size != 1) return;
 
-#endif /* HAVE_MOTIF */
+  s[1] = 0;
 
-#ifndef VMS
-  {
-    struct passwd *pw = getpwuid (getuid ());
-    username = pw->pw_name;
-  }
-#else  /* VMS -- from "R.S.Niranjan" <U00C782%BRKVC1@navistar.com> who says
-                that on OpenVMS 6.1, using `struct passwd' crashes... */
-  username = getenv("USER");
-#endif /* VMS */
+  switch (*s)
+    {
+    case '\010': case '\177':                          /* Backspace */
+      if (!*typed_passwd)
+       XBell (si->dpy, 0);
+      else
+       typed_passwd [strlen(typed_passwd)-1] = 0;
+      break;
+
+    case '\025': case '\030':                          /* Erase line */
+      memset (typed_passwd, 0, pw_size);
+      break;
+
+    case '\012': case '\015':                          /* Enter */
+      if (pw->state != pw_read)
+       ;  /* already done? */
+      else if (typed_passwd[0] == 0)
+       pw->state = pw_null;
+      else
+        {
+          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:
+      i = strlen (typed_passwd);
+      if (i >= pw_size-1)
+       XBell (si->dpy, 0);
+      else
+       {
+         typed_passwd [i] = *s;
+         typed_passwd [i+1] = 0;
+       }
+      break;
+    }
 
-  format_into_label (passwd_label1, si->version);
-  format_into_label (passwd_label3, (username ? username : "???"));
+  i = strlen(typed_passwd);
+  stars = (char *) malloc(i+1);
+  memset (stars, '*', i);
+  stars[i] = 0;
+  update_passwd_window (si, stars, pw->ratio);
+  free (stars);
 }
 
-static int passwd_idle_timer_tick = -1;
-static XtIntervalId passwd_idle_id;
 
 static void
-passwd_idle_timer (XtPointer closure, XtIntervalId *id)
+passwd_event_loop (saver_info *si)
 {
-  saver_info *si = (saver_info *) closure;
   saver_preferences *p = &si->prefs;
+  char *msg = 0;
+  XEvent event;
+  passwd_animate_timer ((XtPointer) si, 0);
 
-  Display *dpy = XtDisplay (passwd_form);
-#ifdef HAVE_ATHENA
-  Window window = XtWindow (passwd_form);
-#else  /* MOTIF */
-  Window window = XtWindow (XtParent(passwd_done));
-#endif /* MOTIF */
-  static Dimension x, y, d, s, ss;
-  static GC gc = 0;
-  int max = p->passwd_timeout / 1000;
-
-  idle_timer ((XtPointer) si, id);
+  while (si->pw_data && si->pw_data->state == pw_read)
+    {
+      XtAppNextEvent (si->app, &event);
+      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);
+      else
+       XtDispatchEvent (&event);
+    }
 
-  if (passwd_idle_timer_tick == max)  /* first time */
+  switch (si->pw_data->state)
     {
-      XGCValues gcv;
-#ifdef HAVE_MOTIF
-      unsigned long fg = 0, bg = 0, ts = 0, bs = 0;
-      Dimension w = 0, h = 0;
-      XtVaGetValues(XtParent(passwd_done),
-                   XmNwidth, &w,
-                   0);
-      XtVaGetValues(passwd_done,
-                   XmNheight, &h,
-                   XmNy, &y,
-                   XtNforeground, &fg,
-                   XtNbackground, &bg,
-                   XmNtopShadowColor, &ts,
-                   XmNbottomShadowColor, &bs,
-                   0);
-
-      if (ts != bg && ts != fg)
-       fg = ts;
-      if (bs != bg && bs != fg)
-       fg = bs;
-
-      d = h / 2;
-      if (d & 1) d++;
-
-      x = (w / 2);
-
-      x -= d/2;
-      y += d/2;
-
-#else  /* HAVE_ATHENA */
-
-      Arg av [100];
-      int ac = 0;
-      unsigned long fg = 0, bg = 0;
-      XtSetArg (av [ac], XtNheight, &d); ac++;
-      XtGetValues (passwd_done, av, ac);
-      ac = 0;
-      XtSetArg (av [ac], XtNwidth, &x); ac++;
-      XtSetArg (av [ac], XtNheight, &y); ac++;
-      XtSetArg (av [ac], XtNforeground, &fg); ac++;
-      XtSetArg (av [ac], XtNbackground, &bg); ac++;
-      XtGetValues (passwd_form, av, ac);
-      x -= d;
-      y -= d;
-      d -= 4;
-
-#endif /* HAVE_ATHENA */
-
-      gcv.foreground = fg;
-      if (gc) XFreeGC (dpy, gc);
-      gc = XCreateGC (dpy, window, GCForeground, &gcv);
-      s = 360*64 / (passwd_idle_timer_tick - 1);
-      ss = 90*64;
-      XFillArc (dpy, window, gc, x, y, d, d, 0, 360*64);
-      XSetForeground (dpy, gc, bg);
-      x += 1;
-      y += 1;
-      d -= 2;
+    case pw_ok:   msg = 0; break;
+    case pw_null: msg = ""; break;
+    case pw_time: msg = "Timed out!"; break;
+    default:      msg = "Sorry!"; break;
     }
 
-  if (--passwd_idle_timer_tick)
+  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)
     {
-      passwd_idle_id = XtAppAddTimeOut (si->app, 1000, passwd_idle_timer,
-                                       (XtPointer) si);
-      XFillArc (dpy, window, gc, x, y, d, d, ss, s);
-      ss += s;
+      /* 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);
 
-#ifdef HAVE_ATHENA
+  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);
 
-void
-pop_up_athena_dialog_box (Widget parent, Widget focus, Widget dialog,
-                         Widget form, int where)
-{
-  /* modified from demo.c */
-  /* I'm sure this is the wrong way to pop up a dialog box, but I can't
-     figure out how else to do it.
-
-     It's important that the screensaver dialogs not get decorated or
-     otherwise reparented by the window manager, because they need to be
-     children of the *real* root window, not the WM's virtual root, in
-     order for us to guarentee that they are visible above the screensaver
-     window itself.
-   */
-  Arg av [100];
-  int ac = 0;
-  Dimension sw, sh, x, y, w, h;
-
-  XtRealizeWidget(dialog);
-  sw = WidthOfScreen (XtScreen (dialog));
-  sh = HeightOfScreen (XtScreen (dialog));
-  ac = 0;
-  XtSetArg (av [ac], XtNwidth, &w); ac++;
-  XtSetArg (av [ac], XtNheight, &h); ac++;
-  XtGetValues (form, av, ac);
-  switch (where)
+      si->unlock_failures = 0;
+    }
+
+  if (msg)
     {
-    case 0:    /* center it in the top-right quadrant */
-      x = (sw/2 + w) / 2 + (sw/2) - w;
-      y = (sh/2 + h) / 2 - h;
-      break;
-    case 1:    /* center it in the bottom-right quadrant */
-      x = (sw/2 + w) / 2 + (sw/2) - w;
-      y = (sh/2 + h) / 2 + (sh/2) - h;
-      break;
-    case 2:    /* center it on the screen */
-      x = (sw + w) / 2 - w;
-      y = (sh + h) / 2 - h;
-      break;
-    default:
-      abort ();
+      si->pw_data->i_beam = 0;
+      update_passwd_window (si, msg, 0.0);
+      XSync (si->dpy, False);
+      sleep (1);
+
+      /* Swallow all pending KeyPress/KeyRelease events. */
+      {
+       XEvent e;
+       while (XCheckMaskEvent (si->dpy, KeyPressMask|KeyReleaseMask, &e))
+         ;
+      }
     }
-  if (x + w > sw) x = sw - w;
-  if (y + h > sh) y = sh - h;
-  ac = 0;
-  XtVaSetValues(dialog,
-               XtNx, x,
-               XtNy, y,
-               NULL);
-  XtVaSetValues(form,
-               XtNx, x,
-               XtNy, y,
-               NULL);
-  XtPopup(dialog,XtGrabNone);
-  steal_focus_and_colormap (focus);
 }
 
+
 static void
-passwd_set_label (char *buf, int len)
+handle_typeahead (saver_info *si)
 {
-  Widget label;
-  if (!passwd_text)
+  passwd_dialog_data *pw = si->pw_data;
+  int i;
+  if (!si->unlock_typeahead)
     return;
-  label=XtNameToWidget(XtParent(passwd_text),"*label");
-  XtVaSetValues(label,
-               XtNlabel, buf,
-               NULL);
+
+  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;
 }
-#endif /* HAVE_ATHENA */
 
-static Bool
-pop_passwd_dialog (saver_info *si)
+
+Bool
+unlock_p (saver_info *si)
 {
   saver_preferences *p = &si->prefs;
-  saver_screen_info *ssi = si->default_screen;
-  Widget parent = ssi->toplevel_shell;
-  Display *dpy = XtDisplay (passwd_dialog);
-  Window focus;
-  int revert_to;
-  int i;
+  Bool status;
 
-  typed_passwd [0] = 0;
-  passwd_state = pw_read;
-  text_field_set_string (passwd_text, "", 0);
-
-  /* In case one of the hacks has unmapped it temporarily...
-     Get that sucker on stage now! */
-  for (i = 0; i < si->nscreens; i++)
-    XMapRaised(si->dpy, si->screens[i].screensaver_window);
-
-  XGetInputFocus (dpy, &focus, &revert_to);
-#if defined(HAVE_MOTIF) && !defined(DESTROY_WORKS)
-  /* This fucker blows up if we destroy the widget.  I can't figure
-     out why.  The second destroy phase dereferences freed memory...
-     So we just keep it around; but unrealizing or unmanaging it
-     doesn't work right either, so we hack the window directly. FMH.
-   */
-  if (XtWindow (passwd_form))
-    XMapRaised (dpy, XtWindow (passwd_dialog));
-#endif
+  raise_window (si, True, True, True);
 
-#ifdef HAVE_ATHENA
-  pop_up_athena_dialog_box (parent, passwd_text, passwd_dialog,
-                           passwd_form, 2);
-#else
-  pop_up_dialog_box (passwd_dialog, passwd_form,
-                    /* for debugging -- don't ask */
-                    (si->prefs.debug_p ? 69 : 0) +
-                    2);
-  XtManageChild (passwd_form);
-#endif
+  if (p->verbose_p)
+    fprintf (stderr, "%s: prompting for password.\n", blurb());
 
-#if defined(HAVE_MOTIF) && (XmVersion < 1002)
-  /* The focus stuff changed around; this causes problems in 1.2.1
-     but is necessary in 1.1.5. */
-  XmProcessTraversal (passwd_text, XmTRAVERSE_CURRENT);
-#endif
+  if (si->pw_data || si->passwd_dialog)
+    destroy_passwd_window (si);
 
-  passwd_idle_timer_tick = p->passwd_timeout / 1000;
-  passwd_idle_id = XtAppAddTimeOut (si->app, 1000,  passwd_idle_timer,
-                                   (XtPointer) si);
+  make_passwd_window (si);
 
-#ifdef HAVE_ATHENA
-  if (roger_label)
-    roger(roger_label, 0, 0);
-#endif /* HAVE_ATHENA */
+  compose_status = calloc (1, sizeof (*compose_status));
 
-  if (!si->prefs.debug_p)
-    XGrabServer (dpy);                         /* ############ DANGER! */
+  handle_typeahead (si);
+  passwd_event_loop (si);
 
-  /* this call to ungrab used to be in main_loop() - see comment in
-      xscreensaver.c around line 857. */
-  ungrab_keyboard_and_mouse (si);
+  status = (si->pw_data->state == pw_ok);
+  destroy_passwd_window (si);
 
-  while (passwd_state == pw_read)
-    {
-      XEvent event;
-      XtAppNextEvent (si->app, &event);
-      /* wait for timer event */
-      if (event.xany.type == 0 && passwd_idle_timer_tick == 0)
-       passwd_state = pw_time;
-      XtDispatchEvent (&event);
-    }
-  XUngrabServer (dpy);
-  XSync (dpy, False);                          /* ###### (danger over) */
+  free (compose_status);
+  compose_status = 0;
 
-  if (passwd_state != pw_time)
-    XtRemoveTimeOut (passwd_idle_id);
+  return status;
+}
 
-  if (passwd_state != pw_ok)
-    {
-      char *lose;
-      switch (passwd_state)
-       {
-       case pw_time: lose = "Timed out!"; break;
-       case pw_fail: lose = "Sorry!"; break;
-       case pw_cancel: lose = 0; break;
-       default: abort ();
-       }
-#ifdef HAVE_MOTIF
-      XmProcessTraversal (passwd_cancel, 0); /* turn off I-beam */
+
+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
-      if (lose)
-       {
-#ifdef HAVE_ATHENA
-         /* show the message */
-         passwd_set_label(lose,strlen(lose)+1);
-
-         /* and clear the password line */
-         memset(typed_passwd, 0, sizeof(typed_passwd));
-         text_field_set_string (passwd_text, "", 0);
-#else
-         text_field_set_string (passwd_text, lose, strlen (lose) + 1);
+#ifdef HAVE_VT_LOCKSWITCH
+  linux_lock_vt_switch (si, locked_p);          /* turn off/on C-Alt-F1 */
 #endif
-         passwd_idle_timer_tick = 1;
-         passwd_idle_id = XtAppAddTimeOut (si->app, 3000, passwd_idle_timer,
-                               (XtPointer) si);
-         while (1)
-           {
-             XEvent event;
-             XtAppNextEvent (si->app, &event);
-             if (event.xany.type == 0 &&       /* wait for timer event */
-                 passwd_idle_timer_tick == 0)
-               break;
-             XtDispatchEvent (&event);
-           }
-       }
-    }
-  memset (typed_passwd, 0, sizeof(typed_passwd));
-  text_field_set_string (passwd_text, "", 0);
-  XtSetKeyboardFocus (parent, None);
-
-#ifdef DESTROY_WORKS
-  XtDestroyWidget (passwd_dialog);
-  passwd_dialog = 0;
-#else
-  XUnmapWindow (XtDisplay (passwd_dialog), XtWindow (passwd_dialog));
+#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
-  {
-    XErrorHandler old_handler = XSetErrorHandler (BadWindow_ehandler);
-    /* I don't understand why this doesn't refocus on the old selected
-       window when MWM is running in click-to-type mode.  The value of
-       `focus' seems to be correct. */
-    XSetInputFocus (dpy, focus, revert_to, CurrentTime);
-    XSync (dpy, False);
-    XSetErrorHandler (old_handler);
-  }
-
-  /* Since we installed our colormap to display the dialog properly, put
-     the old one back, so that the screensaver_window is now displayed
-     properly. */
-  for (i = 0; i < si->nscreens; i++)
-    {
-      saver_screen_info *ssi = &si->screens[i];
-      if (ssi->cmap)
-       XInstallColormap (si->dpy, ssi->cmap);
-    }
 
-  return (passwd_state == pw_ok ? True : False);
+  store_saver_status (si);                     /* store locked-p */
 }
 
-Bool
-unlock_p (saver_info *si)
+
+#else  /*  NO_LOCKING -- whole file */
+
+void
+set_locked_p (saver_info *si, Bool locked_p)
 {
-  static Bool initted = False;
-  if (! initted)
-    {
-#ifndef VERIFY_CALLBACK_WORKS
-      XtAppAddActions (si->app, actions, XtNumber (actions));
-#endif
-      passwd_dialog = 0;
-      initted = True;
-    }
-  if (! passwd_dialog)
-    make_passwd_dialog (si);
-  return pop_passwd_dialog (si);
+  if (locked_p) abort();
 }
 
-#endif /* !NO_LOCKING -- whole file */
+#endif /* !NO_LOCKING */