2c766b55ed3291c8d3edd1e24c48cbf4f66ba590
[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   x1 = pw->shadow_width * 3;
542   y1 = pw->shadow_width * 3;
543   x2 = pw->logo_width - (pw->shadow_width * 6);
544   y2 = pw->logo_height - (pw->shadow_width * 6);
545
546   draw_logo (si, si->passwd_dialog, x1, y1, x2, y2, True);
547
548   /* The thermometer
549    */
550   pw->thermo_field_x = pw->logo_width + pw->shadow_width;
551   pw->thermo_field_y = pw->shadow_width * 3;
552   pw->thermo_field_height = pw->height - (pw->shadow_width * 6);
553
554   /* Solid border inside the logo box. */
555   XSetForeground (si->dpy, gc1, pw->foreground);
556   XDrawRectangle (si->dpy, si->passwd_dialog, gc1, x1, y1, x2-1, y2-1);
557
558   /* The shadow around the logo
559    */
560   draw_shaded_rectangle (si->dpy, si->passwd_dialog,
561                          pw->shadow_width * 2,
562                          pw->shadow_width * 2,
563                          pw->logo_width - (pw->shadow_width * 4),
564                          pw->logo_height - (pw->shadow_width * 4),
565                          pw->shadow_width,
566                          pw->shadow_bottom, pw->shadow_top);
567
568   /* The shadow around the thermometer
569    */
570   draw_shaded_rectangle (si->dpy, si->passwd_dialog,
571                          pw->logo_width,
572                          pw->shadow_width * 2,
573                          pw->thermo_width + (pw->shadow_width * 2),
574                          pw->height - (pw->shadow_width * 4),
575                          pw->shadow_width,
576                          pw->shadow_bottom, pw->shadow_top);
577
578   /* Solid border inside the thermometer. */
579   XSetForeground (si->dpy, gc1, pw->foreground);
580   XDrawRectangle (si->dpy, si->passwd_dialog, gc1, 
581                   pw->logo_width + pw->shadow_width,
582                   pw->shadow_width * 3,
583                   pw->thermo_width - 1,
584                   pw->height - (pw->shadow_width * 6) - 1);
585
586   /* The shadow around the whole window
587    */
588   draw_shaded_rectangle (si->dpy, si->passwd_dialog,
589                          0, 0, pw->width, pw->height, pw->shadow_width,
590                          pw->shadow_top, pw->shadow_bottom);
591
592   XFreeGC (si->dpy, gc1);
593   XFreeGC (si->dpy, gc2);
594
595   update_passwd_window (si, pw->passwd_string, pw->ratio);
596 }
597
598
599 static void
600 update_passwd_window (saver_info *si, const char *printed_passwd, float ratio)
601 {
602   passwd_dialog_data *pw = si->pw_data;
603   XGCValues gcv;
604   GC gc1, gc2;
605   int x, y;
606   XRectangle rects[1];
607
608   pw->ratio = ratio;
609   gcv.foreground = pw->passwd_foreground;
610   gcv.font = pw->passwd_font->fid;
611   gc1 = XCreateGC (si->dpy, si->passwd_dialog, GCForeground|GCFont, &gcv);
612   gcv.foreground = pw->passwd_background;
613   gc2 = XCreateGC (si->dpy, si->passwd_dialog, GCForeground, &gcv);
614
615   if (printed_passwd)
616     {
617       char *s = strdup (printed_passwd);
618       if (pw->passwd_string) free (pw->passwd_string);
619       pw->passwd_string = s;
620     }
621
622   /* the "password" text field
623    */
624   rects[0].x =  pw->passwd_field_x;
625   rects[0].y =  pw->passwd_field_y;
626   rects[0].width = pw->passwd_field_width;
627   rects[0].height = pw->passwd_field_height;
628
629   XFillRectangle (si->dpy, si->passwd_dialog, gc2,
630                   rects[0].x, rects[0].y, rects[0].width, rects[0].height);
631
632   XSetClipRectangles (si->dpy, gc1, 0, 0, rects, 1, Unsorted);
633
634   XDrawString (si->dpy, si->passwd_dialog, gc1,
635                rects[0].x + pw->shadow_width,
636                rects[0].y + (pw->passwd_font->ascent +
637                              pw->passwd_font->descent),
638                pw->passwd_string, strlen(pw->passwd_string));
639
640   XSetClipMask (si->dpy, gc1, None);
641
642   /* The I-beam
643    */
644   if (pw->i_beam != 0)
645     {
646       x = (rects[0].x + pw->shadow_width +
647            string_width (pw->passwd_font, pw->passwd_string));
648       y = rects[0].y + pw->shadow_width;
649
650       if (x > rects[0].x + rects[0].width - 1)
651         x = rects[0].x + rects[0].width - 1;
652       XDrawLine (si->dpy, si->passwd_dialog, gc1, 
653                  x, y, x, y + pw->passwd_font->ascent);
654     }
655
656   pw->i_beam = (pw->i_beam + 1) % 4;
657
658
659   /* the thermometer
660    */
661   y = pw->thermo_field_height * (1.0 - pw->ratio);
662   if (y > 0)
663     {
664       XFillRectangle (si->dpy, si->passwd_dialog, gc2,
665                       pw->thermo_field_x + 1,
666                       pw->thermo_field_y + 1,
667                       pw->thermo_width-2,
668                       y);
669       XSetForeground (si->dpy, gc1, pw->logo_foreground);
670       XFillRectangle (si->dpy, si->passwd_dialog, gc1,
671                       pw->thermo_field_x + 1,
672                       pw->thermo_field_y + 1 + y,
673                       pw->thermo_width-2,
674                       MAX (0, pw->thermo_field_height - y - 2));
675     }
676
677   /* the logo
678    */
679   {
680     int x1, y1, x2, y2;
681     x1 = pw->shadow_width * 3;
682     y1 = pw->shadow_width * 3;
683     x2 = pw->logo_width - (pw->shadow_width * 6);
684     y2 = pw->logo_height - (pw->shadow_width * 6);
685
686     draw_logo (si, si->passwd_dialog, x1, y1, x2, y2, False);
687   }
688
689   XFreeGC (si->dpy, gc1);
690   XFreeGC (si->dpy, gc2);
691   XSync (si->dpy, False);
692 }
693
694
695 static void
696 destroy_passwd_window (saver_info *si)
697 {
698   passwd_dialog_data *pw = si->pw_data;
699   Screen *screen = si->default_screen->screen;
700   Colormap cmap = DefaultColormapOfScreen (screen);
701   Pixel black = BlackPixelOfScreen (screen);
702   Pixel white = WhitePixelOfScreen (screen);
703
704   if (pw->timer)
705     XtRemoveTimeOut (pw->timer);
706
707   move_mouse_grab (si, RootWindowOfScreen(si->screens[0].screen),
708                    si->screens[0].cursor);
709
710   if (si->passwd_dialog)
711     {
712       XDestroyWindow (si->dpy, si->passwd_dialog);
713       si->passwd_dialog = 0;
714     }
715   
716   if (pw->save_under)
717     {
718       XGCValues gcv;
719       GC gc;
720       gcv.function = GXcopy;
721       gc = XCreateGC (si->dpy, si->default_screen->screensaver_window,
722                       GCFunction, &gcv);
723       XCopyArea (si->dpy, pw->save_under,
724                  si->default_screen->screensaver_window, gc,
725                  0, 0,
726                  pw->width + (pw->border_width*2) + 1,
727                  pw->height + (pw->border_width*2) + 1,
728                  pw->x - pw->border_width, pw->y - pw->border_width);
729       XFreePixmap (si->dpy, pw->save_under);
730       pw->save_under = 0;
731       XFreeGC (si->dpy, gc);
732     }
733
734   if (pw->heading_label) free (pw->heading_label);
735   if (pw->body_label)    free (pw->body_label);
736   if (pw->user_label)    free (pw->user_label);
737   if (pw->passwd_label)  free (pw->passwd_label);
738
739   if (pw->heading_font) XFreeFont (si->dpy, pw->heading_font);
740   if (pw->body_font)    XFreeFont (si->dpy, pw->body_font);
741   if (pw->label_font)   XFreeFont (si->dpy, pw->label_font);
742   if (pw->passwd_font)  XFreeFont (si->dpy, pw->passwd_font);
743
744   if (pw->foreground != black && pw->foreground != white)
745     XFreeColors (si->dpy, cmap, &pw->foreground, 1, 0L);
746   if (pw->background != black && pw->background != white)
747     XFreeColors (si->dpy, cmap, &pw->background, 1, 0L);
748   if (pw->passwd_foreground != black && pw->passwd_foreground != white)
749     XFreeColors (si->dpy, cmap, &pw->passwd_foreground, 1, 0L);
750   if (pw->passwd_background != black && pw->passwd_background != white)
751     XFreeColors (si->dpy, cmap, &pw->passwd_background, 1, 0L);
752   if (pw->logo_foreground != black && pw->logo_foreground != white)
753     XFreeColors (si->dpy, cmap, &pw->logo_foreground, 1, 0L);
754   if (pw->logo_background != black && pw->logo_background != white)
755     XFreeColors (si->dpy, cmap, &pw->logo_background, 1, 0L);
756   if (pw->shadow_top != black && pw->shadow_top != white)
757     XFreeColors (si->dpy, cmap, &pw->shadow_top, 1, 0L);
758   if (pw->shadow_bottom != black && pw->shadow_bottom != white)
759     XFreeColors (si->dpy, cmap, &pw->shadow_bottom, 1, 0L);
760
761   memset (pw, 0, sizeof(*pw));
762   free (pw);
763
764   si->pw_data = 0;
765 }
766
767
768 #ifdef HAVE_XHPDISABLERESET
769 /* This function enables and disables the C-Sh-Reset hot-key, which
770    normally resets the X server (logging out the logged-in user.)
771    We don't want random people to be able to do that while the
772    screen is locked.
773  */
774 static void
775 hp_lock_reset (saver_info *si, Bool lock_p)
776 {
777   static Bool hp_locked_p = False;
778
779   /* Calls to XHPDisableReset and XHPEnableReset must be balanced,
780      or BadAccess errors occur.  (It's ok for this to be global,
781      since it affects the whole machine, not just the current screen.)
782   */
783   if (hp_locked_p == lock_p)
784     return;
785
786   if (lock_p)
787     XHPDisableReset (si->dpy);
788   else
789     XHPEnableReset (si->dpy);
790   hp_locked_p = lock_p;
791 }
792 #endif /* HAVE_XHPDISABLERESET */
793
794
795 \f
796 /* This function enables and disables the C-Sh-F1 ... F12 hot-keys,
797    which, on Linux systems, switches to another virtual console.
798    We'd like the whole screen/keyboard to be locked, not just one
799    virtual console, so this function disables that while the X
800    screen is locked.
801
802    Unfortunately, this doesn't work -- this ioctl only works when
803    called by root, and we have disavowed our privileges long ago.
804  */
805 #ifdef HAVE_VT_LOCKSWITCH
806 static void
807 linux_lock_vt_switch (saver_info *si, Bool lock_p)
808 {
809   saver_preferences *p = &si->prefs;
810   static Bool vt_locked_p = False;
811   const char *dev_console = "/dev/console";
812   int fd;
813
814   if (lock_p == vt_locked_p)
815     return;
816
817   if (lock_p && !p->lock_vt_p)
818     return;
819
820   fd = open (dev_console, O_RDWR);
821   if (fd < 0)
822     {
823       char buf [255];
824       sprintf (buf, "%s: couldn't %s VTs: %s", blurb(),
825                (lock_p ? "lock" : "unlock"),
826                dev_console);
827 #if 1 /* #### doesn't work yet, so don't bother complaining */
828       perror (buf);
829 #endif
830       return;
831     }
832
833   if (ioctl (fd, (lock_p ? VT_LOCKSWITCH : VT_UNLOCKSWITCH)) == 0)
834     {
835       vt_locked_p = lock_p;
836
837       if (p->verbose_p)
838         fprintf (stderr, "%s: %s VTs\n", blurb(),
839                  (lock_p ? "locked" : "unlocked"));
840     }
841   else
842     {
843       char buf [255];
844       sprintf (buf, "%s: couldn't %s VTs: ioctl", blurb(),
845                (lock_p ? "lock" : "unlock"));
846 #if 0 /* #### doesn't work yet, so don't bother complaining */
847       perror (buf);
848 #endif
849     }
850
851   close (fd);
852 }
853 #endif /* HAVE_VT_LOCKSWITCH */
854
855 \f
856 /* This function enables and disables the C-Alt-Plus and C-Alt-Minus
857    hot-keys, which normally change the resolution of the X server.
858    We don't want people to be able to switch the server resolution
859    while the screen is locked, because if they switch to a higher
860    resolution, it could cause part of the underlying desktop to become
861    exposed.
862  */
863 #ifdef HAVE_XF86VMODE
864
865 static int ignore_all_errors_ehandler (Display *dpy, XErrorEvent *error);
866 static Bool vp_got_error = False;
867
868 static void
869 xfree_lock_mode_switch (saver_info *si, Bool lock_p)
870 {
871   static Bool mode_locked_p = False;
872   saver_preferences *p = &si->prefs;
873   int screen = 0;  /* always screen 0 */
874   int event, error;
875   Bool status;
876   XErrorHandler old_handler;
877
878   if (mode_locked_p == lock_p)
879     return;
880   if (!XF86VidModeQueryExtension (si->dpy, &event, &error))
881     return;
882
883   XSync (si->dpy, False);
884   old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
885   status = XF86VidModeLockModeSwitch (si->dpy, screen, lock_p);
886   XSync (si->dpy, False);
887   XSetErrorHandler (old_handler);
888   if (vp_got_error) status = False;
889
890   if (status)
891     mode_locked_p = lock_p;
892
893   if (!status && (p->verbose_p || !lock_p))
894     /* Only print this when verbose, or when we locked but can't unlock.
895        I tried printing this message whenever it comes up, but
896        mode-locking always fails if DontZoom is set in XF86Config. */
897     fprintf (stderr, "%s: unable to %s mode switching!\n",
898              blurb(), (lock_p ? "lock" : "unlock"));
899   else if (p->verbose_p)
900     fprintf (stderr, "%s: %s mode switching.\n",
901              blurb(), (lock_p ? "locked" : "unlocked"));
902 }
903
904 static int
905 ignore_all_errors_ehandler (Display *dpy, XErrorEvent *error)
906 {
907   vp_got_error = True;
908   return 0;
909 }
910
911 #endif /* HAVE_XF86VMODE */
912
913 \f
914 /* If the viewport has been scrolled since the screen was blanked,
915    then scroll it back to where it belongs.  This function only exists
916    to patch over a very brief race condition.
917  */
918 static void
919 undo_vp_motion (saver_info *si)
920 {
921 #ifdef HAVE_XF86VMODE
922   saver_preferences *p = &si->prefs;
923   int screen = 0;  /* always screen 0 */
924   saver_screen_info *ssi = &si->screens[screen];
925   int event, error, x, y;
926   Bool status;
927
928   if (ssi->blank_vp_x == -1 && ssi->blank_vp_y == -1)
929     return;
930   if (!XF86VidModeQueryExtension (si->dpy, &event, &error))
931     return;
932   if (!XF86VidModeGetViewPort (si->dpy, 0, &x, &y))
933     return;
934   if (ssi->blank_vp_x == x && ssi->blank_vp_y == y)
935     return;
936     
937   /* We're going to move the viewport.  The mouse has just been grabbed on
938      (and constrained to, thus warped to) the password window, so it is no
939      longer near the edge of the screen.  However, wait a bit anyway, just
940      to make sure the server drains its last motion event, so that the
941      screen doesn't continue to scroll after we've reset the viewport.
942    */
943   XSync (si->dpy, False);
944   usleep (250000);  /* 1/4 second */
945   XSync (si->dpy, False);
946
947   status = XF86VidModeSetViewPort (si->dpy, screen,
948                                    ssi->blank_vp_x, ssi->blank_vp_y);
949
950   if (!status)
951     fprintf (stderr, "%s: unable to move vp from (%d,%d) back to (%d,%d)!\n",
952              blurb(), x, y, ssi->blank_vp_x, ssi->blank_vp_y);
953   else if (p->verbose_p)
954     fprintf (stderr, "%s: vp moved to (%d,%d); moved it back to (%d,%d).\n",
955              blurb(), x, y, ssi->blank_vp_x, ssi->blank_vp_y);
956
957 #endif /* HAVE_XF86VMODE */
958 }
959
960
961 \f
962 /* Interactions
963  */
964
965 static void
966 passwd_animate_timer (XtPointer closure, XtIntervalId *id)
967 {
968   saver_info *si = (saver_info *) closure;
969   int tick = 166;
970   passwd_dialog_data *pw = si->pw_data;
971
972   if (!pw) return;
973
974   pw->ratio -= (1.0 / ((double) si->prefs.passwd_timeout / (double) tick));
975   if (pw->ratio < 0)
976     {
977       pw->ratio = 0;
978       if (pw->state == pw_read)
979         pw->state = pw_time;
980     }
981
982   update_passwd_window (si, 0, pw->ratio);
983
984   if (pw->state == pw_read)
985     pw->timer = XtAppAddTimeOut (si->app, tick, passwd_animate_timer,
986                                  (XtPointer) si);
987   else
988     pw->timer = 0;
989
990   idle_timer ((XtPointer) si, id);
991 }
992
993
994 static XComposeStatus *compose_status;
995
996 static void
997 handle_passwd_key (saver_info *si, XKeyEvent *event)
998 {
999   saver_preferences *p = &si->prefs;
1000   passwd_dialog_data *pw = si->pw_data;
1001   int pw_size = sizeof (pw->typed_passwd) - 1;
1002   char *typed_passwd = pw->typed_passwd;
1003   char s[2];
1004   char *stars = 0;
1005   int i;
1006   int size = XLookupString (event, s, 1, 0, compose_status);
1007
1008   if (size != 1) return;
1009
1010   s[1] = 0;
1011
1012   switch (*s)
1013     {
1014     case '\010': case '\177':                           /* Backspace */
1015       if (!*typed_passwd)
1016         XBell (si->dpy, 0);
1017       else
1018         typed_passwd [strlen(typed_passwd)-1] = 0;
1019       break;
1020
1021     case '\025': case '\030':                           /* Erase line */
1022       memset (typed_passwd, 0, pw_size);
1023       break;
1024
1025     case '\012': case '\015':                           /* Enter */
1026       if (pw->state != pw_read)
1027         ;  /* already done? */
1028       else if (typed_passwd[0] == 0)
1029         pw->state = pw_null;
1030       else
1031         {
1032           update_passwd_window (si, "Checking...", pw->ratio);
1033           XSync (si->dpy, False);
1034           if (passwd_valid_p (typed_passwd, p->verbose_p))
1035             pw->state = pw_ok;
1036           else
1037             pw->state = pw_fail;
1038           update_passwd_window (si, "", pw->ratio);
1039         }
1040       break;
1041
1042     default:
1043       i = strlen (typed_passwd);
1044       if (i >= pw_size-1)
1045         XBell (si->dpy, 0);
1046       else
1047         {
1048           typed_passwd [i] = *s;
1049           typed_passwd [i+1] = 0;
1050         }
1051       break;
1052     }
1053
1054   i = strlen(typed_passwd);
1055   stars = (char *) malloc(i+1);
1056   memset (stars, '*', i);
1057   stars[i] = 0;
1058   update_passwd_window (si, stars, pw->ratio);
1059   free (stars);
1060 }
1061
1062
1063 static void
1064 passwd_event_loop (saver_info *si)
1065 {
1066   saver_preferences *p = &si->prefs;
1067   char *msg = 0;
1068   XEvent event;
1069   passwd_animate_timer ((XtPointer) si, 0);
1070
1071   while (si->pw_data && si->pw_data->state == pw_read)
1072     {
1073       XtAppNextEvent (si->app, &event);
1074       if (event.xany.window == si->passwd_dialog && event.xany.type == Expose)
1075         draw_passwd_window (si);
1076       else if (event.xany.type == KeyPress)
1077         handle_passwd_key (si, &event.xkey);
1078       else
1079         XtDispatchEvent (&event);
1080     }
1081
1082   switch (si->pw_data->state)
1083     {
1084     case pw_ok:   msg = 0; break;
1085     case pw_null: msg = ""; break;
1086     case pw_time: msg = "Timed out!"; break;
1087     default:      msg = "Sorry!"; break;
1088     }
1089
1090   if (si->pw_data->state == pw_fail)
1091     si->unlock_failures++;
1092
1093   if (p->verbose_p)
1094     switch (si->pw_data->state)
1095       {
1096       case pw_ok:
1097         fprintf (stderr, "%s: password correct.\n", blurb()); break;
1098       case pw_fail:
1099         fprintf (stderr, "%s: password incorrect!\n", blurb()); break;
1100       case pw_null:
1101       case pw_cancel:
1102         fprintf (stderr, "%s: password entry cancelled.\n", blurb()); break;
1103       case pw_time:
1104         fprintf (stderr, "%s: password entry timed out.\n", blurb()); break;
1105       default: break;
1106       }
1107
1108 #ifdef HAVE_SYSLOG
1109   if (si->pw_data->state == pw_fail)
1110     {
1111       /* If they typed a password (as opposed to just hitting return) and
1112          the password was invalid, log it.
1113       */
1114       struct passwd *pw = getpwuid (getuid ());
1115       char *d = DisplayString (si->dpy);
1116       char *u = (pw->pw_name ? pw->pw_name : "???");
1117       int opt = 0;
1118       int fac = 0;
1119
1120 # ifdef LOG_PID
1121       opt = LOG_PID;
1122 # endif
1123
1124 # if defined(LOG_AUTHPRIV)
1125       fac = LOG_AUTHPRIV;
1126 # elif defined(LOG_AUTH)
1127       fac = LOG_AUTH;
1128 # else
1129       fac = LOG_DAEMON;
1130 # endif
1131
1132       if (!d) d = "";
1133       openlog (progname, opt, fac);
1134       syslog (LOG_NOTICE, "FAILED LOGIN %d ON DISPLAY \"%s\", FOR \"%s\"",
1135               si->unlock_failures, d, u);
1136       closelog ();
1137     }
1138 #endif /* HAVE_SYSLOG */
1139
1140   if (si->pw_data->state == pw_fail)
1141     XBell (si->dpy, False);
1142
1143   if (si->pw_data->state == pw_ok && si->unlock_failures != 0)
1144     {
1145       if (si->unlock_failures == 1)
1146         fprintf (real_stderr,
1147                  "%s: WARNING: 1 failed attempt to unlock the screen.\n",
1148                  blurb());
1149       else
1150         fprintf (real_stderr,
1151                  "%s: WARNING: %d failed attempts to unlock the screen.\n",
1152                  blurb(), si->unlock_failures);
1153       fflush (real_stderr);
1154
1155       si->unlock_failures = 0;
1156     }
1157
1158   if (msg)
1159     {
1160       si->pw_data->i_beam = 0;
1161       update_passwd_window (si, msg, 0.0);
1162       XSync (si->dpy, False);
1163       sleep (1);
1164
1165       /* Swallow all pending KeyPress/KeyRelease events. */
1166       {
1167         XEvent e;
1168         while (XCheckMaskEvent (si->dpy, KeyPressMask|KeyReleaseMask, &e))
1169           ;
1170       }
1171     }
1172 }
1173
1174
1175 static void
1176 handle_typeahead (saver_info *si)
1177 {
1178   passwd_dialog_data *pw = si->pw_data;
1179   int i;
1180   if (!si->unlock_typeahead)
1181     return;
1182
1183   i = strlen (si->unlock_typeahead);
1184   if (i >= sizeof(pw->typed_passwd) - 1)
1185     i = sizeof(pw->typed_passwd) - 1;
1186
1187   memcpy (pw->typed_passwd, si->unlock_typeahead, i);
1188   pw->typed_passwd [i] = 0;
1189
1190   memset (si->unlock_typeahead, '*', strlen(si->unlock_typeahead));
1191   si->unlock_typeahead[i] = 0;
1192   update_passwd_window (si, si->unlock_typeahead, pw->ratio);
1193
1194   free (si->unlock_typeahead);
1195   si->unlock_typeahead = 0;
1196 }
1197
1198
1199 Bool
1200 unlock_p (saver_info *si)
1201 {
1202   saver_preferences *p = &si->prefs;
1203   Screen *screen = si->default_screen->screen;
1204   Colormap cmap = DefaultColormapOfScreen (screen);
1205   Bool status;
1206
1207   raise_window (si, True, True, True);
1208
1209   if (p->verbose_p)
1210     fprintf (stderr, "%s: prompting for password.\n", blurb());
1211
1212   if (si->pw_data || si->passwd_dialog)
1213     destroy_passwd_window (si);
1214
1215   make_passwd_window (si);
1216   if (cmap) XInstallColormap (si->dpy, cmap);
1217
1218   compose_status = calloc (1, sizeof (*compose_status));
1219
1220   handle_typeahead (si);
1221   passwd_event_loop (si);
1222
1223   status = (si->pw_data->state == pw_ok);
1224   destroy_passwd_window (si);
1225
1226   free (compose_status);
1227   compose_status = 0;
1228
1229   cmap = si->default_screen->cmap;
1230   if (cmap) XInstallColormap (si->dpy, cmap);
1231
1232   return status;
1233 }
1234
1235
1236 void
1237 set_locked_p (saver_info *si, Bool locked_p)
1238 {
1239   si->locked_p = locked_p;
1240
1241 #ifdef HAVE_XHPDISABLERESET
1242   hp_lock_reset (si, locked_p);                 /* turn off/on C-Sh-Reset */
1243 #endif
1244 #ifdef HAVE_VT_LOCKSWITCH
1245   linux_lock_vt_switch (si, locked_p);          /* turn off/on C-Alt-F1 */
1246 #endif
1247 #ifdef HAVE_XF86VMODE
1248   xfree_lock_mode_switch (si, locked_p);        /* turn off/on C-Alt-Plus */
1249 #endif
1250
1251   store_saver_status (si);                      /* store locked-p */
1252 }
1253
1254
1255 #else  /*  NO_LOCKING -- whole file */
1256
1257 void
1258 set_locked_p (saver_info *si, Bool locked_p)
1259 {
1260   if (locked_p) abort();
1261 }
1262
1263 #endif /* !NO_LOCKING */