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

150 lines
3.6 KiB
C++

#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include "Gdi/Caret.h"
#include "Gdi/Dc.h"
#include "Gdi/Gdi.h"
#include "Hook.h"
#include "ScopedCriticalSection.h"
namespace
{
HWINEVENTHOOK g_compatGdiCaretGeneralEventHook = nullptr;
HWINEVENTHOOK g_compatGdiCaretLocationChangeEventHook = nullptr;
template <typename Result, typename... Params>
using FuncPtr = Result(WINAPI *)(Params...);
struct CaretData
{
HWND hwnd;
long left;
long top;
long width;
long height;
bool isVisible;
bool operator==(const CaretData& rhs) const
{
return hwnd == rhs.hwnd &&
left == rhs.left &&
top == rhs.top &&
width == rhs.width &&
height == rhs.height &&
isVisible == rhs.isVisible;
}
};
CaretData g_caret = {};
CRITICAL_SECTION g_caretCriticalSection;
void updateCaret();
void CALLBACK compatGdiCaretEvent(HWINEVENTHOOK, DWORD, HWND, LONG idObject, LONG, DWORD, DWORD)
{
if (OBJID_CARET == idObject)
{
updateCaret();
}
}
template <typename OrigFuncPtr, OrigFuncPtr origFunc, typename Result, typename... Params>
Result WINAPI compatGdiCaretFunc(Params... params)
{
Result result = Compat::getOrigFuncPtr<OrigFuncPtr, origFunc>()(params...);
updateCaret();
return result;
}
void drawCaret(const CaretData& caret)
{
if (caret.isVisible)
{
HDC dc = GetDC(caret.hwnd);
HDC compatDc = Gdi::Dc::getDc(dc);
CALL_ORIG_FUNC(PatBlt)(
compatDc, caret.left, caret.top, caret.width, caret.height, PATINVERT);
Gdi::Dc::releaseDc(dc);
ReleaseDC(caret.hwnd, dc);
}
}
CaretData getCaretData()
{
GUITHREADINFO gti = {};
gti.cbSize = sizeof(gti);
GetGUIThreadInfo(GetCurrentThreadId(), &gti);
CaretData caretData = {};
caretData.hwnd = gti.hwndCaret;
caretData.left = gti.rcCaret.left;
caretData.top = gti.rcCaret.top;
caretData.width = gti.rcCaret.right - gti.rcCaret.left;
caretData.height = gti.rcCaret.bottom - gti.rcCaret.top;
caretData.isVisible = gti.flags & GUI_CARETBLINKING;
return caretData;
}
template <typename OrigFuncPtr, OrigFuncPtr origFunc, typename Result, typename... Params>
OrigFuncPtr getCompatGdiCaretFuncPtr(FuncPtr<Result, Params...>)
{
return &compatGdiCaretFunc<OrigFuncPtr, origFunc, Result, Params...>;
}
void updateCaret()
{
Compat::ScopedCriticalSection lock(g_caretCriticalSection);
CaretData newCaret = getCaretData();
if (newCaret == g_caret)
{
return;
}
if ((g_caret.isVisible || newCaret.isVisible) && Gdi::beginGdiRendering())
{
drawCaret(g_caret);
drawCaret(newCaret);
Gdi::endGdiRendering();
}
g_caret = newCaret;
}
}
#define HOOK_GDI_CARET_FUNCTION(module, func) \
Compat::hookFunction<decltype(&func), &func>( \
#module, #func, getCompatGdiCaretFuncPtr<decltype(&func), &func>(&func));
namespace Gdi
{
namespace Caret
{
void installHooks()
{
InitializeCriticalSection(&g_caretCriticalSection);
HOOK_GDI_CARET_FUNCTION(user32, CreateCaret);
HOOK_GDI_CARET_FUNCTION(user32, DestroyCaret);
HOOK_GDI_CARET_FUNCTION(user32, HideCaret);
HOOK_GDI_CARET_FUNCTION(user32, SetCaretPos);
HOOK_GDI_CARET_FUNCTION(user32, ShowCaret);
const DWORD threadId = GetCurrentThreadId();
g_compatGdiCaretGeneralEventHook = SetWinEventHook(EVENT_OBJECT_CREATE, EVENT_OBJECT_HIDE,
nullptr, &compatGdiCaretEvent, 0, threadId, WINEVENT_OUTOFCONTEXT);
g_compatGdiCaretLocationChangeEventHook = SetWinEventHook(
EVENT_OBJECT_LOCATIONCHANGE, EVENT_OBJECT_LOCATIONCHANGE, nullptr, &compatGdiCaretEvent,
0, threadId, WINEVENT_OUTOFCONTEXT);
}
void uninstallHooks()
{
UnhookWinEvent(g_compatGdiCaretLocationChangeEventHook);
UnhookWinEvent(g_compatGdiCaretGeneralEventHook);
}
}
}