Skip to content

Commit 540a7dd

Browse files
committed
feat: fallback to LogiPlayLedsDInput when SDK can't detect G923
When LogiIsConnected returns NO (SDK doesn't know G923 PID), create a real DirectInput device by loading system dinput8.dll directly and pass it to LogiPlayLedsDInput. This bypasses the SDK's internal PID check while still using its IPC to G Hub for LED control.
1 parent a72b6ce commit 540a7dd

File tree

1 file changed

+151
-74
lines changed

1 file changed

+151
-74
lines changed

Common Files/LogitechLED.cpp

Lines changed: 151 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,12 @@
77
#include <stdio.h>
88
#include <stdarg.h>
99

10+
#define DIRECTINPUT_VERSION 0x0800
11+
#include <dinput.h>
12+
1013
#pragma comment(lib, "hid.lib")
1114
#pragma comment(lib, "setupapi.lib")
15+
#pragma comment(lib, "dxguid.lib")
1216

1317
static const USHORT LOGITECH_VID = 0x046D;
1418
static const USHORT KNOWN_PIDS[] = { 0xC26E, 0xC26D, 0xC267, 0xC266, 0xC24F };
@@ -48,15 +52,19 @@ typedef bool (__cdecl *LogiSteeringInitWithWindow_t)(bool, HWND);
4852
typedef bool (__cdecl *LogiUpdate_t)();
4953
typedef bool (__cdecl *LogiIsConnected_t)(int);
5054
typedef bool (__cdecl *LogiPlayLeds_t)(int, float, float, float);
55+
typedef bool (__cdecl *LogiPlayLedsDInput_t)(LPDIRECTINPUTDEVICE8, float, float, float);
5156
typedef void (__cdecl *LogiSteeringShutdown_t)();
5257

5358
static LogiSteeringInit_t g_SteeringInit = NULL;
5459
static LogiSteeringInitWithWindow_t g_SteeringInitWithWindow = NULL;
5560
static LogiUpdate_t g_SteeringUpdate = NULL;
5661
static LogiIsConnected_t g_IsConnected = NULL;
5762
static LogiPlayLeds_t g_PlayLeds = NULL;
63+
static LogiPlayLedsDInput_t g_PlayLedsDInput = NULL;
5864
static LogiSteeringShutdown_t g_SteeringShutdown = NULL;
5965
static bool g_SteeringNeedsLateInit = false;
66+
static LPDIRECTINPUTDEVICE8A g_realDIDevice = NULL;
67+
static LPDIRECTINPUT8A g_realDI = NULL;
6068

6169
// Bypass flag declared in DllMain.cpp
6270
extern volatile bool g_bypassDIWrapper;
@@ -106,8 +114,12 @@ LogitechLED::~LogitechLED()
106114

107115
void LogitechLED::Close()
108116
{
109-
if (m_method == METHOD_STEERING_SDK && g_SteeringShutdown)
110-
g_SteeringShutdown();
117+
if (m_method == METHOD_STEERING_SDK)
118+
{
119+
if (g_SteeringShutdown) g_SteeringShutdown();
120+
if (g_realDIDevice) { g_realDIDevice->Release(); g_realDIDevice = NULL; }
121+
if (g_realDI) { g_realDI->Release(); g_realDI = NULL; }
122+
}
111123

112124
if (m_method == METHOD_SDK && g_LedShutdown)
113125
g_LedShutdown();
@@ -137,6 +149,7 @@ void LogitechLED::Close()
137149
g_SteeringUpdate = NULL;
138150
g_IsConnected = NULL;
139151
g_PlayLeds = NULL;
152+
g_PlayLedsDInput = NULL;
140153
g_SteeringShutdown = NULL;
141154
g_SteeringNeedsLateInit = false;
142155
g_LedInit = NULL;
@@ -269,6 +282,7 @@ bool LogitechLED::TrySteeringSDK()
269282
g_IsConnected = (LogiIsConnected_t)GetProcAddress(m_steeringDll, connNames[i]);
270283
for (int i = 0; ledsNames[i] && !g_PlayLeds; i++)
271284
g_PlayLeds = (LogiPlayLeds_t)GetProcAddress(m_steeringDll, ledsNames[i]);
285+
g_PlayLedsDInput = (LogiPlayLedsDInput_t)GetProcAddress(m_steeringDll, "LogiPlayLedsDInput");
272286
for (int i = 0; shutNames[i] && !g_SteeringShutdown; i++)
273287
g_SteeringShutdown = (LogiSteeringShutdown_t)GetProcAddress(m_steeringDll, shutNames[i]);
274288

@@ -278,6 +292,7 @@ bool LogitechLED::TrySteeringSDK()
278292
Log(" Update: %s", g_SteeringUpdate ? "FOUND" : "not found");
279293
Log(" IsConnected: %s", g_IsConnected ? "FOUND" : "not found");
280294
Log(" PlayLeds: %s", g_PlayLeds ? "FOUND" : "not found");
295+
Log(" PlayLedsDInput: %s", g_PlayLedsDInput ? "FOUND" : "not found");
281296
Log(" Shutdown: %s", g_SteeringShutdown ? "FOUND" : "not found");
282297

283298
if (!g_SteeringUpdate || !g_IsConnected || !g_PlayLeds)
@@ -351,37 +366,126 @@ bool LogitechLED::TrySteeringSDK()
351366
bool connected = g_IsConnected(0);
352367
Log(" LogiIsConnected(0) -> %s", connected ? "YES" : "NO");
353368

354-
if (!connected)
369+
if (connected)
355370
{
356-
Log(" Wheel not connected via this SDK");
357-
if (g_SteeringShutdown) g_SteeringShutdown();
358-
FreeLibrary(m_steeringDll);
359-
m_steeringDll = NULL;
360-
return false;
361-
}
371+
// Standard path: SDK found the wheel by index
372+
g_SteeringUpdate();
373+
bool led = g_PlayLeds(0, 100.0f, 0.0f, 100.0f);
374+
Log(" LogiPlayLeds(0, 100, 0, 100) -> %s *** ALL LEDs ***", led ? "OK" : "FAIL");
362375

363-
// Test LEDs
364-
g_SteeringUpdate();
365-
bool led = g_PlayLeds(0, 100.0f, 0.0f, 100.0f);
366-
Log(" LogiPlayLeds(0, 100, 0, 100) -> %s *** ALL LEDs ***", led ? "OK" : "FAIL");
376+
if (led)
377+
{
378+
Sleep(1000);
379+
g_SteeringUpdate();
380+
g_PlayLeds(0, 0.0f, 0.0f, 100.0f);
381+
m_method = METHOD_STEERING_SDK;
382+
m_available = true;
383+
Log("=== LED CONTROL ACTIVE (Steering Wheel SDK) ===");
384+
return true;
385+
}
386+
Log(" PlayLeds failed despite connection");
387+
}
367388

368-
if (!led)
389+
// Fallback: SDK doesn't know G923 PID, try LogiPlayLedsDInput
390+
// Create a REAL DirectInput device bypassing our wrapper
391+
if (g_PlayLedsDInput)
369392
{
370-
Log(" PlayLeds failed");
371-
if (g_SteeringShutdown) g_SteeringShutdown();
372-
FreeLibrary(m_steeringDll);
373-
m_steeringDll = NULL;
374-
return false;
375-
}
393+
Log(" Trying LogiPlayLedsDInput fallback...");
376394

377-
Sleep(1000);
378-
g_SteeringUpdate();
379-
g_PlayLeds(0, 0.0f, 0.0f, 100.0f);
395+
// Load the real system dinput8.dll
396+
char sysDir[MAX_PATH];
397+
GetSystemDirectoryA(sysDir, MAX_PATH);
398+
strcat_s(sysDir, MAX_PATH, "\\dinput8.dll");
380399

381-
m_method = METHOD_STEERING_SDK;
382-
m_available = true;
383-
Log("=== LED CONTROL ACTIVE (Steering Wheel SDK) ===");
384-
return true;
400+
typedef HRESULT (WINAPI *DI8Create_t)(HINSTANCE, DWORD, REFIID, LPVOID*, LPUNKNOWN);
401+
HMODULE realDIDll = LoadLibraryA(sysDir);
402+
if (!realDIDll)
403+
{
404+
Log(" Failed to load real dinput8.dll from %s", sysDir);
405+
}
406+
else
407+
{
408+
DI8Create_t realCreate = (DI8Create_t)GetProcAddress(realDIDll, "DirectInput8Create");
409+
if (!realCreate)
410+
{
411+
Log(" DirectInput8Create not found in real dll");
412+
FreeLibrary(realDIDll);
413+
}
414+
else
415+
{
416+
HRESULT hr = realCreate(GetModuleHandle(NULL), DIRECTINPUT_VERSION,
417+
IID_IDirectInput8A, (LPVOID*)&g_realDI, NULL);
418+
Log(" Real DirectInput8Create -> 0x%08lX", hr);
419+
420+
if (SUCCEEDED(hr) && g_realDI)
421+
{
422+
// Enumerate to find a Logitech wheel
423+
struct EnumCtx { GUID guid; bool found; };
424+
EnumCtx ctx = {{0}, false};
425+
426+
g_realDI->EnumDevices(DI8DEVCLASS_GAMECTRL,
427+
[](const DIDEVICEINSTANCEA* inst, VOID* pCtx) -> BOOL {
428+
EnumCtx* c = (EnumCtx*)pCtx;
429+
USHORT vid = LOWORD(inst->guidProduct.Data1);
430+
USHORT pid = HIWORD(inst->guidProduct.Data1);
431+
Log(" DI device: VID=0x%04X PID=0x%04X '%s'",
432+
vid, pid, inst->tszInstanceName);
433+
if (vid == 0x046D && IsKnownPID(pid))
434+
{
435+
c->guid = inst->guidInstance;
436+
c->found = true;
437+
return DIENUM_STOP;
438+
}
439+
return DIENUM_CONTINUE;
440+
}, &ctx, DIEDFL_ATTACHEDONLY);
441+
442+
if (!ctx.found)
443+
{
444+
Log(" No Logitech wheel found via real DirectInput");
445+
g_realDI->Release();
446+
g_realDI = NULL;
447+
}
448+
else
449+
{
450+
hr = g_realDI->CreateDevice(ctx.guid, &g_realDIDevice, NULL);
451+
Log(" CreateDevice -> 0x%08lX", hr);
452+
453+
if (SUCCEEDED(hr) && g_realDIDevice)
454+
{
455+
// Test LogiPlayLedsDInput
456+
g_SteeringUpdate();
457+
bool led = g_PlayLedsDInput(g_realDIDevice, 100.0f, 0.0f, 100.0f);
458+
Log(" LogiPlayLedsDInput(dev, 100, 0, 100) -> %s *** ALL LEDs ***",
459+
led ? "OK" : "FAIL");
460+
461+
if (led)
462+
{
463+
Sleep(1000);
464+
g_SteeringUpdate();
465+
g_PlayLedsDInput(g_realDIDevice, 0.0f, 0.0f, 100.0f);
466+
m_method = METHOD_STEERING_SDK;
467+
m_available = true;
468+
Log("=== LED CONTROL ACTIVE (Steering SDK + DInput) ===");
469+
return true;
470+
}
471+
472+
// Cleanup on failure
473+
g_realDIDevice->Release();
474+
g_realDIDevice = NULL;
475+
}
476+
g_realDI->Release();
477+
g_realDI = NULL;
478+
}
479+
}
480+
}
481+
}
482+
}
483+
484+
Log(" Steering SDK exhausted - falling through");
485+
if (g_SteeringShutdown) g_SteeringShutdown();
486+
FreeLibrary(m_steeringDll);
487+
m_steeringDll = NULL;
488+
return false;
385489
}
386490

387491
g_bypassDIWrapper = false; // Restore wrapper
@@ -824,57 +928,30 @@ bool LogitechLED::SetLEDsFromPercent(double percent)
824928
if (percent > 1.0) percent = 1.0;
825929

826930
// For steering SDK, pass percentage directly as RPM for smooth LED progression
827-
if (m_method == METHOD_STEERING_SDK && g_PlayLeds && g_SteeringUpdate)
931+
if (m_method == METHOD_STEERING_SDK && g_SteeringUpdate)
828932
{
829-
// Deferred init: game window exists now
830-
if (g_SteeringNeedsLateInit)
831-
{
832-
g_SteeringNeedsLateInit = false;
833-
bool ok = false;
834-
835-
HWND fg = GetForegroundWindow();
836-
if (fg && g_SteeringInitWithWindow)
837-
{
838-
ok = g_SteeringInitWithWindow(false, fg);
839-
Log(" LATE InitWithWindow(false, hwnd=0x%p) -> %s", fg, ok ? "OK" : "FAIL");
840-
}
841-
if (!ok && g_SteeringInit)
842-
{
843-
ok = g_SteeringInit(false);
844-
Log(" LATE LogiSteeringInitialize(false) -> %s", ok ? "OK" : "FAIL");
845-
}
846-
847-
if (ok)
848-
{
849-
for (int retry = 0; retry < 10; retry++)
850-
{
851-
Sleep(200);
852-
g_SteeringUpdate();
853-
if (g_IsConnected(0))
854-
{
855-
Log(" LATE: Wheel connected (after %d updates)", retry + 1);
856-
break;
857-
}
858-
}
859-
Log(" LATE: LogiIsConnected(0) -> %s", g_IsConnected(0) ? "YES" : "NO");
860-
}
861-
else
862-
{
863-
Log(" LATE init also failed - steering SDK unavailable");
864-
m_method = METHOD_NONE;
865-
m_available = false;
866-
return false;
867-
}
868-
}
869-
870933
float rpm = (float)(percent * 100.0);
871934
g_SteeringUpdate();
872-
bool ok = g_PlayLeds(0, rpm, 0.0f, 100.0f);
873935

874-
g_setLedsCallCount++;
875-
if (g_setLedsCallCount <= 20 || !ok)
876-
Log("SetLEDsFromPercent(%.2f) [SteeringSDK rpm=%.0f] -> %s (#%d)",
877-
percent, rpm, ok ? "OK" : "FAIL", g_setLedsCallCount);
936+
bool ok = false;
937+
938+
// Prefer LogiPlayLedsDInput (works without SDK device detection)
939+
if (g_PlayLedsDInput && g_realDIDevice)
940+
{
941+
ok = g_PlayLedsDInput(g_realDIDevice, rpm, 0.0f, 100.0f);
942+
g_setLedsCallCount++;
943+
if (g_setLedsCallCount <= 20 || !ok)
944+
Log("SetLEDsFromPercent(%.2f) [DInput rpm=%.0f] -> %s (#%d)",
945+
percent, rpm, ok ? "OK" : "FAIL", g_setLedsCallCount);
946+
}
947+
else if (g_PlayLeds)
948+
{
949+
ok = g_PlayLeds(0, rpm, 0.0f, 100.0f);
950+
g_setLedsCallCount++;
951+
if (g_setLedsCallCount <= 20 || !ok)
952+
Log("SetLEDsFromPercent(%.2f) [SteeringSDK rpm=%.0f] -> %s (#%d)",
953+
percent, rpm, ok ? "OK" : "FAIL", g_setLedsCallCount);
954+
}
878955

879956
return ok;
880957
}

0 commit comments

Comments
 (0)