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

Use window region to emulate pre-DWM system region

This commit is contained in:
narzoul 2020-12-27 17:08:44 +01:00
parent 679bd94629
commit 5911951d08
20 changed files with 791 additions and 708 deletions

View File

@ -16,15 +16,13 @@ namespace
};
std::map<IDirectDrawClipper*, ClipperData> g_clipperData;
bool g_isInvalidated = false;
void updateWindowClipList(CompatRef<IDirectDrawClipper> clipper, ClipperData& data);
void onWindowPosChange()
{
for (auto& clipperData : g_clipperData)
{
updateWindowClipList(*clipperData.first, clipperData.second);
}
g_isInvalidated = true;
}
void updateWindowClipList(CompatRef<IDirectDrawClipper> clipper, ClipperData& data)
@ -146,4 +144,16 @@ namespace DDraw
vtable.SetClipList = &SetClipList;
vtable.SetHWnd = &SetHWnd;
}
void DirectDrawClipper::update()
{
if (g_isInvalidated)
{
g_isInvalidated = false;
for (auto& clipperData : g_clipperData)
{
updateWindowClipList(*clipperData.first, clipperData.second);
}
}
}
}

View File

@ -11,6 +11,7 @@ namespace DDraw
public:
static HRGN getClipRgn(CompatRef<IDirectDrawClipper> clipper);
static HRESULT setClipRgn(CompatRef<IDirectDrawClipper> clipper, HRGN rgn);
static void update();
static void setCompatVtable(IDirectDrawClipperVtbl& vtable);
};

View File

@ -47,137 +47,11 @@ namespace
CompatPtr<IDirectDrawSurface7> getBackBuffer();
CompatPtr<IDirectDrawSurface7> getLastSurface();
void bltToWindow(CompatRef<IDirectDrawSurface7> src)
{
for (auto windowPair : Gdi::Window::getWindows())
{
if (!windowPair.second->isLayered() && !windowPair.second->getVisibleRegion().isEmpty())
{
g_clipper->SetHWnd(g_clipper, 0, windowPair.second->getPresentationWindow());
g_frontBuffer->Blt(g_frontBuffer, nullptr, &src, nullptr, DDBLT_WAIT, nullptr);
}
}
}
void bltToWindowViaGdi(Gdi::Region* primaryRegion)
{
D3dDdi::ScopedCriticalSection lock;
std::unique_ptr<HDC__, void(*)(HDC)> virtualScreenDc(nullptr, &Gdi::VirtualScreen::deleteDc);
RECT virtualScreenBounds = Gdi::VirtualScreen::getBounds();
for (auto windowPair : Gdi::Window::getWindows())
{
HWND presentationWindow = windowPair.second->getPresentationWindow();
if (!presentationWindow)
{
continue;
}
Gdi::Region visibleRegion = windowPair.second->getVisibleRegion();
if (visibleRegion.isEmpty())
{
continue;
}
if (primaryRegion)
{
visibleRegion -= *primaryRegion;
if (visibleRegion.isEmpty())
{
continue;
}
}
if (!virtualScreenDc)
{
virtualScreenDc.reset(Gdi::VirtualScreen::createDc());
if (!virtualScreenDc)
{
return;
}
}
Gdi::AccessGuard accessGuard(Gdi::ACCESS_READ, !primaryRegion);
HDC dc = GetWindowDC(presentationWindow);
RECT rect = windowPair.second->getWindowRect();
visibleRegion.offset(-rect.left, -rect.top);
SelectClipRgn(dc, visibleRegion);
CALL_ORIG_FUNC(BitBlt)(dc, 0, 0, rect.right - rect.left, rect.bottom - rect.top, virtualScreenDc.get(),
rect.left - virtualScreenBounds.left, rect.top - virtualScreenBounds.top, SRCCOPY);
CALL_ORIG_FUNC(ReleaseDC)(presentationWindow, dc);
}
}
void bltVisibleLayeredWindowsToBackBuffer()
{
auto backBuffer(getBackBuffer());
if (!backBuffer)
{
return;
}
HDC backBufferDc = nullptr;
RECT ddrawMonitorRect = D3dDdi::KernelModeThunks::getMonitorRect();
for (auto windowPair : Gdi::Window::getWindows())
{
if (!windowPair.second->isLayered())
{
continue;
}
if (!backBufferDc)
{
D3dDdi::KernelModeThunks::setDcFormatOverride(D3DDDIFMT_X8R8G8B8);
backBuffer->GetDC(backBuffer, &backBufferDc);
D3dDdi::KernelModeThunks::setDcFormatOverride(D3DDDIFMT_UNKNOWN);
if (!backBufferDc)
{
return;
}
}
HDC windowDc = GetWindowDC(windowPair.first);
Gdi::Region rgn(Gdi::getVisibleWindowRgn(windowPair.first));
RECT wr = windowPair.second->getWindowRect();
if (0 != ddrawMonitorRect.left || 0 != ddrawMonitorRect.top)
{
OffsetRect(&wr, -ddrawMonitorRect.left, -ddrawMonitorRect.top);
rgn.offset(-ddrawMonitorRect.left, -ddrawMonitorRect.top);
}
SelectClipRgn(backBufferDc, rgn);
auto colorKey = windowPair.second->getColorKey();
if (CLR_INVALID != colorKey)
{
CALL_ORIG_FUNC(TransparentBlt)(backBufferDc, wr.left, wr.top, wr.right - wr.left, wr.bottom - wr.top,
windowDc, 0, 0, wr.right - wr.left, wr.bottom - wr.top, colorKey);
}
else
{
BLENDFUNCTION blend = {};
blend.SourceConstantAlpha = windowPair.second->getAlpha();
CALL_ORIG_FUNC(AlphaBlend)(backBufferDc, wr.left, wr.top, wr.right - wr.left, wr.bottom - wr.top,
windowDc, 0, 0, wr.right - wr.left, wr.bottom - wr.top, blend);
}
CALL_ORIG_FUNC(ReleaseDC)(windowPair.first, windowDc);
}
if (backBufferDc)
{
SelectClipRgn(backBufferDc, nullptr);
backBuffer->ReleaseDC(backBuffer, backBufferDc);
}
}
void bltToPrimaryChain(CompatRef<IDirectDrawSurface7> src)
{
if (!g_isFullScreen)
{
bltToWindow(src);
Gdi::Window::present(*g_frontBuffer, src, *g_clipper);
return;
}
@ -324,12 +198,13 @@ namespace
if (!g_frontBuffer || !src || DDraw::RealPrimarySurface::isLost())
{
bltToWindowViaGdi(nullptr);
Gdi::Window::present(nullptr);
return;
}
Gdi::Region primaryRegion(D3dDdi::KernelModeThunks::getMonitorRect());
bltToWindowViaGdi(&primaryRegion);
RECT monitorRect = D3dDdi::KernelModeThunks::getMonitorRect();
Gdi::Region excludeRegion(monitorRect);
Gdi::Window::present(excludeRegion);
if (Win32::DisplayMode::getBpp() <= 8)
{
@ -360,7 +235,12 @@ namespace
if (g_isFullScreen && src == DDraw::PrimarySurface::getGdiSurface())
{
bltVisibleLayeredWindowsToBackBuffer();
auto backBuffer(getBackBuffer());
if (backBuffer)
{
POINT offset = { -monitorRect.left, -monitorRect.top };
Gdi::Window::presentLayered(*backBuffer, offset);
}
}
}

View File

@ -1,10 +1,11 @@
#include <set>
#include "DDraw/DirectDrawSurface.h"
#include "DDraw/RealPrimarySurface.h"
#include "DDraw/Surfaces/PrimarySurface.h"
#include "DDraw/Surfaces/Surface.h"
#include "DDraw/Surfaces/SurfaceImpl.h"
#include <DDraw/DirectDrawClipper.h>
#include <DDraw/DirectDrawSurface.h>
#include <DDraw/RealPrimarySurface.h>
#include <DDraw/Surfaces/PrimarySurface.h>
#include <DDraw/Surfaces/Surface.h>
#include <DDraw/Surfaces/SurfaceImpl.h>
namespace DDraw
{
@ -28,6 +29,7 @@ namespace DDraw
{
return DDERR_WASSTILLDRAWING;
}
DirectDrawClipper::update();
return s_origVtable.Blt(This, lpDestRect, lpDDSrcSurface, lpSrcRect, dwFlags, lpDDBltFx);
}

View File

@ -113,14 +113,12 @@ namespace
}
}
void setClippingRegion(const CompatDc& compatDc, std::shared_ptr<Gdi::Window> rootWindow,
const POINT& origin, const RECT& virtualScreenBounds)
void setClippingRegion(const CompatDc& compatDc, HWND hwnd, const POINT& origin, const RECT& virtualScreenBounds)
{
if (rootWindow)
if (hwnd)
{
Gdi::Region sysRgn;
CALL_ORIG_FUNC(GetRandomRgn)(compatDc.origDc, sysRgn, SYSRGN);
sysRgn &= rootWindow->getVisibleRegion();
OffsetRgn(sysRgn, -virtualScreenBounds.left, -virtualScreenBounds.top);
SelectClipRgn(compatDc.dc, sysRgn);
}
@ -196,13 +194,6 @@ namespace Gdi
return it->second.dc;
}
const HWND wnd = CALL_ORIG_FUNC(WindowFromDC)(origDc);
auto rootWindow(wnd ? Gdi::Window::get(GetAncestor(wnd, GA_ROOT)) : nullptr);
if (rootWindow)
{
rootWindow->updateWindow();
}
CompatDc compatDc;
compatDc.dc = Gdi::DcCache::getDc();
if (!compatDc.dc)
@ -212,7 +203,8 @@ namespace Gdi
POINT origin = {};
GetDCOrgEx(origDc, &origin);
if (wnd && GetDesktopWindow() != wnd)
const HWND hwnd = CALL_ORIG_FUNC(WindowFromDC)(origDc);
if (hwnd && GetDesktopWindow() != hwnd)
{
origin.x -= virtualScreenBounds.left;
origin.y -= virtualScreenBounds.top;
@ -223,7 +215,7 @@ namespace Gdi
compatDc.threadId = GetCurrentThreadId();
compatDc.savedState = useMetaRgn ? SaveDC(compatDc.dc) : 0;
copyDcAttributes(compatDc, origDc, origin);
setClippingRegion(compatDc, rootWindow, origin, virtualScreenBounds);
setClippingRegion(compatDc, hwnd, origin, virtualScreenBounds);
g_origDcToCompatDc.insert(CompatDcMap::value_type(origDc, compatDc));

View File

@ -47,17 +47,9 @@ namespace
HDC m_compatDc;
};
struct ExcludeRgnForOverlappingWindowArgs
{
HRGN rgn;
HWND rootWnd;
};
std::unordered_map<void*, const char*> g_funcNames;
thread_local bool g_redirectToDib = true;
HRGN getWindowRegion(HWND hwnd);
#define CREATE_DC_FUNC_ATTRIBUTE(attribute) \
template <typename OrigFuncPtr, OrigFuncPtr origFunc> \
bool attribute() \
@ -266,30 +258,6 @@ namespace
return LOG_RESULT(CALL_ORIG_FUNC(DrawCaption)(hwnd, hdc, lprect, flags));
}
BOOL CALLBACK excludeRgnForOverlappingWindow(HWND hwnd, LPARAM lParam)
{
auto& args = *reinterpret_cast<ExcludeRgnForOverlappingWindowArgs*>(lParam);
if (hwnd == args.rootWnd)
{
return FALSE;
}
DWORD windowPid = 0;
GetWindowThreadProcessId(hwnd, &windowPid);
if (GetCurrentProcessId() != windowPid ||
!IsWindowVisible(hwnd) ||
(CALL_ORIG_FUNC(GetWindowLongA)(hwnd, GWL_EXSTYLE) & (WS_EX_LAYERED | WS_EX_TRANSPARENT)))
{
return TRUE;
}
HRGN windowRgn = getWindowRegion(hwnd);
CombineRgn(args.rgn, args.rgn, windowRgn, RGN_DIFF);
DeleteObject(windowRgn);
return TRUE;
}
template <typename OrigFuncPtr, OrigFuncPtr origFunc, typename Result, typename... Params>
OrigFuncPtr getCompatGdiDcFuncPtr(FuncPtr<Result, HDC, Params...>)
{
@ -302,18 +270,6 @@ namespace
return &compatGdiTextDcFunc<OrigFuncPtr, origFunc, Result, Params...>;
}
HRGN getWindowRegion(HWND hwnd)
{
RECT wr = {};
GetWindowRect(hwnd, &wr);
HRGN windowRgn = CreateRectRgnIndirect(&wr);
if (ERROR != GetWindowRgn(hwnd, windowRgn))
{
OffsetRgn(windowRgn, wr.left, wr.top);
}
return windowRgn;
}
template <typename OrigFuncPtr, OrigFuncPtr origFunc>
void hookGdiDcFunction(const char* moduleName, const char* funcName)
{
@ -336,26 +292,6 @@ namespace
moduleName, funcName, getCompatGdiTextDcFuncPtr<OrigFuncPtr, origFunc>(origFunc));
}
int WINAPI getRandomRgn(HDC hdc, HRGN hrgn, INT iNum)
{
int result = CALL_ORIG_FUNC(GetRandomRgn)(hdc, hrgn, iNum);
if (1 != result)
{
return result;
}
HWND hwnd = WindowFromDC(hdc);
if (!hwnd || hwnd == GetDesktopWindow() || (CALL_ORIG_FUNC(GetWindowLongA)(hwnd, GWL_EXSTYLE) & WS_EX_LAYERED))
{
return 1;
}
ExcludeRgnForOverlappingWindowArgs args = { hrgn, GetAncestor(hwnd, GA_ROOT) };
EnumWindows(excludeRgnForOverlappingWindow, reinterpret_cast<LPARAM>(&args));
return 1;
}
template <typename WndClass, typename WndClassEx>
ATOM WINAPI registerClass(const WndClass* lpWndClass, ATOM(WINAPI* origRegisterClass)(const WndClass*),
ATOM(WINAPI* registerClassEx)(const WndClassEx*))
@ -481,14 +417,6 @@ namespace Gdi
{
namespace DcFunctions
{
HRGN getVisibleWindowRgn(HWND hwnd)
{
HRGN rgn = getWindowRegion(hwnd);
ExcludeRgnForOverlappingWindowArgs args = { rgn, hwnd };
EnumWindows(excludeRgnForOverlappingWindow, reinterpret_cast<LPARAM>(&args));
return rgn;
}
void installHooks()
{
// Bitmap functions
@ -517,9 +445,6 @@ namespace Gdi
// Brush functions
HOOK_GDI_DC_FUNCTION(gdi32, PatBlt);
// Clipping functions
HOOK_FUNCTION(gdi32, GetRandomRgn, getRandomRgn);
// Device context functions
HOOK_GDI_DC_FUNCTION(gdi32, DrawEscape);
HOOK_FUNCTION(user32, WindowFromDC, windowFromDc);

View File

@ -1,12 +1,9 @@
#pragma once
#include <Windows.h>
namespace Gdi
{
namespace DcFunctions
{
HRGN getVisibleWindowRgn(HWND hwnd);
void installHooks();
}
}

View File

@ -34,11 +34,6 @@ namespace Gdi
DcCache::dllThreadDetach();
}
HRGN getVisibleWindowRgn(HWND hwnd)
{
return DcFunctions::getVisibleWindowRgn(hwnd);
}
void installHooks()
{
DcFunctions::installHooks();

View File

@ -9,7 +9,6 @@ namespace Gdi
typedef void(*WindowPosChangeNotifyFunc)();
void dllThreadDetach();
HRGN getVisibleWindowRgn(HWND hwnd);
void installHooks();
bool isDisplayDc(HDC dc);
void redraw(HRGN rgn);

View File

@ -7,6 +7,7 @@
#include <Gdi/ScrollFunctions.h>
#include <Gdi/TitleBar.h>
#include <Gdi/VirtualScreen.h>
#include <Gdi/WinProc.h>
std::ostream& operator<<(std::ostream& os, const MENUITEMINFOW& val)
{
@ -43,7 +44,8 @@ namespace
LRESULT defPaintProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, WNDPROC origWndProc,
const char* origWndProcName);
LRESULT onEraseBackground(HWND hwnd, HDC dc, WNDPROC origWndProc);
LRESULT onNcPaint(HWND hwnd, WPARAM wParam, WNDPROC origWndProc);
LRESULT onNcActivate(HWND hwnd, WPARAM wParam, LPARAM lParam);
LRESULT onNcPaint(HWND hwnd, WNDPROC origWndProc);
LRESULT onPaint(HWND hwnd, WNDPROC origWndProc);
LRESULT onPrint(HWND hwnd, UINT msg, HDC dc, LONG flags, WNDPROC origWndProc);
LRESULT onSetText(HWND hwnd, WPARAM wParam, LPARAM lParam, WNDPROC origWndProc);
@ -143,7 +145,7 @@ namespace
return onEraseBackground(hwnd, reinterpret_cast<HDC>(wParam), origWndProc);
case WM_NCPAINT:
return onNcPaint(hwnd, wParam, origWndProc);
return onNcPaint(hwnd, origWndProc);
case WM_PRINT:
case WM_PRINTCLIENT:
@ -164,14 +166,38 @@ namespace
return LOG_RESULT(defPaintProc(hwnd, msg, wParam, lParam, origWndProc));
}
LRESULT defWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, WNDPROC origDefWindowProc)
{
switch (msg)
{
case WM_NCACTIVATE:
return onNcActivate(hwnd, wParam, lParam);
case WM_NCCREATE:
{
char className[64] = {};
GetClassName(hwnd, className, sizeof(className));
if (std::string(className) == "CompatWindowDesktopReplacement")
{
// Disable VirtualizeDesktopPainting shim
return 0;
}
}
}
return defPaintProc(hwnd, msg, wParam, lParam, origDefWindowProc);
}
LRESULT WINAPI defWindowProcA(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
return defPaintProc(hwnd, msg, wParam, lParam, CALL_ORIG_FUNC(DefWindowProcA), "defWindowProcA");
LOG_FUNC("DefWindowProcA", Compat::WindowMessageStruct(hwnd, msg, wParam, lParam));
return LOG_RESULT(defWindowProc(hwnd, msg, wParam, lParam, CALL_ORIG_FUNC(DefWindowProcA)));
}
LRESULT WINAPI defWindowProcW(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
return defPaintProc(hwnd, msg, wParam, lParam, CALL_ORIG_FUNC(DefWindowProcW), "defWindowProcW");
LOG_FUNC("DefWindowProcW", Compat::WindowMessageStruct(hwnd, msg, wParam, lParam));
return LOG_RESULT(defWindowProc(hwnd, msg, wParam, lParam, CALL_ORIG_FUNC(DefWindowProcW)));
}
LRESULT editWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, WNDPROC origWndProc)
@ -285,7 +311,7 @@ namespace
{
case WM_NCPAINT:
CallWindowProc(origWndProc, hwnd, msg, wParam, lParam);
return onNcPaint(hwnd, wParam, origWndProc);
return onNcPaint(hwnd, origWndProc);
case WM_PAINT:
{
@ -310,6 +336,18 @@ namespace
return result;
}
case WM_WINDOWPOSCHANGED:
{
LRESULT result = CallWindowProc(origWndProc, hwnd, msg, wParam, lParam);
auto exStyle = CALL_ORIG_FUNC(GetWindowLongA)(hwnd, GWL_EXSTYLE);
if (exStyle & WS_EX_LAYERED)
{
CALL_ORIG_FUNC(SetWindowLongA)(hwnd, GWL_EXSTYLE, exStyle & ~WS_EX_LAYERED);
RedrawWindow(hwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_UPDATENOW);
}
return result;
}
case 0x1e5:
if (-1 == wParam)
{
@ -341,11 +379,11 @@ namespace
return CallWindowProc(origWndProc, hwnd, WM_ERASEBKGND, reinterpret_cast<WPARAM>(dc), 0);
}
LRESULT onNcPaint(HWND hwnd, WPARAM wParam, WNDPROC origWndProc)
LRESULT onNcActivate(HWND hwnd, WPARAM wParam, LPARAM lParam)
{
if (!hwnd)
if (-1 == lParam)
{
return CallWindowProc(origWndProc, hwnd, WM_NCPAINT, wParam, 0);
return TRUE;
}
HDC windowDc = GetWindowDC(hwnd);
@ -353,6 +391,26 @@ namespace
if (compatDc)
{
D3dDdi::ScopedCriticalSection lock;
Gdi::AccessGuard accessGuard(Gdi::ACCESS_WRITE);
Gdi::TitleBar titleBar(hwnd, compatDc);
titleBar.setActive(wParam);
titleBar.drawAll();
Gdi::Dc::releaseDc(windowDc);
}
CALL_ORIG_FUNC(ReleaseDC)(hwnd, windowDc);
return TRUE;
}
LRESULT onNcPaint(HWND hwnd, WNDPROC origWndProc)
{
HDC windowDc = GetWindowDC(hwnd);
HDC compatDc = Gdi::Dc::getDc(windowDc);
if (compatDc)
{
D3dDdi::ScopedCriticalSection lock;
Gdi::AccessGuard accessGuard(Gdi::ACCESS_WRITE);
Gdi::TitleBar titleBar(hwnd, compatDc);
titleBar.drawAll();
@ -430,9 +488,10 @@ namespace
if (compatDc)
{
D3dDdi::ScopedCriticalSection lock;
Gdi::AccessGuard accessGuard(Gdi::ACCESS_WRITE);
Gdi::TitleBar titleBar(hwnd, compatDc);
titleBar.drawCaption();
titleBar.drawAll();
Gdi::Dc::releaseDc(windowDc);
}
@ -479,7 +538,12 @@ namespace
[[maybe_unused]] const std::string& procName, WndProcHook wndProcHook, WNDPROC oldWndProcTrampoline)
{
LOG_FUNC(procName.c_str(), Compat::WindowMessageStruct(hwnd, uMsg, wParam, lParam));
return LOG_RESULT(wndProcHook(hwnd, uMsg, wParam, lParam, oldWndProcTrampoline));
LRESULT result = wndProcHook(hwnd, uMsg, wParam, lParam, oldWndProcTrampoline);
if (WM_CREATE == uMsg && -1 != result)
{
Gdi::WinProc::onCreateWindow(hwnd);
}
return LOG_RESULT(result);
}
template <WndProcHook wndProcHook>

View File

@ -15,6 +15,11 @@ namespace
namespace Gdi
{
Region::Region(std::nullptr_t)
: m_region(nullptr)
{
}
Region::Region(HRGN rgn)
: m_region(rgn)
{
@ -42,9 +47,13 @@ namespace Gdi
}
Region::Region(const Region& other)
: Region()
: Region(nullptr)
{
CombineRgn(m_region, other, nullptr, RGN_COPY);
if (other.m_region)
{
m_region = CreateRectRgn(0, 0, 0, 0);
CombineRgn(m_region, other, nullptr, RGN_COPY);
}
}
Region::Region(Region&& other)
@ -81,6 +90,23 @@ namespace Gdi
return m_region;
}
bool Region::operator==(const Region& other) const
{
if (m_region)
{
return other.m_region && EqualRgn(m_region, other.m_region);
}
else
{
return !other.m_region;
}
}
bool Region::operator!=(const Region& other) const
{
return !(*this == other);
}
Region Region::operator&(const Region& other) const
{
return combineRegions(*this, other, RGN_AND);

View File

@ -1,5 +1,7 @@
#pragma once
#include <cstddef>
#include <Windows.h>
namespace Gdi
@ -7,6 +9,7 @@ namespace Gdi
class Region
{
public:
Region(std::nullptr_t);
Region(HRGN rgn);
Region(const RECT& rect = RECT{ 0, 0, 0, 0 });
Region(HWND hwnd);
@ -21,6 +24,9 @@ namespace Gdi
operator HRGN() const;
bool operator==(const Region& other) const;
bool operator!=(const Region& other) const;
Region operator&(const Region& other) const;
Region operator|(const Region& other) const;
Region operator-(const Region& other) const;

View File

@ -19,9 +19,16 @@ namespace
namespace Gdi
{
TitleBar::TitleBar(HWND hwnd, HDC compatDc) :
m_hwnd(hwnd), m_compatDc(compatDc), m_buttonWidth(0), m_buttonHeight(0), m_tbi(),
m_windowRect(), m_hasIcon(false), m_hasTitleBar(false)
TitleBar::TitleBar(HWND hwnd, HDC compatDc)
: m_hwnd(hwnd)
, m_compatDc(compatDc)
, m_buttonWidth(0)
, m_buttonHeight(0)
, m_tbi{}
, m_windowRect{}
, m_hasIcon(false)
, m_hasTitleBar(false)
, m_isActive(false)
{
m_hasTitleBar = 0 != (CALL_ORIG_FUNC(GetWindowLongA)(hwnd, GWL_STYLE) & WS_CAPTION);
if (!m_hasTitleBar)
@ -46,6 +53,7 @@ namespace Gdi
GetWindowRect(hwnd, &m_windowRect);
OffsetRect(&m_tbi.rcTitleBar, -m_windowRect.left, -m_windowRect.top);
m_isActive = GetActiveWindow() == hwnd;
m_buttonWidth = GetSystemMetrics(SM_CXSIZE) - 2;
m_buttonHeight = GetSystemMetrics(SM_CYSIZE) - 4;
@ -94,7 +102,7 @@ namespace Gdi
}
UINT flags = 0;
if (GetActiveWindow() == m_hwnd)
if (m_isActive)
{
flags |= DC_ACTIVE;
}
@ -152,6 +160,11 @@ namespace Gdi
}
}
void TitleBar::setActive(bool isActive)
{
m_isActive = isActive;
}
bool TitleBar::isVisible(std::size_t tbiIndex) const
{
return !(m_tbi.rgstate[tbiIndex] & (STATE_SYSTEM_INVISIBLE | STATE_SYSTEM_OFFSCREEN));

View File

@ -15,6 +15,7 @@ namespace Gdi
void drawButtons() const;
void drawCaption() const;
void excludeFromClipRegion() const;
void setActive(bool isActive);
private:
void adjustButtonSize(RECT& rect) const;
@ -29,5 +30,6 @@ namespace Gdi
RECT m_windowRect;
bool m_hasIcon;
bool m_hasTitleBar;
bool m_isActive;
};
}

View File

@ -5,7 +5,7 @@
#include <Common/Hook.h>
#include <Common/Log.h>
#include <Common/ScopedCriticalSection.h>
#include <Common/ScopedSrwLock.h>
#include <Dll/Dll.h>
#include <Gdi/AccessGuard.h>
#include <Gdi/Dc.h>
@ -38,15 +38,15 @@ namespace
HWINEVENTHOOK g_objectStateChangeEventHook = nullptr;
std::set<Gdi::WindowPosChangeNotifyFunc> g_windowPosChangeNotifyFuncs;
Compat::CriticalSection g_windowProcCs;
Compat::SrwLock g_windowProcSrwLock;
std::map<HWND, WindowProc> g_windowProc;
WindowProc getWindowProc(HWND hwnd);
void onActivate(HWND hwnd);
bool isTopLevelWindow(HWND hwnd);
void onCreateWindow(HWND hwnd);
void onDestroyWindow(HWND hwnd);
void onWindowPosChanged(HWND hwnd);
void onWindowPosChanging(HWND hwnd, const WINDOWPOS& wp);
void onWindowPosChanging(HWND hwnd, WINDOWPOS& wp);
void setWindowProc(HWND hwnd, WNDPROC wndProcA, WNDPROC wndProcW);
LRESULT CALLBACK ddcWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam,
@ -56,6 +56,14 @@ namespace
switch (uMsg)
{
case WM_SYNCPAINT:
if (isTopLevelWindow(hwnd))
{
Gdi::Window::onSyncPaint(hwnd);
return 0;
}
break;
case WM_WINDOWPOSCHANGED:
onWindowPosChanged(hwnd);
break;
@ -65,18 +73,14 @@ namespace
switch (uMsg)
{
case WM_ACTIVATE:
onActivate(hwnd);
break;
case WM_NCDESTROY:
onDestroyWindow(hwnd);
break;
case WM_STYLECHANGED:
if (GWL_EXSTYLE == wParam)
if (isTopLevelWindow(hwnd))
{
onWindowPosChanged(hwnd);
Gdi::Window::onStyleChanged(hwnd, wParam);
}
break;
@ -103,7 +107,7 @@ namespace
{
if (GWL_WNDPROC == nIndex)
{
Compat::ScopedCriticalSection lock(g_windowProcCs);
Compat::ScopedSrwLockExclusive lock(g_windowProcSrwLock);
auto it = g_windowProc.find(hWnd);
if (it != g_windowProc.end())
{
@ -127,7 +131,7 @@ namespace
WindowProc getWindowProc(HWND hwnd)
{
Compat::ScopedCriticalSection lock(g_windowProcCs);
Compat::ScopedSrwLockExclusive lock(g_windowProcSrwLock);
return g_windowProc[hwnd];
}
@ -153,6 +157,11 @@ namespace
return TRUE;
}
bool isTopLevelWindow(HWND hwnd)
{
return GetDesktopWindow() == GetAncestor(hwnd, GA_PARENT);
}
void CALLBACK objectCreateEvent(
HWINEVENTHOOK /*hWinEventHook*/,
DWORD /*event*/,
@ -207,26 +216,28 @@ namespace
}
}
void onActivate(HWND hwnd)
{
RECT windowRect = {};
GetWindowRect(hwnd, &windowRect);
RECT clientRect = {};
GetClientRect(hwnd, &clientRect);
POINT clientOrigin = {};
ClientToScreen(hwnd, &clientOrigin);
OffsetRect(&windowRect, -clientOrigin.x, -clientOrigin.y);
HRGN ncRgn = CreateRectRgnIndirect(&windowRect);
HRGN clientRgn = CreateRectRgnIndirect(&clientRect);
CombineRgn(ncRgn, ncRgn, clientRgn, RGN_DIFF);
RedrawWindow(hwnd, nullptr, ncRgn, RDW_FRAME | RDW_INVALIDATE);
DeleteObject(clientRgn);
DeleteObject(ncRgn);
}
void onCreateWindow(HWND hwnd)
{
LOG_FUNC("onCreateWindow", hwnd);
{
Compat::ScopedSrwLockExclusive lock(g_windowProcSrwLock);
if (g_windowProc.find(hwnd) != g_windowProc.end())
{
return;
}
auto wndProcA = reinterpret_cast<WNDPROC>(CALL_ORIG_FUNC(GetWindowLongA)(hwnd, GWL_WNDPROC));
auto wndProcW = reinterpret_cast<WNDPROC>(CALL_ORIG_FUNC(GetWindowLongW)(hwnd, GWL_WNDPROC));
g_windowProc[hwnd] = { wndProcA, wndProcW };
setWindowProc(hwnd, ddcWindowProcA, ddcWindowProcW);
}
if (!isTopLevelWindow(hwnd))
{
return;
}
char className[64] = {};
GetClassName(hwnd, className, sizeof(className));
if (std::string(className) == "CompatWindowDesktopReplacement")
@ -236,27 +247,19 @@ namespace
return;
}
if (!Gdi::Window::isPresentationWindow(hwnd))
{
Compat::ScopedCriticalSection lock(g_windowProcCs);
if (g_windowProc.find(hwnd) == g_windowProc.end())
{
auto wndProcA = reinterpret_cast<WNDPROC>(CALL_ORIG_FUNC(GetWindowLongA)(hwnd, GWL_WNDPROC));
auto wndProcW = reinterpret_cast<WNDPROC>(CALL_ORIG_FUNC(GetWindowLongW)(hwnd, GWL_WNDPROC));
g_windowProc[hwnd] = { wndProcA, wndProcW };
setWindowProc(hwnd, ddcWindowProcA, ddcWindowProcW);
}
}
Gdi::Window::add(hwnd);
Gdi::Window::updateAll();
}
void onDestroyWindow(HWND hwnd)
{
Gdi::Window::remove(hwnd);
delete reinterpret_cast<ChildWindowInfo*>(RemoveProp(hwnd, PROP_DDRAWCOMPAT));
if (isTopLevelWindow(hwnd))
{
Gdi::Window::updateAll();
return;
}
Compat::ScopedCriticalSection lock(g_windowProcCs);
delete reinterpret_cast<ChildWindowInfo*>(RemoveProp(hwnd, PROP_DDRAWCOMPAT));
Compat::ScopedSrwLockExclusive lock(g_windowProcSrwLock);
auto it = g_windowProc.find(hwnd);
if (it != g_windowProc.end())
{
@ -267,24 +270,13 @@ namespace
void onWindowPosChanged(HWND hwnd)
{
if (Gdi::MENU_ATOM == GetClassLongPtr(hwnd, GCW_ATOM))
{
auto exStyle = CALL_ORIG_FUNC(GetWindowLongA)(hwnd, GWL_EXSTYLE);
if (exStyle & WS_EX_LAYERED)
{
CALL_ORIG_FUNC(SetWindowLongA)(hwnd, GWL_EXSTYLE, exStyle & ~WS_EX_LAYERED);
return;
}
}
for (auto notifyFunc : g_windowPosChangeNotifyFuncs)
{
notifyFunc();
}
if (Gdi::Window::isTopLevelWindow(hwnd))
if (isTopLevelWindow(hwnd))
{
Gdi::Window::add(hwnd);
Gdi::Window::updateAll();
}
else
@ -321,9 +313,13 @@ namespace
}
}
void onWindowPosChanging(HWND hwnd, const WINDOWPOS& wp)
void onWindowPosChanging(HWND hwnd, WINDOWPOS& wp)
{
if (!Gdi::Window::isTopLevelWindow(hwnd))
if (isTopLevelWindow(hwnd))
{
wp.flags |= SWP_NOREDRAW;
}
else
{
std::unique_ptr<ChildWindowInfo> cwi(reinterpret_cast<ChildWindowInfo*>(RemoveProp(hwnd, PROP_DDRAWCOMPAT)));
if (!(wp.flags & SWP_NOMOVE) && IsWindowVisible(hwnd) && !IsIconic(GetAncestor(hwnd, GA_ROOT)))
@ -339,12 +335,25 @@ namespace
}
}
BOOL WINAPI setLayeredWindowAttributes(HWND hwnd, COLORREF crKey, BYTE bAlpha, DWORD dwFlags)
{
LOG_FUNC("SetLayeredWindowAttributes", hwnd, crKey, bAlpha, dwFlags);
BOOL result = CALL_ORIG_FUNC(SetLayeredWindowAttributes)(hwnd, crKey, bAlpha, dwFlags);
if (result)
{
Gdi::Window::updateLayeredWindowInfo(hwnd,
(dwFlags & LWA_COLORKEY) ? crKey : CLR_INVALID,
(dwFlags & LWA_ALPHA) ? bAlpha : 255);
}
return LOG_RESULT(result);
}
LONG setWindowLong(HWND hWnd, int nIndex, LONG dwNewLong,
decltype(&SetWindowLongA) origSetWindowLong, WNDPROC(WindowProc::* wndProc))
{
if (GWL_WNDPROC == nIndex)
{
Compat::ScopedCriticalSection lock(g_windowProcCs);
Compat::ScopedSrwLockExclusive lock(g_windowProcSrwLock);
auto it = g_windowProc.find(hWnd);
if (it != g_windowProc.end() && 0 != origSetWindowLong(hWnd, nIndex, dwNewLong))
{
@ -385,6 +394,7 @@ namespace
wp.cy = cy;
wp.flags = uFlags;
onWindowPosChanging(hWnd, wp);
uFlags = wp.flags;
}
BOOL result = CALL_ORIG_FUNC(SetWindowPos)(hWnd, hWndInsertAfter, X, Y, cx, cy, uFlags);
delete reinterpret_cast<ChildWindowInfo*>(RemoveProp(hWnd, PROP_DDRAWCOMPAT));
@ -402,6 +412,34 @@ namespace
CALL_ORIG_FUNC(SetWindowLongA)(hwnd, GWL_WNDPROC, reinterpret_cast<LONG>(wndProcA));
}
}
BOOL WINAPI updateLayeredWindow(HWND hWnd, HDC hdcDst, POINT* pptDst, SIZE* psize,
HDC hdcSrc, POINT* pptSrc, COLORREF crKey, BLENDFUNCTION* pblend, DWORD dwFlags)
{
LOG_FUNC("UpdateLayeredWindow", hWnd, hdcDst, pptDst, psize, hdcSrc, pptSrc, crKey, pblend, dwFlags);
BOOL result = CALL_ORIG_FUNC(UpdateLayeredWindow)(
hWnd, hdcDst, pptDst, psize, hdcSrc, pptSrc, crKey, pblend, dwFlags);
if (result && hdcSrc)
{
Gdi::Window::updateLayeredWindowInfo(hWnd,
(dwFlags & ULW_COLORKEY) ? crKey : CLR_INVALID,
((dwFlags & LWA_ALPHA) && pblend) ? pblend->SourceConstantAlpha : 255);
}
return LOG_RESULT(result);
}
BOOL WINAPI updateLayeredWindowIndirect(HWND hwnd, const UPDATELAYEREDWINDOWINFO* pULWInfo)
{
LOG_FUNC("UpdateLayeredWindowIndirect", hwnd, pULWInfo);
BOOL result = CALL_ORIG_FUNC(UpdateLayeredWindowIndirect)(hwnd, pULWInfo);
if (result && pULWInfo)
{
Gdi::Window::updateLayeredWindowInfo(hwnd,
(pULWInfo->dwFlags & ULW_COLORKEY) ? pULWInfo->crKey : CLR_INVALID,
((pULWInfo->dwFlags & LWA_ALPHA) && pULWInfo->pblend) ? pULWInfo->pblend->SourceConstantAlpha : 255);
}
return LOG_RESULT(result);
}
}
namespace Gdi
@ -411,7 +449,7 @@ namespace Gdi
void dllThreadDetach()
{
auto threadId = GetCurrentThreadId();
Compat::ScopedCriticalSection lock(g_windowProcCs);
Compat::ScopedSrwLockExclusive lock(g_windowProcSrwLock);
auto it = g_windowProc.begin();
while (it != g_windowProc.end())
{
@ -430,9 +468,12 @@ namespace Gdi
{
HOOK_FUNCTION(user32, GetWindowLongA, getWindowLongA);
HOOK_FUNCTION(user32, GetWindowLongW, getWindowLongW);
HOOK_FUNCTION(user32, SetLayeredWindowAttributes, setLayeredWindowAttributes);
HOOK_FUNCTION(user32, SetWindowLongA, setWindowLongA);
HOOK_FUNCTION(user32, SetWindowLongW, setWindowLongW);
HOOK_FUNCTION(user32, SetWindowPos, setWindowPos);
HOOK_FUNCTION(user32, UpdateLayeredWindow, updateLayeredWindow);
HOOK_FUNCTION(user32, UpdateLayeredWindowIndirect, updateLayeredWindowIndirect);
g_objectCreateEventHook = SetWinEventHook(EVENT_OBJECT_CREATE, EVENT_OBJECT_CREATE,
Dll::g_currentModule, &objectCreateEvent, GetCurrentProcessId(), 0, WINEVENT_INCONTEXT);
@ -443,6 +484,14 @@ namespace Gdi
Gdi::Window::updateAll();
}
void onCreateWindow(HWND hwnd)
{
if (g_objectCreateEventHook)
{
::onCreateWindow(hwnd);
}
}
void watchWindowPosChanges(WindowPosChangeNotifyFunc notifyFunc)
{
g_windowPosChangeNotifyFuncs.insert(notifyFunc);
@ -453,7 +502,7 @@ namespace Gdi
UnhookWinEvent(g_objectStateChangeEventHook);
UnhookWinEvent(g_objectCreateEventHook);
Compat::ScopedCriticalSection lock(g_windowProcCs);
Compat::ScopedSrwLockExclusive lock(g_windowProcSrwLock);
for (const auto& windowProc : g_windowProc)
{
setWindowProc(windowProc.first, windowProc.second.wndProcA, windowProc.second.wndProcW);

View File

@ -1,6 +1,6 @@
#pragma once
#include "Gdi.h"
#include <Gdi/Gdi.h>
namespace Gdi
{
@ -8,6 +8,7 @@ namespace Gdi
{
void dllThreadDetach();
void installHooks();
void onCreateWindow(HWND hwnd);
void watchWindowPosChanges(WindowPosChangeNotifyFunc notifyFunc);
void uninstallHooks();
}

View File

@ -1,22 +1,90 @@
#include <algorithm>
#include <map>
#include <vector>
#include <dwmapi.h>
#include <Common/Hook.h>
#include <Common/Log.h>
#include <D3dDdi/KernelModeThunks.h>
#include <D3dDdi/ScopedCriticalSection.h>
#include <DDraw/RealPrimarySurface.h>
#include <Gdi/Gdi.h>
#include <Gdi/Region.h>
#include <Gdi/VirtualScreen.h>
#include <Gdi/Window.h>
namespace
{
struct UpdateWindowContext
{
Gdi::Region obscuredRegion;
Gdi::Region virtualScreenRegion;
DWORD processId;
};
struct Window
{
HWND hwnd;
HWND presentationWindow;
RECT windowRect;
Gdi::Region windowRegion;
Gdi::Region visibleRegion;
Gdi::Region invalidatedRegion;
COLORREF colorKey;
BYTE alpha;
bool isLayered;
bool isRgnChangePending;
Window(HWND hwnd)
: hwnd(hwnd)
, presentationWindow(nullptr)
, windowRect{}
, windowRegion(nullptr)
, colorKey(CLR_INVALID)
, alpha(255)
, isLayered(true)
, isRgnChangePending(false)
{
}
};
const UINT WM_CREATEPRESENTATIONWINDOW = WM_USER;
const UINT WM_DESTROYPRESENTATIONWINDOW = WM_USER + 1;
const UINT WM_SETPRESENTATIONWINDOWPOS = WM_USER + 2;
const UINT WM_SETPRESENTATIONWINDOWPOS = WM_USER + 1;
const UINT WM_SETPRESENTATIONWINDOWRGN = WM_USER + 2;
const RECT REGION_OVERRIDE_MARKER_RECT = { 32000, 32000, 32001, 32001 };
HANDLE g_presentationWindowThread = nullptr;
DWORD g_presentationWindowThreadId = 0;
HWND g_messageWindow = nullptr;
std::map<HWND, Window> g_windows;
std::vector<Window*> g_windowZOrder;
std::map<HWND, Window>::iterator addWindow(HWND hwnd)
{
DWMNCRENDERINGPOLICY ncRenderingPolicy = DWMNCRP_DISABLED;
DwmSetWindowAttribute(hwnd, DWMWA_NCRENDERING_POLICY, &ncRenderingPolicy, sizeof(ncRenderingPolicy));
BOOL disableTransitions = TRUE;
DwmSetWindowAttribute(hwnd, DWMWA_TRANSITIONS_FORCEDISABLED, &disableTransitions, sizeof(disableTransitions));
const auto style = GetClassLongPtr(hwnd, GCL_STYLE);
if (style & CS_DROPSHADOW)
{
SetClassLongPtr(hwnd, GCL_STYLE, style & ~CS_DROPSHADOW);
}
return g_windows.emplace(hwnd, Window(hwnd)).first;
}
Gdi::Region getWindowRegion(HWND hwnd)
{
Gdi::Region rgn;
if (ERROR == CALL_ORIG_FUNC(GetWindowRgn)(hwnd, rgn))
{
return nullptr;
}
return rgn;
}
LRESULT CALLBACK messageWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
@ -25,25 +93,18 @@ namespace
{
case WM_CREATEPRESENTATIONWINDOW:
{
D3dDdi::ScopedCriticalSection lock;
HWND origWindow = reinterpret_cast<HWND>(lParam);
auto window = Gdi::Window::get(origWindow);
if (!window)
{
return 0;
}
// Workaround for ForceSimpleWindow shim
static auto origCreateWindowExA = reinterpret_cast<decltype(&CreateWindowExA)>(
Compat::getProcAddress(GetModuleHandle("user32"), "CreateWindowExA"));
HWND owner = reinterpret_cast<HWND>(wParam);
HWND presentationWindow = origCreateWindowExA(
WS_EX_LAYERED | WS_EX_TRANSPARENT | WS_EX_NOPARENTNOTIFY | WS_EX_TOOLWINDOW,
"DDrawCompatPresentationWindow",
nullptr,
WS_DISABLED | WS_POPUP,
0, 0, 1, 1,
origWindow,
owner,
nullptr,
nullptr,
nullptr);
@ -51,11 +112,9 @@ namespace
if (presentationWindow)
{
CALL_ORIG_FUNC(SetLayeredWindowAttributes)(presentationWindow, 0, 255, LWA_ALPHA);
SendMessage(presentationWindow, WM_SETPRESENTATIONWINDOWPOS, 0, reinterpret_cast<LPARAM>(origWindow));
window->setPresentationWindow(presentationWindow);
}
return 0;
return reinterpret_cast<LRESULT>(presentationWindow);
}
case WM_DESTROY:
@ -73,62 +132,21 @@ namespace
switch (uMsg)
{
case WM_DESTROYPRESENTATIONWINDOW:
DestroyWindow(hwnd);
return 0;
case WM_SETPRESENTATIONWINDOWPOS:
{
HWND owner = reinterpret_cast<HWND>(lParam);
if (IsWindowVisible(owner) && !IsIconic(owner))
const auto& wp = *reinterpret_cast<WINDOWPOS*>(lParam);
return SetWindowPos(hwnd, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
}
case WM_SETPRESENTATIONWINDOWRGN:
{
HRGN rgn = nullptr;
if (wParam)
{
DWORD flags = SWP_SHOWWINDOW | SWP_NOOWNERZORDER | SWP_NOACTIVATE | SWP_NOSENDCHANGING | SWP_NOREDRAW;
HWND insertAfter = HWND_TOP;
HWND prev = GetWindow(owner, GW_HWNDPREV);
if (prev)
{
if (hwnd == prev)
{
flags = flags | SWP_NOZORDER;
}
else
{
insertAfter = prev;
}
}
RECT wr = {};
GetWindowRect(owner, &wr);
RECT wrPres = {};
GetWindowRect(hwnd, &wrPres);
if (!(flags & SWP_NOZORDER) || !EqualRect(&wr, &wrPres) || !IsWindowVisible(hwnd))
{
CALL_ORIG_FUNC(SetWindowPos)(
hwnd, insertAfter, wr.left, wr.top, wr.right - wr.left, wr.bottom - wr.top, flags);
}
Gdi::Region rgn;
int rgnResult = GetWindowRgn(owner, rgn);
Gdi::Region rgnPres;
int rgnPresResult = GetWindowRgn(hwnd, rgnPres);
if (rgnResult != rgnPresResult || !EqualRgn(rgn, rgnPres))
{
if (ERROR != rgnResult)
{
SetWindowRgn(hwnd, rgn.release(), FALSE);
}
else
{
SetWindowRgn(hwnd, nullptr, FALSE);
}
}
rgn = CreateRectRgn(0, 0, 0, 0);
CombineRgn(rgn, reinterpret_cast<HRGN>(wParam), nullptr, RGN_COPY);
}
else if (IsWindowVisible(hwnd))
{
ShowWindow(hwnd, SW_HIDE);
}
return 0;
return SetWindowRgn(hwnd, rgn, FALSE);
}
}
@ -162,334 +180,469 @@ namespace
return 0;
}
BOOL WINAPI setLayeredWindowAttributes(HWND hwnd, COLORREF crKey, BYTE bAlpha, DWORD dwFlags)
LRESULT sendMessageBlocking(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
LOG_FUNC("SetLayeredWindowAttributes", hwnd, crKey, bAlpha, dwFlags);
BOOL result = CALL_ORIG_FUNC(SetLayeredWindowAttributes)(hwnd, crKey, bAlpha, dwFlags);
if (result)
{
Gdi::Window::updateLayeredWindowInfo(hwnd,
(dwFlags & LWA_COLORKEY) ? crKey : CLR_INVALID,
(dwFlags & LWA_ALPHA) ? bAlpha : 255);
}
return LOG_RESULT(result);
DWORD_PTR result = 0;
SendMessageTimeout(hwnd, msg, wParam, lParam, SMTO_BLOCK | SMTO_NOTIMEOUTIFNOTHUNG, 0, &result);
return result;
}
BOOL WINAPI updateLayeredWindow(HWND hWnd, HDC hdcDst, POINT* pptDst, SIZE* psize,
HDC hdcSrc, POINT* pptSrc, COLORREF crKey, BLENDFUNCTION* pblend, DWORD dwFlags)
void updatePosition(Window& window, const RECT& oldWindowRect, const Gdi::Region& oldVisibleRegion)
{
LOG_FUNC("UpdateLayeredWindow", hWnd, hdcDst, pptDst, psize, hdcSrc, pptSrc, crKey, pblend, dwFlags);
BOOL result = CALL_ORIG_FUNC(UpdateLayeredWindow)(
hWnd, hdcDst, pptDst, psize, hdcSrc, pptSrc, crKey, pblend, dwFlags);
if (result && hdcSrc)
Gdi::Region preservedRegion(oldVisibleRegion);
preservedRegion.offset(window.windowRect.left - oldWindowRect.left, window.windowRect.top - oldWindowRect.top);
preservedRegion &= window.visibleRegion;
POINT clientPos = {};
ClientToScreen(window.hwnd, &clientPos);
if (!preservedRegion.isEmpty())
{
Gdi::Window::updateLayeredWindowInfo(hWnd,
(dwFlags & ULW_COLORKEY) ? crKey : CLR_INVALID,
((dwFlags & LWA_ALPHA) && pblend) ? pblend->SourceConstantAlpha : 255);
Gdi::Region updateRegion;
GetUpdateRgn(window.hwnd, updateRegion, FALSE);
OffsetRgn(updateRegion, clientPos.x, clientPos.y);
preservedRegion -= updateRegion;
if (!preservedRegion.isEmpty() &&
(window.windowRect.left != oldWindowRect.left || window.windowRect.top != oldWindowRect.top))
{
HDC screenDc = GetDC(nullptr);
SelectClipRgn(screenDc, preservedRegion);
BitBlt(screenDc, window.windowRect.left, window.windowRect.top,
oldWindowRect.right - oldWindowRect.left, oldWindowRect.bottom - oldWindowRect.top,
screenDc, oldWindowRect.left, oldWindowRect.top, SRCCOPY);
SelectClipRgn(screenDc, nullptr);
CALL_ORIG_FUNC(ReleaseDC)(nullptr, screenDc);
}
}
return LOG_RESULT(result);
window.invalidatedRegion = window.visibleRegion - preservedRegion;
OffsetRgn(window.invalidatedRegion, -clientPos.x, -clientPos.y);
}
BOOL WINAPI updateLayeredWindowIndirect(HWND hwnd, const UPDATELAYEREDWINDOWINFO* pULWInfo)
BOOL CALLBACK updateWindow(HWND hwnd, LPARAM lParam)
{
LOG_FUNC("UpdateLayeredWindowIndirect", hwnd, pULWInfo);
BOOL result = CALL_ORIG_FUNC(UpdateLayeredWindowIndirect)(hwnd, pULWInfo);
if (result && pULWInfo)
auto& context = *reinterpret_cast<UpdateWindowContext*>(lParam);
DWORD processId = 0;
GetWindowThreadProcessId(hwnd, &processId);
if (GetWindowThreadProcessId(hwnd, &processId) == g_presentationWindowThreadId ||
processId != context.processId)
{
Gdi::Window::updateLayeredWindowInfo(hwnd,
(pULWInfo->dwFlags & ULW_COLORKEY) ? pULWInfo->crKey : CLR_INVALID,
((pULWInfo->dwFlags & LWA_ALPHA) && pULWInfo->pblend) ? pULWInfo->pblend->SourceConstantAlpha : 255);
return TRUE;
}
return LOG_RESULT(result);
auto it = g_windows.find(hwnd);
if (it == g_windows.end())
{
it = addWindow(hwnd);
}
g_windowZOrder.push_back(&it->second);
const LONG exStyle = CALL_ORIG_FUNC(GetWindowLongA)(hwnd, GWL_EXSTYLE);
const bool isLayered = exStyle & WS_EX_LAYERED;
const bool isVisible = IsWindowVisible(hwnd) && !IsIconic(hwnd);
bool setPresentationWindowRgn = false;
if (isLayered != it->second.isLayered)
{
it->second.isLayered = isLayered;
it->second.isRgnChangePending = isVisible;
if (!isLayered)
{
it->second.presentationWindow = reinterpret_cast<HWND>(
sendMessageBlocking(g_messageWindow, WM_CREATEPRESENTATIONWINDOW, reinterpret_cast<WPARAM>(hwnd), 0));
setPresentationWindowRgn = true;
}
else if (it->second.presentationWindow)
{
sendMessageBlocking(it->second.presentationWindow, WM_CLOSE, 0, 0);
it->second.presentationWindow = nullptr;
}
}
Gdi::Region windowRegion(getWindowRegion(hwnd));
if (windowRegion && !PtInRegion(windowRegion, REGION_OVERRIDE_MARKER_RECT.left, REGION_OVERRIDE_MARKER_RECT.top) ||
!windowRegion && it->second.windowRegion)
{
swap(it->second.windowRegion, windowRegion);
setPresentationWindowRgn = true;
}
RECT windowRect = {};
Gdi::Region visibleRegion;
if (isVisible)
{
GetWindowRect(hwnd, &windowRect);
if (!IsRectEmpty(&windowRect))
{
if (it->second.windowRegion)
{
visibleRegion = it->second.windowRegion;
OffsetRgn(visibleRegion, windowRect.left, windowRect.top);
}
else
{
visibleRegion = windowRect;
}
visibleRegion &= context.virtualScreenRegion;
visibleRegion -= context.obscuredRegion;
if (!isLayered && !(exStyle & WS_EX_TRANSPARENT))
{
context.obscuredRegion |= visibleRegion;
}
}
}
std::swap(it->second.windowRect, windowRect);
swap(it->second.visibleRegion, visibleRegion);
if (!isLayered)
{
if (!it->second.visibleRegion.isEmpty())
{
updatePosition(it->second, windowRect, visibleRegion);
}
if (isVisible && !it->second.isRgnChangePending)
{
OffsetRgn(visibleRegion, it->second.windowRect.left - windowRect.left, it->second.windowRect.top - windowRect.top);
if (it->second.visibleRegion != visibleRegion)
{
it->second.isRgnChangePending = true;
}
}
if (it->second.presentationWindow)
{
if (setPresentationWindowRgn)
{
HRGN rgn = it->second.windowRegion;
sendMessageBlocking(it->second.presentationWindow, WM_SETPRESENTATIONWINDOWRGN,
reinterpret_cast<WPARAM>(rgn), 0);
}
WINDOWPOS wp = {};
wp.flags = SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOREDRAW | SWP_NOSENDCHANGING;
if (isVisible)
{
wp.hwndInsertAfter = GetWindow(hwnd, GW_HWNDPREV);
if (!wp.hwndInsertAfter)
{
wp.hwndInsertAfter = (GetWindowLong(hwnd, GWL_EXSTYLE) & WS_EX_TOPMOST) ? HWND_TOPMOST : HWND_TOP;
}
else if (wp.hwndInsertAfter == it->second.presentationWindow)
{
wp.flags |= SWP_NOZORDER;
}
wp.x = it->second.windowRect.left;
wp.y = it->second.windowRect.top;
wp.cx = it->second.windowRect.right - it->second.windowRect.left;
wp.cy = it->second.windowRect.bottom - it->second.windowRect.top;
wp.flags |= SWP_SHOWWINDOW;
}
else
{
wp.flags |= SWP_HIDEWINDOW | SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER;
}
sendMessageBlocking(it->second.presentationWindow, WM_SETPRESENTATIONWINDOWPOS,
0, reinterpret_cast<LPARAM>(&wp));
}
}
return TRUE;
}
}
namespace Gdi
{
Window::Window(HWND hwnd)
: m_hwnd(hwnd)
, m_presentationWindow(nullptr)
, m_windowRect{ 0, 0, 0, 0 }
, m_colorKey(CLR_INVALID)
, m_alpha(255)
, m_isLayered(CALL_ORIG_FUNC(GetWindowLongA)(m_hwnd, GWL_EXSTYLE)& WS_EX_LAYERED)
namespace Window
{
DWMNCRENDERINGPOLICY ncRenderingPolicy = DWMNCRP_DISABLED;
DwmSetWindowAttribute(hwnd, DWMWA_NCRENDERING_POLICY, &ncRenderingPolicy, sizeof(ncRenderingPolicy));
BOOL disableTransitions = TRUE;
DwmSetWindowAttribute(hwnd, DWMWA_TRANSITIONS_FORCEDISABLED, &disableTransitions, sizeof(disableTransitions));
const auto style = GetClassLongPtr(hwnd, GCL_STYLE);
if (style & CS_DROPSHADOW)
void installHooks()
{
SetClassLongPtr(hwnd, GCL_STYLE, style & ~CS_DROPSHADOW);
WNDCLASS wc = {};
wc.lpfnWndProc = &presentationWindowProc;
wc.hInstance = Dll::g_currentModule;
wc.lpszClassName = "DDrawCompatPresentationWindow";
RegisterClass(&wc);
g_presentationWindowThread = CreateThread(
nullptr, 0, &presentationWindowThreadProc, nullptr, 0, &g_presentationWindowThreadId);
int i = 0;
while (!g_messageWindow && i < 1000)
{
Sleep(1);
++i;
}
if (!g_messageWindow)
{
Compat::Log() << "ERROR: Failed to create a message-only window";
}
}
if (!m_isLayered)
bool isPresentationWindow(HWND hwnd)
{
SendNotifyMessage(g_messageWindow, WM_CREATEPRESENTATIONWINDOW, 0, reinterpret_cast<WPARAM>(hwnd));
return g_presentationWindowThreadId == GetWindowThreadProcessId(hwnd, nullptr);
}
update();
}
Window::~Window()
{
if (m_presentationWindow)
void onStyleChanged(HWND hwnd, WPARAM wParam)
{
SendNotifyMessage(m_presentationWindow, WM_DESTROYPRESENTATIONWINDOW, 0, 0);
if (GWL_EXSTYLE == wParam)
{
D3dDdi::ScopedCriticalSection lock;
auto it = g_windows.find(hwnd);
if (it != g_windows.end())
{
const bool isLayered = GetWindowLong(hwnd, GWL_EXSTYLE) & WS_EX_LAYERED;
if (isLayered != it->second.isLayered)
{
updateAll();
}
}
}
}
}
bool Window::add(HWND hwnd)
{
if (isTopLevelWindow(hwnd) && !get(hwnd))
void onSyncPaint(HWND hwnd)
{
LOG_FUNC("Window::onSyncPaint", hwnd);
bool isInvalidated = false;
{
D3dDdi::ScopedCriticalSection lock;
auto it = g_windows.find(hwnd);
if (it == g_windows.end())
{
return;
}
if (it->second.isRgnChangePending)
{
it->second.isRgnChangePending = false;
const LONG origWndProc = CALL_ORIG_FUNC(GetWindowLongA)(hwnd, GWL_WNDPROC);
CALL_ORIG_FUNC(SetWindowLongA)(hwnd, GWL_WNDPROC, reinterpret_cast<LONG>(CALL_ORIG_FUNC(DefWindowProcA)));
if (it->second.isLayered)
{
SetWindowRgn(hwnd, Gdi::Region(it->second.windowRegion).release(), FALSE);
}
else
{
Gdi::Region rgn(it->second.visibleRegion);
OffsetRgn(rgn, -it->second.windowRect.left, -it->second.windowRect.top);
rgn |= REGION_OVERRIDE_MARKER_RECT;
SetWindowRgn(hwnd, rgn, FALSE);
}
CALL_ORIG_FUNC(SetWindowLongA)(hwnd, GWL_WNDPROC, origWndProc);
}
isInvalidated = !it->second.invalidatedRegion.isEmpty();
if (isInvalidated)
{
RedrawWindow(hwnd, nullptr, it->second.invalidatedRegion,
RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_ALLCHILDREN);
it->second.invalidatedRegion = nullptr;
}
}
if (isInvalidated)
{
RECT emptyRect = {};
RedrawWindow(hwnd, &emptyRect, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_ERASENOW);
}
}
void present(CompatRef<IDirectDrawSurface7> dst, CompatRef<IDirectDrawSurface7> src,
CompatRef<IDirectDrawClipper> clipper)
{
D3dDdi::ScopedCriticalSection lock;
s_windows.emplace(hwnd, std::make_shared<Window>(hwnd));
return true;
}
return false;
}
void Window::calcInvalidatedRegion(const RECT& oldWindowRect, const Region& oldVisibleRegion)
{
if (IsRectEmpty(&m_windowRect) || m_visibleRegion.isEmpty())
{
m_invalidatedRegion = Region();
return;
}
m_invalidatedRegion = m_visibleRegion;
if (m_windowRect.right - m_windowRect.left == oldWindowRect.right - oldWindowRect.left &&
m_windowRect.bottom - m_windowRect.top == oldWindowRect.bottom - oldWindowRect.top)
{
Region preservedRegion(oldVisibleRegion);
preservedRegion.offset(m_windowRect.left - oldWindowRect.left, m_windowRect.top - oldWindowRect.top);
preservedRegion &= m_visibleRegion;
if (!preservedRegion.isEmpty())
for (auto window : g_windowZOrder)
{
if (m_windowRect.left != oldWindowRect.left || m_windowRect.top != oldWindowRect.top)
if (window->presentationWindow && !window->visibleRegion.isEmpty())
{
HDC screenDc = GetDC(nullptr);
SelectClipRgn(screenDc, preservedRegion);
BitBlt(screenDc, m_windowRect.left, m_windowRect.top,
oldWindowRect.right - oldWindowRect.left, oldWindowRect.bottom - oldWindowRect.top,
screenDc, oldWindowRect.left, oldWindowRect.top, SRCCOPY);
SelectClipRgn(screenDc, nullptr);
CALL_ORIG_FUNC(ReleaseDC)(nullptr, screenDc);
clipper->SetHWnd(&clipper, 0, window->presentationWindow);
dst->Blt(&dst, nullptr, &src, nullptr, DDBLT_WAIT, nullptr);
}
m_invalidatedRegion -= preservedRegion;
}
}
}
std::shared_ptr<Window> Window::get(HWND hwnd)
{
D3dDdi::ScopedCriticalSection lock;
auto it = s_windows.find(hwnd);
return it != s_windows.end() ? it->second : nullptr;
}
BYTE Window::getAlpha() const
{
D3dDdi::ScopedCriticalSection lock;
return m_alpha;
}
COLORREF Window::getColorKey() const
{
D3dDdi::ScopedCriticalSection lock;
return m_colorKey;
}
HWND Window::getPresentationWindow() const
{
return m_presentationWindow ? m_presentationWindow : m_hwnd;
}
Region Window::getVisibleRegion() const
{
D3dDdi::ScopedCriticalSection lock;
return m_visibleRegion;
}
RECT Window::getWindowRect() const
{
D3dDdi::ScopedCriticalSection lock;
return m_windowRect;
}
std::map<HWND, std::shared_ptr<Window>> Window::getWindows()
{
D3dDdi::ScopedCriticalSection lock;
return s_windows;
}
void Window::installHooks()
{
HOOK_FUNCTION(user32, SetLayeredWindowAttributes, setLayeredWindowAttributes);
HOOK_FUNCTION(user32, UpdateLayeredWindow, updateLayeredWindow);
HOOK_FUNCTION(user32, UpdateLayeredWindowIndirect, updateLayeredWindowIndirect);
WNDCLASS wc = {};
wc.lpfnWndProc = &presentationWindowProc;
wc.hInstance = Dll::g_currentModule;
wc.lpszClassName = "DDrawCompatPresentationWindow";
RegisterClass(&wc);
g_presentationWindowThread = CreateThread(
nullptr, 0, &presentationWindowThreadProc, nullptr, 0, &g_presentationWindowThreadId);
int i = 0;
while (!g_messageWindow && i < 1000)
void present(Gdi::Region excludeRegion)
{
Sleep(1);
++i;
}
D3dDdi::ScopedCriticalSection lock;
std::unique_ptr<HDC__, void(*)(HDC)> virtualScreenDc(nullptr, &Gdi::VirtualScreen::deleteDc);
RECT virtualScreenBounds = Gdi::VirtualScreen::getBounds();
if (!g_messageWindow)
{
Compat::Log() << "ERROR: Failed to create a message-only window";
}
}
bool Window::isLayered() const
{
return m_isLayered;
}
bool Window::isPresentationWindow(HWND hwnd)
{
return IsWindow(hwnd) && g_presentationWindowThreadId == GetWindowThreadProcessId(hwnd, nullptr);
}
bool Window::isTopLevelWindow(HWND hwnd)
{
return GetDesktopWindow() == GetAncestor(hwnd, GA_PARENT);
}
void Window::remove(HWND hwnd)
{
D3dDdi::ScopedCriticalSection lock;
s_windows.erase(hwnd);
}
void Window::uninstallHooks()
{
if (g_presentationWindowThread)
{
SendMessage(g_messageWindow, WM_CLOSE, 0, 0);
if (WAIT_OBJECT_0 != WaitForSingleObject(g_presentationWindowThread, 1000))
for (auto window : g_windowZOrder)
{
TerminateThread(g_presentationWindowThread, 0);
Compat::Log() << "The presentation window thread was terminated forcefully";
if (!window->presentationWindow)
{
continue;
}
Gdi::Region visibleRegion(window->visibleRegion);
if (excludeRegion)
{
visibleRegion -= excludeRegion;
}
if (visibleRegion.isEmpty())
{
continue;
}
if (!virtualScreenDc)
{
virtualScreenDc.reset(Gdi::VirtualScreen::createDc());
if (!virtualScreenDc)
{
return;
}
}
HDC dc = GetWindowDC(window->presentationWindow);
RECT rect = window->windowRect;
visibleRegion.offset(-rect.left, -rect.top);
SelectClipRgn(dc, visibleRegion);
CALL_ORIG_FUNC(BitBlt)(dc, 0, 0, rect.right - rect.left, rect.bottom - rect.top, virtualScreenDc.get(),
rect.left - virtualScreenBounds.left, rect.top - virtualScreenBounds.top, SRCCOPY);
CALL_ORIG_FUNC(ReleaseDC)(window->presentationWindow, dc);
}
}
}
void Window::setPresentationWindow(HWND hwnd)
{
D3dDdi::ScopedCriticalSection lock;
if (m_isLayered)
void presentLayered(CompatRef<IDirectDrawSurface7> dst, POINT offset)
{
SendNotifyMessage(hwnd, WM_DESTROYPRESENTATIONWINDOW, 0, 0);
}
else
{
m_presentationWindow = hwnd;
SendNotifyMessage(m_presentationWindow, WM_SETPRESENTATIONWINDOWPOS, 0, reinterpret_cast<LPARAM>(m_hwnd));
DDraw::RealPrimarySurface::scheduleUpdate();
}
}
D3dDdi::ScopedCriticalSection lock;
void Window::update()
{
D3dDdi::ScopedCriticalSection lock;
const bool isLayered = CALL_ORIG_FUNC(GetWindowLongA)(m_hwnd, GWL_EXSTYLE) & WS_EX_LAYERED;
if (isLayered != m_isLayered)
{
if (!isLayered)
HDC dstDc = nullptr;
for (auto it = g_windowZOrder.rbegin(); it != g_windowZOrder.rend(); ++it)
{
SendNotifyMessage(g_messageWindow, WM_CREATEPRESENTATIONWINDOW, 0, reinterpret_cast<WPARAM>(m_hwnd));
auto& window = **it;
if (!window.isLayered)
{
continue;
}
if (!dstDc)
{
const UINT D3DDDIFMT_UNKNOWN = 0;
const UINT D3DDDIFMT_X8R8G8B8 = 22;
D3dDdi::KernelModeThunks::setDcFormatOverride(D3DDDIFMT_X8R8G8B8);
dst->GetDC(&dst, &dstDc);
D3dDdi::KernelModeThunks::setDcFormatOverride(D3DDDIFMT_UNKNOWN);
if (!dstDc)
{
return;
}
}
HDC windowDc = GetWindowDC(window.hwnd);
Gdi::Region rgn(window.visibleRegion);
RECT wr = window.windowRect;
if (0 != offset.x || 0 != offset.y)
{
OffsetRect(&wr, offset.x, offset.y);
rgn.offset(offset.x, offset.y);
}
SelectClipRgn(dstDc, rgn);
auto colorKey = window.colorKey;
if (CLR_INVALID != colorKey)
{
CALL_ORIG_FUNC(TransparentBlt)(dstDc, wr.left, wr.top, wr.right - wr.left, wr.bottom - wr.top,
windowDc, 0, 0, wr.right - wr.left, wr.bottom - wr.top, colorKey);
}
else
{
BLENDFUNCTION blend = {};
blend.SourceConstantAlpha = window.alpha;
CALL_ORIG_FUNC(AlphaBlend)(dstDc, wr.left, wr.top, wr.right - wr.left, wr.bottom - wr.top,
windowDc, 0, 0, wr.right - wr.left, wr.bottom - wr.top, blend);
}
CALL_ORIG_FUNC(ReleaseDC)(window.hwnd, windowDc);
}
else if (m_presentationWindow)
if (dstDc)
{
SendNotifyMessage(m_presentationWindow, WM_DESTROYPRESENTATIONWINDOW, 0, 0);
m_presentationWindow = nullptr;
SelectClipRgn(dstDc, nullptr);
dst->ReleaseDC(&dst, dstDc);
}
}
m_isLayered = isLayered;
RECT newWindowRect = {};
Region newVisibleRegion;
if (IsWindowVisible(m_hwnd) && !IsIconic(m_hwnd))
void updateAll()
{
GetWindowRect(m_hwnd, &newWindowRect);
if (!IsRectEmpty(&newWindowRect) && !m_isLayered)
LOG_FUNC("Window::updateAll");
UpdateWindowContext context;
context.processId = GetCurrentProcessId();
context.virtualScreenRegion = VirtualScreen::getRegion();
std::vector<HWND> invalidatedWindows;
{
newVisibleRegion = m_hwnd;
D3dDdi::ScopedCriticalSection lock;
g_windowZOrder.clear();
EnumWindows(updateWindow, reinterpret_cast<LPARAM>(&context));
for (auto it = g_windows.begin(); it != g_windows.end();)
{
if (g_windowZOrder.end() == std::find(g_windowZOrder.begin(), g_windowZOrder.end(), &it->second))
{
if (it->second.presentationWindow)
{
sendMessageBlocking(it->second.presentationWindow, WM_CLOSE, 0, 0);
}
it = g_windows.erase(it);
}
else
{
++it;
}
}
for (auto it = g_windowZOrder.rbegin(); it != g_windowZOrder.rend(); ++it)
{
auto& window = **it;
if (window.isRgnChangePending || !window.invalidatedRegion.isEmpty())
{
invalidatedWindows.push_back(window.hwnd);
}
}
}
}
if (m_presentationWindow)
{
SendNotifyMessage(m_presentationWindow, WM_SETPRESENTATIONWINDOWPOS, 0, reinterpret_cast<LPARAM>(m_hwnd));
}
std::swap(m_windowRect, newWindowRect);
swap(m_visibleRegion, newVisibleRegion);
calcInvalidatedRegion(newWindowRect, newVisibleRegion);
}
void Window::updateAll()
{
auto windows(getWindows());
for (auto& windowPair : windows)
{
windowPair.second->update();
}
for (auto& windowPair : windows)
{
if (!windowPair.second->m_invalidatedRegion.isEmpty())
for (auto hwnd : invalidatedWindows)
{
POINT clientOrigin = {};
ClientToScreen(windowPair.first, &clientOrigin);
windowPair.second->m_invalidatedRegion.offset(-clientOrigin.x, -clientOrigin.y);
RedrawWindow(windowPair.first, nullptr, windowPair.second->m_invalidatedRegion,
RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_ALLCHILDREN | RDW_ERASENOW);
SendNotifyMessage(hwnd, WM_SYNCPAINT, 0, 0);
}
}
void updateLayeredWindowInfo(HWND hwnd, COLORREF colorKey, BYTE alpha)
{
D3dDdi::ScopedCriticalSection lock;
auto it = g_windows.find(hwnd);
if (it != g_windows.end())
{
it->second.colorKey = colorKey;
it->second.alpha = alpha;
if (!it->second.visibleRegion.isEmpty())
{
DDraw::RealPrimarySurface::scheduleUpdate();
}
}
}
void uninstallHooks()
{
if (g_presentationWindowThread)
{
sendMessageBlocking(g_messageWindow, WM_CLOSE, 0, 0);
if (WAIT_OBJECT_0 != WaitForSingleObject(g_presentationWindowThread, 1000))
{
TerminateThread(g_presentationWindowThread, 0);
Compat::Log() << "The presentation window thread was terminated forcefully";
}
}
}
}
void Window::updateLayeredWindowInfo(HWND hwnd, COLORREF colorKey, BYTE alpha)
{
D3dDdi::ScopedCriticalSection lock;
auto window(get(hwnd));
if (window)
{
window->m_colorKey = colorKey;
window->m_alpha = alpha;
DDraw::RealPrimarySurface::scheduleUpdate();
}
}
void Window::updateWindow()
{
RECT windowRect = {};
GetWindowRect(m_hwnd, &windowRect);
if (!EqualRect(&windowRect, &m_windowRect))
{
updateAll();
}
}
std::map<HWND, std::shared_ptr<Window>> Window::s_windows;
}

View File

@ -1,57 +1,25 @@
#pragma once
#include <map>
#include <memory>
#include <ddraw.h>
#include <Windows.h>
#include "Gdi/Region.h"
#include <Common/CompatRef.h>
#include <Gdi/Region.h>
namespace Gdi
{
class Window
namespace Window
{
public:
Window(HWND hwnd);
Window(const Window&) = delete;
Window& operator=(const Window&) = delete;
~Window();
bool isPresentationWindow(HWND hwnd);
void onStyleChanged(HWND hwnd, WPARAM wParam);
void onSyncPaint(HWND hwnd);
void present(CompatRef<IDirectDrawSurface7> dst, CompatRef<IDirectDrawSurface7> src,
CompatRef<IDirectDrawClipper> clipper);
void present(Gdi::Region excludeRegion);
void presentLayered(CompatRef<IDirectDrawSurface7> dst, POINT offset);
void updateAll();
void updateLayeredWindowInfo(HWND hwnd, COLORREF colorKey, BYTE alpha);
BYTE getAlpha() const;
COLORREF getColorKey() const;
HWND getPresentationWindow() const;
Region getVisibleRegion() const;
RECT getWindowRect() const;
bool isLayered() const;
void setPresentationWindow(HWND hwnd);
void updateWindow();
static bool add(HWND hwnd);
static std::shared_ptr<Window> get(HWND hwnd);
static void remove(HWND hwnd);
static std::map<HWND, std::shared_ptr<Window>> getWindows();
static bool isPresentationWindow(HWND hwnd);
static bool isTopLevelWindow(HWND hwnd);
static void updateAll();
static void updateLayeredWindowInfo(HWND hwnd, COLORREF colorKey, BYTE alpha);
static void installHooks();
static void uninstallHooks();
private:
void calcInvalidatedRegion(const RECT& oldWindowRect, const Region& oldVisibleRegion);
void update();
HWND m_hwnd;
HWND m_presentationWindow;
RECT m_windowRect;
Region m_visibleRegion;
Region m_invalidatedRegion;
COLORREF m_colorKey;
BYTE m_alpha;
bool m_isLayered;
static std::map<HWND, std::shared_ptr<Window>> s_windows;
};
void installHooks();
void uninstallHooks();
}
}

View File

@ -339,7 +339,7 @@ std::ostream& operator<<(std::ostream& os, const NMHDR& nm)
<< Compat::hex(nm.code);
}
std::ostream& operator<<(std::ostream& os, const POINTS& p)
std::ostream& operator<<(std::ostream& os, const POINT& p)
{
return Compat::LogStruct(os)
<< p.x

View File

@ -32,7 +32,7 @@ std::ostream& operator<<(std::ostream& os, const MINMAXINFO& mmi);
std::ostream& operator<<(std::ostream& os, const MSG& msg);
std::ostream& operator<<(std::ostream& os, const NCCALCSIZE_PARAMS& nccs);
std::ostream& operator<<(std::ostream& os, const NMHDR& nm);
std::ostream& operator<<(std::ostream& os, const POINTS& p);
std::ostream& operator<<(std::ostream& os, const POINT& p);
std::ostream& operator<<(std::ostream& os, const RECT& rect);
std::ostream& operator<<(std::ostream& os, const SIZE& size);
std::ostream& operator<<(std::ostream& os, const STYLESTRUCT& ss);