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

#include "termGlobal.h"

enum	{	CX_TERMSCREENTOFRONT,CX_BUFFERSCREENTOFRONT,CX_SKIPDIALENTRY,CX_ABORTAREXX };

	/* Asynchronous hotkey task. */

STATIC struct Process *CxProcess;

	/* Hotkey(STRPTR Code,struct MsgPort *Port,LONG ID):
	 *
	 *	A custom version of the amiga.lib supplied code.
	 */

STATIC CxObj * __regargs
CustomHotKey(STRPTR Code,struct MsgPort *Port,LONG ID)
{
	CxObj *Filter;

	if(Filter = CxFilter(Code))
	{
		CxObj *Sender;

		if(Sender = CxSender(Port,ID))
		{
			CxObj *Translator;

			AttachCxObj(Filter,Sender);

			if(Translator = CxTranslate(NULL))
			{
				AttachCxObj(Filter,Translator);

				if(!CxObjError(Filter))
					return(Filter);
			}
		}

		DeleteCxObjAll(Filter);
	}

	return(NULL);
}

	/* CreateBroker(struct MsgPort *CxPort):
	 *
	 *	Set up a CxObj commodity broker.
	 */

STATIC CxObj * __regargs
CreateBroker(struct MsgPort *CxPort)
{
	CxObj *Broker;

		/* Set the commodity priority. */

	NewTermBroker . nb_Pri = Hotkeys . CommodityPriority;

		/* Create the broker. */

	if(Broker = CxBroker(&NewTermBroker,NULL))
	{
			/* Add the hotkeys. */

		AttachCxObj(Broker,CustomHotKey(Hotkeys . termScreenToFront,	CxPort,CX_TERMSCREENTOFRONT));
		AttachCxObj(Broker,CustomHotKey(Hotkeys . BufferScreenToFront,	CxPort,CX_BUFFERSCREENTOFRONT));
		AttachCxObj(Broker,CustomHotKey(Hotkeys . SkipDialEntry,	CxPort,CX_SKIPDIALENTRY));
		AttachCxObj(Broker,CustomHotKey(Hotkeys . AbortARexx,		CxPort,CX_ABORTAREXX));

			/* Did an error show up? */

		if(!CxObjError(Broker))
		{
				/* Broker has been added, now activate it. */

			ActivateCxObj(Broker,Hotkeys . HotkeysEnabled);

			return(Broker);
		}

		DeleteCxObjAll(Broker);
	}

	return(NULL);
}

	/* TermCxServer():
	 *
	 *	Asynchronous hotkey server.
	 */

STATIC VOID __saveds
TermCxServer(VOID)
{
	CxObj		*Broker;
	struct MsgPort	*CxPort;
	CxMsg		*Message;

		/* Create a reply port. */

	if(CxPort = CreateMsgPort())
	{
			/* Add the port to the public list. */

		CxPort -> mp_Node . ln_Name = NewTermBroker . nb_Name;

		AddPort(CxPort);

			/* Install the port. */

		NewTermBroker . nb_Port	= CxPort;

			/* Create the broker. */

		if(Broker = CreateBroker(CxPort))
		{
			ULONG	SignalSet;
			BYTE	Terminated = FALSE;

				/* Signal father task that we're done. */

			Signal(ThisProcess,SIG_HANDSHAKE);

				/* Loop and loop... */

			while(!Terminated)
			{
					/* Wait for some signal. */

				SignalSet = Wait(SIG_KILL | SIG_RESET | PORTMASK(CxPort));

					/* ^C aborts. */

				if(SignalSet & SIG_KILL)
					Terminated = TRUE;

					/* ^D removes the broker and
					 * creates a new one.
					 */

				if(SignalSet & SIG_RESET)
				{
					DeleteCxObjAll(Broker);

					Broker = CreateBroker(CxPort);
				}

					/* A commodity message. */

				if(SignalSet & PORTMASK(CxPort))
				{
					ULONG MessageType,MessageID;

						/* Remove all messages. */

					while(Message = (CxMsg *)GetMsg(CxPort))
					{
							/* Extract type and ID. */

						MessageType	= CxMsgID(Message);
						MessageID	= CxMsgType(Message);

						ReplyMsg((struct Message *)Message);

							/* Take a look at the type... */

						switch(MessageID)
						{
								/* A hotkey was pressed. */

							case CXM_IEVENT:

								switch(MessageType)
								{
									case CX_TERMSCREENTOFRONT:

										if(Window)
											BumpWindow(TopWindow);
										else
											Signal(ThisProcess,SIGBREAKF_CTRL_F);

										break;

									case CX_BUFFERSCREENTOFRONT:

										LaunchBuffer();
										break;

									case CX_SKIPDIALENTRY:

										Signal(ThisProcess,SIG_SKIP);
										break;

									case CX_ABORTAREXX:

										if(InRexx)
											Signal(ThisProcess,SIG_BREAK);

										break;
								}

								break;

								/* An internal commodity command. */

							case CXM_COMMAND:

								switch(MessageType)
								{
									case CXCMD_DISABLE:

										ActivateCxObj(Broker,Hotkeys . HotkeysEnabled = FALSE);
										break;

									case CXCMD_ENABLE:

										ActivateCxObj(Broker,Hotkeys . HotkeysEnabled = TRUE);
										break;
								}

								break;
						}
					}
				}
			}

				/* Remove the broker. */

			DeleteCxObjAll(Broker);
		}

			/* Remove the port from the public list. */

		RemPort(CxPort);

			/* Remove all pendig messages. */

		while(Message = (CxMsg *)GetMsg(CxPort))
			ReplyMsg((struct Message *)Message);

			/* Delete the reply port. */

		DeleteMsgPort(CxPort);
	}

	Forbid();

		/* Clear the task ID. */

	CxProcess = NULL;

		/* Signal father process that we're done. */

	Signal(ThisProcess,SIG_HANDSHAKE);
}

	/* ShutdownCx():
	 *
	 *	Remove the hotkey task.
	 */

VOID
ShutdownCx()
{
	if(CxProcess)
	{
		Forbid();

		Signal(CxProcess,SIG_KILL);

		ClrSignal(SIG_HANDSHAKE);

		Wait(SIG_HANDSHAKE);

		Permit();
	}
}

	/* SetupCx():
	 *
	 *	Create the hotkey task.
	 */

BYTE
SetupCx()
{
		/* If the task is already running, tell it to
		 * update the hotkey settings.
		 */

	if(CxProcess)
	{
		Signal(CxProcess,SIG_RESET);

		return(TRUE);
	}
	else
	{
		Forbid();

		if(CxProcess = (struct Process *)CreateNewProcTags(
			NP_Entry,	TermCxServer,
			NP_Name,	"term Hotkey Process",
			NP_Priority,	0,
			NP_StackSize,	8192,
			NP_WindowPtr,	-1,
		TAG_END))
		{
			ClrSignal(SIG_HANDSHAKE);

			Wait(SIG_HANDSHAKE);
		}

		Permit();

		if(CxProcess)
			return(TRUE);
	}

	return(FALSE);
}

	/* LoadHotkeys(STRPTR Name,struct Hotkeys *Keys):
	 *
	 *	Load the hotkey settings from a file.
	 */

BYTE __regargs
LoadHotkeys(STRPTR Name,struct Hotkeys *Keys)
{
	struct IFFHandle	*Handle;
	BYTE			 Success = FALSE;
	struct StoredProperty	*Prop;
	struct TermInfo		*TermInfo;
	LONG			 Error = 0;

	if(Handle = AllocIFF())
	{
		if(Handle -> iff_Stream = Open(Name,MODE_OLDFILE))
		{
			InitIFFasDOS(Handle);

			if(!(Error = OpenIFF(Handle,IFFF_READ)))
			{
				/* Collect version number ID if
				 * available.
				 */

				if(!(Error = PropChunks(Handle,(LONG *)VersionProps,1)))
				{
					/* The following line tells iffparse to stop at the
					 * very beginning of a `Type' chunk contained in a
					 * `TERM' FORM chunk.
					 */

					if(!(Error = StopChunk(Handle,ID_TERM,ID_HOTK)))
					{
						/* Parse the file... */

						if(!ParseIFF(Handle,IFFPARSE_SCAN))
						{
							/* Did we get a version ID? */

							if(Prop = FindProp(Handle,ID_TERM,ID_VERS))
							{
								TermInfo = (struct TermInfo *)Prop -> sp_Data;

								if((TermInfo -> Version < TermVersion) || (TermInfo -> Version == TermVersion && TermInfo -> Revision < TermRevision))
								{
									if(ReadChunkBytes(Handle,Keys,sizeof(struct HotkeysOld)) == sizeof(struct HotkeysOld))
									{
										strcpy(Keys -> AbortARexx,"lshift rshift escape");

										Success = TRUE;
									}
									else
										Error = IoErr();
								}
								else
								{
									if(ReadChunkBytes(Handle,Keys,sizeof(struct Hotkeys)) == sizeof(struct Hotkeys))
										Success = TRUE;
									else
										Error = IoErr();
								}
							}
						}
					}
				}

				CloseIFF(Handle);
			}

			Close(Handle -> iff_Stream);
		}
		else
			Error = IoErr();

		FreeIFF(Handle);
	}
	else
		Error = ERR_NO_MEM;

	if(Error)
		SetIoErr(Error);

	return(Success);
}
