65.5 KB、1314行 生 Blame 履歴
1// dear imgui: Platform Backend for SDL3
2// This needs to be used along with a Renderer (e.g. SDL_GPU, DirectX11, OpenGL3, Vulkan..)
3// (Info: SDL3 is a cross-platform general purpose library for handling windows, inputs, graphics context creation, etc.)
4
5// Implemented features:
6// [X] Platform: Clipboard support.
7// [X] Platform: Mouse support. Can discriminate Mouse/TouchScreen.
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 are obsolete since 1.87 and not supported since 1.91.5]
9// [X] Platform: Gamepad support.
10// [X] Platform: Mouse cursor shape and visibility (ImGuiBackendFlags_HasMouseCursors). Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'.
11// [x] Platform: Multi-viewport support (multiple windows). Enable with 'io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable' -> the OS animation effect when window gets created/destroyed is problematic. SDL2 backend doesn't have issue.
12// Missing features or Issues:
13// [ ] Platform: Multi-viewport: Minimized windows seems to break mouse wheel events (at least under Windows).
14// [x] Platform: IME support. Position somehow broken in SDL3 + 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// Learn about Dear ImGui:
19// - FAQ https://dearimgui.com/faq
20// - Getting Started https://dearimgui.com/getting-started
21// - Documentation https://dearimgui.com/docs (same as your local docs/ folder).
22// - Introduction, links and more at the top of imgui.cpp
23
24// CHANGELOG
25// (minor and older changes stripped away, please see git history for details)
26// 2026-XX-XX: Platform: Added support for multiple windows via the ImGuiPlatformIO interface.
27// 2026-03-25: [Docking] Use SDL_HAS_EVENT_DISPLAY_USABLE_BOUNDS_CHANGED (SDL 3.4.0+) when available to avoid refreshing monitor work area every frame on Windows. (#8415)
28// 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)
29// 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_ImplSDL3_SetMouseCaptureMode() for X11 debugger users. (#3650, #6410, #9235)
30// 2026-01-15: Changed GetClipboardText() handler to return nullptr on error aka clipboard contents is not text. Consistent with other backends. (#9168)
31// 2025-11-05: Fixed an issue with missing characters events when an already active text field changes viewports. (#9054)
32// 2025-10-22: Fixed Platform_OpenInShellFn() return value (unused in core).
33// 2025-09-24: Skip using the SDL_GetGlobalMouseState() state when one of our window is hovered, as the SDL_EVENT_MOUSE_MOTION 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: Use SDL_GetWindowDisplayScale() on Mac to output DisplayFrameBufferScale. The function is more reliable during resolution changes e.g. going fullscreen. (#8703, #4414)
36// 2025-06-27: IME: avoid calling SDL_StartTextInput() again if already active. (#8727)
37// 2025-05-15: [Docking] Add Platform_GetWindowFramebufferScale() handler, to allow varying Retina display density on multiple monitors.
38// 2025-05-06: [Docking] macOS: fixed secondary viewports not appearing on other monitors before of parenting.
39// 2025-04-09: [Docking] Revert update monitors and work areas information every frame. Only do it on Windows. (#8415, #8558)
40// 2025-04-22: IME: honor ImGuiPlatformImeData->WantTextInput as an alternative way to call SDL_StartTextInput(), without IME being necessarily visible.
41// 2025-04-09: Don't attempt to call SDL_CaptureMouse() on drivers where we don't call SDL_GetGlobalMouseState(). (#8561)
42// 2025-03-30: Update for SDL3 api changes: Revert SDL_GetClipboardText() memory ownership change. (#8530, #7801)
43// 2025-03-21: Fill gamepad inputs and set ImGuiBackendFlags_HasGamepad regardless of ImGuiConfigFlags_NavEnableGamepad being set.
44// 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)
45// 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)
46// 2025-02-25: [Docking] Revert to use SDL_GetDisplayBounds() for WorkPos/WorkRect if SDL_GetDisplayUsableBounds() failed.
47// 2025-02-24: Avoid calling SDL_GetGlobalMouseState() when mouse is in relative mode.
48// 2025-02-21: [Docking] Update monitors and work areas information every frame, as the later may change regardless of monitor changes. (#8415)
49// 2025-02-18: Added ImGuiMouseCursor_Wait and ImGuiMouseCursor_Progress mouse cursor support.
50// 2025-02-10: Using SDL_OpenURL() in platform_io.Platform_OpenInShellFn handler.
51// 2025-01-20: Made ImGui_ImplSDL3_SetGamepadMode(ImGui_ImplSDL3_GamepadMode_Manual) accept an empty array.
52// 2024-10-24: Emscripten: SDL_EVENT_MOUSE_WHEEL event doesn't require dividing by 100.0f on Emscripten.
53// 2024-09-11: (Docking) Added support for viewport->ParentViewportId field to support parenting at OS level. (#7973)
54// 2024-09-03: Update for SDL3 api changes: SDL_GetGamepads() memory ownership revert. (#7918, #7898, #7807)
55// 2024-08-22: moved some OS/backend related function pointers from ImGuiIO to ImGuiPlatformIO:
56// - io.GetClipboardTextFn -> platform_io.Platform_GetClipboardTextFn
57// - io.SetClipboardTextFn -> platform_io.Platform_SetClipboardTextFn
58// - io.PlatformSetImeDataFn -> platform_io.Platform_SetImeDataFn
59// 2024-08-19: Storing SDL_WindowID inside ImGuiViewport::PlatformHandle instead of SDL_Window*.
60// 2024-08-19: ImGui_ImplSDL3_ProcessEvent() now ignores events intended for other SDL windows. (#7853)
61// 2024-07-22: Update for SDL3 api changes: SDL_GetGamepads() memory ownership change. (#7807)
62// 2024-07-18: Update for SDL3 api changes: SDL_GetClipboardText() memory ownership change. (#7801)
63// 2024-07-15: Update for SDL3 api changes: SDL_GetProperty() change to SDL_GetPointerProperty(). (#7794)
64// 2024-07-02: Update for SDL3 api changes: SDLK_x renames and SDLK_KP_x removals (#7761, #7762).
65// 2024-07-01: Update for SDL3 api changes: SDL_SetTextInputRect() changed to SDL_SetTextInputArea().
66// 2024-06-26: Update for SDL3 api changes: SDL_StartTextInput()/SDL_StopTextInput()/SDL_SetTextInputRect() functions signatures.
67// 2024-06-24: Update for SDL3 api changes: SDL_EVENT_KEY_DOWN/SDL_EVENT_KEY_UP contents.
68// 2024-06-03; Update for SDL3 api changes: SDL_SYSTEM_CURSOR_ renames.
69// 2024-05-15: Update for SDL3 api changes: SDLK_ renames.
70// 2024-04-15: Inputs: Re-enable calling SDL_StartTextInput()/SDL_StopTextInput() as SDL3 no longer enables it by default and should play nicer with IME.
71// 2024-02-13: Inputs: Fixed gamepad support. Handle gamepad disconnection. Added ImGui_ImplSDL3_SetGamepadMode().
72// 2023-11-13: Updated for recent SDL3 API changes.
73// 2023-10-05: Inputs: Added support for extra ImGuiKey values: F13 to F24 function keys, app back/forward keys.
74// 2023-05-04: Fixed build on Emscripten/iOS/Android. (#6391)
75// 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)
76// 2023-04-04: Inputs: Added support for io.AddMouseSourceEvent() to discriminate ImGuiMouseSource_Mouse/ImGuiMouseSource_TouchScreen. (#2702)
77// 2023-02-23: Accept SDL_GetPerformanceCounter() not returning a monotonically increasing value. (#6189, #6114, #3644)
78// 2023-02-07: Forked "imgui_impl_sdl2" into "imgui_impl_sdl3". Removed version checks for old feature. Refer to imgui_impl_sdl2.cpp for older changelog.
79
80#include "imgui.h"
81#ifndef IMGUI_DISABLE
82#include "imgui_impl_sdl3.h"
83
84// Clang warnings with -Weverything
85#if defined(__clang__)
86#pragma clang diagnostic push
87#pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast
88#pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision
89#endif
90
91// SDL
92#include <SDL3/SDL.h>
93#include <stdio.h> // for snprintf()
94#if defined(__APPLE__)
95#include <TargetConditionals.h>
96#endif
97#ifdef _WIN32
98#ifndef WIN32_LEAN_AND_MEAN
99#define WIN32_LEAN_AND_MEAN
100#endif
101#include <windows.h>
102#endif
103
104#if !defined(__EMSCRIPTEN__) && !defined(__ANDROID__) && !(defined(__APPLE__) && TARGET_OS_IOS) && !defined(__amigaos4__)
105#define SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE 1
106#else
107#define SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE 0
108#endif
109#define SDL_HAS_EVENT_DISPLAY_USABLE_BOUNDS_CHANGED SDL_VERSION_ATLEAST(3,4,0)
110
111// FIXME-LEGACY: remove when SDL 3.1.3 preview is released.
112#ifndef SDLK_APOSTROPHE
113#define SDLK_APOSTROPHE SDLK_QUOTE
114#endif
115#ifndef SDLK_GRAVE
116#define SDLK_GRAVE SDLK_BACKQUOTE
117#endif
118
119// SDL Data
120struct ImGui_ImplSDL3_Data
121{
122 SDL_Window* Window;
123 SDL_WindowID WindowID;
124 SDL_Renderer* Renderer;
125 Uint64 Time;
126 char* ClipboardTextData;
127 char BackendPlatformName[48];
128 bool UseVulkan;
129 bool WantUpdateMonitors;
130
131 // IME handling
132 SDL_Window* ImeWindow;
133 ImGuiPlatformImeData ImeData;
134 bool ImeDirty;
135
136 // Mouse handling
137 Uint32 MouseWindowID;
138 int MouseButtonsDown;
139 SDL_Cursor* MouseCursors[ImGuiMouseCursor_COUNT];
140 SDL_Cursor* MouseLastCursor;
141 int MousePendingLeaveFrame;
142 bool MouseCanUseGlobalState;
143 bool MouseCanReportHoveredViewport; // This is hard to use/unreliable on SDL so we'll set ImGuiBackendFlags_HasMouseHoveredViewport dynamically based on state.
144 ImGui_ImplSDL3_MouseCaptureMode MouseCaptureMode;
145
146 // Gamepad handling
147 ImVector<SDL_Gamepad*> Gamepads;
148 ImGui_ImplSDL3_GamepadMode GamepadMode;
149 bool WantUpdateGamepadsList;
150
151 ImGui_ImplSDL3_Data() { memset((void*)this, 0, sizeof(*this)); }
152};
153
154// Backend data stored in io.BackendPlatformUserData to allow support for multiple Dear ImGui contexts
155// It is STRONGLY preferred that you use docking branch with multi-viewports (== single Dear ImGui context + multiple windows) instead of multiple Dear ImGui contexts.
156// FIXME: multi-context support is not well tested and probably dysfunctional in this backend.
157// FIXME: some shared resources (mouse cursor shape, gamepad) are mishandled when using multi-context.
158static ImGui_ImplSDL3_Data* ImGui_ImplSDL3_GetBackendData()
159{
160 return ImGui::GetCurrentContext() ? (ImGui_ImplSDL3_Data*)ImGui::GetIO().BackendPlatformUserData : nullptr;
161}
162
163// Forward Declarations
164static void ImGui_ImplSDL3_UpdateIme();
165static void ImGui_ImplSDL3_UpdateMonitors();
166static void ImGui_ImplSDL3_InitMultiViewportSupport(SDL_Window* window, void* sdl_gl_context);
167static void ImGui_ImplSDL3_ShutdownMultiViewportSupport();
168
169// Functions
170static const char* ImGui_ImplSDL3_GetClipboardText(ImGuiContext*)
171{
172 ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData();
173 if (bd->ClipboardTextData)
174 SDL_free(bd->ClipboardTextData);
175 if (SDL_HasClipboardText())
176 bd->ClipboardTextData = SDL_GetClipboardText();
177 else
178 bd->ClipboardTextData = nullptr;
179 return bd->ClipboardTextData;
180}
181
182static void ImGui_ImplSDL3_SetClipboardText(ImGuiContext*, const char* text)
183{
184 SDL_SetClipboardText(text);
185}
186
187static ImGuiViewport* ImGui_ImplSDL3_GetViewportForWindowID(SDL_WindowID window_id)
188{
189 return ImGui::FindViewportByPlatformHandle((void*)(intptr_t)window_id);
190}
191
192static void ImGui_ImplSDL3_PlatformSetImeData(ImGuiContext*, ImGuiViewport*, ImGuiPlatformImeData* data)
193{
194 ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData();
195 bd->ImeData = *data;
196 bd->ImeDirty = true;
197 ImGui_ImplSDL3_UpdateIme();
198}
199
200// We discard viewport passed via ImGuiPlatformImeData and always call SDL_StartTextInput() on SDL_GetKeyboardFocus().
201static void ImGui_ImplSDL3_UpdateIme()
202{
203 ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData();
204 ImGuiPlatformImeData* data = &bd->ImeData;
205 SDL_Window* window = SDL_GetKeyboardFocus();
206
207 // Stop previous input
208 if ((!(data->WantVisible || data->WantTextInput) || bd->ImeWindow != window) && bd->ImeWindow != nullptr)
209 {
210 SDL_StopTextInput(bd->ImeWindow);
211 bd->ImeWindow = nullptr;
212 }
213 if ((!bd->ImeDirty && bd->ImeWindow == window) || (window == nullptr))
214 return;
215
216 // Start/update current input
217 bd->ImeDirty = false;
218 if (data->WantVisible)
219 {
220 ImVec2 viewport_pos;
221 if (ImGuiViewport* viewport = ImGui_ImplSDL3_GetViewportForWindowID(SDL_GetWindowID(window)))
222 viewport_pos = viewport->Pos;
223 SDL_Rect r;
224 r.x = (int)(data->InputPos.x - viewport_pos.x);
225 r.y = (int)(data->InputPos.y - viewport_pos.y);
226 r.w = 1;
227 r.h = (int)data->InputLineHeight;
228 SDL_SetTextInputArea(window, &r, 0);
229 bd->ImeWindow = window;
230 }
231 if (!SDL_TextInputActive(window) && (data->WantVisible || data->WantTextInput))
232 SDL_StartTextInput(window);
233}
234
235// Not static to allow third-party code to use that if they want to (but undocumented)
236ImGuiKey ImGui_ImplSDL3_KeyEventToImGuiKey(SDL_Keycode keycode, SDL_Scancode scancode);
237ImGuiKey ImGui_ImplSDL3_KeyEventToImGuiKey(SDL_Keycode keycode, SDL_Scancode scancode)
238{
239 // Keypad doesn't have individual key values in SDL3
240 switch (scancode)
241 {
242 case SDL_SCANCODE_KP_0: return ImGuiKey_Keypad0;
243 case SDL_SCANCODE_KP_1: return ImGuiKey_Keypad1;
244 case SDL_SCANCODE_KP_2: return ImGuiKey_Keypad2;
245 case SDL_SCANCODE_KP_3: return ImGuiKey_Keypad3;
246 case SDL_SCANCODE_KP_4: return ImGuiKey_Keypad4;
247 case SDL_SCANCODE_KP_5: return ImGuiKey_Keypad5;
248 case SDL_SCANCODE_KP_6: return ImGuiKey_Keypad6;
249 case SDL_SCANCODE_KP_7: return ImGuiKey_Keypad7;
250 case SDL_SCANCODE_KP_8: return ImGuiKey_Keypad8;
251 case SDL_SCANCODE_KP_9: return ImGuiKey_Keypad9;
252 case SDL_SCANCODE_KP_PERIOD: return ImGuiKey_KeypadDecimal;
253 case SDL_SCANCODE_KP_DIVIDE: return ImGuiKey_KeypadDivide;
254 case SDL_SCANCODE_KP_MULTIPLY: return ImGuiKey_KeypadMultiply;
255 case SDL_SCANCODE_KP_MINUS: return ImGuiKey_KeypadSubtract;
256 case SDL_SCANCODE_KP_PLUS: return ImGuiKey_KeypadAdd;
257 case SDL_SCANCODE_KP_ENTER: return ImGuiKey_KeypadEnter;
258 case SDL_SCANCODE_KP_EQUALS: return ImGuiKey_KeypadEqual;
259 default: break;
260 }
261 switch (keycode)
262 {
263 case SDLK_TAB: return ImGuiKey_Tab;
264 case SDLK_LEFT: return ImGuiKey_LeftArrow;
265 case SDLK_RIGHT: return ImGuiKey_RightArrow;
266 case SDLK_UP: return ImGuiKey_UpArrow;
267 case SDLK_DOWN: return ImGuiKey_DownArrow;
268 case SDLK_PAGEUP: return ImGuiKey_PageUp;
269 case SDLK_PAGEDOWN: return ImGuiKey_PageDown;
270 case SDLK_HOME: return ImGuiKey_Home;
271 case SDLK_END: return ImGuiKey_End;
272 case SDLK_INSERT: return ImGuiKey_Insert;
273 case SDLK_DELETE: return ImGuiKey_Delete;
274 case SDLK_BACKSPACE: return ImGuiKey_Backspace;
275 case SDLK_SPACE: return ImGuiKey_Space;
276 case SDLK_RETURN: return ImGuiKey_Enter;
277 case SDLK_ESCAPE: return ImGuiKey_Escape;
278 //case SDLK_APOSTROPHE: return ImGuiKey_Apostrophe;
279 case SDLK_COMMA: return ImGuiKey_Comma;
280 //case SDLK_MINUS: return ImGuiKey_Minus;
281 case SDLK_PERIOD: return ImGuiKey_Period;
282 //case SDLK_SLASH: return ImGuiKey_Slash;
283 case SDLK_SEMICOLON: return ImGuiKey_Semicolon;
284 //case SDLK_EQUALS: return ImGuiKey_Equal;
285 //case SDLK_LEFTBRACKET: return ImGuiKey_LeftBracket;
286 //case SDLK_BACKSLASH: return ImGuiKey_Backslash;
287 //case SDLK_RIGHTBRACKET: return ImGuiKey_RightBracket;
288 //case SDLK_GRAVE: return ImGuiKey_GraveAccent;
289 case SDLK_CAPSLOCK: return ImGuiKey_CapsLock;
290 case SDLK_SCROLLLOCK: return ImGuiKey_ScrollLock;
291 case SDLK_NUMLOCKCLEAR: return ImGuiKey_NumLock;
292 case SDLK_PRINTSCREEN: return ImGuiKey_PrintScreen;
293 case SDLK_PAUSE: return ImGuiKey_Pause;
294 case SDLK_LCTRL: return ImGuiKey_LeftCtrl;
295 case SDLK_LSHIFT: return ImGuiKey_LeftShift;
296 case SDLK_LALT: return ImGuiKey_LeftAlt;
297 case SDLK_LGUI: return ImGuiKey_LeftSuper;
298 case SDLK_RCTRL: return ImGuiKey_RightCtrl;
299 case SDLK_RSHIFT: return ImGuiKey_RightShift;
300 case SDLK_RALT: return ImGuiKey_RightAlt;
301 case SDLK_RGUI: return ImGuiKey_RightSuper;
302 case SDLK_APPLICATION: return ImGuiKey_Menu;
303 case SDLK_0: return ImGuiKey_0;
304 case SDLK_1: return ImGuiKey_1;
305 case SDLK_2: return ImGuiKey_2;
306 case SDLK_3: return ImGuiKey_3;
307 case SDLK_4: return ImGuiKey_4;
308 case SDLK_5: return ImGuiKey_5;
309 case SDLK_6: return ImGuiKey_6;
310 case SDLK_7: return ImGuiKey_7;
311 case SDLK_8: return ImGuiKey_8;
312 case SDLK_9: return ImGuiKey_9;
313 case SDLK_A: return ImGuiKey_A;
314 case SDLK_B: return ImGuiKey_B;
315 case SDLK_C: return ImGuiKey_C;
316 case SDLK_D: return ImGuiKey_D;
317 case SDLK_E: return ImGuiKey_E;
318 case SDLK_F: return ImGuiKey_F;
319 case SDLK_G: return ImGuiKey_G;
320 case SDLK_H: return ImGuiKey_H;
321 case SDLK_I: return ImGuiKey_I;
322 case SDLK_J: return ImGuiKey_J;
323 case SDLK_K: return ImGuiKey_K;
324 case SDLK_L: return ImGuiKey_L;
325 case SDLK_M: return ImGuiKey_M;
326 case SDLK_N: return ImGuiKey_N;
327 case SDLK_O: return ImGuiKey_O;
328 case SDLK_P: return ImGuiKey_P;
329 case SDLK_Q: return ImGuiKey_Q;
330 case SDLK_R: return ImGuiKey_R;
331 case SDLK_S: return ImGuiKey_S;
332 case SDLK_T: return ImGuiKey_T;
333 case SDLK_U: return ImGuiKey_U;
334 case SDLK_V: return ImGuiKey_V;
335 case SDLK_W: return ImGuiKey_W;
336 case SDLK_X: return ImGuiKey_X;
337 case SDLK_Y: return ImGuiKey_Y;
338 case SDLK_Z: return ImGuiKey_Z;
339 case SDLK_F1: return ImGuiKey_F1;
340 case SDLK_F2: return ImGuiKey_F2;
341 case SDLK_F3: return ImGuiKey_F3;
342 case SDLK_F4: return ImGuiKey_F4;
343 case SDLK_F5: return ImGuiKey_F5;
344 case SDLK_F6: return ImGuiKey_F6;
345 case SDLK_F7: return ImGuiKey_F7;
346 case SDLK_F8: return ImGuiKey_F8;
347 case SDLK_F9: return ImGuiKey_F9;
348 case SDLK_F10: return ImGuiKey_F10;
349 case SDLK_F11: return ImGuiKey_F11;
350 case SDLK_F12: return ImGuiKey_F12;
351 case SDLK_F13: return ImGuiKey_F13;
352 case SDLK_F14: return ImGuiKey_F14;
353 case SDLK_F15: return ImGuiKey_F15;
354 case SDLK_F16: return ImGuiKey_F16;
355 case SDLK_F17: return ImGuiKey_F17;
356 case SDLK_F18: return ImGuiKey_F18;
357 case SDLK_F19: return ImGuiKey_F19;
358 case SDLK_F20: return ImGuiKey_F20;
359 case SDLK_F21: return ImGuiKey_F21;
360 case SDLK_F22: return ImGuiKey_F22;
361 case SDLK_F23: return ImGuiKey_F23;
362 case SDLK_F24: return ImGuiKey_F24;
363 case SDLK_AC_BACK: return ImGuiKey_AppBack;
364 case SDLK_AC_FORWARD: return ImGuiKey_AppForward;
365 default: break;
366 }
367
368 // Fallback to scancode
369 switch (scancode)
370 {
371 case SDL_SCANCODE_GRAVE: return ImGuiKey_GraveAccent;
372 case SDL_SCANCODE_MINUS: return ImGuiKey_Minus;
373 case SDL_SCANCODE_EQUALS: return ImGuiKey_Equal;
374 case SDL_SCANCODE_LEFTBRACKET: return ImGuiKey_LeftBracket;
375 case SDL_SCANCODE_RIGHTBRACKET: return ImGuiKey_RightBracket;
376 case SDL_SCANCODE_NONUSBACKSLASH: return ImGuiKey_Oem102;
377 case SDL_SCANCODE_BACKSLASH: return ImGuiKey_Backslash;
378 case SDL_SCANCODE_SEMICOLON: return ImGuiKey_Semicolon;
379 case SDL_SCANCODE_APOSTROPHE: return ImGuiKey_Apostrophe;
380 case SDL_SCANCODE_COMMA: return ImGuiKey_Comma;
381 case SDL_SCANCODE_PERIOD: return ImGuiKey_Period;
382 case SDL_SCANCODE_SLASH: return ImGuiKey_Slash;
383 default: break;
384 }
385 return ImGuiKey_None;
386}
387
388static void ImGui_ImplSDL3_UpdateKeyModifiers(SDL_Keymod sdl_key_mods)
389{
390 ImGuiIO& io = ImGui::GetIO();
391 io.AddKeyEvent(ImGuiMod_Ctrl, (sdl_key_mods & SDL_KMOD_CTRL) != 0);
392 io.AddKeyEvent(ImGuiMod_Shift, (sdl_key_mods & SDL_KMOD_SHIFT) != 0);
393 io.AddKeyEvent(ImGuiMod_Alt, (sdl_key_mods & SDL_KMOD_ALT) != 0);
394 io.AddKeyEvent(ImGuiMod_Super, (sdl_key_mods & SDL_KMOD_GUI) != 0);
395}
396
397// You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs.
398// - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data.
399// - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data.
400// Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags.
401bool ImGui_ImplSDL3_ProcessEvent(const SDL_Event* event)
402{
403 ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData();
404 IM_ASSERT(bd != nullptr && "Context or backend not initialized! Did you call ImGui_ImplSDL3_Init()?");
405 ImGuiIO& io = ImGui::GetIO();
406
407 switch (event->type)
408 {
409 case SDL_EVENT_MOUSE_MOTION:
410 {
411 if (ImGui_ImplSDL3_GetViewportForWindowID(event->motion.windowID) == nullptr)
412 return false;
413 ImVec2 mouse_pos((float)event->motion.x, (float)event->motion.y);
414 if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
415 {
416 int window_x, window_y;
417 SDL_GetWindowPosition(SDL_GetWindowFromID(event->motion.windowID), &window_x, &window_y);
418 mouse_pos.x += window_x;
419 mouse_pos.y += window_y;
420 }
421 io.AddMouseSourceEvent(event->motion.which == SDL_TOUCH_MOUSEID ? ImGuiMouseSource_TouchScreen : ImGuiMouseSource_Mouse);
422 io.AddMousePosEvent(mouse_pos.x, mouse_pos.y);
423 return true;
424 }
425 case SDL_EVENT_MOUSE_WHEEL:
426 {
427 if (ImGui_ImplSDL3_GetViewportForWindowID(event->wheel.windowID) == nullptr)
428 return false;
429 //IMGUI_DEBUG_LOG("wheel %.2f %.2f, precise %.2f %.2f\n", (float)event->wheel.x, (float)event->wheel.y, event->wheel.preciseX, event->wheel.preciseY);
430 float wheel_x = -event->wheel.x;
431 float wheel_y = event->wheel.y;
432 io.AddMouseSourceEvent(event->wheel.which == SDL_TOUCH_MOUSEID ? ImGuiMouseSource_TouchScreen : ImGuiMouseSource_Mouse);
433 io.AddMouseWheelEvent(wheel_x, wheel_y);
434 return true;
435 }
436 case SDL_EVENT_MOUSE_BUTTON_DOWN:
437 case SDL_EVENT_MOUSE_BUTTON_UP:
438 {
439 if (ImGui_ImplSDL3_GetViewportForWindowID(event->button.windowID) == nullptr)
440 return false;
441 int mouse_button = -1;
442 if (event->button.button == SDL_BUTTON_LEFT) { mouse_button = 0; }
443 if (event->button.button == SDL_BUTTON_RIGHT) { mouse_button = 1; }
444 if (event->button.button == SDL_BUTTON_MIDDLE) { mouse_button = 2; }
445 if (event->button.button == SDL_BUTTON_X1) { mouse_button = 3; }
446 if (event->button.button == SDL_BUTTON_X2) { mouse_button = 4; }
447 if (mouse_button == -1)
448 break;
449 io.AddMouseSourceEvent(event->button.which == SDL_TOUCH_MOUSEID ? ImGuiMouseSource_TouchScreen : ImGuiMouseSource_Mouse);
450 io.AddMouseButtonEvent(mouse_button, (event->type == SDL_EVENT_MOUSE_BUTTON_DOWN));
451 bd->MouseButtonsDown = (event->type == SDL_EVENT_MOUSE_BUTTON_DOWN) ? (bd->MouseButtonsDown | (1 << mouse_button)) : (bd->MouseButtonsDown & ~(1 << mouse_button));
452 return true;
453 }
454 case SDL_EVENT_TEXT_INPUT:
455 {
456 if (ImGui_ImplSDL3_GetViewportForWindowID(event->text.windowID) == nullptr)
457 return false;
458 io.AddInputCharactersUTF8(event->text.text);
459 return true;
460 }
461 case SDL_EVENT_KEY_DOWN:
462 case SDL_EVENT_KEY_UP:
463 {
464 ImGuiViewport* viewport = ImGui_ImplSDL3_GetViewportForWindowID(event->key.windowID);
465 if (viewport == nullptr)
466 return false;
467 //IMGUI_DEBUG_LOG("SDL_EVENT_KEY_%s : key=0x%08X ('%s'), scancode=%d ('%s'), mod=%X, windowID=%d, viewport=%08X\n",
468 // (event->type == SDL_EVENT_KEY_DOWN) ? "DOWN" : "UP ", event->key.key, SDL_GetKeyName(event->key.key), event->key.scancode, SDL_GetScancodeName(event->key.scancode), event->key.mod, event->key.windowID, viewport ? viewport->ID : 0);
469 ImGui_ImplSDL3_UpdateKeyModifiers((SDL_Keymod)event->key.mod);
470 ImGuiKey key = ImGui_ImplSDL3_KeyEventToImGuiKey(event->key.key, event->key.scancode);
471 io.AddKeyEvent(key, (event->type == SDL_EVENT_KEY_DOWN));
472 io.SetKeyEventNativeData(key, (int)event->key.key, (int)event->key.scancode, (int)event->key.scancode); // To support legacy indexing (<1.87 user code). Legacy backend uses SDLK_*** as indices to IsKeyXXX() functions.
473 return true;
474 }
475 case SDL_EVENT_DISPLAY_ORIENTATION:
476 case SDL_EVENT_DISPLAY_ADDED:
477 case SDL_EVENT_DISPLAY_REMOVED:
478 case SDL_EVENT_DISPLAY_MOVED:
479 case SDL_EVENT_DISPLAY_CONTENT_SCALE_CHANGED:
480#if SDL_HAS_EVENT_DISPLAY_USABLE_BOUNDS_CHANGED
481 case SDL_EVENT_DISPLAY_USABLE_BOUNDS_CHANGED:
482#endif
483 {
484 bd->WantUpdateMonitors = true;
485 return true;
486 }
487 case SDL_EVENT_WINDOW_MOUSE_ENTER:
488 {
489 if (ImGui_ImplSDL3_GetViewportForWindowID(event->window.windowID) == nullptr)
490 return false;
491 bd->MouseWindowID = event->window.windowID;
492 bd->MousePendingLeaveFrame = 0;
493 return true;
494 }
495 // - In some cases, when detaching a window from main viewport SDL may send SDL_WINDOWEVENT_ENTER one frame too late,
496 // causing SDL_WINDOWEVENT_LEAVE on previous frame to interrupt drag operation by clear mouse position. This is why
497 // we delay process the SDL_WINDOWEVENT_LEAVE events by one frame. See issue #5012 for details.
498 // FIXME: Unconfirmed whether this is still needed with SDL3.
499 case SDL_EVENT_WINDOW_MOUSE_LEAVE:
500 {
501 if (ImGui_ImplSDL3_GetViewportForWindowID(event->window.windowID) == nullptr)
502 return false;
503 bd->MousePendingLeaveFrame = ImGui::GetFrameCount() + 1;
504 return true;
505 }
506 case SDL_EVENT_WINDOW_FOCUS_GAINED:
507 case SDL_EVENT_WINDOW_FOCUS_LOST:
508 {
509 ImGuiViewport* viewport = ImGui_ImplSDL3_GetViewportForWindowID(event->window.windowID);
510 if (viewport == nullptr)
511 return false;
512 //IMGUI_DEBUG_LOG("%s: windowId %d, viewport: %08X\n", (event->type == SDL_EVENT_WINDOW_FOCUS_GAINED) ? "SDL_EVENT_WINDOW_FOCUS_GAINED" : "SDL_WINDOWEVENT_FOCUS_LOST", event->window.windowID, viewport ? viewport->ID : 0);
513 io.AddFocusEvent(event->type == SDL_EVENT_WINDOW_FOCUS_GAINED);
514 return true;
515 }
516 case SDL_EVENT_WINDOW_CLOSE_REQUESTED:
517 case SDL_EVENT_WINDOW_MOVED:
518 case SDL_EVENT_WINDOW_RESIZED:
519 {
520 ImGuiViewport* viewport = ImGui_ImplSDL3_GetViewportForWindowID(event->window.windowID);
521 if (viewport == NULL)
522 return false;
523 if (event->type == SDL_EVENT_WINDOW_CLOSE_REQUESTED)
524 viewport->PlatformRequestClose = true;
525 if (event->type == SDL_EVENT_WINDOW_MOVED)
526 viewport->PlatformRequestMove = true;
527 if (event->type == SDL_EVENT_WINDOW_RESIZED)
528 viewport->PlatformRequestResize = true;
529 return true;
530 }
531 case SDL_EVENT_GAMEPAD_ADDED:
532 case SDL_EVENT_GAMEPAD_REMOVED:
533 {
534 bd->WantUpdateGamepadsList = true;
535 return true;
536 }
537 default:
538 break;
539 }
540 return false;
541}
542
543static void ImGui_ImplSDL3_SetupPlatformHandles(ImGuiViewport* viewport, SDL_Window* window)
544{
545 viewport->PlatformHandle = (void*)(intptr_t)SDL_GetWindowID(window);
546 viewport->PlatformHandleRaw = nullptr;
547#if defined(_WIN32) && !defined(__WINRT__)
548 viewport->PlatformHandleRaw = (HWND)SDL_GetPointerProperty(SDL_GetWindowProperties(window), SDL_PROP_WINDOW_WIN32_HWND_POINTER, nullptr);
549#elif defined(__APPLE__)
550 viewport->PlatformHandleRaw = SDL_GetPointerProperty(SDL_GetWindowProperties(window), SDL_PROP_WINDOW_COCOA_WINDOW_POINTER, nullptr);
551#endif
552}
553
554static bool ImGui_ImplSDL3_Init(SDL_Window* window, SDL_Renderer* renderer, void* sdl_gl_context)
555{
556 ImGuiIO& io = ImGui::GetIO();
557 IMGUI_CHECKVERSION();
558 IM_ASSERT(io.BackendPlatformUserData == nullptr && "Already initialized a platform backend!");
559 IM_UNUSED(sdl_gl_context); // Unused in this branch
560 //SDL_SetHint(SDL_HINT_EVENT_LOGGING, "2");
561
562 const int ver_linked = SDL_GetVersion();
563
564 // Setup backend capabilities flags
565 ImGui_ImplSDL3_Data* bd = IM_NEW(ImGui_ImplSDL3_Data)();
566 snprintf(bd->BackendPlatformName, sizeof(bd->BackendPlatformName), "imgui_impl_sdl3 (%d.%d.%d; %d.%d.%d)",
567 SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_MICRO_VERSION, SDL_VERSIONNUM_MAJOR(ver_linked), SDL_VERSIONNUM_MINOR(ver_linked), SDL_VERSIONNUM_MICRO(ver_linked));
568 io.BackendPlatformUserData = (void*)bd;
569 io.BackendPlatformName = bd->BackendPlatformName;
570 io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional)
571 io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used)
572 // (ImGuiBackendFlags_PlatformHasViewports and ImGuiBackendFlags_HasParentViewport may be set just below)
573 // (ImGuiBackendFlags_HasMouseHoveredViewport is set dynamically in our _NewFrame function)
574
575 bd->Window = window;
576 bd->WindowID = SDL_GetWindowID(window);
577 bd->Renderer = renderer;
578
579 // Check and store if we are on a SDL backend that supports SDL_GetGlobalMouseState() and SDL_CaptureMouse()
580 // ("wayland" and "rpi" don't support it, but we chose to use a white-list instead of a black-list)
581 bd->MouseCanUseGlobalState = false;
582 bd->MouseCaptureMode = ImGui_ImplSDL3_MouseCaptureMode_Disabled;
583#if SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE
584 const char* sdl_backend = SDL_GetCurrentVideoDriver();
585 const char* capture_and_global_state_whitelist[] = { "windows", "cocoa", "x11", "DIVE", "VMAN" };
586 for (const char* item : capture_and_global_state_whitelist)
587 if (strncmp(sdl_backend, item, strlen(item)) == 0)
588 {
589 bd->MouseCanUseGlobalState = true;
590 bd->MouseCaptureMode = (strcmp(item, "x11") == 0) ? ImGui_ImplSDL3_MouseCaptureMode_EnabledAfterDrag : ImGui_ImplSDL3_MouseCaptureMode_Enabled;
591 }
592#endif
593 if (bd->MouseCanUseGlobalState)
594 {
595 io.BackendFlags |= ImGuiBackendFlags_PlatformHasViewports; // We can create multi-viewports on the Platform side (optional)
596 io.BackendFlags |= ImGuiBackendFlags_HasParentViewport; // We can honor viewport->ParentViewportId by applying the corresponding parent/child relationship at platform level (optional)
597 }
598
599 // SDL on Linux/OSX doesn't report events for unfocused windows (see https://github.com/ocornut/imgui/issues/4960)
600 // We will use 'MouseCanReportHoveredViewport' to set 'ImGuiBackendFlags_HasMouseHoveredViewport' dynamically each frame.
601#ifndef __APPLE__
602 bd->MouseCanReportHoveredViewport = bd->MouseCanUseGlobalState;
603#else
604 bd->MouseCanReportHoveredViewport = false;
605#endif
606
607 ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
608 platform_io.Platform_SetClipboardTextFn = ImGui_ImplSDL3_SetClipboardText;
609 platform_io.Platform_GetClipboardTextFn = ImGui_ImplSDL3_GetClipboardText;
610 platform_io.Platform_SetImeDataFn = ImGui_ImplSDL3_PlatformSetImeData;
611 platform_io.Platform_OpenInShellFn = [](ImGuiContext*, const char* url) { return SDL_OpenURL(url); };
612
613 // Update monitor a first time during init
614 ImGui_ImplSDL3_UpdateMonitors();
615
616 // Gamepad handling
617 bd->GamepadMode = ImGui_ImplSDL3_GamepadMode_AutoFirst;
618 bd->WantUpdateGamepadsList = true;
619
620 // Load mouse cursors
621 bd->MouseCursors[ImGuiMouseCursor_Arrow] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_DEFAULT);
622 bd->MouseCursors[ImGuiMouseCursor_TextInput] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_TEXT);
623 bd->MouseCursors[ImGuiMouseCursor_ResizeAll] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_MOVE);
624 bd->MouseCursors[ImGuiMouseCursor_ResizeNS] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NS_RESIZE);
625 bd->MouseCursors[ImGuiMouseCursor_ResizeEW] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_EW_RESIZE);
626 bd->MouseCursors[ImGuiMouseCursor_ResizeNESW] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NESW_RESIZE);
627 bd->MouseCursors[ImGuiMouseCursor_ResizeNWSE] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NWSE_RESIZE);
628 bd->MouseCursors[ImGuiMouseCursor_Hand] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_POINTER);
629 bd->MouseCursors[ImGuiMouseCursor_Wait] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_WAIT);
630 bd->MouseCursors[ImGuiMouseCursor_Progress] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_PROGRESS);
631 bd->MouseCursors[ImGuiMouseCursor_NotAllowed] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NOT_ALLOWED);
632
633 // Set platform dependent data in viewport
634 // Our mouse update function expect PlatformHandle to be filled for the main viewport
635 ImGuiViewport* main_viewport = ImGui::GetMainViewport();
636 ImGui_ImplSDL3_SetupPlatformHandles(main_viewport, window);
637
638 // From 2.0.5: Set SDL hint to receive mouse click events on window focus, otherwise SDL doesn't emit the event.
639 // Without this, when clicking to gain focus, our widgets wouldn't activate even though they showed as hovered.
640 // (This is unfortunately a global SDL setting, so enabling it might have a side-effect on your application.
641 // It is unlikely to make a difference, but if your app absolutely needs to ignore the initial on-focus click:
642 // you can ignore SDL_EVENT_MOUSE_BUTTON_DOWN events coming right after a SDL_EVENT_WINDOW_FOCUS_GAINED)
643 SDL_SetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, "1");
644
645 // From 2.0.22: Disable auto-capture, this is preventing drag and drop across multiple windows (see #5710)
646 SDL_SetHint(SDL_HINT_MOUSE_AUTO_CAPTURE, "0");
647
648 // SDL 3.x : see https://github.com/libsdl-org/SDL/issues/6659
649 SDL_SetHint("SDL_BORDERLESS_WINDOWED_STYLE", "0");
650
651 // We need SDL_CaptureMouse(), SDL_GetGlobalMouseState() from SDL 2.0.4+ to support multiple viewports.
652 // We left the call to ImGui_ImplSDL3_InitPlatformInterface() outside of #ifdef to avoid unused-function warnings.
653 if (io.BackendFlags & ImGuiBackendFlags_PlatformHasViewports)
654 ImGui_ImplSDL3_InitMultiViewportSupport(window, sdl_gl_context);
655
656 return true;
657}
658
659// Should technically be a SDL_GLContext but due to typedef it is sane to keep it void* in public interface.
660bool ImGui_ImplSDL3_InitForOpenGL(SDL_Window* window, void* sdl_gl_context)
661{
662 return ImGui_ImplSDL3_Init(window, nullptr, sdl_gl_context);
663}
664
665bool ImGui_ImplSDL3_InitForVulkan(SDL_Window* window)
666{
667 if (!ImGui_ImplSDL3_Init(window, nullptr, nullptr))
668 return false;
669 ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData();
670 bd->UseVulkan = true;
671 return true;
672}
673
674bool ImGui_ImplSDL3_InitForD3D(SDL_Window* window)
675{
676#if !defined(_WIN32)
677 IM_ASSERT(0 && "Unsupported");
678#endif
679 return ImGui_ImplSDL3_Init(window, nullptr, nullptr);
680}
681
682bool ImGui_ImplSDL3_InitForMetal(SDL_Window* window)
683{
684 return ImGui_ImplSDL3_Init(window, nullptr, nullptr);
685}
686
687bool ImGui_ImplSDL3_InitForSDLRenderer(SDL_Window* window, SDL_Renderer* renderer)
688{
689 return ImGui_ImplSDL3_Init(window, renderer, nullptr);
690}
691
692bool ImGui_ImplSDL3_InitForSDLGPU(SDL_Window* window)
693{
694 return ImGui_ImplSDL3_Init(window, nullptr, nullptr);
695}
696
697bool ImGui_ImplSDL3_InitForOther(SDL_Window* window)
698{
699 return ImGui_ImplSDL3_Init(window, nullptr, nullptr);
700}
701
702static void ImGui_ImplSDL3_CloseGamepads();
703
704void ImGui_ImplSDL3_Shutdown()
705{
706 ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData();
707 IM_ASSERT(bd != nullptr && "No platform backend to shutdown, or already shutdown?");
708 ImGuiIO& io = ImGui::GetIO();
709 ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
710
711 ImGui_ImplSDL3_ShutdownMultiViewportSupport();
712 if (bd->ClipboardTextData)
713 SDL_free(bd->ClipboardTextData);
714 for (ImGuiMouseCursor cursor_n = 0; cursor_n < ImGuiMouseCursor_COUNT; cursor_n++)
715 SDL_DestroyCursor(bd->MouseCursors[cursor_n]);
716 ImGui_ImplSDL3_CloseGamepads();
717
718 io.BackendPlatformName = nullptr;
719 io.BackendPlatformUserData = nullptr;
720 io.BackendFlags &= ~(ImGuiBackendFlags_HasMouseCursors | ImGuiBackendFlags_HasSetMousePos | ImGuiBackendFlags_HasGamepad | ImGuiBackendFlags_PlatformHasViewports | ImGuiBackendFlags_HasMouseHoveredViewport | ImGuiBackendFlags_HasParentViewport);
721 platform_io.ClearPlatformHandlers();
722 IM_DELETE(bd);
723}
724
725void ImGui_ImplSDL3_SetMouseCaptureMode(ImGui_ImplSDL3_MouseCaptureMode mode)
726{
727 ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData();
728 if (mode == ImGui_ImplSDL3_MouseCaptureMode_Disabled && bd->MouseCaptureMode != ImGui_ImplSDL3_MouseCaptureMode_Disabled)
729 SDL_CaptureMouse(false);
730 bd->MouseCaptureMode = mode;
731}
732
733static void ImGui_ImplSDL3_UpdateMouseData()
734{
735 ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData();
736 ImGuiIO& io = ImGui::GetIO();
737
738 // We forward mouse input when hovered or captured (via SDL_EVENT_MOUSE_MOTION) or when focused (below)
739#if SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE
740 // - 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.
741 // - 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.
742 if (bd->MouseCaptureMode == ImGui_ImplSDL3_MouseCaptureMode_Enabled)
743 {
744 SDL_CaptureMouse(bd->MouseButtonsDown != 0);
745 }
746 else if (bd->MouseCaptureMode == ImGui_ImplSDL3_MouseCaptureMode_EnabledAfterDrag)
747 {
748 bool want_capture = false;
749 for (int button_n = 0; button_n < ImGuiMouseButton_COUNT && !want_capture; button_n++)
750 if (ImGui::IsMouseDragging(button_n, 1.0f))
751 want_capture = true;
752 SDL_CaptureMouse(want_capture);
753 }
754
755 SDL_Window* focused_window = SDL_GetKeyboardFocus();
756 const bool is_app_focused = (focused_window && (bd->Window == focused_window || ImGui_ImplSDL3_GetViewportForWindowID(SDL_GetWindowID(focused_window)) != NULL));
757#else
758 SDL_Window* focused_window = bd->Window;
759 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
760#endif
761 if (is_app_focused)
762 {
763 // (Optional) Set OS mouse position from Dear ImGui if requested (rarely used, only when io.ConfigNavMoveSetMousePos is enabled by user)
764 if (io.WantSetMousePos)
765 {
766#if SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE
767 if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
768 SDL_WarpMouseGlobal(io.MousePos.x, io.MousePos.y);
769 else
770#endif
771 SDL_WarpMouseInWindow(bd->Window, io.MousePos.x, io.MousePos.y);
772 }
773
774 // (Optional) Fallback to provide unclamped mouse position when focused but not hovered (SDL_EVENT_MOUSE_MOTION already provides this when hovered or captured)
775 // 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.
776 SDL_Window* hovered_window = SDL_GetMouseFocus();
777 const bool is_relative_mouse_mode = SDL_GetWindowRelativeMouseMode(bd->Window);
778 if (hovered_window == nullptr && bd->MouseCanUseGlobalState && bd->MouseButtonsDown == 0 && !is_relative_mouse_mode)
779 {
780 // 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)
781 // 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)
782 float mouse_x, mouse_y;
783 int window_x, window_y;
784 SDL_GetGlobalMouseState(&mouse_x, &mouse_y);
785 if (!(io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable))
786 {
787 SDL_GetWindowPosition(focused_window, &window_x, &window_y);
788 mouse_x -= (float)window_x;
789 mouse_y -= (float)window_y;
790 }
791 io.AddMousePosEvent(mouse_x, mouse_y);
792 }
793 }
794
795 // (Optional) When using multiple viewports: call io.AddMouseViewportEvent() with the viewport the OS mouse cursor is hovering.
796 // If ImGuiBackendFlags_HasMouseHoveredViewport is not set by the backend, Dear imGui will ignore this field and infer the information using its flawed heuristic.
797 // - [!] SDL backend does NOT correctly ignore viewports with the _NoInputs flag.
798 // 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
799 // 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
800 // by the backend, and use its flawed heuristic to guess the viewport behind.
801 // - [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).
802 if (io.BackendFlags & ImGuiBackendFlags_HasMouseHoveredViewport)
803 {
804 ImGuiID mouse_viewport_id = 0;
805 if (ImGuiViewport* mouse_viewport = ImGui_ImplSDL3_GetViewportForWindowID(bd->MouseWindowID))
806 mouse_viewport_id = mouse_viewport->ID;
807 io.AddMouseViewportEvent(mouse_viewport_id);
808 }
809}
810
811static void ImGui_ImplSDL3_UpdateMouseCursor()
812{
813 ImGuiIO& io = ImGui::GetIO();
814 if (io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange)
815 return;
816 ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData();
817
818 ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor();
819 if (io.MouseDrawCursor || imgui_cursor == ImGuiMouseCursor_None)
820 {
821 // Hide OS mouse cursor if imgui is drawing it or if it wants no cursor
822 SDL_HideCursor();
823 }
824 else
825 {
826 // Show OS mouse cursor
827 SDL_Cursor* expected_cursor = bd->MouseCursors[imgui_cursor] ? bd->MouseCursors[imgui_cursor] : bd->MouseCursors[ImGuiMouseCursor_Arrow];
828 if (bd->MouseLastCursor != expected_cursor)
829 {
830 SDL_SetCursor(expected_cursor); // SDL function doesn't have an early out (see #6113)
831 bd->MouseLastCursor = expected_cursor;
832 }
833 SDL_ShowCursor();
834 }
835}
836
837static void ImGui_ImplSDL3_CloseGamepads()
838{
839 ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData();
840 if (bd->GamepadMode != ImGui_ImplSDL3_GamepadMode_Manual)
841 for (SDL_Gamepad* gamepad : bd->Gamepads)
842 SDL_CloseGamepad(gamepad);
843 bd->Gamepads.resize(0);
844}
845
846void ImGui_ImplSDL3_SetGamepadMode(ImGui_ImplSDL3_GamepadMode mode, SDL_Gamepad** manual_gamepads_array, int manual_gamepads_count)
847{
848 ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData();
849 ImGui_ImplSDL3_CloseGamepads();
850 if (mode == ImGui_ImplSDL3_GamepadMode_Manual)
851 {
852 IM_ASSERT(manual_gamepads_array != nullptr || manual_gamepads_count <= 0);
853 for (int n = 0; n < manual_gamepads_count; n++)
854 bd->Gamepads.push_back(manual_gamepads_array[n]);
855 }
856 else
857 {
858 IM_ASSERT(manual_gamepads_array == nullptr && manual_gamepads_count <= 0);
859 bd->WantUpdateGamepadsList = true;
860 }
861 bd->GamepadMode = mode;
862}
863
864static void ImGui_ImplSDL3_UpdateGamepadButton(ImGui_ImplSDL3_Data* bd, ImGuiIO& io, ImGuiKey key, SDL_GamepadButton button_no)
865{
866 bool merged_value = false;
867 for (SDL_Gamepad* gamepad : bd->Gamepads)
868 merged_value |= SDL_GetGamepadButton(gamepad, button_no) != 0;
869 io.AddKeyEvent(key, merged_value);
870}
871
872static inline float Saturate(float v) { return v < 0.0f ? 0.0f : v > 1.0f ? 1.0f : v; }
873static void ImGui_ImplSDL3_UpdateGamepadAnalog(ImGui_ImplSDL3_Data* bd, ImGuiIO& io, ImGuiKey key, SDL_GamepadAxis axis_no, float v0, float v1)
874{
875 float merged_value = 0.0f;
876 for (SDL_Gamepad* gamepad : bd->Gamepads)
877 {
878 float vn = Saturate((float)(SDL_GetGamepadAxis(gamepad, axis_no) - v0) / (float)(v1 - v0));
879 if (merged_value < vn)
880 merged_value = vn;
881 }
882 io.AddKeyAnalogEvent(key, merged_value > 0.1f, merged_value);
883}
884
885static void ImGui_ImplSDL3_UpdateGamepads()
886{
887 ImGuiIO& io = ImGui::GetIO();
888 ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData();
889
890 // Update list of gamepads to use
891 if (bd->WantUpdateGamepadsList && bd->GamepadMode != ImGui_ImplSDL3_GamepadMode_Manual)
892 {
893 ImGui_ImplSDL3_CloseGamepads();
894 int sdl_gamepads_count = 0;
895 SDL_JoystickID* sdl_gamepads = SDL_GetGamepads(&sdl_gamepads_count);
896 for (int n = 0; n < sdl_gamepads_count; n++)
897 if (SDL_Gamepad* gamepad = SDL_OpenGamepad(sdl_gamepads[n]))
898 {
899 bd->Gamepads.push_back(gamepad);
900 if (bd->GamepadMode == ImGui_ImplSDL3_GamepadMode_AutoFirst)
901 break;
902 }
903 bd->WantUpdateGamepadsList = false;
904 SDL_free(sdl_gamepads);
905 }
906
907 io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad;
908 if (bd->Gamepads.Size == 0)
909 return;
910 io.BackendFlags |= ImGuiBackendFlags_HasGamepad;
911
912 // Update gamepad inputs
913 const int thumb_dead_zone = 8000; // SDL_gamepad.h suggests using this value.
914 ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadStart, SDL_GAMEPAD_BUTTON_START);
915 ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadBack, SDL_GAMEPAD_BUTTON_BACK);
916 ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadFaceLeft, SDL_GAMEPAD_BUTTON_WEST); // Xbox X, PS Square
917 ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadFaceRight, SDL_GAMEPAD_BUTTON_EAST); // Xbox B, PS Circle
918 ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadFaceUp, SDL_GAMEPAD_BUTTON_NORTH); // Xbox Y, PS Triangle
919 ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadFaceDown, SDL_GAMEPAD_BUTTON_SOUTH); // Xbox A, PS Cross
920 ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadDpadLeft, SDL_GAMEPAD_BUTTON_DPAD_LEFT);
921 ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadDpadRight, SDL_GAMEPAD_BUTTON_DPAD_RIGHT);
922 ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadDpadUp, SDL_GAMEPAD_BUTTON_DPAD_UP);
923 ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadDpadDown, SDL_GAMEPAD_BUTTON_DPAD_DOWN);
924 ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadL1, SDL_GAMEPAD_BUTTON_LEFT_SHOULDER);
925 ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadR1, SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER);
926 ImGui_ImplSDL3_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadL2, SDL_GAMEPAD_AXIS_LEFT_TRIGGER, 0.0f, 32767);
927 ImGui_ImplSDL3_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadR2, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER, 0.0f, 32767);
928 ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadL3, SDL_GAMEPAD_BUTTON_LEFT_STICK);
929 ImGui_ImplSDL3_UpdateGamepadButton(bd, io, ImGuiKey_GamepadR3, SDL_GAMEPAD_BUTTON_RIGHT_STICK);
930 ImGui_ImplSDL3_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadLStickLeft, SDL_GAMEPAD_AXIS_LEFTX, -thumb_dead_zone, -32768);
931 ImGui_ImplSDL3_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadLStickRight, SDL_GAMEPAD_AXIS_LEFTX, +thumb_dead_zone, +32767);
932 ImGui_ImplSDL3_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadLStickUp, SDL_GAMEPAD_AXIS_LEFTY, -thumb_dead_zone, -32768);
933 ImGui_ImplSDL3_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadLStickDown, SDL_GAMEPAD_AXIS_LEFTY, +thumb_dead_zone, +32767);
934 ImGui_ImplSDL3_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadRStickLeft, SDL_GAMEPAD_AXIS_RIGHTX, -thumb_dead_zone, -32768);
935 ImGui_ImplSDL3_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadRStickRight, SDL_GAMEPAD_AXIS_RIGHTX, +thumb_dead_zone, +32767);
936 ImGui_ImplSDL3_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadRStickUp, SDL_GAMEPAD_AXIS_RIGHTY, -thumb_dead_zone, -32768);
937 ImGui_ImplSDL3_UpdateGamepadAnalog(bd, io, ImGuiKey_GamepadRStickDown, SDL_GAMEPAD_AXIS_RIGHTY, +thumb_dead_zone, +32767);
938}
939
940static void ImGui_ImplSDL3_UpdateMonitors()
941{
942 ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData();
943 ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
944 platform_io.Monitors.resize(0);
945 bd->WantUpdateMonitors = false;
946
947 int display_count;
948 SDL_DisplayID* displays = SDL_GetDisplays(&display_count);
949 for (int n = 0; n < display_count; n++)
950 {
951 // 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.
952 SDL_DisplayID display_id = displays[n];
953 ImGuiPlatformMonitor monitor;
954 SDL_Rect r;
955 SDL_GetDisplayBounds(display_id, &r);
956 monitor.MainPos = monitor.WorkPos = ImVec2((float)r.x, (float)r.y);
957 monitor.MainSize = monitor.WorkSize = ImVec2((float)r.w, (float)r.h);
958 if (SDL_GetDisplayUsableBounds(display_id, &r) && r.w > 0 && r.h > 0)
959 {
960 monitor.WorkPos = ImVec2((float)r.x, (float)r.y);
961 monitor.WorkSize = ImVec2((float)r.w, (float)r.h);
962 }
963 monitor.DpiScale = SDL_GetDisplayContentScale(display_id); // See https://wiki.libsdl.org/SDL3/README-highdpi for details.
964 monitor.PlatformHandle = (void*)(intptr_t)n;
965 if (monitor.DpiScale <= 0.0f)
966 continue; // Some accessibility applications are declaring virtual monitors with a DPI of 0, see #7902.
967 platform_io.Monitors.push_back(monitor);
968 }
969 SDL_free(displays);
970}
971
972static void ImGui_ImplSDL3_GetWindowSizeAndFramebufferScale(SDL_Window* window, ImVec2* out_size, ImVec2* out_framebuffer_scale)
973{
974 int w, h;
975 SDL_GetWindowSize(window, &w, &h);
976 if (SDL_GetWindowFlags(window) & SDL_WINDOW_MINIMIZED)
977 w = h = 0;
978
979#if defined(__APPLE__)
980 float fb_scale_x = SDL_GetWindowDisplayScale(window); // Seems more reliable during resolution change (#8703)
981 float fb_scale_y = fb_scale_x;
982#else
983 int display_w, display_h;
984 SDL_GetWindowSizeInPixels(window, &display_w, &display_h);
985 float fb_scale_x = (w > 0) ? (float)display_w / (float)w : 1.0f;
986 float fb_scale_y = (h > 0) ? (float)display_h / (float)h : 1.0f;
987#endif
988
989 if (out_size != nullptr)
990 *out_size = ImVec2((float)w, (float)h);
991 if (out_framebuffer_scale != nullptr)
992 *out_framebuffer_scale = ImVec2(fb_scale_x, fb_scale_y);
993}
994
995void ImGui_ImplSDL3_NewFrame()
996{
997 ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData();
998 IM_ASSERT(bd != nullptr && "Context or backend not initialized! Did you call ImGui_ImplSDL3_Init()?");
999 ImGuiIO& io = ImGui::GetIO();
1000
1001 // Setup main viewport size (every frame to accommodate for window resizing)
1002 ImGui_ImplSDL3_GetWindowSizeAndFramebufferScale(bd->Window, &io.DisplaySize, &io.DisplayFramebufferScale);
1003
1004 // Update monitors
1005#if defined(WIN32) && !SDL_HAS_EVENT_DISPLAY_USABLE_BOUNDS_CHANGED
1006 bd->WantUpdateMonitors = true; // Keep polling under Windows to handle changes of work area when resizing task-bar (#8415)
1007#endif
1008 if (bd->WantUpdateMonitors)
1009 ImGui_ImplSDL3_UpdateMonitors();
1010
1011 // Setup time step (we could also use SDL_GetTicksNS() available since SDL3)
1012 // (Accept SDL_GetPerformanceCounter() not returning a monotonically increasing value. Happens in VMs and Emscripten, see #6189, #6114, #3644)
1013 static Uint64 frequency = SDL_GetPerformanceFrequency();
1014 Uint64 current_time = SDL_GetPerformanceCounter();
1015 if (current_time <= bd->Time)
1016 current_time = bd->Time + 1;
1017 io.DeltaTime = bd->Time > 0 ? (float)((double)(current_time - bd->Time) / (double)frequency) : (float)(1.0f / 60.0f);
1018 bd->Time = current_time;
1019
1020 if (bd->MousePendingLeaveFrame && bd->MousePendingLeaveFrame >= ImGui::GetFrameCount() && bd->MouseButtonsDown == 0)
1021 {
1022 bd->MouseWindowID = 0;
1023 bd->MousePendingLeaveFrame = 0;
1024 io.AddMousePosEvent(-FLT_MAX, -FLT_MAX);
1025 }
1026
1027 // Our io.AddMouseViewportEvent() calls will only be valid when not capturing.
1028 // Technically speaking testing for 'bd->MouseButtonsDown == 0' would be more rigorous, but testing for payload reduces noise and potential side-effects.
1029 if (bd->MouseCanReportHoveredViewport && ImGui::GetDragDropPayload() == nullptr)
1030 io.BackendFlags |= ImGuiBackendFlags_HasMouseHoveredViewport;
1031 else
1032 io.BackendFlags &= ~ImGuiBackendFlags_HasMouseHoveredViewport;
1033
1034 ImGui_ImplSDL3_UpdateMouseData();
1035 ImGui_ImplSDL3_UpdateMouseCursor();
1036 ImGui_ImplSDL3_UpdateIme();
1037
1038 // Update game controllers (if enabled and available)
1039 ImGui_ImplSDL3_UpdateGamepads();
1040}
1041
1042//--------------------------------------------------------------------------------------------------------
1043// MULTI-VIEWPORT / PLATFORM INTERFACE SUPPORT
1044// This is an _advanced_ and _optional_ feature, allowing the backend to create and handle multiple viewports simultaneously.
1045// 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..
1046//--------------------------------------------------------------------------------------------------------
1047
1048// Helper structure we store in the void* PlatformUserData field of each ImGuiViewport to easily retrieve our backend data.
1049struct ImGui_ImplSDL3_ViewportData
1050{
1051 SDL_Window* Window;
1052 SDL_Window* ParentWindow;
1053 Uint32 WindowID; // Stored in ImGuiViewport::PlatformHandle. Use SDL_GetWindowFromID() to get SDL_Window* from Uint32 WindowID.
1054 bool WindowOwned;
1055 SDL_GLContext GLContext;
1056
1057 ImGui_ImplSDL3_ViewportData() { Window = ParentWindow = nullptr; WindowID = 0; WindowOwned = false; GLContext = nullptr; }
1058 ~ImGui_ImplSDL3_ViewportData() { IM_ASSERT(Window == nullptr && GLContext == nullptr); }
1059};
1060
1061static SDL_Window* ImGui_ImplSDL3_GetSDLWindowFromViewport(ImGuiViewport* viewport)
1062{
1063 if (viewport != nullptr)
1064 {
1065 SDL_WindowID window_id = (SDL_WindowID)(intptr_t)viewport->PlatformHandle;
1066 return SDL_GetWindowFromID(window_id);
1067 }
1068 return nullptr;
1069}
1070
1071static void ImGui_ImplSDL3_CreateWindow(ImGuiViewport* viewport)
1072{
1073 ImGui_ImplSDL3_Data* bd = ImGui_ImplSDL3_GetBackendData();
1074 ImGui_ImplSDL3_ViewportData* vd = IM_NEW(ImGui_ImplSDL3_ViewportData)();
1075 viewport->PlatformUserData = vd;
1076
1077 vd->ParentWindow = ImGui_ImplSDL3_GetSDLWindowFromViewport(viewport->ParentViewport);
1078
1079 ImGuiViewport* main_viewport = ImGui::GetMainViewport();
1080 ImGui_ImplSDL3_ViewportData* main_viewport_data = (ImGui_ImplSDL3_ViewportData*)main_viewport->PlatformUserData;
1081
1082 // Share GL resources with main context
1083 bool use_opengl = (main_viewport_data->GLContext != nullptr);
1084 SDL_GLContext backup_context = nullptr;
1085 if (use_opengl)
1086 {
1087 backup_context = SDL_GL_GetCurrentContext();
1088 SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1);
1089 SDL_GL_MakeCurrent(main_viewport_data->Window, main_viewport_data->GLContext);
1090 }
1091
1092 SDL_WindowFlags sdl_flags = 0;
1093 sdl_flags |= SDL_WINDOW_HIDDEN;
1094 sdl_flags |= use_opengl ? SDL_WINDOW_OPENGL : (bd->UseVulkan ? SDL_WINDOW_VULKAN : 0);
1095 sdl_flags |= SDL_GetWindowFlags(bd->Window) & SDL_WINDOW_HIGH_PIXEL_DENSITY;
1096 sdl_flags |= (viewport->Flags & ImGuiViewportFlags_NoDecoration) ? SDL_WINDOW_BORDERLESS : 0;
1097 sdl_flags |= (viewport->Flags & ImGuiViewportFlags_NoDecoration) ? 0 : SDL_WINDOW_RESIZABLE;
1098 sdl_flags |= (viewport->Flags & ImGuiViewportFlags_NoTaskBarIcon) ? SDL_WINDOW_UTILITY : 0;
1099 sdl_flags |= (viewport->Flags & ImGuiViewportFlags_TopMost) ? SDL_WINDOW_ALWAYS_ON_TOP : 0;
1100 vd->Window = SDL_CreateWindow("No Title Yet", (int)viewport->Size.x, (int)viewport->Size.y, sdl_flags);
1101#ifndef __APPLE__ // On Mac, SDL3 Parenting appears to prevent viewport from appearing in another monitor
1102 SDL_SetWindowParent(vd->Window, vd->ParentWindow);
1103#endif
1104 SDL_SetWindowPosition(vd->Window, (int)viewport->Pos.x, (int)viewport->Pos.y);
1105 vd->WindowOwned = true;
1106 if (use_opengl)
1107 {
1108 vd->GLContext = SDL_GL_CreateContext(vd->Window);
1109 SDL_GL_SetSwapInterval(0);
1110 }
1111 if (use_opengl && backup_context)
1112 SDL_GL_MakeCurrent(vd->Window, backup_context);
1113
1114 ImGui_ImplSDL3_SetupPlatformHandles(viewport, vd->Window);
1115}
1116
1117static void ImGui_ImplSDL3_DestroyWindow(ImGuiViewport* viewport)
1118{
1119 if (ImGui_ImplSDL3_ViewportData* vd = (ImGui_ImplSDL3_ViewportData*)viewport->PlatformUserData)
1120 {
1121 if (vd->GLContext && vd->WindowOwned)
1122 SDL_GL_DestroyContext(vd->GLContext);
1123 if (vd->Window && vd->WindowOwned)
1124 SDL_DestroyWindow(vd->Window);
1125 vd->GLContext = nullptr;
1126 vd->Window = nullptr;
1127 IM_DELETE(vd);
1128 }
1129 viewport->PlatformUserData = viewport->PlatformHandle = nullptr;
1130}
1131
1132static void ImGui_ImplSDL3_ShowWindow(ImGuiViewport* viewport)
1133{
1134 ImGui_ImplSDL3_ViewportData* vd = (ImGui_ImplSDL3_ViewportData*)viewport->PlatformUserData;
1135#if defined(_WIN32) && !(defined(WINAPI_FAMILY) && ((defined(WINAPI_FAMILY_APP) && WINAPI_FAMILY == WINAPI_FAMILY_APP) || (defined(WINAPI_FAMILY_GAMES) && WINAPI_FAMILY == WINAPI_FAMILY_GAMES)))
1136 HWND hwnd = (HWND)viewport->PlatformHandleRaw;
1137
1138 // SDL hack: Show icon in task bar (#7989)
1139 // Note: SDL_WINDOW_UTILITY can be used to control task bar visibility, but on Windows, it does not affect child windows.
1140 if (!(viewport->Flags & ImGuiViewportFlags_NoTaskBarIcon))
1141 {
1142 LONG ex_style = ::GetWindowLong(hwnd, GWL_EXSTYLE);
1143 ex_style |= WS_EX_APPWINDOW;
1144 ex_style &= ~WS_EX_TOOLWINDOW;
1145 ::ShowWindow(hwnd, SW_HIDE);
1146 ::SetWindowLong(hwnd, GWL_EXSTYLE, ex_style);
1147 }
1148#endif
1149
1150#ifdef __APPLE__
1151 SDL_SetHint(SDL_HINT_WINDOW_ACTIVATE_WHEN_SHOWN, "1"); // Otherwise new window appear under
1152#else
1153 SDL_SetHint(SDL_HINT_WINDOW_ACTIVATE_WHEN_SHOWN, (viewport->Flags & ImGuiViewportFlags_NoFocusOnAppearing) ? "0" : "1");
1154#endif
1155 SDL_ShowWindow(vd->Window);
1156}
1157
1158static void ImGui_ImplSDL3_UpdateWindow(ImGuiViewport* viewport)
1159{
1160 ImGui_ImplSDL3_ViewportData* vd = (ImGui_ImplSDL3_ViewportData*)viewport->PlatformUserData;
1161 IM_UNUSED(vd);
1162
1163#ifndef __APPLE__ // On Mac, SDL3 Parenting appears to prevent viewport from appearing in another monitor
1164 // Update SDL3 parent if it changed _after_ creation.
1165 // This is for advanced apps that are manipulating ParentViewportID manually.
1166 SDL_Window* new_parent = ImGui_ImplSDL3_GetSDLWindowFromViewport(viewport->ParentViewport);
1167 if (new_parent != vd->ParentWindow)
1168 {
1169 vd->ParentWindow = new_parent;
1170 SDL_SetWindowParent(vd->Window, vd->ParentWindow);
1171 }
1172#endif
1173}
1174
1175static ImVec2 ImGui_ImplSDL3_GetWindowPos(ImGuiViewport* viewport)
1176{
1177 ImGui_ImplSDL3_ViewportData* vd = (ImGui_ImplSDL3_ViewportData*)viewport->PlatformUserData;
1178 int x = 0, y = 0;
1179 SDL_GetWindowPosition(vd->Window, &x, &y);
1180 return ImVec2((float)x, (float)y);
1181}
1182
1183static void ImGui_ImplSDL3_SetWindowPos(ImGuiViewport* viewport, ImVec2 pos)
1184{
1185 ImGui_ImplSDL3_ViewportData* vd = (ImGui_ImplSDL3_ViewportData*)viewport->PlatformUserData;
1186 SDL_SetWindowPosition(vd->Window, (int)pos.x, (int)pos.y);
1187}
1188
1189static ImVec2 ImGui_ImplSDL3_GetWindowSize(ImGuiViewport* viewport)
1190{
1191 ImGui_ImplSDL3_ViewportData* vd = (ImGui_ImplSDL3_ViewportData*)viewport->PlatformUserData;
1192 int w = 0, h = 0;
1193 SDL_GetWindowSize(vd->Window, &w, &h);
1194 return ImVec2((float)w, (float)h);
1195}
1196
1197static void ImGui_ImplSDL3_SetWindowSize(ImGuiViewport* viewport, ImVec2 size)
1198{
1199 ImGui_ImplSDL3_ViewportData* vd = (ImGui_ImplSDL3_ViewportData*)viewport->PlatformUserData;
1200 SDL_SetWindowSize(vd->Window, (int)size.x, (int)size.y);
1201}
1202
1203static ImVec2 ImGui_ImplSDL3_GetWindowFramebufferScale(ImGuiViewport* viewport)
1204{
1205 ImGui_ImplSDL3_ViewportData* vd = (ImGui_ImplSDL3_ViewportData*)viewport->PlatformUserData;
1206 ImVec2 framebuffer_scale;
1207 ImGui_ImplSDL3_GetWindowSizeAndFramebufferScale(vd->Window, nullptr, &framebuffer_scale);
1208 return framebuffer_scale;
1209}
1210
1211static void ImGui_ImplSDL3_SetWindowTitle(ImGuiViewport* viewport, const char* title)
1212{
1213 ImGui_ImplSDL3_ViewportData* vd = (ImGui_ImplSDL3_ViewportData*)viewport->PlatformUserData;
1214 SDL_SetWindowTitle(vd->Window, title);
1215}
1216
1217static void ImGui_ImplSDL3_SetWindowAlpha(ImGuiViewport* viewport, float alpha)
1218{
1219 ImGui_ImplSDL3_ViewportData* vd = (ImGui_ImplSDL3_ViewportData*)viewport->PlatformUserData;
1220 SDL_SetWindowOpacity(vd->Window, alpha);
1221}
1222
1223static void ImGui_ImplSDL3_SetWindowFocus(ImGuiViewport* viewport)
1224{
1225 ImGui_ImplSDL3_ViewportData* vd = (ImGui_ImplSDL3_ViewportData*)viewport->PlatformUserData;
1226 SDL_RaiseWindow(vd->Window);
1227}
1228
1229static bool ImGui_ImplSDL3_GetWindowFocus(ImGuiViewport* viewport)
1230{
1231 ImGui_ImplSDL3_ViewportData* vd = (ImGui_ImplSDL3_ViewportData*)viewport->PlatformUserData;
1232 return (SDL_GetWindowFlags(vd->Window) & SDL_WINDOW_INPUT_FOCUS) != 0;
1233}
1234
1235static bool ImGui_ImplSDL3_GetWindowMinimized(ImGuiViewport* viewport)
1236{
1237 ImGui_ImplSDL3_ViewportData* vd = (ImGui_ImplSDL3_ViewportData*)viewport->PlatformUserData;
1238 return (SDL_GetWindowFlags(vd->Window) & SDL_WINDOW_MINIMIZED) != 0;
1239}
1240
1241static void ImGui_ImplSDL3_RenderWindow(ImGuiViewport* viewport, void*)
1242{
1243 ImGui_ImplSDL3_ViewportData* vd = (ImGui_ImplSDL3_ViewportData*)viewport->PlatformUserData;
1244 if (vd->GLContext)
1245 SDL_GL_MakeCurrent(vd->Window, vd->GLContext);
1246}
1247
1248static void ImGui_ImplSDL3_SwapBuffers(ImGuiViewport* viewport, void*)
1249{
1250 ImGui_ImplSDL3_ViewportData* vd = (ImGui_ImplSDL3_ViewportData*)viewport->PlatformUserData;
1251 if (vd->GLContext)
1252 {
1253 SDL_GL_MakeCurrent(vd->Window, vd->GLContext);
1254 SDL_GL_SwapWindow(vd->Window);
1255 }
1256}
1257
1258// Vulkan support (the Vulkan renderer needs to call a platform-side support function to create the surface)
1259// SDL is graceful enough to _not_ need <vulkan/vulkan.h> so we can safely include this.
1260#include <SDL3/SDL_vulkan.h>
1261static int ImGui_ImplSDL3_CreateVkSurface(ImGuiViewport* viewport, ImU64 vk_instance, const void* vk_allocator, ImU64* out_vk_surface)
1262{
1263 ImGui_ImplSDL3_ViewportData* vd = (ImGui_ImplSDL3_ViewportData*)viewport->PlatformUserData;
1264 (void)vk_allocator;
1265 bool ret = SDL_Vulkan_CreateSurface(vd->Window, (VkInstance)vk_instance, (const VkAllocationCallbacks*)vk_allocator, (VkSurfaceKHR*)out_vk_surface);
1266 return ret ? 0 : 1; // ret ? VK_SUCCESS : VK_NOT_READY
1267}
1268
1269static void ImGui_ImplSDL3_InitMultiViewportSupport(SDL_Window* window, void* sdl_gl_context)
1270{
1271 // Register platform interface (will be coupled with a renderer interface)
1272 ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO();
1273 platform_io.Platform_CreateWindow = ImGui_ImplSDL3_CreateWindow;
1274 platform_io.Platform_DestroyWindow = ImGui_ImplSDL3_DestroyWindow;
1275 platform_io.Platform_ShowWindow = ImGui_ImplSDL3_ShowWindow;
1276 platform_io.Platform_UpdateWindow = ImGui_ImplSDL3_UpdateWindow;
1277 platform_io.Platform_SetWindowPos = ImGui_ImplSDL3_SetWindowPos;
1278 platform_io.Platform_GetWindowPos = ImGui_ImplSDL3_GetWindowPos;
1279 platform_io.Platform_SetWindowSize = ImGui_ImplSDL3_SetWindowSize;
1280 platform_io.Platform_GetWindowSize = ImGui_ImplSDL3_GetWindowSize;
1281 platform_io.Platform_GetWindowFramebufferScale = ImGui_ImplSDL3_GetWindowFramebufferScale;
1282 platform_io.Platform_SetWindowFocus = ImGui_ImplSDL3_SetWindowFocus;
1283 platform_io.Platform_GetWindowFocus = ImGui_ImplSDL3_GetWindowFocus;
1284 platform_io.Platform_GetWindowMinimized = ImGui_ImplSDL3_GetWindowMinimized;
1285 platform_io.Platform_SetWindowTitle = ImGui_ImplSDL3_SetWindowTitle;
1286 platform_io.Platform_RenderWindow = ImGui_ImplSDL3_RenderWindow;
1287 platform_io.Platform_SwapBuffers = ImGui_ImplSDL3_SwapBuffers;
1288 platform_io.Platform_SetWindowAlpha = ImGui_ImplSDL3_SetWindowAlpha;
1289 platform_io.Platform_CreateVkSurface = ImGui_ImplSDL3_CreateVkSurface;
1290
1291 // Register main window handle (which is owned by the main application, not by us)
1292 // 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.
1293 ImGuiViewport* main_viewport = ImGui::GetMainViewport();
1294 ImGui_ImplSDL3_ViewportData* vd = IM_NEW(ImGui_ImplSDL3_ViewportData)();
1295 vd->Window = window;
1296 vd->WindowID = SDL_GetWindowID(window);
1297 vd->WindowOwned = false;
1298 vd->GLContext = (SDL_GLContext)sdl_gl_context;
1299 main_viewport->PlatformUserData = vd;
1300 main_viewport->PlatformHandle = (void*)(intptr_t)vd->WindowID;
1301}
1302
1303static void ImGui_ImplSDL3_ShutdownMultiViewportSupport()
1304{
1305 ImGui::DestroyPlatformWindows();
1306}
1307
1308//-----------------------------------------------------------------------------
1309
1310#if defined(__clang__)
1311#pragma clang diagnostic pop
1312#endif
1313
1314#endif // #ifndef IMGUI_DISABLE