浏览代码

Pascal ImGui SDL2 Implementation & C++ OpenGL3

Coldzer0 1 年之前
父节点
当前提交
33018d06b7
共有 3 个文件被更改,包括 1282 次插入0 次删除
  1. 86 0
      impl/CImGui.Impl.OpenGL3.pas
  2. 43 0
      impl/CImGui.Impl.SDL2.pas
  3. 1153 0
      impl/PasImGui.SDL2.pas

+ 86 - 0
impl/CImGui.Impl.OpenGL3.pas

@@ -0,0 +1,86 @@
+{
+  FreePascal bindings for ImGui
+
+  Copyright (C) 2023 Coldzer0 <Coldzer0 [at] protonmail.ch>
+
+  This program is free software: you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation, version 3 of the License.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+}
+
+unit CImGui.Impl.OpenGL3;
+
+{$IfDef FPC}
+  {$mode objfpc}{$H+}
+{$EndIf}
+
+Interface
+
+Uses
+  PasImGui.Types,
+  PasImGui.Apis;
+
+
+function  ImGui_Impl_OpenGL3_Init(const glsl_version : PChar = nil) : Boolean;
+Procedure ImGui_Impl_OpenGL3_RenderDrawData(draw_data : PImDrawData);
+Procedure ImGui_Impl_OpenGL3_NewFrame();
+Procedure ImGui_Impl_OpenGL3_Shutdown();
+
+implementation
+
+function  ImGui_ImplOpenGL3_Init(const glsl_version : PChar = nil) : Boolean; cdecl; external CIMGUI_LIB;
+Procedure ImGui_ImplOpenGL3_Shutdown(); cdecl; external CIMGUI_LIB;
+Procedure ImGui_ImplOpenGL3_NewFrame(); cdecl; external CIMGUI_LIB;
+Procedure ImGui_ImplOpenGL3_RenderDrawData(draw_data : PImDrawData); cdecl; external CIMGUI_LIB;
+
+// (Optional) Called by Init/NewFrame/Shutdown
+function  ImGui_ImplOpenGL3_CreateFontsTexture(): Boolean; cdecl; external CIMGUI_LIB;
+Procedure ImGui_ImplOpenGL3_DestroyFontsTexture(); cdecl; external CIMGUI_LIB;
+function  ImGui_ImplOpenGL3_CreateDeviceObjects(): Boolean; cdecl; external CIMGUI_LIB;
+Procedure ImGui_ImplOpenGL3_DestroyDeviceObjects(); cdecl; external CIMGUI_LIB;
+
+function ImGui_Impl_OpenGL3_Init(const glsl_version: PChar): Boolean;
+Var
+  saved: Cardinal;
+Begin
+  saved := SetFpuFlags();
+  Result := ImGui_ImplOpenGL3_Init(glsl_version);
+  ResetFpuFlags(saved);
+end;
+
+procedure ImGui_Impl_OpenGL3_RenderDrawData(draw_data: PImDrawData);
+Var
+  saved: Cardinal;
+Begin
+  saved := SetFpuFlags();
+  ImGui_ImplOpenGL3_RenderDrawData(draw_data);
+  ResetFpuFlags(saved);
+end;
+
+procedure ImGui_Impl_OpenGL3_NewFrame();
+Var
+  saved: Cardinal;
+Begin
+  saved := SetFpuFlags();
+  ImGui_ImplOpenGL3_NewFrame();
+  ResetFpuFlags(saved);
+end;
+
+procedure ImGui_Impl_OpenGL3_Shutdown();
+Var
+  saved: Cardinal;
+Begin
+  saved := SetFpuFlags();
+  ImGui_ImplOpenGL3_Shutdown();
+  ResetFpuFlags(saved);
+end;
+
+
+
+end.
+

+ 43 - 0
impl/CImGui.Impl.SDL2.pas

@@ -0,0 +1,43 @@
+{
+  FreePascal bindings for ImGui
+
+  Copyright (C) 2023 Coldzer0 <Coldzer0 [at] protonmail.ch>
+
+  This program is free software: you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation, version 3 of the License.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+}
+
+Unit CImGui.Impl.SDL2;
+
+{$IfDef FPC}
+  {$mode objfpc}{$H+}
+{$EndIf}
+
+Interface
+
+Uses
+  SDL2, PasImGui.Apis;
+
+Function ImGui_ImplSDL2_InitForOpenGL(window: PSDL_Window;
+  sdl_gl_context: Pointer): Boolean; Cdecl; External CIMGUI_LIB;
+
+Function ImGui_ImplSDL2_InitForVulkan(window: PSDL_Window): Boolean; Cdecl; External CIMGUI_LIB;
+Function ImGui_ImplSDL2_InitForD3D(window: PSDL_Window): Boolean; Cdecl; External CIMGUI_LIB;
+Function ImGui_ImplSDL2_InitForMetal(window: PSDL_Window): Boolean; Cdecl; External CIMGUI_LIB;
+Function ImGui_ImplSDL2_InitForSDLRenderer(window: PSDL_Window; renderer: PSDL_Renderer): Boolean; Cdecl; External CIMGUI_LIB;
+Function ImGui_ImplSDL2_InitForOther(window: PSDL_Window): Boolean; Cdecl; External CIMGUI_LIB;
+
+Procedure ImGui_ImplSDL2_Shutdown(); Cdecl; External CIMGUI_LIB;
+Procedure ImGui_ImplSDL2_NewFrame(); Cdecl; External CIMGUI_LIB;
+Function ImGui_ImplSDL2_ProcessEvent(event: PSDL_Event): Boolean; Cdecl; External CIMGUI_LIB;
+
+
+Implementation
+
+End.

+ 1153 - 0
impl/PasImGui.SDL2.pas

@@ -0,0 +1,1153 @@
+{
+  FreePascal bindings for ImGui
+
+  Copyright (C) 2023 Coldzer0 <Coldzer0 [at] protonmail.ch>
+
+  This program is free software: you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation, version 3 of the License.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+}
+
+Unit PasImGui.SDL2;
+
+{$IFDEF FPC}
+  {$mode objfpc}{$H+}{$J-}
+  {$PackRecords C}
+{$ENDIF}
+
+{$Define SDL_HINT_IME_SHOW_UI}
+{$Define SDL_HINT_MOUSE_AUTO_CAPTURE}
+{$Define SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH}
+{$Define SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE}
+
+Interface
+
+Uses
+  {$IfDef MSWINDOWS}
+  Windows,
+  {$EndIf}
+  SDL2,
+  PasImGui,
+  PasImGui.Enums,
+  PasImGui.Types;
+
+Function ImGui_ImplSDL2_InitForOpenGL_Pas(window: PSDL_Window;
+  sdl_gl_context: Pointer): Boolean;
+Function ImGui_ImplSDL2_ProcessEvent_Pas(event: PSDL_Event): Boolean;
+Procedure ImGui_ImplSDL2_NewFrame_Pas();
+Procedure ImGui_ImplSDL2_Shutdown_Pas();
+
+Type
+  PImGui_ImplSDL2_Data = ^ImGui_ImplSDL2_Data;
+
+  ImGui_ImplSDL2_Data = Record
+    Window: PSDL_Window;
+    Renderer: PSDL_Renderer;
+    Time: Uint64;
+    MouseWindowID: Uint32;
+    MouseButtonsDown: Integer;
+    MouseCursors: Array [0..Ord(ImGuiMouseCursor_COUNT) - 1] Of PSDL_Cursor;
+    LastMouseCursor: PSDL_Cursor;
+    PendingMouseLeaveFrame: Integer;
+    ClipboardTextData: Pansichar;
+    MouseCanUseGlobalState: Boolean;
+    MouseCanReportHoveredViewport: Boolean;
+    UseVulkan: Boolean;
+    WantUpdateMonitors: Boolean;
+  End;
+
+  PImGui_ImplSDL2_ViewportData = ^ImGui_ImplSDL2_ViewportData;
+
+  ImGui_ImplSDL2_ViewportData = Record
+    Window: PSDL_Window;
+    WindowID: Uint32;
+    WindowOwned: Boolean;
+    GLContext: TSDL_GLContext;
+  End;
+
+
+Implementation
+
+Uses
+  SysUtils;
+
+// Done
+Function ImGui_ImplSDL2_GetBackendData(): PImGui_ImplSDL2_Data;
+Begin
+  If ImGui.GetCurrentContext() <> nil Then
+    Result := PImGui_ImplSDL2_Data(ImGui.GetIO()^.BackendPlatformUserData)
+  Else
+    Result := nil;
+End;
+
+//--------------------------------------------------------------------------------------------------------
+// MULTI-VIEWPORT / PLATFORM INTERFACE SUPPORT
+// This is an _advanced_ and _optional_ feature, allowing the backend to create and handle multiple viewports simultaneously.
+// If you are new to dear imgui or creating a new binding for dear imgui, it is recommended that you completely ignore this section first..
+//--------------------------------------------------------------------------------------------------------
+
+// Done
+Procedure ImGui_ImplSDL2_CreateWindow(viewport: PImGuiViewport); Cdecl;
+Var
+  bd: PImGui_ImplSDL2_Data;
+  main_viewport: PImGuiViewport;
+  main_viewport_data, vd: PImGui_ImplSDL2_ViewportData;
+
+  use_opengl: Boolean;
+  backup_context: TSDL_GLContext;
+
+  sdl_flags: Uint32;
+  info: TSDL_SysWMinfo;
+Begin
+  Initialize(info);
+  bd := ImGui_ImplSDL2_GetBackendData();
+  vd := AllocMem(SizeOf(ImGui_ImplSDL2_ViewportData));
+  viewport^.PlatformUserData := vd;
+
+  main_viewport := ImGui.GetMainViewport();
+  main_viewport_data := main_viewport^.PlatformUserData;
+  // Share GL resources with main context
+  use_opengl := (main_viewport_data^.GLContext <> nil);
+  backup_context := nil;
+  If use_opengl Then
+  Begin
+    backup_context := SDL_GL_GetCurrentContext();
+    SDL_GL_SetAttribute(SDL_GL_SHARE_WITH_CURRENT_CONTEXT, 1);
+    SDL_GL_MakeCurrent(main_viewport_data^.Window, main_viewport_data^.GLContext);
+  End;
+
+  sdl_flags := 0;
+  If use_opengl Then
+    sdl_flags := sdl_flags Or SDL_WINDOW_OPENGL;
+
+  { TODO: SDL Unit needs update - Time : 11/7/2023 7:26:01 PM }
+  //if bd^.UseVulkan then
+  //  sdl_flags := sdl_flags or SDL_WINDOW_VULKAN;
+
+  sdl_flags := sdl_flags Or (SDL_GetWindowFlags(bd^.Window) And
+    SDL_WINDOW_ALLOW_HIGHDPI);
+
+  sdl_flags := sdl_flags Or SDL_WINDOW_HIDDEN;
+
+  If Ord(viewport^.Flags And ImGuiViewportFlags_NoDecoration) <> 0 Then
+    sdl_flags := sdl_flags Or SDL_WINDOW_BORDERLESS
+  Else
+    sdl_flags := sdl_flags Or SDL_WINDOW_RESIZABLE;
+
+  {$IfNDef MSWINDOWS}
+  if Ord(viewport^.Flags and ImGuiViewportFlags_NoTaskBarIcon) <> 0 then
+    sdl_flags := sdl_flags or SDL_WINDOW_SKIP_TASKBAR;
+  {$EndIf}
+
+  If SDL_VERSION_ATLEAST(2, 0, 5) Then
+  Begin
+    If Ord(viewport^.Flags And ImGuiViewportFlags_TopMost) <> 0 Then
+      sdl_flags := sdl_flags Or SDL_WINDOW_ALWAYS_ON_TOP;
+  End;
+
+
+  vd^.Window := SDL_CreateWindow('No Title Yet', Trunc(viewport^.Pos.x),
+    Trunc(viewport^.Pos.y), Trunc(viewport^.Size.x), Trunc(viewport^.Size.y),
+    sdl_flags);
+
+  vd^.WindowOwned := True;
+  If use_opengl Then
+  Begin
+    vd^.GLContext := SDL_GL_CreateContext(vd^.Window);
+    SDL_GL_SetSwapInterval(0);
+  End;
+
+  If use_opengl And (backup_context <> nil) Then
+    SDL_GL_MakeCurrent(vd^.Window, backup_context);
+
+  viewport^.PlatformHandle := vd^.Window;
+  viewport^.PlatformHandleRaw := nil;
+
+  FillByte(info, SizeOf(TSDL_SysWMinfo), 0);
+  SDL_VERSION(info.version);
+  If SDL_GetWindowWMInfo(vd^.window, @info) = SDL_TRUE Then
+  Begin
+    {$IfDef MSWINDOWS}
+    viewport^.PlatformHandleRaw := {%H-}Pointer(info.win.window);
+    {$ElseIf defined(DARWIN)}
+    viewport^.PlatformHandleRaw = {%H-}
+Pointer(info.cocoa.window);
+    {$EndIf}
+  End;
+End;
+
+// Done
+Procedure ImGui_ImplSDL2_DestroyWindow(viewport: PImGuiViewport); Cdecl;
+Var
+  vd: PImGui_ImplSDL2_ViewportData;
+Begin
+  vd := viewport^.PlatformUserData;
+  If vd <> nil Then
+  Begin
+    If (vd^.GLContext <> nil) And vd^.WindowOwned Then
+      SDL_GL_DeleteContext(vd^.GLContext);
+    If (vd^.Window <> nil) And vd^.WindowOwned Then
+      SDL_DestroyWindow(vd^.Window);
+
+    vd^.GLContext := nil;
+    vd^.Window := nil;
+    Freemem(vd);
+  End;
+  viewport^.PlatformHandle := nil;
+  viewport^.PlatformUserData := nil;
+End;
+
+// Done
+Procedure ImGui_ImplSDL2_ShowWindow(viewport: PImGuiViewport); Cdecl;
+Var
+  vd: PImGui_ImplSDL2_ViewportData;
+  {$IfDef MSWINDOWS}
+  _hwnd: Windows.HWND;
+  ex_style: LONG;
+  {$EndIf}
+Begin
+  vd := viewport^.PlatformUserData;
+  {$IfDef MSWINDOWS}
+  _hwnd := {%H-}Windows.HWND(viewport^.PlatformHandleRaw);
+  // SDL hack: Hide icon from task bar
+  // Note: SDL 2.0.6+ has a SDL_WINDOW_SKIP_TASKBAR flag which is supported
+  // under Windows but the way it create the window breaks our seamless transition.
+  If Ord(viewport^.Flags And ImGuiViewportFlags_NoTaskBarIcon) <> 0 Then
+  Begin
+    ex_style := GetWindowLong(_hwnd, GWL_EXSTYLE);
+    ex_style := ex_style And Not WS_EX_APPWINDOW;
+    ex_style := ex_style Or WS_EX_TOOLWINDOW;
+    SetWindowLong(_hwnd, GWL_EXSTYLE, ex_style);
+  End;
+  // SDL hack: SDL always activate/focus windows :/
+  If Ord(viewport^.Flags And ImGuiViewportFlags_NoFocusOnAppearing) <> 0 Then
+  Begin
+    ShowWindow(_hwnd, SW_SHOWNA);
+    Exit;
+  End;
+  {$EndIf}
+  SDL_ShowWindow(vd^.Window);
+End;
+
+// Done
+Function ImGui_ImplSDL2_GetWindowPos(viewport: PImGuiViewport): ImVec2; Cdecl;
+Var
+  vd: PImGui_ImplSDL2_ViewportData;
+  x, y: Integer;
+Begin
+  vd := viewport^.PlatformUserData;
+  x := 0;
+  y := 0;
+  SDL_GetWindowPosition(vd^.Window, @x, @y);
+  Result := ImVec2.New(Single(x), Single(y));
+End;
+
+// Done
+Procedure ImGui_ImplSDL2_SetWindowPos(viewport: PImGuiViewport; pos: ImVec2); Cdecl;
+Var
+  vd: PImGui_ImplSDL2_ViewportData;
+Begin
+  vd := viewport^.PlatformUserData;
+  SDL_SetWindowPosition(vd^.Window, Trunc(pos.x), Trunc(pos.y));
+End;
+
+// Done
+Function ImGui_ImplSDL2_GetWindowSize(viewport: PImGuiViewport): ImVec2; Cdecl;
+Var
+  vd: PImGui_ImplSDL2_ViewportData;
+  w, h: Integer;
+Begin
+  vd := viewport^.PlatformUserData;
+  w := 0;
+  h := 0;
+  SDL_GetWindowSize(vd^.Window, @w, @h);
+  Result := ImVec2.New(Single(w), Single(h));
+End;
+
+// Done
+Procedure ImGui_ImplSDL2_SetWindowSize(viewport: PImGuiViewport; size: ImVec2); Cdecl;
+Var
+  vd: PImGui_ImplSDL2_ViewportData;
+Begin
+  vd := viewport^.PlatformUserData;
+  SDL_SetWindowSize(vd^.Window, Trunc(size.x), Trunc(size.y));
+End;
+
+// Done
+Procedure ImGui_ImplSDL2_SetWindowTitle(viewport: PImGuiViewport; title: PChar); Cdecl;
+Var
+  vd: PImGui_ImplSDL2_ViewportData;
+Begin
+  vd := viewport^.PlatformUserData;
+  SDL_SetWindowTitle(vd^.Window, title);
+End;
+
+// Done
+Procedure ImGui_ImplSDL2_SetWindowAlpha(viewport: PImGuiViewport; alpha: Single); Cdecl;
+Var
+  vd: PImGui_ImplSDL2_ViewportData;
+Begin
+  vd := viewport^.PlatformUserData;
+  SDL_SetWindowOpacity(vd^.Window, alpha);
+End;
+
+// Done
+Procedure ImGui_ImplSDL2_SetWindowFocus(viewport: PImGuiViewport); Cdecl;
+Var
+  vd: PImGui_ImplSDL2_ViewportData;
+Begin
+  vd := viewport^.PlatformUserData;
+  SDL_RaiseWindow(vd^.Window);
+End;
+
+// Done
+Function ImGui_ImplSDL2_GetWindowFocus(viewport: PImGuiViewport): Boolean; Cdecl;
+Var
+  vd: PImGui_ImplSDL2_ViewportData;
+Begin
+  vd := viewport^.PlatformUserData;
+  Result := Ord(SDL_GetWindowFlags(vd^.Window) And SDL_WINDOW_INPUT_FOCUS) <> 0;
+End;
+
+// Done
+Function ImGui_ImplSDL2_GetWindowMinimized(viewport: PImGuiViewport): Boolean; Cdecl;
+Var
+  vd: PImGui_ImplSDL2_ViewportData;
+Begin
+  vd := viewport^.PlatformUserData;
+  Result := Ord(SDL_GetWindowFlags(vd^.Window) And SDL_WINDOW_MINIMIZED) <> 0;
+End;
+
+// Done
+Procedure ImGui_ImplSDL2_RenderWindow(viewport: PImGuiViewport;
+  render_arg: Pointer); Cdecl;
+Var
+  vd: PImGui_ImplSDL2_ViewportData;
+Begin
+  vd := viewport^.PlatformUserData;
+  If vd^.GLContext <> nil Then
+    SDL_GL_MakeCurrent(vd^.Window, vd^.GLContext);
+End;
+
+// Done
+Procedure ImGui_ImplSDL2_SwapBuffers(viewport: PImGuiViewport;
+  render_arg: Pointer); Cdecl;
+Var
+  vd: PImGui_ImplSDL2_ViewportData;
+Begin
+  vd := viewport^.PlatformUserData;
+  If vd^.GLContext <> nil Then
+  Begin
+    SDL_GL_MakeCurrent(vd^.Window, vd^.GLContext);
+    SDL_GL_SwapWindow(vd^.Window);
+  End;
+End;
+
+// Done
+Procedure ImGui_ImplSDL2_InitPlatformInterface(window: PSDL_Window;
+  sdl_gl_context: Pointer);
+Var
+  platform_io: PImGuiPlatformIO;
+  main_viewport: PImGuiViewport;
+  vd: PImGui_ImplSDL2_ViewportData;
+Begin
+  platform_io := ImGui.GetPlatformIO();
+
+  platform_io^.Platform_CreateWindow := @ImGui_ImplSDL2_CreateWindow;
+  platform_io^.Platform_DestroyWindow := @ImGui_ImplSDL2_DestroyWindow;
+  platform_io^.Platform_ShowWindow := @ImGui_ImplSDL2_ShowWindow;
+  platform_io^.Platform_SetWindowPos := @ImGui_ImplSDL2_SetWindowPos;
+  platform_io^.Platform_GetWindowPos := @ImGui_ImplSDL2_GetWindowPos;
+  platform_io^.Platform_SetWindowSize := @ImGui_ImplSDL2_SetWindowSize;
+  platform_io^.Platform_GetWindowSize := @ImGui_ImplSDL2_GetWindowSize;
+  platform_io^.Platform_SetWindowFocus := @ImGui_ImplSDL2_SetWindowFocus;
+  platform_io^.Platform_GetWindowFocus := @ImGui_ImplSDL2_GetWindowFocus;
+  platform_io^.Platform_GetWindowMinimized := @ImGui_ImplSDL2_GetWindowMinimized;
+  platform_io^.Platform_SetWindowTitle := @ImGui_ImplSDL2_SetWindowTitle;
+  platform_io^.Platform_RenderWindow := @ImGui_ImplSDL2_RenderWindow;
+  platform_io^.Platform_SwapBuffers := @ImGui_ImplSDL2_SwapBuffers;
+
+  If SDL_VERSION_ATLEAST(2, 0, 5) Then
+  Begin
+    platform_io^.Platform_SetWindowAlpha := @ImGui_ImplSDL2_SetWindowAlpha;
+  End;
+
+  {$IfDef SDL_HAS_VULKAN}
+  platform_io^.Platform_CreateVkSurface := ImGui_ImplSDL2_CreateVkSurface;
+  {$EndIf}
+
+  // Register main window handle (which is owned by the main application, not by us)
+  // This is mostly for simplicity and consistency, so that our code (e.g. mouse handling etc.) can use same logic for main and secondary viewports.
+  main_viewport := ImGui.GetMainViewport();
+  vd := AllocMem(SizeOf(ImGui_ImplSDL2_ViewportData));
+  vd^.Window := window;
+  vd^.WindowID := SDL_GetWindowID(window);
+  vd^.WindowOwned := False;
+  vd^.GLContext := sdl_gl_context;
+  main_viewport^.PlatformUserData := vd;
+  main_viewport^.PlatformHandle := vd^.Window;
+End;
+
+// Done
+Procedure ImGui_ImplSDL2_ShutdownPlatformInterface;
+Begin
+  ImGui.DestroyPlatformWindows();
+End;
+
+//------------------------------------------------------------------------------
+// Frame Generation
+//------------------------------------------------------------------------------
+
+// Done
+Function ImGui_ImplSDL2_GetClipboardText(user_data: Pointer): PChar; Cdecl;
+Var
+  bd: PImGui_ImplSDL2_Data;
+Begin
+  bd := ImGui_ImplSDL2_GetBackendData();
+  If bd^.ClipboardTextData <> nil Then
+    SDL_free(bd^.ClipboardTextData);
+
+  bd^.ClipboardTextData := SDL_GetClipboardText();
+  Result := bd^.ClipboardTextData;
+End;
+
+// Done
+Procedure ImGui_ImplSDL2_SetClipboardText(user_data: Pointer; Text: PChar); Cdecl;
+Begin
+  SDL_SetClipboardText(Text);
+End;
+
+// Done
+// Note: native IME will only display if user calls SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1") _before_ SDL_CreateWindow().
+Procedure ImGui_ImplSDL2_SetPlatformImeData(viewport: PImGuiViewport;
+  Data: PImGuiPlatformImeData); Cdecl;
+Var
+  r: TSDL_Rect;
+Begin
+  If Data^.WantVisible Then
+  Begin
+    r.x := Trunc(Data^.InputPos.x - viewport^.Pos.x);
+    r.y := Trunc(Data^.InputPos.y - viewport^.Pos.y + Data^.InputLineHeight);
+    r.w := 1;
+    r.h := Integer(Data^.InputLineHeight);
+    SDL_SetTextInputRect(@r);
+  End;
+End;
+
+// Done
+Function ImGui_ImplSDL2_KeycodeToImGuiKey(keycode: TSDL_KeyCode): ImGuiKey;
+Begin
+  Case keycode Of
+    SDLK_TAB: Result := ImGuiKey_Tab;
+    SDLK_LEFT: Result := ImGuiKey_LeftArrow;
+    SDLK_RIGHT: Result := ImGuiKey_RightArrow;
+    SDLK_UP: Result := ImGuiKey_UpArrow;
+    SDLK_DOWN: Result := ImGuiKey_DownArrow;
+    SDLK_PAGEUP: Result := ImGuiKey_PageUp;
+    SDLK_PAGEDOWN: Result := ImGuiKey_PageDown;
+    SDLK_HOME: Result := ImGuiKey_Home;
+    SDLK_END: Result := ImGuiKey_End;
+    SDLK_INSERT: Result := ImGuiKey_Insert;
+    SDLK_DELETE: Result := ImGuiKey_Delete;
+    SDLK_BACKSPACE: Result := ImGuiKey_Backspace;
+    SDLK_SPACE: Result := ImGuiKey_Space;
+    SDLK_RETURN: Result := ImGuiKey_Enter;
+    SDLK_ESCAPE: Result := ImGuiKey_Escape;
+    SDLK_QUOTE: Result := ImGuiKey_Apostrophe;
+    SDLK_COMMA: Result := ImGuiKey_Comma;
+    SDLK_MINUS: Result := ImGuiKey_Minus;
+    SDLK_PERIOD: Result := ImGuiKey_Period;
+    SDLK_SLASH: Result := ImGuiKey_Slash;
+    SDLK_SEMICOLON: Result := ImGuiKey_Semicolon;
+    SDLK_EQUALS: Result := ImGuiKey_Equal;
+    SDLK_LEFTBRACKET: Result := ImGuiKey_LeftBracket;
+    SDLK_BACKSLASH: Result := ImGuiKey_Backslash;
+    SDLK_RIGHTBRACKET: Result := ImGuiKey_RightBracket;
+    SDLK_BACKQUOTE: Result := ImGuiKey_GraveAccent;
+    SDLK_CAPSLOCK: Result := ImGuiKey_CapsLock;
+    SDLK_SCROLLLOCK: Result := ImGuiKey_ScrollLock;
+    SDLK_NUMLOCKCLEAR: Result := ImGuiKey_NumLock;
+    SDLK_PRINTSCREEN: Result := ImGuiKey_PrintScreen;
+    SDLK_PAUSE: Result := ImGuiKey_Pause;
+    SDLK_KP_0: Result := ImGuiKey_Keypad0;
+    SDLK_KP_1: Result := ImGuiKey_Keypad1;
+    SDLK_KP_2: Result := ImGuiKey_Keypad2;
+    SDLK_KP_3: Result := ImGuiKey_Keypad3;
+    SDLK_KP_4: Result := ImGuiKey_Keypad4;
+    SDLK_KP_5: Result := ImGuiKey_Keypad5;
+    SDLK_KP_6: Result := ImGuiKey_Keypad6;
+    SDLK_KP_7: Result := ImGuiKey_Keypad7;
+    SDLK_KP_8: Result := ImGuiKey_Keypad8;
+    SDLK_KP_9: Result := ImGuiKey_Keypad9;
+    SDLK_KP_PERIOD: Result := ImGuiKey_KeypadDecimal;
+    SDLK_KP_DIVIDE: Result := ImGuiKey_KeypadDivide;
+    SDLK_KP_MULTIPLY: Result := ImGuiKey_KeypadMultiply;
+    SDLK_KP_MINUS: Result := ImGuiKey_KeypadSubtract;
+    SDLK_KP_PLUS: Result := ImGuiKey_KeypadAdd;
+    SDLK_KP_ENTER: Result := ImGuiKey_KeypadEnter;
+    SDLK_KP_EQUALS: Result := ImGuiKey_KeypadEqual;
+    SDLK_LCTRL: Result := ImGuiKey_LeftCtrl;
+    SDLK_LSHIFT: Result := ImGuiKey_LeftShift;
+    SDLK_LALT: Result := ImGuiKey_LeftAlt;
+    SDLK_LGUI: Result := ImGuiKey_LeftSuper;
+    SDLK_RCTRL: Result := ImGuiKey_RightCtrl;
+    SDLK_RSHIFT: Result := ImGuiKey_RightShift;
+    SDLK_RALT: Result := ImGuiKey_RightAlt;
+    SDLK_RGUI: Result := ImGuiKey_RightSuper;
+    SDLK_APPLICATION: Result := ImGuiKey_Menu;
+    SDLK_0: Result := ImGuiKey_0;
+    SDLK_1: Result := ImGuiKey_1;
+    SDLK_2: Result := ImGuiKey_2;
+    SDLK_3: Result := ImGuiKey_3;
+    SDLK_4: Result := ImGuiKey_4;
+    SDLK_5: Result := ImGuiKey_5;
+    SDLK_6: Result := ImGuiKey_6;
+    SDLK_7: Result := ImGuiKey_7;
+    SDLK_8: Result := ImGuiKey_8;
+    SDLK_9: Result := ImGuiKey_9;
+    SDLK_a: Result := ImGuiKey_A;
+    SDLK_b: Result := ImGuiKey_B;
+    SDLK_c: Result := ImGuiKey_C;
+    SDLK_d: Result := ImGuiKey_D;
+    SDLK_e: Result := ImGuiKey_E;
+    SDLK_f: Result := ImGuiKey_F;
+    SDLK_g: Result := ImGuiKey_G;
+    SDLK_h: Result := ImGuiKey_H;
+    SDLK_i: Result := ImGuiKey_I;
+    SDLK_j: Result := ImGuiKey_J;
+    SDLK_k: Result := ImGuiKey_K;
+    SDLK_l: Result := ImGuiKey_L;
+    SDLK_m: Result := ImGuiKey_M;
+    SDLK_n: Result := ImGuiKey_N;
+    SDLK_o: Result := ImGuiKey_O;
+    SDLK_p: Result := ImGuiKey_P;
+    SDLK_q: Result := ImGuiKey_Q;
+    SDLK_r: Result := ImGuiKey_R;
+    SDLK_s: Result := ImGuiKey_S;
+    SDLK_t: Result := ImGuiKey_T;
+    SDLK_u: Result := ImGuiKey_U;
+    SDLK_v: Result := ImGuiKey_V;
+    SDLK_w: Result := ImGuiKey_W;
+    SDLK_x: Result := ImGuiKey_X;
+    SDLK_y: Result := ImGuiKey_Y;
+    SDLK_z: Result := ImGuiKey_Z;
+    SDLK_F1: Result := ImGuiKey_F1;
+    SDLK_F2: Result := ImGuiKey_F2;
+    SDLK_F3: Result := ImGuiKey_F3;
+    SDLK_F4: Result := ImGuiKey_F4;
+    SDLK_F5: Result := ImGuiKey_F5;
+    SDLK_F6: Result := ImGuiKey_F6;
+    SDLK_F7: Result := ImGuiKey_F7;
+    SDLK_F8: Result := ImGuiKey_F8;
+    SDLK_F9: Result := ImGuiKey_F9;
+    SDLK_F10: Result := ImGuiKey_F10;
+    SDLK_F11: Result := ImGuiKey_F11;
+    SDLK_F12: Result := ImGuiKey_F12;
+    Else
+      Result := ImGuiKey_None;
+  End;
+End;
+
+// Done
+Procedure ImGui_ImplSDL2_UpdateKeyModifiers(sdl_key_mods: TSDL_KeyMod);
+Begin
+  ImGui.AddKeyEvent(ImGuiMod_Ctrl, (sdl_key_mods And KMOD_CTRL) <> 0);
+  ImGui.AddKeyEvent(ImGuiMod_Shift, (sdl_key_mods And KMOD_SHIFT) <> 0);
+  ImGui.AddKeyEvent(ImGuiMod_Alt, (sdl_key_mods And KMOD_ALT) <> 0);
+  ImGui.AddKeyEvent(ImGuiMod_Super, (sdl_key_mods And KMOD_GUI) <> 0);
+End;
+
+// FIXME
+Function ImGui_ImplSDL2_Init(window: PSDL_Window; renderer: PSDL_Renderer;
+  sdl_gl_context: Pointer): Boolean;
+Var
+  io: PImGuiIO;
+  bd: PImGui_ImplSDL2_Data;
+  main_viewport: PImGuiViewport;
+  mouse_can_use_global_state: Boolean;
+  sdl_backend: Pansichar;
+  I: Integer;
+  info: TSDL_SysWMinfo;
+Const
+  global_mouse_whitelist: Array[0..4] Of
+    Pansichar = ('windows', 'cocoa', 'x11', 'DIVE', 'VMAN');
+Begin
+
+  io := ImGui.GetIO();
+  Assert(io^.BackendPlatformUserData = nil, 'Already initialized a platform backend!');
+
+  // Check and store if we are on a SDL backend that supports global mouse position
+  // ("wayland" and "rpi" don't support it, but we chose to use a white-list instead of a black-list)
+  mouse_can_use_global_state := False;
+  sdl_backend := SDL_GetCurrentVideoDriver();
+  For I := 0 To High(global_mouse_whitelist) Do
+  Begin
+    If StrIComp(sdl_backend, global_mouse_whitelist[I]) = 0 Then
+    Begin
+      mouse_can_use_global_state := True;
+      Break; // Break the loop early if a match is found
+    End;
+  End;
+  // Setup backend capabilities flags
+  bd := AllocMem(SizeOf(ImGui_ImplSDL2_Data));
+
+  io^.BackendPlatformUserData := bd;
+  io^.BackendPlatformName := PAnsiChar('imgui_impl_sdl2_pascal');
+
+  // We can honor GetMouseCursor() values (optional)
+  io^.BackendFlags := io^.BackendFlags Or ImGuiBackendFlags_HasMouseCursors;
+  // We can honor io.WantSetMousePos requests (optional, rarely used)
+  io^.BackendFlags := io^.BackendFlags Or ImGuiBackendFlags_HasSetMousePos;
+  If mouse_can_use_global_state Then
+  Begin
+    // We can create multi-viewports on the Platform side (optional)
+    io^.BackendFlags := io^.BackendFlags Or ImGuiBackendFlags_PlatformHasViewports;
+  End;
+
+  bd^.Window := window;
+  bd^.Renderer := renderer;
+
+  // 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 'ImGuiBackendFlags_HasMouseHoveredViewport' dynamically each frame.
+
+  bd^.MouseCanUseGlobalState := mouse_can_use_global_state;
+  {$IfnDef DARWIN}
+  bd^.MouseCanReportHoveredViewport := bd^.MouseCanUseGlobalState;
+  {$ELSE}
+  bd^.MouseCanReportHoveredViewport := False;
+  {$EndIf}
+  bd^.WantUpdateMonitors := True;
+
+  io^.SetClipboardTextFn := @ImGui_ImplSDL2_SetClipboardText;
+  io^.GetClipboardTextFn := @ImGui_ImplSDL2_GetClipboardText;
+  io^.ClipboardUserData := nil;
+  io^.SetPlatformImeDataFn := @ImGui_ImplSDL2_SetPlatformImeData;
+
+  // Load mouse cursors
+  bd^.MouseCursors[Ord(ImGuiMouseCursor_Arrow)] := SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_ARROW);
+  bd^.MouseCursors[Ord(ImGuiMouseCursor_TextInput)] := SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_IBEAM);
+  bd^.MouseCursors[Ord(ImGuiMouseCursor_ResizeAll)] := SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEALL);
+  bd^.MouseCursors[Ord(ImGuiMouseCursor_ResizeNS)] := SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENS);
+  bd^.MouseCursors[Ord(ImGuiMouseCursor_ResizeEW)] := SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEWE);
+  bd^.MouseCursors[Ord(ImGuiMouseCursor_ResizeNESW)] := SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENESW);
+  bd^.MouseCursors[Ord(ImGuiMouseCursor_ResizeNWSE)] := SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENWSE);
+  bd^.MouseCursors[Ord(ImGuiMouseCursor_Hand)] := SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_HAND);
+  bd^.MouseCursors[Ord(ImGuiMouseCursor_NotAllowed)] := SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NO);
+
+  main_viewport := ImGui.GetMainViewport();
+  main_viewport^.PlatformHandle := window;
+  main_viewport^.PlatformHandleRaw := nil;
+
+  FillByte(info, SizeOf(TSDL_SysWMinfo), 0);
+  SDL_VERSION(info.version);
+  If SDL_GetWindowWMInfo(window, @info) = SDL_TRUE Then
+  Begin
+    {$IfDef MSWINDOWS}
+    main_viewport^.PlatformHandleRaw := Pointer(info.win.window);
+    {$ElseIf defined(DARWIN)}
+      main_viewport^.PlatformHandleRaw = Pointer(info.cocoa.window);
+    {$EndIf}
+  End;
+
+  // From 2.0.5 only
+  {$IfDef SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH}
+  If SDL_VERSION_ATLEAST(2, 0, 5) Then
+    SDL_SetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, '1');
+  {$EndIf}
+
+
+  // From 2.0.22: Disable auto-capture, this is preventing drag and drop across multiple windows (see #5710)
+  {$IfDef SDL_HINT_IME_SHOW_UI}
+  If SDL_VERSION_ATLEAST(2, 0, 22) Then
+    SDL_SetHint(SDL_HINT_IME_SHOW_UI, '1');
+  {$ENDIF}
+
+  // From 2.0.22: Disable auto-capture, this is preventing drag and drop across multiple windows (see #5710)
+
+  {$IfDef SDL_HINT_MOUSE_AUTO_CAPTURE}
+  If SDL_VERSION_ATLEAST(2, 0, 22) Then
+    SDL_SetHint(SDL_HINT_MOUSE_AUTO_CAPTURE, '0');
+  {$EndIf}
+
+
+  If (Ord(io^.ConfigFlags And ImGuiConfigFlags_ViewportsEnable) <> 0) And
+    (Ord(io^.BackendFlags And ImGuiBackendFlags_PlatformHasViewports) <> 0) Then
+  Begin
+    ImGui_ImplSDL2_InitPlatformInterface(window, sdl_gl_context);
+  End;
+
+  Result := True;
+End;
+
+// Done
+Function ImGui_ImplSDL2_InitForOpenGL_Pas(window: PSDL_Window;
+  sdl_gl_context: Pointer): Boolean;
+Begin
+  Result := ImGui_ImplSDL2_Init(window, nil, sdl_gl_context);
+End;
+
+// FIXME: Note that doesn't update with DPI/Scaling
+//  change only as SDL2 doesn't have an event for it (SDL3 has).
+Procedure ImGui_ImplSDL2_UpdateMonitors;
+Var
+  platform_io: PImGuiPlatformIO;
+  bd: PImGui_ImplSDL2_Data;
+  display_count, n: Integer;
+  monitor: ImGuiPlatformMonitor;
+  r: TSDL_Rect;
+  dpi: Single;
+Begin
+  bd := ImGui_ImplSDL2_GetBackendData();
+  platform_io := ImGui.GetPlatformIO();
+  platform_io^.Monitors.Size := 0;
+
+  bd^.WantUpdateMonitors := False;
+  display_count := SDL_GetNumVideoDisplays();
+
+  platform_io^.Monitors.Capacity := display_count;
+  For n := 0 To Pred(display_count) Do
+  Begin
+    FillChar(monitor, SizeOf(ImGuiPlatformMonitor), 0);
+
+    SDL_GetDisplayBounds(n, @r);
+
+    monitor.WorkPos := ImVec2.New(Single(r.x), Single(r.y));
+    monitor.WorkSize := ImVec2.New(Single(r.w), Single(r.h));
+    monitor.MainPos := monitor.WorkPos;
+    monitor.MainSize := monitor.WorkSize;
+
+    If SDL_VERSION_ATLEAST(2, 0, 5) Then
+    Begin
+      SDL_GetDisplayUsableBounds(n, @r);
+      monitor.WorkPos := ImVec2.New(Single(r.x), Single(r.y));
+      monitor.WorkSize := ImVec2.New(Single(r.w), Single(r.h));
+    End;
+
+    If SDL_VERSION_ATLEAST(2, 0, 4) Then
+    Begin
+      // FIXME-VIEWPORT: On MacOS SDL reports actual monitor DPI scale,
+      //    ignoring OS configuration. We may want to set
+      //  DpiScale to cocoa_window.backingScaleFactor here.
+      dpi := 0.0;
+      If Not Boolean(SDL_GetDisplayDPI(n, @dpi, nil, nil)) Then
+        monitor.DpiScale := dpi / 96.0;
+    End;
+    monitor.PlatformHandle := Pointer(UIntPtr(n));
+
+    If platform_io^.Monitors.Data = nil Then
+    Begin
+      platform_io^.Monitors.Data := AllocMem(SizeOf(ImGuiPlatformMonitor) * 8);
+      platform_io^.Monitors.Capacity := 8;
+    End;
+    platform_io^.Monitors.Data[n] := monitor;
+    Inc(platform_io^.Monitors.Size, 1);
+  End;
+End;
+
+// Done
+Procedure ImGui_ImplSDL2_UpdateMouseData;
+Var
+  bd: PImGui_ImplSDL2_Data;
+  io: PImGuiIO;
+  focused_window: PSDL_Window;
+  is_app_focused: Boolean;
+  mouse_x, mouse_y, window_x, window_y: Integer;
+  mouse_viewport_id: ImGuiID;
+  mouse_viewport: PImGuiViewport;
+  mouse_btn: TSDL_bool;
+  sdl_mouse_window: PSDL_Window;
+Begin
+  bd := ImGui_ImplSDL2_GetBackendData;
+  io := ImGui.GetIO;
+
+  // We forward mouse input when hovered or captured (via SDL_MOUSEMOTION) or when focused (below)
+
+  {$IfDef SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE}
+  // SDL_CaptureMouse() let the OS know e.g. that our imgui drag outside the SDL window boundaries shouldn't e.g. trigger other operations outside
+  mouse_btn := SDL_FALSE;
+  If bd^.MouseButtonsDown <> 0 Then
+    mouse_btn := SDL_TRUE;
+  SDL_CaptureMouse(mouse_btn);
+
+  focused_window := SDL_GetKeyboardFocus;
+  is_app_focused := (focused_window <> nil) And
+    ((bd^.Window = focused_window) Or
+    (ImGui.FindViewportByPlatformHandle(focused_window) <> nil));
+  {$ELSE}
+  focused_window := bd^.Window;
+  // SDL 2.0.3 and non-windowed systems: single-viewport only
+  is_app_focused := (SDL_GetWindowFlags(bd^.Window) And SDL_WINDOW_INPUT_FOCUS) <> 0;
+  {$EndIf}
+
+  If is_app_focused Then
+  Begin
+    // (Optional) Set OS mouse position from Dear ImGui if requested (rarely used, only when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user)
+    If io^.WantSetMousePos Then
+    Begin
+      {$IfDef SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE}
+      If Ord(io^.ConfigFlags And ImGuiConfigFlags_ViewportsEnable) <> 0 Then
+        SDL_WarpMouseGlobal(Trunc(io^.MousePos.x), Trunc(io^.MousePos.y))
+      Else
+      {$EndIf}
+        SDL_WarpMouseInWindow(bd^.Window, Trunc(io^.MousePos.x),
+          Trunc(io^.MousePos.y));
+    End;
+
+    // (Optional) Fallback to provide mouse position when focused (SDL_MOUSEMOTION already provides this when hovered or captured)
+    If bd^.MouseCanUseGlobalState And (bd^.MouseButtonsDown = 0) Then
+    Begin
+      // Single-viewport mode: mouse position in client window coordinates (io.MousePos is (0,0) when the mouse is on the upper-left corner of the app window)
+      // Multi-viewport mode: mouse position in OS absolute coordinates (io.MousePos is (0,0) when the mouse is on the upper-left of the primary monitor)
+      SDL_GetGlobalMouseState(@mouse_x, @mouse_y);
+      If Ord(io^.ConfigFlags And ImGuiConfigFlags_ViewportsEnable) = 0 Then
+      Begin
+        SDL_GetWindowPosition(focused_window, @window_x, @window_y);
+        Dec(mouse_x, window_x);
+        Dec(mouse_y, window_y);
+      End;
+      ImGui.AddMousePosEvent(Single(mouse_x), Single(mouse_y));
+    End;
+  End;
+
+  // (Optional) When using multiple viewports: call io.AddMouseViewportEvent() with the viewport the OS mouse cursor is hovering.
+  // If ImGuiBackendFlags_HasMouseHoveredViewport is not set by the backend, Dear ImGui will ignore this field and infer the information using its flawed heuristic.
+  // - [!] SDL backend does NOT correctly ignore viewports with the _NoInputs flag.
+  //       Some backend are not able to handle that correctly. If a backend report an hovered viewport that has the _NoInputs flag (e.g. when dragging a window
+  //       for docking, the viewport has the _NoInputs flag in order to allow us to find the viewport under), then Dear ImGui is forced to ignore the value reported
+  //       by the backend, and use its flawed heuristic to guess the viewport behind.
+  // - [X] SDL backend correctly reports this regardless of another viewport behind focused and dragged from (we need this to find a useful drag and drop target).
+  If Ord(io^.BackendFlags And ImGuiBackendFlags_HasMouseHoveredViewport) <> 0 Then
+  Begin
+    mouse_viewport_id := 0;
+    sdl_mouse_window := SDL_GetWindowFromID(bd^.MouseWindowID);
+    If sdl_mouse_window <> nil Then
+    Begin
+      mouse_viewport := ImGui.FindViewportByPlatformHandle(sdl_mouse_window);
+      If mouse_viewport <> nil Then
+      Begin
+        mouse_viewport_id := mouse_viewport^.ID;
+      End;
+    End;
+    ImGui.AddMouseViewportEvent(mouse_viewport_id);
+  End;
+End;
+
+// Done
+Procedure ImGui_ImplSDL2_UpdateMouseCursor;
+Var
+  io: PImGuiIO;
+  bd: PImGui_ImplSDL2_Data;
+  imgui_cursor: ImGuiMouseCursor;
+  expected_cursor: PSDL_Cursor;
+Begin
+  io := ImGui.GetIO;
+  If Ord(io^.ConfigFlags And ImGuiConfigFlags_NoMouseCursorChange) <> 0 Then
+    Exit;
+
+  bd := ImGui_ImplSDL2_GetBackendData;
+
+  imgui_cursor := ImGui.GetMouseCursor;
+  If io^.MouseDrawCursor Or (imgui_cursor = ImGuiMouseCursor_None) Then
+  Begin
+    // Hide OS mouse cursor if imgui is drawing it or if it wants no cursor
+    SDL_ShowCursor(Ord(SDL_FALSE));
+  End
+  Else
+  Begin
+    // Show OS mouse cursor
+    expected_cursor := bd^.MouseCursors[Ord(imgui_cursor)];
+    If expected_cursor = nil Then
+      expected_cursor := bd^.MouseCursors[Ord(ImGuiMouseCursor_Arrow)];
+
+    If bd^.LastMouseCursor <> expected_cursor Then
+    Begin
+      SDL_SetCursor(expected_cursor);
+      // SDL function doesn't have an early out (see #6113)
+      bd^.LastMouseCursor := expected_cursor;
+    End;
+    SDL_ShowCursor(Ord(SDL_TRUE));
+  End;
+End;
+
+{ TODO: Implement this ^_^ - Time : 11/8/2023 1:06:27 AM }
+Procedure ImGui_ImplSDL2_UpdateGamepads;
+Begin
+  { TODO: Implement this :V - Time : 11/9/2023 5:12:29 PM }
+End;
+
+// Done
+Procedure ImGui_ImplSDL2_NewFrame_Pas();
+Var
+  io: PImGuiIO;
+  bd: PImGui_ImplSDL2_Data;
+  w, h: Integer;
+  display_w, display_h: Integer;
+  frequency, current_time: Uint64;
+Begin
+  bd := ImGui_ImplSDL2_GetBackendData();
+  Assert(bd <> nil, 'Did you call ImGui_ImplSDL2_Init()?');
+  io := imgui.GetIO();
+
+  // Setup display size (every frame to accommodate for window resizing)
+  SDL_GetWindowSize(bd^.Window, @w, @h);
+  If Ord(SDL_GetWindowFlags(bd^.Window) And SDL_WINDOW_MINIMIZED) <> 0 Then
+  Begin
+    w := 0;
+    h := 0;
+  End;
+
+  If bd^.Renderer <> nil Then
+    SDL_GetRendererOutputSize(bd^.Renderer, @display_w, @display_h)
+  Else
+    SDL_GL_GetDrawableSize(bd^.Window, @display_w, @display_h);
+
+  io^.DisplaySize := ImVec2.New(Single(w), Single(h));
+
+  If (w > 0) And (h > 0) Then
+    io^.DisplayFramebufferScale :=
+      ImVec2.New(Single(display_w) / w, Single(display_h) / h);
+
+  // Update monitors
+  If bd^.WantUpdateMonitors Then
+    ImGui_ImplSDL2_UpdateMonitors();
+
+  // Setup time step (we don't use SDL_GetTicks() because it is using millisecond resolution)
+  // (Accept SDL_GetPerformanceCounter() not returning a monotonically increasing value.
+  // Happens in VMs and Emscripten, see #6189, #6114, #3644)
+  frequency := SDL_GetPerformanceFrequency;
+  current_time := SDL_GetPerformanceCounter;
+  If current_time <= bd^.Time Then
+    current_time := bd^.Time + 1;
+  If bd^.Time > 0 Then
+    io^.DeltaTime := Single(double(current_time - bd^.Time) / frequency)
+  Else
+    io^.DeltaTime := Single(1.0 / 60.0);
+  bd^.Time := current_time;
+
+
+  If ((bd^.PendingMouseLeaveFrame > 0) And (bd^.PendingMouseLeaveFrame >=
+    ImGui.GetFrameCount()) And (bd^.MouseButtonsDown = 0)) Then
+  Begin
+    bd^.MouseWindowID := 0;
+    bd^.PendingMouseLeaveFrame := 0;
+    ImGui.AddMousePosEvent(-FLT_MAX, -FLT_MAX);
+  End;
+
+  // Our io.AddMouseViewportEvent() calls will only be valid when not capturing.
+  // Technically speaking testing for 'bd->MouseButtonsDown == 0' would be more rygorous,
+  // but testing for payload reduces noise and potential side-effects.
+  If (bd^.MouseCanReportHoveredViewport And (ImGui.GetDragDropPayload() = nil)) Then
+    io^.BackendFlags := io^.BackendFlags Or ImGuiBackendFlags_HasMouseHoveredViewport
+  Else
+    io^.BackendFlags := io^.BackendFlags And Not
+      ImGuiBackendFlags_HasMouseHoveredViewport;
+
+
+  ImGui_ImplSDL2_UpdateMouseData();
+  ImGui_ImplSDL2_UpdateMouseCursor();
+
+  // Update game controllers (if enabled and available)
+  ImGui_ImplSDL2_UpdateGamepads();
+End;
+
+// Done
+Procedure ImGui_ImplSDL2_Shutdown_Pas();
+Var
+  platform_io: PImGuiPlatformIO;
+  bd: PImGui_ImplSDL2_Data;
+  io: PImGuiIO;
+  cursor_n: Integer;
+Begin
+  bd := ImGui_ImplSDL2_GetBackendData();
+  Assert(bd <> nil, 'No platform backend to shutdown, or already shutdown?');
+  io := imgui.GetIO();
+
+  ImGui_ImplSDL2_ShutdownPlatformInterface();
+
+  If bd^.ClipboardTextData <> nil Then
+    SDL_free(bd^.ClipboardTextData);
+
+  For cursor_n := 0 To Pred(Ord(ImGuiMouseCursor_COUNT)) Do
+  Begin
+    SDL_FreeCursor(bd^.MouseCursors[cursor_n]);
+  End;
+
+  bd^.LastMouseCursor := nil;
+
+  io^.BackendPlatformName := nil;
+  io^.BackendPlatformUserData := nil;
+
+  io^.BackendFlags := io^.BackendFlags And Not
+    (ImGuiBackendFlags_HasMouseCursors Or ImGuiBackendFlags_HasSetMousePos Or
+    ImGuiBackendFlags_HasGamepad Or ImGuiBackendFlags_PlatformHasViewports Or
+    ImGuiBackendFlags_HasMouseHoveredViewport);
+
+  // Free Allocated Monitors
+  platform_io := ImGui.GetPlatformIO();
+  platform_io^.Monitors.Size := 0;
+  platform_io^.Monitors.Capacity := 0;
+  Freemem(platform_io^.Monitors.Data);
+  platform_io^.Monitors.Data := nil;
+
+  Freemem(bd);
+End;
+
+// Done
+Function ImGui_ImplSDL2_ProcessEvent_Pas(event: PSDL_Event): Boolean;
+Var
+  ImKey: ImGuiKey;
+  mouse_pos: ImVec2;
+  window_x, window_y: Integer;
+  wheel_x, wheel_y: Single;
+  MouseSrc: ImGuiMouseSource;
+  mouse_button: Integer;
+
+  bd: PImGui_ImplSDL2_Data;
+  io: PImGuiIO;
+  window_event: Uint8;
+  viewport: PImGuiViewport;
+Begin
+  Result := False;
+  io := ImGui.GetIO();
+  bd := ImGui_ImplSDL2_GetBackendData();
+  Case event^.type_ Of
+    SDL_MOUSEMOTION:
+    Begin
+      mouse_pos := ImVec2.New(Single(event^.motion.x), Single(event^.motion.y));
+      If Int64(io^.ConfigFlags And ImGuiConfigFlags_ViewportsEnable) <> 0 Then
+      Begin
+        SDL_GetWindowPosition(SDL_GetWindowFromID(event^.motion.windowID),
+          @window_x, @window_y);
+        mouse_pos.x += window_x;
+        mouse_pos.y += window_y;
+      End;
+
+      If event^.motion.which = SDL_TOUCH_MOUSEID Then
+        ImGui.AddMouseSourceEvent(ImGuiMouseSource_TouchScreen)
+      Else
+        ImGui.AddMouseSourceEvent(ImGuiMouseSource_Mouse);
+
+      ImGui.AddMousePosEvent(mouse_pos.x, mouse_pos.y);
+      Result := True;
+    End;
+    SDL_MOUSEWHEEL:
+    Begin
+      wheel_x := -Single(event^.wheel.x);
+      wheel_y := Single(event^.wheel.y);
+
+      If event^.wheel.which = SDL_TOUCH_MOUSEID Then
+        MouseSrc := ImGuiMouseSource_TouchScreen
+      Else
+        MouseSrc := ImGuiMouseSource_Mouse;
+      ImGui.AddMouseSourceEvent(MouseSrc);
+
+      ImGui.AddMouseWheelEvent(wheel_x, wheel_y);
+      Result := True;
+    End;
+    SDL_MOUSEBUTTONDOWN, SDL_MOUSEBUTTONUP:
+    Begin
+      mouse_button := -1;
+      If (event^.button.button = SDL_BUTTON_LEFT) Then
+        mouse_button := 0;
+      If (event^.button.button = SDL_BUTTON_RIGHT) Then
+        mouse_button := 1;
+      If (event^.button.button = SDL_BUTTON_MIDDLE) Then
+        mouse_button := 2;
+      If (event^.button.button = SDL_BUTTON_X1) Then
+        mouse_button := 3;
+      If (event^.button.button = SDL_BUTTON_X2) Then
+        mouse_button := 4;
+      If (mouse_button = -1) Then
+        Exit;
+
+      If event^.wheel.which = SDL_TOUCH_MOUSEID Then
+        MouseSrc := ImGuiMouseSource_TouchScreen
+      Else
+        MouseSrc := ImGuiMouseSource_Mouse;
+      ImGui.AddMouseSourceEvent(MouseSrc);
+
+      ImGui.AddMouseButtonEvent(mouse_button, (event^.type_ = SDL_MOUSEBUTTONDOWN));
+
+      If (event^.type_ = SDL_MOUSEBUTTONDOWN) Then
+        bd^.MouseButtonsDown := bd^.MouseButtonsDown Or (1 Shl mouse_button)
+      Else
+        bd^.MouseButtonsDown := bd^.MouseButtonsDown And Not (1 Shl mouse_button);
+      Result := True;
+    End;
+    SDL_TEXTINPUT:
+    Begin
+      ImGui.AddInputCharactersUTF8(event^.Text.Text);
+      Result := True;
+    End;
+    SDL_KEYDOWN, SDL_KEYUP:
+    Begin
+      ImGui_ImplSDL2_UpdateKeyModifiers(event^.key.keysym.mod_);
+      ImKey := ImGui_ImplSDL2_KeycodeToImGuiKey(event^.key.keysym.sym);
+      ImGui.AddKeyEvent(ImKey, (event^.type_ = SDL_KEYDOWN));
+      // To support legacy indexing (<1.87 user code). Legacy backend uses SDLK_*** as indices to IsKeyXXX() functions.
+      ImGui.SetKeyEventNativeData(ImKey, event^.key.keysym.sym,
+        event^.key.keysym.scancode, event^.key.keysym.scancode);
+      Result := True;
+    End;
+    // SDL_VERSION_ATLEAST(2,0,9) >> SDL_DISPLAYEVENT
+    SDL_DISPLAYEVENT:
+    Begin
+      bd^.WantUpdateMonitors := True;
+      Result := True;
+    End;
+    SDL_WINDOWEVENT:
+    Begin
+      // - When capturing mouse, SDL will send a bunch of conflicting LEAVE/ENTER
+      //    event on every mouse move, but the final ENTER tends to be right.
+      // - However we won't get a correct LEAVE event for a captured window.
+      // - In some cases, when detaching a window from main viewport SDL may
+      //   send SDL_WINDOWEVENT_ENTER one frame too late,
+      //   causing SDL_WINDOWEVENT_LEAVE on previous frame to interrupt drag
+      //   operation by clear mouse position. This is why
+      //   we delay process the SDL_WINDOWEVENT_LEAVE events by one frame.
+      //   See issue #5012 for details.
+
+      window_event := event^.window.event;
+      If window_event = SDL_WINDOWEVENT_ENTER Then
+      Begin
+        bd^.MouseWindowID := event^.window.windowID;
+        bd^.PendingMouseLeaveFrame := 0;
+      End;
+
+      If window_event = SDL_WINDOWEVENT_LEAVE Then
+        bd^.PendingMouseLeaveFrame := ImGui.GetFrameCount() + 1;
+
+      If window_event = SDL_WINDOWEVENT_FOCUS_GAINED Then
+        ImGui.AddFocusEvent(True)
+      Else If window_event = SDL_WINDOWEVENT_FOCUS_LOST Then
+        ImGui.AddFocusEvent(False);
+
+      If (window_event = SDL_WINDOWEVENT_CLOSE) Or (window_event =
+        SDL_WINDOWEVENT_MOVED) Or (window_event = SDL_WINDOWEVENT_RESIZED) Then
+      Begin
+        viewport := ImGui.FindViewportByPlatformHandle(
+          SDL_GetWindowFromID(event^.window.windowID));
+        If viewport <> nil Then
+        Begin
+          If window_event = SDL_WINDOWEVENT_CLOSE Then
+            viewport^.PlatformRequestClose := True;
+          If window_event = SDL_WINDOWEVENT_MOVED Then
+            viewport^.PlatformRequestMove := True;
+          If window_event = SDL_WINDOWEVENT_RESIZED Then
+            viewport^.PlatformRequestResize := True;
+          Exit(True);
+        End;
+      End;
+      Result := True;
+    End;
+    Else
+      Result := False;
+  End;
+End;
+
+End.