/**************************** Module Header ********************************\ * Module Name: spb.c * * Copyright (c) 1985 - 1999, Microsoft Corporation * * Save Popup Bits (SPB) support routines. * * History: * 18-Jul-1991 DarrinM Created. \***************************************************************************/ #include "precomp.h" #pragma hdrstop /***************************************************************************\ * FBitsTouch * * This routine checkes to see if the rectangle *lprcDirty in pwndDirty * invalidates any bits in the SPB structure at *pspb. * * pwndDirty "touches" pwndSpb if: * 1. pwndDirty is visible AND: * 2. pwndDirty == or descendent of pwndSpb, and pwndSpb is a LOCKUPDATE * spb. * 3. pwndDirty is pwndSpb's parent. (e.g., drawing in the * desktop window, behind a dialog box). * 4. A parent of pwndDirty is the sibling of pwndSpb, and the parent * is lower in the zorder. * * History: * 18-Jul-1991 DarrinM Ported from Win 3.1 sources. \***************************************************************************/ BOOL FBitsTouch( PWND pwndDirty, LPRECT lprcDirty, PSPB pspb, DWORD flags) { PWND pwndSpb, pwndDirtySave; int fSpbLockUpdate; /* * When no window is passed in, skip all the window-related stuff and * go directly to check the rectangle. */ if (pwndDirty == NULL) goto ProbablyTouch; /* * If pwndDirty or its parents are invisible, * then it can't invalidate any SPBs */ if (!IsVisible(pwndDirty)) return FALSE; pwndSpb = pspb->spwnd; fSpbLockUpdate = pspb->flags & SPB_LOCKUPDATE; if (fSpbLockUpdate) { /* * If the guy is drawing through a locked window via * DCX_LOCKWINDOWUPDATE and the spb is a LOCKUPDATE SPB, then * don't do any invalidation of the SPB. Basically we're trying * to avoid having the tracking rectangle invalidate the SPB * since it's drawn via a WinGetClipPS() ps. */ if (flags & DCX_LOCKWINDOWUPDATE) return FALSE; } /* * If pwndDirty is pwndSpb's immediate parent (e.g., drawing in the * desktop window behind a dialog box), then we may touch: do the * intersection. */ if (pwndDirty == pwndSpb->spwndParent) goto ProbablyTouch; /* * We know that pwndDirty != pwndSpb->spwndParent. * Now find the parent of pwndDirty that is a sibling of pwndSpb. */ pwndDirtySave = pwndDirty; while (pwndSpb->spwndParent != pwndDirty->spwndParent) { pwndDirty = pwndDirty->spwndParent; /* * If we get to the top of the tree, it's because: * 1. pwndSpb == pwndDesktop * 2. pwndDirty is a parent of pwndSpb * 3. pwndDirty == pwndDesktop * 4. pwndDirty is a child of some other desktop * 5. pwndSpb and pwndDirty aren't siblings * * In all these cases, pwndDirty can't touch pwndSpb. */ if (pwndDirty == NULL) return FALSE; } /* * If pwndSpb is the same as pwndDirty, then it will invalidate * only if the SPB is LOCKUPDATE. * * Non-LOCKUPDATE SPB's can't be invalidated by their * own windows, but LOCKUPDATE SPB's can. */ if (pwndDirty == pwndSpb) { if (!fSpbLockUpdate) return FALSE; /* * If pwndSpb itself was drawn in, then we can't * try subtracting children. */ if (pwndDirtySave == pwndSpb) goto ProbablyTouch; /* * We want to calculate the immediate child of pwndSpb * on the path from pwndDirty to pwndSpb, so we can * subtract off the rectangles of the children of pwndSpb * in case there are intervening windows. */ while (pwndSpb != pwndDirtySave->spwndParent) { pwndDirtySave = pwndDirtySave->spwndParent; } /* * The SubtractIntervening loop subtracts the * window rects starting from pwndSpb and ending * at the window before pwndDirty, so set up * our variables appropriately. */ pwndDirty = pwndDirtySave; pwndSpb = pwndSpb->spwndChild; } else { /* * Now compare the Z order of pwndDirty and pwndSpb. * If pwndDirty is above pwndSpb, then the SPB can't be touched. */ pwndDirtySave = pwndDirty; /* * Compare the Z order by searching starting at pwndDirty, * moving DOWN the Z order list. If we encounter pwndSpb, * then pwndDirty is ABOVE or EQUAL to pwndSpb. */ for ( ; pwndDirty != NULL; pwndDirty = pwndDirty->spwndNext) { if (pwndDirty == pwndSpb) { return FALSE; } } pwndDirty = pwndDirtySave; /* * We don't want to subtract the SPB window itself */ pwndSpb = pwndSpb->spwndNext; } /* * Subtract Intervening rectangles. * pwndDirty is below pwndSpb. If there are any intervening * windows, subtract their window rects from lprcDirty to see if pwndDirty * is obscured. */ while (pwndSpb && pwndSpb != pwndDirty) { /* * If this window has a region selected, hwndDirty may draw through * it even though it has a full rectangle! We can't subtract its * rect from the dirty rect in this case. */ if ( TestWF(pwndSpb, WFVISIBLE) && !pwndSpb->hrgnClip && !TestWF(pwndSpb, WEFLAYERED) && !SubtractRect(lprcDirty, lprcDirty, &pwndSpb->rcWindow)) { return FALSE; } pwndSpb = pwndSpb->spwndNext; } // fall through ProbablyTouch: /* * If the rectangles don't intersect, there is no invalidation. * (we make this test relatively late because it's expensive compared * to the tests above). * Otherwise, *lprcDirty now has the area of bits not obscured * by intervening windows. */ return IntersectRect(lprcDirty, lprcDirty, &pspb->rc); } /***************************************************************************\ * SpbCheckRect2 * * Subtracts lprc in pwnd from pspb's region if lprc touches pspb. * * Returns FALSE if there is a memory allocation error, or if lprc * contains psbp's region; otherwise, returns TRUE. * * History: * 18-Jul-1991 DarrinM Ported from Win 3.1 sources. \***************************************************************************/ BOOL SpbCheckRect2( PSPB pspb, PWND pwnd, LPRECT lprc, DWORD flags) { RECT rcTouch = *lprc; /* * See if lprc touches any saved bits, taking into account what * window the drawing is occuring in. */ if (FBitsTouch(pwnd, &rcTouch, pspb, flags)) { /* * If no SPB region exists, make one for the whole thing */ if (!pspb->hrgn && SetOrCreateRectRgnIndirectPublic( &pspb->hrgn, &pspb->rc) == ERROR) { goto Error; } /* * Subtract the rectangle that is invalid from the SPB region */ SetRectRgnIndirect(ghrgnSCR, &rcTouch); switch (SubtractRgn(pspb->hrgn, pspb->hrgn, ghrgnSCR)) { case ERROR: case NULLREGION: goto Error; default: break; } } return TRUE; Error: FreeSpb(pspb); return FALSE; } /***************************************************************************\ * SpbTransfer * * Validate the SPB rectangle from a window's update region, after * subtracting the window's update region from the SPB. * * NOTE: Although SpbTransfer calls xxxInternalInvalidate, it doesn't * specify any flags that will cause immediate updating. Therefore the * critsect isn't left and we don't consider this an 'xxx' routine. * Also, no revalidation is necessary. * * History: * 18-Jul-1991 DarrinM Ported from Win 3.1 sources. \***************************************************************************/ BOOL SpbTransfer( PSPB pspb, PWND pwnd, BOOL fChildren) { RECT rc; /* * If the window has an update region... */ if (pwnd->hrgnUpdate != NULL) { /* * Invalidate its update region rectangle from the SPB */ if (pwnd->hrgnUpdate > HRGN_FULL) { GreGetRgnBox(pwnd->hrgnUpdate, &rc); } else { rc = pwnd->rcWindow; } /* * Intersect the update region bounds with the parent client rects, * to make sure we don't invalidate more than we need to. If * nothing to validate, return TRUE (because SPB is probably not empty) * These RDW_ flags won't cause the critical section to be left, nor * will they provoke WinEvent notifications. */ if (IntersectWithParents(pwnd, &rc)) { BEGINATOMICCHECK(); xxxInternalInvalidate(pwnd, ghrgnSPB2, RDW_VALIDATE | RDW_NOCHILDREN); ENDATOMICCHECK(); /* * If the SPB vanished, return FALSE. */ if (!SpbCheckRect2(pspb, pwnd, &rc, DCX_WINDOW)) return FALSE; } } if (fChildren) { for (pwnd = pwnd->spwndChild; pwnd != NULL; pwnd = pwnd->spwndNext) { if (!SpbTransfer(pspb, pwnd, TRUE)) { return FALSE; } } } return TRUE; } /***************************************************************************\ * CreateSpb * * This function, called after the window is created but before it is visible, * saves the contents of the screen where the window will be drawn in a SPB * structure, and links the structure into a linked list of SPB structures. * popup bits. This routine is called from SetWindowPos. * * History: * 18-Jul-1991 DarrinM Ported from Win 3.1 sources. \***************************************************************************/ VOID CreateSpb( PWND pwnd, UINT flags, HDC hdcScreen) { PSPB pspb; int fSpbLockUpdate; /* * Non-LOCKWINDOWUPDATE SPBs can only be created for top-level windows. * * This is because of the way that the display driver RestoreBits function * works. It can put bits down in places that aren't even part of the * window's visrgn, and these bits need to be invalidated. The * SetWindowPos() code to handle this case only knows how to invalidate * one of windows (i.e., the window's immediate parent), but all levels * need to get invalidated. See also the comments in wmswp.c, near the * call to RestoreSpb(). * * For example: the Q&E app brings up a copyright dialog that is a child * of its main window. While this is up, the user alt-f alt-l to execute * the file login command, which brings up another dialog that is a child * of the desktop. When the copyright dialog goes away, the display driver * restores bits on top of the second dialog. The SWP code knows to * invalidate the bogus stuff in the main window, but not in the desktop. * * LOCKUPDATE SPBs are fine, because they don't call RestoreBits. */ fSpbLockUpdate = flags & SPB_LOCKUPDATE; if ( !fSpbLockUpdate && pwnd->spwndParent != NULL && pwnd->spwndParent != PWNDDESKTOP(pwnd)) { return; } /* * We go and check all the existing DCs at this point, to handle the * case where we're saving an image of a window that has a "dirty" * DC, which would eventually invalidate our saved image (but which * is really okay). */ if (AnySpbs()) { SpbCheck(); } else { PDCE pdce; /* * Reset the dirty areas of all of the DC's and enable * bounds accumulation. We're creating a SPB now. This * is only done if there are no other SPB's in the list. */ GreLockDisplay(gpDispInfo->hDev); for (pdce = gpDispInfo->pdceFirst; pdce != NULL; pdce = pdce->pdceNext) { if (pdce->DCX_flags & DCX_REDIRECTED) continue; GreGetBounds(pdce->hdc, NULL, GGB_ENABLE_WINMGR); } GreUnlockDisplay(gpDispInfo->hDev); } /* * Create the save popup bits structure */ pspb = (PSPB)UserAllocPoolWithQuota(sizeof(SPB), TAG_SPB); if (!pspb) return; pspb->spwnd = NULL; pspb->rc = pwnd->rcWindow; /* * Clip to the screen */ if (!IntersectRect(&pspb->rc, &pspb->rc, &gpDispInfo->rcScreen)) goto BMError; pspb->hrgn = NULL; pspb->hbm = NULL; pspb->flags = flags; Lock(&(pspb->spwnd), pwnd); if (!fSpbLockUpdate) { RECT rc = pspb->rc; if (!SYSMET(SAMEDISPLAYFORMAT)) { PMONITOR pMonitor = _MonitorFromRect(&pspb->rc, MONITOR_DEFAULTTOPRIMARY); RECT rcT; /* * If the intersection with the monitor isn't the entire visible * window rectangle, then bail! We don't save SPBs for windows * that span multiple monitors. Since we do a lot of work to * pin dialogs and menus, there won't be too many of these * babies. */ if (SubtractRect(&rcT, &pspb->rc, &pMonitor->rcMonitor) && GreRectInRegion(gpDispInfo->hrgnScreen, &rcT)) goto BMError2; /* * Clip to the window's monitor */ if (!IntersectRect(&pspb->rc, &pspb->rc, &pMonitor->rcMonitor)) goto BMError2; /* * dont save bits in a mixed bitdepth situtation * we cant create the exactly correct format bitmap * in all cases (555/565, and Paletized) so as * a cop-out dont save bitmaps at all (on secondaries) * in mixed bit-depth. * * the correct fix is to create a compatible * bitmap for the monitor device and directly * BitBlt() from/to the device (pMonitor->hdcMonitor) * but this involves too much code at this time. */ if (pMonitor != gpDispInfo->pMonitorPrimary) goto BMError2; } /* * If this window is a regional window, don't use driver save * bits. Because it can only restore an entire rectangle, * invalid region is calculated assuming the old vis rgn was * rectangular. For regional windows, this would end up always * invalidating the area of (rcWindow - hrgnWindow) every * time an spb would be used. On the other hand, the invalid * area calculated when not using driver save bits is perfect, * because the restore blt can be correctly clipped to begin with. */ if ((pwnd->hrgnClip == NULL) && (pspb->ulSaveId = GreSaveScreenBits(gpDispInfo->hDev, SS_SAVE, 0, (RECTL *)&rc))) { /* * Remember that we copied this bitmap into on board memory. */ pspb->flags |= SPB_SAVESCREENBITS; } else { HBITMAP hbmSave; BOOL bRet; /* * The following delta byte-aligns the screen bitmap */ int dx = pspb->rc.left & 0x0007; int cx = pspb->rc.right - pspb->rc.left; int cy = pspb->rc.bottom - pspb->rc.top; /* * NOTE: we don't care about setting up a visrgn in * hdcScreen, because BitBlt ignores it on reads. */ pspb->hbm = GreCreateCompatibleBitmap(hdcScreen, cx + dx, cy); if (!pspb->hbm) goto BMError2; hbmSave = (HBITMAP)GreSelectBitmap(ghdcMem, pspb->hbm); if (!hbmSave) goto BMError2; /* * Copy the contents of the screen to the bitmap in the * save popup bits structure. If we ever find we run * into problems with the screen access check we can * do a bLockDisplay, give this process permission, do * the BitBlt and then take away permission. GDI * accesses the screen and that bit only under the * display semaphore so it is safe. Alternatively * if it is too hard to change this processes permission * here we could do it in GDI by marking the psoSrc * readable temporarily while completing the operation * and then setting it back to unreadable when done. * Or we could just fail it like the CreateCompatibleDC * failed and force a redraw. Basically we can't add * 3K of code in GDI to do a BitBlt that just does 1 * test differently for this 1 place in User. * */ bRet = GreBitBlt(ghdcMem, dx, 0, cx, cy, hdcScreen, pspb->rc.left, pspb->rc.top, 0x00CC0000, 0); GreSelectBitmap(ghdcMem, hbmSave); if (!bRet) goto BMError2; GreSetBitmapOwner(pspb->hbm, OBJECT_OWNER_PUBLIC); } /* * Mark that the window has an SPB. */ SetWF(pwnd, WFHASSPB); /* * non-LOCKUPDATE SPBs are not invalidated by * drawing in pspb->spwnd, so start the SPB validation * loop below at the sibling immediately below us. */ pwnd = pwnd->spwndNext; } /* * Link the new save popup bits structure into the list. */ pspb->pspbNext = gpDispInfo->pspbFirst; gpDispInfo->pspbFirst = pspb; /* * Here we deal with any update regions that may be * pending in windows underneath the SPB. * * For all windows that might affect this SPB: * - Subtract the SPB rect from the update region * - Subtract the window from the SPB * * Note that we use pspb->spwnd here, in case it has * no siblings. * * ghrgnSPB2 is the region that is used inside of SpbTransfer to * validate window update regions. Intersect with the window clipping * region, if it exists. Don't want to intersect with the spb rect if * a clipping region exists because we'll end up validating more than * we want to validate. */ SetRectRgnIndirect(ghrgnSPB2, &pspb->rc); if (pspb->spwnd->hrgnClip != NULL) { /* * If we get an error bail since an error might result in more * being validated than we want. Since the below code is only an * optimizer, this is ok: the window will remain invalid and will * draw, thereby invalidating the SPB like usual. */ if (IntersectRgn(ghrgnSPB2, ghrgnSPB2, pspb->spwnd->hrgnClip) == ERROR) { return; } } if (pspb->spwnd->spwndParent == NULL || SpbTransfer(pspb, pspb->spwnd->spwndParent, FALSE)) { /* * Do the same for the siblings underneath us... */ for ( ; pwnd != NULL; pwnd = pwnd->spwndNext) { if (!SpbTransfer(pspb, pwnd, TRUE)) break; } } return; BMError2: /* * Error creating the bitmap: clean up and return. */ if (pspb->hbm) GreDeleteObject(pspb->hbm); Unlock(&pspb->spwnd); // fall-through BMError: UserFreePool(pspb); } /***************************************************************************\ * RestoreSpb * * Restores the bits associated with pwnd's SPB onto the screen, clipped * to hrgnUncovered if possible. * * Upon return, hrgnUncovered is modified to contain the part of hrgnUncovered * that must be invalidated by the caller. FALSE is returned if the area * to be invalidated is empty. * * NOTE: Because the device driver SaveBitmap() function can not clip, this * function may write bits into an area of the screen larger than the passed-in * hrgnUncovered. In this case, the returned invalid region may be larger * than the passed-in hrgnUncovered. * * History: * 18-Jul-1991 DarrinM Ported from Win 3.1 sources. \***************************************************************************/ UINT RestoreSpb( PWND pwnd, HRGN hrgnUncovered, HDC *phdcScreen) { PSPB pspb; UINT uInvalidate; HRGN hrgnRestorable; /* * Note that we DON'T call SpbCheck() here -- * SpbCheck() is called by zzzBltValidBits(). */ pspb = FindSpb(pwnd); /* * Assume all of hrgnUncovered was restored, and there's nothing * for our caller to invalidate. */ uInvalidate = RSPB_NO_INVALIDATE; hrgnRestorable = hrgnUncovered; /* * First determine whether or not there is any area at all to restore. * If hrgnUncovered & pspb->hrgn is empty, then all of hrgnUncovered * needs to be invalidated, and there's nothing to restore. */ if (pspb->hrgn != NULL) { /* * At least some of hrgnUncovered needs to be invalidated. */ uInvalidate = RSPB_INVALIDATE; /* * Calculate the true area of bits to be restored. If it becomes * empty, then just free the SPB without changing hrgnUncovered, * which is the area that must be invalidated. */ hrgnRestorable = ghrgnSPB1; switch (IntersectRgn(hrgnRestorable, hrgnUncovered, pspb->hrgn)) { case ERROR: case NULLREGION: goto Error; default: break; } } if (pspb->flags & SPB_SAVESCREENBITS) { RECT rc = pspb->rc; /* * Since the restore frees the onboard memory, clear this * bit so FreeSpb() won't try to free it again (regardless of * whether we get an error or not) */ pspb->flags &= ~SPB_SAVESCREENBITS; if (!(GreSaveScreenBits(gpDispInfo->hDev, SS_RESTORE, pspb->ulSaveId, (RECTL *)&rc))) { goto Error; } /* * The SS_RESTORE call will always restore the entire SPB * rectangle, part of which may fall outside of hrgnUncovered. * The area that must be invalidated by our caller is simply * the SPB rectangle minus the area of restorable bits. * * If this region is not empty, then the SPB was not completely * restored, so we must return FALSE. */ SetRectRgnIndirect(ghrgnSPB2, &pspb->rc); if (SubtractRgn(hrgnUncovered, ghrgnSPB2, hrgnRestorable) != NULLREGION) { uInvalidate = RSPB_INVALIDATE_SSB; } } else { HDC hdcScreen; HBITMAP hbmSave; /* * In the unlikely event we need a screen DC and one wasn't passed in, * get it now. If we get one, we return the handle in *phdcScreen * so that our caller can release it later. */ if (!*phdcScreen) { *phdcScreen = gpDispInfo->hdcScreen; } hdcScreen = *phdcScreen; hbmSave = (HBITMAP)GreSelectBitmap(ghdcMem, pspb->hbm); if (!hbmSave) goto Error; /* * Be sure to clip to the area of restorable bits. */ GreSelectVisRgn(hdcScreen, hrgnRestorable, SVR_COPYNEW); GreBitBlt(hdcScreen, pspb->rc.left, pspb->rc.top, pspb->rc.right - pspb->rc.left, pspb->rc.bottom - pspb->rc.top, ghdcMem, pspb->rc.left & 0x0007, 0, SRCCOPY, 0); GreSelectBitmap(ghdcMem, hbmSave); /* * Now compute the area to be invalidated for return. * This is simply the original hrgnUncovered - hrgnRestorable */ SubtractRgn(hrgnUncovered, hrgnUncovered, hrgnRestorable); } if (pwnd->hrgnClip == NULL || !IsVisible(pwnd)) FreeSpb(pspb); return uInvalidate; Error: FreeSpb(pspb); return RSPB_INVALIDATE; } /***************************************************************************\ * LockWindowUpdate2 (API) * * Locks gspwndLockUpdate and it's children from updating. If * gspwndLockUpdate is NULL, then all windows will be unlocked. When * unlocked, the portions of the screen that would have been written to * are invalidated so they get repainted. TRUE is returned if the routine * is successful. * * If called when another app has something locked, then this function fails. * * History: * 18-Jul-1991 DarrinM Ported from Win 3.1 sources. \***************************************************************************/ BOOL LockWindowUpdate2( PWND pwndLock, BOOL fThreadOverride) { PSPB pspb; BOOL fInval; HRGN hrgn; PTHREADINFO ptiCurrent = PtiCurrent(); if ( /* * If we're full screen right now, fail this call. */ TEST_PUDF(PUDF_LOCKFULLSCREEN) || /* * If the screen is already locked, and it's being locked * by some other app, then fail. If fThreadOverride is set * then we're calling internally and it's okay to cancel * someone elses LockUpdate. */ ( gptiLockUpdate != NULL && gptiLockUpdate != PtiCurrent() && !fThreadOverride)) { UserAssert(IsWinEventNotifyDeferredOK()); RIPERR0(ERROR_SCREEN_ALREADY_LOCKED, RIP_WARNING, "LockWindowUpdate failed because screen is locked by another application."); return FALSE; } if ((pwndLock != NULL) == (gptiLockUpdate != NULL)) { if (!fThreadOverride) { RIPERR1(ERROR_INVALID_PARAMETER, RIP_WARNING, "LockWindowUpdate failed because it is already %s.", (pwndLock != NULL) ? "locked" : "unlocked"); } return FALSE; } /* * This must be done while holding the screen critsec. * Deadlock if we callback during this, so defer WinEvent notifications */ DeferWinEventNotify(); GreLockDisplay(gpDispInfo->hDev); if (pwndLock != NULL) { /* * We're about to make pwndLock and its siblings invisible: * go invalidate any other affected SPBs. */ SpbCheckPwnd(pwndLock); CreateSpb(pwndLock, SPB_LOCKUPDATE, NULL); Lock(&(gspwndLockUpdate), pwndLock); gptiLockUpdate = ptiCurrent; zzzInvalidateDCCache(pwndLock, IDC_DEFAULT); } else { /* * Flush any accumulated rectangles and invalidate spbs. */ SpbCheck(); /* * Save this in a local before we set it to NULL */ pwndLock = gspwndLockUpdate; gptiLockUpdate = NULL; Unlock(&gspwndLockUpdate); zzzInvalidateDCCache(pwndLock, IDC_DEFAULT); /* * Assume SPB doesn't exist, or couldn't be created, and that we * must invalidate the entire window. */ fInval = TRUE; hrgn = HRGN_FULL; /* * Find the LOCKUPDATE spb in the list, and if present calculate * the area that has been invalidated, if any. */ for (pspb = gpDispInfo->pspbFirst; pspb != NULL; pspb = pspb->pspbNext) { if (pspb->flags & SPB_LOCKUPDATE) { if (pspb->hrgn == NULL) { /* * If no invalid area, then no invalidation needed. */ fInval = FALSE; } else { /* * Subtract SPB valid region from SPB rectangle, to * yield invalid region. */ hrgn = ghrgnSPB1; SetRectRgnIndirect(hrgn, &pspb->rc); /* * If spb rect minus the spb valid rgn is empty, * then there is nothing to invalidate. */ fInval = SubtractRgn(hrgn, hrgn, pspb->hrgn) != NULLREGION; } FreeSpb(pspb); /* * Exit this loop (there can be only one LOCKUPDATE spb) */ break; } } if (fInval) { /* * When unlocking a Layered window (or a child of a layered * window), we need to invalidate that layered window specifically * or the window will ignore the invalidation request. For regular * windows, we invalidate the desktop instead. */ PWND pwndInvalidate; if ((pwndInvalidate = GetStyleWindow(pwndLock, WEFLAYERED)) == NULL) { pwndInvalidate = PWNDDESKTOP(pwndLock); } BEGINATOMICCHECK(); // want to prevent WinEvent notifies, but this make break asserts DeferWinEventNotify(); xxxInternalInvalidate(pwndInvalidate, hrgn, RDW_INVALIDATE | RDW_ERASE | RDW_ALLCHILDREN); zzzEndDeferWinEventNotify(); ENDATOMICCHECK(); } /* * Invalidate any other SPBs affected by the fact that this window * and its children are being made visible. */ SpbCheckPwnd(pwndLock); } GreUnlockDisplay(gpDispInfo->hDev); zzzEndDeferWinEventNotify(); return TRUE; } /***************************************************************************\ * FindSpb * * Returns a pointer to the SPB structure associated with the specified * window or NULL if there is no associated structure. * * History: * 18-Jul-1991 DarrinM Ported from Win 3.1 sources. \***************************************************************************/ PSPB FindSpb( PWND pwnd) { PSPB pspb; /* * Walk through the list of save popup bits looking for a match on * window handle. */ for (pspb = gpDispInfo->pspbFirst; pspb != NULL; pspb = pspb->pspbNext) { if (pspb->spwnd == pwnd && !(pspb->flags & SPB_LOCKUPDATE)) break; } return pspb; } /***************************************************************************\ * SpbCheck * * Modifies all of the save popup bits structures to reflect changes on the * screen. This function walks through all of the DC's, and if the DC is * dirty, then the dirty area is removed from the associated save popup bits * structure. * * History: * 18-Jul-1991 DarrinM Ported from Win 3.1 sources. \***************************************************************************/ VOID SpbCheck(VOID) { PDCE pdce; RECT rcBounds; if (AnySpbs()) { GreLockDisplay(gpDispInfo->hDev); /* * Walk through all of the DC's, accumulating dirty areas. */ for (pdce = gpDispInfo->pdceFirst; pdce != NULL; pdce = pdce->pdceNext) { /* * Only check valid cache entries... */ if (pdce->DCX_flags & (DCX_INVALID | DCX_DESTROYTHIS)) continue; SpbCheckDce(pdce); } /* * Subtact out DirectDraw dirty rect from all the SPB's. The call to * GreGetDirectDrawBounds will also reset the accumulated bounds. */ if (GreGetDirectDrawBounds(gpDispInfo->hDev, &rcBounds)) { SpbCheckRect(NULL, &rcBounds, 0); } GreUnlockDisplay(gpDispInfo->hDev); } } /***************************************************************************\ * SpbCheckDce * * This function retrieves the dirty area of a DC and removes the area from * the list of SPB structures. The DC is then marked as clean. * * History: * 18-Jul-1991 DarrinM Ported from Win 3.1 sources. \***************************************************************************/ VOID SpbCheckDce( PDCE pdce) { RECT rc; if (pdce->DCX_flags & DCX_REDIRECTED) return; /* * Query the dirty bounds rectangle. Doing this clears the bounds * as well. */ if (GreGetBounds(pdce->hdc, &rc, 0)) { if (pdce->pMonitor != NULL) { /* * Convert the bounds rect to screen coords. */ OffsetRect(&rc, pdce->pMonitor->rcMonitor.left, pdce->pMonitor->rcMonitor.top); } /* * Intersect the returned rectangle with the window rectangle * in case the guy was drawing outside his window */ if (IntersectRect(&rc, &rc, &(pdce->pwndOrg)->rcWindow)) SpbCheckRect(pdce->pwndOrg, &rc, pdce->DCX_flags); } } /***************************************************************************\ * SpbCheckRect * * This function removes the passed rectangle from the SPB structures which * touch it. * * History: * 18-Jul-1991 DarrinM Ported from Win 3.1 sources. \***************************************************************************/ VOID SpbCheckRect( PWND pwnd, LPRECT lprc, DWORD flags) { PSPB pspb, pspbNext; /* * If this window isn't visible, we're done. */ if (!IsVisible(pwnd)) return; for (pspb = gpDispInfo->pspbFirst; pspb != NULL; pspb = pspbNext) { /* * Get the pointer to the next save popup bits structure now * in case SpbCheckRect2() frees the current one. */ pspbNext = pspb->pspbNext; /* * In win3.1 they used to exit the function if this function * returned false. This meant that if one of the spbs was freed * the rest of the spbs would not be invalidated. */ SpbCheckRect2(pspb, pwnd, lprc, flags); } } /***************************************************************************\ * SpbCheckPwnd * * This routine checks to see if the window rectangle of PWND affects any SPBs. * It is called if pwnd or its children are being hidden or shown without * going through WinSetWindowPos(). * * Any SPBs for children of pwnd are destroyed. * * It must be called while pwnd is still visible. * * History: * 18-Jul-1991 DarrinM Ported from Win 3.1 sources. \***************************************************************************/ VOID SpbCheckPwnd( PWND pwnd) { PSPB pspb; PWND pwndSpb; PSPB pspbNext; /* * First blow away any SPBs owned by this window or its children. */ for (pspb = gpDispInfo->pspbFirst; pspb != NULL; pspb = pspbNext) { /* * Get pspbNext now in case we free the SPB */ pspbNext = pspb->pspbNext; /* * If pspb->spwnd is == pwnd or a child of pwnd, then free the SPB */ for (pwndSpb = pspb->spwnd; pwndSpb; pwndSpb = pwndSpb->spwndParent) { if (pwnd == pwndSpb) FreeSpb(pspb); } } /* * Then see if any other SPBs are affected... */ if (gpDispInfo->pspbFirst != NULL) { SpbCheckRect(pwnd, &pwnd->rcWindow, 0); } } /***************************************************************************\ * FreeSpb * * This function deletes the bitmap and region assocaited with a save popup * bits structure and then unlinks and destroys the spb structure itself. * * History: * 18-Jul-1991 DarrinM Ported from Win 3.1 sources. \***************************************************************************/ VOID FreeSpb( PSPB pspb) { PSPB *ppspb; PDCE pdce; if (pspb == NULL) return; /* * Delete the bitmap. If saved in screen memory, make special call. */ if (pspb->flags & SPB_SAVESCREENBITS) { GreSaveScreenBits(gpDispInfo->hDev, SS_FREE, pspb->ulSaveId, NULL); } else if (pspb->hbm != NULL) { GreDeleteObject(pspb->hbm); } /* * Destroy the region. */ if (pspb->hrgn != NULL){ GreDeleteObject(pspb->hrgn); } /* * Forget that there is an attached SPB. */ if (pspb->spwnd != NULL) { ClrWF(pspb->spwnd, WFHASSPB); Unlock(&pspb->spwnd); } /* * Unlink the spb. */ ppspb = &gpDispInfo->pspbFirst; while (*ppspb != pspb) { ppspb = &(*ppspb)->pspbNext; } *ppspb = pspb->pspbNext; /* * Free the save popup bits structure. */ UserFreePool(pspb); /* * If we no longer have any SPBs then turn off window MGR * bounds collection. */ if (!AnySpbs()) { GreLockDisplay(gpDispInfo->hDev); /* * Reset the dirty areas of all of the DC's. NULL means reset. */ for (pdce = gpDispInfo->pdceFirst; pdce != NULL; pdce = pdce->pdceNext) { if (pdce->DCX_flags & DCX_REDIRECTED) continue; GreGetBounds(pdce->hdc, NULL, GGB_DISABLE_WINMGR); } GreUnlockDisplay(gpDispInfo->hDev); } } /***************************************************************************\ * FreeAllSpbs * * This function deletes all spb-bitmaps. * * History: * 07-Oct-1995 ChrisWil Ported from Chicago. \***************************************************************************/ VOID FreeAllSpbs(void) { while(AnySpbs()) { FreeSpb(gpDispInfo->pspbFirst); } gpDispInfo->pspbFirst = NULL; }