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