1 /* lock.c --- handling the password dialog for locking-mode.
2 * xscreensaver, Copyright (c) 1993-2007 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 */
50 ERROR! You must not include vroot.h in this file.
54 # include <sys/utsname.h> /* for hostname info */
55 #endif /* HAVE_UNAME */
62 extern char *getenv(const char *name);
63 extern int validate_user(char *name, char *password);
66 vms_passwd_valid_p(char *pw, Bool verbose_p)
68 return (validate_user (getenv("USER"), typed_passwd) == 1);
70 # undef passwd_valid_p
71 # define passwd_valid_p vms_passwd_valid_p
75 #define SAMPLE_INPUT "MMMMMMMMMMMM"
79 #define MAX(a,b) ((a)>(b)?(a):(b))
81 typedef struct info_dialog_data info_dialog_data;
83 struct passwd_dialog_data {
85 saver_screen_info *prompt_screen;
86 int previous_mouse_x, previous_mouse_y;
88 char typed_passwd [80];
96 Dimension border_width;
99 Bool show_stars_p; /* "I regret that I have but one asterisk for my country."
100 -- Nathan Hale, 1776. */
105 mlstring *info_label;
106 /* The entry field shall only be displayed if prompt_label is not NULL */
107 mlstring *prompt_label;
110 Bool passwd_changed_p; /* Whether the user entry field needs redrawing */
111 Bool caps_p; /* Whether we saw a keypress with caps-lock on */
118 XFontStruct *heading_font;
119 XFontStruct *body_font;
120 XFontStruct *label_font;
121 XFontStruct *passwd_font;
122 XFontStruct *date_font;
123 XFontStruct *button_font;
124 XFontStruct *uname_font;
128 Pixel passwd_foreground;
129 Pixel passwd_background;
130 Pixel thermo_foreground;
131 Pixel thermo_background;
134 Pixel button_foreground;
135 Pixel button_background;
137 Dimension preferred_logo_width, logo_width;
138 Dimension preferred_logo_height, logo_height;
139 Dimension thermo_width;
140 Dimension internal_border;
141 Dimension shadow_width;
143 Dimension passwd_field_x, passwd_field_y;
144 Dimension passwd_field_width, passwd_field_height;
146 Dimension unlock_button_x, unlock_button_y;
147 Dimension unlock_button_width, unlock_button_height;
149 Dimension login_button_x, login_button_y;
150 Dimension login_button_width, login_button_height;
152 Dimension thermo_field_x, thermo_field_y;
153 Dimension thermo_field_height;
156 Pixmap logo_clipmask;
158 unsigned long *logo_pixels;
160 Cursor passwd_cursor;
161 Bool unlock_button_down_p;
162 Bool login_button_down_p;
164 Bool login_button_enabled_p;
165 Bool button_state_changed_p; /* Refers to both buttons */
168 Pixmap user_entry_pixmap;
171 static void draw_passwd_window (saver_info *si);
172 static void update_passwd_window (saver_info *si, const char *printed_passwd,
174 static void destroy_passwd_window (saver_info *si);
175 static void undo_vp_motion (saver_info *si);
176 static void finished_typing_passwd (saver_info *si, passwd_dialog_data *pw);
177 static void cleanup_passwd_window (saver_info *si);
178 static void restore_background (saver_info *si);
180 extern void xss_authenticate(saver_info *si, Bool verbose_p);
183 new_passwd_window (saver_info *si)
185 passwd_dialog_data *pw;
189 saver_screen_info *ssi = &si->screens [mouse_screen (si)];
191 pw = (passwd_dialog_data *) calloc (1, sizeof(*pw));
195 /* Display the button only if the "newLoginCommand" pref is non-null.
197 pw->login_button_p = (si->prefs.new_login_command &&
198 *si->prefs.new_login_command);
200 pw->passwd_cursor = XCreateFontCursor (si->dpy, XC_top_left_arrow);
202 pw->prompt_screen = ssi;
204 screen = pw->prompt_screen->screen;
205 cmap = DefaultColormapOfScreen (screen);
207 pw->show_stars_p = get_boolean_resource(si->dpy, "passwd.asterisks",
210 pw->heading_label = get_string_resource (si->dpy, "passwd.heading.label",
211 "Dialog.Label.Label");
212 pw->body_label = get_string_resource (si->dpy, "passwd.body.label",
213 "Dialog.Label.Label");
214 pw->user_label = get_string_resource (si->dpy, "passwd.user.label",
215 "Dialog.Label.Label");
216 pw->unlock_label = get_string_resource (si->dpy, "passwd.unlock.label",
217 "Dialog.Button.Label");
218 pw->login_label = get_string_resource (si->dpy, "passwd.login.label",
219 "Dialog.Button.Label");
221 pw->date_label = get_string_resource (si->dpy, "dateFormat", "DateFormat");
223 if (!pw->heading_label)
224 pw->heading_label = strdup("ERROR: RESOURCES NOT INSTALLED CORRECTLY");
226 pw->body_label = strdup("ERROR: RESOURCES NOT INSTALLED CORRECTLY");
227 if (!pw->user_label) pw->user_label = strdup("ERROR");
228 if (!pw->date_label) pw->date_label = strdup("ERROR");
229 if (!pw->unlock_label) pw->unlock_label = strdup("ERROR (UNLOCK)");
230 if (!pw->login_label) pw->login_label = strdup ("ERROR (LOGIN)") ;
232 /* Put the version number in the label. */
234 char *s = (char *) malloc (strlen(pw->heading_label) + 20);
235 sprintf(s, pw->heading_label, si->version);
236 free (pw->heading_label);
237 pw->heading_label = s;
240 /* Get hostname info */
241 pw->uname_label = strdup(""); /* Initialy, write nothing */
247 if (uname (&uts) == 0)
249 #if 0 /* Get the full hostname */
252 if ((s = strchr(uts.nodename, '.')))
256 char *s = strdup (uts.nodename);
257 free (pw->uname_label);
263 pw->passwd_string = strdup("");
265 f = get_string_resource (si->dpy, "passwd.headingFont", "Dialog.Font");
266 pw->heading_font = XLoadQueryFont (si->dpy, (f ? f : "fixed"));
267 if (!pw->heading_font) pw->heading_font = XLoadQueryFont (si->dpy, "fixed");
270 f = get_string_resource (si->dpy, "passwd.buttonFont", "Dialog.Font");
271 pw->button_font = XLoadQueryFont (si->dpy, (f ? f : "fixed"));
272 if (!pw->button_font) pw->button_font = XLoadQueryFont (si->dpy, "fixed");
275 f = get_string_resource(si->dpy, "passwd.bodyFont", "Dialog.Font");
276 pw->body_font = XLoadQueryFont (si->dpy, (f ? f : "fixed"));
277 if (!pw->body_font) pw->body_font = XLoadQueryFont (si->dpy, "fixed");
280 f = get_string_resource(si->dpy, "passwd.labelFont", "Dialog.Font");
281 pw->label_font = XLoadQueryFont (si->dpy, (f ? f : "fixed"));
282 if (!pw->label_font) pw->label_font = XLoadQueryFont (si->dpy, "fixed");
285 f = get_string_resource(si->dpy, "passwd.passwdFont", "Dialog.Font");
286 pw->passwd_font = XLoadQueryFont (si->dpy, (f ? f : "fixed"));
287 if (!pw->passwd_font) pw->passwd_font = XLoadQueryFont (si->dpy, "fixed");
290 f = get_string_resource(si->dpy, "passwd.dateFont", "Dialog.Font");
291 pw->date_font = XLoadQueryFont (si->dpy, (f ? f : "fixed"));
292 if (!pw->date_font) pw->date_font = XLoadQueryFont (si->dpy, "fixed");
295 f = get_string_resource(si->dpy, "passwd.unameFont", "Dialog.Font");
296 pw->uname_font = XLoadQueryFont (si->dpy, (f ? f : "fixed"));
297 if (!pw->uname_font) pw->uname_font = XLoadQueryFont (si->dpy, "fixed");
300 pw->show_uname_p = get_boolean_resource(si->dpy, "passwd.uname", "Boolean");
302 pw->foreground = get_pixel_resource (si->dpy, cmap,
304 "Dialog.Foreground" );
305 pw->background = get_pixel_resource (si->dpy, cmap,
307 "Dialog.Background" );
309 if (pw->foreground == pw->background)
311 /* Make sure the error messages show up. */
312 pw->foreground = BlackPixelOfScreen (screen);
313 pw->background = WhitePixelOfScreen (screen);
316 pw->passwd_foreground = get_pixel_resource (si->dpy, cmap,
317 "passwd.text.foreground",
318 "Dialog.Text.Foreground" );
319 pw->passwd_background = get_pixel_resource (si->dpy, cmap,
320 "passwd.text.background",
321 "Dialog.Text.Background" );
322 pw->button_foreground = get_pixel_resource (si->dpy, cmap,
323 "splash.Button.foreground",
324 "Dialog.Button.Foreground" );
325 pw->button_background = get_pixel_resource (si->dpy, cmap,
326 "splash.Button.background",
327 "Dialog.Button.Background" );
328 pw->thermo_foreground = get_pixel_resource (si->dpy, cmap,
329 "passwd.thermometer.foreground",
330 "Dialog.Thermometer.Foreground" );
331 pw->thermo_background = get_pixel_resource ( si->dpy, cmap,
332 "passwd.thermometer.background",
333 "Dialog.Thermometer.Background" );
334 pw->shadow_top = get_pixel_resource ( si->dpy, cmap,
335 "passwd.topShadowColor",
336 "Dialog.Foreground" );
337 pw->shadow_bottom = get_pixel_resource (si->dpy, cmap,
338 "passwd.bottomShadowColor",
339 "Dialog.Background" );
341 pw->preferred_logo_width = get_integer_resource (si->dpy, "passwd.logo.width",
342 "Dialog.Logo.Width");
343 pw->preferred_logo_height = get_integer_resource (si->dpy, "passwd.logo.height",
344 "Dialog.Logo.Height");
345 pw->thermo_width = get_integer_resource (si->dpy, "passwd.thermometer.width",
346 "Dialog.Thermometer.Width");
347 pw->internal_border = get_integer_resource (si->dpy, "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
387 restore these bits. This works, because the running screenhack
388 has already been sent SIGSTOP, so we know nothing else is drawing
393 pw->save_under = XCreatePixmap (si->dpy,
394 pw->prompt_screen->screensaver_window,
395 pw->prompt_screen->width,
396 pw->prompt_screen->height,
397 pw->prompt_screen->current_depth);
398 gcv.function = GXcopy;
399 gc = XCreateGC (si->dpy, pw->save_under, GCFunction, &gcv);
400 XCopyArea (si->dpy, pw->prompt_screen->screensaver_window,
403 pw->prompt_screen->width, pw->prompt_screen->height,
405 XFreeGC (si->dpy, gc);
413 * info_msg and prompt may be NULL.
416 make_passwd_window (saver_info *si,
417 const char *info_msg,
421 XSetWindowAttributes attrs;
422 unsigned long attrmask = 0;
423 passwd_dialog_data *pw;
426 Dimension max_string_width_px;
427 saver_screen_info *ssi = &si->screens [mouse_screen (si)];
429 cleanup_passwd_window (si);
432 new_passwd_window (si);
434 if (!(pw = si->pw_data))
439 pw->prompt_screen = ssi;
440 if (si->prefs.verbose_p)
441 fprintf (stderr, "%s: %d: creating password dialog (\"%s\")\n",
442 blurb(), pw->prompt_screen->number,
443 info_msg ? info_msg : "");
445 screen = pw->prompt_screen->screen;
446 cmap = DefaultColormapOfScreen (screen);
448 pw->echo_input = echo;
450 max_string_width_px = ssi->width
451 - pw->shadow_width * 4
452 - pw->border_width * 2
454 - pw->preferred_logo_width
455 - pw->internal_border * 2;
456 /* As the string wraps it makes the window taller which makes the logo wider
457 * which leaves less room for the text which makes the string wrap. Uh-oh, a
458 * loop. By wrapping at a bit less than the available width, there's some
459 * room for the dialog to grow without going off the edge of the screen. */
460 max_string_width_px *= 0.75;
462 pw->info_label = mlstring_new(info_msg ? info_msg : pw->body_label,
463 pw->label_font, max_string_width_px);
466 int direction, ascent, descent;
472 /* Measure the heading_label. */
473 XTextExtents (pw->heading_font,
474 pw->heading_label, strlen(pw->heading_label),
475 &direction, &ascent, &descent, &overall);
476 if (overall.width > pw->width) pw->width = overall.width;
477 pw->height += ascent + descent;
479 /* Measure the uname_label. */
480 if ((strlen(pw->uname_label)) && pw->show_uname_p)
482 XTextExtents (pw->uname_font,
483 pw->uname_label, strlen(pw->uname_label),
484 &direction, &ascent, &descent, &overall);
485 if (overall.width > pw->width) pw->width = overall.width;
486 pw->height += ascent + descent;
490 Dimension w2 = 0, w3 = 0, button_w = 0;
491 Dimension h2 = 0, h3 = 0, button_h = 0;
492 const char *passwd_string = SAMPLE_INPUT;
494 /* Measure the user_label. */
495 XTextExtents (pw->label_font,
496 pw->user_label, strlen(pw->user_label),
497 &direction, &ascent, &descent, &overall);
498 if (overall.width > w2) w2 = overall.width;
499 h2 += ascent + descent;
501 /* Measure the info_label. */
502 if (pw->info_label->overall_width > pw->width) pw->width = pw->info_label->overall_width;
503 h2 += pw->info_label->overall_height;
505 /* Measure the user string. */
506 XTextExtents (pw->passwd_font,
507 si->user, strlen(si->user),
508 &direction, &ascent, &descent, &overall);
509 overall.width += (pw->shadow_width * 4);
510 ascent += (pw->shadow_width * 4);
511 if (overall.width > w3) w3 = overall.width;
512 h3 += ascent + descent;
514 /* Measure the (dummy) passwd_string. */
517 XTextExtents (pw->passwd_font,
518 passwd_string, strlen(passwd_string),
519 &direction, &ascent, &descent, &overall);
520 overall.width += (pw->shadow_width * 4);
521 ascent += (pw->shadow_width * 4);
522 if (overall.width > w3) w3 = overall.width;
523 h3 += ascent + descent;
525 /* Measure the prompt_label. */
526 max_string_width_px -= w3;
527 pw->prompt_label = mlstring_new(prompt, pw->label_font, max_string_width_px);
529 if (pw->prompt_label->overall_width > w2) w2 = pw->prompt_label->overall_width;
531 h2 += pw->prompt_label->overall_height;
533 w2 = w2 + w3 + (pw->shadow_width * 2);
537 /* The "Unlock" button. */
538 XTextExtents (pw->label_font,
539 pw->unlock_label, strlen(pw->unlock_label),
540 &direction, &ascent, &descent, &overall);
541 button_w = overall.width;
542 button_h = ascent + descent;
544 /* Add some horizontal padding inside the button. */
547 button_w += ((ascent + descent) / 2) + (pw->shadow_width * 2);
548 button_h += ((ascent + descent) / 2) + (pw->shadow_width * 2);
550 pw->unlock_button_width = button_w;
551 pw->unlock_button_height = button_h;
553 w2 = MAX (w2, button_w);
554 h2 += button_h * 1.5;
556 /* The "New Login" button */
557 pw->login_button_width = 0;
558 pw->login_button_height = 0;
560 if (pw->login_button_p)
562 pw->login_button_enabled_p = True;
564 /* Measure the "New Login" button */
565 XTextExtents (pw->button_font, pw->login_label,
566 strlen (pw->login_label),
567 &direction, &ascent, &descent, &overall);
568 button_w = overall.width;
569 button_h = ascent + descent;
571 /* Add some horizontal padding inside the buttons. */
574 button_w += ((ascent + descent) / 2) + (pw->shadow_width * 2);
575 button_h += ((ascent + descent) / 2) + (pw->shadow_width * 2);
577 pw->login_button_width = button_w;
578 pw->login_button_height = button_h;
580 if (button_h > pw->unlock_button_height)
581 h2 += (button_h * 1.5 - pw->unlock_button_height * 1.5);
583 /* Use (2 * shadow_width) spacing between the buttons. Another
584 (2 * shadow_width) is required to account for button shadows. */
585 w2 = MAX (w2, button_w + pw->unlock_button_width + (pw->shadow_width * 4));
588 if (w2 > pw->width) pw->width = w2;
592 pw->width += (pw->internal_border * 2);
593 pw->height += (pw->internal_border * 4);
595 pw->width += pw->thermo_width + (pw->shadow_width * 3);
597 if (pw->preferred_logo_height > pw->height)
598 pw->height = pw->logo_height = pw->preferred_logo_height;
599 else if (pw->height > pw->preferred_logo_height)
600 pw->logo_height = pw->height;
602 pw->logo_width = pw->logo_height;
604 pw->width += pw->logo_width;
607 attrmask |= CWOverrideRedirect; attrs.override_redirect = True;
609 attrmask |= CWEventMask;
610 attrs.event_mask = (ExposureMask | KeyPressMask |
611 ButtonPressMask | ButtonReleaseMask);
613 /* Figure out where on the desktop to place the window so that it will
614 actually be visible; this takes into account virtual viewports as
618 get_screen_viewport (pw->prompt_screen, &x, &y, &w, &h,
619 pw->previous_mouse_x, pw->previous_mouse_y,
620 si->prefs.verbose_p);
621 if (si->prefs.debug_p) w /= 2;
622 pw->x = x + ((w + pw->width) / 2) - pw->width;
623 pw->y = y + ((h + pw->height) / 2) - pw->height;
624 if (pw->x < x) pw->x = x;
625 if (pw->y < y) pw->y = y;
628 pw->border_width = get_integer_resource (si->dpy, "passwd.borderWidth",
629 "Dialog.BorderWidth");
631 /* Only create the window the first time around */
632 if (!si->passwd_dialog)
635 XCreateWindow (si->dpy,
636 RootWindowOfScreen(screen),
637 pw->x, pw->y, pw->width, pw->height, pw->border_width,
638 DefaultDepthOfScreen (screen), InputOutput,
639 DefaultVisualOfScreen(screen),
641 XSetWindowBackground (si->dpy, si->passwd_dialog, pw->background);
643 /* We use the default visual, not ssi->visual, so that the logo pixmap's
644 visual matches that of the si->passwd_dialog window. */
645 pw->logo_pixmap = xscreensaver_logo (ssi->screen,
646 /* ssi->current_visual, */
647 DefaultVisualOfScreen(screen),
648 si->passwd_dialog, cmap,
650 &pw->logo_pixels, &pw->logo_npixels,
651 &pw->logo_clipmask, True);
653 else /* On successive prompts, just resize the window */
656 unsigned int mask = CWX | CWY | CWWidth | CWHeight;
660 wc.width = pw->width;
661 wc.height = pw->height;
663 XConfigureWindow (si->dpy, si->passwd_dialog, mask, &wc);
666 restore_background(si);
668 XMapRaised (si->dpy, si->passwd_dialog);
669 XSync (si->dpy, False);
671 move_mouse_grab (si, si->passwd_dialog,
673 pw->prompt_screen->number);
679 XInstallColormap (si->dpy, cmap);
680 draw_passwd_window (si);
685 draw_passwd_window (saver_info *si)
687 passwd_dialog_data *pw = si->pw_data;
691 int x1, x2, x3, y1, y2;
696 pw->passwd_changed_p = True;
697 pw->button_state_changed_p = True;
699 /* This height is the height of all the elements, not to be confused with
700 * the overall window height which is pw->height. It is used to compute
701 * the amount of spacing (padding) between elements. */
702 height = (pw->heading_font->ascent + pw->heading_font->descent +
703 pw->info_label->overall_height +
704 MAX (((pw->label_font->ascent + pw->label_font->descent) +
705 (pw->prompt_label ? pw->prompt_label->overall_height : 0)),
706 ((pw->passwd_font->ascent + pw->passwd_font->descent) +
707 (pw->shadow_width * 2)) * (pw->prompt_label ? 2 : 1)) +
708 pw->date_font->ascent + pw->date_font->descent);
710 if ((strlen(pw->uname_label)) && pw->show_uname_p)
711 height += (pw->uname_font->ascent + pw->uname_font->descent); /* for uname */
713 height += ((pw->button_font->ascent + pw->button_font->descent) * 2 +
714 2 * pw->shadow_width);
716 spacing = ((pw->height - 2 * pw->shadow_width
717 - pw->internal_border - height)
720 if (spacing < 0) spacing = 0;
722 gcv.foreground = pw->foreground;
723 gc1 = XCreateGC (si->dpy, si->passwd_dialog, GCForeground, &gcv);
724 gc2 = XCreateGC (si->dpy, si->passwd_dialog, GCForeground, &gcv);
725 x1 = pw->logo_width + pw->thermo_width + (pw->shadow_width * 3);
726 x3 = pw->width - (pw->shadow_width * 2);
727 y1 = (pw->shadow_width * 2) + spacing + spacing;
731 XSetFont (si->dpy, gc1, pw->heading_font->fid);
732 sw = string_width (pw->heading_font, pw->heading_label);
733 x2 = (x1 + ((x3 - x1 - sw) / 2));
734 y1 += spacing + pw->heading_font->ascent + pw->heading_font->descent;
735 XDrawString (si->dpy, si->passwd_dialog, gc1, x2, y1,
736 pw->heading_label, strlen(pw->heading_label));
738 /* uname below top heading
740 if ((strlen(pw->uname_label)) && pw->show_uname_p)
742 XSetFont (si->dpy, gc1, pw->uname_font->fid);
743 y1 += spacing + pw->uname_font->ascent + pw->uname_font->descent;
744 sw = string_width (pw->uname_font, pw->uname_label);
745 x2 = (x1 + ((x3 - x1 - sw) / 2));
746 XDrawString (si->dpy, si->passwd_dialog, gc1, x2, y1,
747 pw->uname_label, strlen(pw->uname_label));
750 /* the info_label (below uname)
752 x2 = (x1 + ((x3 - x1 - pw->info_label->overall_width) / 2));
753 y1 += spacing + pw->info_label->font_height / 2;
754 mlstring_draw(si->dpy, si->passwd_dialog, gc1, pw->info_label,
756 y1 += pw->info_label->overall_height;
759 tb_height = (pw->passwd_font->ascent + pw->passwd_font->descent +
760 (pw->shadow_width * 4));
762 /* the "User:" prompt
765 XSetForeground (si->dpy, gc1, pw->foreground);
766 XSetFont (si->dpy, gc1, pw->label_font->fid);
767 y1 += (spacing + tb_height + pw->shadow_width);
768 x2 = (x1 + pw->internal_border +
769 MAX(string_width (pw->label_font, pw->user_label),
770 pw->prompt_label ? pw->prompt_label->overall_width : 0));
771 XDrawString (si->dpy, si->passwd_dialog, gc1,
772 x2 - string_width (pw->label_font, pw->user_label),
773 y1 - pw->passwd_font->descent,
774 pw->user_label, strlen(pw->user_label));
776 /* the prompt_label prompt
778 if (pw->prompt_label)
780 y1 += tb_height - pw->label_font->ascent + pw->shadow_width;
781 mlstring_draw(si->dpy, si->passwd_dialog, gc1, pw->prompt_label,
782 x2 - pw->prompt_label->overall_width, y1);
785 /* the "user name" text field
788 XSetForeground (si->dpy, gc1, pw->passwd_foreground);
789 XSetForeground (si->dpy, gc2, pw->passwd_background);
790 XSetFont (si->dpy, gc1, pw->passwd_font->fid);
791 y1 += (spacing + tb_height);
792 x2 += (pw->shadow_width * 4);
794 pw->passwd_field_width = x3 - x2 - pw->internal_border;
795 pw->passwd_field_height = (pw->passwd_font->ascent +
796 pw->passwd_font->descent +
799 XFillRectangle (si->dpy, si->passwd_dialog, gc2,
800 x2 - pw->shadow_width,
801 y1 - (pw->passwd_font->ascent + pw->passwd_font->descent),
802 pw->passwd_field_width, pw->passwd_field_height);
803 XDrawString (si->dpy, si->passwd_dialog, gc1,
805 y1 - pw->passwd_font->descent,
806 si->user, strlen(si->user));
808 /* the password/prompt text field
810 if (pw->prompt_label)
812 y1 += (spacing + pw->prompt_label->overall_height + pw->shadow_width * 2);
814 pw->passwd_field_x = x2 - pw->shadow_width;
815 pw->passwd_field_y = y1 - (pw->passwd_font->ascent +
816 pw->passwd_font->descent);
819 /* The shadow around the text fields
822 y1 += (spacing + (pw->shadow_width * 3));
823 x1 = x2 - (pw->shadow_width * 2);
824 x2 = pw->passwd_field_width + (pw->shadow_width * 2);
825 y2 = pw->passwd_field_height + (pw->shadow_width * 2);
827 draw_shaded_rectangle (si->dpy, si->passwd_dialog,
830 pw->shadow_bottom, pw->shadow_top);
832 if (pw->prompt_label)
834 y1 += (spacing + pw->prompt_label->overall_height + pw->shadow_width * 2);
835 draw_shaded_rectangle (si->dpy, si->passwd_dialog,
838 pw->shadow_bottom, pw->shadow_top);
842 /* The date, below the text fields
846 time_t now = time ((time_t *) 0);
847 struct tm *tm = localtime (&now);
848 memset (buf, 0, sizeof(buf));
849 strftime (buf, sizeof(buf)-1, pw->date_label, tm);
851 XSetFont (si->dpy, gc1, pw->date_font->fid);
852 y1 += pw->shadow_width;
853 y1 += (spacing + tb_height);
855 sw = string_width (pw->date_font, buf);
857 XDrawString (si->dpy, si->passwd_dialog, gc1, x2, y1, buf, strlen(buf));
860 /* Set up the GCs for the "New Login" and "Unlock" buttons.
862 XSetForeground(si->dpy, gc1, pw->button_foreground);
863 XSetForeground(si->dpy, gc2, pw->button_background);
864 XSetFont(si->dpy, gc1, pw->button_font->fid);
866 /* The "Unlock" button */
867 x2 = pw->width - pw->internal_border - (pw->shadow_width * 2);
869 /* right aligned button */
870 x1 = x2 - pw->unlock_button_width;
872 /* Add half the difference between y1 and the internal edge.
873 * It actually looks better if the internal border is ignored. */
874 y1 += ((pw->height - MAX (pw->unlock_button_height, pw->login_button_height)
878 pw->unlock_button_x = x1;
879 pw->unlock_button_y = y1;
881 /* The "New Login" button
883 if (pw->login_button_p)
885 /* Using the same GC as for the Unlock button */
887 sw = string_width (pw->button_font, pw->login_label);
889 /* left aligned button */
890 x1 = (pw->logo_width + pw->thermo_width + (pw->shadow_width * 3) +
891 pw->internal_border);
893 pw->login_button_x = x1;
894 pw->login_button_y = y1;
899 x1 = pw->shadow_width * 6;
900 y1 = pw->shadow_width * 6;
901 x2 = pw->logo_width - (pw->shadow_width * 12);
902 y2 = pw->logo_height - (pw->shadow_width * 12);
908 unsigned int w, h, bw, d;
909 XGetGeometry (si->dpy, pw->logo_pixmap, &root, &x, &y, &w, &h, &bw, &d);
910 XSetForeground (si->dpy, gc1, pw->foreground);
911 XSetBackground (si->dpy, gc1, pw->background);
912 XSetClipMask (si->dpy, gc1, pw->logo_clipmask);
913 XSetClipOrigin (si->dpy, gc1, x1 + ((x2 - (int)w) / 2), y1 + ((y2 - (int)h) / 2));
915 XCopyPlane (si->dpy, pw->logo_pixmap, si->passwd_dialog, gc1,
917 x1 + ((x2 - (int)w) / 2),
918 y1 + ((y2 - (int)h) / 2),
921 XCopyArea (si->dpy, pw->logo_pixmap, si->passwd_dialog, gc1,
923 x1 + ((x2 - (int)w) / 2),
924 y1 + ((y2 - (int)h) / 2));
929 XSetForeground (si->dpy, gc1, pw->thermo_foreground);
930 XSetForeground (si->dpy, gc2, pw->thermo_background);
932 pw->thermo_field_x = pw->logo_width + pw->shadow_width;
933 pw->thermo_field_y = pw->shadow_width * 5;
934 pw->thermo_field_height = pw->height - (pw->shadow_width * 10);
937 /* Solid border inside the logo box. */
938 XSetForeground (si->dpy, gc1, pw->foreground);
939 XDrawRectangle (si->dpy, si->passwd_dialog, gc1, x1, y1, x2-1, y2-1);
942 /* The shadow around the logo
944 draw_shaded_rectangle (si->dpy, si->passwd_dialog,
945 pw->shadow_width * 4,
946 pw->shadow_width * 4,
947 pw->logo_width - (pw->shadow_width * 8),
948 pw->logo_height - (pw->shadow_width * 8),
950 pw->shadow_bottom, pw->shadow_top);
952 /* The shadow around the thermometer
954 draw_shaded_rectangle (si->dpy, si->passwd_dialog,
956 pw->shadow_width * 4,
957 pw->thermo_width + (pw->shadow_width * 2),
958 pw->height - (pw->shadow_width * 8),
960 pw->shadow_bottom, pw->shadow_top);
963 /* Solid border inside the thermometer. */
964 XSetForeground (si->dpy, gc1, pw->foreground);
965 XDrawRectangle (si->dpy, si->passwd_dialog, gc1,
966 pw->thermo_field_x, pw->thermo_field_y,
967 pw->thermo_width - 1, pw->thermo_field_height - 1);
970 /* The shadow around the whole window
972 draw_shaded_rectangle (si->dpy, si->passwd_dialog,
973 0, 0, pw->width, pw->height, pw->shadow_width,
974 pw->shadow_top, pw->shadow_bottom);
976 XFreeGC (si->dpy, gc1);
977 XFreeGC (si->dpy, gc2);
979 update_passwd_window (si, pw->passwd_string, pw->ratio);
983 draw_button(Display *dpy,
986 unsigned long foreground, unsigned long background,
989 int width, int height,
991 Pixel shadow_light, Pixel shadow_dark,
997 int label_x, label_y;
999 gcv.foreground = foreground;
1000 gcv.font = font->fid;
1001 gc1 = XCreateGC(dpy, dialog, GCForeground|GCFont, &gcv);
1002 gcv.foreground = background;
1003 gc2 = XCreateGC(dpy, dialog, GCForeground, &gcv);
1005 XFillRectangle(dpy, dialog, gc2,
1006 x, y, width, height);
1008 sw = string_width(font, label);
1010 label_x = x + ((width - sw) / 2);
1011 label_y = (y + (height - (font->ascent + font->descent)) / 2 + font->ascent);
1019 XDrawString(dpy, dialog, gc1, label_x, label_y, label, strlen(label));
1024 draw_shaded_rectangle(dpy, dialog, x, y, width, height,
1025 shadow_width, shadow_light, shadow_dark);
1029 update_passwd_window (saver_info *si, const char *printed_passwd, float ratio)
1031 passwd_dialog_data *pw = si->pw_data;
1035 XRectangle rects[1];
1038 gcv.foreground = pw->passwd_foreground;
1039 gcv.font = pw->passwd_font->fid;
1040 gc1 = XCreateGC (si->dpy, si->passwd_dialog, GCForeground|GCFont, &gcv);
1041 gcv.foreground = pw->passwd_background;
1042 gc2 = XCreateGC (si->dpy, si->passwd_dialog, GCForeground, &gcv);
1046 char *s = strdup (printed_passwd);
1047 if (pw->passwd_string) free (pw->passwd_string);
1048 pw->passwd_string = s;
1051 if (pw->prompt_label)
1054 /* the "password" text field
1056 rects[0].x = pw->passwd_field_x;
1057 rects[0].y = pw->passwd_field_y;
1058 rects[0].width = pw->passwd_field_width;
1059 rects[0].height = pw->passwd_field_height;
1061 /* The user entry (password) field is double buffered.
1062 * This avoids flickering, particularly in synchronous mode. */
1064 if (pw->passwd_changed_p)
1066 pw->passwd_changed_p = False;
1068 if (pw->user_entry_pixmap)
1070 XFreePixmap(si->dpy, pw->user_entry_pixmap);
1071 pw->user_entry_pixmap = 0;
1074 pw->user_entry_pixmap =
1075 XCreatePixmap (si->dpy, si->passwd_dialog,
1076 rects[0].width, rects[0].height,
1077 DefaultDepthOfScreen (pw->prompt_screen->screen));
1079 XFillRectangle (si->dpy, pw->user_entry_pixmap, gc2,
1080 0, 0, rects[0].width, rects[0].height);
1082 XDrawString (si->dpy, pw->user_entry_pixmap, gc1,
1084 pw->passwd_font->ascent,
1085 pw->passwd_string, strlen(pw->passwd_string));
1087 /* Ensure the new pixmap gets copied to the window */
1094 if (pw->i_beam == 0)
1096 /* Make the I-beam disappear */
1097 XCopyArea(si->dpy, pw->user_entry_pixmap, si->passwd_dialog, gc2,
1098 0, 0, rects[0].width, rects[0].height,
1099 rects[0].x, rects[0].y);
1101 else if (pw->i_beam == 1)
1103 /* Make the I-beam appear */
1104 x = (rects[0].x + pw->shadow_width +
1105 string_width (pw->passwd_font, pw->passwd_string));
1106 y = rects[0].y + pw->shadow_width;
1108 if (x > rects[0].x + rects[0].width - 1)
1109 x = rects[0].x + rects[0].width - 1;
1110 XDrawLine (si->dpy, si->passwd_dialog, gc1,
1112 x, y + pw->passwd_font->ascent + pw->passwd_font->descent-1);
1115 pw->i_beam = (pw->i_beam + 1) % 4;
1121 y = (pw->thermo_field_height - 2) * (1.0 - pw->ratio);
1124 XFillRectangle (si->dpy, si->passwd_dialog, gc2,
1125 pw->thermo_field_x + 1,
1126 pw->thermo_field_y + 1,
1129 XSetForeground (si->dpy, gc1, pw->thermo_foreground);
1130 XFillRectangle (si->dpy, si->passwd_dialog, gc1,
1131 pw->thermo_field_x + 1,
1132 pw->thermo_field_y + 1 + y,
1134 MAX (0, pw->thermo_field_height - y - 2));
1137 if (pw->button_state_changed_p)
1139 pw->button_state_changed_p = False;
1141 /* The "Unlock" button
1143 draw_button(si->dpy, si->passwd_dialog, pw->button_font,
1144 pw->button_foreground, pw->button_background,
1146 pw->unlock_button_x, pw->unlock_button_y,
1147 pw->unlock_button_width, pw->unlock_button_height,
1149 (pw->unlock_button_down_p ? pw->shadow_bottom : pw->shadow_top),
1150 (pw->unlock_button_down_p ? pw->shadow_top : pw->shadow_bottom),
1151 pw->unlock_button_down_p);
1153 /* The "New Login" button
1155 if (pw->login_button_p)
1157 draw_button(si->dpy, si->passwd_dialog, pw->button_font,
1158 (pw->login_button_enabled_p
1159 ? pw->passwd_foreground
1160 : pw->shadow_bottom),
1161 pw->button_background,
1163 pw->login_button_x, pw->login_button_y,
1164 pw->login_button_width, pw->login_button_height,
1166 (pw->login_button_down_p
1169 (pw->login_button_down_p
1171 : pw->shadow_bottom),
1172 pw->login_button_down_p);
1176 XFreeGC (si->dpy, gc1);
1177 XFreeGC (si->dpy, gc2);
1178 XSync (si->dpy, False);
1183 restore_background (saver_info *si)
1185 passwd_dialog_data *pw = si->pw_data;
1186 saver_screen_info *ssi = pw->prompt_screen;
1190 gcv.function = GXcopy;
1192 gc = XCreateGC (si->dpy, ssi->screensaver_window, GCFunction, &gcv);
1194 XCopyArea (si->dpy, pw->save_under,
1195 ssi->screensaver_window, gc,
1197 ssi->width, ssi->height,
1200 XFreeGC (si->dpy, gc);
1204 /* Frees anything created by make_passwd_window */
1206 cleanup_passwd_window (saver_info *si)
1208 passwd_dialog_data *pw;
1210 if (!(pw = si->pw_data))
1215 mlstring_free(pw->info_label);
1219 if (pw->prompt_label)
1221 mlstring_free(pw->prompt_label);
1222 pw->prompt_label = 0;
1225 memset (pw->typed_passwd, 0, sizeof(pw->typed_passwd));
1226 memset (pw->passwd_string, 0, strlen(pw->passwd_string));
1230 XtRemoveTimeOut (pw->timer);
1234 if (pw->user_entry_pixmap)
1236 XFreePixmap(si->dpy, pw->user_entry_pixmap);
1237 pw->user_entry_pixmap = 0;
1243 destroy_passwd_window (saver_info *si)
1245 saver_preferences *p = &si->prefs;
1246 passwd_dialog_data *pw = si->pw_data;
1247 saver_screen_info *ssi = pw->prompt_screen;
1248 Colormap cmap = DefaultColormapOfScreen (ssi->screen);
1249 Pixel black = BlackPixelOfScreen (ssi->screen);
1250 Pixel white = WhitePixelOfScreen (ssi->screen);
1253 cleanup_passwd_window (si);
1255 if (si->cached_passwd)
1257 char *wipe = si->cached_passwd;
1262 free(si->cached_passwd);
1263 si->cached_passwd = NULL;
1266 move_mouse_grab (si, RootWindowOfScreen (ssi->screen),
1267 ssi->cursor, ssi->number);
1269 if (pw->passwd_cursor)
1270 XFreeCursor (si->dpy, pw->passwd_cursor);
1273 fprintf (stderr, "%s: %d: moving mouse back to %d,%d.\n",
1274 blurb(), ssi->number,
1275 pw->previous_mouse_x, pw->previous_mouse_y);
1277 XWarpPointer (si->dpy, None, RootWindowOfScreen (ssi->screen),
1279 pw->previous_mouse_x, pw->previous_mouse_y);
1281 while (XCheckMaskEvent (si->dpy, PointerMotionMask, &event))
1283 fprintf (stderr, "%s: discarding MotionNotify event.\n", blurb());
1285 if (si->passwd_dialog)
1287 if (si->prefs.verbose_p)
1288 fprintf (stderr, "%s: %d: destroying password dialog.\n",
1289 blurb(), pw->prompt_screen->number);
1291 XDestroyWindow (si->dpy, si->passwd_dialog);
1292 si->passwd_dialog = 0;
1297 restore_background(si);
1298 XFreePixmap (si->dpy, pw->save_under);
1302 if (pw->heading_label) free (pw->heading_label);
1303 if (pw->body_label) free (pw->body_label);
1304 if (pw->user_label) free (pw->user_label);
1305 if (pw->date_label) free (pw->date_label);
1306 if (pw->login_label) free (pw->login_label);
1307 if (pw->unlock_label) free (pw->unlock_label);
1308 if (pw->passwd_string) free (pw->passwd_string);
1309 if (pw->uname_label) free (pw->uname_label);
1311 if (pw->heading_font) XFreeFont (si->dpy, pw->heading_font);
1312 if (pw->body_font) XFreeFont (si->dpy, pw->body_font);
1313 if (pw->label_font) XFreeFont (si->dpy, pw->label_font);
1314 if (pw->passwd_font) XFreeFont (si->dpy, pw->passwd_font);
1315 if (pw->date_font) XFreeFont (si->dpy, pw->date_font);
1316 if (pw->button_font) XFreeFont (si->dpy, pw->button_font);
1317 if (pw->uname_font) XFreeFont (si->dpy, pw->uname_font);
1319 if (pw->foreground != black && pw->foreground != white)
1320 XFreeColors (si->dpy, cmap, &pw->foreground, 1, 0L);
1321 if (pw->background != black && pw->background != white)
1322 XFreeColors (si->dpy, cmap, &pw->background, 1, 0L);
1323 if (!(pw->button_foreground == black || pw->button_foreground == white))
1324 XFreeColors (si->dpy, cmap, &pw->button_foreground, 1, 0L);
1325 if (!(pw->button_background == black || pw->button_background == white))
1326 XFreeColors (si->dpy, cmap, &pw->button_background, 1, 0L);
1327 if (pw->passwd_foreground != black && pw->passwd_foreground != white)
1328 XFreeColors (si->dpy, cmap, &pw->passwd_foreground, 1, 0L);
1329 if (pw->passwd_background != black && pw->passwd_background != white)
1330 XFreeColors (si->dpy, cmap, &pw->passwd_background, 1, 0L);
1331 if (pw->thermo_foreground != black && pw->thermo_foreground != white)
1332 XFreeColors (si->dpy, cmap, &pw->thermo_foreground, 1, 0L);
1333 if (pw->thermo_background != black && pw->thermo_background != white)
1334 XFreeColors (si->dpy, cmap, &pw->thermo_background, 1, 0L);
1335 if (pw->shadow_top != black && pw->shadow_top != white)
1336 XFreeColors (si->dpy, cmap, &pw->shadow_top, 1, 0L);
1337 if (pw->shadow_bottom != black && pw->shadow_bottom != white)
1338 XFreeColors (si->dpy, cmap, &pw->shadow_bottom, 1, 0L);
1340 if (pw->logo_pixmap)
1341 XFreePixmap (si->dpy, pw->logo_pixmap);
1342 if (pw-> logo_clipmask)
1343 XFreePixmap (si->dpy, pw->logo_clipmask);
1344 if (pw->logo_pixels)
1346 if (pw->logo_npixels)
1347 XFreeColors (si->dpy, cmap, pw->logo_pixels, pw->logo_npixels, 0L);
1348 free (pw->logo_pixels);
1349 pw->logo_pixels = 0;
1350 pw->logo_npixels = 0;
1354 XFreePixmap (si->dpy, pw->save_under);
1357 XInstallColormap (si->dpy, cmap);
1359 memset (pw, 0, sizeof(*pw));
1365 static Bool error_handler_hit_p = False;
1368 ignore_all_errors_ehandler (Display *dpy, XErrorEvent *error)
1370 error_handler_hit_p = True;
1375 #ifdef HAVE_XHPDISABLERESET
1376 /* This function enables and disables the C-Sh-Reset hot-key, which
1377 normally resets the X server (logging out the logged-in user.)
1378 We don't want random people to be able to do that while the
1382 hp_lock_reset (saver_info *si, Bool lock_p)
1384 static Bool hp_locked_p = False;
1386 /* Calls to XHPDisableReset and XHPEnableReset must be balanced,
1387 or BadAccess errors occur. (It's ok for this to be global,
1388 since it affects the whole machine, not just the current screen.)
1390 if (hp_locked_p == lock_p)
1394 XHPDisableReset (si->dpy);
1396 XHPEnableReset (si->dpy);
1397 hp_locked_p = lock_p;
1399 #endif /* HAVE_XHPDISABLERESET */
1402 #ifdef HAVE_XF86MISCSETGRABKEYSSTATE
1404 /* This function enables and disables the Ctrl-Alt-KP_star and
1405 Ctrl-Alt-KP_slash hot-keys, which (in XFree86 4.2) break any
1406 grabs and/or kill the grabbing client. That would effectively
1407 unlock the screen, so we don't like that.
1409 The Ctrl-Alt-KP_star and Ctrl-Alt-KP_slash hot-keys only exist
1410 if AllowDeactivateGrabs and/or AllowClosedownGrabs are turned on
1411 in XF86Config. I believe they are disabled by default.
1413 This does not affect any other keys (specifically Ctrl-Alt-BS or
1414 Ctrl-Alt-F1) but I wish it did. Maybe it will someday.
1417 xfree_lock_grab_smasher (saver_info *si, Bool lock_p)
1419 saver_preferences *p = &si->prefs;
1422 XErrorHandler old_handler;
1423 XSync (si->dpy, False);
1424 error_handler_hit_p = False;
1425 old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1426 XSync (si->dpy, False);
1427 status = XF86MiscSetGrabKeysState (si->dpy, !lock_p);
1428 XSync (si->dpy, False);
1429 if (error_handler_hit_p) status = 666;
1431 if (!lock_p && status == MiscExtGrabStateAlready)
1432 status = MiscExtGrabStateSuccess; /* shut up, consider this success */
1434 if (p->verbose_p && status != MiscExtGrabStateSuccess)
1435 fprintf (stderr, "%s: error: XF86MiscSetGrabKeysState(%d) returned %s\n",
1437 (status == MiscExtGrabStateSuccess ? "MiscExtGrabStateSuccess" :
1438 status == MiscExtGrabStateLocked ? "MiscExtGrabStateLocked" :
1439 status == MiscExtGrabStateAlready ? "MiscExtGrabStateAlready" :
1440 status == 666 ? "an X error" :
1443 XSync (si->dpy, False);
1444 XSetErrorHandler (old_handler);
1445 XSync (si->dpy, False);
1447 #endif /* HAVE_XF86MISCSETGRABKEYSSTATE */
1451 /* This function enables and disables the C-Alt-Plus and C-Alt-Minus
1452 hot-keys, which normally change the resolution of the X server.
1453 We don't want people to be able to switch the server resolution
1454 while the screen is locked, because if they switch to a higher
1455 resolution, it could cause part of the underlying desktop to become
1458 #ifdef HAVE_XF86VMODE
1461 xfree_lock_mode_switch (saver_info *si, Bool lock_p)
1463 static Bool any_mode_locked_p = False;
1464 saver_preferences *p = &si->prefs;
1468 XErrorHandler old_handler;
1470 if (any_mode_locked_p == lock_p)
1472 if (!XF86VidModeQueryExtension (si->dpy, &event, &error))
1475 for (screen = 0; screen < (si->xinerama_p ? 1 : si->nscreens); screen++)
1477 XSync (si->dpy, False);
1478 old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1479 error_handler_hit_p = False;
1480 status = XF86VidModeLockModeSwitch (si->dpy, screen, lock_p);
1481 XSync (si->dpy, False);
1482 XSetErrorHandler (old_handler);
1483 if (error_handler_hit_p) status = False;
1486 any_mode_locked_p = lock_p;
1488 if (!status && (p->verbose_p || !lock_p))
1489 /* Only print this when verbose, or when we locked but can't unlock.
1490 I tried printing this message whenever it comes up, but
1491 mode-locking always fails if DontZoom is set in XF86Config. */
1492 fprintf (stderr, "%s: %d: unable to %s mode switching!\n",
1493 blurb(), screen, (lock_p ? "lock" : "unlock"));
1494 else if (p->verbose_p)
1495 fprintf (stderr, "%s: %d: %s mode switching.\n",
1496 blurb(), screen, (lock_p ? "locked" : "unlocked"));
1499 #endif /* HAVE_XF86VMODE */
1502 /* If the viewport has been scrolled since the screen was blanked,
1503 then scroll it back to where it belongs. This function only exists
1504 to patch over a very brief race condition.
1507 undo_vp_motion (saver_info *si)
1509 #ifdef HAVE_XF86VMODE
1510 saver_preferences *p = &si->prefs;
1514 if (!XF86VidModeQueryExtension (si->dpy, &event, &error))
1517 for (screen = 0; screen < si->nscreens; screen++)
1519 saver_screen_info *ssi = &si->screens[screen];
1523 if (ssi->blank_vp_x == -1 && ssi->blank_vp_y == -1)
1525 if (!XF86VidModeGetViewPort (si->dpy, screen, &x, &y))
1527 if (ssi->blank_vp_x == x && ssi->blank_vp_y == y)
1530 /* We're going to move the viewport. The mouse has just been grabbed on
1531 (and constrained to, thus warped to) the password window, so it is no
1532 longer near the edge of the screen. However, wait a bit anyway, just
1533 to make sure the server drains its last motion event, so that the
1534 screen doesn't continue to scroll after we've reset the viewport.
1536 XSync (si->dpy, False);
1537 usleep (250000); /* 1/4 second */
1538 XSync (si->dpy, False);
1540 status = XF86VidModeSetViewPort (si->dpy, screen,
1541 ssi->blank_vp_x, ssi->blank_vp_y);
1545 "%s: %d: unable to move vp from (%d,%d) back to (%d,%d)!\n",
1546 blurb(), screen, x, y, ssi->blank_vp_x, ssi->blank_vp_y);
1547 else if (p->verbose_p)
1549 "%s: %d: vp moved to (%d,%d); moved it back to (%d,%d).\n",
1550 blurb(), screen, x, y, ssi->blank_vp_x, ssi->blank_vp_y);
1552 #endif /* HAVE_XF86VMODE */
1561 passwd_animate_timer (XtPointer closure, XtIntervalId *id)
1563 saver_info *si = (saver_info *) closure;
1565 passwd_dialog_data *pw = si->pw_data;
1569 pw->ratio -= (1.0 / ((double) si->prefs.passwd_timeout / (double) tick));
1573 if (si->unlock_state == ul_read)
1574 si->unlock_state = ul_time;
1577 update_passwd_window (si, 0, pw->ratio);
1579 if (si->unlock_state == ul_read)
1580 pw->timer = XtAppAddTimeOut (si->app, tick, passwd_animate_timer,
1585 idle_timer ((XtPointer) si, 0);
1589 static XComposeStatus *compose_status;
1592 handle_login_button (saver_info *si, XEvent *event)
1594 saver_preferences *p = &si->prefs;
1595 Bool mouse_in_box = False;
1597 passwd_dialog_data *pw = si->pw_data;
1598 saver_screen_info *ssi = pw->prompt_screen;
1600 if (! pw->login_button_enabled_p)
1604 (event->xbutton.x >= pw->login_button_x &&
1605 event->xbutton.x <= pw->login_button_x + pw->login_button_width &&
1606 event->xbutton.y >= pw->login_button_y &&
1607 event->xbutton.y <= pw->login_button_y + pw->login_button_height);
1609 if (ButtonRelease == event->xany.type &&
1610 pw->login_button_down_p &&
1613 /* Only allow them to press the button once: don't want to
1614 accidentally launch a dozen gdm choosers if the machine
1618 pw->login_button_enabled_p = False;
1621 pw->login_button_down_p = (mouse_in_box &&
1622 ButtonRelease != event->xany.type);
1624 update_passwd_window (si, 0, pw->ratio);
1627 fork_and_exec (ssi, p->new_login_command);
1632 handle_unlock_button (saver_info *si, XEvent *event)
1634 Bool mouse_in_box = False;
1635 passwd_dialog_data *pw = si->pw_data;
1638 (event->xbutton.x >= pw->unlock_button_x &&
1639 event->xbutton.x <= pw->unlock_button_x + pw->unlock_button_width &&
1640 event->xbutton.y >= pw->unlock_button_y &&
1641 event->xbutton.y <= pw->unlock_button_y + pw->unlock_button_height);
1643 if (ButtonRelease == event->xany.type &&
1644 pw->unlock_button_down_p &&
1646 finished_typing_passwd (si, pw);
1648 pw->unlock_button_down_p = (mouse_in_box &&
1649 ButtonRelease != event->xany.type);
1654 finished_typing_passwd (saver_info *si, passwd_dialog_data *pw)
1656 if (si->unlock_state == ul_read)
1658 update_passwd_window (si, "Checking...", pw->ratio);
1659 XSync (si->dpy, False);
1661 si->unlock_state = ul_finished;
1662 update_passwd_window (si, "", pw->ratio);
1667 handle_passwd_key (saver_info *si, XKeyEvent *event)
1669 passwd_dialog_data *pw = si->pw_data;
1670 int pw_size = sizeof (pw->typed_passwd) - 1;
1671 char *typed_passwd = pw->typed_passwd;
1675 int size = XLookupString (event, s, 1, 0, compose_status);
1677 if (size != 1) return;
1681 pw->passwd_changed_p = True;
1683 /* Add 10% to the time remaining every time a key is pressed. */
1685 if (pw->ratio > 1) pw->ratio = 1;
1689 case '\010': case '\177': /* Backspace */
1693 typed_passwd [strlen(typed_passwd)-1] = 0;
1696 case '\025': case '\030': /* Erase line */
1697 memset (typed_passwd, 0, pw_size);
1700 case '\012': case '\015': /* Enter */
1701 finished_typing_passwd(si, pw);
1704 case '\033': /* Escape */
1705 si->unlock_state = ul_cancel;
1709 /* Though technically the only illegal characters in Unix passwords
1710 are LF and NUL, most GUI programs (e.g., GDM) use regular text-entry
1711 fields that only let you type printable characters. So, people
1712 who use funky characters in their passwords are already broken.
1713 We follow that precedent.
1715 if (isprint ((unsigned char) *s))
1717 i = strlen (typed_passwd);
1722 typed_passwd [i] = *s;
1723 typed_passwd [i+1] = 0;
1733 /* If the input is wider than the text box, only show the last portion.
1734 * This simulates a horizontally scrolling text field. */
1735 int chars_in_pwfield = (pw->passwd_field_width /
1736 pw->passwd_font->max_bounds.width);
1738 if (strlen(typed_passwd) > chars_in_pwfield)
1739 typed_passwd += (strlen(typed_passwd) - chars_in_pwfield);
1741 update_passwd_window(si, typed_passwd, pw->ratio);
1743 else if (pw->show_stars_p)
1745 i = strlen(typed_passwd);
1746 stars = (char *) malloc(i+1);
1747 memset (stars, '*', i);
1749 update_passwd_window (si, stars, pw->ratio);
1754 update_passwd_window (si, "", pw->ratio);
1760 passwd_event_loop (saver_info *si)
1762 saver_preferences *p = &si->prefs;
1766 passwd_animate_timer ((XtPointer) si, 0);
1768 while (si->unlock_state == ul_read)
1770 XtAppNextEvent (si->app, &event);
1771 if (event.xany.window == si->passwd_dialog && event.xany.type == Expose)
1772 draw_passwd_window (si);
1773 else if (event.xany.type == KeyPress)
1775 handle_passwd_key (si, &event.xkey);
1776 si->pw_data->caps_p = (event.xkey.state & LockMask);
1778 else if (event.xany.type == ButtonPress ||
1779 event.xany.type == ButtonRelease)
1781 si->pw_data->button_state_changed_p = True;
1782 handle_unlock_button (si, &event);
1783 if (si->pw_data->login_button_p)
1784 handle_login_button (si, &event);
1787 XtDispatchEvent (&event);
1790 switch (si->unlock_state)
1792 case ul_cancel: msg = ""; break;
1793 case ul_time: msg = "Timed out!"; break;
1794 case ul_finished: msg = "Checking..."; break;
1795 default: msg = 0; break;
1799 switch (si->unlock_state) {
1801 fprintf (stderr, "%s: input cancelled.\n", blurb()); break;
1803 fprintf (stderr, "%s: input timed out.\n", blurb()); break;
1805 fprintf (stderr, "%s: input finished.\n", blurb()); break;
1811 si->pw_data->i_beam = 0;
1812 update_passwd_window (si, msg, 0.0);
1813 XSync (si->dpy, False);
1815 /* Swallow all pending KeyPress/KeyRelease events. */
1818 while (XCheckMaskEvent (si->dpy, KeyPressMask|KeyReleaseMask, &e))
1826 handle_typeahead (saver_info *si)
1828 passwd_dialog_data *pw = si->pw_data;
1830 if (!si->unlock_typeahead)
1833 pw->passwd_changed_p = True;
1835 i = strlen (si->unlock_typeahead);
1836 if (i >= sizeof(pw->typed_passwd) - 1)
1837 i = sizeof(pw->typed_passwd) - 1;
1839 memcpy (pw->typed_passwd, si->unlock_typeahead, i);
1840 pw->typed_passwd [i] = 0;
1842 memset (si->unlock_typeahead, '*', strlen(si->unlock_typeahead));
1843 si->unlock_typeahead[i] = 0;
1844 update_passwd_window (si, si->unlock_typeahead, pw->ratio);
1846 free (si->unlock_typeahead);
1847 si->unlock_typeahead = 0;
1852 * Returns a copy of the input string with trailing whitespace removed.
1853 * Whitespace is anything considered so by isspace().
1854 * It is safe to call this with NULL, in which case NULL will be returned.
1855 * The returned string (if not NULL) should be freed by the caller with free().
1858 remove_trailing_whitespace(const char *str)
1868 newstr = malloc(len + 1);
1869 (void) strcpy(newstr, str);
1875 while (isspace(*--chr) && chr >= newstr)
1883 * The authentication conversation function.
1884 * Like a PAM conversation function, this accepts multiple messages in a single
1885 * round. It then splits them into individual messages for display on the
1886 * passwd dialog. A message sequence of info or error followed by a prompt will
1887 * be reduced into a single dialog window.
1889 * Returns 0 on success or -1 if some problem occurred (cancelled auth, OOM, ...)
1892 gui_auth_conv(int num_msg,
1893 const struct auth_message auth_msgs[],
1894 struct auth_response **resp,
1898 const char *info_msg, *prompt;
1899 struct auth_response *responses;
1901 if (!(responses = calloc(num_msg, sizeof(struct auth_response))))
1904 for (i = 0; i < num_msg; ++i)
1906 info_msg = prompt = NULL;
1908 /* See if there is a following message that can be shown at the same
1910 if (auth_msgs[i].type == AUTH_MSGTYPE_INFO
1912 && ( auth_msgs[i+1].type == AUTH_MSGTYPE_PROMPT_NOECHO
1913 || auth_msgs[i+1].type == AUTH_MSGTYPE_PROMPT_ECHO)
1916 info_msg = auth_msgs[i].msg;
1917 prompt = auth_msgs[++i].msg;
1921 if ( auth_msgs[i].type == AUTH_MSGTYPE_INFO
1922 || auth_msgs[i].type == AUTH_MSGTYPE_ERROR)
1923 info_msg = auth_msgs[i].msg;
1925 prompt = auth_msgs[i].msg;
1929 char *info_msg_trimmed, *prompt_trimmed;
1931 /* Trailing whitespace looks bad in a GUI */
1932 info_msg_trimmed = remove_trailing_whitespace(info_msg);
1933 prompt_trimmed = remove_trailing_whitespace(prompt);
1935 make_passwd_window(si, info_msg_trimmed, prompt_trimmed,
1936 auth_msgs[i].type == AUTH_MSGTYPE_PROMPT_ECHO
1939 if (info_msg_trimmed)
1940 free(info_msg_trimmed);
1943 free(prompt_trimmed);
1946 compose_status = calloc (1, sizeof (*compose_status));
1947 if (!compose_status)
1950 si->unlock_state = ul_read;
1952 handle_typeahead (si);
1953 passwd_event_loop (si);
1955 if (si->unlock_state == ul_cancel)
1958 responses[i].response = strdup(si->pw_data->typed_passwd);
1960 /* Cache the first response to a PROMPT_NOECHO to save prompting for
1961 * each auth mechanism. */
1962 if (si->cached_passwd == NULL &&
1963 auth_msgs[i].type == AUTH_MSGTYPE_PROMPT_NOECHO)
1964 si->cached_passwd = strdup(responses[i].response);
1966 free (compose_status);
1972 return (si->unlock_state == ul_finished) ? 0 : -1;
1976 free (compose_status);
1980 for (i = 0; i < num_msg; ++i)
1981 if (responses[i].response)
1982 free (responses[i].response);
1991 auth_finished_cb (saver_info *si)
1996 /* If we have something to say, put the dialog back up for a few seconds
1997 to display it. Otherwise, don't bother.
2000 if (si->unlock_state == ul_fail && /* failed with caps lock on */
2001 si->pw_data && si->pw_data->caps_p)
2002 s = "Authentication failed (Caps Lock?)";
2003 else if (si->unlock_state == ul_fail) /* failed without caps lock */
2004 s = "Authentication failed!";
2005 else if (si->unlock_state == ul_success && /* good, but report failures */
2006 si->unlock_failures > 0)
2008 if (si->unlock_failures == 1)
2009 s = "There has been\n1 failed login attempt.";
2012 sprintf (buf, "There have been\n%d failed login attempts.",
2013 si->unlock_failures);
2016 si->unlock_failures = 0;
2018 else /* good, with no failures, */
2019 goto END; /* or timeout, or cancel. */
2021 make_passwd_window (si, s, NULL, True);
2022 XSync (si->dpy, False);
2026 time_t start = time ((time_t *) 0);
2028 while (time ((time_t *) 0) < start + secs)
2029 if (XPending (si->dpy))
2031 XNextEvent (si->dpy, &event);
2032 if (event.xany.window == si->passwd_dialog &&
2033 event.xany.type == Expose)
2034 draw_passwd_window (si);
2035 else if (event.xany.type == ButtonPress ||
2036 event.xany.type == ButtonRelease)
2038 XSync (si->dpy, False);
2041 usleep (250000); /* 1/4 second */
2046 destroy_passwd_window (si);
2051 unlock_p (saver_info *si)
2053 saver_preferences *p = &si->prefs;
2057 fprintf(stderr, "%s: Error: no unlock function specified!\n", blurb());
2061 raise_window (si, True, True, True);
2063 xss_authenticate(si, p->verbose_p);
2065 return (si->unlock_state == ul_success);
2070 set_locked_p (saver_info *si, Bool locked_p)
2072 si->locked_p = locked_p;
2074 #ifdef HAVE_XHPDISABLERESET
2075 hp_lock_reset (si, locked_p); /* turn off/on C-Sh-Reset */
2077 #ifdef HAVE_XF86VMODE
2078 xfree_lock_mode_switch (si, locked_p); /* turn off/on C-Alt-Plus */
2080 #ifdef HAVE_XF86MISCSETGRABKEYSSTATE
2081 xfree_lock_grab_smasher (si, locked_p); /* turn off/on C-Alt-KP-*,/ */
2084 store_saver_status (si); /* store locked-p */
2088 #else /* NO_LOCKING -- whole file */
2091 set_locked_p (saver_info *si, Bool locked_p)
2093 if (locked_p) abort();
2096 #endif /* !NO_LOCKING */