/*
 *  VSCREEN-HANDLER.C
 *
 *                  Creates virtual screens that can be larger than
 *                  the actual display area of your monitor.  The virtual
 *                  screen scrolls when the mouse moves off the edge of
 *                  the display.
 *
 *                  Copyright 1988 by Davide P. Cervone, all rights reserved.
 *
 *                  You may may distibute this code as is, for non-commercial
 *                  purposes, provided that this notice remains intact and the
 *                  documentation file is distributed with it.
 *
 *                  You may not modify this code or incorporate it into your
 *                  own programs without the permission of the author.
 */

/*
 *  WARNING:  This code uses and even modifies the INTUITIONPRIVATE
 *  area of the IntuitionBase structure.  Use it at your own risk.  It is
 *  likely to break under a future release of Intuition.
 */

#include "vScreen.h"

static char program[] = "vScreen-Handler";  /* the program name */
static char author[]  = COPYRIGHT;          /* the copyright notice */
#define MAJVER  1                           /* major version number */
#define MINVER  0                           /* minor version number */


/*
 *  Macros for normalizing screen Y coordinates to absolute display
 *  corrdinates (using the LaceShift and LaceScreen variables).
 */

#define TWICE_Y(y)       ((y)<<LaceShift)
#define NTWICE_Y(y)      ((LaceScreen)?(y)<<1:(y))
#define HALF_Y(y)        ((y)>>LaceShift)
#define NHALF_Y(y)       ((LaceScreen)?(y)>>1:(y))


/*
 *  Macros for normalizing screen X coordinates to absolute display
 *  corrdinates (using the HiResShift and HiResScreen variables).
 */

#define TWICE_X(x)       ((x)<<HiResShift)
#define NTWICE_X(x)      ((HiResScreen)?(x)<<1:(x))
#define HALF_X(x)        ((x)>>HiResShift)
#define NHALF_X(x)       ((HiResScreen)?(x)>>1:(x))


/*
 *  The qualifiers used to check whether the left command key is pressed
 */

#define SHIFTQUALS      (~IEQUALIFIER_RELATIVEMOUSE)


static struct Screen *VScreen;          /* pointer to the virtual screen */
static SHORT ScreenWidth,ScreenHeight;  /* the new width and height */
static SHORT OldWidth,OldHeight;        /* the old width and height */
static SHORT *RxOffset,*RyOffset;       /* pointers to the RasInfo offsets */
static SHORT RxOffset2,RyOffset2;       /* normalized RxOffset and RyOffset */
static LONG  LaceScreen,LaceShift;      /* used to normalize Y positions */
static LONG  HiResScreen,HiResShift;    /* used to normalize X positions */
static WORD  OldMaxDH,OldMaxDR,OldMaxDW; /* Old MaxDisplay values */

static WORD MinXMouse,MaxXMouse,MinYMouse,MaxYMouse;
   /* the Display min and max values (as opposed to the Intuition values) */
static WORD Xshift = 0;                 /* how far to shift VScreen */
static WORD Yshift = 0;                 /* how far to shift VScreen */

static struct Screen *OldScreen = NULL; /* pointer to last active screen */
static WORD ScreenTop,ScreenBottom;     /* Y position of vScreen borders */
static short NotVScreen = FALSE;        /* TRUE when mouse not over VScreen */

#define BOTTOMSCREEN    1               /* Mouse over screen below  VScreen */
#define TOPSCREEN      -1               /* mouse over screen behind VScreen */
#define OVERVSCREEN     (NotVScreen == FALSE)


extern struct vScreenInfo vScreenData;  /* the data neede by vScreen */


/*
 *  Setup()
 *
 *  This routine gets called by vScreen after vScreen-Handler gets LoadSeged.
 *  Setup must return the pointer to the data structure that vScreen uses
 *  to initialize vScreen-Handler's variables.
 *
 *  vScreen passes its version number to Setup().  If Setup() detects a
 *  version mismatch, it should return NULL.
 */

struct vScreenInfo *Setup(version)
int version;
{
   return(&vScreenData);
}


/*
 *  SetScreenMinMax()
 *
 *  Set the OldScreen to the current ActiveScreen.  If it is VScreen,
 *  the set the Min and Max Mouse values to the virtual screen sizes,
 *  otherwise, set them to the current display edges (in the coordinate
 *  system of the virtual screen; i.e., origin is offset by RxOffset and
 *  RyOffset).
 */

static void SetScreenMinMax()
{
   if ((OldScreen = IntuitionBase->ActiveScreen) == VScreen) 
   {
      IntuitionBase->MinXMouse = 0;
      IntuitionBase->MaxXMouse = TWICE_X(ScreenWidth) - 1;
      IntuitionBase->MinYMouse = IntuitionBase->MouseYMinimum;
      IntuitionBase->MaxYMouse = TWICE_Y(ScreenHeight) - 1;
   } else {
      IntuitionBase->MinXMouse = RxOffset2;
      IntuitionBase->MaxXMouse = OldMaxDW + RxOffset2 - 1;
      IntuitionBase->MinYMouse = IntuitionBase->MouseYMinimum + RyOffset2;
      IntuitionBase->MaxYMouse = OldMaxDR + RyOffset2;
   }
}


/*
 *  ResetVScreen()
 *
 *  When the mouse moves over a screen other than the virtual screen, we need 
 *  to reset the modified Intuition fields to their normal values so that if
 *  the mouse is pressed over the other screen, it hits the right position.
 *
 *  Set OldScreen to the ActiveScreen.
 *  Set the Min and Max Mouse values where scrolling will occur to the 
 *    display edges (relative to the non-virtual screen).
 *  Set the Intuition MaxDisplay values to their original dimensions.
 *  Subtract the RxOffset and RyOffsets from the Intuition mouse coordinates
 *    (they are now relative to the screen the mouse is moving over).
 */

static void ResetVScreen()
{
   OldScreen = IntuitionBase->ActiveScreen;
   MinXMouse = IntuitionBase->MinXMouse = 0;
   MaxXMouse = IntuitionBase->MaxXMouse = OldMaxDW - 1;
   MinYMouse = IntuitionBase->MinYMouse = IntuitionBase->MouseYMinimum;
   MaxYMouse = IntuitionBase->MaxYMouse = OldMaxDR;
   IntuitionBase->MaxDisplayHeight = OldMaxDH;
   IntuitionBase->MaxDisplayRow    = OldMaxDR;
   IntuitionBase->MaxDisplayWidth  = OldMaxDW;
   IntuitionBase->MouseX -= RxOffset2;
   IntuitionBase->MouseY -= RyOffset2;
}


/*
 *  SetVScreen()
 *
 *  When the mouse moves back over the virtual screen, we need to change to
 *  Intuition Mouse corrdinates to be relative to the virtual screen again,
 *  so that if the mouse is pressed, it will hit the right spot.
 *
 *  Set the Min and Max values for the Intuition Mouse.
 *  Set the Min and Max values where scrolling will occur (relative to 
 *    the virtual screen origin).
 *  Set the Intuition MaxDisplay values to the full screen size (so that
 *    Intuition will allow the mouse to move to the edge of the virtual
 *    screen).
 *  Add the RxOffset and RyOffset to the Intuition Mouse position so that
 *    Intuition thinks the mouse is positioned relative correctly on the
 *    virtual screen (we compensate for this in MoveSprite).
 */

static void SetVScreen()
{
   SetScreenMinMax();
   MinXMouse = RxOffset2;
   MaxXMouse = OldMaxDW + RxOffset2 - 1;
   MinYMouse = IntuitionBase->MouseYMinimum + RyOffset2;
   MaxYMouse = OldMaxDR + RyOffset2;
   IntuitionBase->MaxDisplayHeight = TWICE_Y(ScreenHeight);
   IntuitionBase->MaxDisplayRow    = TWICE_Y(ScreenHeight) - 1;
   IntuitionBase->MaxDisplayWidth  = TWICE_X(ScreenWidth);
   IntuitionBase->MouseX += RxOffset2;
   IntuitionBase->MouseY += RyOffset2;
}


/*
 *  FindBounds()
 *
 *  Locate the absolute display position of the top of the virtual screen
 *  and of the top of the highest screen that is on top of the virtual
 *  screen.  That is, if the mouse is between the TopScreen and BottomScreen
 *  Y values, then it is positioned over top pf the visible portion of the
 *  virtual screen.  If the screen is not showing, then BottomScreen will
 *  be less that TopScreen.
 *
 *  Note that TopScreen and BottomScreen are relative to a 320 x 200 screen,
 *  since they are compared to the mouse pointer coordinates (since the pointer
 *  is a sprite, its coordinates always are LoRes and Non-Interlaced).
 */

static void FindBounds()
{
   struct Screen *theScreen = IntuitionBase->FirstScreen;
   WORD Top;
   
   ScreenTop = NHALF_Y(VScreen->TopEdge) - 1;
   ScreenBottom = IntuitionBase->MaxDisplayRow;
   while (theScreen && theScreen != VScreen)
   {
      Top = theScreen->TopEdge;
      if (theScreen->ViewPort.Modes & LACE) Top >>= 1;
      if (Top < ScreenBottom) ScreenBottom = Top;
      theScreen = theScreen->NextScreen;
   }
}


/*
 *  FixView()
 *
 *  When the virtual Screen is the first screen, its viewport will try to 
 *  display ALL of the vertical length of the screen.  This might cause an
 *  overscan display when there is not supposed to be one, so when this
 *  occurs, we set the ViewPort height manually, remake the Screen, and
 *  merge the copper list into the rest of the display.  This routine gets
 *  called whenever the intuition View is loaded.  Since this happens
 *  whenever a screen changes position, this is a good place to check the
 *  screen top and bottom bounds.
 */

static void FixView(ForceUpdate)
int ForceUpdate;
{
   short TooBig = (VScreen->ViewPort.DHeight > OldHeight);

   if (TooBig || ForceUpdate)
   {
      if (TooBig) VScreen->ViewPort.DHeight = OldHeight;
      MakeScreen(VScreen);
      MrgCop(&(IntuitionBase->ViewLord));
   }
   FindBounds();
}


/*
 *  CheckShift()
 *
 *  Check that a forced screen shoft will not go too far.  If it does, then
 *  clear the InputEvent mouse movement so that the mouse doesn't move
 *  when the screen can't scroll any more.
 *
 *  If the mouse is not over the virtual screen, then we don't want the
 *  mouse to move at all, so clear the InputEvent mouse movement.
 */

static void CheckShift(Xshift,Yshift,theEvent)
WORD *Xshift,*Yshift;
struct InputEvent *theEvent;
{
   if (*Xshift < -RxOffset2 ||
       *Xshift > TWICE_X(ScreenWidth-OldWidth)-RxOffset2)
   {
      *Xshift -= theEvent->ie_X;
      theEvent->ie_X = 0;
   }
   if (*Yshift < -RyOffset2 ||
       *Yshift > TWICE_Y(ScreenHeight-OldHeight)-RyOffset2)
   {
      *Yshift -= theEvent->ie_Y;
      theEvent->ie_Y = 0;
   }
   if (NotVScreen) theEvent->ie_X = theEvent->ie_Y = 0;
}


/*
 *  ShiftVScreen()
 *
 *  Shift the virtual screen by the specified amount.
 *
 *  If there is movement in the X direction,
 *    Check that the screen does not scroll too far to the left or right.
 *    If it does, then reduce the move so that the screen scrolls to the edge.
 *    If the mouse is over the virtual screen, then
 *      Adjust the Min and Max values for when scrolling occurs.
 *    Increment the Screen's ViewPort RasInfo RxOffset by the amount of
 *      the scroll, and record the normalized value for later use.
 *      The RxOffset is what tells the graphics library which part of the
 *      larger bitmap should actually be displayed.
 *
 *  Similarly for the Y shift.
 *
 *  Finally, if there was actually movement in either direction,
 *    Fix the View (FixView calls MakeScreen() and MrgCop()) and 
 *    Load the new View.
 */

static void ShiftVScreen(Xmove,Ymove)
WORD Xmove,Ymove;
{
   WORD xOffset, yOffset;

   if (Xmove)
   {
      xOffset = *RxOffset + Xmove;
      if (xOffset < 0) Xmove -= xOffset;
      if (xOffset > ScreenWidth-OldWidth)
         Xmove += (ScreenWidth-OldWidth - xOffset);
      if (OVERVSCREEN)
      {
         MinXMouse += TWICE_X(Xmove);
         MaxXMouse += TWICE_X(Xmove);
      }
      *RxOffset += Xmove;
      RxOffset2  = TWICE_X(*RxOffset);
   }

   if (Ymove)
   {
      yOffset = *RyOffset + Ymove;
      if (yOffset < 0) Ymove -= yOffset;
      if (yOffset > ScreenHeight-OldHeight)
         Ymove += (ScreenHeight-OldHeight - yOffset);
      if (OVERVSCREEN)
      {
         MinYMouse += TWICE_Y(Ymove);
         MaxYMouse += TWICE_Y(Ymove);
      }
      *RyOffset += Ymove;
      RyOffset2  = TWICE_Y(*RyOffset);
   }

   if (Xmove || Ymove)
   {
      FixView(TRUE);
      LoadView(&(IntuitionBase->ViewLord));
   }
}


/*
 *  cLoadView()
 *
 *  Replaces the LoadView function in the graphics library (the stub calls
 *  the old LoadView() after cLoadView() runs).
 *
 *  If the virtual screen is still open, and the view is the Intuition View,
 *  the Fix the view (make sure that the height is no larger than the 
 *  old MaxDisplayHeight).
 *
 *  Note:  when you drag a screen up and down, Intuition calls RemakeDisplay()
 *  which in turn calls MakeScreen() on each screen, MrgCop() and finally
 *  LoadView().  cLoadView() gets called in place of LoadView(), and it will
 *  call FixView(), which calls MakeScreen() and MrgCop() again.  This are high
 *  overhead calls, so when the virtual screen is the FirstScreen and there is
 *  another screen showing behind it, this can nearly double the overhead of
 *  dragging a screen.
 */

void cLoadView(view)
struct View *view;
{
   if (VScreen && view == &(IntuitionBase->ViewLord)) FixView(FALSE);
}


/*
 *  cMoveSprite();
 *
 *  Repaces MoveSprite() in the graphics library (the stub calls the old
 *  MoveSprive() after cMoveSprite() runs).
 *
 *  If the sprite being moved is sprite zero (the pointer sprite), and
 *  the virtual screen is still open, then
 *    If the pointer is over the virtual screen
 *      Subtract the offsets (relative to a 320 x 200 screen, since this
 *      is a sprite).  We must do this, since we added RxOffset and RyOffset
 *      to the Intuition MouseX and MouseY values.  Since Intuition does not
 *      take the RxOffset and RyOffset into account, we have to subtract
 *      the values here in order to keep the pointer on the display area.
 *
 *    If the Min and Max X Mouse values are equal, a screen is being dragged.
 *    If the LayerInfo Lock has a NestCount, then the layers are locked.
 *
 *    If the mouse is not over the virtual screen then
 *      Set the MinY for scrolling to the top of the view.
 *      If we used to be over the virtual screen and 
 *         we're not currently dragging a screen, then
 *        If the virtual screen is not locked, then
 *          Reset the coordinate system to fit the screen we are over.
 *          Save which type of screen we're over.
 *         otherwise (the screen is locked; e.g., the user has the
 *                    menu button down, or is dragging a window),
 *          so scroll the screen if we move off the top.
 *    Otherwise (the mouse IS over the virtual screen)
 *      Set the MinY for scrolling to the top of the view relative to the 
 *        virtual screen origin.
 *      If we used to be over some other screen, and we're not dragging
 *         a screen, and the current screen is not locked, then
 *        (we need to change coordinates to the virtual screen coordinates)
 *        If we are going from a bottom screen onto the virtual screen
 *          then activate the cirtual screen (I couldn't find a way to do
 *          the coordinate transfer without this).
 *        Set the coordinates relative to the virtual screen.
 *        Record that we are over the virtual screen.
 *
 *    If a new screen has become active then
 *      If we were over the virtual screen, check that we still are
 *      Record the active screen
 *
 *    If the user is dragging the screen, don't let him drag it off
 *      the bottom of the display area (where he won;t be able to reach it).
 *
 *    If the MouseY has gone past the top of the screen (i.e., if a screen
 *      has closed and the pointer was above it), put the pointer at the
 *      top of the display.
 */

void cMoveSprite(ss,x,y)
struct SimpleSprite *ss;
long x,y;
{
   short NotDrag;
   short NotLocked;

   if (ss->num == 0 && VScreen)
   {
      if (OVERVSCREEN)
      {
         x -= NHALF_X(*RxOffset);
         y -= NHALF_Y(*RyOffset);
      }

      NotDrag = (IntuitionBase->MinXMouse != IntuitionBase->MaxXMouse);
      NotLocked = 
         (IntuitionBase->ActiveScreen->LayerInfo.Lock.ss_NestCount == 0);

      if (y < ScreenTop || y >= ScreenBottom)
      {
         MinYMouse = IntuitionBase->MouseYMinimum;
         if (OVERVSCREEN && NotDrag)
         {
            if (NotLocked)
            {
               ResetVScreen();
               NotVScreen = (y < ScreenTop)? TOPSCREEN: BOTTOMSCREEN;
            } else {
               if (y < ScreenTop) ShiftVScreen(0,NTWICE_Y(y-ScreenTop));
            }
         }
      } else {
         MinYMouse = IntuitionBase->MouseYMinimum + RyOffset2;
         if (NotVScreen && NotDrag && NotLocked)
         {
            if (NotVScreen == BOTTOMSCREEN &&
                IntuitionBase->ActiveScreen != VScreen &&
                VScreen->FirstWindow) ActivateWindow(VScreen->FirstWindow);
            SetVScreen();
            NotVScreen = FALSE;
         }
      }

      if (OldScreen != IntuitionBase->ActiveScreen)
      {
         if (OVERVSCREEN) SetScreenMinMax();
         OldScreen = IntuitionBase->ActiveScreen;
      }

      if (NotDrag == FALSE &&
          IntuitionBase->MaxYMouse > OldMaxDR + RyOffset2)
              IntuitionBase->MaxYMouse = OldMaxDR + RyOffset2;

      if (IntuitionBase->MouseY < IntuitionBase->MinYMouse)
         IntuitionBase->MouseY = IntuitionBase->MinYMouse;
   }
}


/*
 *  cAutoRequest()
 *
 *  Replaces AutoRequest() in the Intuition Library (the stub calls 
 *  AutoRequest() after cAutoRequest() runs).
 *
 *  If the AutoRequest is to appear on the virtual screen, then shift
 *  the virtual screen so that the System Request window will be showing.
 *  Note that this could change the screen position even while the user is
 *  doing critical work (e.g., dragging a window, pulling menus, etc).
 *  It would be better to trap this through BuildSysRequest(), but since
 *  Intuition does not use its own vector table, I can not trap all the
 *  calls to BuildSysRequest, specifically, the ones called by AutoRequest().
 */

void cAutoRequest(theWindow)
struct Window *theWindow;
{
   if (theWindow == NULL || theWindow->WScreen == VScreen)
   {
      IntuitionBase->MouseX -= RxOffset2;
      IntuitionBase->MouseY -= RyOffset2;
      ShiftVScreen(-(*RxOffset),-(*RyOffset));
   }
}


/*
 *  cBuildSysRequest()
 *
 *  Replaces BuildSysRequest() in the Intuition Libaray (the stub routine
 *  calls BuildSysRequest() before calling cBuildSysReques()).
 *
 *  If the window (returned by BuildSysRequest) exists, then
 *    If it is on the virtual screen, and it is not completely showing, then
 *      Move the window so that it is in the upper left corner of the
 *        displayed section of the screen.
 *
 *  Note that this is better than what we do with AutoRequest, since the
 *  screen itself does not scroll.
 */

void cBuildSysRequest(theWindow)
struct Window *theWindow;
{
   if (theWindow && theWindow != (struct Window *)TRUE)
   {
      if (theWindow->WScreen == VScreen &&
         (theWindow->LeftEdge < *RxOffset ||
          theWindow->TopEdge  < *RyOffset))
             MoveWindow(theWindow,*RxOffset-theWindow->LeftEdge,
                                  *RyOffset-theWindow->TopEdge);
   }
}


/*
 *  cCloseScreen()
 *
 *  Replaces CloseScreen() in the IntuitionLibrary (the stub routine calls
 *  CloseScreen() after cCloseScreen() runs).
 *
 *  If the virtual screen is still open, and it is the one being closed,
 *    reset the coordinate system, and mark the virtual screen as closed.
 */

void cCloseScreen(theScreen)
struct Screen *theScreen;
{
   if (theScreen == VScreen && VScreen != NULL)
   {
      ResetVScreen();
      VScreen = NULL;
   }
}


/*
 *  myHandler()
 *
 *  This is the input handler that gets added to the Input.Device input
 *  chain.  It is at priority 51, so it is ahead of Intuition.
 *
 *  If a screen is not being dragged, and the virtual screen is still open,
 *    then for each event in the event list,
 *      If the event is a RAWMOUSE event,
 *        If the left Amiga key is held down then
 *          Add it into the forced shift count and
 *          Check that the shift is valid.
 *        Add the movement into the total movement so far.
 *
 *    Find the new MouseX and MouseY positions.
 *    Shift the screen by the forced shift amounts, checking that the 
 *      coordinates are shifted when needed, and retaining any un-used
 *      shift amount (due to LoRes or Non-Interlaced screens).
 *
 *    If the active screen is the virtual screen, then
 *      if the mouse has moved off the left or right edge of the display,
 *      or if the mouse has moved past the Intuition Min or Max values,
 *      then shift the screen by the appropriate amount.  Do this for 
 *      both the X and Y directions.
 *
 *  Finally, return the original event list.
 */

struct InputEvent *myHandler(EventList,data)
struct InputEvent *EventList;
APTR data;
{
   struct InputEvent *theEvent = EventList;
   WORD MouseX,MouseY;
   WORD Xmove = 0;
   WORD Ymove = 0;
   WORD Ychange = 0;
   WORD Xchange = 0;
   WORD dx,dy;
   
   if (IntuitionBase->MinXMouse != IntuitionBase->MaxXMouse && VScreen)
   {
      Forbid();
      while (theEvent)
      {
         if (theEvent->ie_Class == IECLASS_RAWMOUSE)
         {
            if ((theEvent->ie_Qualifier & SHIFTQUALS) == IEQUALIFIER_LCOMMAND)
            {
               Xshift += theEvent->ie_X;
               Yshift += theEvent->ie_Y;
               CheckShift(&Xshift,&Yshift,theEvent);
            }
            Xchange += theEvent->ie_X;
            Ychange += theEvent->ie_Y;
         }
         theEvent = theEvent->ie_NextEvent;
      }
      Permit();
   
      MouseX = IntuitionBase->MouseX + HALF_X(Xchange);
      MouseY = IntuitionBase->MouseY + HALF_Y(Ychange);

      if (Xshift < 0) dx = -HALF_X(-Xshift); else dx = HALF_X(Xshift);
      if (Yshift < 0) dy = -HALF_Y(-Yshift); else dy = HALF_Y(Yshift);
      if (dx || dy)
      {
         ShiftVScreen(dx,dy);
         if (IntuitionBase->ActiveScreen != VScreen && OVERVSCREEN)
            SetScreenMinMax();
         if (HiResScreen) Xshift = 0; else Xshift %= 2;
         if (LaceScreen)  Yshift = 0; else Yshift %= 2;
      }

      if (IntuitionBase->ActiveScreen == VScreen)
      {
         if (MouseX < MinXMouse) Xmove = MouseX - MinXMouse;
         if (MouseX > MaxXMouse) Xmove = MouseX - MaxXMouse;
         if (Xmove == 0)
         {
            if (MouseX < IntuitionBase->MinXMouse)
               Xmove = MouseX - IntuitionBase->MinXMouse;
            if (MouseX > IntuitionBase->MaxXMouse)
               Xmove = MouseX - IntuitionBase->MaxXMouse;
         }
   
         if (MouseY < MinYMouse) Ymove = MouseY - MinYMouse;
         if (MouseY > MaxYMouse) Ymove = MouseY - MaxYMouse;
         if (Ymove == 0)
         {
            if (MouseY < IntuitionBase->MinYMouse)
               Ymove = MouseY - IntuitionBase->MinYMouse;
            if (MouseY > IntuitionBase->MaxYMouse)
               Ymove = MouseY - IntuitionBase->MaxYMouse;
         }

         if (Xmove || Ymove) ShiftVScreen(Xmove,Ymove);
      }
   }
   return(EventList);
}


/*
 *  These are the assembler stubs needed in order to SetFunction some
 *  of the Intuiton and graphics library functions.
 */

extern void myHandlerStub();

extern void aCloseScreen();
extern void aBuildSysRequest();
extern void aAutoRequest();
extern void aLoadView();
extern void aMoveSprite();


/*
 *  These are the jump addresses in the assembler routines that need to
 *  be filled in by vScreen once the old SetFunction vectors are known.
 *  This is a kludge, and is a form od self-modifying code, but I can't figure
 *  out a better way that works.  The JSR (Ax) form is only good if there is
 *  a free A register to use, which is not always the case.
 */

extern unsigned char *CloseScreenJmpAddress;
extern unsigned char *BuildSysRequestJmpAddress;
extern unsigned char *AutoRequestJmpAddress;
extern unsigned char *LoadViewJmpAddress;
extern unsigned char *MoveSpriteJmpAddress;

static char PortName[] = PORTNAME;      /* the name of the named port */

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


/*
 *  This is the structure passed from the Setup() routine to vScreen.
 *  It includes pointers to the variables that vSCreen needs to initialize,
 *  plus pointers to the routines that vScreen will SetFunction into the
 *  appropriate libraries.
 */

struct vScreenInfo vScreenData =
{
   MAJVER,MINVER,0,
   &program[0],
   &PortName[0],
   NULL,
   &HandlerInfo,
   &IntuitionBase, &GfxBase,

   &VScreen,
   &ScreenWidth,&ScreenHeight,
   &OldWidth,&OldHeight,
   &RxOffset,&RyOffset,
   &RxOffset2,&RyOffset2,
   &LaceScreen,&LaceShift,
   &HiResScreen,&HiResShift,
   &OldMaxDH,&OldMaxDR,&OldMaxDW,
   
   &SetVScreen, &ResetVScreen, &FixView,

   &aCloseScreen, &aBuildSysRequest, &aAutoRequest, &aLoadView, &aMoveSprite,
   
   (long *) &CloseScreenJmpAddress,
   (long *) &BuildSysRequestJmpAddress,
   (long *) &AutoRequestJmpAddress,
   (long *) &LoadViewJmpAddress,
   (long *) &MoveSpriteJmpAddress,
   
   0,0,0,0,0
};
