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