/*
 * xdm - display manager daemon
 *
 * $XConsortium: Login.c,v 1.35 91/07/18 19:31:10 rws Exp $
 *
 * Copyright 1988 Massachusetts Institute of Technology
 *
 * Permission to use, copy, modify, and distribute this software and its
 * documentation for any purpose and without fee is hereby granted, provided
 * that the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation, and that the name of M.I.T. not be used in advertising or
 * publicity pertaining to distribution of the software without specific,
 * written prior permission.  M.I.T. makes no representations about the
 * suitability of this software for any purpose.  It is provided "as is"
 * without express or implied warranty.
 *
 * Author:  Keith Packard, MIT X Consortium
 *
 * Modified by Pierre Ficheux (pierre@lectra.fr):
 *
 *	- displays a logo
 *	- displays the users list (needs the Xpm library)
 *	- user is selectable by pointer
 *	- echo '*' during password input
 *
 */
static char rcsid[] = "$Id: Login.c,v 1.4 1994/11/24 22:37:34 pierre Exp $";

/*
 * Login.c
 */

# include <X11/IntrinsicP.h>
# include <X11/StringDefs.h>
# include <X11/keysym.h>
# include <X11/Xfuncs.h>

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

#ifdef USESHADOW
#include <shadow.h>
#else
#include <pwd.h>
#endif

# include "LoginP.h"

#include <X11/bitmaps/gray>

#ifndef NO_LOGO
#include "logo.xpm"
#endif

#ifndef XDMDIR
#define XDMDIR	"."
#endif

#define offset(field) XtOffsetOf(LoginRec, login.field)
#define goffset(field) XtOffsetOf(WidgetRec, core.field)

static XtResource resources[] = {
    {XtNwidth, XtCWidth, XtRDimension, sizeof(Dimension),
	goffset(width), XtRImmediate,	(XtPointer) 0},
    {XtNheight, XtCHeight, XtRDimension, sizeof(Dimension),
	goffset(height), XtRImmediate,	(XtPointer) 0},
    {XtNx, XtCX, XtRPosition, sizeof (Position),
	goffset(x), XtRImmediate,	(XtPointer) -1},
    {XtNy, XtCY, XtRPosition, sizeof (Position),
	goffset(y), XtRImmediate,	(XtPointer) -1},
    {XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel),
        offset(textpixel), XtRString,	XtDefaultForeground},
    {XtNpromptColor, XtCForeground, XtRPixel, sizeof(Pixel),
        offset(promptpixel), XtRString,	XtDefaultForeground},
    {XtNgreetColor, XtCForeground, XtRPixel, sizeof(Pixel),
        offset(greetpixel), XtRString,	XtDefaultForeground},
    {XtNfailColor, XtCForeground, XtRPixel, sizeof (Pixel),
	offset(failpixel), XtRString,	XtDefaultForeground},
    {XtNfont, XtCFont, XtRFontStruct, sizeof (XFontStruct *),
    	offset (font), XtRString,	"*-new century schoolbook-medium-r-normal-*-180-*"},
    {XtNpromptFont, XtCFont, XtRFontStruct, sizeof (XFontStruct *),
    	offset (promptFont), XtRString, "*-new century schoolbook-bold-r-normal-*-180-*"},
    {XtNgreetFont, XtCFont, XtRFontStruct, sizeof (XFontStruct *),
    	offset (greetFont), XtRString,	"*-new century schoolbook-bold-i-normal-*-240-*"},
    {XtNfailFont, XtCFont, XtRFontStruct, sizeof (XFontStruct *),
	offset (failFont), XtRString,	"*-new century schoolbook-bold-r-normal-*-180-*"},
    {XtNgreeting, XtCGreeting, XtRString, sizeof (char *),
    	offset(greeting), XtRString, "X Window System"},
    {XtNunsecureGreeting, XtCGreeting, XtRString, sizeof (char *),
	offset(unsecure_greet), XtRString, "This is an unsecure session"},
    {XtNnamePrompt, XtCNamePrompt, XtRString, sizeof (char *),
	offset(namePrompt), XtRString, "Login:  "},
    {XtNpasswdPrompt, XtCNamePrompt, XtRString, sizeof (char *),
	offset(passwdPrompt), XtRString, "Password:  "},
    {XtNfail, XtCFail, XtRString, sizeof (char *),
	offset(fail), XtRString, "Login incorrect"},
    {XtNfailTimeout, XtCFailTimeout, XtRInt, sizeof (int),
	offset(failTimeout), XtRImmediate, (XtPointer) 10},
    {XtNnotifyDone, XtCCallback, XtRFunction, sizeof (XtPointer),
	offset(notify_done), XtRFunction, (XtPointer) 0},
    {XtNsessionArgument, XtCSessionArgument, XtRString,	sizeof (char *),
	offset(sessionArg), XtRString, (XtPointer) 0 },
    {XtNsecureSession, XtCSecureSession, XtRBoolean, sizeof (Boolean),
	offset(secure_session), XtRImmediate, False },
    {XtNallowAccess, XtCAllowAccess, XtRBoolean, sizeof (Boolean),
	offset(allow_access), XtRImmediate, False },
#ifndef NO_LOGO
    {XtNdisplayLogo, XtCDisplayLogo, XtRBoolean, sizeof (Boolean),
	offset(display_logo), XtRImmediate, (XtPointer) True },
#endif
    {XtNdisplayUsers, XtCDisplayUsers, XtRBoolean, sizeof (Boolean),
	offset(display_users), XtRImmediate, (XtPointer) True },
    {XtNuserWidth, XtCUserWidth, XtRDimension, sizeof(Dimension),
	offset(userWidth), XtRImmediate,	(XtPointer) 95},
    {XtNuserHeight, XtCUserHeight, XtRDimension, sizeof(Dimension),
	offset(userHeight), XtRImmediate,	(XtPointer) 90},
    {XtNuserFont, XtCFont, XtRFontStruct, sizeof (XFontStruct *),
	offset (userFont), XtRString,	"6x13"},
    {XtNnbColumns, XtCNbColumns, XtRInt, sizeof(int),
	offset(nbColumns), XtRImmediate, (XtPointer) 6},
    {XtNnbLines, XtCNbLines, XtRInt, sizeof(int),
	offset(nbLines), XtRImmediate, (XtPointer) 2},
    {XtNuserColor, XtCForeground, XtRPixel, sizeof(Pixel),
        offset(userpixel), XtRString,	XtDefaultForeground},
    {XtNuseDefaultPixmap, XtCUseDefaultPixmap, XtRBoolean, sizeof (Boolean),
	offset(use_default_pixmap), XtRImmediate, False },
};

#undef offset
#undef goffset

# define TEXT_X_INC(w)	((w)->login.font->max_bounds.width)
# define TEXT_Y_INC(w)	((w)->login.font->max_bounds.ascent +\
			 (w)->login.font->max_bounds.descent)
# define PROMPT_X_INC(w)	((w)->login.promptFont->max_bounds.width)
# define PROMPT_Y_INC(w)	((w)->login.promptFont->max_bounds.ascent +\
			 (w)->login.promptFont->max_bounds.descent)
# define GREET_X_INC(w)	((w)->login.greetFont->max_bounds.width)
# define GREET_Y_INC(w)	((w)->login.greetFont->max_bounds.ascent +\
			 (w)->login.greetFont->max_bounds.descent)
# define FAIL_X_INC(w)	((w)->login.failFont->max_bounds.width)
# define FAIL_Y_INC(w)	((w)->login.failFont->max_bounds.ascent +\
			 (w)->login.failFont->max_bounds.descent)

# define Y_INC(w)	max (TEXT_Y_INC(w), PROMPT_Y_INC(w))

# define LOGIN_PROMPT_W(w) (XTextWidth (w->login.promptFont,\
				 w->login.namePrompt,\
				 strlen (w->login.namePrompt)))
# define PASS_PROMPT_W(w) (XTextWidth (w->login.promptFont,\
				 w->login.passwdPrompt,\
				 strlen (w->login.passwdPrompt)))
# define PROMPT_W(w)	(max(LOGIN_PROMPT_W(w), PASS_PROMPT_W(w)))
# define GREETING(w)	((w)->login.secure_session  && !(w)->login.allow_access ?\
				(w)->login.greeting : (w)->login.unsecure_greet)
# define GREET_X(w)	((int)(w->core.width - XTextWidth (w->login.greetFont,\
			  GREETING(w), strlen (GREETING(w)))) / 2)

#ifndef NO_LOGO
# define GREET_Y(w)	((w)->login.display_logo == True ? w->login.logo_xpma.height + (w)->login.greetFont->max_bounds.ascent : (GREETING(w)[0] ? 2 * GREET_Y_INC (w) : 0))
#else
# define GREET_Y(w)	(GREETING(w)[0] ? 2 * GREET_Y_INC (w) : 0)
#endif

# define GREET_W(w)	(max (XTextWidth (w->login.greetFont,\
			      w->login.greeting, strlen (w->login.greeting)), \
			      XTextWidth (w->login.greetFont,\
			      w->login.unsecure_greet, strlen (w->login.unsecure_greet))))


#define USERS_Y(w)	(GREET_Y(w) + (w)->login.greetFont->max_bounds.descent + Y_INTERVAL)
#define USERS_HEIGHT(w)	((w)->login.display_users == True ? (w)->login.nbLines * (w)->login.userHeight : 0)
#define USERS_WIDTH(w)	((w)->login.nbColumns * (w)->login.userWidth)

# define LOGIN_X(w)	(2 * PROMPT_X_INC(w))
# define LOGIN_Y(w)	(USERS_HEIGHT(w) + GREET_Y(w) + GREET_Y_INC(w) +\
			 w->login.greetFont->max_bounds.ascent + Y_INC(w))
# define LOGIN_W(w)	(w->core.width - 6 * TEXT_X_INC(w))
# define LOGIN_H(w)	(3 * Y_INC(w) / 2)
# define LOGIN_TEXT_X(w)(LOGIN_X(w) + PROMPT_W(w))
# define PASS_X(w)	(LOGIN_X(w))
# define PASS_Y(w)	(LOGIN_Y(w) + 8 * Y_INC(w) / 5)
# define PASS_W(w)	(LOGIN_W(w))
# define PASS_H(w)	(LOGIN_H(w))
# define PASS_TEXT_X(w)	(PASS_X(w) + PROMPT_W(w))
# define FAIL_X(w)	((int)(w->core.width - XTextWidth (w->login.failFont,\
				w->login.fail, strlen (w->login.fail))) / 2)
# define FAIL_Y(w)	(PASS_Y(w) + 2 * FAIL_Y_INC (w) +\
			w->login.failFont->max_bounds.ascent)
# define FAIL_W(w)	(XTextWidth (w->login.failFont,\
			 w->login.fail, strlen (w->login.fail)))

# define PAD_X(w)	(2 * (LOGIN_X(w) + max (GREET_X_INC(w), FAIL_X_INC(w))))

# define PAD_Y(w)	(max (max (Y_INC(w), GREET_Y_INC(w)),\
			     FAIL_Y_INC(w)))
	

#define Y_INTERVAL		15
#define SCROLLBAR_X_INTERVAL	4
#define SCROLLBAR_WIDTH(w)	15
#define SCROLLBAR_HEIGHT(w)	USERS_HEIGHT(w)
#define SCROLLBAR_X(w)    	(USERS_WIDTH(w) + SCROLLBAR_X_INTERVAL)
#define SCROLLBAR_Y(w)    	USERS_Y(w)

#define MOVER_X(w)		(SCROLLBAR_X(w) + 2)
#define MOVER_Y(w)		(SCROLLBAR_Y(w) + 2)
#define MOVER_WIDTH(w)		(SCROLLBAR_WIDTH(w) - 4 + 1)
#define MOVER_HEIGHT(w)		((int)((double)(SCROLLBAR_HEIGHT(w) - 4) * ((double)(w->login.nbLines) / (double)(w->login.nb_lines))))


static void Initialize(), Realize(), Destroy(), Redisplay();
static Boolean SetValues();
static void draw_it ();
static void draw_scrollbar_and_users ();

static void ClassInitialize();

static int max (a,b) { return a > b ? a : b; }

static void
EraseName (w, cursor)
    LoginWidget	w;
    int		cursor;
{
    int	x;

    x = LOGIN_TEXT_X (w);
    if (cursor > 0)
	x += XTextWidth (w->login.font, w->login.data.name, cursor);
    XDrawString (XtDisplay(w), XtWindow (w), w->login.bgGC, x, LOGIN_Y(w),
		w->login.data.name + cursor, strlen (w->login.data.name + cursor));
}

static void fill_echo (w, n)
LoginWidget w;
register int n;
{
    register int i;

    if (n > NAME_LEN)
	return;

    for (i = 0 ; i != n ; i++)
	w->login.echo[i] = '*';
    w->login.echo[i] = 0;
}

static void
ErasePasswd (w, cursor)
    LoginWidget	w;
    int		cursor;
{
    int	x;

    x = PASS_TEXT_X (w);
    fill_echo (w, strlen (w->login.data.passwd));

    if (cursor > 0)
	x += XTextWidth (w->login.font, w->login.echo, cursor);
    XDrawString (XtDisplay(w), XtWindow (w), w->login.bgGC, x, PASS_Y(w),
		w->login.echo + cursor, strlen (w->login.echo + cursor));
}

static void
DrawName (w, cursor)
    LoginWidget	w;
    int		cursor;
{
    int	x;

    x = LOGIN_TEXT_X (w);
    if (cursor > 0)
	x += XTextWidth (w->login.font, w->login.data.name, cursor);
    XDrawString (XtDisplay(w), XtWindow (w), w->login.textGC, x, LOGIN_Y(w),
		w->login.data.name + cursor, strlen (w->login.data.name + cursor));
}

static void
DrawPasswd (w, cursor)
    LoginWidget	w;
    int		cursor;
{
    int	x;

    x = PASS_TEXT_X (w);
    fill_echo (w, strlen (w->login.data.passwd));

    if (cursor > 0)
	x += XTextWidth (w->login.font, w->login.echo, cursor);
    XDrawString (XtDisplay(w), XtWindow (w), w->login.textGC, x, PASS_Y(w),
	       w->login.echo + cursor, strlen (w->login.echo + cursor));
}

static void
realizeCursor (w, gc)
    LoginWidget	w;
    GC		gc;
{
    int	x, y;
    int height, width;

    switch (w->login.state) {
    case GET_NAME:
	x = LOGIN_TEXT_X (w);
	y = LOGIN_Y (w);
	height = w->login.font->max_bounds.ascent + w->login.font->max_bounds.descent;
	width = 1;
	if (w->login.cursor > 0)
	    x += XTextWidth (w->login.font, w->login.data.name, w->login.cursor);
	break;
    case GET_PASSWD:
	x = PASS_TEXT_X (w);
	y = PASS_Y (w);
	height = w->login.font->max_bounds.ascent + w->login.font->max_bounds.descent;
	width = 1;
	fill_echo (w, strlen (w->login.data.passwd));
	if (w->login.cursor > 0)
	    x += XTextWidth (w->login.font, w->login.echo, w->login.cursor);
	break;
    default:
	return;
    }
    XFillRectangle (XtDisplay (w), XtWindow (w), gc,
		    x, y - w->login.font->max_bounds.ascent, width, height);
}

static void
EraseFail (w)
    LoginWidget	w;
{
    int x = FAIL_X(w);
    int y = FAIL_Y(w);

    XSetForeground (XtDisplay (w), w->login.failGC,
			w->core.background_pixel);
    XDrawString (XtDisplay (w), XtWindow (w), w->login.failGC,
		x, y,
		w->login.fail, strlen (w->login.fail));
    w->login.failUp = 0;
    XSetForeground (XtDisplay (w), w->login.failGC,
			w->login.failpixel);
}

static void
XorCursor (w)
    LoginWidget	w;
{
    realizeCursor (w, w->login.xorGC);
}

static void
RemoveFail (w)
    LoginWidget	w;
{
    if (w->login.failUp)
	EraseFail (w);
}

static void
EraseCursor (w)
    LoginWidget (w);
{
    realizeCursor (w, w->login.bgGC);
}

/*ARGSUSED*/
void failTimeout (client_data, id)
    XtPointer	client_data;
    XtIntervalId *	id;
{
    LoginWidget	w = (LoginWidget)client_data;

    Debug ("failTimeout\n");
    EraseFail (w);
}

DrawFail (ctx)
    Widget	ctx;
{
    LoginWidget	w;

    w = (LoginWidget) ctx;
    XorCursor (w);
    ResetLogin (w);
    XorCursor (w);
    w->login.failUp = 1;
    RedrawFail (w);
    if (w->login.failTimeout > 0) {
	Debug ("failTimeout: %d\n", w->login.failTimeout);
	XtAppAddTimeOut(XtWidgetToApplicationContext ((Widget)w),
			w->login.failTimeout * 1000,
		        failTimeout, (XtPointer) w);
    }
}

RedrawFail (w)
    LoginWidget w;
{
    int x = FAIL_X(w);
    int y = FAIL_Y(w);

    if (w->login.failUp)
        XDrawString (XtDisplay (w), XtWindow (w), w->login.failGC,
		    x, y,
		    w->login.fail, strlen (w->login.fail));
}

static void redraw_users (w, client_data, pevent)
Widget w;
caddr_t client_data;
XButtonEvent *pevent;
{
    LoginWidget lw = (LoginWidget)w;
    lw->login.y_mover = lw->login.old_y_mover;
    lw->login.draw_mover = False;
    XtRemoveEventHandler (w, ButtonReleaseMask, False, redraw_users, NULL);
    draw_scrollbar_and_users (w, False, True);
}

static void draw_outline (w, ym, clear)
LoginWidget w;
int ym;
Boolean clear;
{
    int y, mh = MOVER_HEIGHT(w);

    y = (clear == True ? ym - w->login.delta_y_mover : ym);

    if (y < MOVER_Y(w))
	y = MOVER_Y(w);
    else if ((y + mh) > (SCROLLBAR_Y(w) + SCROLLBAR_HEIGHT(w) - 2))
	y = SCROLLBAR_Y(w) + SCROLLBAR_HEIGHT(w) - 2 - mh;

    if ((clear == True) && w->login.old_y_mover)
	XFillRectangle (XtDisplay(w), XtWindow(w), w->login.moverGC, MOVER_X(w), w->login.old_y_mover, MOVER_WIDTH(w), mh);
    XFillRectangle (XtDisplay(w),  XtWindow(w), w->login.moverGC, MOVER_X(w), y, MOVER_WIDTH(w), mh);
    w->login.old_y_mover = y;
}

static void draw_mover_outline (w, client_data, pevent)
LoginWidget w;
caddr_t client_data;
XMotionEvent *pevent;
{
    if (w->login.draw_mover == True)
	draw_outline (w, pevent->y, True, True);
}

static void draw_scrollbar_and_users (w, draw_scrollbar, clear_outline)
LoginWidget w;
Boolean draw_scrollbar, clear_outline;
{
    XpmAttributes xpma;
    XGCValues	xgcv;	
    char xpm_file[256];
    struct stat statb;
    int line_index = 0, column_index = 0, no_first_user;
    int x, y, i;
    register struct xdm_user *current_user;
    Pixmap current_pixmap = (Pixmap)NULL;

    XClearArea (XtDisplay (w), XtWindow (w), SCROLLBAR_X_INTERVAL+1, USERS_Y(w), w->login.userWidth * w->login.nbColumns - 1, w->login.userHeight * w->login.nbLines, False);    

    /* Displays the scrollbar */
    if (w->login.use_scrollbar == True) {
	if (draw_scrollbar == True) {
	    XDrawRectangle (XtDisplay(w), XtWindow(w), w->login.pixmapGC, SCROLLBAR_X_INTERVAL, SCROLLBAR_Y(w) - 2, w->core.width - 2 * SCROLLBAR_X_INTERVAL, SCROLLBAR_HEIGHT(w) + 4);
	    XDrawRectangle (XtDisplay(w), XtWindow(w), w->login.pixmapGC, SCROLLBAR_X(w), SCROLLBAR_Y(w) - 2, SCROLLBAR_WIDTH(w), SCROLLBAR_HEIGHT(w) + 4);
	    draw_outline (w, w->login.y_mover, clear_outline);
	}
	
	/* Then the users */
	no_first_user = (int)(((double)(w->login.y_mover - MOVER_Y(w)) / (double)(SCROLLBAR_HEIGHT(w) - 4)) * w->login.nb_displayed_users);
#ifdef DEBUG
	printf ("draw_scrollbar_and_users: no_first_user = %d\n", no_first_user);
#endif
	
	/* The first displayed user */
	for (i = 0, w->login.first_user = w->login.user_list ; w->login.first_user && (i != no_first_user) ; w->login.first_user = w->login.first_user->next)
	    if (w->login.first_user->displayed)
		i++;

	/* Then the last */
	for (i = 0, w->login.last_user = w->login.first_user ; w->login.last_user && (i != (w->login.nbLines * w->login.nbColumns)) ; w->login.last_user = w->login.last_user->next)
	    if (w->login.last_user->displayed)
		i++;
    }

    w->login.xpmcs[0].name = NULL;
    w->login.xpmcs[0].value = "None";
    w->login.xpmcs[0].pixel = w->core.background_pixel;
    xpma.valuemask = XpmInfos | XpmColorSymbols;
    xpma.colorsymbols = &w->login.xpmcs[0];
    xpma.numsymbols = 1;

    for (current_user = w->login.first_user; current_user != w->login.last_user ; current_user = current_user->next) {
	if (current_user->displayed == False) {
	    if (w->login.use_default_pixmap == False) {
		continue;
	    }
	    else
		sprintf (xpm_file, "%s/pixmaps/default.xpm", XDMDIR);
	}
	else
	    sprintf (xpm_file, "%s/pixmaps/%s.xpm", XDMDIR, current_user->name);
	
	/* Display the X PixMaps */
	if (XpmReadFileToPixmap (XtDisplay (w), XtWindow (w), xpm_file, &current_pixmap, NULL, &xpma) != XpmSuccess) {
	    fprintf (stderr, "draw_scrollbar_and_users: XpmReadFileToPixmap, error with file %s\n", xpm_file);
	    continue;
	}
	
	/* Displays the Pixmap */
	x = (column_index * w->login.userWidth) + (w->login.userWidth - xpma.width) / 2;
	y = USERS_Y(w) + (line_index * w->login.userHeight) + (w->login.userHeight - xpma.height - 5 - (w->login.userFont->max_bounds.ascent + w->login.userFont->max_bounds.descent)) / 2;
#ifdef DEBUG
	printf ("draw_scrollbar_and_users: drawing %s at %d, %d\n", xpm_file, x, y);
#endif
	XCopyArea (XtDisplay(w), current_pixmap, XtWindow(w), w->login.pixmapGC, 0, 0, xpma.width, xpma.height, x, y);
	
	/* Displays the login name */
	x = (column_index * w->login.userWidth) + (w->login.userWidth - XTextWidth (w->login.userFont, current_user->name, strlen (current_user->name))) / 2;
	y += xpma.height + 5 + w->login.userFont->max_bounds.ascent;
	XDrawString (XtDisplay(w),  XtWindow(w), w->login.userGC, x, y, current_user->name, strlen (current_user->name));
	
	current_user->x = x;
	current_user->y = y;
	
	if (++column_index == w->login.nbColumns) {
	    column_index = 0;
	    if (++line_index > w->login.nb_lines) {
		fprintf (stderr, "draw_scrollbar_and_users: too many users to display !\n");
		break;
	    }
	}
    }

    if (current_pixmap)
	XFreePixmap (XtDisplay(w), current_pixmap);
}

static void
draw_it (w)
    LoginWidget	w;
{

    EraseCursor (w);

#ifndef NO_LOGO
    if (w->login.display_logo) {
	XCopyArea (XtDisplay(w), w->login.logopixmap, XtWindow(w), w->login.logoGC, 0, 0, w->login.logo_xpma.width, w->login.logo_xpma.height, 0, 0); 
    }
#endif

    if (GREETING(w)[0])
	    XDrawString (XtDisplay (w), XtWindow (w), w->login.greetGC,
			GREET_X(w), GREET_Y(w),
			GREETING(w), strlen (GREETING(w)));
    XDrawString (XtDisplay (w), XtWindow (w), w->login.promptGC,
		LOGIN_X(w), LOGIN_Y(w),
		w->login.namePrompt, strlen (w->login.namePrompt));
    XDrawString (XtDisplay (w), XtWindow (w), w->login.promptGC,
		PASS_X(w), PASS_Y(w),
		w->login.passwdPrompt, strlen (w->login.passwdPrompt));
    RedrawFail (w);
    DrawName (w, 0);
    if (w->login.display_users == True)
	draw_scrollbar_and_users (w, True, False);
    XorCursor (w);

    /*
     * The GrabKeyboard here is needed only because of
     * a bug in the R3 server -- the keyboard is grabbed on
     * the root window, and the server won't dispatch events
     * to the focus window unless the focus window is a ancestor
     * of the grab window.  Bug in server already found and fixed,
     * compatibility until at least R4.
     */
/*
    if (XGrabKeyboard (XtDisplay (w), XtWindow (w), False, GrabModeAsync,
		       GrabModeAsync, CurrentTime) != GrabSuccess)
    {
	XSetInputFocus (XtDisplay (w), XtWindow (w),
			RevertToPointerRoot, CurrentTime);
    }
*/
}

/*ARGSUSED*/
static void
DeleteBackwardChar (ctxw, event, params, num_params)
    Widget ctxw;
    XEvent	*event;
    String	*params;
    Cardinal	*num_params;
{
    LoginWidget ctx = (LoginWidget)ctxw;

    XorCursor (ctx);
    RemoveFail (ctx);
    if (ctx->login.cursor > 0) {
	ctx->login.cursor--;
	switch (ctx->login.state) {
	case GET_NAME:
	    EraseName (ctx, ctx->login.cursor);
	    strcpy (ctx->login.data.name + ctx->login.cursor,
		    ctx->login.data.name + ctx->login.cursor + 1);
	    DrawName (ctx, ctx->login.cursor);
	    break;
	case GET_PASSWD:
	    ErasePasswd (ctx, ctx->login.cursor);
	    strcpy (ctx->login.data.passwd + ctx->login.cursor,
		    ctx->login.data.passwd + ctx->login.cursor + 1);
	    DrawPasswd (ctx, ctx->login.cursor);
	    break;
	}
    }
    XorCursor (ctx);	
}

/*ARGSUSED*/
static void
DeleteForwardChar (ctxw, event, params, num_params)
    Widget	ctxw;
    XEvent	*event;
    String	*params;
    Cardinal	*num_params;
{
    LoginWidget ctx = (LoginWidget)ctxw;

    XorCursor (ctx);
    RemoveFail (ctx);
    switch (ctx->login.state) {
    case GET_NAME:
	if (ctx->login.cursor < (int)strlen (ctx->login.data.name)) {
	    EraseName (ctx, ctx->login.cursor);
	    strcpy (ctx->login.data.name + ctx->login.cursor,
		    ctx->login.data.name + ctx->login.cursor + 1);
	    DrawName (ctx, ctx->login.cursor);
	}
	break;
    case GET_PASSWD:
    	if (ctx->login.cursor < (int)strlen (ctx->login.data.passwd)) {
	    ErasePasswd (ctx, ctx->login.cursor);
	    strcpy (ctx->login.data.passwd + ctx->login.cursor,
		    ctx->login.data.passwd + ctx->login.cursor + 1);
	    DrawPasswd (ctx, ctx->login.cursor);
	}
	break;
    }
    XorCursor (ctx);	
}

/*ARGSUSED*/
static void
MoveBackwardChar (ctxw, event, params, num_params)
    Widget	ctxw;
    XEvent	*event;
    String	*params;
    Cardinal	*num_params;
{
    LoginWidget	ctx = (LoginWidget)ctxw;

    XorCursor (ctx);
    RemoveFail (ctx);
    if (ctx->login.cursor > 0)
    	ctx->login.cursor--;
    XorCursor (ctx);
}

/*ARGSUSED*/
static void
MoveForwardChar (ctxw, event, params, num_params)
    Widget	ctxw;
    XEvent	*event;
    String	*params;
    Cardinal	*num_params;
{
    LoginWidget ctx = (LoginWidget)ctxw;

    XorCursor (ctx);
    RemoveFail (ctx);
    switch (ctx->login.state) {
    case GET_NAME:
    	if (ctx->login.cursor < (int)strlen(ctx->login.data.name))
	    ++ctx->login.cursor;
	break;
    case GET_PASSWD:
    	if (ctx->login.cursor < (int)strlen(ctx->login.data.passwd))
	    ++ctx->login.cursor;
	break;
    }
    XorCursor (ctx);
}

/*ARGSUSED*/
static void
MoveToBegining (ctxw, event, params, num_params)
    Widget	ctxw;
    XEvent	*event;
    String	*params;
    Cardinal	*num_params;
{
    LoginWidget ctx = (LoginWidget)ctxw;

    XorCursor (ctx);
    RemoveFail (ctx);
    ctx->login.cursor = 0;
    XorCursor (ctx);
}

/*ARGSUSED*/
static void
MoveToEnd (ctxw, event, params, num_params)
    Widget	ctxw;
    XEvent	*event;
    String	*params;
    Cardinal	*num_params;
{
    LoginWidget ctx = (LoginWidget)ctxw;

    XorCursor (ctx);
    RemoveFail (ctx);
    switch (ctx->login.state) {
    case GET_NAME:
    	ctx->login.cursor = strlen (ctx->login.data.name);
	break;
    case GET_PASSWD:
    	ctx->login.cursor = strlen (ctx->login.data.passwd);
	break;
    }
    XorCursor (ctx);
}

/*ARGSUSED*/
static void
EraseToEndOfLine (ctxw, event, params, num_params)
    Widget	ctxw;
    XEvent	*event;
    String	*params;
    Cardinal	*num_params;
{
    LoginWidget ctx = (LoginWidget)ctxw;

    XorCursor (ctx);
    RemoveFail (ctx);
    switch (ctx->login.state) {
    case GET_NAME:
	EraseName (ctx, ctx->login.cursor);
	ctx->login.data.name[ctx->login.cursor] = '\0';
	break;
    case GET_PASSWD:
	ErasePasswd (ctx, ctx->login.cursor);
	ctx->login.data.passwd[ctx->login.cursor] = '\0';
	break;
    }
    XorCursor (ctx);
}

/*ARGSUSED*/
static void
EraseLine (ctxw, event, params, num_params)
    Widget	ctxw;
    XEvent	*event;
    String	*params;
    Cardinal	*num_params;
{
    MoveToBegining (ctxw, event, params, num_params);
    EraseToEndOfLine (ctxw, event, params, num_params);
}

/*ARGSUSED*/
static void
FinishField (ctxw, event, params, num_params)
    Widget	ctxw;
    XEvent	*event;
    String	*params;
    Cardinal	*num_params;
{
    LoginWidget ctx = (LoginWidget)ctxw;
#ifdef USESHADOW
    struct spwd *sp;
#else
    struct passwd *pw;
#endif

    XorCursor (ctx);
    RemoveFail (ctx);
    switch (ctx->login.state) {
    case GET_NAME:

	/* No passwd ==> login ! */
#ifdef USESHADOW
	sp = getspnam (ctx->login.data.name);
	if (sp && *(sp->sp_pwdp) == 0) {
#else
	pw = getpwnam (ctx->login.data.name);
	if (pw && *(pw->pw_passwd) == 0) {
#endif
	    ctx->login.state = DONE;
	    ctx->login.cursor = 0;
	    (*ctx->login.notify_done) (ctx, &ctx->login.data, NOTIFY_OK);
	}
	else {
	    ctx->login.state = GET_PASSWD;
	    ctx->login.cursor = 0;
	}
	break;
    case GET_PASSWD:
	ctx->login.state = DONE;
	ctx->login.cursor = 0;
	(*ctx->login.notify_done) (ctx, &ctx->login.data, NOTIFY_OK);
	break;
    }
    XorCursor (ctx);
}

/*ARGSUSED*/
static void
AllowAccess (ctxw, event, params, num_params)
    Widget	ctxw;
    XEvent	*event;
    String	*params;
    Cardinal	*num_params;
{
    LoginWidget ctx = (LoginWidget)ctxw;
    Arg	arglist[1];
    Boolean allow;

    RemoveFail (ctx);
    XtSetArg (arglist[0], XtNallowAccess, (char *) &allow);
    XtGetValues ((Widget) ctx, arglist, 1);
    XtSetArg (arglist[0], XtNallowAccess, !allow);
    XtSetValues ((Widget) ctx, arglist, 1);
}

/*ARGSUSED*/
static void
SetSessionArgument (ctxw, event, params, num_params)
    Widget	ctxw;
    XEvent	*event;
    String	*params;
    Cardinal	*num_params;
{
    LoginWidget ctx = (LoginWidget)ctxw;

    RemoveFail (ctx);
    if (ctx->login.sessionArg)
	XtFree (ctx->login.sessionArg);
    ctx->login.sessionArg = 0;
    if (*num_params > 0) {
	ctx->login.sessionArg = XtMalloc (strlen (params[0]) + 1);
	if (ctx->login.sessionArg)
	    strcpy (ctx->login.sessionArg, params[0]);
	else
	    LogOutOfMem ("set session argument");
    }
}

/*ARGSUSED*/
static void
RestartSession (ctxw, event, params, num_params)
    Widget	ctxw;
    XEvent	*event;
    String	*params;
    Cardinal	*num_params;
{
    LoginWidget ctx = (LoginWidget)ctxw;

    XorCursor (ctx);
    RemoveFail (ctx);
    ctx->login.state = DONE;
    ctx->login.cursor = 0;
    (*ctx->login.notify_done) (ctx, &ctx->login.data, NOTIFY_RESTART);
    XorCursor (ctx);
}

/*ARGSUSED*/
static void
AbortSession (ctxw, event, params, num_params)
    Widget	ctxw;
    XEvent	*event;
    String	*params;
    Cardinal	*num_params;
{
    LoginWidget ctx = (LoginWidget)ctxw;

    XorCursor (ctx);
    RemoveFail (ctx);
    ctx->login.state = DONE;
    ctx->login.cursor = 0;
    (*ctx->login.notify_done) (ctx, &ctx->login.data, NOTIFY_ABORT);
    XorCursor (ctx);
}

/*ARGSUSED*/
static void
AbortDisplay (ctxw, event, params, num_params)
    Widget	ctxw;
    XEvent	*event;
    String	*params;
    Cardinal	*num_params;
{
    LoginWidget ctx = (LoginWidget)ctxw;

    XorCursor (ctx);
    RemoveFail (ctx);
    ctx->login.state = DONE;
    ctx->login.cursor = 0;
    (*ctx->login.notify_done) (ctx, &ctx->login.data, NOTIFY_ABORT_DISPLAY);
    XorCursor (ctx);
}

ResetLogin (w)
    LoginWidget	w;
{
    EraseName (w, 0);
    ErasePasswd (w, 0);
    w->login.cursor = 0;
    w->login.data.name[0] = '\0';
    w->login.data.passwd[0] = '\0';
    w->login.state = GET_NAME;
}

/* ARGSUSED */
static void
InsertChar (ctxw, event, params, num_params)
    Widget	ctxw;
    XEvent	*event;
    String	*params;
    Cardinal	*num_params;
{
    LoginWidget ctx = (LoginWidget)ctxw;

    char strbuf[128];
    int  len;

    len = XLookupString (&event->xkey, strbuf, sizeof (strbuf), 0, 0);
    strbuf[len] = '\0';

    if (len + (int)strlen(ctx->login.state == GET_NAME ? ctx->login.data.name :  ctx->login.data.passwd) >= NAME_LEN - 1)
	len = NAME_LEN - strlen(ctx->login.state == GET_NAME ? ctx->login.data.name :  ctx->login.data.passwd) - 2;
    if (len == 0)
	return;

    XorCursor (ctx);
    RemoveFail (ctx);
    switch (ctx->login.state) {
    case GET_NAME:
	EraseName (ctx, ctx->login.cursor);
	bcopy (ctx->login.data.name + ctx->login.cursor,
	       ctx->login.data.name + ctx->login.cursor + len,
	       strlen (ctx->login.data.name + ctx->login.cursor) + 1);
	bcopy (strbuf, ctx->login.data.name + ctx->login.cursor, len);
	DrawName (ctx, ctx->login.cursor);
	ctx->login.cursor += len;
	break;
    case GET_PASSWD:
	if (len + (int)strlen(ctx->login.data.passwd) >= NAME_LEN - 1)
	    len = NAME_LEN - strlen(ctx->login.data.passwd) - 2;
	if (len == 0)
	    return;

	ErasePasswd (ctx, ctx->login.cursor);
	bcopy (ctx->login.data.passwd + ctx->login.cursor,
	       ctx->login.data.passwd + ctx->login.cursor + len,
	       strlen (ctx->login.data.passwd + ctx->login.cursor) + 1);
	bcopy (strbuf, ctx->login.data.passwd + ctx->login.cursor, len);
	DrawPasswd (ctx, ctx->login.cursor);
	ctx->login.cursor += len;
	break;
    }
    XorCursor (ctx);
}

/*ARGSUSED*/
static void FetchDisplayArg(widget, size, value)
    Widget widget;
    Cardinal *size;
    XrmValue* value;
{
    value->size = sizeof(Display*);
    value->addr = (caddr_t)&DisplayOfScreen(XtScreenOfObject(widget));
}

static XtConvertArgRec displayConvertArg[] = {
    {XtProcedureArg, (XtPointer)FetchDisplayArg, 0},
};

/* ARGSUSED */
static void Initialize (greq, gnew, args, num_args)
    Widget greq, gnew;
    ArgList args;
    Cardinal *num_args;
{
    LoginWidget w = (LoginWidget)gnew;
    XtGCMask	valuemask, xvaluemask;
    XGCValues	myXGCV;
    Arg		position[2];
    Position	x, y;
    register struct xdm_user *current_user = NULL, *previous_user = NULL;
#ifdef USESHADOW
    struct spwd *sp;
#else
    struct passwd *pw;
#endif

    char xpm_file[256];
    struct stat statb;

    w->login.nb_users = w->login.nb_displayed_users = w->login.nb_lines = 0;
    w->login.user_list = w->login.first_user = w->login.last_user = NULL;
    w->login.draw_mover = w->login.use_scrollbar = w->login.user_selected = False,
    w->login.y_mover = w->login.old_y_mover = w->login.delta_y_mover = 0;
    w->login.echo[0] = 0;

    myXGCV.foreground = w->login.textpixel;
    myXGCV.background = w->core.background_pixel;
    valuemask = GCForeground | GCBackground;
    if (w->login.font) {
	myXGCV.font = w->login.font->fid;
	valuemask |= GCFont;
    }
    w->login.textGC = XtGetGC(gnew, valuemask, &myXGCV);
    myXGCV.foreground = w->core.background_pixel;
    w->login.bgGC = XtGetGC(gnew, valuemask, &myXGCV);

    myXGCV.foreground = w->login.textpixel ^ w->core.background_pixel;
    myXGCV.function = GXxor;
    xvaluemask = valuemask | GCFunction;
    w->login.xorGC = XtGetGC (gnew, xvaluemask, &myXGCV);

    myXGCV.foreground = w->login.textpixel ^ w->core.background_pixel;
    myXGCV.function = GXxor;
    myXGCV.stipple = XCreateBitmapFromData (XtDisplay (w), DefaultRootWindow (XtDisplay (w)), gray_bits, gray_width, gray_height);
    myXGCV.fill_style = FillStippled;
    xvaluemask = valuemask | GCFunction | GCStipple | GCFillStyle;
    w->login.moverGC = XtGetGC (gnew, xvaluemask, &myXGCV);

#ifndef NO_LOGO
    /* Init of the logo */
    if (w->login.display_logo) {
	w->login.logoGC = XtGetGC (gnew, 0, NULL);
	w->login.xpmcs[0].name = NULL;
	w->login.xpmcs[0].value = "None";
	w->login.xpmcs[0].pixel = w->core.background_pixel;
	w->login.logo_xpma.valuemask = XpmInfos | XpmColorSymbols;
	w->login.logo_xpma.colorsymbols = &w->login.xpmcs[0];
	w->login.logo_xpma.numsymbols = 1;
	XpmCreatePixmapFromData (XtDisplay (w), DefaultRootWindow (XtDisplay (w)), logo, &w->login.logopixmap, NULL, &w->login.logo_xpma);
    }
#endif

    /*
     * Note that the second argument is a GCid -- QueryFont accepts a GCid and
     * returns the curently contained font.
     */

    if (w->login.font == NULL)
	w->login.font = XQueryFont (XtDisplay (w),
		XGContextFromGC (DefaultGCOfScreen (XtScreen (w))));

    xvaluemask = valuemask;
    if (w->login.promptFont == NULL)
        w->login.promptFont = w->login.font;
    else
	xvaluemask |= GCFont;

    myXGCV.foreground = w->login.promptpixel;
    myXGCV.font = w->login.promptFont->fid;
    w->login.promptGC = XtGetGC (gnew, xvaluemask, &myXGCV);

    xvaluemask = valuemask;
    if (w->login.greetFont == NULL)
    	w->login.greetFont = w->login.font;
    else
	xvaluemask |= GCFont;

    myXGCV.foreground = w->login.greetpixel;
    myXGCV.font = w->login.greetFont->fid;
    w->login.greetGC = XtGetGC (gnew, xvaluemask, &myXGCV);

    xvaluemask = valuemask;
    if (w->login.failFont == NULL)
	w->login.failFont = w->login.font;
    else
	xvaluemask |= GCFont;

    myXGCV.foreground = w->login.failpixel;
    myXGCV.font = w->login.failFont->fid;
    w->login.failGC = XtGetGC (gnew, xvaluemask, &myXGCV);

    w->login.data.name[0] = '\0';
    w->login.data.passwd[0] = '\0';
    w->login.state = GET_NAME;
    w->login.cursor = 0;
    w->login.failUp = 0;

    if (w->login.display_users == True) {
	myXGCV.font = w->login.userFont->fid;
	myXGCV.foreground = w->login.userpixel;
	w->login.userGC = XtGetGC (gnew, GCFont | GCForeground, &myXGCV);

	myXGCV.foreground = (Pixel)XtDefaultForeground;
	myXGCV.background = w->core.background_pixel;
	w->login.pixmapGC = XtGetGC (gnew, GCForeground | GCBackground, &myXGCV);

	/* list of users */
	for (;;) {
#ifdef USESHADOW
	    if (!(sp = getspent ()))
#else
	    if (!(pw = getpwent ()))
#endif
		break;
	    
	    if (!(current_user = (struct xdm_user *)calloc (1, sizeof(struct xdm_user)))) {
		fprintf (stderr, "Initialize: can't allocate user, aborting !\n");
		exit (1);
	    }

#ifdef USESHADOW	    
	    if (!(current_user->name = (char *)calloc (1, 1+strlen (sp->sp_namp)))) {
#else
	    if (!(current_user->name = (char *)calloc (1, 1+strlen (pw->pw_name)))) {
#endif
		fprintf (stderr, "Initialize: can't allocate user name, aborting !\n");
		exit (1);
	    }
	    
	    if (!w->login.user_list)
		w->login.user_list = current_user;
#ifdef USESHADOW
	    strcpy (current_user->name, sp->sp_namp);
#else
	    strcpy (current_user->name, pw->pw_name);
#endif
#ifdef DEBUG
	    printf ("Initialize: %s, ", current_user->name);
#endif 
	    if (previous_user)
		previous_user->next = current_user;
	    previous_user = current_user;

	    w->login.nb_users++;

	    sprintf (xpm_file, "%s/pixmaps/%s.xpm", XDMDIR, current_user->name);
	    if (stat (xpm_file, &statb) == 0 || w->login.use_default_pixmap) {
		w->login.nb_displayed_users++; 
		current_user->displayed = True;
#ifdef DEBUG
		printf ("displayed !\n");
#endif
	    }
	    else {
		current_user->displayed = False;
#ifdef DEBUG
		putchar('\n');
#endif
	    }
	}

	/* # of lines */
	if (w->login.nb_displayed_users <= w->login.nbColumns)
	    w->login.nb_lines = 1;
	else {
	    w->login.nb_lines = w->login.nb_displayed_users / w->login.nbColumns;
	    if (w->login.nb_displayed_users % w->login.nbColumns > 0)
		w->login.nb_lines++;
	}
		
	w->login.first_user = w->login.user_list;
	w->login.last_user = NULL;
	
	if (w->login.nb_lines > w->login.nbLines)
	    w->login.use_scrollbar = True;

#ifdef USESHADOW	
	endspent ();
#else
	endpwent ();
#endif

	/* width & height */
	if (w->core.width == 0)
	    w->core.width = w->login.userWidth * w->login.nbColumns + (w->login.use_scrollbar == True ? (SCROLLBAR_WIDTH(w) + 2 * SCROLLBAR_X_INTERVAL) : 0);

	if (w->core.height == 0)
	    w->core.height = FAIL_Y(w) + w->login.failFont->max_bounds.descent + Y_INTERVAL;

	XtAddEventHandler ((Widget)w, PointerMotionMask, False, draw_mover_outline, NULL);
	w->login.y_mover = MOVER_Y(w);
    }
    else { /* don't display users */
	if (w->core.width == 0)
	    w->core.width = max (GREET_W(w), FAIL_W(w)) + PAD_X(w);

	if (w->core.height == 0) {
	    int fy = FAIL_Y(w);
	    int pady = PAD_Y(w);
	    w->core.height = fy + pady;	/* for stupid compilers */
	}
    }

    /* position */
    if ((x = w->core.x) == -1)
	x = (int)(WidthOfScreen (XtScreen (w)) - w->core.width) / 2;
    if ((y = w->core.y) == -1)
	y = (int)(HeightOfScreen (XtScreen (w)) - w->core.height) / 3;
    XtSetArg (position[0], XtNx, x);
    XtSetArg (position[1], XtNy, y);
    XtSetValues (XtParent (w), position, (Cardinal) 2);
}

static void Realize (gw, valueMask, attrs)
     Widget gw;
     XtValueMask *valueMask;
     XSetWindowAttributes *attrs;
{
    XtCreateWindow( gw, (unsigned)InputOutput, (Visual *)CopyFromParent,
		     *valueMask, attrs );
}

static void Destroy (gw)
     Widget gw;
{
    LoginWidget w = (LoginWidget)gw;
    register struct xdm_user *current_user = w->login.user_list, *next_user;

    bzero (w->login.data.name, NAME_LEN);
    bzero (w->login.data.passwd, NAME_LEN);
    XtReleaseGC(gw, w->login.textGC);
    XtReleaseGC(gw, w->login.bgGC);
    XtReleaseGC(gw, w->login.xorGC);
    XtReleaseGC(gw, w->login.promptGC);
    XtReleaseGC(gw, w->login.greetGC);
    XtReleaseGC(gw, w->login.failGC);

#ifndef NO_LOGO
    if (w->login.display_logo == True)
	XFreePixmap (XtDisplay (w), w->login.logopixmap);
#endif

    if (w->login.display_users == True) {
	XtReleaseGC(gw, w->login.moverGC);
#ifndef NO_LOGO
	XtReleaseGC(gw, w->login.logoGC);
#endif
	XtReleaseGC(gw, w->login.userGC);
	XtReleaseGC(gw, w->login.pixmapGC);
	
	/* destroy the user's list */
	while (current_user) {
	    next_user = current_user->next;
	    free (current_user->name);
	    free (current_user);
	    current_user = next_user;
	}
    }
}

/* ARGSUSED */
static void Redisplay(gw, event, region)
     Widget gw;
     XEvent *event;
     Region region;
{
    draw_it ((LoginWidget) gw);
}

/*ARGSUSED*/
static Boolean SetValues (current, request, new, args, num_args)
    Widget  current, request, new;
    ArgList args;
    Cardinal *num_args;
{
    LoginWidget currentL, newL;
    
    currentL = (LoginWidget) current;
    newL = (LoginWidget) new;
    if (GREETING (currentL) != GREETING (newL))
	return True;
    return False;
}

static void UnselectUser (client_data, id)
    XtPointer	client_data;
    XtIntervalId *	id;
{
    LoginWidget lw = (LoginWidget)client_data;
    lw->login.user_selected = False;
}

/*ARGSUSED*/
static void
SelectUser (ctxw, event, params, num_params)
    Widget	ctxw;
    XEvent	*event;
    String	*params;
    Cardinal	*num_params;
{
    LoginWidget ctx = (LoginWidget)ctxw;
    int x, y, user;
    register int i;
    register struct xdm_user *current_user;

    if (ctx->login.display_users == False)
	return;

    if (ctx->login.state == GET_NAME) {
	/* # of user ? */
	x = event->xbutton.x;
	y = event->xbutton.y - USERS_Y(ctx);

	if (y >= 0 && y < (ctx->login.nb_lines * ctx->login.userHeight)) {
	    /* User Selection ? */
	    if (x < ctx->login.nbColumns * ctx->login.userWidth) {
		user = (ctx->login.nbColumns * (y / ctx->login.userHeight)) + (x / ctx->login.userWidth) + 1;
		current_user = ctx->login.first_user;
		while (user && current_user) {
		    if (current_user->displayed) {
			if (--user == 0)
			    break;
		    }
		    current_user = current_user->next;
		};

		if (current_user) {
		    if (ctx->login.user_selected == True) {
#ifdef USESHADOW
			struct spwd *sp;
#else
			struct passwd *pw;
#endif

			XorCursor (ctx);
			RemoveFail (ctx);

			/* No passwd ==> login ! */
#ifdef USESHADOW
			sp = getspnam (ctx->login.data.name);
			if (sp && *(sp->sp_pwdp) == 0) {
#else
			    pw = getpwnam (ctx->login.data.name);
			    if (pw && *(pw->pw_passwd) == 0) {
#endif
			    ctx->login.state = DONE;
			    ctx->login.cursor = 0;
			    (*ctx->login.notify_done) (ctx, &ctx->login.data, NOTIFY_OK);
			}
			else {
			    ctx->login.state = GET_PASSWD;
			    ctx->login.cursor = 0;
			    XorCursor (ctx);
			}
		    }
		    else {
			XorCursor (ctx);
			ResetLogin (ctx);
			RemoveFail (ctx);
			strcpy (ctx->login.data.name, current_user->name);
			DrawName (ctx, ctx->login.cursor);
			ctx->login.cursor = strlen (current_user->name);
			XorCursor (ctx);
			ctx->login.user_selected = True;
			XtAppAddTimeOut(XtWidgetToApplicationContext ((Widget)ctx), 500, UnselectUser, (XtPointer)ctx);
		    }
		}
	    }
	    else if ((ctx->login.use_scrollbar == True) && (x > MOVER_X(ctx) && x < (MOVER_X(ctx) + MOVER_WIDTH(ctx)))) {
		ctx->login.delta_y_mover = event->xbutton.y - ctx->login.y_mover;
		ctx->login.draw_mover = True;
		XtAddEventHandler ((Widget)ctx, ButtonReleaseMask, False, redraw_users, NULL);
	    }
	}
    }
}


char defaultLoginTranslations [] =
"\
<Btn1Down>:	select-user() \n\
Ctrl<Key>H:	delete-previous-character() \n\
Ctrl<Key>D:	delete-character() \n\
Ctrl<Key>B:	move-backward-character() \n\
Ctrl<Key>F:	move-forward-character() \n\
Ctrl<Key>A:	move-to-begining() \n\
Ctrl<Key>E:	move-to-end() \n\
Ctrl<Key>K:	erase-to-end-of-line() \n\
Ctrl<Key>U:	erase-line() \n\
Ctrl<Key>X:	erase-line() \n\
Ctrl<Key>C:	restart-session() \n\
Ctrl<Key>\\\\:	abort-session() \n\
:Ctrl<Key>plus:	allow-all-access() \n\
<Key>BackSpace:	delete-previous-character() \n\
<Key>Delete:	delete-previous-character() \n\
<Key>Return:	finish-field() \n\
<Key>:		insert-char() \
";

XtActionsRec loginActionsTable [] = {
  {"delete-previous-character",	DeleteBackwardChar},
  {"delete-character",		DeleteForwardChar},
  {"move-backward-character",	MoveBackwardChar},
  {"move-forward-character",	MoveForwardChar},
  {"move-to-begining",		MoveToBegining},
  {"move-to-end",		MoveToEnd},
  {"erase-to-end-of-line",	EraseToEndOfLine},
  {"erase-line",		EraseLine},
  {"finish-field", 		FinishField},
  {"abort-session",		AbortSession},
  {"abort-display",		AbortDisplay},
  {"restart-session",		RestartSession},
  {"insert-char", 		InsertChar},
  {"set-session-argument",	SetSessionArgument},
  {"allow-all-access",		AllowAccess},
  {"select-user",		SelectUser}
};

LoginClassRec loginClassRec = {
    { /* core fields */
    /* superclass		*/	&widgetClassRec,
    /* class_name		*/	"Login",
    /* size			*/	sizeof(LoginRec),
    /* class_initialize		*/	NULL,
    /* class_part_initialize	*/	NULL,
    /* class_inited		*/	FALSE,
    /* initialize		*/	Initialize,
    /* initialize_hook		*/	NULL,
    /* realize			*/	Realize,
    /* actions			*/	loginActionsTable,
    /* num_actions		*/	XtNumber (loginActionsTable),
    /* resources		*/	resources,
    /* num_resources		*/	XtNumber(resources),
    /* xrm_class		*/	NULLQUARK,
    /* compress_motion		*/	TRUE,
    /* compress_exposure	*/	TRUE,
    /* compress_enterleave	*/	TRUE,
    /* visible_interest		*/	FALSE,
    /* destroy			*/	Destroy,
    /* resize			*/	NULL,
    /* expose			*/	Redisplay,
    /* set_values		*/	SetValues,
    /* set_values_hook		*/	NULL,
    /* set_values_almost	*/	NULL,
    /* get_values_hook		*/	NULL,
    /* accept_focus		*/	NULL,
    /* version			*/	XtVersion,
    /* callback_private		*/	NULL,
    /* tm_table			*/	defaultLoginTranslations,
    /* query_geometry		*/	XtInheritQueryGeometry,
    /* display_accelerator	*/	XtInheritDisplayAccelerator,
    /* extension		*/	NULL
    }
};

WidgetClass loginWidgetClass = (WidgetClass) &loginClassRec;
