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