1
0
mirror of https://github.com/narzoul/DDrawCompat synced 2024-12-30 08:55:36 +01:00

Fixed invalidation of resized windows

This commit is contained in:
narzoul 2021-01-01 19:02:14 +01:00
parent 453dc4d681
commit a0919dcaa6
3 changed files with 110 additions and 40 deletions

View File

@ -68,6 +68,11 @@ namespace Gdi
return *this; return *this;
} }
void Region::clear()
{
SetRectRgn(m_region, 0, 0, 0, 0);
}
bool Region::isEmpty() const bool Region::isEmpty() const
{ {
return sizeof(RGNDATAHEADER) == GetRegionData(m_region, 0, nullptr); return sizeof(RGNDATAHEADER) == GetRegionData(m_region, 0, nullptr);

View File

@ -18,6 +18,7 @@ namespace Gdi
Region(Region&& other); Region(Region&& other);
Region& operator=(Region other); Region& operator=(Region other);
void clear();
bool isEmpty() const; bool isEmpty() const;
void offset(int x, int y); void offset(int x, int y);
HRGN release(); HRGN release();

View File

@ -27,23 +27,25 @@ namespace
HWND hwnd; HWND hwnd;
HWND presentationWindow; HWND presentationWindow;
RECT windowRect; RECT windowRect;
RECT clientRect;
Gdi::Region windowRegion; Gdi::Region windowRegion;
Gdi::Region visibleRegion; Gdi::Region visibleRegion;
Gdi::Region invalidatedRegion; Gdi::Region invalidatedRegion;
COLORREF colorKey; COLORREF colorKey;
BYTE alpha; BYTE alpha;
bool isLayered; bool isLayered;
bool isRgnChangePending; bool isVisibleRegionChanged;
Window(HWND hwnd) Window(HWND hwnd)
: hwnd(hwnd) : hwnd(hwnd)
, presentationWindow(nullptr) , presentationWindow(nullptr)
, windowRect{} , windowRect{}
, clientRect{}
, windowRegion(nullptr) , windowRegion(nullptr)
, colorKey(CLR_INVALID) , colorKey(CLR_INVALID)
, alpha(255) , alpha(255)
, isLayered(true) , isLayered(true)
, isRgnChangePending(false) , isVisibleRegionChanged(false)
{ {
} }
}; };
@ -67,15 +69,29 @@ namespace
BOOL disableTransitions = TRUE; BOOL disableTransitions = TRUE;
DwmSetWindowAttribute(hwnd, DWMWA_TRANSITIONS_FORCEDISABLED, &disableTransitions, sizeof(disableTransitions)); DwmSetWindowAttribute(hwnd, DWMWA_TRANSITIONS_FORCEDISABLED, &disableTransitions, sizeof(disableTransitions));
const auto style = GetClassLongPtr(hwnd, GCL_STYLE); const auto style = GetClassLong(hwnd, GCL_STYLE);
if (style & CS_DROPSHADOW) if (style & CS_DROPSHADOW)
{ {
SetClassLongPtr(hwnd, GCL_STYLE, style & ~CS_DROPSHADOW); CALL_ORIG_FUNC(SetClassLongA)(hwnd, GCL_STYLE, style & ~CS_DROPSHADOW);
} }
return g_windows.emplace(hwnd, Window(hwnd)).first; return g_windows.emplace(hwnd, Window(hwnd)).first;
} }
void bltWindow(const RECT& dst, const RECT& src, const Gdi::Region& clipRegion)
{
if (dst.left == src.left && dst.top == src.top || clipRegion.isEmpty())
{
return;
}
HDC screenDc = GetDC(nullptr);
SelectClipRgn(screenDc, clipRegion);
BitBlt(screenDc, dst.left, dst.top, src.right - src.left, src.bottom - src.top, screenDc, src.left, src.top, SRCCOPY);
SelectClipRgn(screenDc, nullptr);
CALL_ORIG_FUNC(ReleaseDC)(nullptr, screenDc);
}
Gdi::Region getWindowRegion(HWND hwnd) Gdi::Region getWindowRegion(HWND hwnd)
{ {
Gdi::Region rgn; Gdi::Region rgn;
@ -187,36 +203,81 @@ namespace
return result; return result;
} }
void updatePosition(Window& window, const RECT& oldWindowRect, const Gdi::Region& oldVisibleRegion) void updatePosition(Window& window, const RECT& oldWindowRect, const RECT& oldClientRect,
const Gdi::Region& oldVisibleRegion)
{ {
Gdi::Region preservedRegion(oldVisibleRegion); const bool isClientOriginChanged =
preservedRegion.offset(window.windowRect.left - oldWindowRect.left, window.windowRect.top - oldWindowRect.top); window.clientRect.left - window.windowRect.left != oldClientRect.left - oldWindowRect.left ||
preservedRegion &= window.visibleRegion; window.clientRect.top - window.windowRect.top != oldClientRect.top - oldWindowRect.top;
const bool isClientWidthChanged =
window.clientRect.right - window.clientRect.left != oldClientRect.right - oldClientRect.left;
const bool isClientHeightChanged =
window.clientRect.bottom - window.clientRect.top != oldClientRect.bottom - oldClientRect.top;
const bool isClientInvalidated =
isClientWidthChanged && (GetClassLong(window.hwnd, GCL_STYLE) & CS_HREDRAW) ||
isClientHeightChanged && (GetClassLong(window.hwnd, GCL_STYLE) & CS_VREDRAW);
const bool isFrameInvalidated = isClientOriginChanged || isClientWidthChanged || isClientHeightChanged ||
window.windowRect.right - window.windowRect.left != oldWindowRect.right - oldWindowRect.left ||
window.windowRect.bottom - window.windowRect.top != oldWindowRect.bottom - oldWindowRect.top;
POINT clientPos = {}; Gdi::Region preservedRegion;
ClientToScreen(window.hwnd, &clientPos); if (!isClientInvalidated || !isFrameInvalidated)
if (!preservedRegion.isEmpty())
{ {
Gdi::Region updateRegion; preservedRegion = oldVisibleRegion;
GetUpdateRgn(window.hwnd, updateRegion, FALSE); preservedRegion.offset(window.windowRect.left - oldWindowRect.left, window.windowRect.top - oldWindowRect.top);
OffsetRgn(updateRegion, clientPos.x, clientPos.y); preservedRegion &= window.visibleRegion;
preservedRegion -= updateRegion;
if (!preservedRegion.isEmpty() && if (isClientInvalidated)
(window.windowRect.left != oldWindowRect.left || window.windowRect.top != oldWindowRect.top))
{ {
HDC screenDc = GetDC(nullptr); preservedRegion -= window.clientRect;
SelectClipRgn(screenDc, preservedRegion); }
BitBlt(screenDc, window.windowRect.left, window.windowRect.top, else
oldWindowRect.right - oldWindowRect.left, oldWindowRect.bottom - oldWindowRect.top, {
screenDc, oldWindowRect.left, oldWindowRect.top, SRCCOPY); if (isFrameInvalidated)
SelectClipRgn(screenDc, nullptr); {
CALL_ORIG_FUNC(ReleaseDC)(nullptr, screenDc); preservedRegion &= window.clientRect;
}
if (isClientWidthChanged)
{
RECT r = window.clientRect;
r.left += oldClientRect.right - oldClientRect.left;
if (r.left < r.right)
{
preservedRegion -= r;
}
}
if (isClientHeightChanged)
{
RECT r = window.clientRect;
r.top += oldClientRect.bottom - oldClientRect.top;
if (r.top < r.bottom)
{
preservedRegion -= r;
}
}
Gdi::Region updateRegion;
GetUpdateRgn(window.hwnd, updateRegion, FALSE);
updateRegion.offset(window.clientRect.left, window.clientRect.top);
preservedRegion -= updateRegion;
}
if (!isFrameInvalidated)
{
bltWindow(window.windowRect, oldWindowRect, preservedRegion);
}
else
{
bltWindow(window.clientRect, oldClientRect, preservedRegion);
} }
} }
window.invalidatedRegion = window.visibleRegion - preservedRegion; window.invalidatedRegion = window.visibleRegion - preservedRegion;
OffsetRgn(window.invalidatedRegion, -clientPos.x, -clientPos.y); if (!window.invalidatedRegion.isEmpty())
{
window.invalidatedRegion.offset(-window.clientRect.left, -window.clientRect.top);
}
} }
BOOL CALLBACK updateWindow(HWND hwnd, LPARAM lParam) BOOL CALLBACK updateWindow(HWND hwnd, LPARAM lParam)
@ -246,7 +307,7 @@ namespace
if (isLayered != it->second.isLayered) if (isLayered != it->second.isLayered)
{ {
it->second.isLayered = isLayered; it->second.isLayered = isLayered;
it->second.isRgnChangePending = isVisible; it->second.isVisibleRegionChanged = isVisible;
if (!isLayered) if (!isLayered)
{ {
it->second.presentationWindow = reinterpret_cast<HWND>( it->second.presentationWindow = reinterpret_cast<HWND>(
@ -268,22 +329,23 @@ namespace
setPresentationWindowRgn = true; setPresentationWindowRgn = true;
} }
RECT windowRect = {}; WINDOWINFO wi = {};
Gdi::Region visibleRegion; Gdi::Region visibleRegion;
if (isVisible) if (isVisible)
{ {
GetWindowRect(hwnd, &windowRect); wi.cbSize = sizeof(wi);
if (!IsRectEmpty(&windowRect)) GetWindowInfo(hwnd, &wi);
if (!IsRectEmpty(&wi.rcWindow))
{ {
if (it->second.windowRegion) if (it->second.windowRegion)
{ {
visibleRegion = it->second.windowRegion; visibleRegion = it->second.windowRegion;
OffsetRgn(visibleRegion, windowRect.left, windowRect.top); visibleRegion.offset(wi.rcWindow.left, wi.rcWindow.top);
} }
else else
{ {
visibleRegion = windowRect; visibleRegion = wi.rcWindow;
} }
visibleRegion &= context.virtualScreenRegion; visibleRegion &= context.virtualScreenRegion;
visibleRegion -= context.obscuredRegion; visibleRegion -= context.obscuredRegion;
@ -295,22 +357,24 @@ namespace
} }
} }
std::swap(it->second.windowRect, windowRect); std::swap(it->second.windowRect, wi.rcWindow);
std::swap(it->second.clientRect, wi.rcClient);
swap(it->second.visibleRegion, visibleRegion); swap(it->second.visibleRegion, visibleRegion);
if (!isLayered) if (!isLayered)
{ {
if (!it->second.visibleRegion.isEmpty()) if (!it->second.visibleRegion.isEmpty())
{ {
updatePosition(it->second, windowRect, visibleRegion); updatePosition(it->second, wi.rcWindow, wi.rcClient, visibleRegion);
} }
if (isVisible && !it->second.isRgnChangePending) if (isVisible && !it->second.isVisibleRegionChanged)
{ {
OffsetRgn(visibleRegion, it->second.windowRect.left - windowRect.left, it->second.windowRect.top - windowRect.top); visibleRegion.offset(it->second.windowRect.left - wi.rcWindow.left, it->second.windowRect.top - wi.rcWindow.top);
if (it->second.visibleRegion != visibleRegion) if (it->second.visibleRegion != visibleRegion)
{ {
it->second.isRgnChangePending = true; it->second.isVisibleRegionChanged = true;
} }
} }
@ -419,9 +483,9 @@ namespace Gdi
return; return;
} }
if (it->second.isRgnChangePending) if (it->second.isVisibleRegionChanged)
{ {
it->second.isRgnChangePending = false; it->second.isVisibleRegionChanged = false;
const LONG origWndProc = CALL_ORIG_FUNC(GetWindowLongA)(hwnd, GWL_WNDPROC); const LONG origWndProc = CALL_ORIG_FUNC(GetWindowLongA)(hwnd, GWL_WNDPROC);
CALL_ORIG_FUNC(SetWindowLongA)(hwnd, GWL_WNDPROC, reinterpret_cast<LONG>(CALL_ORIG_FUNC(DefWindowProcA))); CALL_ORIG_FUNC(SetWindowLongA)(hwnd, GWL_WNDPROC, reinterpret_cast<LONG>(CALL_ORIG_FUNC(DefWindowProcA)));
if (it->second.isLayered) if (it->second.isLayered)
@ -431,7 +495,7 @@ namespace Gdi
else else
{ {
Gdi::Region rgn(it->second.visibleRegion); Gdi::Region rgn(it->second.visibleRegion);
OffsetRgn(rgn, -it->second.windowRect.left, -it->second.windowRect.top); rgn.offset(-it->second.windowRect.left, -it->second.windowRect.top);
rgn |= REGION_OVERRIDE_MARKER_RECT; rgn |= REGION_OVERRIDE_MARKER_RECT;
SetWindowRgn(hwnd, rgn, FALSE); SetWindowRgn(hwnd, rgn, FALSE);
} }
@ -604,7 +668,7 @@ namespace Gdi
for (auto it = g_windowZOrder.rbegin(); it != g_windowZOrder.rend(); ++it) for (auto it = g_windowZOrder.rbegin(); it != g_windowZOrder.rend(); ++it)
{ {
auto& window = **it; auto& window = **it;
if (window.isRgnChangePending || !window.invalidatedRegion.isEmpty()) if (window.isVisibleRegionChanged || !window.invalidatedRegion.isEmpty())
{ {
invalidatedWindows.push_back(window.hwnd); invalidatedWindows.push_back(window.hwnd);
} }