/ source / imgui / src / imgui_impl_sdl2.cpp
imgui_impl_sdl2.cpp
   1  // dear imgui: Platform Backend for SDL2
   2  // This needs to be used along with a Renderer (e.g. DirectX11, OpenGL3, Vulkan..)
   3  // (Info: SDL2 is a cross-platform general purpose library for handling windows, inputs, graphics context creation, etc.)
   4  // (Prefer SDL 2.0.5+ for full feature support.)
   5  
   6  // Implemented features:
   7  //  [X] Platform: Clipboard support.
   8  //  [X] Platform: Keyboard support. Since 1.87 we are using the io.AddKeyEvent() function. Pass ImGuiKey values to all key functions e.g. ImGui::IsKeyPressed(ImGuiKey_Space). [Legacy SDL_SCANCODE_* values will also be supported unless IMGUI_DISABLE_OBSOLETE_KEYIO is set]
   9  //  [X] Platform: Gamepad support. Enabled with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'.
  10  //  [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
  11  //  [X] Platform: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'.
  12  // Missing features:
  13  //  [ ] Platform: Multi-viewport + Minimized windows seems to break mouse wheel events (at least under Windows).
  14  //  [x] Platform: Basic IME support. App needs to call 'SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1");' before SDL_CreateWindow()!.
  15  
  16  // You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
  17  // Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
  18  // If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp.
  19  // Read online: https://github.com/ocornut/imgui/tree/master/docs
  20  
  21  // CHANGELOG
  22  // (minor and older changes stripped away, please see git history for details)
  23  //  2023-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface.
  24  //  2023-02-23: Accept SDL_GetPerformanceCounter() not returning a monotonically increasing value. (#6189, #6114, #3644)
  25  //  2023-02-07: Implement IME handler (io.SetPlatformImeDataFn will call SDL_SetTextInputRect()/SDL_StartTextInput()).
  26  //  2023-02-07: *BREAKING CHANGE* Renamed this backend file from imgui_impl_sdl.cpp/.h to imgui_impl_sdl2.cpp/.h in prevision for the future release of SDL3.
  27  //  2023-02-02: Avoid calling SDL_SetCursor() when cursor has not changed, as the function is surprisingly costly on Mac with latest SDL (may be fixed in next SDL version).
  28  //  2023-02-02: Added support for SDL 2.0.18+ preciseX/preciseY mouse wheel data for smooth scrolling + Scaling X value on Emscripten (bug?). (#4019, #6096)
  29  //  2023-02-02: Removed SDL_MOUSEWHEEL value clamping, as values seem correct in latest Emscripten. (#4019)
  30  //  2023-02-01: Flipping SDL_MOUSEWHEEL 'wheel.x' value to match other backends and offer consistent horizontal scrolling direction. (#4019, #6096, #1463)
  31  //  2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11.
  32  //  2022-09-26: Inputs: Disable SDL 2.0.22 new "auto capture" (SDL_HINT_MOUSE_AUTO_CAPTURE) which prevents drag and drop across windows for multi-viewport support + don't capture when drag and dropping. (#5710)
  33  //  2022-09-26: Inputs: Renamed ImGuiKey_ModXXX introduced in 1.87 to ImGuiMod_XXX (old names still supported).
  34  //  2022-03-22: Inputs: Fix mouse position issues when dragging outside of boundaries. SDL_CaptureMouse() erroneously still gives out LEAVE events when hovering OS decorations.
  35  //  2022-03-22: Inputs: Added support for extra mouse buttons (SDL_BUTTON_X1/SDL_BUTTON_X2).
  36  //  2022-02-04: Added SDL_Renderer* parameter to ImGui_ImplSDL2_InitForSDLRenderer(), so we can use SDL_GetRendererOutputSize() instead of SDL_GL_GetDrawableSize() when bound to a SDL_Renderer.
  37  //  2022-01-26: Inputs: replaced short-lived io.AddKeyModsEvent() (added two weeks ago) with io.AddKeyEvent() using ImGuiKey_ModXXX flags. Sorry for the confusion.
  38  //  2021-01-20: Inputs: calling new io.AddKeyAnalogEvent() for gamepad support, instead of writing directly to io.NavInputs[].
  39  //  2022-01-17: Inputs: calling new io.AddMousePosEvent(), io.AddMouseButtonEvent(), io.AddMouseWheelEvent() API (1.87+).
  40  //  2022-01-17: Inputs: always update key mods next and before key event (not in NewFrame) to fix input queue with very low framerates.
  41  //  2022-01-12: Update mouse inputs using SDL_MOUSEMOTION/SDL_WINDOWEVENT_LEAVE + fallback to provide it when focused but not hovered/captured. More standard and will allow us to pass it to future input queue API.
  42  //  2022-01-12: Maintain our own copy of MouseButtonsDown mask instead of using ImGui::IsAnyMouseDown() which will be obsoleted.
  43  //  2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModsEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range.
  44  //  2021-08-17: Calling io.AddFocusEvent() on SDL_WINDOWEVENT_FOCUS_GAINED/SDL_WINDOWEVENT_FOCUS_LOST.
  45  //  2021-07-29: Inputs: MousePos is correctly reported when the host platform window is hovered but not focused (using SDL_GetMouseFocus() + SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, requires SDL 2.0.5+)
  46  //  2021-06:29: *BREAKING CHANGE* Removed 'SDL_Window* window' parameter to ImGui_ImplSDL2_NewFrame() which was unnecessary.
  47  //  2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX).
  48  //  2021-03-22: Rework global mouse pos availability check listing supported platforms explicitly, effectively fixing mouse access on Raspberry Pi. (#2837, #3950)
  49  //  2020-05-25: Misc: Report a zero display-size when window is minimized, to be consistent with other backends.
  50  //  2020-02-20: Inputs: Fixed mapping for ImGuiKey_KeyPadEnter (using SDL_SCANCODE_KP_ENTER instead of SDL_SCANCODE_RETURN2).
  51  //  2019-12-17: Inputs: On Wayland, use SDL_GetMouseState (because there is no global mouse state).
  52  //  2019-12-05: Inputs: Added support for ImGuiMouseCursor_NotAllowed mouse cursor.
  53  //  2019-07-21: Inputs: Added mapping for ImGuiKey_KeyPadEnter.
  54  //  2019-04-23: Inputs: Added support for SDL_GameController (if ImGuiConfigFlags_NavEnableGamepad is set by user application).
  55  //  2019-03-12: Misc: Preserve DisplayFramebufferScale when main window is minimized.
  56  //  2018-12-21: Inputs: Workaround for Android/iOS which don't seem to handle focus related calls.
  57  //  2018-11-30: Misc: Setting up io.BackendPlatformName so it can be displayed in the About Window.
  58  //  2018-11-14: Changed the signature of ImGui_ImplSDL2_ProcessEvent() to take a 'const SDL_Event*'.
  59  //  2018-08-01: Inputs: Workaround for Emscripten which doesn't seem to handle focus related calls.
  60  //  2018-06-29: Inputs: Added support for the ImGuiMouseCursor_Hand cursor.
  61  //  2018-06-08: Misc: Extracted imgui_impl_sdl.cpp/.h away from the old combined SDL2+OpenGL/Vulkan examples.
  62  //  2018-06-08: Misc: ImGui_ImplSDL2_InitForOpenGL() now takes a SDL_GLContext parameter.
  63  //  2018-05-09: Misc: Fixed clipboard paste memory leak (we didn't call SDL_FreeMemory on the data returned by SDL_GetClipboardText).
  64  //  2018-03-20: Misc: Setup io.BackendFlags ImGuiBackendFlags_HasMouseCursors flag + honor ImGuiConfigFlags_NoMouseCursorChange flag.
  65  //  2018-02-16: Inputs: Added support for mouse cursors, honoring ImGui::GetMouseCursor() value.
  66  //  2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves.
  67  //  2018-02-06: Inputs: Added mapping for ImGuiKey_Space.
  68  //  2018-02-05: Misc: Using SDL_GetPerformanceCounter() instead of SDL_GetTicks() to be able to handle very high framerate (1000+ FPS).
  69  //  2018-02-05: Inputs: Keyboard mapping is using scancodes everywhere instead of a confusing mixture of keycodes and scancodes.
  70  //  2018-01-20: Inputs: Added Horizontal Mouse Wheel support.
  71  //  2018-01-19: Inputs: When available (SDL 2.0.4+) using SDL_CaptureMouse() to retrieve coordinates outside of client area when dragging. Otherwise (SDL 2.0.3 and before) testing for SDL_WINDOW_INPUT_FOCUS instead of SDL_WINDOW_MOUSE_FOCUS.
  72  //  2018-01-18: Inputs: Added mapping for ImGuiKey_Insert.
  73  //  2017-08-25: Inputs: MousePos set to -FLT_MAX,-FLT_MAX when mouse is unavailable/missing (instead of -1,-1).
  74  //  2016-10-15: Misc: Added a void* user_data parameter to Clipboard function handlers.
  75  
  76  #include "imgui.h"
  77  #include "imgui_impl_sdl2.h"
  78  
  79  // SDL
  80  // (the multi-viewports feature requires SDL features supported from SDL 2.0.4+. SDL 2.0.5+ is highly recommended)
  81  #include <SDL.h>
  82  #include <SDL_syswm.h>
  83  #if defined(__APPLE__)
  84  #include <TargetConditionals.h>
  85  #endif
  86  
  87  #if SDL_VERSION_ATLEAST(2,0,4) && !defined(__EMSCRIPTEN__) && !defined(__ANDROID__) && !(defined(__APPLE__) && TARGET_OS_IOS) && !defined(__amigaos4__)
  88  #define SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE    1
  89  #else
  90  #define SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE    0
  91  #endif
  92  #define SDL_HAS_WINDOW_ALPHA                SDL_VERSION_ATLEAST(2,0,5)
  93  #define SDL_HAS_ALWAYS_ON_TOP               SDL_VERSION_ATLEAST(2,0,5)
  94  #define SDL_HAS_USABLE_DISPLAY_BOUNDS       SDL_VERSION_ATLEAST(2,0,5)
  95  #define SDL_HAS_PER_MONITOR_DPI             SDL_VERSION_ATLEAST(2,0,4)
  96  #define SDL_HAS_VULKAN                      SDL_VERSION_ATLEAST(2,0,6)
  97  #if !SDL_HAS_VULKAN
  98  static const Uint32 SDL_WINDOW_VULKAN = 0x10000000;
  99  #endif
 100  
 101  // SDL Data
 102  struct ImGui_ImplSDL2_Data
 103  {
 104      SDL_Window*     Window;
 105      SDL_Renderer*   Renderer;
 106      Uint64          Time;
 107      Uint32          MouseWindowID;
 108      int             MouseButtonsDown;
 109      SDL_Cursor*     MouseCursors[ImGuiMouseCursor_COUNT];
 110      SDL_Cursor*     LastMouseCursor;
 111      int             PendingMouseLeaveFrame;
 112      char*           ClipboardTextData;
 113      bool            MouseCanUseGlobalState;
 114      bool            MouseCanReportHoveredViewport;  // This is hard to use/unreliable on SDL so we'll set ImGuiBackendFlags_HasMouseHoveredViewport dynamically based on state.
 115      bool            UseVulkan;
 116  
 117      ImGui_ImplSDL2_Data()   { memset((void*)this, 0, sizeof(*this)); }
 118  };
 119  
 120  // Backend data stored in io.BackendPlatformUserData to allow support for multiple Dear ImGui contexts
 121  // It is STRONGLY preferred that you use docking branch with multi-viewports (== single Dear ImGui context + multiple windows) instead of multiple Dear ImGui contexts.
 122  // FIXME: multi-context support is not well tested and probably dysfunctional in this backend.
 123  // FIXME: some shared resources (mouse cursor shape, gamepad) are mishandled when using multi-context.
 124  static ImGui_ImplSDL2_Data* ImGui_ImplSDL2_GetBackendData()
 125  {
 126      return ImGui::GetCurrentContext() ? (ImGui_ImplSDL2_Data*)ImGui::GetIO().BackendPlatformUserData : nullptr;
 127  }
 128  
 129  // Forward Declarations
 130  static void ImGui_ImplSDL2_UpdateMonitors();
 131  static void ImGui_ImplSDL2_InitPlatformInterface(SDL_Window* window, void* sdl_gl_context);
 132  static void ImGui_ImplSDL2_ShutdownPlatformInterface();
 133  
 134  // Functions
 135  static const char* ImGui_ImplSDL2_GetClipboardText(void*)
 136  {
 137      ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData();
 138      if (bd->ClipboardTextData)
 139          SDL_free(bd->ClipboardTextData);
 140      bd->ClipboardTextData = SDL_GetClipboardText();
 141      return bd->ClipboardTextData;
 142  }
 143  
 144  static void ImGui_ImplSDL2_SetClipboardText(void*, const char* text)
 145  {
 146      SDL_SetClipboardText(text);
 147  }
 148  
 149  // Note: native IME will only display if user calls SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1") _before_ SDL_CreateWindow().
 150  static void ImGui_ImplSDL2_SetPlatformImeData(ImGuiViewport*, ImGuiPlatformImeData* data)
 151  {
 152      if (data->WantVisible)
 153      {
 154          SDL_Rect r;
 155          r.x = (int)data->InputPos.x;
 156          r.y = (int)data->InputPos.y;
 157          r.w = 1;
 158          r.h = (int)data->InputLineHeight;
 159          SDL_SetTextInputRect(&r);
 160          SDL_StartTextInput();
 161      }
 162      else
 163      {
 164          SDL_StopTextInput();
 165      }
 166  }
 167  
 168  static ImGuiKey ImGui_ImplSDL2_KeycodeToImGuiKey(int keycode)
 169  {
 170      switch (keycode)
 171      {
 172          case SDLK_TAB: return ImGuiKey_Tab;
 173          case SDLK_LEFT: return ImGuiKey_LeftArrow;
 174          case SDLK_RIGHT: return ImGuiKey_RightArrow;
 175          case SDLK_UP: return ImGuiKey_UpArrow;
 176          case SDLK_DOWN: return ImGuiKey_DownArrow;
 177          case SDLK_PAGEUP: return ImGuiKey_PageUp;
 178          case SDLK_PAGEDOWN: return ImGuiKey_PageDown;
 179          case SDLK_HOME: return ImGuiKey_Home;
 180          case SDLK_END: return ImGuiKey_End;
 181          case SDLK_INSERT: return ImGuiKey_Insert;
 182          case SDLK_DELETE: return ImGuiKey_Delete;
 183          case SDLK_BACKSPACE: return ImGuiKey_Backspace;
 184          case SDLK_SPACE: return ImGuiKey_Space;
 185          case SDLK_RETURN: return ImGuiKey_Enter;
 186          case SDLK_ESCAPE: return ImGuiKey_Escape;
 187          case SDLK_QUOTE: return ImGuiKey_Apostrophe;
 188          case SDLK_COMMA: return ImGuiKey_Comma;
 189          case SDLK_MINUS: return ImGuiKey_Minus;
 190          case SDLK_PERIOD: return ImGuiKey_Period;
 191          case SDLK_SLASH: return ImGuiKey_Slash;
 192          case SDLK_SEMICOLON: return ImGuiKey_Semicolon;
 193          case SDLK_EQUALS: return ImGuiKey_Equal;
 194          case SDLK_LEFTBRACKET: return ImGuiKey_LeftBracket;
 195          case SDLK_BACKSLASH: return ImGuiKey_Backslash;
 196          case SDLK_RIGHTBRACKET: return ImGuiKey_RightBracket;
 197          case SDLK_BACKQUOTE: return ImGuiKey_GraveAccent;
 198          case SDLK_CAPSLOCK: return ImGuiKey_CapsLock;
 199          case SDLK_SCROLLLOCK: return ImGuiKey_ScrollLock;
 200          case SDLK_NUMLOCKCLEAR: return ImGuiKey_NumLock;
 201          case SDLK_PRINTSCREEN: return ImGuiKey_PrintScreen;
 202          case SDLK_PAUSE: return ImGuiKey_Pause;
 203          case SDLK_KP_0: return ImGuiKey_Keypad0;
 204          case SDLK_KP_1: return ImGuiKey_Keypad1;
 205          case SDLK_KP_2: return ImGuiKey_Keypad2;
 206          case SDLK_KP_3: return ImGuiKey_Keypad3;
 207          case SDLK_KP_4: return ImGuiKey_Keypad4;
 208          case SDLK_KP_5: return ImGuiKey_Keypad5;
 209          case SDLK_KP_6: return ImGuiKey_Keypad6;
 210          case SDLK_KP_7: return ImGuiKey_Keypad7;
 211          case SDLK_KP_8: return ImGuiKey_Keypad8;
 212          case SDLK_KP_9: return ImGuiKey_Keypad9;
 213          case SDLK_KP_PERIOD: return ImGuiKey_KeypadDecimal;
 214          case SDLK_KP_DIVIDE: return ImGuiKey_KeypadDivide;
 215          case SDLK_KP_MULTIPLY: return ImGuiKey_KeypadMultiply;
 216          case SDLK_KP_MINUS: return ImGuiKey_KeypadSubtract;
 217          case SDLK_KP_PLUS: return ImGuiKey_KeypadAdd;
 218          case SDLK_KP_ENTER: return ImGuiKey_KeypadEnter;
 219          case SDLK_KP_EQUALS: return ImGuiKey_KeypadEqual;
 220          case SDLK_LCTRL: return ImGuiKey_LeftCtrl;
 221          case SDLK_LSHIFT: return ImGuiKey_LeftShift;
 222          case SDLK_LALT: return ImGuiKey_LeftAlt;
 223          case SDLK_LGUI: return ImGuiKey_LeftSuper;
 224          case SDLK_RCTRL: return ImGuiKey_RightCtrl;
 225          case SDLK_RSHIFT: return ImGuiKey_RightShift;
 226          case SDLK_RALT: return ImGuiKey_RightAlt;
 227          case SDLK_RGUI: return ImGuiKey_RightSuper;
 228          case SDLK_APPLICATION: return ImGuiKey_Menu;
 229          case SDLK_0: return ImGuiKey_0;
 230          case SDLK_1: return ImGuiKey_1;
 231          case SDLK_2: return ImGuiKey_2;
 232          case SDLK_3: return ImGuiKey_3;
 233          case SDLK_4: return ImGuiKey_4;
 234          case SDLK_5: return ImGuiKey_5;
 235          case SDLK_6: return ImGuiKey_6;
 236          case SDLK_7: return ImGuiKey_7;
 237          case SDLK_8: return ImGuiKey_8;
 238          case SDLK_9: return ImGuiKey_9;
 239          case SDLK_a: return ImGuiKey_A;
 240          case SDLK_b: return ImGuiKey_B;
 241          case SDLK_c: return ImGuiKey_C;
 242          case SDLK_d: return ImGuiKey_D;
 243          case SDLK_e: return ImGuiKey_E;
 244          case SDLK_f: return ImGuiKey_F;
 245          case SDLK_g: return ImGuiKey_G;
 246          case SDLK_h: return ImGuiKey_H;
 247          case SDLK_i: return ImGuiKey_I;
 248          case SDLK_j: return ImGuiKey_J;
 249          case SDLK_k: return ImGuiKey_K;
 250          case SDLK_l: return ImGuiKey_L;
 251          case SDLK_m: return ImGuiKey_M;
 252          case SDLK_n: return ImGuiKey_N;
 253          case SDLK_o: return ImGuiKey_O;
 254          case SDLK_p: return ImGuiKey_P;
 255          case SDLK_q: return ImGuiKey_Q;
 256          case SDLK_r: return ImGuiKey_R;
 257          case SDLK_s: return ImGuiKey_S;
 258          case SDLK_t: return ImGuiKey_T;
 259          case SDLK_u: return ImGuiKey_U;
 260          case SDLK_v: return ImGuiKey_V;
 261          case SDLK_w: return ImGuiKey_W;
 262          case SDLK_x: return ImGuiKey_X;
 263          case SDLK_y: return ImGuiKey_Y;
 264          case SDLK_z: return ImGuiKey_Z;
 265          case SDLK_F1: return ImGuiKey_F1;
 266          case SDLK_F2: return ImGuiKey_F2;
 267          case SDLK_F3: return ImGuiKey_F3;
 268          case SDLK_F4: return ImGuiKey_F4;
 269          case SDLK_F5: return ImGuiKey_F5;
 270          case SDLK_F6: return ImGuiKey_F6;
 271          case SDLK_F7: return ImGuiKey_F7;
 272          case SDLK_F8: return ImGuiKey_F8;
 273          case SDLK_F9: return ImGuiKey_F9;
 274          case SDLK_F10: return ImGuiKey_F10;
 275          case SDLK_F11: return ImGuiKey_F11;
 276          case SDLK_F12: return ImGuiKey_F12;
 277      }
 278      return ImGuiKey_None;
 279  }
 280  
 281  static void ImGui_ImplSDL2_UpdateKeyModifiers(SDL_Keymod sdl_key_mods)
 282  {
 283      ImGuiIO& io = ImGui::GetIO();
 284      io.AddKeyEvent(ImGuiMod_Ctrl, (sdl_key_mods & KMOD_CTRL) != 0);
 285      io.AddKeyEvent(ImGuiMod_Shift, (sdl_key_mods & KMOD_SHIFT) != 0);
 286      io.AddKeyEvent(ImGuiMod_Alt, (sdl_key_mods & KMOD_ALT) != 0);
 287      io.AddKeyEvent(ImGuiMod_Super, (sdl_key_mods & KMOD_GUI) != 0);
 288  }
 289  
 290  // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs.
 291  // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data.
 292  // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data.
 293  // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags.
 294  // If you have multiple SDL events and some of them are not meant to be used by dear imgui, you may need to filter events based on their windowID field.
 295  bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event)
 296  {
 297      ImGuiIO& io = ImGui::GetIO();
 298      ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData();
 299  
 300      switch (event->type)
 301      {
 302          case SDL_MOUSEMOTION:
 303          {
 304              ImVec2 mouse_pos((float)event->motion.x, (float)event->motion.y);
 305              if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
 306              {
 307                  int window_x, window_y;
 308                  SDL_GetWindowPosition(SDL_GetWindowFromID(event->motion.windowID), &window_x, &window_y);
 309                  mouse_pos.x += window_x;
 310                  mouse_pos.y += window_y;
 311              }
 312              io.AddMousePosEvent(mouse_pos.x, mouse_pos.y);
 313              return true;
 314          }
 315          case SDL_MOUSEWHEEL:
 316          {
 317              //IMGUI_DEBUG_LOG("wheel %.2f %.2f, precise %.2f %.2f\n", (float)event->wheel.x, (float)event->wheel.y, event->wheel.preciseX, event->wheel.preciseY);
 318  #if SDL_VERSION_ATLEAST(2,0,18) // If this fails to compile on Emscripten: update to latest Emscripten!
 319              float wheel_x = -event->wheel.preciseX;
 320              float wheel_y = event->wheel.preciseY;
 321  #else
 322              float wheel_x = -(float)event->wheel.x;
 323              float wheel_y = (float)event->wheel.y;
 324  #endif
 325  #ifdef __EMSCRIPTEN__
 326              wheel_x /= 100.0f;
 327  #endif
 328              io.AddMouseWheelEvent(wheel_x, wheel_y);
 329              return true;
 330          }
 331          case SDL_MOUSEBUTTONDOWN:
 332          case SDL_MOUSEBUTTONUP:
 333          {
 334              int mouse_button = -1;
 335              if (event->button.button == SDL_BUTTON_LEFT) { mouse_button = 0; }
 336              if (event->button.button == SDL_BUTTON_RIGHT) { mouse_button = 1; }
 337              if (event->button.button == SDL_BUTTON_MIDDLE) { mouse_button = 2; }
 338              if (event->button.button == SDL_BUTTON_X1) { mouse_button = 3; }
 339              if (event->button.button == SDL_BUTTON_X2) { mouse_button = 4; }
 340              if (mouse_button == -1)
 341                  break;
 342              io.AddMouseButtonEvent(mouse_button, (event->type == SDL_MOUSEBUTTONDOWN));
 343              bd->MouseButtonsDown = (event->type == SDL_MOUSEBUTTONDOWN) ? (bd->MouseButtonsDown | (1 << mouse_button)) : (bd->MouseButtonsDown & ~(1 << mouse_button));
 344              return true;
 345          }
 346          case SDL_TEXTINPUT:
 347          {
 348              io.AddInputCharactersUTF8(event->text.text);
 349              return true;
 350          }
 351          case SDL_KEYDOWN:
 352          case SDL_KEYUP:
 353          {
 354              ImGui_ImplSDL2_UpdateKeyModifiers((SDL_Keymod)event->key.keysym.mod);
 355              ImGuiKey key = ImGui_ImplSDL2_KeycodeToImGuiKey(event->key.keysym.sym);
 356              io.AddKeyEvent(key, (event->type == SDL_KEYDOWN));
 357              io.SetKeyEventNativeData(key, event->key.keysym.sym, event->key.keysym.scancode, event->key.keysym.scancode); // To support legacy indexing (<1.87 user code). Legacy backend uses SDLK_*** as indices to IsKeyXXX() functions.
 358              return true;
 359          }
 360          case SDL_WINDOWEVENT:
 361          {
 362              // - When capturing mouse, SDL will send a bunch of conflicting LEAVE/ENTER event on every mouse move, but the final ENTER tends to be right.
 363              // - However we won't get a correct LEAVE event for a captured window.
 364              // - In some cases, when detaching a window from main viewport SDL may send SDL_WINDOWEVENT_ENTER one frame too late,
 365              //   causing SDL_WINDOWEVENT_LEAVE on previous frame to interrupt drag operation by clear mouse position. This is why
 366              //   we delay process the SDL_WINDOWEVENT_LEAVE events by one frame. See issue #5012 for details.
 367              Uint8 window_event = event->window.event;
 368              if (window_event == SDL_WINDOWEVENT_ENTER)
 369              {
 370                  bd->MouseWindowID = event->window.windowID;
 371                  bd->PendingMouseLeaveFrame = 0;
 372              }
 373              if (window_event == SDL_WINDOWEVENT_LEAVE)
 374                  bd->PendingMouseLeaveFrame = ImGui::GetFrameCount() + 1;
 375              if (window_event == SDL_WINDOWEVENT_FOCUS_GAINED)
 376                  io.AddFocusEvent(true);
 377              else if (window_event == SDL_WINDOWEVENT_FOCUS_LOST)
 378                  io.AddFocusEvent(false);
 379              if (window_event == SDL_WINDOWEVENT_CLOSE || window_event == SDL_WINDOWEVENT_MOVED || window_event == SDL_WINDOWEVENT_RESIZED)
 380                  if (ImGuiViewport* viewport = ImGui::FindViewportByPlatformHandle((void*)SDL_GetWindowFromID(event->window.windowID)))
 381                  {
 382                      if (window_event == SDL_WINDOWEVENT_CLOSE)
 383                          viewport->PlatformRequestClose = true;
 384                      if (window_event == SDL_WINDOWEVENT_MOVED)
 385                          viewport->PlatformRequestMove = true;
 386                      if (window_event == SDL_WINDOWEVENT_RESIZED)
 387                          viewport->PlatformRequestResize = true;
 388                      return true;
 389                  }
 390              return true;
 391          }
 392      }
 393      return false;
 394  }
 395  
 396  static bool ImGui_ImplSDL2_Init(SDL_Window* window, SDL_Renderer* renderer, void* sdl_gl_context)
 397  {
 398      ImGuiIO& io = ImGui::GetIO();
 399      IM_ASSERT(io.BackendPlatformUserData == nullptr && "Already initialized a platform backend!");
 400  
 401      // Check and store if we are on a SDL backend that supports global mouse position
 402      // ("wayland" and "rpi" don't support it, but we chose to use a white-list instead of a black-list)
 403      bool mouse_can_use_global_state = false;
 404  #if SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE
 405      const char* sdl_backend = SDL_GetCurrentVideoDriver();
 406      const char* global_mouse_whitelist[] = { "windows", "cocoa", "x11", "DIVE", "VMAN" };
 407      for (int n = 0; n < IM_ARRAYSIZE(global_mouse_whitelist); n++)
 408          if (strncmp(sdl_backend, global_mouse_whitelist[n], strlen(global_mouse_whitelist[n])) == 0)
 409              mouse_can_use_global_state = true;
 410  #endif
 411  
 412      // Setup backend capabilities flags
 413      ImGui_ImplSDL2_Data* bd = IM_NEW(ImGui_ImplSDL2_Data)();
 414      io.BackendPlatformUserData = (void*)bd;
 415      io.BackendPlatformName = "imgui_impl_sdl2";
 416      io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors;           // We can honor GetMouseCursor() values (optional)
 417      io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos;            // We can honor io.WantSetMousePos requests (optional, rarely used)
 418      if (mouse_can_use_global_state)
 419          io.BackendFlags |= ImGuiBackendFlags_PlatformHasViewports;  // We can create multi-viewports on the Platform side (optional)
 420  
 421      bd->Window = window;
 422      bd->Renderer = renderer;
 423  
 424      // SDL on Linux/OSX doesn't report events for unfocused windows (see https://github.com/ocornut/imgui/issues/4960)
 425      // We will use 'MouseCanReportHoveredViewport' to set 'ImGuiBackendFlags_HasMouseHoveredViewport' dynamically each frame.
 426      bd->MouseCanUseGlobalState = mouse_can_use_global_state;
 427  #ifndef __APPLE__
 428      bd->MouseCanReportHoveredViewport = bd->MouseCanUseGlobalState;
 429  #else
 430      bd->MouseCanReportHoveredViewport = false;
 431  #endif
 432  
 433      io.SetClipboardTextFn = ImGui_ImplSDL2_SetClipboardText;
 434      io.GetClipboardTextFn = ImGui_ImplSDL2_GetClipboardText;
 435      io.ClipboardUserData = nullptr;
 436      io.SetPlatformImeDataFn = ImGui_ImplSDL2_SetPlatformImeData;
 437  
 438      // Load mouse cursors
 439      bd->MouseCursors[ImGuiMouseCursor_Arrow] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_ARROW);
 440      bd->MouseCursors[ImGuiMouseCursor_TextInput] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_IBEAM);
 441      bd->MouseCursors[ImGuiMouseCursor_ResizeAll] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEALL);
 442      bd->MouseCursors[ImGuiMouseCursor_ResizeNS] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENS);
 443      bd->MouseCursors[ImGuiMouseCursor_ResizeEW] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEWE);
 444      bd->MouseCursors[ImGuiMouseCursor_ResizeNESW] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENESW);
 445      bd->MouseCursors[ImGuiMouseCursor_ResizeNWSE] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENWSE);
 446      bd->MouseCursors[ImGuiMouseCursor_Hand] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_HAND);
 447      bd->MouseCursors[ImGuiMouseCursor_NotAllowed] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NO);
 448  
 449      // Set platform dependent data in viewport
 450      // Our mouse update function expect PlatformHandle to be filled for the main viewport
 451      ImGuiViewport* main_viewport = ImGui::GetMainViewport();
 452      main_viewport->PlatformHandle = (void*)window;
 453      main_viewport->PlatformHandleRaw = nullptr;
 454      SDL_SysWMinfo info;
 455      SDL_VERSION(&info.version);
 456      if (SDL_GetWindowWMInfo(window, &info))
 457      {
 458  #if defined(SDL_VIDEO_DRIVER_WINDOWS)
 459          main_viewport->PlatformHandleRaw = (void*)info.info.win.window;
 460  #elif defined(__APPLE__) && defined(SDL_VIDEO_DRIVER_COCOA)
 461          main_viewport->PlatformHandleRaw = (void*)info.info.cocoa.window;
 462  #endif
 463      }
 464  
 465      // From 2.0.5: Set SDL hint to receive mouse click events on window focus, otherwise SDL doesn't emit the event.
 466      // Without this, when clicking to gain focus, our widgets wouldn't activate even though they showed as hovered.
 467      // (This is unfortunately a global SDL setting, so enabling it might have a side-effect on your application.
 468      // It is unlikely to make a difference, but if your app absolutely needs to ignore the initial on-focus click:
 469      // you can ignore SDL_MOUSEBUTTONDOWN events coming right after a SDL_WINDOWEVENT_FOCUS_GAINED)
 470  #ifdef SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH
 471      SDL_SetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, "1");
 472  #endif
 473  
 474      // From 2.0.18: Enable native IME.
 475      // IMPORTANT: This is used at the time of SDL_CreateWindow() so this will only affects secondary windows, if any.
 476      // For the main window to be affected, your application needs to call this manually before calling SDL_CreateWindow().
 477  #ifdef SDL_HINT_IME_SHOW_UI
 478      SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1");
 479  #endif
 480  
 481      // From 2.0.22: Disable auto-capture, this is preventing drag and drop across multiple windows (see #5710)
 482  #ifdef SDL_HINT_MOUSE_AUTO_CAPTURE
 483      SDL_SetHint(SDL_HINT_MOUSE_AUTO_CAPTURE, "0");
 484  #endif
 485  
 486      // Update monitors
 487      ImGui_ImplSDL2_UpdateMonitors();
 488  
 489      // We need SDL_CaptureMouse(), SDL_GetGlobalMouseState() from SDL 2.0.4+ to support multiple viewports.
 490      // We left the call to ImGui_ImplSDL2_InitPlatformInterface() outside of #ifdef to avoid unused-function warnings.
 491      if ((io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable) && (io.BackendFlags & ImGuiBackendFlags_PlatformHasViewports))
 492          ImGui_ImplSDL2_InitPlatformInterface(window, sdl_gl_context);
 493  
 494      return true;
 495  }
 496  
 497  bool ImGui_ImplSDL2_InitForOpenGL(SDL_Window* window, void* sdl_gl_context)
 498  {
 499      return ImGui_ImplSDL2_Init(window, nullptr, sdl_gl_context);
 500  }
 501  
 502  bool ImGui_ImplSDL2_InitForVulkan(SDL_Window* window)
 503  {
 504  #if !SDL_HAS_VULKAN
 505      IM_ASSERT(0 && "Unsupported");
 506  #endif
 507      if (!ImGui_ImplSDL2_Init(window, nullptr, nullptr))
 508          return false;
 509      ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData();
 510      bd->UseVulkan = true;
 511      return true;
 512  }
 513  
 514  bool ImGui_ImplSDL2_InitForD3D(SDL_Window* window)
 515  {
 516  #if !defined(_WIN32)
 517      IM_ASSERT(0 && "Unsupported");
 518  #endif
 519      return ImGui_ImplSDL2_Init(window, nullptr, nullptr);
 520  }
 521  
 522  bool ImGui_ImplSDL2_InitForMetal(SDL_Window* window)
 523  {
 524      return ImGui_ImplSDL2_Init(window, nullptr, nullptr);
 525  }
 526  
 527  bool ImGui_ImplSDL2_InitForSDLRenderer(SDL_Window* window, SDL_Renderer* renderer)
 528  {
 529      return ImGui_ImplSDL2_Init(window, renderer, nullptr);
 530  }
 531  
 532  void ImGui_ImplSDL2_Shutdown()
 533  {
 534      ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData();
 535      IM_ASSERT(bd != nullptr && "No platform backend to shutdown, or already shutdown?");
 536      ImGuiIO& io = ImGui::GetIO();
 537  
 538      ImGui_ImplSDL2_ShutdownPlatformInterface();
 539  
 540      if (bd->ClipboardTextData)
 541          SDL_free(bd->ClipboardTextData);
 542      for (ImGuiMouseCursor cursor_n = 0; cursor_n < ImGuiMouseCursor_COUNT; cursor_n++)
 543          SDL_FreeCursor(bd->MouseCursors[cursor_n]);
 544      bd->LastMouseCursor = NULL;
 545  
 546      io.BackendPlatformName = nullptr;
 547      io.BackendPlatformUserData = nullptr;
 548      IM_DELETE(bd);
 549  }
 550  
 551  // This code is incredibly messy because some of the functions we need for full viewport support are not available in SDL < 2.0.4.
 552  static void ImGui_ImplSDL2_UpdateMouseData()
 553  {
 554      ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData();
 555      ImGuiIO& io = ImGui::GetIO();
 556  
 557      // We forward mouse input when hovered or captured (via SDL_MOUSEMOTION) or when focused (below)
 558  #if SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE
 559      // SDL_CaptureMouse() let the OS know e.g. that our imgui drag outside the SDL window boundaries shouldn't e.g. trigger other operations outside
 560      SDL_CaptureMouse((bd->MouseButtonsDown != 0) ? SDL_TRUE : SDL_FALSE);
 561      SDL_Window* focused_window = SDL_GetKeyboardFocus();
 562      const bool is_app_focused = (focused_window && (bd->Window == focused_window || ImGui::FindViewportByPlatformHandle((void*)focused_window)));
 563  #else
 564      SDL_Window* focused_window = bd->Window;
 565      const bool is_app_focused = (SDL_GetWindowFlags(bd->Window) & SDL_WINDOW_INPUT_FOCUS) != 0; // SDL 2.0.3 and non-windowed systems: single-viewport only
 566  #endif
 567  
 568      if (is_app_focused)
 569      {
 570          // (Optional) Set OS mouse position from Dear ImGui if requested (rarely used, only when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user)
 571          if (io.WantSetMousePos)
 572          {
 573  #if SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE
 574              if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
 575                  SDL_WarpMouseGlobal((int)io.MousePos.x, (int)io.MousePos.y);
 576              else
 577  #endif
 578                  SDL_WarpMouseInWindow(bd->Window, (int)io.MousePos.x, (int)io.MousePos.y);
 579          }
 580  
 581          // (Optional) Fallback to provide mouse position when focused (SDL_MOUSEMOTION already provides this when hovered or captured)
 582          if (bd->MouseCanUseGlobalState && bd->MouseButtonsDown == 0)
 583          {
 584              // Single-viewport mode: mouse position in client window coordinates (io.MousePos is (0,0) when the mouse is on the upper-left corner of the app window)
 585              // Multi-viewport mode: mouse position in OS absolute coordinates (io.MousePos is (0,0) when the mouse is on the upper-left of the primary monitor)
 586              int mouse_x, mouse_y, window_x, window_y;
 587              SDL_GetGlobalMouseState(&mouse_x, &mouse_y);
 588              if (!(io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable))
 589              {
 590                  SDL_GetWindowPosition(focused_window, &window_x, &window_y);
 591                  mouse_x -= window_x;
 592                  mouse_y -= window_y;
 593              }
 594              io.AddMousePosEvent((float)mouse_x, (float)mouse_y);
 595          }
 596      }
 597  
 598      // (Optional) When using multiple viewports: call io.AddMouseViewportEvent() with the viewport the OS mouse cursor is hovering.
 599      // If ImGuiBackendFlags_HasMouseHoveredViewport is not set by the backend, Dear imGui will ignore this field and infer the information using its flawed heuristic.
 600      // - [!] SDL backend does NOT correctly ignore viewports with the _NoInputs flag.
 601      //       Some backend are not able to handle that correctly. If a backend report an hovered viewport that has the _NoInputs flag (e.g. when dragging a window
 602      //       for docking, the viewport has the _NoInputs flag in order to allow us to find the viewport under), then Dear ImGui is forced to ignore the value reported
 603      //       by the backend, and use its flawed heuristic to guess the viewport behind.
 604      // - [X] SDL backend correctly reports this regardless of another viewport behind focused and dragged from (we need this to find a useful drag and drop target).
 605      if (io.BackendFlags & ImGuiBackendFlags_HasMouseHoveredViewport)
 606      {
 607          ImGuiID mouse_viewport_id = 0;
 608          if (SDL_Window* sdl_mouse_window = SDL_GetWindowFromID(bd->MouseWindowID))
 609              if (ImGuiViewport* mouse_viewport = ImGui::FindViewportByPlatformHandle((void*)sdl_mouse_window))
 610                  mouse_viewport_id = mouse_viewport->ID;
 611          io.AddMouseViewportEvent(mouse_viewport_id);
 612      }
 613  }
 614  
 615  static void ImGui_ImplSDL2_UpdateMouseCursor()
 616  {
 617      ImGuiIO& io = ImGui::GetIO();
 618      if (io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange)
 619          return;
 620      ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData();
 621  
 622      ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor();
 623      if (io.MouseDrawCursor || imgui_cursor == ImGuiMouseCursor_None)
 624      {
 625          // Hide OS mouse cursor if imgui is drawing it or if it wants no cursor
 626          SDL_ShowCursor(SDL_FALSE);
 627      }
 628      else
 629      {
 630          // Show OS mouse cursor
 631          SDL_Cursor* expected_cursor = bd->MouseCursors[imgui_cursor] ? bd->MouseCursors[imgui_cursor] : bd->MouseCursors[ImGuiMouseCursor_Arrow];
 632          if (bd->LastMouseCursor != expected_cursor)
 633          {
 634              SDL_SetCursor(expected_cursor); // SDL function doesn't have an early out (see #6113)
 635              bd->LastMouseCursor = expected_cursor;
 636          }
 637          SDL_ShowCursor(SDL_TRUE);
 638      }
 639  }
 640  
 641  static void ImGui_ImplSDL2_UpdateGamepads()
 642  {
 643      ImGuiIO& io = ImGui::GetIO();
 644      if ((io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) == 0) // FIXME: Technically feeding gamepad shouldn't depend on this now that they are regular inputs.
 645          return;
 646  
 647      // Get gamepad
 648      io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad;
 649      SDL_GameController* game_controller = SDL_GameControllerOpen(0);
 650      if (!game_controller)
 651          return;
 652      io.BackendFlags |= ImGuiBackendFlags_HasGamepad;
 653  
 654      // Update gamepad inputs
 655      #define IM_SATURATE(V)                      (V < 0.0f ? 0.0f : V > 1.0f ? 1.0f : V)
 656      #define MAP_BUTTON(KEY_NO, BUTTON_NO)       { io.AddKeyEvent(KEY_NO, SDL_GameControllerGetButton(game_controller, BUTTON_NO) != 0); }
 657      #define MAP_ANALOG(KEY_NO, AXIS_NO, V0, V1) { float vn = (float)(SDL_GameControllerGetAxis(game_controller, AXIS_NO) - V0) / (float)(V1 - V0); vn = IM_SATURATE(vn); io.AddKeyAnalogEvent(KEY_NO, vn > 0.1f, vn); }
 658      const int thumb_dead_zone = 8000;           // SDL_gamecontroller.h suggests using this value.
 659      MAP_BUTTON(ImGuiKey_GamepadStart,           SDL_CONTROLLER_BUTTON_START);
 660      MAP_BUTTON(ImGuiKey_GamepadBack,            SDL_CONTROLLER_BUTTON_BACK);
 661      MAP_BUTTON(ImGuiKey_GamepadFaceLeft,        SDL_CONTROLLER_BUTTON_X);              // Xbox X, PS Square
 662      MAP_BUTTON(ImGuiKey_GamepadFaceRight,       SDL_CONTROLLER_BUTTON_B);              // Xbox B, PS Circle
 663      MAP_BUTTON(ImGuiKey_GamepadFaceUp,          SDL_CONTROLLER_BUTTON_Y);              // Xbox Y, PS Triangle
 664      MAP_BUTTON(ImGuiKey_GamepadFaceDown,        SDL_CONTROLLER_BUTTON_A);              // Xbox A, PS Cross
 665      MAP_BUTTON(ImGuiKey_GamepadDpadLeft,        SDL_CONTROLLER_BUTTON_DPAD_LEFT);
 666      MAP_BUTTON(ImGuiKey_GamepadDpadRight,       SDL_CONTROLLER_BUTTON_DPAD_RIGHT);
 667      MAP_BUTTON(ImGuiKey_GamepadDpadUp,          SDL_CONTROLLER_BUTTON_DPAD_UP);
 668      MAP_BUTTON(ImGuiKey_GamepadDpadDown,        SDL_CONTROLLER_BUTTON_DPAD_DOWN);
 669      MAP_BUTTON(ImGuiKey_GamepadL1,              SDL_CONTROLLER_BUTTON_LEFTSHOULDER);
 670      MAP_BUTTON(ImGuiKey_GamepadR1,              SDL_CONTROLLER_BUTTON_RIGHTSHOULDER);
 671      MAP_ANALOG(ImGuiKey_GamepadL2,              SDL_CONTROLLER_AXIS_TRIGGERLEFT,  0.0f, 32767);
 672      MAP_ANALOG(ImGuiKey_GamepadR2,              SDL_CONTROLLER_AXIS_TRIGGERRIGHT, 0.0f, 32767);
 673      MAP_BUTTON(ImGuiKey_GamepadL3,              SDL_CONTROLLER_BUTTON_LEFTSTICK);
 674      MAP_BUTTON(ImGuiKey_GamepadR3,              SDL_CONTROLLER_BUTTON_RIGHTSTICK);
 675      MAP_ANALOG(ImGuiKey_GamepadLStickLeft,      SDL_CONTROLLER_AXIS_LEFTX,  -thumb_dead_zone, -32768);
 676      MAP_ANALOG(ImGuiKey_GamepadLStickRight,     SDL_CONTROLLER_AXIS_LEFTX,  +thumb_dead_zone, +32767);
 677      MAP_ANALOG(ImGuiKey_GamepadLStickUp,        SDL_CONTROLLER_AXIS_LEFTY,  -thumb_dead_zone, -32768);
 678      MAP_ANALOG(ImGuiKey_GamepadLStickDown,      SDL_CONTROLLER_AXIS_LEFTY,  +thumb_dead_zone, +32767);
 679      MAP_ANALOG(ImGuiKey_GamepadRStickLeft,      SDL_CONTROLLER_AXIS_RIGHTX, -thumb_dead_zone, -32768);
 680      MAP_ANALOG(ImGuiKey_GamepadRStickRight,     SDL_CONTROLLER_AXIS_RIGHTX, +thumb_dead_zone, +32767);
 681      MAP_ANALOG(ImGuiKey_GamepadRStickUp,        SDL_CONTROLLER_AXIS_RIGHTY, -thumb_dead_zone, -32768);
 682      MAP_ANALOG(ImGuiKey_GamepadRStickDown,      SDL_CONTROLLER_AXIS_RIGHTY, +thumb_dead_zone, +32767);
 683      #undef MAP_BUTTON
 684      #undef MAP_ANALOG
 685  }
 686  
 687  // FIXME-PLATFORM: SDL doesn't have an event to notify the application of display/monitor changes
 688  static void ImGui_ImplSDL2_UpdateMonitors()
 689  {
 690      ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
 691      platform_io.Monitors.resize(0);
 692      int display_count = SDL_GetNumVideoDisplays();
 693      for (int n = 0; n < display_count; n++)
 694      {
 695          // Warning: the validity of monitor DPI information on Windows depends on the application DPI awareness settings, which generally needs to be set in the manifest or at runtime.
 696          ImGuiPlatformMonitor monitor;
 697          SDL_Rect r;
 698          SDL_GetDisplayBounds(n, &r);
 699          monitor.MainPos = monitor.WorkPos = ImVec2((float)r.x, (float)r.y);
 700          monitor.MainSize = monitor.WorkSize = ImVec2((float)r.w, (float)r.h);
 701  #if SDL_HAS_USABLE_DISPLAY_BOUNDS
 702          SDL_GetDisplayUsableBounds(n, &r);
 703          monitor.WorkPos = ImVec2((float)r.x, (float)r.y);
 704          monitor.WorkSize = ImVec2((float)r.w, (float)r.h);
 705  #endif
 706  #if SDL_HAS_PER_MONITOR_DPI
 707          // FIXME-VIEWPORT: On MacOS SDL reports actual monitor DPI scale, ignoring OS configuration. We may want to set
 708          //  DpiScale to cocoa_window.backingScaleFactor here.
 709          float dpi = 0.0f;
 710          if (!SDL_GetDisplayDPI(n, &dpi, nullptr, nullptr))
 711              monitor.DpiScale = dpi / 96.0f;
 712  #endif
 713          platform_io.Monitors.push_back(monitor);
 714      }
 715  }
 716  
 717  void ImGui_ImplSDL2_NewFrame()
 718  {
 719      ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData();
 720      IM_ASSERT(bd != nullptr && "Did you call ImGui_ImplSDL2_Init()?");
 721      ImGuiIO& io = ImGui::GetIO();
 722  
 723      // Setup display size (every frame to accommodate for window resizing)
 724      int w, h;
 725      int display_w, display_h;
 726      SDL_GetWindowSize(bd->Window, &w, &h);
 727      if (SDL_GetWindowFlags(bd->Window) & SDL_WINDOW_MINIMIZED)
 728          w = h = 0;
 729      if (bd->Renderer != nullptr)
 730          SDL_GetRendererOutputSize(bd->Renderer, &display_w, &display_h);
 731      else
 732          SDL_GL_GetDrawableSize(bd->Window, &display_w, &display_h);
 733      io.DisplaySize = ImVec2((float)w, (float)h);
 734      if (w > 0 && h > 0)
 735          io.DisplayFramebufferScale = ImVec2((float)display_w / w, (float)display_h / h);
 736  
 737      // Setup time step (we don't use SDL_GetTicks() because it is using millisecond resolution)
 738      // (Accept SDL_GetPerformanceCounter() not returning a monotonically increasing value. Happens in VMs and Emscripten, see #6189, #6114, #3644)
 739      static Uint64 frequency = SDL_GetPerformanceFrequency();
 740      Uint64 current_time = SDL_GetPerformanceCounter();
 741      if (current_time <= bd->Time)
 742          current_time = bd->Time + 1;
 743      io.DeltaTime = bd->Time > 0 ? (float)((double)(current_time - bd->Time) / frequency) : (float)(1.0f / 60.0f);
 744      bd->Time = current_time;
 745  
 746      if (bd->PendingMouseLeaveFrame && bd->PendingMouseLeaveFrame >= ImGui::GetFrameCount() && bd->MouseButtonsDown == 0)
 747      {
 748          bd->MouseWindowID = 0;
 749          bd->PendingMouseLeaveFrame = 0;
 750          io.AddMousePosEvent(-FLT_MAX, -FLT_MAX);
 751      }
 752  
 753      // Our io.AddMouseViewportEvent() calls will only be valid when not capturing.
 754      // Technically speaking testing for 'bd->MouseButtonsDown == 0' would be more rygorous, but testing for payload reduces noise and potential side-effects.
 755      if (bd->MouseCanReportHoveredViewport && ImGui::GetDragDropPayload() == nullptr)
 756          io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport;
 757      else
 758          io.BackendFlags &= ~ImGuiBackendFlags_HasMouseHoveredViewport;
 759  
 760      ImGui_ImplSDL2_UpdateMouseData();
 761      ImGui_ImplSDL2_UpdateMouseCursor();
 762  
 763      // Update game controllers (if enabled and available)
 764      ImGui_ImplSDL2_UpdateGamepads();
 765  }
 766  
 767  //--------------------------------------------------------------------------------------------------------
 768  // MULTI-VIEWPORT / PLATFORM INTERFACE SUPPORT
 769  // This is an _advanced_ and _optional_ feature, allowing the backend to create and handle multiple viewports simultaneously.
 770  // If you are new to dear imgui or creating a new binding for dear imgui, it is recommended that you completely ignore this section first..
 771  //--------------------------------------------------------------------------------------------------------
 772  
 773  // Helper structure we store in the void* RenderUserData field of each ImGuiViewport to easily retrieve our backend data.
 774  struct ImGui_ImplSDL2_ViewportData
 775  {
 776      SDL_Window*     Window;
 777      Uint32          WindowID;
 778      bool            WindowOwned;
 779      SDL_GLContext   GLContext;
 780  
 781      ImGui_ImplSDL2_ViewportData() { Window = nullptr; WindowID = 0; WindowOwned = false; GLContext = nullptr; }
 782      ~ImGui_ImplSDL2_ViewportData() { IM_ASSERT(Window == nullptr && GLContext == nullptr); }
 783  };
 784  
 785  static void ImGui_ImplSDL2_CreateWindow(ImGuiViewport* viewport)
 786  {
 787      ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData();
 788      ImGui_ImplSDL2_ViewportData* vd = IM_NEW(ImGui_ImplSDL2_ViewportData)();
 789      viewport->PlatformUserData = vd;
 790  
 791      ImGuiViewport* main_viewport = ImGui::GetMainViewport();
 792      ImGui_ImplSDL2_ViewportData* main_viewport_data = (ImGui_ImplSDL2_ViewportData*)main_viewport->PlatformUserData;
 793  
 794      // Share GL resources with main context
 795      bool use_opengl = (main_viewport_data->GLContext != nullptr);
 796      SDL_GLContext backup_context = nullptr;
 797      if (use_opengl)
 798      {
 799          backup_context = SDL_GL_GetCurrentContext();
 800          SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1);
 801          SDL_GL_MakeCurrent(main_viewport_data->Window, main_viewport_data->GLContext);
 802      }
 803  
 804      Uint32 sdl_flags = 0;
 805      sdl_flags |= use_opengl ? SDL_WINDOW_OPENGL : (bd->UseVulkan ? SDL_WINDOW_VULKAN : 0);
 806      sdl_flags |= SDL_GetWindowFlags(bd->Window) & SDL_WINDOW_ALLOW_HIGHDPI;
 807      sdl_flags |= SDL_WINDOW_HIDDEN;
 808      sdl_flags |= (viewport->Flags & ImGuiViewportFlags_NoDecoration) ? SDL_WINDOW_BORDERLESS : 0;
 809      sdl_flags |= (viewport->Flags & ImGuiViewportFlags_NoDecoration) ? 0 : SDL_WINDOW_RESIZABLE;
 810  #if !defined(_WIN32)
 811      // See SDL hack in ImGui_ImplSDL2_ShowWindow().
 812      sdl_flags |= (viewport->Flags & ImGuiViewportFlags_NoTaskBarIcon) ? SDL_WINDOW_SKIP_TASKBAR : 0;
 813  #endif
 814  #if SDL_HAS_ALWAYS_ON_TOP
 815      sdl_flags |= (viewport->Flags & ImGuiViewportFlags_TopMost) ? SDL_WINDOW_ALWAYS_ON_TOP : 0;
 816  #endif
 817      vd->Window = SDL_CreateWindow("No Title Yet", (int)viewport->Pos.x, (int)viewport->Pos.y, (int)viewport->Size.x, (int)viewport->Size.y, sdl_flags);
 818      vd->WindowOwned = true;
 819      if (use_opengl)
 820      {
 821          vd->GLContext = SDL_GL_CreateContext(vd->Window);
 822          SDL_GL_SetSwapInterval(0);
 823      }
 824      if (use_opengl && backup_context)
 825          SDL_GL_MakeCurrent(vd->Window, backup_context);
 826  
 827      viewport->PlatformHandle = (void*)vd->Window;
 828      viewport->PlatformHandleRaw = nullptr;
 829      SDL_SysWMinfo info;
 830      SDL_VERSION(&info.version);
 831      if (SDL_GetWindowWMInfo(vd->Window, &info))
 832      {
 833  #if defined(SDL_VIDEO_DRIVER_WINDOWS)
 834          viewport->PlatformHandleRaw = info.info.win.window;
 835  #elif defined(__APPLE__) && defined(SDL_VIDEO_DRIVER_COCOA)
 836          viewport->PlatformHandleRaw = (void*)info.info.cocoa.window;
 837  #endif
 838      }
 839  }
 840  
 841  static void ImGui_ImplSDL2_DestroyWindow(ImGuiViewport* viewport)
 842  {
 843      if (ImGui_ImplSDL2_ViewportData* vd = (ImGui_ImplSDL2_ViewportData*)viewport->PlatformUserData)
 844      {
 845          if (vd->GLContext && vd->WindowOwned)
 846              SDL_GL_DeleteContext(vd->GLContext);
 847          if (vd->Window && vd->WindowOwned)
 848              SDL_DestroyWindow(vd->Window);
 849          vd->GLContext = nullptr;
 850          vd->Window = nullptr;
 851          IM_DELETE(vd);
 852      }
 853      viewport->PlatformUserData = viewport->PlatformHandle = nullptr;
 854  }
 855  
 856  static void ImGui_ImplSDL2_ShowWindow(ImGuiViewport* viewport)
 857  {
 858      ImGui_ImplSDL2_ViewportData* vd = (ImGui_ImplSDL2_ViewportData*)viewport->PlatformUserData;
 859  #if defined(_WIN32)
 860      HWND hwnd = (HWND)viewport->PlatformHandleRaw;
 861  
 862      // SDL hack: Hide icon from task bar
 863      // Note: SDL 2.0.6+ has a SDL_WINDOW_SKIP_TASKBAR flag which is supported under Windows but the way it create the window breaks our seamless transition.
 864      if (viewport->Flags & ImGuiViewportFlags_NoTaskBarIcon)
 865      {
 866          LONG ex_style = ::GetWindowLong(hwnd, GWL_EXSTYLE);
 867          ex_style &= ~WS_EX_APPWINDOW;
 868          ex_style |= WS_EX_TOOLWINDOW;
 869          ::SetWindowLong(hwnd, GWL_EXSTYLE, ex_style);
 870      }
 871  
 872      // SDL hack: SDL always activate/focus windows :/
 873      if (viewport->Flags & ImGuiViewportFlags_NoFocusOnAppearing)
 874      {
 875          ::ShowWindow(hwnd, SW_SHOWNA);
 876          return;
 877      }
 878  #endif
 879  
 880      SDL_ShowWindow(vd->Window);
 881  }
 882  
 883  static ImVec2 ImGui_ImplSDL2_GetWindowPos(ImGuiViewport* viewport)
 884  {
 885      ImGui_ImplSDL2_ViewportData* vd = (ImGui_ImplSDL2_ViewportData*)viewport->PlatformUserData;
 886      int x = 0, y = 0;
 887      SDL_GetWindowPosition(vd->Window, &x, &y);
 888      return ImVec2((float)x, (float)y);
 889  }
 890  
 891  static void ImGui_ImplSDL2_SetWindowPos(ImGuiViewport* viewport, ImVec2 pos)
 892  {
 893      ImGui_ImplSDL2_ViewportData* vd = (ImGui_ImplSDL2_ViewportData*)viewport->PlatformUserData;
 894      SDL_SetWindowPosition(vd->Window, (int)pos.x, (int)pos.y);
 895  }
 896  
 897  static ImVec2 ImGui_ImplSDL2_GetWindowSize(ImGuiViewport* viewport)
 898  {
 899      ImGui_ImplSDL2_ViewportData* vd = (ImGui_ImplSDL2_ViewportData*)viewport->PlatformUserData;
 900      int w = 0, h = 0;
 901      SDL_GetWindowSize(vd->Window, &w, &h);
 902      return ImVec2((float)w, (float)h);
 903  }
 904  
 905  static void ImGui_ImplSDL2_SetWindowSize(ImGuiViewport* viewport, ImVec2 size)
 906  {
 907      ImGui_ImplSDL2_ViewportData* vd = (ImGui_ImplSDL2_ViewportData*)viewport->PlatformUserData;
 908      SDL_SetWindowSize(vd->Window, (int)size.x, (int)size.y);
 909  }
 910  
 911  static void ImGui_ImplSDL2_SetWindowTitle(ImGuiViewport* viewport, const char* title)
 912  {
 913      ImGui_ImplSDL2_ViewportData* vd = (ImGui_ImplSDL2_ViewportData*)viewport->PlatformUserData;
 914      SDL_SetWindowTitle(vd->Window, title);
 915  }
 916  
 917  #if SDL_HAS_WINDOW_ALPHA
 918  static void ImGui_ImplSDL2_SetWindowAlpha(ImGuiViewport* viewport, float alpha)
 919  {
 920      ImGui_ImplSDL2_ViewportData* vd = (ImGui_ImplSDL2_ViewportData*)viewport->PlatformUserData;
 921      SDL_SetWindowOpacity(vd->Window, alpha);
 922  }
 923  #endif
 924  
 925  static void ImGui_ImplSDL2_SetWindowFocus(ImGuiViewport* viewport)
 926  {
 927      ImGui_ImplSDL2_ViewportData* vd = (ImGui_ImplSDL2_ViewportData*)viewport->PlatformUserData;
 928      SDL_RaiseWindow(vd->Window);
 929  }
 930  
 931  static bool ImGui_ImplSDL2_GetWindowFocus(ImGuiViewport* viewport)
 932  {
 933      ImGui_ImplSDL2_ViewportData* vd = (ImGui_ImplSDL2_ViewportData*)viewport->PlatformUserData;
 934      return (SDL_GetWindowFlags(vd->Window) & SDL_WINDOW_INPUT_FOCUS) != 0;
 935  }
 936  
 937  static bool ImGui_ImplSDL2_GetWindowMinimized(ImGuiViewport* viewport)
 938  {
 939      ImGui_ImplSDL2_ViewportData* vd = (ImGui_ImplSDL2_ViewportData*)viewport->PlatformUserData;
 940      return (SDL_GetWindowFlags(vd->Window) & SDL_WINDOW_MINIMIZED) != 0;
 941  }
 942  
 943  static void ImGui_ImplSDL2_RenderWindow(ImGuiViewport* viewport, void*)
 944  {
 945      ImGui_ImplSDL2_ViewportData* vd = (ImGui_ImplSDL2_ViewportData*)viewport->PlatformUserData;
 946      if (vd->GLContext)
 947          SDL_GL_MakeCurrent(vd->Window, vd->GLContext);
 948  }
 949  
 950  static void ImGui_ImplSDL2_SwapBuffers(ImGuiViewport* viewport, void*)
 951  {
 952      ImGui_ImplSDL2_ViewportData* vd = (ImGui_ImplSDL2_ViewportData*)viewport->PlatformUserData;
 953      if (vd->GLContext)
 954      {
 955          SDL_GL_MakeCurrent(vd->Window, vd->GLContext);
 956          SDL_GL_SwapWindow(vd->Window);
 957      }
 958  }
 959  
 960  // Vulkan support (the Vulkan renderer needs to call a platform-side support function to create the surface)
 961  // SDL is graceful enough to _not_ need <vulkan/vulkan.h> so we can safely include this.
 962  #if SDL_HAS_VULKAN
 963  #include <SDL_vulkan.h>
 964  static int ImGui_ImplSDL2_CreateVkSurface(ImGuiViewport* viewport, ImU64 vk_instance, const void* vk_allocator, ImU64* out_vk_surface)
 965  {
 966      ImGui_ImplSDL2_ViewportData* vd = (ImGui_ImplSDL2_ViewportData*)viewport->PlatformUserData;
 967      (void)vk_allocator;
 968      SDL_bool ret = SDL_Vulkan_CreateSurface(vd->Window, (VkInstance)vk_instance, (VkSurfaceKHR*)out_vk_surface);
 969      return ret ? 0 : 1; // ret ? VK_SUCCESS : VK_NOT_READY
 970  }
 971  #endif // SDL_HAS_VULKAN
 972  
 973  static void ImGui_ImplSDL2_InitPlatformInterface(SDL_Window* window, void* sdl_gl_context)
 974  {
 975      // Register platform interface (will be coupled with a renderer interface)
 976      ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
 977      platform_io.Platform_CreateWindow = ImGui_ImplSDL2_CreateWindow;
 978      platform_io.Platform_DestroyWindow = ImGui_ImplSDL2_DestroyWindow;
 979      platform_io.Platform_ShowWindow = ImGui_ImplSDL2_ShowWindow;
 980      platform_io.Platform_SetWindowPos = ImGui_ImplSDL2_SetWindowPos;
 981      platform_io.Platform_GetWindowPos = ImGui_ImplSDL2_GetWindowPos;
 982      platform_io.Platform_SetWindowSize = ImGui_ImplSDL2_SetWindowSize;
 983      platform_io.Platform_GetWindowSize = ImGui_ImplSDL2_GetWindowSize;
 984      platform_io.Platform_SetWindowFocus = ImGui_ImplSDL2_SetWindowFocus;
 985      platform_io.Platform_GetWindowFocus = ImGui_ImplSDL2_GetWindowFocus;
 986      platform_io.Platform_GetWindowMinimized = ImGui_ImplSDL2_GetWindowMinimized;
 987      platform_io.Platform_SetWindowTitle = ImGui_ImplSDL2_SetWindowTitle;
 988      platform_io.Platform_RenderWindow = ImGui_ImplSDL2_RenderWindow;
 989      platform_io.Platform_SwapBuffers = ImGui_ImplSDL2_SwapBuffers;
 990  #if SDL_HAS_WINDOW_ALPHA
 991      platform_io.Platform_SetWindowAlpha = ImGui_ImplSDL2_SetWindowAlpha;
 992  #endif
 993  #if SDL_HAS_VULKAN
 994      platform_io.Platform_CreateVkSurface = ImGui_ImplSDL2_CreateVkSurface;
 995  #endif
 996  
 997      // Register main window handle (which is owned by the main application, not by us)
 998      // This is mostly for simplicity and consistency, so that our code (e.g. mouse handling etc.) can use same logic for main and secondary viewports.
 999      ImGuiViewport* main_viewport = ImGui::GetMainViewport();
1000      ImGui_ImplSDL2_ViewportData* vd = IM_NEW(ImGui_ImplSDL2_ViewportData)();
1001      vd->Window = window;
1002      vd->WindowID = SDL_GetWindowID(window);
1003      vd->WindowOwned = false;
1004      vd->GLContext = sdl_gl_context;
1005      main_viewport->PlatformUserData = vd;
1006      main_viewport->PlatformHandle = vd->Window;
1007  }
1008  
1009  static void ImGui_ImplSDL2_ShutdownPlatformInterface()
1010  {
1011      ImGui::DestroyPlatformWindows();
1012  }