Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2076 lines
60 KiB

  1. /******************************Module*Header*******************************\
  2. * Module Name: multi.c
  3. *
  4. * Supports multiple display boards as a single virtual desktop.
  5. *
  6. * This is implemented by presenting to GDI a single large virtual
  7. * display and adding a layer between GDI and the driver's Drv functions.
  8. * For the most part, the rest of the driver outside of multi.c doesn't
  9. * have to change much, subject to the requirements below.
  10. *
  11. * This implementation requires that each board have the same virtual
  12. * resolution and colour depth (e.g., all be running 1024x768x256), and
  13. * that the boards be arranged in a rectangular configuration.
  14. *
  15. * Each board has its own PDEV, and completely manages its surface
  16. * independently, down to glyph and bitmap caching. The Mul
  17. * routine intercepts the DDI call, and for each board dispatches
  18. * a Drv call with the appropriate PDEV and clip object modifications.
  19. *
  20. * The following support in the main driver is required:
  21. *
  22. * 1) The driver should be able to handle a per-surface offset. For
  23. * example, if two 1024x768 displays are pasted side-by-side, the
  24. * right board will get drawing operations in the range (1024, 768) -
  25. * (2048, 768). The driver has a (-1024, 0) surface offset to convert
  26. * the actual drawing on the right board to the expected (0, 0) -
  27. * (1024, 768).
  28. *
  29. * The current driver already uses this notion to support device-format
  30. * bitmaps drawn in off-screen memory.
  31. *
  32. * Another option would be to handle the surface offsets in this layer,
  33. * but then all parameters including clip objects, paths and glyph
  34. * enumerations would have to be adjusted here as well.
  35. *
  36. * 2) The main driver must be able to share realized pattern information
  37. * between board instances. That is, with the current DDI specification
  38. * GDI entirely handles brush memory allocation via pvAllocRBrush,
  39. * and the driver doesn't get notified when the brush is destroyed, so
  40. * the driver has to keep all information about the brush for all the
  41. * boards in the one brush realization. This isn't too onerous.
  42. *
  43. * Problems:
  44. *
  45. * 1) DrvSetPointerShape requires that support be consistent between all
  46. * board instances -- for example, one board instance cannot accept
  47. * a h/w cursor and the other fail it, because GDI doesn't know it would
  48. * have to simulate on one area of the screen and not the other.
  49. *
  50. * 2) CompatibleBitmaps would have to be shared between board instances.
  51. * This becomes a problem when the bitmaps are kept by the driver in off-
  52. * screen memory.
  53. *
  54. * Status:
  55. *
  56. * This code is officially untested. However, I know of no outstanding
  57. * bugs -- everything seems to just work.
  58. *
  59. * Note that I haven't addressed any initialization issues; to adapt this
  60. * code, you will need to solve some of those problems (such as proper
  61. * miniport support, and a nice method for the user to control the board
  62. * configurations and geometry).
  63. *
  64. * Disclaimer:
  65. *
  66. * This code is provided as sample code only. It is not intended to
  67. * represent Microsoft's endorsed solution for multiple screen support.
  68. *
  69. * Copyright (c) 1993-1995 Microsoft Corporation
  70. \**************************************************************************/
  71. #include "precomp.h"
  72. #if MULTI_BOARDS
  73. // We change the active board to the home board after every drawing
  74. // operation. We do this only because the Metheus BIOS does not reset
  75. // the active board on a soft-reset, and so the initial POST text
  76. // would come up on whatever happened to be the last board drawn to.
  77. #define GO_HOME(pmdev) vSelectBoard(pmdev, pmdev->pmbHome)
  78. #define GO_BOARD(pmdev, pmb) vSelectBoard(pmdev, pmb)
  79. struct _MULTI_BOARD;
  80. typedef struct _MULTI_BOARD MULTI_BOARD; /* mb */
  81. struct _MULTI_BOARD
  82. {
  83. LONG iHwBoard; // Hardware board number
  84. LONG iBoard; // Sequentially allocated board number
  85. RECTL rcl; // Board's coordinates
  86. MULTI_BOARD* pmbNext; // For traversing the entire list of boards
  87. MULTI_BOARD* pmbLeft; // For traversing by direction
  88. MULTI_BOARD* pmbUp;
  89. MULTI_BOARD* pmbRight;
  90. MULTI_BOARD* pmbDown;
  91. PDEV* ppdev; // Pointer to the board's PDEV
  92. SURFOBJ* pso; // Surface representing the board
  93. HSURF hsurf; // Handle to surface
  94. }; /* mb, pmb */
  95. typedef struct _MDEV
  96. {
  97. MULTI_BOARD* pmb; // Where to start enumerating
  98. MULTI_BOARD* pmbHome; // Board used for full-screen
  99. MULTI_BOARD* pmbUpperLeft; // Board in upper-left corner
  100. MULTI_BOARD* pmbUpperRight;
  101. MULTI_BOARD* pmbLowerLeft;
  102. MULTI_BOARD* pmbLowerRight;
  103. LONG cxBoards; // Number of boards per row
  104. LONG cyBoards; // Number of boards per column
  105. LONG cBoards; // Total number of boards
  106. MULTI_BOARD* pmbPointer; // Board where cursor is currently visible
  107. MULTI_BOARD* pmbCurrent; // Currently selected board (needed for
  108. // DrvRealizeBrush)
  109. HDEV hdev; // Handle that GDI knows us by
  110. HSURF hsurf; // Handle to our virtual surface
  111. CLIPOBJ* pco; // A temporary CLIPOBJ that we can modify
  112. ULONG iBitmapFormat; // Current colour depth
  113. FLONG flHooks; // Those functions that the main driver
  114. // is hooking
  115. } MDEV; /* mdev, pmdev */
  116. typedef struct _PVCONSUMER
  117. {
  118. PVOID pvConsumer;
  119. } PVCONSUMER;
  120. typedef struct _FONT_CONSUMER
  121. {
  122. LONG cConsumers; // Total number of boards
  123. PVCONSUMER apvc[MAX_BOARDS]; // Array of structures cConsumers in length
  124. } FONT_CONSUMER; /* fc, pfc */
  125. typedef struct _BITBLTDATA
  126. {
  127. RECTL rclBounds;
  128. MDEV* pmdev;
  129. SURFOBJ* psoDst;
  130. SURFOBJ* psoSrc;
  131. SURFOBJ* psoMask;
  132. CLIPOBJ* pco;
  133. XLATEOBJ* pxlo;
  134. RECTL* prclDst;
  135. POINTL* pptlSrc;
  136. POINTL* pptlMask;
  137. BRUSHOBJ* pbo;
  138. POINTL* pptlBrush;
  139. ROP4 rop4;
  140. } BITBLTDATA; /* bb, pbb */
  141. /******************************Public*Routine******************************\
  142. * bFindBoard
  143. *
  144. * Returns in ppmb a pointer to the board containing the upper-left
  145. * corner of prcl.
  146. *
  147. * Returns TRUE if prcl is entirely containing on one board; FALSE if
  148. * prcl spans multiple boards.
  149. *
  150. \**************************************************************************/
  151. BOOL bFindBoard(MDEV* pmdev, RECTL* prcl, MULTI_BOARD** ppmb)
  152. {
  153. MULTI_BOARD* pmb;
  154. pmb = pmdev->pmbUpperLeft;
  155. // It should never happen that GDI will give us a call whose bounds
  156. // don't intersect the virtual screen. But so that we don't crash
  157. // should it ever happen, we'll return an intersection with the first
  158. // board -- we can assume GDI at least said the clipping was non-
  159. // trivial, in which case that board's display routine will realize
  160. // nothing had to be done:
  161. *ppmb = pmb;
  162. // First find the row:
  163. while (prcl->top >= pmb->rcl.bottom)
  164. {
  165. pmb = pmb->pmbDown;
  166. if (pmb == NULL)
  167. return(FALSE); // This is a case where the bounds doesn't
  168. // intercept the virtual screen
  169. }
  170. // Now find the column:
  171. while (prcl->left >= pmb->rcl.right)
  172. {
  173. pmb = pmb->pmbRight;
  174. if (pmb == NULL)
  175. return(FALSE); // This is a case where the bounds doesn't
  176. // intercept the virtual screen
  177. }
  178. // So we found the first board:
  179. *ppmb = pmb;
  180. return(prcl->right <= pmb->rcl.right &&
  181. prcl->bottom <= pmb->rcl.bottom);
  182. }
  183. /******************************Public*Routine******************************\
  184. * bNextBoard
  185. *
  186. * Returns in ppmb a pointer to the next board after intersecting prcl, going
  187. * left-to-right then top-to-bottom.
  188. *
  189. * Returns TRUE if all boards intersecting prcl have been enumerated; FALSE
  190. * if there are more boards.
  191. *
  192. \**************************************************************************/
  193. BOOL bNextBoard(RECTL* prcl, MULTI_BOARD** ppmb)
  194. {
  195. MULTI_BOARD* pmb;
  196. pmb = *ppmb;
  197. // We'll do all the boards in a row first, remembering that the
  198. // bounds rectangle can extend past the end of our virtual screen:
  199. if ((prcl->right > pmb->rcl.right) && (pmb->pmbRight != NULL))
  200. {
  201. *ppmb = pmb->pmbRight;
  202. return(TRUE);
  203. }
  204. // Go to next row if need be, starting at the rcl.left:
  205. if ((prcl->bottom > pmb->rcl.bottom) && (pmb->pmbDown != NULL))
  206. {
  207. pmb = pmb->pmbDown;
  208. while ((prcl->left < pmb->rcl.left) && (pmb->pmbLeft != NULL))
  209. {
  210. pmb = pmb->pmbLeft;
  211. }
  212. *ppmb = pmb;
  213. return(TRUE);
  214. }
  215. return(FALSE);
  216. }
  217. /******************************Public*Routine******************************\
  218. * vIntersect
  219. *
  220. * Returns in prclOut the intersection of rectangles prcl1 and prcl2.
  221. *
  222. \**************************************************************************/
  223. VOID vIntersect(RECTL* prcl1, RECTL* prcl2, RECTL* prclOut)
  224. {
  225. prclOut->left = max(prcl1->left, prcl2->left);
  226. prclOut->top = max(prcl1->top, prcl2->top);
  227. prclOut->right = min(prcl1->right, prcl2->right);
  228. prclOut->bottom = min(prcl1->bottom, prcl2->bottom);
  229. }
  230. /******************************Public*Routine******************************\
  231. * bVeryTemporaryInitializationCode
  232. *
  233. \**************************************************************************/
  234. BOOL bVeryTemporaryInitializationCode(MDEV* pmdev)
  235. {
  236. MULTI_BOARD* pmb1 = NULL;
  237. MULTI_BOARD* pmb2 = NULL;
  238. pmb1 = EngAllocMem(FL_ZERO_MEMORY, sizeof(MULTI_BOARD), ALLOC_TAG);
  239. pmb2 = EngAllocMem(FL_ZERO_MEMORY, sizeof(MULTI_BOARD), ALLOC_TAG);
  240. if ((pmb1 == NULL) || (pmb2 == NULL))
  241. {
  242. EngFreeMem(pmb1);
  243. EngFreeMem(pmb2);
  244. return(FALSE);
  245. }
  246. // Only this initialization part is hard-coded to have two monitors,
  247. // side-by-side.
  248. // Board one:
  249. pmb1->iHwBoard = 0;
  250. pmb1->iBoard = 0;
  251. pmb1->pmbNext = pmb2;
  252. pmb1->pmbRight = pmb2;
  253. pmdev->pmb = pmb1;
  254. pmdev->pmbUpperLeft = pmb1;
  255. pmdev->pmbLowerLeft = pmb1;
  256. pmdev->pmbHome = pmb1;
  257. // Board two:
  258. pmb2->iHwBoard = 1;
  259. pmb2->iBoard = 1;
  260. pmb2->pmbLeft = pmb1;
  261. pmdev->pmbUpperRight = pmb2;
  262. pmdev->pmbLowerRight = pmb2;
  263. pmdev->cxBoards = 2;
  264. pmdev->cyBoards = 1;
  265. pmdev->cBoards = 2;
  266. // Assume that the currently active board is the 'home' board:
  267. pmdev->pmbCurrent = pmdev->pmbHome;
  268. return(TRUE);
  269. }
  270. /******************************Public*Routine******************************\
  271. * vSelectBoard
  272. *
  273. * Selects board pmb for drawing. We can have only one Metheus board active
  274. * at any time, so we have to disable the old board before enabling the new.
  275. *
  276. * Obviously, your hardware implementation may not require this entire
  277. * function. For example, you probably won't have to do this if you can
  278. * have each board's accelerator registers mapped into separate address
  279. * spaces. NOTE: You'll still have to set pmdev->pmbCurrent, however,
  280. * for DrvRealizeBrush to work!
  281. *
  282. \**************************************************************************/
  283. VOID vSelectBoard(MDEV* pmdev, MULTI_BOARD* pmb)
  284. {
  285. LONG iOldHwBoard;
  286. ASSERTDD(pmdev->pmbCurrent != NULL, "Can't have NULL pmbCurrent");
  287. // LATER: Expand this to handle more than 8 Metheus boards.
  288. iOldHwBoard = pmdev->pmbCurrent->iHwBoard;
  289. if (pmb->iHwBoard != iOldHwBoard)
  290. {
  291. OUTP(0x220, iOldHwBoard); // Disable old board
  292. OUTP(0x220, (8 | pmb->iHwBoard)); // Enable new one
  293. pmdev->pmbCurrent = pmb; // Remember new board
  294. }
  295. }
  296. /******************************Public*Routine******************************\
  297. * bBoardCopy
  298. *
  299. * Given the BitBlt parameters in pbb, bitblt's the part of the rectangle
  300. * on the pmbSrc board that must bitblt'ed to the pmbDst board. Bails
  301. * out quickly if nothing actually has to be copied.
  302. *
  303. * Will do a screen-to-screen blt if pmbSrc and pmbDst are the same board;
  304. * otherwise it uses the psoTmp bitmap as temporary storage for transferring
  305. * between the two boards.
  306. *
  307. * NOTE: If your hardware allows you to have all the frame buffers mapped
  308. * into memory simultaneously, you can avoid the 'psoTmp' bitmap
  309. * allocation and extra copy!
  310. *
  311. \**************************************************************************/
  312. BOOL bBoardCopy(
  313. BITBLTDATA* pbb,
  314. SURFOBJ* psoTmp,
  315. MULTI_BOARD* pmbDst,
  316. MULTI_BOARD* pmbSrc)
  317. {
  318. BOOL b;
  319. RECTL rclDst;
  320. LONG dx;
  321. LONG dy;
  322. RECTL rclTmp;
  323. POINTL ptlSrc;
  324. // If there's really no source board, we're guaranteed not to
  325. // have to copy anything from it:
  326. if (pmbSrc == NULL)
  327. return(TRUE);
  328. dx = pbb->prclDst->left - pbb->pptlSrc->x;
  329. dy = pbb->prclDst->top - pbb->pptlSrc->y;
  330. // Pretend we're going to copy the entire source board's screen.
  331. // rclDst would be the destination rectangle:
  332. rclDst.left = pmbSrc->rcl.left + dx;
  333. rclDst.right = pmbSrc->rcl.right + dx;
  334. rclDst.top = pmbSrc->rcl.top + dy;
  335. rclDst.bottom = pmbSrc->rcl.bottom + dy;
  336. // We really want to copy only the part that overlaps the
  337. // destination board's screen:
  338. vIntersect(&pmbDst->rcl, &rclDst, &rclDst);
  339. // Plus we really only want to copy anything to what is contained
  340. // in the original destination rectangle:
  341. vIntersect(&pbb->rclBounds, &rclDst, &rclDst);
  342. // rclDst is now the destination rectangle for our call. We'll
  343. // need a temporary bitmap for copying, so compute its extents:
  344. rclTmp.left = 0;
  345. rclTmp.top = 0;
  346. rclTmp.right = rclDst.right - rclDst.left;
  347. rclTmp.bottom = rclDst.bottom - rclDst.top;
  348. // If it's empty, we're outta here:
  349. if ((rclTmp.right <= 0) || (rclTmp.bottom <= 0))
  350. return(TRUE);
  351. if (pmbDst == pmbSrc)
  352. {
  353. // If the source and destination are the same board, we don't
  354. // need a temporary bitmap:
  355. psoTmp = pmbSrc->pso;
  356. ptlSrc = *pbb->pptlSrc;
  357. }
  358. else
  359. {
  360. ASSERTDD(psoTmp != NULL, "Need non-null bitmap");
  361. ASSERTDD(psoTmp->sizlBitmap.cx >= rclTmp.right, "Bitmap too small in x");
  362. ASSERTDD(psoTmp->sizlBitmap.cy >= rclTmp.bottom, "Bitmap too small in y");
  363. // Figure out the upper-left source corner corresponding to our
  364. // upper-left destination corner:
  365. ptlSrc.x = rclDst.left - dx;
  366. ptlSrc.y = rclDst.top - dy;
  367. // Copy the rectangle from the source to the temporary bitmap:
  368. GO_BOARD(pbb->pmdev, pmbSrc);
  369. b = DrvCopyBits(psoTmp, pmbSrc->pso, NULL, NULL, &rclTmp, &ptlSrc);
  370. // Then get ready to do the copy from the temporary bitmap to
  371. // the destination:
  372. ptlSrc.x = pbb->prclDst->left - rclDst.left;
  373. ptlSrc.y = pbb->prclDst->top - rclDst.top;
  374. }
  375. pbb->pco->rclBounds = rclDst;
  376. GO_BOARD(pbb->pmdev, pmbDst);
  377. b &= DrvBitBlt(pmbDst->pso, psoTmp, pbb->psoMask, pbb->pco, pbb->pxlo,
  378. pbb->prclDst, &ptlSrc, pbb->pptlMask, pbb->pbo,
  379. pbb->pptlBrush, pbb->rop4);
  380. return(b);
  381. }
  382. /******************************Public*Routine******************************\
  383. * bBitBltBetweenBoards
  384. *
  385. * Handles screen-to-screen blts across multiple boards.
  386. *
  387. \**************************************************************************/
  388. BOOL bBitBltBetweenBoards(
  389. SURFOBJ* psoDst,
  390. SURFOBJ* psoSrc,
  391. SURFOBJ* psoMask,
  392. CLIPOBJ* pco,
  393. XLATEOBJ* pxlo,
  394. RECTL* prclDst,
  395. POINTL* pptlSrc,
  396. POINTL* pptlMask,
  397. BRUSHOBJ* pbo,
  398. POINTL* pptlBrush,
  399. ROP4 rop4,
  400. RECTL* prclUnion, // Rectangular union of source and destination
  401. MULTI_BOARD* pmbUnion) // Board containing upper-left corner of prclUnion
  402. {
  403. BOOL b = TRUE;
  404. BITBLTDATA bb;
  405. RECTL rclOriginalBounds;
  406. SIZEL sizlBoard;
  407. SIZEL sizlDst;
  408. SIZEL sizl;
  409. MULTI_BOARD* pmbSrc;
  410. MULTI_BOARD* pmbDst;
  411. LONG dx;
  412. LONG dy;
  413. RECTL rclStart;
  414. SURFOBJ* pso0 = NULL; // Initialize these first off in case we
  415. SURFOBJ* pso1 = NULL; // early-out
  416. SURFOBJ* pso2 = NULL;
  417. SURFOBJ* pso3 = NULL;
  418. HSURF hsurf0 = 0;
  419. HSURF hsurf1 = 0;
  420. bb.pmdev = (MDEV*) psoDst->dhpdev;
  421. bb.psoDst = psoDst;
  422. bb.psoSrc = psoSrc;
  423. bb.psoMask = psoMask;
  424. bb.pxlo = pxlo;
  425. bb.prclDst = prclDst;
  426. bb.pptlSrc = pptlSrc;
  427. bb.pptlMask = pptlMask;
  428. bb.pbo = pbo;
  429. bb.pptlBrush = pptlBrush;
  430. bb.rop4 = rop4;
  431. bb.pco = pco;
  432. if ((pco == NULL) || (pco->iDComplexity == DC_TRIVIAL))
  433. bb.pco = bb.pmdev->pco;
  434. vIntersect(&bb.pco->rclBounds, prclDst, &bb.rclBounds);
  435. rclOriginalBounds = bb.pco->rclBounds;
  436. sizlDst.cx = bb.rclBounds.right - bb.rclBounds.left;
  437. sizlDst.cy = bb.rclBounds.bottom - bb.rclBounds.top;
  438. // This really should never happen, but we'll be paranoid:
  439. if ((sizlDst.cx <= 0) || (sizlDst.cy <= 0))
  440. return(TRUE);
  441. // Compute delta from source to destination:
  442. dx = prclDst->left - pptlSrc->x;
  443. dy = prclDst->top - pptlSrc->y;
  444. // Figure out the size of a board:
  445. sizlBoard.cx = bb.pmdev->pmbUpperLeft->rcl.right;
  446. sizlBoard.cy = bb.pmdev->pmbUpperLeft->rcl.bottom;
  447. // We use temporary bitmaps as intermediaries for copying from one
  448. // board to another. Note that it is much more efficient to allocate
  449. // on the fly, rather than keeping a dedicated bitmap around that
  450. // would have to be swapped in and out.
  451. // When the destination is close to the source, we can accomplish
  452. // most of the blt using screen-to-screen copies, and will need
  453. // only two small temporary bitmaps to temporarily hold the bits
  454. // that must be transferred from one board to another:
  455. if ((abs(dx) < (sizlBoard.cx >> 1)) && (abs(dy) < (sizlBoard.cy >> 1)))
  456. {
  457. // Create a temporary bitmap for the horizontal delta only if
  458. // the blt actually spans boards in the x-direction:
  459. if ((dx != 0) && (prclUnion->right > pmbUnion->rcl.right))
  460. {
  461. sizl.cx = min(sizlDst.cx, abs(dx));
  462. sizl.cy = min(sizlDst.cy, sizlBoard.cy - abs(dy));
  463. hsurf0 = (HSURF) EngCreateBitmap(sizl, 0, bb.pmdev->iBitmapFormat,
  464. 0, NULL);
  465. pso1 = EngLockSurface(hsurf0);
  466. if (pso1 == NULL)
  467. return(FALSE);
  468. // Can use same temporary bitmap for section '3':
  469. pso3 = pso1;
  470. }
  471. // Similarly for the vertical delta:
  472. if ((dy != 0) && (prclUnion->bottom > pmbUnion->rcl.bottom))
  473. {
  474. sizl.cx = min(sizlDst.cx, sizlBoard.cx - abs(dx));
  475. sizl.cy = min(sizlDst.cy, abs(dy));
  476. hsurf1 = (HSURF) EngCreateBitmap(sizl, 0, bb.pmdev->iBitmapFormat,
  477. 0, NULL);
  478. pso2 = EngLockSurface(hsurf1);
  479. if (pso2 == NULL)
  480. {
  481. b = FALSE;
  482. goto OuttaHere;
  483. }
  484. }
  485. }
  486. else
  487. {
  488. // Make the bitmap the size of a board, or the size of the
  489. // destination rectangle, which ever is smaller:
  490. sizl.cx = min(sizlDst.cx, sizlBoard.cx);
  491. sizl.cy = min(sizlDst.cy, sizlBoard.cy);
  492. hsurf0 = (HSURF) EngCreateBitmap(sizl, 0, bb.pmdev->iBitmapFormat,
  493. 0, NULL);
  494. pso0 = EngLockSurface(hsurf0);
  495. if (pso0 == NULL)
  496. return(FALSE);
  497. pso1 = pso0;
  498. pso2 = pso0;
  499. pso3 = pso0;
  500. }
  501. if ((dx <= 0) && (dy <= 0))
  502. {
  503. // Move the rectangle up and to the left:
  504. // Find the board containing the upper-left corner of the destination:
  505. pmbDst = bb.pmdev->pmbUpperLeft;
  506. while (pmbDst->rcl.right <= bb.rclBounds.left)
  507. pmbDst = pmbDst->pmbRight;
  508. while (pmbDst->rcl.bottom <= bb.rclBounds.top)
  509. pmbDst = pmbDst->pmbDown;
  510. // Find the upper-left of the four source boards' rectangles which
  511. // can potentially overlap our destination board's rectangle:
  512. rclStart.left = pmbDst->rcl.left - dx;
  513. rclStart.top = pmbDst->rcl.top - dy;
  514. pmbSrc = pmbDst;
  515. while (pmbSrc->rcl.right <= rclStart.left)
  516. pmbSrc = pmbSrc->pmbRight;
  517. while (pmbSrc->rcl.bottom <= rclStart.top)
  518. pmbSrc = pmbSrc->pmbDown;
  519. while (TRUE)
  520. {
  521. b &= bBoardCopy(&bb, pso0, pmbDst, pmbSrc);
  522. b &= bBoardCopy(&bb, pso1, pmbDst, pmbSrc->pmbRight);
  523. b &= bBoardCopy(&bb, pso2, pmbDst, pmbSrc->pmbDown);
  524. if (pmbSrc->pmbDown != NULL)
  525. b &= bBoardCopy(&bb, pso3, pmbDst, pmbSrc->pmbDown->pmbRight);
  526. if (pmbDst->rcl.right < bb.rclBounds.right)
  527. {
  528. // Move right in the row of boards:
  529. pmbDst = pmbDst->pmbRight;
  530. pmbSrc = pmbSrc->pmbRight;
  531. }
  532. else
  533. {
  534. // We may be all done:
  535. if (pmbDst->rcl.bottom >= bb.rclBounds.bottom)
  536. break;
  537. // Nope, have to go down to left side of next lower row:
  538. while (pmbDst->rcl.left > bb.rclBounds.left)
  539. {
  540. pmbDst = pmbDst->pmbLeft;
  541. pmbSrc = pmbSrc->pmbLeft;
  542. }
  543. pmbDst = pmbDst->pmbDown;
  544. pmbSrc = pmbSrc->pmbDown;
  545. }
  546. }
  547. }
  548. else if ((dx >= 0) && (dy >= 0))
  549. {
  550. // Move the rectangle down and to the right:
  551. // Find the board containing the lower-right corner of the destination:
  552. pmbDst = bb.pmdev->pmbLowerRight;
  553. while (pmbDst->rcl.left >= bb.rclBounds.right)
  554. pmbDst = pmbDst->pmbLeft;
  555. while (pmbDst->rcl.top >= bb.rclBounds.bottom)
  556. pmbDst = pmbDst->pmbUp;
  557. // Find the lower-right of the four source boards' rectangles which
  558. // can potentially overlap our destination board's rectangle:
  559. rclStart.right = pmbDst->rcl.right - dx;
  560. rclStart.bottom = pmbDst->rcl.bottom - dy;
  561. pmbSrc = pmbDst;
  562. while (pmbSrc->rcl.left >= rclStart.right)
  563. pmbSrc = pmbSrc->pmbLeft;
  564. while (pmbSrc->rcl.top >= rclStart.bottom)
  565. pmbSrc = pmbSrc->pmbUp;
  566. while (TRUE)
  567. {
  568. b &= bBoardCopy(&bb, pso0, pmbDst, pmbSrc);
  569. b &= bBoardCopy(&bb, pso1, pmbDst, pmbSrc->pmbLeft);
  570. b &= bBoardCopy(&bb, pso2, pmbDst, pmbSrc->pmbUp);
  571. if (pmbSrc->pmbUp != NULL)
  572. b &= bBoardCopy(&bb, pso3, pmbDst, pmbSrc->pmbUp->pmbLeft);
  573. if (pmbDst->rcl.left > bb.rclBounds.left)
  574. {
  575. // Move left in the row of boards:
  576. pmbDst = pmbDst->pmbLeft;
  577. pmbSrc = pmbSrc->pmbLeft;
  578. }
  579. else
  580. {
  581. // We may be all done:
  582. if (pmbDst->rcl.top <= bb.rclBounds.top)
  583. break;
  584. // Nope, have to go up to right side of next upper row:
  585. while (pmbDst->rcl.right < bb.rclBounds.right)
  586. {
  587. pmbDst = pmbDst->pmbRight;
  588. pmbSrc = pmbSrc->pmbRight;
  589. }
  590. pmbDst = pmbDst->pmbUp;
  591. pmbSrc = pmbSrc->pmbUp;
  592. }
  593. }
  594. }
  595. else if ((dx >= 0) && (dy <= 0))
  596. {
  597. // Move the rectangle up and to the right:
  598. // Find the board containing the upper-right corner of the destination:
  599. pmbDst = bb.pmdev->pmbUpperRight;
  600. while (pmbDst->rcl.left >= bb.rclBounds.right)
  601. pmbDst = pmbDst->pmbLeft;
  602. while (pmbDst->rcl.bottom <= bb.rclBounds.top)
  603. pmbDst = pmbDst->pmbDown;
  604. // Find the upper-right of the four source boards' rectangles which
  605. // can potentially overlap our destination board's rectangle:
  606. rclStart.right = pmbDst->rcl.right - dx;
  607. rclStart.top = pmbDst->rcl.top - dy;
  608. pmbSrc = pmbDst;
  609. while (pmbSrc->rcl.left >= rclStart.right)
  610. pmbSrc = pmbSrc->pmbLeft;
  611. while (pmbSrc->rcl.bottom <= rclStart.top)
  612. pmbSrc = pmbSrc->pmbDown;
  613. while (TRUE)
  614. {
  615. b &= bBoardCopy(&bb, pso0, pmbDst, pmbSrc);
  616. b &= bBoardCopy(&bb, pso1, pmbDst, pmbSrc->pmbLeft);
  617. b &= bBoardCopy(&bb, pso2, pmbDst, pmbSrc->pmbDown);
  618. if (pmbSrc->pmbDown != NULL)
  619. b &= bBoardCopy(&bb, pso3, pmbDst, pmbSrc->pmbDown->pmbLeft);
  620. if (pmbDst->rcl.left > bb.rclBounds.left)
  621. {
  622. // Move left in the row of boards:
  623. pmbDst = pmbDst->pmbLeft;
  624. pmbSrc = pmbSrc->pmbLeft;
  625. }
  626. else
  627. {
  628. // We may be all done:
  629. if (pmbDst->rcl.bottom >= bb.rclBounds.bottom)
  630. break;
  631. // Nope, have to go down to right side of next lower row:
  632. while (pmbDst->rcl.right < bb.rclBounds.right)
  633. {
  634. pmbDst = pmbDst->pmbRight;
  635. pmbSrc = pmbSrc->pmbRight;
  636. }
  637. pmbDst = pmbDst->pmbDown;
  638. pmbSrc = pmbSrc->pmbDown;
  639. }
  640. }
  641. }
  642. else
  643. {
  644. // Move the rectangle down and to the left:
  645. // Find the board containing the lower-left corner of the destination:
  646. pmbDst = bb.pmdev->pmbLowerLeft;
  647. while (pmbDst->rcl.right <= bb.rclBounds.left)
  648. pmbDst = pmbDst->pmbRight;
  649. while (pmbDst->rcl.top >= bb.rclBounds.bottom)
  650. pmbDst = pmbDst->pmbUp;
  651. // Find the lower-left of the four source boards' rectangles which
  652. // can potentially overlap our destination board's rectangle:
  653. rclStart.left = pmbDst->rcl.left - dx;
  654. rclStart.bottom = pmbDst->rcl.bottom - dy;
  655. pmbSrc = pmbDst;
  656. while (pmbSrc->rcl.right <= rclStart.left)
  657. pmbSrc = pmbSrc->pmbRight;
  658. while (pmbSrc->rcl.top >= rclStart.bottom)
  659. pmbSrc = pmbSrc->pmbUp;
  660. while (TRUE)
  661. {
  662. b &= bBoardCopy(&bb, pso0, pmbDst, pmbSrc);
  663. b &= bBoardCopy(&bb, pso1, pmbDst, pmbSrc->pmbRight);
  664. b &= bBoardCopy(&bb, pso2, pmbDst, pmbSrc->pmbUp);
  665. if (pmbSrc->pmbUp != NULL)
  666. b &= bBoardCopy(&bb, pso3, pmbDst, pmbSrc->pmbUp->pmbRight);
  667. if (pmbDst->rcl.right < bb.rclBounds.right)
  668. {
  669. // Move right in the row of boards:
  670. pmbDst = pmbDst->pmbRight;
  671. pmbSrc = pmbSrc->pmbRight;
  672. }
  673. else
  674. {
  675. // We may be all done:
  676. if (pmbDst->rcl.top <= bb.rclBounds.top)
  677. break;
  678. // Nope, have to up down to left side of next upper row:
  679. while (pmbDst->rcl.left > bb.rclBounds.left)
  680. {
  681. pmbDst = pmbDst->pmbLeft;
  682. pmbSrc = pmbSrc->pmbLeft;
  683. }
  684. pmbDst = pmbDst->pmbUp;
  685. pmbSrc = pmbSrc->pmbUp;
  686. }
  687. }
  688. }
  689. GO_HOME(bb.pmdev);
  690. bb.pco->rclBounds = rclOriginalBounds;
  691. OuttaHere:
  692. // In one case, pso0 == pso1 == pso2 == pso3, and we don't want to
  693. // unlock the same surface twice:
  694. if (pso1 != pso2)
  695. EngUnlockSurface(pso1);
  696. EngUnlockSurface(pso2);
  697. EngDeleteSurface(hsurf0);
  698. EngDeleteSurface(hsurf1);
  699. return(b);
  700. }
  701. /******************************Public*Routine******************************\
  702. * MulGetModes
  703. *
  704. \**************************************************************************/
  705. ULONG MulGetModes(
  706. HANDLE hDriver,
  707. ULONG cjSize,
  708. DEVMODEW* pdm)
  709. {
  710. ULONG ulRet;
  711. ulRet = DrvGetModes(hDriver, cjSize, pdm);
  712. return(ulRet);
  713. }
  714. /******************************Public*Routine******************************\
  715. * MulEnablePDEV
  716. *
  717. \**************************************************************************/
  718. DHPDEV MulEnablePDEV(
  719. DEVMODEW* pDevmode,
  720. PWSTR pwszLogAddress,
  721. ULONG cPatterns,
  722. HSURF* ahsurfPatterns,
  723. ULONG cjGdiInfo,
  724. ULONG* pGdiInfo,
  725. ULONG cjDevInfo,
  726. DEVINFO* pDevInfo,
  727. HDEV hdev,
  728. PWSTR pwszDeviceName,
  729. HANDLE hDriver)
  730. {
  731. MDEV* pmdev; // Multi-board PDEV
  732. PDEV* ppdev; // Per-board PDEV
  733. MULTI_BOARD* pmb;
  734. LONG cx;
  735. LONG cy;
  736. // Note that we depend on the zero initialization:
  737. pmdev = (MDEV*) EngAllocMem(FL_ZERO_MEMORY, sizeof(MDEV), ALLOC_TAG);
  738. if (pmdev == NULL)
  739. goto ReturnFailure0;
  740. if (!bVeryTemporaryInitializationCode(pmdev))
  741. goto ReturnFailure1;
  742. // For every board, we'll create our own PDEV and surface:
  743. for (pmb = pmdev->pmb; pmb != NULL; pmb = pmb->pmbNext)
  744. {
  745. // Initialize each board and create a surface to go with it:
  746. ppdev = (PDEV*) DrvEnablePDEV(pDevmode, pwszLogAddress,
  747. cPatterns, ahsurfPatterns,
  748. cjGdiInfo, pGdiInfo,
  749. cjDevInfo, pDevInfo,
  750. hdev, pwszDeviceName,
  751. hDriver);
  752. if (ppdev == NULL)
  753. goto ReturnFailure1;
  754. pmb->ppdev = ppdev;
  755. }
  756. // Choose a board, any board:
  757. pmb = pmdev->pmbLowerLeft;
  758. // Get a copy of what functions we're supposed to hook, sans
  759. // HOOK_STRETCHBLT because I can't be bothered to write its
  760. // MulStretchBlt function, and HOOK_SYNCHRONIZE because we have
  761. // to be a device-managed surface (so DrvSynchronize is unneeded):
  762. pmdev->flHooks = pmb->ppdev->flHooks
  763. & ~(HOOK_STRETCHBLT | HOOK_SYNCHRONIZE);
  764. pmdev->iBitmapFormat = pmb->ppdev->iBitmapFormat;
  765. // As part of our hard-coded initialization hack, we will simply
  766. // take whatever resolution was requested via the Control Panel
  767. // and create a two board virtual desktop, where the screens are
  768. // side-by-side.
  769. //
  770. // The DrvEnablePDEV function for any board has already figured
  771. // out what the requested mode was:
  772. cx = ((GDIINFO*) pGdiInfo)->ulHorzRes;
  773. cy = ((GDIINFO*) pGdiInfo)->ulVertRes;
  774. // Set up bounds for left board:
  775. pmb->rcl.left = 0;
  776. pmb->rcl.top = 0;
  777. pmb->rcl.right = cx;
  778. pmb->rcl.bottom = cy;
  779. // Set up bounds for right board:
  780. pmb = pmb->pmbRight;
  781. pmb->rcl.left = cx;
  782. pmb->rcl.top = 0;
  783. pmb->rcl.right = 2 * cx;
  784. pmb->rcl.bottom = cy;
  785. // Adjust the stuff we return back to GDI:
  786. // ((GDIINFO*) pGdiInfo)->ulDesktopHorzRes *= 2;
  787. // ((GDIINFO*) pGdiInfo)->ulHorzSize *= 2;
  788. // With the Metheus board, since only one board can be mapped in
  789. // at one time, we cannot allow asynchronous pointers:
  790. pDevInfo->flGraphicsCaps &= ~(GCAPS_ASYNCMOVE | GCAPS_ASYNCCHANGE);
  791. return((DHPDEV) pmdev);
  792. ReturnFailure1:
  793. MulDisablePDEV((DHPDEV) pmdev);
  794. ReturnFailure0:
  795. DISPDBG((0, "Failed MulEnablePDEV"));
  796. return(0);
  797. }
  798. /******************************Public*Routine******************************\
  799. * MulCompletePDEV
  800. *
  801. \**************************************************************************/
  802. VOID MulCompletePDEV(
  803. DHPDEV dhpdev,
  804. HDEV hdev)
  805. {
  806. MDEV* pmdev;
  807. MULTI_BOARD* pmb;
  808. pmdev = (MDEV*) dhpdev;
  809. pmdev->hdev = hdev;
  810. for (pmb = pmdev->pmb; pmb != NULL; pmb = pmb->pmbNext)
  811. {
  812. GO_BOARD(pmdev, pmb);
  813. DrvCompletePDEV((DHPDEV) pmb->ppdev, hdev);
  814. }
  815. }
  816. /******************************Public*Routine******************************\
  817. * MulEnableSurface
  818. *
  819. \**************************************************************************/
  820. HSURF MulEnableSurface(DHPDEV dhpdev)
  821. {
  822. MDEV* pmdev;
  823. MULTI_BOARD* pmb;
  824. SIZEL sizlVirtual;
  825. HSURF hsurfBoard; // Gnarly, dude!
  826. SURFOBJ* psoBoard;
  827. DSURF* pdsurfBoard;
  828. HSURF hsurfVirtual;
  829. CLIPOBJ* pco;
  830. pmdev = (MDEV*) dhpdev;
  831. for (pmb = pmdev->pmb; pmb != NULL; pmb = pmb->pmbNext)
  832. {
  833. GO_BOARD(pmdev, pmb);
  834. hsurfBoard = DrvEnableSurface((DHPDEV) pmb->ppdev);
  835. if (hsurfBoard == 0)
  836. goto ReturnFailure;
  837. pmb->hsurf = hsurfBoard;
  838. // Every time we draw on a particular board, we'll refer to it
  839. // using this surface:
  840. psoBoard = EngLockSurface(hsurfBoard);
  841. if (psoBoard == NULL)
  842. goto ReturnFailure;
  843. pmb->pso = psoBoard;
  844. // There are a few things in the board's data instances that we
  845. // have to modify:
  846. pdsurfBoard = (DSURF*) psoBoard->dhsurf;
  847. pmb->ppdev->iBoard = pmb->iBoard;
  848. pdsurfBoard->poh->x = -pmb->rcl.left;
  849. pdsurfBoard->poh->y = -pmb->rcl.top;
  850. // This is sort of a hack. Whenever we pass a call on to a board's
  851. // Drv function using 'pmb->pso', it has to be able to retrieve
  852. // it's own PDEV pointer from 'dhpdev':
  853. pmb->pso->dhpdev = (DHPDEV) pmb->ppdev;
  854. }
  855. // Now create the surface which the engine will use to refer to our
  856. // entire multi-board virtual screen:
  857. sizlVirtual.cx = pmdev->pmbLowerRight->rcl.right;
  858. sizlVirtual.cy = pmdev->pmbLowerRight->rcl.bottom;
  859. hsurfVirtual = EngCreateDeviceSurface((DHSURF) pmdev, sizlVirtual,
  860. pmdev->iBitmapFormat);
  861. if (hsurfVirtual == 0)
  862. goto ReturnFailure;
  863. pmdev->hsurf = hsurfVirtual;
  864. if (!EngAssociateSurface(hsurfVirtual, pmdev->hdev, pmdev->flHooks))
  865. goto ReturnFailure;
  866. // Create a temporary clip object that we can use when a drawing
  867. // operation spans multiple boards:
  868. pco = EngCreateClip();
  869. if (pco == NULL)
  870. goto ReturnFailure;
  871. pmdev->pco = pco;
  872. pmdev->pco->iDComplexity = DC_RECT;
  873. pmdev->pco->iMode = TC_RECTANGLES;
  874. pmdev->pco->rclBounds.left = 0;
  875. pmdev->pco->rclBounds.top = 0;
  876. pmdev->pco->rclBounds.right = pmdev->pmbLowerRight->rcl.right;
  877. pmdev->pco->rclBounds.bottom = pmdev->pmbLowerRight->rcl.bottom;
  878. return(hsurfVirtual);
  879. ReturnFailure:
  880. MulDisableSurface((DHPDEV) pmdev);
  881. DISPDBG((0, "Failed MulEnableSurface"));
  882. return(0);
  883. }
  884. /******************************Public*Routine******************************\
  885. * MulStrokePath
  886. *
  887. \**************************************************************************/
  888. BOOL MulStrokePath(
  889. SURFOBJ* pso,
  890. PATHOBJ* ppo,
  891. CLIPOBJ* pco,
  892. XFORMOBJ* pxo,
  893. BRUSHOBJ* pbo,
  894. POINTL* pptlBrush,
  895. LINEATTRS* pla,
  896. MIX mix)
  897. {
  898. RECTFX rcfxBounds;
  899. RECTL rclBounds;
  900. MDEV* pmdev;
  901. RECTL rclOriginalBounds;
  902. MULTI_BOARD* pmb;
  903. BOOL b;
  904. FLOAT_LONG elStyleState;
  905. // Get the path bounds and make it lower-right exclusive:
  906. PATHOBJ_vGetBounds(ppo, &rcfxBounds);
  907. rclBounds.left = (rcfxBounds.xLeft >> 4);
  908. rclBounds.top = (rcfxBounds.yTop >> 4);
  909. rclBounds.right = (rcfxBounds.xRight >> 4) + 2;
  910. rclBounds.bottom = (rcfxBounds.yBottom >> 4) + 2;
  911. pmdev = (MDEV*) pso->dhpdev;
  912. if (bFindBoard(pmdev, &rclBounds, &pmb))
  913. {
  914. GO_BOARD(pmdev, pmb);
  915. b = DrvStrokePath(pmb->pso, ppo, pco, pxo, pbo, pptlBrush, pla, mix);
  916. }
  917. else
  918. {
  919. if ((pco == NULL) || (pco->iDComplexity == DC_TRIVIAL))
  920. {
  921. // If the CLIPOBJ doesn't have at least DC_RECT complexity,
  922. // substitute one that does:
  923. pco = pmdev->pco;
  924. }
  925. rclOriginalBounds = pco->rclBounds;
  926. elStyleState = pla->elStyleState;
  927. b = TRUE;
  928. do {
  929. // For each board, make sure the style state gets reset and
  930. // the path enumeration gets restarted:
  931. pla->elStyleState = elStyleState;
  932. PATHOBJ_vEnumStart(ppo);
  933. if (bIntersect(&rclOriginalBounds, &pmb->rcl, &pco->rclBounds))
  934. {
  935. GO_BOARD(pmdev, pmb);
  936. b &= DrvStrokePath(pmb->pso, ppo, pco, pxo, pbo, pptlBrush, pla,
  937. mix);
  938. }
  939. } while (bNextBoard(&rclBounds, &pmb));
  940. // Restore the original clip bounds:
  941. pco->rclBounds = rclOriginalBounds;
  942. }
  943. GO_HOME(pmdev);
  944. return(b);
  945. }
  946. /******************************Public*Routine******************************\
  947. * MulFillPath
  948. *
  949. \**************************************************************************/
  950. BOOL MulFillPath(
  951. SURFOBJ* pso,
  952. PATHOBJ* ppo,
  953. CLIPOBJ* pco,
  954. BRUSHOBJ* pbo,
  955. POINTL* pptlBrush,
  956. MIX mix,
  957. FLONG flOptions)
  958. {
  959. RECTFX rcfxBounds;
  960. RECTL rclBounds;
  961. MDEV* pmdev;
  962. RECTL rclOriginalBounds;
  963. MULTI_BOARD* pmb;
  964. BOOL b;
  965. // Get the path bounds and make it lower-right exclusive:
  966. PATHOBJ_vGetBounds(ppo, &rcfxBounds);
  967. rclBounds.left = (rcfxBounds.xLeft >> 4);
  968. rclBounds.top = (rcfxBounds.yTop >> 4);
  969. rclBounds.right = (rcfxBounds.xRight >> 4) + 2;
  970. rclBounds.bottom = (rcfxBounds.yBottom >> 4) + 2;
  971. pmdev = (MDEV*) pso->dhpdev;
  972. if (bFindBoard(pmdev, &rclBounds, &pmb))
  973. {
  974. GO_BOARD(pmdev, pmb);
  975. b = DrvFillPath(pmb->pso, ppo, pco, pbo, pptlBrush, mix, flOptions);
  976. }
  977. else
  978. {
  979. if ((pco == NULL) || (pco->iDComplexity == DC_TRIVIAL))
  980. {
  981. // If the CLIPOBJ doesn't have at least DC_RECT complexity,
  982. // substitute one that does:
  983. pco = pmdev->pco;
  984. }
  985. rclOriginalBounds = pco->rclBounds;
  986. b = TRUE;
  987. do {
  988. // Make sure we restart the path enumeration if need be:
  989. PATHOBJ_vEnumStart(ppo);
  990. if (bIntersect(&rclOriginalBounds, &pmb->rcl, &pco->rclBounds))
  991. {
  992. GO_BOARD(pmdev, pmb);
  993. b &= DrvFillPath(pmb->pso, ppo, pco, pbo, pptlBrush, mix,
  994. flOptions);
  995. }
  996. } while (bNextBoard(&rclBounds, &pmb));
  997. // Restore the original clip bounds:
  998. pco->rclBounds = rclOriginalBounds;
  999. }
  1000. GO_HOME(pmdev);
  1001. return(b);
  1002. }
  1003. /******************************Public*Routine******************************\
  1004. * MulBitBlt
  1005. *
  1006. \**************************************************************************/
  1007. BOOL MulBitBlt(
  1008. SURFOBJ* psoDst,
  1009. SURFOBJ* psoSrc,
  1010. SURFOBJ* psoMask,
  1011. CLIPOBJ* pco,
  1012. XLATEOBJ* pxlo,
  1013. RECTL* prclDst,
  1014. POINTL* pptlSrc,
  1015. POINTL* pptlMask,
  1016. BRUSHOBJ* pbo,
  1017. POINTL* pptlBrush,
  1018. ROP4 rop4)
  1019. {
  1020. BOOL bFromScreen;
  1021. BOOL bToScreen;
  1022. MDEV* pmdev;
  1023. MULTI_BOARD* pmb;
  1024. RECTL rclOriginalBounds;
  1025. BOOL b;
  1026. RECTL rclBounds;
  1027. LONG xOffset;
  1028. LONG yOffset;
  1029. RECTL rclDstBounds;
  1030. RECTL rclDst;
  1031. bFromScreen = ((psoSrc != NULL) && (psoSrc->iType == STYPE_DEVICE));
  1032. bToScreen = ((psoDst != NULL) && (psoDst->iType == STYPE_DEVICE));
  1033. // We copy the prclDst rectangle here because sometimes GDI will
  1034. // simply point prclDst to the same rectangle in pco->rclBounds,
  1035. // and we'll be mucking with pco->rclBounds...
  1036. rclDst = *prclDst;
  1037. if (bToScreen && bFromScreen)
  1038. {
  1039. ///////////////////////////////////////////////////////////////
  1040. // Screen-to-screen
  1041. ///////////////////////////////////////////////////////////////
  1042. pmdev = (MDEV*) psoDst->dhpdev;
  1043. // rclBounds is the union of the source and destination rectangles:
  1044. rclBounds.left = min(rclDst.left, pptlSrc->x);
  1045. rclBounds.top = min(rclDst.top, pptlSrc->y);
  1046. rclBounds.right = max(rclDst.right,
  1047. pptlSrc->x + (rclDst.right - rclDst.left));
  1048. rclBounds.bottom = max(rclDst.bottom,
  1049. pptlSrc->y + (rclDst.bottom - rclDst.top));
  1050. if (bFindBoard(pmdev, &rclBounds, &pmb))
  1051. {
  1052. GO_BOARD(pmdev, pmb);
  1053. b = DrvBitBlt(pmb->pso, pmb->pso, psoMask, pco, pxlo, &rclDst,
  1054. pptlSrc, pptlMask, pbo, pptlBrush, rop4);
  1055. }
  1056. else
  1057. {
  1058. return(bBitBltBetweenBoards(psoDst, psoSrc, psoMask, pco, pxlo,
  1059. &rclDst, pptlSrc, pptlMask, pbo,
  1060. pptlBrush, rop4, &rclBounds, pmb));
  1061. }
  1062. }
  1063. else if (bToScreen)
  1064. {
  1065. ///////////////////////////////////////////////////////////////
  1066. // To-screen
  1067. ///////////////////////////////////////////////////////////////
  1068. pmdev = (MDEV*) psoDst->dhpdev;
  1069. if (bFindBoard(pmdev, &rclDst, &pmb))
  1070. {
  1071. GO_BOARD(pmdev, pmb);
  1072. b = DrvBitBlt(pmb->pso, psoSrc, psoMask, pco, pxlo, &rclDst,
  1073. pptlSrc, pptlMask, pbo, pptlBrush, rop4);
  1074. }
  1075. else
  1076. {
  1077. if ((pco == NULL) || (pco->iDComplexity == DC_TRIVIAL))
  1078. {
  1079. // If the CLIPOBJ doesn't have at least DC_RECT complexity,
  1080. // substitute one that does:
  1081. pco = pmdev->pco;
  1082. }
  1083. rclOriginalBounds = pco->rclBounds;
  1084. b = TRUE;
  1085. do {
  1086. if (bIntersect(&rclOriginalBounds, &pmb->rcl, &pco->rclBounds))
  1087. {
  1088. GO_BOARD(pmdev, pmb);
  1089. b &= DrvBitBlt(pmb->pso, psoSrc, psoMask, pco, pxlo, &rclDst,
  1090. pptlSrc, pptlMask, pbo, pptlBrush, rop4);
  1091. }
  1092. } while (bNextBoard(&rclDst, &pmb));
  1093. // Restore the original clip bounds:
  1094. pco->rclBounds = rclOriginalBounds;
  1095. }
  1096. }
  1097. else
  1098. {
  1099. ///////////////////////////////////////////////////////////////
  1100. // From-screen
  1101. ///////////////////////////////////////////////////////////////
  1102. pmdev = (MDEV*) psoSrc->dhpdev;
  1103. // rclBounds is the source rectangle:
  1104. rclBounds.left = pptlSrc->x;
  1105. rclBounds.top = pptlSrc->y;
  1106. rclBounds.right = pptlSrc->x + (rclDst.right - rclDst.left);
  1107. rclBounds.bottom = pptlSrc->y + (rclDst.bottom - rclDst.top);
  1108. if (bFindBoard(pmdev, &rclBounds, &pmb))
  1109. {
  1110. GO_BOARD(pmdev, pmb);
  1111. b = DrvBitBlt(psoDst, pmb->pso, psoMask, pco, pxlo, &rclDst,
  1112. pptlSrc, pptlMask, pbo, pptlBrush, rop4);
  1113. }
  1114. else
  1115. {
  1116. if ((pco == NULL) || (pco->iDComplexity == DC_TRIVIAL))
  1117. {
  1118. // If the CLIPOBJ doesn't have at least DC_RECT complexity,
  1119. // substitute one that does:
  1120. pco = pmdev->pco;
  1121. }
  1122. rclOriginalBounds = pco->rclBounds;
  1123. // Offset to transform from source rectangle to destination
  1124. // rectangle:
  1125. xOffset = rclDst.left - pptlSrc->x;
  1126. yOffset = rclDst.top - pptlSrc->y;
  1127. b = TRUE;
  1128. do {
  1129. // Since the screen is the source, but the clip bounds applies
  1130. // to the destination, we have to convert our board clipping
  1131. // information to destination coordinates:
  1132. rclDstBounds.left = pmb->rcl.left + xOffset;
  1133. rclDstBounds.right = pmb->rcl.right + xOffset;
  1134. rclDstBounds.top = pmb->rcl.top + yOffset;
  1135. rclDstBounds.bottom = pmb->rcl.bottom + yOffset;
  1136. if (bIntersect(&rclOriginalBounds, &rclDstBounds, &pco->rclBounds))
  1137. {
  1138. GO_BOARD(pmdev, pmb);
  1139. b &= DrvBitBlt(psoDst, pmb->pso, psoMask, pco, pxlo, &rclDst,
  1140. pptlSrc, pptlMask, pbo, pptlBrush, rop4);
  1141. }
  1142. } while (bNextBoard(&rclBounds, &pmb));
  1143. // Restore the original clip bounds:
  1144. pco->rclBounds = rclOriginalBounds;
  1145. }
  1146. }
  1147. GO_HOME(pmdev);
  1148. return(b);
  1149. }
  1150. /******************************Public*Routine******************************\
  1151. * MulDisablePDEV
  1152. *
  1153. * Note: May be called before MulEnablePDEV successfully completed!
  1154. *
  1155. \**************************************************************************/
  1156. VOID MulDisablePDEV(DHPDEV dhpdev)
  1157. {
  1158. MULTI_BOARD* pmb;
  1159. MDEV* pmdev;
  1160. pmdev = (MDEV*) dhpdev;
  1161. for (pmb = pmdev->pmb; pmb != NULL; pmb = pmb->pmbNext)
  1162. {
  1163. if (pmb->ppdev != NULL)
  1164. {
  1165. GO_BOARD(pmdev, pmb);
  1166. DrvDisablePDEV((DHPDEV) pmb->ppdev);
  1167. }
  1168. }
  1169. GO_HOME(pmdev);
  1170. for (pmb = pmdev->pmb; pmb != NULL; pmb = pmb->pmbNext)
  1171. {
  1172. EngFreeMem(pmb); // Undo 'bVeryTemporaryInitializationCode'
  1173. } // allocation
  1174. EngFreeMem(pmdev);
  1175. }
  1176. /******************************Public*Routine******************************\
  1177. * MulDisableSurface
  1178. *
  1179. * Note: May be called before MulEnableSurface successfully completed!
  1180. *
  1181. \**************************************************************************/
  1182. VOID MulDisableSurface(DHPDEV dhpdev)
  1183. {
  1184. MULTI_BOARD* pmb;
  1185. MDEV* pmdev;
  1186. pmdev = (MDEV*) dhpdev;
  1187. if (pmdev->pco != NULL)
  1188. EngDeleteClip(pmdev->pco);
  1189. EngDeleteSurface(pmdev->hsurf);
  1190. for (pmb = pmdev->pmb; pmb != NULL; pmb = pmb->pmbNext)
  1191. {
  1192. GO_BOARD(pmdev, pmb);
  1193. EngUnlockSurface(pmb->pso);
  1194. DrvDisableSurface((DHPDEV) pmb->ppdev);
  1195. }
  1196. GO_HOME(pmdev);
  1197. }
  1198. /******************************Public*Routine******************************\
  1199. * MulAssertMode
  1200. *
  1201. \**************************************************************************/
  1202. BOOL MulAssertMode(
  1203. DHPDEV dhpdev,
  1204. BOOL bEnable)
  1205. {
  1206. MDEV* pmdev;
  1207. MULTI_BOARD* pmb;
  1208. pmdev = (MDEV*) dhpdev;
  1209. if (!bEnable)
  1210. {
  1211. // When switching to full-screen mode, PatBlt blackness over
  1212. // all the inactive screens (otherwise it looks goofy when
  1213. // the desktop is frozen on the inactive screens and the user
  1214. // can't do anything with it):
  1215. for (pmb = pmdev->pmb; pmb != NULL; pmb = pmb->pmbNext)
  1216. {
  1217. if (pmb != pmdev->pmbHome)
  1218. {
  1219. GO_BOARD(pmdev, pmb);
  1220. DrvBitBlt(pmb->pso, NULL, NULL, NULL, NULL, &pmb->rcl, NULL,
  1221. NULL, NULL, NULL, 0);
  1222. }
  1223. }
  1224. }
  1225. // We use the 'home' board for full-screen switching:
  1226. GO_BOARD(pmdev, pmdev->pmbHome);
  1227. return (DrvAssertMode((DHPDEV) pmdev->pmbHome->ppdev, bEnable));
  1228. }
  1229. /******************************Public*Routine******************************\
  1230. * MulMovePointer
  1231. *
  1232. \**************************************************************************/
  1233. VOID MulMovePointer(
  1234. SURFOBJ* pso,
  1235. LONG x,
  1236. LONG y,
  1237. RECTL* prcl)
  1238. {
  1239. MDEV* pmdev;
  1240. MULTI_BOARD* pmbPointer;
  1241. RECTL rclPointer;
  1242. pmdev = (MDEV*) pso->dhpdev;
  1243. pmbPointer = pmdev->pmbPointer;
  1244. if (pmbPointer != NULL)
  1245. {
  1246. // The most common case is when the pointer is moved to a spot
  1247. // on the same board:
  1248. if ((x >= pmbPointer->rcl.left) &&
  1249. (x < pmbPointer->rcl.right) &&
  1250. (y >= pmbPointer->rcl.top) &&
  1251. (y < pmbPointer->rcl.bottom))
  1252. {
  1253. GO_BOARD(pmdev, pmbPointer);
  1254. DrvMovePointer(pmbPointer->pso, x, y, prcl);
  1255. GO_HOME(pmdev);
  1256. return;
  1257. }
  1258. // Tell the old board to erase its cursor:
  1259. GO_BOARD(pmdev, pmbPointer);
  1260. DrvMovePointer(pmbPointer->pso, -1, -1, NULL);
  1261. }
  1262. if (x == -1)
  1263. {
  1264. pmdev->pmbPointer = NULL;
  1265. GO_HOME(pmdev);
  1266. return;
  1267. }
  1268. // Find the new board and tell it to draw its new cursor:
  1269. rclPointer.left = x;
  1270. rclPointer.right = x;
  1271. rclPointer.top = y;
  1272. rclPointer.bottom = y;
  1273. bFindBoard(pmdev, &rclPointer, &pmbPointer);
  1274. GO_BOARD(pmdev, pmbPointer);
  1275. DrvMovePointer(pmbPointer->pso, x, y, prcl);
  1276. pmdev->pmbPointer = pmbPointer;
  1277. GO_HOME(pmdev);
  1278. }
  1279. /******************************Public*Routine******************************\
  1280. * MulSetPointerShape
  1281. *
  1282. \**************************************************************************/
  1283. ULONG MulSetPointerShape(
  1284. SURFOBJ* pso,
  1285. SURFOBJ* psoMask,
  1286. SURFOBJ* psoColor,
  1287. XLATEOBJ* pxlo,
  1288. LONG xHot,
  1289. LONG yHot,
  1290. LONG x,
  1291. LONG y,
  1292. RECTL* prcl,
  1293. FLONG fl)
  1294. {
  1295. MULTI_BOARD* pmb;
  1296. MDEV* pmdev;
  1297. ULONG ulRetPrevious = (ULONG) -1;
  1298. ULONG ulRet;
  1299. RECTL rclPointer;
  1300. MULTI_BOARD* pmbPointer; // Board on which cursor is visible
  1301. pmdev = (MDEV*) pso->dhpdev;
  1302. // Find out which board that the cursor is visible on, if any:
  1303. pmbPointer = NULL;
  1304. if (x != 1)
  1305. {
  1306. rclPointer.left = x;
  1307. rclPointer.right = x;
  1308. rclPointer.top = y;
  1309. rclPointer.bottom = y;
  1310. bFindBoard(pmdev, &rclPointer, &pmbPointer);
  1311. }
  1312. pmdev->pmbPointer = pmbPointer;
  1313. // LATER: Fix the case for when some boards may fail the call, and others
  1314. // won't.
  1315. for (pmb = pmdev->pmb; pmb != NULL; pmb = pmb->pmbNext)
  1316. {
  1317. // We notify all boards of the new cursor shape, but only the board
  1318. // on which the cursor is visible is told to draw it:
  1319. GO_BOARD(pmdev, pmb);
  1320. if (pmb == pmbPointer)
  1321. {
  1322. ulRet = DrvSetPointerShape(pmb->pso, psoMask, psoColor, pxlo,
  1323. xHot, yHot, x, y, prcl, fl);
  1324. }
  1325. else
  1326. {
  1327. ulRet = DrvSetPointerShape(pmb->pso, psoMask, psoColor, pxlo,
  1328. xHot, yHot, -1, y, NULL, fl);
  1329. }
  1330. if ((ulRetPrevious != (ULONG) -1) && (ulRetPrevious != ulRet))
  1331. {
  1332. RIP("MulSetPointerShape not all DrvSetPointerShapes same\n");
  1333. }
  1334. ulRetPrevious = ulRet;
  1335. }
  1336. GO_HOME(pmdev);
  1337. return(ulRetPrevious);
  1338. }
  1339. /******************************Public*Routine******************************\
  1340. * MulDitherColor
  1341. *
  1342. \**************************************************************************/
  1343. ULONG MulDitherColor(
  1344. DHPDEV dhpdev,
  1345. ULONG iMode,
  1346. ULONG rgb,
  1347. ULONG* pul)
  1348. {
  1349. PDEV* ppdev;
  1350. ULONG ulRet;
  1351. // Let the first board's driver do the dithering:
  1352. ppdev = ((MDEV*) dhpdev)->pmb->ppdev;
  1353. ulRet = DrvDitherColor((DHPDEV) ppdev, iMode, rgb, pul);
  1354. return(ulRet);
  1355. }
  1356. /******************************Public*Routine******************************\
  1357. * MulSetPalette
  1358. *
  1359. \**************************************************************************/
  1360. BOOL MulSetPalette(
  1361. DHPDEV dhpdev,
  1362. PALOBJ* ppalo,
  1363. FLONG fl,
  1364. ULONG iStart,
  1365. ULONG cColors)
  1366. {
  1367. MULTI_BOARD* pmb;
  1368. MDEV* pmdev;
  1369. BOOL bRet = TRUE;
  1370. // Notify all boards of the palette change:
  1371. pmdev = (MDEV*) dhpdev;
  1372. for (pmb = pmdev->pmb; pmb != NULL; pmb = pmb->pmbNext)
  1373. {
  1374. GO_BOARD(pmdev, pmb);
  1375. bRet &= DrvSetPalette((DHPDEV) pmb->ppdev, ppalo, fl, iStart, cColors);
  1376. }
  1377. GO_HOME(pmdev);
  1378. return(bRet);
  1379. }
  1380. /******************************Public*Routine******************************\
  1381. * MulCopyBits
  1382. *
  1383. \**************************************************************************/
  1384. BOOL MulCopyBits(
  1385. SURFOBJ* psoDst,
  1386. SURFOBJ* psoSrc,
  1387. CLIPOBJ* pco,
  1388. XLATEOBJ* pxlo,
  1389. RECTL* prclDst,
  1390. POINTL* pptlSrc)
  1391. {
  1392. BOOL bFromScreen;
  1393. BOOL bToScreen;
  1394. MDEV* pmdev;
  1395. MULTI_BOARD* pmb;
  1396. RECTL rclOriginalBounds;
  1397. BOOL b;
  1398. RECTL rclBounds;
  1399. RECTL rclDst;
  1400. bFromScreen = ((psoSrc != NULL) && (psoSrc->iType == STYPE_DEVICE));
  1401. bToScreen = ((psoDst != NULL) && (psoDst->iType == STYPE_DEVICE));
  1402. // We copy the prclDst rectangle here because sometimes GDI will
  1403. // simply point prclDst to the same rectangle in pco->rclBounds,
  1404. // and we'll be mucking with pco->rclBounds...
  1405. rclDst = *prclDst;
  1406. if (bToScreen && bFromScreen)
  1407. {
  1408. ///////////////////////////////////////////////////////////////
  1409. // Screen-to-screen
  1410. ///////////////////////////////////////////////////////////////
  1411. pmdev = (MDEV*) psoDst->dhpdev;
  1412. // rclBounds is the union of the source and destination rectangles:
  1413. rclBounds.left = min(rclDst.left, pptlSrc->x);
  1414. rclBounds.top = min(rclDst.top, pptlSrc->y);
  1415. rclBounds.right = max(rclDst.right,
  1416. pptlSrc->x + (rclDst.right - rclDst.left));
  1417. rclBounds.bottom = max(rclDst.bottom,
  1418. pptlSrc->y + (rclDst.bottom - rclDst.top));
  1419. if (bFindBoard(pmdev, &rclBounds, &pmb))
  1420. {
  1421. GO_BOARD(pmdev, pmb);
  1422. b = DrvCopyBits(pmb->pso, pmb->pso, pco, pxlo, &rclDst, pptlSrc);
  1423. }
  1424. else
  1425. {
  1426. return(bBitBltBetweenBoards(psoDst, psoSrc, NULL, pco, pxlo,
  1427. &rclDst, pptlSrc, NULL, NULL,
  1428. NULL, 0x0000cccc, &rclBounds, pmb));
  1429. }
  1430. }
  1431. else if (bToScreen)
  1432. {
  1433. ///////////////////////////////////////////////////////////////
  1434. // To-screen
  1435. ///////////////////////////////////////////////////////////////
  1436. pmdev = (MDEV*) psoDst->dhpdev;
  1437. if (bFindBoard(pmdev, &rclDst, &pmb))
  1438. {
  1439. GO_BOARD(pmdev, pmb);
  1440. b = DrvCopyBits(pmb->pso, psoSrc, pco, pxlo, &rclDst, pptlSrc);
  1441. }
  1442. else
  1443. {
  1444. if ((pco == NULL) || (pco->iDComplexity == DC_TRIVIAL))
  1445. {
  1446. // If the CLIPOBJ doesn't have at least DC_RECT complexity,
  1447. // substitute one that does:
  1448. pco = pmdev->pco;
  1449. }
  1450. rclOriginalBounds = pco->rclBounds;
  1451. b = TRUE;
  1452. do {
  1453. if (bIntersect(&rclOriginalBounds, &pmb->rcl, &pco->rclBounds))
  1454. {
  1455. GO_BOARD(pmdev, pmb);
  1456. b &= DrvCopyBits(pmb->pso, psoSrc, pco, pxlo, &rclDst,
  1457. pptlSrc);
  1458. }
  1459. } while (bNextBoard(&rclDst, &pmb));
  1460. // Restore the original clip bounds:
  1461. pco->rclBounds = rclOriginalBounds;
  1462. }
  1463. }
  1464. else
  1465. {
  1466. ///////////////////////////////////////////////////////////////
  1467. // From-screen
  1468. ///////////////////////////////////////////////////////////////
  1469. // This rarely happens, so save some code space:
  1470. return(MulBitBlt(psoDst, psoSrc, NULL, pco, pxlo, prclDst,
  1471. pptlSrc, NULL, NULL, NULL, 0x0000cccc));
  1472. }
  1473. GO_HOME(pmdev);
  1474. return(b);
  1475. }
  1476. /******************************Public*Routine******************************\
  1477. * MulTextOut
  1478. *
  1479. \**************************************************************************/
  1480. BOOL MulTextOut(
  1481. SURFOBJ* pso,
  1482. STROBJ* pstro,
  1483. FONTOBJ* pfo,
  1484. CLIPOBJ* pco,
  1485. RECTL* prclExtra,
  1486. RECTL* prclOpaque,
  1487. BRUSHOBJ* pboFore,
  1488. BRUSHOBJ* pboOpaque,
  1489. POINTL* pptlOrg,
  1490. MIX mix)
  1491. {
  1492. MDEV* pmdev;
  1493. MULTI_BOARD* pmb;
  1494. RECTL rclOriginalBounds;
  1495. BYTE fjOriginalOptions;
  1496. BOOL b;
  1497. RECTL* prclBounds;
  1498. FONT_CONSUMER* pfcArray;
  1499. pmdev = (MDEV*) pso->dhpdev;
  1500. // In keeping with our philosophy for multiple board support, we handle
  1501. // multiple consumers of the same font at this level. We do this by
  1502. // monitoring pfo->pvConsumer, and the first time a board sets the
  1503. // field, we take control of pfo->pvConsumer. We use it to allocate
  1504. // a pvConsumer array where we can keep track of every board's
  1505. // individual pvConsumer.
  1506. pfcArray = pfo->pvConsumer;
  1507. prclBounds = (prclOpaque != NULL) ? prclOpaque : &pstro->rclBkGround;
  1508. bFindBoard(pmdev, prclBounds, &pmb);
  1509. if ((pco == NULL) || (pco->iDComplexity == DC_TRIVIAL))
  1510. {
  1511. // If the CLIPOBJ doesn't have at least DC_RECT complexity,
  1512. // substitute one that does:
  1513. pco = pmdev->pco;
  1514. }
  1515. rclOriginalBounds = pco->rclBounds;
  1516. fjOriginalOptions = pco->fjOptions;
  1517. // OR in the OC_BANK_CLIP flag to let GDI know that we may be calling
  1518. // EngTextOut multiple times with the same parameters (EngTextOut
  1519. // is destructive in that it modifies that parameters passed to it,
  1520. // unless this bit is set):
  1521. pco->fjOptions |= OC_BANK_CLIP;
  1522. b = TRUE;
  1523. do {
  1524. if (pfcArray != NULL)
  1525. pfo->pvConsumer = pfcArray->apvc[pmb->iBoard].pvConsumer;
  1526. // Make sure we restart the glyph enumeration if need be:
  1527. STROBJ_vEnumStart(pstro);
  1528. if (bIntersect(&rclOriginalBounds, &pmb->rcl, &pco->rclBounds))
  1529. {
  1530. GO_BOARD(pmdev, pmb);
  1531. b &= DrvTextOut(pmb->pso, pstro, pfo, pco, prclExtra, prclOpaque,
  1532. pboFore, pboOpaque, pptlOrg, mix);
  1533. }
  1534. if (pfcArray != NULL)
  1535. {
  1536. // Copy the pvConsumer, in case the last DrvTextOut changed
  1537. // it:
  1538. pfcArray->apvc[pmb->iBoard].pvConsumer = pfo->pvConsumer;
  1539. }
  1540. else
  1541. {
  1542. if (pfo->pvConsumer != NULL)
  1543. {
  1544. // The board allocated a new consumer, so create our array
  1545. // to keep track of consumers for every board:
  1546. pfcArray = (FONT_CONSUMER*) EngAllocMem(FL_ZERO_MEMORY,
  1547. sizeof(FONT_CONSUMER), ALLOC_TAG);
  1548. if (pfcArray == NULL)
  1549. DrvDestroyFont(pfo);
  1550. else
  1551. {
  1552. pfcArray->cConsumers = pmdev->cBoards;
  1553. pfcArray->apvc[pmb->iBoard].pvConsumer = pfo->pvConsumer;
  1554. }
  1555. }
  1556. }
  1557. } while (bNextBoard(prclBounds, &pmb));
  1558. // Restore the original clip bounds:
  1559. pco->rclBounds = rclOriginalBounds;
  1560. pco->fjOptions = fjOriginalOptions;
  1561. // Make sure we restore/set the font's pvConsumer:
  1562. pfo->pvConsumer = pfcArray;
  1563. GO_HOME(pmdev);
  1564. return(b);
  1565. }
  1566. /******************************Public*Routine******************************\
  1567. * MulDestroyFont
  1568. *
  1569. \**************************************************************************/
  1570. VOID MulDestroyFont(FONTOBJ *pfo)
  1571. {
  1572. FONT_CONSUMER* pfcArray;
  1573. LONG i;
  1574. PVOID pvConsumer;
  1575. if (pfo->pvConsumer != NULL)
  1576. {
  1577. pfcArray = pfo->pvConsumer;
  1578. for (i = 0; i < pfcArray->cConsumers; i++)
  1579. {
  1580. pvConsumer = pfcArray->apvc[i].pvConsumer;
  1581. if (pvConsumer != NULL)
  1582. {
  1583. pfo->pvConsumer = pvConsumer;
  1584. DrvDestroyFont(pfo);
  1585. }
  1586. }
  1587. EngFreeMem(pfcArray);
  1588. pfo->pvConsumer = NULL;
  1589. }
  1590. }
  1591. /******************************Public*Routine******************************\
  1592. * MulPaint
  1593. *
  1594. \**************************************************************************/
  1595. BOOL MulPaint(
  1596. SURFOBJ* pso,
  1597. CLIPOBJ* pco,
  1598. BRUSHOBJ* pbo,
  1599. POINTL* pptlBrush,
  1600. MIX mix)
  1601. {
  1602. MDEV* pmdev;
  1603. RECTL rclOriginalBounds;
  1604. MULTI_BOARD* pmb;
  1605. BOOL b;
  1606. pmdev = (MDEV*) pso->dhpdev;
  1607. if (bFindBoard(pmdev, &pco->rclBounds, &pmb))
  1608. {
  1609. GO_BOARD(pmdev, pmb);
  1610. b = DrvPaint(pmb->pso, pco, pbo, pptlBrush, mix);
  1611. }
  1612. else
  1613. {
  1614. rclOriginalBounds = pco->rclBounds;
  1615. b = TRUE;
  1616. do {
  1617. if (bIntersect(&rclOriginalBounds, &pmb->rcl, &pco->rclBounds))
  1618. {
  1619. GO_BOARD(pmdev, pmb);
  1620. b &= DrvPaint(pmb->pso, pco, pbo, pptlBrush, mix);
  1621. }
  1622. } while (bNextBoard(&rclOriginalBounds, &pmb));
  1623. // Restore the original clip bounds:
  1624. pco->rclBounds = rclOriginalBounds;
  1625. }
  1626. GO_HOME(pmdev);
  1627. return(b);
  1628. }
  1629. /******************************Public*Routine******************************\
  1630. * MulRealizeBrush
  1631. *
  1632. \**************************************************************************/
  1633. BOOL MulRealizeBrush(
  1634. BRUSHOBJ* pbo,
  1635. SURFOBJ* psoTarget,
  1636. SURFOBJ* psoPattern,
  1637. SURFOBJ* psoMask,
  1638. XLATEOBJ* pxlo,
  1639. ULONG iHatch)
  1640. {
  1641. MDEV* pmdev;
  1642. BOOL b;
  1643. pmdev = (MDEV*) psoTarget->dhpdev;
  1644. // DrvRealizeBrush is only ever calling from within a Drv function.
  1645. // 'psoTarget' points to our multi-board surface, but we have to point
  1646. // it to the surface of the board for which the DrvBitBlt call was made.
  1647. // NOTE: If SLOWFILL_PATTERNS are enabled, we will have to do a
  1648. // GO_BOARD here, because our DrvRealizeBrush routine actually
  1649. // draws for that case.
  1650. b = DrvRealizeBrush(pbo, pmdev->pmbCurrent->pso, psoPattern, psoMask,
  1651. pxlo, iHatch);
  1652. return(b);
  1653. }
  1654. #endif // MULTI_BOARDS