1 /* lock.c --- handling the password dialog for locking-mode.
2 * xscreensaver, Copyright (c) 1993-2017 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;
203 saver_screen_info *ssi = &si->screens [mouse_screen (si)];
205 pw = (passwd_dialog_data *) calloc (1, sizeof(*pw));
209 /* Display the button only if the "newLoginCommand" pref is non-null.
211 pw->login_button_p = (si->prefs.new_login_command &&
212 *si->prefs.new_login_command);
214 pw->passwd_cursor = XCreateFontCursor (si->dpy, XC_top_left_arrow);
216 pw->prompt_screen = ssi;
218 screen = pw->prompt_screen->screen;
219 cmap = DefaultColormapOfScreen (screen);
221 pw->show_stars_p = get_boolean_resource(si->dpy, "passwd.asterisks",
224 pw->heading_label = get_string_resource (si->dpy, "passwd.heading.label",
225 "Dialog.Label.Label");
226 pw->body_label = get_string_resource (si->dpy, "passwd.body.label",
227 "Dialog.Label.Label");
228 pw->user_label = get_string_resource (si->dpy, "passwd.user.label",
229 "Dialog.Label.Label");
230 pw->unlock_label = get_string_resource (si->dpy, "passwd.unlock.label",
231 "Dialog.Button.Label");
232 pw->login_label = get_string_resource (si->dpy, "passwd.login.label",
233 "Dialog.Button.Label");
235 pw->date_label = get_string_resource (si->dpy, "dateFormat", "DateFormat");
237 if (!pw->heading_label)
238 pw->heading_label = strdup("ERROR: RESOURCES NOT INSTALLED CORRECTLY");
240 pw->body_label = strdup("ERROR: RESOURCES NOT INSTALLED CORRECTLY");
241 if (!pw->user_label) pw->user_label = strdup("ERROR");
242 if (!pw->date_label) pw->date_label = strdup("ERROR");
243 if (!pw->unlock_label) pw->unlock_label = strdup("ERROR (UNLOCK)");
244 if (!pw->login_label) pw->login_label = strdup ("ERROR (LOGIN)") ;
246 /* Put the version number in the label. */
248 char *s = (char *) malloc (strlen(pw->heading_label) + 20);
249 sprintf(s, pw->heading_label, si->version);
250 free (pw->heading_label);
251 pw->heading_label = s;
254 /* Get hostname info */
255 pw->uname_label = strdup(""); /* Initialy, write nothing */
261 if (uname (&uts) == 0)
263 #if 0 /* Get the full hostname */
266 if ((s = strchr(uts.nodename, '.')))
270 char *s = strdup (uts.nodename);
271 free (pw->uname_label);
277 pw->passwd_string = strdup("");
280 splash_load_font (si->dpy, "passwd.headingFont", "Dialog.Font");
282 splash_load_font (si->dpy, "passwd.buttonFont", "Dialog.Font");
284 splash_load_font (si->dpy, "passwd.bodyFont", "Dialog.Font");
286 splash_load_font (si->dpy, "passwd.labelFont", "Dialog.Font");
288 splash_load_font (si->dpy, "passwd.passwdFont", "Dialog.Font");
290 splash_load_font (si->dpy, "passwd.dateFont", "Dialog.Font");
292 splash_load_font (si->dpy, "passwd.unameFont", "Dialog.Font");
294 pw->show_uname_p = get_boolean_resource(si->dpy, "passwd.uname", "Boolean");
296 pw->foreground = get_pixel_resource (si->dpy, cmap,
298 "Dialog.Foreground" );
299 pw->background = get_pixel_resource (si->dpy, cmap,
301 "Dialog.Background" );
302 pw->border = get_pixel_resource (si->dpy, cmap,
303 "passwd.borderColor",
304 "Dialog.borderColor");
306 if (pw->foreground == pw->background)
308 /* Make sure the error messages show up. */
309 pw->foreground = BlackPixelOfScreen (screen);
310 pw->background = WhitePixelOfScreen (screen);
313 pw->passwd_foreground = get_pixel_resource (si->dpy, cmap,
314 "passwd.text.foreground",
315 "Dialog.Text.Foreground" );
316 pw->passwd_background = get_pixel_resource (si->dpy, cmap,
317 "passwd.text.background",
318 "Dialog.Text.Background" );
319 pw->button_foreground = get_pixel_resource (si->dpy, cmap,
320 "splash.Button.foreground",
321 "Dialog.Button.Foreground" );
322 pw->button_background = get_pixel_resource (si->dpy, cmap,
323 "splash.Button.background",
324 "Dialog.Button.Background" );
325 pw->thermo_foreground = get_pixel_resource (si->dpy, cmap,
326 "passwd.thermometer.foreground",
327 "Dialog.Thermometer.Foreground");
328 pw->thermo_background = get_pixel_resource ( si->dpy, cmap,
329 "passwd.thermometer.background",
330 "Dialog.Thermometer.Background");
331 pw->shadow_top = get_pixel_resource ( si->dpy, cmap,
332 "passwd.topShadowColor",
333 "Dialog.Foreground" );
334 pw->shadow_bottom = get_pixel_resource (si->dpy, cmap,
335 "passwd.bottomShadowColor",
336 "Dialog.Background" );
338 pw->preferred_logo_width = get_integer_resource (si->dpy,
340 "Dialog.Logo.Width");
341 pw->preferred_logo_height = get_integer_resource (si->dpy,
342 "passwd.logo.height",
343 "Dialog.Logo.Height");
344 pw->thermo_width = get_integer_resource (si->dpy, "passwd.thermometer.width",
345 "Dialog.Thermometer.Width");
346 pw->internal_border = get_integer_resource (si->dpy,
347 "passwd.internalBorderWidth",
348 "Dialog.InternalBorderWidth");
349 pw->shadow_width = get_integer_resource (si->dpy, "passwd.shadowThickness",
350 "Dialog.ShadowThickness");
352 if (pw->preferred_logo_width == 0) pw->preferred_logo_width = 150;
353 if (pw->preferred_logo_height == 0) pw->preferred_logo_height = 150;
354 if (pw->internal_border == 0) pw->internal_border = 15;
355 if (pw->shadow_width == 0) pw->shadow_width = 4;
356 if (pw->thermo_width == 0) pw->thermo_width = pw->shadow_width;
359 /* We need to remember the mouse position and restore it afterward, or
360 sometimes (perhaps only with Xinerama?) the mouse gets warped to
361 inside the bounds of the lock dialog window.
364 Window pointer_root, pointer_child;
365 int root_x, root_y, win_x, win_y;
367 pw->previous_mouse_x = 0;
368 pw->previous_mouse_y = 0;
369 if (XQueryPointer (si->dpy, RootWindowOfScreen (pw->prompt_screen->screen),
370 &pointer_root, &pointer_child,
371 &root_x, &root_y, &win_x, &win_y, &mask))
373 pw->previous_mouse_x = root_x;
374 pw->previous_mouse_y = root_y;
375 if (si->prefs.verbose_p)
376 fprintf (stderr, "%s: %d: mouse is at %d,%d.\n",
377 blurb(), pw->prompt_screen->number,
378 pw->previous_mouse_x, pw->previous_mouse_y);
380 else if (si->prefs.verbose_p)
381 fprintf (stderr, "%s: %d: unable to determine mouse position?\n",
382 blurb(), pw->prompt_screen->number);
385 /* Before mapping the window, save a pixmap of the current screen.
386 When we lower the window, we restore these bits. This works,
387 because the running screenhack has already been sent SIGSTOP, so
388 we know nothing else is drawing right now! */
392 pw->save_under = XCreatePixmap (si->dpy,
393 pw->prompt_screen->screensaver_window,
394 pw->prompt_screen->width,
395 pw->prompt_screen->height,
396 pw->prompt_screen->current_depth);
397 gcv.function = GXcopy;
398 gc = XCreateGC (si->dpy, pw->save_under, GCFunction, &gcv);
399 XCopyArea (si->dpy, pw->prompt_screen->screensaver_window,
402 pw->prompt_screen->width, pw->prompt_screen->height,
404 XFreeGC (si->dpy, gc);
412 Bool debug_passwd_window_p = False; /* used only by test-passwd.c */
416 * info_msg and prompt may be NULL.
419 make_passwd_window (saver_info *si,
420 const char *info_msg,
424 XSetWindowAttributes attrs;
425 unsigned long attrmask = 0;
426 passwd_dialog_data *pw;
429 Dimension max_string_width_px;
430 saver_screen_info *ssi = &si->screens [mouse_screen (si)];
432 cleanup_passwd_window (si);
434 if (! ssi) /* WTF? Trying to prompt while no screens connected? */
438 if (new_passwd_window (si) < 0)
441 if (!(pw = si->pw_data))
446 pw->prompt_screen = ssi;
447 if (si->prefs.verbose_p)
448 fprintf (stderr, "%s: %d: creating password dialog (\"%s\")\n",
449 blurb(), pw->prompt_screen->number,
450 info_msg ? info_msg : "");
452 screen = pw->prompt_screen->screen;
453 cmap = DefaultColormapOfScreen (screen);
455 pw->echo_input = echo;
457 max_string_width_px = ssi->width
458 - pw->shadow_width * 4
459 - pw->border_width * 2
461 - pw->preferred_logo_width
462 - pw->internal_border * 2;
463 /* As the string wraps it makes the window taller which makes the logo wider
464 * which leaves less room for the text which makes the string wrap. Uh-oh, a
465 * loop. By wrapping at a bit less than the available width, there's some
466 * room for the dialog to grow without going off the edge of the screen. */
467 max_string_width_px *= 0.75;
469 if (!info_msg && decrepit_p())
471 "This version of XScreenSaver\n"
472 "is very old! Please upgrade!\n");
474 pw->info_label = mlstring_new(info_msg ? info_msg : pw->body_label,
475 pw->label_font, max_string_width_px);
478 int direction, ascent, descent;
484 /* Measure the heading_label. */
485 XTextExtents (pw->heading_font,
486 pw->heading_label, strlen(pw->heading_label),
487 &direction, &ascent, &descent, &overall);
488 if (overall.width > pw->width) pw->width = overall.width;
489 pw->height += ascent + descent;
491 /* Measure the uname_label. */
492 if ((strlen(pw->uname_label)) && pw->show_uname_p)
494 XTextExtents (pw->uname_font,
495 pw->uname_label, strlen(pw->uname_label),
496 &direction, &ascent, &descent, &overall);
497 if (overall.width > pw->width) pw->width = overall.width;
498 pw->height += ascent + descent;
502 Dimension w2 = 0, w3 = 0, button_w = 0;
503 Dimension h2 = 0, h3 = 0, button_h = 0;
504 const char *passwd_string = SAMPLE_INPUT;
506 /* Measure the user_label. */
507 XTextExtents (pw->label_font,
508 pw->user_label, strlen(pw->user_label),
509 &direction, &ascent, &descent, &overall);
510 if (overall.width > w2) w2 = overall.width;
511 h2 += ascent + descent;
513 /* Measure the info_label. */
514 if (pw->info_label->overall_width > pw->width)
515 pw->width = pw->info_label->overall_width;
516 h2 += pw->info_label->overall_height;
518 /* Measure the user string. */
519 XTextExtents (pw->passwd_font,
520 si->user, strlen(si->user),
521 &direction, &ascent, &descent, &overall);
522 overall.width += (pw->shadow_width * 4);
523 ascent += (pw->shadow_width * 4);
524 if (overall.width > w3) w3 = overall.width;
525 h3 += ascent + descent;
527 /* Measure the (dummy) passwd_string. */
530 XTextExtents (pw->passwd_font,
531 passwd_string, strlen(passwd_string),
532 &direction, &ascent, &descent, &overall);
533 overall.width += (pw->shadow_width * 4);
534 ascent += (pw->shadow_width * 4);
535 if (overall.width > w3) w3 = overall.width;
536 h3 += ascent + descent;
538 /* Measure the prompt_label. */
539 max_string_width_px -= w3;
540 pw->prompt_label = mlstring_new (prompt, pw->label_font,
541 max_string_width_px);
543 if (pw->prompt_label->overall_width > w2)
544 w2 = pw->prompt_label->overall_width;
546 h2 += pw->prompt_label->overall_height;
548 w2 = w2 + w3 + (pw->shadow_width * 2);
552 /* The "Unlock" button. */
553 XTextExtents (pw->label_font,
554 pw->unlock_label, strlen(pw->unlock_label),
555 &direction, &ascent, &descent, &overall);
556 button_w = overall.width;
557 button_h = ascent + descent;
559 /* Add some horizontal padding inside the button. */
562 button_w += ((ascent + descent) / 2) + (pw->shadow_width * 2);
563 button_h += ((ascent + descent) / 2) + (pw->shadow_width * 2);
565 pw->unlock_button_width = button_w;
566 pw->unlock_button_height = button_h;
568 w2 = MAX (w2, button_w);
569 h2 += button_h * 1.5;
571 /* The "New Login" button */
572 pw->login_button_width = 0;
573 pw->login_button_height = 0;
575 if (pw->login_button_p)
577 pw->login_button_enabled_p = True;
579 /* Measure the "New Login" button */
580 XTextExtents (pw->button_font, pw->login_label,
581 strlen (pw->login_label),
582 &direction, &ascent, &descent, &overall);
583 button_w = overall.width;
584 button_h = ascent + descent;
586 /* Add some horizontal padding inside the buttons. */
589 button_w += ((ascent + descent) / 2) + (pw->shadow_width * 2);
590 button_h += ((ascent + descent) / 2) + (pw->shadow_width * 2);
592 pw->login_button_width = button_w;
593 pw->login_button_height = button_h;
595 if (button_h > pw->unlock_button_height)
596 h2 += (button_h * 1.5 - pw->unlock_button_height * 1.5);
598 /* Use (2 * shadow_width) spacing between the buttons. Another
599 (2 * shadow_width) is required to account for button shadows. */
601 button_w + pw->unlock_button_width +
602 (pw->shadow_width * 4));
605 if (w2 > pw->width) pw->width = w2;
609 pw->width += (pw->internal_border * 2);
610 pw->height += (pw->internal_border * 4);
612 pw->width += pw->thermo_width + (pw->shadow_width * 3);
614 if (pw->preferred_logo_height > pw->height)
615 pw->height = pw->logo_height = pw->preferred_logo_height;
616 else if (pw->height > pw->preferred_logo_height)
617 pw->logo_height = pw->height;
619 pw->logo_width = pw->logo_height;
621 pw->width += pw->logo_width;
624 attrmask |= CWOverrideRedirect; attrs.override_redirect = True;
626 if (debug_passwd_window_p)
627 attrs.override_redirect = False; /* kludge for test-passwd.c */
629 attrmask |= CWEventMask;
630 attrs.event_mask = (ExposureMask | KeyPressMask |
631 ButtonPressMask | ButtonReleaseMask);
633 /* Figure out where on the desktop to place the window so that it will
634 actually be visible; this takes into account virtual viewports as
637 saver_screen_info *ssi = &si->screens [mouse_screen (si)];
642 if (si->prefs.debug_p) w /= 2;
643 pw->x = x + ((w + pw->width) / 2) - pw->width;
644 pw->y = y + ((h + pw->height) / 2) - pw->height;
645 if (pw->x < x) pw->x = x;
646 if (pw->y < y) pw->y = y;
649 pw->border_width = get_integer_resource (si->dpy, "passwd.borderWidth",
650 "Dialog.BorderWidth");
652 /* Only create the window the first time around */
653 if (!si->passwd_dialog)
656 XCreateWindow (si->dpy,
657 RootWindowOfScreen(screen),
658 pw->x, pw->y, pw->width, pw->height, pw->border_width,
659 DefaultDepthOfScreen (screen), InputOutput,
660 DefaultVisualOfScreen(screen),
662 XSetWindowBackground (si->dpy, si->passwd_dialog, pw->background);
663 XSetWindowBorder (si->dpy, si->passwd_dialog, pw->border);
665 /* We use the default visual, not ssi->visual, so that the logo pixmap's
666 visual matches that of the si->passwd_dialog window. */
667 pw->logo_pixmap = xscreensaver_logo (ssi->screen,
668 /* ssi->current_visual, */
669 DefaultVisualOfScreen(screen),
670 si->passwd_dialog, cmap,
672 &pw->logo_pixels, &pw->logo_npixels,
673 &pw->logo_clipmask, True);
675 else /* On successive prompts, just resize the window */
678 unsigned int mask = CWX | CWY | CWWidth | CWHeight;
682 wc.width = pw->width;
683 wc.height = pw->height;
685 XConfigureWindow (si->dpy, si->passwd_dialog, mask, &wc);
688 restore_background(si);
690 XMapRaised (si->dpy, si->passwd_dialog);
691 XSync (si->dpy, False);
693 move_mouse_grab (si, si->passwd_dialog,
695 pw->prompt_screen->number);
701 XInstallColormap (si->dpy, cmap);
702 draw_passwd_window (si);
709 draw_passwd_window (saver_info *si)
711 passwd_dialog_data *pw = si->pw_data;
715 int x1, x2, x3, y1, y2;
720 pw->passwd_changed_p = True;
721 pw->button_state_changed_p = True;
723 /* This height is the height of all the elements, not to be confused with
724 * the overall window height which is pw->height. It is used to compute
725 * the amount of spacing (padding) between elements. */
726 height = (pw->heading_font->ascent + pw->heading_font->descent +
727 pw->info_label->overall_height +
728 MAX (((pw->label_font->ascent + pw->label_font->descent) +
729 (pw->prompt_label ? pw->prompt_label->overall_height : 0)),
730 ((pw->passwd_font->ascent + pw->passwd_font->descent) +
731 (pw->shadow_width * 2)) * (pw->prompt_label ? 2 : 1)) +
732 pw->date_font->ascent + pw->date_font->descent);
734 if ((strlen(pw->uname_label)) && pw->show_uname_p)
735 height += (pw->uname_font->ascent + pw->uname_font->descent);
737 height += ((pw->button_font->ascent + pw->button_font->descent) * 2 +
738 2 * pw->shadow_width);
740 spacing = ((pw->height - 2 * pw->shadow_width
741 - pw->internal_border - height)
744 if (spacing < 0) spacing = 0;
746 gcv.foreground = pw->foreground;
747 gc1 = XCreateGC (si->dpy, si->passwd_dialog, GCForeground, &gcv);
748 gc2 = XCreateGC (si->dpy, si->passwd_dialog, GCForeground, &gcv);
749 x1 = pw->logo_width + pw->thermo_width + (pw->shadow_width * 3);
750 x3 = pw->width - (pw->shadow_width * 2);
751 y1 = (pw->shadow_width * 2) + spacing + spacing;
755 XSetFont (si->dpy, gc1, pw->heading_font->fid);
756 sw = string_width (pw->heading_font, pw->heading_label);
757 x2 = (x1 + ((x3 - x1 - sw) / 2));
758 y1 += spacing + pw->heading_font->ascent + pw->heading_font->descent;
759 XDrawString (si->dpy, si->passwd_dialog, gc1, x2, y1,
760 pw->heading_label, strlen(pw->heading_label));
762 /* uname below top heading
764 if ((strlen(pw->uname_label)) && pw->show_uname_p)
766 XSetFont (si->dpy, gc1, pw->uname_font->fid);
767 y1 += spacing + pw->uname_font->ascent + pw->uname_font->descent;
768 sw = string_width (pw->uname_font, pw->uname_label);
769 x2 = (x1 + ((x3 - x1 - sw) / 2));
770 XDrawString (si->dpy, si->passwd_dialog, gc1, x2, y1,
771 pw->uname_label, strlen(pw->uname_label));
774 /* the info_label (below uname)
776 x2 = (x1 + ((x3 - x1 - pw->info_label->overall_width) / 2));
777 y1 += spacing + pw->info_label->font_height / 2;
778 mlstring_draw(si->dpy, si->passwd_dialog, gc1, pw->info_label,
780 y1 += pw->info_label->overall_height;
783 tb_height = (pw->passwd_font->ascent + pw->passwd_font->descent +
784 (pw->shadow_width * 4));
786 /* the "User:" prompt
789 XSetForeground (si->dpy, gc1, pw->foreground);
790 XSetFont (si->dpy, gc1, pw->label_font->fid);
791 y1 += (spacing + tb_height + pw->shadow_width);
792 x2 = (x1 + pw->internal_border +
793 MAX(string_width (pw->label_font, pw->user_label),
794 pw->prompt_label ? pw->prompt_label->overall_width : 0));
795 XDrawString (si->dpy, si->passwd_dialog, gc1,
796 x2 - string_width (pw->label_font, pw->user_label),
797 y1 - pw->passwd_font->descent,
798 pw->user_label, strlen(pw->user_label));
800 /* the prompt_label prompt
802 if (pw->prompt_label)
804 y1 += tb_height - pw->label_font->ascent + pw->shadow_width;
805 mlstring_draw(si->dpy, si->passwd_dialog, gc1, pw->prompt_label,
806 x2 - pw->prompt_label->overall_width, y1);
809 /* the "user name" text field
812 XSetForeground (si->dpy, gc1, pw->passwd_foreground);
813 XSetForeground (si->dpy, gc2, pw->passwd_background);
814 XSetFont (si->dpy, gc1, pw->passwd_font->fid);
815 y1 += (spacing + tb_height);
816 x2 += (pw->shadow_width * 4);
818 pw->passwd_field_width = x3 - x2 - pw->internal_border;
819 pw->passwd_field_height = (pw->passwd_font->ascent +
820 pw->passwd_font->descent +
823 XFillRectangle (si->dpy, si->passwd_dialog, gc2,
824 x2 - pw->shadow_width,
825 y1 - (pw->passwd_font->ascent + pw->passwd_font->descent),
826 pw->passwd_field_width, pw->passwd_field_height);
827 XDrawString (si->dpy, si->passwd_dialog, gc1,
829 y1 - pw->passwd_font->descent,
830 si->user, strlen(si->user));
832 /* the password/prompt text field
834 if (pw->prompt_label)
836 y1 += (spacing + pw->prompt_label->overall_height + pw->shadow_width*2);
838 pw->passwd_field_x = x2 - pw->shadow_width;
839 pw->passwd_field_y = y1 - (pw->passwd_font->ascent +
840 pw->passwd_font->descent);
843 /* The shadow around the text fields
846 y1 += (spacing + (pw->shadow_width * 3));
847 x1 = x2 - (pw->shadow_width * 2);
848 x2 = pw->passwd_field_width + (pw->shadow_width * 2);
849 y2 = pw->passwd_field_height + (pw->shadow_width * 2);
851 draw_shaded_rectangle (si->dpy, si->passwd_dialog,
854 pw->shadow_bottom, pw->shadow_top);
856 if (pw->prompt_label)
858 y1 += (spacing + pw->prompt_label->overall_height + pw->shadow_width*2);
859 draw_shaded_rectangle (si->dpy, si->passwd_dialog,
862 pw->shadow_bottom, pw->shadow_top);
866 /* The date, below the text fields
870 time_t now = time ((time_t *) 0);
871 struct tm *tm = localtime (&now);
872 memset (buf, 0, sizeof(buf));
873 strftime (buf, sizeof(buf)-1, pw->date_label, tm);
875 XSetFont (si->dpy, gc1, pw->date_font->fid);
876 y1 += pw->shadow_width;
877 y1 += (spacing + tb_height);
879 sw = string_width (pw->date_font, buf);
881 XDrawString (si->dpy, si->passwd_dialog, gc1, x2, y1, buf, strlen(buf));
884 /* Set up the GCs for the "New Login" and "Unlock" buttons.
886 XSetForeground(si->dpy, gc1, pw->button_foreground);
887 XSetForeground(si->dpy, gc2, pw->button_background);
888 XSetFont(si->dpy, gc1, pw->button_font->fid);
890 /* The "Unlock" button */
891 x2 = pw->width - pw->internal_border - (pw->shadow_width * 2);
893 /* right aligned button */
894 x1 = x2 - pw->unlock_button_width;
896 /* Add half the difference between y1 and the internal edge.
897 * It actually looks better if the internal border is ignored. */
898 y1 += ((pw->height - MAX (pw->unlock_button_height, pw->login_button_height)
902 pw->unlock_button_x = x1;
903 pw->unlock_button_y = y1;
905 /* The "New Login" button
907 if (pw->login_button_p)
909 /* Using the same GC as for the Unlock button */
911 sw = string_width (pw->button_font, pw->login_label);
913 /* left aligned button */
914 x1 = (pw->logo_width + pw->thermo_width + (pw->shadow_width * 3) +
915 pw->internal_border);
917 pw->login_button_x = x1;
918 pw->login_button_y = y1;
923 x1 = pw->shadow_width * 6;
924 y1 = pw->shadow_width * 6;
925 x2 = pw->logo_width - (pw->shadow_width * 12);
926 y2 = pw->logo_height - (pw->shadow_width * 12);
932 unsigned int w, h, bw, d;
933 XGetGeometry (si->dpy, pw->logo_pixmap, &root, &x, &y, &w, &h, &bw, &d);
934 XSetForeground (si->dpy, gc1, pw->foreground);
935 XSetBackground (si->dpy, gc1, pw->background);
936 XSetClipMask (si->dpy, gc1, pw->logo_clipmask);
937 XSetClipOrigin (si->dpy, gc1,
938 x1 + ((x2 - (int)w) / 2),
939 y1 + ((y2 - (int)h) / 2));
941 XCopyPlane (si->dpy, pw->logo_pixmap, si->passwd_dialog, gc1,
943 x1 + ((x2 - (int)w) / 2),
944 y1 + ((y2 - (int)h) / 2),
947 XCopyArea (si->dpy, pw->logo_pixmap, si->passwd_dialog, gc1,
949 x1 + ((x2 - (int)w) / 2),
950 y1 + ((y2 - (int)h) / 2));
955 XSetForeground (si->dpy, gc1, pw->thermo_foreground);
956 XSetForeground (si->dpy, gc2, pw->thermo_background);
958 pw->thermo_field_x = pw->logo_width + pw->shadow_width;
959 pw->thermo_field_y = pw->shadow_width * 5;
960 pw->thermo_field_height = pw->height - (pw->shadow_width * 10);
963 /* Solid border inside the logo box. */
964 XSetForeground (si->dpy, gc1, pw->foreground);
965 XDrawRectangle (si->dpy, si->passwd_dialog, gc1, x1, y1, x2-1, y2-1);
968 /* The shadow around the logo
970 draw_shaded_rectangle (si->dpy, si->passwd_dialog,
971 pw->shadow_width * 4,
972 pw->shadow_width * 4,
973 pw->logo_width - (pw->shadow_width * 8),
974 pw->logo_height - (pw->shadow_width * 8),
976 pw->shadow_bottom, pw->shadow_top);
978 /* The shadow around the thermometer
980 draw_shaded_rectangle (si->dpy, si->passwd_dialog,
982 pw->shadow_width * 4,
983 pw->thermo_width + (pw->shadow_width * 2),
984 pw->height - (pw->shadow_width * 8),
986 pw->shadow_bottom, pw->shadow_top);
989 /* Solid border inside the thermometer. */
990 XSetForeground (si->dpy, gc1, pw->foreground);
991 XDrawRectangle (si->dpy, si->passwd_dialog, gc1,
992 pw->thermo_field_x, pw->thermo_field_y,
993 pw->thermo_width - 1, pw->thermo_field_height - 1);
996 /* The shadow around the whole window
998 draw_shaded_rectangle (si->dpy, si->passwd_dialog,
999 0, 0, pw->width, pw->height, pw->shadow_width,
1000 pw->shadow_top, pw->shadow_bottom);
1002 XFreeGC (si->dpy, gc1);
1003 XFreeGC (si->dpy, gc2);
1005 update_passwd_window (si, pw->passwd_string, pw->ratio);
1009 draw_button(Display *dpy,
1012 unsigned long foreground, unsigned long background,
1015 int width, int height,
1017 Pixel shadow_light, Pixel shadow_dark,
1023 int label_x, label_y;
1025 gcv.foreground = foreground;
1026 gcv.font = font->fid;
1027 gc1 = XCreateGC(dpy, dialog, GCForeground|GCFont, &gcv);
1028 gcv.foreground = background;
1029 gc2 = XCreateGC(dpy, dialog, GCForeground, &gcv);
1031 XFillRectangle(dpy, dialog, gc2,
1032 x, y, width, height);
1034 sw = string_width(font, label);
1036 label_x = x + ((width - sw) / 2);
1037 label_y = (y + (height - (font->ascent + font->descent)) / 2 + font->ascent);
1045 XDrawString(dpy, dialog, gc1, label_x, label_y, label, strlen(label));
1050 draw_shaded_rectangle(dpy, dialog, x, y, width, height,
1051 shadow_width, shadow_light, shadow_dark);
1055 update_passwd_window (saver_info *si, const char *printed_passwd, float ratio)
1057 passwd_dialog_data *pw = si->pw_data;
1061 XRectangle rects[1];
1064 gcv.foreground = pw->passwd_foreground;
1065 gcv.font = pw->passwd_font->fid;
1066 gc1 = XCreateGC (si->dpy, si->passwd_dialog, GCForeground|GCFont, &gcv);
1067 gcv.foreground = pw->passwd_background;
1068 gc2 = XCreateGC (si->dpy, si->passwd_dialog, GCForeground, &gcv);
1072 char *s = strdup (printed_passwd);
1073 if (pw->passwd_string) free (pw->passwd_string);
1074 pw->passwd_string = s;
1077 if (pw->prompt_label)
1080 /* the "password" text field
1082 rects[0].x = pw->passwd_field_x;
1083 rects[0].y = pw->passwd_field_y;
1084 rects[0].width = pw->passwd_field_width;
1085 rects[0].height = pw->passwd_field_height;
1087 /* The user entry (password) field is double buffered.
1088 * This avoids flickering, particularly in synchronous mode. */
1090 if (pw->passwd_changed_p)
1092 pw->passwd_changed_p = False;
1094 if (pw->user_entry_pixmap)
1096 XFreePixmap(si->dpy, pw->user_entry_pixmap);
1097 pw->user_entry_pixmap = 0;
1100 pw->user_entry_pixmap =
1101 XCreatePixmap (si->dpy, si->passwd_dialog,
1102 rects[0].width, rects[0].height,
1103 DefaultDepthOfScreen (pw->prompt_screen->screen));
1105 XFillRectangle (si->dpy, pw->user_entry_pixmap, gc2,
1106 0, 0, rects[0].width, rects[0].height);
1108 XDrawString (si->dpy, pw->user_entry_pixmap, gc1,
1110 pw->passwd_font->ascent,
1111 pw->passwd_string, strlen(pw->passwd_string));
1113 /* Ensure the new pixmap gets copied to the window */
1120 if (pw->i_beam == 0)
1122 /* Make the I-beam disappear */
1123 XCopyArea(si->dpy, pw->user_entry_pixmap, si->passwd_dialog, gc2,
1124 0, 0, rects[0].width, rects[0].height,
1125 rects[0].x, rects[0].y);
1127 else if (pw->i_beam == 1)
1129 /* Make the I-beam appear */
1130 x = (rects[0].x + pw->shadow_width +
1131 string_width (pw->passwd_font, pw->passwd_string));
1132 y = rects[0].y + pw->shadow_width;
1134 if (x > rects[0].x + rects[0].width - 1)
1135 x = rects[0].x + rects[0].width - 1;
1136 XDrawLine (si->dpy, si->passwd_dialog, gc1,
1138 x, y + pw->passwd_font->ascent +
1139 pw->passwd_font->descent-1);
1142 pw->i_beam = (pw->i_beam + 1) % 4;
1148 y = (pw->thermo_field_height - 2) * (1.0 - pw->ratio);
1151 XFillRectangle (si->dpy, si->passwd_dialog, gc2,
1152 pw->thermo_field_x + 1,
1153 pw->thermo_field_y + 1,
1156 XSetForeground (si->dpy, gc1, pw->thermo_foreground);
1157 XFillRectangle (si->dpy, si->passwd_dialog, gc1,
1158 pw->thermo_field_x + 1,
1159 pw->thermo_field_y + 1 + y,
1161 MAX (0, pw->thermo_field_height - y - 2));
1164 if (pw->button_state_changed_p)
1166 pw->button_state_changed_p = False;
1168 /* The "Unlock" button
1170 draw_button(si->dpy, si->passwd_dialog, pw->button_font,
1171 pw->button_foreground, pw->button_background,
1173 pw->unlock_button_x, pw->unlock_button_y,
1174 pw->unlock_button_width, pw->unlock_button_height,
1176 (pw->unlock_button_down_p ? pw->shadow_bottom :
1178 (pw->unlock_button_down_p ? pw->shadow_top :
1180 pw->unlock_button_down_p);
1182 /* The "New Login" button
1184 if (pw->login_button_p)
1186 draw_button(si->dpy, si->passwd_dialog, pw->button_font,
1187 (pw->login_button_enabled_p
1188 ? pw->passwd_foreground
1189 : pw->shadow_bottom),
1190 pw->button_background,
1192 pw->login_button_x, pw->login_button_y,
1193 pw->login_button_width, pw->login_button_height,
1195 (pw->login_button_down_p
1198 (pw->login_button_down_p
1200 : pw->shadow_bottom),
1201 pw->login_button_down_p);
1205 XFreeGC (si->dpy, gc1);
1206 XFreeGC (si->dpy, gc2);
1207 XSync (si->dpy, False);
1212 restore_background (saver_info *si)
1214 passwd_dialog_data *pw = si->pw_data;
1215 saver_screen_info *ssi = pw->prompt_screen;
1219 gcv.function = GXcopy;
1221 gc = XCreateGC (si->dpy, ssi->screensaver_window, GCFunction, &gcv);
1223 XCopyArea (si->dpy, pw->save_under,
1224 ssi->screensaver_window, gc,
1226 ssi->width, ssi->height,
1229 XFreeGC (si->dpy, gc);
1233 /* Frees anything created by make_passwd_window */
1235 cleanup_passwd_window (saver_info *si)
1237 passwd_dialog_data *pw;
1239 if (!(pw = si->pw_data))
1244 mlstring_free(pw->info_label);
1248 if (pw->prompt_label)
1250 mlstring_free(pw->prompt_label);
1251 pw->prompt_label = 0;
1254 memset (pw->typed_passwd, 0, sizeof(pw->typed_passwd));
1255 memset (pw->typed_passwd_char_size, 0, sizeof(pw->typed_passwd_char_size));
1256 memset (pw->passwd_string, 0, strlen(pw->passwd_string));
1260 XtRemoveTimeOut (pw->timer);
1264 if (pw->user_entry_pixmap)
1266 XFreePixmap(si->dpy, pw->user_entry_pixmap);
1267 pw->user_entry_pixmap = 0;
1273 destroy_passwd_window (saver_info *si)
1275 saver_preferences *p = &si->prefs;
1276 passwd_dialog_data *pw = si->pw_data;
1277 saver_screen_info *ssi = pw->prompt_screen;
1278 Colormap cmap = DefaultColormapOfScreen (ssi->screen);
1279 Pixel black = BlackPixelOfScreen (ssi->screen);
1280 Pixel white = WhitePixelOfScreen (ssi->screen);
1283 cleanup_passwd_window (si);
1285 if (si->cached_passwd)
1287 char *wipe = si->cached_passwd;
1292 free(si->cached_passwd);
1293 si->cached_passwd = NULL;
1296 move_mouse_grab (si, RootWindowOfScreen (ssi->screen),
1297 ssi->cursor, ssi->number);
1299 if (pw->passwd_cursor)
1300 XFreeCursor (si->dpy, pw->passwd_cursor);
1303 fprintf (stderr, "%s: %d: moving mouse back to %d,%d.\n",
1304 blurb(), ssi->number,
1305 pw->previous_mouse_x, pw->previous_mouse_y);
1307 XWarpPointer (si->dpy, None, RootWindowOfScreen (ssi->screen),
1309 pw->previous_mouse_x, pw->previous_mouse_y);
1310 XSync (si->dpy, False);
1312 while (XCheckMaskEvent (si->dpy, PointerMotionMask, &event))
1314 fprintf (stderr, "%s: discarding MotionNotify event.\n", blurb());
1317 if (si->using_xinput_extension && si->xinput_DeviceMotionNotify)
1318 while (XCheckTypedEvent (si->dpy, si->xinput_DeviceMotionNotify, &event))
1320 fprintf (stderr, "%s: discarding DeviceMotionNotify event.\n",
1324 if (si->passwd_dialog)
1326 if (si->prefs.verbose_p)
1327 fprintf (stderr, "%s: %d: destroying password dialog.\n",
1328 blurb(), pw->prompt_screen->number);
1330 XDestroyWindow (si->dpy, si->passwd_dialog);
1331 si->passwd_dialog = 0;
1336 restore_background(si);
1337 XFreePixmap (si->dpy, pw->save_under);
1341 if (pw->heading_label) free (pw->heading_label);
1342 if (pw->body_label) free (pw->body_label);
1343 if (pw->user_label) free (pw->user_label);
1344 if (pw->date_label) free (pw->date_label);
1345 if (pw->login_label) free (pw->login_label);
1346 if (pw->unlock_label) free (pw->unlock_label);
1347 if (pw->passwd_string) free (pw->passwd_string);
1348 if (pw->uname_label) free (pw->uname_label);
1350 if (pw->heading_font) XFreeFont (si->dpy, pw->heading_font);
1351 if (pw->body_font) XFreeFont (si->dpy, pw->body_font);
1352 if (pw->label_font) XFreeFont (si->dpy, pw->label_font);
1353 if (pw->passwd_font) XFreeFont (si->dpy, pw->passwd_font);
1354 if (pw->date_font) XFreeFont (si->dpy, pw->date_font);
1355 if (pw->button_font) XFreeFont (si->dpy, pw->button_font);
1356 if (pw->uname_font) XFreeFont (si->dpy, pw->uname_font);
1358 if (pw->foreground != black && pw->foreground != white)
1359 XFreeColors (si->dpy, cmap, &pw->foreground, 1, 0L);
1360 if (pw->background != black && pw->background != white)
1361 XFreeColors (si->dpy, cmap, &pw->background, 1, 0L);
1362 if (!(pw->button_foreground == black || pw->button_foreground == white))
1363 XFreeColors (si->dpy, cmap, &pw->button_foreground, 1, 0L);
1364 if (!(pw->button_background == black || pw->button_background == white))
1365 XFreeColors (si->dpy, cmap, &pw->button_background, 1, 0L);
1366 if (pw->passwd_foreground != black && pw->passwd_foreground != white)
1367 XFreeColors (si->dpy, cmap, &pw->passwd_foreground, 1, 0L);
1368 if (pw->passwd_background != black && pw->passwd_background != white)
1369 XFreeColors (si->dpy, cmap, &pw->passwd_background, 1, 0L);
1370 if (pw->thermo_foreground != black && pw->thermo_foreground != white)
1371 XFreeColors (si->dpy, cmap, &pw->thermo_foreground, 1, 0L);
1372 if (pw->thermo_background != black && pw->thermo_background != white)
1373 XFreeColors (si->dpy, cmap, &pw->thermo_background, 1, 0L);
1374 if (pw->shadow_top != black && pw->shadow_top != white)
1375 XFreeColors (si->dpy, cmap, &pw->shadow_top, 1, 0L);
1376 if (pw->shadow_bottom != black && pw->shadow_bottom != white)
1377 XFreeColors (si->dpy, cmap, &pw->shadow_bottom, 1, 0L);
1379 if (pw->logo_pixmap)
1380 XFreePixmap (si->dpy, pw->logo_pixmap);
1381 if (pw-> logo_clipmask)
1382 XFreePixmap (si->dpy, pw->logo_clipmask);
1383 if (pw->logo_pixels)
1385 if (pw->logo_npixels)
1386 XFreeColors (si->dpy, cmap, pw->logo_pixels, pw->logo_npixels, 0L);
1387 free (pw->logo_pixels);
1388 pw->logo_pixels = 0;
1389 pw->logo_npixels = 0;
1393 XFreePixmap (si->dpy, pw->save_under);
1396 XInstallColormap (si->dpy, cmap);
1398 memset (pw, 0, sizeof(*pw));
1404 #if defined(HAVE_XF86MISCSETGRABKEYSSTATE) || defined(HAVE_XF86VMODE)
1406 static Bool error_handler_hit_p = False;
1409 ignore_all_errors_ehandler (Display *dpy, XErrorEvent *error)
1411 error_handler_hit_p = True;
1415 #endif /* HAVE_XF86MISCSETGRABKEYSSTATE || HAVE_XF86VMODE */
1418 #ifdef HAVE_XHPDISABLERESET
1419 /* This function enables and disables the C-Sh-Reset hot-key, which
1420 normally resets the X server (logging out the logged-in user.)
1421 We don't want random people to be able to do that while the
1425 hp_lock_reset (saver_info *si, Bool lock_p)
1427 static Bool hp_locked_p = False;
1429 /* Calls to XHPDisableReset and XHPEnableReset must be balanced,
1430 or BadAccess errors occur. (It's ok for this to be global,
1431 since it affects the whole machine, not just the current screen.)
1433 if (hp_locked_p == lock_p)
1437 XHPDisableReset (si->dpy);
1439 XHPEnableReset (si->dpy);
1440 hp_locked_p = lock_p;
1442 #endif /* HAVE_XHPDISABLERESET */
1445 #ifdef HAVE_XF86MISCSETGRABKEYSSTATE
1447 /* This function enables and disables the Ctrl-Alt-KP_star and
1448 Ctrl-Alt-KP_slash hot-keys, which (in XFree86 4.2) break any
1449 grabs and/or kill the grabbing client. That would effectively
1450 unlock the screen, so we don't like that.
1452 The Ctrl-Alt-KP_star and Ctrl-Alt-KP_slash hot-keys only exist
1453 if AllowDeactivateGrabs and/or AllowClosedownGrabs are turned on
1454 in XF86Config. I believe they are disabled by default.
1456 This does not affect any other keys (specifically Ctrl-Alt-BS or
1457 Ctrl-Alt-F1) but I wish it did. Maybe it will someday.
1460 xfree_lock_grab_smasher (saver_info *si, Bool lock_p)
1462 saver_preferences *p = &si->prefs;
1465 XErrorHandler old_handler;
1467 if (!XF86MiscQueryExtension(si->dpy, &event, &error))
1470 XSync (si->dpy, False);
1471 error_handler_hit_p = False;
1472 old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1473 XSync (si->dpy, False);
1474 status = XF86MiscSetGrabKeysState (si->dpy, !lock_p);
1475 XSync (si->dpy, False);
1476 if (error_handler_hit_p) status = 666;
1478 if (!lock_p && status == MiscExtGrabStateAlready)
1479 status = MiscExtGrabStateSuccess; /* shut up, consider this success */
1481 if (p->verbose_p && status != MiscExtGrabStateSuccess)
1482 fprintf (stderr, "%s: error: XF86MiscSetGrabKeysState(%d) returned %s\n",
1484 (status == MiscExtGrabStateSuccess ? "MiscExtGrabStateSuccess" :
1485 status == MiscExtGrabStateLocked ? "MiscExtGrabStateLocked" :
1486 status == MiscExtGrabStateAlready ? "MiscExtGrabStateAlready" :
1487 status == 666 ? "an X error" :
1490 XSync (si->dpy, False);
1491 XSetErrorHandler (old_handler);
1492 XSync (si->dpy, False);
1494 #endif /* HAVE_XF86MISCSETGRABKEYSSTATE */
1498 /* This function enables and disables the C-Alt-Plus and C-Alt-Minus
1499 hot-keys, which normally change the resolution of the X server.
1500 We don't want people to be able to switch the server resolution
1501 while the screen is locked, because if they switch to a higher
1502 resolution, it could cause part of the underlying desktop to become
1505 #ifdef HAVE_XF86VMODE
1508 xfree_lock_mode_switch (saver_info *si, Bool lock_p)
1510 static Bool any_mode_locked_p = False;
1511 saver_preferences *p = &si->prefs;
1513 int real_nscreens = ScreenCount (si->dpy);
1516 XErrorHandler old_handler;
1518 if (any_mode_locked_p == lock_p)
1520 if (!XF86VidModeQueryExtension (si->dpy, &event, &error))
1523 for (screen = 0; screen < real_nscreens; screen++)
1525 XSync (si->dpy, False);
1526 old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1527 error_handler_hit_p = False;
1528 status = XF86VidModeLockModeSwitch (si->dpy, screen, lock_p);
1529 XSync (si->dpy, False);
1530 XSetErrorHandler (old_handler);
1531 if (error_handler_hit_p) status = False;
1534 any_mode_locked_p = lock_p;
1536 if (!status && (p->verbose_p || !lock_p))
1537 /* Only print this when verbose, or when we locked but can't unlock.
1538 I tried printing this message whenever it comes up, but
1539 mode-locking always fails if DontZoom is set in XF86Config. */
1540 fprintf (stderr, "%s: %d: unable to %s mode switching!\n",
1541 blurb(), screen, (lock_p ? "lock" : "unlock"));
1542 else if (p->verbose_p)
1543 fprintf (stderr, "%s: %d: %s mode switching.\n",
1544 blurb(), screen, (lock_p ? "locked" : "unlocked"));
1547 #endif /* HAVE_XF86VMODE */
1550 /* If the viewport has been scrolled since the screen was blanked,
1551 then scroll it back to where it belongs. This function only exists
1552 to patch over a very brief race condition.
1555 undo_vp_motion (saver_info *si)
1557 #ifdef HAVE_XF86VMODE
1558 saver_preferences *p = &si->prefs;
1560 int real_nscreens = ScreenCount (si->dpy);
1563 if (!XF86VidModeQueryExtension (si->dpy, &event, &error))
1566 for (screen = 0; screen < real_nscreens; screen++)
1568 saver_screen_info *ssi = &si->screens[screen];
1572 if (ssi->blank_vp_x == -1 && ssi->blank_vp_y == -1)
1574 if (!XF86VidModeGetViewPort (si->dpy, screen, &x, &y))
1576 if (ssi->blank_vp_x == x && ssi->blank_vp_y == y)
1579 /* We're going to move the viewport. The mouse has just been grabbed on
1580 (and constrained to, thus warped to) the password window, so it is no
1581 longer near the edge of the screen. However, wait a bit anyway, just
1582 to make sure the server drains its last motion event, so that the
1583 screen doesn't continue to scroll after we've reset the viewport.
1585 XSync (si->dpy, False);
1586 usleep (250000); /* 1/4 second */
1587 XSync (si->dpy, False);
1589 status = XF86VidModeSetViewPort (si->dpy, screen,
1590 ssi->blank_vp_x, ssi->blank_vp_y);
1594 "%s: %d: unable to move vp from (%d,%d) back to (%d,%d)!\n",
1595 blurb(), screen, x, y, ssi->blank_vp_x, ssi->blank_vp_y);
1596 else if (p->verbose_p)
1598 "%s: %d: vp moved to (%d,%d); moved it back to (%d,%d).\n",
1599 blurb(), screen, x, y, ssi->blank_vp_x, ssi->blank_vp_y);
1601 #endif /* HAVE_XF86VMODE */
1610 passwd_animate_timer (XtPointer closure, XtIntervalId *id)
1612 saver_info *si = (saver_info *) closure;
1614 passwd_dialog_data *pw = si->pw_data;
1618 pw->ratio -= (1.0 / ((double) si->prefs.passwd_timeout / (double) tick));
1622 if (si->unlock_state == ul_read)
1623 si->unlock_state = ul_time;
1626 update_passwd_window (si, 0, pw->ratio);
1628 if (si->unlock_state == ul_read)
1629 pw->timer = XtAppAddTimeOut (si->app, tick, passwd_animate_timer,
1634 idle_timer ((XtPointer) si, 0);
1638 static XComposeStatus *compose_status;
1641 handle_login_button (saver_info *si, XEvent *event)
1643 saver_preferences *p = &si->prefs;
1644 Bool mouse_in_box = False;
1646 passwd_dialog_data *pw = si->pw_data;
1647 saver_screen_info *ssi = pw->prompt_screen;
1649 if (! pw->login_button_enabled_p)
1653 (event->xbutton.x >= pw->login_button_x &&
1654 event->xbutton.x <= pw->login_button_x + pw->login_button_width &&
1655 event->xbutton.y >= pw->login_button_y &&
1656 event->xbutton.y <= pw->login_button_y + pw->login_button_height);
1658 if (ButtonRelease == event->xany.type &&
1659 pw->login_button_down_p &&
1662 /* Only allow them to press the button once: don't want to
1663 accidentally launch a dozen gdm choosers if the machine
1667 pw->login_button_enabled_p = False;
1670 pw->login_button_down_p = (mouse_in_box &&
1671 ButtonRelease != event->xany.type);
1673 update_passwd_window (si, 0, pw->ratio);
1676 fork_and_exec (ssi, p->new_login_command);
1681 handle_unlock_button (saver_info *si, XEvent *event)
1683 Bool mouse_in_box = False;
1684 passwd_dialog_data *pw = si->pw_data;
1687 (event->xbutton.x >= pw->unlock_button_x &&
1688 event->xbutton.x <= pw->unlock_button_x + pw->unlock_button_width &&
1689 event->xbutton.y >= pw->unlock_button_y &&
1690 event->xbutton.y <= pw->unlock_button_y + pw->unlock_button_height);
1692 if (ButtonRelease == event->xany.type &&
1693 pw->unlock_button_down_p &&
1695 finished_typing_passwd (si, pw);
1697 pw->unlock_button_down_p = (mouse_in_box &&
1698 ButtonRelease != event->xany.type);
1703 finished_typing_passwd (saver_info *si, passwd_dialog_data *pw)
1705 if (si->unlock_state == ul_read)
1707 update_passwd_window (si, "Checking...", pw->ratio);
1708 XSync (si->dpy, False);
1710 si->unlock_state = ul_finished;
1711 update_passwd_window (si, "", pw->ratio);
1716 handle_passwd_key (saver_info *si, XKeyEvent *event)
1718 passwd_dialog_data *pw = si->pw_data;
1719 unsigned char decoded [MAX_BYTES_PER_CHAR * 10]; /* leave some slack */
1722 /* XLookupString may return more than one character via XRebindKeysym;
1723 and on some systems it returns multi-byte UTF-8 characters (contrary
1724 to its documentation, which says it returns only Latin1.)
1726 It seems to only do so, however, if setlocale() has been called.
1727 See the code inside ENABLE_NLS in xscreensaver.c.
1729 int decoded_size = XLookupString (event, (char *)decoded, sizeof(decoded),
1730 &keysym, compose_status);
1734 const char *ks = XKeysymToString (keysym);
1736 fprintf(stderr, "## %-12s\t=> %d\t", (ks ? ks : "(null)"), decoded_size);
1737 for (i = 0; i < decoded_size; i++)
1738 fprintf(stderr, "%c", decoded[i]);
1739 fprintf(stderr, "\t");
1740 for (i = 0; i < decoded_size; i++)
1741 fprintf(stderr, "\\%03o", ((unsigned char *)decoded)[i]);
1742 fprintf(stderr, "\n");
1746 if (decoded_size > MAX_BYTES_PER_CHAR)
1748 /* The multi-byte character returned is too large. */
1753 decoded[decoded_size] = 0;
1754 pw->passwd_changed_p = True;
1756 /* Add 10% to the time remaining every time a key is pressed. */
1758 if (pw->ratio > 1) pw->ratio = 1;
1760 if (decoded_size == 1) /* Handle single-char commands */
1764 case '\010': case '\177': /* Backspace */
1766 /* kludgey way to get the number of "logical" characters. */
1767 int nchars = strlen (pw->typed_passwd_char_size);
1768 int nbytes = strlen (pw->typed_passwd);
1774 for (i = pw->typed_passwd_char_size[nchars-1]; i >= 0; i--)
1776 if (nbytes < 0) abort();
1777 pw->typed_passwd[nbytes--] = 0;
1779 pw->typed_passwd_char_size[nchars-1] = 0;
1784 case '\012': case '\015': /* Enter */
1785 finished_typing_passwd (si, pw);
1788 case '\033': /* Escape */
1789 si->unlock_state = ul_cancel;
1792 case '\025': case '\030': /* Erase line */
1793 memset (pw->typed_passwd, 0, sizeof (pw->typed_passwd));
1794 memset (pw->typed_passwd_char_size, 0,
1795 sizeof (pw->typed_passwd_char_size));
1799 if (*decoded < ' ' && *decoded != '\t') /* Other ctrl char */
1810 nbytes = strlen (pw->typed_passwd);
1811 nchars = strlen (pw->typed_passwd_char_size);
1812 if (nchars + 1 >= sizeof (pw->typed_passwd_char_size)-1 ||
1813 nbytes + decoded_size >= sizeof (pw->typed_passwd)-1) /* overflow */
1817 pw->typed_passwd_char_size[nchars] = decoded_size;
1818 pw->typed_passwd_char_size[nchars+1] = 0;
1819 memcpy (pw->typed_passwd + nbytes, decoded, decoded_size);
1820 pw->typed_passwd[nbytes + decoded_size] = 0;
1826 /* If the input is wider than the text box, only show the last portion,
1827 to simulate a horizontally-scrolling text field. */
1828 int chars_in_pwfield = (pw->passwd_field_width /
1829 pw->passwd_font->max_bounds.width);
1830 const char *output = pw->typed_passwd;
1831 if (strlen(output) > chars_in_pwfield)
1832 output += (strlen(output) - chars_in_pwfield);
1833 update_passwd_window (si, output, pw->ratio);
1835 else if (pw->show_stars_p)
1837 int nchars = strlen (pw->typed_passwd_char_size);
1839 stars = (char *) malloc(nchars + 1);
1840 memset (stars, '*', nchars);
1842 update_passwd_window (si, stars, pw->ratio);
1847 update_passwd_window (si, "", pw->ratio);
1853 passwd_event_loop (saver_info *si)
1855 saver_preferences *p = &si->prefs;
1858 /* We have to go through this union bullshit because gcc-4.4.0 has
1859 stricter struct-aliasing rules. Without this, the optimizer
1865 XRRScreenChangeNotifyEvent xrr_event;
1866 # endif /* HAVE_RANDR */
1869 passwd_animate_timer ((XtPointer) si, 0);
1870 reset_watchdog_timer (si, False); /* Disable watchdog while dialog up */
1872 while (si->unlock_state == ul_read)
1874 XtAppNextEvent (si->app, &event.x_event);
1877 if (si->using_randr_extension &&
1878 (event.x_event.type ==
1879 (si->randr_event_number + RRScreenChangeNotify)))
1881 /* The Resize and Rotate extension sends an event when the
1882 size, rotation, or refresh rate of any screen has changed. */
1886 /* XRRRootToScreen is in Xrandr.h 1.4, 2001/06/07 */
1887 int screen = XRRRootToScreen(si->dpy, event.xrr_event.window);
1888 fprintf (stderr, "%s: %d: screen change event received\n",
1892 #ifdef RRScreenChangeNotifyMask
1893 /* Inform Xlib that it's ok to update its data structures. */
1894 XRRUpdateConfiguration(&event.x_event); /* Xrandr.h 1.9, 2002/09/29*/
1895 #endif /* RRScreenChangeNotifyMask */
1897 /* Resize the existing xscreensaver windows and cached ssi data. */
1898 if (update_screen_layout (si))
1902 fprintf (stderr, "%s: new layout:\n", blurb());
1903 describe_monitor_layout (si);
1905 resize_screensaver_window (si);
1909 #endif /* HAVE_RANDR */
1911 if (event.x_event.xany.window == si->passwd_dialog &&
1912 event.x_event.xany.type == Expose)
1913 draw_passwd_window (si);
1914 else if (event.x_event.xany.type == KeyPress)
1916 handle_passwd_key (si, &event.x_event.xkey);
1917 si->pw_data->caps_p = (event.x_event.xkey.state & LockMask);
1919 else if (event.x_event.xany.type == ButtonPress ||
1920 event.x_event.xany.type == ButtonRelease)
1922 si->pw_data->button_state_changed_p = True;
1923 handle_unlock_button (si, &event.x_event);
1924 if (si->pw_data->login_button_p)
1925 handle_login_button (si, &event.x_event);
1928 XtDispatchEvent (&event.x_event);
1931 switch (si->unlock_state)
1933 case ul_cancel: msg = ""; break;
1934 case ul_time: msg = "Timed out!"; break;
1935 case ul_finished: msg = "Checking..."; break;
1936 default: msg = 0; break;
1940 switch (si->unlock_state) {
1942 fprintf (stderr, "%s: input cancelled.\n", blurb()); break;
1944 fprintf (stderr, "%s: input timed out.\n", blurb()); break;
1946 fprintf (stderr, "%s: input finished.\n", blurb()); break;
1952 si->pw_data->i_beam = 0;
1953 update_passwd_window (si, msg, 0.0);
1954 XSync (si->dpy, False);
1956 /* Swallow all pending KeyPress/KeyRelease events. */
1959 while (XCheckMaskEvent (si->dpy, KeyPressMask|KeyReleaseMask, &e))
1964 reset_watchdog_timer (si, True); /* Re-enable watchdog */
1969 handle_typeahead (saver_info *si)
1971 passwd_dialog_data *pw = si->pw_data;
1973 if (!si->unlock_typeahead)
1976 pw->passwd_changed_p = True;
1978 i = strlen (si->unlock_typeahead);
1979 if (i >= sizeof(pw->typed_passwd) - 1)
1980 i = sizeof(pw->typed_passwd) - 1;
1982 memcpy (pw->typed_passwd, si->unlock_typeahead, i);
1983 pw->typed_passwd [i] = 0;
1986 char *c = pw->typed_passwd_char_size;
1987 for (j = 0; j < i; j++)
1992 memset (si->unlock_typeahead, '*', strlen(si->unlock_typeahead));
1993 si->unlock_typeahead[i] = 0;
1994 update_passwd_window (si, si->unlock_typeahead, pw->ratio);
1996 free (si->unlock_typeahead);
1997 si->unlock_typeahead = 0;
2002 * Returns a copy of the input string with trailing whitespace removed.
2003 * Whitespace is anything considered so by isspace().
2004 * It is safe to call this with NULL, in which case NULL will be returned.
2005 * The returned string (if not NULL) should be freed by the caller with free().
2008 remove_trailing_whitespace(const char *str)
2018 newstr = malloc(len + 1);
2022 (void) strcpy(newstr, str);
2024 while (isspace(*--chr) && chr >= newstr)
2032 * The authentication conversation function.
2033 * Like a PAM conversation function, this accepts multiple messages in a single
2034 * round. It then splits them into individual messages for display on the
2035 * passwd dialog. A message sequence of info or error followed by a prompt will
2036 * be reduced into a single dialog window.
2038 * Returns 0 on success or -1 if some problem occurred (cancelled, OOM, etc.)
2041 gui_auth_conv(int num_msg,
2042 const struct auth_message auth_msgs[],
2043 struct auth_response **resp,
2047 const char *info_msg, *prompt;
2048 struct auth_response *responses;
2050 if (si->unlock_state == ul_cancel ||
2051 si->unlock_state == ul_time)
2052 /* If we've already cancelled or timed out in this PAM conversation,
2053 don't prompt again even if PAM asks us to! */
2056 if (!(responses = calloc(num_msg, sizeof(struct auth_response))))
2059 for (i = 0; i < num_msg; ++i)
2061 info_msg = prompt = NULL;
2063 /* See if there is a following message that can be shown at the same
2065 if (auth_msgs[i].type == AUTH_MSGTYPE_INFO
2067 && ( auth_msgs[i+1].type == AUTH_MSGTYPE_PROMPT_NOECHO
2068 || auth_msgs[i+1].type == AUTH_MSGTYPE_PROMPT_ECHO)
2071 info_msg = auth_msgs[i].msg;
2072 prompt = auth_msgs[++i].msg;
2076 if ( auth_msgs[i].type == AUTH_MSGTYPE_INFO
2077 || auth_msgs[i].type == AUTH_MSGTYPE_ERROR)
2078 info_msg = auth_msgs[i].msg;
2080 prompt = auth_msgs[i].msg;
2084 char *info_msg_trimmed, *prompt_trimmed;
2086 /* Trailing whitespace looks bad in a GUI */
2087 info_msg_trimmed = remove_trailing_whitespace(info_msg);
2088 prompt_trimmed = remove_trailing_whitespace(prompt);
2090 if (make_passwd_window(si, info_msg_trimmed, prompt_trimmed,
2091 auth_msgs[i].type == AUTH_MSGTYPE_PROMPT_ECHO
2096 if (info_msg_trimmed)
2097 free(info_msg_trimmed);
2100 free(prompt_trimmed);
2103 compose_status = calloc (1, sizeof (*compose_status));
2104 if (!compose_status)
2107 si->unlock_state = ul_read;
2109 handle_typeahead (si);
2110 passwd_event_loop (si);
2112 if (si->unlock_state == ul_cancel)
2115 responses[i].response = strdup(si->pw_data->typed_passwd);
2117 /* Cache the first response to a PROMPT_NOECHO to save prompting for
2118 * each auth mechanism. */
2119 if (si->cached_passwd == NULL &&
2120 auth_msgs[i].type == AUTH_MSGTYPE_PROMPT_NOECHO)
2121 si->cached_passwd = strdup(responses[i].response);
2123 free (compose_status);
2129 return (si->unlock_state == ul_finished) ? 0 : -1;
2133 free (compose_status);
2138 for (i = 0; i < num_msg; ++i)
2139 if (responses[i].response)
2140 free (responses[i].response);
2149 auth_finished_cb (saver_info *si)
2154 /* If we have something to say, put the dialog back up for a few seconds
2155 to display it. Otherwise, don't bother.
2158 if (si->unlock_state == ul_fail && /* failed with caps lock on */
2159 si->pw_data && si->pw_data->caps_p)
2160 s = "Authentication failed (Caps Lock?)";
2161 else if (si->unlock_state == ul_fail) /* failed without caps lock */
2162 s = "Authentication failed!";
2163 else if (si->unlock_state == ul_success && /* good, but report failures */
2164 si->unlock_failures > 0)
2166 if (si->unlock_failures == 1)
2167 s = "There has been\n1 failed login attempt.";
2170 sprintf (buf, "There have been\n%d failed login attempts.",
2171 si->unlock_failures);
2174 si->unlock_failures = 0;
2176 /* ignore failures if they all were too recent */
2177 if (time((time_t *) 0) - si->unlock_failure_time
2178 < si->prefs.auth_warning_slack)
2181 else /* good, with no failures, */
2182 goto END; /* or timeout, or cancel. */
2184 make_passwd_window (si, s, NULL, True);
2185 XSync (si->dpy, False);
2189 time_t start = time ((time_t *) 0);
2191 while (time ((time_t *) 0) < start + secs)
2192 if (XPending (si->dpy))
2194 XNextEvent (si->dpy, &event);
2195 if (event.xany.window == si->passwd_dialog &&
2196 event.xany.type == Expose)
2197 draw_passwd_window (si);
2198 else if (event.xany.type == ButtonPress ||
2199 event.xany.type == KeyPress)
2201 XSync (si->dpy, False);
2204 usleep (250000); /* 1/4 second */
2209 destroy_passwd_window (si);
2214 unlock_p (saver_info *si)
2216 saver_preferences *p = &si->prefs;
2220 fprintf(stderr, "%s: Error: no unlock function specified!\n", blurb());
2224 raise_window (si, True, True, True);
2226 xss_authenticate(si, p->verbose_p);
2228 return (si->unlock_state == ul_success);
2233 set_locked_p (saver_info *si, Bool locked_p)
2235 si->locked_p = locked_p;
2237 #ifdef HAVE_XHPDISABLERESET
2238 hp_lock_reset (si, locked_p); /* turn off/on C-Sh-Reset */
2240 #ifdef HAVE_XF86VMODE
2241 xfree_lock_mode_switch (si, locked_p); /* turn off/on C-Alt-Plus */
2243 #ifdef HAVE_XF86MISCSETGRABKEYSSTATE
2244 xfree_lock_grab_smasher (si, locked_p); /* turn off/on C-Alt-KP-*,/ */
2247 store_saver_status (si); /* store locked-p */
2251 #else /* NO_LOCKING -- whole file */
2254 set_locked_p (saver_info *si, Bool locked_p)
2256 if (locked_p) abort();
2259 #endif /* !NO_LOCKING */