1 /* lock.c --- handling the password dialog for locking-mode.
2 * xscreensaver, Copyright (c) 1993-2014 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 if (!info_msg && senescent_p())
492 "This version of XScreenSaver\n"
493 "is very old! Please upgrade!\n");
495 pw->info_label = mlstring_new(info_msg ? info_msg : pw->body_label,
496 pw->label_font, max_string_width_px);
499 int direction, ascent, descent;
505 /* Measure the heading_label. */
506 XTextExtents (pw->heading_font,
507 pw->heading_label, strlen(pw->heading_label),
508 &direction, &ascent, &descent, &overall);
509 if (overall.width > pw->width) pw->width = overall.width;
510 pw->height += ascent + descent;
512 /* Measure the uname_label. */
513 if ((strlen(pw->uname_label)) && pw->show_uname_p)
515 XTextExtents (pw->uname_font,
516 pw->uname_label, strlen(pw->uname_label),
517 &direction, &ascent, &descent, &overall);
518 if (overall.width > pw->width) pw->width = overall.width;
519 pw->height += ascent + descent;
523 Dimension w2 = 0, w3 = 0, button_w = 0;
524 Dimension h2 = 0, h3 = 0, button_h = 0;
525 const char *passwd_string = SAMPLE_INPUT;
527 /* Measure the user_label. */
528 XTextExtents (pw->label_font,
529 pw->user_label, strlen(pw->user_label),
530 &direction, &ascent, &descent, &overall);
531 if (overall.width > w2) w2 = overall.width;
532 h2 += ascent + descent;
534 /* Measure the info_label. */
535 if (pw->info_label->overall_width > pw->width)
536 pw->width = pw->info_label->overall_width;
537 h2 += pw->info_label->overall_height;
539 /* Measure the user string. */
540 XTextExtents (pw->passwd_font,
541 si->user, strlen(si->user),
542 &direction, &ascent, &descent, &overall);
543 overall.width += (pw->shadow_width * 4);
544 ascent += (pw->shadow_width * 4);
545 if (overall.width > w3) w3 = overall.width;
546 h3 += ascent + descent;
548 /* Measure the (dummy) passwd_string. */
551 XTextExtents (pw->passwd_font,
552 passwd_string, strlen(passwd_string),
553 &direction, &ascent, &descent, &overall);
554 overall.width += (pw->shadow_width * 4);
555 ascent += (pw->shadow_width * 4);
556 if (overall.width > w3) w3 = overall.width;
557 h3 += ascent + descent;
559 /* Measure the prompt_label. */
560 max_string_width_px -= w3;
561 pw->prompt_label = mlstring_new (prompt, pw->label_font,
562 max_string_width_px);
564 if (pw->prompt_label->overall_width > w2)
565 w2 = pw->prompt_label->overall_width;
567 h2 += pw->prompt_label->overall_height;
569 w2 = w2 + w3 + (pw->shadow_width * 2);
573 /* The "Unlock" button. */
574 XTextExtents (pw->label_font,
575 pw->unlock_label, strlen(pw->unlock_label),
576 &direction, &ascent, &descent, &overall);
577 button_w = overall.width;
578 button_h = ascent + descent;
580 /* Add some horizontal padding inside the button. */
583 button_w += ((ascent + descent) / 2) + (pw->shadow_width * 2);
584 button_h += ((ascent + descent) / 2) + (pw->shadow_width * 2);
586 pw->unlock_button_width = button_w;
587 pw->unlock_button_height = button_h;
589 w2 = MAX (w2, button_w);
590 h2 += button_h * 1.5;
592 /* The "New Login" button */
593 pw->login_button_width = 0;
594 pw->login_button_height = 0;
596 if (pw->login_button_p)
598 pw->login_button_enabled_p = True;
600 /* Measure the "New Login" button */
601 XTextExtents (pw->button_font, pw->login_label,
602 strlen (pw->login_label),
603 &direction, &ascent, &descent, &overall);
604 button_w = overall.width;
605 button_h = ascent + descent;
607 /* Add some horizontal padding inside the buttons. */
610 button_w += ((ascent + descent) / 2) + (pw->shadow_width * 2);
611 button_h += ((ascent + descent) / 2) + (pw->shadow_width * 2);
613 pw->login_button_width = button_w;
614 pw->login_button_height = button_h;
616 if (button_h > pw->unlock_button_height)
617 h2 += (button_h * 1.5 - pw->unlock_button_height * 1.5);
619 /* Use (2 * shadow_width) spacing between the buttons. Another
620 (2 * shadow_width) is required to account for button shadows. */
622 button_w + pw->unlock_button_width +
623 (pw->shadow_width * 4));
626 if (w2 > pw->width) pw->width = w2;
630 pw->width += (pw->internal_border * 2);
631 pw->height += (pw->internal_border * 4);
633 pw->width += pw->thermo_width + (pw->shadow_width * 3);
635 if (pw->preferred_logo_height > pw->height)
636 pw->height = pw->logo_height = pw->preferred_logo_height;
637 else if (pw->height > pw->preferred_logo_height)
638 pw->logo_height = pw->height;
640 pw->logo_width = pw->logo_height;
642 pw->width += pw->logo_width;
645 attrmask |= CWOverrideRedirect; attrs.override_redirect = True;
647 if (debug_passwd_window_p)
648 attrs.override_redirect = False; /* kludge for test-passwd.c */
650 attrmask |= CWEventMask;
651 attrs.event_mask = (ExposureMask | KeyPressMask |
652 ButtonPressMask | ButtonReleaseMask);
654 /* Figure out where on the desktop to place the window so that it will
655 actually be visible; this takes into account virtual viewports as
658 saver_screen_info *ssi = &si->screens [mouse_screen (si)];
663 if (si->prefs.debug_p) w /= 2;
664 pw->x = x + ((w + pw->width) / 2) - pw->width;
665 pw->y = y + ((h + pw->height) / 2) - pw->height;
666 if (pw->x < x) pw->x = x;
667 if (pw->y < y) pw->y = y;
670 pw->border_width = get_integer_resource (si->dpy, "passwd.borderWidth",
671 "Dialog.BorderWidth");
673 /* Only create the window the first time around */
674 if (!si->passwd_dialog)
677 XCreateWindow (si->dpy,
678 RootWindowOfScreen(screen),
679 pw->x, pw->y, pw->width, pw->height, pw->border_width,
680 DefaultDepthOfScreen (screen), InputOutput,
681 DefaultVisualOfScreen(screen),
683 XSetWindowBackground (si->dpy, si->passwd_dialog, pw->background);
684 XSetWindowBorder (si->dpy, si->passwd_dialog, pw->border);
686 /* We use the default visual, not ssi->visual, so that the logo pixmap's
687 visual matches that of the si->passwd_dialog window. */
688 pw->logo_pixmap = xscreensaver_logo (ssi->screen,
689 /* ssi->current_visual, */
690 DefaultVisualOfScreen(screen),
691 si->passwd_dialog, cmap,
693 &pw->logo_pixels, &pw->logo_npixels,
694 &pw->logo_clipmask, True);
696 else /* On successive prompts, just resize the window */
699 unsigned int mask = CWX | CWY | CWWidth | CWHeight;
703 wc.width = pw->width;
704 wc.height = pw->height;
706 XConfigureWindow (si->dpy, si->passwd_dialog, mask, &wc);
709 restore_background(si);
711 XMapRaised (si->dpy, si->passwd_dialog);
712 XSync (si->dpy, False);
714 move_mouse_grab (si, si->passwd_dialog,
716 pw->prompt_screen->number);
722 XInstallColormap (si->dpy, cmap);
723 draw_passwd_window (si);
730 draw_passwd_window (saver_info *si)
732 passwd_dialog_data *pw = si->pw_data;
736 int x1, x2, x3, y1, y2;
741 pw->passwd_changed_p = True;
742 pw->button_state_changed_p = True;
744 /* This height is the height of all the elements, not to be confused with
745 * the overall window height which is pw->height. It is used to compute
746 * the amount of spacing (padding) between elements. */
747 height = (pw->heading_font->ascent + pw->heading_font->descent +
748 pw->info_label->overall_height +
749 MAX (((pw->label_font->ascent + pw->label_font->descent) +
750 (pw->prompt_label ? pw->prompt_label->overall_height : 0)),
751 ((pw->passwd_font->ascent + pw->passwd_font->descent) +
752 (pw->shadow_width * 2)) * (pw->prompt_label ? 2 : 1)) +
753 pw->date_font->ascent + pw->date_font->descent);
755 if ((strlen(pw->uname_label)) && pw->show_uname_p)
756 height += (pw->uname_font->ascent + pw->uname_font->descent);
758 height += ((pw->button_font->ascent + pw->button_font->descent) * 2 +
759 2 * pw->shadow_width);
761 spacing = ((pw->height - 2 * pw->shadow_width
762 - pw->internal_border - height)
765 if (spacing < 0) spacing = 0;
767 gcv.foreground = pw->foreground;
768 gc1 = XCreateGC (si->dpy, si->passwd_dialog, GCForeground, &gcv);
769 gc2 = XCreateGC (si->dpy, si->passwd_dialog, GCForeground, &gcv);
770 x1 = pw->logo_width + pw->thermo_width + (pw->shadow_width * 3);
771 x3 = pw->width - (pw->shadow_width * 2);
772 y1 = (pw->shadow_width * 2) + spacing + spacing;
776 XSetFont (si->dpy, gc1, pw->heading_font->fid);
777 sw = string_width (pw->heading_font, pw->heading_label);
778 x2 = (x1 + ((x3 - x1 - sw) / 2));
779 y1 += spacing + pw->heading_font->ascent + pw->heading_font->descent;
780 XDrawString (si->dpy, si->passwd_dialog, gc1, x2, y1,
781 pw->heading_label, strlen(pw->heading_label));
783 /* uname below top heading
785 if ((strlen(pw->uname_label)) && pw->show_uname_p)
787 XSetFont (si->dpy, gc1, pw->uname_font->fid);
788 y1 += spacing + pw->uname_font->ascent + pw->uname_font->descent;
789 sw = string_width (pw->uname_font, pw->uname_label);
790 x2 = (x1 + ((x3 - x1 - sw) / 2));
791 XDrawString (si->dpy, si->passwd_dialog, gc1, x2, y1,
792 pw->uname_label, strlen(pw->uname_label));
795 /* the info_label (below uname)
797 x2 = (x1 + ((x3 - x1 - pw->info_label->overall_width) / 2));
798 y1 += spacing + pw->info_label->font_height / 2;
799 mlstring_draw(si->dpy, si->passwd_dialog, gc1, pw->info_label,
801 y1 += pw->info_label->overall_height;
804 tb_height = (pw->passwd_font->ascent + pw->passwd_font->descent +
805 (pw->shadow_width * 4));
807 /* the "User:" prompt
810 XSetForeground (si->dpy, gc1, pw->foreground);
811 XSetFont (si->dpy, gc1, pw->label_font->fid);
812 y1 += (spacing + tb_height + pw->shadow_width);
813 x2 = (x1 + pw->internal_border +
814 MAX(string_width (pw->label_font, pw->user_label),
815 pw->prompt_label ? pw->prompt_label->overall_width : 0));
816 XDrawString (si->dpy, si->passwd_dialog, gc1,
817 x2 - string_width (pw->label_font, pw->user_label),
818 y1 - pw->passwd_font->descent,
819 pw->user_label, strlen(pw->user_label));
821 /* the prompt_label prompt
823 if (pw->prompt_label)
825 y1 += tb_height - pw->label_font->ascent + pw->shadow_width;
826 mlstring_draw(si->dpy, si->passwd_dialog, gc1, pw->prompt_label,
827 x2 - pw->prompt_label->overall_width, y1);
830 /* the "user name" text field
833 XSetForeground (si->dpy, gc1, pw->passwd_foreground);
834 XSetForeground (si->dpy, gc2, pw->passwd_background);
835 XSetFont (si->dpy, gc1, pw->passwd_font->fid);
836 y1 += (spacing + tb_height);
837 x2 += (pw->shadow_width * 4);
839 pw->passwd_field_width = x3 - x2 - pw->internal_border;
840 pw->passwd_field_height = (pw->passwd_font->ascent +
841 pw->passwd_font->descent +
844 XFillRectangle (si->dpy, si->passwd_dialog, gc2,
845 x2 - pw->shadow_width,
846 y1 - (pw->passwd_font->ascent + pw->passwd_font->descent),
847 pw->passwd_field_width, pw->passwd_field_height);
848 XDrawString (si->dpy, si->passwd_dialog, gc1,
850 y1 - pw->passwd_font->descent,
851 si->user, strlen(si->user));
853 /* the password/prompt text field
855 if (pw->prompt_label)
857 y1 += (spacing + pw->prompt_label->overall_height + pw->shadow_width*2);
859 pw->passwd_field_x = x2 - pw->shadow_width;
860 pw->passwd_field_y = y1 - (pw->passwd_font->ascent +
861 pw->passwd_font->descent);
864 /* The shadow around the text fields
867 y1 += (spacing + (pw->shadow_width * 3));
868 x1 = x2 - (pw->shadow_width * 2);
869 x2 = pw->passwd_field_width + (pw->shadow_width * 2);
870 y2 = pw->passwd_field_height + (pw->shadow_width * 2);
872 draw_shaded_rectangle (si->dpy, si->passwd_dialog,
875 pw->shadow_bottom, pw->shadow_top);
877 if (pw->prompt_label)
879 y1 += (spacing + pw->prompt_label->overall_height + pw->shadow_width*2);
880 draw_shaded_rectangle (si->dpy, si->passwd_dialog,
883 pw->shadow_bottom, pw->shadow_top);
887 /* The date, below the text fields
891 time_t now = time ((time_t *) 0);
892 struct tm *tm = localtime (&now);
893 memset (buf, 0, sizeof(buf));
894 strftime (buf, sizeof(buf)-1, pw->date_label, tm);
896 XSetFont (si->dpy, gc1, pw->date_font->fid);
897 y1 += pw->shadow_width;
898 y1 += (spacing + tb_height);
900 sw = string_width (pw->date_font, buf);
902 XDrawString (si->dpy, si->passwd_dialog, gc1, x2, y1, buf, strlen(buf));
905 /* Set up the GCs for the "New Login" and "Unlock" buttons.
907 XSetForeground(si->dpy, gc1, pw->button_foreground);
908 XSetForeground(si->dpy, gc2, pw->button_background);
909 XSetFont(si->dpy, gc1, pw->button_font->fid);
911 /* The "Unlock" button */
912 x2 = pw->width - pw->internal_border - (pw->shadow_width * 2);
914 /* right aligned button */
915 x1 = x2 - pw->unlock_button_width;
917 /* Add half the difference between y1 and the internal edge.
918 * It actually looks better if the internal border is ignored. */
919 y1 += ((pw->height - MAX (pw->unlock_button_height, pw->login_button_height)
923 pw->unlock_button_x = x1;
924 pw->unlock_button_y = y1;
926 /* The "New Login" button
928 if (pw->login_button_p)
930 /* Using the same GC as for the Unlock button */
932 sw = string_width (pw->button_font, pw->login_label);
934 /* left aligned button */
935 x1 = (pw->logo_width + pw->thermo_width + (pw->shadow_width * 3) +
936 pw->internal_border);
938 pw->login_button_x = x1;
939 pw->login_button_y = y1;
944 x1 = pw->shadow_width * 6;
945 y1 = pw->shadow_width * 6;
946 x2 = pw->logo_width - (pw->shadow_width * 12);
947 y2 = pw->logo_height - (pw->shadow_width * 12);
953 unsigned int w, h, bw, d;
954 XGetGeometry (si->dpy, pw->logo_pixmap, &root, &x, &y, &w, &h, &bw, &d);
955 XSetForeground (si->dpy, gc1, pw->foreground);
956 XSetBackground (si->dpy, gc1, pw->background);
957 XSetClipMask (si->dpy, gc1, pw->logo_clipmask);
958 XSetClipOrigin (si->dpy, gc1,
959 x1 + ((x2 - (int)w) / 2),
960 y1 + ((y2 - (int)h) / 2));
962 XCopyPlane (si->dpy, pw->logo_pixmap, si->passwd_dialog, gc1,
964 x1 + ((x2 - (int)w) / 2),
965 y1 + ((y2 - (int)h) / 2),
968 XCopyArea (si->dpy, pw->logo_pixmap, si->passwd_dialog, gc1,
970 x1 + ((x2 - (int)w) / 2),
971 y1 + ((y2 - (int)h) / 2));
976 XSetForeground (si->dpy, gc1, pw->thermo_foreground);
977 XSetForeground (si->dpy, gc2, pw->thermo_background);
979 pw->thermo_field_x = pw->logo_width + pw->shadow_width;
980 pw->thermo_field_y = pw->shadow_width * 5;
981 pw->thermo_field_height = pw->height - (pw->shadow_width * 10);
984 /* Solid border inside the logo box. */
985 XSetForeground (si->dpy, gc1, pw->foreground);
986 XDrawRectangle (si->dpy, si->passwd_dialog, gc1, x1, y1, x2-1, y2-1);
989 /* The shadow around the logo
991 draw_shaded_rectangle (si->dpy, si->passwd_dialog,
992 pw->shadow_width * 4,
993 pw->shadow_width * 4,
994 pw->logo_width - (pw->shadow_width * 8),
995 pw->logo_height - (pw->shadow_width * 8),
997 pw->shadow_bottom, pw->shadow_top);
999 /* The shadow around the thermometer
1001 draw_shaded_rectangle (si->dpy, si->passwd_dialog,
1003 pw->shadow_width * 4,
1004 pw->thermo_width + (pw->shadow_width * 2),
1005 pw->height - (pw->shadow_width * 8),
1007 pw->shadow_bottom, pw->shadow_top);
1010 /* Solid border inside the thermometer. */
1011 XSetForeground (si->dpy, gc1, pw->foreground);
1012 XDrawRectangle (si->dpy, si->passwd_dialog, gc1,
1013 pw->thermo_field_x, pw->thermo_field_y,
1014 pw->thermo_width - 1, pw->thermo_field_height - 1);
1017 /* The shadow around the whole window
1019 draw_shaded_rectangle (si->dpy, si->passwd_dialog,
1020 0, 0, pw->width, pw->height, pw->shadow_width,
1021 pw->shadow_top, pw->shadow_bottom);
1023 XFreeGC (si->dpy, gc1);
1024 XFreeGC (si->dpy, gc2);
1026 update_passwd_window (si, pw->passwd_string, pw->ratio);
1030 draw_button(Display *dpy,
1033 unsigned long foreground, unsigned long background,
1036 int width, int height,
1038 Pixel shadow_light, Pixel shadow_dark,
1044 int label_x, label_y;
1046 gcv.foreground = foreground;
1047 gcv.font = font->fid;
1048 gc1 = XCreateGC(dpy, dialog, GCForeground|GCFont, &gcv);
1049 gcv.foreground = background;
1050 gc2 = XCreateGC(dpy, dialog, GCForeground, &gcv);
1052 XFillRectangle(dpy, dialog, gc2,
1053 x, y, width, height);
1055 sw = string_width(font, label);
1057 label_x = x + ((width - sw) / 2);
1058 label_y = (y + (height - (font->ascent + font->descent)) / 2 + font->ascent);
1066 XDrawString(dpy, dialog, gc1, label_x, label_y, label, strlen(label));
1071 draw_shaded_rectangle(dpy, dialog, x, y, width, height,
1072 shadow_width, shadow_light, shadow_dark);
1076 update_passwd_window (saver_info *si, const char *printed_passwd, float ratio)
1078 passwd_dialog_data *pw = si->pw_data;
1082 XRectangle rects[1];
1085 gcv.foreground = pw->passwd_foreground;
1086 gcv.font = pw->passwd_font->fid;
1087 gc1 = XCreateGC (si->dpy, si->passwd_dialog, GCForeground|GCFont, &gcv);
1088 gcv.foreground = pw->passwd_background;
1089 gc2 = XCreateGC (si->dpy, si->passwd_dialog, GCForeground, &gcv);
1093 char *s = strdup (printed_passwd);
1094 if (pw->passwd_string) free (pw->passwd_string);
1095 pw->passwd_string = s;
1098 if (pw->prompt_label)
1101 /* the "password" text field
1103 rects[0].x = pw->passwd_field_x;
1104 rects[0].y = pw->passwd_field_y;
1105 rects[0].width = pw->passwd_field_width;
1106 rects[0].height = pw->passwd_field_height;
1108 /* The user entry (password) field is double buffered.
1109 * This avoids flickering, particularly in synchronous mode. */
1111 if (pw->passwd_changed_p)
1113 pw->passwd_changed_p = False;
1115 if (pw->user_entry_pixmap)
1117 XFreePixmap(si->dpy, pw->user_entry_pixmap);
1118 pw->user_entry_pixmap = 0;
1121 pw->user_entry_pixmap =
1122 XCreatePixmap (si->dpy, si->passwd_dialog,
1123 rects[0].width, rects[0].height,
1124 DefaultDepthOfScreen (pw->prompt_screen->screen));
1126 XFillRectangle (si->dpy, pw->user_entry_pixmap, gc2,
1127 0, 0, rects[0].width, rects[0].height);
1129 XDrawString (si->dpy, pw->user_entry_pixmap, gc1,
1131 pw->passwd_font->ascent,
1132 pw->passwd_string, strlen(pw->passwd_string));
1134 /* Ensure the new pixmap gets copied to the window */
1141 if (pw->i_beam == 0)
1143 /* Make the I-beam disappear */
1144 XCopyArea(si->dpy, pw->user_entry_pixmap, si->passwd_dialog, gc2,
1145 0, 0, rects[0].width, rects[0].height,
1146 rects[0].x, rects[0].y);
1148 else if (pw->i_beam == 1)
1150 /* Make the I-beam appear */
1151 x = (rects[0].x + pw->shadow_width +
1152 string_width (pw->passwd_font, pw->passwd_string));
1153 y = rects[0].y + pw->shadow_width;
1155 if (x > rects[0].x + rects[0].width - 1)
1156 x = rects[0].x + rects[0].width - 1;
1157 XDrawLine (si->dpy, si->passwd_dialog, gc1,
1159 x, y + pw->passwd_font->ascent +
1160 pw->passwd_font->descent-1);
1163 pw->i_beam = (pw->i_beam + 1) % 4;
1169 y = (pw->thermo_field_height - 2) * (1.0 - pw->ratio);
1172 XFillRectangle (si->dpy, si->passwd_dialog, gc2,
1173 pw->thermo_field_x + 1,
1174 pw->thermo_field_y + 1,
1177 XSetForeground (si->dpy, gc1, pw->thermo_foreground);
1178 XFillRectangle (si->dpy, si->passwd_dialog, gc1,
1179 pw->thermo_field_x + 1,
1180 pw->thermo_field_y + 1 + y,
1182 MAX (0, pw->thermo_field_height - y - 2));
1185 if (pw->button_state_changed_p)
1187 pw->button_state_changed_p = False;
1189 /* The "Unlock" button
1191 draw_button(si->dpy, si->passwd_dialog, pw->button_font,
1192 pw->button_foreground, pw->button_background,
1194 pw->unlock_button_x, pw->unlock_button_y,
1195 pw->unlock_button_width, pw->unlock_button_height,
1197 (pw->unlock_button_down_p ? pw->shadow_bottom :
1199 (pw->unlock_button_down_p ? pw->shadow_top :
1201 pw->unlock_button_down_p);
1203 /* The "New Login" button
1205 if (pw->login_button_p)
1207 draw_button(si->dpy, si->passwd_dialog, pw->button_font,
1208 (pw->login_button_enabled_p
1209 ? pw->passwd_foreground
1210 : pw->shadow_bottom),
1211 pw->button_background,
1213 pw->login_button_x, pw->login_button_y,
1214 pw->login_button_width, pw->login_button_height,
1216 (pw->login_button_down_p
1219 (pw->login_button_down_p
1221 : pw->shadow_bottom),
1222 pw->login_button_down_p);
1226 XFreeGC (si->dpy, gc1);
1227 XFreeGC (si->dpy, gc2);
1228 XSync (si->dpy, False);
1233 restore_background (saver_info *si)
1235 passwd_dialog_data *pw = si->pw_data;
1236 saver_screen_info *ssi = pw->prompt_screen;
1240 gcv.function = GXcopy;
1242 gc = XCreateGC (si->dpy, ssi->screensaver_window, GCFunction, &gcv);
1244 XCopyArea (si->dpy, pw->save_under,
1245 ssi->screensaver_window, gc,
1247 ssi->width, ssi->height,
1250 XFreeGC (si->dpy, gc);
1254 /* Frees anything created by make_passwd_window */
1256 cleanup_passwd_window (saver_info *si)
1258 passwd_dialog_data *pw;
1260 if (!(pw = si->pw_data))
1265 mlstring_free(pw->info_label);
1269 if (pw->prompt_label)
1271 mlstring_free(pw->prompt_label);
1272 pw->prompt_label = 0;
1275 memset (pw->typed_passwd, 0, sizeof(pw->typed_passwd));
1276 memset (pw->typed_passwd_char_size, 0, sizeof(pw->typed_passwd_char_size));
1277 memset (pw->passwd_string, 0, strlen(pw->passwd_string));
1281 XtRemoveTimeOut (pw->timer);
1285 if (pw->user_entry_pixmap)
1287 XFreePixmap(si->dpy, pw->user_entry_pixmap);
1288 pw->user_entry_pixmap = 0;
1294 destroy_passwd_window (saver_info *si)
1296 saver_preferences *p = &si->prefs;
1297 passwd_dialog_data *pw = si->pw_data;
1298 saver_screen_info *ssi = pw->prompt_screen;
1299 Colormap cmap = DefaultColormapOfScreen (ssi->screen);
1300 Pixel black = BlackPixelOfScreen (ssi->screen);
1301 Pixel white = WhitePixelOfScreen (ssi->screen);
1304 cleanup_passwd_window (si);
1306 if (si->cached_passwd)
1308 char *wipe = si->cached_passwd;
1313 free(si->cached_passwd);
1314 si->cached_passwd = NULL;
1317 move_mouse_grab (si, RootWindowOfScreen (ssi->screen),
1318 ssi->cursor, ssi->number);
1320 if (pw->passwd_cursor)
1321 XFreeCursor (si->dpy, pw->passwd_cursor);
1324 fprintf (stderr, "%s: %d: moving mouse back to %d,%d.\n",
1325 blurb(), ssi->number,
1326 pw->previous_mouse_x, pw->previous_mouse_y);
1328 XWarpPointer (si->dpy, None, RootWindowOfScreen (ssi->screen),
1330 pw->previous_mouse_x, pw->previous_mouse_y);
1331 XSync (si->dpy, False);
1333 while (XCheckMaskEvent (si->dpy, PointerMotionMask, &event))
1335 fprintf (stderr, "%s: discarding MotionNotify event.\n", blurb());
1338 if (si->using_xinput_extension && si->xinput_DeviceMotionNotify)
1339 while (XCheckTypedEvent (si->dpy, si->xinput_DeviceMotionNotify, &event))
1341 fprintf (stderr, "%s: discarding DeviceMotionNotify event.\n",
1345 if (si->passwd_dialog)
1347 if (si->prefs.verbose_p)
1348 fprintf (stderr, "%s: %d: destroying password dialog.\n",
1349 blurb(), pw->prompt_screen->number);
1351 XDestroyWindow (si->dpy, si->passwd_dialog);
1352 si->passwd_dialog = 0;
1357 restore_background(si);
1358 XFreePixmap (si->dpy, pw->save_under);
1362 if (pw->heading_label) free (pw->heading_label);
1363 if (pw->body_label) free (pw->body_label);
1364 if (pw->user_label) free (pw->user_label);
1365 if (pw->date_label) free (pw->date_label);
1366 if (pw->login_label) free (pw->login_label);
1367 if (pw->unlock_label) free (pw->unlock_label);
1368 if (pw->passwd_string) free (pw->passwd_string);
1369 if (pw->uname_label) free (pw->uname_label);
1371 if (pw->heading_font) XFreeFont (si->dpy, pw->heading_font);
1372 if (pw->body_font) XFreeFont (si->dpy, pw->body_font);
1373 if (pw->label_font) XFreeFont (si->dpy, pw->label_font);
1374 if (pw->passwd_font) XFreeFont (si->dpy, pw->passwd_font);
1375 if (pw->date_font) XFreeFont (si->dpy, pw->date_font);
1376 if (pw->button_font) XFreeFont (si->dpy, pw->button_font);
1377 if (pw->uname_font) XFreeFont (si->dpy, pw->uname_font);
1379 if (pw->foreground != black && pw->foreground != white)
1380 XFreeColors (si->dpy, cmap, &pw->foreground, 1, 0L);
1381 if (pw->background != black && pw->background != white)
1382 XFreeColors (si->dpy, cmap, &pw->background, 1, 0L);
1383 if (!(pw->button_foreground == black || pw->button_foreground == white))
1384 XFreeColors (si->dpy, cmap, &pw->button_foreground, 1, 0L);
1385 if (!(pw->button_background == black || pw->button_background == white))
1386 XFreeColors (si->dpy, cmap, &pw->button_background, 1, 0L);
1387 if (pw->passwd_foreground != black && pw->passwd_foreground != white)
1388 XFreeColors (si->dpy, cmap, &pw->passwd_foreground, 1, 0L);
1389 if (pw->passwd_background != black && pw->passwd_background != white)
1390 XFreeColors (si->dpy, cmap, &pw->passwd_background, 1, 0L);
1391 if (pw->thermo_foreground != black && pw->thermo_foreground != white)
1392 XFreeColors (si->dpy, cmap, &pw->thermo_foreground, 1, 0L);
1393 if (pw->thermo_background != black && pw->thermo_background != white)
1394 XFreeColors (si->dpy, cmap, &pw->thermo_background, 1, 0L);
1395 if (pw->shadow_top != black && pw->shadow_top != white)
1396 XFreeColors (si->dpy, cmap, &pw->shadow_top, 1, 0L);
1397 if (pw->shadow_bottom != black && pw->shadow_bottom != white)
1398 XFreeColors (si->dpy, cmap, &pw->shadow_bottom, 1, 0L);
1400 if (pw->logo_pixmap)
1401 XFreePixmap (si->dpy, pw->logo_pixmap);
1402 if (pw-> logo_clipmask)
1403 XFreePixmap (si->dpy, pw->logo_clipmask);
1404 if (pw->logo_pixels)
1406 if (pw->logo_npixels)
1407 XFreeColors (si->dpy, cmap, pw->logo_pixels, pw->logo_npixels, 0L);
1408 free (pw->logo_pixels);
1409 pw->logo_pixels = 0;
1410 pw->logo_npixels = 0;
1414 XFreePixmap (si->dpy, pw->save_under);
1417 XInstallColormap (si->dpy, cmap);
1419 memset (pw, 0, sizeof(*pw));
1425 #if defined(HAVE_XF86MISCSETGRABKEYSSTATE) || defined(HAVE_XF86VMODE)
1427 static Bool error_handler_hit_p = False;
1430 ignore_all_errors_ehandler (Display *dpy, XErrorEvent *error)
1432 error_handler_hit_p = True;
1436 #endif /* HAVE_XF86MISCSETGRABKEYSSTATE || HAVE_XF86VMODE */
1439 #ifdef HAVE_XHPDISABLERESET
1440 /* This function enables and disables the C-Sh-Reset hot-key, which
1441 normally resets the X server (logging out the logged-in user.)
1442 We don't want random people to be able to do that while the
1446 hp_lock_reset (saver_info *si, Bool lock_p)
1448 static Bool hp_locked_p = False;
1450 /* Calls to XHPDisableReset and XHPEnableReset must be balanced,
1451 or BadAccess errors occur. (It's ok for this to be global,
1452 since it affects the whole machine, not just the current screen.)
1454 if (hp_locked_p == lock_p)
1458 XHPDisableReset (si->dpy);
1460 XHPEnableReset (si->dpy);
1461 hp_locked_p = lock_p;
1463 #endif /* HAVE_XHPDISABLERESET */
1466 #ifdef HAVE_XF86MISCSETGRABKEYSSTATE
1468 /* This function enables and disables the Ctrl-Alt-KP_star and
1469 Ctrl-Alt-KP_slash hot-keys, which (in XFree86 4.2) break any
1470 grabs and/or kill the grabbing client. That would effectively
1471 unlock the screen, so we don't like that.
1473 The Ctrl-Alt-KP_star and Ctrl-Alt-KP_slash hot-keys only exist
1474 if AllowDeactivateGrabs and/or AllowClosedownGrabs are turned on
1475 in XF86Config. I believe they are disabled by default.
1477 This does not affect any other keys (specifically Ctrl-Alt-BS or
1478 Ctrl-Alt-F1) but I wish it did. Maybe it will someday.
1481 xfree_lock_grab_smasher (saver_info *si, Bool lock_p)
1483 saver_preferences *p = &si->prefs;
1486 XErrorHandler old_handler;
1488 if (!XF86MiscQueryExtension(si->dpy, &event, &error))
1491 XSync (si->dpy, False);
1492 error_handler_hit_p = False;
1493 old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1494 XSync (si->dpy, False);
1495 status = XF86MiscSetGrabKeysState (si->dpy, !lock_p);
1496 XSync (si->dpy, False);
1497 if (error_handler_hit_p) status = 666;
1499 if (!lock_p && status == MiscExtGrabStateAlready)
1500 status = MiscExtGrabStateSuccess; /* shut up, consider this success */
1502 if (p->verbose_p && status != MiscExtGrabStateSuccess)
1503 fprintf (stderr, "%s: error: XF86MiscSetGrabKeysState(%d) returned %s\n",
1505 (status == MiscExtGrabStateSuccess ? "MiscExtGrabStateSuccess" :
1506 status == MiscExtGrabStateLocked ? "MiscExtGrabStateLocked" :
1507 status == MiscExtGrabStateAlready ? "MiscExtGrabStateAlready" :
1508 status == 666 ? "an X error" :
1511 XSync (si->dpy, False);
1512 XSetErrorHandler (old_handler);
1513 XSync (si->dpy, False);
1515 #endif /* HAVE_XF86MISCSETGRABKEYSSTATE */
1519 /* This function enables and disables the C-Alt-Plus and C-Alt-Minus
1520 hot-keys, which normally change the resolution of the X server.
1521 We don't want people to be able to switch the server resolution
1522 while the screen is locked, because if they switch to a higher
1523 resolution, it could cause part of the underlying desktop to become
1526 #ifdef HAVE_XF86VMODE
1529 xfree_lock_mode_switch (saver_info *si, Bool lock_p)
1531 static Bool any_mode_locked_p = False;
1532 saver_preferences *p = &si->prefs;
1534 int real_nscreens = ScreenCount (si->dpy);
1537 XErrorHandler old_handler;
1539 if (any_mode_locked_p == lock_p)
1541 if (!XF86VidModeQueryExtension (si->dpy, &event, &error))
1544 for (screen = 0; screen < real_nscreens; screen++)
1546 XSync (si->dpy, False);
1547 old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1548 error_handler_hit_p = False;
1549 status = XF86VidModeLockModeSwitch (si->dpy, screen, lock_p);
1550 XSync (si->dpy, False);
1551 XSetErrorHandler (old_handler);
1552 if (error_handler_hit_p) status = False;
1555 any_mode_locked_p = lock_p;
1557 if (!status && (p->verbose_p || !lock_p))
1558 /* Only print this when verbose, or when we locked but can't unlock.
1559 I tried printing this message whenever it comes up, but
1560 mode-locking always fails if DontZoom is set in XF86Config. */
1561 fprintf (stderr, "%s: %d: unable to %s mode switching!\n",
1562 blurb(), screen, (lock_p ? "lock" : "unlock"));
1563 else if (p->verbose_p)
1564 fprintf (stderr, "%s: %d: %s mode switching.\n",
1565 blurb(), screen, (lock_p ? "locked" : "unlocked"));
1568 #endif /* HAVE_XF86VMODE */
1571 /* If the viewport has been scrolled since the screen was blanked,
1572 then scroll it back to where it belongs. This function only exists
1573 to patch over a very brief race condition.
1576 undo_vp_motion (saver_info *si)
1578 #ifdef HAVE_XF86VMODE
1579 saver_preferences *p = &si->prefs;
1581 int real_nscreens = ScreenCount (si->dpy);
1584 if (!XF86VidModeQueryExtension (si->dpy, &event, &error))
1587 for (screen = 0; screen < real_nscreens; screen++)
1589 saver_screen_info *ssi = &si->screens[screen];
1593 if (ssi->blank_vp_x == -1 && ssi->blank_vp_y == -1)
1595 if (!XF86VidModeGetViewPort (si->dpy, screen, &x, &y))
1597 if (ssi->blank_vp_x == x && ssi->blank_vp_y == y)
1600 /* We're going to move the viewport. The mouse has just been grabbed on
1601 (and constrained to, thus warped to) the password window, so it is no
1602 longer near the edge of the screen. However, wait a bit anyway, just
1603 to make sure the server drains its last motion event, so that the
1604 screen doesn't continue to scroll after we've reset the viewport.
1606 XSync (si->dpy, False);
1607 usleep (250000); /* 1/4 second */
1608 XSync (si->dpy, False);
1610 status = XF86VidModeSetViewPort (si->dpy, screen,
1611 ssi->blank_vp_x, ssi->blank_vp_y);
1615 "%s: %d: unable to move vp from (%d,%d) back to (%d,%d)!\n",
1616 blurb(), screen, x, y, ssi->blank_vp_x, ssi->blank_vp_y);
1617 else if (p->verbose_p)
1619 "%s: %d: vp moved to (%d,%d); moved it back to (%d,%d).\n",
1620 blurb(), screen, x, y, ssi->blank_vp_x, ssi->blank_vp_y);
1622 #endif /* HAVE_XF86VMODE */
1631 passwd_animate_timer (XtPointer closure, XtIntervalId *id)
1633 saver_info *si = (saver_info *) closure;
1635 passwd_dialog_data *pw = si->pw_data;
1639 pw->ratio -= (1.0 / ((double) si->prefs.passwd_timeout / (double) tick));
1643 if (si->unlock_state == ul_read)
1644 si->unlock_state = ul_time;
1647 update_passwd_window (si, 0, pw->ratio);
1649 if (si->unlock_state == ul_read)
1650 pw->timer = XtAppAddTimeOut (si->app, tick, passwd_animate_timer,
1655 idle_timer ((XtPointer) si, 0);
1659 static XComposeStatus *compose_status;
1662 handle_login_button (saver_info *si, XEvent *event)
1664 saver_preferences *p = &si->prefs;
1665 Bool mouse_in_box = False;
1667 passwd_dialog_data *pw = si->pw_data;
1668 saver_screen_info *ssi = pw->prompt_screen;
1670 if (! pw->login_button_enabled_p)
1674 (event->xbutton.x >= pw->login_button_x &&
1675 event->xbutton.x <= pw->login_button_x + pw->login_button_width &&
1676 event->xbutton.y >= pw->login_button_y &&
1677 event->xbutton.y <= pw->login_button_y + pw->login_button_height);
1679 if (ButtonRelease == event->xany.type &&
1680 pw->login_button_down_p &&
1683 /* Only allow them to press the button once: don't want to
1684 accidentally launch a dozen gdm choosers if the machine
1688 pw->login_button_enabled_p = False;
1691 pw->login_button_down_p = (mouse_in_box &&
1692 ButtonRelease != event->xany.type);
1694 update_passwd_window (si, 0, pw->ratio);
1697 fork_and_exec (ssi, p->new_login_command);
1702 handle_unlock_button (saver_info *si, XEvent *event)
1704 Bool mouse_in_box = False;
1705 passwd_dialog_data *pw = si->pw_data;
1708 (event->xbutton.x >= pw->unlock_button_x &&
1709 event->xbutton.x <= pw->unlock_button_x + pw->unlock_button_width &&
1710 event->xbutton.y >= pw->unlock_button_y &&
1711 event->xbutton.y <= pw->unlock_button_y + pw->unlock_button_height);
1713 if (ButtonRelease == event->xany.type &&
1714 pw->unlock_button_down_p &&
1716 finished_typing_passwd (si, pw);
1718 pw->unlock_button_down_p = (mouse_in_box &&
1719 ButtonRelease != event->xany.type);
1724 finished_typing_passwd (saver_info *si, passwd_dialog_data *pw)
1726 if (si->unlock_state == ul_read)
1728 update_passwd_window (si, "Checking...", pw->ratio);
1729 XSync (si->dpy, False);
1731 si->unlock_state = ul_finished;
1732 update_passwd_window (si, "", pw->ratio);
1737 handle_passwd_key (saver_info *si, XKeyEvent *event)
1739 passwd_dialog_data *pw = si->pw_data;
1740 unsigned char decoded [MAX_BYTES_PER_CHAR * 10]; /* leave some slack */
1743 /* XLookupString may return more than one character via XRebindKeysym;
1744 and on some systems it returns multi-byte UTF-8 characters (contrary
1745 to its documentation, which says it returns only Latin1.)
1747 It seems to only do so, however, if setlocale() has been called.
1748 See the code inside ENABLE_NLS in xscreensaver.c.
1750 int decoded_size = XLookupString (event, (char *)decoded, sizeof(decoded),
1751 &keysym, compose_status);
1755 const char *ks = XKeysymToString (keysym);
1757 fprintf(stderr, "## %-12s\t=> %d\t", (ks ? ks : "(null)"), decoded_size);
1758 for (i = 0; i < decoded_size; i++)
1759 fprintf(stderr, "%c", decoded[i]);
1760 fprintf(stderr, "\t");
1761 for (i = 0; i < decoded_size; i++)
1762 fprintf(stderr, "\\%03o", ((unsigned char *)decoded)[i]);
1763 fprintf(stderr, "\n");
1767 if (decoded_size > MAX_BYTES_PER_CHAR)
1769 /* The multi-byte character returned is too large. */
1774 decoded[decoded_size] = 0;
1775 pw->passwd_changed_p = True;
1777 /* Add 10% to the time remaining every time a key is pressed. */
1779 if (pw->ratio > 1) pw->ratio = 1;
1781 if (decoded_size == 1) /* Handle single-char commands */
1785 case '\010': case '\177': /* Backspace */
1787 /* kludgey way to get the number of "logical" characters. */
1788 int nchars = strlen (pw->typed_passwd_char_size);
1789 int nbytes = strlen (pw->typed_passwd);
1795 for (i = pw->typed_passwd_char_size[nchars-1]; i >= 0; i--)
1797 if (nbytes < 0) abort();
1798 pw->typed_passwd[nbytes--] = 0;
1800 pw->typed_passwd_char_size[nchars-1] = 0;
1805 case '\012': case '\015': /* Enter */
1806 finished_typing_passwd (si, pw);
1809 case '\033': /* Escape */
1810 si->unlock_state = ul_cancel;
1813 case '\025': case '\030': /* Erase line */
1814 memset (pw->typed_passwd, 0, sizeof (pw->typed_passwd));
1815 memset (pw->typed_passwd_char_size, 0,
1816 sizeof (pw->typed_passwd_char_size));
1820 if (*decoded < ' ' && *decoded != '\t') /* Other ctrl char */
1831 nbytes = strlen (pw->typed_passwd);
1832 nchars = strlen (pw->typed_passwd_char_size);
1833 if (nchars + 1 >= sizeof (pw->typed_passwd_char_size)-1 ||
1834 nbytes + decoded_size >= sizeof (pw->typed_passwd)-1) /* overflow */
1838 pw->typed_passwd_char_size[nchars] = decoded_size;
1839 pw->typed_passwd_char_size[nchars+1] = 0;
1840 memcpy (pw->typed_passwd + nbytes, decoded, decoded_size);
1841 pw->typed_passwd[nbytes + decoded_size] = 0;
1847 /* If the input is wider than the text box, only show the last portion,
1848 to simulate a horizontally-scrolling text field. */
1849 int chars_in_pwfield = (pw->passwd_field_width /
1850 pw->passwd_font->max_bounds.width);
1851 const char *output = pw->typed_passwd;
1852 if (strlen(output) > chars_in_pwfield)
1853 output += (strlen(output) - chars_in_pwfield);
1854 update_passwd_window (si, output, pw->ratio);
1856 else if (pw->show_stars_p)
1858 int nchars = strlen (pw->typed_passwd_char_size);
1860 stars = (char *) malloc(nchars + 1);
1861 memset (stars, '*', nchars);
1863 update_passwd_window (si, stars, pw->ratio);
1868 update_passwd_window (si, "", pw->ratio);
1874 passwd_event_loop (saver_info *si)
1876 saver_preferences *p = &si->prefs;
1879 /* We have to go through this union bullshit because gcc-4.4.0 has
1880 stricter struct-aliasing rules. Without this, the optimizer
1886 XRRScreenChangeNotifyEvent xrr_event;
1887 # endif /* HAVE_RANDR */
1890 passwd_animate_timer ((XtPointer) si, 0);
1891 reset_watchdog_timer (si, False); /* Disable watchdog while dialog up */
1893 while (si->unlock_state == ul_read)
1895 XtAppNextEvent (si->app, &event.x_event);
1898 if (si->using_randr_extension &&
1899 (event.x_event.type ==
1900 (si->randr_event_number + RRScreenChangeNotify)))
1902 /* The Resize and Rotate extension sends an event when the
1903 size, rotation, or refresh rate of any screen has changed. */
1907 /* XRRRootToScreen is in Xrandr.h 1.4, 2001/06/07 */
1908 int screen = XRRRootToScreen(si->dpy, event.xrr_event.window);
1909 fprintf (stderr, "%s: %d: screen change event received\n",
1913 #ifdef RRScreenChangeNotifyMask
1914 /* Inform Xlib that it's ok to update its data structures. */
1915 XRRUpdateConfiguration(&event.x_event); /* Xrandr.h 1.9, 2002/09/29*/
1916 #endif /* RRScreenChangeNotifyMask */
1918 /* Resize the existing xscreensaver windows and cached ssi data. */
1919 if (update_screen_layout (si))
1923 fprintf (stderr, "%s: new layout:\n", blurb());
1924 describe_monitor_layout (si);
1926 resize_screensaver_window (si);
1930 #endif /* HAVE_RANDR */
1932 if (event.x_event.xany.window == si->passwd_dialog &&
1933 event.x_event.xany.type == Expose)
1934 draw_passwd_window (si);
1935 else if (event.x_event.xany.type == KeyPress)
1937 handle_passwd_key (si, &event.x_event.xkey);
1938 si->pw_data->caps_p = (event.x_event.xkey.state & LockMask);
1940 else if (event.x_event.xany.type == ButtonPress ||
1941 event.x_event.xany.type == ButtonRelease)
1943 si->pw_data->button_state_changed_p = True;
1944 handle_unlock_button (si, &event.x_event);
1945 if (si->pw_data->login_button_p)
1946 handle_login_button (si, &event.x_event);
1949 XtDispatchEvent (&event.x_event);
1952 switch (si->unlock_state)
1954 case ul_cancel: msg = ""; break;
1955 case ul_time: msg = "Timed out!"; break;
1956 case ul_finished: msg = "Checking..."; break;
1957 default: msg = 0; break;
1961 switch (si->unlock_state) {
1963 fprintf (stderr, "%s: input cancelled.\n", blurb()); break;
1965 fprintf (stderr, "%s: input timed out.\n", blurb()); break;
1967 fprintf (stderr, "%s: input finished.\n", blurb()); break;
1973 si->pw_data->i_beam = 0;
1974 update_passwd_window (si, msg, 0.0);
1975 XSync (si->dpy, False);
1977 /* Swallow all pending KeyPress/KeyRelease events. */
1980 while (XCheckMaskEvent (si->dpy, KeyPressMask|KeyReleaseMask, &e))
1985 reset_watchdog_timer (si, True); /* Re-enable watchdog */
1990 handle_typeahead (saver_info *si)
1992 passwd_dialog_data *pw = si->pw_data;
1994 if (!si->unlock_typeahead)
1997 pw->passwd_changed_p = True;
1999 i = strlen (si->unlock_typeahead);
2000 if (i >= sizeof(pw->typed_passwd) - 1)
2001 i = sizeof(pw->typed_passwd) - 1;
2003 memcpy (pw->typed_passwd, si->unlock_typeahead, i);
2004 pw->typed_passwd [i] = 0;
2007 char *c = pw->typed_passwd_char_size;
2008 for (j = 0; j < i; j++)
2013 memset (si->unlock_typeahead, '*', strlen(si->unlock_typeahead));
2014 si->unlock_typeahead[i] = 0;
2015 update_passwd_window (si, si->unlock_typeahead, pw->ratio);
2017 free (si->unlock_typeahead);
2018 si->unlock_typeahead = 0;
2023 * Returns a copy of the input string with trailing whitespace removed.
2024 * Whitespace is anything considered so by isspace().
2025 * It is safe to call this with NULL, in which case NULL will be returned.
2026 * The returned string (if not NULL) should be freed by the caller with free().
2029 remove_trailing_whitespace(const char *str)
2039 newstr = malloc(len + 1);
2043 (void) strcpy(newstr, str);
2045 while (isspace(*--chr) && chr >= newstr)
2053 * The authentication conversation function.
2054 * Like a PAM conversation function, this accepts multiple messages in a single
2055 * round. It then splits them into individual messages for display on the
2056 * passwd dialog. A message sequence of info or error followed by a prompt will
2057 * be reduced into a single dialog window.
2059 * Returns 0 on success or -1 if some problem occurred (cancelled, OOM, etc.)
2062 gui_auth_conv(int num_msg,
2063 const struct auth_message auth_msgs[],
2064 struct auth_response **resp,
2068 const char *info_msg, *prompt;
2069 struct auth_response *responses;
2071 if (si->unlock_state == ul_cancel ||
2072 si->unlock_state == ul_time)
2073 /* If we've already cancelled or timed out in this PAM conversation,
2074 don't prompt again even if PAM asks us to! */
2077 if (!(responses = calloc(num_msg, sizeof(struct auth_response))))
2080 for (i = 0; i < num_msg; ++i)
2082 info_msg = prompt = NULL;
2084 /* See if there is a following message that can be shown at the same
2086 if (auth_msgs[i].type == AUTH_MSGTYPE_INFO
2088 && ( auth_msgs[i+1].type == AUTH_MSGTYPE_PROMPT_NOECHO
2089 || auth_msgs[i+1].type == AUTH_MSGTYPE_PROMPT_ECHO)
2092 info_msg = auth_msgs[i].msg;
2093 prompt = auth_msgs[++i].msg;
2097 if ( auth_msgs[i].type == AUTH_MSGTYPE_INFO
2098 || auth_msgs[i].type == AUTH_MSGTYPE_ERROR)
2099 info_msg = auth_msgs[i].msg;
2101 prompt = auth_msgs[i].msg;
2105 char *info_msg_trimmed, *prompt_trimmed;
2107 /* Trailing whitespace looks bad in a GUI */
2108 info_msg_trimmed = remove_trailing_whitespace(info_msg);
2109 prompt_trimmed = remove_trailing_whitespace(prompt);
2111 if (make_passwd_window(si, info_msg_trimmed, prompt_trimmed,
2112 auth_msgs[i].type == AUTH_MSGTYPE_PROMPT_ECHO
2117 if (info_msg_trimmed)
2118 free(info_msg_trimmed);
2121 free(prompt_trimmed);
2124 compose_status = calloc (1, sizeof (*compose_status));
2125 if (!compose_status)
2128 si->unlock_state = ul_read;
2130 handle_typeahead (si);
2131 passwd_event_loop (si);
2133 if (si->unlock_state == ul_cancel)
2136 responses[i].response = strdup(si->pw_data->typed_passwd);
2138 /* Cache the first response to a PROMPT_NOECHO to save prompting for
2139 * each auth mechanism. */
2140 if (si->cached_passwd == NULL &&
2141 auth_msgs[i].type == AUTH_MSGTYPE_PROMPT_NOECHO)
2142 si->cached_passwd = strdup(responses[i].response);
2144 free (compose_status);
2150 return (si->unlock_state == ul_finished) ? 0 : -1;
2154 free (compose_status);
2159 for (i = 0; i < num_msg; ++i)
2160 if (responses[i].response)
2161 free (responses[i].response);
2170 auth_finished_cb (saver_info *si)
2175 /* If we have something to say, put the dialog back up for a few seconds
2176 to display it. Otherwise, don't bother.
2179 if (si->unlock_state == ul_fail && /* failed with caps lock on */
2180 si->pw_data && si->pw_data->caps_p)
2181 s = "Authentication failed (Caps Lock?)";
2182 else if (si->unlock_state == ul_fail) /* failed without caps lock */
2183 s = "Authentication failed!";
2184 else if (si->unlock_state == ul_success && /* good, but report failures */
2185 si->unlock_failures > 0)
2187 if (si->unlock_failures == 1)
2188 s = "There has been\n1 failed login attempt.";
2191 sprintf (buf, "There have been\n%d failed login attempts.",
2192 si->unlock_failures);
2195 si->unlock_failures = 0;
2197 /* ignore failures if they all were too recent */
2198 if (time((time_t *) 0) - si->unlock_failure_time
2199 < si->prefs.auth_warning_slack)
2202 else /* good, with no failures, */
2203 goto END; /* or timeout, or cancel. */
2205 make_passwd_window (si, s, NULL, True);
2206 XSync (si->dpy, False);
2210 time_t start = time ((time_t *) 0);
2212 while (time ((time_t *) 0) < start + secs)
2213 if (XPending (si->dpy))
2215 XNextEvent (si->dpy, &event);
2216 if (event.xany.window == si->passwd_dialog &&
2217 event.xany.type == Expose)
2218 draw_passwd_window (si);
2219 else if (event.xany.type == ButtonPress ||
2220 event.xany.type == KeyPress)
2222 XSync (si->dpy, False);
2225 usleep (250000); /* 1/4 second */
2230 destroy_passwd_window (si);
2235 unlock_p (saver_info *si)
2237 saver_preferences *p = &si->prefs;
2241 fprintf(stderr, "%s: Error: no unlock function specified!\n", blurb());
2245 raise_window (si, True, True, True);
2247 xss_authenticate(si, p->verbose_p);
2249 return (si->unlock_state == ul_success);
2254 set_locked_p (saver_info *si, Bool locked_p)
2256 si->locked_p = locked_p;
2258 #ifdef HAVE_XHPDISABLERESET
2259 hp_lock_reset (si, locked_p); /* turn off/on C-Sh-Reset */
2261 #ifdef HAVE_XF86VMODE
2262 xfree_lock_mode_switch (si, locked_p); /* turn off/on C-Alt-Plus */
2264 #ifdef HAVE_XF86MISCSETGRABKEYSSTATE
2265 xfree_lock_grab_smasher (si, locked_p); /* turn off/on C-Alt-KP-*,/ */
2268 store_saver_status (si); /* store locked-p */
2272 #else /* NO_LOCKING -- whole file */
2275 set_locked_p (saver_info *si, Bool locked_p)
2277 if (locked_p) abort();
2280 #endif /* !NO_LOCKING */