Day 017 DInput Woe

Another one that has me stumped.

I got the keyboard changes for D17 working. I was feeling pretty good until I connected the DInput gamepad. Suddenly the keyboard stopped responding. Except...when I set breakpoints in the code I can see the input get captured and passed right along to the destination. Then I recalled that you have to re-acquire a Dinput device on a WM_ACTIVATEAPP event. But no soap.

My searches keep coming up with nothing or trivial samples.

For now I'm stuck using a single input device.

- tim
If keyboard input get captured and passed right to place where it is processed, then what exactly doesn't work?

Put some breakpoints in place where it is processed and see how data is changed (or not).

Edited by Mārtiņš Možeiko on
With no breakpoints set the keyboard doesn't respond unless the gamepad (Logitech Wingman) is disconnected. Then it runs fine. When the gamepad is connected _it_ works fine. Just not both together.

Aside: the code is C++ and I hesitate to post it here. I don't want to get into a diversion on that. Otherwise I'd post it all.

- tim

p.s. When i trace broth execution paths...they work as expected. Works fine under the debugger but...Schrodinger's cat.

Edited by Timothy McCarthy on Reason: ps
For completeness, I have a test application that interfaces to the Wingman using DirectInput. That program uses the same code (static lib) for the device and the program is in the same solution, etc. So I'm fairly confident the issue is with my version of the code base. The stream code doesn't work with DInput devices so can't verify there.

- tim
Oh, I misunderstood what you were telling. I though game code was not processing input correctly (that's why I told put some breakpoints and debug). If keyboard doesn't respond at all that sounds like DirectInput exclusive mode thing: https://msdn.microsoft.com/en-us/...ary/windows/desktop/ee418998.aspx
Under Windows 2000, acquiring the keyboard in exclusive mode prevents any applications that are subsequently launched from receiving keyboard data.

Is that something you are using?
Yes. The joystick SetCooperativeLevel uses the DISCL_EXCLUSIVE flag.

So I tried DISCL_NONEXCLUSIVE with no luck.

What is peculiar is that I have a Directinput test app that seems to be working fine. This application was created from the MSDN Tutorials. I don't have specific keyboard handling in it but there is a menu and that seems to respond to the keyboard just fine.

My HMH app ("ManufacturedVillain") uses the same library code for DirectInput that the Tutorial does but has different behavior.

I'm gonna try a bit more testing to see if I can narrow the differences between the two down. if that doesn't work then I'll try to figure out how to put the code it Github for inspection.

- tim
I think I tracked down the issue.

With a game pad connected...

In handmade.cpp...
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
void GameUpdateAndRender(/*...*/)
{
	//...

    for (/* all controllers */)
    {
        game_controller_input *Controller = GetController(Input, ControllerIndex);
        if(Controller->IsAnalog)
        {
	    //...process StickAverageX value
        }
        else
        {
            //...process MoveLeft or MoveRight
	}
            //...processs ActionDown
   }
   //...
}
The focus here is that keyboard input for MoveLeft or MoveRight is processed only
for non-analog controllers. ActionDown is for all controllers. (This is where the "root"
problem lies. I was too focused on the platform code when a clear read of the game code
might have saved time.)

There is no check for unconnected controllers but means that unconnected controllers use
the default value for IsAnalog, which is 0 == false so they are processed for keyboard.
Unconnected controllers will never alter MoveLeft, MoveRight, or ActionDown so they have
no effect.

In win32_handmade.cpp...

With a connected controller...but keybaord input only

ActionDown is first set by the keybaord through Win32ProcessPendingMessages,
1
2
3
4
5
6
	//...
    else if(VKCode == VK_DOWN)
    {
        Win32ProcessKeyboardMessage(&KeyboardController->ActionDown, IsDown);
    }
	//...

Next the iteration through the controllers happens. The key is the game pad is
untouched so it remains in a default state. The logic to toggle IsAnalog has no effect
and IsAnalog should be false.

But then gamepad is polled
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
	//...
	Win32ProcessXInputDigitalButton(
		(NewController->StickAverageX < -Threshold) ? 1 : 0,
		&OldController->MoveLeft, 1,
		&NewController->MoveLeft);
	Win32ProcessXInputDigitalButton(
		(NewController->StickAverageX > Threshold) ? 1 : 0,
		&OldController->MoveRight, 1,
		&NewController->MoveRight);

	//...
	Win32ProcessXInputDigitalButton(Pad->wButtons,
		&OldController->ActionDown, XINPUT_GAMEPAD_A,
		&NewController->ActionDown);
	//...
	void Win32ProcessXInputDigitalButton(/*...*/)
	{
	    NewState->EndedDown = ((XInputButtonState & ButtonBit) == ButtonBit);
	    NewState->HalfTransitionCount = (OldState->EndedDown != NewState->EndedDown) ? 1 : 0;
	}
and the keyboard MoveLeft, MoveRight, and ActionDown get over-written.

Finally, the swapping of input controlers at the end of each frame is ok as long as
the keyboard input is sent to the current input controller[0] after controller[0]
is updated with the previous keyboard state.

This explains why in my code when the gamepad is not connected, the keyboard input
is fine but "disappears" when the gamepad is connected.

I think.

- tim