/****************************************************************************
*
*	KeySupport.c ----------	Keymacro support routines.
*
*	Author ----------------	Olaf Barthel, MXM
*				Brabeckstrasse 35
*				D-3000 Hannover 71
*
*	KeyMacro  ©  Copyright  1990  by  MXM;  Executable  program,
*	documentation  and  source  code are shareware.  If you like
*	this  program  a  small donation will entitle you to receive
*	updates and new programs from MXM.
*
****************************************************************************/

	/* AllocRem():
	 *
	 *	Allocate public memory and keep track of its size.
	 */

VOID *
AllocRem(LONG ByteSize,LONG Requirements)
{
	LONG	*MemoryBlock = NULL;
	LONG	 RemSize = ByteSize + sizeof(LONG);

	if(ByteSize > 0)
		MemoryBlock = (LONG *)AllocMem(RemSize,Requirements);

	if(MemoryBlock)
		*MemoryBlock++ = RemSize;

	return((VOID *)MemoryBlock);
}

	/* FreeRem():
	 *
	 *	Free a tracked portion of memory.
	 */

VOID *
FreeRem(LONG *MemoryBlock)
{
	if(MemoryBlock--)
		FreeMem(MemoryBlock,*MemoryBlock);

	return(NULL);
}

	/* SendMacroMsg(scm_Msg,scm_Port):
	 *
	 *	Post a cloned macro message to a MsgPort.
	 */

VOID *
SendMacroMsg(struct MacroMessage *scm_Msg,struct MsgPort *scm_Port)
{
	struct MacroMessage *scm_TempMsg;

	if(scm_TempMsg = (struct MacroMessage *)AllocRem(sizeof(struct MacroMessage),MEMF_PUBLIC | MEMF_CLEAR))
	{
		CopyMem(scm_Msg,scm_TempMsg,sizeof(struct MacroMessage));

		scm_TempMsg -> mm_Message . mn_Node . ln_Name	= (char *)scm_TempMsg;
		scm_TempMsg -> mm_Message . mn_ReplyPort	= NULL;
		scm_TempMsg -> mm_Message . mn_Length		= sizeof(struct MacroMessage);

		PutMsg(scm_Port,(struct Message *)scm_TempMsg);
	}

	return((VOID *)scm_TempMsg);
}

	/* The following piece of code was written by Jim Mackraz;
	 * I do not claim any publishing rights for it, the code
	 * is still his property. I will try to remove it as soon
	 * as Kick 2.x has left the beta phase (read the docs,
	 * there will probably be no further KeyMacro revisions
	 * for pre-2.x systems).
	 */

#define CONTROLBITS     ( (1 << 5) | (1 << 6) )

STATIC BYTE
CheckNormal(four_bytesp, val, type, qualp)
LONG four_bytesp;
UBYTE val;
UWORD type;
UWORD *qualp;
{
	UBYTE *p = (UBYTE *)four_bytesp; /* codes held in long word  */
	long position;

		/* start with last of four bytes, "more vanilla"        */

	p += 3;

	for(position = 3 ; position >= 0 ; --position, --p)
	{
		if(*p == val)
		{
			switch(type)
			{
				case KC_NOQUAL:	if(position != 3)
							goto NOT_THIS;

						break;

				case KCF_SHIFT:	if(!(position & 2))
							goto NOT_THIS;

						if(position == 2)
							*qualp |= IEQUALIFIER_LSHIFT;

						break;

				case KCF_ALT:	if(!(position & 2))
							goto NOT_THIS;

						if(position == 2)
							*qualp |= IEQUALIFIER_LALT;

						break;


				case KCF_CONTROL:

						if(!(position & 2))
							goto NOT_THIS;

						if(position == 2)
							*qualp |= IEQUALIFIER_CONTROL;

						break;

				case KCF_ALT | KCF_CONTROL:

						if(!(position & 1))
							*qualp |= IEQUALIFIER_LALT;

						if(!(position & 2))
							*qualp |= IEQUALIFIER_CONTROL;

						break;

				case KCF_SHIFT | KCF_CONTROL:

						if(!(position & 1))
							*qualp |= IEQUALIFIER_LSHIFT;

						if(!(position & 2))
							*qualp |= IEQUALIFIER_CONTROL;

						break;


				case KCF_SHIFT | KCF_ALT:

						if(!(position & 1))
							*qualp |= IEQUALIFIER_LSHIFT;

						if(!(position & 2))
							*qualp |= IEQUALIFIER_LALT;

						break;

				default:	break;
			}

			return(TRUE);
		}
NOT_THIS:       ;
	}

	return(FALSE);
}

STATIC BYTE
CheckVanilla(p,val,qualp)
UBYTE *p;		/* note: byte pointer   */
UBYTE val;
UWORD *qualp;
{
	int i;

		/* only one way to match a vanilla control key  */

	if(!(val & CONTROLBITS))
	{
		/* is a control code    */

		if((p[3] & ~CONTROLBITS) == val)
		{
			*qualp |= IEQUALIFIER_CONTROL;
			return(TRUE);
		}
	}
	else
	{
		/* not a control        */

		for(i = 3 ; i >= 0 ; --i)
		{
			if(p[i] == val)
			{
				if(!(i & 1))
					*qualp |= IEQUALIFIER_LSHIFT;

				if(!(i & 2))
					*qualp |= IEQUALIFIER_LALT;

				return(TRUE);
			}
		}
	}

	return(FALSE);
}

#define KEYMAPSIZE      64

/* LowKeyInvert returns good code else <0 if no find
 *
 * regarding keymap as many-to-one mapping:
 * -entries for a given key are scanned so that
 *      the minimum number of qualifiers are associated
 *      with the keystroke.
 * -passing a character value of zero corresponds, in
 *      the default keymap, to CTRL-`, which is probably a bug.
 * -numerals are matched with numeric pad keystrokes (no
 *      qualifiers) on standard keymap.  The way to specify
 *      a key on the number row is via its shifted value;
 *      specify explicitly that the qualifier is to be unshifted,
 *      or a "don't care."
 */

STATIC UWORD
LowKeyInvert(value,km,codep,qualp,indexp)
UBYTE value;		/* translation value from low keymap    */
struct KeyMap *km;
UWORD *codep;		/* pointers where answers are to be put */
UWORD *qualp;
ULONG *indexp;		/* dead-key index information (put into ie?)    */
{
	UWORD code = KEYMAPSIZE - 1;	/* last entry   */
	unsigned int type;
	LONG *p;		/* points to four-byte lokeymap entry   */
	int found_it = 0;

	*indexp = *qualp = 0;

	p = (LONG *)km -> km_LoKeyMap + code;

	do
	{
			/* determine type of key        */

		if((type = km -> km_LoKeyMapTypes[code]) == KC_VANILLA)
			found_it = CheckVanilla(p,value,qualp);
		else
			if(!(type & KCF_NOP))
				found_it = CheckNormal(p,value,type,qualp);

		--p;
	}
	while(!found_it && code--);

	*codep = code;

	return(code);
}

ULONG
InvertKeyMap(ULONG ansicode,struct InputEvent *ie,struct KeyMap *km)
{
	LONG	kindex;
	UBYTE	code = 0;

	ie -> ie_Class		= IECLASS_RAWKEY;
	ie -> ie_EventAddress	= 0;

		/* check for codes in (default) high map first  */

	switch(ansicode)
	{
		case ' ':	code = 0x40;	/* space            */
				break;

		case 0x08:      code = 0x41;	/* backspace    */
				break;

		case '\t':	code = 0x42;	/* tab                  */
				break;

		case 0x0A:
		case 0x0D:	code = 0x44;	/* return               */
				break;

		case 0x1B:	code = 0x45;	/* esc                  */
				break;

		case 0x7F:	code = 0x46;	/* del                  */
				break;

		case '0':	code = 0x0a;
				break;

		case '1':
		case '2':
		case '3':
		case '4':
		case '5':
		case '6':
		case '7':
		case '8':
		case '9':	code = ansicode - '0';
				break;
	}

	if(ansicode >= '1' && ansicode <= '0')
		code = 

	ie -> ie_Code		= 0;
	ie -> ie_Qualifier	= 0;

	if(code)
	{
		ie -> ie_Code		= code;
		ie -> ie_Qualifier	= 0;

		return(TRUE);
	}

	LowKeyInvert((UBYTE)ansicode,km,&ie -> ie_Code,&ie -> ie_Qualifier,(ULONG *)&kindex);

	if(!ie -> ie_Code && !ie -> ie_Qualifier)
		return(FALSE);

	return(TRUE);
}
