X-Git-Url: http://git.hungrycats.org/cgi-bin/gitweb.cgi?p=xscreensaver;a=blobdiff_plain;f=driver%2Flock.c;h=7a3ed6a259ffbf3ca1a6a5a6da6fdae625f8c64f;hp=f388c0d61deeceffdc560032f7ed85a88e854280;hb=2a991a811de4c7b22f812682b267b616a809fd9a;hpb=ce3185de9d9705e259f2b60dd4b5509007fa17d4 diff --git a/driver/lock.c b/driver/lock.c index f388c0d6..7a3ed6a2 100644 --- a/driver/lock.c +++ b/driver/lock.c @@ -19,657 +19,774 @@ #ifndef NO_LOCKING /* whole file */ -#include #include -#include /* for XtResizeWidget */ #include "xscreensaver.h" #include "resources.h" +#ifdef _VROOT_H_ +ERROR! You must not include vroot.h in this file. +#endif + #ifndef VMS # include #else /* VMS */ + extern char *getenv(const char *name); extern int validate_user(char *name, char *password); + +static Bool +vms_passwd_valid_p(char *pw) +{ + return (validate_user (getenv("USER"), typed_passwd) == 1); +} +# undef passwd_valid_p +# define passwd_valid_p vms_passwd_valid_p + #endif /* VMS */ -#ifdef HAVE_ATHENA +#undef MAX +#define MAX(a,b) ((a)>(b)?(a):(b)) -# include -# include -# include -# include -# include +enum passwd_state { pw_read, pw_ok, pw_fail, pw_cancel, pw_time }; -#else /* HAVE_MOTIF */ +struct passwd_dialog_data { -# include -# include -# include + enum passwd_state state; + char typed_passwd [80]; + XtIntervalId timer; + int i_beam; -#endif /* HAVE_MOTIF */ + float ratio; + Dimension width; + Dimension height; -#ifdef _VROOT_H_ -ERROR! You must not include vroot.h in this file. -#endif + char *heading_label; + char *body_label; + char *user_label; + char *passwd_label; + char *user_string; + char *passwd_string; -extern Widget passwd_dialog; -extern Widget passwd_form; -extern Widget roger_label; -extern Widget passwd_label1; -extern Widget passwd_label3; -extern Widget passwd_cancel; + XFontStruct *heading_font; + XFontStruct *body_font; + XFontStruct *label_font; + XFontStruct *passwd_font; -#ifdef HAVE_MOTIF -extern Widget passwd_text; -extern Widget passwd_done; -#else /* HAVE_ATHENA */ -static Widget passwd_text = 0; /* gag... */ -static Widget passwd_done = 0; -#endif /* HAVE_ATHENA */ + Pixel foreground; + Pixel background; + Pixel passwd_foreground; + Pixel passwd_background; + Pixel logo_foreground; + Pixel logo_background; + Pixel shadow_top; + Pixel shadow_bottom; + Dimension logo_width; + Dimension logo_height; + Dimension thermo_width; + Dimension internal_border; + Dimension shadow_width; + Dimension passwd_field_x, passwd_field_y; + Dimension passwd_field_width, passwd_field_height; -static enum { pw_read, pw_ok, pw_fail, pw_cancel, pw_time } passwd_state; -static char typed_passwd [80]; + Dimension thermo_field_x, thermo_field_y; + Dimension thermo_field_height; +}; - -#if defined(HAVE_ATHENA) || (XmVersion >= 1002) - /* The `destroy' bug apears to be fixed as of Motif 1.2.1, but - the `verify-callback' bug is still present. */ -# define DESTROY_WORKS -#endif -static void -passwd_cancel_cb (Widget button, XtPointer client_data, XtPointer call_data) +void +make_passwd_window (saver_info *si) { - passwd_state = pw_cancel; -} + struct passwd *p = getpwuid (getuid ()); + int x, y, bw; + XSetWindowAttributes attrs; + unsigned long attrmask = 0; + Screen *screen = si->default_screen->screen; + passwd_dialog_data *pw = (passwd_dialog_data *) calloc (1, sizeof(*pw)); + Colormap cmap = DefaultColormapOfScreen (screen); + char *f; + + pw->ratio = 1.0; + + pw->heading_label = get_string_resource ("passwd.heading.label", + "Dialog.Label.Label"); + pw->body_label = get_string_resource ("passwd.body.label", + "Dialog.Label.Label"); + pw->user_label = get_string_resource ("passwd.user.label", + "Dialog.Label.Label"); + pw->passwd_label = get_string_resource ("passwd.passwd.label", + "Dialog.Label.Label"); + + if (!pw->heading_label) + pw->heading_label = strdup("ERROR: REESOURCES NOT INSTALLED CORRECTLY"); + if (!pw->body_label) + pw->body_label = strdup("ERROR: REESOURCES NOT INSTALLED CORRECTLY"); + if (!pw->user_label) pw->user_label = strdup("ERROR"); + if (!pw->passwd_label) pw->passwd_label = strdup("ERROR"); + + /* Put the version number in the label. */ + { + char *s = (char *) malloc (strlen(pw->heading_label) + 20); + sprintf(s, pw->heading_label, si->version); + free (pw->heading_label); + pw->heading_label = s; + } + pw->user_string = (p->pw_name ? p->pw_name : "???"); + pw->passwd_string = strdup(""); + + f = get_string_resource ("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.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"); + 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"); + pw->passwd_font = XLoadQueryFont (si->dpy, (f ? f : "fixed")); + if (!pw->passwd_font) pw->passwd_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); + + if (pw->foreground == pw->background) + { + /* Make sure the error messages show up. */ + pw->foreground = BlackPixelOfScreen (screen); + pw->background = WhitePixelOfScreen (screen); + } -#ifdef VMS -static Bool -vms_passwd_valid_p(char *pw) -{ - char *u = getenv("USER"); - return (validate_user (i, typed_passwd) == 1); -} -# undef passwd_valid_p -# define passwd_valid_p vms_passwd_valid_p + 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->logo_foreground = get_pixel_resource ("passwd.logo.foreground", + "Dialog.Logo.Foreground", + si->dpy, cmap); + pw->logo_background = get_pixel_resource ("passwd.logo.background", + "Dialog.Logo.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", + "Dialog.Thermometer.Width"); + pw->internal_border = get_integer_resource ("passwd.internalBorderWidth", + "Dialog.InternalBorderWidth"); + pw->shadow_width = get_integer_resource ("passwd.shadowThickness", + "Dialog.ShadowThickness"); + + if (pw->logo_width == 0) pw->logo_width = 150; + if (pw->logo_height == 0) pw->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; -#endif /* VMS */ + { + int direction, ascent, descent; + XCharStruct overall; + + pw->width = 0; + pw->height = 0; + + /* Measure the heading_label. */ + XTextExtents (pw->heading_font, + pw->heading_label, strlen(pw->heading_label), + &direction, &ascent, &descent, &overall); + 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; + { + Dimension w2 = 0, w3 = 0; + Dimension h2 = 0, h3 = 0; + const char *passwd_string = "MMMMMMMMMMMM"; + + /* Measure the user_label. */ + XTextExtents (pw->label_font, + pw->user_label, strlen(pw->user_label), + &direction, &ascent, &descent, &overall); + 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 user_string. */ + XTextExtents (pw->passwd_font, + pw->user_string, strlen(pw->user_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 (maximally-sized, dummy) passwd_string. */ + 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; + + w2 = w2 + w3 + (pw->shadow_width * 2); + h2 = MAX (h2, h3); + + if (w2 > pw->width) pw->width = w2; + pw->height += h2; + } -static void -passwd_done_cb (Widget button, XtPointer client_data, XtPointer call_data) -{ - if (passwd_state != pw_read) return; /* already done */ + pw->width += (pw->internal_border * 2); + pw->height += (pw->internal_border * 4); - if (passwd_valid_p (typed_passwd)) - passwd_state = pw_ok; - else - passwd_state = pw_fail; -} + 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) + pw->logo_height = pw->height; -#if defined(HAVE_MOTIF) && defined(VERIFY_CALLBACK_WORKS) + pw->logo_width = pw->logo_height; - /* It looks to me like adding any modifyVerify callback causes - Motif 1.1.4 to free the the TextF_Value() twice. I can't see - the bug in the Motif source, but Purify complains, even if - check_passwd_cb() is a no-op. + pw->width += pw->logo_width; + } - Update: Motif 1.2.1 also loses, but in a different way: it - writes beyond the end of a malloc'ed block in ModifyVerify(). - Probably this block is the text field's text. - */ + attrmask |= CWOverrideRedirect; attrs.override_redirect = True; + attrmask |= CWEventMask; attrs.event_mask = ExposureMask|KeyPressMask; -static void -check_passwd_cb (Widget button, XtPointer client_data, XtPointer call_data) -{ - XmTextVerifyCallbackStruct *vcb = (XmTextVerifyCallbackStruct *) call_data; + { + Dimension w = WidthOfScreen(screen); + Dimension h = HeightOfScreen(screen); + if (si->prefs.debug_p) w /= 2; + x = ((w + pw->width) / 2) - pw->width; + y = ((h + pw->height) / 2) - pw->height; + if (x < 0) x = 0; + if (y < 0) y = 0; + } - if (passwd_state != pw_read) - return; - else if (vcb->reason == XmCR_ACTIVATE) - { - passwd_done_cb (0, 0, 0); - } - else if (vcb->text->length > 1) /* don't allow "paste" operations */ - { - vcb->doit = False; - } - else if (vcb->text->ptr != 0) - { - int i; - int L = vcb->text->length; - if (L >= sizeof(typed_passwd)) - L = sizeof(typed_passwd)-1; - strncat (typed_passwd, vcb->text->ptr, L); - typed_passwd [vcb->endPos + L] = 0; - for (i = 0; i < vcb->text->length; i++) - vcb->text->ptr [i] = '*'; - } -} + bw = get_integer_resource ("passwd.borderWidth", "Dialog.BorderWidth"); -# else /* HAVE_ATHENA || !VERIFY_CALLBACK_WORKS */ - -static void keypress (Widget w, XEvent *event, String *av, Cardinal *ac); -static void backspace (Widget w, XEvent *event, String *av, Cardinal *ac); -static void kill_line (Widget w, XEvent *event, String *av, Cardinal *ac); -static void done (Widget w, XEvent *event, String *av, Cardinal *ac); - -static XtActionsRec actions[] = {{"keypress", keypress}, - {"backspace", backspace}, - {"kill_line", kill_line}, - {"done", done} - }; - -# if 0 /* This works for Athena, but not Motif: keypress() gets called - for all keys anyway. So, the implementation of keypress() - has BackSpace, etc, hardcoded into it instead. FMH! - */ -static char translations[] = "BackSpace: backspace()\n" - "Delete: backspace()\n" - "CtrlH: backspace()\n" - "CtrlU: kill_line()\n" - "CtrlX: kill_line()\n" - "CtrlJ: done()\n" - "CtrlM: done()\n" - ": keypress()\n"; -# else /* !0 */ -static char translations[] = ": keypress()\n"; -# endif /* !0 */ + si->passwd_dialog = + XCreateWindow (si->dpy, + RootWindowOfScreen(screen), + x, y, pw->width, pw->height, bw, + DefaultDepthOfScreen (screen), InputOutput, + DefaultVisualOfScreen(screen), + attrmask, &attrs); + XSetWindowBackground (si->dpy, si->passwd_dialog, pw->background); + XMapRaised (si->dpy, si->passwd_dialog); + XSync (si->dpy, False); -static void -text_field_set_string (Widget widget, char *text, int position) -{ -#ifdef HAVE_MOTIF - XmTextFieldSetString (widget, text); - XmTextFieldSetInsertionPosition (widget, position); - -#else /* HAVE_ATHENA */ - char *buf; - int end_pos; - - XawTextBlock block; - block.firstPos = 0; - block.length = strlen (text); - block.ptr = text; - block.format = 0; - if (block.length == 0) - { - buf = XawDialogGetValueString(passwd_form); - if (buf) - end_pos = strlen(buf); - else - end_pos = -1; - } - XawTextReplace (widget, 0, end_pos, &block); - XawTextSetInsertionPoint (widget, position); -#endif /* HAVE_ATHENA */ + si->pw_data = pw; + + draw_passwd_window (si); + XSync (si->dpy, False); } -static void -keypress (Widget w, XEvent *event, String *argv, Cardinal *argc) +void +draw_passwd_window (saver_info *si) { - int i, j; - char s [sizeof(typed_passwd)]; - int size = XLookupString ((XKeyEvent *) event, s, sizeof(s)-1, 0, 0); - if (size != 1) return; + passwd_dialog_data *pw = si->pw_data; + XGCValues gcv; + GC gc1, gc2; + int spacing, height; + int x1, x2, x3, y1, y2; + int sw; + int tb_height; + + 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))))); + spacing = ((pw->height - (2 * pw->shadow_width) - + pw->internal_border - height)) / 8; + if (spacing < 0) spacing = 0; + + gcv.foreground = pw->foreground; + gc1 = XCreateGC (si->dpy, si->passwd_dialog, GCForeground, &gcv); + gc2 = XCreateGC (si->dpy, si->passwd_dialog, GCForeground, &gcv); + x1 = pw->logo_width + pw->thermo_width + (pw->shadow_width * 3); + x3 = pw->width - (pw->shadow_width * 2); + y1 = (pw->shadow_width * 2) + spacing + spacing; + + /* top heading + */ + XSetFont (si->dpy, gc1, pw->heading_font->fid); + sw = string_width (pw->heading_font, pw->heading_label); + x2 = (x1 + ((x3 - x1 - sw) / 2)); + y1 += spacing + pw->heading_font->ascent + pw->heading_font->descent; + XDrawString (si->dpy, si->passwd_dialog, gc1, x2, y1, + pw->heading_label, strlen(pw->heading_label)); + + /* text 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)); - /* hack because I can't get translations to dance to my tune... */ - if (*s == '\010') { backspace (w, event, argv, argc); return; } - if (*s == '\177') { backspace (w, event, argv, argc); return; } - if (*s == '\025') { kill_line (w, event, argv, argc); return; } - if (*s == '\030') { kill_line (w, event, argv, argc); return; } - if (*s == '\012') { done (w, event, argv, argc); return; } - if (*s == '\015') { done (w, event, argv, argc); return; } - i = j = strlen (typed_passwd); + tb_height = (pw->passwd_font->ascent + pw->passwd_font->descent + + (pw->shadow_width * 4)); - if (i >= (sizeof(typed_passwd)-1)) - { - XBell(XtDisplay(w), 0); - return; - } + /* 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); + x2 = (x1 + pw->internal_border + + MAX(string_width (pw->label_font, pw->user_label), + string_width (pw->label_font, pw->passwd_label))); + XDrawString (si->dpy, si->passwd_dialog, gc1, + x2 - string_width (pw->label_font, pw->user_label), + y1, + pw->user_label, strlen(pw->user_label)); + + /* the "Password:" prompt + */ + y1 += (spacing + tb_height); + XDrawString (si->dpy, si->passwd_dialog, gc1, + x2 - string_width (pw->label_font, pw->passwd_label), + y1, + pw->passwd_label, strlen(pw->passwd_label)); - typed_passwd [i] = *s; - s [++i] = 0; - while (i--) - s [i] = '*'; - text_field_set_string (passwd_text, s, j + 1); -} + XSetForeground (si->dpy, gc2, pw->passwd_background); -static void -backspace (Widget w, XEvent *event, String *argv, Cardinal *argc) -{ - char s [sizeof(typed_passwd)]; - int i = strlen (typed_passwd); - int j = i; - if (i == 0) - return; - typed_passwd [--i] = 0; - s [i] = 0; - while (i--) - s [i] = '*'; - - text_field_set_string (passwd_text, s, j + 1); -} + /* the "user name" text field + */ + y1 = y2; + XSetForeground (si->dpy, gc1, pw->passwd_foreground); + XSetFont (si->dpy, gc1, pw->passwd_font->fid); + y1 += (spacing + tb_height); + x2 += (pw->shadow_width * 4); + + pw->passwd_field_width = x3 - x2 - pw->internal_border; + pw->passwd_field_height = (pw->passwd_font->ascent + + pw->passwd_font->descent + + pw->shadow_width); + + XFillRectangle (si->dpy, si->passwd_dialog, gc2, + x2 - pw->shadow_width, + y1 - (pw->passwd_font->ascent + pw->passwd_font->descent), + pw->passwd_field_width, pw->passwd_field_height); + XDrawString (si->dpy, si->passwd_dialog, gc1, x2, y1, + pw->user_string, strlen(pw->user_string)); + + /* the "password" text field + */ + y1 += (spacing + tb_height); -static void -kill_line (Widget w, XEvent *event, String *argv, Cardinal *argc) -{ - memset (typed_passwd, 0, sizeof(typed_passwd)); - text_field_set_string (passwd_text, "", 0); -} + pw->passwd_field_x = x2 - pw->shadow_width; + pw->passwd_field_y = y1 - (pw->passwd_font->ascent + + pw->passwd_font->descent); -static void -done (Widget w, XEvent *event, String *argv, Cardinal *argc) -{ - passwd_done_cb (w, 0, 0); -} + /* The shadow around the text fields + */ + y1 = y2; + y1 += (spacing + (pw->shadow_width * 3)); + x1 = x2 - (pw->shadow_width * 2); + x2 = pw->passwd_field_width + (pw->shadow_width * 2); + y2 = pw->passwd_field_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); + + 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); + + /* the logo + */ + XSetForeground (si->dpy, gc1, pw->logo_foreground); + XSetForeground (si->dpy, gc2, pw->logo_background); -#endif /* HAVE_ATHENA || !VERIFY_CALLBACK_WORKS */ + x1 = pw->shadow_width * 3; + y1 = pw->shadow_width * 3; + x2 = pw->logo_width - (pw->shadow_width * 6); + y2 = pw->logo_height - (pw->shadow_width * 6); + XFillRectangle (si->dpy, si->passwd_dialog, gc2, x1, y1, x2, y2); + skull (si->dpy, si->passwd_dialog, gc1, gc2, + x1 + pw->shadow_width, y1 + pw->shadow_width, + x2 - (pw->shadow_width * 2), y2 - (pw->shadow_width * 2)); -static void -make_passwd_dialog (saver_info *si) -{ - char *username = 0; - saver_screen_info *ssi = si->default_screen; - Widget parent = ssi->toplevel_shell; + /* The thermometer + */ + pw->thermo_field_x = pw->logo_width + pw->shadow_width; + pw->thermo_field_y = pw->shadow_width * 3; + pw->thermo_field_height = pw->height - (pw->shadow_width * 6); - if (ssi->demo_cmap && - ssi->demo_cmap != ssi->cmap && - ssi->demo_cmap != DefaultColormapOfScreen (ssi->screen)) - { - XFreeColormap (si->dpy, ssi->demo_cmap); - ssi->demo_cmap = 0; - } + /* Solid border inside the logo box. */ + XSetForeground (si->dpy, gc1, pw->foreground); + XDrawRectangle (si->dpy, si->passwd_dialog, gc1, x1, y1, x2-1, y2-1); - if (ssi->default_visual == DefaultVisualOfScreen (ssi->screen)) - ssi->demo_cmap = DefaultColormapOfScreen (ssi->screen); - else - ssi->demo_cmap = XCreateColormap (si->dpy, - RootWindowOfScreen (ssi->screen), - ssi->default_visual, AllocNone); + /* The shadow around the logo + */ + draw_shaded_rectangle (si->dpy, si->passwd_dialog, + pw->shadow_width * 2, + pw->shadow_width * 2, + pw->logo_width - (pw->shadow_width * 4), + pw->logo_height - (pw->shadow_width * 4), + pw->shadow_width, + pw->shadow_bottom, pw->shadow_top); + + /* The shadow around the thermometer + */ + draw_shaded_rectangle (si->dpy, si->passwd_dialog, + pw->logo_width, + pw->shadow_width * 2, + pw->thermo_width + (pw->shadow_width * 2), + pw->height - (pw->shadow_width * 4), + pw->shadow_width, + pw->shadow_bottom, pw->shadow_top); + + /* Solid border inside the thermometer. */ + XSetForeground (si->dpy, gc1, pw->foreground); + XDrawRectangle (si->dpy, si->passwd_dialog, gc1, + pw->logo_width + pw->shadow_width, + pw->shadow_width * 3, + pw->thermo_width - 1, + pw->height - (pw->shadow_width * 6) - 1); + + /* The shadow around the whole window + */ + draw_shaded_rectangle (si->dpy, si->passwd_dialog, + 0, 0, pw->width, pw->height, pw->shadow_width, + pw->shadow_top, pw->shadow_bottom); - create_passwd_dialog (parent, ssi->default_visual, ssi->demo_cmap); + XFreeGC (si->dpy, gc1); + XFreeGC (si->dpy, gc2); -#ifdef HAVE_ATHENA - XtVaSetValues(passwd_form, XtNvalue, typed_passwd, 0); + update_passwd_window (si, pw->passwd_string, pw->ratio); +} - XawDialogAddButton(passwd_form,"ok", passwd_done_cb, 0); - XawDialogAddButton(passwd_form,"cancel", passwd_cancel_cb, 0); - passwd_done = XtNameToWidget(passwd_form,"ok"); - passwd_text = XtNameToWidget(passwd_form,"value"); - XtAppAddActions(XtWidgetToApplicationContext(passwd_text), - actions, XtNumber(actions)); - XtOverrideTranslations(passwd_text, XtParseTranslationTable(translations)); +void +update_passwd_window (saver_info *si, const char *printed_passwd, float ratio) +{ + passwd_dialog_data *pw = si->pw_data; + XGCValues gcv; + GC gc1, gc2; + int x, y; + + pw->ratio = ratio; + gcv.foreground = pw->passwd_foreground; + gcv.font = pw->passwd_font->fid; + gc1 = XCreateGC (si->dpy, si->passwd_dialog, GCForeground|GCFont, &gcv); + gcv.foreground = pw->passwd_background; + gc2 = XCreateGC (si->dpy, si->passwd_dialog, GCForeground, &gcv); + + if (printed_passwd) + { + char *s = strdup (printed_passwd); + if (pw->passwd_string) free (pw->passwd_string); + pw->passwd_string = s; + } - /* Lose the label on the inner dialog. */ - { - Widget w = XtNameToWidget(passwd_form, "label"); - if (w) XtUnmanageChild(w); - } + /* the "password" text field + */ + XFillRectangle (si->dpy, si->passwd_dialog, gc2, + pw->passwd_field_x, pw->passwd_field_y, + pw->passwd_field_width, pw->passwd_field_height); + XDrawString (si->dpy, si->passwd_dialog, gc1, + pw->passwd_field_x + pw->shadow_width, + pw->passwd_field_y + (pw->passwd_font->ascent + + pw->passwd_font->descent), + pw->passwd_string, strlen(pw->passwd_string)); + + /* The I-beam + */ + if (pw->i_beam != 0) + { + x = (pw->passwd_field_x + pw->shadow_width + + string_width (pw->passwd_font, pw->passwd_string)); + y = pw->passwd_field_y + pw->shadow_width; + XDrawLine (si->dpy, si->passwd_dialog, gc1, + x, y, x, y + pw->passwd_font->ascent); + } -#else /* HAVE_MOTIF */ + pw->i_beam = (pw->i_beam + 1) % 4; - XtAddCallback (passwd_done, XmNactivateCallback, passwd_done_cb, 0); - XtAddCallback (passwd_cancel, XmNactivateCallback, passwd_cancel_cb, 0); - XtAddCallback (roger_label, XmNexposeCallback, roger, 0); -# ifdef VERIFY_CALLBACK_WORKS - XtAddCallback (passwd_text, XmNmodifyVerifyCallback, check_passwd_cb, 0); - XtAddCallback (passwd_text, XmNactivateCallback, check_passwd_cb, 0); -# else /* !VERIFY_CALLBACK_WORKS */ - XtAddCallback (passwd_text, XmNactivateCallback, passwd_done_cb, 0); - XtOverrideTranslations (passwd_text, XtParseTranslationTable (translations)); -# endif /* !VERIFY_CALLBACK_WORKS */ + /* the thermometer + */ + y = pw->thermo_field_height * (1.0 - pw->ratio); + if (y > 0) + { + XFillRectangle (si->dpy, si->passwd_dialog, gc2, + pw->thermo_field_x + 1, + pw->thermo_field_y + 1, + pw->thermo_width-2, + y); + XSetForeground (si->dpy, gc1, pw->logo_foreground); + XFillRectangle (si->dpy, si->passwd_dialog, gc1, + pw->thermo_field_x + 1, + pw->thermo_field_y + 1 + y, + pw->thermo_width-2, + MAX (0, pw->thermo_field_height - y - 2)); + } -# if defined(HAVE_MOTIF) && (XmVersion >= 1002) - /* The focus stuff changed around; this didn't exist in 1.1.5. */ - XtVaSetValues (passwd_form, XmNinitialFocus, passwd_text, 0); -# endif /* HAVE_MOTIF && XmVersion >= 1002 */ + XFreeGC (si->dpy, gc1); + XFreeGC (si->dpy, gc2); + XSync (si->dpy, False); +} - /* Another random thing necessary in 1.2.1 but not 1.1.5... */ - XtVaSetValues (roger_label, XmNborderWidth, 2, 0); -#endif /* HAVE_MOTIF */ +void +destroy_passwd_window (saver_info *si) +{ + passwd_dialog_data *pw = si->pw_data; + Screen *screen = si->default_screen->screen; + Colormap cmap = DefaultColormapOfScreen (screen); + Pixel black = BlackPixelOfScreen (screen); + Pixel white = WhitePixelOfScreen (screen); -#ifndef VMS - { - struct passwd *pw = getpwuid (getuid ()); - username = pw->pw_name; - } -#else /* VMS -- from "R.S.Niranjan" who says - that on OpenVMS 6.1, using `struct passwd' crashes... */ - username = getenv("USER"); -#endif /* VMS */ + if (pw->timer) + XtRemoveTimeOut (pw->timer); - format_into_label (passwd_label1, si->version); - format_into_label (passwd_label3, (username ? username : "???")); + if (si->passwd_dialog) + { + XDestroyWindow (si->dpy, si->passwd_dialog); + si->passwd_dialog = 0; + } + + 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->heading_font) XFreeFont (si->dpy, pw->heading_font); + if (pw->body_font) XFreeFont (si->dpy, pw->body_font); + if (pw->label_font) XFreeFont (si->dpy, pw->label_font); + if (pw->passwd_font) XFreeFont (si->dpy, pw->passwd_font); + + if (pw->foreground != black && pw->foreground != white) + XFreeColors (si->dpy, cmap, &pw->foreground, 1, 0L); + if (pw->background != black && pw->background != white) + XFreeColors (si->dpy, cmap, &pw->background, 1, 0L); + if (pw->passwd_foreground != black && pw->passwd_foreground != white) + XFreeColors (si->dpy, cmap, &pw->passwd_foreground, 1, 0L); + if (pw->passwd_background != black && pw->passwd_background != white) + XFreeColors (si->dpy, cmap, &pw->passwd_background, 1, 0L); + if (pw->logo_foreground != black && pw->logo_foreground != white) + XFreeColors (si->dpy, cmap, &pw->logo_foreground, 1, 0L); + if (pw->logo_background != black && pw->logo_background != white) + XFreeColors (si->dpy, cmap, &pw->logo_background, 1, 0L); + if (pw->shadow_top != black && pw->shadow_top != white) + XFreeColors (si->dpy, cmap, &pw->shadow_top, 1, 0L); + if (pw->shadow_bottom != black && pw->shadow_bottom != white) + XFreeColors (si->dpy, cmap, &pw->shadow_bottom, 1, 0L); + + memset (pw, 0, sizeof(*pw)); + free (pw); + + si->pw_data = 0; } -static int passwd_idle_timer_tick = -1; -static XtIntervalId passwd_idle_id; + +/* Interactions + */ static void -passwd_idle_timer (XtPointer closure, XtIntervalId *id) +passwd_animate_timer (XtPointer closure, XtIntervalId *id) { saver_info *si = (saver_info *) closure; - saver_preferences *p = &si->prefs; - - Display *dpy = XtDisplay (passwd_form); -#ifdef HAVE_ATHENA - Window window = XtWindow (passwd_form); -#else /* MOTIF */ - Window window = XtWindow (XtParent(passwd_done)); -#endif /* MOTIF */ - static Dimension x, y, d, s, ss; - static GC gc = 0; - int max = p->passwd_timeout / 1000; + int tick = 166; + passwd_dialog_data *pw = si->pw_data; - idle_timer ((XtPointer) si, id); + if (!pw) return; - if (passwd_idle_timer_tick == max) /* first time */ + pw->ratio -= (1.0 / ((double) si->prefs.passwd_timeout / (double) tick)); + if (pw->ratio < 0) { - XGCValues gcv; -#ifdef HAVE_MOTIF - unsigned long fg = 0, bg = 0, ts = 0, bs = 0; - Dimension w = 0, h = 0; - XtVaGetValues(XtParent(passwd_done), - XmNwidth, &w, - 0); - XtVaGetValues(passwd_done, - XmNheight, &h, - XmNy, &y, - 0); - XtVaGetValues(passwd_form, - XtNforeground, &fg, - XtNbackground, &bg, - XmNtopShadowColor, &ts, - XmNbottomShadowColor, &bs, - 0); - - if (ts != bg && ts != fg) - fg = ts; - if (bs != bg && bs != fg) - fg = bs; - - d = h / 2; - if (d & 1) d++; - - x = (w / 2); - -# ifdef __sgi /* Kludge -- SGI's Motif hacks place buttons differently. */ - { - static int sgi_mode = -1; - if (sgi_mode == -1) - sgi_mode = get_boolean_resource("sgiMode", "sgiMode") ? 1 : 0; - - if (sgi_mode) - x = d; - } -# endif /* __sgi */ - - x -= d/2; - y += d/2; - -#else /* HAVE_ATHENA */ - - Arg av [100]; - int ac = 0; - unsigned long fg = 0, bg = 0; - XtSetArg (av [ac], XtNheight, &d); ac++; - XtGetValues (passwd_done, av, ac); - ac = 0; - XtSetArg (av [ac], XtNwidth, &x); ac++; - XtSetArg (av [ac], XtNheight, &y); ac++; - XtSetArg (av [ac], XtNforeground, &fg); ac++; - XtSetArg (av [ac], XtNbackground, &bg); ac++; - XtGetValues (passwd_form, av, ac); - x -= d; - y -= d; - d -= 4; - -#endif /* HAVE_ATHENA */ - - gcv.foreground = fg; - if (gc) XFreeGC (dpy, gc); - gc = XCreateGC (dpy, window, GCForeground, &gcv); - s = 360*64 / (passwd_idle_timer_tick - 1); - ss = 90*64; - XFillArc (dpy, window, gc, x, y, d, d, 0, 360*64); - XSetForeground (dpy, gc, bg); - x += 1; - y += 1; - d -= 2; + pw->ratio = 0; + if (pw->state == pw_read) + pw->state = pw_time; } - if (--passwd_idle_timer_tick) - { - passwd_idle_id = XtAppAddTimeOut (si->app, 1000, passwd_idle_timer, - (XtPointer) si); - XFillArc (dpy, window, gc, x, y, d, d, ss, s); - ss += s; - } + update_passwd_window (si, 0, pw->ratio); + + if (pw->state == pw_read) + pw->timer = XtAppAddTimeOut (si->app, tick, passwd_animate_timer, + (XtPointer) si); + else + pw->timer = 0; + + idle_timer ((XtPointer) si, id); } -static Bool -pop_passwd_dialog (saver_info *si) +static void +handle_passwd_key (saver_info *si, XKeyEvent *event) { - saver_preferences *p = &si->prefs; - saver_screen_info *ssi = si->default_screen; - Widget parent = ssi->toplevel_shell; - Display *dpy = XtDisplay (passwd_dialog); - Window focus; - int revert_to; + passwd_dialog_data *pw = si->pw_data; + int pw_size = sizeof (pw->typed_passwd) - 1; + char *typed_passwd = pw->typed_passwd; + char s[2]; + char *stars = 0; int i; - Window grab_window = RootWindowOfScreen(si->screens[0].screen); - - typed_passwd [0] = 0; - passwd_state = pw_read; - text_field_set_string (passwd_text, "", 0); - - /* In case one of the hacks has unmapped it temporarily... - Get that sucker on stage now! */ - for (i = 0; i < si->nscreens; i++) - XMapRaised(si->dpy, si->screens[i].screensaver_window); - - XGetInputFocus (dpy, &focus, &revert_to); -#if defined(HAVE_MOTIF) && !defined(DESTROY_WORKS) - /* This fucker blows up if we destroy the widget. I can't figure - out why. The second destroy phase dereferences freed memory... - So we just keep it around; but unrealizing or unmanaging it - doesn't work right either, so we hack the window directly. FMH. - */ - if (XtWindow (passwd_form)) - XMapRaised (dpy, XtWindow (passwd_dialog)); -#endif /* HAVE_MOTIF && !DESTROY_WORKS */ - - monitor_power_on (si); - pop_up_dialog_box (passwd_dialog, passwd_form, - /* for debugging -- don't ask */ - (si->prefs.debug_p ? 69 : 0) + - 2); - XtManageChild (passwd_form); - -#ifdef HAVE_ATHENA - steal_focus_and_colormap (passwd_text); - - /* For some reason, the passwd_form box is not stretching all the way - to the right edge of the window, despite being XtChainRight. - So... resize it by hand. - */ - { - Dimension x=0, w=0, h=0; - XtVaGetValues(passwd_form, XtNx, &x, XtNwidth, &w, XtNheight, &h, 0); - XtVaGetValues(XtParent(passwd_form), XtNwidth, &w, 0); - w -= x; - w -= 6; - if (w > 0) XtResizeWidget(passwd_form, w, h, 0); - } + int size = XLookupString (event, s, 1, 0, 0); + + if (size != 1) return; -#endif /* HAVE_ATHENA */ + s[1] = 0; + switch (*s) + { + case '\010': case '\177': /* Backspace */ + if (!*typed_passwd) + XBell (si->dpy, 0); + else + typed_passwd [strlen(typed_passwd)-1] = 0; + break; + + case '\025': case '\030': /* Erase line */ + memset (typed_passwd, 0, pw_size); + break; + + case '\012': case '\015': /* Enter */ + if (pw->state != pw_read) + ; /* already done? */ + else if (passwd_valid_p (typed_passwd)) + pw->state = pw_ok; + else + pw->state = pw_fail; + break; -#if defined(HAVE_MOTIF) && (XmVersion < 1002) - /* The focus stuff changed around; this causes problems in 1.2.1 - but is necessary in 1.1.5. */ - XmProcessTraversal (passwd_text, XmTRAVERSE_CURRENT); -#endif /* HAVE_MOTIF && XmVersion < 1002 */ + default: + i = strlen (typed_passwd); + if (i >= pw_size-1) + XBell (si->dpy, 0); + else + { + typed_passwd [i] = *s; + typed_passwd [i+1] = 0; + } + break; + } - passwd_idle_timer_tick = p->passwd_timeout / 1000; - passwd_idle_id = XtAppAddTimeOut (si->app, 1000, passwd_idle_timer, - (XtPointer) si); + i = strlen(typed_passwd); + stars = (char *) malloc(i+1); + memset (stars, '*', i); + stars[i] = 0; + update_passwd_window (si, stars, pw->ratio); + free (stars); +} -#ifdef HAVE_ATHENA - if (roger_label) - roger(roger_label, 0, 0); -#endif /* HAVE_ATHENA */ +static void +passwd_event_loop (saver_info *si) +{ + char *msg = 0; + XEvent event; + passwd_animate_timer ((XtPointer) si, 0); - /* Make sure the mouse cursor is visible. - Since the screensaver was already active, we had already called - grab_keyboard_and_mouse() with our "invisible" Cursor object. - Now we need to change that. (cursor == 0 means "server default - cursor.") - */ - if (grab_window != si->mouse_grab_window || - grab_window != si->keyboard_grab_window) - fprintf(stderr, - "%s: WARNING: expected mouse and keyboard grabs on 0x%x,\n" - "\tbut mouse-grab is 0x%x and keyboard-grab is 0x%x.\n", - blurb(), - (unsigned long) grab_window, - (unsigned long) si->mouse_grab_window, - (unsigned long) si->keyboard_grab_window); - - if (p->verbose_p) - fprintf(stderr, "%s: re-grabbing keyboard and mouse to expose cursor.\n", - blurb()); - grab_keyboard_and_mouse (si, grab_window, 0); - - - if (!si->prefs.debug_p) - XGrabServer (dpy); /* ############ DANGER! */ - - while (passwd_state == pw_read) + while (si->pw_data && si->pw_data->state == pw_read) { - XEvent event; XtAppNextEvent (si->app, &event); - /* wait for timer event */ - if (event.xany.type == 0 && passwd_idle_timer_tick == 0) - passwd_state = pw_time; - XtDispatchEvent (&event); + if (event.xany.window == si->passwd_dialog && event.xany.type == Expose) + draw_passwd_window (si); + else if (event.xany.type == KeyPress) + handle_passwd_key (si, &event.xkey); + else + XtDispatchEvent (&event); } - XUngrabServer (dpy); - XSync (dpy, False); /* ###### (danger over) */ - - - /* Now turn off the mouse cursor again. - */ - if (p->verbose_p) - fprintf(stderr, "%s: re-grabbing keyboard and mouse to hide cursor.\n", - blurb()); - grab_keyboard_and_mouse (si, grab_window, si->screens[0].cursor); - - if (passwd_state != pw_time) - XtRemoveTimeOut (passwd_idle_id); - - if (passwd_state != pw_ok) + switch (si->pw_data->state) { - char *lose; - switch (passwd_state) - { - case pw_time: lose = "Timed out!"; break; - case pw_fail: lose = "Sorry!"; break; - case pw_cancel: lose = 0; break; - default: abort (); - } - -#ifdef HAVE_MOTIF - XmProcessTraversal (passwd_cancel, 0); /* turn off I-beam */ -#else /* HAVE_ATHENA */ - steal_focus_and_colormap (passwd_done); -#endif /* HAVE_ATHENA */ - - if (lose) - { - text_field_set_string (passwd_text, lose, strlen (lose) + 1); - - passwd_idle_timer_tick = 1; - passwd_idle_id = XtAppAddTimeOut (si->app, 3000, passwd_idle_timer, - (XtPointer) si); - while (1) - { - XEvent event; - XtAppNextEvent (si->app, &event); - if (event.xany.type == 0 && /* wait for timer event */ - passwd_idle_timer_tick == 0) - break; - XtDispatchEvent (&event); - } - } + case pw_ok: msg = 0; break; + case pw_time: msg = "Timed out!"; break; + default: msg = "Sorry!"; break; } - memset (typed_passwd, 0, sizeof(typed_passwd)); - text_field_set_string (passwd_text, "", 0); - XtSetKeyboardFocus (parent, None); - -#ifdef DESTROY_WORKS - XtDestroyWidget (passwd_dialog); - passwd_dialog = 0; -#else /* !DESTROY_WORKS */ - XUnmapWindow (XtDisplay (passwd_dialog), XtWindow (passwd_dialog)); -#endif /* !DESTROY_WORKS */ - { - XErrorHandler old_handler = XSetErrorHandler (BadWindow_ehandler); - /* I don't understand why this doesn't refocus on the old selected - window when MWM is running in click-to-type mode. The value of - `focus' seems to be correct. */ - XSetInputFocus (dpy, focus, revert_to, CurrentTime); - XSync (dpy, False); - XSetErrorHandler (old_handler); - } - /* Since we installed our colormap to display the dialog properly, put - the old one back, so that the screensaver_window is now displayed - properly. */ - for (i = 0; i < si->nscreens; i++) + if (msg) { - saver_screen_info *ssi = &si->screens[i]; - if (ssi->cmap) - XInstallColormap (si->dpy, ssi->cmap); - } + si->pw_data->i_beam = 0; + update_passwd_window (si, msg, 0.0); + XBell (si->dpy, False); + XSync (si->dpy, False); + sleep (1); - return (passwd_state == pw_ok ? True : False); + /* Swallow all pending KeyPress/KeyRelease events. */ + { + XEvent e; + while (XCheckMaskEvent (si->dpy, KeyPressMask|KeyReleaseMask, &e)) + ; + } + } } Bool unlock_p (saver_info *si) { - static Bool initted = False; - if (! initted) - { + Screen *screen = si->default_screen->screen; + Colormap cmap = DefaultColormapOfScreen (screen); + Bool status; -#ifndef VERIFY_CALLBACK_WORKS - XtAppAddActions (si->app, actions, XtNumber (actions)); -#endif /* !VERIFY_CALLBACK_WORKS */ + if (si->pw_data || si->passwd_dialog) + destroy_passwd_window (si); - passwd_dialog = 0; - initted = True; - } - if (! passwd_dialog) - make_passwd_dialog (si); - return pop_passwd_dialog (si); + make_passwd_window (si); + if (cmap) XInstallColormap (si->dpy, cmap); + + passwd_event_loop (si); + + status = (si->pw_data->state == pw_ok); + destroy_passwd_window (si); + + cmap = si->default_screen->cmap; + if (cmap) XInstallColormap (si->dpy, cmap); + + return status; } #endif /* !NO_LOCKING -- whole file */