#if GODOT_PC using Godot; using ImGuiNET; using System; using System.Runtime.InteropServices; using Vector2 = System.Numerics.Vector2; namespace ImGuiGodot.Internal; internal sealed class GodotImGuiWindow : IDisposable { private readonly GCHandle _gcHandle; private readonly ImGuiViewportPtr _vp; private readonly Window _window; private readonly bool _isOwnedWindow = false; /// /// sub window /// public GodotImGuiWindow(ImGuiViewportPtr vp) { _gcHandle = GCHandle.Alloc(this); _vp = vp; _vp.PlatformUserData = (IntPtr)_gcHandle; _isOwnedWindow = true; Rect2I winRect = new(_vp.Pos.ToVector2I(), _vp.Size.ToVector2I()); Window mainWindow = ImGuiController.Instance.GetWindow(); if (mainWindow.GuiEmbedSubwindows) { if ((bool)ProjectSettings.GetSetting("display/window/subwindows/embed_subwindows")) { GD.PushWarning( "ImGui Viewports: 'display/window/subwindows/embed_subwindows' needs to be disabled"); } mainWindow.GuiEmbedSubwindows = false; } _window = new Window() { Borderless = true, Position = winRect.Position, Size = winRect.Size, Transparent = true, TransparentBg = true, AlwaysOnTop = vp.Flags.HasFlag(ImGuiViewportFlags.TopMost), Unfocusable = vp.Flags.HasFlag(ImGuiViewportFlags.NoFocusOnClick) }; _window.CloseRequested += () => _vp.PlatformRequestClose = true; _window.SizeChanged += () => _vp.PlatformRequestResize = true; _window.WindowInput += ImGuiController.WindowInputCallback; ImGuiController.Instance.AddChild(_window); // need to do this after AddChild _window.Transparent = true; // it's our window, so just draw directly to the root viewport var vprid = _window.GetViewportRid(); _vp.RendererUserData = (IntPtr)vprid.Id; _vp.PlatformHandle = _window.GetWindowId(); State.Instance.Renderer.InitViewport(vprid); RenderingServer.ViewportSetTransparentBackground(_window.GetViewportRid(), true); } /// /// main window /// public GodotImGuiWindow(ImGuiViewportPtr vp, Window gw, Rid mainSubViewport) { _gcHandle = GCHandle.Alloc(this); _vp = vp; _vp.PlatformUserData = (IntPtr)_gcHandle; _window = gw; _vp.RendererUserData = (IntPtr)mainSubViewport.Id; } public void Dispose() { if (_gcHandle.IsAllocated) { if (_isOwnedWindow) { State.Instance.Renderer .CloseViewport(Util.ConstructRid((ulong)_vp.RendererUserData)); _window.GetParent().RemoveChild(_window); _window.Free(); } _gcHandle.Free(); } } public void ShowWindow() { _window.Show(); } public void SetWindowPos(Vector2I pos) { _window.Position = pos; } public Vector2I GetWindowPos() { return _window.Position; } public void SetWindowSize(Vector2I size) { _window.Size = size; } public Vector2I GetWindowSize() { return _window.Size; } public void SetWindowFocus() { _window.GrabFocus(); } public bool GetWindowFocus() { return _window.HasFocus(); } public bool GetWindowMinimized() { return _window.Mode.HasFlag(Window.ModeEnum.Minimized); } public void SetWindowTitle(string title) { _window.Title = title; } } internal static class ViewportsExts { internal static Vector2 ToImVec2(this Vector2I v) { return new Vector2(v.X, v.Y); } internal static Vector2I ToVector2I(this Vector2 v) { return new Vector2I((int)v.X, (int)v.Y); } } internal sealed partial class Viewports { [LibraryImport("cimgui")] [UnmanagedCallConv(CallConvs = [typeof(System.Runtime.CompilerServices.CallConvCdecl)])] private static unsafe partial void ImGuiPlatformIO_Set_Platform_GetWindowPos( ImGuiPlatformIO* platform_io, IntPtr funcPtr); [LibraryImport("cimgui")] [UnmanagedCallConv(CallConvs = [typeof(System.Runtime.CompilerServices.CallConvCdecl)])] private static unsafe partial void ImGuiPlatformIO_Set_Platform_GetWindowSize( ImGuiPlatformIO* platform_io, IntPtr funcPtr); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] private delegate void Platform_CreateWindow(ImGuiViewportPtr vp); private static readonly Platform_CreateWindow _createWindow = Godot_CreateWindow; [UnmanagedFunctionPointer(CallingConvention.Cdecl)] private delegate void Platform_DestroyWindow(ImGuiViewportPtr vp); private static readonly Platform_DestroyWindow _destroyWindow = Godot_DestroyWindow; [UnmanagedFunctionPointer(CallingConvention.Cdecl)] private delegate void Platform_ShowWindow(ImGuiViewportPtr vp); private static readonly Platform_ShowWindow _showWindow = Godot_ShowWindow; [UnmanagedFunctionPointer(CallingConvention.Cdecl)] private delegate void Platform_SetWindowPos(ImGuiViewportPtr vp, Vector2 pos); private static readonly Platform_SetWindowPos _setWindowPos = Godot_SetWindowPos; [UnmanagedFunctionPointer(CallingConvention.Cdecl)] private delegate void Platform_GetWindowPos(ImGuiViewportPtr vp, out Vector2 pos); private static readonly Platform_GetWindowPos _getWindowPos = Godot_GetWindowPos; [UnmanagedFunctionPointer(CallingConvention.Cdecl)] private delegate void Platform_SetWindowSize(ImGuiViewportPtr vp, Vector2 pos); private static readonly Platform_SetWindowSize _setWindowSize = Godot_SetWindowSize; [UnmanagedFunctionPointer(CallingConvention.Cdecl)] private delegate void Platform_GetWindowSize(ImGuiViewportPtr vp, out Vector2 size); private static readonly Platform_GetWindowSize _getWindowSize = Godot_GetWindowSize; [UnmanagedFunctionPointer(CallingConvention.Cdecl)] private delegate void Platform_SetWindowFocus(ImGuiViewportPtr vp); private static readonly Platform_SetWindowFocus _setWindowFocus = Godot_SetWindowFocus; [UnmanagedFunctionPointer(CallingConvention.Cdecl)] private delegate bool Platform_GetWindowFocus(ImGuiViewportPtr vp); private static readonly Platform_GetWindowFocus _getWindowFocus = Godot_GetWindowFocus; [UnmanagedFunctionPointer(CallingConvention.Cdecl)] private delegate bool Platform_GetWindowMinimized(ImGuiViewportPtr vp); private static readonly Platform_GetWindowMinimized _getWindowMinimized = Godot_GetWindowMinimized; [UnmanagedFunctionPointer(CallingConvention.Cdecl)] private delegate void Platform_SetWindowTitle(ImGuiViewportPtr vp, string title); private static readonly Platform_SetWindowTitle _setWindowTitle = Godot_SetWindowTitle; private GodotImGuiWindow _mainWindow = null!; private static void UpdateMonitors() { var pio = ImGui.GetPlatformIO(); int screenCount = DisplayServer.GetScreenCount(); // workaround for lack of ImVector constructor unsafe { int bytes = screenCount * sizeof(ImGuiPlatformMonitor); if (pio.NativePtr->Monitors.Data != IntPtr.Zero) ImGui.MemFree(pio.NativePtr->Monitors.Data); *&pio.NativePtr->Monitors.Data = ImGui.MemAlloc((uint)bytes); *&pio.NativePtr->Monitors.Capacity = screenCount; *&pio.NativePtr->Monitors.Size = screenCount; } for (int i = 0; i < screenCount; ++i) { var monitor = pio.Monitors[i]; monitor.MainPos = DisplayServer.ScreenGetPosition(i).ToImVec2(); monitor.MainSize = DisplayServer.ScreenGetSize(i).ToImVec2(); monitor.DpiScale = DisplayServer.ScreenGetScale(i); var r = DisplayServer.ScreenGetUsableRect(i); monitor.WorkPos = r.Position.ToImVec2(); monitor.WorkSize = r.Size.ToImVec2(); } // TODO: add monitor if headless } private static unsafe void InitPlatformInterface() { var pio = ImGui.GetPlatformIO().NativePtr; pio->Platform_CreateWindow = Marshal.GetFunctionPointerForDelegate(_createWindow); pio->Platform_DestroyWindow = Marshal.GetFunctionPointerForDelegate(_destroyWindow); pio->Platform_ShowWindow = Marshal.GetFunctionPointerForDelegate(_showWindow); pio->Platform_SetWindowPos = Marshal.GetFunctionPointerForDelegate(_setWindowPos); //pio->Platform_GetWindowPos = Marshal.GetFunctionPointerForDelegate(_getWindowPos); pio->Platform_SetWindowSize = Marshal.GetFunctionPointerForDelegate(_setWindowSize); //pio->Platform_GetWindowSize = Marshal.GetFunctionPointerForDelegate(_getWindowSize); pio->Platform_SetWindowFocus = Marshal.GetFunctionPointerForDelegate(_setWindowFocus); pio->Platform_GetWindowFocus = Marshal.GetFunctionPointerForDelegate(_getWindowFocus); pio->Platform_GetWindowMinimized = Marshal.GetFunctionPointerForDelegate( _getWindowMinimized); pio->Platform_SetWindowTitle = Marshal.GetFunctionPointerForDelegate(_setWindowTitle); ImGuiPlatformIO_Set_Platform_GetWindowPos( pio, Marshal.GetFunctionPointerForDelegate(_getWindowPos)); ImGuiPlatformIO_Set_Platform_GetWindowSize( pio, Marshal.GetFunctionPointerForDelegate(_getWindowSize)); } public Viewports() { InitPlatformInterface(); UpdateMonitors(); } public void SetMainWindow(Window window, Rid mainSubViewport) { _mainWindow?.Dispose(); _mainWindow = new GodotImGuiWindow(ImGui.GetMainViewport(), window, mainSubViewport); } private static void Godot_CreateWindow(ImGuiViewportPtr vp) { _ = new GodotImGuiWindow(vp); } private static void Godot_DestroyWindow(ImGuiViewportPtr vp) { if (vp.PlatformUserData != IntPtr.Zero) { var window = (GodotImGuiWindow)GCHandle.FromIntPtr(vp.PlatformUserData).Target!; window.Dispose(); vp.PlatformUserData = IntPtr.Zero; vp.RendererUserData = IntPtr.Zero; } } private static void Godot_ShowWindow(ImGuiViewportPtr vp) { var window = (GodotImGuiWindow)GCHandle.FromIntPtr(vp.PlatformUserData).Target!; window.ShowWindow(); } private static void Godot_SetWindowPos(ImGuiViewportPtr vp, Vector2 pos) { var window = (GodotImGuiWindow)GCHandle.FromIntPtr(vp.PlatformUserData).Target!; window.SetWindowPos(pos.ToVector2I()); } private static void Godot_GetWindowPos(ImGuiViewportPtr vp, out Vector2 pos) { var window = (GodotImGuiWindow)GCHandle.FromIntPtr(vp.PlatformUserData).Target!; pos = window.GetWindowPos().ToImVec2(); } private static void Godot_SetWindowSize(ImGuiViewportPtr vp, Vector2 size) { var window = (GodotImGuiWindow)GCHandle.FromIntPtr(vp.PlatformUserData).Target!; window.SetWindowSize(size.ToVector2I()); } private static void Godot_GetWindowSize(ImGuiViewportPtr vp, out Vector2 size) { var window = (GodotImGuiWindow)GCHandle.FromIntPtr(vp.PlatformUserData).Target!; size = window.GetWindowSize().ToImVec2(); } private static void Godot_SetWindowFocus(ImGuiViewportPtr vp) { var window = (GodotImGuiWindow)GCHandle.FromIntPtr(vp.PlatformUserData).Target!; window.SetWindowFocus(); } private static bool Godot_GetWindowFocus(ImGuiViewportPtr vp) { var window = (GodotImGuiWindow)GCHandle.FromIntPtr(vp.PlatformUserData).Target!; return window.GetWindowFocus(); } private static bool Godot_GetWindowMinimized(ImGuiViewportPtr vp) { var window = (GodotImGuiWindow)GCHandle.FromIntPtr(vp.PlatformUserData).Target!; return window.GetWindowMinimized(); } private static void Godot_SetWindowTitle(ImGuiViewportPtr vp, string title) { var window = (GodotImGuiWindow)GCHandle.FromIntPtr(vp.PlatformUserData).Target!; window.SetWindowTitle(title); } } #endif