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.

2071 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 = AtiAllocMem(LPTR, FL_ZERO_MEMORY, sizeof(MULTI_BOARD));
  239. pmb2 = AtiAllocMem(LPTR, FL_ZERO_MEMORY, sizeof(MULTI_BOARD));
  240. if ((pmb1 == NULL) || (pmb2 == NULL))
  241. {
  242. AtiFreeMem(pmb1);
  243. AtiFreeMem(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(ppdev->pjIoBase, 0x220, iOldHwBoard); // Disable old board
  292. OUTP(ppdev->pjIoBase, 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 = AtiAllocMem(LPTR, FL_ZERO_MEMORY, sizeof(MDEV));
  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:
  761. pmdev->flHooks = pmb->ppdev->flHooks & ~HOOK_STRETCHBLT;
  762. pmdev->iBitmapFormat = pmb->ppdev->iBitmapFormat;
  763. // As part of our hard-coded initialization hack, we will simply
  764. // take whatever resolution was requested via the Control Panel
  765. // and create a two board virtual desktop, where the screens are
  766. // side-by-side.
  767. //
  768. // The DrvEnablePDEV function for any board has already figured
  769. // out what the requested mode was:
  770. cx = ((GDIINFO*) pGdiInfo)->ulHorzRes;
  771. cy = ((GDIINFO*) pGdiInfo)->ulVertRes;
  772. // Set up bounds for left board:
  773. pmb->rcl.left = 0;
  774. pmb->rcl.top = 0;
  775. pmb->rcl.right = cx;
  776. pmb->rcl.bottom = cy;
  777. // Set up bounds for right board:
  778. pmb = pmb->pmbRight;
  779. pmb->rcl.left = cx;
  780. pmb->rcl.top = 0;
  781. pmb->rcl.right = 2 * cx;
  782. pmb->rcl.bottom = cy;
  783. // Adjust the stuff we return back to GDI to reflect that our
  784. // virtual surface size is now twice as wide:
  785. ((GDIINFO*) pGdiInfo)->ulPanningHorzRes *= 2;
  786. ((GDIINFO*) pGdiInfo)->ulHorzSize *= 2;
  787. // With the Metheus board, since only one board can be mapped in
  788. // at one time, we cannot allow asynchronous pointers:
  789. pDevInfo->flGraphicsCaps &= ~(GCAPS_ASYNCMOVE | GCAPS_ASYNCCHANGE);
  790. return((DHPDEV) pmdev);
  791. ReturnFailure1:
  792. MulDisablePDEV((DHPDEV) pmdev);
  793. ReturnFailure0:
  794. DISPDBG((0, "Failed MulEnablePDEV"));
  795. return(0);
  796. }
  797. /******************************Public*Routine******************************\
  798. * MulCompletePDEV
  799. *
  800. \**************************************************************************/
  801. VOID MulCompletePDEV(
  802. DHPDEV dhpdev,
  803. HDEV hdev)
  804. {
  805. MDEV* pmdev;
  806. MULTI_BOARD* pmb;
  807. pmdev = (MDEV*) dhpdev;
  808. pmdev->hdev = hdev;
  809. for (pmb = pmdev->pmb; pmb != NULL; pmb = pmb->pmbNext)
  810. {
  811. GO_BOARD(pmdev, pmb);
  812. DrvCompletePDEV((DHPDEV) pmb->ppdev, hdev);
  813. }
  814. }
  815. /******************************Public*Routine******************************\
  816. * MulEnableSurface
  817. *
  818. \**************************************************************************/
  819. HSURF MulEnableSurface(DHPDEV dhpdev)
  820. {
  821. MDEV* pmdev;
  822. MULTI_BOARD* pmb;
  823. SIZEL sizlVirtual;
  824. HSURF hsurfBoard; // Gnarly, dude!
  825. SURFOBJ* psoBoard;
  826. DSURF* pdsurfBoard;
  827. HSURF hsurfVirtual;
  828. CLIPOBJ* pco;
  829. pmdev = (MDEV*) dhpdev;
  830. for (pmb = pmdev->pmb; pmb != NULL; pmb = pmb->pmbNext)
  831. {
  832. GO_BOARD(pmdev, pmb);
  833. hsurfBoard = DrvEnableSurface((DHPDEV) pmb->ppdev);
  834. if (hsurfBoard == 0)
  835. goto ReturnFailure;
  836. pmb->hsurf = hsurfBoard;
  837. // Every time we draw on a particular board, we'll refer to it
  838. // using this surface:
  839. psoBoard = EngLockSurface(hsurfBoard);
  840. if (psoBoard == NULL)
  841. goto ReturnFailure;
  842. pmb->pso = psoBoard;
  843. // There are a few things in the board's data instances that we
  844. // have to modify:
  845. pdsurfBoard = (DSURF*) psoBoard->dhsurf;
  846. pmb->ppdev->iBoard = pmb->iBoard;
  847. pdsurfBoard->poh->x = -pmb->rcl.left;
  848. pdsurfBoard->poh->y = -pmb->rcl.top;
  849. // This is sort of a hack. Whenever we pass a call on to a board's
  850. // Drv function using 'pmb->pso', it has to be able to retrieve
  851. // it's own PDEV pointer from 'dhpdev':
  852. pmb->pso->dhpdev = (DHPDEV) pmb->ppdev;
  853. }
  854. // Now create the surface which the engine will use to refer to our
  855. // entire multi-board virtual screen:
  856. sizlVirtual.cx = pmdev->pmbLowerRight->rcl.right;
  857. sizlVirtual.cy = pmdev->pmbLowerRight->rcl.bottom;
  858. hsurfVirtual = EngCreateDeviceSurface((DHSURF) pmdev, sizlVirtual,
  859. pmdev->iBitmapFormat);
  860. if (hsurfVirtual == 0)
  861. goto ReturnFailure;
  862. pmdev->hsurf = hsurfVirtual;
  863. if (!EngAssociateSurface(hsurfVirtual, pmdev->hdev, pmdev->flHooks))
  864. goto ReturnFailure;
  865. // Create a temporary clip object that we can use when a drawing
  866. // operation spans multiple boards:
  867. pco = EngCreateClip();
  868. if (pco == NULL)
  869. goto ReturnFailure;
  870. pmdev->pco = pco;
  871. pmdev->pco->iDComplexity = DC_RECT;
  872. pmdev->pco->iMode = TC_RECTANGLES;
  873. pmdev->pco->rclBounds.left = 0;
  874. pmdev->pco->rclBounds.top = 0;
  875. pmdev->pco->rclBounds.right = pmdev->pmbLowerRight->rcl.right;
  876. pmdev->pco->rclBounds.bottom = pmdev->pmbLowerRight->rcl.bottom;
  877. return(hsurfVirtual);
  878. ReturnFailure:
  879. MulDisableSurface((DHPDEV) pmdev);
  880. DISPDBG((0, "Failed MulEnableSurface"));
  881. return(0);
  882. }
  883. /******************************Public*Routine******************************\
  884. * MulStrokePath
  885. *
  886. \**************************************************************************/
  887. BOOL MulStrokePath(
  888. SURFOBJ* pso,
  889. PATHOBJ* ppo,
  890. CLIPOBJ* pco,
  891. XFORMOBJ* pxo,
  892. BRUSHOBJ* pbo,
  893. POINTL* pptlBrush,
  894. LINEATTRS* pla,
  895. MIX mix)
  896. {
  897. RECTFX rcfxBounds;
  898. RECTL rclBounds;
  899. MDEV* pmdev;
  900. RECTL rclOriginalBounds;
  901. MULTI_BOARD* pmb;
  902. BOOL b;
  903. FLOAT_LONG elStyleState;
  904. // Get the path bounds and make it lower-right exclusive:
  905. PATHOBJ_vGetBounds(ppo, &rcfxBounds);
  906. rclBounds.left = (rcfxBounds.xLeft >> 4);
  907. rclBounds.top = (rcfxBounds.yTop >> 4);
  908. rclBounds.right = (rcfxBounds.xRight >> 4) + 2;
  909. rclBounds.bottom = (rcfxBounds.yBottom >> 4) + 2;
  910. pmdev = (MDEV*) pso->dhpdev;
  911. if (bFindBoard(pmdev, &rclBounds, &pmb))
  912. {
  913. GO_BOARD(pmdev, pmb);
  914. b = DrvStrokePath(pmb->pso, ppo, pco, pxo, pbo, pptlBrush, pla, mix);
  915. }
  916. else
  917. {
  918. if ((pco == NULL) || (pco->iDComplexity == DC_TRIVIAL))
  919. {
  920. // If the CLIPOBJ doesn't have at least DC_RECT complexity,
  921. // substitute one that does:
  922. pco = pmdev->pco;
  923. }
  924. rclOriginalBounds = pco->rclBounds;
  925. elStyleState = pla->elStyleState;
  926. b = TRUE;
  927. do {
  928. // For each board, make sure the style state gets reset and
  929. // the path enumeration gets restarted:
  930. pla->elStyleState = elStyleState;
  931. PATHOBJ_vEnumStart(ppo);
  932. if (bIntersect(&rclOriginalBounds, &pmb->rcl, &pco->rclBounds))
  933. {
  934. GO_BOARD(pmdev, pmb);
  935. b &= DrvStrokePath(pmb->pso, ppo, pco, pxo, pbo, pptlBrush, pla,
  936. mix);
  937. }
  938. } while (bNextBoard(&rclBounds, &pmb));
  939. // Restore the original clip bounds:
  940. pco->rclBounds = rclOriginalBounds;
  941. }
  942. GO_HOME(pmdev);
  943. return(b);
  944. }
  945. /******************************Public*Routine******************************\
  946. * MulFillPath
  947. *
  948. \**************************************************************************/
  949. BOOL MulFillPath(
  950. SURFOBJ* pso,
  951. PATHOBJ* ppo,
  952. CLIPOBJ* pco,
  953. BRUSHOBJ* pbo,
  954. POINTL* pptlBrush,
  955. MIX mix,
  956. FLONG flOptions)
  957. {
  958. RECTFX rcfxBounds;
  959. RECTL rclBounds;
  960. MDEV* pmdev;
  961. RECTL rclOriginalBounds;
  962. MULTI_BOARD* pmb;
  963. BOOL b;
  964. // Get the path bounds and make it lower-right exclusive:
  965. PATHOBJ_vGetBounds(ppo, &rcfxBounds);
  966. rclBounds.left = (rcfxBounds.xLeft >> 4);
  967. rclBounds.top = (rcfxBounds.yTop >> 4);
  968. rclBounds.right = (rcfxBounds.xRight >> 4) + 2;
  969. rclBounds.bottom = (rcfxBounds.yBottom >> 4) + 2;
  970. pmdev = (MDEV*) pso->dhpdev;
  971. if (bFindBoard(pmdev, &rclBounds, &pmb))
  972. {
  973. GO_BOARD(pmdev, pmb);
  974. b = DrvFillPath(pmb->pso, ppo, pco, pbo, pptlBrush, mix, flOptions);
  975. }
  976. else
  977. {
  978. if ((pco == NULL) || (pco->iDComplexity == DC_TRIVIAL))
  979. {
  980. // If the CLIPOBJ doesn't have at least DC_RECT complexity,
  981. // substitute one that does:
  982. pco = pmdev->pco;
  983. }
  984. rclOriginalBounds = pco->rclBounds;
  985. b = TRUE;
  986. do {
  987. // Make sure we restart the path enumeration if need be:
  988. PATHOBJ_vEnumStart(ppo);
  989. if (bIntersect(&rclOriginalBounds, &pmb->rcl, &pco->rclBounds))
  990. {
  991. GO_BOARD(pmdev, pmb);
  992. b &= DrvFillPath(pmb->pso, ppo, pco, pbo, pptlBrush, mix,
  993. flOptions);
  994. }
  995. } while (bNextBoard(&rclBounds, &pmb));
  996. // Restore the original clip bounds:
  997. pco->rclBounds = rclOriginalBounds;
  998. }
  999. GO_HOME(pmdev);
  1000. return(b);
  1001. }
  1002. /******************************Public*Routine******************************\
  1003. * MulBitBlt
  1004. *
  1005. \**************************************************************************/
  1006. BOOL MulBitBlt(
  1007. SURFOBJ* psoDst,
  1008. SURFOBJ* psoSrc,
  1009. SURFOBJ* psoMask,
  1010. CLIPOBJ* pco,
  1011. XLATEOBJ* pxlo,
  1012. RECTL* prclDst,
  1013. POINTL* pptlSrc,
  1014. POINTL* pptlMask,
  1015. BRUSHOBJ* pbo,
  1016. POINTL* pptlBrush,
  1017. ROP4 rop4)
  1018. {
  1019. BOOL bFromScreen;
  1020. BOOL bToScreen;
  1021. MDEV* pmdev;
  1022. MULTI_BOARD* pmb;
  1023. RECTL rclOriginalBounds;
  1024. BOOL b;
  1025. RECTL rclBounds;
  1026. LONG xOffset;
  1027. LONG yOffset;
  1028. RECTL rclDstBounds;
  1029. RECTL rclDst;
  1030. bFromScreen = ((psoSrc != NULL) && (psoSrc->iType == STYPE_DEVICE));
  1031. bToScreen = ((psoDst != NULL) && (psoDst->iType == STYPE_DEVICE));
  1032. // We copy the prclDst rectangle here because sometimes GDI will
  1033. // simply point prclDst to the same rectangle in pco->rclBounds,
  1034. // and we'll be mucking with pco->rclBounds...
  1035. rclDst = *prclDst;
  1036. if (bToScreen && bFromScreen)
  1037. {
  1038. ///////////////////////////////////////////////////////////////
  1039. // Screen-to-screen
  1040. ///////////////////////////////////////////////////////////////
  1041. pmdev = (MDEV*) psoDst->dhpdev;
  1042. // rclBounds is the union of the source and destination rectangles:
  1043. rclBounds.left = min(rclDst.left, pptlSrc->x);
  1044. rclBounds.top = min(rclDst.top, pptlSrc->y);
  1045. rclBounds.right = max(rclDst.right,
  1046. pptlSrc->x + (rclDst.right - rclDst.left));
  1047. rclBounds.bottom = max(rclDst.bottom,
  1048. pptlSrc->y + (rclDst.bottom - rclDst.top));
  1049. if (bFindBoard(pmdev, &rclBounds, &pmb))
  1050. {
  1051. GO_BOARD(pmdev, pmb);
  1052. b = DrvBitBlt(pmb->pso, pmb->pso, psoMask, pco, pxlo, &rclDst,
  1053. pptlSrc, pptlMask, pbo, pptlBrush, rop4);
  1054. }
  1055. else
  1056. {
  1057. return(bBitBltBetweenBoards(psoDst, psoSrc, psoMask, pco, pxlo,
  1058. &rclDst, pptlSrc, pptlMask, pbo,
  1059. pptlBrush, rop4, &rclBounds, pmb));
  1060. }
  1061. }
  1062. else if (bToScreen)
  1063. {
  1064. ///////////////////////////////////////////////////////////////
  1065. // To-screen
  1066. ///////////////////////////////////////////////////////////////
  1067. pmdev = (MDEV*) psoDst->dhpdev;
  1068. if (bFindBoard(pmdev, &rclDst, &pmb))
  1069. {
  1070. GO_BOARD(pmdev, pmb);
  1071. b = DrvBitBlt(pmb->pso, psoSrc, psoMask, pco, pxlo, &rclDst,
  1072. pptlSrc, pptlMask, pbo, pptlBrush, rop4);
  1073. }
  1074. else
  1075. {
  1076. if ((pco == NULL) || (pco->iDComplexity == DC_TRIVIAL))
  1077. {
  1078. // If the CLIPOBJ doesn't have at least DC_RECT complexity,
  1079. // substitute one that does:
  1080. pco = pmdev->pco;
  1081. }
  1082. rclOriginalBounds = pco->rclBounds;
  1083. b = TRUE;
  1084. do {
  1085. if (bIntersect(&rclOriginalBounds, &pmb->rcl, &pco->rclBounds))
  1086. {
  1087. GO_BOARD(pmdev, pmb);
  1088. b &= DrvBitBlt(pmb->pso, psoSrc, psoMask, pco, pxlo, &rclDst,
  1089. pptlSrc, pptlMask, pbo, pptlBrush, rop4);
  1090. }
  1091. } while (bNextBoard(&rclDst, &pmb));
  1092. // Restore the original clip bounds:
  1093. pco->rclBounds = rclOriginalBounds;
  1094. }
  1095. }
  1096. else
  1097. {
  1098. ///////////////////////////////////////////////////////////////
  1099. // From-screen
  1100. ///////////////////////////////////////////////////////////////
  1101. pmdev = (MDEV*) psoSrc->dhpdev;
  1102. // rclBounds is the source rectangle:
  1103. rclBounds.left = pptlSrc->x;
  1104. rclBounds.top = pptlSrc->y;
  1105. rclBounds.right = pptlSrc->x + (rclDst.right - rclDst.left);
  1106. rclBounds.bottom = pptlSrc->y + (rclDst.bottom - rclDst.top);
  1107. if (bFindBoard(pmdev, &rclBounds, &pmb))
  1108. {
  1109. GO_BOARD(pmdev, pmb);
  1110. b = DrvBitBlt(psoDst, pmb->pso, psoMask, pco, pxlo, &rclDst,
  1111. pptlSrc, pptlMask, pbo, pptlBrush, rop4);
  1112. }
  1113. else
  1114. {
  1115. if ((pco == NULL) || (pco->iDComplexity == DC_TRIVIAL))
  1116. {
  1117. // If the CLIPOBJ doesn't have at least DC_RECT complexity,
  1118. // substitute one that does:
  1119. pco = pmdev->pco;
  1120. }
  1121. rclOriginalBounds = pco->rclBounds;
  1122. // Offset to transform from source rectangle to destination
  1123. // rectangle:
  1124. xOffset = rclDst.left - pptlSrc->x;
  1125. yOffset = rclDst.top - pptlSrc->y;
  1126. b = TRUE;
  1127. do {
  1128. // Since the screen is the source, but the clip bounds applies
  1129. // to the destination, we have to convert our board clipping
  1130. // information to destination coordinates:
  1131. rclDstBounds.left = pmb->rcl.left + xOffset;
  1132. rclDstBounds.right = pmb->rcl.right + xOffset;
  1133. rclDstBounds.top = pmb->rcl.top + yOffset;
  1134. rclDstBounds.bottom = pmb->rcl.bottom + yOffset;
  1135. if (bIntersect(&rclOriginalBounds, &rclDstBounds, &pco->rclBounds))
  1136. {
  1137. GO_BOARD(pmdev, pmb);
  1138. b &= DrvBitBlt(psoDst, pmb->pso, psoMask, pco, pxlo, &rclDst,
  1139. pptlSrc, pptlMask, pbo, pptlBrush, rop4);
  1140. }
  1141. } while (bNextBoard(&rclBounds, &pmb));
  1142. // Restore the original clip bounds:
  1143. pco->rclBounds = rclOriginalBounds;
  1144. }
  1145. }
  1146. GO_HOME(pmdev);
  1147. return(b);
  1148. }
  1149. /******************************Public*Routine******************************\
  1150. * MulDisablePDEV
  1151. *
  1152. * Note: May be called before MulEnablePDEV successfully completed!
  1153. *
  1154. \**************************************************************************/
  1155. VOID MulDisablePDEV(DHPDEV dhpdev)
  1156. {
  1157. MULTI_BOARD* pmb;
  1158. MDEV* pmdev;
  1159. pmdev = (MDEV*) dhpdev;
  1160. for (pmb = pmdev->pmb; pmb != NULL; pmb = pmb->pmbNext)
  1161. {
  1162. if (pmb->ppdev != NULL)
  1163. {
  1164. GO_BOARD(pmdev, pmb);
  1165. DrvDisablePDEV((DHPDEV) pmb->ppdev);
  1166. }
  1167. }
  1168. GO_HOME(pmdev);
  1169. for (pmb = pmdev->pmb; pmb != NULL; pmb = pmb->pmbNext)
  1170. {
  1171. AtiFreeMem(pmb); // Undo 'bVeryTemporaryInitializationCode'
  1172. } // allocation
  1173. AtiFreeMem(pmdev);
  1174. }
  1175. /******************************Public*Routine******************************\
  1176. * MulDisableSurface
  1177. *
  1178. * Note: May be called before MulEnableSurface successfully completed!
  1179. *
  1180. \**************************************************************************/
  1181. VOID MulDisableSurface(DHPDEV dhpdev)
  1182. {
  1183. MULTI_BOARD* pmb;
  1184. MDEV* pmdev;
  1185. pmdev = (MDEV*) dhpdev;
  1186. if (pmdev->pco != NULL)
  1187. EngDeleteClip(pmdev->pco);
  1188. EngDeleteSurface(pmdev->hsurf);
  1189. for (pmb = pmdev->pmb; pmb != NULL; pmb = pmb->pmbNext)
  1190. {
  1191. GO_BOARD(pmdev, pmb);
  1192. EngUnlockSurface(pmb->pso);
  1193. DrvDisableSurface((DHPDEV) pmb->ppdev);
  1194. }
  1195. GO_HOME(pmdev);
  1196. }
  1197. /******************************Public*Routine******************************\
  1198. * MulAssertMode
  1199. *
  1200. \**************************************************************************/
  1201. BOOL MulAssertMode(
  1202. DHPDEV dhpdev,
  1203. BOOL bEnable)
  1204. {
  1205. MDEV* pmdev;
  1206. MULTI_BOARD* pmb;
  1207. pmdev = (MDEV*) dhpdev;
  1208. if (!bEnable)
  1209. {
  1210. // When switching to full-screen mode, PatBlt blackness over
  1211. // all the inactive screens (otherwise it looks goofy when
  1212. // the desktop is frozen on the inactive screens and the user
  1213. // can't do anything with it):
  1214. for (pmb = pmdev->pmb; pmb != NULL; pmb = pmb->pmbNext)
  1215. {
  1216. if (pmb != pmdev->pmbHome)
  1217. {
  1218. GO_BOARD(pmdev, pmb);
  1219. DrvBitBlt(pmb->pso, NULL, NULL, NULL, NULL, &pmb->rcl, NULL,
  1220. NULL, NULL, NULL, 0);
  1221. }
  1222. }
  1223. }
  1224. // We use the 'home' board for full-screen switching:
  1225. GO_BOARD(pmdev, pmdev->pmbHome);
  1226. return (DrvAssertMode((DHPDEV) pmdev->pmbHome->ppdev, bEnable));
  1227. }
  1228. /******************************Public*Routine******************************\
  1229. * MulMovePointer
  1230. *
  1231. \**************************************************************************/
  1232. VOID MulMovePointer(
  1233. SURFOBJ* pso,
  1234. LONG x,
  1235. LONG y,
  1236. RECTL* prcl)
  1237. {
  1238. MDEV* pmdev;
  1239. MULTI_BOARD* pmbPointer;
  1240. RECTL rclPointer;
  1241. pmdev = (MDEV*) pso->dhpdev;
  1242. pmbPointer = pmdev->pmbPointer;
  1243. if (pmbPointer != NULL)
  1244. {
  1245. // The most common case is when the pointer is moved to a spot
  1246. // on the same board:
  1247. if ((x >= pmbPointer->rcl.left) &&
  1248. (x < pmbPointer->rcl.right) &&
  1249. (y >= pmbPointer->rcl.top) &&
  1250. (y < pmbPointer->rcl.bottom))
  1251. {
  1252. GO_BOARD(pmdev, pmbPointer);
  1253. DrvMovePointer(pmbPointer->pso, x, y, prcl);
  1254. GO_HOME(pmdev);
  1255. return;
  1256. }
  1257. // Tell the old board to erase its cursor:
  1258. GO_BOARD(pmdev, pmbPointer);
  1259. DrvMovePointer(pmbPointer->pso, -1, -1, NULL);
  1260. }
  1261. if (x == -1)
  1262. {
  1263. pmdev->pmbPointer = NULL;
  1264. GO_HOME(pmdev);
  1265. return;
  1266. }
  1267. // Find the new board and tell it to draw its new cursor:
  1268. rclPointer.left = x;
  1269. rclPointer.right = x;
  1270. rclPointer.top = y;
  1271. rclPointer.bottom = y;
  1272. bFindBoard(pmdev, &rclPointer, &pmbPointer);
  1273. GO_BOARD(pmdev, pmbPointer);
  1274. DrvMovePointer(pmbPointer->pso, x, y, prcl);
  1275. pmdev->pmbPointer = pmbPointer;
  1276. GO_HOME(pmdev);
  1277. }
  1278. /******************************Public*Routine******************************\
  1279. * MulSetPointerShape
  1280. *
  1281. \**************************************************************************/
  1282. ULONG MulSetPointerShape(
  1283. SURFOBJ* pso,
  1284. SURFOBJ* psoMask,
  1285. SURFOBJ* psoColor,
  1286. XLATEOBJ* pxlo,
  1287. LONG xHot,
  1288. LONG yHot,
  1289. LONG x,
  1290. LONG y,
  1291. RECTL* prcl,
  1292. FLONG fl)
  1293. {
  1294. MULTI_BOARD* pmb;
  1295. MDEV* pmdev;
  1296. ULONG ulRetPrevious = (ULONG) -1;
  1297. ULONG ulRet;
  1298. RECTL rclPointer;
  1299. MULTI_BOARD* pmbPointer; // Board on which cursor is visible
  1300. pmdev = (MDEV*) pso->dhpdev;
  1301. // Find out which board that the cursor is visible on, if any:
  1302. pmbPointer = NULL;
  1303. if (x != -1)
  1304. {
  1305. rclPointer.left = x;
  1306. rclPointer.right = x;
  1307. rclPointer.top = y;
  1308. rclPointer.bottom = y;
  1309. bFindBoard(pmdev, &rclPointer, &pmbPointer);
  1310. }
  1311. pmdev->pmbPointer = pmbPointer;
  1312. // LATER: Fix the case for when some boards may fail the call, and others
  1313. // won't.
  1314. for (pmb = pmdev->pmb; pmb != NULL; pmb = pmb->pmbNext)
  1315. {
  1316. // We notify all boards of the new cursor shape, but only the board
  1317. // on which the cursor is visible is told to draw it:
  1318. GO_BOARD(pmdev, pmb);
  1319. if (pmb == pmbPointer)
  1320. {
  1321. ulRet = DrvSetPointerShape(pmb->pso, psoMask, psoColor, pxlo,
  1322. xHot, yHot, x, y, prcl, fl);
  1323. }
  1324. else
  1325. {
  1326. ulRet = DrvSetPointerShape(pmb->pso, psoMask, psoColor, pxlo,
  1327. xHot, yHot, -1, y, NULL, fl);
  1328. }
  1329. if ((ulRetPrevious != (ULONG) -1) && (ulRetPrevious != ulRet))
  1330. {
  1331. RIP("MulSetPointerShape not all DrvSetPointerShapes same\n");
  1332. }
  1333. ulRetPrevious = ulRet;
  1334. }
  1335. GO_HOME(pmdev);
  1336. return(ulRetPrevious);
  1337. }
  1338. /******************************Public*Routine******************************\
  1339. * MulDitherColor
  1340. *
  1341. \**************************************************************************/
  1342. ULONG MulDitherColor(
  1343. DHPDEV dhpdev,
  1344. ULONG iMode,
  1345. ULONG rgb,
  1346. ULONG* pul)
  1347. {
  1348. PDEV* ppdev;
  1349. ULONG ulRet;
  1350. // Let the first board's driver do the dithering:
  1351. ppdev = ((MDEV*) dhpdev)->pmb->ppdev;
  1352. ulRet = DrvDitherColor((DHPDEV) ppdev, iMode, rgb, pul);
  1353. return(ulRet);
  1354. }
  1355. /******************************Public*Routine******************************\
  1356. * MulSetPalette
  1357. *
  1358. \**************************************************************************/
  1359. BOOL MulSetPalette(
  1360. DHPDEV dhpdev,
  1361. PALOBJ* ppalo,
  1362. FLONG fl,
  1363. ULONG iStart,
  1364. ULONG cColors)
  1365. {
  1366. MULTI_BOARD* pmb;
  1367. MDEV* pmdev;
  1368. BOOL bRet = TRUE;
  1369. // Notify all boards of the palette change:
  1370. pmdev = (MDEV*) dhpdev;
  1371. for (pmb = pmdev->pmb; pmb != NULL; pmb = pmb->pmbNext)
  1372. {
  1373. GO_BOARD(pmdev, pmb);
  1374. bRet &= DrvSetPalette((DHPDEV) pmb->ppdev, ppalo, fl, iStart, cColors);
  1375. }
  1376. GO_HOME(pmdev);
  1377. return(bRet);
  1378. }
  1379. /******************************Public*Routine******************************\
  1380. * MulCopyBits
  1381. *
  1382. \**************************************************************************/
  1383. BOOL MulCopyBits(
  1384. SURFOBJ* psoDst,
  1385. SURFOBJ* psoSrc,
  1386. CLIPOBJ* pco,
  1387. XLATEOBJ* pxlo,
  1388. RECTL* prclDst,
  1389. POINTL* pptlSrc)
  1390. {
  1391. BOOL bFromScreen;
  1392. BOOL bToScreen;
  1393. MDEV* pmdev;
  1394. MULTI_BOARD* pmb;
  1395. RECTL rclOriginalBounds;
  1396. BOOL b;
  1397. RECTL rclBounds;
  1398. RECTL rclDst;
  1399. bFromScreen = ((psoSrc != NULL) && (psoSrc->iType == STYPE_DEVICE));
  1400. bToScreen = ((psoDst != NULL) && (psoDst->iType == STYPE_DEVICE));
  1401. // We copy the prclDst rectangle here because sometimes GDI will
  1402. // simply point prclDst to the same rectangle in pco->rclBounds,
  1403. // and we'll be mucking with pco->rclBounds...
  1404. rclDst = *prclDst;
  1405. if (bToScreen && bFromScreen)
  1406. {
  1407. ///////////////////////////////////////////////////////////////
  1408. // Screen-to-screen
  1409. ///////////////////////////////////////////////////////////////
  1410. pmdev = (MDEV*) psoDst->dhpdev;
  1411. // rclBounds is the union of the source and destination rectangles:
  1412. rclBounds.left = min(rclDst.left, pptlSrc->x);
  1413. rclBounds.top = min(rclDst.top, pptlSrc->y);
  1414. rclBounds.right = max(rclDst.right,
  1415. pptlSrc->x + (rclDst.right - rclDst.left));
  1416. rclBounds.bottom = max(rclDst.bottom,
  1417. pptlSrc->y + (rclDst.bottom - rclDst.top));
  1418. if (bFindBoard(pmdev, &rclBounds, &pmb))
  1419. {
  1420. GO_BOARD(pmdev, pmb);
  1421. b = DrvCopyBits(pmb->pso, pmb->pso, pco, pxlo, &rclDst, pptlSrc);
  1422. }
  1423. else
  1424. {
  1425. return(bBitBltBetweenBoards(psoDst, psoSrc, NULL, pco, pxlo,
  1426. &rclDst, pptlSrc, NULL, NULL,
  1427. NULL, 0x0000cccc, &rclBounds, pmb));
  1428. }
  1429. }
  1430. else if (bToScreen)
  1431. {
  1432. ///////////////////////////////////////////////////////////////
  1433. // To-screen
  1434. ///////////////////////////////////////////////////////////////
  1435. pmdev = (MDEV*) psoDst->dhpdev;
  1436. if (bFindBoard(pmdev, &rclDst, &pmb))
  1437. {
  1438. GO_BOARD(pmdev, pmb);
  1439. b = DrvCopyBits(pmb->pso, psoSrc, pco, pxlo, &rclDst, pptlSrc);
  1440. }
  1441. else
  1442. {
  1443. if ((pco == NULL) || (pco->iDComplexity == DC_TRIVIAL))
  1444. {
  1445. // If the CLIPOBJ doesn't have at least DC_RECT complexity,
  1446. // substitute one that does:
  1447. pco = pmdev->pco;
  1448. }
  1449. rclOriginalBounds = pco->rclBounds;
  1450. b = TRUE;
  1451. do {
  1452. if (bIntersect(&rclOriginalBounds, &pmb->rcl, &pco->rclBounds))
  1453. {
  1454. GO_BOARD(pmdev, pmb);
  1455. b &= DrvCopyBits(pmb->pso, psoSrc, pco, pxlo, &rclDst,
  1456. pptlSrc);
  1457. }
  1458. } while (bNextBoard(&rclDst, &pmb));
  1459. // Restore the original clip bounds:
  1460. pco->rclBounds = rclOriginalBounds;
  1461. }
  1462. }
  1463. else
  1464. {
  1465. ///////////////////////////////////////////////////////////////
  1466. // From-screen
  1467. ///////////////////////////////////////////////////////////////
  1468. // This rarely happens, so save some code space:
  1469. return(MulBitBlt(psoDst, psoSrc, NULL, pco, pxlo, prclDst,
  1470. pptlSrc, NULL, NULL, NULL, 0x0000cccc));
  1471. }
  1472. GO_HOME(pmdev);
  1473. return(b);
  1474. }
  1475. /******************************Public*Routine******************************\
  1476. * MulTextOut
  1477. *
  1478. \**************************************************************************/
  1479. BOOL MulTextOut(
  1480. SURFOBJ* pso,
  1481. STROBJ* pstro,
  1482. FONTOBJ* pfo,
  1483. CLIPOBJ* pco,
  1484. RECTL* prclExtra,
  1485. RECTL* prclOpaque,
  1486. BRUSHOBJ* pboFore,
  1487. BRUSHOBJ* pboOpaque,
  1488. POINTL* pptlOrg,
  1489. MIX mix)
  1490. {
  1491. MDEV* pmdev;
  1492. MULTI_BOARD* pmb;
  1493. RECTL rclOriginalBounds;
  1494. BYTE fjOriginalOptions;
  1495. BOOL b;
  1496. RECTL* prclBounds;
  1497. FONT_CONSUMER* pfcArray;
  1498. pmdev = (MDEV*) pso->dhpdev;
  1499. // In keeping with our philosophy for multiple board support, we handle
  1500. // multiple consumers of the same font at this level. We do this by
  1501. // monitoring pfo->pvConsumer, and the first time a board sets the
  1502. // field, we take control of pfo->pvConsumer. We use it to allocate
  1503. // a pvConsumer array where we can keep track of every board's
  1504. // individual pvConsumer.
  1505. pfcArray = pfo->pvConsumer;
  1506. prclBounds = (prclOpaque != NULL) ? prclOpaque : &pstro->rclBkGround;
  1507. bFindBoard(pmdev, prclBounds, &pmb);
  1508. if ((pco == NULL) || (pco->iDComplexity == DC_TRIVIAL))
  1509. {
  1510. // If the CLIPOBJ doesn't have at least DC_RECT complexity,
  1511. // substitute one that does:
  1512. pco = pmdev->pco;
  1513. }
  1514. rclOriginalBounds = pco->rclBounds;
  1515. fjOriginalOptions = pco->fjOptions;
  1516. // OR in the OC_BANK_CLIP flag to let GDI know that we may be calling
  1517. // EngTextOut multiple times with the same parameters (EngTextOut
  1518. // is destructive in that it modifies that parameters passed to it,
  1519. // unless this bit is set):
  1520. pco->fjOptions |= OC_BANK_CLIP;
  1521. b = TRUE;
  1522. do {
  1523. if (pfcArray != NULL)
  1524. pfo->pvConsumer = pfcArray->apvc[pmb->iBoard].pvConsumer;
  1525. // Make sure we restart the glyph enumeration if need be:
  1526. STROBJ_vEnumStart(pstro);
  1527. if (bIntersect(&rclOriginalBounds, &pmb->rcl, &pco->rclBounds))
  1528. {
  1529. GO_BOARD(pmdev, pmb);
  1530. b &= DrvTextOut(pmb->pso, pstro, pfo, pco, prclExtra, prclOpaque,
  1531. pboFore, pboOpaque, pptlOrg, mix);
  1532. }
  1533. if (pfcArray != NULL)
  1534. {
  1535. // Copy the pvConsumer, in case the last DrvTextOut changed
  1536. // it:
  1537. pfcArray->apvc[pmb->iBoard].pvConsumer = pfo->pvConsumer;
  1538. }
  1539. else
  1540. {
  1541. if (pfo->pvConsumer != NULL)
  1542. {
  1543. // The board allocated a new consumer, so create our array
  1544. // to keep track of consumers for every board:
  1545. pfcArray = AtiAllocMem(LPTR, FL_ZERO_MEMORY, sizeof(FONT_CONSUMER));
  1546. if (pfcArray == NULL)
  1547. DrvDestroyFont(pfo);
  1548. else
  1549. {
  1550. pfcArray->cConsumers = pmdev->cBoards;
  1551. pfcArray->apvc[pmb->iBoard].pvConsumer = pfo->pvConsumer;
  1552. }
  1553. }
  1554. }
  1555. } while (bNextBoard(prclBounds, &pmb));
  1556. // Restore the original clip bounds:
  1557. pco->rclBounds = rclOriginalBounds;
  1558. pco->fjOptions = fjOriginalOptions;
  1559. // Make sure we restore/set the font's pvConsumer:
  1560. pfo->pvConsumer = pfcArray;
  1561. GO_HOME(pmdev);
  1562. return(b);
  1563. }
  1564. /******************************Public*Routine******************************\
  1565. * MulDestroyFont
  1566. *
  1567. \**************************************************************************/
  1568. VOID MulDestroyFont(FONTOBJ *pfo)
  1569. {
  1570. FONT_CONSUMER* pfcArray;
  1571. LONG i;
  1572. PVOID pvConsumer;
  1573. if (pfo->pvConsumer != NULL)
  1574. {
  1575. pfcArray = pfo->pvConsumer;
  1576. for (i = 0; i < pfcArray->cConsumers; i++)
  1577. {
  1578. pvConsumer = pfcArray->apvc[i].pvConsumer;
  1579. if (pvConsumer != NULL)
  1580. {
  1581. pfo->pvConsumer = pvConsumer;
  1582. DrvDestroyFont(pfo);
  1583. }
  1584. }
  1585. AtiFreeMem(pfcArray);
  1586. pfo->pvConsumer = NULL;
  1587. }
  1588. }
  1589. /******************************Public*Routine******************************\
  1590. * MulPaint
  1591. *
  1592. \**************************************************************************/
  1593. BOOL MulPaint(
  1594. SURFOBJ* pso,
  1595. CLIPOBJ* pco,
  1596. BRUSHOBJ* pbo,
  1597. POINTL* pptlBrush,
  1598. MIX mix)
  1599. {
  1600. MDEV* pmdev;
  1601. RECTL rclOriginalBounds;
  1602. MULTI_BOARD* pmb;
  1603. BOOL b;
  1604. pmdev = (MDEV*) pso->dhpdev;
  1605. if (bFindBoard(pmdev, &pco->rclBounds, &pmb))
  1606. {
  1607. GO_BOARD(pmdev, pmb);
  1608. b = DrvPaint(pmb->pso, pco, pbo, pptlBrush, mix);
  1609. }
  1610. else
  1611. {
  1612. rclOriginalBounds = pco->rclBounds;
  1613. b = TRUE;
  1614. do {
  1615. if (bIntersect(&rclOriginalBounds, &pmb->rcl, &pco->rclBounds))
  1616. {
  1617. GO_BOARD(pmdev, pmb);
  1618. b &= DrvPaint(pmb->pso, pco, pbo, pptlBrush, mix);
  1619. }
  1620. } while (bNextBoard(&rclOriginalBounds, &pmb));
  1621. // Restore the original clip bounds:
  1622. pco->rclBounds = rclOriginalBounds;
  1623. }
  1624. GO_HOME(pmdev);
  1625. return(b);
  1626. }
  1627. /******************************Public*Routine******************************\
  1628. * MulRealizeBrush
  1629. *
  1630. \**************************************************************************/
  1631. BOOL MulRealizeBrush(
  1632. BRUSHOBJ* pbo,
  1633. SURFOBJ* psoTarget,
  1634. SURFOBJ* psoPattern,
  1635. SURFOBJ* psoMask,
  1636. XLATEOBJ* pxlo,
  1637. ULONG iHatch)
  1638. {
  1639. MDEV* pmdev;
  1640. BOOL b;
  1641. pmdev = (MDEV*) psoTarget->dhpdev;
  1642. // DrvRealizeBrush is only ever calling from within a Drv function.
  1643. // 'psoTarget' points to our multi-board surface, but we have to point
  1644. // it to the surface of the board for which the DrvBitBlt call was made.
  1645. b = DrvRealizeBrush(pbo, pmdev->pmbCurrent->pso, psoPattern, psoMask,
  1646. pxlo, iHatch);
  1647. return(b);
  1648. }
  1649. #endif // MULTI_BOARDS
  1650.