OXYGENHUB
◆ main openmsx / src / 3rdparty / imgui / imgui_impl_sdl2.cc
70.5 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
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
157
static const Uint32 SDL_WINDOW_VULKAN = 0x10000000;
158
#endif
159
160
// SDL Data
161
struct 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.
194
static ImGui_ImplSDL2_Data* ImGui_ImplSDL2_GetBackendData()
195
{
196
    return ImGui::GetCurrentContext() ? (ImGui_ImplSDL2_Data*)ImGui::GetIO().BackendPlatformUserData : nullptr;
197
}
198
199
// Forward Declarations
200
static void ImGui_ImplSDL2_UpdateMonitors();
201
static void ImGui_ImplSDL2_InitMultiViewportSupport(SDL_Window* window, void* sdl_gl_context);
202
static void ImGui_ImplSDL2_ShutdownMultiViewportSupport();
203
204
// Functions
205
static 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
217
static 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().
223
static 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)
237
ImGuiKey ImGui_ImplSDL2_KeyEventToImGuiKey(SDL_Keycode keycode, SDL_Scancode scancode);
238
ImGuiKey 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
384
static 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
393
static 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.
402
bool 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__
539
EM_JS(void, ImGui_ImplSDL2_EmscriptenOpenURL, (char const* url), { url = url ? UTF8ToString(url) : null; if (url) window.open(url, '_blank'); });
540
#endif
541
542
static 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
670
bool ImGui_ImplSDL2_InitForOpenGL(SDL_Window* window, void* sdl_gl_context)
671
{
672
    return ImGui_ImplSDL2_Init(window, nullptr, sdl_gl_context);
673
}
674
675
bool 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
687
bool 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
695
bool ImGui_ImplSDL2_InitForMetal(SDL_Window* window)
696
{
697
    return ImGui_ImplSDL2_Init(window, nullptr, nullptr);
698
}
699
700
bool ImGui_ImplSDL2_InitForSDLRenderer(SDL_Window* window, SDL_Renderer* renderer)
701
{
702
    return ImGui_ImplSDL2_Init(window, renderer, nullptr);
703
}
704
705
bool ImGui_ImplSDL2_InitForOther(SDL_Window* window)
706
{
707
    return ImGui_ImplSDL2_Init(window, nullptr, nullptr);
708
}
709
710
static void ImGui_ImplSDL2_CloseGamepads();
711
712
void 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
733
void 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.
742
static 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
821
static 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.
850
float 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.
856
float 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
876
static 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
885
void 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
903
static 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
911
static inline float Saturate(float v) { return v < 0.0f ? 0.0f : v  > 1.0f ? 1.0f : v; }
912
static 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
924
static 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).
979
static 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
1010
static 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
1031
void 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.
1084
struct 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
1095
static 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
1151
static 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
1166
static 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
1196
static 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
1204
static 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
1210
static 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
1218
static 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
1224
static 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
1233
static 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
1240
static 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
1247
static void ImGui_ImplSDL2_SetWindowFocus(ImGuiViewport* viewport)
1248
{
1249
    ImGui_ImplSDL2_ViewportData* vd = (ImGui_ImplSDL2_ViewportData*)viewport->PlatformUserData;
1250
    SDL_RaiseWindow(vd->Window);
1251
}
1252
1253
static 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
1259
static 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
1265
static 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
1272
static 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>
1286
static 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
1295
static 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
1332
static 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