/****************************************************************************
*
*	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);
}

	/* PackQualifiers():
	 *
	 *	Pack a couple of qualifiers indicated by a keymap
	 *	qualifier tag (sortof).
	 */

STATIC UWORD
PackQualifiers(BYTE Bits)
{
	UWORD Qualifier = 0;

	if(Bits & KCF_SHIFT)
		Qualifier |= IEQUALIFIER_LSHIFT;

	if(Bits & KCF_ALT)
		Qualifier |= IEQUALIFIER_LALT;

	if(Bits & KCF_CONTROL)
		Qualifier |= IEQUALIFIER_CONTROL;

	return(Qualifier);
}

	/* ScanKeyMap():
	 *
	 *	Scan a given part of the keymap area and handle the
	 *	necessary Ansi<->InputEvent conversion.
	 */

BYTE
ScanKeyMap(UBYTE Hi,UBYTE Offset,UBYTE *KeyTable,UBYTE *KeyTypes,UBYTE AnsiKey,struct InputEvent *Event)
{
		/* This table contains the qualifiers associated with
		 * the various KeyType flag bits.
		 */

	STATIC struct {
		UBYTE	QualBits;
		UWORD	Qualifiers[4];
	} QualType[8] = {
		KC_NOQUAL,		0,					0,			0,			0,
		KCF_SHIFT,		0,					0,			IEQUALIFIER_LSHIFT,	0,
		KCF_ALT,		0,					0,			IEQUALIFIER_LALT,	0,
		KCF_CONTROL,		0,					0,			IEQUALIFIER_CONTROL,	0,
		KCF_ALT|KCF_SHIFT,	IEQUALIFIER_LSHIFT|IEQUALIFIER_LALT,	IEQUALIFIER_LALT,	IEQUALIFIER_LSHIFT,	0,
		KCF_CONTROL|KCF_ALT,	IEQUALIFIER_CONTROL|IEQUALIFIER_LALT,	IEQUALIFIER_CONTROL,	IEQUALIFIER_LALT,	0,
		KCF_CONTROL|KCF_SHIFT,	IEQUALIFIER_CONTROL|IEQUALIFIER_LSHIFT,	IEQUALIFIER_CONTROL,	IEQUALIFIER_LSHIFT,	0,
		KC_VANILLA,		IEQUALIFIER_LSHIFT|IEQUALIFIER_LALT,	IEQUALIFIER_LALT,	IEQUALIFIER_LSHIFT,	0
	};

	BYTE	*String;
	SHORT	 i,j,k;

		/* Scan the area. */

	for(i = 0 ; i < Hi ; i++)
	{
			/* This one's a dead key or dead-key-modifiable. */

		if(KeyTypes[i] & KCF_DEAD)
		{
			String = (BYTE *)(((ULONG *)KeyTable)[i]);

				/* There a eight two-byte-pairs. The first
				 * byte indicates the type of the dead
				 * key.
				 */

			for(j = 0 ; j < 8 ; j++)
			{
				switch(String[2 * j])
				{
						/* Vanilla key. */

					case 0:		if((UBYTE)String[2 * j + 1] == AnsiKey)
							{
								Event -> ie_Qualifier	= PackQualifiers(j);
								Event -> ie_Code	= Offset + i;

								return(TRUE);
							}

							break;

						/* Dead key modifiable, let's hope
						 * that the first character in
						 * the table indicates our ansi key.
						 */

					case DPF_MOD:	if((UBYTE)String[String[2 * j + 1]] == AnsiKey)
							{
								Event -> ie_Qualifier	= PackQualifiers(j);
								Event -> ie_Code	= Offset + i;

								return(TRUE);
							}

							break;

						/* Ignore the rest. */

					default:	break;
				}
			}
		}

			/* Is a string mapped to this key? */

		if(KeyTypes[i] & KCF_STRING)
		{
			String = (BYTE *)(((ULONG *)KeyTable)[i]);

				/* We need only 1 character strings. */

			if(String[0] == 1)
			{
				if((UBYTE)String[String[1]] == AnsiKey)
				{
					for(k = 0 ; k < 8 ; k++)
					{
						if(QualType[k] . QualBits == (KeyTypes[i] & KC_VANILLA))
						{
							Event -> ie_Qualifier	= QualType[k] . Qualifiers[j];
							Event -> ie_Code	= Offset + i;

							return(TRUE);
						}
					}
				}
			}

				/* Find the approriate qualifier. */

			for(j = 0 ; j < 3 ; j++)
			{
				if(KeyTypes[i] & (1 << j))
				{
					if(String[2 + (2 * j)] == 1)
					{
						if((UBYTE)String[String[3 + (2 * j)]] == AnsiKey)
						{
							for(k = 0 ; k < 8 ; k++)
							{
								if(QualType[k] . QualBits == (KeyTypes[i] & KC_VANILLA))
								{
									Event -> ie_Qualifier	= QualType[k] . Qualifiers[j];
									Event -> ie_Code	= Offset + i;

									return(TRUE);
								}
							}
						}
					}
				}
			}
		}
		else
		{
				/* It's a fair vanilla key. */

			for(j = 0 ; j < 4 ; j++)
			{
				if(AnsiKey == KeyTable[4 * i + j])
				{
					for(k = 0 ; k < 8 ; k++)
					{
						if(QualType[k] . QualBits == KeyTypes[i])
						{
							Event -> ie_Qualifier	= QualType[k] . Qualifiers[j];
							Event -> ie_Code	= Offset + i;

							return(TRUE);
						}
					}
				}
			}
		}
	}

	return(FALSE);
}

STATIC VOID
PutKey(struct InputEvent *Event,UBYTE RawKey)
{
	Event -> ie_Qualifier	= 0;
	Event -> ie_Code	= RawKey;
}

	/* KeyInvert():
	 *
	 *	Find the qualifier & code which generate the Ansi
	 *	character passed to this routine.
	 */

BYTE
KeyInvert(UBYTE AnsiKey,struct InputEvent *Event,struct KeyMap *KeyMap)
{
		/* Careful: if this is actually a control character
		 *          (return, escape, etc.) we will check the
		 *          high keymap table first, else the low
		 *          keymap table.
		 */

	switch(AnsiKey)
	{
		case ' ':	PutKey(Event,0x40);
				return(TRUE);

		case '\b':	PutKey(Event,0x41);
				return(TRUE);

		case '\t':	PutKey(Event,0x42);
				return(TRUE);

		case '\n':
		case '\r':	PutKey(Event,0x44);
				return(TRUE);

		case '\33':	PutKey(Event,0x45);
				return(TRUE);

		case '\177':	PutKey(Event,0x46);
				return(TRUE);
/*
		This does - strange enough - not work correctly. Can you
		discover why?

		case ' ':
		case '\b':
		case '\t':
		case '\n':
		case '\r':
		case '\33':
		case '\177':	if(!ScanKeyMap(0x28,0x40,KeyMap -> km_HiKeyMap,KeyMap -> km_HiKeyMapTypes,AnsiKey,Event))
					return(ScanKeyMap(0x40,0x00,KeyMap -> km_LoKeyMap,KeyMap -> km_LoKeyMapTypes,AnsiKey,Event));
				else
					return(TRUE);
*/
					/* Check the low keymap table first. */

		default:	if(!ScanKeyMap(0x40,0x00,KeyMap -> km_LoKeyMap,KeyMap -> km_LoKeyMapTypes,AnsiKey,Event))
					return(ScanKeyMap(0x28,0x40,KeyMap -> km_HiKeyMap,KeyMap -> km_HiKeyMapTypes,AnsiKey,Event));
				else
					return(TRUE);
	}
}
