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

Fixed caret display issues

This commit is contained in:
narzoul 2018-10-24 13:10:56 +02:00
parent 2f00b74a56
commit 4717b9aa1b
3 changed files with 73 additions and 83 deletions

View File

@ -15,6 +15,7 @@
#include "DDraw/Surfaces/PrimarySurface.h" #include "DDraw/Surfaces/PrimarySurface.h"
#include "DDraw/Types.h" #include "DDraw/Types.h"
#include "Gdi/AccessGuard.h" #include "Gdi/AccessGuard.h"
#include "Gdi/Caret.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"
@ -459,6 +460,7 @@ namespace
Sleep(msPresentDelayAfterVBlank); Sleep(msPresentDelayAfterVBlank);
DDraw::ScopedThreadLock lock; DDraw::ScopedThreadLock lock;
Gdi::Caret::blink();
waitForVBlank = Time::qpcToMs(Time::queryPerformanceCounter() - waitForVBlank = Time::qpcToMs(Time::queryPerformanceCounter() -
D3dDdi::KernelModeThunks::getQpcLastVerticalBlank()) >= msPresentDelayAfterVBlank; D3dDdi::KernelModeThunks::getQpcLastVerticalBlank()) >= msPresentDelayAfterVBlank;

View File

@ -3,19 +3,16 @@
#include <Windows.h> #include <Windows.h>
#include "Common/Hook.h" #include "Common/Hook.h"
#include "Common/ScopedCriticalSection.h" #include "Common/Time.h"
#include "Gdi/AccessGuard.h" #include "DDraw/ScopedThreadLock.h"
#include "Gdi/Caret.h" #include "Gdi/Caret.h"
#include "Gdi/Dc.h"
#include "Gdi/Gdi.h" extern "C" IMAGE_DOS_HEADER __ImageBase;
namespace namespace
{ {
HWINEVENTHOOK g_compatGdiCaretGeneralEventHook = nullptr; HWINEVENTHOOK g_caretGeneralEventHook = nullptr;
HWINEVENTHOOK g_compatGdiCaretLocationChangeEventHook = nullptr; HWINEVENTHOOK g_caretLocationChangeEventHook = nullptr;
template <typename Result, typename... Params>
using FuncPtr = Result(WINAPI *)(Params...);
struct CaretData struct CaretData
{ {
@ -25,58 +22,36 @@ namespace
long width; long width;
long height; long height;
bool isVisible; bool isVisible;
bool isDrawn;
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 = {}; CaretData g_caret = {};
CRITICAL_SECTION g_caretCriticalSection; long long g_qpcLastBlink = 0;
void updateCaret();
void CALLBACK compatGdiCaretEvent(HWINEVENTHOOK, DWORD, HWND, LONG idObject, LONG, DWORD, DWORD) void updateCaret(DWORD threadId);
void CALLBACK caretEvent(HWINEVENTHOOK, DWORD, HWND hwnd, LONG idObject, LONG, DWORD, DWORD)
{ {
if (OBJID_CARET == idObject) if (OBJID_CARET == idObject)
{ {
updateCaret(); DDraw::ScopedThreadLock lock;
updateCaret(GetWindowThreadProcessId(hwnd, nullptr));
} }
} }
template <typename OrigFuncPtr, OrigFuncPtr origFunc, typename Result, typename... Params> void drawCaret()
Result WINAPI compatGdiCaretFunc(Params... params)
{ {
Result result = Compat::getOrigFuncPtr<OrigFuncPtr, origFunc>()(params...); HDC dc = GetDC(g_caret.hwnd);
updateCaret(); PatBlt(dc, g_caret.left, g_caret.top, g_caret.width, g_caret.height, PATINVERT);
return result; ReleaseDC(g_caret.hwnd, dc);
} }
void drawCaret(const CaretData& caret) CaretData getCaretData(DWORD threadId)
{
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 = {}; GUITHREADINFO gti = {};
gti.cbSize = sizeof(gti); gti.cbSize = sizeof(gti);
GetGUIThreadInfo(Gdi::getGdiThreadId(), &gti); GetGUIThreadInfo(threadId, &gti);
CaretData caretData = {}; CaretData caretData = {};
caretData.hwnd = gti.hwndCaret; caretData.hwnd = gti.hwndCaret;
@ -88,63 +63,75 @@ namespace
return caretData; return caretData;
} }
template <typename OrigFuncPtr, OrigFuncPtr origFunc, typename Result, typename... Params> void updateCaret(DWORD threadId)
OrigFuncPtr getCompatGdiCaretFuncPtr(FuncPtr<Result, Params...>)
{ {
return &compatGdiCaretFunc<OrigFuncPtr, origFunc, Result, Params...>; DDraw::ScopedThreadLock lock;
} if (g_caret.isDrawn)
void updateCaret()
{
Compat::ScopedCriticalSection lock(g_caretCriticalSection);
CaretData newCaret = getCaretData();
if (newCaret == g_caret)
{ {
return; drawCaret();
} }
if ((g_caret.isVisible || newCaret.isVisible)) g_caret = getCaretData(threadId);
{
Gdi::GdiAccessGuard accessGuard(Gdi::ACCESS_WRITE);
drawCaret(g_caret);
drawCaret(newCaret);
}
g_caret = newCaret; if (g_caret.isVisible)
{
g_caret.isDrawn = true;
drawCaret();
g_qpcLastBlink = Time::queryPerformanceCounter();
}
} }
} }
#define HOOK_GDI_CARET_FUNCTION(module, func) \
Compat::hookFunction<decltype(&func), &func>( \
#module, #func, getCompatGdiCaretFuncPtr<decltype(&func), &func>(&func));
namespace Gdi namespace Gdi
{ {
namespace Caret namespace Caret
{ {
void blink()
{
DDraw::ScopedThreadLock lock;
if (!g_caret.isVisible)
{
return;
}
UINT caretBlinkTime = GetCaretBlinkTime();
if (INFINITE == caretBlinkTime)
{
return;
}
const long long qpcNow = Time::queryPerformanceCounter();
if (Time::qpcToMs(qpcNow - g_qpcLastBlink) >= caretBlinkTime)
{
g_qpcLastBlink = qpcNow;
GUITHREADINFO gti = {};
gti.cbSize = sizeof(gti);
GetGUIThreadInfo(GetWindowThreadProcessId(g_caret.hwnd, nullptr), &gti);
if (!(gti.flags & (GUI_INMENUMODE | GUI_POPUPMENUMODE | GUI_SYSTEMMENUMODE)))
{
g_caret.isDrawn = !g_caret.isDrawn;
drawCaret();
}
}
}
void installHooks() void installHooks()
{ {
InitializeCriticalSection(&g_caretCriticalSection); g_caretGeneralEventHook = SetWinEventHook(
EVENT_OBJECT_SHOW, EVENT_OBJECT_HIDE,
HOOK_GDI_CARET_FUNCTION(user32, CreateCaret); reinterpret_cast<HMODULE>(&__ImageBase), &caretEvent,
HOOK_GDI_CARET_FUNCTION(user32, DestroyCaret); GetCurrentProcessId(), 0, WINEVENT_INCONTEXT);
HOOK_GDI_CARET_FUNCTION(user32, HideCaret); g_caretLocationChangeEventHook = SetWinEventHook(
HOOK_GDI_CARET_FUNCTION(user32, SetCaretPos); EVENT_OBJECT_LOCATIONCHANGE, EVENT_OBJECT_LOCATIONCHANGE,
HOOK_GDI_CARET_FUNCTION(user32, ShowCaret); reinterpret_cast<HMODULE>(&__ImageBase), &caretEvent,
GetCurrentProcessId(), 0, WINEVENT_INCONTEXT);
const DWORD threadId = Gdi::getGdiThreadId();
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() void uninstallHooks()
{ {
UnhookWinEvent(g_compatGdiCaretLocationChangeEventHook); UnhookWinEvent(g_caretLocationChangeEventHook);
UnhookWinEvent(g_compatGdiCaretGeneralEventHook); UnhookWinEvent(g_caretGeneralEventHook);
} }
} }
} }

View File

@ -4,6 +4,7 @@ namespace Gdi
{ {
namespace Caret namespace Caret
{ {
void blink();
void installHooks(); void installHooks();
void uninstallHooks(); void uninstallHooks();
} }