http://ftp.x.org/contrib/applications/xscreensaver-3.20.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->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 void
989 handle_passwd_key (saver_info *si, XKeyEvent *event)
990 {
991   saver_preferences *p = &si->prefs;
992   passwd_dialog_data *pw = si->pw_data;
993   int pw_size = sizeof (pw->typed_passwd) - 1;
994   char *typed_passwd = pw->typed_passwd;
995   char s[2];
996   char *stars = 0;
997   int i;
998   int size = XLookupString (event, s, 1, 0, 0);
999
1000   if (size != 1) return;
1001
1002   s[1] = 0;
1003
1004   switch (*s)
1005     {
1006     case '\010': case '\177':                           /* Backspace */
1007       if (!*typed_passwd)
1008         XBell (si->dpy, 0);
1009       else
1010         typed_passwd [strlen(typed_passwd)-1] = 0;
1011       break;
1012
1013     case '\025': case '\030':                           /* Erase line */
1014       memset (typed_passwd, 0, pw_size);
1015       break;
1016
1017     case '\012': case '\015':                           /* Enter */
1018       if (pw->state != pw_read)
1019         ;  /* already done? */
1020       else if (typed_passwd[0] == 0)
1021         pw->state = pw_null;
1022       else
1023         {
1024           update_passwd_window (si, "Checking...", pw->ratio);
1025           XSync (si->dpy, False);
1026           if (passwd_valid_p (typed_passwd, p->verbose_p))
1027             pw->state = pw_ok;
1028           else
1029             pw->state = pw_fail;
1030           update_passwd_window (si, "", pw->ratio);
1031         }
1032       break;
1033
1034     default:
1035       i = strlen (typed_passwd);
1036       if (i >= pw_size-1)
1037         XBell (si->dpy, 0);
1038       else
1039         {
1040           typed_passwd [i] = *s;
1041           typed_passwd [i+1] = 0;
1042         }
1043       break;
1044     }
1045
1046   i = strlen(typed_passwd);
1047   stars = (char *) malloc(i+1);
1048   memset (stars, '*', i);
1049   stars[i] = 0;
1050   update_passwd_window (si, stars, pw->ratio);
1051   free (stars);
1052 }
1053
1054
1055 static void
1056 passwd_event_loop (saver_info *si)
1057 {
1058   saver_preferences *p = &si->prefs;
1059   char *msg = 0;
1060   XEvent event;
1061   passwd_animate_timer ((XtPointer) si, 0);
1062
1063   while (si->pw_data && si->pw_data->state == pw_read)
1064     {
1065       XtAppNextEvent (si->app, &event);
1066       if (event.xany.window == si->passwd_dialog && event.xany.type == Expose)
1067         draw_passwd_window (si);
1068       else if (event.xany.type == KeyPress)
1069         handle_passwd_key (si, &event.xkey);
1070       else
1071         XtDispatchEvent (&event);
1072     }
1073
1074   switch (si->pw_data->state)
1075     {
1076     case pw_ok:   msg = 0; break;
1077     case pw_null: msg = ""; break;
1078     case pw_time: msg = "Timed out!"; break;
1079     default:      msg = "Sorry!"; break;
1080     }
1081
1082   if (si->pw_data->state == pw_fail)
1083     si->unlock_failures++;
1084
1085   if (p->verbose_p)
1086     switch (si->pw_data->state)
1087       {
1088       case pw_ok:
1089         fprintf (stderr, "%s: password correct.\n", blurb()); break;
1090       case pw_fail:
1091         fprintf (stderr, "%s: password incorrect!\n", blurb()); break;
1092       case pw_null:
1093       case pw_cancel:
1094         fprintf (stderr, "%s: password entry cancelled.\n", blurb()); break;
1095       case pw_time:
1096         fprintf (stderr, "%s: password entry timed out.\n", blurb()); break;
1097       default: break;
1098       }
1099
1100 #ifdef HAVE_SYSLOG
1101   if (si->pw_data->state == pw_fail)
1102     {
1103       /* If they typed a password (as opposed to just hitting return) and
1104          the password was invalid, log it.
1105       */
1106       struct passwd *pw = getpwuid (getuid ());
1107       char *d = DisplayString (si->dpy);
1108       char *u = (pw->pw_name ? pw->pw_name : "???");
1109       int opt = 0;
1110       int fac = 0;
1111
1112 # ifdef LOG_PID
1113       opt = LOG_PID;
1114 # endif
1115
1116 # if defined(LOG_AUTHPRIV)
1117       fac = LOG_AUTHPRIV;
1118 # elif defined(LOG_AUTH)
1119       fac = LOG_AUTH;
1120 # else
1121       fac = LOG_DAEMON;
1122 # endif
1123
1124       if (!d) d = "";
1125       openlog (progname, opt, fac);
1126       syslog (LOG_NOTICE, "FAILED LOGIN %d ON DISPLAY \"%s\", FOR \"%s\"",
1127               si->unlock_failures, d, u);
1128       closelog ();
1129     }
1130 #endif /* HAVE_SYSLOG */
1131
1132   if (si->pw_data->state == pw_fail)
1133     XBell (si->dpy, False);
1134
1135   if (si->pw_data->state == pw_ok && si->unlock_failures != 0)
1136     {
1137       if (si->unlock_failures == 1)
1138         fprintf (real_stderr,
1139                  "%s: WARNING: 1 failed attempt to unlock the screen.\n",
1140                  blurb());
1141       else
1142         fprintf (real_stderr,
1143                  "%s: WARNING: %d failed attempts to unlock the screen.\n",
1144                  blurb(), si->unlock_failures);
1145       fflush (real_stderr);
1146
1147       si->unlock_failures = 0;
1148     }
1149
1150   if (msg)
1151     {
1152       si->pw_data->i_beam = 0;
1153       update_passwd_window (si, msg, 0.0);
1154       XSync (si->dpy, False);
1155       sleep (1);
1156
1157       /* Swallow all pending KeyPress/KeyRelease events. */
1158       {
1159         XEvent e;
1160         while (XCheckMaskEvent (si->dpy, KeyPressMask|KeyReleaseMask, &e))
1161           ;
1162       }
1163     }
1164 }
1165
1166
1167 static void
1168 handle_typeahead (saver_info *si)
1169 {
1170   passwd_dialog_data *pw = si->pw_data;
1171   int i;
1172   if (!si->unlock_typeahead)
1173     return;
1174
1175   i = strlen (si->unlock_typeahead);
1176   if (i >= sizeof(pw->typed_passwd) - 1)
1177     i = sizeof(pw->typed_passwd) - 1;
1178
1179   memcpy (pw->typed_passwd, si->unlock_typeahead, i);
1180   pw->typed_passwd [i] = 0;
1181
1182   memset (si->unlock_typeahead, '*', strlen(si->unlock_typeahead));
1183   si->unlock_typeahead[i] = 0;
1184   update_passwd_window (si, si->unlock_typeahead, pw->ratio);
1185
1186   free (si->unlock_typeahead);
1187   si->unlock_typeahead = 0;
1188 }
1189
1190
1191 Bool
1192 unlock_p (saver_info *si)
1193 {
1194   saver_preferences *p = &si->prefs;
1195   Screen *screen = si->default_screen->screen;
1196   Colormap cmap = DefaultColormapOfScreen (screen);
1197   Bool status;
1198
1199   raise_window (si, True, True, True);
1200
1201   if (p->verbose_p)
1202     fprintf (stderr, "%s: prompting for password.\n", blurb());
1203
1204   if (si->pw_data || si->passwd_dialog)
1205     destroy_passwd_window (si);
1206
1207   make_passwd_window (si);
1208   if (cmap) XInstallColormap (si->dpy, cmap);
1209
1210   handle_typeahead (si);
1211   passwd_event_loop (si);
1212
1213   status = (si->pw_data->state == pw_ok);
1214   destroy_passwd_window (si);
1215
1216   cmap = si->default_screen->cmap;
1217   if (cmap) XInstallColormap (si->dpy, cmap);
1218
1219   return status;
1220 }
1221
1222
1223 void
1224 set_locked_p (saver_info *si, Bool locked_p)
1225 {
1226   si->locked_p = locked_p;
1227
1228 #ifdef HAVE_XHPDISABLERESET
1229   hp_lock_reset (si, locked_p);                 /* turn off/on C-Sh-Reset */
1230 #endif
1231 #ifdef HAVE_VT_LOCKSWITCH
1232   linux_lock_vt_switch (si, locked_p);          /* turn off/on C-Alt-F1 */
1233 #endif
1234 #ifdef HAVE_XF86VMODE
1235   xfree_lock_mode_switch (si, locked_p);        /* turn off/on C-Alt-Plus */
1236 #endif
1237
1238   store_saver_status (si);                      /* store locked-p */
1239 }
1240
1241
1242 #else  /*  NO_LOCKING -- whole file */
1243
1244 void
1245 set_locked_p (saver_info *si, Bool locked_p)
1246 {
1247   if (locked_p) abort();
1248 }
1249
1250 #endif /* !NO_LOCKING */