169880b1b6a993f7ead31e3206406a7150241d00
[xscreensaver] / driver / lock.c
1 /* lock.c --- handling the password dialog for locking-mode.
2  * xscreensaver, Copyright (c) 1993-2005 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/cursorfont.h>
22 #include <X11/Xos.h>            /* for time() */
23 #include <time.h>
24 #include <sys/time.h>
25 #include "xscreensaver.h"
26 #include "resources.h"
27
28 #ifndef NO_LOCKING              /* (mostly) whole file */
29
30 #ifdef HAVE_SYSLOG
31 # include <syslog.h>
32 #endif /* HAVE_SYSLOG */
33
34 #ifdef HAVE_XHPDISABLERESET
35 # include <X11/XHPlib.h>
36   static void hp_lock_reset (saver_info *si, Bool lock_p);
37 #endif /* HAVE_XHPDISABLERESET */
38
39 #ifdef HAVE_VT_LOCKSWITCH
40 # include <fcntl.h>
41 # include <sys/ioctl.h>
42 # include <sys/vt.h>
43   static void linux_lock_vt_switch (saver_info *si, Bool lock_p);
44 #endif /* HAVE_VT_LOCKSWITCH */
45
46 #ifdef HAVE_XF86VMODE
47 # include <X11/extensions/xf86vmode.h>
48   static void xfree_lock_mode_switch (saver_info *si, Bool lock_p);
49 #endif /* HAVE_XF86VMODE */
50
51 #ifdef HAVE_XF86MISCSETGRABKEYSSTATE
52 # include <X11/extensions/xf86misc.h>
53   static void xfree_lock_grab_smasher (saver_info *si, Bool lock_p);
54 #endif /* HAVE_XF86MISCSETGRABKEYSSTATE */
55
56
57 #ifdef _VROOT_H_
58 ERROR!  You must not include vroot.h in this file.
59 #endif
60
61 #ifndef VMS
62 # include <pwd.h>
63 #else /* VMS */
64
65 extern char *getenv(const char *name);
66 extern int validate_user(char *name, char *password);
67
68 static Bool
69 vms_passwd_valid_p(char *pw, Bool verbose_p)
70 {
71   return (validate_user (getenv("USER"), typed_passwd) == 1);
72 }
73 # undef passwd_valid_p
74 # define passwd_valid_p vms_passwd_valid_p
75
76 #endif /* VMS */
77
78
79 #undef MAX
80 #define MAX(a,b) ((a)>(b)?(a):(b))
81
82 enum passwd_state { pw_read, pw_ok, pw_null, pw_fail, pw_cancel, pw_time };
83
84 struct passwd_dialog_data {
85
86   saver_screen_info *prompt_screen;
87   int previous_mouse_x, previous_mouse_y;
88
89   enum passwd_state state;
90   char typed_passwd [80];
91   XtIntervalId timer;
92   int i_beam;
93
94   float ratio;
95   Position x, y;
96   Dimension width;
97   Dimension height;
98   Dimension border_width;
99
100   Bool show_stars_p; /* "I regret that I have but one asterisk for my country."
101                         -- Nathan Hale, 1776. */
102
103   char *heading_label;
104   char *body_label;
105   char *user_label;
106   char *passwd_label;
107   char *date_label;
108   char *user_string;
109   char *passwd_string;
110   char *login_label;
111
112   XFontStruct *heading_font;
113   XFontStruct *body_font;
114   XFontStruct *label_font;
115   XFontStruct *passwd_font;
116   XFontStruct *date_font;
117   XFontStruct *button_font;
118
119   Pixel foreground;
120   Pixel background;
121   Pixel passwd_foreground;
122   Pixel passwd_background;
123   Pixel thermo_foreground;
124   Pixel thermo_background;
125   Pixel shadow_top;
126   Pixel shadow_bottom;
127   Pixel button_foreground;
128   Pixel button_background;
129
130   Dimension logo_width;
131   Dimension logo_height;
132   Dimension thermo_width;
133   Dimension internal_border;
134   Dimension shadow_width;
135
136   Dimension passwd_field_x, passwd_field_y;
137   Dimension passwd_field_width, passwd_field_height;
138
139   Dimension login_button_x, login_button_y;
140   Dimension login_button_width, login_button_height;
141
142   Dimension thermo_field_x, thermo_field_y;
143   Dimension thermo_field_height;
144
145   Pixmap logo_pixmap;
146   int logo_npixels;
147   unsigned long *logo_pixels;
148
149   Cursor passwd_cursor;
150   Bool login_button_down_p;
151   Bool login_button_p;
152   Bool login_button_enabled_p;
153
154   Pixmap save_under;
155 };
156
157 static void draw_passwd_window (saver_info *si);
158 static void update_passwd_window (saver_info *si, const char *printed_passwd,
159                                   float ratio);
160 static void destroy_passwd_window (saver_info *si);
161 static void undo_vp_motion (saver_info *si);
162 static void handle_passwd_button (saver_info *si, XEvent *event);
163
164
165 static void
166 make_passwd_window (saver_info *si)
167 {
168   struct passwd *p = getpwuid (getuid ());
169   XSetWindowAttributes attrs;
170   unsigned long attrmask = 0;
171   passwd_dialog_data *pw = (passwd_dialog_data *) calloc (1, sizeof(*pw));
172   Screen *screen;
173   Colormap cmap;
174   char *f;
175   saver_screen_info *ssi = &si->screens [mouse_screen (si)];
176
177   /* Display the button only if the "newLoginCommand" pref is non-null.
178    */
179   pw->login_button_p = (si->prefs.new_login_command &&
180                         *si->prefs.new_login_command);
181
182   if (pw->login_button_p)
183     pw->passwd_cursor = XCreateFontCursor (si->dpy, XC_top_left_arrow);
184   else
185     pw->passwd_cursor = 0;
186
187   pw->prompt_screen = ssi;
188   if (si->prefs.verbose_p)
189     fprintf (stderr, "%s: %d: creating password dialog.\n",
190              blurb(), pw->prompt_screen->number);
191
192   screen = pw->prompt_screen->screen;
193   cmap = DefaultColormapOfScreen (screen);
194
195   pw->ratio = 1.0;
196
197   pw->show_stars_p = get_boolean_resource("passwd.asterisks", "Boolean");
198   
199   pw->heading_label = get_string_resource ("passwd.heading.label",
200                                            "Dialog.Label.Label");
201   pw->body_label = get_string_resource ("passwd.body.label",
202                                         "Dialog.Label.Label");
203   pw->user_label = get_string_resource ("passwd.user.label",
204                                         "Dialog.Label.Label");
205   pw->passwd_label = get_string_resource ("passwd.passwd.label",
206                                           "Dialog.Label.Label");
207   pw->login_label = get_string_resource ("passwd.login.label",
208                                          "Dialog.Button.Label");
209
210   pw->date_label = get_string_resource ("dateFormat", "DateFormat");
211
212   if (!pw->heading_label)
213     pw->heading_label = strdup("ERROR: RESOURCES NOT INSTALLED CORRECTLY");
214   if (!pw->body_label)
215     pw->body_label = strdup("ERROR: RESOURCES NOT INSTALLED CORRECTLY");
216   if (!pw->user_label) pw->user_label = strdup("ERROR");
217   if (!pw->passwd_label) pw->passwd_label = strdup("ERROR");
218   if (!pw->date_label) pw->date_label = strdup("ERROR");
219   if (!pw->login_label) pw->login_label = strdup ("ERROR (LOGIN)") ;
220
221   /* Put the version number in the label. */
222   {
223     char *s = (char *) malloc (strlen(pw->heading_label) + 20);
224     sprintf(s, pw->heading_label, si->version);
225     free (pw->heading_label);
226     pw->heading_label = s;
227   }
228
229   pw->user_string = strdup (p && p->pw_name ? p->pw_name : "???");
230   pw->passwd_string = strdup("");
231
232   f = get_string_resource ("passwd.headingFont", "Dialog.Font");
233   pw->heading_font = XLoadQueryFont (si->dpy, (f ? f : "fixed"));
234   if (!pw->heading_font) pw->heading_font = XLoadQueryFont (si->dpy, "fixed");
235   if (f) free (f);
236
237   f = get_string_resource ("passwd.buttonFont", "Dialog.Font");
238   pw->button_font = XLoadQueryFont (si->dpy, (f ? f : "fixed"));
239   if (!pw->button_font) pw->button_font = XLoadQueryFont (si->dpy, "fixed");
240   if (f) free (f);
241
242   f = get_string_resource("passwd.bodyFont", "Dialog.Font");
243   pw->body_font = XLoadQueryFont (si->dpy, (f ? f : "fixed"));
244   if (!pw->body_font) pw->body_font = XLoadQueryFont (si->dpy, "fixed");
245   if (f) free (f);
246
247   f = get_string_resource("passwd.labelFont", "Dialog.Font");
248   pw->label_font = XLoadQueryFont (si->dpy, (f ? f : "fixed"));
249   if (!pw->label_font) pw->label_font = XLoadQueryFont (si->dpy, "fixed");
250   if (f) free (f);
251
252   f = get_string_resource("passwd.passwdFont", "Dialog.Font");
253   pw->passwd_font = XLoadQueryFont (si->dpy, (f ? f : "fixed"));
254   if (!pw->passwd_font) pw->passwd_font = XLoadQueryFont (si->dpy, "fixed");
255   if (f) free (f);
256
257   f = get_string_resource("passwd.dateFont", "Dialog.Font");
258   pw->date_font = XLoadQueryFont (si->dpy, (f ? f : "fixed"));
259   if (!pw->date_font) pw->date_font = XLoadQueryFont (si->dpy, "fixed");
260   if (f) free (f);
261
262   pw->foreground = get_pixel_resource ("passwd.foreground",
263                                        "Dialog.Foreground",
264                                        si->dpy, cmap);
265   pw->background = get_pixel_resource ("passwd.background",
266                                        "Dialog.Background",
267                                        si->dpy, cmap);
268
269   if (pw->foreground == pw->background)
270     {
271       /* Make sure the error messages show up. */
272       pw->foreground = BlackPixelOfScreen (screen);
273       pw->background = WhitePixelOfScreen (screen);
274     }
275
276   pw->passwd_foreground = get_pixel_resource ("passwd.text.foreground",
277                                               "Dialog.Text.Foreground",
278                                               si->dpy, cmap);
279   pw->passwd_background = get_pixel_resource ("passwd.text.background",
280                                               "Dialog.Text.Background",
281                                               si->dpy, cmap);
282   pw->button_foreground = get_pixel_resource ("splash.Button.foreground",
283                                               "Dialog.Button.Foreground",
284                                               si->dpy, cmap);
285   pw->button_background = get_pixel_resource ("splash.Button.background",
286                                               "Dialog.Button.Background",
287                                               si->dpy, cmap);
288   pw->thermo_foreground = get_pixel_resource ("passwd.thermometer.foreground",
289                                               "Dialog.Thermometer.Foreground",
290                                               si->dpy, cmap);
291   pw->thermo_background = get_pixel_resource ("passwd.thermometer.background",
292                                               "Dialog.Thermometer.Background",
293                                               si->dpy, cmap);
294   pw->shadow_top = get_pixel_resource ("passwd.topShadowColor",
295                                        "Dialog.Foreground",
296                                        si->dpy, cmap);
297   pw->shadow_bottom = get_pixel_resource ("passwd.bottomShadowColor",
298                                           "Dialog.Background",
299                                           si->dpy, cmap);
300
301   pw->logo_width = get_integer_resource ("passwd.logo.width",
302                                          "Dialog.Logo.Width");
303   pw->logo_height = get_integer_resource ("passwd.logo.height",
304                                           "Dialog.Logo.Height");
305   pw->thermo_width = get_integer_resource ("passwd.thermometer.width",
306                                            "Dialog.Thermometer.Width");
307   pw->internal_border = get_integer_resource ("passwd.internalBorderWidth",
308                                               "Dialog.InternalBorderWidth");
309   pw->shadow_width = get_integer_resource ("passwd.shadowThickness",
310                                            "Dialog.ShadowThickness");
311
312   if (pw->logo_width == 0)  pw->logo_width = 150;
313   if (pw->logo_height == 0) pw->logo_height = 150;
314   if (pw->internal_border == 0) pw->internal_border = 15;
315   if (pw->shadow_width == 0) pw->shadow_width = 4;
316   if (pw->thermo_width == 0) pw->thermo_width = pw->shadow_width;
317
318   {
319     int direction, ascent, descent;
320     XCharStruct overall;
321
322     pw->width = 0;
323     pw->height = 0;
324
325     /* Measure the heading_label. */
326     XTextExtents (pw->heading_font,
327                   pw->heading_label, strlen(pw->heading_label),
328                   &direction, &ascent, &descent, &overall);
329     if (overall.width > pw->width) pw->width = overall.width;
330     pw->height += ascent + descent;
331
332     /* Measure the body_label. */
333     XTextExtents (pw->body_font,
334                   pw->body_label, strlen(pw->body_label),
335                   &direction, &ascent, &descent, &overall);
336     if (overall.width > pw->width) pw->width = overall.width;
337     pw->height += ascent + descent;
338
339     {
340       Dimension w2 = 0, w3 = 0, button_w = 0;
341       Dimension h2 = 0, h3 = 0, button_h = 0;
342       const char *passwd_string = "MMMMMMMMMMMM";
343
344       /* Measure the user_label. */
345       XTextExtents (pw->label_font,
346                     pw->user_label, strlen(pw->user_label),
347                     &direction, &ascent, &descent, &overall);
348       if (overall.width > w2)  w2 = overall.width;
349       h2 += ascent + descent;
350
351       /* Measure the passwd_label. */
352       XTextExtents (pw->label_font,
353                     pw->passwd_label, strlen(pw->passwd_label),
354                     &direction, &ascent, &descent, &overall);
355       if (overall.width > w2)  w2 = overall.width;
356       h2 += ascent + descent;
357
358       /* Measure the user_string. */
359       XTextExtents (pw->passwd_font,
360                     pw->user_string, strlen(pw->user_string),
361                     &direction, &ascent, &descent, &overall);
362       overall.width += (pw->shadow_width * 4);
363       ascent += (pw->shadow_width * 4);
364       if (overall.width > w3)  w3 = overall.width;
365       h3 += ascent + descent;
366
367       /* Measure the (maximally-sized, dummy) passwd_string. */
368       XTextExtents (pw->passwd_font,
369                     passwd_string, strlen(passwd_string),
370                     &direction, &ascent, &descent, &overall);
371       overall.width += (pw->shadow_width * 4);
372       ascent += (pw->shadow_width * 4);
373       if (overall.width > w3)  w3 = overall.width;
374       h3 += ascent + descent;
375
376       w2 = w2 + w3 + (pw->shadow_width * 2);
377       h2 = MAX (h2, h3);
378
379       pw->login_button_width = 0;
380       pw->login_button_height = 0;
381
382       if (pw->login_button_p)
383         {
384           pw->login_button_enabled_p = True;
385
386           /* Measure the "New Login" button */
387           XTextExtents (pw->button_font, pw->login_label,
388                         strlen (pw->login_label),
389                         &direction, &ascent, &descent, &overall);
390           button_w = overall.width;
391           button_h = ascent + descent;
392
393           /* Add some horizontal padding inside the buttons. */
394           button_w += ascent;
395
396           button_w += ((ascent + descent) / 2) + (pw->shadow_width * 2);
397           button_h += ((ascent + descent) / 2) + (pw->shadow_width * 2);
398
399           pw->login_button_width = button_w;
400           pw->login_button_height = button_h;
401
402           w2 = MAX (w2, button_w);
403           h2 += button_h * 1.5;
404         }
405
406       if (w2 > pw->width)  pw->width  = w2;
407       pw->height += h2;
408     }
409
410     pw->width  += (pw->internal_border * 2);
411     pw->height += (pw->internal_border * 4);
412
413     pw->width += pw->thermo_width + (pw->shadow_width * 3);
414
415     if (pw->logo_height > pw->height)
416       pw->height = pw->logo_height;
417     else if (pw->height > pw->logo_height)
418       pw->logo_height = pw->height;
419
420     pw->logo_width = pw->logo_height;
421
422     pw->width += pw->logo_width;
423   }
424
425   attrmask |= CWOverrideRedirect; attrs.override_redirect = True;
426
427   attrmask |= CWEventMask;
428   attrs.event_mask = (ExposureMask | KeyPressMask |
429                       ButtonPressMask | ButtonReleaseMask);
430
431   /* We need to remember the mouse position and restore it afterward, or
432      sometimes (perhaps only with Xinerama?) the mouse gets warped to
433      inside the bounds of the lock dialog window.
434    */
435   {
436     Window pointer_root, pointer_child;
437     int root_x, root_y, win_x, win_y;
438     unsigned int mask;
439     pw->previous_mouse_x = 0;
440     pw->previous_mouse_y = 0;
441     if (XQueryPointer (si->dpy, RootWindowOfScreen (pw->prompt_screen->screen),
442                        &pointer_root, &pointer_child,
443                        &root_x, &root_y, &win_x, &win_y, &mask))
444       {
445         pw->previous_mouse_x = root_x;
446         pw->previous_mouse_y = root_y;
447         if (si->prefs.verbose_p)
448           fprintf (stderr, "%s: %d: mouse is at %d,%d.\n",
449                    blurb(), pw->prompt_screen->number,
450                    pw->previous_mouse_x, pw->previous_mouse_y);
451       }
452     else if (si->prefs.verbose_p)
453       fprintf (stderr, "%s: %d: unable to determine mouse position?\n",
454                blurb(), pw->prompt_screen->number);
455   }
456
457   /* Figure out where on the desktop to place the window so that it will
458      actually be visible; this takes into account virtual viewports as
459      well as Xinerama. */
460   {
461     int x, y, w, h;
462     get_screen_viewport (pw->prompt_screen, &x, &y, &w, &h,
463                          pw->previous_mouse_x, pw->previous_mouse_y,
464                          si->prefs.verbose_p);
465     if (si->prefs.debug_p) w /= 2;
466     pw->x = x + ((w + pw->width) / 2) - pw->width;
467     pw->y = y + ((h + pw->height) / 2) - pw->height;
468     if (pw->x < x) pw->x = x;
469     if (pw->y < y) pw->y = y;
470   }
471
472   pw->border_width = get_integer_resource ("passwd.borderWidth",
473                                            "Dialog.BorderWidth");
474
475   si->passwd_dialog =
476     XCreateWindow (si->dpy,
477                    RootWindowOfScreen(screen),
478                    pw->x, pw->y, pw->width, pw->height, pw->border_width,
479                    DefaultDepthOfScreen (screen), InputOutput,
480                    DefaultVisualOfScreen(screen),
481                    attrmask, &attrs);
482   XSetWindowBackground (si->dpy, si->passwd_dialog, pw->background);
483
484   /* We use the default visual, not ssi->visual, so that the logo pixmap's
485      visual matches that of the si->passwd_dialog window. */
486   pw->logo_pixmap = xscreensaver_logo (ssi->screen,
487                                        /* ssi->current_visual, */
488                                        DefaultVisualOfScreen(screen),
489                                        si->passwd_dialog, cmap,
490                                        pw->background, 
491                                        &pw->logo_pixels, &pw->logo_npixels,
492                                        0, True);
493
494   /* Before mapping the window, save the bits that are underneath the
495      rectangle the window will occlude.  When we lower the window, we
496      restore these bits.  This works, because the running screenhack
497      has already been sent SIGSTOP, so we know nothing else is drawing
498      right now! */
499   {
500     XGCValues gcv;
501     GC gc;
502     pw->save_under = XCreatePixmap (si->dpy,
503                                     pw->prompt_screen->screensaver_window,
504                                     pw->width + (pw->border_width*2) + 1,
505                                     pw->height + (pw->border_width*2) + 1,
506                                     pw->prompt_screen->current_depth);
507     gcv.function = GXcopy;
508     gc = XCreateGC (si->dpy, pw->save_under, GCFunction, &gcv);
509     XCopyArea (si->dpy, pw->prompt_screen->screensaver_window,
510                pw->save_under, gc,
511                pw->x - pw->border_width, pw->y - pw->border_width,
512                pw->width + (pw->border_width*2) + 1,
513                pw->height + (pw->border_width*2) + 1,
514                0, 0);
515     XFreeGC (si->dpy, gc);
516   }
517
518   XMapRaised (si->dpy, si->passwd_dialog);
519   XSync (si->dpy, False);
520
521   move_mouse_grab (si, si->passwd_dialog,
522                    (pw->passwd_cursor
523                     ? pw->passwd_cursor
524                     : pw->prompt_screen->cursor),
525                    pw->prompt_screen->number);
526   undo_vp_motion (si);
527
528   si->pw_data = pw;
529
530   if (cmap)
531     XInstallColormap (si->dpy, cmap);
532   draw_passwd_window (si);
533   XSync (si->dpy, False);
534 }
535
536
537 static void
538 draw_passwd_window (saver_info *si)
539 {
540   passwd_dialog_data *pw = si->pw_data;
541   XGCValues gcv;
542   GC gc1, gc2;
543   int spacing, height;
544   int x1, x2, x3, y1, y2;
545   int sw;
546   int tb_height;
547
548   height = (pw->heading_font->ascent + pw->heading_font->descent +
549             pw->body_font->ascent + pw->body_font->descent +
550             (2 * MAX ((pw->label_font->ascent + pw->label_font->descent),
551                       (pw->passwd_font->ascent + pw->passwd_font->descent +
552                        (pw->shadow_width * 4)))) +
553             pw->date_font->ascent + pw->date_font->descent);
554
555   if (pw->login_button_p)
556     height += ((pw->button_font->ascent + pw->button_font->descent) * 2 +
557                2 * pw->shadow_width);
558
559   spacing = (((pw->height
560                - ((pw->login_button_p ? 4 : 2) * pw->shadow_width)
561                - pw->internal_border - height))
562              / 8);
563
564   if (spacing < 0) spacing = 0;
565
566   gcv.foreground = pw->foreground;
567   gc1 = XCreateGC (si->dpy, si->passwd_dialog, GCForeground, &gcv);
568   gc2 = XCreateGC (si->dpy, si->passwd_dialog, GCForeground, &gcv);
569   x1 = pw->logo_width + pw->thermo_width + (pw->shadow_width * 3);
570   x3 = pw->width - (pw->shadow_width * 2);
571   y1 = (pw->shadow_width * 2) + spacing + spacing;
572
573   /* top heading
574    */
575   XSetFont (si->dpy, gc1, pw->heading_font->fid);
576   sw = string_width (pw->heading_font, pw->heading_label);
577   x2 = (x1 + ((x3 - x1 - sw) / 2));
578   y1 += spacing + pw->heading_font->ascent + pw->heading_font->descent;
579   XDrawString (si->dpy, si->passwd_dialog, gc1, x2, y1,
580                pw->heading_label, strlen(pw->heading_label));
581
582   /* text below top heading
583    */
584   XSetFont (si->dpy, gc1, pw->body_font->fid);
585   y1 += spacing + pw->body_font->ascent + pw->body_font->descent;
586   sw = string_width (pw->body_font, pw->body_label);
587   x2 = (x1 + ((x3 - x1 - sw) / 2));
588   XDrawString (si->dpy, si->passwd_dialog, gc1, x2, y1,
589                pw->body_label, strlen(pw->body_label));
590
591
592   tb_height = (pw->passwd_font->ascent + pw->passwd_font->descent +
593                (pw->shadow_width * 4));
594
595   /* the "User:" prompt
596    */
597   y1 += spacing;
598   y2 = y1;
599   XSetForeground (si->dpy, gc1, pw->foreground);
600   XSetFont (si->dpy, gc1, pw->label_font->fid);
601   y1 += (spacing + tb_height);
602   x2 = (x1 + pw->internal_border +
603         MAX(string_width (pw->label_font, pw->user_label),
604             string_width (pw->label_font, pw->passwd_label)));
605   XDrawString (si->dpy, si->passwd_dialog, gc1,
606                x2 - string_width (pw->label_font, pw->user_label),
607                y1 - pw->passwd_font->descent,
608                pw->user_label, strlen(pw->user_label));
609
610   /* the "Password:" prompt
611    */
612   y1 += (spacing + tb_height);
613   XDrawString (si->dpy, si->passwd_dialog, gc1,
614                x2 - string_width (pw->label_font, pw->passwd_label),
615                y1 - pw->passwd_font->descent,
616                pw->passwd_label, strlen(pw->passwd_label));
617
618
619   XSetForeground (si->dpy, gc2, pw->passwd_background);
620
621   /* the "user name" text field
622    */
623   y1 = y2;
624   XSetForeground (si->dpy, gc1, pw->passwd_foreground);
625   XSetFont (si->dpy, gc1, pw->passwd_font->fid);
626   y1 += (spacing + tb_height);
627   x2 += (pw->shadow_width * 4);
628
629   pw->passwd_field_width = x3 - x2 - pw->internal_border;
630   pw->passwd_field_height = (pw->passwd_font->ascent +
631                              pw->passwd_font->descent +
632                              pw->shadow_width);
633
634   XFillRectangle (si->dpy, si->passwd_dialog, gc2,
635                   x2 - pw->shadow_width,
636                   y1 - (pw->passwd_font->ascent + pw->passwd_font->descent),
637                   pw->passwd_field_width, pw->passwd_field_height);
638   XDrawString (si->dpy, si->passwd_dialog, gc1,
639                x2,
640                y1 - pw->passwd_font->descent,
641                pw->user_string, strlen(pw->user_string));
642
643   /* the "password" text field
644    */
645   y1 += (spacing + tb_height);
646
647   pw->passwd_field_x = x2 - pw->shadow_width;
648   pw->passwd_field_y = y1 - (pw->passwd_font->ascent +
649                              pw->passwd_font->descent);
650
651   /* The shadow around the text fields
652    */
653   y1 = y2;
654   y1 += (spacing + (pw->shadow_width * 3));
655   x1 = x2 - (pw->shadow_width * 2);
656   x2 = pw->passwd_field_width + (pw->shadow_width * 2);
657   y2 = pw->passwd_field_height + (pw->shadow_width * 2);
658
659   draw_shaded_rectangle (si->dpy, si->passwd_dialog,
660                          x1, y1, x2, y2,
661                          pw->shadow_width,
662                          pw->shadow_bottom, pw->shadow_top);
663
664   y1 += (spacing + pw->passwd_font->ascent + pw->passwd_font->descent +
665          (pw->shadow_width * 4));
666   draw_shaded_rectangle (si->dpy, si->passwd_dialog,
667                          x1, y1, x2, y2,
668                          pw->shadow_width,
669                          pw->shadow_bottom, pw->shadow_top);
670
671
672   /* The date, below the text fields
673    */
674   {
675     char buf[100];
676     time_t now = time ((time_t *) 0);
677     struct tm *tm = localtime (&now);
678     memset (buf, 0, sizeof(buf));
679     strftime (buf, sizeof(buf)-1, pw->date_label, tm);
680
681     XSetFont (si->dpy, gc1, pw->date_font->fid);
682     y1 += pw->shadow_width;
683     y1 += (spacing + tb_height);
684     y1 += spacing/2;
685     sw = string_width (pw->date_font, buf);
686     x2 = x1 + x2 - sw;
687     XDrawString (si->dpy, si->passwd_dialog, gc1, x2, y1, buf, strlen(buf));
688   }
689
690   /* The "New Login" button
691    */
692   if (pw->login_button_p)
693     {
694       XSetForeground (si->dpy, gc1, pw->button_foreground);
695       XSetForeground (si->dpy, gc2, pw->button_background);
696       XSetFont (si->dpy, gc1, pw->button_font->fid);
697
698       sw = string_width (pw->button_font, pw->login_label);
699
700       x2 = pw->width - pw->internal_border - (pw->shadow_width * 2);
701
702       /* right aligned button */
703       /* x1 = x2 - pw->login_button_width;  */
704
705       /* centered button */
706       x1 = (pw->logo_width + pw->thermo_width + (pw->shadow_width * 3) +
707             pw->internal_border);
708       x1 = x1 + (x2 - x1 - pw->login_button_width) / 2;
709
710       y1 = (pw->height - pw->internal_border - pw->login_button_height +
711             spacing);
712       y2 = (y1 +
713             ((pw->login_button_height -
714               (pw->button_font->ascent + pw->button_font->descent))
715              / 2) +
716             pw->button_font->ascent);
717
718       pw->login_button_x = x1;
719       pw->login_button_y = y1;
720     }
721
722   /* The logo
723    */
724   x1 = pw->shadow_width * 6;
725   y1 = pw->shadow_width * 6;
726   x2 = pw->logo_width - (pw->shadow_width * 12);
727   y2 = pw->logo_height - (pw->shadow_width * 12);
728
729   if (pw->logo_pixmap)
730     {
731       Window root;
732       int x, y;
733       unsigned int w, h, bw, d;
734       XGetGeometry (si->dpy, pw->logo_pixmap, &root, &x, &y, &w, &h, &bw, &d);
735       XSetForeground (si->dpy, gc1, pw->foreground);
736       XSetBackground (si->dpy, gc1, pw->background);
737       if (d == 1)
738         XCopyPlane (si->dpy, pw->logo_pixmap, si->passwd_dialog, gc1,
739                     0, 0, w, h,
740                     x1 + ((x2 - (int)w) / 2),
741                     y1 + ((y2 - (int)h) / 2),
742                     1);
743       else
744         XCopyArea (si->dpy, pw->logo_pixmap, si->passwd_dialog, gc1,
745                    0, 0, w, h,
746                    x1 + ((x2 - (int)w) / 2),
747                    y1 + ((y2 - (int)h) / 2));
748     }
749
750   /* The thermometer
751    */
752   XSetForeground (si->dpy, gc1, pw->thermo_foreground);
753   XSetForeground (si->dpy, gc2, pw->thermo_background);
754
755   pw->thermo_field_x = pw->logo_width + pw->shadow_width;
756   pw->thermo_field_y = pw->shadow_width * 5;
757   pw->thermo_field_height = pw->height - (pw->shadow_width * 10);
758
759 #if 0
760   /* Solid border inside the logo box. */
761   XSetForeground (si->dpy, gc1, pw->foreground);
762   XDrawRectangle (si->dpy, si->passwd_dialog, gc1, x1, y1, x2-1, y2-1);
763 #endif
764
765   /* The shadow around the logo
766    */
767   draw_shaded_rectangle (si->dpy, si->passwd_dialog,
768                          pw->shadow_width * 4,
769                          pw->shadow_width * 4,
770                          pw->logo_width - (pw->shadow_width * 8),
771                          pw->logo_height - (pw->shadow_width * 8),
772                          pw->shadow_width,
773                          pw->shadow_bottom, pw->shadow_top);
774
775   /* The shadow around the thermometer
776    */
777   draw_shaded_rectangle (si->dpy, si->passwd_dialog,
778                          pw->logo_width,
779                          pw->shadow_width * 4,
780                          pw->thermo_width + (pw->shadow_width * 2),
781                          pw->height - (pw->shadow_width * 8),
782                          pw->shadow_width,
783                          pw->shadow_bottom, pw->shadow_top);
784
785 #if 1
786   /* Solid border inside the thermometer. */
787   XSetForeground (si->dpy, gc1, pw->foreground);
788   XDrawRectangle (si->dpy, si->passwd_dialog, gc1, 
789                   pw->thermo_field_x, pw->thermo_field_y,
790                   pw->thermo_width - 1, pw->thermo_field_height - 1);
791 #endif
792
793   /* The shadow around the whole window
794    */
795   draw_shaded_rectangle (si->dpy, si->passwd_dialog,
796                          0, 0, pw->width, pw->height, pw->shadow_width,
797                          pw->shadow_top, pw->shadow_bottom);
798
799   XFreeGC (si->dpy, gc1);
800   XFreeGC (si->dpy, gc2);
801
802   update_passwd_window (si, pw->passwd_string, pw->ratio);
803 }
804
805
806 static void
807 update_passwd_window (saver_info *si, const char *printed_passwd, float ratio)
808 {
809   passwd_dialog_data *pw = si->pw_data;
810   XGCValues gcv;
811   GC gc1, gc2;
812   int x, y;
813   XRectangle rects[1];
814
815   pw->ratio = ratio;
816   gcv.foreground = pw->passwd_foreground;
817   gcv.font = pw->passwd_font->fid;
818   gc1 = XCreateGC (si->dpy, si->passwd_dialog, GCForeground|GCFont, &gcv);
819   gcv.foreground = pw->passwd_background;
820   gc2 = XCreateGC (si->dpy, si->passwd_dialog, GCForeground, &gcv);
821
822   if (printed_passwd)
823     {
824       char *s = strdup (printed_passwd);
825       if (pw->passwd_string) free (pw->passwd_string);
826       pw->passwd_string = s;
827     }
828
829   /* the "password" text field
830    */
831   rects[0].x =  pw->passwd_field_x;
832   rects[0].y =  pw->passwd_field_y;
833   rects[0].width = pw->passwd_field_width;
834   rects[0].height = pw->passwd_field_height;
835
836   XFillRectangle (si->dpy, si->passwd_dialog, gc2,
837                   rects[0].x, rects[0].y, rects[0].width, rects[0].height);
838
839   XSetClipRectangles (si->dpy, gc1, 0, 0, rects, 1, Unsorted);
840
841   XDrawString (si->dpy, si->passwd_dialog, gc1,
842                rects[0].x + pw->shadow_width,
843                rects[0].y + pw->passwd_font->ascent,
844                pw->passwd_string, strlen(pw->passwd_string));
845
846   XSetClipMask (si->dpy, gc1, None);
847
848   /* The I-beam
849    */
850   if (pw->i_beam != 0)
851     {
852       x = (rects[0].x + pw->shadow_width +
853            string_width (pw->passwd_font, pw->passwd_string));
854       y = rects[0].y + pw->shadow_width;
855
856       if (x > rects[0].x + rects[0].width - 1)
857         x = rects[0].x + rects[0].width - 1;
858       XDrawLine (si->dpy, si->passwd_dialog, gc1, 
859                  x, y,
860                  x, y + pw->passwd_font->ascent + pw->passwd_font->descent-1);
861     }
862
863   pw->i_beam = (pw->i_beam + 1) % 4;
864
865
866   /* the thermometer
867    */
868   y = (pw->thermo_field_height - 2) * (1.0 - pw->ratio);
869   if (y > 0)
870     {
871       XFillRectangle (si->dpy, si->passwd_dialog, gc2,
872                       pw->thermo_field_x + 1,
873                       pw->thermo_field_y + 1,
874                       pw->thermo_width-2,
875                       y);
876       XSetForeground (si->dpy, gc1, pw->thermo_foreground);
877       XFillRectangle (si->dpy, si->passwd_dialog, gc1,
878                       pw->thermo_field_x + 1,
879                       pw->thermo_field_y + 1 + y,
880                       pw->thermo_width-2,
881                       MAX (0, pw->thermo_field_height - y - 2));
882     }
883
884   /* The "New Login" button
885    */
886   if (pw->login_button_p)
887     {
888       int x2, y2, sw;
889       XSetFont (si->dpy, gc1, pw->button_font->fid);
890       XSetForeground (si->dpy, gc1,
891                       (pw->login_button_enabled_p
892                        ? pw->passwd_foreground
893                        : pw->shadow_bottom));
894       XSetForeground (si->dpy, gc2, pw->button_background);
895
896       XFillRectangle (si->dpy, si->passwd_dialog, gc2,
897                       pw->login_button_x, pw->login_button_y,
898                       pw->login_button_width, pw->login_button_height);
899
900       sw = string_width (pw->button_font, pw->login_label);
901       x2 = pw->login_button_x + ((pw->login_button_width - sw) / 2);
902       y2 = (pw->login_button_y +
903             ((pw->login_button_height -
904               (pw->button_font->ascent + pw->button_font->descent))
905              / 2) +
906             pw->button_font->ascent);
907
908       XDrawString (si->dpy, si->passwd_dialog, gc1, x2, y2,
909                    pw->login_label, strlen(pw->login_label));
910
911       draw_shaded_rectangle (si->dpy, si->passwd_dialog,
912                              pw->login_button_x, pw->login_button_y, 
913                              pw->login_button_width, pw->login_button_height,
914                              pw->shadow_width,
915                              (pw->login_button_down_p
916                               ? pw->shadow_bottom
917                               : pw->shadow_top), 
918                              (pw->login_button_down_p
919                               ? pw->shadow_top
920                               : pw->shadow_bottom));
921     }
922
923   XFreeGC (si->dpy, gc1);
924   XFreeGC (si->dpy, gc2);
925   XSync (si->dpy, False);
926 }
927
928
929 static void
930 destroy_passwd_window (saver_info *si)
931 {
932   saver_preferences *p = &si->prefs;
933   passwd_dialog_data *pw = si->pw_data;
934   saver_screen_info *ssi = pw->prompt_screen;
935   Colormap cmap = DefaultColormapOfScreen (ssi->screen);
936   Pixel black = BlackPixelOfScreen (ssi->screen);
937   Pixel white = WhitePixelOfScreen (ssi->screen);
938   XEvent event;
939
940   memset (pw->typed_passwd, 0, sizeof(pw->typed_passwd));
941   memset (pw->passwd_string, 0, strlen(pw->passwd_string));
942
943   if (pw->timer)
944     XtRemoveTimeOut (pw->timer);
945
946   move_mouse_grab (si, RootWindowOfScreen (ssi->screen),
947                    ssi->cursor, ssi->number);
948
949   if (pw->passwd_cursor)
950     XFreeCursor (si->dpy, pw->passwd_cursor);
951
952   if (p->verbose_p)
953     fprintf (stderr, "%s: %d: moving mouse back to %d,%d.\n",
954              blurb(), ssi->number,
955              pw->previous_mouse_x, pw->previous_mouse_y);
956
957   XWarpPointer (si->dpy, None, RootWindowOfScreen (ssi->screen),
958                 0, 0, 0, 0,
959                 pw->previous_mouse_x, pw->previous_mouse_y);
960
961   XSync (si->dpy, False);
962   while (XCheckMaskEvent (si->dpy, PointerMotionMask, &event))
963     if (p->verbose_p)
964       fprintf (stderr, "%s: discarding MotionNotify event.\n", blurb());
965
966   if (si->passwd_dialog)
967     {
968       XDestroyWindow (si->dpy, si->passwd_dialog);
969       si->passwd_dialog = 0;
970     }
971   
972   if (pw->save_under)
973     {
974       XGCValues gcv;
975       GC gc;
976       gcv.function = GXcopy;
977       gc = XCreateGC (si->dpy, ssi->screensaver_window, GCFunction, &gcv);
978       XCopyArea (si->dpy, pw->save_under,
979                  ssi->screensaver_window, gc,
980                  0, 0,
981                  pw->width + (pw->border_width*2) + 1,
982                  pw->height + (pw->border_width*2) + 1,
983                  pw->x - pw->border_width, pw->y - pw->border_width);
984       XFreePixmap (si->dpy, pw->save_under);
985       pw->save_under = 0;
986       XFreeGC (si->dpy, gc);
987     }
988
989   if (pw->heading_label) free (pw->heading_label);
990   if (pw->body_label)    free (pw->body_label);
991   if (pw->user_label)    free (pw->user_label);
992   if (pw->passwd_label)  free (pw->passwd_label);
993   if (pw->date_label)    free (pw->date_label);
994   if (pw->login_label)   free (pw->login_label);
995   if (pw->user_string)   free (pw->user_string);
996   if (pw->passwd_string) free (pw->passwd_string);
997
998   if (pw->heading_font) XFreeFont (si->dpy, pw->heading_font);
999   if (pw->body_font)    XFreeFont (si->dpy, pw->body_font);
1000   if (pw->label_font)   XFreeFont (si->dpy, pw->label_font);
1001   if (pw->passwd_font)  XFreeFont (si->dpy, pw->passwd_font);
1002   if (pw->date_font)    XFreeFont (si->dpy, pw->date_font);
1003   if (pw->button_font)  XFreeFont (si->dpy, pw->button_font);
1004
1005   if (pw->foreground != black && pw->foreground != white)
1006     XFreeColors (si->dpy, cmap, &pw->foreground, 1, 0L);
1007   if (pw->background != black && pw->background != white)
1008     XFreeColors (si->dpy, cmap, &pw->background, 1, 0L);
1009   if (!(pw->button_foreground == black || pw->button_foreground == white))
1010     XFreeColors (si->dpy, cmap, &pw->button_foreground, 1, 0L);
1011   if (!(pw->button_background == black || pw->button_background == white))
1012     XFreeColors (si->dpy, cmap, &pw->button_background, 1, 0L);
1013   if (pw->passwd_foreground != black && pw->passwd_foreground != white)
1014     XFreeColors (si->dpy, cmap, &pw->passwd_foreground, 1, 0L);
1015   if (pw->passwd_background != black && pw->passwd_background != white)
1016     XFreeColors (si->dpy, cmap, &pw->passwd_background, 1, 0L);
1017   if (pw->thermo_foreground != black && pw->thermo_foreground != white)
1018     XFreeColors (si->dpy, cmap, &pw->thermo_foreground, 1, 0L);
1019   if (pw->thermo_background != black && pw->thermo_background != white)
1020     XFreeColors (si->dpy, cmap, &pw->thermo_background, 1, 0L);
1021   if (pw->shadow_top != black && pw->shadow_top != white)
1022     XFreeColors (si->dpy, cmap, &pw->shadow_top, 1, 0L);
1023   if (pw->shadow_bottom != black && pw->shadow_bottom != white)
1024     XFreeColors (si->dpy, cmap, &pw->shadow_bottom, 1, 0L);
1025
1026   if (pw->logo_pixmap)
1027     XFreePixmap (si->dpy, pw->logo_pixmap);
1028   if (pw->logo_pixels)
1029     {
1030       if (pw->logo_npixels)
1031         XFreeColors (si->dpy, cmap, pw->logo_pixels, pw->logo_npixels, 0L);
1032       free (pw->logo_pixels);
1033       pw->logo_pixels = 0;
1034       pw->logo_npixels = 0;
1035     }
1036
1037   if (pw->save_under)
1038     XFreePixmap (si->dpy, pw->save_under);
1039
1040   if (cmap)
1041     XInstallColormap (si->dpy, cmap);
1042
1043   memset (pw, 0, sizeof(*pw));
1044   free (pw);
1045   si->pw_data = 0;
1046 }
1047
1048
1049 static Bool error_handler_hit_p = False;
1050
1051 static int
1052 ignore_all_errors_ehandler (Display *dpy, XErrorEvent *error)
1053 {
1054   error_handler_hit_p = True;
1055   return 0;
1056 }
1057
1058
1059 #ifdef HAVE_XHPDISABLERESET
1060 /* This function enables and disables the C-Sh-Reset hot-key, which
1061    normally resets the X server (logging out the logged-in user.)
1062    We don't want random people to be able to do that while the
1063    screen is locked.
1064  */
1065 static void
1066 hp_lock_reset (saver_info *si, Bool lock_p)
1067 {
1068   static Bool hp_locked_p = False;
1069
1070   /* Calls to XHPDisableReset and XHPEnableReset must be balanced,
1071      or BadAccess errors occur.  (It's ok for this to be global,
1072      since it affects the whole machine, not just the current screen.)
1073   */
1074   if (hp_locked_p == lock_p)
1075     return;
1076
1077   if (lock_p)
1078     XHPDisableReset (si->dpy);
1079   else
1080     XHPEnableReset (si->dpy);
1081   hp_locked_p = lock_p;
1082 }
1083 #endif /* HAVE_XHPDISABLERESET */
1084
1085 \f
1086 #ifdef HAVE_XF86MISCSETGRABKEYSSTATE
1087
1088 /* This function enables and disables the Ctrl-Alt-KP_star and 
1089    Ctrl-Alt-KP_slash hot-keys, which (in XFree86 4.2) break any
1090    grabs and/or kill the grabbing client.  That would effectively
1091    unlock the screen, so we don't like that.
1092
1093    The Ctrl-Alt-KP_star and Ctrl-Alt-KP_slash hot-keys only exist
1094    if AllowDeactivateGrabs and/or AllowClosedownGrabs are turned on
1095    in XF86Config.  I believe they are disabled by default.
1096
1097    This does not affect any other keys (specifically Ctrl-Alt-BS or
1098    Ctrl-Alt-F1) but I wish it did.  Maybe it will someday.
1099  */
1100 static void
1101 xfree_lock_grab_smasher (saver_info *si, Bool lock_p)
1102 {
1103   saver_preferences *p = &si->prefs;
1104   int status;
1105
1106   XErrorHandler old_handler;
1107   XSync (si->dpy, False);
1108   error_handler_hit_p = False;
1109   old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1110   XSync (si->dpy, False);
1111   status = XF86MiscSetGrabKeysState (si->dpy, !lock_p);
1112   XSync (si->dpy, False);
1113   if (error_handler_hit_p) status = 666;
1114
1115   if (!lock_p && status == MiscExtGrabStateAlready)
1116     status = MiscExtGrabStateSuccess;  /* shut up, consider this success */
1117
1118   if (p->verbose_p && status != MiscExtGrabStateSuccess)
1119     fprintf (stderr, "%s: error: XF86MiscSetGrabKeysState(%d) returned %s\n",
1120              blurb(), !lock_p,
1121              (status == MiscExtGrabStateSuccess ? "MiscExtGrabStateSuccess" :
1122               status == MiscExtGrabStateLocked  ? "MiscExtGrabStateLocked"  :
1123               status == MiscExtGrabStateAlready ? "MiscExtGrabStateAlready" :
1124               status == 666 ? "an X error" :
1125               "unknown value"));
1126
1127   XSync (si->dpy, False);
1128   XSetErrorHandler (old_handler);
1129   XSync (si->dpy, False);
1130 }
1131 #endif /* HAVE_XF86MISCSETGRABKEYSSTATE */
1132
1133
1134
1135 \f
1136 /* This function enables and disables the C-Sh-F1 ... F12 hot-keys,
1137    which, on Linux systems, switches to another virtual console.
1138    We'd like the whole screen/keyboard to be locked, not just one
1139    virtual console, so this function disables that while the X
1140    screen is locked.
1141
1142    Unfortunately, this doesn't work -- this ioctl only works when
1143    called by root, and we have disavowed our privileges long ago.
1144  */
1145 #ifdef HAVE_VT_LOCKSWITCH
1146 static void
1147 linux_lock_vt_switch (saver_info *si, Bool lock_p)
1148 {
1149   saver_preferences *p = &si->prefs;
1150   static Bool vt_locked_p = False;
1151   const char *dev_console = "/dev/console";
1152   int fd;
1153
1154   if (lock_p == vt_locked_p)
1155     return;
1156
1157   if (lock_p && !p->lock_vt_p)
1158     return;
1159
1160   fd = open (dev_console, O_RDWR);
1161   if (fd < 0)
1162     {
1163       char buf [255];
1164       sprintf (buf, "%s: couldn't %s VTs: %s", blurb(),
1165                (lock_p ? "lock" : "unlock"),
1166                dev_console);
1167 #if 1 /* #### doesn't work yet, so don't bother complaining */
1168       perror (buf);
1169 #endif
1170       return;
1171     }
1172
1173   if (ioctl (fd, (lock_p ? VT_LOCKSWITCH : VT_UNLOCKSWITCH)) == 0)
1174     {
1175       vt_locked_p = lock_p;
1176
1177       if (p->verbose_p)
1178         fprintf (stderr, "%s: %s VTs\n", blurb(),
1179                  (lock_p ? "locked" : "unlocked"));
1180     }
1181   else
1182     {
1183       char buf [255];
1184       sprintf (buf, "%s: couldn't %s VTs: ioctl", blurb(),
1185                (lock_p ? "lock" : "unlock"));
1186 #if 0 /* #### doesn't work yet, so don't bother complaining */
1187       perror (buf);
1188 #endif
1189     }
1190
1191   close (fd);
1192 }
1193 #endif /* HAVE_VT_LOCKSWITCH */
1194
1195 \f
1196 /* This function enables and disables the C-Alt-Plus and C-Alt-Minus
1197    hot-keys, which normally change the resolution of the X server.
1198    We don't want people to be able to switch the server resolution
1199    while the screen is locked, because if they switch to a higher
1200    resolution, it could cause part of the underlying desktop to become
1201    exposed.
1202  */
1203 #ifdef HAVE_XF86VMODE
1204
1205 static void
1206 xfree_lock_mode_switch (saver_info *si, Bool lock_p)
1207 {
1208   static Bool any_mode_locked_p = False;
1209   saver_preferences *p = &si->prefs;
1210   int screen;
1211   int event, error;
1212   Bool status;
1213   XErrorHandler old_handler;
1214
1215   if (any_mode_locked_p == lock_p)
1216     return;
1217   if (!XF86VidModeQueryExtension (si->dpy, &event, &error))
1218     return;
1219
1220   for (screen = 0; screen < (si->xinerama_p ? 1 : si->nscreens); screen++)
1221     {
1222       XSync (si->dpy, False);
1223       old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1224       error_handler_hit_p = False;
1225       status = XF86VidModeLockModeSwitch (si->dpy, screen, lock_p);
1226       XSync (si->dpy, False);
1227       XSetErrorHandler (old_handler);
1228       if (error_handler_hit_p) status = False;
1229
1230       if (status)
1231         any_mode_locked_p = lock_p;
1232
1233       if (!status && (p->verbose_p || !lock_p))
1234         /* Only print this when verbose, or when we locked but can't unlock.
1235            I tried printing this message whenever it comes up, but
1236            mode-locking always fails if DontZoom is set in XF86Config. */
1237         fprintf (stderr, "%s: %d: unable to %s mode switching!\n",
1238                  blurb(), screen, (lock_p ? "lock" : "unlock"));
1239       else if (p->verbose_p)
1240         fprintf (stderr, "%s: %d: %s mode switching.\n",
1241                  blurb(), screen, (lock_p ? "locked" : "unlocked"));
1242     }
1243 }
1244 #endif /* HAVE_XF86VMODE */
1245
1246 \f
1247 /* If the viewport has been scrolled since the screen was blanked,
1248    then scroll it back to where it belongs.  This function only exists
1249    to patch over a very brief race condition.
1250  */
1251 static void
1252 undo_vp_motion (saver_info *si)
1253 {
1254 #ifdef HAVE_XF86VMODE
1255   saver_preferences *p = &si->prefs;
1256   int screen;
1257   int event, error;
1258
1259   if (!XF86VidModeQueryExtension (si->dpy, &event, &error))
1260     return;
1261
1262   for (screen = 0; screen < si->nscreens; screen++)
1263     {
1264       saver_screen_info *ssi = &si->screens[screen];
1265       int x, y;
1266       Bool status;
1267
1268       if (ssi->blank_vp_x == -1 && ssi->blank_vp_y == -1)
1269         break;
1270       if (!XF86VidModeGetViewPort (si->dpy, screen, &x, &y))
1271         return;
1272       if (ssi->blank_vp_x == x && ssi->blank_vp_y == y)
1273         return;
1274     
1275       /* We're going to move the viewport.  The mouse has just been grabbed on
1276          (and constrained to, thus warped to) the password window, so it is no
1277          longer near the edge of the screen.  However, wait a bit anyway, just
1278          to make sure the server drains its last motion event, so that the
1279          screen doesn't continue to scroll after we've reset the viewport.
1280        */
1281       XSync (si->dpy, False);
1282       usleep (250000);  /* 1/4 second */
1283       XSync (si->dpy, False);
1284
1285       status = XF86VidModeSetViewPort (si->dpy, screen,
1286                                        ssi->blank_vp_x, ssi->blank_vp_y);
1287
1288       if (!status)
1289         fprintf (stderr,
1290                  "%s: %d: unable to move vp from (%d,%d) back to (%d,%d)!\n",
1291                  blurb(), screen, x, y, ssi->blank_vp_x, ssi->blank_vp_y);
1292       else if (p->verbose_p)
1293         fprintf (stderr,
1294                  "%s: %d: vp moved to (%d,%d); moved it back to (%d,%d).\n",
1295                  blurb(), screen, x, y, ssi->blank_vp_x, ssi->blank_vp_y);
1296     }
1297 #endif /* HAVE_XF86VMODE */
1298 }
1299
1300
1301 \f
1302 /* Interactions
1303  */
1304
1305 static void
1306 passwd_animate_timer (XtPointer closure, XtIntervalId *id)
1307 {
1308   saver_info *si = (saver_info *) closure;
1309   int tick = 166;
1310   passwd_dialog_data *pw = si->pw_data;
1311
1312   if (!pw) return;
1313
1314   pw->ratio -= (1.0 / ((double) si->prefs.passwd_timeout / (double) tick));
1315   if (pw->ratio < 0)
1316     {
1317       pw->ratio = 0;
1318       if (pw->state == pw_read)
1319         pw->state = pw_time;
1320     }
1321
1322   update_passwd_window (si, 0, pw->ratio);
1323
1324   if (pw->state == pw_read)
1325     pw->timer = XtAppAddTimeOut (si->app, tick, passwd_animate_timer,
1326                                  (XtPointer) si);
1327   else
1328     pw->timer = 0;
1329
1330   idle_timer ((XtPointer) si, 0);
1331 }
1332
1333
1334 static XComposeStatus *compose_status;
1335
1336 static void
1337 handle_passwd_button (saver_info *si, XEvent *event)
1338 {
1339   saver_preferences *p = &si->prefs;
1340   Bool mouse_in_box = False;
1341   Bool hit_p = False;
1342   passwd_dialog_data *pw = si->pw_data;
1343   saver_screen_info *ssi = pw->prompt_screen;
1344
1345   if (! pw->login_button_enabled_p)
1346     return;
1347
1348   mouse_in_box = 
1349     (event->xbutton.x >= pw->login_button_x &&
1350      event->xbutton.x <= pw->login_button_x + pw->login_button_width &&
1351      event->xbutton.y >= pw->login_button_y &&
1352      event->xbutton.y <= pw->login_button_y + pw->login_button_height);
1353
1354   if (ButtonRelease == event->xany.type &&
1355       pw->login_button_down_p &&
1356       mouse_in_box)
1357     {
1358       /* Only allow them to press the button once: don't want to
1359          accidentally launch a dozen gdm choosers if the machine
1360          is being slow.
1361        */
1362       hit_p = True;
1363       pw->login_button_enabled_p = False;
1364     }
1365
1366   pw->login_button_down_p = (mouse_in_box &&
1367                              ButtonRelease != event->xany.type);
1368
1369   update_passwd_window (si, 0, pw->ratio);
1370
1371   if (hit_p)
1372     fork_and_exec (ssi, p->new_login_command);
1373 }
1374
1375
1376 static void
1377 handle_passwd_key (saver_info *si, XKeyEvent *event)
1378 {
1379   saver_preferences *p = &si->prefs;
1380   passwd_dialog_data *pw = si->pw_data;
1381   int pw_size = sizeof (pw->typed_passwd) - 1;
1382   char *typed_passwd = pw->typed_passwd;
1383   char s[2];
1384   char *stars = 0;
1385   int i;
1386   int size = XLookupString (event, s, 1, 0, compose_status);
1387
1388   if (size != 1) return;
1389
1390   s[1] = 0;
1391
1392   /* Add 10% to the time remaining every time a key is pressed. */
1393   pw->ratio += 0.1;
1394   if (pw->ratio > 1) pw->ratio = 1;
1395
1396   switch (*s)
1397     {
1398     case '\010': case '\177':                           /* Backspace */
1399       if (!*typed_passwd)
1400         XBell (si->dpy, 0);
1401       else
1402         typed_passwd [strlen(typed_passwd)-1] = 0;
1403       break;
1404
1405     case '\025': case '\030':                           /* Erase line */
1406       memset (typed_passwd, 0, pw_size);
1407       break;
1408
1409     case '\012': case '\015':                           /* Enter */
1410       if (pw->state != pw_read)
1411         ;  /* already done? */
1412       else if (typed_passwd[0] == 0)
1413         pw->state = pw_null;
1414       else
1415         {
1416           update_passwd_window (si, "Checking...", pw->ratio);
1417           XSync (si->dpy, False);
1418           if (passwd_valid_p (typed_passwd, p->verbose_p))
1419             pw->state = pw_ok;
1420           else
1421             pw->state = pw_fail;
1422           update_passwd_window (si, "", pw->ratio);
1423         }
1424       break;
1425
1426     default:
1427       i = strlen (typed_passwd);
1428       if (i >= pw_size-1)
1429         XBell (si->dpy, 0);
1430       else
1431         {
1432           typed_passwd [i] = *s;
1433           typed_passwd [i+1] = 0;
1434         }
1435       break;
1436     }
1437
1438   if (pw->show_stars_p)
1439     {
1440       i = strlen(typed_passwd);
1441       stars = (char *) malloc(i+1);
1442       memset (stars, '*', i);
1443       stars[i] = 0;
1444       update_passwd_window (si, stars, pw->ratio);
1445       free (stars);
1446     }
1447   else
1448     {
1449       update_passwd_window (si, "", pw->ratio);
1450     }
1451 }
1452
1453
1454 static void
1455 passwd_event_loop (saver_info *si)
1456 {
1457   saver_preferences *p = &si->prefs;
1458   char *msg = 0;
1459   XEvent event;
1460   unsigned int caps_p = 0;
1461
1462   passwd_animate_timer ((XtPointer) si, 0);
1463
1464   while (si->pw_data && si->pw_data->state == pw_read)
1465     {
1466       XtAppNextEvent (si->app, &event);
1467       if (event.xany.window == si->passwd_dialog && event.xany.type == Expose)
1468         draw_passwd_window (si);
1469       else if (event.xany.type == KeyPress)
1470         {
1471           handle_passwd_key (si, &event.xkey);
1472           caps_p = (event.xkey.state & LockMask);
1473         }
1474       else if ((event.xany.type == ButtonPress || 
1475                 event.xany.type == ButtonRelease) && 
1476                si->pw_data->login_button_p)
1477         handle_passwd_button (si, &event);
1478       else
1479         XtDispatchEvent (&event);
1480     }
1481
1482   switch (si->pw_data->state)
1483     {
1484     case pw_ok:   msg = 0; break;
1485     case pw_null: msg = ""; break;
1486     case pw_time: msg = "Timed out!"; break;
1487     default:      msg = (caps_p ? "CapsLock?" : "Sorry!"); break;
1488     }
1489
1490   if (si->pw_data->state == pw_fail)
1491     si->unlock_failures++;
1492
1493   if (p->verbose_p)
1494     switch (si->pw_data->state)
1495       {
1496       case pw_ok:
1497         fprintf (stderr, "%s: password correct.\n", blurb()); break;
1498       case pw_fail:
1499         fprintf (stderr, "%s: password incorrect!%s\n", blurb(),
1500                  (caps_p ? "  (CapsLock)" : ""));
1501         break;
1502       case pw_null:
1503       case pw_cancel:
1504         fprintf (stderr, "%s: password entry cancelled.\n", blurb()); break;
1505       case pw_time:
1506         fprintf (stderr, "%s: password entry timed out.\n", blurb()); break;
1507       default: break;
1508       }
1509
1510 #ifdef HAVE_SYSLOG
1511   if (si->pw_data->state == pw_fail)
1512     {
1513       /* If they typed a password (as opposed to just hitting return) and
1514          the password was invalid, log it.
1515       */
1516       struct passwd *pw = getpwuid (getuid ());
1517       char *d = DisplayString (si->dpy);
1518       char *u = (pw->pw_name ? pw->pw_name : "???");
1519       int opt = 0;
1520       int fac = 0;
1521
1522 # ifdef LOG_PID
1523       opt = LOG_PID;
1524 # endif
1525
1526 # if defined(LOG_AUTHPRIV)
1527       fac = LOG_AUTHPRIV;
1528 # elif defined(LOG_AUTH)
1529       fac = LOG_AUTH;
1530 # else
1531       fac = LOG_DAEMON;
1532 # endif
1533
1534       if (!d) d = "";
1535       openlog (progname, opt, fac);
1536       syslog (LOG_NOTICE, "FAILED LOGIN %d ON DISPLAY \"%s\", FOR \"%s\"",
1537               si->unlock_failures, d, u);
1538       closelog ();
1539     }
1540 #endif /* HAVE_SYSLOG */
1541
1542   if (si->pw_data->state == pw_fail)
1543     XBell (si->dpy, False);
1544
1545   if (si->pw_data->state == pw_ok && si->unlock_failures != 0)
1546     {
1547       if (si->unlock_failures == 1)
1548         fprintf (real_stderr,
1549                  "%s: WARNING: 1 failed attempt to unlock the screen.\n",
1550                  blurb());
1551       else
1552         fprintf (real_stderr,
1553                  "%s: WARNING: %d failed attempts to unlock the screen.\n",
1554                  blurb(), si->unlock_failures);
1555       fflush (real_stderr);
1556
1557       si->unlock_failures = 0;
1558     }
1559
1560   if (msg)
1561     {
1562       si->pw_data->i_beam = 0;
1563       update_passwd_window (si, msg, 0.0);
1564       XSync (si->dpy, False);
1565       sleep (1);
1566
1567       /* Swallow all pending KeyPress/KeyRelease events. */
1568       {
1569         XEvent e;
1570         while (XCheckMaskEvent (si->dpy, KeyPressMask|KeyReleaseMask, &e))
1571           ;
1572       }
1573     }
1574 }
1575
1576
1577 static void
1578 handle_typeahead (saver_info *si)
1579 {
1580   passwd_dialog_data *pw = si->pw_data;
1581   int i;
1582   if (!si->unlock_typeahead)
1583     return;
1584
1585   i = strlen (si->unlock_typeahead);
1586   if (i >= sizeof(pw->typed_passwd) - 1)
1587     i = sizeof(pw->typed_passwd) - 1;
1588
1589   memcpy (pw->typed_passwd, si->unlock_typeahead, i);
1590   pw->typed_passwd [i] = 0;
1591
1592   memset (si->unlock_typeahead, '*', strlen(si->unlock_typeahead));
1593   si->unlock_typeahead[i] = 0;
1594   update_passwd_window (si, si->unlock_typeahead, pw->ratio);
1595
1596   free (si->unlock_typeahead);
1597   si->unlock_typeahead = 0;
1598 }
1599
1600
1601 Bool
1602 unlock_p (saver_info *si)
1603 {
1604   saver_preferences *p = &si->prefs;
1605   Bool status;
1606
1607   raise_window (si, True, True, True);
1608
1609   if (p->verbose_p)
1610     fprintf (stderr, "%s: prompting for password.\n", blurb());
1611
1612   if (si->pw_data || si->passwd_dialog)
1613     destroy_passwd_window (si);
1614
1615   make_passwd_window (si);
1616
1617   compose_status = calloc (1, sizeof (*compose_status));
1618
1619   handle_typeahead (si);
1620   passwd_event_loop (si);
1621
1622   status = (si->pw_data->state == pw_ok);
1623   destroy_passwd_window (si);
1624
1625   free (compose_status);
1626   compose_status = 0;
1627
1628   return status;
1629 }
1630
1631
1632 void
1633 set_locked_p (saver_info *si, Bool locked_p)
1634 {
1635   si->locked_p = locked_p;
1636
1637 #ifdef HAVE_XHPDISABLERESET
1638   hp_lock_reset (si, locked_p);                 /* turn off/on C-Sh-Reset */
1639 #endif
1640 #ifdef HAVE_VT_LOCKSWITCH
1641   linux_lock_vt_switch (si, locked_p);          /* turn off/on C-Alt-F1 */
1642 #endif
1643 #ifdef HAVE_XF86VMODE
1644   xfree_lock_mode_switch (si, locked_p);        /* turn off/on C-Alt-Plus */
1645 #endif
1646 #ifdef HAVE_XF86MISCSETGRABKEYSSTATE
1647   xfree_lock_grab_smasher (si, locked_p);       /* turn off/on C-Alt-KP-*,/ */
1648 #endif
1649
1650   store_saver_status (si);                      /* store locked-p */
1651 }
1652
1653
1654 #else  /*  NO_LOCKING -- whole file */
1655
1656 void
1657 set_locked_p (saver_info *si, Bool locked_p)
1658 {
1659   if (locked_p) abort();
1660 }
1661
1662 #endif /* !NO_LOCKING */