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; public sealed unsafe class SDL3Window : IDisposable { public readonly nint Window; public readonly nint Renderer; public event Action RenderCallback; public Vector4 ClearColor = new Vector4(0.06f, 0.05882353f, 0.05882353f, 1f); private ImGuiContextPtr _imGuiContext; private ImPlotContextPtr _imPlotContext; private ImPlot3DContextPtr _imPlot3DContext; public bool Disposed => _disposed; private bool _disposed; private FileSystemWatcher? _watcher; public SDL3Window(string name, int posX, int posY, int width, int height, SDL.WindowFlags flags = SDL.WindowFlags.Resizable | SDL.WindowFlags.HighPixelDensity) { if (!SDL.Init(SDL.InitFlags.Events | SDL.InitFlags.Video | SDL.InitFlags.Gamepad)) throw new Exception($"SDL_Init failed: {SDL.GetError()}"); // Create window & renderer if (!SDL.CreateWindowAndRenderer(name, width, height, flags, out Window, out Renderer)) throw new Exception($"SDL_CreateWindowAndRenderer failed: {SDL.GetError()}"); SDL.SetWindowPosition(Window, posX, posY); SDL.SetRenderVSync(Renderer, 1); SDL.ShowWindow(Window); // Create ImGui context _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.AddFontDefault(); try { string fontsPath = Path.Combine(AppContext.BaseDirectory, "Fonts"); if (!Path.Exists(fontsPath)) Directory.CreateDirectory(fontsPath); _watcher = new FileSystemWatcher(fontsPath); _watcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.CreationTime; _watcher.Created += (a, b) => { if (!File.Exists(b.FullPath)) return; if (Path.GetExtension(b.FullPath) != ".ttf") return; ImGui.GetIO().AddFont(b.FullPath); }; _watcher.Deleted += (a, b) => { if (Path.GetExtension(b.FullPath) != ".ttf") return; ImGui.GetIO().RemoveFont(b.Name); }; _watcher.IncludeSubdirectories = false; _watcher.EnableRaisingEvents = true; string[] fonts = Directory.GetFiles(fontsPath, "*.ttf", SearchOption.AllDirectories); foreach (string font in fonts) { io.AddFont(font); } } catch (Exception e) { Console.WriteLine(e); } // Init platform and renderer ImGuiSDL3Platform.Init(Window, Renderer, this); ImGuiSDL3Renderer.Init(Renderer, this); } public bool ShouldClose; public void Run() { while (!_disposed) { 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; } } ImGuiSDL3Platform.NewFrame(); ImGuiSDL3Renderer.NewFrame(); ImGui.NewFrame(); RenderCallback?.Invoke(); 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) { Dispose(); break; } } if (!_disposed) Dispose(); } ~SDL3Window() { Dispose(false); } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } private void Dispose(bool disposing) { if (_disposed) return; _disposed = true; 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(); RenderCallback = null; } if (_imGuiContext.Handle != null) { ImGui.SetCurrentContext(null); 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) { SDL.DestroyRenderer(Renderer); } if (Window != nint.Zero) { SDL.DestroyWindow(Window); } } }