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

Fixed display issues with user32 controls

This commit is contained in:
narzoul 2018-10-20 17:49:00 +02:00
parent 537ef9c595
commit 2f00b74a56
15 changed files with 510 additions and 385 deletions

View File

@ -96,10 +96,13 @@ std::ostream& operator<<(std::ostream& os, HRGN__& rgn)
std::ostream& operator<<(std::ostream& os, HWND__& hwnd) std::ostream& operator<<(std::ostream& os, HWND__& hwnd)
{ {
char name[256] = {}; char name[256] = "INVALID";
GetClassName(&hwnd, name, sizeof(name));
RECT rect = {}; RECT rect = {};
GetWindowRect(&hwnd, &rect); if (IsWindow(&hwnd))
{
GetClassName(&hwnd, name, sizeof(name));
GetWindowRect(&hwnd, &rect);
}
return os << "WND(" << static_cast<void*>(&hwnd) << ',' << name << ',' << rect << ')'; return os << "WND(" << static_cast<void*>(&hwnd) << ',' << name << ',' << rect << ')';
} }

View File

@ -22,18 +22,6 @@
namespace namespace
{ {
struct BltToWindowViaGdiArgs
{
std::unique_ptr<HDC__, void(*)(HDC)> virtualScreenDc;
Gdi::Region* primaryRegion;
BltToWindowViaGdiArgs()
: virtualScreenDc(nullptr, &Gdi::VirtualScreen::deleteDc)
, primaryRegion(nullptr)
{
}
};
void onRelease(); void onRelease();
DWORD WINAPI updateThreadProc(LPVOID lpParameter); DWORD WINAPI updateThreadProc(LPVOID lpParameter);
@ -70,65 +58,61 @@ namespace
return TRUE; return TRUE;
} }
BOOL CALLBACK bltToWindow(HWND hwnd, LPARAM lParam) void bltToWindow(CompatRef<IDirectDrawSurface7> src)
{ {
if (!IsWindowVisible(hwnd) || !Gdi::Window::isPresentationWindow(hwnd)) for (auto windowPair : Gdi::Window::getWindows())
{ {
return TRUE; if (IsWindowVisible(windowPair.first))
{
g_clipper->SetHWnd(g_clipper, 0, windowPair.second->getPresentationWindow());
g_frontBuffer->Blt(g_frontBuffer, nullptr, &src, nullptr, DDBLT_WAIT, nullptr);
}
} }
g_clipper->SetHWnd(g_clipper, 0, hwnd);
auto src = reinterpret_cast<IDirectDrawSurface7*>(lParam);
g_frontBuffer->Blt(g_frontBuffer, nullptr, src, nullptr, DDBLT_WAIT, nullptr);
return TRUE;
} }
BOOL CALLBACK bltToWindowViaGdi(HWND hwnd, LPARAM lParam) void bltToWindowViaGdi(Gdi::Region* primaryRegion)
{ {
if (!IsWindowVisible(hwnd) || !Gdi::Window::isPresentationWindow(hwnd)) std::unique_ptr<HDC__, void(*)(HDC)> virtualScreenDc(nullptr, &Gdi::VirtualScreen::deleteDc);
{
return TRUE;
}
auto window = Gdi::Window::get(GetParent(hwnd)); for (auto windowPair : Gdi::Window::getWindows())
if (!window)
{ {
return TRUE; if (!IsWindowVisible(windowPair.first))
} {
continue;
}
Gdi::Region visibleRegion = window->getVisibleRegion(); Gdi::Region visibleRegion = windowPair.second->getVisibleRegion();
if (visibleRegion.isEmpty())
{
return TRUE;
}
auto& args = *reinterpret_cast<BltToWindowViaGdiArgs*>(lParam);
if (args.primaryRegion)
{
visibleRegion -= *args.primaryRegion;
if (visibleRegion.isEmpty()) if (visibleRegion.isEmpty())
{ {
return TRUE; continue;
} }
}
if (!args.virtualScreenDc) if (primaryRegion)
{
args.virtualScreenDc.reset(Gdi::VirtualScreen::createDc());
if (!args.virtualScreenDc)
{ {
return FALSE; visibleRegion -= *primaryRegion;
if (visibleRegion.isEmpty())
{
continue;
}
} }
if (!virtualScreenDc)
{
virtualScreenDc.reset(Gdi::VirtualScreen::createDc());
if (!virtualScreenDc)
{
return;
}
}
Gdi::GdiAccessGuard gdiAccessGuard(Gdi::ACCESS_READ);
HWND presentationWindow = windowPair.second->getPresentationWindow();
HDC dc = GetWindowDC(presentationWindow);
RECT rect = windowPair.second->getWindowRect();
CALL_ORIG_FUNC(BitBlt)(dc, 0, 0, rect.right - rect.left, rect.bottom - rect.top,
virtualScreenDc.get(), rect.left, rect.top, SRCCOPY);
ReleaseDC(presentationWindow, dc);
} }
Gdi::GdiAccessGuard accessGuard(Gdi::ACCESS_READ);
HDC presentationWindowDc = GetWindowDC(hwnd);
RECT rect = window->getWindowRect();
CALL_ORIG_FUNC(BitBlt)(presentationWindowDc, 0, 0, rect.right - rect.left, rect.bottom - rect.top,
args.virtualScreenDc.get(), rect.left, rect.top, SRCCOPY);
ReleaseDC(hwnd, presentationWindowDc);
return TRUE;
} }
void bltVisibleLayeredWindowsToBackBuffer() void bltVisibleLayeredWindowsToBackBuffer()
@ -174,7 +158,7 @@ namespace
{ {
if (!g_isFullScreen) if (!g_isFullScreen)
{ {
EnumThreadWindows(Gdi::getGdiThreadId(), bltToWindow, reinterpret_cast<LPARAM>(&src)); bltToWindow(src);
return; return;
} }
@ -364,19 +348,15 @@ namespace
Gdi::VirtualScreen::update(); Gdi::VirtualScreen::update();
BltToWindowViaGdiArgs bltToWindowViaGdiArgs;
if (!g_frontBuffer || !src || DDraw::RealPrimarySurface::isLost()) if (!g_frontBuffer || !src || DDraw::RealPrimarySurface::isLost())
{ {
EnumThreadWindows(Gdi::getGdiThreadId(), bltToWindowViaGdi, bltToWindowViaGdi(nullptr);
reinterpret_cast<LPARAM>(&bltToWindowViaGdiArgs));
Compat::LogLeave("RealPrimarySurface::presentToPrimaryChain", src.get()) << false; Compat::LogLeave("RealPrimarySurface::presentToPrimaryChain", src.get()) << false;
return; return;
} }
Gdi::Region primaryRegion(D3dDdi::KernelModeThunks::getMonitorRect()); Gdi::Region primaryRegion(D3dDdi::KernelModeThunks::getMonitorRect());
bltToWindowViaGdiArgs.primaryRegion = &primaryRegion; bltToWindowViaGdi(&primaryRegion);
EnumThreadWindows(Gdi::getGdiThreadId(), bltToWindowViaGdi,
reinterpret_cast<LPARAM>(&bltToWindowViaGdiArgs));
Gdi::DDrawAccessGuard accessGuard(Gdi::ACCESS_READ, DDraw::PrimarySurface::isGdiSurface(src.get())); Gdi::DDrawAccessGuard accessGuard(Gdi::ACCESS_READ, DDraw::PrimarySurface::isGdiSurface(src.get()));
if (DDraw::PrimarySurface::getDesc().ddpfPixelFormat.dwRGBBitCount <= 8) if (DDraw::PrimarySurface::getDesc().ddpfPixelFormat.dwRGBBitCount <= 8)

View File

@ -4,7 +4,7 @@
#include "Common/Hook.h" #include "Common/Hook.h"
#include "Common/Log.h" #include "Common/Log.h"
#include "Common/ScopedCriticalSection.h" #include "DDraw/ScopedThreadLock.h"
#include "Gdi/Dc.h" #include "Gdi/Dc.h"
#include "Gdi/DcCache.h" #include "Gdi/DcCache.h"
#include "Gdi/Gdi.h" #include "Gdi/Gdi.h"
@ -24,7 +24,6 @@ namespace
typedef std::unique_ptr<HDC__, void(*)(HDC)> OrigDc; typedef std::unique_ptr<HDC__, void(*)(HDC)> OrigDc;
typedef std::unordered_map<HDC, CompatDc> CompatDcMap; typedef std::unordered_map<HDC, CompatDc> CompatDcMap;
CRITICAL_SECTION g_cs;
CompatDcMap g_origDcToCompatDc; CompatDcMap g_origDcToCompatDc;
thread_local std::vector<OrigDc> g_threadDcs; thread_local std::vector<OrigDc> g_threadDcs;
@ -82,7 +81,7 @@ namespace
void deleteDc(HDC origDc) void deleteDc(HDC origDc)
{ {
Compat::ScopedCriticalSection lock(g_cs); DDraw::ScopedThreadLock lock;
auto it = g_origDcToCompatDc.find(origDc); auto it = g_origDcToCompatDc.find(origDc);
RestoreDC(it->second.dc, it->second.savedState); RestoreDC(it->second.dc, it->second.savedState);
Gdi::DcCache::deleteDc(it->second.dc); Gdi::DcCache::deleteDc(it->second.dc);
@ -110,24 +109,6 @@ namespace
} }
DeleteObject(clipRgn); DeleteObject(clipRgn);
} }
void updateWindow(HWND wnd)
{
auto window = Gdi::Window::get(wnd);
if (!window)
{
return;
}
RECT windowRect = {};
GetWindowRect(wnd, &windowRect);
RECT cachedWindowRect = window->getWindowRect();
if (!EqualRect(&windowRect, &cachedWindowRect))
{
Gdi::Window::updateAll();
}
}
} }
namespace Gdi namespace Gdi
@ -141,7 +122,7 @@ namespace Gdi
return nullptr; return nullptr;
} }
Compat::ScopedCriticalSection lock(g_cs); DDraw::ScopedThreadLock lock;
auto it = g_origDcToCompatDc.find(origDc); auto it = g_origDcToCompatDc.find(origDc);
if (it != g_origDcToCompatDc.end()) if (it != g_origDcToCompatDc.end())
{ {
@ -150,10 +131,15 @@ namespace Gdi
} }
const HWND wnd = CALL_ORIG_FUNC(WindowFromDC)(origDc); const HWND wnd = CALL_ORIG_FUNC(WindowFromDC)(origDc);
const HWND rootWnd = wnd ? GetAncestor(wnd, GA_ROOT) : nullptr; auto rootWnd = wnd ? GetAncestor(wnd, GA_ROOT) : nullptr;
if (rootWnd && GetDesktopWindow() != rootWnd) if (rootWnd && GetDesktopWindow() != rootWnd)
{ {
updateWindow(rootWnd); auto rootWindow(Window::get(rootWnd));
if (!rootWindow)
{
return nullptr;
}
rootWindow->updateWindow();
} }
CompatDc compatDc; CompatDc compatDc;
@ -183,20 +169,15 @@ namespace Gdi
HDC getOrigDc(HDC dc) HDC getOrigDc(HDC dc)
{ {
Compat::ScopedCriticalSection lock(g_cs); DDraw::ScopedThreadLock lock;
const auto it = std::find_if(g_origDcToCompatDc.begin(), g_origDcToCompatDc.end(), const auto it = std::find_if(g_origDcToCompatDc.begin(), g_origDcToCompatDc.end(),
[dc](const CompatDcMap::value_type& compatDc) { return compatDc.second.dc == dc; }); [dc](const CompatDcMap::value_type& compatDc) { return compatDc.second.dc == dc; });
return it != g_origDcToCompatDc.end() ? it->first : dc; return it != g_origDcToCompatDc.end() ? it->first : dc;
} }
void init()
{
InitializeCriticalSection(&g_cs);
}
void releaseDc(HDC origDc) void releaseDc(HDC origDc)
{ {
Compat::ScopedCriticalSection lock(g_cs); DDraw::ScopedThreadLock lock;
auto it = g_origDcToCompatDc.find(origDc); auto it = g_origDcToCompatDc.find(origDc);
if (it == g_origDcToCompatDc.end()) if (it == g_origDcToCompatDc.end())
{ {

View File

@ -10,7 +10,6 @@ namespace Gdi
{ {
HDC getDc(HDC origDc); HDC getDc(HDC origDc);
HDC getOrigDc(HDC dc); HDC getOrigDc(HDC dc);
void init();
void releaseDc(HDC origDc); void releaseDc(HDC origDc);
} }
} }

View File

@ -6,7 +6,9 @@
#include "Gdi/Dc.h" #include "Gdi/Dc.h"
#include "Gdi/DcFunctions.h" #include "Gdi/DcFunctions.h"
#include "Gdi/Gdi.h" #include "Gdi/Gdi.h"
#include "Gdi/Region.h"
#include "Gdi/VirtualScreen.h" #include "Gdi/VirtualScreen.h"
#include "Gdi/Window.h"
#include "Win32/DisplayMode.h" #include "Win32/DisplayMode.h"
namespace namespace
@ -20,7 +22,7 @@ namespace
std::unordered_map<void*, const char*> g_funcNames; std::unordered_map<void*, const char*> g_funcNames;
template <typename OrigFuncPtr, OrigFuncPtr origFunc, typename... Params> template <typename OrigFuncPtr, OrigFuncPtr origFunc, typename... Params>
DWORD getDdLockFlags(Params... params); HDC getDestinationDc(Params... params);
HRGN getWindowRegion(HWND hwnd); HRGN getWindowRegion(HWND hwnd);
@ -85,7 +87,7 @@ namespace
Result result = 0; Result result = 0;
if (hasDisplayDcArg(params...)) if (hasDisplayDcArg(params...))
{ {
const bool isReadOnlyAccess = getDdLockFlags<OrigFuncPtr, origFunc>(params...) & DDLOCK_READONLY; const bool isReadOnlyAccess = !hasDisplayDcArg(getDestinationDc<OrigFuncPtr, origFunc>(params...));
Gdi::GdiAccessGuard accessGuard(isReadOnlyAccess ? Gdi::ACCESS_READ : Gdi::ACCESS_WRITE); Gdi::GdiAccessGuard accessGuard(isReadOnlyAccess ? Gdi::ACCESS_READ : Gdi::ACCESS_WRITE);
result = Compat::getOrigFuncPtr<OrigFuncPtr, origFunc>()(replaceDc(params)...); result = Compat::getOrigFuncPtr<OrigFuncPtr, origFunc>()(replaceDc(params)...);
releaseDc(params...); releaseDc(params...);
@ -102,6 +104,49 @@ namespace
return result; return result;
} }
template <>
BOOL WINAPI compatGdiDcFunc<decltype(&ExtTextOutW), &ExtTextOutW>(
HDC hdc, int x, int y, UINT options, const RECT* lprect, LPCWSTR lpString, UINT c, const INT* lpDx)
{
Compat::LogEnter("ExtTextOutW", hdc, x, y, options, lprect, lpString, c, lpDx);
BOOL result = TRUE;
if (hasDisplayDcArg(hdc))
{
HWND hwnd = CALL_ORIG_FUNC(WindowFromDC)(hdc);
ATOM atom = static_cast<ATOM>(GetClassLong(hwnd, GCW_ATOM));
POINT p = { x, y };
if (Gdi::MENU_ATOM == atom)
{
RedrawWindow(hwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE);
}
else if (GetCurrentThreadId() == GetWindowThreadProcessId(hwnd, nullptr) &&
LPtoDP(hdc, &p, 1) &&
HTMENU == SendMessage(hwnd, WM_NCHITTEST, 0, (p.y << 16) | (p.x & 0xFFFF)))
{
WINDOWINFO wi = {};
GetWindowInfo(hwnd, &wi);
Gdi::Region ncRegion(wi.rcWindow);
ncRegion -= wi.rcClient;
ncRegion.offset(-wi.rcClient.left, -wi.rcClient.top);
RedrawWindow(hwnd, nullptr, ncRegion, RDW_INVALIDATE | RDW_FRAME);
}
else
{
Gdi::GdiAccessGuard accessGuard(Gdi::ACCESS_WRITE);
result = CALL_ORIG_FUNC(ExtTextOutW)(replaceDc(hdc), x, y, options, lprect, lpString, c, lpDx);
releaseDc(hdc);
}
}
else
{
result = CALL_ORIG_FUNC(ExtTextOutW)(hdc, x, y, options, lprect, lpString, c, lpDx);
}
Compat::LogLeave("ExtTextOutW", hdc, x, y, options, lprect, lpString, c, lpDx) << result;
return result;
}
BOOL CALLBACK excludeRgnForOverlappingWindow(HWND hwnd, LPARAM lParam) BOOL CALLBACK excludeRgnForOverlappingWindow(HWND hwnd, LPARAM lParam)
{ {
auto& args = *reinterpret_cast<ExcludeRgnForOverlappingWindowArgs*>(lParam); auto& args = *reinterpret_cast<ExcludeRgnForOverlappingWindowArgs*>(lParam);
@ -129,84 +174,40 @@ namespace
return &compatGdiDcFunc<OrigFuncPtr, origFunc, Result, Params...>; return &compatGdiDcFunc<OrigFuncPtr, origFunc, Result, Params...>;
} }
DWORD getDdLockFlagsBlt(HDC hdcDest, HDC hdcSrc) HDC getFirstDc()
{ {
return hasDisplayDcArg(hdcSrc) && !hasDisplayDcArg(hdcDest) ? DDLOCK_READONLY : 0; return nullptr;
}
template <typename... Params>
HDC getFirstDc(HDC dc, Params...)
{
return dc;
}
template <typename FirstParam, typename... Params>
HDC getFirstDc(FirstParam, Params... params)
{
return getFirstDc(params...);
} }
template <typename OrigFuncPtr, OrigFuncPtr origFunc, typename... Params> template <typename OrigFuncPtr, OrigFuncPtr origFunc, typename... Params>
DWORD getDdLockFlags(Params...) HDC getDestinationDc(Params... params)
{ {
return 0; return getFirstDc(params...);
} }
template <> template <>
DWORD getDdLockFlags<decltype(&AlphaBlend), &AlphaBlend>( HDC getDestinationDc<decltype(&GetDIBits), &GetDIBits>(
HDC hdcDest, int, int, int, int, HDC hdcSrc, int, int, int, int, BLENDFUNCTION)
{
return getDdLockFlagsBlt(hdcDest, hdcSrc);
}
template <>
DWORD getDdLockFlags<decltype(&BitBlt), &BitBlt>(
HDC hdcDest, int, int, int, int, HDC hdcSrc, int, int, DWORD)
{
return getDdLockFlagsBlt(hdcDest, hdcSrc);
}
template <>
DWORD getDdLockFlags<decltype(&GdiAlphaBlend), &GdiAlphaBlend>(
HDC hdcDest, int, int, int, int, HDC hdcSrc, int, int, int, int, BLENDFUNCTION)
{
return getDdLockFlagsBlt(hdcDest, hdcSrc);
}
template <>
DWORD getDdLockFlags<decltype(&GdiTransparentBlt), &GdiTransparentBlt >(
HDC hdcDest, int, int, int, int, HDC hdcSrc, int, int, int, int, UINT)
{
return getDdLockFlagsBlt(hdcDest, hdcSrc);
}
template <>
DWORD getDdLockFlags<decltype(&GetDIBits), &GetDIBits>(
HDC, HBITMAP, UINT, UINT, LPVOID, LPBITMAPINFO, UINT) HDC, HBITMAP, UINT, UINT, LPVOID, LPBITMAPINFO, UINT)
{ {
return DDLOCK_READONLY; return nullptr;
} }
template <> template <>
DWORD getDdLockFlags<decltype(&GetPixel), &GetPixel>(HDC, int, int) HDC getDestinationDc<decltype(&GetPixel), &GetPixel>(HDC, int, int)
{ {
return DDLOCK_READONLY; return nullptr;
}
template <>
DWORD getDdLockFlags<decltype(&MaskBlt), &MaskBlt>(
HDC hdcDest, int, int, int, int, HDC hdcSrc, int, int, HBITMAP, int, int, DWORD)
{
return getDdLockFlagsBlt(hdcDest, hdcSrc);
}
template <>
DWORD getDdLockFlags<decltype(&PlgBlt), &PlgBlt>(
HDC hdcDest, const POINT*, HDC hdcSrc, int, int, int, int, HBITMAP, int, int)
{
return getDdLockFlagsBlt(hdcDest, hdcSrc);
}
template <>
DWORD getDdLockFlags<decltype(&StretchBlt), &StretchBlt>(
HDC hdcDest, int, int, int, int, HDC hdcSrc, int, int, int, int, DWORD)
{
return getDdLockFlagsBlt(hdcDest, hdcSrc);
}
template <>
DWORD getDdLockFlags<decltype(&TransparentBlt), &TransparentBlt>(
HDC hdcDest, int, int, int, int, HDC hdcSrc, int, int, int, int, UINT)
{
return getDdLockFlagsBlt(hdcDest, hdcSrc);
} }
HRGN getWindowRegion(HWND hwnd) HRGN getWindowRegion(HWND hwnd)

View File

@ -1,6 +1,5 @@
#include "DDraw/Surfaces/PrimarySurface.h" #include "DDraw/Surfaces/PrimarySurface.h"
#include "Gdi/Caret.h" #include "Gdi/Caret.h"
#include "Gdi/Dc.h"
#include "Gdi/DcFunctions.h" #include "Gdi/DcFunctions.h"
#include "Gdi/Gdi.h" #include "Gdi/Gdi.h"
#include "Gdi/PaintHandlers.h" #include "Gdi/PaintHandlers.h"
@ -37,20 +36,11 @@ namespace Gdi
return DcFunctions::getVisibleWindowRgn(hwnd); return DcFunctions::getVisibleWindowRgn(hwnd);
} }
void hookWndProc(LPCSTR className, WNDPROC &oldWndProc, WNDPROC newWndProc)
{
HWND hwnd = CreateWindow(className, nullptr, 0, 0, 0, 0, 0, nullptr, nullptr, nullptr, 0);
oldWndProc = reinterpret_cast<WNDPROC>(
SetClassLongPtr(hwnd, GCLP_WNDPROC, reinterpret_cast<LONG>(newWndProc)));
DestroyWindow(hwnd);
}
void installHooks() void installHooks()
{ {
g_gdiThreadId = GetCurrentThreadId(); g_gdiThreadId = GetCurrentThreadId();
g_screenDc = GetDC(nullptr); g_screenDc = GetDC(nullptr);
Gdi::Dc::init();
Gdi::DcFunctions::installHooks(); Gdi::DcFunctions::installHooks();
Gdi::PaintHandlers::installHooks(); Gdi::PaintHandlers::installHooks();
Gdi::ScrollFunctions::installHooks(); Gdi::ScrollFunctions::installHooks();

View File

@ -6,12 +6,13 @@
namespace Gdi namespace Gdi
{ {
const ATOM MENU_ATOM = 0x8000;
typedef void(*WindowPosChangeNotifyFunc)(); typedef void(*WindowPosChangeNotifyFunc)();
DWORD getGdiThreadId(); DWORD getGdiThreadId();
HDC getScreenDc(); HDC getScreenDc();
HRGN getVisibleWindowRgn(HWND hwnd); HRGN getVisibleWindowRgn(HWND hwnd);
void hookWndProc(LPCSTR className, WNDPROC &oldWndProc, WNDPROC newWndProc);
void installHooks(); void installHooks();
void redraw(HRGN rgn); void redraw(HRGN rgn);
void redrawWindow(HWND hwnd, HRGN rgn); void redrawWindow(HWND hwnd, HRGN rgn);

View File

@ -1,3 +1,5 @@
#include <vector>
#include "Common/Hook.h" #include "Common/Hook.h"
#include "Common/Log.h" #include "Common/Log.h"
#include "DDraw/RealPrimarySurface.h" #include "DDraw/RealPrimarySurface.h"
@ -8,56 +10,130 @@
#include "Gdi/ScrollBar.h" #include "Gdi/ScrollBar.h"
#include "Gdi/ScrollFunctions.h" #include "Gdi/ScrollFunctions.h"
#include "Gdi/TitleBar.h" #include "Gdi/TitleBar.h"
#include "Gdi/VirtualScreen.h"
#include "Gdi/Window.h"
#include "Win32/Registry.h" #include "Win32/Registry.h"
namespace namespace
{ {
LRESULT WINAPI defPaintProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, typedef LRESULT(*WndProcHook)(HWND, UINT, WPARAM, LPARAM, WNDPROC);
WNDPROC origWndProc, const char* origWndProcName);
struct User32WndProc
{
WNDPROC oldWndProcTrampoline;
WNDPROC oldWndProc;
WNDPROC newWndProc;
std::string name;
User32WndProc()
: oldWndProcTrampoline(nullptr)
, oldWndProc(nullptr)
, newWndProc(nullptr)
{
}
};
LRESULT defPaintProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, WNDPROC origWndProc);
LRESULT defPaintProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, WNDPROC origWndProc,
const char* origWndProcName);
LRESULT onEraseBackground(HWND hwnd, HDC dc, WNDPROC origWndProc); LRESULT onEraseBackground(HWND hwnd, HDC dc, WNDPROC origWndProc);
LRESULT onMenuPaint(HWND hwnd, WNDPROC origWndProc);
LRESULT onNcPaint(HWND hwnd, WPARAM wParam, WNDPROC origWndProc); LRESULT onNcPaint(HWND hwnd, WPARAM wParam, WNDPROC origWndProc);
LRESULT onPaint(HWND hwnd, WNDPROC origWndProc); LRESULT onPaint(HWND hwnd, WNDPROC origWndProc);
LRESULT onPrint(HWND hwnd, UINT msg, HDC dc, LONG flags, WNDPROC origWndProc); LRESULT onPrint(HWND hwnd, UINT msg, HDC dc, LONG flags, WNDPROC origWndProc);
WNDPROC g_origButtonWndProc = nullptr; HHOOK g_cbtProcHook = nullptr;
WNDPROC g_origComboListBoxWndProc = nullptr; int g_menuWndProcIndex = 0;
WNDPROC g_origEditWndProc = nullptr; int g_scrollBarWndProcIndex = 0;
WNDPROC g_origListBoxWndProc = nullptr;
WNDPROC g_origMenuWndProc = nullptr;
WNDPROC g_origScrollBarWndProc = nullptr;
LRESULT WINAPI buttonWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) std::vector<User32WndProc> g_user32WndProcA;
std::vector<User32WndProc> g_user32WndProcW;
std::vector<WndProcHook> g_user32WndProcHook;
LRESULT buttonWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, WNDPROC origWndProc)
{ {
Compat::LogEnter("buttonWndProc", hwnd, msg, wParam, lParam);
LRESULT result = 0;
switch (msg) switch (msg)
{ {
case WM_PAINT: case WM_PAINT:
result = onPaint(hwnd, g_origButtonWndProc); return onPaint(hwnd, origWndProc);
break;
case WM_ENABLE: case WM_ENABLE:
case WM_SETTEXT: case WM_SETTEXT:
case BM_SETCHECK: case BM_SETCHECK:
case BM_SETSTATE: case BM_SETSTATE:
result = CallWindowProc(g_origButtonWndProc, hwnd, msg, wParam, lParam); {
LRESULT result = CallWindowProc(origWndProc, hwnd, msg, wParam, lParam);
RedrawWindow(hwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_UPDATENOW); RedrawWindow(hwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_UPDATENOW);
break; return result;
default:
result = CallWindowProc(g_origButtonWndProc, hwnd, msg, wParam, lParam);
break;
} }
Compat::LogLeave("buttonWndProc", hwnd, msg, wParam, lParam) << result; default:
return CallWindowProc(origWndProc, hwnd, msg, wParam, lParam);
}
}
LRESULT CALLBACK cbtProc(int nCode, WPARAM wParam, LPARAM lParam)
{
Compat::LogEnter("cbtProc", Compat::hex(nCode), Compat::hex(wParam), Compat::hex(lParam));
LRESULT result = 0;
if (nCode < 0)
{
result = CallNextHookEx(nullptr, nCode, wParam, lParam);
}
else if (HCBT_CREATEWND == nCode)
{
HWND hwnd = reinterpret_cast<HWND>(wParam);
WNDPROC wndProcA = reinterpret_cast<WNDPROC>(GetWindowLongA(hwnd, GWL_WNDPROC));
WNDPROC wndProcW = reinterpret_cast<WNDPROC>(GetWindowLongW(hwnd, GWL_WNDPROC));
int index = -1;
if (wndProcA == g_user32WndProcA[g_menuWndProcIndex].oldWndProc ||
wndProcW == g_user32WndProcW[g_menuWndProcIndex].oldWndProc)
{
index = g_menuWndProcIndex;
}
else if (wndProcA == g_user32WndProcA[g_scrollBarWndProcIndex].oldWndProc ||
wndProcW == g_user32WndProcW[g_scrollBarWndProcIndex].oldWndProc)
{
index = g_scrollBarWndProcIndex;
}
if (-1 != index)
{
if (IsWindowUnicode(hwnd))
{
CALL_ORIG_FUNC(SetWindowLongW)(hwnd, GWL_WNDPROC,
reinterpret_cast<LONG>(g_user32WndProcW[index].newWndProc));
}
else
{
CALL_ORIG_FUNC(SetWindowLongA)(hwnd, GWL_WNDPROC,
reinterpret_cast<LONG>(g_user32WndProcA[index].newWndProc));
}
}
}
Compat::LogLeave("cbtProc", Compat::hex(nCode), Compat::hex(wParam), Compat::hex(lParam)) << result;
return result; return result;
} }
LRESULT WINAPI comboListBoxWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) LRESULT comboBoxWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, WNDPROC origWndProc)
{ {
return defPaintProc(hwnd, msg, wParam, lParam, g_origComboListBoxWndProc, "comboListBoxWndProc"); return defPaintProc(hwnd, msg, wParam, lParam, origWndProc);
}
LRESULT comboListBoxWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, WNDPROC origWndProc)
{
LRESULT result = defPaintProc(hwnd, msg, wParam, lParam, origWndProc);
switch (msg)
{
case WM_NCPAINT:
CallWindowProc(origWndProc, hwnd, msg, wParam, lParam);
break;
}
return result;
} }
LRESULT WINAPI defDlgProcA(HWND hdlg, UINT msg, WPARAM wParam, LPARAM lParam) LRESULT WINAPI defDlgProcA(HWND hdlg, UINT msg, WPARAM wParam, LPARAM lParam)
@ -70,38 +146,31 @@ namespace
return defPaintProc(hdlg, msg, wParam, lParam, CALL_ORIG_FUNC(DefDlgProcW), "defDlgProcW"); return defPaintProc(hdlg, msg, wParam, lParam, CALL_ORIG_FUNC(DefDlgProcW), "defDlgProcW");
} }
LRESULT WINAPI defPaintProc( LRESULT defPaintProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, WNDPROC origWndProc)
HWND hwnd,
UINT msg,
WPARAM wParam,
LPARAM lParam,
WNDPROC origWndProc,
const char* origWndProcName)
{ {
Compat::LogEnter(origWndProcName, hwnd, msg, wParam, lParam);
LRESULT result = 0;
switch (msg) switch (msg)
{ {
case WM_ERASEBKGND: case WM_ERASEBKGND:
result = onEraseBackground(hwnd, reinterpret_cast<HDC>(wParam), origWndProc); return onEraseBackground(hwnd, reinterpret_cast<HDC>(wParam), origWndProc);
break;
case WM_NCPAINT: case WM_NCPAINT:
result = onNcPaint(hwnd, wParam, origWndProc); return onNcPaint(hwnd, wParam, origWndProc);
break;
case WM_PRINT: case WM_PRINT:
case WM_PRINTCLIENT: case WM_PRINTCLIENT:
result = onPrint(hwnd, msg, reinterpret_cast<HDC>(wParam), lParam, origWndProc); return onPrint(hwnd, msg, reinterpret_cast<HDC>(wParam), lParam, origWndProc);
break;
default: default:
result = CallWindowProc(origWndProc, hwnd, msg, wParam, lParam); return CallWindowProc(origWndProc, hwnd, msg, wParam, lParam);
break;
} }
}
Compat::LogLeave(origWndProcName, hwnd, msg, wParam, lParam) << result; LRESULT defPaintProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, WNDPROC origWndProc,
const char* origWndProcName)
{
Compat::LogEnter(origWndProcName, hwnd, Compat::hex(msg), Compat::hex(wParam), Compat::hex(lParam));
LRESULT result = defPaintProc(hwnd, msg, wParam, lParam, origWndProc);
Compat::LogLeave(origWndProcName, hwnd, Compat::hex(msg), Compat::hex(wParam), Compat::hex(lParam)) << result;
return result; return result;
} }
@ -141,9 +210,9 @@ namespace
0); 0);
} }
LRESULT WINAPI editWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) LRESULT editWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, WNDPROC origWndProc)
{ {
LRESULT result = defPaintProc(hwnd, msg, wParam, lParam, g_origEditWndProc, "editWndProc"); LRESULT result = defPaintProc(hwnd, msg, wParam, lParam, origWndProc);
if (0 == result && (WM_HSCROLL == msg || WM_VSCROLL == msg)) if (0 == result && (WM_HSCROLL == msg || WM_VSCROLL == msg))
{ {
Gdi::ScrollFunctions::updateScrolledWindow(hwnd); Gdi::ScrollFunctions::updateScrolledWindow(hwnd);
@ -151,37 +220,88 @@ namespace
return result; return result;
} }
LRESULT WINAPI listBoxWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) void hookUser32WndProc(const std::string& wndProcName, HWND hwnd, WNDPROC newWndProc,
decltype(GetWindowLongPtr)* getWindowLong, std::vector<User32WndProc>& user32WndProc)
{ {
return defPaintProc(hwnd, msg, wParam, lParam, g_origListBoxWndProc, "listBoxWndProc"); User32WndProc wndProc;
wndProc.oldWndProc =
reinterpret_cast<WNDPROC>(getWindowLong(hwnd, GWL_WNDPROC));
wndProc.oldWndProcTrampoline = wndProc.oldWndProc;
wndProc.newWndProc = newWndProc;
wndProc.name = wndProcName;
user32WndProc.push_back(wndProc);
if (reinterpret_cast<DWORD>(wndProc.oldWndProcTrampoline) < 0xFFFF0000)
{
Compat::hookFunction(
reinterpret_cast<void*&>(user32WndProc.back().oldWndProcTrampoline), newWndProc);
}
} }
LRESULT WINAPI menuWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) void hookUser32WndProcA(const char* className, WNDPROC newWndProc, const std::string& wndProcName)
{ {
Compat::LogEnter("menuWndProc", hwnd, msg, wParam, lParam); CLIENTCREATESTRUCT ccs = {};
LRESULT result = 0; HWND hwnd = CreateWindowA(className, "", 0, 0, 0, 0, 0, 0, 0, 0, &ccs);
hookUser32WndProc(wndProcName + 'A', hwnd, newWndProc, CALL_ORIG_FUNC(GetWindowLongA), g_user32WndProcA);
DestroyWindow(hwnd);
}
void hookUser32WndProcW(const char* name, WNDPROC newWndProc, const std::string& wndProcName)
{
CLIENTCREATESTRUCT ccs = {};
HWND hwnd = CreateWindowW(
std::wstring(name, name + std::strlen(name)).c_str(), L"", 0, 0, 0, 0, 0, 0, 0, 0, &ccs);
hookUser32WndProc(wndProcName + 'W', hwnd, newWndProc, CALL_ORIG_FUNC(GetWindowLongW), g_user32WndProcW);
DestroyWindow(hwnd);
}
LRESULT listBoxWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, WNDPROC origWndProc)
{
return defPaintProc(hwnd, msg, wParam, lParam, origWndProc);
}
LRESULT mdiClientWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, WNDPROC origWndProc)
{
return defPaintProc(hwnd, msg, wParam, lParam, origWndProc);
}
LRESULT menuWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, WNDPROC origWndProc)
{
switch (msg) switch (msg)
{ {
case WM_NCPAINT:
CallWindowProc(origWndProc, hwnd, msg, wParam, lParam);
return onNcPaint(hwnd, wParam, origWndProc);
case WM_PAINT: case WM_PAINT:
result = onMenuPaint(hwnd, g_origMenuWndProc); return onPaint(hwnd, origWndProc);
break;
case WM_PRINTCLIENT:
{
RECT r = {};
GetClientRect(hwnd, &r);
HDC dc = CreateCompatibleDC(nullptr);
HBITMAP dib = Gdi::VirtualScreen::createOffScreenDib(r.right, r.bottom);
HGDIOBJ origBitmap = SelectObject(dc, dib);
CallWindowProc(origWndProc, hwnd, WM_ERASEBKGND, reinterpret_cast<WPARAM>(dc), 0);
LRESULT result = CallWindowProc(origWndProc, hwnd, msg, reinterpret_cast<WPARAM>(dc), lParam);
CALL_ORIG_FUNC(BitBlt)(reinterpret_cast<HDC>(wParam), 0, 0, r.right, r.bottom, dc, 0, 0, SRCCOPY);
SelectObject(dc, origBitmap);
DeleteObject(dib);
DeleteDC(dc);
return result;
}
case 0x1e5: case 0x1e5:
if (-1 == wParam) if (-1 == wParam)
{ {
// Clearing of selection is not caught by WM_MENUSELECT when mouse leaves menu window RedrawWindow(hwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE);
RedrawWindow(hwnd, nullptr, nullptr, RDW_INVALIDATE);
} }
// fall through to default return CallWindowProc(origWndProc, hwnd, msg, wParam, lParam);
default: default:
result = CallWindowProc(g_origMenuWndProc, hwnd, msg, wParam, lParam); return defPaintProc(hwnd, msg, wParam, lParam, origWndProc);
break;
} }
Compat::LogLeave("menuWndProc", hwnd, msg, wParam, lParam) << result;
return result;
} }
LRESULT onEraseBackground(HWND hwnd, HDC dc, WNDPROC origWndProc) LRESULT onEraseBackground(HWND hwnd, HDC dc, WNDPROC origWndProc)
@ -202,32 +322,6 @@ namespace
return CallWindowProc(origWndProc, hwnd, WM_ERASEBKGND, reinterpret_cast<WPARAM>(dc), 0); return CallWindowProc(origWndProc, hwnd, WM_ERASEBKGND, reinterpret_cast<WPARAM>(dc), 0);
} }
LRESULT onMenuPaint(HWND hwnd, WNDPROC origWndProc)
{
if (!hwnd)
{
return CallWindowProc(origWndProc, hwnd, WM_PAINT, 0, 0);
}
HDC dc = GetWindowDC(hwnd);
HDC compatDc = Gdi::Dc::getDc(dc);
if (compatDc)
{
Gdi::GdiAccessGuard accessGuard(Gdi::ACCESS_WRITE);
CallWindowProc(origWndProc, hwnd, WM_PRINT, reinterpret_cast<WPARAM>(compatDc),
PRF_NONCLIENT | PRF_ERASEBKGND | PRF_CLIENT);
ValidateRect(hwnd, nullptr);
Gdi::Dc::releaseDc(dc);
}
else
{
CallWindowProc(origWndProc, hwnd, WM_PAINT, 0, 0);
}
ReleaseDC(hwnd, dc);
return 0;
}
LRESULT onNcPaint(HWND hwnd, WPARAM wParam, WNDPROC origWndProc) LRESULT onNcPaint(HWND hwnd, WPARAM wParam, WNDPROC origWndProc)
{ {
if (!hwnd) if (!hwnd)
@ -249,7 +343,7 @@ namespace
scrollBar.drawAll(); scrollBar.drawAll();
scrollBar.excludeFromClipRegion(); scrollBar.excludeFromClipRegion();
SendMessage(hwnd, WM_PRINT, reinterpret_cast<WPARAM>(compatDc), PRF_NONCLIENT); CallWindowProc(origWndProc, hwnd, WM_PRINT, reinterpret_cast<WPARAM>(compatDc), PRF_NONCLIENT);
Gdi::Dc::releaseDc(windowDc); Gdi::Dc::releaseDc(windowDc);
} }
@ -265,6 +359,7 @@ namespace
return CallWindowProc(origWndProc, hwnd, WM_PAINT, 0, 0); return CallWindowProc(origWndProc, hwnd, WM_PAINT, 0, 0);
} }
DDraw::ScopedThreadLock lock;
PAINTSTRUCT paint = {}; PAINTSTRUCT paint = {};
HDC dc = BeginPaint(hwnd, &paint); HDC dc = BeginPaint(hwnd, &paint);
HDC compatDc = Gdi::Dc::getDc(dc); HDC compatDc = Gdi::Dc::getDc(dc);
@ -303,33 +398,52 @@ namespace
return result; return result;
} }
LRESULT WINAPI scrollBarWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) LRESULT scrollBarWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, WNDPROC origWndProc)
{ {
Compat::LogEnter("scrollBarWndProc", hwnd, msg, wParam, lParam);
LRESULT result = 0;
switch (msg) switch (msg)
{ {
case WM_PAINT: case WM_PAINT:
result = onPaint(hwnd, g_origScrollBarWndProc); return onPaint(hwnd, origWndProc);
break;
case WM_SETCURSOR: case WM_SETCURSOR:
if (GetWindowLong(hwnd, GWL_STYLE) & (SBS_SIZEBOX | SBS_SIZEGRIP)) if (GetWindowLong(hwnd, GWL_STYLE) & (SBS_SIZEBOX | SBS_SIZEGRIP))
{ {
SetCursor(LoadCursor(nullptr, IDC_SIZENWSE)); SetCursor(LoadCursor(nullptr, IDC_SIZENWSE));
} }
result = TRUE; return TRUE;
break;
default: default:
result = CallWindowProc(g_origScrollBarWndProc, hwnd, msg, wParam, lParam); return CallWindowProc(origWndProc, hwnd, msg, wParam, lParam);
break;
} }
}
Compat::LogLeave("scrollBarWndProc", hwnd, msg, wParam, lParam) << result; LRESULT staticWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam, WNDPROC origWndProc)
{
return defPaintProc(hwnd, msg, wParam, lParam, origWndProc);
}
LRESULT CALLBACK user32WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam,
const User32WndProc& user32WndProc, WndProcHook wndProcHook)
{
Compat::LogEnter(user32WndProc.name.c_str(),
hwnd, Compat::hex(uMsg), Compat::hex(wParam), Compat::hex(lParam));
LRESULT result = wndProcHook(hwnd, uMsg, wParam, lParam, user32WndProc.oldWndProcTrampoline);
Compat::LogLeave(user32WndProc.name.c_str(),
hwnd, Compat::hex(uMsg), Compat::hex(wParam), Compat::hex(lParam)) << result;
return result; return result;
} }
template <int index>
LRESULT CALLBACK user32WndProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
return user32WndProc(hwnd, uMsg, wParam, lParam, g_user32WndProcA[index], g_user32WndProcHook[index]);
}
template <int index>
LRESULT CALLBACK user32WndProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
return user32WndProc(hwnd, uMsg, wParam, lParam, g_user32WndProcW[index], g_user32WndProcHook[index]);
}
} }
namespace Gdi namespace Gdi
@ -340,27 +454,40 @@ namespace Gdi
{ {
disableImmersiveContextMenus(); disableImmersiveContextMenus();
Gdi::hookWndProc("Button", g_origButtonWndProc, &buttonWndProc); #define HOOK_USER32_WNDPROC(index, name, wndProcHook) \
Gdi::hookWndProc("ComboLBox", g_origComboListBoxWndProc, &comboListBoxWndProc); g_user32WndProcHook.push_back(wndProcHook); \
Gdi::hookWndProc("Edit", g_origEditWndProc, &editWndProc); hookUser32WndProcA(name, user32WndProcA<index>, #wndProcHook); \
Gdi::hookWndProc("ListBox", g_origListBoxWndProc, &listBoxWndProc); hookUser32WndProcW(name, user32WndProcW<index>, #wndProcHook)
Gdi::hookWndProc("#32768", g_origMenuWndProc, &menuWndProc);
Gdi::hookWndProc("ScrollBar", g_origScrollBarWndProc, &scrollBarWndProc); g_user32WndProcA.reserve(9);
g_user32WndProcW.reserve(9);
HOOK_USER32_WNDPROC(0, "Button", buttonWndProc);
HOOK_USER32_WNDPROC(1, "ComboBox", comboBoxWndProc);
HOOK_USER32_WNDPROC(2, "Edit", editWndProc);
HOOK_USER32_WNDPROC(3, "ListBox", listBoxWndProc);
HOOK_USER32_WNDPROC(4, "MDIClient", mdiClientWndProc);
HOOK_USER32_WNDPROC(5, "ScrollBar", scrollBarWndProc);
HOOK_USER32_WNDPROC(6, "Static", staticWndProc);
HOOK_USER32_WNDPROC(7, "ComboLBox", comboListBoxWndProc);
HOOK_USER32_WNDPROC(8, "#32768", menuWndProc);
g_scrollBarWndProcIndex = 5;
g_menuWndProcIndex = 8;
#undef HOOK_USER32_WNDPROC
HOOK_FUNCTION(user32, DefWindowProcA, defWindowProcA); HOOK_FUNCTION(user32, DefWindowProcA, defWindowProcA);
HOOK_FUNCTION(user32, DefWindowProcW, defWindowProcW); HOOK_FUNCTION(user32, DefWindowProcW, defWindowProcW);
HOOK_FUNCTION(user32, DefDlgProcA, defDlgProcA); HOOK_FUNCTION(user32, DefDlgProcA, defDlgProcA);
HOOK_FUNCTION(user32, DefDlgProcW, defDlgProcW); HOOK_FUNCTION(user32, DefDlgProcW, defDlgProcW);
g_cbtProcHook = SetWindowsHookEx(WH_CBT, cbtProc, nullptr, Gdi::getGdiThreadId());
} }
void uninstallHooks() void uninstallHooks()
{ {
Gdi::unhookWndProc("Button", g_origButtonWndProc); UnhookWindowsHookEx(g_cbtProcHook);
Gdi::unhookWndProc("ComboLBox", g_origComboListBoxWndProc);
Gdi::unhookWndProc("Edit", g_origEditWndProc);
Gdi::unhookWndProc("ListBox", g_origListBoxWndProc);
Gdi::unhookWndProc("#32768", g_origMenuWndProc);
Gdi::unhookWndProc("ScrollBar", g_origScrollBarWndProc);
} }
} }
} }

View File

@ -1,8 +1,9 @@
#include "Common/Hook.h" #include "Common/Hook.h"
#include "Common/Log.h" #include "Common/Log.h"
#include "DDraw/RealPrimarySurface.h" #include "DDraw/ScopedThreadLock.h"
#include "Gdi/Gdi.h" #include "Gdi/Gdi.h"
#include "Gdi/ScrollFunctions.h" #include "Gdi/ScrollFunctions.h"
#include "Gdi/Window.h"
namespace namespace
{ {
@ -55,10 +56,14 @@ namespace Gdi
void updateScrolledWindow(HWND hwnd) void updateScrolledWindow(HWND hwnd)
{ {
DDraw::RealPrimarySurface::disableUpdates(); DDraw::ScopedThreadLock lock;
RedrawWindow(hwnd, nullptr, nullptr, auto window(Gdi::Window::get(hwnd));
RDW_ERASE | RDW_FRAME | RDW_INVALIDATE | RDW_NOCHILDREN | RDW_UPDATENOW); UINT flags = RDW_ERASE | RDW_INVALIDATE | RDW_NOCHILDREN | RDW_UPDATENOW;
DDraw::RealPrimarySurface::enableUpdates(); if (!window || window->getPresentationWindow() != hwnd)
{
flags |= RDW_FRAME;
}
RedrawWindow(hwnd, nullptr, nullptr, flags);
} }
} }
} }

View File

@ -32,6 +32,37 @@ namespace
virtualScreenRegion |= monitorRegion; virtualScreenRegion |= monitorRegion;
return TRUE; return TRUE;
} }
HBITMAP createDibSection(DWORD width, DWORD height, HANDLE section)
{
struct BITMAPINFO256 : public BITMAPINFO
{
RGBQUAD bmiRemainingColors[255];
};
BITMAPINFO256 bmi = {};
bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader);
bmi.bmiHeader.biWidth = width;
bmi.bmiHeader.biHeight = -static_cast<LONG>(height);
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = static_cast<WORD>(g_bpp);
bmi.bmiHeader.biCompression = 8 == g_bpp ? BI_RGB : BI_BITFIELDS;
if (8 == g_bpp)
{
memcpy(bmi.bmiColors, g_systemPalette, sizeof(g_systemPalette));
}
else
{
const auto pf = DDraw::getRgbPixelFormat(g_bpp);
reinterpret_cast<DWORD&>(bmi.bmiColors[0]) = pf.dwRBitMask;
reinterpret_cast<DWORD&>(bmi.bmiColors[1]) = pf.dwGBitMask;
reinterpret_cast<DWORD&>(bmi.bmiColors[2]) = pf.dwBBitMask;
}
void* bits = nullptr;
return CreateDIBSection(nullptr, &bmi, DIB_RGB_COLORS, &bits, section, 0);
}
} }
namespace Gdi namespace Gdi
@ -74,34 +105,13 @@ namespace Gdi
{ {
return nullptr; return nullptr;
} }
return createDibSection(g_width, g_height, g_surfaceFileMapping);
}
struct BITMAPINFO256 : public BITMAPINFO HBITMAP createOffScreenDib(DWORD width, DWORD height)
{ {
RGBQUAD bmiRemainingColors[255]; Compat::ScopedCriticalSection lock(g_cs);
}; return createDibSection(width, height, nullptr);
BITMAPINFO256 bmi = {};
bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader);
bmi.bmiHeader.biWidth = g_width;
bmi.bmiHeader.biHeight = -g_height;
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = static_cast<WORD>(g_bpp);
bmi.bmiHeader.biCompression = 8 == g_bpp ? BI_RGB : BI_BITFIELDS;
if (8 == g_bpp)
{
memcpy(bmi.bmiColors, g_systemPalette, sizeof(g_systemPalette));
}
else
{
const auto pf = DDraw::getRgbPixelFormat(g_bpp);
reinterpret_cast<DWORD&>(bmi.bmiColors[0]) = pf.dwRBitMask;
reinterpret_cast<DWORD&>(bmi.bmiColors[1]) = pf.dwGBitMask;
reinterpret_cast<DWORD&>(bmi.bmiColors[2]) = pf.dwBBitMask;
}
void* bits = nullptr;
return CreateDIBSection(nullptr, &bmi, DIB_RGB_COLORS, &bits, g_surfaceFileMapping, 0);
} }
CompatPtr<IDirectDrawSurface7> createSurface(const RECT& rect) CompatPtr<IDirectDrawSurface7> createSurface(const RECT& rect)

View File

@ -14,6 +14,7 @@ namespace Gdi
{ {
HDC createDc(); HDC createDc();
HBITMAP createDib(); HBITMAP createDib();
HBITMAP createOffScreenDib(DWORD width, DWORD height);
CompatPtr<IDirectDrawSurface7> createSurface(const RECT& rect); CompatPtr<IDirectDrawSurface7> createSurface(const RECT& rect);
void deleteDc(HDC dc); void deleteDc(HDC dc);

View File

@ -27,14 +27,13 @@ namespace
void onActivate(HWND hwnd); void onActivate(HWND hwnd);
void onCreateWindow(HWND hwnd); void onCreateWindow(HWND hwnd);
void onDestroyWindow(HWND hwnd); void onDestroyWindow(HWND hwnd);
void onMenuSelect();
void onWindowPosChanged(HWND hwnd); void onWindowPosChanged(HWND hwnd);
void removeDropShadow(HWND hwnd); void removeDropShadow(HWND hwnd);
LRESULT CALLBACK callWndRetProc(int nCode, WPARAM wParam, LPARAM lParam) LRESULT CALLBACK callWndRetProc(int nCode, WPARAM wParam, LPARAM lParam)
{ {
auto ret = reinterpret_cast<CWPRETSTRUCT*>(lParam); auto ret = reinterpret_cast<CWPRETSTRUCT*>(lParam);
Compat::LogEnter("callWndRetProc", nCode, wParam, ret); Compat::LogEnter("callWndRetProc", Compat::hex(nCode), Compat::hex(wParam), ret);
if (HC_ACTION == nCode) if (HC_ACTION == nCode)
{ {
@ -62,14 +61,10 @@ namespace
Gdi::ScrollFunctions::updateScrolledWindow(reinterpret_cast<HWND>(ret->lParam)); Gdi::ScrollFunctions::updateScrolledWindow(reinterpret_cast<HWND>(ret->lParam));
} }
} }
else if (WM_MENUSELECT == ret->message)
{
onMenuSelect();
}
} }
LRESULT result = CallNextHookEx(nullptr, nCode, wParam, lParam); LRESULT result = CallNextHookEx(nullptr, nCode, wParam, lParam);
Compat::LogLeave("callWndRetProc", nCode, wParam, ret) << result; Compat::LogLeave("callWndRetProc", Compat::hex(nCode), Compat::hex(wParam), ret) << result;
return result; return result;
} }
@ -95,12 +90,6 @@ namespace
return TRUE; return TRUE;
} }
bool isTopLevelNonLayeredWindow(HWND hwnd)
{
return !(GetWindowLongPtr(hwnd, GWL_EXSTYLE) & WS_EX_LAYERED) &&
(!(GetWindowLongPtr(hwnd, GWL_STYLE) & WS_CHILD) || GetParent(hwnd) == GetDesktopWindow());
}
void CALLBACK objectStateChangeEvent( void CALLBACK objectStateChangeEvent(
HWINEVENTHOOK /*hWinEventHook*/, HWINEVENTHOOK /*hWinEventHook*/,
DWORD /*event*/, DWORD /*event*/,
@ -160,12 +149,9 @@ namespace
void onCreateWindow(HWND hwnd) void onCreateWindow(HWND hwnd)
{ {
if (isTopLevelNonLayeredWindow(hwnd)) disableDwmAttributes(hwnd);
{ removeDropShadow(hwnd);
disableDwmAttributes(hwnd); Gdi::Window::add(hwnd);
removeDropShadow(hwnd);
Gdi::Window::add(hwnd);
}
} }
void onDestroyWindow(HWND hwnd) void onDestroyWindow(HWND hwnd)
@ -173,20 +159,9 @@ namespace
Gdi::Window::remove(hwnd); Gdi::Window::remove(hwnd);
} }
void onMenuSelect()
{
HWND menuWindow = FindWindow(reinterpret_cast<LPCSTR>(0x8000), nullptr);
while (menuWindow)
{
RedrawWindow(menuWindow, nullptr, nullptr, RDW_INVALIDATE);
menuWindow = FindWindowEx(nullptr, menuWindow, reinterpret_cast<LPCSTR>(0x8000), nullptr);
}
}
void onWindowPosChanged(HWND hwnd) void onWindowPosChanged(HWND hwnd)
{ {
const ATOM menuAtom = 0x8000; if (Gdi::MENU_ATOM == GetClassLongPtr(hwnd, GCW_ATOM))
if (menuAtom == GetClassLongPtr(hwnd, GCW_ATOM))
{ {
SetWindowLongPtr(hwnd, GWL_EXSTYLE, GetWindowLongPtr(hwnd, GWL_EXSTYLE) & ~WS_EX_LAYERED); SetWindowLongPtr(hwnd, GWL_EXSTYLE, GetWindowLongPtr(hwnd, GWL_EXSTYLE) & ~WS_EX_LAYERED);
} }
@ -196,7 +171,7 @@ namespace
notifyFunc(); notifyFunc();
} }
if (isTopLevelNonLayeredWindow(hwnd)) if (Gdi::Window::get(hwnd))
{ {
Gdi::Window::updateAll(); Gdi::Window::updateAll();
} }

View File

@ -1,3 +1,4 @@
#include "DDraw/ScopedThreadLock.h"
#include "Gdi/Gdi.h" #include "Gdi/Gdi.h"
#include "Gdi/VirtualScreen.h" #include "Gdi/VirtualScreen.h"
#include "Gdi/Window.h" #include "Gdi/Window.h"
@ -8,6 +9,13 @@ namespace
{ {
ATOM registerPresentationWindowClass(); ATOM registerPresentationWindowClass();
ATOM getComboLBoxAtom()
{
WNDCLASS wc = {};
static ATOM comboLBoxAtom = static_cast<ATOM>(GetClassInfo(nullptr, "ComboLBox", &wc));
return comboLBoxAtom;
}
ATOM getPresentationWindowClassAtom() ATOM getPresentationWindowClassAtom()
{ {
static ATOM atom = registerPresentationWindowClass(); static ATOM atom = registerPresentationWindowClass();
@ -33,37 +41,43 @@ namespace Gdi
{ {
Window::Window(HWND hwnd) Window::Window(HWND hwnd)
: m_hwnd(hwnd) : m_hwnd(hwnd)
, m_presentationWindow(nullptr)
, m_windowRect{ 0, 0, 0, 0 } , m_windowRect{ 0, 0, 0, 0 }
, m_isUpdating(false) , m_isUpdating(false)
{ {
m_presentationWindow = CreateWindowEx( const ATOM atom = static_cast<ATOM>(GetClassLong(hwnd, GCW_ATOM));
WS_EX_LAYERED | WS_EX_TRANSPARENT, if (MENU_ATOM != atom && getComboLBoxAtom() != atom)
reinterpret_cast<const char*>(getPresentationWindowClassAtom()), {
nullptr, m_presentationWindow = CreateWindowEx(
WS_DISABLED | WS_POPUP, WS_EX_LAYERED | WS_EX_TRANSPARENT,
0, 0, 1, 1, reinterpret_cast<const char*>(getPresentationWindowClassAtom()),
m_hwnd, nullptr,
nullptr, WS_DISABLED | WS_POPUP,
nullptr, 0, 0, 1, 1,
nullptr); m_hwnd,
SetLayeredWindowAttributes(m_presentationWindow, 0, 255, LWA_ALPHA); nullptr,
nullptr,
nullptr);
SetLayeredWindowAttributes(m_presentationWindow, 0, 255, LWA_ALPHA);
}
update(); update();
} }
Window* Window::add(HWND hwnd) bool Window::add(HWND hwnd)
{ {
auto it = s_windows.find(hwnd); const bool isTopLevelNonLayeredWindow = !(GetWindowLongPtr(hwnd, GWL_EXSTYLE) & WS_EX_LAYERED) &&
if (it != s_windows.end()) (!(GetWindowLongPtr(hwnd, GWL_STYLE) & WS_CHILD) || GetParent(hwnd) == GetDesktopWindow() ||
getComboLBoxAtom() == GetClassLong(hwnd, GCW_ATOM));
if (isTopLevelNonLayeredWindow && !get(hwnd) &&
GetClassLong(hwnd, GCW_ATOM) != getPresentationWindowClassAtom())
{ {
return &it->second; s_windows.emplace(hwnd, std::make_shared<Window>(hwnd));
return true;
} }
if (isPresentationWindow(hwnd)) return false;
{
return nullptr;
}
return &s_windows.emplace(hwnd, hwnd).first->second;
} }
void Window::calcInvalidatedRegion(const RECT& oldWindowRect, const Region& oldVisibleRegion) void Window::calcInvalidatedRegion(const RECT& oldWindowRect, const Region& oldVisibleRegion)
@ -97,22 +111,36 @@ namespace Gdi
} }
} }
Window* Window::get(HWND hwnd) std::shared_ptr<Window> Window::get(HWND hwnd)
{ {
DDraw::ScopedThreadLock lock;
auto it = s_windows.find(hwnd); auto it = s_windows.find(hwnd);
return it != s_windows.end() ? &it->second : nullptr; return it != s_windows.end() ? it->second : nullptr;
}
HWND Window::getPresentationWindow() const
{
return m_presentationWindow ? m_presentationWindow : m_hwnd;
} }
Region Window::getVisibleRegion() const Region Window::getVisibleRegion() const
{ {
DDraw::ScopedThreadLock lock;
return m_visibleRegion; return m_visibleRegion;
} }
RECT Window::getWindowRect() const RECT Window::getWindowRect() const
{ {
DDraw::ScopedThreadLock lock;
return m_windowRect; return m_windowRect;
} }
std::map<HWND, std::shared_ptr<Window>> Window::getWindows()
{
DDraw::ScopedThreadLock lock;
return s_windows;
}
bool Window::isPresentationWindow(HWND hwnd) bool Window::isPresentationWindow(HWND hwnd)
{ {
return GetClassLong(hwnd, GCW_ATOM) == getPresentationWindowClassAtom(); return GetClassLong(hwnd, GCW_ATOM) == getPresentationWindowClassAtom();
@ -120,11 +148,13 @@ namespace Gdi
void Window::remove(HWND hwnd) void Window::remove(HWND hwnd)
{ {
DDraw::ScopedThreadLock lock;
s_windows.erase(hwnd); s_windows.erase(hwnd);
} }
void Window::update() void Window::update()
{ {
DDraw::ScopedThreadLock lock;
if (m_isUpdating) if (m_isUpdating)
{ {
return; return;
@ -145,9 +175,16 @@ namespace Gdi
newVisibleRegion &= VirtualScreen::getRegion(); newVisibleRegion &= VirtualScreen::getRegion();
} }
SetWindowPos(m_presentationWindow, nullptr, newWindowRect.left, newWindowRect.top, if (m_presentationWindow)
newWindowRect.right - newWindowRect.left, newWindowRect.bottom - newWindowRect.top, {
SWP_SHOWWINDOW | SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOACTIVATE | SWP_NOSENDCHANGING | SWP_NOREDRAW); SetWindowPos(m_presentationWindow, nullptr, newWindowRect.left, newWindowRect.top,
newWindowRect.right - newWindowRect.left, newWindowRect.bottom - newWindowRect.top,
SWP_SHOWWINDOW | SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOACTIVATE | SWP_NOSENDCHANGING | SWP_NOREDRAW);
}
}
else if (m_presentationWindow)
{
ShowWindow(m_presentationWindow, SW_HIDE);
} }
std::swap(m_windowRect, newWindowRect); std::swap(m_windowRect, newWindowRect);
@ -160,23 +197,34 @@ namespace Gdi
void Window::updateAll() void Window::updateAll()
{ {
for (auto& windowPair : s_windows) auto windows(getWindows());
for (auto& windowPair : windows)
{ {
windowPair.second.update(); windowPair.second->update();
} }
for (auto& windowPair : s_windows) for (auto& windowPair : windows)
{ {
if (!windowPair.second.m_invalidatedRegion.isEmpty()) if (!windowPair.second->m_invalidatedRegion.isEmpty())
{ {
POINT clientOrigin = {}; POINT clientOrigin = {};
ClientToScreen(windowPair.first, &clientOrigin); ClientToScreen(windowPair.first, &clientOrigin);
windowPair.second.m_invalidatedRegion.offset(-clientOrigin.x, -clientOrigin.y); windowPair.second->m_invalidatedRegion.offset(-clientOrigin.x, -clientOrigin.y);
RedrawWindow(windowPair.first, nullptr, windowPair.second.m_invalidatedRegion, RedrawWindow(windowPair.first, nullptr, windowPair.second->m_invalidatedRegion,
RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_ALLCHILDREN | RDW_ERASENOW); RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_ALLCHILDREN | RDW_ERASENOW);
} }
} }
} }
std::map<HWND, Window> Window::s_windows; 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

@ -3,6 +3,7 @@
#define WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN
#include <map> #include <map>
#include <memory>
#include <Windows.h> #include <Windows.h>
@ -17,13 +18,16 @@ namespace Gdi
Window(const Window&) = delete; Window(const Window&) = delete;
Window& operator=(const Window&) = delete; Window& operator=(const Window&) = delete;
HWND getPresentationWindow() const;
Region getVisibleRegion() const; Region getVisibleRegion() const;
RECT getWindowRect() const; RECT getWindowRect() const;
void updateWindow();
static Window* add(HWND hwnd); static bool add(HWND hwnd);
static Window* get(HWND hwnd); static std::shared_ptr<Window> get(HWND hwnd);
static void remove(HWND hwnd); static void remove(HWND hwnd);
static std::map<HWND, std::shared_ptr<Window>> getWindows();
static bool isPresentationWindow(HWND hwnd); static bool isPresentationWindow(HWND hwnd);
static void updateAll(); static void updateAll();
@ -38,6 +42,6 @@ namespace Gdi
Region m_invalidatedRegion; Region m_invalidatedRegion;
bool m_isUpdating; bool m_isUpdating;
static std::map<HWND, Window> s_windows; static std::map<HWND, std::shared_ptr<Window>> s_windows;
}; };
} }

View File

@ -30,7 +30,7 @@ namespace
const int subKeyComp = lstrcmpiW(subKey.c_str(), rhs.subKey.c_str()); const int subKeyComp = lstrcmpiW(subKey.c_str(), rhs.subKey.c_str());
if (subKeyComp < 0) { return true; } if (subKeyComp < 0) { return true; }
if (subKeyComp > 0) { return false; } if (subKeyComp > 0) { return false; }
return lstrcmpiW(value.c_str(), rhs.value.c_str()); return lstrcmpiW(value.c_str(), rhs.value.c_str()) < 0;
} }
bool operator==(const RegistryKey& rhs) const bool operator==(const RegistryKey& rhs) const