Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 42cfbeb111 | |||
| f57a671d9f | |||
| bdca389f2e | |||
| 0d3fb60b4c | |||
| cdfcc46019 | |||
| 7e118874ce | |||
| 1c8b0cbdc3 | |||
| 65c276b617 |
@@ -0,0 +1,13 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"isRoot": true,
|
||||||
|
"tools": {
|
||||||
|
"tcli": {
|
||||||
|
"version": "0.2.4",
|
||||||
|
"commands": [
|
||||||
|
"tcli"
|
||||||
|
],
|
||||||
|
"rollForward": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
###############################################################################
|
|
||||||
# Set default behavior to automatically normalize line endings.
|
|
||||||
###############################################################################
|
|
||||||
* text=auto
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
# Set default behavior for command prompt diff.
|
|
||||||
#
|
|
||||||
# This is need for earlier builds of msysgit that does not have it on by
|
|
||||||
# default for csharp files.
|
|
||||||
# Note: This is only used by command line
|
|
||||||
###############################################################################
|
|
||||||
#*.cs diff=csharp
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
# Set the merge driver for project and solution files
|
|
||||||
#
|
|
||||||
# Merging from the command prompt will add diff markers to the files if there
|
|
||||||
# are conflicts (Merging from VS is not affected by the settings below, in VS
|
|
||||||
# the diff markers are never inserted). Diff markers may cause the following
|
|
||||||
# file extensions to fail to load in VS. An alternative would be to treat
|
|
||||||
# these files as binary and thus will always conflict and require user
|
|
||||||
# intervention with every merge. To do so, just uncomment the entries below
|
|
||||||
###############################################################################
|
|
||||||
#*.sln merge=binary
|
|
||||||
#*.csproj merge=binary
|
|
||||||
#*.vbproj merge=binary
|
|
||||||
#*.vcxproj merge=binary
|
|
||||||
#*.vcproj merge=binary
|
|
||||||
#*.dbproj merge=binary
|
|
||||||
#*.fsproj merge=binary
|
|
||||||
#*.lsproj merge=binary
|
|
||||||
#*.wixproj merge=binary
|
|
||||||
#*.modelproj merge=binary
|
|
||||||
#*.sqlproj merge=binary
|
|
||||||
#*.wwaproj merge=binary
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
# behavior for image files
|
|
||||||
#
|
|
||||||
# image files are treated as binary by default.
|
|
||||||
###############################################################################
|
|
||||||
#*.jpg binary
|
|
||||||
#*.png binary
|
|
||||||
#*.gif binary
|
|
||||||
|
|
||||||
###############################################################################
|
|
||||||
# diff behavior for common document formats
|
|
||||||
#
|
|
||||||
# Convert binary document formats to text before diffing them. This feature
|
|
||||||
# is only available from the command line. Turn it on by uncommenting the
|
|
||||||
# entries below.
|
|
||||||
###############################################################################
|
|
||||||
#*.doc diff=astextplain
|
|
||||||
#*.DOC diff=astextplain
|
|
||||||
#*.docx diff=astextplain
|
|
||||||
#*.DOCX diff=astextplain
|
|
||||||
#*.dot diff=astextplain
|
|
||||||
#*.DOT diff=astextplain
|
|
||||||
#*.pdf diff=astextplain
|
|
||||||
#*.PDF diff=astextplain
|
|
||||||
#*.rtf diff=astextplain
|
|
||||||
#*.RTF diff=astextplain
|
|
||||||
+91
-3
@@ -1,7 +1,10 @@
|
|||||||
## Ignore Visual Studio temporary files, build results, and
|
## Ignore Visual Studio temporary files, build results, and
|
||||||
## files generated by popular Visual Studio add-ons.
|
## files generated by popular Visual Studio add-ons.
|
||||||
##
|
##
|
||||||
## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore
|
## Get latest from `dotnet new gitignore`
|
||||||
|
|
||||||
|
# dotenv files
|
||||||
|
.env
|
||||||
|
|
||||||
# User-specific files
|
# User-specific files
|
||||||
*.rsuser
|
*.rsuser
|
||||||
@@ -57,11 +60,14 @@ dlldata.c
|
|||||||
# Benchmark Results
|
# Benchmark Results
|
||||||
BenchmarkDotNet.Artifacts/
|
BenchmarkDotNet.Artifacts/
|
||||||
|
|
||||||
# .NET Core
|
# .NET
|
||||||
project.lock.json
|
project.lock.json
|
||||||
project.fragment.lock.json
|
project.fragment.lock.json
|
||||||
artifacts/
|
artifacts/
|
||||||
|
|
||||||
|
# Tye
|
||||||
|
.tye/
|
||||||
|
|
||||||
# ASP.NET Scaffolding
|
# ASP.NET Scaffolding
|
||||||
ScaffoldingReadMe.txt
|
ScaffoldingReadMe.txt
|
||||||
|
|
||||||
@@ -82,6 +88,8 @@ StyleCopReport.xml
|
|||||||
*.pgc
|
*.pgc
|
||||||
*.pgd
|
*.pgd
|
||||||
*.rsp
|
*.rsp
|
||||||
|
# but not Directory.Build.rsp, as it configures directory-level build defaults
|
||||||
|
!Directory.Build.rsp
|
||||||
*.sbr
|
*.sbr
|
||||||
*.tlb
|
*.tlb
|
||||||
*.tli
|
*.tli
|
||||||
@@ -395,4 +403,84 @@ FodyWeavers.xsd
|
|||||||
*.msp
|
*.msp
|
||||||
|
|
||||||
# JetBrains Rider
|
# JetBrains Rider
|
||||||
*.sln.iml
|
*.sln.iml
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
##
|
||||||
|
## Visual studio for Mac
|
||||||
|
##
|
||||||
|
|
||||||
|
|
||||||
|
# globs
|
||||||
|
Makefile.in
|
||||||
|
*.userprefs
|
||||||
|
*.usertasks
|
||||||
|
config.make
|
||||||
|
config.status
|
||||||
|
aclocal.m4
|
||||||
|
install-sh
|
||||||
|
autom4te.cache/
|
||||||
|
*.tar.gz
|
||||||
|
tarballs/
|
||||||
|
test-results/
|
||||||
|
|
||||||
|
# content below from: https://github.com/github/gitignore/blob/main/Global/macOS.gitignore
|
||||||
|
# General
|
||||||
|
.DS_Store
|
||||||
|
.AppleDouble
|
||||||
|
.LSOverride
|
||||||
|
|
||||||
|
# Icon must end with two \r
|
||||||
|
Icon
|
||||||
|
|
||||||
|
|
||||||
|
# Thumbnails
|
||||||
|
._*
|
||||||
|
|
||||||
|
# Files that might appear in the root of a volume
|
||||||
|
.DocumentRevisions-V100
|
||||||
|
.fseventsd
|
||||||
|
.Spotlight-V100
|
||||||
|
.TemporaryItems
|
||||||
|
.Trashes
|
||||||
|
.VolumeIcon.icns
|
||||||
|
.com.apple.timemachine.donotpresent
|
||||||
|
|
||||||
|
# Directories potentially created on remote AFP share
|
||||||
|
.AppleDB
|
||||||
|
.AppleDesktop
|
||||||
|
Network Trash Folder
|
||||||
|
Temporary Items
|
||||||
|
.apdisk
|
||||||
|
|
||||||
|
# content below from: https://github.com/github/gitignore/blob/main/Global/Windows.gitignore
|
||||||
|
# Windows thumbnail cache files
|
||||||
|
Thumbs.db
|
||||||
|
ehthumbs.db
|
||||||
|
ehthumbs_vista.db
|
||||||
|
|
||||||
|
# Dump file
|
||||||
|
*.stackdump
|
||||||
|
|
||||||
|
# Folder config file
|
||||||
|
[Dd]esktop.ini
|
||||||
|
|
||||||
|
# Recycle Bin used on file shares
|
||||||
|
$RECYCLE.BIN/
|
||||||
|
|
||||||
|
# Windows Installer files
|
||||||
|
*.cab
|
||||||
|
*.msi
|
||||||
|
*.msix
|
||||||
|
*.msm
|
||||||
|
*.msp
|
||||||
|
|
||||||
|
# Windows shortcuts
|
||||||
|
*.lnk
|
||||||
|
|
||||||
|
# Vim temporary swap files
|
||||||
|
*.swp
|
||||||
|
|
||||||
|
# Thunderstore packaging
|
||||||
|
dist/
|
||||||
|
build/
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
# Changelog
|
||||||
|
|
||||||
|
## 2.0.0
|
||||||
|
|
||||||
|
Initial BepisLoader Release
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
<Project>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Build Thunderstore package by calling `dotnet build -c Release -target:PackTS -v d` (verbosity detailed).
|
||||||
|
Publish to Thunderstore by including `-property:PublishTS=true` in the command.
|
||||||
|
-->
|
||||||
|
<Target Name="PackTS" DependsOnTargets="Build">
|
||||||
|
<CallTarget Targets="PackTS_Execute" Condition="'$(ThunderstorePackable)' == 'true' and '$(Configuration)' == 'Release'" />
|
||||||
|
</Target>
|
||||||
|
|
||||||
|
<Target Name="PackTS_Execute">
|
||||||
|
<PropertyGroup>
|
||||||
|
<BuildArgument Condition="'$(PublishTS)' != 'true'">build</BuildArgument>
|
||||||
|
<BuildArgument Condition="'$(PublishTS)' == 'true'">publish</BuildArgument>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<Exec Command="dotnet tool restore" WorkingDirectory="$(SolutionDir)" />
|
||||||
|
<Exec Command="dotnet tcli $(BuildArgument) --package-version $(Version)" WorkingDirectory="$(SolutionDir)" />
|
||||||
|
</Target>
|
||||||
|
</Project>
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
MIT No Attribution
|
MIT No Attribution
|
||||||
|
|
||||||
Copyright 2023 art0007i
|
Copyright (c) 2025 art0007i
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
@@ -0,0 +1,203 @@
|
|||||||
|
using BepInEx.Configuration;
|
||||||
|
using Elements.Core;
|
||||||
|
using ResoniteImGuiLib.SDL3;
|
||||||
|
using SDL3;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
|
||||||
|
namespace ResoniteImGuiLib;
|
||||||
|
|
||||||
|
public class ImGuiInstance
|
||||||
|
{
|
||||||
|
private ImGuiInstance(string name)
|
||||||
|
{
|
||||||
|
_name = name;
|
||||||
|
_entry = Plugin.Config.Bind("Windows", "Rect_" + Name, new int4(200, 200, 400, 300));
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Name => _name;
|
||||||
|
/// <summary>
|
||||||
|
/// Called whenever ImGui is being rendered. You can use ImGui functions inside here.
|
||||||
|
/// </summary>
|
||||||
|
public event Action? Layout;
|
||||||
|
/// <summary>
|
||||||
|
/// Called whenever the window is processing an event. Returning false means that the event will not be processed further.
|
||||||
|
/// </summary>
|
||||||
|
public event Func<SDL.Event, bool>? SdlEvent;
|
||||||
|
|
||||||
|
private string _name;
|
||||||
|
private ConfigEntry<int4> _entry;
|
||||||
|
|
||||||
|
private static bool IsSDL3Running => _sdl3Thread is { IsAlive: true };
|
||||||
|
private static Thread? _sdl3Thread;
|
||||||
|
private static Dictionary<string, ImGuiInstance> GuiInstances = new();
|
||||||
|
internal static ConcurrentQueue<ImGuiInstance> newInstances = new();
|
||||||
|
internal static ConcurrentQueue<(string, Action<SDL3Window>)> windowRequests = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tries to open the window. If it's already open, nothing happens. This function may be expensive to call rapidly.
|
||||||
|
/// </summary>
|
||||||
|
public void OpenWindow()
|
||||||
|
{
|
||||||
|
newInstances.Enqueue(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a reference to the SDL3Window inside of a callback.
|
||||||
|
/// The callback will be called inside the SDL3 Thread.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="callback"></param>
|
||||||
|
public void GetImGui(Action<SDL3Window> callback)
|
||||||
|
{
|
||||||
|
windowRequests.Enqueue((Name, callback));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new ImGuiInstance with the "global" name or returns it if it already exists.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="onReady">Callback for when the ImGui instance is ready. You can put initialization logic here.</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static ImGuiInstance GetOrCreate(ImGuiReady onReady) => GetOrCreate("global", onReady);
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new ImGuiInstance or returns one if it already exists.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="onReady">Callback for when the ImGui instance is ready. You can put initialization logic here.</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static ImGuiInstance GetOrCreate(string name = "global", ImGuiReady? onReady = null)
|
||||||
|
{
|
||||||
|
if (GuiInstances.TryGetValue(name, out var instance))
|
||||||
|
{
|
||||||
|
if (onReady != null)
|
||||||
|
instance.GetImGui((gui) => onReady(gui, false));
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
instance = new ImGuiInstance(name);
|
||||||
|
GuiInstances.Add(name, instance);
|
||||||
|
if (onReady != null)
|
||||||
|
instance.GetImGui((gui) => onReady(gui, true));
|
||||||
|
|
||||||
|
newInstances.Enqueue(instance);
|
||||||
|
TryStartSDL3Thread();
|
||||||
|
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void TryStartSDL3Thread()
|
||||||
|
{
|
||||||
|
if (IsSDL3Running)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_sdl3Thread = new Thread(RunSDL3)
|
||||||
|
{
|
||||||
|
Name = $"Resonite ImGui SDL3",
|
||||||
|
Priority = ThreadPriority.Highest,
|
||||||
|
IsBackground = false
|
||||||
|
};
|
||||||
|
|
||||||
|
_sdl3Thread.Start();
|
||||||
|
}
|
||||||
|
static Dictionary<string, SDL3Window> windows = new();
|
||||||
|
static Dictionary<uint, SDL3Window> windowsById = new();
|
||||||
|
private static void RunSDL3()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
while (newInstances.TryDequeue(out var request))
|
||||||
|
{
|
||||||
|
if (windows.ContainsKey(request.Name)) continue;
|
||||||
|
|
||||||
|
SDL3Window app = new SDL3Window("ImGuiContext: " + request.Name, request._entry.Value.x, request._entry.Value.y, request._entry.Value.z, request._entry.Value.w);
|
||||||
|
app.WindowRectModified += (rect) => request._entry.Value = rect;
|
||||||
|
// TODO: maybe listen to config changes to resize window
|
||||||
|
// request._entry.SettingChanged += (_, _) => { };
|
||||||
|
app.RenderCallback = request.Layout;
|
||||||
|
app.SdlEventReceived = request.SdlEvent;
|
||||||
|
if (!windows.TryAdd(request.Name, app))
|
||||||
|
{
|
||||||
|
Plugin.Log.LogError($"Failed to add window with name {request.Name}!");
|
||||||
|
}
|
||||||
|
if (!windowsById.TryAdd(app.SdlWindowId, app))
|
||||||
|
{
|
||||||
|
Plugin.Log.LogError($"Failed to add window with id {app.SdlWindowId}, name {request.Name}!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int i = 0; i < windowRequests.Count; i++)
|
||||||
|
{
|
||||||
|
if (windowRequests.TryDequeue(out var callback))
|
||||||
|
{
|
||||||
|
if (windows.TryGetValue(callback.Item1, out var window))
|
||||||
|
{
|
||||||
|
callback.Item2?.Invoke(window);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
windowRequests.Enqueue(callback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (SDL.PollEvent(out SDL.Event ev))
|
||||||
|
{
|
||||||
|
if (windowsById.TryGetValue(ev.Window.WindowID, out var window))
|
||||||
|
{
|
||||||
|
window.EventQueue.Enqueue(ev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var removed = new HashSet<(string, uint)>();
|
||||||
|
foreach (var (key, window) in windows)
|
||||||
|
{
|
||||||
|
if(window.Disposed)
|
||||||
|
{
|
||||||
|
removed.Add((key, window.SdlWindowId));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
window.SetContext();
|
||||||
|
|
||||||
|
if (Plugin.CancellationToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
window.ForceDispose();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (window.ShouldClose)
|
||||||
|
{
|
||||||
|
removed.Add((key, window.SdlWindowId));
|
||||||
|
window.Dispose();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
window.RunOneFrame();
|
||||||
|
}
|
||||||
|
foreach (var (k, k2) in removed)
|
||||||
|
{
|
||||||
|
var b = windows.Remove(k);
|
||||||
|
var b2 = windowsById.Remove(k2);
|
||||||
|
Plugin.Log.LogDebug($"Removed window {k}, success codes: {b}|{b2}");
|
||||||
|
}
|
||||||
|
if (Plugin.CancellationToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prevent busy looping when nothing is happening.
|
||||||
|
if (windows.Count == 0)
|
||||||
|
{
|
||||||
|
Thread.Sleep(1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Plugin.Log.LogError($"SDL3 Thread crashed: {e}");
|
||||||
|
}
|
||||||
|
|
||||||
|
_sdl3Thread?.Interrupt();
|
||||||
|
_sdl3Thread = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public delegate void ImGuiReady(SDL3Window imGui, bool isNewInstance);
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
using BepInEx;
|
||||||
|
using BepInEx.Configuration;
|
||||||
|
using BepInEx.Logging;
|
||||||
|
using BepInEx.NET.Common;
|
||||||
|
using BepInExResoniteShim;
|
||||||
|
using Elements.Core;
|
||||||
|
using FrooxEngine;
|
||||||
|
|
||||||
|
namespace ResoniteImGuiLib;
|
||||||
|
|
||||||
|
public static class ImGuiLib
|
||||||
|
{
|
||||||
|
public static ImGuiInstance GetOrCreateInstance(ImGuiReady onReady)
|
||||||
|
{
|
||||||
|
return GetOrCreateInstance("global", onReady);
|
||||||
|
}
|
||||||
|
public static ImGuiInstance GetOrCreateInstance(string name = "global", ImGuiReady? onReady = null)
|
||||||
|
{
|
||||||
|
return ImGuiInstance.GetOrCreate(name, onReady);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[ResonitePlugin(PluginMetadata.GUID, PluginMetadata.NAME, PluginMetadata.VERSION, PluginMetadata.AUTHORS, PluginMetadata.REPOSITORY_URL)]
|
||||||
|
[BepInDependency(BepInExResoniteShim.PluginMetadata.GUID, BepInDependency.DependencyFlags.HardDependency)]
|
||||||
|
public class Plugin : BasePlugin
|
||||||
|
{
|
||||||
|
#nullable disable
|
||||||
|
internal static new ManualLogSource Log;
|
||||||
|
internal static new ConfigFile Config;
|
||||||
|
|
||||||
|
internal static ConfigEntry<color> DefaultBackgroundColor;
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
|
public static readonly CancellationTokenSource CancellationToken = new CancellationTokenSource();
|
||||||
|
|
||||||
|
public override void Load()
|
||||||
|
{
|
||||||
|
Log = base.Log;
|
||||||
|
Config = base.Config;
|
||||||
|
|
||||||
|
DefaultBackgroundColor = Config.Bind("General", "DefaultBackgroundColor", new color(0.45f, 0.55f, 0.60f, 1.00f), "Default background color that new ImGui windows will have.");
|
||||||
|
|
||||||
|
BepisResoniteWrapper.ResoniteHooks.OnEngineReady += () =>
|
||||||
|
{
|
||||||
|
Engine.Current.OnShutdown += () => CancellationToken.Cancel();
|
||||||
|
Engine.Current.OnShutdownRequest += _ => CancellationToken.Cancel();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"profiles": {
|
||||||
|
"Launch": {
|
||||||
|
"commandName": "Executable",
|
||||||
|
"executablePath": "$(GamePath)Renderite.Host.exe",
|
||||||
|
"commandLineArgs": "-Screen $(ResoniteLaunchArguments)",
|
||||||
|
"workingDirectory": "$(GamePath)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<Version>2.1.0</Version>
|
||||||
|
<Authors>art0007i</Authors>
|
||||||
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
|
<RepositoryUrl>https://github.com/art0007i/ResoniteImGuiLib</RepositoryUrl>
|
||||||
|
<PackageId>art0007i.ResoniteImGuiLib</PackageId>
|
||||||
|
<Product>ResoniteImGuiLib</Product>
|
||||||
|
<RootNamespace>ResoniteImGuiLib</RootNamespace>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<Deterministic>true</Deterministic>
|
||||||
|
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||||
|
<CopyToPlugins>false</CopyToPlugins>
|
||||||
|
<ThunderstorePackable>true</ThunderstorePackable>
|
||||||
|
<GamePath Condition="'$(ResonitePath)' != ''">$(ResonitePath)/</GamePath>
|
||||||
|
<GamePath Condition="Exists('$(MSBuildProgramFiles32)\Steam\steamapps\common\Resonite\')">$(MSBuildProgramFiles32)\Steam\steamapps\common\Resonite\</GamePath>
|
||||||
|
<GamePath Condition="Exists('$(HOME)/.steam/steam/steamapps/common/Resonite/')">$(HOME)/.steam/steam/steamapps/common/Resonite/</GamePath>
|
||||||
|
<PluginTargetDir>$(GamePath)BepInEx\plugins\$(AssemblyName)</PluginTargetDir>
|
||||||
|
<RestoreAdditionalProjectSources>
|
||||||
|
https://nuget-modding.resonite.net/v3/index.json;
|
||||||
|
</RestoreAdditionalProjectSources>
|
||||||
|
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||||
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<!-- Modding dependencies -->
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="BepInEx.ResonitePluginInfoProps" Version="3.*" />
|
||||||
|
<PackageReference Include="ResoniteModding.BepInExResoniteShim" Version="0.8.*" />
|
||||||
|
<PackageReference Include="ResoniteModding.BepisResoniteWrapper" Version="1.0.*" />
|
||||||
|
|
||||||
|
<PackageReference Include="ImGui.NET" Version="1.91.6.1" />
|
||||||
|
<PackageReference Include="SDL3-CS" Version="3.2.18" />
|
||||||
|
<PackageReference Include="SDL3-CS.Native" Version="3.2.18" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<!-- NuGet fallback stripped game references -->
|
||||||
|
<ItemGroup Condition="!Exists('$(GamePath)')">
|
||||||
|
<PackageReference Include="Resonite.GameLibs" Version="2025.*" PrivateAssets="all" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<!-- Local game references -->
|
||||||
|
<ItemGroup Condition="Exists('$(GamePath)')">
|
||||||
|
<Reference Include="FrooxEngine">
|
||||||
|
<HintPath>$(GamePath)FrooxEngine.dll</HintPath>
|
||||||
|
<Private>False</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Elements.Core">
|
||||||
|
<HintPath>$(GamePath)Elements.Core.dll</HintPath>
|
||||||
|
<Private>False</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Renderite.Shared">
|
||||||
|
<HintPath>$(GamePath)Renderite.Shared.dll</HintPath>
|
||||||
|
<Private>False</Private>
|
||||||
|
</Reference>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<!-- Post-build copy to game plugins folder -->
|
||||||
|
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
|
||||||
|
<ItemGroup>
|
||||||
|
<PluginFiles Include="$(TargetPath)" />
|
||||||
|
<PluginFiles Include="$(TargetDir)$(TargetName).pdb" Condition="Exists('$(TargetDir)$(TargetName).pdb')" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<Copy SourceFiles="@(PluginFiles)" DestinationFolder="$(PluginTargetDir)" Condition="'$(CopyToPlugins)' == 'true'" />
|
||||||
|
<Message Text="Copied plugin files to $(PluginTargetDir)" Importance="high" Condition="'$(CopyToPlugins)' == 'true'" />
|
||||||
|
</Target>
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,427 @@
|
|||||||
|
using ImGuiNET;
|
||||||
|
using SDL3;
|
||||||
|
|
||||||
|
namespace ResoniteImGuiLib.SDL3;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Implementation of SDL3 platform backend for ImGui.
|
||||||
|
/// https://github.com/ocornut/imgui/blob/master/backends/imgui_impl_sdl3.h
|
||||||
|
/// </summary>
|
||||||
|
public class ImGuiSDL3 : IDisposable
|
||||||
|
{
|
||||||
|
public readonly IntPtr Window;
|
||||||
|
public readonly IntPtr Renderer;
|
||||||
|
public readonly uint WindowId;
|
||||||
|
private uint _mouseWindowId;
|
||||||
|
private int _mousePendingLeaveFrame;
|
||||||
|
private IntPtr[] _mouseCursors = new IntPtr[(int)ImGuiMouseCursor.COUNT];
|
||||||
|
private IntPtr _mouseLastCursor = -1;
|
||||||
|
private int _mouseButtonsDown;
|
||||||
|
|
||||||
|
// I don't think we actually need these since it works just fine without them
|
||||||
|
//
|
||||||
|
// [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
// public delegate IntPtr Platform_GetClipboardTextFn(IntPtr ctx);
|
||||||
|
//
|
||||||
|
// [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||||
|
// public delegate void Platform_SetClipboardTextFn(IntPtr ctx, IntPtr text);
|
||||||
|
//
|
||||||
|
// private static Platform_GetClipboardTextFn getClipboardDelegate = GetClipboardText;
|
||||||
|
// private static Platform_SetClipboardTextFn setClipboardDelegate = SetClipboardText;
|
||||||
|
//
|
||||||
|
// private static IntPtr getClipboardPtr;
|
||||||
|
// private static IntPtr setClipboardPtr;
|
||||||
|
//
|
||||||
|
// private static IntPtr clipboardTextPtr = IntPtr.Zero;
|
||||||
|
|
||||||
|
public ImGuiSDL3(IntPtr window, IntPtr renderer)
|
||||||
|
{
|
||||||
|
ImGuiIOPtr io = ImGui.GetIO();
|
||||||
|
|
||||||
|
io.BackendFlags |= ImGuiBackendFlags.HasMouseCursors;
|
||||||
|
io.BackendFlags |= ImGuiBackendFlags.HasSetMousePos;
|
||||||
|
Window = window;
|
||||||
|
WindowId = SDL.GetWindowID(Window);
|
||||||
|
Renderer = renderer;
|
||||||
|
|
||||||
|
// I don't think we actually need these since it works just fine without them
|
||||||
|
//
|
||||||
|
// ImGuiPlatformIOPtr platformIo = ImGui.GetPlatformIO();
|
||||||
|
// platformIo.Platform_SetClipboardTextFn = Marshal.GetFunctionPointerForDelegate(setClipboardDelegate);
|
||||||
|
// platformIo.Platform_GetClipboardTextFn = Marshal.GetFunctionPointerForDelegate(getClipboardDelegate);
|
||||||
|
// getClipboardPtr = platformIo.Platform_GetClipboardTextFn;
|
||||||
|
// setClipboardPtr = platformIo.Platform_SetClipboardTextFn;
|
||||||
|
|
||||||
|
_mouseCursors[(int)ImGuiMouseCursor.Arrow] = SDL.CreateSystemCursor(SDL.SystemCursor.Default);
|
||||||
|
_mouseCursors[(int)ImGuiMouseCursor.TextInput] = SDL.CreateSystemCursor(SDL.SystemCursor.Text);
|
||||||
|
_mouseCursors[(int)ImGuiMouseCursor.ResizeAll] = SDL.CreateSystemCursor(SDL.SystemCursor.Move);
|
||||||
|
_mouseCursors[(int)ImGuiMouseCursor.ResizeNS] = SDL.CreateSystemCursor(SDL.SystemCursor.NSResize);
|
||||||
|
_mouseCursors[(int)ImGuiMouseCursor.ResizeEW] = SDL.CreateSystemCursor(SDL.SystemCursor.EWResize);
|
||||||
|
_mouseCursors[(int)ImGuiMouseCursor.ResizeNESW] = SDL.CreateSystemCursor(SDL.SystemCursor.NESWResize);
|
||||||
|
_mouseCursors[(int)ImGuiMouseCursor.ResizeNWSE] = SDL.CreateSystemCursor(SDL.SystemCursor.NWSEResize);
|
||||||
|
_mouseCursors[(int)ImGuiMouseCursor.Hand] = SDL.CreateSystemCursor(SDL.SystemCursor.Pointer);
|
||||||
|
_mouseCursors[(int)ImGuiMouseCursor.NotAllowed] = SDL.CreateSystemCursor(SDL.SystemCursor.NotAllowed);
|
||||||
|
|
||||||
|
ImGuiViewportPtr viewport = ImGui.GetMainViewport();
|
||||||
|
SetupPlatformHandles(viewport, window);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
foreach (IntPtr cursor in _mouseCursors)
|
||||||
|
SDL.DestroyCursor(cursor);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void NewFrame()
|
||||||
|
{
|
||||||
|
ImGuiIOPtr io = ImGui.GetIO();
|
||||||
|
|
||||||
|
SDL.GetWindowSize(Window, out int w, out int h);
|
||||||
|
if (SDL.GetWindowFlags(Window).HasFlag(SDL.WindowFlags.Minimized))
|
||||||
|
{
|
||||||
|
w = h = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL.GetWindowSizeInPixels(Window, out int displayW, out int displayH);
|
||||||
|
io.DisplaySize = new System.Numerics.Vector2(w, h);
|
||||||
|
|
||||||
|
if (w > 0 && h > 0)
|
||||||
|
io.DisplayFramebufferScale = new System.Numerics.Vector2((float)displayW / w, (float)displayH / h);
|
||||||
|
|
||||||
|
if (_mousePendingLeaveFrame > 0 && _mousePendingLeaveFrame >= ImGui.GetFrameCount())
|
||||||
|
{
|
||||||
|
_mouseWindowId = 0;
|
||||||
|
_mousePendingLeaveFrame = 0;
|
||||||
|
io.AddMousePosEvent(-float.MaxValue, -float.MaxValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateMouseData();
|
||||||
|
UpdateMouseCursor();
|
||||||
|
}
|
||||||
|
|
||||||
|
public unsafe bool ProcessEvent(SDL.Event e)
|
||||||
|
{
|
||||||
|
ImGuiIOPtr io = ImGui.GetIO();
|
||||||
|
|
||||||
|
switch ((SDL.EventType)e.Type)
|
||||||
|
{
|
||||||
|
case SDL.EventType.MouseMotion:
|
||||||
|
if (GetViewportForWindowId(e.Motion.WindowID) == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
io.AddMouseSourceEvent(e.Motion.Which == SDL.TouchMouseID ? ImGuiMouseSource.TouchScreen : ImGuiMouseSource.Mouse);
|
||||||
|
io.AddMousePosEvent(e.Motion.X, e.Motion.Y);
|
||||||
|
return true;
|
||||||
|
case SDL.EventType.MouseWheel:
|
||||||
|
if (GetViewportForWindowId(e.Wheel.WindowID) == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
float wheelX = -e.Wheel.X;
|
||||||
|
float wheelY = e.Wheel.Y;
|
||||||
|
|
||||||
|
io.AddMouseSourceEvent(e.Wheel.Which == SDL.TouchMouseID ? ImGuiMouseSource.TouchScreen : ImGuiMouseSource.Mouse);
|
||||||
|
io.AddMouseWheelEvent(wheelX, wheelY);
|
||||||
|
return true;
|
||||||
|
case SDL.EventType.MouseButtonDown:
|
||||||
|
case SDL.EventType.MouseButtonUp:
|
||||||
|
if (GetViewportForWindowId(e.Button.WindowID) == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
int mouseButton = e.Button.Button switch
|
||||||
|
{
|
||||||
|
SDL.ButtonLeft => 0,
|
||||||
|
SDL.ButtonRight => 1,
|
||||||
|
SDL.ButtonMiddle => 2,
|
||||||
|
SDL.ButtonX1 => 3,
|
||||||
|
SDL.ButtonX2 => 4,
|
||||||
|
_ => -1
|
||||||
|
};
|
||||||
|
if (mouseButton == -1) break;
|
||||||
|
|
||||||
|
io.AddMouseSourceEvent(e.Button.Which == SDL.TouchMouseID ? ImGuiMouseSource.TouchScreen : ImGuiMouseSource.Mouse);
|
||||||
|
io.AddMouseButtonEvent(mouseButton, (SDL.EventType)e.Type == SDL.EventType.MouseButtonDown);
|
||||||
|
_mouseButtonsDown = (SDL.EventType)e.Type == SDL.EventType.MouseButtonDown ? _mouseButtonsDown | 1 << mouseButton : _mouseButtonsDown & ~(1 << mouseButton);
|
||||||
|
return true;
|
||||||
|
case SDL.EventType.TextInput:
|
||||||
|
if (GetViewportForWindowId(e.Text.WindowID) == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
ImGuiNative.ImGuiIO_AddInputCharactersUTF8(io, (byte*)e.Text.Text);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
case SDL.EventType.KeyDown:
|
||||||
|
case SDL.EventType.KeyUp:
|
||||||
|
if (GetViewportForWindowId(e.Key.WindowID) == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
UpdateKeyModifiers(e.Key.Mod);
|
||||||
|
ImGuiKey imguiKey = KeyEventToImGui(e.Key.Key, e.Key.Scancode);
|
||||||
|
bool pressed = (SDL.EventType)e.Type == SDL.EventType.KeyDown;
|
||||||
|
|
||||||
|
io.AddKeyEvent(imguiKey, pressed);
|
||||||
|
io.SetKeyEventNativeData(imguiKey, (int)e.Key.Key, (int)e.Key.Scancode, (int)e.Key.Scancode);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
case SDL.EventType.WindowMouseEnter:
|
||||||
|
if (GetViewportForWindowId(e.Window.WindowID) == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
_mouseWindowId = e.Window.WindowID;
|
||||||
|
_mousePendingLeaveFrame = 0;
|
||||||
|
return true;
|
||||||
|
case SDL.EventType.WindowMouseLeave:
|
||||||
|
if (GetViewportForWindowId(e.Window.WindowID) == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
_mousePendingLeaveFrame = ImGui.GetFrameCount() + 1;
|
||||||
|
return true;
|
||||||
|
case SDL.EventType.WindowFocusGained:
|
||||||
|
case SDL.EventType.WindowFocusLost:
|
||||||
|
if (GetViewportForWindowId(e.Window.WindowID) == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
io.AddFocusEvent((SDL.EventType)e.Type == SDL.EventType.WindowFocusGained);
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateMouseData()
|
||||||
|
{
|
||||||
|
ImGuiIOPtr io = ImGui.GetIO();
|
||||||
|
|
||||||
|
IntPtr focusedWindow = SDL.GetKeyboardFocus();
|
||||||
|
bool isAppFocused = focusedWindow == Window;
|
||||||
|
if (isAppFocused)
|
||||||
|
{
|
||||||
|
if (io.WantSetMousePos)
|
||||||
|
{
|
||||||
|
SDL.WarpMouseInWindow(Window, (int)io.MousePos.X, (int)io.MousePos.Y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateMouseCursor()
|
||||||
|
{
|
||||||
|
ImGuiIOPtr io = ImGui.GetIO();
|
||||||
|
if ((io.ConfigFlags & ImGuiConfigFlags.NoMouseCursorChange) != 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
ImGuiMouseCursor imguiCursor = ImGui.GetMouseCursor();
|
||||||
|
|
||||||
|
if (io.MouseDrawCursor || imguiCursor == ImGuiMouseCursor.None)
|
||||||
|
{
|
||||||
|
SDL.HideCursor();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
IntPtr expectedCursor = _mouseCursors[(int)imguiCursor];
|
||||||
|
if (_mouseLastCursor != expectedCursor)
|
||||||
|
{
|
||||||
|
SDL.SetCursor(expectedCursor);
|
||||||
|
_mouseLastCursor = expectedCursor;
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL.ShowCursor();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private ImGuiKey KeyEventToImGui(SDL.Keycode keycoade, SDL.Scancode scancode)
|
||||||
|
{
|
||||||
|
switch (scancode)
|
||||||
|
{
|
||||||
|
case SDL.Scancode.Kp0: return ImGuiKey.Keypad0;
|
||||||
|
case SDL.Scancode.Kp1: return ImGuiKey.Keypad1;
|
||||||
|
case SDL.Scancode.Kp2: return ImGuiKey.Keypad2;
|
||||||
|
case SDL.Scancode.Kp3: return ImGuiKey.Keypad3;
|
||||||
|
case SDL.Scancode.Kp4: return ImGuiKey.Keypad4;
|
||||||
|
case SDL.Scancode.Kp5: return ImGuiKey.Keypad5;
|
||||||
|
case SDL.Scancode.Kp6: return ImGuiKey.Keypad6;
|
||||||
|
case SDL.Scancode.Kp7: return ImGuiKey.Keypad7;
|
||||||
|
case SDL.Scancode.Kp8: return ImGuiKey.Keypad8;
|
||||||
|
case SDL.Scancode.Kp9: return ImGuiKey.Keypad9;
|
||||||
|
case SDL.Scancode.KpPeriod: return ImGuiKey.KeypadDecimal;
|
||||||
|
case SDL.Scancode.KpDivide: return ImGuiKey.KeypadDivide;
|
||||||
|
case SDL.Scancode.KpMultiply: return ImGuiKey.KeypadMultiply;
|
||||||
|
case SDL.Scancode.KpMinus: return ImGuiKey.KeypadSubtract;
|
||||||
|
case SDL.Scancode.KpPlus: return ImGuiKey.KeypadAdd;
|
||||||
|
case SDL.Scancode.KpEnter: return ImGuiKey.KeypadEnter;
|
||||||
|
case SDL.Scancode.KpEquals: return ImGuiKey.KeypadEqual;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (keycoade)
|
||||||
|
{
|
||||||
|
case SDL.Keycode.Tab: return ImGuiKey.Tab;
|
||||||
|
case SDL.Keycode.Left: return ImGuiKey.LeftArrow;
|
||||||
|
case SDL.Keycode.Right: return ImGuiKey.RightArrow;
|
||||||
|
case SDL.Keycode.Up: return ImGuiKey.UpArrow;
|
||||||
|
case SDL.Keycode.Down: return ImGuiKey.DownArrow;
|
||||||
|
case SDL.Keycode.Pageup: return ImGuiKey.PageUp;
|
||||||
|
case SDL.Keycode.Pagedown: return ImGuiKey.PageDown;
|
||||||
|
case SDL.Keycode.Home: return ImGuiKey.Home;
|
||||||
|
case SDL.Keycode.End: return ImGuiKey.End;
|
||||||
|
case SDL.Keycode.Insert: return ImGuiKey.Insert;
|
||||||
|
case SDL.Keycode.Delete: return ImGuiKey.Delete;
|
||||||
|
case SDL.Keycode.Backspace: return ImGuiKey.Backspace;
|
||||||
|
case SDL.Keycode.Space: return ImGuiKey.Space;
|
||||||
|
case SDL.Keycode.Return: return ImGuiKey.Enter;
|
||||||
|
case SDL.Keycode.Escape: return ImGuiKey.Escape;
|
||||||
|
case SDL.Keycode.Apostrophe: return ImGuiKey.Apostrophe;
|
||||||
|
case SDL.Keycode.Comma: return ImGuiKey.Comma;
|
||||||
|
case SDL.Keycode.Minus: return ImGuiKey.Minus;
|
||||||
|
case SDL.Keycode.Period: return ImGuiKey.Period;
|
||||||
|
case SDL.Keycode.Slash: return ImGuiKey.Slash;
|
||||||
|
case SDL.Keycode.Semicolon: return ImGuiKey.Semicolon;
|
||||||
|
case SDL.Keycode.Equals: return ImGuiKey.Equal;
|
||||||
|
case SDL.Keycode.LeftBracket: return ImGuiKey.LeftBracket;
|
||||||
|
case SDL.Keycode.Backslash: return ImGuiKey.Backslash;
|
||||||
|
case SDL.Keycode.RightBracket: return ImGuiKey.RightBracket;
|
||||||
|
case SDL.Keycode.Grave: return ImGuiKey.GraveAccent;
|
||||||
|
case SDL.Keycode.Capslock: return ImGuiKey.CapsLock;
|
||||||
|
case SDL.Keycode.ScrollLock: return ImGuiKey.ScrollLock;
|
||||||
|
case SDL.Keycode.NumLockClear: return ImGuiKey.NumLock;
|
||||||
|
case SDL.Keycode.PrintScreen: return ImGuiKey.PrintScreen;
|
||||||
|
case SDL.Keycode.Pause: return ImGuiKey.Pause;
|
||||||
|
case SDL.Keycode.LCtrl: return ImGuiKey.LeftCtrl;
|
||||||
|
case SDL.Keycode.LShift: return ImGuiKey.LeftShift;
|
||||||
|
case SDL.Keycode.LAlt: return ImGuiKey.LeftAlt;
|
||||||
|
case SDL.Keycode.LGui: return ImGuiKey.LeftSuper;
|
||||||
|
case SDL.Keycode.RCtrl: return ImGuiKey.RightCtrl;
|
||||||
|
case SDL.Keycode.RShift: return ImGuiKey.RightShift;
|
||||||
|
case SDL.Keycode.RAlt: return ImGuiKey.RightAlt;
|
||||||
|
case SDL.Keycode.RGUI: return ImGuiKey.RightSuper;
|
||||||
|
case SDL.Keycode.Application: return ImGuiKey.Menu;
|
||||||
|
case SDL.Keycode.Alpha0: return ImGuiKey._0;
|
||||||
|
case SDL.Keycode.Alpha1: return ImGuiKey._1;
|
||||||
|
case SDL.Keycode.Alpha2: return ImGuiKey._2;
|
||||||
|
case SDL.Keycode.Alpha3: return ImGuiKey._3;
|
||||||
|
case SDL.Keycode.Alpha4: return ImGuiKey._4;
|
||||||
|
case SDL.Keycode.Alpha5: return ImGuiKey._5;
|
||||||
|
case SDL.Keycode.Alpha6: return ImGuiKey._6;
|
||||||
|
case SDL.Keycode.Alpha7: return ImGuiKey._7;
|
||||||
|
case SDL.Keycode.Alpha8: return ImGuiKey._8;
|
||||||
|
case SDL.Keycode.Alpha9: return ImGuiKey._9;
|
||||||
|
case SDL.Keycode.A: return ImGuiKey.A;
|
||||||
|
case SDL.Keycode.B: return ImGuiKey.B;
|
||||||
|
case SDL.Keycode.C: return ImGuiKey.C;
|
||||||
|
case SDL.Keycode.D: return ImGuiKey.D;
|
||||||
|
case SDL.Keycode.E: return ImGuiKey.E;
|
||||||
|
case SDL.Keycode.F: return ImGuiKey.F;
|
||||||
|
case SDL.Keycode.G: return ImGuiKey.G;
|
||||||
|
case SDL.Keycode.H: return ImGuiKey.H;
|
||||||
|
case SDL.Keycode.I: return ImGuiKey.I;
|
||||||
|
case SDL.Keycode.J: return ImGuiKey.J;
|
||||||
|
case SDL.Keycode.K: return ImGuiKey.K;
|
||||||
|
case SDL.Keycode.L: return ImGuiKey.L;
|
||||||
|
case SDL.Keycode.M: return ImGuiKey.M;
|
||||||
|
case SDL.Keycode.N: return ImGuiKey.N;
|
||||||
|
case SDL.Keycode.O: return ImGuiKey.O;
|
||||||
|
case SDL.Keycode.P: return ImGuiKey.P;
|
||||||
|
case SDL.Keycode.Q: return ImGuiKey.Q;
|
||||||
|
case SDL.Keycode.R: return ImGuiKey.R;
|
||||||
|
case SDL.Keycode.S: return ImGuiKey.S;
|
||||||
|
case SDL.Keycode.T: return ImGuiKey.T;
|
||||||
|
case SDL.Keycode.U: return ImGuiKey.U;
|
||||||
|
case SDL.Keycode.V: return ImGuiKey.V;
|
||||||
|
case SDL.Keycode.W: return ImGuiKey.W;
|
||||||
|
case SDL.Keycode.X: return ImGuiKey.X;
|
||||||
|
case SDL.Keycode.Y: return ImGuiKey.Y;
|
||||||
|
case SDL.Keycode.Z: return ImGuiKey.Z;
|
||||||
|
case SDL.Keycode.F1: return ImGuiKey.F1;
|
||||||
|
case SDL.Keycode.F2: return ImGuiKey.F2;
|
||||||
|
case SDL.Keycode.F3: return ImGuiKey.F3;
|
||||||
|
case SDL.Keycode.F4: return ImGuiKey.F4;
|
||||||
|
case SDL.Keycode.F5: return ImGuiKey.F5;
|
||||||
|
case SDL.Keycode.F6: return ImGuiKey.F6;
|
||||||
|
case SDL.Keycode.F7: return ImGuiKey.F7;
|
||||||
|
case SDL.Keycode.F8: return ImGuiKey.F8;
|
||||||
|
case SDL.Keycode.F9: return ImGuiKey.F9;
|
||||||
|
case SDL.Keycode.F10: return ImGuiKey.F10;
|
||||||
|
case SDL.Keycode.F11: return ImGuiKey.F11;
|
||||||
|
case SDL.Keycode.F12: return ImGuiKey.F12;
|
||||||
|
case SDL.Keycode.F13: return ImGuiKey.F13;
|
||||||
|
case SDL.Keycode.F14: return ImGuiKey.F14;
|
||||||
|
case SDL.Keycode.F15: return ImGuiKey.F15;
|
||||||
|
case SDL.Keycode.F16: return ImGuiKey.F16;
|
||||||
|
case SDL.Keycode.F17: return ImGuiKey.F17;
|
||||||
|
case SDL.Keycode.F18: return ImGuiKey.F18;
|
||||||
|
case SDL.Keycode.F19: return ImGuiKey.F19;
|
||||||
|
case SDL.Keycode.F20: return ImGuiKey.F20;
|
||||||
|
case SDL.Keycode.F21: return ImGuiKey.F21;
|
||||||
|
case SDL.Keycode.F22: return ImGuiKey.F22;
|
||||||
|
case SDL.Keycode.F23: return ImGuiKey.F23;
|
||||||
|
case SDL.Keycode.F24: return ImGuiKey.F24;
|
||||||
|
case SDL.Keycode.AcBack: return ImGuiKey.AppBack;
|
||||||
|
case SDL.Keycode.AcForward: return ImGuiKey.AppForward;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ImGuiKey.None;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IntPtr Data() => ImGui.GetIO().BackendPlatformUserData;
|
||||||
|
|
||||||
|
// I don't think we actually need these since it works just fine without them
|
||||||
|
//
|
||||||
|
// public static IntPtr GetClipboardText(IntPtr ctx)
|
||||||
|
// {
|
||||||
|
// if (clipboardTextPtr != IntPtr.Zero)
|
||||||
|
// {
|
||||||
|
// Marshal.FreeHGlobal(clipboardTextPtr);
|
||||||
|
// clipboardTextPtr = IntPtr.Zero;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// string text = SDL.GetClipboardText();
|
||||||
|
// clipboardTextPtr = Marshal.StringToHGlobalAnsi(text ?? string.Empty);
|
||||||
|
// return clipboardTextPtr;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// public static void SetClipboardText(IntPtr ctx, IntPtr text)
|
||||||
|
// {
|
||||||
|
// string managedText = Marshal.PtrToStringAnsi(text);
|
||||||
|
// if (managedText != null)
|
||||||
|
// {
|
||||||
|
// SDL.SetClipboardText(managedText);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// !! This is sorta fucked
|
||||||
|
// public static void UpdateKeyModifiers(SDL.Keymod keymods)
|
||||||
|
// {
|
||||||
|
// ImGuiIOPtr io = ImGui.GetIO();
|
||||||
|
// io.KeyCtrl = keymods.HasFlag(SDL.Keymod.LCtrl) || keymods.HasFlag(SDL.Keymod.RCtrl);
|
||||||
|
// io.KeyShift = keymods.HasFlag(SDL.Keymod.LShift) || keymods.HasFlag(SDL.Keymod.RShift);
|
||||||
|
// io.KeyAlt = keymods.HasFlag(SDL.Keymod.LAlt) || keymods.HasFlag(SDL.Keymod.RAlt);
|
||||||
|
// io.KeySuper = keymods.HasFlag(SDL.Keymod.LGUI) || keymods.HasFlag(SDL.Keymod.RGUI);
|
||||||
|
// }
|
||||||
|
|
||||||
|
private static void UpdateKeyModifiers(SDL.Keymod mod)
|
||||||
|
{
|
||||||
|
ImGuiIOPtr io = ImGui.GetIO();
|
||||||
|
io.AddKeyEvent(ImGuiKey.ModCtrl, (mod & SDL.Keymod.Ctrl) != 0);
|
||||||
|
io.AddKeyEvent(ImGuiKey.ModShift, (mod & SDL.Keymod.Shift) != 0);
|
||||||
|
io.AddKeyEvent(ImGuiKey.ModAlt, (mod & SDL.Keymod.Alt) != 0);
|
||||||
|
io.AddKeyEvent(ImGuiKey.ModSuper, (mod & SDL.Keymod.GUI) != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ImGuiViewportPtr? GetViewportForWindowId(uint id)
|
||||||
|
{
|
||||||
|
ImGuiViewportPtr viewport = ImGui.GetMainViewport();
|
||||||
|
return viewport.ID == id ? ImGui.GetMainViewport() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void SetupPlatformHandles(ImGuiViewportPtr viewport, IntPtr window)
|
||||||
|
{
|
||||||
|
viewport.PlatformHandle = window;
|
||||||
|
viewport.PlatformHandleRaw = 0;
|
||||||
|
#if _WIN32 && !__WINTR__
|
||||||
|
SDL.GetPointerProperty(SDL.GetWindowProperties(window), SDL.Props.WindowWin32HWNDPointer, 0);
|
||||||
|
#elif __APPLE__ && SDL_VIDEO_DRIVER_COCOA
|
||||||
|
SDL.GetPointerProperty(SDL.GetWindowProperties(window), SDL.Props.WindowCocoaWindow, 0);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,302 @@
|
|||||||
|
using System.Numerics;
|
||||||
|
using ImGuiNET;
|
||||||
|
using SDL3;
|
||||||
|
|
||||||
|
namespace ResoniteImGuiLib.SDL3;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Implementation of ImGui for SDL3 Renderer backend.
|
||||||
|
/// https://github.com/ocornut/imgui/blob/master/backends/imgui_impl_sdlrenderer3.h
|
||||||
|
/// </summary>
|
||||||
|
public class ImGuiSDL3Renderer : IDisposable
|
||||||
|
{
|
||||||
|
struct BackupSDLRendererState
|
||||||
|
{
|
||||||
|
public SDL.Rect Viewport;
|
||||||
|
public bool ViewportEnabled;
|
||||||
|
public bool ClipEnabled;
|
||||||
|
public SDL.Rect ClipRect;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SDL.Rect DefaultClipRect = new SDL.Rect();
|
||||||
|
public readonly IntPtr Renderer;
|
||||||
|
private IntPtr _fontTexture = IntPtr.Zero;
|
||||||
|
|
||||||
|
public ImGuiSDL3Renderer(IntPtr renderer)
|
||||||
|
{
|
||||||
|
Renderer = renderer;
|
||||||
|
|
||||||
|
ImGuiIOPtr io = ImGui.GetIO();
|
||||||
|
io.BackendFlags |= ImGuiBackendFlags.RendererHasVtxOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
ImGuiIOPtr io = ImGui.GetIO();
|
||||||
|
if (_fontTexture != IntPtr.Zero)
|
||||||
|
{
|
||||||
|
io.Fonts.SetTexID(IntPtr.Zero);
|
||||||
|
SDL.DestroyTexture(_fontTexture);
|
||||||
|
_fontTexture = IntPtr.Zero;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void NewFrame()
|
||||||
|
{
|
||||||
|
if (_fontTexture == IntPtr.Zero)
|
||||||
|
{
|
||||||
|
CreateDeviceObjects();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RenderDrawData(ImDrawDataPtr drawData)
|
||||||
|
{
|
||||||
|
// Skip if no data to render
|
||||||
|
unsafe
|
||||||
|
{
|
||||||
|
if (drawData.NativePtr == null || drawData.CmdListsCount == 0)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get display size & framebuffer scale
|
||||||
|
Vector2 renderScale = new Vector2(drawData.FramebufferScale.X, drawData.FramebufferScale.Y);
|
||||||
|
|
||||||
|
int fbWidth = (int)(drawData.DisplaySize.X * renderScale.X);
|
||||||
|
int fbHeight = (int)(drawData.DisplaySize.Y * renderScale.Y);
|
||||||
|
if (fbWidth <= 0 || fbHeight <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Backup SDL renderer state
|
||||||
|
BackupSDLRendererState oldState = BackupRendererState();
|
||||||
|
|
||||||
|
// Set up render state
|
||||||
|
SetupRenderState();
|
||||||
|
|
||||||
|
// Set render state in platform IO
|
||||||
|
ImGuiPlatformIOPtr platformIo = ImGui.GetPlatformIO();
|
||||||
|
platformIo.Renderer_RenderState = Renderer;
|
||||||
|
|
||||||
|
Vector2 clipOffset = drawData.DisplayPos;
|
||||||
|
|
||||||
|
// Render command lists
|
||||||
|
for (int n = 0; n < drawData.CmdListsCount; n++)
|
||||||
|
{
|
||||||
|
ImDrawListPtr cmdList = drawData.CmdLists[n];
|
||||||
|
|
||||||
|
for (int cmdIndex = 0; cmdIndex < cmdList.CmdBuffer.Size; cmdIndex++)
|
||||||
|
{
|
||||||
|
ImDrawCmdPtr cmd = cmdList.CmdBuffer[cmdIndex];
|
||||||
|
|
||||||
|
if (cmd.UserCallback != IntPtr.Zero)
|
||||||
|
{
|
||||||
|
// User callback not implemented
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply clipping rectangle
|
||||||
|
Vector4 clipRect = cmd.ClipRect;
|
||||||
|
SDL.Rect r = CalculateClipRect(clipRect, clipOffset, renderScale, fbWidth, fbHeight);
|
||||||
|
SDL.SetRenderClipRect(Renderer, r);
|
||||||
|
|
||||||
|
// Get texture
|
||||||
|
IntPtr texId = cmd.GetTexID();
|
||||||
|
|
||||||
|
// Convert ImGui vertices to SDL vertices
|
||||||
|
if (!RenderDrawCommand(cmdList, cmd, texId, renderScale))
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Failed to render ImGui draw command: {SDL.GetError()}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset render state
|
||||||
|
platformIo.Renderer_RenderState = IntPtr.Zero;
|
||||||
|
|
||||||
|
// Restore renderer state
|
||||||
|
RestoreRendererState(oldState);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool RenderDrawCommand(ImDrawListPtr drawList, ImDrawCmdPtr cmd, IntPtr texId, Vector2 scale)
|
||||||
|
{
|
||||||
|
// Get indices and vertices for this command
|
||||||
|
int indexOffset = (int)cmd.IdxOffset;
|
||||||
|
int vertexOffset = (int)cmd.VtxOffset;
|
||||||
|
int elemCount = (int)cmd.ElemCount;
|
||||||
|
|
||||||
|
// Create SDL vertices just for the vertices used by this command
|
||||||
|
// Determine the vertex range by looking at the indices
|
||||||
|
int minVertexIdx = int.MaxValue;
|
||||||
|
int maxVertexIdx = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < elemCount; i++)
|
||||||
|
{
|
||||||
|
ushort idx = drawList.IdxBuffer[indexOffset + i];
|
||||||
|
minVertexIdx = Math.Min(minVertexIdx, idx);
|
||||||
|
maxVertexIdx = Math.Max(maxVertexIdx, idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adjust for the vertex offset
|
||||||
|
minVertexIdx += vertexOffset;
|
||||||
|
maxVertexIdx += vertexOffset;
|
||||||
|
|
||||||
|
// Calculate the number of vertices we need
|
||||||
|
int numVertices = maxVertexIdx - minVertexIdx + 1;
|
||||||
|
|
||||||
|
// Create arrays for the vertices and indices we're going to use
|
||||||
|
SDL.Vertex[] vertices = new SDL.Vertex[numVertices];
|
||||||
|
int[] indices = new int[elemCount];
|
||||||
|
|
||||||
|
// Convert only the vertices we need
|
||||||
|
for (int i = 0; i < numVertices; i++)
|
||||||
|
{
|
||||||
|
int vertIdx = minVertexIdx + i;
|
||||||
|
ImDrawVertPtr srcVert = drawList.VtxBuffer[vertIdx];
|
||||||
|
|
||||||
|
// Convert from ImGui ABGR color to SDL RGBA color
|
||||||
|
uint col = srcVert.col;
|
||||||
|
byte r = (byte)(col >> 0 & 0xFF); // Extract R from the least significant byte
|
||||||
|
byte g = (byte)(col >> 8 & 0xFF); // Extract G
|
||||||
|
byte b = (byte)(col >> 16 & 0xFF); // Extract B
|
||||||
|
byte a = (byte)(col >> 24 & 0xFF); // Extract A from the most significant byte
|
||||||
|
|
||||||
|
vertices[i] = new SDL.Vertex
|
||||||
|
{
|
||||||
|
Position = new SDL.FPoint { X = srcVert.pos.X, Y = srcVert.pos.Y },
|
||||||
|
Color = new SDL.FColor { R = r / 255f, G = g / 255f, B = b / 255f, A = a / 255f },
|
||||||
|
TexCoord = new SDL.FPoint { X = srcVert.uv.X, Y = srcVert.uv.Y }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adjust indices to be relative to our new vertex array
|
||||||
|
for (int i = 0; i < elemCount; i++)
|
||||||
|
{
|
||||||
|
ushort originalIdx = drawList.IdxBuffer[indexOffset + i];
|
||||||
|
indices[i] = (ushort)(originalIdx - (minVertexIdx - vertexOffset));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call your SDL.RenderGeometry wrapper with the managed arrays
|
||||||
|
return SDL.RenderGeometry(
|
||||||
|
Renderer,
|
||||||
|
texId,
|
||||||
|
vertices,
|
||||||
|
numVertices,
|
||||||
|
indices,
|
||||||
|
elemCount
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetupRenderState()
|
||||||
|
{
|
||||||
|
SDL.SetRenderViewport(Renderer, 0);
|
||||||
|
SDL.SetRenderClipRect(Renderer, IntPtr.Zero);
|
||||||
|
SDL.SetRenderDrawBlendMode(Renderer, SDL.BlendMode.Blend);
|
||||||
|
}
|
||||||
|
|
||||||
|
private BackupSDLRendererState BackupRendererState()
|
||||||
|
{
|
||||||
|
BackupSDLRendererState state = new BackupSDLRendererState
|
||||||
|
{
|
||||||
|
ViewportEnabled = SDL.RenderViewportSet(Renderer),
|
||||||
|
ClipEnabled = SDL.RenderClipEnabled(Renderer)
|
||||||
|
};
|
||||||
|
SDL.GetRenderViewport(Renderer, out state.Viewport);
|
||||||
|
SDL.GetRenderClipRect(Renderer, out state.ClipRect);
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RestoreRendererState(BackupSDLRendererState state)
|
||||||
|
{
|
||||||
|
if (state.ViewportEnabled)
|
||||||
|
SDL.SetRenderViewport(Renderer, state.Viewport);
|
||||||
|
else
|
||||||
|
SDL.SetRenderViewport(Renderer, IntPtr.Zero);
|
||||||
|
|
||||||
|
if (state.ClipEnabled)
|
||||||
|
SDL.SetRenderClipRect(Renderer, state.ClipRect);
|
||||||
|
else
|
||||||
|
SDL.SetRenderClipRect(Renderer, IntPtr.Zero);
|
||||||
|
}
|
||||||
|
|
||||||
|
private SDL.Rect CalculateClipRect(Vector4 clipRect, Vector2 clipOffset, Vector2 scale, int fbWidth, int fbHeight)
|
||||||
|
{
|
||||||
|
Vector2 clipMin = new Vector2((clipRect.X - clipOffset.X) * scale.X, (clipRect.Y - clipOffset.Y) * scale.Y);
|
||||||
|
Vector2 clipMax = new Vector2((clipRect.Z - clipOffset.X) * scale.X, (clipRect.W - clipOffset.Y) * scale.Y);
|
||||||
|
|
||||||
|
// Clamp to framebuffer bounds
|
||||||
|
clipMin.X = Math.Max(0, clipMin.X);
|
||||||
|
clipMin.Y = Math.Max(0, clipMin.Y);
|
||||||
|
clipMax.X = Math.Min(fbWidth, clipMax.X);
|
||||||
|
clipMax.Y = Math.Min(fbHeight, clipMax.Y);
|
||||||
|
|
||||||
|
SDL.Rect r = new SDL.Rect
|
||||||
|
{
|
||||||
|
X = (int)clipMin.X,
|
||||||
|
Y = (int)clipMin.Y,
|
||||||
|
W = (int)(clipMax.X - clipMin.X),
|
||||||
|
H = (int)(clipMax.Y - clipMin.Y)
|
||||||
|
};
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
private unsafe bool CreateDeviceObjects()
|
||||||
|
{
|
||||||
|
ImGuiIOPtr io = ImGui.GetIO();
|
||||||
|
|
||||||
|
io.Fonts.AddFontDefault();
|
||||||
|
|
||||||
|
// TODO: Load custom fonts from "Fonts" directory
|
||||||
|
//string fontsPath = Path.Combine(Plugin.AssemblyDirectory, "Fonts");
|
||||||
|
//if (Path.Exists(fontsPath))
|
||||||
|
//{
|
||||||
|
// string[] fonts = Directory.GetFiles(fontsPath, "*.ttf");
|
||||||
|
// if (fonts.Length > 0)
|
||||||
|
// {
|
||||||
|
// foreach (string font in fonts)
|
||||||
|
// {
|
||||||
|
// io.Fonts.AddFontFromFileTTF(font, 20f);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
// Build texture atlas
|
||||||
|
io.Fonts.GetTexDataAsRGBA32(out byte* pixels, out int width, out int height);
|
||||||
|
|
||||||
|
// Create surface from pixel data
|
||||||
|
IntPtr surface = SDL.CreateSurfaceFrom(width, height, SDL.PixelFormat.RGBA8888, (IntPtr)pixels, width * 4);
|
||||||
|
if (surface == IntPtr.Zero)
|
||||||
|
{
|
||||||
|
SDL.LogError(SDL.LogCategory.Application, $"Failed to create font surface: {SDL.GetError()}");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create texture
|
||||||
|
_fontTexture = SDL.CreateTextureFromSurface(Renderer, surface);
|
||||||
|
if (_fontTexture == IntPtr.Zero)
|
||||||
|
{
|
||||||
|
SDL.LogError(SDL.LogCategory.Application, $"Failed to create font texture: {SDL.GetError()}");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update texture directly without converting pixel format
|
||||||
|
if (!SDL.UpdateTexture(_fontTexture, IntPtr.Zero, (IntPtr)pixels, width * 4))
|
||||||
|
{
|
||||||
|
SDL.LogError(SDL.LogCategory.Application, $"Failed to update font texture: {SDL.GetError()}");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure proper blending for font rendering
|
||||||
|
SDL.SetTextureBlendMode(_fontTexture, SDL.BlendMode.Blend);
|
||||||
|
|
||||||
|
// Use nearest neighbor filtering for crisp font rendering at small sizes
|
||||||
|
SDL.SetTextureScaleMode(_fontTexture, SDL.ScaleMode.Linear);
|
||||||
|
|
||||||
|
// Store our identifier
|
||||||
|
io.Fonts.SetTexID(_fontTexture);
|
||||||
|
|
||||||
|
SDL.DestroySurface(surface);
|
||||||
|
io.Fonts.ClearTexData();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,238 @@
|
|||||||
|
using Elements.Core;
|
||||||
|
using ImGuiNET;
|
||||||
|
using SDL3;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
namespace ResoniteImGuiLib.SDL3;
|
||||||
|
|
||||||
|
public class SDL3Window : IDisposable
|
||||||
|
{
|
||||||
|
public readonly IntPtr Window;
|
||||||
|
public readonly IntPtr Device;
|
||||||
|
public readonly ImGuiSDL3 Platform;
|
||||||
|
public readonly ImGuiSDL3Renderer Renderer;
|
||||||
|
public readonly uint SdlWindowId;
|
||||||
|
internal readonly Queue<SDL.Event> EventQueue = new();
|
||||||
|
internal Action? RenderCallback { get; set; }
|
||||||
|
internal Func<SDL.Event, bool>? SdlEventReceived { get; set; }
|
||||||
|
public event Action<int4>? WindowRectModified;
|
||||||
|
|
||||||
|
public color? ClearColor = null;
|
||||||
|
|
||||||
|
|
||||||
|
private readonly Stopwatch _timer = Stopwatch.StartNew();
|
||||||
|
private TimeSpan _time = TimeSpan.Zero;
|
||||||
|
|
||||||
|
private SDL.Rect _screenClipRect;
|
||||||
|
private IntPtr _imGuiContext;
|
||||||
|
|
||||||
|
public SDL3Window(string name, int posX, int posY, int width, int height)
|
||||||
|
{
|
||||||
|
if (!SDL.Init(SDL.InitFlags.Video))
|
||||||
|
throw new Exception($"SDL_Init failed: {SDL.GetError()}");
|
||||||
|
|
||||||
|
// Create window & renderer
|
||||||
|
const SDL.WindowFlags windowFlags = SDL.WindowFlags.Resizable;
|
||||||
|
if (!SDL.CreateWindowAndRenderer(name, width, height, windowFlags, out Window, out Device))
|
||||||
|
throw new Exception($"SDL_CreateWindowAndRenderer failed: {SDL.GetError()}");
|
||||||
|
|
||||||
|
SDL.SetWindowTitle(Window, name);
|
||||||
|
SDL.SetWindowSize(Window, width, height);
|
||||||
|
SDL.SetWindowPosition(Window, posX, posY);
|
||||||
|
|
||||||
|
// Enable VSync
|
||||||
|
SDL.SetRenderVSync(Device, 1);
|
||||||
|
|
||||||
|
// Setup screen clip rect
|
||||||
|
SetupScreenClipRect();
|
||||||
|
|
||||||
|
// Create ImGui context
|
||||||
|
_imGuiContext = ImGui.CreateContext();
|
||||||
|
ImGui.SetCurrentContext(_imGuiContext);
|
||||||
|
|
||||||
|
ImGuiIOPtr io = ImGui.GetIO();
|
||||||
|
io.ConfigFlags |= ImGuiConfigFlags.NavEnableKeyboard | ImGuiConfigFlags.DockingEnable;
|
||||||
|
io.Fonts.Flags |= ImFontAtlasFlags.NoBakedLines;
|
||||||
|
|
||||||
|
// Init platform and renderer
|
||||||
|
Platform = new ImGuiSDL3(Window, Device);
|
||||||
|
Renderer = new ImGuiSDL3Renderer(Device);
|
||||||
|
|
||||||
|
SdlWindowId = SDL.GetWindowID(Window);
|
||||||
|
//ImGuiManager.TriggerImGuiRecreated();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Disposed => _disposed;
|
||||||
|
private bool _disposed;
|
||||||
|
|
||||||
|
~SDL3Window()
|
||||||
|
{
|
||||||
|
Dispose(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Dispose(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ForceDispose()
|
||||||
|
{
|
||||||
|
Dispose(true);
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (_disposed) return;
|
||||||
|
_disposed = true;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (Window != IntPtr.Zero)
|
||||||
|
{
|
||||||
|
SDL.GetWindowPosition(Window, out int x, out int y);
|
||||||
|
SDL.GetWindowSize(Window, out int w, out int h);
|
||||||
|
WindowRectModified?.Invoke(new(x, y, w, h));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// Ignore if SDL window is already invalid
|
||||||
|
}
|
||||||
|
|
||||||
|
if (disposing)
|
||||||
|
{
|
||||||
|
Renderer?.Dispose();
|
||||||
|
Platform?.Dispose();
|
||||||
|
|
||||||
|
RenderCallback = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_imGuiContext != IntPtr.Zero)
|
||||||
|
{
|
||||||
|
ImGui.SetCurrentContext(IntPtr.Zero);
|
||||||
|
ImGui.DestroyContext(_imGuiContext);
|
||||||
|
_imGuiContext = IntPtr.Zero;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Device != IntPtr.Zero)
|
||||||
|
{
|
||||||
|
SDL.DestroyRenderer(Device);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Window != IntPtr.Zero)
|
||||||
|
{
|
||||||
|
SDL.DestroyWindow(Window);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetContext()
|
||||||
|
{
|
||||||
|
ImGui.SetCurrentContext(_imGuiContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RunOneFrame()
|
||||||
|
{
|
||||||
|
if (_disposed) return;
|
||||||
|
|
||||||
|
ImGui.GetIO().DeltaTime = (float) (_timer.Elapsed - _time).TotalSeconds;
|
||||||
|
_time = _timer.Elapsed;
|
||||||
|
|
||||||
|
PollEvents();
|
||||||
|
|
||||||
|
Update();
|
||||||
|
|
||||||
|
var clear = ClearColor ?? Plugin.DefaultBackgroundColor.Value;
|
||||||
|
SDL.SetRenderDrawColor(Device, (byte) (clear.R * 255), (byte) (clear.G * 255), (byte) (clear.B * 255), (byte) (clear.A * 255));
|
||||||
|
|
||||||
|
Render();
|
||||||
|
|
||||||
|
ImGui.SetCurrentContext(IntPtr.Zero);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ShouldClose;
|
||||||
|
|
||||||
|
private void PollEvents()
|
||||||
|
{
|
||||||
|
if (ImGui.GetIO().WantTextInput && !SDL.TextInputActive(Window))
|
||||||
|
SDL.StartTextInput(Window);
|
||||||
|
else if (!ImGui.GetIO().WantTextInput && SDL.TextInputActive(Window))
|
||||||
|
SDL.StopTextInput(Window);
|
||||||
|
|
||||||
|
while (EventQueue.TryDequeue(out var ev))
|
||||||
|
{
|
||||||
|
var skip = false;
|
||||||
|
foreach (var func in Delegate.EnumerateInvocationList(SdlEventReceived))
|
||||||
|
{
|
||||||
|
if (!func(ev))
|
||||||
|
{
|
||||||
|
skip = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (skip) continue;
|
||||||
|
|
||||||
|
Platform.ProcessEvent(ev);
|
||||||
|
|
||||||
|
switch ((SDL.EventType) ev.Type)
|
||||||
|
{
|
||||||
|
// TODO: reimplement these events
|
||||||
|
case SDL.EventType.WindowFocusGained:
|
||||||
|
//ImGuiManager.TriggerImGuiFocusChanged(true);
|
||||||
|
break;
|
||||||
|
case SDL.EventType.WindowFocusLost:
|
||||||
|
//ImGuiManager.TriggerImGuiFocusChanged(false);
|
||||||
|
break;
|
||||||
|
case SDL.EventType.Terminating:
|
||||||
|
case SDL.EventType.WindowCloseRequested:
|
||||||
|
case SDL.EventType.Quit:
|
||||||
|
ShouldClose = true;
|
||||||
|
break;
|
||||||
|
case SDL.EventType.WindowResized:
|
||||||
|
SetupScreenClipRect();
|
||||||
|
SDL.GetWindowPosition(Window, out var x, out var y);
|
||||||
|
WindowRectModified?.Invoke(new(x, y, ev.Window.Data1, ev.Window.Data2));
|
||||||
|
break;
|
||||||
|
case SDL.EventType.WindowMoved:
|
||||||
|
SDL.GetWindowSize(Window, out var w, out var h);
|
||||||
|
WindowRectModified?.Invoke(new(ev.Window.Data1, ev.Window.Data2, w, h));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Update()
|
||||||
|
{
|
||||||
|
Platform.NewFrame();
|
||||||
|
Renderer.NewFrame();
|
||||||
|
ImGui.NewFrame();
|
||||||
|
RenderCallback?.Invoke();
|
||||||
|
ImGui.EndFrame();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Render()
|
||||||
|
{
|
||||||
|
SDL.RenderClear(Device);
|
||||||
|
|
||||||
|
// Reset the clip rect to the screen size
|
||||||
|
SDL.SetRenderClipRect(Device, _screenClipRect);
|
||||||
|
|
||||||
|
// Render ImGui
|
||||||
|
ImGui.Render();
|
||||||
|
Renderer.RenderDrawData(ImGui.GetDrawData());
|
||||||
|
|
||||||
|
SDL.RenderPresent(Device);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetupScreenClipRect()
|
||||||
|
{
|
||||||
|
SDL.GetWindowSize(Window, out int w, out int h);
|
||||||
|
_screenClipRect = new SDL.Rect
|
||||||
|
{
|
||||||
|
X = 0,
|
||||||
|
Y = 0,
|
||||||
|
W = w,
|
||||||
|
H = h
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,10 +1,13 @@
|
|||||||
# ResoniteImGuiLib
|
# ResoniteImGuiLib
|
||||||
|
[](https://thunderstore.io/c/resonite/)
|
||||||
|
|
||||||
A [ResoniteModLoader](https://github.com/resonite-modding-group/ResoniteModLoader) mod for [Resonite](https://resonite.com/) that is a library which allows modders to use [ImGuiUnityInject](https://github.com/art0007i/ImGuiUnityInject) in resonite.
|
A [Resonite](https://resonite.com/) library that allows modders to create ImGui windows.
|
||||||
|
|
||||||
## Installation
|
Example usage can be found here: https://github.com/art0007i/ImGuiExample
|
||||||
1. Install [ResoniteModLoader](https://github.com/resonite-modding-group/ResoniteModLoader).
|
|
||||||
1. Place [ResoniteImGuiLib.dll](https://github.com/art0007i/ResoniteImGuiLib/releases/latest/download/ResoniteImGuiLib.dll) into your `rml_mods` folder. This folder should be at `C:\Program Files (x86)\Steam\steamapps\common\Resonite\rml_mods` for a default install. You can create it if it's missing, or if you launch the game once with ResoniteModLoader installed it will create the folder for you.
|
## Installation (Manual)
|
||||||
2. Place [ImGuiUnityInject.dll](https://github.com/art0007i/ImGuiUnityInject/releases/latest/download/ImGuiUnityInject.dll) into your `rml_libs` folder.
|
1. Install [BepisLoader](https://github.com/ResoniteModding/BepisLoader) for Resonite.
|
||||||
3. Place the files from [ImGuiUnityInject/Plugins](https://github.com/art0007i/ImGuiUnityInject/tree/master/Plugins) into your `Resonite_Data/Plugins/x86_64` folder.
|
2. Download the latest release ZIP file (e.g., `art0007i-ResoniteImGuiLib-2.0.0.zip`) from the [Releases](https://github.com/art0007i/ResoniteImGuiLib/releases) page.
|
||||||
1. Start the game. If you want to verify that the mod is working you can check your Resonite logs.
|
3. Extract the ZIP and copy the `plugins` folder to your BepInEx folder in your Resonite installation directory:
|
||||||
|
- **Default location:** `C:\Program Files (x86)\Steam\steamapps\common\Resonite\BepInEx\`
|
||||||
|
4. Start the game. If you want to verify that the mod is working you can check your BepInEx logs.
|
||||||
+21
-8
@@ -1,21 +1,34 @@
|
|||||||
|
|
||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ResoniteImGuiLib", "ResoniteImGuiLib\ResoniteImGuiLib.csproj", "{FFA93FA9-4040-46FF-8A1C-2A190C0CC235}"
|
# Visual Studio Version 17
|
||||||
|
VisualStudioVersion = 17.0.31903.59
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ResoniteImGuiLib", "Plugin\ResoniteImGuiLib.csproj", "{797E46C4-2CE9-40D2-9B24-64E35847ECC6}"
|
||||||
EndProject
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Debug|x64 = Debug|x64
|
||||||
|
Debug|x86 = Debug|x86
|
||||||
Release|Any CPU = Release|Any CPU
|
Release|Any CPU = Release|Any CPU
|
||||||
|
Release|x64 = Release|x64
|
||||||
|
Release|x86 = Release|x86
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
{FFA93FA9-4040-46FF-8A1C-2A190C0CC235}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{797E46C4-2CE9-40D2-9B24-64E35847ECC6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{FFA93FA9-4040-46FF-8A1C-2A190C0CC235}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{797E46C4-2CE9-40D2-9B24-64E35847ECC6}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{FFA93FA9-4040-46FF-8A1C-2A190C0CC235}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{797E46C4-2CE9-40D2-9B24-64E35847ECC6}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
{FFA93FA9-4040-46FF-8A1C-2A190C0CC235}.Release|Any CPU.Build.0 = Release|Any CPU
|
{797E46C4-2CE9-40D2-9B24-64E35847ECC6}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{797E46C4-2CE9-40D2-9B24-64E35847ECC6}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{797E46C4-2CE9-40D2-9B24-64E35847ECC6}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{797E46C4-2CE9-40D2-9B24-64E35847ECC6}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{797E46C4-2CE9-40D2-9B24-64E35847ECC6}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{797E46C4-2CE9-40D2-9B24-64E35847ECC6}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{797E46C4-2CE9-40D2-9B24-64E35847ECC6}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{797E46C4-2CE9-40D2-9B24-64E35847ECC6}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{797E46C4-2CE9-40D2-9B24-64E35847ECC6}.Release|x86.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
|
||||||
SolutionGuid = {FFA93FA9-4040-46FF-8A1C-2A190C0CC235}
|
|
||||||
EndGlobalSection
|
|
||||||
EndGlobal
|
EndGlobal
|
||||||
|
|||||||
@@ -1,35 +0,0 @@
|
|||||||
using System.Reflection;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
// General Information about an assembly is controlled through the following
|
|
||||||
// set of attributes. Change these attribute values to modify the information
|
|
||||||
// associated with an assembly.
|
|
||||||
[assembly: AssemblyTitle("ResoniteImGuiLib")]
|
|
||||||
[assembly: AssemblyDescription("")]
|
|
||||||
[assembly: AssemblyConfiguration("")]
|
|
||||||
[assembly: AssemblyCompany("art0007i")]
|
|
||||||
[assembly: AssemblyProduct("ResoniteImGuiLib")]
|
|
||||||
[assembly: AssemblyCopyright("Copyright © 2023")]
|
|
||||||
[assembly: AssemblyTrademark("")]
|
|
||||||
[assembly: AssemblyCulture("")]
|
|
||||||
|
|
||||||
// Setting ComVisible to false makes the types in this assembly not visible
|
|
||||||
// to COM components. If you need to access a type in this assembly from
|
|
||||||
// COM, set the ComVisible attribute to true on that type.
|
|
||||||
[assembly: ComVisible(false)]
|
|
||||||
|
|
||||||
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
|
||||||
[assembly: Guid("FFA93FA9-4040-46FF-8A1C-2A190C0CC235")]
|
|
||||||
|
|
||||||
// Version information for an assembly consists of the following four values:
|
|
||||||
//
|
|
||||||
// Major Version
|
|
||||||
// Minor Version
|
|
||||||
// Build Number
|
|
||||||
// Revision
|
|
||||||
//
|
|
||||||
// You can specify all the values or you can default the Build and Revision Numbers
|
|
||||||
// by using the '*' as shown below:
|
|
||||||
// [assembly: AssemblyVersion("1.0.*")]
|
|
||||||
[assembly: AssemblyVersion("1.0.0")]
|
|
||||||
[assembly: AssemblyFileVersion("1.0.0")]
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
{
|
|
||||||
"profiles": {
|
|
||||||
"Launch Resonite": {
|
|
||||||
"commandName": "Executable",
|
|
||||||
"executablePath": "$(GamePath)Resonite.exe",
|
|
||||||
"commandLineArgs": "-DataPath \"D:\\YDMS\\Resonite Data\" -CachePath \"D:\\YDMS\\Resonite Cache\" -Screen -Invisible -LoadAssembly \"Libraries/ResoniteModLoader.dll\" -DoNotAutoLoadHome",
|
|
||||||
"workingDirectory": "$(GamePath)"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,95 +0,0 @@
|
|||||||
using HarmonyLib;
|
|
||||||
using ResoniteModLoader;
|
|
||||||
using FrooxEngine;
|
|
||||||
using Elements.Core;
|
|
||||||
using ImGuiNET;
|
|
||||||
using UnityEngine;
|
|
||||||
using ImGuiUnityInject;
|
|
||||||
using UnityEngine.SceneManagement;
|
|
||||||
using System.Linq;
|
|
||||||
using UnityFrooxEngineRunner;
|
|
||||||
|
|
||||||
namespace ResoniteImGuiLib;
|
|
||||||
|
|
||||||
public static class ImGuiLib
|
|
||||||
{
|
|
||||||
public static ImGuiInstance GetOrCreateInstance(ImGuiReady onReady)
|
|
||||||
{
|
|
||||||
return GetOrCreateInstance("global", onReady);
|
|
||||||
}
|
|
||||||
public static ImGuiInstance GetOrCreateInstance(string name = "global", ImGuiReady onReady = null)
|
|
||||||
{
|
|
||||||
return ImGuiInstance.GetOrCreate((gui, isNew) =>
|
|
||||||
{
|
|
||||||
if (isNew) gui._camera = SceneManager.GetActiveScene().GetRootGameObjects().Where(go => go.name == "FrooxEngine").First().GetComponent<FrooxEngineRunner>().OverlayCamera;
|
|
||||||
|
|
||||||
if (onReady != null) onReady(gui, isNew);
|
|
||||||
else gui.enabled = true;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ResoniteImGuiLib : ResoniteMod
|
|
||||||
{
|
|
||||||
public override string Name => "ResoniteImGuiLib";
|
|
||||||
public override string Author => "art0007i";
|
|
||||||
public override string Version => "1.0.0";
|
|
||||||
public override string Link => "https://github.com/art0007i/ResoniteImGuiLib/";
|
|
||||||
public override void OnEngineInit()
|
|
||||||
{
|
|
||||||
Harmony harmony = new Harmony("me.art0007i.ResoniteImGuiLib");
|
|
||||||
harmony.PatchAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
[HarmonyPatch(typeof(MouseDriver), "UpdateMouse")]
|
|
||||||
class CursorUpdatePatch
|
|
||||||
{
|
|
||||||
public static bool Prefix(Mouse mouse)
|
|
||||||
{
|
|
||||||
var io = ImGui.GetIO();
|
|
||||||
if (io.WantCaptureMouse)
|
|
||||||
{
|
|
||||||
mouse.LeftButton.UpdateState(false);
|
|
||||||
mouse.RightButton.UpdateState(false);
|
|
||||||
mouse.MiddleButton.UpdateState(false);
|
|
||||||
mouse.MouseButton4.UpdateState(false);
|
|
||||||
mouse.MouseButton5.UpdateState(false);
|
|
||||||
mouse.DirectDelta.UpdateValue(float2.Zero, Time.deltaTime);
|
|
||||||
mouse.ScrollWheelDelta.UpdateValue(float2.Zero, Time.deltaTime);
|
|
||||||
mouse.NormalizedScrollWheelDelta.UpdateValue(float2.Zero, Time.deltaTime);
|
|
||||||
|
|
||||||
var cursor = ImGui.GetMouseCursor();
|
|
||||||
Cursor.visible = cursor != ImGuiMouseCursor.None;
|
|
||||||
Cursor.lockState = CursorLockMode.None;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[HarmonyPatch(typeof(KeyboardDriver), "Current_onTextInput")]
|
|
||||||
class KeyboardDeltaPatch
|
|
||||||
{
|
|
||||||
public static bool Prefix()
|
|
||||||
{
|
|
||||||
if (ImGui.GetIO().WantCaptureKeyboard)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
[HarmonyPatch(typeof(KeyboardDriver), "GetKeyState")]
|
|
||||||
class KeyboardStatePatch
|
|
||||||
{
|
|
||||||
public static bool Prefix(ref bool __result)
|
|
||||||
{
|
|
||||||
if (ImGui.GetIO().WantCaptureKeyboard)
|
|
||||||
{
|
|
||||||
__result = false;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
|
||||||
<PropertyGroup>
|
|
||||||
<ProjectGuid>{FFA93FA9-4040-46FF-8A1C-2A190C0CC235}</ProjectGuid>
|
|
||||||
<OutputType>Library</OutputType>
|
|
||||||
<AppDesignerFolder>Properties</AppDesignerFolder>
|
|
||||||
<RootNamespace>ResoniteImGuiLib</RootNamespace>
|
|
||||||
<AssemblyTitle>ResoniteImGuiLib</AssemblyTitle>
|
|
||||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
|
||||||
<LangVersion>10</LangVersion>
|
|
||||||
<TargetFramework>net472</TargetFramework>
|
|
||||||
<FileAlignment>512</FileAlignment>
|
|
||||||
<Deterministic>true</Deterministic>
|
|
||||||
<GamePath>$(MSBuildThisFileDirectory)Resonite</GamePath>
|
|
||||||
<GamePath Condition="Exists('C:\Program Files (x86)\Steam\steamapps\common\Resonite\')">C:\Program Files (x86)\Steam\steamapps\common\Resonite\</GamePath>
|
|
||||||
<GamePath Condition="Exists('$(HOME)/.steam/steam/steamapps/common/Resonite/')">$(HOME)/.steam/steam/steamapps/common/Resonite/</GamePath>
|
|
||||||
<GamePath Condition="Exists('E:\Programs\Steam\steamapps\common\Resonite')">E:\Programs\Steam\steamapps\common\Resonite\</GamePath>
|
|
||||||
<CopyLocal>false</CopyLocal>
|
|
||||||
<CopyToMods Condition="'$(CopyToMods)'==''">true</CopyToMods>
|
|
||||||
<DebugSymbols Condition="'$(Configuration)'=='Release'">false</DebugSymbols>
|
|
||||||
<DebugType Condition="'$(Configuration)'=='Release'">None</DebugType>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Reference Include="HarmonyLib">
|
|
||||||
<HintPath>$(GamePath)rml_libs\0Harmony.dll</HintPath>
|
|
||||||
<HintPath Condition="Exists('$(GamePath)0Harmony.dll')">$(GamePath)0Harmony.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="Elements.Core">
|
|
||||||
<HintPath>$(GamePath)Resonite_Data\Managed\Elements.Core.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="FrooxEngine">
|
|
||||||
<HintPath>$(GamePath)Resonite_Data\Managed\FrooxEngine.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="ResoniteModLoader">
|
|
||||||
<HintPath>$(GamePath)ResoniteModLoader.dll</HintPath>
|
|
||||||
<HintPath>$(GamePath)Libraries\ResoniteModLoader.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="UnityFrooxEngineRunner.dll">
|
|
||||||
<HintPath>$(GamePath)Resonite_Data/Managed/UnityFrooxEngineRunner.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="Assembly-CSharp">
|
|
||||||
<HintPath>$(GamePath)Resonite_Data/Managed/Assembly-CSharp.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="ImGuiUnityInject.dll">
|
|
||||||
<HintPath>$(GamePath)rml_libs/ImGuiUnityInject.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
<Reference Include="UnityEngine.CoreModule.dll">
|
|
||||||
<HintPath>$(GamePath)Resonite_Data/Managed/UnityEngine.CoreModule.dll</HintPath>
|
|
||||||
</Reference>
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<Target Name="PostBuild" AfterTargets="PostBuildEvent" Condition="'$(CopyToMods)'=='true'">
|
|
||||||
<Copy SourceFiles="$(TargetPath)" DestinationFolder="$(GamePath)rml_mods" />
|
|
||||||
<Message Text="Copied $(TargetFileName) to $(GamePath)rml_mods" Importance="high" />
|
|
||||||
</Target>
|
|
||||||
</Project>
|
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
# This file is used by the Thunderstore CLI to build and publish your mod to Thunderstore. https://github.com/thunderstore-io/thunderstore-cli
|
||||||
|
|
||||||
|
[config]
|
||||||
|
schemaVersion = "0.0.1"
|
||||||
|
|
||||||
|
[package]
|
||||||
|
namespace = "art0007i"
|
||||||
|
name = "ResoniteImGuiLib"
|
||||||
|
description = "Library that allows modders to create ImGui windows."
|
||||||
|
websiteUrl = "https://github.com/art0007i/ResoniteImGuiLib"
|
||||||
|
containsNsfwContent = false
|
||||||
|
|
||||||
|
[package.dependencies]
|
||||||
|
ResoniteModding-BepisLoader = "1.4.1"
|
||||||
|
ResoniteModding-BepisResoniteWrapper = "1.0.0"
|
||||||
|
|
||||||
|
[build]
|
||||||
|
icon = "./icon.png"
|
||||||
|
readme = "./README.md"
|
||||||
|
outdir = "./build"
|
||||||
|
|
||||||
|
[[build.copy]]
|
||||||
|
source = "./Plugin/bin/Release/ResoniteImGuiLib.dll"
|
||||||
|
target = "plugins/ResoniteImGuiLib/"
|
||||||
|
|
||||||
|
[[build.copy]]
|
||||||
|
source = "./Plugin/bin/Release/ResoniteImGuiLib.pdb"
|
||||||
|
target = "plugins/ResoniteImGuiLib/"
|
||||||
|
|
||||||
|
[[build.copy]]
|
||||||
|
source = "./Plugin/bin/Release/ImGui.NET.dll"
|
||||||
|
target = "plugins/ResoniteImGuiLib/"
|
||||||
|
|
||||||
|
[[build.copy]]
|
||||||
|
source = "./Plugin/bin/Release/runtimes/linux-x64/native/libcimgui.so"
|
||||||
|
target = "plugins/ResoniteImGuiLib/"
|
||||||
|
|
||||||
|
[[build.copy]]
|
||||||
|
source = "./Plugin/bin/Release/runtimes/win-x64/native/cimgui.dll"
|
||||||
|
target = "plugins/ResoniteImGuiLib/"
|
||||||
|
|
||||||
|
[[build.copy]]
|
||||||
|
source = "./CHANGELOG.md"
|
||||||
|
target = "/"
|
||||||
|
|
||||||
|
[[build.copy]]
|
||||||
|
source = "./LICENSE"
|
||||||
|
target = "/"
|
||||||
|
|
||||||
|
[publish]
|
||||||
|
repository = "https://thunderstore.io"
|
||||||
|
communities = ["resonite"]
|
||||||
|
|
||||||
|
[publish.categories]
|
||||||
|
resonite = [ "libraries", "technicaltweaks" ] # TODO: Change this to your mod's categories, see https://thunderstore.io/api/experimental/community/resonite/category/ for a list of available categories
|
||||||
Reference in New Issue
Block a user