1 /* lock.c --- handling the password dialog for locking-mode.
2 * xscreensaver, Copyright (c) 1993-2008 Jamie Zawinski <jwz@jwz.org>
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that
7 * copyright notice and this permission notice appear in supporting
8 * documentation. No representations are made about the suitability of this
9 * software for any purpose. It is provided "as is" without express or
13 /* Athena locking code contributed by Jon A. Christopher <jac8782@tamu.edu> */
14 /* Copyright 1997, with the same permissions as above. */
21 #include <X11/Intrinsic.h>
22 #include <X11/cursorfont.h>
23 #include <X11/Xos.h> /* for time() */
26 #include "xscreensaver.h"
27 #include "resources.h"
31 #ifndef NO_LOCKING /* (mostly) whole file */
33 #ifdef HAVE_XHPDISABLERESET
34 # include <X11/XHPlib.h>
35 static void hp_lock_reset (saver_info *si, Bool lock_p);
36 #endif /* HAVE_XHPDISABLERESET */
39 # include <X11/extensions/xf86vmode.h>
40 static void xfree_lock_mode_switch (saver_info *si, Bool lock_p);
41 #endif /* HAVE_XF86VMODE */
43 #ifdef HAVE_XF86MISCSETGRABKEYSSTATE
44 # include <X11/extensions/xf86misc.h>
45 static void xfree_lock_grab_smasher (saver_info *si, Bool lock_p);
46 #endif /* HAVE_XF86MISCSETGRABKEYSSTATE */
49 # include <X11/extensions/Xrandr.h>
50 #endif /* HAVE_RANDR */
53 ERROR! You must not include vroot.h in this file.
57 # include <sys/utsname.h> /* for hostname info */
58 #endif /* HAVE_UNAME */
65 extern char *getenv(const char *name);
66 extern int validate_user(char *name, char *password);
69 vms_passwd_valid_p(char *pw, Bool verbose_p)
71 return (validate_user (getenv("USER"), typed_passwd) == 1);
73 # undef passwd_valid_p
74 # define passwd_valid_p vms_passwd_valid_p
78 #define SAMPLE_INPUT "MMMMMMMMMMMM"
82 #define MAX(a,b) ((a)>(b)?(a):(b))
84 typedef struct info_dialog_data info_dialog_data;
86 struct passwd_dialog_data {
88 saver_screen_info *prompt_screen;
89 int previous_mouse_x, previous_mouse_y;
91 char typed_passwd [80];
99 Dimension border_width;
102 Bool show_stars_p; /* "I regret that I have but one asterisk for my country."
103 -- Nathan Hale, 1776. */
108 mlstring *info_label;
109 /* The entry field shall only be displayed if prompt_label is not NULL */
110 mlstring *prompt_label;
113 Bool passwd_changed_p; /* Whether the user entry field needs redrawing */
114 Bool caps_p; /* Whether we saw a keypress with caps-lock on */
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;
207 screen = pw->prompt_screen->screen;
208 cmap = DefaultColormapOfScreen (screen);
210 pw->show_stars_p = get_boolean_resource(si->dpy, "passwd.asterisks",
213 pw->heading_label = get_string_resource (si->dpy, "passwd.heading.label",
214 "Dialog.Label.Label");
215 pw->body_label = get_string_resource (si->dpy, "passwd.body.label",
216 "Dialog.Label.Label");
217 pw->user_label = get_string_resource (si->dpy, "passwd.user.label",
218 "Dialog.Label.Label");
219 pw->unlock_label = get_string_resource (si->dpy, "passwd.unlock.label",
220 "Dialog.Button.Label");
221 pw->login_label = get_string_resource (si->dpy, "passwd.login.label",
222 "Dialog.Button.Label");
224 pw->date_label = get_string_resource (si->dpy, "dateFormat", "DateFormat");
226 if (!pw->heading_label)
227 pw->heading_label = strdup("ERROR: RESOURCES NOT INSTALLED CORRECTLY");
229 pw->body_label = strdup("ERROR: RESOURCES NOT INSTALLED CORRECTLY");
230 if (!pw->user_label) pw->user_label = strdup("ERROR");
231 if (!pw->date_label) pw->date_label = strdup("ERROR");
232 if (!pw->unlock_label) pw->unlock_label = strdup("ERROR (UNLOCK)");
233 if (!pw->login_label) pw->login_label = strdup ("ERROR (LOGIN)") ;
235 /* Put the version number in the label. */
237 char *s = (char *) malloc (strlen(pw->heading_label) + 20);
238 sprintf(s, pw->heading_label, si->version);
239 free (pw->heading_label);
240 pw->heading_label = s;
243 /* Get hostname info */
244 pw->uname_label = strdup(""); /* Initialy, write nothing */
250 if (uname (&uts) == 0)
252 #if 0 /* Get the full hostname */
255 if ((s = strchr(uts.nodename, '.')))
259 char *s = strdup (uts.nodename);
260 free (pw->uname_label);
266 pw->passwd_string = strdup("");
268 f = get_string_resource (si->dpy, "passwd.headingFont", "Dialog.Font");
269 pw->heading_font = XLoadQueryFont (si->dpy, (f ? f : "fixed"));
270 if (!pw->heading_font) pw->heading_font = XLoadQueryFont (si->dpy, "fixed");
273 f = get_string_resource (si->dpy, "passwd.buttonFont", "Dialog.Font");
274 pw->button_font = XLoadQueryFont (si->dpy, (f ? f : "fixed"));
275 if (!pw->button_font) pw->button_font = XLoadQueryFont (si->dpy, "fixed");
278 f = get_string_resource(si->dpy, "passwd.bodyFont", "Dialog.Font");
279 pw->body_font = XLoadQueryFont (si->dpy, (f ? f : "fixed"));
280 if (!pw->body_font) pw->body_font = XLoadQueryFont (si->dpy, "fixed");
283 f = get_string_resource(si->dpy, "passwd.labelFont", "Dialog.Font");
284 pw->label_font = XLoadQueryFont (si->dpy, (f ? f : "fixed"));
285 if (!pw->label_font) pw->label_font = XLoadQueryFont (si->dpy, "fixed");
288 f = get_string_resource(si->dpy, "passwd.passwdFont", "Dialog.Font");
289 pw->passwd_font = XLoadQueryFont (si->dpy, (f ? f : "fixed"));
290 if (!pw->passwd_font) pw->passwd_font = XLoadQueryFont (si->dpy, "fixed");
293 f = get_string_resource(si->dpy, "passwd.dateFont", "Dialog.Font");
294 pw->date_font = XLoadQueryFont (si->dpy, (f ? f : "fixed"));
295 if (!pw->date_font) pw->date_font = XLoadQueryFont (si->dpy, "fixed");
298 f = get_string_resource(si->dpy, "passwd.unameFont", "Dialog.Font");
299 pw->uname_font = XLoadQueryFont (si->dpy, (f ? f : "fixed"));
300 if (!pw->uname_font) pw->uname_font = XLoadQueryFont (si->dpy, "fixed");
303 pw->show_uname_p = get_boolean_resource(si->dpy, "passwd.uname", "Boolean");
305 pw->foreground = get_pixel_resource (si->dpy, cmap,
307 "Dialog.Foreground" );
308 pw->background = get_pixel_resource (si->dpy, cmap,
310 "Dialog.Background" );
312 if (pw->foreground == pw->background)
314 /* Make sure the error messages show up. */
315 pw->foreground = BlackPixelOfScreen (screen);
316 pw->background = WhitePixelOfScreen (screen);
319 pw->passwd_foreground = get_pixel_resource (si->dpy, cmap,
320 "passwd.text.foreground",
321 "Dialog.Text.Foreground" );
322 pw->passwd_background = get_pixel_resource (si->dpy, cmap,
323 "passwd.text.background",
324 "Dialog.Text.Background" );
325 pw->button_foreground = get_pixel_resource (si->dpy, cmap,
326 "splash.Button.foreground",
327 "Dialog.Button.Foreground" );
328 pw->button_background = get_pixel_resource (si->dpy, cmap,
329 "splash.Button.background",
330 "Dialog.Button.Background" );
331 pw->thermo_foreground = get_pixel_resource (si->dpy, cmap,
332 "passwd.thermometer.foreground",
333 "Dialog.Thermometer.Foreground" );
334 pw->thermo_background = get_pixel_resource ( si->dpy, cmap,
335 "passwd.thermometer.background",
336 "Dialog.Thermometer.Background" );
337 pw->shadow_top = get_pixel_resource ( si->dpy, cmap,
338 "passwd.topShadowColor",
339 "Dialog.Foreground" );
340 pw->shadow_bottom = get_pixel_resource (si->dpy, cmap,
341 "passwd.bottomShadowColor",
342 "Dialog.Background" );
344 pw->preferred_logo_width = get_integer_resource (si->dpy, "passwd.logo.width",
345 "Dialog.Logo.Width");
346 pw->preferred_logo_height = get_integer_resource (si->dpy, "passwd.logo.height",
347 "Dialog.Logo.Height");
348 pw->thermo_width = get_integer_resource (si->dpy, "passwd.thermometer.width",
349 "Dialog.Thermometer.Width");
350 pw->internal_border = get_integer_resource (si->dpy, "passwd.internalBorderWidth",
351 "Dialog.InternalBorderWidth");
352 pw->shadow_width = get_integer_resource (si->dpy, "passwd.shadowThickness",
353 "Dialog.ShadowThickness");
355 if (pw->preferred_logo_width == 0) pw->preferred_logo_width = 150;
356 if (pw->preferred_logo_height == 0) pw->preferred_logo_height = 150;
357 if (pw->internal_border == 0) pw->internal_border = 15;
358 if (pw->shadow_width == 0) pw->shadow_width = 4;
359 if (pw->thermo_width == 0) pw->thermo_width = pw->shadow_width;
362 /* We need to remember the mouse position and restore it afterward, or
363 sometimes (perhaps only with Xinerama?) the mouse gets warped to
364 inside the bounds of the lock dialog window.
367 Window pointer_root, pointer_child;
368 int root_x, root_y, win_x, win_y;
370 pw->previous_mouse_x = 0;
371 pw->previous_mouse_y = 0;
372 if (XQueryPointer (si->dpy, RootWindowOfScreen (pw->prompt_screen->screen),
373 &pointer_root, &pointer_child,
374 &root_x, &root_y, &win_x, &win_y, &mask))
376 pw->previous_mouse_x = root_x;
377 pw->previous_mouse_y = root_y;
378 if (si->prefs.verbose_p)
379 fprintf (stderr, "%s: %d: mouse is at %d,%d.\n",
380 blurb(), pw->prompt_screen->number,
381 pw->previous_mouse_x, pw->previous_mouse_y);
383 else if (si->prefs.verbose_p)
384 fprintf (stderr, "%s: %d: unable to determine mouse position?\n",
385 blurb(), pw->prompt_screen->number);
388 /* Before mapping the window, save a pixmap of the current screen.
389 When we lower the window, we
390 restore these bits. This works, because the running screenhack
391 has already been sent SIGSTOP, so we know nothing else is drawing
396 pw->save_under = XCreatePixmap (si->dpy,
397 pw->prompt_screen->screensaver_window,
398 pw->prompt_screen->width,
399 pw->prompt_screen->height,
400 pw->prompt_screen->current_depth);
401 gcv.function = GXcopy;
402 gc = XCreateGC (si->dpy, pw->save_under, GCFunction, &gcv);
403 XCopyArea (si->dpy, pw->prompt_screen->screensaver_window,
406 pw->prompt_screen->width, pw->prompt_screen->height,
408 XFreeGC (si->dpy, gc);
417 * info_msg and prompt may be NULL.
420 make_passwd_window (saver_info *si,
421 const char *info_msg,
425 XSetWindowAttributes attrs;
426 unsigned long attrmask = 0;
427 passwd_dialog_data *pw;
430 Dimension max_string_width_px;
431 saver_screen_info *ssi = &si->screens [mouse_screen (si)];
433 cleanup_passwd_window (si);
435 if (! ssi) /* WTF? Trying to prompt while no screens connected? */
439 if (new_passwd_window (si) < 0)
442 if (!(pw = si->pw_data))
447 pw->prompt_screen = ssi;
448 if (si->prefs.verbose_p)
449 fprintf (stderr, "%s: %d: creating password dialog (\"%s\")\n",
450 blurb(), pw->prompt_screen->number,
451 info_msg ? info_msg : "");
453 screen = pw->prompt_screen->screen;
454 cmap = DefaultColormapOfScreen (screen);
456 pw->echo_input = echo;
458 max_string_width_px = ssi->width
459 - pw->shadow_width * 4
460 - pw->border_width * 2
462 - pw->preferred_logo_width
463 - pw->internal_border * 2;
464 /* As the string wraps it makes the window taller which makes the logo wider
465 * which leaves less room for the text which makes the string wrap. Uh-oh, a
466 * loop. By wrapping at a bit less than the available width, there's some
467 * room for the dialog to grow without going off the edge of the screen. */
468 max_string_width_px *= 0.75;
470 pw->info_label = mlstring_new(info_msg ? info_msg : pw->body_label,
471 pw->label_font, max_string_width_px);
474 int direction, ascent, descent;
480 /* Measure the heading_label. */
481 XTextExtents (pw->heading_font,
482 pw->heading_label, strlen(pw->heading_label),
483 &direction, &ascent, &descent, &overall);
484 if (overall.width > pw->width) pw->width = overall.width;
485 pw->height += ascent + descent;
487 /* Measure the uname_label. */
488 if ((strlen(pw->uname_label)) && pw->show_uname_p)
490 XTextExtents (pw->uname_font,
491 pw->uname_label, strlen(pw->uname_label),
492 &direction, &ascent, &descent, &overall);
493 if (overall.width > pw->width) pw->width = overall.width;
494 pw->height += ascent + descent;
498 Dimension w2 = 0, w3 = 0, button_w = 0;
499 Dimension h2 = 0, h3 = 0, button_h = 0;
500 const char *passwd_string = SAMPLE_INPUT;
502 /* Measure the user_label. */
503 XTextExtents (pw->label_font,
504 pw->user_label, strlen(pw->user_label),
505 &direction, &ascent, &descent, &overall);
506 if (overall.width > w2) w2 = overall.width;
507 h2 += ascent + descent;
509 /* Measure the info_label. */
510 if (pw->info_label->overall_width > pw->width) pw->width = pw->info_label->overall_width;
511 h2 += pw->info_label->overall_height;
513 /* Measure the user string. */
514 XTextExtents (pw->passwd_font,
515 si->user, strlen(si->user),
516 &direction, &ascent, &descent, &overall);
517 overall.width += (pw->shadow_width * 4);
518 ascent += (pw->shadow_width * 4);
519 if (overall.width > w3) w3 = overall.width;
520 h3 += ascent + descent;
522 /* Measure the (dummy) passwd_string. */
525 XTextExtents (pw->passwd_font,
526 passwd_string, strlen(passwd_string),
527 &direction, &ascent, &descent, &overall);
528 overall.width += (pw->shadow_width * 4);
529 ascent += (pw->shadow_width * 4);
530 if (overall.width > w3) w3 = overall.width;
531 h3 += ascent + descent;
533 /* Measure the prompt_label. */
534 max_string_width_px -= w3;
535 pw->prompt_label = mlstring_new(prompt, pw->label_font, max_string_width_px);
537 if (pw->prompt_label->overall_width > w2) w2 = pw->prompt_label->overall_width;
539 h2 += pw->prompt_label->overall_height;
541 w2 = w2 + w3 + (pw->shadow_width * 2);
545 /* The "Unlock" button. */
546 XTextExtents (pw->label_font,
547 pw->unlock_label, strlen(pw->unlock_label),
548 &direction, &ascent, &descent, &overall);
549 button_w = overall.width;
550 button_h = ascent + descent;
552 /* Add some horizontal padding inside the button. */
555 button_w += ((ascent + descent) / 2) + (pw->shadow_width * 2);
556 button_h += ((ascent + descent) / 2) + (pw->shadow_width * 2);
558 pw->unlock_button_width = button_w;
559 pw->unlock_button_height = button_h;
561 w2 = MAX (w2, button_w);
562 h2 += button_h * 1.5;
564 /* The "New Login" button */
565 pw->login_button_width = 0;
566 pw->login_button_height = 0;
568 if (pw->login_button_p)
570 pw->login_button_enabled_p = True;
572 /* Measure the "New Login" button */
573 XTextExtents (pw->button_font, pw->login_label,
574 strlen (pw->login_label),
575 &direction, &ascent, &descent, &overall);
576 button_w = overall.width;
577 button_h = ascent + descent;
579 /* Add some horizontal padding inside the buttons. */
582 button_w += ((ascent + descent) / 2) + (pw->shadow_width * 2);
583 button_h += ((ascent + descent) / 2) + (pw->shadow_width * 2);
585 pw->login_button_width = button_w;
586 pw->login_button_height = button_h;
588 if (button_h > pw->unlock_button_height)
589 h2 += (button_h * 1.5 - pw->unlock_button_height * 1.5);
591 /* Use (2 * shadow_width) spacing between the buttons. Another
592 (2 * shadow_width) is required to account for button shadows. */
593 w2 = MAX (w2, button_w + pw->unlock_button_width + (pw->shadow_width * 4));
596 if (w2 > pw->width) pw->width = w2;
600 pw->width += (pw->internal_border * 2);
601 pw->height += (pw->internal_border * 4);
603 pw->width += pw->thermo_width + (pw->shadow_width * 3);
605 if (pw->preferred_logo_height > pw->height)
606 pw->height = pw->logo_height = pw->preferred_logo_height;
607 else if (pw->height > pw->preferred_logo_height)
608 pw->logo_height = pw->height;
610 pw->logo_width = pw->logo_height;
612 pw->width += pw->logo_width;
615 attrmask |= CWOverrideRedirect; attrs.override_redirect = True;
617 attrmask |= CWEventMask;
618 attrs.event_mask = (ExposureMask | KeyPressMask |
619 ButtonPressMask | ButtonReleaseMask);
621 /* Figure out where on the desktop to place the window so that it will
622 actually be visible; this takes into account virtual viewports as
625 saver_screen_info *ssi = &si->screens [mouse_screen (si)];
630 if (si->prefs.debug_p) w /= 2;
631 pw->x = x + ((w + pw->width) / 2) - pw->width;
632 pw->y = y + ((h + pw->height) / 2) - pw->height;
633 if (pw->x < x) pw->x = x;
634 if (pw->y < y) pw->y = y;
637 pw->border_width = get_integer_resource (si->dpy, "passwd.borderWidth",
638 "Dialog.BorderWidth");
640 /* Only create the window the first time around */
641 if (!si->passwd_dialog)
644 XCreateWindow (si->dpy,
645 RootWindowOfScreen(screen),
646 pw->x, pw->y, pw->width, pw->height, pw->border_width,
647 DefaultDepthOfScreen (screen), InputOutput,
648 DefaultVisualOfScreen(screen),
650 XSetWindowBackground (si->dpy, si->passwd_dialog, pw->background);
652 /* We use the default visual, not ssi->visual, so that the logo pixmap's
653 visual matches that of the si->passwd_dialog window. */
654 pw->logo_pixmap = xscreensaver_logo (ssi->screen,
655 /* ssi->current_visual, */
656 DefaultVisualOfScreen(screen),
657 si->passwd_dialog, cmap,
659 &pw->logo_pixels, &pw->logo_npixels,
660 &pw->logo_clipmask, True);
662 else /* On successive prompts, just resize the window */
665 unsigned int mask = CWX | CWY | CWWidth | CWHeight;
669 wc.width = pw->width;
670 wc.height = pw->height;
672 XConfigureWindow (si->dpy, si->passwd_dialog, mask, &wc);
675 restore_background(si);
677 XMapRaised (si->dpy, si->passwd_dialog);
678 XSync (si->dpy, False);
680 move_mouse_grab (si, si->passwd_dialog,
682 pw->prompt_screen->number);
688 XInstallColormap (si->dpy, cmap);
689 draw_passwd_window (si);
696 draw_passwd_window (saver_info *si)
698 passwd_dialog_data *pw = si->pw_data;
702 int x1, x2, x3, y1, y2;
707 pw->passwd_changed_p = True;
708 pw->button_state_changed_p = True;
710 /* This height is the height of all the elements, not to be confused with
711 * the overall window height which is pw->height. It is used to compute
712 * the amount of spacing (padding) between elements. */
713 height = (pw->heading_font->ascent + pw->heading_font->descent +
714 pw->info_label->overall_height +
715 MAX (((pw->label_font->ascent + pw->label_font->descent) +
716 (pw->prompt_label ? pw->prompt_label->overall_height : 0)),
717 ((pw->passwd_font->ascent + pw->passwd_font->descent) +
718 (pw->shadow_width * 2)) * (pw->prompt_label ? 2 : 1)) +
719 pw->date_font->ascent + pw->date_font->descent);
721 if ((strlen(pw->uname_label)) && pw->show_uname_p)
722 height += (pw->uname_font->ascent + pw->uname_font->descent); /* for uname */
724 height += ((pw->button_font->ascent + pw->button_font->descent) * 2 +
725 2 * pw->shadow_width);
727 spacing = ((pw->height - 2 * pw->shadow_width
728 - pw->internal_border - height)
731 if (spacing < 0) spacing = 0;
733 gcv.foreground = pw->foreground;
734 gc1 = XCreateGC (si->dpy, si->passwd_dialog, GCForeground, &gcv);
735 gc2 = XCreateGC (si->dpy, si->passwd_dialog, GCForeground, &gcv);
736 x1 = pw->logo_width + pw->thermo_width + (pw->shadow_width * 3);
737 x3 = pw->width - (pw->shadow_width * 2);
738 y1 = (pw->shadow_width * 2) + spacing + spacing;
742 XSetFont (si->dpy, gc1, pw->heading_font->fid);
743 sw = string_width (pw->heading_font, pw->heading_label);
744 x2 = (x1 + ((x3 - x1 - sw) / 2));
745 y1 += spacing + pw->heading_font->ascent + pw->heading_font->descent;
746 XDrawString (si->dpy, si->passwd_dialog, gc1, x2, y1,
747 pw->heading_label, strlen(pw->heading_label));
749 /* uname below top heading
751 if ((strlen(pw->uname_label)) && pw->show_uname_p)
753 XSetFont (si->dpy, gc1, pw->uname_font->fid);
754 y1 += spacing + pw->uname_font->ascent + pw->uname_font->descent;
755 sw = string_width (pw->uname_font, pw->uname_label);
756 x2 = (x1 + ((x3 - x1 - sw) / 2));
757 XDrawString (si->dpy, si->passwd_dialog, gc1, x2, y1,
758 pw->uname_label, strlen(pw->uname_label));
761 /* the info_label (below uname)
763 x2 = (x1 + ((x3 - x1 - pw->info_label->overall_width) / 2));
764 y1 += spacing + pw->info_label->font_height / 2;
765 mlstring_draw(si->dpy, si->passwd_dialog, gc1, pw->info_label,
767 y1 += pw->info_label->overall_height;
770 tb_height = (pw->passwd_font->ascent + pw->passwd_font->descent +
771 (pw->shadow_width * 4));
773 /* the "User:" prompt
776 XSetForeground (si->dpy, gc1, pw->foreground);
777 XSetFont (si->dpy, gc1, pw->label_font->fid);
778 y1 += (spacing + tb_height + pw->shadow_width);
779 x2 = (x1 + pw->internal_border +
780 MAX(string_width (pw->label_font, pw->user_label),
781 pw->prompt_label ? pw->prompt_label->overall_width : 0));
782 XDrawString (si->dpy, si->passwd_dialog, gc1,
783 x2 - string_width (pw->label_font, pw->user_label),
784 y1 - pw->passwd_font->descent,
785 pw->user_label, strlen(pw->user_label));
787 /* the prompt_label prompt
789 if (pw->prompt_label)
791 y1 += tb_height - pw->label_font->ascent + pw->shadow_width;
792 mlstring_draw(si->dpy, si->passwd_dialog, gc1, pw->prompt_label,
793 x2 - pw->prompt_label->overall_width, y1);
796 /* the "user name" text field
799 XSetForeground (si->dpy, gc1, pw->passwd_foreground);
800 XSetForeground (si->dpy, gc2, pw->passwd_background);
801 XSetFont (si->dpy, gc1, pw->passwd_font->fid);
802 y1 += (spacing + tb_height);
803 x2 += (pw->shadow_width * 4);
805 pw->passwd_field_width = x3 - x2 - pw->internal_border;
806 pw->passwd_field_height = (pw->passwd_font->ascent +
807 pw->passwd_font->descent +
810 XFillRectangle (si->dpy, si->passwd_dialog, gc2,
811 x2 - pw->shadow_width,
812 y1 - (pw->passwd_font->ascent + pw->passwd_font->descent),
813 pw->passwd_field_width, pw->passwd_field_height);
814 XDrawString (si->dpy, si->passwd_dialog, gc1,
816 y1 - pw->passwd_font->descent,
817 si->user, strlen(si->user));
819 /* the password/prompt text field
821 if (pw->prompt_label)
823 y1 += (spacing + pw->prompt_label->overall_height + pw->shadow_width * 2);
825 pw->passwd_field_x = x2 - pw->shadow_width;
826 pw->passwd_field_y = y1 - (pw->passwd_font->ascent +
827 pw->passwd_font->descent);
830 /* The shadow around the text fields
833 y1 += (spacing + (pw->shadow_width * 3));
834 x1 = x2 - (pw->shadow_width * 2);
835 x2 = pw->passwd_field_width + (pw->shadow_width * 2);
836 y2 = pw->passwd_field_height + (pw->shadow_width * 2);
838 draw_shaded_rectangle (si->dpy, si->passwd_dialog,
841 pw->shadow_bottom, pw->shadow_top);
843 if (pw->prompt_label)
845 y1 += (spacing + pw->prompt_label->overall_height + pw->shadow_width * 2);
846 draw_shaded_rectangle (si->dpy, si->passwd_dialog,
849 pw->shadow_bottom, pw->shadow_top);
853 /* The date, below the text fields
857 time_t now = time ((time_t *) 0);
858 struct tm *tm = localtime (&now);
859 memset (buf, 0, sizeof(buf));
860 strftime (buf, sizeof(buf)-1, pw->date_label, tm);
862 XSetFont (si->dpy, gc1, pw->date_font->fid);
863 y1 += pw->shadow_width;
864 y1 += (spacing + tb_height);
866 sw = string_width (pw->date_font, buf);
868 XDrawString (si->dpy, si->passwd_dialog, gc1, x2, y1, buf, strlen(buf));
871 /* Set up the GCs for the "New Login" and "Unlock" buttons.
873 XSetForeground(si->dpy, gc1, pw->button_foreground);
874 XSetForeground(si->dpy, gc2, pw->button_background);
875 XSetFont(si->dpy, gc1, pw->button_font->fid);
877 /* The "Unlock" button */
878 x2 = pw->width - pw->internal_border - (pw->shadow_width * 2);
880 /* right aligned button */
881 x1 = x2 - pw->unlock_button_width;
883 /* Add half the difference between y1 and the internal edge.
884 * It actually looks better if the internal border is ignored. */
885 y1 += ((pw->height - MAX (pw->unlock_button_height, pw->login_button_height)
889 pw->unlock_button_x = x1;
890 pw->unlock_button_y = y1;
892 /* The "New Login" button
894 if (pw->login_button_p)
896 /* Using the same GC as for the Unlock button */
898 sw = string_width (pw->button_font, pw->login_label);
900 /* left aligned button */
901 x1 = (pw->logo_width + pw->thermo_width + (pw->shadow_width * 3) +
902 pw->internal_border);
904 pw->login_button_x = x1;
905 pw->login_button_y = y1;
910 x1 = pw->shadow_width * 6;
911 y1 = pw->shadow_width * 6;
912 x2 = pw->logo_width - (pw->shadow_width * 12);
913 y2 = pw->logo_height - (pw->shadow_width * 12);
919 unsigned int w, h, bw, d;
920 XGetGeometry (si->dpy, pw->logo_pixmap, &root, &x, &y, &w, &h, &bw, &d);
921 XSetForeground (si->dpy, gc1, pw->foreground);
922 XSetBackground (si->dpy, gc1, pw->background);
923 XSetClipMask (si->dpy, gc1, pw->logo_clipmask);
924 XSetClipOrigin (si->dpy, gc1, x1 + ((x2 - (int)w) / 2), y1 + ((y2 - (int)h) / 2));
926 XCopyPlane (si->dpy, pw->logo_pixmap, si->passwd_dialog, gc1,
928 x1 + ((x2 - (int)w) / 2),
929 y1 + ((y2 - (int)h) / 2),
932 XCopyArea (si->dpy, pw->logo_pixmap, si->passwd_dialog, gc1,
934 x1 + ((x2 - (int)w) / 2),
935 y1 + ((y2 - (int)h) / 2));
940 XSetForeground (si->dpy, gc1, pw->thermo_foreground);
941 XSetForeground (si->dpy, gc2, pw->thermo_background);
943 pw->thermo_field_x = pw->logo_width + pw->shadow_width;
944 pw->thermo_field_y = pw->shadow_width * 5;
945 pw->thermo_field_height = pw->height - (pw->shadow_width * 10);
948 /* Solid border inside the logo box. */
949 XSetForeground (si->dpy, gc1, pw->foreground);
950 XDrawRectangle (si->dpy, si->passwd_dialog, gc1, x1, y1, x2-1, y2-1);
953 /* The shadow around the logo
955 draw_shaded_rectangle (si->dpy, si->passwd_dialog,
956 pw->shadow_width * 4,
957 pw->shadow_width * 4,
958 pw->logo_width - (pw->shadow_width * 8),
959 pw->logo_height - (pw->shadow_width * 8),
961 pw->shadow_bottom, pw->shadow_top);
963 /* The shadow around the thermometer
965 draw_shaded_rectangle (si->dpy, si->passwd_dialog,
967 pw->shadow_width * 4,
968 pw->thermo_width + (pw->shadow_width * 2),
969 pw->height - (pw->shadow_width * 8),
971 pw->shadow_bottom, pw->shadow_top);
974 /* Solid border inside the thermometer. */
975 XSetForeground (si->dpy, gc1, pw->foreground);
976 XDrawRectangle (si->dpy, si->passwd_dialog, gc1,
977 pw->thermo_field_x, pw->thermo_field_y,
978 pw->thermo_width - 1, pw->thermo_field_height - 1);
981 /* The shadow around the whole window
983 draw_shaded_rectangle (si->dpy, si->passwd_dialog,
984 0, 0, pw->width, pw->height, pw->shadow_width,
985 pw->shadow_top, pw->shadow_bottom);
987 XFreeGC (si->dpy, gc1);
988 XFreeGC (si->dpy, gc2);
990 update_passwd_window (si, pw->passwd_string, pw->ratio);
994 draw_button(Display *dpy,
997 unsigned long foreground, unsigned long background,
1000 int width, int height,
1002 Pixel shadow_light, Pixel shadow_dark,
1008 int label_x, label_y;
1010 gcv.foreground = foreground;
1011 gcv.font = font->fid;
1012 gc1 = XCreateGC(dpy, dialog, GCForeground|GCFont, &gcv);
1013 gcv.foreground = background;
1014 gc2 = XCreateGC(dpy, dialog, GCForeground, &gcv);
1016 XFillRectangle(dpy, dialog, gc2,
1017 x, y, width, height);
1019 sw = string_width(font, label);
1021 label_x = x + ((width - sw) / 2);
1022 label_y = (y + (height - (font->ascent + font->descent)) / 2 + font->ascent);
1030 XDrawString(dpy, dialog, gc1, label_x, label_y, label, strlen(label));
1035 draw_shaded_rectangle(dpy, dialog, x, y, width, height,
1036 shadow_width, shadow_light, shadow_dark);
1040 update_passwd_window (saver_info *si, const char *printed_passwd, float ratio)
1042 passwd_dialog_data *pw = si->pw_data;
1046 XRectangle rects[1];
1049 gcv.foreground = pw->passwd_foreground;
1050 gcv.font = pw->passwd_font->fid;
1051 gc1 = XCreateGC (si->dpy, si->passwd_dialog, GCForeground|GCFont, &gcv);
1052 gcv.foreground = pw->passwd_background;
1053 gc2 = XCreateGC (si->dpy, si->passwd_dialog, GCForeground, &gcv);
1057 char *s = strdup (printed_passwd);
1058 if (pw->passwd_string) free (pw->passwd_string);
1059 pw->passwd_string = s;
1062 if (pw->prompt_label)
1065 /* the "password" text field
1067 rects[0].x = pw->passwd_field_x;
1068 rects[0].y = pw->passwd_field_y;
1069 rects[0].width = pw->passwd_field_width;
1070 rects[0].height = pw->passwd_field_height;
1072 /* The user entry (password) field is double buffered.
1073 * This avoids flickering, particularly in synchronous mode. */
1075 if (pw->passwd_changed_p)
1077 pw->passwd_changed_p = False;
1079 if (pw->user_entry_pixmap)
1081 XFreePixmap(si->dpy, pw->user_entry_pixmap);
1082 pw->user_entry_pixmap = 0;
1085 pw->user_entry_pixmap =
1086 XCreatePixmap (si->dpy, si->passwd_dialog,
1087 rects[0].width, rects[0].height,
1088 DefaultDepthOfScreen (pw->prompt_screen->screen));
1090 XFillRectangle (si->dpy, pw->user_entry_pixmap, gc2,
1091 0, 0, rects[0].width, rects[0].height);
1093 XDrawString (si->dpy, pw->user_entry_pixmap, gc1,
1095 pw->passwd_font->ascent,
1096 pw->passwd_string, strlen(pw->passwd_string));
1098 /* Ensure the new pixmap gets copied to the window */
1105 if (pw->i_beam == 0)
1107 /* Make the I-beam disappear */
1108 XCopyArea(si->dpy, pw->user_entry_pixmap, si->passwd_dialog, gc2,
1109 0, 0, rects[0].width, rects[0].height,
1110 rects[0].x, rects[0].y);
1112 else if (pw->i_beam == 1)
1114 /* Make the I-beam appear */
1115 x = (rects[0].x + pw->shadow_width +
1116 string_width (pw->passwd_font, pw->passwd_string));
1117 y = rects[0].y + pw->shadow_width;
1119 if (x > rects[0].x + rects[0].width - 1)
1120 x = rects[0].x + rects[0].width - 1;
1121 XDrawLine (si->dpy, si->passwd_dialog, gc1,
1123 x, y + pw->passwd_font->ascent + pw->passwd_font->descent-1);
1126 pw->i_beam = (pw->i_beam + 1) % 4;
1132 y = (pw->thermo_field_height - 2) * (1.0 - pw->ratio);
1135 XFillRectangle (si->dpy, si->passwd_dialog, gc2,
1136 pw->thermo_field_x + 1,
1137 pw->thermo_field_y + 1,
1140 XSetForeground (si->dpy, gc1, pw->thermo_foreground);
1141 XFillRectangle (si->dpy, si->passwd_dialog, gc1,
1142 pw->thermo_field_x + 1,
1143 pw->thermo_field_y + 1 + y,
1145 MAX (0, pw->thermo_field_height - y - 2));
1148 if (pw->button_state_changed_p)
1150 pw->button_state_changed_p = False;
1152 /* The "Unlock" button
1154 draw_button(si->dpy, si->passwd_dialog, pw->button_font,
1155 pw->button_foreground, pw->button_background,
1157 pw->unlock_button_x, pw->unlock_button_y,
1158 pw->unlock_button_width, pw->unlock_button_height,
1160 (pw->unlock_button_down_p ? pw->shadow_bottom : pw->shadow_top),
1161 (pw->unlock_button_down_p ? pw->shadow_top : pw->shadow_bottom),
1162 pw->unlock_button_down_p);
1164 /* The "New Login" button
1166 if (pw->login_button_p)
1168 draw_button(si->dpy, si->passwd_dialog, pw->button_font,
1169 (pw->login_button_enabled_p
1170 ? pw->passwd_foreground
1171 : pw->shadow_bottom),
1172 pw->button_background,
1174 pw->login_button_x, pw->login_button_y,
1175 pw->login_button_width, pw->login_button_height,
1177 (pw->login_button_down_p
1180 (pw->login_button_down_p
1182 : pw->shadow_bottom),
1183 pw->login_button_down_p);
1187 XFreeGC (si->dpy, gc1);
1188 XFreeGC (si->dpy, gc2);
1189 XSync (si->dpy, False);
1194 restore_background (saver_info *si)
1196 passwd_dialog_data *pw = si->pw_data;
1197 saver_screen_info *ssi = pw->prompt_screen;
1201 gcv.function = GXcopy;
1203 gc = XCreateGC (si->dpy, ssi->screensaver_window, GCFunction, &gcv);
1205 XCopyArea (si->dpy, pw->save_under,
1206 ssi->screensaver_window, gc,
1208 ssi->width, ssi->height,
1211 XFreeGC (si->dpy, gc);
1215 /* Frees anything created by make_passwd_window */
1217 cleanup_passwd_window (saver_info *si)
1219 passwd_dialog_data *pw;
1221 if (!(pw = si->pw_data))
1226 mlstring_free(pw->info_label);
1230 if (pw->prompt_label)
1232 mlstring_free(pw->prompt_label);
1233 pw->prompt_label = 0;
1236 memset (pw->typed_passwd, 0, sizeof(pw->typed_passwd));
1237 memset (pw->passwd_string, 0, strlen(pw->passwd_string));
1241 XtRemoveTimeOut (pw->timer);
1245 if (pw->user_entry_pixmap)
1247 XFreePixmap(si->dpy, pw->user_entry_pixmap);
1248 pw->user_entry_pixmap = 0;
1254 destroy_passwd_window (saver_info *si)
1256 saver_preferences *p = &si->prefs;
1257 passwd_dialog_data *pw = si->pw_data;
1258 saver_screen_info *ssi = pw->prompt_screen;
1259 Colormap cmap = DefaultColormapOfScreen (ssi->screen);
1260 Pixel black = BlackPixelOfScreen (ssi->screen);
1261 Pixel white = WhitePixelOfScreen (ssi->screen);
1264 cleanup_passwd_window (si);
1266 if (si->cached_passwd)
1268 char *wipe = si->cached_passwd;
1273 free(si->cached_passwd);
1274 si->cached_passwd = NULL;
1277 move_mouse_grab (si, RootWindowOfScreen (ssi->screen),
1278 ssi->cursor, ssi->number);
1280 if (pw->passwd_cursor)
1281 XFreeCursor (si->dpy, pw->passwd_cursor);
1284 fprintf (stderr, "%s: %d: moving mouse back to %d,%d.\n",
1285 blurb(), ssi->number,
1286 pw->previous_mouse_x, pw->previous_mouse_y);
1288 XWarpPointer (si->dpy, None, RootWindowOfScreen (ssi->screen),
1290 pw->previous_mouse_x, pw->previous_mouse_y);
1291 XSync (si->dpy, False);
1293 while (XCheckMaskEvent (si->dpy, PointerMotionMask, &event))
1295 fprintf (stderr, "%s: discarding MotionNotify event.\n", blurb());
1298 if (si->using_xinput_extension && si->xinput_DeviceMotionNotify)
1299 while (XCheckTypedEvent (si->dpy, si->xinput_DeviceMotionNotify, &event))
1301 fprintf (stderr, "%s: discarding DeviceMotionNotify event.\n",
1305 if (si->passwd_dialog)
1307 if (si->prefs.verbose_p)
1308 fprintf (stderr, "%s: %d: destroying password dialog.\n",
1309 blurb(), pw->prompt_screen->number);
1311 XDestroyWindow (si->dpy, si->passwd_dialog);
1312 si->passwd_dialog = 0;
1317 restore_background(si);
1318 XFreePixmap (si->dpy, pw->save_under);
1322 if (pw->heading_label) free (pw->heading_label);
1323 if (pw->body_label) free (pw->body_label);
1324 if (pw->user_label) free (pw->user_label);
1325 if (pw->date_label) free (pw->date_label);
1326 if (pw->login_label) free (pw->login_label);
1327 if (pw->unlock_label) free (pw->unlock_label);
1328 if (pw->passwd_string) free (pw->passwd_string);
1329 if (pw->uname_label) free (pw->uname_label);
1331 if (pw->heading_font) XFreeFont (si->dpy, pw->heading_font);
1332 if (pw->body_font) XFreeFont (si->dpy, pw->body_font);
1333 if (pw->label_font) XFreeFont (si->dpy, pw->label_font);
1334 if (pw->passwd_font) XFreeFont (si->dpy, pw->passwd_font);
1335 if (pw->date_font) XFreeFont (si->dpy, pw->date_font);
1336 if (pw->button_font) XFreeFont (si->dpy, pw->button_font);
1337 if (pw->uname_font) XFreeFont (si->dpy, pw->uname_font);
1339 if (pw->foreground != black && pw->foreground != white)
1340 XFreeColors (si->dpy, cmap, &pw->foreground, 1, 0L);
1341 if (pw->background != black && pw->background != white)
1342 XFreeColors (si->dpy, cmap, &pw->background, 1, 0L);
1343 if (!(pw->button_foreground == black || pw->button_foreground == white))
1344 XFreeColors (si->dpy, cmap, &pw->button_foreground, 1, 0L);
1345 if (!(pw->button_background == black || pw->button_background == white))
1346 XFreeColors (si->dpy, cmap, &pw->button_background, 1, 0L);
1347 if (pw->passwd_foreground != black && pw->passwd_foreground != white)
1348 XFreeColors (si->dpy, cmap, &pw->passwd_foreground, 1, 0L);
1349 if (pw->passwd_background != black && pw->passwd_background != white)
1350 XFreeColors (si->dpy, cmap, &pw->passwd_background, 1, 0L);
1351 if (pw->thermo_foreground != black && pw->thermo_foreground != white)
1352 XFreeColors (si->dpy, cmap, &pw->thermo_foreground, 1, 0L);
1353 if (pw->thermo_background != black && pw->thermo_background != white)
1354 XFreeColors (si->dpy, cmap, &pw->thermo_background, 1, 0L);
1355 if (pw->shadow_top != black && pw->shadow_top != white)
1356 XFreeColors (si->dpy, cmap, &pw->shadow_top, 1, 0L);
1357 if (pw->shadow_bottom != black && pw->shadow_bottom != white)
1358 XFreeColors (si->dpy, cmap, &pw->shadow_bottom, 1, 0L);
1360 if (pw->logo_pixmap)
1361 XFreePixmap (si->dpy, pw->logo_pixmap);
1362 if (pw-> logo_clipmask)
1363 XFreePixmap (si->dpy, pw->logo_clipmask);
1364 if (pw->logo_pixels)
1366 if (pw->logo_npixels)
1367 XFreeColors (si->dpy, cmap, pw->logo_pixels, pw->logo_npixels, 0L);
1368 free (pw->logo_pixels);
1369 pw->logo_pixels = 0;
1370 pw->logo_npixels = 0;
1374 XFreePixmap (si->dpy, pw->save_under);
1377 XInstallColormap (si->dpy, cmap);
1379 memset (pw, 0, sizeof(*pw));
1385 static Bool error_handler_hit_p = False;
1388 ignore_all_errors_ehandler (Display *dpy, XErrorEvent *error)
1390 error_handler_hit_p = True;
1395 #ifdef HAVE_XHPDISABLERESET
1396 /* This function enables and disables the C-Sh-Reset hot-key, which
1397 normally resets the X server (logging out the logged-in user.)
1398 We don't want random people to be able to do that while the
1402 hp_lock_reset (saver_info *si, Bool lock_p)
1404 static Bool hp_locked_p = False;
1406 /* Calls to XHPDisableReset and XHPEnableReset must be balanced,
1407 or BadAccess errors occur. (It's ok for this to be global,
1408 since it affects the whole machine, not just the current screen.)
1410 if (hp_locked_p == lock_p)
1414 XHPDisableReset (si->dpy);
1416 XHPEnableReset (si->dpy);
1417 hp_locked_p = lock_p;
1419 #endif /* HAVE_XHPDISABLERESET */
1422 #ifdef HAVE_XF86MISCSETGRABKEYSSTATE
1424 /* This function enables and disables the Ctrl-Alt-KP_star and
1425 Ctrl-Alt-KP_slash hot-keys, which (in XFree86 4.2) break any
1426 grabs and/or kill the grabbing client. That would effectively
1427 unlock the screen, so we don't like that.
1429 The Ctrl-Alt-KP_star and Ctrl-Alt-KP_slash hot-keys only exist
1430 if AllowDeactivateGrabs and/or AllowClosedownGrabs are turned on
1431 in XF86Config. I believe they are disabled by default.
1433 This does not affect any other keys (specifically Ctrl-Alt-BS or
1434 Ctrl-Alt-F1) but I wish it did. Maybe it will someday.
1437 xfree_lock_grab_smasher (saver_info *si, Bool lock_p)
1439 saver_preferences *p = &si->prefs;
1442 XErrorHandler old_handler;
1444 if (!XF86MiscQueryExtension(si->dpy, &event, &error))
1447 XSync (si->dpy, False);
1448 error_handler_hit_p = False;
1449 old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1450 XSync (si->dpy, False);
1451 status = XF86MiscSetGrabKeysState (si->dpy, !lock_p);
1452 XSync (si->dpy, False);
1453 if (error_handler_hit_p) status = 666;
1455 if (!lock_p && status == MiscExtGrabStateAlready)
1456 status = MiscExtGrabStateSuccess; /* shut up, consider this success */
1458 if (p->verbose_p && status != MiscExtGrabStateSuccess)
1459 fprintf (stderr, "%s: error: XF86MiscSetGrabKeysState(%d) returned %s\n",
1461 (status == MiscExtGrabStateSuccess ? "MiscExtGrabStateSuccess" :
1462 status == MiscExtGrabStateLocked ? "MiscExtGrabStateLocked" :
1463 status == MiscExtGrabStateAlready ? "MiscExtGrabStateAlready" :
1464 status == 666 ? "an X error" :
1467 XSync (si->dpy, False);
1468 XSetErrorHandler (old_handler);
1469 XSync (si->dpy, False);
1471 #endif /* HAVE_XF86MISCSETGRABKEYSSTATE */
1475 /* This function enables and disables the C-Alt-Plus and C-Alt-Minus
1476 hot-keys, which normally change the resolution of the X server.
1477 We don't want people to be able to switch the server resolution
1478 while the screen is locked, because if they switch to a higher
1479 resolution, it could cause part of the underlying desktop to become
1482 #ifdef HAVE_XF86VMODE
1485 xfree_lock_mode_switch (saver_info *si, Bool lock_p)
1487 static Bool any_mode_locked_p = False;
1488 saver_preferences *p = &si->prefs;
1490 int real_nscreens = ScreenCount (si->dpy);
1493 XErrorHandler old_handler;
1495 if (any_mode_locked_p == lock_p)
1497 if (!XF86VidModeQueryExtension (si->dpy, &event, &error))
1500 for (screen = 0; screen < real_nscreens; screen++)
1502 XSync (si->dpy, False);
1503 old_handler = XSetErrorHandler (ignore_all_errors_ehandler);
1504 error_handler_hit_p = False;
1505 status = XF86VidModeLockModeSwitch (si->dpy, screen, lock_p);
1506 XSync (si->dpy, False);
1507 XSetErrorHandler (old_handler);
1508 if (error_handler_hit_p) status = False;
1511 any_mode_locked_p = lock_p;
1513 if (!status && (p->verbose_p || !lock_p))
1514 /* Only print this when verbose, or when we locked but can't unlock.
1515 I tried printing this message whenever it comes up, but
1516 mode-locking always fails if DontZoom is set in XF86Config. */
1517 fprintf (stderr, "%s: %d: unable to %s mode switching!\n",
1518 blurb(), screen, (lock_p ? "lock" : "unlock"));
1519 else if (p->verbose_p)
1520 fprintf (stderr, "%s: %d: %s mode switching.\n",
1521 blurb(), screen, (lock_p ? "locked" : "unlocked"));
1524 #endif /* HAVE_XF86VMODE */
1527 /* If the viewport has been scrolled since the screen was blanked,
1528 then scroll it back to where it belongs. This function only exists
1529 to patch over a very brief race condition.
1532 undo_vp_motion (saver_info *si)
1534 #ifdef HAVE_XF86VMODE
1535 saver_preferences *p = &si->prefs;
1537 int real_nscreens = ScreenCount (si->dpy);
1540 if (!XF86VidModeQueryExtension (si->dpy, &event, &error))
1543 for (screen = 0; screen < real_nscreens; screen++)
1545 saver_screen_info *ssi = &si->screens[screen];
1549 if (ssi->blank_vp_x == -1 && ssi->blank_vp_y == -1)
1551 if (!XF86VidModeGetViewPort (si->dpy, screen, &x, &y))
1553 if (ssi->blank_vp_x == x && ssi->blank_vp_y == y)
1556 /* We're going to move the viewport. The mouse has just been grabbed on
1557 (and constrained to, thus warped to) the password window, so it is no
1558 longer near the edge of the screen. However, wait a bit anyway, just
1559 to make sure the server drains its last motion event, so that the
1560 screen doesn't continue to scroll after we've reset the viewport.
1562 XSync (si->dpy, False);
1563 usleep (250000); /* 1/4 second */
1564 XSync (si->dpy, False);
1566 status = XF86VidModeSetViewPort (si->dpy, screen,
1567 ssi->blank_vp_x, ssi->blank_vp_y);
1571 "%s: %d: unable to move vp from (%d,%d) back to (%d,%d)!\n",
1572 blurb(), screen, x, y, ssi->blank_vp_x, ssi->blank_vp_y);
1573 else if (p->verbose_p)
1575 "%s: %d: vp moved to (%d,%d); moved it back to (%d,%d).\n",
1576 blurb(), screen, x, y, ssi->blank_vp_x, ssi->blank_vp_y);
1578 #endif /* HAVE_XF86VMODE */
1587 passwd_animate_timer (XtPointer closure, XtIntervalId *id)
1589 saver_info *si = (saver_info *) closure;
1591 passwd_dialog_data *pw = si->pw_data;
1595 pw->ratio -= (1.0 / ((double) si->prefs.passwd_timeout / (double) tick));
1599 if (si->unlock_state == ul_read)
1600 si->unlock_state = ul_time;
1603 update_passwd_window (si, 0, pw->ratio);
1605 if (si->unlock_state == ul_read)
1606 pw->timer = XtAppAddTimeOut (si->app, tick, passwd_animate_timer,
1611 idle_timer ((XtPointer) si, 0);
1615 static XComposeStatus *compose_status;
1618 handle_login_button (saver_info *si, XEvent *event)
1620 saver_preferences *p = &si->prefs;
1621 Bool mouse_in_box = False;
1623 passwd_dialog_data *pw = si->pw_data;
1624 saver_screen_info *ssi = pw->prompt_screen;
1626 if (! pw->login_button_enabled_p)
1630 (event->xbutton.x >= pw->login_button_x &&
1631 event->xbutton.x <= pw->login_button_x + pw->login_button_width &&
1632 event->xbutton.y >= pw->login_button_y &&
1633 event->xbutton.y <= pw->login_button_y + pw->login_button_height);
1635 if (ButtonRelease == event->xany.type &&
1636 pw->login_button_down_p &&
1639 /* Only allow them to press the button once: don't want to
1640 accidentally launch a dozen gdm choosers if the machine
1644 pw->login_button_enabled_p = False;
1647 pw->login_button_down_p = (mouse_in_box &&
1648 ButtonRelease != event->xany.type);
1650 update_passwd_window (si, 0, pw->ratio);
1653 fork_and_exec (ssi, p->new_login_command);
1658 handle_unlock_button (saver_info *si, XEvent *event)
1660 Bool mouse_in_box = False;
1661 passwd_dialog_data *pw = si->pw_data;
1664 (event->xbutton.x >= pw->unlock_button_x &&
1665 event->xbutton.x <= pw->unlock_button_x + pw->unlock_button_width &&
1666 event->xbutton.y >= pw->unlock_button_y &&
1667 event->xbutton.y <= pw->unlock_button_y + pw->unlock_button_height);
1669 if (ButtonRelease == event->xany.type &&
1670 pw->unlock_button_down_p &&
1672 finished_typing_passwd (si, pw);
1674 pw->unlock_button_down_p = (mouse_in_box &&
1675 ButtonRelease != event->xany.type);
1680 finished_typing_passwd (saver_info *si, passwd_dialog_data *pw)
1682 if (si->unlock_state == ul_read)
1684 update_passwd_window (si, "Checking...", pw->ratio);
1685 XSync (si->dpy, False);
1687 si->unlock_state = ul_finished;
1688 update_passwd_window (si, "", pw->ratio);
1693 handle_passwd_key (saver_info *si, XKeyEvent *event)
1695 passwd_dialog_data *pw = si->pw_data;
1696 int pw_size = sizeof (pw->typed_passwd) - 1;
1697 char *typed_passwd = pw->typed_passwd;
1701 int size = XLookupString (event, s, 1, 0, compose_status);
1703 if (size != 1) return;
1707 pw->passwd_changed_p = True;
1709 /* Add 10% to the time remaining every time a key is pressed. */
1711 if (pw->ratio > 1) pw->ratio = 1;
1715 case '\010': case '\177': /* Backspace */
1719 typed_passwd [strlen(typed_passwd)-1] = 0;
1722 case '\025': case '\030': /* Erase line */
1723 memset (typed_passwd, 0, pw_size);
1726 case '\012': case '\015': /* Enter */
1727 finished_typing_passwd(si, pw);
1730 case '\033': /* Escape */
1731 si->unlock_state = ul_cancel;
1735 /* Though technically the only illegal characters in Unix passwords
1736 are LF and NUL, most GUI programs (e.g., GDM) use regular text-entry
1737 fields that only let you type printable characters. So, people
1738 who use funky characters in their passwords are already broken.
1739 We follow that precedent.
1741 if (isprint ((unsigned char) *s))
1743 i = strlen (typed_passwd);
1748 typed_passwd [i] = *s;
1749 typed_passwd [i+1] = 0;
1759 /* If the input is wider than the text box, only show the last portion.
1760 * This simulates a horizontally scrolling text field. */
1761 int chars_in_pwfield = (pw->passwd_field_width /
1762 pw->passwd_font->max_bounds.width);
1764 if (strlen(typed_passwd) > chars_in_pwfield)
1765 typed_passwd += (strlen(typed_passwd) - chars_in_pwfield);
1767 update_passwd_window(si, typed_passwd, pw->ratio);
1769 else if (pw->show_stars_p)
1771 i = strlen(typed_passwd);
1772 stars = (char *) malloc(i+1);
1773 memset (stars, '*', i);
1775 update_passwd_window (si, stars, pw->ratio);
1780 update_passwd_window (si, "", pw->ratio);
1786 passwd_event_loop (saver_info *si)
1788 saver_preferences *p = &si->prefs;
1791 /* We have to go through this union bullshit because gcc-4.4.0 has
1792 stricter struct-aliasing rules. Without this, the optimizer
1798 XRRScreenChangeNotifyEvent xrr_event;
1799 # endif /* HAVE_RANDR */
1802 passwd_animate_timer ((XtPointer) si, 0);
1804 while (si->unlock_state == ul_read)
1806 XtAppNextEvent (si->app, &event.x_event);
1809 if (si->using_randr_extension &&
1810 (event.x_event.type ==
1811 (si->randr_event_number + RRScreenChangeNotify)))
1813 /* The Resize and Rotate extension sends an event when the
1814 size, rotation, or refresh rate of any screen has changed. */
1818 /* XRRRootToScreen is in Xrandr.h 1.4, 2001/06/07 */
1819 int screen = XRRRootToScreen(si->dpy, event.xrr_event.window);
1820 fprintf (stderr, "%s: %d: screen change event received\n",
1824 #ifdef RRScreenChangeNotifyMask
1825 /* Inform Xlib that it's ok to update its data structures. */
1826 XRRUpdateConfiguration(&event.x_event); /* Xrandr.h 1.9, 2002/09/29 */
1827 #endif /* RRScreenChangeNotifyMask */
1829 /* Resize the existing xscreensaver windows and cached ssi data. */
1830 if (update_screen_layout (si))
1834 fprintf (stderr, "%s: new layout:\n", blurb());
1835 describe_monitor_layout (si);
1837 resize_screensaver_window (si);
1841 #endif /* HAVE_RANDR */
1843 if (event.x_event.xany.window == si->passwd_dialog &&
1844 event.x_event.xany.type == Expose)
1845 draw_passwd_window (si);
1846 else if (event.x_event.xany.type == KeyPress)
1848 handle_passwd_key (si, &event.x_event.xkey);
1849 si->pw_data->caps_p = (event.x_event.xkey.state & LockMask);
1851 else if (event.x_event.xany.type == ButtonPress ||
1852 event.x_event.xany.type == ButtonRelease)
1854 si->pw_data->button_state_changed_p = True;
1855 handle_unlock_button (si, &event.x_event);
1856 if (si->pw_data->login_button_p)
1857 handle_login_button (si, &event.x_event);
1860 XtDispatchEvent (&event.x_event);
1863 switch (si->unlock_state)
1865 case ul_cancel: msg = ""; break;
1866 case ul_time: msg = "Timed out!"; break;
1867 case ul_finished: msg = "Checking..."; break;
1868 default: msg = 0; break;
1872 switch (si->unlock_state) {
1874 fprintf (stderr, "%s: input cancelled.\n", blurb()); break;
1876 fprintf (stderr, "%s: input timed out.\n", blurb()); break;
1878 fprintf (stderr, "%s: input finished.\n", blurb()); break;
1884 si->pw_data->i_beam = 0;
1885 update_passwd_window (si, msg, 0.0);
1886 XSync (si->dpy, False);
1888 /* Swallow all pending KeyPress/KeyRelease events. */
1891 while (XCheckMaskEvent (si->dpy, KeyPressMask|KeyReleaseMask, &e))
1899 handle_typeahead (saver_info *si)
1901 passwd_dialog_data *pw = si->pw_data;
1903 if (!si->unlock_typeahead)
1906 pw->passwd_changed_p = True;
1908 i = strlen (si->unlock_typeahead);
1909 if (i >= sizeof(pw->typed_passwd) - 1)
1910 i = sizeof(pw->typed_passwd) - 1;
1912 memcpy (pw->typed_passwd, si->unlock_typeahead, i);
1913 pw->typed_passwd [i] = 0;
1915 memset (si->unlock_typeahead, '*', strlen(si->unlock_typeahead));
1916 si->unlock_typeahead[i] = 0;
1917 update_passwd_window (si, si->unlock_typeahead, pw->ratio);
1919 free (si->unlock_typeahead);
1920 si->unlock_typeahead = 0;
1925 * Returns a copy of the input string with trailing whitespace removed.
1926 * Whitespace is anything considered so by isspace().
1927 * It is safe to call this with NULL, in which case NULL will be returned.
1928 * The returned string (if not NULL) should be freed by the caller with free().
1931 remove_trailing_whitespace(const char *str)
1941 newstr = malloc(len + 1);
1945 (void) strcpy(newstr, str);
1947 while (isspace(*--chr) && chr >= newstr)
1955 * The authentication conversation function.
1956 * Like a PAM conversation function, this accepts multiple messages in a single
1957 * round. It then splits them into individual messages for display on the
1958 * passwd dialog. A message sequence of info or error followed by a prompt will
1959 * be reduced into a single dialog window.
1961 * Returns 0 on success or -1 if some problem occurred (cancelled auth, OOM, ...)
1964 gui_auth_conv(int num_msg,
1965 const struct auth_message auth_msgs[],
1966 struct auth_response **resp,
1970 const char *info_msg, *prompt;
1971 struct auth_response *responses;
1973 if (si->unlock_state == ul_cancel ||
1974 si->unlock_state == ul_time)
1975 /* If we've already cancelled or timed out in this PAM conversation,
1976 don't prompt again even if PAM asks us to! */
1979 if (!(responses = calloc(num_msg, sizeof(struct auth_response))))
1982 for (i = 0; i < num_msg; ++i)
1984 info_msg = prompt = NULL;
1986 /* See if there is a following message that can be shown at the same
1988 if (auth_msgs[i].type == AUTH_MSGTYPE_INFO
1990 && ( auth_msgs[i+1].type == AUTH_MSGTYPE_PROMPT_NOECHO
1991 || auth_msgs[i+1].type == AUTH_MSGTYPE_PROMPT_ECHO)
1994 info_msg = auth_msgs[i].msg;
1995 prompt = auth_msgs[++i].msg;
1999 if ( auth_msgs[i].type == AUTH_MSGTYPE_INFO
2000 || auth_msgs[i].type == AUTH_MSGTYPE_ERROR)
2001 info_msg = auth_msgs[i].msg;
2003 prompt = auth_msgs[i].msg;
2007 char *info_msg_trimmed, *prompt_trimmed;
2009 /* Trailing whitespace looks bad in a GUI */
2010 info_msg_trimmed = remove_trailing_whitespace(info_msg);
2011 prompt_trimmed = remove_trailing_whitespace(prompt);
2013 if (make_passwd_window(si, info_msg_trimmed, prompt_trimmed,
2014 auth_msgs[i].type == AUTH_MSGTYPE_PROMPT_ECHO
2019 if (info_msg_trimmed)
2020 free(info_msg_trimmed);
2023 free(prompt_trimmed);
2026 compose_status = calloc (1, sizeof (*compose_status));
2027 if (!compose_status)
2030 si->unlock_state = ul_read;
2032 handle_typeahead (si);
2033 passwd_event_loop (si);
2035 if (si->unlock_state == ul_cancel)
2038 responses[i].response = strdup(si->pw_data->typed_passwd);
2040 /* Cache the first response to a PROMPT_NOECHO to save prompting for
2041 * each auth mechanism. */
2042 if (si->cached_passwd == NULL &&
2043 auth_msgs[i].type == AUTH_MSGTYPE_PROMPT_NOECHO)
2044 si->cached_passwd = strdup(responses[i].response);
2046 free (compose_status);
2052 return (si->unlock_state == ul_finished) ? 0 : -1;
2056 free (compose_status);
2060 for (i = 0; i < num_msg; ++i)
2061 if (responses[i].response)
2062 free (responses[i].response);
2071 auth_finished_cb (saver_info *si)
2076 /* If we have something to say, put the dialog back up for a few seconds
2077 to display it. Otherwise, don't bother.
2080 if (si->unlock_state == ul_fail && /* failed with caps lock on */
2081 si->pw_data && si->pw_data->caps_p)
2082 s = "Authentication failed (Caps Lock?)";
2083 else if (si->unlock_state == ul_fail) /* failed without caps lock */
2084 s = "Authentication failed!";
2085 else if (si->unlock_state == ul_success && /* good, but report failures */
2086 si->unlock_failures > 0)
2088 if (si->unlock_failures == 1)
2089 s = "There has been\n1 failed login attempt.";
2092 sprintf (buf, "There have been\n%d failed login attempts.",
2093 si->unlock_failures);
2096 si->unlock_failures = 0;
2098 else /* good, with no failures, */
2099 goto END; /* or timeout, or cancel. */
2101 make_passwd_window (si, s, NULL, True);
2102 XSync (si->dpy, False);
2106 time_t start = time ((time_t *) 0);
2108 while (time ((time_t *) 0) < start + secs)
2109 if (XPending (si->dpy))
2111 XNextEvent (si->dpy, &event);
2112 if (event.xany.window == si->passwd_dialog &&
2113 event.xany.type == Expose)
2114 draw_passwd_window (si);
2115 else if (event.xany.type == ButtonPress ||
2116 event.xany.type == KeyPress)
2118 XSync (si->dpy, False);
2121 usleep (250000); /* 1/4 second */
2126 destroy_passwd_window (si);
2131 unlock_p (saver_info *si)
2133 saver_preferences *p = &si->prefs;
2137 fprintf(stderr, "%s: Error: no unlock function specified!\n", blurb());
2141 raise_window (si, True, True, True);
2143 xss_authenticate(si, p->verbose_p);
2145 return (si->unlock_state == ul_success);
2150 set_locked_p (saver_info *si, Bool locked_p)
2152 si->locked_p = locked_p;
2154 #ifdef HAVE_XHPDISABLERESET
2155 hp_lock_reset (si, locked_p); /* turn off/on C-Sh-Reset */
2157 #ifdef HAVE_XF86VMODE
2158 xfree_lock_mode_switch (si, locked_p); /* turn off/on C-Alt-Plus */
2160 #ifdef HAVE_XF86MISCSETGRABKEYSSTATE
2161 xfree_lock_grab_smasher (si, locked_p); /* turn off/on C-Alt-KP-*,/ */
2164 store_saver_status (si); /* store locked-p */
2168 #else /* NO_LOCKING -- whole file */
2171 set_locked_p (saver_info *si, Bool locked_p)
2173 if (locked_p) abort();
2176 #endif /* !NO_LOCKING */