/*
**	termStatusDisplay.c
**
**	Status information display routines
**
**	Copyright © 1990-1995 by Olaf `Olsen' Barthel
**		All Rights Reserved
*/

#include "termGlobal.h"

	/* The information to be displayed. */

enum	{	INFO_STATUS,INFO_BUFFER,INFO_PROTOCOL,INFO_EMULATION,INFO_BAUDRATE,
		INFO_PARAMETERS,INFO_CURRENTTIME,INFO_ONLINETIME,INFO_ONLINECOST
	};

	/* The current status line display mode. */

enum	{	MODE_SCREEN_NORMAL,MODE_SCREEN_COMPRESSED,
		MODE_WB_NORMAL,MODE_WB_COMPRESSED
	};

	/* Enumerated text boxes. */

enum	{	STATUSBOX_STATUS_FONT,STATUSBOX_PROTOCOL_TERMINAL,
		STATUSBOX_RATE_PARAMETERS,STATUSBOX_TIME_ONLINE
	};

	/* The status server passes this structure to the
	 * rendering routine if the status information
	 * is to be updated.
	 */

struct ObjectCarrier
{
	struct RastPort	 *RPort;
	LONG		  FirstColumn,
			  FullWidth;
	struct TextBox	**BoxArray,
			 *BoxList;
};

	/* A custom message type for the display update server. */

struct UpdateMessage
{
	struct Message	VanillaMessage;

	APTR		Object;
	UBYTE		Mode,
			Type;
};

	/* The following static strings are displayed in the status
	 * window.
	 */

STATIC STRPTR	ConfigBufferState[3],
		ConfigEmulation[6],
		ConfigParity[6],
		ConfigStatus[8];

	/* Width of the status line text, required in case the user interface
	 * font happens to be proportional-spaced.
	 */

STATIC LONG			 StatusLineWidth;

	/* The status display update task. */

STATIC struct Task		*StatusDisplayTask;
STATIC struct MsgPort		*StatusDisplayPort;

	/* Display update data */

STATIC BOOL			 NeedFullRefresh;
STATIC WORD			 UpdateSig;
STATIC ULONG			 UpdateMask;

STATIC VOID __regargs
DrawSeparator(struct RastPort *RPort)
{
	UWORD	Left	= StatusDisplayLeft,
		Width	= StatusDisplayWidth;

	SetAPen(RPort,DrawInfo -> dri_Pens[SHADOWPEN]);
	Move(RPort,Left,StatusDisplayTop);
	Draw(RPort,Left + Width - 1,StatusDisplayTop);

	SetAPen(RPort,DrawInfo -> dri_Pens[SHINEPEN]);
	Move(RPort,Left,StatusDisplayTop + 1);
	Draw(RPort,Left + Width - 1,StatusDisplayTop + 1);
}

	/* DoStatusInfo(APTR Object,UBYTE Mode,UBYTE Type,STRPTR String):
	 *
	 *	Display information in the status line area.
	 */

STATIC VOID __regargs
DoStatusInfo(APTR Object,UBYTE Mode,UBYTE Type,STRPTR String)
{
		/* What mode of operation is the status area in? */

	switch(Mode)
	{
			/* Compressed mode. */

		case MODE_SCREEN_COMPRESSED:
		{
			struct RastPort *RPort = Object;

			STATIC BYTE Offsets[8] =
			{
				 0,
				-1,	/* Not supported */
				26,
				11,
				40,
				53,
				61,
				72
			};

			STATIC UBYTE Strings[8][20];
			UBYTE LineBuffer[90];
			LONG i,j,k,Width;

			strcpy(Strings[Type],String);

			strcpy(LineBuffer,"         ·              ·             ·            ·       ·          ·         ");

			for(i = 0 ; i < 8 ; i++)
			{
				if(Offsets[i] >= 0)
				{
					j = strlen(Strings[i]);

					for(k = 0 ; k < j ; k++)
						LineBuffer[Offsets[i] + k] = Strings[i][k];
				}
			}

			Width = TextLength(RPort,LineBuffer,80);

			if(AttemptLockLayerRom(RPort -> Layer))
			{
				if((StatusLineWidth && StatusLineWidth != Width) || NeedFullRefresh)
				{
					LONG OldPen = ReadAPen(RPort);

					SetAPen(RPort,DrawInfo -> dri_Pens[TEXTPEN]);
					RectFill(RPort,StatusDisplayLeft,StatusDisplayTop,StatusDisplayLeft + StatusDisplayWidth - 1,StatusDisplayTop + StatusDisplayHeight - 1);
					SetAPen(RPort,OldPen);

					StatusLineWidth = Width;
					NeedFullRefresh = FALSE;
				}

				Move(RPort,StatusDisplayLeft + (StatusDisplayWidth - Width) / 2,StatusDisplayTop + UserFontBase);
				Text(RPort,LineBuffer,80);

				UnlockLayerRom(RPort -> Layer);
			}
		}

		break;

			/* Normal mode. */

		case MODE_SCREEN_NORMAL:
		{
			STATIC UBYTE Codes[8][2] =
			{
				STATUSBOX_STATUS_FONT,		0,
				STATUSBOX_STATUS_FONT,		1,

				STATUSBOX_PROTOCOL_TERMINAL,	0,
				STATUSBOX_PROTOCOL_TERMINAL,	1,

				STATUSBOX_RATE_PARAMETERS,	0,
				STATUSBOX_RATE_PARAMETERS,	1,

				STATUSBOX_TIME_ONLINE,		0,
				STATUSBOX_TIME_ONLINE,		1
			};

			struct ObjectCarrier *Carrier = (struct ObjectCarrier *)Object;

			if(AttemptLockLayerRom(Carrier -> RPort -> Layer))
			{
				if(NeedFullRefresh && !Config -> ScreenConfig -> SplitStatus)
				{
					struct RastPort	*RPort = Carrier -> RPort;
					LONG		 Left;

					if((Left = StatusDisplayLeft + (StatusDisplayWidth - Carrier -> FullWidth) / 2) < 0)
						Left = 0;

					Left += Carrier -> FirstColumn - SZ_GetBoxInfo(Carrier -> BoxList,BOX_LEFT);

					SetAPen(RPort,DrawInfo -> dri_Pens[BACKGROUNDPEN]);
					RectFill(RPort,StatusDisplayLeft,StatusDisplayTop,StatusDisplayLeft + StatusDisplayWidth - 1,StatusDisplayTop + StatusDisplayHeight - 1);

					DrawSeparator(RPort);

					SZ_SetBoxes(Carrier -> BoxList,-1,StatusDisplayTop + 3);
					SZ_MoveBoxes(Carrier -> BoxList,Left,0);

					SZ_DrawBoxes(RPort,Carrier -> BoxList);

					NeedFullRefresh = FALSE;
				}

				SZ_PrintLine(Carrier -> RPort,Carrier -> BoxArray[Codes[Type][0]],Codes[Type][1],String);

				UnlockLayerRom(Carrier -> RPort -> Layer);
			}
		}

		break;
	}
}

	/* DoInfo(APTR Object,UBYTE Mode,UBYTE Type,STRPTR String):
	 *
	 *	Post an update message to the status display server.
	 */

STATIC VOID __regargs
DoInfo(APTR Object,UBYTE Mode,UBYTE Type,STRPTR String)
{
	struct UpdateMessage	*Msg;
	WORD			 Len = strlen(String) + 1;

		/* Allocate enough space to hold both the string
		 * and the message.
		 */

	if(Msg = (struct UpdateMessage *)AllocVecPooled(sizeof(struct UpdateMessage) + Len,MEMF_ANY | MEMF_PUBLIC | MEMF_CLEAR))
	{
			/* Fill in the message head. */

		Msg -> VanillaMessage . mn_Length = sizeof(struct UpdateMessage) + Len;

			/* Set up the name pointer. */

		Msg -> VanillaMessage . mn_Node . ln_Name = (STRPTR)(Msg + 1);

			/* Copy the string. */

		strcpy(Msg -> VanillaMessage . mn_Node . ln_Name,String);

			/* Fill in the remaining data. */

		Msg -> Object	= Object;
		Msg -> Mode	= Mode;
		Msg -> Type	= Type;

			/* Post the message. */

		PutMsg(StatusDisplayPort,(struct Message *)Msg);
	}
}

	/* UpdateInfo(APTR Object,UBYTE Mode,UBYTE Type,...):
	 *
	 *	Update the information displayed in the status
	 *	area.
	 */

STATIC VOID __stdargs
UpdateInfo(APTR Object,UBYTE Mode,UBYTE Type,...)
{
	if(Object)
	{
		UBYTE	 MiniBuffer[50];
		STRPTR	*String;
		LONG	*Numeral;
		va_list	 VarArgs;

		va_start(VarArgs,Type);

		String	= (STRPTR *)VarArgs;
		Numeral	= (LONG *)VarArgs;

		switch(Type)
		{
			case INFO_STATUS:

				strcpy(MiniBuffer,ConfigStatus[Numeral[0]]);
				strcat(MiniBuffer,"         ");

				MiniBuffer[9] = 0;

				DoInfo(Object,Mode,Type,MiniBuffer);

				break;

			case INFO_BUFFER:

				if(Mode == MODE_SCREEN_NORMAL)
					DoInfo(Object,Mode,Type,ConfigBufferState[Numeral[0]]);

				break;

			case INFO_ONLINECOST:

				strcpy(MiniBuffer,String[0]);
				strcat(MiniBuffer,"          ");

				MiniBuffer[8] = 0;

				DoInfo(Object,Mode,INFO_ONLINETIME,MiniBuffer);

				break;

			case INFO_CURRENTTIME:

				FormatTime(MiniBuffer,Numeral[0],Numeral[1],Numeral[2]);

				DoInfo(Object,Mode,Type,MiniBuffer);

				break;

			case INFO_ONLINETIME:

				SPrintf(MiniBuffer,"%02ld:%02ld:%02ld",Numeral[0],Numeral[1],Numeral[2]);

				DoInfo(Object,Mode,Type,MiniBuffer);

				break;

			case INFO_BAUDRATE:

				if(LocaleBase)
					SPrintf(MiniBuffer,"%lD        ",Numeral[0]);
				else
					SPrintf(MiniBuffer,"%ld        ",Numeral[0]);

				MiniBuffer[7] = 0;

				DoInfo(Object,Mode,Type,MiniBuffer);

				break;

			case INFO_PROTOCOL:
			case INFO_EMULATION:

				strcpy(MiniBuffer,String[0]);

				strcat(MiniBuffer,"           ");

				MiniBuffer[12] = 0;

				DoInfo(Object,Mode,Type,MiniBuffer);

				break;

			case INFO_PARAMETERS:

				if(Mode == MODE_SCREEN_COMPRESSED)
				{
					STATIC UBYTE Parities[5] =
					{
						'N','E','O','M','S'
					};

					SPrintf(MiniBuffer,"%ld-%lc-%ld",Numeral[0],Parities[Numeral[1]],Numeral[2]);
				}
				else
					SPrintf(MiniBuffer,"%ld-%s-%ld",Numeral[0],ConfigParity[Numeral[1]],Numeral[2]);

				DoInfo(Object,Mode,Type,MiniBuffer);

				break;
		}

		va_end(VarArgs);
	}
}

	/* Raise(UWORD Colour):
	 *
	 *	Make an RGB value brighter.
	 */

STATIC UWORD __inline
Raise(UWORD Colour)
{
	UWORD R,G,B;

	R =  (Colour >> 8)        + 4;
	G = ((Colour >> 4) & 0xF) + 4;
	B = ((Colour     ) & 0xF) + 4;

	if(R > 15)
		R = 15;

	if(G > 15)
		G = 15;

	if(B > 15)
		B = 15;

	return((UWORD)(R << 8 | G << 4 | B));
}

	/* VisualBeep():
	 *
	 *	Handle the visual part of the display beep.
	 */

STATIC BYTE
VisualBeep(VOID)
{
	struct UCopList	*UserCopperList;

		/* Create a user copper list. */

	if(UserCopperList = (struct UCopList *)AllocMem(sizeof(struct UCopList),MEMF_ANY|MEMF_CLEAR))
	{
			/* Initialize for 35 commands. */

		if(UCopperListInit(UserCopperList,1 + 16 + 1 + 16 + 1))
		{
			WORD i;

				/* Wait until first line of window. */

			CWAIT(UserCopperList,Window -> TopEdge,0);

				/* Set the light colours. */

			for(i = 0 ; i < 16 ; i++)
				CMOVE(UserCopperList,custom . color[i],Raise(GetRGB4(Screen -> ViewPort . ColorMap,i)));

				/* Wait until bottom of window. */

			CWAIT(UserCopperList,Window -> TopEdge + Window -> Height - 1,0);

				/* Set the standard colours. */

			for(i = 0 ; i < 16 ; i++)
				CMOVE(UserCopperList,custom . color[i],GetRGB4(Screen -> ViewPort . ColorMap,i));

				/* Finish list. */

			CEND(UserCopperList);

				/* Install user copper list... */

			Screen -> ViewPort . UCopIns = UserCopperList;

				/* ...and display it. */

			RethinkDisplay();

			return(TRUE);
		}
		else
			FreeMem(UserCopperList,sizeof(struct UCopList));
	}

	return(FALSE);
}

	/* StatusDisplayServer(VOID):
	 *
	 *	Yet another asynchronous background task to display
	 *	some information.
	 */

STATIC VOID __saveds
StatusDisplayServer(VOID)
{
		/* Create the interface port. */

	if(StatusDisplayPort = CreateMsgPort())
	{
		struct UpdateMessage	*Msg;
		ULONG			 Signals;

			/* Ring back... */

		Signal(StatusProcess,SIG_HANDSHAKE);

			/* Wait for messages or termination signal. */

		FOREVER
		{
			Signals = Wait(SIG_KILL | PORTMASK(StatusDisplayPort));

				/* Termination? */

			if(Signals & SIG_KILL)
				break;

				/* Message arrival? */

			if(Signals & PORTMASK(StatusDisplayPort))
			{
					/* Process all pending messages. */

				while(Msg = (struct UpdateMessage *)GetMsg(StatusDisplayPort))
				{
					DoStatusInfo(Msg -> Object,Msg -> Mode,Msg -> Type,Msg -> VanillaMessage . mn_Node . ln_Name);

					FreeVecPooled(Msg);
				}
			}
		}

			/* Remove all pending messages. */

		while(Msg = (struct UpdateMessage *)GetMsg(StatusDisplayPort))
			FreeVecPooled(Msg);

			/* Remove the msgport. */

		DeleteMsgPort(StatusDisplayPort);
	}

		/* Lock & quit... */

	Forbid();

	Signal(StatusProcess,SIG_HANDSHAKE);

	RemTask(StatusDisplayTask = NULL);
}

	/* StatusServer():
	 *
	 *	Asynchronous process to continuosly display the current
	 *	terminal settings.
	 */

VOID __saveds
StatusServer()
{
	STATIC struct timeval	 OnlineTime,
				 LastTime,
				 TempTime;
	STATIC BYTE		 GotOnline		= FALSE,
				 WasOnline		= FALSE,
				 ShowPay		= FALSE,
				 FlagBit		= FALSE;
	STATIC LONG		 SecCount		= 0,
				 MinCount		= 0,
				 BeepCount		= 0;

	struct TextBox		*BoxArray[4],
				*BoxList = NULL,
				*Box;

	struct RastPort		*RPort;

	APTR			 SomeObject;
	struct ObjectCarrier	 Carrier;

	struct timerequest	*StatusTimeRequest;
	struct MsgPort		*StatusTimePort;

	BYTE			 Background		= FALSE,
				 FlashIt		= FALSE,
				 SetColours		= FALSE,
				 StandardColours	= TRUE,
				 KeepGoing		= TRUE,
				 Beeping		= FALSE,
				 StatusMode		= Config -> ScreenConfig -> StatusLine;

	BYTE			 LastProtocol[40],
				 LastEmulationName[40];

	BYTE			 LastFrozen		= -1,
				 LastEmulation		= -1,
				 LastBitsPerChar	= -1,
				 LastParity		= -1,
				 LastStopBits		= -1,
				 LastStatus		= -1;

	LONG			 LastBaud		= -1;

	LONG			 i,
				 ThisHour,
				 ThisMinute,
				 BoxCounter = 0,
				 FullWidth;
	WORD			 ColumnLeft[4],
				 ColumnWidth[4],
				 Max,
				 Len;

	BYTE			 AllFine = TRUE;
	UBYTE			 Mode;

	StatusLineWidth = 0;

	LastProtocol[0] = 0;

	LastEmulationName[0] = 0;

	LocalizeString(ConfigBufferState,MSG_TERMSTATUSDISPLAY_FROZEN_TXT,MSG_TERMSTATUSDISPLAY_RECORDING_TXT);
	LocalizeString(ConfigEmulation,MSG_TERMAUX_ANSI_VT102_TXT,MSG_TERMAUX_HEX_TXT);
	LocalizeString(ConfigParity,MSG_TERMAUX_NONE_TXT,MSG_TERMAUX_SPACE_TXT);
	LocalizeString(ConfigStatus,MSG_TERMAUX_READY_TXT,MSG_TERMAUX_RECORD_LINE_TXT);

	if(StatusWindow)
		RPort = StatusWindow -> RPort;
	else
		RPort = StatusRPort;

	if(RPort)
	{
		SetAPen(RPort,DrawInfo -> dri_Pens[BACKGROUNDPEN]);
		RectFill(RPort,StatusDisplayLeft,StatusDisplayTop,StatusDisplayLeft + StatusDisplayWidth - 1,StatusDisplayTop + StatusDisplayHeight - 1);

			/* Render the information. */

		if(StatusWindow)
			SZ_SizeSetup(StatusWindow -> WScreen,&UserFont);
		else
			SZ_SizeSetup(Window -> WScreen,&UserFont);

		if(StatusMode == STATUSLINE_COMPRESSED)
		{
			StatusOffset = (StatusDisplayWidth - 80 * UserFontWidth) / 2;

			SetAPen(RPort,DrawInfo -> dri_Pens[TEXTPEN]);
			RectFill(RPort,StatusDisplayLeft,StatusDisplayTop,StatusDisplayLeft + StatusDisplayWidth - 1,StatusDisplayTop + StatusDisplayHeight - 1);

			SetDrMd(RPort,JAM2);
			SetAPen(RPort,DrawInfo -> dri_Pens[BACKGROUNDPEN]);
			SetBPen(RPort,DrawInfo -> dri_Pens[TEXTPEN]);

			SetFont(RPort,UserTextFont);
		}
		else
		{
			SetBPen(RPort,DrawInfo -> dri_Pens[BACKGROUNDPEN]);

				/* Draw a separating line. */

			if(!Config -> ScreenConfig -> SplitStatus)
				DrawSeparator(RPort);

			SetAPen(RPort,DrawInfo -> dri_Pens[TEXTPEN]);

			ColumnLeft[0] = SZ_LeftOffsetN(MSG_TERMSTATUSDISPLAY_STATUS_TXT,MSG_TERMSTATUSDISPLAY_FONT_TXT,-1);
			ColumnLeft[1] = SZ_LeftOffsetN(MSG_TERMSTATUSDISPLAY_PROTOCOL_TXT,MSG_TERMSTATUSDISPLAY_TERMINAL_TXT,-1);
			ColumnLeft[2] = SZ_LeftOffsetN(MSG_TERMSTATUSDISPLAY_BAUDRATE_TXT,MSG_TERMSTATUSDISPLAY_PARAMETERS_TXT,-1);
			ColumnLeft[3] = SZ_LeftOffsetN(MSG_TERMSTATUSDISPLAY_TIME_TXT,MSG_TERMSTATUSDISPLAY_ONLINE_TXT,-1);

			Max = 0;

			for(i = 0 ; ConfigStatus[i] ; i++)
			{
				if((Len = SZ_BoxWidth(strlen(ConfigStatus[i]))) > Max)
					Max = Len;
			}

			for(i = 0 ; ConfigBufferState[i] ; i++)
			{
				if((Len = SZ_BoxWidth(strlen(ConfigBufferState[i]))) > Max)
					Max = Len;
			}

			ColumnWidth[0] = Max;

			Max = SZ_BoxWidth(12);

			for(i = 0 ; ConfigEmulation[i] ; i++)
			{
				if((Len = SZ_BoxWidth(strlen(ConfigEmulation[i]))) > Max)
					Max = Len;
			}

			ColumnWidth[1] = Max;

			Max = SZ_BoxWidth(10);

			for(i = 0 ; ConfigParity[i] ; i++)
			{
				if((Len = SZ_BoxWidth(4 + strlen(ConfigParity[i]))) > Max)
					Max = Len;
			}

			ColumnWidth[2] = Max;

			ColumnWidth[3] = SZ_BoxWidth(8);

			FullWidth = 0;

			for(i = 0 ; i < 4 ; i++)
				FullWidth += ColumnWidth[i] + ColumnLeft[i];

			FullWidth += 3 * InterWidth + 2;

			Carrier . FullWidth	= FullWidth;
			Carrier . FirstColumn	= ColumnLeft[0];

			if(!Config -> ScreenConfig -> SplitStatus)
			{
				if(FullWidth > StatusDisplayWidth)
					SZ_SetLeftEdge(StatusDisplayLeft + ColumnLeft[0]);
				else
					SZ_SetLeftEdge(StatusDisplayLeft + (StatusDisplayWidth - FullWidth) / 2 + ColumnLeft[0]);

				SZ_SetAbsoluteTop(StatusDisplayTop + 3);
				SZ_SetTopEdge(StatusDisplayTop + 3);
			}
			else
			{
				SZ_SetLeftEdge(StatusDisplayLeft + ColumnLeft[0] + 1);

				SZ_SetAbsoluteTop(StatusDisplayTop + 1);
				SZ_SetTopEdge(StatusDisplayTop + 1);
			}

			SZ_SetWidth(ColumnWidth[0]);

			BoxArray[BoxCounter++] = Box = SZ_CreateTextBox(&BoxList,
				SZ_Lines,	2,
				SZ_AutoWidth,	TRUE,
			TAG_DONE);

			SZ_SetBoxTitles(Box,LocaleString(MSG_TERMSTATUSDISPLAY_STATUS_TXT),LocaleString(MSG_TERMSTATUSDISPLAY_FONT_TXT),NULL);

			SZ_SetWidth(ColumnWidth[1]);
			SZ_AddLeftOffset(ColumnLeft[1]);

			BoxArray[BoxCounter++] = Box = SZ_CreateTextBox(&BoxList,
				SZ_Lines,	2,
				SZ_AutoWidth,	TRUE,
				SZ_NewColumn,	TRUE,
			TAG_DONE);

			SZ_SetBoxTitles(Box,LocaleString(MSG_TERMSTATUSDISPLAY_PROTOCOL_TXT),LocaleString(MSG_TERMSTATUSDISPLAY_TERMINAL_TXT),NULL);

			SZ_SetWidth(ColumnWidth[2]);
			SZ_AddLeftOffset(ColumnLeft[2]);

			BoxArray[BoxCounter++] = Box = SZ_CreateTextBox(&BoxList,
				SZ_Lines,	2,
				SZ_AutoWidth,	TRUE,
				SZ_NewColumn,	TRUE,
			TAG_DONE);

			SZ_SetBoxTitles(Box,LocaleString(MSG_TERMSTATUSDISPLAY_BAUDRATE_TXT),LocaleString(MSG_TERMSTATUSDISPLAY_PARAMETERS_TXT),NULL);

			SZ_SetWidth(ColumnWidth[3]);
			SZ_AddLeftOffset(ColumnLeft[3]);

			BoxArray[BoxCounter] = Box = SZ_CreateTextBox(&BoxList,
				SZ_Lines,	2,
				SZ_AutoWidth,	TRUE,
				SZ_NewColumn,	TRUE,
			TAG_DONE);

			SZ_SetBoxTitles(Box,LocaleString(MSG_TERMSTATUSDISPLAY_TIME_TXT),LocaleString(MSG_TERMSTATUSDISPLAY_ONLINE_TXT),NULL);

			if(!Box)
				AllFine = FALSE;
			else
				SZ_DrawBoxes(RPort,BoxList);
		}
	}
	else
		AllFine = TRUE;

	if((UpdateSig = AllocSignal(-1)) == -1)
		AllFine = FALSE;
	else
		UpdateMask = 1L << UpdateSig;

		/* Everything fine so far? */

	if(AllFine)
	{
		Forbid();

			/* Create the display server task. */

		if(StatusDisplayTask = CreateTask("term Status Display Task",5,StatusDisplayServer,4000))
		{
			ClrSignal(SIG_HANDSHAKE);

			Wait(SIG_HANDSHAKE);
		}

		Permit();
	}

		/* Is the display server task up and running? */

	if(StatusDisplayTask)
	{
			/* Create a timer device request. */

		if(StatusTimePort = (struct MsgPort *)CreateMsgPort())
		{
			if(StatusTimeRequest = (struct timerequest *)CreateIORequest(StatusTimePort,sizeof(struct timerequest)))
			{
				if(!OpenDevice(TIMERNAME,UNIT_VBLANK,StatusTimeRequest,0))
				{
						/* Signal our father process
						 * that we're running.
						 */

					Signal(ThisProcess,SIG_HANDSHAKE);

					if(RPort)
					{
						if(StatusMode == STATUSLINE_COMPRESSED)
						{
							Mode = MODE_SCREEN_COMPRESSED;

							SomeObject = RPort;
						}
						else
						{
							Mode = MODE_SCREEN_NORMAL;

							Carrier . RPort		= RPort;
							Carrier . BoxArray	= BoxArray;
							Carrier . BoxList	= BoxList;

							SomeObject = &Carrier;
						}

						UpdateInfo(SomeObject,Mode,INFO_ONLINETIME,0,0,0);
					}

						/* Keep on displaying. */

					while(KeepGoing)
					{
							/* Are we to quit? */

						if(CheckSignal(SIG_KILL))
							KeepGoing = FALSE;

							/* Get the current time. */

						StatusTimeRequest -> tr_node . io_Command = TR_GETSYSTIME;

						DoIO(StatusTimeRequest);

							/* A connection has just
							 * been established.
							 */

						ObtainSemaphore(&OnlineSemaphore);

						if(Online && !GotOnline)
						{
							OnlineTime = StatusTimeRequest -> tr_time;

								/* Add up connection time. */

							if(OnlinePlus)
							{
								struct timeval GetBack;

								GetBack . tv_secs	= OnlinePlus;
								GetBack . tv_micro	= 0;

									/* Technically, the connection happened a
									 * bit earlier.
									 */

								SubTime(&OnlineTime,&GetBack);

									/* Choose the right rate accounting
									 * time.
									 */

								SelectTime(ChosenEntry,ChosenPattern,&OnlineTime);

									/* Enter the first rate. */

								if(!CurrentPay)
									CurrentPay = PayPerUnit[DT_FIRST_UNIT];

									/* Take care of the remaining seconds. */

								while(SecPerUnit[WhichUnit] && OnlinePlus >= SecPerUnit[WhichUnit])
								{
									WhichUnit = DT_NEXT_UNIT;

									CurrentPay += PayPerUnit[DT_NEXT_UNIT];

									OnlinePlus -= SecPerUnit[WhichUnit];
								}

									/* Remember the remaining seconds. */

								SecCount = OnlinePlus;

								OnlinePlus = 0;
							}
							else
								SecCount = 0;

							GotOnline	= TRUE;
							FlagBit		= FALSE;
						}

						ReleaseSemaphore(&OnlineSemaphore);

							/* Print the current time. */

						ThisHour	= (StatusTimeRequest -> tr_time . tv_secs % 86400) / 3600;
						ThisMinute	= (StatusTimeRequest -> tr_time . tv_secs % 3600) / 60;

						UpdateInfo(SomeObject,Mode,INFO_CURRENTTIME,ThisHour,ThisMinute,StatusTimeRequest -> tr_time . tv_secs % 60);

							/* Handle routine checkup actions. */

						if(!(SecCount & 1))
							Signal(ThisProcess,SIG_CHECK);

						ObtainSemaphore(&OnlineSemaphore);

						if(Online)
						{
							ReleaseSemaphore(&OnlineSemaphore);

							WasOnline = TRUE;

							ObtainSemaphore(&PatternSemaphore);

							if(ChosenEntry || ChosenPattern)
							{
								if(!(StatusTimeRequest -> tr_time . tv_secs % 60))
									SelectTime(ChosenEntry,ChosenPattern,NULL);

								if(!CurrentPay)
									CurrentPay = PayPerUnit[DT_FIRST_UNIT];

								FlagBit ^= TRUE;

								if(!FlagBit)
								{
									while(SecPerUnit[WhichUnit] && SecCount >= SecPerUnit[WhichUnit])
									{
										SecCount -= SecPerUnit[WhichUnit];

										WhichUnit = DT_NEXT_UNIT;

										CurrentPay += PayPerUnit[DT_NEXT_UNIT];
									}

									SecCount++;
								}
							}

							ReleaseSemaphore(&PatternSemaphore);

								/* Show the time
								 * we have been online
								 * yet.
								 */

							TempTime = StatusTimeRequest -> tr_time;

							SubTime(&TempTime,&OnlineTime);

							if(StatusTimeRequest -> tr_time . tv_secs != LastTime . tv_secs)
							{
								LastTime = StatusTimeRequest -> tr_time;

								switch(Config -> ScreenConfig -> TimeMode)
								{
									case ONLINETIME_TIME:	ShowPay = FALSE;
												break;

									case ONLINETIME_COST:	ShowPay = TRUE;
												break;

									case ONLINETIME_BOTH:	if(TempTime . tv_secs && !(TempTime . tv_secs % 5))
													ShowPay ^= TRUE;

												break;
								}

								ObtainSemaphore(&PatternSemaphore);

								if(ShowPay && (ChosenEntry || ChosenPattern))
									UpdateInfo(SomeObject,Mode,INFO_ONLINECOST,CreateSum(CurrentPay,FALSE));
								else
									UpdateInfo(SomeObject,Mode,INFO_ONLINETIME,(TempTime . tv_secs % 86400) / 3600,(TempTime . tv_secs % 3600) / 60,TempTime . tv_secs % 60);

								ReleaseSemaphore(&PatternSemaphore);

								OnlineMinutes = TempTime . tv_secs / 60;
							}
						}
						else
						{
							ReleaseSemaphore(&OnlineSemaphore);

							if(WasOnline)
							{
								WasOnline = FALSE;

								UpdateInfo(SomeObject,Mode,INFO_ONLINETIME,(TempTime . tv_secs % 86400) / 3600,(TempTime . tv_secs % 3600) / 60,TempTime . tv_secs % 60);
							}

							if(GotOnline)
								GotOnline = FALSE;
						}

							/* Take care of the visual beep
							 * if enabled.
							 */

						if(Beeping)
						{
							if(!(BeepCount--))
							{
								Beeping = FALSE;

									/* Remove the copper list. */

								FreeVPortCopLists(&Screen -> ViewPort);

									/* Really remove it. */

								RemakeDisplay();

									/* Clear the signal bit. */

								ClrSignal(SIG_BELL);
							}
						}

							/* Are we to show a visual beep? */

						if(CheckSignal(SIG_BELL))
						{
							if(Config -> TerminalConfig -> BellMode == BELL_SYSTEM)
								DisplayBeep(Window -> WScreen);
							else
							{
								if(Screen && !Config -> ScreenConfig -> UseWorkbench && !Beeping && (Config -> TerminalConfig -> BellMode == BELL_VISIBLE || Config -> TerminalConfig -> BellMode == BELL_BOTH))
								{
									if(VisualBeep())
									{
										Beeping = TRUE;

										BeepCount = 1;
									}
								}
							}
						}

							/* Display the current terminal
							 * status.
							 */

						if(LastStatus != Status)
							UpdateInfo(SomeObject,Mode,INFO_STATUS,LastStatus = Status);

							/* Show the current transfer
							 * protocol.
							 */

						if(strcmp(LastProtocol,TransferProtocolName))
						{
							strcpy(LastProtocol,TransferProtocolName);

							if(LastProtocol[0])
								UpdateInfo(SomeObject,Mode,INFO_PROTOCOL,LastProtocol);
							else
								UpdateInfo(SomeObject,Mode,INFO_PROTOCOL,"-");
						}

							/* Show the current baud
							 * rate.
							 */

						if(LastBaud != Config -> SerialConfig -> BaudRate)
							UpdateInfo(SomeObject,Mode,INFO_BAUDRATE,LastBaud = Config -> SerialConfig -> BaudRate);

							/* Show the current
							 * terminal font.
							 */

						if(LastFrozen != BufferFrozen)
						{
							LastFrozen = BufferFrozen;

							UpdateInfo(SomeObject,Mode,INFO_BUFFER,LastFrozen != TRUE);
						}

							/* Show the current terminal
							 * emulation.
							 */

						if(LastEmulation != Config -> TerminalConfig -> EmulationMode || (Config -> TerminalConfig -> EmulationMode == EMULATION_EXTERNAL && Stricmp(EmulationName,LastEmulationName)))
						{
							LastEmulation = Config -> TerminalConfig -> EmulationMode;

							if(Config -> TerminalConfig -> EmulationMode == EMULATION_EXTERNAL)
							{
								UpdateInfo(SomeObject,Mode,INFO_EMULATION,EmulationName);

								strcpy(LastEmulationName,EmulationName);
							}
							else
								UpdateInfo(SomeObject,Mode,INFO_EMULATION,ConfigEmulation[LastEmulation]);
						}

							/* Show the current serial
							 * parameters (parity, etc).
							 */

						if(LastBitsPerChar != Config -> SerialConfig -> BitsPerChar || LastParity != Config -> SerialConfig -> Parity || LastStopBits != Config -> SerialConfig -> StopBits)
						{
							LastBitsPerChar	= Config -> SerialConfig -> BitsPerChar;
							LastParity	= Config -> SerialConfig -> Parity;
							LastStopBits	= Config -> SerialConfig -> StopBits;

							UpdateInfo(SomeObject,Mode,INFO_PARAMETERS,LastBitsPerChar,LastParity,LastStopBits);
						}

							/* Wait another half a second. */

						if(KeepGoing)
						{
							ULONG	Mask;
							BOOLEAN	ResetTime = FALSE;

							StatusTimeRequest -> tr_node . io_Command	= TR_ADDREQUEST;
							StatusTimeRequest -> tr_time . tv_secs		= 0;
							StatusTimeRequest -> tr_time . tv_micro		= MILLION / 2;

							SendIO(StatusTimeRequest);

							FOREVER
							{
								Mask = Wait(SIG_BELL | PORTMASK(StatusTimePort) | SIG_CLOSEWINDOW | SIG_RESETTIME | UpdateMask);

								if(Mask & UpdateMask)
								{
									Signal(ThisProcess,SIG_HANDSHAKE);

									Wait(UpdateMask);

									NeedFullRefresh = TRUE;

									UpdateInfo(SomeObject,Mode,INFO_STATUS,LastStatus = Status);
								}

									/* Close the window and keep quiet? */

								if(Mask & SIG_CLOSEWINDOW)
								{
									Forbid();

									if(StatusDisplayTask)
									{
										Signal(StatusDisplayTask,SIG_KILL);

										ClrSignal(SIG_HANDSHAKE);

										Wait(SIG_HANDSHAKE);
									}

									if(BoxList)
									{
										SZ_FreeBoxes(BoxList);

										BoxList = NULL;
									}

									SomeObject = NULL;

									Signal(ThisProcess,SIG_HANDSHAKE);

									Config -> ScreenConfig -> StatusLine = STATUSLINE_DISABLED;

									Permit();
								}

								if(Mask & SIG_BELL)
								{
									if(Config -> TerminalConfig -> BellMode == BELL_SYSTEM)
										DisplayBeep(Window -> WScreen);
									else
									{
										if(Screen && !Config -> ScreenConfig -> UseWorkbench && !Beeping && (Config -> TerminalConfig -> BellMode == BELL_VISIBLE || Config -> TerminalConfig -> BellMode == BELL_BOTH))
										{
											if(VisualBeep())
											{
												Beeping = TRUE;

												BeepCount = 1;
											}
										}
									}
								}

									/* Reset the online time counter. */

								if(Mask & SIG_RESETTIME)
									ResetTime = TRUE;

								if(Mask & PORTMASK(StatusTimePort))
								{
									WaitIO(StatusTimeRequest);

									break;
								}
							}

								/* Reset the online time. */

							if(ResetTime)
							{
								StatusTimeRequest -> tr_node . io_Command = TR_GETSYSTIME;

								DoIO(StatusTimeRequest);

								OnlineTime = StatusTimeRequest -> tr_time;
							}

								/* Count up to a minute. */

							if(MinCount++ == 120)
							{
								MinCount = 0;

									/* Time limit present? */

								if(LimitCount > 0)
									LimitCount--;

									/* Limit reached? */

								if(!LimitCount)
									Signal(ThisProcess,SIG_CHECK);
							}
						}

							/* Make the colours blink. */

						if(Screen && !Config -> ScreenConfig -> UseWorkbench)
						{
							if(Screen == IntuitionBase -> FirstScreen)
							{
									/* No main screen window active? */

								if(StatusWindow)
								{
									if(!(Window -> Flags & WFLG_WINDOWACTIVE) && !(StatusWindow -> Flags & WFLG_WINDOWACTIVE))
										StandardColours = TRUE;
								}
								else
								{
									if(!(Window -> Flags & WFLG_WINDOWACTIVE))
										StandardColours = TRUE;
								}

									/* Menu button pressed or window disabled? */

								if(Window -> Flags & (WFLG_MENUSTATE | WFLG_INREQUEST))
									StandardColours = TRUE;

									/* User is currently dragging the
									 * mouse in order to mark something
									 * on the screen?
									 */

								if(Marking)
									StandardColours = TRUE;

								Background = FALSE;
							}
							else
							{
								if(!Background)
									StandardColours = TRUE;

								Background = TRUE;
							}

							if(StandardColours)
							{
								if(!SetColours)
								{
									LoadColourTable(VPort,NormalColourTable,NormalColours,PaletteSize);

									SetColours = TRUE;
								}

								StandardColours = FlashIt = FALSE;
							}
							else
							{
									/* Are we to flash the display? */

								if(Config -> ScreenConfig -> Blinking)
								{
									if(Screen == IntuitionBase -> FirstScreen)
									{
										if(FlashIt)
										{
											LoadColourTable(VPort,BlinkColourTable,BlinkColours,PaletteSize);

											SetColours = FALSE;
										}
										else
										{
											LoadColourTable(VPort,NormalColourTable,NormalColours,PaletteSize);

											SetColours = TRUE;
										}
									}

									FlashIt ^= TRUE;
								}
							}
						}
					}

					CloseDevice(StatusTimeRequest);
				}

				DeleteIORequest(StatusTimeRequest);
			}

			DeleteMsgPort(StatusTimePort);
		}

		if(StatusDisplayTask)
		{
			Forbid();

			Signal(StatusDisplayTask,SIG_KILL);

			ClrSignal(SIG_HANDSHAKE);

			Wait(SIG_HANDSHAKE);

			Permit();
		}

		if(BoxList)
			SZ_FreeBoxes(BoxList);
	}

	if(UpdateSig != -1)
		FreeSignal(UpdateSig);

		/* Signal the father process that we're done
		 * and quietly remove ourselves.
		 */

	Forbid();

	Signal(ThisProcess,SIG_HANDSHAKE);

	StatusProcess = NULL;
}

	/* ForceStatusUpdate():
	 *
	 *	Make sure that the status display gets updated
	 *	as soon as possible.
	 */

VOID
ForceStatusUpdate()
{
	if(StatusProcess && Window)
	{
			// Stop the execution

		Forbid();

		Signal(StatusProcess,UpdateMask);

		ClrSignal(SIG_HANDSHAKE);

		Wait(SIG_HANDSHAKE);

		Permit();

			// Ok, the status process is waiting, now update the data

		DropMarker();

		UpdateTerminalLimits();

			// Clear the window

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

			// Restart the status process

		Signal(StatusProcess,UpdateMask);

			// Repaint the window border, in case it got trashed

		RefreshWindowFrame(Window);
	}
}
