/*
 *  SWINDOWS    A program to allow you to open windows on any screen by
 *              supplying the screen name in thw window title
 *
 *              Copyright 1989 by Davide P. Cervone.
 *  You may use this code, provided this copywrite notice is kept intact.
 */

#include "swMain.h"
#include "sWindows.h"

struct IntuitionBase *IntuitionBase = NULL;
extern struct SysBase *SysBase;

static char *program   = PROGRAM;
static char *copyright = COPYRIGHT;

static char *handler   = HANDLERCODE;       /* the name of the handler file */
#define HANDLER          &(handler[2])      /* handler without the L: */

static struct swHandlerInfo *swHandlerData; /* data shared with the handler */
static long Segment;                        /* the loaded handler segment */

static SLISTITEM *ScreenList;               /* screen item linked list */
static WLISTITEM *WindowList;               /* window item linked list */


/*
 *  FreeAllMemory()
 *
 *  After the function vectors have been restored, the linked lists are
 *  freed.  If any tasks are waiting for the close signal before
 *  closing their screens, then these tasks are signalled immediately.
 *  Allowing the screen to close before the windows are all closed, however,
 *  is not a good idea.
 */

void FreeAllMemory()
{
   SLISTITEM theScreen;
   WLISTITEM theWindow;

   Forbid();
   while (*WindowList)
   {
      theWindow = *WindowList;
      *WindowList = theWindow->Next;
      FREE(WindowListItem,theWindow);
   }
   while (*ScreenList)
   {
      theScreen = *ScreenList;
      *ScreenList = theScreen->Next;
      if (theScreen->CloseTask)
         Signal(theScreen->CloseTask,theScreen->CloseSignal);
      FREE(ScreenListItem,theScreen);
   }
   Permit();
}


/*
 *  DoExit()
 *
 *  General purpose error-exit routine.  Print an error message if one was
 *  supplied (it can have up to three parameters), and then clean up any
 *  memory, libraries, etc. that need to be handled before exiting.
 */

static void DoExit(s,x1,x2,x3)
char *s, *x1, *x2, *x3;
{
   long status = EXIT_OK;
   
   if (s != NULL)
   {
      printf(s,x1,x2,x3);
      printf("\n");
      status = EXIT_ERROR;
   }
   FreeAllMemory();
   if (Segment)       UnLoadSeg(Segment);
   if (IntuitionBase) CloseLibrary(IntuitionBase);
   exit(status);
}


/*
 *  CheckLibOpen()
 *
 *  Call OpenLibrary() for the specified library, and check that the 
 *  open succeeded.
 */

static void CheckLibOpen(lib,name,rev)
APTR *lib;
char *name;
int rev;
{
   extern APTR OpenLibrary();

   if ((*lib = OpenLibrary(name,(LONG)rev)) == NULL)
      DoExit("Can't open %s",name);
}


/*
 *  LoadHandler()
 *
 *  Try to LoadSeg the handler from the current directory, and if it is not
 *  found, try the L: directory.  If neither can be loaded, exit with an
 *  error message.  Once the handler is loaded, call the Setup routine
 *  in the handler code and pass the loader version number.  The handler will
 *  check the version for compatibility and returns NULL if there is a
 *  mismatch, or a pointer to the shared data if everything is OK.
 *  Check the handler version number, and then store the loader version
 *  and the segment list pointer for use in unloading the handler later.
 *  The MsgPort is the first item in the swHandler structure.  It is used
 *  to link the information into the system port list, where we can find it
 *  later.
 */

void LoadHandler(thePort)
struct MsgPort **thePort;
{
   struct swHandlerInfo *(*Setup)();
   
   if ((Segment = LoadSeg(HANDLER)) == NULL)
      if ((Segment = LoadSeg(handler)) == NULL)
        DoExit("Can't load %s",handler);
   Setup = (struct swHandlerInfo *(*)()) ((Segment << 2) + 4);
   
   swHandlerData = (*Setup)(LOADVERS);
   if (swHandlerData)
   {
      if (var(MajVers) > 1) DoExit("Version mismatch with %s",HANDLER);
      *thePort = &(swHandlerData->swPort);
   } else {
      DoExit("%s reports a version mismatch",HANDLER);
   }
   
   var(Segment)  = Segment;
   var(LoadVers) = LOADVERS;
}


/*
 *  SetVerctors()
 *
 *  Set the Intuition library vectors for the OpenWindow, CloseWindow
 *  and CloseScreen to the routines specified by the handler.  Save the
 *  old routine pointers for later replacement.
 */

void SetVectors()
{
   VAR(OldOpenWindow)  =
      SetFunction(IntuitionBase,&LVOOpenWindow,var(aOpenWindow));
   VAR(OldCloseWindow) =
      SetFunction(IntuitionBase,&LVOCloseWindow,var(aCloseWindow));
   VAR(OldCloseScreen) =
      SetFunction(IntuitionBase,&LVOCloseScreen,var(aCloseScreen));
}


/*
 *  UnSetVectors()
 *
 *  Replace the old Intuition library vectors, but make sure that no one
 *  else has changed them behind our back.  If they are not the same as
 *  what we set them to originally, then put back the ones that we found,
 *  and return an error status.
 */

int UnSetVectors()
{
   long NewOpenWindow,NewCloseWindow,NewCloseScreen;
   int status = TRUE;

   NewOpenWindow =
      SetFunction(IntuitionBase,&LVOOpenWindow,VAR(OldOpenWindow));
   NewCloseWindow =
      SetFunction(IntuitionBase,&LVOCloseWindow,VAR(OldCloseWindow));
   NewCloseScreen =
      SetFunction(IntuitionBase,&LVOCloseScreen,VAR(OldCloseScreen));

   if (NewOpenWindow  != (long) var(aOpenWindow)  ||
       NewCloseWindow != (long) var(aCloseWindow) ||
       NewCloseScreen != (long) var(aCloseScreen))
   {
      SetFunction(IntuitionBase,&LVOOpenWindow,NewOpenWindow);
      SetFunction(IntuitionBase,&LVOCloseWindow,NewCloseWindow);
      SetFunction(IntuitionBase,&LVOCloseScreen,NewCloseScreen);
      status = FALSE;
   }
   return(status);
}


/*
 *  SetVariables()
 *
 *  The swHandlerData structure is used to allow the loading program to
 *  set up variables needed by the handler (like Intuitionbase, etc.).  This
 *  keeps the handler code to a minimum.  The loader retains pointers to the
 *  linked lists, in case it needs to free memory on behalf of the handler.
 */

void SetVariables(thePort)
struct MsgPort *thePort;
{
   VAR(IntuitionBase) = IntuitionBase;
   VAR(SysBase) = SysBase;
   ScreenList = var(ScreenList);
   WindowList = var(WindowList);
}


/*
 *  GetVariables()
 *
 *  Look up the values stored in the swHandlerData structure.  The 
 *  Intuition library already was opened, and we will need to close it.
 *  The data in the linked lists may need to be freed.
 */

void GetVariables(thePort)
struct MsgPort *thePort;
{
   swHandlerData = (struct swHandlerInfo *) thePort;
   IntuitionBase = VAR(IntuitionBase);
   ScreenList    = var(ScreenList);
   WindowList    = var(WindowList);
}


/*
 *  Main()
 *
 *  Look for the sWindows port, which indicates that sWindows already exists.
 *  If the port does not exist, then sWindows is not active, so
 *    Open Intuition.
 *    Load the handler code and check its version.
 *    Add the port (supplied by the handler) into the system list so we
 *      can find it later.
 *    Set the variables needed by the handler.
 *    Set the Intuition Library vectors the the handler routines
 *    Notify the user that all is ready.
 *  else (the port already exists, so sWindows alreay is active)
 *    Get the pointer to the swHandlerData structure from the port,
 *      and get any variables we need from the structure.
 *    Check that the loader versions are compatible.
 *    Try to remove the SetFunction calls.
 *    If successfull, then
 *      Remove the port from the system list.
 *      Free any window and screen item memory from the linked lists.
 *      Unload the handler segment list.
 *      Notify the user that sWindows is deactivated.
 *      Close Intuition.
 *    else (we could not replace the functions)
 *      Inform the user that sWindows can not be removed
 */
 
void main()
{
   struct MsgPort *NamedPort;

   NamedPort = FindPort(PORTNAME);
   if (NamedPort == NULL)
   {
      CheckLibOpen(&IntuitionBase,"intuition.library",INTUITION_REV);
      LoadHandler(&NamedPort);
      AddPort(NamedPort);
      SetVariables(NamedPort);
      SetVectors(NamedPort);
      printf("%s v%d.%d.%d Installed\n",program,
         var(MajVers),var(MinVers),var(LoadVers));
   } else {
      GetVariables(NamedPort);
      if (var(LoadVers) > LOADVERS)
      {
         printf("Loader version mismatch\n");
         printf("Current handler was started by a higher-version loader\n");
         printf("%s not removed\n",program);
      } else {
         if (UnSetVectors(NamedPort))
         {
            RemPort(NamedPort);
            FreeAllMemory();
            UnLoadSeg(var(Segment));
            printf("%s removed\n",program);
            CloseLibrary(IntuitionBase);
         } else {
            printf("SetFunction vectors have been changed!\n");
            printf("Cannot remove %s\n",program);
         }
      }
   }
}
