/********************************************************************
*   DrvKeyboard.c
*
*   Macintosh driver keyboard subsystem implementation.
*
*   Copyright (c) 1995-1997, Willows Software Inc.  All rights reserved.  
********************************************************************/

#include <string.h>
#include <DeskBus.h>

#include "DrvHook.h"
#include "Log.h"
#include "DrvDP.h"
#include "DrvKeyboard.h"
#include "DrvMenus.h"
#include "DrvSystem.h"
#include "DrvGlobals.h"
#include "DrvEvents.h"

/* static data */
/* gKeyMap will hold the current key states */
static KEYSTATE gKeyMap[256];				/* Indexed by Windows VK */
static KEYSTATE gKeyTranslate[256];		/* Indexed by mac VK */

/* this maps Apple KeyCodes to Windows VK keys */
/* it is built by reverse engineering the  */
/* KeySyms to KeyCodes, and then setting   */
/* the VK key of the keycode, we do allow  */
/* more than one KeyCode to generate a VK  */
/* key, primarily for control and shifts.  */

static KEYTABLE VK2KeySym[] = {
	VK_NUMLOCK, 	AK_Num_Lock, 		"num lock",
	VK_BACK,			AK_BackSpace, 	"backspace",
	VK_TAB,			AK_Tab,				"tab",
	VK_CLEAR, 		AK_Clear,				"clear",
	VK_RETURN, 	AK_Return,			"return",
	VK_RETURN, 	AK_KP_Enter,		"enter",
	VK_SHIFT, 		AK_Shift_L,			"shift left",
	VK_SHIFT, 		AK_Shift_R,			"shift right",
	VK_CONTROL, 	AK_Control_L,		"control left",
	VK_CONTROL, 	AK_Control_R,		"control right",
	VK_MENU, 		AK_Menu,				"menu",

	VK_PAUSE, 		AK_Pause,			"pause",
	VK_CAPITAL, 	AK_Caps_Lock,		"caps lock",

	VK_SPACE, 		AK_space,			"space",
	VK_PRIOR, 		AK_Prior,				"prior",
	VK_NEXT, 		AK_Next,				"next",
	VK_END, 			AK_End,				"end",
	VK_HOME, 		AK_Home,				"home",

	VK_LEFT, 			AK_Left,				"left",
	VK_UP, 			AK_Up,					"up",	
	VK_RIGHT, 		AK_Right,				"right",	
	VK_DOWN, 		AK_Down,				"down",
	VK_SELECT, 		AK_Select,			"select",

	VK_ALT,    		AK_Alt_R,			"alt right",
	VK_ALT,    		AK_Alt_L,			"alt left",
	VK_CANCEL, 	AK_Break,			"cancel",

	VK_EXECUTE, 	AK_Execute,			"execute",	

	VK_INSERT, 		AK_Insert,			"insert",
	VK_DELETE, 		AK_Delete,			"delete",
	VK_HELP, 			AK_Help,				"help",	

	VK_0, AK_0,	"0",
	VK_1, AK_1,	"1",
	VK_2, AK_2,	"2",
	VK_3, AK_3,	"3",
	VK_4, AK_4,	"4",
	VK_5, AK_5,	"5",
	VK_6, AK_6,	"6",
	VK_7, AK_7,	"7",
	VK_8, AK_8,	"8",
	VK_9, AK_9,	"9",

/* strictly speaking not needed, only one case will be used */
	VK_A, AK_a,	"a",
	VK_B, AK_b,		"b",
	VK_C, AK_c,		"c",
	VK_D, AK_d,		"d",
	VK_E, AK_e,		"e",
	VK_F, AK_f,		"f",
	VK_G, AK_g,		"g",
	VK_H, AK_h,		"h",
	VK_I, AK_i,		"i",
	VK_J, AK_j,		"j",
	VK_K, AK_k,		"k",
	VK_L, AK_l,		"l",
	VK_M, AK_m,	"m",
	VK_N, AK_n,		"n",
	VK_O, AK_o,		"o",
	VK_P, AK_p,		"p",
	VK_Q, AK_q,		"q",
	VK_R, AK_r,		"r",
	VK_S, AK_s,		"s",
	VK_T, AK_t,		"t",
	VK_U, AK_u,		"u",
	VK_V, AK_v,	"v",
	VK_W, AK_w,	"w",
	VK_X, AK_x,	"x",
	VK_Y, AK_y,	"y",
	VK_Z, AK_z,		"z",

	VK_NUMPAD0, AK_KP_0,		"num pad 0",
	VK_NUMPAD1, AK_KP_1,		"num pad 1",
	VK_NUMPAD2, AK_KP_2,		"num pad 2",
	VK_NUMPAD3, AK_KP_3,		"num pad 3",
	VK_NUMPAD4, AK_KP_4,		"num pad 4",
	VK_NUMPAD5, AK_KP_5,		"num pad 5",
	VK_NUMPAD6, AK_KP_6,		"num pad 6",
	VK_NUMPAD7, AK_KP_7,		"num pad 7",
	VK_NUMPAD8, AK_KP_8,		"num pad 8",
	VK_NUMPAD9, AK_KP_9,		"num pad 9",

	VK_MULTIPLY, 		AK_KP_Multiply,		"multiply",
	VK_ADD, 				AK_KP_Add,				"add",
	VK_SEPARATOR, 	AK_KP_Separator,	"seperator",
	VK_SUBTRACT, 	AK_KP_Subtract,		"subtract",
	VK_DECIMAL, 		AK_KP_Decimal,		"decimal",	
	VK_DIVIDE, 			AK_KP_Divide,			"divide",	

	VK_OEM_SCROLL, AK_Scroll_Lock, 		"scroll lock",	
	VK_SNAPSHOT,		AK_Print,					"print",
	
	VK_F1, AK_F1,		"F1",
	VK_F2, AK_F2,		"F2",
	VK_F3, AK_F3,		"F3",
	VK_F4, AK_F4,		"F4",
	VK_F5, AK_F5,		"F5",
	VK_F6, AK_F6,		"F6",
	VK_F7, AK_F7,		"F7",
	VK_F8, AK_F8,		"F8",
	VK_F9, AK_F9,		"F9",
	VK_F10, AK_F10,	"F10",
	VK_F11, AK_F11,	"F11",
	VK_F12, AK_F12,	"F12",
	VK_F13, AK_F13,	"F13",
	VK_F14, AK_F14,	"F14",
	VK_F15, AK_F15,	"F15",
	VK_F16, AK_F16,	"F16",

	VK_OEM_1, AK_semicolon,		"semi-colon",
	VK_OEM_PLUS, AK_equal,			"equal",
	VK_OEM_COMMA, AK_comma,	"comma",
	VK_OEM_MINUS, AK_minus,		"minus",
	VK_OEM_PERIOD, AK_period,	"period",
	VK_OEM_2, AK_slash,				"slash",

	VK_OEM_3, AK_quoteleft,			"quote left",

	VK_OEM_4, AK_bracketleft,		"bracket left",
	VK_OEM_5, AK_backslash,		"backslash",
	VK_OEM_6, AK_bracketright,	"bracket right",
	VK_OEM_7, AK_quoteright,		"quote right",

	VK_CANCEL,AK_Cancel,			"cancel",
	VK_ESCAPE, AK_Escape,			"escape",

	0,0, ""
};

/* Local Procedures */
static BOOL DoCmdKey(EventRecord *event);
static DWORD DrvInitKeyboard(void);
static DWORD DrvHandleKeyboardEvents(EventRecord *event);
static DWORD DrvGetKeyboardType(int);
static DWORD DrvGetKBCodePage(void);
static DWORD DrvKeyboardState(BOOL, LPBYTE);
static DWORD DrvKeyState(int, BOOL);
static DWORD DrvButtonState(LPMSG);
static DWORD DrvGetKeyNameText(LONG, LPSTR, int);
static DWORD DrvMapVirtualKey(UINT, UINT);
static DWORD DrvGetChar(UINT);
static DWORD DrvBuildKeyMessage(EventRecord *event, LPMSG lpMsg);

static void SetModifierState(short modifiers);
static void SetKeyState(short vk, short message, short keybdID);
static void MapNumPad(void);
static UINT KeysymToVKcode(UINT);
static UINT VKcodeToKeysym(UINT);

/* ADB procedures */
static short GetLEDValue(ADBAddress addr, int *curLEDValue);
static short SetLEDValue(ADBAddress addr, int newLEDValue);


/* LED Toggle defs, for NumLockLED call */
#define LED_ON 	TRUE
#define LED_OFF 	FALSE
#define ADB_TIMEOUT	120			/* Two seconds should be plenty */
#define kNumLockLED	0x1			/* Num lock is bit 1 */
#define kListenMask		8				/* Masks for ADB commands */
#define kTalkMask		12
#define kLEDRegister	2				/* register containing LED settings */
#define kLEDValueMask	7				/* mask for bits containing current LED setting */


static BOOL gNumLockLatched 	= TRUE;
static BOOL gKeyboardDirty 	= TRUE;			/* Triggers verification of the keyboard */
static char gADBDone;
static unsigned char gADBRegisterData[8];



/********************************************************************
*   PrivateKeyboardHook
*
*	Subsystem message dispatcher.
********************************************************************/
DWORD PrivateKeyboardHook(WORD wFunc, LPARAM dwParam1,	LPARAM dwParam2, LPVOID lpStruct)
{
    switch (wFunc) {
    /* (dwParm)?INITIALIZE:DEINITIALIZE */
	case DSUBSYSTEM_INIT:
	    if (dwParam2)
		    return DrvInitKeyboard();
		else
			return TRUE;
		
	case DSUBSYSTEM_EVENTS:
	    return DrvHandleKeyboardEvents(lpStruct);

	case DSUBSYSTEM_GETCAPS:
	    return 0L;

	/* dwParam1 - type of info to retrieve */
	case PKH_GETTYPE:
	    return DrvGetKeyboardType((int)dwParam1);

	case PKH_CODEPAGE:
	    return DrvGetKBCodePage();

	/* dwParam1 - get/set modifier */
	/* lpStruct - address of the byte array of key states to get/set */
	case PKH_KBSTATE:
	    return DrvKeyboardState((BOOL)dwParam1, (LPBYTE)lpStruct);

	/* dwParam1 - virtual key */
	/* dwParam2 - sync/async modifier */
	case PKH_KEYSTATE:
	    return DrvKeyState((int)dwParam1, (BOOL)dwParam2);

	/* lpStruct - address of the message structure */
	case PKH_BTNSTATE:
	    return DrvButtonState((LPMSG)lpStruct);

	/* dwParam1 - lParam of keyboard message */
	/* dwParam2 - maximum length of the output buffer */
	/* lpStruct - address of the buffer to fill */
	case PKH_KEYTEXT:
	    return DrvGetKeyNameText((LONG)dwParam1,
			(LPSTR)lpStruct, (int)dwParam2);

	/* dwParam1 - key to map */
	/* dwParam2 - mapping type */
	case PKH_MAPKEY:
	    return DrvMapVirtualKey((UINT)dwParam1, (UINT)dwParam2);

	/* dwParam1 - virtual key to get char value for */
	case PKH_GETCHAR:
	    return DrvGetChar((UINT)dwParam1);

	default:
	    return 0L;

    }
    
}


/********************************************************************
*   DrvInitKeyboard
*
********************************************************************/
static DWORD DrvInitKeyboard(void)
{
int n;
short keycode;
UInt32 state = 0;
long ks0, ks1;
UINT vk;
Ptr KCHRPtr;

	/* Get the current KCHR translation table */
	KCHRPtr = (Ptr) GetScriptManagerVariable(smKCHRCache);
	
	/* The gKeyMap is indexed by the Windows Virtual Keys, build the map */
    for(n=0; n<256; n++) {
		gKeyMap[n].vk_state = WM_KEYUP;		/* Clear the state */

		keycode = VKcodeToKeysym(n);			/* Get the Mac Virtual Key Code */

		/* map virtual key to selected Mac keycode (currently unused) */
		vk = KeysymToVKcode(keycode); 
		gKeyMap[n].virtual     = vk;
		gKeyMap[n].keypad     = vk;

		/* Get the unshifted character code from the system */
		ks0 = KeyTranslate(KCHRPtr, keycode, &state);

		/* Get shifted character code from the system */
		keycode |= shiftKey;						
		ks1 = KeyTranslate(KCHRPtr, keycode, &state);

		/* The ks0 and ks1 returned is defined as :						*/
		/*  31..........24	23..........16	15..........8	7..........0		*/
		/*  Reserved 1		Char code 1	Reserved 2	Char code 2	*/
		/* For single char codes, it will be in char code 2.				*/
		/* For two char bytes, char code 1&2 are valid.				*/
		gKeyMap[n].vkUnshifted 	= ks0 & charCodeMask;		/* take char code 2 */
		gKeyMap[n].vkShifted 		= ks1 & charCodeMask;		/* take char code 2 */
			

		/* Build up the translation table */
		vk = KeysymToVKcode(n);				/* Map Mac virtual key to win virt key */
		gKeyTranslate[n].virtual = vk;		/* Set the translation map */
		gKeyTranslate[n].keypad = vk;	
					   		
    }

	MapNumPad();			/* Modify the num pad's translation map. */

    return 1L;
}


/********************************************************************
*   MapNumPad
*
*	Re-Map the num pad to reflect the non-num lock state.
********************************************************************/
static void MapNumPad(void)
{

	/* The keypad was originally maped with the numbers (num-lock on)	*/
	/*  so that the characters turn out ok.							*/
	gKeyTranslate[AK_KP_Decimal].virtual = VK_DELETE;
	gKeyTranslate[AK_KP_0].virtual = VK_INSERT;
	gKeyTranslate[AK_KP_1].virtual = VK_END;
	gKeyTranslate[AK_KP_2].virtual = VK_DOWN;
	gKeyTranslate[AK_KP_3].virtual = VK_NEXT;
	gKeyTranslate[AK_KP_4].virtual = VK_LEFT;
	gKeyTranslate[AK_KP_5].virtual = VK_CLEAR;
	gKeyTranslate[AK_KP_6].virtual = VK_RIGHT;
	gKeyTranslate[AK_KP_7].virtual = VK_HOME;
	gKeyTranslate[AK_KP_8].virtual = VK_UP;
	gKeyTranslate[AK_KP_9].virtual = VK_PRIOR;
	
}



/********************************************************************
*   KeysymToVKcode
*
*	Maps a Mac Virtual Key code (keysim) to a Windows Virtual Key.
********************************************************************/
static UINT KeysymToVKcode(UINT keysym)
{
int index;
	
	for(index=0; index<256; index++)
		if(VK2KeySym[index].ToCode == keysym)
			return(VK2KeySym[index].FromCode);

	return AK_DEADKEY;
}



/********************************************************************
*   VKcodeToKeysym
*
*	Maps a Windows Virtual Key to the Mac Virtual Key (keysim).
********************************************************************/
static UINT VKcodeToKeysym(UINT VKcode)
{
int index;

	for(index=0; index<256; index++)
		if(VK2KeySym[index].FromCode == VKcode)
			return(VK2KeySym[index].ToCode);

	return AK_DEADKEY;
}


/********************************************************************
*   DrvGetKeyboardType
*
********************************************************************/
static DWORD DrvGetKeyboardType(int fnKeybInfo)
{
    switch (fnKeybInfo) {
	case KT_TYPE:
	    return (DWORD) 4;

	case KT_SUBTYPE:
	    return (DWORD) 0;

	case KT_NUMFKEYS:
	    return (DWORD) 12;

	default:
	    return 0L;
    }
}


/********************************************************************
*   DrvGetKBCodePage
*
********************************************************************/
static DWORD DrvGetKBCodePage(void)
{
	return((DWORD) 437);
}


/********************************************************************
*   DrvKeyboardState
*
********************************************************************/
static DWORD DrvKeyboardState(BOOL fSet, LPBYTE lpbKeyState)
{
    LPBYTE lpb = lpbKeyState;
    int n;

    if (fSet == 0) {
		for(n=0;n<256;n++)
			if(gKeyMap[n].vk_state == WM_KEYUP)
				*lpb++ = 0;
	   	 	else
				*lpb++ = 0x80;
    }
    else {
		for(n=0;n<256;n++)
	   		if(*lpb++ & 0x80)
	  			gKeyMap[n].vk_state = WM_KEYDOWN;
	    	else
	  			gKeyMap[n].vk_state = WM_KEYUP;
    }

    return 0;
}



/********************************************************************
*   DrvKeyState
*
********************************************************************/
static DWORD DrvKeyState(int nVirtKey, BOOL)
{
DWORD retcode;

    retcode = (gKeyMap[nVirtKey].vk_state == WM_KEYDOWN) ? 0x8000 : 0;

    return retcode;
}



/********************************************************************
*   DrvButtonState
*
********************************************************************/
static DWORD DrvButtonState(LPMSG lpMsg)
{

    switch (lpMsg->message) {
	case WM_LBUTTONDOWN:
	case WM_LBUTTONDBLCLK:
	    gKeyMap[VK_LBUTTON].vk_state = WM_KEYDOWN;
	    break;

	case WM_MBUTTONDOWN:
	case WM_MBUTTONDBLCLK:
	    gKeyMap[VK_MBUTTON].vk_state = WM_KEYDOWN;
	    break;

	case WM_RBUTTONDOWN:
	case WM_RBUTTONDBLCLK:
	    gKeyMap[VK_RBUTTON].vk_state = WM_KEYDOWN;
	    break;

	case WM_LBUTTONUP:
	    gKeyMap[VK_LBUTTON].vk_state = WM_KEYUP;
	    break;

	case WM_MBUTTONUP:
	    gKeyMap[VK_MBUTTON].vk_state = WM_KEYUP;
	    break;

	case WM_RBUTTONUP:
	    gKeyMap[VK_RBUTTON].vk_state = WM_KEYUP;
	    break;
    }

    if (gKeyMap[VK_CONTROL].vk_state == WM_KEYDOWN)
		lpMsg->wParam |= MK_CONTROL;

    if (gKeyMap[VK_SHIFT].vk_state == WM_KEYDOWN)
		lpMsg->wParam |= MK_SHIFT;

    return 0L;
}



/********************************************************************
*   SetModifierState
*
*	Maps and sets the modifier keys to the gKeyMap.
* This is because the MacOS does NOT generate events for these keys.
********************************************************************/
static void SetModifierState(short modifiers)
{
	if (modifiers & shiftKey)
    	gKeyMap[gKeyTranslate[AK_Shift_L].virtual].vk_state = WM_KEYDOWN;
	else
    	gKeyMap[gKeyTranslate[AK_Shift_L].virtual].vk_state = WM_KEYUP;
	
	if (modifiers & cmdKey)
    	gKeyMap[gKeyTranslate[AK_Menu].virtual].vk_state = WM_KEYDOWN;
	else
    	gKeyMap[gKeyTranslate[AK_Menu].virtual].vk_state = WM_KEYUP;

	if (modifiers & alphaLock)
    	gKeyMap[gKeyTranslate[AK_Caps_Lock].virtual].vk_state = WM_KEYDOWN;
	else
    	gKeyMap[gKeyTranslate[AK_Caps_Lock].virtual].vk_state = WM_KEYUP;

	if (modifiers & optionKey)
    	gKeyMap[gKeyTranslate[AK_Alt_L].virtual].vk_state = WM_KEYDOWN;
	else
    	gKeyMap[gKeyTranslate[AK_Alt_L].virtual].vk_state = WM_KEYUP;

	if (modifiers & controlKey)
    	gKeyMap[gKeyTranslate[AK_Control_L].virtual].vk_state = WM_KEYDOWN;
	else
    	gKeyMap[gKeyTranslate[AK_Control_L].virtual].vk_state = WM_KEYUP;

}


/********************************************************************
*   NumLockLED
*
*	Toggles the led on the keyboard on/off.
********************************************************************/
static void NumLockLED(short on, short keybdAddr)
{
short err;
int curLEDValue = 0;

	/* Get current state to avoid touching the caps lock or scroll lock */
	err = GetLEDValue(keybdAddr, &curLEDValue);

	curLEDValue &= 0x6;				/* Mask off the num lock bit */
	
	if (!on)
		curLEDValue |= 0x1;				/* Numlock is LSB, set to one to turn off */

	/* Set the new led state */		
	err = SetLEDValue(keybdAddr, curLEDValue);

}


/********************************************************************
*   VerifyKeyboard
*
*	Checks the num lock LED, in case it was set by another app.
*	It will set the gKeyMap accordingly, but no event is generated.
********************************************************************/
static void VerifyKeyboard(EventRecord *event)
{
int curLEDValue = 0;
short err;
short keybdAddr;

	keybdAddr = (event->message & adbAddrMask) >> 16;		/* Keyboard address */

	/* Get current state  */
	err = GetLEDValue(keybdAddr, &curLEDValue);
	
	/* Now set the state without generating events.  The user has already */
	/* pushed it at some time or another. */
	if (curLEDValue & kNumLockLED)
		gKeyMap[VK_NUMLOCK].vk_state = WM_KEYUP;
	else
		gKeyMap[VK_NUMLOCK].vk_state = WM_KEYDOWN;
		
	gKeyboardDirty = FALSE;
			
}


/********************************************************************
*   SetKeyState
*
*	Sets the key map state and handles the toggling of the Num Lock key.
********************************************************************/
static void SetKeyState(short vk, short message, short keybdID)
{
	
	if (vk != VK_NUMLOCK)		
	    gKeyMap[vk].vk_state = message;				/* Handle all non-numlock events */
	else	{												
		/* Num lock key, handle it seperately */
		/* Only toggle on key-down after key has been latched (keyup) */
		if ((message == WM_KEYDOWN) && gNumLockLatched) {	
			if (gKeyMap[vk].vk_state == WM_KEYDOWN) {
				gKeyMap[vk].vk_state = WM_KEYUP;
				NumLockLED(LED_OFF, keybdID);
			}
			else {
				gKeyMap[vk].vk_state = WM_KEYDOWN;
				NumLockLED(LED_ON, keybdID);
			}
			gNumLockLatched = FALSE;						/* Must have keyup to clear latch */
		}
		else if (message == WM_KEYUP)
			gNumLockLatched = TRUE;						/* OK to toggle the num lock now */
	}
	
}



/********************************************************************
*   DrvBuildKeyMessage
*
*	Build the key message for sending upstairs.
********************************************************************/
static DWORD DrvBuildKeyMessage(EventRecord *event, LPMSG lpMsg)
{
UINT vk;
WORD wContext;
char keycode, theChar;
short keybdID;

	/* Pack some of the easier variables */
    lpMsg->hwnd	= 0;
    lpMsg->message = (event->what == keyUp) ? WM_KEYUP: WM_KEYDOWN;
    lpMsg->time	= event->when;
    lpMsg->pt.x	= event->where.h;
    lpMsg->pt.y	= event->where.v;
	
	theChar = (event->message & charCodeMask);				/* The ascii character */
	keycode = (event->message & keyCodeMask) >> 8;		/* Get virtual code */
	keybdID = (event->message & adbAddrMask) >> 16;		/* Keyboard address */

	SetModifierState(event->modifiers);		/* Set keys that do not generate events */

	/* Translate the Mac virtual key to Windows virtual key */
    if (gKeyMap[VK_NUMLOCK].vk_state == WM_KEYDOWN)
		vk = gKeyTranslate[keycode].keypad;
    else
		vk = gKeyTranslate[keycode].virtual;

    if (vk == AK_DEADKEY) {
		LOGSTR((LF_LOG,"dead key %x %s\n",keycode,(keycode)));
		return 0L;
    }

    lpMsg->wParam = vk;						/* pack the virtual code */
    
    /* Set the key map state (but leave the num lock along ) */
	SetKeyState(vk, lpMsg->message, keybdID);	/* Also handles num-lock on/off */

    if(vk == VK_ALT)
		return 0L;

	/* Setup the key transition bits */
    if (lpMsg->message == WM_KEYUP)
		wContext = 0xC000;							/* key transition bits */
    else
		if (event->what == autoKey)
			wContext = 0x4000;						/* Quessed at this from viewing input app */
    	else
			wContext = 0x0000;
    
    wContext |= keycode;

    if (gKeyMap[VK_ALT].vk_state == WM_KEYDOWN) {
		wContext |= 0x2000;						/* alt key pressed bit */

		if (lpMsg->message == WM_KEYUP)
	    	lpMsg->message = WM_SYSKEYUP;

		if (lpMsg->message == WM_KEYDOWN)
	  	  lpMsg->message = WM_SYSKEYDOWN;
    }
		
    lpMsg->lParam = MAKELONG(1,wContext);

    return (DWORD)vk;
}



/********************************************************************
*   DrvMapVirtualKey
*
*	
********************************************************************/
static DWORD DrvMapVirtualKey(UINT uKeyCode, UINT fuMapType)
{

    switch (fuMapType) {
	case 0:
	    return VKcodeToKeysym(uKeyCode);

	case 1:
	    return KeysymToVKcode(uKeyCode);

	case 2:
	    return uKeyCode;
    }

    return 0;
}


/********************************************************************
*   IsKeyPressed
*
*	KeyCode is the Keyboard Scan Code.  0-127
*  Use the #define codes, ie AK_Control from DrvKeyboard.h.
********************************************************************/
short IsKeyPressed(unsigned short keyCode )
{
KeyMap kmap;
	
	GetKeys(kmap);
	
   return((((char *) kmap)[keyCode >> 3] & (1 << (keyCode & 7))) != 0);
}


/********************************************************************
*   DoCmdKey
*
*	Handle cmd key events, these are essentially menu events.
********************************************************************/
static BOOL DoCmdKey(EventRecord *event)
{
long msel;
int item,menu;
char theChar, keyCode;

	/* We want to ignore keyUp's on menu commands to avoid doubling of menu hits */
	if (event->what == keyUp)
		return(FALSE);

	theChar = event->message & charCodeMask;					/* The ascii character */
	keyCode = (event->message & keyCodeMask) >> 8;		/* Get virtual code */

	/* Treat an cmd-control-period as a drop into debugger function */
	if ((keyCode == AK_period) && (event->modifiers & controlKey))
		DropIntoDebugger(NULL);

	/* Normally we adjust the menu states here */
	msel = MenuKey(theChar);
	item = LoWord(msel);
	menu = HiWord(msel);
	DrvMenuDispatch(menu, item);

	HiliteMenu(0);						/* remove menu title hiliting */
	
	return(1L);		/* Return true in case we posted a msg to the library */
}


/********************************************************************
*   DrvHandleKeyboardEvents
*
********************************************************************/
static DWORD DrvHandleKeyboardEvents(EventRecord *event)
{
MSG msg;

	switch(event->what) {
	case keyDown:
	case keyUp:
	case autoKey:	
		if (gKeyboardDirty)
			VerifyKeyboard(event);

		if (event->modifiers & cmdKey)
			return DoCmdKey(event);			/* Intercept cmd keys (Mac menu) */
			
	    if (DrvBuildKeyMessage(event, &msg))
			return LibCallback(TWINLIBCALLBACK_KEYBOARD,	0, 0, (LPVOID)&msg);
	    else
			return 0L;

	case APP_EVT:
		switch (event->message) {
		case EVT_SUSPENDAPP:
		case EVT_RESUMEAPP:
			gKeyboardDirty = true;
			break;
		default:
			break;
		}	
		return 0;
		
	default:
	    return 0L;
	}

}



/********************************************************************
*   DrvGetKeyNameText
*
********************************************************************/
static DWORD DrvGetKeyNameText(LONG lParam, LPSTR lpszBuffer, int cbMaxKey)
{
int index;
UINT VKcode = HIWORD(lParam);

	lpszBuffer[0] = '\0';			/* Terminate string in case we do not find a match */

	/* Look for the virtual key code, then copy the name text */
	for(index=0; index<256; index++)
		if(VK2KeySym[index].FromCode == VKcode) {
			strncpy(lpszBuffer, VK2KeySym[index].name, cbMaxKey-1);
		}

    return 0L;
}


/********************************************************************
*   DrvGetChar
*
********************************************************************/
static DWORD DrvGetChar(UINT vk)
{

    if (gKeyMap[VK_SHIFT].vk_state == WM_KEYDOWN)
		return (DWORD)gKeyMap[vk].vkShifted;
	else if ((gKeyMap[VK_CAPITAL].vk_state == WM_KEYDOWN) &&
				(vk >= VK_A) && (vk<= VK_Z))
		return (DWORD)gKeyMap[vk].vkShifted;
    else
		return (DWORD)gKeyMap[vk].vkUnshifted;

	return 0L;
}


/********************************************************************
*	SendAltF4Keystroke
*
*	Mimic the pressing of the Alt and F4 keys.  Mainly used for a File:Quit event.
*********************************************************************/
DWORD PostAltF4Keystroke(void)
{
long eventMsg;
EvQEl *queElementPtr;
OSErr err;

	/* Set the message to be the f4 function key */
	/* Note that the keyboard id will be zero, since this currently */
	/*  applies only for the keyboard LED's, it should be ok. */
	eventMsg = 0x10;												/* The character that f4 ends up mapping to */
	eventMsg |= AK_F4 << 8;									/* Set the virtual key code */

	/* Post the keydown event */
	err = PPostEvent(keyDown, eventMsg, &queElementPtr);
	queElementPtr->evtQModifiers = optionKey;		/* Set the modifier */

#if 0			// Sending the up event seems to be making things beep twice
	/* Post the keyup event */
	err = PPostEvent(keyUp, eventMsg, &queElementPtr);
	queElementPtr->evtQModifiers = optionKey;		/* Set the modifier */
#endif

	return (FALSE);			/* Posted through the system event que, no need to let library know we did it. */
}


/********************************************************************
*   ADBComplete
*
*	Callback for ADB asyncronous communications, just set the flag.
********************************************************************/
static void ADBComplete(void)
{
	gADBDone = TRUE;
}


/********************************************************************
*   SendADBCommand
*
********************************************************************/
static short SendADBCommand(void *buffer, int command)
{
short err;
long oldTicks;
ADBCompletionUPP ADBComplete_UPP;

	gADBDone = FALSE;			/* Reset the done flag */
	
	/* Create a UPP for the callback */
	ADBComplete_UPP = NewADBCompletionProc(ADBComplete);
	/* Send the command to the keyboard */
	err = ADBOp(&gADBDone, NewADBCompletionProc(ADBComplete), buffer, command);

	/* Now wait for the command to finish */
	if (err == noErr) {
		oldTicks = TickCount();
		while(!gADBDone)
			if ((TickCount() - oldTicks) > ADB_TIMEOUT) {
				err = -1;						/* Force a time out for safety's sake */
				gADBDone = TRUE;
			}
	}
	
	/* Dispose of the callback UPP since we are done with it. */	
	DisposeRoutineDescriptor(ADBComplete_UPP);
	
	return(err);
}


/********************************************************************
*   GetLEDValue
*
********************************************************************/
static short GetLEDValue(ADBAddress addr, int *curLEDValue)
{
int command;
short err;

	/* initialize length of buffer; on return, the ADB device sets */
	gADBRegisterData[0] = 0;		/* This byte to the number of bytes returned */

	/* Get existing register contents with a Talk command */
	command = (addr * 16) + kTalkMask + kLEDRegister;

	err = SendADBCommand(&gADBRegisterData, command);
	
	if (err == noErr)
		*curLEDValue = gADBRegisterData[2] & kLEDValueMask;
	else
		*curLEDValue = 0;
		
	return(err);
}


/********************************************************************
*   SetLEDValue
*
********************************************************************/
static short SetLEDValue(ADBAddress addr, int newLEDValue)
{
int command;
short err;
Byte myByte;

	gADBRegisterData[0] = 2;		/* Set length of buffer */

	/* get existing register contents with a Talk command */
	command = (addr * 16) + kTalkMask + kLEDRegister;

	err = SendADBCommand(&gADBRegisterData, command);

	if (err != noErr)
		return(err);
	
	/* gADBRegisterData now contains the existing data in device register 2 */
	/* Reset the lower 3 bits of byt 2 to the desired value */
	myByte = gADBRegisterData[2];
	myByte &= 0xF8;								/* Mask off the lower 3 bits. */
	myByte |= newLEDValue;					/* Install new setting */
	gADBRegisterData[2] = myByte;
	
	command = (addr * 16) + kListenMask + kLEDRegister;
	
	err = SendADBCommand(&gADBRegisterData, command);

	return(err);
}




