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.

741 lines
19 KiB

  1. //
  2. // CM.C
  3. // Cursor Manager
  4. //
  5. // Copyright(c) 1997-
  6. //
  7. #include <as16.h>
  8. //
  9. // CM_DDProcessRequest()
  10. // Handles CM escapes
  11. //
  12. BOOL CM_DDProcessRequest
  13. (
  14. UINT fnEscape,
  15. LPOSI_ESCAPE_HEADER pResult,
  16. DWORD cbResult
  17. )
  18. {
  19. BOOL rc;
  20. DebugEntry(CM_DDProcessRequest);
  21. switch (fnEscape)
  22. {
  23. case CM_ESC_XFORM:
  24. {
  25. ASSERT(cbResult == sizeof(CM_DRV_XFORM_INFO));
  26. ((LPCM_DRV_XFORM_INFO)pResult)->result =
  27. CMDDSetTransform((LPCM_DRV_XFORM_INFO)pResult);
  28. rc = TRUE;
  29. }
  30. break;
  31. default:
  32. {
  33. ERROR_OUT(("Unrecognized CM_ escape"));
  34. rc = FALSE;
  35. }
  36. break;
  37. }
  38. DebugExitBOOL(CM_DDProcessRequest, rc);
  39. return(rc);
  40. }
  41. //
  42. // CM_DDInit()
  43. //
  44. BOOL CM_DDInit(HDC hdcScreen)
  45. {
  46. BOOL rc = FALSE;
  47. HGLOBAL hg;
  48. LPBYTE lpfnPatch;
  49. DebugEntry(CM_DDInit);
  50. //
  51. // Get the size of the cursor
  52. //
  53. g_cxCursor = GetSystemMetrics(SM_CXCURSOR);
  54. g_cyCursor = GetSystemMetrics(SM_CYCURSOR);
  55. //
  56. // Create our work bit buffers
  57. //
  58. g_cmMonoByteSize = BitmapSize(g_cxCursor, g_cyCursor, 1, 1);
  59. g_cmColorByteSize = BitmapSize(g_cxCursor, g_cyCursor,
  60. g_osiScreenPlanes, g_osiScreenBitsPlane);
  61. // This will hold a color cursor, mono is always <= to this
  62. hg = GlobalAlloc(GMEM_FIXED | GMEM_SHARE, sizeof(CURSORSHAPE) +
  63. g_cmMonoByteSize + g_cmColorByteSize);
  64. g_cmMungedCursor = MAKELP(hg, 0);
  65. // Always alloc mono Xform
  66. hg = GlobalAlloc(GMEM_FIXED | GMEM_SHARE, 2 * g_cmMonoByteSize);
  67. g_cmXformMono = MAKELP(hg, 0);
  68. if (!SELECTOROF(g_cmMungedCursor) || !SELECTOROF(g_cmXformMono))
  69. {
  70. ERROR_OUT(("Couldn't allocate cursor xform buffers"));
  71. DC_QUIT;
  72. }
  73. lpfnPatch = (LPBYTE)g_lpfnSetCursor;
  74. // If color cursors supported, alloc color image bits, again 2x the size
  75. if (GetDeviceCaps(hdcScreen, CAPS1) & C1_COLORCURSOR)
  76. {
  77. hg = GlobalAlloc(GMEM_FIXED | GMEM_SHARE, 2 * g_cmColorByteSize);
  78. if (!hg)
  79. {
  80. ERROR_OUT(("Couldn't allocate color cursor xform buffer"));
  81. DC_QUIT;
  82. }
  83. g_cmXformColor = MAKELP(hg, 0);
  84. }
  85. else
  86. {
  87. //
  88. // Older drivers (VGA and SUPERVGA e.g.) hook int2f and read their
  89. // DS from the SetCursor ddi prolog code, in many places. Therefore,
  90. // if we patch over this instruction, they will blow up. For these
  91. // drivers, we patch 3 bytes after the start, which leaves
  92. // mov ax, DGROUP
  93. // intact and is harmless. When we call the original routine, we call
  94. // back to the beginning, which will set up ax again before the body
  95. // of the ddi code.
  96. //
  97. // NOTE:
  98. // We use the color cursor caps for this detection. DRIVERVERSION
  99. // doesn't work, VGA et al. got restamped in Win95. This is the
  100. // most reliable way to decide if this is an older driver or not.
  101. //
  102. // NOTE 2:
  103. // We still want to decode this routine to see if it is of the form
  104. // mov ax, xxxx. If not, patch at the front anyway, or we'll write
  105. // possibly into the middle of an instruction.
  106. //
  107. if (*lpfnPatch == OPCODE_MOVAX)
  108. lpfnPatch = lpfnPatch + 3;
  109. }
  110. if (!CreateFnPatch(lpfnPatch, DrvSetPointerShape, &g_cmSetCursorPatch, 0))
  111. {
  112. ERROR_OUT(("Couldn't get cursor routines"));
  113. DC_QUIT;
  114. }
  115. g_cmSetCursorPatch.fInterruptable = TRUE;
  116. rc = TRUE;
  117. DC_EXIT_POINT:
  118. DebugExitBOOL(CM_DDInit, rc);
  119. return(rc);
  120. }
  121. //
  122. // CM_DDTerm()
  123. //
  124. void CM_DDTerm(void)
  125. {
  126. DebugEntry(CM_DDTerm);
  127. //
  128. // Clean up our patches
  129. //
  130. DestroyFnPatch(&g_cmSetCursorPatch);
  131. g_cmXformOn = FALSE;
  132. g_cmCursorHidden = FALSE;
  133. //
  134. // Free our memory blocks.
  135. //
  136. if (SELECTOROF(g_cmXformColor))
  137. {
  138. GlobalFree((HGLOBAL)SELECTOROF(g_cmXformColor));
  139. g_cmXformColor = NULL;
  140. }
  141. if (SELECTOROF(g_cmXformMono))
  142. {
  143. GlobalFree((HGLOBAL)SELECTOROF(g_cmXformMono));
  144. g_cmXformMono = NULL;
  145. }
  146. if (SELECTOROF(g_cmMungedCursor))
  147. {
  148. GlobalFree((HGLOBAL)SELECTOROF(g_cmMungedCursor));
  149. g_cmMungedCursor = NULL;
  150. }
  151. DebugExitVOID(CM_DDTerm);
  152. }
  153. //
  154. // CMDDSetTransform()
  155. //
  156. BOOL CMDDSetTransform(LPCM_DRV_XFORM_INFO pResult)
  157. {
  158. BOOL rc = FALSE;
  159. LPBYTE lpAND;
  160. DebugEntry(CMDDSetTransform);
  161. //
  162. // Turn off transform
  163. //
  164. // Do this first--that way if an interrupt comes in, we won't apply
  165. // some half-copied xform to the cursor. This can only happen for
  166. // an anicur. We jiggle the cursor below, which will reset the
  167. // xform if necessary.
  168. //
  169. g_cmXformOn = FALSE;
  170. //
  171. // If AND bitmap is NULL, we are turning the transform off. We also
  172. // do this if we can't get a 16:16 pointer to this memory
  173. //
  174. if (pResult->pANDMask == 0)
  175. {
  176. TRACE_OUT(("Clear transform"));
  177. rc = TRUE;
  178. }
  179. else
  180. {
  181. ASSERT(pResult->width == g_cxCursor);
  182. ASSERT(pResult->height == g_cyCursor);
  183. lpAND = MapLS(pResult->pANDMask);
  184. if (!SELECTOROF(lpAND))
  185. {
  186. ERROR_OUT(("Couldn't get AND mask pointer"));
  187. DC_QUIT;
  188. }
  189. hmemcpy(g_cmXformMono, lpAND, 2 * g_cmMonoByteSize);
  190. UnMapLS(lpAND);
  191. if (SELECTOROF(g_cmXformColor))
  192. {
  193. HBITMAP hbmMono = NULL;
  194. HBITMAP hbmMonoOld;
  195. HBITMAP hbmColorOld;
  196. HBITMAP hbmColor = NULL;
  197. HDC hdcMono = NULL;
  198. HDC hdcColor = NULL;
  199. //
  200. // Get color expanded version of the mask & image.
  201. // We do this blting the mono bitmap into a color one, then
  202. // getting the color bits.
  203. //
  204. hdcColor = CreateCompatibleDC(g_osiScreenDC);
  205. hbmColor = CreateCompatibleBitmap(g_osiScreenDC, g_cxCursor,
  206. 2*g_cyCursor);
  207. if (!hdcColor || !hbmColor)
  208. goto ColorError;
  209. hbmColorOld = SelectBitmap(hdcColor, hbmColor);
  210. hdcMono = CreateCompatibleDC(hdcColor);
  211. hbmMono = CreateBitmap(g_cxCursor, 2*g_cyCursor, 1, 1,
  212. g_cmXformMono);
  213. hbmMonoOld = SelectBitmap(hdcMono, hbmMono);
  214. if (!hdcMono || !hbmMono)
  215. goto ColorError;
  216. //
  217. // The defaults should be black & white for the text/back
  218. // colors, since we just created these DCs
  219. //
  220. ASSERT(GetBkColor(hdcColor) == RGB(255, 255, 255));
  221. ASSERT(GetTextColor(hdcColor) == RGB(0, 0, 0));
  222. ASSERT(GetBkColor(hdcMono) == RGB(255, 255, 255));
  223. ASSERT(GetTextColor(hdcMono) == RGB(0, 0, 0));
  224. BitBlt(hdcColor, 0, 0, g_cxCursor, 2*g_cyCursor, hdcMono,
  225. 0, 0, SRCCOPY);
  226. GetBitmapBits(hbmColor, 2*g_cmColorByteSize, g_cmXformColor);
  227. g_cmXformOn = TRUE;
  228. ColorError:
  229. if (hbmColor)
  230. {
  231. SelectBitmap(hdcColor, hbmColorOld);
  232. DeleteBitmap(hbmColor);
  233. }
  234. if (hdcColor)
  235. {
  236. DeleteDC(hdcColor);
  237. }
  238. if (hbmMono)
  239. {
  240. SelectBitmap(hdcMono, hbmMonoOld);
  241. DeleteBitmap(hbmMono);
  242. }
  243. if (hdcMono)
  244. {
  245. DeleteDC(hdcMono);
  246. }
  247. }
  248. else
  249. g_cmXformOn = TRUE;
  250. rc = (g_cmXformOn != 0);
  251. }
  252. DC_EXIT_POINT:
  253. //
  254. // Jiggle the cursor to get it to redraw with the new transform
  255. //
  256. CMDDJiggleCursor();
  257. DebugExitBOOL(CMDDSetTransform, rc);
  258. return(rc);
  259. }
  260. //
  261. // CM_DDViewing()
  262. //
  263. // We install our hooks & jiggle the cursor, if starting.
  264. // We remove our hooks, if stopping.
  265. //
  266. void CM_DDViewing(BOOL fViewers)
  267. {
  268. DebugEntry(CM_DDViewing);
  269. //
  270. // SetCursor() can be called at interrupt time for animated cursors.
  271. // Fortunately, we don't have to really pagelock the data segments
  272. // we touch. Animated cursors aren't allowed when you page through DOS.
  273. // When paging in protected mode, the guts of Windows can handle
  274. // page-ins during 16-bit ring3 reflected interrupts. Therfore
  275. // GlobalFix() works just fine.
  276. //
  277. if (fViewers)
  278. {
  279. // Do this BEFORE enabling patch
  280. GlobalFix(g_hInstAs16);
  281. GlobalFix((HGLOBAL)SELECTOROF((LPBYTE)DrvSetPointerShape));
  282. GlobalFix((HGLOBAL)SELECTOROF(g_cmMungedCursor));
  283. GlobalFix((HGLOBAL)SELECTOROF(g_cmXformMono));
  284. if (SELECTOROF(g_cmXformColor))
  285. GlobalFix((HGLOBAL)SELECTOROF(g_cmXformColor));
  286. }
  287. //
  288. // This enable will disable interrupts while copying bytes back and
  289. // forth. Animated cursors draw at interrupt time, and one could
  290. // come in while we're in the middle of copying the patch. The code
  291. // would blow up on half-copied instructions.
  292. //
  293. EnableFnPatch(&g_cmSetCursorPatch, (fViewers ? PATCH_ACTIVATE : PATCH_DEACTIVATE));
  294. if (!fViewers)
  295. {
  296. // Do this AFTER disabling patch
  297. if (SELECTOROF(g_cmXformColor))
  298. GlobalUnfix((HGLOBAL)SELECTOROF(g_cmXformColor));
  299. GlobalUnfix((HGLOBAL)SELECTOROF(g_cmXformMono));
  300. GlobalUnfix((HGLOBAL)SELECTOROF(g_cmMungedCursor));
  301. GlobalUnfix((HGLOBAL)SELECTOROF((LPBYTE)DrvSetPointerShape));
  302. GlobalUnfix(g_hInstAs16);
  303. }
  304. else
  305. {
  306. //
  307. // Jiggle the cursor to get the current image
  308. //
  309. CMDDJiggleCursor();
  310. }
  311. DebugExitVOID(CM_DDViewing);
  312. }
  313. //
  314. // CMDDJiggleCursor()
  315. // This causes the cursor to redraw with/without our tag.
  316. //
  317. void CMDDJiggleCursor(void)
  318. {
  319. DebugEntry(CMDDJiggleCursor);
  320. if (g_asSharedMemory)
  321. {
  322. //
  323. // Toggle full screen via WinOldAppHackOMatic(). This is the most
  324. // innocuous way I can come up with to force USER to refresh the
  325. // cursor with all the right parameters.
  326. //
  327. // If a full screen dos box is currently up, we don't need to do
  328. // anything--the user doesn't have a cursor, and the cursor will
  329. // refesh when we go back to windows mode anyway.
  330. //
  331. // Sometimes 16-bit code is beautiful! We own the win16lock,
  332. // so the two function calls below are atomic, and we know USER
  333. // won't do any calculation that would check the fullscreen state
  334. // while we're in the middle.
  335. //
  336. if (!g_asSharedMemory->fullScreen)
  337. {
  338. WinOldAppHackoMatic(WOAHACK_LOSINGDISPLAYFOCUS);
  339. WinOldAppHackoMatic(WOAHACK_GAININGDISPLAYFOCUS);
  340. }
  341. }
  342. DebugExitVOID(CMDDJiggleCursor);
  343. }
  344. //
  345. // DrvSetPointerShape()
  346. // This is the intercept for the display driver's SetCursor routine.
  347. //
  348. // NOTE THAT THIS CAN BE CALLED AT INTERRUPT TIME FOR ANIMATED CURSORS.
  349. //
  350. // While we can access our data (interrupt calls only happen when NOT
  351. // paging thru DOS, and protected mode paging can take pagefaults in ring3
  352. // reflected interrupt code), we can not call kernel routines that might
  353. // access non-fixed things.
  354. //
  355. // THIS MEANS NO DEBUG TRACING AT ALL IN THIS FUNCTION. AND NO CALLS TO
  356. // HMEMCPY.
  357. //
  358. // We must preserve EDX. Memphis display drivers get passed an instance
  359. // value from USER in this register. We only trash DX, so that's all we
  360. // need to save.
  361. //
  362. #pragma optimize("gle", off)
  363. BOOL WINAPI DrvSetPointerShape(LPCURSORSHAPE lpcur)
  364. {
  365. UINT dxSave;
  366. BOOL rc;
  367. UINT i;
  368. LPDWORD lpDst;
  369. LPDWORD lpSrc;
  370. LPCURSORSHAPE lpcurNew;
  371. LPCM_FAST_DATA lpcmShared;
  372. _asm mov dxSave, dx
  373. //
  374. // Call the original entry point in the driver with the xformed bits
  375. // NOTE:
  376. // For VGA/SUPERVGA et al, we patch at SetCursor+3 to leave the
  377. // move ax, dgroup instruction intact. We call through the org
  378. // routine to get ax reset up.
  379. //
  380. EnableFnPatch(&g_cmSetCursorPatch, PATCH_DISABLE);
  381. lpcurNew = XformCursorBits(lpcur);
  382. _asm mov dx, dxSave
  383. rc = g_lpfnSetCursor(lpcurNew);
  384. EnableFnPatch(&g_cmSetCursorPatch, PATCH_ENABLE);
  385. //
  386. // Did it succeed?
  387. //
  388. if (!rc)
  389. DC_QUIT;
  390. //
  391. // Hiding the cursor is done on Win95 by calling with NULL
  392. //
  393. if (!SELECTOROF(lpcur))
  394. {
  395. if (!g_cmCursorHidden)
  396. {
  397. CM_SHM_START_WRITING;
  398. g_asSharedMemory->cmCursorHidden = TRUE;
  399. CM_SHM_STOP_WRITING;
  400. g_cmCursorHidden = TRUE;
  401. }
  402. }
  403. else
  404. {
  405. // Set the bits first, THEN show the cursor to avoid flicker
  406. lpcmShared = CM_SHM_START_WRITING;
  407. //
  408. // NOTE: if this isn't the right size or a recognizable color
  409. // format, set a NULL cursor. This should never happen, but Win95's
  410. // own display driver has checks for it, and if it does we'll blue
  411. // screen if we do nothing.
  412. //
  413. if ((lpcur->cx != g_cxCursor) ||
  414. (lpcur->cy != g_cyCursor) ||
  415. ((lpcur->BitsPixel != 1) && (lpcur->BitsPixel != g_osiScreenBitsPlane)) ||
  416. ((lpcur->Planes != 1) && (lpcur->Planes != g_osiScreenPlanes)))
  417. {
  418. // Set 'null' cursor
  419. lpcmShared->cmCursorShapeData.hdr.cPlanes = 0xFF;
  420. lpcmShared->cmCursorShapeData.hdr.cBitsPerPel = 0xFF;
  421. goto CursorDone;
  422. }
  423. lpcmShared->cmCursorShapeData.hdr.ptHotSpot.x = lpcur->xHotSpot;
  424. lpcmShared->cmCursorShapeData.hdr.ptHotSpot.y = lpcur->yHotSpot;
  425. lpcmShared->cmCursorShapeData.hdr.cx = lpcur->cx;
  426. lpcmShared->cmCursorShapeData.hdr.cy = lpcur->cy;
  427. lpcmShared->cmCursorShapeData.hdr.cPlanes = lpcur->Planes;
  428. lpcmShared->cmCursorShapeData.hdr.cBitsPerPel = lpcur->BitsPixel;
  429. lpcmShared->cmCursorShapeData.hdr.cbRowWidth = lpcur->cbWidth;
  430. //
  431. // Can't call hmemcpy at interrupt time. So we copy a DWORD
  432. // at a time.
  433. //
  434. // LAURABU: NM 2.0 did this too. But maybe we should right this
  435. // in ASM for speed...
  436. //
  437. i = BitmapSize(lpcur->cx, lpcur->cy, 1, 1) +
  438. BitmapSize(lpcur->cx, lpcur->cy, lpcur->Planes, lpcur->BitsPixel);
  439. i >>= 2;
  440. lpDst = (LPDWORD)lpcmShared->cmCursorShapeData.data;
  441. lpSrc = (LPDWORD)(lpcur+1);
  442. while (i-- > 0)
  443. {
  444. *(lpDst++) = *(lpSrc++);
  445. }
  446. if ((lpcur->Planes == 1) && (lpcur->BitsPixel == 1))
  447. {
  448. //
  449. // Mono color table
  450. //
  451. lpcmShared->colorTable[0].peRed = 0;
  452. lpcmShared->colorTable[0].peGreen = 0;
  453. lpcmShared->colorTable[0].peBlue = 0;
  454. lpcmShared->colorTable[0].peFlags = 0;
  455. lpcmShared->colorTable[1].peRed = 255;
  456. lpcmShared->colorTable[1].peGreen = 255;
  457. lpcmShared->colorTable[1].peBlue = 255;
  458. lpcmShared->colorTable[1].peFlags = 0;
  459. }
  460. else if (g_osiScreenBPP <= 8)
  461. {
  462. UINT iBase;
  463. //
  464. // Color cursors for this depth only use VGA colors. So fill
  465. // in LOW 8 and HIGH 8, skip rest. There will be garbage in
  466. // the middle 256-16 colors for 256 color cursors, but none
  467. // of those RGBs are referenced in the bitmap data.
  468. //
  469. for (i = 0; i < 8; i++)
  470. {
  471. lpcmShared->colorTable[i] = g_osiVgaPalette[i];
  472. }
  473. if (g_osiScreenBPP == 4)
  474. iBase = 8;
  475. else
  476. iBase = 0xF8;
  477. for (i = 0; i < 8; i++)
  478. {
  479. lpcmShared->colorTable[i + iBase] = g_osiVgaPalette[i + 8];
  480. }
  481. }
  482. else
  483. {
  484. lpcmShared->bitmasks[0] = g_osiScreenRedMask;
  485. lpcmShared->bitmasks[1] = g_osiScreenGreenMask;
  486. lpcmShared->bitmasks[2] = g_osiScreenBlueMask;
  487. }
  488. CursorDone:
  489. lpcmShared->cmCursorStamp = g_cmNextCursorStamp++;
  490. if (g_cmCursorHidden)
  491. {
  492. g_asSharedMemory->cmCursorHidden = FALSE;
  493. g_cmCursorHidden = FALSE;
  494. }
  495. CM_SHM_STOP_WRITING;
  496. }
  497. DC_EXIT_POINT:
  498. return(rc);
  499. }
  500. #pragma optimize("", on)
  501. //
  502. // XformCursorBits()
  503. // This routine copies and transforms the cursor bits at the same time.
  504. // We return either the same thing passed in (if we can't xform it) or
  505. // our temp buffer g_cmXformMono.
  506. //
  507. LPCURSORSHAPE XformCursorBits
  508. (
  509. LPCURSORSHAPE lpOrg
  510. )
  511. {
  512. LPCURSORSHAPE lpResult;
  513. LPDWORD lpDst;
  514. LPDWORD lpSrc;
  515. LPDWORD lpXform;
  516. UINT cDwords;
  517. BOOL fColor;
  518. lpResult = lpOrg;
  519. //
  520. // If no xform is on, bail out
  521. //
  522. if (!g_cmXformOn || !SELECTOROF(lpOrg))
  523. DC_QUIT;
  524. //
  525. // If the cursor isn't the right size, forget it.
  526. //
  527. if ((lpOrg->cx != g_cxCursor) || (lpOrg->cy != g_cyCursor))
  528. DC_QUIT;
  529. //
  530. // If the cursor isn't monochrome or the color depth of the display,
  531. // forget it.
  532. //
  533. if ((lpOrg->Planes == 1) && (lpOrg->BitsPixel == 1))
  534. {
  535. // We handle this
  536. fColor = FALSE;
  537. }
  538. else if ((lpOrg->Planes == g_osiScreenPlanes) && (lpOrg->BitsPixel == g_osiScreenBitsPlane))
  539. {
  540. // We handle this
  541. fColor = TRUE;
  542. }
  543. else
  544. {
  545. // Unrecognized
  546. DC_QUIT;
  547. }
  548. //
  549. // OK, we can handle this
  550. //
  551. lpResult = g_cmMungedCursor;
  552. //
  553. // COPY THE HEADER
  554. //
  555. *lpResult = *lpOrg;
  556. //
  557. // FIRST:
  558. // AND the two masks together (both are mono)
  559. //
  560. lpDst = (LPDWORD)(lpResult+1);
  561. lpSrc = (LPDWORD)(lpOrg+1);
  562. lpXform = (LPDWORD)g_cmXformMono;
  563. cDwords = g_cmMonoByteSize >> 2;
  564. while (cDwords-- > 0)
  565. {
  566. *lpDst = (*lpSrc) & (*lpXform);
  567. lpDst++;
  568. lpSrc++;
  569. lpXform++;
  570. }
  571. //
  572. // SECOND:
  573. // AND the mask of the xform with the image of the cursor. If the
  574. // cursor is color, use the color-expanded mask of the xform
  575. //
  576. if (fColor)
  577. {
  578. lpXform = (LPDWORD)g_cmXformColor;
  579. cDwords = g_cmColorByteSize;
  580. }
  581. else
  582. {
  583. lpXform = (LPDWORD)g_cmXformMono;
  584. cDwords = g_cmMonoByteSize;
  585. }
  586. cDwords >>= 2;
  587. while (cDwords-- > 0)
  588. {
  589. *lpDst = (*lpSrc) & (*lpXform);
  590. lpDst++;
  591. lpSrc++;
  592. lpXform++;
  593. }
  594. //
  595. // LAST:
  596. // XOR the image of the xform with the image of the cursor
  597. //
  598. if (fColor)
  599. {
  600. lpXform = (LPDWORD)(g_cmXformColor + g_cmColorByteSize);
  601. cDwords = g_cmColorByteSize;
  602. }
  603. else
  604. {
  605. lpXform = (LPDWORD)(g_cmXformMono + g_cmMonoByteSize);
  606. cDwords = g_cmMonoByteSize;
  607. }
  608. cDwords >>= 2;
  609. lpDst = (LPDWORD)((LPBYTE)(lpResult+1) + g_cmMonoByteSize);
  610. while (cDwords-- > 0)
  611. {
  612. *lpDst ^= (*lpXform);
  613. lpDst++;
  614. lpXform++;
  615. }
  616. DC_EXIT_POINT:
  617. return(lpResult);
  618. }