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 int decoded_size = XLookupString (event, (char *)decoded, sizeof(decoded),
1739 &keysym, compose_status);
1743 const char *ks = XKeysymToString (keysym);
1745 fprintf(stderr, "## %-12s\t=> %d\t", (ks ? ks : "(null)"), decoded_size);
1746 for (i = 0; i < decoded_size; i++)
1747 fprintf(stderr, "%c", decoded[i]);
1748 fprintf(stderr, "\t");
1749 for (i = 0; i < decoded_size; i++)
1750 fprintf(stderr, "\\%03o", ((unsigned char *)decoded)[i]);
1751 fprintf(stderr, "\n");
1755 if (decoded_size > MAX_BYTES_PER_CHAR)
1757 /* The multi-byte character returned is too large. */
1762 decoded[decoded_size] = 0;
1763 pw->passwd_changed_p = True;
1765 /* Add 10% to the time remaining every time a key is pressed. */
1767 if (pw->ratio > 1) pw->ratio = 1;
1769 if (decoded_size == 1) /* Handle single-char commands */
1773 case '\010': case '\177': /* Backspace */
1775 /* kludgey way to get the number of "logical" characters. */
1776 int nchars = strlen (pw->typed_passwd_char_size);
1777 int nbytes = strlen (pw->typed_passwd);
1783 for (i = pw->typed_passwd_char_size[nchars-1]; i >= 0; i--)
1785 if (nbytes < 0) abort();
1786 pw->typed_passwd[nbytes--] = 0;
1788 pw->typed_passwd_char_size[nchars-1] = 0;
1793 case '\012': case '\015': /* Enter */
1794 finished_typing_passwd (si, pw);
1797 case '\033': /* Escape */
1798 si->unlock_state = ul_cancel;
1801 case '\025': case '\030': /* Erase line */
1802 memset (pw->typed_passwd, 0, sizeof (pw->typed_passwd));
1803 memset (pw->typed_passwd_char_size, 0,
1804 sizeof (pw->typed_passwd_char_size));
1808 if (*decoded < ' ' && *decoded != '\t') /* Other ctrl char */
1819 nbytes = strlen (pw->typed_passwd);
1820 nchars = strlen (pw->typed_passwd_char_size);
1821 if (nchars + 1 >= sizeof (pw->typed_passwd_char_size)-1 ||
1822 nbytes + decoded_size >= sizeof (pw->typed_passwd)-1) /* overflow */
1826 pw->typed_passwd_char_size[nchars] = decoded_size;
1827 pw->typed_passwd_char_size[nchars+1] = 0;
1828 memcpy (pw->typed_passwd + nbytes, decoded, decoded_size);
1829 pw->typed_passwd[nbytes + decoded_size] = 0;
1835 /* If the input is wider than the text box, only show the last portion,
1836 to simulate a horizontally-scrolling text field. */
1837 int chars_in_pwfield = (pw->passwd_field_width /
1838 pw->passwd_font->max_bounds.width);
1839 const char *output = pw->typed_passwd;
1840 if (strlen(output) > chars_in_pwfield)
1841 output += (strlen(output) - chars_in_pwfield);
1842 update_passwd_window (si, output, pw->ratio);
1844 else if (pw->show_stars_p)
1846 int nchars = strlen (pw->typed_passwd_char_size);
1848 stars = (char *) malloc(nchars + 1);
1849 memset (stars, '*', nchars);
1851 update_passwd_window (si, stars, pw->ratio);
1856 update_passwd_window (si, "", pw->ratio);
1862 passwd_event_loop (saver_info *si)
1864 saver_preferences *p = &si->prefs;
1867 /* We have to go through this union bullshit because gcc-4.4.0 has
1868 stricter struct-aliasing rules. Without this, the optimizer
1874 XRRScreenChangeNotifyEvent xrr_event;
1875 # endif /* HAVE_RANDR */
1878 passwd_animate_timer ((XtPointer) si, 0);
1880 while (si->unlock_state == ul_read)
1882 XtAppNextEvent (si->app, &event.x_event);
1885 if (si->using_randr_extension &&
1886 (event.x_event.type ==
1887 (si->randr_event_number + RRScreenChangeNotify)))
1889 /* The Resize and Rotate extension sends an event when the
1890 size, rotation, or refresh rate of any screen has changed. */
1894 /* XRRRootToScreen is in Xrandr.h 1.4, 2001/06/07 */
1895 int screen = XRRRootToScreen(si->dpy, event.xrr_event.window);
1896 fprintf (stderr, "%s: %d: screen change event received\n",
1900 #ifdef RRScreenChangeNotifyMask
1901 /* Inform Xlib that it's ok to update its data structures. */
1902 XRRUpdateConfiguration(&event.x_event); /* Xrandr.h 1.9, 2002/09/29*/
1903 #endif /* RRScreenChangeNotifyMask */
1905 /* Resize the existing xscreensaver windows and cached ssi data. */
1906 if (update_screen_layout (si))
1910 fprintf (stderr, "%s: new layout:\n", blurb());
1911 describe_monitor_layout (si);
1913 resize_screensaver_window (si);
1917 #endif /* HAVE_RANDR */
1919 if (event.x_event.xany.window == si->passwd_dialog &&
1920 event.x_event.xany.type == Expose)
1921 draw_passwd_window (si);
1922 else if (event.x_event.xany.type == KeyPress)
1924 handle_passwd_key (si, &event.x_event.xkey);
1925 si->pw_data->caps_p = (event.x_event.xkey.state & LockMask);
1927 else if (event.x_event.xany.type == ButtonPress ||
1928 event.x_event.xany.type == ButtonRelease)
1930 si->pw_data->button_state_changed_p = True;
1931 handle_unlock_button (si, &event.x_event);
1932 if (si->pw_data->login_button_p)
1933 handle_login_button (si, &event.x_event);
1936 XtDispatchEvent (&event.x_event);
1939 switch (si->unlock_state)
1941 case ul_cancel: msg = ""; break;
1942 case ul_time: msg = "Timed out!"; break;
1943 case ul_finished: msg = "Checking..."; break;
1944 default: msg = 0; break;
1948 switch (si->unlock_state) {
1950 fprintf (stderr, "%s: input cancelled.\n", blurb()); break;
1952 fprintf (stderr, "%s: input timed out.\n", blurb()); break;
1954 fprintf (stderr, "%s: input finished.\n", blurb()); break;
1960 si->pw_data->i_beam = 0;
1961 update_passwd_window (si, msg, 0.0);
1962 XSync (si->dpy, False);
1964 /* Swallow all pending KeyPress/KeyRelease events. */
1967 while (XCheckMaskEvent (si->dpy, KeyPressMask|KeyReleaseMask, &e))
1975 handle_typeahead (saver_info *si)
1977 passwd_dialog_data *pw = si->pw_data;
1979 if (!si->unlock_typeahead)
1982 pw->passwd_changed_p = True;
1984 i = strlen (si->unlock_typeahead);
1985 if (i >= sizeof(pw->typed_passwd) - 1)
1986 i = sizeof(pw->typed_passwd) - 1;
1988 memcpy (pw->typed_passwd, si->unlock_typeahead, i);
1989 pw->typed_passwd [i] = 0;
1992 char *c = pw->typed_passwd_char_size;
1993 for (j = 0; j < i; j++)
1998 memset (si->unlock_typeahead, '*', strlen(si->unlock_typeahead));
1999 si->unlock_typeahead[i] = 0;
2000 update_passwd_window (si, si->unlock_typeahead, pw->ratio);
2002 free (si->unlock_typeahead);
2003 si->unlock_typeahead = 0;
2008 * Returns a copy of the input string with trailing whitespace removed.
2009 * Whitespace is anything considered so by isspace().
2010 * It is safe to call this with NULL, in which case NULL will be returned.
2011 * The returned string (if not NULL) should be freed by the caller with free().
2014 remove_trailing_whitespace(const char *str)
2024 newstr = malloc(len + 1);
2028 (void) strcpy(newstr, str);
2030 while (isspace(*--chr) && chr >= newstr)
2038 * The authentication conversation function.
2039 * Like a PAM conversation function, this accepts multiple messages in a single
2040 * round. It then splits them into individual messages for display on the
2041 * passwd dialog. A message sequence of info or error followed by a prompt will
2042 * be reduced into a single dialog window.
2044 * Returns 0 on success or -1 if some problem occurred (cancelled, OOM, etc.)
2047 gui_auth_conv(int num_msg,
2048 const struct auth_message auth_msgs[],
2049 struct auth_response **resp,
2053 const char *info_msg, *prompt;
2054 struct auth_response *responses;
2056 if (si->unlock_state == ul_cancel ||
2057 si->unlock_state == ul_time)
2058 /* If we've already cancelled or timed out in this PAM conversation,
2059 don't prompt again even if PAM asks us to! */
2062 if (!(responses = calloc(num_msg, sizeof(struct auth_response))))
2065 for (i = 0; i < num_msg; ++i)
2067 info_msg = prompt = NULL;
2069 /* See if there is a following message that can be shown at the same
2071 if (auth_msgs[i].type == AUTH_MSGTYPE_INFO
2073 && ( auth_msgs[i+1].type == AUTH_MSGTYPE_PROMPT_NOECHO
2074 || auth_msgs[i+1].type == AUTH_MSGTYPE_PROMPT_ECHO)
2077 info_msg = auth_msgs[i].msg;
2078 prompt = auth_msgs[++i].msg;
2082 if ( auth_msgs[i].type == AUTH_MSGTYPE_INFO
2083 || auth_msgs[i].type == AUTH_MSGTYPE_ERROR)
2084 info_msg = auth_msgs[i].msg;
2086 prompt = auth_msgs[i].msg;
2090 char *info_msg_trimmed, *prompt_trimmed;
2092 /* Trailing whitespace looks bad in a GUI */
2093 info_msg_trimmed = remove_trailing_whitespace(info_msg);
2094 prompt_trimmed = remove_trailing_whitespace(prompt);
2096 if (make_passwd_window(si, info_msg_trimmed, prompt_trimmed,
2097 auth_msgs[i].type == AUTH_MSGTYPE_PROMPT_ECHO
2102 if (info_msg_trimmed)
2103 free(info_msg_trimmed);
2106 free(prompt_trimmed);
2109 compose_status = calloc (1, sizeof (*compose_status));
2110 if (!compose_status)
2113 si->unlock_state = ul_read;
2115 handle_typeahead (si);
2116 passwd_event_loop (si);
2118 if (si->unlock_state == ul_cancel)
2121 responses[i].response = strdup(si->pw_data->typed_passwd);
2123 /* Cache the first response to a PROMPT_NOECHO to save prompting for
2124 * each auth mechanism. */
2125 if (si->cached_passwd == NULL &&
2126 auth_msgs[i].type == AUTH_MSGTYPE_PROMPT_NOECHO)
2127 si->cached_passwd = strdup(responses[i].response);
2129 free (compose_status);
2135 return (si->unlock_state == ul_finished) ? 0 : -1;
2139 free (compose_status);
2144 for (i = 0; i < num_msg; ++i)
2145 if (responses[i].response)
2146 free (responses[i].response);
2155 auth_finished_cb (saver_info *si)
2160 /* If we have something to say, put the dialog back up for a few seconds
2161 to display it. Otherwise, don't bother.
2164 if (si->unlock_state == ul_fail && /* failed with caps lock on */
2165 si->pw_data && si->pw_data->caps_p)
2166 s = "Authentication failed (Caps Lock?)";
2167 else if (si->unlock_state == ul_fail) /* failed without caps lock */
2168 s = "Authentication failed!";
2169 else if (si->unlock_state == ul_success && /* good, but report failures */
2170 si->unlock_failures > 0)
2172 if (si->unlock_failures == 1)
2173 s = "There has been\n1 failed login attempt.";
2176 sprintf (buf, "There have been\n%d failed login attempts.",
2177 si->unlock_failures);
2180 si->unlock_failures = 0;
2182 else /* good, with no failures, */
2183 goto END; /* or timeout, or cancel. */
2185 make_passwd_window (si, s, NULL, True);
2186 XSync (si->dpy, False);
2190 time_t start = time ((time_t *) 0);
2192 while (time ((time_t *) 0) < start + secs)
2193 if (XPending (si->dpy))
2195 XNextEvent (si->dpy, &event);
2196 if (event.xany.window == si->passwd_dialog &&
2197 event.xany.type == Expose)
2198 draw_passwd_window (si);
2199 else if (event.xany.type == ButtonPress ||
2200 event.xany.type == KeyPress)
2202 XSync (si->dpy, False);
2205 usleep (250000); /* 1/4 second */
2210 destroy_passwd_window (si);
2215 unlock_p (saver_info *si)
2217 saver_preferences *p = &si->prefs;
2221 fprintf(stderr, "%s: Error: no unlock function specified!\n", blurb());
2225 raise_window (si, True, True, True);
2227 xss_authenticate(si, p->verbose_p);
2229 return (si->unlock_state == ul_success);
2234 set_locked_p (saver_info *si, Bool locked_p)
2236 si->locked_p = locked_p;
2238 #ifdef HAVE_XHPDISABLERESET
2239 hp_lock_reset (si, locked_p); /* turn off/on C-Sh-Reset */
2241 #ifdef HAVE_XF86VMODE
2242 xfree_lock_mode_switch (si, locked_p); /* turn off/on C-Alt-Plus */
2244 #ifdef HAVE_XF86MISCSETGRABKEYSSTATE
2245 xfree_lock_grab_smasher (si, locked_p); /* turn off/on C-Alt-KP-*,/ */
2248 store_saver_status (si); /* store locked-p */
2252 #else /* NO_LOCKING -- whole file */
2255 set_locked_p (saver_info *si, Bool locked_p)
2257 if (locked_p) abort();
2260 #endif /* !NO_LOCKING */