Leaked source code of windows server 2003
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.

782 lines
23 KiB

  1. /****************************** Module Header ******************************\
  2. * Module Name: loadbits.c
  3. *
  4. * Copyright (c) 1985 - 1999, Microsoft Corporation
  5. *
  6. * Loads and creates icons / cursors / bitmaps. All 3 functions can either
  7. * load from a client resource file, load from user's resource file, or
  8. * load from the display's resource file. Beware that hmodules are not
  9. * unique across processes!
  10. *
  11. * 05-Apr-1991 ScottLu Rewrote to work with client/server
  12. \***************************************************************************/
  13. #include "precomp.h"
  14. #pragma hdrstop
  15. #include <wchar.h>
  16. /***************************************************************************\
  17. * _CreateEmptyCursorObject
  18. *
  19. * Creates a cursor object and links it into a cursor list.
  20. *
  21. * 08-Feb-92 ScottLu Created.
  22. \***************************************************************************/
  23. HCURSOR _CreateEmptyCursorObject(
  24. BOOL fPublic)
  25. {
  26. PCURSOR pcurT;
  27. /*
  28. * Create the cursor object.
  29. */
  30. pcurT = (PCURSOR)HMAllocObject(PtiCurrent(),
  31. NULL,
  32. TYPE_CURSOR,
  33. max(sizeof(CURSOR),
  34. sizeof(ACON)));
  35. if ((fPublic) && (pcurT != NULL) && (ISCSRSS())) {
  36. pcurT->head.ppi = NULL;
  37. }
  38. return (HCURSOR)PtoH(pcurT);
  39. }
  40. /***************************************************************************\
  41. * DestroyEmptyCursorObject
  42. * UnlinkCursor
  43. *
  44. * Destroys an empty cursor object (structure holds nothing that needs
  45. * destroying).
  46. *
  47. * 08-Feb-1992 ScottLu Created.
  48. \***************************************************************************/
  49. VOID UnlinkCursor(
  50. PCURSOR pcur)
  51. {
  52. PCURSOR *ppcurT;
  53. BOOL fTriedPublicCache;
  54. BOOL fTriedThisProcessCache = FALSE;
  55. /*
  56. * First unlink this cursor object from the cursor list (it will be the
  57. * first one in the list, so this'll be fast... but just in case, make
  58. * it a loop).
  59. */
  60. if (fTriedPublicCache = (pcur->head.ppi == NULL)) {
  61. ppcurT = &gpcurFirst;
  62. } else {
  63. ppcurT = &pcur->head.ppi->pCursorCache;
  64. }
  65. LookAgain:
  66. for (; *ppcurT != NULL; ppcurT = &((*ppcurT)->pcurNext)) {
  67. if (*ppcurT == pcur) {
  68. *ppcurT = pcur->pcurNext;
  69. FreeIt:
  70. pcur->pcurNext = NULL;
  71. pcur->CURSORF_flags &= ~CURSORF_LINKED;
  72. return;
  73. }
  74. }
  75. /*
  76. * If we get here, it means that the cursor used to be public but
  77. * got assigned to the current thread due to being unlocked. We
  78. * have to look for it in the public cache.
  79. */
  80. if (!fTriedPublicCache) {
  81. ppcurT = &gpcurFirst;
  82. fTriedPublicCache = TRUE;
  83. goto LookAgain;
  84. }
  85. /*
  86. * If we got here, it means that it was locked during process
  87. * cleanup and got assigned to no owner. Try the current process
  88. * cache.
  89. */
  90. if (!fTriedThisProcessCache) {
  91. ppcurT = &PpiCurrent()->pCursorCache;
  92. fTriedThisProcessCache = TRUE;
  93. goto LookAgain;
  94. }
  95. /*
  96. * Getting Desperate here... Look through every cursor and process
  97. * cache for it.
  98. */
  99. {
  100. PHE pheMax, pheT;
  101. pheMax = &gSharedInfo.aheList[giheLast];
  102. for (pheT = gSharedInfo.aheList; pheT <= pheMax; pheT++) {
  103. if (pheT->bType == TYPE_CURSOR) {
  104. if (((PCURSOR)pheT->phead)->pcurNext == pcur) {
  105. ((PCURSOR)pheT->phead)->pcurNext = pcur->pcurNext;
  106. goto FreeIt;
  107. } else if (pheT->pOwner && ((PPROCESSINFO)pheT->pOwner)->pCursorCache == pcur) {
  108. ((PPROCESSINFO)pheT->pOwner)->pCursorCache = pcur->pcurNext;
  109. goto FreeIt;
  110. }
  111. }
  112. }
  113. }
  114. UserAssert(FALSE);
  115. }
  116. /***************************************************************************\
  117. * DestroyEmptyCursorObject
  118. *
  119. \***************************************************************************/
  120. VOID DestroyEmptyCursorObject(
  121. PCURSOR pcur)
  122. {
  123. if (pcur->CURSORF_flags & CURSORF_LINKED) {
  124. UnlinkCursor(pcur);
  125. }
  126. HMFreeObject(pcur);
  127. }
  128. /***************************************************************************\
  129. * ZombieCursor
  130. *
  131. * Unlink the cursor and set its owner to the system process.
  132. *
  133. * 3-Sep-1997 vadimg created
  134. \***************************************************************************/
  135. VOID ZombieCursor(PCURSOR pcur)
  136. {
  137. if (pcur->CURSORF_flags & CURSORF_LINKED) {
  138. UnlinkCursor(pcur);
  139. }
  140. #if DBG
  141. if (ISTS()) {
  142. PHE phe;
  143. phe = HMPheFromObject(pcur);
  144. if (phe->pOwner == NULL) {
  145. RIPMSG2(RIP_ERROR, "NULL owner for cursor %#p phe %#p\n",
  146. pcur, phe);
  147. }
  148. }
  149. #endif // DBG
  150. HMChangeOwnerProcess(pcur, gptiRit);
  151. RIPMSG1(RIP_WARNING, "ZombieCursor: %#p became a zombie", pcur);
  152. }
  153. /***************************************************************************\
  154. * ResStrCmp
  155. *
  156. * This function compares two strings taking into account that one or both
  157. * of them may be resource IDs. The function returns a TRUE if the strings
  158. * are equal, instead of the zero lstrcmp() returns.
  159. *
  160. * History:
  161. * 20-Apr-91 DavidPe Created
  162. \***************************************************************************/
  163. BOOL ResStrCmp(
  164. PUNICODE_STRING cczpstr1,
  165. PUNICODE_STRING pstr2)
  166. {
  167. BOOL retval = FALSE;
  168. /*
  169. * pstr1 is a STRING that is in kernel space, but the buffer may
  170. * be in client space.
  171. */
  172. if (cczpstr1->Length == 0) {
  173. /*
  174. * pstr1 is a resource ID, so just compare the values.
  175. */
  176. if (cczpstr1->Buffer == pstr2->Buffer)
  177. return TRUE;
  178. } else {
  179. try {
  180. /*
  181. * pstr1 is a string. if pstr2 is an actual string compare the
  182. * string values; if pstr2 is not a string then pstr1 may be an
  183. * "integer string" of the form "#123456". so convert it to an
  184. * integer and compare the integers.
  185. * Before calling lstrcmp(), make sure pstr2 is an actual
  186. * string, not a resource ID.
  187. */
  188. if (pstr2->Length != 0) {
  189. if (RtlEqualUnicodeString(cczpstr1, pstr2, TRUE))
  190. retval = TRUE;
  191. } else if (cczpstr1->Buffer[0] == '#') {
  192. UNICODE_STRING strId;
  193. int id;
  194. strId.Length = cczpstr1->Length - sizeof(WCHAR);
  195. strId.MaximumLength = strId.Length;
  196. strId.Buffer = cczpstr1->Buffer + 1;
  197. RtlUnicodeStringToInteger(&strId, 10, (PULONG)&id);
  198. if (id == (LONG_PTR)pstr2->Buffer)
  199. retval = TRUE;
  200. }
  201. } except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
  202. }
  203. }
  204. return retval;
  205. }
  206. /***********************************************************************\
  207. * SearchIconCache
  208. *
  209. * Worker routine for FindExistingCursorIcon().
  210. *
  211. * Returns: pCurFound
  212. *
  213. * 28-Sep-1995 SanfordS Created.
  214. \***********************************************************************/
  215. PCURSOR SearchIconCache(
  216. PCURSOR pCursorCache,
  217. ATOM atomModName,
  218. PUNICODE_STRING cczpstrResName,
  219. PCURSOR pcurSrc,
  220. PCURSORFIND pcfSearch)
  221. {
  222. /*
  223. * Run through the list of 'resource' objects created,
  224. * and see if the cursor requested has already been loaded.
  225. * If so just return it. We do this to be consistent with
  226. * Win 3.0 where they simply return a pointer to the res-data
  227. * for a cursor/icon handle. Many apps count on this and
  228. * call LoadCursor/Icon() often.
  229. *
  230. * LR_SHARED implies:
  231. * 1) icons never get deleted till process (LATER or WOW module)
  232. * goes away.
  233. * 2) This cache is consulted before trying to load a res.
  234. */
  235. for (; pCursorCache != NULL; pCursorCache = pCursorCache->pcurNext) {
  236. /*
  237. * If we are given a specific cursor to look for, then
  238. * search for that first.
  239. */
  240. if (pcurSrc && (pCursorCache == pcurSrc))
  241. return pcurSrc;
  242. /*
  243. * No need to look further if the module name doesn't match.
  244. */
  245. if (atomModName != pCursorCache->atomModName)
  246. continue;
  247. /*
  248. * We only return images that cannot be destroyed by the app.
  249. * so we don't have to deal with ref counts. This is owned
  250. * by us, but not LR_SHARED.
  251. */
  252. if (!(pCursorCache->CURSORF_flags & CURSORF_LRSHARED))
  253. continue;
  254. /*
  255. * Check the other distinguishing search criteria for
  256. * a match.
  257. */
  258. if ((pCursorCache->rt == LOWORD(pcfSearch->rt)) &&
  259. ResStrCmp(cczpstrResName, &pCursorCache->strName)) {
  260. /*
  261. * Acons don't have a size per se because each frame
  262. * can be a different size. We always make it a hit
  263. * on acons so replacement of system icons is possible.
  264. */
  265. if (pCursorCache->CURSORF_flags & CURSORF_ACON)
  266. return pCursorCache;
  267. /*
  268. * First hit wins. Nothing fancy here. Apps that use
  269. * LR_SHARED have to watch out for this.
  270. */
  271. if ((!pcfSearch->cx || (pCursorCache->cx == pcfSearch->cx)) &&
  272. (!pcfSearch->cy || ((pCursorCache->cy / 2) == pcfSearch->cy)) &&
  273. (!pcfSearch->bpp || (pCursorCache->bpp == pcfSearch->bpp))) {
  274. return pCursorCache;
  275. }
  276. }
  277. }
  278. return NULL;
  279. }
  280. /***********************************************************************\
  281. * _FindExistingCursorIcon
  282. *
  283. * This routine searches all existing icons for one matching the properties
  284. * given. This routine will only return cursors/icons that are of
  285. * the type that cannot be destroyed by the app. (CURSORF_LRSHARED or
  286. * unowned) and will take the first hit it finds.
  287. *
  288. * 32bit apps that call LoadImage() will normally not have this cacheing
  289. * feature unless they specify LR_SHARED. If they do so, it is the apps
  290. * responsability to be careful with how they use the cache since wild
  291. * lookups (ie 0s in cx, cy or bpp) will result in different results
  292. * depending on the history of icon/cursor creation. It is thus recommended
  293. * that apps only use the LR_SHARED option when they are only working
  294. * with one size/colordepth of icon or when they call LoadImage() with
  295. * specific size and/or color content requested.
  296. *
  297. * For the future it would be nice to have a cacheing scheeme that would
  298. * simply be used to speed up reloading of images. To do this right,
  299. * you would need ref counts to allow deletes to work properly and would
  300. * have to remember whether the images in the cache had been stretched
  301. * or color munged so you don't allow restretching.
  302. *
  303. * Returns: pcurFound
  304. *
  305. *
  306. * 17-Sep-1995 SanfordS Created.
  307. \***********************************************************************/
  308. PCURSOR _FindExistingCursorIcon(
  309. ATOM atomModName,
  310. PUNICODE_STRING cczpstrResName,
  311. PCURSOR pcurSrc,
  312. PCURSORFIND pcfSearch)
  313. {
  314. PCURSOR pcurT = NULL;
  315. /*
  316. * If rt is zero we're doing an indirect create, so matching with
  317. * a previously loaded cursor/icon would be inappropriate.
  318. */
  319. if (pcfSearch->rt && atomModName) {
  320. pcurT = SearchIconCache(PpiCurrent()->pCursorCache,
  321. atomModName,
  322. cczpstrResName,
  323. pcurSrc,
  324. pcfSearch);
  325. if (pcurT == NULL) {
  326. pcurT = SearchIconCache(gpcurFirst,
  327. atomModName,
  328. cczpstrResName,
  329. pcurSrc,
  330. pcfSearch);
  331. }
  332. }
  333. return pcurT;
  334. }
  335. /***************************************************************************\
  336. * _InternalGetIconInfo
  337. *
  338. * History:
  339. * 09-Mar-1993 MikeKe Created.
  340. \***************************************************************************/
  341. BOOL _InternalGetIconInfo(
  342. IN PCURSOR pcur,
  343. OUT PICONINFO ccxpiconinfo,
  344. OUT OPTIONAL PUNICODE_STRING pstrInstanceName,
  345. OUT OPTIONAL PUNICODE_STRING pstrResName,
  346. OUT OPTIONAL LPDWORD ccxpbpp,
  347. IN BOOL fInternalCursor)
  348. {
  349. HBITMAP hbmBitsT;
  350. HBITMAP hbmDstT;
  351. HBITMAP hbmMask;
  352. HBITMAP hbmColor;
  353. /*
  354. * Note -- while the STRING structures are in kernel mode memory, the
  355. * buffers are in user-mode memory. So all use of the buffers should
  356. * be protected bytry blocks.
  357. */
  358. /*
  359. * If this is an animated cursor, just grab the first frame and return
  360. * the info for it.
  361. */
  362. if (pcur->CURSORF_flags & CURSORF_ACON)
  363. pcur = ((PACON)pcur)->aspcur[0];
  364. /*
  365. * Make copies of the bitmaps
  366. *
  367. * If the color bitmap is around, then there is no XOR mask in the
  368. * hbmMask bitmap.
  369. */
  370. hbmMask = GreCreateBitmap(
  371. pcur->cx,
  372. (pcur->hbmColor && !fInternalCursor) ? pcur->cy / 2 : pcur->cy,
  373. 1,
  374. 1,
  375. NULL);
  376. if (hbmMask == NULL)
  377. return FALSE;
  378. hbmColor = NULL;
  379. if (pcur->hbmColor != NULL) {
  380. if (pcur->bpp == 32) {
  381. BITMAPINFO bi;
  382. RtlZeroMemory(&bi, sizeof(bi));
  383. bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
  384. bi.bmiHeader.biWidth = pcur->cx;
  385. bi.bmiHeader.biHeight = pcur->cy/2;
  386. bi.bmiHeader.biPlanes = 1;
  387. bi.bmiHeader.biBitCount = 32;
  388. bi.bmiHeader.biCompression = BI_RGB;
  389. bi.bmiHeader.biSizeImage = 0;
  390. bi.bmiHeader.biClrUsed = 0;
  391. bi.bmiHeader.biClrImportant = 0;
  392. hbmColor = GreCreateDIBitmapReal(HDCBITS(),
  393. 0,
  394. NULL,
  395. (LPBITMAPINFO)&bi,
  396. DIB_RGB_COLORS,
  397. sizeof(bi),
  398. 0,
  399. NULL,
  400. 0,
  401. NULL,
  402. 0,
  403. 0,
  404. NULL);
  405. } else {
  406. hbmColor = GreCreateCompatibleBitmap(HDCBITS(),
  407. pcur->cx,
  408. pcur->cy / 2);
  409. }
  410. if (hbmColor == NULL) {
  411. GreDeleteObject(hbmMask);
  412. return FALSE;
  413. }
  414. }
  415. hbmBitsT = GreSelectBitmap(ghdcMem2, pcur->hbmMask);
  416. hbmDstT = GreSelectBitmap(ghdcMem, hbmMask);
  417. GreBitBlt(ghdcMem,
  418. 0,
  419. 0,
  420. pcur->cx,
  421. (pcur->hbmColor && !fInternalCursor) ? pcur->cy / 2 : pcur->cy,
  422. ghdcMem2,
  423. 0,
  424. 0,
  425. SRCCOPY,
  426. 0x00ffffff);
  427. if (hbmColor != NULL) {
  428. GreSelectBitmap(ghdcMem2, pcur->hbmColor);
  429. GreSelectBitmap(ghdcMem, hbmColor);
  430. GreBitBlt(ghdcMem,
  431. 0,
  432. 0,
  433. pcur->cx,
  434. pcur->cy / 2,
  435. ghdcMem2,
  436. 0,
  437. 0,
  438. SRCCOPY,
  439. 0);
  440. }
  441. GreSelectBitmap(ghdcMem2, hbmBitsT);
  442. GreSelectBitmap(ghdcMem, hbmDstT);
  443. /*
  444. * Fill in the iconinfo structure. make copies of the bitmaps.
  445. */
  446. try {
  447. ccxpiconinfo->fIcon = (pcur->rt == PTR_TO_ID(RT_ICON));
  448. ccxpiconinfo->xHotspot = pcur->xHotspot;
  449. ccxpiconinfo->yHotspot = pcur->yHotspot;
  450. ccxpiconinfo->hbmMask = hbmMask;
  451. ccxpiconinfo->hbmColor = hbmColor;
  452. if (pstrInstanceName != NULL) {
  453. if (pcur->atomModName) {
  454. pstrInstanceName->Length = (USHORT)
  455. UserGetAtomName(pcur->atomModName,
  456. pstrInstanceName->Buffer,
  457. (int) (pstrInstanceName->MaximumLength / sizeof(WCHAR))
  458. * sizeof(WCHAR));
  459. } else {
  460. pstrInstanceName->Length = 0;
  461. }
  462. }
  463. if (pstrResName != NULL) {
  464. if (IS_PTR(pcur->strName.Buffer)) {
  465. RtlCopyUnicodeString(pstrResName, &pcur->strName);
  466. } else {
  467. *pstrResName = pcur->strName;
  468. }
  469. }
  470. if (ccxpbpp)
  471. *ccxpbpp = pcur->bpp;
  472. } except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
  473. GreDeleteObject(hbmMask);
  474. GreDeleteObject(hbmColor);
  475. return FALSE;
  476. }
  477. return TRUE;
  478. }
  479. /***************************************************************************\
  480. * _DestroyCursor
  481. *
  482. * History:
  483. * 25-Apr-1991 DavidPe Created.
  484. * 04-Aug-1992 DarrinM Now destroys ACONs as well.
  485. \***************************************************************************/
  486. BOOL _DestroyCursor(
  487. PCURSOR pcur,
  488. DWORD cmdDestroy)
  489. {
  490. PPROCESSINFO ppi;
  491. PPROCESSINFO ppiCursor;
  492. int i;
  493. extern BOOL DestroyAniIcon(PACON pacon);
  494. if (pcur == NULL) {
  495. UserAssert(FALSE);
  496. return(TRUE);
  497. }
  498. ppi = PpiCurrent();
  499. ppiCursor = GETPPI(pcur);
  500. /*
  501. * Remove this icon from the caption icon cache.
  502. */
  503. for (i = 0; i < CCACHEDCAPTIONS; i++) {
  504. if (gcachedCaptions[i].spcursor == pcur) {
  505. Unlock( &(gcachedCaptions[i].spcursor) );
  506. }
  507. }
  508. /*
  509. * First step in destroying an cursor
  510. */
  511. switch (cmdDestroy) {
  512. case CURSOR_ALWAYSDESTROY:
  513. /*
  514. * Always destroy? then don't do any checking...
  515. */
  516. break;
  517. case CURSOR_CALLFROMCLIENT:
  518. /*
  519. * Can't destroy public cursors/icons.
  520. */
  521. if (ppiCursor == NULL)
  522. /*
  523. * Fake success if its a resource loaded icon because
  524. * this is how win95 responded.
  525. */
  526. return !!(pcur->CURSORF_flags & CURSORF_FROMRESOURCE);
  527. /*
  528. * If this cursor was loaded from a resource, don't free it till the
  529. * process exits. This is the way we stay compatible with win3.0's
  530. * cursors which were actually resources. Resources under win3 have
  531. * reference counting and other "features" like handle values that
  532. * never change. Read more in the comment in
  533. * ServerLoadCreateCursorIcon().
  534. */
  535. if (pcur->CURSORF_flags & (CURSORF_LRSHARED | CURSORF_SECRET)) {
  536. return TRUE;
  537. }
  538. /*
  539. * One thread can't destroy the objects created by another.
  540. */
  541. if (ppiCursor != ppi) {
  542. RIPERR0(ERROR_DESTROY_OBJECT_OF_OTHER_THREAD, RIP_ERROR, "DestroyCursor: cursor belongs to another process");
  543. return FALSE;
  544. }
  545. /*
  546. * fall through.
  547. */
  548. case CURSOR_THREADCLEANUP:
  549. /*
  550. * Don't destroy public objects either (pretend it worked though).
  551. */
  552. if (ppiCursor == NULL)
  553. return TRUE;
  554. break;
  555. }
  556. /*
  557. * First mark the object for destruction. This tells the locking code that
  558. * we want to destroy this object when the lock count goes to 0. If this
  559. * returns FALSE, we can't destroy the object yet.
  560. */
  561. if (!HMMarkObjectDestroy((PHEAD)pcur))
  562. return FALSE;
  563. if (pcur->strName.Length != 0) {
  564. UserFreePool((LPSTR)pcur->strName.Buffer);
  565. }
  566. if (pcur->atomModName != 0) {
  567. UserDeleteAtom(pcur->atomModName);
  568. }
  569. /*
  570. * If this is an ACON call its special routine to destroy it.
  571. */
  572. if (pcur->CURSORF_flags & CURSORF_ACON) {
  573. DestroyAniIcon((PACON)pcur);
  574. } else {
  575. if (pcur->hbmMask != NULL) {
  576. GreDeleteObject(pcur->hbmMask);
  577. GreDecQuotaCount((PW32PROCESS)(pcur->head.ppi));
  578. }
  579. if (pcur->hbmColor != NULL) {
  580. GreDeleteObject(pcur->hbmColor);
  581. GreDecQuotaCount((PW32PROCESS)(pcur->head.ppi));
  582. }
  583. if (pcur->hbmUserAlpha != NULL) {
  584. GreDeleteObject(pcur->hbmUserAlpha);
  585. GreDecQuotaCount((PW32PROCESS)(pcur->head.ppi));
  586. }
  587. if (pcur->hbmAlpha != NULL) {
  588. /*
  589. * This is an internal GDI object, and so not covered by quota.
  590. */
  591. GreDeleteObject(pcur->hbmAlpha);
  592. }
  593. }
  594. /*
  595. * Ok to destroy... Free the handle (which will free the object and the
  596. * handle).
  597. */
  598. DestroyEmptyCursorObject(pcur);
  599. return TRUE;
  600. }
  601. /***************************************************************************\
  602. * DestroyUnlockedCursor
  603. *
  604. * Called when a cursor is destoyed due to an unlock.
  605. *
  606. * History:
  607. * 24-Feb-1997 adams Created.
  608. \***************************************************************************/
  609. void
  610. DestroyUnlockedCursor(void * pv)
  611. {
  612. _DestroyCursor((PCURSOR)pv, CURSOR_THREADCLEANUP);
  613. }
  614. /***************************************************************************\
  615. * _SetCursorContents
  616. *
  617. *
  618. * History:
  619. * 27-Apr-1992 ScottLu Created.
  620. \***************************************************************************/
  621. BOOL _SetCursorContents(
  622. PCURSOR pcur,
  623. PCURSOR pcurNew)
  624. {
  625. HBITMAP hbmpT;
  626. if (!(pcur->CURSORF_flags & CURSORF_ACON)) {
  627. /*
  628. * Swap bitmaps.
  629. */
  630. hbmpT = pcur->hbmMask;
  631. pcur->hbmMask = pcurNew->hbmMask;
  632. pcurNew->hbmMask = hbmpT;
  633. hbmpT = pcur->hbmColor;
  634. pcur->hbmColor = pcurNew->hbmColor;
  635. pcurNew->hbmColor = hbmpT;
  636. hbmpT = pcur->hbmUserAlpha;
  637. pcur->hbmUserAlpha = pcurNew->hbmUserAlpha;
  638. pcurNew->hbmUserAlpha = hbmpT;
  639. hbmpT = pcur->hbmAlpha;
  640. pcur->hbmAlpha = pcurNew->hbmAlpha;
  641. pcurNew->hbmAlpha = hbmpT;
  642. /*
  643. * Remember hotspot info and size info
  644. */
  645. pcur->xHotspot = pcurNew->xHotspot;
  646. pcur->yHotspot = pcurNew->yHotspot;
  647. pcur->cx = pcurNew->cx;
  648. pcur->cy = pcurNew->cy;
  649. pcur->bpp = pcurNew->bpp;
  650. pcur->rt = pcurNew->rt;
  651. pcur->rcBounds = pcurNew->rcBounds;
  652. }
  653. /*
  654. * Destroy the cursor we copied from.
  655. */
  656. _DestroyCursor(pcurNew, CURSOR_THREADCLEANUP);
  657. /*
  658. * If the current logical cursor is changing then force the current physical
  659. * cursor to change.
  660. */
  661. if (gpcurLogCurrent == pcur) {
  662. gpcurLogCurrent = NULL;
  663. gpcurPhysCurrent = NULL;
  664. zzzUpdateCursorImage();
  665. }
  666. return TRUE;
  667. }