/*
**	termXEM.c
**
**	External emulation support routines
**
**	Copyright © 1990-1995 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)
{
	return((LONG)FlushSerialRead());
}

STATIC LONG __saveds __asm
xem_squery(VOID)
{
	if(WriteRequest)
	{
		ULONG	Waiting;
		UWORD	Status;

		GetSerialInfo(&Waiting,&Status);

			/* Return error if carrier is lost. */

		if((Status & CIAF_COMCD) && Config -> SerialConfig -> CheckCarrier && !Config -> SerialConfig -> DirectConnection)
		{
			ObtainSemaphore(&OnlineSemaphore);

			if(Online)
			{
				WasOnline	= Online;
				Online		= FALSE;
			}

			ReleaseSemaphore(&OnlineSemaphore);
		}
		else
			return((LONG)Waiting);
	}

	return(-1);
}

STATIC LONG __saveds __asm
xem_sread(register __a0 STRPTR Buffer,register __d0 LONG Size,register __d1 ULONG Timeout)
{
		/* Valid size parameter? */

	if(Size > 0)
	{
		LONG Total = 0;

			/* Return error if carrier is lost. */

		if(Config -> SerialConfig -> CheckCarrier && !Config -> SerialConfig -> DirectConnection)
		{
			if(GetSerialStatus() & CIAF_COMCD)
			{
				ObtainSemaphore(&OnlineSemaphore);

				if(Online)
				{
					WasOnline	= Online;
					Online		= FALSE;
				}

				ReleaseSemaphore(&OnlineSemaphore);

				return(-1);
			}
		}

			// Did the read request terminate, is data available?

		if(CheckSerialRead())
		{
				// Pick it up...

			if(WaitSerialRead())
			{
				RestartSerial();

				return(0);
			}

				// One byte was read

			*Buffer++ = ReadBuffer[0];
			Size--;
			BytesIn++;
			Total++;

				// Is there still something to be read?

			if(Size > 0)
			{
				ULONG	Waiting;
				LONG	Result;

					// Check how many bytes are still
					// waiting to be read

				if(Waiting = GetSerialWaiting())
				{
						// Don't read too many

					if(Waiting > Size)
						Waiting = Size;

					if(DoSerialRead(Buffer,Waiting))
						Result = 0;
					else
						Result = Waiting;
				}
				else
					Result = 0;

				if(Result > 0)
				{
					Buffer	+= Result;
					Size	-= Result;
					BytesIn	+= Result;
					Total	+= Result;
				}

				Result++;

				RestartSerial();

					// Now check if there is a timeout value
					// given. If there is, we need to fill the
					// buffer with as many bytes as there are
					// requested and may not be able to return
					// right now. We will return if enough
					// data was read.

				if(!Timeout || !Size)
					return(Result);
			}
			else
			{
				RestartSerial();

				return(1);
			}
		}
		else
		{
				// No data is available and no timeout
				// is given. We can't deliver anything,
				// so let's scram.

			if(!Timeout)
				return(0);
		}

			// At this point we still need to read some data
			// and a timeout is given.

		/* ALWAYS */
		{
			register ULONG	SerialMask = PORTMASK(ReadPort),
					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;

				/* Prevent early termination. */

			ClrSignal(SIG_TIMER);

				/* Start IO... */

			SendIO(TimeRequest);

			FOREVER
			{
				SignalSet = Wait(SerialMask | SIG_TIMER);

					/* Receive buffer filled? */

				if(SignalSet & SerialMask)
				{
						/* Did the request terminate gracefully? */

					if(WaitSerialRead())
						RestartSerial();
					else
					{
							// One byte was read

						*Buffer++ = ReadBuffer[0];
						Size--;
						BytesIn++;
						Total++;

							// Is there still something to be read?

						if(Size > 0)
						{
							ULONG	Waiting;
							LONG	Result;

								// Check how many bytes are still
								// waiting to be read

							if(Waiting = GetSerialWaiting())
							{
									// Don't read too many

								if(Waiting > Size)
									Waiting = Size;

								if(DoSerialRead(Buffer,Waiting))
									Result = 0;
								else
									Result = Waiting;
							}
							else
								Result = 0;

								// Did we get an error?

							if(Result > 0)
							{
								Buffer	+= Result;
								Size	-= Result;
								BytesIn	+= Result;
								Total	+= Result;
							}
						}

						RestartSerial();

						if(!Size)
						{
								/* Abort the timer request. */

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

							WaitIO(TimeRequest);

							return(Total);
						}
					}
				}

					/* Hit by timeout? */

				if(SignalSet & SIG_TIMER)
				{
						/* Remove the timer request. */

					WaitIO(TimeRequest);

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

					if(CheckSerialRead())
					{
							/* Did the request terminate gracefully? */

						if(WaitSerialRead())
							RestartSerial();
						else
						{
								// One byte was read

							*Buffer++ = ReadBuffer[0];
							Size--;
							BytesIn++;
							Total++;

								// Is there still something to be read?

							if(Size > 0)
							{
								ULONG	Waiting;
								LONG	Result;

									// Check how many bytes are still
									// waiting to be read

								if(Waiting = GetSerialWaiting())
								{
										// Don't read too many

									if(Waiting > Size)
										Waiting = Size;

									if(DoSerialRead(Buffer,Waiting))
										Result = 0;
									else
										Result = Waiting;
								}
								else
									Result = 0;

									// Did we get an error?

								if(Result > 0)
								{
									Size	-= Result;
									BytesIn	+= Result;
									Total	+= Result;
								}
							}

							RestartSerial();
						}
					}

					return(Total);
				}
			}
		}
	}
	else
		return(0);
}

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	*PanelWindow;
			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,		15,
											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,	15,
											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,	15,
											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,	15,
										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(PanelWindow = LT_Build(Handle,
				LAWN_TitleText,		OptionTitle ? OptionTitle : LocaleString(MSG_V36_1840),
				LAWN_IDCMP,		IDCMP_CLOSEWINDOW,
				LAWN_HelpHook,		&GuideHook,
				LAWN_Parent,		Window,
				WA_DepthGadget,		TRUE,
				WA_CloseGadget,		TRUE,
				WA_DragBar,		TRUE,
				WA_RMBTrap,		TRUE,
				WA_Activate,		TRUE,
				WA_SimpleRefresh,	TRUE,
			TAG_DONE))
			{
				struct IntuiMessage	*Message;
				BOOLEAN			 Done = FALSE;
				ULONG			 MsgClass;
				UWORD			 MsgCode;
				struct Gadget		*MsgGadget;
				BOOLEAN			 CheckFlags = FALSE;

				PushWindow(PanelWindow);

				LT_ShowWindow(Handle,TRUE);

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

					while(Message = (struct IntuiMessage *)LT_GetIMsg(Handle))
					{
						MsgClass	= Message -> Class;
						MsgCode		= Message -> Code;
						MsgGadget	= (struct Gadget *)Message -> IAddress;

						LT_ReplyIMsg(Message);

						if(MsgClass == IDCMP_CLOSEWINDOW)
							Done = TRUE;

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

									LT_UpdateStrings(Handle);

									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)
				{
					STRPTR String;

					LT_LockWindow(PanelWindow);

					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");

										NewOptions = TRUE;
									}

									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));

										NewOptions = TRUE;
									}

									break;

								case XEMO_COMMPAR:
								case XEMO_STRING:

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

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

											NewOptions = TRUE;
										}
									}

									break;
							}
						}
					}

					LT_UnlockWindow(PanelWindow);
				}
				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();
}

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

STATIC LONG __asm __saveds
xem_sstop(VOID)
{
	StopSerialRead();

	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 *PanelWindow;

		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(PanelWindow = LT_Build(Handle,
			LAWN_TitleID,		MSG_GLOBAL_ENTER_TEXT_TXT,
			LAWN_IDCMP,		IDCMP_CLOSEWINDOW,
			LAWN_HelpHook,		&GuideHook,
			LAWN_Parent,		Window,
			WA_DepthGadget,		TRUE,
			WA_CloseGadget,		TRUE,
			WA_DragBar,		TRUE,
			WA_RMBTrap,		TRUE,
			WA_Activate,		TRUE,
			WA_SimpleRefresh,	TRUE,
		TAG_DONE))
		{
			struct IntuiMessage	*Message;
			BOOLEAN			 Done = FALSE;
			ULONG			 MsgClass;
			UWORD			 MsgCode;
			struct Gadget		*MsgGadget;

			PushWindow(PanelWindow);

			LT_ShowWindow(Handle,TRUE);

			LT_Activate(Handle,GAD_STRING);

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

				while(Message = (struct IntuiMessage *)LT_GetIMsg(Handle))
				{
					MsgClass	= Message -> Class;
					MsgCode		= Message -> Code;
					MsgGadget	= (struct Gadget *)Message -> IAddress;

					LT_ReplyIMsg(Message);

					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	= GetBitMapDepth(Window -> WScreen -> RastPort . BitMap);

			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())
		{
			SetMask(RPort,DepthMask);

			ClearSerial();

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

				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();
		}

		DrawCursor();

		CloseLibrary(XEmulatorBase);
	}

	strcpy(EmulationName,LocaleString(MSG_TERMXEM_NO_EMULATION_TXT));

	XEmulatorBase	= NULL;
	XEM_Signal	= NULL;

	return(FALSE);
}

	/* xOn():
	 *
	 *	Small local routine, complements XOff().
	 */

STATIC VOID
xOn(VOID)
{
	UBYTE c = XON;

	if(Config -> SerialConfig -> xONxOFF)
		Status = STATUS_HOLDING;

	if(Config -> SerialConfig -> PassThrough)
		SerWrite(&c,1);
}

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

STATIC VOID
xOff(VOID)
{
	UBYTE c = XOF;

	if(Status == STATUS_HOLDING)
		Status = STATUS_READY;

	if(Config -> SerialConfig -> PassThrough)
		SerWrite(&c,1);
}

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

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