/*
 * OneKeyII
 * 
 * Inspired by OneKey, a program by Carolyn Scheppner of CATS.
 * This incarnation fixes some problems OneKey had with 2.1,
 * adds a few useful features and improves compatibility with
 * other programs.
 *
 * Code was rather cobbled together with stuff I had lying at
 * hand, so please excuse any lack of clarity.
 *
 * #include <std_disclaimer.h>	// no warranties attached.
 * Placed in the public domain by
 * Martin W. Scott, Monday 23-Nov-92.
 */
#include <exec/libraries.h>
#include <libraries/commodities.h>
#include <dos/dos.h>
#include <clib/exec_protos.h>
#include <clib/alib_protos.h>
#include <clib/alib_stdio_protos.h>
#include <clib/commodities_protos.h>
#include <devices/inputevent.h>

#include "icon.h"	/* my icon routines */

#define INTERRUPT void __interrupt __saveds

BOOL OpenLibs(void);
void CloseLibs(void);
void _main(void);
void ProcessMsg(void);
INTERRUPT OneKeyHandler(CxMsg *, CxObj *);

extern void DrawState(UWORD);
extern void InitWindowPos(void);
extern BOOL ShowWindow(void);
extern void HideWindow(void);
extern BOOL HandleIDCMP(void);

extern struct WBStartup *WBenchMsg;
struct Library *CxBase, *IconBase, *GadToolsBase;
struct GfxBase *GfxBase;
struct IntuitionBase *IntuitionBase;
struct MsgPort *broker_mp;
CxObj *broker, *cocustom, *popobj;

struct NewBroker newbroker =
{
    NB_VERSION,
    "OneKeyII",			/* string to identify this broker */
    "OneKeyII",
    "Type one-key-at-a-time (public domain)",
    NBU_UNIQUE | NBU_NOTIFY,
    COF_SHOW_HIDE
};

UBYTE *vers = "\0$VER: OneKeyII 1.0";

struct Task *task;
ULONG cxsigflag, signal, cxobjsignal;
UWORD quals;
BOOL toggle;


#define EVT_POPKEY 1L

BOOL
OpenLibs()			/* open required libraries */
{
    if ((GfxBase = (void *) OpenLibrary("graphics.library", 0L)) &&
	(IntuitionBase = (void *) OpenLibrary("intuition.library", 37L)) &&
	(GadToolsBase = (void *) OpenLibrary("gadtools.library", 37L)) &&
	(IconBase = (void *) OpenLibrary("icon.library", 37L)) &&
	(CxBase = (void *) OpenLibrary("commodities.library", 37L)))
	return TRUE;
    CloseLibs();
    return FALSE;
}

void
CloseLibs()			/* close opened libraries */
{
    if (IconBase)
	CloseLibrary(IconBase);
    if (CxBase)
	CloseLibrary(CxBase);
    if (GfxBase)
	CloseLibrary(GfxBase);
    if (IntuitionBase)
	CloseLibrary(IntuitionBase);
    if (GadToolsBase)
	CloseLibrary(GadToolsBase);
}

void
_main()
{
    UBYTE *popstr;
    CxMsg *msg;

    if (OpenLibs())
    {
	if (broker_mp = CreateMsgPort())
	{
	    newbroker.nb_Port = broker_mp;
	    cxsigflag = 1L << broker_mp->mp_SigBit;

	    GetOurIcon(WBenchMsg);
	    newbroker.nb_Pri = (BYTE) TTInt("CX_PRIORITY", 127);
	    popstr = TTString("CX_POPKEY", "control 1");

	    if (broker = CxBroker(&newbroker, NULL))
	    {

		if (cocustom = CxCustom(OneKeyHandler, 0L))
		{
		    AttachCxObj(broker, cocustom);

		    /* Allocate a signal bit for the signal CxObj */
		    if ((signal = (ULONG) AllocSignal(-1L)) != -1)
		    {
			/* set up the signal mask */
			cxobjsignal = 1L << signal;
			cxsigflag |= cxobjsignal;
			task = FindTask(NULL);

			/* NB: hotkey added AFTER handler, so it works with one-key */
			if (popobj = HotKey(popstr, broker_mp, EVT_POPKEY))
			{
			    AttachCxObj(broker, popobj);

			    toggle = TTBool("TOGGLE", FALSE);
			    InitWindowPos();
			    if (TTBool("CX_POPUP", TRUE))
				ShowWindow();
			    FreeOurIcon();

			    ActivateCxObj(broker, 1L);
			    ProcessMsg();
			    HideWindow();

			    FreeSignal(signal);
			}

		    }
		}
		DeleteCxObjAll(broker);

		/* Empty the port of all CxMsgs */
		while (msg = (CxMsg *) GetMsg(broker_mp))
		    ReplyMsg((struct Message *) msg);

	    }
	    DeleteMsgPort(broker_mp);
	}
	CloseLibs();
    }
}

void
ProcessMsg(void)
{
    extern struct MsgPort *broker_mp;
    extern CxObj *broker;
    extern ULONG cxsigflag, winsigflag;
    extern struct Window *window;
    CxMsg *msg;
    ULONG sigrcvd, msgid, msgtype;
    LONG returnvalue = 1L;

    while (returnvalue)
    {
	sigrcvd = Wait(SIGBREAKF_CTRL_C | cxsigflag | winsigflag);

	while (msg = (CxMsg *) GetMsg(broker_mp))
	{
	    msgid = CxMsgID(msg);
	    msgtype = CxMsgType(msg);
	    ReplyMsg((struct Message *) msg);

	    switch (msgtype)
	    {
	    case CXM_IEVENT:
		if (msgid == EVT_POPKEY)
		    if (window)
			WindowToFront(window);
		    else
			ShowWindow();
		break;
	    case CXM_COMMAND:
		switch (msgid)
		{
		case CXCMD_UNIQUE:
		case CXCMD_APPEAR:
		    ShowWindow();
		    break;
		case CXCMD_DISAPPEAR:
		    HideWindow();
		    break;
		case CXCMD_DISABLE:
		    ActivateCxObj(broker, 0L);
		    break;
		case CXCMD_ENABLE:
		    ActivateCxObj(broker, 1L);
		    break;
		case CXCMD_KILL:
		    returnvalue = 0L;
		    break;
		}
	    }
	}

	if (sigrcvd & SIGBREAKF_CTRL_C)
	    returnvalue = 0L;

	if (sigrcvd & winsigflag)
	    if (!HandleIDCMP())
		returnvalue = 0L;

	/* Check to see if the signal CxObj signalled us. */
	if ((sigrcvd & cxobjsignal) && window)
	    DrawState(quals);
    }
}

/******************************************************************************/
/*									      */
/*	ONEKEY HANDLER							      */
/*									      */
/******************************************************************************/

#define KCNT	7
#define CODE_LSHIFT	0x60
#define CODE_RSHIFT	0x61
#define CODE_CONTROL	0x63
#define CODE_LALT	0x64
#define CODE_RALT	0x65
#define CODE_LAMIGA	0x66
#define CODE_RAMIGA	0x67

UWORD kcodes[] =
{
    CODE_LSHIFT, CODE_RSHIFT,
    CODE_LALT, CODE_RALT,
    CODE_LAMIGA, CODE_RAMIGA,
    CODE_CONTROL
};

UWORD kquals[] =
{
    IEQUALIFIER_LSHIFT, IEQUALIFIER_RSHIFT,
    IEQUALIFIER_LALT, IEQUALIFIER_RALT,
    IEQUALIFIER_LCOMMAND, IEQUALIFIER_RCOMMAND,
    IEQUALIFIER_CONTROL
};

#define ALL_BUTTONS     (IEQUALIFIER_LEFTBUTTON|IEQUALIFIER_RBUTTON|IEQUALIFIER_MIDBUTTON)
#define KEY_QUAL	(IEQUALIFIER_LSHIFT|IEQUALIFIER_RSHIFT \
			|IEQUALIFIER_CONTROL \
			|IEQUALIFIER_LALT|IEQUALIFIER_RALT \
			|IEQUALIFIER_LCOMMAND|IEQUALIFIER_RCOMMAND)

INTERRUPT
OneKeyHandler(register CxMsg * cxm, CxObj * co)
{
    extern struct Window *window;	/* display window */
    struct InputEvent *ie;
    UWORD oldquals, i;
    BOOL wasqual;		/* is this a qualifier? */

    ie = (struct InputEvent *) CxMsgData(cxm);
    oldquals = quals;	/* spot the difference below... */

    if (ie->ie_Class == IECLASS_RAWKEY)
    {
	if (!(ie->ie_Code & IECODE_UP_PREFIX))	/* a downstroke */
	{
	    /* is code a qualifier key? */
	    for (i = 0, wasqual = FALSE; i < KCNT; i++)
		if (ie->ie_Code == kcodes[i])
		{
		    if (toggle)
			quals ^= kquals[i];
		    else
			quals |= kquals[i];
		    wasqual = TRUE;
		}

	    if (!wasqual && quals)	/* non-qualifier - add quals */
	    {
		ie->ie_Qualifier |= quals;	/* coallesce qualifiers */
		quals = 0;			/* reset them */
	    }
	}
    }
    else if (ie->ie_Class == IECLASS_RAWMOUSE)  /* mouse-cancel? */
    {
	if (ie->ie_Code != IECODE_NOBUTTON && (ie->ie_Qualifier & KEY_QUAL))
	    quals = 0;
    }

    if (oldquals != quals && window)	/* update display */
	Signal(task, cxobjsignal);
}
