http://ftp.nluug.nl/pub/os/Linux/distr/pardusrepo/sources/xscreensaver-5.02.tar.gz
[xscreensaver] / driver / lock.c
1 /* lock.c --- handling the password dialog for locking-mode.
2  * xscreensaver, Copyright (c) 1993-2007 Jamie Zawinski <jwz@jwz.org>
3  *
4  * Permission to use, copy, modify, distribute, and sell this software and its
5  * documentation for any purpose is hereby granted without fee, provided that
6  * the above copyright notice appear in all copies and that both that
7  * copyright notice and this permission notice appear in supporting
8  * documentation.  No representations are made about the suitability of this
9  * software for any purpose.  It is provided "as is" without express or 
10  * implied warranty.
11  */
12
13 /* Athena locking code contributed by Jon A. Christopher <jac8782@tamu.edu> */
14 /* Copyright 1997, with the same permissions as above. */
15
16 #ifdef HAVE_CONFIG_H
17 # include "config.h"
18 #endif
19
20 #include <ctype.h>
21 #include <X11/Intrinsic.h>
22 #include <X11/cursorfont.h>
23 #include <X11/Xos.h>            /* for time() */
24 #include <time.h>
25 #include <sys/time.h>
26 #include "xscreensaver.h"
27 #include "resources.h"
28 #include "mlstring.h"
29 #include "auth.h"
30
31 #ifndef NO_LOCKING              /* (mostly) whole file */
32
33 #ifdef HAVE_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 = XCreatePixmap(si->dpy, si->passwd_dialog,
1080               rects[0].width, rects[0].height, pw->prompt_screen->current_depth);
1081
1082
1083           XFillRectangle (si->dpy, pw->user_entry_pixmap, gc2,
1084                           0, 0, rects[0].width, rects[0].height);
1085
1086           XDrawString (si->dpy, pw->user_entry_pixmap, gc1,
1087                        pw->shadow_width,
1088                        pw->passwd_font->ascent,
1089                        pw->passwd_string, strlen(pw->passwd_string));
1090
1091           /* Ensure the new pixmap gets copied to the window */
1092           pw->i_beam = 0;
1093
1094         }
1095
1096       /* The I-beam
1097        */
1098       if (pw->i_beam == 0)
1099         {
1100           /* Make the I-beam disappear */
1101           XCopyArea(si->dpy, pw->user_entry_pixmap, si->passwd_dialog, gc2,
1102                     0, 0, rects[0].width, rects[0].height,
1103                     rects[0].x, rects[0].y);
1104         }
1105       else if (pw->i_beam == 1)
1106         {
1107           /* Make the I-beam appear */
1108           x = (rects[0].x + pw->shadow_width +
1109                string_width (pw->passwd_font, pw->passwd_string));
1110           y = rects[0].y + pw->shadow_width;
1111
1112           if (x > rects[0].x + rects[0].width - 1)
1113             x = rects[0].x + rects[0].width - 1;
1114           XDrawLine (si->dpy, si->passwd_dialog, gc1, 
1115                      x, y,
1116                      x, y + pw->passwd_font->ascent + pw->passwd_font->descent-1);
1117         }
1118
1119       pw->i_beam = (pw->i_beam + 1) % 4;
1120
1121     }
1122
1123   /* the thermometer
1124    */
1125   y = (pw->thermo_field_height - 2) * (1.0 - pw->ratio);
1126   if (y > 0)
1127     {
1128       XFillRectangle (si->dpy, si->passwd_dialog, gc2,
1129                       pw->thermo_field_x + 1,
1130                       pw->thermo_field_y + 1,
1131                       pw->thermo_width-2,
1132                       y);
1133       XSetForeground (si->dpy, gc1, pw->thermo_foreground);
1134       XFillRectangle (si->dpy, si->passwd_dialog, gc1,
1135                       pw->thermo_field_x + 1,
1136                       pw->thermo_field_y + 1 + y,
1137                       pw->thermo_width-2,
1138                       MAX (0, pw->thermo_field_height - y - 2));
1139     }
1140
1141   if (pw->button_state_changed_p)
1142     {
1143       pw->button_state_changed_p = False;
1144
1145       /* The "Unlock" button
1146        */
1147       draw_button(si->dpy, si->passwd_dialog, pw->button_font,
1148                   pw->button_foreground, pw->button_background,
1149                   pw->unlock_label,
1150                   pw->unlock_button_x, pw->unlock_button_y,
1151                   pw->unlock_button_width, pw->unlock_button_height,
1152                   pw->shadow_width,
1153                   (pw->unlock_button_down_p ? pw->shadow_bottom : pw->shadow_top),
1154                   (pw->unlock_button_down_p ? pw->shadow_top : pw->shadow_bottom),
1155                   pw->unlock_button_down_p);
1156
1157       /* The "New Login" button
1158        */
1159       if (pw->login_button_p)
1160         {
1161           draw_button(si->dpy, si->passwd_dialog, pw->button_font,
1162                       (pw->login_button_enabled_p
1163                        ? pw->passwd_foreground
1164                        : pw->shadow_bottom),
1165                       pw->button_background,
1166                       pw->login_label,
1167                       pw->login_button_x, pw->login_button_y,
1168                       pw->login_button_width, pw->login_button_height,
1169                       pw->shadow_width,
1170                       (pw->login_button_down_p
1171                        ? pw->shadow_bottom
1172                        : pw->shadow_top),
1173                       (pw->login_button_down_p
1174                        ? pw->shadow_top
1175                        : pw->shadow_bottom),
1176                       pw->login_button_down_p);
1177         }
1178     }
1179
1180   XFreeGC (si->dpy, gc1);
1181   XFreeGC (si->dpy, gc2);
1182   XSync (si->dpy, False);
1183 }
1184
1185
1186 void
1187 restore_background (saver_info *si)
1188 {
1189   passwd_dialog_data *pw = si->pw_data;
1190   saver_screen_info *ssi = pw->prompt_screen;
1191   XGCValues gcv;
1192   GC gc;
1193
1194   gcv.function = GXcopy;
1195
1196   gc = XCreateGC (si->dpy, ssi->screensaver_window, GCFunction, &gcv);
1197
1198   XCopyArea (si->dpy, pw->save_under,
1199              ssi->screensaver_window, gc,
1200              0, 0,
1201              ssi->width, ssi->height,
1202              0, 0);
1203
1204   XFreeGC (si->dpy, gc);
1205 }
1206
1207
1208 /* Frees anything created by make_passwd_window */
1209 static void
1210 cleanup_passwd_window (saver_info *si)
1211 {
1212   passwd_dialog_data *pw;
1213
1214   if (!(pw = si->pw_data))
1215     return;
1216
1217   if (pw->info_label)
1218     {
1219       mlstring_free(pw->info_label);
1220       pw->info_label = 0;
1221     }
1222
1223   if (pw->prompt_label)
1224     {
1225       mlstring_free(pw->prompt_label);
1226       pw->prompt_label = 0;
1227     }
1228
1229   memset (pw->typed_passwd, 0, sizeof(pw->typed_passwd));
1230   memset (pw->passwd_string, 0, strlen(pw->passwd_string));
1231
1232   if (pw->timer)
1233     {
1234       XtRemoveTimeOut (pw->timer);
1235       pw->timer = 0;
1236     }
1237
1238   if (pw->user_entry_pixmap)
1239     {
1240       XFreePixmap(si->dpy, pw->user_entry_pixmap);
1241       pw->user_entry_pixmap = 0;
1242     }
1243 }
1244
1245
1246 static void
1247 destroy_passwd_window (saver_info *si)
1248 {
1249   saver_preferences *p = &si->prefs;
1250   passwd_dialog_data *pw = si->pw_data;
1251   saver_screen_info *ssi = pw->prompt_screen;
1252   Colormap cmap = DefaultColormapOfScreen (ssi->screen);
1253   Pixel black = BlackPixelOfScreen (ssi->screen);
1254   Pixel white = WhitePixelOfScreen (ssi->screen);
1255   XEvent event;
1256
1257   cleanup_passwd_window (si);
1258
1259   if (si->cached_passwd)
1260     {
1261       char *wipe = si->cached_passwd;
1262
1263       while (*wipe)
1264         *wipe++ = '\0';
1265
1266       free(si->cached_passwd);
1267       si->cached_passwd = NULL;
1268     }
1269
1270   move_mouse_grab (si, RootWindowOfScreen (ssi->screen),
1271                    ssi->cursor, ssi->number);
1272
1273   if (pw->passwd_cursor)
1274     XFreeCursor (si->dpy, pw->passwd_cursor);
1275
1276   if (p->verbose_p)
1277     fprintf (stderr, "%s: %d: moving mouse back to %d,%d.\n",
1278              blurb(), ssi->number,
1279              pw->previous_mouse_x, pw->previous_mouse_y);
1280
1281   XWarpPointer (si->dpy, None, RootWindowOfScreen (ssi->screen),
1282                 0, 0, 0, 0,
1283                 pw->previous_mouse_x, pw->previous_mouse_y);
1284
1285   while (XCheckMaskEvent (si->dpy, PointerMotionMask, &event))
1286     if (p->verbose_p)
1287       fprintf (stderr, "%s: discarding MotionNotify event.\n", blurb());
1288
1289   if (si->passwd_dialog)
1290     {
1291       XDestroyWindow (si->dpy, si->passwd_dialog);
1292       si->passwd_dialog = 0;
1293     }
1294   
1295   if (pw->save_under)
1296     {
1297       restore_background(si);
1298       XFreePixmap (si->dpy, pw->save_under);
1299       pw->save_under = 0;
1300     }
1301
1302   if (pw->heading_label) free (pw->heading_label);
1303   if (pw->body_label)    free (pw->body_label);
1304   if (pw->user_label)    free (pw->user_label);
1305   if (pw->date_label)    free (pw->date_label);
1306   if (pw->login_label)   free (pw->login_label);
1307   if (pw->unlock_label)  free (pw->unlock_label);
1308   if (pw->passwd_string) free (pw->passwd_string);
1309   if (pw->uname_label)   free (pw->uname_label);
1310
1311   if (pw->heading_font) XFreeFont (si->dpy, pw->heading_font);
1312   if (pw->body_font)    XFreeFont (si->dpy, pw->body_font);
1313   if (pw->label_font)   XFreeFont (si->dpy, pw->label_font);
1314   if (pw->passwd_font)  XFreeFont (si->dpy, pw->passwd_font);
1315   if (pw->date_font)    XFreeFont (si->dpy, pw->date_font);
1316   if (pw->button_font)  XFreeFont (si->dpy, pw->button_font);
1317   if (pw->uname_font)   XFreeFont (si->dpy, pw->uname_font);
1318
1319   if (pw->foreground != black && pw->foreground != white)
1320     XFreeColors (si->dpy, cmap, &pw->foreground, 1, 0L);
1321   if (pw->background != black && pw->background != white)
1322     XFreeColors (si->dpy, cmap, &pw->background, 1, 0L);
1323   if (!(pw->button_foreground == black || pw->button_foreground == white))
1324     XFreeColors (si->dpy, cmap, &pw->button_foreground, 1, 0L);
1325   if (!(pw->button_background == black || pw->button_background == white))
1326     XFreeColors (si->dpy, cmap, &pw->button_background, 1, 0L);
1327   if (pw->passwd_foreground != black && pw->passwd_foreground != white)
1328     XFreeColors (si->dpy, cmap, &pw->passwd_foreground, 1, 0L);
1329   if (pw->passwd_background != black && pw->passwd_background != white)
1330     XFreeColors (si->dpy, cmap, &pw->passwd_background, 1, 0L);
1331   if (pw->thermo_foreground != black && pw->thermo_foreground != white)
1332     XFreeColors (si->dpy, cmap, &pw->thermo_foreground, 1, 0L);
1333   if (pw->thermo_background != black && pw->thermo_background != white)
1334     XFreeColors (si->dpy, cmap, &pw->thermo_background, 1, 0L);
1335   if (pw->shadow_top != black && pw->shadow_top != white)
1336     XFreeColors (si->dpy, cmap, &pw->shadow_top, 1, 0L);
1337   if (pw->shadow_bottom != black && pw->shadow_bottom != white)
1338     XFreeColors (si->dpy, cmap, &pw->shadow_bottom, 1, 0L);
1339
1340   if (pw->logo_pixmap)
1341     XFreePixmap (si->dpy, pw->logo_pixmap);
1342   if (pw-> logo_clipmask)
1343     XFreePixmap (si->dpy, pw->logo_clipmask);
1344   if (pw->logo_pixels)
1345     {
1346       if (pw->logo_npixels)
1347         XFreeColors (si->dpy, cmap, pw->logo_pixels, pw->logo_npixels, 0L);
1348       free (pw->logo_pixels);
1349       pw->logo_pixels = 0;
1350       pw->logo_npixels = 0;
1351     }
1352
1353   if (pw->save_under)
1354     XFreePixmap (si->dpy, pw->save_under);
1355
1356   if (cmap)
1357     XInstallColormap (si->dpy, cmap);
1358
1359   memset (pw, 0, sizeof(*pw));
1360   free (pw);
1361   si->pw_data = 0;
1362 }
1363
1364
1365 static Bool error_handler_hit_p = False;
1366
1367 static int
1368 ignore_all_errors_ehandler (Display *dpy, XErrorEvent *error)
1369 {
1370   error_handler_hit_p = True;
1371   return 0;
1372 }
1373
1374
1375 #ifdef HAVE_XHPDISABLERESET
1376 /* This function enables and disables the C-Sh-Reset hot-key, which
1377    normally resets the X server (logging out the logged-in user.)
1378    We don't want random people to be able to do that while the
1379    screen is locked.
1380  */
1381 static void
1382 hp_lock_reset (saver_info *si, Bool lock_p)
1383 {
1384   static Bool hp_locked_p = False;
1385
1386   /* Calls to XHPDisableReset and XHPEnableReset must be balanced,
1387      or BadAccess errors occur.  (It's ok for this to be global,
1388      since it affects the whole machine, not just the current screen.)
1389   */
1390   if (hp_locked_p == lock_p)
1391     return;
1392
1393   if (lock_p)
1394     XHPDisableReset (si->dpy);
1395   else
1396     XHPEnableReset (si->dpy);
1397   hp_locked_p = lock_p;
1398 }
1399 #endif /* HAVE_XHPDISABLERESET */
1400
1401 \f
1402 #ifdef HAVE_XF86MISCSETGRABKEYSSTATE
1403
1404 /* This function enables and disables the Ctrl-Alt-KP_star and 
1405    Ctrl-Alt-KP_slash hot-keys, which (in XFree86 4.2) break any
1406    grabs and/or kill the grabbing client.  That would effectively
1407    unlock the screen, so we don't like that.
1408
1409    The Ctrl-Alt-KP_star and Ctrl-Alt-KP_slash hot-keys only exist
1410    if AllowDeactivateGrabs and/or AllowClosedownGrabs are turned on
1411    in XF86Config.  I believe they are disabled by default.
1412
1413    This does not affect any other keys (specifically Ctrl-Alt-BS or
1414    Ctrl-Alt-F1) but I wish it did.  Maybe it will someday.
1415  */
1416 static void
1417 xfree_lock_grab_smasher (saver_info *si, Bool lock_p)
1418 {
1419   saver_preferences *p = &si->prefs;
1420   int status;
1421
1422   XErrorHandler old_handler;
1423   XSync (si->dpy, False);
1424   error_handler_hit_p = False;
1425   old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1426   XSync (si->dpy, False);
1427   status = XF86MiscSetGrabKeysState (si->dpy, !lock_p);
1428   XSync (si->dpy, False);
1429   if (error_handler_hit_p) status = 666;
1430
1431   if (!lock_p && status == MiscExtGrabStateAlready)
1432     status = MiscExtGrabStateSuccess;  /* shut up, consider this success */
1433
1434   if (p->verbose_p && status != MiscExtGrabStateSuccess)
1435     fprintf (stderr, "%s: error: XF86MiscSetGrabKeysState(%d) returned %s\n",
1436              blurb(), !lock_p,
1437              (status == MiscExtGrabStateSuccess ? "MiscExtGrabStateSuccess" :
1438               status == MiscExtGrabStateLocked  ? "MiscExtGrabStateLocked"  :
1439               status == MiscExtGrabStateAlready ? "MiscExtGrabStateAlready" :
1440               status == 666 ? "an X error" :
1441               "unknown value"));
1442
1443   XSync (si->dpy, False);
1444   XSetErrorHandler (old_handler);
1445   XSync (si->dpy, False);
1446 }
1447 #endif /* HAVE_XF86MISCSETGRABKEYSSTATE */
1448
1449
1450 \f
1451 /* This function enables and disables the C-Alt-Plus and C-Alt-Minus
1452    hot-keys, which normally change the resolution of the X server.
1453    We don't want people to be able to switch the server resolution
1454    while the screen is locked, because if they switch to a higher
1455    resolution, it could cause part of the underlying desktop to become
1456    exposed.
1457  */
1458 #ifdef HAVE_XF86VMODE
1459
1460 static void
1461 xfree_lock_mode_switch (saver_info *si, Bool lock_p)
1462 {
1463   static Bool any_mode_locked_p = False;
1464   saver_preferences *p = &si->prefs;
1465   int screen;
1466   int event, error;
1467   Bool status;
1468   XErrorHandler old_handler;
1469
1470   if (any_mode_locked_p == lock_p)
1471     return;
1472   if (!XF86VidModeQueryExtension (si->dpy, &event, &error))
1473     return;
1474
1475   for (screen = 0; screen < (si->xinerama_p ? 1 : si->nscreens); screen++)
1476     {
1477       XSync (si->dpy, False);
1478       old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1479       error_handler_hit_p = False;
1480       status = XF86VidModeLockModeSwitch (si->dpy, screen, lock_p);
1481       XSync (si->dpy, False);
1482       XSetErrorHandler (old_handler);
1483       if (error_handler_hit_p) status = False;
1484
1485       if (status)
1486         any_mode_locked_p = lock_p;
1487
1488       if (!status && (p->verbose_p || !lock_p))
1489         /* Only print this when verbose, or when we locked but can't unlock.
1490            I tried printing this message whenever it comes up, but
1491            mode-locking always fails if DontZoom is set in XF86Config. */
1492         fprintf (stderr, "%s: %d: unable to %s mode switching!\n",
1493                  blurb(), screen, (lock_p ? "lock" : "unlock"));
1494       else if (p->verbose_p)
1495         fprintf (stderr, "%s: %d: %s mode switching.\n",
1496                  blurb(), screen, (lock_p ? "locked" : "unlocked"));
1497     }
1498 }
1499 #endif /* HAVE_XF86VMODE */
1500
1501 \f
1502 /* If the viewport has been scrolled since the screen was blanked,
1503    then scroll it back to where it belongs.  This function only exists
1504    to patch over a very brief race condition.
1505  */
1506 static void
1507 undo_vp_motion (saver_info *si)
1508 {
1509 #ifdef HAVE_XF86VMODE
1510   saver_preferences *p = &si->prefs;
1511   int screen;
1512   int event, error;
1513
1514   if (!XF86VidModeQueryExtension (si->dpy, &event, &error))
1515     return;
1516
1517   for (screen = 0; screen < si->nscreens; screen++)
1518     {
1519       saver_screen_info *ssi = &si->screens[screen];
1520       int x, y;
1521       Bool status;
1522
1523       if (ssi->blank_vp_x == -1 && ssi->blank_vp_y == -1)
1524         break;
1525       if (!XF86VidModeGetViewPort (si->dpy, screen, &x, &y))
1526         return;
1527       if (ssi->blank_vp_x == x && ssi->blank_vp_y == y)
1528         return;
1529     
1530       /* We're going to move the viewport.  The mouse has just been grabbed on
1531          (and constrained to, thus warped to) the password window, so it is no
1532          longer near the edge of the screen.  However, wait a bit anyway, just
1533          to make sure the server drains its last motion event, so that the
1534          screen doesn't continue to scroll after we've reset the viewport.
1535        */
1536       XSync (si->dpy, False);
1537       usleep (250000);  /* 1/4 second */
1538       XSync (si->dpy, False);
1539
1540       status = XF86VidModeSetViewPort (si->dpy, screen,
1541                                        ssi->blank_vp_x, ssi->blank_vp_y);
1542
1543       if (!status)
1544         fprintf (stderr,
1545                  "%s: %d: unable to move vp from (%d,%d) back to (%d,%d)!\n",
1546                  blurb(), screen, x, y, ssi->blank_vp_x, ssi->blank_vp_y);
1547       else if (p->verbose_p)
1548         fprintf (stderr,
1549                  "%s: %d: vp moved to (%d,%d); moved it back to (%d,%d).\n",
1550                  blurb(), screen, x, y, ssi->blank_vp_x, ssi->blank_vp_y);
1551     }
1552 #endif /* HAVE_XF86VMODE */
1553 }
1554
1555
1556 \f
1557 /* Interactions
1558  */
1559
1560 static void
1561 passwd_animate_timer (XtPointer closure, XtIntervalId *id)
1562 {
1563   saver_info *si = (saver_info *) closure;
1564   int tick = 166;
1565   passwd_dialog_data *pw = si->pw_data;
1566
1567   if (!pw) return;
1568
1569   pw->ratio -= (1.0 / ((double) si->prefs.passwd_timeout / (double) tick));
1570   if (pw->ratio < 0)
1571     {
1572       pw->ratio = 0;
1573       if (si->unlock_state == ul_read)
1574         si->unlock_state = ul_time;
1575     }
1576
1577   update_passwd_window (si, 0, pw->ratio);
1578
1579   if (si->unlock_state == ul_read)
1580     pw->timer = XtAppAddTimeOut (si->app, tick, passwd_animate_timer,
1581                                  (XtPointer) si);
1582   else
1583     pw->timer = 0;
1584
1585   idle_timer ((XtPointer) si, 0);
1586 }
1587
1588
1589 static XComposeStatus *compose_status;
1590
1591 static void
1592 handle_login_button (saver_info *si, XEvent *event)
1593 {
1594   saver_preferences *p = &si->prefs;
1595   Bool mouse_in_box = False;
1596   Bool hit_p = False;
1597   passwd_dialog_data *pw = si->pw_data;
1598   saver_screen_info *ssi = pw->prompt_screen;
1599
1600   if (! pw->login_button_enabled_p)
1601     return;
1602
1603   mouse_in_box = 
1604     (event->xbutton.x >= pw->login_button_x &&
1605      event->xbutton.x <= pw->login_button_x + pw->login_button_width &&
1606      event->xbutton.y >= pw->login_button_y &&
1607      event->xbutton.y <= pw->login_button_y + pw->login_button_height);
1608
1609   if (ButtonRelease == event->xany.type &&
1610       pw->login_button_down_p &&
1611       mouse_in_box)
1612     {
1613       /* Only allow them to press the button once: don't want to
1614          accidentally launch a dozen gdm choosers if the machine
1615          is being slow.
1616        */
1617       hit_p = True;
1618       pw->login_button_enabled_p = False;
1619     }
1620
1621   pw->login_button_down_p = (mouse_in_box &&
1622                              ButtonRelease != event->xany.type);
1623
1624   update_passwd_window (si, 0, pw->ratio);
1625
1626   if (hit_p)
1627     fork_and_exec (ssi, p->new_login_command);
1628 }
1629
1630
1631 static void
1632 handle_unlock_button (saver_info *si, XEvent *event)
1633 {
1634   Bool mouse_in_box = False;
1635   passwd_dialog_data *pw = si->pw_data;
1636
1637   mouse_in_box =
1638     (event->xbutton.x >= pw->unlock_button_x &&
1639      event->xbutton.x <= pw->unlock_button_x + pw->unlock_button_width &&
1640      event->xbutton.y >= pw->unlock_button_y &&
1641      event->xbutton.y <= pw->unlock_button_y + pw->unlock_button_height);
1642
1643   if (ButtonRelease == event->xany.type &&
1644       pw->unlock_button_down_p &&
1645       mouse_in_box)
1646     finished_typing_passwd (si, pw);
1647
1648   pw->unlock_button_down_p = (mouse_in_box &&
1649                                 ButtonRelease != event->xany.type);
1650 }
1651
1652
1653 static void
1654 finished_typing_passwd (saver_info *si, passwd_dialog_data *pw)
1655 {
1656   if (si->unlock_state == ul_read)
1657     {
1658       update_passwd_window (si, "Checking...", pw->ratio);
1659       XSync (si->dpy, False);
1660
1661       si->unlock_state = ul_finished;
1662       update_passwd_window (si, "", pw->ratio);
1663     }
1664 }
1665
1666 static void
1667 handle_passwd_key (saver_info *si, XKeyEvent *event)
1668 {
1669   passwd_dialog_data *pw = si->pw_data;
1670   int pw_size = sizeof (pw->typed_passwd) - 1;
1671   char *typed_passwd = pw->typed_passwd;
1672   char s[2];
1673   char *stars = 0;
1674   int i;
1675   int size = XLookupString (event, s, 1, 0, compose_status);
1676
1677   if (size != 1) return;
1678
1679   s[1] = 0;
1680
1681   pw->passwd_changed_p = True;
1682
1683   /* Add 10% to the time remaining every time a key is pressed. */
1684   pw->ratio += 0.1;
1685   if (pw->ratio > 1) pw->ratio = 1;
1686
1687   switch (*s)
1688     {
1689     case '\010': case '\177':                           /* Backspace */
1690       if (!*typed_passwd)
1691         XBell (si->dpy, 0);
1692       else
1693         typed_passwd [strlen(typed_passwd)-1] = 0;
1694       break;
1695
1696     case '\025': case '\030':                           /* Erase line */
1697       memset (typed_passwd, 0, pw_size);
1698       break;
1699
1700     case '\012': case '\015':                           /* Enter */
1701       finished_typing_passwd(si, pw);
1702       break;
1703
1704     case '\033':                                        /* Escape */
1705       si->unlock_state = ul_cancel;
1706       break;
1707
1708     default:
1709       /* Though technically the only illegal characters in Unix passwords
1710          are LF and NUL, most GUI programs (e.g., GDM) use regular text-entry
1711          fields that only let you type printable characters.  So, people
1712          who use funky characters in their passwords are already broken.
1713          We follow that precedent.
1714        */
1715       if (isprint ((unsigned char) *s))
1716         {
1717           i = strlen (typed_passwd);
1718           if (i >= pw_size-1)
1719             XBell (si->dpy, 0);
1720           else
1721             {
1722               typed_passwd [i] = *s;
1723               typed_passwd [i+1] = 0;
1724             }
1725         }
1726       else
1727         XBell (si->dpy, 0);
1728       break;
1729     }
1730
1731   if (pw->echo_input)
1732     {
1733       /* If the input is wider than the text box, only show the last portion.
1734        * This simulates a horizontally scrolling text field. */
1735       int chars_in_pwfield = (pw->passwd_field_width /
1736                               pw->passwd_font->max_bounds.width);
1737
1738       if (strlen(typed_passwd) > chars_in_pwfield)
1739         typed_passwd += (strlen(typed_passwd) - chars_in_pwfield);
1740
1741       update_passwd_window(si, typed_passwd, pw->ratio);
1742     }
1743   else if (pw->show_stars_p)
1744     {
1745       i = strlen(typed_passwd);
1746       stars = (char *) malloc(i+1);
1747       memset (stars, '*', i);
1748       stars[i] = 0;
1749       update_passwd_window (si, stars, pw->ratio);
1750       free (stars);
1751     }
1752   else
1753     {
1754       update_passwd_window (si, "", pw->ratio);
1755     }
1756 }
1757
1758
1759 static void
1760 passwd_event_loop (saver_info *si)
1761 {
1762   saver_preferences *p = &si->prefs;
1763   char *msg = 0;
1764   XEvent event;
1765   unsigned int caps_p = 0;
1766
1767   passwd_animate_timer ((XtPointer) si, 0);
1768
1769   while (si->unlock_state == ul_read)
1770     {
1771       XtAppNextEvent (si->app, &event);
1772       if (event.xany.window == si->passwd_dialog && event.xany.type == Expose)
1773         draw_passwd_window (si);
1774       else if (event.xany.type == KeyPress)
1775         {
1776           handle_passwd_key (si, &event.xkey);
1777           caps_p = (event.xkey.state & LockMask);
1778         }
1779       else if (event.xany.type == ButtonPress || 
1780                event.xany.type == ButtonRelease)
1781         {
1782           si->pw_data->button_state_changed_p = True;
1783           handle_unlock_button (si, &event);
1784           if (si->pw_data->login_button_p)
1785             handle_login_button (si, &event);
1786         }
1787       else
1788         XtDispatchEvent (&event);
1789     }
1790
1791   switch (si->unlock_state)
1792     {
1793     case ul_cancel: msg = ""; break;
1794     case ul_time: msg = "Timed out!"; break;
1795     case ul_finished: msg = "Checking..."; break;
1796     default: msg = 0; break;
1797     }
1798
1799   if (si->unlock_state == ul_fail)
1800     si->unlock_failures++;
1801
1802   if (p->verbose_p)
1803     switch (si->unlock_state)
1804       {
1805       case ul_fail:
1806         fprintf (stderr, "%s: auth/input incorrect!%s\n", blurb(),
1807                  (caps_p ? "  (CapsLock)" : ""));
1808         break;
1809       case ul_cancel:
1810         fprintf (stderr, "%s: input cancelled.\n", blurb()); break;
1811       case ul_time:
1812         fprintf (stderr, "%s: input timed out.\n", blurb()); break;
1813       case ul_finished:
1814         fprintf (stderr, "%s: input finished.\n", blurb()); break;
1815       default: break;
1816       }
1817
1818 #ifdef HAVE_SYSLOG
1819   if (si->unlock_state == ul_fail)
1820     {
1821       /* If they typed a password (as opposed to just hitting return) and
1822          the password was invalid, log it.
1823       */
1824       struct passwd *pw = getpwuid (getuid ());
1825       char *d = DisplayString (si->dpy);
1826       char *u = (pw && pw->pw_name ? pw->pw_name : "???");
1827       int opt = 0;
1828       int fac = 0;
1829
1830 # ifdef LOG_PID
1831       opt = LOG_PID;
1832 # endif
1833
1834 # if defined(LOG_AUTHPRIV)
1835       fac = LOG_AUTHPRIV;
1836 # elif defined(LOG_AUTH)
1837       fac = LOG_AUTH;
1838 # else
1839       fac = LOG_DAEMON;
1840 # endif
1841
1842       if (!d) d = "";
1843       openlog (progname, opt, fac);
1844       syslog (LOG_NOTICE, "FAILED LOGIN %d ON DISPLAY \"%s\", FOR \"%s\"",
1845               si->unlock_failures, d, u);
1846       closelog ();
1847     }
1848 #endif /* HAVE_SYSLOG */
1849
1850   if (si->unlock_state == ul_fail)
1851     XBell (si->dpy, False);
1852
1853   if (si->unlock_state == ul_success && si->unlock_failures != 0)
1854     {
1855       if (si->unlock_failures == 1)
1856         fprintf (real_stderr,
1857                  "%s: WARNING: 1 failed attempt to unlock the screen.\n",
1858                  blurb());
1859       else
1860         fprintf (real_stderr,
1861                  "%s: WARNING: %d failed attempts to unlock the screen.\n",
1862                  blurb(), si->unlock_failures);
1863       fflush (real_stderr);
1864
1865       si->unlock_failures = 0;
1866     }
1867
1868   if (msg)
1869     {
1870       si->pw_data->i_beam = 0;
1871       update_passwd_window (si, msg, 0.0);
1872       XSync (si->dpy, False);
1873       sleep (1);
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   destroy_passwd_window (si);
2070 }
2071
2072
2073 Bool
2074 unlock_p (saver_info *si)
2075 {
2076   saver_preferences *p = &si->prefs;
2077
2078   if (!si->unlock_cb)
2079     {
2080       fprintf(stderr, "%s: Error: no unlock function specified!\n", blurb());
2081       return False;
2082     }
2083
2084   raise_window (si, True, True, True);
2085
2086   if (p->verbose_p)
2087     fprintf (stderr, "%s: prompting for password.\n", blurb());
2088
2089   xss_authenticate(si, p->verbose_p);
2090
2091   return (si->unlock_state == ul_success);
2092 }
2093
2094
2095 void
2096 set_locked_p (saver_info *si, Bool locked_p)
2097 {
2098   si->locked_p = locked_p;
2099
2100 #ifdef HAVE_XHPDISABLERESET
2101   hp_lock_reset (si, locked_p);                 /* turn off/on C-Sh-Reset */
2102 #endif
2103 #ifdef HAVE_XF86VMODE
2104   xfree_lock_mode_switch (si, locked_p);        /* turn off/on C-Alt-Plus */
2105 #endif
2106 #ifdef HAVE_XF86MISCSETGRABKEYSSTATE
2107   xfree_lock_grab_smasher (si, locked_p);       /* turn off/on C-Alt-KP-*,/ */
2108 #endif
2109
2110   store_saver_status (si);                      /* store locked-p */
2111 }
2112
2113
2114 #else  /*  NO_LOCKING -- whole file */
2115
2116 void
2117 set_locked_p (saver_info *si, Bool locked_p)
2118 {
2119   if (locked_p) abort();
2120 }
2121
2122 #endif /* !NO_LOCKING */