/* $XConsortium: sunKbd.c,v 5.20 92/11/30 21:09:51 rws Exp $ */
/*-
 * sunKbd.c --
 *	Functions for retrieving data from a keyboard.
 *
 * Copyright (c) 1987 by the Regents of the University of California
 *
 * 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.  The University of California
 * makes no representations about the suitability of this
 * software for any purpose.  It is provided "as is" without
 * express or implied warranty.
 *
 */

/************************************************************
Copyright 1987 by Sun Microsystems, Inc. Mountain View, CA.

                    All Rights Reserved

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 no-
tice  appear  in all copies and that both that copyright no-
tice and this permission notice appear in  supporting  docu-
mentation,  and  that the names of Sun or MIT not be used in
advertising or publicity pertaining to distribution  of  the
software  without specific prior written permission. Sun and
M.I.T. make no representations about the suitability of this
software for any purpose. It is provided "as is" without any
express or implied warranty.

SUN DISCLAIMS ALL WARRANTIES WITH REGARD TO  THIS  SOFTWARE,
INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FIT-
NESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL SUN BE  LI-
ABLE  FOR  ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,  DATA  OR
PROFITS,  WHETHER  IN  AN  ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION  WITH
THE USE OR PERFORMANCE OF THIS SOFTWARE.

********************************************************/

#define NEED_EVENTS
#include "sun.h"
#include <stdio.h>
#include "Xproto.h"
#include "keysym.h"
#include "inputstr.h"
#include <signal.h>
#include <sys/ioctl.h>
#ifdef SVR4
#include <sys/filio.h>
#include <sys/stropts.h>
#endif

typedef struct {
    int	    	  trans;          	/* Original translation form */
} SunKbPrivRec, *SunKbPrivPtr;

extern CARD8 *sunModMap[];
extern KeySymsRec sunKeySyms[];

extern void	ProcessInputEvents();
extern void	miPointerPosition();

static void 	  sunBell();
static void 	  sunKbdCtrl();
static Firm_event *sunKbdGetEvents();
static void 	  sunKbdEnqueueEvent();
int	  	  autoRepeatKeyDown = 0;
int	  	  autoRepeatReady;
long	  	  autoRepeatInitiate = 1000 * AUTOREPEAT_INITIATE;
long	  	  autoRepeatDelay = 1000 * AUTOREPEAT_DELAY;
static int	  autoRepeatFirst;
struct timeval    autoRepeatLastKeyDownTv;
struct timeval    autoRepeatDeltaTv;
static KeybdCtrl  sysKbCtrl;
static int	  lockKeyDown;

static SunKbPrivRec	sunKbPriv;  
static KbPrivRec  	sysKbPriv = {
    -1,				/* Type of keyboard */
    -1,				/* Descriptor open to device */
    sunKbdGetEvents,		/* Function to read events */
    sunKbdEnqueueEvent,		/* Function to enqueue an event */
    (pointer)&sunKbPriv,	/* Private to keyboard device */
    (Bool)0,			/* Mapped queue */
    0,				/* offset for device keycodes */
    &sysKbCtrl,			/* Initial full duration = .25 sec. */
    0,				/* lock state */
};

static void sunAlarmHandler() {}

/*-
 *-----------------------------------------------------------------------
 * sunKbdProc --
 *	Handle the initialization, etc. of a keyboard.
 *
 * Results:
 *	None.
 *
 * Side Effects:
 *
 * Note:
 *	When using sunwindows, all input comes off a single fd, stored in the
 *	global windowFd.  Therefore, only one device should be enabled and
 *	disabled, even though the application still sees both mouse and
 *	keyboard.  We have arbitrarily chosen to enable and disable windowFd
 *	in the keyboard routine sunKbdProc rather than in sunMouseProc.
 *
 *-----------------------------------------------------------------------
 */
int
sunKbdProc (pKeyboard, what)
    DevicePtr	  pKeyboard;	/* Keyboard to manipulate */
    int	    	  what;	    	/* What to do to it */
{
    KbPrivPtr	  pPriv;
    register int  kbdFd;
#ifdef	SUN_WINDOWS
#define	TR_UNDEFINED (TR_NONE-1)
    static int	  deviceOffKbdState = TR_UNDEFINED;
#endif /* SUN_WINDOWS */

    switch (what) {
	case DEVICE_INIT:
	    if (pKeyboard != LookupKeyboardDevice()) {
		ErrorF ("Cannot open non-system keyboard");
		return (!Success);
	    }
	    
	    /*
	     * First open and find the current state of the keyboard.
	     */
/*
 * The Sun 386i has system include files that preclude this pre SunOS 4.1
 * test for the presence of a type 4 keyboard however it really doesn't
 * matter since no 386i has ever been shipped with a type 3 keyboard.
 * SunOS 4.1 no longer needs this kludge.
 */
#if !defined(i386) && !defined(KIOCGKEY)
#define TYPE4KEYBOARDOVERRIDE
#endif
	    if (sysKbPriv.fd >= 0) {
		kbdFd = sysKbPriv.fd;
	    } else {
		kbdFd = open ("/dev/kbd", O_RDWR, 0);
		if (kbdFd < 0) {
		    Error ("Opening /dev/kbd");
		    return (!Success);
		}
		sysKbPriv.fd = kbdFd;
		(void) ioctl (kbdFd, KIOCTYPE, &sysKbPriv.type);
		(void) ioctl (kbdFd, KIOCGTRANS, &sunKbPriv.trans);
#ifdef TYPE4KEYBOARDOVERRIDE
                /*
                 * Magic. Look for a key which is non-existent on a real type
                 * 3 keyboard but does exist on a type 4 keyboard.
                 */
                if (sysKbPriv.type == KB_SUN3) {
                    struct kiockey key;

                    key.kio_tablemask = 0;
                    key.kio_station = 118;
                    if (ioctl(kbdFd, KIOCGETKEY, &key)) {
                        perror( "ioctl KIOCGETKEY" );
			FatalError("Can't KIOCGETKEY on fd %d\n", kbdFd);
                    }
                    if (key.kio_entry != HOLE)
                        sysKbPriv.type = KB_SUN4;
                }
#endif

		if (sysKbPriv.type < 0 || sysKbPriv.type > KB_SUN4
		    || sunKeySyms[sysKbPriv.type].map == NULL)
		    FatalError("Unsupported keyboard type %d\n", 
			sysKbPriv.type);
		if (sunUseSunWindows()) {
		    (void) close( kbdFd );
		    sysKbPriv.fd = -1;
		} else {
		    if (fcntl (kbdFd, F_SETFL, (FNDELAY|FASYNC)) < 0
#ifdef SVR4
			|| ioctl(kbdFd, I_SETSIG, S_RDNORM|S_RDBAND|S_HIPRI) < 0
#else
			|| fcntl(kbdFd, F_SETOWN, getpid()) < 0
#endif
			) {
			perror("sunKbdProc");
			FatalError("Can't set up kbd on fd %d\n", kbdFd);
		    }
		}
	    }

	    /*
	     * Perform final initialization of the system private keyboard
	     * structure and fill in various slots in the device record
	     * itself which couldn't be filled in before.
	     */
	    pKeyboard->devicePrivate = (pointer)&sysKbPriv;
	    pKeyboard->on = FALSE;
	    sysKbCtrl = defaultKeyboardControl;
	    sysKbPriv.ctrl = &sysKbCtrl;
	    autoRepeatKeyDown = 0;

	    /*
	     * ensure that the keycodes on the wire are >= MIN_KEYCODE
	     */
	    if (sunKeySyms[sysKbPriv.type].minKeyCode < MIN_KEYCODE) {
		int offset = MIN_KEYCODE -sunKeySyms[sysKbPriv.type].minKeyCode;

		sunKeySyms[sysKbPriv.type].minKeyCode += offset;
		sunKeySyms[sysKbPriv.type].maxKeyCode += offset;
		sysKbPriv.offset = offset;
	    }
	    InitKeyboardDeviceStruct(
		    pKeyboard,
		    &(sunKeySyms[sysKbPriv.type]),
		    (sunModMap[sysKbPriv.type]),
		    sunBell,
		    sunKbdCtrl);
	    break;

	case DEVICE_ON:
	    if (sunUseSunWindows()) {
#ifdef SUN_WINDOWS
		if (! sunSetUpKbdSunWin(windowFd, TRUE)) {
		    FatalError("Can't set up keyboard\n");
		}
		/*
		 * Don't tamper with keyboard translation here
		 * unless this is a server reset.  If server
		 * is reset then set translation to that saved
		 * when DEVICE_CLOSE was executed.
		 * Translation is set/reset upon receipt of
		 * KBD_USE/KBD_DONE input events (sunIo.c)
		 */
		if (deviceOffKbdState != TR_UNDEFINED) {
		    if (sunChangeKbdTranslation(pKeyboard,
				deviceOffKbdState == TR_UNTRANS_EVENT) < 0) {
			FatalError("Can't set (SW) keyboard translation\n");
		    }
		}
		AddEnabledDevice(windowFd);
#endif /* SUN_WINDOWS */
	    }
	    else {
		pPriv = (KbPrivPtr)pKeyboard->devicePrivate;
		kbdFd = pPriv->fd;

	        /*
	         * Set the keyboard into "direct" mode and turn on
	         * event translation.
	         */
		if (sunChangeKbdTranslation(pKeyboard,TRUE) < 0) {
		    FatalError("Can't set keyboard translation\n");
		}

		AddEnabledDevice(kbdFd);
	    }
	    pKeyboard->on = TRUE;
	    break;

	case DEVICE_CLOSE:
	case DEVICE_OFF:
	    if (sunUseSunWindows()) {
#ifdef SUN_WINDOWS
		/*
		 * Save current translation state in case of server
		 * reset.  Used above when DEVICE_ON is executed.
		 */
		if ((kbdFd = open("/dev/kbd", O_RDONLY, 0)) < 0) {
		    Error("DEVICE_OFF: Can't open kbd\n");
		    goto badkbd;
		}
		if (ioctl(kbdFd, KIOCGTRANS, &deviceOffKbdState) < 0) {
		    Error("Can't save keyboard state\n");
		}
		(void) close(kbdFd);

badkbd:
		if (! sunSetUpKbdSunWin(windowFd, FALSE)) {
		    FatalError("Can't close keyboard\n");
		}

		/*
		 * Restore SunWindows translation.
		 */
		if (sunChangeKbdTranslation(pKeyboard,FALSE) < 0) {
		    FatalError("Can't reset keyboard translation\n");
		}

		RemoveEnabledDevice(windowFd);
#endif /* SUN_WINDOWS */
	    }
	    else {
		pPriv = (KbPrivPtr)pKeyboard->devicePrivate;
		kbdFd = pPriv->fd;

	        /*
	         * Restore original keyboard directness and translation.
	         */
		if (sunChangeKbdTranslation(pKeyboard,FALSE) < 0) {
		    FatalError("Can't reset keyboard translation\n");
		}

		RemoveEnabledDevice(kbdFd);
	    }
	    pKeyboard->on = FALSE;
	    break;
    }
    return (Success);
}

/*-
 *-----------------------------------------------------------------------
 * sunBell --
 *	Ring the terminal/keyboard bell
 *
 * Results:
 *	Ring the keyboard bell for an amount of time proportional to
 *	"loudness."
 *
 * Side Effects:
 *	None, really...
 *
 *-----------------------------------------------------------------------
 */
static void
sunBell (loudness, pKeyboard)
    int	    	  loudness;	    /* Percentage of full volume */
    DevicePtr	  pKeyboard;	    /* Keyboard to ring */
{
    KbPrivPtr	  pPriv = (KbPrivPtr) pKeyboard->devicePrivate;
    int	  	  kbdCmd;   	    /* Command to give keyboard */
    int	 	  kbdOpenedHere; 
 
    if (loudness == 0) {
 	return;
    }

    kbdOpenedHere = ( pPriv->fd < 0 );
    if ( kbdOpenedHere ) {
	pPriv->fd = open("/dev/kbd", O_RDWR, 0);
	if (pPriv->fd < 0) {
	    ErrorF("sunBell: can't open keyboard");
	    return;
	}
    }	
 
    kbdCmd = KBD_CMD_BELL;
    if (ioctl (pPriv->fd, KIOCCMD, &kbdCmd) < 0) {
 	ErrorF ("Failed to activate bell");
	goto bad;
    }
 
    /*
     * Leave the bell on for a while == duration (ms) proportional to
     * loudness desired with a 10 thrown in to convert from ms to usecs.
     */
#ifdef SVR4
    {
	struct itimerval ival;
	sigset_t nmask, omask;

	Signal(SIGALRM, sunAlarmHandler);
	ival.it_interval.tv_sec = 0;
	ival.it_interval.tv_usec = 0;
	ival.it_value.tv_sec = 0;
	ival.it_value.tv_usec = pPriv->ctrl->bell_duration * 1000;
	if (ival.it_value.tv_usec >= 1000000)
	{
	    ival.it_value.tv_sec += ival.it_value.tv_usec / 1000000;
	    ival.it_value.tv_usec %= 1000000;
	}
	sigemptyset(&nmask);
	sigaddset(&nmask, SIGALRM);
	sigaddset(&nmask, SIGPOLL);
	sigprocmask(SIG_BLOCK, &nmask, &omask);
	nmask = omask;
	sigaddset(&nmask, SIGPOLL);
	setitimer(ITIMER_REAL, &ival, NULL);
	sigsuspend(&nmask);
	ival.it_value.tv_sec = 0;
	ival.it_value.tv_usec = 0;
	setitimer(ITIMER_REAL, &ival, NULL);
	sigprocmask(SIG_SETMASK, &omask, NULL);
    }
#else
    usleep (pPriv->ctrl->bell_duration * 1000);
#endif
 
    kbdCmd = KBD_CMD_NOBELL;
    if (ioctl (pPriv->fd, KIOCCMD, &kbdCmd) < 0) {
	ErrorF ("Failed to deactivate bell");
	goto bad;
    }

bad:
    if ( kbdOpenedHere ) {
	(void) close(pPriv->fd);
	pPriv->fd = -1;
    }
}

/*
 * The LEDs on the type-4 keyboard are in a *strange*
 * order.  This code remaps them left-to-right, which
 * may not be what you want, but it seems reasonable to me.
 */

#define LED_LOCK    0x08
#define LED_1	    0x02
#define LED_2	    0x04
#define LED_3	    0x01
#define LED_X	    (LED_1 | LED_2 | LED_3)
#define LED_ALL	    (LED_LOCK | LED_X)

sunKbdSetLights (pKeyboard)
    DevicePtr	pKeyboard;
{
    KbPrivPtr	pPriv = (KbPrivPtr) pKeyboard->devicePrivate;
    char	device, request;

    request = pPriv->ctrl->leds;
    device = 0;
    if (request & 0x01)
	device |= LED_1;
    if (request & 0x02)
	device |= LED_2;
    if (request & 0x04)
	device |= LED_3;
    if (pPriv->lockLight)
	device |= LED_LOCK;
#ifdef KIOCSLED
    if (ioctl (pPriv->fd, KIOCSLED, &device) == -1)
	ErrorF("Failed to set keyboard lights");
#endif
}

/*-
 *-----------------------------------------------------------------------
 * sunKbdCtrl --
 *	Alter some of the keyboard control parameters
 *
 * Results:
 *	None.
 *
 * Side Effects:
 *	Some...
 *
 *-----------------------------------------------------------------------
 */

static void
sunKbdCtrl (pKeyboard, ctrl)
    DevicePtr	  pKeyboard;	    /* Keyboard to alter */
    KeybdCtrl     *ctrl;
{
    KbPrivPtr	  pPriv = (KbPrivPtr) pKeyboard->devicePrivate;
    int	 	  kbdOpenedHere; 
    char	  led;
    int		  i;

    kbdOpenedHere = ( pPriv->fd < 0 );
    if ( kbdOpenedHere ) {
	pPriv->fd = open("/dev/kbd", O_WRONLY, 0);
	if (pPriv->fd < 0) {
	    ErrorF("sunKbdCtrl: can't open keyboard");
	    return;
	}
    }

    if (ctrl->click != pPriv->ctrl->click)
    {
    	int kbdClickCmd;

	pPriv->ctrl->click = ctrl->click;
	kbdClickCmd = pPriv->ctrl->click ? KBD_CMD_CLICK : KBD_CMD_NOCLICK;
    	if (ioctl (pPriv->fd, KIOCCMD, &kbdClickCmd) < 0)
 	    ErrorF("Failed to set keyclick");
    }
 
    if (ctrl->leds != pPriv->ctrl->leds)
    {
	pPriv->ctrl->leds = ctrl->leds & LED_X;
	sunKbdSetLights (pKeyboard);
    }

    pPriv->ctrl->bell = ctrl->bell;
    pPriv->ctrl->bell_pitch = ctrl->bell_pitch;
    pPriv->ctrl->bell_duration = ctrl->bell_duration;
    pPriv->ctrl->autoRepeat = ctrl->autoRepeat;
    for (i = 0; i < sizeof ctrl->autoRepeats / sizeof ctrl->autoRepeats[0]; i++)
	pPriv->ctrl->autoRepeats[i] = ctrl->autoRepeats[i];

    if ( kbdOpenedHere ) {
	(void) close(pPriv->fd);
	pPriv->fd = -1;
    }
}

sunKbdLockLight (pKeyboard, on)
    DevicePtr	pKeyboard;
    Bool	on;
{
    KbPrivPtr	pPriv = (KbPrivPtr) pKeyboard->devicePrivate;
    KeybdCtrl	ctrl;

    pPriv->lockLight = on;
    sunKbdSetLights (pKeyboard);
}

/*-
 *-----------------------------------------------------------------------
 * sunKbdGetEvents --
 *	Return the events waiting in the wings for the given keyboard.
 *
 * Results:
 *	A pointer to an array of Firm_events or (Firm_event *)0 if no events
 *	The number of events contained in the array.
 *	A boolean as to whether more events might be available.
 *
 * Side Effects:
 *	None.
 *-----------------------------------------------------------------------
 */
static Firm_event *
sunKbdGetEvents (pKeyboard, pNumEvents, pAgain)
    DevicePtr	  pKeyboard;	    /* Keyboard to read */
    int	    	  *pNumEvents;	    /* Place to return number of events */
    Bool	  *pAgain;	    /* whether more might be available */
{
    int	    	  nBytes;	    /* number of bytes of events available. */
    KbPrivPtr	  pPriv;
    static Firm_event	evBuf[MAXEVENTS];   /* Buffer for Firm_events */

    pPriv = (KbPrivPtr) pKeyboard->devicePrivate;
    nBytes = read (pPriv->fd, evBuf, sizeof(evBuf));

    if (nBytes < 0) {
	if (errno == EWOULDBLOCK) {
	    *pNumEvents = 0;
	    *pAgain = FALSE;
	} else {
	    Error ("Reading keyboard");
	    FatalError ("Could not read the keyboard");
	}
    } else {
	*pNumEvents = nBytes / sizeof (Firm_event);
	*pAgain = (nBytes == sizeof (evBuf));
    }
    return (evBuf);
}

/*-
 *-----------------------------------------------------------------------
 * sunKbdEnqueueEvent --
 *
 * Results:
 *
 * Side Effects:
 *
 * Caveat:
 *      To reduce duplication of code and logic (and therefore bugs), the
 *      sunwindows version of kbd processing (sunKbdEnqueueEventSunWin())
 *      counterfeits a firm event and calls this routine.  This
 *      couunterfeiting relies on the fact this this routine only looks at the
 *      id, time, and value fields of the firm event which it is passed.  If
 *      this ever changes, the sunKbdEnqueueEventSunWin will also have to
 *      change.
 *
 *-----------------------------------------------------------------------
 */

static xEvent	autoRepeatEvent;

static void
sunKbdEnqueueEvent (pKeyboard, fe)
    DevicePtr	  pKeyboard;
    Firm_event	  *fe;
{
    xEvent		xE;
    KbPrivPtr		pPriv;
    int			delta;
    BYTE		key;
    CARD8		keyModifiers;

    key = (fe->id & 0x7F) + sysKbPriv.offset;
    keyModifiers = ((DeviceIntPtr)pKeyboard)->key->modifierMap[key];
    if (autoRepeatKeyDown && (keyModifiers == 0) &&
	((fe->value == VKEY_DOWN) || (key == autoRepeatEvent.u.u.detail))) {
	/*
	 * Kill AutoRepeater on any real non-modifier key down, or auto key up
	 */
	autoRepeatKeyDown = 0;
    }

    xE.u.keyButtonPointer.time = TVTOMILLI(fe->time);
    xE.u.u.type = ((fe->value == VKEY_UP) ? KeyRelease : KeyPress);
    xE.u.u.detail = key;

    if (keyModifiers & LockMask) {
	if (xE.u.u.type == KeyRelease)
	    return; /* this assumes autorepeat is not desired */
	if (lockKeyDown) {
	    xE.u.u.type = KeyRelease;
	    xE.u.u.detail = lockKeyDown;
	    lockKeyDown = 0;
	}
 	else
	    lockKeyDown = key;
	sunKbdLockLight (pKeyboard, xE.u.u.type == KeyPress);
    }

    if ((xE.u.u.type == KeyPress) && (keyModifiers == 0)) {
	/* initialize new AutoRepeater event & mark AutoRepeater on */
	autoRepeatEvent = xE;
	autoRepeatFirst = TRUE;
	autoRepeatKeyDown++;
	autoRepeatLastKeyDownTv = fe->time;
    }

    mieqEnqueue (&xE);
}

sunEnqueueAutoRepeat ()
{
#ifdef SVR4
    sigset_t newmask, oldmask;
#else
    int	oldmask;
#endif
    int	delta;

    if (sysKbPriv.ctrl->autoRepeat != AutoRepeatModeOn) {
	autoRepeatKeyDown = 0;
	return;
    }
    /*
     * Generate auto repeat event.	XXX one for now.
     * Update time & pointer location of saved KeyPress event.
     */

    delta = TVTOMILLI(autoRepeatDeltaTv);
    autoRepeatFirst = FALSE;

    /*
     * Fake a key up event and a key down event
     * for the last key pressed.
     */
    autoRepeatEvent.u.keyButtonPointer.time += delta;
    autoRepeatEvent.u.u.type = KeyRelease;
#ifdef SVR4
    sigemptyset(&newmask);
    sigaddset(&newmask, SIGPOLL);
    sigprocmask(SIG_BLOCK, &newmask, &oldmask);
#else
    oldmask = sigblock (sigmask(SIGIO));
#endif
    mieqEnqueue (&autoRepeatEvent);
    autoRepeatEvent.u.u.type = KeyPress;
    mieqEnqueue (&autoRepeatEvent);
#ifdef SVR4
    sigprocmask(SIG_SETMASK, &oldmask, NULL);
#else
    sigsetmask (oldmask);
#endif
    /* Update time of last key down */
    tvplus(autoRepeatLastKeyDownTv, autoRepeatLastKeyDownTv, 
		    autoRepeatDeltaTv);

}

/*-
 *-----------------------------------------------------------------------
 * sunChangeKbdTranslation
 *	Makes operating system calls to set keyboard translation 
 *	and direction on or off.
 *
 * Results:
 *	-1 if failure, else 0.
 *
 * Side Effects:
 * 	Changes kernel management of keyboard.
 *
 *-----------------------------------------------------------------------
 */
int
sunChangeKbdTranslation(pKeyboard,makeTranslated)
    DevicePtr pKeyboard;
    Bool makeTranslated;
{   
    KbPrivPtr	pPriv;
    int 	kbdFd;
    int 	tmp;
    int		kbdOpenedHere;
#ifdef SVR4
    sigset_t	new_mask, old_mask;
#else
    int		old_mask;
#endif
    int		toread;
    char	junk[8192];

    static struct timeval lastChngKbdTransTv;
    struct timeval tv;
    struct timeval lastChngKbdDeltaTv;
    int lastChngKbdDelta;

    /*
     * Workaround for SS1 serial driver kernel bug when KIOCTRANS ioctl()s
     * occur too closely together in time.
     */
#ifdef SVR4
    sigfillset(&new_mask);
    sigprocmask(SIG_BLOCK, &new_mask, &old_mask);
#else
    old_mask = sigblock (~0);
#endif
    gettimeofday(&tv, (struct timezone *) NULL);
    tvminus(lastChngKbdDeltaTv, tv, lastChngKbdTransTv);
    lastChngKbdDelta = TVTOMILLI(lastChngKbdDeltaTv);
    if (lastChngKbdDelta < 750) {
	struct timeval wait;

	/*
         * We need to guarantee at least 750 milliseconds between
	 * calls to KIOCTRANS. YUCK!
	 */
	wait.tv_sec = 0;
	wait.tv_usec = (750L - lastChngKbdDelta) * 1000L;
        (void) select(0, (int *)0, (int *)0, (int *)0, &wait);
        gettimeofday(&tv, (struct timezone *) NULL);
    }
    lastChngKbdTransTv = tv;

    kbdFd = -1;
    if (pKeyboard)
    {
    	pPriv = (KbPrivPtr)pKeyboard->devicePrivate;
	if (pPriv)
	    kbdFd = pPriv->fd;
    }

    kbdOpenedHere = ( kbdFd < 0 );
    if ( kbdOpenedHere ) {
	kbdFd = open("/dev/kbd", O_RDONLY, 0);
	if ( kbdFd < 0 ) {
	    Error( "sunChangeKbdTranslation: Can't open keyboard" );
	    goto bad;
	}
    }
	
    if (makeTranslated) {
        /*
         * Next set the keyboard into "direct" mode and turn on
         * event translation. If either of these fails, we can't go
         * on.
         */
	if ( ! sunUseSunWindows() ) {
	    tmp = 1;
	    if (ioctl (kbdFd, KIOCSDIRECT, &tmp) < 0) {
		Error ("Setting keyboard direct mode");
		goto bad;
	    }
	}
	tmp = TR_UNTRANS_EVENT;
	if (ioctl (kbdFd, KIOCTRANS, &tmp) < 0) {
	    ErrorF("sunChangeKbdTranslation: kbdFd=%d\n",kbdFd);
	    Error ("Setting keyboard translation");
	    goto bad;
	}
    }
    else {
        /*
         * Next set the keyboard into "indirect" mode and turn off
         * event translation.
         */
	if ( ! sunUseSunWindows() ) {
	    tmp = 0;
	    (void)ioctl (kbdFd, KIOCSDIRECT, &tmp);
	    tmp = TR_ASCII;
	}
	else if (pKeyboard && pPriv && pPriv->devPrivate)
	    tmp = ((SunKbPrivPtr)pPriv->devPrivate)->trans;
	else
	    tmp = TR_ASCII;
	(void)ioctl (kbdFd, KIOCTRANS, &tmp);
    }

    if (ioctl (kbdFd, FIONREAD, &toread) != -1 && toread > 0) {
	while (toread) {
	    tmp = toread;
	    if (toread > sizeof (junk))
		tmp = sizeof (junk);
	    (void) read (kbdFd, junk, tmp);
	    toread -= tmp;
	}
    }
    if ( kbdOpenedHere )
	(void) close( kbdFd );
#ifdef SVR4
    sigprocmask(SIG_SETMASK, &old_mask, NULL);
#else
    sigsetmask (old_mask);
#endif
    return(0);

bad:
    if ( kbdOpenedHere && kbdFd >= 0 )
	(void) close( kbdFd );
#ifdef SVR4
    sigprocmask(SIG_SETMASK, &old_mask, NULL);
#else
    sigsetmask (old_mask);
#endif
    return(-1);
}

#ifdef SUN_WINDOWS

/*-
 *-----------------------------------------------------------------------
 * sunSetUpKbdSunWin
 *	Change which events the kernel will pass through as keyboard
 * 	events.
 *
 *	Does NOT affect keyboard translation.
 *
 * Results:
 *	Inputevent mask modified.
 *
 * Side Effects:
 *
 *-----------------------------------------------------------------------
 */

Bool
sunSetUpKbdSunWin(windowFd, onoff)
    int windowFd;
    Bool onoff;
{
    struct inputmask inputMask;
    static struct inputmask oldInputMask;

    if (onoff) {
        register int i;

	win_get_kbd_mask(windowFd, &oldInputMask);
	input_imnull(&inputMask);
	inputMask.im_flags |= 
		IM_ASCII | IM_NEGASCII | 
		IM_META | IM_NEGMETA | 
		IM_NEGEVENT | IM_INTRANSIT;
	win_setinputcodebit(&inputMask, KBD_USE);
	win_setinputcodebit(&inputMask, KBD_DONE);
	win_setinputcodebit(&inputMask, SHIFT_CAPSLOCK);
	win_setinputcodebit(&inputMask, SHIFT_LOCK);
	win_setinputcodebit(&inputMask, SHIFT_LEFT);
	win_setinputcodebit(&inputMask, SHIFT_RIGHT);
	win_setinputcodebit(&inputMask, SHIFT_LEFTCTRL);
	win_setinputcodebit(&inputMask, SHIFT_RIGHTCTRL);
	win_setinputcodebit(&inputMask, SHIFT_META);
	win_setinputcodebit(&inputMask, WIN_STOP);

        for (i=KEY_LEFTFIRST; i<=KEY_LEFTLAST; i++) {
            win_setinputcodebit(&inputMask, i);
        }
        for (i=KEY_TOPFIRST; i<=KEY_TOPLAST; i++) {
            win_setinputcodebit(&inputMask, i);
        }
        for (i=KEY_RIGHTFIRST; i<=KEY_RIGHTLAST; i++) {
            win_setinputcodebit(&inputMask, i);
        }

	win_set_kbd_mask(windowFd, &inputMask);
    }
    else {
	win_set_kbd_mask(windowFd, &oldInputMask);
    }
    return (TRUE);
}

#endif /* SUN_WINDOWS */


#ifdef SUN_WINDOWS

/*-
 *-----------------------------------------------------------------------
 * sunKbdEnqueueEventSunWin
 *	Process sunwindows event destined for the keyboard.
 *      Rather than replicate the logic (and therefore replicate
 * 	bug fixes, etc), this code counterfeits a vuid 
 *	Firm_event and then uses the non-sunwindows code.
 * 	
 * Results:
 *
 * Side Effects:
 *
 *-----------------------------------------------------------------------
 */

void
sunKbdEnqueueEventSunWin(pKeyboard,se)
    DeviceRec *pKeyboard;
    register struct inputevent *se;
{   
    Firm_event	fe;

    fe.time = event_time(se);
    fe.id = event_id(se);
    fe.value = (event_is_up(se) ? VKEY_UP : VKEY_DOWN);

    sunKbdEnqueueEvent (pKeyboard, &fe);
}
#endif /* SUN_WINDOWS */

/*ARGSUSED*/
Bool
LegalModifier(key, pDev)
    BYTE    key;
    DevicePtr	pDev;
{
    return (TRUE);
}

static KeybdCtrl *pKbdCtrl = (KeybdCtrl *) 0;

/*ARGSUSED*/
void
sunBlockHandler(nscreen, pbdata, pptv, pReadmask)
    int nscreen;
    pointer pbdata;
    struct timeval **pptv;
    pointer pReadmask;
{
    static struct timeval artv = { 0, 0 };	/* autorepeat timeval */

    if (!autoRepeatKeyDown)
	return;

    if (pKbdCtrl == (KeybdCtrl *) 0)
	pKbdCtrl = ((KbPrivPtr) LookupKeyboardDevice()->devicePrivate)->ctrl;

    if (pKbdCtrl->autoRepeat != AutoRepeatModeOn)
	return;

    if (autoRepeatFirst == TRUE)
	artv.tv_usec = autoRepeatInitiate;
    else
	artv.tv_usec = autoRepeatDelay;
    *pptv = &artv;

}

/*ARGSUSED*/
void
sunWakeupHandler(nscreen, pbdata, err, pReadmask)
    int nscreen;
    pointer pbdata;
    unsigned long err;
    pointer pReadmask;
{
    struct timeval tv;

    if (pKbdCtrl == (KeybdCtrl *) 0)
	pKbdCtrl = ((KbPrivPtr) LookupKeyboardDevice()->devicePrivate)->ctrl;

    if (pKbdCtrl->autoRepeat != AutoRepeatModeOn)
	return;

    if (autoRepeatKeyDown) {
	gettimeofday(&tv, (struct timezone *) NULL);
	tvminus(autoRepeatDeltaTv, tv, autoRepeatLastKeyDownTv);
	if (autoRepeatDeltaTv.tv_sec > 0 ||
			(!autoRepeatFirst && autoRepeatDeltaTv.tv_usec >
				autoRepeatDelay) ||
			(autoRepeatDeltaTv.tv_usec >
				autoRepeatInitiate))
		autoRepeatReady++;
    }
    
    if (autoRepeatReady)
    {
	sunEnqueueAutoRepeat ();
	autoRepeatReady = 0;
    }
}
