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