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