http://www.jwz.org/xscreensaver/xscreensaver-5.11.tar.gz
[xscreensaver] / driver / lock.c
1 /* lock.c --- handling the password dialog for locking-mode.
2  * xscreensaver, Copyright (c) 1993-2008 Jamie Zawinski <jwz@jwz.org>
3  *
4  * Permission to use, copy, modify, distribute, and sell this software and its
5  * documentation for any purpose is hereby granted without fee, provided that
6  * the above copyright notice appear in all copies and that both that
7  * copyright notice and this permission notice appear in supporting
8  * documentation.  No representations are made about the suitability of this
9  * software for any purpose.  It is provided "as is" without express or 
10  * implied warranty.
11  */
12
13 /* Athena locking code contributed by Jon A. Christopher <jac8782@tamu.edu> */
14 /* Copyright 1997, with the same permissions as above. */
15
16 #ifdef HAVE_CONFIG_H
17 # include "config.h"
18 #endif
19
20 #include <ctype.h>
21 #include <X11/Intrinsic.h>
22 #include <X11/cursorfont.h>
23 #include <X11/Xos.h>            /* for time() */
24 #include <time.h>
25 #include <sys/time.h>
26 #include "xscreensaver.h"
27 #include "resources.h"
28 #include "mlstring.h"
29 #include "auth.h"
30
31 #ifndef NO_LOCKING              /* (mostly) whole file */
32
33 #ifdef HAVE_XHPDISABLERESET
34 # include <X11/XHPlib.h>
35   static void hp_lock_reset (saver_info *si, Bool lock_p);
36 #endif /* HAVE_XHPDISABLERESET */
37
38 #ifdef HAVE_XF86VMODE
39 # include <X11/extensions/xf86vmode.h>
40   static void xfree_lock_mode_switch (saver_info *si, Bool lock_p);
41 #endif /* HAVE_XF86VMODE */
42
43 #ifdef HAVE_XF86MISCSETGRABKEYSSTATE
44 # include <X11/extensions/xf86misc.h>
45   static void xfree_lock_grab_smasher (saver_info *si, Bool lock_p);
46 #endif /* HAVE_XF86MISCSETGRABKEYSSTATE */
47
48
49 #ifdef _VROOT_H_
50 ERROR!  You must not include vroot.h in this file.
51 #endif
52
53 #ifdef HAVE_UNAME
54 # include <sys/utsname.h> /* for hostname info */
55 #endif /* HAVE_UNAME */
56 #include <ctype.h>
57
58 #ifndef VMS
59 # include <pwd.h>
60 #else /* VMS */
61
62 extern char *getenv(const char *name);
63 extern int validate_user(char *name, char *password);
64
65 static Bool
66 vms_passwd_valid_p(char *pw, Bool verbose_p)
67 {
68   return (validate_user (getenv("USER"), typed_passwd) == 1);
69 }
70 # undef passwd_valid_p
71 # define passwd_valid_p vms_passwd_valid_p
72
73 #endif /* VMS */
74
75 #define SAMPLE_INPUT "MMMMMMMMMMMM"
76
77
78 #undef MAX
79 #define MAX(a,b) ((a)>(b)?(a):(b))
80
81 typedef struct info_dialog_data info_dialog_data;
82
83 struct passwd_dialog_data {
84
85   saver_screen_info *prompt_screen;
86   int previous_mouse_x, previous_mouse_y;
87
88   char typed_passwd [80];
89   XtIntervalId timer;
90   int i_beam;
91
92   float ratio;
93   Position x, y;
94   Dimension width;
95   Dimension height;
96   Dimension border_width;
97
98   Bool echo_input;
99   Bool show_stars_p; /* "I regret that I have but one asterisk for my country."
100                         -- Nathan Hale, 1776. */
101
102   char *heading_label;
103   char *body_label;
104   char *user_label;
105   mlstring *info_label;
106   /* The entry field shall only be displayed if prompt_label is not NULL */
107   mlstring *prompt_label;
108   char *date_label;
109   char *passwd_string;
110   Bool passwd_changed_p; /* Whether the user entry field needs redrawing */
111   Bool caps_p;           /* Whether we saw a keypress with caps-lock on */
112   char *unlock_label;
113   char *login_label;
114   char *uname_label;
115
116   Bool show_uname_p;
117
118   XFontStruct *heading_font;
119   XFontStruct *body_font;
120   XFontStruct *label_font;
121   XFontStruct *passwd_font;
122   XFontStruct *date_font;
123   XFontStruct *button_font;
124   XFontStruct *uname_font;
125
126   Pixel foreground;
127   Pixel background;
128   Pixel passwd_foreground;
129   Pixel passwd_background;
130   Pixel thermo_foreground;
131   Pixel thermo_background;
132   Pixel shadow_top;
133   Pixel shadow_bottom;
134   Pixel button_foreground;
135   Pixel button_background;
136
137   Dimension preferred_logo_width, logo_width;
138   Dimension preferred_logo_height, logo_height;
139   Dimension thermo_width;
140   Dimension internal_border;
141   Dimension shadow_width;
142
143   Dimension passwd_field_x, passwd_field_y;
144   Dimension passwd_field_width, passwd_field_height;
145
146   Dimension unlock_button_x, unlock_button_y;
147   Dimension unlock_button_width, unlock_button_height;
148
149   Dimension login_button_x, login_button_y;
150   Dimension login_button_width, login_button_height;
151
152   Dimension thermo_field_x, thermo_field_y;
153   Dimension thermo_field_height;
154
155   Pixmap logo_pixmap;
156   Pixmap logo_clipmask;
157   int logo_npixels;
158   unsigned long *logo_pixels;
159
160   Cursor passwd_cursor;
161   Bool unlock_button_down_p;
162   Bool login_button_down_p;
163   Bool login_button_p;
164   Bool login_button_enabled_p;
165   Bool button_state_changed_p; /* Refers to both buttons */
166
167   Pixmap save_under;
168   Pixmap user_entry_pixmap;
169 };
170
171 static void draw_passwd_window (saver_info *si);
172 static void update_passwd_window (saver_info *si, const char *printed_passwd,
173                                   float ratio);
174 static void destroy_passwd_window (saver_info *si);
175 static void undo_vp_motion (saver_info *si);
176 static void finished_typing_passwd (saver_info *si, passwd_dialog_data *pw);
177 static void cleanup_passwd_window (saver_info *si);
178 static void restore_background (saver_info *si);
179
180 extern void xss_authenticate(saver_info *si, Bool verbose_p);
181
182 static int
183 new_passwd_window (saver_info *si)
184 {
185   passwd_dialog_data *pw;
186   Screen *screen;
187   Colormap cmap;
188   char *f;
189   saver_screen_info *ssi = &si->screens [mouse_screen (si)];
190
191   pw = (passwd_dialog_data *) calloc (1, sizeof(*pw));
192   if (!pw)
193     return -1;
194
195   /* Display the button only if the "newLoginCommand" pref is non-null.
196    */
197   pw->login_button_p = (si->prefs.new_login_command &&
198                         *si->prefs.new_login_command);
199
200   pw->passwd_cursor = XCreateFontCursor (si->dpy, XC_top_left_arrow);
201
202   pw->prompt_screen = ssi;
203
204   screen = pw->prompt_screen->screen;
205   cmap = DefaultColormapOfScreen (screen);
206
207   pw->show_stars_p = get_boolean_resource(si->dpy, "passwd.asterisks", 
208                                           "Boolean");
209   
210   pw->heading_label = get_string_resource (si->dpy, "passwd.heading.label",
211                                            "Dialog.Label.Label");
212   pw->body_label = get_string_resource (si->dpy, "passwd.body.label",
213                                         "Dialog.Label.Label");
214   pw->user_label = get_string_resource (si->dpy, "passwd.user.label",
215                                         "Dialog.Label.Label");
216   pw->unlock_label = get_string_resource (si->dpy, "passwd.unlock.label",
217                                           "Dialog.Button.Label");
218   pw->login_label = get_string_resource (si->dpy, "passwd.login.label",
219                                          "Dialog.Button.Label");
220
221   pw->date_label = get_string_resource (si->dpy, "dateFormat", "DateFormat");
222
223   if (!pw->heading_label)
224     pw->heading_label = strdup("ERROR: RESOURCES NOT INSTALLED CORRECTLY");
225   if (!pw->body_label)
226     pw->body_label = strdup("ERROR: RESOURCES NOT INSTALLED CORRECTLY");
227   if (!pw->user_label) pw->user_label = strdup("ERROR");
228   if (!pw->date_label) pw->date_label = strdup("ERROR");
229   if (!pw->unlock_label) pw->unlock_label = strdup("ERROR (UNLOCK)");
230   if (!pw->login_label) pw->login_label = strdup ("ERROR (LOGIN)") ;
231
232   /* Put the version number in the label. */
233   {
234     char *s = (char *) malloc (strlen(pw->heading_label) + 20);
235     sprintf(s, pw->heading_label, si->version);
236     free (pw->heading_label);
237     pw->heading_label = s;
238   }
239
240   /* Get hostname info */
241   pw->uname_label = strdup(""); /* Initialy, write nothing */
242
243 # ifdef HAVE_UNAME
244   {
245     struct utsname uts;
246
247     if (uname (&uts) == 0)
248       {
249 #if 0 /* Get the full hostname */
250         {
251           char *s;
252           if ((s = strchr(uts.nodename, '.')))
253             *s = 0;
254         }
255 #endif
256         char *s = strdup (uts.nodename);
257         free (pw->uname_label);
258         pw->uname_label = s;
259       }
260   }
261 # endif
262
263   pw->passwd_string = strdup("");
264
265   f = get_string_resource (si->dpy, "passwd.headingFont", "Dialog.Font");
266   pw->heading_font = XLoadQueryFont (si->dpy, (f ? f : "fixed"));
267   if (!pw->heading_font) pw->heading_font = XLoadQueryFont (si->dpy, "fixed");
268   if (f) free (f);
269
270   f = get_string_resource (si->dpy, "passwd.buttonFont", "Dialog.Font");
271   pw->button_font = XLoadQueryFont (si->dpy, (f ? f : "fixed"));
272   if (!pw->button_font) pw->button_font = XLoadQueryFont (si->dpy, "fixed");
273   if (f) free (f);
274
275   f = get_string_resource(si->dpy, "passwd.bodyFont", "Dialog.Font");
276   pw->body_font = XLoadQueryFont (si->dpy, (f ? f : "fixed"));
277   if (!pw->body_font) pw->body_font = XLoadQueryFont (si->dpy, "fixed");
278   if (f) free (f);
279
280   f = get_string_resource(si->dpy, "passwd.labelFont", "Dialog.Font");
281   pw->label_font = XLoadQueryFont (si->dpy, (f ? f : "fixed"));
282   if (!pw->label_font) pw->label_font = XLoadQueryFont (si->dpy, "fixed");
283   if (f) free (f);
284
285   f = get_string_resource(si->dpy, "passwd.passwdFont", "Dialog.Font");
286   pw->passwd_font = XLoadQueryFont (si->dpy, (f ? f : "fixed"));
287   if (!pw->passwd_font) pw->passwd_font = XLoadQueryFont (si->dpy, "fixed");
288   if (f) free (f);
289
290   f = get_string_resource(si->dpy, "passwd.dateFont", "Dialog.Font");
291   pw->date_font = XLoadQueryFont (si->dpy, (f ? f : "fixed"));
292   if (!pw->date_font) pw->date_font = XLoadQueryFont (si->dpy, "fixed");
293   if (f) free (f);
294
295   f = get_string_resource(si->dpy, "passwd.unameFont", "Dialog.Font");
296   pw->uname_font = XLoadQueryFont (si->dpy, (f ? f : "fixed"));
297   if (!pw->uname_font) pw->uname_font = XLoadQueryFont (si->dpy, "fixed");
298   if (f) free (f);
299   
300   pw->show_uname_p = get_boolean_resource(si->dpy, "passwd.uname", "Boolean");
301
302   pw->foreground = get_pixel_resource (si->dpy, cmap,
303                                        "passwd.foreground",
304                                        "Dialog.Foreground" );
305   pw->background = get_pixel_resource (si->dpy, cmap,
306                                        "passwd.background",
307                                        "Dialog.Background" );
308
309   if (pw->foreground == pw->background)
310     {
311       /* Make sure the error messages show up. */
312       pw->foreground = BlackPixelOfScreen (screen);
313       pw->background = WhitePixelOfScreen (screen);
314     }
315
316   pw->passwd_foreground = get_pixel_resource (si->dpy, cmap,
317                                               "passwd.text.foreground",
318                                               "Dialog.Text.Foreground" );
319   pw->passwd_background = get_pixel_resource (si->dpy, cmap,
320                                               "passwd.text.background",
321                                               "Dialog.Text.Background" );
322   pw->button_foreground = get_pixel_resource (si->dpy, cmap, 
323                                               "splash.Button.foreground",
324                                               "Dialog.Button.Foreground" );
325   pw->button_background = get_pixel_resource (si->dpy, cmap,
326                                               "splash.Button.background",
327                                               "Dialog.Button.Background" );
328   pw->thermo_foreground = get_pixel_resource (si->dpy, cmap,
329                                               "passwd.thermometer.foreground",
330                                               "Dialog.Thermometer.Foreground" );
331   pw->thermo_background = get_pixel_resource ( si->dpy, cmap,
332                                               "passwd.thermometer.background",
333                                               "Dialog.Thermometer.Background" );
334   pw->shadow_top = get_pixel_resource ( si->dpy, cmap,
335                                        "passwd.topShadowColor",
336                                        "Dialog.Foreground" );
337   pw->shadow_bottom = get_pixel_resource (si->dpy, cmap, 
338                                           "passwd.bottomShadowColor",
339                                           "Dialog.Background" );
340
341   pw->preferred_logo_width = get_integer_resource (si->dpy, "passwd.logo.width",
342                                                    "Dialog.Logo.Width");
343   pw->preferred_logo_height = get_integer_resource (si->dpy, "passwd.logo.height",
344                                                     "Dialog.Logo.Height");
345   pw->thermo_width = get_integer_resource (si->dpy, "passwd.thermometer.width",
346                                            "Dialog.Thermometer.Width");
347   pw->internal_border = get_integer_resource (si->dpy, "passwd.internalBorderWidth",
348                                               "Dialog.InternalBorderWidth");
349   pw->shadow_width = get_integer_resource (si->dpy, "passwd.shadowThickness",
350                                            "Dialog.ShadowThickness");
351
352   if (pw->preferred_logo_width == 0)  pw->preferred_logo_width = 150;
353   if (pw->preferred_logo_height == 0) pw->preferred_logo_height = 150;
354   if (pw->internal_border == 0) pw->internal_border = 15;
355   if (pw->shadow_width == 0) pw->shadow_width = 4;
356   if (pw->thermo_width == 0) pw->thermo_width = pw->shadow_width;
357
358
359   /* We need to remember the mouse position and restore it afterward, or
360      sometimes (perhaps only with Xinerama?) the mouse gets warped to
361      inside the bounds of the lock dialog window.
362    */
363   {
364     Window pointer_root, pointer_child;
365     int root_x, root_y, win_x, win_y;
366     unsigned int mask;
367     pw->previous_mouse_x = 0;
368     pw->previous_mouse_y = 0;
369     if (XQueryPointer (si->dpy, RootWindowOfScreen (pw->prompt_screen->screen),
370                        &pointer_root, &pointer_child,
371                        &root_x, &root_y, &win_x, &win_y, &mask))
372       {
373         pw->previous_mouse_x = root_x;
374         pw->previous_mouse_y = root_y;
375         if (si->prefs.verbose_p)
376           fprintf (stderr, "%s: %d: mouse is at %d,%d.\n",
377                    blurb(), pw->prompt_screen->number,
378                    pw->previous_mouse_x, pw->previous_mouse_y);
379       }
380     else if (si->prefs.verbose_p)
381       fprintf (stderr, "%s: %d: unable to determine mouse position?\n",
382                blurb(), pw->prompt_screen->number);
383   }
384
385   /* Before mapping the window, save a pixmap of the current screen.
386      When we lower the window, we
387      restore these bits.  This works, because the running screenhack
388      has already been sent SIGSTOP, so we know nothing else is drawing
389      right now! */
390   {
391     XGCValues gcv;
392     GC gc;
393     pw->save_under = XCreatePixmap (si->dpy,
394                                     pw->prompt_screen->screensaver_window,
395                                     pw->prompt_screen->width,
396                                     pw->prompt_screen->height,
397                                     pw->prompt_screen->current_depth);
398     gcv.function = GXcopy;
399     gc = XCreateGC (si->dpy, pw->save_under, GCFunction, &gcv);
400     XCopyArea (si->dpy, pw->prompt_screen->screensaver_window,
401                pw->save_under, gc,
402                0, 0,
403                pw->prompt_screen->width, pw->prompt_screen->height,
404                0, 0);
405     XFreeGC (si->dpy, gc);
406   }
407
408   si->pw_data = pw;
409   return 0;
410 }
411
412
413 /**
414  * info_msg and prompt may be NULL.
415  */
416 static int
417 make_passwd_window (saver_info *si,
418                     const char *info_msg,
419                     const char *prompt,
420                     Bool echo)
421 {
422   XSetWindowAttributes attrs;
423   unsigned long attrmask = 0;
424   passwd_dialog_data *pw;
425   Screen *screen;
426   Colormap cmap;
427   Dimension max_string_width_px;
428   saver_screen_info *ssi = &si->screens [mouse_screen (si)];
429
430   cleanup_passwd_window (si);
431
432   if (! ssi)   /* WTF?  Trying to prompt while no screens connected? */
433     return -1;
434
435   if (!si->pw_data)
436     if (new_passwd_window (si) < 0)
437       return -1;
438
439   if (!(pw = si->pw_data))
440     return -1;
441
442   pw->ratio = 1.0;
443
444   pw->prompt_screen = ssi;
445   if (si->prefs.verbose_p)
446     fprintf (stderr, "%s: %d: creating password dialog (\"%s\")\n",
447              blurb(), pw->prompt_screen->number,
448              info_msg ? info_msg : "");
449
450   screen = pw->prompt_screen->screen;
451   cmap = DefaultColormapOfScreen (screen);
452
453   pw->echo_input = echo;
454
455   max_string_width_px = ssi->width
456       - pw->shadow_width * 4
457       - pw->border_width * 2
458       - pw->thermo_width
459       - pw->preferred_logo_width
460       - pw->internal_border * 2;
461   /* As the string wraps it makes the window taller which makes the logo wider
462    * which leaves less room for the text which makes the string wrap. Uh-oh, a
463    * loop. By wrapping at a bit less than the available width, there's some
464    * room for the dialog to grow without going off the edge of the screen. */
465   max_string_width_px *= 0.75;
466
467   pw->info_label = mlstring_new(info_msg ? info_msg : pw->body_label,
468                                 pw->label_font, max_string_width_px);
469
470   {
471     int direction, ascent, descent;
472     XCharStruct overall;
473
474     pw->width = 0;
475     pw->height = 0;
476
477     /* Measure the heading_label. */
478     XTextExtents (pw->heading_font,
479                   pw->heading_label, strlen(pw->heading_label),
480                   &direction, &ascent, &descent, &overall);
481     if (overall.width > pw->width) pw->width = overall.width;
482     pw->height += ascent + descent;
483
484     /* Measure the uname_label. */
485     if ((strlen(pw->uname_label)) && pw->show_uname_p)
486       {
487         XTextExtents (pw->uname_font,
488                       pw->uname_label, strlen(pw->uname_label),
489                       &direction, &ascent, &descent, &overall);
490         if (overall.width > pw->width) pw->width = overall.width;
491         pw->height += ascent + descent;
492       }
493
494     {
495       Dimension w2 = 0, w3 = 0, button_w = 0;
496       Dimension h2 = 0, h3 = 0, button_h = 0;
497       const char *passwd_string = SAMPLE_INPUT;
498
499       /* Measure the user_label. */
500       XTextExtents (pw->label_font,
501                     pw->user_label, strlen(pw->user_label),
502                     &direction, &ascent, &descent, &overall);
503       if (overall.width > w2)  w2 = overall.width;
504       h2 += ascent + descent;
505
506       /* Measure the info_label. */
507       if (pw->info_label->overall_width > pw->width) pw->width = pw->info_label->overall_width;
508         h2 += pw->info_label->overall_height;
509
510       /* Measure the user string. */
511       XTextExtents (pw->passwd_font,
512                     si->user, strlen(si->user),
513                     &direction, &ascent, &descent, &overall);
514       overall.width += (pw->shadow_width * 4);
515       ascent += (pw->shadow_width * 4);
516       if (overall.width > w3)  w3 = overall.width;
517       h3 += ascent + descent;
518
519       /* Measure the (dummy) passwd_string. */
520       if (prompt)
521         {
522           XTextExtents (pw->passwd_font,
523                         passwd_string, strlen(passwd_string),
524                         &direction, &ascent, &descent, &overall);
525           overall.width += (pw->shadow_width * 4);
526           ascent += (pw->shadow_width * 4);
527           if (overall.width > w3)  w3 = overall.width;
528           h3 += ascent + descent;
529
530           /* Measure the prompt_label. */
531           max_string_width_px -= w3;
532           pw->prompt_label = mlstring_new(prompt, pw->label_font, max_string_width_px);
533
534           if (pw->prompt_label->overall_width > w2) w2 = pw->prompt_label->overall_width;
535
536           h2 += pw->prompt_label->overall_height;
537
538           w2 = w2 + w3 + (pw->shadow_width * 2);
539           h2 = MAX (h2, h3);
540         }
541
542       /* The "Unlock" button. */
543       XTextExtents (pw->label_font,
544                     pw->unlock_label, strlen(pw->unlock_label),
545                     &direction, &ascent, &descent, &overall);
546       button_w = overall.width;
547       button_h = ascent + descent;
548
549       /* Add some horizontal padding inside the button. */
550       button_w += ascent;
551       
552       button_w += ((ascent + descent) / 2) + (pw->shadow_width * 2);
553       button_h += ((ascent + descent) / 2) + (pw->shadow_width * 2);
554
555       pw->unlock_button_width = button_w;
556       pw->unlock_button_height = button_h;
557
558       w2 = MAX (w2, button_w);
559       h2 += button_h * 1.5;
560
561       /* The "New Login" button */
562       pw->login_button_width = 0;
563       pw->login_button_height = 0;
564
565       if (pw->login_button_p)
566         {
567           pw->login_button_enabled_p = True;
568
569           /* Measure the "New Login" button */
570           XTextExtents (pw->button_font, pw->login_label,
571                         strlen (pw->login_label),
572                         &direction, &ascent, &descent, &overall);
573           button_w = overall.width;
574           button_h = ascent + descent;
575
576           /* Add some horizontal padding inside the buttons. */
577           button_w += ascent;
578
579           button_w += ((ascent + descent) / 2) + (pw->shadow_width * 2);
580           button_h += ((ascent + descent) / 2) + (pw->shadow_width * 2);
581
582           pw->login_button_width = button_w;
583           pw->login_button_height = button_h;
584
585           if (button_h > pw->unlock_button_height)
586             h2 += (button_h * 1.5 - pw->unlock_button_height * 1.5);
587
588           /* Use (2 * shadow_width) spacing between the buttons. Another
589              (2 * shadow_width) is required to account for button shadows. */
590           w2 = MAX (w2, button_w + pw->unlock_button_width + (pw->shadow_width * 4));
591         }
592
593       if (w2 > pw->width)  pw->width  = w2;
594       pw->height += h2;
595     }
596
597     pw->width  += (pw->internal_border * 2);
598     pw->height += (pw->internal_border * 4);
599
600     pw->width += pw->thermo_width + (pw->shadow_width * 3);
601
602     if (pw->preferred_logo_height > pw->height)
603       pw->height = pw->logo_height = pw->preferred_logo_height;
604     else if (pw->height > pw->preferred_logo_height)
605       pw->logo_height = pw->height;
606
607     pw->logo_width = pw->logo_height;
608
609     pw->width += pw->logo_width;
610   }
611
612   attrmask |= CWOverrideRedirect; attrs.override_redirect = True;
613
614   attrmask |= CWEventMask;
615   attrs.event_mask = (ExposureMask | KeyPressMask |
616                       ButtonPressMask | ButtonReleaseMask);
617
618   /* Figure out where on the desktop to place the window so that it will
619      actually be visible; this takes into account virtual viewports as
620      well as Xinerama. */
621   {
622     saver_screen_info *ssi = &si->screens [mouse_screen (si)];
623     int x = ssi->x;
624     int y = ssi->y;
625     int w = ssi->width;
626     int h = ssi->height;
627     if (si->prefs.debug_p) w /= 2;
628     pw->x = x + ((w + pw->width) / 2) - pw->width;
629     pw->y = y + ((h + pw->height) / 2) - pw->height;
630     if (pw->x < x) pw->x = x;
631     if (pw->y < y) pw->y = y;
632   }
633
634   pw->border_width = get_integer_resource (si->dpy, "passwd.borderWidth",
635                                            "Dialog.BorderWidth");
636
637   /* Only create the window the first time around */
638   if (!si->passwd_dialog)
639     {
640       si->passwd_dialog =
641         XCreateWindow (si->dpy,
642                        RootWindowOfScreen(screen),
643                        pw->x, pw->y, pw->width, pw->height, pw->border_width,
644                        DefaultDepthOfScreen (screen), InputOutput,
645                        DefaultVisualOfScreen(screen),
646                        attrmask, &attrs);
647       XSetWindowBackground (si->dpy, si->passwd_dialog, pw->background);
648
649       /* We use the default visual, not ssi->visual, so that the logo pixmap's
650          visual matches that of the si->passwd_dialog window. */
651       pw->logo_pixmap = xscreensaver_logo (ssi->screen,
652                                            /* ssi->current_visual, */
653                                            DefaultVisualOfScreen(screen),
654                                            si->passwd_dialog, cmap,
655                                            pw->background, 
656                                            &pw->logo_pixels, &pw->logo_npixels,
657                                            &pw->logo_clipmask, True);
658     }
659   else /* On successive prompts, just resize the window */
660     {
661       XWindowChanges wc;
662       unsigned int mask = CWX | CWY | CWWidth | CWHeight;
663
664       wc.x = pw->x;
665       wc.y = pw->y;
666       wc.width = pw->width;
667       wc.height = pw->height;
668
669       XConfigureWindow (si->dpy, si->passwd_dialog, mask, &wc);
670     }
671
672   restore_background(si);
673
674   XMapRaised (si->dpy, si->passwd_dialog);
675   XSync (si->dpy, False);
676
677   move_mouse_grab (si, si->passwd_dialog,
678                    pw->passwd_cursor,
679                    pw->prompt_screen->number);
680   undo_vp_motion (si);
681
682   si->pw_data = pw;
683
684   if (cmap)
685     XInstallColormap (si->dpy, cmap);
686   draw_passwd_window (si);
687
688   return 0;
689 }
690
691
692 static void
693 draw_passwd_window (saver_info *si)
694 {
695   passwd_dialog_data *pw = si->pw_data;
696   XGCValues gcv;
697   GC gc1, gc2;
698   int spacing, height;
699   int x1, x2, x3, y1, y2;
700   int sw;
701   int tb_height;
702
703   /* Force redraw */
704   pw->passwd_changed_p = True;
705   pw->button_state_changed_p = True;
706
707   /* This height is the height of all the elements, not to be confused with
708    * the overall window height which is pw->height. It is used to compute
709    * the amount of spacing (padding) between elements. */
710   height = (pw->heading_font->ascent + pw->heading_font->descent +
711             pw->info_label->overall_height +
712             MAX (((pw->label_font->ascent + pw->label_font->descent) +
713                   (pw->prompt_label ? pw->prompt_label->overall_height : 0)),
714                  ((pw->passwd_font->ascent + pw->passwd_font->descent) +
715                   (pw->shadow_width * 2)) * (pw->prompt_label ? 2 : 1)) +
716             pw->date_font->ascent + pw->date_font->descent);
717
718   if ((strlen(pw->uname_label)) && pw->show_uname_p)
719     height += (pw->uname_font->ascent + pw->uname_font->descent); /* for uname */
720
721   height += ((pw->button_font->ascent + pw->button_font->descent) * 2 +
722              2 * pw->shadow_width);
723
724   spacing = ((pw->height - 2 * pw->shadow_width
725                - pw->internal_border - height)
726              / 10);
727
728   if (spacing < 0) spacing = 0;
729
730   gcv.foreground = pw->foreground;
731   gc1 = XCreateGC (si->dpy, si->passwd_dialog, GCForeground, &gcv);
732   gc2 = XCreateGC (si->dpy, si->passwd_dialog, GCForeground, &gcv);
733   x1 = pw->logo_width + pw->thermo_width + (pw->shadow_width * 3);
734   x3 = pw->width - (pw->shadow_width * 2);
735   y1 = (pw->shadow_width * 2) + spacing + spacing;
736
737   /* top heading
738    */
739   XSetFont (si->dpy, gc1, pw->heading_font->fid);
740   sw = string_width (pw->heading_font, pw->heading_label);
741   x2 = (x1 + ((x3 - x1 - sw) / 2));
742   y1 += spacing + pw->heading_font->ascent + pw->heading_font->descent;
743   XDrawString (si->dpy, si->passwd_dialog, gc1, x2, y1,
744                pw->heading_label, strlen(pw->heading_label));
745
746   /* uname below top heading
747    */
748   if ((strlen(pw->uname_label)) && pw->show_uname_p)
749     {
750       XSetFont (si->dpy, gc1, pw->uname_font->fid);
751       y1 += spacing + pw->uname_font->ascent + pw->uname_font->descent;
752       sw = string_width (pw->uname_font, pw->uname_label);
753       x2 = (x1 + ((x3 - x1 - sw) / 2));
754       XDrawString (si->dpy, si->passwd_dialog, gc1, x2, y1,
755                    pw->uname_label, strlen(pw->uname_label));
756     }
757
758   /* the info_label (below uname)
759    */
760   x2 = (x1 + ((x3 - x1 - pw->info_label->overall_width) / 2));
761   y1 += spacing + pw->info_label->font_height / 2;
762   mlstring_draw(si->dpy, si->passwd_dialog, gc1, pw->info_label,
763                 x2, y1);
764   y1 += pw->info_label->overall_height;
765
766
767   tb_height = (pw->passwd_font->ascent + pw->passwd_font->descent +
768                (pw->shadow_width * 4));
769
770   /* the "User:" prompt
771    */
772   y2 = y1;
773   XSetForeground (si->dpy, gc1, pw->foreground);
774   XSetFont (si->dpy, gc1, pw->label_font->fid);
775   y1 += (spacing + tb_height + pw->shadow_width);
776   x2 = (x1 + pw->internal_border +
777         MAX(string_width (pw->label_font, pw->user_label),
778             pw->prompt_label ? pw->prompt_label->overall_width : 0));
779   XDrawString (si->dpy, si->passwd_dialog, gc1,
780                x2 - string_width (pw->label_font, pw->user_label),
781                y1 - pw->passwd_font->descent,
782                pw->user_label, strlen(pw->user_label));
783
784   /* the prompt_label prompt
785    */
786   if (pw->prompt_label)
787     {
788       y1 += tb_height - pw->label_font->ascent + pw->shadow_width;
789       mlstring_draw(si->dpy, si->passwd_dialog, gc1, pw->prompt_label,
790                     x2 - pw->prompt_label->overall_width, y1);
791     }
792
793   /* the "user name" text field
794    */
795   y1 = y2;
796   XSetForeground (si->dpy, gc1, pw->passwd_foreground);
797   XSetForeground (si->dpy, gc2, pw->passwd_background);
798   XSetFont (si->dpy, gc1, pw->passwd_font->fid);
799   y1 += (spacing + tb_height);
800   x2 += (pw->shadow_width * 4);
801
802   pw->passwd_field_width = x3 - x2 - pw->internal_border;
803   pw->passwd_field_height = (pw->passwd_font->ascent +
804                              pw->passwd_font->descent +
805                              pw->shadow_width);
806
807   XFillRectangle (si->dpy, si->passwd_dialog, gc2,
808                   x2 - pw->shadow_width,
809                   y1 - (pw->passwd_font->ascent + pw->passwd_font->descent),
810                   pw->passwd_field_width, pw->passwd_field_height);
811   XDrawString (si->dpy, si->passwd_dialog, gc1,
812                x2,
813                y1 - pw->passwd_font->descent,
814                si->user, strlen(si->user));
815
816   /* the password/prompt text field
817    */
818   if (pw->prompt_label)
819     {
820       y1 += (spacing + pw->prompt_label->overall_height + pw->shadow_width * 2);
821
822       pw->passwd_field_x = x2 - pw->shadow_width;
823       pw->passwd_field_y = y1 - (pw->passwd_font->ascent +
824                                  pw->passwd_font->descent);
825     }
826
827   /* The shadow around the text fields
828    */
829   y1 = y2;
830   y1 += (spacing + (pw->shadow_width * 3));
831   x1 = x2 - (pw->shadow_width * 2);
832   x2 = pw->passwd_field_width + (pw->shadow_width * 2);
833   y2 = pw->passwd_field_height + (pw->shadow_width * 2);
834
835   draw_shaded_rectangle (si->dpy, si->passwd_dialog,
836                          x1, y1, x2, y2,
837                          pw->shadow_width,
838                          pw->shadow_bottom, pw->shadow_top);
839
840   if (pw->prompt_label)
841     {
842       y1 += (spacing + pw->prompt_label->overall_height + pw->shadow_width * 2);
843       draw_shaded_rectangle (si->dpy, si->passwd_dialog,
844                              x1, y1, x2, y2,
845                              pw->shadow_width,
846                              pw->shadow_bottom, pw->shadow_top);
847     }
848
849
850   /* The date, below the text fields
851    */
852   {
853     char buf[100];
854     time_t now = time ((time_t *) 0);
855     struct tm *tm = localtime (&now);
856     memset (buf, 0, sizeof(buf));
857     strftime (buf, sizeof(buf)-1, pw->date_label, tm);
858
859     XSetFont (si->dpy, gc1, pw->date_font->fid);
860     y1 += pw->shadow_width;
861     y1 += (spacing + tb_height);
862     y1 += spacing/2;
863     sw = string_width (pw->date_font, buf);
864     x2 = x1 + x2 - sw;
865     XDrawString (si->dpy, si->passwd_dialog, gc1, x2, y1, buf, strlen(buf));
866   }
867
868   /* Set up the GCs for the "New Login" and "Unlock" buttons.
869    */
870   XSetForeground(si->dpy, gc1, pw->button_foreground);
871   XSetForeground(si->dpy, gc2, pw->button_background);
872   XSetFont(si->dpy, gc1, pw->button_font->fid);
873
874   /* The "Unlock" button */
875   x2 = pw->width - pw->internal_border - (pw->shadow_width * 2);
876
877   /* right aligned button */
878   x1 = x2 - pw->unlock_button_width;
879
880   /* Add half the difference between y1 and the internal edge.
881    * It actually looks better if the internal border is ignored. */
882   y1 += ((pw->height - MAX (pw->unlock_button_height, pw->login_button_height)
883           - spacing - y1)
884          / 2);
885
886   pw->unlock_button_x = x1;
887   pw->unlock_button_y = y1;
888
889   /* The "New Login" button
890    */
891   if (pw->login_button_p)
892     {
893       /* Using the same GC as for the Unlock button */
894
895       sw = string_width (pw->button_font, pw->login_label);
896
897       /* left aligned button */
898       x1 = (pw->logo_width + pw->thermo_width + (pw->shadow_width * 3) +
899             pw->internal_border);
900
901       pw->login_button_x = x1;
902       pw->login_button_y = y1;
903     }
904
905   /* The logo
906    */
907   x1 = pw->shadow_width * 6;
908   y1 = pw->shadow_width * 6;
909   x2 = pw->logo_width - (pw->shadow_width * 12);
910   y2 = pw->logo_height - (pw->shadow_width * 12);
911
912   if (pw->logo_pixmap)
913     {
914       Window root;
915       int x, y;
916       unsigned int w, h, bw, d;
917       XGetGeometry (si->dpy, pw->logo_pixmap, &root, &x, &y, &w, &h, &bw, &d);
918       XSetForeground (si->dpy, gc1, pw->foreground);
919       XSetBackground (si->dpy, gc1, pw->background);
920       XSetClipMask (si->dpy, gc1, pw->logo_clipmask);
921       XSetClipOrigin (si->dpy, gc1, x1 + ((x2 - (int)w) / 2), y1 + ((y2 - (int)h) / 2));
922       if (d == 1)
923         XCopyPlane (si->dpy, pw->logo_pixmap, si->passwd_dialog, gc1,
924                     0, 0, w, h,
925                     x1 + ((x2 - (int)w) / 2),
926                     y1 + ((y2 - (int)h) / 2),
927                     1);
928       else
929         XCopyArea (si->dpy, pw->logo_pixmap, si->passwd_dialog, gc1,
930                    0, 0, w, h,
931                    x1 + ((x2 - (int)w) / 2),
932                    y1 + ((y2 - (int)h) / 2));
933     }
934
935   /* The thermometer
936    */
937   XSetForeground (si->dpy, gc1, pw->thermo_foreground);
938   XSetForeground (si->dpy, gc2, pw->thermo_background);
939
940   pw->thermo_field_x = pw->logo_width + pw->shadow_width;
941   pw->thermo_field_y = pw->shadow_width * 5;
942   pw->thermo_field_height = pw->height - (pw->shadow_width * 10);
943
944 #if 0
945   /* Solid border inside the logo box. */
946   XSetForeground (si->dpy, gc1, pw->foreground);
947   XDrawRectangle (si->dpy, si->passwd_dialog, gc1, x1, y1, x2-1, y2-1);
948 #endif
949
950   /* The shadow around the logo
951    */
952   draw_shaded_rectangle (si->dpy, si->passwd_dialog,
953                          pw->shadow_width * 4,
954                          pw->shadow_width * 4,
955                          pw->logo_width - (pw->shadow_width * 8),
956                          pw->logo_height - (pw->shadow_width * 8),
957                          pw->shadow_width,
958                          pw->shadow_bottom, pw->shadow_top);
959
960   /* The shadow around the thermometer
961    */
962   draw_shaded_rectangle (si->dpy, si->passwd_dialog,
963                          pw->logo_width,
964                          pw->shadow_width * 4,
965                          pw->thermo_width + (pw->shadow_width * 2),
966                          pw->height - (pw->shadow_width * 8),
967                          pw->shadow_width,
968                          pw->shadow_bottom, pw->shadow_top);
969
970 #if 1
971   /* Solid border inside the thermometer. */
972   XSetForeground (si->dpy, gc1, pw->foreground);
973   XDrawRectangle (si->dpy, si->passwd_dialog, gc1, 
974                   pw->thermo_field_x, pw->thermo_field_y,
975                   pw->thermo_width - 1, pw->thermo_field_height - 1);
976 #endif
977
978   /* The shadow around the whole window
979    */
980   draw_shaded_rectangle (si->dpy, si->passwd_dialog,
981                          0, 0, pw->width, pw->height, pw->shadow_width,
982                          pw->shadow_top, pw->shadow_bottom);
983
984   XFreeGC (si->dpy, gc1);
985   XFreeGC (si->dpy, gc2);
986
987   update_passwd_window (si, pw->passwd_string, pw->ratio);
988 }
989
990 static void
991 draw_button(Display *dpy,
992             Drawable dialog,
993             XFontStruct *font,
994             unsigned long foreground, unsigned long background,
995             char *label,
996             int x, int y,
997             int width, int height,
998             int shadow_width,
999             Pixel shadow_light, Pixel shadow_dark,
1000             Bool button_down)
1001 {
1002   XGCValues gcv;
1003   GC gc1, gc2;
1004   int sw;
1005   int label_x, label_y;
1006
1007   gcv.foreground = foreground;
1008   gcv.font = font->fid;
1009   gc1 = XCreateGC(dpy, dialog, GCForeground|GCFont, &gcv);
1010   gcv.foreground = background;
1011   gc2 = XCreateGC(dpy, dialog, GCForeground, &gcv);
1012
1013   XFillRectangle(dpy, dialog, gc2,
1014                  x, y, width, height);
1015
1016   sw = string_width(font, label);
1017
1018   label_x = x + ((width - sw) / 2);
1019   label_y = (y + (height - (font->ascent + font->descent)) / 2 + font->ascent);
1020
1021   if (button_down)
1022     {
1023       label_x += 2;
1024       label_y += 2;
1025     }
1026
1027   XDrawString(dpy, dialog, gc1, label_x, label_y, label, strlen(label));
1028
1029   XFreeGC(dpy, gc1);
1030   XFreeGC(dpy, gc2);
1031
1032   draw_shaded_rectangle(dpy, dialog, x, y, width, height,
1033                         shadow_width, shadow_light, shadow_dark);
1034 }
1035
1036 static void
1037 update_passwd_window (saver_info *si, const char *printed_passwd, float ratio)
1038 {
1039   passwd_dialog_data *pw = si->pw_data;
1040   XGCValues gcv;
1041   GC gc1, gc2;
1042   int x, y;
1043   XRectangle rects[1];
1044
1045   pw->ratio = ratio;
1046   gcv.foreground = pw->passwd_foreground;
1047   gcv.font = pw->passwd_font->fid;
1048   gc1 = XCreateGC (si->dpy, si->passwd_dialog, GCForeground|GCFont, &gcv);
1049   gcv.foreground = pw->passwd_background;
1050   gc2 = XCreateGC (si->dpy, si->passwd_dialog, GCForeground, &gcv);
1051
1052   if (printed_passwd)
1053     {
1054       char *s = strdup (printed_passwd);
1055       if (pw->passwd_string) free (pw->passwd_string);
1056       pw->passwd_string = s;
1057     }
1058
1059   if (pw->prompt_label)
1060     {
1061
1062       /* the "password" text field
1063        */
1064       rects[0].x =  pw->passwd_field_x;
1065       rects[0].y =  pw->passwd_field_y;
1066       rects[0].width = pw->passwd_field_width;
1067       rects[0].height = pw->passwd_field_height;
1068
1069       /* The user entry (password) field is double buffered.
1070        * This avoids flickering, particularly in synchronous mode. */
1071
1072       if (pw->passwd_changed_p)
1073         {
1074           pw->passwd_changed_p = False;
1075
1076           if (pw->user_entry_pixmap)
1077             {
1078               XFreePixmap(si->dpy, pw->user_entry_pixmap);
1079               pw->user_entry_pixmap = 0;
1080             }
1081
1082           pw->user_entry_pixmap = 
1083             XCreatePixmap (si->dpy, si->passwd_dialog,
1084                            rects[0].width, rects[0].height, 
1085                            DefaultDepthOfScreen (pw->prompt_screen->screen));
1086
1087           XFillRectangle (si->dpy, pw->user_entry_pixmap, gc2,
1088                           0, 0, rects[0].width, rects[0].height);
1089
1090           XDrawString (si->dpy, pw->user_entry_pixmap, gc1,
1091                        pw->shadow_width,
1092                        pw->passwd_font->ascent,
1093                        pw->passwd_string, strlen(pw->passwd_string));
1094
1095           /* Ensure the new pixmap gets copied to the window */
1096           pw->i_beam = 0;
1097
1098         }
1099
1100       /* The I-beam
1101        */
1102       if (pw->i_beam == 0)
1103         {
1104           /* Make the I-beam disappear */
1105           XCopyArea(si->dpy, pw->user_entry_pixmap, si->passwd_dialog, gc2,
1106                     0, 0, rects[0].width, rects[0].height,
1107                     rects[0].x, rects[0].y);
1108         }
1109       else if (pw->i_beam == 1)
1110         {
1111           /* Make the I-beam appear */
1112           x = (rects[0].x + pw->shadow_width +
1113                string_width (pw->passwd_font, pw->passwd_string));
1114           y = rects[0].y + pw->shadow_width;
1115
1116           if (x > rects[0].x + rects[0].width - 1)
1117             x = rects[0].x + rects[0].width - 1;
1118           XDrawLine (si->dpy, si->passwd_dialog, gc1, 
1119                      x, y,
1120                      x, y + pw->passwd_font->ascent + pw->passwd_font->descent-1);
1121         }
1122
1123       pw->i_beam = (pw->i_beam + 1) % 4;
1124
1125     }
1126
1127   /* the thermometer
1128    */
1129   y = (pw->thermo_field_height - 2) * (1.0 - pw->ratio);
1130   if (y > 0)
1131     {
1132       XFillRectangle (si->dpy, si->passwd_dialog, gc2,
1133                       pw->thermo_field_x + 1,
1134                       pw->thermo_field_y + 1,
1135                       pw->thermo_width-2,
1136                       y);
1137       XSetForeground (si->dpy, gc1, pw->thermo_foreground);
1138       XFillRectangle (si->dpy, si->passwd_dialog, gc1,
1139                       pw->thermo_field_x + 1,
1140                       pw->thermo_field_y + 1 + y,
1141                       pw->thermo_width-2,
1142                       MAX (0, pw->thermo_field_height - y - 2));
1143     }
1144
1145   if (pw->button_state_changed_p)
1146     {
1147       pw->button_state_changed_p = False;
1148
1149       /* The "Unlock" button
1150        */
1151       draw_button(si->dpy, si->passwd_dialog, pw->button_font,
1152                   pw->button_foreground, pw->button_background,
1153                   pw->unlock_label,
1154                   pw->unlock_button_x, pw->unlock_button_y,
1155                   pw->unlock_button_width, pw->unlock_button_height,
1156                   pw->shadow_width,
1157                   (pw->unlock_button_down_p ? pw->shadow_bottom : pw->shadow_top),
1158                   (pw->unlock_button_down_p ? pw->shadow_top : pw->shadow_bottom),
1159                   pw->unlock_button_down_p);
1160
1161       /* The "New Login" button
1162        */
1163       if (pw->login_button_p)
1164         {
1165           draw_button(si->dpy, si->passwd_dialog, pw->button_font,
1166                       (pw->login_button_enabled_p
1167                        ? pw->passwd_foreground
1168                        : pw->shadow_bottom),
1169                       pw->button_background,
1170                       pw->login_label,
1171                       pw->login_button_x, pw->login_button_y,
1172                       pw->login_button_width, pw->login_button_height,
1173                       pw->shadow_width,
1174                       (pw->login_button_down_p
1175                        ? pw->shadow_bottom
1176                        : pw->shadow_top),
1177                       (pw->login_button_down_p
1178                        ? pw->shadow_top
1179                        : pw->shadow_bottom),
1180                       pw->login_button_down_p);
1181         }
1182     }
1183
1184   XFreeGC (si->dpy, gc1);
1185   XFreeGC (si->dpy, gc2);
1186   XSync (si->dpy, False);
1187 }
1188
1189
1190 void
1191 restore_background (saver_info *si)
1192 {
1193   passwd_dialog_data *pw = si->pw_data;
1194   saver_screen_info *ssi = pw->prompt_screen;
1195   XGCValues gcv;
1196   GC gc;
1197
1198   gcv.function = GXcopy;
1199
1200   gc = XCreateGC (si->dpy, ssi->screensaver_window, GCFunction, &gcv);
1201
1202   XCopyArea (si->dpy, pw->save_under,
1203              ssi->screensaver_window, gc,
1204              0, 0,
1205              ssi->width, ssi->height,
1206              0, 0);
1207
1208   XFreeGC (si->dpy, gc);
1209 }
1210
1211
1212 /* Frees anything created by make_passwd_window */
1213 static void
1214 cleanup_passwd_window (saver_info *si)
1215 {
1216   passwd_dialog_data *pw;
1217
1218   if (!(pw = si->pw_data))
1219     return;
1220
1221   if (pw->info_label)
1222     {
1223       mlstring_free(pw->info_label);
1224       pw->info_label = 0;
1225     }
1226
1227   if (pw->prompt_label)
1228     {
1229       mlstring_free(pw->prompt_label);
1230       pw->prompt_label = 0;
1231     }
1232
1233   memset (pw->typed_passwd, 0, sizeof(pw->typed_passwd));
1234   memset (pw->passwd_string, 0, strlen(pw->passwd_string));
1235
1236   if (pw->timer)
1237     {
1238       XtRemoveTimeOut (pw->timer);
1239       pw->timer = 0;
1240     }
1241
1242   if (pw->user_entry_pixmap)
1243     {
1244       XFreePixmap(si->dpy, pw->user_entry_pixmap);
1245       pw->user_entry_pixmap = 0;
1246     }
1247 }
1248
1249
1250 static void
1251 destroy_passwd_window (saver_info *si)
1252 {
1253   saver_preferences *p = &si->prefs;
1254   passwd_dialog_data *pw = si->pw_data;
1255   saver_screen_info *ssi = pw->prompt_screen;
1256   Colormap cmap = DefaultColormapOfScreen (ssi->screen);
1257   Pixel black = BlackPixelOfScreen (ssi->screen);
1258   Pixel white = WhitePixelOfScreen (ssi->screen);
1259   XEvent event;
1260
1261   cleanup_passwd_window (si);
1262
1263   if (si->cached_passwd)
1264     {
1265       char *wipe = si->cached_passwd;
1266
1267       while (*wipe)
1268         *wipe++ = '\0';
1269
1270       free(si->cached_passwd);
1271       si->cached_passwd = NULL;
1272     }
1273
1274   move_mouse_grab (si, RootWindowOfScreen (ssi->screen),
1275                    ssi->cursor, ssi->number);
1276
1277   if (pw->passwd_cursor)
1278     XFreeCursor (si->dpy, pw->passwd_cursor);
1279
1280   if (p->verbose_p)
1281     fprintf (stderr, "%s: %d: moving mouse back to %d,%d.\n",
1282              blurb(), ssi->number,
1283              pw->previous_mouse_x, pw->previous_mouse_y);
1284
1285   XWarpPointer (si->dpy, None, RootWindowOfScreen (ssi->screen),
1286                 0, 0, 0, 0,
1287                 pw->previous_mouse_x, pw->previous_mouse_y);
1288
1289   while (XCheckMaskEvent (si->dpy, PointerMotionMask, &event))
1290     if (p->verbose_p)
1291       fprintf (stderr, "%s: discarding MotionNotify event.\n", blurb());
1292
1293   if (si->passwd_dialog)
1294     {
1295       if (si->prefs.verbose_p)
1296         fprintf (stderr, "%s: %d: destroying password dialog.\n",
1297                  blurb(), pw->prompt_screen->number);
1298
1299       XDestroyWindow (si->dpy, si->passwd_dialog);
1300       si->passwd_dialog = 0;
1301     }
1302   
1303   if (pw->save_under)
1304     {
1305       restore_background(si);
1306       XFreePixmap (si->dpy, pw->save_under);
1307       pw->save_under = 0;
1308     }
1309
1310   if (pw->heading_label) free (pw->heading_label);
1311   if (pw->body_label)    free (pw->body_label);
1312   if (pw->user_label)    free (pw->user_label);
1313   if (pw->date_label)    free (pw->date_label);
1314   if (pw->login_label)   free (pw->login_label);
1315   if (pw->unlock_label)  free (pw->unlock_label);
1316   if (pw->passwd_string) free (pw->passwd_string);
1317   if (pw->uname_label)   free (pw->uname_label);
1318
1319   if (pw->heading_font) XFreeFont (si->dpy, pw->heading_font);
1320   if (pw->body_font)    XFreeFont (si->dpy, pw->body_font);
1321   if (pw->label_font)   XFreeFont (si->dpy, pw->label_font);
1322   if (pw->passwd_font)  XFreeFont (si->dpy, pw->passwd_font);
1323   if (pw->date_font)    XFreeFont (si->dpy, pw->date_font);
1324   if (pw->button_font)  XFreeFont (si->dpy, pw->button_font);
1325   if (pw->uname_font)   XFreeFont (si->dpy, pw->uname_font);
1326
1327   if (pw->foreground != black && pw->foreground != white)
1328     XFreeColors (si->dpy, cmap, &pw->foreground, 1, 0L);
1329   if (pw->background != black && pw->background != white)
1330     XFreeColors (si->dpy, cmap, &pw->background, 1, 0L);
1331   if (!(pw->button_foreground == black || pw->button_foreground == white))
1332     XFreeColors (si->dpy, cmap, &pw->button_foreground, 1, 0L);
1333   if (!(pw->button_background == black || pw->button_background == white))
1334     XFreeColors (si->dpy, cmap, &pw->button_background, 1, 0L);
1335   if (pw->passwd_foreground != black && pw->passwd_foreground != white)
1336     XFreeColors (si->dpy, cmap, &pw->passwd_foreground, 1, 0L);
1337   if (pw->passwd_background != black && pw->passwd_background != white)
1338     XFreeColors (si->dpy, cmap, &pw->passwd_background, 1, 0L);
1339   if (pw->thermo_foreground != black && pw->thermo_foreground != white)
1340     XFreeColors (si->dpy, cmap, &pw->thermo_foreground, 1, 0L);
1341   if (pw->thermo_background != black && pw->thermo_background != white)
1342     XFreeColors (si->dpy, cmap, &pw->thermo_background, 1, 0L);
1343   if (pw->shadow_top != black && pw->shadow_top != white)
1344     XFreeColors (si->dpy, cmap, &pw->shadow_top, 1, 0L);
1345   if (pw->shadow_bottom != black && pw->shadow_bottom != white)
1346     XFreeColors (si->dpy, cmap, &pw->shadow_bottom, 1, 0L);
1347
1348   if (pw->logo_pixmap)
1349     XFreePixmap (si->dpy, pw->logo_pixmap);
1350   if (pw-> logo_clipmask)
1351     XFreePixmap (si->dpy, pw->logo_clipmask);
1352   if (pw->logo_pixels)
1353     {
1354       if (pw->logo_npixels)
1355         XFreeColors (si->dpy, cmap, pw->logo_pixels, pw->logo_npixels, 0L);
1356       free (pw->logo_pixels);
1357       pw->logo_pixels = 0;
1358       pw->logo_npixels = 0;
1359     }
1360
1361   if (pw->save_under)
1362     XFreePixmap (si->dpy, pw->save_under);
1363
1364   if (cmap)
1365     XInstallColormap (si->dpy, cmap);
1366
1367   memset (pw, 0, sizeof(*pw));
1368   free (pw);
1369   si->pw_data = 0;
1370 }
1371
1372
1373 static Bool error_handler_hit_p = False;
1374
1375 static int
1376 ignore_all_errors_ehandler (Display *dpy, XErrorEvent *error)
1377 {
1378   error_handler_hit_p = True;
1379   return 0;
1380 }
1381
1382
1383 #ifdef HAVE_XHPDISABLERESET
1384 /* This function enables and disables the C-Sh-Reset hot-key, which
1385    normally resets the X server (logging out the logged-in user.)
1386    We don't want random people to be able to do that while the
1387    screen is locked.
1388  */
1389 static void
1390 hp_lock_reset (saver_info *si, Bool lock_p)
1391 {
1392   static Bool hp_locked_p = False;
1393
1394   /* Calls to XHPDisableReset and XHPEnableReset must be balanced,
1395      or BadAccess errors occur.  (It's ok for this to be global,
1396      since it affects the whole machine, not just the current screen.)
1397   */
1398   if (hp_locked_p == lock_p)
1399     return;
1400
1401   if (lock_p)
1402     XHPDisableReset (si->dpy);
1403   else
1404     XHPEnableReset (si->dpy);
1405   hp_locked_p = lock_p;
1406 }
1407 #endif /* HAVE_XHPDISABLERESET */
1408
1409 \f
1410 #ifdef HAVE_XF86MISCSETGRABKEYSSTATE
1411
1412 /* This function enables and disables the Ctrl-Alt-KP_star and 
1413    Ctrl-Alt-KP_slash hot-keys, which (in XFree86 4.2) break any
1414    grabs and/or kill the grabbing client.  That would effectively
1415    unlock the screen, so we don't like that.
1416
1417    The Ctrl-Alt-KP_star and Ctrl-Alt-KP_slash hot-keys only exist
1418    if AllowDeactivateGrabs and/or AllowClosedownGrabs are turned on
1419    in XF86Config.  I believe they are disabled by default.
1420
1421    This does not affect any other keys (specifically Ctrl-Alt-BS or
1422    Ctrl-Alt-F1) but I wish it did.  Maybe it will someday.
1423  */
1424 static void
1425 xfree_lock_grab_smasher (saver_info *si, Bool lock_p)
1426 {
1427   saver_preferences *p = &si->prefs;
1428   int status;
1429   int event, error;
1430   XErrorHandler old_handler;
1431
1432   if (!XF86MiscQueryExtension(si->dpy, &event, &error))
1433     return;
1434
1435   XSync (si->dpy, False);
1436   error_handler_hit_p = False;
1437   old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1438   XSync (si->dpy, False);
1439   status = XF86MiscSetGrabKeysState (si->dpy, !lock_p);
1440   XSync (si->dpy, False);
1441   if (error_handler_hit_p) status = 666;
1442
1443   if (!lock_p && status == MiscExtGrabStateAlready)
1444     status = MiscExtGrabStateSuccess;  /* shut up, consider this success */
1445
1446   if (p->verbose_p && status != MiscExtGrabStateSuccess)
1447     fprintf (stderr, "%s: error: XF86MiscSetGrabKeysState(%d) returned %s\n",
1448              blurb(), !lock_p,
1449              (status == MiscExtGrabStateSuccess ? "MiscExtGrabStateSuccess" :
1450               status == MiscExtGrabStateLocked  ? "MiscExtGrabStateLocked"  :
1451               status == MiscExtGrabStateAlready ? "MiscExtGrabStateAlready" :
1452               status == 666 ? "an X error" :
1453               "unknown value"));
1454
1455   XSync (si->dpy, False);
1456   XSetErrorHandler (old_handler);
1457   XSync (si->dpy, False);
1458 }
1459 #endif /* HAVE_XF86MISCSETGRABKEYSSTATE */
1460
1461
1462 \f
1463 /* This function enables and disables the C-Alt-Plus and C-Alt-Minus
1464    hot-keys, which normally change the resolution of the X server.
1465    We don't want people to be able to switch the server resolution
1466    while the screen is locked, because if they switch to a higher
1467    resolution, it could cause part of the underlying desktop to become
1468    exposed.
1469  */
1470 #ifdef HAVE_XF86VMODE
1471
1472 static void
1473 xfree_lock_mode_switch (saver_info *si, Bool lock_p)
1474 {
1475   static Bool any_mode_locked_p = False;
1476   saver_preferences *p = &si->prefs;
1477   int screen;
1478   int real_nscreens = ScreenCount (si->dpy);
1479   int event, error;
1480   Bool status;
1481   XErrorHandler old_handler;
1482
1483   if (any_mode_locked_p == lock_p)
1484     return;
1485   if (!XF86VidModeQueryExtension (si->dpy, &event, &error))
1486     return;
1487
1488   for (screen = 0; screen < real_nscreens; screen++)
1489     {
1490       XSync (si->dpy, False);
1491       old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1492       error_handler_hit_p = False;
1493       status = XF86VidModeLockModeSwitch (si->dpy, screen, lock_p);
1494       XSync (si->dpy, False);
1495       XSetErrorHandler (old_handler);
1496       if (error_handler_hit_p) status = False;
1497
1498       if (status)
1499         any_mode_locked_p = lock_p;
1500
1501       if (!status && (p->verbose_p || !lock_p))
1502         /* Only print this when verbose, or when we locked but can't unlock.
1503            I tried printing this message whenever it comes up, but
1504            mode-locking always fails if DontZoom is set in XF86Config. */
1505         fprintf (stderr, "%s: %d: unable to %s mode switching!\n",
1506                  blurb(), screen, (lock_p ? "lock" : "unlock"));
1507       else if (p->verbose_p)
1508         fprintf (stderr, "%s: %d: %s mode switching.\n",
1509                  blurb(), screen, (lock_p ? "locked" : "unlocked"));
1510     }
1511 }
1512 #endif /* HAVE_XF86VMODE */
1513
1514 \f
1515 /* If the viewport has been scrolled since the screen was blanked,
1516    then scroll it back to where it belongs.  This function only exists
1517    to patch over a very brief race condition.
1518  */
1519 static void
1520 undo_vp_motion (saver_info *si)
1521 {
1522 #ifdef HAVE_XF86VMODE
1523   saver_preferences *p = &si->prefs;
1524   int screen;
1525   int real_nscreens = ScreenCount (si->dpy);
1526   int event, error;
1527
1528   if (!XF86VidModeQueryExtension (si->dpy, &event, &error))
1529     return;
1530
1531   for (screen = 0; screen < real_nscreens; screen++)
1532     {
1533       saver_screen_info *ssi = &si->screens[screen];
1534       int x, y;
1535       Bool status;
1536
1537       if (ssi->blank_vp_x == -1 && ssi->blank_vp_y == -1)
1538         break;
1539       if (!XF86VidModeGetViewPort (si->dpy, screen, &x, &y))
1540         return;
1541       if (ssi->blank_vp_x == x && ssi->blank_vp_y == y)
1542         return;
1543     
1544       /* We're going to move the viewport.  The mouse has just been grabbed on
1545          (and constrained to, thus warped to) the password window, so it is no
1546          longer near the edge of the screen.  However, wait a bit anyway, just
1547          to make sure the server drains its last motion event, so that the
1548          screen doesn't continue to scroll after we've reset the viewport.
1549        */
1550       XSync (si->dpy, False);
1551       usleep (250000);  /* 1/4 second */
1552       XSync (si->dpy, False);
1553
1554       status = XF86VidModeSetViewPort (si->dpy, screen,
1555                                        ssi->blank_vp_x, ssi->blank_vp_y);
1556
1557       if (!status)
1558         fprintf (stderr,
1559                  "%s: %d: unable to move vp from (%d,%d) back to (%d,%d)!\n",
1560                  blurb(), screen, x, y, ssi->blank_vp_x, ssi->blank_vp_y);
1561       else if (p->verbose_p)
1562         fprintf (stderr,
1563                  "%s: %d: vp moved to (%d,%d); moved it back to (%d,%d).\n",
1564                  blurb(), screen, x, y, ssi->blank_vp_x, ssi->blank_vp_y);
1565     }
1566 #endif /* HAVE_XF86VMODE */
1567 }
1568
1569
1570 \f
1571 /* Interactions
1572  */
1573
1574 static void
1575 passwd_animate_timer (XtPointer closure, XtIntervalId *id)
1576 {
1577   saver_info *si = (saver_info *) closure;
1578   int tick = 166;
1579   passwd_dialog_data *pw = si->pw_data;
1580
1581   if (!pw) return;
1582
1583   pw->ratio -= (1.0 / ((double) si->prefs.passwd_timeout / (double) tick));
1584   if (pw->ratio < 0)
1585     {
1586       pw->ratio = 0;
1587       if (si->unlock_state == ul_read)
1588         si->unlock_state = ul_time;
1589     }
1590
1591   update_passwd_window (si, 0, pw->ratio);
1592
1593   if (si->unlock_state == ul_read)
1594     pw->timer = XtAppAddTimeOut (si->app, tick, passwd_animate_timer,
1595                                  (XtPointer) si);
1596   else
1597     pw->timer = 0;
1598
1599   idle_timer ((XtPointer) si, 0);
1600 }
1601
1602
1603 static XComposeStatus *compose_status;
1604
1605 static void
1606 handle_login_button (saver_info *si, XEvent *event)
1607 {
1608   saver_preferences *p = &si->prefs;
1609   Bool mouse_in_box = False;
1610   Bool hit_p = False;
1611   passwd_dialog_data *pw = si->pw_data;
1612   saver_screen_info *ssi = pw->prompt_screen;
1613
1614   if (! pw->login_button_enabled_p)
1615     return;
1616
1617   mouse_in_box = 
1618     (event->xbutton.x >= pw->login_button_x &&
1619      event->xbutton.x <= pw->login_button_x + pw->login_button_width &&
1620      event->xbutton.y >= pw->login_button_y &&
1621      event->xbutton.y <= pw->login_button_y + pw->login_button_height);
1622
1623   if (ButtonRelease == event->xany.type &&
1624       pw->login_button_down_p &&
1625       mouse_in_box)
1626     {
1627       /* Only allow them to press the button once: don't want to
1628          accidentally launch a dozen gdm choosers if the machine
1629          is being slow.
1630        */
1631       hit_p = True;
1632       pw->login_button_enabled_p = False;
1633     }
1634
1635   pw->login_button_down_p = (mouse_in_box &&
1636                              ButtonRelease != event->xany.type);
1637
1638   update_passwd_window (si, 0, pw->ratio);
1639
1640   if (hit_p)
1641     fork_and_exec (ssi, p->new_login_command);
1642 }
1643
1644
1645 static void
1646 handle_unlock_button (saver_info *si, XEvent *event)
1647 {
1648   Bool mouse_in_box = False;
1649   passwd_dialog_data *pw = si->pw_data;
1650
1651   mouse_in_box =
1652     (event->xbutton.x >= pw->unlock_button_x &&
1653      event->xbutton.x <= pw->unlock_button_x + pw->unlock_button_width &&
1654      event->xbutton.y >= pw->unlock_button_y &&
1655      event->xbutton.y <= pw->unlock_button_y + pw->unlock_button_height);
1656
1657   if (ButtonRelease == event->xany.type &&
1658       pw->unlock_button_down_p &&
1659       mouse_in_box)
1660     finished_typing_passwd (si, pw);
1661
1662   pw->unlock_button_down_p = (mouse_in_box &&
1663                                 ButtonRelease != event->xany.type);
1664 }
1665
1666
1667 static void
1668 finished_typing_passwd (saver_info *si, passwd_dialog_data *pw)
1669 {
1670   if (si->unlock_state == ul_read)
1671     {
1672       update_passwd_window (si, "Checking...", pw->ratio);
1673       XSync (si->dpy, False);
1674
1675       si->unlock_state = ul_finished;
1676       update_passwd_window (si, "", pw->ratio);
1677     }
1678 }
1679
1680 static void
1681 handle_passwd_key (saver_info *si, XKeyEvent *event)
1682 {
1683   passwd_dialog_data *pw = si->pw_data;
1684   int pw_size = sizeof (pw->typed_passwd) - 1;
1685   char *typed_passwd = pw->typed_passwd;
1686   char s[2];
1687   char *stars = 0;
1688   int i;
1689   int size = XLookupString (event, s, 1, 0, compose_status);
1690
1691   if (size != 1) return;
1692
1693   s[1] = 0;
1694
1695   pw->passwd_changed_p = True;
1696
1697   /* Add 10% to the time remaining every time a key is pressed. */
1698   pw->ratio += 0.1;
1699   if (pw->ratio > 1) pw->ratio = 1;
1700
1701   switch (*s)
1702     {
1703     case '\010': case '\177':                           /* Backspace */
1704       if (!*typed_passwd)
1705         XBell (si->dpy, 0);
1706       else
1707         typed_passwd [strlen(typed_passwd)-1] = 0;
1708       break;
1709
1710     case '\025': case '\030':                           /* Erase line */
1711       memset (typed_passwd, 0, pw_size);
1712       break;
1713
1714     case '\012': case '\015':                           /* Enter */
1715       finished_typing_passwd(si, pw);
1716       break;
1717
1718     case '\033':                                        /* Escape */
1719       si->unlock_state = ul_cancel;
1720       break;
1721
1722     default:
1723       /* Though technically the only illegal characters in Unix passwords
1724          are LF and NUL, most GUI programs (e.g., GDM) use regular text-entry
1725          fields that only let you type printable characters.  So, people
1726          who use funky characters in their passwords are already broken.
1727          We follow that precedent.
1728        */
1729       if (isprint ((unsigned char) *s))
1730         {
1731           i = strlen (typed_passwd);
1732           if (i >= pw_size-1)
1733             XBell (si->dpy, 0);
1734           else
1735             {
1736               typed_passwd [i] = *s;
1737               typed_passwd [i+1] = 0;
1738             }
1739         }
1740       else
1741         XBell (si->dpy, 0);
1742       break;
1743     }
1744
1745   if (pw->echo_input)
1746     {
1747       /* If the input is wider than the text box, only show the last portion.
1748        * This simulates a horizontally scrolling text field. */
1749       int chars_in_pwfield = (pw->passwd_field_width /
1750                               pw->passwd_font->max_bounds.width);
1751
1752       if (strlen(typed_passwd) > chars_in_pwfield)
1753         typed_passwd += (strlen(typed_passwd) - chars_in_pwfield);
1754
1755       update_passwd_window(si, typed_passwd, pw->ratio);
1756     }
1757   else if (pw->show_stars_p)
1758     {
1759       i = strlen(typed_passwd);
1760       stars = (char *) malloc(i+1);
1761       memset (stars, '*', i);
1762       stars[i] = 0;
1763       update_passwd_window (si, stars, pw->ratio);
1764       free (stars);
1765     }
1766   else
1767     {
1768       update_passwd_window (si, "", pw->ratio);
1769     }
1770 }
1771
1772
1773 static void
1774 passwd_event_loop (saver_info *si)
1775 {
1776   saver_preferences *p = &si->prefs;
1777   char *msg = 0;
1778   XEvent event;
1779
1780   passwd_animate_timer ((XtPointer) si, 0);
1781
1782   while (si->unlock_state == ul_read)
1783     {
1784       XtAppNextEvent (si->app, &event);
1785       if (event.xany.window == si->passwd_dialog && event.xany.type == Expose)
1786         draw_passwd_window (si);
1787       else if (event.xany.type == KeyPress)
1788         {
1789           handle_passwd_key (si, &event.xkey);
1790           si->pw_data->caps_p = (event.xkey.state & LockMask);
1791         }
1792       else if (event.xany.type == ButtonPress || 
1793                event.xany.type == ButtonRelease)
1794         {
1795           si->pw_data->button_state_changed_p = True;
1796           handle_unlock_button (si, &event);
1797           if (si->pw_data->login_button_p)
1798             handle_login_button (si, &event);
1799         }
1800       else
1801         XtDispatchEvent (&event);
1802     }
1803
1804   switch (si->unlock_state)
1805     {
1806     case ul_cancel: msg = ""; break;
1807     case ul_time: msg = "Timed out!"; break;
1808     case ul_finished: msg = "Checking..."; break;
1809     default: msg = 0; break;
1810     }
1811
1812   if (p->verbose_p)
1813     switch (si->unlock_state) {
1814     case ul_cancel:
1815       fprintf (stderr, "%s: input cancelled.\n", blurb()); break;
1816     case ul_time:
1817       fprintf (stderr, "%s: input timed out.\n", blurb()); break;
1818     case ul_finished:
1819       fprintf (stderr, "%s: input finished.\n", blurb()); break;
1820     default: break;
1821     }
1822
1823   if (msg)
1824     {
1825       si->pw_data->i_beam = 0;
1826       update_passwd_window (si, msg, 0.0);
1827       XSync (si->dpy, False);
1828
1829       /* Swallow all pending KeyPress/KeyRelease events. */
1830       {
1831         XEvent e;
1832         while (XCheckMaskEvent (si->dpy, KeyPressMask|KeyReleaseMask, &e))
1833           ;
1834       }
1835     }
1836 }
1837
1838
1839 static void
1840 handle_typeahead (saver_info *si)
1841 {
1842   passwd_dialog_data *pw = si->pw_data;
1843   int i;
1844   if (!si->unlock_typeahead)
1845     return;
1846
1847   pw->passwd_changed_p = True;
1848
1849   i = strlen (si->unlock_typeahead);
1850   if (i >= sizeof(pw->typed_passwd) - 1)
1851     i = sizeof(pw->typed_passwd) - 1;
1852
1853   memcpy (pw->typed_passwd, si->unlock_typeahead, i);
1854   pw->typed_passwd [i] = 0;
1855
1856   memset (si->unlock_typeahead, '*', strlen(si->unlock_typeahead));
1857   si->unlock_typeahead[i] = 0;
1858   update_passwd_window (si, si->unlock_typeahead, pw->ratio);
1859
1860   free (si->unlock_typeahead);
1861   si->unlock_typeahead = 0;
1862 }
1863
1864
1865 /**
1866  * Returns a copy of the input string with trailing whitespace removed.
1867  * Whitespace is anything considered so by isspace().
1868  * It is safe to call this with NULL, in which case NULL will be returned.
1869  * The returned string (if not NULL) should be freed by the caller with free().
1870  */
1871 static char *
1872 remove_trailing_whitespace(const char *str)
1873 {
1874   size_t len;
1875   char *newstr, *chr;
1876
1877   if (!str)
1878     return NULL;
1879
1880   len = strlen(str);
1881
1882   newstr = malloc(len + 1);
1883   (void) strcpy(newstr, str);
1884
1885   if (!newstr)
1886     return NULL;
1887
1888   chr = newstr + len;
1889   while (isspace(*--chr) && chr >= newstr)
1890     *chr = '\0';
1891
1892   return newstr;
1893 }
1894
1895
1896 /*
1897  * The authentication conversation function.
1898  * Like a PAM conversation function, this accepts multiple messages in a single
1899  * round. It then splits them into individual messages for display on the
1900  * passwd dialog. A message sequence of info or error followed by a prompt will
1901  * be reduced into a single dialog window.
1902  *
1903  * Returns 0 on success or -1 if some problem occurred (cancelled auth, OOM, ...)
1904  */
1905 int
1906 gui_auth_conv(int num_msg,
1907           const struct auth_message auth_msgs[],
1908           struct auth_response **resp,
1909           saver_info *si)
1910 {
1911   int i;
1912   const char *info_msg, *prompt;
1913   struct auth_response *responses;
1914
1915   if (si->unlock_state == ul_cancel ||
1916       si->unlock_state == ul_time)
1917     /* If we've already cancelled or timed out in this PAM conversation,
1918        don't prompt again even if PAM asks us to! */
1919     return -1;
1920
1921   if (!(responses = calloc(num_msg, sizeof(struct auth_response))))
1922     goto fail;
1923
1924   for (i = 0; i < num_msg; ++i)
1925     {
1926       info_msg = prompt = NULL;
1927
1928       /* See if there is a following message that can be shown at the same
1929        * time */
1930       if (auth_msgs[i].type == AUTH_MSGTYPE_INFO
1931           && i+1 < num_msg
1932           && (   auth_msgs[i+1].type == AUTH_MSGTYPE_PROMPT_NOECHO
1933               || auth_msgs[i+1].type == AUTH_MSGTYPE_PROMPT_ECHO)
1934          )
1935         {
1936           info_msg = auth_msgs[i].msg;
1937           prompt = auth_msgs[++i].msg;
1938         }
1939       else
1940         {
1941           if (   auth_msgs[i].type == AUTH_MSGTYPE_INFO
1942               || auth_msgs[i].type == AUTH_MSGTYPE_ERROR)
1943             info_msg = auth_msgs[i].msg;
1944           else
1945             prompt = auth_msgs[i].msg;
1946         }
1947
1948       {
1949         char *info_msg_trimmed, *prompt_trimmed;
1950
1951         /* Trailing whitespace looks bad in a GUI */
1952         info_msg_trimmed = remove_trailing_whitespace(info_msg);
1953         prompt_trimmed = remove_trailing_whitespace(prompt);
1954
1955         if (make_passwd_window(si, info_msg_trimmed, prompt_trimmed,
1956                                auth_msgs[i].type == AUTH_MSGTYPE_PROMPT_ECHO
1957                                ? True : False)
1958             < 0)
1959           goto fail;
1960
1961         if (info_msg_trimmed)
1962           free(info_msg_trimmed);
1963
1964         if (prompt_trimmed)
1965           free(prompt_trimmed);
1966       }
1967
1968       compose_status = calloc (1, sizeof (*compose_status));
1969       if (!compose_status)
1970         goto fail;
1971
1972       si->unlock_state = ul_read;
1973
1974       handle_typeahead (si);
1975       passwd_event_loop (si);
1976
1977       if (si->unlock_state == ul_cancel)
1978         goto fail;
1979
1980       responses[i].response = strdup(si->pw_data->typed_passwd);
1981
1982       /* Cache the first response to a PROMPT_NOECHO to save prompting for
1983        * each auth mechanism. */
1984       if (si->cached_passwd == NULL &&
1985           auth_msgs[i].type == AUTH_MSGTYPE_PROMPT_NOECHO)
1986         si->cached_passwd = strdup(responses[i].response);
1987
1988       free (compose_status);
1989       compose_status = 0;
1990     }
1991
1992   *resp = responses;
1993
1994   return (si->unlock_state == ul_finished) ? 0 : -1;
1995
1996 fail:
1997   if (compose_status)
1998     free (compose_status);
1999
2000   if (responses)
2001     {
2002       for (i = 0; i < num_msg; ++i)
2003         if (responses[i].response)
2004           free (responses[i].response);
2005       free (responses);
2006     }
2007
2008   return -1;
2009 }
2010
2011
2012 void
2013 auth_finished_cb (saver_info *si)
2014 {
2015   char buf[1024];
2016   const char *s;
2017
2018   /* If we have something to say, put the dialog back up for a few seconds
2019      to display it.  Otherwise, don't bother.
2020    */
2021
2022   if (si->unlock_state == ul_fail &&            /* failed with caps lock on */
2023       si->pw_data && si->pw_data->caps_p)
2024     s = "Authentication failed (Caps Lock?)";
2025   else if (si->unlock_state == ul_fail)         /* failed without caps lock */
2026     s = "Authentication failed!";
2027   else if (si->unlock_state == ul_success &&    /* good, but report failures */
2028            si->unlock_failures > 0)
2029     {
2030       if (si->unlock_failures == 1)
2031         s = "There has been\n1 failed login attempt.";
2032       else
2033         {
2034           sprintf (buf, "There have been\n%d failed login attempts.",
2035                    si->unlock_failures);
2036           s = buf;
2037         }
2038       si->unlock_failures = 0;
2039     }
2040   else                                          /* good, with no failures, */
2041     goto END;                                   /* or timeout, or cancel. */
2042
2043   make_passwd_window (si, s, NULL, True);
2044   XSync (si->dpy, False);
2045
2046   {
2047     int secs = 4;
2048     time_t start = time ((time_t *) 0);
2049     XEvent event;
2050     while (time ((time_t *) 0) < start + secs)
2051       if (XPending (si->dpy))
2052         {
2053           XNextEvent (si->dpy, &event);
2054           if (event.xany.window == si->passwd_dialog && 
2055               event.xany.type == Expose)
2056             draw_passwd_window (si);
2057           else if (event.xany.type == ButtonPress || 
2058                    event.xany.type == KeyPress)
2059             break;
2060           XSync (si->dpy, False);
2061         }
2062       else
2063         usleep (250000);  /* 1/4 second */
2064   }
2065
2066  END:
2067   if (si->pw_data)
2068     destroy_passwd_window (si);
2069 }
2070
2071
2072 Bool
2073 unlock_p (saver_info *si)
2074 {
2075   saver_preferences *p = &si->prefs;
2076
2077   if (!si->unlock_cb)
2078     {
2079       fprintf(stderr, "%s: Error: no unlock function specified!\n", blurb());
2080       return False;
2081     }
2082
2083   raise_window (si, True, True, True);
2084
2085   xss_authenticate(si, p->verbose_p);
2086
2087   return (si->unlock_state == ul_success);
2088 }
2089
2090
2091 void
2092 set_locked_p (saver_info *si, Bool locked_p)
2093 {
2094   si->locked_p = locked_p;
2095
2096 #ifdef HAVE_XHPDISABLERESET
2097   hp_lock_reset (si, locked_p);                 /* turn off/on C-Sh-Reset */
2098 #endif
2099 #ifdef HAVE_XF86VMODE
2100   xfree_lock_mode_switch (si, locked_p);        /* turn off/on C-Alt-Plus */
2101 #endif
2102 #ifdef HAVE_XF86MISCSETGRABKEYSSTATE
2103   xfree_lock_grab_smasher (si, locked_p);       /* turn off/on C-Alt-KP-*,/ */
2104 #endif
2105
2106   store_saver_status (si);                      /* store locked-p */
2107 }
2108
2109
2110 #else  /*  NO_LOCKING -- whole file */
2111
2112 void
2113 set_locked_p (saver_info *si, Bool locked_p)
2114 {
2115   if (locked_p) abort();
2116 }
2117
2118 #endif /* !NO_LOCKING */