The online racing simulator
Driving when LFS loses focus...
(12 posts, started )
Driving when LFS loses focus...
Is this possible?

With my AI project it would be super useful to be able to have LFS being controlled by the virtual steering wheel even when the LFS window doesn't have primary focus. Does anyone know of a way to do this?
If you'll be passing the steering commands through emulated joystick device then maybe you can intercept the SetCooperativeLevel DInput call and replace the flags with DISCL_BACKGROUND | DISCL_EXCLUSIVE. IIRC something similar worked for me in another project. You can get a dinput8.dll proxy template from here: http://mikoweb.eu/index.php?node=28
MadCatX that was a brilliant idea and was quite easy to test with that template. Unfortunately it didn't work. I did ensure that the DLL was being injected properly by building it with a message box that did appear, (although LFS would crash with the MessageBox popping up I do assume that it was working without the message box).

I think LFS is detecting that it is not the active window, and will then skip the input updates. I could be wrong, but it feels like this is what is happening, and I'm not sure there is a way around that without support from the developers.
I tried MadCatX suggestion and it did work! (in most cases)

Problem is that in some cases when LFS losses focus it will Release DirectInput device ...
Interesting. I was unable to get it to work (Windows XP) although I was trying to click from LFS to a different application. Do you know what situations provoke the device to be released?
Can't tell for sure...

Here is short video, I'm moving throttle all the time :



Create a console for easier function tracing and put some printfs/couts into them.

AllocConsole();
freopen("conin$", "r", stdin);
freopen("conout$", "w", stdout);
freopen("conout$", "w", stderr);
SetConsoleTitle("Console Output");

I'll give that a go a bit later. I may just have to live with it as it is, but it would be really nice to be able to control things through the AIRS application without having the car careen off course when leaving it running. Worst case I think I can keep dealing with it.
Setting both EXCLUSIVE and BACKGROUND flags for keyboard or mouse is invalid and DInput will throw an error when you try that. You might therefore want to check that you're trying to modify the cooperative level only on the device you want to use. The handling of these invalid flags is perhaps different on WinXP which is why LFS crashed for you. If it's any help, I did it like this when I wanted to check for a Logitech G27 in my proxied dinput8.dll. BTW, I found out that calling Beep() from a proxy DLL is a crude but effective way to check that the code reaches a certain place Smile

1) An application has to enumerate the devices first so let's take advantage of that and enumerate them along with it

HRESULT myIDirectInput8::EnumDevices(DWORD a, LPDIENUMDEVICESCALLBACKW b, LPVOID c, DWORD d)
{
m_pIDirectInput8->EnumDevices(a, MyDevEnumCallBack, c, d);
return m_pIDirectInput8->EnumDevices(a, b, c, d);
}

2) Get some interesting info about a device during the enumeration

BOOL CALLBACK myIDirectInput8::MyDevEnumCallBack(const DIDEVICEINSTANCE* inst, VOID* context)
{
UNREFERENCED_PARAMETER(context);

/* Here is where we store the GUIDs of devices that we want to manipulate with later */
extern std::vector<GUID> gl_myG27Instance;

/* Check the device's identity */
if ((inst->guidProduct.Data1 == G27ID) /* || (inst->guidProduct.Data1 == DFPID) */) {
/* Check if we already have this device added */
if (gl_myG27Instance.size() > 20)
return DIENUM_CONTINUE;

for (std::vector<GUID>::const_iterator cit = gl_myG27Instance.begin(); cit != gl_myG27Instance.end(); cit++) {
if (IsEqualCLSID(inst->guidProduct, *cit))
return DIENUM_CONTINUE;
}

gl_myG27Instance.push_back(inst->guidInstance);
}

return DIENUM_CONTINUE;
}

3) OK, now we know the GUIDs that reference the devices we're interested in so let's intercept only those devices */

HRESULT myIDirectInput8::CreateDevice(REFGUID a, LPDIRECTINPUTDEVICE8W *b, LPUNKNOWN c)
{
extern std::vector<GUID> gl_myG27Instance;
std::vector<GUID>::const_iterator cit;
for (cit = gl_myG27Instance.begin(); cit < gl_myG27Instance.end(); cit++) {
if (IsEqualCLSID(a, *cit)) {
/* A G27 (or DFP) found, take control over this device */
// global var
extern myIDirectInputDevice8* gl_pmyIDirectInputDevice8Array[];

LogMessage(L"DI::CreateDevice> Creating DIDevice");

// we intercept this call and provide our own "fake" Device Object
HRESULT hres = m_pIDirectInput8->CreateDevice(a,b,c);

/*
* Check for error
*/

// find free Device slot
int i;
for (i=0; i<MAXNUMBER_DEVICES; i++)
if (gl_pmyIDirectInputDevice8Array[i] == NULL) break;

/*
* Should return an error if i == MAXNUMBER_DEVICES
*/

// Create our own Device object and store it in global pointer
// note: the object will delete itself once Ref count is zero (similar to COM objects)
gl_pmyIDirectInputDevice8Array[i] = new myIDirectInputDevice8(*b, i, *cit);

// store our pointer (the fake one) for returning it to the calling progam
*b = gl_pmyIDirectInputDevice8Array[i];

LPOLESTR guidHash;
HRESULT guidHRes = StringFromCLSID(a, &guidHash);
if (guidHRes == S_OK) {
std::wostringstream oss;
oss << L"DI::CreateDevice> Creating DIDevice, GUID " << guidHash;
LogMessage(oss.str().c_str());
CoTaskMemFree(guidHash);
} else
LogMessage(L"(WARN)DI::CreateDevice> Unable to convert GUID to string.");

if (hres == S_OK)
LogMessage(L"DI::CreateDevice> Device sucessfully created.");
else
LogMessage(L"(WARN)DI::CreateDevice> Unable to create device.");

return(hres);
}
}

/* Not a G27 (or DFP), don't touch this device */
LogMessage(L"DI::CreateDevice> Not a G27, ignoring.");
return m_pIDirectInput8->CreateDevice(a, b, c);
}

The code above is far from brilliant but it seems to have solved the problem in my G27 LEDs hack.
It didn't seem to be crashing when I tried with exclusive and background, but only when I added the message box, so I assumed kicking LFS out of focus at that moment caused it to handle a message wrong. I will keep trying though. It would be really awesome to get it working so I won't give up just yet. Thanks for the information. I'm not quite sure what is happening in Step 3 though, although that may be because I should only need to worry about SetCooperativeLevel and not actually in CreateDevice, just check to make sure I'm dealing with the right device, I'll give it a go.
The step 3 either returns the "fake" DirectInputDevice object created by the proxy DLL or returns a "normal" DirectInputDevice object. Only calls made upon the "fake" object (such as SetCooperativeLevel() ) will be intercepted by the proxy DLL. The logic boils down to this:


HRESULT myIDirectInput8::CreateDevice(REFGUID a, LPDIRECTINPUTDEVICE8W *b, LPUNKNOWN c)
{
if (WantToIntercept(a)) {
LPDIRECTINPUTDEVICE8W fake;
HRESULT res = CreateFakeDIObject(&fake); // my own function
gl_myFakeObjectsStorage[FreeSlot] = fake;
*b = fake;
return hres;
}
return CreateDevice(a, b, c); // DirectInput internal function
}

Ahh, I see. Well, in interesting news I have had no issues just calling SetCooperativeLevel() BACKGROUND | EXLCUSIVE on all devices, LFS works just normally.

I even managed, after watching @DANIEL-CRO's video a few times, to successfully control LFS when it did not have focus. However, it will still mess up my AI driver to lose control for the moment it takes to get it to work. Still, better than nothing so I will keep the injected DLL where it is.

I've also tried modifying the AIRS application to hijack the Window Procedure of LFS to intercept the "WM_KILLFOCUS" and "WM_ACTIVATE" messages to attempt to hijack and not let LFS think it is inactive- but this ultimately failed, I think because the LFS window class is created from a different process... Go figure. So I think I've got it working as well as it is going to work.

Essentially what I need to do when changing out is focus an application from the task bar, then click it again to give LFS the focus back, but before LFS gains full focus reclick the task bar a final time and if successful, LFS will still collect input.
This is working a lot better than not at all. It works great when I don't have to click inside to restart the race, reset the car or other issues. Thanks for the help guys!

Driving when LFS loses focus...
(12 posts, started )
FGED GREDG RDFGDR GSFDG