mirror of
https://github.com/narzoul/DDrawCompat
synced 2024-12-30 08:55:36 +01:00
Simplified caret handling and fixed positioning issues
This commit is contained in:
parent
fd1063defe
commit
607eb5e25b
@ -1,7 +1,5 @@
|
||||
#define CINTERFACE
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
|
||||
#include <oleacc.h>
|
||||
#include <Windows.h>
|
||||
|
||||
#include "CompatGdi.h"
|
||||
@ -11,6 +9,9 @@
|
||||
|
||||
namespace
|
||||
{
|
||||
template <typename Result, typename... Params>
|
||||
using FuncPtr = Result(WINAPI *)(Params...);
|
||||
|
||||
struct CaretData
|
||||
{
|
||||
HWND hwnd;
|
||||
@ -18,195 +19,102 @@ namespace
|
||||
long top;
|
||||
long width;
|
||||
long height;
|
||||
bool isDrawn;
|
||||
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();
|
||||
|
||||
class CaretScopedThreadLock
|
||||
void CALLBACK compatGdiCaretEvent(HWINEVENTHOOK, DWORD, HWND, LONG idObject, LONG, DWORD, DWORD)
|
||||
{
|
||||
public:
|
||||
CaretScopedThreadLock()
|
||||
if (OBJID_CARET == idObject)
|
||||
{
|
||||
EnterCriticalSection(&g_caretCriticalSection);
|
||||
updateCaret();
|
||||
}
|
||||
}
|
||||
|
||||
~CaretScopedThreadLock()
|
||||
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 = CompatGdiDc::getDc(dc);
|
||||
CALL_ORIG_FUNC(PatBlt)(
|
||||
compatDc, caret.left, caret.top, caret.width, caret.height, PATINVERT);
|
||||
CompatGdiDc::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()
|
||||
{
|
||||
EnterCriticalSection(&g_caretCriticalSection);
|
||||
CaretData newCaret = getCaretData();
|
||||
if (newCaret == g_caret)
|
||||
{
|
||||
LeaveCriticalSection(&g_caretCriticalSection);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
BOOL WINAPI hideCaret(HWND hWnd);
|
||||
BOOL WINAPI showCaret(HWND hWnd);
|
||||
|
||||
void drawCaret()
|
||||
{
|
||||
if (CompatGdi::beginGdiRendering())
|
||||
if ((g_caret.isVisible || newCaret.isVisible) && CompatGdi::beginGdiRendering())
|
||||
{
|
||||
HDC dc = GetDC(g_caret.hwnd);
|
||||
HDC compatDc = CompatGdiDc::getDc(dc);
|
||||
PatBlt(compatDc, g_caret.left, g_caret.top, g_caret.width, g_caret.height, PATINVERT);
|
||||
CompatGdiDc::releaseDc(dc);
|
||||
ReleaseDC(g_caret.hwnd, dc);
|
||||
drawCaret(g_caret);
|
||||
drawCaret(newCaret);
|
||||
CompatGdi::endGdiRendering();
|
||||
}
|
||||
}
|
||||
|
||||
LONG getCaretState(IAccessible* accessible)
|
||||
{
|
||||
VARIANT varChild = {};
|
||||
varChild.vt = VT_I4;
|
||||
varChild.lVal = CHILDID_SELF;
|
||||
VARIANT varState = {};
|
||||
accessible->lpVtbl->get_accState(accessible, varChild, &varState);
|
||||
return varState.lVal;
|
||||
}
|
||||
|
||||
void CALLBACK caretDestroyEvent(
|
||||
HWINEVENTHOOK /*hWinEventHook*/,
|
||||
DWORD /*event*/,
|
||||
HWND hwnd,
|
||||
LONG idObject,
|
||||
LONG idChild,
|
||||
DWORD /*dwEventThread*/,
|
||||
DWORD /*dwmsEventTime*/)
|
||||
{
|
||||
if (OBJID_CARET != idObject)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CaretScopedThreadLock caretLock;
|
||||
if (!g_caret.isDrawn || g_caret.hwnd != hwnd)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
IAccessible* accessible = nullptr;
|
||||
VARIANT varChild = {};
|
||||
AccessibleObjectFromEvent(hwnd, idObject, idChild, &accessible, &varChild);
|
||||
if (accessible)
|
||||
{
|
||||
if (STATE_SYSTEM_INVISIBLE == getCaretState(accessible))
|
||||
{
|
||||
drawCaret();
|
||||
g_caret.isDrawn = false;
|
||||
}
|
||||
accessible->lpVtbl->Release(accessible);
|
||||
}
|
||||
}
|
||||
|
||||
void CALLBACK caretLocationChangeEvent(
|
||||
HWINEVENTHOOK /*hWinEventHook*/,
|
||||
DWORD /*event*/,
|
||||
HWND hwnd,
|
||||
LONG idObject,
|
||||
LONG /*idChild*/,
|
||||
DWORD /*dwEventThread*/,
|
||||
DWORD /*dwmsEventTime*/)
|
||||
{
|
||||
if (OBJID_CARET != idObject)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CaretScopedThreadLock caretLock;
|
||||
if (g_caret.isDrawn && g_caret.hwnd == hwnd)
|
||||
{
|
||||
hideCaret(hwnd);
|
||||
showCaret(hwnd);
|
||||
}
|
||||
}
|
||||
|
||||
void CALLBACK caretShowEvent(
|
||||
HWINEVENTHOOK /*hWinEventHook*/,
|
||||
DWORD /*event*/,
|
||||
HWND hwnd,
|
||||
LONG idObject,
|
||||
LONG /*idChild*/,
|
||||
DWORD /*dwEventThread*/,
|
||||
DWORD /*dwmsEventTime*/)
|
||||
{
|
||||
if (OBJID_CARET != idObject)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CaretScopedThreadLock caretLock;
|
||||
if (!g_caret.isDrawn && g_caret.hwnd == hwnd)
|
||||
{
|
||||
drawCaret();
|
||||
g_caret.isDrawn = true;
|
||||
}
|
||||
}
|
||||
|
||||
BOOL WINAPI createCaret(HWND hWnd, HBITMAP hBitmap, int nWidth, int nHeight)
|
||||
{
|
||||
BOOL result = CALL_ORIG_FUNC(CreateCaret)(hWnd, hBitmap, nWidth, nHeight);
|
||||
if (result)
|
||||
{
|
||||
CaretScopedThreadLock caretLock;
|
||||
if (g_caret.isDrawn)
|
||||
{
|
||||
drawCaret();
|
||||
g_caret.isDrawn = false;
|
||||
}
|
||||
g_caret.width = nWidth ? nWidth : GetSystemMetrics(SM_CXBORDER);
|
||||
g_caret.height = nHeight ? nHeight : GetSystemMetrics(SM_CYBORDER);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
BOOL WINAPI hideCaret(HWND hWnd)
|
||||
{
|
||||
BOOL result = CALL_ORIG_FUNC(HideCaret)(hWnd);
|
||||
if (result)
|
||||
{
|
||||
CaretScopedThreadLock caretLock;
|
||||
if (g_caret.isDrawn)
|
||||
{
|
||||
drawCaret();
|
||||
g_caret.isDrawn = false;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
BOOL WINAPI showCaret(HWND hWnd)
|
||||
{
|
||||
if (!CALL_ORIG_FUNC(ShowCaret)(hWnd))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
CaretScopedThreadLock caretLock;
|
||||
if (!g_caret.isDrawn)
|
||||
{
|
||||
IAccessible* accessible = nullptr;
|
||||
AccessibleObjectFromWindow(hWnd, static_cast<DWORD>(OBJID_CARET), IID_IAccessible,
|
||||
reinterpret_cast<void**>(&accessible));
|
||||
if (accessible)
|
||||
{
|
||||
if (0 == getCaretState(accessible))
|
||||
{
|
||||
POINT caretPos = {};
|
||||
GetCaretPos(&caretPos);
|
||||
g_caret.left = caretPos.x;
|
||||
g_caret.top = caretPos.y;
|
||||
g_caret.hwnd = hWnd;
|
||||
drawCaret();
|
||||
g_caret.isDrawn = true;
|
||||
}
|
||||
accessible->lpVtbl->Release(accessible);
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
g_caret = newCaret;
|
||||
LeaveCriticalSection(&g_caretCriticalSection);
|
||||
}
|
||||
}
|
||||
|
||||
#define HOOK_GDI_CARET_FUNCTION(module, func) \
|
||||
Compat::hookFunction<decltype(&func), &func>( \
|
||||
#module, #func, getCompatGdiCaretFuncPtr<decltype(&func), &func>(&func));
|
||||
|
||||
namespace CompatGdiCaret
|
||||
{
|
||||
void installHooks()
|
||||
@ -214,17 +122,17 @@ namespace CompatGdiCaret
|
||||
InitializeCriticalSection(&g_caretCriticalSection);
|
||||
|
||||
Compat::beginHookTransaction();
|
||||
HOOK_FUNCTION(user32, CreateCaret, createCaret);
|
||||
HOOK_FUNCTION(user32, HideCaret, hideCaret);
|
||||
HOOK_FUNCTION(user32, ShowCaret, showCaret);
|
||||
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);
|
||||
Compat::endHookTransaction();
|
||||
|
||||
const DWORD threadId = GetCurrentThreadId();
|
||||
SetWinEventHook(EVENT_OBJECT_DESTROY, EVENT_OBJECT_DESTROY,
|
||||
nullptr, &caretDestroyEvent, 0, threadId, WINEVENT_OUTOFCONTEXT);
|
||||
SetWinEventHook(EVENT_OBJECT_CREATE, EVENT_OBJECT_HIDE,
|
||||
nullptr, &compatGdiCaretEvent, 0, threadId, WINEVENT_OUTOFCONTEXT);
|
||||
SetWinEventHook(EVENT_OBJECT_LOCATIONCHANGE, EVENT_OBJECT_LOCATIONCHANGE,
|
||||
nullptr, &caretLocationChangeEvent, 0, threadId, WINEVENT_OUTOFCONTEXT);
|
||||
SetWinEventHook(EVENT_OBJECT_SHOW, EVENT_OBJECT_SHOW,
|
||||
nullptr, &caretShowEvent, 0, threadId, WINEVENT_OUTOFCONTEXT);
|
||||
nullptr, &compatGdiCaretEvent, 0, threadId, WINEVENT_OUTOFCONTEXT);
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user