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 }