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