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