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 =
1080 XCreatePixmap (si->dpy, si->passwd_dialog,
1081 rects[0].width, rects[0].height,
1082 DefaultDepthOfScreen (pw->prompt_screen->screen));
1084 XFillRectangle (si->dpy, pw->user_entry_pixmap, gc2,
1085 0, 0, rects[0].width, rects[0].height);
1087 XDrawString (si->dpy, pw->user_entry_pixmap, gc1,
1089 pw->passwd_font->ascent,
1090 pw->passwd_string, strlen(pw->passwd_string));
1092 /* Ensure the new pixmap gets copied to the window */
1099 if (pw->i_beam == 0)
1101 /* Make the I-beam disappear */
1102 XCopyArea(si->dpy, pw->user_entry_pixmap, si->passwd_dialog, gc2,
1103 0, 0, rects[0].width, rects[0].height,
1104 rects[0].x, rects[0].y);
1106 else if (pw->i_beam == 1)
1108 /* Make the I-beam appear */
1109 x = (rects[0].x + pw->shadow_width +
1110 string_width (pw->passwd_font, pw->passwd_string));
1111 y = rects[0].y + pw->shadow_width;
1113 if (x > rects[0].x + rects[0].width - 1)
1114 x = rects[0].x + rects[0].width - 1;
1115 XDrawLine (si->dpy, si->passwd_dialog, gc1,
1117 x, y + pw->passwd_font->ascent + pw->passwd_font->descent-1);
1120 pw->i_beam = (pw->i_beam + 1) % 4;
1126 y = (pw->thermo_field_height - 2) * (1.0 - pw->ratio);
1129 XFillRectangle (si->dpy, si->passwd_dialog, gc2,
1130 pw->thermo_field_x + 1,
1131 pw->thermo_field_y + 1,
1134 XSetForeground (si->dpy, gc1, pw->thermo_foreground);
1135 XFillRectangle (si->dpy, si->passwd_dialog, gc1,
1136 pw->thermo_field_x + 1,
1137 pw->thermo_field_y + 1 + y,
1139 MAX (0, pw->thermo_field_height - y - 2));
1142 if (pw->button_state_changed_p)
1144 pw->button_state_changed_p = False;
1146 /* The "Unlock" button
1148 draw_button(si->dpy, si->passwd_dialog, pw->button_font,
1149 pw->button_foreground, pw->button_background,
1151 pw->unlock_button_x, pw->unlock_button_y,
1152 pw->unlock_button_width, pw->unlock_button_height,
1154 (pw->unlock_button_down_p ? pw->shadow_bottom : pw->shadow_top),
1155 (pw->unlock_button_down_p ? pw->shadow_top : pw->shadow_bottom),
1156 pw->unlock_button_down_p);
1158 /* The "New Login" button
1160 if (pw->login_button_p)
1162 draw_button(si->dpy, si->passwd_dialog, pw->button_font,
1163 (pw->login_button_enabled_p
1164 ? pw->passwd_foreground
1165 : pw->shadow_bottom),
1166 pw->button_background,
1168 pw->login_button_x, pw->login_button_y,
1169 pw->login_button_width, pw->login_button_height,
1171 (pw->login_button_down_p
1174 (pw->login_button_down_p
1176 : pw->shadow_bottom),
1177 pw->login_button_down_p);
1181 XFreeGC (si->dpy, gc1);
1182 XFreeGC (si->dpy, gc2);
1183 XSync (si->dpy, False);
1188 restore_background (saver_info *si)
1190 passwd_dialog_data *pw = si->pw_data;
1191 saver_screen_info *ssi = pw->prompt_screen;
1195 gcv.function = GXcopy;
1197 gc = XCreateGC (si->dpy, ssi->screensaver_window, GCFunction, &gcv);
1199 XCopyArea (si->dpy, pw->save_under,
1200 ssi->screensaver_window, gc,
1202 ssi->width, ssi->height,
1205 XFreeGC (si->dpy, gc);
1209 /* Frees anything created by make_passwd_window */
1211 cleanup_passwd_window (saver_info *si)
1213 passwd_dialog_data *pw;
1215 if (!(pw = si->pw_data))
1220 mlstring_free(pw->info_label);
1224 if (pw->prompt_label)
1226 mlstring_free(pw->prompt_label);
1227 pw->prompt_label = 0;
1230 memset (pw->typed_passwd, 0, sizeof(pw->typed_passwd));
1231 memset (pw->passwd_string, 0, strlen(pw->passwd_string));
1235 XtRemoveTimeOut (pw->timer);
1239 if (pw->user_entry_pixmap)
1241 XFreePixmap(si->dpy, pw->user_entry_pixmap);
1242 pw->user_entry_pixmap = 0;
1248 destroy_passwd_window (saver_info *si)
1250 saver_preferences *p = &si->prefs;
1251 passwd_dialog_data *pw = si->pw_data;
1252 saver_screen_info *ssi = pw->prompt_screen;
1253 Colormap cmap = DefaultColormapOfScreen (ssi->screen);
1254 Pixel black = BlackPixelOfScreen (ssi->screen);
1255 Pixel white = WhitePixelOfScreen (ssi->screen);
1258 cleanup_passwd_window (si);
1260 if (si->cached_passwd)
1262 char *wipe = si->cached_passwd;
1267 free(si->cached_passwd);
1268 si->cached_passwd = NULL;
1271 move_mouse_grab (si, RootWindowOfScreen (ssi->screen),
1272 ssi->cursor, ssi->number);
1274 if (pw->passwd_cursor)
1275 XFreeCursor (si->dpy, pw->passwd_cursor);
1278 fprintf (stderr, "%s: %d: moving mouse back to %d,%d.\n",
1279 blurb(), ssi->number,
1280 pw->previous_mouse_x, pw->previous_mouse_y);
1282 XWarpPointer (si->dpy, None, RootWindowOfScreen (ssi->screen),
1284 pw->previous_mouse_x, pw->previous_mouse_y);
1286 while (XCheckMaskEvent (si->dpy, PointerMotionMask, &event))
1288 fprintf (stderr, "%s: discarding MotionNotify event.\n", blurb());
1290 if (si->passwd_dialog)
1292 XDestroyWindow (si->dpy, si->passwd_dialog);
1293 si->passwd_dialog = 0;
1298 restore_background(si);
1299 XFreePixmap (si->dpy, pw->save_under);
1303 if (pw->heading_label) free (pw->heading_label);
1304 if (pw->body_label) free (pw->body_label);
1305 if (pw->user_label) free (pw->user_label);
1306 if (pw->date_label) free (pw->date_label);
1307 if (pw->login_label) free (pw->login_label);
1308 if (pw->unlock_label) free (pw->unlock_label);
1309 if (pw->passwd_string) free (pw->passwd_string);
1310 if (pw->uname_label) free (pw->uname_label);
1312 if (pw->heading_font) XFreeFont (si->dpy, pw->heading_font);
1313 if (pw->body_font) XFreeFont (si->dpy, pw->body_font);
1314 if (pw->label_font) XFreeFont (si->dpy, pw->label_font);
1315 if (pw->passwd_font) XFreeFont (si->dpy, pw->passwd_font);
1316 if (pw->date_font) XFreeFont (si->dpy, pw->date_font);
1317 if (pw->button_font) XFreeFont (si->dpy, pw->button_font);
1318 if (pw->uname_font) XFreeFont (si->dpy, pw->uname_font);
1320 if (pw->foreground != black && pw->foreground != white)
1321 XFreeColors (si->dpy, cmap, &pw->foreground, 1, 0L);
1322 if (pw->background != black && pw->background != white)
1323 XFreeColors (si->dpy, cmap, &pw->background, 1, 0L);
1324 if (!(pw->button_foreground == black || pw->button_foreground == white))
1325 XFreeColors (si->dpy, cmap, &pw->button_foreground, 1, 0L);
1326 if (!(pw->button_background == black || pw->button_background == white))
1327 XFreeColors (si->dpy, cmap, &pw->button_background, 1, 0L);
1328 if (pw->passwd_foreground != black && pw->passwd_foreground != white)
1329 XFreeColors (si->dpy, cmap, &pw->passwd_foreground, 1, 0L);
1330 if (pw->passwd_background != black && pw->passwd_background != white)
1331 XFreeColors (si->dpy, cmap, &pw->passwd_background, 1, 0L);
1332 if (pw->thermo_foreground != black && pw->thermo_foreground != white)
1333 XFreeColors (si->dpy, cmap, &pw->thermo_foreground, 1, 0L);
1334 if (pw->thermo_background != black && pw->thermo_background != white)
1335 XFreeColors (si->dpy, cmap, &pw->thermo_background, 1, 0L);
1336 if (pw->shadow_top != black && pw->shadow_top != white)
1337 XFreeColors (si->dpy, cmap, &pw->shadow_top, 1, 0L);
1338 if (pw->shadow_bottom != black && pw->shadow_bottom != white)
1339 XFreeColors (si->dpy, cmap, &pw->shadow_bottom, 1, 0L);
1341 if (pw->logo_pixmap)
1342 XFreePixmap (si->dpy, pw->logo_pixmap);
1343 if (pw-> logo_clipmask)
1344 XFreePixmap (si->dpy, pw->logo_clipmask);
1345 if (pw->logo_pixels)
1347 if (pw->logo_npixels)
1348 XFreeColors (si->dpy, cmap, pw->logo_pixels, pw->logo_npixels, 0L);
1349 free (pw->logo_pixels);
1350 pw->logo_pixels = 0;
1351 pw->logo_npixels = 0;
1355 XFreePixmap (si->dpy, pw->save_under);
1358 XInstallColormap (si->dpy, cmap);
1360 memset (pw, 0, sizeof(*pw));
1366 static Bool error_handler_hit_p = False;
1369 ignore_all_errors_ehandler (Display *dpy, XErrorEvent *error)
1371 error_handler_hit_p = True;
1376 #ifdef HAVE_XHPDISABLERESET
1377 /* This function enables and disables the C-Sh-Reset hot-key, which
1378 normally resets the X server (logging out the logged-in user.)
1379 We don't want random people to be able to do that while the
1383 hp_lock_reset (saver_info *si, Bool lock_p)
1385 static Bool hp_locked_p = False;
1387 /* Calls to XHPDisableReset and XHPEnableReset must be balanced,
1388 or BadAccess errors occur. (It's ok for this to be global,
1389 since it affects the whole machine, not just the current screen.)
1391 if (hp_locked_p == lock_p)
1395 XHPDisableReset (si->dpy);
1397 XHPEnableReset (si->dpy);
1398 hp_locked_p = lock_p;
1400 #endif /* HAVE_XHPDISABLERESET */
1403 #ifdef HAVE_XF86MISCSETGRABKEYSSTATE
1405 /* This function enables and disables the Ctrl-Alt-KP_star and
1406 Ctrl-Alt-KP_slash hot-keys, which (in XFree86 4.2) break any
1407 grabs and/or kill the grabbing client. That would effectively
1408 unlock the screen, so we don't like that.
1410 The Ctrl-Alt-KP_star and Ctrl-Alt-KP_slash hot-keys only exist
1411 if AllowDeactivateGrabs and/or AllowClosedownGrabs are turned on
1412 in XF86Config. I believe they are disabled by default.
1414 This does not affect any other keys (specifically Ctrl-Alt-BS or
1415 Ctrl-Alt-F1) but I wish it did. Maybe it will someday.
1418 xfree_lock_grab_smasher (saver_info *si, Bool lock_p)
1420 saver_preferences *p = &si->prefs;
1423 XErrorHandler old_handler;
1424 XSync (si->dpy, False);
1425 error_handler_hit_p = False;
1426 old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1427 XSync (si->dpy, False);
1428 status = XF86MiscSetGrabKeysState (si->dpy, !lock_p);
1429 XSync (si->dpy, False);
1430 if (error_handler_hit_p) status = 666;
1432 if (!lock_p && status == MiscExtGrabStateAlready)
1433 status = MiscExtGrabStateSuccess; /* shut up, consider this success */
1435 if (p->verbose_p && status != MiscExtGrabStateSuccess)
1436 fprintf (stderr, "%s: error: XF86MiscSetGrabKeysState(%d) returned %s\n",
1438 (status == MiscExtGrabStateSuccess ? "MiscExtGrabStateSuccess" :
1439 status == MiscExtGrabStateLocked ? "MiscExtGrabStateLocked" :
1440 status == MiscExtGrabStateAlready ? "MiscExtGrabStateAlready" :
1441 status == 666 ? "an X error" :
1444 XSync (si->dpy, False);
1445 XSetErrorHandler (old_handler);
1446 XSync (si->dpy, False);
1448 #endif /* HAVE_XF86MISCSETGRABKEYSSTATE */
1452 /* This function enables and disables the C-Alt-Plus and C-Alt-Minus
1453 hot-keys, which normally change the resolution of the X server.
1454 We don't want people to be able to switch the server resolution
1455 while the screen is locked, because if they switch to a higher
1456 resolution, it could cause part of the underlying desktop to become
1459 #ifdef HAVE_XF86VMODE
1462 xfree_lock_mode_switch (saver_info *si, Bool lock_p)
1464 static Bool any_mode_locked_p = False;
1465 saver_preferences *p = &si->prefs;
1469 XErrorHandler old_handler;
1471 if (any_mode_locked_p == lock_p)
1473 if (!XF86VidModeQueryExtension (si->dpy, &event, &error))
1476 for (screen = 0; screen < (si->xinerama_p ? 1 : si->nscreens); screen++)
1478 XSync (si->dpy, False);
1479 old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1480 error_handler_hit_p = False;
1481 status = XF86VidModeLockModeSwitch (si->dpy, screen, lock_p);
1482 XSync (si->dpy, False);
1483 XSetErrorHandler (old_handler);
1484 if (error_handler_hit_p) status = False;
1487 any_mode_locked_p = lock_p;
1489 if (!status && (p->verbose_p || !lock_p))
1490 /* Only print this when verbose, or when we locked but can't unlock.
1491 I tried printing this message whenever it comes up, but
1492 mode-locking always fails if DontZoom is set in XF86Config. */
1493 fprintf (stderr, "%s: %d: unable to %s mode switching!\n",
1494 blurb(), screen, (lock_p ? "lock" : "unlock"));
1495 else if (p->verbose_p)
1496 fprintf (stderr, "%s: %d: %s mode switching.\n",
1497 blurb(), screen, (lock_p ? "locked" : "unlocked"));
1500 #endif /* HAVE_XF86VMODE */
1503 /* If the viewport has been scrolled since the screen was blanked,
1504 then scroll it back to where it belongs. This function only exists
1505 to patch over a very brief race condition.
1508 undo_vp_motion (saver_info *si)
1510 #ifdef HAVE_XF86VMODE
1511 saver_preferences *p = &si->prefs;
1515 if (!XF86VidModeQueryExtension (si->dpy, &event, &error))
1518 for (screen = 0; screen < si->nscreens; screen++)
1520 saver_screen_info *ssi = &si->screens[screen];
1524 if (ssi->blank_vp_x == -1 && ssi->blank_vp_y == -1)
1526 if (!XF86VidModeGetViewPort (si->dpy, screen, &x, &y))
1528 if (ssi->blank_vp_x == x && ssi->blank_vp_y == y)
1531 /* We're going to move the viewport. The mouse has just been grabbed on
1532 (and constrained to, thus warped to) the password window, so it is no
1533 longer near the edge of the screen. However, wait a bit anyway, just
1534 to make sure the server drains its last motion event, so that the
1535 screen doesn't continue to scroll after we've reset the viewport.
1537 XSync (si->dpy, False);
1538 usleep (250000); /* 1/4 second */
1539 XSync (si->dpy, False);
1541 status = XF86VidModeSetViewPort (si->dpy, screen,
1542 ssi->blank_vp_x, ssi->blank_vp_y);
1546 "%s: %d: unable to move vp from (%d,%d) back to (%d,%d)!\n",
1547 blurb(), screen, x, y, ssi->blank_vp_x, ssi->blank_vp_y);
1548 else if (p->verbose_p)
1550 "%s: %d: vp moved to (%d,%d); moved it back to (%d,%d).\n",
1551 blurb(), screen, x, y, ssi->blank_vp_x, ssi->blank_vp_y);
1553 #endif /* HAVE_XF86VMODE */
1562 passwd_animate_timer (XtPointer closure, XtIntervalId *id)
1564 saver_info *si = (saver_info *) closure;
1566 passwd_dialog_data *pw = si->pw_data;
1570 pw->ratio -= (1.0 / ((double) si->prefs.passwd_timeout / (double) tick));
1574 if (si->unlock_state == ul_read)
1575 si->unlock_state = ul_time;
1578 update_passwd_window (si, 0, pw->ratio);
1580 if (si->unlock_state == ul_read)
1581 pw->timer = XtAppAddTimeOut (si->app, tick, passwd_animate_timer,
1586 idle_timer ((XtPointer) si, 0);
1590 static XComposeStatus *compose_status;
1593 handle_login_button (saver_info *si, XEvent *event)
1595 saver_preferences *p = &si->prefs;
1596 Bool mouse_in_box = False;
1598 passwd_dialog_data *pw = si->pw_data;
1599 saver_screen_info *ssi = pw->prompt_screen;
1601 if (! pw->login_button_enabled_p)
1605 (event->xbutton.x >= pw->login_button_x &&
1606 event->xbutton.x <= pw->login_button_x + pw->login_button_width &&
1607 event->xbutton.y >= pw->login_button_y &&
1608 event->xbutton.y <= pw->login_button_y + pw->login_button_height);
1610 if (ButtonRelease == event->xany.type &&
1611 pw->login_button_down_p &&
1614 /* Only allow them to press the button once: don't want to
1615 accidentally launch a dozen gdm choosers if the machine
1619 pw->login_button_enabled_p = False;
1622 pw->login_button_down_p = (mouse_in_box &&
1623 ButtonRelease != event->xany.type);
1625 update_passwd_window (si, 0, pw->ratio);
1628 fork_and_exec (ssi, p->new_login_command);
1633 handle_unlock_button (saver_info *si, XEvent *event)
1635 Bool mouse_in_box = False;
1636 passwd_dialog_data *pw = si->pw_data;
1639 (event->xbutton.x >= pw->unlock_button_x &&
1640 event->xbutton.x <= pw->unlock_button_x + pw->unlock_button_width &&
1641 event->xbutton.y >= pw->unlock_button_y &&
1642 event->xbutton.y <= pw->unlock_button_y + pw->unlock_button_height);
1644 if (ButtonRelease == event->xany.type &&
1645 pw->unlock_button_down_p &&
1647 finished_typing_passwd (si, pw);
1649 pw->unlock_button_down_p = (mouse_in_box &&
1650 ButtonRelease != event->xany.type);
1655 finished_typing_passwd (saver_info *si, passwd_dialog_data *pw)
1657 if (si->unlock_state == ul_read)
1659 update_passwd_window (si, "Checking...", pw->ratio);
1660 XSync (si->dpy, False);
1662 si->unlock_state = ul_finished;
1663 update_passwd_window (si, "", pw->ratio);
1668 handle_passwd_key (saver_info *si, XKeyEvent *event)
1670 passwd_dialog_data *pw = si->pw_data;
1671 int pw_size = sizeof (pw->typed_passwd) - 1;
1672 char *typed_passwd = pw->typed_passwd;
1676 int size = XLookupString (event, s, 1, 0, compose_status);
1678 if (size != 1) return;
1682 pw->passwd_changed_p = True;
1684 /* Add 10% to the time remaining every time a key is pressed. */
1686 if (pw->ratio > 1) pw->ratio = 1;
1690 case '\010': case '\177': /* Backspace */
1694 typed_passwd [strlen(typed_passwd)-1] = 0;
1697 case '\025': case '\030': /* Erase line */
1698 memset (typed_passwd, 0, pw_size);
1701 case '\012': case '\015': /* Enter */
1702 finished_typing_passwd(si, pw);
1705 case '\033': /* Escape */
1706 si->unlock_state = ul_cancel;
1710 /* Though technically the only illegal characters in Unix passwords
1711 are LF and NUL, most GUI programs (e.g., GDM) use regular text-entry
1712 fields that only let you type printable characters. So, people
1713 who use funky characters in their passwords are already broken.
1714 We follow that precedent.
1716 if (isprint ((unsigned char) *s))
1718 i = strlen (typed_passwd);
1723 typed_passwd [i] = *s;
1724 typed_passwd [i+1] = 0;
1734 /* If the input is wider than the text box, only show the last portion.
1735 * This simulates a horizontally scrolling text field. */
1736 int chars_in_pwfield = (pw->passwd_field_width /
1737 pw->passwd_font->max_bounds.width);
1739 if (strlen(typed_passwd) > chars_in_pwfield)
1740 typed_passwd += (strlen(typed_passwd) - chars_in_pwfield);
1742 update_passwd_window(si, typed_passwd, pw->ratio);
1744 else if (pw->show_stars_p)
1746 i = strlen(typed_passwd);
1747 stars = (char *) malloc(i+1);
1748 memset (stars, '*', i);
1750 update_passwd_window (si, stars, pw->ratio);
1755 update_passwd_window (si, "", pw->ratio);
1761 passwd_event_loop (saver_info *si)
1763 saver_preferences *p = &si->prefs;
1766 unsigned int caps_p = 0;
1768 passwd_animate_timer ((XtPointer) si, 0);
1770 while (si->unlock_state == ul_read)
1772 XtAppNextEvent (si->app, &event);
1773 if (event.xany.window == si->passwd_dialog && event.xany.type == Expose)
1774 draw_passwd_window (si);
1775 else if (event.xany.type == KeyPress)
1777 handle_passwd_key (si, &event.xkey);
1778 caps_p = (event.xkey.state & LockMask);
1780 else if (event.xany.type == ButtonPress ||
1781 event.xany.type == ButtonRelease)
1783 si->pw_data->button_state_changed_p = True;
1784 handle_unlock_button (si, &event);
1785 if (si->pw_data->login_button_p)
1786 handle_login_button (si, &event);
1789 XtDispatchEvent (&event);
1792 switch (si->unlock_state)
1794 case ul_cancel: msg = ""; break;
1795 case ul_time: msg = "Timed out!"; break;
1796 case ul_finished: msg = "Checking..."; break;
1797 default: msg = 0; break;
1800 if (si->unlock_state == ul_fail)
1801 si->unlock_failures++;
1804 switch (si->unlock_state)
1807 fprintf (stderr, "%s: auth/input incorrect!%s\n", blurb(),
1808 (caps_p ? " (CapsLock)" : ""));
1811 fprintf (stderr, "%s: input cancelled.\n", blurb()); break;
1813 fprintf (stderr, "%s: input timed out.\n", blurb()); break;
1815 fprintf (stderr, "%s: input finished.\n", blurb()); break;
1820 if (si->unlock_state == ul_fail)
1822 /* If they typed a password (as opposed to just hitting return) and
1823 the password was invalid, log it.
1825 struct passwd *pw = getpwuid (getuid ());
1826 char *d = DisplayString (si->dpy);
1827 char *u = (pw && pw->pw_name ? pw->pw_name : "???");
1835 # if defined(LOG_AUTHPRIV)
1837 # elif defined(LOG_AUTH)
1844 openlog (progname, opt, fac);
1845 syslog (LOG_NOTICE, "FAILED LOGIN %d ON DISPLAY \"%s\", FOR \"%s\"",
1846 si->unlock_failures, d, u);
1849 #endif /* HAVE_SYSLOG */
1851 if (si->unlock_state == ul_fail)
1852 XBell (si->dpy, False);
1854 if (si->unlock_state == ul_success && si->unlock_failures != 0)
1856 if (si->unlock_failures == 1)
1857 fprintf (real_stderr,
1858 "%s: WARNING: 1 failed attempt to unlock the screen.\n",
1861 fprintf (real_stderr,
1862 "%s: WARNING: %d failed attempts to unlock the screen.\n",
1863 blurb(), si->unlock_failures);
1864 fflush (real_stderr);
1866 si->unlock_failures = 0;
1871 si->pw_data->i_beam = 0;
1872 update_passwd_window (si, msg, 0.0);
1873 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))
2070 destroy_passwd_window (si);
2075 unlock_p (saver_info *si)
2077 saver_preferences *p = &si->prefs;
2081 fprintf(stderr, "%s: Error: no unlock function specified!\n", blurb());
2085 raise_window (si, True, True, True);
2088 fprintf (stderr, "%s: prompting for password.\n", blurb());
2090 xss_authenticate(si, p->verbose_p);
2092 return (si->unlock_state == ul_success);
2097 set_locked_p (saver_info *si, Bool locked_p)
2099 si->locked_p = locked_p;
2101 #ifdef HAVE_XHPDISABLERESET
2102 hp_lock_reset (si, locked_p); /* turn off/on C-Sh-Reset */
2104 #ifdef HAVE_XF86VMODE
2105 xfree_lock_mode_switch (si, locked_p); /* turn off/on C-Alt-Plus */
2107 #ifdef HAVE_XF86MISCSETGRABKEYSSTATE
2108 xfree_lock_grab_smasher (si, locked_p); /* turn off/on C-Alt-KP-*,/ */
2111 store_saver_status (si); /* store locked-p */
2115 #else /* NO_LOCKING -- whole file */
2118 set_locked_p (saver_info *si, Bool locked_p)
2120 if (locked_p) abort();
2123 #endif /* !NO_LOCKING */