X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?p=xscreensaver;a=blobdiff_plain;f=driver%2Flock.c;h=e1d29dc92a958b31d8cbf3b6489e5997df8d012b;hp=169880b1b6a993f7ead31e3206406a7150241d00;hb=6b1c86cf395f59389e4ece4ea8f4bea2c332745b;hpb=3f438031d610c7e15fd33876a879b97e290e05fb diff --git a/driver/lock.c b/driver/lock.c index 169880b1..e1d29dc9 100644 --- a/driver/lock.c +++ b/driver/lock.c @@ -1,5 +1,5 @@ /* lock.c --- handling the password dialog for locking-mode. - * xscreensaver, Copyright (c) 1993-2005 Jamie Zawinski + * xscreensaver, Copyright (c) 1993-2007 Jamie Zawinski * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that @@ -17,6 +17,7 @@ # include "config.h" #endif +#include #include #include #include /* for time() */ @@ -24,25 +25,16 @@ #include #include "xscreensaver.h" #include "resources.h" +#include "mlstring.h" +#include "auth.h" #ifndef NO_LOCKING /* (mostly) whole file */ -#ifdef HAVE_SYSLOG -# include -#endif /* HAVE_SYSLOG */ - #ifdef HAVE_XHPDISABLERESET # include static void hp_lock_reset (saver_info *si, Bool lock_p); #endif /* HAVE_XHPDISABLERESET */ -#ifdef HAVE_VT_LOCKSWITCH -# include -# include -# include - static void linux_lock_vt_switch (saver_info *si, Bool lock_p); -#endif /* HAVE_VT_LOCKSWITCH */ - #ifdef HAVE_XF86VMODE # include static void xfree_lock_mode_switch (saver_info *si, Bool lock_p); @@ -58,6 +50,11 @@ ERROR! You must not include vroot.h in this file. #endif +#ifdef HAVE_UNAME +# include /* for hostname info */ +#endif /* HAVE_UNAME */ +#include + #ifndef VMS # include #else /* VMS */ @@ -75,18 +72,19 @@ vms_passwd_valid_p(char *pw, Bool verbose_p) #endif /* VMS */ +#define SAMPLE_INPUT "MMMMMMMMMMMM" + #undef MAX #define MAX(a,b) ((a)>(b)?(a):(b)) -enum passwd_state { pw_read, pw_ok, pw_null, pw_fail, pw_cancel, pw_time }; +typedef struct info_dialog_data info_dialog_data; struct passwd_dialog_data { saver_screen_info *prompt_screen; int previous_mouse_x, previous_mouse_y; - enum passwd_state state; char typed_passwd [80]; XtIntervalId timer; int i_beam; @@ -97,17 +95,25 @@ struct passwd_dialog_data { Dimension height; Dimension border_width; + Bool echo_input; Bool show_stars_p; /* "I regret that I have but one asterisk for my country." -- Nathan Hale, 1776. */ char *heading_label; char *body_label; char *user_label; - char *passwd_label; + mlstring *info_label; + /* The entry field shall only be displayed if prompt_label is not NULL */ + mlstring *prompt_label; char *date_label; - char *user_string; char *passwd_string; + Bool passwd_changed_p; /* Whether the user entry field needs redrawing */ + Bool caps_p; /* Whether we saw a keypress with caps-lock on */ + char *unlock_label; char *login_label; + char *uname_label; + + Bool show_uname_p; XFontStruct *heading_font; XFontStruct *body_font; @@ -115,6 +121,7 @@ struct passwd_dialog_data { XFontStruct *passwd_font; XFontStruct *date_font; XFontStruct *button_font; + XFontStruct *uname_font; Pixel foreground; Pixel background; @@ -127,8 +134,8 @@ struct passwd_dialog_data { Pixel button_foreground; Pixel button_background; - Dimension logo_width; - Dimension logo_height; + Dimension preferred_logo_width, logo_width; + Dimension preferred_logo_height, logo_height; Dimension thermo_width; Dimension internal_border; Dimension shadow_width; @@ -136,6 +143,9 @@ struct passwd_dialog_data { Dimension passwd_field_x, passwd_field_y; Dimension passwd_field_width, passwd_field_height; + Dimension unlock_button_x, unlock_button_y; + Dimension unlock_button_width, unlock_button_height; + Dimension login_button_x, login_button_y; Dimension login_button_width, login_button_height; @@ -143,15 +153,19 @@ struct passwd_dialog_data { Dimension thermo_field_height; Pixmap logo_pixmap; + Pixmap logo_clipmask; int logo_npixels; unsigned long *logo_pixels; Cursor passwd_cursor; + Bool unlock_button_down_p; Bool login_button_down_p; Bool login_button_p; Bool login_button_enabled_p; + Bool button_state_changed_p; /* Refers to both buttons */ Pixmap save_under; + Pixmap user_entry_pixmap; }; static void draw_passwd_window (saver_info *si); @@ -159,63 +173,60 @@ static void update_passwd_window (saver_info *si, const char *printed_passwd, float ratio); static void destroy_passwd_window (saver_info *si); static void undo_vp_motion (saver_info *si); -static void handle_passwd_button (saver_info *si, XEvent *event); +static void finished_typing_passwd (saver_info *si, passwd_dialog_data *pw); +static void cleanup_passwd_window (saver_info *si); +static void restore_background (saver_info *si); +extern void xss_authenticate(saver_info *si, Bool verbose_p); static void -make_passwd_window (saver_info *si) +new_passwd_window (saver_info *si) { - struct passwd *p = getpwuid (getuid ()); - XSetWindowAttributes attrs; - unsigned long attrmask = 0; - passwd_dialog_data *pw = (passwd_dialog_data *) calloc (1, sizeof(*pw)); + passwd_dialog_data *pw; Screen *screen; Colormap cmap; char *f; saver_screen_info *ssi = &si->screens [mouse_screen (si)]; + pw = (passwd_dialog_data *) calloc (1, sizeof(*pw)); + if (!pw) + return; + /* Display the button only if the "newLoginCommand" pref is non-null. */ pw->login_button_p = (si->prefs.new_login_command && *si->prefs.new_login_command); - if (pw->login_button_p) - pw->passwd_cursor = XCreateFontCursor (si->dpy, XC_top_left_arrow); - else - pw->passwd_cursor = 0; + pw->passwd_cursor = XCreateFontCursor (si->dpy, XC_top_left_arrow); pw->prompt_screen = ssi; - if (si->prefs.verbose_p) - fprintf (stderr, "%s: %d: creating password dialog.\n", - blurb(), pw->prompt_screen->number); screen = pw->prompt_screen->screen; cmap = DefaultColormapOfScreen (screen); - pw->ratio = 1.0; - - pw->show_stars_p = get_boolean_resource("passwd.asterisks", "Boolean"); + pw->show_stars_p = get_boolean_resource(si->dpy, "passwd.asterisks", + "Boolean"); - pw->heading_label = get_string_resource ("passwd.heading.label", + pw->heading_label = get_string_resource (si->dpy, "passwd.heading.label", "Dialog.Label.Label"); - pw->body_label = get_string_resource ("passwd.body.label", + pw->body_label = get_string_resource (si->dpy, "passwd.body.label", "Dialog.Label.Label"); - pw->user_label = get_string_resource ("passwd.user.label", + pw->user_label = get_string_resource (si->dpy, "passwd.user.label", "Dialog.Label.Label"); - pw->passwd_label = get_string_resource ("passwd.passwd.label", - "Dialog.Label.Label"); - pw->login_label = get_string_resource ("passwd.login.label", + pw->unlock_label = get_string_resource (si->dpy, "passwd.unlock.label", + "Dialog.Button.Label"); + pw->login_label = get_string_resource (si->dpy, "passwd.login.label", "Dialog.Button.Label"); - pw->date_label = get_string_resource ("dateFormat", "DateFormat"); + pw->date_label = get_string_resource (si->dpy, "dateFormat", "DateFormat"); if (!pw->heading_label) pw->heading_label = strdup("ERROR: RESOURCES NOT INSTALLED CORRECTLY"); if (!pw->body_label) pw->body_label = strdup("ERROR: RESOURCES NOT INSTALLED CORRECTLY"); if (!pw->user_label) pw->user_label = strdup("ERROR"); - if (!pw->passwd_label) pw->passwd_label = strdup("ERROR"); if (!pw->date_label) pw->date_label = strdup("ERROR"); + if (!pw->unlock_label) pw->unlock_label = strdup("ERROR (UNLOCK)"); if (!pw->login_label) pw->login_label = strdup ("ERROR (LOGIN)") ; /* Put the version number in the label. */ @@ -226,45 +237,74 @@ make_passwd_window (saver_info *si) pw->heading_label = s; } - pw->user_string = strdup (p && p->pw_name ? p->pw_name : "???"); + /* Get hostname info */ + pw->uname_label = strdup(""); /* Initialy, write nothing */ + +# ifdef HAVE_UNAME + { + struct utsname uts; + + if (uname (&uts) == 0) + { +#if 0 /* Get the full hostname */ + { + char *s; + if ((s = strchr(uts.nodename, '.'))) + *s = 0; + } +#endif + char *s = strdup (uts.nodename); + free (pw->uname_label); + pw->uname_label = s; + } + } +# endif + pw->passwd_string = strdup(""); - f = get_string_resource ("passwd.headingFont", "Dialog.Font"); + f = get_string_resource (si->dpy, "passwd.headingFont", "Dialog.Font"); pw->heading_font = XLoadQueryFont (si->dpy, (f ? f : "fixed")); if (!pw->heading_font) pw->heading_font = XLoadQueryFont (si->dpy, "fixed"); if (f) free (f); - f = get_string_resource ("passwd.buttonFont", "Dialog.Font"); + f = get_string_resource (si->dpy, "passwd.buttonFont", "Dialog.Font"); pw->button_font = XLoadQueryFont (si->dpy, (f ? f : "fixed")); if (!pw->button_font) pw->button_font = XLoadQueryFont (si->dpy, "fixed"); if (f) free (f); - f = get_string_resource("passwd.bodyFont", "Dialog.Font"); + f = get_string_resource(si->dpy, "passwd.bodyFont", "Dialog.Font"); pw->body_font = XLoadQueryFont (si->dpy, (f ? f : "fixed")); if (!pw->body_font) pw->body_font = XLoadQueryFont (si->dpy, "fixed"); if (f) free (f); - f = get_string_resource("passwd.labelFont", "Dialog.Font"); + f = get_string_resource(si->dpy, "passwd.labelFont", "Dialog.Font"); pw->label_font = XLoadQueryFont (si->dpy, (f ? f : "fixed")); if (!pw->label_font) pw->label_font = XLoadQueryFont (si->dpy, "fixed"); if (f) free (f); - f = get_string_resource("passwd.passwdFont", "Dialog.Font"); + f = get_string_resource(si->dpy, "passwd.passwdFont", "Dialog.Font"); pw->passwd_font = XLoadQueryFont (si->dpy, (f ? f : "fixed")); if (!pw->passwd_font) pw->passwd_font = XLoadQueryFont (si->dpy, "fixed"); if (f) free (f); - f = get_string_resource("passwd.dateFont", "Dialog.Font"); + f = get_string_resource(si->dpy, "passwd.dateFont", "Dialog.Font"); pw->date_font = XLoadQueryFont (si->dpy, (f ? f : "fixed")); if (!pw->date_font) pw->date_font = XLoadQueryFont (si->dpy, "fixed"); if (f) free (f); - pw->foreground = get_pixel_resource ("passwd.foreground", - "Dialog.Foreground", - si->dpy, cmap); - pw->background = get_pixel_resource ("passwd.background", - "Dialog.Background", - si->dpy, cmap); + f = get_string_resource(si->dpy, "passwd.unameFont", "Dialog.Font"); + pw->uname_font = XLoadQueryFont (si->dpy, (f ? f : "fixed")); + if (!pw->uname_font) pw->uname_font = XLoadQueryFont (si->dpy, "fixed"); + if (f) free (f); + + pw->show_uname_p = get_boolean_resource(si->dpy, "passwd.uname", "Boolean"); + + pw->foreground = get_pixel_resource (si->dpy, cmap, + "passwd.foreground", + "Dialog.Foreground" ); + pw->background = get_pixel_resource (si->dpy, cmap, + "passwd.background", + "Dialog.Background" ); if (pw->foreground == pw->background) { @@ -273,48 +313,155 @@ make_passwd_window (saver_info *si) pw->background = WhitePixelOfScreen (screen); } - pw->passwd_foreground = get_pixel_resource ("passwd.text.foreground", - "Dialog.Text.Foreground", - si->dpy, cmap); - pw->passwd_background = get_pixel_resource ("passwd.text.background", - "Dialog.Text.Background", - si->dpy, cmap); - pw->button_foreground = get_pixel_resource ("splash.Button.foreground", - "Dialog.Button.Foreground", - si->dpy, cmap); - pw->button_background = get_pixel_resource ("splash.Button.background", - "Dialog.Button.Background", - si->dpy, cmap); - pw->thermo_foreground = get_pixel_resource ("passwd.thermometer.foreground", - "Dialog.Thermometer.Foreground", - si->dpy, cmap); - pw->thermo_background = get_pixel_resource ("passwd.thermometer.background", - "Dialog.Thermometer.Background", - si->dpy, cmap); - pw->shadow_top = get_pixel_resource ("passwd.topShadowColor", - "Dialog.Foreground", - si->dpy, cmap); - pw->shadow_bottom = get_pixel_resource ("passwd.bottomShadowColor", - "Dialog.Background", - si->dpy, cmap); - - pw->logo_width = get_integer_resource ("passwd.logo.width", - "Dialog.Logo.Width"); - pw->logo_height = get_integer_resource ("passwd.logo.height", - "Dialog.Logo.Height"); - pw->thermo_width = get_integer_resource ("passwd.thermometer.width", + pw->passwd_foreground = get_pixel_resource (si->dpy, cmap, + "passwd.text.foreground", + "Dialog.Text.Foreground" ); + pw->passwd_background = get_pixel_resource (si->dpy, cmap, + "passwd.text.background", + "Dialog.Text.Background" ); + pw->button_foreground = get_pixel_resource (si->dpy, cmap, + "splash.Button.foreground", + "Dialog.Button.Foreground" ); + pw->button_background = get_pixel_resource (si->dpy, cmap, + "splash.Button.background", + "Dialog.Button.Background" ); + pw->thermo_foreground = get_pixel_resource (si->dpy, cmap, + "passwd.thermometer.foreground", + "Dialog.Thermometer.Foreground" ); + pw->thermo_background = get_pixel_resource ( si->dpy, cmap, + "passwd.thermometer.background", + "Dialog.Thermometer.Background" ); + pw->shadow_top = get_pixel_resource ( si->dpy, cmap, + "passwd.topShadowColor", + "Dialog.Foreground" ); + pw->shadow_bottom = get_pixel_resource (si->dpy, cmap, + "passwd.bottomShadowColor", + "Dialog.Background" ); + + pw->preferred_logo_width = get_integer_resource (si->dpy, "passwd.logo.width", + "Dialog.Logo.Width"); + pw->preferred_logo_height = get_integer_resource (si->dpy, "passwd.logo.height", + "Dialog.Logo.Height"); + pw->thermo_width = get_integer_resource (si->dpy, "passwd.thermometer.width", "Dialog.Thermometer.Width"); - pw->internal_border = get_integer_resource ("passwd.internalBorderWidth", + pw->internal_border = get_integer_resource (si->dpy, "passwd.internalBorderWidth", "Dialog.InternalBorderWidth"); - pw->shadow_width = get_integer_resource ("passwd.shadowThickness", + pw->shadow_width = get_integer_resource (si->dpy, "passwd.shadowThickness", "Dialog.ShadowThickness"); - if (pw->logo_width == 0) pw->logo_width = 150; - if (pw->logo_height == 0) pw->logo_height = 150; + if (pw->preferred_logo_width == 0) pw->preferred_logo_width = 150; + if (pw->preferred_logo_height == 0) pw->preferred_logo_height = 150; if (pw->internal_border == 0) pw->internal_border = 15; if (pw->shadow_width == 0) pw->shadow_width = 4; if (pw->thermo_width == 0) pw->thermo_width = pw->shadow_width; + + /* We need to remember the mouse position and restore it afterward, or + sometimes (perhaps only with Xinerama?) the mouse gets warped to + inside the bounds of the lock dialog window. + */ + { + Window pointer_root, pointer_child; + int root_x, root_y, win_x, win_y; + unsigned int mask; + pw->previous_mouse_x = 0; + pw->previous_mouse_y = 0; + if (XQueryPointer (si->dpy, RootWindowOfScreen (pw->prompt_screen->screen), + &pointer_root, &pointer_child, + &root_x, &root_y, &win_x, &win_y, &mask)) + { + pw->previous_mouse_x = root_x; + pw->previous_mouse_y = root_y; + if (si->prefs.verbose_p) + fprintf (stderr, "%s: %d: mouse is at %d,%d.\n", + blurb(), pw->prompt_screen->number, + pw->previous_mouse_x, pw->previous_mouse_y); + } + else if (si->prefs.verbose_p) + fprintf (stderr, "%s: %d: unable to determine mouse position?\n", + blurb(), pw->prompt_screen->number); + } + + /* Before mapping the window, save a pixmap of the current screen. + When we lower the window, we + restore these bits. This works, because the running screenhack + has already been sent SIGSTOP, so we know nothing else is drawing + right now! */ + { + XGCValues gcv; + GC gc; + pw->save_under = XCreatePixmap (si->dpy, + pw->prompt_screen->screensaver_window, + pw->prompt_screen->width, + pw->prompt_screen->height, + pw->prompt_screen->current_depth); + gcv.function = GXcopy; + gc = XCreateGC (si->dpy, pw->save_under, GCFunction, &gcv); + XCopyArea (si->dpy, pw->prompt_screen->screensaver_window, + pw->save_under, gc, + 0, 0, + pw->prompt_screen->width, pw->prompt_screen->height, + 0, 0); + XFreeGC (si->dpy, gc); + } + + si->pw_data = pw; +} + + +/** + * info_msg and prompt may be NULL. + */ +static void +make_passwd_window (saver_info *si, + const char *info_msg, + const char *prompt, + Bool echo) +{ + XSetWindowAttributes attrs; + unsigned long attrmask = 0; + passwd_dialog_data *pw; + Screen *screen; + Colormap cmap; + Dimension max_string_width_px; + saver_screen_info *ssi = &si->screens [mouse_screen (si)]; + + cleanup_passwd_window (si); + + if (!si->pw_data) + new_passwd_window (si); + + if (!(pw = si->pw_data)) + return; + + pw->ratio = 1.0; + + pw->prompt_screen = ssi; + if (si->prefs.verbose_p) + fprintf (stderr, "%s: %d: creating password dialog (\"%s\")\n", + blurb(), pw->prompt_screen->number, + info_msg ? info_msg : ""); + + screen = pw->prompt_screen->screen; + cmap = DefaultColormapOfScreen (screen); + + pw->echo_input = echo; + + max_string_width_px = ssi->width + - pw->shadow_width * 4 + - pw->border_width * 2 + - pw->thermo_width + - pw->preferred_logo_width + - pw->internal_border * 2; + /* As the string wraps it makes the window taller which makes the logo wider + * which leaves less room for the text which makes the string wrap. Uh-oh, a + * loop. By wrapping at a bit less than the available width, there's some + * room for the dialog to grow without going off the edge of the screen. */ + max_string_width_px *= 0.75; + + pw->info_label = mlstring_new(info_msg ? info_msg : pw->body_label, + pw->label_font, max_string_width_px); + { int direction, ascent, descent; XCharStruct overall; @@ -329,17 +476,20 @@ make_passwd_window (saver_info *si) if (overall.width > pw->width) pw->width = overall.width; pw->height += ascent + descent; - /* Measure the body_label. */ - XTextExtents (pw->body_font, - pw->body_label, strlen(pw->body_label), - &direction, &ascent, &descent, &overall); - if (overall.width > pw->width) pw->width = overall.width; - pw->height += ascent + descent; + /* Measure the uname_label. */ + if ((strlen(pw->uname_label)) && pw->show_uname_p) + { + XTextExtents (pw->uname_font, + pw->uname_label, strlen(pw->uname_label), + &direction, &ascent, &descent, &overall); + if (overall.width > pw->width) pw->width = overall.width; + pw->height += ascent + descent; + } { Dimension w2 = 0, w3 = 0, button_w = 0; Dimension h2 = 0, h3 = 0, button_h = 0; - const char *passwd_string = "MMMMMMMMMMMM"; + const char *passwd_string = SAMPLE_INPUT; /* Measure the user_label. */ XTextExtents (pw->label_font, @@ -348,34 +498,62 @@ make_passwd_window (saver_info *si) if (overall.width > w2) w2 = overall.width; h2 += ascent + descent; - /* Measure the passwd_label. */ - XTextExtents (pw->label_font, - pw->passwd_label, strlen(pw->passwd_label), - &direction, &ascent, &descent, &overall); - if (overall.width > w2) w2 = overall.width; - h2 += ascent + descent; + /* Measure the info_label. */ + if (pw->info_label->overall_width > pw->width) pw->width = pw->info_label->overall_width; + h2 += pw->info_label->overall_height; - /* Measure the user_string. */ + /* Measure the user string. */ XTextExtents (pw->passwd_font, - pw->user_string, strlen(pw->user_string), + si->user, strlen(si->user), &direction, &ascent, &descent, &overall); overall.width += (pw->shadow_width * 4); ascent += (pw->shadow_width * 4); if (overall.width > w3) w3 = overall.width; h3 += ascent + descent; - /* Measure the (maximally-sized, dummy) passwd_string. */ - XTextExtents (pw->passwd_font, - passwd_string, strlen(passwd_string), + /* Measure the (dummy) passwd_string. */ + if (prompt) + { + XTextExtents (pw->passwd_font, + passwd_string, strlen(passwd_string), + &direction, &ascent, &descent, &overall); + overall.width += (pw->shadow_width * 4); + ascent += (pw->shadow_width * 4); + if (overall.width > w3) w3 = overall.width; + h3 += ascent + descent; + + /* Measure the prompt_label. */ + max_string_width_px -= w3; + pw->prompt_label = mlstring_new(prompt, pw->label_font, max_string_width_px); + + if (pw->prompt_label->overall_width > w2) w2 = pw->prompt_label->overall_width; + + h2 += pw->prompt_label->overall_height; + + w2 = w2 + w3 + (pw->shadow_width * 2); + h2 = MAX (h2, h3); + } + + /* The "Unlock" button. */ + XTextExtents (pw->label_font, + pw->unlock_label, strlen(pw->unlock_label), &direction, &ascent, &descent, &overall); - overall.width += (pw->shadow_width * 4); - ascent += (pw->shadow_width * 4); - if (overall.width > w3) w3 = overall.width; - h3 += ascent + descent; + button_w = overall.width; + button_h = ascent + descent; + + /* Add some horizontal padding inside the button. */ + button_w += ascent; + + button_w += ((ascent + descent) / 2) + (pw->shadow_width * 2); + button_h += ((ascent + descent) / 2) + (pw->shadow_width * 2); - w2 = w2 + w3 + (pw->shadow_width * 2); - h2 = MAX (h2, h3); + pw->unlock_button_width = button_w; + pw->unlock_button_height = button_h; + w2 = MAX (w2, button_w); + h2 += button_h * 1.5; + + /* The "New Login" button */ pw->login_button_width = 0; pw->login_button_height = 0; @@ -399,8 +577,12 @@ make_passwd_window (saver_info *si) pw->login_button_width = button_w; pw->login_button_height = button_h; - w2 = MAX (w2, button_w); - h2 += button_h * 1.5; + if (button_h > pw->unlock_button_height) + h2 += (button_h * 1.5 - pw->unlock_button_height * 1.5); + + /* Use (2 * shadow_width) spacing between the buttons. Another + (2 * shadow_width) is required to account for button shadows. */ + w2 = MAX (w2, button_w + pw->unlock_button_width + (pw->shadow_width * 4)); } if (w2 > pw->width) pw->width = w2; @@ -412,9 +594,9 @@ make_passwd_window (saver_info *si) pw->width += pw->thermo_width + (pw->shadow_width * 3); - if (pw->logo_height > pw->height) - pw->height = pw->logo_height; - else if (pw->height > pw->logo_height) + if (pw->preferred_logo_height > pw->height) + pw->height = pw->logo_height = pw->preferred_logo_height; + else if (pw->height > pw->preferred_logo_height) pw->logo_height = pw->height; pw->logo_width = pw->logo_height; @@ -428,32 +610,6 @@ make_passwd_window (saver_info *si) attrs.event_mask = (ExposureMask | KeyPressMask | ButtonPressMask | ButtonReleaseMask); - /* We need to remember the mouse position and restore it afterward, or - sometimes (perhaps only with Xinerama?) the mouse gets warped to - inside the bounds of the lock dialog window. - */ - { - Window pointer_root, pointer_child; - int root_x, root_y, win_x, win_y; - unsigned int mask; - pw->previous_mouse_x = 0; - pw->previous_mouse_y = 0; - if (XQueryPointer (si->dpy, RootWindowOfScreen (pw->prompt_screen->screen), - &pointer_root, &pointer_child, - &root_x, &root_y, &win_x, &win_y, &mask)) - { - pw->previous_mouse_x = root_x; - pw->previous_mouse_y = root_y; - if (si->prefs.verbose_p) - fprintf (stderr, "%s: %d: mouse is at %d,%d.\n", - blurb(), pw->prompt_screen->number, - pw->previous_mouse_x, pw->previous_mouse_y); - } - else if (si->prefs.verbose_p) - fprintf (stderr, "%s: %d: unable to determine mouse position?\n", - blurb(), pw->prompt_screen->number); - } - /* Figure out where on the desktop to place the window so that it will actually be visible; this takes into account virtual viewports as well as Xinerama. */ @@ -469,59 +625,51 @@ make_passwd_window (saver_info *si) if (pw->y < y) pw->y = y; } - pw->border_width = get_integer_resource ("passwd.borderWidth", + pw->border_width = get_integer_resource (si->dpy, "passwd.borderWidth", "Dialog.BorderWidth"); - si->passwd_dialog = - XCreateWindow (si->dpy, - RootWindowOfScreen(screen), - pw->x, pw->y, pw->width, pw->height, pw->border_width, - DefaultDepthOfScreen (screen), InputOutput, - DefaultVisualOfScreen(screen), - attrmask, &attrs); - XSetWindowBackground (si->dpy, si->passwd_dialog, pw->background); - - /* We use the default visual, not ssi->visual, so that the logo pixmap's - visual matches that of the si->passwd_dialog window. */ - pw->logo_pixmap = xscreensaver_logo (ssi->screen, - /* ssi->current_visual, */ - DefaultVisualOfScreen(screen), - si->passwd_dialog, cmap, - pw->background, - &pw->logo_pixels, &pw->logo_npixels, - 0, True); - - /* Before mapping the window, save the bits that are underneath the - rectangle the window will occlude. When we lower the window, we - restore these bits. This works, because the running screenhack - has already been sent SIGSTOP, so we know nothing else is drawing - right now! */ - { - XGCValues gcv; - GC gc; - pw->save_under = XCreatePixmap (si->dpy, - pw->prompt_screen->screensaver_window, - pw->width + (pw->border_width*2) + 1, - pw->height + (pw->border_width*2) + 1, - pw->prompt_screen->current_depth); - gcv.function = GXcopy; - gc = XCreateGC (si->dpy, pw->save_under, GCFunction, &gcv); - XCopyArea (si->dpy, pw->prompt_screen->screensaver_window, - pw->save_under, gc, - pw->x - pw->border_width, pw->y - pw->border_width, - pw->width + (pw->border_width*2) + 1, - pw->height + (pw->border_width*2) + 1, - 0, 0); - XFreeGC (si->dpy, gc); - } + /* Only create the window the first time around */ + if (!si->passwd_dialog) + { + si->passwd_dialog = + XCreateWindow (si->dpy, + RootWindowOfScreen(screen), + pw->x, pw->y, pw->width, pw->height, pw->border_width, + DefaultDepthOfScreen (screen), InputOutput, + DefaultVisualOfScreen(screen), + attrmask, &attrs); + XSetWindowBackground (si->dpy, si->passwd_dialog, pw->background); + + /* We use the default visual, not ssi->visual, so that the logo pixmap's + visual matches that of the si->passwd_dialog window. */ + pw->logo_pixmap = xscreensaver_logo (ssi->screen, + /* ssi->current_visual, */ + DefaultVisualOfScreen(screen), + si->passwd_dialog, cmap, + pw->background, + &pw->logo_pixels, &pw->logo_npixels, + &pw->logo_clipmask, True); + } + else /* On successive prompts, just resize the window */ + { + XWindowChanges wc; + unsigned int mask = CWX | CWY | CWWidth | CWHeight; + + wc.x = pw->x; + wc.y = pw->y; + wc.width = pw->width; + wc.height = pw->height; + + XConfigureWindow (si->dpy, si->passwd_dialog, mask, &wc); + } + + restore_background(si); XMapRaised (si->dpy, si->passwd_dialog); XSync (si->dpy, False); move_mouse_grab (si, si->passwd_dialog, - (pw->passwd_cursor - ? pw->passwd_cursor - : pw->prompt_screen->cursor), + pw->passwd_cursor, pw->prompt_screen->number); undo_vp_motion (si); @@ -530,7 +678,6 @@ make_passwd_window (saver_info *si) if (cmap) XInstallColormap (si->dpy, cmap); draw_passwd_window (si); - XSync (si->dpy, False); } @@ -545,21 +692,30 @@ draw_passwd_window (saver_info *si) int sw; int tb_height; + /* Force redraw */ + pw->passwd_changed_p = True; + pw->button_state_changed_p = True; + + /* This height is the height of all the elements, not to be confused with + * the overall window height which is pw->height. It is used to compute + * the amount of spacing (padding) between elements. */ height = (pw->heading_font->ascent + pw->heading_font->descent + - pw->body_font->ascent + pw->body_font->descent + - (2 * MAX ((pw->label_font->ascent + pw->label_font->descent), - (pw->passwd_font->ascent + pw->passwd_font->descent + - (pw->shadow_width * 4)))) + + pw->info_label->overall_height + + MAX (((pw->label_font->ascent + pw->label_font->descent) + + (pw->prompt_label ? pw->prompt_label->overall_height : 0)), + ((pw->passwd_font->ascent + pw->passwd_font->descent) + + (pw->shadow_width * 2)) * (pw->prompt_label ? 2 : 1)) + pw->date_font->ascent + pw->date_font->descent); - if (pw->login_button_p) - height += ((pw->button_font->ascent + pw->button_font->descent) * 2 + - 2 * pw->shadow_width); + if ((strlen(pw->uname_label)) && pw->show_uname_p) + height += (pw->uname_font->ascent + pw->uname_font->descent); /* for uname */ - spacing = (((pw->height - - ((pw->login_button_p ? 4 : 2) * pw->shadow_width) - - pw->internal_border - height)) - / 8); + height += ((pw->button_font->ascent + pw->button_font->descent) * 2 + + 2 * pw->shadow_width); + + spacing = ((pw->height - 2 * pw->shadow_width + - pw->internal_border - height) + / 10); if (spacing < 0) spacing = 0; @@ -579,14 +735,25 @@ draw_passwd_window (saver_info *si) XDrawString (si->dpy, si->passwd_dialog, gc1, x2, y1, pw->heading_label, strlen(pw->heading_label)); - /* text below top heading + /* uname below top heading */ - XSetFont (si->dpy, gc1, pw->body_font->fid); - y1 += spacing + pw->body_font->ascent + pw->body_font->descent; - sw = string_width (pw->body_font, pw->body_label); - x2 = (x1 + ((x3 - x1 - sw) / 2)); - XDrawString (si->dpy, si->passwd_dialog, gc1, x2, y1, - pw->body_label, strlen(pw->body_label)); + if ((strlen(pw->uname_label)) && pw->show_uname_p) + { + XSetFont (si->dpy, gc1, pw->uname_font->fid); + y1 += spacing + pw->uname_font->ascent + pw->uname_font->descent; + sw = string_width (pw->uname_font, pw->uname_label); + x2 = (x1 + ((x3 - x1 - sw) / 2)); + XDrawString (si->dpy, si->passwd_dialog, gc1, x2, y1, + pw->uname_label, strlen(pw->uname_label)); + } + + /* the info_label (below uname) + */ + x2 = (x1 + ((x3 - x1 - pw->info_label->overall_width) / 2)); + y1 += spacing + pw->info_label->font_height / 2; + mlstring_draw(si->dpy, si->passwd_dialog, gc1, pw->info_label, + x2, y1); + y1 += pw->info_label->overall_height; tb_height = (pw->passwd_font->ascent + pw->passwd_font->descent + @@ -594,34 +761,32 @@ draw_passwd_window (saver_info *si) /* the "User:" prompt */ - y1 += spacing; y2 = y1; XSetForeground (si->dpy, gc1, pw->foreground); XSetFont (si->dpy, gc1, pw->label_font->fid); - y1 += (spacing + tb_height); + y1 += (spacing + tb_height + pw->shadow_width); x2 = (x1 + pw->internal_border + MAX(string_width (pw->label_font, pw->user_label), - string_width (pw->label_font, pw->passwd_label))); + pw->prompt_label ? pw->prompt_label->overall_width : 0)); XDrawString (si->dpy, si->passwd_dialog, gc1, x2 - string_width (pw->label_font, pw->user_label), y1 - pw->passwd_font->descent, pw->user_label, strlen(pw->user_label)); - /* the "Password:" prompt + /* the prompt_label prompt */ - y1 += (spacing + tb_height); - XDrawString (si->dpy, si->passwd_dialog, gc1, - x2 - string_width (pw->label_font, pw->passwd_label), - y1 - pw->passwd_font->descent, - pw->passwd_label, strlen(pw->passwd_label)); - - - XSetForeground (si->dpy, gc2, pw->passwd_background); + if (pw->prompt_label) + { + y1 += tb_height - pw->label_font->ascent + pw->shadow_width; + mlstring_draw(si->dpy, si->passwd_dialog, gc1, pw->prompt_label, + x2 - pw->prompt_label->overall_width, y1); + } /* the "user name" text field */ y1 = y2; XSetForeground (si->dpy, gc1, pw->passwd_foreground); + XSetForeground (si->dpy, gc2, pw->passwd_background); XSetFont (si->dpy, gc1, pw->passwd_font->fid); y1 += (spacing + tb_height); x2 += (pw->shadow_width * 4); @@ -638,15 +803,18 @@ draw_passwd_window (saver_info *si) XDrawString (si->dpy, si->passwd_dialog, gc1, x2, y1 - pw->passwd_font->descent, - pw->user_string, strlen(pw->user_string)); + si->user, strlen(si->user)); - /* the "password" text field + /* the password/prompt text field */ - y1 += (spacing + tb_height); + if (pw->prompt_label) + { + y1 += (spacing + pw->prompt_label->overall_height + pw->shadow_width * 2); - pw->passwd_field_x = x2 - pw->shadow_width; - pw->passwd_field_y = y1 - (pw->passwd_font->ascent + - pw->passwd_font->descent); + pw->passwd_field_x = x2 - pw->shadow_width; + pw->passwd_field_y = y1 - (pw->passwd_font->ascent + + pw->passwd_font->descent); + } /* The shadow around the text fields */ @@ -661,12 +829,14 @@ draw_passwd_window (saver_info *si) pw->shadow_width, pw->shadow_bottom, pw->shadow_top); - y1 += (spacing + pw->passwd_font->ascent + pw->passwd_font->descent + - (pw->shadow_width * 4)); - draw_shaded_rectangle (si->dpy, si->passwd_dialog, - x1, y1, x2, y2, - pw->shadow_width, - pw->shadow_bottom, pw->shadow_top); + if (pw->prompt_label) + { + y1 += (spacing + pw->prompt_label->overall_height + pw->shadow_width * 2); + draw_shaded_rectangle (si->dpy, si->passwd_dialog, + x1, y1, x2, y2, + pw->shadow_width, + pw->shadow_bottom, pw->shadow_top); + } /* The date, below the text fields @@ -687,33 +857,38 @@ draw_passwd_window (saver_info *si) XDrawString (si->dpy, si->passwd_dialog, gc1, x2, y1, buf, strlen(buf)); } + /* Set up the GCs for the "New Login" and "Unlock" buttons. + */ + XSetForeground(si->dpy, gc1, pw->button_foreground); + XSetForeground(si->dpy, gc2, pw->button_background); + XSetFont(si->dpy, gc1, pw->button_font->fid); + + /* The "Unlock" button */ + x2 = pw->width - pw->internal_border - (pw->shadow_width * 2); + + /* right aligned button */ + x1 = x2 - pw->unlock_button_width; + + /* Add half the difference between y1 and the internal edge. + * It actually looks better if the internal border is ignored. */ + y1 += ((pw->height - MAX (pw->unlock_button_height, pw->login_button_height) + - spacing - y1) + / 2); + + pw->unlock_button_x = x1; + pw->unlock_button_y = y1; + /* The "New Login" button */ if (pw->login_button_p) { - XSetForeground (si->dpy, gc1, pw->button_foreground); - XSetForeground (si->dpy, gc2, pw->button_background); - XSetFont (si->dpy, gc1, pw->button_font->fid); + /* Using the same GC as for the Unlock button */ sw = string_width (pw->button_font, pw->login_label); - x2 = pw->width - pw->internal_border - (pw->shadow_width * 2); - - /* right aligned button */ - /* x1 = x2 - pw->login_button_width; */ - - /* centered button */ + /* left aligned button */ x1 = (pw->logo_width + pw->thermo_width + (pw->shadow_width * 3) + - pw->internal_border); - x1 = x1 + (x2 - x1 - pw->login_button_width) / 2; - - y1 = (pw->height - pw->internal_border - pw->login_button_height + - spacing); - y2 = (y1 + - ((pw->login_button_height - - (pw->button_font->ascent + pw->button_font->descent)) - / 2) + - pw->button_font->ascent); + pw->internal_border); pw->login_button_x = x1; pw->login_button_y = y1; @@ -734,6 +909,8 @@ draw_passwd_window (saver_info *si) XGetGeometry (si->dpy, pw->logo_pixmap, &root, &x, &y, &w, &h, &bw, &d); XSetForeground (si->dpy, gc1, pw->foreground); XSetBackground (si->dpy, gc1, pw->background); + XSetClipMask (si->dpy, gc1, pw->logo_clipmask); + XSetClipOrigin (si->dpy, gc1, x1 + ((x2 - (int)w) / 2), y1 + ((y2 - (int)h) / 2)); if (d == 1) XCopyPlane (si->dpy, pw->logo_pixmap, si->passwd_dialog, gc1, 0, 0, w, h, @@ -802,6 +979,51 @@ draw_passwd_window (saver_info *si) update_passwd_window (si, pw->passwd_string, pw->ratio); } +static void +draw_button(Display *dpy, + Drawable dialog, + XFontStruct *font, + unsigned long foreground, unsigned long background, + char *label, + int x, int y, + int width, int height, + int shadow_width, + Pixel shadow_light, Pixel shadow_dark, + Bool button_down) +{ + XGCValues gcv; + GC gc1, gc2; + int sw; + int label_x, label_y; + + gcv.foreground = foreground; + gcv.font = font->fid; + gc1 = XCreateGC(dpy, dialog, GCForeground|GCFont, &gcv); + gcv.foreground = background; + gc2 = XCreateGC(dpy, dialog, GCForeground, &gcv); + + XFillRectangle(dpy, dialog, gc2, + x, y, width, height); + + sw = string_width(font, label); + + label_x = x + ((width - sw) / 2); + label_y = (y + (height - (font->ascent + font->descent)) / 2 + font->ascent); + + if (button_down) + { + label_x += 2; + label_y += 2; + } + + XDrawString(dpy, dialog, gc1, label_x, label_y, label, strlen(label)); + + XFreeGC(dpy, gc1); + XFreeGC(dpy, gc2); + + draw_shaded_rectangle(dpy, dialog, x, y, width, height, + shadow_width, shadow_light, shadow_dark); +} static void update_passwd_window (saver_info *si, const char *printed_passwd, float ratio) @@ -826,42 +1048,73 @@ update_passwd_window (saver_info *si, const char *printed_passwd, float ratio) pw->passwd_string = s; } - /* the "password" text field - */ - rects[0].x = pw->passwd_field_x; - rects[0].y = pw->passwd_field_y; - rects[0].width = pw->passwd_field_width; - rects[0].height = pw->passwd_field_height; + if (pw->prompt_label) + { - XFillRectangle (si->dpy, si->passwd_dialog, gc2, - rects[0].x, rects[0].y, rects[0].width, rects[0].height); + /* the "password" text field + */ + rects[0].x = pw->passwd_field_x; + rects[0].y = pw->passwd_field_y; + rects[0].width = pw->passwd_field_width; + rects[0].height = pw->passwd_field_height; - XSetClipRectangles (si->dpy, gc1, 0, 0, rects, 1, Unsorted); + /* The user entry (password) field is double buffered. + * This avoids flickering, particularly in synchronous mode. */ - XDrawString (si->dpy, si->passwd_dialog, gc1, - rects[0].x + pw->shadow_width, - rects[0].y + pw->passwd_font->ascent, - pw->passwd_string, strlen(pw->passwd_string)); + if (pw->passwd_changed_p) + { + pw->passwd_changed_p = False; - XSetClipMask (si->dpy, gc1, None); + if (pw->user_entry_pixmap) + { + XFreePixmap(si->dpy, pw->user_entry_pixmap); + pw->user_entry_pixmap = 0; + } - /* The I-beam - */ - if (pw->i_beam != 0) - { - x = (rects[0].x + pw->shadow_width + - string_width (pw->passwd_font, pw->passwd_string)); - y = rects[0].y + pw->shadow_width; - - if (x > rects[0].x + rects[0].width - 1) - x = rects[0].x + rects[0].width - 1; - XDrawLine (si->dpy, si->passwd_dialog, gc1, - x, y, - x, y + pw->passwd_font->ascent + pw->passwd_font->descent-1); - } + pw->user_entry_pixmap = + XCreatePixmap (si->dpy, si->passwd_dialog, + rects[0].width, rects[0].height, + DefaultDepthOfScreen (pw->prompt_screen->screen)); - pw->i_beam = (pw->i_beam + 1) % 4; + XFillRectangle (si->dpy, pw->user_entry_pixmap, gc2, + 0, 0, rects[0].width, rects[0].height); + XDrawString (si->dpy, pw->user_entry_pixmap, gc1, + pw->shadow_width, + pw->passwd_font->ascent, + pw->passwd_string, strlen(pw->passwd_string)); + + /* Ensure the new pixmap gets copied to the window */ + pw->i_beam = 0; + + } + + /* The I-beam + */ + if (pw->i_beam == 0) + { + /* Make the I-beam disappear */ + XCopyArea(si->dpy, pw->user_entry_pixmap, si->passwd_dialog, gc2, + 0, 0, rects[0].width, rects[0].height, + rects[0].x, rects[0].y); + } + else if (pw->i_beam == 1) + { + /* Make the I-beam appear */ + x = (rects[0].x + pw->shadow_width + + string_width (pw->passwd_font, pw->passwd_string)); + y = rects[0].y + pw->shadow_width; + + if (x > rects[0].x + rects[0].width - 1) + x = rects[0].x + rects[0].width - 1; + XDrawLine (si->dpy, si->passwd_dialog, gc1, + x, y, + x, y + pw->passwd_font->ascent + pw->passwd_font->descent-1); + } + + pw->i_beam = (pw->i_beam + 1) % 4; + + } /* the thermometer */ @@ -881,43 +1134,43 @@ update_passwd_window (saver_info *si, const char *printed_passwd, float ratio) MAX (0, pw->thermo_field_height - y - 2)); } - /* The "New Login" button - */ - if (pw->login_button_p) + if (pw->button_state_changed_p) { - int x2, y2, sw; - XSetFont (si->dpy, gc1, pw->button_font->fid); - XSetForeground (si->dpy, gc1, - (pw->login_button_enabled_p - ? pw->passwd_foreground - : pw->shadow_bottom)); - XSetForeground (si->dpy, gc2, pw->button_background); - - XFillRectangle (si->dpy, si->passwd_dialog, gc2, - pw->login_button_x, pw->login_button_y, - pw->login_button_width, pw->login_button_height); - - sw = string_width (pw->button_font, pw->login_label); - x2 = pw->login_button_x + ((pw->login_button_width - sw) / 2); - y2 = (pw->login_button_y + - ((pw->login_button_height - - (pw->button_font->ascent + pw->button_font->descent)) - / 2) + - pw->button_font->ascent); - - XDrawString (si->dpy, si->passwd_dialog, gc1, x2, y2, - pw->login_label, strlen(pw->login_label)); + pw->button_state_changed_p = False; - draw_shaded_rectangle (si->dpy, si->passwd_dialog, - pw->login_button_x, pw->login_button_y, - pw->login_button_width, pw->login_button_height, - pw->shadow_width, - (pw->login_button_down_p - ? pw->shadow_bottom - : pw->shadow_top), - (pw->login_button_down_p - ? pw->shadow_top - : pw->shadow_bottom)); + /* The "Unlock" button + */ + draw_button(si->dpy, si->passwd_dialog, pw->button_font, + pw->button_foreground, pw->button_background, + pw->unlock_label, + pw->unlock_button_x, pw->unlock_button_y, + pw->unlock_button_width, pw->unlock_button_height, + pw->shadow_width, + (pw->unlock_button_down_p ? pw->shadow_bottom : pw->shadow_top), + (pw->unlock_button_down_p ? pw->shadow_top : pw->shadow_bottom), + pw->unlock_button_down_p); + + /* The "New Login" button + */ + if (pw->login_button_p) + { + draw_button(si->dpy, si->passwd_dialog, pw->button_font, + (pw->login_button_enabled_p + ? pw->passwd_foreground + : pw->shadow_bottom), + pw->button_background, + pw->login_label, + pw->login_button_x, pw->login_button_y, + pw->login_button_width, pw->login_button_height, + pw->shadow_width, + (pw->login_button_down_p + ? pw->shadow_bottom + : pw->shadow_top), + (pw->login_button_down_p + ? pw->shadow_top + : pw->shadow_bottom), + pw->login_button_down_p); + } } XFreeGC (si->dpy, gc1); @@ -926,6 +1179,66 @@ update_passwd_window (saver_info *si, const char *printed_passwd, float ratio) } +void +restore_background (saver_info *si) +{ + passwd_dialog_data *pw = si->pw_data; + saver_screen_info *ssi = pw->prompt_screen; + XGCValues gcv; + GC gc; + + gcv.function = GXcopy; + + gc = XCreateGC (si->dpy, ssi->screensaver_window, GCFunction, &gcv); + + XCopyArea (si->dpy, pw->save_under, + ssi->screensaver_window, gc, + 0, 0, + ssi->width, ssi->height, + 0, 0); + + XFreeGC (si->dpy, gc); +} + + +/* Frees anything created by make_passwd_window */ +static void +cleanup_passwd_window (saver_info *si) +{ + passwd_dialog_data *pw; + + if (!(pw = si->pw_data)) + return; + + if (pw->info_label) + { + mlstring_free(pw->info_label); + pw->info_label = 0; + } + + if (pw->prompt_label) + { + mlstring_free(pw->prompt_label); + pw->prompt_label = 0; + } + + memset (pw->typed_passwd, 0, sizeof(pw->typed_passwd)); + memset (pw->passwd_string, 0, strlen(pw->passwd_string)); + + if (pw->timer) + { + XtRemoveTimeOut (pw->timer); + pw->timer = 0; + } + + if (pw->user_entry_pixmap) + { + XFreePixmap(si->dpy, pw->user_entry_pixmap); + pw->user_entry_pixmap = 0; + } +} + + static void destroy_passwd_window (saver_info *si) { @@ -937,11 +1250,18 @@ destroy_passwd_window (saver_info *si) Pixel white = WhitePixelOfScreen (ssi->screen); XEvent event; - memset (pw->typed_passwd, 0, sizeof(pw->typed_passwd)); - memset (pw->passwd_string, 0, strlen(pw->passwd_string)); + cleanup_passwd_window (si); - if (pw->timer) - XtRemoveTimeOut (pw->timer); + if (si->cached_passwd) + { + char *wipe = si->cached_passwd; + + while (*wipe) + *wipe++ = '\0'; + + free(si->cached_passwd); + si->cached_passwd = NULL; + } move_mouse_grab (si, RootWindowOfScreen (ssi->screen), ssi->cursor, ssi->number); @@ -958,42 +1278,35 @@ destroy_passwd_window (saver_info *si) 0, 0, 0, 0, pw->previous_mouse_x, pw->previous_mouse_y); - XSync (si->dpy, False); while (XCheckMaskEvent (si->dpy, PointerMotionMask, &event)) if (p->verbose_p) fprintf (stderr, "%s: discarding MotionNotify event.\n", blurb()); if (si->passwd_dialog) { + if (si->prefs.verbose_p) + fprintf (stderr, "%s: %d: destroying password dialog.\n", + blurb(), pw->prompt_screen->number); + XDestroyWindow (si->dpy, si->passwd_dialog); si->passwd_dialog = 0; } if (pw->save_under) { - XGCValues gcv; - GC gc; - gcv.function = GXcopy; - gc = XCreateGC (si->dpy, ssi->screensaver_window, GCFunction, &gcv); - XCopyArea (si->dpy, pw->save_under, - ssi->screensaver_window, gc, - 0, 0, - pw->width + (pw->border_width*2) + 1, - pw->height + (pw->border_width*2) + 1, - pw->x - pw->border_width, pw->y - pw->border_width); + restore_background(si); XFreePixmap (si->dpy, pw->save_under); pw->save_under = 0; - XFreeGC (si->dpy, gc); } if (pw->heading_label) free (pw->heading_label); if (pw->body_label) free (pw->body_label); if (pw->user_label) free (pw->user_label); - if (pw->passwd_label) free (pw->passwd_label); if (pw->date_label) free (pw->date_label); if (pw->login_label) free (pw->login_label); - if (pw->user_string) free (pw->user_string); + if (pw->unlock_label) free (pw->unlock_label); if (pw->passwd_string) free (pw->passwd_string); + if (pw->uname_label) free (pw->uname_label); if (pw->heading_font) XFreeFont (si->dpy, pw->heading_font); if (pw->body_font) XFreeFont (si->dpy, pw->body_font); @@ -1001,6 +1314,7 @@ destroy_passwd_window (saver_info *si) if (pw->passwd_font) XFreeFont (si->dpy, pw->passwd_font); if (pw->date_font) XFreeFont (si->dpy, pw->date_font); if (pw->button_font) XFreeFont (si->dpy, pw->button_font); + if (pw->uname_font) XFreeFont (si->dpy, pw->uname_font); if (pw->foreground != black && pw->foreground != white) XFreeColors (si->dpy, cmap, &pw->foreground, 1, 0L); @@ -1025,6 +1339,8 @@ destroy_passwd_window (saver_info *si) if (pw->logo_pixmap) XFreePixmap (si->dpy, pw->logo_pixmap); + if (pw-> logo_clipmask) + XFreePixmap (si->dpy, pw->logo_clipmask); if (pw->logo_pixels) { if (pw->logo_npixels) @@ -1131,67 +1447,6 @@ xfree_lock_grab_smasher (saver_info *si, Bool lock_p) #endif /* HAVE_XF86MISCSETGRABKEYSSTATE */ - - -/* This function enables and disables the C-Sh-F1 ... F12 hot-keys, - which, on Linux systems, switches to another virtual console. - We'd like the whole screen/keyboard to be locked, not just one - virtual console, so this function disables that while the X - screen is locked. - - Unfortunately, this doesn't work -- this ioctl only works when - called by root, and we have disavowed our privileges long ago. - */ -#ifdef HAVE_VT_LOCKSWITCH -static void -linux_lock_vt_switch (saver_info *si, Bool lock_p) -{ - saver_preferences *p = &si->prefs; - static Bool vt_locked_p = False; - const char *dev_console = "/dev/console"; - int fd; - - if (lock_p == vt_locked_p) - return; - - if (lock_p && !p->lock_vt_p) - return; - - fd = open (dev_console, O_RDWR); - if (fd < 0) - { - char buf [255]; - sprintf (buf, "%s: couldn't %s VTs: %s", blurb(), - (lock_p ? "lock" : "unlock"), - dev_console); -#if 1 /* #### doesn't work yet, so don't bother complaining */ - perror (buf); -#endif - return; - } - - if (ioctl (fd, (lock_p ? VT_LOCKSWITCH : VT_UNLOCKSWITCH)) == 0) - { - vt_locked_p = lock_p; - - if (p->verbose_p) - fprintf (stderr, "%s: %s VTs\n", blurb(), - (lock_p ? "locked" : "unlocked")); - } - else - { - char buf [255]; - sprintf (buf, "%s: couldn't %s VTs: ioctl", blurb(), - (lock_p ? "lock" : "unlock")); -#if 0 /* #### doesn't work yet, so don't bother complaining */ - perror (buf); -#endif - } - - close (fd); -} -#endif /* HAVE_VT_LOCKSWITCH */ - /* This function enables and disables the C-Alt-Plus and C-Alt-Minus hot-keys, which normally change the resolution of the X server. @@ -1315,13 +1570,13 @@ passwd_animate_timer (XtPointer closure, XtIntervalId *id) if (pw->ratio < 0) { pw->ratio = 0; - if (pw->state == pw_read) - pw->state = pw_time; + if (si->unlock_state == ul_read) + si->unlock_state = ul_time; } update_passwd_window (si, 0, pw->ratio); - if (pw->state == pw_read) + if (si->unlock_state == ul_read) pw->timer = XtAppAddTimeOut (si->app, tick, passwd_animate_timer, (XtPointer) si); else @@ -1334,7 +1589,7 @@ passwd_animate_timer (XtPointer closure, XtIntervalId *id) static XComposeStatus *compose_status; static void -handle_passwd_button (saver_info *si, XEvent *event) +handle_login_button (saver_info *si, XEvent *event) { saver_preferences *p = &si->prefs; Bool mouse_in_box = False; @@ -1373,10 +1628,44 @@ handle_passwd_button (saver_info *si, XEvent *event) } +static void +handle_unlock_button (saver_info *si, XEvent *event) +{ + Bool mouse_in_box = False; + passwd_dialog_data *pw = si->pw_data; + + mouse_in_box = + (event->xbutton.x >= pw->unlock_button_x && + event->xbutton.x <= pw->unlock_button_x + pw->unlock_button_width && + event->xbutton.y >= pw->unlock_button_y && + event->xbutton.y <= pw->unlock_button_y + pw->unlock_button_height); + + if (ButtonRelease == event->xany.type && + pw->unlock_button_down_p && + mouse_in_box) + finished_typing_passwd (si, pw); + + pw->unlock_button_down_p = (mouse_in_box && + ButtonRelease != event->xany.type); +} + + +static void +finished_typing_passwd (saver_info *si, passwd_dialog_data *pw) +{ + if (si->unlock_state == ul_read) + { + update_passwd_window (si, "Checking...", pw->ratio); + XSync (si->dpy, False); + + si->unlock_state = ul_finished; + update_passwd_window (si, "", pw->ratio); + } +} + static void handle_passwd_key (saver_info *si, XKeyEvent *event) { - saver_preferences *p = &si->prefs; passwd_dialog_data *pw = si->pw_data; int pw_size = sizeof (pw->typed_passwd) - 1; char *typed_passwd = pw->typed_passwd; @@ -1389,6 +1678,8 @@ handle_passwd_key (saver_info *si, XKeyEvent *event) s[1] = 0; + pw->passwd_changed_p = True; + /* Add 10% to the time remaining every time a key is pressed. */ pw->ratio += 0.1; if (pw->ratio > 1) pw->ratio = 1; @@ -1407,35 +1698,49 @@ handle_passwd_key (saver_info *si, XKeyEvent *event) break; case '\012': case '\015': /* Enter */ - if (pw->state != pw_read) - ; /* already done? */ - else if (typed_passwd[0] == 0) - pw->state = pw_null; - else - { - update_passwd_window (si, "Checking...", pw->ratio); - XSync (si->dpy, False); - if (passwd_valid_p (typed_passwd, p->verbose_p)) - pw->state = pw_ok; - else - pw->state = pw_fail; - update_passwd_window (si, "", pw->ratio); - } + finished_typing_passwd(si, pw); + break; + + case '\033': /* Escape */ + si->unlock_state = ul_cancel; break; default: - i = strlen (typed_passwd); - if (i >= pw_size-1) - XBell (si->dpy, 0); + /* Though technically the only illegal characters in Unix passwords + are LF and NUL, most GUI programs (e.g., GDM) use regular text-entry + fields that only let you type printable characters. So, people + who use funky characters in their passwords are already broken. + We follow that precedent. + */ + if (isprint ((unsigned char) *s)) + { + i = strlen (typed_passwd); + if (i >= pw_size-1) + XBell (si->dpy, 0); + else + { + typed_passwd [i] = *s; + typed_passwd [i+1] = 0; + } + } else - { - typed_passwd [i] = *s; - typed_passwd [i+1] = 0; - } + XBell (si->dpy, 0); break; } - if (pw->show_stars_p) + if (pw->echo_input) + { + /* If the input is wider than the text box, only show the last portion. + * This simulates a horizontally scrolling text field. */ + int chars_in_pwfield = (pw->passwd_field_width / + pw->passwd_font->max_bounds.width); + + if (strlen(typed_passwd) > chars_in_pwfield) + typed_passwd += (strlen(typed_passwd) - chars_in_pwfield); + + update_passwd_window(si, typed_passwd, pw->ratio); + } + else if (pw->show_stars_p) { i = strlen(typed_passwd); stars = (char *) malloc(i+1); @@ -1457,11 +1762,10 @@ passwd_event_loop (saver_info *si) saver_preferences *p = &si->prefs; char *msg = 0; XEvent event; - unsigned int caps_p = 0; passwd_animate_timer ((XtPointer) si, 0); - while (si->pw_data && si->pw_data->state == pw_read) + while (si->unlock_state == ul_read) { XtAppNextEvent (si->app, &event); if (event.xany.window == si->passwd_dialog && event.xany.type == Expose) @@ -1469,92 +1773,37 @@ passwd_event_loop (saver_info *si) else if (event.xany.type == KeyPress) { handle_passwd_key (si, &event.xkey); - caps_p = (event.xkey.state & LockMask); + si->pw_data->caps_p = (event.xkey.state & LockMask); } - else if ((event.xany.type == ButtonPress || - event.xany.type == ButtonRelease) && - si->pw_data->login_button_p) - handle_passwd_button (si, &event); + else if (event.xany.type == ButtonPress || + event.xany.type == ButtonRelease) + { + si->pw_data->button_state_changed_p = True; + handle_unlock_button (si, &event); + if (si->pw_data->login_button_p) + handle_login_button (si, &event); + } else XtDispatchEvent (&event); } - switch (si->pw_data->state) + switch (si->unlock_state) { - case pw_ok: msg = 0; break; - case pw_null: msg = ""; break; - case pw_time: msg = "Timed out!"; break; - default: msg = (caps_p ? "CapsLock?" : "Sorry!"); break; + case ul_cancel: msg = ""; break; + case ul_time: msg = "Timed out!"; break; + case ul_finished: msg = "Checking..."; break; + default: msg = 0; break; } - if (si->pw_data->state == pw_fail) - si->unlock_failures++; - if (p->verbose_p) - switch (si->pw_data->state) - { - case pw_ok: - fprintf (stderr, "%s: password correct.\n", blurb()); break; - case pw_fail: - fprintf (stderr, "%s: password incorrect!%s\n", blurb(), - (caps_p ? " (CapsLock)" : "")); - break; - case pw_null: - case pw_cancel: - fprintf (stderr, "%s: password entry cancelled.\n", blurb()); break; - case pw_time: - fprintf (stderr, "%s: password entry timed out.\n", blurb()); break; - default: break; - } - -#ifdef HAVE_SYSLOG - if (si->pw_data->state == pw_fail) - { - /* If they typed a password (as opposed to just hitting return) and - the password was invalid, log it. - */ - struct passwd *pw = getpwuid (getuid ()); - char *d = DisplayString (si->dpy); - char *u = (pw->pw_name ? pw->pw_name : "???"); - int opt = 0; - int fac = 0; - -# ifdef LOG_PID - opt = LOG_PID; -# endif - -# if defined(LOG_AUTHPRIV) - fac = LOG_AUTHPRIV; -# elif defined(LOG_AUTH) - fac = LOG_AUTH; -# else - fac = LOG_DAEMON; -# endif - - if (!d) d = ""; - openlog (progname, opt, fac); - syslog (LOG_NOTICE, "FAILED LOGIN %d ON DISPLAY \"%s\", FOR \"%s\"", - si->unlock_failures, d, u); - closelog (); - } -#endif /* HAVE_SYSLOG */ - - if (si->pw_data->state == pw_fail) - XBell (si->dpy, False); - - if (si->pw_data->state == pw_ok && si->unlock_failures != 0) - { - if (si->unlock_failures == 1) - fprintf (real_stderr, - "%s: WARNING: 1 failed attempt to unlock the screen.\n", - blurb()); - else - fprintf (real_stderr, - "%s: WARNING: %d failed attempts to unlock the screen.\n", - blurb(), si->unlock_failures); - fflush (real_stderr); - - si->unlock_failures = 0; + switch (si->unlock_state) { + case ul_cancel: + fprintf (stderr, "%s: input cancelled.\n", blurb()); break; + case ul_time: + fprintf (stderr, "%s: input timed out.\n", blurb()); break; + case ul_finished: + fprintf (stderr, "%s: input finished.\n", blurb()); break; + default: break; } if (msg) @@ -1562,7 +1811,6 @@ passwd_event_loop (saver_info *si) si->pw_data->i_beam = 0; update_passwd_window (si, msg, 0.0); XSync (si->dpy, False); - sleep (1); /* Swallow all pending KeyPress/KeyRelease events. */ { @@ -1582,6 +1830,8 @@ handle_typeahead (saver_info *si) if (!si->unlock_typeahead) return; + pw->passwd_changed_p = True; + i = strlen (si->unlock_typeahead); if (i >= sizeof(pw->typed_passwd) - 1) i = sizeof(pw->typed_passwd) - 1; @@ -1598,34 +1848,221 @@ handle_typeahead (saver_info *si) } -Bool -unlock_p (saver_info *si) +/** + * Returns a copy of the input string with trailing whitespace removed. + * Whitespace is anything considered so by isspace(). + * It is safe to call this with NULL, in which case NULL will be returned. + * The returned string (if not NULL) should be freed by the caller with free(). + */ +static char * +remove_trailing_whitespace(const char *str) { - saver_preferences *p = &si->prefs; - Bool status; + size_t len; + char *newstr, *chr; - raise_window (si, True, True, True); + if (!str) + return NULL; - if (p->verbose_p) - fprintf (stderr, "%s: prompting for password.\n", blurb()); + len = strlen(str); + + newstr = malloc(len + 1); + (void) strcpy(newstr, str); + + if (!newstr) + return NULL; - if (si->pw_data || si->passwd_dialog) + chr = newstr + len; + while (isspace(*--chr) && chr >= newstr) + *chr = '\0'; + + return newstr; +} + + +/* + * The authentication conversation function. + * Like a PAM conversation function, this accepts multiple messages in a single + * round. It then splits them into individual messages for display on the + * passwd dialog. A message sequence of info or error followed by a prompt will + * be reduced into a single dialog window. + * + * Returns 0 on success or -1 if some problem occurred (cancelled auth, OOM, ...) + */ +int +gui_auth_conv(int num_msg, + const struct auth_message auth_msgs[], + struct auth_response **resp, + saver_info *si) +{ + int i; + const char *info_msg, *prompt; + struct auth_response *responses; + + if (!(responses = calloc(num_msg, sizeof(struct auth_response)))) + goto fail; + + for (i = 0; i < num_msg; ++i) + { + info_msg = prompt = NULL; + + /* See if there is a following message that can be shown at the same + * time */ + if (auth_msgs[i].type == AUTH_MSGTYPE_INFO + && i+1 < num_msg + && ( auth_msgs[i+1].type == AUTH_MSGTYPE_PROMPT_NOECHO + || auth_msgs[i+1].type == AUTH_MSGTYPE_PROMPT_ECHO) + ) + { + info_msg = auth_msgs[i].msg; + prompt = auth_msgs[++i].msg; + } + else + { + if ( auth_msgs[i].type == AUTH_MSGTYPE_INFO + || auth_msgs[i].type == AUTH_MSGTYPE_ERROR) + info_msg = auth_msgs[i].msg; + else + prompt = auth_msgs[i].msg; + } + + { + char *info_msg_trimmed, *prompt_trimmed; + + /* Trailing whitespace looks bad in a GUI */ + info_msg_trimmed = remove_trailing_whitespace(info_msg); + prompt_trimmed = remove_trailing_whitespace(prompt); + + make_passwd_window(si, info_msg_trimmed, prompt_trimmed, + auth_msgs[i].type == AUTH_MSGTYPE_PROMPT_ECHO + ? True : False); + + if (info_msg_trimmed) + free(info_msg_trimmed); + + if (prompt_trimmed) + free(prompt_trimmed); + } + + compose_status = calloc (1, sizeof (*compose_status)); + if (!compose_status) + goto fail; + + si->unlock_state = ul_read; + + handle_typeahead (si); + passwd_event_loop (si); + + if (si->unlock_state == ul_cancel) + goto fail; + + responses[i].response = strdup(si->pw_data->typed_passwd); + + /* Cache the first response to a PROMPT_NOECHO to save prompting for + * each auth mechanism. */ + if (si->cached_passwd == NULL && + auth_msgs[i].type == AUTH_MSGTYPE_PROMPT_NOECHO) + si->cached_passwd = strdup(responses[i].response); + + free (compose_status); + compose_status = 0; + } + + *resp = responses; + + return (si->unlock_state == ul_finished) ? 0 : -1; + +fail: + if (compose_status) + free (compose_status); + + if (responses) + { + for (i = 0; i < num_msg; ++i) + if (responses[i].response) + free (responses[i].response); + free (responses); + } + + return -1; +} + + +void +auth_finished_cb (saver_info *si) +{ + char buf[1024]; + const char *s; + + /* If we have something to say, put the dialog back up for a few seconds + to display it. Otherwise, don't bother. + */ + + if (si->unlock_state == ul_fail && /* failed with caps lock on */ + si->pw_data && si->pw_data->caps_p) + s = "Authentication failed (Caps Lock?)"; + else if (si->unlock_state == ul_fail) /* failed without caps lock */ + s = "Authentication failed!"; + else if (si->unlock_state == ul_success && /* good, but report failures */ + si->unlock_failures > 0) + { + if (si->unlock_failures == 1) + s = "There has been\n1 failed login attempt."; + else + { + sprintf (buf, "There have been\n%d failed login attempts.", + si->unlock_failures); + s = buf; + } + si->unlock_failures = 0; + } + else /* good, with no failures, */ + goto END; /* or timeout, or cancel. */ + + make_passwd_window (si, s, NULL, True); + XSync (si->dpy, False); + + { + int secs = 4; + time_t start = time ((time_t *) 0); + XEvent event; + while (time ((time_t *) 0) < start + secs) + if (XPending (si->dpy)) + { + XNextEvent (si->dpy, &event); + if (event.xany.window == si->passwd_dialog && + event.xany.type == Expose) + draw_passwd_window (si); + else if (event.xany.type == ButtonPress || + event.xany.type == ButtonRelease) + break; + XSync (si->dpy, False); + } + else + usleep (250000); /* 1/4 second */ + } + + END: + if (si->pw_data) destroy_passwd_window (si); +} - make_passwd_window (si); - compose_status = calloc (1, sizeof (*compose_status)); +Bool +unlock_p (saver_info *si) +{ + saver_preferences *p = &si->prefs; - handle_typeahead (si); - passwd_event_loop (si); + if (!si->unlock_cb) + { + fprintf(stderr, "%s: Error: no unlock function specified!\n", blurb()); + return False; + } - status = (si->pw_data->state == pw_ok); - destroy_passwd_window (si); + raise_window (si, True, True, True); - free (compose_status); - compose_status = 0; + xss_authenticate(si, p->verbose_p); - return status; + return (si->unlock_state == ul_success); } @@ -1637,9 +2074,6 @@ set_locked_p (saver_info *si, Bool locked_p) #ifdef HAVE_XHPDISABLERESET hp_lock_reset (si, locked_p); /* turn off/on C-Sh-Reset */ #endif -#ifdef HAVE_VT_LOCKSWITCH - linux_lock_vt_switch (si, locked_p); /* turn off/on C-Alt-F1 */ -#endif #ifdef HAVE_XF86VMODE xfree_lock_mode_switch (si, locked_p); /* turn off/on C-Alt-Plus */ #endif