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