/*
 *  CALC      Provides a calculator that opens on the active screen when
 *            you press a specific key sequence.  Otherwise, the program
 *            waits quitely in the background.
 *
 *              Copyright 1989 by Davide P. Cervone.
 *  You may use this code, provided this copywrite notice is kept intact.
 */

#define INTUITION_PREFERENCES_H         /* don't need 'em */
#include <intuition/intuitionbase.h>
#include <exec/interrupts.h>
#include <devices/input.h>
#include <libraries/dos.h>
#include <libraries/dosextens.h>
#include <proto/exec.h>

#include "cHandler.h"
#include "cSetup.h"

extern struct Process *FindTask();


#define GETSTARTUP(p)       ((struct StartupMessage *)GetMsg(p))

#define ONE     1L
#define SIGBREAKF_ANY\
   (SIGBREAKF_CTRL_C| SIGBREAKF_CTRL_D| SIGBREAKF_CTRL_E| SIGBREAKF_CTRL_F)


extern void myHandlerStub();
#ifndef SWINDOWS
extern void aCloseScreen();
extern void cCloseScreen();
long OldCloseScreen;
#endif


LONG CalcSMask;                 /* Signal for calc to open on active screen */
static LONG CalcSSignal;        /* Signal number for CalcSMask */

#ifndef SWINDOWS
LONG CalcCMask;                 /* Signal for screen close */
static LONG CalcCSignal;        /* Signal number for CalcCMask */
#endif


struct Task *CalcTask;          /* Pointer to Calc task */
static struct Task *ParentTask; /* Pointer to parent task */


extern UWORD KeyCode;           /* KeyCode to activate Calc */
extern UWORD Qualifiers;        /*   and required qualifiers */


static struct Interrupt Calc_Interrupt = /* the Interrupt needed to add a */
{                                         /*  handler to the input chain */
   {NULL, NULL, 0, 51, NULL},             /*  ln_Pri = 51 (before Intuition) */
   NULL,
   &myHandlerStub                         /* the handler to add */
};

static struct HandlerData cHandlerData =
{
   {                                            /* the MsgPort is pre-setup */
      {NULL,NULL, NT_MSGPORT, 0, PORTNAME},     /*  to include the name and */
      PA_IGNORE, 0, NULL,                       /*  type so that it can just */
      {NULL,NULL,NULL, 0,0}                     /*  be added to the port list */
   },                                           /*  so it can be found later */
   MAJVERS,MINVERS, MINLOADVER,         /* the handler versio numbers */
   NULL,                                /* the handler code segment */
   &IntuitionBase,                      /* pointer to IntuitionBase and */
   &GfxBase,                            /*   GfxBase (set up by loader) */
   
   &Calc_Interrupt,                     /* the interrupt used by Input.Device */
   &ParentTask,                         /* so we know who to signal later */

   &KeyCode,                            /* the KeyCode to acticate calc */
   &Qualifiers,                         /*   and required qualifiers */

#ifndef SWINDOWS   
   &aCloseScreen,                       /* ASM CloseScreen stub */
   &OldCloseScreen,                     /* result of SetFunction */
#else
   NULL,NULL
#endif
};


/*
 *  cExit()
 *
 *  End CalcTask from running.  Forbid() so that we are not unloaded until
 *  after we're done exiting.  Signal the parent that we are done running,
 *  and exit.
 */

void cExit()
{
   Forbid();
   if (ParentTask) Signal(ParentTask,ENDSIGNAL);
   Exit(0);
}


/*
 *  GetSignal()
 *
 *  Allocate a signal (error if none available) and set the mask to
 *  the proper value.
 */

#define DEFAULTSIGNAL       20

void GetSignal(theSignal,theMask)
LONG *theSignal, *theMask;
{
   LONG signal;

   if ((signal = AllocSignal(-ONE)) == -ONE) signal = DEFAULTSIGNAL;
   *theSignal = signal;
   *theMask = (ONE << signal);
}


/*
 *  WaitForStartup()
 *
 *  Find our task pointer and wait for a startup message passed buy the loader.
 *  (This structure is non-standard and is defined in cSetup.h).  Check that
 *  we have not been signaled to end (in case the user executed the handler
 *  as a command, for example).  Otherwise, once we have received the
 *  startup message, get the parent task pointer from the message, and
 *  check the loader version number.  If OK, then set the HandlerData so
 *  the loader can do its thing.  Finally, reply to the message to inform 
 *  the loader that everything is ready for it.
 */

static void WaitForStartup()
{
   struct Process *cTask = FindTask(NULL);
   struct MsgPort *cPort = &cTask->pr_MsgPort;
   struct StartupMessage *sMessage = NULL;
   ULONG Mask = (ONE << cPort->mp_SigBit);
   ULONG signals;
   
   CalcTask = (struct Task *)cTask;
   while (sMessage == NULL)
   {
      signals = Wait(Mask|ENDSIGNAL);
      if (signals & ENDSIGNAL) cExit();
      sMessage = GETSTARTUP(cPort);
   }
   ParentTask = sMessage->sm_ParentTask;
   if (sMessage->sm_LoadVers < MINLOADVER)
      sMessage->sm_HandlerData = NULL;
     else
      sMessage->sm_HandlerData = &cHandlerData;
   ReplyMsg(sMessage);
}


/*
 *  InitCalc()
 *
 *  Clear any pending signals, and wait for the startup message from the
 *  loader.  Then wait for the loader to signal us that it is finished
 *  setting things up for us.  If it signaled an END, then exit, otherwise
 *  allocate the signals that we will need
 */

void InitCalc()
{
   ULONG signals;

   SetSignal(0L,SIGBREAKF_ANY);
   WaitForStartup();
   signals = Wait(STARTSIGNAL | ENDSIGNAL);
   if (signals & ENDSIGNAL) cExit();
   GetSignal(&CalcSSignal,&CalcSMask);
#ifndef SWINDOWS
   GetSignal(&CalcCSignal,&CalcCMask);
#endif
}


/*
 *  ConfirmExit()
 *
 *  Called when the loader tries to unload the handler.  First we signal
 *  the loader that everything is ready for it to try to deinstall us.  
 *  Then we wait for it to signal that everything has be removed properly.
 *  If it signals us with ENDSIGNAL we can safely end (it has been able to 
 *  remove everything).  Otherwise, we must continue to run.
 */
 
int ConfirmExit()
{
   ULONG signals;
   int NotDone = TRUE;

   if (ParentTask)
   {
      Signal(ParentTask,ENDSIGNAL);
      signals = Wait(ENDSIGNAL | STARTSIGNAL);
      if (signals & ENDSIGNAL) NotDone = FALSE;
   }
   return(NotDone);
}
