diff --git a/Program.cs b/Program.cs index e21c550..f3c3fe2 100644 --- a/Program.cs +++ b/Program.cs @@ -18,7 +18,7 @@ public class Program public static void Main() { SDL3Window window = new SDL3Window("SDL3 Testing Suite", 100, 100, 1280, 720); - window.RenderCallback = () => + window.RenderCallback += () => { ImGuiViewportPtr viewport = ImGui.GetMainViewport(); ImGui.SetNextWindowPos(viewport.WorkPos); @@ -65,7 +65,7 @@ public class Program fontName = string.IsNullOrEmpty(fontName) ? _font.FontId.ToString() : fontName; if (!_initialized || change) { - if (!GlyphsByName.TryGetValue(fontName, out List glyphs)) + if (!GlyphsByName.TryGetValue(fontName, out List? glyphs)) { glyphs = new List(); diff --git a/SDL3/DelegateHelpers.cs b/SDL3/DelegateHelpers.cs new file mode 100644 index 0000000..01a971a --- /dev/null +++ b/SDL3/DelegateHelpers.cs @@ -0,0 +1,8 @@ +using System.Runtime.InteropServices; + +namespace SDL3_TestingSuite.SDL3; + +public unsafe static class DelegateHelpers +{ + public static void* GetPtrForDelegate(this TDelegate _delegate) where TDelegate : notnull => (void*)Marshal.GetFunctionPointerForDelegate(_delegate); +} \ No newline at end of file diff --git a/SDL3/FontFind.cs b/SDL3/FontFind.cs index d02567c..15c00b8 100644 --- a/SDL3/FontFind.cs +++ b/SDL3/FontFind.cs @@ -51,7 +51,7 @@ public static class FontFind bool flag = false; ImVector fonts = io.Fonts.Fonts; List toRemove = new List(); - + for (int i = 0; i < fonts.Size; i++) { ImFontPtr font = fonts[i]; @@ -61,13 +61,13 @@ public static class FontFind flag = true; } } - + foreach (ImFontPtr font in toRemove) { io.Fonts.RemoveFont(font); Program.Logger.Log("Removed font: " + fontName); } - + return flag; } } @@ -110,11 +110,9 @@ public static class FontFind metrics.WinDescent = ReadS16Be(os2Offset + 76); } - metrics.TypoLineHeight = - metrics.TypoAscender - metrics.TypoDescender + metrics.TypoLineGap; + metrics.TypoLineHeight = metrics.TypoAscender - metrics.TypoDescender + metrics.TypoLineGap; - metrics.WinLineHeight = - metrics.WinAscent + metrics.WinDescent; + metrics.WinLineHeight = metrics.WinAscent + metrics.WinDescent; return metrics; @@ -142,7 +140,7 @@ public static class FontFind return new string(new char[] { c1, c2, c3, c4 }); } } - + public static int GetRecommendedPixelSize(FontMetrics metrics) { if (metrics.UnitsPerEm == 0 || metrics.TypoLineHeight == 0) diff --git a/SDL3/ImGuiSDL3Platform.cs b/SDL3/ImGuiSDL3Platform.cs index 1234d52..aa8035f 100644 --- a/SDL3/ImGuiSDL3Platform.cs +++ b/SDL3/ImGuiSDL3Platform.cs @@ -57,43 +57,11 @@ public unsafe static class ImGuiSDL3Platform } } - public class ViewPortData - { - public nint Window; - public nint Renderer; - public nint ParentWindow; - public uint WindowID; // Stored in ImGuiViewport.PlatformHandle. Use SDL.GetWindowFromID() to get SDL.Window* from Uint32 WindowID. - public bool WindowOwned; - public bool RendererOwned; - } + public static PlatformData GetPlatformData() => ImGui.GetCurrentContext().Handle != null ? ImGuiUserData.Get(ImGui.GetIO().BackendPlatformUserData)! : null!; - public static SDL3Window Parent; + public static SDL3Window? Parent; - // ReSharper disable NotAccessedField.Local - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - private delegate nint PlatformGetClipboardTextFn(nint ctx); - - private static readonly PlatformGetClipboardTextFn GetClipboardDelegate = GetClipboardText; - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - private delegate void PlatformSetClipboardTextFn(nint ctx, nint text); - - private static readonly PlatformSetClipboardTextFn SetClipboardDelegate = SetClipboardText; - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - private delegate void PlatformSetImeDataFn(nint ctx, nint userData, nint imeData); - - private static readonly PlatformSetImeDataFn SetImeDataDelegate = SetImeData; - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - private delegate bool PlatformOpenInShellFn(nint ctx, nint url); - - private static readonly PlatformOpenInShellFn OpenInShellDelegate = OpenInShell; - // ReSharper restore NotAccessedField.Local - - public static PlatformData Data => ImGui.GetCurrentContext().Handle != null ? ImGuiUserData.Get(ImGui.GetIO().BackendPlatformUserData)! : null!; - - public static bool Init(nint window, nint renderer, SDL3Window parentWindow = null) + public static bool Init(nint window, nint renderer, SDL3Window? parentWindow = null) { Parent = parentWindow; ImGuiIOPtr io = ImGui.GetIO(); @@ -112,8 +80,8 @@ public unsafe static class ImGuiSDL3Platform data.MouseCaptureMode = MouseCaptureMode.Disabled; string? backend = SDL.GetCurrentVideoDriver(); - string[] captureAndGlobalStateWhitelist = new string[] { "windows", "cocoa", "x11", "DIVE", "VMAN", "wayland" }; - foreach (var item in captureAndGlobalStateWhitelist) + string[] captureAndGlobalStateWhitelist = new string[] { "windows", "cocoa", "x11", "DIVE", "VMAN" }; + foreach (string item in captureAndGlobalStateWhitelist) { if (item != backend) continue; data.MouseCanUseGlobalState = true; @@ -131,10 +99,10 @@ public unsafe static class ImGuiSDL3Platform #endif ImGuiPlatformIOPtr platformIo = ImGui.GetPlatformIO(); - platformIo.PlatformGetClipboardTextFn = (void*)Marshal.GetFunctionPointerForDelegate(GetClipboardDelegate); - platformIo.PlatformSetClipboardTextFn = (void*)Marshal.GetFunctionPointerForDelegate(SetClipboardDelegate); - platformIo.PlatformSetImeDataFn = (void*)Marshal.GetFunctionPointerForDelegate(SetImeDataDelegate); - platformIo.PlatformOpenInShellFn = (void*)Marshal.GetFunctionPointerForDelegate(OpenInShellDelegate); + platformIo.PlatformGetClipboardTextFn = DelegateStorage.GetClipboardDelegate.GetPtrForDelegate(); + platformIo.PlatformSetClipboardTextFn = DelegateStorage.SetClipboardDelegate.GetPtrForDelegate(); + platformIo.PlatformSetImeDataFn = DelegateStorage.SetImeDataDelegate.GetPtrForDelegate(); + platformIo.PlatformOpenInShellFn = DelegateStorage.OpenInShellDelegate.GetPtrForDelegate(); UpdateMonitors(); @@ -165,25 +133,10 @@ public unsafe static class ImGuiSDL3Platform return true; } - private static void GetWindowSizeAndFramebufferScale(nint window, out Vector2 out_size, out Vector2 out_framebuffer_scale) - { - SDL.GetWindowSize(window, out int w, out int h); - if ((SDL.GetWindowFlags(window) & SDL.WindowFlags.Minimized) != 0) - w = h = 0; - - SDL.GetWindowSizeInPixels(window, out int displayW, out int displayH); - float fbScaleX = w > 0 ? displayW / (float)w : 1.0f; - float fbScaleY = h > 0 ? displayH / (float)h : 1.0f; - - out_size = new Vector2(w, h); - out_framebuffer_scale = new Vector2(fbScaleX, fbScaleY); - } - - public static void NewFrame() { ImGuiIOPtr io = ImGui.GetIO(); - PlatformData data = Data; + PlatformData data = GetPlatformData(); GetWindowSizeAndFramebufferScale(data.Window, out Vector2 size, out Vector2 framebufferScale); @@ -225,10 +178,53 @@ public unsafe static class ImGuiSDL3Platform UpdateGamepads(); } + public static void UpdateMonitors() + { + PlatformData bd = GetPlatformData(); + ImGuiPlatformIOPtr platformio = ImGui.GetPlatformIO(); + platformio.Monitors.Resize(0); + bd.WantUpdateMonitors = false; + + uint[] displays = SDL.GetDisplays(out int displayCount)!; + for (int n = 0; n < displayCount; n++) + { + // 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. + uint displayID = displays[n]; + ImGuiPlatformMonitor monitor = new ImGuiPlatformMonitor(); + SDL.GetDisplayBounds(displayID, out SDL.Rect r); + monitor.MainPos = monitor.WorkPos = new Vector2(r.X, r.Y); + monitor.MainSize = monitor.WorkSize = new Vector2(r.W, r.H); + if (SDL.GetDisplayUsableBounds(displayID, out SDL.Rect r2) && r2.W > 0 && r2.H > 0) + { + monitor.WorkPos = new Vector2(r2.X, r2.Y); + monitor.WorkSize = new Vector2(r2.W, r2.H); + } + monitor.DpiScale = SDL.GetDisplayContentScale(displayID); // See https://wiki.libsdl.org/SDL3/README-highdpi for details. + monitor.PlatformHandle = (void*)n; + if (monitor.DpiScale <= 0.0f) + continue; // Some accessibility applications are declaring virtual monitors with a DPI of 0, see #7902. + platformio.Monitors.PushBack(monitor); + } + } + + private static void GetWindowSizeAndFramebufferScale(nint window, out Vector2 out_size, out Vector2 out_framebuffer_scale) + { + SDL.GetWindowSize(window, out int w, out int h); + if (SDL.GetWindowFlags(window).HasFlag(SDL.WindowFlags.Minimized)) + w = h = 0; + + SDL.GetWindowSizeInPixels(window, out int displayW, out int displayH); + float fbScaleX = w > 0 ? displayW / (float)w : 1.0f; + float fbScaleY = h > 0 ? displayH / (float)h : 1.0f; + + out_size = new Vector2(w, h); + out_framebuffer_scale = new Vector2(fbScaleX, fbScaleY); + } + public static bool ProcessEvent(SDL.Event e) { ImGuiIOPtr io = ImGui.GetIO(); - PlatformData data = Data; + PlatformData data = GetPlatformData(); switch ((SDL.EventType)e.Type) { @@ -316,9 +312,9 @@ public unsafe static class ImGuiSDL3Platform } // This code is rather messy because some of the functions we need for full viewport support are not available in SDL < 2.0.4. - private static void UpdateMouseData() + internal static void UpdateMouseData() { - var bd = Data; + PlatformData bd = GetPlatformData(); ImGuiIOPtr io = ImGui.GetIO(); // We forward mouse input when hovered or captured (via SDL.MOUSEMOTION) or when focused (below) @@ -326,7 +322,7 @@ public unsafe static class ImGuiSDL3Platform // - 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. if (bd.MouseCaptureMode == MouseCaptureMode.Enabled) { - SDL.CaptureMouse((bd.MouseButtonsDown != 0) ? true : false); + SDL.CaptureMouse(bd.MouseButtonsDown != 0 ? true : false); } else if (bd.MouseCaptureMode == MouseCaptureMode.EnabledAfterDrag) { @@ -340,14 +336,14 @@ public unsafe static class ImGuiSDL3Platform } nint focusedWindow = SDL.GetKeyboardFocus(); - bool isAppFocused = (focusedWindow != nint.Zero && (bd.Window == focusedWindow || GetViewportForWindowId(SDL.GetWindowID(focusedWindow)) != null)); + bool isAppFocused = focusedWindow != nint.Zero && (bd.Window == focusedWindow || GetViewportForWindowId(SDL.GetWindowID(focusedWindow)) != null); if (isAppFocused) { // (Optional) Set OS mouse position from Dear ImGui if requested (rarely used, only when io.ConfigNavMoveSetMousePos is enabled by user) if (io.WantSetMousePos) { - if ((io.ConfigFlags.HasFlag(ImGuiConfigFlags.ViewportsEnable))) + if (io.ConfigFlags.HasFlag(ImGuiConfigFlags.ViewportsEnable)) SDL.WarpMouseGlobal((int)io.MousePos.X, (int)io.MousePos.Y); else SDL.WarpMouseInWindow(bd.Window, (int)io.MousePos.X, (int)io.MousePos.Y); @@ -379,23 +375,23 @@ public unsafe static class ImGuiSDL3Platform // 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 // by the backend, and use its flawed heuristic to guess the viewport behind. // - [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). - if ((io.BackendFlags.HasFlag(ImGuiBackendFlags.HasMouseHoveredViewport))) + if (io.BackendFlags.HasFlag(ImGuiBackendFlags.HasMouseHoveredViewport)) { uint mouseViewportID = 0; - var mouseViewport = GetViewportForWindowId(bd.MouseWindowID); + ImGuiViewportPtr? mouseViewport = GetViewportForWindowId(bd.MouseWindowID); if (mouseViewport != null) mouseViewportID = mouseViewport.Value.ID; io.AddMouseViewportEvent(mouseViewportID); } } - private static void UpdateMouseCursor() + internal static void UpdateMouseCursor() { ImGuiIOPtr io = ImGui.GetIO(); - if ((io.ConfigFlags & ImGuiConfigFlags.NoMouseCursorChange) != 0) + if (io.ConfigFlags.HasFlag(ImGuiConfigFlags.NoMouseCursorChange)) return; - var bd = Data; + PlatformData bd = GetPlatformData(); ImGuiMouseCursor imguiCursor = ImGui.GetMouseCursor(); if (io.MouseDrawCursor || imguiCursor == ImGuiMouseCursor.None) @@ -416,94 +412,16 @@ public unsafe static class ImGuiSDL3Platform } } - private static void CloseGamepads() + internal static void UpdateGamepads() { - PlatformData bd = Data; - - if (bd.GamepadMode != GamepadMode.Manual) - { - for (int i = 0; i < bd.GamepadCount; i++) - { - SDL.CloseGamepad(bd.Gamepads[i]); - bd.Gamepads[i] = nint.Zero; - } - } - - bd.GamepadCount = 0; - } - - private static void SetGamepadMode(GamepadMode mode, nint[] manual_gamepads_array, int manual_gamepads_count) - { - PlatformData bd = Data; - - CloseGamepads(); - - if (mode == GamepadMode.Manual) - { - int count = manual_gamepads_count; - - if (count > bd.Gamepads.Length) - count = bd.Gamepads.Length; - - for (int n = 0; n < count; n++) - bd.Gamepads[n] = manual_gamepads_array[n]; - - bd.GamepadCount = count; - } - else - { - bd.WantUpdateGamepadsList = true; - } - - bd.GamepadMode = mode; - } - - private static void UpdateGamepadButton(PlatformData bd, ImGuiIOPtr io, ImGuiKey key, SDL.GamepadButton button_no) - { - bool mergedValue = false; - - for (int i = 0; i < bd.GamepadCount; i++) - { - nint gamepad = bd.Gamepads[i]; - mergedValue |= SDL.GetGamepadButton(gamepad, button_no); - } - - io.AddKeyEvent(key, mergedValue); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static float Saturate(float v) - { - return v < 0.0f ? 0.0f : v > 1.0f ? 1.0f : v; - } - private static void UpdateGamepadAnalog(PlatformData bd, ImGuiIOPtr io, ImGuiKey key, SDL.GamepadAxis axis_no, float v0, float v1) - { - float mergedValue = 0.0f; - - for (int i = 0; i < bd.GamepadCount; i++) - { - nint gamepad = bd.Gamepads[i]; - - float raw = SDL.GetGamepadAxis(gamepad, axis_no); - float vn = Saturate((raw - v0) / (v1 - v0)); - - if (mergedValue < vn) - mergedValue = vn; - } - - io.AddKeyAnalogEvent(key, mergedValue > 0.1f, mergedValue); - } - - private static void UpdateGamepads() - { - PlatformData data = Data; + PlatformData data = GetPlatformData(); ImGuiIOPtr io = ImGui.GetIO(); if (data.WantUpdateGamepadsList && data.GamepadMode != GamepadMode.Manual) { CloseGamepads(); - uint[] sdlGamepads = SDL.GetGamepads(out int count); + uint[] sdlGamepads = SDL.GetGamepads(out int count)!; for (int n = 0; n < count; n++) { @@ -558,33 +476,82 @@ public unsafe static class ImGuiSDL3Platform UpdateGamepadAnalog(data, io, ImGuiKey.GamepadRStickDown, SDL.GamepadAxis.RightY, thumbDeadZone, 32767); } - public static void UpdateMonitors() + private static void UpdateGamepadButton(PlatformData bd, ImGuiIOPtr io, ImGuiKey key, SDL.GamepadButton button_no) { - PlatformData bd = Data; - ImGuiPlatformIOPtr platformio = ImGui.GetPlatformIO(); - platformio.Monitors.Resize(0); - bd.WantUpdateMonitors = false; + bool mergedValue = false; - var displays = SDL.GetDisplays(out int displayCount); - for (int n = 0; n < displayCount; n++) + for (int i = 0; i < bd.GamepadCount; i++) { - // 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. - var displayID = displays[n]; - ImGuiPlatformMonitor monitor = new ImGuiPlatformMonitor(); - SDL.GetDisplayBounds(displayID, out SDL.Rect r); - monitor.MainPos = monitor.WorkPos = new Vector2(r.X, r.Y); - monitor.MainSize = monitor.WorkSize = new Vector2(r.W, r.H); - if (SDL.GetDisplayUsableBounds(displayID, out SDL.Rect r2) && r2.W > 0 && r2.H > 0) - { - monitor.WorkPos = new Vector2(r2.X, r2.Y); - monitor.WorkSize = new Vector2(r2.W, r2.H); - } - monitor.DpiScale = SDL.GetDisplayContentScale(displayID); // See https://wiki.libsdl.org/SDL3/README-highdpi for details. - monitor.PlatformHandle = (void*)n; - if (monitor.DpiScale <= 0.0f) - continue; // Some accessibility applications are declaring virtual monitors with a DPI of 0, see #7902. - platformio.Monitors.PushBack(monitor); + nint gamepad = bd.Gamepads[i]; + mergedValue |= SDL.GetGamepadButton(gamepad, button_no); } + + io.AddKeyEvent(key, mergedValue); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static float Saturate(float v) + { + return v < 0.0f ? 0.0f : v > 1.0f ? 1.0f : v; + } + private static void UpdateGamepadAnalog(PlatformData bd, ImGuiIOPtr io, ImGuiKey key, SDL.GamepadAxis axis_no, float v0, float v1) + { + float mergedValue = 0.0f; + + for (int i = 0; i < bd.GamepadCount; i++) + { + nint gamepad = bd.Gamepads[i]; + + float raw = SDL.GetGamepadAxis(gamepad, axis_no); + float vn = Saturate((raw - v0) / (v1 - v0)); + + if (mergedValue < vn) + mergedValue = vn; + } + + io.AddKeyAnalogEvent(key, mergedValue > 0.1f, mergedValue); + } + + internal static void SetGamepadMode(GamepadMode mode, nint[] manual_gamepads_array, int manual_gamepads_count) + { + PlatformData bd = GetPlatformData(); + + CloseGamepads(); + + if (mode == GamepadMode.Manual) + { + int count = manual_gamepads_count; + + if (count > bd.Gamepads.Length) + count = bd.Gamepads.Length; + + for (int n = 0; n < count; n++) + bd.Gamepads[n] = manual_gamepads_array[n]; + + bd.GamepadCount = count; + } + else + { + bd.WantUpdateGamepadsList = true; + } + + bd.GamepadMode = mode; + } + + internal static void CloseGamepads() + { + PlatformData bd = GetPlatformData(); + + if (bd.GamepadMode != GamepadMode.Manual) + { + for (int i = 0; i < bd.GamepadCount; i++) + { + SDL.CloseGamepad(bd.Gamepads[i]); + bd.Gamepads[i] = nint.Zero; + } + } + + bd.GamepadCount = 0; } private static ImGuiKey KeyEventToImGui(SDL.Keycode keycode, SDL.Scancode scancode) @@ -718,34 +685,10 @@ public unsafe static class ImGuiSDL3Platform } - public static nint GetClipboardText(nint ctx) - { - PlatformData data = Data; - - string text = SDL.GetClipboardText(); - data.ClipboardTextData = Marshal.StringToHGlobalAnsi(text ?? string.Empty); - return data.ClipboardTextData; - } - - public static void SetClipboardText(nint ctx, nint text) - { - string? managedText = Marshal.PtrToStringAnsi(text); - if (managedText != null) - { - SDL.SetClipboardText(managedText); - } - } - - private static void SetImeData(nint ctx, nint userData, nint imeData) - { - PlatformData data = Data; - data.ImeData = Marshal.PtrToStructure(imeData); - data.ImeDirty = true; - } public static void UpdateIme() { - PlatformData data = Data; + PlatformData data = GetPlatformData(); ImGuiPlatformImeData imeData = data.ImeData; nint window = SDL.GetKeyboardFocus(); @@ -776,21 +719,6 @@ public unsafe static class ImGuiSDL3Platform SDL.StartTextInput(window); } - private static bool OpenInShell(nint ctx, nint url) - { - string managedUrl = Marshal.PtrToStringUTF8(url) ?? string.Empty; - return SDL.OpenURL(managedUrl); - } - - // !! This is sorta fucked - // public static void UpdateKeyModifiers(SDL.Keymod keymods) - // { - // ImGuiIOPtr io = ImGui.GetIO(); - // io.KeyCtrl = keymods.HasFlag(SDL.Keymod.LCtrl) || keymods.HasFlag(SDL.Keymod.RCtrl); - // io.KeyShift = keymods.HasFlag(SDL.Keymod.LShift) || keymods.HasFlag(SDL.Keymod.RShift); - // io.KeyAlt = keymods.HasFlag(SDL.Keymod.LAlt) || keymods.HasFlag(SDL.Keymod.RAlt); - // io.KeySuper = keymods.HasFlag(SDL.Keymod.LGUI) || keymods.HasFlag(SDL.Keymod.RGUI); - // } private static void UpdateKeyModifiers(SDL.Keymod mod) { @@ -801,281 +729,45 @@ public unsafe static class ImGuiSDL3Platform io.AddKeyEvent(ImGuiKey.ModSuper, (mod & SDL.Keymod.GUI) != 0); } - private static void CreateWindow(ImGuiViewportPtr viewport) + + public class ImGui_ImplSDL3_ViewportData : ViewPortData { } + + public class ViewPortData { - PlatformData bd = Data; - - ViewPortData vd = new ViewPortData(); - viewport.PlatformUserData = ImGuiUserData.Store(vd); - - SDL.WindowFlags flags = SDL.WindowFlags.Hidden; - - flags |= SDL.GetWindowFlags(bd.Window) & SDL.WindowFlags.HighPixelDensity; - - if ((viewport.Flags & ImGuiViewportFlags.NoDecoration) != 0) - { - flags |= SDL.WindowFlags.Borderless; - } - else - { - flags |= SDL.WindowFlags.Resizable; - } - - if ((viewport.Flags & ImGuiViewportFlags.NoTaskBarIcon) != 0) - { - flags |= SDL.WindowFlags.Utility; - } - - if ((viewport.Flags & ImGuiViewportFlags.TopMost) != 0) - { - flags |= SDL.WindowFlags.AlwaysOnTop; - } - - vd.Window = SDL.CreateWindow("Untitled", (int)viewport.Size.X, (int)viewport.Size.Y, flags | SDL.WindowFlags.Transparent); - - ImGuiViewportPtr? parentViewport = viewport.ParentViewportId != 0 ? ImGui.FindViewportByID(viewport.ParentViewportId) : null; - if (parentViewport != null && parentViewport.Value.Handle != null) - { - vd.ParentWindow = GetSDLWindowFromViewport(parentViewport.Value); - if (vd.ParentWindow != nint.Zero) - SDL.SetWindowParent(vd.Window, vd.ParentWindow); - } - - SDL.SetWindowPosition(vd.Window, (int)viewport.Pos.X, (int)viewport.Pos.Y); - vd.WindowOwned = true; - - SetupPlatformHandles(viewport, vd.Window); - - Program.Logger.Log($"SDL Window: {vd.Window} {vd.WindowID} {viewport.Pos.X} {viewport.Pos.Y} {viewport.Size.X} {viewport.Size.Y}"); - -#if WINDOWS - viewport.PlatformHandleRaw = SDL.GetPointerProperty( - SDL.GetWindowProperties(vd.Window), - SDL.Properties.WindowWin32HWNDPointer, - nint.Zero - ).ToPointer(); -#endif + public nint Window; + public nint Renderer; + public nint ParentWindow; + public uint WindowID; // Stored in ImGuiViewport.PlatformHandle. Use SDL.GetWindowFromID() to get SDL.Window* from Uint32 WindowID. + public bool WindowOwned; + public bool RendererOwned; } - private static void DestroyWindow(ImGuiViewportPtr viewport) - { - ViewPortData? vd = ImGuiUserData.Get(viewport.PlatformUserData); - - if (vd != null) - { - if (vd.Window != nint.Zero && vd.WindowOwned) - { - SDL.DestroyWindow(vd.Window); - } - } - - ImGuiUserData.Free(viewport.PlatformUserData); - - viewport.PlatformUserData = null; - viewport.PlatformHandle = null; - viewport.PlatformHandleRaw = null; - } - - private static void ShowWindow(ImGuiViewportPtr viewport) - { - ViewPortData vd = ImGuiUserData.Get(viewport.PlatformUserData)!; - - string? oldHint = SDL.GetHint(SDL.Hints.WindowActivateWhenShown); - - SDL.SetHint(SDL.Hints.WindowActivateWhenShown, ((viewport.Flags & ImGuiViewportFlags.NoFocusOnAppearing) != 0) ? "0" : "1"); - - SDL.ShowWindow(vd.Window); - - SDL.SetHint(SDL.Hints.WindowActivateWhenShown, oldHint); - } - - private static void UpdateWindow(ImGuiViewportPtr viewport) - { - ViewPortData vd = ImGuiUserData.Get(viewport.PlatformUserData)!; - - if ((viewport.Flags & ImGuiViewportFlags.TopMost) != 0) - { - SDL.SetWindowAlwaysOnTop(vd.Window, true); - } - else - { - SDL.SetWindowAlwaysOnTop(vd.Window, false); - } - } - - private static Vector2 GetWindowPos(ImGuiViewportPtr viewport) - { - ViewPortData vd = ImGuiUserData.Get(viewport.PlatformUserData)!; - - SDL.GetWindowPosition(vd.Window, out int x, out int y); - - return new Vector2(x, y); - } - - private static void SetWindowPos(ImGuiViewportPtr viewport, Vector2 pos) - { - ViewPortData vd = ImGuiUserData.Get(viewport.PlatformUserData)!; - - SDL.SetWindowPosition(vd.Window, (int)pos.X, (int)pos.Y); - } - - private static Vector2 GetWindowSize(ImGuiViewportPtr viewport) - { - ViewPortData vd = ImGuiUserData.Get(viewport.PlatformUserData)!; - - SDL.GetWindowSize(vd.Window, out int w, out int h); - - return new Vector2(w, h); - } - - private static void SetWindowSize(ImGuiViewportPtr viewport, Vector2 size) - { - ViewPortData vd = ImGuiUserData.Get(viewport.PlatformUserData)!; - - SDL.SetWindowSize(vd.Window, (int)size.X, (int)size.Y); - } - - private static Vector2 GetWindowFramebufferScale(ImGuiViewportPtr viewport) - { - ViewPortData vd = ImGuiUserData.Get(viewport.PlatformUserData)!; - - GetWindowSizeAndFramebufferScale(vd.Window, out Vector2 framebufferSize, out Vector2 framebufferScale); - - return framebufferScale; - } - - private static void SetWindowTitle(ImGuiViewportPtr viewport, string title) - { - ViewPortData vd = ImGuiUserData.Get(viewport.PlatformUserData)!; - - SDL.SetWindowTitle(vd.Window, title); - } - - private static void SetWindowAlpha(ImGuiViewportPtr viewport, float alpha) - { - ViewPortData vd = ImGuiUserData.Get(viewport.PlatformUserData)!; - - SDL.SetWindowOpacity(vd.Window, alpha); - } - - private static void SetWindowFocus(ImGuiViewportPtr viewport) - { - ViewPortData vd = ImGuiUserData.Get(viewport.PlatformUserData)!; - - SDL.RaiseWindow(vd.Window); - } - - private static bool GetWindowFocus(ImGuiViewportPtr viewport) - { - ViewPortData vd = ImGuiUserData.Get(viewport.PlatformUserData)!; - - return (SDL.GetWindowFlags(vd.Window) & SDL.WindowFlags.InputFocus) != 0; - } - - private static bool GetWindowMinimized(ImGuiViewportPtr viewport) - { - ViewPortData vd = ImGuiUserData.Get(viewport.PlatformUserData)!; - - return (SDL.GetWindowFlags(vd.Window) & SDL.WindowFlags.Minimized) != 0; - } - - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - private delegate void CreateWindowDelegatelFn(ImGuiViewportPtr ctx); - - private static readonly CreateWindowDelegatelFn CreateWindowDelegate = CreateWindow; - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - private delegate void DestroyWindowDelegatelFn(ImGuiViewportPtr ctx); - - private static readonly DestroyWindowDelegatelFn DestroyWindowDelegate = DestroyWindow; - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - private delegate void ShowWindowDelegatelFn(ImGuiViewportPtr ctx); - - private static readonly ShowWindowDelegatelFn ShowWindowDelegate = ShowWindow; - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - private delegate void UpdateWindowDelegatelFn(ImGuiViewportPtr ctx); - - private static readonly UpdateWindowDelegatelFn UpdateWindowDelegate = UpdateWindow; - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - private delegate void SetWindowPosDelegatelFn(ImGuiViewportPtr ctx, Vector2 pos); - - private static readonly SetWindowPosDelegatelFn SetWindowPosDelegate = SetWindowPos; - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - private delegate Vector2 GetWindowPosDelegatelFn(ImGuiViewportPtr ctx); - - private static readonly GetWindowPosDelegatelFn GetWindowPosDelegate = GetWindowPos; - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - private delegate void SetWindowSizeDelegatelFn(ImGuiViewportPtr ctx, Vector2 size); - - private static readonly SetWindowSizeDelegatelFn SetWindowSizeDelegate = SetWindowSize; - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - private delegate Vector2 GetWindowSizeDelegateFn(ImGuiViewportPtr ctx); - - private static readonly GetWindowSizeDelegateFn GetWindowSizeDelegate = GetWindowSize; - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - private delegate Vector2 GetWindowFramebufferScaleDelegatelFn(ImGuiViewportPtr ctx); - - private static readonly GetWindowFramebufferScaleDelegatelFn GetWindowFramebufferScaleDelegate = GetWindowFramebufferScale; - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - private delegate void SetWindowFocusDelegatelFn(ImGuiViewportPtr ctx); - - private static readonly SetWindowFocusDelegatelFn SetWindowFocusDelegate = SetWindowFocus; - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - private delegate bool GetWindowFocusDelegatelFn(ImGuiViewportPtr ctx); - - private static readonly GetWindowFocusDelegatelFn GetWindowFocusDelegate = GetWindowFocus; - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - private delegate bool GetWindowMinimizedDelegatelFn(ImGuiViewportPtr ctx); - - private static readonly GetWindowMinimizedDelegatelFn GetWindowMinimizedDelegate = GetWindowMinimized; - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - private delegate void SetWindowTitleDelegatelFn(ImGuiViewportPtr ctx, string title); - - private static readonly SetWindowTitleDelegatelFn SetWindowTitleDelegate = SetWindowTitle; - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - private delegate void SetWindowAlphaDelegatelFn(ImGuiViewportPtr ctx, float alpha); - - private static readonly SetWindowAlphaDelegatelFn SetWindowAlphaDelegate = SetWindowAlpha; - private static void InitMultiViewportSupport(nint window) { ImGuiPlatformIOPtr platformIO = ImGui.GetPlatformIO(); - platformIO.PlatformCreateWindow = (void*)Marshal.GetFunctionPointerForDelegate(CreateWindowDelegate); - platformIO.PlatformDestroyWindow = (void*)Marshal.GetFunctionPointerForDelegate(DestroyWindowDelegate); - platformIO.PlatformShowWindow = (void*)Marshal.GetFunctionPointerForDelegate(ShowWindowDelegate); - platformIO.PlatformSetWindowPos = (void*)Marshal.GetFunctionPointerForDelegate(SetWindowPosDelegate); - platformIO.PlatformGetWindowPos = (void*)Marshal.GetFunctionPointerForDelegate(GetWindowPosDelegate); - platformIO.PlatformSetWindowSize = (void*)Marshal.GetFunctionPointerForDelegate(SetWindowSizeDelegate); - platformIO.PlatformGetWindowSize = (void*)Marshal.GetFunctionPointerForDelegate(GetWindowSizeDelegate); - platformIO.PlatformSetWindowFocus = (void*)Marshal.GetFunctionPointerForDelegate(SetWindowFocusDelegate); - platformIO.PlatformGetWindowFocus = (void*)Marshal.GetFunctionPointerForDelegate(GetWindowFocusDelegate); - platformIO.PlatformGetWindowMinimized = (void*)Marshal.GetFunctionPointerForDelegate(GetWindowMinimizedDelegate); - platformIO.PlatformSetWindowTitle = (void*)Marshal.GetFunctionPointerForDelegate(SetWindowTitleDelegate); - platformIO.PlatformSetWindowAlpha = (void*)Marshal.GetFunctionPointerForDelegate(SetWindowAlphaDelegate); - platformIO.PlatformUpdateWindow = (void*)Marshal.GetFunctionPointerForDelegate(UpdateWindowDelegate); - platformIO.PlatformGetWindowFramebufferScale = (void*)Marshal.GetFunctionPointerForDelegate(GetWindowFramebufferScaleDelegate); + platformIO.PlatformCreateWindow = DelegateStorage.CreateWindowDelegate.GetPtrForDelegate(); + platformIO.PlatformDestroyWindow = DelegateStorage.DestroyWindowDelegate.GetPtrForDelegate(); + platformIO.PlatformShowWindow = DelegateStorage.ShowWindowDelegate.GetPtrForDelegate(); + platformIO.PlatformSetWindowPos = DelegateStorage.SetWindowPosDelegate.GetPtrForDelegate(); + platformIO.PlatformGetWindowPos = DelegateStorage.GetWindowPosDelegate.GetPtrForDelegate(); + platformIO.PlatformSetWindowSize = DelegateStorage.SetWindowSizeDelegate.GetPtrForDelegate(); + platformIO.PlatformGetWindowSize = DelegateStorage.GetWindowSizeDelegate.GetPtrForDelegate(); + platformIO.PlatformSetWindowFocus = DelegateStorage.SetWindowFocusDelegate.GetPtrForDelegate(); + platformIO.PlatformGetWindowFocus = DelegateStorage.GetWindowFocusDelegate.GetPtrForDelegate(); + platformIO.PlatformGetWindowMinimized = DelegateStorage.GetWindowMinimizedDelegate.GetPtrForDelegate(); + platformIO.PlatformSetWindowTitle = DelegateStorage.SetWindowTitleDelegate.GetPtrForDelegate(); + platformIO.PlatformSetWindowAlpha = DelegateStorage.SetWindowAlphaDelegate.GetPtrForDelegate(); + platformIO.PlatformUpdateWindow = DelegateStorage.UpdateWindowDelegate.GetPtrForDelegate(); + platformIO.PlatformGetWindowFramebufferScale = DelegateStorage.GetWindowFramebufferScaleDelegate.GetPtrForDelegate(); ImGuiViewportPtr mainViewport = ImGui.GetMainViewport(); ViewPortData vd = new ViewPortData { Window = window, - Renderer = Data.Renderer, + Renderer = GetPlatformData().Renderer, WindowID = SDL.GetWindowID(window), WindowOwned = false, RendererOwned = false @@ -1117,10 +809,15 @@ public unsafe static class ImGuiSDL3Platform public static void Dispose() { - PlatformData? data = Data; - + PlatformData? data = GetPlatformData(); if (data != null) { + if (data.ClipboardTextData != nint.Zero) + { + Marshal.FreeHGlobal(data.ClipboardTextData); + data.ClipboardTextData = nint.Zero; + } + foreach (nint cursor in data.MouseCursors) { if (cursor != nint.Zero) @@ -1137,6 +834,272 @@ public unsafe static class ImGuiSDL3Platform ImGuiUserData.Free(io.BackendPlatformUserData); io.BackendPlatformUserData = null; } + + // @formatter:off — disable formatter after this line + public static class DelegateStorage + { + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate nint PlatformGetClipboardTextFn(nint ctx); + internal static readonly PlatformGetClipboardTextFn GetClipboardDelegate = GetClipboardText; + private static nint GetClipboardText(nint ctx) + { + PlatformData data = GetPlatformData(); + + if (data.ClipboardTextData != nint.Zero) + { + Marshal.FreeHGlobal(data.ClipboardTextData); + data.ClipboardTextData = nint.Zero; + } + + string text = SDL.GetClipboardText(); + data.ClipboardTextData = Marshal.StringToHGlobalAnsi(text ?? string.Empty); + return data.ClipboardTextData; + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate void PlatformSetClipboardTextFn(nint ctx, nint text); + internal static readonly PlatformSetClipboardTextFn SetClipboardDelegate = SetClipboardText; + private static void SetClipboardText(nint ctx, nint text) + { + string? managedText = Marshal.PtrToStringAnsi(text); + if (managedText != null) + { + SDL.SetClipboardText(managedText); + } + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate void PlatformSetImeDataFn(nint ctx, nint userData, nint imeData); + internal static readonly PlatformSetImeDataFn SetImeDataDelegate = SetImeData; + private static void SetImeData(nint ctx, nint userData, nint imeData) + { + PlatformData data = GetPlatformData(); + data.ImeData = Marshal.PtrToStructure(imeData); + data.ImeDirty = true; + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate bool PlatformOpenInShellFn(nint ctx, nint url); + internal static readonly PlatformOpenInShellFn OpenInShellDelegate = OpenInShell; + private static bool OpenInShell(nint ctx, nint url) + { + string managedUrl = Marshal.PtrToStringUTF8(url) ?? string.Empty; + return SDL.OpenURL(managedUrl); + } + + // // // // // // // // // // // // // // + // Below is for Multi-Viewport Support // + // // + // // // // // // // // // // // // // // + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate void CreateWindowDelegateFn(ImGuiViewportPtr ctx); + internal static readonly CreateWindowDelegateFn CreateWindowDelegate = CreateWindow; + private static void CreateWindow(ImGuiViewportPtr viewport) + { + PlatformData bd = GetPlatformData(); + + ViewPortData vd = new ViewPortData(); + viewport.PlatformUserData = ImGuiUserData.Store(vd); + + SDL.WindowFlags flags = SDL.WindowFlags.Hidden; + + flags |= SDL.GetWindowFlags(bd.Window) & SDL.WindowFlags.HighPixelDensity; + + if (viewport.Flags.HasFlag(ImGuiViewportFlags.NoDecoration)) + flags |= SDL.WindowFlags.Borderless; + else + flags |= SDL.WindowFlags.Resizable; + if (viewport.Flags.HasFlag(ImGuiViewportFlags.NoTaskBarIcon)) + flags |= SDL.WindowFlags.Utility; + if (viewport.Flags.HasFlag(ImGuiViewportFlags.TopMost)) + flags |= SDL.WindowFlags.AlwaysOnTop; + + vd.Window = SDL.CreateWindow("Untitled", (int)viewport.Size.X, (int)viewport.Size.Y, flags); + + ImGuiViewportPtr? parentViewport = viewport.ParentViewportId != 0 ? ImGui.FindViewportByID(viewport.ParentViewportId) : null; + if (parentViewport != null && parentViewport.Value.Handle != null) + { + vd.ParentWindow = GetSDLWindowFromViewport(parentViewport.Value); + if (vd.ParentWindow != nint.Zero) + SDL.SetWindowParent(vd.Window, vd.ParentWindow); + } + + SDL.SetWindowPosition(vd.Window, (int)viewport.Pos.X, (int)viewport.Pos.Y); + vd.WindowOwned = true; + + SetupPlatformHandles(viewport, vd.Window); + + Program.Logger.Log($"New SDL Viewport: {vd.Window} {vd.WindowID} {viewport.Pos.X} {viewport.Pos.Y} {viewport.Size.X} {viewport.Size.Y}"); + + #if WINDOWS + viewport.PlatformHandleRaw = SDL.GetPointerProperty( + SDL.GetWindowProperties(vd.Window), + SDL.Properties.WindowWin32HWNDPointer, + nint.Zero + ).ToPointer(); + #endif + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate void DestroyWindowDelegateFn(ImGuiViewportPtr ctx); + internal static readonly DestroyWindowDelegateFn DestroyWindowDelegate = DestroyWindow; + private static void DestroyWindow(ImGuiViewportPtr viewport) + { + ViewPortData? vd = ImGuiUserData.Get(viewport.PlatformUserData); + + if (vd != null) + { + if (vd.Window != nint.Zero && vd.WindowOwned) + { + SDL.DestroyWindow(vd.Window); + } + } + + ImGuiUserData.Free(viewport.PlatformUserData); + + viewport.PlatformUserData = null; + viewport.PlatformHandle = null; + viewport.PlatformHandleRaw = null; + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate void ShowWindowDelegateFn(ImGuiViewportPtr ctx); + internal static readonly ShowWindowDelegateFn ShowWindowDelegate = ShowWindow; + private static void ShowWindow(ImGuiViewportPtr viewport) + { + ViewPortData vd = ImGuiUserData.Get(viewport.PlatformUserData)!; + + string? oldHint = SDL.GetHint(SDL.Hints.WindowActivateWhenShown); + + SDL.SetHint(SDL.Hints.WindowActivateWhenShown, (viewport.Flags & ImGuiViewportFlags.NoFocusOnAppearing) != 0 ? "0" : "1"); + + SDL.ShowWindow(vd.Window); + + SDL.SetHint(SDL.Hints.WindowActivateWhenShown, oldHint); + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate void UpdateWindowDelegateFn(ImGuiViewportPtr ctx); + internal static readonly UpdateWindowDelegateFn UpdateWindowDelegate = UpdateWindow; + private static void UpdateWindow(ImGuiViewportPtr viewport) + { + ViewPortData vd = ImGuiUserData.Get(viewport.PlatformUserData)!; + + if ((viewport.Flags & ImGuiViewportFlags.TopMost) != 0) + { + SDL.SetWindowAlwaysOnTop(vd.Window, true); + } + else + { + SDL.SetWindowAlwaysOnTop(vd.Window, false); + } + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate void SetWindowPosDelegateFn(ImGuiViewportPtr ctx, Vector2 pos); + internal static readonly SetWindowPosDelegateFn SetWindowPosDelegate = SetWindowPos; + private static void SetWindowPos(ImGuiViewportPtr viewport, Vector2 pos) + { + ViewPortData vd = ImGuiUserData.Get(viewport.PlatformUserData)!; + + SDL.SetWindowPosition(vd.Window, (int)pos.X, (int)pos.Y); + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate Vector2 GetWindowPosDelegateFn(ImGuiViewportPtr ctx); + internal static readonly GetWindowPosDelegateFn GetWindowPosDelegate = GetWindowPos; + private static Vector2 GetWindowPos(ImGuiViewportPtr viewport) + { + ViewPortData vd = ImGuiUserData.Get(viewport.PlatformUserData)!; + SDL.GetWindowPosition(vd.Window, out int x, out int y); + return new Vector2(x, y); + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate void SetWindowSizeDelegateFn(ImGuiViewportPtr ctx, Vector2 size); + internal static readonly SetWindowSizeDelegateFn SetWindowSizeDelegate = SetWindowSize; + private static void SetWindowSize(ImGuiViewportPtr viewport, Vector2 size) + { + ViewPortData vd = ImGuiUserData.Get(viewport.PlatformUserData)!; + + SDL.SetWindowSize(vd.Window, (int)size.X, (int)size.Y); + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate Vector2 GetWindowSizeDelegateFn(ImGuiViewportPtr ctx); + internal static readonly GetWindowSizeDelegateFn GetWindowSizeDelegate = GetWindowSize; + private static Vector2 GetWindowSize(ImGuiViewportPtr viewport) + { + ViewPortData vd = ImGuiUserData.Get(viewport.PlatformUserData)!; + + SDL.GetWindowSize(vd.Window, out int w, out int h); + + return new Vector2(w, h); + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate Vector2 GetWindowFramebufferScaleDelegateFn(ImGuiViewportPtr ctx); + internal static readonly GetWindowFramebufferScaleDelegateFn GetWindowFramebufferScaleDelegate = GetWindowFramebufferScale; + private static Vector2 GetWindowFramebufferScale(ImGuiViewportPtr viewport) + { + ViewPortData vd = ImGuiUserData.Get(viewport.PlatformUserData)!; + + GetWindowSizeAndFramebufferScale(vd.Window, out Vector2 _, out Vector2 framebufferScale); + + return framebufferScale; + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate void SetWindowFocusDelegateFn(ImGuiViewportPtr ctx); + internal static readonly SetWindowFocusDelegateFn SetWindowFocusDelegate = SetWindowFocus; + private static void SetWindowFocus(ImGuiViewportPtr viewport) + { + ViewPortData vd = ImGuiUserData.Get(viewport.PlatformUserData)!; + + SDL.RaiseWindow(vd.Window); + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate bool GetWindowFocusDelegateFn(ImGuiViewportPtr ctx); + internal static readonly GetWindowFocusDelegateFn GetWindowFocusDelegate = GetWindowFocus; + private static bool GetWindowFocus(ImGuiViewportPtr viewport) + { + ViewPortData vd = ImGuiUserData.Get(viewport.PlatformUserData)!; + + return (SDL.GetWindowFlags(vd.Window) & SDL.WindowFlags.InputFocus) != 0; + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate bool GetWindowMinimizedDelegateFn(ImGuiViewportPtr ctx); + internal static readonly GetWindowMinimizedDelegateFn GetWindowMinimizedDelegate = GetWindowMinimized; + private static bool GetWindowMinimized(ImGuiViewportPtr viewport) + { + ViewPortData vd = ImGuiUserData.Get(viewport.PlatformUserData)!; + + return (SDL.GetWindowFlags(vd.Window) & SDL.WindowFlags.Minimized) != 0; + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate void SetWindowTitleDelegateFn(ImGuiViewportPtr ctx, string title); + internal static readonly SetWindowTitleDelegateFn SetWindowTitleDelegate = SetWindowTitle; + private static void SetWindowTitle(ImGuiViewportPtr viewport, string title) + { + ViewPortData vd = ImGuiUserData.Get(viewport.PlatformUserData)!; + + SDL.SetWindowTitle(vd.Window, title); + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate void SetWindowAlphaDelegateFn(ImGuiViewportPtr ctx, float alpha); + internal static readonly SetWindowAlphaDelegateFn SetWindowAlphaDelegate = SetWindowAlpha; + private static void SetWindowAlpha(ImGuiViewportPtr viewport, float alpha) + { + ViewPortData vd = ImGuiUserData.Get(viewport.PlatformUserData)!; + + SDL.SetWindowOpacity(vd.Window, alpha); + } + } + // @formatter:on — enable formatter after this line } public enum GamepadMode @@ -1151,36 +1114,4 @@ public enum MouseCaptureMode Enabled, EnabledAfterDrag, Disabled -} - -public unsafe static class ImGuiUserData where T : class -{ - public static void* Store(T value) - { - if (value == null) - return null; - - GCHandle handle = GCHandle.Alloc(value, GCHandleType.Normal); - return (void*)GCHandle.ToIntPtr(handle); - } - - public static T Get(void* ptr) - { - if (ptr == null) - return null; - - GCHandle handle = GCHandle.FromIntPtr((nint)ptr); - return (T)handle.Target; - } - - public static void Free(void* ptr) - { - if (ptr == null) - return; - - GCHandle handle = GCHandle.FromIntPtr((nint)ptr); - - if (handle.IsAllocated) - handle.Free(); - } } \ No newline at end of file diff --git a/SDL3/ImGuiSDL3Renderer.cs b/SDL3/ImGuiSDL3Renderer.cs index f007b26..abced6d 100644 --- a/SDL3/ImGuiSDL3Renderer.cs +++ b/SDL3/ImGuiSDL3Renderer.cs @@ -1,4 +1,6 @@ +using System.Diagnostics; using System.Numerics; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using Hexa.NET.ImGui; using SDL3; @@ -11,8 +13,6 @@ namespace SDL3_TestingSuite.SDL3; /// public unsafe static class ImGuiSDL3Renderer { - public static SDL3Window Parent; - public class RendererData { public nint Renderer; // Main viewport's renderer @@ -25,34 +25,15 @@ public unsafe static class ImGuiSDL3Renderer private struct BackupSDLRendererState { public SDL.Rect Viewport; - public bool ViewportEnabled; public bool ClipEnabled; public SDL.Rect ClipRect; } - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - private delegate void RendererCreateWindowFn(ImGuiViewportPtr viewport); + public static RendererData GetRendererData() => ImGui.GetCurrentContext().Handle != null ? ImGuiUserData.Get(ImGui.GetIO().BackendRendererUserData)! : null!; - private static readonly RendererCreateWindowFn RendererCreateWindowDelegate = RendererCreateWindow; + public static SDL3Window? Parent; - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - private delegate void RendererDestroyWindowFn(ImGuiViewportPtr viewport); - - private static readonly RendererDestroyWindowFn RendererDestroyWindowDelegate = RendererDestroyWindow; - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - private delegate void RendererRenderWindowFn(ImGuiViewportPtr viewport); - - private static readonly RendererRenderWindowFn RendererRenderWindowDelegate = RendererRenderWindow; - - [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - private delegate void RendererSwapBuffersFn(ImGuiViewportPtr viewport); - - private static readonly RendererSwapBuffersFn RendererSwapBuffersDelegate = RendererSwapBuffers; - - public static RendererData Data => ImGui.GetCurrentContext().Handle != null ? ImGuiUserData.Get(ImGui.GetIO().BackendRendererUserData)! : null!; - - public static bool Init(nint renderer, SDL3Window parentWindow = null) + public static bool Init(nint renderer, SDL3Window? parentWindow = null) { Parent = parentWindow; ImGuiIOPtr io = ImGui.GetIO(); @@ -67,88 +48,45 @@ public unsafe static class ImGuiSDL3Renderer bd.Renderer = renderer; ImGuiPlatformIOPtr platformIO = ImGui.GetPlatformIO(); - platformIO.RendererCreateWindow = (void*)Marshal.GetFunctionPointerForDelegate(RendererCreateWindowDelegate); - platformIO.RendererDestroyWindow = (void*)Marshal.GetFunctionPointerForDelegate(RendererDestroyWindowDelegate); - platformIO.RendererRenderWindow = (void*)Marshal.GetFunctionPointerForDelegate(RendererRenderWindowDelegate); - platformIO.RendererSwapBuffers = (void*)Marshal.GetFunctionPointerForDelegate(RendererSwapBuffersDelegate); + platformIO.RendererCreateWindow = DelegateStorage.RendererCreateWindowDelegate.GetPtrForDelegate(); + platformIO.RendererDestroyWindow = DelegateStorage.RendererDestroyWindowDelegate.GetPtrForDelegate(); + platformIO.RendererRenderWindow = DelegateStorage.RendererRenderWindowDelegate.GetPtrForDelegate(); + platformIO.RendererSwapBuffers = DelegateStorage.RendererSwapBuffersDelegate.GetPtrForDelegate(); return true; } public static void Dispose() { - var io = ImGui.GetIO(); - var platformIO = ImGui.GetPlatformIO(); + ImGuiIOPtr io = ImGui.GetIO(); + ImGuiPlatformIOPtr platformIO = ImGui.GetPlatformIO(); - DestroyDeviceObjects(); + if (io.BackendRendererName != null) + { + Marshal.FreeHGlobal((nint)io.BackendRendererName); + } + ImGuiUserData.Free(io.BackendRendererUserData); io.BackendRendererName = null; io.BackendRendererUserData = null; - io.BackendFlags &= ~(ImGuiBackendFlags.RendererHasVtxOffset | ImGuiBackendFlags.RendererHasTextures); + io.BackendFlags &= ~(ImGuiBackendFlags.RendererHasVtxOffset | ImGuiBackendFlags.RendererHasTextures | ImGuiBackendFlags.RendererHasViewports); platformIO.RendererTextureMaxWidth = 0; platformIO.RendererTextureMaxHeight = 0; platformIO.RendererRenderState = null; - platformIO.RendererCreateWindow = null; - platformIO.RendererDestroyWindow = null; - platformIO.RendererSetWindowSize = null; - platformIO.RendererRenderWindow = null; - platformIO.RendererSwapBuffers = null; } - public static void NewFrame() { } - - private static void RendererCreateWindow(ImGuiViewportPtr viewport) + public static void SetupRenderState(nint renderer) { - ImGuiSDL3Platform.ViewPortData? vd = ImGuiUserData.Get(viewport.PlatformUserData); - if (vd == null || vd.Window == nint.Zero) - return; - - if (vd.Renderer == nint.Zero) - { - vd.Renderer = SDL.CreateRenderer(vd.Window, null); - vd.RendererOwned = true; - } + // Clear out any viewports and cliprect set by the user + // FIXME: Technically speaking there are lots of other things we could backup/setup/restore during our render process. + SDL.SetRenderViewport(renderer, nint.Zero); + SDL.SetRenderClipRect(renderer, nint.Zero); } - private static void RendererDestroyWindow(ImGuiViewportPtr viewport) + public static void NewFrame() { - ImGuiSDL3Platform.ViewPortData? vd = ImGuiUserData.Get(viewport.PlatformUserData); - if (vd == null) - return; - - if (vd.RendererOwned && vd.Renderer != nint.Zero) - { - SDL.DestroyRenderer(vd.Renderer); - } - - vd.Renderer = nint.Zero; - vd.RendererOwned = false; - } - - private static void RendererRenderWindow(ImGuiViewportPtr viewport) - { - ImGuiSDL3Platform.ViewPortData? vd = ImGuiUserData.Get(viewport.PlatformUserData); - if (vd == null || vd.Renderer == nint.Zero) - { - return; - } - - if (Parent != null) - { - SDL.SetRenderDrawColorFloat(vd.Renderer, Parent.ClearColor.X, Parent.ClearColor.Y, Parent.ClearColor.Z, 0f); - SDL.RenderClear(vd.Renderer); - } - - RenderDrawData(viewport.DrawData, vd.Renderer); - } - - private static void RendererSwapBuffers(ImGuiViewportPtr viewport) - { - ImGuiSDL3Platform.ViewPortData? vd = ImGuiUserData.Get(viewport.PlatformUserData); - if (vd == null || vd.Renderer == nint.Zero) - return; - - SDL.RenderPresent(vd.Renderer); + RendererData bd = GetRendererData(); + Debug.Assert(bd != null, "Context or backend not initialized! Did you call ImGuiSDL3Renderer.Init()?"); } public static void RenderDrawData(ImDrawDataPtr drawData, nint renderer) @@ -167,22 +105,23 @@ public unsafe static class ImGuiSDL3Renderer for (int i = 0; i < drawData.Textures.Size; i++) { - var texture = drawData.Textures[i]; - UpdateTexture(texture, renderer); + ImTextureDataPtr texture = drawData.Textures[i]; + if (texture.Handle != null) + { + UpdateTexture(texture, renderer); + } } // Backup SDL renderer state BackupSDLRendererState old = new BackupSDLRendererState { - ViewportEnabled = SDL.RenderViewportSet(renderer), ClipEnabled = SDL.RenderClipEnabled(renderer) }; SDL.GetRenderViewport(renderer, out old.Viewport); SDL.GetRenderClipRect(renderer, out old.ClipRect); // Set up render state - SDL.SetRenderViewport(renderer, nint.Zero); - SDL.SetRenderClipRect(renderer, nint.Zero); + SetupRenderState(renderer); // Set render state in platform IO ImGuiPlatformIOPtr platformIo = ImGui.GetPlatformIO(); @@ -193,45 +132,103 @@ public unsafe static class ImGuiSDL3Renderer // Render command lists for (int n = 0; n < drawData.CmdListsCount; n++) { - ImDrawListPtr cmdList = drawData.CmdLists[n]; + ImDrawListPtr drawList = drawData.CmdLists[n]; - for (int cmdIndex = 0; cmdIndex < cmdList.CmdBuffer.Size; cmdIndex++) + for (int cmdIndex = 0; cmdIndex < drawList.CmdBuffer.Size; cmdIndex++) { - ImDrawCmd cmd = cmdList.CmdBuffer[cmdIndex]; + ImDrawCmd cmd = drawList.CmdBuffer[cmdIndex]; if (cmd.UserCallback != null) { - continue; // User callback not implemented + if (cmd.UserCallback == DelegateStorage.DrawCallbackResetRenderStateDelegate.GetPtrForDelegate()) + { + SetupRenderState(renderer); + } + else + { + // this is cursed af and idk if it even works :) + ImDrawCmdPtr cmdPtr = new ImDrawCmdPtr + { + Handle = (ImDrawCmd*)Unsafe.AsPointer(ref cmd) + }; + ((delegate* unmanaged[Cdecl])cmd.UserCallback)(drawList, cmdPtr); + } } - - // Apply clipping rectangle - Vector4 clipRect = cmd.ClipRect; - Vector2 clipMin = new Vector2((clipRect.X - clipOffset.X) * renderScale.X, (clipRect.Y - clipOffset.Y) * renderScale.Y); - Vector2 clipMax = new Vector2((clipRect.Z - clipOffset.X) * renderScale.X, (clipRect.W - clipOffset.Y) * renderScale.Y); - - clipMin.X = Math.Max(0, clipMin.X); - clipMin.Y = Math.Max(0, clipMin.Y); - clipMax.X = Math.Min(fbWidth, clipMax.X); - clipMax.Y = Math.Min(fbHeight, clipMax.Y); - if (clipMax.X <= clipMin.X || clipMax.Y <= clipMin.Y) - continue; - - SDL.Rect r = new SDL.Rect + else { - X = (int)clipMin.X, - Y = (int)clipMin.Y, - W = (int)(clipMax.X - clipMin.X), - H = (int)(clipMax.Y - clipMin.Y) - }; - SDL.SetRenderClipRect(renderer, r); + // Apply clipping rectangle + Vector4 clipRect = cmd.ClipRect; + Vector2 clipMin = new Vector2((clipRect.X - clipOffset.X) * renderScale.X, (clipRect.Y - clipOffset.Y) * renderScale.Y); + Vector2 clipMax = new Vector2((clipRect.Z - clipOffset.X) * renderScale.X, (clipRect.W - clipOffset.Y) * renderScale.Y); - // Get texture - nint texId = cmd.GetTexID(); + clipMin.X = Math.Max(0, clipMin.X); + clipMin.Y = Math.Max(0, clipMin.Y); + clipMax.X = Math.Min(fbWidth, clipMax.X); + clipMax.Y = Math.Min(fbHeight, clipMax.Y); + if (clipMax.X <= clipMin.X || clipMax.Y <= clipMin.Y) + continue; - // Convert ImGui vertices to SDL vertices - if (!RenderDrawCommand(cmdList, cmd, renderer, texId, renderScale, clipOffset)) - { - Console.WriteLine($"Failed to render ImGui draw command: {SDL.GetError()}"); + SDL.Rect r = new SDL.Rect + { + X = (int)clipMin.X, + Y = (int)clipMin.Y, + W = (int)(clipMax.X - clipMin.X), + H = (int)(clipMax.Y - clipMin.Y) + }; + SDL.SetRenderClipRect(renderer, r); + + // Convert ImGui vertices to SDL vertices + uint indexOffset = cmd.IdxOffset; + uint vertexOffset = cmd.VtxOffset; + uint elemCount = cmd.ElemCount; + + SDL.Vertex[] vertices = new SDL.Vertex[elemCount]; + int[] indices = new int[elemCount]; + + for (int i = 0; i < elemCount; i++) + { + ushort idx = drawList.IdxBuffer[(int)indexOffset + i]; + int vertIdx = (int)(vertexOffset + idx); + + ImDrawVert srcVert = drawList.VtxBuffer[vertIdx]; + + uint col = srcVert.Col; + + byte colR = (byte)((col >> 0) & 0xFF); + byte colG = (byte)((col >> 8) & 0xFF); + byte colB = (byte)((col >> 16) & 0xFF); + byte colA = (byte)((col >> 24) & 0xFF); + + vertices[i] = new SDL.Vertex + { + Position = new SDL.FPoint + { + X = (srcVert.Pos.X - clipOffset.X) * renderScale.X, + Y = (srcVert.Pos.Y - clipOffset.Y) * renderScale.Y + }, + Color = new SDL.FColor + { + R = colR / 255f, + G = colG / 255f, + B = colB / 255f, + A = colA / 255f + }, + TexCoord = new SDL.FPoint + { + X = srcVert.Uv.X, + Y = srcVert.Uv.Y + } + }; + + indices[i] = i; + } + + // Get texture + ImTextureID texId = cmd.GetTexID(); + if (!SDL.RenderGeometry(renderer, texId, vertices, vertices.Length, indices, indices.Length)) + { + Program.Logger.Log($"Failed to render ImGui draw command: {SDL.GetError()}"); + } } } } @@ -240,7 +237,7 @@ public unsafe static class ImGuiSDL3Renderer platformIo.RendererRenderState = null; // Restore renderer state - SDL.SetRenderViewport(renderer, old.ViewportEnabled ? old.Viewport : new SDL.Rect()); + SDL.SetRenderViewport(renderer, old.Viewport); SDL.SetRenderClipRect(renderer, old.ClipEnabled ? old.ClipRect : new SDL.Rect()); } @@ -281,7 +278,7 @@ public unsafe static class ImGuiSDL3Renderer nint sdlTexture = tex.TexID; for (int i = 0; i < tex.Updates.Size; i++) { - var r = tex.Updates[i]; + ImTextureRect r = tex.Updates[i]; SDL.Rect sdlR = new SDL.Rect { X = r.X, Y = r.Y, W = r.W, H = r.H }; SDL.UpdateTexture(sdlTexture, sdlR, (nint)tex.GetPixelsAt(r.X, r.Y), tex.GetPitch()); } @@ -289,67 +286,92 @@ public unsafe static class ImGuiSDL3Renderer } } - - private static bool RenderDrawCommand(ImDrawListPtr drawList, ImDrawCmd cmd, nint renderer, nint texId, Vector2 scale, Vector2 displayPos) - { - uint indexOffset = cmd.IdxOffset; - uint vertexOffset = cmd.VtxOffset; - uint elemCount = cmd.ElemCount; - - SDL.Vertex[] vertices = new SDL.Vertex[elemCount]; - int[] indices = new int[elemCount]; - - for (int i = 0; i < elemCount; i++) - { - ushort idx = drawList.IdxBuffer[(int)indexOffset + i]; - int vertIdx = (int)(vertexOffset + idx); - - ImDrawVert srcVert = drawList.VtxBuffer[vertIdx]; - - uint col = srcVert.Col; - - byte r = (byte)((col >> 0) & 0xFF); - byte g = (byte)((col >> 8) & 0xFF); - byte b = (byte)((col >> 16) & 0xFF); - byte a = (byte)((col >> 24) & 0xFF); - - vertices[i] = new SDL.Vertex - { - Position = new SDL.FPoint - { - X = (srcVert.Pos.X - displayPos.X) * scale.X, - Y = (srcVert.Pos.Y - displayPos.Y) * scale.Y - }, - Color = new SDL.FColor - { - R = r / 255f, - G = g / 255f, - B = b / 255f, - A = a / 255f - }, - TexCoord = new SDL.FPoint - { - X = srcVert.Uv.X, - Y = srcVert.Uv.Y - } - }; - - indices[i] = i; - } - - return SDL.RenderGeometry(renderer, texId, vertices, vertices.Length, indices, indices.Length); - } - public static void CreateDeviceObjects() { } public static void DestroyDeviceObjects() { - var texures = ImGui.GetPlatformIO().Textures; + ImVector texures = ImGui.GetPlatformIO().Textures; for (int i = 0; i < texures.Size; i++) { - var texture = texures[i]; + ImTextureDataPtr texture = texures[i]; texture.Status = ImTextureStatus.WantDestroy; - UpdateTexture(texture, Data.Renderer); + UpdateTexture(texture, GetRendererData().Renderer); } } + + // @formatter:off — disable formatter after this line + public static class DelegateStorage + { + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate void DrawCallbackResetRenderStateFn(ImDrawListPtr drawList, ImDrawCmdPtr cmd); + internal static readonly DrawCallbackResetRenderStateFn DrawCallbackResetRenderStateDelegate = DrawCallbackResetRenderState; + private static void DrawCallbackResetRenderState(ImDrawListPtr drawList, ImDrawCmdPtr cmd) { } // Intentionally empty. Used as an identifier for rendering loop to call its code. Simpler to implement this way. + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate void RendererCreateWindowFn(ImGuiViewportPtr viewport); + internal static readonly RendererCreateWindowFn RendererCreateWindowDelegate = RendererCreateWindow; + private static void RendererCreateWindow(ImGuiViewportPtr viewport) + { + ImGuiSDL3Platform.ViewPortData? vd = ImGuiUserData.Get(viewport.PlatformUserData); + if (vd == null || vd.Window == nint.Zero) + return; + + if (vd.Renderer == nint.Zero) + { + vd.Renderer = SDL.CreateRenderer(vd.Window, null); + vd.RendererOwned = true; + } + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate void RendererDestroyWindowFn(ImGuiViewportPtr viewport); + internal static readonly RendererDestroyWindowFn RendererDestroyWindowDelegate = RendererDestroyWindow; + private static void RendererDestroyWindow(ImGuiViewportPtr viewport) + { + ImGuiSDL3Platform.ViewPortData? vd = ImGuiUserData.Get(viewport.PlatformUserData); + if (vd == null) + return; + + if (vd.RendererOwned && vd.Renderer != nint.Zero) + { + SDL.DestroyRenderer(vd.Renderer); + } + + vd.Renderer = nint.Zero; + vd.RendererOwned = false; + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate void RendererRenderWindowFn(ImGuiViewportPtr viewport); + internal static readonly RendererRenderWindowFn RendererRenderWindowDelegate = RendererRenderWindow; + private static void RendererRenderWindow(ImGuiViewportPtr viewport) + { + ImGuiSDL3Platform.ViewPortData? vd = ImGuiUserData.Get(viewport.PlatformUserData); + if (vd == null || vd.Renderer == nint.Zero) + { + return; + } + + if (Parent != null) + { + SDL.SetRenderDrawColorFloat(vd.Renderer, Parent.ClearColor.X, Parent.ClearColor.Y, Parent.ClearColor.Z, 0f); + SDL.RenderClear(vd.Renderer); + } + + RenderDrawData(viewport.DrawData, vd.Renderer); + } + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate void RendererSwapBuffersFn(ImGuiViewportPtr viewport); + internal static readonly RendererSwapBuffersFn RendererSwapBuffersDelegate = RendererSwapBuffers; + private static void RendererSwapBuffers(ImGuiViewportPtr viewport) + { + ImGuiSDL3Platform.ViewPortData? vd = ImGuiUserData.Get(viewport.PlatformUserData); + if (vd == null || vd.Renderer == nint.Zero) + return; + + SDL.RenderPresent(vd.Renderer); + } + } + // @formatter:on — enable formatter after this line } \ No newline at end of file diff --git a/SDL3/ImGuiUserData.cs b/SDL3/ImGuiUserData.cs new file mode 100644 index 0000000..4afbbfa --- /dev/null +++ b/SDL3/ImGuiUserData.cs @@ -0,0 +1,35 @@ +using System.Runtime.InteropServices; + +namespace SDL3_TestingSuite.SDL3; + +public unsafe static class ImGuiUserData where T : class +{ + public static void* Store(T value) + { + if (value == null) + return null; + + GCHandle handle = GCHandle.Alloc(value, GCHandleType.Normal); + return (void*)GCHandle.ToIntPtr(handle); + } + + public static T? Get(void* ptr) + { + if (ptr == null) + return null; + + GCHandle handle = GCHandle.FromIntPtr((nint)ptr); + return (T?)handle.Target; + } + + public static void Free(void* ptr) + { + if (ptr == null) + return; + + GCHandle handle = GCHandle.FromIntPtr((nint)ptr); + + if (handle.IsAllocated) + handle.Free(); + } +} \ No newline at end of file diff --git a/SDL3/SDL3Window.cs b/SDL3/SDL3Window.cs index 6ceda11..2be215f 100644 --- a/SDL3/SDL3Window.cs +++ b/SDL3/SDL3Window.cs @@ -1,8 +1,8 @@ -using System.Diagnostics; -using System.Numerics; +using System.Numerics; using Hexa.NET.ImGui; using Hexa.NET.ImGuizmo; using Hexa.NET.ImPlot; +using Hexa.NET.ImPlot3D; using SDL3; namespace SDL3_TestingSuite.SDL3; @@ -12,15 +12,13 @@ public sealed unsafe class SDL3Window : IDisposable public readonly nint Window; public readonly nint Renderer; - internal Action? RenderCallback { get; set; } + public event Action RenderCallback; public Vector4 ClearColor = new Vector4(0.06f, 0.05882353f, 0.05882353f, 1f); - private readonly Stopwatch _timer = Stopwatch.StartNew(); - private TimeSpan _time = TimeSpan.Zero; - - private SDL.Rect _screenClipRect; private ImGuiContextPtr _imGuiContext; + private ImPlotContextPtr _imPlotContext; + private ImPlot3DContextPtr _imPlot3DContext; public bool Disposed => _disposed; private bool _disposed; @@ -41,18 +39,18 @@ public sealed unsafe class SDL3Window : IDisposable SDL.ShowWindow(Window); // Create ImGui context - var context = ImGui.CreateContext(); - ImPlot.CreateContext(); - ImPlot.SetImGuiContext(context); - ImGuizmo.SetImGuiContext(context); - // ImPlot3D.SetImGuiContext(context); - // ImPlot3D.CreateContext(); + _imGuiContext = ImGui.CreateContext(); + _imPlotContext = ImPlot.CreateContext(); + // _imPlot3DContext = ImPlot3D.CreateContext(); + ImPlot.SetImGuiContext(_imGuiContext); + ImGuizmo.SetImGuiContext(_imGuiContext); + // ImPlot3D.SetImGuiContext(_imGuiContext); ImGuiIOPtr io = ImGui.GetIO(); io.ConfigFlags |= ImGuiConfigFlags.NavEnableKeyboard | ImGuiConfigFlags.NavEnableGamepad | ImGuiConfigFlags.DockingEnable; io.ConfigFlags |= ImGuiConfigFlags.ViewportsEnable; - io.Fonts.Flags |= ImFontAtlasFlags.NoBakedLines; + io.Fonts.Flags |= ImFontAtlasFlags.NoBakedLines; io.Fonts.AddFontDefault(); try @@ -96,20 +94,54 @@ public sealed unsafe class SDL3Window : IDisposable ImGuiSDL3Renderer.Init(Renderer, this); } + public bool ShouldClose; + public void Run() { while (!_disposed) { - ImGui.GetIO().DeltaTime = (float)(_timer.Elapsed - _time).TotalSeconds; - _time = _timer.Elapsed; + if (ImGui.GetIO().WantTextInput && !SDL.TextInputActive(Window)) + SDL.StartTextInput(Window); + else if (!ImGui.GetIO().WantTextInput && SDL.TextInputActive(Window)) + SDL.StopTextInput(Window); - PollEvents(); + while (SDL.PollEvent(out SDL.Event ev)) + { + ImGuiSDL3Platform.ProcessEvent(ev); - Update(); + switch ((SDL.EventType)ev.Type) + { + case SDL.EventType.Terminating: + case SDL.EventType.WindowCloseRequested: + case SDL.EventType.Quit: + ShouldClose = true; + break; + } + } + + ImGuiSDL3Platform.NewFrame(); + ImGuiSDL3Renderer.NewFrame(); + ImGui.NewFrame(); RenderCallback?.Invoke(); - Render(); + ImGuiIOPtr io = ImGui.GetIO(); + + ImGui.Render(); + + SDL.SetRenderScale(Renderer, io.DisplayFramebufferScale.X, io.DisplayFramebufferScale.Y); + SDL.SetRenderDrawColorFloat(Renderer, ClearColor.X, ClearColor.Y, ClearColor.Z, ClearColor.W); + SDL.RenderClear(Renderer); + + ImGuiSDL3Renderer.RenderDrawData(ImGui.GetDrawData(), Renderer); + + if ((io.ConfigFlags & ImGuiConfigFlags.ViewportsEnable) != 0) + { + ImGui.UpdatePlatformWindows(); + ImGui.RenderPlatformWindowsDefault(); + } + + SDL.RenderPresent(Renderer); if (ShouldClose) { @@ -122,56 +154,6 @@ public sealed unsafe class SDL3Window : IDisposable Dispose(); } - public bool ShouldClose; - - private void PollEvents() - { - if (ImGui.GetIO().WantTextInput && !SDL.TextInputActive(Window)) - SDL.StartTextInput(Window); - else if (!ImGui.GetIO().WantTextInput && SDL.TextInputActive(Window)) - SDL.StopTextInput(Window); - - while (SDL.PollEvent(out SDL.Event ev)) - { - ImGuiSDL3Platform.ProcessEvent(ev); - - switch ((SDL.EventType)ev.Type) - { - case SDL.EventType.Terminating: - case SDL.EventType.WindowCloseRequested: - case SDL.EventType.Quit: - ShouldClose = true; - break; - } - } - } - - private void Update() - { - ImGuiSDL3Platform.NewFrame(); - ImGuiSDL3Renderer.NewFrame(); - ImGui.NewFrame(); - } - - private void Render() - { - ImGuiIOPtr io = ImGui.GetIO(); - - ImGui.Render(); - SDL.SetRenderScale(Renderer, io.DisplayFramebufferScale.X, io.DisplayFramebufferScale.Y); - SDL.SetRenderDrawColorFloat(Renderer, ClearColor.X, ClearColor.Y, ClearColor.Z, ClearColor.W); - SDL.RenderClear(Renderer); - ImGuiSDL3Renderer.RenderDrawData(ImGui.GetDrawData(), Renderer); - - if ((io.ConfigFlags & ImGuiConfigFlags.ViewportsEnable) != 0) - { - ImGui.UpdatePlatformWindows(); - ImGui.RenderPlatformWindowsDefault(); - } - - SDL.RenderPresent(Renderer); - } - ~SDL3Window() { Dispose(false); @@ -190,6 +172,19 @@ public sealed unsafe class SDL3Window : IDisposable if (disposing) { + if (_imGuiContext.Handle != null) + { + ImGui.SetCurrentContext(_imGuiContext); + } + if (_imPlotContext.Handle != null) + { + ImPlot.SetCurrentContext(_imPlotContext); + } + // if (_imPlot3DContext.Handle != null) + // { + // ImPlot3D.SetCurrentContext(_imPlot3DContext); + // } + ImGuiSDL3Renderer.Dispose(); ImGuiSDL3Platform.Dispose(); @@ -202,6 +197,18 @@ public sealed unsafe class SDL3Window : IDisposable ImGui.DestroyContext(_imGuiContext); _imGuiContext = null; } + if (_imPlotContext.Handle != null) + { + ImPlot.SetCurrentContext(null); + ImPlot.DestroyContext(_imPlotContext); + _imPlotContext = null; + } + // if (_imPlot3DContext.Handle != null) + // { + // ImPlot3D.SetCurrentContext(null); + // ImPlot3D.DestroyContext(_imPlot3DContext); + // _imPlot3DContext = null; + // } if (Renderer != nint.Zero) {