2025-07-28 23:08:43 +02:00

566 lines
21 KiB
C#

using Echo.Core.Common.Packed;
using ImGuiNET;
using SDL2;
using SDL2Demo.SdlWrapper;
using skyscraper8.Skyscraper.Plugins;
using System;
using System.Numerics;
using System.Runtime.InteropServices;
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;
using testdrid.SdlWrapper;
namespace Echo.UserInterface.Backend;
using static SDL;
/// <summary>
/// Implementation reference:
/// https://github.com/ocornut/imgui/blob/e23c5edd5fdef85ea0f5418b1368adb94bf86230/backends/imgui_impl_sdl.cpp
/// https://github.com/ocornut/imgui/blob/e23c5edd5fdef85ea0f5418b1368adb94bf86230/backends/imgui_impl_sdlrenderer.cpp
/// </summary>
public sealed unsafe class ImGuiDevice : IDisposable, IEventConsumer
{
private static PluginLogger logger = PluginLogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
public ImGuiDevice(IntPtr window, IntPtr renderer)
{
this.window = window;
this.renderer = renderer;
io = ImGui.GetIO();
//Assign global names and flags
AssignBackendNames();
io.BackendFlags |= ImGuiBackendFlags.HasMouseCursors;
io.BackendFlags |= ImGuiBackendFlags.HasSetMousePos;
io.BackendFlags |= ImGuiBackendFlags.RendererHasVtxOffset;
//Link viewport data
SDL_SysWMinfo info = default;
SDL_VERSION(out info.version);
if (SDL_GetWindowWMInfo(window, ref info) == SDL_bool.SDL_TRUE)
{
ImGuiViewportPtr viewport = ImGui.GetMainViewport();
viewport.PlatformHandleRaw = info.info.win.window;
}
//Setup SDL hint
SDL_SetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, "1");
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "nearest"); //Can be nearest, linear, or best
}
public void Initialize()
{
//Create resources
CreateMouseCursors();
CreateFontTexture();
CreateClipboardSetup();
}
readonly IntPtr window;
readonly IntPtr renderer;
ImGuiIOPtr io;
IntPtr[] mouseCursors;
IntPtr fontTexture;
int mouseButtonDownCount;
int pendingMouseLeaveFrame;
bool disposed;
static readonly SetClipboardTextFn SetClipboardText = (_, text) => SDL_SetClipboardText(text);
static readonly GetClipboardTextFn GetClipboardText = _ => SDL_GetClipboardText();
public void ProcessEvent(in SDL_Event sdlEvent)
{
switch (sdlEvent.type)
{
case SDL_EventType.SDL_MOUSEMOTION:
{
io.AddMousePosEvent(sdlEvent.motion.x, sdlEvent.motion.y);
break;
}
case SDL_EventType.SDL_MOUSEWHEEL:
{
io.AddMouseWheelEvent(sdlEvent.wheel.x, sdlEvent.wheel.y);
break;
}
case SDL_EventType.SDL_MOUSEBUTTONDOWN:
case SDL_EventType.SDL_MOUSEBUTTONUP:
{
ProcessMouseButtonEvent(sdlEvent.button);
break;
}
case SDL_EventType.SDL_TEXTINPUT:
{
fixed (byte* ptr = sdlEvent.text.text) ImGuiNative.ImGuiIO_AddInputCharactersUTF8(io.NativePtr, ptr);
break;
}
case SDL_EventType.SDL_KEYDOWN:
case SDL_EventType.SDL_KEYUP:
{
ProcessKeyboardEvent(sdlEvent.key);
break;
}
case SDL_EventType.SDL_WINDOWEVENT:
{
ProcessWindowEvent(sdlEvent.window);
break;
}
}
}
public void NewFrame(in TimeSpan deltaTime)
{
io.DeltaTime = (float)deltaTime.TotalSeconds;
RefreshDisplaySize();
UpdateMouseData();
UpdateMouseCursor();
}
public void Render(ImDrawDataPtr data)
{
//Setup clip information
Vector2 scale = data.FramebufferScale;
Float4 clipSize = Widen(scale * data.DisplaySize);
if (clipSize.X <= 0f || clipSize.Y <= 0f) return;
SDL_Rect zerocopy = new SDL_Rect();
//Render
if (SDL_RenderSetScale(renderer, scale.X, scale.Y) != 0)
throw SdlException.GenerateException();
if (SDL_RenderSetClipRect(renderer, ref zerocopy) != 0)
throw SdlException.GenerateException();
Float4 clipOffset = Widen(data.DisplayPos);
var lists = data.CmdListsRange;
for (int i = 0; i < lists.Count; i++)
{
ExecuteCommandList(lists[i], clipOffset, clipSize);
}
// SDL_RenderPresent(renderer);
static Float4 Widen(Vector2 vector) => new Float4(vector.AsVector128()).XYXY;
}
public IntPtr CreateTexture(Int2 size, bool streaming, bool bigEndian = false)
{
int access = streaming ?
(int)SDL_TextureAccess.SDL_TEXTUREACCESS_STREAMING :
(int)SDL_TextureAccess.SDL_TEXTUREACCESS_STATIC;
uint format = bigEndian ? SDL_PIXELFORMAT_RGBA8888 : SDL_PIXELFORMAT_ABGR8888;
IntPtr texture = SDL_CreateTexture(renderer, format, access, size.X, size.Y);
if (texture == IntPtr.Zero)
throw SdlException.GenerateException();
if (SDL_SetTextureBlendMode(texture, SDL_BlendMode.SDL_BLENDMODE_BLEND) != 0)
throw SdlException.GenerateException();
return texture;
}
public void DestroyTexture(ref IntPtr texture)
{
if (texture == IntPtr.Zero) return;
SDL_DestroyTexture(texture);
texture = IntPtr.Zero;
}
public void Dispose()
{
if (disposed) return;
disposed = true;
DestroyMouseCursors();
DestroyFontTexture();
DestroyClipboardSetup();
io = null;
}
void AssignBackendNames()
{
if (SDL_GetRendererInfo(renderer, out SDL_RendererInfo info) != 0)
throw SdlException.GenerateException();
var name = (Marshal.PtrToStringAnsi(info.name) ?? "unknown").ToUpper();
var size = new Int2(info.max_texture_width, info.max_texture_height);
io.NativePtr->BackendPlatformName = (byte*)Marshal.StringToHGlobalAnsi("SDL2 & Dear ImGui for C#");
io.NativePtr->BackendRendererName = (byte*)Marshal.StringToHGlobalAnsi($"{name} {size.X}x{size.Y}");
}
void CreateMouseCursors()
{
mouseCursors = new IntPtr[(int)ImGuiMouseCursor.COUNT];
mouseCursors[(int)ImGuiMouseCursor.Arrow] = SDL_CreateSystemCursor(SDL_SystemCursor.SDL_SYSTEM_CURSOR_ARROW);
mouseCursors[(int)ImGuiMouseCursor.TextInput] = SDL_CreateSystemCursor(SDL_SystemCursor.SDL_SYSTEM_CURSOR_IBEAM);
mouseCursors[(int)ImGuiMouseCursor.ResizeAll] = SDL_CreateSystemCursor(SDL_SystemCursor.SDL_SYSTEM_CURSOR_SIZEALL);
mouseCursors[(int)ImGuiMouseCursor.ResizeNS] = SDL_CreateSystemCursor(SDL_SystemCursor.SDL_SYSTEM_CURSOR_SIZENS);
mouseCursors[(int)ImGuiMouseCursor.ResizeEW] = SDL_CreateSystemCursor(SDL_SystemCursor.SDL_SYSTEM_CURSOR_SIZEWE);
mouseCursors[(int)ImGuiMouseCursor.ResizeNESW] = SDL_CreateSystemCursor(SDL_SystemCursor.SDL_SYSTEM_CURSOR_SIZENESW);
mouseCursors[(int)ImGuiMouseCursor.ResizeNWSE] = SDL_CreateSystemCursor(SDL_SystemCursor.SDL_SYSTEM_CURSOR_SIZENWSE);
mouseCursors[(int)ImGuiMouseCursor.Hand] = SDL_CreateSystemCursor(SDL_SystemCursor.SDL_SYSTEM_CURSOR_HAND);
mouseCursors[(int)ImGuiMouseCursor.NotAllowed] = SDL_CreateSystemCursor(SDL_SystemCursor.SDL_SYSTEM_CURSOR_NO);
}
void DestroyMouseCursors()
{
if (mouseCursors == null) return;
foreach (IntPtr cursor in mouseCursors)
{
if (cursor == IntPtr.Zero) continue;
SDL_FreeCursor(cursor);
}
mouseCursors = null;
}
void CreateFontTexture()
{
io.Fonts.GetTexDataAsRGBA32(out IntPtr pixels, out int width, out int height);
fontTexture = CreateTexture(new Int2(width, height), false);
if (SDL_UpdateTexture(fontTexture, IntPtr.Zero, pixels, width * sizeof(uint)) != 0)
throw SdlException.GenerateException();
if (SDL_SetTextureBlendMode(fontTexture, SDL_BlendMode.SDL_BLENDMODE_BLEND) != 0)
throw SdlException.GenerateException();
if (SDL_SetTextureScaleMode(fontTexture, SDL_ScaleMode.SDL_ScaleModeLinear) != 0)
throw SdlException.GenerateException();
io.Fonts.SetTexID(fontTexture);
}
void DestroyFontTexture()
{
DestroyTexture(ref fontTexture);
io.Fonts.SetTexID(IntPtr.Zero);
}
void CreateClipboardSetup()
{
io.SetClipboardTextFn = Marshal.GetFunctionPointerForDelegate(SetClipboardText);
io.GetClipboardTextFn = Marshal.GetFunctionPointerForDelegate(GetClipboardText);
}
void DestroyClipboardSetup()
{
io.SetClipboardTextFn = IntPtr.Zero;
io.GetClipboardTextFn = IntPtr.Zero;
}
void ProcessMouseButtonEvent(SDL_MouseButtonEvent mouseButtonEvent)
{
int mouseButton = (uint)mouseButtonEvent.button switch
{
SDL_BUTTON_LEFT => 0,
SDL_BUTTON_RIGHT => 1,
SDL_BUTTON_MIDDLE => 2,
SDL_BUTTON_X1 => 3,
SDL_BUTTON_X2 => 4,
_ => -1
};
if (mouseButton < 0) return;
bool down = mouseButtonEvent.type == SDL_EventType.SDL_MOUSEBUTTONDOWN;
io.AddMouseButtonEvent(mouseButton, down);
mouseButtonDownCount += down ? 1 : -1;
}
void ProcessKeyboardEvent(in SDL_KeyboardEvent keyboardEvent)
{
ref readonly SDL_Keysym key = ref keyboardEvent.keysym;
bool down = keyboardEvent.type == SDL_EventType.SDL_KEYDOWN;
io.AddKeyEvent(ImGuiKey.ModCtrl, (key.mod & SDL_Keymod.KMOD_CTRL) != 0);
io.AddKeyEvent(ImGuiKey.ModShift, (key.mod & SDL_Keymod.KMOD_SHIFT) != 0);
io.AddKeyEvent(ImGuiKey.ModAlt, (key.mod & SDL_Keymod.KMOD_ALT) != 0);
io.AddKeyEvent(ImGuiKey.ModSuper, (key.mod & SDL_Keymod.KMOD_GUI) != 0);
io.AddKeyEvent(SDL_KeycodeToImGuiKey(key.sym), down);
}
public static ImGuiKey SDL_KeycodeToImGuiKey(SDL_Keycode key) => key switch
{
SDL_Keycode.SDLK_TAB => ImGuiKey.Tab,
SDL_Keycode.SDLK_LEFT => ImGuiKey.LeftArrow,
SDL_Keycode.SDLK_RIGHT => ImGuiKey.RightArrow,
SDL_Keycode.SDLK_UP => ImGuiKey.UpArrow,
SDL_Keycode.SDLK_DOWN => ImGuiKey.DownArrow,
SDL_Keycode.SDLK_PAGEUP => ImGuiKey.PageUp,
SDL_Keycode.SDLK_PAGEDOWN => ImGuiKey.PageDown,
SDL_Keycode.SDLK_HOME => ImGuiKey.Home,
SDL_Keycode.SDLK_END => ImGuiKey.End,
SDL_Keycode.SDLK_INSERT => ImGuiKey.Insert,
SDL_Keycode.SDLK_DELETE => ImGuiKey.Delete,
SDL_Keycode.SDLK_BACKSPACE => ImGuiKey.Backspace,
SDL_Keycode.SDLK_SPACE => ImGuiKey.Space,
SDL_Keycode.SDLK_RETURN => ImGuiKey.Enter,
SDL_Keycode.SDLK_ESCAPE => ImGuiKey.Escape,
SDL_Keycode.SDLK_QUOTE => ImGuiKey.Apostrophe,
SDL_Keycode.SDLK_COMMA => ImGuiKey.Comma,
SDL_Keycode.SDLK_MINUS => ImGuiKey.Minus,
SDL_Keycode.SDLK_PERIOD => ImGuiKey.Period,
SDL_Keycode.SDLK_SLASH => ImGuiKey.Slash,
SDL_Keycode.SDLK_SEMICOLON => ImGuiKey.Semicolon,
SDL_Keycode.SDLK_EQUALS => ImGuiKey.Equal,
SDL_Keycode.SDLK_LEFTBRACKET => ImGuiKey.LeftBracket,
SDL_Keycode.SDLK_BACKSLASH => ImGuiKey.Backslash,
SDL_Keycode.SDLK_RIGHTBRACKET => ImGuiKey.RightBracket,
SDL_Keycode.SDLK_BACKQUOTE => ImGuiKey.GraveAccent,
SDL_Keycode.SDLK_CAPSLOCK => ImGuiKey.CapsLock,
SDL_Keycode.SDLK_SCROLLLOCK => ImGuiKey.ScrollLock,
SDL_Keycode.SDLK_NUMLOCKCLEAR => ImGuiKey.NumLock,
SDL_Keycode.SDLK_PRINTSCREEN => ImGuiKey.PrintScreen,
SDL_Keycode.SDLK_PAUSE => ImGuiKey.Pause,
SDL_Keycode.SDLK_KP_0 => ImGuiKey.Keypad0,
SDL_Keycode.SDLK_KP_1 => ImGuiKey.Keypad1,
SDL_Keycode.SDLK_KP_2 => ImGuiKey.Keypad2,
SDL_Keycode.SDLK_KP_3 => ImGuiKey.Keypad3,
SDL_Keycode.SDLK_KP_4 => ImGuiKey.Keypad4,
SDL_Keycode.SDLK_KP_5 => ImGuiKey.Keypad5,
SDL_Keycode.SDLK_KP_6 => ImGuiKey.Keypad6,
SDL_Keycode.SDLK_KP_7 => ImGuiKey.Keypad7,
SDL_Keycode.SDLK_KP_8 => ImGuiKey.Keypad8,
SDL_Keycode.SDLK_KP_9 => ImGuiKey.Keypad9,
SDL_Keycode.SDLK_KP_PERIOD => ImGuiKey.KeypadDecimal,
SDL_Keycode.SDLK_KP_DIVIDE => ImGuiKey.KeypadDivide,
SDL_Keycode.SDLK_KP_MULTIPLY => ImGuiKey.KeypadMultiply,
SDL_Keycode.SDLK_KP_MINUS => ImGuiKey.KeypadSubtract,
SDL_Keycode.SDLK_KP_PLUS => ImGuiKey.KeypadAdd,
SDL_Keycode.SDLK_KP_ENTER => ImGuiKey.KeypadEnter,
SDL_Keycode.SDLK_KP_EQUALS => ImGuiKey.KeypadEqual,
SDL_Keycode.SDLK_LCTRL => ImGuiKey.LeftCtrl,
SDL_Keycode.SDLK_LSHIFT => ImGuiKey.LeftShift,
SDL_Keycode.SDLK_LALT => ImGuiKey.LeftAlt,
SDL_Keycode.SDLK_LGUI => ImGuiKey.LeftSuper,
SDL_Keycode.SDLK_RCTRL => ImGuiKey.RightCtrl,
SDL_Keycode.SDLK_RSHIFT => ImGuiKey.RightShift,
SDL_Keycode.SDLK_RALT => ImGuiKey.RightAlt,
SDL_Keycode.SDLK_RGUI => ImGuiKey.RightSuper,
SDL_Keycode.SDLK_APPLICATION => ImGuiKey.Menu,
SDL_Keycode.SDLK_0 => ImGuiKey._0,
SDL_Keycode.SDLK_1 => ImGuiKey._1,
SDL_Keycode.SDLK_2 => ImGuiKey._2,
SDL_Keycode.SDLK_3 => ImGuiKey._3,
SDL_Keycode.SDLK_4 => ImGuiKey._4,
SDL_Keycode.SDLK_5 => ImGuiKey._5,
SDL_Keycode.SDLK_6 => ImGuiKey._6,
SDL_Keycode.SDLK_7 => ImGuiKey._7,
SDL_Keycode.SDLK_8 => ImGuiKey._8,
SDL_Keycode.SDLK_9 => ImGuiKey._9,
SDL_Keycode.SDLK_a => ImGuiKey.A,
SDL_Keycode.SDLK_b => ImGuiKey.B,
SDL_Keycode.SDLK_c => ImGuiKey.C,
SDL_Keycode.SDLK_d => ImGuiKey.D,
SDL_Keycode.SDLK_e => ImGuiKey.E,
SDL_Keycode.SDLK_f => ImGuiKey.F,
SDL_Keycode.SDLK_g => ImGuiKey.G,
SDL_Keycode.SDLK_h => ImGuiKey.H,
SDL_Keycode.SDLK_i => ImGuiKey.I,
SDL_Keycode.SDLK_j => ImGuiKey.J,
SDL_Keycode.SDLK_k => ImGuiKey.K,
SDL_Keycode.SDLK_l => ImGuiKey.L,
SDL_Keycode.SDLK_m => ImGuiKey.M,
SDL_Keycode.SDLK_n => ImGuiKey.N,
SDL_Keycode.SDLK_o => ImGuiKey.O,
SDL_Keycode.SDLK_p => ImGuiKey.P,
SDL_Keycode.SDLK_q => ImGuiKey.Q,
SDL_Keycode.SDLK_r => ImGuiKey.R,
SDL_Keycode.SDLK_s => ImGuiKey.S,
SDL_Keycode.SDLK_t => ImGuiKey.T,
SDL_Keycode.SDLK_u => ImGuiKey.U,
SDL_Keycode.SDLK_v => ImGuiKey.V,
SDL_Keycode.SDLK_w => ImGuiKey.W,
SDL_Keycode.SDLK_x => ImGuiKey.X,
SDL_Keycode.SDLK_y => ImGuiKey.Y,
SDL_Keycode.SDLK_z => ImGuiKey.Z,
SDL_Keycode.SDLK_F1 => ImGuiKey.F1,
SDL_Keycode.SDLK_F2 => ImGuiKey.F2,
SDL_Keycode.SDLK_F3 => ImGuiKey.F3,
SDL_Keycode.SDLK_F4 => ImGuiKey.F4,
SDL_Keycode.SDLK_F5 => ImGuiKey.F5,
SDL_Keycode.SDLK_F6 => ImGuiKey.F6,
SDL_Keycode.SDLK_F7 => ImGuiKey.F7,
SDL_Keycode.SDLK_F8 => ImGuiKey.F8,
SDL_Keycode.SDLK_F9 => ImGuiKey.F9,
SDL_Keycode.SDLK_F10 => ImGuiKey.F10,
SDL_Keycode.SDLK_F11 => ImGuiKey.F11,
SDL_Keycode.SDLK_F12 => ImGuiKey.F12,
_ => ImGuiKey.None
};
void ProcessWindowEvent(in SDL_WindowEvent windowEvent)
{
switch (windowEvent.windowEvent)
{
case SDL_WindowEventID.SDL_WINDOWEVENT_ENTER:
{
pendingMouseLeaveFrame = 0;
break;
}
case SDL_WindowEventID.SDL_WINDOWEVENT_LEAVE:
{
pendingMouseLeaveFrame = ImGui.GetFrameCount() + 1;
break;
}
case SDL_WindowEventID.SDL_WINDOWEVENT_FOCUS_GAINED:
{
io.AddFocusEvent(true);
break;
}
case SDL_WindowEventID.SDL_WINDOWEVENT_FOCUS_LOST:
{
io.AddFocusEvent(false);
break;
}
}
}
void RefreshDisplaySize()
{
SDL_GetWindowSize(window, out int width, out int height);
var size = io.DisplaySize = new Vector2(width, height);
if (SDL_GetRendererOutputSize(renderer, out int displayWidth, out int displayHeight) != 0)
throw SdlException.GenerateException();
if ((SDL_GetWindowFlags(window) & (uint)SDL_WindowFlags.SDL_WINDOW_MINIMIZED) != 0) size = Vector2.Zero;
if (size.X > 0f && size.Y > 0f) io.DisplayFramebufferScale = new Vector2(displayWidth, displayHeight) / size;
}
private long nandate = 0;
void UpdateMouseData()
{
if (pendingMouseLeaveFrame >= ImGui.GetFrameCount() && mouseButtonDownCount == 0)
{
io.AddMousePosEvent(float.MinValue, float.MinValue);
pendingMouseLeaveFrame = 0;
}
if (SDL_CaptureMouse(mouseButtonDownCount != 0 ? SDL_bool.SDL_TRUE : SDL_bool.SDL_FALSE) != 0)
{
nandate++;
//throw SdlException.GenerateException();
}
if (window != SDL_GetKeyboardFocus()) return;
if (io.WantSetMousePos) SDL_WarpMouseInWindow(window, (int)io.MousePos.X, (int)io.MousePos.Y);
if (mouseButtonDownCount == 0)
{
_ = SDL_GetGlobalMouseState(out int globalX, out int globalY);
SDL_GetWindowPosition(window, out int windowX, out int windowY);
io.AddMousePosEvent(globalX - windowX, globalY - windowY);
}
}
void UpdateMouseCursor()
{
if ((io.ConfigFlags & ImGuiConfigFlags.NoMouseCursorChange) != 0) return;
ImGuiMouseCursor cursor = ImGui.GetMouseCursor();
if (!io.MouseDrawCursor && cursor != ImGuiMouseCursor.None)
{
IntPtr mouseCursor = mouseCursors[(int)cursor];
const int Fallback = (int)ImGuiMouseCursor.Arrow;
if (mouseCursor == IntPtr.Zero) mouseCursor = mouseCursors[Fallback];
SDL_SetCursor(mouseCursor);
_ = SDL_ShowCursor((int)SDL_bool.SDL_TRUE);
}
else _ = SDL_ShowCursor((int)SDL_bool.SDL_FALSE);
}
private uint numErrors;
void ExecuteCommandList(ImDrawListPtr list, in Float4 clipOffset, in Float4 clipSize)
{
ImPtrVector<ImDrawCmdPtr> buffer = list.CmdBuffer;
var vertices = (ImDrawVert*)list.VtxBuffer.Data;
var indices = (ushort*)list.IdxBuffer.Data;
for (int i = 0; i < buffer.Size; i++)
{
ImDrawCmdPtr command = buffer[i];
if (command.UserCallback == IntPtr.Zero)
{
var clipRect = new Float4(command.ClipRect.AsVector128()) - clipOffset;
Float4 clipMin = clipRect.Max(Float4.Zero); //(minX, minY, ____, ____).Max(zero)
Float4 clipMax = clipRect.Min(clipSize); //(____, ____, maxX, maxY).Min(size)
if (clipMax.Z <= clipMin.X || clipMax.W <= clipMin.Y) continue;
clipMin = clipMin.XYXY; //(minX, minY, minX, minY)
clipMax = clipMax.__ZW; //(0000, 0000, maxX, maxY)
Float4 clip = (clipMax - clipMin).Absoluted; //(-minX, -minY, maxX - minX, maxY - minY).Absoluted
var rect = Sse2.ConvertToVector128Int32(clip.v); //( X, Y, width, height)
ImDrawVert* vertex = vertices + command.VtxOffset;
int stride = sizeof(ImDrawVert);
lock (Renderer.Lockable)
{
if (SDL_RenderGeometryRaw
(
renderer, command.TextureId,
(float*)&vertex->pos, stride,
(int*)&vertex->col, stride,
(float*)&vertex->uv, stride,
list.VtxBuffer.Size - (int)command.VtxOffset,
(IntPtr)(indices + command.IdxOffset),
(int)command.ElemCount, sizeof(ushort)
) != 0)
{
numErrors++;
SdlException sdlException = SdlException.GenerateException();
//We can tolerate the Geometry Renderer going sideways a few times
logger.Log(PluginLogLevel.Error, String.Format("{0}, ({1}x)",sdlException.ToString(),numErrors));
}
}
}
else
{
var callback = Marshal.GetDelegateForFunctionPointer<ImDrawCallback>(command.UserCallback);
callback(new IntPtr(list), new IntPtr(command)); //Perform user callback, not really used
}
}
}
[DllImport("SDL2", CallingConvention = CallingConvention.Cdecl)]
public static extern unsafe int SDL_RenderGeometryRaw(IntPtr renderer,
IntPtr texture,
float* xy,
int xy_stride,
int* color,
int color_stride,
float* uv,
int uv_stride,
int num_vertices,
IntPtr indices,
int num_indices,
int size_indices);
delegate void SetClipboardTextFn(IntPtr _, string text);
delegate string GetClipboardTextFn(IntPtr _);
delegate void ImDrawCallback(IntPtr list, IntPtr cmd);
public void ProcessEvent(SDL_Event evt)
{
ProcessEvent(in evt);
}
}