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 */
35 #endif /* HAVE_SYSLOG */
37 #ifdef HAVE_XHPDISABLERESET
38 # include <X11/XHPlib.h>
39 static void hp_lock_reset (saver_info *si, Bool lock_p);
40 #endif /* HAVE_XHPDISABLERESET */
43 # include <X11/extensions/xf86vmode.h>
44 static void xfree_lock_mode_switch (saver_info *si, Bool lock_p);
45 #endif /* HAVE_XF86VMODE */
47 #ifdef HAVE_XF86MISCSETGRABKEYSSTATE
48 # include <X11/extensions/xf86misc.h>
49 static void xfree_lock_grab_smasher (saver_info *si, Bool lock_p);
50 #endif /* HAVE_XF86MISCSETGRABKEYSSTATE */
54 ERROR! You must not include vroot.h in this file.
58 # include <sys/utsname.h> /* for hostname info */
59 #endif /* HAVE_UNAME */
66 extern char *getenv(const char *name);
67 extern int validate_user(char *name, char *password);
70 vms_passwd_valid_p(char *pw, Bool verbose_p)
72 return (validate_user (getenv("USER"), typed_passwd) == 1);
74 # undef passwd_valid_p
75 # define passwd_valid_p vms_passwd_valid_p
79 #define SAMPLE_INPUT "MMMMMMMMMMMM"
83 #define MAX(a,b) ((a)>(b)?(a):(b))
85 typedef struct info_dialog_data info_dialog_data;
87 struct passwd_dialog_data {
89 saver_screen_info *prompt_screen;
90 int previous_mouse_x, previous_mouse_y;
92 char typed_passwd [80];
100 Dimension border_width;
103 Bool show_stars_p; /* "I regret that I have but one asterisk for my country."
104 -- Nathan Hale, 1776. */
109 mlstring *info_label;
110 /* The entry field shall only be displayed if prompt_label is not NULL */
111 mlstring *prompt_label;
114 Bool passwd_changed_p; /* Whether the user entry field needs redrawing */
121 XFontStruct *heading_font;
122 XFontStruct *body_font;
123 XFontStruct *label_font;
124 XFontStruct *passwd_font;
125 XFontStruct *date_font;
126 XFontStruct *button_font;
127 XFontStruct *uname_font;
131 Pixel passwd_foreground;
132 Pixel passwd_background;
133 Pixel thermo_foreground;
134 Pixel thermo_background;
137 Pixel button_foreground;
138 Pixel button_background;
140 Dimension preferred_logo_width, logo_width;
141 Dimension preferred_logo_height, logo_height;
142 Dimension thermo_width;
143 Dimension internal_border;
144 Dimension shadow_width;
146 Dimension passwd_field_x, passwd_field_y;
147 Dimension passwd_field_width, passwd_field_height;
149 Dimension unlock_button_x, unlock_button_y;
150 Dimension unlock_button_width, unlock_button_height;
152 Dimension login_button_x, login_button_y;
153 Dimension login_button_width, login_button_height;
155 Dimension thermo_field_x, thermo_field_y;
156 Dimension thermo_field_height;
159 Pixmap logo_clipmask;
161 unsigned long *logo_pixels;
163 Cursor passwd_cursor;
164 Bool unlock_button_down_p;
165 Bool login_button_down_p;
167 Bool login_button_enabled_p;
168 Bool button_state_changed_p; /* Refers to both buttons */
171 Pixmap user_entry_pixmap;
174 static void draw_passwd_window (saver_info *si);
175 static void update_passwd_window (saver_info *si, const char *printed_passwd,
177 static void destroy_passwd_window (saver_info *si);
178 static void undo_vp_motion (saver_info *si);
179 static void finished_typing_passwd (saver_info *si, passwd_dialog_data *pw);
180 static void cleanup_passwd_window (saver_info *si);
181 static void restore_background (saver_info *si);
183 extern void xss_authenticate(saver_info *si, Bool verbose_p);
186 new_passwd_window (saver_info *si)
188 passwd_dialog_data *pw;
192 saver_screen_info *ssi = &si->screens [mouse_screen (si)];
194 pw = (passwd_dialog_data *) calloc (1, sizeof(*pw));
198 /* Display the button only if the "newLoginCommand" pref is non-null.
200 pw->login_button_p = (si->prefs.new_login_command &&
201 *si->prefs.new_login_command);
203 pw->passwd_cursor = XCreateFontCursor (si->dpy, XC_top_left_arrow);
205 pw->prompt_screen = ssi;
206 if (si->prefs.verbose_p)
207 fprintf (stderr, "%s: %d: creating password dialog.\n",
208 blurb(), pw->prompt_screen->number);
210 screen = pw->prompt_screen->screen;
211 cmap = DefaultColormapOfScreen (screen);
213 pw->show_stars_p = get_boolean_resource(si->dpy, "passwd.asterisks",
216 pw->heading_label = get_string_resource (si->dpy, "passwd.heading.label",
217 "Dialog.Label.Label");
218 pw->body_label = get_string_resource (si->dpy, "passwd.body.label",
219 "Dialog.Label.Label");
220 pw->user_label = get_string_resource (si->dpy, "passwd.user.label",
221 "Dialog.Label.Label");
222 pw->unlock_label = get_string_resource (si->dpy, "passwd.unlock.label",
223 "Dialog.Button.Label");
224 pw->login_label = get_string_resource (si->dpy, "passwd.login.label",
225 "Dialog.Button.Label");
227 pw->date_label = get_string_resource (si->dpy, "dateFormat", "DateFormat");
229 if (!pw->heading_label)
230 pw->heading_label = strdup("ERROR: RESOURCES NOT INSTALLED CORRECTLY");
232 pw->body_label = strdup("ERROR: RESOURCES NOT INSTALLED CORRECTLY");
233 if (!pw->user_label) pw->user_label = strdup("ERROR");
234 if (!pw->date_label) pw->date_label = strdup("ERROR");
235 if (!pw->unlock_label) pw->unlock_label = strdup("ERROR (UNLOCK)");
236 if (!pw->login_label) pw->login_label = strdup ("ERROR (LOGIN)") ;
238 /* Put the version number in the label. */
240 char *s = (char *) malloc (strlen(pw->heading_label) + 20);
241 sprintf(s, pw->heading_label, si->version);
242 free (pw->heading_label);
243 pw->heading_label = s;
246 /* Get hostname info */
247 pw->uname_label = strdup(""); /* Initialy, write nothing */
253 if (uname (&uts) == 0)
255 #if 0 /* Get the full hostname */
258 if ((s = strchr(uts.nodename, '.')))
262 char *s = strdup (uts.nodename);
263 free (pw->uname_label);
269 pw->passwd_string = strdup("");
271 f = get_string_resource (si->dpy, "passwd.headingFont", "Dialog.Font");
272 pw->heading_font = XLoadQueryFont (si->dpy, (f ? f : "fixed"));
273 if (!pw->heading_font) pw->heading_font = XLoadQueryFont (si->dpy, "fixed");
276 f = get_string_resource (si->dpy, "passwd.buttonFont", "Dialog.Font");
277 pw->button_font = XLoadQueryFont (si->dpy, (f ? f : "fixed"));
278 if (!pw->button_font) pw->button_font = XLoadQueryFont (si->dpy, "fixed");
281 f = get_string_resource(si->dpy, "passwd.bodyFont", "Dialog.Font");
282 pw->body_font = XLoadQueryFont (si->dpy, (f ? f : "fixed"));
283 if (!pw->body_font) pw->body_font = XLoadQueryFont (si->dpy, "fixed");
286 f = get_string_resource(si->dpy, "passwd.labelFont", "Dialog.Font");
287 pw->label_font = XLoadQueryFont (si->dpy, (f ? f : "fixed"));
288 if (!pw->label_font) pw->label_font = XLoadQueryFont (si->dpy, "fixed");
291 f = get_string_resource(si->dpy, "passwd.passwdFont", "Dialog.Font");
292 pw->passwd_font = XLoadQueryFont (si->dpy, (f ? f : "fixed"));
293 if (!pw->passwd_font) pw->passwd_font = XLoadQueryFont (si->dpy, "fixed");
296 f = get_string_resource(si->dpy, "passwd.dateFont", "Dialog.Font");
297 pw->date_font = XLoadQueryFont (si->dpy, (f ? f : "fixed"));
298 if (!pw->date_font) pw->date_font = XLoadQueryFont (si->dpy, "fixed");
301 f = get_string_resource(si->dpy, "passwd.unameFont", "Dialog.Font");
302 pw->uname_font = XLoadQueryFont (si->dpy, (f ? f : "fixed"));
303 if (!pw->uname_font) pw->uname_font = XLoadQueryFont (si->dpy, "fixed");
306 pw->show_uname_p = get_boolean_resource(si->dpy, "passwd.uname", "Boolean");
308 pw->foreground = get_pixel_resource (si->dpy, cmap,
310 "Dialog.Foreground" );
311 pw->background = get_pixel_resource (si->dpy, cmap,
313 "Dialog.Background" );
315 if (pw->foreground == pw->background)
317 /* Make sure the error messages show up. */
318 pw->foreground = BlackPixelOfScreen (screen);
319 pw->background = WhitePixelOfScreen (screen);
322 pw->passwd_foreground = get_pixel_resource (si->dpy, cmap,
323 "passwd.text.foreground",
324 "Dialog.Text.Foreground" );
325 pw->passwd_background = get_pixel_resource (si->dpy, cmap,
326 "passwd.text.background",
327 "Dialog.Text.Background" );
328 pw->button_foreground = get_pixel_resource (si->dpy, cmap,
329 "splash.Button.foreground",
330 "Dialog.Button.Foreground" );
331 pw->button_background = get_pixel_resource (si->dpy, cmap,
332 "splash.Button.background",
333 "Dialog.Button.Background" );
334 pw->thermo_foreground = get_pixel_resource (si->dpy, cmap,
335 "passwd.thermometer.foreground",
336 "Dialog.Thermometer.Foreground" );
337 pw->thermo_background = get_pixel_resource ( si->dpy, cmap,
338 "passwd.thermometer.background",
339 "Dialog.Thermometer.Background" );
340 pw->shadow_top = get_pixel_resource ( si->dpy, cmap,
341 "passwd.topShadowColor",
342 "Dialog.Foreground" );
343 pw->shadow_bottom = get_pixel_resource (si->dpy, cmap,
344 "passwd.bottomShadowColor",
345 "Dialog.Background" );
347 pw->preferred_logo_width = get_integer_resource (si->dpy, "passwd.logo.width",
348 "Dialog.Logo.Width");
349 pw->preferred_logo_height = get_integer_resource (si->dpy, "passwd.logo.height",
350 "Dialog.Logo.Height");
351 pw->thermo_width = get_integer_resource (si->dpy, "passwd.thermometer.width",
352 "Dialog.Thermometer.Width");
353 pw->internal_border = get_integer_resource (si->dpy, "passwd.internalBorderWidth",
354 "Dialog.InternalBorderWidth");
355 pw->shadow_width = get_integer_resource (si->dpy, "passwd.shadowThickness",
356 "Dialog.ShadowThickness");
358 if (pw->preferred_logo_width == 0) pw->preferred_logo_width = 150;
359 if (pw->preferred_logo_height == 0) pw->preferred_logo_height = 150;
360 if (pw->internal_border == 0) pw->internal_border = 15;
361 if (pw->shadow_width == 0) pw->shadow_width = 4;
362 if (pw->thermo_width == 0) pw->thermo_width = pw->shadow_width;
365 /* We need to remember the mouse position and restore it afterward, or
366 sometimes (perhaps only with Xinerama?) the mouse gets warped to
367 inside the bounds of the lock dialog window.
370 Window pointer_root, pointer_child;
371 int root_x, root_y, win_x, win_y;
373 pw->previous_mouse_x = 0;
374 pw->previous_mouse_y = 0;
375 if (XQueryPointer (si->dpy, RootWindowOfScreen (pw->prompt_screen->screen),
376 &pointer_root, &pointer_child,
377 &root_x, &root_y, &win_x, &win_y, &mask))
379 pw->previous_mouse_x = root_x;
380 pw->previous_mouse_y = root_y;
381 if (si->prefs.verbose_p)
382 fprintf (stderr, "%s: %d: mouse is at %d,%d.\n",
383 blurb(), pw->prompt_screen->number,
384 pw->previous_mouse_x, pw->previous_mouse_y);
386 else if (si->prefs.verbose_p)
387 fprintf (stderr, "%s: %d: unable to determine mouse position?\n",
388 blurb(), pw->prompt_screen->number);
391 /* Before mapping the window, save a pixmap of the current screen.
392 When we lower the window, we
393 restore these bits. This works, because the running screenhack
394 has already been sent SIGSTOP, so we know nothing else is drawing
399 pw->save_under = XCreatePixmap (si->dpy,
400 pw->prompt_screen->screensaver_window,
401 pw->prompt_screen->width,
402 pw->prompt_screen->height,
403 pw->prompt_screen->current_depth);
404 gcv.function = GXcopy;
405 gc = XCreateGC (si->dpy, pw->save_under, GCFunction, &gcv);
406 XCopyArea (si->dpy, pw->prompt_screen->screensaver_window,
409 pw->prompt_screen->width, pw->prompt_screen->height,
411 XFreeGC (si->dpy, gc);
419 * info_msg and prompt may be NULL.
422 make_passwd_window (saver_info *si,
423 const char *info_msg,
427 XSetWindowAttributes attrs;
428 unsigned long attrmask = 0;
429 passwd_dialog_data *pw;
432 Dimension max_string_width_px;
433 saver_screen_info *ssi = &si->screens [mouse_screen (si)];
435 cleanup_passwd_window (si);
438 new_passwd_window (si);
440 if (!(pw = si->pw_data))
445 pw->prompt_screen = ssi;
446 if (si->prefs.verbose_p)
447 fprintf (stderr, "%s: %d: creating password dialog.\n",
448 blurb(), pw->prompt_screen->number);
450 screen = pw->prompt_screen->screen;
451 cmap = DefaultColormapOfScreen (screen);
453 pw->echo_input = echo;
455 max_string_width_px = ssi->width
456 - pw->shadow_width * 4
457 - pw->border_width * 2
459 - pw->preferred_logo_width
460 - pw->internal_border * 2;
461 /* As the string wraps it makes the window taller which makes the logo wider
462 * which leaves less room for the text which makes the string wrap. Uh-oh, a
463 * loop. By wrapping at a bit less than the available width, there's some
464 * room for the dialog to grow without going off the edge of the screen. */
465 max_string_width_px *= 0.75;
467 pw->info_label = mlstring_new(info_msg ? info_msg : pw->body_label,
468 pw->label_font, max_string_width_px);
471 int direction, ascent, descent;
477 /* Measure the heading_label. */
478 XTextExtents (pw->heading_font,
479 pw->heading_label, strlen(pw->heading_label),
480 &direction, &ascent, &descent, &overall);
481 if (overall.width > pw->width) pw->width = overall.width;
482 pw->height += ascent + descent;
484 /* Measure the uname_label. */
485 if ((strlen(pw->uname_label)) && pw->show_uname_p)
487 XTextExtents (pw->uname_font,
488 pw->uname_label, strlen(pw->uname_label),
489 &direction, &ascent, &descent, &overall);
490 if (overall.width > pw->width) pw->width = overall.width;
491 pw->height += ascent + descent;
495 Dimension w2 = 0, w3 = 0, button_w = 0;
496 Dimension h2 = 0, h3 = 0, button_h = 0;
497 const char *passwd_string = SAMPLE_INPUT;
499 /* Measure the user_label. */
500 XTextExtents (pw->label_font,
501 pw->user_label, strlen(pw->user_label),
502 &direction, &ascent, &descent, &overall);
503 if (overall.width > w2) w2 = overall.width;
504 h2 += ascent + descent;
506 /* Measure the info_label. */
507 if (pw->info_label->overall_width > pw->width) pw->width = pw->info_label->overall_width;
508 h2 += pw->info_label->overall_height;
510 /* Measure the user string. */
511 XTextExtents (pw->passwd_font,
512 si->user, strlen(si->user),
513 &direction, &ascent, &descent, &overall);
514 overall.width += (pw->shadow_width * 4);
515 ascent += (pw->shadow_width * 4);
516 if (overall.width > w3) w3 = overall.width;
517 h3 += ascent + descent;
519 /* Measure the (dummy) passwd_string. */
522 XTextExtents (pw->passwd_font,
523 passwd_string, strlen(passwd_string),
524 &direction, &ascent, &descent, &overall);
525 overall.width += (pw->shadow_width * 4);
526 ascent += (pw->shadow_width * 4);
527 if (overall.width > w3) w3 = overall.width;
528 h3 += ascent + descent;
530 /* Measure the prompt_label. */
531 max_string_width_px -= w3;
532 pw->prompt_label = mlstring_new(prompt, pw->label_font, max_string_width_px);
534 if (pw->prompt_label->overall_width > w2) w2 = pw->prompt_label->overall_width;
536 h2 += pw->prompt_label->overall_height;
538 w2 = w2 + w3 + (pw->shadow_width * 2);
542 /* The "Unlock" button. */
543 XTextExtents (pw->label_font,
544 pw->unlock_label, strlen(pw->unlock_label),
545 &direction, &ascent, &descent, &overall);
546 button_w = overall.width;
547 button_h = ascent + descent;
549 /* Add some horizontal padding inside the button. */
552 button_w += ((ascent + descent) / 2) + (pw->shadow_width * 2);
553 button_h += ((ascent + descent) / 2) + (pw->shadow_width * 2);
555 pw->unlock_button_width = button_w;
556 pw->unlock_button_height = button_h;
558 w2 = MAX (w2, button_w);
559 h2 += button_h * 1.5;
561 /* The "New Login" button */
562 pw->login_button_width = 0;
563 pw->login_button_height = 0;
565 if (pw->login_button_p)
567 pw->login_button_enabled_p = True;
569 /* Measure the "New Login" button */
570 XTextExtents (pw->button_font, pw->login_label,
571 strlen (pw->login_label),
572 &direction, &ascent, &descent, &overall);
573 button_w = overall.width;
574 button_h = ascent + descent;
576 /* Add some horizontal padding inside the buttons. */
579 button_w += ((ascent + descent) / 2) + (pw->shadow_width * 2);
580 button_h += ((ascent + descent) / 2) + (pw->shadow_width * 2);
582 pw->login_button_width = button_w;
583 pw->login_button_height = button_h;
585 if (button_h > pw->unlock_button_height)
586 h2 += (button_h * 1.5 - pw->unlock_button_height * 1.5);
588 /* Use (2 * shadow_width) spacing between the buttons. Another
589 (2 * shadow_width) is required to account for button shadows. */
590 w2 = MAX (w2, button_w + pw->unlock_button_width + (pw->shadow_width * 4));
593 if (w2 > pw->width) pw->width = w2;
597 pw->width += (pw->internal_border * 2);
598 pw->height += (pw->internal_border * 4);
600 pw->width += pw->thermo_width + (pw->shadow_width * 3);
602 if (pw->preferred_logo_height > pw->height)
603 pw->height = pw->logo_height = pw->preferred_logo_height;
604 else if (pw->height > pw->preferred_logo_height)
605 pw->logo_height = pw->height;
607 pw->logo_width = pw->logo_height;
609 pw->width += pw->logo_width;
612 attrmask |= CWOverrideRedirect; attrs.override_redirect = True;
614 attrmask |= CWEventMask;
615 attrs.event_mask = (ExposureMask | KeyPressMask |
616 ButtonPressMask | ButtonReleaseMask);
618 /* Figure out where on the desktop to place the window so that it will
619 actually be visible; this takes into account virtual viewports as
623 get_screen_viewport (pw->prompt_screen, &x, &y, &w, &h,
624 pw->previous_mouse_x, pw->previous_mouse_y,
625 si->prefs.verbose_p);
626 if (si->prefs.debug_p) w /= 2;
627 pw->x = x + ((w + pw->width) / 2) - pw->width;
628 pw->y = y + ((h + pw->height) / 2) - pw->height;
629 if (pw->x < x) pw->x = x;
630 if (pw->y < y) pw->y = y;
633 pw->border_width = get_integer_resource (si->dpy, "passwd.borderWidth",
634 "Dialog.BorderWidth");
636 /* Only create the window the first time around */
637 if (!si->passwd_dialog)
640 XCreateWindow (si->dpy,
641 RootWindowOfScreen(screen),
642 pw->x, pw->y, pw->width, pw->height, pw->border_width,
643 DefaultDepthOfScreen (screen), InputOutput,
644 DefaultVisualOfScreen(screen),
646 XSetWindowBackground (si->dpy, si->passwd_dialog, pw->background);
648 /* We use the default visual, not ssi->visual, so that the logo pixmap's
649 visual matches that of the si->passwd_dialog window. */
650 pw->logo_pixmap = xscreensaver_logo (ssi->screen,
651 /* ssi->current_visual, */
652 DefaultVisualOfScreen(screen),
653 si->passwd_dialog, cmap,
655 &pw->logo_pixels, &pw->logo_npixels,
656 &pw->logo_clipmask, True);
658 else /* On successive prompts, just resize the window */
661 unsigned int mask = CWX | CWY | CWWidth | CWHeight;
665 wc.width = pw->width;
666 wc.height = pw->height;
668 XConfigureWindow (si->dpy, si->passwd_dialog, mask, &wc);
671 restore_background(si);
673 XMapRaised (si->dpy, si->passwd_dialog);
674 XSync (si->dpy, False);
676 move_mouse_grab (si, si->passwd_dialog,
678 pw->prompt_screen->number);
684 XInstallColormap (si->dpy, cmap);
685 draw_passwd_window (si);
690 draw_passwd_window (saver_info *si)
692 passwd_dialog_data *pw = si->pw_data;
696 int x1, x2, x3, y1, y2;
701 pw->passwd_changed_p = True;
702 pw->button_state_changed_p = True;
704 /* This height is the height of all the elements, not to be confused with
705 * the overall window height which is pw->height. It is used to compute
706 * the amount of spacing (padding) between elements. */
707 height = (pw->heading_font->ascent + pw->heading_font->descent +
708 pw->info_label->overall_height +
709 MAX (((pw->label_font->ascent + pw->label_font->descent) +
710 (pw->prompt_label ? pw->prompt_label->overall_height : 0)),
711 ((pw->passwd_font->ascent + pw->passwd_font->descent) +
712 (pw->shadow_width * 2)) * (pw->prompt_label ? 2 : 1)) +
713 pw->date_font->ascent + pw->date_font->descent);
715 if ((strlen(pw->uname_label)) && pw->show_uname_p)
716 height += (pw->uname_font->ascent + pw->uname_font->descent); /* for uname */
718 height += ((pw->button_font->ascent + pw->button_font->descent) * 2 +
719 2 * pw->shadow_width);
721 spacing = ((pw->height - 2 * pw->shadow_width
722 - pw->internal_border - height)
725 if (spacing < 0) spacing = 0;
727 gcv.foreground = pw->foreground;
728 gc1 = XCreateGC (si->dpy, si->passwd_dialog, GCForeground, &gcv);
729 gc2 = XCreateGC (si->dpy, si->passwd_dialog, GCForeground, &gcv);
730 x1 = pw->logo_width + pw->thermo_width + (pw->shadow_width * 3);
731 x3 = pw->width - (pw->shadow_width * 2);
732 y1 = (pw->shadow_width * 2) + spacing + spacing;
736 XSetFont (si->dpy, gc1, pw->heading_font->fid);
737 sw = string_width (pw->heading_font, pw->heading_label);
738 x2 = (x1 + ((x3 - x1 - sw) / 2));
739 y1 += spacing + pw->heading_font->ascent + pw->heading_font->descent;
740 XDrawString (si->dpy, si->passwd_dialog, gc1, x2, y1,
741 pw->heading_label, strlen(pw->heading_label));
743 /* uname below top heading
745 if ((strlen(pw->uname_label)) && pw->show_uname_p)
747 XSetFont (si->dpy, gc1, pw->uname_font->fid);
748 y1 += spacing + pw->uname_font->ascent + pw->uname_font->descent;
749 sw = string_width (pw->uname_font, pw->uname_label);
750 x2 = (x1 + ((x3 - x1 - sw) / 2));
751 XDrawString (si->dpy, si->passwd_dialog, gc1, x2, y1,
752 pw->uname_label, strlen(pw->uname_label));
755 /* the info_label (below uname)
757 x2 = (x1 + ((x3 - x1 - pw->info_label->overall_width) / 2));
758 y1 += spacing + pw->info_label->font_height / 2;
759 mlstring_draw(si->dpy, si->passwd_dialog, gc1, pw->info_label,
761 y1 += pw->info_label->overall_height;
764 tb_height = (pw->passwd_font->ascent + pw->passwd_font->descent +
765 (pw->shadow_width * 4));
767 /* the "User:" prompt
770 XSetForeground (si->dpy, gc1, pw->foreground);
771 XSetFont (si->dpy, gc1, pw->label_font->fid);
772 y1 += (spacing + tb_height + pw->shadow_width);
773 x2 = (x1 + pw->internal_border +
774 MAX(string_width (pw->label_font, pw->user_label),
775 pw->prompt_label ? pw->prompt_label->overall_width : 0));
776 XDrawString (si->dpy, si->passwd_dialog, gc1,
777 x2 - string_width (pw->label_font, pw->user_label),
778 y1 - pw->passwd_font->descent,
779 pw->user_label, strlen(pw->user_label));
781 /* the prompt_label prompt
783 if (pw->prompt_label)
785 y1 += tb_height - pw->label_font->ascent + pw->shadow_width;
786 mlstring_draw(si->dpy, si->passwd_dialog, gc1, pw->prompt_label,
787 x2 - pw->prompt_label->overall_width, y1);
790 /* the "user name" text field
793 XSetForeground (si->dpy, gc1, pw->passwd_foreground);
794 XSetForeground (si->dpy, gc2, pw->passwd_background);
795 XSetFont (si->dpy, gc1, pw->passwd_font->fid);
796 y1 += (spacing + tb_height);
797 x2 += (pw->shadow_width * 4);
799 pw->passwd_field_width = x3 - x2 - pw->internal_border;
800 pw->passwd_field_height = (pw->passwd_font->ascent +
801 pw->passwd_font->descent +
804 XFillRectangle (si->dpy, si->passwd_dialog, gc2,
805 x2 - pw->shadow_width,
806 y1 - (pw->passwd_font->ascent + pw->passwd_font->descent),
807 pw->passwd_field_width, pw->passwd_field_height);
808 XDrawString (si->dpy, si->passwd_dialog, gc1,
810 y1 - pw->passwd_font->descent,
811 si->user, strlen(si->user));
813 /* the password/prompt text field
815 if (pw->prompt_label)
817 y1 += (spacing + pw->prompt_label->overall_height + pw->shadow_width * 2);
819 pw->passwd_field_x = x2 - pw->shadow_width;
820 pw->passwd_field_y = y1 - (pw->passwd_font->ascent +
821 pw->passwd_font->descent);
824 /* The shadow around the text fields
827 y1 += (spacing + (pw->shadow_width * 3));
828 x1 = x2 - (pw->shadow_width * 2);
829 x2 = pw->passwd_field_width + (pw->shadow_width * 2);
830 y2 = pw->passwd_field_height + (pw->shadow_width * 2);
832 draw_shaded_rectangle (si->dpy, si->passwd_dialog,
835 pw->shadow_bottom, pw->shadow_top);
837 if (pw->prompt_label)
839 y1 += (spacing + pw->prompt_label->overall_height + pw->shadow_width * 2);
840 draw_shaded_rectangle (si->dpy, si->passwd_dialog,
843 pw->shadow_bottom, pw->shadow_top);
847 /* The date, below the text fields
851 time_t now = time ((time_t *) 0);
852 struct tm *tm = localtime (&now);
853 memset (buf, 0, sizeof(buf));
854 strftime (buf, sizeof(buf)-1, pw->date_label, tm);
856 XSetFont (si->dpy, gc1, pw->date_font->fid);
857 y1 += pw->shadow_width;
858 y1 += (spacing + tb_height);
860 sw = string_width (pw->date_font, buf);
862 XDrawString (si->dpy, si->passwd_dialog, gc1, x2, y1, buf, strlen(buf));
865 /* Set up the GCs for the "New Login" and "Unlock" buttons.
867 XSetForeground(si->dpy, gc1, pw->button_foreground);
868 XSetForeground(si->dpy, gc2, pw->button_background);
869 XSetFont(si->dpy, gc1, pw->button_font->fid);
871 /* The "Unlock" button */
872 x2 = pw->width - pw->internal_border - (pw->shadow_width * 2);
874 /* right aligned button */
875 x1 = x2 - pw->unlock_button_width;
877 /* Add half the difference between y1 and the internal edge.
878 * It actually looks better if the internal border is ignored. */
879 y1 += ((pw->height - MAX (pw->unlock_button_height, pw->login_button_height)
883 pw->unlock_button_x = x1;
884 pw->unlock_button_y = y1;
886 /* The "New Login" button
888 if (pw->login_button_p)
890 /* Using the same GC as for the Unlock button */
892 sw = string_width (pw->button_font, pw->login_label);
894 /* left aligned button */
895 x1 = (pw->logo_width + pw->thermo_width + (pw->shadow_width * 3) +
896 pw->internal_border);
898 pw->login_button_x = x1;
899 pw->login_button_y = y1;
904 x1 = pw->shadow_width * 6;
905 y1 = pw->shadow_width * 6;
906 x2 = pw->logo_width - (pw->shadow_width * 12);
907 y2 = pw->logo_height - (pw->shadow_width * 12);
913 unsigned int w, h, bw, d;
914 XGetGeometry (si->dpy, pw->logo_pixmap, &root, &x, &y, &w, &h, &bw, &d);
915 XSetForeground (si->dpy, gc1, pw->foreground);
916 XSetBackground (si->dpy, gc1, pw->background);
917 XSetClipMask (si->dpy, gc1, pw->logo_clipmask);
918 XSetClipOrigin (si->dpy, gc1, x1 + ((x2 - (int)w) / 2), y1 + ((y2 - (int)h) / 2));
920 XCopyPlane (si->dpy, pw->logo_pixmap, si->passwd_dialog, gc1,
922 x1 + ((x2 - (int)w) / 2),
923 y1 + ((y2 - (int)h) / 2),
926 XCopyArea (si->dpy, pw->logo_pixmap, si->passwd_dialog, gc1,
928 x1 + ((x2 - (int)w) / 2),
929 y1 + ((y2 - (int)h) / 2));
934 XSetForeground (si->dpy, gc1, pw->thermo_foreground);
935 XSetForeground (si->dpy, gc2, pw->thermo_background);
937 pw->thermo_field_x = pw->logo_width + pw->shadow_width;
938 pw->thermo_field_y = pw->shadow_width * 5;
939 pw->thermo_field_height = pw->height - (pw->shadow_width * 10);
942 /* Solid border inside the logo box. */
943 XSetForeground (si->dpy, gc1, pw->foreground);
944 XDrawRectangle (si->dpy, si->passwd_dialog, gc1, x1, y1, x2-1, y2-1);
947 /* The shadow around the logo
949 draw_shaded_rectangle (si->dpy, si->passwd_dialog,
950 pw->shadow_width * 4,
951 pw->shadow_width * 4,
952 pw->logo_width - (pw->shadow_width * 8),
953 pw->logo_height - (pw->shadow_width * 8),
955 pw->shadow_bottom, pw->shadow_top);
957 /* The shadow around the thermometer
959 draw_shaded_rectangle (si->dpy, si->passwd_dialog,
961 pw->shadow_width * 4,
962 pw->thermo_width + (pw->shadow_width * 2),
963 pw->height - (pw->shadow_width * 8),
965 pw->shadow_bottom, pw->shadow_top);
968 /* Solid border inside the thermometer. */
969 XSetForeground (si->dpy, gc1, pw->foreground);
970 XDrawRectangle (si->dpy, si->passwd_dialog, gc1,
971 pw->thermo_field_x, pw->thermo_field_y,
972 pw->thermo_width - 1, pw->thermo_field_height - 1);
975 /* The shadow around the whole window
977 draw_shaded_rectangle (si->dpy, si->passwd_dialog,
978 0, 0, pw->width, pw->height, pw->shadow_width,
979 pw->shadow_top, pw->shadow_bottom);
981 XFreeGC (si->dpy, gc1);
982 XFreeGC (si->dpy, gc2);
984 update_passwd_window (si, pw->passwd_string, pw->ratio);
988 draw_button(Display *dpy,
991 unsigned long foreground, unsigned long background,
994 int width, int height,
996 Pixel shadow_light, Pixel shadow_dark,
1002 int label_x, label_y;
1004 gcv.foreground = foreground;
1005 gcv.font = font->fid;
1006 gc1 = XCreateGC(dpy, dialog, GCForeground|GCFont, &gcv);
1007 gcv.foreground = background;
1008 gc2 = XCreateGC(dpy, dialog, GCForeground, &gcv);
1010 XFillRectangle(dpy, dialog, gc2,
1011 x, y, width, height);
1013 sw = string_width(font, label);
1015 label_x = x + ((width - sw) / 2);
1016 label_y = (y + (height - (font->ascent + font->descent)) / 2 + font->ascent);
1024 XDrawString(dpy, dialog, gc1, label_x, label_y, label, strlen(label));
1029 draw_shaded_rectangle(dpy, dialog, x, y, width, height,
1030 shadow_width, shadow_light, shadow_dark);
1034 update_passwd_window (saver_info *si, const char *printed_passwd, float ratio)
1036 passwd_dialog_data *pw = si->pw_data;
1040 XRectangle rects[1];
1043 gcv.foreground = pw->passwd_foreground;
1044 gcv.font = pw->passwd_font->fid;
1045 gc1 = XCreateGC (si->dpy, si->passwd_dialog, GCForeground|GCFont, &gcv);
1046 gcv.foreground = pw->passwd_background;
1047 gc2 = XCreateGC (si->dpy, si->passwd_dialog, GCForeground, &gcv);
1051 char *s = strdup (printed_passwd);
1052 if (pw->passwd_string) free (pw->passwd_string);
1053 pw->passwd_string = s;
1056 if (pw->prompt_label)
1059 /* the "password" text field
1061 rects[0].x = pw->passwd_field_x;
1062 rects[0].y = pw->passwd_field_y;
1063 rects[0].width = pw->passwd_field_width;
1064 rects[0].height = pw->passwd_field_height;
1066 /* The user entry (password) field is double buffered.
1067 * This avoids flickering, particularly in synchronous mode. */
1069 if (pw->passwd_changed_p)
1071 pw->passwd_changed_p = False;
1073 if (pw->user_entry_pixmap)
1075 XFreePixmap(si->dpy, pw->user_entry_pixmap);
1076 pw->user_entry_pixmap = 0;
1079 pw->user_entry_pixmap = XCreatePixmap(si->dpy, si->passwd_dialog,
1080 rects[0].width, rects[0].height, pw->prompt_screen->current_depth);
1083 XFillRectangle (si->dpy, pw->user_entry_pixmap, gc2,
1084 0, 0, rects[0].width, rects[0].height);
1086 XDrawString (si->dpy, pw->user_entry_pixmap, gc1,
1088 pw->passwd_font->ascent,
1089 pw->passwd_string, strlen(pw->passwd_string));
1091 /* Ensure the new pixmap gets copied to the window */
1098 if (pw->i_beam == 0)
1100 /* Make the I-beam disappear */
1101 XCopyArea(si->dpy, pw->user_entry_pixmap, si->passwd_dialog, gc2,
1102 0, 0, rects[0].width, rects[0].height,
1103 rects[0].x, rects[0].y);
1105 else if (pw->i_beam == 1)
1107 /* Make the I-beam appear */
1108 x = (rects[0].x + pw->shadow_width +
1109 string_width (pw->passwd_font, pw->passwd_string));
1110 y = rects[0].y + pw->shadow_width;
1112 if (x > rects[0].x + rects[0].width - 1)
1113 x = rects[0].x + rects[0].width - 1;
1114 XDrawLine (si->dpy, si->passwd_dialog, gc1,
1116 x, y + pw->passwd_font->ascent + pw->passwd_font->descent-1);
1119 pw->i_beam = (pw->i_beam + 1) % 4;
1125 y = (pw->thermo_field_height - 2) * (1.0 - pw->ratio);
1128 XFillRectangle (si->dpy, si->passwd_dialog, gc2,
1129 pw->thermo_field_x + 1,
1130 pw->thermo_field_y + 1,
1133 XSetForeground (si->dpy, gc1, pw->thermo_foreground);
1134 XFillRectangle (si->dpy, si->passwd_dialog, gc1,
1135 pw->thermo_field_x + 1,
1136 pw->thermo_field_y + 1 + y,
1138 MAX (0, pw->thermo_field_height - y - 2));
1141 if (pw->button_state_changed_p)
1143 pw->button_state_changed_p = False;
1145 /* The "Unlock" button
1147 draw_button(si->dpy, si->passwd_dialog, pw->button_font,
1148 pw->button_foreground, pw->button_background,
1150 pw->unlock_button_x, pw->unlock_button_y,
1151 pw->unlock_button_width, pw->unlock_button_height,
1153 (pw->unlock_button_down_p ? pw->shadow_bottom : pw->shadow_top),
1154 (pw->unlock_button_down_p ? pw->shadow_top : pw->shadow_bottom),
1155 pw->unlock_button_down_p);
1157 /* The "New Login" button
1159 if (pw->login_button_p)
1161 draw_button(si->dpy, si->passwd_dialog, pw->button_font,
1162 (pw->login_button_enabled_p
1163 ? pw->passwd_foreground
1164 : pw->shadow_bottom),
1165 pw->button_background,
1167 pw->login_button_x, pw->login_button_y,
1168 pw->login_button_width, pw->login_button_height,
1170 (pw->login_button_down_p
1173 (pw->login_button_down_p
1175 : pw->shadow_bottom),
1176 pw->login_button_down_p);
1180 XFreeGC (si->dpy, gc1);
1181 XFreeGC (si->dpy, gc2);
1182 XSync (si->dpy, False);
1187 restore_background (saver_info *si)
1189 passwd_dialog_data *pw = si->pw_data;
1190 saver_screen_info *ssi = pw->prompt_screen;
1194 gcv.function = GXcopy;
1196 gc = XCreateGC (si->dpy, ssi->screensaver_window, GCFunction, &gcv);
1198 XCopyArea (si->dpy, pw->save_under,
1199 ssi->screensaver_window, gc,
1201 ssi->width, ssi->height,
1204 XFreeGC (si->dpy, gc);
1208 /* Frees anything created by make_passwd_window */
1210 cleanup_passwd_window (saver_info *si)
1212 passwd_dialog_data *pw;
1214 if (!(pw = si->pw_data))
1219 mlstring_free(pw->info_label);
1223 if (pw->prompt_label)
1225 mlstring_free(pw->prompt_label);
1226 pw->prompt_label = 0;
1229 memset (pw->typed_passwd, 0, sizeof(pw->typed_passwd));
1230 memset (pw->passwd_string, 0, strlen(pw->passwd_string));
1234 XtRemoveTimeOut (pw->timer);
1238 if (pw->user_entry_pixmap)
1240 XFreePixmap(si->dpy, pw->user_entry_pixmap);
1241 pw->user_entry_pixmap = 0;
1247 destroy_passwd_window (saver_info *si)
1249 saver_preferences *p = &si->prefs;
1250 passwd_dialog_data *pw = si->pw_data;
1251 saver_screen_info *ssi = pw->prompt_screen;
1252 Colormap cmap = DefaultColormapOfScreen (ssi->screen);
1253 Pixel black = BlackPixelOfScreen (ssi->screen);
1254 Pixel white = WhitePixelOfScreen (ssi->screen);
1257 cleanup_passwd_window (si);
1259 if (si->cached_passwd)
1261 char *wipe = si->cached_passwd;
1266 free(si->cached_passwd);
1267 si->cached_passwd = NULL;
1270 move_mouse_grab (si, RootWindowOfScreen (ssi->screen),
1271 ssi->cursor, ssi->number);
1273 if (pw->passwd_cursor)
1274 XFreeCursor (si->dpy, pw->passwd_cursor);
1277 fprintf (stderr, "%s: %d: moving mouse back to %d,%d.\n",
1278 blurb(), ssi->number,
1279 pw->previous_mouse_x, pw->previous_mouse_y);
1281 XWarpPointer (si->dpy, None, RootWindowOfScreen (ssi->screen),
1283 pw->previous_mouse_x, pw->previous_mouse_y);
1285 while (XCheckMaskEvent (si->dpy, PointerMotionMask, &event))
1287 fprintf (stderr, "%s: discarding MotionNotify event.\n", blurb());
1289 if (si->passwd_dialog)
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;
1765 unsigned int caps_p = 0;
1767 passwd_animate_timer ((XtPointer) si, 0);
1769 while (si->unlock_state == ul_read)
1771 XtAppNextEvent (si->app, &event);
1772 if (event.xany.window == si->passwd_dialog && event.xany.type == Expose)
1773 draw_passwd_window (si);
1774 else if (event.xany.type == KeyPress)
1776 handle_passwd_key (si, &event.xkey);
1777 caps_p = (event.xkey.state & LockMask);
1779 else if (event.xany.type == ButtonPress ||
1780 event.xany.type == ButtonRelease)
1782 si->pw_data->button_state_changed_p = True;
1783 handle_unlock_button (si, &event);
1784 if (si->pw_data->login_button_p)
1785 handle_login_button (si, &event);
1788 XtDispatchEvent (&event);
1791 switch (si->unlock_state)
1793 case ul_cancel: msg = ""; break;
1794 case ul_time: msg = "Timed out!"; break;
1795 case ul_finished: msg = "Checking..."; break;
1796 default: msg = 0; break;
1799 if (si->unlock_state == ul_fail)
1800 si->unlock_failures++;
1803 switch (si->unlock_state)
1806 fprintf (stderr, "%s: auth/input incorrect!%s\n", blurb(),
1807 (caps_p ? " (CapsLock)" : ""));
1810 fprintf (stderr, "%s: input cancelled.\n", blurb()); break;
1812 fprintf (stderr, "%s: input timed out.\n", blurb()); break;
1814 fprintf (stderr, "%s: input finished.\n", blurb()); break;
1819 if (si->unlock_state == ul_fail)
1821 /* If they typed a password (as opposed to just hitting return) and
1822 the password was invalid, log it.
1824 struct passwd *pw = getpwuid (getuid ());
1825 char *d = DisplayString (si->dpy);
1826 char *u = (pw && pw->pw_name ? pw->pw_name : "???");
1834 # if defined(LOG_AUTHPRIV)
1836 # elif defined(LOG_AUTH)
1843 openlog (progname, opt, fac);
1844 syslog (LOG_NOTICE, "FAILED LOGIN %d ON DISPLAY \"%s\", FOR \"%s\"",
1845 si->unlock_failures, d, u);
1848 #endif /* HAVE_SYSLOG */
1850 if (si->unlock_state == ul_fail)
1851 XBell (si->dpy, False);
1853 if (si->unlock_state == ul_success && si->unlock_failures != 0)
1855 if (si->unlock_failures == 1)
1856 fprintf (real_stderr,
1857 "%s: WARNING: 1 failed attempt to unlock the screen.\n",
1860 fprintf (real_stderr,
1861 "%s: WARNING: %d failed attempts to unlock the screen.\n",
1862 blurb(), si->unlock_failures);
1863 fflush (real_stderr);
1865 si->unlock_failures = 0;
1870 si->pw_data->i_beam = 0;
1871 update_passwd_window (si, msg, 0.0);
1872 XSync (si->dpy, False);
1875 /* Swallow all pending KeyPress/KeyRelease events. */
1878 while (XCheckMaskEvent (si->dpy, KeyPressMask|KeyReleaseMask, &e))
1886 handle_typeahead (saver_info *si)
1888 passwd_dialog_data *pw = si->pw_data;
1890 if (!si->unlock_typeahead)
1893 pw->passwd_changed_p = True;
1895 i = strlen (si->unlock_typeahead);
1896 if (i >= sizeof(pw->typed_passwd) - 1)
1897 i = sizeof(pw->typed_passwd) - 1;
1899 memcpy (pw->typed_passwd, si->unlock_typeahead, i);
1900 pw->typed_passwd [i] = 0;
1902 memset (si->unlock_typeahead, '*', strlen(si->unlock_typeahead));
1903 si->unlock_typeahead[i] = 0;
1904 update_passwd_window (si, si->unlock_typeahead, pw->ratio);
1906 free (si->unlock_typeahead);
1907 si->unlock_typeahead = 0;
1912 * Returns a copy of the input string with trailing whitespace removed.
1913 * Whitespace is anything considered so by isspace().
1914 * It is safe to call this with NULL, in which case NULL will be returned.
1915 * The returned string (if not NULL) should be freed by the caller with free().
1918 remove_trailing_whitespace(const char *str)
1928 newstr = malloc(len + 1);
1929 (void) strcpy(newstr, str);
1935 while (isspace(*--chr) && chr >= newstr)
1943 * The authentication conversation function.
1944 * Like a PAM conversation function, this accepts multiple messages in a single
1945 * round. It then splits them into individual messages for display on the
1946 * passwd dialog. A message sequence of info or error followed by a prompt will
1947 * be reduced into a single dialog window.
1949 * Returns 0 on success or -1 if some problem occurred (cancelled auth, OOM, ...)
1952 gui_auth_conv(int num_msg,
1953 const struct auth_message auth_msgs[],
1954 struct auth_response **resp,
1958 const char *info_msg, *prompt;
1959 struct auth_response *responses;
1961 if (!(responses = calloc(num_msg, sizeof(struct auth_response))))
1964 for (i = 0; i < num_msg; ++i)
1966 info_msg = prompt = NULL;
1968 /* See if there is a following message that can be shown at the same
1970 if (auth_msgs[i].type == AUTH_MSGTYPE_INFO
1972 && ( auth_msgs[i+1].type == AUTH_MSGTYPE_PROMPT_NOECHO
1973 || auth_msgs[i+1].type == AUTH_MSGTYPE_PROMPT_ECHO)
1976 info_msg = auth_msgs[i].msg;
1977 prompt = auth_msgs[++i].msg;
1981 if ( auth_msgs[i].type == AUTH_MSGTYPE_INFO
1982 || auth_msgs[i].type == AUTH_MSGTYPE_ERROR)
1983 info_msg = auth_msgs[i].msg;
1985 prompt = auth_msgs[i].msg;
1989 char *info_msg_trimmed, *prompt_trimmed;
1991 /* Trailing whitespace looks bad in a GUI */
1992 info_msg_trimmed = remove_trailing_whitespace(info_msg);
1993 prompt_trimmed = remove_trailing_whitespace(prompt);
1995 make_passwd_window(si, info_msg_trimmed, prompt_trimmed,
1996 auth_msgs[i].type == AUTH_MSGTYPE_PROMPT_ECHO
1999 if (info_msg_trimmed)
2000 free(info_msg_trimmed);
2003 free(prompt_trimmed);
2006 compose_status = calloc (1, sizeof (*compose_status));
2007 if (!compose_status)
2010 si->unlock_state = ul_read;
2012 handle_typeahead (si);
2013 passwd_event_loop (si);
2015 if (si->unlock_state == ul_cancel)
2018 responses[i].response = strdup(si->pw_data->typed_passwd);
2020 /* Cache the first response to a PROMPT_NOECHO to save prompting for
2021 * each auth mechanism. */
2022 if (si->cached_passwd == NULL &&
2023 auth_msgs[i].type == AUTH_MSGTYPE_PROMPT_NOECHO)
2024 si->cached_passwd = strdup(responses[i].response);
2026 free (compose_status);
2032 return (si->unlock_state == ul_finished) ? 0 : -1;
2036 free (compose_status);
2040 for (i = 0; i < num_msg; ++i)
2041 if (responses[i].response)
2042 free (responses[i].response);
2051 auth_finished_cb (saver_info *si)
2053 if (si->unlock_state == ul_fail)
2055 make_passwd_window (si, "Authentication failed!", NULL, True);
2056 sleep (2); /* Not very nice, I know */
2058 /* Swallow any keyboard or mouse events that were received while the
2062 long mask = (KeyPressMask | KeyReleaseMask |
2063 ButtonPressMask | ButtonReleaseMask);
2064 while (XCheckMaskEvent (si->dpy, mask, &e))
2069 destroy_passwd_window (si);
2074 unlock_p (saver_info *si)
2076 saver_preferences *p = &si->prefs;
2080 fprintf(stderr, "%s: Error: no unlock function specified!\n", blurb());
2084 raise_window (si, True, True, True);
2087 fprintf (stderr, "%s: prompting for password.\n", blurb());
2089 xss_authenticate(si, p->verbose_p);
2091 return (si->unlock_state == ul_success);
2096 set_locked_p (saver_info *si, Bool locked_p)
2098 si->locked_p = locked_p;
2100 #ifdef HAVE_XHPDISABLERESET
2101 hp_lock_reset (si, locked_p); /* turn off/on C-Sh-Reset */
2103 #ifdef HAVE_XF86VMODE
2104 xfree_lock_mode_switch (si, locked_p); /* turn off/on C-Alt-Plus */
2106 #ifdef HAVE_XF86MISCSETGRABKEYSSTATE
2107 xfree_lock_grab_smasher (si, locked_p); /* turn off/on C-Alt-KP-*,/ */
2110 store_saver_status (si); /* store locked-p */
2114 #else /* NO_LOCKING -- whole file */
2117 set_locked_p (saver_info *si, Bool locked_p)
2119 if (locked_p) abort();
2122 #endif /* !NO_LOCKING */