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