Hi, I wanted to add support for gamepads in a game, and started to look at XInput, and the doc mentioned a newer API that should support all gamepads, not just xbox ones.
https://learn.microsoft.com/en-us/gaming/gdk/_content/gc/input/overviews/input-overview
I tried it even if I don't like that it most likely requires the user to install some redists, and got it to work. But I just don't get how to have two controllers working.
The examples I found in the doc use GetCurrentReading to discover a controller. By asking for a gamepad reading and passing 0 as the device, the function will return an input that matches gamepad and we can get the device from the reading. But when we want to detect another controller, if we pass 0 as the device it will (or can I don't know if it will do it 100% of the time) return a reading for the first controller. I though that I could use GetNextReading or GetPreviousReading but passing 0 for the device in those function makes the function return NOT IMPLEMENTED.
Does anyone knows how to get several controller to work ? Is GameInput any good ? In the doc several function are labeled as not implemented, which doesn't inspire confidence.
Here is a sample application if anyone wants to try stuff (all the code for GameInput comes from the doc). Note that you'll need Microsoft GDK, and call C:\Program Files (x86)\Microsoft GDK\Command Prompts\GamingDesktopVars.cmd GamingDesktopVS2022
/* cl main.c -Zi -Od -Fegameinput -link -subsystem:windows user32.lib gameinput.lib */ #include <windows.h> #include <stdbool.h> #define COBJMACROS #include <gameinput.h> int running = 1; IGameInput* g_gameInput = 0; IGameInputDevice* g_gamepad = 0; HRESULT InitializeInput( void ) { HRESULT result = GameInputCreate( &g_gameInput ); return result; } void ShutdownInput( void ) { if ( g_gamepad ) { IGameInputDevice_Release( g_gamepad ); } if ( g_gameInput ) { IGameInput_Release( g_gameInput ); } } void PollGamepadInput( void ) { // Ask for the latest reading from devices that provide fixed-format // gamepad state. If a device has been assigned to g_gamepad, filter // readings to just the ones coming from that device. Otherwise, if // g_gamepad is null, it will allow readings from any device. IGameInputReading * reading; HRESULT success = IGameInput_GetCurrentReading( g_gameInput, GameInputKindGamepad, g_gamepad, &reading ); if ( SUCCEEDED( success ) ) { // If no device has been assigned to g_gamepad yet, set it // to the first device we receive input from. (This must be // the one the player is using because it's generating input.) if ( !g_gamepad ) { IGameInputReading_GetDevice( reading, &g_gamepad ); } // Retrieve the fixed-format gamepad state from the reading. GameInputGamepadState state; IGameInputReading_GetGamepadState( reading, &state ); IGameInputReading_Release( reading ); // Application-specific code to process the gamepad state goes here. OutputDebugStringW( L"got state\n" ); if ( state.buttons /* & GameInputGamepadA */ ) { OutputDebugStringW( L"A pressed\n" ); running = 0; } } // If an error is returned from GetCurrentReading(), it means the // gamepad we were reading from has disconnected. Reset the // device pointer, and go back to looking for an active gamepad. else if (g_gamepad) { IGameInputDevice_Release( g_gamepad ); g_gamepad = 0; } } LRESULT CALLBACK window_proc( HWND window, UINT message, WPARAM wParam, LPARAM lParam ) { LRESULT result = 0; switch ( message ) { case WM_DESTROY: case WM_CLOSE: { running = 0; } break; default: { result = DefWindowProcW( window, message, wParam, lParam ); } break; } return result; } int APIENTRY WinMain( HINSTANCE hInst, HINSTANCE hInstPrev, PSTR cmdline, int cmdshow ) { WNDCLASSW window_class = { 0 }; window_class.lpfnWndProc = window_proc; window_class.hInstance = hInst; window_class.lpszClassName = L"gameinput"; RegisterClassW( &window_class ); HWND window = CreateWindowW( L"gameinput", L"gameinput", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, 0, 0, 0, 0 ); ShowWindow( window, SW_SHOW ); InitializeInput( ); while ( running ) { MSG message; // GetMessage( &message, 0, 0, 0 ); // TranslateMessage( &message ); // DispatchMessageW( &message ); message = ( MSG ) { 0 }; while ( PeekMessageW( &message, 0, 0, 0, PM_REMOVE ) ) { TranslateMessage( &message ); DispatchMessageW( &message ); } PollGamepadInput( ); } ShutdownInput( ); return 0; }