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.

1172 lines
34 KiB

  1. /****************************** Module Header ******************************\
  2. * Module Name: scrollw.c
  3. *
  4. * Copyright (c) 1985 - 1999, Microsoft Corporation
  5. *
  6. * Window and DC scrolling routines.
  7. *
  8. * History:
  9. * 18-Jul-1991 DarrinM Recreated from Win 3.1 source.
  10. \***************************************************************************/
  11. #include "precomp.h"
  12. #pragma hdrstop
  13. /*
  14. * Problems so far:
  15. * DCs not at origin (0, 0)
  16. * funny coordinate systems
  17. */
  18. /***************************************************************************\
  19. * GetTrueClipRgn
  20. *
  21. * Get copy of true clip region and its bounds.
  22. *
  23. * History:
  24. * 18-Jul-1991 DarrinM Ported from Win 3.1 sources.
  25. \***************************************************************************/
  26. int GetTrueClipRgn(
  27. HDC hdc,
  28. HRGN hrgnClip)
  29. {
  30. POINT pt;
  31. int code;
  32. code = GreCopyVisRgn(hdc, hrgnClip);
  33. /*
  34. * NOTE!!! The global ghrgnScrl2 is used in this routine!
  35. */
  36. GreGetDCOrg(hdc, &pt);
  37. if (GreGetRandomRgn(hdc, ghrgnScrl2, 1)) {
  38. GreOffsetRgn(ghrgnScrl2, pt.x, pt.y);
  39. code = IntersectRgn(hrgnClip, hrgnClip, ghrgnScrl2);
  40. }
  41. /*
  42. * Finally convert the result to DC coordinates
  43. */
  44. GreOffsetRgn(hrgnClip, -pt.x, -pt.y);
  45. return code;
  46. }
  47. /***************************************************************************\
  48. * InternalScrollDC
  49. *
  50. * This function requires all input parameters in device coordinates
  51. * (NOT screen!)
  52. *
  53. * History:
  54. * 18-Jul-1991 DarrinM Ported from Win 3.1 sources.
  55. \***************************************************************************/
  56. int InternalScrollDC(
  57. HDC hdc,
  58. int dx,
  59. int dy,
  60. RECT *prcSrc,
  61. RECT *prcClip,
  62. HRGN hrgnInvalid,
  63. HRGN hrgnUpdate,
  64. LPRECT prcUpdate,
  65. BOOL fLogUnits)
  66. {
  67. RECT rcVis;
  68. RECT rcSrc;
  69. RECT rcClip;
  70. RECT rcUnclippedSrc;
  71. RECT rcDst;
  72. RECT rcUpdate;
  73. RECT rcValid;
  74. BOOL fSrcNotEmpty;
  75. BOOL fHaveVisRgn;
  76. POINT rgpt[2];
  77. int dxLog;
  78. int dyLog;
  79. int wClip;
  80. int wClipValid;
  81. BOOL bMirroredDC=FALSE;
  82. fHaveVisRgn = FALSE;
  83. /*
  84. * Enter a critical region to ensure that no one changes visrgns
  85. * or update regions while we scroll bits around.
  86. */
  87. GreLockDisplay(gpDispInfo->hDev);
  88. if ((wClip = GreGetClipBox(hdc, &rcVis, TRUE)) == ERROR) {
  89. ErrorExit:
  90. GreUnlockDisplay(gpDispInfo->hDev);
  91. return ERROR;
  92. }
  93. CopyRect(&rcSrc, (prcSrc) ? prcSrc : &rcVis);
  94. if (prcClip) {
  95. CopyRect(&rcClip, prcClip);
  96. }
  97. dxLog = dx;
  98. dyLog = dy;
  99. if (fLogUnits) {
  100. /*
  101. * Convert input parameters to device coordinates
  102. */
  103. GreLPtoDP(hdc, (LPPOINT)&rcVis, 2);
  104. GreLPtoDP(hdc, (LPPOINT)&rcSrc, 2);
  105. //
  106. // Since this is a mirrored DC, then the resulting
  107. // device coord will be flowing from right to left
  108. // (i.e. rc.right < rc.left) so they should be flipped.
  109. // [samera]
  110. //
  111. if (GreGetLayout(hdc) & LAYOUT_RTL) {
  112. int iTemp = rcVis.left;
  113. rcVis.left = rcVis.right;
  114. rcVis.right = iTemp;
  115. iTemp = rcSrc.left;
  116. rcSrc.left = rcSrc.right;
  117. rcSrc.right = iTemp;
  118. bMirroredDC = TRUE;
  119. }
  120. if (prcClip) {
  121. GreLPtoDP(hdc, (LPPOINT)&rcClip, 2);
  122. //
  123. // Since this is a mirrored DC, then the resulting
  124. // device coord will be flowing from right to left
  125. // (i.e. rc.right < rc.left) so they should be flipped.
  126. // [samera]
  127. //
  128. if (bMirroredDC) {
  129. int iTemp = rcClip.left;
  130. rcClip.left = rcClip.right;
  131. rcClip.right = iTemp;
  132. }
  133. }
  134. /*
  135. * The delta values must be treated as a vector from
  136. * the point (0, 0) to (dx, dy). Scale it as such, then
  137. * compute the difference. This handles flipped coordinate systems.
  138. */
  139. rgpt[0].x = rgpt[0].y = 0;
  140. rgpt[1].x = dx;
  141. rgpt[1].y = dy;
  142. GreLPtoDP(hdc, rgpt, 2);
  143. dx = rgpt[1].x - rgpt[0].x;
  144. dy = rgpt[1].y - rgpt[0].y;
  145. }
  146. switch (wClip) {
  147. case NULLREGION:
  148. NullExit:
  149. if (hrgnUpdate && !SetEmptyRgn(hrgnUpdate))
  150. goto ErrorExit;
  151. if (prcUpdate) {
  152. SetRectEmpty(prcUpdate);
  153. }
  154. GreUnlockDisplay(gpDispInfo->hDev);
  155. return NULLREGION;
  156. case COMPLEXREGION:
  157. GetTrueClipRgn(hdc, ghrgnScrlVis);
  158. fHaveVisRgn = TRUE;
  159. break;
  160. }
  161. /*
  162. * First compute the source and destination rectangles.
  163. *
  164. * rcDst = Offset(rcSrc, dx, dy)
  165. */
  166. rcDst.left = rcSrc.left + dx;
  167. rcDst.right = rcSrc.right + dx;
  168. rcDst.top = rcSrc.top + dy;
  169. rcDst.bottom = rcSrc.bottom + dy;
  170. /*
  171. * If necessary, intersect with caller-supplied clip rect.
  172. */
  173. if (prcClip) {
  174. if ((wClip == SIMPLEREGION) &&
  175. ((hrgnInvalid == NULL) || (hrgnInvalid == HRGN_FULL))) {
  176. /*
  177. * Simple clip region: just a rect intersection
  178. */
  179. if (!IntersectRect(&rcVis, &rcVis, &rcClip))
  180. goto NullExit;
  181. } else {
  182. if (!fHaveVisRgn) {
  183. if (GetTrueClipRgn(hdc, ghrgnScrlVis) == ERROR)
  184. goto ErrorExit;
  185. fHaveVisRgn = TRUE;
  186. }
  187. SetRectRgnIndirect(ghrgnScrl1, &rcClip);
  188. wClip = IntersectRgn(ghrgnScrlVis, ghrgnScrl1, ghrgnScrlVis);
  189. switch (wClip) {
  190. case ERROR:
  191. goto ErrorExit;
  192. case NULLREGION:
  193. goto NullExit;
  194. case SIMPLEREGION:
  195. /*
  196. * If the clipped region is simple, we're back in fat
  197. * rect city.
  198. */
  199. GreGetRgnBox(ghrgnScrlVis, &rcVis);
  200. break;
  201. case COMPLEXREGION:
  202. break;
  203. }
  204. }
  205. }
  206. /*
  207. * Time for basic scrolling area calculations:
  208. *
  209. * Dst = Offset(Src, dx, dy) & Vis
  210. * Src = Src & Vis
  211. * Valid = Offset(Src, dx, dy) & Dst
  212. * Valid = Valid & Invalid & Offset(Invalid, dx, dy)
  213. * Update = (Src | Dst) - Valid
  214. *
  215. * If the vis region is simple, then we know that the valid region
  216. * will be rectangular.
  217. *
  218. * The rectangular calculation case can only deal with
  219. * ghrgnInvalid == NULL or (HRGN)1: the region case is handled the hard way.
  220. */
  221. if ((wClip == SIMPLEREGION) &&
  222. ((hrgnInvalid == NULL) || (hrgnInvalid == HRGN_FULL))) {
  223. /*
  224. * Save a copy of this for update rect calc optimization.
  225. */
  226. CopyRect(&rcUnclippedSrc, &rcSrc);
  227. /*
  228. * Dst = Offset(Src, dx, dy) & Vis.
  229. */
  230. IntersectRect(&rcDst, &rcDst, &rcVis);
  231. /*
  232. * Src = Src & Vis.
  233. */
  234. fSrcNotEmpty = IntersectRect(&rcSrc, &rcSrc, &rcVis);
  235. /*
  236. * Valid = Offset(Src, dx, dy) & Dst.
  237. */
  238. if (hrgnInvalid == HRGN_FULL) {
  239. SetRectEmpty(&rcValid);
  240. } else {
  241. rcValid.left = rcSrc.left + dx;
  242. rcValid.right = rcSrc.right + dx;
  243. rcValid.top = rcSrc.top + dy;
  244. rcValid.bottom = rcSrc.bottom + dy;
  245. IntersectRect(&rcValid, &rcValid, &rcDst);
  246. }
  247. /*
  248. * Now calculate the update area.
  249. *
  250. * There are two cases where the result will be a rectangle:
  251. *
  252. * 1) The source rectangle lies completely within the visrgn,
  253. * and the source and destination don't overlap. In this
  254. * case the update region is equal to the source rect.
  255. *
  256. * 2) The clipped source rectangle is empty, in which case
  257. * the update region is equal to the clipped dest rect.
  258. *
  259. * 3) We're scrolling in one dimension only, and the source
  260. * and destination DO overlap. In this case we can use
  261. * UnionRect() and SubtractRect() to do the area arithmetic.
  262. */
  263. if (!fSrcNotEmpty) {
  264. /*
  265. * Clipped source is empty. Update area is the clipped dest.
  266. */
  267. CopyRect(&rcUpdate, &rcDst);
  268. goto RectUpdate;
  269. } else if (IntersectRect(&rcUpdate, &rcSrc, &rcDst)) {
  270. /*
  271. * They overlap. If we're scrolling in one dimension only
  272. * then we can use rect arithmetic...
  273. */
  274. if (dx == 0 || dy == 0) {
  275. UnionRect(&rcUpdate, &rcSrc, &rcDst);
  276. SubtractRect(&rcUpdate, &rcUpdate, &rcValid);
  277. goto RectUpdate;
  278. }
  279. } else if (EqualRect(&rcSrc, &rcUnclippedSrc)) {
  280. /*
  281. * They don't overlap, and the source lies completely
  282. * within the visible region. Update region is the source.
  283. */
  284. CopyRect(&rcUpdate, &rcSrc);
  285. RectUpdate:
  286. if (prcUpdate) {
  287. CopyRect(prcUpdate, &rcUpdate);
  288. }
  289. if (hrgnUpdate && !SetRectRgnIndirect(hrgnUpdate, &rcUpdate)) {
  290. goto ErrorExit;
  291. }
  292. wClip = SIMPLEREGION;
  293. if (rcUpdate.left >= rcUpdate.right ||
  294. rcUpdate.top >= rcUpdate.bottom)
  295. wClip = NULLREGION;
  296. goto DoRectBlt;
  297. }
  298. /*
  299. * The update region isn't rectangular. Need to do our
  300. * area calculations with region calls. Skip all this
  301. * if the caller doesn't care about the update region.
  302. *
  303. * If he wants a rectangle but no region, use ghrgnScrl2 as a temp.
  304. */
  305. if (hrgnUpdate == NULL && prcUpdate) {
  306. hrgnUpdate = ghrgnScrl2;
  307. }
  308. if (hrgnUpdate != NULL) {
  309. /*
  310. * hrgnUpdateCalc = (rcSrc | rcDst) - rcBltDst
  311. */
  312. SetRectRgnIndirect(ghrgnScrl1, &rcSrc);
  313. SetRectRgnIndirect(hrgnUpdate, &rcDst);
  314. if (UnionRgn(hrgnUpdate, hrgnUpdate, ghrgnScrl1) == ERROR)
  315. goto ErrorExit;
  316. SetRectRgnIndirect(ghrgnScrl1, &rcValid);
  317. wClip = SubtractRgn(hrgnUpdate, hrgnUpdate, ghrgnScrl1);
  318. if (wClip == ERROR)
  319. goto ErrorExit;
  320. if (prcUpdate) {
  321. GreGetRgnBox(hrgnUpdate, prcUpdate);
  322. }
  323. }
  324. DoRectBlt:
  325. /*
  326. * If the valid rectangle's not empty, then copy those bits...
  327. */
  328. if (rcValid.left < rcValid.right && rcValid.top < rcValid.bottom) {
  329. /*
  330. * If the DC is in a funny map mode, then be sure to map from
  331. * device to logical coordinates for BLT call...
  332. */
  333. if (fLogUnits)
  334. GreDPtoLP(hdc, (LPPOINT)&rcValid, 2);
  335. GreBitBlt(hdc,
  336. rcValid.left,
  337. rcValid.top,
  338. rcValid.right - rcValid.left,
  339. rcValid.bottom - rcValid.top,
  340. hdc,
  341. rcValid.left - dxLog,
  342. rcValid.top - dyLog,
  343. SRCCOPY,
  344. 0);
  345. }
  346. } else {
  347. /*
  348. * Get the true visrgn if we haven't already.
  349. */
  350. if (!fHaveVisRgn) {
  351. if (GetTrueClipRgn(hdc, ghrgnScrlVis) == ERROR)
  352. goto ErrorExit;
  353. fHaveVisRgn = TRUE;
  354. }
  355. /*
  356. * The visrgn is not empty. Need to do all our calculations
  357. * with regions.
  358. *
  359. * hrgnSrc = hrgnSrc & ghrgnScrlVis
  360. */
  361. SetRectRgnIndirect(ghrgnScrlSrc, &rcSrc);
  362. if (IntersectRgn(ghrgnScrlSrc, ghrgnScrlSrc, ghrgnScrlVis) == ERROR)
  363. goto ErrorExit;
  364. /*
  365. * hrgnDst = hrgnDst & ghrgnScrlVis
  366. */
  367. SetRectRgnIndirect(ghrgnScrlDst, &rcDst);
  368. if (IntersectRgn(ghrgnScrlDst, ghrgnScrlDst, ghrgnScrlVis) == ERROR)
  369. goto ErrorExit;
  370. /*
  371. * Now compute the valid region:
  372. *
  373. * Valid = Offset(Src, dx, dy) & Dst.
  374. * Valid = Valid & Invalid & Offset(Invalid, dx, dy)
  375. *
  376. * If hrgnInvalid is (HRGN)1, then the valid area is empty.
  377. */
  378. wClipValid = NULLREGION;
  379. if (hrgnInvalid != HRGN_FULL) {
  380. /*
  381. * Valid = Offset(Src, dx, dy) & Dst
  382. */
  383. if (CopyRgn(ghrgnScrlValid, ghrgnScrlSrc) == ERROR)
  384. goto ErrorExit;
  385. GreOffsetRgn(ghrgnScrlValid, dx, dy);
  386. wClipValid = IntersectRgn(ghrgnScrlValid,
  387. ghrgnScrlValid,
  388. ghrgnScrlDst);
  389. /*
  390. * Valid = Valid - Invalid - Offset(Invalid, dx, dy)
  391. * We need bother only if hrgnInvalid is a real region.
  392. */
  393. if (hrgnInvalid > HRGN_FULL) {
  394. if (wClipValid != ERROR && wClipValid != NULLREGION) {
  395. POINT pt;
  396. GetDCOrgOnScreen(hdc, &pt);
  397. /*
  398. * hrgnInvalid is in screen coordinates: map to dc coords
  399. */
  400. CopyRgn(ghrgnScrl2, hrgnInvalid);
  401. GreOffsetRgn(ghrgnScrl2, -pt.x, -pt.y);
  402. wClipValid = SubtractRgn(ghrgnScrlValid,
  403. ghrgnScrlValid,
  404. ghrgnScrl2);
  405. }
  406. if (wClipValid != ERROR && wClipValid != NULLREGION) {
  407. GreOffsetRgn(ghrgnScrl2, dx, dy);
  408. wClipValid = SubtractRgn(ghrgnScrlValid,
  409. ghrgnScrlValid,
  410. ghrgnScrl2);
  411. }
  412. }
  413. if (wClipValid == ERROR)
  414. goto ErrorExit;
  415. }
  416. /*
  417. * If he wants a rectangle but no region, use ghrgnScrl2 as a temp.
  418. */
  419. if (hrgnUpdate == NULL && prcUpdate) {
  420. hrgnUpdate = ghrgnScrl2;
  421. }
  422. if (hrgnUpdate != NULL) {
  423. /*
  424. * Update = (Src | Dst) - Valid.
  425. */
  426. wClip = UnionRgn(hrgnUpdate, ghrgnScrlDst, ghrgnScrlSrc);
  427. if (wClip == ERROR)
  428. goto ErrorExit;
  429. if (wClipValid != NULLREGION) {
  430. wClip = SubtractRgn(hrgnUpdate, hrgnUpdate, ghrgnScrlValid);
  431. }
  432. if (prcUpdate) {
  433. GreGetRgnBox(hrgnUpdate, prcUpdate);
  434. }
  435. }
  436. if (wClipValid != NULLREGION) {
  437. #ifdef LATER
  438. /*
  439. * don't use the visrgn here
  440. */
  441. HRGN hrgnSaveVis = CreateEmptyRgn();
  442. if (hrgnSaveVis != NULL) {
  443. BOOL fClipped;
  444. fClipped = (GreGetRandomRgn(hdc, hrgnSaveVis, 1) == 1);
  445. GreExtSelectClipRgn(hdc, ghrgnScrlValid, RGN_COPY);
  446. /*
  447. * If the DC is in a funny map mode, then be sure to
  448. * map from device to logical coordinates for BLT call...
  449. */
  450. if (fLogUnits)
  451. GreDPtoLP(hdc, (LPPOINT)&rcDst, 2);
  452. /*
  453. * Gdi can take along time to process this call if
  454. * it's a printer DC
  455. */
  456. GreBitBlt(hdc,
  457. rcDst.left,
  458. rcDst.top,
  459. rcDst.right - rcDst.left,
  460. rcDst.bottom - rcDst.top,
  461. hdc,
  462. rcDst.left - dxLog,
  463. rcDst.top - dyLog,
  464. SRCCOPY,
  465. 0);
  466. GreExtSelectClipRgn(hdc,
  467. (fClipped ? hrgnSaveVis : NULL),
  468. RGN_COPY);
  469. GreDeleteObject(hrgnSaveVis);
  470. }
  471. #else
  472. /*
  473. * Visrgn is expected in DC surface coordinates: offset
  474. * as appropriate.
  475. */
  476. POINT pt;
  477. GreGetDCOrg(hdc, &pt);
  478. GreOffsetRgn(ghrgnScrlValid, pt.x, pt.y);
  479. /*
  480. * Select in the temporary vis rgn, saving the old
  481. */
  482. GreSelectVisRgn(hdc, ghrgnScrlValid, SVR_SWAP);
  483. /*
  484. * If the DC is in a funny map mode, then be sure to map from
  485. * device to logical coordinates for BLT call...
  486. */
  487. if (fLogUnits)
  488. GreDPtoLP(hdc, (LPPOINT)&rcDst, 2);
  489. /*
  490. * Gdi can take along time to process this call if it's
  491. * a printer DC.
  492. */
  493. GreBitBlt(hdc,
  494. rcDst.left,
  495. rcDst.top,
  496. rcDst.right - rcDst.left,
  497. rcDst.bottom - rcDst.top,
  498. hdc,
  499. rcDst.left - dxLog,
  500. rcDst.top - dyLog,
  501. SRCCOPY,
  502. 0);
  503. /*
  504. * Restore the old vis rgn, leaving ghrgnScrlValid with
  505. * a valid rgn
  506. */
  507. GreSelectVisRgn(hdc, ghrgnScrlValid, SVR_SWAP);
  508. #endif
  509. }
  510. }
  511. /*
  512. * If necessary, convert the resultant update rect back
  513. * to logical coordinates.
  514. */
  515. if (fLogUnits && prcUpdate) {
  516. GreDPtoLP(hdc, (LPPOINT)prcUpdate, 2);
  517. }
  518. GreUnlockDisplay(gpDispInfo->hDev);
  519. return wClip;
  520. }
  521. /***************************************************************************\
  522. * _ScrollDC (API)
  523. *
  524. *
  525. * History:
  526. * 18-Jul-1991 DarrinM Ported from Win 3.1 sources.
  527. \***************************************************************************/
  528. BOOL _ScrollDC(
  529. HDC hdc,
  530. int dx,
  531. int dy,
  532. LPRECT prcSrc,
  533. LPRECT prcClip,
  534. HRGN hrgnUpdate,
  535. LPRECT prcUpdate)
  536. {
  537. RECT rcSrc;
  538. RECT rcSpb;
  539. PWND pwnd;
  540. HRGN hrgnInvalid;
  541. BOOL fRet;
  542. /*
  543. * ScrollDC does not scroll update region. Under WinNT, an app calling
  544. * GetUpdateRgn() then ScrollDC() then InvalidateRgn() will not get
  545. * any new update region that happened between the Get and Scroll. Under
  546. * Win3.1, that was not a problem because no other app ran during this
  547. * time. So pass hrgnInvalid - this will affect the hrgnUpdate and
  548. * prcUpdate values being returned from ScrollDC with the update region.
  549. */
  550. hrgnInvalid = NULL;
  551. if ((pwnd = FastWindowFromDC(hdc)) != NULL) {
  552. hrgnInvalid = pwnd->hrgnUpdate;
  553. if (hrgnInvalid == HRGN_FULL) {
  554. /*
  555. * This is a fix for winhell, a performance testing app
  556. * written by some guy working for a windows magazine.
  557. * this app scrolls it's window while it is completely
  558. * invalid. We normaly won't scroll invalid bits but
  559. * but we make the exception here
  560. */
  561. hrgnInvalid = NULL;
  562. }
  563. }
  564. fRet = InternalScrollDC(hdc,
  565. dx,
  566. dy,
  567. prcSrc,
  568. prcClip,
  569. hrgnInvalid,
  570. hrgnUpdate,
  571. prcUpdate,
  572. TRUE) != ERROR;
  573. /*
  574. * InternalScrollDC() only scrolls those areas inside the visible region.
  575. * This means it does no operations on parts of the window if the window
  576. * isn't visible. This means SPBs don't get properly invalidated. This
  577. * could be seen by starting a dir, then moving another window with the
  578. * mouse (and keeping the mouse down until the dir finished). The
  579. * screen is remembered with an SPB, and the dir window doesn't get
  580. * properly invalidated because of this.
  581. */
  582. if (pwnd != NULL && AnySpbs()) {
  583. if (prcSrc) {
  584. rcSrc = *prcSrc;
  585. OffsetRect(&rcSrc, pwnd->rcClient.left, pwnd->rcClient.top);
  586. rcSpb = rcSrc;
  587. OffsetRect(&rcSpb, dx, dy);
  588. UnionRect(&rcSpb, &rcSpb, &rcSrc);
  589. } else {
  590. rcSpb = pwnd->rcClient;
  591. }
  592. SpbCheckRect(pwnd, &rcSpb, 0);
  593. }
  594. return fRet;
  595. }
  596. /***************************************************************************\
  597. * ScrollWindowEx (API)
  598. *
  599. *
  600. * History:
  601. * 18-Jul-1991 DarrinM Ported from Win 3.1 sources.
  602. \***************************************************************************/
  603. int xxxScrollWindowEx(
  604. PWND pwnd,
  605. int dx,
  606. int dy,
  607. RECT *prcScroll,
  608. RECT *prcClip,
  609. HRGN hrgnUpdate,
  610. LPRECT prcUpdate,
  611. DWORD flags)
  612. {
  613. INT code;
  614. HDC hdc;
  615. int dxDev;
  616. int dyDev;
  617. RECT rcSrcDev;
  618. RECT rcSpb, rcSrc;
  619. DWORD flagsDCX;
  620. BOOL fHideCaret;
  621. BOOL fRcScroll = (prcScroll != NULL);
  622. BOOL fInvisible = FALSE;
  623. PCARET pcaret;
  624. POINT pt;
  625. TL tlpwndChild;
  626. HRGN hrgnInvalid;
  627. PTHREADINFO ptiCurrent = PtiCurrent();
  628. CheckLock(pwnd);
  629. UserAssert(IsWinEventNotifyDeferredOK());
  630. if (pwnd == NULL)
  631. pwnd = ptiCurrent->rpdesk->pDeskInfo->spwnd; // pwndDesktop
  632. if (TestWF(pwnd, WEFLAYOUTRTL)) {
  633. dx = -dx;
  634. MirrorRegion(pwnd, hrgnUpdate, TRUE);
  635. if(prcScroll) {
  636. MirrorClientRect(pwnd, prcScroll);
  637. }
  638. if (prcClip) {
  639. MirrorClientRect(pwnd, prcClip);
  640. }
  641. }
  642. /*
  643. * If nothing's moving, nothing to do.
  644. */
  645. if ((dx | dy) == 0 ) {
  646. goto DoNothing;
  647. } else if (!IsVisible(pwnd)) {
  648. /* We want to offset our children if we're not minimized. IsVisible()
  649. * will return FALSE if we're minimized, invisible, or the child of
  650. * a minimized/invisible ancestore.
  651. */
  652. if (!TestWF(pwnd, WFMINIMIZED) &&
  653. (flags & SW_SCROLLCHILDREN) &&
  654. !fRcScroll) {
  655. fInvisible = TRUE;
  656. flags &= ~SW_INVALIDATE;
  657. }
  658. DoNothing:
  659. if (hrgnUpdate) {
  660. SetEmptyRgn(hrgnUpdate);
  661. }
  662. if (prcUpdate) {
  663. SetRectEmpty(prcUpdate);
  664. }
  665. if (!fInvisible)
  666. return NULLREGION;
  667. }
  668. /*
  669. * Hide the caret.
  670. */
  671. fHideCaret = FALSE;
  672. if (!fInvisible) {
  673. pcaret = &ptiCurrent->pq->caret;
  674. if (pcaret->spwnd != NULL && _IsDescendant(pcaret->spwnd, pwnd)) {
  675. fHideCaret = TRUE;
  676. zzzInternalHideCaret();
  677. }
  678. }
  679. /*
  680. * If scrollwindow, and window is clipchildren, use a cache entry.
  681. * Otherwise, always use a
  682. *
  683. * Determine what kind of DC we'll be needing. If the DCX_CACHE bit
  684. * isn't set, it means that we'll be operating in logical coordinates.
  685. */
  686. if (flags & SW_SCROLLWINDOW) {
  687. /*
  688. * ScrollWindow() call: use the cache if not OWNDC or CLASSDC.
  689. */
  690. flagsDCX = DCX_USESTYLE;
  691. if (!TestCF(pwnd, CFOWNDC) && !TestCF(pwnd, CFCLASSDC))
  692. flagsDCX |= DCX_CACHE;
  693. /*
  694. * If SW_SCROLLCHILDREN (i.e., lprcScroll == NULL) and CLIPCHILDREN,
  695. * then use the cache and don't clip children.
  696. * This is screwy, but 3.0 backward compatible.
  697. */
  698. if ((flags & SW_SCROLLCHILDREN) && TestWF(pwnd, WFCLIPCHILDREN))
  699. flagsDCX |= DCX_NOCLIPCHILDREN | DCX_CACHE;
  700. } else {
  701. /*
  702. * ScrollWindowEx() call: always use the cache
  703. */
  704. flagsDCX = DCX_USESTYLE | DCX_CACHE;
  705. /*
  706. * if SW_SCROLLCHILDREN, always use noclipchildren.
  707. */
  708. if (flags & SW_SCROLLCHILDREN)
  709. flagsDCX |= DCX_NOCLIPCHILDREN;
  710. }
  711. flagsDCX |= DCX_NOMIRROR;
  712. hdc = _GetDCEx(pwnd, NULL, flagsDCX);
  713. if (flags & SW_INVALIDATE) {
  714. /*
  715. * Get device origin while DC is valid, for later offsetting
  716. */
  717. GetDCOrgOnScreen(hdc, &pt);
  718. /*
  719. * If the user didn't give us a region to use, use ghrgnSW.
  720. */
  721. if (hrgnUpdate == NULL)
  722. hrgnUpdate = ghrgnSW;
  723. }
  724. /*
  725. * The DC will be in some logical coordinate system if OWNDC or CLASSDC.
  726. */
  727. if (!fRcScroll) {
  728. prcScroll = &rcSrc;
  729. /*
  730. * IMPORTANT:
  731. * We have to use CopyOffsetRect() here because GetClientRect() gives
  732. * unreliable results for minimized windows. 3.1 dudes get told that
  733. * their client is non-empty, for compatibility reasons.
  734. */
  735. GetRect(pwnd, &rcSrc, GRECT_CLIENT | GRECT_CLIENTCOORDS);
  736. /*
  737. * If the DC might be a screwy one, then map the
  738. * rect to logical units.
  739. */
  740. if (!(flagsDCX & DCX_CACHE))
  741. GreDPtoLP(hdc, (LPPOINT)&rcSrc, 2);
  742. }
  743. /*
  744. * If the DC is in logical coordinates, map *prcScroll and dx, dy
  745. * to device units for use later.
  746. */
  747. dxDev = dx;
  748. dyDev = dy;
  749. rcSrcDev = *prcScroll;
  750. if (!(flagsDCX & DCX_CACHE)) {
  751. POINT rgpt[2];
  752. GreLPtoDP(hdc, (POINT FAR*)&rcSrcDev, 2);
  753. /*
  754. * The delta values must be treated as a vector from
  755. * the point (0, 0) to (dx, dy). Scale it as such, then
  756. * compute the difference. This handles flipped coordinate systems.
  757. */
  758. rgpt[0].x = rgpt[0].y = 0;
  759. rgpt[1].x = dx;
  760. rgpt[1].y = dy;
  761. GreLPtoDP(hdc, rgpt, 2);
  762. dxDev = rgpt[1].x - rgpt[0].x;
  763. dyDev = rgpt[1].y - rgpt[0].y;
  764. }
  765. if (fInvisible)
  766. code = NULLREGION;
  767. else {
  768. hrgnInvalid = pwnd->hrgnUpdate;
  769. if ((flags & SW_SCROLLWINDOW) && !TestWF(pwnd, WFWIN31COMPAT)) {
  770. /*
  771. * 3.0 Backward compatibility hack:
  772. * The following incorrect code is what 3.0 used to do, and
  773. * there are apps such as Finale and Scrapbook+ that have worked
  774. * around this bug in ways that don't work with the "correct" code.
  775. */
  776. if (pwnd->hrgnUpdate > HRGN_FULL) {
  777. RECT rc;
  778. GreGetRgnBox(pwnd->hrgnUpdate, &rc);
  779. OffsetRect(&rc,
  780. dxDev - pwnd->rcClient.left,
  781. dyDev - pwnd->rcClient.top);
  782. xxxRedrawWindow(pwnd,
  783. &rc, NULL,
  784. RDW_INVALIDATE | RDW_ERASE | RDW_ALLCHILDREN);
  785. }
  786. hrgnInvalid = NULL;
  787. }
  788. code = InternalScrollDC(hdc,
  789. dx,
  790. dy,
  791. prcScroll,
  792. prcClip,
  793. hrgnInvalid,
  794. hrgnUpdate,
  795. prcUpdate,
  796. !(flagsDCX & DCX_CACHE));
  797. if (prcUpdate && TestWF(pwnd, WEFLAYOUTRTL)) {
  798. MirrorClientRect(pwnd, prcUpdate);
  799. }
  800. }
  801. /*
  802. * Release the hdc we used.
  803. */
  804. _ReleaseDC(hdc);
  805. /*
  806. * Check the union of the src and dst rectangle against any SPBs.
  807. * We do this because the window
  808. * might be completely obscured by some window with an SPB, but
  809. * since we're completely covered no BitBlt call will be made
  810. * to accumulate bounds in that area.
  811. */
  812. if (!fInvisible && AnySpbs()) {
  813. if (fRcScroll) {
  814. if (pwnd == PWNDDESKTOP(pwnd)) {
  815. rcSrc = rcSrcDev;
  816. } else {
  817. CopyOffsetRect(
  818. &rcSrc,
  819. &rcSrcDev,
  820. pwnd->rcClient.left,
  821. pwnd->rcClient.top);
  822. }
  823. rcSpb = rcSrc;
  824. OffsetRect(&rcSpb, dxDev, dyDev);
  825. UnionRect(&rcSpb, &rcSpb, &rcSrc);
  826. } else {
  827. /*
  828. * Use the entire client area.
  829. */
  830. rcSpb = pwnd->rcClient;
  831. }
  832. SpbCheckRect(pwnd, &rcSpb, 0);
  833. }
  834. /*
  835. * If this guy wants to scroll his children, go at it. Only scroll those
  836. * children intersecting prcScroll. Then invalidate any vis rgns
  837. * calculated for these child windows.
  838. */
  839. if (flags & SW_SCROLLCHILDREN) {
  840. RECT rc;
  841. /*
  842. * If this window has the caret then offset it if:
  843. * a) The whole window is scrolling
  844. * b) The rectangle scrolled contains the caret rectangle
  845. */
  846. if (!fInvisible && (pwnd == pcaret->spwnd)) {
  847. if (fRcScroll)
  848. SetRect(&rc,
  849. pcaret->x,
  850. pcaret->y,
  851. pcaret->x + pcaret->cx,
  852. pcaret->y + pcaret->cy);
  853. if (!fRcScroll || IntersectRect(&rc, &rc, &rcSrcDev)) {
  854. pcaret->x += dxDev;
  855. pcaret->y += dyDev;
  856. }
  857. }
  858. if (fRcScroll) {
  859. /*
  860. * Create a copy of prcScroll and map to absolute coordinates...
  861. */
  862. if (pwnd == PWNDDESKTOP(pwnd)) {
  863. CopyRect(&rc, &rcSrcDev);
  864. } else {
  865. CopyOffsetRect(
  866. &rc,
  867. &rcSrcDev,
  868. pwnd->rcClient.left,
  869. pwnd->rcClient.top);
  870. }
  871. }
  872. if (pwnd->spwndChild) {
  873. OffsetChildren(pwnd,
  874. dxDev,
  875. dyDev,
  876. (fRcScroll ? (LPRECT)&rc : NULL));
  877. /*
  878. * If we're clipchildren, then shuffling our children
  879. * will affect our client visrgn (but not our window visrgn).
  880. * Otherwise, only our children's
  881. * visrgns were affected by the scroll.
  882. * No need to DeferWinEventNotify() judging by xxxInternalInvalidate() below
  883. */
  884. zzzInvalidateDCCache(pwnd,
  885. TestWF(pwnd, WFCLIPCHILDREN) ?
  886. IDC_CLIENTONLY : IDC_CHILDRENONLY);
  887. }
  888. }
  889. if (flags & SW_INVALIDATE) {
  890. /*
  891. * If the caller supplied a region, invalidate using a copy,
  892. * because InternalInvalidate may trash the passed-in region.
  893. */
  894. if (hrgnUpdate != ghrgnSW)
  895. CopyRgn(ghrgnSW, hrgnUpdate);
  896. /*
  897. * Make ghrgnSW screen-relative before invalidation...
  898. */
  899. GreOffsetRgn(ghrgnSW, pt.x, pt.y);
  900. xxxInternalInvalidate(
  901. pwnd,
  902. ghrgnSW,
  903. (flags & SW_ERASE) ?
  904. (RDW_INVALIDATE | RDW_ALLCHILDREN | RDW_ERASE) :
  905. (RDW_INVALIDATE | RDW_ALLCHILDREN));
  906. }
  907. /*
  908. * Send child move messages if needed.
  909. */
  910. if (flags & SW_SCROLLCHILDREN) {
  911. PWND pwndChild;
  912. RECT rc;
  913. RECT rcScrolledChildren;
  914. /*
  915. * NOTE: the following code will send MOVE messages
  916. * to windows that didn't move but were in the source rectangle.
  917. * This is not a big deal, and definitely not worth fixing.
  918. */
  919. if (fRcScroll) {
  920. if (pwnd->spwndParent == PWNDDESKTOP(pwnd)) {
  921. CopyOffsetRect(&rcScrolledChildren, &rcSrcDev, dxDev, dyDev);
  922. } else {
  923. CopyOffsetRect(
  924. &rcScrolledChildren,
  925. &rcSrcDev,
  926. dxDev + pwnd->spwndParent->rcClient.left,
  927. dyDev + pwnd->spwndParent->rcClient.top);
  928. }
  929. }
  930. ThreadLockNever(&tlpwndChild);
  931. pwndChild = pwnd->spwndChild;
  932. while (pwndChild != NULL) {
  933. if ( !fRcScroll ||
  934. IntersectRect(&rc, &rcScrolledChildren, &pwndChild->rcWindow)) {
  935. /*
  936. * NOTE: Win 3.0 and below passed wParam == TRUE here.
  937. * This was not documented or used, so it was changed
  938. * to be consistent with the documentation.
  939. */
  940. ThreadLockExchangeAlways(pwndChild, &tlpwndChild);
  941. xxxSendMessage(
  942. pwndChild,
  943. WM_MOVE,
  944. 0,
  945. (pwnd == PWNDDESKTOP(pwnd)) ?
  946. MAKELONG(pwndChild->rcClient.left, pwndChild->rcClient.top) :
  947. MAKELONG(pwndChild->rcClient.left - pwnd->rcClient.left,
  948. pwndChild->rcClient.top - pwnd->rcClient.top));
  949. }
  950. pwndChild = pwndChild->spwndNext;
  951. }
  952. ThreadUnlock(&tlpwndChild);
  953. }
  954. if (fHideCaret) {
  955. /*
  956. * Show the caret again.
  957. */
  958. zzzInternalShowCaret();
  959. }
  960. /*
  961. * Return the region code.
  962. */
  963. return code;
  964. }