mirror of
https://github.com/narzoul/DDrawCompat
synced 2024-12-30 08:55:36 +01:00
150 lines
3.6 KiB
C++
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(), >i);
|
|
|
|
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);
|
|
}
|
|
}
|
|
}
|