1 /* lock.c --- handling the password dialog for locking-mode.
2 * xscreensaver, Copyright (c) 1993-2013 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 static Bool error_handler_hit_p = False;
1428 ignore_all_errors_ehandler (Display *dpy, XErrorEvent *error)
1430 error_handler_hit_p = True;
1435 #ifdef HAVE_XHPDISABLERESET
1436 /* This function enables and disables the C-Sh-Reset hot-key, which
1437 normally resets the X server (logging out the logged-in user.)
1438 We don't want random people to be able to do that while the
1442 hp_lock_reset (saver_info *si, Bool lock_p)
1444 static Bool hp_locked_p = False;
1446 /* Calls to XHPDisableReset and XHPEnableReset must be balanced,
1447 or BadAccess errors occur. (It's ok for this to be global,
1448 since it affects the whole machine, not just the current screen.)
1450 if (hp_locked_p == lock_p)
1454 XHPDisableReset (si->dpy);
1456 XHPEnableReset (si->dpy);
1457 hp_locked_p = lock_p;
1459 #endif /* HAVE_XHPDISABLERESET */
1462 #ifdef HAVE_XF86MISCSETGRABKEYSSTATE
1464 /* This function enables and disables the Ctrl-Alt-KP_star and
1465 Ctrl-Alt-KP_slash hot-keys, which (in XFree86 4.2) break any
1466 grabs and/or kill the grabbing client. That would effectively
1467 unlock the screen, so we don't like that.
1469 The Ctrl-Alt-KP_star and Ctrl-Alt-KP_slash hot-keys only exist
1470 if AllowDeactivateGrabs and/or AllowClosedownGrabs are turned on
1471 in XF86Config. I believe they are disabled by default.
1473 This does not affect any other keys (specifically Ctrl-Alt-BS or
1474 Ctrl-Alt-F1) but I wish it did. Maybe it will someday.
1477 xfree_lock_grab_smasher (saver_info *si, Bool lock_p)
1479 saver_preferences *p = &si->prefs;
1482 XErrorHandler old_handler;
1484 if (!XF86MiscQueryExtension(si->dpy, &event, &error))
1487 XSync (si->dpy, False);
1488 error_handler_hit_p = False;
1489 old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1490 XSync (si->dpy, False);
1491 status = XF86MiscSetGrabKeysState (si->dpy, !lock_p);
1492 XSync (si->dpy, False);
1493 if (error_handler_hit_p) status = 666;
1495 if (!lock_p && status == MiscExtGrabStateAlready)
1496 status = MiscExtGrabStateSuccess; /* shut up, consider this success */
1498 if (p->verbose_p && status != MiscExtGrabStateSuccess)
1499 fprintf (stderr, "%s: error: XF86MiscSetGrabKeysState(%d) returned %s\n",
1501 (status == MiscExtGrabStateSuccess ? "MiscExtGrabStateSuccess" :
1502 status == MiscExtGrabStateLocked ? "MiscExtGrabStateLocked" :
1503 status == MiscExtGrabStateAlready ? "MiscExtGrabStateAlready" :
1504 status == 666 ? "an X error" :
1507 XSync (si->dpy, False);
1508 XSetErrorHandler (old_handler);
1509 XSync (si->dpy, False);
1511 #endif /* HAVE_XF86MISCSETGRABKEYSSTATE */
1515 /* This function enables and disables the C-Alt-Plus and C-Alt-Minus
1516 hot-keys, which normally change the resolution of the X server.
1517 We don't want people to be able to switch the server resolution
1518 while the screen is locked, because if they switch to a higher
1519 resolution, it could cause part of the underlying desktop to become
1522 #ifdef HAVE_XF86VMODE
1525 xfree_lock_mode_switch (saver_info *si, Bool lock_p)
1527 static Bool any_mode_locked_p = False;
1528 saver_preferences *p = &si->prefs;
1530 int real_nscreens = ScreenCount (si->dpy);
1533 XErrorHandler old_handler;
1535 if (any_mode_locked_p == lock_p)
1537 if (!XF86VidModeQueryExtension (si->dpy, &event, &error))
1540 for (screen = 0; screen < real_nscreens; screen++)
1542 XSync (si->dpy, False);
1543 old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1544 error_handler_hit_p = False;
1545 status = XF86VidModeLockModeSwitch (si->dpy, screen, lock_p);
1546 XSync (si->dpy, False);
1547 XSetErrorHandler (old_handler);
1548 if (error_handler_hit_p) status = False;
1551 any_mode_locked_p = lock_p;
1553 if (!status && (p->verbose_p || !lock_p))
1554 /* Only print this when verbose, or when we locked but can't unlock.
1555 I tried printing this message whenever it comes up, but
1556 mode-locking always fails if DontZoom is set in XF86Config. */
1557 fprintf (stderr, "%s: %d: unable to %s mode switching!\n",
1558 blurb(), screen, (lock_p ? "lock" : "unlock"));
1559 else if (p->verbose_p)
1560 fprintf (stderr, "%s: %d: %s mode switching.\n",
1561 blurb(), screen, (lock_p ? "locked" : "unlocked"));
1564 #endif /* HAVE_XF86VMODE */
1567 /* If the viewport has been scrolled since the screen was blanked,
1568 then scroll it back to where it belongs. This function only exists
1569 to patch over a very brief race condition.
1572 undo_vp_motion (saver_info *si)
1574 #ifdef HAVE_XF86VMODE
1575 saver_preferences *p = &si->prefs;
1577 int real_nscreens = ScreenCount (si->dpy);
1580 if (!XF86VidModeQueryExtension (si->dpy, &event, &error))
1583 for (screen = 0; screen < real_nscreens; screen++)
1585 saver_screen_info *ssi = &si->screens[screen];
1589 if (ssi->blank_vp_x == -1 && ssi->blank_vp_y == -1)
1591 if (!XF86VidModeGetViewPort (si->dpy, screen, &x, &y))
1593 if (ssi->blank_vp_x == x && ssi->blank_vp_y == y)
1596 /* We're going to move the viewport. The mouse has just been grabbed on
1597 (and constrained to, thus warped to) the password window, so it is no
1598 longer near the edge of the screen. However, wait a bit anyway, just
1599 to make sure the server drains its last motion event, so that the
1600 screen doesn't continue to scroll after we've reset the viewport.
1602 XSync (si->dpy, False);
1603 usleep (250000); /* 1/4 second */
1604 XSync (si->dpy, False);
1606 status = XF86VidModeSetViewPort (si->dpy, screen,
1607 ssi->blank_vp_x, ssi->blank_vp_y);
1611 "%s: %d: unable to move vp from (%d,%d) back to (%d,%d)!\n",
1612 blurb(), screen, x, y, ssi->blank_vp_x, ssi->blank_vp_y);
1613 else if (p->verbose_p)
1615 "%s: %d: vp moved to (%d,%d); moved it back to (%d,%d).\n",
1616 blurb(), screen, x, y, ssi->blank_vp_x, ssi->blank_vp_y);
1618 #endif /* HAVE_XF86VMODE */
1627 passwd_animate_timer (XtPointer closure, XtIntervalId *id)
1629 saver_info *si = (saver_info *) closure;
1631 passwd_dialog_data *pw = si->pw_data;
1635 pw->ratio -= (1.0 / ((double) si->prefs.passwd_timeout / (double) tick));
1639 if (si->unlock_state == ul_read)
1640 si->unlock_state = ul_time;
1643 update_passwd_window (si, 0, pw->ratio);
1645 if (si->unlock_state == ul_read)
1646 pw->timer = XtAppAddTimeOut (si->app, tick, passwd_animate_timer,
1651 idle_timer ((XtPointer) si, 0);
1655 static XComposeStatus *compose_status;
1658 handle_login_button (saver_info *si, XEvent *event)
1660 saver_preferences *p = &si->prefs;
1661 Bool mouse_in_box = False;
1663 passwd_dialog_data *pw = si->pw_data;
1664 saver_screen_info *ssi = pw->prompt_screen;
1666 if (! pw->login_button_enabled_p)
1670 (event->xbutton.x >= pw->login_button_x &&
1671 event->xbutton.x <= pw->login_button_x + pw->login_button_width &&
1672 event->xbutton.y >= pw->login_button_y &&
1673 event->xbutton.y <= pw->login_button_y + pw->login_button_height);
1675 if (ButtonRelease == event->xany.type &&
1676 pw->login_button_down_p &&
1679 /* Only allow them to press the button once: don't want to
1680 accidentally launch a dozen gdm choosers if the machine
1684 pw->login_button_enabled_p = False;
1687 pw->login_button_down_p = (mouse_in_box &&
1688 ButtonRelease != event->xany.type);
1690 update_passwd_window (si, 0, pw->ratio);
1693 fork_and_exec (ssi, p->new_login_command);
1698 handle_unlock_button (saver_info *si, XEvent *event)
1700 Bool mouse_in_box = False;
1701 passwd_dialog_data *pw = si->pw_data;
1704 (event->xbutton.x >= pw->unlock_button_x &&
1705 event->xbutton.x <= pw->unlock_button_x + pw->unlock_button_width &&
1706 event->xbutton.y >= pw->unlock_button_y &&
1707 event->xbutton.y <= pw->unlock_button_y + pw->unlock_button_height);
1709 if (ButtonRelease == event->xany.type &&
1710 pw->unlock_button_down_p &&
1712 finished_typing_passwd (si, pw);
1714 pw->unlock_button_down_p = (mouse_in_box &&
1715 ButtonRelease != event->xany.type);
1720 finished_typing_passwd (saver_info *si, passwd_dialog_data *pw)
1722 if (si->unlock_state == ul_read)
1724 update_passwd_window (si, "Checking...", pw->ratio);
1725 XSync (si->dpy, False);
1727 si->unlock_state = ul_finished;
1728 update_passwd_window (si, "", pw->ratio);
1733 handle_passwd_key (saver_info *si, XKeyEvent *event)
1735 passwd_dialog_data *pw = si->pw_data;
1736 unsigned char decoded [MAX_BYTES_PER_CHAR * 10]; /* leave some slack */
1739 /* XLookupString may return more than one character via XRebindKeysym;
1740 and on some systems it returns multi-byte UTF-8 characters (contrary
1741 to its documentation, which says it returns only Latin1.)
1743 It seems to only do so, however, if setlocale() has been called.
1744 See the code inside ENABLE_NLS in xscreensaver.c.
1746 int decoded_size = XLookupString (event, (char *)decoded, sizeof(decoded),
1747 &keysym, compose_status);
1751 const char *ks = XKeysymToString (keysym);
1753 fprintf(stderr, "## %-12s\t=> %d\t", (ks ? ks : "(null)"), decoded_size);
1754 for (i = 0; i < decoded_size; i++)
1755 fprintf(stderr, "%c", decoded[i]);
1756 fprintf(stderr, "\t");
1757 for (i = 0; i < decoded_size; i++)
1758 fprintf(stderr, "\\%03o", ((unsigned char *)decoded)[i]);
1759 fprintf(stderr, "\n");
1763 if (decoded_size > MAX_BYTES_PER_CHAR)
1765 /* The multi-byte character returned is too large. */
1770 decoded[decoded_size] = 0;
1771 pw->passwd_changed_p = True;
1773 /* Add 10% to the time remaining every time a key is pressed. */
1775 if (pw->ratio > 1) pw->ratio = 1;
1777 if (decoded_size == 1) /* Handle single-char commands */
1781 case '\010': case '\177': /* Backspace */
1783 /* kludgey way to get the number of "logical" characters. */
1784 int nchars = strlen (pw->typed_passwd_char_size);
1785 int nbytes = strlen (pw->typed_passwd);
1791 for (i = pw->typed_passwd_char_size[nchars-1]; i >= 0; i--)
1793 if (nbytes < 0) abort();
1794 pw->typed_passwd[nbytes--] = 0;
1796 pw->typed_passwd_char_size[nchars-1] = 0;
1801 case '\012': case '\015': /* Enter */
1802 finished_typing_passwd (si, pw);
1805 case '\033': /* Escape */
1806 si->unlock_state = ul_cancel;
1809 case '\025': case '\030': /* Erase line */
1810 memset (pw->typed_passwd, 0, sizeof (pw->typed_passwd));
1811 memset (pw->typed_passwd_char_size, 0,
1812 sizeof (pw->typed_passwd_char_size));
1816 if (*decoded < ' ' && *decoded != '\t') /* Other ctrl char */
1827 nbytes = strlen (pw->typed_passwd);
1828 nchars = strlen (pw->typed_passwd_char_size);
1829 if (nchars + 1 >= sizeof (pw->typed_passwd_char_size)-1 ||
1830 nbytes + decoded_size >= sizeof (pw->typed_passwd)-1) /* overflow */
1834 pw->typed_passwd_char_size[nchars] = decoded_size;
1835 pw->typed_passwd_char_size[nchars+1] = 0;
1836 memcpy (pw->typed_passwd + nbytes, decoded, decoded_size);
1837 pw->typed_passwd[nbytes + decoded_size] = 0;
1843 /* If the input is wider than the text box, only show the last portion,
1844 to simulate a horizontally-scrolling text field. */
1845 int chars_in_pwfield = (pw->passwd_field_width /
1846 pw->passwd_font->max_bounds.width);
1847 const char *output = pw->typed_passwd;
1848 if (strlen(output) > chars_in_pwfield)
1849 output += (strlen(output) - chars_in_pwfield);
1850 update_passwd_window (si, output, pw->ratio);
1852 else if (pw->show_stars_p)
1854 int nchars = strlen (pw->typed_passwd_char_size);
1856 stars = (char *) malloc(nchars + 1);
1857 memset (stars, '*', nchars);
1859 update_passwd_window (si, stars, pw->ratio);
1864 update_passwd_window (si, "", pw->ratio);
1870 passwd_event_loop (saver_info *si)
1872 saver_preferences *p = &si->prefs;
1875 /* We have to go through this union bullshit because gcc-4.4.0 has
1876 stricter struct-aliasing rules. Without this, the optimizer
1882 XRRScreenChangeNotifyEvent xrr_event;
1883 # endif /* HAVE_RANDR */
1886 passwd_animate_timer ((XtPointer) si, 0);
1888 while (si->unlock_state == ul_read)
1890 XtAppNextEvent (si->app, &event.x_event);
1893 if (si->using_randr_extension &&
1894 (event.x_event.type ==
1895 (si->randr_event_number + RRScreenChangeNotify)))
1897 /* The Resize and Rotate extension sends an event when the
1898 size, rotation, or refresh rate of any screen has changed. */
1902 /* XRRRootToScreen is in Xrandr.h 1.4, 2001/06/07 */
1903 int screen = XRRRootToScreen(si->dpy, event.xrr_event.window);
1904 fprintf (stderr, "%s: %d: screen change event received\n",
1908 #ifdef RRScreenChangeNotifyMask
1909 /* Inform Xlib that it's ok to update its data structures. */
1910 XRRUpdateConfiguration(&event.x_event); /* Xrandr.h 1.9, 2002/09/29*/
1911 #endif /* RRScreenChangeNotifyMask */
1913 /* Resize the existing xscreensaver windows and cached ssi data. */
1914 if (update_screen_layout (si))
1918 fprintf (stderr, "%s: new layout:\n", blurb());
1919 describe_monitor_layout (si);
1921 resize_screensaver_window (si);
1925 #endif /* HAVE_RANDR */
1927 if (event.x_event.xany.window == si->passwd_dialog &&
1928 event.x_event.xany.type == Expose)
1929 draw_passwd_window (si);
1930 else if (event.x_event.xany.type == KeyPress)
1932 handle_passwd_key (si, &event.x_event.xkey);
1933 si->pw_data->caps_p = (event.x_event.xkey.state & LockMask);
1935 else if (event.x_event.xany.type == ButtonPress ||
1936 event.x_event.xany.type == ButtonRelease)
1938 si->pw_data->button_state_changed_p = True;
1939 handle_unlock_button (si, &event.x_event);
1940 if (si->pw_data->login_button_p)
1941 handle_login_button (si, &event.x_event);
1944 XtDispatchEvent (&event.x_event);
1947 switch (si->unlock_state)
1949 case ul_cancel: msg = ""; break;
1950 case ul_time: msg = "Timed out!"; break;
1951 case ul_finished: msg = "Checking..."; break;
1952 default: msg = 0; break;
1956 switch (si->unlock_state) {
1958 fprintf (stderr, "%s: input cancelled.\n", blurb()); break;
1960 fprintf (stderr, "%s: input timed out.\n", blurb()); break;
1962 fprintf (stderr, "%s: input finished.\n", blurb()); break;
1968 si->pw_data->i_beam = 0;
1969 update_passwd_window (si, msg, 0.0);
1970 XSync (si->dpy, False);
1972 /* Swallow all pending KeyPress/KeyRelease events. */
1975 while (XCheckMaskEvent (si->dpy, KeyPressMask|KeyReleaseMask, &e))
1983 handle_typeahead (saver_info *si)
1985 passwd_dialog_data *pw = si->pw_data;
1987 if (!si->unlock_typeahead)
1990 pw->passwd_changed_p = True;
1992 i = strlen (si->unlock_typeahead);
1993 if (i >= sizeof(pw->typed_passwd) - 1)
1994 i = sizeof(pw->typed_passwd) - 1;
1996 memcpy (pw->typed_passwd, si->unlock_typeahead, i);
1997 pw->typed_passwd [i] = 0;
2000 char *c = pw->typed_passwd_char_size;
2001 for (j = 0; j < i; j++)
2006 memset (si->unlock_typeahead, '*', strlen(si->unlock_typeahead));
2007 si->unlock_typeahead[i] = 0;
2008 update_passwd_window (si, si->unlock_typeahead, pw->ratio);
2010 free (si->unlock_typeahead);
2011 si->unlock_typeahead = 0;
2016 * Returns a copy of the input string with trailing whitespace removed.
2017 * Whitespace is anything considered so by isspace().
2018 * It is safe to call this with NULL, in which case NULL will be returned.
2019 * The returned string (if not NULL) should be freed by the caller with free().
2022 remove_trailing_whitespace(const char *str)
2032 newstr = malloc(len + 1);
2036 (void) strcpy(newstr, str);
2038 while (isspace(*--chr) && chr >= newstr)
2046 * The authentication conversation function.
2047 * Like a PAM conversation function, this accepts multiple messages in a single
2048 * round. It then splits them into individual messages for display on the
2049 * passwd dialog. A message sequence of info or error followed by a prompt will
2050 * be reduced into a single dialog window.
2052 * Returns 0 on success or -1 if some problem occurred (cancelled, OOM, etc.)
2055 gui_auth_conv(int num_msg,
2056 const struct auth_message auth_msgs[],
2057 struct auth_response **resp,
2061 const char *info_msg, *prompt;
2062 struct auth_response *responses;
2064 if (si->unlock_state == ul_cancel ||
2065 si->unlock_state == ul_time)
2066 /* If we've already cancelled or timed out in this PAM conversation,
2067 don't prompt again even if PAM asks us to! */
2070 if (!(responses = calloc(num_msg, sizeof(struct auth_response))))
2073 for (i = 0; i < num_msg; ++i)
2075 info_msg = prompt = NULL;
2077 /* See if there is a following message that can be shown at the same
2079 if (auth_msgs[i].type == AUTH_MSGTYPE_INFO
2081 && ( auth_msgs[i+1].type == AUTH_MSGTYPE_PROMPT_NOECHO
2082 || auth_msgs[i+1].type == AUTH_MSGTYPE_PROMPT_ECHO)
2085 info_msg = auth_msgs[i].msg;
2086 prompt = auth_msgs[++i].msg;
2090 if ( auth_msgs[i].type == AUTH_MSGTYPE_INFO
2091 || auth_msgs[i].type == AUTH_MSGTYPE_ERROR)
2092 info_msg = auth_msgs[i].msg;
2094 prompt = auth_msgs[i].msg;
2098 char *info_msg_trimmed, *prompt_trimmed;
2100 /* Trailing whitespace looks bad in a GUI */
2101 info_msg_trimmed = remove_trailing_whitespace(info_msg);
2102 prompt_trimmed = remove_trailing_whitespace(prompt);
2104 if (make_passwd_window(si, info_msg_trimmed, prompt_trimmed,
2105 auth_msgs[i].type == AUTH_MSGTYPE_PROMPT_ECHO
2110 if (info_msg_trimmed)
2111 free(info_msg_trimmed);
2114 free(prompt_trimmed);
2117 compose_status = calloc (1, sizeof (*compose_status));
2118 if (!compose_status)
2121 si->unlock_state = ul_read;
2123 handle_typeahead (si);
2124 passwd_event_loop (si);
2126 if (si->unlock_state == ul_cancel)
2129 responses[i].response = strdup(si->pw_data->typed_passwd);
2131 /* Cache the first response to a PROMPT_NOECHO to save prompting for
2132 * each auth mechanism. */
2133 if (si->cached_passwd == NULL &&
2134 auth_msgs[i].type == AUTH_MSGTYPE_PROMPT_NOECHO)
2135 si->cached_passwd = strdup(responses[i].response);
2137 free (compose_status);
2143 return (si->unlock_state == ul_finished) ? 0 : -1;
2147 free (compose_status);
2152 for (i = 0; i < num_msg; ++i)
2153 if (responses[i].response)
2154 free (responses[i].response);
2163 auth_finished_cb (saver_info *si)
2168 /* If we have something to say, put the dialog back up for a few seconds
2169 to display it. Otherwise, don't bother.
2172 if (si->unlock_state == ul_fail && /* failed with caps lock on */
2173 si->pw_data && si->pw_data->caps_p)
2174 s = "Authentication failed (Caps Lock?)";
2175 else if (si->unlock_state == ul_fail) /* failed without caps lock */
2176 s = "Authentication failed!";
2177 else if (si->unlock_state == ul_success && /* good, but report failures */
2178 si->unlock_failures > 0)
2180 if (si->unlock_failures == 1)
2181 s = "There has been\n1 failed login attempt.";
2184 sprintf (buf, "There have been\n%d failed login attempts.",
2185 si->unlock_failures);
2188 si->unlock_failures = 0;
2190 else /* good, with no failures, */
2191 goto END; /* or timeout, or cancel. */
2193 make_passwd_window (si, s, NULL, True);
2194 XSync (si->dpy, False);
2198 time_t start = time ((time_t *) 0);
2200 while (time ((time_t *) 0) < start + secs)
2201 if (XPending (si->dpy))
2203 XNextEvent (si->dpy, &event);
2204 if (event.xany.window == si->passwd_dialog &&
2205 event.xany.type == Expose)
2206 draw_passwd_window (si);
2207 else if (event.xany.type == ButtonPress ||
2208 event.xany.type == KeyPress)
2210 XSync (si->dpy, False);
2213 usleep (250000); /* 1/4 second */
2218 destroy_passwd_window (si);
2223 unlock_p (saver_info *si)
2225 saver_preferences *p = &si->prefs;
2229 fprintf(stderr, "%s: Error: no unlock function specified!\n", blurb());
2233 raise_window (si, True, True, True);
2235 xss_authenticate(si, p->verbose_p);
2237 return (si->unlock_state == ul_success);
2242 set_locked_p (saver_info *si, Bool locked_p)
2244 si->locked_p = locked_p;
2246 #ifdef HAVE_XHPDISABLERESET
2247 hp_lock_reset (si, locked_p); /* turn off/on C-Sh-Reset */
2249 #ifdef HAVE_XF86VMODE
2250 xfree_lock_mode_switch (si, locked_p); /* turn off/on C-Alt-Plus */
2252 #ifdef HAVE_XF86MISCSETGRABKEYSSTATE
2253 xfree_lock_grab_smasher (si, locked_p); /* turn off/on C-Alt-KP-*,/ */
2256 store_saver_status (si); /* store locked-p */
2260 #else /* NO_LOCKING -- whole file */
2263 set_locked_p (saver_info *si, Bool locked_p)
2265 if (locked_p) abort();
2268 #endif /* !NO_LOCKING */