/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* |_o_o|\\ Copyright (c) 1990 The Software Distillery.  All Rights Reserved.*/
/* |. o.| || This program may not be distributed without the permission of   */
/* | .  | || the authors.                                                    */
/* | o  | ||                                                                 */
/* |  . |// Written by Doug Walker                                           */
/* ======          BBS:(919)-471-6436      VOICE:(919)-467-4764              */ 
/*                 BIX: djwalker           USENET: ...!mcnc!rti!sas!walker   */
/*                 405 B3 Gooseneck Dr, Cary, NC 27513, USA                  */
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*
 * Based on POPCLI III, written by John Toebes
 * Uses Matt Dillon's DNET
 */

#include "netkeys.h"
#include <intuition/intuitionbase.h>
#include <proto/intuition.h>

#ifdef DEBUG
#undef DEBUG
#endif

#define DEBUG 0

#if DEBUG
#define BUG(x) if(_Backstdout) MSG(_Backstdout, x);
#else
#define BUG(x) ;
#endif

/* Declarations for CBACK */
extern BPTR _Backstdout;         /* standard output when run in background */
long _BackGroundIO = 1;          /* Flag to tell it we want to do I/O      */
long _stack = 4000;              /* Amount of stack space our task needs   */
char *_procname = "NetKeys II";  /* The name of the task to create         */
long _priority = 20;             /* The priority to run us at              */

int InitDevice(GLOBAL_DATA *, int);
int TermDevice(GLOBAL_DATA *);

struct InputEvent * __regargs myhandler(struct InputEvent *ev, GLOBAL_DATA *gptr)
{
   struct InputEvent *ep, *laste;
   struct NETKEYMSG *nkm;
   int msgtype;

   if(gptr->state < EM_DOIT) return(ev);  /* In the process of quitting */

   /* run down the list of events to see if they pressed the magic button */
   for (ep = ev, laste = NULL; ep != NULL; ep = ep->ie_NextEvent)
   {
      msgtype = NKM_NONE;
      if(gptr->state == EM_RELURK)
      {
         gptr->state = EM_LURK;
         msgtype = NKM_ACK;
      }
      else if(ep->ie_Class == IECLASS_RAWKEY || ep->ie_Class == IECLASS_RAWMOUSE)
      {
         if(
             (
               ep->ie_Class == IECLASS_RAWKEY                  &&
               ep->ie_Code  == gptr->dokey                     &&
               (ep->ie_Qualifier & IEQUALIFIER_LCOMMAND)
             ) 
             ||
             (
               gptr->state == EM_LURK                                         &&
               ep->ie_Class == IECLASS_RAWMOUSE                               &&
               (ep->ie_Qualifier & IEQUALIFIER_RELATIVEMOUSE)                 &&
               (ep->ie_Code == IECODE_NOBUTTON)                               &&
               ((gptr->ibase->MouseX == 0 && ep->ie_X < 0 && 
                    (gptr->exit & IECODE_NKLEFT)) ||
                (gptr->ibase->MouseX == gptr->swidth && ep->ie_X > 0 &&
                    (gptr->exit & IECODE_NKRIGHT))
               )
             )
           )
         {
            /* Remove the event from the chain */
            if (laste == NULL)
               ev = ep->ie_NextEvent;
            else
               laste->ie_NextEvent = ep->ie_NextEvent;

            /* Telling us to toggle... */
            if(gptr->state == EM_DOIT)
            {
               gptr->state = EM_LURK;
               msgtype = NKM_WEBEDONE;
            }
            else
            {
               gptr->state = EM_DOIT;
               msgtype = NKM_HEREWEGO;
            }
         }
         else if(gptr->state != EM_LURK)
         {
            /* Remove the event from the chain */
            if (laste == NULL)
               ev = ep->ie_NextEvent;
            else
               laste->ie_NextEvent = ep->ie_NextEvent;

            msgtype = NKM_IEVENT;
         }
      }
      else
         laste = ep;

      if(msgtype != NKM_NONE)
      {
         if(!(nkm=(struct NETKEYMSG *)GetMsg(gptr->rtnp)))
         {
            if(gptr->nmsgs <= MAXMSGS)
               nkm=AllocMem(NKMSZ, MEMF_CLEAR|MEMF_PUBLIC);
         }
         if(nkm)
         {
            nkm->msgtype = msgtype;
            if(msgtype == NKM_IEVENT || msgtype == NKM_HEREWEGO) nkm->ie = *ep;
            PutMsg((struct MsgPort *)gptr->nkmp, (struct Message *)nkm);
         }
      }
   }

   /* pass on the pointer to the event */
   return(ev);
}

#if DEBUG > 1
BPTR _Backstdout = NULL;
char *cmd;
#define DCLMAIN int main(int argc, char *argv[])
#else
#define DCLMAIN int _main(char *cmd)
#endif

DCLMAIN
{
   struct MsgPort  *inputDevPort = NULL;
   struct MsgPort  *oldport;
   struct IOStdReq *inputRequestBlock = NULL;
   struct Interrupt handlerStuff;
   GLOBAL_DATA global;
   struct NETKEYMSG *msg, *oldmsg, *tmpmsg;
   int quit = 0;
   UWORD smooth = 2;
   struct Screen wbdat;
   struct IntuitionBase *IntuitionBase;
   struct GfxBase *GfxBase = NULL;
   UWORD *NoSprData = NULL;
   ULONG IBaseLock;

#if DEBUG > 1
   /* Not really linked with cback */
   cmd = argv[1];
   _Backstdout = Output();
#endif

   MSG(_Backstdout, BANNER);

   memset(&global, 0, sizeof(global));
   global.buddy = FindTask(0L);
   global.dokey  = DEFDOKEY;
   global.state = EM_LURK;

   if(!(global.rtnp = CreatePort(0,0)))
   {
      MSG(_Backstdout, "Can't CreatePort\n");
      goto abort;
   }

   oldport = FindPort(NKMPNAME);

   if(cmd)
   {
      long tmpl = DEFDOKEY;
      while(*cmd)
      {
         while(*cmd != ' ' && *cmd != '\0') cmd++;
         while(*cmd == ' ') cmd++;

         switch(cmd[0])
         {
            case '\0': 
            case '\n':
               break;

            case '0':
               if(cmd[1] == 'x')
               {
                  stch_l(cmd+2, &tmpl);
                  global.dokey = tmpl;
               }
               break;

            case 'F': case 'f':
               smooth = 0;
               break;

            case 'L': case 'l':
               global.exit = IECODE_NKLEFT;
               break;

            case 'Q': case 'q':
               quit = 1;
               break;

            case 'R': case 'r':
               global.exit = IECODE_NKRIGHT;
               break;

            case 'S': case 's':
               smooth = 1;
               break;

            case 'W': case 'w':
               global.exit = IECODE_NKLEFT|IECODE_NKRIGHT;
               break;

            default:
               MSG(_Backstdout, 
                "USAGE: NetKeys [0xkey] [F|S] [L|R|W] [Q]\n");
               MSG(_Backstdout, "   [0xkey]  - RAW key code for hotkey\n");
               MSG(_Backstdout, "   [F|S]    - FAST or SMOOTH mode\n");
               MSG(_Backstdout, 
                  "   [L|R|W]  - Mouse cursor exits LEFT, RIGHT or WRAPs\n");
               MSG(_Backstdout, "   [Quit]   - Terminate NetKeys\n");
               goto abort;
         }
      }
   }

   if(!(oldmsg = AllocMem(NKMSZ, MEMF_CLEAR|MEMF_PUBLIC)))
   {
      MSG(_Backstdout, "No memory\n");
      goto abort;
   }
   if(quit)
   {
      if(!oldport)
      {
         MSG(_Backstdout, "Netkeys not installed\n");
         goto abort;
      }
      else
      {
         oldmsg->msgtype = NKM_QUIT;
         oldmsg->m.mn_Node.ln_Type = NT_MESSAGE;
         oldmsg->m.mn_ReplyPort = global.rtnp;

         PutMsg(oldport, (struct Message *)oldmsg);
         while(!GetMsg(global.rtnp))
            WaitPort(global.rtnp);
         GetMsg(global.rtnp);

         MSG(_Backstdout, "NetKeys removed\n");
         goto abort;      
      }
   }

   if(oldport)
   {
      /* Modify the parms for the existing guy */
      oldmsg->msgtype = NKM_CONTROL;
      oldmsg->m.mn_Node.ln_Type = NT_MESSAGE;
      oldmsg->m.mn_ReplyPort = global.rtnp;
      oldmsg->ie.ie_Class = global.dokey;
      oldmsg->ie.ie_SubClass = global.exit;
      oldmsg->ie.ie_Code = smooth;
      PutMsg(oldport, (struct Message *)oldmsg);

      while(!GetMsg(global.rtnp))
         WaitPort(global.rtnp);
      GetMsg(global.rtnp);

      MSG(_Backstdout, "Parameters modified on existing NetKeys command\n");
      goto abort;
   }
   FreeMem(oldmsg, NKMSZ);
   oldmsg = NULL;

   MSG(_Backstdout, INSTR);

   if(global.exit == 0) global.exit = IECODE_NKLEFT|IECODE_NKRIGHT;

   if(smooth == 2) smooth = 0;

   if (
       ((global.nkmp = CreatePort(NKMPNAME, 0)) == NULL) ||
       ((inputDevPort = CreatePort(0,0)) == NULL)        ||
        InitDevice(&global, NKM_HANDLER)                 ||
        ((inputRequestBlock = 
               CreateIOReq(inputDevPort, sizeof(struct IOStdReq))) == NULL) ||

       !(global.ibase = (struct IntuitionBase *)
                       OpenLibrary("intuition.library", 0))                 ||

       !(GfxBase = (struct GfxBase *)OpenLibrary("graphics.library", 0))    ||

       !(NoSprData = AllocMem(12, MEMF_PUBLIC|MEMF_CHIP|MEMF_CLEAR))        ||

      OpenDevice("input.device",0,(struct IORequest *)inputRequestBlock,0))
      {
         MSG(_Backstdout, "Can't initialize - check DNET connection\n");
         goto abort;
      }

   MSG(_Backstdout, "NetKeys installed\n");

#if DEBUG < 1
   if(_Backstdout) Close(_Backstdout);
   _Backstdout = 0;
#endif

   IntuitionBase = global.ibase;
   NoSprData[0] = 0xFE00;
   NoSprData[1] = 0xFF00;

   /* If we have an appropriate version of intuition, ask for info */
   /* about the workbench screen                                   */
   if(IntuitionBase->LibNode.lib_Version < 33
      || !(GetScreenData((char *)&wbdat, sizeof(struct Screen), 
                              WBENCHSCREEN, NULL)))
   {
      /* Either we're running on an old version of the system, or the */
      /* GetScreenData call failed for some reason.  In either case,  */
      /* simply assume a 640x200 screen and be done with it.          */
      global.swidth  = 639;
   }
   else
   {
      /* Use the wb width, and STDSCREENHEIGHT. */
      global.swidth  = wbdat.Width-1;
   }

   handlerStuff.is_Data = (APTR)&global;
   handlerStuff.is_Code = (VOID (*)()) myhandler;
   handlerStuff.is_Node.ln_Pri = 52;

   inputRequestBlock->io_Command = IND_ADDHANDLER;
   inputRequestBlock->io_Data    = (APTR)&handlerStuff;

   DoIO((struct IORequest *)inputRequestBlock);

   while(global.state >= EM_DOIT && 
        !(SIGBREAKF_CTRL_C & Wait((1<<global.nkmp->mp_SigBit) | SIGBREAKF_CTRL_C)))
   {
      while(msg=(struct NETKEYMSG *)GetMsg(global.nkmp))
      {
         if(msg->msgtype == NKM_QUIT)          /* Msg from another invocation */
         {
            ReplyMsg((struct Message *)msg);
            goto abort;
         }
         else if(msg->msgtype == NKM_CONTROL)  /* Msg from another invocation */
         {
            global.dokey = msg->ie.ie_Class;
            if(msg->ie.ie_SubClass)
               global.exit = msg->ie.ie_SubClass;
            if(msg->ie.ie_Code != 2)
               smooth = msg->ie.ie_Code;
            ReplyMsg((struct Message *)msg);
            continue;
         }
         else if(msg->msgtype == NKM_HEREWEGO) /* From handler - start sending */
         {
            if(msg->ie.ie_Class == IECLASS_RAWMOUSE)
               msg->ie.ie_Code = global.exit;
            else
               msg->ie.ie_Code = global.exit|IECODE_NKHOTKEY;

            msg->ie.ie_Class = IECLASS_NKCONTROL;

            msg->ie.ie_Y = IntuitionBase->MouseY;
            msg->ie.ie_X = IntuitionBase->MouseX;

            mshow(GfxBase, NoSprData, 0);  /* Blank mouse */
         }
         else if(msg->msgtype == NKM_WEBEDONE) /* From handler - quit sending*/
         {
            mshow(GfxBase, NoSprData, 1);  /* Unblank mouse */
            msg->ie.ie_Class = IECLASS_NKCONTROL;
            msg->ie.ie_Code  = IECODE_NKHOTQUIT|global.exit;
            msg->ie.ie_Y = IntuitionBase->MouseY;
            msg->ie.ie_X = IntuitionBase->MouseX;
         }

         tmpmsg = NULL;
         if( !smooth &&
             msg->ie.ie_Class == IECLASS_RAWMOUSE &&
             msg->ie.ie_Code == IECODE_NOBUTTON)
         {
            while((tmpmsg = (struct NETKEYMSG *)GetMsg(global.nkmp)) &&
                   tmpmsg->msgtype == NKM_IEVENT &&
                   tmpmsg->ie.ie_Class == IECLASS_RAWMOUSE &&
                   tmpmsg->ie.ie_Code == IECODE_NOBUTTON &&
                   (tmpmsg->ie.ie_Qualifier & IEQUALIFIER_RELATIVEMOUSE))
            {
               tmpmsg->ie.ie_X += msg->ie.ie_X;
               tmpmsg->ie.ie_Y += msg->ie.ie_Y;
               PutMsg(global.rtnp, (struct Message *)msg);
               msg = tmpmsg;
               tmpmsg = NULL;
            }
         }
putit:
         if(DWrite(global.chan, (char *)&msg->ie, IESZ) != IESZ)
            break;

         PutMsg(global.rtnp, (struct Message *)msg);

         if(DNRead(global.chan, (char *)&msg->ie, IESZ) == IESZ)
         {
            while(DNRead(global.chan, (char *)&msg->ie, IESZ) == IESZ);

            IBaseLock = LockIBase(0);
            IntuitionBase->MouseY = msg->ie.ie_Y;
            IntuitionBase->MouseX = (msg->ie.ie_X == 0 ? global.swidth-1 : 1);
            UnlockIBase(IBaseLock);

            /* Set global state to RELURK and wait for handler to acknowledge */
            global.state = EM_RELURK;
            do
            {
               if(!(msg=(struct NETKEYMSG *)GetMsg(global.nkmp))) 
                  WaitPort(global.nkmp);
            }
            while(!msg || msg->msgtype != NKM_ACK);
            mshow(GfxBase, NoSprData, 1);
         }
         else if(tmpmsg)
         {
            msg = tmpmsg;
            tmpmsg = NULL;
            goto putit;
         }
      }
   }

abort:
   if(global.ibase) CloseLibrary((struct Library *)global.ibase);

   if(GfxBase)
   {
      if(NoSprData)
      {
         mshow(GfxBase, NoSprData, 1);
         FreeMem(NoSprData, 12);
      }
      CloseLibrary((struct Library *)GfxBase);
   }

   TermDevice(&global);

   if (inputRequestBlock != NULL)
   {
      if (inputRequestBlock->io_Device != NULL)
      {
         inputRequestBlock->io_Command = IND_REMHANDLER;
         inputRequestBlock->io_Data = (APTR)&handlerStuff;
         DoIO((struct IORequest *)inputRequestBlock);

         CloseDevice((struct IORequest *)inputRequestBlock);
      }
      DeleteIOReq(inputRequestBlock);
   }

   if (inputDevPort != NULL)  DeletePort(inputDevPort);

   if(global.nkmp)
   {
      while(msg=(struct NETKEYMSG *)GetMsg(global.nkmp)) FreeMem(msg, NKMSZ);
      DeletePort(global.nkmp);
   }

   if(global.rtnp)
   {
      while(msg=(struct NETKEYMSG *)GetMsg(global.rtnp)) FreeMem(msg, NKMSZ);
      DeletePort(global.rtnp);
   }

   if(_Backstdout) Close(_Backstdout);

   return(0);
}

int InitDevice(GLOBAL_DATA *global, int which)
{

   if(global->active) return(1);
   global->active = 1;

   if(!(global->chan=(struct DChannel *)
      DOpen(NULL, (uword)(PORT_NETKEYS), 127, 127)))
         return(1);

   return(0);
}

int TermDevice(GLOBAL_DATA *global)
{
   if(!global->active) return(1);

   global->active = 0;

   if(global->chan) DClose(global->chan);

   global->chan = NULL;

   return(0);
}

void MemCleanup(void);
void MemCleanup(){}
