#!/bin/sh
#Date:     Thu, 17 Mar 88 11:51 EDT
#From:     Davide P. Cervone <DPVC@UORDBV>
#Subject:  vScreen.sh
#To:       TLIMONCE@DREW
#
#vScreen allows you to have screens that are larger than the actual display
#area of your monitor.  These larger "virtual" screens scroll when you move the
#mouse off the edge of the visible section of the screen.  For example, you can
#have a 1024 x 512 workbench screen (with a 640 x 200 section showing) that you
#can scroll around.  Of course, windows can be stretched to the full size of
#the screen, if desired.
#
#This file contians all the source code for vScreen and its associated
#handler.  See the executable file for documentation.
#
#Davide P. Cervone
#dpvc@tut.cc.rochester.edu
#dpvc@ur-tut.UUCP
#DPVC@UORDBV.BITNET
#
#
#       This is a shell archive.
#       Remove everything above and including the cut line.
#       Then run the rest of the file through sh.
#----cut here-----cut here-----cut here-----cut here----#
#!/bin/sh
# shar:    Shell Archiver
#       Run the following text with /bin/sh to create:
#       vScreen.c
#       vScreen.h
#       vScreenSetup.c
#       vScreen-Handler.c
#       vScreenStubs.a
#       vScreenStubs.o.uu
#       vScreen.lnk
#       vScreen-Handler.lnk
# This archive created: Thu Mar 10 18:45:08 1988
# By:    ()
cat << \SHAR_EOF > vScreen.c
/*
 *  VSCREEN.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";           /* the program name */
static char *author  = COPYRIGHT;           /* the copyright notice */
#define LOADVERS        1                   /* this program's version */
 
static char *handler = "L:vScreen-Handler"; /* the name of the handler */
#define HANDLER         &(handler[2])       /* same but in the current dir */
 
 
extern struct LayersBase *LayersBase;       /* the Layers library */
 
struct vScreenInfo *vScreenData;            /* data needed by the handler */
struct Screen *VScreen = NULL;              /* the virtual screen */
static long Segment = NULL;                 /* the loaded handler */
SHORT ScreenWidth,ScreenHeight;             /* the new screen sizes */
 
static struct MsgPort *NamedPort = NULL;    /* used to find the handler later */
static struct MsgPort *InputPort = NULL;    /* to talk to Input.Device */
static struct IOStdReq *InputBlock = NULL;  /* IO block for Input.Device */
static short  InputDevice = FALSE;          /* is Input.Device open? */
static int    Enlarged = FALSE;             /* is screen changed? */
 
 
/*
 *  These routines are in vScreenSetup.c
 */
 
extern struct Screen *FindScreen();
extern void SetVariables();
extern void GetVariables();
extern int  EnlargeScreen();
extern void RestoreScreen();
 
 
#ifndef PROTO
extern long SetFunction();
#endif
 
/*
 *  We trap these routines via SetFunction in order to make vSscreen work.
 */
 
#ifndef PROTO
extern void MoveSprite();
extern void LoadView();
extern void AutoRequest();
extern void BuildSysRequest();
extern void CloseScreen();
#endif
 
extern long LVOMoveSprite;
extern long LVOLoadView;
extern long LVOAutoRequest;
extern long LVOBuildSysRequest;
extern long LVOCloseScreen;
 
 
/*
 *  CreateNonSigPort()
 *
 *  Creates a message port with signal type PA_IGNORE.  Based on
 *  CreatePort() from the exec support functions documented in the RKM.
 */
 
struct MsgPort *CreateNonSigPort(name,pri)
char *name;
BYTE pri;
{
   struct MsgPort *thePort;
 
   thePort = (struct MsgPort *)AllocMem((ULONG)sizeof(struct MsgPort),
                                         MEMF_PUBLIC | MEMF_CLEAR);
   if (thePort)
   {
      thePort->mp_Node.ln_Name = name;
      thePort->mp_Node.ln_Pri  = pri;
      thePort->mp_Node.ln_Type = NT_MSGPORT;
 
      thePort->mp_Flags = PA_IGNORE;
      thePort->mp_SigBit = 0;
      thePort->mp_SigTask = NULL;
 
      if (name)
         AddPort(thePort);
        else
         NewList(&(thePort->mp_MsgList));
   }
   return(thePort);
}
 
 
/*
 *  DeleteNonSigPort()
 *
 *  Deletes a message port with signal type PA_IGNORE.  Based on
 *  DeletePort() from the exec support functions documented in the RKM
 */
 
void DeleteNonSigPort(thePort)
struct MsgPort *thePort;
{
   if (thePort->mp_Node.ln_Name) RemPort(thePort);
   thePort->mp_Node.ln_Type = 0xFF;
   thePort->mp_MsgList.lh_Head = (struct Node *) -1;
   FreeMem(thePort,(ULONG)sizeof(struct MsgPort));
}
 
 
/*
 *  DoExit()
 *
 *  Exit with error status.  Print a message with up to three parameters
 *  and then clean up everything (restore the screen if it is enlarged,
 *  unload the handler if it is loaded, close the Input.Device and free
 *  its IO blocks and ports, and close any open libraries).  Return the
 *  error status.
 */
 
void DoExit(s,x1,x2,x3)
char *s, *x1,*x2,*x3;
{
   int status = OK_EXIT;
 
   if (s)
   {
      printf(s,x1,x2,x3);
      printf("\n");
      status = ERROR_EXIT;
   }
   if (Enlarged)        RestoreScreen();
   if (NamedPort)       DeleteNonSigPort(NamedPort);
   if (Segment)         UnLoadSeg(Segment);
   if (InputDevice)     CloseDevice(InputBlock);
   if (InputBlock)      DeleteStdIO(InputBlock);
   if (InputPort)       DeletePort(InputPort);
   if (IntuitionBase)   CloseLibrary(IntuitionBase);
   if (GfxBase)         CloseLibrary(GfxBase);
   if (LayersBase)      CloseLibrary(LayersBase);
   exit(status);
}
 
 
/*
 *  CheckLibOpen()
 *
 *  Call OpenLibrary() for the specified library, and check that the
 *  open succeeded.
 */
 
void CheckLibOpen(lib,name,rev)
APTR *lib;
char *name;
int rev;
{
   #ifndef PROTO
   extern APTR OpenLibrary();
   #endif
 
   if ((*lib = OpenLibrary(name,(ULONG)rev)) == NULL)
      DoExit("Can't open '%s'",name);
}
 
 
/*
 *  GetInt()
 *
 *  Read the first integer from the given string variable (used to parse
 *  the command-line arguments).  If no integer can be read, exit and
 *  show the usage string.
 */
 
static long GetInt(s)
char *s;
{
   long i;
 
   if (sscanf(s,"%ld",&i) != 1) DoExit("Usage:  %s",USAGE);
   return(i);
}
 
 
/*
 *  ParseArguments()
 *
 *  Parse the command-line arguments.  If there are too many or too few
 *  arguments, exit with an error message, otherwise get the width and height
 *  agruments.  If there was a screen name specified, record that.
 *  Find the specified screen.
 */
 
static void ParseArguments(argc,argv)
int argc;
char *argv[];
{
   char *ScreenName = NULL;
 
   if (argc < 3) DoExit("Width and Height are required\nUsage:  %s",USAGE);
   if (argc > 4) DoExit("Too many arguments");
   ScreenWidth  = GetInt(argv[1]);
   ScreenHeight = GetInt(argv[2]);
   if (ScreenWidth <= 0 || ScreenHeight <= 0)
      DoExit("Screen height and width must be positive");
   if (ScreenWidth > 1024 || ScreenHeight > 1024)
      printf("Warning:  sizes greater than 1024 may cause Blitter problems\n");
   if (argc > 3 && argv[3] && argv[3][0] != '\0') ScreenName = argv[3];
   VScreen = FindScreen(ScreenName);
}
 
 
/*
 *  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 a
 *  message.  Once the handler is loaded, call its Setup routine passing it
 *  our version number.  The handler will check the versions for compatability,
 *  and return NULL for version mismatch, or a pointer to its data structure
 *  with pointers to the variables that vScreen will initialize for it.
 *  LoadHandler() sets the pointer to the handlers SegList, and sets the
 *  loader version number
 */
 
static void LoadHandler()
{
   struct vScreenInfo *(*Setup)();
 
   if ((Segment = LoadSeg(HANDLER)) == NULL)
      if ((Segment = LoadSeg(handler)) == NULL)
         DoExit("Can't Load '%s'",handler);
   Setup = (struct vScreenInfo *(*)()) ((Segment << 2) + 4);
 
   vScreenData   = (*Setup)(LOADVERS);
   if (vScreenData)
   {
      if (var(MajVers) > 1) DoExit("version mismatch with '%s'",HANDLER);
   } else {
      DoExit("'%s' reports a version mismatch",HANDLER);
   }
 
   var(Segment)  = Segment;
   var(LoadVers) = LOADVERS;
}
 
 
/*
 *  TellInputDevice()
 *
 *  Create a port and I/O block, then open the input device.  Set up the
 *  I/O block to add or remove the input handler, and send the request
 *  to the input device.  Finally, close the device and delete the
 *  I/O block and port.
 */
 
void TellInputDevice(function)
int function;
{
   long status;
 
   if ((InputPort = CreatePort(0,0)) == NULL) DoExit("Can't Create Port");
   if ((InputBlock = CreateStdIO(InputPort)) == NULL)
      DoExit("Can't Create Standard IO Block");
   InputDevice = (OpenDevice("input.device",0,InputBlock,0) == 0);
   if (InputDevice == FALSE) DoExit("Can't Open 'input.device'");
 
   InputBlock->io_Command = (long) function;
   InputBlock->io_Data    = (APTR) vScreenData->HandlerInfo;
   if (status = DoIO(InputBlock)) DoExit("Error from DoIO:  %ld",status);
 
   CloseDevice(InputBlock); InputDevice = FALSE;
   DeleteStdIO(InputBlock); InputBlock = NULL;
   DeletePort(InputPort);   InputPort = NULL;
}
 
 
/*
 *  SetVectors()
 *
 *  Call SetFunction() to replace the library vectores for the routines that
 *  we need to trap.  Use the routines that the handler has supplied (they
 *  were passed to us in the vScreenData structure).  Save the old vectors
 *  in the vScreenData strucutre so we can replace them later.  Set the
 *  jump addresses so that the stub routines can call the old vectors.
 *  I Know this is a kludge, and is a form of self-modifying code, but I
 *  can't figure out a better method.  The JSR (Ax) is only good when there
 *  is a free A register, which is not always the case.
 */
 
void SetVectors()
{
   var(OldCloseScreen) =
       SetFunction(IntuitionBase,&LVOCloseScreen,var(aCloseScreen));
   var(CloseScreenJmpTarget)[-1] = (long) var(OldCloseScreen);
 
   var(OldBuildSysRequest) =
       SetFunction(IntuitionBase,&LVOBuildSysRequest,var(aBuildSysRequest));
   var(BuildSysRequestJmpTarget)[-1] = (long) var(OldBuildSysRequest);
 
   var(OldAutoRequest) =
      SetFunction(IntuitionBase,&LVOAutoRequest,var(aAutoRequest));
   var(AutoRequestJmpTarget)[-1] = (long) var(OldAutoRequest);
 
   var(OldLoadView) = SetFunction(GfxBase,&LVOLoadView,var(aLoadView));
   var(LoadViewJmpTarget)[-1] = (long) var(OldLoadView);
 
   var(OldMoveSprite) = SetFunction(GfxBase,&LVOMoveSprite,var(aMoveSprite));
   var(MoveSpriteJmpTarget)[-1] = (long) var(OldMoveSprite);
}
 
 
/*
 *  UnSetVectors()
 *
 *  Put back the old jump vectors for the routines that we replaced.
 *  Make sure that no one else has replced them behind our backs, however.
 *  If they are not the same way we left them, return an error status.
 */
 
int UnSetVectors()
{
   long NewCloseScreen,NewBuildSysRequest,NewAutoRequest,
        NewLoadView,NewMoveSprite;
   int status = TRUE;
 
   NewCloseScreen =
      SetFunction(IntuitionBase,&LVOCloseScreen,var(OldCloseScreen));
   NewBuildSysRequest =
      SetFunction(IntuitionBase,&LVOBuildSysRequest,var(OldBuildSysRequest));
   NewAutoRequest =
      SetFunction(IntuitionBase,&LVOAutoRequest,var(OldAutoRequest));
   NewLoadView   = SetFunction(GfxBase,&LVOLoadView,var(OldLoadView));
   NewMoveSprite = SetFunction(GfxBase,&LVOMoveSprite,var(OldMoveSprite));
 
   if (NewCloseScreen     != (long) var(aCloseScreen) ||
       NewBuildSysRequest != (long) var(aBuildSysRequest) ||
       NewAutoRequest     != (long) var(aAutoRequest) ||
       NewLoadView        != (long) var(aLoadView) ||
       NewMoveSprite      != (long) var(aMoveSprite))
   {
      SetFunction(IntuitionBase,&LVOCloseScreen,NewCloseScreen);
      SetFunction(IntuitionBase,&LVOBuildSysRequest,NewBuildSysRequest);
      SetFunction(IntuitionBase,&LVOAutoRequest,NewAutoRequest);
      SetFunction(GfxBase,&LVOLoadView,NewLoadView);
      SetFunction(GfxBase,&LVOMoveSprite,NewMoveSprite);
      status = FALSE;
   }
   return(status);
}
 
 
/*
 *  main()
 *
 *  Look for the vScreen port.
 *  If one does not exist then vScreen is not currently active, so
 *    open the intuition and graphics libraries.
 *    Parse the command-line arguments to get the width, height, and screen.
 *    Load the handler code, and check its version.
 *    Create the named port used to store information about the handler.
 *    Setup some of the variables needed by the handler, and save a pointer
 *      to the handler data in the named port.
 *    Try to enlarge the size of the screen bitmap, and set more variables.
 *    Add the input handler into the input chain.
 *    SetFunction the neede vectors.  At this point vScreen is active.
 *    Print a message that reports the version numbers.
 *  Otherwise (the port already exists, so vScreen already is active)
 *    Get the pointer to the vScreenData structure from the port.
 *    If they user had supplied arguments, tell him vScreen already is running.
 *    Try to unset the routines we replaced earlier.
 *    If the vectors we removed successfully then
 *      Remove the input handler.
 *      Restore the screen to its original size.
 *      Delete the (no-longer-needed) named port.
 *      Unload the handler code.
 *      Tell the user that the code is removed.
 *      Close the libraries (note that these remained open between calls
 *        to vScreen, while the handler was active).
 *    Otherwise (we could not replce the function vectors)
 *      Report the problem, and leave the handler running.
 */
 
void main(argc,argv)
int argc;
char *argv[];
{
   NamedPort = FindPort(PORTNAME);
   if (NamedPort == NULL)
   {
      CheckLibOpen(&IntuitionBase,"intuition.library",INTUITION_REV);
      CheckLibOpen(&GfxBase,"graphics.library",GRAPHICS_REV);
 
      ParseArguments(argc,argv);
      LoadHandler();
      if ((NamedPort = CreateNonSigPort(var(PortName),0L)) == NULL)
         DoExit("Can't Create Message Port '%s'",var(PortName));
 
      SetVariables(NamedPort);
      Enlarged = EnlargeScreen();
      TellInputDevice(IND_ADDHANDLER);
      SetVectors();
 
      printf("%s v%d.%d.%d Installed\n",program,
         var(MajVers),var(MinVers),var(LoadVers));
   } else {
      GetVariables(NamedPort);
      if (argc > 1)
      {
         printf("%s already active on screen '%s'\n",program,VScreen->Title);
      } else {
         if (UnSetVectors())
         {
            TellInputDevice(IND_REMHANDLER);
            RestoreScreen();
            DeleteNonSigPort(NamedPort);
            UnLoadSeg(var(Segment));
            printf("%s Removed\n",program);
            CloseLibrary(IntuitionBase);
            CloseLibrary(GfxBase);
         } else {
            printf("SetFunction vectors have been changed!\n");
            printf("%s Not Removed\n",program);
         }
      }
   }
}
SHAR_EOF
cat << \SHAR_EOF > vScreen.h
/*
 *  VSCREEN.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.
 */
 
#define INTUITIONPRIVATE
 
#include <exec/types.h>
#include <exec/semaphores.h>
#include <exec/interrupts.h>
#include <exec/memory.h>
#include <devices/input.h>
#include <graphics/sprite.h>
#include <intuition/intuition.h>
#include <intuition/intuitionbase.h>
#include <intuition/screens.h>
#include <libraries/dos.h>
#ifndef MANX
   #ifdef PROTO
      #include <proto/intuition.h>
      #include <proto/exec.h>
      #include <proto/graphics.h>
      #include <proto/layers.h>
      #include <proto/dos.h>
   #endif
#endif
 
 
#define USAGE       "VSCREEN width height [screen]"
#define COPYRIGHT   "Copyright 1988 by Davide P. Cervone, all rights reserved"
 
#define PORTNAME    "vScreenPort"
 
 
#define INTUITION_REV   0L
#define GRAPHICS_REV    0L
#define LAYERS_REV      0L
 
#define ERROR_EXIT      RETURN_ERROR
#define OK_EXIT         RETURN_OK
 
extern struct IntuitionBase *IntuitionBase;
extern struct GfxBase *GfxBase;
#ifndef PROTO
extern struct DOSBase *DOSBase;
#endif
 
 
/*
 *  This structure is used to pass information from the Handler to the
 *  loader.  It contins pointers to the variables that the loader will
 *  have to initialize, and to the routines that it has to call, or
 *  SetFunction into the libraries.  A pointer to this structure is
 *  stored in the MsgPort so that the loader can find and remove the handler
 *  once it's job is done.
 */
 
struct vScreenInfo
{
   short MajVers,MinVers, LoadVers;     /* version of handler and loader */
   char *Program;                       /* name of the handler program */
   char *PortName;                      /* name of the public port */
   long Segment;                        /* SegList loaded by loader */
   struct Interrupt *HandlerInfo;       /* Input.Device handler */
   struct IntuitionBase **IntuitionBase;
   struct GfxBase **GfxBase;
 
   struct Screen **VScreen;             /* the virtual screen */
   SHORT *ScreenWidth,*ScreenHeight;    /* the new width and height */
   SHORT *OldWidth,*OldHeight;          /* old screen size */
   SHORT **RxOffset,**RyOffset;         /* pointer to RasInfo offsets */
   SHORT *RxOffset2,*RyOffset2;         /* normalized offsets */
   LONG  *LaceScreen,*LaceShift;        /* LACE screen? */
   LONG  *HiResScreen,*HiResShift;      /* HIRES screen ? */
   WORD  *OldMaxDH,*OldMaxDR,*OldMaxDW; /* old IntuitionBase MaxDisplays */
 
   /*
    *  These routines are called by the loader:
    */
 
   void (*SetVScreen)();
   void (*ResetVScreen)();
   void (*FixView)();
 
   /*
    *  These routines replace the Intution and Graphics library routines:
    */
 
   void (*aCloseScreen)();
   void (*aBuildSysRequest)();
   void (*aAutoRequest)();
   void (*aLoadView)();
   void (*aMoveSprite)();
 
   /*
    *  These are the addresses of the JSR or JMP instructions that
    *  need to be changed to call the old library vectors:
    */
 
   long *CloseScreenJmpTarget;
   long *BuildSysRequestJmpTarget;
   long *AutoRequestJmpTarget;
   long *LoadViewJmpTarget;
   long *MoveSpriteJmpTarget;
 
   /*
    *  These are the old function vectors returned by SetFunction():
    */
 
   long OldCloseScreen;
   long OldBuildSysRequest;
   long OldAutoRequest;
   long OldLoadView;
   long OldMoveSprite;
};
 
 
/*
 *  These macros give easy access to the vScreenInfo structure:
 */
 
#define VAR(x)      (*(vScreenData->x))
#define var(x)      (vScreenData->x)
 
 
/*
 *  External routine declarations:
 */
 
#ifndef PROTO
extern struct Window *OpenWindow();
extern struct IntuiMessage *GetMsg();
extern APTR OpenLibrary();
extern PLANEPTR AllocRaster();
extern UBYTE *AllocMem();
extern struct MsgPort *CreatePort();
extern struct IOStdReq *CreateStdIO();
extern LONG OpenDevice();
extern struct MsgPort *FindPort();
extern long LoadSeg();
#endif
SHAR_EOF
cat << \SHAR_EOF > vScreenSetup.c
/*
 *  VSCREENSETUP.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"
 
struct LayersBase *LayersBase = NULL;       /* the Layers Library */
 
extern struct vScreenInfo *vScreenData;     /* the data from the Handler */
extern struct Screen *VScreen;              /* the virtual screen pointer */
extern SHORT ScreenWidth,ScreenHeight;      /* the new width and height */
static UBYTE OldDepth;                      /* the screen depth */
 
extern void CheckLibOpen();
extern void DoExit();
 
 
#define BLT_COPY    0xC0                    /* blitter copy function */
#define MIN(x,y)    (((x)<(y))?(x):(y))     /* MIN macro */
 
 
/*
 *  FindScreen()
 *
 *  If a screen name was specified, look through the Intuition screens
 *  for the first one that matches the specified name (case does not matter),
 *  otherwise, use the active screen.
 *  if the screen was not found, exit with an error message.
 */
 
struct Screen *FindScreen(ScreenName)
char *ScreenName;
{
   struct Screen *theScreen;
 
   Forbid();
   if (ScreenName && ScreenName[0])
   {
      theScreen = IntuitionBase->FirstScreen;
      while (theScreen && (theScreen->Title == NULL ||
             stricmp(theScreen->Title,ScreenName) != 0))
                theScreen = theScreen->NextScreen;
   } else {
      theScreen = IntuitionBase->ActiveScreen;
   }
   Permit();
   if (theScreen == NULL) DoExit("Can't find screen '%s'",ScreenName);
   return(theScreen);
}
 
 
/*
 *  CheckWindows()
 *
 *  Make sure that all the windows on the virtual screen will fit on the
 *  screen when we reduce it to its original size.
 *
 *  For each window on the screen,
 *    check that its right edge will be on the smaller-sized screen.
 *    if not, move it to the left so that it will be.
 *      if that would move the left egde off the screen, then
 *        shrink the window so that it fits.
 *    Check the the bottom edge will be on the smaller screen.
 *    if no, then move it up so that it will be.
 *      if that would move the top edge off the screen, then
 *         shrink the window so that it fits.
 *    If the window needs to change size or position, do so, and record
 *      the number of changes made.
 *
 *  if any windows were changed, delay long enough for Intuition to update
 *    the windows before we actually restore the screen size.  (This is a
 *    kludge, but I don't know a better method that this.  You may need to
 *    adjust the timing factore for busier screens).
 */
 
static void CheckWindows(theScreen,theWidth,theHeight)
struct Screen *theScreen;
SHORT theWidth,theHeight;
{
   struct Window *theWindow;
   SHORT x,y, w,h;
   SHORT Wx,Wy, Ww,Wh;
   short wChanged = 0;
 
   if (theScreen)
   {
      theWindow = theScreen->FirstWindow;
      while (theWindow)
      {
         Wx = x = theWindow->LeftEdge;
         Wy = y = theWindow->TopEdge;
         Ww = w = theWindow->Width;
         Wh = h = theWindow->Height;
 
         if (x+w > theWidth)
         {
            x = theWidth - w;
            if (x < 0)
            {
               x = 0;
               w = theWidth;
            }
         }
 
         if (y+h > theHeight)
         {
            y = theHeight - h;
            if (y < 0)
            {
               y = 0;
               h = theHeight;
            }
         }
 
         if (x != Wx || y != Wy)
         {
            MoveWindow(theWindow,x-Wx,y-Wy);
            wChanged++;
         }
 
         if (w != Ww || h != Wh)
         {
            SizeWindow(theWindow,w-Ww,h-Wh);
            wChanged++;
         }
 
         theWindow = theWindow->NextWindow;
      }
   }
   if (wChanged) Delay(wChanged * 30L);
}
 
 
/*
 *  GetPlane()
 *
 *  Allocate a bitplane and copy one of the VScren bitplanes into it, then
 *  free the old bitplane and replace it with the new one.
 *
 *  Two temporary bitmaps are used so that we can call BltBitMap.  The
 *  new bitplane is cleared in case it is larger than the old one, then
 *  the old one is copied.  Since BltBitMap is asynchronous, we call BltClear
 *  on a dummy section of memory to synchronize with the BltBitMap.  That way
 *  we don't free the old raster until it is fully copied.
 *
 *  GetPlane() is used to get the larger bitmap as well as the smaller one
 *  when we restore the original screen, so MIN() is used to determine
 *  the size of the BltBitMap() action.
 */
 
static int GetPlane(i,Map1,Map2,junk)
int i;
struct BitMap *Map1,*Map2;
UBYTE *junk;
{
   int error = TRUE;
   long w1 = (Map1->BytesPerRow) << 3;
   long h1 = Map1->Rows;
   long w2 = (Map2->BytesPerRow) << 3;
   long h2 = Map2->Rows;
 
   Map2->Planes[0] = VScreen->BitMap.Planes[i];
   Map1->Planes[0] = AllocRaster(w1,h1);
   if (Map1->Planes[0])
   {
      BltClear(Map1->Planes[0],RASSIZE(w1,h1),0L);
      BltBitMap(Map2,0L,0L,Map1,0L,0L,MIN(w1,w2),MIN(h1,h2),BLT_COPY,0xFF,NULL);
      BltClear(junk,8L,0L);  /* synchronize with BltBitMap */
      VScreen->BitMap.Planes[i] = Map1->Planes[0];
      FreeRaster(Map2->Planes[0],w2,h2);
      error = FALSE;
   }
   return(error);
}
 
 
/*
 *  GetBitMap()
 *
 *  GetBitMap allocates and copies a new, larger bitmap for the virtual
 *  screen.  It does so one plane at a time, however, in order avoid having
 *  the complete old screen and the complete new screen in memory at the
 *  same time.  Two temporary bitmaps are used to perform the single-plane
 *  copies.  The junk memory is used to synchronize the BltBitMap calls (see
 *  GetPlane() above).
 *
 *  For each bitplane in the screen, get a new plane of the proper
 *  size.  If the allocation fails, try to clean up (it's usually too
 *  late, however).
 *
 *  Modify the screen's bitmap to reflect the changed size.
 *  Free the junk space.
 */
 
static void GetBitMap()
{
   struct BitMap MyBitMap;
   struct BitMap theBitMap;
   int i;
   UBYTE *junk;
 
   junk = AllocMem(8L,MEMF_CHIP);
   InitBitMap(&MyBitMap,1L,ScreenWidth,ScreenHeight);
   InitBitMap(&theBitMap,1L,VAR(OldWidth),VAR(OldHeight));
   for (i=0; i<OldDepth; i++)
   {
      if (GetPlane(i,&MyBitMap,&theBitMap,junk))
      {
         for(i--; i; i--)
         {
            if (GetPlane(i,&theBitMap,&MyBitMap,junk))
               DoExit("Bail Out!  Serious Trouble Restoring Bit Planes!");
         }
         FreeMem(junk,8L);
         DoExit("Can't Get Memory for Large Bit Planes");
      }
   }
   VScreen->BitMap.BytesPerRow = MyBitMap.BytesPerRow;
   VScreen->BitMap.Rows = MyBitMap.Rows;
   FreeMem(junk,8L);
}
 
 
/*
 *  FreeBitMap()
 *
 *  Similar to GetBitMap, except that FreeBitMap trys to allocate a bitmap
 *  the size of the original screen.
 */
 
static void FreeBitMap()
{
   struct BitMap MyBitMap;
   struct BitMap theBitMap;
   short i;
   UBYTE *junk;
 
   junk = AllocMem(8L,MEMF_CHIP);
   InitBitMap(&MyBitMap,1L,ScreenWidth,ScreenHeight);
   InitBitMap(&theBitMap,1L,VAR(OldWidth),VAR(OldHeight));
   for (i=0; i<OldDepth; i++)
   {
      if (GetPlane(i,&theBitMap,&MyBitMap,junk))
      {
         FreeMem(junk,8L);
         DoExit("Help!  Failed to Restore Bit Planes!");
      }
   }
   VScreen->BitMap.BytesPerRow = theBitMap.BytesPerRow;
   VScreen->BitMap.Rows = theBitMap.Rows;
   FreeMem(junk,8L);
}
 
 
/*
 *  SetClipRects()
 *
 *  Since the screen size is changing, we need to modify the ClipRects
 *  for the menubar's Layer.  This allows the layer to update itself
 *  properly.
 *
 *  Set the menubar bounds-rectangle size.
 *  For each ClipRect in the menubar layer,
 *    If the bounds-rectangle's right edge is at the edge of the old screen
 *      then set it to be the edge of the new screen.
 */
 
static void SetClipRects(OldX,NewX)
WORD OldX,NewX;
{
   struct ClipRect *theClipRect = VScreen->BarLayer->ClipRect;
 
   OldX--; NewX--;
   VScreen->BarLayer->bounds.MaxX = NewX;
   while (theClipRect)
   {
      if (theClipRect->bounds.MaxX == OldX)
         theClipRect->bounds.MaxX = NewX;
      theClipRect = theClipRect->Next;
   }
}
 
 
/*
 *  EnlargeScreen()
 *
 *  Store the current state of the screen and change what needs to be
 *  changed in order to make it bigger.
 *
 *  Open the Layers Library so that we can call LockLayers().
 *  Lock the Layers for the screen.  The Forbid() may not be necessary.
 *
 *  Get the HIRES and LACE flags for the screen.  Set up the Shift values
 *  needed to convert the screen's local coordinates to actual display
 *  coordinates (always considered 640 x 400 mode).
 *
 *  Get the pointers to the RxOffset and RyOffset variables of the RasInfo
 *  for the ViewPort of the virtual Screen.  These are what tell the
 *  graphics library which part of the bitmap to display.  These are what
 *  make it possible to scroll the screen quickly and easily.  Without them,
 *  vScreen would be impossible, but luckily, the graphics library knows how
 *  to use them.  Unfortunately, Intuition does not, so we have to bend
 *  over backwards to fool intuition.  See vScreen-Handler.c for details.
 *
 *  Get the old width and height of the screen, and the depth.
 *  Get the new, larger bitplanes for the screen.
 *
 *  Set the screen width and height, and repair the ClipRects in the
 *  menubar Layer.
 *
 *  Get the old MaxDisplay values.
 *
 *  Call the handler's SetVScreen() routine (it sets the MaxDisplay
 *  values, the Max and Min Mouse values, and some other stuff),
 *  and the FixView() routine, which calls MakeVPort() and MrgCop() to
 *  incorporate the screen changes into the Intuition View structure.
 *  Finally, load the new View so taht the larger screen will be displayed.
 *
 *  Unlock the layers and close the library.
 *
 *  Call ShowTitle, so that the menubar layer will be updated (so that
 *  it is as wide as the new screen).
 */
 
void EnlargeScreen()
{
   CheckLibOpen(&LayersBase,"layers.library",LAYERS_REV);
   LockLayers(&(VScreen->LayerInfo));
   Forbid();
 
   VAR(HiResScreen) = (VScreen->ViewPort.Modes & HIRES);
   VAR(LaceScreen)  = (VScreen->ViewPort.Modes & LACE);
   VAR(HiResShift) = (VAR(HiResScreen))? 0: 1;
   VAR(LaceShift)  = (VAR(LaceScreen))? 0: 1;
 
   VAR(RxOffset) = &(VScreen->ViewPort.RasInfo->RxOffset);
   VAR(RyOffset) = &(VScreen->ViewPort.RasInfo->RyOffset);
   VAR(RxOffset2) = (*(VAR(RxOffset))) << VAR(HiResShift);
   VAR(RyOffset2) = (*(VAR(RyOffset))) << VAR(LaceShift);
 
   VAR(OldWidth)  = VScreen->Width;
   VAR(OldHeight) = VScreen->Height;
   OldDepth = VScreen->BitMap.Depth;
   GetBitMap();
 
   VScreen->Width  = ScreenWidth;
   VScreen->Height = ScreenHeight;
   SetClipRects(VAR(OldWidth),ScreenWidth);
 
   VAR(OldMaxDH) = IntuitionBase->MaxDisplayHeight;
   VAR(OldMaxDR) = IntuitionBase->MaxDisplayRow;
   VAR(OldMaxDW) = IntuitionBase->MaxDisplayWidth;
 
   VAR(SetVScreen)();
   VAR(FixView)(TRUE);
   LoadView(&(IntuitionBase->ViewLord));
 
   Permit();
   UnlockLayers(&(VScreen->LayerInfo));
   CloseLibrary(LayersBase); LayersBase = NULL;
 
   ShowTitle(VScreen,(VScreen->Flags & SHOWTITLE)? TRUE: FALSE);
}
 
 
/*
 *  RestoreScreen()
 *
 *  If the screen still exists (i.e.,it was not closed while vScreen was
 *  running), then check the windows to be sure that they all will fit on
 *  the original-sized screen.
 *  Open the Layers Library, and lock the screen layers.
 *  Reset the old screen size, and set the menubar ClipRects to the old size.
 *  Set the offsets to zero, and call the Handler's ResetVScreen() routine
 *  (which resets Intuitions MaxDisplay and Min and Max Mouse fields).
 *  Get the screen depth and restore the old bitmap.
 *  Finally, unlock the screen, and remake the Intuition display so that
 *  the new sized screen is displayed.
 *  update the title bar so that it is the right size.
 */
 
void RestoreScreen()
{
   if (VScreen)
   {
      CheckWindows(VScreen,VAR(OldWidth),VAR(OldHeight));
 
      CheckLibOpen(&LayersBase,"layers.library",LAYERS_REV);
      LockLayers(&(VScreen->LayerInfo));
      Forbid();
 
      VScreen->Width  = VAR(OldWidth);
      VScreen->Height = VAR(OldHeight);
      SetClipRects(ScreenWidth,VAR(OldWidth));
 
      *(VAR(RxOffset)) = 0;
      *(VAR(RyOffset)) = 0;
      VAR(ResetVScreen)();
 
      OldDepth = VScreen->BitMap.Depth;
      FreeBitMap();
 
      Permit();
      UnlockLayers(&(VScreen->LayerInfo));
      CloseLibrary(LayersBase); LayersBase = NULL;
 
      RemakeDisplay();
      ShowTitle(VScreen,(VScreen->Flags & SHOWTITLE)? TRUE: FALSE);
   }
}
 
 
/*
 *  SetVariables()
 *
 *  Store that vScreenData pointer in the MsgPort structure so we can look
 *  it up later (in order to remove the handler).  Save the library pointers
 *  so that the handler can use them, and save the pointer to the virtual
 *  screen abd its new width and height.
 */
 
void SetVariables(NamedPort)
struct MsgPort *NamedPort;
{
   NamedPort->mp_SigTask = (struct Task *) vScreenData;
   VAR(IntuitionBase) = IntuitionBase;
   VAR(GfxBase)       = GfxBase;
   VAR(VScreen)       = VScreen;
   VAR(ScreenWidth)   = ScreenWidth;
   VAR(ScreenHeight)  = ScreenHeight;
}
 
 
/*
 *  GetVariables()
 *
 *  Retrieve the vScreenData pointer from the MsgPort were we stored it
 *  earlier.  Get back the library pointers so that we can use them and close
 *  them.  Get back the pointer to the virtual screen and its new width
 *  and height.
 */
 
void GetVariables(NamedPort)
struct MsgPort *NamedPort;
{
   vScreenData   = (struct vScreenInfo *) (NamedPort->mp_SigTask);
   IntuitionBase = VAR(IntuitionBase);
   GfxBase       = VAR(GfxBase);
   VScreen       = VAR(VScreen);
   ScreenWidth   = VAR(ScreenWidth);
   ScreenHeight  = VAR(ScreenHeight);
}
SHAR_EOF
#       End of shell archive
exit 0
