Skip to content

Commit

Permalink
Ensure mouse button data is not lost
Browse files Browse the repository at this point in the history
  • Loading branch information
elishacloud committed Jun 22, 2024
1 parent b8241f5 commit 9292075
Show file tree
Hide file tree
Showing 8 changed files with 112 additions and 100 deletions.
2 changes: 1 addition & 1 deletion Dllmain/BuildNo.rc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
#define BUILD_NUMBER 7058
#define BUILD_NUMBER 7059
1 change: 1 addition & 0 deletions Settings/AllSettings.ini
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ SetNamedLayer =
IgnoreWindowName =

[dinput8]
DeviceLookupCacheTime = 0
FilterNonActiveInput = 0
FixHighFrequencyMouse = 0

Expand Down
2 changes: 2 additions & 0 deletions Settings/Settings.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
visit(Dinput8HookSystem32) \
visit(DsoundHookSystem32) \
visit(SetSwapEffectShim) \
visit(DeviceLookupCacheTime) \
visit(DisableGameUX) \
visit(DisableHighDPIScaling) \
visit(DisableLogging) \
Expand Down Expand Up @@ -241,6 +242,7 @@ struct CONFIG
DWORD DinputHookSystem32 = 0; // Hooks the dinput.dll file in the Windows System32 folder
DWORD Dinput8HookSystem32 = 0; // Hooks the dinput8.dll file in the Windows System32 folder
DWORD DsoundHookSystem32 = 0; // Hooks the dsound.dll file in the Windows System32 folder
DWORD DeviceLookupCacheTime = 0; // Number of seconds to cache the DeviceEnum callback data
bool DirectShowEmulation = false; // Emulates DirectShow APIs
bool DisableGameUX = false; // Disables the Microsoft Game Explorer which can sometimes cause high CPU in rundll32.exe and hang the game process
bool DisableHighDPIScaling = false; // Disables display scaling on high DPI settings
Expand Down
1 change: 1 addition & 0 deletions Settings/Settings.ini
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ ForceWindowResize = 0
WaitForWindowChanges = 0

[dinput8]
DeviceLookupCacheTime = 0
FilterNonActiveInput = 0
FixHighFrequencyMouse = 0

Expand Down
56 changes: 37 additions & 19 deletions dinput8/IDirectInput8.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,11 +91,16 @@ HRESULT m_IDirectInput8::EnumDevicesT(DWORD dwDevType, V lpCallback, LPVOID pvRe
{
Logging::LogDebug() << __FUNCTION__ << " (" << this << ")";

if (!lpCallback)
{
return DIERR_INVALIDPARAM;
}

auto now = std::chrono::steady_clock::now();
auto& cachedData = GetEnumCache(GetProxyInterface<T>());

// Check if cached data is valid
if (dwDevType == cachedData.dwDevType && dwFlags == cachedData.dwFlags && (now - cachedData.lastUpdate) < cacheDuration && lpCallback)
if (Config.DeviceLookupCacheTime && dwDevType == cachedData.dwDevType && dwFlags == cachedData.dwFlags && (now - cachedData.lastUpdate) < cacheDuration)
{
// Use cached data
for (const auto& entry : cachedData.devices)
Expand All @@ -110,40 +115,53 @@ HRESULT m_IDirectInput8::EnumDevicesT(DWORD dwDevType, V lpCallback, LPVOID pvRe

struct EnumDevices
{
LPVOID pvRef = nullptr;
V lpCallback = nullptr;
std::vector<D> CacheDevices;

static BOOL CALLBACK DIEnumDevicesCallback(const D* lpddi, LPVOID pvRef)
{
EnumDevices* self = (EnumDevices*)pvRef;

if (lpddi)
if (Config.DeviceLookupCacheTime)
{
D ddi = {};
ddi.dwSize = sizeof(ddi);
memcpy(&ddi, lpddi, min(lpddi->dwSize, sizeof(ddi)));
self->CacheDevices.push_back(ddi);
if (lpddi)
{
D ddi = {};
memcpy(&ddi, lpddi, min(lpddi->dwSize, sizeof(ddi)));
ddi.dwSize = min(lpddi->dwSize, sizeof(ddi));
self->CacheDevices.push_back(ddi);
}
return DIENUM_CONTINUE;
}
else
{
return self->lpCallback(lpddi, self->pvRef);
}

return DIENUM_CONTINUE;
}
} CallbackContext;
CallbackContext.pvRef = pvRef;
CallbackContext.lpCallback = lpCallback;

HRESULT hr = GetProxyInterface<T>()->EnumDevices(dwDevType, EnumDevices::DIEnumDevicesCallback, &CallbackContext, dwFlags);

if (SUCCEEDED(hr) && lpCallback)
if (SUCCEEDED(hr))
{
// Update the cache
cachedData.lastUpdate = now;
cachedData.dwDevType = dwDevType;
cachedData.dwFlags = dwFlags;
cachedData.devices = std::move(CallbackContext.CacheDevices);

// Use the new data
for (const auto& entry : cachedData.devices)
if (Config.DeviceLookupCacheTime)
{
if (lpCallback(&entry, pvRef) == DIENUM_STOP)
// Update the cache
cachedData.lastUpdate = now;
cachedData.dwDevType = dwDevType;
cachedData.dwFlags = dwFlags;
cachedData.devices = std::move(CallbackContext.CacheDevices);

// Use the new data
for (const auto& entry : cachedData.devices)
{
break;
if (lpCallback(&entry, pvRef) == DIENUM_STOP)
{
break;
}
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions dinput8/IDirectInput8.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ class m_IDirectInput8 : public IDirectInput8A, public IDirectInput8W, public Add
REFIID WrapperID;
REFIID WrapperDeviceID;

const std::chrono::seconds cacheDuration = std::chrono::seconds(5); // Cache duration in seconds
const std::chrono::seconds cacheDuration = std::chrono::seconds(Config.DeviceLookupCacheTime); // Cache duration in seconds

// Define a template structure to hold cached device data
template <class T, class V>
struct CachedDeviceDataT
{
std::chrono::steady_clock::time_point lastUpdate;
std::chrono::steady_clock::time_point lastUpdate = std::chrono::steady_clock::now();
DWORD dwDevType = 0;
DWORD dwFlags = 0;
std::vector<T> devices;
Expand Down
134 changes: 58 additions & 76 deletions dinput8/IDirectInputDevice8.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -130,17 +130,14 @@ HRESULT m_IDirectInputDevice8::FakeGetDeviceData(DWORD cbObjectData, LPDIDEVICEO
return DIERR_INVALIDPARAM;
}

bool isPeek = ((dwFlags & DIGDD_PEEK) > 0);

if (rgdod == nullptr && isPeek)
{
*pdwInOut = min(6, *pdwInOut); // Just hard code to 6 as the current number of possible buffered events
return DI_OK;
}
bool isPeek = (dwFlags == DIGDD_PEEK);

Lock();

// Get latest ouse state from the DirectInput8 buffer
// Move cached data to new vector
std::vector<MOUSEBUTTONDATA> mouseButtonDataGame = std::move(mouseButtonData);

// Get latest mouse state from the DirectInput8 buffer
{
std::vector<DIDEVICEOBJECTDATA> dod;
dod.resize(dwItems);
Expand All @@ -151,61 +148,68 @@ HRESULT m_IDirectInputDevice8::FakeGetDeviceData(DWORD cbObjectData, LPDIDEVICEO
{
if (dod[x].dwOfs == DIMOFS_X)
{
mouseStateDeviceData.lX += dod[x].dwData;
}
if (dod[x].dwOfs == DIMOFS_Y)
{
mouseStateDeviceData.lY += dod[x].dwData;
}
if (dod[x].dwOfs == DIMOFS_Z)
{
mouseStateDeviceData.lZ += dod[x].dwData;
mouseMovementData.X += dod[x].dwData;
}
if (dod[x].dwOfs == DIMOFS_BUTTON0)
else if (dod[x].dwOfs == DIMOFS_Y)
{
mouseStateDeviceData.rgbButtons[0] = (BYTE)dod[x].dwData;
mouseMovementData.Y += dod[x].dwData;
}
if (dod[x].dwOfs == DIMOFS_BUTTON1)
else if (dod[x].dwOfs == DIMOFS_Z)
{
mouseStateDeviceData.rgbButtons[1] = (BYTE)dod[x].dwData;
mouseMovementData.Z += dod[x].dwData;
}
if (dod[x].dwOfs == DIMOFS_BUTTON2)
else
{
mouseStateDeviceData.rgbButtons[2] = (BYTE)dod[x].dwData;
MOUSEBUTTONDATA item = { dod[x].dwOfs, dod[x].dwData };
mouseButtonDataGame.push_back(item);
}
}
}
}

DWORD dwOut = 0;

// Determine timestamp:
__int64 fTime = 0;
GetSystemTimeAsFileTime((FILETIME*)&fTime);
fTime = fTime / 1000;

DWORD dwOut = 0;
DWORD dwVectorLoc = 0;

// Checking for overflow
if (rgdod == nullptr && *pdwInOut == 0)
{
// Don't return overflow as just using mouse state
// Never return overflow
}
// Flush buffer
else if (rgdod == nullptr && !isPeek)
else if (rgdod == nullptr && *pdwInOut == INFINITE && !isPeek)
{
// Flush mouse movement data
mouseMovementData = {};
}
// Number of records in the buffer
else if (rgdod == nullptr && *pdwInOut == INFINITE && isPeek)
{
// Don't flush buffer as just using mouse state
size_t Counter = 0;
if (mouseMovementData.X != 0) Counter++;
if (mouseMovementData.Y != 0) Counter++;
if (mouseMovementData.Z != 0) Counter++;
Counter += mouseButtonDataGame.size();

*pdwInOut = Counter;
}
// Full device object data
else if (rgdod)
{
memset(rgdod, 0, *pdwInOut * cbObjectData);
MOUSEMOVEMENTDATA mouseMovementDataGame = mouseMovementData;

LPDIDEVICEOBJECTDATA p_rgdod = rgdod;
for (DWORD i = 0; i < *pdwInOut; i++)
{
// Sending DIMOFS_X
if (mouseStateDeviceData.lX != 0)
if (mouseMovementDataGame.X != 0)
{
p_rgdod->dwData = mouseStateDeviceData.lX;
p_rgdod->dwData = mouseMovementDataGame.X;
p_rgdod->dwOfs = DIMOFS_X;
p_rgdod->dwSequence = dwSequence;
p_rgdod->dwTimeStamp = (DWORD)fTime;
Expand All @@ -214,14 +218,15 @@ HRESULT m_IDirectInputDevice8::FakeGetDeviceData(DWORD cbObjectData, LPDIDEVICEO
p_rgdod->uAppData = NULL;
}

dwSequence++;
mouseStateDeviceData.lX = 0;
dwOut++;
dwSequence++;
mouseMovementDataGame.X = 0;
if (!isPeek) mouseMovementData.X = 0;
}
// Sending DIMOFS_Y
else if (mouseStateDeviceData.lY != 0)
else if (mouseMovementDataGame.Y != 0)
{
p_rgdod->dwData = mouseStateDeviceData.lY;
p_rgdod->dwData = mouseMovementDataGame.Y;
p_rgdod->dwOfs = DIMOFS_Y;
p_rgdod->dwSequence = dwSequence;
p_rgdod->dwTimeStamp = (DWORD)fTime;
Expand All @@ -230,14 +235,15 @@ HRESULT m_IDirectInputDevice8::FakeGetDeviceData(DWORD cbObjectData, LPDIDEVICEO
p_rgdod->uAppData = NULL;
}

dwSequence++;
mouseStateDeviceData.lY = 0;
dwOut++;
dwSequence++;
mouseMovementDataGame.Y = 0;
if (!isPeek) mouseMovementData.Y = 0;
}
// Sending DIMOFS_Z
else if (mouseStateDeviceData.lZ != 0)
else if (mouseMovementDataGame.Z != 0)
{
p_rgdod->dwData = mouseStateDeviceData.lZ;
p_rgdod->dwData = mouseMovementDataGame.Z;
p_rgdod->dwOfs = DIMOFS_Z;
p_rgdod->dwSequence = dwSequence;
p_rgdod->dwTimeStamp = (DWORD)fTime;
Expand All @@ -246,57 +252,26 @@ HRESULT m_IDirectInputDevice8::FakeGetDeviceData(DWORD cbObjectData, LPDIDEVICEO
p_rgdod->uAppData = NULL;
}

dwSequence++;
mouseStateDeviceData.lZ = 0;
dwOut++;
}
// Sending DIMOFS_BUTTON0
else if (mouseStateDeviceData.rgbButtons[0] != mouseStateDeviceDataGame.rgbButtons[0])
{
p_rgdod->dwData = mouseStateDeviceData.rgbButtons[0];
p_rgdod->dwOfs = DIMOFS_BUTTON0;
p_rgdod->dwSequence = dwSequence;
p_rgdod->dwTimeStamp = (DWORD)fTime;
if (cbObjectData == sizeof(DIDEVICEOBJECTDATA))
{
p_rgdod->uAppData = NULL;
}

dwSequence++;
mouseStateDeviceDataGame.rgbButtons[0] = mouseStateDeviceData.rgbButtons[0];
dwOut++;
mouseMovementDataGame.Z = 0;
if (!isPeek) mouseMovementData.Z = 0;
}
// Sending DIMOFS_BUTTON1
else if (mouseStateDeviceData.rgbButtons[1] != mouseStateDeviceDataGame.rgbButtons[1])
// Sending DIMOFS_BUTTON data
else if (dwVectorLoc < mouseButtonDataGame.size())
{
p_rgdod->dwData = mouseStateDeviceData.rgbButtons[1];
p_rgdod->dwOfs = DIMOFS_BUTTON1;
p_rgdod->dwData = mouseButtonDataGame[dwVectorLoc].dwData;
p_rgdod->dwOfs = mouseButtonDataGame[dwVectorLoc].dwOfs;
p_rgdod->dwSequence = dwSequence;
p_rgdod->dwTimeStamp = (DWORD)fTime;
if (cbObjectData == sizeof(DIDEVICEOBJECTDATA))
{
p_rgdod->uAppData = NULL;
}

dwSequence++;
mouseStateDeviceDataGame.rgbButtons[1] = mouseStateDeviceData.rgbButtons[1];
dwOut++;
}
// Sending DIMOFS_BUTTON2
else if (mouseStateDeviceData.rgbButtons[2] != mouseStateDeviceDataGame.rgbButtons[2])
{
p_rgdod->dwData = mouseStateDeviceData.rgbButtons[2];
p_rgdod->dwOfs = DIMOFS_BUTTON2;
p_rgdod->dwSequence = dwSequence;
p_rgdod->dwTimeStamp = (DWORD)fTime;
if (cbObjectData == sizeof(DIDEVICEOBJECTDATA))
{
p_rgdod->uAppData = NULL;
}

dwSequence++;
mouseStateDeviceDataGame.rgbButtons[2] = mouseStateDeviceData.rgbButtons[2];
dwOut++;
dwVectorLoc++;
}
// No more data to sent
else
Expand All @@ -308,6 +283,13 @@ HRESULT m_IDirectInputDevice8::FakeGetDeviceData(DWORD cbObjectData, LPDIDEVICEO
}
}

// Save unsent mouse button data
for (size_t x = dwVectorLoc; x < mouseButtonDataGame.size(); x++)
{
MOUSEBUTTONDATA item = { mouseButtonDataGame[x].dwOfs, mouseButtonDataGame[x].dwData };
mouseButtonData.push_back(item);
}

Unlock();

*pdwInOut = dwOut;
Expand Down
12 changes: 10 additions & 2 deletions dinput8/IDirectInputDevice8.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,16 @@ class m_IDirectInputDevice8 : public IDirectInputDevice8A, public IDirectInputDe

CRITICAL_SECTION critSect = {};

DIMOUSESTATE mouseStateDeviceData = {};
DIMOUSESTATE mouseStateDeviceDataGame = {};
struct MOUSEBUTTONDATA {
DWORD dwOfs;
DWORD dwData;
};
std::vector<MOUSEBUTTONDATA> mouseButtonData;
struct MOUSEMOVEMENTDATA {
DWORD X;
DWORD Y;
DWORD Z;
} mouseMovementData = {};

void Lock() { EnterCriticalSection(&critSect); }
void Unlock() { LeaveCriticalSection(&critSect); }
Expand Down

0 comments on commit 9292075

Please sign in to comment.