1 /* lock.c --- handling the password dialog for locking-mode.
2 * xscreensaver, Copyright (c) 1993-2011 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 */
49 # include <X11/extensions/Xrandr.h>
50 #endif /* HAVE_RANDR */
53 ERROR! You must not include vroot.h in this file.
57 # include <sys/utsname.h> /* for hostname info */
58 #endif /* HAVE_UNAME */
65 extern char *getenv(const char *name);
66 extern int validate_user(char *name, char *password);
69 vms_passwd_valid_p(char *pw, Bool verbose_p)
71 return (validate_user (getenv("USER"), typed_passwd) == 1);
73 # undef passwd_valid_p
74 # define passwd_valid_p vms_passwd_valid_p
78 #define SAMPLE_INPUT "MMMMMMMMMMMM"
82 #define MAX(a,b) ((a)>(b)?(a):(b))
84 typedef struct info_dialog_data info_dialog_data;
87 #define MAX_BYTES_PER_CHAR 8 /* UTF-8 uses no more than 3, I think */
88 #define MAX_PASSWD_CHARS 128 /* Longest possible passphrase */
90 struct passwd_dialog_data {
92 saver_screen_info *prompt_screen;
93 int previous_mouse_x, previous_mouse_y;
95 /* "Characters" in the password may be a variable number of bytes long.
96 typed_passwd contains the raw bytes.
97 typed_passwd_char_size indicates the size in bytes of each character,
98 so that we can make backspace work.
100 char typed_passwd [MAX_PASSWD_CHARS * MAX_BYTES_PER_CHAR];
101 char typed_passwd_char_size [MAX_PASSWD_CHARS];
110 Dimension border_width;
113 Bool show_stars_p; /* "I regret that I have but one asterisk for my country."
114 -- Nathan Hale, 1776. */
119 mlstring *info_label;
120 /* The entry field shall only be displayed if prompt_label is not NULL */
121 mlstring *prompt_label;
124 Bool passwd_changed_p; /* Whether the user entry field needs redrawing */
125 Bool caps_p; /* Whether we saw a keypress with caps-lock on */
132 XFontStruct *heading_font;
133 XFontStruct *body_font;
134 XFontStruct *label_font;
135 XFontStruct *passwd_font;
136 XFontStruct *date_font;
137 XFontStruct *button_font;
138 XFontStruct *uname_font;
143 Pixel passwd_foreground;
144 Pixel passwd_background;
145 Pixel thermo_foreground;
146 Pixel thermo_background;
149 Pixel button_foreground;
150 Pixel button_background;
152 Dimension preferred_logo_width, logo_width;
153 Dimension preferred_logo_height, logo_height;
154 Dimension thermo_width;
155 Dimension internal_border;
156 Dimension shadow_width;
158 Dimension passwd_field_x, passwd_field_y;
159 Dimension passwd_field_width, passwd_field_height;
161 Dimension unlock_button_x, unlock_button_y;
162 Dimension unlock_button_width, unlock_button_height;
164 Dimension login_button_x, login_button_y;
165 Dimension login_button_width, login_button_height;
167 Dimension thermo_field_x, thermo_field_y;
168 Dimension thermo_field_height;
171 Pixmap logo_clipmask;
173 unsigned long *logo_pixels;
175 Cursor passwd_cursor;
176 Bool unlock_button_down_p;
177 Bool login_button_down_p;
179 Bool login_button_enabled_p;
180 Bool button_state_changed_p; /* Refers to both buttons */
183 Pixmap user_entry_pixmap;
186 static void draw_passwd_window (saver_info *si);
187 static void update_passwd_window (saver_info *si, const char *printed_passwd,
189 static void destroy_passwd_window (saver_info *si);
190 static void undo_vp_motion (saver_info *si);
191 static void finished_typing_passwd (saver_info *si, passwd_dialog_data *pw);
192 static void cleanup_passwd_window (saver_info *si);
193 static void restore_background (saver_info *si);
195 extern void xss_authenticate(saver_info *si, Bool verbose_p);
198 new_passwd_window (saver_info *si)
200 passwd_dialog_data *pw;
204 saver_screen_info *ssi = &si->screens [mouse_screen (si)];
206 pw = (passwd_dialog_data *) calloc (1, sizeof(*pw));
210 /* Display the button only if the "newLoginCommand" pref is non-null.
212 pw->login_button_p = (si->prefs.new_login_command &&
213 *si->prefs.new_login_command);
215 pw->passwd_cursor = XCreateFontCursor (si->dpy, XC_top_left_arrow);
217 pw->prompt_screen = ssi;
219 screen = pw->prompt_screen->screen;
220 cmap = DefaultColormapOfScreen (screen);
222 pw->show_stars_p = get_boolean_resource(si->dpy, "passwd.asterisks",
225 pw->heading_label = get_string_resource (si->dpy, "passwd.heading.label",
226 "Dialog.Label.Label");
227 pw->body_label = get_string_resource (si->dpy, "passwd.body.label",
228 "Dialog.Label.Label");
229 pw->user_label = get_string_resource (si->dpy, "passwd.user.label",
230 "Dialog.Label.Label");
231 pw->unlock_label = get_string_resource (si->dpy, "passwd.unlock.label",
232 "Dialog.Button.Label");
233 pw->login_label = get_string_resource (si->dpy, "passwd.login.label",
234 "Dialog.Button.Label");
236 pw->date_label = get_string_resource (si->dpy, "dateFormat", "DateFormat");
238 if (!pw->heading_label)
239 pw->heading_label = strdup("ERROR: RESOURCES NOT INSTALLED CORRECTLY");
241 pw->body_label = strdup("ERROR: RESOURCES NOT INSTALLED CORRECTLY");
242 if (!pw->user_label) pw->user_label = strdup("ERROR");
243 if (!pw->date_label) pw->date_label = strdup("ERROR");
244 if (!pw->unlock_label) pw->unlock_label = strdup("ERROR (UNLOCK)");
245 if (!pw->login_label) pw->login_label = strdup ("ERROR (LOGIN)") ;
247 /* Put the version number in the label. */
249 char *s = (char *) malloc (strlen(pw->heading_label) + 20);
250 sprintf(s, pw->heading_label, si->version);
251 free (pw->heading_label);
252 pw->heading_label = s;
255 /* Get hostname info */
256 pw->uname_label = strdup(""); /* Initialy, write nothing */
262 if (uname (&uts) == 0)
264 #if 0 /* Get the full hostname */
267 if ((s = strchr(uts.nodename, '.')))
271 char *s = strdup (uts.nodename);
272 free (pw->uname_label);
278 pw->passwd_string = strdup("");
280 f = get_string_resource (si->dpy, "passwd.headingFont", "Dialog.Font");
281 pw->heading_font = XLoadQueryFont (si->dpy, (f ? f : "fixed"));
282 if (!pw->heading_font) pw->heading_font = XLoadQueryFont (si->dpy, "fixed");
285 f = get_string_resource (si->dpy, "passwd.buttonFont", "Dialog.Font");
286 pw->button_font = XLoadQueryFont (si->dpy, (f ? f : "fixed"));
287 if (!pw->button_font) pw->button_font = XLoadQueryFont (si->dpy, "fixed");
290 f = get_string_resource(si->dpy, "passwd.bodyFont", "Dialog.Font");
291 pw->body_font = XLoadQueryFont (si->dpy, (f ? f : "fixed"));
292 if (!pw->body_font) pw->body_font = XLoadQueryFont (si->dpy, "fixed");
295 f = get_string_resource(si->dpy, "passwd.labelFont", "Dialog.Font");
296 pw->label_font = XLoadQueryFont (si->dpy, (f ? f : "fixed"));
297 if (!pw->label_font) pw->label_font = XLoadQueryFont (si->dpy, "fixed");
300 f = get_string_resource(si->dpy, "passwd.passwdFont", "Dialog.Font");
301 pw->passwd_font = XLoadQueryFont (si->dpy, (f ? f : "fixed"));
302 if (!pw->passwd_font) pw->passwd_font = XLoadQueryFont (si->dpy, "fixed");
305 f = get_string_resource(si->dpy, "passwd.dateFont", "Dialog.Font");
306 pw->date_font = XLoadQueryFont (si->dpy, (f ? f : "fixed"));
307 if (!pw->date_font) pw->date_font = XLoadQueryFont (si->dpy, "fixed");
310 f = get_string_resource(si->dpy, "passwd.unameFont", "Dialog.Font");
311 pw->uname_font = XLoadQueryFont (si->dpy, (f ? f : "fixed"));
312 if (!pw->uname_font) pw->uname_font = XLoadQueryFont (si->dpy, "fixed");
315 pw->show_uname_p = get_boolean_resource(si->dpy, "passwd.uname", "Boolean");
317 pw->foreground = get_pixel_resource (si->dpy, cmap,
319 "Dialog.Foreground" );
320 pw->background = get_pixel_resource (si->dpy, cmap,
322 "Dialog.Background" );
323 pw->border = get_pixel_resource (si->dpy, cmap,
324 "passwd.borderColor",
325 "Dialog.borderColor");
327 if (pw->foreground == pw->background)
329 /* Make sure the error messages show up. */
330 pw->foreground = BlackPixelOfScreen (screen);
331 pw->background = WhitePixelOfScreen (screen);
334 pw->passwd_foreground = get_pixel_resource (si->dpy, cmap,
335 "passwd.text.foreground",
336 "Dialog.Text.Foreground" );
337 pw->passwd_background = get_pixel_resource (si->dpy, cmap,
338 "passwd.text.background",
339 "Dialog.Text.Background" );
340 pw->button_foreground = get_pixel_resource (si->dpy, cmap,
341 "splash.Button.foreground",
342 "Dialog.Button.Foreground" );
343 pw->button_background = get_pixel_resource (si->dpy, cmap,
344 "splash.Button.background",
345 "Dialog.Button.Background" );
346 pw->thermo_foreground = get_pixel_resource (si->dpy, cmap,
347 "passwd.thermometer.foreground",
348 "Dialog.Thermometer.Foreground");
349 pw->thermo_background = get_pixel_resource ( si->dpy, cmap,
350 "passwd.thermometer.background",
351 "Dialog.Thermometer.Background");
352 pw->shadow_top = get_pixel_resource ( si->dpy, cmap,
353 "passwd.topShadowColor",
354 "Dialog.Foreground" );
355 pw->shadow_bottom = get_pixel_resource (si->dpy, cmap,
356 "passwd.bottomShadowColor",
357 "Dialog.Background" );
359 pw->preferred_logo_width = get_integer_resource (si->dpy,
361 "Dialog.Logo.Width");
362 pw->preferred_logo_height = get_integer_resource (si->dpy,
363 "passwd.logo.height",
364 "Dialog.Logo.Height");
365 pw->thermo_width = get_integer_resource (si->dpy, "passwd.thermometer.width",
366 "Dialog.Thermometer.Width");
367 pw->internal_border = get_integer_resource (si->dpy,
368 "passwd.internalBorderWidth",
369 "Dialog.InternalBorderWidth");
370 pw->shadow_width = get_integer_resource (si->dpy, "passwd.shadowThickness",
371 "Dialog.ShadowThickness");
373 if (pw->preferred_logo_width == 0) pw->preferred_logo_width = 150;
374 if (pw->preferred_logo_height == 0) pw->preferred_logo_height = 150;
375 if (pw->internal_border == 0) pw->internal_border = 15;
376 if (pw->shadow_width == 0) pw->shadow_width = 4;
377 if (pw->thermo_width == 0) pw->thermo_width = pw->shadow_width;
380 /* We need to remember the mouse position and restore it afterward, or
381 sometimes (perhaps only with Xinerama?) the mouse gets warped to
382 inside the bounds of the lock dialog window.
385 Window pointer_root, pointer_child;
386 int root_x, root_y, win_x, win_y;
388 pw->previous_mouse_x = 0;
389 pw->previous_mouse_y = 0;
390 if (XQueryPointer (si->dpy, RootWindowOfScreen (pw->prompt_screen->screen),
391 &pointer_root, &pointer_child,
392 &root_x, &root_y, &win_x, &win_y, &mask))
394 pw->previous_mouse_x = root_x;
395 pw->previous_mouse_y = root_y;
396 if (si->prefs.verbose_p)
397 fprintf (stderr, "%s: %d: mouse is at %d,%d.\n",
398 blurb(), pw->prompt_screen->number,
399 pw->previous_mouse_x, pw->previous_mouse_y);
401 else if (si->prefs.verbose_p)
402 fprintf (stderr, "%s: %d: unable to determine mouse position?\n",
403 blurb(), pw->prompt_screen->number);
406 /* Before mapping the window, save a pixmap of the current screen.
407 When we lower the window, we restore these bits. This works,
408 because the running screenhack has already been sent SIGSTOP, so
409 we know nothing else is drawing right now! */
413 pw->save_under = XCreatePixmap (si->dpy,
414 pw->prompt_screen->screensaver_window,
415 pw->prompt_screen->width,
416 pw->prompt_screen->height,
417 pw->prompt_screen->current_depth);
418 gcv.function = GXcopy;
419 gc = XCreateGC (si->dpy, pw->save_under, GCFunction, &gcv);
420 XCopyArea (si->dpy, pw->prompt_screen->screensaver_window,
423 pw->prompt_screen->width, pw->prompt_screen->height,
425 XFreeGC (si->dpy, gc);
433 Bool debug_passwd_window_p = False; /* used only by test-passwd.c */
437 * info_msg and prompt may be NULL.
440 make_passwd_window (saver_info *si,
441 const char *info_msg,
445 XSetWindowAttributes attrs;
446 unsigned long attrmask = 0;
447 passwd_dialog_data *pw;
450 Dimension max_string_width_px;
451 saver_screen_info *ssi = &si->screens [mouse_screen (si)];
453 cleanup_passwd_window (si);
455 if (! ssi) /* WTF? Trying to prompt while no screens connected? */
459 if (new_passwd_window (si) < 0)
462 if (!(pw = si->pw_data))
467 pw->prompt_screen = ssi;
468 if (si->prefs.verbose_p)
469 fprintf (stderr, "%s: %d: creating password dialog (\"%s\")\n",
470 blurb(), pw->prompt_screen->number,
471 info_msg ? info_msg : "");
473 screen = pw->prompt_screen->screen;
474 cmap = DefaultColormapOfScreen (screen);
476 pw->echo_input = echo;
478 max_string_width_px = ssi->width
479 - pw->shadow_width * 4
480 - pw->border_width * 2
482 - pw->preferred_logo_width
483 - pw->internal_border * 2;
484 /* As the string wraps it makes the window taller which makes the logo wider
485 * which leaves less room for the text which makes the string wrap. Uh-oh, a
486 * loop. By wrapping at a bit less than the available width, there's some
487 * room for the dialog to grow without going off the edge of the screen. */
488 max_string_width_px *= 0.75;
490 pw->info_label = mlstring_new(info_msg ? info_msg : pw->body_label,
491 pw->label_font, max_string_width_px);
494 int direction, ascent, descent;
500 /* Measure the heading_label. */
501 XTextExtents (pw->heading_font,
502 pw->heading_label, strlen(pw->heading_label),
503 &direction, &ascent, &descent, &overall);
504 if (overall.width > pw->width) pw->width = overall.width;
505 pw->height += ascent + descent;
507 /* Measure the uname_label. */
508 if ((strlen(pw->uname_label)) && pw->show_uname_p)
510 XTextExtents (pw->uname_font,
511 pw->uname_label, strlen(pw->uname_label),
512 &direction, &ascent, &descent, &overall);
513 if (overall.width > pw->width) pw->width = overall.width;
514 pw->height += ascent + descent;
518 Dimension w2 = 0, w3 = 0, button_w = 0;
519 Dimension h2 = 0, h3 = 0, button_h = 0;
520 const char *passwd_string = SAMPLE_INPUT;
522 /* Measure the user_label. */
523 XTextExtents (pw->label_font,
524 pw->user_label, strlen(pw->user_label),
525 &direction, &ascent, &descent, &overall);
526 if (overall.width > w2) w2 = overall.width;
527 h2 += ascent + descent;
529 /* Measure the info_label. */
530 if (pw->info_label->overall_width > pw->width)
531 pw->width = pw->info_label->overall_width;
532 h2 += pw->info_label->overall_height;
534 /* Measure the user string. */
535 XTextExtents (pw->passwd_font,
536 si->user, strlen(si->user),
537 &direction, &ascent, &descent, &overall);
538 overall.width += (pw->shadow_width * 4);
539 ascent += (pw->shadow_width * 4);
540 if (overall.width > w3) w3 = overall.width;
541 h3 += ascent + descent;
543 /* Measure the (dummy) passwd_string. */
546 XTextExtents (pw->passwd_font,
547 passwd_string, strlen(passwd_string),
548 &direction, &ascent, &descent, &overall);
549 overall.width += (pw->shadow_width * 4);
550 ascent += (pw->shadow_width * 4);
551 if (overall.width > w3) w3 = overall.width;
552 h3 += ascent + descent;
554 /* Measure the prompt_label. */
555 max_string_width_px -= w3;
556 pw->prompt_label = mlstring_new (prompt, pw->label_font,
557 max_string_width_px);
559 if (pw->prompt_label->overall_width > w2)
560 w2 = pw->prompt_label->overall_width;
562 h2 += pw->prompt_label->overall_height;
564 w2 = w2 + w3 + (pw->shadow_width * 2);
568 /* The "Unlock" button. */
569 XTextExtents (pw->label_font,
570 pw->unlock_label, strlen(pw->unlock_label),
571 &direction, &ascent, &descent, &overall);
572 button_w = overall.width;
573 button_h = ascent + descent;
575 /* Add some horizontal padding inside the button. */
578 button_w += ((ascent + descent) / 2) + (pw->shadow_width * 2);
579 button_h += ((ascent + descent) / 2) + (pw->shadow_width * 2);
581 pw->unlock_button_width = button_w;
582 pw->unlock_button_height = button_h;
584 w2 = MAX (w2, button_w);
585 h2 += button_h * 1.5;
587 /* The "New Login" button */
588 pw->login_button_width = 0;
589 pw->login_button_height = 0;
591 if (pw->login_button_p)
593 pw->login_button_enabled_p = True;
595 /* Measure the "New Login" button */
596 XTextExtents (pw->button_font, pw->login_label,
597 strlen (pw->login_label),
598 &direction, &ascent, &descent, &overall);
599 button_w = overall.width;
600 button_h = ascent + descent;
602 /* Add some horizontal padding inside the buttons. */
605 button_w += ((ascent + descent) / 2) + (pw->shadow_width * 2);
606 button_h += ((ascent + descent) / 2) + (pw->shadow_width * 2);
608 pw->login_button_width = button_w;
609 pw->login_button_height = button_h;
611 if (button_h > pw->unlock_button_height)
612 h2 += (button_h * 1.5 - pw->unlock_button_height * 1.5);
614 /* Use (2 * shadow_width) spacing between the buttons. Another
615 (2 * shadow_width) is required to account for button shadows. */
617 button_w + pw->unlock_button_width +
618 (pw->shadow_width * 4));
621 if (w2 > pw->width) pw->width = w2;
625 pw->width += (pw->internal_border * 2);
626 pw->height += (pw->internal_border * 4);
628 pw->width += pw->thermo_width + (pw->shadow_width * 3);
630 if (pw->preferred_logo_height > pw->height)
631 pw->height = pw->logo_height = pw->preferred_logo_height;
632 else if (pw->height > pw->preferred_logo_height)
633 pw->logo_height = pw->height;
635 pw->logo_width = pw->logo_height;
637 pw->width += pw->logo_width;
640 attrmask |= CWOverrideRedirect; attrs.override_redirect = True;
642 if (debug_passwd_window_p)
643 attrs.override_redirect = False; /* kludge for test-passwd.c */
645 attrmask |= CWEventMask;
646 attrs.event_mask = (ExposureMask | KeyPressMask |
647 ButtonPressMask | ButtonReleaseMask);
649 /* Figure out where on the desktop to place the window so that it will
650 actually be visible; this takes into account virtual viewports as
653 saver_screen_info *ssi = &si->screens [mouse_screen (si)];
658 if (si->prefs.debug_p) w /= 2;
659 pw->x = x + ((w + pw->width) / 2) - pw->width;
660 pw->y = y + ((h + pw->height) / 2) - pw->height;
661 if (pw->x < x) pw->x = x;
662 if (pw->y < y) pw->y = y;
665 pw->border_width = get_integer_resource (si->dpy, "passwd.borderWidth",
666 "Dialog.BorderWidth");
668 /* Only create the window the first time around */
669 if (!si->passwd_dialog)
672 XCreateWindow (si->dpy,
673 RootWindowOfScreen(screen),
674 pw->x, pw->y, pw->width, pw->height, pw->border_width,
675 DefaultDepthOfScreen (screen), InputOutput,
676 DefaultVisualOfScreen(screen),
678 XSetWindowBackground (si->dpy, si->passwd_dialog, pw->background);
679 XSetWindowBorder (si->dpy, si->passwd_dialog, pw->border);
681 /* We use the default visual, not ssi->visual, so that the logo pixmap's
682 visual matches that of the si->passwd_dialog window. */
683 pw->logo_pixmap = xscreensaver_logo (ssi->screen,
684 /* ssi->current_visual, */
685 DefaultVisualOfScreen(screen),
686 si->passwd_dialog, cmap,
688 &pw->logo_pixels, &pw->logo_npixels,
689 &pw->logo_clipmask, True);
691 else /* On successive prompts, just resize the window */
694 unsigned int mask = CWX | CWY | CWWidth | CWHeight;
698 wc.width = pw->width;
699 wc.height = pw->height;
701 XConfigureWindow (si->dpy, si->passwd_dialog, mask, &wc);
704 restore_background(si);
706 XMapRaised (si->dpy, si->passwd_dialog);
707 XSync (si->dpy, False);
709 move_mouse_grab (si, si->passwd_dialog,
711 pw->prompt_screen->number);
717 XInstallColormap (si->dpy, cmap);
718 draw_passwd_window (si);
725 draw_passwd_window (saver_info *si)
727 passwd_dialog_data *pw = si->pw_data;
731 int x1, x2, x3, y1, y2;
736 pw->passwd_changed_p = True;
737 pw->button_state_changed_p = True;
739 /* This height is the height of all the elements, not to be confused with
740 * the overall window height which is pw->height. It is used to compute
741 * the amount of spacing (padding) between elements. */
742 height = (pw->heading_font->ascent + pw->heading_font->descent +
743 pw->info_label->overall_height +
744 MAX (((pw->label_font->ascent + pw->label_font->descent) +
745 (pw->prompt_label ? pw->prompt_label->overall_height : 0)),
746 ((pw->passwd_font->ascent + pw->passwd_font->descent) +
747 (pw->shadow_width * 2)) * (pw->prompt_label ? 2 : 1)) +
748 pw->date_font->ascent + pw->date_font->descent);
750 if ((strlen(pw->uname_label)) && pw->show_uname_p)
751 height += (pw->uname_font->ascent + pw->uname_font->descent);
753 height += ((pw->button_font->ascent + pw->button_font->descent) * 2 +
754 2 * pw->shadow_width);
756 spacing = ((pw->height - 2 * pw->shadow_width
757 - pw->internal_border - height)
760 if (spacing < 0) spacing = 0;
762 gcv.foreground = pw->foreground;
763 gc1 = XCreateGC (si->dpy, si->passwd_dialog, GCForeground, &gcv);
764 gc2 = XCreateGC (si->dpy, si->passwd_dialog, GCForeground, &gcv);
765 x1 = pw->logo_width + pw->thermo_width + (pw->shadow_width * 3);
766 x3 = pw->width - (pw->shadow_width * 2);
767 y1 = (pw->shadow_width * 2) + spacing + spacing;
771 XSetFont (si->dpy, gc1, pw->heading_font->fid);
772 sw = string_width (pw->heading_font, pw->heading_label);
773 x2 = (x1 + ((x3 - x1 - sw) / 2));
774 y1 += spacing + pw->heading_font->ascent + pw->heading_font->descent;
775 XDrawString (si->dpy, si->passwd_dialog, gc1, x2, y1,
776 pw->heading_label, strlen(pw->heading_label));
778 /* uname below top heading
780 if ((strlen(pw->uname_label)) && pw->show_uname_p)
782 XSetFont (si->dpy, gc1, pw->uname_font->fid);
783 y1 += spacing + pw->uname_font->ascent + pw->uname_font->descent;
784 sw = string_width (pw->uname_font, pw->uname_label);
785 x2 = (x1 + ((x3 - x1 - sw) / 2));
786 XDrawString (si->dpy, si->passwd_dialog, gc1, x2, y1,
787 pw->uname_label, strlen(pw->uname_label));
790 /* the info_label (below uname)
792 x2 = (x1 + ((x3 - x1 - pw->info_label->overall_width) / 2));
793 y1 += spacing + pw->info_label->font_height / 2;
794 mlstring_draw(si->dpy, si->passwd_dialog, gc1, pw->info_label,
796 y1 += pw->info_label->overall_height;
799 tb_height = (pw->passwd_font->ascent + pw->passwd_font->descent +
800 (pw->shadow_width * 4));
802 /* the "User:" prompt
805 XSetForeground (si->dpy, gc1, pw->foreground);
806 XSetFont (si->dpy, gc1, pw->label_font->fid);
807 y1 += (spacing + tb_height + pw->shadow_width);
808 x2 = (x1 + pw->internal_border +
809 MAX(string_width (pw->label_font, pw->user_label),
810 pw->prompt_label ? pw->prompt_label->overall_width : 0));
811 XDrawString (si->dpy, si->passwd_dialog, gc1,
812 x2 - string_width (pw->label_font, pw->user_label),
813 y1 - pw->passwd_font->descent,
814 pw->user_label, strlen(pw->user_label));
816 /* the prompt_label prompt
818 if (pw->prompt_label)
820 y1 += tb_height - pw->label_font->ascent + pw->shadow_width;
821 mlstring_draw(si->dpy, si->passwd_dialog, gc1, pw->prompt_label,
822 x2 - pw->prompt_label->overall_width, y1);
825 /* the "user name" text field
828 XSetForeground (si->dpy, gc1, pw->passwd_foreground);
829 XSetForeground (si->dpy, gc2, pw->passwd_background);
830 XSetFont (si->dpy, gc1, pw->passwd_font->fid);
831 y1 += (spacing + tb_height);
832 x2 += (pw->shadow_width * 4);
834 pw->passwd_field_width = x3 - x2 - pw->internal_border;
835 pw->passwd_field_height = (pw->passwd_font->ascent +
836 pw->passwd_font->descent +
839 XFillRectangle (si->dpy, si->passwd_dialog, gc2,
840 x2 - pw->shadow_width,
841 y1 - (pw->passwd_font->ascent + pw->passwd_font->descent),
842 pw->passwd_field_width, pw->passwd_field_height);
843 XDrawString (si->dpy, si->passwd_dialog, gc1,
845 y1 - pw->passwd_font->descent,
846 si->user, strlen(si->user));
848 /* the password/prompt text field
850 if (pw->prompt_label)
852 y1 += (spacing + pw->prompt_label->overall_height + pw->shadow_width*2);
854 pw->passwd_field_x = x2 - pw->shadow_width;
855 pw->passwd_field_y = y1 - (pw->passwd_font->ascent +
856 pw->passwd_font->descent);
859 /* The shadow around the text fields
862 y1 += (spacing + (pw->shadow_width * 3));
863 x1 = x2 - (pw->shadow_width * 2);
864 x2 = pw->passwd_field_width + (pw->shadow_width * 2);
865 y2 = pw->passwd_field_height + (pw->shadow_width * 2);
867 draw_shaded_rectangle (si->dpy, si->passwd_dialog,
870 pw->shadow_bottom, pw->shadow_top);
872 if (pw->prompt_label)
874 y1 += (spacing + pw->prompt_label->overall_height + pw->shadow_width*2);
875 draw_shaded_rectangle (si->dpy, si->passwd_dialog,
878 pw->shadow_bottom, pw->shadow_top);
882 /* The date, below the text fields
886 time_t now = time ((time_t *) 0);
887 struct tm *tm = localtime (&now);
888 memset (buf, 0, sizeof(buf));
889 strftime (buf, sizeof(buf)-1, pw->date_label, tm);
891 XSetFont (si->dpy, gc1, pw->date_font->fid);
892 y1 += pw->shadow_width;
893 y1 += (spacing + tb_height);
895 sw = string_width (pw->date_font, buf);
897 XDrawString (si->dpy, si->passwd_dialog, gc1, x2, y1, buf, strlen(buf));
900 /* Set up the GCs for the "New Login" and "Unlock" buttons.
902 XSetForeground(si->dpy, gc1, pw->button_foreground);
903 XSetForeground(si->dpy, gc2, pw->button_background);
904 XSetFont(si->dpy, gc1, pw->button_font->fid);
906 /* The "Unlock" button */
907 x2 = pw->width - pw->internal_border - (pw->shadow_width * 2);
909 /* right aligned button */
910 x1 = x2 - pw->unlock_button_width;
912 /* Add half the difference between y1 and the internal edge.
913 * It actually looks better if the internal border is ignored. */
914 y1 += ((pw->height - MAX (pw->unlock_button_height, pw->login_button_height)
918 pw->unlock_button_x = x1;
919 pw->unlock_button_y = y1;
921 /* The "New Login" button
923 if (pw->login_button_p)
925 /* Using the same GC as for the Unlock button */
927 sw = string_width (pw->button_font, pw->login_label);
929 /* left aligned button */
930 x1 = (pw->logo_width + pw->thermo_width + (pw->shadow_width * 3) +
931 pw->internal_border);
933 pw->login_button_x = x1;
934 pw->login_button_y = y1;
939 x1 = pw->shadow_width * 6;
940 y1 = pw->shadow_width * 6;
941 x2 = pw->logo_width - (pw->shadow_width * 12);
942 y2 = pw->logo_height - (pw->shadow_width * 12);
948 unsigned int w, h, bw, d;
949 XGetGeometry (si->dpy, pw->logo_pixmap, &root, &x, &y, &w, &h, &bw, &d);
950 XSetForeground (si->dpy, gc1, pw->foreground);
951 XSetBackground (si->dpy, gc1, pw->background);
952 XSetClipMask (si->dpy, gc1, pw->logo_clipmask);
953 XSetClipOrigin (si->dpy, gc1,
954 x1 + ((x2 - (int)w) / 2),
955 y1 + ((y2 - (int)h) / 2));
957 XCopyPlane (si->dpy, pw->logo_pixmap, si->passwd_dialog, gc1,
959 x1 + ((x2 - (int)w) / 2),
960 y1 + ((y2 - (int)h) / 2),
963 XCopyArea (si->dpy, pw->logo_pixmap, si->passwd_dialog, gc1,
965 x1 + ((x2 - (int)w) / 2),
966 y1 + ((y2 - (int)h) / 2));
971 XSetForeground (si->dpy, gc1, pw->thermo_foreground);
972 XSetForeground (si->dpy, gc2, pw->thermo_background);
974 pw->thermo_field_x = pw->logo_width + pw->shadow_width;
975 pw->thermo_field_y = pw->shadow_width * 5;
976 pw->thermo_field_height = pw->height - (pw->shadow_width * 10);
979 /* Solid border inside the logo box. */
980 XSetForeground (si->dpy, gc1, pw->foreground);
981 XDrawRectangle (si->dpy, si->passwd_dialog, gc1, x1, y1, x2-1, y2-1);
984 /* The shadow around the logo
986 draw_shaded_rectangle (si->dpy, si->passwd_dialog,
987 pw->shadow_width * 4,
988 pw->shadow_width * 4,
989 pw->logo_width - (pw->shadow_width * 8),
990 pw->logo_height - (pw->shadow_width * 8),
992 pw->shadow_bottom, pw->shadow_top);
994 /* The shadow around the thermometer
996 draw_shaded_rectangle (si->dpy, si->passwd_dialog,
998 pw->shadow_width * 4,
999 pw->thermo_width + (pw->shadow_width * 2),
1000 pw->height - (pw->shadow_width * 8),
1002 pw->shadow_bottom, pw->shadow_top);
1005 /* Solid border inside the thermometer. */
1006 XSetForeground (si->dpy, gc1, pw->foreground);
1007 XDrawRectangle (si->dpy, si->passwd_dialog, gc1,
1008 pw->thermo_field_x, pw->thermo_field_y,
1009 pw->thermo_width - 1, pw->thermo_field_height - 1);
1012 /* The shadow around the whole window
1014 draw_shaded_rectangle (si->dpy, si->passwd_dialog,
1015 0, 0, pw->width, pw->height, pw->shadow_width,
1016 pw->shadow_top, pw->shadow_bottom);
1018 XFreeGC (si->dpy, gc1);
1019 XFreeGC (si->dpy, gc2);
1021 update_passwd_window (si, pw->passwd_string, pw->ratio);
1025 draw_button(Display *dpy,
1028 unsigned long foreground, unsigned long background,
1031 int width, int height,
1033 Pixel shadow_light, Pixel shadow_dark,
1039 int label_x, label_y;
1041 gcv.foreground = foreground;
1042 gcv.font = font->fid;
1043 gc1 = XCreateGC(dpy, dialog, GCForeground|GCFont, &gcv);
1044 gcv.foreground = background;
1045 gc2 = XCreateGC(dpy, dialog, GCForeground, &gcv);
1047 XFillRectangle(dpy, dialog, gc2,
1048 x, y, width, height);
1050 sw = string_width(font, label);
1052 label_x = x + ((width - sw) / 2);
1053 label_y = (y + (height - (font->ascent + font->descent)) / 2 + font->ascent);
1061 XDrawString(dpy, dialog, gc1, label_x, label_y, label, strlen(label));
1066 draw_shaded_rectangle(dpy, dialog, x, y, width, height,
1067 shadow_width, shadow_light, shadow_dark);
1071 update_passwd_window (saver_info *si, const char *printed_passwd, float ratio)
1073 passwd_dialog_data *pw = si->pw_data;
1077 XRectangle rects[1];
1080 gcv.foreground = pw->passwd_foreground;
1081 gcv.font = pw->passwd_font->fid;
1082 gc1 = XCreateGC (si->dpy, si->passwd_dialog, GCForeground|GCFont, &gcv);
1083 gcv.foreground = pw->passwd_background;
1084 gc2 = XCreateGC (si->dpy, si->passwd_dialog, GCForeground, &gcv);
1088 char *s = strdup (printed_passwd);
1089 if (pw->passwd_string) free (pw->passwd_string);
1090 pw->passwd_string = s;
1093 if (pw->prompt_label)
1096 /* the "password" text field
1098 rects[0].x = pw->passwd_field_x;
1099 rects[0].y = pw->passwd_field_y;
1100 rects[0].width = pw->passwd_field_width;
1101 rects[0].height = pw->passwd_field_height;
1103 /* The user entry (password) field is double buffered.
1104 * This avoids flickering, particularly in synchronous mode. */
1106 if (pw->passwd_changed_p)
1108 pw->passwd_changed_p = False;
1110 if (pw->user_entry_pixmap)
1112 XFreePixmap(si->dpy, pw->user_entry_pixmap);
1113 pw->user_entry_pixmap = 0;
1116 pw->user_entry_pixmap =
1117 XCreatePixmap (si->dpy, si->passwd_dialog,
1118 rects[0].width, rects[0].height,
1119 DefaultDepthOfScreen (pw->prompt_screen->screen));
1121 XFillRectangle (si->dpy, pw->user_entry_pixmap, gc2,
1122 0, 0, rects[0].width, rects[0].height);
1124 XDrawString (si->dpy, pw->user_entry_pixmap, gc1,
1126 pw->passwd_font->ascent,
1127 pw->passwd_string, strlen(pw->passwd_string));
1129 /* Ensure the new pixmap gets copied to the window */
1136 if (pw->i_beam == 0)
1138 /* Make the I-beam disappear */
1139 XCopyArea(si->dpy, pw->user_entry_pixmap, si->passwd_dialog, gc2,
1140 0, 0, rects[0].width, rects[0].height,
1141 rects[0].x, rects[0].y);
1143 else if (pw->i_beam == 1)
1145 /* Make the I-beam appear */
1146 x = (rects[0].x + pw->shadow_width +
1147 string_width (pw->passwd_font, pw->passwd_string));
1148 y = rects[0].y + pw->shadow_width;
1150 if (x > rects[0].x + rects[0].width - 1)
1151 x = rects[0].x + rects[0].width - 1;
1152 XDrawLine (si->dpy, si->passwd_dialog, gc1,
1154 x, y + pw->passwd_font->ascent +
1155 pw->passwd_font->descent-1);
1158 pw->i_beam = (pw->i_beam + 1) % 4;
1164 y = (pw->thermo_field_height - 2) * (1.0 - pw->ratio);
1167 XFillRectangle (si->dpy, si->passwd_dialog, gc2,
1168 pw->thermo_field_x + 1,
1169 pw->thermo_field_y + 1,
1172 XSetForeground (si->dpy, gc1, pw->thermo_foreground);
1173 XFillRectangle (si->dpy, si->passwd_dialog, gc1,
1174 pw->thermo_field_x + 1,
1175 pw->thermo_field_y + 1 + y,
1177 MAX (0, pw->thermo_field_height - y - 2));
1180 if (pw->button_state_changed_p)
1182 pw->button_state_changed_p = False;
1184 /* The "Unlock" button
1186 draw_button(si->dpy, si->passwd_dialog, pw->button_font,
1187 pw->button_foreground, pw->button_background,
1189 pw->unlock_button_x, pw->unlock_button_y,
1190 pw->unlock_button_width, pw->unlock_button_height,
1192 (pw->unlock_button_down_p ? pw->shadow_bottom :
1194 (pw->unlock_button_down_p ? pw->shadow_top :
1196 pw->unlock_button_down_p);
1198 /* The "New Login" button
1200 if (pw->login_button_p)
1202 draw_button(si->dpy, si->passwd_dialog, pw->button_font,
1203 (pw->login_button_enabled_p
1204 ? pw->passwd_foreground
1205 : pw->shadow_bottom),
1206 pw->button_background,
1208 pw->login_button_x, pw->login_button_y,
1209 pw->login_button_width, pw->login_button_height,
1211 (pw->login_button_down_p
1214 (pw->login_button_down_p
1216 : pw->shadow_bottom),
1217 pw->login_button_down_p);
1221 XFreeGC (si->dpy, gc1);
1222 XFreeGC (si->dpy, gc2);
1223 XSync (si->dpy, False);
1228 restore_background (saver_info *si)
1230 passwd_dialog_data *pw = si->pw_data;
1231 saver_screen_info *ssi = pw->prompt_screen;
1235 gcv.function = GXcopy;
1237 gc = XCreateGC (si->dpy, ssi->screensaver_window, GCFunction, &gcv);
1239 XCopyArea (si->dpy, pw->save_under,
1240 ssi->screensaver_window, gc,
1242 ssi->width, ssi->height,
1245 XFreeGC (si->dpy, gc);
1249 /* Frees anything created by make_passwd_window */
1251 cleanup_passwd_window (saver_info *si)
1253 passwd_dialog_data *pw;
1255 if (!(pw = si->pw_data))
1260 mlstring_free(pw->info_label);
1264 if (pw->prompt_label)
1266 mlstring_free(pw->prompt_label);
1267 pw->prompt_label = 0;
1270 memset (pw->typed_passwd, 0, sizeof(pw->typed_passwd));
1271 memset (pw->typed_passwd_char_size, 0, sizeof(pw->typed_passwd_char_size));
1272 memset (pw->passwd_string, 0, strlen(pw->passwd_string));
1276 XtRemoveTimeOut (pw->timer);
1280 if (pw->user_entry_pixmap)
1282 XFreePixmap(si->dpy, pw->user_entry_pixmap);
1283 pw->user_entry_pixmap = 0;
1289 destroy_passwd_window (saver_info *si)
1291 saver_preferences *p = &si->prefs;
1292 passwd_dialog_data *pw = si->pw_data;
1293 saver_screen_info *ssi = pw->prompt_screen;
1294 Colormap cmap = DefaultColormapOfScreen (ssi->screen);
1295 Pixel black = BlackPixelOfScreen (ssi->screen);
1296 Pixel white = WhitePixelOfScreen (ssi->screen);
1299 cleanup_passwd_window (si);
1301 if (si->cached_passwd)
1303 char *wipe = si->cached_passwd;
1308 free(si->cached_passwd);
1309 si->cached_passwd = NULL;
1312 move_mouse_grab (si, RootWindowOfScreen (ssi->screen),
1313 ssi->cursor, ssi->number);
1315 if (pw->passwd_cursor)
1316 XFreeCursor (si->dpy, pw->passwd_cursor);
1319 fprintf (stderr, "%s: %d: moving mouse back to %d,%d.\n",
1320 blurb(), ssi->number,
1321 pw->previous_mouse_x, pw->previous_mouse_y);
1323 XWarpPointer (si->dpy, None, RootWindowOfScreen (ssi->screen),
1325 pw->previous_mouse_x, pw->previous_mouse_y);
1326 XSync (si->dpy, False);
1328 while (XCheckMaskEvent (si->dpy, PointerMotionMask, &event))
1330 fprintf (stderr, "%s: discarding MotionNotify event.\n", blurb());
1333 if (si->using_xinput_extension && si->xinput_DeviceMotionNotify)
1334 while (XCheckTypedEvent (si->dpy, si->xinput_DeviceMotionNotify, &event))
1336 fprintf (stderr, "%s: discarding DeviceMotionNotify event.\n",
1340 if (si->passwd_dialog)
1342 if (si->prefs.verbose_p)
1343 fprintf (stderr, "%s: %d: destroying password dialog.\n",
1344 blurb(), pw->prompt_screen->number);
1346 XDestroyWindow (si->dpy, si->passwd_dialog);
1347 si->passwd_dialog = 0;
1352 restore_background(si);
1353 XFreePixmap (si->dpy, pw->save_under);
1357 if (pw->heading_label) free (pw->heading_label);
1358 if (pw->body_label) free (pw->body_label);
1359 if (pw->user_label) free (pw->user_label);
1360 if (pw->date_label) free (pw->date_label);
1361 if (pw->login_label) free (pw->login_label);
1362 if (pw->unlock_label) free (pw->unlock_label);
1363 if (pw->passwd_string) free (pw->passwd_string);
1364 if (pw->uname_label) free (pw->uname_label);
1366 if (pw->heading_font) XFreeFont (si->dpy, pw->heading_font);
1367 if (pw->body_font) XFreeFont (si->dpy, pw->body_font);
1368 if (pw->label_font) XFreeFont (si->dpy, pw->label_font);
1369 if (pw->passwd_font) XFreeFont (si->dpy, pw->passwd_font);
1370 if (pw->date_font) XFreeFont (si->dpy, pw->date_font);
1371 if (pw->button_font) XFreeFont (si->dpy, pw->button_font);
1372 if (pw->uname_font) XFreeFont (si->dpy, pw->uname_font);
1374 if (pw->foreground != black && pw->foreground != white)
1375 XFreeColors (si->dpy, cmap, &pw->foreground, 1, 0L);
1376 if (pw->background != black && pw->background != white)
1377 XFreeColors (si->dpy, cmap, &pw->background, 1, 0L);
1378 if (!(pw->button_foreground == black || pw->button_foreground == white))
1379 XFreeColors (si->dpy, cmap, &pw->button_foreground, 1, 0L);
1380 if (!(pw->button_background == black || pw->button_background == white))
1381 XFreeColors (si->dpy, cmap, &pw->button_background, 1, 0L);
1382 if (pw->passwd_foreground != black && pw->passwd_foreground != white)
1383 XFreeColors (si->dpy, cmap, &pw->passwd_foreground, 1, 0L);
1384 if (pw->passwd_background != black && pw->passwd_background != white)
1385 XFreeColors (si->dpy, cmap, &pw->passwd_background, 1, 0L);
1386 if (pw->thermo_foreground != black && pw->thermo_foreground != white)
1387 XFreeColors (si->dpy, cmap, &pw->thermo_foreground, 1, 0L);
1388 if (pw->thermo_background != black && pw->thermo_background != white)
1389 XFreeColors (si->dpy, cmap, &pw->thermo_background, 1, 0L);
1390 if (pw->shadow_top != black && pw->shadow_top != white)
1391 XFreeColors (si->dpy, cmap, &pw->shadow_top, 1, 0L);
1392 if (pw->shadow_bottom != black && pw->shadow_bottom != white)
1393 XFreeColors (si->dpy, cmap, &pw->shadow_bottom, 1, 0L);
1395 if (pw->logo_pixmap)
1396 XFreePixmap (si->dpy, pw->logo_pixmap);
1397 if (pw-> logo_clipmask)
1398 XFreePixmap (si->dpy, pw->logo_clipmask);
1399 if (pw->logo_pixels)
1401 if (pw->logo_npixels)
1402 XFreeColors (si->dpy, cmap, pw->logo_pixels, pw->logo_npixels, 0L);
1403 free (pw->logo_pixels);
1404 pw->logo_pixels = 0;
1405 pw->logo_npixels = 0;
1409 XFreePixmap (si->dpy, pw->save_under);
1412 XInstallColormap (si->dpy, cmap);
1414 memset (pw, 0, sizeof(*pw));
1420 static Bool error_handler_hit_p = False;
1423 ignore_all_errors_ehandler (Display *dpy, XErrorEvent *error)
1425 error_handler_hit_p = True;
1430 #ifdef HAVE_XHPDISABLERESET
1431 /* This function enables and disables the C-Sh-Reset hot-key, which
1432 normally resets the X server (logging out the logged-in user.)
1433 We don't want random people to be able to do that while the
1437 hp_lock_reset (saver_info *si, Bool lock_p)
1439 static Bool hp_locked_p = False;
1441 /* Calls to XHPDisableReset and XHPEnableReset must be balanced,
1442 or BadAccess errors occur. (It's ok for this to be global,
1443 since it affects the whole machine, not just the current screen.)
1445 if (hp_locked_p == lock_p)
1449 XHPDisableReset (si->dpy);
1451 XHPEnableReset (si->dpy);
1452 hp_locked_p = lock_p;
1454 #endif /* HAVE_XHPDISABLERESET */
1457 #ifdef HAVE_XF86MISCSETGRABKEYSSTATE
1459 /* This function enables and disables the Ctrl-Alt-KP_star and
1460 Ctrl-Alt-KP_slash hot-keys, which (in XFree86 4.2) break any
1461 grabs and/or kill the grabbing client. That would effectively
1462 unlock the screen, so we don't like that.
1464 The Ctrl-Alt-KP_star and Ctrl-Alt-KP_slash hot-keys only exist
1465 if AllowDeactivateGrabs and/or AllowClosedownGrabs are turned on
1466 in XF86Config. I believe they are disabled by default.
1468 This does not affect any other keys (specifically Ctrl-Alt-BS or
1469 Ctrl-Alt-F1) but I wish it did. Maybe it will someday.
1472 xfree_lock_grab_smasher (saver_info *si, Bool lock_p)
1474 saver_preferences *p = &si->prefs;
1477 XErrorHandler old_handler;
1479 if (!XF86MiscQueryExtension(si->dpy, &event, &error))
1482 XSync (si->dpy, False);
1483 error_handler_hit_p = False;
1484 old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1485 XSync (si->dpy, False);
1486 status = XF86MiscSetGrabKeysState (si->dpy, !lock_p);
1487 XSync (si->dpy, False);
1488 if (error_handler_hit_p) status = 666;
1490 if (!lock_p && status == MiscExtGrabStateAlready)
1491 status = MiscExtGrabStateSuccess; /* shut up, consider this success */
1493 if (p->verbose_p && status != MiscExtGrabStateSuccess)
1494 fprintf (stderr, "%s: error: XF86MiscSetGrabKeysState(%d) returned %s\n",
1496 (status == MiscExtGrabStateSuccess ? "MiscExtGrabStateSuccess" :
1497 status == MiscExtGrabStateLocked ? "MiscExtGrabStateLocked" :
1498 status == MiscExtGrabStateAlready ? "MiscExtGrabStateAlready" :
1499 status == 666 ? "an X error" :
1502 XSync (si->dpy, False);
1503 XSetErrorHandler (old_handler);
1504 XSync (si->dpy, False);
1506 #endif /* HAVE_XF86MISCSETGRABKEYSSTATE */
1510 /* This function enables and disables the C-Alt-Plus and C-Alt-Minus
1511 hot-keys, which normally change the resolution of the X server.
1512 We don't want people to be able to switch the server resolution
1513 while the screen is locked, because if they switch to a higher
1514 resolution, it could cause part of the underlying desktop to become
1517 #ifdef HAVE_XF86VMODE
1520 xfree_lock_mode_switch (saver_info *si, Bool lock_p)
1522 static Bool any_mode_locked_p = False;
1523 saver_preferences *p = &si->prefs;
1525 int real_nscreens = ScreenCount (si->dpy);
1528 XErrorHandler old_handler;
1530 if (any_mode_locked_p == lock_p)
1532 if (!XF86VidModeQueryExtension (si->dpy, &event, &error))
1535 for (screen = 0; screen < real_nscreens; screen++)
1537 XSync (si->dpy, False);
1538 old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1539 error_handler_hit_p = False;
1540 status = XF86VidModeLockModeSwitch (si->dpy, screen, lock_p);
1541 XSync (si->dpy, False);
1542 XSetErrorHandler (old_handler);
1543 if (error_handler_hit_p) status = False;
1546 any_mode_locked_p = lock_p;
1548 if (!status && (p->verbose_p || !lock_p))
1549 /* Only print this when verbose, or when we locked but can't unlock.
1550 I tried printing this message whenever it comes up, but
1551 mode-locking always fails if DontZoom is set in XF86Config. */
1552 fprintf (stderr, "%s: %d: unable to %s mode switching!\n",
1553 blurb(), screen, (lock_p ? "lock" : "unlock"));
1554 else if (p->verbose_p)
1555 fprintf (stderr, "%s: %d: %s mode switching.\n",
1556 blurb(), screen, (lock_p ? "locked" : "unlocked"));
1559 #endif /* HAVE_XF86VMODE */
1562 /* If the viewport has been scrolled since the screen was blanked,
1563 then scroll it back to where it belongs. This function only exists
1564 to patch over a very brief race condition.
1567 undo_vp_motion (saver_info *si)
1569 #ifdef HAVE_XF86VMODE
1570 saver_preferences *p = &si->prefs;
1572 int real_nscreens = ScreenCount (si->dpy);
1575 if (!XF86VidModeQueryExtension (si->dpy, &event, &error))
1578 for (screen = 0; screen < real_nscreens; screen++)
1580 saver_screen_info *ssi = &si->screens[screen];
1584 if (ssi->blank_vp_x == -1 && ssi->blank_vp_y == -1)
1586 if (!XF86VidModeGetViewPort (si->dpy, screen, &x, &y))
1588 if (ssi->blank_vp_x == x && ssi->blank_vp_y == y)
1591 /* We're going to move the viewport. The mouse has just been grabbed on
1592 (and constrained to, thus warped to) the password window, so it is no
1593 longer near the edge of the screen. However, wait a bit anyway, just
1594 to make sure the server drains its last motion event, so that the
1595 screen doesn't continue to scroll after we've reset the viewport.
1597 XSync (si->dpy, False);
1598 usleep (250000); /* 1/4 second */
1599 XSync (si->dpy, False);
1601 status = XF86VidModeSetViewPort (si->dpy, screen,
1602 ssi->blank_vp_x, ssi->blank_vp_y);
1606 "%s: %d: unable to move vp from (%d,%d) back to (%d,%d)!\n",
1607 blurb(), screen, x, y, ssi->blank_vp_x, ssi->blank_vp_y);
1608 else if (p->verbose_p)
1610 "%s: %d: vp moved to (%d,%d); moved it back to (%d,%d).\n",
1611 blurb(), screen, x, y, ssi->blank_vp_x, ssi->blank_vp_y);
1613 #endif /* HAVE_XF86VMODE */
1622 passwd_animate_timer (XtPointer closure, XtIntervalId *id)
1624 saver_info *si = (saver_info *) closure;
1626 passwd_dialog_data *pw = si->pw_data;
1630 pw->ratio -= (1.0 / ((double) si->prefs.passwd_timeout / (double) tick));
1634 if (si->unlock_state == ul_read)
1635 si->unlock_state = ul_time;
1638 update_passwd_window (si, 0, pw->ratio);
1640 if (si->unlock_state == ul_read)
1641 pw->timer = XtAppAddTimeOut (si->app, tick, passwd_animate_timer,
1646 idle_timer ((XtPointer) si, 0);
1650 static XComposeStatus *compose_status;
1653 handle_login_button (saver_info *si, XEvent *event)
1655 saver_preferences *p = &si->prefs;
1656 Bool mouse_in_box = False;
1658 passwd_dialog_data *pw = si->pw_data;
1659 saver_screen_info *ssi = pw->prompt_screen;
1661 if (! pw->login_button_enabled_p)
1665 (event->xbutton.x >= pw->login_button_x &&
1666 event->xbutton.x <= pw->login_button_x + pw->login_button_width &&
1667 event->xbutton.y >= pw->login_button_y &&
1668 event->xbutton.y <= pw->login_button_y + pw->login_button_height);
1670 if (ButtonRelease == event->xany.type &&
1671 pw->login_button_down_p &&
1674 /* Only allow them to press the button once: don't want to
1675 accidentally launch a dozen gdm choosers if the machine
1679 pw->login_button_enabled_p = False;
1682 pw->login_button_down_p = (mouse_in_box &&
1683 ButtonRelease != event->xany.type);
1685 update_passwd_window (si, 0, pw->ratio);
1688 fork_and_exec (ssi, p->new_login_command);
1693 handle_unlock_button (saver_info *si, XEvent *event)
1695 Bool mouse_in_box = False;
1696 passwd_dialog_data *pw = si->pw_data;
1699 (event->xbutton.x >= pw->unlock_button_x &&
1700 event->xbutton.x <= pw->unlock_button_x + pw->unlock_button_width &&
1701 event->xbutton.y >= pw->unlock_button_y &&
1702 event->xbutton.y <= pw->unlock_button_y + pw->unlock_button_height);
1704 if (ButtonRelease == event->xany.type &&
1705 pw->unlock_button_down_p &&
1707 finished_typing_passwd (si, pw);
1709 pw->unlock_button_down_p = (mouse_in_box &&
1710 ButtonRelease != event->xany.type);
1715 finished_typing_passwd (saver_info *si, passwd_dialog_data *pw)
1717 if (si->unlock_state == ul_read)
1719 update_passwd_window (si, "Checking...", pw->ratio);
1720 XSync (si->dpy, False);
1722 si->unlock_state = ul_finished;
1723 update_passwd_window (si, "", pw->ratio);
1728 handle_passwd_key (saver_info *si, XKeyEvent *event)
1730 passwd_dialog_data *pw = si->pw_data;
1731 unsigned char decoded [MAX_BYTES_PER_CHAR * 10]; /* leave some slack */
1734 /* XLookupString may return more than one character via XRebindKeysym;
1735 and on some systems it returns multi-byte UTF-8 characters (contrary
1736 to its documentation, which says it returns only Latin1.)
1738 It seems to only do so, however, if setlocale() has been called.
1739 See the code inside ENABLE_NLS in xscreensaver.c.
1741 int decoded_size = XLookupString (event, (char *)decoded, sizeof(decoded),
1742 &keysym, compose_status);
1746 const char *ks = XKeysymToString (keysym);
1748 fprintf(stderr, "## %-12s\t=> %d\t", (ks ? ks : "(null)"), decoded_size);
1749 for (i = 0; i < decoded_size; i++)
1750 fprintf(stderr, "%c", decoded[i]);
1751 fprintf(stderr, "\t");
1752 for (i = 0; i < decoded_size; i++)
1753 fprintf(stderr, "\\%03o", ((unsigned char *)decoded)[i]);
1754 fprintf(stderr, "\n");
1758 if (decoded_size > MAX_BYTES_PER_CHAR)
1760 /* The multi-byte character returned is too large. */
1765 decoded[decoded_size] = 0;
1766 pw->passwd_changed_p = True;
1768 /* Add 10% to the time remaining every time a key is pressed. */
1770 if (pw->ratio > 1) pw->ratio = 1;
1772 if (decoded_size == 1) /* Handle single-char commands */
1776 case '\010': case '\177': /* Backspace */
1778 /* kludgey way to get the number of "logical" characters. */
1779 int nchars = strlen (pw->typed_passwd_char_size);
1780 int nbytes = strlen (pw->typed_passwd);
1786 for (i = pw->typed_passwd_char_size[nchars-1]; i >= 0; i--)
1788 if (nbytes < 0) abort();
1789 pw->typed_passwd[nbytes--] = 0;
1791 pw->typed_passwd_char_size[nchars-1] = 0;
1796 case '\012': case '\015': /* Enter */
1797 finished_typing_passwd (si, pw);
1800 case '\033': /* Escape */
1801 si->unlock_state = ul_cancel;
1804 case '\025': case '\030': /* Erase line */
1805 memset (pw->typed_passwd, 0, sizeof (pw->typed_passwd));
1806 memset (pw->typed_passwd_char_size, 0,
1807 sizeof (pw->typed_passwd_char_size));
1811 if (*decoded < ' ' && *decoded != '\t') /* Other ctrl char */
1822 nbytes = strlen (pw->typed_passwd);
1823 nchars = strlen (pw->typed_passwd_char_size);
1824 if (nchars + 1 >= sizeof (pw->typed_passwd_char_size)-1 ||
1825 nbytes + decoded_size >= sizeof (pw->typed_passwd)-1) /* overflow */
1829 pw->typed_passwd_char_size[nchars] = decoded_size;
1830 pw->typed_passwd_char_size[nchars+1] = 0;
1831 memcpy (pw->typed_passwd + nbytes, decoded, decoded_size);
1832 pw->typed_passwd[nbytes + decoded_size] = 0;
1838 /* If the input is wider than the text box, only show the last portion,
1839 to simulate a horizontally-scrolling text field. */
1840 int chars_in_pwfield = (pw->passwd_field_width /
1841 pw->passwd_font->max_bounds.width);
1842 const char *output = pw->typed_passwd;
1843 if (strlen(output) > chars_in_pwfield)
1844 output += (strlen(output) - chars_in_pwfield);
1845 update_passwd_window (si, output, pw->ratio);
1847 else if (pw->show_stars_p)
1849 int nchars = strlen (pw->typed_passwd_char_size);
1851 stars = (char *) malloc(nchars + 1);
1852 memset (stars, '*', nchars);
1854 update_passwd_window (si, stars, pw->ratio);
1859 update_passwd_window (si, "", pw->ratio);
1865 passwd_event_loop (saver_info *si)
1867 saver_preferences *p = &si->prefs;
1870 /* We have to go through this union bullshit because gcc-4.4.0 has
1871 stricter struct-aliasing rules. Without this, the optimizer
1877 XRRScreenChangeNotifyEvent xrr_event;
1878 # endif /* HAVE_RANDR */
1881 passwd_animate_timer ((XtPointer) si, 0);
1883 while (si->unlock_state == ul_read)
1885 XtAppNextEvent (si->app, &event.x_event);
1888 if (si->using_randr_extension &&
1889 (event.x_event.type ==
1890 (si->randr_event_number + RRScreenChangeNotify)))
1892 /* The Resize and Rotate extension sends an event when the
1893 size, rotation, or refresh rate of any screen has changed. */
1897 /* XRRRootToScreen is in Xrandr.h 1.4, 2001/06/07 */
1898 int screen = XRRRootToScreen(si->dpy, event.xrr_event.window);
1899 fprintf (stderr, "%s: %d: screen change event received\n",
1903 #ifdef RRScreenChangeNotifyMask
1904 /* Inform Xlib that it's ok to update its data structures. */
1905 XRRUpdateConfiguration(&event.x_event); /* Xrandr.h 1.9, 2002/09/29*/
1906 #endif /* RRScreenChangeNotifyMask */
1908 /* Resize the existing xscreensaver windows and cached ssi data. */
1909 if (update_screen_layout (si))
1913 fprintf (stderr, "%s: new layout:\n", blurb());
1914 describe_monitor_layout (si);
1916 resize_screensaver_window (si);
1920 #endif /* HAVE_RANDR */
1922 if (event.x_event.xany.window == si->passwd_dialog &&
1923 event.x_event.xany.type == Expose)
1924 draw_passwd_window (si);
1925 else if (event.x_event.xany.type == KeyPress)
1927 handle_passwd_key (si, &event.x_event.xkey);
1928 si->pw_data->caps_p = (event.x_event.xkey.state & LockMask);
1930 else if (event.x_event.xany.type == ButtonPress ||
1931 event.x_event.xany.type == ButtonRelease)
1933 si->pw_data->button_state_changed_p = True;
1934 handle_unlock_button (si, &event.x_event);
1935 if (si->pw_data->login_button_p)
1936 handle_login_button (si, &event.x_event);
1939 XtDispatchEvent (&event.x_event);
1942 switch (si->unlock_state)
1944 case ul_cancel: msg = ""; break;
1945 case ul_time: msg = "Timed out!"; break;
1946 case ul_finished: msg = "Checking..."; break;
1947 default: msg = 0; break;
1951 switch (si->unlock_state) {
1953 fprintf (stderr, "%s: input cancelled.\n", blurb()); break;
1955 fprintf (stderr, "%s: input timed out.\n", blurb()); break;
1957 fprintf (stderr, "%s: input finished.\n", blurb()); break;
1963 si->pw_data->i_beam = 0;
1964 update_passwd_window (si, msg, 0.0);
1965 XSync (si->dpy, False);
1967 /* Swallow all pending KeyPress/KeyRelease events. */
1970 while (XCheckMaskEvent (si->dpy, KeyPressMask|KeyReleaseMask, &e))
1978 handle_typeahead (saver_info *si)
1980 passwd_dialog_data *pw = si->pw_data;
1982 if (!si->unlock_typeahead)
1985 pw->passwd_changed_p = True;
1987 i = strlen (si->unlock_typeahead);
1988 if (i >= sizeof(pw->typed_passwd) - 1)
1989 i = sizeof(pw->typed_passwd) - 1;
1991 memcpy (pw->typed_passwd, si->unlock_typeahead, i);
1992 pw->typed_passwd [i] = 0;
1995 char *c = pw->typed_passwd_char_size;
1996 for (j = 0; j < i; j++)
2001 memset (si->unlock_typeahead, '*', strlen(si->unlock_typeahead));
2002 si->unlock_typeahead[i] = 0;
2003 update_passwd_window (si, si->unlock_typeahead, pw->ratio);
2005 free (si->unlock_typeahead);
2006 si->unlock_typeahead = 0;
2011 * Returns a copy of the input string with trailing whitespace removed.
2012 * Whitespace is anything considered so by isspace().
2013 * It is safe to call this with NULL, in which case NULL will be returned.
2014 * The returned string (if not NULL) should be freed by the caller with free().
2017 remove_trailing_whitespace(const char *str)
2027 newstr = malloc(len + 1);
2031 (void) strcpy(newstr, str);
2033 while (isspace(*--chr) && chr >= newstr)
2041 * The authentication conversation function.
2042 * Like a PAM conversation function, this accepts multiple messages in a single
2043 * round. It then splits them into individual messages for display on the
2044 * passwd dialog. A message sequence of info or error followed by a prompt will
2045 * be reduced into a single dialog window.
2047 * Returns 0 on success or -1 if some problem occurred (cancelled, OOM, etc.)
2050 gui_auth_conv(int num_msg,
2051 const struct auth_message auth_msgs[],
2052 struct auth_response **resp,
2056 const char *info_msg, *prompt;
2057 struct auth_response *responses;
2059 if (si->unlock_state == ul_cancel ||
2060 si->unlock_state == ul_time)
2061 /* If we've already cancelled or timed out in this PAM conversation,
2062 don't prompt again even if PAM asks us to! */
2065 if (!(responses = calloc(num_msg, sizeof(struct auth_response))))
2068 for (i = 0; i < num_msg; ++i)
2070 info_msg = prompt = NULL;
2072 /* See if there is a following message that can be shown at the same
2074 if (auth_msgs[i].type == AUTH_MSGTYPE_INFO
2076 && ( auth_msgs[i+1].type == AUTH_MSGTYPE_PROMPT_NOECHO
2077 || auth_msgs[i+1].type == AUTH_MSGTYPE_PROMPT_ECHO)
2080 info_msg = auth_msgs[i].msg;
2081 prompt = auth_msgs[++i].msg;
2085 if ( auth_msgs[i].type == AUTH_MSGTYPE_INFO
2086 || auth_msgs[i].type == AUTH_MSGTYPE_ERROR)
2087 info_msg = auth_msgs[i].msg;
2089 prompt = auth_msgs[i].msg;
2093 char *info_msg_trimmed, *prompt_trimmed;
2095 /* Trailing whitespace looks bad in a GUI */
2096 info_msg_trimmed = remove_trailing_whitespace(info_msg);
2097 prompt_trimmed = remove_trailing_whitespace(prompt);
2099 if (make_passwd_window(si, info_msg_trimmed, prompt_trimmed,
2100 auth_msgs[i].type == AUTH_MSGTYPE_PROMPT_ECHO
2105 if (info_msg_trimmed)
2106 free(info_msg_trimmed);
2109 free(prompt_trimmed);
2112 compose_status = calloc (1, sizeof (*compose_status));
2113 if (!compose_status)
2116 si->unlock_state = ul_read;
2118 handle_typeahead (si);
2119 passwd_event_loop (si);
2121 if (si->unlock_state == ul_cancel)
2124 responses[i].response = strdup(si->pw_data->typed_passwd);
2126 /* Cache the first response to a PROMPT_NOECHO to save prompting for
2127 * each auth mechanism. */
2128 if (si->cached_passwd == NULL &&
2129 auth_msgs[i].type == AUTH_MSGTYPE_PROMPT_NOECHO)
2130 si->cached_passwd = strdup(responses[i].response);
2132 free (compose_status);
2138 return (si->unlock_state == ul_finished) ? 0 : -1;
2142 free (compose_status);
2147 for (i = 0; i < num_msg; ++i)
2148 if (responses[i].response)
2149 free (responses[i].response);
2158 auth_finished_cb (saver_info *si)
2163 /* If we have something to say, put the dialog back up for a few seconds
2164 to display it. Otherwise, don't bother.
2167 if (si->unlock_state == ul_fail && /* failed with caps lock on */
2168 si->pw_data && si->pw_data->caps_p)
2169 s = "Authentication failed (Caps Lock?)";
2170 else if (si->unlock_state == ul_fail) /* failed without caps lock */
2171 s = "Authentication failed!";
2172 else if (si->unlock_state == ul_success && /* good, but report failures */
2173 si->unlock_failures > 0)
2175 if (si->unlock_failures == 1)
2176 s = "There has been\n1 failed login attempt.";
2179 sprintf (buf, "There have been\n%d failed login attempts.",
2180 si->unlock_failures);
2183 si->unlock_failures = 0;
2185 else /* good, with no failures, */
2186 goto END; /* or timeout, or cancel. */
2188 make_passwd_window (si, s, NULL, True);
2189 XSync (si->dpy, False);
2193 time_t start = time ((time_t *) 0);
2195 while (time ((time_t *) 0) < start + secs)
2196 if (XPending (si->dpy))
2198 XNextEvent (si->dpy, &event);
2199 if (event.xany.window == si->passwd_dialog &&
2200 event.xany.type == Expose)
2201 draw_passwd_window (si);
2202 else if (event.xany.type == ButtonPress ||
2203 event.xany.type == KeyPress)
2205 XSync (si->dpy, False);
2208 usleep (250000); /* 1/4 second */
2213 destroy_passwd_window (si);
2218 unlock_p (saver_info *si)
2220 saver_preferences *p = &si->prefs;
2224 fprintf(stderr, "%s: Error: no unlock function specified!\n", blurb());
2228 raise_window (si, True, True, True);
2230 xss_authenticate(si, p->verbose_p);
2232 return (si->unlock_state == ul_success);
2237 set_locked_p (saver_info *si, Bool locked_p)
2239 si->locked_p = locked_p;
2241 #ifdef HAVE_XHPDISABLERESET
2242 hp_lock_reset (si, locked_p); /* turn off/on C-Sh-Reset */
2244 #ifdef HAVE_XF86VMODE
2245 xfree_lock_mode_switch (si, locked_p); /* turn off/on C-Alt-Plus */
2247 #ifdef HAVE_XF86MISCSETGRABKEYSSTATE
2248 xfree_lock_grab_smasher (si, locked_p); /* turn off/on C-Alt-KP-*,/ */
2251 store_saver_status (si); /* store locked-p */
2255 #else /* NO_LOCKING -- whole file */
2258 set_locked_p (saver_info *si, Bool locked_p)
2260 if (locked_p) abort();
2263 #endif /* !NO_LOCKING */