mirror of
https://github.com/narzoul/DDrawCompat
synced 2024-12-30 08:55:36 +01:00
1026 lines
28 KiB
C++
1026 lines
28 KiB
C++
#include <map>
|
|
#include <set>
|
|
|
|
#include <Windows.h>
|
|
#include <Windowsx.h>
|
|
#include <dwmapi.h>
|
|
|
|
#include <Common/Hook.h>
|
|
#include <Common/Log.h>
|
|
#include <Common/ScopedSrwLock.h>
|
|
#include <Common/ScopedThreadPriority.h>
|
|
#include <Common/Time.h>
|
|
#include <Config/Settings/FpsLimiter.h>
|
|
#include <Config/Settings/RemoveBorders.h>
|
|
#include <Dll/Dll.h>
|
|
#include <DDraw/DirectDraw.h>
|
|
#include <DDraw/RealPrimarySurface.h>
|
|
#include <DDraw/Surfaces/PrimarySurface.h>
|
|
#include <DDraw/Surfaces/TagSurface.h>
|
|
#include <Gdi/CompatDc.h>
|
|
#include <Gdi/Cursor.h>
|
|
#include <Gdi/Dc.h>
|
|
#include <Gdi/Gdi.h>
|
|
#include <Gdi/GuiThread.h>
|
|
#include <Gdi/PresentationWindow.h>
|
|
#include <Gdi/ScrollBar.h>
|
|
#include <Gdi/ScrollFunctions.h>
|
|
#include <Gdi/TitleBar.h>
|
|
#include <Gdi/Window.h>
|
|
#include <Gdi/WinProc.h>
|
|
#include <Overlay/ConfigWindow.h>
|
|
#include <Overlay/StatsWindow.h>
|
|
#include <Win32/DisplayMode.h>
|
|
#include <Win32/DpiAwareness.h>
|
|
|
|
namespace
|
|
{
|
|
class ScopedIncrement
|
|
{
|
|
public:
|
|
ScopedIncrement(unsigned& num) : m_num(num) { ++m_num; }
|
|
~ScopedIncrement() { --m_num; }
|
|
|
|
private:
|
|
unsigned& m_num;
|
|
};
|
|
|
|
struct WindowProc
|
|
{
|
|
WNDPROC wndProcA;
|
|
WNDPROC wndProcW;
|
|
WNDPROC ddrawOrigWndProc;
|
|
};
|
|
|
|
decltype(&DwmSetIconicThumbnail) g_dwmSetIconicThumbnail = nullptr;
|
|
|
|
std::map<HMENU, UINT> g_menuMaxHeight;
|
|
std::set<Gdi::WindowPosChangeNotifyFunc> g_windowPosChangeNotifyFuncs;
|
|
|
|
Compat::SrwLock g_windowProcSrwLock;
|
|
std::map<HWND, WindowProc> g_windowProc;
|
|
|
|
thread_local POINT* g_cursorPos = nullptr;
|
|
thread_local unsigned g_inCreateDialog = 0;
|
|
thread_local unsigned g_inMessageBox = 0;
|
|
thread_local unsigned g_inWindowProc = 0;
|
|
thread_local long long g_qpcWaitEnd = 0;
|
|
thread_local bool g_isFrameStarted = false;
|
|
thread_local bool g_waiting = false;
|
|
|
|
void dwmSendIconicThumbnail(HWND hwnd, LONG width, LONG height);
|
|
WindowProc getWindowProc(HWND hwnd);
|
|
bool isUser32ScrollBar(HWND hwnd);
|
|
void onDestroyWindow(HWND hwnd);
|
|
void onGetMinMaxInfo(MINMAXINFO& mmi);
|
|
void onInitDialog(HWND hwnd);
|
|
void onInitMenuPopup(HMENU menu);
|
|
void onUninitMenuPopup(HMENU menu);
|
|
void onWindowPosChanged(HWND hwnd, const WINDOWPOS& wp);
|
|
void onWindowPosChanging(HWND hwnd, WINDOWPOS& wp);
|
|
LONG WINAPI setWindowLongA(HWND hWnd, int nIndex, LONG dwNewLong);
|
|
void setWindowProc(HWND hwnd, WNDPROC wndProcA, WNDPROC wndProcW);
|
|
|
|
BOOL WINAPI animateWindow(HWND hWnd, DWORD dwTime, DWORD dwFlags)
|
|
{
|
|
LOG_FUNC("AnimateWindow", hWnd, dwTime, Compat::hex(dwFlags));
|
|
if (dwFlags & AW_BLEND)
|
|
{
|
|
dwFlags &= ~AW_BLEND;
|
|
dwFlags |= AW_SLIDE | AW_HOR_POSITIVE;
|
|
}
|
|
return LOG_RESULT(CALL_ORIG_FUNC(AnimateWindow)(hWnd, dwTime, dwFlags));
|
|
}
|
|
|
|
template <auto func, typename Result, typename... Params>
|
|
Result WINAPI createDialog(Params... params)
|
|
{
|
|
LOG_FUNC(Compat::g_origFuncName<func>.c_str(), params...);
|
|
++g_inCreateDialog;
|
|
Result result = CALL_ORIG_FUNC(func)(params...);
|
|
--g_inCreateDialog;
|
|
return LOG_RESULT(result);
|
|
}
|
|
|
|
LRESULT CALLBACK ddcWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam,
|
|
decltype(&CallWindowProcA) callWindowProc, WNDPROC wndProc)
|
|
{
|
|
LOG_FUNC("ddcWindowProc", Compat::WindowMessageStruct(hwnd, uMsg, wParam, lParam));
|
|
ScopedIncrement inc(g_inWindowProc);
|
|
POINT cursorPos = {};
|
|
|
|
switch (uMsg)
|
|
{
|
|
case WM_DISPLAYCHANGE:
|
|
if (0 != wParam)
|
|
{
|
|
return 0;
|
|
}
|
|
wParam = Win32::DisplayMode::getBpp();
|
|
break;
|
|
|
|
case WM_DWMCOMPOSITIONCHANGED:
|
|
Gdi::checkDesktopComposition();
|
|
break;
|
|
|
|
case WM_DWMSENDICONICTHUMBNAIL:
|
|
dwmSendIconicThumbnail(hwnd, HIWORD(lParam), LOWORD(lParam));
|
|
return 0;
|
|
|
|
case WM_GETMINMAXINFO:
|
|
onGetMinMaxInfo(*reinterpret_cast<MINMAXINFO*>(lParam));
|
|
break;
|
|
|
|
case WM_INITDIALOG:
|
|
onInitDialog(hwnd);
|
|
break;
|
|
|
|
case WM_MOUSEMOVE:
|
|
if (1 == g_inWindowProc)
|
|
{
|
|
cursorPos = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
|
|
ClientToScreen(hwnd, &cursorPos);
|
|
g_cursorPos = &cursorPos;
|
|
}
|
|
break;
|
|
|
|
case WM_SYNCPAINT:
|
|
if (Gdi::Window::isTopLevelWindow(hwnd))
|
|
{
|
|
RECT emptyRect = {};
|
|
RedrawWindow(hwnd, &emptyRect, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_ERASENOW);
|
|
return 0;
|
|
}
|
|
break;
|
|
|
|
case WM_UNINITMENUPOPUP:
|
|
onUninitMenuPopup(reinterpret_cast<HMENU>(wParam));
|
|
break;
|
|
|
|
case WM_WINDOWPOSCHANGED:
|
|
onWindowPosChanged(hwnd, *reinterpret_cast<WINDOWPOS*>(lParam));
|
|
break;
|
|
}
|
|
|
|
LRESULT result = callWindowProc(wndProc, hwnd, uMsg, wParam, lParam);
|
|
|
|
switch (uMsg)
|
|
{
|
|
case WM_ACTIVATEAPP:
|
|
Gdi::GuiThread::execute([&]()
|
|
{
|
|
static bool hidden = false;
|
|
static bool configVisible = false;
|
|
static bool statsVisible = false;
|
|
|
|
auto configWindow = Gdi::GuiThread::getConfigWindow();
|
|
auto statsWindow = Gdi::GuiThread::getStatsWindow();
|
|
if (!wParam && !hidden)
|
|
{
|
|
configVisible = configWindow ? configWindow->isVisible() : false;
|
|
statsVisible = statsWindow ? statsWindow->isVisible() : false;
|
|
hidden = true;
|
|
}
|
|
|
|
if (configWindow)
|
|
{
|
|
configWindow->setVisible(wParam ? configVisible : false);
|
|
}
|
|
if (statsWindow)
|
|
{
|
|
statsWindow->setVisible(wParam ? statsVisible : false);
|
|
}
|
|
|
|
if (wParam)
|
|
{
|
|
hidden = false;
|
|
}
|
|
else
|
|
{
|
|
CALL_ORIG_FUNC(ClipCursor)(nullptr);
|
|
}
|
|
});
|
|
break;
|
|
|
|
case WM_CTLCOLORSCROLLBAR:
|
|
if (reinterpret_cast<HWND>(lParam) != hwnd &&
|
|
isUser32ScrollBar(reinterpret_cast<HWND>(lParam)))
|
|
{
|
|
Gdi::ScrollBar::onCtlColorScrollBar(hwnd, wParam, lParam, result);
|
|
}
|
|
break;
|
|
|
|
case WM_INITMENUPOPUP:
|
|
onInitMenuPopup(reinterpret_cast<HMENU>(wParam));
|
|
break;
|
|
|
|
case WM_MOUSEMOVE:
|
|
if (1 == g_inWindowProc)
|
|
{
|
|
g_cursorPos = nullptr;
|
|
}
|
|
break;
|
|
|
|
case WM_NCDESTROY:
|
|
onDestroyWindow(hwnd);
|
|
break;
|
|
|
|
case WM_SETCURSOR:
|
|
SetCursor(GetCursor());
|
|
break;
|
|
|
|
case WM_STYLECHANGED:
|
|
if (Gdi::Window::isTopLevelWindow(hwnd))
|
|
{
|
|
Gdi::Window::onStyleChanged(hwnd, wParam);
|
|
}
|
|
break;
|
|
|
|
case WM_WINDOWPOSCHANGING:
|
|
onWindowPosChanging(hwnd, *reinterpret_cast<WINDOWPOS*>(lParam));
|
|
break;
|
|
}
|
|
|
|
return LOG_RESULT(result);
|
|
}
|
|
|
|
LRESULT CALLBACK ddcWindowProcA(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
return ddcWindowProc(hwnd, uMsg, wParam, lParam, CallWindowProcA, getWindowProc(hwnd).wndProcA);
|
|
}
|
|
|
|
LRESULT CALLBACK ddcWindowProcW(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
return ddcWindowProc(hwnd, uMsg, wParam, lParam, CallWindowProcW, getWindowProc(hwnd).wndProcW);
|
|
}
|
|
|
|
LONG WINAPI ddrawSetWindowLongA(HWND hWnd, int nIndex, LONG dwNewLong)
|
|
{
|
|
LOG_FUNC("ddrawSetWindowLongA", hWnd, nIndex, dwNewLong);
|
|
if (GWL_WNDPROC == nIndex)
|
|
{
|
|
auto origWndProc = setWindowLongA(hWnd, GWL_WNDPROC, dwNewLong);
|
|
if (Dll::g_origDDrawModule == Compat::getModuleHandleFromAddress(reinterpret_cast<void*>(dwNewLong)))
|
|
{
|
|
Compat::ScopedSrwLockExclusive lock(g_windowProcSrwLock);
|
|
auto it = g_windowProc.find(hWnd);
|
|
if (it != g_windowProc.end())
|
|
{
|
|
it->second.ddrawOrigWndProc = reinterpret_cast<WNDPROC>(origWndProc);
|
|
}
|
|
DDraw::DirectDraw::hookDDrawWindowProc(reinterpret_cast<WNDPROC>(dwNewLong));
|
|
}
|
|
return origWndProc;
|
|
}
|
|
|
|
if (GWL_STYLE == nIndex)
|
|
{
|
|
auto style = CALL_ORIG_FUNC(GetWindowLongA)(hWnd, GWL_STYLE);
|
|
if (style & WS_CLIPCHILDREN)
|
|
{
|
|
dwNewLong = style | WS_CLIPCHILDREN;
|
|
if (dwNewLong == style)
|
|
{
|
|
return LOG_RESULT(style);
|
|
}
|
|
}
|
|
}
|
|
|
|
return LOG_RESULT(CALL_ORIG_FUNC(SetWindowLongA)(hWnd, nIndex, dwNewLong));
|
|
}
|
|
|
|
void dwmSendIconicThumbnail(HWND hwnd, LONG width, LONG height)
|
|
{
|
|
auto presentationWindow = Gdi::Window::getPresentationWindow(hwnd);
|
|
if (!presentationWindow || !g_dwmSetIconicThumbnail)
|
|
{
|
|
return;
|
|
}
|
|
|
|
BITMAPINFO bmi = {};
|
|
bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader);
|
|
bmi.bmiHeader.biWidth = width;
|
|
bmi.bmiHeader.biHeight = -height;
|
|
bmi.bmiHeader.biPlanes = 1;
|
|
bmi.bmiHeader.biBitCount = 32;
|
|
bmi.bmiHeader.biCompression = BI_RGB;
|
|
void* bits = nullptr;
|
|
HBITMAP bmp = CreateDIBSection(nullptr, &bmi, DIB_RGB_COLORS, &bits, nullptr, 0);
|
|
if (!bmp)
|
|
{
|
|
return;
|
|
}
|
|
|
|
HDC srcDc = GetWindowDC(presentationWindow);
|
|
HDC dstDc = CreateCompatibleDC(nullptr);
|
|
auto prevBmp = SelectBitmap(dstDc, bmp);
|
|
|
|
RECT srcRect = {};
|
|
GetWindowRect(presentationWindow, &srcRect);
|
|
|
|
SetStretchBltMode(dstDc, HALFTONE);
|
|
CALL_ORIG_FUNC(StretchBlt)(dstDc, 0, 0, width, height,
|
|
srcDc, 0, 0, srcRect.right - srcRect.left, srcRect.bottom - srcRect.top, SRCCOPY);
|
|
|
|
SelectBitmap(dstDc, prevBmp);
|
|
DeleteDC(dstDc);
|
|
ReleaseDC(presentationWindow, srcDc);
|
|
|
|
Win32::ScopedDpiAwareness dpiAwareness;
|
|
g_dwmSetIconicThumbnail(hwnd, bmp, 0);
|
|
DeleteObject(bmp);
|
|
}
|
|
|
|
BOOL WINAPI getCursorPos(LPPOINT lpPoint)
|
|
{
|
|
if (lpPoint && g_cursorPos)
|
|
{
|
|
*lpPoint = *g_cursorPos;
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL result = CALL_ORIG_FUNC(GetCursorPos)(lpPoint);
|
|
if (result)
|
|
{
|
|
Gdi::Cursor::clip(*lpPoint);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
BOOL WINAPI getMessage(LPMSG lpMsg, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilterMax,
|
|
decltype(&GetMessageA) origGetMessage)
|
|
{
|
|
DDraw::RealPrimarySurface::setUpdateReady();
|
|
Gdi::Cursor::clip();
|
|
return origGetMessage(lpMsg, hWnd, wMsgFilterMin, wMsgFilterMax);
|
|
}
|
|
|
|
BOOL WINAPI getMessageA(LPMSG lpMsg, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilterMax)
|
|
{
|
|
return getMessage(lpMsg, hWnd, wMsgFilterMin, wMsgFilterMax, CALL_ORIG_FUNC(GetMessageA));
|
|
}
|
|
|
|
BOOL WINAPI getMessageW(LPMSG lpMsg, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilterMax)
|
|
{
|
|
return getMessage(lpMsg, hWnd, wMsgFilterMin, wMsgFilterMax, CALL_ORIG_FUNC(GetMessageW));
|
|
}
|
|
|
|
int WINAPI getRandomRgn(HDC hdc, HRGN hrgn, INT i)
|
|
{
|
|
LOG_FUNC("GetRandomRgn", hdc, hrgn, i);
|
|
return LOG_RESULT(Gdi::Window::getRandomRgn(hdc, hrgn, i));
|
|
}
|
|
|
|
LONG getWindowLong(HWND hWnd, int nIndex,
|
|
decltype(&GetWindowLongA) origGetWindowLong, WNDPROC(WindowProc::* wndProc))
|
|
{
|
|
if (GWL_WNDPROC == nIndex)
|
|
{
|
|
Compat::ScopedSrwLockShared lock(g_windowProcSrwLock);
|
|
auto it = g_windowProc.find(hWnd);
|
|
if (it != g_windowProc.end())
|
|
{
|
|
return reinterpret_cast<LONG>(it->second.*wndProc);
|
|
}
|
|
}
|
|
return origGetWindowLong(hWnd, nIndex);
|
|
}
|
|
|
|
LONG WINAPI getWindowLongA(HWND hWnd, int nIndex)
|
|
{
|
|
LOG_FUNC("GetWindowLongA", hWnd, nIndex);
|
|
return LOG_RESULT(getWindowLong(hWnd, nIndex, CALL_ORIG_FUNC(GetWindowLongA), &WindowProc::wndProcA));
|
|
}
|
|
|
|
LONG WINAPI getWindowLongW(HWND hWnd, int nIndex)
|
|
{
|
|
LOG_FUNC("GetWindowLongW", hWnd, nIndex);
|
|
return LOG_RESULT(getWindowLong(hWnd, nIndex, CALL_ORIG_FUNC(GetWindowLongW), &WindowProc::wndProcW));
|
|
}
|
|
|
|
WindowProc getWindowProc(HWND hwnd)
|
|
{
|
|
Compat::ScopedSrwLockShared lock(g_windowProcSrwLock);
|
|
auto it = g_windowProc.find(hwnd);
|
|
return it != g_windowProc.end() ? it->second : WindowProc{};
|
|
}
|
|
|
|
template <auto func, typename... Params>
|
|
int WINAPI messageBox(Params... params)
|
|
{
|
|
LOG_FUNC(Compat::g_origFuncName<func>.c_str(), params...);
|
|
++g_inMessageBox;
|
|
int result = CALL_ORIG_FUNC(func)(params...);
|
|
--g_inMessageBox;
|
|
return LOG_RESULT(result);
|
|
}
|
|
|
|
void onInitDialog(HWND hwnd)
|
|
{
|
|
if (!Gdi::Window::isTopLevelWindow(hwnd) ||
|
|
0 == g_inMessageBox &&
|
|
(0 == g_inCreateDialog || !(CALL_ORIG_FUNC(GetWindowLongA)(hwnd, GWL_STYLE) & DS_CENTER)))
|
|
{
|
|
return;
|
|
}
|
|
|
|
auto mi = Win32::DisplayMode::getMonitorInfo(hwnd);
|
|
|
|
if (!EqualRect(&mi.rcEmulated, &mi.rcMonitor))
|
|
{
|
|
RECT wr = {};
|
|
GetWindowRect(hwnd, &wr);
|
|
const LONG width = wr.right - wr.left;
|
|
const LONG height = wr.bottom - wr.top;
|
|
|
|
if (0 == g_inMessageBox)
|
|
{
|
|
mi.rcWork.right = mi.rcWork.left + mi.rcEmulated.right - mi.rcEmulated.left;
|
|
mi.rcWork.bottom = mi.rcWork.top + mi.rcEmulated.bottom - mi.rcEmulated.top;
|
|
}
|
|
|
|
const RECT& mr = 0 == g_inMessageBox ? mi.rcWork : mi.rcEmulated;
|
|
const LONG left = (mr.left + mr.right - width) / 2;
|
|
const LONG top = (mr.top + mr.bottom - height) / 2;
|
|
|
|
CALL_ORIG_FUNC(SetWindowPos)(hwnd, nullptr, left, top, width, height,
|
|
SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSENDCHANGING);
|
|
}
|
|
}
|
|
|
|
bool isUser32ScrollBar(HWND hwnd)
|
|
{
|
|
WNDCLASS wc = {};
|
|
static const ATOM sbAtom = static_cast<ATOM>(GetClassInfo(nullptr, "ScrollBar", &wc));
|
|
if (sbAtom != GetClassLong(hwnd, GCW_ATOM))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
auto it = g_windowProc.find(hwnd);
|
|
if (it == g_windowProc.end())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return GetModuleHandle("comctl32") != Compat::getModuleHandleFromAddress(
|
|
IsWindowUnicode(hwnd) ? it->second.wndProcW : it->second.wndProcA);
|
|
}
|
|
|
|
void onDestroyWindow(HWND hwnd)
|
|
{
|
|
if (Gdi::Window::isTopLevelWindow(hwnd))
|
|
{
|
|
Gdi::Window::destroyWindow(hwnd);
|
|
Gdi::GuiThread::deleteTaskbarTab(hwnd);
|
|
return;
|
|
}
|
|
|
|
Compat::ScopedSrwLockExclusive lock(g_windowProcSrwLock);
|
|
auto it = g_windowProc.find(hwnd);
|
|
if (it != g_windowProc.end())
|
|
{
|
|
setWindowProc(hwnd, it->second.wndProcA, it->second.wndProcW);
|
|
g_windowProc.erase(it);
|
|
}
|
|
}
|
|
|
|
void onGetMinMaxInfo(MINMAXINFO& mmi)
|
|
{
|
|
const auto& mi = Win32::DisplayMode::getMonitorInfo();
|
|
mmi.ptMaxSize.x = mi.rcEmulated.right - 2 * mmi.ptMaxPosition.x;
|
|
mmi.ptMaxSize.y = mi.rcEmulated.bottom - 2 * mmi.ptMaxPosition.y;
|
|
}
|
|
|
|
void onInitMenuPopup(HMENU menu)
|
|
{
|
|
auto deviceName = Win32::DisplayMode::getEmulatedDisplayMode().deviceName;
|
|
if (deviceName.empty())
|
|
{
|
|
return;
|
|
}
|
|
const RECT& mr = Win32::DisplayMode::getMonitorInfo(deviceName).rcEmulated;
|
|
|
|
MENUINFO mi = {};
|
|
mi.cbSize = sizeof(mi);
|
|
mi.fMask = MIM_MAXHEIGHT;
|
|
GetMenuInfo(menu, &mi);
|
|
|
|
UINT maxHeight = mr.bottom - mr.top;
|
|
if (0 == mi.cyMax || mi.cyMax > maxHeight)
|
|
{
|
|
g_menuMaxHeight[menu] = mi.cyMax;
|
|
mi.cyMax = maxHeight;
|
|
SetMenuInfo(menu, &mi);
|
|
}
|
|
}
|
|
|
|
void onUninitMenuPopup(HMENU menu)
|
|
{
|
|
auto it = g_menuMaxHeight.find(menu);
|
|
if (it != g_menuMaxHeight.end())
|
|
{
|
|
MENUINFO mi = {};
|
|
mi.cbSize = sizeof(mi);
|
|
mi.fMask = MIM_MAXHEIGHT;
|
|
mi.cyMax = it->second;
|
|
SetMenuInfo(menu, &mi);
|
|
g_menuMaxHeight.erase(it);
|
|
}
|
|
}
|
|
|
|
void onWindowPosChanged(HWND hwnd, const WINDOWPOS& wp)
|
|
{
|
|
for (auto notifyFunc : g_windowPosChangeNotifyFuncs)
|
|
{
|
|
notifyFunc();
|
|
}
|
|
|
|
if (Gdi::Window::isTopLevelWindow(hwnd))
|
|
{
|
|
DDraw::RealPrimarySurface::setPresentationWindowTopmost();
|
|
Gdi::Window::updateWindowPos(hwnd);
|
|
|
|
if (g_dwmSetIconicThumbnail)
|
|
{
|
|
const BOOL isIconic = IsIconic(hwnd);
|
|
DwmSetWindowAttribute(hwnd, DWMWA_FORCE_ICONIC_REPRESENTATION, &isIconic, sizeof(isIconic));
|
|
DwmSetWindowAttribute(hwnd, DWMWA_HAS_ICONIC_BITMAP, &isIconic, sizeof(isIconic));
|
|
}
|
|
}
|
|
|
|
if (wp.flags & SWP_FRAMECHANGED)
|
|
{
|
|
RECT r = { -1, -1, 0, 0 };
|
|
RedrawWindow(hwnd, &r, nullptr, RDW_INVALIDATE | RDW_FRAME);
|
|
}
|
|
}
|
|
|
|
void onWindowPosChanging(HWND hwnd, WINDOWPOS& wp)
|
|
{
|
|
if (Gdi::Window::isTopLevelWindow(hwnd))
|
|
{
|
|
wp.flags |= SWP_NOREDRAW;
|
|
}
|
|
else
|
|
{
|
|
wp.flags |= SWP_NOCOPYBITS;
|
|
}
|
|
}
|
|
|
|
BOOL peekMessage(LPMSG lpMsg, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilterMax, UINT wRemoveMsg,
|
|
decltype(&PeekMessageA) origPeekMessage)
|
|
{
|
|
DDraw::RealPrimarySurface::setUpdateReady();
|
|
Gdi::Cursor::clip();
|
|
BOOL result = origPeekMessage(lpMsg, hWnd, wMsgFilterMin, wMsgFilterMax, wRemoveMsg);
|
|
if (!g_isFrameStarted || Config::Settings::FpsLimiter::MSGLOOP != Config::fpsLimiter.get())
|
|
{
|
|
return result;
|
|
}
|
|
|
|
auto qpcNow = Time::queryPerformanceCounter();
|
|
if (qpcNow - g_qpcWaitEnd >= 0)
|
|
{
|
|
if (!g_waiting)
|
|
{
|
|
g_qpcWaitEnd = qpcNow;
|
|
}
|
|
g_isFrameStarted = false;
|
|
g_waiting = false;
|
|
return result;
|
|
}
|
|
|
|
g_waiting = true;
|
|
if (result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
Compat::ScopedThreadPriority prio(THREAD_PRIORITY_TIME_CRITICAL);
|
|
while (Time::qpcToMs(g_qpcWaitEnd - qpcNow) > 0)
|
|
{
|
|
Time::waitForNextTick();
|
|
if (origPeekMessage(lpMsg, hWnd, wMsgFilterMin, wMsgFilterMax, wRemoveMsg))
|
|
{
|
|
return TRUE;
|
|
}
|
|
qpcNow = Time::queryPerformanceCounter();
|
|
}
|
|
|
|
while (g_qpcWaitEnd - qpcNow > 0)
|
|
{
|
|
if (origPeekMessage(lpMsg, hWnd, wMsgFilterMin, wMsgFilterMax, wRemoveMsg))
|
|
{
|
|
return TRUE;
|
|
}
|
|
qpcNow = Time::queryPerformanceCounter();
|
|
}
|
|
|
|
g_isFrameStarted = false;
|
|
g_waiting = false;
|
|
return result;
|
|
}
|
|
|
|
BOOL WINAPI peekMessageA(LPMSG lpMsg, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilterMax, UINT wRemoveMsg)
|
|
{
|
|
return peekMessage(lpMsg, hWnd, wMsgFilterMin, wMsgFilterMax, wRemoveMsg, CALL_ORIG_FUNC(PeekMessageA));
|
|
}
|
|
|
|
BOOL WINAPI peekMessageW(LPMSG lpMsg, HWND hWnd, UINT wMsgFilterMin, UINT wMsgFilterMax, UINT wRemoveMsg)
|
|
{
|
|
return peekMessage(lpMsg, hWnd, wMsgFilterMin, wMsgFilterMax, wRemoveMsg, CALL_ORIG_FUNC(PeekMessageW));
|
|
}
|
|
|
|
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 && DDraw::RealPrimarySurface::isFullscreen())
|
|
{
|
|
DDraw::RealPrimarySurface::scheduleOverlayUpdate();
|
|
}
|
|
return LOG_RESULT(result);
|
|
}
|
|
|
|
LONG setWindowLong(HWND hWnd, int nIndex, LONG dwNewLong,
|
|
decltype(&SetWindowLongA) origSetWindowLong, WNDPROC(WindowProc::* wndProc))
|
|
{
|
|
if (GWL_WNDPROC == nIndex)
|
|
{
|
|
Compat::ScopedSrwLockExclusive lock(g_windowProcSrwLock);
|
|
auto it = g_windowProc.find(hWnd);
|
|
if (it != g_windowProc.end() && 0 != origSetWindowLong(hWnd, nIndex, dwNewLong))
|
|
{
|
|
WNDPROC oldWndProc = it->second.*wndProc;
|
|
it->second.wndProcA = reinterpret_cast<WNDPROC>(CALL_ORIG_FUNC(GetWindowLongA)(hWnd, GWL_WNDPROC));
|
|
it->second.wndProcW = reinterpret_cast<WNDPROC>(CALL_ORIG_FUNC(GetWindowLongW)(hWnd, GWL_WNDPROC));
|
|
WindowProc newWindowProc = { ddcWindowProcA, ddcWindowProcW };
|
|
origSetWindowLong(hWnd, GWL_WNDPROC, reinterpret_cast<LONG>(newWindowProc.*wndProc));
|
|
return reinterpret_cast<LONG>(oldWndProc);
|
|
}
|
|
}
|
|
else if ((GWL_STYLE == nIndex || GWL_EXSTYLE == nIndex) && Config::removeBorders.get())
|
|
{
|
|
auto tagSurface = DDraw::TagSurface::findFullscreenWindow(hWnd);
|
|
if (tagSurface)
|
|
{
|
|
if (GWL_STYLE == nIndex)
|
|
{
|
|
return tagSurface->setWindowStyle(dwNewLong);
|
|
}
|
|
else
|
|
{
|
|
return tagSurface->setWindowExStyle(dwNewLong);
|
|
}
|
|
}
|
|
}
|
|
|
|
return origSetWindowLong(hWnd, nIndex, dwNewLong);
|
|
}
|
|
|
|
LONG WINAPI setWindowLongA(HWND hWnd, int nIndex, LONG dwNewLong)
|
|
{
|
|
LOG_FUNC("SetWindowLongA", hWnd, nIndex, dwNewLong);
|
|
return LOG_RESULT(setWindowLong(hWnd, nIndex, dwNewLong, CALL_ORIG_FUNC(SetWindowLongA), &WindowProc::wndProcA));
|
|
}
|
|
|
|
LONG WINAPI setWindowLongW(HWND hWnd, int nIndex, LONG dwNewLong)
|
|
{
|
|
LOG_FUNC("SetWindowLongW", hWnd, nIndex, dwNewLong);
|
|
return LOG_RESULT(setWindowLong(hWnd, nIndex, dwNewLong, CALL_ORIG_FUNC(SetWindowLongW), &WindowProc::wndProcW));
|
|
}
|
|
|
|
BOOL WINAPI setWindowPos(HWND hWnd, HWND hWndInsertAfter, int X, int Y, int cx, int cy, UINT uFlags)
|
|
{
|
|
LOG_FUNC("SetWindowPos", hWnd, hWndInsertAfter, X, Y, cx, cy, Compat::hex(uFlags));
|
|
|
|
if (uFlags & SWP_NOSENDCHANGING)
|
|
{
|
|
WINDOWPOS wp = {};
|
|
wp.hwnd = hWnd;
|
|
wp.hwndInsertAfter = hWndInsertAfter;
|
|
wp.x = X;
|
|
wp.y = Y;
|
|
wp.cx = cx;
|
|
wp.cy = cy;
|
|
wp.flags = uFlags;
|
|
onWindowPosChanging(hWnd, wp);
|
|
uFlags = wp.flags;
|
|
}
|
|
|
|
if ((uFlags & SWP_NOACTIVATE) && !(uFlags && SWP_NOZORDER) &&
|
|
(HWND_TOP == hWndInsertAfter || HWND_TOPMOST == hWndInsertAfter) &&
|
|
(GetWindowLong(hWnd, GWL_EXSTYLE) & WS_EX_TOPMOST))
|
|
{
|
|
const HWND topmost = DDraw::RealPrimarySurface::getTopmost();
|
|
if (topmost != HWND_TOPMOST)
|
|
{
|
|
hWndInsertAfter = topmost;
|
|
}
|
|
}
|
|
|
|
return LOG_RESULT(CALL_ORIG_FUNC(SetWindowPos)(hWnd, hWndInsertAfter, X, Y, cx, cy, uFlags));
|
|
}
|
|
|
|
void setWindowProc(HWND hwnd, WNDPROC wndProcA, WNDPROC wndProcW)
|
|
{
|
|
if (IsWindowUnicode(hwnd))
|
|
{
|
|
CALL_ORIG_FUNC(SetWindowLongW)(hwnd, GWL_WNDPROC, reinterpret_cast<LONG>(wndProcW));
|
|
}
|
|
else
|
|
{
|
|
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)
|
|
{
|
|
Gdi::Window::updateLayeredWindowInfo(hWnd, hdcSrc, pptSrc,
|
|
(dwFlags & ULW_COLORKEY) ? crKey : CLR_INVALID,
|
|
((dwFlags & ULW_ALPHA) && pblend) ? pblend->SourceConstantAlpha : 255,
|
|
pblend ? pblend->AlphaFormat : 0);
|
|
}
|
|
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->hdcSrc, pULWInfo->pptSrc,
|
|
(pULWInfo->dwFlags & ULW_COLORKEY) ? pULWInfo->crKey : CLR_INVALID,
|
|
((pULWInfo->dwFlags & ULW_ALPHA) && pULWInfo->pblend) ? pULWInfo->pblend->SourceConstantAlpha : 255,
|
|
pULWInfo->pblend ? pULWInfo->pblend->AlphaFormat : 0);
|
|
}
|
|
return LOG_RESULT(result);
|
|
}
|
|
|
|
void CALLBACK winEventProc(
|
|
HWINEVENTHOOK /*hWinEventHook*/,
|
|
DWORD event,
|
|
HWND hwnd,
|
|
LONG idObject,
|
|
LONG /*idChild*/,
|
|
DWORD /*dwEventThread*/,
|
|
DWORD /*dwmsEventTime*/)
|
|
{
|
|
LOG_FUNC("winEventProc", Compat::hex(event), hwnd, idObject);
|
|
|
|
switch (event)
|
|
{
|
|
case EVENT_OBJECT_CREATE:
|
|
if (OBJID_WINDOW == idObject)
|
|
{
|
|
Gdi::WinProc::onCreateWindow(hwnd);
|
|
}
|
|
break;
|
|
|
|
case EVENT_OBJECT_NAMECHANGE:
|
|
if (OBJID_WINDOW == idObject && Gdi::Window::isTopLevelWindow(hwnd) && !Gdi::GuiThread::isGuiThreadWindow(hwnd))
|
|
{
|
|
auto presentationWindow = Gdi::Window::getPresentationWindow(hwnd);
|
|
if (presentationWindow)
|
|
{
|
|
Gdi::Window::updatePresentationWindowText(presentationWindow);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case EVENT_OBJECT_SHOW:
|
|
case EVENT_OBJECT_HIDE:
|
|
if (OBJID_CURSOR == idObject && Gdi::Cursor::isEmulated())
|
|
{
|
|
Gdi::Cursor::setCursor(CALL_ORIG_FUNC(GetCursor)());
|
|
}
|
|
break;
|
|
|
|
case EVENT_OBJECT_STATECHANGE:
|
|
switch (idObject)
|
|
{
|
|
case OBJID_TITLEBAR:
|
|
{
|
|
HDC dc = GetWindowDC(hwnd);
|
|
Gdi::TitleBar(hwnd).drawButtons(dc);
|
|
ReleaseDC(hwnd, dc);
|
|
break;
|
|
}
|
|
|
|
case OBJID_CLIENT:
|
|
if (!isUser32ScrollBar(hwnd))
|
|
{
|
|
break;
|
|
}
|
|
case OBJID_HSCROLL:
|
|
case OBJID_VSCROLL:
|
|
{
|
|
HDC dc = GetWindowDC(hwnd);
|
|
if (OBJID_CLIENT == idObject)
|
|
{
|
|
SendMessage(GetParent(hwnd), WM_CTLCOLORSCROLLBAR,
|
|
reinterpret_cast<WPARAM>(dc), reinterpret_cast<LPARAM>(hwnd));
|
|
}
|
|
else
|
|
{
|
|
DefWindowProc(hwnd, WM_CTLCOLORSCROLLBAR,
|
|
reinterpret_cast<WPARAM>(dc), reinterpret_cast<LPARAM>(hwnd));
|
|
}
|
|
ReleaseDC(hwnd, dc);
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
namespace Gdi
|
|
{
|
|
namespace WinProc
|
|
{
|
|
void dllThreadDetach()
|
|
{
|
|
auto threadId = GetCurrentThreadId();
|
|
Compat::ScopedSrwLockExclusive lock(g_windowProcSrwLock);
|
|
auto it = g_windowProc.begin();
|
|
while (it != g_windowProc.end())
|
|
{
|
|
if (threadId == GetWindowThreadProcessId(it->first, nullptr))
|
|
{
|
|
it = g_windowProc.erase(it);
|
|
}
|
|
else
|
|
{
|
|
++it;
|
|
}
|
|
}
|
|
}
|
|
|
|
WNDPROC getDDrawOrigWndProc(HWND hwnd)
|
|
{
|
|
Compat::ScopedSrwLockShared lock(g_windowProcSrwLock);
|
|
auto it = g_windowProc.find(hwnd);
|
|
return it != g_windowProc.end() ? it->second.ddrawOrigWndProc : nullptr;
|
|
}
|
|
|
|
void installHooks()
|
|
{
|
|
HOOK_FUNCTION(user32, AnimateWindow, animateWindow);
|
|
HOOK_FUNCTION(user32, CreateDialogIndirectParamA, createDialog<CreateDialogIndirectParamA>);
|
|
HOOK_FUNCTION(user32, CreateDialogIndirectParamW, createDialog<CreateDialogIndirectParamW>);
|
|
HOOK_FUNCTION(user32, CreateDialogParamA, createDialog<CreateDialogParamA>);
|
|
HOOK_FUNCTION(user32, CreateDialogParamW, createDialog<CreateDialogParamW>);
|
|
HOOK_FUNCTION(user32, DialogBoxParamA, createDialog<DialogBoxParamA>);
|
|
HOOK_FUNCTION(user32, DialogBoxParamW, createDialog<DialogBoxParamW>);
|
|
HOOK_FUNCTION(user32, DialogBoxIndirectParamA, createDialog<DialogBoxIndirectParamA>);
|
|
HOOK_FUNCTION(user32, DialogBoxIndirectParamW, createDialog<DialogBoxIndirectParamW>);
|
|
HOOK_FUNCTION(user32, GetCursorPos, getCursorPos);
|
|
HOOK_FUNCTION(user32, GetMessageA, getMessageA);
|
|
HOOK_FUNCTION(user32, GetMessageW, getMessageW);
|
|
HOOK_FUNCTION(gdi32, GetRandomRgn, getRandomRgn);
|
|
HOOK_FUNCTION(user32, GetWindowLongA, getWindowLongA);
|
|
HOOK_FUNCTION(user32, GetWindowLongW, getWindowLongW);
|
|
HOOK_FUNCTION(user32, MessageBoxA, messageBox<MessageBoxA>);
|
|
HOOK_FUNCTION(user32, MessageBoxW, messageBox<MessageBoxW>);
|
|
HOOK_FUNCTION(user32, MessageBoxExA, messageBox<MessageBoxExA>);
|
|
HOOK_FUNCTION(user32, MessageBoxExW, messageBox<MessageBoxExW>);
|
|
HOOK_FUNCTION(user32, MessageBoxIndirectA, messageBox<MessageBoxIndirectA>);
|
|
HOOK_FUNCTION(user32, MessageBoxIndirectW, messageBox<MessageBoxIndirectW>);
|
|
HOOK_FUNCTION(user32, PeekMessageA, peekMessageA);
|
|
HOOK_FUNCTION(user32, PeekMessageW, peekMessageW);
|
|
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_dwmSetIconicThumbnail = reinterpret_cast<decltype(&DwmSetIconicThumbnail)>(
|
|
GetProcAddress(GetModuleHandle("dwmapi"), "DwmSetIconicThumbnail"));
|
|
|
|
Compat::hookIatFunction(Dll::g_origDDrawModule, "SetWindowLongA", ddrawSetWindowLongA);
|
|
|
|
SetWinEventHook(EVENT_OBJECT_CREATE, EVENT_OBJECT_CREATE,
|
|
Dll::g_currentModule, &winEventProc, GetCurrentProcessId(), 0, WINEVENT_INCONTEXT);
|
|
SetWinEventHook(EVENT_OBJECT_NAMECHANGE, EVENT_OBJECT_NAMECHANGE,
|
|
Dll::g_currentModule, &winEventProc, GetCurrentProcessId(), 0, WINEVENT_INCONTEXT);
|
|
SetWinEventHook(EVENT_OBJECT_SHOW, EVENT_OBJECT_HIDE,
|
|
Dll::g_currentModule, &winEventProc, GetCurrentProcessId(), 0, WINEVENT_INCONTEXT);
|
|
SetWinEventHook(EVENT_OBJECT_STATECHANGE, EVENT_OBJECT_STATECHANGE,
|
|
Dll::g_currentModule, &winEventProc, GetCurrentProcessId(), 0, WINEVENT_INCONTEXT);
|
|
}
|
|
|
|
void onCreateWindow(HWND hwnd)
|
|
{
|
|
LOG_FUNC("onCreateWindow", hwnd);
|
|
if (!GuiThread::isReady() || GuiThread::isGuiThreadWindow(hwnd))
|
|
{
|
|
return;
|
|
}
|
|
|
|
{
|
|
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 (!Gdi::Window::isTopLevelWindow(hwnd))
|
|
{
|
|
return;
|
|
}
|
|
|
|
char className[64] = {};
|
|
GetClassName(hwnd, className, sizeof(className));
|
|
if (std::string(className) == "CompatWindowDesktopReplacement")
|
|
{
|
|
// Disable VirtualizeDesktopPainting shim
|
|
SendNotifyMessage(hwnd, WM_CLOSE, 0, 0);
|
|
return;
|
|
}
|
|
|
|
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 = GetClassLong(hwnd, GCL_STYLE);
|
|
if (style & CS_DROPSHADOW)
|
|
{
|
|
CALL_ORIG_FUNC(SetClassLongA)(hwnd, GCL_STYLE, style & ~CS_DROPSHADOW);
|
|
}
|
|
|
|
Gdi::Window::updateWindowPos(hwnd);
|
|
}
|
|
|
|
void startFrame()
|
|
{
|
|
if (Config::Settings::FpsLimiter::MSGLOOP != Config::fpsLimiter.get() || g_inWindowProc)
|
|
{
|
|
return;
|
|
}
|
|
|
|
auto fps = Config::fpsLimiter.getParam();
|
|
if (0 == fps)
|
|
{
|
|
fps = 1000;
|
|
}
|
|
|
|
if (!g_isFrameStarted)
|
|
{
|
|
g_qpcWaitEnd += Time::g_qpcFrequency / fps;
|
|
g_isFrameStarted = true;
|
|
return;
|
|
}
|
|
|
|
if (!g_waiting)
|
|
{
|
|
return;
|
|
}
|
|
|
|
g_qpcWaitEnd += Time::g_qpcFrequency / fps;
|
|
g_waiting = false;
|
|
|
|
auto qpcNow = Time::queryPerformanceCounter();
|
|
if (qpcNow - g_qpcWaitEnd >= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Compat::ScopedThreadPriority prio(THREAD_PRIORITY_TIME_CRITICAL);
|
|
while (Time::qpcToMs(g_qpcWaitEnd - qpcNow) > 0)
|
|
{
|
|
Time::waitForNextTick();
|
|
DDraw::RealPrimarySurface::flush();
|
|
qpcNow = Time::queryPerformanceCounter();
|
|
}
|
|
|
|
while (g_qpcWaitEnd - qpcNow > 0)
|
|
{
|
|
qpcNow = Time::queryPerformanceCounter();
|
|
}
|
|
}
|
|
|
|
void watchWindowPosChanges(WindowPosChangeNotifyFunc notifyFunc)
|
|
{
|
|
g_windowPosChangeNotifyFuncs.insert(notifyFunc);
|
|
}
|
|
}
|
|
}
|