diff --git a/Program.cs b/Program.cs index d898eb4..28c5279 100644 --- a/Program.cs +++ b/Program.cs @@ -1,8 +1,11 @@ using System.Numerics; +using System.Reflection; using System.Runtime.CompilerServices; using Hexa.NET.ImGui; using Hexa.NET.ImPlot; +using HexaGen.Runtime; using SDL3_TestingSuite.SDL3; +using SDL3; namespace SDL3_TestingSuite; @@ -12,15 +15,37 @@ public class Program private static bool _imPlotDemoVisible; private static bool _fontStuff; private static readonly Dictionary?> GlyphsByName = new Dictionary?>(); - private static bool _initialized; + private static bool _fontStuffInitialized; + private static bool _init; private static ImFontPtr _font; private static float size = 1f; + private static SDL3Window _window = null!; + private static OpenGLTexture? _imageTexture; + private static string? _imageLoadError; public static void Main() { - SDL3Window window = new SDL3Window("SDL3 Testing Suite", 100, 100, 1280, 720); - window.RenderCallback += () => + string baseDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!; + string osPlatform = typeof(LibraryLoader).GetMethod("GetOSPlatform", BindingFlags.Static | BindingFlags.NonPublic)?.Invoke(null, null) as string ?? ""; // LibraryLoader.GetOSPlatform(); + string architecture = typeof(LibraryLoader).GetMethod("GetArchitecture", BindingFlags.Static | BindingFlags.NonPublic)?.Invoke(null, null) as string ?? ""; // LibraryLoader.GetArchitecture()); + LibraryLoader.CustomLoadFolders.AddRange(new string[] { + Path.Combine(baseDirectory), + Path.Combine(baseDirectory, "runtimes", osPlatform, "native"), + Path.Combine(baseDirectory, "runtimes", $"{osPlatform}-{architecture}", "debug"), // allows debug builds sideload. + Path.Combine(baseDirectory, "runtimes", $"{osPlatform}-{architecture}", "native"), + }); + + _window = new SDL3Window("SDL3 Testing Suite", 100, 100, 1280, 720); + _window.Disposing += DisposeImages; + _window.RenderCallback += () => + { + if (!_init) + { + _init = true; + LoadImageTexture(""); + } + ImGuiViewportPtr viewport = ImGui.GetMainViewport(); ImGui.SetNextWindowPos(viewport.WorkPos); ImGui.SetNextWindowSize(viewport.WorkSize); @@ -33,7 +58,7 @@ public class Program { if (ImGui.MenuItem("Quit")) { - window.ShouldClose = true; + _window.ShouldClose = true; } ImGui.EndMenu(); @@ -65,7 +90,7 @@ public class Program string fontName = _font.GetDebugNameS(); fontName = string.IsNullOrEmpty(fontName) ? _font.FontId.ToString() : fontName; - if (!_initialized || change) + if (!_fontStuffInitialized || change) { if (!GlyphsByName.TryGetValue(fontName, out List? glyphs)) { @@ -89,7 +114,7 @@ public class Program Logger.Log($"Initialized font {fontName} with {glyphs.Count} glyphs"); } - _initialized = true; + _fontStuffInitialized = true; } ImGui.PushFont(null, 24); @@ -161,6 +186,48 @@ public class Program ImGui.SliderFloat("Sine", ref size, 1f, 120f); ImGui.End(); + bool regen1 = true; + + if (_imageTexture != null) + { + Vector2 displaySize = ImGui.GetIO().DisplaySize; + Vector2 imageSize = new Vector2(_imageTexture.Width, _imageTexture.Height); + + float maxWidth = displaySize.X * 0.5f; + float maxHeight = displaySize.Y * 0.5f; + float scale = MathF.Min(maxWidth / imageSize.X, maxHeight / imageSize.Y); + + Vector2 scaledSize = imageSize * MathF.Min(scale, 1.0f); + Vector2 padding = ImGui.GetStyle().WindowPadding; + Vector2 windowSize = new Vector2(scaledSize.X + padding.X * 2, scaledSize.Y + padding.Y * 2 + (ImGui.GetFrameHeight() * 4)); + + ImGui.SetNextWindowSize(windowSize, ImGuiCond.Always); + ImGui.Begin(_imageTexture.FileName ?? "Image", ref regen1, ImGuiWindowFlags.NoResize | ImGuiWindowFlags.NoScrollbar | ImGuiWindowFlags.NoSavedSettings); + + regen1 = !ImGui.Button("Regen"); + + ImGui.Image(_imageTexture.ImGuiTexture, scaledSize); + + string text1 = $"{_imageTexture.Width}x{_imageTexture.Height}"; + ImGui.TextUnformatted(text1); + ImGui.SameLine(ImGui.CalcTextSize(text1).X + padding.X * 2); + ImGui.TextUnformatted($"EXT: {(_imageTexture.Extension ?? "Unknown Format")}"); + ImGui.TextUnformatted($"{(_imageTexture.FilePath ?? "Unknown Path")}"); + } + else + { + ImGui.Begin("Image", ref regen1, ImGuiWindowFlags.NoResize | ImGuiWindowFlags.NoScrollbar | ImGuiWindowFlags.NoSavedSettings); + + ImGui.TextUnformatted(_imageLoadError ?? "No image loaded."); + } + ImGui.End(); + bool regen = !regen1; + if (regen) + { + DisposeImages(); + LoadImageTexture(""); + } + if (_demoWindowVisible) { ImGui.ShowDemoWindow(ref _demoWindowVisible); @@ -171,7 +238,30 @@ public class Program } }; - window.Run(); + _window.Run(); + } + + private static void LoadImageTexture(string path) + { + if (string.IsNullOrEmpty(path)) return; + + try + { + _imageTexture = OpenGLTexture.FromFile(_window.GL, path); + _imageLoadError = null; + } + catch (Exception e) + { + DisposeImages(); + _imageLoadError = e.Message; + Logger.Log(e); + } + } + + private static void DisposeImages() + { + _imageTexture?.Dispose(); + _imageTexture = null; } public static class Logger @@ -181,7 +271,12 @@ public class Program public static void Log(object? value, [CallerFilePath] string path = "", [CallerMemberName] string caller = "", [CallerLineNumber] int line = 0) { - Write(value?.ToString(), path, caller, line); + if (value == null) + { + Write("null", path, caller, line); + return; + } + Write(value.ToString(), path, caller, line); } public static void Log(object?[] values, [CallerFilePath] string path = "", [CallerMemberName] string caller = "", [CallerLineNumber] int line = 0) diff --git a/SDL3 TestingSuite.csproj b/SDL3 TestingSuite.csproj index 97336e7..e4d10f2 100644 --- a/SDL3 TestingSuite.csproj +++ b/SDL3 TestingSuite.csproj @@ -14,11 +14,14 @@ + + + diff --git a/SDL3/ImGuiSDL3Platform.cs b/SDL3/ImGuiSDL3Platform.cs index e9aa0e8..bac2606 100644 --- a/SDL3/ImGuiSDL3Platform.cs +++ b/SDL3/ImGuiSDL3Platform.cs @@ -28,8 +28,6 @@ public unsafe static class ImGuiSDL3Platform // IME Handling public nint ImeWindow; - public ImGuiPlatformImeData ImeData; - public bool ImeDirty; // Mouse Handling public uint MouseWindowID; @@ -177,8 +175,6 @@ public unsafe static class ImGuiSDL3Platform UpdateMouseData(); UpdateMouseCursor(); - UpdateIme(); - UpdateGamepads(); } @@ -752,43 +748,41 @@ public unsafe static class ImGuiSDL3Platform return ImGuiKey.None; } - public static void UpdateIme() + private static void UpdateIme(ImGuiViewportPtr viewport, ImGuiPlatformImeDataPtr imeData) { PlatformData data = GetPlatformData(); - ImGuiPlatformImeData imeData = data.ImeData; - nint window = SDL.GetKeyboardFocus(); + if (imeData.IsNull) return; + + nint window = GetSDLWindowFromViewport(viewport); + bool wantTextInput = imeData.WantVisible || imeData.WantTextInput; // Stop previous input - if ((!(imeData.WantVisible == 1 || imeData.WantTextInput == 1) || data.ImeWindow != window) && data.ImeWindow != nint.Zero) + if ((!wantTextInput || data.ImeWindow != window) && data.ImeWindow != nint.Zero) { SDL.StopTextInput(data.ImeWindow); data.ImeWindow = nint.Zero; } - if (!data.ImeDirty && data.ImeWindow == window || window == nint.Zero) + + if (!wantTextInput || window == nint.Zero) return; // Start/update current input - data.ImeDirty = false; - if (imeData.WantVisible == 1) + if (imeData.WantVisible) { // Offset the IME input area by the viewport position so it tracks the correct screen location // when multiple viewports are in use. - Vector2 viewportPos = Vector2.Zero; - ImGuiViewportPtr? viewport = GetViewportForWindowId(SDL.GetWindowID(window)); - if (viewport != null && !viewport.Value.IsNull) - viewportPos = viewport.Value.Pos; - SDL.Rect r = new SDL.Rect { - X = (int)(imeData.InputPos.X - viewportPos.X), - Y = (int)(imeData.InputPos.Y - viewportPos.Y), + X = (int)(imeData.InputPos.X - viewport.Pos.X), + Y = (int)(imeData.InputPos.Y - viewport.Pos.Y), W = 1, H = (int)imeData.InputLineHeight }; SDL.SetTextInputArea(window, r, 0); - data.ImeWindow = window; } - if (!SDL.TextInputActive(window) && (imeData.WantVisible == 1 || imeData.WantTextInput == 1)) + + data.ImeWindow = window; + if (!SDL.TextInputActive(window)) SDL.StartTextInput(window); } @@ -915,9 +909,9 @@ public unsafe static class ImGuiSDL3Platform public static class DelegateStorage { [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - internal delegate nint PlatformGetClipboardTextFn(nint ctx); + internal delegate nint PlatformGetClipboardTextFn(ImGuiContextPtr ctx); internal static readonly PlatformGetClipboardTextFn GetClipboardDelegate = GetClipboardText; - private static nint GetClipboardText(nint ctx) + private static nint GetClipboardText(ImGuiContextPtr ctx) { PlatformData data = GetPlatformData(); if (data.ClipboardTextData != nint.Zero) @@ -934,9 +928,9 @@ public unsafe static class ImGuiSDL3Platform } [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - internal delegate void PlatformSetClipboardTextFn(nint ctx, nint text); + internal delegate void PlatformSetClipboardTextFn(ImGuiContextPtr ctx, nint text); internal static readonly PlatformSetClipboardTextFn SetClipboardDelegate = SetClipboardText; - private static void SetClipboardText(nint ctx, nint text) + private static void SetClipboardText(ImGuiContextPtr ctx, nint text) { string? managedText = Marshal.PtrToStringAnsi(text); if (managedText != null) @@ -944,14 +938,11 @@ public unsafe static class ImGuiSDL3Platform } [UnmanagedFunctionPointer(CallingConvention.Cdecl)] - internal delegate void PlatformSetImeDataFn(nint ctx, nint userData, nint imeData); + internal delegate void PlatformSetImeDataFn(ImGuiContextPtr ctx, ImGuiViewportPtr userData, ImGuiPlatformImeDataPtr imeData); internal static readonly PlatformSetImeDataFn SetImeDataDelegate = SetImeData; - private static void SetImeData(nint ctx, nint userData, nint imeData) + private static void SetImeData(ImGuiContextPtr ctx, ImGuiViewportPtr viewport, ImGuiPlatformImeDataPtr imeData) { - PlatformData data = GetPlatformData(); - data.ImeData = Marshal.PtrToStructure(imeData); - data.ImeDirty = true; - UpdateIme(); + UpdateIme(viewport, imeData); } [UnmanagedFunctionPointer(CallingConvention.Cdecl)] @@ -1202,4 +1193,4 @@ public enum MouseCaptureMode Enabled, EnabledAfterDrag, Disabled -} \ No newline at end of file +} diff --git a/SDL3/SDL3Window.cs b/SDL3/SDL3Window.cs index 4e6225e..5d3cfad 100644 --- a/SDL3/SDL3Window.cs +++ b/SDL3/SDL3Window.cs @@ -22,6 +22,7 @@ public sealed unsafe class SDL3Window : IDisposable } public event Action? RenderCallback; + public event Action? Disposing; private ImGuiContextPtr _imGuiContext; private ImPlotContextPtr _imPlotContext; @@ -31,7 +32,7 @@ public sealed unsafe class SDL3Window : IDisposable private readonly FileSystemWatcher? _watcher; - private readonly GL _gl; + public readonly GL GL; public SDL3Window(string name, int posX, int posY, int width, int height, SDL.WindowFlags flags = SDL.WindowFlags.Resizable | SDL.WindowFlags.HighPixelDensity) { @@ -116,11 +117,11 @@ public sealed unsafe class SDL3Window : IDisposable Console.WriteLine(e); } - _gl = new GL(new BindingsContext(Window, glContext)); + GL = new GL(new BindingsContext(Window, glContext)); // Init platform and renderer - ImGuiSDL3Platform.Init(_gl, Window, glContext); - ImGuiSDL3Renderer.Init(_gl, "#version 330"); + ImGuiSDL3Platform.Init(GL, Window, glContext); + ImGuiSDL3Renderer.Init(GL, "#version 330"); } public bool ShouldClose; @@ -130,11 +131,6 @@ public sealed unsafe class SDL3Window : IDisposable while (!_disposed) { SDL.PumpEvents(); - - 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)) { @@ -149,10 +145,16 @@ public sealed unsafe class SDL3Window : IDisposable break; } } + + if (SDL.GetWindowFlags(Window).HasFlag(SDL.WindowFlags.Minimized)) + { + SDL.Delay(10); + continue; + } - _gl.MakeCurrent(); - _gl.ClearColor(ClearColor.X, ClearColor.Y, ClearColor.Z, ClearColor.W); - _gl.Clear(GLClearBufferMask.ColorBufferBit); + GL.MakeCurrent(); + GL.ClearColor(ClearColor.X, ClearColor.Y, ClearColor.Z, ClearColor.W); + GL.Clear(GLClearBufferMask.ColorBufferBit); ImGuiSDL3Platform.NewFrame(); ImGuiSDL3Renderer.NewFrame(); @@ -164,7 +166,7 @@ public sealed unsafe class SDL3Window : IDisposable ImGui.Render(); - _gl.MakeCurrent(); + GL.MakeCurrent(); ImGuiSDL3Renderer.RenderDrawData(ImGui.GetDrawData()); @@ -174,8 +176,8 @@ public sealed unsafe class SDL3Window : IDisposable ImGui.RenderPlatformWindowsDefault(); } - _gl.MakeCurrent(); - _gl.SwapBuffers(); + GL.MakeCurrent(); + GL.SwapBuffers(); if (ShouldClose) { @@ -215,10 +217,13 @@ public sealed unsafe class SDL3Window : IDisposable ImPlot.SetCurrentContext(_imPlotContext); } + Disposing?.Invoke(); + ImGuiSDL3Renderer.Dispose(); ImGuiSDL3Platform.Dispose(); RenderCallback = null; + Disposing = null; } if (_imGuiContext.Handle != null) @@ -294,4 +299,4 @@ public unsafe class BindingsContext : IGLContext procAddress = (nint)SDL.GLGetProcAddress(procName).GetPtrForDelegate(); return procAddress != 0; } -} \ No newline at end of file +}