|
|
/******************************Module*Header*******************************\
* Module Name: multi.c * * Supports multiple display boards as a single virtual desktop. * * This is implemented by presenting to GDI a single large virtual * display and adding a layer between GDI and the driver's Drv functions. * For the most part, the rest of the driver outside of multi.c doesn't * have to change much, subject to the requirements below. * * This implementation requires that each board have the same virtual * resolution and colour depth (e.g., all be running 1024x768x256), and * that the boards be arranged in a rectangular configuration. * * Each board has its own PDEV, and completely manages its surface * independently, down to glyph and bitmap caching. The Mul * routine intercepts the DDI call, and for each board dispatches * a Drv call with the appropriate PDEV and clip object modifications. * * The following support in the main driver is required: * * 1) The driver should be able to handle a per-surface offset. For * example, if two 1024x768 displays are pasted side-by-side, the * right board will get drawing operations in the range (1024, 768) - * (2048, 768). The driver has a (-1024, 0) surface offset to convert * the actual drawing on the right board to the expected (0, 0) - * (1024, 768). * * The current driver already uses this notion to support device-format * bitmaps drawn in off-screen memory. * * Another option would be to handle the surface offsets in this layer, * but then all parameters including clip objects, paths and glyph * enumerations would have to be adjusted here as well. * * 2) The main driver must be able to share realized pattern information * between board instances. That is, with the current DDI specification * GDI entirely handles brush memory allocation via pvAllocRBrush, * and the driver doesn't get notified when the brush is destroyed, so * the driver has to keep all information about the brush for all the * boards in the one brush realization. This isn't too onerous. * * Problems: * * 1) CompatibleBitmaps would have to be shared between board instances. * This becomes a problem when the bitmaps are kept by the driver in off- * screen memory. * * Copyright (c) 1993-1996 Microsoft Corporation * Copyright (c) 1993-1996 Matrox Electronic Systems, Ltd. \**************************************************************************/
#include "precomp.h"
#if MULTI_BOARDS
#define GO_BOARD(pmdev, pmb) { (pmdev)->pmbCurrent = (pmb); }
#define MAKE_BOARD_CURRENT(pmdev, pmb) \
{ \ ULONG ReturnedDataLength; \ \ if (EngDeviceIoControl((pmdev)->hDriver, \ IOCTL_VIDEO_MTX_MAKE_BOARD_CURRENT, \ &(pmb)->iBoard, /* Input */ \ sizeof(LONG), \ NULL, /* Output */ \ 0, \ &ReturnedDataLength)) \ { \ RIP("Failed MTX_MAKE_BOARD_CURRENT"); \ } \ }
struct _MULTI_BOARD;
typedef struct _MULTI_BOARD MULTI_BOARD; /* mb */
struct _MULTI_BOARD { LONG iBoard; // Sequentially allocated board number
RECTL rcl; // Board's coordinates
MULTI_BOARD* pmbNext; // For traversing the entire list of boards
MULTI_BOARD* pmbLeft; // For traversing by direction
MULTI_BOARD* pmbUp; MULTI_BOARD* pmbRight; MULTI_BOARD* pmbDown;
PDEV* ppdev; // Pointer to the board's PDEV
SURFOBJ* pso; // Surface representing the board
HSURF hsurf; // Handle to surface
}; /* mb, pmb */
typedef struct _MDEV { MULTI_BOARD* pmb; // Where to start enumerating
MULTI_BOARD* pmbUpperLeft; // Board in upper-left corner
MULTI_BOARD* pmbUpperRight; MULTI_BOARD* pmbLowerLeft; MULTI_BOARD* pmbLowerRight; LONG cxBoards; // Number of boards per row
LONG cyBoards; // Number of boards per column
LONG cBoards; // Total number of boards
MULTI_BOARD* pmbPointer; // Board where cursor is currently visible
MULTI_BOARD* pmbCurrent; // Currently selected board (needed for
// DrvRealizeBrush)
HANDLE hDriver; // Our handle to miniport
HDEV hdev; // Handle that GDI knows us by
HSURF hsurf; // Handle to our virtual surface
CLIPOBJ* pco; // A temporary CLIPOBJ that we can modify
ULONG iBitmapFormat; // Current colour depth
FLONG flHooks; // Those functions that the main driver
// is hooking
ULONG ulMode; // 'Super' mode for each screen
} MDEV; /* mdev, pmdev */
typedef struct _PVCONSUMER { PVOID pvConsumer; } PVCONSUMER;
typedef struct _FONT_CONSUMER { LONG cConsumers; // Total number of boards
PVCONSUMER apvc[MAX_BOARDS]; // Array of structures cConsumers in length
} FONT_CONSUMER; /* fc, pfc */
typedef struct _BITBLTDATA { RECTL rclBounds; MDEV* pmdev;
SURFOBJ* psoDst; SURFOBJ* psoSrc; SURFOBJ* psoMask; CLIPOBJ* pco; XLATEOBJ* pxlo; RECTL* prclDst; POINTL* pptlSrc; POINTL* pptlMask; BRUSHOBJ* pbo; POINTL* pptlBrush; ROP4 rop4; } BITBLTDATA; /* bb, pbb */
/******************************Public*Routine******************************\
* BOOL bFindBoard * * Returns in ppmb a pointer to the board containing the upper-left * corner of prcl. * * Returns TRUE if prcl is entirely containing on one board; FALSE if * prcl spans multiple boards. * \**************************************************************************/
BOOL bFindBoard(MDEV* pmdev, RECTL* prcl, MULTI_BOARD** ppmb) { MULTI_BOARD* pmb;
pmb = pmdev->pmbUpperLeft;
// It should never happen that GDI will give us a call whose bounds
// don't intersect the virtual screen. But so that we don't crash
// should it ever happen, we'll return an intersection with the first
// board -- we can assume GDI at least said the clipping was non-
// trivial, in which case that board's display routine will realize
// nothing had to be done:
*ppmb = pmb;
// First find the row:
while (prcl->top >= pmb->rcl.bottom) { pmb = pmb->pmbDown; if (pmb == NULL) return(FALSE); // This is a case where the bounds doesn't
// intercept the virtual screen
}
// Now find the column:
while (prcl->left >= pmb->rcl.right) { pmb = pmb->pmbRight; if (pmb == NULL) return(FALSE); // This is a case where the bounds doesn't
// intercept the virtual screen
}
// So we found the first board:
*ppmb = pmb;
return(prcl->right <= pmb->rcl.right && prcl->bottom <= pmb->rcl.bottom); }
/******************************Public*Routine******************************\
* BOOL bNextBoard * * Returns in ppmb a pointer to the next board after intersecting prcl, going * left-to-right then top-to-bottom. * * Returns TRUE if all boards intersecting prcl have been enumerated; FALSE * if there are more boards. * \**************************************************************************/
BOOL bNextBoard(RECTL* prcl, MULTI_BOARD** ppmb) { MULTI_BOARD* pmb;
pmb = *ppmb;
// We'll do all the boards in a row first, remembering that the
// bounds rectangle can extend past the end of our virtual screen:
if ((prcl->right > pmb->rcl.right) && (pmb->pmbRight != NULL)) { *ppmb = pmb->pmbRight; return(TRUE); }
// Go to next row if need be, starting at the rcl.left:
if ((prcl->bottom > pmb->rcl.bottom) && (pmb->pmbDown != NULL)) { pmb = pmb->pmbDown; while ((prcl->left < pmb->rcl.left) && (pmb->pmbLeft != NULL)) { pmb = pmb->pmbLeft; } *ppmb = pmb; return(TRUE); }
return(FALSE); }
/******************************Public*Routine******************************\
* VOID vIntersect * * Returns in prclOut the intersection of rectangles prcl1 and prcl2. * \**************************************************************************/
VOID vIntersect(RECTL* prcl1, RECTL* prcl2, RECTL* prclOut) { prclOut->left = max(prcl1->left, prcl2->left); prclOut->top = max(prcl1->top, prcl2->top); prclOut->right = min(prcl1->right, prcl2->right); prclOut->bottom = min(prcl1->bottom, prcl2->bottom); }
/******************************Public*Routine******************************\
* BOOL bBoardCopy * * Given the BitBlt parameters in pbb, bitblt's the part of the rectangle * on the pmbSrc board that must bitblt'ed to the pmbDst board. Bails * out quickly if nothing actually has to be copied. * * Will do a screen-to-screen blt if pmbSrc and pmbDst are the same board; * otherwise it uses the psoTmp bitmap as temporary storage for transferring * between the two boards. * * NOTE: If your hardware allows you to have all the frame buffers mapped * into memory simultaneously, you can avoid the 'psoTmp' bitmap * allocation and extra copy! * \**************************************************************************/
BOOL bBoardCopy( BITBLTDATA* pbb, SURFOBJ* psoTmp, MULTI_BOARD* pmbDst, MULTI_BOARD* pmbSrc) { BOOL b; RECTL rclDst; LONG dx; LONG dy; RECTL rclTmp; POINTL ptlSrc;
// If there's really no source board, we're guaranteed not to
// have to copy anything from it:
if (pmbSrc == NULL) return(TRUE);
dx = pbb->prclDst->left - pbb->pptlSrc->x; dy = pbb->prclDst->top - pbb->pptlSrc->y;
// Pretend we're going to copy the entire source board's screen.
// rclDst would be the destination rectangle:
rclDst.left = pmbSrc->rcl.left + dx; rclDst.right = pmbSrc->rcl.right + dx; rclDst.top = pmbSrc->rcl.top + dy; rclDst.bottom = pmbSrc->rcl.bottom + dy;
// We really want to copy only the part that overlaps the
// destination board's screen:
vIntersect(&pmbDst->rcl, &rclDst, &rclDst);
// Plus we really only want to copy anything to what is contained
// in the original destination rectangle:
vIntersect(&pbb->rclBounds, &rclDst, &rclDst);
// rclDst is now the destination rectangle for our call. We'll
// need a temporary bitmap for copying, so compute its extents:
rclTmp.left = 0; rclTmp.top = 0; rclTmp.right = rclDst.right - rclDst.left; rclTmp.bottom = rclDst.bottom - rclDst.top;
// If it's empty, we're outta here:
if ((rclTmp.right <= 0) || (rclTmp.bottom <= 0)) return(TRUE);
if (pmbDst == pmbSrc) { // If the source and destination are the same board, we don't
// need a temporary bitmap:
psoTmp = pmbSrc->pso; ptlSrc = *pbb->pptlSrc; } else { ASSERTDD(psoTmp != NULL, "Need non-null bitmap"); ASSERTDD(psoTmp->sizlBitmap.cx >= rclTmp.right, "Bitmap too small in x"); ASSERTDD(psoTmp->sizlBitmap.cy >= rclTmp.bottom, "Bitmap too small in y");
// Figure out the upper-left source corner corresponding to our
// upper-left destination corner:
ptlSrc.x = rclDst.left - dx; ptlSrc.y = rclDst.top - dy;
// Copy the rectangle from the source to the temporary bitmap:
GO_BOARD(pbb->pmdev, pmbSrc); b = DrvCopyBits(psoTmp, pmbSrc->pso, NULL, NULL, &rclTmp, &ptlSrc);
// Then get ready to do the copy from the temporary bitmap to
// the destination:
ptlSrc.x = pbb->prclDst->left - rclDst.left; ptlSrc.y = pbb->prclDst->top - rclDst.top; }
pbb->pco->rclBounds = rclDst; GO_BOARD(pbb->pmdev, pmbDst); b &= DrvBitBlt(pmbDst->pso, psoTmp, pbb->psoMask, pbb->pco, pbb->pxlo, pbb->prclDst, &ptlSrc, pbb->pptlMask, pbb->pbo, pbb->pptlBrush, pbb->rop4);
return(b); }
/******************************Public*Routine******************************\
* BOOL bBitBltBetweenBoards * * Handles screen-to-screen blts across multiple boards. * \**************************************************************************/
BOOL bBitBltBetweenBoards( SURFOBJ* psoDst, SURFOBJ* psoSrc, SURFOBJ* psoMask, CLIPOBJ* pco, XLATEOBJ* pxlo, RECTL* prclDst, POINTL* pptlSrc, POINTL* pptlMask, BRUSHOBJ* pbo, POINTL* pptlBrush, ROP4 rop4, RECTL* prclUnion, // Rectangular union of source and destination
MULTI_BOARD* pmbUnion) // Board containing upper-left corner of prclUnion
{ BOOL b = TRUE; BITBLTDATA bb; RECTL rclOriginalBounds; SIZEL sizlBoard; SIZEL sizlDst; SIZEL sizl; MULTI_BOARD* pmbSrc; MULTI_BOARD* pmbDst; LONG dx; LONG dy; RECTL rclStart;
SURFOBJ* pso0 = NULL; // Initialize these first off in case we
SURFOBJ* pso1 = NULL; // early-out
SURFOBJ* pso2 = NULL; SURFOBJ* pso3 = NULL; HSURF hsurf0 = 0; HSURF hsurf1 = 0;
bb.pmdev = (MDEV*) psoDst->dhpdev; bb.psoDst = psoDst; bb.psoSrc = psoSrc; bb.psoMask = psoMask; bb.pxlo = pxlo; bb.prclDst = prclDst; bb.pptlSrc = pptlSrc; bb.pptlMask = pptlMask; bb.pbo = pbo; bb.pptlBrush = pptlBrush; bb.rop4 = rop4; bb.pco = pco; if ((pco == NULL) || (pco->iDComplexity == DC_TRIVIAL)) bb.pco = bb.pmdev->pco;
vIntersect(&bb.pco->rclBounds, prclDst, &bb.rclBounds); rclOriginalBounds = bb.pco->rclBounds;
sizlDst.cx = bb.rclBounds.right - bb.rclBounds.left; sizlDst.cy = bb.rclBounds.bottom - bb.rclBounds.top;
// This really should never happen, but we'll be paranoid:
if ((sizlDst.cx <= 0) || (sizlDst.cy <= 0)) return(TRUE);
// Compute delta from source to destination:
dx = prclDst->left - pptlSrc->x; dy = prclDst->top - pptlSrc->y;
// Figure out the size of a board:
sizlBoard.cx = bb.pmdev->pmbUpperLeft->rcl.right; sizlBoard.cy = bb.pmdev->pmbUpperLeft->rcl.bottom;
// We use temporary bitmaps as intermediaries for copying from one
// board to another. Note that it is much more efficient to allocate
// on the fly, rather than keeping a dedicated bitmap around that
// would have to be swapped in and out.
// When the destination is close to the source, we can accomplish
// most of the blt using screen-to-screen copies, and will need
// only two small temporary bitmaps to temporarily hold the bits
// that must be transferred from one board to another:
if ((abs(dx) < (sizlBoard.cx >> 1)) && (abs(dy) < (sizlBoard.cy >> 1))) { // Create a temporary bitmap for the horizontal delta only if
// the blt actually spans boards in the x-direction:
if ((dx != 0) && (prclUnion->right > pmbUnion->rcl.right)) { sizl.cx = min(sizlDst.cx, abs(dx)); sizl.cy = min(sizlDst.cy, sizlBoard.cy - abs(dy));
hsurf0 = (HSURF) EngCreateBitmap(sizl, 0, bb.pmdev->iBitmapFormat, 0, NULL); pso1 = EngLockSurface(hsurf0); if (pso1 == NULL) return(FALSE);
// Can use same temporary bitmap for section '3':
pso3 = pso1; }
// Similarly for the vertical delta:
if ((dy != 0) && (prclUnion->bottom > pmbUnion->rcl.bottom)) { sizl.cx = min(sizlDst.cx, sizlBoard.cx - abs(dx)); sizl.cy = min(sizlDst.cy, abs(dy));
hsurf1 = (HSURF) EngCreateBitmap(sizl, 0, bb.pmdev->iBitmapFormat, 0, NULL); pso2 = EngLockSurface(hsurf1); if (pso2 == NULL) { b = FALSE; goto OuttaHere; } } } else { // Make the bitmap the size of a board, or the size of the
// destination rectangle, which ever is smaller:
sizl.cx = min(sizlDst.cx, sizlBoard.cx); sizl.cy = min(sizlDst.cy, sizlBoard.cy);
hsurf0 = (HSURF) EngCreateBitmap(sizl, 0, bb.pmdev->iBitmapFormat, 0, NULL); pso0 = EngLockSurface(hsurf0); if (pso0 == NULL) return(FALSE);
pso1 = pso0; pso2 = pso0; pso3 = pso0; }
if ((dx <= 0) && (dy <= 0)) { // Move the rectangle up and to the left:
// Find the board containing the upper-left corner of the destination:
pmbDst = bb.pmdev->pmbUpperLeft; while (pmbDst->rcl.right <= bb.rclBounds.left) pmbDst = pmbDst->pmbRight; while (pmbDst->rcl.bottom <= bb.rclBounds.top) pmbDst = pmbDst->pmbDown;
// Find the upper-left of the four source boards' rectangles which
// can potentially overlap our destination board's rectangle:
rclStart.left = pmbDst->rcl.left - dx; rclStart.top = pmbDst->rcl.top - dy;
pmbSrc = pmbDst; while (pmbSrc->rcl.right <= rclStart.left) pmbSrc = pmbSrc->pmbRight; while (pmbSrc->rcl.bottom <= rclStart.top) pmbSrc = pmbSrc->pmbDown;
while (TRUE) { b &= bBoardCopy(&bb, pso0, pmbDst, pmbSrc); b &= bBoardCopy(&bb, pso1, pmbDst, pmbSrc->pmbRight); b &= bBoardCopy(&bb, pso2, pmbDst, pmbSrc->pmbDown); if (pmbSrc->pmbDown != NULL) b &= bBoardCopy(&bb, pso3, pmbDst, pmbSrc->pmbDown->pmbRight);
if (pmbDst->rcl.right < bb.rclBounds.right) { // Move right in the row of boards:
pmbDst = pmbDst->pmbRight; pmbSrc = pmbSrc->pmbRight; } else { // We may be all done:
if (pmbDst->rcl.bottom >= bb.rclBounds.bottom) break;
// Nope, have to go down to left side of next lower row:
while (pmbDst->rcl.left > bb.rclBounds.left) { pmbDst = pmbDst->pmbLeft; pmbSrc = pmbSrc->pmbLeft; }
pmbDst = pmbDst->pmbDown; pmbSrc = pmbSrc->pmbDown; } } } else if ((dx >= 0) && (dy >= 0)) { // Move the rectangle down and to the right:
// Find the board containing the lower-right corner of the destination:
pmbDst = bb.pmdev->pmbLowerRight; while (pmbDst->rcl.left >= bb.rclBounds.right) pmbDst = pmbDst->pmbLeft; while (pmbDst->rcl.top >= bb.rclBounds.bottom) pmbDst = pmbDst->pmbUp;
// Find the lower-right of the four source boards' rectangles which
// can potentially overlap our destination board's rectangle:
rclStart.right = pmbDst->rcl.right - dx; rclStart.bottom = pmbDst->rcl.bottom - dy;
pmbSrc = pmbDst; while (pmbSrc->rcl.left >= rclStart.right) pmbSrc = pmbSrc->pmbLeft; while (pmbSrc->rcl.top >= rclStart.bottom) pmbSrc = pmbSrc->pmbUp;
while (TRUE) { b &= bBoardCopy(&bb, pso0, pmbDst, pmbSrc); b &= bBoardCopy(&bb, pso1, pmbDst, pmbSrc->pmbLeft); b &= bBoardCopy(&bb, pso2, pmbDst, pmbSrc->pmbUp); if (pmbSrc->pmbUp != NULL) b &= bBoardCopy(&bb, pso3, pmbDst, pmbSrc->pmbUp->pmbLeft);
if (pmbDst->rcl.left > bb.rclBounds.left) { // Move left in the row of boards:
pmbDst = pmbDst->pmbLeft; pmbSrc = pmbSrc->pmbLeft; } else { // We may be all done:
if (pmbDst->rcl.top <= bb.rclBounds.top) break;
// Nope, have to go up to right side of next upper row:
while (pmbDst->rcl.right < bb.rclBounds.right) { pmbDst = pmbDst->pmbRight; pmbSrc = pmbSrc->pmbRight; }
pmbDst = pmbDst->pmbUp; pmbSrc = pmbSrc->pmbUp; } } } else if ((dx >= 0) && (dy <= 0)) { // Move the rectangle up and to the right:
// Find the board containing the upper-right corner of the destination:
pmbDst = bb.pmdev->pmbUpperRight; while (pmbDst->rcl.left >= bb.rclBounds.right) pmbDst = pmbDst->pmbLeft; while (pmbDst->rcl.bottom <= bb.rclBounds.top) pmbDst = pmbDst->pmbDown;
// Find the upper-right of the four source boards' rectangles which
// can potentially overlap our destination board's rectangle:
rclStart.right = pmbDst->rcl.right - dx; rclStart.top = pmbDst->rcl.top - dy;
pmbSrc = pmbDst; while (pmbSrc->rcl.left >= rclStart.right) pmbSrc = pmbSrc->pmbLeft; while (pmbSrc->rcl.bottom <= rclStart.top) pmbSrc = pmbSrc->pmbDown;
while (TRUE) { b &= bBoardCopy(&bb, pso0, pmbDst, pmbSrc); b &= bBoardCopy(&bb, pso1, pmbDst, pmbSrc->pmbLeft); b &= bBoardCopy(&bb, pso2, pmbDst, pmbSrc->pmbDown); if (pmbSrc->pmbDown != NULL) b &= bBoardCopy(&bb, pso3, pmbDst, pmbSrc->pmbDown->pmbLeft);
if (pmbDst->rcl.left > bb.rclBounds.left) { // Move left in the row of boards:
pmbDst = pmbDst->pmbLeft; pmbSrc = pmbSrc->pmbLeft; } else { // We may be all done:
if (pmbDst->rcl.bottom >= bb.rclBounds.bottom) break;
// Nope, have to go down to right side of next lower row:
while (pmbDst->rcl.right < bb.rclBounds.right) { pmbDst = pmbDst->pmbRight; pmbSrc = pmbSrc->pmbRight; }
pmbDst = pmbDst->pmbDown; pmbSrc = pmbSrc->pmbDown; } } } else { // Move the rectangle down and to the left:
// Find the board containing the lower-left corner of the destination:
pmbDst = bb.pmdev->pmbLowerLeft; while (pmbDst->rcl.right <= bb.rclBounds.left) pmbDst = pmbDst->pmbRight; while (pmbDst->rcl.top >= bb.rclBounds.bottom) pmbDst = pmbDst->pmbUp;
// Find the lower-left of the four source boards' rectangles which
// can potentially overlap our destination board's rectangle:
rclStart.left = pmbDst->rcl.left - dx; rclStart.bottom = pmbDst->rcl.bottom - dy;
pmbSrc = pmbDst; while (pmbSrc->rcl.right <= rclStart.left) pmbSrc = pmbSrc->pmbRight; while (pmbSrc->rcl.top >= rclStart.bottom) pmbSrc = pmbSrc->pmbUp;
while (TRUE) { b &= bBoardCopy(&bb, pso0, pmbDst, pmbSrc); b &= bBoardCopy(&bb, pso1, pmbDst, pmbSrc->pmbRight); b &= bBoardCopy(&bb, pso2, pmbDst, pmbSrc->pmbUp); if (pmbSrc->pmbUp != NULL) b &= bBoardCopy(&bb, pso3, pmbDst, pmbSrc->pmbUp->pmbRight);
if (pmbDst->rcl.right < bb.rclBounds.right) { // Move right in the row of boards:
pmbDst = pmbDst->pmbRight; pmbSrc = pmbSrc->pmbRight; } else { // We may be all done:
if (pmbDst->rcl.top <= bb.rclBounds.top) break;
// Nope, have to up down to left side of next upper row:
while (pmbDst->rcl.left > bb.rclBounds.left) { pmbDst = pmbDst->pmbLeft; pmbSrc = pmbSrc->pmbLeft; }
pmbDst = pmbDst->pmbUp; pmbSrc = pmbSrc->pmbUp; } } }
bb.pco->rclBounds = rclOriginalBounds;
OuttaHere:
// In one case, pso0 == pso1 == pso2 == pso3, and we don't want to
// unlock the same surface twice:
if (pso1 != pso2) EngUnlockSurface(pso1);
EngUnlockSurface(pso2); EngDeleteSurface(hsurf0); EngDeleteSurface(hsurf1);
return(b); }
/******************************Public*Routine******************************\
* ULONG MulGetModes * \**************************************************************************/
ULONG MulGetModes( HANDLE hDriver, ULONG cjSize, DEVMODEW* pdm) { ULONG ulRet;
ulRet = DrvGetModes(hDriver, cjSize, pdm);
return(ulRet); }
/******************************Public*Routine******************************\
* BOOL bQueryMultiBoards * * Performs the minimal initialization required to ask the miniport * if we'll be supporting multiple boards. * \**************************************************************************/
BOOL bQueryMultiBoards( HANDLE hDriver, // Input
DEVMODEW* pdm, // Input and Output -- updates some fields
SIZEL* pszlBoardArray, // Output
SIZEL* pszlScreen, // Output
ULONG* pulMode) // Output
{ VIDEO_MODE_INFORMATION VideoModeInformation; ULONG ulBoardId; ULONG ReturnedDataLength; SIZEL szlBoardArray; MULTI_BOARD* pmb; DWORD cModes; PVIDEO_MODE_INFORMATION pVideoBuffer; PVIDEO_MODE_INFORMATION pVideoTemp; DWORD cbModeSize;
// Figure out the requested virtual desktop size:
if (!bSelectMode(hDriver, pdm, &VideoModeInformation, &ulBoardId)) { DISPDBG((0, "bQueryMultiBoards -- Failed bSelectMode")); goto ReturnFailure0; }
*pulMode = VideoModeInformation.ModeIndex;
// Call the miniport via a public IOCTL to set the graphics mode.
// The MGA miniport requires that we do this before calling
// MTX_QUERY_BOARD_ARRAY:
if (EngDeviceIoControl(hDriver, IOCTL_VIDEO_SET_CURRENT_MODE, &VideoModeInformation.ModeIndex, // Input
sizeof(DWORD), NULL, // Output
0, &ReturnedDataLength)) { DISPDBG((0, "bQueryMultiBoards - Failed VIDEO_SET_CURRENT_MODE")); goto ReturnFailure0; }
// Now that we've set the mode, we can query the MGA miniport
// via a private IOCTL to find out how many boards there will be.
if (EngDeviceIoControl(hDriver, IOCTL_VIDEO_MTX_QUERY_BOARD_ARRAY, NULL, // Input
0, pszlBoardArray, // Output
sizeof(SIZEL), &ReturnedDataLength)) { DISPDBG((0, "bQueryMultiBoards -- Failed MTX_QUERY_BOARD_ARRAY")); goto ReturnFailure0; }
// Convert the devmode so that it is no longer a request for
// the entire virtual desktop dimension, but is now a request
// for the resolution of each board:
pdm->dmPelsWidth /= pszlBoardArray->cx; pdm->dmPelsHeight /= pszlBoardArray->cy;
// Remember some stuff about the mode:
pszlScreen->cx = VideoModeInformation.VisScreenWidth / pszlBoardArray->cx; pszlScreen->cy = VideoModeInformation.VisScreenHeight / pszlBoardArray->cy;
DISPDBG((1, "Board array: %li x %li", pszlBoardArray->cx, pszlBoardArray->cy));
return(TRUE);
ReturnFailure0:
return(FALSE); }
/******************************Public*Routine******************************\
* BOOL bInitializeGeometry * * Initializes all our multi-board data structures describing the * geometry of the multiple board configuration. * \**************************************************************************/
BOOL bInitializeGeometry( MDEV* pmdev, LONG cxBoards, LONG cyBoards, LONG cxScreen, LONG cyScreen) { LONG cBoards; LONG i; LONG x; LONG y; MULTI_BOARD* apmb; MULTI_BOARD* pmb;
// Create all of our multi-board structures:
cBoards = cxBoards * cyBoards;
pmdev->cBoards = cBoards; pmdev->cxBoards = cxBoards; pmdev->cyBoards = cyBoards;
// Allocate and initialize the linked list of structures for every
// board:
apmb = EngAllocMem(FL_ZERO_MEMORY, cBoards * sizeof(MULTI_BOARD), ALLOC_TAG); if (apmb == NULL) { DISPDBG((0, "bInitializeGeometry -- Failed EngAllocMem")); goto ReturnFailure0; }
for (pmb = apmb + 1, i = 1; i < cBoards; pmb++, i++) { // The first board's 'iBoard' is already set to zero...
(pmb)->iBoard = i; (pmb - 1)->pmbNext = pmb; }
pmdev->pmb = apmb; pmdev->pmbUpperLeft = &apmb[0]; pmdev->pmbUpperRight = &apmb[cxBoards - 1]; pmdev->pmbLowerLeft = &apmb[cBoards - cxBoards]; pmdev->pmbLowerRight = &apmb[cBoards - 1];
// Now set the neighbor pointers. If there is no neighbor for
// a board, the neighbor pointer must be set to NULL (note that
// we rely on the zero initialization to take care of this):
i = 0;
for (x = 1; x <= cxBoards; x++) { for (y = 1; y <= cyBoards; y++) { if (x > 1) apmb[i].pmbLeft = &apmb[i - 1]; if (y > 1) apmb[i].pmbUp = &apmb[i - cxBoards]; if (x < cxBoards) apmb[i].pmbRight = &apmb[i + 1]; if (y < cyBoards) apmb[i].pmbDown = &apmb[i + cxBoards];
apmb[i].rcl.right = x * cxScreen; apmb[i].rcl.left = x * cxScreen - cxScreen; apmb[i].rcl.bottom = y * cyScreen; apmb[i].rcl.top = y * cyScreen - cyScreen;
i++; } }
return(TRUE);
ReturnFailure0:
DISPDBG((0, "Failed bInitializeGeometry"));
return(FALSE); }
/******************************Public*Routine******************************\
* DHPDEV MulEnablePDEV * \**************************************************************************/
DHPDEV MulEnablePDEV( DEVMODEW* pdm, // Contains data pertaining to requested mode
PWSTR pwszLogAddr, // Logical address
ULONG cPat, // Count of standard patterns
HSURF* phsurfPatterns, // Buffer for standard patterns
ULONG cjCaps, // Size of buffer for device caps 'pdevcaps'
ULONG* pdevcaps, // Buffer for device caps, also known as 'gdiinfo'
ULONG cjDevInfo, // Number of bytes in device info 'pdi'
DEVINFO* pdi, // Device information
HDEV hdev, // HDEV, used for callbacks
PWSTR pwszDeviceName, // Device name
HANDLE hDriver) // Kernel driver handle
{ MDEV* pmdev; // Multi-board PDEV
PDEV* ppdev; // Per-board PDEV
MULTI_BOARD* pmb; DEVMODEW dm; // Local copy of requested devmode
SIZEL szlBoardArray; // Configuration of boards
SIZEL szlScreen; // Resolution of each screen
ULONG ulMode;
// Future versions of NT had better supply 'devcaps' and 'devinfo'
// structures that are the same size or larger than the current
// structures:
if ((cjCaps < sizeof(GDIINFO)) || (cjDevInfo < sizeof(DEVINFO))) { DISPDBG((0, "MulEnablePDEV -- Buffer size too small")); goto ReturnFailure0; }
// Make a local copy of the DEVMODE that we can modify. We need
// to do this because it will, for example, contain a request
// for 2048x768, and we want to convert it to 1024x768:
dm = *pdm;
if (!bQueryMultiBoards(hDriver, &dm, &szlBoardArray, &szlScreen, &ulMode)) { DISPDBG((0, "MulEnablePDEV -- Failed bQueryMultiBoards")); goto ReturnFailure0; }
// Note that we depend on the zero initialization:
pmdev = (MDEV*) EngAllocMem(FL_ZERO_MEMORY, sizeof(MDEV), ALLOC_TAG); if (pmdev == NULL) { DISPDBG((0, "MulEnablePDEV -- Failed EngAllocMem")); goto ReturnFailure0; }
// Remember some stuff:
pmdev->hDriver = hDriver; pmdev->ulMode = ulMode;
if (!bInitializeGeometry(pmdev, szlBoardArray.cx, szlBoardArray.cy, szlScreen.cx, szlScreen.cy)) { DISPDBG((0, "MulEnablePDEV -- Failed bInitializeGeometry")); goto ReturnFailure1; }
// For every board, we'll create our own PDEV.
for (pmb = pmdev->pmb; pmb != NULL; pmb = pmb->pmbNext) { // Initialize each board and create a surface to go with it:
MAKE_BOARD_CURRENT(pmdev, pmb);
ppdev = (PDEV*) DrvEnablePDEV(&dm, pwszLogAddr, cPat, phsurfPatterns, cjCaps, pdevcaps, cjDevInfo, pdi, hdev, pwszDeviceName, hDriver); if (ppdev == NULL) goto ReturnFailure1;
pmb->ppdev = ppdev;
// The PDEV sometimes needs to know its board number, and some
// other stuff:
ppdev->iBoard = pmb->iBoard; ppdev->ulMode = pmdev->ulMode; }
// Get a copy of what functions we're supposed to hook, sans
// HOOK_STRETCHBLT because I can't be bothered to write its
// MulStretchBlt function. First, choose a board, any board:
pmb = pmdev->pmbLowerLeft;
pmdev->flHooks = pmb->ppdev->flHooks & ~HOOK_STRETCHBLT; pmdev->iBitmapFormat = pmb->ppdev->iBitmapFormat;
// Adjust the stuff we return back to GDI.
//
// NOTE: By Setting 'DesktopHorzRes' and 'DesktopVertRes' to the
// size of the virtual desktop, but keeping 'HorzRes' and
// 'VertRes' as the size of the individual screens, we
// get dialogs centered on the one primary screen, but
// windows can be dragged to any screen.
// ((GDIINFO*) pdevcaps)->ulDesktopHorzRes *= pmdev->cxBoards;
// ((GDIINFO*) pdevcaps)->ulDesktopVertRes *= pmdev->cyBoards;
// ((GDIINFO*) pdevcaps)->ulHorzSize *= pmdev->cxBoards;
// ((GDIINFO*) pdevcaps)->ulVertSize *= pmdev->cyBoards;
return((DHPDEV) pmdev);
ReturnFailure1: MulDisablePDEV((DHPDEV) pmdev);
ReturnFailure0: DISPDBG((0, "Failed MulEnablePDEV"));
return(0); }
/******************************Public*Routine******************************\
* VOID MulCompletePDEV * \**************************************************************************/
VOID MulCompletePDEV( DHPDEV dhpdev, HDEV hdev) { MDEV* pmdev; MULTI_BOARD* pmb;
pmdev = (MDEV*) dhpdev; pmdev->hdev = hdev;
for (pmb = pmdev->pmb; pmb != NULL; pmb = pmb->pmbNext) { DrvCompletePDEV((DHPDEV) pmb->ppdev, hdev); } }
/******************************Public*Routine******************************\
* HSURF MulEnableSurface * \**************************************************************************/
HSURF MulEnableSurface(DHPDEV dhpdev) { MDEV* pmdev; FLONG flStatus; MULTI_BOARD* pmb; SIZEL sizlVirtual; HSURF hsurfBoard; // Gnarly, dude!
SURFOBJ* psoBoard; DSURF* pdsurfBoard; HSURF hsurfVirtual; CLIPOBJ* pco;
pmdev = (MDEV*) dhpdev;
flStatus = (FLONG) -1; for (pmb = pmdev->pmb; pmb != NULL; pmb = pmb->pmbNext) { MAKE_BOARD_CURRENT(pmdev, pmb);
hsurfBoard = DrvEnableSurface((DHPDEV) pmb->ppdev); if (hsurfBoard == 0) goto ReturnFailure;
pmb->hsurf = hsurfBoard;
// Every time we draw on a particular board, we'll refer to it
// using this surface:
psoBoard = EngLockSurface(hsurfBoard); if (psoBoard == NULL) goto ReturnFailure;
pmb->pso = psoBoard;
// There are a few things in the board's data instances that we
// have to modify:
pdsurfBoard = (DSURF*) psoBoard->dhsurf;
// This is how we change 'xOffset' and 'yOffset' for each
// individual board 'pdev':
pdsurfBoard->poh->x = -pmb->rcl.left; pdsurfBoard->poh->y = -pmb->rcl.top;
// This is sort of sleazy. Whenever we pass a call on to a board's
// Drv function using 'pmb->pso', it has to be able to retrieve
// its own PDEV pointer from 'dhpdev':
pmb->pso->dhpdev = (DHPDEV) pmb->ppdev;
// Accumulate all the flags:
flStatus &= pmb->ppdev->flStatus; }
// We may encounter a rare situation where one of the boards does
// not have enough memory for the allocation of a brush cache in
// off-screen memory. The current method of handling this situation
// is to look at 'ppdev->flStatus' in DrvRealizeBrush, and fail it
// if no brush cache has been allocated. With multiple boards,
// that has to be converted to a check to see if brush caches have
// been allocated for all boards. We accomplish this by turning off
// the brush cache flag for every 'pdev'.
//
// (We could add extra logic to our pattern routines to handle some
// boards having a brush cache, and some not, but it's not worth
// slowing down the common case.)
if (!(flStatus & STAT_BRUSH_CACHE)) { for (pmb = pmdev->pmb; pmb != NULL; pmb = pmb->pmbNext) { pmb->ppdev->flStatus &= ~STAT_BRUSH_CACHE; } }
// Now create the surface which the engine will use to refer to our
// entire multi-board virtual screen:
sizlVirtual.cx = pmdev->pmbLowerRight->rcl.right; sizlVirtual.cy = pmdev->pmbLowerRight->rcl.bottom;
hsurfVirtual = EngCreateDeviceSurface((DHSURF) pmdev, sizlVirtual, pmdev->iBitmapFormat); if (hsurfVirtual == 0) goto ReturnFailure;
pmdev->hsurf = hsurfVirtual;
if (!EngAssociateSurface(hsurfVirtual, pmdev->hdev, pmdev->flHooks)) goto ReturnFailure;
// Create a temporary clip object that we can use when a drawing
// operation spans multiple boards:
pco = EngCreateClip(); if (pco == NULL) goto ReturnFailure;
pmdev->pco = pco;
pmdev->pco->iDComplexity = DC_RECT; pmdev->pco->iMode = TC_RECTANGLES; pmdev->pco->rclBounds.left = 0; pmdev->pco->rclBounds.top = 0; pmdev->pco->rclBounds.right = pmdev->pmbLowerRight->rcl.right; pmdev->pco->rclBounds.bottom = pmdev->pmbLowerRight->rcl.bottom;
return(hsurfVirtual);
ReturnFailure: MulDisableSurface((DHPDEV) pmdev);
DISPDBG((0, "Failed MulEnableSurface"));
return(0); }
/******************************Public*Routine******************************\
* BOOL MulStrokePath * \**************************************************************************/
BOOL MulStrokePath( SURFOBJ* pso, PATHOBJ* ppo, CLIPOBJ* pco, XFORMOBJ* pxo, BRUSHOBJ* pbo, POINTL* pptlBrush, LINEATTRS* pla, MIX mix) { RECTFX rcfxBounds; RECTL rclBounds; MDEV* pmdev; RECTL rclOriginalBounds; MULTI_BOARD* pmb; BOOL b; FLOAT_LONG elStyleState;
// Get the path bounds and make it lower-right exclusive:
PATHOBJ_vGetBounds(ppo, &rcfxBounds);
rclBounds.left = (rcfxBounds.xLeft >> 4); rclBounds.top = (rcfxBounds.yTop >> 4); rclBounds.right = (rcfxBounds.xRight >> 4) + 2; rclBounds.bottom = (rcfxBounds.yBottom >> 4) + 2;
pmdev = (MDEV*) pso->dhpdev; if (bFindBoard(pmdev, &rclBounds, &pmb)) { GO_BOARD(pmdev, pmb); b = DrvStrokePath(pmb->pso, ppo, pco, pxo, pbo, pptlBrush, pla, mix); } else { if ((pco == NULL) || (pco->iDComplexity == DC_TRIVIAL)) { // If the CLIPOBJ doesn't have at least DC_RECT complexity,
// substitute one that does:
pco = pmdev->pco; }
rclOriginalBounds = pco->rclBounds; elStyleState = pla->elStyleState;
b = TRUE; do { // For each board, make sure the style state gets reset and
// the path enumeration gets restarted:
pla->elStyleState = elStyleState; PATHOBJ_vEnumStart(ppo);
if (bIntersect(&rclOriginalBounds, &pmb->rcl, &pco->rclBounds)) { GO_BOARD(pmdev, pmb); b &= DrvStrokePath(pmb->pso, ppo, pco, pxo, pbo, pptlBrush, pla, mix); }
} while (bNextBoard(&rclBounds, &pmb));
// Restore the original clip bounds:
pco->rclBounds = rclOriginalBounds; }
return(b); }
/******************************Public*Routine******************************\
* BOOL MulFillPath * \**************************************************************************/
BOOL MulFillPath( SURFOBJ* pso, PATHOBJ* ppo, CLIPOBJ* pco, BRUSHOBJ* pbo, POINTL* pptlBrush, MIX mix, FLONG flOptions) { RECTFX rcfxBounds; RECTL rclBounds; MDEV* pmdev; RECTL rclOriginalBounds; MULTI_BOARD* pmb; BOOL b;
// Get the path bounds and make it lower-right exclusive:
PATHOBJ_vGetBounds(ppo, &rcfxBounds);
rclBounds.left = (rcfxBounds.xLeft >> 4); rclBounds.top = (rcfxBounds.yTop >> 4); rclBounds.right = (rcfxBounds.xRight >> 4) + 2; rclBounds.bottom = (rcfxBounds.yBottom >> 4) + 2;
pmdev = (MDEV*) pso->dhpdev; if (bFindBoard(pmdev, &rclBounds, &pmb)) { GO_BOARD(pmdev, pmb); b = DrvFillPath(pmb->pso, ppo, pco, pbo, pptlBrush, mix, flOptions); } else { if ((pco == NULL) || (pco->iDComplexity == DC_TRIVIAL)) { // If the CLIPOBJ doesn't have at least DC_RECT complexity,
// substitute one that does:
pco = pmdev->pco; }
rclOriginalBounds = pco->rclBounds;
b = TRUE; do { // Make sure we restart the path enumeration if need be:
PATHOBJ_vEnumStart(ppo); if (bIntersect(&rclOriginalBounds, &pmb->rcl, &pco->rclBounds)) { GO_BOARD(pmdev, pmb); b &= DrvFillPath(pmb->pso, ppo, pco, pbo, pptlBrush, mix, flOptions); }
} while (bNextBoard(&rclBounds, &pmb));
// Restore the original clip bounds:
pco->rclBounds = rclOriginalBounds; }
return(b); }
/******************************Public*Routine******************************\
* BOOL MulBitBlt * \**************************************************************************/
BOOL MulBitBlt( SURFOBJ* psoDst, SURFOBJ* psoSrc, SURFOBJ* psoMask, CLIPOBJ* pco, XLATEOBJ* pxlo, RECTL* prclDst, POINTL* pptlSrc, POINTL* pptlMask, BRUSHOBJ* pbo, POINTL* pptlBrush, ROP4 rop4) { BOOL bFromScreen; BOOL bToScreen; MDEV* pmdev; MULTI_BOARD* pmb; RECTL rclOriginalBounds; BOOL b; RECTL rclBounds; LONG xOffset; LONG yOffset; RECTL rclDstBounds; RECTL rclDst;
bFromScreen = ((psoSrc != NULL) && (psoSrc->iType == STYPE_DEVICE)); bToScreen = ((psoDst != NULL) && (psoDst->iType == STYPE_DEVICE));
// We copy the prclDst rectangle here because sometimes GDI will
// simply point prclDst to the same rectangle in pco->rclBounds,
// and we'll be mucking with pco->rclBounds...
rclDst = *prclDst;
if (bToScreen && bFromScreen) { ///////////////////////////////////////////////////////////////
// Screen-to-screen
///////////////////////////////////////////////////////////////
pmdev = (MDEV*) psoDst->dhpdev;
// rclBounds is the union of the source and destination rectangles:
rclBounds.left = min(rclDst.left, pptlSrc->x); rclBounds.top = min(rclDst.top, pptlSrc->y); rclBounds.right = max(rclDst.right, pptlSrc->x + (rclDst.right - rclDst.left)); rclBounds.bottom = max(rclDst.bottom, pptlSrc->y + (rclDst.bottom - rclDst.top));
if (bFindBoard(pmdev, &rclBounds, &pmb)) { GO_BOARD(pmdev, pmb); b = DrvBitBlt(pmb->pso, pmb->pso, psoMask, pco, pxlo, &rclDst, pptlSrc, pptlMask, pbo, pptlBrush, rop4); } else { return(bBitBltBetweenBoards(psoDst, psoSrc, psoMask, pco, pxlo, &rclDst, pptlSrc, pptlMask, pbo, pptlBrush, rop4, &rclBounds, pmb)); } } else if (bToScreen) { ///////////////////////////////////////////////////////////////
// To-screen
///////////////////////////////////////////////////////////////
pmdev = (MDEV*) psoDst->dhpdev; if (bFindBoard(pmdev, &rclDst, &pmb)) { GO_BOARD(pmdev, pmb); b = DrvBitBlt(pmb->pso, psoSrc, psoMask, pco, pxlo, &rclDst, pptlSrc, pptlMask, pbo, pptlBrush, rop4); } else { if ((pco == NULL) || (pco->iDComplexity == DC_TRIVIAL)) { // If the CLIPOBJ doesn't have at least DC_RECT complexity,
// substitute one that does:
pco = pmdev->pco; }
rclOriginalBounds = pco->rclBounds;
b = TRUE; do { if (bIntersect(&rclOriginalBounds, &pmb->rcl, &pco->rclBounds)) { GO_BOARD(pmdev, pmb); b &= DrvBitBlt(pmb->pso, psoSrc, psoMask, pco, pxlo, &rclDst, pptlSrc, pptlMask, pbo, pptlBrush, rop4); }
} while (bNextBoard(&rclDst, &pmb));
// Restore the original clip bounds:
pco->rclBounds = rclOriginalBounds; } } else { ///////////////////////////////////////////////////////////////
// From-screen
///////////////////////////////////////////////////////////////
pmdev = (MDEV*) psoSrc->dhpdev;
// rclBounds is the source rectangle:
rclBounds.left = pptlSrc->x; rclBounds.top = pptlSrc->y; rclBounds.right = pptlSrc->x + (rclDst.right - rclDst.left); rclBounds.bottom = pptlSrc->y + (rclDst.bottom - rclDst.top);
if (bFindBoard(pmdev, &rclBounds, &pmb)) { GO_BOARD(pmdev, pmb); b = DrvBitBlt(psoDst, pmb->pso, psoMask, pco, pxlo, &rclDst, pptlSrc, pptlMask, pbo, pptlBrush, rop4); } else { if ((pco == NULL) || (pco->iDComplexity == DC_TRIVIAL)) { // If the CLIPOBJ doesn't have at least DC_RECT complexity,
// substitute one that does:
pco = pmdev->pco; }
rclOriginalBounds = pco->rclBounds;
// Offset to transform from source rectangle to destination
// rectangle:
xOffset = rclDst.left - pptlSrc->x; yOffset = rclDst.top - pptlSrc->y;
b = TRUE; do { // Since the screen is the source, but the clip bounds applies
// to the destination, we have to convert our board clipping
// information to destination coordinates:
rclDstBounds.left = pmb->rcl.left + xOffset; rclDstBounds.right = pmb->rcl.right + xOffset; rclDstBounds.top = pmb->rcl.top + yOffset; rclDstBounds.bottom = pmb->rcl.bottom + yOffset;
if (bIntersect(&rclOriginalBounds, &rclDstBounds, &pco->rclBounds)) { GO_BOARD(pmdev, pmb); b &= DrvBitBlt(psoDst, pmb->pso, psoMask, pco, pxlo, &rclDst, pptlSrc, pptlMask, pbo, pptlBrush, rop4); }
} while (bNextBoard(&rclBounds, &pmb));
// Restore the original clip bounds:
pco->rclBounds = rclOriginalBounds; } }
return(b); }
/******************************Public*Routine******************************\
* VOID MulDisablePDEV * * Note: May be called before MulEnablePDEV successfully completed! * \**************************************************************************/
VOID MulDisablePDEV(DHPDEV dhpdev) { MULTI_BOARD* pmb; MDEV* pmdev;
pmdev = (MDEV*) dhpdev;
for (pmb = pmdev->pmb; pmb != NULL; pmb = pmb->pmbNext) { if (pmb->ppdev != NULL) { GO_BOARD(pmdev, pmb); DrvDisablePDEV((DHPDEV) pmb->ppdev); } }
EngFreeMem(pmdev->pmb); // Undo 'bInitializeGeometry' allocation
EngFreeMem(pmdev); }
/******************************Public*Routine******************************\
* VOID MulDisableSurface * * Note: May be called before MulEnableSurface successfully completed! * \**************************************************************************/
VOID MulDisableSurface(DHPDEV dhpdev) { MULTI_BOARD* pmb; MDEV* pmdev;
pmdev = (MDEV*) dhpdev;
if (pmdev->pco != NULL) EngDeleteClip(pmdev->pco);
EngDeleteSurface(pmdev->hsurf);
for (pmb = pmdev->pmb; pmb != NULL; pmb = pmb->pmbNext) { GO_BOARD(pmdev, pmb);
EngUnlockSurface(pmb->pso);
DrvDisableSurface((DHPDEV) pmb->ppdev); } }
/******************************Public*Routine******************************\
* VOID MulAssertMode * \**************************************************************************/
BOOL MulAssertMode( DHPDEV dhpdev, BOOL bEnable) { MDEV* pmdev; MULTI_BOARD* pmb;
pmdev = (MDEV*) dhpdev;
if (!bEnable) { // When switching to full-screen mode, PatBlt blackness over
// all the inactive screens (otherwise it looks goofy when
// the desktop is frozen on the inactive screens and the user
// can't do anything with it):
for (pmb = pmdev->pmb; pmb != NULL; pmb = pmb->pmbNext) { GO_BOARD(pmdev, pmb); DrvBitBlt(pmb->pso, NULL, NULL, NULL, NULL, &pmb->rcl, NULL, NULL, NULL, NULL, 0); } }
for (pmb = pmdev->pmb; pmb != NULL; pmb = pmb->pmbNext) { GO_BOARD(pmdev, pmb); DrvAssertMode((DHPDEV) pmb->ppdev, bEnable); }
return TRUE; }
/******************************Public*Routine******************************\
* VOID MulMovePointer * \**************************************************************************/
VOID MulMovePointer( SURFOBJ* pso, LONG x, LONG y, RECTL* prcl) { MDEV* pmdev; MULTI_BOARD* pmbPointer; RECTL rclPointer;
pmdev = (MDEV*) pso->dhpdev; pmbPointer = pmdev->pmbPointer;
if (pmbPointer != NULL) { // The most common case is when the pointer is moved to a spot
// on the same board:
if ((x >= pmbPointer->rcl.left) && (x < pmbPointer->rcl.right) && (y >= pmbPointer->rcl.top) && (y < pmbPointer->rcl.bottom)) { GO_BOARD(pmdev, pmbPointer); DrvMovePointer(pmbPointer->pso, x, y, prcl);
return; }
// Tell the old board to erase its cursor:
GO_BOARD(pmdev, pmbPointer); DrvMovePointer(pmbPointer->pso, -1, -1, NULL); }
if (x == -1) { pmdev->pmbPointer = NULL;
return; }
// Find the new board and tell it to draw its new cursor:
rclPointer.left = x; rclPointer.right = x; rclPointer.top = y; rclPointer.bottom = y;
bFindBoard(pmdev, &rclPointer, &pmbPointer);
GO_BOARD(pmdev, pmbPointer); DrvMovePointer(pmbPointer->pso, x, y, prcl);
pmdev->pmbPointer = pmbPointer; }
/******************************Public*Routine******************************\
* ULONG MulSetPointerShape * \**************************************************************************/
ULONG MulSetPointerShape( SURFOBJ* pso, SURFOBJ* psoMask, SURFOBJ* psoColor, XLATEOBJ* pxlo, LONG xHot, LONG yHot, LONG x, LONG y, RECTL* prcl, FLONG fl) { MULTI_BOARD* pmb; MDEV* pmdev; ULONG ulRet; RECTL rclPointer; MULTI_BOARD* pmbPointer; // Board on which cursor is visible
BOOL b;
pmdev = (MDEV*) pso->dhpdev;
// Find out which board that the cursor is visible on, if any:
pmbPointer = NULL; if (x != -1) { rclPointer.left = x; rclPointer.right = x; rclPointer.top = y; rclPointer.bottom = y;
b = bFindBoard(pmdev, &rclPointer, &pmbPointer);
ASSERTDD(b, "Woah, couldn't find what board the pointer was on?"); }
pmdev->pmbPointer = pmbPointer;
ulRet = SPS_ACCEPT_NOEXCLUDE;
for (pmb = pmdev->pmb; pmb != NULL; pmb = pmb->pmbNext) { // Send the new shape down to every board, and at the same
// time check to see if each board will support the pointer by
// creating it as hidden on every one.
GO_BOARD(pmdev, pmb);
if (DrvSetPointerShape(pmb->pso, psoMask, psoColor, pxlo, xHot, yHot, -1, y, NULL, fl) != SPS_ACCEPT_NOEXCLUDE) { // Oh no, one of the boards won't support this pointer. We'll
// have to ask GDI to simulate for all boards:
ulRet = SPS_DECLINE; } }
if ((ulRet == SPS_ACCEPT_NOEXCLUDE) && (pmbPointer != NULL)) { // All boards accepted the hardware pointer, so show it on the
// appropriate board. If 'pmbPointer' is NULL, we're not being
// asked to show the pointer at all.
GO_BOARD(pmdev, pmbPointer);
DrvMovePointer(pmbPointer->pso, x, y, NULL); }
return(ulRet); }
/******************************Public*Routine******************************\
* ULONG MulDitherColor * \**************************************************************************/
ULONG MulDitherColor( DHPDEV dhpdev, ULONG iMode, ULONG rgb, ULONG* pul) { PDEV* ppdev; ULONG ulRet;
// Let the first board's driver do the dithering:
ppdev = ((MDEV*) dhpdev)->pmb->ppdev; ulRet = DrvDitherColor((DHPDEV) ppdev, iMode, rgb, pul);
return(ulRet); }
/******************************Public*Routine******************************\
* ULONG MulSetPalette * \**************************************************************************/
BOOL MulSetPalette( DHPDEV dhpdev, PALOBJ* ppalo, FLONG fl, ULONG iStart, ULONG cColors) { MULTI_BOARD* pmb; MDEV* pmdev; BOOL bRet = TRUE;
// Notify all boards of the palette change:
pmdev = (MDEV*) dhpdev; for (pmb = pmdev->pmb; pmb != NULL; pmb = pmb->pmbNext) { GO_BOARD(pmdev, pmb);
MAKE_BOARD_CURRENT(pmdev, pmb);
bRet &= DrvSetPalette((DHPDEV) pmb->ppdev, ppalo, fl, iStart, cColors); }
return(bRet); }
/******************************Public*Routine******************************\
* BOOL MulCopyBits * \**************************************************************************/
BOOL MulCopyBits( SURFOBJ* psoDst, SURFOBJ* psoSrc, CLIPOBJ* pco, XLATEOBJ* pxlo, RECTL* prclDst, POINTL* pptlSrc) { BOOL bFromScreen; BOOL bToScreen; MDEV* pmdev; MULTI_BOARD* pmb; RECTL rclOriginalBounds; BOOL b; RECTL rclBounds; RECTL rclDst;
bFromScreen = ((psoSrc != NULL) && (psoSrc->iType == STYPE_DEVICE)); bToScreen = ((psoDst != NULL) && (psoDst->iType == STYPE_DEVICE));
// We copy the prclDst rectangle here because sometimes GDI will
// simply point prclDst to the same rectangle in pco->rclBounds,
// and we'll be mucking with pco->rclBounds...
rclDst = *prclDst;
if (bToScreen && bFromScreen) { ///////////////////////////////////////////////////////////////
// Screen-to-screen
///////////////////////////////////////////////////////////////
pmdev = (MDEV*) psoDst->dhpdev;
// rclBounds is the union of the source and destination rectangles:
rclBounds.left = min(rclDst.left, pptlSrc->x); rclBounds.top = min(rclDst.top, pptlSrc->y); rclBounds.right = max(rclDst.right, pptlSrc->x + (rclDst.right - rclDst.left)); rclBounds.bottom = max(rclDst.bottom, pptlSrc->y + (rclDst.bottom - rclDst.top));
if (bFindBoard(pmdev, &rclBounds, &pmb)) { GO_BOARD(pmdev, pmb); b = DrvCopyBits(pmb->pso, pmb->pso, pco, pxlo, &rclDst, pptlSrc); } else { return(bBitBltBetweenBoards(psoDst, psoSrc, NULL, pco, pxlo, &rclDst, pptlSrc, NULL, NULL, NULL, 0x0000cccc, &rclBounds, pmb)); } } else if (bToScreen) { ///////////////////////////////////////////////////////////////
// To-screen
///////////////////////////////////////////////////////////////
pmdev = (MDEV*) psoDst->dhpdev; if (bFindBoard(pmdev, &rclDst, &pmb)) { GO_BOARD(pmdev, pmb); b = DrvCopyBits(pmb->pso, psoSrc, pco, pxlo, &rclDst, pptlSrc); } else { if ((pco == NULL) || (pco->iDComplexity == DC_TRIVIAL)) { // If the CLIPOBJ doesn't have at least DC_RECT complexity,
// substitute one that does:
pco = pmdev->pco; }
rclOriginalBounds = pco->rclBounds;
b = TRUE; do { if (bIntersect(&rclOriginalBounds, &pmb->rcl, &pco->rclBounds)) { GO_BOARD(pmdev, pmb); b &= DrvCopyBits(pmb->pso, psoSrc, pco, pxlo, &rclDst, pptlSrc); }
} while (bNextBoard(&rclDst, &pmb));
// Restore the original clip bounds:
pco->rclBounds = rclOriginalBounds; } } else { ///////////////////////////////////////////////////////////////
// From-screen
///////////////////////////////////////////////////////////////
// This rarely happens, so save some code space:
return(MulBitBlt(psoDst, psoSrc, NULL, pco, pxlo, prclDst, pptlSrc, NULL, NULL, NULL, 0x0000cccc)); }
return(b); }
/******************************Public*Routine******************************\
* BOOL MulTextOut * \**************************************************************************/
BOOL MulTextOut( SURFOBJ* pso, STROBJ* pstro, FONTOBJ* pfo, CLIPOBJ* pco, RECTL* prclExtra, RECTL* prclOpaque, BRUSHOBJ* pboFore, BRUSHOBJ* pboOpaque, POINTL* pptlOrg, MIX mix) { MDEV* pmdev; MULTI_BOARD* pmb; RECTL rclOriginalBounds; BYTE fjOriginalOptions; BOOL b; RECTL* prclBounds; FONT_CONSUMER* pfcArray;
pmdev = (MDEV*) pso->dhpdev;
// In keeping with our philosophy for multiple board support, we handle
// multiple consumers of the same font at this level. We do this by
// monitoring pfo->pvConsumer, and the first time a board sets the
// field, we take control of pfo->pvConsumer. We use it to allocate
// a pvConsumer array where we can keep track of every board's
// individual pvConsumer.
pfcArray = pfo->pvConsumer;
prclBounds = (prclOpaque != NULL) ? prclOpaque : &pstro->rclBkGround;
bFindBoard(pmdev, prclBounds, &pmb);
if ((pco == NULL) || (pco->iDComplexity == DC_TRIVIAL)) { // If the CLIPOBJ doesn't have at least DC_RECT complexity,
// substitute one that does:
pco = pmdev->pco; }
rclOriginalBounds = pco->rclBounds; fjOriginalOptions = pco->fjOptions;
// OR in the OC_BANK_CLIP flag to let GDI know that we may be calling
// EngTextOut multiple times with the same parameters (EngTextOut
// is destructive in that it modifies that parameters passed to it,
// unless this bit is set):
pco->fjOptions |= OC_BANK_CLIP;
b = TRUE; do { if (pfcArray != NULL) pfo->pvConsumer = pfcArray->apvc[pmb->iBoard].pvConsumer;
// Make sure we restart the glyph enumeration if need be:
STROBJ_vEnumStart(pstro); if (bIntersect(&rclOriginalBounds, &pmb->rcl, &pco->rclBounds)) { GO_BOARD(pmdev, pmb); b &= DrvTextOut(pmb->pso, pstro, pfo, pco, prclExtra, prclOpaque, pboFore, pboOpaque, pptlOrg, mix); }
if (pfcArray != NULL) { // Copy the pvConsumer, in case the last DrvTextOut changed
// it:
pfcArray->apvc[pmb->iBoard].pvConsumer = pfo->pvConsumer; } else { if (pfo->pvConsumer != NULL) { // The board allocated a new consumer, so create our array
// to keep track of consumers for every board:
pfcArray = (FONT_CONSUMER*) EngAllocMem(FL_ZERO_MEMORY, sizeof(FONT_CONSUMER), ALLOC_TAG); if (pfcArray == NULL) DrvDestroyFont(pfo); else { pfcArray->cConsumers = pmdev->cBoards; pfcArray->apvc[pmb->iBoard].pvConsumer = pfo->pvConsumer;
} } } } while (bNextBoard(prclBounds, &pmb));
// Restore the original clip bounds:
pco->rclBounds = rclOriginalBounds; pco->fjOptions = fjOriginalOptions;
// Make sure we restore/set the font's pvConsumer:
pfo->pvConsumer = pfcArray;
return(b); }
/******************************Public*Routine******************************\
* VOID MulDestroyFont * \**************************************************************************/
VOID MulDestroyFont(FONTOBJ *pfo) { FONT_CONSUMER* pfcArray; LONG i; PVOID pvConsumer;
if (pfo->pvConsumer != NULL) { pfcArray = pfo->pvConsumer; for (i = 0; i < pfcArray->cConsumers; i++) { pvConsumer = pfcArray->apvc[i].pvConsumer; if (pvConsumer != NULL) { pfo->pvConsumer = pvConsumer; DrvDestroyFont(pfo); } }
EngFreeMem(pfcArray); pfo->pvConsumer = NULL; }
}
/******************************Public*Routine******************************\
* BOOL MulPaint * \**************************************************************************/
BOOL MulPaint( SURFOBJ* pso, CLIPOBJ* pco, BRUSHOBJ* pbo, POINTL* pptlBrush, MIX mix) { MDEV* pmdev; RECTL rclOriginalBounds; MULTI_BOARD* pmb; BOOL b;
pmdev = (MDEV*) pso->dhpdev; if (bFindBoard(pmdev, &pco->rclBounds, &pmb)) { GO_BOARD(pmdev, pmb); b = DrvPaint(pmb->pso, pco, pbo, pptlBrush, mix); } else { rclOriginalBounds = pco->rclBounds;
b = TRUE; do { if (bIntersect(&rclOriginalBounds, &pmb->rcl, &pco->rclBounds)) { GO_BOARD(pmdev, pmb); b &= DrvPaint(pmb->pso, pco, pbo, pptlBrush, mix); }
} while (bNextBoard(&rclOriginalBounds, &pmb));
// Restore the original clip bounds:
pco->rclBounds = rclOriginalBounds; }
return(b); }
/******************************Public*Routine******************************\
* BOOL MulRealizeBrush * \**************************************************************************/
BOOL MulRealizeBrush( BRUSHOBJ* pbo, SURFOBJ* psoTarget, SURFOBJ* psoPattern, SURFOBJ* psoMask, XLATEOBJ* pxlo, ULONG iHatch) { MDEV* pmdev; BOOL b;
pmdev = (MDEV*) psoTarget->dhpdev;
// DrvRealizeBrush is only ever called from within a Drv function.
// 'psoTarget' points to our multi-board surface, but we have to point
// it to the surface of the board for which the DrvBitBlt call was made.
b = DrvRealizeBrush(pbo, pmdev->pmbCurrent->pso, psoPattern, psoMask, pxlo, iHatch);
return(b); }
#endif // MULTI_BOARDS
|