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