http://ftp.x.org/contrib/applications/xscreensaver-3.03.tar.gz
[xscreensaver] / driver / lock.c
1 /* lock.c --- handling the password dialog for locking-mode.
2  * xscreensaver, Copyright (c) 1993-1998 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 #ifndef NO_LOCKING   /* whole file */
21
22 #include <X11/Intrinsic.h>
23 #include "xscreensaver.h"
24 #include "resources.h"
25
26 #ifdef HAVE_SYSLOG
27 # include <syslog.h>
28 #endif /* HAVE_SYSLOG */
29
30 #ifdef _VROOT_H_
31 ERROR!  You must not include vroot.h in this file.
32 #endif
33
34 #ifndef VMS
35 # include <pwd.h>
36 #else /* VMS */
37
38 extern char *getenv(const char *name);
39 extern int validate_user(char *name, char *password);
40
41 static Bool
42 vms_passwd_valid_p(char *pw, Bool verbose_p)
43 {
44   return (validate_user (getenv("USER"), typed_passwd) == 1);
45 }
46 # undef passwd_valid_p
47 # define passwd_valid_p vms_passwd_valid_p
48
49 #endif /* VMS */
50
51
52 #undef MAX
53 #define MAX(a,b) ((a)>(b)?(a):(b))
54
55 enum passwd_state { pw_read, pw_ok, pw_null, pw_fail, pw_cancel, pw_time };
56
57 struct passwd_dialog_data {
58
59   enum passwd_state state;
60   char typed_passwd [80];
61   XtIntervalId timer;
62   int i_beam;
63
64   float ratio;
65   Position x, y;
66   Dimension width;
67   Dimension height;
68   Dimension border_width;
69
70   char *heading_label;
71   char *body_label;
72   char *user_label;
73   char *passwd_label;
74   char *user_string;
75   char *passwd_string;
76
77   XFontStruct *heading_font;
78   XFontStruct *body_font;
79   XFontStruct *label_font;
80   XFontStruct *passwd_font;
81
82   Pixel foreground;
83   Pixel background;
84   Pixel passwd_foreground;
85   Pixel passwd_background;
86   Pixel logo_foreground;
87   Pixel logo_background;
88   Pixel shadow_top;
89   Pixel shadow_bottom;
90
91   Dimension logo_width;
92   Dimension logo_height;
93   Dimension thermo_width;
94   Dimension internal_border;
95   Dimension shadow_width;
96
97   Dimension passwd_field_x, passwd_field_y;
98   Dimension passwd_field_width, passwd_field_height;
99
100   Dimension thermo_field_x, thermo_field_y;
101   Dimension thermo_field_height;
102
103   Pixmap save_under;
104 };
105
106
107 void
108 make_passwd_window (saver_info *si)
109 {
110   struct passwd *p = getpwuid (getuid ());
111   XSetWindowAttributes attrs;
112   unsigned long attrmask = 0;
113   Screen *screen = si->default_screen->screen;
114   passwd_dialog_data *pw = (passwd_dialog_data *) calloc (1, sizeof(*pw));
115   Colormap cmap = DefaultColormapOfScreen (screen);
116   char *f;
117
118   pw->ratio = 1.0;
119
120   pw->heading_label = get_string_resource ("passwd.heading.label",
121                                            "Dialog.Label.Label");
122   pw->body_label = get_string_resource ("passwd.body.label",
123                                         "Dialog.Label.Label");
124   pw->user_label = get_string_resource ("passwd.user.label",
125                                         "Dialog.Label.Label");
126   pw->passwd_label = get_string_resource ("passwd.passwd.label",
127                                           "Dialog.Label.Label");
128
129   if (!pw->heading_label)
130     pw->heading_label = strdup("ERROR: REESOURCES NOT INSTALLED CORRECTLY");
131   if (!pw->body_label)
132     pw->body_label = strdup("ERROR: REESOURCES NOT INSTALLED CORRECTLY");
133   if (!pw->user_label) pw->user_label = strdup("ERROR");
134   if (!pw->passwd_label) pw->passwd_label = strdup("ERROR");
135
136   /* Put the version number in the label. */
137   {
138     char *s = (char *) malloc (strlen(pw->heading_label) + 20);
139     sprintf(s, pw->heading_label, si->version);
140     free (pw->heading_label);
141     pw->heading_label = s;
142   }
143
144   pw->user_string = (p->pw_name ? p->pw_name : "???");
145   pw->passwd_string = strdup("");
146
147   f = get_string_resource ("passwd.headingFont", "Dialog.Font");
148   pw->heading_font = XLoadQueryFont (si->dpy, (f ? f : "fixed"));
149   if (!pw->heading_font) pw->heading_font = XLoadQueryFont (si->dpy, "fixed");
150   if (f) free (f);
151
152   f = get_string_resource("passwd.bodyFont", "Dialog.Font");
153   pw->body_font = XLoadQueryFont (si->dpy, (f ? f : "fixed"));
154   if (!pw->body_font) pw->body_font = XLoadQueryFont (si->dpy, "fixed");
155   if (f) free (f);
156
157   f = get_string_resource("passwd.labelFont", "Dialog.Font");
158   pw->label_font = XLoadQueryFont (si->dpy, (f ? f : "fixed"));
159   if (!pw->label_font) pw->label_font = XLoadQueryFont (si->dpy, "fixed");
160   if (f) free (f);
161
162   f = get_string_resource("passwd.passwdFont", "Dialog.Font");
163   pw->passwd_font = XLoadQueryFont (si->dpy, (f ? f : "fixed"));
164   if (!pw->passwd_font) pw->passwd_font = XLoadQueryFont (si->dpy, "fixed");
165   if (f) free (f);
166
167   pw->foreground = get_pixel_resource ("passwd.foreground",
168                                        "Dialog.Foreground",
169                                        si->dpy, cmap);
170   pw->background = get_pixel_resource ("passwd.background",
171                                        "Dialog.Background",
172                                        si->dpy, cmap);
173
174   if (pw->foreground == pw->background)
175     {
176       /* Make sure the error messages show up. */
177       pw->foreground = BlackPixelOfScreen (screen);
178       pw->background = WhitePixelOfScreen (screen);
179     }
180
181   pw->passwd_foreground = get_pixel_resource ("passwd.text.foreground",
182                                               "Dialog.Text.Foreground",
183                                               si->dpy, cmap);
184   pw->passwd_background = get_pixel_resource ("passwd.text.background",
185                                               "Dialog.Text.Background",
186                                               si->dpy, cmap);
187   pw->logo_foreground = get_pixel_resource ("passwd.logo.foreground",
188                                             "Dialog.Logo.Foreground",
189                                             si->dpy, cmap);
190   pw->logo_background = get_pixel_resource ("passwd.logo.background",
191                                             "Dialog.Logo.Background",
192                                             si->dpy, cmap);
193   pw->shadow_top = get_pixel_resource ("passwd.topShadowColor",
194                                        "Dialog.Foreground",
195                                        si->dpy, cmap);
196   pw->shadow_bottom = get_pixel_resource ("passwd.bottomShadowColor",
197                                           "Dialog.Background",
198                                           si->dpy, cmap);
199
200   pw->logo_width = get_integer_resource ("passwd.logo.width",
201                                          "Dialog.Logo.Width");
202   pw->logo_height = get_integer_resource ("passwd.logo.height",
203                                           "Dialog.Logo.Height");
204   pw->thermo_width = get_integer_resource ("passwd.thermometer.width",
205                                            "Dialog.Thermometer.Width");
206   pw->internal_border = get_integer_resource ("passwd.internalBorderWidth",
207                                               "Dialog.InternalBorderWidth");
208   pw->shadow_width = get_integer_resource ("passwd.shadowThickness",
209                                            "Dialog.ShadowThickness");
210
211   if (pw->logo_width == 0)  pw->logo_width = 150;
212   if (pw->logo_height == 0) pw->logo_height = 150;
213   if (pw->internal_border == 0) pw->internal_border = 15;
214   if (pw->shadow_width == 0) pw->shadow_width = 4;
215   if (pw->thermo_width == 0) pw->thermo_width = pw->shadow_width;
216
217   {
218     int direction, ascent, descent;
219     XCharStruct overall;
220
221     pw->width = 0;
222     pw->height = 0;
223
224     /* Measure the heading_label. */
225     XTextExtents (pw->heading_font,
226                   pw->heading_label, strlen(pw->heading_label),
227                   &direction, &ascent, &descent, &overall);
228     if (overall.width > pw->width) pw->width = overall.width;
229     pw->height += ascent + descent;
230
231     /* Measure the body_label. */
232     XTextExtents (pw->body_font,
233                   pw->body_label, strlen(pw->body_label),
234                   &direction, &ascent, &descent, &overall);
235     if (overall.width > pw->width) pw->width = overall.width;
236     pw->height += ascent + descent;
237
238     {
239       Dimension w2 = 0, w3 = 0;
240       Dimension h2 = 0, h3 = 0;
241       const char *passwd_string = "MMMMMMMMMMMM";
242
243       /* Measure the user_label. */
244       XTextExtents (pw->label_font,
245                     pw->user_label, strlen(pw->user_label),
246                     &direction, &ascent, &descent, &overall);
247       if (overall.width > w2)  w2 = overall.width;
248       h2 += ascent + descent;
249
250       /* Measure the passwd_label. */
251       XTextExtents (pw->label_font,
252                     pw->passwd_label, strlen(pw->passwd_label),
253                     &direction, &ascent, &descent, &overall);
254       if (overall.width > w2)  w2 = overall.width;
255       h2 += ascent + descent;
256
257       /* Measure the user_string. */
258       XTextExtents (pw->passwd_font,
259                     pw->user_string, strlen(pw->user_string),
260                     &direction, &ascent, &descent, &overall);
261       overall.width += (pw->shadow_width * 4);
262       ascent += (pw->shadow_width * 4);
263       if (overall.width > w3)  w3 = overall.width;
264       h3 += ascent + descent;
265
266       /* Measure the (maximally-sized, dummy) passwd_string. */
267       XTextExtents (pw->passwd_font,
268                     passwd_string, strlen(passwd_string),
269                     &direction, &ascent, &descent, &overall);
270       overall.width += (pw->shadow_width * 4);
271       ascent += (pw->shadow_width * 4);
272       if (overall.width > w3)  w3 = overall.width;
273       h3 += ascent + descent;
274
275       w2 = w2 + w3 + (pw->shadow_width * 2);
276       h2 = MAX (h2, h3);
277
278       if (w2 > pw->width)  pw->width  = w2;
279       pw->height += h2;
280     }
281
282     pw->width  += (pw->internal_border * 2);
283     pw->height += (pw->internal_border * 4);
284
285     pw->width += pw->thermo_width + (pw->shadow_width * 3);
286
287     if (pw->logo_height > pw->height)
288       pw->height = pw->logo_height;
289     else if (pw->height > pw->logo_height)
290       pw->logo_height = pw->height;
291
292     pw->logo_width = pw->logo_height;
293
294     pw->width += pw->logo_width;
295   }
296
297   attrmask |= CWOverrideRedirect; attrs.override_redirect = True;
298   attrmask |= CWEventMask; attrs.event_mask = ExposureMask|KeyPressMask;
299
300   {
301     Dimension w = WidthOfScreen(screen);
302     Dimension h = HeightOfScreen(screen);
303     if (si->prefs.debug_p) w /= 2;
304     pw->x = ((w + pw->width) / 2) - pw->width;
305     pw->y = ((h + pw->height) / 2) - pw->height;
306     if (pw->x < 0) pw->x = 0;
307     if (pw->y < 0) pw->y = 0;
308   }
309
310   pw->border_width = get_integer_resource ("passwd.borderWidth",
311                                            "Dialog.BorderWidth");
312
313   si->passwd_dialog =
314     XCreateWindow (si->dpy,
315                    RootWindowOfScreen(screen),
316                    pw->x, pw->y, pw->width, pw->height, pw->border_width,
317                    DefaultDepthOfScreen (screen), InputOutput,
318                    DefaultVisualOfScreen(screen),
319                    attrmask, &attrs);
320   XSetWindowBackground (si->dpy, si->passwd_dialog, pw->background);
321
322
323   /* Before mapping the window, save the bits that are underneath the
324      rectangle the window will occlude.  When we lower the window, we
325      restore these bits.  This works, because the running screenhack
326      has already been sent SIGSTOP, so we know nothing else is drawing
327      right now! */
328   {
329     XGCValues gcv;
330     GC gc;
331     pw->save_under = XCreatePixmap (si->dpy,
332                                     si->default_screen->screensaver_window,
333                                     pw->width + (pw->border_width*2) + 1,
334                                     pw->height + (pw->border_width*2) + 1,
335                                     si->default_screen->current_depth);
336     gcv.function = GXcopy;
337     gc = XCreateGC (si->dpy, pw->save_under, GCFunction, &gcv);
338     XCopyArea (si->dpy, si->default_screen->screensaver_window,
339                pw->save_under, gc,
340                pw->x - pw->border_width, pw->y - pw->border_width,
341                pw->width + (pw->border_width*2) + 1,
342                pw->height + (pw->border_width*2) + 1,
343                0, 0);
344     XFreeGC (si->dpy, gc);
345   }
346
347   XMapRaised (si->dpy, si->passwd_dialog);
348   XSync (si->dpy, False);
349
350   si->pw_data = pw;
351
352   draw_passwd_window (si);
353   XSync (si->dpy, False);
354 }
355
356
357 void
358 draw_passwd_window (saver_info *si)
359 {
360   passwd_dialog_data *pw = si->pw_data;
361   XGCValues gcv;
362   GC gc1, gc2;
363   int spacing, height;
364   int x1, x2, x3, y1, y2;
365   int sw;
366   int tb_height;
367
368   height = (pw->heading_font->ascent + pw->heading_font->descent +
369             pw->body_font->ascent + pw->body_font->descent +
370             (2 * MAX ((pw->label_font->ascent + pw->label_font->descent),
371                       (pw->passwd_font->ascent + pw->passwd_font->descent +
372                        (pw->shadow_width * 4)))));
373   spacing = ((pw->height - (2 * pw->shadow_width) -
374               pw->internal_border - height)) / 8;
375   if (spacing < 0) spacing = 0;
376
377   gcv.foreground = pw->foreground;
378   gc1 = XCreateGC (si->dpy, si->passwd_dialog, GCForeground, &gcv);
379   gc2 = XCreateGC (si->dpy, si->passwd_dialog, GCForeground, &gcv);
380   x1 = pw->logo_width + pw->thermo_width + (pw->shadow_width * 3);
381   x3 = pw->width - (pw->shadow_width * 2);
382   y1 = (pw->shadow_width * 2) + spacing + spacing;
383
384   /* top heading
385    */
386   XSetFont (si->dpy, gc1, pw->heading_font->fid);
387   sw = string_width (pw->heading_font, pw->heading_label);
388   x2 = (x1 + ((x3 - x1 - sw) / 2));
389   y1 += spacing + pw->heading_font->ascent + pw->heading_font->descent;
390   XDrawString (si->dpy, si->passwd_dialog, gc1, x2, y1,
391                pw->heading_label, strlen(pw->heading_label));
392
393   /* text below top heading
394    */
395   XSetFont (si->dpy, gc1, pw->body_font->fid);
396   y1 += spacing + pw->body_font->ascent + pw->body_font->descent;
397   sw = string_width (pw->body_font, pw->body_label);
398   x2 = (x1 + ((x3 - x1 - sw) / 2));
399   XDrawString (si->dpy, si->passwd_dialog, gc1, x2, y1,
400                pw->body_label, strlen(pw->body_label));
401
402
403   tb_height = (pw->passwd_font->ascent + pw->passwd_font->descent +
404                (pw->shadow_width * 4));
405
406   /* the "User:" prompt
407    */
408   y1 += spacing;
409   y2 = y1;
410   XSetForeground (si->dpy, gc1, pw->foreground);
411   XSetFont (si->dpy, gc1, pw->label_font->fid);
412   y1 += (spacing + tb_height);
413   x2 = (x1 + pw->internal_border +
414         MAX(string_width (pw->label_font, pw->user_label),
415             string_width (pw->label_font, pw->passwd_label)));
416   XDrawString (si->dpy, si->passwd_dialog, gc1,
417                x2 - string_width (pw->label_font, pw->user_label),
418                y1,
419                pw->user_label, strlen(pw->user_label));
420
421   /* the "Password:" prompt
422    */
423   y1 += (spacing + tb_height);
424   XDrawString (si->dpy, si->passwd_dialog, gc1,
425                x2 - string_width (pw->label_font, pw->passwd_label),
426                y1,
427                pw->passwd_label, strlen(pw->passwd_label));
428
429
430   XSetForeground (si->dpy, gc2, pw->passwd_background);
431
432   /* the "user name" text field
433    */
434   y1 = y2;
435   XSetForeground (si->dpy, gc1, pw->passwd_foreground);
436   XSetFont (si->dpy, gc1, pw->passwd_font->fid);
437   y1 += (spacing + tb_height);
438   x2 += (pw->shadow_width * 4);
439
440   pw->passwd_field_width = x3 - x2 - pw->internal_border;
441   pw->passwd_field_height = (pw->passwd_font->ascent +
442                              pw->passwd_font->descent +
443                              pw->shadow_width);
444
445   XFillRectangle (si->dpy, si->passwd_dialog, gc2,
446                   x2 - pw->shadow_width,
447                   y1 - (pw->passwd_font->ascent + pw->passwd_font->descent),
448                   pw->passwd_field_width, pw->passwd_field_height);
449   XDrawString (si->dpy, si->passwd_dialog, gc1, x2, y1,
450                pw->user_string, strlen(pw->user_string));
451
452   /* the "password" text field
453    */
454   y1 += (spacing + tb_height);
455
456   pw->passwd_field_x = x2 - pw->shadow_width;
457   pw->passwd_field_y = y1 - (pw->passwd_font->ascent +
458                              pw->passwd_font->descent);
459
460   /* The shadow around the text fields
461    */
462   y1 = y2;
463   y1 += (spacing + (pw->shadow_width * 3));
464   x1 = x2 - (pw->shadow_width * 2);
465   x2 = pw->passwd_field_width + (pw->shadow_width * 2);
466   y2 = pw->passwd_field_height + (pw->shadow_width * 2);
467
468   draw_shaded_rectangle (si->dpy, si->passwd_dialog,
469                          x1, y1, x2, y2,
470                          pw->shadow_width,
471                          pw->shadow_bottom, pw->shadow_top);
472
473   y1 += (spacing + pw->passwd_font->ascent + pw->passwd_font->descent +
474          (pw->shadow_width * 4));
475   draw_shaded_rectangle (si->dpy, si->passwd_dialog,
476                          x1, y1, x2, y2,
477                          pw->shadow_width,
478                          pw->shadow_bottom, pw->shadow_top);
479
480   /* the logo
481    */
482   XSetForeground (si->dpy, gc1, pw->logo_foreground);
483   XSetForeground (si->dpy, gc2, pw->logo_background);
484
485   x1 = pw->shadow_width * 3;
486   y1 = pw->shadow_width * 3;
487   x2 = pw->logo_width - (pw->shadow_width * 6);
488   y2 = pw->logo_height - (pw->shadow_width * 6);
489
490   XFillRectangle (si->dpy, si->passwd_dialog, gc2, x1, y1, x2, y2);
491   skull (si->dpy, si->passwd_dialog, gc1, gc2,
492          x1 + pw->shadow_width, y1 + pw->shadow_width,
493          x2 - (pw->shadow_width * 2), y2 - (pw->shadow_width * 2));
494
495   /* The thermometer
496    */
497   pw->thermo_field_x = pw->logo_width + pw->shadow_width;
498   pw->thermo_field_y = pw->shadow_width * 3;
499   pw->thermo_field_height = pw->height - (pw->shadow_width * 6);
500
501   /* Solid border inside the logo box. */
502   XSetForeground (si->dpy, gc1, pw->foreground);
503   XDrawRectangle (si->dpy, si->passwd_dialog, gc1, x1, y1, x2-1, y2-1);
504
505   /* The shadow around the logo
506    */
507   draw_shaded_rectangle (si->dpy, si->passwd_dialog,
508                          pw->shadow_width * 2,
509                          pw->shadow_width * 2,
510                          pw->logo_width - (pw->shadow_width * 4),
511                          pw->logo_height - (pw->shadow_width * 4),
512                          pw->shadow_width,
513                          pw->shadow_bottom, pw->shadow_top);
514
515   /* The shadow around the thermometer
516    */
517   draw_shaded_rectangle (si->dpy, si->passwd_dialog,
518                          pw->logo_width,
519                          pw->shadow_width * 2,
520                          pw->thermo_width + (pw->shadow_width * 2),
521                          pw->height - (pw->shadow_width * 4),
522                          pw->shadow_width,
523                          pw->shadow_bottom, pw->shadow_top);
524
525   /* Solid border inside the thermometer. */
526   XSetForeground (si->dpy, gc1, pw->foreground);
527   XDrawRectangle (si->dpy, si->passwd_dialog, gc1, 
528                   pw->logo_width + pw->shadow_width,
529                   pw->shadow_width * 3,
530                   pw->thermo_width - 1,
531                   pw->height - (pw->shadow_width * 6) - 1);
532
533   /* The shadow around the whole window
534    */
535   draw_shaded_rectangle (si->dpy, si->passwd_dialog,
536                          0, 0, pw->width, pw->height, pw->shadow_width,
537                          pw->shadow_top, pw->shadow_bottom);
538
539   XFreeGC (si->dpy, gc1);
540   XFreeGC (si->dpy, gc2);
541
542   update_passwd_window (si, pw->passwd_string, pw->ratio);
543 }
544
545
546 void
547 update_passwd_window (saver_info *si, const char *printed_passwd, float ratio)
548 {
549   passwd_dialog_data *pw = si->pw_data;
550   XGCValues gcv;
551   GC gc1, gc2;
552   int x, y;
553
554   pw->ratio = ratio;
555   gcv.foreground = pw->passwd_foreground;
556   gcv.font = pw->passwd_font->fid;
557   gc1 = XCreateGC (si->dpy, si->passwd_dialog, GCForeground|GCFont, &gcv);
558   gcv.foreground = pw->passwd_background;
559   gc2 = XCreateGC (si->dpy, si->passwd_dialog, GCForeground, &gcv);
560
561   if (printed_passwd)
562     {
563       char *s = strdup (printed_passwd);
564       if (pw->passwd_string) free (pw->passwd_string);
565       pw->passwd_string = s;
566     }
567
568   /* the "password" text field
569    */
570   XFillRectangle (si->dpy, si->passwd_dialog, gc2,
571                   pw->passwd_field_x, pw->passwd_field_y,
572                   pw->passwd_field_width, pw->passwd_field_height);
573   XDrawString (si->dpy, si->passwd_dialog, gc1,
574                pw->passwd_field_x + pw->shadow_width,
575                pw->passwd_field_y + (pw->passwd_font->ascent +
576                                      pw->passwd_font->descent),
577                pw->passwd_string, strlen(pw->passwd_string));
578
579   /* The I-beam
580    */
581   if (pw->i_beam != 0)
582     {
583       x = (pw->passwd_field_x + pw->shadow_width +
584            string_width (pw->passwd_font, pw->passwd_string));
585       y = pw->passwd_field_y + pw->shadow_width;
586       XDrawLine (si->dpy, si->passwd_dialog, gc1, 
587                  x, y, x, y + pw->passwd_font->ascent);
588     }
589
590   pw->i_beam = (pw->i_beam + 1) % 4;
591
592
593   /* the thermometer
594    */
595   y = pw->thermo_field_height * (1.0 - pw->ratio);
596   if (y > 0)
597     {
598       XFillRectangle (si->dpy, si->passwd_dialog, gc2,
599                       pw->thermo_field_x + 1,
600                       pw->thermo_field_y + 1,
601                       pw->thermo_width-2,
602                       y);
603       XSetForeground (si->dpy, gc1, pw->logo_foreground);
604       XFillRectangle (si->dpy, si->passwd_dialog, gc1,
605                       pw->thermo_field_x + 1,
606                       pw->thermo_field_y + 1 + y,
607                       pw->thermo_width-2,
608                       MAX (0, pw->thermo_field_height - y - 2));
609     }
610
611   XFreeGC (si->dpy, gc1);
612   XFreeGC (si->dpy, gc2);
613   XSync (si->dpy, False);
614 }
615
616
617 void
618 destroy_passwd_window (saver_info *si)
619 {
620   passwd_dialog_data *pw = si->pw_data;
621   Screen *screen = si->default_screen->screen;
622   Colormap cmap = DefaultColormapOfScreen (screen);
623   Pixel black = BlackPixelOfScreen (screen);
624   Pixel white = WhitePixelOfScreen (screen);
625
626   if (pw->timer)
627     XtRemoveTimeOut (pw->timer);
628
629   if (si->passwd_dialog)
630     {
631       XDestroyWindow (si->dpy, si->passwd_dialog);
632       si->passwd_dialog = 0;
633     }
634   
635   if (pw->save_under)
636     {
637       XGCValues gcv;
638       GC gc;
639       gcv.function = GXcopy;
640       gc = XCreateGC (si->dpy, si->default_screen->screensaver_window,
641                       GCFunction, &gcv);
642       XCopyArea (si->dpy, pw->save_under,
643                  si->default_screen->screensaver_window, gc,
644                  0, 0,
645                  pw->width + (pw->border_width*2) + 1,
646                  pw->height + (pw->border_width*2) + 1,
647                  pw->x - pw->border_width, pw->y - pw->border_width);
648       XFreePixmap (si->dpy, pw->save_under);
649       pw->save_under = 0;
650       XFreeGC (si->dpy, gc);
651     }
652
653   if (pw->heading_label) free (pw->heading_label);
654   if (pw->body_label)    free (pw->body_label);
655   if (pw->user_label)    free (pw->user_label);
656   if (pw->passwd_label)  free (pw->passwd_label);
657
658   if (pw->heading_font) XFreeFont (si->dpy, pw->heading_font);
659   if (pw->body_font)    XFreeFont (si->dpy, pw->body_font);
660   if (pw->label_font)   XFreeFont (si->dpy, pw->label_font);
661   if (pw->passwd_font)  XFreeFont (si->dpy, pw->passwd_font);
662
663   if (pw->foreground != black && pw->foreground != white)
664     XFreeColors (si->dpy, cmap, &pw->foreground, 1, 0L);
665   if (pw->background != black && pw->background != white)
666     XFreeColors (si->dpy, cmap, &pw->background, 1, 0L);
667   if (pw->passwd_foreground != black && pw->passwd_foreground != white)
668     XFreeColors (si->dpy, cmap, &pw->passwd_foreground, 1, 0L);
669   if (pw->passwd_background != black && pw->passwd_background != white)
670     XFreeColors (si->dpy, cmap, &pw->passwd_background, 1, 0L);
671   if (pw->logo_foreground != black && pw->logo_foreground != white)
672     XFreeColors (si->dpy, cmap, &pw->logo_foreground, 1, 0L);
673   if (pw->logo_background != black && pw->logo_background != white)
674     XFreeColors (si->dpy, cmap, &pw->logo_background, 1, 0L);
675   if (pw->shadow_top != black && pw->shadow_top != white)
676     XFreeColors (si->dpy, cmap, &pw->shadow_top, 1, 0L);
677   if (pw->shadow_bottom != black && pw->shadow_bottom != white)
678     XFreeColors (si->dpy, cmap, &pw->shadow_bottom, 1, 0L);
679
680   memset (pw, 0, sizeof(*pw));
681   free (pw);
682
683   si->pw_data = 0;
684 }
685
686 \f
687 /* Interactions
688  */
689
690 static void
691 passwd_animate_timer (XtPointer closure, XtIntervalId *id)
692 {
693   saver_info *si = (saver_info *) closure;
694   int tick = 166;
695   passwd_dialog_data *pw = si->pw_data;
696
697   if (!pw) return;
698
699   pw->ratio -= (1.0 / ((double) si->prefs.passwd_timeout / (double) tick));
700   if (pw->ratio < 0)
701     {
702       pw->ratio = 0;
703       if (pw->state == pw_read)
704         pw->state = pw_time;
705     }
706
707   update_passwd_window (si, 0, pw->ratio);
708
709   if (pw->state == pw_read)
710     pw->timer = XtAppAddTimeOut (si->app, tick, passwd_animate_timer,
711                                  (XtPointer) si);
712   else
713     pw->timer = 0;
714
715   idle_timer ((XtPointer) si, id);
716 }
717
718
719 static void
720 handle_passwd_key (saver_info *si, XKeyEvent *event)
721 {
722   saver_preferences *p = &si->prefs;
723   passwd_dialog_data *pw = si->pw_data;
724   int pw_size = sizeof (pw->typed_passwd) - 1;
725   char *typed_passwd = pw->typed_passwd;
726   char s[2];
727   char *stars = 0;
728   int i;
729   int size = XLookupString (event, s, 1, 0, 0);
730
731   if (size != 1) return;
732
733   s[1] = 0;
734
735   switch (*s)
736     {
737     case '\010': case '\177':                           /* Backspace */
738       if (!*typed_passwd)
739         XBell (si->dpy, 0);
740       else
741         typed_passwd [strlen(typed_passwd)-1] = 0;
742       break;
743
744     case '\025': case '\030':                           /* Erase line */
745       memset (typed_passwd, 0, pw_size);
746       break;
747
748     case '\012': case '\015':                           /* Enter */
749       if (pw->state != pw_read)
750         ;  /* already done? */
751       else if (typed_passwd[0] == 0)
752         pw->state = pw_null;
753       else
754         {
755           update_passwd_window (si, "Checking...", pw->ratio);
756           XSync (si->dpy, False);
757           if (passwd_valid_p (typed_passwd, p->verbose_p))
758             pw->state = pw_ok;
759           else
760             pw->state = pw_fail;
761           update_passwd_window (si, "", pw->ratio);
762         }
763       break;
764
765     default:
766       i = strlen (typed_passwd);
767       if (i >= pw_size-1)
768         XBell (si->dpy, 0);
769       else
770         {
771           typed_passwd [i] = *s;
772           typed_passwd [i+1] = 0;
773         }
774       break;
775     }
776
777   i = strlen(typed_passwd);
778   stars = (char *) malloc(i+1);
779   memset (stars, '*', i);
780   stars[i] = 0;
781   update_passwd_window (si, stars, pw->ratio);
782   free (stars);
783 }
784
785
786 static void
787 passwd_event_loop (saver_info *si)
788 {
789   saver_preferences *p = &si->prefs;
790   char *msg = 0;
791   XEvent event;
792   passwd_animate_timer ((XtPointer) si, 0);
793
794   while (si->pw_data && si->pw_data->state == pw_read)
795     {
796       XtAppNextEvent (si->app, &event);
797       if (event.xany.window == si->passwd_dialog && event.xany.type == Expose)
798         draw_passwd_window (si);
799       else if (event.xany.type == KeyPress)
800         handle_passwd_key (si, &event.xkey);
801       else
802         XtDispatchEvent (&event);
803     }
804
805   switch (si->pw_data->state)
806     {
807     case pw_ok:   msg = 0; break;
808     case pw_null: msg = ""; break;
809     case pw_time: msg = "Timed out!"; break;
810     default:      msg = "Sorry!"; break;
811     }
812
813   if (si->pw_data->state == pw_fail)
814     si->unlock_failures++;
815
816   if (p->verbose_p)
817     switch (si->pw_data->state)
818       {
819       case pw_ok:
820         fprintf (stderr, "%s: password correct.\n", blurb()); break;
821       case pw_fail:
822         fprintf (stderr, "%s: password incorrect!\n", blurb()); break;
823       case pw_null:
824       case pw_cancel:
825         fprintf (stderr, "%s: password entry cancelled.\n", blurb()); break;
826       case pw_time:
827         fprintf (stderr, "%s: password entry timed out.\n", blurb()); break;
828       default: break;
829       }
830
831 #ifdef HAVE_SYSLOG
832   if (si->pw_data->state == pw_fail)
833     {
834       /* If they typed a password (as opposed to just hitting return) and
835          the password was invalid, log it.
836       */
837       struct passwd *pw = getpwuid (getuid ());
838       char *d = DisplayString (si->dpy);
839       char *u = (pw->pw_name ? pw->pw_name : "???");
840       int opt = 0;
841       int fac = 0;
842
843 # ifdef LOG_PID
844       opt = LOG_PID;
845 # endif
846
847 # if defined(LOG_AUTHPRIV)
848       fac = LOG_AUTHPRIV;
849 # elif defined(LOG_AUTH)
850       fac = LOG_AUTH;
851 # else
852       fac = LOG_DAEMON;
853 # endif
854
855       if (!d) d = "";
856       openlog (progname, opt, fac);
857       syslog (LOG_NOTICE, "FAILED LOGIN %d ON DISPLAY \"%s\", FOR \"%s\"",
858               si->unlock_failures, d, u);
859       closelog ();
860     }
861 #endif /* HAVE_SYSLOG */
862
863   if (si->pw_data->state == pw_fail)
864     XBell (si->dpy, False);
865
866   if (si->pw_data->state == pw_ok && si->unlock_failures != 0)
867     {
868       if (si->unlock_failures == 1)
869         fprintf (real_stderr,
870                  "%s: WARNING: 1 failed attempt to unlock the screen.\n",
871                  blurb());
872       else
873         fprintf (real_stderr,
874                  "%s: WARNING: %d failed attempts to unlock the screen.\n",
875                  blurb(), si->unlock_failures);
876       fflush (real_stderr);
877
878       si->unlock_failures = 0;
879     }
880
881   if (msg)
882     {
883       si->pw_data->i_beam = 0;
884       update_passwd_window (si, msg, 0.0);
885       XSync (si->dpy, False);
886       sleep (1);
887
888       /* Swallow all pending KeyPress/KeyRelease events. */
889       {
890         XEvent e;
891         while (XCheckMaskEvent (si->dpy, KeyPressMask|KeyReleaseMask, &e))
892           ;
893       }
894     }
895 }
896
897
898 Bool
899 unlock_p (saver_info *si)
900 {
901   saver_preferences *p = &si->prefs;
902   Screen *screen = si->default_screen->screen;
903   Colormap cmap = DefaultColormapOfScreen (screen);
904   Bool status;
905
906   if (p->verbose_p)
907     fprintf (stderr, "%s: prompting for password.\n", blurb());
908
909   if (si->pw_data || si->passwd_dialog)
910     destroy_passwd_window (si);
911
912   make_passwd_window (si);
913   if (cmap) XInstallColormap (si->dpy, cmap);
914
915   passwd_event_loop (si);
916
917   status = (si->pw_data->state == pw_ok);
918   destroy_passwd_window (si);
919
920   cmap = si->default_screen->cmap;
921   if (cmap) XInstallColormap (si->dpy, cmap);
922
923   return status;
924 }
925
926 #endif /* !NO_LOCKING -- whole file */