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