/*
**	termXEM.c
**
**	External emulation support routines
**
**	Copyright © 1990-1994 by Olaf `Olsen' Barthel
**		All Rights Reserved
*/

#include "termGlobal.h"

	/* GetOptionMode(struct xpr_option *Option):
	 *
	 *	Turn text into a boolean value.
	 */

STATIC BYTE __regargs
GetOptionMode(struct xem_option *Option)
{
	if(Option)
	{
		STATIC STRPTR TrueOptions[] =
		{
			"ON",
			"TRUE",
			"T",
			"YES",
			"Y",
			NULL
		};

		register WORD i;

		for(i = 0 ; TrueOptions[i] ; i++)
		{
			if(!Stricmp(Option -> xemo_value,TrueOptions[i]))
				return(TRUE);
		}
	}

	return(FALSE);
}

STATIC LONG __saveds __asm
xem_sflush(VOID)
{
	if(WriteRequest)
	{
		WriteRequest -> IOSer . io_Command = CMD_CLEAR;

		if(!DoIO(WriteRequest))
			return(0);
	}

	return(-1);
}

STATIC LONG __saveds __asm
xem_squery(VOID)
{
	if(WriteRequest)
	{
		WriteRequest -> IOSer . io_Command = SDCMD_QUERY;

		DoIO(WriteRequest);

			/* Return error if carrier is lost. */

		if((WriteRequest -> io_Status & CIAF_COMCD) && Config -> SerialConfig -> CheckCarrier)
			Online = FALSE;
		else
			return((LONG)WriteRequest -> IOSer . io_Actual);
	}

	return(-1);
}

STATIC LONG __saveds __asm
xem_sread(register __a0 APTR Buffer,register __d0 LONG Size,register __d1 ULONG Timeout)
{
		/* Are both IORequests available? */

	if(WriteRequest && ReadRequest)
	{
			/* Valid size parameter? */

		if(Size > 0)
		{
				/* How many bytes are still in the serial buffer? */

			WriteRequest -> IOSer . io_Command = SDCMD_QUERY;

			DoIO(WriteRequest);

				/* Return error if carrier is lost. */

			if(Config -> SerialConfig -> CheckCarrier)
			{
				if(WriteRequest -> io_Status & CIAF_COMCD)
				{
					Online = FALSE;

					return(-1);
				}
			}

			/* ALWAYS */
			{
				register ULONG Waiting;

				if(Waiting = WriteRequest -> IOSer . io_Actual)
				{
						/* No timeout specified? Read as many
						 * bytes as available.
						 */

					if(!Timeout)
					{
						if(Waiting > Size)
							Waiting = Size;

						ReadRequest -> IOSer . io_Command	= CMD_READ;
						ReadRequest -> IOSer . io_Data		= Buffer;
						ReadRequest -> IOSer . io_Length	= Waiting;

						if(DoIO(ReadRequest))
							Waiting = ReadRequest -> IOSer . io_Actual;

						BytesIn += Waiting;

						return((LONG)Waiting);
					}

						/* Enough data pending to be read? */

					if(Waiting >= Size)
					{
						ReadRequest -> IOSer . io_Command	= CMD_READ;
						ReadRequest -> IOSer . io_Data		= Buffer;
						ReadRequest -> IOSer . io_Length	= Size;

						if(DoIO(ReadRequest))
							Size = ReadRequest -> IOSer . io_Actual;

						BytesIn += Size;

						return(Size);
					}
				}
				else
				{
						/* No timeout & no bytes available:
						 * return immediately.
						 */

					if(!Timeout)
						return(0);
				}
			}

			/* ALWAYS */
			{
				register ULONG SignalSet;

					/* Set up the timer. */

				TimeRequest -> tr_node . io_Command	= TR_ADDREQUEST;
				TimeRequest -> tr_time . tv_secs	= Timeout / MILLION;
				TimeRequest -> tr_time . tv_micro	= Timeout % MILLION;

					/* Set up the read request. */

				ReadRequest -> IOSer . io_Command	= CMD_READ;
				ReadRequest -> IOSer . io_Data		= Buffer;
				ReadRequest -> IOSer . io_Length	= Size;

					/* Prevent early termination. */

				ClrSignal(SIG_SERIAL | SIG_TIMER);

					/* Start IO... */

				SendIO(ReadRequest);
				SendIO(TimeRequest);

				FOREVER
				{
					SignalSet = Wait(SIG_SERIAL | SIG_TIMER);

						/* Receive buffer filled? */

					if(SignalSet & SIG_SERIAL)
					{
							/* Abort the timer request. */

						if(!CheckIO(TimeRequest))
							AbortIO(TimeRequest);

						WaitIO(TimeRequest);

							/* Did the request terminate gracefully? */

						if(WaitIO(ReadRequest))
							Size = ReadRequest -> IOSer . io_Actual;

						BytesIn += Size;

						return(Size);
					}

						/* Hit by timeout? */

					if(SignalSet & SIG_TIMER)
					{
							/* Abort the read request. */

						if(!CheckIO(ReadRequest))
							AbortIO(ReadRequest);

						WaitIO(ReadRequest);

							/* Remove the timer request. */

						WaitIO(TimeRequest);

							/* Did the driver receive any
							 * data?
							 */

						if(ReadRequest -> IOSer . io_Actual)
						{
							BytesIn += ReadRequest -> IOSer . io_Actual;

							return((LONG)ReadRequest -> IOSer . io_Actual);
						}
						else
						{
								/* Take a second look and query the number of
								 * bytes ready to be received, there may
								 * still be some bytes in the buffer.
								 * Note: this depends on the way the
								 * driver handles read abort.
								 */

							WriteRequest -> IOSer . io_Command = SDCMD_QUERY;

							DoIO(WriteRequest);

								/* Don't read too much. */

							if(Size > WriteRequest -> IOSer . io_Actual)
								Size = WriteRequest -> IOSer . io_Actual;

								/* Are there any bytes to be transferred? */

							if(Size)
							{
									/* Read the data. */

								ReadRequest -> IOSer . io_Command	= CMD_READ;
								ReadRequest -> IOSer . io_Data		= Buffer;
								ReadRequest -> IOSer . io_Length	= Size;

									/* Read the data. */

								if(DoIO(ReadRequest))
									Size = ReadRequest -> IOSer . io_Actual;

								BytesIn += Size;
							}

							return(Size);
						}
					}
				}
			}
		}
		else
			return(0);
	}
	else
		return(-1);
}

STATIC ULONG __saveds __asm
xem_toptions(register __d0 LONG NumOpts,register __a0 struct xem_option **Opts)
{
	if(NumOpts && Opts)
	{
		enum	{	GAD_USE=1,GAD_CANCEL,GAD_SPECIAL };

		struct LayoutHandle	*Handle;
		ULONG			 Flags = NULL;

			/* We only have 32 bits! */

		if(NumOpts > 32)
			NumOpts = 32;

		if(Handle = LT_CreateHandleTags(Window -> WScreen,
			LH_LocaleHook,	&LocaleHook,
		TAG_DONE))
		{
			struct Window	*Window;
			LONG		 i,Split;

			if(NumOpts > 16)
				Split = NumOpts / 2;
			else
				Split = -1;

			LT_New(Handle,
				LA_Type,	VERTICAL_KIND,
			TAG_DONE);
			{
				LT_New(Handle,
					LA_Type,	HORIZONTAL_KIND,
					LA_LabelID,	MSG_V36_1501,
				TAG_DONE);
				{
					LT_New(Handle,
						LA_Type,	VERTICAL_KIND,
					TAG_DONE);
					{
						for(i = 0 ; i < NumOpts ; i++)
						{
							if(Opts[i])
							{
								switch(Opts[i] -> xemo_type)
								{
									case XEMO_BOOLEAN:

										LT_New(Handle,
											LA_Type,	CHECKBOX_KIND,
											LA_LabelText,	Opts[i] -> xemo_description,
											LA_ID,		GAD_SPECIAL + i,
											GTCB_Checked,	GetOptionMode(Opts[i]),
										TAG_DONE);

										break;

									case XEMO_LONG:

										LT_New(Handle,
											LA_Type,		INTEGER_KIND,
											LA_LabelText,		Opts[i] -> xemo_description,
											LA_ID,			GAD_SPECIAL + i,
											LA_Chars,		20,
											GTIN_Number,		Atol(Opts[i] -> xemo_value),
											LAIN_UseIncrementers,	TRUE,
										TAG_DONE);

										break;

									case XEMO_STRING:

										LT_New(Handle,
											LA_Type,	STRING_KIND,
											LA_LabelText,	Opts[i] -> xemo_description,
											LA_ID,		GAD_SPECIAL + i,
											LA_Chars,	20,
											GTST_String,	Opts[i] -> xemo_value,
											GTST_MaxChars,	Opts[i] -> xemo_length - 1,
										TAG_DONE);

										break;

									case XEMO_COMMPAR:

										LT_New(Handle,
											LA_Type,	STRING_KIND,
											LA_LabelText,	Opts[i] -> xemo_description,
											LA_ID,		GAD_SPECIAL + i,
											LA_Chars,	20,
											LA_HighLabel,	TRUE,
											GTST_String,	Opts[i] -> xemo_value,
											GTST_MaxChars,	Opts[i] -> xemo_length - 1,
										TAG_DONE);

										break;

									case XEMO_HEADER:

										LT_New(Handle,
											LA_Type,	TEXT_KIND,
											LA_LabelText,	Opts[i] -> xemo_description,
											LA_HighLabel,	TRUE,
											GTTX_Text,	" ",
										TAG_DONE);

										break;

									case XEMO_COMMAND:

										LT_New(Handle,
											LA_Type,	BUTTON_KIND,
											LA_LabelText,	Opts[i] -> xemo_description,
											LA_ID,		GAD_SPECIAL + i,
											LA_Chars,	20,
										TAG_DONE);

										break;
								}
							}

							if(i == Split)
							{
								LT_EndGroup(Handle);

								LT_New(Handle,
									LA_Type,	VERTICAL_KIND,
								TAG_DONE);
							}
						}

						LT_EndGroup(Handle);
					}

					LT_EndGroup(Handle);
				}

				LT_New(Handle,
					LA_Type,	VERTICAL_KIND,
				TAG_DONE);
				{
					LT_New(Handle,LA_Type,XBAR_KIND,LAXB_FullSize,TRUE,TAG_DONE);
	
					LT_EndGroup(Handle);
				}

				LT_New(Handle,LA_Type,HORIZONTAL_KIND,
					LAGR_SameSize,	TRUE,
					LAGR_Spread,	TRUE,
				TAG_DONE);
				{
					LT_New(Handle,
						LA_Type,	BUTTON_KIND,
						LA_LabelID,	MSG_GLOBAL_USE_GAD,
						LA_ID,		GAD_USE,
						LABT_ReturnKey,	TRUE,
						LABT_ExtraFat,	TRUE,
					TAG_DONE);

					LT_New(Handle,
						LA_Type,	BUTTON_KIND,
						LA_LabelID,	MSG_GLOBAL_CANCEL_GAD,
						LA_ID,		GAD_CANCEL,
						LABT_EscKey,	TRUE,
						LABT_ExtraFat,	TRUE,
					TAG_DONE);

					LT_EndGroup(Handle);
				}

				LT_EndGroup(Handle);
			}

			if(Window = LT_Layout(Handle,OptionTitle ? OptionTitle : LocaleString(MSG_V36_1840),NULL,0,0,IDCMP_CLOSEWINDOW,0,
				LAWN_HelpHook,		&GuideHook,
				WA_DepthGadget,		TRUE,
				WA_CloseGadget,		TRUE,
				WA_DragBar,		TRUE,
				WA_RMBTrap,		TRUE,
				WA_Activate,		TRUE,
			TAG_DONE))
			{
				struct IntuiMessage	*Message;
				BOOLEAN			 Done = FALSE;
				ULONG			 MsgClass,
							 MsgQualifier;
				UWORD			 MsgCode;
				struct Gadget		*MsgGadget;
				BOOLEAN			 CheckFlags = FALSE;

				PushWindow(Window);

				LT_ShowWindow(Handle,TRUE);

				do
				{
					if(Wait(PORTMASK(Window -> UserPort) | SIG_BREAK) & SIG_BREAK)
						break;

					while(Message = (struct IntuiMessage *)GT_GetIMsg(Window -> UserPort))
					{
						MsgClass	= Message -> Class;
						MsgQualifier	= Message -> Qualifier;
						MsgCode		= Message -> Code;
						MsgGadget	= (struct Gadget *)Message -> IAddress;

						GT_ReplyIMsg(Message);

						LT_HandleInput(Handle,MsgQualifier,&MsgClass,&MsgCode,&MsgGadget);

						if(MsgClass == IDCMP_CLOSEWINDOW)
							Done = TRUE;

						if(MsgClass == IDCMP_GADGETUP)
						{
							switch(MsgGadget -> GadgetID)
							{
								case GAD_USE:

									Done = CheckFlags = TRUE;
									break;

								case GAD_CANCEL:

									Done = TRUE;
									break;

								default:

									if(MsgGadget -> GadgetID - GAD_SPECIAL < NumOpts)
									{
										i = MsgGadget -> GadgetID - GAD_SPECIAL;

										if(Opts[i] -> xemo_type == XEMO_COMMAND || (Opts[i] -> xemo_type == XEMO_COMMPAR && MsgCode != '\t'))
										{
											Flags = (1L << i);

											Done = CheckFlags = TRUE;
										}
									}

									break;
							}
						}
					}
				}
				while(!Done);

				PopWindow();

				if(CheckFlags)
				{
					LT_LockWindow(Window);

					for(i = 0 ; i < NumOpts ; i++)
					{
						if(Opts[i])
						{
							switch(Opts[i] -> xemo_type)
							{
								case XEMO_BOOLEAN:

									if(LT_GetAttributes(Handle,GAD_SPECIAL + i,TAG_DONE) != GetOptionMode(Opts[i]))
									{
										Flags |= (1L << i);

										if(LT_GetAttributes(Handle,GAD_SPECIAL + i,TAG_DONE))
											strcpy(Opts[i] -> xemo_value,"yes");
										else
											strcpy(Opts[i] -> xemo_value,"no");
									}

									break;

								case XEMO_LONG:

									if(Atol(Opts[i] -> xemo_value) != LT_GetAttributes(Handle,GAD_SPECIAL + i,TAG_DONE))
									{
										Flags |= (1L << i);

										SPrintf(Opts[i] -> xemo_value,"%ld",LT_GetAttributes(Handle,GAD_SPECIAL + i,TAG_DONE));
									}

									break;

								case XEMO_COMMPAR:
								case XEMO_STRING:

									if(strcmp(Opts[i] -> xemo_value,(STRPTR)LT_GetAttributes(Handle,GAD_SPECIAL + i,TAG_DONE)))
									{
										Flags |= (1L << i);

										strcpy(Opts[i] -> xemo_value,(STRPTR)LT_GetAttributes(Handle,GAD_SPECIAL + i,TAG_DONE));
									}

									break;
							}
						}
					}

					LT_UnlockWindow(Window);
				}
				else
					Flags = NULL;
			}

			LT_DeleteHandle(Handle);
		}

		return(Flags);
	}
	else
		return(NULL);
}

	/* xem_swrite():
	 *
	 *	Send a few bytes across the serial line.
	 */

STATIC LONG __saveds __asm
xem_swrite(register __a0 STRPTR Buffer,register __d0 LONG Size)
{
	if(WriteRequest)
	{
		SerWrite(Buffer,Size);

		return(0);
	}
	else
		return(-1);
}

	/* xem_sbreak():
	 *
	 *	Send a break signal across the serial line.
	 */

STATIC LONG __asm __saveds
xem_sbreak(VOID)
{
	if(!WriteRequest)
		return(-1);
	else
	{
		SendBreak();

		return(0);
	}
}

	/* xem_sstart():
	 *
	 *	Restart serial read activity.
	 */

STATIC VOID __asm __saveds
xem_sstart(VOID)
{
	RestartSerial(FALSE);
}

	/* xem_sstop():
	 *
	 *	Stop serial read activity.
	 */

STATIC LONG __asm __saveds
xem_sstop(VOID)
{
	if(ReadRequest)
	{
		if(!CheckIO(ReadRequest))
			AbortIO(ReadRequest);

		WaitIO(ReadRequest);
	}

	return(0);
}

	/* xem_tgets(STRPTR Prompt,STRPTR Buffer,ULONG Size):
	 *
	 *	Get a string from the user.
	 */

STATIC LONG __saveds __asm
xem_tgets(register __a0 STRPTR Prompt,register __a1 STRPTR Buffer,register __d0 ULONG Size)
{
	enum	{	GAD_OK=1,GAD_CANCEL,GAD_STRING };

	struct LayoutHandle	*Handle;
	LONG			 Success = FALSE;
	UBYTE			 LocalBuffer[256];

	if(strlen(Buffer) > 255)
	{
		CopyMem(Buffer,LocalBuffer,255);

		LocalBuffer[255] = 0;
	}
	else
		strcpy(LocalBuffer,Buffer);

	if(!Prompt)
		Prompt = LocaleString(MSG_TERMXPR_INPUT_REQUIRED_TXT);

	if(Handle = LT_CreateHandleTags(Window -> WScreen,
		LH_LocaleHook,	&LocaleHook,
	TAG_DONE))
	{
		struct Window *Window;

		LT_New(Handle,
			LA_Type,	VERTICAL_KIND,
		TAG_DONE);
		{
			LT_New(Handle,
				LA_Type,	VERTICAL_KIND,
				LA_LabelText,	Prompt,
			TAG_DONE);
			{
				LT_New(Handle,
					LA_Type,	STRING_KIND,
					LA_STRPTR,	LocalBuffer,
					LA_Chars,	30,
					GTST_MaxChars,	Size,
				TAG_DONE);

				LT_EndGroup(Handle);
			}

			LT_New(Handle,
				LA_Type,VERTICAL_KIND,
			TAG_DONE);
			{
				LT_New(Handle,
					LA_Type,	XBAR_KIND,
					LAXB_FullSize,	TRUE,
				TAG_DONE);

				LT_EndGroup(Handle);
			}

			LT_New(Handle,LA_Type,HORIZONTAL_KIND,
				LAGR_SameSize,	TRUE,
				LAGR_Spread,	TRUE,
			TAG_DONE);
			{
				LT_New(Handle,
					LA_Type,	BUTTON_KIND,
					LA_LabelID,	MSG_TERMXPR_OKAY_GAD,
					LA_ID,		GAD_OK,
					LABT_ReturnKey,	TRUE,
					LABT_ExtraFat,	TRUE,
				TAG_DONE);

				LT_New(Handle,
					LA_Type,	BUTTON_KIND,
					LA_LabelID,	MSG_GLOBAL_CANCEL_GAD,
					LA_ID,		GAD_CANCEL,
					LABT_EscKey,	TRUE,
					LABT_ExtraFat,	TRUE,
				TAG_DONE);

				LT_EndGroup(Handle);
			}
		}

		if(Window = LT_Layout(Handle,LocaleString(MSG_GLOBAL_ENTER_TEXT_TXT),NULL,0,0,IDCMP_CLOSEWINDOW,0,
			LAWN_HelpHook,		&GuideHook,
			WA_DepthGadget,		TRUE,
			WA_CloseGadget,		TRUE,
			WA_DragBar,		TRUE,
			WA_RMBTrap,		TRUE,
			WA_Activate,		TRUE,
		TAG_DONE))
		{
			struct IntuiMessage	*Message;
			BOOLEAN			 Done = FALSE;
			ULONG			 MsgClass,
						 MsgQualifier;
			UWORD			 MsgCode;
			struct Gadget		*MsgGadget;

			PushWindow(Window);

			LT_ShowWindow(Handle,TRUE);

			LT_Activate(Handle,GAD_STRING);

			do
			{
				if(Wait(PORTMASK(Window -> UserPort) | SIG_BREAK) & SIG_BREAK)
					break;

				while(Message = (struct IntuiMessage *)GT_GetIMsg(Window -> UserPort))
				{
					MsgClass	= Message -> Class;
					MsgQualifier	= Message -> Qualifier;
					MsgCode		= Message -> Code;
					MsgGadget	= (struct Gadget *)Message -> IAddress;

					GT_ReplyIMsg(Message);

					LT_HandleInput(Handle,MsgQualifier,&MsgClass,&MsgCode,&MsgGadget);

					if(MsgClass == IDCMP_CLOSEWINDOW)
						Done = TRUE;

					if(MsgClass == IDCMP_GADGETUP)
					{
						switch(MsgGadget -> GadgetID)
						{
							case GAD_STRING:

								if(MsgCode == '\r')
								{
									strcpy(Buffer,LocalBuffer);

									Success = Done = TRUE;

									LT_PressButton(Handle,GAD_OK);
								}

								break;

							case GAD_OK:

								strcpy(Buffer,LocalBuffer);

								Success = Done = TRUE;
								break;

							case GAD_CANCEL:

								Done = TRUE;
								break;
						}
					}
				}
			}
			while(!Done);

			PopWindow();
		}

		LT_DeleteHandle(Handle);
	}

	return(Success);
}

	/* xem_tbeep(ULONG Times,ULONG Delay):
	 *
	 *	Beep the terminal display.
	 */

STATIC VOID __saveds __asm
xem_tbeep(register __d0 ULONG Times,register __d1 ULONG Delay)
{
	WORD i;

	for(i = 0 ; i < Times ; i++)
	{
			/* Handle the visual part. */

		if(Config -> TerminalConfig -> BellMode != BELL_AUDIBLE)
		{
			if(StatusProcess)
				Signal(StatusProcess,SIG_BELL);
		}

			/* Let it beep. */

		if(Config -> TerminalConfig -> BellMode == BELL_AUDIBLE || Config -> TerminalConfig -> BellMode == BELL_BOTH)
			SoundPlay(SOUND_BELL);
	}
}

	/* xem_macrodispatch(struct XEmulatorMacroKey *XEM_MacroKey):
	 *
	 *	Dispatch a macro key call.
	 */

STATIC LONG __saveds __asm
xem_macrodispatch(register __a0 struct XEmulatorMacroKey *XEM_MacroKey)
{
	VOID (*Routine)(VOID);

		/* If a routine to call is available (most likely xON or xOFF),
		 * make a call to it, else process the macro key data.
		 */

	if(Routine = (VPTR)XEM_MacroKey -> xmk_UserData)
		(*Routine)();
	else
		SerialCommand(MacroKeys -> Keys[XEM_MacroKey -> xmk_Qualifier][XEM_MacroKey -> xmk_Code - 0x50]);

	return(0);
}

	/* SetEmulatorOptions(BYTE Mode):
	 *
	 *	Save or load the emulator options.
	 */

BYTE __regargs
SetEmulatorOptions(BYTE Mode)
{
	BYTE Success = FALSE;

		/* Is the library available and running? */

	if(XEmulatorBase && XEM_IO)
	{
			/* Are we using the new library code? */

		if(XEmulatorBase -> lib_Version >= 4)
		{
				/* Get the name of the library. */

			strcpy(SharedBuffer,FilePart(XEmulatorBase -> lib_Node . ln_Name));

				/* Does it have any name? */

			if(SharedBuffer[0])
			{
				UBYTE	OtherBuffer[50];
				WORD	i;

					/* Strip the `.library' bit. */

				for(i = strlen(SharedBuffer) - 1 ; i >= 0 ; i--)
				{
					if(SharedBuffer[i] == '.')
					{
						SharedBuffer[i] = 0;

						break;
					}
				}

					/* What are we to do? */

				if(Mode == XEM_PREFS_LOAD)
				{
						/* Restore settings... */

					strcpy(OtherBuffer,"ENV:");

					if(AddPart(OtherBuffer,SharedBuffer,50))
					{
							/* If we can't load them,
							 * reset to defaults.
							 */

						if(!XEmulatorPreferences(XEM_IO,OtherBuffer,Mode))
						{
							strcpy(OtherBuffer,"ENV:xem");

							if(AddPart(OtherBuffer,SharedBuffer,50))
							{
								if(XEmulatorPreferences(XEM_IO,OtherBuffer,Mode))
									Success = TRUE;
							}
						}
						else
							Success = TRUE;

						if(!Success)
							XEmulatorPreferences(XEM_IO,NULL,XEM_PREFS_RESET);
					}
				}
				else
				{
						/* Save settings to ENV: */

					strcpy(OtherBuffer,"ENV:");

					if(AddPart(OtherBuffer,SharedBuffer,50))
					{
						if(XEmulatorPreferences(XEM_IO,OtherBuffer,Mode))
							Success = TRUE;
					}

					if(Success)
					{
						Success = FALSE;

							/* Save settings to ENVARC: */

						strcpy(OtherBuffer,"ENVARC:");

						if(AddPart(OtherBuffer,SharedBuffer,50))
						{
							if(XEmulatorPreferences(XEM_IO,OtherBuffer,Mode))
								Success = TRUE;
						}
					}
				}
			}
		}
	}

		/* Return result. */

	return(Success);
}

	/* SetupEmulator(BYTE OpenConsole):
	 *
	 *	Initialize the XEM_IO structure.
	 */

STATIC BYTE
SetupEmulator(VOID)
{
	if(!XEM_IO)
	{
		if(XEM_IO = (struct XEM_IO *)AllocVec(sizeof(struct XEM_IO),MEMF_ANY | MEMF_CLEAR | MEMF_PUBLIC))
		{
			XEM_IO -> xem_window		= Window;
			XEM_IO -> xem_font		= CurrentFont;
			XEM_IO -> xem_signal		= &XEM_Signal;
			XEM_IO -> xem_screendepth	= Window -> WScreen -> RastPort . BitMap -> Depth;

			XEM_IO -> xem_sread		= xem_sread;
			XEM_IO -> xem_swrite		= xem_swrite;
			XEM_IO -> xem_sflush		= xem_sflush;
			XEM_IO -> xem_sbreak		= xem_sbreak;
			XEM_IO -> xem_squery		= xem_squery;
			XEM_IO -> xem_sstart		= xem_sstart;
			XEM_IO -> xem_sstop		= xem_sstop;

			XEM_IO -> xem_tbeep		= xem_tbeep;
			XEM_IO -> xem_tgets		= xem_tgets;
			XEM_IO -> xem_toptions		= xem_toptions;

			XEM_IO -> xem_process_macrokeys	= xem_macrodispatch;

			return(TRUE);
		}
	}
	else
		return(FALSE);

	return(FALSE);
}

	/* CloseEmulator():
	 *
	 *	Close the emulation library.
	 */

VOID
CloseEmulator()
{
	if(XEmulatorBase)
	{
		if(XEM_IO)
		{
			XEmulatorMacroKeyFilter(XEM_IO,NULL);
			XEmulatorCloseConsole(XEM_IO);
			XEmulatorCleanup(XEM_IO);
		}

		CloseLibrary(XEmulatorBase);

		if(XEM_IO)
			FreeVec(XEM_IO);

		strcpy(EmulationName,LocaleString(MSG_TERMXEM_NO_EMULATION_TXT));

		XEmulatorBase	= NULL;
		XEM_Signal	= NULL;
		XEM_IO		= NULL;

		RasterEnabled = TRUE;

		ClearCursor();

		Reset();

		DrawCursor();
	}
}

	/* OpenEmulator(STRPTR Name):
	 *
	 *	Open an emulation library.
	 */

BYTE __regargs
OpenEmulator(STRPTR Name)
{
	CloseEmulator();

	XEM_HostData . Source		= NULL;
	XEM_HostData . Destination	= NULL;
	XEM_HostData . InESC		= FALSE;
	XEM_HostData . InCSI		= FALSE;

	if(XEmulatorBase = OpenLibrary(Name,0))
	{
		ClearCursor();

		Reset();

		if(SetupEmulator())
		{
			SetWrMsk(RPort,DepthMask);

			ClearSerial();

			if(XEmulatorSetup(XEM_IO))
			{
				RestartSerial(FALSE);

				SetEmulatorOptions(XEM_PREFS_LOAD);

				if(XEmulatorOpenConsole(XEM_IO))
				{
					STRPTR LibName = FilePart(XEmulatorBase -> lib_Node . ln_Name);

					strcpy(EmulationName,&LibName[3]);

					EmulationName[strlen(EmulationName) - 8] = 0;

					SetupXEM_MacroKeys(MacroKeys);

					return(TRUE);
				}
			}
			else
				RestartSerial(FALSE);
		}

		DrawCursor();

		CloseLibrary(XEmulatorBase);

		strcpy(EmulationName,LocaleString(MSG_TERMXEM_NO_EMULATION_TXT));

		XEmulatorBase	= NULL;
		XEM_Signal	= NULL;
	}

	return(FALSE);
}

	/* XOff():
	 *
	 *	Small local routine, complements XOn() in Serial.c
	 */

STATIC VOID
XOff(VOID)
{
	if(Status == STATUS_HOLDING)
	{
		UBYTE c = XOF;

		SerWrite(&c,1);

		Status = STATUS_READY;
	}
}

	/* SetupXEM_MacroKeys(struct MacroKeys *Keys):
	 *
	 *	Sets up the internal representation of the macro key
	 *	data to fit the XEM specification.
	 */

VOID __regargs
SetupXEM_MacroKeys(struct MacroKeys *Keys)
{
		/* Are we allowed to do what we want to do? */

	if(XEM_MacroKeys && XEmulatorBase && Config -> TerminalConfig -> EmulationMode == EMULATION_EXTERNAL)
	{
		WORD i,j,k = 0;

			/* Clear the macro list. */

		NewList(&XEM_MacroList);

			/* Run down the list of qualifiers. */

		for(i = XMKQ_NONE ; i <= XMKQ_CONTROL ; i++)
		{
				/* Run down the function keys. */

			for(j = 0 ; j < 10 ; j++)
			{
					/* If the key has no data attached,
					 * don't use it in the list.
					 */

				if(Keys -> Keys[i][j][0])
				{
					XEM_MacroKeys[k] . xmk_Type		= XMKT_RAWKEY;
					XEM_MacroKeys[k] . xmk_Qualifier	= i;
					XEM_MacroKeys[k] . xmk_Code		= 0x50 + j;
					XEM_MacroKeys[k] . xmk_UserData		= NULL;

					AddTail(&XEM_MacroList,(struct Node *)&XEM_MacroKeys[k++]);
				}
			}
		}

			/* Take care of the rest, add support for the xON key. */

		XEM_MacroKeys[k] . xmk_Type 		= XMKT_COOKED;
		XEM_MacroKeys[k] . xmk_Qualifier	= NULL;
		XEM_MacroKeys[k] . xmk_Code		= XON;
		XEM_MacroKeys[k] . xmk_UserData		= (APTR)DoxON;

		AddTail(&XEM_MacroList,(struct Node *)&XEM_MacroKeys[k++]);

			/* Take care of the xOFF key. */

		XEM_MacroKeys[k] . xmk_Type 		= XMKT_COOKED;
		XEM_MacroKeys[k] . xmk_Qualifier	= NULL;
		XEM_MacroKeys[k] . xmk_Code		= XOF;
		XEM_MacroKeys[k] . xmk_UserData		= (APTR)XOff;

		AddTail(&XEM_MacroList,(struct Node *)&XEM_MacroKeys[k]);

			/* Make the emulator notice the new settings. */

		XEmulatorMacroKeyFilter(XEM_IO,&XEM_MacroList);
	}
}
