mirror of
				https://github.com/Drezil/imgui.git
				synced 2025-10-26 10:41:10 +01:00 
			
		
		
		
	Backends: SDL3: Fixed gamepad. Added support for disconnection. Added support for multiple gamepads. Added ImGui_ImplSDL3_SetGamepadMode(). (#7180, #3884, #6559, #6890)
This commit is contained in:
		| @@ -22,6 +22,7 @@ | ||||
|  | ||||
| // CHANGELOG | ||||
| // (minor and older changes stripped away, please see git history for details) | ||||
| //  2024-02-13: Inputs: Fixed gamepad support. Handle gamepad disconnection. Added ImGui_ImplSDL3_SetGamepadMode(). | ||||
| //  2023-11-13: Updated for recent SDL3 API changes. | ||||
| //  2023-10-05: Inputs: Added support for extra ImGuiKey values: F13 to F24 function keys, app back/forward keys. | ||||
| //  2023-05-04: Fixed build on Emscripten/iOS/Android. (#6391) | ||||
| @@ -74,6 +75,11 @@ struct ImGui_ImplSDL3_Data | ||||
|     int                     MousePendingLeaveFrame; | ||||
|     bool                    MouseCanUseGlobalState; | ||||
|  | ||||
|     // Gamepad handling | ||||
|     ImVector<SDL_Gamepad*>      Gamepads; | ||||
|     ImGui_ImplSDL3_GamepadMode  GamepadMode; | ||||
|     bool                        WantUpdateGamepadsList; | ||||
|  | ||||
|     ImGui_ImplSDL3_Data()   { memset((void*)this, 0, sizeof(*this)); } | ||||
| }; | ||||
|  | ||||
| @@ -332,6 +338,12 @@ bool ImGui_ImplSDL3_ProcessEvent(const SDL_Event* event) | ||||
|         case SDL_EVENT_WINDOW_FOCUS_LOST: | ||||
|             io.AddFocusEvent(false); | ||||
|             return true; | ||||
|         case SDL_EVENT_GAMEPAD_ADDED: | ||||
|         case SDL_EVENT_GAMEPAD_REMOVED: | ||||
|         { | ||||
|             bd->WantUpdateGamepadsList = true; | ||||
|             return true; | ||||
|         } | ||||
|     } | ||||
|     return false; | ||||
| } | ||||
| @@ -380,6 +392,10 @@ static bool ImGui_ImplSDL3_Init(SDL_Window* window, SDL_Renderer* renderer, void | ||||
|     io.ClipboardUserData = nullptr; | ||||
|     io.SetPlatformImeDataFn = ImGui_ImplSDL3_SetPlatformImeData; | ||||
|  | ||||
|     // Gamepad handling | ||||
|     bd->GamepadMode = ImGui_ImplSDL3_GamepadMode_AutoFirst; | ||||
|     bd->WantUpdateGamepadsList = true; | ||||
|  | ||||
|     // Load mouse cursors | ||||
|     bd->MouseCursors[ImGuiMouseCursor_Arrow] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_ARROW); | ||||
|     bd->MouseCursors[ImGuiMouseCursor_TextInput] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_IBEAM); | ||||
| @@ -447,6 +463,8 @@ bool ImGui_ImplSDL3_InitForOther(SDL_Window* window) | ||||
|     return ImGui_ImplSDL3_Init(window, nullptr, nullptr); | ||||
| } | ||||
|  | ||||
| static void ImGui_ImplSDL3_CloseGamepads(); | ||||
|  | ||||
| void ImGui_ImplSDL3_Shutdown() | ||||
| { | ||||
|     ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData(); | ||||
| @@ -457,7 +475,7 @@ void ImGui_ImplSDL3_Shutdown() | ||||
|         SDL_free(bd->ClipboardTextData); | ||||
|     for (ImGuiMouseCursor cursor_n = 0; cursor_n < ImGuiMouseCursor_COUNT; cursor_n++) | ||||
|         SDL_DestroyCursor(bd->MouseCursors[cursor_n]); | ||||
|     bd->MouseLastCursor = nullptr; | ||||
|     ImGui_ImplSDL3_CloseGamepads(); | ||||
|  | ||||
|     io.BackendPlatformName = nullptr; | ||||
|     io.BackendPlatformUserData = nullptr; | ||||
| @@ -525,50 +543,109 @@ static void ImGui_ImplSDL3_UpdateMouseCursor() | ||||
|     } | ||||
| } | ||||
|  | ||||
| static void ImGui_ImplSDL3_CloseGamepads() | ||||
| { | ||||
|     ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData(); | ||||
|     if (bd->GamepadMode != ImGui_ImplSDL3_GamepadMode_Manual) | ||||
|         for (SDL_Gamepad* gamepad : bd->Gamepads) | ||||
|             SDL_CloseGamepad(gamepad); | ||||
|     bd->Gamepads.resize(0); | ||||
| } | ||||
|  | ||||
| void ImGui_ImplSDL3_SetGamepadMode(ImGui_ImplSDL3_GamepadMode mode, SDL_Gamepad** manual_gamepads_array, int manual_gamepads_count) | ||||
| { | ||||
|     ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData(); | ||||
|     ImGui_ImplSDL3_CloseGamepads(); | ||||
|     if (mode == ImGui_ImplSDL3_GamepadMode_Manual) | ||||
|     { | ||||
|         IM_ASSERT(manual_gamepads_array != nullptr && manual_gamepads_count > 0); | ||||
|         for (int n = 0; n < manual_gamepads_count; n++) | ||||
|             bd->Gamepads.push_back(manual_gamepads_array[n]); | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|         IM_ASSERT(manual_gamepads_array == nullptr && manual_gamepads_count <= 0); | ||||
|         bd->WantUpdateGamepadsList = true; | ||||
|     } | ||||
|     bd->GamepadMode = mode; | ||||
| } | ||||
|  | ||||
| static void ImGui_ImplSDL3_UpdateGamepadButton(ImGui_ImplSDL3_Data* bd, ImGuiIO& io, ImGuiKey key, SDL_GamepadButton button_no) | ||||
| { | ||||
|     bool merged_value = false; | ||||
|     for (SDL_Gamepad* gamepad : bd->Gamepads) | ||||
|         merged_value |= SDL_GetGamepadButton(gamepad, button_no) != 0; | ||||
|     io.AddKeyEvent(key, merged_value); | ||||
| } | ||||
|  | ||||
| static inline float Saturate(float v) { return v < 0.0f ? 0.0f : v  > 1.0f ? 1.0f : v; } | ||||
| static void ImGui_ImplSDL3_UpdateGamepadAnalog(ImGui_ImplSDL3_Data* bd, ImGuiIO& io, ImGuiKey key, SDL_GamepadAxis axis_no, float v0, float v1) | ||||
| { | ||||
|     float merged_value = 0.0f; | ||||
|     for (SDL_Gamepad* gamepad : bd->Gamepads) | ||||
|     { | ||||
|         float vn = Saturate((float)(SDL_GetGamepadAxis(gamepad, axis_no) - v0) / (float)(v1 - v0)); | ||||
|         if (merged_value < vn) | ||||
|             merged_value = vn; | ||||
|     } | ||||
|     io.AddKeyAnalogEvent(key, merged_value > 0.1f, merged_value); | ||||
| } | ||||
|  | ||||
| static void ImGui_ImplSDL3_UpdateGamepads() | ||||
| { | ||||
|     ImGuiIO& io = ImGui::GetIO(); | ||||
|     if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == 0) // FIXME: Technically feeding gamepad shouldn't depend on this now that they are regular inputs. | ||||
|         return; | ||||
|     ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData(); | ||||
|  | ||||
|     // Get gamepad | ||||
|     // Update list of gamepads to use | ||||
|     if (bd->WantUpdateGamepadsList && bd->GamepadMode != ImGui_ImplSDL3_GamepadMode_Manual) | ||||
|     { | ||||
|         ImGui_ImplSDL3_CloseGamepads(); | ||||
|         int sdl_gamepads_count = 0; | ||||
|         SDL_JoystickID* sdl_gamepads = SDL_GetGamepads(&sdl_gamepads_count); | ||||
|         for (int n = 0; n < sdl_gamepads_count; n++) | ||||
|             if (SDL_Gamepad* gamepad = SDL_OpenGamepad(sdl_gamepads[n])) | ||||
|             { | ||||
|                 bd->Gamepads.push_back(gamepad); | ||||
|                 if (bd->GamepadMode == ImGui_ImplSDL3_GamepadMode_AutoFirst) | ||||
|                     break; | ||||
|             } | ||||
|         bd->WantUpdateGamepadsList = false; | ||||
|     } | ||||
|  | ||||
|     // FIXME: Technically feeding gamepad shouldn't depend on this now that they are regular inputs. | ||||
|     if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == 0) | ||||
|         return; | ||||
|     io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad; | ||||
|     SDL_Gamepad* gamepad = SDL_OpenGamepad(0); | ||||
|     if (!gamepad) | ||||
|     if (bd->Gamepads.Size == 0) | ||||
|         return; | ||||
|     io.BackendFlags |= ImGuiBackendFlags_HasGamepad; | ||||
|  | ||||
|     // Update gamepad inputs | ||||
|     #define IM_SATURATE(V)                      (V < 0.0f ? 0.0f : V > 1.0f ? 1.0f : V) | ||||
|     #define MAP_BUTTON(KEY_NO, BUTTON_NO)       { io.AddKeyEvent(KEY_NO, SDL_GetGamepadButton(gamepad, BUTTON_NO) != 0); } | ||||
|     #define MAP_ANALOG(KEY_NO, AXIS_NO, V0, V1) { float vn = (float)(SDL_GetGamepadAxis(gamepad, AXIS_NO) - V0) / (float)(V1 - V0); vn = IM_SATURATE(vn); io.AddKeyAnalogEvent(KEY_NO, vn > 0.1f, vn); } | ||||
|     const int thumb_dead_zone = 8000;           // SDL_gamecontroller.h suggests using this value. | ||||
|     MAP_BUTTON(ImGuiKey_GamepadStart,           SDL_GAMEPAD_BUTTON_START); | ||||
|     MAP_BUTTON(ImGuiKey_GamepadBack,            SDL_GAMEPAD_BUTTON_BACK); | ||||
|     MAP_BUTTON(ImGuiKey_GamepadFaceLeft,        SDL_GAMEPAD_BUTTON_WEST);           // Xbox X, PS Square | ||||
|     MAP_BUTTON(ImGuiKey_GamepadFaceRight,       SDL_GAMEPAD_BUTTON_EAST);           // Xbox B, PS Circle | ||||
|     MAP_BUTTON(ImGuiKey_GamepadFaceUp,          SDL_GAMEPAD_BUTTON_NORTH);          // Xbox Y, PS Triangle | ||||
|     MAP_BUTTON(ImGuiKey_GamepadFaceDown,        SDL_GAMEPAD_BUTTON_SOUTH);          // Xbox A, PS Cross | ||||
|     MAP_BUTTON(ImGuiKey_GamepadDpadLeft,        SDL_GAMEPAD_BUTTON_DPAD_LEFT); | ||||
|     MAP_BUTTON(ImGuiKey_GamepadDpadRight,       SDL_GAMEPAD_BUTTON_DPAD_RIGHT); | ||||
|     MAP_BUTTON(ImGuiKey_GamepadDpadUp,          SDL_GAMEPAD_BUTTON_DPAD_UP); | ||||
|     MAP_BUTTON(ImGuiKey_GamepadDpadDown,        SDL_GAMEPAD_BUTTON_DPAD_DOWN); | ||||
|     MAP_BUTTON(ImGuiKey_GamepadL1,              SDL_GAMEPAD_BUTTON_LEFT_SHOULDER); | ||||
|     MAP_BUTTON(ImGuiKey_GamepadR1,              SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER); | ||||
|     MAP_ANALOG(ImGuiKey_GamepadL2,              SDL_GAMEPAD_AXIS_LEFT_TRIGGER,  0.0f, 32767); | ||||
|     MAP_ANALOG(ImGuiKey_GamepadR2,              SDL_GAMEPAD_AXIS_RIGHT_TRIGGER, 0.0f, 32767); | ||||
|     MAP_BUTTON(ImGuiKey_GamepadL3,              SDL_GAMEPAD_BUTTON_LEFT_STICK); | ||||
|     MAP_BUTTON(ImGuiKey_GamepadR3,              SDL_GAMEPAD_BUTTON_RIGHT_STICK); | ||||
|     MAP_ANALOG(ImGuiKey_GamepadLStickLeft,      SDL_GAMEPAD_AXIS_LEFTX,  -thumb_dead_zone, -32768); | ||||
|     MAP_ANALOG(ImGuiKey_GamepadLStickRight,     SDL_GAMEPAD_AXIS_LEFTX,  +thumb_dead_zone, +32767); | ||||
|     MAP_ANALOG(ImGuiKey_GamepadLStickUp,        SDL_GAMEPAD_AXIS_LEFTY,  -thumb_dead_zone, -32768); | ||||
|     MAP_ANALOG(ImGuiKey_GamepadLStickDown,      SDL_GAMEPAD_AXIS_LEFTY,  +thumb_dead_zone, +32767); | ||||
|     MAP_ANALOG(ImGuiKey_GamepadRStickLeft,      SDL_GAMEPAD_AXIS_RIGHTX, -thumb_dead_zone, -32768); | ||||
|     MAP_ANALOG(ImGuiKey_GamepadRStickRight,     SDL_GAMEPAD_AXIS_RIGHTX, +thumb_dead_zone, +32767); | ||||
|     MAP_ANALOG(ImGuiKey_GamepadRStickUp,        SDL_GAMEPAD_AXIS_RIGHTY, -thumb_dead_zone, -32768); | ||||
|     MAP_ANALOG(ImGuiKey_GamepadRStickDown,      SDL_GAMEPAD_AXIS_RIGHTY, +thumb_dead_zone, +32767); | ||||
|     #undef MAP_BUTTON | ||||
|     #undef MAP_ANALOG | ||||
|     const int thumb_dead_zone = 8000;           // SDL_gamepad.h suggests using this value. | ||||
|     ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadStart,           SDL_GAMEPAD_BUTTON_START); | ||||
|     ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadBack,            SDL_GAMEPAD_BUTTON_BACK); | ||||
|     ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadFaceLeft,        SDL_GAMEPAD_BUTTON_WEST);           // Xbox X, PS Square | ||||
|     ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadFaceRight,       SDL_GAMEPAD_BUTTON_EAST);           // Xbox B, PS Circle | ||||
|     ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadFaceUp,          SDL_GAMEPAD_BUTTON_NORTH);          // Xbox Y, PS Triangle | ||||
|     ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadFaceDown,        SDL_GAMEPAD_BUTTON_SOUTH);          // Xbox A, PS Cross | ||||
|     ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadDpadLeft,        SDL_GAMEPAD_BUTTON_DPAD_LEFT); | ||||
|     ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadDpadRight,       SDL_GAMEPAD_BUTTON_DPAD_RIGHT); | ||||
|     ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadDpadUp,          SDL_GAMEPAD_BUTTON_DPAD_UP); | ||||
|     ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadDpadDown,        SDL_GAMEPAD_BUTTON_DPAD_DOWN); | ||||
|     ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadL1,              SDL_GAMEPAD_BUTTON_LEFT_SHOULDER); | ||||
|     ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadR1,              SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER); | ||||
|     ImGui_ImplSDL3_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadL2,              SDL_GAMEPAD_AXIS_LEFT_TRIGGER,  0.0f, 32767); | ||||
|     ImGui_ImplSDL3_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadR2,              SDL_GAMEPAD_AXIS_RIGHT_TRIGGER, 0.0f, 32767); | ||||
|     ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadL3,              SDL_GAMEPAD_BUTTON_LEFT_STICK); | ||||
|     ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadR3,              SDL_GAMEPAD_BUTTON_RIGHT_STICK); | ||||
|     ImGui_ImplSDL3_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadLStickLeft,      SDL_GAMEPAD_AXIS_LEFTX,  -thumb_dead_zone, -32768); | ||||
|     ImGui_ImplSDL3_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadLStickRight,     SDL_GAMEPAD_AXIS_LEFTX,  +thumb_dead_zone, +32767); | ||||
|     ImGui_ImplSDL3_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadLStickUp,        SDL_GAMEPAD_AXIS_LEFTY,  -thumb_dead_zone, -32768); | ||||
|     ImGui_ImplSDL3_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadLStickDown,      SDL_GAMEPAD_AXIS_LEFTY,  +thumb_dead_zone, +32767); | ||||
|     ImGui_ImplSDL3_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadRStickLeft,      SDL_GAMEPAD_AXIS_RIGHTX, -thumb_dead_zone, -32768); | ||||
|     ImGui_ImplSDL3_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadRStickRight,     SDL_GAMEPAD_AXIS_RIGHTX, +thumb_dead_zone, +32767); | ||||
|     ImGui_ImplSDL3_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadRStickUp,        SDL_GAMEPAD_AXIS_RIGHTY, -thumb_dead_zone, -32768); | ||||
|     ImGui_ImplSDL3_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadRStickDown,      SDL_GAMEPAD_AXIS_RIGHTY, +thumb_dead_zone, +32767); | ||||
| } | ||||
|  | ||||
| void ImGui_ImplSDL3_NewFrame() | ||||
|   | ||||
| @@ -26,6 +26,7 @@ | ||||
|  | ||||
| struct SDL_Window; | ||||
| struct SDL_Renderer; | ||||
| struct SDL_Gamepad; | ||||
| typedef union SDL_Event SDL_Event; | ||||
|  | ||||
| IMGUI_IMPL_API bool     ImGui_ImplSDL3_InitForOpenGL(SDL_Window* window, void* sdl_gl_context); | ||||
| @@ -38,4 +39,9 @@ IMGUI_IMPL_API void     ImGui_ImplSDL3_Shutdown(); | ||||
| IMGUI_IMPL_API void     ImGui_ImplSDL3_NewFrame(); | ||||
| IMGUI_IMPL_API bool     ImGui_ImplSDL3_ProcessEvent(const SDL_Event* event); | ||||
|  | ||||
| // Gamepad selection automatically starts in AutoFirst mode, picking first available SDL_Gamepad. You may override this. | ||||
| // When using manual mode, caller is responsible for opening/closing gamepad. | ||||
| enum ImGui_ImplSDL3_GamepadMode { ImGui_ImplSDL3_GamepadMode_AutoFirst, ImGui_ImplSDL3_GamepadMode_AutoAll, ImGui_ImplSDL3_GamepadMode_Manual }; | ||||
| IMGUI_IMPL_API void     ImGui_ImplSDL3_SetGamepadMode(ImGui_ImplSDL3_GamepadMode mode, SDL_Gamepad** manual_gamepads_array = NULL, int manual_gamepads_count = -1); | ||||
|  | ||||
| #endif // #ifndef IMGUI_DISABLE | ||||
|   | ||||
| @@ -54,7 +54,8 @@ Other changes: | ||||
|   continuously. Added support for multiple simultaneous gamepads. | ||||
|   Added ImGui_ImplSDL2_SetGamepadMode()) function to select whether to automatically pick | ||||
|   first available gamepad, all gamepads, or specific gamepads. | ||||
|   (#3884, #6559, #6890) [@ocornut, @lethal-guitar, @wn2000, @bog-dan-ro] | ||||
|   (#3884, #6559, #6890, #7180) [@ocornut, @lethal-guitar, @wn2000, @bog-dan-ro] | ||||
| - BackendsL SDL3: Fixed gamepad handling. (#7180) [@bog-dan-ro] | ||||
| - Backends: SDLRenderer3: query newly added SDL_RenderViewportSet() to not restore | ||||
|   a wrong viewport if none was initially set. | ||||
| - Backends: DirectX9: Using RGBA format when allowed by the driver to avoid CPU side | ||||
|   | ||||
		Reference in New Issue
	
	Block a user