11#include " LogitechLED.h"
2+ #include " LogitechSDK/LogitechSteeringWheelLib.h"
23
34#include < stdio.h>
45#include < stdarg.h>
56
6- // --- Logitech LED Escape protocol (from SDK "Independent" sample) ---
7-
8- static const DWORD ESCAPE_COMMAND_LEDS = 0 ;
9- static const DWORD LEDS_VERSION_NUMBER = 0x00000001 ;
10-
11- struct LedsRpmData
12- {
13- FLOAT currentRPM;
14- FLOAT rpmFirstLedTurnsOn;
15- FLOAT rpmRedLine;
16- };
17-
18- struct WheelData
19- {
20- DWORD size;
21- DWORD versionNbr;
22- LedsRpmData rpmData;
23- };
24-
25- // --- Known Logitech wheel VID/PIDs ---
26-
27- static const DWORD LOGITECH_VID = 0x046D ;
28- static const DWORD KNOWN_PIDS[] = {
29- 0xC24F , // G29
30- 0xC260 , // G920 (no RPM LEDs but detected for completeness)
31- 0xC262 , // G920 Xbox
32- 0xC266 , // G923 PS
33- 0xC267 , // G923 PS (alt)
34- 0xC26D , // G923 Xbox (alt PID)
35- 0xC26E , // G923 Xbox
36- };
37- static const int NUM_PIDS = sizeof (KNOWN_PIDS) / sizeof (KNOWN_PIDS[0 ]);
38-
397// --- Logging ---
408
419static FILE* g_logFile = NULL ;
@@ -56,20 +24,10 @@ static void Log(const char* fmt, ...)
5624 OutputDebugStringA (" \n " );
5725}
5826
59- static bool IsKnownPID (DWORD pid)
60- {
61- for (int i = 0 ; i < NUM_PIDS; i++)
62- if (KNOWN_PIDS[i] == pid) return true ;
63- return false ;
64- }
65-
6627// --- LogitechLED ---
6728
6829LogitechLED::LogitechLED ()
69- : m_dinputDll(NULL )
70- , m_pDI(NULL )
71- , m_pDevice(NULL )
72- , m_available(false )
30+ : m_available(false )
7331{
7432}
7533
@@ -80,196 +38,66 @@ LogitechLED::~LogitechLED()
8038
8139void LogitechLED::Close ()
8240{
83- if (m_pDevice)
84- {
85- m_pDevice->Unacquire ();
86- m_pDevice->Release ();
87- m_pDevice = NULL ;
88- }
89-
90- if (m_pDI)
91- {
92- m_pDI->Release ();
93- m_pDI = NULL ;
94- }
95-
96- if (m_dinputDll)
41+ if (m_available)
9742 {
98- FreeLibrary (m_dinputDll);
99- m_dinputDll = NULL ;
43+ LogiSteeringShutdown ();
44+ m_available = false ;
45+ Log (" LogitechLED shutdown" );
10046 }
101-
102- m_available = false ;
10347}
10448
10549bool LogitechLED::IsAvailable () const
10650{
10751 return m_available;
10852}
10953
110- // --- Device enumeration callback ---
111-
112- BOOL CALLBACK LogitechLED::EnumDevicesCallback (LPCDIDEVICEINSTANCEA lpddi, LPVOID pvRef)
113- {
114- EnumContext* ctx = (EnumContext*)pvRef;
115-
116- DWORD vid = LOWORD (lpddi->guidProduct .Data1 );
117- DWORD pid = HIWORD (lpddi->guidProduct .Data1 );
118-
119- Log (" Enum: VID=0x%04X PID=0x%04X Name='%s'" , vid, pid, lpddi->tszProductName );
120-
121- if (vid == LOGITECH_VID && IsKnownPID (pid))
122- {
123- Log (" -> Logitech wheel found!" );
124- ctx->deviceGuid = lpddi->guidInstance ;
125- ctx->found = true ;
126- return DIENUM_STOP;
127- }
128-
129- return DIENUM_CONTINUE;
130- }
131-
132- // --- Init ---
133-
13454bool LogitechLED::Init ()
13555{
136- Log (" LogitechLED::Init() - DirectInput Escape mode " );
56+ Log (" LogitechLED::Init() - Logitech SDK (static link) " );
13757
13858 if (m_available)
13959 return true ;
14060
141- // Load the REAL dinput8.dll from System32 (not our wrapper)
142- char sysDir[MAX_PATH];
143- GetSystemDirectoryA (sysDir, MAX_PATH);
144- strcat_s (sysDir, " \\ dinput8.dll" );
145-
146- m_dinputDll = LoadLibraryA (sysDir);
147- if (!m_dinputDll)
148- {
149- Log (" Failed to load real dinput8.dll from %s (err=%lu)" , sysDir, GetLastError ());
150- return false ;
151- }
152-
153- Log (" Real dinput8.dll loaded from %s" , sysDir);
154-
155- // Get the real DirectInput8Create
156- typedef HRESULT (WINAPI* PFN_DirectInput8Create)(HINSTANCE, DWORD, REFIID, LPVOID*, LPUNKNOWN);
157- PFN_DirectInput8Create pfnCreate = (PFN_DirectInput8Create)GetProcAddress (m_dinputDll, " DirectInput8Create" );
158- if (!pfnCreate)
159- {
160- Log (" DirectInput8Create not found in real dinput8.dll" );
161- Close ();
162- return false ;
163- }
61+ // Try init with foreground window first, then without
62+ HWND hwnd = GetForegroundWindow ();
63+ bool initOk = false ;
16464
165- // Create DirectInput interface
166- HRESULT hr = pfnCreate (GetModuleHandle (NULL ), DIRECTINPUT_VERSION, IID_IDirectInput8A, (LPVOID*)&m_pDI, NULL );
167- if (FAILED (hr))
65+ if (hwnd)
16866 {
169- Log (" DirectInput8Create failed (hr=0x%08X)" , hr);
170- Close ();
171- return false ;
67+ Log (" Trying LogiSteeringInitializeWithWindow (hwnd=%p)" , hwnd);
68+ initOk = LogiSteeringInitializeWithWindow (false , hwnd);
17269 }
17370
174- Log (" DirectInput8 interface created" );
175-
176- // Enumerate game controllers to find Logitech wheel
177- EnumContext ctx = {};
178- ctx.found = false ;
179-
180- Log (" Enumerating game controllers..." );
181- m_pDI->EnumDevices (DI8DEVCLASS_GAMECTRL, EnumDevicesCallback, &ctx, DIEDFL_ATTACHEDONLY);
182-
183- if (!ctx.found )
71+ if (!initOk)
18472 {
185- Log (" No Logitech wheel found" );
186- Close ();
187- return false ;
73+ Log (" Trying LogiSteeringInitialize (no window)" );
74+ initOk = LogiSteeringInitialize (false );
18875 }
18976
190- // Create device
191- hr = m_pDI->CreateDevice (ctx.deviceGuid , &m_pDevice, NULL );
192- if (FAILED (hr))
77+ if (!initOk)
19378 {
194- Log (" CreateDevice failed (hr=0x%08X)" , hr);
195- Close ();
79+ Log (" SDK init failed - is G Hub running? Is LogitechSteeringWheelEnginesWrapper.dll present?" );
19680 return false ;
19781 }
19882
199- // Set data format
200- hr = m_pDevice->SetDataFormat (&c_dfDIJoystick2);
201- if (FAILED (hr))
202- {
203- Log (" SetDataFormat failed (hr=0x%08X)" , hr);
204- Close ();
205- return false ;
206- }
83+ Log (" SDK initialized" );
20784
208- // Set cooperative level: background + non-exclusive (don't steal from game)
209- HWND hwnd = GetForegroundWindow ();
210- if (!hwnd) hwnd = GetDesktopWindow ();
85+ // Give SDK time to enumerate
86+ LogiUpdate ();
21187
212- hr = m_pDevice->SetCooperativeLevel (hwnd, DISCL_BACKGROUND | DISCL_NONEXCLUSIVE);
213- if (FAILED (hr))
88+ if (LogiIsConnected (0 ))
21489 {
215- Log ( " SetCooperativeLevel failed (hr=0x%08X) " , hr) ;
216- Close ( );
217- return false ;
90+ wchar_t name[ 256 ] = {} ;
91+ LogiGetFriendlyProductName ( 0 , name, 256 );
92+ Log ( " Wheel connected at index 0: %ls " , name) ;
21893 }
219-
220- // Acquire the device
221- hr = m_pDevice->Acquire ();
222- if (FAILED (hr))
94+ else
22395 {
224- Log (" Acquire failed (hr=0x%08X)" , hr);
225- Close ();
226- return false ;
227- }
228-
229- Log (" Device acquired, testing Escape LED command..." );
230-
231- // Test: try to clear LEDs to verify Escape() works
232- if (!PlayLedsEscape (0 .0f , 200 .0f , 1000 .0f ))
233- {
234- Log (" Escape LED command not supported on this device" );
235- Close ();
236- return false ;
96+ Log (" No wheel detected yet (will retry on first LED update)" );
23797 }
23898
23999 m_available = true ;
240- Log (" LogitechLED ready (DirectInput Escape mode)" );
241- return true ;
242- }
243-
244- // --- LED control via Escape ---
245-
246- bool LogitechLED::PlayLedsEscape (float currentRPM, float rpmFirstLed, float rpmRedLine)
247- {
248- if (!m_pDevice)
249- return false ;
250-
251- WheelData wheelData;
252- ZeroMemory (&wheelData, sizeof (wheelData));
253- wheelData.size = sizeof (WheelData);
254- wheelData.versionNbr = LEDS_VERSION_NUMBER;
255- wheelData.rpmData .currentRPM = currentRPM;
256- wheelData.rpmData .rpmFirstLedTurnsOn = rpmFirstLed;
257- wheelData.rpmData .rpmRedLine = rpmRedLine;
258-
259- DIEFFESCAPE escape;
260- ZeroMemory (&escape, sizeof (escape));
261- escape.dwSize = sizeof (DIEFFESCAPE);
262- escape.dwCommand = ESCAPE_COMMAND_LEDS;
263- escape.lpvInBuffer = &wheelData;
264- escape.cbInBuffer = sizeof (wheelData);
265-
266- HRESULT hr = m_pDevice->Escape (&escape);
267- if (FAILED (hr))
268- {
269- Log (" Escape failed (hr=0x%08X)" , hr);
270- return false ;
271- }
272-
100+ Log (" LogitechLED ready" );
273101 return true ;
274102}
275103
@@ -281,9 +109,13 @@ bool LogitechLED::SetLEDsFromPercent(double percent)
281109 if (percent < 0.0 ) percent = 0.0 ;
282110 if (percent > 1.0 ) percent = 1.0 ;
283111
112+ LogiUpdate ();
113+
284114 // Map FFB strength (0.0-1.0) to RPM values
115+ // rpmFirstLed=200: first LED at ~20% strength
116+ // rpmRedLine=1000: all LEDs at 100% strength
285117 float currentRPM = (float )(percent * 1000.0 );
286- return PlayLedsEscape ( currentRPM, 200 .0f , 1000 .0f );
118+ return LogiPlayLeds ( 0 , currentRPM, 200 .0f , 1000 .0f );
287119}
288120
289121bool LogitechLED::SetLEDs (BYTE ledMask)
@@ -303,5 +135,6 @@ bool LogitechLED::ClearLEDs()
303135 if (!m_available)
304136 return false ;
305137
306- return PlayLedsEscape (0 .0f , 200 .0f , 1000 .0f );
138+ LogiUpdate ();
139+ return LogiPlayLeds (0 , 0 .0f , 200 .0f , 1000 .0f );
307140}
0 commit comments