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.

2054 lines
62 KiB

  1. /****************************** Module Header ******************************\
  2. * Module Name: minmax.c
  3. *
  4. * Copyright (c) 1985 - 1999, Microsoft Corporation
  5. *
  6. * Window Minimize/Maximize Routines
  7. *
  8. \***************************************************************************/
  9. #include "precomp.h"
  10. #pragma hdrstop
  11. /*
  12. * How long we want animation to last, in milliseconds
  13. */
  14. #define CMS_ANIMATION 250
  15. #define DX_GAP (SYSMET(CXMINSPACING) - SYSMET(CXMINIMIZED))
  16. #define DY_GAP (SYSMET(CYMINSPACING) - SYSMET(CYMINIMIZED))
  17. /***************************************************************************\
  18. * xxxInitSendValidateMinMaxInfo()
  19. *
  20. * Routine which initializes the minmax array, sends WM_GETMINMAXINFO to
  21. * the caller, and validates the results.
  22. *
  23. * Returns FALSE is the window went away in the middle.
  24. *
  25. \***************************************************************************/
  26. void
  27. xxxInitSendValidateMinMaxInfo(PWND pwnd, LPMINMAXINFO lpmmi)
  28. {
  29. PTHREADINFO ptiCurrent;
  30. PMONITOR pMonitorReal;
  31. PMONITOR pMonitorPrimary;
  32. TL tlpMonitorReal;
  33. TL tlpMonitorPrimary;
  34. CHECKPOINT * pcp;
  35. RECT rcParent;
  36. int cBorders;
  37. int xMin, yMin;
  38. BOOL bTopLevel;
  39. CheckLock(pwnd);
  40. ptiCurrent = PtiCurrent();
  41. /*
  42. * FILL IN THE MINMAXINFO WE THINK IS APPROPRIATE
  43. */
  44. /*
  45. * Minimized Size
  46. */
  47. lpmmi->ptReserved.x = SYSMET(CXMINIMIZED);
  48. lpmmi->ptReserved.y = SYSMET(CYMINIMIZED);
  49. /*
  50. * Maximized Position and Size
  51. * Figure out where the window would be maximized within its parent.
  52. */
  53. pMonitorPrimary = GetPrimaryMonitor();
  54. /*
  55. * [msadek], #31003
  56. * Cache window parent status in case some code reparents
  57. * the window during WM_GETMINMAXINFO
  58. */
  59. if (bTopLevel = (pwnd->spwndParent == PWNDDESKTOP(pwnd))) {
  60. /* What monitor is the window really going to maximize to? */
  61. pMonitorReal = _MonitorFromWindow(pwnd, MONITOR_DEFAULTTOPRIMARY);
  62. /* Send dimensions based on the primary only. */
  63. rcParent = pMonitorPrimary->rcMonitor;
  64. } else {
  65. pMonitorReal = NULL;
  66. _GetClientRect(pwnd->spwndParent, &rcParent);
  67. }
  68. cBorders = GetWindowBorders(pwnd->style, pwnd->ExStyle, TRUE, FALSE);
  69. InflateRect(&rcParent,
  70. cBorders * SYSMET(CXBORDER),
  71. cBorders * SYSMET(CYBORDER));
  72. rcParent.right -= rcParent.left;
  73. rcParent.bottom -= rcParent.top;
  74. /* rcParent.right, bottom are width and height now. */
  75. lpmmi->ptMaxSize.x = rcParent.right;
  76. lpmmi->ptMaxSize.y = rcParent.bottom;
  77. pcp = (CHECKPOINT *)_GetProp(pwnd, PROP_CHECKPOINT, PROPF_INTERNAL);
  78. if (pcp && pcp->fMaxInitialized) {
  79. /*
  80. * Note: For top level windows, we will fix this point up after
  81. * the fact if it has gotten out of date because the size border
  82. * changed.
  83. */
  84. lpmmi->ptMaxPosition = pcp->ptMax;
  85. } else {
  86. lpmmi->ptMaxPosition = *((LPPOINT)&rcParent.left);
  87. }
  88. /*
  89. * Normal minimum tracking size
  90. * Only enforce min tracking size for windows with captions
  91. */
  92. xMin = cBorders*SYSMET(CXEDGE);
  93. yMin = cBorders*SYSMET(CYEDGE);
  94. if (TestWF(pwnd, WFCAPTION) && !TestWF(pwnd, WEFTOOLWINDOW)) {
  95. lpmmi->ptMinTrackSize.x = SYSMET(CXMINTRACK);
  96. lpmmi->ptMinTrackSize.y = SYSMET(CYMINTRACK);
  97. } else {
  98. lpmmi->ptMinTrackSize.x = max(SYSMET(CXEDGE), xMin);
  99. lpmmi->ptMinTrackSize.y = max(SYSMET(CYEDGE), yMin);
  100. }
  101. /*
  102. * Normal maximum tracking size
  103. */
  104. lpmmi->ptMaxTrackSize.x = SYSMET(CXMAXTRACK);
  105. lpmmi->ptMaxTrackSize.y = SYSMET(CYMAXTRACK);
  106. /*
  107. * SEND THE WM_GETMINMAXINFO MESSAGE
  108. */
  109. ThreadLockWithPti(ptiCurrent, pMonitorReal, &tlpMonitorReal);
  110. ThreadLockAlwaysWithPti(ptiCurrent, pMonitorPrimary, &tlpMonitorPrimary);
  111. xxxSendMessage(pwnd, WM_GETMINMAXINFO, 0, (LPARAM)lpmmi);
  112. /*
  113. * VALIDATE THE MINMAXINFO
  114. */
  115. /*
  116. * Minimized Size (this is read only)
  117. */
  118. lpmmi->ptReserved.x = SYSMET(CXMINIMIZED);
  119. lpmmi->ptReserved.y = SYSMET(CYMINIMIZED);
  120. /*
  121. * Maximized Postion and Size (only for top level windows)
  122. */
  123. if (bTopLevel) {
  124. LPRECT lprcRealMax;
  125. GetMonitorMaxArea(pwnd, pMonitorReal, &lprcRealMax);
  126. /*
  127. * Is the window a TRUE maximized dude, or somebody like the DOS box
  128. * who can maximize but not take up the entire screen?
  129. *
  130. * Is the window really maximizeable?
  131. */
  132. if ((lpmmi->ptMaxSize.x >= (pMonitorPrimary->rcMonitor.right - pMonitorPrimary->rcMonitor.left)) &&
  133. (lpmmi->ptMaxSize.y >= (pMonitorPrimary->rcMonitor.bottom - pMonitorPrimary->rcMonitor.top))) {
  134. SetWF(pwnd, WFREALLYMAXIMIZABLE);
  135. /*
  136. * Need to reload the checkpoint here, since it might have gotten
  137. * blown away while we were in the xxxSendMessage call above.
  138. */
  139. pcp = (CHECKPOINT *)_GetProp(pwnd, PROP_CHECKPOINT, PROPF_INTERNAL);
  140. if ( pcp &&
  141. pcp->fMaxInitialized &&
  142. TestWF(pwnd, WFSIZEBOX) &&
  143. (lpmmi->ptMaxPosition.x != rcParent.left) &&
  144. (pcp->ptMax.x == lpmmi->ptMaxPosition.x)) {
  145. /*
  146. * If this window has a weird maximize point that doesn't jibe
  147. * with what we'd expect and it has a checkpoint, fix up the
  148. * checkpoint. It means that somebody's WINDOWPLACEMENT
  149. * got out of date when the size border changed dimensions.
  150. */
  151. pcp->fMaxInitialized = FALSE;
  152. lpmmi->ptMaxPosition.y += (rcParent.left - lpmmi->ptMaxPosition.x);
  153. lpmmi->ptMaxPosition.x = rcParent.left;
  154. }
  155. /*
  156. * Transfer the maximum size over to the monitor we are REALLY
  157. * moving to. And fix up guys going fullscreen. A whole bunch
  158. * of Consumer titles + Word '95 and XL '95 move their caption
  159. * above the top of the monitor when going fullscreen. Detect
  160. * these guys now, and let them take up the monitor.
  161. */
  162. if ( lpmmi->ptMaxPosition.y + SYSMET(CYCAPTION) <=
  163. pMonitorPrimary->rcMonitor.top
  164. &&
  165. lpmmi->ptMaxPosition.y + lpmmi->ptMaxSize.y >=
  166. pMonitorPrimary->rcMonitor.bottom) {
  167. lprcRealMax = &pMonitorReal->rcMonitor;
  168. }
  169. /*
  170. * Compensate for the difference between the primary monitor
  171. * and the monitor we are actually on.
  172. */
  173. lpmmi->ptMaxSize.x = lpmmi->ptMaxSize.x -
  174. (pMonitorPrimary->rcMonitor.right - pMonitorPrimary->rcMonitor.left) +
  175. (lprcRealMax->right - lprcRealMax->left);
  176. lpmmi->ptMaxSize.y = lpmmi->ptMaxSize.y -
  177. (pMonitorPrimary->rcMonitor.bottom - pMonitorPrimary->rcMonitor.top) +
  178. (lprcRealMax->bottom - lprcRealMax->top);
  179. } else {
  180. ClrWF(pwnd, WFREALLYMAXIMIZABLE);
  181. }
  182. /*
  183. * Now transfer the max position over to the monitor we are REALLY
  184. * moving to.
  185. */
  186. lpmmi->ptMaxPosition.x += lprcRealMax->left;
  187. lpmmi->ptMaxPosition.y += lprcRealMax->top;
  188. }
  189. ThreadUnlock(&tlpMonitorPrimary);
  190. ThreadUnlock(&tlpMonitorReal);
  191. /*
  192. * Normal minimum tracking size.
  193. */
  194. /*
  195. * WFCAPTION == WFBORDER | WFDLGFRAME; So, when we want to test for the
  196. * presence of CAPTION, we must test for both the bits. Otherwise we
  197. * might mistake WFBORDER or WFDLGFRAME to be a CAPTION.
  198. *
  199. *
  200. * We must not allow a window to be sized smaller than the border
  201. * thickness -- SANKAR -- 06/12/91 --
  202. */
  203. if (TestWF(pwnd, WFCPRESENT)) {
  204. /*
  205. * NOTE THAT IF YOU CHANGE THE SPACING OF STUFF IN THE CAPTION,
  206. * YOU NEED TO KEEP THE FOLLOWING IN SSYNC:
  207. * (1) Default CXMINTRACK, CYMINTRACK in inctlpan.c
  208. * (2) The default minimum right below
  209. * (3) Hit testing
  210. *
  211. * The minimum size should be space for:
  212. * * The borders
  213. * * The buttons
  214. * * Margins
  215. * * 4 chars of text
  216. * * Caption icon
  217. */
  218. yMin = SYSMET(CYMINTRACK);
  219. /*
  220. * Min track size is determined by the number of buttons in
  221. * the caption.
  222. */
  223. if (TestWF(pwnd, WEFTOOLWINDOW)) {
  224. /*
  225. * Add in space for close button.
  226. */
  227. if (TestWF(pwnd, WFSYSMENU))
  228. xMin += SYSMET(CXSMSIZE);
  229. /*
  230. * DON'T add in space for 2 characters--breaks
  231. * MFC toolbar stuff. They want to make vertical undocked
  232. * toolbars narrower than what that would produce.
  233. */
  234. xMin += (2 * SYSMET(CXEDGE));
  235. } else {
  236. if (TestWF(pwnd, WFSYSMENU)) {
  237. /*
  238. * Add in space for min/max/close buttons. Otherwise,
  239. * if it's a contexthelp window, then add in space
  240. * for help/close buttons.
  241. */
  242. if (TestWF(pwnd, (WFMINBOX | WFMAXBOX)))
  243. xMin += 3 * SYSMET(CXSIZE);
  244. else if (TestWF(pwnd, WEFCONTEXTHELP))
  245. xMin += 2 * SYSMET(CXSIZE);
  246. /*
  247. * Add in space for system menu icon.
  248. */
  249. if (_HasCaptionIcon(pwnd))
  250. xMin += SYSMET(CYSIZE);
  251. }
  252. /*
  253. * Add in space for 4 characters and margins.
  254. */
  255. xMin += 4 * gcxCaptionFontChar + 2 * SYSMET(CXEDGE);
  256. }
  257. }
  258. lpmmi->ptMinTrackSize.x = max(lpmmi->ptMinTrackSize.x, xMin);
  259. lpmmi->ptMinTrackSize.y = max(lpmmi->ptMinTrackSize.y, yMin);
  260. }
  261. /***************************************************************************\
  262. * ParkIcon
  263. *
  264. * Called when minimizing a window. This parks the minwnd in the position
  265. * given in the checkpoint or calculates a new position for it.
  266. *
  267. * LauraBu 10/15/92
  268. * We now let the user specify two things that affect parking and arranging:
  269. * (1) The corner to start arranging from
  270. * (2) The direction to move in first
  271. * MCostea 11/13/98 #246397
  272. * Add sanity check for the number of tries. If the metrics are messed up
  273. * and pwnd has a lot of siblings, the for-ever loop would make us timeout
  274. *
  275. \***************************************************************************/
  276. VOID ParkIcon(
  277. PWND pwnd,
  278. PCHECKPOINT pcp)
  279. {
  280. RECT rcTest;
  281. RECT rcT;
  282. UINT xIconPositions;
  283. UINT xIconT;
  284. PWND pwndTest;
  285. PWND pwndParent;
  286. int xOrg;
  287. int yOrg;
  288. int dx;
  289. int dy;
  290. int dxSlot;
  291. int dySlot;
  292. int iteration;
  293. BOOL fHorizontal;
  294. PCHECKPOINT pncp;
  295. /*
  296. * Put these into local vars immediately. The compiler is too dumb to
  297. * know that we're using a constant offset into a constant address, and
  298. * thus a resulting constant address.
  299. */
  300. dxSlot = SYSMET(CXMINSPACING);
  301. dySlot = SYSMET(CYMINSPACING);
  302. if (IsTrayWindow(pwnd)) {
  303. pcp->fMinInitialized = TRUE;
  304. pcp->ptMin.x = WHERE_NOONE_CAN_SEE_ME;
  305. pcp->ptMin.y = WHERE_NOONE_CAN_SEE_ME;
  306. return;
  307. }
  308. /* We need to adjust the client rectangle for scrollbars, just like we
  309. * do in ArrangeIconicWindows(). If one thing is clear, it is that
  310. * parking and arranging must follow the same principles. This is to
  311. * avoid the user arranging some windows, creating a new one, and parking
  312. * it in a place not consistent with the arrangement of the others.
  313. */
  314. pwndParent = pwnd->spwndParent;
  315. GetRealClientRect(pwndParent, &rcT, GRC_SCROLLS, NULL);
  316. /*
  317. * Get gravity & move vars. We want gaps to start on the sides that
  318. * we begin arranging from.
  319. *
  320. * Horizontal gravity
  321. */
  322. if (SYSMET(ARRANGE) & ARW_STARTRIGHT) {
  323. /*
  324. * Starting on right side
  325. */
  326. rcTest.left = xOrg = rcT.right - dxSlot;
  327. dx = -dxSlot;
  328. } else {
  329. /*
  330. * Starting on left
  331. */
  332. rcTest.left = xOrg = rcT.left + DX_GAP;
  333. dx = dxSlot;
  334. }
  335. /*
  336. * Vertical gravity
  337. */
  338. if (SYSMET(ARRANGE) & ARW_STARTTOP) {
  339. /*
  340. * Starting on top side
  341. */
  342. rcTest.top = yOrg = rcT.top + DY_GAP;
  343. dy = dySlot;
  344. } else {
  345. /*
  346. * Starting on bottom
  347. */
  348. rcTest.top = yOrg = rcT.bottom - dySlot;
  349. dy = -dySlot;
  350. }
  351. /*
  352. * Get arrangement direction. Note that ARW_HORIZONTAL is 0, so we
  353. * can't test for it.
  354. */
  355. fHorizontal = ((SYSMET(ARRANGE) & ARW_DOWN) ? FALSE : TRUE);
  356. if (fHorizontal)
  357. xIconPositions = xIconT = max(1, (rcT.right / dxSlot));
  358. else
  359. xIconPositions = xIconT = max(1, (rcT.bottom / dySlot));
  360. /*
  361. * BOGUS
  362. * LauraBu 10/15/92
  363. * What happens if the parent is scrolled over horizontally or
  364. * vertically? Just like when you drop an object...
  365. */
  366. iteration = 0;
  367. while (iteration < 5000) {
  368. /*
  369. * Make a rectangle representing this position, in screen coords
  370. */
  371. rcTest.right = rcTest.left + dxSlot;
  372. rcTest.bottom = rcTest.top + dySlot;
  373. /*
  374. * Look for intersections with existing iconic windows
  375. */
  376. for (pwndTest = pwndParent->spwndChild; pwndTest; pwndTest = pwndTest->spwndNext) {
  377. if (!TestWF(pwndTest, WFVISIBLE))
  378. continue;
  379. if (pwndTest == pwnd)
  380. continue;
  381. if (!TestWF(pwndTest, WFMINIMIZED)) {
  382. /*
  383. * This is a non-minimized window. See if it has a checkpoint
  384. * and find out where it would be if it were minimized. We
  385. * will try not to park an icon in this spot.
  386. */
  387. pncp = (PCHECKPOINT)_GetProp(pwndTest,
  388. PROP_CHECKPOINT,
  389. PROPF_INTERNAL);
  390. if (!pncp || !pncp->fDragged || !pncp->fMinInitialized)
  391. continue;
  392. /*
  393. * Get parent coordinates of minimized window pos.
  394. */
  395. rcT.right = rcT.left = pncp->ptMin.x;
  396. rcT.right += dxSlot;
  397. rcT.bottom = rcT.top = pncp->ptMin.y;
  398. rcT.bottom += dySlot;
  399. } else {
  400. /*
  401. * Get parent coordinates of currently minimized window
  402. */
  403. GetRect(pwndTest, &rcT, GRECT_WINDOW | GRECT_PARENTCOORDS);
  404. }
  405. iteration++;
  406. /*
  407. * Get out of loop if they overlap
  408. */
  409. if (IntersectRect(&rcT, &rcT, &rcTest))
  410. break;
  411. }
  412. /*
  413. * Found a position that doesn't overlap, so get out of search loop
  414. */
  415. if (!pwndTest)
  416. break;
  417. /*
  418. * Else setup to process the next position
  419. */
  420. if (--xIconT == 0) {
  421. /*
  422. * Setup next pass
  423. */
  424. xIconT = xIconPositions;
  425. if (fHorizontal) {
  426. rcTest.left = xOrg;
  427. rcTest.top += dy;
  428. } else {
  429. rcTest.left += dx;
  430. rcTest.top = yOrg;
  431. }
  432. } else {
  433. /*
  434. * Same pass.
  435. */
  436. if (fHorizontal)
  437. rcTest.left += dx;
  438. else
  439. rcTest.top += dy;
  440. }
  441. }
  442. /*
  443. * Note that rcTest is in parent coordinates already.
  444. */
  445. pcp->fMinInitialized = TRUE;
  446. pcp->ptMin.x = rcTest.left;
  447. pcp->ptMin.y = rcTest.top;
  448. }
  449. /***************************************************************************\
  450. * xxxAnimateCaption
  451. *
  452. *
  453. \***************************************************************************/
  454. ULONG_PTR SaveScreen(PWND pwnd, ULONG iMode, ULONG_PTR iSave, int x, int y, int cx, int cy)
  455. {
  456. RECT rc;
  457. /*
  458. * x and y are in the DC coordinates, make the screen in the
  459. * (meta hdev) coordinates for the call to Gre/driver.
  460. */
  461. rc.left = x + pwnd->rcWindow.left;
  462. rc.right = x + cx;
  463. rc.top = y + pwnd->rcWindow.top;
  464. rc.bottom = y + cy;
  465. if (IntersectRect(&rc, &rc, &gpDispInfo->rcScreen)) {
  466. return GreSaveScreenBits(gpDispInfo->hDev, iMode, iSave, (RECTL*)&rc);
  467. } else {
  468. return 0;
  469. }
  470. }
  471. VOID xxxAnimateCaption(
  472. PWND pwnd,
  473. HDC hdc,
  474. LPRECT lprcStart,
  475. LPRECT lprcEnd)
  476. {
  477. DWORD dwTimeStart;
  478. DWORD iTimeElapsed;
  479. int iLeftStart;
  480. int iTopStart;
  481. int cxStart;
  482. int dLeft;
  483. int dTop;
  484. int dcx;
  485. int iLeft;
  486. int iTop;
  487. int cx;
  488. int iLeftNew;
  489. int iTopNew;
  490. int cxNew;
  491. int cBorders;
  492. HBITMAP hbmpOld;
  493. RECT rc;
  494. int cy;
  495. HDC hdcMem;
  496. ULONG_PTR uSave;
  497. PWND pwndOrg;
  498. CheckLock(pwnd);
  499. if ((pwndOrg = _WindowFromDC(hdc)) == NULL) {
  500. RIPMSG0(RIP_WARNING, "SaveScreen: invalid DC passed in");
  501. return;
  502. }
  503. cy = SYSMET(CYCAPTION) - 1;
  504. /*
  505. * kurtp: 29-Jan-1997
  506. *
  507. * We don't do anything when animating the caption,
  508. * because we couldn't get the desired effect at the
  509. * client. If we do use it then the
  510. * cache gets a bunch of bitmaps (size: 2xCaption by CXScreen)
  511. * that are never re-used. This slows down clients
  512. * because the GreBitBlts always generate new bitmaps
  513. * and the cache is displaced by the new bitmaps (yuk!).
  514. */
  515. if (IsRemoteConnection() || SYSMETBOOL2(SM_REMOTECONTROL))
  516. return;
  517. if ((hdcMem = GreCreateCompatibleDC(ghdcMem)) == NULL)
  518. return;
  519. /*
  520. * If the caption strip doesn't exist, then attempt to recreate it. This
  521. * might be necessary if the user does a mode-switch during low memory
  522. * and is not able to recreate the surface. When the memory becomes
  523. * available, we'll attempt to recreate it here.
  524. */
  525. if (ghbmCaption == NULL) {
  526. ghbmCaption = CreateCaptionStrip();
  527. }
  528. hbmpOld = GreSelectBitmap(hdcMem, ghbmCaption);
  529. /*
  530. * initialize start values
  531. */
  532. iTopStart = lprcStart->top;
  533. iLeftStart = lprcStart->left;
  534. cxStart = lprcStart->right - iLeftStart;
  535. /*
  536. * initialize delta values to the destination dimensions
  537. */
  538. dLeft = lprcEnd->left;
  539. dTop = lprcEnd->top;
  540. dcx = lprcEnd->right - dLeft;
  541. /*
  542. * adjust for window borders as appropriate
  543. */
  544. cBorders = GetWindowBorders(pwnd->style,
  545. pwnd->ExStyle,
  546. TRUE,
  547. FALSE);
  548. if ((lprcStart->bottom - iTopStart) > SYSMET(CYCAPTION)) {
  549. iLeftStart += cBorders;
  550. iTopStart += cBorders;
  551. cxStart -= 2*cBorders;
  552. }
  553. if ((lprcEnd->bottom - dTop) > SYSMET(CYCAPTION)) {
  554. dLeft += cBorders;
  555. dTop += cBorders;
  556. dcx -= 2*cBorders;
  557. }
  558. /*
  559. * initialize step values
  560. */
  561. iLeft = iLeftStart;
  562. iTop = iTopStart;
  563. cx = cxStart;
  564. /*
  565. * initialize off screen bitmap with caption drawing and first saved rect
  566. */
  567. rc.left = 0;
  568. rc.top = cy;
  569. rc.right = max(cxStart, dcx);
  570. rc.bottom = cy * 2;
  571. xxxDrawCaptionTemp(pwnd,
  572. hdcMem,
  573. &rc,
  574. NULL,
  575. NULL,
  576. NULL,
  577. DC_ACTIVE | DC_ICON | DC_TEXT |
  578. (TestALPHA(GRADIENTCAPTIONS) ? DC_GRADIENT : 0));
  579. if ((uSave = SaveScreen(pwndOrg, SS_SAVE, 0,iLeft, iTop, cx, cy)) == 0) {
  580. if (!GreBitBlt(hdcMem,
  581. 0,
  582. 0,
  583. cx,
  584. cy,
  585. hdc,
  586. iLeft,
  587. iTop,
  588. SRCCOPY,
  589. 0)) {
  590. goto Cleanup;
  591. }
  592. }
  593. /*
  594. * compute delta values by subtracting source dimensions
  595. */
  596. dLeft -= iLeftStart;
  597. dTop -= iTopStart;
  598. dcx -= cxStart;
  599. /*
  600. * blt and time first caption on screen
  601. * WARNING: If you use *lpSystemTickCount here,
  602. * the compiler may not generate code to do a DWORD fetch;
  603. */
  604. dwTimeStart = NtGetTickCount();
  605. GreBitBlt(hdc,
  606. iLeft,
  607. iTop,
  608. cx,
  609. cy,
  610. hdcMem,
  611. 0,
  612. cy,
  613. SRCCOPY,
  614. 0);
  615. iTimeElapsed = (NtGetTickCount() - dwTimeStart);
  616. while (LOWORD(iTimeElapsed) <= CMS_ANIMATION) {
  617. iLeftNew = iLeftStart + MultDiv(dLeft, LOWORD(iTimeElapsed), CMS_ANIMATION);
  618. iTopNew = iTopStart + MultDiv(dTop, LOWORD(iTimeElapsed), CMS_ANIMATION);
  619. cxNew = cxStart + MultDiv(dcx, LOWORD(iTimeElapsed), CMS_ANIMATION);
  620. /*
  621. * Delay before next frame
  622. */
  623. UserSleep(1);
  624. /*
  625. * restore saved rect
  626. */
  627. if (uSave != 0) {
  628. SaveScreen(pwndOrg, SS_RESTORE, uSave, iLeft, iTop, cx, cy);
  629. } else {
  630. GreBitBlt(hdc,
  631. iLeft,
  632. iTop,
  633. cx,
  634. cy,
  635. hdcMem,
  636. 0,
  637. 0,
  638. SRCCOPY,
  639. 0);
  640. }
  641. iLeft = iLeftNew;
  642. iTop = iTopNew;
  643. cx = cxNew;
  644. /*
  645. * save new rect offscreen and then draw over it onscreen.
  646. */
  647. if (uSave != 0) {
  648. uSave = SaveScreen(pwndOrg, SS_SAVE, 0, iLeft, iTop, cx, cy);
  649. } else {
  650. GreBitBlt(hdcMem,
  651. 0,
  652. 0,
  653. cx,
  654. cy,
  655. hdc,
  656. iLeft,
  657. iTop,
  658. SRCCOPY,
  659. 0);
  660. }
  661. GreBitBlt(hdc,
  662. iLeft,
  663. iTop,
  664. cx,
  665. cy,
  666. hdcMem,
  667. 0,
  668. cy,
  669. SRCCOPY,
  670. 0);
  671. /*
  672. * update elapsed time
  673. * WARNING: If you use *lpSystemTickCount here,
  674. * the compiler may not generate code to do a DWORD fetch;
  675. */
  676. iTimeElapsed = (NtGetTickCount() - dwTimeStart);
  677. }
  678. /*
  679. * restore saved rect
  680. */
  681. if (uSave != 0) {
  682. SaveScreen(pwndOrg, SS_RESTORE, uSave, iLeft, iTop, cx, cy);
  683. } else {
  684. GreBitBlt(hdc,
  685. iLeft,
  686. iTop,
  687. cx,
  688. cy,
  689. hdcMem,
  690. 0,
  691. 0,
  692. SRCCOPY,
  693. 0);
  694. }
  695. Cleanup:
  696. GreSelectBitmap(hdcMem, hbmpOld);
  697. GreDeleteDC(hdcMem);
  698. }
  699. #if 0 // DISABLE OLD ANIMATION FOR M7
  700. /***************************************************************************\
  701. * DrawWireFrame
  702. *
  703. * Draws wire frame trapezoid
  704. *
  705. *
  706. \***************************************************************************/
  707. VOID DrawWireFrame(
  708. HDC hdc,
  709. LPRECT prcFront,
  710. LPRECT prcBack)
  711. {
  712. RECT rcFront;
  713. RECT rcBack;
  714. RECT rcT;
  715. HRGN hrgnSave;
  716. BOOL fClip;
  717. /*
  718. * Save these locally
  719. */
  720. CopyRect(&rcFront, prcFront);
  721. CopyRect(&rcBack, prcBack);
  722. /*
  723. * Front face
  724. */
  725. GreMoveTo(hdc, rcFront.left, rcFront.top);
  726. GreLineTo(hdc, rcFront.left, rcFront.bottom);
  727. GreLineTo(hdc, rcFront.right, rcFront.bottom);
  728. GreLineTo(hdc, rcFront.right, rcFront.top);
  729. GreLineTo(hdc, rcFront.left, rcFront.top);
  730. /*
  731. * Exclude front face from clipping area, only if back face isn't
  732. * entirely within interior. We need variable because SaveClipRgn()
  733. * can return NULL.
  734. */
  735. fClip = (EqualRect(&rcFront, &rcBack) ||
  736. !IntersectRect(&rcT, &rcFront, &rcBack) ||
  737. !EqualRect(&rcT, &rcBack));
  738. if (fClip) {
  739. hrgnSave = GreSaveClipRgn(hdc);
  740. GreExcludeClipRect(hdc,
  741. rcFront.left,
  742. rcFront.top,
  743. rcFront.right,
  744. rcFront.bottom);
  745. }
  746. /*
  747. * Edges
  748. */
  749. GreMoveTo(hdc, rcBack.left, rcBack.top);
  750. LineTo(hdc, rcFront.left, rcFront.top);
  751. GreMoveTo(hdc, rcBack.right, rcBack.top);
  752. GreLineTo(hdc, rcFront.right, rcFront.top);
  753. GreMoveTo(hdc, rcBack.right, rcBack.bottom);
  754. GreLineTo(hdc, rcFront.right, rcFront.bottom);
  755. GreMoveTo(hdc, rcBack.left, rcBack.bottom);
  756. GreLineTo(hdc, rcFront.left, rcFront.bottom);
  757. /*
  758. * Back face
  759. */
  760. MoveTo(hdc, rcBack.left, rcBack.top);
  761. LineTo(hdc, rcBack.left, rcBack.bottom);
  762. LineTo(hdc, rcBack.right, rcBack.bottom);
  763. LineTo(hdc, rcBack.right, rcBack.top);
  764. LineTo(hdc, rcBack.left, rcBack.top);
  765. if (fClip)
  766. GreRestoreClipRgn(hdc, hrgnSave);
  767. }
  768. /***************************************************************************\
  769. * AnimateFrame
  770. *
  771. * Draws wire frame 3D trapezoid
  772. *
  773. *
  774. \***************************************************************************/
  775. VOID AnimateFrame(
  776. HDC hdc,
  777. LPRECT prcStart,
  778. LPRECT prcEnd,
  779. BOOL fGrowing)
  780. {
  781. RECT rcBack;
  782. RECT rcFront;
  783. RECT rcT;
  784. HPEN hpen;
  785. int nMode;
  786. int iTrans;
  787. int nTrans;
  788. DWORD dwTimeStart;
  789. DWORD dwTimeCur;
  790. /*
  791. * Get pen for drawing lines
  792. */
  793. hpen = GreSelectPen(hdc, GetStockObject(WHITE_PEN));
  794. nMode = GreSetROP2(hdc, R2_XORPEN);
  795. /*
  796. * Save these locally
  797. */
  798. if (fGrowing) {
  799. CopyRect(&rcBack, prcStart);
  800. CopyRect(&rcFront, prcStart);
  801. } else {
  802. /*
  803. * Initial is trapezoid entire way from small to big. We're going
  804. * to shrink it from the front face.
  805. */
  806. CopyRect(&rcFront, prcStart);
  807. CopyRect(&rcBack, prcEnd);
  808. }
  809. /*
  810. * Offset left & top edges of rects, due to way that lines work.
  811. */
  812. rcFront.left -= 1;
  813. rcFront.top -= 1;
  814. rcBack.left -= 1;
  815. rcBack.top -= 1;
  816. /*
  817. * Get tick count. We'll draw then check how much time elapsed. From
  818. * that we can calculate how many more transitions to draw. For the first
  819. * We basically want whole animation to last 3/4 of a second, or 750
  820. * milliseconds.
  821. *
  822. * WARNING: If you use *lpSystemTickCount here,
  823. * the compiler may not generate code to do a DWORD fetch;
  824. */
  825. dwTimeStart = GetSystemMsecCount();
  826. DrawWireFrame(hdc, &rcFront, &rcBack);
  827. /*
  828. * WARNING: If you use *lpSystemTickCount here,
  829. * the compiler may not generate code to do a DWORD fetch;
  830. */
  831. dwTimeCur = GetSystemMsecCount();
  832. /*
  833. * Get rough estimate for how much time it took.
  834. */
  835. if (dwTimeCur == dwTimeStart)
  836. nTrans = CMS_ANIMATION / 55;
  837. else
  838. nTrans = CMS_ANIMATION / ((int)(dwTimeCur - dwTimeStart));
  839. iTrans = 1;
  840. while (iTrans <= nTrans) {
  841. /*
  842. * Grow the trapezoid out or shrink it in. Fortunately, prcStart
  843. * and prcEnd are already set up for us.
  844. */
  845. rcT.left = prcStart->left +
  846. MultDiv(prcEnd->left - prcStart->left, iTrans, nTrans);
  847. rcT.top = prcStart->top +
  848. MultDiv(prcEnd->top - prcStart->top, iTrans, nTrans);
  849. rcT.right = prcStart->right +
  850. MultDiv(prcEnd->right - prcStart->right, iTrans, nTrans);
  851. rcT.bottom = prcStart->bottom +
  852. MultDiv(prcEnd->bottom - prcStart->bottom, iTrans, nTrans);
  853. /*
  854. * Undraw old and draw new
  855. */
  856. DrawWireFrame(hdc, &rcFront, &rcBack);
  857. CopyRect(&rcFront, &rcT);
  858. DrawWireFrame(hdc, &rcFront, &rcBack);
  859. /*
  860. * Check the time. How many more transitions left?
  861. * iTrans / nTrans AS (dwTimeCur-dwTimeStart) / 750
  862. *
  863. * WARNING: If you use *lpSystemTickCount here,
  864. * the compiler may not generate code to do a DWORD fetch;
  865. */
  866. dwTimeCur = GetSystemMsecCount();
  867. iTrans = MultDiv(nTrans,
  868. (int)(dwTimeCur - dwTimeStart),
  869. CMS_ANIMATION);
  870. }
  871. /*
  872. * Undraw wire frame
  873. */
  874. DrawWireFrame(hdc, &rcFront, &rcBack);
  875. /*
  876. * Clean up
  877. */
  878. GreSetROP2(hdc, nMode);
  879. hpen = GreSelectPen(hdc, hpen);
  880. }
  881. #endif // END DISABLE OLD ANIMATION FOR M7
  882. /***************************************************************************\
  883. * xxxDrawAnimatedRects
  884. *
  885. * General routine, like PlaySoundEvent(), that calls other routines for
  886. * various animation effects. Currently used for changing state from/to
  887. * minimized.
  888. *
  889. \***************************************************************************/
  890. BOOL xxxDrawAnimatedRects(
  891. PWND pwndClip,
  892. int idAnimation,
  893. LPRECT lprcStart,
  894. LPRECT lprcEnd)
  895. {
  896. HDC hdc;
  897. POINT rgPt[4];
  898. RECT rcClip;
  899. HRGN hrgn;
  900. PWND pwndAnimate = NULL;
  901. int iPt;
  902. CheckLock(pwndClip);
  903. /*
  904. * Get rects into variables
  905. */
  906. CopyRect((LPRECT)&rgPt[0], lprcStart);
  907. CopyRect((LPRECT)&rgPt[2], lprcEnd);
  908. /*
  909. * DISABLE OLD ANIMATION FOR M7
  910. */
  911. if (idAnimation != IDANI_CAPTION)
  912. return TRUE;
  913. pwndAnimate = pwndClip;
  914. if (!pwndAnimate || pwndAnimate == PWNDDESKTOP(pwndAnimate))
  915. return FALSE;
  916. pwndClip = pwndClip->spwndParent;
  917. if (!pwndClip) {
  918. RIPMSG0(RIP_WARNING, "xxxDrawAnimatedRects: pwndClip->spwndParent is NULL");
  919. } else if (pwndClip == PWNDDESKTOP(pwndClip)) {
  920. pwndClip = NULL;
  921. }
  922. /*
  923. * NOTE:
  924. * We do NOT need to do LockWindowUpdate(). We never yield within this
  925. * function! Anything that was invalid will stay invalid, etc. So our
  926. * XOR drawing won't leave remnants around.
  927. *
  928. * WIN32NT may need to take display critical section or do LWU().
  929. *
  930. * Get clipping area
  931. * Neat feature:
  932. * NULL window means whole screen, don't clip out children
  933. * hwndDesktop means working area, don't clip out children
  934. */
  935. if (pwndClip == NULL) {
  936. pwndClip = _GetDesktopWindow();
  937. CopyRect(&rcClip, &pwndClip->rcClient);
  938. if ((hrgn = GreCreateRectRgnIndirect(&rcClip)) == NULL) {
  939. hrgn = HRGN_FULL;
  940. }
  941. /*
  942. * Get drawing DC
  943. */
  944. hdc = _GetDCEx(pwndClip,
  945. hrgn,
  946. DCX_WINDOW |
  947. DCX_CACHE |
  948. DCX_INTERSECTRGN |
  949. DCX_LOCKWINDOWUPDATE);
  950. } else {
  951. hdc = _GetDCEx(pwndClip,
  952. HRGN_FULL,
  953. DCX_WINDOW | DCX_USESTYLE | DCX_INTERSECTRGN);
  954. /*
  955. * We now have a window DC. We need to convert client coords
  956. * to window coords.
  957. */
  958. for (iPt = 0; iPt < 4; iPt++) {
  959. rgPt[iPt].x += (pwndClip->rcClient.left - pwndClip->rcWindow.left);
  960. rgPt[iPt].y += (pwndClip->rcClient.top - pwndClip->rcWindow.top);
  961. }
  962. }
  963. /*
  964. * Get drawing DC:
  965. * Unclipped if desktop, clipped otherwise.
  966. * Note that ReleaseDC() will free the region if needed.
  967. */
  968. if (idAnimation == IDANI_CAPTION) {
  969. CheckLock(pwndAnimate);
  970. xxxAnimateCaption(pwndAnimate, hdc, (LPRECT)&rgPt[0], (LPRECT)&rgPt[2]);
  971. }
  972. /*
  973. * DISABLE OLD ANIMATION FOR M7
  974. */
  975. #if 0
  976. else {
  977. AnimateFrame(hdc,
  978. (LPRECT)&rgPt[0],
  979. (LPRECT)&rgPt[2],
  980. (idAnimation == IDANI_OPEN));
  981. }
  982. #endif
  983. /*
  984. * END DISABLE OLD ANIMATION FOR M7
  985. */
  986. /*
  987. * Clean up
  988. */
  989. _ReleaseDC(hdc);
  990. return TRUE;
  991. }
  992. /***************************************************************************\
  993. * CalcMinZOrder
  994. *
  995. *
  996. * Compute the Z-order of a window to be minimized.
  997. *
  998. * The strategy is to find the bottom-most sibling of pwndMinimize that
  999. * shares the same owner, and insert ourself behind that. We must also
  1000. * take into account that a TOPMOST window should stay among other TOPMOST,
  1001. * and vice versa.
  1002. *
  1003. * We must make sure never to insert after a bottom-most window.
  1004. *
  1005. * This code works for child windows too, since they don't have owners
  1006. * and never have WEFTOPMOST set.
  1007. *
  1008. * If NULL is returned, the window shouldn't be Z-ordered.
  1009. *
  1010. \***************************************************************************/
  1011. PWND CalcMinZOrder(
  1012. PWND pwndMinimize)
  1013. {
  1014. BYTE bTopmost;
  1015. PWND pwndAfter;
  1016. PWND pwnd;
  1017. bTopmost = TestWF(pwndMinimize, WEFTOPMOST);
  1018. pwndAfter = NULL;
  1019. for (pwnd = pwndMinimize->spwndNext; pwnd && !TestWF(pwnd, WFBOTTOMMOST); pwnd = pwnd->spwndNext) {
  1020. /*
  1021. * If we've enumerated a window that isn't the same topmost-wise
  1022. * as pwndMinimize, we've gone as far as we can.
  1023. */
  1024. if (TestWF(pwnd, WEFTOPMOST) != bTopmost)
  1025. break;
  1026. if (pwnd->spwndOwner == pwndMinimize->spwndOwner)
  1027. pwndAfter = pwnd;
  1028. }
  1029. return pwndAfter;
  1030. }
  1031. /***************************************************************************\
  1032. * xxxActivateOnMinimize
  1033. *
  1034. * Activate the previously active window, provided that window still exists
  1035. * and is a NORMAL window (not bottomost, minimized, disabled, or invisible).
  1036. * If it's not NORMAL, then activate the first non WS_EX_TOPMOST window
  1037. * that's normal. Return TRUE when no activation is needed or the activation
  1038. * has been done in this function. Return FALSE if failed to find a window
  1039. * to activate.
  1040. *
  1041. \***************************************************************************/
  1042. BOOL xxxActivateOnMinimize(PWND pwnd)
  1043. {
  1044. PTHREADINFO ptiCurrent = PtiCurrent();
  1045. PWND pwndStart, pwndFirstTool, pwndT;
  1046. BOOL fTryTopmost = TRUE;
  1047. BOOL fPrevCheck = (ptiCurrent->pq->spwndActivePrev != NULL);
  1048. TL tlpwndT;
  1049. /*
  1050. * We should always have a last-topmost window.
  1051. */
  1052. pwndStart = GetLastTopMostWindow();
  1053. if (pwndStart) {
  1054. pwndStart = pwndStart->spwndNext;
  1055. } else {
  1056. pwndStart = pwnd->spwndParent->spwndChild;
  1057. }
  1058. UserAssert(HIBYTE(WFMINIMIZED) == HIBYTE(WFVISIBLE));
  1059. UserAssert(HIBYTE(WFVISIBLE) == HIBYTE(WFDISABLED));
  1060. SearchAgain:
  1061. pwndT = (fPrevCheck ? ptiCurrent->pq->spwndActivePrev : pwndStart);
  1062. pwndFirstTool = NULL;
  1063. /*
  1064. * TryThisWindow must be outside the beginning of the for loop such
  1065. * that pwndT is checked for NULL before being dereferenced.
  1066. */
  1067. TryThisWindow:
  1068. for ( ; pwndT ; pwndT = pwndT->spwndNext) {
  1069. /*
  1070. * Use the first nonminimized, visible, nondisabled, and
  1071. * nonbottommost window
  1072. */
  1073. if (!HMIsMarkDestroy(pwndT) &&
  1074. !TestWF(pwndT, WEFNOACTIVATE) &&
  1075. (TestWF(pwndT, WFVISIBLE | WFDISABLED) == LOBYTE(WFVISIBLE)) &&
  1076. (!TestWF(pwndT, WFMINIMIZED) || GetFullScreen(pwndT) == FULLSCREEN)) {
  1077. if (TestWF(pwndT, WEFTOOLWINDOW)) {
  1078. if (!pwndFirstTool) {
  1079. pwndFirstTool = pwndT;
  1080. }
  1081. } else {
  1082. break;
  1083. }
  1084. }
  1085. if (fPrevCheck) {
  1086. fPrevCheck = FALSE;
  1087. pwndT = pwndStart;
  1088. goto TryThisWindow;
  1089. }
  1090. }
  1091. if (!pwndT) {
  1092. if (fTryTopmost) {
  1093. fTryTopmost = FALSE;
  1094. if (pwndStart != NULL) {
  1095. pwndStart = pwndStart->spwndParent->spwndChild;
  1096. } else {
  1097. PWND pwndDesktop = _GetDesktopWindow();
  1098. pwndStart = (pwndDesktop != NULL) ? pwndDesktop->spwndChild : NULL;
  1099. }
  1100. goto SearchAgain;
  1101. }
  1102. pwndT = pwndFirstTool;
  1103. }
  1104. if (pwndT) {
  1105. ThreadLockAlwaysWithPti(ptiCurrent, pwndT, &tlpwndT);
  1106. xxxSetForegroundWindow(pwndT, FALSE);
  1107. ThreadUnlock(&tlpwndT);
  1108. } else {
  1109. return FALSE;
  1110. }
  1111. return TRUE;
  1112. }
  1113. /***************************************************************************\
  1114. * xxxMinMaximize
  1115. *
  1116. * cmd = SW_MINIMIZE, SW_SHOWMINNOACTIVE, SW_SHOWMINIZED,
  1117. * SW_SHOWMAXIMIZED, SW_SHOWNOACTIVE, SW_NORMAL
  1118. *
  1119. * If MINMAX_KEEPHIDDEN is set in dwFlags, keep it hidden, otherwise show it.
  1120. * This is always cleared, except in the case we call it from
  1121. * createwindow(), where the wnd is iconic, but hidden. we
  1122. * need to call this func, to set it up correctly so that when
  1123. * the app shows the wnd, it is displayed correctly.
  1124. *
  1125. * When changing state, we always add on SWP_STATECHANGE. This lets
  1126. * SetWindowPos() know to always send WM_WINDOWPOSCHANGING/CHANGED messages
  1127. * even if the new size is the same as the old size. This is because
  1128. * apps watch the WM_SIZE wParam field to see when they are changing state.
  1129. * If SWP doesn't send WM_WINDOWPOSCHANGED, then they won't get a WM_SIZE
  1130. * message at all.
  1131. *
  1132. * Furthermore, when changing state to/from maximized, if we are really
  1133. * maximizing and are in multiple monitor mode, we want to set the window's
  1134. * region so that it can't draw outside of the monitor. Otherwise, it
  1135. * will spill over onto another. The borders are really annoying.
  1136. *
  1137. \***************************************************************************/
  1138. PWND xxxMinMaximize(
  1139. PWND pwnd,
  1140. UINT cmd,
  1141. DWORD dwFlags)
  1142. {
  1143. RECT rc;
  1144. RECT rcWindow;
  1145. RECT rcRestore;
  1146. BOOL fShow = FALSE;
  1147. BOOL fSetFocus = FALSE;
  1148. BOOL fShowOwned = FALSE;
  1149. BOOL fSendActivate = FALSE;
  1150. BOOL fMaxStateChanging = FALSE;
  1151. int idAnimation = 0;
  1152. BOOL fFlushPalette = FALSE;
  1153. UINT swpFlags = 0;
  1154. HWND hwndAfter = NULL;
  1155. PWND pwndT;
  1156. PCHECKPOINT pcp;
  1157. PTHREADINFO ptiCurrent;
  1158. TL tlpwndParent;
  1159. TL tlpwndT;
  1160. PSMWP psmwp;
  1161. BOOL fIsTrayWindowNow = FALSE;
  1162. NTSTATUS Status;
  1163. MINMAXINFO mmi;
  1164. UINT uEvent = 0;
  1165. PWND pwndParent = pwnd->spwndParent;
  1166. BOOL bMirroredParent=FALSE;
  1167. CheckLock(pwnd);
  1168. /*
  1169. * Get window rect, in parent client coordinates.
  1170. */
  1171. GetRect(pwnd, &rcWindow, GRECT_WINDOW | GRECT_PARENTCOORDS);
  1172. /*
  1173. * If this is NULL, we're out of memory, so punt now.
  1174. */
  1175. pcp = CkptRestore(pwnd, &rcWindow);
  1176. if (!pcp)
  1177. goto Exit;
  1178. /*
  1179. * If this top-level window is placed in a mirrored desktop,
  1180. * its coordinates should be mirrored here so that xxxAnimateCaptions
  1181. * works properly, however we shouldn't change the actual screen coordinates
  1182. * of the window. This is why I do it after CkptRestore(...). [samera]
  1183. */
  1184. if (TestWF(pwndParent,WEFLAYOUTRTL) &&
  1185. (!TestWF(pwnd,WFCHILD))) {
  1186. int iLeft = rcWindow.left;
  1187. rcWindow.left = pwndParent->rcWindow.right - rcWindow.right;
  1188. rcWindow.right = pwndParent->rcWindow.right - iLeft;
  1189. bMirroredParent = TRUE;
  1190. }
  1191. /*
  1192. * Save the previous restore size.
  1193. */
  1194. CopyRect(&rcRestore, &pcp->rcNormal);
  1195. /*
  1196. * First ask the CBT hook if we can do this operation.
  1197. */
  1198. if ( IsHooked(PtiCurrent(), WHF_CBT) &&
  1199. xxxCallHook(HCBT_MINMAX, (WPARAM)HWq(pwnd), (DWORD)cmd, WH_CBT)) {
  1200. goto Exit;
  1201. }
  1202. /*
  1203. * If another MDI window is being maximized, and we want to restore this
  1204. * one to its previous state, we can't change the zorder or the
  1205. * activation. We'd mess things up that way. BTW, this SW_ value is
  1206. * internal.
  1207. */
  1208. if (cmd == SW_MDIRESTORE) {
  1209. swpFlags |= SWP_NOZORDER | SWP_NOACTIVATE;
  1210. cmd = (pcp->fWasMinimizedBeforeMaximized ?
  1211. SW_SHOWMINIMIZED : SW_SHOWNORMAL);
  1212. }
  1213. ptiCurrent = PtiCurrent();
  1214. switch (cmd) {
  1215. case SW_MINIMIZE: // Bottom of zorder, make top-level active
  1216. case SW_SHOWMINNOACTIVE: // Bottom of zorder, don't change activation
  1217. if (gpqForeground && gpqForeground->spwndActive)
  1218. swpFlags |= SWP_NOACTIVATE;
  1219. if ((pwndT = CalcMinZOrder(pwnd)) == NULL) {
  1220. swpFlags |= SWP_NOZORDER;
  1221. } else {
  1222. hwndAfter = PtoHq(pwndT);
  1223. }
  1224. /*
  1225. * FALL THRU
  1226. */
  1227. case SW_SHOWMINIMIZED: // Top of zorder, make active
  1228. /*
  1229. * Force a show.
  1230. */
  1231. fShow = TRUE;
  1232. /*
  1233. * If already minimized, then don't change the existing
  1234. * parking spot.
  1235. */
  1236. if (TestWF(pwnd, WFMINIMIZED)) {
  1237. /*
  1238. * If we're already minimized and we're properly visible
  1239. * or not visible, don't do anything
  1240. */
  1241. if (TestWF(pwnd, WFVISIBLE))
  1242. return NULL;
  1243. swpFlags |= SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE;
  1244. goto Showit;
  1245. }
  1246. /*
  1247. * We're becoming minimized although we currently are not. So
  1248. * we want to draw the transition animation, and ALWAYS send
  1249. * sizing messages.
  1250. */
  1251. idAnimation = IDANI_CLOSE;
  1252. if (!pcp->fDragged)
  1253. pcp->fMinInitialized = FALSE;
  1254. if (!pcp->fMinInitialized)
  1255. ParkIcon(pwnd, pcp);
  1256. rc.left = pcp->ptMin.x;
  1257. rc.top = pcp->ptMin.y;
  1258. rc.right = pcp->ptMin.x + SYSMET(CXMINIMIZED);
  1259. rc.bottom = pcp->ptMin.y + SYSMET(CYMINIMIZED);
  1260. xxxShowOwnedWindows(pwnd, SW_PARENTCLOSING, NULL);
  1261. pwndT = ptiCurrent->pq->spwndFocus;
  1262. while (pwndT) {
  1263. /*
  1264. * if we or any child has the focus, punt it away
  1265. */
  1266. if (pwndT != pwnd) {
  1267. pwndT = pwndT->spwndParent;
  1268. continue;
  1269. }
  1270. ThreadLockAlwaysWithPti(ptiCurrent, pwndT, &tlpwndT);
  1271. if (TestwndChild(pwnd)) {
  1272. ThreadLockWithPti(ptiCurrent, pwnd->spwndParent, &tlpwndParent);
  1273. xxxSetFocus(pwnd->spwndParent);
  1274. ThreadUnlock(&tlpwndParent);
  1275. } else {
  1276. xxxSetFocus(NULL);
  1277. }
  1278. ThreadUnlock(&tlpwndT);
  1279. break;
  1280. }
  1281. /*
  1282. * Save the maximized state so that we can restore the window maxed
  1283. */
  1284. if (TestWF(pwnd, WFMAXIMIZED)) {
  1285. pcp->fWasMaximizedBeforeMinimized = TRUE;
  1286. fMaxStateChanging = TRUE;
  1287. } else{
  1288. pcp->fWasMaximizedBeforeMinimized = FALSE;
  1289. }
  1290. if (!TestWF(pwnd, WFWIN40COMPAT))
  1291. fIsTrayWindowNow = IsTrayWindow(pwnd);
  1292. /*
  1293. * Decrement the visible-windows count only if the
  1294. * window is visible. If the window is marked for
  1295. * destruction, we will not decrement for that as
  1296. * well. Let SetMinimize take care of this.
  1297. */
  1298. SetMinimize(pwnd, SMIN_SET);
  1299. ClrWF(pwnd, WFMAXIMIZED);
  1300. uEvent = EVENT_SYSTEM_MINIMIZESTART;
  1301. if (!TestWF(pwnd, WFWIN40COMPAT))
  1302. fIsTrayWindowNow = (fIsTrayWindowNow != IsTrayWindow(pwnd));
  1303. /*
  1304. * The children of this window are now no longer visible.
  1305. * Ensure that they no longer have any update regions...
  1306. */
  1307. for (pwndT = pwnd->spwndChild; pwndT; pwndT = pwndT->spwndNext)
  1308. ClrFTrueVis(pwndT);
  1309. /*
  1310. * B#2919
  1311. * Ensure that the client area gets recomputed, and make
  1312. * sure that no bits are copied when the size is changed. And
  1313. * make sure that WM_SIZE messages get sent, even if our client
  1314. * size is staying the same.
  1315. */
  1316. swpFlags |= (SWP_DRAWFRAME | SWP_NOCOPYBITS | SWP_STATECHANGE);
  1317. /*
  1318. * We are going minimized, so we want to give palette focus to
  1319. * another app.
  1320. */
  1321. if (pwnd->spwndParent == PWNDDESKTOP(pwnd)) {
  1322. fFlushPalette = (BOOL)TestWF(pwnd, WFHASPALETTE);
  1323. }
  1324. break;
  1325. case SW_SHOWNOACTIVATE:
  1326. if (gpqForeground && gpqForeground->spwndActive)
  1327. swpFlags |= SWP_NOACTIVATE;
  1328. /*
  1329. * FALL THRU
  1330. */
  1331. case SW_RESTORE:
  1332. /*
  1333. * If restoring a minimized window that was maximized before
  1334. * being minimized, go back to being maximized.
  1335. */
  1336. if (TestWF(pwnd, WFMINIMIZED) && pcp->fWasMaximizedBeforeMinimized)
  1337. cmd = SW_SHOWMAXIMIZED;
  1338. else
  1339. cmd = SW_NORMAL;
  1340. /*
  1341. * FALL THRU
  1342. */
  1343. case SW_NORMAL:
  1344. case SW_SHOWMAXIMIZED:
  1345. if (cmd == SW_SHOWMAXIMIZED) {
  1346. /*
  1347. * If already maximized and visible, we have nothing to do
  1348. * Otherwise, for the DOSbox, still set fMaxStateChanging
  1349. * to TRUE so we recalc the monitor region if need be.
  1350. * That way WinOldAp can change its "changing from maxed to
  1351. * maxed with new bigger font" code to work right.
  1352. */
  1353. if (TestWF(pwnd, WFMAXIMIZED)) {
  1354. if (TestWF(pwnd, WFVISIBLE)) {
  1355. return NULL;
  1356. }
  1357. } else {
  1358. /*
  1359. * We're changing from normal to maximized, so always
  1360. * send WM_SIZE.
  1361. */
  1362. swpFlags |= SWP_STATECHANGE;
  1363. }
  1364. fMaxStateChanging = TRUE;
  1365. /*
  1366. * If calling from CreateWindow, don't let the thing become
  1367. * activated by the SWP call below. Acitvation will happen
  1368. * on the ShowWindow done by CreateWindow or the app.
  1369. */
  1370. if (dwFlags & MINMAX_KEEPHIDDEN)
  1371. swpFlags |= SWP_NOACTIVATE;
  1372. /*
  1373. * This is for MDI's auto-restore behaviour (craigc)
  1374. */
  1375. if (TestWF(pwnd, WFMINIMIZED))
  1376. pcp->fWasMinimizedBeforeMaximized = TRUE;
  1377. xxxInitSendValidateMinMaxInfo(pwnd, &mmi);
  1378. } else {
  1379. /*
  1380. * We're changing state from non-normal to normal. Make
  1381. * sure WM_SIZE gets sents.
  1382. */
  1383. UserAssert(HIBYTE(WFMINIMIZED) == HIBYTE(WFMAXIMIZED));
  1384. if (TestWF(pwnd, WFMINIMIZED | WFMAXIMIZED)) {
  1385. swpFlags |= SWP_STATECHANGE;
  1386. }
  1387. if (TestWF(pwnd, WFMAXIMIZED)) {
  1388. fMaxStateChanging = TRUE;
  1389. }
  1390. }
  1391. /*
  1392. * If currently minimized, show windows' popups
  1393. */
  1394. if (TestWF(pwnd, WFMINIMIZED)) {
  1395. /*
  1396. * Send WM_QUERYOPEN to make sure this guy should unminimize
  1397. */
  1398. if (!xxxSendMessage(pwnd, WM_QUERYOPEN, 0, 0L))
  1399. return NULL;
  1400. idAnimation = IDANI_OPEN;
  1401. fShowOwned = TRUE;
  1402. fSetFocus = TRUE;
  1403. /*
  1404. * JEFFBOG B#2868
  1405. * Condition added before setting fSendActivate prevents
  1406. * WM_ACTIVATE message from reaching a child window. Might
  1407. * be backwards compatibility problems if a pre 3.1 app
  1408. * relies on WM_ACTIVATE reaching a child.
  1409. */
  1410. if (!TestWF(pwnd, WFCHILD))
  1411. fSendActivate = TRUE;
  1412. swpFlags |= SWP_NOCOPYBITS;
  1413. } else {
  1414. idAnimation = IDANI_CAPTION;
  1415. }
  1416. if (cmd == SW_SHOWMAXIMIZED) {
  1417. rc.left = mmi.ptMaxPosition.x;
  1418. rc.top = mmi.ptMaxPosition.y;
  1419. rc.right = rc.left + mmi.ptMaxSize.x;
  1420. rc.bottom = rc.top + mmi.ptMaxSize.y;
  1421. SetWF(pwnd, WFMAXIMIZED);
  1422. } else {
  1423. CopyRect(&rc, &rcRestore);
  1424. ClrWF(pwnd, WFMAXIMIZED);
  1425. }
  1426. /*
  1427. * We do this TestWF again since we left the critical section
  1428. * above and someone might have already 'un-minimized us'.
  1429. */
  1430. if (TestWF(pwnd, WFMINIMIZED)) {
  1431. if (!TestWF(pwnd, WFWIN40COMPAT))
  1432. fIsTrayWindowNow = IsTrayWindow(pwnd);
  1433. /*
  1434. * Mark it as minimized and adjust cVisWindows.
  1435. */
  1436. SetMinimize(pwnd, SMIN_CLEAR);
  1437. uEvent = EVENT_SYSTEM_MINIMIZEEND;
  1438. /*
  1439. * if we're unminimizing a window that is now
  1440. * not seen in maximized/restore mode then remove him
  1441. * from the tray
  1442. */
  1443. if (!TestWF(pwnd, WFWIN40COMPAT) &&
  1444. (fIsTrayWindowNow != IsTrayWindow(pwnd)) &&
  1445. FDoTray()) {
  1446. HWND hw = HWq(pwnd);
  1447. if (FCallHookTray()) {
  1448. xxxCallHook(HSHELL_WINDOWDESTROYED,
  1449. (WPARAM)hw,
  1450. (LPARAM)0,
  1451. WH_SHELL);
  1452. }
  1453. /*
  1454. * NT specific code. Post the window-destroyed message
  1455. * to the shell.
  1456. */
  1457. if (FPostTray(pwnd->head.rpdesk))
  1458. PostShellHookMessages(HSHELL_WINDOWDESTROYED, (LPARAM)hw);
  1459. }
  1460. fIsTrayWindowNow = FALSE;
  1461. /*
  1462. * If we're un-minimizing a visible top-level window, cVisWindows
  1463. * was zero, and we're either activating a window or showing
  1464. * the currently active window, set ourselves into the
  1465. * foreground. If the window isn't currently visible
  1466. * then we can rely on SetWindowPos() to do the right
  1467. * thing for us.
  1468. */
  1469. if (!TestwndChild(pwnd) &&
  1470. TestWF(pwnd, WFVISIBLE) &&
  1471. (GETPTI(pwnd)->cVisWindows == 1) &&
  1472. (GETPTI(pwnd)->pq != gpqForeground) &&
  1473. (!(swpFlags & SWP_NOACTIVATE)
  1474. || (GETPTI(pwnd)->pq->spwndActive == pwnd))) {
  1475. xxxSetForegroundWindow2(pwnd, GETPTI(pwnd), SFW_STARTUP);
  1476. }
  1477. }
  1478. /*
  1479. * Ensure that client area gets recomputed, and that
  1480. * the frame gets redrawn to reflect the new state.
  1481. */
  1482. swpFlags |= SWP_DRAWFRAME;
  1483. break;
  1484. }
  1485. /*
  1486. * For the iconic case, we need to also show the window because it
  1487. * might not be visible yet.
  1488. */
  1489. Showit:
  1490. if (!(dwFlags & MINMAX_KEEPHIDDEN)) {
  1491. if (TestWF(pwnd, WFVISIBLE)) {
  1492. if (fShow)
  1493. swpFlags |= SWP_SHOWWINDOW;
  1494. /* if we're full screening a DOS BOX then don't draw
  1495. * the animation 'cause it looks bad.
  1496. * overloaded WFFULLSCREEN bit for MDI child windows --
  1497. * use it to indicate to not animate size change.
  1498. */
  1499. if (IsVisible(pwnd) &&
  1500. (dwFlags & MINMAX_ANIMATE) &&
  1501. idAnimation &&
  1502. (!TestWF(pwnd, WFCHILD) || !TestWF(pwnd, WFNOANIMATE))) {
  1503. /*
  1504. * If this top-level window is placed in a mirrored desktop,
  1505. * its coordinates should be mirrored here so that xxxAnimateCaptions
  1506. * works properly, however we shouldn't change the actual screen coordinates
  1507. * of the window. This is why I do it here and restore it afterwards before
  1508. * doing the _DeferWindowPos(...). [samera]
  1509. */
  1510. RECT rcT;
  1511. if (bMirroredParent) {
  1512. int iLeft = rc.left;
  1513. rcT = rc;
  1514. rc.left = pwndParent->rcWindow.right - rc.right;
  1515. rc.right = pwndParent->rcWindow.right - iLeft;
  1516. }
  1517. if ((idAnimation != IDANI_CAPTION) && IsTrayWindow(pwnd)) {
  1518. RECT rcMin;
  1519. SetRectEmpty(&rcMin);
  1520. #if 0 // Win95 call.
  1521. CallHook(HSHELL_GETMINRECT, (WPARAM)HW16(hwnd), (LPARAM)(LPRECT)&rcMin, WH_SHELL);
  1522. #else
  1523. xxxSendMinRectMessages(pwnd, &rcMin);
  1524. #endif
  1525. if (!IsRectEmpty(&rcMin)) {
  1526. if (idAnimation == IDANI_CLOSE) {
  1527. xxxDrawAnimatedRects(pwnd,
  1528. IDANI_CAPTION,
  1529. &rcWindow,
  1530. &rcMin);
  1531. } else {
  1532. xxxDrawAnimatedRects(pwnd,
  1533. IDANI_CAPTION,
  1534. &rcMin,
  1535. &rc);
  1536. }
  1537. }
  1538. } else {
  1539. xxxDrawAnimatedRects(pwnd, IDANI_CAPTION, &rcWindow, &rc);
  1540. }
  1541. /*
  1542. * Restore the original rect, after doing the animation
  1543. */
  1544. if (bMirroredParent) {
  1545. rc = rcT;
  1546. }
  1547. }
  1548. } else {
  1549. swpFlags |= SWP_SHOWWINDOW;
  1550. }
  1551. }
  1552. /*
  1553. * hack for VB - we add their window in when their minimizing.
  1554. */
  1555. if (!TestWF(pwnd, WFWIN40COMPAT) && fIsTrayWindowNow && FDoTray()) {
  1556. HWND hw = HWq(pwnd);
  1557. if (FCallHookTray()) {
  1558. xxxCallHook(HSHELL_WINDOWCREATED,
  1559. (WPARAM)hw,
  1560. (LPARAM)0,
  1561. WH_SHELL);
  1562. }
  1563. /*
  1564. * NT specific code. Post the window-created message
  1565. * to the shell.
  1566. */
  1567. if (FPostTray(pwnd->head.rpdesk))
  1568. PostShellHookMessages(HSHELL_WINDOWCREATED, (LPARAM)hw);
  1569. }
  1570. /*
  1571. * BACKWARD COMPATIBILITY HACK:
  1572. *
  1573. * Because SetWindowPos() won't honor sizing, moving and SWP_SHOWWINDOW
  1574. * at the same time in version 3.0 or below, we call DeferWindowPos()
  1575. * directly here.
  1576. */
  1577. if (psmwp = InternalBeginDeferWindowPos(1)) {
  1578. psmwp = _DeferWindowPos(psmwp,
  1579. pwnd,
  1580. ((hwndAfter != NULL) ? RevalidateHwnd(hwndAfter) : NULL),
  1581. rc.left, rc.top,
  1582. rc.right - rc.left,
  1583. rc.bottom - rc.top,
  1584. swpFlags);
  1585. if (psmwp) {
  1586. /*
  1587. * HACK FOR MULTIPLE MONITOR TRUE MAXIMIZATION CLIPPING
  1588. * On a multiple monitor system, we would like the
  1589. * borders not to spill over onto another monitor when a
  1590. * window 'really' maximizes. The only way to get this
  1591. * to work right is to set a rectangular region, namely
  1592. * a copy of the monitor region, on the window. We can
  1593. * only do this if the window isn't currently regional.
  1594. *
  1595. * Going to maximized: Add the monitor region
  1596. * Coming from maximized: Remove the monitor region
  1597. */
  1598. if (fMaxStateChanging && gpDispInfo->cMonitors > 1) {
  1599. if ( TestWF(pwnd, WFMAXIMIZED) &&
  1600. pwnd->spwndParent == PWNDDESKTOP(pwnd)) {
  1601. psmwp->acvr[0].hrgnClip = HRGN_MONITOR;
  1602. } else if (TestWF(pwnd, WFMAXFAKEREGIONAL)) {
  1603. UserAssert(pwnd->hrgnClip);
  1604. psmwp->acvr[0].hrgnClip = HRGN_FULL;
  1605. }
  1606. }
  1607. xxxEndDeferWindowPosEx(psmwp, FALSE);
  1608. }
  1609. }
  1610. if (uEvent) {
  1611. xxxWindowEvent(uEvent, pwnd, OBJID_WINDOW, 0, WEF_USEPWNDTHREAD);
  1612. }
  1613. /*
  1614. * COMPATIBILITY HACK:
  1615. * Borland's OBEX expects a WM_PAINT message when it starts running
  1616. * minimized and initializes all it's data during that message.
  1617. * So, we generate a bogus WM_PAINT message here.
  1618. * Also, Visionware's XServer can not handle getting a WM_PAINT msg, as it
  1619. * would always get a WM_PAINTICON msg in 3.1, so make sure the logic is here
  1620. * to generate the correct message.
  1621. */
  1622. if((cmd == SW_SHOWMINIMIZED) &&
  1623. (!TestWF(pwnd, WFWIN40COMPAT)) &&
  1624. TestWF(pwnd, WFVISIBLE) &&
  1625. TestWF(pwnd, WFTOPLEVEL)) {
  1626. if (pwnd->pcls->spicn)
  1627. _PostMessage(pwnd, WM_PAINTICON, (WPARAM)TRUE, 0L);
  1628. else
  1629. _PostMessage(pwnd, WM_PAINT, 0, 0L);
  1630. }
  1631. if (fShowOwned)
  1632. xxxShowOwnedWindows(pwnd, SW_PARENTOPENING, NULL);
  1633. if ((cmd == SW_MINIMIZE) && (pwnd->spwndParent == PWNDDESKTOP(pwnd))) {
  1634. if (!xxxActivateOnMinimize(pwnd)) {
  1635. xxxActivateWindow(pwnd, AW_SKIP);
  1636. }
  1637. {
  1638. PEPROCESS p;
  1639. if (gptiForeground && ptiCurrent->ppi != gptiForeground->ppi && !(ptiCurrent->TIF_flags & TIF_SYSTEMTHREAD)) {
  1640. p = PsGetThreadProcess(ptiCurrent->pEThread);
  1641. KeAttachProcess(PsGetProcessPcb(p));
  1642. Status = MmAdjustWorkingSetSize((SIZE_T)-1,
  1643. (SIZE_T)-1,
  1644. FALSE,
  1645. TRUE);
  1646. KeDetachProcess();
  1647. if (!NT_SUCCESS(Status)) {
  1648. RIPMSG1(RIP_ERROR, "Error adjusting working set, status = %x\n", Status);
  1649. }
  1650. }
  1651. }
  1652. /*
  1653. * If any app is starting, restore its right to foreground activate
  1654. * (activate and come on top of everything else) because we just
  1655. * minimized what we were working on.
  1656. */
  1657. RestoreForegroundActivate();
  1658. }
  1659. /*
  1660. * If going from iconic, insure the focus is in the window.
  1661. */
  1662. if (fSetFocus)
  1663. xxxSetFocus(pwnd);
  1664. /*
  1665. * This was added for 1.03 compatibility reasons. If apps watch
  1666. * WM_ACTIVATE to set their focus, sending this message will appear
  1667. * as if the window just got activated (like in 1.03). Before this
  1668. * was added, opening an iconic window never sent this message since
  1669. * it was already active (but HIWORD(lParam) != 0).
  1670. */
  1671. if (fSendActivate)
  1672. xxxSendMessage(pwnd, WM_ACTIVATE, WA_ACTIVE, 0);
  1673. /*
  1674. * Flush the palette. We do this on a minimize of a palette app.
  1675. */
  1676. if (fFlushPalette)
  1677. xxxFlushPalette(pwnd);
  1678. Exit:
  1679. return NULL;
  1680. }
  1681. /***************************************************************************\
  1682. * xxxMinimizeHungWindow
  1683. *
  1684. * 10/31/96 vadimg created
  1685. \***************************************************************************/
  1686. void xxxMinimizeHungWindow(PWND pwnd)
  1687. {
  1688. RECT rcMin;
  1689. HRGN hrgnHung;
  1690. CheckLock(pwnd);
  1691. /*
  1692. * If the window is already minimized or not visible don't do anything.
  1693. */
  1694. if (TestWF(pwnd, WFMINIMIZED) || !TestWF(pwnd, WFVISIBLE))
  1695. return;
  1696. /*
  1697. * Animate the caption to the minimized state.
  1698. */
  1699. if (TEST_PUDF(PUDF_ANIMATE)) {
  1700. SetRectEmpty(&rcMin);
  1701. xxxSendMinRectMessages(pwnd, &rcMin);
  1702. if (!IsRectEmpty(&rcMin)) {
  1703. xxxDrawAnimatedRects(pwnd, IDANI_CAPTION, &pwnd->rcWindow, &rcMin);
  1704. }
  1705. }
  1706. /*
  1707. * Reset the visible bit on the window itself and ownees. At the same
  1708. * time calculate how much needs to be repainted. We must invalidate
  1709. * the DC cache to make sure that the visible regions get recalculated.
  1710. */
  1711. SetVisible(pwnd, SV_UNSET);
  1712. hrgnHung = GreCreateRectRgnIndirect(&pwnd->rcWindow);
  1713. xxxShowOwnedWindows(pwnd, SW_PARENTCLOSING, hrgnHung);
  1714. zzzInvalidateDCCache(pwnd, IDC_DEFAULT);
  1715. xxxRedrawWindow(NULL, NULL, hrgnHung, RDW_INVALIDATE | RDW_ERASE | RDW_ALLCHILDREN);
  1716. GreDeleteObject(hrgnHung);
  1717. /*
  1718. * Deal with activating some other window for top-level windows.
  1719. */
  1720. if (pwnd->spwndParent == PWNDDESKTOP(pwnd)) {
  1721. xxxActivateOnMinimize(pwnd);
  1722. }
  1723. PostEventMessage(GETPTI(pwnd), GETPTI(pwnd)->pq, QEVENT_HUNGTHREAD, pwnd, 0, 0, 0);
  1724. }