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.

1312 lines
38 KiB

  1. /****************************** Module Header ******************************\
  2. * Module Name: winmgr.c
  3. *
  4. * Copyright (c) 1985 - 1999, Microsoft Corporation
  5. *
  6. * Core Window Manager APIs and support routines.
  7. *
  8. * History:
  9. * 24-Sep-1990 darrinm Generated stubs.
  10. * 22-Jan-1991 IanJa Handle revalidation added
  11. * 19-Feb-1991 JimA Added enum access checks
  12. \***************************************************************************/
  13. #include "precomp.h"
  14. #pragma hdrstop
  15. /***************************************************************************\
  16. * xxxFlashWindow (API)
  17. *
  18. * New for 5.0: HIWORD(dwFlags) contains the number of times the window should be
  19. * flashed. LOWORD(dwFlags) contains the FLASHW_ bits.
  20. *
  21. * History:
  22. * 27-Nov-1990 DarrinM Ported.
  23. * 15-Nov-1997 MCostea Added dwTimeout and windowing the maximised cmd
  24. \***************************************************************************/
  25. BOOL xxxFlashWindow(
  26. PWND pwnd,
  27. DWORD dwFlags,
  28. DWORD dwTimeout)
  29. {
  30. BOOL fStatePrev = FALSE;
  31. BOOL fFlashOn;
  32. DWORD dwState;
  33. CheckLock(pwnd);
  34. /*
  35. * Get the previous state. If not available (FLASHW_STOP) then
  36. * initialize on/off based on frame
  37. */
  38. dwState = GetFlashWindowState(pwnd);
  39. if (dwState == FLASHW_DONE) {
  40. /*
  41. * We just need to clean up and to set the activation correctly
  42. */
  43. dwState |= FLASHW_KILLTIMER;
  44. dwFlags = FLASHW_STOP;
  45. goto flash;
  46. }
  47. if (dwState == FLASHW_STOP) {
  48. #if defined(_X86_)
  49. /*
  50. * If there is a fullscreen cmd window, switch it to window mode
  51. * so that the user gets a chance to see the flashing one
  52. */
  53. if (gbFullScreen == FULLSCREEN) {
  54. _PostMessage(gspwndFullScreen, CM_MODE_TRANSITION, (WPARAM)WINDOWED, (LPARAM)0);
  55. }
  56. #endif // _X86_
  57. if (TestWF(pwnd, WFFRAMEON)) {
  58. dwState = FLASHW_ON | FLASHW_STARTON;
  59. }
  60. } else if (dwFlags == FLASHW_TIMERCALL) {
  61. dwFlags = dwState;
  62. }
  63. dwFlags &= FLASHW_CALLERBITS;
  64. fStatePrev = (dwState & FLASHW_ON);
  65. /*
  66. * Later5.0 Gerardob
  67. * Not sure why we do this check but it used to be here.
  68. */
  69. if (pwnd == gspwndAltTab) {
  70. return fStatePrev;
  71. }
  72. /*
  73. * Check if we're waiting to come to the foreground to stop.
  74. */
  75. if (dwState & FLASHW_FLASHNOFG) {
  76. if (gpqForeground == GETPTI(pwnd)->pq)
  77. dwFlags = FLASHW_STOP;
  78. }
  79. flash:
  80. /*
  81. * Figure out new state
  82. */
  83. if (dwFlags != FLASHW_STOP) {
  84. fFlashOn = !fStatePrev;
  85. } else {
  86. fFlashOn = (gpqForeground != NULL) && (gpqForeground->spwndActive == pwnd);
  87. }
  88. /*
  89. * Flash'em
  90. */
  91. if ((dwFlags == FLASHW_STOP) || (dwFlags & FLASHW_CAPTION)) {
  92. xxxSendMessage(pwnd, WM_NCACTIVATE, fFlashOn, 0L);
  93. }
  94. if ((dwFlags == FLASHW_STOP) || (dwFlags & FLASHW_TRAY)) {
  95. if (IsTrayWindow(pwnd)) {
  96. HWND hw = HWq(pwnd);
  97. BOOL fShellFlash;
  98. if (dwState & FLASHW_DONE) {
  99. /*
  100. * If the window is not the active one when we're done flashing,
  101. * let the tray icon remain activated. The Shell is going to
  102. * take care to restore it at the when the window gets activated
  103. */
  104. fShellFlash = !fFlashOn;
  105. } else {
  106. fShellFlash = (dwFlags == FLASHW_STOP ? FALSE : fFlashOn);
  107. }
  108. xxxCallHook(HSHELL_REDRAW, (WPARAM) hw, (LPARAM) fShellFlash, WH_SHELL);
  109. PostShellHookMessages(fShellFlash? HSHELL_FLASH:HSHELL_REDRAW, (LPARAM)hw);
  110. }
  111. }
  112. /*
  113. * If we're to continue, check count, set timer and store
  114. * state as appropriate. Otherwise, kill timer and remove
  115. * state
  116. */
  117. if (dwFlags != FLASHW_STOP) {
  118. /*
  119. * If counting, decrement count when we complete a cycle
  120. */
  121. if (HIWORD(dwFlags) != 0) {
  122. dwState |= FLASHW_COUNTING;
  123. if (!(fFlashOn ^ !!(dwState & FLASHW_STARTON))) {
  124. dwFlags -= MAKELONG(0,1);
  125. }
  126. /*
  127. * Make sure we have a timer going.
  128. */
  129. if (!(dwState & FLASHW_KILLTIMER)) {
  130. dwFlags |= FLASHW_TIMER;
  131. }
  132. }
  133. /*
  134. * Set a timer if needed.
  135. */
  136. if (dwFlags & FLASHW_TIMER) {
  137. dwState |= FLASHW_KILLTIMER;
  138. InternalSetTimer(pwnd,
  139. IDSYS_FLASHWND,
  140. dwTimeout ? dwTimeout : gpsi->dtCaretBlink,
  141. xxxSystemTimerProc,
  142. TMRF_SYSTEM);
  143. }
  144. /*
  145. * Remember on/off state, propagate public flags
  146. * and count then save the state
  147. */
  148. if (dwState & FLASHW_COUNTING &&
  149. HIWORD(dwFlags) == 0) {
  150. dwState = FLASHW_DONE;
  151. }
  152. else {
  153. SET_OR_CLEAR_FLAG(dwState, FLASHW_ON, fFlashOn);
  154. COPY_FLAG(dwState, dwFlags, FLASHW_CALLERBITS & ~FLASHW_TIMER);
  155. }
  156. SetFlashWindowState(pwnd, dwState);
  157. } else {
  158. /*
  159. * We're done.
  160. */
  161. if (dwState & FLASHW_KILLTIMER) {
  162. _KillSystemTimer(pwnd, IDSYS_FLASHWND);
  163. }
  164. RemoveFlashWindowState(pwnd);
  165. }
  166. return fStatePrev;
  167. }
  168. /***************************************************************************\
  169. * xxxEnableWindow (API)
  170. *
  171. *
  172. * History:
  173. * 12-Nov-1990 DarrinM Ported.
  174. \***************************************************************************/
  175. BOOL xxxEnableWindow(
  176. PWND pwnd,
  177. BOOL fEnable)
  178. {
  179. BOOL fOldState, fChange;
  180. CheckLock(pwnd);
  181. UserAssert(IsWinEventNotifyDeferredOK());
  182. fOldState = TestWF(pwnd, WFDISABLED);
  183. if (!fEnable) {
  184. fChange = !TestWF(pwnd, WFDISABLED);
  185. xxxSendMessage(pwnd, WM_CANCELMODE, 0, 0);
  186. if (pwnd == PtiCurrent()->pq->spwndFocus) {
  187. xxxSetFocus(NULL);
  188. }
  189. SetWF(pwnd, WFDISABLED);
  190. } else {
  191. fChange = TestWF(pwnd, WFDISABLED);
  192. ClrWF(pwnd, WFDISABLED);
  193. }
  194. if (fChange) {
  195. xxxWindowEvent(EVENT_OBJECT_STATECHANGE, pwnd, OBJID_WINDOW,
  196. INDEXID_CONTAINER, 0);
  197. xxxSendMessage(pwnd, WM_ENABLE, fEnable, 0L);
  198. }
  199. return fOldState;
  200. }
  201. /***************************************************************************\
  202. * xxxDoSend
  203. *
  204. * The following code is REALLY BOGUS!!!! Basically it prevents an
  205. * app from hooking the WM_GET/SETTEXT messages if they're going to
  206. * be called from another app.
  207. *
  208. * History:
  209. * 04-Mar-1992 JimA Ported from Win 3.1 sources.
  210. \***************************************************************************/
  211. LRESULT xxxDoSend(
  212. PWND pwnd,
  213. UINT message,
  214. WPARAM wParam,
  215. LPARAM lParam)
  216. {
  217. /*
  218. * We compare PROCESSINFO sturctures here so multi-threaded
  219. * app can do what the want.
  220. */
  221. if (GETPTI(pwnd)->ppi == PtiCurrent()->ppi) {
  222. return xxxSendMessage(pwnd, message, wParam, lParam);
  223. } else {
  224. return xxxDefWindowProc(pwnd, message, wParam, lParam);
  225. }
  226. }
  227. /***************************************************************************\
  228. * xxxGetWindowText (API)
  229. *
  230. *
  231. * History:
  232. * 09-Nov-1990 DarrinM Wrote.
  233. \***************************************************************************/
  234. int xxxGetWindowText(
  235. PWND pwnd,
  236. LPTSTR psz,
  237. int cchMax)
  238. {
  239. LARGE_UNICODE_STRING str;
  240. UINT nRet, nLen;
  241. CheckLock(pwnd);
  242. if (cchMax) {
  243. /*
  244. * Initialize string empty, in case xxxSendMessage aborts validation
  245. * If a bogus value was returned, rely on str.Length
  246. */
  247. str.bAnsi = FALSE;
  248. str.MaximumLength = cchMax * sizeof(WCHAR);
  249. str.Buffer = psz;
  250. str.Length = 0;
  251. *psz = TEXT('\0');
  252. nRet = (UINT)xxxDoSend(pwnd, WM_GETTEXT, cchMax, (LPARAM)&str);
  253. nLen = str.Length / sizeof(WCHAR);
  254. return (nRet > nLen) ? nLen : nRet;
  255. }
  256. return 0;
  257. }
  258. /***************************************************************************\
  259. * xxxSetParent (API)
  260. *
  261. * Change a windows parent to a new window. These steps are taken:
  262. *
  263. * 1. The window is hidden (if visible),
  264. * 2. Its coordinates are mapped into the new parent's space such that the
  265. * window's screen-relative position is unchanged.
  266. * 3. The window is unlinked from its old parent and relinked to the new.
  267. * 4. xxxSetWindowPos is used to move the window to its new position.
  268. * 5. The window is shown again (if originally visible)
  269. *
  270. * NOTE: If you have a child window and set its parent to be NULL (the
  271. * desktop), the WS_CHILD style isn't removed from the window. This bug has
  272. * been in windows since 2.x. It turns out the apps group depends on this for
  273. * their combo boxes to work. Basically, you end up with a top level window
  274. * that never gets activated (our activation code blows it off due to the
  275. * WS_CHILD bit).
  276. *
  277. * History:
  278. * 12-Nov-1990 DarrinM Ported.
  279. * 19-Feb-1991 JimA Added enum access check
  280. * 12-Apr-2001 Mohamed Added the check of parenting your owner.
  281. \***************************************************************************/
  282. PWND xxxSetParent(
  283. PWND pwnd,
  284. PWND pwndNewParent)
  285. {
  286. POINT pt;
  287. BOOL fVisible;
  288. PWND pwndOldParent, pwndOldRedirectedParent, pwndNewRedirectedParent;
  289. TL tlpwndOldParent;
  290. TL tlpwndNewParent;
  291. PVOID pvRet;
  292. PWND pwndDesktop;
  293. PWND pwndT;
  294. int flags = SWP_NOZORDER | SWP_NOSIZE;
  295. CheckLock(pwnd);
  296. CheckLock(pwndNewParent);
  297. if (!ValidateParentDepth(pwnd, pwndNewParent)) {
  298. RIPERR0(ERROR_INVALID_PARAMETER, RIP_WARNING, "Exceeded nested children limit");
  299. return NULL;
  300. }
  301. pwndDesktop = PWNDDESKTOP(pwnd);
  302. /*
  303. * In 1.0x, an app's parent was null, but now it is pwndDesktop.
  304. * Need to remember to lock pwndNewParent because we're reassigning
  305. * it here.
  306. */
  307. if (pwndNewParent == NULL)
  308. pwndNewParent = pwndDesktop;
  309. /*
  310. * Don't ever change the parent of the desktop.
  311. */
  312. if ((pwnd == pwndDesktop) || (pwnd == PWNDMESSAGE(pwnd))) {
  313. RIPERR0(ERROR_ACCESS_DENIED,
  314. RIP_WARNING,
  315. "Access denied: can't change parent of the desktop");
  316. return NULL;
  317. }
  318. /*
  319. * Don't let the window become its own parent, grandparent, etc.
  320. */
  321. for (pwndT = pwndNewParent; pwndT != NULL; pwndT = pwndT->spwndParent) {
  322. if (pwnd == pwndT) {
  323. RIPERR0(ERROR_INVALID_PARAMETER, RIP_WARNING,
  324. "Attempting to create a parent-child relationship loop.");
  325. return NULL;
  326. }
  327. }
  328. /*
  329. * Don't let the window become the parent of its owner, or of its owner's
  330. * owner, etc. This throws ZOrderByOwner2 into an infinite loop.
  331. */
  332. for (pwndT = pwndNewParent->spwndOwner; pwndT != NULL; pwndT = pwndT->spwndOwner) {
  333. if (pwnd == pwndT) {
  334. RIPERR0(ERROR_INVALID_PARAMETER,
  335. RIP_WARNING,
  336. "Attempting to create a parent-owner relationship loop.");
  337. return NULL;
  338. }
  339. }
  340. /*
  341. * We still need pwndNewParent across callbacks... and even though
  342. * it was passed in, it may have been reassigned above.
  343. */
  344. ThreadLock(pwndNewParent, &tlpwndNewParent);
  345. /*
  346. * Make the thing disappear from original parent.
  347. */
  348. fVisible = xxxShowWindow(pwnd, MAKELONG(SW_HIDE, TEST_PUDF(PUDF_ANIMATE)));
  349. /*
  350. * Ensure that the window being changed and the new parent
  351. * are not in a destroyed state.
  352. *
  353. * IMPORTANT: After this check, do not leave the critical section
  354. * until the window links have been rearranged.
  355. */
  356. if (TestWF(pwnd, WFDESTROYED) || TestWF(pwndNewParent, WFDESTROYED)) {
  357. ThreadUnlock(&tlpwndNewParent);
  358. return NULL;
  359. }
  360. pwndOldRedirectedParent = GetStyleWindow(pwnd, WEFPREDIRECTED);
  361. pwndNewRedirectedParent = GetStyleWindow(pwndNewParent, WEFPREDIRECTED);
  362. if ((pwndOldRedirectedParent != NULL) && (pwndOldRedirectedParent != pwnd)
  363. && (pwndNewRedirectedParent == NULL)) {
  364. ConvertRedirectionDCs(pwnd, NULL);
  365. }
  366. pwndOldParent = pwnd->spwndParent;
  367. ThreadLock(pwndOldParent, &tlpwndOldParent);
  368. if (TestWF(pwndOldParent, WEFLAYOUTRTL)) {
  369. pt.x = pwnd->rcWindow.right;
  370. } else {
  371. pt.x = pwnd->rcWindow.left;
  372. }
  373. pt.y = pwnd->rcWindow.top;
  374. _ScreenToClient(pwndOldParent, &pt);
  375. UnlinkWindow(pwnd, pwndOldParent);
  376. Lock(&pwnd->spwndParent, pwndNewParent);
  377. if (pwndNewParent == PWNDDESKTOP(pwnd) && !TestWF(pwnd, WEFTOPMOST)) {
  378. /*
  379. * Make sure a child who's owner is topmost inherits the topmost
  380. * bit. - win31 bug 7568
  381. */
  382. if (TestWF(pwnd, WFCHILD) &&
  383. (pwnd->spwndOwner) &&
  384. TestWF(pwnd->spwndOwner, WEFTOPMOST)) {
  385. SetWF(pwnd, WEFTOPMOST);
  386. }
  387. /*
  388. * BACKWARD COMPATIBILITY HACK ALERT
  389. *
  390. * All top level windows must be WS_CLIPSIBLINGs bit set.
  391. * The SDM ComboBox() code calls SetParent() with a listbox
  392. * window that does not have this set. This causes problems
  393. * with InternalInvalidate2() because it does not subtract off
  394. * the window from the desktop's update region.
  395. *
  396. * We must invalidate the DC cache here, too, because if there is
  397. * a cache entry lying around, its clipping region will be incorrect.
  398. */
  399. if ((pwndNewParent == _GetDesktopWindow()) &&
  400. !TestWF(pwnd, WFCLIPSIBLINGS)) {
  401. SetWF(pwnd, WFCLIPSIBLINGS);
  402. zzzInvalidateDCCache(pwnd, IDC_DEFAULT);
  403. }
  404. /*
  405. * This is a top level window but it isn't a topmost window so we
  406. * have to link it below all topmost windows.
  407. */
  408. LinkWindow(pwnd,
  409. CalcForegroundInsertAfter(pwnd),
  410. pwndNewParent);
  411. } else {
  412. /*
  413. * If this is a child window or if this is a TOPMOST window, we can
  414. * link at the head of the parent chain.
  415. */
  416. LinkWindow(pwnd, NULL, pwndNewParent);
  417. }
  418. /*
  419. * If we're a child window, do any necessary attaching and
  420. * detaching.
  421. */
  422. if (TestwndChild(pwnd)) {
  423. /*
  424. * Make sure we're not a WFCHILD window that got SetParent()'ed
  425. * to the desktop.
  426. */
  427. if ((pwnd->spwndParent != PWNDDESKTOP(pwnd)) &&
  428. GETPTI(pwnd) != GETPTI(pwndOldParent)) {
  429. zzzAttachThreadInput(GETPTI(pwnd), GETPTI(pwndOldParent), FALSE);
  430. }
  431. /*
  432. * If the new parent window is on a different thread, and also
  433. * isn't the desktop window, attach ourselves appropriately.
  434. */
  435. if (pwndNewParent != PWNDDESKTOP(pwnd) &&
  436. GETPTI(pwnd) != GETPTI(pwndNewParent)) {
  437. zzzAttachThreadInput(GETPTI(pwnd), GETPTI(pwndNewParent), TRUE);
  438. }
  439. }
  440. /*
  441. * If we are moving under a WS_EX_COMPOSITED parent-chain, we need to turn
  442. * off any child windows in the subtree that are WS_EX_COMPOSITED.
  443. */
  444. if (GetStyleWindow(pwnd->spwndParent, WEFCOMPOSITED) != NULL) {
  445. xxxTurnOffCompositing(pwnd, FALSE);
  446. }
  447. if (pwndNewParent == PWNDMESSAGE(pwnd) || pwndOldParent == PWNDMESSAGE(pwnd))
  448. flags |= SWP_NOACTIVATE;
  449. xxxWindowEvent(EVENT_OBJECT_PARENTCHANGE, pwnd, OBJID_WINDOW,
  450. INDEXID_CONTAINER, WEF_USEPWNDTHREAD);
  451. /*
  452. * We mustn't return an invalid pwndOldParent
  453. */
  454. xxxSetWindowPos(pwnd, NULL, pt.x, pt.y, 0, 0, flags);
  455. if (fVisible) {
  456. xxxShowWindow(pwnd, MAKELONG(SW_SHOWNORMAL, TEST_PUDF(PUDF_ANIMATE)));
  457. }
  458. /*
  459. * returns pwndOldParent if still valid, else NULL.
  460. */
  461. pvRet = ThreadUnlock(&tlpwndOldParent);
  462. ThreadUnlock(&tlpwndNewParent);
  463. return pvRet;
  464. }
  465. /***************************************************************************\
  466. * xxxFindWindowEx (API)
  467. *
  468. * Searches for a window among top level windows. The keys used are pszClass,
  469. * (the class name) and/or pszName, (the window title name). Either can be
  470. * NULL.
  471. *
  472. * History:
  473. * 06-Jun-1994 JohnL Converted xxxFindWindow to xxxFindWindowEx
  474. * 10-Nov-1992 mikeke Added 16bit and 32bit only flag
  475. * 24-Sep-1990 DarrinM Generated stubs.
  476. * 02-Jun-1991 ScottLu Ported from Win3.
  477. * 19-Feb-1991 JimA Added enum access check
  478. \***************************************************************************/
  479. #define CCHMAXNAME 80
  480. PWND _FindWindowEx(
  481. PWND pwndParent,
  482. PWND pwndChild,
  483. LPCWSTR ccxlpszClass,
  484. LPCWSTR ccxlpszName,
  485. DWORD dwType)
  486. {
  487. /*
  488. * Note that the Class and Name pointers are client-side addresses.
  489. */
  490. PBWL pbwl;
  491. HWND *phwnd;
  492. PWND pwnd;
  493. WORD atomClass = 0;
  494. LPCWSTR lpName;
  495. BOOL fTryMessage = FALSE;
  496. if (ccxlpszClass != NULL) {
  497. /*
  498. * note that we do a version-less check here, then call FindClassAtom right away.
  499. */
  500. atomClass = FindClassAtom(ccxlpszClass);
  501. if (atomClass == 0) {
  502. return NULL;
  503. }
  504. }
  505. /*
  506. * Setup parent window
  507. */
  508. if (!pwndParent) {
  509. pwndParent = _GetDesktopWindow();
  510. /*
  511. * If we are starting from the root and no child window
  512. * was specified, then check the message window tree too
  513. * in case we don't find it on the desktop tree.
  514. */
  515. if (!pwndChild)
  516. fTryMessage = TRUE;
  517. }
  518. TryAgain:
  519. /*
  520. * Setup first child
  521. */
  522. if (!pwndChild) {
  523. pwndChild = pwndParent->spwndChild;
  524. } else {
  525. if (pwndChild->spwndParent != pwndParent) {
  526. RIPMSG0(RIP_WARNING,
  527. "FindWindowEx: Child window doesn't have proper parent");
  528. return NULL;
  529. }
  530. pwndChild = pwndChild->spwndNext;
  531. }
  532. /*
  533. * Generate a list of top level windows.
  534. */
  535. if ((pbwl = BuildHwndList(pwndChild, BWL_ENUMLIST, NULL)) == NULL) {
  536. return NULL;
  537. }
  538. /*
  539. * Set pwnd to NULL in case the window list is empty.
  540. */
  541. pwnd = NULL;
  542. try {
  543. for (phwnd = pbwl->rghwnd; *phwnd != (HWND)1; phwnd++) {
  544. /*
  545. * Validate this hwnd since we left the critsec earlier (below
  546. * in the loop we send a message!
  547. */
  548. if ((pwnd = RevalidateHwnd(*phwnd)) == NULL)
  549. continue;
  550. /*
  551. * make sure this window is of the right type
  552. */
  553. if (dwType != FW_BOTH) {
  554. if (((dwType == FW_16BIT) && !(GETPTI(pwnd)->TIF_flags & TIF_16BIT)) ||
  555. ((dwType == FW_32BIT) && (GETPTI(pwnd)->TIF_flags & TIF_16BIT)))
  556. continue;
  557. }
  558. /*
  559. * If the class is specified and doesn't match, skip this window
  560. * note that we do a version-less check here, use pcls->atomNVClassName
  561. */
  562. if (!atomClass || (atomClass == pwnd->pcls->atomNVClassName)) {
  563. if (!ccxlpszName)
  564. break;
  565. if (pwnd->strName.Length) {
  566. lpName = pwnd->strName.Buffer;
  567. } else {
  568. lpName = szNull;
  569. }
  570. /*
  571. * Is the text the same? If so, return with this window!
  572. */
  573. if (_wcsicmp(ccxlpszName, lpName) == 0)
  574. break;
  575. }
  576. /*
  577. * The window did not match.
  578. */
  579. pwnd = NULL;
  580. }
  581. } except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
  582. pwnd = NULL;
  583. }
  584. FreeHwndList(pbwl);
  585. if (!pwnd && fTryMessage) {
  586. fTryMessage = FALSE;
  587. pwndParent = _GetMessageWindow();
  588. pwndChild = NULL;
  589. goto TryAgain;
  590. }
  591. return ((*phwnd == (HWND)1) ? NULL : pwnd);
  592. }
  593. /***************************************************************************\
  594. * UpdateCheckpoint
  595. *
  596. * Checkpoints the current window size/position/state and returns a pointer
  597. * to the structure.
  598. *
  599. * History:
  600. \***************************************************************************/
  601. PCHECKPOINT UpdateCheckpoint(
  602. PWND pwnd)
  603. {
  604. RECT rc;
  605. GetRect(pwnd, &rc, GRECT_WINDOW | GRECT_PARENTCOORDS);
  606. return CkptRestore(pwnd, &rc);
  607. }
  608. /***************************************************************************\
  609. * GetWindowPlacement
  610. *
  611. * History:
  612. * 02-Mar-1992 MikeKe From Win 3.1
  613. \***************************************************************************/
  614. BOOL _GetWindowPlacement(
  615. PWND pwnd,
  616. PWINDOWPLACEMENT pwp)
  617. {
  618. CHECKPOINT * pcp;
  619. /*
  620. * this will set the normal or the minimize point in the checkpoint,
  621. * so that all elements will be up to date.
  622. */
  623. pcp = UpdateCheckpoint(pwnd);
  624. if (!pcp)
  625. return FALSE;
  626. if (TestWF(pwnd, WFMINIMIZED)) {
  627. pwp->showCmd = SW_SHOWMINIMIZED;
  628. } else if (TestWF(pwnd, WFMAXIMIZED)) {
  629. pwp->showCmd = SW_SHOWMAXIMIZED;
  630. } else {
  631. pwp->showCmd = SW_SHOWNORMAL;
  632. }
  633. CopyRect(&pwp->rcNormalPosition, &pcp->rcNormal);
  634. if (pcp->fMinInitialized) {
  635. pwp->ptMinPosition = pcp->ptMin;
  636. } else {
  637. pwp->ptMinPosition.x = pwp->ptMinPosition.y = -1;
  638. }
  639. /*
  640. * We never ever save the position of "normal" maximized windows. Other
  641. * wise, when the size border changes dimensions, the max pos would be
  642. * invalid, and you would never be able to reset it.
  643. */
  644. if (pcp->fMaxInitialized && !TestWF(pwnd, WFREALLYMAXIMIZABLE)) {
  645. pwp->ptMaxPosition = pcp->ptMax;
  646. } else {
  647. pwp->ptMaxPosition.x = pwp->ptMaxPosition.y = -1;
  648. }
  649. if ((pwnd->spwndParent == PWNDDESKTOP(pwnd)) &&
  650. !TestWF(pwnd, WEFTOOLWINDOW)) {
  651. PMONITOR pMonitor;
  652. pMonitor = _MonitorFromRect(&pwp->rcNormalPosition, MONITOR_DEFAULTTOPRIMARY);
  653. /*
  654. * Convert min, normal positions to be relative to the working area.
  655. * The max pos already is (always is saved that way).
  656. *
  657. * working area, except for maximized position, which is always
  658. * working area relative.
  659. */
  660. if (pcp->fMinInitialized) {
  661. pwp->ptMinPosition.x -= (pMonitor->rcWork.left - pMonitor->rcMonitor.left);
  662. pwp->ptMinPosition.y -= (pMonitor->rcWork.top - pMonitor->rcMonitor.top);
  663. }
  664. OffsetRect(&pwp->rcNormalPosition,
  665. pMonitor->rcMonitor.left - pMonitor->rcWork.left,
  666. pMonitor->rcMonitor.top - pMonitor->rcWork.top);
  667. }
  668. pwp->flags = 0;
  669. /*
  670. * B#3276
  671. * Don't allow WPF_SETMINPOSITION on top-level windows.
  672. */
  673. if (TestwndChild(pwnd) && pcp->fDragged)
  674. pwp->flags |= WPF_SETMINPOSITION;
  675. if (pcp->fWasMaximizedBeforeMinimized || TestWF(pwnd, WFMAXIMIZED))
  676. pwp->flags |= WPF_RESTORETOMAXIMIZED;
  677. pwp->length = sizeof(WINDOWPLACEMENT);
  678. return TRUE;
  679. }
  680. /***************************************************************************\
  681. * CheckPlacementBounds
  682. *
  683. * History:
  684. * 02-Mar-1992 MikeKe From Win 3.1
  685. \***************************************************************************/
  686. VOID CheckPlacementBounds(
  687. LPRECT lprc,
  688. LPPOINT ptMin,
  689. LPPOINT ptMax,
  690. PMONITOR pMonitor)
  691. {
  692. int xIcon;
  693. int yIcon;
  694. int sTop;
  695. int sBottom;
  696. int sLeft;
  697. int sRight;
  698. /*
  699. * Check Normal Window Placement
  700. */
  701. /*
  702. * Possible values for these sign variables are :
  703. * -1 : less than the minimum for that dimension
  704. * 0 : within the range for that dimension
  705. * 1 : more than the maximum for that dimension
  706. */
  707. sTop = (lprc->top < pMonitor->rcWork.top) ? -1 :
  708. ((lprc->top > pMonitor->rcWork.bottom) ? 1 : 0);
  709. sBottom = (lprc->bottom < pMonitor->rcWork.top) ? -1 :
  710. ((lprc->bottom > pMonitor->rcWork.bottom) ? 1 : 0);
  711. sLeft = (lprc->left < pMonitor->rcWork.left) ? -1 :
  712. ((lprc->left > pMonitor->rcWork.right) ? 1 : 0);
  713. sRight = (lprc->right < pMonitor->rcWork.left) ? -1 :
  714. ((lprc->right > pMonitor->rcWork.right) ? 1 : 0);
  715. if ((sTop * sBottom > 0) || (sLeft * sRight > 0)) {
  716. /*
  717. * Window is TOTALLY outside monitor bounds. The resolution and/or
  718. * configuration of monitors probably changed since the last time
  719. * we ran this app.
  720. *
  721. * Slide it FULLY onto the monitor at the nearest position.
  722. */
  723. int size;
  724. if (sTop < 0) {
  725. lprc->bottom -= lprc->top;
  726. lprc->top = pMonitor->rcWork.top;
  727. } else if (sBottom > 0) {
  728. size = lprc->bottom - lprc->top;
  729. lprc->top = max(pMonitor->rcWork.bottom - size, pMonitor->rcWork.top);
  730. lprc->bottom = lprc->top + size;
  731. }
  732. if (sLeft < 0) {
  733. lprc->right -= lprc->left;
  734. lprc->left = pMonitor->rcWork.left;
  735. } else if (sRight > 0) {
  736. size = lprc->right - lprc->left;
  737. lprc->left = max(pMonitor->rcWork.right - size, pMonitor->rcWork.left);
  738. lprc->right = lprc->left + size;
  739. }
  740. }
  741. /*
  742. * Check Iconic Window Placement
  743. */
  744. if (ptMin->x != -1) {
  745. xIcon = SYSMET(CXMINSPACING);
  746. yIcon = SYSMET(CYMINSPACING);
  747. sTop = (ptMin->y < pMonitor->rcWork.top) ? -1 :
  748. ((ptMin->y > pMonitor->rcWork.bottom) ? 1 : 0);
  749. sBottom = (ptMin->y + yIcon < pMonitor->rcWork.top) ? -1 :
  750. ((ptMin->y + yIcon > pMonitor->rcWork.bottom) ? 1 : 0);
  751. sLeft = (ptMin->x < pMonitor->rcWork.left) ? -1 :
  752. ((ptMin->x > pMonitor->rcWork.right) ? 1 : 0);
  753. sRight = (ptMin->x + xIcon < pMonitor->rcWork.left) ? -1 :
  754. ((ptMin->x + xIcon > pMonitor->rcWork.right) ? 1 : 0);
  755. /*
  756. * Icon is TOTALLY outside monitor bounds; repark it.
  757. */
  758. if ((sTop * sBottom > 0) || (sLeft * sRight > 0))
  759. ptMin->x = ptMin->y = -1;
  760. }
  761. /*
  762. * Check Maximized Window Placement
  763. */
  764. if (ptMax->x != -1 &&
  765. (ptMax->x + pMonitor->rcWork.left >= pMonitor->rcWork.right ||
  766. ptMax->y + pMonitor->rcWork.top >= pMonitor->rcWork.bottom)) {
  767. /*
  768. * window is TOTALLY below beyond maximum dimensions; zero the
  769. * position so that the window will at least be clipped to the
  770. * monitor.
  771. */
  772. ptMax->x = 0;
  773. ptMax->y = 0;
  774. }
  775. }
  776. /***************************************************************************\
  777. * WPUpdateCheckPointSettings
  778. *
  779. * History:
  780. * 02/23/98 GerardoB Extracted from xxxSetWindowPlacement
  781. \***************************************************************************/
  782. void WPUpdateCheckPointSettings (PWND pwnd, UINT uWPFlags)
  783. {
  784. CHECKPOINT * pcp;
  785. UserAssert(TestWF(pwnd, WFMINIMIZED));
  786. if (pcp = UpdateCheckpoint(pwnd)) {
  787. /*
  788. * Save settings in the checkpoint struct
  789. */
  790. if (uWPFlags & WPF_SETMINPOSITION)
  791. pcp->fDragged = TRUE;
  792. if (uWPFlags & WPF_RESTORETOMAXIMIZED) {
  793. pcp->fWasMaximizedBeforeMinimized = TRUE;
  794. } else {
  795. pcp->fWasMaximizedBeforeMinimized = FALSE;
  796. }
  797. }
  798. }
  799. /***************************************************************************\
  800. * xxxSetWindowPlacement
  801. *
  802. * History:
  803. * 02-Mar-1992 MikeKe From Win 3.1
  804. \***************************************************************************/
  805. BOOL xxxSetWindowPlacement(
  806. PWND pwnd,
  807. PWINDOWPLACEMENT pwp)
  808. {
  809. CHECKPOINT * pcp;
  810. PMONITOR pMonitor;
  811. RECT rc;
  812. POINT ptMin;
  813. POINT ptMax;
  814. BOOL fMin;
  815. BOOL fMax;
  816. UINT uSWPFlags;
  817. BOOL fRealAsync;
  818. CheckLock(pwnd);
  819. CopyRect(&rc, &pwp->rcNormalPosition);
  820. if (pwnd->spwndParent == PWNDDESKTOP(pwnd)) {
  821. pMonitor = _MonitorFromRect(&rc, MONITOR_DEFAULTTOPRIMARY);
  822. }
  823. ptMin = pwp->ptMinPosition;
  824. fMin = ((ptMin.x != -1) && (ptMin.y != -1));
  825. ptMax = pwp->ptMaxPosition;
  826. fMax = ((ptMax.x != -1) && (ptMax.y != -1));
  827. /*
  828. * Convert back to working rectangle coordinates
  829. */
  830. if ( pwnd->spwndParent == PWNDDESKTOP(pwnd) &&
  831. !TestWF(pwnd, WEFTOOLWINDOW)) {
  832. OffsetRect(
  833. &rc,
  834. pMonitor->rcWork.left - pMonitor->rcMonitor.left,
  835. pMonitor->rcWork.top - pMonitor->rcMonitor.top);
  836. if (fMin) {
  837. ptMin.x += pMonitor->rcWork.left - pMonitor->rcMonitor.left;
  838. ptMin.y += pMonitor->rcWork.top - pMonitor->rcMonitor.top;
  839. }
  840. CheckPlacementBounds(&rc, &ptMin, &ptMax, pMonitor);
  841. }
  842. if (pcp = UpdateCheckpoint(pwnd)) {
  843. /*
  844. * Save settings in the checkpoint struct
  845. */
  846. CopyRect(&pcp->rcNormal, &rc);
  847. pcp->ptMin = ptMin;
  848. pcp->fMinInitialized = fMin;
  849. pcp->fDragged = (pwp->flags & WPF_SETMINPOSITION) ?
  850. TRUE : FALSE;
  851. pcp->ptMax = ptMax;
  852. pcp->fMaxInitialized = fMax;
  853. pcp->fWasMaximizedBeforeMinimized = FALSE;
  854. }
  855. /*
  856. * WPF_ASYNCWINDOWPLACEMENT new for NT5.
  857. */
  858. uSWPFlags = SWP_NOZORDER | SWP_NOACTIVATE
  859. | ((pwp->flags & WPF_ASYNCWINDOWPLACEMENT) ? SWP_ASYNCWINDOWPOS : 0);
  860. if (TestWF(pwnd, WFMINIMIZED)) {
  861. if ((!pcp || pcp->fDragged) && fMin) {
  862. xxxSetWindowPos(pwnd,
  863. PWND_TOP,
  864. ptMin.x,
  865. ptMin.y,
  866. 0,
  867. 0,
  868. SWP_NOSIZE | uSWPFlags);
  869. }
  870. } else if (TestWF(pwnd, WFMAXIMIZED)) {
  871. if (pcp != NULL) {
  872. if (TestWF(pwnd, WFREALLYMAXIMIZABLE))
  873. pcp->fMaxInitialized = FALSE;
  874. if (pcp->fMaxInitialized) {
  875. if (pwnd->spwndParent == PWNDDESKTOP(pwnd)) {
  876. ptMax.x += pMonitor->rcWork.left;
  877. ptMax.y += pMonitor->rcWork.top;
  878. }
  879. xxxSetWindowPos(pwnd,
  880. PWND_TOP,
  881. ptMax.x,
  882. ptMax.y,
  883. 0,
  884. 0,
  885. SWP_NOSIZE | uSWPFlags);
  886. }
  887. }
  888. } else {
  889. xxxSetWindowPos(pwnd,
  890. PWND_TOP,
  891. rc.left,
  892. rc.top,
  893. rc.right - rc.left,
  894. rc.bottom - rc.top,
  895. uSWPFlags);
  896. }
  897. /*
  898. * xxxSetWindowPos is only assync when the window's thread is on a
  899. * different queue than the current thread's. See AsyncWindowPos.
  900. */
  901. fRealAsync = (pwp->flags & WPF_ASYNCWINDOWPLACEMENT)
  902. && (GETPTI(pwnd)->pq != PtiCurrent()->pq);
  903. if (fRealAsync) {
  904. _ShowWindowAsync(pwnd, pwp->showCmd, pwp->flags);
  905. } else {
  906. xxxShowWindow(pwnd, MAKELONG(pwp->showCmd, TEST_PUDF(PUDF_ANIMATE)));
  907. }
  908. if (TestWF(pwnd, WFMINIMIZED) && !fRealAsync) {
  909. WPUpdateCheckPointSettings(pwnd, pwp->flags);
  910. }
  911. return TRUE;
  912. }
  913. /***************************************************************************\
  914. * xxxSetInternalWindowPos
  915. *
  916. * Sets a window to the size, position and state it was most recently
  917. * in. Side effect (possibly bug): shows and activates the window as well.
  918. *
  919. * History:
  920. * 28-Mar-1991 DavidPe Ported from Win 3.1 sources.
  921. \***************************************************************************/
  922. BOOL xxxSetInternalWindowPos(
  923. PWND pwnd,
  924. UINT cmdShow,
  925. LPRECT lprcWin,
  926. LPPOINT lpptMin)
  927. {
  928. CHECKPOINT * pcp;
  929. PMONITOR pMonitor;
  930. CheckLock(pwnd);
  931. if ((pcp = UpdateCheckpoint(pwnd)) == NULL) {
  932. return FALSE;
  933. }
  934. if (lprcWin) {
  935. pcp->rcNormal = *lprcWin;
  936. if (pwnd->spwndParent == PWNDDESKTOP(pwnd)) {
  937. pMonitor = _MonitorFromRect(lprcWin, MONITOR_DEFAULTTOPRIMARY);
  938. OffsetRect(
  939. &pcp->rcNormal,
  940. pMonitor->rcWork.left - pMonitor->rcMonitor.left,
  941. pMonitor->rcWork.top - pMonitor->rcMonitor.top);
  942. }
  943. }
  944. if (lpptMin && (lpptMin->x != -1)) {
  945. pcp->ptMin = *lpptMin;
  946. if (pwnd->spwndParent == PWNDDESKTOP(pwnd)) {
  947. pMonitor = _MonitorFromRect(&pcp->rcNormal, MONITOR_DEFAULTTOPRIMARY);
  948. pcp->ptMin.x += pMonitor->rcWork.left - pMonitor->rcMonitor.left;
  949. pcp->ptMin.y += pMonitor->rcWork.top - pMonitor->rcMonitor.top;
  950. }
  951. pcp->fDragged = TRUE;
  952. pcp->fMinInitialized = TRUE;
  953. } else {
  954. pcp->fMinInitialized = FALSE;
  955. pcp->fDragged = FALSE;
  956. }
  957. if (TestWF(pwnd, WFMINIMIZED)) {
  958. /*
  959. * need to move the icon
  960. */
  961. if (pcp->fMinInitialized) {
  962. xxxSetWindowPos(pwnd,
  963. PWND_TOP,
  964. pcp->ptMin.x,
  965. pcp->ptMin.y,
  966. 0,
  967. 0,
  968. SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
  969. }
  970. } else if (!TestWF(pwnd, WFMAXIMIZED) && lprcWin) {
  971. /*
  972. * need to set the size and the position
  973. */
  974. xxxSetWindowPos(pwnd,
  975. NULL,
  976. lprcWin->left,
  977. lprcWin->top,
  978. lprcWin->right - lprcWin->left,
  979. lprcWin->bottom - lprcWin->top,
  980. SWP_NOZORDER);
  981. }
  982. xxxShowWindow(pwnd, MAKELONG(cmdShow, TEST_PUDF(PUDF_ANIMATE)));
  983. return TRUE;
  984. }
  985. /***************************************************************************\
  986. * _GetDesktopWindow (API)
  987. *
  988. * History:
  989. * 07-Nov-1990 DarrinM Implemented.
  990. \***************************************************************************/
  991. PWND _GetDesktopWindow(VOID)
  992. {
  993. PTHREADINFO pti = PtiCurrent();
  994. PDESKTOPINFO pdi;
  995. if (pti == NULL)
  996. return NULL;
  997. pdi = pti->pDeskInfo;
  998. return pdi == NULL ? NULL : pdi->spwnd;
  999. }
  1000. /***************************************************************************\
  1001. * _GetDesktopWindow (API)
  1002. *
  1003. * History:
  1004. * 07-Nov-1990 DarrinM Implemented.
  1005. \***************************************************************************/
  1006. PWND _GetMessageWindow(VOID)
  1007. {
  1008. PTHREADINFO pti = PtiCurrent();
  1009. PDESKTOP pdi;
  1010. if (pti == NULL)
  1011. return NULL;
  1012. pdi = pti->rpdesk;
  1013. return pdi == NULL ? NULL : pdi->spwndMessage;
  1014. }
  1015. /**************************************************************************\
  1016. * TestWindowProcess
  1017. *
  1018. * History:
  1019. * 14-Nov-1994 JimA Created.
  1020. \**************************************************************************/
  1021. BOOL TestWindowProcess(
  1022. PWND pwnd)
  1023. {
  1024. return (PpiCurrent() == GETPTI(pwnd)->ppi);
  1025. }
  1026. /***************************************************************************\
  1027. * ValidateDepth
  1028. *
  1029. * The function conveniently simulates recursion by utilizing the fact
  1030. * that from any sibling in the Next chain we can correctly get to the
  1031. * parent window and that two siblings in the Next chain cannot have
  1032. * different parents.
  1033. *
  1034. * 12-Mar-1997 vadimg created
  1035. \***************************************************************************/
  1036. #define NESTED_WINDOW_LIMIT 100
  1037. BOOL ValidateParentDepth(PWND pwnd, PWND pwndParent)
  1038. {
  1039. UINT cDepth = 1, cDepthMax;
  1040. PWND pwndStop;
  1041. /*
  1042. * Calculate the depth of the parent chain.
  1043. */
  1044. while (pwndParent != NULL) {
  1045. pwndParent = pwndParent->spwndParent;
  1046. cDepth++;
  1047. }
  1048. cDepthMax = cDepth;
  1049. /*
  1050. * When pwnd is NULL, it means that we want to add one more
  1051. * level to the existing depth of pwndParent.
  1052. */
  1053. if (pwnd == NULL || pwnd->spwndChild == NULL) {
  1054. goto Exit;
  1055. } else {
  1056. pwndStop = pwnd->spwndParent;
  1057. }
  1058. Restart:
  1059. if (pwnd->spwndChild != NULL) {
  1060. pwnd = pwnd->spwndChild;
  1061. cDepth++;
  1062. } else if (pwnd->spwndNext != NULL) {
  1063. pwnd = pwnd->spwndNext;
  1064. } else {
  1065. if (cDepth > cDepthMax) {
  1066. cDepthMax = cDepth;
  1067. }
  1068. /*
  1069. * Find a parent with siblings and recurse on them. Terminate
  1070. * when we reach the parent of the original pwnd.
  1071. */
  1072. do {
  1073. pwnd = pwnd->spwndParent;
  1074. cDepth--;
  1075. if (pwnd == pwndStop)
  1076. goto Exit;
  1077. } while (pwnd->spwndNext == NULL);
  1078. pwnd = pwnd->spwndNext;
  1079. }
  1080. goto Restart;
  1081. Exit:
  1082. return (cDepthMax <= NESTED_WINDOW_LIMIT);
  1083. }
  1084. /***************************************************************************\
  1085. * ValidateOwnerDepth
  1086. *
  1087. * pwndOwner is the new intended owner, we basically add 1 to the current
  1088. * nested owner chain depth. We assume that the actual window does not have
  1089. * any ownees. In reality, it can through SetWindowLong, but finding the
  1090. * maximum depth of the ownee chain is really tricky - just look in swp.c.
  1091. *
  1092. * 12-Mar-1997 vadimg created
  1093. \***************************************************************************/
  1094. BOOL ValidateOwnerDepth(PWND pwnd, PWND pwndOwner)
  1095. {
  1096. UINT cDepth = 1;
  1097. while (pwndOwner != NULL) {
  1098. /*
  1099. * Do not allow loops in the owner chain.
  1100. */
  1101. if (pwndOwner == pwnd) {
  1102. return FALSE;
  1103. }
  1104. pwndOwner = pwndOwner->spwndOwner;
  1105. cDepth++;
  1106. }
  1107. return (cDepth <= NESTED_WINDOW_LIMIT);
  1108. }