diff --git a/src/BizHawk.Client.Common/Api/Classes/GuiApi.cs b/src/BizHawk.Client.Common/Api/Classes/GuiApi.cs
index ce448247e00..241eeebdbf6 100644
--- a/src/BizHawk.Client.Common/Api/Classes/GuiApi.cs
+++ b/src/BizHawk.Client.Common/Api/Classes/GuiApi.cs
@@ -140,9 +140,15 @@ public void DrawNew(string name, bool clear)
public void AddMessage(string message, [LiteralExpected] int? duration = null)
=> _dialogController.AddOnScreenMessage(message, duration);
- public void ClearGraphics(DisplaySurfaceID? surfaceID = null) => Get2DRenderer(surfaceID).Clear();
+ public void ClearGraphics(DisplaySurfaceID? surfaceID = null) => LogCallback("the `ClearGraphics()` function has been deprecated; use `Draw()`\n");
- public void ClearText() => _displayManager.OSD.ClearGuiText();
+ public void ClearText() => LogCallback("the `ClearText()` function has been deprecated; use `Draw()`\n");
+
+ public void Draw() => _displayManager.DoApiRedraw();
+
+ public void AddDrawCallback(Action callback) => _displayManager.OnDraw += callback;
+
+ public void RemoveDrawCallback(Action callback) => _displayManager.OnDraw -= callback;
public void SetDefaultForegroundColor(Color color) => _defaultForeground = color;
diff --git a/src/BizHawk.Client.Common/Api/Interfaces/IGuiApi.cs b/src/BizHawk.Client.Common/Api/Interfaces/IGuiApi.cs
index a2c3311037a..27e336ec012 100644
--- a/src/BizHawk.Client.Common/Api/Interfaces/IGuiApi.cs
+++ b/src/BizHawk.Client.Common/Api/Interfaces/IGuiApi.cs
@@ -33,8 +33,24 @@ public interface IGuiApi : IDisposable, IExternalApi
void AddMessage(string message, int? duration = null);
+ [Obsolete("use Draw and AddDrawCallback instead")]
void ClearGraphics(DisplaySurfaceID? surfaceID = null);
+
+ [Obsolete("use Draw and AddDrawCallback instead")]
void ClearText();
+
+ ///
+ /// Clears all prior drawings and calls any actions given to .
+ /// This should only be used when you want to redraw while paused.
+ ///
+ void Draw();
+
+ ///
+ /// The given action will be called before each API draw. This means after each frame and any time is called.
+ ///
+ void AddDrawCallback(Action callback);
+ void RemoveDrawCallback(Action callback);
+
void SetDefaultForegroundColor(Color color);
void SetDefaultBackgroundColor(Color color);
Color GetDefaultTextBackground();
diff --git a/src/BizHawk.Client.Common/DisplayManager/DisplayManagerBase.cs b/src/BizHawk.Client.Common/DisplayManager/DisplayManagerBase.cs
index 5a30295c011..8e21b3b0bde 100644
--- a/src/BizHawk.Client.Common/DisplayManager/DisplayManagerBase.cs
+++ b/src/BizHawk.Client.Common/DisplayManager/DisplayManagerBase.cs
@@ -51,6 +51,8 @@ public DisplayManagerRenderTargetProvider(Func callback)
public SnowyNullVideo SnowyVP { get; private set; }
+ public Action/*?*/ OnDraw;
+
protected DisplayManagerBase(
Config config,
IEmulator emulator,
@@ -961,12 +963,25 @@ public I2DRenderer GetApiHawk2DRenderer(DisplaySurfaceID surfaceID)
? _apiHawkIDTo2DRenderer.GetValueOrPut(surfaceID, _ => _gl.Create2DRenderer(_imGuiResourceCache))
: throw new ArgumentOutOfRangeException(paramName: nameof(surfaceID), surfaceID, message: "invalid surface ID");
+ ///
+ /// Clear stuff drawn by the gui API
+ ///
public void ClearApiHawkSurfaces()
{
foreach (var renderer in _apiHawkIDTo2DRenderer.Values)
{
renderer.Clear();
}
+ OSD.ClearApiHawkText();
+ }
+
+ ///
+ /// Clear stuff drawn by the gui API and raise the draw event.
+ ///
+ public void DoApiRedraw()
+ {
+ ClearApiHawkSurfaces();
+ OnDraw?.Invoke();
}
public void DiscardApiHawkSurfaces()
diff --git a/src/BizHawk.Client.Common/DisplayManager/OSDManager.cs b/src/BizHawk.Client.Common/DisplayManager/OSDManager.cs
index 1ab1aec2b0c..1a0d5ac6130 100644
--- a/src/BizHawk.Client.Common/DisplayManager/OSDManager.cs
+++ b/src/BizHawk.Client.Common/DisplayManager/OSDManager.cs
@@ -120,7 +120,7 @@ public void AddGuiText(string message, MessagePosition pos, Color backGround, Co
});
}
- public void ClearGuiText()
+ public void ClearApiHawkText()
=> _guiTextList.Clear();
private void DrawMessage(IBlitter g, UIMessage message, int yOffset)
diff --git a/src/BizHawk.Client.Common/lua/NamedLuaFunction.cs b/src/BizHawk.Client.Common/lua/NamedLuaFunction.cs
index bf665b0b826..84d06748dfe 100644
--- a/src/BizHawk.Client.Common/lua/NamedLuaFunction.cs
+++ b/src/BizHawk.Client.Common/lua/NamedLuaFunction.cs
@@ -28,6 +28,8 @@ public sealed class NamedLuaFunction : INamedLuaFunction
public const string EVENT_TYPE_FUTURE = "BeforeFutureFrame";
+ public const string EVENT_TYPE_DRAW = "OnDraw";
+
private readonly LuaFunction _function;
private readonly ILuaLibraries _luaImp;
diff --git a/src/BizHawk.Client.EmuHawk/MainForm.cs b/src/BizHawk.Client.EmuHawk/MainForm.cs
index 01a208b9ccd..f27b26956bb 100644
--- a/src/BizHawk.Client.EmuHawk/MainForm.cs
+++ b/src/BizHawk.Client.EmuHawk/MainForm.cs
@@ -2918,8 +2918,8 @@ private void StepRunLoop_Core(bool force = false)
_lastFastForwardingOrRewinding = isFastForwardingOrRewinding;
- // client input-related duties
- OSD.ClearGuiText();
+ // Clear stuff previously drawn by gui API
+ DisplayManager.ClearApiHawkSurfaces();
CheatList.Pulse();
@@ -3033,6 +3033,7 @@ private void StepRunLoop_Core(bool force = false)
{
UpdateToolsAfter();
}
+ DisplayManager.OnDraw?.Invoke();
if (newFrame)
{
@@ -4080,7 +4081,7 @@ public bool LoadState(string path, string userFriendlyStateName, bool suppressOS
return false;
}
- OSD.ClearGuiText();
+ DisplayManager.ClearApiHawkSurfaces();
if (SavestateLoaded is not null)
{
StateLoadedEventArgs args = new(userFriendlyStateName);
@@ -4095,6 +4096,8 @@ public bool LoadState(string path, string userFriendlyStateName, bool suppressOS
UpdateToolsLoadstate();
InputManager.AutoFireController.ClearStarts();
+ DisplayManager.OnDraw?.Invoke(); // not DoApiRedraw, so scripts drawing in the savestate load event still work
+
//we don't want to analyze how to intermix movies, rewinding, and states
//so purge rewind history when loading a state while doing a movie
if (ToolControllingRewind is null && MovieSession.Movie.IsActive())
diff --git a/src/BizHawk.Client.EmuHawk/tools/Lua/Libraries/GuiLuaLibrary.cs b/src/BizHawk.Client.EmuHawk/tools/Lua/Libraries/GuiLuaLibrary.cs
index 4c06f27caa2..fbcaf734ba5 100644
--- a/src/BizHawk.Client.EmuHawk/tools/Lua/Libraries/GuiLuaLibrary.cs
+++ b/src/BizHawk.Client.EmuHawk/tools/Lua/Libraries/GuiLuaLibrary.cs
@@ -6,10 +6,12 @@
namespace BizHawk.Client.EmuHawk
{
- public sealed class GuiLuaLibrary : LuaLibraryBase, IDisposable
+ public sealed class GuiLuaLibrary : LuaLibraryBase, IDisposable, IRegisterFunctions
{
private DisplaySurfaceID _rememberedSurfaceID = DisplaySurfaceID.EmuCore;
+ public NLFAddCallback CreateAndRegisterNamedFunction { get; set; }
+
public GuiLuaLibrary(ILuaLibraries luaLibsImpl, ApiContainer apiContainer, Action logOutputCallback)
: base(luaLibsImpl, apiContainer, logOutputCallback) {}
@@ -37,15 +39,36 @@ public void DrawFinish()
public void AddMessage(string message)
=> APIs.Gui.AddMessage(message);
- [LuaMethodExample("gui.clearGraphics( );")]
+#pragma warning disable CS0618
+ [LuaDeprecatedMethod]
[LuaMethod("clearGraphics", "clears all lua drawn graphics from the screen")]
public void ClearGraphics(string surfaceName = null)
=> APIs.Gui.ClearGraphics(surfaceID: UseOrFallback(surfaceName));
- [LuaMethodExample("gui.cleartext( );")]
+ [LuaDeprecatedMethod]
[LuaMethod("cleartext", "clears all text created by gui.text()")]
public void ClearText()
=> APIs.Gui.ClearText();
+#pragma warning restore CS0618
+
+ [LuaMethodExample("gui.draw();")]
+ [LuaMethod("draw", "Clears all prior drawings and calls any functions given to gui.addDrawCallback. Use this to re-draw while paused.")]
+ public void Draw()
+ {
+ APIs.Gui.Draw();
+ }
+
+ [LuaMethodExample("local draw_cb_id = gui.addDrawCallback(\r\n\tfunction()\r\n\t\t-- gui.draw*** calls go here\r\n\tend);")]
+ [LuaMethod("addDrawCallback", "The given function will be called before each API draw. This means after each frame and any time gui.draw is called.")]
+ public string AddDrawCallback(LuaFunction luaf, string name = null)
+ {
+ INamedLuaFunction nlf = CreateAndRegisterNamedFunction(luaf, NamedLuaFunction.EVENT_TYPE_DRAW, ApiGroup.PROHIBITED_MID_FRAME, name);
+ Action callback = () => nlf.Call();
+ APIs.Gui.AddDrawCallback(callback);
+ nlf.OnRemove += () => APIs.Gui.RemoveDrawCallback(callback);
+
+ return nlf.GuidStr;
+ }
[LuaMethodExample("gui.defaultForeground( 0x000000FF );")]
[LuaMethod("defaultForeground", "Sets the default foreground color to use in drawing methods, white by default")]
diff --git a/src/BizHawk.Client.EmuHawk/tools/Lua/LuaConsole.cs b/src/BizHawk.Client.EmuHawk/tools/Lua/LuaConsole.cs
index 68c1a6acec6..5004cb9f3a8 100644
--- a/src/BizHawk.Client.EmuHawk/tools/Lua/LuaConsole.cs
+++ b/src/BizHawk.Client.EmuHawk/tools/Lua/LuaConsole.cs
@@ -144,12 +144,12 @@ public LuaConsole()
{
Settings.Columns = LuaListView.AllColumns;
- DisplayManager.ClearApiHawkSurfaces();
+ _openedFiles.StopAllScripts();
+ DisplayManager.DoApiRedraw();
DisplayManager.ClearApiHawkTextureCache();
ResetDrawSurfacePadding();
ClearFileWatches();
LuaImp?.Close();
- DisplayManager.OSD.ClearGuiText();
}
else
{
@@ -1004,9 +1004,8 @@ private void RemoveScriptMenuItem_Click(object sender, EventArgs e)
}
UpdateDialog();
- DisplayManager.ClearApiHawkSurfaces();
+ DisplayManager.DoApiRedraw();
DisplayManager.ClearApiHawkTextureCache();
- DisplayManager.OSD.ClearGuiText();
if (!_openedFiles.Any(static lf => !lf.IsSeparator)) ResetDrawSurfacePadding(); // just removed last script, reset padding
}
}
@@ -1513,7 +1512,8 @@ protected override bool ProcessTabKey(bool forward)
private void EraseToolbarItem_Click(object sender, EventArgs e)
{
- DisplayManager.ClearApiHawkSurfaces();
+ // tooltip says this is specifically for "stale" or "stuck" stuff
+ DisplayManager.DoApiRedraw();
}
// Stupid designer