/* * 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 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 nor 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); } } } }