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