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