70.5 KB、1343行 生 Blame 履歴
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: Mouse support. Can discriminate Mouse/TouchScreen.
9// [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 are obsolete since 1.87 and not supported since 1.91.5]
10// [X] Platform: Gamepad support.
11// [X] Platform: Mouse cursor shape and visibility (ImGuiBackendFlags_HasMouseCursors). Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
12// [X] Platform: Basic IME support. App needs to call 'SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1");' before SDL_CreateWindow()!.
13// [X] Platform: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable'.
14// Missing features or Issues:
15// [ ] Platform: Multi-viewport: Minimized windows seems to break mouse wheel events (at least under Windows).
16// [ ] Platform: Multi-viewport: Missing ImGuiBackendFlags_HasParentViewport support. The viewport->ParentViewportID field is ignored, and therefore io.ConfigViewportsNoDefaultParent has no effect either.
17
18// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
19// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
20// Learn about Dear ImGui:
21// - FAQ https://dearimgui.com/faq
22// - Getting Started https://dearimgui.com/getting-started
23// - Documentation https://dearimgui.com/docs (same as your local docs/ folder).
24// - Introduction, links and more at the top of imgui.cpp
25
26// CHANGELOG
27// (minor and older changes stripped away, please see git history for details)
28// 2026-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface.
29// 2026-04-16: Made ImGui_ImplSDL2_GetContentScaleForWindow(), ImGui_ImplSDL2_GetContentScaleForDisplay() helpers return a minimum of 1.0f, as some Linux setup seems to report <1.0f value and this breaks scaling border size. (#9369)
30// 2026-03-09: [Docking] Fixed an issue dated 2025/04/09 (1.92 WIP) where a refactor+merge caused ImGuiBackendFlags_HasMouseHoveredViewport to never be set, causing foreign windows to be ignored when deciding of hovered viewport. (#9284)
31// 2026-02-13: Inputs: systems other than X11 are back to starting mouse capture on mouse down (reverts 2025-02-26 change). Only X11 requires waiting for a drag by default (not ideal, but a better default for X11 users). Added ImGui_ImplSDL2_SetMouseCaptureMode() for X11 debugger users. (#3650, #6410, #9235)
32// 2026-01-15: Changed GetClipboardText() handler to return nullptr on error aka clipboard contents is not text. Consistent with other backends. (#9168)
33// 2025-09-24: Skip using the SDL_GetGlobalMouseState() state when one of our window is hovered, as the SDL_MOUSEMOTION data is reliable. Fix macOS notch mouse coordinates issue in fullscreen mode + better perf on X11. (#7919, #7786)
34// 2025-09-18: Call platform_io.ClearPlatformHandlers() on shutdown.
35// 2025-09-15: Content Scales are always reported as 1.0 on Wayland. (#8921)
36// 2025-07-08: Made ImGui_ImplSDL2_GetContentScaleForWindow(), ImGui_ImplSDL2_GetContentScaleForDisplay() helpers return 1.0f on Emscripten and Android platforms, matching macOS logic. (#8742, #8733)
37// 2025-06-11: Added ImGui_ImplSDL2_GetContentScaleForWindow(SDL_Window* window) and ImGui_ImplSDL2_GetContentScaleForDisplay(int display_index) helper to facilitate making DPI-aware apps.
38// 2025-05-15: [Docking] Add Platform_GetWindowFramebufferScale() handler, to allow varying Retina display density on multiple monitors.
39// 2025-04-09: [Docking] Revert update monitors and work areas information every frame. Only do it on Windows. (#8415, #8558)
40// 2025-04-09: Don't attempt to call SDL_CaptureMouse() on drivers where we don't call SDL_GetGlobalMouseState(). (#8561)
41// 2025-03-21: Fill gamepad inputs and set ImGuiBackendFlags_HasGamepad regardless of ImGuiConfigFlags_NavEnableGamepad being set.
42// 2025-03-10: When dealing with OEM keys, use scancodes instead of translated keycodes to choose ImGuiKey values. (#7136, #7201, #7206, #7306, #7670, #7672, #8468)
43// 2025-02-26: Only start SDL_CaptureMouse() when mouse is being dragged, to mitigate issues with e.g. Linux debuggers not claiming capture back. (#6410, #3650)
44// 2025-02-25: [Docking] Revert to use SDL_GetDisplayBounds() for WorkPos/WorkRect if SDL_GetDisplayUsableBounds() failed.
45// 2025-02-24: Avoid calling SDL_GetGlobalMouseState() when mouse is in relative mode.
46// 2025-02-21: [Docking] Update monitors and work areas information every frame, as the later may change regardless of monitor changes. (#8415)
47// 2025-02-18: Added ImGuiMouseCursor_Wait and ImGuiMouseCursor_Progress mouse cursor support.
48// 2025-02-10: Using SDL_OpenURL() in platform_io.Platform_OpenInShellFn handler.
49// 2025-01-20: Made ImGui_ImplSDL2_SetGamepadMode(ImGui_ImplSDL2_GamepadMode_Manual) accept an empty array.
50// 2024-10-24: Emscripten: from SDL 2.30.9, SDL_EVENT_MOUSE_WHEEL event doesn't require dividing by 100.0f.
51// 2024-09-09: use SDL_Vulkan_GetDrawableSize() when available. (#7967, #3190)
52// 2024-08-22: moved some OS/backend related function pointers from ImGuiIO to ImGuiPlatformIO:
53// - io.GetClipboardTextFn -> platform_io.Platform_GetClipboardTextFn
54// - io.SetClipboardTextFn -> platform_io.Platform_SetClipboardTextFn
55// - io.PlatformOpenInShellFn -> platform_io.Platform_OpenInShellFn
56// - io.PlatformSetImeDataFn -> platform_io.Platform_SetImeDataFn
57// 2024-08-19: Storing SDL's Uint32 WindowID inside ImGuiViewport::PlatformHandle instead of SDL_Window*.
58// 2024-08-19: ImGui_ImplSDL2_ProcessEvent() now ignores events intended for other SDL windows. (#7853)
59// 2024-07-02: Emscripten: Added io.PlatformOpenInShellFn() handler for Emscripten versions.
60// 2024-07-02: Update for io.SetPlatformImeDataFn() -> io.PlatformSetImeDataFn() renaming in main library.
61// 2024-02-14: Inputs: Handle gamepad disconnection. Added ImGui_ImplSDL2_SetGamepadMode().
62// 2023-10-05: Inputs: Added support for extra ImGuiKey values: F13 to F24 function keys, app back/forward keys.
63// 2023-04-06: Inputs: Avoid calling SDL_StartTextInput()/SDL_StopTextInput() as they don't only pertain to IME. It's unclear exactly what their relation is to IME. (#6306)
64// 2023-04-04: Inputs: Added support for io.AddMouseSourceEvent() to discriminate ImGuiMouseSource_Mouse/ImGuiMouseSource_TouchScreen. (#2702)
65// 2023-02-23: Accept SDL_GetPerformanceCounter() not returning a monotonically increasing value. (#6189, #6114, #3644)
66// 2023-02-07: Implement IME handler (io.SetPlatformImeDataFn will call SDL_SetTextInputRect()/SDL_StartTextInput()).
67// 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.
68// 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).
69// 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)
70// 2023-02-02: Removed SDL_MOUSEWHEEL value clamping, as values seem correct in latest Emscripten. (#4019)
71// 2023-02-01: Flipping SDL_MOUSEWHEEL 'wheel.x' value to match other backends and offer consistent horizontal scrolling direction. (#4019, #6096, #1463)
72// 2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11.
73// 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)
74// 2022-09-26: Inputs: Renamed ImGuiKey_ModXXX introduced in 1.87 to ImGuiMod_XXX (old names still supported).
75// 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.
76// 2022-03-22: Inputs: Added support for extra mouse buttons (SDL_BUTTON_X1/SDL_BUTTON_X2).
77// 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.
78// 2022-01-26: Inputs: replaced short-lived io.AddKeyModsEvent() (added two weeks ago) with io.AddKeyEvent() using ImGuiKey_ModXXX flags. Sorry for the confusion.
79// 2021-01-20: Inputs: calling new io.AddKeyAnalogEvent() for gamepad support, instead of writing directly to io.NavInputs[].
80// 2022-01-17: Inputs: calling new io.AddMousePosEvent(), io.AddMouseButtonEvent(), io.AddMouseWheelEvent() API (1.87+).
81// 2022-01-17: Inputs: always update key mods next and before key event (not in NewFrame) to fix input queue with very low framerates.
82// 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.
83// 2022-01-12: Maintain our own copy of MouseButtonsDown mask instead of using ImGui::IsAnyMouseDown() which will be obsoleted.
84// 2022-01-10: Inputs: calling new io.AddKeyEvent(), io.AddKeyModsEvent() + io.SetKeyEventNativeData() API (1.87+). Support for full ImGuiKey range.
85// 2021-08-17: Calling io.AddFocusEvent() on SDL_WINDOWEVENT_FOCUS_GAINED/SDL_WINDOWEVENT_FOCUS_LOST.
86// 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+)
87// 2021-06:29: *BREAKING CHANGE* Removed 'SDL_Window* window' parameter to ImGui_ImplSDL2_NewFrame() which was unnecessary.
88// 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).
89// 2021-03-22: Rework global mouse pos availability check listing supported platforms explicitly, effectively fixing mouse access on Raspberry Pi. (#2837, #3950)
90// 2020-05-25: Misc: Report a zero display-size when window is minimized, to be consistent with other backends.
91// 2020-02-20: Inputs: Fixed mapping for ImGuiKey_KeyPadEnter (using SDL_SCANCODE_KP_ENTER instead of SDL_SCANCODE_RETURN2).
92// 2019-12-17: Inputs: On Wayland, use SDL_GetMouseState (because there is no global mouse state).
93// 2019-12-05: Inputs: Added support for ImGuiMouseCursor_NotAllowed mouse cursor.
94// 2019-07-21: Inputs: Added mapping for ImGuiKey_KeyPadEnter.
95// 2019-04-23: Inputs: Added support for SDL_GameController (if ImGuiConfigFlags_NavEnableGamepad is set by user application).
96// 2019-03-12: Misc: Preserve DisplayFramebufferScale when main window is minimized.
97// 2018-12-21: Inputs: Workaround for Android/iOS which don't seem to handle focus related calls.
98// 2018-11-30: Misc: Setting up io.BackendPlatformName so it can be displayed in the About Window.
99// 2018-11-14: Changed the signature of ImGui_ImplSDL2_ProcessEvent() to take a 'const SDL_Event*'.
100// 2018-08-01: Inputs: Workaround for Emscripten which doesn't seem to handle focus related calls.
101// 2018-06-29: Inputs: Added support for the ImGuiMouseCursor_Hand cursor.
102// 2018-06-08: Misc: Extracted imgui_impl_sdl.cpp/.h away from the old combined SDL2+OpenGL/Vulkan examples.
103// 2018-06-08: Misc: ImGui_ImplSDL2_InitForOpenGL() now takes a SDL_GLContext parameter.
104// 2018-05-09: Misc: Fixed clipboard paste memory leak (we didn't call SDL_FreeMemory on the data returned by SDL_GetClipboardText).
105// 2018-03-20: Misc: Setup io.BackendFlags ImGuiBackendFlags_HasMouseCursors flag + honor ImGuiConfigFlags_NoMouseCursorChange flag.
106// 2018-02-16: Inputs: Added support for mouse cursors, honoring ImGui::GetMouseCursor() value.
107// 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves.
108// 2018-02-06: Inputs: Added mapping for ImGuiKey_Space.
109// 2018-02-05: Misc: Using SDL_GetPerformanceCounter() instead of SDL_GetTicks() to be able to handle very high framerate (1000+ FPS).
110// 2018-02-05: Inputs: Keyboard mapping is using scancodes everywhere instead of a confusing mixture of keycodes and scancodes.
111// 2018-01-20: Inputs: Added Horizontal Mouse Wheel support.
112// 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.
113// 2018-01-18: Inputs: Added mapping for ImGuiKey_Insert.
114// 2017-08-25: Inputs: MousePos set to -FLT_MAX,-FLT_MAX when mouse is unavailable/missing (instead of -1,-1).
115// 2016-10-15: Misc: Added a void* user_data parameter to Clipboard function handlers.
116
117#include "imgui.h"
118#ifndef IMGUI_DISABLE
119#include "imgui_impl_sdl2.h"
120
121// Clang warnings with -Weverything
122#if defined(__clang__)
123#pragma clang diagnostic push
124#pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast
125#pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision
126#endif
127
128// SDL
129// (the multi-viewports feature requires SDL features supported from SDL 2.0.4+. SDL 2.0.5+ is highly recommended)
130#include <SDL.h>
131#include <SDL_syswm.h>
132#include <stdio.h> // for snprintf()
133#ifdef __APPLE__
134#include <TargetConditionals.h>
135#endif
136#ifdef __EMSCRIPTEN__
137#include <emscripten/em_js.h>
138#endif
139#undef Status // X11 headers are leaking this.
140
141#if SDL_VERSION_ATLEAST(2,0,4) && !defined(__EMSCRIPTEN__) && !defined(__ANDROID__) && !(defined(__APPLE__) && TARGET_OS_IOS) && !defined(__amigaos4__)
142#define SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE 1
143#else
144#define SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE 0
145#endif
146#define SDL_HAS_WINDOW_ALPHA SDL_VERSION_ATLEAST(2,0,5)
147#define SDL_HAS_ALWAYS_ON_TOP SDL_VERSION_ATLEAST(2,0,5)
148#define SDL_HAS_USABLE_DISPLAY_BOUNDS SDL_VERSION_ATLEAST(2,0,5)
149#define SDL_HAS_PER_MONITOR_DPI SDL_VERSION_ATLEAST(2,0,4)
150#define SDL_HAS_VULKAN SDL_VERSION_ATLEAST(2,0,6)
151#define SDL_HAS_DISPLAY_EVENT SDL_VERSION_ATLEAST(2,0,9)
152#define SDL_HAS_OPEN_URL SDL_VERSION_ATLEAST(2,0,14)
153#define SDL_HAS_SHOW_WINDOW_ACTIVATION_HINT SDL_VERSION_ATLEAST(2,0,18)
154#if SDL_HAS_VULKAN
155#include <SDL_vulkan.h>
156#else
157static const Uint32 SDL_WINDOW_VULKAN = 0x10000000;
158#endif
159
160// SDL Data
161struct ImGui_ImplSDL2_Data
162{
163 SDL_Window* Window;
164 Uint32 WindowID; // Stored in ImGuiViewport::PlatformHandle. Use SDL_GetWindowFromID() to get SDL_Window* from Uint32 WindowID.
165 SDL_Renderer* Renderer;
166 Uint64 Time;
167 char* ClipboardTextData;
168 char BackendPlatformName[48];
169 bool UseVulkan;
170 bool WantUpdateMonitors;
171
172 // Mouse handling
173 Uint32 MouseWindowID;
174 int MouseButtonsDown;
175 SDL_Cursor* MouseCursors[ImGuiMouseCursor_COUNT];
176 SDL_Cursor* MouseLastCursor;
177 int MouseLastLeaveFrame;
178 bool MouseCanUseGlobalState;
179 bool MouseCanReportHoveredViewport; // This is hard to use/unreliable on SDL so we'll set ImGuiBackendFlags_HasMouseHoveredViewport dynamically based on state.
180 ImGui_ImplSDL2_MouseCaptureMode MouseCaptureMode;
181
182 // Gamepad handling
183 ImVector<SDL_GameController*> Gamepads;
184 ImGui_ImplSDL2_GamepadMode GamepadMode;
185 bool WantUpdateGamepadsList;
186
187 ImGui_ImplSDL2_Data() { memset((void*)this, 0, sizeof(*this)); }
188};
189
190// Backend data stored in io.BackendPlatformUserData to allow support for multiple Dear ImGui contexts
191// It is STRONGLY preferred that you use docking branch with multi-viewports (== single Dear ImGui context + multiple windows) instead of multiple Dear ImGui contexts.
192// FIXME: multi-context support is not well tested and probably dysfunctional in this backend.
193// FIXME: some shared resources (mouse cursor shape, gamepad) are mishandled when using multi-context.
194static ImGui_ImplSDL2_Data* ImGui_ImplSDL2_GetBackendData()
195{
196 return ImGui::GetCurrentContext() ? (ImGui_ImplSDL2_Data*)ImGui::GetIO().BackendPlatformUserData : nullptr;
197}
198
199// Forward Declarations
200static void ImGui_ImplSDL2_UpdateMonitors();
201static void ImGui_ImplSDL2_InitMultiViewportSupport(SDL_Window* window, void* sdl_gl_context);
202static void ImGui_ImplSDL2_ShutdownMultiViewportSupport();
203
204// Functions
205static const char* ImGui_ImplSDL2_GetClipboardText(ImGuiContext*)
206{
207 ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData();
208 if (bd->ClipboardTextData)
209 SDL_free(bd->ClipboardTextData);
210 if (SDL_HasClipboardText())
211 bd->ClipboardTextData = SDL_GetClipboardText();
212 else
213 bd->ClipboardTextData = nullptr;
214 return bd->ClipboardTextData;
215}
216
217static void ImGui_ImplSDL2_SetClipboardText(ImGuiContext*, const char* text)
218{
219 SDL_SetClipboardText(text);
220}
221
222// Note: native IME will only display if user calls SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1") _before_ SDL_CreateWindow().
223static void ImGui_ImplSDL2_PlatformSetImeData(ImGuiContext*, ImGuiViewport* viewport, ImGuiPlatformImeData* data)
224{
225 if (data->WantVisible)
226 {
227 SDL_Rect r;
228 r.x = (int)(data->InputPos.x - viewport->Pos.x);
229 r.y = (int)(data->InputPos.y - viewport->Pos.y);
230 r.w = 1;
231 r.h = (int)data->InputLineHeight;
232 SDL_SetTextInputRect(&r);
233 }
234}
235
236// Not static to allow third-party code to use that if they want to (but undocumented)
237ImGuiKey ImGui_ImplSDL2_KeyEventToImGuiKey(SDL_Keycode keycode, SDL_Scancode scancode);
238ImGuiKey ImGui_ImplSDL2_KeyEventToImGuiKey(SDL_Keycode keycode, SDL_Scancode scancode)
239{
240 switch (keycode)
241 {
242 case SDLK_TAB: return ImGuiKey_Tab;
243 case SDLK_LEFT: return ImGuiKey_LeftArrow;
244 case SDLK_RIGHT: return ImGuiKey_RightArrow;
245 case SDLK_UP: return ImGuiKey_UpArrow;
246 case SDLK_DOWN: return ImGuiKey_DownArrow;
247 case SDLK_PAGEUP: return ImGuiKey_PageUp;
248 case SDLK_PAGEDOWN: return ImGuiKey_PageDown;
249 case SDLK_HOME: return ImGuiKey_Home;
250 case SDLK_END: return ImGuiKey_End;
251 case SDLK_INSERT: return ImGuiKey_Insert;
252 case SDLK_DELETE: return ImGuiKey_Delete;
253 case SDLK_BACKSPACE: return ImGuiKey_Backspace;
254 case SDLK_SPACE: return ImGuiKey_Space;
255 case SDLK_RETURN: return ImGuiKey_Enter;
256 case SDLK_ESCAPE: return ImGuiKey_Escape;
257 //case SDLK_QUOTE: return ImGuiKey_Apostrophe;
258 case SDLK_COMMA: return ImGuiKey_Comma;
259 //case SDLK_MINUS: return ImGuiKey_Minus;
260 case SDLK_PERIOD: return ImGuiKey_Period;
261 //case SDLK_SLASH: return ImGuiKey_Slash;
262 case SDLK_SEMICOLON: return ImGuiKey_Semicolon;
263 //case SDLK_EQUALS: return ImGuiKey_Equal;
264 //case SDLK_LEFTBRACKET: return ImGuiKey_LeftBracket;
265 //case SDLK_BACKSLASH: return ImGuiKey_Backslash;
266 //case SDLK_RIGHTBRACKET: return ImGuiKey_RightBracket;
267 //case SDLK_BACKQUOTE: return ImGuiKey_GraveAccent;
268 case SDLK_CAPSLOCK: return ImGuiKey_CapsLock;
269 case SDLK_SCROLLLOCK: return ImGuiKey_ScrollLock;
270 case SDLK_NUMLOCKCLEAR: return ImGuiKey_NumLock;
271 case SDLK_PRINTSCREEN: return ImGuiKey_PrintScreen;
272 case SDLK_PAUSE: return ImGuiKey_Pause;
273 case SDLK_KP_0: return ImGuiKey_Keypad0;
274 case SDLK_KP_1: return ImGuiKey_Keypad1;
275 case SDLK_KP_2: return ImGuiKey_Keypad2;
276 case SDLK_KP_3: return ImGuiKey_Keypad3;
277 case SDLK_KP_4: return ImGuiKey_Keypad4;
278 case SDLK_KP_5: return ImGuiKey_Keypad5;
279 case SDLK_KP_6: return ImGuiKey_Keypad6;
280 case SDLK_KP_7: return ImGuiKey_Keypad7;
281 case SDLK_KP_8: return ImGuiKey_Keypad8;
282 case SDLK_KP_9: return ImGuiKey_Keypad9;
283 case SDLK_KP_PERIOD: return ImGuiKey_KeypadDecimal;
284 case SDLK_KP_DIVIDE: return ImGuiKey_KeypadDivide;
285 case SDLK_KP_MULTIPLY: return ImGuiKey_KeypadMultiply;
286 case SDLK_KP_MINUS: return ImGuiKey_KeypadSubtract;
287 case SDLK_KP_PLUS: return ImGuiKey_KeypadAdd;
288 case SDLK_KP_ENTER: return ImGuiKey_KeypadEnter;
289 case SDLK_KP_EQUALS: return ImGuiKey_KeypadEqual;
290 case SDLK_LCTRL: return ImGuiKey_LeftCtrl;
291 case SDLK_LSHIFT: return ImGuiKey_LeftShift;
292 case SDLK_LALT: return ImGuiKey_LeftAlt;
293 case SDLK_LGUI: return ImGuiKey_LeftSuper;
294 case SDLK_RCTRL: return ImGuiKey_RightCtrl;
295 case SDLK_RSHIFT: return ImGuiKey_RightShift;
296 case SDLK_RALT: return ImGuiKey_RightAlt;
297 case SDLK_RGUI: return ImGuiKey_RightSuper;
298 case SDLK_APPLICATION: return ImGuiKey_Menu;
299 case SDLK_0: return ImGuiKey_0;
300 case SDLK_1: return ImGuiKey_1;
301 case SDLK_2: return ImGuiKey_2;
302 case SDLK_3: return ImGuiKey_3;
303 case SDLK_4: return ImGuiKey_4;
304 case SDLK_5: return ImGuiKey_5;
305 case SDLK_6: return ImGuiKey_6;
306 case SDLK_7: return ImGuiKey_7;
307 case SDLK_8: return ImGuiKey_8;
308 case SDLK_9: return ImGuiKey_9;
309 case SDLK_a: return ImGuiKey_A;
310 case SDLK_b: return ImGuiKey_B;
311 case SDLK_c: return ImGuiKey_C;
312 case SDLK_d: return ImGuiKey_D;
313 case SDLK_e: return ImGuiKey_E;
314 case SDLK_f: return ImGuiKey_F;
315 case SDLK_g: return ImGuiKey_G;
316 case SDLK_h: return ImGuiKey_H;
317 case SDLK_i: return ImGuiKey_I;
318 case SDLK_j: return ImGuiKey_J;
319 case SDLK_k: return ImGuiKey_K;
320 case SDLK_l: return ImGuiKey_L;
321 case SDLK_m: return ImGuiKey_M;
322 case SDLK_n: return ImGuiKey_N;
323 case SDLK_o: return ImGuiKey_O;
324 case SDLK_p: return ImGuiKey_P;
325 case SDLK_q: return ImGuiKey_Q;
326 case SDLK_r: return ImGuiKey_R;
327 case SDLK_s: return ImGuiKey_S;
328 case SDLK_t: return ImGuiKey_T;
329 case SDLK_u: return ImGuiKey_U;
330 case SDLK_v: return ImGuiKey_V;
331 case SDLK_w: return ImGuiKey_W;
332 case SDLK_x: return ImGuiKey_X;
333 case SDLK_y: return ImGuiKey_Y;
334 case SDLK_z: return ImGuiKey_Z;
335 case SDLK_F1: return ImGuiKey_F1;
336 case SDLK_F2: return ImGuiKey_F2;
337 case SDLK_F3: return ImGuiKey_F3;
338 case SDLK_F4: return ImGuiKey_F4;
339 case SDLK_F5: return ImGuiKey_F5;
340 case SDLK_F6: return ImGuiKey_F6;
341 case SDLK_F7: return ImGuiKey_F7;
342 case SDLK_F8: return ImGuiKey_F8;
343 case SDLK_F9: return ImGuiKey_F9;
344 case SDLK_F10: return ImGuiKey_F10;
345 case SDLK_F11: return ImGuiKey_F11;
346 case SDLK_F12: return ImGuiKey_F12;
347 case SDLK_F13: return ImGuiKey_F13;
348 case SDLK_F14: return ImGuiKey_F14;
349 case SDLK_F15: return ImGuiKey_F15;
350 case SDLK_F16: return ImGuiKey_F16;
351 case SDLK_F17: return ImGuiKey_F17;
352 case SDLK_F18: return ImGuiKey_F18;
353 case SDLK_F19: return ImGuiKey_F19;
354 case SDLK_F20: return ImGuiKey_F20;
355 case SDLK_F21: return ImGuiKey_F21;
356 case SDLK_F22: return ImGuiKey_F22;
357 case SDLK_F23: return ImGuiKey_F23;
358 case SDLK_F24: return ImGuiKey_F24;
359 case SDLK_AC_BACK: return ImGuiKey_AppBack;
360 case SDLK_AC_FORWARD: return ImGuiKey_AppForward;
361 default: break;
362 }
363
364 // Fallback to scancode
365 switch (scancode)
366 {
367 case SDL_SCANCODE_GRAVE: return ImGuiKey_GraveAccent;
368 case SDL_SCANCODE_MINUS: return ImGuiKey_Minus;
369 case SDL_SCANCODE_EQUALS: return ImGuiKey_Equal;
370 case SDL_SCANCODE_LEFTBRACKET: return ImGuiKey_LeftBracket;
371 case SDL_SCANCODE_RIGHTBRACKET: return ImGuiKey_RightBracket;
372 case SDL_SCANCODE_NONUSBACKSLASH: return ImGuiKey_Oem102;
373 case SDL_SCANCODE_BACKSLASH: return ImGuiKey_Backslash;
374 case SDL_SCANCODE_SEMICOLON: return ImGuiKey_Semicolon;
375 case SDL_SCANCODE_APOSTROPHE: return ImGuiKey_Apostrophe;
376 case SDL_SCANCODE_COMMA: return ImGuiKey_Comma;
377 case SDL_SCANCODE_PERIOD: return ImGuiKey_Period;
378 case SDL_SCANCODE_SLASH: return ImGuiKey_Slash;
379 default: break;
380 }
381 return ImGuiKey_None;
382}
383
384static void ImGui_ImplSDL2_UpdateKeyModifiers(SDL_Keymod sdl_key_mods)
385{
386 ImGuiIO& io = ImGui::GetIO();
387 io.AddKeyEvent(ImGuiMod_Ctrl, (sdl_key_mods & KMOD_CTRL) != 0);
388 io.AddKeyEvent(ImGuiMod_Shift, (sdl_key_mods & KMOD_SHIFT) != 0);
389 io.AddKeyEvent(ImGuiMod_Alt, (sdl_key_mods & KMOD_ALT) != 0);
390 io.AddKeyEvent(ImGuiMod_Super, (sdl_key_mods & KMOD_GUI) != 0);
391}
392
393static ImGuiViewport* ImGui_ImplSDL2_GetViewportForWindowID(Uint32 window_id)
394{
395 return ImGui::FindViewportByPlatformHandle((void*)(intptr_t)window_id);
396}
397
398// You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs.
399// - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data.
400// - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data.
401// Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags.
402bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event)
403{
404 ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData();
405 IM_ASSERT(bd != nullptr && "Context or backend not initialized! Did you call ImGui_ImplSDL2_Init()?");
406 ImGuiIO& io = ImGui::GetIO();
407
408 switch (event->type)
409 {
410 case SDL_MOUSEMOTION:
411 {
412 if (ImGui_ImplSDL2_GetViewportForWindowID(event->motion.windowID) == nullptr)
413 return false;
414 ImVec2 mouse_pos((float)event->motion.x, (float)event->motion.y);
415 if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
416 {
417 int window_x, window_y;
418 SDL_GetWindowPosition(SDL_GetWindowFromID(event->motion.windowID), &window_x, &window_y);
419 mouse_pos.x += window_x;
420 mouse_pos.y += window_y;
421 }
422 io.AddMouseSourceEvent(event->motion.which == SDL_TOUCH_MOUSEID ? ImGuiMouseSource_TouchScreen : ImGuiMouseSource_Mouse);
423 io.AddMousePosEvent(mouse_pos.x, mouse_pos.y);
424 return true;
425 }
426 case SDL_MOUSEWHEEL:
427 {
428 if (ImGui_ImplSDL2_GetViewportForWindowID(event->wheel.windowID) == nullptr)
429 return false;
430 //IMGUI_DEBUG_LOG("wheel %.2f %.2f, precise %.2f %.2f\n", (float)event->wheel.x, (float)event->wheel.y, event->wheel.preciseX, event->wheel.preciseY);
431#if SDL_VERSION_ATLEAST(2,0,18) // If this fails to compile on Emscripten: update to latest Emscripten!
432 float wheel_x = -event->wheel.preciseX;
433 float wheel_y = event->wheel.preciseY;
434#else
435 float wheel_x = -(float)event->wheel.x;
436 float wheel_y = (float)event->wheel.y;
437#endif
438#if defined(__EMSCRIPTEN__) && !SDL_VERSION_ATLEAST(2,31,0)
439 wheel_x /= 100.0f;
440#endif
441 io.AddMouseSourceEvent(event->wheel.which == SDL_TOUCH_MOUSEID ? ImGuiMouseSource_TouchScreen : ImGuiMouseSource_Mouse);
442 io.AddMouseWheelEvent(wheel_x, wheel_y);
443 return true;
444 }
445 case SDL_MOUSEBUTTONDOWN:
446 case SDL_MOUSEBUTTONUP:
447 {
448 if (ImGui_ImplSDL2_GetViewportForWindowID(event->button.windowID) == nullptr)
449 return false;
450 int mouse_button = -1;
451 if (event->button.button == SDL_BUTTON_LEFT) { mouse_button = 0; }
452 if (event->button.button == SDL_BUTTON_RIGHT) { mouse_button = 1; }
453 if (event->button.button == SDL_BUTTON_MIDDLE) { mouse_button = 2; }
454 if (event->button.button == SDL_BUTTON_X1) { mouse_button = 3; }
455 if (event->button.button == SDL_BUTTON_X2) { mouse_button = 4; }
456 if (mouse_button == -1)
457 break;
458 io.AddMouseSourceEvent(event->button.which == SDL_TOUCH_MOUSEID ? ImGuiMouseSource_TouchScreen : ImGuiMouseSource_Mouse);
459 io.AddMouseButtonEvent(mouse_button, (event->type == SDL_MOUSEBUTTONDOWN));
460 bd->MouseButtonsDown = (event->type == SDL_MOUSEBUTTONDOWN) ? (bd->MouseButtonsDown | (1 << mouse_button)) : (bd->MouseButtonsDown & ~(1 << mouse_button));
461 return true;
462 }
463 case SDL_TEXTINPUT:
464 {
465 if (ImGui_ImplSDL2_GetViewportForWindowID(event->text.windowID) == nullptr)
466 return false;
467 io.AddInputCharactersUTF8(event->text.text);
468 return true;
469 }
470 case SDL_KEYDOWN:
471 case SDL_KEYUP:
472 {
473 ImGuiViewport* viewport = ImGui_ImplSDL2_GetViewportForWindowID(event->key.windowID);
474 if (viewport == nullptr)
475 return false;
476 //IMGUI_DEBUG_LOG("SDL_KEY%s : key=0x%08X ('%s'), scancode=%d ('%s'), mod=%X, windowID=%d, viewport=%08X\n",
477 // (event->type == SDL_KEYDOWN) ? "DOWN" : "UP ", event->key.keysym.sym, SDL_GetKeyName(event->key.keysym.sym), event->key.keysym.scancode, SDL_GetScancodeName(event->key.keysym.scancode), event->key.keysym.mod, event->key.windowID, viewport ? viewport->ID : 0);
478 ImGui_ImplSDL2_UpdateKeyModifiers((SDL_Keymod)event->key.keysym.mod);
479 ImGuiKey key = ImGui_ImplSDL2_KeyEventToImGuiKey(event->key.keysym.sym, event->key.keysym.scancode);
480 io.AddKeyEvent(key, (event->type == SDL_KEYDOWN));
481 io.SetKeyEventNativeData(key, (int)event->key.keysym.sym, (int)event->key.keysym.scancode, (int)event->key.keysym.scancode); // To support legacy indexing (<1.87 user code). Legacy backend uses SDLK_*** as indices to IsKeyXXX() functions.
482 return true;
483 }
484#if SDL_HAS_DISPLAY_EVENT
485 case SDL_DISPLAYEVENT:
486 {
487 // 2.0.26 has SDL_DISPLAYEVENT_CONNECTED/SDL_DISPLAYEVENT_DISCONNECTED/SDL_DISPLAYEVENT_ORIENTATION,
488 // so change of DPI/Scaling are not reflected in this event. (SDL3 has it)
489 bd->WantUpdateMonitors = true;
490 return true;
491 }
492#endif
493 case SDL_WINDOWEVENT:
494 {
495 ImGuiViewport* viewport = ImGui_ImplSDL2_GetViewportForWindowID(event->window.windowID);
496 if (viewport == NULL)
497 return false;
498
499 // - 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.
500 // - However we won't get a correct LEAVE event for a captured window.
501 // - In some cases, when detaching a window from main viewport SDL may send SDL_WINDOWEVENT_ENTER one frame too late,
502 // causing SDL_WINDOWEVENT_LEAVE on previous frame to interrupt drag operation by clear mouse position. This is why
503 // we delay process the SDL_WINDOWEVENT_LEAVE events by one frame. See issue #5012 for details.
504 Uint8 window_event = event->window.event;
505 if (window_event == SDL_WINDOWEVENT_ENTER)
506 {
507 bd->MouseWindowID = event->window.windowID;
508 bd->MouseLastLeaveFrame = 0;
509 }
510 if (window_event == SDL_WINDOWEVENT_LEAVE)
511 bd->MouseLastLeaveFrame = ImGui::GetFrameCount() + 1;
512 //if (window_event == SDL_WINDOWEVENT_FOCUS_GAINED) { IMGUI_DEBUG_LOG("SDL_WINDOWEVENT_FOCUS_GAINED: windowId %d, viewport: %08X\n", event->window.windowID, viewport ? viewport->ID : 0); }
513 //if (window_event == SDL_WINDOWEVENT_FOCUS_LOST) { IMGUI_DEBUG_LOG("SDL_WINDOWEVENT_FOCUS_LOST: windowId %d, viewport: %08X\n", event->window.windowID, viewport ? viewport->ID : 0); }
514 if (window_event == SDL_WINDOWEVENT_FOCUS_GAINED)
515 io.AddFocusEvent(true);
516 if (window_event == SDL_WINDOWEVENT_FOCUS_LOST)
517 io.AddFocusEvent(false);
518 if (window_event == SDL_WINDOWEVENT_CLOSE)
519 viewport->PlatformRequestClose = true;
520 if (window_event == SDL_WINDOWEVENT_MOVED)
521 viewport->PlatformRequestMove = true;
522 if (window_event == SDL_WINDOWEVENT_RESIZED)
523 viewport->PlatformRequestResize = true;
524 return true;
525 }
526 case SDL_CONTROLLERDEVICEADDED:
527 case SDL_CONTROLLERDEVICEREMOVED:
528 {
529 bd->WantUpdateGamepadsList = true;
530 return true;
531 }
532 default:
533 break;
534 }
535 return false;
536}
537
538#ifdef __EMSCRIPTEN__
539EM_JS(void, ImGui_ImplSDL2_EmscriptenOpenURL, (char const* url), { url = url ? UTF8ToString(url) : null; if (url) window.open(url, '_blank'); });
540#endif
541
542static bool ImGui_ImplSDL2_Init(SDL_Window* window, SDL_Renderer* renderer, void* sdl_gl_context)
543{
544 ImGuiIO& io = ImGui::GetIO();
545 IMGUI_CHECKVERSION();
546 IM_ASSERT(io.BackendPlatformUserData == nullptr && "Already initialized a platform backend!");
547 //SDL_SetHint(SDL_HINT_EVENT_LOGGING, "2");
548
549 // Obtain compiled and runtime versions
550 SDL_version ver_compiled;
551 SDL_version ver_runtime;
552 SDL_VERSION(&ver_compiled);
553 SDL_GetVersion(&ver_runtime);
554
555 // Setup backend capabilities flags
556 ImGui_ImplSDL2_Data* bd = IM_NEW(ImGui_ImplSDL2_Data)();
557 snprintf(bd->BackendPlatformName, sizeof(bd->BackendPlatformName), "imgui_impl_sdl2 (%u.%u.%u, %u.%u.%u)",
558 ver_compiled.major, ver_compiled.minor, ver_compiled.patch, ver_runtime.major, ver_runtime.minor, ver_runtime.patch);
559 io.BackendPlatformUserData = (void*)bd;
560 io.BackendPlatformName = bd->BackendPlatformName;
561 io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional)
562 io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used)
563 // (ImGuiBackendFlags_PlatformHasViewports may be set just below)
564
565 bd->Window = window;
566 bd->WindowID = SDL_GetWindowID(window);
567 bd->Renderer = renderer;
568
569 // Check and store if we are on a SDL backend that supports SDL_GetGlobalMouseState() and SDL_CaptureMouse()
570 // ("wayland" and "rpi" don't support it, but we chose to use a white-list instead of a black-list)
571 bd->MouseCanUseGlobalState = false;
572 bd->MouseCaptureMode = ImGui_ImplSDL2_MouseCaptureMode_Disabled;
573#if SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE
574 const char* sdl_backend = SDL_GetCurrentVideoDriver();
575 const char* capture_and_global_state_whitelist[] = { "windows", "cocoa", "x11", "DIVE", "VMAN" };
576 for (const char* item : capture_and_global_state_whitelist)
577 if (strncmp(sdl_backend, item, strlen(item)) == 0)
578 {
579 bd->MouseCanUseGlobalState = true;
580 bd->MouseCaptureMode = (strcmp(item, "x11") == 0) ? ImGui_ImplSDL2_MouseCaptureMode_EnabledAfterDrag : ImGui_ImplSDL2_MouseCaptureMode_Enabled;
581 }
582#endif
583 if (bd->MouseCanUseGlobalState)
584 io.BackendFlags |= ImGuiBackendFlags_PlatformHasViewports; // We can create multi-viewports on the Platform side (optional)
585
586 // SDL on Linux/OSX doesn't report events for unfocused windows (see https://github.com/ocornut/imgui/issues/4960)
587 // We will use 'MouseCanReportHoveredViewport' to set 'ImGuiBackendFlags_HasMouseHoveredViewport' dynamically each frame.
588#ifndef __APPLE__
589 bd->MouseCanReportHoveredViewport = bd->MouseCanUseGlobalState;
590#else
591 bd->MouseCanReportHoveredViewport = false;
592#endif
593
594 ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
595 platform_io.Platform_SetClipboardTextFn = ImGui_ImplSDL2_SetClipboardText;
596 platform_io.Platform_GetClipboardTextFn = ImGui_ImplSDL2_GetClipboardText;
597 platform_io.Platform_ClipboardUserData = nullptr;
598 platform_io.Platform_SetImeDataFn = ImGui_ImplSDL2_PlatformSetImeData;
599#ifdef __EMSCRIPTEN__
600 platform_io.Platform_OpenInShellFn = [](ImGuiContext*, const char* url) { ImGui_ImplSDL2_EmscriptenOpenURL(url); return true; };
601#elif SDL_HAS_OPEN_URL
602 platform_io.Platform_OpenInShellFn = [](ImGuiContext*, const char* url) { return SDL_OpenURL(url) == 0; };
603#endif
604
605 // Update monitor a first time during init
606 ImGui_ImplSDL2_UpdateMonitors();
607
608 // Gamepad handling
609 bd->GamepadMode = ImGui_ImplSDL2_GamepadMode_AutoFirst;
610 bd->WantUpdateGamepadsList = true;
611
612 // Load mouse cursors
613 bd->MouseCursors[ImGuiMouseCursor_Arrow] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_ARROW);
614 bd->MouseCursors[ImGuiMouseCursor_TextInput] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_IBEAM);
615 bd->MouseCursors[ImGuiMouseCursor_ResizeAll] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEALL);
616 bd->MouseCursors[ImGuiMouseCursor_ResizeNS] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENS);
617 bd->MouseCursors[ImGuiMouseCursor_ResizeEW] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEWE);
618 bd->MouseCursors[ImGuiMouseCursor_ResizeNESW] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENESW);
619 bd->MouseCursors[ImGuiMouseCursor_ResizeNWSE] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENWSE);
620 bd->MouseCursors[ImGuiMouseCursor_Hand] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_HAND);
621 bd->MouseCursors[ImGuiMouseCursor_Wait] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_WAIT);
622 bd->MouseCursors[ImGuiMouseCursor_Progress] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_WAITARROW);
623 bd->MouseCursors[ImGuiMouseCursor_NotAllowed] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NO);
624
625 // Set platform dependent data in viewport
626 // Our mouse update function expect PlatformHandle to be filled for the main viewport
627 ImGuiViewport* main_viewport = ImGui::GetMainViewport();
628 main_viewport->PlatformHandle = (void*)(intptr_t)bd->WindowID;
629 main_viewport->PlatformHandleRaw = nullptr;
630 SDL_SysWMinfo info;
631 SDL_VERSION(&info.version);
632 if (SDL_GetWindowWMInfo(window, &info))
633 {
634#if defined(SDL_VIDEO_DRIVER_WINDOWS)
635 main_viewport->PlatformHandleRaw = (void*)info.info.win.window;
636#elif defined(__APPLE__) && defined(SDL_VIDEO_DRIVER_COCOA)
637 main_viewport->PlatformHandleRaw = (void*)info.info.cocoa.window;
638#endif
639 }
640
641 // From 2.0.5: Set SDL hint to receive mouse click events on window focus, otherwise SDL doesn't emit the event.
642 // Without this, when clicking to gain focus, our widgets wouldn't activate even though they showed as hovered.
643 // (This is unfortunately a global SDL setting, so enabling it might have a side-effect on your application.
644 // It is unlikely to make a difference, but if your app absolutely needs to ignore the initial on-focus click:
645 // you can ignore SDL_MOUSEBUTTONDOWN events coming right after a SDL_WINDOWEVENT_FOCUS_GAINED)
646#ifdef SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH
647 SDL_SetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, "1");
648#endif
649
650 // From 2.0.18: Enable native IME.
651 // IMPORTANT: This is used at the time of SDL_CreateWindow() so this will only affects secondary windows, if any.
652 // For the main window to be affected, your application needs to call this manually before calling SDL_CreateWindow().
653#ifdef SDL_HINT_IME_SHOW_UI
654 SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1");
655#endif
656
657 // From 2.0.22: Disable auto-capture, this is preventing drag and drop across multiple windows (see #5710)
658#ifdef SDL_HINT_MOUSE_AUTO_CAPTURE
659 SDL_SetHint(SDL_HINT_MOUSE_AUTO_CAPTURE, "0");
660#endif
661
662 // We need SDL_CaptureMouse(), SDL_GetGlobalMouseState() from SDL 2.0.4+ to support multiple viewports.
663 // We left the call to ImGui_ImplSDL2_InitMultiViewportSupport() outside of #ifdef to avoid unused-function warnings.
664 if (io.BackendFlags & ImGuiBackendFlags_PlatformHasViewports)
665 ImGui_ImplSDL2_InitMultiViewportSupport(window, sdl_gl_context);
666
667 return true;
668}
669
670bool ImGui_ImplSDL2_InitForOpenGL(SDL_Window* window, void* sdl_gl_context)
671{
672 return ImGui_ImplSDL2_Init(window, nullptr, sdl_gl_context);
673}
674
675bool ImGui_ImplSDL2_InitForVulkan(SDL_Window* window)
676{
677#if !SDL_HAS_VULKAN
678 IM_ASSERT(0 && "Unsupported");
679#endif
680 if (!ImGui_ImplSDL2_Init(window, nullptr, nullptr))
681 return false;
682 ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData();
683 bd->UseVulkan = true;
684 return true;
685}
686
687bool ImGui_ImplSDL2_InitForD3D(SDL_Window* window)
688{
689#if !defined(_WIN32)
690 IM_ASSERT(0 && "Unsupported");
691#endif
692 return ImGui_ImplSDL2_Init(window, nullptr, nullptr);
693}
694
695bool ImGui_ImplSDL2_InitForMetal(SDL_Window* window)
696{
697 return ImGui_ImplSDL2_Init(window, nullptr, nullptr);
698}
699
700bool ImGui_ImplSDL2_InitForSDLRenderer(SDL_Window* window, SDL_Renderer* renderer)
701{
702 return ImGui_ImplSDL2_Init(window, renderer, nullptr);
703}
704
705bool ImGui_ImplSDL2_InitForOther(SDL_Window* window)
706{
707 return ImGui_ImplSDL2_Init(window, nullptr, nullptr);
708}
709
710static void ImGui_ImplSDL2_CloseGamepads();
711
712void ImGui_ImplSDL2_Shutdown()
713{
714 ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData();
715 IM_ASSERT(bd != nullptr && "No platform backend to shutdown, or already shutdown?");
716 ImGuiIO& io = ImGui::GetIO();
717 ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
718
719 ImGui_ImplSDL2_ShutdownMultiViewportSupport();
720 if (bd->ClipboardTextData)
721 SDL_free(bd->ClipboardTextData);
722 for (ImGuiMouseCursor cursor_n = 0; cursor_n < ImGuiMouseCursor_COUNT; cursor_n++)
723 SDL_FreeCursor(bd->MouseCursors[cursor_n]);
724 ImGui_ImplSDL2_CloseGamepads();
725
726 io.BackendPlatformName = nullptr;
727 io.BackendPlatformUserData = nullptr;
728 io.BackendFlags &= ~(ImGuiBackendFlags_HasMouseCursors | ImGuiBackendFlags_HasSetMousePos | ImGuiBackendFlags_HasGamepad | ImGuiBackendFlags_PlatformHasViewports | ImGuiBackendFlags_HasMouseHoveredViewport);
729 platform_io.ClearPlatformHandlers();
730 IM_DELETE(bd);
731}
732
733void ImGui_ImplSDL2_SetMouseCaptureMode(ImGui_ImplSDL2_MouseCaptureMode mode)
734{
735 ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData();
736 if (mode == ImGui_ImplSDL2_MouseCaptureMode_Disabled && bd->MouseCaptureMode != ImGui_ImplSDL2_MouseCaptureMode_Disabled)
737 SDL_CaptureMouse(SDL_FALSE);
738 bd->MouseCaptureMode = mode;
739}
740
741// This code is rather messy because some of the functions we need for full viewport support are not available in SDL < 2.0.4.
742static void ImGui_ImplSDL2_UpdateMouseData()
743{
744 ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData();
745 ImGuiIO& io = ImGui::GetIO();
746
747 // We forward mouse input when hovered or captured (via SDL_MOUSEMOTION) or when focused (below)
748#if SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE
749 // - SDL_CaptureMouse() let the OS know e.g. that our drags can extend outside of parent boundaries (we want updated position) and shouldn't trigger other operations outside.
750 // - Debuggers under Linux tends to leave captured mouse on break, which may be very inconvenient, so to mitigate the issue on X11 we we wait until mouse has moved to begin capture.
751 if (bd->MouseCaptureMode == ImGui_ImplSDL2_MouseCaptureMode_Enabled)
752 {
753 SDL_CaptureMouse((bd->MouseButtonsDown != 0) ? SDL_TRUE : SDL_FALSE);
754 }
755 else if (bd->MouseCaptureMode == ImGui_ImplSDL2_MouseCaptureMode_EnabledAfterDrag)
756 {
757 bool want_capture = false;
758 for (int button_n = 0; button_n < ImGuiMouseButton_COUNT && !want_capture; button_n++)
759 if (ImGui::IsMouseDragging(button_n, 1.0f))
760 want_capture = true;
761 SDL_CaptureMouse(want_capture ? SDL_TRUE : SDL_FALSE);
762 }
763
764 SDL_Window* focused_window = SDL_GetKeyboardFocus();
765 const bool is_app_focused = (focused_window && (bd->Window == focused_window || ImGui_ImplSDL2_GetViewportForWindowID(SDL_GetWindowID(focused_window)) != NULL));
766#else
767 SDL_Window* focused_window = bd->Window;
768 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
769#endif
770
771 if (is_app_focused)
772 {
773 // (Optional) Set OS mouse position from Dear ImGui if requested (rarely used, only when io.ConfigNavMoveSetMousePos is enabled by user)
774 if (io.WantSetMousePos)
775 {
776#if SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE
777 if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
778 SDL_WarpMouseGlobal((int)io.MousePos.x, (int)io.MousePos.y);
779 else
780#endif
781 SDL_WarpMouseInWindow(bd->Window, (int)io.MousePos.x, (int)io.MousePos.y);
782 }
783
784 // (Optional) Fallback to provide unclamped mouse position when focused but not hovered (SDL_MOUSEMOTION already provides this when hovered or captured)
785 // Note that SDL_GetGlobalMouseState() is in theory slow on X11, but this only runs on rather specific cases. If a problem we may provide a way to opt-out this feature.
786 SDL_Window* hovered_window = SDL_GetMouseFocus();
787 const bool is_relative_mouse_mode = SDL_GetRelativeMouseMode() != 0;
788 if (hovered_window == nullptr && bd->MouseCanUseGlobalState && bd->MouseButtonsDown == 0 && !is_relative_mouse_mode)
789 {
790 // 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)
791 // 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)
792 int mouse_x, mouse_y;
793 int window_x, window_y;
794 SDL_GetGlobalMouseState(&mouse_x, &mouse_y);
795 if (!(io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable))
796 {
797 SDL_GetWindowPosition(focused_window, &window_x, &window_y);
798 mouse_x -= window_x;
799 mouse_y -= window_y;
800 }
801 io.AddMousePosEvent((float)mouse_x, (float)mouse_y);
802 }
803 }
804
805 // (Optional) When using multiple viewports: call io.AddMouseViewportEvent() with the viewport the OS mouse cursor is hovering.
806 // If ImGuiBackendFlags_HasMouseHoveredViewport is not set by the backend, Dear imGui will ignore this field and infer the information using its flawed heuristic.
807 // - [!] SDL backend does NOT correctly ignore viewports with the _NoInputs flag.
808 // 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
809 // 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
810 // by the backend, and use its flawed heuristic to guess the viewport behind.
811 // - [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).
812 if (io.BackendFlags & ImGuiBackendFlags_HasMouseHoveredViewport)
813 {
814 ImGuiID mouse_viewport_id = 0;
815 if (ImGuiViewport* mouse_viewport = ImGui_ImplSDL2_GetViewportForWindowID(bd->MouseWindowID))
816 mouse_viewport_id = mouse_viewport->ID;
817 io.AddMouseViewportEvent(mouse_viewport_id);
818 }
819}
820
821static void ImGui_ImplSDL2_UpdateMouseCursor()
822{
823 ImGuiIO& io = ImGui::GetIO();
824 if (io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange)
825 return;
826 ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData();
827
828 ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor();
829 if (io.MouseDrawCursor || imgui_cursor == ImGuiMouseCursor_None)
830 {
831 // Hide OS mouse cursor if imgui is drawing it or if it wants no cursor
832 SDL_ShowCursor(SDL_FALSE);
833 }
834 else
835 {
836 // Show OS mouse cursor
837 SDL_Cursor* expected_cursor = bd->MouseCursors[imgui_cursor] ? bd->MouseCursors[imgui_cursor] : bd->MouseCursors[ImGuiMouseCursor_Arrow];
838 if (bd->MouseLastCursor != expected_cursor)
839 {
840 SDL_SetCursor(expected_cursor); // SDL function doesn't have an early out (see #6113)
841 bd->MouseLastCursor = expected_cursor;
842 }
843 SDL_ShowCursor(SDL_TRUE);
844 }
845}
846
847// - On Windows the process needs to be marked DPI-aware!! SDL2 doesn't do it by default. You can call ::SetProcessDPIAware() or call ImGui_ImplWin32_EnableDpiAwareness() from Win32 backend.
848// - Apple platforms use FramebufferScale so we always return 1.0f.
849// - Some accessibility applications are declaring virtual monitors with a DPI of 0.0f, see #7902. We preserve this value for caller to handle.
850float ImGui_ImplSDL2_GetContentScaleForWindow(SDL_Window* window)
851{
852 return ImGui_ImplSDL2_GetContentScaleForDisplay(SDL_GetWindowDisplayIndex(window));
853}
854
855// SDL_GetDisplayDPI() seems rather unreliable on Linux.
856float ImGui_ImplSDL2_GetContentScaleForDisplay(int display_index)
857{
858 const char* sdl_driver = SDL_GetCurrentVideoDriver();
859 if (sdl_driver && strcmp(sdl_driver, "wayland") == 0)
860 return 1.0f;
861#if SDL_HAS_PER_MONITOR_DPI
862#if !defined(__APPLE__) && !defined(__EMSCRIPTEN__) && !defined(__ANDROID__)
863 float dpi = 0.0f;
864 if (SDL_GetDisplayDPI(display_index, &dpi, nullptr, nullptr) == 0)
865 {
866 if (dpi < 96.0f)
867 dpi = 96.0f;
868 return dpi / 96.0f;
869 }
870#endif
871#endif
872 IM_UNUSED(display_index);
873 return 1.0f;
874}
875
876static void ImGui_ImplSDL2_CloseGamepads()
877{
878 ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData();
879 if (bd->GamepadMode != ImGui_ImplSDL2_GamepadMode_Manual)
880 for (SDL_GameController* gamepad : bd->Gamepads)
881 SDL_GameControllerClose(gamepad);
882 bd->Gamepads.resize(0);
883}
884
885void ImGui_ImplSDL2_SetGamepadMode(ImGui_ImplSDL2_GamepadMode mode, struct _SDL_GameController** manual_gamepads_array, int manual_gamepads_count)
886{
887 ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData();
888 ImGui_ImplSDL2_CloseGamepads();
889 if (mode == ImGui_ImplSDL2_GamepadMode_Manual)
890 {
891 IM_ASSERT(manual_gamepads_array != nullptr || manual_gamepads_count <= 0);
892 for (int n = 0; n < manual_gamepads_count; n++)
893 bd->Gamepads.push_back(manual_gamepads_array[n]);
894 }
895 else
896 {
897 IM_ASSERT(manual_gamepads_array == nullptr && manual_gamepads_count <= 0);
898 bd->WantUpdateGamepadsList = true;
899 }
900 bd->GamepadMode = mode;
901}
902
903static void ImGui_ImplSDL2_UpdateGamepadButton(ImGui_ImplSDL2_Data* bd, ImGuiIO& io, ImGuiKey key, SDL_GameControllerButton button_no)
904{
905 bool merged_value = false;
906 for (SDL_GameController* gamepad : bd->Gamepads)
907 merged_value |= SDL_GameControllerGetButton(gamepad, button_no) != 0;
908 io.AddKeyEvent(key, merged_value);
909}
910
911static inline float Saturate(float v) { return v < 0.0f ? 0.0f : v > 1.0f ? 1.0f : v; }
912static void ImGui_ImplSDL2_UpdateGamepadAnalog(ImGui_ImplSDL2_Data* bd, ImGuiIO& io, ImGuiKey key, SDL_GameControllerAxis axis_no, float v0, float v1)
913{
914 float merged_value = 0.0f;
915 for (SDL_GameController* gamepad : bd->Gamepads)
916 {
917 float vn = Saturate((float)(SDL_GameControllerGetAxis(gamepad, axis_no) - v0) / (float)(v1 - v0));
918 if (merged_value < vn)
919 merged_value = vn;
920 }
921 io.AddKeyAnalogEvent(key, merged_value > 0.1f, merged_value);
922}
923
924static void ImGui_ImplSDL2_UpdateGamepads()
925{
926 ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData();
927 ImGuiIO& io = ImGui::GetIO();
928
929 // Update list of controller(s) to use
930 if (bd->WantUpdateGamepadsList && bd->GamepadMode != ImGui_ImplSDL2_GamepadMode_Manual)
931 {
932 ImGui_ImplSDL2_CloseGamepads();
933 int joystick_count = SDL_NumJoysticks();
934 for (int n = 0; n < joystick_count; n++)
935 if (SDL_IsGameController(n))
936 if (SDL_GameController* gamepad = SDL_GameControllerOpen(n))
937 {
938 bd->Gamepads.push_back(gamepad);
939 if (bd->GamepadMode == ImGui_ImplSDL2_GamepadMode_AutoFirst)
940 break;
941 }
942 bd->WantUpdateGamepadsList = false;
943 }
944
945 io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad;
946 if (bd->Gamepads.Size == 0)
947 return;
948 io.BackendFlags |= ImGuiBackendFlags_HasGamepad;
949
950 // Update gamepad inputs
951 const int thumb_dead_zone = 8000; // SDL_gamecontroller.h suggests using this value.
952 ImGui_ImplSDL2_UpdateGamepadButton(bd, io, ImGuiKey_GamepadStart, SDL_CONTROLLER_BUTTON_START);
953 ImGui_ImplSDL2_UpdateGamepadButton(bd, io, ImGuiKey_GamepadBack, SDL_CONTROLLER_BUTTON_BACK);
954 ImGui_ImplSDL2_UpdateGamepadButton(bd, io, ImGuiKey_GamepadFaceLeft, SDL_CONTROLLER_BUTTON_X); // Xbox X, PS Square
955 ImGui_ImplSDL2_UpdateGamepadButton(bd, io, ImGuiKey_GamepadFaceRight, SDL_CONTROLLER_BUTTON_B); // Xbox B, PS Circle
956 ImGui_ImplSDL2_UpdateGamepadButton(bd, io, ImGuiKey_GamepadFaceUp, SDL_CONTROLLER_BUTTON_Y); // Xbox Y, PS Triangle
957 ImGui_ImplSDL2_UpdateGamepadButton(bd, io, ImGuiKey_GamepadFaceDown, SDL_CONTROLLER_BUTTON_A); // Xbox A, PS Cross
958 ImGui_ImplSDL2_UpdateGamepadButton(bd, io, ImGuiKey_GamepadDpadLeft, SDL_CONTROLLER_BUTTON_DPAD_LEFT);
959 ImGui_ImplSDL2_UpdateGamepadButton(bd, io, ImGuiKey_GamepadDpadRight, SDL_CONTROLLER_BUTTON_DPAD_RIGHT);
960 ImGui_ImplSDL2_UpdateGamepadButton(bd, io, ImGuiKey_GamepadDpadUp, SDL_CONTROLLER_BUTTON_DPAD_UP);
961 ImGui_ImplSDL2_UpdateGamepadButton(bd, io, ImGuiKey_GamepadDpadDown, SDL_CONTROLLER_BUTTON_DPAD_DOWN);
962 ImGui_ImplSDL2_UpdateGamepadButton(bd, io, ImGuiKey_GamepadL1, SDL_CONTROLLER_BUTTON_LEFTSHOULDER);
963 ImGui_ImplSDL2_UpdateGamepadButton(bd, io, ImGuiKey_GamepadR1, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER);
964 ImGui_ImplSDL2_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadL2, SDL_CONTROLLER_AXIS_TRIGGERLEFT, 0.0f, 32767);
965 ImGui_ImplSDL2_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadR2, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, 0.0f, 32767);
966 ImGui_ImplSDL2_UpdateGamepadButton(bd, io, ImGuiKey_GamepadL3, SDL_CONTROLLER_BUTTON_LEFTSTICK);
967 ImGui_ImplSDL2_UpdateGamepadButton(bd, io, ImGuiKey_GamepadR3, SDL_CONTROLLER_BUTTON_RIGHTSTICK);
968 ImGui_ImplSDL2_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadLStickLeft, SDL_CONTROLLER_AXIS_LEFTX, -thumb_dead_zone, -32768);
969 ImGui_ImplSDL2_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadLStickRight, SDL_CONTROLLER_AXIS_LEFTX, +thumb_dead_zone, +32767);
970 ImGui_ImplSDL2_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadLStickUp, SDL_CONTROLLER_AXIS_LEFTY, -thumb_dead_zone, -32768);
971 ImGui_ImplSDL2_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadLStickDown, SDL_CONTROLLER_AXIS_LEFTY, +thumb_dead_zone, +32767);
972 ImGui_ImplSDL2_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadRStickLeft, SDL_CONTROLLER_AXIS_RIGHTX, -thumb_dead_zone, -32768);
973 ImGui_ImplSDL2_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadRStickRight, SDL_CONTROLLER_AXIS_RIGHTX, +thumb_dead_zone, +32767);
974 ImGui_ImplSDL2_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadRStickUp, SDL_CONTROLLER_AXIS_RIGHTY, -thumb_dead_zone, -32768);
975 ImGui_ImplSDL2_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadRStickDown, SDL_CONTROLLER_AXIS_RIGHTY, +thumb_dead_zone, +32767);
976}
977
978// FIXME: Note that doesn't update with DPI/Scaling change only as SDL2 doesn't have an event for it (SDL3 has).
979static void ImGui_ImplSDL2_UpdateMonitors()
980{
981 ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData();
982 ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
983 platform_io.Monitors.resize(0);
984 bd->WantUpdateMonitors = false;
985 int display_count = SDL_GetNumVideoDisplays();
986 for (int n = 0; n < display_count; n++)
987 {
988 // 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.
989 ImGuiPlatformMonitor monitor;
990 SDL_Rect r;
991 SDL_GetDisplayBounds(n, &r);
992 monitor.MainPos = monitor.WorkPos = ImVec2((float)r.x, (float)r.y);
993 monitor.MainSize = monitor.WorkSize = ImVec2((float)r.w, (float)r.h);
994#if SDL_HAS_USABLE_DISPLAY_BOUNDS
995 if (SDL_GetDisplayUsableBounds(n, &r) == 0 && r.w > 0 && r.h > 0)
996 {
997 monitor.WorkPos = ImVec2((float)r.x, (float)r.y);
998 monitor.WorkSize = ImVec2((float)r.w, (float)r.h);
999 }
1000#endif
1001 float dpi_scale = ImGui_ImplSDL2_GetContentScaleForDisplay(n);
1002 if (dpi_scale <= 0.0f)
1003 continue; // Some accessibility applications are declaring virtual monitors with a DPI of 0, see #7902.
1004 monitor.DpiScale = dpi_scale;
1005 monitor.PlatformHandle = (void*)(intptr_t)n;
1006 platform_io.Monitors.push_back(monitor);
1007 }
1008}
1009
1010static void ImGui_ImplSDL2_GetWindowSizeAndFramebufferScale(SDL_Window* window, SDL_Renderer* renderer, ImVec2* out_size, ImVec2* out_framebuffer_scale)
1011{
1012 int w, h;
1013 int display_w, display_h;
1014 SDL_GetWindowSize(window, &w, &h);
1015 if (SDL_GetWindowFlags(window) & SDL_WINDOW_MINIMIZED)
1016 w = h = 0;
1017 if (renderer != nullptr)
1018 SDL_GetRendererOutputSize(renderer, &display_w, &display_h);
1019#if SDL_HAS_VULKAN
1020 else if (SDL_GetWindowFlags(window) & SDL_WINDOW_VULKAN)
1021 SDL_Vulkan_GetDrawableSize(window, &display_w, &display_h);
1022#endif
1023 else
1024 SDL_GL_GetDrawableSize(window, &display_w, &display_h);
1025 if (out_size != nullptr)
1026 *out_size = ImVec2((float)w, (float)h);
1027 if (out_framebuffer_scale != nullptr)
1028 *out_framebuffer_scale = (w > 0 && h > 0) ? ImVec2((float)display_w / (float)w, (float)display_h / (float)h) : ImVec2(1.0f, 1.0f);
1029}
1030
1031void ImGui_ImplSDL2_NewFrame()
1032{
1033 ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData();
1034 IM_ASSERT(bd != nullptr && "Context or backend not initialized! Did you call ImGui_ImplSDL2_Init()?");
1035 ImGuiIO& io = ImGui::GetIO();
1036
1037 // Setup main viewport size (every frame to accommodate for window resizing)
1038 ImGui_ImplSDL2_GetWindowSizeAndFramebufferScale(bd->Window, bd->Renderer, &io.DisplaySize, &io.DisplayFramebufferScale);
1039
1040 // Update monitors
1041#ifdef WIN32
1042 bd->WantUpdateMonitors = true; // Keep polling under Windows to handle changes of work area when resizing task-bar (#8415)
1043#endif
1044 if (bd->WantUpdateMonitors)
1045 ImGui_ImplSDL2_UpdateMonitors();
1046
1047 // Setup time step (we don't use SDL_GetTicks() because it is using millisecond resolution)
1048 // (Accept SDL_GetPerformanceCounter() not returning a monotonically increasing value. Happens in VMs and Emscripten, see #6189, #6114, #3644)
1049 static Uint64 frequency = SDL_GetPerformanceFrequency();
1050 Uint64 current_time = SDL_GetPerformanceCounter();
1051 if (current_time <= bd->Time)
1052 current_time = bd->Time + 1;
1053 io.DeltaTime = bd->Time > 0 ? (float)((double)(current_time - bd->Time) / (double)frequency) : (float)(1.0f / 60.0f);
1054 bd->Time = current_time;
1055
1056 if (bd->MouseLastLeaveFrame && bd->MouseLastLeaveFrame >= ImGui::GetFrameCount() && bd->MouseButtonsDown == 0)
1057 {
1058 bd->MouseWindowID = 0;
1059 bd->MouseLastLeaveFrame = 0;
1060 io.AddMousePosEvent(-FLT_MAX, -FLT_MAX);
1061 }
1062
1063 // Our io.AddMouseViewportEvent() calls will only be valid when not capturing.
1064 // Technically speaking testing for 'bd->MouseButtonsDown == 0' would be more rigorous, but testing for payload reduces noise and potential side-effects.
1065 if (bd->MouseCanReportHoveredViewport && ImGui::GetDragDropPayload() == nullptr)
1066 io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport;
1067 else
1068 io.BackendFlags &= ~ImGuiBackendFlags_HasMouseHoveredViewport;
1069
1070 ImGui_ImplSDL2_UpdateMouseData();
1071 ImGui_ImplSDL2_UpdateMouseCursor();
1072
1073 // Update game controllers (if enabled and available)
1074 ImGui_ImplSDL2_UpdateGamepads();
1075}
1076
1077//--------------------------------------------------------------------------------------------------------
1078// MULTI-VIEWPORT / PLATFORM INTERFACE SUPPORT
1079// This is an _advanced_ and _optional_ feature, allowing the backend to create and handle multiple viewports simultaneously.
1080// 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..
1081//--------------------------------------------------------------------------------------------------------
1082
1083// Helper structure we store in the void* PlatformUserData field of each ImGuiViewport to easily retrieve our backend data.
1084struct ImGui_ImplSDL2_ViewportData
1085{
1086 SDL_Window* Window;
1087 Uint32 WindowID; // Stored in ImGuiViewport::PlatformHandle. Use SDL_GetWindowFromID() to get SDL_Window* from Uint32 WindowID.
1088 bool WindowOwned;
1089 SDL_GLContext GLContext;
1090
1091 ImGui_ImplSDL2_ViewportData() { Window = nullptr; WindowID = 0; WindowOwned = false; GLContext = nullptr; }
1092 ~ImGui_ImplSDL2_ViewportData() { IM_ASSERT(Window == nullptr && GLContext == nullptr); }
1093};
1094
1095static void ImGui_ImplSDL2_CreateWindow(ImGuiViewport* viewport)
1096{
1097 ImGui_ImplSDL2_Data* bd = ImGui_ImplSDL2_GetBackendData();
1098 ImGui_ImplSDL2_ViewportData* vd = IM_NEW(ImGui_ImplSDL2_ViewportData)();
1099 viewport->PlatformUserData = vd;
1100
1101 ImGuiViewport* main_viewport = ImGui::GetMainViewport();
1102 ImGui_ImplSDL2_ViewportData* main_viewport_data = (ImGui_ImplSDL2_ViewportData*)main_viewport->PlatformUserData;
1103
1104 // Share GL resources with main context
1105 bool use_opengl = (main_viewport_data->GLContext != nullptr);
1106 SDL_GLContext backup_context = nullptr;
1107 if (use_opengl)
1108 {
1109 backup_context = SDL_GL_GetCurrentContext();
1110 SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1);
1111 SDL_GL_MakeCurrent(main_viewport_data->Window, main_viewport_data->GLContext);
1112 }
1113
1114 Uint32 sdl_flags = 0;
1115 sdl_flags |= use_opengl ? SDL_WINDOW_OPENGL : (bd->UseVulkan ? SDL_WINDOW_VULKAN : 0);
1116 sdl_flags |= SDL_GetWindowFlags(bd->Window) & SDL_WINDOW_ALLOW_HIGHDPI;
1117 sdl_flags |= SDL_WINDOW_HIDDEN;
1118 sdl_flags |= (viewport->Flags & ImGuiViewportFlags_NoDecoration) ? SDL_WINDOW_BORDERLESS : 0;
1119 sdl_flags |= (viewport->Flags & ImGuiViewportFlags_NoDecoration) ? 0 : SDL_WINDOW_RESIZABLE;
1120#if !defined(_WIN32)
1121 // See SDL hack in ImGui_ImplSDL2_ShowWindow().
1122 sdl_flags |= (viewport->Flags & ImGuiViewportFlags_NoTaskBarIcon) ? SDL_WINDOW_SKIP_TASKBAR : 0;
1123#endif
1124#if SDL_HAS_ALWAYS_ON_TOP
1125 sdl_flags |= (viewport->Flags & ImGuiViewportFlags_TopMost) ? SDL_WINDOW_ALWAYS_ON_TOP : 0;
1126#endif
1127 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);
1128 vd->WindowOwned = true;
1129 if (use_opengl)
1130 {
1131 vd->GLContext = SDL_GL_CreateContext(vd->Window);
1132 SDL_GL_SetSwapInterval(0);
1133 }
1134 if (use_opengl && backup_context)
1135 SDL_GL_MakeCurrent(vd->Window, backup_context);
1136
1137 viewport->PlatformHandle = (void*)(intptr_t)SDL_GetWindowID(vd->Window);
1138 viewport->PlatformHandleRaw = nullptr;
1139 SDL_SysWMinfo info;
1140 SDL_VERSION(&info.version);
1141 if (SDL_GetWindowWMInfo(vd->Window, &info))
1142 {
1143#if defined(SDL_VIDEO_DRIVER_WINDOWS)
1144 viewport->PlatformHandleRaw = info.info.win.window;
1145#elif defined(__APPLE__) && defined(SDL_VIDEO_DRIVER_COCOA)
1146 viewport->PlatformHandleRaw = (void*)info.info.cocoa.window;
1147#endif
1148 }
1149}
1150
1151static void ImGui_ImplSDL2_DestroyWindow(ImGuiViewport* viewport)
1152{
1153 if (ImGui_ImplSDL2_ViewportData* vd = (ImGui_ImplSDL2_ViewportData*)viewport->PlatformUserData)
1154 {
1155 if (vd->GLContext && vd->WindowOwned)
1156 SDL_GL_DeleteContext(vd->GLContext);
1157 if (vd->Window && vd->WindowOwned)
1158 SDL_DestroyWindow(vd->Window);
1159 vd->GLContext = nullptr;
1160 vd->Window = nullptr;
1161 IM_DELETE(vd);
1162 }
1163 viewport->PlatformUserData = viewport->PlatformHandle = nullptr;
1164}
1165
1166static void ImGui_ImplSDL2_ShowWindow(ImGuiViewport* viewport)
1167{
1168 ImGui_ImplSDL2_ViewportData* vd = (ImGui_ImplSDL2_ViewportData*)viewport->PlatformUserData;
1169#if defined(_WIN32) && !(defined(WINAPI_FAMILY) && ((defined(WINAPI_FAMILY_APP) && WINAPI_FAMILY == WINAPI_FAMILY_APP) || (defined(WINAPI_FAMILY_GAMES) && WINAPI_FAMILY == WINAPI_FAMILY_GAMES)))
1170 HWND hwnd = (HWND)viewport->PlatformHandleRaw;
1171
1172 // SDL hack: Hide icon from task bar
1173 // 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.
1174 if (viewport->Flags & ImGuiViewportFlags_NoTaskBarIcon)
1175 {
1176 LONG ex_style = ::GetWindowLong(hwnd, GWL_EXSTYLE);
1177 ex_style &= ~WS_EX_APPWINDOW;
1178 ex_style |= WS_EX_TOOLWINDOW;
1179 ::SetWindowLong(hwnd, GWL_EXSTYLE, ex_style);
1180 }
1181#endif
1182
1183#if SDL_HAS_SHOW_WINDOW_ACTIVATION_HINT
1184 SDL_SetHint(SDL_HINT_WINDOW_NO_ACTIVATION_WHEN_SHOWN, (viewport->Flags & ImGuiViewportFlags_NoFocusOnAppearing) ? "1" : "0");
1185#elif defined(_WIN32)
1186 // SDL hack: SDL always activate/focus windows :/
1187 if (viewport->Flags & ImGuiViewportFlags_NoFocusOnAppearing)
1188 {
1189 ::ShowWindow(hwnd, SW_SHOWNA);
1190 return;
1191 }
1192#endif
1193 SDL_ShowWindow(vd->Window);
1194}
1195
1196static ImVec2 ImGui_ImplSDL2_GetWindowPos(ImGuiViewport* viewport)
1197{
1198 ImGui_ImplSDL2_ViewportData* vd = (ImGui_ImplSDL2_ViewportData*)viewport->PlatformUserData;
1199 int x = 0, y = 0;
1200 SDL_GetWindowPosition(vd->Window, &x, &y);
1201 return ImVec2((float)x, (float)y);
1202}
1203
1204static void ImGui_ImplSDL2_SetWindowPos(ImGuiViewport* viewport, ImVec2 pos)
1205{
1206 ImGui_ImplSDL2_ViewportData* vd = (ImGui_ImplSDL2_ViewportData*)viewport->PlatformUserData;
1207 SDL_SetWindowPosition(vd->Window, (int)pos.x, (int)pos.y);
1208}
1209
1210static ImVec2 ImGui_ImplSDL2_GetWindowSize(ImGuiViewport* viewport)
1211{
1212 ImGui_ImplSDL2_ViewportData* vd = (ImGui_ImplSDL2_ViewportData*)viewport->PlatformUserData;
1213 int w = 0, h = 0;
1214 SDL_GetWindowSize(vd->Window, &w, &h);
1215 return ImVec2((float)w, (float)h);
1216}
1217
1218static void ImGui_ImplSDL2_SetWindowSize(ImGuiViewport* viewport, ImVec2 size)
1219{
1220 ImGui_ImplSDL2_ViewportData* vd = (ImGui_ImplSDL2_ViewportData*)viewport->PlatformUserData;
1221 SDL_SetWindowSize(vd->Window, (int)size.x, (int)size.y);
1222}
1223
1224static ImVec2 ImGui_ImplSDL2_GetWindowFramebufferScale(ImGuiViewport* viewport)
1225{
1226 // FIXME: SDL_Renderer does not support multi-viewport.
1227 ImGui_ImplSDL2_ViewportData* vd = (ImGui_ImplSDL2_ViewportData*)viewport->PlatformUserData;
1228 ImVec2 framebuffer_scale;
1229 ImGui_ImplSDL2_GetWindowSizeAndFramebufferScale(vd->Window, nullptr, nullptr, &framebuffer_scale);
1230 return framebuffer_scale;
1231}
1232
1233static void ImGui_ImplSDL2_SetWindowTitle(ImGuiViewport* viewport, const char* title)
1234{
1235 ImGui_ImplSDL2_ViewportData* vd = (ImGui_ImplSDL2_ViewportData*)viewport->PlatformUserData;
1236 SDL_SetWindowTitle(vd->Window, title);
1237}
1238
1239#if SDL_HAS_WINDOW_ALPHA
1240static void ImGui_ImplSDL2_SetWindowAlpha(ImGuiViewport* viewport, float alpha)
1241{
1242 ImGui_ImplSDL2_ViewportData* vd = (ImGui_ImplSDL2_ViewportData*)viewport->PlatformUserData;
1243 SDL_SetWindowOpacity(vd->Window, alpha);
1244}
1245#endif
1246
1247static void ImGui_ImplSDL2_SetWindowFocus(ImGuiViewport* viewport)
1248{
1249 ImGui_ImplSDL2_ViewportData* vd = (ImGui_ImplSDL2_ViewportData*)viewport->PlatformUserData;
1250 SDL_RaiseWindow(vd->Window);
1251}
1252
1253static bool ImGui_ImplSDL2_GetWindowFocus(ImGuiViewport* viewport)
1254{
1255 ImGui_ImplSDL2_ViewportData* vd = (ImGui_ImplSDL2_ViewportData*)viewport->PlatformUserData;
1256 return (SDL_GetWindowFlags(vd->Window) & SDL_WINDOW_INPUT_FOCUS) != 0;
1257}
1258
1259static bool ImGui_ImplSDL2_GetWindowMinimized(ImGuiViewport* viewport)
1260{
1261 ImGui_ImplSDL2_ViewportData* vd = (ImGui_ImplSDL2_ViewportData*)viewport->PlatformUserData;
1262 return (SDL_GetWindowFlags(vd->Window) & SDL_WINDOW_MINIMIZED) != 0;
1263}
1264
1265static void ImGui_ImplSDL2_RenderWindow(ImGuiViewport* viewport, void*)
1266{
1267 ImGui_ImplSDL2_ViewportData* vd = (ImGui_ImplSDL2_ViewportData*)viewport->PlatformUserData;
1268 if (vd->GLContext)
1269 SDL_GL_MakeCurrent(vd->Window, vd->GLContext);
1270}
1271
1272static void ImGui_ImplSDL2_SwapBuffers(ImGuiViewport* viewport, void*)
1273{
1274 ImGui_ImplSDL2_ViewportData* vd = (ImGui_ImplSDL2_ViewportData*)viewport->PlatformUserData;
1275 if (vd->GLContext)
1276 {
1277 SDL_GL_MakeCurrent(vd->Window, vd->GLContext);
1278 SDL_GL_SwapWindow(vd->Window);
1279 }
1280}
1281
1282// Vulkan support (the Vulkan renderer needs to call a platform-side support function to create the surface)
1283// SDL is graceful enough to _not_ need <vulkan/vulkan.h> so we can safely include this.
1284#if SDL_HAS_VULKAN
1285#include <SDL_vulkan.h>
1286static int ImGui_ImplSDL2_CreateVkSurface(ImGuiViewport* viewport, ImU64 vk_instance, const void* vk_allocator, ImU64* out_vk_surface)
1287{
1288 ImGui_ImplSDL2_ViewportData* vd = (ImGui_ImplSDL2_ViewportData*)viewport->PlatformUserData;
1289 (void)vk_allocator;
1290 SDL_bool ret = SDL_Vulkan_CreateSurface(vd->Window, (VkInstance)vk_instance, (VkSurfaceKHR*)out_vk_surface);
1291 return ret ? 0 : 1; // ret ? VK_SUCCESS : VK_NOT_READY
1292}
1293#endif // SDL_HAS_VULKAN
1294
1295static void ImGui_ImplSDL2_InitMultiViewportSupport(SDL_Window* window, void* sdl_gl_context)
1296{
1297 // Register platform interface (will be coupled with a renderer interface)
1298 ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
1299 platform_io.Platform_CreateWindow = ImGui_ImplSDL2_CreateWindow;
1300 platform_io.Platform_DestroyWindow = ImGui_ImplSDL2_DestroyWindow;
1301 platform_io.Platform_ShowWindow = ImGui_ImplSDL2_ShowWindow;
1302 platform_io.Platform_SetWindowPos = ImGui_ImplSDL2_SetWindowPos;
1303 platform_io.Platform_GetWindowPos = ImGui_ImplSDL2_GetWindowPos;
1304 platform_io.Platform_SetWindowSize = ImGui_ImplSDL2_SetWindowSize;
1305 platform_io.Platform_GetWindowSize = ImGui_ImplSDL2_GetWindowSize;
1306 platform_io.Platform_GetWindowFramebufferScale = ImGui_ImplSDL2_GetWindowFramebufferScale;
1307 platform_io.Platform_SetWindowFocus = ImGui_ImplSDL2_SetWindowFocus;
1308 platform_io.Platform_GetWindowFocus = ImGui_ImplSDL2_GetWindowFocus;
1309 platform_io.Platform_GetWindowMinimized = ImGui_ImplSDL2_GetWindowMinimized;
1310 platform_io.Platform_SetWindowTitle = ImGui_ImplSDL2_SetWindowTitle;
1311 platform_io.Platform_RenderWindow = ImGui_ImplSDL2_RenderWindow;
1312 platform_io.Platform_SwapBuffers = ImGui_ImplSDL2_SwapBuffers;
1313#if SDL_HAS_WINDOW_ALPHA
1314 platform_io.Platform_SetWindowAlpha = ImGui_ImplSDL2_SetWindowAlpha;
1315#endif
1316#if SDL_HAS_VULKAN
1317 platform_io.Platform_CreateVkSurface = ImGui_ImplSDL2_CreateVkSurface;
1318#endif
1319
1320 // Register main window handle (which is owned by the main application, not by us)
1321 // 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.
1322 ImGuiViewport* main_viewport = ImGui::GetMainViewport();
1323 ImGui_ImplSDL2_ViewportData* vd = IM_NEW(ImGui_ImplSDL2_ViewportData)();
1324 vd->Window = window;
1325 vd->WindowID = SDL_GetWindowID(window);
1326 vd->WindowOwned = false;
1327 vd->GLContext = sdl_gl_context;
1328 main_viewport->PlatformUserData = vd;
1329 main_viewport->PlatformHandle = (void*)(intptr_t)vd->WindowID;
1330}
1331
1332static void ImGui_ImplSDL2_ShutdownMultiViewportSupport()
1333{
1334 ImGui::DestroyPlatformWindows();
1335}
1336
1337//-----------------------------------------------------------------------------
1338
1339#if defined(__clang__)
1340#pragma clang diagnostic pop
1341#endif
1342
1343#endif // #ifndef IMGUI_DISABLE