03890a909c3e58dbe4a059b36c8c734864a03162
[xscreensaver] / driver / lock.c
1 /* lock.c --- handling the password dialog for locking-mode.
2  * xscreensaver, Copyright (c) 1993-2002 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 <X11/Intrinsic.h>
21 #include <X11/Xos.h>            /* for time() */
22 #include <time.h>
23 #include <sys/time.h>
24 #include "xscreensaver.h"
25 #include "resources.h"
26
27 #ifndef NO_LOCKING              /* (mostly) whole file */
28
29 #ifdef HAVE_SYSLOG
30 # include <syslog.h>
31 #endif /* HAVE_SYSLOG */
32
33 #ifdef HAVE_XHPDISABLERESET
34 # include <X11/XHPlib.h>
35   static void hp_lock_reset (saver_info *si, Bool lock_p);
36 #endif /* HAVE_XHPDISABLERESET */
37
38 #ifdef HAVE_VT_LOCKSWITCH
39 # include <fcntl.h>
40 # include <sys/ioctl.h>
41 # include <sys/vt.h>
42   static void linux_lock_vt_switch (saver_info *si, Bool lock_p);
43 #endif /* HAVE_VT_LOCKSWITCH */
44
45 #ifdef HAVE_XF86VMODE
46 # include <X11/extensions/xf86vmode.h>
47   static void xfree_lock_mode_switch (saver_info *si, Bool lock_p);
48 #endif /* HAVE_XF86VMODE */
49
50
51 #ifdef _VROOT_H_
52 ERROR!  You must not include vroot.h in this file.
53 #endif
54
55 #ifndef VMS
56 # include <pwd.h>
57 #else /* VMS */
58
59 extern char *getenv(const char *name);
60 extern int validate_user(char *name, char *password);
61
62 static Bool
63 vms_passwd_valid_p(char *pw, Bool verbose_p)
64 {
65   return (validate_user (getenv("USER"), typed_passwd) == 1);
66 }
67 # undef passwd_valid_p
68 # define passwd_valid_p vms_passwd_valid_p
69
70 #endif /* VMS */
71
72
73 #undef MAX
74 #define MAX(a,b) ((a)>(b)?(a):(b))
75
76 enum passwd_state { pw_read, pw_ok, pw_null, pw_fail, pw_cancel, pw_time };
77
78 struct passwd_dialog_data {
79
80   saver_screen_info *prompt_screen;
81   int previous_mouse_x, previous_mouse_y;
82
83   enum passwd_state state;
84   char typed_passwd [80];
85   XtIntervalId timer;
86   int i_beam;
87
88   float ratio;
89   Position x, y;
90   Dimension width;
91   Dimension height;
92   Dimension border_width;
93
94   char *heading_label;
95   char *body_label;
96   char *user_label;
97   char *passwd_label;
98   char *date_label;
99   char *user_string;
100   char *passwd_string;
101
102   XFontStruct *heading_font;
103   XFontStruct *body_font;
104   XFontStruct *label_font;
105   XFontStruct *passwd_font;
106   XFontStruct *date_font;
107
108   Pixel foreground;
109   Pixel background;
110   Pixel passwd_foreground;
111   Pixel passwd_background;
112   Pixel thermo_foreground;
113   Pixel thermo_background;
114   Pixel shadow_top;
115   Pixel shadow_bottom;
116
117   Dimension logo_width;
118   Dimension logo_height;
119   Dimension thermo_width;
120   Dimension internal_border;
121   Dimension shadow_width;
122
123   Dimension passwd_field_x, passwd_field_y;
124   Dimension passwd_field_width, passwd_field_height;
125
126   Dimension thermo_field_x, thermo_field_y;
127   Dimension thermo_field_height;
128
129   Pixmap logo_pixmap;
130   int logo_npixels;
131   unsigned long *logo_pixels;
132
133   Pixmap save_under;
134 };
135
136 static void draw_passwd_window (saver_info *si);
137 static void update_passwd_window (saver_info *si, const char *printed_passwd,
138                                   float ratio);
139 static void destroy_passwd_window (saver_info *si);
140 static void undo_vp_motion (saver_info *si);
141
142
143 static void
144 make_passwd_window (saver_info *si)
145 {
146   struct passwd *p = getpwuid (getuid ());
147   XSetWindowAttributes attrs;
148   unsigned long attrmask = 0;
149   passwd_dialog_data *pw = (passwd_dialog_data *) calloc (1, sizeof(*pw));
150   Screen *screen;
151   Colormap cmap;
152   char *f;
153
154   pw->prompt_screen = &si->screens [mouse_screen (si)];
155   if (si->prefs.verbose_p)
156     fprintf (stderr, "%s: %d: creating password dialog.\n",
157              blurb(), pw->prompt_screen->number);
158
159   screen = pw->prompt_screen->screen;
160   cmap = DefaultColormapOfScreen (screen);
161
162   pw->ratio = 1.0;
163
164   pw->heading_label = get_string_resource ("passwd.heading.label",
165                                            "Dialog.Label.Label");
166   pw->body_label = get_string_resource ("passwd.body.label",
167                                         "Dialog.Label.Label");
168   pw->user_label = get_string_resource ("passwd.user.label",
169                                         "Dialog.Label.Label");
170   pw->passwd_label = get_string_resource ("passwd.passwd.label",
171                                           "Dialog.Label.Label");
172   pw->date_label = get_string_resource ("dateFormat", "DateFormat");
173
174   if (!pw->heading_label)
175     pw->heading_label = strdup("ERROR: REESOURCES NOT INSTALLED CORRECTLY");
176   if (!pw->body_label)
177     pw->body_label = strdup("ERROR: REESOURCES NOT INSTALLED CORRECTLY");
178   if (!pw->user_label) pw->user_label = strdup("ERROR");
179   if (!pw->passwd_label) pw->passwd_label = strdup("ERROR");
180   if (!pw->date_label) pw->date_label = strdup("ERROR");
181
182   /* Put the version number in the label. */
183   {
184     char *s = (char *) malloc (strlen(pw->heading_label) + 20);
185     sprintf(s, pw->heading_label, si->version);
186     free (pw->heading_label);
187     pw->heading_label = s;
188   }
189
190   pw->user_string = strdup (p && p->pw_name ? p->pw_name : "???");
191   pw->passwd_string = strdup("");
192
193   f = get_string_resource ("passwd.headingFont", "Dialog.Font");
194   pw->heading_font = XLoadQueryFont (si->dpy, (f ? f : "fixed"));
195   if (!pw->heading_font) pw->heading_font = XLoadQueryFont (si->dpy, "fixed");
196   if (f) free (f);
197
198   f = get_string_resource("passwd.bodyFont", "Dialog.Font");
199   pw->body_font = XLoadQueryFont (si->dpy, (f ? f : "fixed"));
200   if (!pw->body_font) pw->body_font = XLoadQueryFont (si->dpy, "fixed");
201   if (f) free (f);
202
203   f = get_string_resource("passwd.labelFont", "Dialog.Font");
204   pw->label_font = XLoadQueryFont (si->dpy, (f ? f : "fixed"));
205   if (!pw->label_font) pw->label_font = XLoadQueryFont (si->dpy, "fixed");
206   if (f) free (f);
207
208   f = get_string_resource("passwd.passwdFont", "Dialog.Font");
209   pw->passwd_font = XLoadQueryFont (si->dpy, (f ? f : "fixed"));
210   if (!pw->passwd_font) pw->passwd_font = XLoadQueryFont (si->dpy, "fixed");
211   if (f) free (f);
212
213   f = get_string_resource("passwd.dateFont", "Dialog.Font");
214   pw->date_font = XLoadQueryFont (si->dpy, (f ? f : "fixed"));
215   if (!pw->date_font) pw->date_font = XLoadQueryFont (si->dpy, "fixed");
216   if (f) free (f);
217
218   pw->foreground = get_pixel_resource ("passwd.foreground",
219                                        "Dialog.Foreground",
220                                        si->dpy, cmap);
221   pw->background = get_pixel_resource ("passwd.background",
222                                        "Dialog.Background",
223                                        si->dpy, cmap);
224
225   if (pw->foreground == pw->background)
226     {
227       /* Make sure the error messages show up. */
228       pw->foreground = BlackPixelOfScreen (screen);
229       pw->background = WhitePixelOfScreen (screen);
230     }
231
232   pw->passwd_foreground = get_pixel_resource ("passwd.text.foreground",
233                                               "Dialog.Text.Foreground",
234                                               si->dpy, cmap);
235   pw->passwd_background = get_pixel_resource ("passwd.text.background",
236                                               "Dialog.Text.Background",
237                                               si->dpy, cmap);
238   pw->thermo_foreground = get_pixel_resource ("passwd.thermometer.foreground",
239                                               "Dialog.Thermometer.Foreground",
240                                               si->dpy, cmap);
241   pw->thermo_background = get_pixel_resource ("passwd.thermometer.background",
242                                               "Dialog.Thermometer.Background",
243                                               si->dpy, cmap);
244   pw->shadow_top = get_pixel_resource ("passwd.topShadowColor",
245                                        "Dialog.Foreground",
246                                        si->dpy, cmap);
247   pw->shadow_bottom = get_pixel_resource ("passwd.bottomShadowColor",
248                                           "Dialog.Background",
249                                           si->dpy, cmap);
250
251   pw->logo_width = get_integer_resource ("passwd.logo.width",
252                                          "Dialog.Logo.Width");
253   pw->logo_height = get_integer_resource ("passwd.logo.height",
254                                           "Dialog.Logo.Height");
255   pw->thermo_width = get_integer_resource ("passwd.thermometer.width",
256                                            "Dialog.Thermometer.Width");
257   pw->internal_border = get_integer_resource ("passwd.internalBorderWidth",
258                                               "Dialog.InternalBorderWidth");
259   pw->shadow_width = get_integer_resource ("passwd.shadowThickness",
260                                            "Dialog.ShadowThickness");
261
262   if (pw->logo_width == 0)  pw->logo_width = 150;
263   if (pw->logo_height == 0) pw->logo_height = 150;
264   if (pw->internal_border == 0) pw->internal_border = 15;
265   if (pw->shadow_width == 0) pw->shadow_width = 4;
266   if (pw->thermo_width == 0) pw->thermo_width = pw->shadow_width;
267
268   {
269     int direction, ascent, descent;
270     XCharStruct overall;
271
272     pw->width = 0;
273     pw->height = 0;
274
275     /* Measure the heading_label. */
276     XTextExtents (pw->heading_font,
277                   pw->heading_label, strlen(pw->heading_label),
278                   &direction, &ascent, &descent, &overall);
279     if (overall.width > pw->width) pw->width = overall.width;
280     pw->height += ascent + descent;
281
282     /* Measure the body_label. */
283     XTextExtents (pw->body_font,
284                   pw->body_label, strlen(pw->body_label),
285                   &direction, &ascent, &descent, &overall);
286     if (overall.width > pw->width) pw->width = overall.width;
287     pw->height += ascent + descent;
288
289     {
290       Dimension w2 = 0, w3 = 0;
291       Dimension h2 = 0, h3 = 0;
292       const char *passwd_string = "MMMMMMMMMMMM";
293
294       /* Measure the user_label. */
295       XTextExtents (pw->label_font,
296                     pw->user_label, strlen(pw->user_label),
297                     &direction, &ascent, &descent, &overall);
298       if (overall.width > w2)  w2 = overall.width;
299       h2 += ascent + descent;
300
301       /* Measure the passwd_label. */
302       XTextExtents (pw->label_font,
303                     pw->passwd_label, strlen(pw->passwd_label),
304                     &direction, &ascent, &descent, &overall);
305       if (overall.width > w2)  w2 = overall.width;
306       h2 += ascent + descent;
307
308       /* Measure the user_string. */
309       XTextExtents (pw->passwd_font,
310                     pw->user_string, strlen(pw->user_string),
311                     &direction, &ascent, &descent, &overall);
312       overall.width += (pw->shadow_width * 4);
313       ascent += (pw->shadow_width * 4);
314       if (overall.width > w3)  w3 = overall.width;
315       h3 += ascent + descent;
316
317       /* Measure the (maximally-sized, dummy) passwd_string. */
318       XTextExtents (pw->passwd_font,
319                     passwd_string, strlen(passwd_string),
320                     &direction, &ascent, &descent, &overall);
321       overall.width += (pw->shadow_width * 4);
322       ascent += (pw->shadow_width * 4);
323       if (overall.width > w3)  w3 = overall.width;
324       h3 += ascent + descent;
325
326       w2 = w2 + w3 + (pw->shadow_width * 2);
327       h2 = MAX (h2, h3);
328
329       if (w2 > pw->width)  pw->width  = w2;
330       pw->height += h2;
331     }
332
333     pw->width  += (pw->internal_border * 2);
334     pw->height += (pw->internal_border * 4);
335
336     pw->width += pw->thermo_width + (pw->shadow_width * 3);
337
338     if (pw->logo_height > pw->height)
339       pw->height = pw->logo_height;
340     else if (pw->height > pw->logo_height)
341       pw->logo_height = pw->height;
342
343     pw->logo_width = pw->logo_height;
344
345     pw->width += pw->logo_width;
346   }
347
348   attrmask |= CWOverrideRedirect; attrs.override_redirect = True;
349   attrmask |= CWEventMask; attrs.event_mask = ExposureMask|KeyPressMask;
350
351   /* We need to remember the mouse position and restore it afterward, or
352      sometimes (perhaps only with Xinerama?) the mouse gets warped to
353      inside the bounds of the lock dialog window.
354    */
355   {
356     Window pointer_root, pointer_child;
357     int root_x, root_y, win_x, win_y;
358     unsigned int mask;
359     pw->previous_mouse_x = 0;
360     pw->previous_mouse_y = 0;
361     if (XQueryPointer (si->dpy, RootWindowOfScreen (pw->prompt_screen->screen),
362                        &pointer_root, &pointer_child,
363                        &root_x, &root_y, &win_x, &win_y, &mask))
364       {
365         pw->previous_mouse_x = root_x;
366         pw->previous_mouse_y = root_y;
367         if (si->prefs.verbose_p)
368           fprintf (stderr, "%s: %d: mouse is at %d,%d.\n",
369                    blurb(), pw->prompt_screen->number,
370                    pw->previous_mouse_x, pw->previous_mouse_y);
371       }
372     else if (si->prefs.verbose_p)
373       fprintf (stderr, "%s: %d: unable to determine mouse position?\n",
374                blurb(), pw->prompt_screen->number);
375   }
376
377   /* Figure out where on the desktop to place the window so that it will
378      actually be visible; this takes into account virtual viewports as
379      well as Xinerama. */
380   {
381     int x, y, w, h;
382     get_screen_viewport (pw->prompt_screen, &x, &y, &w, &h,
383                          pw->previous_mouse_x, pw->previous_mouse_y,
384                          si->prefs.verbose_p);
385     if (si->prefs.debug_p) w /= 2;
386     pw->x = x + ((w + pw->width) / 2) - pw->width;
387     pw->y = y + ((h + pw->height) / 2) - pw->height;
388     if (pw->x < x) pw->x = x;
389     if (pw->y < y) pw->y = y;
390   }
391
392   pw->border_width = get_integer_resource ("passwd.borderWidth",
393                                            "Dialog.BorderWidth");
394
395   si->passwd_dialog =
396     XCreateWindow (si->dpy,
397                    RootWindowOfScreen(screen),
398                    pw->x, pw->y, pw->width, pw->height, pw->border_width,
399                    DefaultDepthOfScreen (screen), InputOutput,
400                    DefaultVisualOfScreen(screen),
401                    attrmask, &attrs);
402   XSetWindowBackground (si->dpy, si->passwd_dialog, pw->background);
403
404   pw->logo_pixmap = xscreensaver_logo (si->dpy, si->passwd_dialog, cmap,
405                                        pw->background, 
406                                        &pw->logo_pixels, &pw->logo_npixels,
407                                        0, True);
408
409   /* Before mapping the window, save the bits that are underneath the
410      rectangle the window will occlude.  When we lower the window, we
411      restore these bits.  This works, because the running screenhack
412      has already been sent SIGSTOP, so we know nothing else is drawing
413      right now! */
414   {
415     XGCValues gcv;
416     GC gc;
417     pw->save_under = XCreatePixmap (si->dpy,
418                                     pw->prompt_screen->screensaver_window,
419                                     pw->width + (pw->border_width*2) + 1,
420                                     pw->height + (pw->border_width*2) + 1,
421                                     pw->prompt_screen->current_depth);
422     gcv.function = GXcopy;
423     gc = XCreateGC (si->dpy, pw->save_under, GCFunction, &gcv);
424     XCopyArea (si->dpy, pw->prompt_screen->screensaver_window,
425                pw->save_under, gc,
426                pw->x - pw->border_width, pw->y - pw->border_width,
427                pw->width + (pw->border_width*2) + 1,
428                pw->height + (pw->border_width*2) + 1,
429                0, 0);
430     XFreeGC (si->dpy, gc);
431   }
432
433   XMapRaised (si->dpy, si->passwd_dialog);
434   XSync (si->dpy, False);
435
436   move_mouse_grab (si, si->passwd_dialog,
437                    pw->prompt_screen->cursor,
438                    pw->prompt_screen->number);
439   undo_vp_motion (si);
440
441   si->pw_data = pw;
442
443   if (cmap)
444     XInstallColormap (si->dpy, cmap);
445   draw_passwd_window (si);
446   XSync (si->dpy, False);
447 }
448
449
450 static void
451 draw_passwd_window (saver_info *si)
452 {
453   passwd_dialog_data *pw = si->pw_data;
454   XGCValues gcv;
455   GC gc1, gc2;
456   int spacing, height;
457   int x1, x2, x3, y1, y2;
458   int sw;
459   int tb_height;
460
461   height = (pw->heading_font->ascent + pw->heading_font->descent +
462             pw->body_font->ascent + pw->body_font->descent +
463             (2 * MAX ((pw->label_font->ascent + pw->label_font->descent),
464                       (pw->passwd_font->ascent + pw->passwd_font->descent +
465                        (pw->shadow_width * 4)))) +
466             pw->date_font->ascent + pw->date_font->descent
467             );
468   spacing = ((pw->height - (2 * pw->shadow_width) -
469               pw->internal_border - height)) / 8;
470   if (spacing < 0) spacing = 0;
471
472   gcv.foreground = pw->foreground;
473   gc1 = XCreateGC (si->dpy, si->passwd_dialog, GCForeground, &gcv);
474   gc2 = XCreateGC (si->dpy, si->passwd_dialog, GCForeground, &gcv);
475   x1 = pw->logo_width + pw->thermo_width + (pw->shadow_width * 3);
476   x3 = pw->width - (pw->shadow_width * 2);
477   y1 = (pw->shadow_width * 2) + spacing + spacing;
478
479   /* top heading
480    */
481   XSetFont (si->dpy, gc1, pw->heading_font->fid);
482   sw = string_width (pw->heading_font, pw->heading_label);
483   x2 = (x1 + ((x3 - x1 - sw) / 2));
484   y1 += spacing + pw->heading_font->ascent + pw->heading_font->descent;
485   XDrawString (si->dpy, si->passwd_dialog, gc1, x2, y1,
486                pw->heading_label, strlen(pw->heading_label));
487
488   /* text below top heading
489    */
490   XSetFont (si->dpy, gc1, pw->body_font->fid);
491   y1 += spacing + pw->body_font->ascent + pw->body_font->descent;
492   sw = string_width (pw->body_font, pw->body_label);
493   x2 = (x1 + ((x3 - x1 - sw) / 2));
494   XDrawString (si->dpy, si->passwd_dialog, gc1, x2, y1,
495                pw->body_label, strlen(pw->body_label));
496
497
498   tb_height = (pw->passwd_font->ascent + pw->passwd_font->descent +
499                (pw->shadow_width * 4));
500
501   /* the "User:" prompt
502    */
503   y1 += spacing;
504   y2 = y1;
505   XSetForeground (si->dpy, gc1, pw->foreground);
506   XSetFont (si->dpy, gc1, pw->label_font->fid);
507   y1 += (spacing + tb_height);
508   x2 = (x1 + pw->internal_border +
509         MAX(string_width (pw->label_font, pw->user_label),
510             string_width (pw->label_font, pw->passwd_label)));
511   XDrawString (si->dpy, si->passwd_dialog, gc1,
512                x2 - string_width (pw->label_font, pw->user_label),
513                y1,
514                pw->user_label, strlen(pw->user_label));
515
516   /* the "Password:" prompt
517    */
518   y1 += (spacing + tb_height);
519   XDrawString (si->dpy, si->passwd_dialog, gc1,
520                x2 - string_width (pw->label_font, pw->passwd_label),
521                y1,
522                pw->passwd_label, strlen(pw->passwd_label));
523
524
525   XSetForeground (si->dpy, gc2, pw->passwd_background);
526
527   /* the "user name" text field
528    */
529   y1 = y2;
530   XSetForeground (si->dpy, gc1, pw->passwd_foreground);
531   XSetFont (si->dpy, gc1, pw->passwd_font->fid);
532   y1 += (spacing + tb_height);
533   x2 += (pw->shadow_width * 4);
534
535   pw->passwd_field_width = x3 - x2 - pw->internal_border;
536   pw->passwd_field_height = (pw->passwd_font->ascent +
537                              pw->passwd_font->descent +
538                              pw->shadow_width);
539
540   XFillRectangle (si->dpy, si->passwd_dialog, gc2,
541                   x2 - pw->shadow_width,
542                   y1 - (pw->passwd_font->ascent + pw->passwd_font->descent),
543                   pw->passwd_field_width, pw->passwd_field_height);
544   XDrawString (si->dpy, si->passwd_dialog, gc1, x2, y1,
545                pw->user_string, strlen(pw->user_string));
546
547   /* the "password" text field
548    */
549   y1 += (spacing + tb_height);
550
551   pw->passwd_field_x = x2 - pw->shadow_width;
552   pw->passwd_field_y = y1 - (pw->passwd_font->ascent +
553                              pw->passwd_font->descent);
554
555   /* The shadow around the text fields
556    */
557   y1 = y2;
558   y1 += (spacing + (pw->shadow_width * 3));
559   x1 = x2 - (pw->shadow_width * 2);
560   x2 = pw->passwd_field_width + (pw->shadow_width * 2);
561   y2 = pw->passwd_field_height + (pw->shadow_width * 2);
562
563   draw_shaded_rectangle (si->dpy, si->passwd_dialog,
564                          x1, y1, x2, y2,
565                          pw->shadow_width,
566                          pw->shadow_bottom, pw->shadow_top);
567
568   y1 += (spacing + pw->passwd_font->ascent + pw->passwd_font->descent +
569          (pw->shadow_width * 4));
570   draw_shaded_rectangle (si->dpy, si->passwd_dialog,
571                          x1, y1, x2, y2,
572                          pw->shadow_width,
573                          pw->shadow_bottom, pw->shadow_top);
574
575
576   /* The date, below the text fields
577    */
578   {
579     char buf[100];
580     time_t now = time ((time_t *) 0);
581     struct tm *tm = localtime (&now);
582     memset (buf, 0, sizeof(buf));
583     strftime (buf, sizeof(buf)-1, pw->date_label, tm);
584
585     XSetFont (si->dpy, gc1, pw->date_font->fid);
586     y1 += pw->shadow_width;
587     y1 += (spacing + tb_height);
588     y1 += spacing/2;
589     sw = string_width (pw->date_font, buf);
590     x2 = x1 + x2 - sw;
591     XDrawString (si->dpy, si->passwd_dialog, gc1, x2, y1, buf, strlen(buf));
592   }
593
594
595   /* The logo
596    */
597   x1 = pw->shadow_width * 6;
598   y1 = pw->shadow_width * 6;
599   x2 = pw->logo_width - (pw->shadow_width * 12);
600   y2 = pw->logo_height - (pw->shadow_width * 12);
601
602   if (pw->logo_pixmap)
603     {
604       Window root;
605       int x, y;
606       unsigned int w, h, bw, d;
607       XGetGeometry (si->dpy, pw->logo_pixmap, &root, &x, &y, &w, &h, &bw, &d);
608       XSetForeground (si->dpy, gc1, pw->foreground);
609       XSetBackground (si->dpy, gc1, pw->background);
610       if (d == 1)
611         XCopyPlane (si->dpy, pw->logo_pixmap, si->passwd_dialog, gc1,
612                     0, 0, w, h,
613                     x1 + ((x2 - (int)w) / 2),
614                     y1 + ((y2 - (int)h) / 2),
615                     1);
616       else
617         XCopyArea (si->dpy, pw->logo_pixmap, si->passwd_dialog, gc1,
618                    0, 0, w, h,
619                    x1 + ((x2 - (int)w) / 2),
620                    y1 + ((y2 - (int)h) / 2));
621     }
622
623   /* The thermometer
624    */
625   XSetForeground (si->dpy, gc1, pw->thermo_foreground);
626   XSetForeground (si->dpy, gc2, pw->thermo_background);
627
628   pw->thermo_field_x = pw->logo_width + pw->shadow_width;
629   pw->thermo_field_y = pw->shadow_width * 5;
630   pw->thermo_field_height = pw->height - (pw->shadow_width * 10);
631
632 #if 0
633   /* Solid border inside the logo box. */
634   XSetForeground (si->dpy, gc1, pw->foreground);
635   XDrawRectangle (si->dpy, si->passwd_dialog, gc1, x1, y1, x2-1, y2-1);
636 #endif
637
638   /* The shadow around the logo
639    */
640   draw_shaded_rectangle (si->dpy, si->passwd_dialog,
641                          pw->shadow_width * 4,
642                          pw->shadow_width * 4,
643                          pw->logo_width - (pw->shadow_width * 8),
644                          pw->logo_height - (pw->shadow_width * 8),
645                          pw->shadow_width,
646                          pw->shadow_bottom, pw->shadow_top);
647
648   /* The shadow around the thermometer
649    */
650   draw_shaded_rectangle (si->dpy, si->passwd_dialog,
651                          pw->logo_width,
652                          pw->shadow_width * 4,
653                          pw->thermo_width + (pw->shadow_width * 2),
654                          pw->height - (pw->shadow_width * 8),
655                          pw->shadow_width,
656                          pw->shadow_bottom, pw->shadow_top);
657
658 #if 1
659   /* Solid border inside the thermometer. */
660   XSetForeground (si->dpy, gc1, pw->foreground);
661   XDrawRectangle (si->dpy, si->passwd_dialog, gc1, 
662                   pw->thermo_field_x, pw->thermo_field_y,
663                   pw->thermo_width - 1, pw->thermo_field_height - 1);
664 #endif
665
666   /* The shadow around the whole window
667    */
668   draw_shaded_rectangle (si->dpy, si->passwd_dialog,
669                          0, 0, pw->width, pw->height, pw->shadow_width,
670                          pw->shadow_top, pw->shadow_bottom);
671
672   XFreeGC (si->dpy, gc1);
673   XFreeGC (si->dpy, gc2);
674
675   update_passwd_window (si, pw->passwd_string, pw->ratio);
676 }
677
678
679 static void
680 update_passwd_window (saver_info *si, const char *printed_passwd, float ratio)
681 {
682   passwd_dialog_data *pw = si->pw_data;
683   XGCValues gcv;
684   GC gc1, gc2;
685   int x, y;
686   XRectangle rects[1];
687
688   pw->ratio = ratio;
689   gcv.foreground = pw->passwd_foreground;
690   gcv.font = pw->passwd_font->fid;
691   gc1 = XCreateGC (si->dpy, si->passwd_dialog, GCForeground|GCFont, &gcv);
692   gcv.foreground = pw->passwd_background;
693   gc2 = XCreateGC (si->dpy, si->passwd_dialog, GCForeground, &gcv);
694
695   if (printed_passwd)
696     {
697       char *s = strdup (printed_passwd);
698       if (pw->passwd_string) free (pw->passwd_string);
699       pw->passwd_string = s;
700     }
701
702   /* the "password" text field
703    */
704   rects[0].x =  pw->passwd_field_x;
705   rects[0].y =  pw->passwd_field_y;
706   rects[0].width = pw->passwd_field_width;
707   rects[0].height = pw->passwd_field_height;
708
709   XFillRectangle (si->dpy, si->passwd_dialog, gc2,
710                   rects[0].x, rects[0].y, rects[0].width, rects[0].height);
711
712   XSetClipRectangles (si->dpy, gc1, 0, 0, rects, 1, Unsorted);
713
714   XDrawString (si->dpy, si->passwd_dialog, gc1,
715                rects[0].x + pw->shadow_width,
716                rects[0].y + (pw->passwd_font->ascent +
717                              pw->passwd_font->descent),
718                pw->passwd_string, strlen(pw->passwd_string));
719
720   XSetClipMask (si->dpy, gc1, None);
721
722   /* The I-beam
723    */
724   if (pw->i_beam != 0)
725     {
726       x = (rects[0].x + pw->shadow_width +
727            string_width (pw->passwd_font, pw->passwd_string));
728       y = rects[0].y + pw->shadow_width;
729
730       if (x > rects[0].x + rects[0].width - 1)
731         x = rects[0].x + rects[0].width - 1;
732       XDrawLine (si->dpy, si->passwd_dialog, gc1, 
733                  x, y, x, y + pw->passwd_font->ascent);
734     }
735
736   pw->i_beam = (pw->i_beam + 1) % 4;
737
738
739   /* the thermometer
740    */
741   y = (pw->thermo_field_height - 2) * (1.0 - pw->ratio);
742   if (y > 0)
743     {
744       XFillRectangle (si->dpy, si->passwd_dialog, gc2,
745                       pw->thermo_field_x + 1,
746                       pw->thermo_field_y + 1,
747                       pw->thermo_width-2,
748                       y);
749       XSetForeground (si->dpy, gc1, pw->thermo_foreground);
750       XFillRectangle (si->dpy, si->passwd_dialog, gc1,
751                       pw->thermo_field_x + 1,
752                       pw->thermo_field_y + 1 + y,
753                       pw->thermo_width-2,
754                       MAX (0, pw->thermo_field_height - y - 2));
755     }
756
757   XFreeGC (si->dpy, gc1);
758   XFreeGC (si->dpy, gc2);
759   XSync (si->dpy, False);
760 }
761
762
763 static void
764 destroy_passwd_window (saver_info *si)
765 {
766   saver_preferences *p = &si->prefs;
767   passwd_dialog_data *pw = si->pw_data;
768   saver_screen_info *ssi = pw->prompt_screen;
769   Colormap cmap = DefaultColormapOfScreen (ssi->screen);
770   Pixel black = BlackPixelOfScreen (ssi->screen);
771   Pixel white = WhitePixelOfScreen (ssi->screen);
772   XEvent event;
773
774   memset (pw->typed_passwd, 0, sizeof(pw->typed_passwd));
775   memset (pw->passwd_string, 0, strlen(pw->passwd_string));
776
777   if (pw->timer)
778     XtRemoveTimeOut (pw->timer);
779
780   move_mouse_grab (si, RootWindowOfScreen (ssi->screen),
781                    ssi->cursor, ssi->number);
782
783   if (p->verbose_p)
784     fprintf (stderr, "%s: %d: moving mouse back to %d,%d.\n",
785              blurb(), ssi->number,
786              pw->previous_mouse_x, pw->previous_mouse_y);
787
788   XWarpPointer (si->dpy, None, RootWindowOfScreen (ssi->screen),
789                 0, 0, 0, 0,
790                 pw->previous_mouse_x, pw->previous_mouse_y);
791
792   XSync (si->dpy, False);
793   while (XCheckMaskEvent (si->dpy, PointerMotionMask, &event))
794     if (p->verbose_p)
795       fprintf (stderr, "%s: discarding MotionNotify event.\n", blurb());
796
797   if (si->passwd_dialog)
798     {
799       XDestroyWindow (si->dpy, si->passwd_dialog);
800       si->passwd_dialog = 0;
801     }
802   
803   if (pw->save_under)
804     {
805       XGCValues gcv;
806       GC gc;
807       gcv.function = GXcopy;
808       gc = XCreateGC (si->dpy, ssi->screensaver_window, GCFunction, &gcv);
809       XCopyArea (si->dpy, pw->save_under,
810                  ssi->screensaver_window, gc,
811                  0, 0,
812                  pw->width + (pw->border_width*2) + 1,
813                  pw->height + (pw->border_width*2) + 1,
814                  pw->x - pw->border_width, pw->y - pw->border_width);
815       XFreePixmap (si->dpy, pw->save_under);
816       pw->save_under = 0;
817       XFreeGC (si->dpy, gc);
818     }
819
820   if (pw->heading_label) free (pw->heading_label);
821   if (pw->body_label)    free (pw->body_label);
822   if (pw->user_label)    free (pw->user_label);
823   if (pw->passwd_label)  free (pw->passwd_label);
824   if (pw->date_label)    free (pw->date_label);
825   if (pw->user_string)   free (pw->user_string);
826   if (pw->passwd_string) free (pw->passwd_string);
827
828   if (pw->heading_font) XFreeFont (si->dpy, pw->heading_font);
829   if (pw->body_font)    XFreeFont (si->dpy, pw->body_font);
830   if (pw->label_font)   XFreeFont (si->dpy, pw->label_font);
831   if (pw->passwd_font)  XFreeFont (si->dpy, pw->passwd_font);
832   if (pw->date_font)    XFreeFont (si->dpy, pw->date_font);
833
834   if (pw->foreground != black && pw->foreground != white)
835     XFreeColors (si->dpy, cmap, &pw->foreground, 1, 0L);
836   if (pw->background != black && pw->background != white)
837     XFreeColors (si->dpy, cmap, &pw->background, 1, 0L);
838   if (pw->passwd_foreground != black && pw->passwd_foreground != white)
839     XFreeColors (si->dpy, cmap, &pw->passwd_foreground, 1, 0L);
840   if (pw->passwd_background != black && pw->passwd_background != white)
841     XFreeColors (si->dpy, cmap, &pw->passwd_background, 1, 0L);
842   if (pw->thermo_foreground != black && pw->thermo_foreground != white)
843     XFreeColors (si->dpy, cmap, &pw->thermo_foreground, 1, 0L);
844   if (pw->thermo_background != black && pw->thermo_background != white)
845     XFreeColors (si->dpy, cmap, &pw->thermo_background, 1, 0L);
846   if (pw->shadow_top != black && pw->shadow_top != white)
847     XFreeColors (si->dpy, cmap, &pw->shadow_top, 1, 0L);
848   if (pw->shadow_bottom != black && pw->shadow_bottom != white)
849     XFreeColors (si->dpy, cmap, &pw->shadow_bottom, 1, 0L);
850
851   if (pw->logo_pixmap)
852     XFreePixmap (si->dpy, pw->logo_pixmap);
853   if (pw->logo_pixels)
854     {
855       if (pw->logo_npixels)
856         XFreeColors (si->dpy, cmap, pw->logo_pixels, pw->logo_npixels, 0L);
857       free (pw->logo_pixels);
858       pw->logo_pixels = 0;
859       pw->logo_npixels = 0;
860     }
861
862   if (pw->save_under)
863     XFreePixmap (si->dpy, pw->save_under);
864
865   if (cmap)
866     XInstallColormap (si->dpy, cmap);
867
868   memset (pw, 0, sizeof(*pw));
869   free (pw);
870   si->pw_data = 0;
871 }
872
873
874 #ifdef HAVE_XHPDISABLERESET
875 /* This function enables and disables the C-Sh-Reset hot-key, which
876    normally resets the X server (logging out the logged-in user.)
877    We don't want random people to be able to do that while the
878    screen is locked.
879  */
880 static void
881 hp_lock_reset (saver_info *si, Bool lock_p)
882 {
883   static Bool hp_locked_p = False;
884
885   /* Calls to XHPDisableReset and XHPEnableReset must be balanced,
886      or BadAccess errors occur.  (It's ok for this to be global,
887      since it affects the whole machine, not just the current screen.)
888   */
889   if (hp_locked_p == lock_p)
890     return;
891
892   if (lock_p)
893     XHPDisableReset (si->dpy);
894   else
895     XHPEnableReset (si->dpy);
896   hp_locked_p = lock_p;
897 }
898 #endif /* HAVE_XHPDISABLERESET */
899
900
901 \f
902 /* This function enables and disables the C-Sh-F1 ... F12 hot-keys,
903    which, on Linux systems, switches to another virtual console.
904    We'd like the whole screen/keyboard to be locked, not just one
905    virtual console, so this function disables that while the X
906    screen is locked.
907
908    Unfortunately, this doesn't work -- this ioctl only works when
909    called by root, and we have disavowed our privileges long ago.
910  */
911 #ifdef HAVE_VT_LOCKSWITCH
912 static void
913 linux_lock_vt_switch (saver_info *si, Bool lock_p)
914 {
915   saver_preferences *p = &si->prefs;
916   static Bool vt_locked_p = False;
917   const char *dev_console = "/dev/console";
918   int fd;
919
920   if (lock_p == vt_locked_p)
921     return;
922
923   if (lock_p && !p->lock_vt_p)
924     return;
925
926   fd = open (dev_console, O_RDWR);
927   if (fd < 0)
928     {
929       char buf [255];
930       sprintf (buf, "%s: couldn't %s VTs: %s", blurb(),
931                (lock_p ? "lock" : "unlock"),
932                dev_console);
933 #if 1 /* #### doesn't work yet, so don't bother complaining */
934       perror (buf);
935 #endif
936       return;
937     }
938
939   if (ioctl (fd, (lock_p ? VT_LOCKSWITCH : VT_UNLOCKSWITCH)) == 0)
940     {
941       vt_locked_p = lock_p;
942
943       if (p->verbose_p)
944         fprintf (stderr, "%s: %s VTs\n", blurb(),
945                  (lock_p ? "locked" : "unlocked"));
946     }
947   else
948     {
949       char buf [255];
950       sprintf (buf, "%s: couldn't %s VTs: ioctl", blurb(),
951                (lock_p ? "lock" : "unlock"));
952 #if 0 /* #### doesn't work yet, so don't bother complaining */
953       perror (buf);
954 #endif
955     }
956
957   close (fd);
958 }
959 #endif /* HAVE_VT_LOCKSWITCH */
960
961 \f
962 /* This function enables and disables the C-Alt-Plus and C-Alt-Minus
963    hot-keys, which normally change the resolution of the X server.
964    We don't want people to be able to switch the server resolution
965    while the screen is locked, because if they switch to a higher
966    resolution, it could cause part of the underlying desktop to become
967    exposed.
968  */
969 #ifdef HAVE_XF86VMODE
970
971 static int ignore_all_errors_ehandler (Display *dpy, XErrorEvent *error);
972 static Bool vp_got_error = False;
973
974 static void
975 xfree_lock_mode_switch (saver_info *si, Bool lock_p)
976 {
977   static Bool any_mode_locked_p = False;
978   saver_preferences *p = &si->prefs;
979   int screen;
980   int event, error;
981   Bool status;
982   XErrorHandler old_handler;
983
984   if (any_mode_locked_p == lock_p)
985     return;
986   if (!XF86VidModeQueryExtension (si->dpy, &event, &error))
987     return;
988
989   for (screen = 0; screen < si->nscreens; screen++)
990     {
991       XSync (si->dpy, False);
992       old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
993       status = XF86VidModeLockModeSwitch (si->dpy, screen, lock_p);
994       XSync (si->dpy, False);
995       XSetErrorHandler (old_handler);
996       if (vp_got_error) status = False;
997
998       if (status)
999         any_mode_locked_p = lock_p;
1000
1001       if (!status && (p->verbose_p || !lock_p))
1002         /* Only print this when verbose, or when we locked but can't unlock.
1003            I tried printing this message whenever it comes up, but
1004            mode-locking always fails if DontZoom is set in XF86Config. */
1005         fprintf (stderr, "%s: %d: unable to %s mode switching!\n",
1006                  blurb(), screen, (lock_p ? "lock" : "unlock"));
1007       else if (p->verbose_p)
1008         fprintf (stderr, "%s: %d: %s mode switching.\n",
1009                  blurb(), screen, (lock_p ? "locked" : "unlocked"));
1010     }
1011 }
1012
1013 static int
1014 ignore_all_errors_ehandler (Display *dpy, XErrorEvent *error)
1015 {
1016   vp_got_error = True;
1017   return 0;
1018 }
1019
1020 #endif /* HAVE_XF86VMODE */
1021
1022 \f
1023 /* If the viewport has been scrolled since the screen was blanked,
1024    then scroll it back to where it belongs.  This function only exists
1025    to patch over a very brief race condition.
1026  */
1027 static void
1028 undo_vp_motion (saver_info *si)
1029 {
1030 #ifdef HAVE_XF86VMODE
1031   saver_preferences *p = &si->prefs;
1032   int screen;
1033   int event, error;
1034
1035   if (!XF86VidModeQueryExtension (si->dpy, &event, &error))
1036     return;
1037
1038   for (screen = 0; screen < si->nscreens; screen++)
1039     {
1040       saver_screen_info *ssi = &si->screens[screen];
1041       int x, y;
1042       Bool status;
1043
1044       if (ssi->blank_vp_x == -1 && ssi->blank_vp_y == -1)
1045         break;
1046       if (!XF86VidModeGetViewPort (si->dpy, screen, &x, &y))
1047         return;
1048       if (ssi->blank_vp_x == x && ssi->blank_vp_y == y)
1049         return;
1050     
1051       /* We're going to move the viewport.  The mouse has just been grabbed on
1052          (and constrained to, thus warped to) the password window, so it is no
1053          longer near the edge of the screen.  However, wait a bit anyway, just
1054          to make sure the server drains its last motion event, so that the
1055          screen doesn't continue to scroll after we've reset the viewport.
1056        */
1057       XSync (si->dpy, False);
1058       usleep (250000);  /* 1/4 second */
1059       XSync (si->dpy, False);
1060
1061       status = XF86VidModeSetViewPort (si->dpy, screen,
1062                                        ssi->blank_vp_x, ssi->blank_vp_y);
1063
1064       if (!status)
1065         fprintf (stderr,
1066                  "%s: %d: unable to move vp from (%d,%d) back to (%d,%d)!\n",
1067                  blurb(), screen, x, y, ssi->blank_vp_x, ssi->blank_vp_y);
1068       else if (p->verbose_p)
1069         fprintf (stderr,
1070                  "%s: %d: vp moved to (%d,%d); moved it back to (%d,%d).\n",
1071                  blurb(), screen, x, y, ssi->blank_vp_x, ssi->blank_vp_y);
1072     }
1073 #endif /* HAVE_XF86VMODE */
1074 }
1075
1076
1077 \f
1078 /* Interactions
1079  */
1080
1081 static void
1082 passwd_animate_timer (XtPointer closure, XtIntervalId *id)
1083 {
1084   saver_info *si = (saver_info *) closure;
1085   int tick = 166;
1086   passwd_dialog_data *pw = si->pw_data;
1087
1088   if (!pw) return;
1089
1090   pw->ratio -= (1.0 / ((double) si->prefs.passwd_timeout / (double) tick));
1091   if (pw->ratio < 0)
1092     {
1093       pw->ratio = 0;
1094       if (pw->state == pw_read)
1095         pw->state = pw_time;
1096     }
1097
1098   update_passwd_window (si, 0, pw->ratio);
1099
1100   if (pw->state == pw_read)
1101     pw->timer = XtAppAddTimeOut (si->app, tick, passwd_animate_timer,
1102                                  (XtPointer) si);
1103   else
1104     pw->timer = 0;
1105
1106   idle_timer ((XtPointer) si, id);
1107 }
1108
1109
1110 static XComposeStatus *compose_status;
1111
1112 static void
1113 handle_passwd_key (saver_info *si, XKeyEvent *event)
1114 {
1115   saver_preferences *p = &si->prefs;
1116   passwd_dialog_data *pw = si->pw_data;
1117   int pw_size = sizeof (pw->typed_passwd) - 1;
1118   char *typed_passwd = pw->typed_passwd;
1119   char s[2];
1120   char *stars = 0;
1121   int i;
1122   int size = XLookupString (event, s, 1, 0, compose_status);
1123
1124   if (size != 1) return;
1125
1126   s[1] = 0;
1127
1128   switch (*s)
1129     {
1130     case '\010': case '\177':                           /* Backspace */
1131       if (!*typed_passwd)
1132         XBell (si->dpy, 0);
1133       else
1134         typed_passwd [strlen(typed_passwd)-1] = 0;
1135       break;
1136
1137     case '\025': case '\030':                           /* Erase line */
1138       memset (typed_passwd, 0, pw_size);
1139       break;
1140
1141     case '\012': case '\015':                           /* Enter */
1142       if (pw->state != pw_read)
1143         ;  /* already done? */
1144       else if (typed_passwd[0] == 0)
1145         pw->state = pw_null;
1146       else
1147         {
1148           update_passwd_window (si, "Checking...", pw->ratio);
1149           XSync (si->dpy, False);
1150           if (passwd_valid_p (typed_passwd, p->verbose_p))
1151             pw->state = pw_ok;
1152           else
1153             pw->state = pw_fail;
1154           update_passwd_window (si, "", pw->ratio);
1155         }
1156       break;
1157
1158     default:
1159       i = strlen (typed_passwd);
1160       if (i >= pw_size-1)
1161         XBell (si->dpy, 0);
1162       else
1163         {
1164           typed_passwd [i] = *s;
1165           typed_passwd [i+1] = 0;
1166         }
1167       break;
1168     }
1169
1170   i = strlen(typed_passwd);
1171   stars = (char *) malloc(i+1);
1172   memset (stars, '*', i);
1173   stars[i] = 0;
1174   update_passwd_window (si, stars, pw->ratio);
1175   free (stars);
1176 }
1177
1178
1179 static void
1180 passwd_event_loop (saver_info *si)
1181 {
1182   saver_preferences *p = &si->prefs;
1183   char *msg = 0;
1184   XEvent event;
1185   passwd_animate_timer ((XtPointer) si, 0);
1186
1187   while (si->pw_data && si->pw_data->state == pw_read)
1188     {
1189       XtAppNextEvent (si->app, &event);
1190       if (event.xany.window == si->passwd_dialog && event.xany.type == Expose)
1191         draw_passwd_window (si);
1192       else if (event.xany.type == KeyPress)
1193         handle_passwd_key (si, &event.xkey);
1194       else
1195         XtDispatchEvent (&event);
1196     }
1197
1198   switch (si->pw_data->state)
1199     {
1200     case pw_ok:   msg = 0; break;
1201     case pw_null: msg = ""; break;
1202     case pw_time: msg = "Timed out!"; break;
1203     default:      msg = "Sorry!"; break;
1204     }
1205
1206   if (si->pw_data->state == pw_fail)
1207     si->unlock_failures++;
1208
1209   if (p->verbose_p)
1210     switch (si->pw_data->state)
1211       {
1212       case pw_ok:
1213         fprintf (stderr, "%s: password correct.\n", blurb()); break;
1214       case pw_fail:
1215         fprintf (stderr, "%s: password incorrect!\n", blurb()); break;
1216       case pw_null:
1217       case pw_cancel:
1218         fprintf (stderr, "%s: password entry cancelled.\n", blurb()); break;
1219       case pw_time:
1220         fprintf (stderr, "%s: password entry timed out.\n", blurb()); break;
1221       default: break;
1222       }
1223
1224 #ifdef HAVE_SYSLOG
1225   if (si->pw_data->state == pw_fail)
1226     {
1227       /* If they typed a password (as opposed to just hitting return) and
1228          the password was invalid, log it.
1229       */
1230       struct passwd *pw = getpwuid (getuid ());
1231       char *d = DisplayString (si->dpy);
1232       char *u = (pw->pw_name ? pw->pw_name : "???");
1233       int opt = 0;
1234       int fac = 0;
1235
1236 # ifdef LOG_PID
1237       opt = LOG_PID;
1238 # endif
1239
1240 # if defined(LOG_AUTHPRIV)
1241       fac = LOG_AUTHPRIV;
1242 # elif defined(LOG_AUTH)
1243       fac = LOG_AUTH;
1244 # else
1245       fac = LOG_DAEMON;
1246 # endif
1247
1248       if (!d) d = "";
1249       openlog (progname, opt, fac);
1250       syslog (LOG_NOTICE, "FAILED LOGIN %d ON DISPLAY \"%s\", FOR \"%s\"",
1251               si->unlock_failures, d, u);
1252       closelog ();
1253     }
1254 #endif /* HAVE_SYSLOG */
1255
1256   if (si->pw_data->state == pw_fail)
1257     XBell (si->dpy, False);
1258
1259   if (si->pw_data->state == pw_ok && si->unlock_failures != 0)
1260     {
1261       if (si->unlock_failures == 1)
1262         fprintf (real_stderr,
1263                  "%s: WARNING: 1 failed attempt to unlock the screen.\n",
1264                  blurb());
1265       else
1266         fprintf (real_stderr,
1267                  "%s: WARNING: %d failed attempts to unlock the screen.\n",
1268                  blurb(), si->unlock_failures);
1269       fflush (real_stderr);
1270
1271       si->unlock_failures = 0;
1272     }
1273
1274   if (msg)
1275     {
1276       si->pw_data->i_beam = 0;
1277       update_passwd_window (si, msg, 0.0);
1278       XSync (si->dpy, False);
1279       sleep (1);
1280
1281       /* Swallow all pending KeyPress/KeyRelease events. */
1282       {
1283         XEvent e;
1284         while (XCheckMaskEvent (si->dpy, KeyPressMask|KeyReleaseMask, &e))
1285           ;
1286       }
1287     }
1288 }
1289
1290
1291 static void
1292 handle_typeahead (saver_info *si)
1293 {
1294   passwd_dialog_data *pw = si->pw_data;
1295   int i;
1296   if (!si->unlock_typeahead)
1297     return;
1298
1299   i = strlen (si->unlock_typeahead);
1300   if (i >= sizeof(pw->typed_passwd) - 1)
1301     i = sizeof(pw->typed_passwd) - 1;
1302
1303   memcpy (pw->typed_passwd, si->unlock_typeahead, i);
1304   pw->typed_passwd [i] = 0;
1305
1306   memset (si->unlock_typeahead, '*', strlen(si->unlock_typeahead));
1307   si->unlock_typeahead[i] = 0;
1308   update_passwd_window (si, si->unlock_typeahead, pw->ratio);
1309
1310   free (si->unlock_typeahead);
1311   si->unlock_typeahead = 0;
1312 }
1313
1314
1315 Bool
1316 unlock_p (saver_info *si)
1317 {
1318   saver_preferences *p = &si->prefs;
1319   Bool status;
1320
1321   raise_window (si, True, True, True);
1322
1323   if (p->verbose_p)
1324     fprintf (stderr, "%s: prompting for password.\n", blurb());
1325
1326   if (si->pw_data || si->passwd_dialog)
1327     destroy_passwd_window (si);
1328
1329   make_passwd_window (si);
1330
1331   compose_status = calloc (1, sizeof (*compose_status));
1332
1333   handle_typeahead (si);
1334   passwd_event_loop (si);
1335
1336   status = (si->pw_data->state == pw_ok);
1337   destroy_passwd_window (si);
1338
1339   free (compose_status);
1340   compose_status = 0;
1341
1342   return status;
1343 }
1344
1345
1346 void
1347 set_locked_p (saver_info *si, Bool locked_p)
1348 {
1349   si->locked_p = locked_p;
1350
1351 #ifdef HAVE_XHPDISABLERESET
1352   hp_lock_reset (si, locked_p);                 /* turn off/on C-Sh-Reset */
1353 #endif
1354 #ifdef HAVE_VT_LOCKSWITCH
1355   linux_lock_vt_switch (si, locked_p);          /* turn off/on C-Alt-F1 */
1356 #endif
1357 #ifdef HAVE_XF86VMODE
1358   xfree_lock_mode_switch (si, locked_p);        /* turn off/on C-Alt-Plus */
1359 #endif
1360
1361   store_saver_status (si);                      /* store locked-p */
1362 }
1363
1364
1365 #else  /*  NO_LOCKING -- whole file */
1366
1367 void
1368 set_locked_p (saver_info *si, Bool locked_p)
1369 {
1370   if (locked_p) abort();
1371 }
1372
1373 #endif /* !NO_LOCKING */