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.

6366 lines
206 KiB

  1. /****************************** Module Header ******************************\
  2. * Module Name: swp.c
  3. *
  4. * Copyright (c) 1985 - 1999, Microsoft Corporation
  5. *
  6. * Contains the xxxSetWindowPos API and related functions.
  7. *
  8. * History:
  9. * 20-Oct-1990 DarrinM Created.
  10. * 25-Jan-1991 IanJa added window revalidation
  11. * 11-Jul-1991 DarrinM Replaced everything with re-ported Win 3.1 code.
  12. \***************************************************************************/
  13. #include "precomp.h"
  14. #pragma hdrstop
  15. #define CTM_NOCHANGE 0
  16. #define CTM_TOPMOST 1
  17. #define CTM_NOTOPMOST 2
  18. VOID FixBogusSWP(PWND pwnd, int * px, int * py, int cx, int cy, UINT flags);
  19. VOID PreventInterMonitorBlts(PCVR pcvr);
  20. /***************************************************************************\
  21. * DBGCheckSMWP
  22. *
  23. * SMWP can be a HM object, a cached structure or just a pool allocation
  24. *
  25. * History:
  26. * 05/21/98 GerardoB Created.
  27. \***************************************************************************/
  28. #if DBG
  29. VOID DBGCheckSMWP(
  30. PSMWP psmwp)
  31. {
  32. if (psmwp->bHandle) {
  33. UserAssert(psmwp->head.h != NULL);
  34. UserAssert(psmwp == HtoPqCat(PtoHq(psmwp)));
  35. UserAssert(psmwp != &gSMWP);
  36. } else {
  37. UserAssert((psmwp->head.h == NULL) && (psmwp->head.cLockObj == 0));
  38. if (psmwp == &gSMWP) {
  39. UserAssert(TEST_PUDF(PUDF_GSMWPINUSE));
  40. }
  41. }
  42. UserAssert(psmwp->ccvr <= psmwp->ccvrAlloc);
  43. UserAssert(psmwp->acvr != NULL);
  44. }
  45. #else
  46. #define DBGCheckSMWP(psmwp)
  47. #endif // DBG
  48. /***************************************************************************\
  49. * DestroySMWP
  50. *
  51. * Destroys an SMWP object.
  52. *
  53. * History:
  54. * 24-Feb-1997 adams Created.
  55. \***************************************************************************/
  56. VOID DestroySMWP(
  57. PSMWP psmwp)
  58. {
  59. BOOL fFree;
  60. CheckCritIn();
  61. DBGCheckSMWP(psmwp);
  62. /*
  63. * First mark the object for destruction. This tells the locking code
  64. * that we want to destroy this object when the lock count goes to 0.
  65. * If this returns FALSE, we can't destroy the object yet.
  66. */
  67. if (psmwp->bHandle) {
  68. if (!HMMarkObjectDestroy(psmwp)) {
  69. return;
  70. }
  71. fFree = TRUE;
  72. } else {
  73. /*
  74. * Is this the global cached structure?
  75. */
  76. fFree = (psmwp != &gSMWP);
  77. }
  78. if (psmwp->acvr) {
  79. /*
  80. * Free any hrgnInterMonitor stuff we accumulated.
  81. */
  82. PCVR pcvr;
  83. int ccvr;
  84. for (pcvr = psmwp->acvr, ccvr = psmwp->ccvr; --ccvr >= 0; pcvr++) {
  85. if (pcvr->hrgnInterMonitor != NULL) {
  86. GreDeleteObject(pcvr->hrgnInterMonitor);
  87. }
  88. }
  89. if (fFree) {
  90. UserFreePool(psmwp->acvr);
  91. }
  92. }
  93. /*
  94. * Ok to destroy ... Free the handle (which will free the object
  95. * and the handle).
  96. */
  97. if (psmwp->bHandle) {
  98. HMFreeObject(psmwp);
  99. } else if (fFree) {
  100. UserFreePool(psmwp);
  101. } else {
  102. UserAssert(TEST_PUDF(PUDF_GSMWPINUSE));
  103. CLEAR_PUDF(PUDF_GSMWPINUSE);
  104. /*
  105. * If acvr grew too much, shrink it.
  106. * Don't use realloc since we don't care about the left over data
  107. * [msadek], should this be ">=8" since we usually grow it from 4->8 in
  108. * _DeferWindowPos?
  109. */
  110. if (psmwp->ccvrAlloc > 8) {
  111. PCVR pcvr = UserAllocPool(4 * sizeof(CVR), TAG_SWP);
  112. if (pcvr != NULL) {
  113. UserFreePool(psmwp->acvr);
  114. psmwp->acvr = pcvr;
  115. psmwp->ccvrAlloc = 4;
  116. }
  117. }
  118. }
  119. }
  120. #define MW_FLAGS_REDRAW (SWP_NOZORDER | SWP_NOACTIVATE)
  121. #define MW_FLAGS_NOREDRAW (SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREDRAW)
  122. /***************************************************************************\
  123. * MoveWindow (API)
  124. *
  125. *
  126. * History:
  127. * 25-Jul-1991 DarrinM Ported from Win 3.1 sources.
  128. \***************************************************************************/
  129. BOOL xxxMoveWindow(
  130. PWND pwnd,
  131. int x,
  132. int y,
  133. int cx,
  134. int cy,
  135. BOOL fRedraw)
  136. {
  137. CheckLock(pwnd);
  138. if ((pwnd == PWNDDESKTOP(pwnd)) ||
  139. TestWF(pwnd, WFWIN31COMPAT) ||
  140. (pwnd->spwndParent != PWNDDESKTOP(pwnd))) {
  141. return xxxSetWindowPos(
  142. pwnd,
  143. NULL,
  144. x,
  145. y,
  146. cx,
  147. cy,
  148. (fRedraw ? MW_FLAGS_REDRAW : MW_FLAGS_NOREDRAW));
  149. } else {
  150. /*
  151. * BACKWARD COMPATIBILITY CODE FOR WIN 3.00 AND BELOW
  152. *
  153. * Everyone and their brother seems to depend on this behavior for
  154. * top-level windows. Specific examples are:
  155. *
  156. * AfterDark help window animation
  157. * Finale Speedy Note Editing
  158. *
  159. * If the window is a top-level window and fRedraw is FALSE,
  160. * we must call SetWindowPos with SWP_NOREDRAW CLEAR anyway so that
  161. * the frame and window background get drawn. We then validate the
  162. * entire client rectangle to avoid repainting that.
  163. */
  164. BOOL fResult = xxxSetWindowPos(pwnd,
  165. NULL,
  166. x,
  167. y,
  168. cx,
  169. cy,
  170. MW_FLAGS_REDRAW);
  171. if (!fRedraw) {
  172. xxxValidateRect(pwnd, NULL);
  173. }
  174. return fResult;
  175. }
  176. }
  177. /***************************************************************************\
  178. * AllocateCvr
  179. *
  180. * History:
  181. * 05/20/98 GerardoB Extracted from old _BeginDeferWindowPos
  182. \***************************************************************************/
  183. BOOL AllocateCvr(
  184. PSMWP psmwp,
  185. int cwndHint)
  186. {
  187. PCVR acvr;
  188. UserAssert(cwndHint != 0);
  189. if (cwndHint > (INT_MAX / sizeof(CVR))) {
  190. return FALSE;
  191. }
  192. if (psmwp == &gSMWP) {
  193. UserAssert(psmwp->bHandle == FALSE);
  194. acvr = (PCVR)UserAllocPool(sizeof(CVR) * cwndHint, TAG_SWP);
  195. } else {
  196. acvr = (PCVR)UserAllocPoolWithQuota(sizeof(CVR) * cwndHint, TAG_SWP);
  197. }
  198. if (acvr == NULL) {
  199. return FALSE;
  200. }
  201. /*
  202. * Initialize psmwp related fields.
  203. * CVR array is initialized by _DeferWindowPos
  204. */
  205. psmwp->acvr = acvr;
  206. psmwp->ccvrAlloc = cwndHint;
  207. psmwp->ccvr = 0;
  208. return TRUE;
  209. }
  210. /***************************************************************************\
  211. * InternalBeginDeferWindowPos
  212. *
  213. * History:
  214. * 05/20/98 GerardoB Created
  215. \***************************************************************************/
  216. PSMWP InternalBeginDeferWindowPos(
  217. int cwndHint)
  218. {
  219. PSMWP psmwp;
  220. CheckCritIn();
  221. /*
  222. * If gSMWP in being used, allocate one.
  223. * Note that SMWP is zero init but CVR is not; _DeferWindowPos initializes it.
  224. */
  225. if (TEST_PUDF(PUDF_GSMWPINUSE) || (cwndHint > gSMWP.ccvrAlloc)) {
  226. psmwp = (PSMWP)UserAllocPoolWithQuotaZInit(sizeof(SMWP), TAG_SWP);
  227. if (psmwp == NULL) {
  228. return NULL;
  229. }
  230. if (!AllocateCvr(psmwp, cwndHint)) {
  231. UserFreePool(psmwp);
  232. return NULL;
  233. }
  234. } else {
  235. SET_PUDF(PUDF_GSMWPINUSE);
  236. psmwp = &gSMWP;
  237. RtlZeroMemory(&gSMWP, FIELD_OFFSET(SMWP, ccvrAlloc));
  238. UserAssert(gSMWP.ccvr == 0);
  239. UserAssert(gSMWP.acvr != NULL);
  240. }
  241. DBGCheckSMWP(psmwp);
  242. return psmwp;
  243. }
  244. /***************************************************************************\
  245. * BeginDeferWindowPos (API)
  246. *
  247. * This must be called from the client side only. Internally we should
  248. * call InternalBeginDeferWindowPos to avoid going through the handle table
  249. * and perhaps even use the cached strucuture.
  250. *
  251. * History:
  252. * 11-Jul-1991 DarrinM Ported from Win 3.1 sources.
  253. \***************************************************************************/
  254. PSMWP _BeginDeferWindowPos(
  255. int cwndHint)
  256. {
  257. PSMWP psmwp;
  258. psmwp = (PSMWP)HMAllocObject(PtiCurrent(), NULL, TYPE_SETWINDOWPOS, sizeof(SMWP));
  259. if (psmwp == NULL) {
  260. return NULL;
  261. }
  262. if (cwndHint == 0) {
  263. cwndHint = 8;
  264. }
  265. if (!AllocateCvr(psmwp, cwndHint)) {
  266. HMFreeObject(psmwp);
  267. return NULL;
  268. }
  269. psmwp->bHandle = TRUE;
  270. DBGCheckSMWP(psmwp);
  271. return psmwp;
  272. }
  273. /***************************************************************************\
  274. * PWInsertAfter
  275. *
  276. * History:
  277. * 04-Mar-1992 MikeKe From win31
  278. \***************************************************************************/
  279. PWND PWInsertAfter(
  280. HWND hwnd)
  281. {
  282. PWND pwnd;
  283. /*
  284. * HWND_GROUPTOTOP and HWND_TOPMOST are the same thing.
  285. */
  286. switch ((ULONG_PTR)hwnd) {
  287. case (ULONG_PTR)HWND_TOP:
  288. case (ULONG_PTR)HWND_BOTTOM:
  289. case (ULONG_PTR)HWND_TOPMOST:
  290. case (ULONG_PTR)HWND_NOTOPMOST:
  291. return (PWND)hwnd;
  292. default:
  293. /*
  294. * Don't insert after a destroyed window! It will cause the
  295. * window being z-ordered to become unlinked from it's siblings.
  296. */
  297. if (pwnd = RevalidateHwnd(hwnd)) {
  298. /*
  299. * Do not insert after a destroyed window. Put it at the
  300. * bottom of the list, if it is z-ordered at all.
  301. */
  302. if (TestWF(pwnd, WFDESTROYED) || pwnd->spwndParent == NULL)
  303. return NULL;
  304. UserAssert(_IsDescendant(pwnd->spwndParent, pwnd));
  305. return pwnd;
  306. }
  307. return NULL;
  308. }
  309. }
  310. HWND HWInsertAfter(
  311. PWND pwnd)
  312. {
  313. /*
  314. * HWND_GROUPTOTOP and HWND_TOPMOST are the same thing.
  315. */
  316. switch ((ULONG_PTR)pwnd) {
  317. case (ULONG_PTR)HWND_TOP:
  318. case (ULONG_PTR)HWND_BOTTOM:
  319. case (ULONG_PTR)HWND_TOPMOST:
  320. case (ULONG_PTR)HWND_NOTOPMOST:
  321. return (HWND)pwnd;
  322. default:
  323. return HW(pwnd);
  324. }
  325. }
  326. /***************************************************************************\
  327. * DeferWindowPos (API)
  328. *
  329. *
  330. * History:
  331. * 07-11-91 darrinm Ported from Win 3.1 sources.
  332. \***************************************************************************/
  333. PSMWP _DeferWindowPos(
  334. PSMWP psmwp,
  335. PWND pwnd,
  336. PWND pwndInsertAfter,
  337. int x,
  338. int y,
  339. int cx,
  340. int cy,
  341. UINT flags)
  342. {
  343. PWINDOWPOS ppos;
  344. PCVR pcvr;
  345. DBGCheckSMWP(psmwp);
  346. if (psmwp->ccvr + 1 > psmwp->ccvrAlloc) {
  347. /*
  348. * Make space for 4 more windows.
  349. */
  350. DWORD dwNewAlloc = psmwp->ccvrAlloc + 4;
  351. if (psmwp == &gSMWP) {
  352. UserAssert(psmwp->bHandle == FALSE);
  353. pcvr = (PCVR)UserReAllocPoolWithTag(psmwp->acvr,
  354. psmwp->ccvrAlloc * sizeof(CVR),
  355. sizeof(CVR) * dwNewAlloc,
  356. TAG_SWP);
  357. } else {
  358. pcvr = (PCVR)UserReAllocPoolWithQuota(psmwp->acvr,
  359. psmwp->ccvrAlloc * sizeof(CVR),
  360. sizeof(CVR) * dwNewAlloc,
  361. TAG_SWP);
  362. }
  363. if (pcvr == NULL) {
  364. DestroySMWP(psmwp);
  365. return NULL;
  366. }
  367. psmwp->acvr = pcvr;
  368. psmwp->ccvrAlloc = dwNewAlloc;
  369. }
  370. pcvr = &psmwp->acvr[psmwp->ccvr++];
  371. ppos = &pcvr->pos;
  372. ppos->hwnd = HWq(pwnd);
  373. ppos->hwndInsertAfter = (TestWF(pwnd, WFBOTTOMMOST)) ?
  374. HWND_BOTTOM : HWInsertAfter(pwndInsertAfter);
  375. ppos->x = x;
  376. ppos->y = y;
  377. ppos->cx = cx;
  378. ppos->cy = cy;
  379. ppos->flags = flags;
  380. pcvr->hrgnClip = NULL;
  381. pcvr->hrgnInterMonitor = NULL;
  382. return psmwp;
  383. }
  384. /***************************************************************************\
  385. * ValidateWindowPos
  386. *
  387. * checks validity of SWP structure
  388. *
  389. * NOTE: For performance reasons, this routine is only called
  390. * in the DEBUG version of USER.
  391. *
  392. * History:
  393. * 10-Jul-1991 DarrinM Ported from Win 3.1 sources.
  394. \***************************************************************************/
  395. BOOL ValidateWindowPos(
  396. PCVR pcvr,
  397. PWND pwndParent)
  398. {
  399. PWND pwnd;
  400. PWND pwndInsertAfter;
  401. HWND hwndInsertAfter;
  402. if ((pwnd = RevalidateHwnd(pcvr->pos.hwnd)) == NULL)
  403. return FALSE;
  404. /*
  405. * Save the pti.
  406. */
  407. pcvr->pti = GETPTI(pwnd);
  408. /*
  409. * If the SWP_NOZORDER bit is not set, validate the Insert behind window.
  410. */
  411. if (!(pcvr->pos.flags & SWP_NOZORDER)) {
  412. BOOL fTopLevel = (pwnd->spwndParent == PWNDDESKTOP(pwnd));
  413. /*
  414. * Do not z-order destroyed windows
  415. */
  416. if (TestWF(pwnd, WFDESTROYED))
  417. return FALSE;
  418. hwndInsertAfter = pcvr->pos.hwndInsertAfter;
  419. /*
  420. * If pwndParent is provided, we're about to link this window so we
  421. * need to validate LinkWindow assumptions. We have to do this since
  422. * we callback after determining hwndInsertAfter.
  423. */
  424. if ((hwndInsertAfter == HWND_TOPMOST) ||
  425. (hwndInsertAfter == HWND_NOTOPMOST)) {
  426. if (!fTopLevel) {
  427. return FALSE;
  428. }
  429. } else if (hwndInsertAfter == HWND_TOP) {
  430. /*
  431. * If pwnd is not topmost, the first child must not be topmost.
  432. */
  433. if ((pwndParent != NULL) && fTopLevel
  434. && !FSwpTopmost(pwnd)
  435. && (pwndParent->spwndChild != NULL)
  436. && FSwpTopmost(pwndParent->spwndChild)) {
  437. RIPMSG2(RIP_WARNING, "ValidateWindowPos: pwnd is not SWPTopMost."
  438. " pwnd:%#p. hwndInsertAfter:%#p",
  439. pwnd, hwndInsertAfter);
  440. return FALSE;
  441. }
  442. } else if (hwndInsertAfter != HWND_BOTTOM) {
  443. /*
  444. * Ensure pwndInsertAfter is valid
  445. */
  446. if (((pwndInsertAfter = RevalidateHwnd(hwndInsertAfter)) == NULL) ||
  447. TestWF(pwndInsertAfter, WFDESTROYED)) {
  448. RIPERR1(ERROR_INVALID_HANDLE, RIP_WARNING, "Invalid hwndInsertAfter (%#p)", hwndInsertAfter);
  449. return FALSE;
  450. }
  451. /*
  452. * Ensure that pwndInsertAfter is a sibling of pwnd
  453. */
  454. if (pwnd == pwndInsertAfter ||
  455. pwnd->spwndParent != pwndInsertAfter->spwndParent) {
  456. RIPMSG2(RIP_WARNING, "hwndInsertAfter (%#p) is not a sibling "
  457. "of hwnd (%#p)", hwndInsertAfter, pcvr->pos.hwnd);
  458. return FALSE;
  459. }
  460. /*
  461. * Ensure proper topmost/nontopmost insert position
  462. */
  463. if ((pwndParent != NULL) && fTopLevel) {
  464. if (FSwpTopmost(pwnd)) {
  465. /*
  466. * Check if we're trying to insert a topmost window after a non-topmost one.
  467. */
  468. if (!FSwpTopmost(pwndInsertAfter)) {
  469. RIPMSG2(RIP_WARNING, "ValidateWindowPos: pwndInsertAfter is not SWPTopMost."
  470. " pwnd:%#p. pwndInsertAfter:%#p",
  471. pwnd, pwndInsertAfter);
  472. return FALSE;
  473. }
  474. } else {
  475. /*
  476. * Check if we're trying to insert a non-top most window
  477. * between two top-most ones.
  478. */
  479. if ((pwndInsertAfter->spwndNext != NULL)
  480. && FSwpTopmost(pwndInsertAfter->spwndNext)) {
  481. RIPMSG2(RIP_WARNING, "ValidateWindowPos: pwndInsertAfter->spwndNext is SWPTopMost."
  482. " pwnd:%#p. pwndInsertAfter:%#p",
  483. pwnd, pwndInsertAfter);
  484. return FALSE;
  485. }
  486. }
  487. }
  488. }
  489. /*
  490. * Check that the parent hasn't changed.
  491. */
  492. if (pwndParent != NULL) {
  493. if (pwndParent != pwnd->spwndParent) {
  494. RIPMSG3(RIP_WARNING, "ValidateWindowPos: parent has changed."
  495. " pwnd:%#p. Old Parent:%#p. Current Parent:%#p",
  496. pwnd, pwndParent, pwnd->spwndParent);
  497. return FALSE;
  498. }
  499. }
  500. }
  501. return TRUE;
  502. }
  503. /***************************************************************************\
  504. * IsStillWindowC
  505. *
  506. * Checks if window is valid HWNDC still, and child of proper dude.
  507. *
  508. * History:
  509. \***************************************************************************/
  510. BOOL IsStillWindowC(
  511. HWND hwndc)
  512. {
  513. switch ((ULONG_PTR)hwndc) {
  514. case (ULONG_PTR)HWND_TOP:
  515. case (ULONG_PTR)HWND_BOTTOM:
  516. case (ULONG_PTR)HWND_TOPMOST:
  517. case (ULONG_PTR)HWND_NOTOPMOST:
  518. return TRUE;
  519. default:
  520. /*
  521. * Make sure we're going to insert after a window that's:
  522. * (1) Valid
  523. * (2) Peer
  524. */
  525. return (RevalidateHwnd(hwndc) != 0);
  526. }
  527. }
  528. /***************************************************************************\
  529. * ValidateSmwp
  530. *
  531. * Validate the SMWP and figure out which window should get activated,
  532. *
  533. * History:
  534. * 10-Jul-1991 DarrinM Ported from Win 3.1 sources.
  535. \***************************************************************************/
  536. BOOL ValidateSmwp(
  537. PSMWP psmwp,
  538. BOOL *pfSyncPaint)
  539. {
  540. PCVR pcvr;
  541. PWND pwndParent;
  542. PWND pwndT;
  543. int ccvr;
  544. *pfSyncPaint = TRUE;
  545. pwndT = RevalidateHwnd(psmwp->acvr[0].pos.hwnd);
  546. if (pwndT == NULL)
  547. return FALSE;
  548. pwndParent = pwndT->spwndParent;
  549. /*
  550. * Validate the passed-in WINDOWPOS structs, and find a window to activate.
  551. */
  552. for (pcvr = psmwp->acvr, ccvr = psmwp->ccvr; --ccvr >= 0; pcvr++) {
  553. if (!ValidateWindowPos(pcvr, NULL)) {
  554. pcvr->pos.hwnd = NULL;
  555. continue;
  556. }
  557. /*
  558. * All windows in the pos list must have the same parent.
  559. * If not, yell and return FALSE.
  560. */
  561. UserAssert(IsStillWindowC(pcvr->pos.hwnd));
  562. UserAssert(PW(pcvr->pos.hwnd));
  563. if (PW(pcvr->pos.hwnd)->spwndParent != pwndParent) {
  564. RIPERR0(ERROR_HWNDS_HAVE_DIFF_PARENT, RIP_VERBOSE, "");
  565. return FALSE;
  566. }
  567. /*
  568. * If SWP_DEFERDRAWING is set for any of the windows, suppress
  569. * DoSyncPaint() call later.
  570. */
  571. if (pcvr->pos.flags & SWP_DEFERDRAWING)
  572. *pfSyncPaint = FALSE;
  573. }
  574. return TRUE;
  575. }
  576. /***************************************************************************\
  577. * FindValidWindowPos
  578. *
  579. * Some of the windows in the SMWP list may be NULL at ths point (removed
  580. * because they'll be handled by their creator's thread) so we've got to
  581. * look for the first non-NULL window and return it.
  582. *
  583. * History:
  584. * 10-Sep-1991 DarrinM Created.
  585. \***************************************************************************/
  586. PWINDOWPOS FindValidWindowPos(
  587. PSMWP psmwp)
  588. {
  589. int i;
  590. for (i = 0; i < psmwp->ccvr; i++) {
  591. if (psmwp->acvr[i].pos.hwnd != NULL)
  592. return &psmwp->acvr[i].pos;
  593. }
  594. return NULL;
  595. }
  596. /***************************************************************************\
  597. * GetLastNonBottomMostWindow
  598. *
  599. * Returns the last non bottom-most window in the z-order, NULL if
  600. * there isn't one. When figuring out whom to insert after, we want to
  601. * skip ourself. But when figuring out if we're already in place, we don't
  602. * want to skip ourself on enum.
  603. *
  604. * History:
  605. \***************************************************************************/
  606. PWND GetLastNonBottomMostWindow(
  607. PWND pwnd,
  608. BOOL fSkipSelf)
  609. {
  610. PWND pwndT;
  611. PWND pwndLast = NULL;
  612. for (pwndT = pwnd->spwndParent->spwndChild;
  613. pwndT && !TestWF(pwndT, WFBOTTOMMOST);
  614. pwndT = pwndT->spwndNext) {
  615. if (!fSkipSelf || (pwnd != pwndT))
  616. pwndLast = pwndT;
  617. }
  618. return pwndLast;
  619. }
  620. /***************************************************************************\
  621. * ValidateZorder
  622. *
  623. * Checks to see if the specified window is already in the specified Z order
  624. * position, by comparing the current Z position with the specified
  625. * pwndInsertAfter.
  626. *
  627. * History:
  628. * 11-Jul-1991 DarrinM Ported from Win 3.1 sources.
  629. \***************************************************************************/
  630. BOOL ValidateZorder(
  631. PCVR pcvr)
  632. {
  633. PWND pwnd;
  634. PWND pwndPrev;
  635. PWND pwndInsertAfter;
  636. BYTE bTopmost;
  637. /*
  638. * Validate just to make sure this routine doesn't do anything bogus.
  639. * Its caller will actually redetect and handle the error.
  640. */
  641. UserAssert(RevalidateCatHwnd(pcvr->pos.hwnd));
  642. pwnd = PWCat(pcvr->pos.hwnd); // Known to be valid at this point.
  643. /*
  644. * Don't z-order a destroyed window.
  645. */
  646. if (TestWF(pwnd, WFDESTROYED)) {
  647. return TRUE;
  648. }
  649. UserAssert((HMPheFromObject(pwnd)->bFlags & HANDLEF_DESTROY) == 0);
  650. pwndInsertAfter = PWInsertAfter(pcvr->pos.hwndInsertAfter);
  651. if (pcvr->pos.hwndInsertAfter != NULL && pwndInsertAfter == NULL) {
  652. return TRUE;
  653. }
  654. if (pwndInsertAfter == PWND_BOTTOM) {
  655. if (TestWF(pwnd, WFBOTTOMMOST)) {
  656. return (pwnd->spwndNext == NULL);
  657. } else {
  658. return (pwnd == GetLastNonBottomMostWindow(pwnd, FALSE));
  659. }
  660. }
  661. pwndPrev = pwnd->spwndParent->spwndChild;
  662. if (pwndInsertAfter == PWND_TOP) {
  663. return pwndPrev == pwnd;
  664. }
  665. if (TestWF(pwndInsertAfter, WFDESTROYED)) {
  666. return TRUE;
  667. }
  668. /*
  669. * When we compare the state of the window, we must use
  670. * the EVENTUAL state of the window that is moving, but
  671. * the CURRENT state of the window it's inserted behind.
  672. *
  673. * Prevent nonbottommost windows from going behind the bottommost one.
  674. */
  675. if (TestWF(pwndInsertAfter, WFBOTTOMMOST)) {
  676. pcvr->pos.hwndInsertAfter = HWInsertAfter(GetLastNonBottomMostWindow(pwnd, TRUE));
  677. return FALSE;
  678. }
  679. /*
  680. * If we are not topmost, but pwndInsertAfter is, OR
  681. * if we are topmost, but pwndInsertAfter is not,
  682. * we need to adjust pwndInsertAfter to be the last of
  683. * the topmost windows.
  684. */
  685. bTopmost = TestWF(pwnd, WEFTOPMOST);
  686. if (TestWF(pwnd, WFTOGGLETOPMOST))
  687. bTopmost ^= LOBYTE(WEFTOPMOST);
  688. if (bTopmost != (BYTE)TestWF(pwndInsertAfter, WEFTOPMOST)) {
  689. pwndInsertAfter = GetLastTopMostWindow();
  690. /*
  691. * We're correctly positioned if we're already at the bottom
  692. */
  693. if (pwndInsertAfter == pwnd) {
  694. return TRUE;
  695. }
  696. pcvr->pos.hwndInsertAfter = HW(pwndInsertAfter);
  697. }
  698. /*
  699. * Look for our previous window in the list ...
  700. */
  701. if (pwndPrev != pwnd) {
  702. for (; pwndPrev != NULL; pwndPrev = pwndPrev->spwndNext) {
  703. if (pwndPrev->spwndNext == pwnd) {
  704. return pwndInsertAfter == pwndPrev;
  705. }
  706. }
  707. /*
  708. * NTRAID#NTBUG9-345299-2001/04/09-jasonsch
  709. *
  710. * If we get to here, pwnd is not in the sibling list.
  711. * REALLY BAD NEWS!
  712. *
  713. * Changing this to a warning since we seem to handle it fine
  714. * and there's a shell dude hitting this. Need to revisit
  715. * this in Blackcomb.
  716. */
  717. RIPMSG1(RIP_WARNING, "Pwnd 0x%p not found in sibling list.", pwnd);
  718. return TRUE;
  719. }
  720. return FALSE;
  721. }
  722. /***************************************************************************\
  723. * xxxCalcValidRects
  724. *
  725. * Based on the WINDOWPOS flags in the fs parameter in each WINDOWPOS structure,
  726. * this routine calcs the new position and size of each window, determines if
  727. * its changing Z order, or whether its showing or hiding. Any redundant
  728. * flags are AND'ed out of the fs parameter. If no redrawing is needed,
  729. * SWP_NOREDRAW is OR'ed into the flags. This is called from EndDeferWindowPos.
  730. *
  731. * History:
  732. * 10-Jul-1991 DarrinM Ported from Win 3.1 sources.
  733. \***************************************************************************/
  734. BOOL xxxCalcValidRects(
  735. PSMWP psmwp,
  736. HWND *phwndNewActive)
  737. {
  738. PCVR pcvr;
  739. PWND pwnd;
  740. PWND pwndParent;
  741. HWND hwnd;
  742. HWND hwndNewActive = NULL;
  743. PWINDOWPOS ppos;
  744. BOOL fNoZorder;
  745. BOOL fForceNCCalcSize;
  746. NCCALCSIZE_PARAMS params;
  747. int cxSrc;
  748. int cySrc;
  749. int cxDst;
  750. int cyDst;
  751. int cmd;
  752. int ccvr;
  753. int xClientOld;
  754. int yClientOld;
  755. int cxClientOld;
  756. int cyClientOld;
  757. int xWindowOld;
  758. int xWindowOldLogical;
  759. int yWindowOld;
  760. int cxWindowOld;
  761. int cyWindowOld;
  762. TL tlpwndParent;
  763. TL tlpwnd;
  764. BOOL fSetZeroDx=FALSE;
  765. BOOL fMirroredParent = FALSE;
  766. /*
  767. * Some of the windows in the SMWP list may be NULL at ths point
  768. * (removed because they'll be handled by their creator's thread)
  769. * so we've got to look for the first non-NULL window before we can
  770. * execute some of the tests below. FindValidWindowPos returns NULL if
  771. * the list has no valid windows in it.
  772. */
  773. if ((ppos = FindValidWindowPos(psmwp)) == NULL)
  774. return FALSE;
  775. UserAssert(PW(ppos->hwnd));
  776. pwndParent = PW(ppos->hwnd)->spwndParent;
  777. UserAssert(HMRevalidateCatHandle(PtoH(pwndParent)));
  778. ThreadLock(pwndParent, &tlpwndParent);
  779. fNoZorder = TRUE;
  780. /*
  781. * Go through the SMWP list, enumerating each WINDOWPOS, and compute
  782. * its new window and client rectangles.
  783. */
  784. for (pcvr = psmwp->acvr, ccvr = psmwp->ccvr; --ccvr >= 0; pcvr++) {
  785. /*
  786. * This loop may leave the critsect during each iteration so
  787. * we revalidate pos.hwnd before use.
  788. */
  789. if ((hwnd = pcvr->pos.hwnd) == NULL)
  790. continue;
  791. pwnd = RevalidateHwnd(hwnd);
  792. if ((pwnd == NULL) || !IsStillWindowC(pcvr->pos.hwndInsertAfter)) {
  793. pcvr->pos.hwnd = NULL;
  794. pcvr->pos.flags = SWP_NOREDRAW | SWP_NOCHANGE;
  795. continue;
  796. }
  797. ThreadLockAlways(pwnd, &tlpwnd);
  798. /*
  799. * Used for 3.0 compatibility. 3.0 sent the NCCALCSIZE message even if
  800. * the size of the window wasn't changing.
  801. */
  802. fForceNCCalcSize = FALSE;
  803. if (!hwndNewActive && !(pcvr->pos.flags & SWP_NOACTIVATE))
  804. hwndNewActive = HWq(pwnd);
  805. if (!(pcvr->pos.flags & SWP_NOSENDCHANGING)) {
  806. PWND pwndT;
  807. xxxSendMessage(pwnd, WM_WINDOWPOSCHANGING, 0, (LPARAM)&pcvr->pos);
  808. /*
  809. * Don't let them change pcvr->pos.hwnd. It doesn't make sense
  810. * plus it'll mess us up. I'm making this RIP_ERROR because we're
  811. * too close to RTM (7/11/96) just to make sure that we won't
  812. * break anyone. This should be changed to a RIP_WARNING after we
  813. * ship. Use LOWORD to ignore "changes" by NTVDM.
  814. */
  815. #if DBG
  816. if (LOWORD(pcvr->pos.hwnd) != LOWORD(hwnd)) {
  817. RIPMSG0(RIP_ERROR,
  818. "xxxCalcValidRects: Ignoring pcvr->pos.hwnd change by WM_WINDOWPOSCHANGING");
  819. }
  820. #endif
  821. pcvr->pos.hwnd = hwnd;
  822. /*
  823. * If the window sets again 'hwndInsertAfter' to HWND_NOTOPMOST
  824. * or HWND_TOPMOST, we need to set this member appropriately.
  825. * See CheckTopmost for details.
  826. */
  827. if (pcvr->pos.hwndInsertAfter == HWND_NOTOPMOST) {
  828. if (TestWF(pwnd, WEFTOPMOST)) {
  829. pwndT = GetLastTopMostWindow();
  830. pcvr->pos.hwndInsertAfter = HW(pwndT);
  831. if (pcvr->pos.hwndInsertAfter == pcvr->pos.hwnd) {
  832. pwndT = _GetWindow(pwnd, GW_HWNDPREV);
  833. pcvr->pos.hwndInsertAfter = HW(pwndT);
  834. }
  835. } else {
  836. pwndT = _GetWindow(pwnd, GW_HWNDPREV);
  837. pcvr->pos.hwndInsertAfter = HW(pwndT);
  838. }
  839. } else if (pcvr->pos.hwndInsertAfter == HWND_TOPMOST) {
  840. pcvr->pos.hwndInsertAfter = HWND_TOP;
  841. }
  842. }
  843. /*
  844. * make sure the rectangle still matches the window's region
  845. *
  846. * Remember the old window rectangle in parent coordinates
  847. */
  848. xWindowOld = pwnd->rcWindow.left;
  849. yWindowOld = pwnd->rcWindow.top;
  850. xWindowOldLogical = xWindowOld;
  851. if (pwndParent != PWNDDESKTOP(pwnd)) {
  852. xWindowOld -= pwndParent->rcClient.left;
  853. yWindowOld -= pwndParent->rcClient.top;
  854. fMirroredParent = (TestWF(pwndParent, WEFLAYOUTRTL) && TestwndChild(pwnd));
  855. if (fMirroredParent) {
  856. xWindowOldLogical = pwndParent->rcClient.right - pwnd->rcWindow.right;
  857. } else {
  858. xWindowOldLogical = xWindowOld;
  859. }
  860. }
  861. cxWindowOld = pwnd->rcWindow.right - pwnd->rcWindow.left;
  862. cyWindowOld = pwnd->rcWindow.bottom - pwnd->rcWindow.top;
  863. /*
  864. * Assume the client is not moving or sizing
  865. */
  866. pcvr->pos.flags |= SWP_NOCLIENTSIZE | SWP_NOCLIENTMOVE;
  867. if (!(pcvr->pos.flags & SWP_NOMOVE)) {
  868. if (pcvr->pos.x == xWindowOldLogical && pcvr->pos.y == yWindowOld) {
  869. pcvr->pos.flags |= SWP_NOMOVE;
  870. if (fMirroredParent) {
  871. fSetZeroDx = TRUE;
  872. }
  873. }
  874. if (TestWF(pwnd, WFMINIMIZED) && IsTrayWindow(pwnd)) {
  875. pcvr->pos.x = WHERE_NOONE_CAN_SEE_ME;
  876. pcvr->pos.y = WHERE_NOONE_CAN_SEE_ME;
  877. }
  878. } else {
  879. pcvr->pos.x = xWindowOldLogical;
  880. pcvr->pos.y = yWindowOld;
  881. }
  882. if (!(pcvr->pos.flags & SWP_NOSIZE)) {
  883. /*
  884. * Don't allow an invalid window rectangle.
  885. * BOGUS HACK: For Norton Antivirus, they call
  886. * MoveWindow at WM_CREATE Time EVEN though
  887. * the window is minimzed, but they assume its
  888. * restored at WM_CREATE time.... B#11185, t-arthb
  889. */
  890. if (TestWF(pwnd, WFMINIMIZED) &&
  891. _GetProp(pwnd, PROP_CHECKPOINT, PROPF_INTERNAL)) {
  892. pcvr->pos.cx = SYSMET(CXMINIMIZED);
  893. pcvr->pos.cy = SYSMET(CYMINIMIZED);
  894. } else {
  895. if (pcvr->pos.cx < 0)
  896. pcvr->pos.cx = 0;
  897. if (pcvr->pos.cy < 0)
  898. pcvr->pos.cy = 0;
  899. }
  900. if (pcvr->pos.cx == cxWindowOld && pcvr->pos.cy == cyWindowOld) {
  901. pcvr->pos.flags |= SWP_NOSIZE;
  902. if (!TestWF(pwnd, WFWIN31COMPAT))
  903. fForceNCCalcSize = TRUE;
  904. }
  905. } else {
  906. pcvr->pos.cx = cxWindowOld;
  907. pcvr->pos.cy = cyWindowOld;
  908. }
  909. if (fMirroredParent) {
  910. UserAssert(pwndParent != PWNDDESKTOP(pwnd));
  911. pcvr->pos.x = (pwndParent->rcClient.right - pwndParent->rcClient.left) - pcvr->pos.x - pcvr->pos.cx;
  912. }
  913. /*
  914. * If showing and already visible, or hiding and already hidden,
  915. * turn off the appropriate bit.
  916. */
  917. if (TestWF(pwnd, WFVISIBLE)) {
  918. pcvr->pos.flags &= ~SWP_SHOWWINDOW;
  919. } else {
  920. pcvr->pos.flags &= ~SWP_HIDEWINDOW;
  921. /*
  922. * If hidden, and we're NOT showing, then we won't be drawing,
  923. * no matter what else is going on.
  924. */
  925. if (!(pcvr->pos.flags & SWP_SHOWWINDOW))
  926. pcvr->pos.flags |= SWP_NOREDRAW;
  927. }
  928. /*
  929. * Child windows inside a composited window can't use screen to
  930. * screen bit copy because this can move translucent bits.
  931. */
  932. if (!TestWF(pwnd, WEFCOMPOSITED) &&
  933. GetStyleWindow(pwnd, WEFCOMPOSITED) != NULL) {
  934. pcvr->pos.flags |= SWP_NOCOPYBITS;
  935. }
  936. /*
  937. * Muck with the zorder for bottommost windows, again
  938. * See comment in DeferWindowPos
  939. */
  940. if (TestWF(pwnd, WFBOTTOMMOST)) {
  941. pcvr->pos.flags &= ~SWP_NOZORDER;
  942. pcvr->pos.hwndInsertAfter = HWND_BOTTOM;
  943. }
  944. /*
  945. * If we're Z-ordering, we can try to remove the Z order
  946. * bit, as long as all previous windows in the WINDOWPOS list
  947. * have SWP_NOZORDER set.
  948. *
  949. * The reason we don't do this for each window individually
  950. * is that a window's eventual Z order depends on changes that
  951. * may have occured on windows earlier in the WINDOWPOS list,
  952. * so we can only call ValidateZorder if none of the previous
  953. * windows have changed.
  954. */
  955. if (fNoZorder && !(pcvr->pos.flags & SWP_NOZORDER)) {
  956. /*
  957. * If the TOPMOST bit is changing, the Z order is "changing",
  958. * so don't clear the bit even if it's in the right place in the
  959. * list.
  960. */
  961. fNoZorder = FALSE;
  962. if (!TestWF(pwnd, WFTOGGLETOPMOST) && ValidateZorder(pcvr)) {
  963. fNoZorder = TRUE;
  964. pcvr->pos.flags |= SWP_NOZORDER;
  965. }
  966. }
  967. /*
  968. * If no change is occuring, or if a parent is invisible,
  969. * we won't be redrawing.
  970. */
  971. if (!(pcvr->pos.flags & SWP_NOREDRAW)) {
  972. if ((pcvr->pos.flags & SWP_CHANGEMASK) == SWP_NOCHANGE ||
  973. !_FChildVisible(pwnd)) {
  974. pcvr->pos.flags |= SWP_NOREDRAW;
  975. }
  976. }
  977. /*
  978. * BACKWARD COMPATIBILITY HACK
  979. *
  980. * In 3.0, if a window was moving but not sizing, we'd send the
  981. * WM_NCCALCSIZE message anyhow. Lotus Notes 2.1 depends on this
  982. * in order to move its "navigation bar" when the main window moves.
  983. */
  984. if (!(pcvr->pos.flags & SWP_NOMOVE) &&
  985. !TestWF(pwnd, WFWIN31COMPAT) &&
  986. (GetAppCompatFlags(NULL) & GACF_NCCALCSIZEONMOVE)) {
  987. fForceNCCalcSize = TRUE;
  988. }
  989. /*
  990. * If the window rect is sizing, or if the frame has changed,
  991. * send the WM_NCCALCSIZE message and deal with valid areas.
  992. */
  993. if (((pcvr->pos.flags & (SWP_NOSIZE | SWP_FRAMECHANGED)) != SWP_NOSIZE) ||
  994. fForceNCCalcSize) {
  995. WINDOWPOS pos;
  996. /*
  997. * check for full screen main app window
  998. */
  999. if (!TestWF(pwnd, WFCHILD) && !TestWF(pwnd, WEFTOOLWINDOW)) {
  1000. xxxCheckFullScreen(pwnd, (PSIZERECT)&pcvr->pos.x);
  1001. }
  1002. /*
  1003. * Set up NCCALCSIZE message parameters (in parent coords)
  1004. * wParam = fClientOnly = TRUE
  1005. * lParam = &params
  1006. */
  1007. pos = pcvr->pos; // Make a local stack copy
  1008. params.lppos = &pos;
  1009. /*
  1010. * params.rgrc[0] = rcWindowNew = New window rectangle
  1011. * params.rgrc[1] = rcWindowOld = Old window rectangle
  1012. * params.rgrc[2] = rcClientOld = Old client rectangle
  1013. */
  1014. #define rcWindowNew params.rgrc[0]
  1015. #define rcWindowOld params.rgrc[1]
  1016. #define rcClientOld params.rgrc[2]
  1017. /*
  1018. * Set up rcWindowNew in parent relative coordinates
  1019. */
  1020. rcWindowNew.left = pcvr->pos.x;
  1021. rcWindowNew.right = rcWindowNew.left + pcvr->pos.cx;
  1022. rcWindowNew.top = pcvr->pos.y;
  1023. rcWindowNew.bottom = rcWindowNew.top + pcvr->pos.cy;
  1024. /*
  1025. * Set up rcWindowOld in parent relative coordinates
  1026. */
  1027. GetRect(pwnd, &rcWindowOld, GRECT_WINDOW | GRECT_PARENTCOORDS);
  1028. /*
  1029. * Set up rcClientOld in parent relative coordinates
  1030. */
  1031. GetRect(pwnd, &rcClientOld, GRECT_CLIENT | GRECT_PARENTCOORDS);
  1032. /*
  1033. * Keep around a copy of the old client position
  1034. */
  1035. xClientOld = rcClientOld.left;
  1036. cxClientOld = rcClientOld.right - rcClientOld.left;
  1037. yClientOld = rcClientOld.top;
  1038. cyClientOld = rcClientOld.bottom - rcClientOld.top;
  1039. cmd = (UINT)xxxSendMessage(pwnd, WM_NCCALCSIZE, TRUE, (LPARAM)&params);
  1040. if (!IsStillWindowC(pcvr->pos.hwndInsertAfter)) {
  1041. ThreadUnlock(&tlpwnd);
  1042. ThreadUnlock(&tlpwndParent);
  1043. return FALSE;
  1044. }
  1045. /*
  1046. * Upon return from NCCALCSIZE:
  1047. *
  1048. * params.rgrc[0] = rcClientNew = New client rect
  1049. * params.rgrc[1] = rcValidDst = Destination valid rectangle
  1050. * params.rgrc[2] = rcValidSrc = Source valid rectangle
  1051. */
  1052. #undef rcWindowNew
  1053. #undef rcWindowOld
  1054. #undef rcClientOld
  1055. #define rcClientNew params.rgrc[0]
  1056. #define rcValidDst params.rgrc[1]
  1057. #define rcValidSrc params.rgrc[2]
  1058. /*
  1059. * Calculate the distance the window contents are
  1060. * moving. If 0 or an invalid value was returned
  1061. * from the WM_NCCALCSIZE message, assume the
  1062. * entire client area is valid and top-left aligned.
  1063. */
  1064. if (cmd < WVR_MINVALID || cmd > WVR_MAXVALID) {
  1065. /*
  1066. * We don't need to copy rcValidSrc to rcClientOld,
  1067. * because it's already stored in rgrc[2].
  1068. *
  1069. * rcValidSrc = rcClientOld
  1070. */
  1071. rcValidDst = rcClientNew;
  1072. cmd = WVR_ALIGNTOP | WVR_ALIGNLEFT;
  1073. }
  1074. /*
  1075. * Calculate the distance we'll be shifting bits...
  1076. */
  1077. if (TestWF(pwnd, WEFLAYOUTRTL)) {
  1078. pcvr->dxBlt = rcValidDst.right - rcValidSrc.right;
  1079. } else {
  1080. pcvr->dxBlt = rcValidDst.left - rcValidSrc.left;
  1081. }
  1082. pcvr->dyBlt = rcValidDst.top - rcValidSrc.top;
  1083. /*
  1084. * Calculate new client rect size and position
  1085. */
  1086. pcvr->xClientNew = rcClientNew.left;
  1087. pcvr->yClientNew = rcClientNew.top;
  1088. pcvr->cxClientNew = rcClientNew.right - rcClientNew.left;
  1089. pcvr->cyClientNew = rcClientNew.bottom - rcClientNew.top;
  1090. /*
  1091. * Figure out whether the client rectangle is moving or sizing,
  1092. * and diddle the appropriate bit if not.
  1093. */
  1094. if (xClientOld != rcClientNew.left || yClientOld != rcClientNew.top)
  1095. pcvr->pos.flags &= ~SWP_NOCLIENTMOVE;
  1096. if (cxClientOld != pcvr->cxClientNew || cyClientOld != pcvr->cyClientNew) {
  1097. pcvr->pos.flags &= ~SWP_NOCLIENTSIZE;
  1098. }
  1099. /*
  1100. * If the caller doesn't want us to save any bits, then don't.
  1101. */
  1102. if (pcvr->pos.flags & SWP_NOCOPYBITS) {
  1103. AllInvalid:
  1104. /*
  1105. * The entire window is invalid: Set the blt rectangle
  1106. * to empty, to ensure nothing gets bltted.
  1107. */
  1108. SetRectEmpty(&pcvr->rcBlt);
  1109. ThreadUnlock(&tlpwnd);
  1110. continue;
  1111. }
  1112. /*
  1113. * If we are just resizing this window without moving it and its parent
  1114. * is mirrored then no need to copy any bits (i.e. empty pcvr->rcBlt).
  1115. */
  1116. if (fSetZeroDx) {
  1117. goto AllInvalid;
  1118. }
  1119. /*
  1120. * If this is a transparent window, be sure to invalidate
  1121. * everything, because only some of the window's bits are
  1122. * blittable.
  1123. */
  1124. if (TestWF(pwnd, WEFTRANSPARENT))
  1125. goto AllInvalid;
  1126. /*
  1127. * If both client and window did not change size, the frame didn't
  1128. * change, and the blt rectangle moved the same distance as the
  1129. * rectangle, then the entire window area is valid.
  1130. */
  1131. if (((pcvr->pos.flags &
  1132. (SWP_NOSIZE | SWP_NOCLIENTSIZE | SWP_FRAMECHANGED))
  1133. == (SWP_NOSIZE | SWP_NOCLIENTSIZE)) &&
  1134. pcvr->dxBlt == (pcvr->pos.x - xWindowOld) &&
  1135. pcvr->dyBlt == (pcvr->pos.y - yWindowOld)) {
  1136. goto AllValid;
  1137. }
  1138. /*
  1139. * Now compute the valid blt rectangle.
  1140. *
  1141. * Check for horz or vert client size changes
  1142. *
  1143. * NOTE: Assumes WVR_REDRAW == WVR_HREDRAW | WVR_VREDRAW
  1144. */
  1145. if (cxClientOld != pcvr->cxClientNew) {
  1146. if ((cmd & WVR_HREDRAW) || TestCF(pwnd, CFHREDRAW))
  1147. goto AllInvalid;
  1148. }
  1149. if (cyClientOld != pcvr->cyClientNew) {
  1150. if ((cmd & WVR_VREDRAW) || TestCF(pwnd, CFVREDRAW))
  1151. goto AllInvalid;
  1152. }
  1153. cxSrc = rcValidSrc.right - rcValidSrc.left;
  1154. cySrc = rcValidSrc.bottom - rcValidSrc.top;
  1155. cxDst = rcValidDst.right - rcValidDst.left;
  1156. cyDst = rcValidDst.bottom - rcValidDst.top;
  1157. if ((!!(cmd & WVR_ALIGNRIGHT)) ^ (!!TestWF(pwnd, WEFLAYOUTRTL)))
  1158. rcValidDst.left += ((TestWF(pwnd, WEFLAYOUTRTL) && (cxSrc > cxDst)) ? (cxSrc-cxDst) : (cxDst - cxSrc));
  1159. if (cmd & WVR_ALIGNBOTTOM)
  1160. rcValidDst.top += (cyDst - cySrc);
  1161. /*
  1162. * Superimpose the source on the destination, and intersect
  1163. * the rectangles. This is done by looking at the
  1164. * extent of the rectangles, and pinning as appropriate.
  1165. */
  1166. if (cxSrc < cxDst)
  1167. rcValidDst.right = rcValidDst.left + cxSrc;
  1168. if (cySrc < cyDst)
  1169. rcValidDst.bottom = rcValidDst.top + cySrc;
  1170. /*
  1171. * Finally map the blt rectangle to screen coordinates.
  1172. */
  1173. pcvr->rcBlt = rcValidDst;
  1174. if (pwndParent != PWNDDESKTOP(pwnd)) {
  1175. OffsetRect(
  1176. &pcvr->rcBlt,
  1177. pwndParent->rcClient.left,
  1178. pwndParent->rcClient.top);
  1179. }
  1180. } else { // if !SWP_NOSIZE or SWP_FRAMECHANGED
  1181. AllValid:
  1182. /*
  1183. * No client size change: Blt the entire window,
  1184. * including the frame. Offset everything by
  1185. * the distance the window rect changed.
  1186. */
  1187. if (pcvr->pos.flags & SWP_NOCOPYBITS) {
  1188. SetRectEmpty(&pcvr->rcBlt);
  1189. } else {
  1190. pcvr->rcBlt.left = pcvr->pos.x;
  1191. pcvr->rcBlt.top = pcvr->pos.y;
  1192. if (pwndParent != PWNDDESKTOP(pwnd)) {
  1193. pcvr->rcBlt.left += pwndParent->rcClient.left;
  1194. pcvr->rcBlt.top += pwndParent->rcClient.top;
  1195. }
  1196. pcvr->rcBlt.right = pcvr->rcBlt.left + pcvr->pos.cx;
  1197. pcvr->rcBlt.bottom = pcvr->rcBlt.top + pcvr->pos.cy;
  1198. }
  1199. /*
  1200. * Offset everything by the distance the window moved.
  1201. */
  1202. if (TestWF(pwnd, WEFLAYOUTRTL)) {
  1203. pcvr->dxBlt = (pcvr->pos.x + pcvr->pos.cx) - (xWindowOld + cxWindowOld);
  1204. } else {
  1205. pcvr->dxBlt = pcvr->pos.x - xWindowOld;
  1206. }
  1207. pcvr->dyBlt = pcvr->pos.y - yWindowOld;
  1208. /*
  1209. * If we're moving, we need to set up the client.
  1210. */
  1211. if (!(pcvr->pos.flags & SWP_NOMOVE)) {
  1212. pcvr->pos.flags &= ~SWP_NOCLIENTMOVE;
  1213. pcvr->xClientNew = pwnd->rcClient.left + pcvr->dxBlt;
  1214. pcvr->yClientNew = pwnd->rcClient.top + pcvr->dyBlt;
  1215. if (pwndParent != PWNDDESKTOP(pwnd)) {
  1216. pcvr->xClientNew -= pwndParent->rcClient.left;
  1217. pcvr->yClientNew -= pwndParent->rcClient.top;
  1218. }
  1219. pcvr->cxClientNew = pwnd->rcClient.right - pwnd->rcClient.left;
  1220. pcvr->cyClientNew = pwnd->rcClient.bottom - pwnd->rcClient.top;
  1221. }
  1222. }
  1223. ThreadUnlock(&tlpwnd);
  1224. } // for (... pcvr ...)
  1225. ThreadUnlock(&tlpwndParent);
  1226. *phwndNewActive = hwndNewActive;
  1227. return TRUE;
  1228. }
  1229. /***************************************************************************\
  1230. * GetLastTopMostWindow
  1231. *
  1232. * Returns the last topmost window in the window list. Returns NULL if no
  1233. * topmost windows. Used so that we can fill in the pwndInsertAfter field
  1234. * in various SWP calls.
  1235. *
  1236. * History:
  1237. * 11-Jul-1991 DarrinM Ported from Win 3.1 sources.
  1238. \***************************************************************************/
  1239. PWND GetLastTopMostWindow(VOID)
  1240. {
  1241. PWND pwndT;
  1242. PDESKTOP pdesk = PtiCurrent()->rpdesk;
  1243. if (pdesk == NULL)
  1244. return NULL;
  1245. pwndT = pdesk->pDeskInfo->spwnd->spwndChild;
  1246. if (!pwndT || !TestWF(pwndT, WEFTOPMOST))
  1247. return NULL;
  1248. while (pwndT->spwndNext) {
  1249. if (!TestWF(pwndT->spwndNext, WEFTOPMOST))
  1250. break;
  1251. pwndT = pwndT->spwndNext;
  1252. }
  1253. return pwndT;
  1254. }
  1255. /***************************************************************************\
  1256. * SetWindowPos (API)
  1257. *
  1258. *
  1259. * History:
  1260. * 11-Jul-1991 DarrinM Ported from Win 3.1 sources.
  1261. \***************************************************************************/
  1262. BOOL xxxSetWindowPos(
  1263. PWND pwnd,
  1264. PWND pwndInsertAfter,
  1265. int x,
  1266. int y,
  1267. int cx,
  1268. int cy,
  1269. UINT flags)
  1270. {
  1271. PSMWP psmwp;
  1272. BOOL fInval = FALSE;
  1273. #if DBG
  1274. CheckLock(pwnd);
  1275. switch((ULONG_PTR)pwndInsertAfter) {
  1276. case 0x0000FFFF:
  1277. case (ULONG_PTR)HWND_TOPMOST:
  1278. case (ULONG_PTR)HWND_NOTOPMOST:
  1279. case (ULONG_PTR)HWND_TOP:
  1280. case (ULONG_PTR)HWND_BOTTOM:
  1281. break;
  1282. default:
  1283. CheckLock(pwndInsertAfter);
  1284. break;
  1285. }
  1286. #endif
  1287. /*
  1288. * BACKWARD COMPATIBILITY HACKS
  1289. *
  1290. * Hack 1: For Win 3.0 and below, SetWindowPos() must ignore the
  1291. * move and size flags if SWP_SHOWWINDOW or SWP_HIDEWINDOW
  1292. * is specified. KnowledgePro is one application that depends on
  1293. * this behavior for the positioning of its MDI icons.
  1294. *
  1295. * Hack 2: In 3.0, if SetWindowPos() is called with SWP_SHOWWINDOW
  1296. * and the window is already visible, then the window was
  1297. * completely invalidated anyway. So, we do that here too.
  1298. *
  1299. * NOTE: The placement of the invalidation AFTER the EndDeferWindowPos()
  1300. * call means that if the guy is Z-ordering and showing a 3.0 window,
  1301. * it may flash, because EndDefer calls DoSyncPaint, and we invalidate
  1302. * again after that. Could be fixed with some major hackery in EndDefer,
  1303. * and it's probably not worth the trouble.
  1304. */
  1305. if (flags & (SWP_SHOWWINDOW | SWP_HIDEWINDOW)) {
  1306. if (!TestWF(pwnd, WFWIN31COMPAT)) {
  1307. flags |= SWP_NOMOVE | SWP_NOSIZE;
  1308. if ((flags & SWP_SHOWWINDOW) && TestWF(pwnd, WFVISIBLE))
  1309. fInval = TRUE;
  1310. }
  1311. if (flags & SWP_SHOWWINDOW) {
  1312. SetWF(pwnd, WEFGHOSTMAKEVISIBLE);
  1313. } else {
  1314. ClrWF(pwnd, WEFGHOSTMAKEVISIBLE);
  1315. }
  1316. }
  1317. /*
  1318. * MULTIMONITOR HACKS
  1319. *
  1320. * if a app is centering or cliping a hidden owned window
  1321. * to the primary monitor we should center the window to the owner
  1322. *
  1323. * this makes apps that center/position their own dialogs
  1324. * work when the app is on a secondary monitor.
  1325. */
  1326. if ( !TestWF(pwnd, WFWIN50COMPAT) &&
  1327. gpDispInfo->cMonitors > 1 &&
  1328. !(flags & SWP_NOMOVE) &&
  1329. !TestWF(pwnd, WFCHILD) &&
  1330. !TestWF(pwnd, WFVISIBLE) &&
  1331. (TestWF(pwnd, WFBORDERMASK) == LOBYTE(WFCAPTION)) &&
  1332. pwnd->spwndOwner &&
  1333. TestWF(pwnd->spwndOwner, WFVISIBLE) &&
  1334. !IsRectEmpty(&pwnd->spwndOwner->rcWindow)) {
  1335. FixBogusSWP(pwnd, &x, &y, cx, cy, flags);
  1336. }
  1337. if (!(psmwp = InternalBeginDeferWindowPos(1)) ||
  1338. !(psmwp = _DeferWindowPos(psmwp,
  1339. pwnd,
  1340. pwndInsertAfter,
  1341. x,
  1342. y,
  1343. cx,
  1344. cy,
  1345. flags))) {
  1346. return FALSE;
  1347. }
  1348. if (xxxEndDeferWindowPosEx(psmwp, flags & SWP_ASYNCWINDOWPOS)) {
  1349. if (fInval) {
  1350. xxxRedrawWindow(
  1351. pwnd,
  1352. NULL,
  1353. NULL,
  1354. RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_ALLCHILDREN);
  1355. }
  1356. return TRUE;
  1357. }
  1358. return FALSE;
  1359. }
  1360. /***************************************************************************\
  1361. * xxxSwpActivate
  1362. *
  1363. *
  1364. * History:
  1365. * 11-Jul-1991 DarrinM Ported from Win 3.1 sources.
  1366. \***************************************************************************/
  1367. BOOL xxxSwpActivate(
  1368. PWND pwndNewActive)
  1369. {
  1370. PTHREADINFO pti;
  1371. CheckLock(pwndNewActive);
  1372. if (pwndNewActive == NULL)
  1373. return FALSE;
  1374. pti = PtiCurrent();
  1375. if (TestwndChild(pwndNewActive)) {
  1376. xxxSendMessage(pwndNewActive, WM_CHILDACTIVATE, 0, 0L);
  1377. } else if (pti->pq->spwndActive != pwndNewActive) {
  1378. /*
  1379. * Remember if this window wants to be active. We are either setting
  1380. * our own window active (most likely), or setting a window of
  1381. * another thread active on purpose. If so that means this thread is
  1382. * controlling this window and will probably want to set itself
  1383. * active and foreground really soon (for example, a setup
  1384. * program doing dde to progman). Allow this thread and the target
  1385. * thread to do forground activates.
  1386. *
  1387. * Let's stop doing this for NT5 in an effort to close the number
  1388. * of ways applications can force a foreground change. This is not
  1389. * quite needed anyway, because:
  1390. * -If the current thread is already in the foreground, then it doesn't need
  1391. * the TIF_ALLOWFOREGROUNDACTIVATE to make a foreground change.
  1392. * -Since FRemoveForegroundActive removes this bit, the current thread
  1393. * will lose it anyway during the xxxActivateWindow call.
  1394. * -But xxxActivateWindow will set it back anyway because we're activating
  1395. * a window from a different queue.
  1396. * -The destination window/thread will take the foreground
  1397. * as a result of the xxxActivateWindow call, hence it doesn't
  1398. * need the bit on (if you're in the foreground, you don't need it).
  1399. */
  1400. #ifdef DONTDOTHISANYMORE
  1401. if ((pti->pq == gpqForeground) && (pti != GETPTI(pwndNewActive))) {
  1402. /*
  1403. * Allow foreground activate on the source and dest.
  1404. */
  1405. pti->TIF_flags |= TIF_ALLOWFOREGROUNDACTIVATE;
  1406. TAGMSG1(DBGTAG_FOREGROUND, "xxxSwpActivate set TIF %#p", pti);
  1407. GETPTI(pwndNewActive)->TIF_flags |= TIF_ALLOWFOREGROUNDACTIVATE;
  1408. TAGMSG1(DBGTAG_FOREGROUND, "xxxSwpActivate set TIF %#p", GETPTI(pwndNewActive));
  1409. }
  1410. #endif
  1411. if (!xxxActivateWindow(pwndNewActive, AW_USE))
  1412. return FALSE;
  1413. /*
  1414. * HACK ALERT: We set these bits to prevent
  1415. * the frames from redrawing themselves in
  1416. * the later call to DoSyncPaint().
  1417. *
  1418. * Prevent these captions from being repainted during
  1419. * the DoSyncPaint(). (bobgu 6/10/87)
  1420. */
  1421. if (pti->pq->spwndActive != NULL)
  1422. SetWF(pti->pq->spwndActive, WFNONCPAINT);
  1423. if (pti->pq->spwndActivePrev != NULL)
  1424. SetWF(pti->pq->spwndActivePrev, WFNONCPAINT);
  1425. return TRUE; // Indicate that we diddled these bits
  1426. }
  1427. return FALSE;
  1428. }
  1429. /***************************************************************************\
  1430. * xxxImeWindowPosChanged
  1431. *
  1432. * Send IME private message to update the composition window position
  1433. *
  1434. \***************************************************************************/
  1435. VOID xxxImeWindowPosChanged(
  1436. PSMWP psmwp)
  1437. {
  1438. PBWL pbwl;
  1439. PHWND phwnd;
  1440. PWND pwndDesktop = _GetDesktopWindow();
  1441. PTHREADINFO ptiCurrent = PtiCurrent();
  1442. if (pwndDesktop == NULL) {
  1443. return;
  1444. }
  1445. pbwl = BuildHwndList(pwndDesktop->spwndChild, BWL_ENUMLIST, ptiCurrent);
  1446. if (pbwl == NULL) {
  1447. return;
  1448. }
  1449. for (phwnd = pbwl->rghwnd; *phwnd != (HWND)1; ++phwnd) {
  1450. PWND pwndIme = ValidateHwnd(*phwnd);
  1451. TAGMSG1(DBGTAG_IMM, "ImePosC: pwndIme=%p", pwndIme);
  1452. /*
  1453. * If the thread is going away, just bail out.
  1454. */
  1455. if (ptiCurrent->TIF_flags & TIF_INCLEANUP) {
  1456. break;
  1457. }
  1458. if (pwndIme && pwndIme->head.pti == ptiCurrent &&
  1459. pwndIme->pcls->atomClassName == gpsi->atomSysClass[ICLS_IME]) {
  1460. HWND hwnd;
  1461. PWND pwnd;
  1462. TAGMSG1(DBGTAG_IMM, "ImePosC: OK, pwndIme=%p is one of us.", pwndIme);
  1463. try {
  1464. hwnd = ProbeAndReadStructure(((PIMEWND)pwndIme)->pimeui, IMEUI).hwndIMC;
  1465. } except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
  1466. continue;
  1467. }
  1468. pwnd = RevalidateHwnd(hwnd);
  1469. TAGMSG2(DBGTAG_IMM, "ImePosC: hwndImc=%p and its pwnd=%p", hwnd, pwnd);
  1470. /*
  1471. * Search upward
  1472. */
  1473. while (pwnd && pwnd != pwndDesktop) {
  1474. PCVR pcvr;
  1475. int ccvr;
  1476. hwnd = HWq(pwnd);
  1477. for (pcvr = psmwp->acvr, ccvr = psmwp->ccvr; --ccvr >= 0; pcvr++) {
  1478. if (hwnd == pcvr->pos.hwnd) {
  1479. TAGMSG1(DBGTAG_IMM, "ImePosC: pwnd=%p in the SWP list.", pwnd);
  1480. /*
  1481. * Send this private message if the window's size changes
  1482. * or the window moves. I.e.
  1483. * when (flag & (SWP_NOSIZE | SWP_NOMOVE)) != (SWP_NOSIZE | SWP_NOMOVE).
  1484. */
  1485. if (~pcvr->pos.flags & (SWP_NOSIZE | SWP_NOMOVE)) {
  1486. TL tl;
  1487. TAGMSG1(DBGTAG_IMM, "ImePosC: pwnd=%p is gonna move or resize.", pwnd);
  1488. ThreadLockAlwaysWithPti(ptiCurrent, pwndIme, &tl);
  1489. xxxSendMessage(pwndIme, WM_IME_SYSTEM, IMS_WINDOWPOS, 0);
  1490. ThreadUnlock(&tl);
  1491. }
  1492. break;
  1493. }
  1494. }
  1495. if (ccvr >= 0) {
  1496. break;
  1497. }
  1498. pwnd = pwnd->spwndParent;
  1499. }
  1500. }
  1501. }
  1502. FreeHwndList(pbwl);
  1503. }
  1504. /***************************************************************************\
  1505. * xxxSendChangedMsgs
  1506. *
  1507. * Send WM_WINDOWPOSCHANGED messages as needed
  1508. *
  1509. * History:
  1510. * 10-Jul-1991 DarrinM Ported from Win 3.1 sources.
  1511. \***************************************************************************/
  1512. VOID xxxSendChangedMsgs(
  1513. PSMWP psmwp)
  1514. {
  1515. PWND pwnd;
  1516. PCVR pcvr;
  1517. int ccvr;
  1518. TL tlpwnd;
  1519. /*
  1520. * Send all the messages that need to be sent...
  1521. */
  1522. for (pcvr = psmwp->acvr, ccvr = psmwp->ccvr; --ccvr >= 0; pcvr++) {
  1523. if (pcvr->pos.hwnd == NULL)
  1524. continue;
  1525. /*
  1526. * If the window's state didn't change, don't send the message.
  1527. */
  1528. if ((pcvr->pos.flags & SWP_CHANGEMASK) == SWP_NOCHANGE)
  1529. continue;
  1530. if ((pwnd = RevalidateHwnd(pcvr->pos.hwnd)) == NULL) {
  1531. RIPMSG0(RIP_WARNING, "xxxSendChangedMsgs: window went away in middle");
  1532. pcvr->pos.flags = SWP_NOREDRAW | SWP_NOCHANGE;
  1533. pcvr->pos.hwnd = NULL;
  1534. continue;
  1535. }
  1536. if (!IsStillWindowC(pcvr->pos.hwndInsertAfter)) {
  1537. pcvr->pos.hwnd = NULL;
  1538. continue;
  1539. }
  1540. /*
  1541. * Send the WM_WINDOWPOSCHANGED message...
  1542. *
  1543. * Make a frame copy of the WINDOWPOS, because the pcvr
  1544. * info may get reused if SetWindowPos()
  1545. * is called by the message handler: see the comments in
  1546. * AllocSmwp().
  1547. *
  1548. * WM_SIZE, WM_MOVE and WM_SHOW messages are sent by the
  1549. * DefWindowProc() WM_WINDOWPOSCHANGED message processing.
  1550. *
  1551. * Note: It's okay to destroy the window while processing this
  1552. * message, since this is the last call made by the window manager
  1553. * with the window handle before returning from SetWindowPos().
  1554. * This also means we don't have to revalidate the pwnd.
  1555. */
  1556. ThreadLockAlways(pwnd, &tlpwnd);
  1557. if (TestCF(pwnd, CFDROPSHADOW) && !(GetAppCompatFlags2ForPti(GETPTI(pwnd), VERMAX) & GACF2_NOSHADOW)) {
  1558. if (pcvr->pos.flags & SWP_HIDEWINDOW) {
  1559. xxxRemoveShadow(pwnd);
  1560. } else if (pcvr->pos.flags & SWP_SHOWWINDOW) {
  1561. BOOL fAddShadow = TRUE;
  1562. /*
  1563. * We don't want to add a shadow to menus that are being slid
  1564. * out because they don't use AnimateWindow and do not create
  1565. * a window rgn to clip inside during the animation. This means
  1566. * that even if we keep the shadow in sync with the menu, it
  1567. * won't be visible because it is actually z-ordered below the
  1568. * menu.
  1569. */
  1570. if ((GETFNID(pwnd) == FNID_MENU) && (!TestALPHA(MENUFADE)) && TestEffectUP(MENUANIMATION)) {
  1571. fAddShadow = FALSE;
  1572. }
  1573. if (fAddShadow) {
  1574. xxxAddShadow(pwnd);
  1575. }
  1576. } else {
  1577. if (!(pcvr->pos.flags & SWP_NOSIZE) ||
  1578. (pcvr->pos.flags & SWP_FRAMECHANGED)) {
  1579. UpdateShadowShape(pwnd);
  1580. } else if (!(pcvr->pos.flags & SWP_NOMOVE)) {
  1581. MoveShadow(pwnd);
  1582. }
  1583. if (!(pcvr->pos.flags & SWP_NOZORDER)) {
  1584. xxxUpdateShadowZorder(pwnd);
  1585. }
  1586. }
  1587. }
  1588. xxxSendMessage(pwnd, WM_WINDOWPOSCHANGED, 0, (LPARAM)&pcvr->pos);
  1589. /*
  1590. * Only send a shape change when moving/sizing/minimizing/restoring/
  1591. * maximizing (or framechange for NetMeeting to detect SetWindowRgn)
  1592. */
  1593. if (!(pcvr->pos.flags & SWP_NOCLIENTMOVE) ||
  1594. !(pcvr->pos.flags & SWP_NOCLIENTSIZE) ||
  1595. (pcvr->pos.flags & SWP_STATECHANGE) ||
  1596. (pcvr->pos.flags & SWP_FRAMECHANGED)) {
  1597. xxxWindowEvent(EVENT_OBJECT_LOCATIONCHANGE, pwnd, OBJID_WINDOW, INDEXID_CONTAINER, WEF_USEPWNDTHREAD);
  1598. }
  1599. ThreadUnlock(&tlpwnd);
  1600. } // for (... pcvr ...)
  1601. if (IS_IME_ENABLED()) {
  1602. xxxImeWindowPosChanged(psmwp);
  1603. }
  1604. }
  1605. /***************************************************************************\
  1606. * AsyncWindowPos
  1607. *
  1608. * This functions pulls from the passed-in SMWP all windows not owned by the
  1609. * current thread and passes them off to their owners to be handled. This
  1610. * eliminates synchronization where thread B won't get a chance to paint
  1611. * until thread A has completed painting (or at least returned from handling
  1612. * painting-related messages). Such synchronizations are bad because they
  1613. * can cause threads of unrelated process to hang each other.
  1614. *
  1615. * History:
  1616. * 09-10-91 darrinm Created.
  1617. \***************************************************************************/
  1618. VOID AsyncWindowPos(
  1619. PSMWP psmwp)
  1620. {
  1621. BOOL fFinished;
  1622. PCVR pcvrFirst;
  1623. PCVR pcvr;
  1624. PCVR pcvrT;
  1625. int ccvrRemaining;
  1626. int ccvr;
  1627. int chwnd;
  1628. PTHREADINFO ptiT;
  1629. PTHREADINFO ptiCurrent;
  1630. PSMWP psmwpNew;
  1631. pcvrFirst = psmwp->acvr;
  1632. ccvrRemaining = psmwp->ccvr;
  1633. ptiCurrent = PtiCurrent();
  1634. while (TRUE) {
  1635. fFinished = TRUE;
  1636. /*
  1637. * Loop through all windows in the SMWP list searching for windows
  1638. * owned by other threads. Return if none are found.
  1639. */
  1640. for (; ccvrRemaining != 0; pcvrFirst++, ccvrRemaining--) {
  1641. if (pcvrFirst->pos.hwnd == NULL)
  1642. continue;
  1643. ptiT = pcvrFirst->pti;
  1644. if (ptiT->pq != ptiCurrent->pq) {
  1645. fFinished = FALSE;
  1646. break;
  1647. }
  1648. }
  1649. if (fFinished) {
  1650. return;
  1651. }
  1652. /*
  1653. * We've found a window of another thread. Count how many other
  1654. * windows in the list are owned by the same thread so we can
  1655. * allocate a CVR array for them.
  1656. */
  1657. chwnd = 0;
  1658. for (pcvr = pcvrFirst, ccvr = ccvrRemaining; --ccvr >= 0; pcvr++) {
  1659. if (pcvr->pos.hwnd == NULL)
  1660. continue;
  1661. if (pcvr->pti->pq == ptiT->pq)
  1662. chwnd++;
  1663. }
  1664. /*
  1665. * Allocate temp SMWP/CVR structure to be passed to the other thread.
  1666. */
  1667. psmwpNew = (PSMWP)UserAllocPool(sizeof(SMWP) + (sizeof(CVR) * chwnd),
  1668. TAG_SWP);
  1669. /*
  1670. * Even if we can't allocate memory to pass the SMWP to another
  1671. * thread we still need to remove its windows from the current list.
  1672. */
  1673. if (psmwpNew == NULL) {
  1674. for (pcvr = pcvrFirst; chwnd != 0; pcvr++) {
  1675. if (pcvr->pti->pq == ptiT->pq) {
  1676. pcvr->pos.hwnd = NULL;
  1677. chwnd--;
  1678. }
  1679. }
  1680. continue;
  1681. }
  1682. psmwpNew->ccvr = chwnd;
  1683. psmwpNew->acvr = (PCVR)((PBYTE)psmwpNew + sizeof(SMWP));
  1684. for (pcvr = pcvrFirst, pcvrT = psmwpNew->acvr; chwnd != 0; pcvr++) {
  1685. if (pcvr->pos.hwnd == NULL)
  1686. continue;
  1687. /*
  1688. * Copy the appropriate CVR structs into our temp array.
  1689. */
  1690. if (pcvr->pti->pq == ptiT->pq) {
  1691. *pcvrT++ = *pcvr;
  1692. chwnd--;
  1693. /*
  1694. * Remove this window from the list of windows to be handled
  1695. * by the current thread.
  1696. */
  1697. pcvr->pos.hwnd = NULL;
  1698. }
  1699. }
  1700. /*
  1701. * This lets the other thread know it needs to do some windowposing.
  1702. * The other thread is responsible for freeing the temp SMWP/CVR array.
  1703. */
  1704. if (!PostEventMessage(ptiT, ptiT->pq, QEVENT_SETWINDOWPOS, NULL, 0,
  1705. (WPARAM)psmwpNew, (LPARAM)ptiT)) {
  1706. // IANJA RIP only to catch what was previously a bug: psmwpNew not freed
  1707. RIPMSG1(RIP_WARNING, "PostEventMessage swp to pti %#p failed", ptiT);
  1708. UserFreePool(psmwpNew);
  1709. }
  1710. }
  1711. }
  1712. /***************************************************************************\
  1713. * xxxProcessSetWindowPosEvent
  1714. *
  1715. * This function is called from xxxProcessEvent (QUEUE.C) to respond to
  1716. * posted QEVENT_SETWINDOWPOS events which originate at the AsyncWindowPos
  1717. * function above.
  1718. *
  1719. * History:
  1720. * 10-Sep-1991 DarrinM Created.
  1721. \***************************************************************************/
  1722. VOID xxxProcessSetWindowPosEvent(
  1723. PSMWP psmwpT)
  1724. {
  1725. PSMWP psmwp;
  1726. /*
  1727. * Create a bonafide SMWP/CVR array that xxxEndDeferWindowPos can use
  1728. * and later free.
  1729. */
  1730. if ((psmwp = InternalBeginDeferWindowPos(psmwpT->ccvr)) == NULL) {
  1731. UserFreePool(psmwpT);
  1732. return;
  1733. }
  1734. /*
  1735. * Copy the contents of the temp SMWP/CVR array into the real one.
  1736. */
  1737. RtlCopyMemory(psmwp->acvr, psmwpT->acvr, sizeof(CVR) * psmwpT->ccvr);
  1738. psmwp->ccvr = psmwpT->ccvr;
  1739. /*
  1740. * Complete the MultWindowPos operation now that we're on the correct
  1741. * context.
  1742. */
  1743. xxxEndDeferWindowPosEx(psmwp, FALSE);
  1744. /*
  1745. * Free the temp SMWP/CVR array.
  1746. */
  1747. UserFreePool(psmwpT);
  1748. }
  1749. #define SWP_BOZO ( SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOREDRAW | SWP_NOACTIVATE )
  1750. /***************************************************************************\
  1751. * DBGValidateSibblingZOrder
  1752. *
  1753. * History:
  1754. * 04/01/98 GerardoB Created
  1755. \***************************************************************************/
  1756. #if DBG
  1757. VOID DBGValidateSibblingZOrder(
  1758. PWND pwndParent)
  1759. {
  1760. PWND pwndT = pwndParent->spwndChild;
  1761. /*
  1762. * Check that the sibbling list looks OK right now
  1763. * We don't really care about the z-order of message windows.
  1764. */
  1765. if ((pwndT != NULL) && (pwndParent != PWNDMESSAGE(pwndParent))) {
  1766. BOOL fFoundNonTopMost = !TestWF(pwndT, WEFTOPMOST);
  1767. while (pwndT != NULL) {
  1768. if (TestWF(pwndT, WEFTOPMOST)) {
  1769. UserAssert(!fFoundNonTopMost);
  1770. } else {
  1771. fFoundNonTopMost = TRUE;
  1772. }
  1773. pwndT = pwndT->spwndNext;
  1774. }
  1775. }
  1776. }
  1777. #else
  1778. #define DBGValidateSibblingZOrder(pwndParent)
  1779. #endif // DBG
  1780. /***************************************************************************\
  1781. * zzzChangeStates
  1782. *
  1783. * History:
  1784. * 10-Jul-1991 DarrinM Ported from Win 3.1 sources.
  1785. \***************************************************************************/
  1786. VOID zzzChangeStates(
  1787. PWND pwndParent,
  1788. PSMWP psmwp)
  1789. {
  1790. int ccvr;
  1791. PCVR pcvr;
  1792. PWND pwnd;
  1793. TL tlpwnd;
  1794. TL tlpwndParent;
  1795. int czorder = 0;
  1796. BEGINATOMICCHECK();
  1797. ThreadLockAlways(pwndParent, &tlpwndParent);
  1798. /*
  1799. * Check that the sibbling list looks OK right now
  1800. *
  1801. * Here's the reason why this DBG code is commented out:
  1802. * Owned windows are always expected to be on top of the owner.
  1803. * However, an app can call SetWindowPos and insert the ownee after
  1804. * the owner. IME somehow does this too.
  1805. * This causes us to have A to be inserted after B and later in the
  1806. * windowpos array, B to be inserted somewhere else. Hence, A won't be in the
  1807. * expected position, because B will be moved after A is inserted.
  1808. * In other words, a window in hwndInsertAfter must not appear later
  1809. * as a hwnd to be z-ordered. Ownees below owners cause this situation.
  1810. */
  1811. // DBGValidateSibblingZOrder(pwndParent);
  1812. /*
  1813. * Now change the window states
  1814. */
  1815. for (pcvr = psmwp->acvr, ccvr = psmwp->ccvr; --ccvr >= 0; pcvr++) {
  1816. if (pcvr->pos.hwnd == NULL)
  1817. continue;
  1818. UserAssert(0 == (pcvr->pos.flags & SWP_NOTIFYALL));
  1819. pwnd = RevalidateHwnd(pcvr->pos.hwnd);
  1820. if ((pwnd == NULL) || !IsStillWindowC(pcvr->pos.hwndInsertAfter)) {
  1821. RIPMSG0(RIP_WARNING, "zzzChangeStates: Window went away in middle");
  1822. pcvr->pos.flags = SWP_NOREDRAW | SWP_NOCHANGE;
  1823. pcvr->pos.hwnd = NULL;
  1824. }
  1825. #if DBG
  1826. /*
  1827. * This can happen when we get re-entered during a callback or mulitple
  1828. * threads are z-ordering the same window. The tray does stuff like this.
  1829. * We would need to keep the toggle state in the windowpos structure to
  1830. * have each call have its own state.
  1831. */
  1832. if (TestWF(pwnd, WFTOGGLETOPMOST) && (pcvr->pos.flags & SWP_NOZORDER)) {
  1833. RIPMSG0(RIP_WARNING, "zzzChangeState: WFTOGGLETOPMOST should not be set");
  1834. }
  1835. #endif
  1836. /*
  1837. * Check to se if there is any state to change. If not, just
  1838. * continue.
  1839. */
  1840. if ((pcvr->pos.flags & SWP_CHANGEMASK) == SWP_NOCHANGE) {
  1841. pcvr->pos.flags |= SWP_NOREDRAW;
  1842. continue;
  1843. }
  1844. /*
  1845. * Change the window region if needed.
  1846. *
  1847. * Before we do anything, check to see if we're only Z-ordering.
  1848. * If so, then check to see if we're already in the right place,
  1849. * and if so, clear the ZORDER flag.
  1850. *
  1851. * We have to make this test in the state-change loop if previous
  1852. * windows in the WINDOWPOS list were Z-ordered, since the test depends
  1853. * on any ordering that may have happened previously.
  1854. *
  1855. * We don't bother to do this redundancy check if there are
  1856. * other bits set, because the amount of time saved in that
  1857. * case is about as much as the amount of time it takes to
  1858. * test for redundancy.
  1859. */
  1860. if (((pcvr->pos.flags & SWP_CHANGEMASK) ==
  1861. (SWP_NOCHANGE & ~SWP_NOZORDER))) {
  1862. /*
  1863. * If the window's Z order won't be changing, then
  1864. * we can clear the ZORDER bit and set NOREDRAW.
  1865. */
  1866. if ((!TestWF(pwnd, WFTOGGLETOPMOST)) && ValidateZorder(pcvr)) {
  1867. /*
  1868. * The window's already in the right place:
  1869. * Set SWP_NOZORDER bit, set SWP_NOREDRAW,
  1870. * and destroy the visrgn that we created earlier.
  1871. */
  1872. pcvr->pos.flags |= SWP_NOZORDER | SWP_NOREDRAW;
  1873. if (pcvr->hrgnVisOld) {
  1874. GreDeleteObject(pcvr->hrgnVisOld);
  1875. pcvr->hrgnVisOld = NULL;
  1876. }
  1877. continue;
  1878. }
  1879. }
  1880. /*
  1881. * Change the window state, as appropriate...
  1882. */
  1883. if ((pcvr->pos.flags &
  1884. (SWP_NOMOVE | SWP_NOSIZE | SWP_NOCLIENTSIZE | SWP_NOCLIENTMOVE)) !=
  1885. (SWP_NOMOVE | SWP_NOSIZE | SWP_NOCLIENTSIZE | SWP_NOCLIENTMOVE)) {
  1886. PCARET pcaret = &PtiCurrent()->pq->caret;
  1887. BOOL fRecreateRedirectionBitmap = FALSE;
  1888. int dxWindow, dyWindow, xOldWindow, yOldWindow;
  1889. if (TestWF(pwnd, WEFPREDIRECTED)) {
  1890. int cx = pwnd->rcWindow.right - pwnd->rcWindow.left;
  1891. int cy = pwnd->rcWindow.bottom - pwnd->rcWindow.top;
  1892. if (cx != pcvr->pos.cx || cy != pcvr->pos.cy) {
  1893. fRecreateRedirectionBitmap = TRUE;
  1894. }
  1895. }
  1896. /*
  1897. * Set up the new window and client rectangles.
  1898. */
  1899. xOldWindow = pwnd->rcWindow.left;
  1900. yOldWindow = pwnd->rcWindow.top;
  1901. pwnd->rcWindow.left = pcvr->pos.x;
  1902. pwnd->rcWindow.top = pcvr->pos.y;
  1903. if (pwndParent != PWNDDESKTOP(pwnd)) {
  1904. pwnd->rcWindow.left += pwndParent->rcClient.left;
  1905. pwnd->rcWindow.top += pwndParent->rcClient.top;
  1906. }
  1907. dxWindow = pwnd->rcWindow.left - xOldWindow;
  1908. dyWindow = pwnd->rcWindow.top - yOldWindow;
  1909. pwnd->rcWindow.right = pwnd->rcWindow.left + pcvr->pos.cx;
  1910. pwnd->rcWindow.bottom = pwnd->rcWindow.top + pcvr->pos.cy;
  1911. if (pwnd->rcWindow.right < pwnd->rcWindow.left) {
  1912. RIPMSG1(RIP_WARNING, "SWP: cx changed for pwnd %#p", pwnd);
  1913. pwnd->rcWindow.right = pwnd->rcWindow.left;
  1914. }
  1915. if (pwnd->rcWindow.bottom < pwnd->rcWindow.top) {
  1916. RIPMSG1(RIP_WARNING, "SWP: cy changed for pwnd %#p", pwnd);
  1917. pwnd->rcWindow.bottom = pwnd->rcWindow.top;
  1918. }
  1919. /*
  1920. * If the client moved relative to its parent,
  1921. * offset the caret by the amount that rcBlt moved
  1922. * relative to the client rect.
  1923. */
  1924. if (pwnd == pcaret->spwnd) {
  1925. /*
  1926. * Calculate the distance the contents of the client area
  1927. * is moving, in client-relative coordinates.
  1928. *
  1929. * Calculates dBlt + (old position - new position)
  1930. */
  1931. int dx = pcvr->dxBlt + pwnd->rcClient.left - pcvr->xClientNew;
  1932. int dy = pcvr->dyBlt + pwnd->rcClient.top - pcvr->yClientNew;
  1933. if (pwndParent != PWNDDESKTOP(pwnd))
  1934. {
  1935. dx -= pwndParent->rcClient.left;
  1936. dy -= pwndParent->rcClient.top;
  1937. }
  1938. if ((dx | dy) != 0) {
  1939. pcaret->x += dx;
  1940. pcaret->y += dy;
  1941. }
  1942. }
  1943. /*
  1944. * Set up the new client rect
  1945. * coordinates provided.
  1946. */
  1947. pwnd->rcClient.left = pcvr->xClientNew;
  1948. pwnd->rcClient.top = pcvr->yClientNew;
  1949. if (pwndParent != PWNDDESKTOP(pwnd))
  1950. {
  1951. pwnd->rcClient.left += pwndParent->rcClient.left;
  1952. pwnd->rcClient.top += pwndParent->rcClient.top;
  1953. }
  1954. pwnd->rcClient.right = pwnd->rcClient.left + pcvr->cxClientNew;
  1955. pwnd->rcClient.bottom = pwnd->rcClient.top + pcvr->cyClientNew;
  1956. /*
  1957. * If the window becomes smaller than the monitor, the system
  1958. * allows it to be moved (see SetSysMenu) and so we must remove
  1959. * the monitor region.
  1960. */
  1961. if (TestWF(pwnd, WFMAXFAKEREGIONAL) && IsSmallerThanScreen(pwnd)) {
  1962. SelectWindowRgn(pwnd, NULL);
  1963. }
  1964. /*
  1965. * If the layered window is resizing, try to resize the
  1966. * redirection bitmap associated with it.
  1967. */
  1968. if (fRecreateRedirectionBitmap) {
  1969. RecreateRedirectionBitmap(pwnd);
  1970. }
  1971. if ((dxWindow != 0) || (dyWindow != 0)) {
  1972. if ((pwnd->hrgnClip > HRGN_FULL) && (!TestWF(pwnd, WFMAXFAKEREGIONAL))) {
  1973. #ifdef LATER
  1974. /*
  1975. * LATER: The original USER code was offsetting the window
  1976. * region by dxBlt and dyBlt. This had problems for using
  1977. * window regions when hiding and showing the menus, so
  1978. * dxWindow and dyWindow were added (correctly). However,
  1979. * we should be aware of all of the places these values
  1980. * don't agree to make sure that we don't introduce
  1981. * regressions. Unfortunately in Whistler, we were
  1982. * periodically getting too much spew, and didn't have time
  1983. * to fully track down each of these cases.
  1984. */
  1985. /*
  1986. * Change position of window region, if it has one
  1987. * and it isn't a monitor region for a maximized window
  1988. */
  1989. if ((dxWindow != pcvr->dxBlt) || (dyWindow != pcvr->dyBlt)) {
  1990. /*
  1991. * If not moving by the same amount as the PCVR indicates,
  1992. * give a warning. This is normal when calling xxxSetMenu(),
  1993. * but we need to know if it is called in other situations.
  1994. */
  1995. RIPMSG1(RIP_WARNING, "SWP: (dxWindow != dxBlt) || (dyWindow != dyBlt) for pwnd %#p", pwnd);
  1996. }
  1997. #endif
  1998. GreOffsetRgn(pwnd->hrgnClip, dxWindow, dyWindow);
  1999. }
  2000. }
  2001. /*
  2002. * Offset the absolute positions of the window's update region,
  2003. * and the position and update regions of its children.
  2004. */
  2005. if ((pcvr->dxBlt | pcvr->dyBlt) != 0) {
  2006. if (pwnd->hrgnUpdate > HRGN_FULL) {
  2007. GreOffsetRgn(pwnd->hrgnUpdate, pcvr->dxBlt, pcvr->dyBlt);
  2008. }
  2009. OffsetChildren(pwnd, pcvr->dxBlt, pcvr->dyBlt, NULL);
  2010. /*
  2011. * Change the position of the sprite associated with
  2012. * this window.
  2013. */
  2014. if (TestWF(pwnd, WEFLAYERED)) {
  2015. POINT ptPos = {pcvr->pos.x, pcvr->pos.y};
  2016. GreUpdateSprite(gpDispInfo->hDev, PtoHq(pwnd), NULL, NULL,
  2017. &ptPos, NULL, NULL, NULL, 0, NULL, 0, NULL);
  2018. }
  2019. }
  2020. }
  2021. /*
  2022. * Change the Z order if the flag is set. Revalidate
  2023. * hwndInsertAfter to make sure that it is still valid
  2024. */
  2025. if (!(pcvr->pos.flags & SWP_NOZORDER)) {
  2026. if (ValidateWindowPos(pcvr, pwndParent)) {
  2027. UnlinkWindow(pwnd, pwndParent);
  2028. LinkWindow(pwnd,
  2029. PWInsertAfter(pcvr->pos.hwndInsertAfter),
  2030. pwndParent);
  2031. czorder++;
  2032. /*
  2033. * HACK ALERT MERGE
  2034. *
  2035. * ValidateZOrder() depends on rational, consistent setting of the
  2036. * WEFTOPMOST bit in order for it to work properly. What this means
  2037. * is that we can't set or clear these bits ahead of time based on
  2038. * where the window is moving to: instead we have to change the bit
  2039. * after we've moved it. Enter the WFTOGGLETOPMOST bit: That bit
  2040. * is set in ZOrderByOwner() based on what the topmost bit will
  2041. * eventually be set to. To maintain a consistent state, we make
  2042. * any changes AFTER the window has been Z-ordered.
  2043. */
  2044. if (TestWF(pwnd, WFTOGGLETOPMOST)) {
  2045. PBYTE pb;
  2046. ClrWF(pwnd, WFTOGGLETOPMOST);
  2047. pb = ((BYTE *)&pwnd->state);
  2048. pb[HIBYTE(WEFTOPMOST)] ^= LOBYTE(WEFTOPMOST);
  2049. }
  2050. } else {
  2051. pcvr->pos.flags |= SWP_NOZORDER;
  2052. ClrWF(pwnd, WFTOGGLETOPMOST);
  2053. }
  2054. }
  2055. /*
  2056. * Handle SWP_HIDEWINDOW and SWP_SHOWWINDOW, by clearing or setting
  2057. * the WS_VISIBLE bit.
  2058. */
  2059. UserAssert(pwndParent != NULL);
  2060. ThreadLockAlways(pwnd, &tlpwnd);
  2061. if (pcvr->pos.flags & SWP_SHOWWINDOW) {
  2062. /*
  2063. * Window is showing. If this app is still in startup mode,
  2064. * (still starting), give the the app starting cursor 5 more
  2065. * seconds.
  2066. */
  2067. if (GETPTI(pwnd)->ppi->W32PF_Flags & W32PF_APPSTARTING)
  2068. zzzCalcStartCursorHide((PW32PROCESS)GETPTI(pwnd)->ppi, 5000);
  2069. /*
  2070. * Set the WS_VISIBLE bit.
  2071. */
  2072. SetVisible(pwnd, SV_SET);
  2073. zzzWindowEvent(EVENT_OBJECT_SHOW, pwnd, OBJID_WINDOW, INDEXID_CONTAINER, WEF_USEPWNDTHREAD);
  2074. if (IsTrayWindow(pwnd)) {
  2075. #ifdef HUNGAPP_GHOSTING
  2076. if((GETFNID(pwnd) == FNID_GHOST)) {
  2077. if(TestWF(pwnd, WFFRAMEON)) {
  2078. psmwp->bShellNotify = TRUE;
  2079. pcvr->pos.flags |= SWP_NOTIFYACTIVATE;
  2080. }
  2081. }
  2082. else
  2083. #endif // HUNGAPP_GHOSTING
  2084. {
  2085. psmwp->bShellNotify = TRUE;
  2086. pcvr->pos.flags |= TestWF(pwnd, WFFRAMEON) ? SWP_NOTIFYACTIVATE|SWP_NOTIFYCREATE: SWP_NOTIFYCREATE;
  2087. }
  2088. } else if (TestWF(pwnd, WFFULLSCREEN)) {
  2089. /*
  2090. * Wake up the tray so it can notice that there is now
  2091. * a fullscreen visible window. This deals with bugs
  2092. * 32164, 141563, and 150217.
  2093. */
  2094. psmwp->bShellNotify = TRUE;
  2095. pcvr->pos.flags |= SWP_NOTIFYFS;
  2096. }
  2097. /*
  2098. * If we're redrawing, create an SPB for this window if
  2099. * needed.
  2100. */
  2101. if (!(pcvr->pos.flags & SWP_NOREDRAW) ||
  2102. (pcvr->pos.flags & SWP_CREATESPB)) {
  2103. /*
  2104. * ONLY create an SPB if this window happens to be
  2105. * on TOP of all others. NOTE: We could optimize this by
  2106. * passing in the new vis rgn to CreateSpb() so that the
  2107. * non-visible part of the window is automatically
  2108. * invalid in the SPB.
  2109. */
  2110. /*
  2111. * Make sure this window's desktop is on top !
  2112. */
  2113. if (TestCF(pwnd, CFSAVEBITS) &&
  2114. pwnd->head.rpdesk == grpdeskRitInput) {
  2115. /*
  2116. * If this window is the topmost VISIBLE window,
  2117. * then we can create an SPB.
  2118. */
  2119. PWND pwndT;
  2120. RECT rcT;
  2121. for (pwndT = pwnd->spwndParent->spwndChild ;
  2122. pwndT;
  2123. pwndT = pwndT->spwndNext) {
  2124. if (pwndT == pwnd) {
  2125. CreateSpb(pwnd, FALSE, gpDispInfo->hdcScreen);
  2126. break;
  2127. }
  2128. if (TestWF(pwndT, WFVISIBLE)) {
  2129. /*
  2130. * Does this window intersect the SAVEBITS
  2131. * window at all? If so, bail out.
  2132. */
  2133. if (IntersectRect(&rcT,
  2134. &pwnd->rcWindow,
  2135. &pwndT->rcWindow)) {
  2136. break;
  2137. }
  2138. }
  2139. }
  2140. }
  2141. }
  2142. } else if (pcvr->pos.flags & SWP_HIDEWINDOW) {
  2143. /*
  2144. * for people like MS-Access 2.0 who SetWindowPos( SWP_BOZO
  2145. * and blow away themselves on the shell, then lets
  2146. * just ignore their plea to be removed from the tray
  2147. */
  2148. if (((pcvr->pos.flags & SWP_BOZO ) != SWP_BOZO) &&
  2149. IsTrayWindow(pwnd)
  2150. #ifdef HUNGAPP_GHOSTING
  2151. && (GETFNID(pwnd) != FNID_GHOST)
  2152. #endif // HUNGAPP_GHOSTING
  2153. ) {
  2154. psmwp->bShellNotify = TRUE;
  2155. pcvr->pos.flags |= SWP_NOTIFYDESTROY;
  2156. }
  2157. /*
  2158. * Clear the WS_VISIBLE bit.
  2159. */
  2160. SetVisible(pwnd, SV_UNSET | SV_CLRFTRUEVIS);
  2161. zzzWindowEvent(EVENT_OBJECT_HIDE, pwnd, OBJID_WINDOW, INDEXID_CONTAINER, WEF_USEPWNDTHREAD);
  2162. }
  2163. /*
  2164. * BACKWARD COMPATIBILITY HACK
  2165. *
  2166. * Under 3.0, window frames were always redrawn, even if
  2167. * SWP_NOREDRAW was specified. If we've gotten this far
  2168. * and we're visible, and SWP_NOREDRAW was specified, set
  2169. * the WFSENDNCPAINT bit.
  2170. *
  2171. * Apps such as ABC Flowcharter and 123W assume this.
  2172. * Typical offending code is MoveWindow(pwnd, ..., FALSE);
  2173. * followed by InvalidateRect(pwnd, NULL, TRUE);
  2174. */
  2175. if (TestWF(pwnd, WFVISIBLE)) {
  2176. if ((pcvr->pos.flags & SWP_STATECHANGE) ||
  2177. (!TestWF(pwnd, WFWIN31COMPAT) && (pcvr->pos.flags & SWP_NOREDRAW))) {
  2178. SetWF(pwnd, WFSENDNCPAINT);
  2179. }
  2180. }
  2181. /*
  2182. * If this window has a clipping region set it now
  2183. */
  2184. if (pcvr->hrgnClip != NULL) {
  2185. SelectWindowRgn(pwnd, pcvr->hrgnClip);
  2186. }
  2187. ThreadUnlock(&tlpwnd);
  2188. }
  2189. /*
  2190. * Check that the sibbling list looks OK now that we're done
  2191. */
  2192. // DBGValidateSibblingZOrder(pwndParent);
  2193. if (czorder) {
  2194. zzzWindowEvent(EVENT_OBJECT_REORDER, pwndParent, OBJID_CLIENT, INDEXID_CONTAINER, 0);
  2195. }
  2196. ThreadUnlock(&tlpwndParent);
  2197. ENDATOMICCHECK();
  2198. }
  2199. /***************************************************************************\
  2200. * SwpCalcVisRgn
  2201. *
  2202. * This routine calculates a non-clipchildren visrgn for pwnd into hrgn.
  2203. *
  2204. * History:
  2205. * 10-Jul-1991 DarrinM Ported from Win 3.1 sources.
  2206. \***************************************************************************/
  2207. BOOL SwpCalcVisRgn(
  2208. PWND pwnd,
  2209. HRGN hrgn)
  2210. {
  2211. /*
  2212. * If this window is invisible, then
  2213. * the visrgn will be empty, so return FALSE.
  2214. */
  2215. if (!TestWF(pwnd, WFVISIBLE))
  2216. return FALSE;
  2217. /*
  2218. * Otherwise do it the hard way...
  2219. */
  2220. return CalcVisRgn(&hrgn,
  2221. pwnd,
  2222. pwnd,
  2223. (TestWF(pwnd, WFCLIPSIBLINGS) ?
  2224. (DCX_CLIPSIBLINGS | DCX_WINDOW) : (DCX_WINDOW)));
  2225. }
  2226. /***************************************************************************\
  2227. * CombineOldNewVis
  2228. *
  2229. * ORs or DIFFs hrgnOldVis and hrgnNewVis, depending on crgn, and the
  2230. * RE_* bits of fsRgnEmpty. Basically, this routine handles the optimization
  2231. * where if either region is empty, the other can be copied. Returns FALSE
  2232. * if the result is empty.
  2233. *
  2234. * History:
  2235. * 10-Jul-1991 DarrinM Ported from Win 3.1 sources.
  2236. \***************************************************************************/
  2237. BOOL CombineOldNewVis(
  2238. HRGN hrgn,
  2239. HRGN hrgnVisOld,
  2240. HRGN hrgnVisNew,
  2241. UINT crgn,
  2242. UINT fsRgnEmpty)
  2243. {
  2244. switch (fsRgnEmpty & (RE_VISOLD | RE_VISNEW)) {
  2245. case RE_VISOLD:
  2246. /*
  2247. * If we're calculating old - new and old is empty, then result is
  2248. * empty. Otherwise, result is new.
  2249. */
  2250. if (crgn == RGN_DIFF)
  2251. return FALSE;
  2252. CopyRgn(hrgn, hrgnVisNew);
  2253. break;
  2254. case RE_VISNEW:
  2255. /*
  2256. * New is empty: result will be the old.
  2257. */
  2258. CopyRgn(hrgn, hrgnVisOld);
  2259. break;
  2260. case RE_VISNEW | RE_VISOLD:
  2261. /*
  2262. * Both empty: so's the result.
  2263. */
  2264. return FALSE;
  2265. case 0:
  2266. /*
  2267. * Neither are empty: do the real combine.
  2268. */
  2269. switch (GreCombineRgn(hrgn, hrgnVisOld, hrgnVisNew, crgn)) {
  2270. case NULLREGION:
  2271. case ERROR:
  2272. return FALSE;
  2273. default:
  2274. break;
  2275. }
  2276. break;
  2277. }
  2278. return TRUE;
  2279. }
  2280. /***************************************************************************\
  2281. * BltValidInit
  2282. *
  2283. *
  2284. * History:
  2285. * 10-Jul-1991 DarrinM Ported from Win 3.1 sources.
  2286. \***************************************************************************/
  2287. int BltValidInit(
  2288. PSMWP psmwp)
  2289. {
  2290. int ccvr;
  2291. int cIter = 0;
  2292. PCVR pcvr;
  2293. PWND pwnd;
  2294. BOOL fChangeState = FALSE;
  2295. /*
  2296. * Before we change any window state, calculate the old visrgn
  2297. */
  2298. for (pcvr = psmwp->acvr, ccvr = psmwp->ccvr; --ccvr >= 0; pcvr++) {
  2299. UINT flags = pcvr->pos.flags;
  2300. /*
  2301. * Make sure this is initialized to NULL; we may be sticking something
  2302. * in it, and we want to know later if we need to free that thing.
  2303. */
  2304. pcvr->hrgnVisOld = NULL;
  2305. if (pcvr->pos.hwnd == NULL)
  2306. continue;
  2307. pwnd = RevalidateHwnd(pcvr->pos.hwnd);
  2308. if ((pwnd == NULL) || !IsStillWindowC(pcvr->pos.hwndInsertAfter)) {
  2309. pcvr->pos.hwnd = NULL;
  2310. pcvr->pos.flags = SWP_NOREDRAW | SWP_NOCHANGE;
  2311. continue;
  2312. }
  2313. /*
  2314. * Before we change any window's state, ensure that any SPBs
  2315. * over the window's old location are invalidated if necessary.
  2316. * This must be done because no WM_PAINT messages will be
  2317. * sent to anyone for the covered area if the area is obscured
  2318. * by other windows.
  2319. */
  2320. if (AnySpbs() && !(flags & SWP_NOREDRAW))
  2321. SpbCheckRect(pwnd, &pwnd->rcWindow, DCX_WINDOW);
  2322. /*
  2323. * Count the number of passes through the loop
  2324. */
  2325. cIter++;
  2326. /*
  2327. * Remember if any SWPs need their state changed.
  2328. */
  2329. if ((flags & SWP_CHANGEMASK) != SWP_NOCHANGE)
  2330. fChangeState = TRUE;
  2331. /*
  2332. * If we're not redrawing, no need to calculate visrgn
  2333. */
  2334. if (pcvr->pos.flags & SWP_NOREDRAW)
  2335. continue;
  2336. if (!SYSMET(SAMEDISPLAYFORMAT))
  2337. PreventInterMonitorBlts(pcvr);
  2338. pcvr->fsRE = 0;
  2339. pcvr->hrgnVisOld = CreateEmptyRgn();
  2340. if (pcvr->hrgnVisOld == NULL ||
  2341. !SwpCalcVisRgn(pwnd, pcvr->hrgnVisOld)) {
  2342. pcvr->fsRE = RE_VISOLD;
  2343. }
  2344. }
  2345. return (fChangeState ? cIter : 0);
  2346. }
  2347. /***************************************************************************\
  2348. * zzzBltValidBits
  2349. *
  2350. * NOTE: Although zzzBltValidBits calls 'xxxInternalInvalidate' it does not
  2351. * specify any of the flags that will cause immediate updating. This means
  2352. * that it does not actually leave the critsect and therefore is not an 'xxx'
  2353. * routine and doesn't have to bother with revalidation.
  2354. *
  2355. * This is the routine that blts the windows around on the screen, taking
  2356. * into account SPBs.
  2357. *
  2358. * Here is the basic algebra going on here:
  2359. *
  2360. * ASSUMES: - rcBlt is aligned to the DESTINATION
  2361. * - offset() offsets from source to destination
  2362. *
  2363. * 1. hrgnSrc = offset(rcBlt) & hrgnVisOld
  2364. *
  2365. * Source region is the blt rectangle aligned with the old visrgn,
  2366. * intersected with the old visrgn.
  2367. *
  2368. * 2. hrgnDst = rcBlt & hrgnVisNew
  2369. *
  2370. * Dest region is the blt rectangle intersected with the new visrgn.
  2371. *
  2372. * 3. ghrgnValid = offset(hrgnSrc) & hrgnDst
  2373. *
  2374. * Valid area is the intersection of the destination with the source
  2375. * superimposed on the destination.
  2376. *
  2377. * 3.1 ghrgnValid = ghrgnValid - hrgnInterMonitor
  2378. *
  2379. * Subtract out any pieces that are moving across monitors.
  2380. *
  2381. * 4. ghrgnValid -= ghrgnValidSum
  2382. *
  2383. * This step takes into account the possibility that another window's
  2384. * valid bits were bltted on top of this windows valid bits. So, as we
  2385. * blt a window's bits, we accumulate where it went, and subtract it
  2386. * from subsequent window's valid area.
  2387. *
  2388. * 5. ghrgnInvalid = (hrgnSrc | hrgnDst) - ghrgnValid
  2389. *
  2390. * 6. ghrgnInvalid += RestoreSpb(ghrgnInvalid) (sort of)
  2391. *
  2392. * This is the wild part, because of the grungy way that the device
  2393. * driver SaveBits() routine works. We call RestoreSpb() with
  2394. * a copy of ghrgnInvalid. If the SPB valid region doesn't intersect
  2395. * ghrgnInvalid, RestoreSpb() does nothing. But if it DOES intersect,
  2396. * it blts down the ENTIRE saved SPB bitmap, which may include area
  2397. * of the old window position that IS NOT part of ghrgnValid!
  2398. *
  2399. * To correct for this, ghrgnValid is adjusted by subtracting off
  2400. * the ghrgnInvalid computed by RestoreSpb, if it modified it.
  2401. *
  2402. * 7. ghrgnInvalidSum |= ghrgnInvalid
  2403. *
  2404. * We save up the sum of all the invalid areas, and invalidate the
  2405. * whole schmear in one fell swoop at the end.
  2406. *
  2407. * 8. ghrgnValidSum |= ghrgnValid
  2408. *
  2409. * We keep track of the valid areas so far, which are subtracted
  2410. * in step 4.
  2411. *
  2412. * The actual steps occur in a slightly different order than above, and
  2413. * there are numerous optimizations that are taken advantage of (the
  2414. * most important having to do with hiding and showing, and complete
  2415. * SPB restoration).
  2416. *
  2417. * Returns TRUE if some drawing was done, FALSE otherwise.
  2418. *
  2419. * History:
  2420. * 10-Jul-1991 DarrinM Ported from Win 3.1 sources.
  2421. \***************************************************************************/
  2422. BOOL zzzBltValidBits(
  2423. PSMWP psmwp)
  2424. {
  2425. int ccvr;
  2426. int cIter;
  2427. PCVR pcvr;
  2428. PWND pwnd;
  2429. PWND pwndParent;
  2430. PWND pwndT;
  2431. PWINDOWPOS ppos;
  2432. HRGN hrgnInvalidate;
  2433. UINT fsRgnEmpty;
  2434. UINT fsSumEmpty;
  2435. int cwndShowing;
  2436. BOOL fSyncPaint = FALSE;
  2437. BOOL fInvalidateLayers = FALSE;
  2438. HDC hdcScreen = NULL;
  2439. DeferWinEventNotify();
  2440. GreLockDisplay(gpDispInfo->hDev);
  2441. /*
  2442. * Compute old visrgns and count total CVRs in list. A side-effect of
  2443. * BltValidInit is that revalidates all the windows in the SMWP array.
  2444. */
  2445. if ((cIter = BltValidInit(psmwp)) == 0) {
  2446. CleanupAndExit:
  2447. /*
  2448. * Go through the cvr list and free the regions that BltValidInit()
  2449. * created.
  2450. */
  2451. for (pcvr = psmwp->acvr, ccvr = psmwp->ccvr; --ccvr >= 0; pcvr++) {
  2452. if (pcvr->hrgnVisOld) {
  2453. GreDeleteObject(pcvr->hrgnVisOld);
  2454. pcvr->hrgnVisOld = NULL;
  2455. }
  2456. }
  2457. goto UnlockAndExit;
  2458. }
  2459. /*
  2460. * We left the crit sect since last time we validated the smwp. Validate
  2461. * it again, and find the first WINDOWPOS structure with a non-NULL
  2462. * hwnd in it.
  2463. */
  2464. ppos = NULL;
  2465. for (pcvr = psmwp->acvr, ccvr = psmwp->ccvr; --ccvr >= 0; pcvr++) {
  2466. /*
  2467. * Revalidate window and if it's invalid, NULL it out in the WINDOWPOS
  2468. * struct.
  2469. */
  2470. pwnd = RevalidateHwnd(pcvr->pos.hwnd);
  2471. if ((pwnd == NULL) ||
  2472. (pwnd->spwndParent == NULL) ||
  2473. !IsStillWindowC(pcvr->pos.hwndInsertAfter)) {
  2474. pcvr->pos.hwnd = NULL;
  2475. pcvr->pos.flags = SWP_NOREDRAW | SWP_NOCHANGE;
  2476. continue;
  2477. }
  2478. /*
  2479. * Remember the first WINDOWPOS structure that has a non-NULL
  2480. * hwnd.
  2481. */
  2482. if (ppos == NULL)
  2483. ppos = &pcvr->pos;
  2484. }
  2485. if (ppos == NULL)
  2486. goto CleanupAndExit;
  2487. UserAssert(PW(ppos->hwnd));
  2488. pwndParent = PW(ppos->hwnd)->spwndParent;
  2489. UserAssert(pwndParent);
  2490. /*
  2491. * Go account for any dirty DCs at this point, to ensure that:
  2492. * - any drawing done before we create an SPB will not
  2493. * later invalidate that SPB
  2494. * - the SPB regions reflect the true state of the screen,
  2495. * so that we don't validate parts of windows that are dirty.
  2496. *
  2497. * We must make this call before we change any window state.
  2498. */
  2499. if (AnySpbs())
  2500. SpbCheck();
  2501. /*
  2502. * Change the window states
  2503. */
  2504. zzzChangeStates(pwndParent, psmwp);
  2505. /*
  2506. * move window bits around
  2507. *
  2508. * Invalidate the DCs for the siblings of this window.
  2509. *
  2510. * If our parent is not clipchildren, then we don't need to
  2511. * invalidate its DCs. If it IS clipchildren, its client visrgn
  2512. * will be changing, so we must invalidate it too.
  2513. *
  2514. * Note, because IDC_MOVEBLT is set, final completion of WNDOBJ
  2515. * notification is delayed until GreClientRgnDone is called.
  2516. * This final notification does not happen until after the
  2517. * window move blts have completed.
  2518. */
  2519. zzzInvalidateDCCache(pwndParent,
  2520. IDC_MOVEBLT |
  2521. (TestWF(pwndParent, WFCLIPCHILDREN) ?
  2522. IDC_CLIENTONLY : IDC_CHILDRENONLY));
  2523. /*
  2524. * Now, do the bltting or whatever that is required.
  2525. */
  2526. fsSumEmpty = RE_VALIDSUM | RE_INVALIDSUM;
  2527. hrgnInvalidate = ghrgnInvalidSum;
  2528. /*
  2529. * Init count of windows being shown with SWP_SHOWWINDOW
  2530. * for our backward compatibility hack later.
  2531. */
  2532. cwndShowing = 0;
  2533. for (pcvr = psmwp->acvr, ccvr = psmwp->ccvr; --ccvr >= 0; pcvr++) {
  2534. /*
  2535. * Decrement loop count. When cIter is 0, then
  2536. * we're on the last pass through the loop.
  2537. */
  2538. cIter--;
  2539. if (pcvr->pos.hwnd == NULL)
  2540. continue;
  2541. /*
  2542. * If we're not redrawing, try the next one.
  2543. */
  2544. if (pcvr->pos.flags & SWP_NOREDRAW)
  2545. continue;
  2546. /*
  2547. * Some drawing has been done
  2548. */
  2549. fSyncPaint = TRUE;
  2550. pwnd = PW(pcvr->pos.hwnd);
  2551. fsRgnEmpty = pcvr->fsRE;
  2552. /*
  2553. * Sprites should not be invalidated or cause invalidation.
  2554. */
  2555. #ifdef REDIRECTION
  2556. if (TestWF(pwnd, WEFLAYERED) || TestWF(pwnd, WEFEXTREDIRECTED)) {
  2557. #else // REDIRECTION
  2558. if (TestWF(pwnd, WEFLAYERED)) {
  2559. #endif // REDIRECTION
  2560. if (GetRedirectionBitmap(pwnd) == NULL)
  2561. goto InvalidEmpty;
  2562. /*
  2563. * Sizing or showing uncovers new bits for a window, so
  2564. * do the normal invalidation in this case. When sizing makes a
  2565. * window smaller setting fInvalidateLayers to TRUE has the side
  2566. * effect of allowing other layered windows to be invalidated.
  2567. * Ideally, it should only allow invlalidating just the windows
  2568. * that resized or showed. This would be a bunch of work, but we
  2569. * should consider it for later.
  2570. */
  2571. if ((pcvr->pos.flags & SWP_NOSIZE) &&
  2572. (pcvr->pos.flags & (SWP_SHOWWINDOW | SWP_FRAMECHANGED)) == 0) {
  2573. goto InvalidEmpty;
  2574. } else {
  2575. fInvalidateLayers = TRUE;
  2576. }
  2577. }
  2578. /*
  2579. * Calculate the new visrgn
  2580. */
  2581. if (!SwpCalcVisRgn(pwnd, ghrgnVisNew))
  2582. fsRgnEmpty |= RE_VISNEW;
  2583. /*
  2584. * If the window is obscured by another window with an SPB,
  2585. * we have to ensure that that SPB gets invalidated properly
  2586. * since the app may not be getting a WM_PAINT msg or anything
  2587. * to invalidate the bits.
  2588. */
  2589. if (AnySpbs())
  2590. SpbCheckRect(pwnd, &pwnd->rcWindow, DCX_WINDOW);
  2591. /*
  2592. * Calculate ghrgnValid:
  2593. *
  2594. * ghrgnValid = OffsetRgn(rcBlt, -dxBlt, -dyBlt) & hrgnVisOld
  2595. * ghrgnValid = ghrgnValid - ghrgnValidSum
  2596. * OffsetRgn(ghrgnValid, dxBlt, dyBlt);
  2597. * ghrgnValid = ghrgnValid - hrgnUpdate
  2598. * ghrgnValid = ghrgnValid & hrgnVisNew;
  2599. *
  2600. * If either the old or new visrgns are empty, there
  2601. * can be no valid bits...
  2602. */
  2603. if (fsRgnEmpty & (RE_VISOLD | RE_VISNEW))
  2604. goto ValidEmpty;
  2605. /*
  2606. * If the entire window is already completely invalid, blow out.
  2607. */
  2608. if (pwnd->hrgnUpdate == HRGN_FULL)
  2609. goto ValidEmpty;
  2610. /*
  2611. * If the blt rectangle is empty, there can be no valid bits.
  2612. */
  2613. if ((pcvr->rcBlt.right <= pcvr->rcBlt.left) ||
  2614. (pcvr->rcBlt.bottom <= pcvr->rcBlt.top)) {
  2615. goto ValidEmpty;
  2616. }
  2617. GreSetRectRgn(ghrgnSWP1,
  2618. pcvr->rcBlt.left - pcvr->dxBlt,
  2619. pcvr->rcBlt.top - pcvr->dyBlt,
  2620. pcvr->rcBlt.right - pcvr->dxBlt,
  2621. pcvr->rcBlt.bottom - pcvr->dyBlt);
  2622. switch (IntersectRgn(ghrgnValid, ghrgnSWP1, pcvr->hrgnVisOld)) {
  2623. case NULLREGION:
  2624. case ERROR:
  2625. goto ValidEmpty;
  2626. break;
  2627. }
  2628. if (!(fsSumEmpty & RE_VALIDSUM)) {
  2629. switch (SubtractRgn(ghrgnValid, ghrgnValid, ghrgnValidSum)) {
  2630. case NULLREGION:
  2631. case ERROR:
  2632. goto ValidEmpty;
  2633. break;
  2634. }
  2635. }
  2636. if ((pcvr->dxBlt | pcvr->dyBlt) != 0)
  2637. GreOffsetRgn(ghrgnValid, pcvr->dxBlt, pcvr->dyBlt);
  2638. /*
  2639. * Now subtract off the update regions of ourself and any
  2640. * non-clipchildren parents...
  2641. */
  2642. pwndT = pwnd;
  2643. do {
  2644. if (pwndT->hrgnUpdate == HRGN_FULL)
  2645. goto ValidEmpty;
  2646. if (pwndT->hrgnUpdate != NULL) {
  2647. switch (SubtractRgn(ghrgnValid, ghrgnValid, pwndT->hrgnUpdate)) {
  2648. case NULLREGION:
  2649. case ERROR:
  2650. goto ValidEmpty;
  2651. break;
  2652. }
  2653. }
  2654. pwndT = pwndT->spwndParent;
  2655. } while (pwndT && !TestWF(pwndT, WFCLIPCHILDREN));
  2656. /*
  2657. * Subtract out the intermonitor blt pieces.
  2658. */
  2659. if (pcvr->hrgnInterMonitor != NULL) {
  2660. switch (SubtractRgn(ghrgnValid, ghrgnValid, pcvr->hrgnInterMonitor)) {
  2661. case NULLREGION:
  2662. case ERROR:
  2663. goto ValidEmpty;
  2664. }
  2665. }
  2666. switch (IntersectRgn(ghrgnValid, ghrgnValid, ghrgnVisNew)) {
  2667. case NULLREGION:
  2668. case ERROR:
  2669. ValidEmpty:
  2670. fsRgnEmpty |= RE_VALID;
  2671. break;
  2672. }
  2673. /*
  2674. * Before we restore the restore bits over part of our
  2675. * image, we need to first copy any valid bits to their
  2676. * final destination.
  2677. */
  2678. if (!(fsRgnEmpty & RE_VALID) && ((pcvr->dxBlt | pcvr->dyBlt) != 0)) {
  2679. if (hdcScreen == NULL)
  2680. hdcScreen = gpDispInfo->hdcScreen;
  2681. GreSelectVisRgn(hdcScreen, ghrgnValid, SVR_COPYNEW);
  2682. #ifdef _WINDOWBLT_NOTIFICATION_
  2683. /*
  2684. * Define _WINDOWBLT_NOTIFICATION_ to turn on Window BLT notification.
  2685. * This notification will set a special flag in the SURFOBJ passed to
  2686. * drivers when the DrvCopyBits operation is called to move a window.
  2687. *
  2688. * See also:
  2689. * ntgdi\gre\maskblt.cxx
  2690. */
  2691. NtGdiBitBlt(hdcScreen,
  2692. pcvr->rcBlt.left,
  2693. pcvr->rcBlt.top,
  2694. pcvr->rcBlt.right - pcvr->rcBlt.left,
  2695. pcvr->rcBlt.bottom - pcvr->rcBlt.top,
  2696. hdcScreen,
  2697. pcvr->rcBlt.left - pcvr->dxBlt,
  2698. pcvr->rcBlt.top - pcvr->dyBlt,
  2699. SRCCOPY,
  2700. 0,
  2701. GBB_WINDOWBLT);
  2702. #else
  2703. GreBitBlt(hdcScreen,
  2704. pcvr->rcBlt.left,
  2705. pcvr->rcBlt.top,
  2706. pcvr->rcBlt.right - pcvr->rcBlt.left,
  2707. pcvr->rcBlt.bottom - pcvr->rcBlt.top,
  2708. hdcScreen,
  2709. pcvr->rcBlt.left - pcvr->dxBlt,
  2710. pcvr->rcBlt.top - pcvr->dyBlt,
  2711. SRCCOPY,
  2712. 0);
  2713. #endif
  2714. }
  2715. /*
  2716. * Now take care of any SPB bit restoration we need to do.
  2717. *
  2718. * Calculate the region to clip the RestoreSpb() output to:
  2719. *
  2720. * ghrgnInvalid = hrgnVisOld - hrgnVisNew
  2721. */
  2722. if (TestWF(pwnd, WFHASSPB) &&
  2723. !(fsRgnEmpty & RE_VISOLD) &&
  2724. CombineOldNewVis(ghrgnInvalid, pcvr->hrgnVisOld, ghrgnVisNew, RGN_DIFF, fsRgnEmpty)) {
  2725. UINT retRSPB;
  2726. /*
  2727. * Perform SPB bits restore. We pass RestoreSpb() the region of
  2728. * the part of the SPB that got uncovered by this window rearrangement.
  2729. * It tries to restore as much of this area as it can from the SPB,
  2730. * and returns the area that could not be restored from the SPB.
  2731. *
  2732. * The device driver's SaveBitmap() function does not clip at all
  2733. * when it restores bits, which means that it might write bits
  2734. * in an otherwise valid area. This means that the invalid area
  2735. * returned by RestoreSpb() may actually be LARGER than the original
  2736. * hrgnSpb passed in.
  2737. *
  2738. * RestoreSpb() returns TRUE if some part of ghrgnInvalid needs
  2739. * to be invalidated.
  2740. */
  2741. if ((retRSPB = RestoreSpb(pwnd, ghrgnInvalid, &hdcScreen)) == RSPB_NO_INVALIDATE) {
  2742. /*
  2743. * If hrgnVisNew is empty, then we know the whole invalid
  2744. * area is empty.
  2745. */
  2746. if (fsRgnEmpty & RE_VISNEW)
  2747. goto InvalidEmpty;
  2748. } else if (retRSPB == RSPB_INVALIDATE_SSB) {
  2749. /*
  2750. * If RestoreSpb actually invalidated some area and we already
  2751. * have a ghrgnValidSum then subtract the newly invalidated area
  2752. * Warning this region subtract is not in the Win 3.1 code but
  2753. * they probably did not have the problem as severe because their
  2754. * drivers were limited to one level of SaveScreenBits
  2755. */
  2756. if (!(fsSumEmpty & RE_VALIDSUM))
  2757. SubtractRgn(ghrgnValidSum, ghrgnValidSum, ghrgnInvalid);
  2758. }
  2759. /*
  2760. * ghrgnInvalid += hrgnVisNew
  2761. */
  2762. if (!(fsRgnEmpty & RE_VISNEW))
  2763. UnionRgn(ghrgnInvalid, ghrgnInvalid, ghrgnVisNew);
  2764. /*
  2765. * Some of the area we calculated as valid may have gotten
  2766. * obliterated by the SPB restore. To ensure this isn't
  2767. * the case, subtract off the ghrgnInvalid returned by RestoreSpb.
  2768. */
  2769. // LATER mikeke VALIDSUM / ghrgnValid mismatch
  2770. if (!(fsRgnEmpty & RE_VALIDSUM)) {
  2771. switch (SubtractRgn(ghrgnValid, ghrgnValid, ghrgnInvalid)) {
  2772. case NULLREGION:
  2773. case ERROR:
  2774. fsRgnEmpty |= RE_VALIDSUM;
  2775. break;
  2776. }
  2777. }
  2778. } else {
  2779. /*
  2780. * No SPB. Simple ghrgnInvalid calculation is:
  2781. *
  2782. * ghrgnInvalid = hrgnVisNew + hrgnVisOld;
  2783. */
  2784. if (pcvr->hrgnVisOld == NULL) {
  2785. /*
  2786. * If we couldn't create hrgnVisOld, then
  2787. * invalidate the entire parent
  2788. */
  2789. SetRectRgnIndirect(ghrgnInvalid, &pwndParent->rcWindow);
  2790. } else {
  2791. if (!CombineOldNewVis(ghrgnInvalid,
  2792. pcvr->hrgnVisOld,
  2793. ghrgnVisNew,
  2794. RGN_OR,
  2795. fsRgnEmpty)) {
  2796. goto InvalidEmpty;
  2797. }
  2798. }
  2799. }
  2800. /*
  2801. * Update ghrgnValidSum
  2802. *
  2803. * ghrgnValidSum += ghrgnValid
  2804. */
  2805. if (!(fsRgnEmpty & RE_VALID)) {
  2806. /*
  2807. * If the sum region is empty, then COPY instead of OR
  2808. */
  2809. if (fsSumEmpty & RE_VALIDSUM)
  2810. CopyRgn(ghrgnValidSum, ghrgnValid);
  2811. else
  2812. UnionRgn(ghrgnValidSum, ghrgnValid, ghrgnValidSum);
  2813. fsSumEmpty &= ~RE_VALIDSUM;
  2814. }
  2815. /*
  2816. * Subtract ghrgnValidSum from ghrgnInvalid if non-empty,
  2817. * otherwise use ghrgnValid. Note, ghrgnValid has been OR'ed
  2818. * into ghrgnValidSum already.
  2819. */
  2820. if (!(fsSumEmpty & RE_VALIDSUM) || !(fsRgnEmpty & RE_VALID)) {
  2821. switch (SubtractRgn(ghrgnInvalid, ghrgnInvalid,
  2822. !(fsSumEmpty & RE_VALIDSUM) ? ghrgnValidSum : ghrgnValid)) {
  2823. case NULLREGION:
  2824. case ERROR:
  2825. InvalidEmpty:
  2826. fsRgnEmpty |= RE_INVALID;
  2827. break;
  2828. }
  2829. }
  2830. /*
  2831. * If there are any SPB bits left over, it wasn't just created
  2832. * (SWP_SHOWWINDOW), and an operation occured that invalidates
  2833. * the spb bits, get rid of the spb. A move, size, hide, or
  2834. * zorder operation will invalidate the bits. Note that we do this
  2835. * outside of the SWP_NOREDRAW case in case the guy set that flag
  2836. * when he had some SPB bits lying around.
  2837. */
  2838. if (TestWF(pwnd, WFHASSPB) && !(pcvr->pos.flags & SWP_SHOWWINDOW) &&
  2839. (pcvr->pos.flags &
  2840. (SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_HIDEWINDOW))
  2841. != (SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER)) {
  2842. FreeSpb(FindSpb(pwnd));
  2843. }
  2844. /*
  2845. * Finally, free up hrgnVisOld.
  2846. */
  2847. if (pcvr->hrgnVisOld) {
  2848. GreDeleteObject(pcvr->hrgnVisOld);
  2849. pcvr->hrgnVisOld = NULL;
  2850. }
  2851. /*
  2852. * BACKWARD COMPATIBILITY HACK
  2853. *
  2854. * In 3.0, a ShowWindow() NEVER invalidated any of the children.
  2855. * It would invalidate the parent and the window being shown, but
  2856. * no others.
  2857. *
  2858. * We only apply hack (a) to 3.0 apps when all the windows involved
  2859. * are doing a SWP_SHOWWINDOW: if any aren't, then we have to make
  2860. * sure the siblings are invalidated too. So, we count the windows
  2861. * doing a SHOWWINDOW and compare it to the total count in the CVR.
  2862. */
  2863. if (!TestWF(pwnd, WFWIN31COMPAT) && (pcvr->pos.flags & SWP_SHOWWINDOW))
  2864. cwndShowing++;
  2865. /*
  2866. * Update ghrgnInvalidSum:
  2867. *
  2868. * ghrgnInvalidSum += ghrgnInvalid
  2869. */
  2870. if (!(fsRgnEmpty & RE_INVALID)) {
  2871. /*
  2872. * BACKWARD COMPATIBILITY HACK
  2873. *
  2874. * In many cases including ShowWindow, CS_V/HREDRAW,
  2875. * SWP_NOCOPYBITS, etc, 3.0 always invalidated the window with
  2876. * (HRGN)1, regardless of how it was clipped by children, siblings,
  2877. * or parents. Besides being more efficient, this caused child
  2878. * windows that would otherwise not get update regions to get
  2879. * invalidated -- see the hack notes in InternalInvalidate2.
  2880. *
  2881. * This is a performance hack (usually) because (HRGN)1 can save
  2882. * a lot of region calculations in the normal case. So, we do this
  2883. * for 3.1 apps as well as 3.0 apps.
  2884. *
  2885. * We detect the case as follows: invalid area not empty,
  2886. * valid area empty, and new visrgn not empty.
  2887. */
  2888. if ((fsRgnEmpty & RE_VALID) && !(fsRgnEmpty & RE_VISNEW)) {
  2889. /*
  2890. * With the parameters we use InternalInvalidate() does
  2891. * not leave the critical section
  2892. */
  2893. BEGINATOMICCHECK();
  2894. xxxInternalInvalidate(pwnd,
  2895. HRGN_FULL,
  2896. RDW_INVALIDATE |
  2897. RDW_FRAME |
  2898. RDW_ERASE |
  2899. RDW_ALLCHILDREN);
  2900. ENDATOMICCHECK();
  2901. }
  2902. /*
  2903. * If the sum region is empty, then COPY instead of OR
  2904. */
  2905. if (fsSumEmpty & RE_INVALIDSUM) {
  2906. /*
  2907. * HACK ALERT:
  2908. * If this is the last pass through the loop (cIter == 0)
  2909. * and ghrgnInvalidSum is currently empty,
  2910. * then instead of copying ghrgnInvalid to ghrgnInvalidSum,
  2911. * just set hrgnInvalidate to ghrgnInvalid. This saves
  2912. * a region copy in the single-window case.
  2913. */
  2914. if (cIter == 0) {
  2915. hrgnInvalidate = ghrgnInvalid;
  2916. } else {
  2917. CopyRgn(ghrgnInvalidSum, ghrgnInvalid);
  2918. }
  2919. } else {
  2920. UnionRgn(ghrgnInvalidSum, ghrgnInvalid, ghrgnInvalidSum);
  2921. }
  2922. fsSumEmpty &= ~RE_INVALIDSUM;
  2923. }
  2924. } // for (... pcvr ...)
  2925. /*
  2926. * Now, invalidate as needed.
  2927. */
  2928. if (!(fsSumEmpty & RE_INVALIDSUM)) {
  2929. /*
  2930. * BACKWARD COMPATIBILITY HACK (see above)
  2931. *
  2932. * If all the windows involved were being shown, then
  2933. * invalidate the parent ONLY -- don't enumerate any children.
  2934. * (the windows involved have already been invalidated above).
  2935. * This hack is only applied to 3.0 apps (see above).
  2936. */
  2937. /*
  2938. * More hack-o-rama. On Win3.1, the desktop paint would only
  2939. * repaint those portions inside the rect returned from GetClipBox().
  2940. * Dialogs with spbs outside the rect returned from GetClipBox() would
  2941. * not get their spbs invalidated until later, when you clicked on
  2942. * them to make them active. The only dialog that wouldn't really loose
  2943. * its bits is the control panel desktop dialog, which would restore
  2944. * its bad bits when it went away (in certain configurations). On
  2945. * NT, the desktop would repaint and then the dialog would go away.
  2946. * On Win3.1, the dialog would go away and then the desktop would
  2947. * repaint. On NT, because of preemption and little differences in
  2948. * painting order between applications, there was an opportunity to
  2949. * put bad bits on the screen, on Win3.1 there wasn't.
  2950. *
  2951. * Now... the below code that passes RDW_NOCHILDREN only gets executed
  2952. * if the app is marked as a win3.0 app (latest CorelDraw, also wep
  2953. * freecell demonstrates the same problem). This code would get
  2954. * executed when a dialog got shown. So for a win3.0 app, spb would get
  2955. * saved, the dialog would get shown, the desktop invalidated, the
  2956. * desktop would paint, the spb would get clobbered. In short, when
  2957. * a win3.0 app would put up a dialog, all spbs would get freed because
  2958. * of the new (and correct) way the desktop repaints.
  2959. *
  2960. * So the desktop check hack will counter-act the invalidate
  2961. * RDW_NOCHILDREN case if all windows are hiding / showing and the
  2962. * desktop is being invalidated. Note that in the no RDW_NOCHILDREN
  2963. * case, the invalid area gets distributed to the children first (in
  2964. * this case, children of the desktop), so if the children cover the
  2965. * desktop, the desktop won't get any invalid region, which is what
  2966. * we want. - scottlu
  2967. */
  2968. /*
  2969. * With the parameters we use InternalInvalidate() does not leave
  2970. * the critical section
  2971. */
  2972. DWORD dwFlags = RDW_INVALIDATE | RDW_ERASE;
  2973. if (cwndShowing == psmwp->ccvr &&
  2974. pwndParent != PWNDDESKTOP(pwndParent)) {
  2975. dwFlags |= RDW_NOCHILDREN;
  2976. } else {
  2977. dwFlags |= RDW_ALLCHILDREN;
  2978. }
  2979. if (fInvalidateLayers) {
  2980. dwFlags |= RDW_INVALIDATELAYERS;
  2981. }
  2982. BEGINATOMICCHECK();
  2983. xxxInternalInvalidate(pwndParent, hrgnInvalidate, dwFlags);
  2984. ENDATOMICCHECK();
  2985. }
  2986. /*
  2987. * Since zzzInvalidateDCCache was called with IDC_MOVEBLT specified,
  2988. * we must complete the WNDOBJ notification with a call to
  2989. * GreClientRgnDone.
  2990. *
  2991. * Note: in zzzInvalidateDCCache, it is necessary to call
  2992. * GreClientRgnUpdated even if gcountPWO is 0. However,
  2993. * GreClientRgnDone only does something if gcountPWO is non-zero,
  2994. * so we can optimize slightly.
  2995. */
  2996. if (gcountPWO != 0) {
  2997. GreClientRgnDone(GCR_WNDOBJEXISTS);
  2998. }
  2999. UnlockAndExit:
  3000. /*
  3001. * If necessary, release the screen DC
  3002. */
  3003. if (hdcScreen != NULL) {
  3004. /*
  3005. * Reset the visrgn before we go...
  3006. */
  3007. GreSelectVisRgn(hdcScreen, NULL, SVR_DELETEOLD);
  3008. /*
  3009. * Make sure that the drawing we did in this DC does not affect
  3010. * any SPBs. Clear the dirty rect.
  3011. */
  3012. GreGetBounds(hdcScreen, NULL, 0); // NULL means reset
  3013. }
  3014. /*
  3015. * All the dirty work is done. Ok to leave the critsects we entered
  3016. * earlier and dispatch any deferred Window Event notifications.
  3017. */
  3018. GreUnlockDisplay(gpDispInfo->hDev);
  3019. zzzEndDeferWinEventNotify();
  3020. return fSyncPaint;
  3021. }
  3022. /***************************************************************************\
  3023. * xxxHandleWindowPosChanged
  3024. *
  3025. * DefWindowProc() HandleWindowPosChanged handler.
  3026. *
  3027. * History:
  3028. * 11-Jul-1991 DarrinM Ported from Win 3.1 sources.
  3029. \***************************************************************************/
  3030. VOID xxxHandleWindowPosChanged(
  3031. PWND pwnd,
  3032. PWINDOWPOS ppos)
  3033. {
  3034. CheckLock(pwnd);
  3035. if (!(ppos->flags & SWP_NOCLIENTMOVE)) {
  3036. POINT pt;
  3037. PWND pwndParent;
  3038. pt.x = pwnd->rcClient.left;
  3039. pt.y = pwnd->rcClient.top;
  3040. pwndParent = pwnd->spwndParent;
  3041. UserAssert(pwndParent);
  3042. if (pwndParent != PWNDDESKTOP(pwnd)) {
  3043. pt.x -= pwndParent->rcClient.left;
  3044. pt.y -= pwndParent->rcClient.top;
  3045. }
  3046. xxxSendMessage(
  3047. pwnd,
  3048. WM_MOVE,
  3049. FALSE,
  3050. MAKELONG(pt.x, pt.y));
  3051. }
  3052. if ((ppos->flags & SWP_STATECHANGE) || !(ppos->flags & SWP_NOCLIENTSIZE)) {
  3053. if (TestWF(pwnd, WFMINIMIZED))
  3054. xxxSendSizeMessage(pwnd, SIZEICONIC);
  3055. else if (TestWF(pwnd, WFMAXIMIZED))
  3056. xxxSendSizeMessage(pwnd, SIZEFULLSCREEN);
  3057. else
  3058. xxxSendSizeMessage(pwnd, SIZENORMAL);
  3059. }
  3060. }
  3061. /***************************************************************************\
  3062. * PWND GetRealOwner(pwnd)
  3063. *
  3064. * Returns the owner of pwnd, normalized so that it shares the same parent
  3065. * of pwnd.
  3066. *
  3067. * History:
  3068. * 04-Mar-1992 MikeKe From win31
  3069. \***************************************************************************/
  3070. PWND GetRealOwner(
  3071. PWND pwnd)
  3072. {
  3073. PWND pwndParent = pwnd->spwndParent;
  3074. /*
  3075. * A frame window owned by itself is "unowned"
  3076. */
  3077. if (pwnd != pwnd->spwndOwner && (pwnd = pwnd->spwndOwner) != NULL) {
  3078. /*
  3079. * The NULL test is in case the owner is higher than the
  3080. * passed in window (e.g. your owner IS your parent)
  3081. */
  3082. while (pwnd != NULL && pwnd->spwndParent != pwndParent)
  3083. pwnd = pwnd->spwndParent;
  3084. }
  3085. return pwnd;
  3086. }
  3087. /***************************************************************************\
  3088. *
  3089. * Starting at pwnd (or pwndParent->spwndChild if pwnd == NULL), find
  3090. * next window owned by pwndOwner
  3091. *
  3092. * History:
  3093. * 04-Mar-1992 MikeKe From win31
  3094. \***************************************************************************/
  3095. PWND NextOwnedWindow(
  3096. PWND pwnd,
  3097. PWND pwndOwner,
  3098. PWND pwndParent)
  3099. {
  3100. if (pwnd == NULL) {
  3101. pwnd = pwndParent->spwndChild;
  3102. /*
  3103. * In xxxCreateWindowEx(), we callback the window proc while the
  3104. * window is still not linked yet to the window tree. If it is the
  3105. * first child of its parent, then the window spwndParent will point
  3106. * to the parent while the parent spwndChild will still be NULL. If
  3107. * the window proc called ShowWindow() or SetWindowPos() in response
  3108. * to those early callbacks, we will end up here with broken window
  3109. * tree. The right fix is to never call back while the window tree
  3110. * is in an intermediate state (i.e. linking the window to the tree
  3111. * before any callback) but this seem scary to change now because of
  3112. * app compat.
  3113. *
  3114. * Windows Bug #482192.
  3115. */
  3116. if (pwnd == NULL) {
  3117. RIPMSG1(RIP_WARNING,
  3118. "Window tree structure broken at pwnd: 0x%p",
  3119. pwndParent);
  3120. return NULL;
  3121. }
  3122. goto loop;
  3123. }
  3124. while ((pwnd = pwnd->spwndNext) != NULL) {
  3125. loop:
  3126. /*
  3127. * If owner of pwnd is pwndOwner, break out of here.
  3128. */
  3129. if (pwndOwner == GetRealOwner(pwnd)) {
  3130. break;
  3131. }
  3132. }
  3133. return pwnd;
  3134. }
  3135. /***************************************************************************\
  3136. *
  3137. * Recursively enumerate owned windows starting from pwndRoot,
  3138. * to set the state of WEFTOPMOST. Doesn't actually diddle
  3139. * this bit yet: the work gets done in zzzChangeStates():
  3140. * instead we just set the WFTOGGLETOPMOST bit as appropriate.
  3141. *
  3142. * We can't diddle the state until the Z order changes are done,
  3143. * or else GetTopMostWindow() and the like will do the wrong thing.
  3144. *
  3145. * History:
  3146. * 04-Mar-1992 MikeKe From win31
  3147. \***************************************************************************/
  3148. VOID SetTopmost(
  3149. PWND pwndRoot,
  3150. BOOL fTopmost)
  3151. {
  3152. PWND pwnd;
  3153. /*
  3154. * If the new state is different than the current state,
  3155. * then set the TOGGLETOPMOST bit, so it'll get toggled
  3156. * in ChangeStates().
  3157. */
  3158. UserAssert((fTopmost == TRUE) || (fTopmost == FALSE));
  3159. if (!!TestWF(pwndRoot, WEFTOPMOST) ^ fTopmost) {
  3160. SetWF(pwndRoot, WFTOGGLETOPMOST);
  3161. } else {
  3162. ClrWF(pwndRoot, WFTOGGLETOPMOST);
  3163. }
  3164. pwnd = NULL;
  3165. while (pwnd = NextOwnedWindow(pwnd, pwndRoot, pwndRoot->spwndParent)) {
  3166. SetTopmost(pwnd, fTopmost);
  3167. }
  3168. }
  3169. /*
  3170. * LATER: (hiroyama) #88810
  3171. * The IME code here broke the US regression test, so backing it up until we
  3172. * hit the problem on NT.
  3173. */
  3174. #ifdef LATER
  3175. /***************************************************************************\
  3176. * IsBottomIMEWindow()
  3177. *
  3178. * returns TRUE if pwnd is IME window and its toplevel window is BOTTOMMOST
  3179. *
  3180. * Ported: 18-Apr-1997 Hiroyama from Memphis
  3181. \***************************************************************************/
  3182. BOOL IsBottomIMEWindow(
  3183. PWND pwnd)
  3184. {
  3185. if (TestCF(pwnd, CFIME) ||
  3186. (pwnd->pcls->atomClassName == gpsi->atomSysClass[ICLS_IME])) {
  3187. PWND pwndT2 = pwnd;
  3188. PWND pwndTopOwner = pwnd;
  3189. PWND pwndDesktop;
  3190. if (grpdeskRitInput == NULL || grpdeskRitInput->pDeskInfo == NULL) {
  3191. // Desktop is being created or not yet created
  3192. RIPMSG1(RIP_WARNING, "IsBottomIMEWindow: Desktop is being created or not yet created. pwnd=%#p\n",
  3193. pwnd);
  3194. return FALSE;
  3195. }
  3196. pwndDesktop = grpdeskRitInput->pDeskInfo->spwnd;
  3197. UserAssert(pwndDesktop);
  3198. /*
  3199. * search the toplevel owner window of the IME window.
  3200. */
  3201. while (pwndT2 && (pwndT2 != pwndDesktop)) {
  3202. pwndTopOwner = pwndT2;
  3203. pwndT2 = pwndT2->spwndOwner;
  3204. }
  3205. /*
  3206. * TRUE if the toplevel owner window of the IME window is BOTTOMMOST
  3207. */
  3208. return (BOOL)(TestWF(pwndTopOwner, WFBOTTOMMOST));
  3209. }
  3210. return FALSE;
  3211. }
  3212. /***************************************************************************\
  3213. * ImeCheckBottomIMEWindow()
  3214. *
  3215. * returns TRUE if pwndT->spwndNext's owner is BOTTOMMOST
  3216. *
  3217. * Ported: 18-Apr-1997 Hiroyama from Memphis
  3218. \***************************************************************************/
  3219. BOOL ImeCheckBottomIMEWindow(
  3220. PWND pwndT)
  3221. {
  3222. /*
  3223. * pwnd is IME window and its toplevel window is BOTTOMMOST
  3224. */
  3225. PWND pwndDesktop;
  3226. PWND pwndT2 = pwndT->spwndNext;
  3227. PWND pwndTopOwner = pwndT2;
  3228. UserAssert(grpdeskRipInput != NULL && grpdeskRitInput->pDeskInfo != NULL);
  3229. pwndDesktop = grpdeskRitInput->pDeskInfo->spwnd;
  3230. /*
  3231. * check if toplevel owner window of pwnd->spwndNext is bottommost
  3232. */
  3233. while (pwndT2 && (pwndT2 != pwndDesktop)) {
  3234. pwndTopOwner = pwndT2;
  3235. pwndT2 = pwndT2->spwndOwner;
  3236. }
  3237. if (pwndTopOwner && TestWF(pwndTopOwner, WFBOTTOMMOST)) {
  3238. /*
  3239. * yes, pwndT is the last one whose toplevel window is *not* BOTTOMMOST
  3240. */
  3241. return TRUE;
  3242. }
  3243. return FALSE;
  3244. }
  3245. #endif // LATER
  3246. /***************************************************************************\
  3247. * CalcForegroundInsertAfter
  3248. *
  3249. * Calculates where to zorder a window that doesn't belong to the foreground
  3250. * thread and is not topmost but wants to come to the top. This routine
  3251. * calculates what "top" means under those conditions.
  3252. *
  3253. * 14-Sep-1992 ScottLu Created.
  3254. \***************************************************************************/
  3255. PWND CalcForegroundInsertAfter(
  3256. PWND pwnd)
  3257. {
  3258. PWND pwndInsertAfter, pwndInsertAfterSave;
  3259. PWND pwndT;
  3260. PTHREADINFO ptiTop;
  3261. #ifdef LATER // see #88810
  3262. BOOLEAN fImeOwnerIsBottom = FALSE;
  3263. #endif
  3264. /*
  3265. * If we're allowing this application to make this top
  3266. * level window foreground active, then this app may
  3267. * not be foreground yet, but we want any windows it
  3268. * zorders to appear on top because it is probably about
  3269. * to activate them (this is a guess!) If this is the case,
  3270. * let it do what it wants. A good example of this is an
  3271. * application like toolbook that creates a window without a
  3272. * caption, doesn't activate it, and wants that to appear on top.
  3273. */
  3274. if (TestWF(pwnd, WFBOTTOMMOST)) {
  3275. pwndInsertAfter = GetLastNonBottomMostWindow(pwnd, TRUE);
  3276. } else {
  3277. pwndInsertAfter = GetLastTopMostWindow();
  3278. #ifdef LATER // see #88810
  3279. if (IS_IME_ENABLED()) {
  3280. fImeOwnerIsBottom = IsBottomIMEWindow(pwnd);
  3281. if (fImeOwnerIsBottom) {
  3282. for (pwndT = pwndInsertAfter; pwndT; pwndT = pwndT->spwndNext) {
  3283. if (ImeCheckBottomIMEWindow(pwndT)) {
  3284. /*
  3285. * toplevel owner of pwndT->spwndNext is BOTTOMMOST
  3286. */
  3287. break;
  3288. }
  3289. pwndInsertAfter = pwndT;
  3290. }
  3291. }
  3292. }
  3293. #endif // LATER
  3294. }
  3295. if (!TestwndChild(pwnd)) {
  3296. // if (hwnd->hwndParent == hwndDesktop) -- Chicago conditional FritzS
  3297. if ((GETPTI(pwnd)->TIF_flags & TIF_ALLOWFOREGROUNDACTIVATE) ||
  3298. (GETPTI(pwnd)->ppi->W32PF_Flags & W32PF_ALLOWFOREGROUNDACTIVATE)) {
  3299. return pwndInsertAfter;
  3300. }
  3301. }
  3302. /*
  3303. * If there is no foreground thread or this pwnd is of the foreground
  3304. * thread, then let it come to the top.
  3305. */
  3306. if (gpqForeground == NULL)
  3307. return pwndInsertAfter;
  3308. if (GETPTI(pwnd)->pq == gpqForeground)
  3309. return pwndInsertAfter;
  3310. /*
  3311. * This thread is not of the foreground queue, so search for a window
  3312. * of this thread to zorder above.
  3313. */
  3314. pwndT = ((pwndInsertAfter == NULL) ?
  3315. pwnd->spwndParent->spwndChild :
  3316. pwndInsertAfter);
  3317. /*
  3318. * Remember the top insert after in case this first loop
  3319. * fails to find a window
  3320. */
  3321. pwndInsertAfterSave = pwndInsertAfter;
  3322. for (; pwndT != NULL; pwndT = pwndT->spwndNext) {
  3323. /*
  3324. * This window wants to come to the top if possible.
  3325. * If we're passing our own window, get out of this loop:
  3326. * by now we already have pwndInsertAfter set up to the
  3327. * last available window to insert after.
  3328. */
  3329. if ((pwndT == pwnd) || TestWF(pwndT, WFBOTTOMMOST))
  3330. break;
  3331. /*
  3332. * If this window isn't owned by this thread, continue.
  3333. */
  3334. if (GETPTI(pwndT) != GETPTI(pwnd)) {
  3335. pwndInsertAfter = pwndT;
  3336. continue;
  3337. }
  3338. /*
  3339. * Don't want a window zordering below one of its top most windows
  3340. * if it isn't foreground.
  3341. */
  3342. if (TestWF(pwndT, WEFTOPMOST)) {
  3343. pwndInsertAfter = pwndT;
  3344. continue;
  3345. }
  3346. #ifdef LATER // see #88810
  3347. // FE_IME
  3348. if (fImeOwnerIsBottom && ImeCheckBottomIMEWindow(pwndT)) {
  3349. /*
  3350. * owner of pwndT->spwndNext is BOTTOMMOST
  3351. * so pwndT is the last one whose owner is not bottommost.
  3352. */
  3353. pwndInsertAfter = pwndT;
  3354. continue;
  3355. }
  3356. // end FE_IME
  3357. #endif
  3358. /*
  3359. * Ok to change zorder of top level windows because of
  3360. * invisible windows laying around, but not children:
  3361. * applications would go nuts if we did this.
  3362. */
  3363. if (!TestwndChild(pwndT)) {
  3364. if (!TestWF(pwndT, WFVISIBLE)) {
  3365. pwndInsertAfter = pwndT;
  3366. continue;
  3367. }
  3368. }
  3369. break;
  3370. }
  3371. /*
  3372. * If we didn't find a window in the previous loop,
  3373. * it means that the thread has no
  3374. * other sibling windows, so we need to put it after the
  3375. * foreground window (foreground thread). Search for the
  3376. * first unowned window of the foreground app to zorder
  3377. * after.
  3378. */
  3379. if ((pwndT == NULL) || TestWF(pwndT, WFBOTTOMMOST)) {
  3380. /*
  3381. * This is our first guess in case nothing works out.
  3382. */
  3383. pwndInsertAfter = pwndInsertAfterSave;
  3384. /*
  3385. * Start below the last topmost or from the top if no
  3386. * topmost windows.
  3387. */
  3388. if ((pwndT = pwndInsertAfter) == NULL)
  3389. pwndT = pwnd->spwndParent->spwndChild;
  3390. /*
  3391. * ptiTop is the pti of the active window in the foreground queue!
  3392. */
  3393. ptiTop = NULL;
  3394. if (gpqForeground->spwndActive != NULL)
  3395. ptiTop = GETPTI(gpqForeground->spwndActive);
  3396. for (; pwndT != NULL; pwndT = pwndT->spwndNext) {
  3397. if (TestWF(pwndT, WFBOTTOMMOST))
  3398. break;
  3399. /*
  3400. * If not the top most thread, continue.
  3401. */
  3402. if (GETPTI(pwndT) != ptiTop)
  3403. continue;
  3404. /*
  3405. * Found one of the foreground thread. Remember this
  3406. * as the next best guess. Try to find an unowned
  3407. * visible window, which would indicate the main
  3408. * window of the foreground thread. If owned,
  3409. * continue.
  3410. */
  3411. if (pwndT->spwndOwner != NULL) {
  3412. pwndInsertAfter = pwndT;
  3413. continue;
  3414. }
  3415. /*
  3416. * Unowned and of the foreground thread. Is it visible?
  3417. * If not, get out of here.
  3418. */
  3419. if (!TestWF(pwndT, WFVISIBLE))
  3420. continue;
  3421. #ifdef LATER // see #88810
  3422. // FE_IME
  3423. if (fImeOwnerIsBottom && ImeCheckBottomIMEWindow(pwndT)) {
  3424. continue;
  3425. }
  3426. // end FE_IME
  3427. #endif
  3428. /*
  3429. * Best possible match so far: unowned visible window
  3430. * of the foreground thread.
  3431. */
  3432. pwndInsertAfter = pwndT;
  3433. }
  3434. }
  3435. UserAssert(pwnd != pwndInsertAfter);
  3436. return pwndInsertAfter;
  3437. }
  3438. /***************************************************************************\
  3439. * GetTopMostInsertAfter
  3440. *
  3441. * We don't want any one to get in front of a hard error box, except menus,
  3442. * screen savers, etc.
  3443. *
  3444. * Don't call it directly, use the GETTOPMOSTINSERTAFTER macro to avoid
  3445. * the call when there is no hard error box up (gHardErrorHandler.pti == NULL).
  3446. *
  3447. * 04-25-96 GerardoB Created
  3448. \***************************************************************************/
  3449. PWND GetTopMostInsertAfter(
  3450. PWND pwnd)
  3451. {
  3452. PWND pwndT;
  3453. PTHREADINFO ptiCurrent;
  3454. PDESKTOP pdesk;
  3455. WORD wfnid;
  3456. /*
  3457. * If you hit this assertion, you're probably not using the
  3458. * GETTOPMOSTINSERTAFTER macro to make this call.
  3459. */
  3460. UserAssert(gHardErrorHandler.pti != NULL);
  3461. /*
  3462. * pwnd: Menu and switch (ALT-TAB) windows can go on top.
  3463. */
  3464. wfnid = GETFNID(pwnd);
  3465. if ((wfnid == FNID_MENU) || (wfnid == FNID_SWITCH)) {
  3466. return NULL;
  3467. }
  3468. /*
  3469. * pti: If this is the error handler thread, don't bother any longer.
  3470. * Screen saver can go on top too.
  3471. */
  3472. ptiCurrent = PtiCurrent();
  3473. UserAssert(ptiCurrent != NULL);
  3474. if (ptiCurrent == gHardErrorHandler.pti || (ptiCurrent->ppi->W32PF_Flags & W32PF_SCREENSAVER)) {
  3475. return NULL;
  3476. }
  3477. /*
  3478. * pdesk: Leave the logon desktop alone.
  3479. * Make sure the hard error box is on this desktop
  3480. */
  3481. pdesk = ptiCurrent->rpdesk;
  3482. UserAssert(pdesk != NULL);
  3483. UserAssert(pdesk->rpwinstaParent);
  3484. UserAssert(pdesk->rpwinstaParent->pTerm);
  3485. if ((pdesk == grpdeskLogon)
  3486. || (pdesk != gHardErrorHandler.pti->rpdesk)) {
  3487. return NULL;
  3488. }
  3489. /*
  3490. * Walk the window list looking for the hard error box.
  3491. * Start searching from the current desktop's first child.
  3492. * Note that the harderror box migth not be created yet.
  3493. */
  3494. UserAssert(pdesk->pDeskInfo);
  3495. UserAssert(pdesk->pDeskInfo->spwnd);
  3496. for (pwndT = pdesk->pDeskInfo->spwnd->spwndChild;
  3497. pwndT != NULL; pwndT = pwndT->spwndNext) {
  3498. /*
  3499. * Hard error boxes are always top most.
  3500. */
  3501. if (!TestWF(pwndT, WEFTOPMOST)) {
  3502. break;
  3503. }
  3504. /*
  3505. * If this window was created by the hard error handler thread,
  3506. * then this is it.
  3507. */
  3508. if (gHardErrorHandler.pti == GETPTI(pwndT)) {
  3509. return pwndT;
  3510. }
  3511. }
  3512. return NULL;
  3513. }
  3514. /***************************************************************************\
  3515. *
  3516. * This routine maps the special HWND_* values of ppos->hwndInsertAfter,
  3517. * and returns whether or not the window's owner group should be labelled
  3518. * TOPMOST or not, or left alone.
  3519. *
  3520. * Here are the TOPMOST rules. If pwndInsertAfter is:
  3521. *
  3522. * 1. HWND_BOTTOM == (HWND)1:
  3523. *
  3524. * The group is made non-TOPMOST.
  3525. *
  3526. * 2. HWND_TOPMOST == (HWND)-1:
  3527. *
  3528. * hwndInsertAfter is set to HWND_TOP, and the group is made TOPMOST.
  3529. *
  3530. * 3. HWND_NOTOPMOST == (HWND)-2:
  3531. *
  3532. * Treated same as HWND_TOP, except that the TOPMOST bit is cleared.
  3533. * and the entire group is made non-topmost.
  3534. * Used to make a topmost window non-topmost, but still leave it at
  3535. * the top of the non-topmost pile.
  3536. * The group is not changed if the window is already non-topmost.
  3537. *
  3538. * 4. HWND_TOP == (HWND)NULL:
  3539. *
  3540. * pwndInsertAfter is set to the last TOPMOST window if pwnd
  3541. * is not itself TOPMOST. If pwnd IS TOPMOST, then pwndInsertAfter
  3542. * remains HWND_TOP.
  3543. *
  3544. * 5. A TOPMOST window:
  3545. *
  3546. * If a window is being inserted among the TOPMOST windows, then
  3547. * the group becomes topmost too, EXCEPT if it's being inserted behind
  3548. * the bottom-most topmost window: in that case the window retains
  3549. * its current TOPMOST bit.
  3550. *
  3551. * 6. A non-TOPMOST window:
  3552. *
  3553. * If a window is being inserted among non-TOPMOST windows, the group is made
  3554. * non-TOPMOST and inserted there.
  3555. *
  3556. * Whenever a group is made TOPMOST, only that window and its ownees are made
  3557. * topmost. When a group is made NOTOPMOST, the entire window is made non-topmost.
  3558. *
  3559. * This routine must NOT set SWP_NOZORDER if the topmost state is changing:
  3560. * this would prevent the topmost bits from getting toggled in ChangeStates.
  3561. *
  3562. * History:
  3563. * 04-Mar-1992 MikeKe From win31
  3564. \***************************************************************************/
  3565. int CheckTopmost(
  3566. PWINDOWPOS ppos)
  3567. {
  3568. PWND pwnd, pwndInsertAfter, pwndT;
  3569. /*
  3570. * BACKWARD COMPATIBILITY HACK
  3571. *
  3572. * If we're activating a window and Z-ordering too, we must ignore the
  3573. * specified Z order and bring the window to the top, EXCEPT in the
  3574. * following conditions:
  3575. *
  3576. * 1. The window is already active (in which case, the activation code
  3577. * will not be bringing the window to the top)
  3578. *
  3579. * 2. HWND_TOP or HWND_NOTOPMOST is specified. This allows us to
  3580. * activate and move to topmost or nontopmost at the same time.
  3581. *
  3582. * NOTE: It would have been possible to modify ActivateWindow() to
  3583. * take a flag to prevent it from ever doing the BringWindowToTop,
  3584. * thus allowing SetWindowPos() to properly honor pwndInsertBehind
  3585. * AND activation, but this change was considered too late in the
  3586. * game -- there could be problems with existing 3.1 apps, such as
  3587. * PenWin, etc.
  3588. */
  3589. pwnd = PW(ppos->hwnd);
  3590. if (!(ppos->flags & SWP_NOACTIVATE) &&
  3591. !(ppos->flags & SWP_NOZORDER) &&
  3592. (ppos->hwndInsertAfter != HWND_TOPMOST &&
  3593. ppos->hwndInsertAfter != HWND_NOTOPMOST) &&
  3594. (pwnd != GETPTI(pwnd)->pq->spwndActive)) {
  3595. ppos->hwndInsertAfter = HWND_TOP;
  3596. }
  3597. /*
  3598. * If we're not Z-ordering, don't do anything.
  3599. */
  3600. if (ppos->flags & SWP_NOZORDER) {
  3601. return CTM_NOCHANGE;
  3602. }
  3603. if (ppos->hwndInsertAfter == HWND_BOTTOM) {
  3604. return CTM_NOTOPMOST;
  3605. } else if (ppos->hwndInsertAfter == HWND_NOTOPMOST) {
  3606. /*
  3607. * If currently topmost, move to top of non-topmost list.
  3608. * Otherwise, no change.
  3609. *
  3610. * Note that we don't set SWP_NOZORDER -- we still need to
  3611. * check the TOGGLETOPMOST bits in ChangeStates()
  3612. */
  3613. if (TestWF(pwnd, WEFTOPMOST)) {
  3614. pwndT = GetLastTopMostWindow();
  3615. ppos->hwndInsertAfter = HW(pwndT);
  3616. if (ppos->hwndInsertAfter == ppos->hwnd) {
  3617. pwndT = _GetWindow(pwnd, GW_HWNDPREV);
  3618. ppos->hwndInsertAfter = HW(pwndT);
  3619. }
  3620. } else {
  3621. pwndT = _GetWindow(pwnd, GW_HWNDPREV);
  3622. ppos->hwndInsertAfter = HW(pwndT);
  3623. }
  3624. return CTM_NOTOPMOST;
  3625. } else if (ppos->hwndInsertAfter == HWND_TOPMOST) {
  3626. pwndT = GETTOPMOSTINSERTAFTER(pwnd);
  3627. if (pwndT != NULL) {
  3628. ppos->hwndInsertAfter = HW(pwndT);
  3629. } else {
  3630. ppos->hwndInsertAfter = HWND_TOP;
  3631. }
  3632. return CTM_TOPMOST;
  3633. } else if (ppos->hwndInsertAfter == HWND_TOP) {
  3634. /*
  3635. * If we're not topmost, position ourself after
  3636. * the last topmost window. Otherwise, make sure
  3637. * that no one gets in front of a hard error box.
  3638. */
  3639. if (TestWF(pwnd, WEFTOPMOST)) {
  3640. pwndT = GETTOPMOSTINSERTAFTER(pwnd);
  3641. if (pwndT != NULL) {
  3642. ppos->hwndInsertAfter = HW(pwndT);
  3643. }
  3644. return CTM_NOCHANGE;
  3645. }
  3646. /*
  3647. * Calculate the window to zorder after for this window, taking
  3648. * into account foreground status.
  3649. */
  3650. pwndInsertAfter = CalcForegroundInsertAfter(pwnd);
  3651. ppos->hwndInsertAfter = HW(pwndInsertAfter);
  3652. return CTM_NOCHANGE;
  3653. }
  3654. /*
  3655. * If we're being inserted after the last topmost window,
  3656. * then don't change the topmost status.
  3657. */
  3658. pwndT = GetLastTopMostWindow();
  3659. if (ppos->hwndInsertAfter == HW(pwndT))
  3660. return CTM_NOCHANGE;
  3661. /*
  3662. * Otherwise, if we're inserting a TOPMOST among non-TOPMOST,
  3663. * or vice versa, change the status appropriately.
  3664. */
  3665. if (TestWF(PW(ppos->hwndInsertAfter), WEFTOPMOST)) {
  3666. if (!TestWF(pwnd, WEFTOPMOST)) {
  3667. return CTM_TOPMOST;
  3668. }
  3669. pwndT = GETTOPMOSTINSERTAFTER(pwnd);
  3670. if (pwndT != NULL) {
  3671. ppos->hwndInsertAfter = HW(pwndT);
  3672. }
  3673. } else {
  3674. if (TestWF(pwnd, WEFTOPMOST))
  3675. return CTM_NOTOPMOST;
  3676. }
  3677. return CTM_NOCHANGE;
  3678. }
  3679. /***************************************************************************\
  3680. * IsOwnee(pwndOwnee, pwndOwner)
  3681. *
  3682. * Returns TRUE if pwndOwnee is owned by pwndOwner
  3683. *
  3684. *
  3685. * History:
  3686. * 04-Mar-1992 MikeKe From win31
  3687. \***************************************************************************/
  3688. BOOL IsOwnee(
  3689. PWND pwndOwnee,
  3690. PWND pwndOwner)
  3691. {
  3692. PWND pwnd;
  3693. while (pwndOwnee != NULL) {
  3694. /*
  3695. * See if pwndOwnee is a child of pwndOwner...
  3696. */
  3697. for (pwnd = pwndOwnee; pwnd != NULL; pwnd = pwnd->spwndParent) {
  3698. if (pwnd == pwndOwner)
  3699. return TRUE;
  3700. }
  3701. /*
  3702. * If the window doesn't own itself, then set the owner
  3703. * to itself.
  3704. */
  3705. pwndOwnee = (pwndOwnee->spwndOwner != pwndOwnee ?
  3706. pwndOwnee->spwndOwner : NULL);
  3707. }
  3708. return FALSE;
  3709. }
  3710. /***************************************************************************\
  3711. *
  3712. * History:
  3713. * 04-Mar-1992 MikeKe From win31
  3714. \***************************************************************************/
  3715. BOOL IsBehind(
  3716. PWND pwnd,
  3717. PWND pwndReference)
  3718. {
  3719. /*
  3720. * Starting at pwnd, move down until we reach the end of the window
  3721. * list, or until we reach pwndReference. If we encounter pwndReference,
  3722. * then pwnd is above pwndReference, so return FALSE. If we get to the
  3723. * end of the window list, pwnd is behind, so return TRUE.
  3724. */
  3725. if (pwndReference == (PWND)HWND_TOP)
  3726. return TRUE;
  3727. if (pwndReference == (PWND)HWND_BOTTOM)
  3728. return FALSE;
  3729. for ( ; pwnd != NULL; pwnd = pwnd->spwndNext) {
  3730. if (pwnd == pwndReference)
  3731. return FALSE;
  3732. }
  3733. return TRUE;
  3734. }
  3735. /***************************************************************************\
  3736. *
  3737. * Add pwnd to the SMWP. pwndChange is the "real" pwnd being repositioned
  3738. * and pwndInsertAfter is the place where it's being inserted.
  3739. *
  3740. * pwndTopInsert is the window handle where the top of the owner tree should be
  3741. * inserted. The special value of (HWND)-2 is used to indicate recursion, in
  3742. * which case newly added SWPs are added to the previous element.
  3743. *
  3744. * History:
  3745. * 04-Mar-1992 MikeKe From win31
  3746. \***************************************************************************/
  3747. PSMWP AddSelfAndOwnees(
  3748. PSMWP psmwp,
  3749. PWND pwnd,
  3750. PWND pwndChange,
  3751. PWND pwndInsertAfter,
  3752. int iTop)
  3753. {
  3754. PWND pwndChgOwnee;
  3755. PWND pwndT;
  3756. BOOL fChgOwneeInserted;
  3757. CVR *pcvr;
  3758. /*
  3759. * The general idea here is to first add our ownees, then add ourself.
  3760. * When we add our ownees though, we add them as appropriate based
  3761. * on the pwndInsertAfter parameter.
  3762. *
  3763. * Find out if any of our ownees are on a direct path between pwndChange
  3764. * and the root of the owner tree. If one is, then its Z order relative
  3765. * to its owner-siblings will be changing. If none are, then
  3766. * we want to add our ownees to the list in their current order.
  3767. */
  3768. pwndChgOwnee = pwndChange;
  3769. while (pwndChgOwnee != NULL) {
  3770. pwndT = GetRealOwner(pwndChgOwnee);
  3771. if (pwnd == pwndT)
  3772. break;
  3773. pwndChgOwnee = pwndT;
  3774. }
  3775. /*
  3776. * Now enumerate all other ownees, and insert them in their
  3777. * current order.
  3778. */
  3779. fChgOwneeInserted = FALSE;
  3780. pwndT = NULL;
  3781. while ((pwndT = NextOwnedWindow(pwndT, pwnd, pwnd->spwndParent)) != NULL) {
  3782. /*
  3783. * If these siblings are to be reordered, compare the sibling's
  3784. * current Z order with pwndInsertAfter.
  3785. */
  3786. if (pwndChgOwnee == NULL) {
  3787. /*
  3788. * No Z change for our ownees: just add them in their current order
  3789. */
  3790. psmwp = AddSelfAndOwnees(psmwp, pwndT, NULL, NULL, iTop);
  3791. } else {
  3792. /*
  3793. * If we haven't already inserted the ChgOwnee, and the
  3794. * enumerated owner-sibling is behind pwndInsertAfter, then
  3795. * add ChgOwnee.
  3796. */
  3797. if (!fChgOwneeInserted && IsBehind(pwndT, pwndInsertAfter)) {
  3798. psmwp = AddSelfAndOwnees(psmwp,
  3799. pwndChgOwnee,
  3800. pwndChange,
  3801. pwndInsertAfter,
  3802. iTop);
  3803. if (psmwp == NULL)
  3804. return NULL;
  3805. fChgOwneeInserted = TRUE;
  3806. }
  3807. if (pwndT != pwndChgOwnee) {
  3808. /*
  3809. * Not the change ownee: add it in its current order.
  3810. */
  3811. psmwp = AddSelfAndOwnees(psmwp, pwndT, NULL, NULL, iTop);
  3812. }
  3813. }
  3814. if (psmwp == NULL)
  3815. return NULL;
  3816. }
  3817. /*
  3818. * If we never added the change ownee in the loop, add it now.
  3819. */
  3820. if ((pwndChgOwnee != NULL) && !fChgOwneeInserted) {
  3821. psmwp = AddSelfAndOwnees(psmwp,
  3822. pwndChgOwnee,
  3823. pwndChange,
  3824. pwndInsertAfter,
  3825. iTop);
  3826. if (psmwp == NULL)
  3827. return NULL;
  3828. }
  3829. /*
  3830. * Finally, add this window to the list.
  3831. */
  3832. psmwp = _DeferWindowPos(psmwp, pwnd, NULL,
  3833. 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
  3834. if (psmwp == NULL)
  3835. return NULL;
  3836. /*
  3837. * If we aren't inserting the topmost entry,
  3838. * link this entry to the previous one.
  3839. * The topmost entry will get set by our caller.
  3840. */
  3841. if (iTop != psmwp->ccvr - 1) {
  3842. pcvr = &psmwp->acvr[psmwp->ccvr - 1];
  3843. pcvr->pos.hwndInsertAfter = (pcvr - 1)->pos.hwnd;
  3844. }
  3845. return psmwp;
  3846. }
  3847. /***************************************************************************\
  3848. *
  3849. * ZOrderByOwner2 - Add the current window and all it owns to the SWP list,
  3850. * and arrange them in the new Z ordering. Called only if the Z order of the
  3851. * window is changing.
  3852. *
  3853. * History:
  3854. * 04-Mar-1992 MikeKe From win31
  3855. \***************************************************************************/
  3856. PSMWP ZOrderByOwner2(
  3857. PSMWP psmwp,
  3858. int iTop)
  3859. {
  3860. PWND pwndT;
  3861. PWND pwndOwnerRoot;
  3862. PWND pwndTopInsert;
  3863. PWINDOWPOS ppos;
  3864. PWND pwnd;
  3865. PWND pwndInsertAfter;
  3866. BOOL fHasOwnees;
  3867. ppos = &psmwp->acvr[iTop].pos;
  3868. /*
  3869. * If inside message box processing, not Z ordering,
  3870. * or if SWP_NOOWNERZORDER specified, all done.
  3871. */
  3872. // LATER 04-Mar-1992 MikeKe
  3873. // do we have a substitue for fMessageBox
  3874. if ((ppos->flags & SWP_NOZORDER) ||
  3875. (ppos->flags & SWP_NOOWNERZORDER)) {
  3876. return psmwp;
  3877. }
  3878. pwnd = PW(ppos->hwnd);
  3879. pwndInsertAfter = PWInsertAfter(ppos->hwndInsertAfter);
  3880. fHasOwnees = (NextOwnedWindow(NULL, pwnd, pwnd->spwndParent) != NULL);
  3881. /*
  3882. * If the window isn't owned, and it doesn't own any other window,
  3883. * do nothing.
  3884. */
  3885. if (!pwnd->spwndOwner && !fHasOwnees)
  3886. return psmwp;
  3887. /*
  3888. * Find the unowned window to start building the tree from.
  3889. * This is easy: just zip upwards until we find a window with no owner.
  3890. */
  3891. pwndOwnerRoot = pwndT = pwnd;
  3892. while ((pwndT = GetRealOwner(pwndT)) != NULL)
  3893. pwndOwnerRoot = pwndT;
  3894. /*
  3895. * We need to calculate what pwndInsertAfter should be for
  3896. * the first (topmost) window of the SWP list.
  3897. *
  3898. * If pwndInsertAfter is part of the owner tree we'll be building,
  3899. * then we want to reorder windows within the owner group, so the
  3900. * entire group should maintain it's relative order.
  3901. *
  3902. * If pwndInsertAfter is part of another owner tree, then we want
  3903. * the whole group relative to that.
  3904. *
  3905. * If pwndInsertAfter is HWND_BOTTOM, then we want the whole
  3906. * group to go to the bottom, so we position it relative to
  3907. * the bottom most window that is not part of the tree. We also
  3908. * want to put pwnd on the bottom relative to its owner siblings.
  3909. *
  3910. * If pwndInsertAfter is HWND_TOP, then bring the whole group
  3911. * to the top, as well as bringing pwnd to the top relative to its
  3912. * owner siblings.
  3913. *
  3914. * Assume the topmost of group is same as topmost
  3915. * (true for all cases except where rearranging subtree of group)
  3916. */
  3917. pwndTopInsert = pwndInsertAfter;
  3918. if (pwndInsertAfter == (PWND)HWND_TOP) {
  3919. /*
  3920. * Bring the whole group to the top: nothing fancy to do.
  3921. */
  3922. } else if (pwndInsertAfter == (PWND)HWND_BOTTOM) {
  3923. /*
  3924. * Put the whole group on the bottom. pwndTopInsert should
  3925. * be the bottommost window unowned by pwndOwnerRoot.
  3926. */
  3927. for (pwndT = pwnd->spwndParent->spwndChild;
  3928. (pwndT != NULL) && !TestWF(pwndT, WFBOTTOMMOST); pwndT = pwndT->spwndNext) {
  3929. /*
  3930. * If it's not owned, then this is the bottommost so far.
  3931. */
  3932. if (!IsOwnee(pwndT, pwndOwnerRoot))
  3933. pwndTopInsert = pwndT;
  3934. }
  3935. /*
  3936. * If there were no other windows not in our tree,
  3937. * then there is no Z ordering change to be done.
  3938. */
  3939. if (pwndTopInsert == (PWND)HWND_BOTTOM)
  3940. ppos->flags |= SWP_NOZORDER;
  3941. } else {
  3942. /*
  3943. * pwndInsertAfter is a window. Compute pwndTopInsert
  3944. */
  3945. if (IsOwnee(pwndInsertAfter, pwndOwnerRoot)) {
  3946. /*
  3947. * SPECIAL CASE: If we do not own any windows, and we're
  3948. * being moved within our owner group in such a way that
  3949. * we remain above our owner, then no other windows will
  3950. * be moving with us, and we can just exit
  3951. * without building our tree. This can save a LOT of
  3952. * extra work, especially with the MS apps CBT tutorials,
  3953. * which do this kind of thing a lot.
  3954. */
  3955. if (!fHasOwnees) {
  3956. /*
  3957. * Make sure we will still be above our owner by searching
  3958. * for our owner starting from pwndInsertAfter. If we
  3959. * find our owner, then pwndInsertAfter is above it.
  3960. */
  3961. for (pwndT = pwndInsertAfter; pwndT != NULL;
  3962. pwndT = pwndT->spwndNext) {
  3963. if (pwndT == pwnd->spwndOwner)
  3964. return psmwp;
  3965. }
  3966. }
  3967. /*
  3968. * Part of same group: Find out which window the topmost
  3969. * of the group is currently inserted behind.
  3970. */
  3971. pwndTopInsert = (PWND)HWND_TOP;
  3972. for (pwndT = pwnd->spwndParent->spwndChild; pwndT != NULL;
  3973. pwndT = pwndT->spwndNext) {
  3974. if (IsOwnee(pwndT, pwndOwnerRoot))
  3975. break;
  3976. pwndTopInsert = pwndT;
  3977. }
  3978. }
  3979. }
  3980. /*
  3981. * Okay, now go recursively build the owned window list...
  3982. */
  3983. if (!(ppos->flags & SWP_NOZORDER)) {
  3984. /*
  3985. * First "delete" the last entry (the one we're sorting with)
  3986. */
  3987. psmwp->ccvr--;
  3988. psmwp = AddSelfAndOwnees(psmwp,
  3989. pwndOwnerRoot,
  3990. pwnd,
  3991. pwndInsertAfter,
  3992. iTop);
  3993. /*
  3994. * Now set the place where the whole group is going.
  3995. */
  3996. if (psmwp != NULL)
  3997. psmwp->acvr[iTop].pos.hwndInsertAfter = HW(pwndTopInsert);
  3998. }
  3999. return psmwp;
  4000. }
  4001. /***************************************************************************\
  4002. * TrackBackground
  4003. *
  4004. * Adjust zorder if we're crossing a TOPMOST boundary. Make sure that a
  4005. * non-topmost window in a background thread doesn't come in front of
  4006. * non-topmost windows in the foreground thread.
  4007. \***************************************************************************/
  4008. BOOL TrackBackground(WINDOWPOS *ppos, PWND pwndPrev, PWND pwnd)
  4009. {
  4010. PWND pwndT;
  4011. if (pwndPrev == NULL)
  4012. return FALSE;
  4013. /*
  4014. * Is this window foreground? If so, let it go. For wow apps,
  4015. * check to see if any thread of the process is foreground.
  4016. */
  4017. if (GETPTI(pwnd)->TIF_flags & TIF_16BIT) {
  4018. if (gptiForeground == NULL)
  4019. return FALSE;
  4020. if (GETPTI(pwnd)->ppi == gptiForeground->ppi)
  4021. return FALSE;
  4022. } else {
  4023. if (GETPTI(pwnd) == gptiForeground)
  4024. return FALSE;
  4025. }
  4026. /*
  4027. * Make sure the previous window is either staying or becoming
  4028. * topmost. If not, continue: no top most boundary.
  4029. */
  4030. if (!FSwpTopmost(pwndPrev))
  4031. return FALSE;
  4032. /*
  4033. * Is the current window already top-most? If so then don't
  4034. * calculate a special insert after. If we don't check for
  4035. * this, then pwnd's insert after may be calculated as what
  4036. * pwnd already is, if pwnd is the last top most window. That
  4037. * would cause window links to get corrupted.
  4038. */
  4039. if (TestWF(pwnd, WEFTOPMOST))
  4040. return FALSE;
  4041. /*
  4042. * Doing this assign prevents this routine from being called
  4043. * twice, since HW() is a conditional macro.
  4044. */
  4045. pwndT = CalcForegroundInsertAfter(pwnd);
  4046. ppos->hwndInsertAfter = HW(pwndT);
  4047. return TRUE;
  4048. }
  4049. /***************************************************************************\
  4050. * TrackZorder, TrackZorderHelper
  4051. *
  4052. * Set up hwndInsertAfter links to point to the previous window in the
  4053. * CVR array and partition them in TOPMOST and non-TOPMOST chains.
  4054. *
  4055. * 05/16/97 vadimg created
  4056. \***************************************************************************/
  4057. VOID TrackZorderHelper(
  4058. WINDOWPOS *ppos,
  4059. HWND *phwnd)
  4060. {
  4061. /*
  4062. * phwnd (hwndTopmost or hwndRegular) have been initialized to NULL before
  4063. * the beginning of the scan. This way the first hwndInsertAfter that
  4064. * we process remains with the value that was previously calculated.
  4065. */
  4066. if (*phwnd != NULL) {
  4067. #if DBG
  4068. if (ppos->hwndInsertAfter != *phwnd) {
  4069. RIPMSG0(RIP_WARNING, "TrackZorder: modified hwndInsertAfter");
  4070. }
  4071. #endif
  4072. ppos->hwndInsertAfter = *phwnd;
  4073. }
  4074. *phwnd = ppos->hwnd;
  4075. }
  4076. PWND TrackZorder(
  4077. WINDOWPOS* ppos,
  4078. PWND pwndPrev,
  4079. HWND *phwndTop,
  4080. HWND *phwndReg)
  4081. {
  4082. PWND pwnd = PW(ppos->hwnd);
  4083. if (pwnd == NULL)
  4084. return NULL;
  4085. if (TrackBackground(ppos, pwndPrev, pwnd)) {
  4086. *phwndReg = ppos->hwnd;
  4087. } else if (FSwpTopmost(pwnd)) {
  4088. TrackZorderHelper(ppos, phwndTop);
  4089. } else {
  4090. TrackZorderHelper(ppos, phwndReg);
  4091. }
  4092. return pwnd;
  4093. }
  4094. /***************************************************************************\
  4095. * ZOrderByOwner
  4096. *
  4097. * This routine Z-Orders windows by their owners.
  4098. *
  4099. * LATER
  4100. * This code currently assumes that all of the window handles are valid
  4101. *
  4102. * History:
  4103. * 04-Mar-1992 MikeKe from win31
  4104. \***************************************************************************/
  4105. PSMWP ZOrderByOwner(
  4106. PSMWP psmwp)
  4107. {
  4108. int i;
  4109. PWND pwnd;
  4110. PWND pwndT;
  4111. WINDOWPOS pos;
  4112. PTHREADINFO ptiT;
  4113. HRGN hrgnClipSave;
  4114. /*
  4115. * Some of the windows in the SMWP list may be NULL at ths point
  4116. * (removed because they'll be handled by their creator's thread)
  4117. * so we've got to look for the first non-NULL window before we can
  4118. * execute some of the tests below. FindValidWindowPos returns NULL if
  4119. * the list has no valid windows in it.
  4120. */
  4121. if (FindValidWindowPos(psmwp) == NULL)
  4122. return psmwp;
  4123. /*
  4124. * For each SWP in the array, move it to the end of the array
  4125. * and generate its entire owner tree in sorted order.
  4126. */
  4127. for (i = psmwp->ccvr; i-- != 0; ) {
  4128. int iScan;
  4129. int iTop;
  4130. int code;
  4131. WINDOWPOS *ppos;
  4132. HWND hwndTopmost;
  4133. HWND hwndRegular;
  4134. if (psmwp->acvr[0].pos.hwnd == NULL)
  4135. continue;
  4136. code = CheckTopmost(&psmwp->acvr[0].pos);
  4137. /*
  4138. * Make a local copy for later...
  4139. *
  4140. * Why don't we copy all CVR fields? This seems pretty hard to maintain.
  4141. * Perhaps because most of them haven't been used yet....
  4142. *
  4143. */
  4144. pos = psmwp->acvr[0].pos;
  4145. ptiT = psmwp->acvr[0].pti;
  4146. hrgnClipSave = psmwp->acvr[0].hrgnClip;
  4147. /*
  4148. * Move the CVR to the end (if it isn't already)
  4149. */
  4150. iTop = psmwp->ccvr - 1;
  4151. if (iTop != 0) {
  4152. RtlCopyMemory(&psmwp->acvr[0],
  4153. &psmwp->acvr[1],
  4154. iTop * sizeof(CVR));
  4155. psmwp->acvr[iTop].pos = pos;
  4156. psmwp->acvr[iTop].pti = ptiT;
  4157. psmwp->acvr[iTop].hrgnClip = hrgnClipSave;
  4158. }
  4159. if ((psmwp = ZOrderByOwner2(psmwp, iTop)) == NULL)
  4160. break;
  4161. /*
  4162. * Deal with WEFTOPMOST bits. If we're SETTING the TOPMOST bits,
  4163. * we want to set them for this window and
  4164. * all its owned windows -- the owners stay unchanged. If we're
  4165. * CLEARING, though, we need to enumerate ALL the windows, since
  4166. * they all need to lose the topmost bit when one loses it.
  4167. *
  4168. * Note that since a status change doesn't necessarily mean that
  4169. * the true Z order of the windows have changed, so ZOrderByOwner2
  4170. * may not have enumerated all of the owned and owner windows.
  4171. * So, we enumerate them separately here.
  4172. */
  4173. if (code != CTM_NOCHANGE) {
  4174. PWND pwndRoot = PW(pos.hwnd);
  4175. #if DBG
  4176. PWND pwndOriginal = pwndRoot;
  4177. #endif
  4178. /*
  4179. * Make sure we're z-ordering this window, or settting topmost
  4180. * is bad.
  4181. */
  4182. UserAssert(!(pos.flags & SWP_NOZORDER));
  4183. /*
  4184. * If we're CLEARING the topmost, then we want to enumerate
  4185. * the owners and ownees, so start our enumeration at the root.
  4186. */
  4187. if (code == CTM_NOTOPMOST) {
  4188. while (pwnd = GetRealOwner(pwndRoot))
  4189. pwndRoot = pwnd;
  4190. }
  4191. #if DBG
  4192. if ((pos.flags & SWP_NOOWNERZORDER)
  4193. && ((pwndOriginal != pwndRoot)
  4194. || (NextOwnedWindow(NULL, pwndRoot, pwndRoot->spwndParent) != NULL))) {
  4195. /*
  4196. * We're not doing owner z-order but pwndOriginal has an owner and/or
  4197. * owns some windows. The problem is, SetTopMost always affects the
  4198. * whole owner/ownee group. So we might end up with WFTOGGLETOPMOST
  4199. * windows that won't be z-ordered. It has always been like that.
  4200. */
  4201. RIPMSG2(RIP_WARNING, "ZOrderByOwner: Topmost change while using SWP_NOOWNERZORDER."
  4202. " pwndRoot:%p pwndOriginal:%p",
  4203. pwndRoot, pwndOriginal);
  4204. }
  4205. #endif
  4206. SetTopmost(pwndRoot, code == CTM_TOPMOST);
  4207. }
  4208. /*
  4209. * Now scan the list forwards (from the bottom of the
  4210. * owner tree towards the root) looking for the window
  4211. * we were positioning originally (it may have been in
  4212. * the middle of the owner tree somewhere). Update the
  4213. * window pos structure stored there with the original
  4214. * information (though the z-order info is retained from
  4215. * the sort).
  4216. */
  4217. pwnd = NULL;
  4218. hwndTopmost = hwndRegular = NULL;
  4219. for (iScan = iTop; iScan != psmwp->ccvr; iScan++) {
  4220. ppos = &psmwp->acvr[iScan].pos;
  4221. if (ppos->hwnd == pos.hwnd) {
  4222. ppos->x = pos.x;
  4223. ppos->y = pos.y;
  4224. ppos->cx = pos.cx;
  4225. ppos->cy = pos.cy;
  4226. ppos->flags ^= ((ppos->flags ^ pos.flags) & ~SWP_NOZORDER);
  4227. psmwp->acvr[iScan].hrgnClip = hrgnClipSave;
  4228. }
  4229. pwndT = pwnd;
  4230. pwnd = TrackZorder(ppos, pwndT, &hwndTopmost, &hwndRegular);
  4231. }
  4232. }
  4233. return psmwp;
  4234. }
  4235. /***************************************************************************\
  4236. * xxxEndDeferWindowPosEx
  4237. *
  4238. *
  4239. * History:
  4240. * 11-Jul-1991 DarrinM Ported from Win 3.1 sources.
  4241. \***************************************************************************/
  4242. BOOL xxxEndDeferWindowPosEx(
  4243. PSMWP psmwp,
  4244. BOOL fAsync)
  4245. {
  4246. PWND pwndNewActive;
  4247. PWND pwndParent;
  4248. PWND pwndActive;
  4249. PWND pwndActivePrev;
  4250. HWND hwndNewActive;
  4251. PWINDOWPOS pwp;
  4252. BOOL fClearBits;
  4253. BOOL fSyncPaint;
  4254. UINT cVisWindowsPrev;
  4255. PTHREADINFO ptiCurrent = PtiCurrent();
  4256. TL tlpwndNewActive;
  4257. TL tlpwndParent;
  4258. TL tlcuSMWP;
  4259. BOOL fForegroundPrev;
  4260. UserAssert(IsWinEventNotifyDeferredOK());
  4261. DBGCheckSMWP(psmwp);
  4262. if (psmwp->bHandle) {
  4263. CheckLock(psmwp);
  4264. }
  4265. /*
  4266. * Validate the window pos structures and find a window to activate.
  4267. */
  4268. if ((psmwp->ccvr != 0) && ValidateSmwp(psmwp, &fSyncPaint)) {
  4269. if ((pwp = FindValidWindowPos(psmwp)) == NULL)
  4270. goto lbFinished;
  4271. /*
  4272. * Make sure to stop at the mother desktop window. In Win95
  4273. * a SetWindowPos() on a desktop window will have a NULL parent
  4274. * window. This is not true in NT, but our mother desktop
  4275. * window does have a NULL rpdesk, so check it too.
  4276. */
  4277. UserAssert(PW(pwp->hwnd));
  4278. pwndParent = PW(pwp->hwnd)->spwndParent;
  4279. if (pwndParent == NULL || pwndParent->head.rpdesk == NULL)
  4280. goto lbFinished;
  4281. /*
  4282. * Usually all window positioning happens synchronously across threads.
  4283. * This is because apps expect that behavior now - if it was async,
  4284. * callers could not expect the state to be set once the api returned.
  4285. * This is not the semantics of SetWindowPos(). The downside of this
  4286. * synchronicity is that a SetWindowPos() on an hwnd created by another
  4287. * thread will cause the caller to wait for that thread - even if that
  4288. * thread is hung. That's what you get.
  4289. *
  4290. * We don't want task manager to hang though, no matter who else is
  4291. * hung, so when taskman calls, it calls a special entry point for
  4292. * tiling / cascading, which does SetWindowPos() asynchronously -
  4293. * by posting an event in each thread's queue that makes it set its
  4294. * own window position - that way if the thread is hung, who cares -
  4295. * it doesn't effect taskman.
  4296. *
  4297. * Do async window pos positioning before zorder by owner so that
  4298. * we retain any cross thread ownership relationships synchronously.
  4299. */
  4300. if (fAsync) {
  4301. AsyncWindowPos(psmwp);
  4302. }
  4303. /*
  4304. * If needed, Z order the windows by owner.
  4305. * This may grow the SMWP, if new CVRs are added.
  4306. */
  4307. if (pwndParent == PWNDDESKTOP(pwndParent)) {
  4308. if ((psmwp = ZOrderByOwner(psmwp)) == NULL) {
  4309. return FALSE;
  4310. } else if (fAsync) {
  4311. if (!ValidateSmwp(psmwp, &fSyncPaint)) {
  4312. goto lbFinished;
  4313. }
  4314. /*
  4315. * ZOrderByOwner() could possibly add other thread windows to the
  4316. * list. Filter them again else we would hung.
  4317. */
  4318. AsyncWindowPos(psmwp);
  4319. }
  4320. }
  4321. ThreadLockAlwaysWithPti(ptiCurrent, pwndParent, &tlpwndParent);
  4322. ThreadLockPoolCleanup(ptiCurrent, psmwp, &tlcuSMWP, DestroySMWP);
  4323. /*
  4324. * Calc new window positions.
  4325. */
  4326. if (xxxCalcValidRects(psmwp, &hwndNewActive)) {
  4327. int i;
  4328. pwndNewActive = RevalidateHwnd(hwndNewActive);
  4329. ThreadLockWithPti(ptiCurrent, pwndNewActive, &tlpwndNewActive);
  4330. cVisWindowsPrev = ptiCurrent->cVisWindows;
  4331. fForegroundPrev = (ptiCurrent == gptiForeground);
  4332. /*
  4333. * The call to zzzBltValidBits will leave the critical section
  4334. * if there are any notifications to make.
  4335. */
  4336. UserAssert(IsWinEventNotifyDeferredOK());
  4337. if (!zzzBltValidBits(psmwp))
  4338. fSyncPaint = FALSE;
  4339. UserAssert(IsWinEventNotifyDeferredOK());
  4340. if (psmwp->bShellNotify) {
  4341. for (i = psmwp->ccvr; i-- != 0; ) {
  4342. /*
  4343. * Loop through the windows, looking for notifications.
  4344. */
  4345. if (0 == (psmwp->acvr[i].pos.flags & SWP_NOTIFYALL))
  4346. continue;
  4347. if (psmwp->acvr[i].pos.flags & SWP_NOTIFYCREATE) {
  4348. PostShellHookMessages(HSHELL_WINDOWCREATED,
  4349. (LPARAM)psmwp->acvr[i].pos.hwnd);
  4350. xxxCallHook(HSHELL_WINDOWCREATED,
  4351. (WPARAM)psmwp->acvr[i].pos.hwnd,
  4352. (LPARAM)0,
  4353. WH_SHELL);
  4354. }
  4355. if (psmwp->acvr[i].pos.flags & SWP_NOTIFYDESTROY) {
  4356. PostShellHookMessages(HSHELL_WINDOWDESTROYED,
  4357. (LPARAM)psmwp->acvr[i].pos.hwnd);
  4358. xxxCallHook(HSHELL_WINDOWDESTROYED,
  4359. (WPARAM)psmwp->acvr[i].pos.hwnd,
  4360. (LPARAM)0,
  4361. WH_SHELL);
  4362. }
  4363. if (psmwp->acvr[i].pos.flags & SWP_NOTIFYACTIVATE) {
  4364. PWND pwnd = RevalidateHwnd(psmwp->acvr[i].pos.hwnd);
  4365. if (pwnd != NULL){
  4366. TL tlpwnd;
  4367. ThreadLockAlwaysWithPti(ptiCurrent, pwnd, &tlpwnd);
  4368. xxxSetTrayWindow(pwnd->head.rpdesk, pwnd, NULL);
  4369. ThreadUnlock(&tlpwnd);
  4370. }
  4371. }
  4372. if (psmwp->acvr[i].pos.flags & SWP_NOTIFYFS) {
  4373. xxxSetTrayWindow(ptiCurrent->rpdesk, STW_SAME, NULL);
  4374. }
  4375. }
  4376. }
  4377. /*
  4378. * If this process went from some windows to no windows visible
  4379. * and it was in the foreground, then let its next activate
  4380. * come to the foreground.
  4381. */
  4382. if (fForegroundPrev && cVisWindowsPrev && !ptiCurrent->cVisWindows) {
  4383. ptiCurrent->TIF_flags |= TIF_ALLOWFOREGROUNDACTIVATE;
  4384. TAGMSG1(DBGTAG_FOREGROUND, "xxxEndDeferWindowPosEx set TIF %#p", ptiCurrent);
  4385. /*
  4386. * Also if any apps were in the middle of starting when
  4387. * this happened, allow them to foreground activate again.
  4388. */
  4389. RestoreForegroundActivate();
  4390. }
  4391. /*
  4392. * Deal with any activation...
  4393. */
  4394. fClearBits = FALSE;
  4395. if (pwndNewActive != NULL) {
  4396. fClearBits = xxxSwpActivate(pwndNewActive);
  4397. }
  4398. /*
  4399. * Now draw frames and erase backgrounds of all the windows
  4400. * involved.
  4401. */
  4402. UserAssert(pwndParent);
  4403. if (fSyncPaint) {
  4404. xxxDoSyncPaint(pwndParent, DSP_ENUMCLIPPEDCHILDREN);
  4405. }
  4406. ThreadUnlock(&tlpwndNewActive);
  4407. /*
  4408. * If SwpActivate() set the NONCPAINT bits, clear them now.
  4409. */
  4410. if (fClearBits) {
  4411. if (pwndActive = ptiCurrent->pq->spwndActive) {
  4412. ClrWF(pwndActive, WFNONCPAINT);
  4413. }
  4414. if (pwndActivePrev = ptiCurrent->pq->spwndActivePrev) {
  4415. ClrWF(pwndActivePrev, WFNONCPAINT);
  4416. }
  4417. }
  4418. /*
  4419. * Send WM_WINDOWPOSCHANGED messages
  4420. */
  4421. xxxSendChangedMsgs(psmwp);
  4422. }
  4423. ThreadUnlockPoolCleanup(ptiCurrent, &tlcuSMWP);
  4424. ThreadUnlock(&tlpwndParent);
  4425. }
  4426. lbFinished:
  4427. /*
  4428. * All done. Free everything up and return.
  4429. */
  4430. DestroySMWP(psmwp);
  4431. return TRUE;
  4432. }
  4433. /***************************************************************************\
  4434. * IncVisWindows
  4435. * DecVisWindows
  4436. *
  4437. * These routines deal with incrementing/decrementing the visible windows
  4438. * on the thread.
  4439. *
  4440. \***************************************************************************/
  4441. #if DBG
  4442. BOOL gfVisVerify = FALSE;
  4443. VOID VerifycVisWindows(
  4444. PWND pwnd)
  4445. {
  4446. BOOL fShowMeTheWindows = FALSE;
  4447. PTHREADINFO pti = GETPTI(pwnd);
  4448. PWND pwndNext;
  4449. UINT uVisWindows = 0;
  4450. if (!gfVisVerify) {
  4451. return;
  4452. }
  4453. /*
  4454. * Make sure the count makes sense
  4455. */
  4456. if ((int)pti->cVisWindows < 0) {
  4457. RIPMSG0(RIP_ERROR, "VerifycVisWindows: pti->cVisWindows underflow!");
  4458. fShowMeTheWindows = TRUE;
  4459. }
  4460. /*
  4461. * This window might be owned by a desktop-less service
  4462. */
  4463. if (pti->rpdesk == NULL || (pti->TIF_flags & TIF_SYSTEMTHREAD)) {
  4464. return;
  4465. }
  4466. /*
  4467. * Child windows don't affect cVisWindows
  4468. */
  4469. if (!FTopLevel(pwnd)) {
  4470. return;
  4471. }
  4472. ShowMeTheWindows:
  4473. /*
  4474. * We're going to count all the windows owned by this pti
  4475. * that should be included in cVisWindows.
  4476. */
  4477. pwndNext = pti->rpdesk->pDeskInfo->spwnd;
  4478. /*
  4479. * If this is a top level window, start with the first child.
  4480. * If not, it should be a desktop thread window.
  4481. */
  4482. if (pwndNext == pwnd->spwndParent) {
  4483. pwndNext = pwndNext->spwndChild;
  4484. } else if (pwndNext->spwndParent != pwnd->spwndParent) {
  4485. RIPMSG1(RIP_WARNING, "VerifycVisWindows: Non top level window:%#p", pwnd);
  4486. return;
  4487. }
  4488. if (fShowMeTheWindows) {
  4489. RIPMSG1(RIP_WARNING, "VerifycVisWindows: Start window walk at:%#p", pwndNext);
  4490. }
  4491. /*
  4492. * Count the visble-not-minimized windows owned by this pti.
  4493. */
  4494. while (pwndNext != NULL) {
  4495. if (pti == GETPTI(pwndNext)) {
  4496. if (fShowMeTheWindows) {
  4497. RIPMSG1(RIP_WARNING, "VerifycVisWindows: pwndNext:%#p", pwndNext);
  4498. }
  4499. if (!TestWF(pwndNext, WFMINIMIZED)
  4500. && TestWF(pwndNext, WFVISIBLE)) {
  4501. uVisWindows++;
  4502. if (fShowMeTheWindows) {
  4503. RIPMSG1(RIP_WARNING, "VerifycVisWindows: Counted:%#p", pwndNext);
  4504. }
  4505. }
  4506. }
  4507. pwndNext = pwndNext->spwndNext;
  4508. }
  4509. /*
  4510. * It must match.
  4511. */
  4512. if (pti->cVisWindows != uVisWindows) {
  4513. RIPMSG2(RIP_WARNING, "VerifycVisWindows: pti->cVisWindows:%#lx. uVisWindows:%#lx",
  4514. pti->cVisWindows, uVisWindows);
  4515. /*
  4516. * Disable going through the list and make the error into a warning.
  4517. * There are many loopholes as to how the cVisWindow count may get
  4518. * messed up. See bug 109807.
  4519. */
  4520. fShowMeTheWindows = TRUE;
  4521. if (!fShowMeTheWindows) {
  4522. fShowMeTheWindows = TRUE;
  4523. uVisWindows = 0;
  4524. goto ShowMeTheWindows;
  4525. }
  4526. }
  4527. }
  4528. #endif
  4529. /***************************************************************************\
  4530. * FVisCountable
  4531. *
  4532. * Desktops and top-level i.e. whose parent is the desktop) non-minimized
  4533. * windows should be counted in the per-thread visible window counts.
  4534. \***************************************************************************/
  4535. BOOL FVisCountable(
  4536. PWND pwnd)
  4537. {
  4538. if (!TestWF(pwnd, WFDESTROYED)) {
  4539. if ((GETFNID(pwnd) == FNID_DESKTOP) ||
  4540. (FTopLevel(pwnd) && !TestWF(pwnd, WFMINIMIZED))) {
  4541. return TRUE;
  4542. }
  4543. }
  4544. return FALSE;
  4545. }
  4546. /***************************************************************************\
  4547. * IncVisWindows
  4548. *
  4549. \***************************************************************************/
  4550. VOID IncVisWindows(
  4551. PWND pwnd)
  4552. {
  4553. if (FVisCountable(pwnd)) {
  4554. GETPTI(pwnd)->cVisWindows++;
  4555. }
  4556. if (TestWF(pwnd, WEFPREDIRECTED)) {
  4557. gnVisibleRedirectedCount++;
  4558. if (gnVisibleRedirectedCount == 1) {
  4559. InternalSetTimer(gTermIO.spwndDesktopOwner,
  4560. IDSYS_LAYER,
  4561. 100,
  4562. xxxSystemTimerProc,
  4563. TMRF_SYSTEM | TMRF_PTIWINDOW);
  4564. }
  4565. }
  4566. #if DBG
  4567. if (!ISTS()) {
  4568. VerifycVisWindows(pwnd);
  4569. }
  4570. #endif
  4571. }
  4572. /***************************************************************************\
  4573. * cDecVis
  4574. *
  4575. * An inline that allows debug code to decrement the vis window count
  4576. * without doing verification right away. Also alled by DecVisWindows
  4577. * to do the actual work.
  4578. \***************************************************************************/
  4579. __inline VOID cDecVis(
  4580. PWND pwnd)
  4581. {
  4582. UserAssert(pwnd != NULL);
  4583. if (FVisCountable(pwnd)) {
  4584. GETPTI(pwnd)->cVisWindows--;
  4585. }
  4586. if (TestWF(pwnd, WEFPREDIRECTED)) {
  4587. if (gnVisibleRedirectedCount > 0) {
  4588. gnVisibleRedirectedCount--;
  4589. if (gnVisibleRedirectedCount == 0) {
  4590. _KillSystemTimer(gTermIO.spwndDesktopOwner, IDSYS_LAYER);
  4591. }
  4592. }
  4593. }
  4594. }
  4595. /***************************************************************************\
  4596. * DecVisWindows
  4597. *
  4598. \***************************************************************************/
  4599. VOID DecVisWindows(
  4600. PWND pwnd)
  4601. {
  4602. cDecVis(pwnd);
  4603. #if DBG
  4604. if (!ISTS()) {
  4605. VerifycVisWindows(pwnd);
  4606. }
  4607. #endif
  4608. }
  4609. /***************************************************************************\
  4610. * SetMiminize
  4611. *
  4612. * This routine must be used to flip the WS_MIMIMIZE style bit.
  4613. * It adjusts the cVisWindows count if appropriate.
  4614. *
  4615. * 06/06/96 GerardoB Created
  4616. \***************************************************************************/
  4617. VOID SetMinimize(
  4618. PWND pwnd,
  4619. UINT uFlags)
  4620. {
  4621. /*
  4622. * Note that Dec and IncVisWindows check the WFMINIMIZED flag, so the order
  4623. * in which we set/clear the flag and call these functions is important.
  4624. *
  4625. * If the window is not WFVISIBLE, cVisWindows must not change.
  4626. */
  4627. if (uFlags & SMIN_SET) {
  4628. UserAssert(!TestWF(pwnd, WFMINIMIZED));
  4629. if (TestWF(pwnd, WFVISIBLE)) {
  4630. /*
  4631. * Decrement the count because the window is not minimized
  4632. * and visible, and we're about to mark it as minimized.
  4633. */
  4634. #if DBG
  4635. cDecVis(pwnd);
  4636. #else
  4637. DecVisWindows(pwnd);
  4638. #endif
  4639. }
  4640. SetWF(pwnd, WFMINIMIZED);
  4641. #if DBG
  4642. VerifycVisWindows(pwnd);
  4643. #endif
  4644. } else {
  4645. UserAssert(TestWF(pwnd, WFMINIMIZED));
  4646. ClrWF(pwnd, WFMINIMIZED);
  4647. if (TestWF(pwnd, WFVISIBLE)) {
  4648. /*
  4649. * Increment the count because the window is visible
  4650. * and it's no longer marked as minimized.
  4651. */
  4652. IncVisWindows(pwnd);
  4653. }
  4654. }
  4655. }
  4656. /***************************************************************************\
  4657. * SetVisible
  4658. *
  4659. * This routine must be used to set or clear the WS_VISIBLE style bit.
  4660. * It also handles the setting or clearing of the WF_TRUEVIS bit.
  4661. *
  4662. * Note that we don't check if the window is already in the (in)visible
  4663. * state before setting/clearing the WFVISIBLE bit and calling
  4664. * Inc/DecVisWindows. If the window is already in the given state and
  4665. * someone calls SetVisible to change into the same state, the VisCount
  4666. * will get out of sync. This could happen, for example, if someone
  4667. * passed two SWP_SHOWWINDOW for the same hwnd CVR's in the same
  4668. * EndDeferWindowPos call. It would be ideal to do the check here, but
  4669. * most of the time the caller does the check and we don't want to
  4670. * penalize everybody just because of the weird cases.
  4671. *
  4672. * History:
  4673. * 11-Jul-1991 DarrinM Ported from Win 3.1 sources.
  4674. \***************************************************************************/
  4675. VOID SetVisible(
  4676. PWND pwnd,
  4677. UINT flags)
  4678. {
  4679. #ifdef REDIRECTION
  4680. PDESKTOP pdesk = pwnd->head.rpdesk;
  4681. #endif
  4682. if (flags & SV_SET) {
  4683. if (TestWF(pwnd, WFINDESTROY)) {
  4684. RIPMSG1(RIP_WARNING, "SetVisible: show INDESTROY 0x%p", pwnd);
  4685. }
  4686. if (TestWF(pwnd, WFVISIBLE)) {
  4687. RIPMSG1(RIP_WARNING, "SetVisible: already visible 0x%p", pwnd);
  4688. } else {
  4689. SetWF(pwnd, WFVISIBLE);
  4690. IncVisWindows(pwnd);
  4691. #ifdef REDIRECTION
  4692. if (((pdesk != NULL && (pdesk->dwDTFlags & DF_REDIRECTED)
  4693. && !(GETPTI(pwnd)->ppi->dwRedirection & PF_REDIRECTIONHOST))
  4694. || (GETPTI(pwnd)->ppi->dwRedirection & PF_REDIRECTED))
  4695. && FTopLevel(pwnd)) {
  4696. SetRedirectedWindow(pwnd, REDIRECT_EXTREDIRECTED);
  4697. SetWF(pwnd, WEFEXTREDIRECTED);
  4698. }
  4699. #endif
  4700. }
  4701. } else {
  4702. if (flags & SV_CLRFTRUEVIS) {
  4703. ClrFTrueVis(pwnd);
  4704. }
  4705. if (TestWF(pwnd, WFDESTROYED)) {
  4706. RIPMSG1(RIP_WARNING, "SetVisible: hide DESTROYED 0x%p", pwnd);
  4707. }
  4708. if (TestWF(pwnd, WFVISIBLE)) {
  4709. ClrWF(pwnd, WFVISIBLE);
  4710. DecVisWindows(pwnd);
  4711. #ifdef REDIRECTION
  4712. if (((pdesk != NULL && (pdesk->dwDTFlags & DF_REDIRECTED)
  4713. && !(GETPTI(pwnd)->ppi->dwRedirection & PF_REDIRECTIONHOST))
  4714. || (GETPTI(pwnd)->ppi->dwRedirection & PF_REDIRECTED))
  4715. && FTopLevel(pwnd)) {
  4716. UnsetRedirectedWindow(pwnd, REDIRECT_EXTREDIRECTED);
  4717. ClrWF(pwnd, WEFEXTREDIRECTED);
  4718. }
  4719. #endif
  4720. } else {
  4721. RIPMSG1(RIP_WARNING, "SetVisible: already hidden 0x%p", pwnd);
  4722. }
  4723. }
  4724. }
  4725. /***************************************************************************\
  4726. * IsMaxedRect
  4727. *
  4728. * Determines if a window is "maximizing" to a certain area
  4729. *
  4730. * History:
  4731. \***************************************************************************/
  4732. BOOL IsMaxedRect(
  4733. LPRECT lprcWithin,
  4734. PCSIZERECT psrcMaybe)
  4735. {
  4736. return(psrcMaybe->x <= lprcWithin->left &&
  4737. psrcMaybe->y <= lprcWithin->top &&
  4738. psrcMaybe->cx >= lprcWithin->right - lprcWithin->left &&
  4739. psrcMaybe->cy >= lprcWithin->bottom - lprcWithin->top);
  4740. }
  4741. /***************************************************************************\
  4742. * xxxCheckFullScreen
  4743. *
  4744. * Sees if a window is really fullscreen or just a maximized window in
  4745. * disguise. If the latter, it will be forced to the proper maximized
  4746. * size.
  4747. *
  4748. * This is called from both CalcValidRects() and CreateWindowEx().
  4749. *
  4750. * History:
  4751. \***************************************************************************/
  4752. BOOL xxxCheckFullScreen(
  4753. PWND pwnd,
  4754. PSIZERECT psrc)
  4755. {
  4756. BOOL fYielded = FALSE;
  4757. PMONITOR pMonitor;
  4758. PMONITOR pMonitorPrimary;
  4759. TL tlpMonitor;
  4760. RECT rc;
  4761. BOOL fIsPrimary;
  4762. CheckLock(pwnd);
  4763. /*
  4764. * SINCE THIS IS ONLY CALLED IN 2 PLACES, make the checks there
  4765. * instead of the overhead of calling this function in time critical
  4766. * places.
  4767. *
  4768. * If 3 or more places call it, put the child/toolwindow checks here
  4769. */
  4770. UserAssert(!TestWF(pwnd, WFCHILD));
  4771. UserAssert(!TestWF(pwnd, WEFTOOLWINDOW));
  4772. pMonitorPrimary = GetPrimaryMonitor();
  4773. if (gpDispInfo->cMonitors == 1) {
  4774. pMonitor = pMonitorPrimary;
  4775. } else {
  4776. /*
  4777. * In multiple monitor mode, windows that take up the entire
  4778. * virtual screen are not considered 'full screen'. 'Full screen'
  4779. * means full single monitor only. This detection is so that any
  4780. * docked bars--tray, office'95 tools--can get out of the way for
  4781. * the application.
  4782. *
  4783. * There are only three types of windows that ought to go full
  4784. * virtual screen. None of them need the tray et al. to get out of
  4785. * the way:
  4786. * (1) Normal app windows that want a lot of space
  4787. * * Those guys just activate and deactivate normally.
  4788. * (2) Desktop windows
  4789. * * Shell, User desktop sit behind everything else.
  4790. * (3) Screen savers, demos, etc.
  4791. * * These guys should be WS_EX_TOPMOST to ensure they sit
  4792. * over everybody.
  4793. */
  4794. if (IsMaxedRect(&gpDispInfo->rcScreen, psrc))
  4795. return fYielded;
  4796. RECTFromSIZERECT(&rc, psrc);
  4797. pMonitor = _MonitorFromRect(&rc, MONITOR_DEFAULTTOPRIMARY);
  4798. }
  4799. fIsPrimary = (pMonitor == pMonitorPrimary);
  4800. ThreadLockAlways(pMonitor, &tlpMonitor);
  4801. if (IsMaxedRect(&pMonitor->rcWork, psrc)) {
  4802. if (TestWF(pwnd, WFMAXIMIZED)) {
  4803. SetWF(pwnd, WFREALLYMAXIMIZABLE);
  4804. if (gpDispInfo->cMonitors > 1) {
  4805. /*
  4806. * This is for XL '95 going fullscreen when already maxed. It
  4807. * always uses the primary display. Let's hack them, and any
  4808. * other old app that tries to move its truly maximized window.
  4809. * They will be clipped otherwise by our fake regional stuff.
  4810. */
  4811. PMONITOR pMonitorReal;
  4812. pMonitorReal = _MonitorFromWindow(pwnd, MONITOR_DEFAULTTOPRIMARY);
  4813. if (pMonitorReal != pMonitor && fIsPrimary) {
  4814. /*
  4815. * Transfer over the shape to the REAL monitor.
  4816. */
  4817. psrc->x += pMonitorReal->rcMonitor.left;
  4818. psrc->y += pMonitorReal->rcMonitor.top;
  4819. psrc->cx -= (pMonitor->rcMonitor.right - pMonitor->rcMonitor.left) +
  4820. (pMonitorReal->rcMonitor.right - pMonitorReal->rcMonitor.left);
  4821. psrc->cy -= (pMonitor->rcMonitor.bottom - pMonitor->rcMonitor.top) +
  4822. (pMonitorReal->rcMonitor.bottom - pMonitorReal->rcMonitor.top);
  4823. ThreadUnlock(&tlpMonitor);
  4824. pMonitor = pMonitorReal;
  4825. fIsPrimary = FALSE;
  4826. ThreadLockAlways(pMonitor, &tlpMonitor);
  4827. }
  4828. }
  4829. }
  4830. if ( TestWF(pwnd, WFMAXIMIZED) &&
  4831. TestWF(pwnd, WFMAXBOX) &&
  4832. (TestWF(pwnd, WFBORDERMASK) == LOBYTE(WFCAPTION))) {
  4833. if ( psrc->y + SYSMET(CYCAPTION) <= pMonitor->rcMonitor.top &&
  4834. psrc->y + psrc->cy >= pMonitor->rcMonitor.bottom) {
  4835. if (!TestWF(pwnd, WFFULLSCREEN)) {
  4836. /*
  4837. * Only want to do full screen stuff on the tray
  4838. * monitor.
  4839. */
  4840. fYielded = xxxAddFullScreen(pwnd, pMonitor);
  4841. }
  4842. } else {
  4843. int iRight;
  4844. int iBottom;
  4845. int dxy;
  4846. if (TestWF(pwnd, WFFULLSCREEN)) {
  4847. fYielded = xxxRemoveFullScreen(pwnd, pMonitor);
  4848. }
  4849. /*
  4850. * Despite the code in GetMinMaxInfo() to fix up
  4851. * the max rect, we still have to hack old apps.
  4852. * Word '95 & XL '95 do weird things when going to/from
  4853. * full screen when maximized already.
  4854. *
  4855. * NOTE: you can have more than one docked bar on a
  4856. * monitor. Win '95 code doesn't work right in that
  4857. * case.
  4858. */
  4859. dxy = GetWindowBorders(pwnd->style, pwnd->ExStyle, TRUE, FALSE);
  4860. dxy *= SYSMET(CXBORDER);
  4861. psrc->x = pMonitor->rcWork.left - dxy;
  4862. psrc->y = pMonitor->rcWork.top - dxy;
  4863. dxy *= 2;
  4864. iRight = pMonitor->rcWork.right - pMonitor->rcWork.left + dxy;
  4865. iBottom = pMonitor->rcWork.bottom - pMonitor->rcWork.top + dxy;
  4866. /*
  4867. * Let console windows maximze smaller than defaults.
  4868. */
  4869. if (pwnd->pcls->atomClassName == gatomConsoleClass) {
  4870. psrc->cx = min(iRight, psrc->cx);
  4871. psrc->cy = min(iBottom, psrc->cy);
  4872. } else {
  4873. psrc->cx = iRight;
  4874. /*
  4875. * B#14012 save QuickLink II that wants 4 pixels hanging off
  4876. * the screen for every edge except the bottom edge, which
  4877. * they only want to overhang by 2 pixels -- jeffbog 5/17/95
  4878. *
  4879. * BUT THIS CODE DOESN'T WORK FOR MULTIPLE MONITORS, so don't
  4880. * do it on secondary dudes. Else, XL '95 flakes out.
  4881. */
  4882. if (fIsPrimary && !TestWF(pwnd, WFWIN40COMPAT)) {
  4883. psrc->cy = min(iBottom, psrc->cy);
  4884. } else {
  4885. psrc->cy = iBottom;
  4886. }
  4887. }
  4888. }
  4889. } else if (IsMaxedRect(&pMonitor->rcMonitor, psrc)) {
  4890. fYielded = xxxAddFullScreen(pwnd, pMonitor);
  4891. }
  4892. } else {
  4893. if (TestWF(pwnd, WFMAXIMIZED)) {
  4894. ClrWF(pwnd, WFREALLYMAXIMIZABLE);
  4895. }
  4896. fYielded = xxxRemoveFullScreen(pwnd, pMonitor);
  4897. }
  4898. ThreadUnlock(&tlpMonitor);
  4899. return fYielded;
  4900. }
  4901. /***************************************************************************\
  4902. * ClrFTrueVis
  4903. *
  4904. * Called when making a window invisible. This routine destroys any update
  4905. * regions that may exist, and clears the WF_TRUEVIS of all windows below
  4906. * the passed in window.
  4907. *
  4908. * History:
  4909. * 11-Jul-1991 DarrinM Ported from Win 3.1 sources.
  4910. \***************************************************************************/
  4911. VOID ClrFTrueVis(
  4912. PWND pwnd)
  4913. {
  4914. /*
  4915. * Destroy pwnd and its children's update regions.
  4916. * We do this here to guarantee that a hidden window
  4917. * and its children don't have update regions.
  4918. *
  4919. * This fixes bugs when destroying windows that have
  4920. * update regions (SendDestroyMessages) among others
  4921. * and allows us to simplify SetParent(). This was
  4922. * deemed better than hacking DoPaint() and/or
  4923. * DestroyWindow().
  4924. *
  4925. * We can stop recursing when we find a window that doesn't
  4926. * have the visible bit set, because by definition it won't
  4927. * have any update regions below it (this routine will have been called)
  4928. */
  4929. if (NEEDSPAINT(pwnd)) {
  4930. DeleteMaybeSpecialRgn(pwnd->hrgnUpdate);
  4931. ClrWF(pwnd, WFINTERNALPAINT);
  4932. pwnd->hrgnUpdate = NULL;
  4933. DecPaintCount(pwnd);
  4934. }
  4935. for (pwnd = pwnd->spwndChild; pwnd != NULL; pwnd = pwnd->spwndNext) {
  4936. /*
  4937. * pwnd->fs &= ~WF_TRUEVIS;
  4938. */
  4939. if (TestWF(pwnd, WFVISIBLE))
  4940. ClrFTrueVis(pwnd);
  4941. }
  4942. }
  4943. /***************************************************************************\
  4944. * OffsetChildren
  4945. *
  4946. * Offsets the window and client rects of all children of hwnd.
  4947. * Also deals with the children's update regions and SPB rects.
  4948. *
  4949. * History:
  4950. * 22-Jul-1991 DarrinM Ported from Win 3.1 sources.
  4951. \***************************************************************************/
  4952. VOID OffsetChildren(
  4953. PWND pwnd,
  4954. int dx,
  4955. int dy,
  4956. LPRECT prcHitTest)
  4957. {
  4958. RECT rc;
  4959. PWND pwndStop;
  4960. if (!pwnd->spwndChild)
  4961. return;
  4962. pwndStop = pwnd;
  4963. pwnd = pwndStop->spwndChild;
  4964. for (;;) {
  4965. /*
  4966. * Skip windows that don't intersect prcHitTest...
  4967. */
  4968. if (prcHitTest && !IntersectRect(&rc, prcHitTest, &pwnd->rcWindow))
  4969. goto NextWindow;
  4970. pwnd->rcWindow.left += dx;
  4971. pwnd->rcWindow.right += dx;
  4972. pwnd->rcWindow.top += dy;
  4973. pwnd->rcWindow.bottom += dy;
  4974. pwnd->rcClient.left += dx;
  4975. pwnd->rcClient.right += dx;
  4976. pwnd->rcClient.top += dy;
  4977. pwnd->rcClient.bottom += dy;
  4978. if (pwnd->hrgnUpdate > HRGN_FULL && !TestWF(pwnd, WFMAXFAKEREGIONAL)) {
  4979. GreOffsetRgn(pwnd->hrgnUpdate, dx, dy);
  4980. }
  4981. /*
  4982. * Change position of window region, if it has one
  4983. */
  4984. if (pwnd->hrgnClip != NULL)
  4985. GreOffsetRgn(pwnd->hrgnClip, dx, dy);
  4986. if (TestWF(pwnd, WFHASSPB))
  4987. OffsetRect(&(FindSpb(pwnd))->rc, dx, dy);
  4988. #ifdef CHILD_LAYERING
  4989. if (TestWF(pwnd, WEFLAYERED)) {
  4990. POINT ptPos = {pwnd->rcWindow.left, pwnd->rcWindow.top};
  4991. GreUpdateSprite(gpDispInfo->hDev, PtoHq(pwnd), NULL, NULL,
  4992. &ptPos, NULL, NULL, NULL, 0, NULL, 0, NULL);
  4993. }
  4994. #endif // CHILD_LAYERING
  4995. /*
  4996. * Recurse into the child tree if there are children.
  4997. */
  4998. if (pwnd->spwndChild) {
  4999. pwnd = pwnd->spwndChild;
  5000. continue;
  5001. }
  5002. NextWindow:
  5003. if (pwnd->spwndNext) {
  5004. /*
  5005. * Recurse to the next sibling in the list.
  5006. */
  5007. pwnd = pwnd->spwndNext;
  5008. } else {
  5009. for (;;) {
  5010. /*
  5011. * We're at the end of the sibling window list.
  5012. * Go to the parent's next window.
  5013. */
  5014. pwnd = pwnd->spwndParent;
  5015. if (pwnd == pwndStop)
  5016. return;
  5017. if (pwnd->spwndNext) {
  5018. pwnd = pwnd->spwndNext;
  5019. break;
  5020. }
  5021. }
  5022. }
  5023. }
  5024. }
  5025. /***************************************************************************\
  5026. * SetWindowRgn
  5027. *
  5028. * Parameters:
  5029. * hwnd -- Window handle
  5030. * hrgn -- Region to set into window. NULL can be accepted.
  5031. * fRedraw -- TRUE to go through SetWindowPos() and calculate
  5032. * update regions correctly. If the window is visible
  5033. * this will usually be TRUE.
  5034. *
  5035. * Returns:
  5036. * TRUE for success, FALSE for failure
  5037. *
  5038. * Comments:
  5039. * This is a very simple routine to set a window region. It goes through
  5040. * SetWindowPos() to get perfect update region calculation, and to deal
  5041. * with other related issues like vis rgn change & dc invalidation,
  5042. * display lock holding, spb invalidation, etc. Also since it sends
  5043. * WM_WINDOWPOSCHANGING & WM_WINDOWPOSCHANGED, we'll be able to expand
  5044. * SetWindowPos() in the future to take hrgns directly for efficient
  5045. * window state change control (like setting the rect and region at
  5046. * the same time, among others) without harming compatibility.
  5047. *
  5048. * hrgn is in window rect coordinates (not client rect coordinates).
  5049. * Once set, hrgn is owned by the system. A copy is not made!
  5050. *
  5051. * 30-Jul-1994 ScottLu Created.
  5052. \***************************************************************************/
  5053. #define SWR_FLAGS_REDRAW (SWP_NOCHANGE | SWP_FRAMECHANGED | SWP_NOACTIVATE)
  5054. #define SWR_FLAGS_NOREDRAW (SWP_NOCHANGE | SWP_FRAMECHANGED | SWP_NOACTIVATE | SWP_NOREDRAW)
  5055. BOOL xxxSetWindowRgn(
  5056. PWND pwnd,
  5057. HRGN hrgn,
  5058. BOOL fRedraw)
  5059. {
  5060. PSMWP psmwp;
  5061. HRGN hrgnClip = NULL;
  5062. BOOL bRet = FALSE;
  5063. /*
  5064. * Validate the region handle. We did this for 3.51, so
  5065. * we better do it for later versions. Our validation will
  5066. * make a copy of the clip-rgn and send it through to the
  5067. * SetWIndowRgn code. Once this is set in the kernel, we
  5068. * will return to the client and the old region will be deleted
  5069. * there.
  5070. *
  5071. * If the region passed in is NULL, then we get rid of the
  5072. * current retion. Map it to HRGN_FULL so that SetWindowPos()
  5073. * can tell this is what the caller wants.
  5074. */
  5075. if (hrgn) {
  5076. if ((hrgnClip = UserValidateCopyRgn(hrgn)) == NULL) {
  5077. #if DBG
  5078. RIPMSG0(RIP_WARNING, "xxxSetWindowRgn: Failed to create region!");
  5079. #endif
  5080. goto swrClean;
  5081. }
  5082. MirrorRegion(pwnd, hrgnClip, FALSE);
  5083. } else {
  5084. hrgnClip = HRGN_FULL;
  5085. }
  5086. /*
  5087. * Get a psmwp, and put the region in it, correctly offset.
  5088. * Use SWP_FRAMECHANGED with acts really as a "empty" SetWindowPos
  5089. * that still sends WM_WINDOWPOSCHANGING and CHANGED messages.
  5090. * SWP_NOCHANGE ensures that we don't size, move, activate, zorder.
  5091. */
  5092. if (psmwp = InternalBeginDeferWindowPos(1)) {
  5093. /*
  5094. * psmwp gets freed automatically if this routine fails.
  5095. */
  5096. if (psmwp = _DeferWindowPos(
  5097. psmwp,
  5098. pwnd,
  5099. PWND_TOP,
  5100. 0,
  5101. 0,
  5102. 0,
  5103. 0,
  5104. fRedraw ? SWR_FLAGS_REDRAW : SWR_FLAGS_NOREDRAW)) {
  5105. /*
  5106. * Do the operation. Note that hrgn is still in window coordinates.
  5107. * SetWindowPos() will change it to screen coordinates before
  5108. * selecting into the window.
  5109. */
  5110. psmwp->acvr[0].hrgnClip = hrgnClip;
  5111. bRet = xxxEndDeferWindowPosEx(psmwp, FALSE);
  5112. }
  5113. }
  5114. /*
  5115. * If the call failed, then delete our region we created. A FALSE
  5116. * return means it should've never made it to the xxxSelectWindowRgn
  5117. * call, so everything should be as it was.
  5118. */
  5119. if (!bRet && (hrgnClip != HRGN_FULL)) {
  5120. swrClean:
  5121. GreDeleteObject(hrgnClip);
  5122. }
  5123. return bRet;
  5124. }
  5125. /***************************************************************************\
  5126. * SelectWindowRgn
  5127. *
  5128. * This routine does the work of actually selecting in the window region.
  5129. *
  5130. * 30-Jul-1994 ScottLu Created.
  5131. \***************************************************************************/
  5132. VOID SelectWindowRgn(
  5133. PWND pwnd,
  5134. HRGN hrgnClip)
  5135. {
  5136. /*
  5137. * If there is a region already there, delete it becausea new one is
  5138. * being set. For maximized windows in multiple monitor mode, we
  5139. * always use the monitor HRGN. We don't make a copy. This way, when
  5140. * the hrgn changes because of monitor config, the window's monitor
  5141. * region automatically gets updated. Clever huh? Also saves memory.
  5142. */
  5143. if (pwnd->hrgnClip != NULL) {
  5144. if (TestWF(pwnd, WFMAXFAKEREGIONAL)) {
  5145. ClrWF(pwnd, WFMAXFAKEREGIONAL);
  5146. } else {
  5147. /*
  5148. * Do NOT select in a monitor region if the window is normally
  5149. * regional. The MinMaximize code will always pass HRGN_MONITOR
  5150. * to us no matter what. But when we get here, bail out and
  5151. * don't destroy the app's region if it has one.
  5152. */
  5153. if (hrgnClip == HRGN_MONITOR)
  5154. return;
  5155. GreDeleteObject(pwnd->hrgnClip);
  5156. }
  5157. pwnd->hrgnClip = NULL;
  5158. }
  5159. /*
  5160. * NULL or HRGN_FULL means "set to NULL". If we have a real region,
  5161. * use it. USER needs to own it, and it needs to be in screen
  5162. * coordinates.
  5163. */
  5164. if (hrgnClip > HRGN_FULL) {
  5165. if (hrgnClip == HRGN_MONITOR) {
  5166. PMONITOR pMonitor;
  5167. /*
  5168. * Use the monitor region if the window is really maxed
  5169. * on a monitor. It's already happened by the time we get here,
  5170. * if so. And xxxCheckFullScreen will clear the reallymaximed
  5171. * style for a maximized window if it doesn't cover the whole
  5172. * max area.
  5173. */
  5174. UserAssert(pwnd->spwndParent == PWNDDESKTOP(pwnd));
  5175. if (!TestWF(pwnd, WFMAXIMIZED) || !TestWF(pwnd, WFREALLYMAXIMIZABLE))
  5176. return;
  5177. /*
  5178. * Do nothing for windows off screen.
  5179. */
  5180. pMonitor = _MonitorFromWindow(pwnd, MONITOR_DEFAULTTONULL);
  5181. if (!pMonitor)
  5182. return;
  5183. hrgnClip = pMonitor->hrgnMonitor;
  5184. SetWF(pwnd, WFMAXFAKEREGIONAL);
  5185. } else {
  5186. if (pwnd != PWNDDESKTOP(pwnd)) {
  5187. GreOffsetRgn(hrgnClip, pwnd->rcWindow.left, pwnd->rcWindow.top);
  5188. }
  5189. GreSetRegionOwner(hrgnClip, OBJECT_OWNER_PUBLIC);
  5190. }
  5191. pwnd->hrgnClip = hrgnClip;
  5192. }
  5193. }
  5194. /***************************************************************************\
  5195. * TestRectBogus
  5196. *
  5197. * Returns TRUE if the window rect [x,y,cx,cy] is centered or
  5198. * clipped to the monitor or work rect [prc], FALSE otherwise.
  5199. *
  5200. * History:
  5201. * 26-Mar-1997 adams Created.
  5202. \***************************************************************************/
  5203. #define SLOP_X 8
  5204. #define SLOP_Y 8
  5205. BOOL
  5206. TestRectBogus(RECT * prc, int x, int y, int cx, int cy)
  5207. {
  5208. //
  5209. // check for a fullscreen (or offscreen) window
  5210. //
  5211. if ( x <= prc->left &&
  5212. y <= prc->top &&
  5213. cx >= (prc->right - prc->left) &&
  5214. cy >= (prc->bottom - prc->top)) {
  5215. // rect is fullscreen
  5216. return FALSE;
  5217. }
  5218. //
  5219. // check for the window being centered to the work area
  5220. // use <= for y to catch dialogs centered "high"
  5221. // (like the network logon dialog)
  5222. //
  5223. if ( abs(x - (prc->right + prc->left - cx) / 2) <= SLOP_X &&
  5224. abs(y - (prc->bottom + prc->top - cy) / 2) <= SLOP_Y ) {
  5225. // rect centered
  5226. return TRUE;
  5227. }
  5228. //
  5229. // check for the window being cliped to the work area
  5230. //
  5231. if ( x == prc->left ||
  5232. y == prc->top ||
  5233. x == (prc->right - cx) ||
  5234. y == (prc->bottom - cy)) {
  5235. // rect is clipped
  5236. return TRUE;
  5237. }
  5238. return FALSE;
  5239. }
  5240. /***************************************************************************\
  5241. * IsRectBogus
  5242. *
  5243. * Returns TRUE if the window rect [x,y,cx,cy] is centered or
  5244. * clipped to the monitor or work rect of the primary monitor.
  5245. *
  5246. * History:
  5247. * 26-Mar-1997 adams Created.
  5248. \***************************************************************************/
  5249. BOOL
  5250. IsRectBogus(
  5251. int x,
  5252. int y,
  5253. int cx,
  5254. int cy)
  5255. {
  5256. PMONITOR pMonitorPrimary = GetPrimaryMonitor();
  5257. return TestRectBogus(&pMonitorPrimary->rcWork, x, y, cx, cy) ||
  5258. TestRectBogus(&pMonitorPrimary->rcMonitor, x, y, cx, cy);
  5259. }
  5260. /***************************************************************************\
  5261. * FixBogusSWP
  5262. *
  5263. * Detects if a rect is being centered or clipped to the primary monitor,
  5264. * and centers it in its owner's window if so. This prevents apps that
  5265. * are not multimon aware from having their "main" window displayed on
  5266. * one monitor but their dialogs moved to the primary monitor
  5267. * because they believe the dialog is offscreen.
  5268. *
  5269. * History:
  5270. * 26-Mar-1997 adams Created.
  5271. \***************************************************************************/
  5272. VOID
  5273. FixBogusSWP(
  5274. PWND pwnd,
  5275. int * px,
  5276. int * py,
  5277. int cx,
  5278. int cy,
  5279. UINT flags)
  5280. {
  5281. PMONITOR pMonitor;
  5282. pMonitor = _MonitorFromWindow(pwnd->spwndOwner, MONITOR_DEFAULTTONEAREST);
  5283. //
  5284. // only check for a bogus SWP if the owner is not on the primary
  5285. //
  5286. if (pMonitor != GetPrimaryMonitor()) {
  5287. //
  5288. // get the current size if SWP_NOSIZE is set
  5289. //
  5290. if (flags & SWP_NOSIZE) {
  5291. cx = pwnd->rcWindow.right - pwnd->rcWindow.left;
  5292. cy = pwnd->rcWindow.bottom - pwnd->rcWindow.top;
  5293. }
  5294. //
  5295. // see if the app is trying to center or clip the window
  5296. //
  5297. if (IsRectBogus(*px, *py, cx, cy))
  5298. {
  5299. RECT rc;
  5300. #if DBG
  5301. int oldX = *px;
  5302. int oldY = *py;
  5303. #endif
  5304. //
  5305. // the app wants to center/clip the window
  5306. // we will have to do it for them.
  5307. //
  5308. // get the window rect of the parent and
  5309. // intersect that with the work area of
  5310. // the owning monitor, then center the
  5311. // window to this rect.
  5312. //
  5313. IntersectRect(&rc, &pMonitor->rcWork, &pwnd->spwndOwner->rcWindow);
  5314. //
  5315. // new multimonior friendly position.
  5316. //
  5317. *px = rc.left + (rc.right - rc.left - cx) / 2;
  5318. *py = rc.top + (rc.bottom - rc.top - cy) / 2;
  5319. //
  5320. // now clip to the work area.
  5321. //
  5322. if (*px + cx > pMonitor->rcWork.right) {
  5323. *px = pMonitor->rcWork.right - cx;
  5324. }
  5325. if (*py + cy > pMonitor->rcWork.bottom) {
  5326. *py = pMonitor->rcWork.bottom - cy;
  5327. }
  5328. if (*px < pMonitor->rcWork.left) {
  5329. *px = pMonitor->rcWork.left;
  5330. }
  5331. if (*py < pMonitor->rcWork.top) {
  5332. *py = pMonitor->rcWork.top;
  5333. }
  5334. RIPMSG0(RIP_WARNING | RIP_THERESMORE, "SetWindowPos detected that your app is centering or clipping");
  5335. RIPMSG0(RIP_WARNING | RIP_THERESMORE | RIP_NONAME, "a window to the primary monitor when its owner is on a different monitor.");
  5336. RIPMSG0(RIP_WARNING | RIP_THERESMORE | RIP_NONAME, "Consider fixing your app to use the Window Manager Multimonitor APIs.");
  5337. RIPMSG4(RIP_WARNING | RIP_NONAME, "SetWindowPos moved the window from (%d,%d) to (%d,%d).\n",
  5338. oldX, oldY, *px, *py);
  5339. }
  5340. }
  5341. }
  5342. /***************************************************************************\
  5343. * PreventInterMonitorBlts()
  5344. *
  5345. * Prevents monitor-to-monitor blts when they are different caps. This
  5346. * way we redraw the part of a window that moves to a different monitor.
  5347. * We try to blt as much as possible.
  5348. *
  5349. * We look at the source rect and what monitor owns it, and how much that
  5350. * monitor also contains of the destination rect. Then we compare that
  5351. * with the destination rect and what monitor owns that, and how much it
  5352. * contains of the source rect. The larger wins.
  5353. *
  5354. * rcBlt is in screen coordinates and is the DESTINATION.
  5355. *
  5356. * History:
  5357. * 11-11-1997 vadimg ported from Memphis
  5358. \***************************************************************************/
  5359. VOID PreventInterMonitorBlts(
  5360. PCVR pcvr)
  5361. {
  5362. RECT rcSrc;
  5363. RECT rcDst;
  5364. RECT rcSrcT;
  5365. RECT rcDstT;
  5366. PMONITOR pMonitor;
  5367. /*
  5368. * If the destination is empty do nothing.
  5369. */
  5370. if (IsRectEmpty(&pcvr->rcBlt)) {
  5371. return;
  5372. }
  5373. /*
  5374. * Get the source rect (rcBlt is the destination, dxBlt/dyBlt are the
  5375. * distance moved from the source).
  5376. */
  5377. CopyOffsetRect(&rcSrc, &pcvr->rcBlt, -pcvr->dxBlt, -pcvr->dyBlt);
  5378. /*
  5379. * Split up the source into its monitor pieces. If the source intersects
  5380. * a monitor, then figure out where that part will be in the destination.
  5381. * Intersect the destination part with the same monitor. The result is
  5382. * the amount we can blt from the source to the dest on that monitor.
  5383. *
  5384. * We do this for each monitor to find the biggest blt rect. We want
  5385. * the biggest because we want to repaint as little as possible. We do
  5386. * bail out if both the source and dest are fully contained on the same
  5387. * monitor.
  5388. */
  5389. for (pMonitor = gpDispInfo->pMonitorFirst;
  5390. pMonitor != NULL;
  5391. pMonitor = pMonitor->pMonitorNext) {
  5392. /*
  5393. * We're only interested in visible monitors.
  5394. */
  5395. if (!(pMonitor->dwMONFlags & MONF_VISIBLE))
  5396. continue;
  5397. /*
  5398. * If this monitor doesn't contain a piece of the source, we don't
  5399. * care about it. We won't be doing a same monitor blt on it for sure.
  5400. */
  5401. if (!IntersectRect(&rcSrcT, &rcSrc, &pMonitor->rcMonitor))
  5402. continue;
  5403. /*
  5404. * See where this rect would be in the destination.
  5405. */
  5406. CopyOffsetRect(&rcDst, &rcSrcT, pcvr->dxBlt, pcvr->dyBlt);
  5407. /*
  5408. * Intersect this rect with the same monitor rect to see what piece
  5409. * can be safely blted on the same monitor.
  5410. */
  5411. IntersectRect(&rcDstT, &rcDst, &pMonitor->rcMonitor);
  5412. /*
  5413. * Is this piece of the source staying on this monitor?
  5414. */
  5415. if (EqualRect(&rcDstT, &rcDst)) {
  5416. /*
  5417. * This source piece is staying completely on this monitor when
  5418. * it becomes the destination. Hence there is nothing to add
  5419. * to our invalid sum, hrgnInterMonitor.
  5420. */
  5421. if (EqualRect(&rcSrcT, &rcSrc)) {
  5422. /*
  5423. * The source is completely ON one monitor and moving to
  5424. * a location also completely ON this monitor. Great, no
  5425. * intermonitor blts whatsoever. We are done.
  5426. */
  5427. UserAssert(pcvr->hrgnInterMonitor == NULL);
  5428. return;
  5429. } else {
  5430. continue;
  5431. }
  5432. }
  5433. /*
  5434. * OK, some piece of the source is moving across monitors. Figure
  5435. * out what it is and where that piece is in the destination. That
  5436. * piece in the destination must be invalidated and not blted.
  5437. */
  5438. if (pcvr->hrgnInterMonitor == NULL) {
  5439. pcvr->hrgnInterMonitor = CreateEmptyRgn();
  5440. }
  5441. /*
  5442. * The difference between the transposed source to the dest, and the
  5443. * real part of the dest that lies on this monitor, is the amount
  5444. * of the source that will move across a monitor boundary. Add this
  5445. * to our accumulated invalid region.
  5446. *
  5447. * rcDst is the whole source chunk, rcDstT is the part on the same
  5448. * monitor as the source chunk.
  5449. */
  5450. GreSetRectRgn(ghrgnInv2, rcDst.left, rcDst.top, rcDst.right, rcDst.bottom);
  5451. GreSetRectRgn(ghrgnGDC, rcDstT.left, rcDstT.top, rcDstT.right, rcDstT.bottom);
  5452. SubtractRgn(ghrgnInv2, ghrgnInv2, ghrgnGDC);
  5453. UnionRgn(pcvr->hrgnInterMonitor, pcvr->hrgnInterMonitor, ghrgnInv2);
  5454. }
  5455. #if DBG
  5456. VerifyVisibleMonitorCount();
  5457. #endif
  5458. }