1 /* lock.c --- handling the password dialog for locking-mode.
2 * xscreensaver, Copyright (c) 1993-2008 Jamie Zawinski <jwz@jwz.org>
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
13 /* Athena locking code contributed by Jon A. Christopher <jac8782@tamu.edu> */
14 /* Copyright 1997, with the same permissions as above. */
21 #include <X11/Intrinsic.h>
22 #include <X11/cursorfont.h>
23 #include <X11/Xos.h> /* for time() */
26 #include "xscreensaver.h"
27 #include "resources.h"
31 #ifndef NO_LOCKING /* (mostly) whole file */
33 #ifdef HAVE_XHPDISABLERESET
34 # include <X11/XHPlib.h>
35 static void hp_lock_reset (saver_info *si, Bool lock_p);
36 #endif /* HAVE_XHPDISABLERESET */
39 # include <X11/extensions/xf86vmode.h>
40 static void xfree_lock_mode_switch (saver_info *si, Bool lock_p);
41 #endif /* HAVE_XF86VMODE */
43 #ifdef HAVE_XF86MISCSETGRABKEYSSTATE
44 # include <X11/extensions/xf86misc.h>
45 static void xfree_lock_grab_smasher (saver_info *si, Bool lock_p);
46 #endif /* HAVE_XF86MISCSETGRABKEYSSTATE */
50 ERROR! You must not include vroot.h in this file.
54 # include <sys/utsname.h> /* for hostname info */
55 #endif /* HAVE_UNAME */
62 extern char *getenv(const char *name);
63 extern int validate_user(char *name, char *password);
66 vms_passwd_valid_p(char *pw, Bool verbose_p)
68 return (validate_user (getenv("USER"), typed_passwd) == 1);
70 # undef passwd_valid_p
71 # define passwd_valid_p vms_passwd_valid_p
75 #define SAMPLE_INPUT "MMMMMMMMMMMM"
79 #define MAX(a,b) ((a)>(b)?(a):(b))
81 typedef struct info_dialog_data info_dialog_data;
83 struct passwd_dialog_data {
85 saver_screen_info *prompt_screen;
86 int previous_mouse_x, previous_mouse_y;
88 char typed_passwd [80];
96 Dimension border_width;
99 Bool show_stars_p; /* "I regret that I have but one asterisk for my country."
100 -- Nathan Hale, 1776. */
105 mlstring *info_label;
106 /* The entry field shall only be displayed if prompt_label is not NULL */
107 mlstring *prompt_label;
110 Bool passwd_changed_p; /* Whether the user entry field needs redrawing */
111 Bool caps_p; /* Whether we saw a keypress with caps-lock on */
118 XFontStruct *heading_font;
119 XFontStruct *body_font;
120 XFontStruct *label_font;
121 XFontStruct *passwd_font;
122 XFontStruct *date_font;
123 XFontStruct *button_font;
124 XFontStruct *uname_font;
128 Pixel passwd_foreground;
129 Pixel passwd_background;
130 Pixel thermo_foreground;
131 Pixel thermo_background;
134 Pixel button_foreground;
135 Pixel button_background;
137 Dimension preferred_logo_width, logo_width;
138 Dimension preferred_logo_height, logo_height;
139 Dimension thermo_width;
140 Dimension internal_border;
141 Dimension shadow_width;
143 Dimension passwd_field_x, passwd_field_y;
144 Dimension passwd_field_width, passwd_field_height;
146 Dimension unlock_button_x, unlock_button_y;
147 Dimension unlock_button_width, unlock_button_height;
149 Dimension login_button_x, login_button_y;
150 Dimension login_button_width, login_button_height;
152 Dimension thermo_field_x, thermo_field_y;
153 Dimension thermo_field_height;
156 Pixmap logo_clipmask;
158 unsigned long *logo_pixels;
160 Cursor passwd_cursor;
161 Bool unlock_button_down_p;
162 Bool login_button_down_p;
164 Bool login_button_enabled_p;
165 Bool button_state_changed_p; /* Refers to both buttons */
168 Pixmap user_entry_pixmap;
171 static void draw_passwd_window (saver_info *si);
172 static void update_passwd_window (saver_info *si, const char *printed_passwd,
174 static void destroy_passwd_window (saver_info *si);
175 static void undo_vp_motion (saver_info *si);
176 static void finished_typing_passwd (saver_info *si, passwd_dialog_data *pw);
177 static void cleanup_passwd_window (saver_info *si);
178 static void restore_background (saver_info *si);
180 extern void xss_authenticate(saver_info *si, Bool verbose_p);
183 new_passwd_window (saver_info *si)
185 passwd_dialog_data *pw;
189 saver_screen_info *ssi = &si->screens [mouse_screen (si)];
191 pw = (passwd_dialog_data *) calloc (1, sizeof(*pw));
195 /* Display the button only if the "newLoginCommand" pref is non-null.
197 pw->login_button_p = (si->prefs.new_login_command &&
198 *si->prefs.new_login_command);
200 pw->passwd_cursor = XCreateFontCursor (si->dpy, XC_top_left_arrow);
202 pw->prompt_screen = ssi;
204 screen = pw->prompt_screen->screen;
205 cmap = DefaultColormapOfScreen (screen);
207 pw->show_stars_p = get_boolean_resource(si->dpy, "passwd.asterisks",
210 pw->heading_label = get_string_resource (si->dpy, "passwd.heading.label",
211 "Dialog.Label.Label");
212 pw->body_label = get_string_resource (si->dpy, "passwd.body.label",
213 "Dialog.Label.Label");
214 pw->user_label = get_string_resource (si->dpy, "passwd.user.label",
215 "Dialog.Label.Label");
216 pw->unlock_label = get_string_resource (si->dpy, "passwd.unlock.label",
217 "Dialog.Button.Label");
218 pw->login_label = get_string_resource (si->dpy, "passwd.login.label",
219 "Dialog.Button.Label");
221 pw->date_label = get_string_resource (si->dpy, "dateFormat", "DateFormat");
223 if (!pw->heading_label)
224 pw->heading_label = strdup("ERROR: RESOURCES NOT INSTALLED CORRECTLY");
226 pw->body_label = strdup("ERROR: RESOURCES NOT INSTALLED CORRECTLY");
227 if (!pw->user_label) pw->user_label = strdup("ERROR");
228 if (!pw->date_label) pw->date_label = strdup("ERROR");
229 if (!pw->unlock_label) pw->unlock_label = strdup("ERROR (UNLOCK)");
230 if (!pw->login_label) pw->login_label = strdup ("ERROR (LOGIN)") ;
232 /* Put the version number in the label. */
234 char *s = (char *) malloc (strlen(pw->heading_label) + 20);
235 sprintf(s, pw->heading_label, si->version);
236 free (pw->heading_label);
237 pw->heading_label = s;
240 /* Get hostname info */
241 pw->uname_label = strdup(""); /* Initialy, write nothing */
247 if (uname (&uts) == 0)
249 #if 0 /* Get the full hostname */
252 if ((s = strchr(uts.nodename, '.')))
256 char *s = strdup (uts.nodename);
257 free (pw->uname_label);
263 pw->passwd_string = strdup("");
265 f = get_string_resource (si->dpy, "passwd.headingFont", "Dialog.Font");
266 pw->heading_font = XLoadQueryFont (si->dpy, (f ? f : "fixed"));
267 if (!pw->heading_font) pw->heading_font = XLoadQueryFont (si->dpy, "fixed");
270 f = get_string_resource (si->dpy, "passwd.buttonFont", "Dialog.Font");
271 pw->button_font = XLoadQueryFont (si->dpy, (f ? f : "fixed"));
272 if (!pw->button_font) pw->button_font = XLoadQueryFont (si->dpy, "fixed");
275 f = get_string_resource(si->dpy, "passwd.bodyFont", "Dialog.Font");
276 pw->body_font = XLoadQueryFont (si->dpy, (f ? f : "fixed"));
277 if (!pw->body_font) pw->body_font = XLoadQueryFont (si->dpy, "fixed");
280 f = get_string_resource(si->dpy, "passwd.labelFont", "Dialog.Font");
281 pw->label_font = XLoadQueryFont (si->dpy, (f ? f : "fixed"));
282 if (!pw->label_font) pw->label_font = XLoadQueryFont (si->dpy, "fixed");
285 f = get_string_resource(si->dpy, "passwd.passwdFont", "Dialog.Font");
286 pw->passwd_font = XLoadQueryFont (si->dpy, (f ? f : "fixed"));
287 if (!pw->passwd_font) pw->passwd_font = XLoadQueryFont (si->dpy, "fixed");
290 f = get_string_resource(si->dpy, "passwd.dateFont", "Dialog.Font");
291 pw->date_font = XLoadQueryFont (si->dpy, (f ? f : "fixed"));
292 if (!pw->date_font) pw->date_font = XLoadQueryFont (si->dpy, "fixed");
295 f = get_string_resource(si->dpy, "passwd.unameFont", "Dialog.Font");
296 pw->uname_font = XLoadQueryFont (si->dpy, (f ? f : "fixed"));
297 if (!pw->uname_font) pw->uname_font = XLoadQueryFont (si->dpy, "fixed");
300 pw->show_uname_p = get_boolean_resource(si->dpy, "passwd.uname", "Boolean");
302 pw->foreground = get_pixel_resource (si->dpy, cmap,
304 "Dialog.Foreground" );
305 pw->background = get_pixel_resource (si->dpy, cmap,
307 "Dialog.Background" );
309 if (pw->foreground == pw->background)
311 /* Make sure the error messages show up. */
312 pw->foreground = BlackPixelOfScreen (screen);
313 pw->background = WhitePixelOfScreen (screen);
316 pw->passwd_foreground = get_pixel_resource (si->dpy, cmap,
317 "passwd.text.foreground",
318 "Dialog.Text.Foreground" );
319 pw->passwd_background = get_pixel_resource (si->dpy, cmap,
320 "passwd.text.background",
321 "Dialog.Text.Background" );
322 pw->button_foreground = get_pixel_resource (si->dpy, cmap,
323 "splash.Button.foreground",
324 "Dialog.Button.Foreground" );
325 pw->button_background = get_pixel_resource (si->dpy, cmap,
326 "splash.Button.background",
327 "Dialog.Button.Background" );
328 pw->thermo_foreground = get_pixel_resource (si->dpy, cmap,
329 "passwd.thermometer.foreground",
330 "Dialog.Thermometer.Foreground" );
331 pw->thermo_background = get_pixel_resource ( si->dpy, cmap,
332 "passwd.thermometer.background",
333 "Dialog.Thermometer.Background" );
334 pw->shadow_top = get_pixel_resource ( si->dpy, cmap,
335 "passwd.topShadowColor",
336 "Dialog.Foreground" );
337 pw->shadow_bottom = get_pixel_resource (si->dpy, cmap,
338 "passwd.bottomShadowColor",
339 "Dialog.Background" );
341 pw->preferred_logo_width = get_integer_resource (si->dpy, "passwd.logo.width",
342 "Dialog.Logo.Width");
343 pw->preferred_logo_height = get_integer_resource (si->dpy, "passwd.logo.height",
344 "Dialog.Logo.Height");
345 pw->thermo_width = get_integer_resource (si->dpy, "passwd.thermometer.width",
346 "Dialog.Thermometer.Width");
347 pw->internal_border = get_integer_resource (si->dpy, "passwd.internalBorderWidth",
348 "Dialog.InternalBorderWidth");
349 pw->shadow_width = get_integer_resource (si->dpy, "passwd.shadowThickness",
350 "Dialog.ShadowThickness");
352 if (pw->preferred_logo_width == 0) pw->preferred_logo_width = 150;
353 if (pw->preferred_logo_height == 0) pw->preferred_logo_height = 150;
354 if (pw->internal_border == 0) pw->internal_border = 15;
355 if (pw->shadow_width == 0) pw->shadow_width = 4;
356 if (pw->thermo_width == 0) pw->thermo_width = pw->shadow_width;
359 /* We need to remember the mouse position and restore it afterward, or
360 sometimes (perhaps only with Xinerama?) the mouse gets warped to
361 inside the bounds of the lock dialog window.
364 Window pointer_root, pointer_child;
365 int root_x, root_y, win_x, win_y;
367 pw->previous_mouse_x = 0;
368 pw->previous_mouse_y = 0;
369 if (XQueryPointer (si->dpy, RootWindowOfScreen (pw->prompt_screen->screen),
370 &pointer_root, &pointer_child,
371 &root_x, &root_y, &win_x, &win_y, &mask))
373 pw->previous_mouse_x = root_x;
374 pw->previous_mouse_y = root_y;
375 if (si->prefs.verbose_p)
376 fprintf (stderr, "%s: %d: mouse is at %d,%d.\n",
377 blurb(), pw->prompt_screen->number,
378 pw->previous_mouse_x, pw->previous_mouse_y);
380 else if (si->prefs.verbose_p)
381 fprintf (stderr, "%s: %d: unable to determine mouse position?\n",
382 blurb(), pw->prompt_screen->number);
385 /* Before mapping the window, save a pixmap of the current screen.
386 When we lower the window, we
387 restore these bits. This works, because the running screenhack
388 has already been sent SIGSTOP, so we know nothing else is drawing
393 pw->save_under = XCreatePixmap (si->dpy,
394 pw->prompt_screen->screensaver_window,
395 pw->prompt_screen->width,
396 pw->prompt_screen->height,
397 pw->prompt_screen->current_depth);
398 gcv.function = GXcopy;
399 gc = XCreateGC (si->dpy, pw->save_under, GCFunction, &gcv);
400 XCopyArea (si->dpy, pw->prompt_screen->screensaver_window,
403 pw->prompt_screen->width, pw->prompt_screen->height,
405 XFreeGC (si->dpy, gc);
414 * info_msg and prompt may be NULL.
417 make_passwd_window (saver_info *si,
418 const char *info_msg,
422 XSetWindowAttributes attrs;
423 unsigned long attrmask = 0;
424 passwd_dialog_data *pw;
427 Dimension max_string_width_px;
428 saver_screen_info *ssi = &si->screens [mouse_screen (si)];
430 cleanup_passwd_window (si);
432 if (! ssi) /* WTF? Trying to prompt while no screens connected? */
436 if (new_passwd_window (si) < 0)
439 if (!(pw = si->pw_data))
444 pw->prompt_screen = ssi;
445 if (si->prefs.verbose_p)
446 fprintf (stderr, "%s: %d: creating password dialog (\"%s\")\n",
447 blurb(), pw->prompt_screen->number,
448 info_msg ? info_msg : "");
450 screen = pw->prompt_screen->screen;
451 cmap = DefaultColormapOfScreen (screen);
453 pw->echo_input = echo;
455 max_string_width_px = ssi->width
456 - pw->shadow_width * 4
457 - pw->border_width * 2
459 - pw->preferred_logo_width
460 - pw->internal_border * 2;
461 /* As the string wraps it makes the window taller which makes the logo wider
462 * which leaves less room for the text which makes the string wrap. Uh-oh, a
463 * loop. By wrapping at a bit less than the available width, there's some
464 * room for the dialog to grow without going off the edge of the screen. */
465 max_string_width_px *= 0.75;
467 pw->info_label = mlstring_new(info_msg ? info_msg : pw->body_label,
468 pw->label_font, max_string_width_px);
471 int direction, ascent, descent;
477 /* Measure the heading_label. */
478 XTextExtents (pw->heading_font,
479 pw->heading_label, strlen(pw->heading_label),
480 &direction, &ascent, &descent, &overall);
481 if (overall.width > pw->width) pw->width = overall.width;
482 pw->height += ascent + descent;
484 /* Measure the uname_label. */
485 if ((strlen(pw->uname_label)) && pw->show_uname_p)
487 XTextExtents (pw->uname_font,
488 pw->uname_label, strlen(pw->uname_label),
489 &direction, &ascent, &descent, &overall);
490 if (overall.width > pw->width) pw->width = overall.width;
491 pw->height += ascent + descent;
495 Dimension w2 = 0, w3 = 0, button_w = 0;
496 Dimension h2 = 0, h3 = 0, button_h = 0;
497 const char *passwd_string = SAMPLE_INPUT;
499 /* Measure the user_label. */
500 XTextExtents (pw->label_font,
501 pw->user_label, strlen(pw->user_label),
502 &direction, &ascent, &descent, &overall);
503 if (overall.width > w2) w2 = overall.width;
504 h2 += ascent + descent;
506 /* Measure the info_label. */
507 if (pw->info_label->overall_width > pw->width) pw->width = pw->info_label->overall_width;
508 h2 += pw->info_label->overall_height;
510 /* Measure the user string. */
511 XTextExtents (pw->passwd_font,
512 si->user, strlen(si->user),
513 &direction, &ascent, &descent, &overall);
514 overall.width += (pw->shadow_width * 4);
515 ascent += (pw->shadow_width * 4);
516 if (overall.width > w3) w3 = overall.width;
517 h3 += ascent + descent;
519 /* Measure the (dummy) passwd_string. */
522 XTextExtents (pw->passwd_font,
523 passwd_string, strlen(passwd_string),
524 &direction, &ascent, &descent, &overall);
525 overall.width += (pw->shadow_width * 4);
526 ascent += (pw->shadow_width * 4);
527 if (overall.width > w3) w3 = overall.width;
528 h3 += ascent + descent;
530 /* Measure the prompt_label. */
531 max_string_width_px -= w3;
532 pw->prompt_label = mlstring_new(prompt, pw->label_font, max_string_width_px);
534 if (pw->prompt_label->overall_width > w2) w2 = pw->prompt_label->overall_width;
536 h2 += pw->prompt_label->overall_height;
538 w2 = w2 + w3 + (pw->shadow_width * 2);
542 /* The "Unlock" button. */
543 XTextExtents (pw->label_font,
544 pw->unlock_label, strlen(pw->unlock_label),
545 &direction, &ascent, &descent, &overall);
546 button_w = overall.width;
547 button_h = ascent + descent;
549 /* Add some horizontal padding inside the button. */
552 button_w += ((ascent + descent) / 2) + (pw->shadow_width * 2);
553 button_h += ((ascent + descent) / 2) + (pw->shadow_width * 2);
555 pw->unlock_button_width = button_w;
556 pw->unlock_button_height = button_h;
558 w2 = MAX (w2, button_w);
559 h2 += button_h * 1.5;
561 /* The "New Login" button */
562 pw->login_button_width = 0;
563 pw->login_button_height = 0;
565 if (pw->login_button_p)
567 pw->login_button_enabled_p = True;
569 /* Measure the "New Login" button */
570 XTextExtents (pw->button_font, pw->login_label,
571 strlen (pw->login_label),
572 &direction, &ascent, &descent, &overall);
573 button_w = overall.width;
574 button_h = ascent + descent;
576 /* Add some horizontal padding inside the buttons. */
579 button_w += ((ascent + descent) / 2) + (pw->shadow_width * 2);
580 button_h += ((ascent + descent) / 2) + (pw->shadow_width * 2);
582 pw->login_button_width = button_w;
583 pw->login_button_height = button_h;
585 if (button_h > pw->unlock_button_height)
586 h2 += (button_h * 1.5 - pw->unlock_button_height * 1.5);
588 /* Use (2 * shadow_width) spacing between the buttons. Another
589 (2 * shadow_width) is required to account for button shadows. */
590 w2 = MAX (w2, button_w + pw->unlock_button_width + (pw->shadow_width * 4));
593 if (w2 > pw->width) pw->width = w2;
597 pw->width += (pw->internal_border * 2);
598 pw->height += (pw->internal_border * 4);
600 pw->width += pw->thermo_width + (pw->shadow_width * 3);
602 if (pw->preferred_logo_height > pw->height)
603 pw->height = pw->logo_height = pw->preferred_logo_height;
604 else if (pw->height > pw->preferred_logo_height)
605 pw->logo_height = pw->height;
607 pw->logo_width = pw->logo_height;
609 pw->width += pw->logo_width;
612 attrmask |= CWOverrideRedirect; attrs.override_redirect = True;
614 attrmask |= CWEventMask;
615 attrs.event_mask = (ExposureMask | KeyPressMask |
616 ButtonPressMask | ButtonReleaseMask);
618 /* Figure out where on the desktop to place the window so that it will
619 actually be visible; this takes into account virtual viewports as
622 saver_screen_info *ssi = &si->screens [mouse_screen (si)];
627 if (si->prefs.debug_p) w /= 2;
628 pw->x = x + ((w + pw->width) / 2) - pw->width;
629 pw->y = y + ((h + pw->height) / 2) - pw->height;
630 if (pw->x < x) pw->x = x;
631 if (pw->y < y) pw->y = y;
634 pw->border_width = get_integer_resource (si->dpy, "passwd.borderWidth",
635 "Dialog.BorderWidth");
637 /* Only create the window the first time around */
638 if (!si->passwd_dialog)
641 XCreateWindow (si->dpy,
642 RootWindowOfScreen(screen),
643 pw->x, pw->y, pw->width, pw->height, pw->border_width,
644 DefaultDepthOfScreen (screen), InputOutput,
645 DefaultVisualOfScreen(screen),
647 XSetWindowBackground (si->dpy, si->passwd_dialog, pw->background);
649 /* We use the default visual, not ssi->visual, so that the logo pixmap's
650 visual matches that of the si->passwd_dialog window. */
651 pw->logo_pixmap = xscreensaver_logo (ssi->screen,
652 /* ssi->current_visual, */
653 DefaultVisualOfScreen(screen),
654 si->passwd_dialog, cmap,
656 &pw->logo_pixels, &pw->logo_npixels,
657 &pw->logo_clipmask, True);
659 else /* On successive prompts, just resize the window */
662 unsigned int mask = CWX | CWY | CWWidth | CWHeight;
666 wc.width = pw->width;
667 wc.height = pw->height;
669 XConfigureWindow (si->dpy, si->passwd_dialog, mask, &wc);
672 restore_background(si);
674 XMapRaised (si->dpy, si->passwd_dialog);
675 XSync (si->dpy, False);
677 move_mouse_grab (si, si->passwd_dialog,
679 pw->prompt_screen->number);
685 XInstallColormap (si->dpy, cmap);
686 draw_passwd_window (si);
693 draw_passwd_window (saver_info *si)
695 passwd_dialog_data *pw = si->pw_data;
699 int x1, x2, x3, y1, y2;
704 pw->passwd_changed_p = True;
705 pw->button_state_changed_p = True;
707 /* This height is the height of all the elements, not to be confused with
708 * the overall window height which is pw->height. It is used to compute
709 * the amount of spacing (padding) between elements. */
710 height = (pw->heading_font->ascent + pw->heading_font->descent +
711 pw->info_label->overall_height +
712 MAX (((pw->label_font->ascent + pw->label_font->descent) +
713 (pw->prompt_label ? pw->prompt_label->overall_height : 0)),
714 ((pw->passwd_font->ascent + pw->passwd_font->descent) +
715 (pw->shadow_width * 2)) * (pw->prompt_label ? 2 : 1)) +
716 pw->date_font->ascent + pw->date_font->descent);
718 if ((strlen(pw->uname_label)) && pw->show_uname_p)
719 height += (pw->uname_font->ascent + pw->uname_font->descent); /* for uname */
721 height += ((pw->button_font->ascent + pw->button_font->descent) * 2 +
722 2 * pw->shadow_width);
724 spacing = ((pw->height - 2 * pw->shadow_width
725 - pw->internal_border - height)
728 if (spacing < 0) spacing = 0;
730 gcv.foreground = pw->foreground;
731 gc1 = XCreateGC (si->dpy, si->passwd_dialog, GCForeground, &gcv);
732 gc2 = XCreateGC (si->dpy, si->passwd_dialog, GCForeground, &gcv);
733 x1 = pw->logo_width + pw->thermo_width + (pw->shadow_width * 3);
734 x3 = pw->width - (pw->shadow_width * 2);
735 y1 = (pw->shadow_width * 2) + spacing + spacing;
739 XSetFont (si->dpy, gc1, pw->heading_font->fid);
740 sw = string_width (pw->heading_font, pw->heading_label);
741 x2 = (x1 + ((x3 - x1 - sw) / 2));
742 y1 += spacing + pw->heading_font->ascent + pw->heading_font->descent;
743 XDrawString (si->dpy, si->passwd_dialog, gc1, x2, y1,
744 pw->heading_label, strlen(pw->heading_label));
746 /* uname below top heading
748 if ((strlen(pw->uname_label)) && pw->show_uname_p)
750 XSetFont (si->dpy, gc1, pw->uname_font->fid);
751 y1 += spacing + pw->uname_font->ascent + pw->uname_font->descent;
752 sw = string_width (pw->uname_font, pw->uname_label);
753 x2 = (x1 + ((x3 - x1 - sw) / 2));
754 XDrawString (si->dpy, si->passwd_dialog, gc1, x2, y1,
755 pw->uname_label, strlen(pw->uname_label));
758 /* the info_label (below uname)
760 x2 = (x1 + ((x3 - x1 - pw->info_label->overall_width) / 2));
761 y1 += spacing + pw->info_label->font_height / 2;
762 mlstring_draw(si->dpy, si->passwd_dialog, gc1, pw->info_label,
764 y1 += pw->info_label->overall_height;
767 tb_height = (pw->passwd_font->ascent + pw->passwd_font->descent +
768 (pw->shadow_width * 4));
770 /* the "User:" prompt
773 XSetForeground (si->dpy, gc1, pw->foreground);
774 XSetFont (si->dpy, gc1, pw->label_font->fid);
775 y1 += (spacing + tb_height + pw->shadow_width);
776 x2 = (x1 + pw->internal_border +
777 MAX(string_width (pw->label_font, pw->user_label),
778 pw->prompt_label ? pw->prompt_label->overall_width : 0));
779 XDrawString (si->dpy, si->passwd_dialog, gc1,
780 x2 - string_width (pw->label_font, pw->user_label),
781 y1 - pw->passwd_font->descent,
782 pw->user_label, strlen(pw->user_label));
784 /* the prompt_label prompt
786 if (pw->prompt_label)
788 y1 += tb_height - pw->label_font->ascent + pw->shadow_width;
789 mlstring_draw(si->dpy, si->passwd_dialog, gc1, pw->prompt_label,
790 x2 - pw->prompt_label->overall_width, y1);
793 /* the "user name" text field
796 XSetForeground (si->dpy, gc1, pw->passwd_foreground);
797 XSetForeground (si->dpy, gc2, pw->passwd_background);
798 XSetFont (si->dpy, gc1, pw->passwd_font->fid);
799 y1 += (spacing + tb_height);
800 x2 += (pw->shadow_width * 4);
802 pw->passwd_field_width = x3 - x2 - pw->internal_border;
803 pw->passwd_field_height = (pw->passwd_font->ascent +
804 pw->passwd_font->descent +
807 XFillRectangle (si->dpy, si->passwd_dialog, gc2,
808 x2 - pw->shadow_width,
809 y1 - (pw->passwd_font->ascent + pw->passwd_font->descent),
810 pw->passwd_field_width, pw->passwd_field_height);
811 XDrawString (si->dpy, si->passwd_dialog, gc1,
813 y1 - pw->passwd_font->descent,
814 si->user, strlen(si->user));
816 /* the password/prompt text field
818 if (pw->prompt_label)
820 y1 += (spacing + pw->prompt_label->overall_height + pw->shadow_width * 2);
822 pw->passwd_field_x = x2 - pw->shadow_width;
823 pw->passwd_field_y = y1 - (pw->passwd_font->ascent +
824 pw->passwd_font->descent);
827 /* The shadow around the text fields
830 y1 += (spacing + (pw->shadow_width * 3));
831 x1 = x2 - (pw->shadow_width * 2);
832 x2 = pw->passwd_field_width + (pw->shadow_width * 2);
833 y2 = pw->passwd_field_height + (pw->shadow_width * 2);
835 draw_shaded_rectangle (si->dpy, si->passwd_dialog,
838 pw->shadow_bottom, pw->shadow_top);
840 if (pw->prompt_label)
842 y1 += (spacing + pw->prompt_label->overall_height + pw->shadow_width * 2);
843 draw_shaded_rectangle (si->dpy, si->passwd_dialog,
846 pw->shadow_bottom, pw->shadow_top);
850 /* The date, below the text fields
854 time_t now = time ((time_t *) 0);
855 struct tm *tm = localtime (&now);
856 memset (buf, 0, sizeof(buf));
857 strftime (buf, sizeof(buf)-1, pw->date_label, tm);
859 XSetFont (si->dpy, gc1, pw->date_font->fid);
860 y1 += pw->shadow_width;
861 y1 += (spacing + tb_height);
863 sw = string_width (pw->date_font, buf);
865 XDrawString (si->dpy, si->passwd_dialog, gc1, x2, y1, buf, strlen(buf));
868 /* Set up the GCs for the "New Login" and "Unlock" buttons.
870 XSetForeground(si->dpy, gc1, pw->button_foreground);
871 XSetForeground(si->dpy, gc2, pw->button_background);
872 XSetFont(si->dpy, gc1, pw->button_font->fid);
874 /* The "Unlock" button */
875 x2 = pw->width - pw->internal_border - (pw->shadow_width * 2);
877 /* right aligned button */
878 x1 = x2 - pw->unlock_button_width;
880 /* Add half the difference between y1 and the internal edge.
881 * It actually looks better if the internal border is ignored. */
882 y1 += ((pw->height - MAX (pw->unlock_button_height, pw->login_button_height)
886 pw->unlock_button_x = x1;
887 pw->unlock_button_y = y1;
889 /* The "New Login" button
891 if (pw->login_button_p)
893 /* Using the same GC as for the Unlock button */
895 sw = string_width (pw->button_font, pw->login_label);
897 /* left aligned button */
898 x1 = (pw->logo_width + pw->thermo_width + (pw->shadow_width * 3) +
899 pw->internal_border);
901 pw->login_button_x = x1;
902 pw->login_button_y = y1;
907 x1 = pw->shadow_width * 6;
908 y1 = pw->shadow_width * 6;
909 x2 = pw->logo_width - (pw->shadow_width * 12);
910 y2 = pw->logo_height - (pw->shadow_width * 12);
916 unsigned int w, h, bw, d;
917 XGetGeometry (si->dpy, pw->logo_pixmap, &root, &x, &y, &w, &h, &bw, &d);
918 XSetForeground (si->dpy, gc1, pw->foreground);
919 XSetBackground (si->dpy, gc1, pw->background);
920 XSetClipMask (si->dpy, gc1, pw->logo_clipmask);
921 XSetClipOrigin (si->dpy, gc1, x1 + ((x2 - (int)w) / 2), y1 + ((y2 - (int)h) / 2));
923 XCopyPlane (si->dpy, pw->logo_pixmap, si->passwd_dialog, gc1,
925 x1 + ((x2 - (int)w) / 2),
926 y1 + ((y2 - (int)h) / 2),
929 XCopyArea (si->dpy, pw->logo_pixmap, si->passwd_dialog, gc1,
931 x1 + ((x2 - (int)w) / 2),
932 y1 + ((y2 - (int)h) / 2));
937 XSetForeground (si->dpy, gc1, pw->thermo_foreground);
938 XSetForeground (si->dpy, gc2, pw->thermo_background);
940 pw->thermo_field_x = pw->logo_width + pw->shadow_width;
941 pw->thermo_field_y = pw->shadow_width * 5;
942 pw->thermo_field_height = pw->height - (pw->shadow_width * 10);
945 /* Solid border inside the logo box. */
946 XSetForeground (si->dpy, gc1, pw->foreground);
947 XDrawRectangle (si->dpy, si->passwd_dialog, gc1, x1, y1, x2-1, y2-1);
950 /* The shadow around the logo
952 draw_shaded_rectangle (si->dpy, si->passwd_dialog,
953 pw->shadow_width * 4,
954 pw->shadow_width * 4,
955 pw->logo_width - (pw->shadow_width * 8),
956 pw->logo_height - (pw->shadow_width * 8),
958 pw->shadow_bottom, pw->shadow_top);
960 /* The shadow around the thermometer
962 draw_shaded_rectangle (si->dpy, si->passwd_dialog,
964 pw->shadow_width * 4,
965 pw->thermo_width + (pw->shadow_width * 2),
966 pw->height - (pw->shadow_width * 8),
968 pw->shadow_bottom, pw->shadow_top);
971 /* Solid border inside the thermometer. */
972 XSetForeground (si->dpy, gc1, pw->foreground);
973 XDrawRectangle (si->dpy, si->passwd_dialog, gc1,
974 pw->thermo_field_x, pw->thermo_field_y,
975 pw->thermo_width - 1, pw->thermo_field_height - 1);
978 /* The shadow around the whole window
980 draw_shaded_rectangle (si->dpy, si->passwd_dialog,
981 0, 0, pw->width, pw->height, pw->shadow_width,
982 pw->shadow_top, pw->shadow_bottom);
984 XFreeGC (si->dpy, gc1);
985 XFreeGC (si->dpy, gc2);
987 update_passwd_window (si, pw->passwd_string, pw->ratio);
991 draw_button(Display *dpy,
994 unsigned long foreground, unsigned long background,
997 int width, int height,
999 Pixel shadow_light, Pixel shadow_dark,
1005 int label_x, label_y;
1007 gcv.foreground = foreground;
1008 gcv.font = font->fid;
1009 gc1 = XCreateGC(dpy, dialog, GCForeground|GCFont, &gcv);
1010 gcv.foreground = background;
1011 gc2 = XCreateGC(dpy, dialog, GCForeground, &gcv);
1013 XFillRectangle(dpy, dialog, gc2,
1014 x, y, width, height);
1016 sw = string_width(font, label);
1018 label_x = x + ((width - sw) / 2);
1019 label_y = (y + (height - (font->ascent + font->descent)) / 2 + font->ascent);
1027 XDrawString(dpy, dialog, gc1, label_x, label_y, label, strlen(label));
1032 draw_shaded_rectangle(dpy, dialog, x, y, width, height,
1033 shadow_width, shadow_light, shadow_dark);
1037 update_passwd_window (saver_info *si, const char *printed_passwd, float ratio)
1039 passwd_dialog_data *pw = si->pw_data;
1043 XRectangle rects[1];
1046 gcv.foreground = pw->passwd_foreground;
1047 gcv.font = pw->passwd_font->fid;
1048 gc1 = XCreateGC (si->dpy, si->passwd_dialog, GCForeground|GCFont, &gcv);
1049 gcv.foreground = pw->passwd_background;
1050 gc2 = XCreateGC (si->dpy, si->passwd_dialog, GCForeground, &gcv);
1054 char *s = strdup (printed_passwd);
1055 if (pw->passwd_string) free (pw->passwd_string);
1056 pw->passwd_string = s;
1059 if (pw->prompt_label)
1062 /* the "password" text field
1064 rects[0].x = pw->passwd_field_x;
1065 rects[0].y = pw->passwd_field_y;
1066 rects[0].width = pw->passwd_field_width;
1067 rects[0].height = pw->passwd_field_height;
1069 /* The user entry (password) field is double buffered.
1070 * This avoids flickering, particularly in synchronous mode. */
1072 if (pw->passwd_changed_p)
1074 pw->passwd_changed_p = False;
1076 if (pw->user_entry_pixmap)
1078 XFreePixmap(si->dpy, pw->user_entry_pixmap);
1079 pw->user_entry_pixmap = 0;
1082 pw->user_entry_pixmap =
1083 XCreatePixmap (si->dpy, si->passwd_dialog,
1084 rects[0].width, rects[0].height,
1085 DefaultDepthOfScreen (pw->prompt_screen->screen));
1087 XFillRectangle (si->dpy, pw->user_entry_pixmap, gc2,
1088 0, 0, rects[0].width, rects[0].height);
1090 XDrawString (si->dpy, pw->user_entry_pixmap, gc1,
1092 pw->passwd_font->ascent,
1093 pw->passwd_string, strlen(pw->passwd_string));
1095 /* Ensure the new pixmap gets copied to the window */
1102 if (pw->i_beam == 0)
1104 /* Make the I-beam disappear */
1105 XCopyArea(si->dpy, pw->user_entry_pixmap, si->passwd_dialog, gc2,
1106 0, 0, rects[0].width, rects[0].height,
1107 rects[0].x, rects[0].y);
1109 else if (pw->i_beam == 1)
1111 /* Make the I-beam appear */
1112 x = (rects[0].x + pw->shadow_width +
1113 string_width (pw->passwd_font, pw->passwd_string));
1114 y = rects[0].y + pw->shadow_width;
1116 if (x > rects[0].x + rects[0].width - 1)
1117 x = rects[0].x + rects[0].width - 1;
1118 XDrawLine (si->dpy, si->passwd_dialog, gc1,
1120 x, y + pw->passwd_font->ascent + pw->passwd_font->descent-1);
1123 pw->i_beam = (pw->i_beam + 1) % 4;
1129 y = (pw->thermo_field_height - 2) * (1.0 - pw->ratio);
1132 XFillRectangle (si->dpy, si->passwd_dialog, gc2,
1133 pw->thermo_field_x + 1,
1134 pw->thermo_field_y + 1,
1137 XSetForeground (si->dpy, gc1, pw->thermo_foreground);
1138 XFillRectangle (si->dpy, si->passwd_dialog, gc1,
1139 pw->thermo_field_x + 1,
1140 pw->thermo_field_y + 1 + y,
1142 MAX (0, pw->thermo_field_height - y - 2));
1145 if (pw->button_state_changed_p)
1147 pw->button_state_changed_p = False;
1149 /* The "Unlock" button
1151 draw_button(si->dpy, si->passwd_dialog, pw->button_font,
1152 pw->button_foreground, pw->button_background,
1154 pw->unlock_button_x, pw->unlock_button_y,
1155 pw->unlock_button_width, pw->unlock_button_height,
1157 (pw->unlock_button_down_p ? pw->shadow_bottom : pw->shadow_top),
1158 (pw->unlock_button_down_p ? pw->shadow_top : pw->shadow_bottom),
1159 pw->unlock_button_down_p);
1161 /* The "New Login" button
1163 if (pw->login_button_p)
1165 draw_button(si->dpy, si->passwd_dialog, pw->button_font,
1166 (pw->login_button_enabled_p
1167 ? pw->passwd_foreground
1168 : pw->shadow_bottom),
1169 pw->button_background,
1171 pw->login_button_x, pw->login_button_y,
1172 pw->login_button_width, pw->login_button_height,
1174 (pw->login_button_down_p
1177 (pw->login_button_down_p
1179 : pw->shadow_bottom),
1180 pw->login_button_down_p);
1184 XFreeGC (si->dpy, gc1);
1185 XFreeGC (si->dpy, gc2);
1186 XSync (si->dpy, False);
1191 restore_background (saver_info *si)
1193 passwd_dialog_data *pw = si->pw_data;
1194 saver_screen_info *ssi = pw->prompt_screen;
1198 gcv.function = GXcopy;
1200 gc = XCreateGC (si->dpy, ssi->screensaver_window, GCFunction, &gcv);
1202 XCopyArea (si->dpy, pw->save_under,
1203 ssi->screensaver_window, gc,
1205 ssi->width, ssi->height,
1208 XFreeGC (si->dpy, gc);
1212 /* Frees anything created by make_passwd_window */
1214 cleanup_passwd_window (saver_info *si)
1216 passwd_dialog_data *pw;
1218 if (!(pw = si->pw_data))
1223 mlstring_free(pw->info_label);
1227 if (pw->prompt_label)
1229 mlstring_free(pw->prompt_label);
1230 pw->prompt_label = 0;
1233 memset (pw->typed_passwd, 0, sizeof(pw->typed_passwd));
1234 memset (pw->passwd_string, 0, strlen(pw->passwd_string));
1238 XtRemoveTimeOut (pw->timer);
1242 if (pw->user_entry_pixmap)
1244 XFreePixmap(si->dpy, pw->user_entry_pixmap);
1245 pw->user_entry_pixmap = 0;
1251 destroy_passwd_window (saver_info *si)
1253 saver_preferences *p = &si->prefs;
1254 passwd_dialog_data *pw = si->pw_data;
1255 saver_screen_info *ssi = pw->prompt_screen;
1256 Colormap cmap = DefaultColormapOfScreen (ssi->screen);
1257 Pixel black = BlackPixelOfScreen (ssi->screen);
1258 Pixel white = WhitePixelOfScreen (ssi->screen);
1261 cleanup_passwd_window (si);
1263 if (si->cached_passwd)
1265 char *wipe = si->cached_passwd;
1270 free(si->cached_passwd);
1271 si->cached_passwd = NULL;
1274 move_mouse_grab (si, RootWindowOfScreen (ssi->screen),
1275 ssi->cursor, ssi->number);
1277 if (pw->passwd_cursor)
1278 XFreeCursor (si->dpy, pw->passwd_cursor);
1281 fprintf (stderr, "%s: %d: moving mouse back to %d,%d.\n",
1282 blurb(), ssi->number,
1283 pw->previous_mouse_x, pw->previous_mouse_y);
1285 XWarpPointer (si->dpy, None, RootWindowOfScreen (ssi->screen),
1287 pw->previous_mouse_x, pw->previous_mouse_y);
1289 while (XCheckMaskEvent (si->dpy, PointerMotionMask, &event))
1291 fprintf (stderr, "%s: discarding MotionNotify event.\n", blurb());
1293 if (si->passwd_dialog)
1295 if (si->prefs.verbose_p)
1296 fprintf (stderr, "%s: %d: destroying password dialog.\n",
1297 blurb(), pw->prompt_screen->number);
1299 XDestroyWindow (si->dpy, si->passwd_dialog);
1300 si->passwd_dialog = 0;
1305 restore_background(si);
1306 XFreePixmap (si->dpy, pw->save_under);
1310 if (pw->heading_label) free (pw->heading_label);
1311 if (pw->body_label) free (pw->body_label);
1312 if (pw->user_label) free (pw->user_label);
1313 if (pw->date_label) free (pw->date_label);
1314 if (pw->login_label) free (pw->login_label);
1315 if (pw->unlock_label) free (pw->unlock_label);
1316 if (pw->passwd_string) free (pw->passwd_string);
1317 if (pw->uname_label) free (pw->uname_label);
1319 if (pw->heading_font) XFreeFont (si->dpy, pw->heading_font);
1320 if (pw->body_font) XFreeFont (si->dpy, pw->body_font);
1321 if (pw->label_font) XFreeFont (si->dpy, pw->label_font);
1322 if (pw->passwd_font) XFreeFont (si->dpy, pw->passwd_font);
1323 if (pw->date_font) XFreeFont (si->dpy, pw->date_font);
1324 if (pw->button_font) XFreeFont (si->dpy, pw->button_font);
1325 if (pw->uname_font) XFreeFont (si->dpy, pw->uname_font);
1327 if (pw->foreground != black && pw->foreground != white)
1328 XFreeColors (si->dpy, cmap, &pw->foreground, 1, 0L);
1329 if (pw->background != black && pw->background != white)
1330 XFreeColors (si->dpy, cmap, &pw->background, 1, 0L);
1331 if (!(pw->button_foreground == black || pw->button_foreground == white))
1332 XFreeColors (si->dpy, cmap, &pw->button_foreground, 1, 0L);
1333 if (!(pw->button_background == black || pw->button_background == white))
1334 XFreeColors (si->dpy, cmap, &pw->button_background, 1, 0L);
1335 if (pw->passwd_foreground != black && pw->passwd_foreground != white)
1336 XFreeColors (si->dpy, cmap, &pw->passwd_foreground, 1, 0L);
1337 if (pw->passwd_background != black && pw->passwd_background != white)
1338 XFreeColors (si->dpy, cmap, &pw->passwd_background, 1, 0L);
1339 if (pw->thermo_foreground != black && pw->thermo_foreground != white)
1340 XFreeColors (si->dpy, cmap, &pw->thermo_foreground, 1, 0L);
1341 if (pw->thermo_background != black && pw->thermo_background != white)
1342 XFreeColors (si->dpy, cmap, &pw->thermo_background, 1, 0L);
1343 if (pw->shadow_top != black && pw->shadow_top != white)
1344 XFreeColors (si->dpy, cmap, &pw->shadow_top, 1, 0L);
1345 if (pw->shadow_bottom != black && pw->shadow_bottom != white)
1346 XFreeColors (si->dpy, cmap, &pw->shadow_bottom, 1, 0L);
1348 if (pw->logo_pixmap)
1349 XFreePixmap (si->dpy, pw->logo_pixmap);
1350 if (pw-> logo_clipmask)
1351 XFreePixmap (si->dpy, pw->logo_clipmask);
1352 if (pw->logo_pixels)
1354 if (pw->logo_npixels)
1355 XFreeColors (si->dpy, cmap, pw->logo_pixels, pw->logo_npixels, 0L);
1356 free (pw->logo_pixels);
1357 pw->logo_pixels = 0;
1358 pw->logo_npixels = 0;
1362 XFreePixmap (si->dpy, pw->save_under);
1365 XInstallColormap (si->dpy, cmap);
1367 memset (pw, 0, sizeof(*pw));
1373 static Bool error_handler_hit_p = False;
1376 ignore_all_errors_ehandler (Display *dpy, XErrorEvent *error)
1378 error_handler_hit_p = True;
1383 #ifdef HAVE_XHPDISABLERESET
1384 /* This function enables and disables the C-Sh-Reset hot-key, which
1385 normally resets the X server (logging out the logged-in user.)
1386 We don't want random people to be able to do that while the
1390 hp_lock_reset (saver_info *si, Bool lock_p)
1392 static Bool hp_locked_p = False;
1394 /* Calls to XHPDisableReset and XHPEnableReset must be balanced,
1395 or BadAccess errors occur. (It's ok for this to be global,
1396 since it affects the whole machine, not just the current screen.)
1398 if (hp_locked_p == lock_p)
1402 XHPDisableReset (si->dpy);
1404 XHPEnableReset (si->dpy);
1405 hp_locked_p = lock_p;
1407 #endif /* HAVE_XHPDISABLERESET */
1410 #ifdef HAVE_XF86MISCSETGRABKEYSSTATE
1412 /* This function enables and disables the Ctrl-Alt-KP_star and
1413 Ctrl-Alt-KP_slash hot-keys, which (in XFree86 4.2) break any
1414 grabs and/or kill the grabbing client. That would effectively
1415 unlock the screen, so we don't like that.
1417 The Ctrl-Alt-KP_star and Ctrl-Alt-KP_slash hot-keys only exist
1418 if AllowDeactivateGrabs and/or AllowClosedownGrabs are turned on
1419 in XF86Config. I believe they are disabled by default.
1421 This does not affect any other keys (specifically Ctrl-Alt-BS or
1422 Ctrl-Alt-F1) but I wish it did. Maybe it will someday.
1425 xfree_lock_grab_smasher (saver_info *si, Bool lock_p)
1427 saver_preferences *p = &si->prefs;
1430 XErrorHandler old_handler;
1432 if (!XF86MiscQueryExtension(si->dpy, &event, &error))
1435 XSync (si->dpy, False);
1436 error_handler_hit_p = False;
1437 old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1438 XSync (si->dpy, False);
1439 status = XF86MiscSetGrabKeysState (si->dpy, !lock_p);
1440 XSync (si->dpy, False);
1441 if (error_handler_hit_p) status = 666;
1443 if (!lock_p && status == MiscExtGrabStateAlready)
1444 status = MiscExtGrabStateSuccess; /* shut up, consider this success */
1446 if (p->verbose_p && status != MiscExtGrabStateSuccess)
1447 fprintf (stderr, "%s: error: XF86MiscSetGrabKeysState(%d) returned %s\n",
1449 (status == MiscExtGrabStateSuccess ? "MiscExtGrabStateSuccess" :
1450 status == MiscExtGrabStateLocked ? "MiscExtGrabStateLocked" :
1451 status == MiscExtGrabStateAlready ? "MiscExtGrabStateAlready" :
1452 status == 666 ? "an X error" :
1455 XSync (si->dpy, False);
1456 XSetErrorHandler (old_handler);
1457 XSync (si->dpy, False);
1459 #endif /* HAVE_XF86MISCSETGRABKEYSSTATE */
1463 /* This function enables and disables the C-Alt-Plus and C-Alt-Minus
1464 hot-keys, which normally change the resolution of the X server.
1465 We don't want people to be able to switch the server resolution
1466 while the screen is locked, because if they switch to a higher
1467 resolution, it could cause part of the underlying desktop to become
1470 #ifdef HAVE_XF86VMODE
1473 xfree_lock_mode_switch (saver_info *si, Bool lock_p)
1475 static Bool any_mode_locked_p = False;
1476 saver_preferences *p = &si->prefs;
1478 int real_nscreens = ScreenCount (si->dpy);
1481 XErrorHandler old_handler;
1483 if (any_mode_locked_p == lock_p)
1485 if (!XF86VidModeQueryExtension (si->dpy, &event, &error))
1488 for (screen = 0; screen < real_nscreens; screen++)
1490 XSync (si->dpy, False);
1491 old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1492 error_handler_hit_p = False;
1493 status = XF86VidModeLockModeSwitch (si->dpy, screen, lock_p);
1494 XSync (si->dpy, False);
1495 XSetErrorHandler (old_handler);
1496 if (error_handler_hit_p) status = False;
1499 any_mode_locked_p = lock_p;
1501 if (!status && (p->verbose_p || !lock_p))
1502 /* Only print this when verbose, or when we locked but can't unlock.
1503 I tried printing this message whenever it comes up, but
1504 mode-locking always fails if DontZoom is set in XF86Config. */
1505 fprintf (stderr, "%s: %d: unable to %s mode switching!\n",
1506 blurb(), screen, (lock_p ? "lock" : "unlock"));
1507 else if (p->verbose_p)
1508 fprintf (stderr, "%s: %d: %s mode switching.\n",
1509 blurb(), screen, (lock_p ? "locked" : "unlocked"));
1512 #endif /* HAVE_XF86VMODE */
1515 /* If the viewport has been scrolled since the screen was blanked,
1516 then scroll it back to where it belongs. This function only exists
1517 to patch over a very brief race condition.
1520 undo_vp_motion (saver_info *si)
1522 #ifdef HAVE_XF86VMODE
1523 saver_preferences *p = &si->prefs;
1525 int real_nscreens = ScreenCount (si->dpy);
1528 if (!XF86VidModeQueryExtension (si->dpy, &event, &error))
1531 for (screen = 0; screen < real_nscreens; screen++)
1533 saver_screen_info *ssi = &si->screens[screen];
1537 if (ssi->blank_vp_x == -1 && ssi->blank_vp_y == -1)
1539 if (!XF86VidModeGetViewPort (si->dpy, screen, &x, &y))
1541 if (ssi->blank_vp_x == x && ssi->blank_vp_y == y)
1544 /* We're going to move the viewport. The mouse has just been grabbed on
1545 (and constrained to, thus warped to) the password window, so it is no
1546 longer near the edge of the screen. However, wait a bit anyway, just
1547 to make sure the server drains its last motion event, so that the
1548 screen doesn't continue to scroll after we've reset the viewport.
1550 XSync (si->dpy, False);
1551 usleep (250000); /* 1/4 second */
1552 XSync (si->dpy, False);
1554 status = XF86VidModeSetViewPort (si->dpy, screen,
1555 ssi->blank_vp_x, ssi->blank_vp_y);
1559 "%s: %d: unable to move vp from (%d,%d) back to (%d,%d)!\n",
1560 blurb(), screen, x, y, ssi->blank_vp_x, ssi->blank_vp_y);
1561 else if (p->verbose_p)
1563 "%s: %d: vp moved to (%d,%d); moved it back to (%d,%d).\n",
1564 blurb(), screen, x, y, ssi->blank_vp_x, ssi->blank_vp_y);
1566 #endif /* HAVE_XF86VMODE */
1575 passwd_animate_timer (XtPointer closure, XtIntervalId *id)
1577 saver_info *si = (saver_info *) closure;
1579 passwd_dialog_data *pw = si->pw_data;
1583 pw->ratio -= (1.0 / ((double) si->prefs.passwd_timeout / (double) tick));
1587 if (si->unlock_state == ul_read)
1588 si->unlock_state = ul_time;
1591 update_passwd_window (si, 0, pw->ratio);
1593 if (si->unlock_state == ul_read)
1594 pw->timer = XtAppAddTimeOut (si->app, tick, passwd_animate_timer,
1599 idle_timer ((XtPointer) si, 0);
1603 static XComposeStatus *compose_status;
1606 handle_login_button (saver_info *si, XEvent *event)
1608 saver_preferences *p = &si->prefs;
1609 Bool mouse_in_box = False;
1611 passwd_dialog_data *pw = si->pw_data;
1612 saver_screen_info *ssi = pw->prompt_screen;
1614 if (! pw->login_button_enabled_p)
1618 (event->xbutton.x >= pw->login_button_x &&
1619 event->xbutton.x <= pw->login_button_x + pw->login_button_width &&
1620 event->xbutton.y >= pw->login_button_y &&
1621 event->xbutton.y <= pw->login_button_y + pw->login_button_height);
1623 if (ButtonRelease == event->xany.type &&
1624 pw->login_button_down_p &&
1627 /* Only allow them to press the button once: don't want to
1628 accidentally launch a dozen gdm choosers if the machine
1632 pw->login_button_enabled_p = False;
1635 pw->login_button_down_p = (mouse_in_box &&
1636 ButtonRelease != event->xany.type);
1638 update_passwd_window (si, 0, pw->ratio);
1641 fork_and_exec (ssi, p->new_login_command);
1646 handle_unlock_button (saver_info *si, XEvent *event)
1648 Bool mouse_in_box = False;
1649 passwd_dialog_data *pw = si->pw_data;
1652 (event->xbutton.x >= pw->unlock_button_x &&
1653 event->xbutton.x <= pw->unlock_button_x + pw->unlock_button_width &&
1654 event->xbutton.y >= pw->unlock_button_y &&
1655 event->xbutton.y <= pw->unlock_button_y + pw->unlock_button_height);
1657 if (ButtonRelease == event->xany.type &&
1658 pw->unlock_button_down_p &&
1660 finished_typing_passwd (si, pw);
1662 pw->unlock_button_down_p = (mouse_in_box &&
1663 ButtonRelease != event->xany.type);
1668 finished_typing_passwd (saver_info *si, passwd_dialog_data *pw)
1670 if (si->unlock_state == ul_read)
1672 update_passwd_window (si, "Checking...", pw->ratio);
1673 XSync (si->dpy, False);
1675 si->unlock_state = ul_finished;
1676 update_passwd_window (si, "", pw->ratio);
1681 handle_passwd_key (saver_info *si, XKeyEvent *event)
1683 passwd_dialog_data *pw = si->pw_data;
1684 int pw_size = sizeof (pw->typed_passwd) - 1;
1685 char *typed_passwd = pw->typed_passwd;
1689 int size = XLookupString (event, s, 1, 0, compose_status);
1691 if (size != 1) return;
1695 pw->passwd_changed_p = True;
1697 /* Add 10% to the time remaining every time a key is pressed. */
1699 if (pw->ratio > 1) pw->ratio = 1;
1703 case '\010': case '\177': /* Backspace */
1707 typed_passwd [strlen(typed_passwd)-1] = 0;
1710 case '\025': case '\030': /* Erase line */
1711 memset (typed_passwd, 0, pw_size);
1714 case '\012': case '\015': /* Enter */
1715 finished_typing_passwd(si, pw);
1718 case '\033': /* Escape */
1719 si->unlock_state = ul_cancel;
1723 /* Though technically the only illegal characters in Unix passwords
1724 are LF and NUL, most GUI programs (e.g., GDM) use regular text-entry
1725 fields that only let you type printable characters. So, people
1726 who use funky characters in their passwords are already broken.
1727 We follow that precedent.
1729 if (isprint ((unsigned char) *s))
1731 i = strlen (typed_passwd);
1736 typed_passwd [i] = *s;
1737 typed_passwd [i+1] = 0;
1747 /* If the input is wider than the text box, only show the last portion.
1748 * This simulates a horizontally scrolling text field. */
1749 int chars_in_pwfield = (pw->passwd_field_width /
1750 pw->passwd_font->max_bounds.width);
1752 if (strlen(typed_passwd) > chars_in_pwfield)
1753 typed_passwd += (strlen(typed_passwd) - chars_in_pwfield);
1755 update_passwd_window(si, typed_passwd, pw->ratio);
1757 else if (pw->show_stars_p)
1759 i = strlen(typed_passwd);
1760 stars = (char *) malloc(i+1);
1761 memset (stars, '*', i);
1763 update_passwd_window (si, stars, pw->ratio);
1768 update_passwd_window (si, "", pw->ratio);
1774 passwd_event_loop (saver_info *si)
1776 saver_preferences *p = &si->prefs;
1780 passwd_animate_timer ((XtPointer) si, 0);
1782 while (si->unlock_state == ul_read)
1784 XtAppNextEvent (si->app, &event);
1785 if (event.xany.window == si->passwd_dialog && event.xany.type == Expose)
1786 draw_passwd_window (si);
1787 else if (event.xany.type == KeyPress)
1789 handle_passwd_key (si, &event.xkey);
1790 si->pw_data->caps_p = (event.xkey.state & LockMask);
1792 else if (event.xany.type == ButtonPress ||
1793 event.xany.type == ButtonRelease)
1795 si->pw_data->button_state_changed_p = True;
1796 handle_unlock_button (si, &event);
1797 if (si->pw_data->login_button_p)
1798 handle_login_button (si, &event);
1801 XtDispatchEvent (&event);
1804 switch (si->unlock_state)
1806 case ul_cancel: msg = ""; break;
1807 case ul_time: msg = "Timed out!"; break;
1808 case ul_finished: msg = "Checking..."; break;
1809 default: msg = 0; break;
1813 switch (si->unlock_state) {
1815 fprintf (stderr, "%s: input cancelled.\n", blurb()); break;
1817 fprintf (stderr, "%s: input timed out.\n", blurb()); break;
1819 fprintf (stderr, "%s: input finished.\n", blurb()); break;
1825 si->pw_data->i_beam = 0;
1826 update_passwd_window (si, msg, 0.0);
1827 XSync (si->dpy, False);
1829 /* Swallow all pending KeyPress/KeyRelease events. */
1832 while (XCheckMaskEvent (si->dpy, KeyPressMask|KeyReleaseMask, &e))
1840 handle_typeahead (saver_info *si)
1842 passwd_dialog_data *pw = si->pw_data;
1844 if (!si->unlock_typeahead)
1847 pw->passwd_changed_p = True;
1849 i = strlen (si->unlock_typeahead);
1850 if (i >= sizeof(pw->typed_passwd) - 1)
1851 i = sizeof(pw->typed_passwd) - 1;
1853 memcpy (pw->typed_passwd, si->unlock_typeahead, i);
1854 pw->typed_passwd [i] = 0;
1856 memset (si->unlock_typeahead, '*', strlen(si->unlock_typeahead));
1857 si->unlock_typeahead[i] = 0;
1858 update_passwd_window (si, si->unlock_typeahead, pw->ratio);
1860 free (si->unlock_typeahead);
1861 si->unlock_typeahead = 0;
1866 * Returns a copy of the input string with trailing whitespace removed.
1867 * Whitespace is anything considered so by isspace().
1868 * It is safe to call this with NULL, in which case NULL will be returned.
1869 * The returned string (if not NULL) should be freed by the caller with free().
1872 remove_trailing_whitespace(const char *str)
1882 newstr = malloc(len + 1);
1883 (void) strcpy(newstr, str);
1889 while (isspace(*--chr) && chr >= newstr)
1897 * The authentication conversation function.
1898 * Like a PAM conversation function, this accepts multiple messages in a single
1899 * round. It then splits them into individual messages for display on the
1900 * passwd dialog. A message sequence of info or error followed by a prompt will
1901 * be reduced into a single dialog window.
1903 * Returns 0 on success or -1 if some problem occurred (cancelled auth, OOM, ...)
1906 gui_auth_conv(int num_msg,
1907 const struct auth_message auth_msgs[],
1908 struct auth_response **resp,
1912 const char *info_msg, *prompt;
1913 struct auth_response *responses;
1915 if (si->unlock_state == ul_cancel ||
1916 si->unlock_state == ul_time)
1917 /* If we've already cancelled or timed out in this PAM conversation,
1918 don't prompt again even if PAM asks us to! */
1921 if (!(responses = calloc(num_msg, sizeof(struct auth_response))))
1924 for (i = 0; i < num_msg; ++i)
1926 info_msg = prompt = NULL;
1928 /* See if there is a following message that can be shown at the same
1930 if (auth_msgs[i].type == AUTH_MSGTYPE_INFO
1932 && ( auth_msgs[i+1].type == AUTH_MSGTYPE_PROMPT_NOECHO
1933 || auth_msgs[i+1].type == AUTH_MSGTYPE_PROMPT_ECHO)
1936 info_msg = auth_msgs[i].msg;
1937 prompt = auth_msgs[++i].msg;
1941 if ( auth_msgs[i].type == AUTH_MSGTYPE_INFO
1942 || auth_msgs[i].type == AUTH_MSGTYPE_ERROR)
1943 info_msg = auth_msgs[i].msg;
1945 prompt = auth_msgs[i].msg;
1949 char *info_msg_trimmed, *prompt_trimmed;
1951 /* Trailing whitespace looks bad in a GUI */
1952 info_msg_trimmed = remove_trailing_whitespace(info_msg);
1953 prompt_trimmed = remove_trailing_whitespace(prompt);
1955 if (make_passwd_window(si, info_msg_trimmed, prompt_trimmed,
1956 auth_msgs[i].type == AUTH_MSGTYPE_PROMPT_ECHO
1961 if (info_msg_trimmed)
1962 free(info_msg_trimmed);
1965 free(prompt_trimmed);
1968 compose_status = calloc (1, sizeof (*compose_status));
1969 if (!compose_status)
1972 si->unlock_state = ul_read;
1974 handle_typeahead (si);
1975 passwd_event_loop (si);
1977 if (si->unlock_state == ul_cancel)
1980 responses[i].response = strdup(si->pw_data->typed_passwd);
1982 /* Cache the first response to a PROMPT_NOECHO to save prompting for
1983 * each auth mechanism. */
1984 if (si->cached_passwd == NULL &&
1985 auth_msgs[i].type == AUTH_MSGTYPE_PROMPT_NOECHO)
1986 si->cached_passwd = strdup(responses[i].response);
1988 free (compose_status);
1994 return (si->unlock_state == ul_finished) ? 0 : -1;
1998 free (compose_status);
2002 for (i = 0; i < num_msg; ++i)
2003 if (responses[i].response)
2004 free (responses[i].response);
2013 auth_finished_cb (saver_info *si)
2018 /* If we have something to say, put the dialog back up for a few seconds
2019 to display it. Otherwise, don't bother.
2022 if (si->unlock_state == ul_fail && /* failed with caps lock on */
2023 si->pw_data && si->pw_data->caps_p)
2024 s = "Authentication failed (Caps Lock?)";
2025 else if (si->unlock_state == ul_fail) /* failed without caps lock */
2026 s = "Authentication failed!";
2027 else if (si->unlock_state == ul_success && /* good, but report failures */
2028 si->unlock_failures > 0)
2030 if (si->unlock_failures == 1)
2031 s = "There has been\n1 failed login attempt.";
2034 sprintf (buf, "There have been\n%d failed login attempts.",
2035 si->unlock_failures);
2038 si->unlock_failures = 0;
2040 else /* good, with no failures, */
2041 goto END; /* or timeout, or cancel. */
2043 make_passwd_window (si, s, NULL, True);
2044 XSync (si->dpy, False);
2048 time_t start = time ((time_t *) 0);
2050 while (time ((time_t *) 0) < start + secs)
2051 if (XPending (si->dpy))
2053 XNextEvent (si->dpy, &event);
2054 if (event.xany.window == si->passwd_dialog &&
2055 event.xany.type == Expose)
2056 draw_passwd_window (si);
2057 else if (event.xany.type == ButtonPress ||
2058 event.xany.type == KeyPress)
2060 XSync (si->dpy, False);
2063 usleep (250000); /* 1/4 second */
2068 destroy_passwd_window (si);
2073 unlock_p (saver_info *si)
2075 saver_preferences *p = &si->prefs;
2079 fprintf(stderr, "%s: Error: no unlock function specified!\n", blurb());
2083 raise_window (si, True, True, True);
2085 xss_authenticate(si, p->verbose_p);
2087 return (si->unlock_state == ul_success);
2092 set_locked_p (saver_info *si, Bool locked_p)
2094 si->locked_p = locked_p;
2096 #ifdef HAVE_XHPDISABLERESET
2097 hp_lock_reset (si, locked_p); /* turn off/on C-Sh-Reset */
2099 #ifdef HAVE_XF86VMODE
2100 xfree_lock_mode_switch (si, locked_p); /* turn off/on C-Alt-Plus */
2102 #ifdef HAVE_XF86MISCSETGRABKEYSSTATE
2103 xfree_lock_grab_smasher (si, locked_p); /* turn off/on C-Alt-KP-*,/ */
2106 store_saver_status (si); /* store locked-p */
2110 #else /* NO_LOCKING -- whole file */
2113 set_locked_p (saver_info *si, Bool locked_p)
2115 if (locked_p) abort();
2118 #endif /* !NO_LOCKING */