/*
**	termPacket.c
**
**	Support routines for the `packet window'
**
**	Copyright © 1990-1994 by Olaf `Olsen' Barthel
**		All Rights Reserved
*/

#include "termGlobal.h"

	/* A custom message structure. */

struct PacketMessage
{
	struct Message	 VanillaMessage;
	LONG		 Line;
};

	/* Some private data required to handle both the window,
	 * the editing functions and the command history buffer.
	 */

STATIC struct Hook		 PacketHook;
STATIC STRPTR			 PacketContents;

STATIC struct Process		*PacketProcess;
STATIC struct SignalSemaphore	 PacketSemaphore;
STATIC struct MsgPort		*PacketPort;

STATIC LONG			 PacketLine;
STATIC LONG			 PacketCount;
STATIC STRPTR			 PacketString	= NULL;
STATIC LONG			 PacketX	= -1,
				 PacketY	= -1,
				 PacketWidth	= -1,
				 PacketHeight	= -1;

STATIC BYTE			 HasList	= FALSE,
				 Echo		= -1;

	/* Gadget IDs */

enum	{	GAD_STRING, GAD_LIST };

	/* The menu to attach to the packet window. */

enum	{	MEN_LOADHISTORY=1,MEN_SAVEHISTORY,MEN_CLEARHISTORY,MEN_OTHERWINDOW,
		MEN_TOGGLE_ECHO,MEN_QUITPANEL
	};

STATIC struct NewMenu NewPacketMenu[] =
{
	{ NM_TITLE, NULL,		 0 , 0,		0, (APTR)0},
	{  NM_ITEM, NULL,		 0 , 0,		0, (APTR)MEN_LOADHISTORY},
	{  NM_ITEM, NULL,		 0 , 0,		0, (APTR)MEN_SAVEHISTORY},
	{  NM_ITEM, NULL,		 0 , 0,		0, (APTR)MEN_CLEARHISTORY},
	{  NM_ITEM, NM_BARLABEL,	 0 , 0,		0, (APTR)0},
	{  NM_ITEM, NULL,		 0 , 0,		0, (APTR)MEN_OTHERWINDOW},
	{  NM_ITEM, NM_BARLABEL,	 0 , 0,		0, (APTR)0},
	{  NM_ITEM, NULL,		 0 , TICK,	0, (APTR)MEN_TOGGLE_ECHO},
	{  NM_ITEM, NM_BARLABEL,	 0 , 0,		0, (APTR)0},
	{  NM_ITEM, NULL,		 0 , 0,		0, (APTR)MEN_QUITPANEL},
	{ NM_END, 0,			 0 , 0,		0, (APTR)0}
};

STATIC VOID __stdargs
PacketDestructor(struct MsgItem *Item)
{
	Signal(PacketProcess,SIG_HANDSHAKE);
}

STATIC VOID __regargs
PacketSerWrite(APTR Data,LONG Size)
{
	struct DataMsg Msg;

	InitMsgItem(&Msg,PacketDestructor);

	Msg . Type = DATAMSGTYPE_WRITE;
	Msg . Data = Data;
	Msg . Size = Size;

	Forbid();

	ClrSignal(SIG_HANDSHAKE);

	PutMsgItem(SpecialQueue,(struct MsgItem *)&Msg);

	Wait(SIG_HANDSHAKE);

	Permit();
}

STATIC VOID __regargs
PacketSerialCommand(STRPTR String)
{
	struct DataMsg Msg;

	InitMsgItem(&Msg,PacketDestructor);

	if(Echo)
		Msg . Type = DATAMSGTYPE_SERIALCOMMAND;
	else
		Msg . Type = DATAMSGTYPE_SERIALCOMMANDNOECHO;

	Msg . Data = String;

	Forbid();

	ClrSignal(SIG_HANDSHAKE);

	PutMsgItem(SpecialQueue,(struct MsgItem *)&Msg);

	Wait(SIG_HANDSHAKE);

	Permit();
}

	/* SendPacketMsg(LONG Line):
	 *
	 *	Tell the main process to display a certain line.
	 */

STATIC VOID __regargs
SendPacketMsg(LONG Line)
{
	struct PacketMessage *Msg;

	if(Msg = (struct PacketMessage *)AllocVecPooled(sizeof(struct PacketMessage),MEMF_PUBLIC | MEMF_CLEAR))
	{
		Msg -> VanillaMessage . mn_Length	= sizeof(struct PacketMessage);
		Msg -> Line				= Line;

		PutMsg(PacketPort,Msg);
	}
}

	/* CreateAllGadgets():
	 *
	 *	Create the packet string gadget.
	 */

STATIC struct Gadget * __regargs
CreateAllGadgets(LONG Width,struct Gadget **GadgetArray,struct Gadget **GadgetList,APTR VisualInfo)
{
	struct Gadget		*Gadget;
	struct NewGadget	 NewGadget;

	ObtainSemaphore(&PacketSemaphore);

	memset(&NewGadget,0,sizeof(struct NewGadget));

	if(Gadget = CreateContext(GadgetList))
	{
		WORD Rest,StringHeight,ProtoHeight;

		StringHeight	= 3 + TextAttr . tta_YSize + 3;
		ProtoHeight	= Window -> WScreen -> WBorTop + Window -> WScreen -> Font -> ta_YSize + 1 + 2 + StringHeight + 2;
		Rest		= (PacketHeight - 2 - ProtoHeight - 2) / TextAttr . tta_YSize;

		NewGadget . ng_Width		= Width - 26;
		NewGadget . ng_Height		= StringHeight;
		NewGadget . ng_GadgetText	= NULL;
		NewGadget . ng_TextAttr		= &TextAttr;
		NewGadget . ng_VisualInfo	= VisualInfo;
		NewGadget . ng_GadgetID		= GAD_STRING;
		NewGadget . ng_Flags		= 0;
		NewGadget . ng_LeftEdge		= 6;
		NewGadget . ng_TopEdge		= Window -> WScreen -> WBorTop + Window -> WScreen -> Font -> ta_YSize + 2;

		GadgetArray[GAD_STRING] = Gadget = CreateGadget(STRING_KIND,Gadget,&NewGadget,
			GTST_MaxChars,	1024,
			GTST_EditHook,	&PacketHook,
			GA_TabCycle,	FALSE,
		TAG_DONE);

			/* If the window is large enough to display the
			 * list, create the list view gadget.
			 */

		if(Rest > 0)
		{
			NewGadget . ng_Height		= 2 + Rest * TextAttr . tta_YSize + 2 + StringHeight;
			NewGadget . ng_GadgetID		= GAD_LIST;

			GadgetArray[GAD_LIST] = Gadget = CreateGadget(LISTVIEW_KIND,Gadget,&NewGadget,
				GTLV_ShowSelected,	GadgetArray[GAD_STRING],
				GTLV_Labels,		&PacketHistoryList,
				GTLV_Selected,		PacketCount,
				GTLV_Top,		PacketCount,
			TAG_DONE);

			if(Gadget)
				HasList = TRUE;
			else
				HasList = FALSE;
		}
		else
			HasList = FALSE;
	}

	ReleaseSemaphore(&PacketSemaphore);

	return(Gadget);
}

	/* PacketKey(struct Hook *Hook,struct SGWork *Work,ULONG *Msg):
	 *
	 *	This is a string gadget editing call back routine
	 *	(a so-called `hook') which is to perform all the
	 *	line editing and command history actions supported
	 *	by the packet window.
	 */

STATIC ULONG __saveds __asm
PacketKey(register __a0 struct Hook *Hook,register __a1 ULONG *Msg,register __a2 struct SGWork *Work)
{
		/* Someone activated the string gadget and
		 * hit a key.
		 */

	if(*Msg == SGH_KEY)
	{
			/* Right-Amiga-key was pressed, release the
			 * string gadget so user may select a menu
			 * item.
			 */

		if((Work -> IEvent -> ie_Qualifier & AMIGARIGHT) && Work -> IEvent -> ie_Code < 96)
		{
			if(!(Work -> IEvent -> ie_Qualifier & (IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT)) && (Work -> IEvent -> ie_Code == KEYCODE_X || Work -> IEvent -> ie_Code == KEYCODE_Q))
				return(TRUE);
			else
			{
				Work -> Actions = (Work -> Actions & ~(SGA_USE | SGA_BEEP)) | SGA_END | SGA_REUSE;
				Work -> Code = '\t';

				CommandWindow = Work -> GadgetInfo -> gi_Window;
				CommandGadget = Work -> Gadget;
			}
		}

		if(Work -> IEvent -> ie_Code >= F01_CODE && Work -> IEvent -> ie_Code <= F10_CODE)
		{
			STATIC UBYTE __far OtherBuffer[2048];

			STRPTR	String;
			LONG	Len,Index = Work -> IEvent -> ie_Code - F01_CODE;

			Forbid();

			if(Work -> IEvent -> ie_Qualifier & IEQUALIFIER_CONTROL)
				String = MacroKeys -> Keys[3][Index];
			else
			{
				if(Work -> IEvent -> ie_Qualifier & (IEQUALIFIER_LALT | IEQUALIFIER_RALT))
					String = MacroKeys -> Keys[2][Index];
				else
				{
					if(Work -> IEvent -> ie_Qualifier & (IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT))
						String = MacroKeys -> Keys[1][Index];
					else
						String = MacroKeys -> Keys[0][Index];
				}
			}

			if(Len = strlen(String))
			{
				if(Work -> BufferPos)
					CopyMem(Work -> PrevBuffer,OtherBuffer,Work -> BufferPos);

				OtherBuffer[Work -> BufferPos] = 0;

				strcat(OtherBuffer,String);

				strcat(OtherBuffer,&Work -> PrevBuffer[Work -> BufferPos]);

				if(strlen(OtherBuffer) > 1023)
					OtherBuffer[1023] = 0;

				strcpy(Work -> WorkBuffer,OtherBuffer);

				if(Work -> StringInfo -> UndoBuffer)
					strcpy(Work -> StringInfo -> UndoBuffer,Work -> PrevBuffer);

				Work -> StringInfo -> UndoPos = Work -> BufferPos;

				Work -> BufferPos	+= Len;
				Work -> NumChars	+= Len;

				Work -> Actions = (Work -> Actions & ~SGA_BEEP) | SGA_USE | SGA_REDISPLAY;
			}

			Permit();

			return(TRUE);
		}

			/* The user pressed the cursor-right key to
			 * move the cursor to the next word in the buffer.
			 */

		if(Work -> IEvent -> ie_Code == CURSORRIGHT && (Work -> IEvent -> ie_Qualifier & IEQUALIFIER_CONTROL))
		{
			if(Work -> BufferPos != Work -> NumChars)
			{
				WORD i,Position = -1;

				for(i = Work -> BufferPos ; i < Work -> NumChars ; i++)
				{
					if(Work -> WorkBuffer[i] == ' ')
					{
						for( ; i < Work -> NumChars ; i++)
						{
							if(Work -> WorkBuffer[i] != ' ')
							{
								Position = i;
								break;
							}
						}

						break;
					}
				}

				if(Position != -1)
					Work -> BufferPos = Position;
				else
					Work -> BufferPos = Work -> NumChars;

				Work -> EditOp = EO_MOVECURSOR;
			}

			return(TRUE);
		}

			/* The user pressed the cursor-right key to
			 * move the cursor to the previous word in the buffer.
			 */

		if(Work -> IEvent -> ie_Code == CURSORLEFT && (Work -> IEvent -> ie_Qualifier & IEQUALIFIER_CONTROL))
		{
			if(Work -> BufferPos)
			{
				WORD i,Position = -1;

				for(i = Work -> BufferPos ; i >= 0 ; i--)
				{
					if(Work -> WorkBuffer[i] != ' ')
					{
						Position = i;
						break;
					}
				}

				if(Position == -1)
					Position = 0;

				if(Position)
				{
					i = Position;

					Position = -1;

					for( ; i >= 0 ; i--)
					{
						if(Work -> WorkBuffer[i] == ' ')
						{
							Position = i + 1;
							break;
						}
					}
				}

				if(Position != -1)
					Work -> BufferPos = Position;
				else
					Work -> BufferPos = 0;

				Work -> EditOp = EO_MOVECURSOR;
			}
		}

			/* The user pressed the cursor-up key to
			 * scroll through the command history.
			 */

		if(Work -> IEvent -> ie_Code == CURSORUP)
		{
				/* Shift key: jump to first command
				 * history entry.
				 */

			if(Work -> IEvent -> ie_Qualifier & (IEQUALIFIER_LSHIFT|IEQUALIFIER_RSHIFT))
			{
				if(PacketLine)
					SendPacketMsg(0);
			}
			else
			{
				if(PacketLine && PacketCount > 0)
					SendPacketMsg(PacketCount - 1);
			}
		}

			/* The user pressed the cursor-down key to
			 * scroll through the command history.
			 */

		if(Work -> IEvent -> ie_Code == CURSORDOWN)
		{
				/* Shift key: jump to last command
				 * history entry.
				 */

			if(Work -> IEvent -> ie_Qualifier & (IEQUALIFIER_LSHIFT|IEQUALIFIER_RSHIFT))
			{
				if(PacketLine > 0)
					SendPacketMsg(PacketLine);
			}
			else
			{
				if(PacketCount < PacketLine)
					SendPacketMsg(PacketCount + 1);
			}
		}

		return(TRUE);
	}
	else
	{
		if(*Msg == SGH_CLICK)
			return(TRUE);
		else
			return(FALSE);
	}
}

	/* HandlePacket():
	 *
	 *	Process the input coming through the packet window.
	 */

STATIC BOOLEAN
HandlePacket(VOID)
{
	struct IntuiMessage	*Message;
	struct PacketMessage	*PacketMsg;
	ULONG			 MsgClass,MsgCode;
	struct Gadget		*MsgGadget;
	struct FileRequester	*FileRequest;
	UBYTE			 DummyBuffer[MAX_FILENAME_LENGTH];
	BOOLEAN			 SwapWindow = FALSE;

	FOREVER
	{
		if(Wait(PORTMASK(PacketWindow -> UserPort) | SIG_KILL) & SIG_KILL)
			return(TRUE);

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

			GT_ReplyIMsg(Message);

				/* Re-enable the string gadget if necessary. */

			if(MsgClass == IDCMP_RAWKEY)
			{
				if(MsgCode == IECODE_UP_PREFIX|RAMIGA_CODE && CommandWindow == PacketWindow)
					ActivateGadget(CommandGadget,PacketWindow,NULL);

				if(MsgCode == HELP_CODE)
					GuideDisplay(CONTEXT_PACKETWINDOW);
			}

				/* Handle the menu. */

			if(MsgClass == IDCMP_MENUPICK)
			{
				struct MenuItem *MenuItem;

				while(MsgCode != MENUNULL)
				{
					MenuItem = ItemAddress(PacketMenu,MsgCode);

					switch((ULONG)GTMENUITEM_USERDATA(MenuItem))
					{
						case MEN_TOGGLE_ECHO:

							if(MenuItem -> Flags & CHECKED)
								Echo = TRUE;
							else
								Echo = FALSE;

							break;

						case MEN_QUITPANEL:

							MsgClass = IDCMP_CLOSEWINDOW;
							break;

						case MEN_LOADHISTORY:

							LT_LockWindow(PacketWindow);

							if(FileRequest = GetFile(PacketWindow,LocaleString(MSG_PACKET_LOAD_HISTORY_TXT),"","",DummyBuffer,NULL,FALSE,FALSE,FALSE,LocaleString(MSG_GLOBAL_LOAD_TXT),FALSE))
							{
								if(GetFileSize(DummyBuffer))
								{
									BPTR SomeFile;

									if(SomeFile = Open(DummyBuffer,MODE_OLDFILE))
									{
										if(PacketLine)
										{
											switch(MyEasyRequest(PacketWindow,LocaleString(MSG_PACKET_PACKET_WINDOW_STILL_HOLDS_LINES_TXT),LocaleString(MSG_PACKET_DISCARD_APPEND_CANCEL_TXT),PacketLine))
											{
												case 1:	ClearPacketHistory();
													break;

												case 0:	Close(SomeFile);
													SomeFile = NULL;
													break;
											}
										}

										if(SomeFile)
										{
											WORD Len;

											LineRead(NULL,NULL,NULL);

											while(Len = LineRead(SomeFile,DummyBuffer,255))
											{
												DummyBuffer[Len - 1] = 0;

												AddPacketHistory(DummyBuffer);
											}

											Close(SomeFile);
										}
									}
									else
										ShowError(PacketWindow,ERR_LOAD_ERROR,IoErr(),DummyBuffer);
								}

								FreeAslRequest(FileRequest);
							}

							LT_UnlockWindow(PacketWindow);
							break;

						case MEN_SAVEHISTORY:

							LT_LockWindow(PacketWindow);

							if(!PacketLine)
								MyEasyRequest(PacketWindow,LocaleString(MSG_PACKET_NOTHING_IN_THE_BUFFER_TXT),LocaleString(MSG_GLOBAL_CONTINUE_TXT));
							else
							{
								if(FileRequest = GetFile(PacketWindow,LocaleString(MSG_PACKET_SAVE_HISTORY_TXT),"","",DummyBuffer,NULL,TRUE,FALSE,FALSE,LocaleString(MSG_GLOBAL_SAVE_TXT),FALSE))
								{
									BPTR SomeFile = NULL;
									LONG Error = 0;

										/* If the file we are about
										 * to create already exists,
										 * ask the user whether we are
										 * to create, append or skip
										 * the file.
										 */

									if(GetFileSize(DummyBuffer))
									{
										switch(MyEasyRequest(PacketWindow,LocaleString(MSG_GLOBAL_FILE_ALREADY_EXISTS_TXT),LocaleString(MSG_GLOBAL_CREATE_APPEND_CANCEL_TXT),DummyBuffer))
										{
											case 1:	SomeFile = Open(DummyBuffer,MODE_NEWFILE);
												break;

											case 2:	if(SomeFile = Open(DummyBuffer,MODE_READWRITE))
												{
													if(Seek(SomeFile,0,OFFSET_END) == -1)
													{
														Close(SomeFile);

														SomeFile = NULL;
													}
												}
												break;
										}
									}
									else
										SomeFile = Open(DummyBuffer,MODE_NEWFILE);

									if(!SomeFile)
										Error = IoErr();
									else
									{
										struct Node *SomeNode;

										ObtainSemaphore(&PacketSemaphore);

										SomeNode = PacketHistoryList . lh_Head;

										while(SomeNode -> ln_Succ)
										{
											SetIoErr(0);

											if(FPrintf(SomeFile,"%s\n",SomeNode -> ln_Name) < 1)
											{
												Error = IoErr();

												break;
											}

											SomeNode = SomeNode -> ln_Succ;
										}

										Close(SomeFile);

										if(GetFileSize(DummyBuffer))
										{
											AddProtection(DummyBuffer,FIBF_EXECUTE);

											if(Config -> MiscConfig -> CreateIcons)
												AddIcon(DummyBuffer,FILETYPE_TEXT,TRUE);
										}
										else
											DeleteFile(DummyBuffer);

										ReleaseSemaphore(&PacketSemaphore);
									}

									if(Error)
										ShowError(PacketWindow,ERR_SAVE_ERROR,Error,DummyBuffer);

									FreeAslRequest(FileRequest);
								}
							}

							LT_UnlockWindow(PacketWindow);
							break;

						case MEN_CLEARHISTORY:

							LT_LockWindow(PacketWindow);

							ClearPacketHistory();

							LT_UnlockWindow(PacketWindow);
							break;

						case MEN_OTHERWINDOW:

							SwapWindow = TRUE;
							break;
					}

					MsgCode = MenuItem -> NextSelect;
				}

				ActivateGadget(PacketGadgetArray[GAD_STRING],PacketWindow,NULL);
			}

				/* Shut down. */

			if(MsgClass == IDCMP_CLOSEWINDOW)
			{
				Forbid();

				if(!(SetSignal(0,SIG_KILL) & SIG_KILL))
					return(FALSE);
				else
					return(TRUE);
			}

				/* Activate the string gadget as well. */

			if(MsgClass == IDCMP_ACTIVEWINDOW)
				ActivateGadget(PacketGadgetArray[GAD_STRING],PacketWindow,NULL);

			if(MsgClass == IDCMP_NEWSIZE)
			{
				PacketWindow -> Flags |= WFLG_RMBTRAP;

				strcpy(DummyBuffer,PacketContents);

				RemoveGList(PacketWindow,PacketGadgetList,(UWORD)-1);

				FreeGadgets(PacketGadgetList);

				PacketGadgetList = NULL;

				SetAPen(PacketWindow -> RPort,DrawInfo -> dri_Pens[BACKGROUNDPEN]);
				RectFill(PacketWindow -> RPort,PacketWindow -> BorderLeft,PacketWindow -> BorderTop,PacketWindow -> Width - PacketWindow -> BorderRight,PacketWindow -> Height - PacketWindow -> BorderBottom);
				RefreshWindowFrame(PacketWindow);

				PacketHeight	= PacketWindow -> Height;
				PacketWidth	= PacketWindow -> Width;

				ObtainSemaphore(&PacketSemaphore);

				if(CreateAllGadgets(PacketWindow -> Width,PacketGadgetArray,&PacketGadgetList,VisualInfo))
				{
					PacketContents = ((struct StringInfo *)PacketGadgetArray[GAD_STRING] -> SpecialInfo) -> Buffer;

					AddGList(PacketWindow,PacketGadgetList,(UWORD)-1,(UWORD)-1,NULL);
					RefreshGList(PacketGadgetList,PacketWindow,NULL,(UWORD)-1);
					GT_RefreshWindow(PacketWindow,NULL);

					ReleaseSemaphore(&PacketSemaphore);

					PacketWindow -> Flags &= ~WFLG_RMBTRAP;

					GT_SetGadgetAttrs(PacketGadgetArray[GAD_STRING],PacketWindow,NULL,
						GTST_String,	DummyBuffer,
					TAG_DONE);

					ActivateGadget(PacketGadgetArray[GAD_STRING],PacketWindow,NULL);
				}
				else
				{
					DisplayBeep(PacketWindow -> WScreen);

					ReleaseSemaphore(&PacketSemaphore);

					if(!(SetSignal(0,SIG_KILL) & SIG_KILL))
						return(FALSE);
					else
						return(TRUE);
				}
			}

			if(MsgClass == IDCMP_MOUSEBUTTONS)
				ActivateGadget(PacketGadgetArray[GAD_STRING],PacketWindow,NULL);

				/* User has entered a string. */

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

						if(MsgCode != '\t')
						{
								/* Is there anything in the buffer at all? */

							if(PacketContents[0])
							{
								strcpy(DummyBuffer,PacketContents);

								if(PacketString)
								{
									if(strcmp(PacketString,DummyBuffer))
										AddPacketHistory(DummyBuffer);

									PacketString = NULL;
								}
								else
									AddPacketHistory(DummyBuffer);

									/* Convert alien IBM characters. */

								if(Config -> TerminalConfig -> FontMode == FONT_IBM)
								{
									UBYTE Char;
									WORD i;

									for(i = 0 ; i < strlen(DummyBuffer) ; i++)
									{
										if(Char = IBMConversion[DummyBuffer[i]])
											DummyBuffer[i] = Char;
									}
								}

									/* Execute the command. */

								PacketSerialCommand(DummyBuffer);
							}

								/* Clear the packet window string
								 * gadget.
								 */

							GT_SetGadgetAttrs(PacketGadgetArray[GAD_STRING],PacketWindow,NULL,
								GTST_String,	"",
							TAG_DONE);

								/* Send a terminating `CR'. */

							switch(Config -> TerminalConfig -> SendCR)
							{
								case CR_ASCR:

									PacketSerWrite("\r",1);
									break;

								case CR_ASCRLF:

									PacketSerWrite("\r\n",2);
									break;
							}

								/* Re-activate the string gadget. */

							ActivateGadget(PacketGadgetArray[GAD_STRING],PacketWindow,NULL);
						}

						break;

					case GAD_LIST:

						ActivateGadget(PacketGadgetArray[GAD_STRING],PacketWindow,NULL);
						break;
				}
			}

			if(SwapWindow)
			{
				BumpWindow(Window);

				SwapWindow = FALSE;
			}
		}

		if(PacketMsg = (struct PacketMessage *)GetMsg(PacketPort))
		{
			struct Node *Node;

			ObtainSemaphore(&PacketSemaphore);

			if(Node = GetListNode(PacketMsg -> Line,&PacketHistoryList))
			{
				PacketString = Node -> ln_Name;

				if(HasList)
				{
					GT_SetGadgetAttrs(PacketGadgetArray[GAD_LIST],PacketWindow,NULL,
						GTLV_Selected,	PacketMsg -> Line,
						GTLV_Top,	PacketMsg -> Line,
					TAG_DONE);
				}
				else
				{
					GT_SetGadgetAttrs(PacketGadgetArray[GAD_STRING],PacketWindow,NULL,
						GTST_String,	Node -> ln_Name,
					TAG_DONE);
				}
			}
			else
			{
				PacketString = NULL;

				GT_SetGadgetAttrs(PacketGadgetArray[GAD_STRING],PacketWindow,NULL,
					GTST_String,	"",
				TAG_DONE);
			}

			ReleaseSemaphore(&PacketSemaphore);

			PacketCount = PacketMsg -> Line;

			FreeVecPooled(PacketMsg);

			ActivateGadget(PacketGadgetArray[GAD_STRING],PacketWindow,NULL);
		}
	}
}

STATIC VOID __saveds
PacketProcessEntry(VOID)
{
	BOOLEAN RingBack = TRUE;

	if(PacketPort = (struct MsgPort *)AllocVecPooled(sizeof(struct MsgPort),MEMF_PUBLIC | MEMF_CLEAR))
	{
		struct Message *Message;

		if(PacketMenu = CreateMenus(NewPacketMenu,TAG_DONE))
		{
			if(LayoutMenus(PacketMenu,VisualInfo,
				GTMN_TextAttr,		&UserFont,
				GTMN_NewLookMenus,	TRUE,

				AmigaGlyph ? GTMN_AmigaKey :  TAG_IGNORE, AmigaGlyph,
				CheckGlyph ? GTMN_Checkmark : TAG_IGNORE, CheckGlyph,
			TAG_DONE))
			{
				LONG ProtoHeight;

				ProtoHeight = Window -> WScreen -> WBorTop + Window -> WScreen -> Font -> ta_YSize + 1 + 2 + 3 + TextAttr . tta_YSize + 3 + 2;

				if(PacketWidth == -1)
					PacketWidth = Window -> Width;

				if(PacketHeight < ProtoHeight)
					PacketHeight = ProtoHeight;

				if(PacketX == -1 || (PacketX + PacketWidth > Window -> WScreen -> Width))
				{
					PacketX = Window -> LeftEdge;

					if(PacketX + PacketWidth > Window -> WScreen -> Width)
						PacketWidth = Window -> WScreen -> Width - PacketX;
				}

				if(PacketY == -1 || (PacketY + PacketHeight > Window -> WScreen -> Height))
				{
					PacketY = Window -> TopEdge + Window -> Height;

					if(PacketY + PacketHeight > Window -> WScreen -> Height)
						PacketHeight = ProtoHeight;
				}

				if(CreateAllGadgets(PacketWidth,PacketGadgetArray,&PacketGadgetList,VisualInfo))
				{
					if(PacketWindow = OpenWindowTags(NULL,
						WA_Width,		PacketWidth,
						WA_Height,		PacketHeight,

						WA_Left,		PacketX,
						WA_Top,			PacketY,

						WA_Activate,		TRUE,
						WA_DragBar,		TRUE,
						WA_DepthGadget,		TRUE,
						WA_CloseGadget,		TRUE,
						WA_RMBTrap,		TRUE,
						WA_SizeGadget,		TRUE,
						WA_MinWidth,		80,
						WA_MinHeight,		ProtoHeight,
						WA_MaxWidth,		Window -> WScreen -> Width,
						WA_MaxHeight,		Window -> WScreen -> Height,
						WA_CustomScreen,	Window -> WScreen,
						WA_NoCareRefresh,	TRUE,
						WA_NewLookMenus,	TRUE,
						WA_BackFill,		&BackfillHook,

						WA_IDCMP,		STRINGIDCMP | LISTVIEWIDCMP | IDCMP_NEWSIZE | IDCMP_SIZEVERIFY | IDCMP_CLOSEWINDOW | IDCMP_MENUPICK | IDCMP_ACTIVEWINDOW | IDCMP_RAWKEY | IDCMP_MOUSEBUTTONS,

						WA_Title,		LocaleString(MSG_GLOBAL_PACKET_WINDOW_TXT),

						AmigaGlyph ? WA_AmigaKey  : TAG_IGNORE, AmigaGlyph,
						CheckGlyph ? WA_Checkmark : TAG_IGNORE, CheckGlyph,
					TAG_DONE))
					{
						APTR OldPtr;

						SetAPen(PacketWindow -> RPort,DrawInfo -> dri_Pens[BACKGROUNDPEN]);
						SetDrMd(PacketWindow -> RPort,JAM1);
						RectFill(PacketWindow -> RPort,PacketWindow -> BorderLeft,PacketWindow -> BorderTop,PacketWindow -> Width - (PacketWindow -> BorderRight + 1),PacketWindow -> Height - (PacketWindow -> BorderBottom + 1));

						PacketPort -> mp_Flags		= PA_SIGNAL;
						PacketPort -> mp_SigBit		= PacketWindow -> UserPort -> mp_SigBit;
						PacketPort -> mp_SigTask	= PacketWindow -> UserPort -> mp_SigTask;

						NewList(&PacketPort -> mp_MsgList);

						PacketHook . h_Entry		= (LONG (*)())PacketKey;
						PacketHook . h_SubEntry		= NULL;
						PacketHook . h_Data		= NULL;

						PacketContents = ((struct StringInfo *)PacketGadgetArray[GAD_STRING] -> SpecialInfo) -> Buffer;

						AddGList(PacketWindow,PacketGadgetList,(UWORD)-1,(UWORD)-1,NULL);
						RefreshGList(PacketGadgetList,PacketWindow,NULL,(UWORD)-1);
						GT_RefreshWindow(PacketWindow,NULL);

						SetMenuStrip(PacketWindow,PacketMenu);

						PacketWindow -> Flags &= ~WFLG_RMBTRAP;

						ActivateGadget(PacketGadgetArray[GAD_STRING],PacketWindow,NULL);

						PacketProcess = (struct Process *)FindTask(NULL);

						OldPtr = PacketProcess -> pr_WindowPtr;

						PacketProcess -> pr_WindowPtr = PacketWindow;

						Signal(ThisProcess,SIG_HANDSHAKE);

						RingBack = HandlePacket();

						PacketProcess -> pr_WindowPtr = OldPtr;

						PacketWindow -> Flags |= WFLG_RMBTRAP;

						ClearMenuStrip(PacketWindow);

						RemoveGList(PacketWindow,PacketGadgetList,(UWORD)-1);

						PacketX		= PacketWindow -> LeftEdge;
						PacketY		= PacketWindow -> TopEdge;

						PacketWidth	= PacketWindow -> Width;
						PacketHeight	= PacketWindow -> Height;
				
						LT_DeleteWindowLock(PacketWindow);

						CloseWindow(PacketWindow);

						PacketWindow = NULL;
					}

					FreeGadgets(PacketGadgetList);

					PacketGadgetList = NULL;
				}
			}

			FreeMenus(PacketMenu);

			PacketMenu = NULL;
		}

		while(Message = GetMsg(PacketPort))
			FreeVecPooled(Message);

		FreeVecPooled(PacketPort);

		PacketPort = NULL;
	}

	Forbid();

	PacketProcess = NULL;

	if(RingBack)
		Signal(ThisProcess,SIG_HANDSHAKE);
	else
		CheckItem(MEN_PACKET_WINDOW,FALSE);
}

	/* ClearPacketHistory():
	 *
	 *	Release the command history.
	 */

VOID
ClearPacketHistory()
{
	if(PacketProcess)
	{
		ObtainSemaphore(&PacketSemaphore);

		if(HasList)
		{
			GT_SetGadgetAttrs(PacketGadgetArray[GAD_LIST],PacketWindow,NULL,
				GTLV_Labels,~0,
			TAG_DONE);
		}
	
		FreeList(&PacketHistoryList);
	
		PacketCount = PacketLine = 0;
	
		if(HasList)
		{
			GT_SetGadgetAttrs(PacketGadgetArray[GAD_LIST],PacketWindow,NULL,
				GTLV_Labels,&PacketHistoryList,
			TAG_DONE);
		}
	
		PacketString = NULL;

		ReleaseSemaphore(&PacketSemaphore);
	}
}

	/* AddPacketHistory(STRPTR Buffer):
	 *
	 *	Add a line to the packet window command history. This
	 *	works very much the same as the AddLine()-routine.
	 */

VOID __regargs
AddPacketHistory(STRPTR Buffer)
{
	if(PacketProcess)
	{
		struct Node *SomeNode;
	
		ObtainSemaphore(&PacketSemaphore);

		if(HasList)
		{
			GT_SetGadgetAttrs(PacketGadgetArray[GAD_LIST],PacketWindow,NULL,
				GTLV_Labels,	~0,
			TAG_DONE);
		}
	
		if(SomeNode = CreateNode(Buffer))
		{
			AddTail(&PacketHistoryList,SomeNode);
	
			PacketCount = ++PacketLine;
		}
	
		if(HasList)
		{
			GT_SetGadgetAttrs(PacketGadgetArray[GAD_LIST],PacketWindow,NULL,
				GTLV_Top,	PacketCount - 1,
				GTLV_Labels,	&PacketHistoryList,
			TAG_DONE);
		}

		ReleaseSemaphore(&PacketSemaphore);
	}
}

	/* DeletePacketWindow():
	 *
	 *	Delete the packet window and release the command
	 *	history.
	 */

VOID __regargs
DeletePacketWindow(BYTE WindowOnly)
{
	CheckItem(MEN_PACKET_WINDOW,FALSE);

	if(PacketProcess)
	{
		Forbid();

		ClrSignal(SIG_HANDSHAKE);

		Signal(PacketProcess,SIG_KILL);

		Wait(SIG_HANDSHAKE);

		Permit();

		if(!WindowOnly)
		{
			HasList = FALSE;

			FreeList(&PacketHistoryList);
	
			PacketCount = PacketLine = 0;
	
			PacketString = NULL;
		}
	}
}

	/* CreatePacketWindow():
	 *
	 *	Open the packet window and allocate the command history
	 *	buffer.
	 */

BYTE
CreatePacketWindow()
{
	if(Echo == -1)
		Echo = Config -> SerialConfig -> Duplex;

	if(!PacketProcess)
	{
		InitSemaphore(&PacketSemaphore);

		LocalizeMenu(NewPacketMenu,MSG_PACKET_PROJECT_MEN);

		Forbid();

		if(CreateNewProcTags(
			NP_Name,	"term Packet Process",
			NP_Priority,	SysBase -> ThisTask -> tc_Node . ln_Pri,
			NP_Entry,	PacketProcessEntry,
			NP_StackSize,	6000,
		TAG_DONE))
		{
			ClrSignal(SIG_HANDSHAKE);

			Wait(SIG_HANDSHAKE);
		}

		Permit();
	}

	if(PacketProcess)
	{
		Forbid();

		ActivateWindow(PacketWindow);

		CheckItem(MEN_PACKET_WINDOW,TRUE);

		Permit();

		return(TRUE);
	}
	else
	{
		CheckItem(MEN_PACKET_WINDOW,FALSE);

		return(FALSE);
	}
}
