#include <string.h>
#include <stdarg.h>
#include <exec/libraries.h>
#include <exec/memory.h>
#include <libraries/commodities.h>
#include <libraries/gadtools.h>
#include <intuition/gadgetclass.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 <intuition/intuition.h>
#include <devices/inputevent.h>
#include <clib/AMarquee_protos.h>
#include <pragmas/AMarquee_pragmas.h>
#include <exec/types.h>
#include <hardware/cia.h>

#include "ARemote_temp_aux.h"

#define CIAA 0xBFE001

#define AREMOTE_NAME "ARemote v1.00B"

const char version[] = "$VER: ARemote 1.00B";

#define DEFAULT_AREMOTE_PASSWORD  "ARemote"
#define DEFAULT_AREMOTE_PORT      20000
#define DEFAULT_AREMOTE_TOGGLEKEY "lcommand shift x"
#define DEFAULT_AREMOTE_POPKEY    "lcommand y"

#define EVENT_TOGGLEKEY  1
#define EVENT_SHOWWINDOW 2

#define UNLESS(x) if(!(x))

int main(int, char **);
void ProcessMsg(struct MsgPort * broker_mp);
void CxFunction(CxMsg *, CxObj *);

char * connectTo = NULL;
int connectToPort;
char * connectToPassword = NULL;
char * acceptFrom  = NULL;
int acceptFromPort;
char * acceptFromPassword = NULL;
BOOL BConnectOnStartup;
BOOL BXmitCue;
char * toggleKey = NULL;
char * popKey = NULL;

/* Internal state info */
BOOL BEnabled              = TRUE;
BOOL BConnectOkay          = FALSE; 
BOOL BWindowOpen           = FALSE;
BOOL BGUINeedsUpdate       = FALSE;
BOOL BSessionIsHostSession = FALSE;
BOOL BXmitting             = FALSE;  /* True iff we are diverting all input to the TCP stream */
BOOL BStartedFromWB        = FALSE;

struct QSession * session     = NULL;
struct Library * AMarqueeBase = NULL, * CxBase        = NULL, * IconBase = NULL,
               * GadToolsBase = NULL, * GfxBase  = NULL,
               * UtilityBase  = NULL, * DiskFontBase  = NULL;
struct Task * mainTask;

struct MsgPort * event_mp;
CxObj * cxBroker = NULL, * cxFilter = NULL, * cxCustom = NULL, * cxSender = NULL, * cxDrain1 = NULL, * cxDrain2 = NULL, * cxPopFilter = NULL, * cxPopSender = NULL, * cxPopDrain = NULL;

ULONG winRequests;

struct NewBroker newbroker =
{ 
  NB_VERSION,
  "ARemote",
  "ARemote",
  "Remote-control your Amiga",
  NBU_UNIQUE | NBU_NOTIFY,
  COF_SHOW_HIDE, 0, 0, 0
};

int MakeReq(char *sTitle, char *sText, char *sGadgets)
{
	struct EasyStruct myreq;
	int nResult;

	UNLESS(sTitle)   sTitle   = "ARemote Message";
	UNLESS(sText)    sText    = "Check this out!";
	UNLESS(sGadgets) sGadgets = "OK";

	myreq.es_TextFormat   = sText;
	myreq.es_Title        = sTitle;
	myreq.es_GadgetFormat = sGadgets;

    if (IntuitionBase) nResult = EasyRequest(NULL, &myreq, NULL, NULL, NULL);
                  else printf("Notice: [%s] [%s]\n",sTitle, sText);
       
	return(nResult);
}


/* does dynamic allocating and freeing of char strings */
/* The old value will not be freed unless a new one could be allocated. */
/* (or no new value was requested) */
char * SetString(char * oldString, char * newVal)
{
  char * newString;
    
  if (newVal)
  {
    if (newString = AllocVec(strlen(newVal)+1, MEMF_ANY))
    {
      strcpy(newString, newVal);
      if (oldString) FreeVec(oldString);
      return(newString);
    }
    else 
    {
      DisplayBeep(NULL);
      return(oldString);
    }
  }
  else if (oldString) FreeVec(oldString);
  
  return(NULL);
}

void StatMsg(char * msg)
{
  static char retain[200] = "ARemote Ready.";
  
  if (msg) strncpy(retain, msg, sizeof(retain));
  if (BWindowOpen) SetWindowTitles(ARemoteWnd, retain, (char *)~0);
}

/* Makes pcString lower case */
char * ToLower(char * pcString)
{
	char * pcTemp = pcString;
	
	while(*pcTemp) 
	{
		if ((*pcTemp >= 'A')&&(*pcTemp <= 'Z'))
			*pcTemp += 'a'-'A';
		pcTemp++;
	}
	return(pcString);
}

void edebug(int i)
{
  int a = *((int *)i);
}

#define UPDATEMENUFLAG(item, variable, flag) {if (variable) item->Flags |= flag; else item->Flags &= ~(flag);}
void UpdateGUIFromState(void)
{
  if (BWindowOpen)
  {
    struct Menu *currentMenu = ARemoteMenus;  /* Project Menu */
    struct MenuItem *currentItem;

    /* Go through all gadgets and menu items, set them to the correct state */
    GT_SetGadgetAttrs(ARemoteGadgets[GD_ConnectToString],   ARemoteWnd, NULL, GTST_String, connectTo,      TAG_END);
    GT_SetGadgetAttrs(ARemoteGadgets[GD_AcceptFromString],  ARemoteWnd, NULL, GTST_String, acceptFrom,     TAG_END);
    GT_SetGadgetAttrs(ARemoteGadgets[GD_ConnectToPortInt],  ARemoteWnd, NULL, GTIN_Number, connectToPort,  TAG_END);
    GT_SetGadgetAttrs(ARemoteGadgets[GD_AcceptFromPortInt], ARemoteWnd, NULL, GTIN_Number, acceptFromPort, TAG_END);
    GT_SetGadgetAttrs(ARemoteGadgets[GD_ToggleString],      ARemoteWnd, NULL, GTST_String, toggleKey,      TAG_END);
    GT_SetGadgetAttrs(ARemoteGadgets[GD_ConnectToPasswordString],  ARemoteWnd, NULL, GTST_String, connectToPassword,  TAG_END);
    GT_SetGadgetAttrs(ARemoteGadgets[GD_AcceptFromPasswordString], ARemoteWnd, NULL, GTST_String, acceptFromPassword, TAG_END);
    GT_SetGadgetAttrs(ARemoteGadgets[GD_ConnectOnStartupCheckbox], ARemoteWnd, NULL, GTCB_Checked, BConnectOnStartup, TAG_END);
    GT_SetGadgetAttrs(ARemoteGadgets[GD_ConnectButton],    ARemoteWnd, NULL, GA_Disabled, BConnectOkay, TAG_END);
    GT_SetGadgetAttrs(ARemoteGadgets[GD_DisconnectButton], ARemoteWnd, NULL, GA_Disabled, !BConnectOkay, TAG_END);
    GT_SetGadgetAttrs(ARemoteGadgets[GD_XmitCueCheckbox],  ARemoteWnd, NULL, GTCB_Checked, BXmitCue, TAG_END);
    GT_SetGadgetAttrs(ARemoteGadgets[GD_PopupHotkeyString],ARemoteWnd, NULL, GTST_String, popKey, TAG_END);
    
    ClearMenuStrip(ARemoteWnd);
     currentItem = currentMenu->FirstItem;  /* About... */
     currentItem = currentItem->NextItem;  /* Bar */
     currentItem = currentItem->NextItem;  /* Enabled */
     UPDATEMENUFLAG(currentItem, BEnabled, CHECKED);

     currentItem = currentItem->NextItem;   /* Save Settings */
     UPDATEMENUFLAG(currentItem, FALSE, ITEMENABLED);
    ResetMenuStrip(ARemoteWnd, ARemoteMenus);
  }
  BGUINeedsUpdate = FALSE;
}

/* return TRUE iff the string represents the boolean value true */
BOOL ParseBool(char * string)
{
  char temp[100];
  
  strncpy(temp,string,sizeof(temp));
  temp[99]=0;
  
  ToLower(temp);
  
  return(
    (*temp=='\0')              ||  /* then it's just KEYWORD, so yes, right? */
    (strcmp("true",temp) == 0) ||
    (strcmp("yes",temp)  == 0) ||
    (strcmp("1",temp)    == 0));   /* Anyone who specifies 1 for true is a weenie! */
}

void StartTransmitting(void)
{
  if (BXmitting) return;  /* we're already there */
  
  if (BConnectOkay) 
  {
    char szTemp[300];
    
    strcpy(szTemp, "[");
    strncat(szTemp, toggleKey, sizeof(szTemp));
    strncat(szTemp, "] to stop.", sizeof(szTemp));
    StatMsg(szTemp);
    
    AttachCxObj(cxBroker, cxCustom);
    AttachCxObj(cxBroker, cxDrain1);
    BXmitting = TRUE;
  }
  else
  {
    DisplayBeep(NULL);
    StatMsg("Can't transmit: not connected!");
  }
}

void StopTransmitting(void)
{
  if (BXmitting) 
  {
    StatMsg("Transmission stopped.");
    RemoveCxObj(cxCustom);
    RemoveCxObj(cxDrain1);
  }
  BXmitting = FALSE;
}

void DoCloseWindow(void)
{
  if (BWindowOpen) 
  {
    CloseARemoteWindow(); 
    BWindowOpen = FALSE;
  } 
}

void Disconnect(void)
{
  StopTransmitting();
  if (session) 
  {
    QFreeSession(session);
    if ((BSessionIsHostSession==FALSE)||(BConnectOkay))
      StatMsg(BConnectOkay ? "Connection closed." : "Couldn't connect.");
  }
  session = NULL;
  BConnectOkay = FALSE;
  BGUINeedsUpdate = TRUE;
}


void Accept(void)
{
  int port = acceptFromPort;
    
  Disconnect();
  session = QNewHostSession(acceptFrom, &port, acceptFromPassword);
  BSessionIsHostSession = TRUE;
}

void DoOpenWindow(void)
{
  if (BWindowOpen == FALSE)
  {
    int error;

    if (error = OpenARemoteWindow())
    {
      printf("Window open failed, error %i\n",error);
      CloseARemoteWindow();
    }
    else 
    {
      BWindowOpen = TRUE;
      UpdateGUIFromState();
      StatMsg(NULL);
    }
  }
}

void EnableCommodity(void)
{
  ActivateCxObj(cxBroker, 1L);
  BEnabled = TRUE;
  BGUINeedsUpdate = TRUE;
}

void DisableCommodity(void)
{
  ActivateCxObj(cxBroker, 0L);
  BEnabled = FALSE;
  BGUINeedsUpdate = TRUE;
}


void ShowAboutBox(void)
{
  MakeReq(NULL, AREMOTE_NAME"\nby Jeremy Friesner\njfriesne@ucsd.edu\nCompiled: "__DATE__,"Yowza");
}

void UpdateStateFromGUI(void)
{  
  if (BWindowOpen)
  {
    connectTo          = SetString(connectTo, GetString(ARemoteGadgets[GD_ConnectToString]));
    acceptFrom         = SetString(acceptFrom, GetString(ARemoteGadgets[GD_AcceptFromString]));
    toggleKey          = SetString(toggleKey, GetString(ARemoteGadgets[GD_ToggleString]));
    connectToPassword  = SetString(connectToPassword, GetString(ARemoteGadgets[GD_ConnectToPasswordString]));
    acceptFromPassword = SetString(acceptFromPassword, GetString(ARemoteGadgets[GD_AcceptFromPasswordString]));
    popKey             = SetString(popKey, GetString(ARemoteGadgets[GD_PopupHotkeyString]));

    connectToPort     = GetNumber(ARemoteGadgets[GD_ConnectToPortInt]);
    acceptFromPort    = GetNumber(ARemoteGadgets[GD_AcceptFromPortInt]);
    BConnectOnStartup = (((ARemoteGadgets[GD_ConnectOnStartupCheckbox]->Flags)&GFLG_SELECTED) != 0);
    BXmitCue          = (((ARemoteGadgets[GD_XmitCueCheckbox]->Flags)&GFLG_SELECTED) != 0);
  }
}

void DoChangeAccept(void)
{
  /* Only make a new host session if we have no session, or we have an unconnected host session */
  if ((session == NULL)||((BSessionIsHostSession)&&(BConnectOkay == FALSE))) Accept();
}


/* Returns library name on failure, NULL on success */
char * OpenAllLibraries(struct Library ** setLibPtr, char * libName, ULONG libVersion, ...)
{
  va_list vlist;
  BOOL BFirst = TRUE;
    
  va_start(vlist, setLibPtr);
  while((BFirst)||(setLibPtr = va_arg(vlist, struct Library **)))
  {
    libName    = va_arg(vlist, char *);
    libVersion = va_arg(vlist, ULONG);
    UNLESS(*setLibPtr = OpenLibrary(libName, libVersion)) return(libName);
    BFirst = FALSE;
  }
  va_end(vlist);
  return(NULL);
}

void CloseAllLibraries(struct Library ** closeLib, ...)
{
  va_list vlist;
  
  if (closeLib == NULL) return;
  if (*closeLib) 
  {
    CloseLibrary(*closeLib);
    *closeLib = NULL;
  }
  
  va_start(vlist, closeLib);
  while(closeLib = va_arg(vlist, struct Library **)) 
  {
    if (*closeLib) 
    {
      CloseLibrary(*closeLib);
      *closeLib = NULL;
    }
  }
  va_end(vlist);
}

/* Allocates default strings, does default settings */
BOOL CreateDefaultState(UBYTE ** ttypes)
{
  UNLESS((connectTo          = SetString(NULL, ArgString(ttypes, "CONNECTTO", ""))) &&
         (connectToPassword  = SetString(NULL, ArgString(ttypes, "CONNECTTOPASSWORD", DEFAULT_AREMOTE_PASSWORD))) &&
         (acceptFrom         = SetString(NULL, ArgString(ttypes, "ACCEPTFROM", ""))) &&
         (acceptFromPassword = SetString(NULL, ArgString(ttypes, "ACCEPTFROMPASSWORD", DEFAULT_AREMOTE_PASSWORD))) &&
         (toggleKey          = SetString(NULL, ArgString(ttypes, "TOGGLEKEY", DEFAULT_AREMOTE_TOGGLEKEY))) && 
         (popKey             = SetString(NULL, ArgString(ttypes, "CX_POPKEY", DEFAULT_AREMOTE_POPKEY)))) return(FALSE);

  BXmitCue = ParseBool(ArgString(ttypes, "FLASHLED", "FALSE"));
  BConnectOnStartup = ParseBool(ArgString(ttypes, "CONNECTONSTARTUP", "FALSE"));
  connectToPort  = ArgInt(ttypes, "CONNECTTOPORT", DEFAULT_AREMOTE_PORT);
  acceptFromPort = ArgInt(ttypes, "ACCEPTFROMPORT", DEFAULT_AREMOTE_PORT);
    
  /* disable myself? */
  if (ParseBool(ArgString(ttypes, "ENABLED", "TRUE")) == FALSE) Signal(FindTask(NULL), SIGBREAKF_CTRL_D);
  
  /* open window? */
  if (ParseBool(ArgString(ttypes, "CX_POPUP", "TRUE"))) Signal(FindTask(NULL), SIGBREAKF_CTRL_F);
  
  return(TRUE);
}

/* Frees all setting strings */
void FreeState()
{
  connectTo          = SetString(connectTo,          NULL);
  connectToPassword  = SetString(connectToPassword,  NULL);
  acceptFrom         = SetString(acceptFrom,         NULL);
  acceptFromPassword = SetString(acceptFromPassword, NULL);
  toggleKey          = SetString(toggleKey,          NULL);
  popKey             = SetString(popKey,             NULL);
}

void wbmain(struct WBStartup *wbargv)
{    
  BStartedFromWB = TRUE;
  main(0,wbargv);
}

int main(int argc, char ** argv)
{
  UBYTE ** ttypes;
  CxMsg * msg;
  char * errorLib;
    
  mainTask = FindTask(NULL);

  if ((BStartedFromWB == FALSE)&&(argc>=2)&&(*argv[1]=='?'))
  {
    printf("Usage: ARemote CONNECTTO/K,CONNECTTOPASSWORD/K,CONNECTTOPORT/K/N,\n"
           "               ACCEPTFROM/K,ACCEPTFROMPASSWORD/K,ACCEPTFROMPORT/K/N,\n"
           "               TOGGLEKEY/K,CX_POPKEY/K,CX_POPUP/K,CX_PRIORITY/K/N\n"
           "               FLASHLED/K,CONNECTONSTARTUP/K,ENABLED/K,ALLOWMULTIPLE/K\n");
    exit(5);
  }  

  if (errorLib = OpenAllLibraries(
   &AMarqueeBase,  "amarquee.library",    43L,
   &CxBase,        "commodities.library", 37L,
   &IconBase,      "icon.library",        36L,
   &GadToolsBase,  "gadtools.library",    37L,
   &IntuitionBase, "intuition.library",   37L,
   &GfxBase,       "graphics.library",    37L,
   &UtilityBase,   "utility.library",     37L,
   &DiskFontBase,  "diskfont.library",    38L,  NULL))
  {
    char temp[300];
    
    sprintf(temp, "Error: Couldn't open library [%s]",errorLib);
    MakeReq(NULL,temp,NULL);
  }
  else
  {
    struct MsgPort * broker_mp;

    if (SetupScreen()) MakeReq(NULL, "Couldn't lock screen!", NULL);
    else
    {
      int got_to = 0;
      
      if (event_mp = CreateMsgPort())
      {
        got_to = 1;
        
        if (broker_mp = CreateMsgPort())
        { 
          got_to = 2;
          newbroker.nb_Port = broker_mp;
        
          ttypes = ArgArrayInit(argc, argv);
          newbroker.nb_Pri = (BYTE) ArgInt(ttypes, "CX_PRIORITY", 127);
          if (ParseBool(ArgString(ttypes, "ALLOWMULTIPLE", "FALSE"))) newbroker.nb_Unique &= ~(NBU_UNIQUE);
          
          if (CreateDefaultState(ttypes))
          {
            got_to = 3;
            if ((cxPopFilter = CxFilter(popKey))                  &&
                (cxPopSender = CxSender(broker_mp, EVENT_SHOWWINDOW)) &&
                (cxPopDrain  = CxTranslate(NULL))                 &&
                (cxFilter = CxFilter(toggleKey))                  &&
                (cxSender = CxSender(broker_mp, EVENT_TOGGLEKEY)) &&
                (cxCustom = CxCustom(CxFunction, 0L))             &&
                (cxDrain1 = CxTranslate(NULL))                    &&
                (cxDrain2 = CxTranslate(NULL))                    &&
                (cxBroker = CxBroker(&newbroker, NULL)))
            {
              got_to = 4;
              /* Catch the cx_popup hotkey */
              AttachCxObj(cxBroker,    cxPopFilter);
              AttachCxObj(cxPopFilter, cxPopSender);
              AttachCxObj(cxPopFilter, cxPopDrain);
              
              /* Catch the toggle hotkey */
              AttachCxObj(cxBroker, cxFilter);
              AttachCxObj(cxFilter, cxSender);
              AttachCxObj(cxFilter, cxDrain2);

              ActivateCxObj(cxBroker, 1L);

              ProcessMsg(broker_mp);
            }

            /* Clean up */
            if (cxPopFilter) DeleteCxObj(cxPopFilter);
            if (cxPopSender) DeleteCxObj(cxPopSender);
            if (cxPopDrain)  DeleteCxObj(cxPopDrain);
            if (cxDrain1) DeleteCxObj(cxDrain1);
            if (cxDrain2) DeleteCxObj(cxDrain2);
            if (cxCustom) DeleteCxObj(cxCustom);
            if (cxSender) DeleteCxObj(cxSender);
            if (cxFilter) DeleteCxObj(cxFilter);
            if (cxBroker) DeleteCxObj(cxBroker);
            while(msg = (CxMsg *) GetMsg(broker_mp)) ReplyMsg((struct Message *)msg);
          }
          FreeState();
          DeletePort(broker_mp);
        }
         
        while(msg = (CxMsg *) GetMsg(event_mp)) FreeMem(msg, ((struct Message *)msg)->mn_Length);
        DeletePort(event_mp);
      }
      ArgArrayDone();
      
      if (got_to != 4) 
      {
        char temp[300];
        
        sprintf(temp, "Error setting up in stage %i", got_to);
        MakeReq(NULL, temp, NULL);
      }
    }
    DoCloseWindow();
    CloseDownScreen();  /* must be called even if SetupScreen() failed */
  }
  CloseAllLibraries(&AMarqueeBase, &CxBase, &IconBase, &GadToolsBase,
                    &IntuitionBase, &GfxBase, &UtilityBase, &DiskFontBase, NULL);
  return(0);
}


void Connect(void)
{  
  char temp[300];
  
  Disconnect();
  session = QNewSessionAsync(connectTo, connectToPort, connectToPassword);
  BSessionIsHostSession = FALSE;
  
  if (session)
  {
    strcpy(temp, "Connecting: [");
    strncat(temp, connectTo, sizeof(temp));
    strncat(temp, "]", sizeof(temp));
    StatMsg(temp);
  }
  else StatMsg("Can't make connection.");
}

void ChangeToggle(void)
{
  CxObj * newFilter;

  if (newFilter = CxFilter(toggleKey))
  {
    DeleteCxObj(cxFilter);
    cxFilter = newFilter;
    
    AttachCxObj(cxFilter, cxSender);
    AttachCxObj(cxFilter, cxDrain2);
    AttachCxObj(cxBroker, cxFilter);    
  }
  else DisplayBeep(NULL);
}

void ChangePopup(void)
{
  CxObj * newFilter;

  if (newFilter = CxFilter(popKey))
  {
    DeleteCxObj(cxPopFilter);
    cxPopFilter = newFilter;
    
    AttachCxObj(cxPopFilter, cxPopSender);
    AttachCxObj(cxPopFilter, cxPopDrain);
    AttachCxObj(cxBroker, cxPopFilter);    
  }
  else DisplayBeep(NULL);
}

void ToggleLED(void)
{
  static BOOL Bright = TRUE;
  static struct CIA *cia = (struct CIA *) CIAA;
  
  Bright = !Bright;
  
  if (Bright) cia->ciapra |= CIAF_LED; else cia->ciapra &= ~(CIAF_LED);
}


void ProcessMsg(struct MsgPort * broker_mp)
{
  struct Message * msg;
  CxMsg * cmsg;
  ULONG sigrcvd;
  BOOL keepGoing = TRUE;

  if (BConnectOnStartup) Connect(); else Accept();
       
  while(keepGoing)
  {
    sigrcvd = Wait((BWindowOpen ? (1L<<ARemoteWnd->UserPort->mp_SigBit) : 0L) | SIGBREAKF_CTRL_C | SIGBREAKF_CTRL_D | SIGBREAKF_CTRL_E | SIGBREAKF_CTRL_F | (1L<<broker_mp->mp_SigBit) | (1L<<event_mp->mp_SigBit) | (session ? (1L<<session->qMsgPort->mp_SigBit) : 0L));
    
    if ((BWindowOpen)&&(sigrcvd & (1L<<ARemoteWnd->UserPort->mp_SigBit))) 
    {
      winRequests = 0L;
      
      HandleARemoteIDCMP();
      
      /* Check to see if HandleARemoteIDCMP() wanted the window open or closed */
      if (winRequests & FLAG_UPDATE_STATE)  UpdateStateFromGUI();
      if (winRequests & FLAG_CHANGE_ACCEPT) DoChangeAccept();
      if (winRequests & FLAG_ABOUT)         ShowAboutBox();
      if (winRequests & FLAG_CLOSE_WINDOW)  DoCloseWindow();
      if (winRequests & FLAG_DISCONNECT)    Accept();
      if (winRequests & FLAG_CONNECT)       Connect();
      if (winRequests & FLAG_DISABLE)       DisableCommodity();
      if (winRequests & FLAG_ENABLE)        EnableCommodity();
      if (winRequests & FLAG_CHANGE_TOGGLE) ChangeToggle();
      if (winRequests & FLAG_CHANGE_POPUP)  ChangePopup();
      if (winRequests & FLAG_QUIT)          keepGoing = FALSE;
    }
    
    while(cmsg = (CxMsg *)GetMsg(broker_mp))
    {
      ULONG msgid, msgtype;
      
      msgid = CxMsgID(cmsg);
      msgtype = CxMsgType(cmsg);
      
      ReplyMsg((struct Message *)cmsg);

      switch(msgtype)
      {
        case CXM_IEVENT:
          switch(msgid)
          {
            case EVENT_TOGGLEKEY:
              if (BXmitting) StopTransmitting(); else StartTransmitting();
            break;
            
            case EVENT_SHOWWINDOW:
              DoOpenWindow();
              break;
              
            default:
              printf("Unknown event %i?\n",msgid);
              break;
          }
          break;
          
        case CXM_COMMAND:
          switch(msgid)
          {
            case CXCMD_APPEAR:    DoOpenWindow();     break;
            case CXCMD_DISAPPEAR: DoCloseWindow();    break;
            case CXCMD_DISABLE:   DisableCommodity(); break;
            case CXCMD_ENABLE:    EnableCommodity();  break;
            case CXCMD_KILL:      keepGoing = FALSE;  break;
            case CXCMD_UNIQUE:    DoOpenWindow();     break;
          }
          break;
      }
    }

    /* handle incoming InputEvent messages from our custom CxObject */
    {
      BOOL BSent = FALSE;

      while(msg = (struct Message *)GetMsg(event_mp))
      {
        if ((session)&&(BConnectOkay))
        {
          struct InputEvent * ie = (struct InputEvent *) (((UBYTE *)msg)+sizeof(struct Message));
      
          /* Send a copy of the Input Event to our buddy */
          if (QStreamOp(session, "e", ie, sizeof(struct InputEvent))) BSent = TRUE;
        }
        FreeMem(msg, msg->mn_Length);
      }
      if (BSent) 
      { 
        QGo(session, 0L);
        if (BXmitCue) ToggleLED();
      }
    }

    if (session)
    {
      struct QMessage * qmsg;
      
      while(qmsg = (struct QMessage *)GetMsg(session->qMsgPort))
      { 
        if (qmsg->qm_Status == QERROR_NO_ERROR)
        {
          if (qmsg->qm_ID == 0) 
          {
            char temp[300];
            char * t = qmsg->qm_Path, * u;

                 if (t == NULL) t = "(unknown)";
            else if ((*t)&&(u = strchr(t+1, '/'))) t = u+1;
            
            strncpy(temp, "Connected: [", sizeof(temp));
            strncat(temp, t, sizeof(temp));
            strncat(temp, "]", sizeof(temp));
            StatMsg(temp);

            BConnectOkay = TRUE;
            BGUINeedsUpdate = TRUE;
          }
          else
          {
            if (qmsg->qm_DataLen == sizeof(struct InputEvent))
            { 
              struct InputEvent * ie = (struct InputEvent *)qmsg->qm_Data;
              if ((BEnabled)&&(BXmitting == FALSE)) AddIEvents(ie);  /* Don't generate events from incoming data if we are xmitting events... we don't want a feedback loop! */
            }
            else printf("Error, bad data size (%i != %i)\n",qmsg->qm_DataLen, sizeof(struct InputEvent));
          }
        }
        else
        {
          FreeQMessage(session, qmsg);  /* Gotta free this NOW!  Before the session is gone */
          Accept();  /* Will disconnect and put up a passive session again... */
          break;  /* Can't evaluate while() condition again!  session is dangling! */
        }
        FreeQMessage(session, qmsg);
      }
    }
    
    if (sigrcvd & SIGBREAKF_CTRL_C) {printf("CTRL-C detected.\n"); keepGoing = FALSE;}
    if (sigrcvd & SIGBREAKF_CTRL_D) DisableCommodity();
    if (sigrcvd & SIGBREAKF_CTRL_E) EnableCommodity();
    if (sigrcvd & SIGBREAKF_CTRL_F) DoOpenWindow();

    if (BGUINeedsUpdate) UpdateGUIFromState(); 
  }
  
  if (session) 
  {
    QFreeSession(session);
    session = NULL;
  }
  
}

__geta4 void CxFunction(register CxMsg * cxm, CxObj * co)
{
  struct InputEvent * ie;
  
  ie = (struct InputEvent *)CxMsgData(cxm);

  if ((ie->ie_Class == IECLASS_RAWKEY)     ||
      (ie->ie_Class == IECLASS_RAWMOUSE)   ||
      (ie->ie_Class == IECLASS_POINTERPOS)) 
  {
     /* Send a message containing a copy of this InputEvent to our program for transmission */
     int bufLen = sizeof(struct Message)+sizeof(struct InputEvent);
     struct Message * msg = (struct Message *)AllocMem(bufLen, MEMF_PUBLIC | MEMF_CLEAR);
       
     if (msg)
     {
       msg->mn_Node.ln_Type = NT_MESSAGE;
       msg->mn_Length       = bufLen;
       msg->mn_ReplyPort    = NULL;   /* No reply needed; we'll just free it */
       memcpy(((UBYTE *)msg)+sizeof(struct Message), ie, sizeof(struct InputEvent));
       PutMsg(event_mp, msg);
     }
  }
}