diff --git a/SDL3/ImGuiSDL3Platform.cs b/SDL3/ImGuiSDL3Platform.cs index bac2606..3ad2fc4 100644 --- a/SDL3/ImGuiSDL3Platform.cs +++ b/SDL3/ImGuiSDL3Platform.cs @@ -97,11 +97,14 @@ public unsafe static class ImGuiSDL3Platform // SDL on Linux/OSX doesn't report events for unfocused windows (see https://github.com/ocornut/imgui/issues/4960) // We will use MouseCanReportHoveredViewport to set HasMouseHoveredViewport dynamically each frame. -#if __APPLE__ - data.MouseCanReportHoveredViewport = false; -#else - data.MouseCanReportHoveredViewport = data.MouseCanUseGlobalState; -#endif + if (OperatingSystem.IsMacOS()) + { + data.MouseCanReportHoveredViewport = false; + } + else + { + data.MouseCanReportHoveredViewport = data.MouseCanUseGlobalState; + } ImGuiPlatformIOPtr platformIo = ImGui.GetPlatformIO(); platformIo.PlatformGetClipboardTextFn = DelegateStorage.GetClipboardDelegate.GetPtrForDelegate(); @@ -148,9 +151,10 @@ public unsafe static class ImGuiSDL3Platform io.DisplaySize = size; io.DisplayFramebufferScale = framebufferScale; -#if WIN32 - data.WantUpdateMonitors = true; -#endif + if (OperatingSystem.IsWindows()) + { + data.WantUpdateMonitors = true; + } if (data.WantUpdateMonitors) UpdateMonitors(); @@ -213,15 +217,20 @@ public unsafe static class ImGuiSDL3Platform if (SDL.GetWindowFlags(window).HasFlag(SDL.WindowFlags.Minimized)) w = h = 0; -#if __APPLE__ - // SDL_GetWindowDisplayScale() seems more reliable during resolution changes e.g. going fullscreen (#8703, #4414) - float fbScaleX = SDL.GetWindowDisplayScale(window); - float fbScaleY = fbScaleX; -#else - 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; -#endif + float fbScaleX; + float fbScaleY; + if (OperatingSystem.IsMacOS()) + { + // SDL_GetWindowDisplayScale() seems more reliable during resolution changes e.g. going fullscreen (#8703, #4414) + fbScaleX = SDL.GetWindowDisplayScale(window); + fbScaleY = fbScaleX; + } + else + { + SDL.GetWindowSizeInPixels(window, out int displayW, out int displayH); + fbScaleX = w > 0 ? displayW / (float)w : 1.0f; + fbScaleY = h > 0 ? displayH / (float)h : 1.0f; + } out_size = new Vector2(w, h); out_framebuffer_scale = new Vector2(fbScaleX, fbScaleY); @@ -812,10 +821,7 @@ public unsafe static class ImGuiSDL3Platform 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.PlatformGetWindowFramebufferScale = DelegateStorage.GetWindowFramebufferScaleDelegate.GetPtrForDelegate(); platformIO.PlatformSetWindowFocus = DelegateStorage.SetWindowFocusDelegate.GetPtrForDelegate(); platformIO.PlatformGetWindowFocus = DelegateStorage.GetWindowFocusDelegate.GetPtrForDelegate(); platformIO.PlatformGetWindowMinimized = DelegateStorage.GetWindowMinimizedDelegate.GetPtrForDelegate(); @@ -825,6 +831,19 @@ public unsafe static class ImGuiSDL3Platform platformIO.PlatformRenderWindow = DelegateStorage.RenderWindowDelegate.GetPtrForDelegate(); platformIO.PlatformSwapBuffers = DelegateStorage.SwapBuffersDelegate.GetPtrForDelegate(); + if (OperatingSystem.IsWindows()) + { + platformIO.PlatformGetWindowPos = DelegateStorage.GetWindowPosWindowsDelegate.GetPtrForDelegate(); + platformIO.PlatformGetWindowSize = DelegateStorage.GetWindowSizeWindowsDelegate.GetPtrForDelegate(); + platformIO.PlatformGetWindowFramebufferScale = DelegateStorage.GetWindowFramebufferScaleWindowsDelegate.GetPtrForDelegate(); + } + else + { + platformIO.PlatformGetWindowPos = DelegateStorage.GetWindowPosDelegate.GetPtrForDelegate(); + platformIO.PlatformGetWindowSize = DelegateStorage.GetWindowSizeDelegate.GetPtrForDelegate(); + platformIO.PlatformGetWindowFramebufferScale = DelegateStorage.GetWindowFramebufferScaleDelegate.GetPtrForDelegate(); + } + // Register main window handle (owned by the main application, not by us). // This is mostly for simplicity and consistency so that our code (e.g. mouse handling) can use // the same logic for main and secondary viewports. @@ -840,13 +859,14 @@ public unsafe static class ImGuiSDL3Platform mainViewport.PlatformUserData = ImGuiUserData.Store(vd); mainViewport.PlatformHandle = (void*)(nint)vd.WindowID; -#if WINDOWS - mainViewport.PlatformHandleRaw = SDL.GetPointerProperty( - SDL.GetWindowProperties(window), - SDL.Properties.WindowWin32HWNDPointer, - nint.Zero - ).ToPointer(); -#endif + if (OperatingSystem.IsWindows()) + { + mainViewport.PlatformHandleRaw = SDL.GetPointerProperty( + SDL.GetWindowProperties(window), + SDL.Props.WindowWin32HWNDPointer, + nint.Zero + ).ToPointer(); + } } public static ImGuiViewportPtr? GetViewportForWindowId(uint id) @@ -863,11 +883,19 @@ public unsafe static class ImGuiSDL3Platform { viewport.PlatformHandle = (void*)(nint)SDL.GetWindowID(window); viewport.PlatformHandleRaw = (void*)nint.Zero; -#if _WIN32 && !__WINTR__ - viewport.PlatformHandleRaw = SDL.GetPointerProperty(SDL.GetWindowProperties(window), SDL.Props.WindowWin32HWNDPointer, 0).ToPointer(); -#elif __APPLE__ && SDL_VIDEO_DRIVER_COCOA - viewport.PlatformHandleRaw = SDL.GetPointerProperty(SDL.GetWindowProperties(window), SDL.Props.WindowCocoaWindow, 0).ToPointer(); -#endif + if (OperatingSystem.IsWindows()) + { + viewport.PlatformHandleRaw = SDL.GetPointerProperty(SDL.GetWindowProperties(window), SDL.Props.WindowWin32HWNDPointer, 0).ToPointer(); + } + else if (OperatingSystem.IsMacOS() && SDL.GetHint("SDL_VIDEO_DRIVER_COCOA") != null) + { + viewport.PlatformHandleRaw = SDL.GetPointerProperty(SDL.GetWindowProperties(window), SDL.Props.WindowCocoaWindowPointer, 0).ToPointer(); + } + // #if _WIN32 && !__WINTR__ + // viewport.PlatformHandleRaw = SDL.GetPointerProperty(SDL.GetWindowProperties(window), SDL.Props.WindowWin32HWNDPointer, 0).ToPointer(); + // #elif __APPLE__ && SDL_VIDEO_DRIVER_COCOA + // viewport.PlatformHandleRaw = SDL.GetPointerProperty(SDL.GetWindowProperties(window), SDL.Props.WindowCocoaWindow, 0).ToPointer(); + // #endif } public static void Dispose() @@ -965,6 +993,8 @@ public unsafe static class ImGuiSDL3Platform [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate Vector2 ViewportCallbackGetVec2(ImGuiViewportPtr ctx); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + internal delegate Vector2* ViewportCallbackGetVec2Windows(Vector2* result, ImGuiViewportPtr ctx); + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate bool ViewportCallbackBool(ImGuiViewportPtr ctx); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] internal delegate void ViewportCallbackSetString(ImGuiViewportPtr ctx, string title); @@ -1018,10 +1048,12 @@ public unsafe static class ImGuiSDL3Platform vd.Window = SDL.CreateWindow("No Title Yet", (int)viewport.Size.X, (int)viewport.Size.Y, flags); vd.WindowID = SDL.GetWindowID(vd.Window); -#if !__APPLE__ // On Mac, SDL3 parenting appears to prevent viewport from appearing on another monitor - if (vd.ParentWindow != nint.Zero) - SDL.SetWindowParent(vd.Window, vd.ParentWindow); -#endif + if (!OperatingSystem.IsMacOS()) // On Mac, SDL3 parenting appears to prevent viewport from appearing on another monitor + { + 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; @@ -1037,13 +1069,14 @@ public unsafe static class ImGuiSDL3Platform 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 + if (OperatingSystem.IsWindows()) + { + viewport.PlatformHandleRaw = SDL.GetPointerProperty( + SDL.GetWindowProperties(vd.Window), + SDL.Props.WindowWin32HWNDPointer, + nint.Zero + ).ToPointer(); + } } internal static readonly ViewportCallback DestroyWindowDelegate = DestroyWindow; @@ -1071,12 +1104,16 @@ public unsafe static class ImGuiSDL3Platform { ViewPortData vd = ImGuiUserData.Get(viewport.PlatformUserData)!; -#if __APPLE__ - // On macOS new windows should always appear on top so they aren't hidden under existing ones. - SDL.SetHint(SDL.Hints.WindowActivateWhenShown, "1"); -#else - SDL.SetHint(SDL.Hints.WindowActivateWhenShown, (viewport.Flags & ImGuiViewportFlags.NoFocusOnAppearing) != 0 ? "0" : "1"); -#endif + if (OperatingSystem.IsMacOS()) + { + // On macOS new windows should always appear on top so they aren't hidden under existing ones. + SDL.SetHint(SDL.Hints.WindowActivateWhenShown, "1"); + } + else + { + SDL.SetHint(SDL.Hints.WindowActivateWhenShown, (viewport.Flags & ImGuiViewportFlags.NoFocusOnAppearing) != 0 ? "0" : "1"); + } + SDL.ShowWindow(vd.Window); } @@ -1119,6 +1156,13 @@ public unsafe static class ImGuiSDL3Platform SDL.GetWindowPosition(vd.Window, out int x, out int y); return new Vector2(x, y); } + + internal static readonly ViewportCallbackGetVec2Windows GetWindowPosWindowsDelegate = GetWindowPosWindows; + private static Vector2* GetWindowPosWindows(Vector2* result, ImGuiViewportPtr viewport) + { + *result = GetWindowPos(viewport); + return result; + } internal static readonly ViewportCallbackSetVec2 SetWindowSizeDelegate = SetWindowSize; private static void SetWindowSize(ImGuiViewportPtr viewport, Vector2 size) @@ -1134,6 +1178,13 @@ public unsafe static class ImGuiSDL3Platform SDL.GetWindowSize(vd.Window, out int w, out int h); return new Vector2(w, h); } + + internal static readonly ViewportCallbackGetVec2Windows GetWindowSizeWindowsDelegate = GetWindowSizeWindows; + private static Vector2* GetWindowSizeWindows(Vector2* result, ImGuiViewportPtr viewport) + { + *result = GetWindowSize(viewport); + return result; + } internal static readonly ViewportCallbackGetVec2 GetWindowFramebufferScaleDelegate = GetWindowFramebufferScale; private static Vector2 GetWindowFramebufferScale(ImGuiViewportPtr viewport) @@ -1142,6 +1193,13 @@ public unsafe static class ImGuiSDL3Platform GetWindowSizeAndFramebufferScale(vd.Window, out _, out Vector2 framebufferScale); return framebufferScale; } + + internal static readonly ViewportCallbackGetVec2Windows GetWindowFramebufferScaleWindowsDelegate = GetWindowFramebufferScaleWindows; + private static Vector2* GetWindowFramebufferScaleWindows(Vector2* result, ImGuiViewportPtr viewport) + { + *result = GetWindowFramebufferScale(viewport); + return result; + } internal static readonly ViewportCallback SetWindowFocusDelegate = SetWindowFocus; private static void SetWindowFocus(ImGuiViewportPtr viewport) diff --git a/SDL3/OpenGLTexture.cs b/SDL3/OpenGLTexture.cs index 0d20ea9..9f5b7bd 100644 --- a/SDL3/OpenGLTexture.cs +++ b/SDL3/OpenGLTexture.cs @@ -105,7 +105,60 @@ public sealed unsafe class OpenGLTexture : IDisposable } } + public static OpenGLTexture FromBgra32(GL gl, nint pixels, int width, int height, int stride) + { + if (pixels == nint.Zero) + { + throw new ArgumentException("Pixels cannot be null.", nameof(pixels)); + } + + uint texture = CreateTexture(gl, pixels, width, height, stride, GLPixelFormat.Bgra); + return new OpenGLTexture(gl, texture, width, height); + } + + public static OpenGLTexture FromHandle(GL gl, uint handle, int width, int height) + { + if (handle == 0) + { + throw new ArgumentException("Texture handle cannot be zero.", nameof(handle)); + } + + return new OpenGLTexture(gl, handle, width, height); + } + + public void UpdateBgra32(nint pixels, int stride) + { + if (pixels == nint.Zero) + { + throw new ArgumentException("Pixels cannot be null.", nameof(pixels)); + } + + _gl.GetIntegerv(GLGetPName.TextureBinding2D, out int previousTexture); + _gl.BindTexture(GLTextureTarget.Texture2D, Handle); + + _gl.PixelStorei(GLPixelStoreParameter.UnpackAlignment, 1); + _gl.PixelStorei(GLPixelStoreParameter.UnpackRowLength, stride / 4); + _gl.TexSubImage2D( + GLTextureTarget.Texture2D, + 0, + 0, + 0, + Width, + Height, + GLPixelFormat.Bgra, + GLPixelType.UnsignedByte, + pixels); + _gl.PixelStorei(GLPixelStoreParameter.UnpackRowLength, 0); + + _gl.BindTexture(GLTextureTarget.Texture2D, (uint)previousTexture); + } + private static uint UploadRgba32(GL gl, SDL.Surface* surface) + { + return CreateTexture(gl, surface->Pixels, surface->Width, surface->Height, surface->Pitch, GLPixelFormat.Rgba); + } + + private static uint CreateTexture(GL gl, nint pixels, int width, int height, int stride, GLPixelFormat pixelFormat) { uint texture = 0; gl.GenTextures(1, ref texture); @@ -119,17 +172,17 @@ public sealed unsafe class OpenGLTexture : IDisposable gl.TexParameteri(GLTextureTarget.Texture2D, GLTextureParameterName.WrapT, (int)GLEnum.ClampToEdge); gl.PixelStorei(GLPixelStoreParameter.UnpackAlignment, 1); - gl.PixelStorei(GLPixelStoreParameter.UnpackRowLength, surface->Pitch / 4); + gl.PixelStorei(GLPixelStoreParameter.UnpackRowLength, stride / 4); gl.TexImage2D( GLTextureTarget.Texture2D, 0, GLInternalFormat.Rgba, - surface->Width, - surface->Height, + width, + height, 0, - GLPixelFormat.Rgba, + pixelFormat, GLPixelType.UnsignedByte, - surface->Pixels); + pixels); gl.PixelStorei(GLPixelStoreParameter.UnpackRowLength, 0); gl.BindTexture(GLTextureTarget.Texture2D, (uint)previousTexture); diff --git a/SDL3/SDL3Window.cs b/SDL3/SDL3Window.cs index fefc580..9235be8 100644 --- a/SDL3/SDL3Window.cs +++ b/SDL3/SDL3Window.cs @@ -42,7 +42,10 @@ public sealed unsafe class SDL3Window : IDisposable public SDL3Window(string name, int posX, int posY, int width, int height, SDL.WindowFlags flags = SDL.WindowFlags.Resizable | SDL.WindowFlags.HighPixelDensity) { if (OperatingSystem.IsLinux()) + { SDL.SetHint(SDL.Hints.VideoDriver, "x11"); + SDL.SetHint(SDL.Hints.VideoForceEGL, "1"); + } if (!SDL.Init(SDL.InitFlags.Events | SDL.InitFlags.Video | SDL.InitFlags.Gamepad)) throw new Exception($"SDL_Init failed: {SDL.GetError()}");