From 7749d02382d1c9e682cbd28ff3dd3240e5b91227 Mon Sep 17 00:00:00 2001 From: James Groom Date: Mon, 18 Jan 2021 03:16:48 +1000 Subject: [PATCH] Refactor DisplaySurface locking as used by ApiHawk (and Lua) (#2575) * Refactor how the "emu" drawing surface is automatically opened/closed fixes #2571 again `gui.DrawNew("native")` now throws (I will replace this with something better). `gui.DrawNew("emu")` and `gui.DrawFinish()` do nothing but print warning messages, for backwards compatibility. This removes the feature which allowed scripts to draw as soon as they became enabled. It also removes the feature to draw without clearing the surface, though that wasn't working. * Reimplement drawing to "client" surface (see desc.) Changed surface names in APIs to "emucore" and "client" (not in DisplayManager yet because I can't be bothered). Via ApiHawk, `IGuiApi.WithEmuSurface(Action)` has been replaced with `IGuiApi.WithSurface(DrawingSurfaceID, Action)`, the first param being an enum. Via Lua (or ApiHawk), pass an extra string param (`DrawingSurfaceID` enum for ApiHawk) to each `gui.draw*` call. To make it less verbose, omitting the param is treated as using the default "emucore" surface, *unless* the helper `gui.use_surface("client")` had been called, which persists the chosen surface until Lua restarts or it's overwritten. (The same is done when using `WithSurface` in ApiHawk, though it's cleared once `WithSurface` returns.) Along with the new surface names, the old names are still valid in the `surface` params and `gui.use_surface` for now. * Propogate enum to DisplayManager, s/Lua/ApiHawk/ in DisplayManager --- .../Api/Classes/GuiApi.cs | 165 +++++++++++++----- .../Api/DisplaySurfaceID.cs | 33 ++++ .../Api/Interfaces/IGuiApi.cs | 45 +++-- .../DisplayManager/Filters/Gui.cs | 1 + .../DisplayManager/IDisplayManagerForApi.cs | 8 +- .../lua/CommonLibs/GuiLuaLibrary.cs | 63 ++++--- .../DisplayManager/DisplayManager.cs | 96 +++++----- .../tools/Lua/LuaConsole.cs | 37 +--- .../tools/Lua/Win32LuaLibraries.cs | 30 ++-- 9 files changed, 281 insertions(+), 197 deletions(-) create mode 100644 src/BizHawk.Client.Common/Api/DisplaySurfaceID.cs diff --git a/src/BizHawk.Client.Common/Api/Classes/GuiApi.cs b/src/BizHawk.Client.Common/Api/Classes/GuiApi.cs index 2d125d68e60..b6cf385f490 100644 --- a/src/BizHawk.Client.Common/Api/Classes/GuiApi.cs +++ b/src/BizHawk.Client.Common/Api/Classes/GuiApi.cs @@ -39,10 +39,14 @@ public sealed class GuiApi : IGuiApi private Color? _defaultTextBackground = Color.FromArgb(128, 0, 0, 0); + private DisplaySurface _clientSurface; + private DisplaySurface _GUISurface; private (int Left, int Top, int Right, int Bottom) _padding = (0, 0, 0, 0); + private DisplaySurfaceID? _usingSurfaceID = null; + public bool HasGUISurface => _GUISurface != null; public GuiApi(Action logCallback, IDisplayManagerForApi displayManager) @@ -55,9 +59,9 @@ public GuiApi(Action logCallback, IDisplayManagerForApi displayManager) private Pen GetPen(Color color) => _pens.TryGetValue(color, out var p) ? p : (_pens[color] = new Pen(color)); - private Graphics GetGraphics() + private Graphics GetGraphics(DisplaySurfaceID? surfaceID) { - var g = _GUISurface?.GetGraphics() ?? Graphics.FromImage(_nullGraphicsBitmap); + var g = GetRelevantSurface(surfaceID)?.GetGraphics() ?? Graphics.FromImage(_nullGraphicsBitmap); var (tx, ty) = Emulator.ScreenLogicalOffsets(); if (tx != 0 || ty != 0) { @@ -75,12 +79,68 @@ private Graphics GetGraphics() public void SetAttributes(ImageAttributes a) => _attributes = a; - public void DrawNew(string name, bool clear) + private DisplaySurface GetRelevantSurface(DisplaySurfaceID? surfaceID) => (surfaceID ?? _usingSurfaceID) switch + { + DisplaySurfaceID.EmuCore => _GUISurface, + DisplaySurfaceID.Client => _clientSurface, + _ => throw new Exception() + }; + + private void LockSurface(DisplaySurfaceID surfaceID) + { + switch (surfaceID) + { + case DisplaySurfaceID.EmuCore: + if (_GUISurface != null) throw new InvalidOperationException("attempt to lock surface without unlocking previous"); + _GUISurface = _displayManager.LockApiHawkSurface(surfaceID, clear: true); + break; + case DisplaySurfaceID.Client: + if (_clientSurface != null) throw new InvalidOperationException("attempt to lock surface without unlocking previous"); + _clientSurface = _displayManager.LockApiHawkSurface(surfaceID, clear: true); + break; + default: + throw new ArgumentException(message: "not a valid enum member", paramName: nameof(surfaceID)); + } + } + + private void UnlockSurface(DisplaySurfaceID surfaceID) + { + switch (surfaceID) + { + case DisplaySurfaceID.EmuCore: + if (_GUISurface != null) _displayManager.UnlockApiHawkSurface(_GUISurface); + _GUISurface = null; + break; + case DisplaySurfaceID.Client: + if (_clientSurface != null) _displayManager.UnlockApiHawkSurface(_clientSurface); + _clientSurface = null; + break; + default: + throw new ArgumentException(message: "not a valid enum member", paramName: nameof(surfaceID)); + } + } + + public void WithSurface(DisplaySurfaceID surfaceID, Action drawingCallsFunc) + { + LockSurface(surfaceID); + _usingSurfaceID = surfaceID; + try + { + drawingCallsFunc(); + } + finally + { + _usingSurfaceID = null; + UnlockSurface(surfaceID); + } + } + + public void LockEmuSurfaceLua() { try { - DrawFinish(); - _GUISurface = _displayManager.LockLuaSurface(name, clear); + UnlockSurface(DisplaySurfaceID.EmuCore); + LockSurface(DisplaySurfaceID.EmuCore); } catch (InvalidOperationException ex) { @@ -88,12 +148,25 @@ public void DrawNew(string name, bool clear) } } - public void DrawFinish() + public void UnlockEmuSurfaceLua() => UnlockSurface(DisplaySurfaceID.EmuCore); + + public void DrawNew(string name, bool clear) { - if (_GUISurface != null) _displayManager.UnlockLuaSurface(_GUISurface); - _GUISurface = null; + switch (name) + { + case null: + case "emu": + LogCallback("the `DrawNew(\"emu\")` function has been deprecated"); + return; + case "native": + throw new InvalidOperationException("the ability to draw in the margins with `DrawNew(\"native\")` has been removed"); + default: + throw new InvalidOperationException("invalid surface name"); + } } + public void DrawFinish() => LogCallback("the `DrawFinish()` function has been deprecated"); + public void SetPadding(int all) => _padding = (all, all, all, all); public void SetPadding(int x, int y) => _padding = (x / 2, y / 2, x / 2 + x & 1, y / 2 + y & 1); @@ -104,11 +177,7 @@ public void DrawFinish() public void AddMessage(string message) => _displayManager.OSD.AddMessage(message); - public void ClearGraphics() - { - _GUISurface.Clear(); - DrawFinish(); - } + public void ClearGraphics(DisplaySurfaceID? surfaceID = null) => GetRelevantSurface(surfaceID).Clear(); public void ClearText() => _displayManager.OSD.ClearGuiText(); @@ -138,11 +207,11 @@ public void SetDefaultPixelFont(string fontfamily) } } - public void DrawBezier(Point p1, Point p2, Point p3, Point p4, Color? color = null) + public void DrawBezier(Point p1, Point p2, Point p3, Point p4, Color? color = null, DisplaySurfaceID? surfaceID = null) { try { - using var g = GetGraphics(); + using var g = GetGraphics(surfaceID); g.CompositingMode = _compositingMode; g.DrawBezier(GetPen(color ?? _defaultForeground), p1, p2, p3, p4); } @@ -152,11 +221,11 @@ public void DrawBezier(Point p1, Point p2, Point p3, Point p4, Color? color = nu } } - public void DrawBeziers(Point[] points, Color? color = null) + public void DrawBeziers(Point[] points, Color? color = null, DisplaySurfaceID? surfaceID = null) { try { - using var g = GetGraphics(); + using var g = GetGraphics(surfaceID); g.CompositingMode = _compositingMode; g.DrawBeziers(GetPen(color ?? _defaultForeground), points); } @@ -166,7 +235,7 @@ public void DrawBeziers(Point[] points, Color? color = null) } } - public void DrawBox(int x, int y, int x2, int y2, Color? line = null, Color? background = null) + public void DrawBox(int x, int y, int x2, int y2, Color? line = null, Color? background = null, DisplaySurfaceID? surfaceID = null) { try { @@ -192,7 +261,7 @@ public void DrawBox(int x, int y, int x2, int y2, Color? line = null, Color? bac y -= y2; h = Math.Max(y2, 0.1f); } - using var g = GetGraphics(); + using var g = GetGraphics(surfaceID); g.CompositingMode = _compositingMode; g.DrawRectangle(GetPen(line ?? _defaultForeground), x, y, w, h); var bg = background ?? _defaultBackground; @@ -204,11 +273,11 @@ public void DrawBox(int x, int y, int x2, int y2, Color? line = null, Color? bac } } - public void DrawEllipse(int x, int y, int width, int height, Color? line = null, Color? background = null) + public void DrawEllipse(int x, int y, int width, int height, Color? line = null, Color? background = null, DisplaySurfaceID? surfaceID = null) { try { - using var g = GetGraphics(); + using var g = GetGraphics(surfaceID); var bg = background ?? _defaultBackground; if (bg != null) g.FillEllipse(GetBrush(bg.Value), x, y, width, height); g.CompositingMode = _compositingMode; @@ -220,7 +289,7 @@ public void DrawEllipse(int x, int y, int width, int height, Color? line = null, } } - public void DrawIcon(string path, int x, int y, int? width = null, int? height = null) + public void DrawIcon(string path, int x, int y, int? width = null, int? height = null, DisplaySurfaceID? surfaceID = null) { try { @@ -229,7 +298,7 @@ public void DrawIcon(string path, int x, int y, int? width = null, int? height = AddMessage($"File not found: {path}"); return; } - using var g = GetGraphics(); + using var g = GetGraphics(surfaceID); g.CompositingMode = _compositingMode; g.DrawIcon( width != null && height != null @@ -245,9 +314,9 @@ public void DrawIcon(string path, int x, int y, int? width = null, int? height = } } - public void DrawImage(Image img, int x, int y, int? width = null, int? height = null, bool cache = true) + public void DrawImage(Image img, int x, int y, int? width = null, int? height = null, bool cache = true, DisplaySurfaceID? surfaceID = null) { - using var g = GetGraphics(); + using var g = GetGraphics(surfaceID); g.CompositingMode = _compositingMode; g.DrawImage( img, @@ -260,14 +329,14 @@ public void DrawImage(Image img, int x, int y, int? width = null, int? height = _attributes ); } - public void DrawImage(string path, int x, int y, int? width = null, int? height = null, bool cache = true) + public void DrawImage(string path, int x, int y, int? width = null, int? height = null, bool cache = true, DisplaySurfaceID? surfaceID = null) { if (!File.Exists(path)) { LogCallback($"File not found: {path}"); return; } - using var g = GetGraphics(); + using var g = GetGraphics(surfaceID); var isCached = _imageCache.ContainsKey(path); var img = isCached ? _imageCache[path] : Image.FromFile(path); if (!isCached && cache) _imageCache[path] = img; @@ -290,9 +359,9 @@ public void ClearImageCache() _imageCache.Clear(); } - public void DrawImageRegion(Image img, int source_x, int source_y, int source_width, int source_height, int dest_x, int dest_y, int? dest_width = null, int? dest_height = null) + public void DrawImageRegion(Image img, int source_x, int source_y, int source_width, int source_height, int dest_x, int dest_y, int? dest_width = null, int? dest_height = null, DisplaySurfaceID? surfaceID = null) { - using var g = GetGraphics(); + using var g = GetGraphics(surfaceID); g.CompositingMode = _compositingMode; g.DrawImage( img, @@ -306,14 +375,14 @@ public void DrawImageRegion(Image img, int source_x, int source_y, int source_wi ); } - public void DrawImageRegion(string path, int source_x, int source_y, int source_width, int source_height, int dest_x, int dest_y, int? dest_width = null, int? dest_height = null) + public void DrawImageRegion(string path, int source_x, int source_y, int source_width, int source_height, int dest_x, int dest_y, int? dest_width = null, int? dest_height = null, DisplaySurfaceID? surfaceID = null) { if (!File.Exists(path)) { LogCallback($"File not found: {path}"); return; } - using var g = GetGraphics(); + using var g = GetGraphics(surfaceID); g.CompositingMode = _compositingMode; g.DrawImage( _imageCache.TryGetValue(path, out var img) ? img : (_imageCache[path] = Image.FromFile(path)), @@ -327,33 +396,33 @@ public void DrawImageRegion(string path, int source_x, int source_y, int source_ ); } - public void DrawLine(int x1, int y1, int x2, int y2, Color? color = null) + public void DrawLine(int x1, int y1, int x2, int y2, Color? color = null, DisplaySurfaceID? surfaceID = null) { - using var g = GetGraphics(); + using var g = GetGraphics(surfaceID); g.CompositingMode = _compositingMode; g.DrawLine(GetPen(color ?? _defaultForeground), x1, y1, x2, y2); } - public void DrawAxis(int x, int y, int size, Color? color = null) + public void DrawAxis(int x, int y, int size, Color? color = null, DisplaySurfaceID? surfaceID = null) { - DrawLine(x + size, y, x - size, y, color ?? _defaultForeground); - DrawLine(x, y + size, x, y - size, color ?? _defaultForeground); + DrawLine(x + size, y, x - size, y, color ?? _defaultForeground, surfaceID: surfaceID); + DrawLine(x, y + size, x, y - size, color ?? _defaultForeground, surfaceID: surfaceID); } - public void DrawPie(int x, int y, int width, int height, int startangle, int sweepangle, Color? line = null, Color? background = null) + public void DrawPie(int x, int y, int width, int height, int startangle, int sweepangle, Color? line = null, Color? background = null, DisplaySurfaceID? surfaceID = null) { - using var g = GetGraphics(); + using var g = GetGraphics(surfaceID); g.CompositingMode = _compositingMode; var bg = background ?? _defaultBackground; if (bg != null) g.FillPie(GetBrush(bg.Value), x, y, width, height, startangle, sweepangle); g.DrawPie(GetPen(line ?? _defaultForeground), x + 1, y + 1, width - 1, height - 1, startangle, sweepangle); } - public void DrawPixel(int x, int y, Color? color = null) + public void DrawPixel(int x, int y, Color? color = null, DisplaySurfaceID? surfaceID = null) { try { - using var g = GetGraphics(); + using var g = GetGraphics(surfaceID); g.DrawLine(GetPen(color ?? _defaultForeground), x, y, x + 0.1F, y); } catch (Exception) @@ -362,11 +431,11 @@ public void DrawPixel(int x, int y, Color? color = null) } } - public void DrawPolygon(Point[] points, Color? line = null, Color? background = null) + public void DrawPolygon(Point[] points, Color? line = null, Color? background = null, DisplaySurfaceID? surfaceID = null) { try { - using var g = GetGraphics(); + using var g = GetGraphics(surfaceID); g.DrawPolygon(GetPen(line ?? _defaultForeground), points); var bg = background ?? _defaultBackground; if (bg != null) g.FillPolygon(GetBrush(bg.Value), points); @@ -377,9 +446,9 @@ public void DrawPolygon(Point[] points, Color? line = null, Color? background = } } - public void DrawRectangle(int x, int y, int width, int height, Color? line = null, Color? background = null) + public void DrawRectangle(int x, int y, int width, int height, Color? line = null, Color? background = null, DisplaySurfaceID? surfaceID = null) { - using var g = GetGraphics(); + using var g = GetGraphics(surfaceID); var w = Math.Max(width, 0.1F); var h = Math.Max(height, 0.1F); g.DrawRectangle(GetPen(line ?? _defaultForeground), x, y, w, h); @@ -387,7 +456,7 @@ public void DrawRectangle(int x, int y, int width, int height, Color? line = nul if (bg != null) g.FillRectangle(GetBrush(bg.Value), x + 1, y + 1, Math.Max(w - 1, 0), Math.Max(h - 1, 0)); } - public void DrawString(int x, int y, string message, Color? forecolor = null, Color? backcolor = null, int? fontsize = null, string fontfamily = null, string fontstyle = null, string horizalign = null, string vertalign = null) + public void DrawString(int x, int y, string message, Color? forecolor = null, Color? backcolor = null, int? fontsize = null, string fontfamily = null, string fontstyle = null, string horizalign = null, string vertalign = null, DisplaySurfaceID? surfaceID = null) { try { @@ -402,7 +471,7 @@ public void DrawString(int x, int y, string message, Color? forecolor = null, Co _ => FontStyle.Regular }; - using var g = GetGraphics(); + using var g = GetGraphics(surfaceID); // The text isn't written out using GenericTypographic, so measuring it using GenericTypographic seemed to make it worse. // And writing it out with GenericTypographic just made it uglier. :p @@ -455,7 +524,7 @@ public void DrawString(int x, int y, string message, Color? forecolor = null, Co } } - public void DrawText(int x, int y, string message, Color? forecolor = null, Color? backcolor = null, string fontfamily = null) + public void DrawText(int x, int y, string message, Color? forecolor = null, Color? backcolor = null, string fontfamily = null, DisplaySurfaceID? surfaceID = null) { try { @@ -479,7 +548,7 @@ public void DrawText(int x, int y, string message, Color? forecolor = null, Colo index = _defaultPixelFont; break; } - using var g = GetGraphics(); + using var g = GetGraphics(surfaceID); var font = new Font(_displayManager.CustomFonts.Families[index], 8, FontStyle.Regular, GraphicsUnit.Pixel); var sizeOfText = g.MeasureString( message, diff --git a/src/BizHawk.Client.Common/Api/DisplaySurfaceID.cs b/src/BizHawk.Client.Common/Api/DisplaySurfaceID.cs new file mode 100644 index 00000000000..a4e641a5a8d --- /dev/null +++ b/src/BizHawk.Client.Common/Api/DisplaySurfaceID.cs @@ -0,0 +1,33 @@ +#nullable enable + +using System; + +namespace BizHawk.Client.Common +{ + public enum DisplaySurfaceID : int + { + EmuCore = 0, + Client = 1, + } + + /// should probably centralise these enum extensions and not-extensions somewhere... --yoshi + public static class DisplaySurfaceIDParser + { + public static DisplaySurfaceID? Parse(string? str) => str?.ToLowerInvariant() switch + { + null => null, // this makes it easy to cascade the "remembered" value + "client" => DisplaySurfaceID.Client, + "emu" => DisplaySurfaceID.EmuCore, + "emucore" => DisplaySurfaceID.EmuCore, + "native" => DisplaySurfaceID.Client, + _ => throw new ArgumentException(message: $"{str} is not the name of a display surface", paramName: nameof(str)) + }; + + public static string GetName(this DisplaySurfaceID surfaceID) => surfaceID switch + { + DisplaySurfaceID.EmuCore => "emucore", + DisplaySurfaceID.Client => "client", + _ => throw new ArgumentException(message: "not a valid enum member", paramName: nameof(surfaceID)) + }; + } +} diff --git a/src/BizHawk.Client.Common/Api/Interfaces/IGuiApi.cs b/src/BizHawk.Client.Common/Api/Interfaces/IGuiApi.cs index d268e39a19d..563eb2de8e7 100644 --- a/src/BizHawk.Client.Common/Api/Interfaces/IGuiApi.cs +++ b/src/BizHawk.Client.Common/Api/Interfaces/IGuiApi.cs @@ -9,8 +9,16 @@ public interface IGuiApi : IDisposable, IExternalApi void ToggleCompositingMode(); ImageAttributes GetAttributes(); void SetAttributes(ImageAttributes a); + + void WithSurface(DisplaySurfaceID surfaceID, Action drawingCallsFunc); + + [Obsolete] void DrawNew(string name, bool clear = true); + + [Obsolete] void DrawFinish(); + + [Obsolete] bool HasGUISurface { get; } void SetPadding(int all); @@ -19,32 +27,31 @@ public interface IGuiApi : IDisposable, IExternalApi (int Left, int Top, int Right, int Bottom) GetPadding(); void AddMessage(string message); - void ClearGraphics(); + void ClearGraphics(DisplaySurfaceID? surfaceID = null); void ClearText(); void SetDefaultForegroundColor(Color color); void SetDefaultBackgroundColor(Color color); Color? GetDefaultTextBackground(); void SetDefaultTextBackground(Color color); void SetDefaultPixelFont(string fontfamily); - void DrawBezier(Point p1, Point p2, Point p3, Point p4, Color? color = null); - void DrawBeziers(Point[] points, Color? color = null); - void DrawBox(int x, int y, int x2, int y2, Color? line = null, Color? background = null); - void DrawEllipse(int x, int y, int width, int height, Color? line = null, Color? background = null); - void DrawIcon(string path, int x, int y, int? width = null, int? height = null); - void DrawImage(Image img, int x, int y, int? width = null, int? height = null, bool cache = true); - void DrawImage(string path, int x, int y, int? width = null, int? height = null, bool cache = true); + void DrawBezier(Point p1, Point p2, Point p3, Point p4, Color? color = null, DisplaySurfaceID? surfaceID = null); + void DrawBeziers(Point[] points, Color? color = null, DisplaySurfaceID? surfaceID = null); + void DrawBox(int x, int y, int x2, int y2, Color? line = null, Color? background = null, DisplaySurfaceID? surfaceID = null); + void DrawEllipse(int x, int y, int width, int height, Color? line = null, Color? background = null, DisplaySurfaceID? surfaceID = null); + void DrawIcon(string path, int x, int y, int? width = null, int? height = null, DisplaySurfaceID? surfaceID = null); + void DrawImage(Image img, int x, int y, int? width = null, int? height = null, bool cache = true, DisplaySurfaceID? surfaceID = null); + void DrawImage(string path, int x, int y, int? width = null, int? height = null, bool cache = true, DisplaySurfaceID? surfaceID = null); void ClearImageCache(); - void DrawImageRegion(Image img, int source_x, int source_y, int source_width, int source_height, int dest_x, int dest_y, int? dest_width = null, int? dest_height = null); - void DrawImageRegion(string path, int source_x, int source_y, int source_width, int source_height, int dest_x, int dest_y, int? dest_width = null, int? dest_height = null); - void DrawLine(int x1, int y1, int x2, int y2, Color? color = null); - void DrawAxis(int x, int y, int size, Color? color = null); - void DrawPie(int x, int y, int width, int height, int startangle, int sweepangle, Color? line = null, Color? background = null); - void DrawPixel(int x, int y, Color? color = null); - void DrawPolygon(Point[] points, Color? line = null, Color? background = null); - void DrawRectangle(int x, int y, int width, int height, Color? line = null, Color? background = null); - void DrawString(int x, int y, string message, Color? forecolor = null, Color? backcolor = null, int? fontsize = null, - string fontfamily = null, string fontstyle = null, string horizalign = null, string vertalign = null); - void DrawText(int x, int y, string message, Color? forecolor = null, Color? backcolor = null, string fontfamily = null); + void DrawImageRegion(Image img, int source_x, int source_y, int source_width, int source_height, int dest_x, int dest_y, int? dest_width = null, int? dest_height = null, DisplaySurfaceID? surfaceID = null); + void DrawImageRegion(string path, int source_x, int source_y, int source_width, int source_height, int dest_x, int dest_y, int? dest_width = null, int? dest_height = null, DisplaySurfaceID? surfaceID = null); + void DrawLine(int x1, int y1, int x2, int y2, Color? color = null, DisplaySurfaceID? surfaceID = null); + void DrawAxis(int x, int y, int size, Color? color = null, DisplaySurfaceID? surfaceID = null); + void DrawPie(int x, int y, int width, int height, int startangle, int sweepangle, Color? line = null, Color? background = null, DisplaySurfaceID? surfaceID = null); + void DrawPixel(int x, int y, Color? color = null, DisplaySurfaceID? surfaceID = null); + void DrawPolygon(Point[] points, Color? line = null, Color? background = null, DisplaySurfaceID? surfaceID = null); + void DrawRectangle(int x, int y, int width, int height, Color? line = null, Color? background = null, DisplaySurfaceID? surfaceID = null); + void DrawString(int x, int y, string message, Color? forecolor = null, Color? backcolor = null, int? fontsize = null, string fontfamily = null, string fontstyle = null, string horizalign = null, string vertalign = null, DisplaySurfaceID? surfaceID = null); + void DrawText(int x, int y, string message, Color? forecolor = null, Color? backcolor = null, string fontfamily = null, DisplaySurfaceID? surfaceID = null); void Text(int x, int y, string message, Color? forecolor = null, string anchor = null); } } \ No newline at end of file diff --git a/src/BizHawk.Client.Common/DisplayManager/Filters/Gui.cs b/src/BizHawk.Client.Common/DisplayManager/Filters/Gui.cs index c96f8719743..ce9c992dbdc 100644 --- a/src/BizHawk.Client.Common/DisplayManager/Filters/Gui.cs +++ b/src/BizHawk.Client.Common/DisplayManager/Filters/Gui.cs @@ -675,6 +675,7 @@ public override void Run() } } + /// More accurately, ApiHawkLayer, since the gui Lua library is delegated. public class LuaLayer : BaseFilter { public override void Initialize() diff --git a/src/BizHawk.Client.Common/DisplayManager/IDisplayManagerForApi.cs b/src/BizHawk.Client.Common/DisplayManager/IDisplayManagerForApi.cs index fb0f8a7ddaf..7334dcafc5c 100644 --- a/src/BizHawk.Client.Common/DisplayManager/IDisplayManagerForApi.cs +++ b/src/BizHawk.Client.Common/DisplayManager/IDisplayManagerForApi.cs @@ -9,12 +9,12 @@ public interface IDisplayManagerForApi OSDManager OSD { get; } - /// locks the lua surface called + /// locks the surface with ID /// already locked, or unknown surface - DisplaySurface LockLuaSurface(string name, bool clear = true); + DisplaySurface LockApiHawkSurface(DisplaySurfaceID surfaceID, bool clear = true); - /// unlocks this DisplaySurface which had better have been locked as a lua surface + /// unlocks the given , which must be a locked surface produced by /// already unlocked - void UnlockLuaSurface(DisplaySurface surface); + void UnlockApiHawkSurface(DisplaySurface surface); } } diff --git a/src/BizHawk.Client.Common/lua/CommonLibs/GuiLuaLibrary.cs b/src/BizHawk.Client.Common/lua/CommonLibs/GuiLuaLibrary.cs index 7dfd78bc174..3dd811dee74 100644 --- a/src/BizHawk.Client.Common/lua/CommonLibs/GuiLuaLibrary.cs +++ b/src/BizHawk.Client.Common/lua/CommonLibs/GuiLuaLibrary.cs @@ -8,6 +8,8 @@ namespace BizHawk.Client.Common { public sealed class GuiLuaLibrary : LuaLibraryBase, IDisposable { + private DisplaySurfaceID _rememberedSurfaceID = DisplaySurfaceID.EmuCore; + public Func CreateLuaCanvasCallback { get; set; } public GuiLuaLibrary(IPlatformLuaLibEnv luaLibsImpl, ApiContainer apiContainer, Action logOutputCallback) @@ -15,17 +17,17 @@ public GuiLuaLibrary(IPlatformLuaLibEnv luaLibsImpl, ApiContainer apiContainer, public override string Name => "gui"; - public bool HasLuaSurface => APIs.Gui.HasGUISurface; - - public bool SurfaceIsNull => !APIs.Gui.HasGUISurface; + private DisplaySurfaceID UseOrFallback(string surfaceName) => DisplaySurfaceIDParser.Parse(surfaceName) ?? _rememberedSurfaceID; - [LuaMethodExample("gui.DrawNew( \"native\", false );")] +#pragma warning disable CS0612 + [LuaDeprecatedMethod] [LuaMethod("DrawNew", "Changes drawing target to the specified lua surface name. This may clobber any previous drawing to this surface (pass false if you don't want it to)")] public void DrawNew(string name, bool? clear = true) => APIs.Gui.DrawNew(name, clear ?? true); - [LuaMethodExample("gui.DrawFinish( );")] + [LuaDeprecatedMethod] [LuaMethod("DrawFinish", "Finishes drawing to the current lua surface and causes it to get displayed.")] public void DrawFinish() => APIs.Gui.DrawFinish(); +#pragma warning restore CS0612 [LuaMethodExample("gui.addmessage( \"Some message\" );")] [LuaMethod("addmessage", "Adds a message to the OSD's message area")] @@ -33,7 +35,7 @@ public GuiLuaLibrary(IPlatformLuaLibEnv luaLibsImpl, ApiContainer apiContainer, [LuaMethodExample("gui.clearGraphics( );")] [LuaMethod("clearGraphics", "clears all lua drawn graphics from the screen")] - public void ClearGraphics() => APIs.Gui.ClearGraphics(); + public void ClearGraphics(string surfaceName = null) => APIs.Gui.ClearGraphics(surfaceID: UseOrFallback(surfaceName)); [LuaMethodExample("gui.cleartext( );")] [LuaMethod("cleartext", "clears all text created by gui.text()")] @@ -57,7 +59,7 @@ public GuiLuaLibrary(IPlatformLuaLibEnv luaLibsImpl, ApiContainer apiContainer, [LuaMethodExample("gui.drawBezier( { { 5, 10 }, { 10, 10 }, { 10, 20 }, { 5, 20 } }, 0x000000FF );")] [LuaMethod("drawBezier", "Draws a Bezier curve using the table of coordinates provided in the given color")] - public void DrawBezier(LuaTable points, Color color) + public void DrawBezier(LuaTable points, Color color, string surfaceName = null) { try { @@ -73,7 +75,7 @@ public void DrawBezier(LuaTable points, Color color) break; } } - APIs.Gui.DrawBezier(pointsArr[0], pointsArr[1], pointsArr[2], pointsArr[3], color); + APIs.Gui.DrawBezier(pointsArr[0], pointsArr[1], pointsArr[2], pointsArr[3], color, surfaceID: UseOrFallback(surfaceName)); } catch (Exception) { @@ -83,19 +85,19 @@ public void DrawBezier(LuaTable points, Color color) [LuaMethodExample("gui.drawBox( 16, 32, 162, 322, 0x007F00FF, 0x7F7F7FFF );")] [LuaMethod("drawBox", "Draws a rectangle on screen from x1/y1 to x2/y2. Same as drawRectangle except it receives two points intead of a point and width/height")] - public void DrawBox(int x, int y, int x2, int y2, Color? line = null, Color? background = null) => APIs.Gui.DrawBox(x, y, x2, y2, line, background); + public void DrawBox(int x, int y, int x2, int y2, Color? line = null, Color? background = null, string surfaceName = null) => APIs.Gui.DrawBox(x, y, x2, y2, line, background, surfaceID: UseOrFallback(surfaceName)); [LuaMethodExample("gui.drawEllipse( 16, 32, 77, 99, 0x007F00FF, 0x7F7F7FFF );")] [LuaMethod("drawEllipse", "Draws an ellipse at the given coordinates and the given width and height. Line is the color of the ellipse. Background is the optional fill color")] - public void DrawEllipse(int x, int y, int width, int height, Color? line = null, Color? background = null) => APIs.Gui.DrawEllipse(x, y, width, height, line, background); + public void DrawEllipse(int x, int y, int width, int height, Color? line = null, Color? background = null, string surfaceName = null) => APIs.Gui.DrawEllipse(x, y, width, height, line, background, surfaceID: UseOrFallback(surfaceName)); [LuaMethodExample("gui.drawIcon( \"C:\\sample.ico\", 16, 32, 18, 24 );")] [LuaMethod("drawIcon", "draws an Icon (.ico) file from the given path at the given coordinate. width and height are optional. If specified, it will resize the image accordingly")] - public void DrawIcon(string path, int x, int y, int? width = null, int? height = null) => APIs.Gui.DrawIcon(path, x, y, width, height); + public void DrawIcon(string path, int x, int y, int? width = null, int? height = null, string surfaceName = null) => APIs.Gui.DrawIcon(path, x, y, width, height, surfaceID: UseOrFallback(surfaceName)); [LuaMethodExample("gui.drawImage( \"C:\\sample.bmp\", 16, 32, 18, 24, false );")] [LuaMethod("drawImage", "draws an image file from the given path at the given coordinate. width and height are optional. If specified, it will resize the image accordingly")] - public void DrawImage(string path, int x, int y, int? width = null, int? height = null, bool cache = true) => APIs.Gui.DrawImage(path, x, y, width, height, cache); + public void DrawImage(string path, int x, int y, int? width = null, int? height = null, bool cache = true, string surfaceName = null) => APIs.Gui.DrawImage(path, x, y, width, height, cache, surfaceID: UseOrFallback(surfaceName)); [LuaMethodExample("gui.clearImageCache( );")] [LuaMethod("clearImageCache", "clears the image cache that is built up by using gui.drawImage, also releases the file handle for cached images")] @@ -103,27 +105,27 @@ public void DrawBezier(LuaTable points, Color color) [LuaMethodExample("gui.drawImageRegion( \"C:\\sample.png\", 11, 22, 33, 44, 21, 43, 34, 45 );")] [LuaMethod("drawImageRegion", "draws a given region of an image file from the given path at the given coordinate, and optionally with the given size")] - public void DrawImageRegion(string path, int source_x, int source_y, int source_width, int source_height, int dest_x, int dest_y, int? dest_width = null, int? dest_height = null) => APIs.Gui.DrawImageRegion(path, source_x, source_y, source_width, source_height, dest_x, dest_y, dest_width, dest_height); + public void DrawImageRegion(string path, int source_x, int source_y, int source_width, int source_height, int dest_x, int dest_y, int? dest_width = null, int? dest_height = null, string surfaceName = null) => APIs.Gui.DrawImageRegion(path, source_x, source_y, source_width, source_height, dest_x, dest_y, dest_width, dest_height, surfaceID: UseOrFallback(surfaceName)); [LuaMethodExample("gui.drawLine( 161, 321, 162, 322, 0xFFFFFFFF );")] [LuaMethod("drawLine", "Draws a line from the first coordinate pair to the 2nd. Color is optional (if not specified it will be drawn black)")] - public void DrawLine(int x1, int y1, int x2, int y2, Color? color = null) => APIs.Gui.DrawLine(x1, y1, x2, y2, color); + public void DrawLine(int x1, int y1, int x2, int y2, Color? color = null, string surfaceName = null) => APIs.Gui.DrawLine(x1, y1, x2, y2, color, surfaceID: UseOrFallback(surfaceName)); [LuaMethodExample("gui.drawAxis( 16, 32, 15, 0xFFFFFFFF );")] [LuaMethod("drawAxis", "Draws an axis of the specified size at the coordinate pair.)")] - public void DrawAxis(int x, int y, int size, Color? color = null) => APIs.Gui.DrawAxis(x, y, size, color); + public void DrawAxis(int x, int y, int size, Color? color = null, string surfaceName = null) => APIs.Gui.DrawAxis(x, y, size, color, surfaceID: UseOrFallback(surfaceName)); [LuaMethodExample("gui.drawPie( 16, 32, 77, 99, 180, 90, 0x007F00FF, 0x7F7F7FFF );")] [LuaMethod("drawPie", "draws a Pie shape at the given coordinates and the given width and height")] - public void DrawPie(int x, int y, int width, int height, int startangle, int sweepangle, Color? line = null, Color? background = null) => APIs.Gui.DrawPie(x, y, width, height, startangle, sweepangle, line, background); + public void DrawPie(int x, int y, int width, int height, int startangle, int sweepangle, Color? line = null, Color? background = null, string surfaceName = null) => APIs.Gui.DrawPie(x, y, width, height, startangle, sweepangle, line, background, surfaceID: UseOrFallback(surfaceName)); [LuaMethodExample("gui.drawPixel( 16, 32, 0xFFFFFFFF );")] [LuaMethod("drawPixel", "Draws a single pixel at the given coordinates in the given color. Color is optional (if not specified it will be drawn black)")] - public void DrawPixel(int x, int y, Color? color = null) => APIs.Gui.DrawPixel(x, y, color); + public void DrawPixel(int x, int y, Color? color = null, string surfaceName = null) => APIs.Gui.DrawPixel(x, y, color, surfaceID: UseOrFallback(surfaceName)); [LuaMethodExample("gui.drawPolygon( { { 5, 10 }, { 10, 10 }, { 10, 20 }, { 5, 20 } }, 10, 30, 0x007F00FF, 0x7F7F7FFF );")] [LuaMethod("drawPolygon", "Draws a polygon using the table of coordinates specified in points. This should be a table of tables(each of size 2). If x or y is passed, the polygon will be translated by the passed coordinate pair. Line is the color of the polygon. Background is the optional fill color")] - public void DrawPolygon(LuaTable points, int? offsetX = null, int? offsetY = null, Color? line = null, Color? background = null) + public void DrawPolygon(LuaTable points, int? offsetX = null, int? offsetY = null, Color? line = null, Color? background = null, string surfaceName = null) { var pointsList = _th.EnumerateValues(points) .Select(table => _th.EnumerateValues(table).ToList()).ToList(); @@ -136,7 +138,7 @@ public void DrawPolygon(LuaTable points, int? offsetX = null, int? offsetY = nul pointsArr[i] = new Point(LuaInt(point[0]) + (offsetX ?? 0), LuaInt(point[1]) + (offsetY ?? 0)); i++; } - APIs.Gui.DrawPolygon(pointsArr, line, background); + APIs.Gui.DrawPolygon(pointsArr, line, background, surfaceID: UseOrFallback(surfaceName)); } catch (Exception) { @@ -146,7 +148,7 @@ public void DrawPolygon(LuaTable points, int? offsetX = null, int? offsetY = nul [LuaMethodExample("gui.drawRectangle( 16, 32, 77, 99, 0x007F00FF, 0x7F7F7FFF );")] [LuaMethod("drawRectangle", "Draws a rectangle at the given coordinate and the given width and height. Line is the color of the box. Background is the optional fill color")] - public void DrawRectangle(int x, int y, int width, int height, Color? line = null, Color? background = null) => APIs.Gui.DrawRectangle(x, y, width, height, line, background); + public void DrawRectangle(int x, int y, int width, int height, Color? line = null, Color? background = null, string surfaceName = null) => APIs.Gui.DrawRectangle(x, y, width, height, line, background, surfaceID: UseOrFallback(surfaceName)); [LuaMethodExample("gui.drawString( 16, 32, \"Some message\", 0x7F0000FF, 0x00007FFF, 8, \"Arial Narrow\", \"bold\", \"center\", \"middle\" );")] [LuaMethod("drawString", "Alias of gui.drawText()")] @@ -160,18 +162,19 @@ public void DrawString( string fontfamily = null, string fontstyle = null, string horizalign = null, - string vertalign = null) + string vertalign = null, + string surfaceName = null) { - DrawText(x, y, message, forecolor, backcolor, fontsize, fontfamily, fontstyle, horizalign, vertalign); + DrawText(x, y, message, forecolor, backcolor, fontsize, fontfamily, fontstyle, horizalign, vertalign, surfaceName: surfaceName); } [LuaMethodExample("gui.drawText( 16, 32, \"Some message\", 0x7F0000FF, 0x00007FFF, 8, \"Arial Narrow\", \"bold\", \"center\", \"middle\" );")] [LuaMethod("drawText", "Draws the given message in the emulator screen space (like all draw functions) at the given x,y coordinates and the given color. The default color is white. A fontfamily can be specified and is monospace generic if none is specified (font family options are the same as the .NET FontFamily class). The fontsize default is 12. The default font style is regular. Font style options are regular, bold, italic, strikethrough, underline. Horizontal alignment options are left (default), center, or right. Vertical alignment options are bottom (default), middle, or top. Alignment options specify which ends of the text will be drawn at the x and y coordinates. For pixel-perfect font look, make sure to disable aspect ratio correction.")] - public void DrawText(int x, int y, string message, Color? forecolor = null, Color? backcolor = null, int? fontsize = null, string fontfamily = null, string fontstyle = null, string horizalign = null, string vertalign = null) => APIs.Gui.DrawString(x, y, message, forecolor, backcolor, fontsize, fontfamily, fontstyle, horizalign, vertalign); + public void DrawText(int x, int y, string message, Color? forecolor = null, Color? backcolor = null, int? fontsize = null, string fontfamily = null, string fontstyle = null, string horizalign = null, string vertalign = null, string surfaceName = null) => APIs.Gui.DrawString(x, y, message, forecolor, backcolor, fontsize, fontfamily, fontstyle, horizalign, vertalign, surfaceID: UseOrFallback(surfaceName)); [LuaMethodExample("gui.pixelText( 16, 32, \"Some message\", 0x7F0000FF, 0x00007FFF, \"Arial Narrow\" );")] [LuaMethod("pixelText", "Draws the given message in the emulator screen space (like all draw functions) at the given x,y coordinates and the given color. The default color is white. Two font families are available, \"fceux\" and \"gens\" (or \"0\" and \"1\" respectively), both are monospace and have the same size as in the emulators they've been taken from. If no font family is specified, it uses \"gens\" font, unless that's overridden via gui.defaultPixelFont()")] - public void DrawText(int x, int y, string message, Color? forecolor = null, Color? backcolor = null, string fontfamily = null) => APIs.Gui.DrawText(x, y, message, forecolor, backcolor ?? APIs.Gui.GetDefaultTextBackground().Value, fontfamily); + public void DrawText(int x, int y, string message, Color? forecolor = null, Color? backcolor = null, string fontfamily = null, string surfaceName = null) => APIs.Gui.DrawText(x, y, message, forecolor, backcolor ?? APIs.Gui.GetDefaultTextBackground().Value, fontfamily, surfaceID: UseOrFallback(surfaceName)); [LuaMethodExample("gui.text( 16, 32, \"Some message\", 0x7F0000FF, \"bottomleft\" );")] [LuaMethod("text", "Displays the given text on the screen at the given coordinates. Optional Foreground color. The optional anchor flag anchors the text to one of the four corners. Anchor flag parameters: topleft, topright, bottomleft, bottomright")] @@ -181,6 +184,18 @@ public void DrawString( [LuaMethod("createcanvas", "Creates a canvas of the given size and, if specified, the given coordinates.")] public LuaTable Text(int width, int height, int? x = null, int? y = null) => CreateLuaCanvasCallback(width, height, x, y); + [LuaMethodExample("gui.use_surface( \"client\" );")] + [LuaMethod("use_surface", "Stores the name of a surface to draw on, so you don't need to pass it to every draw function. The default is \"emucore\", and the other valid value is \"client\".")] + public void UseSurface(string surfaceName) + { + if (surfaceName == null) + { + Log("Surface name cannot be nil. Pass \"emucore\" to `gui.use_surface` to restore the default."); + return; + } + _rememberedSurfaceID = DisplaySurfaceIDParser.Parse(surfaceName).Value; // iff param is not null, returns not null or throws + } + public void Dispose() => APIs.Gui.Dispose(); } } diff --git a/src/BizHawk.Client.EmuHawk/DisplayManager/DisplayManager.cs b/src/BizHawk.Client.EmuHawk/DisplayManager/DisplayManager.cs index 8c05d5829ab..296fa871719 100644 --- a/src/BizHawk.Client.EmuHawk/DisplayManager/DisplayManager.cs +++ b/src/BizHawk.Client.EmuHawk/DisplayManager/DisplayManager.cs @@ -130,10 +130,10 @@ public DisplayManager(Config config, IEmulator emulator, InputManager inputManag } } - _luaSurfaceSets["emu"] = new SwappableDisplaySurfaceSet(); - _luaSurfaceSets["native"] = new SwappableDisplaySurfaceSet(); - _luaSurfaceFrugalizers["emu"] = new TextureFrugalizer(_gl); - _luaSurfaceFrugalizers["native"] = new TextureFrugalizer(_gl); + _apiHawkSurfaceSets[DisplaySurfaceID.EmuCore] = new SwappableDisplaySurfaceSet(); + _apiHawkSurfaceSets[DisplaySurfaceID.Client] = new SwappableDisplaySurfaceSet(); + _apiHawkSurfaceFrugalizers[DisplaySurfaceID.EmuCore] = new TextureFrugalizer(_gl); + _apiHawkSurfaceFrugalizers[DisplaySurfaceID.Client] = new TextureFrugalizer(_gl); RefreshUserShader(); } @@ -152,7 +152,7 @@ public void Dispose() if (Disposed) return; Disposed = true; _videoTextureFrugalizer.Dispose(); - foreach (var f in _luaSurfaceFrugalizers.Values) + foreach (var f in _apiHawkSurfaceFrugalizers.Values) { f.Dispose(); } @@ -217,7 +217,7 @@ public void Dispose() public PrivateFontCollection CustomFonts { get; } = new PrivateFontCollection(); private readonly TextureFrugalizer _videoTextureFrugalizer; - private readonly Dictionary _luaSurfaceFrugalizers = new Dictionary(); + private readonly Dictionary _apiHawkSurfaceFrugalizers = new(); private readonly RenderTargetFrugalizer[] _shaderChainFrugalizers; private readonly RetroShaderChain _shaderChainHq2X, _shaderChainScanlines, _shaderChainBicubic; private RetroShaderChain _shaderChainUser; @@ -340,7 +340,7 @@ private FilterProgram BuildDefaultChain(Size chainInSize, Size chainOutSize, boo } //add lua layer 'emu' - AppendLuaLayer(chain, "emu"); + AppendApiHawkLayer(chain, DisplaySurfaceID.EmuCore); if(includeUserFilters) if (GlobalConfig.DispPrescale != 1) @@ -394,7 +394,7 @@ private FilterProgram BuildDefaultChain(Size chainInSize, Size chainOutSize, boo chain.AddFilter(fPresent, "presentation"); //add lua layer 'native' - AppendLuaLayer(chain, "native"); + AppendApiHawkLayer(chain, DisplaySurfaceID.Client); // and OSD goes on top of that // TODO - things break if this isn't present (the final presentation filter gets messed up when used with prescaling) @@ -416,18 +416,18 @@ private void AppendRetroShaderChain(FilterProgram program, string name, RetroSha } } - private void AppendLuaLayer(FilterProgram chain, string name) + private void AppendApiHawkLayer(FilterProgram chain, DisplaySurfaceID surfaceID) { - var luaNativeSurface = _luaSurfaceSets[name].GetCurrent(); + var luaNativeSurface = _apiHawkSurfaceSets[surfaceID].GetCurrent(); if (luaNativeSurface == null) { return; } - Texture2d luaNativeTexture = _luaSurfaceFrugalizers[name].Get(luaNativeSurface); + Texture2d luaNativeTexture = _apiHawkSurfaceFrugalizers[surfaceID].Get(luaNativeSurface); var fLuaLayer = new LuaLayer(); fLuaLayer.SetTexture(luaNativeTexture); - chain.AddFilter(fLuaLayer, name); + chain.AddFilter(fLuaLayer, surfaceID.GetName()); } /// @@ -1100,33 +1100,34 @@ private void LoadCustomFont(Stream fontStream) private bool? _lastVsyncSetting; private GraphicsControl _lastVsyncSettingGraphicsControl; - private readonly Dictionary _mapNameToLuaSurface = new Dictionary(); - private readonly Dictionary _mapLuaSurfaceToName = new Dictionary(); - private readonly Dictionary _luaSurfaceSets = new Dictionary(); + private readonly Dictionary _apiHawkIDToSurface = new(); + + /// Can't this just be a field/prop of ? --yoshi + private readonly Dictionary _apiHawkSurfaceToID = new(); + + private readonly Dictionary _apiHawkSurfaceSets = new(); /// /// Peeks a locked lua surface, or returns null if it isn't locked /// - public DisplaySurface PeekLockedLuaSurface(string name) + public DisplaySurface PeekApiHawkLockedSurface(DisplaySurfaceID surfaceID) { - if (_mapNameToLuaSurface.ContainsKey(name)) - return _mapNameToLuaSurface[name]; + if (_apiHawkIDToSurface.ContainsKey(surfaceID)) + return _apiHawkIDToSurface[surfaceID]; return null; } - /// locks the lua surface called - /// already locked, or unknown surface - public DisplaySurface LockLuaSurface(string name, bool clear=true) + public DisplaySurface LockApiHawkSurface(DisplaySurfaceID surfaceID, bool clear) { - if (_mapNameToLuaSurface.ContainsKey(name)) + if (_apiHawkIDToSurface.ContainsKey(surfaceID)) { - throw new InvalidOperationException($"Lua surface is already locked: {name}"); + throw new InvalidOperationException($"ApiHawk/Lua surface is already locked: {surfaceID.GetName()}"); } - if (!_luaSurfaceSets.TryGetValue(name, out var sdss)) + if (!_apiHawkSurfaceSets.TryGetValue(surfaceID, out var sdss)) { sdss = new SwappableDisplaySurfaceSet(); - _luaSurfaceSets.Add(name, sdss); + _apiHawkSurfaceSets.Add(surfaceID, sdss); } // placeholder logic for more abstracted surface definitions from filter chain @@ -1136,45 +1137,38 @@ public DisplaySurface LockLuaSurface(string name, bool clear=true) currNativeWidth += _clientExtraPadding.Horizontal; currNativeHeight += _clientExtraPadding.Vertical; - int width,height; - if (name == "emu") - { - width = _currEmuWidth; - height = _currEmuHeight; - width += _gameExtraPadding.Horizontal; - height += _gameExtraPadding.Vertical; - } - else if (name == "native") + var (width, height) = surfaceID switch { - width = currNativeWidth; height = currNativeHeight; - } - else throw new InvalidOperationException($"Unknown lua surface name: {name}"); + DisplaySurfaceID.EmuCore => (_currEmuWidth + _gameExtraPadding.Horizontal, _currEmuHeight + _gameExtraPadding.Vertical), + DisplaySurfaceID.Client => (currNativeWidth, currNativeHeight), + _ => throw new ArgumentException(message: "not a valid enum member", paramName: nameof(surfaceID)) + }; DisplaySurface ret = sdss.AllocateSurface(width, height, clear); - _mapNameToLuaSurface[name] = ret; - _mapLuaSurfaceToName[ret] = name; + _apiHawkIDToSurface[surfaceID] = ret; + _apiHawkSurfaceToID[ret] = surfaceID; return ret; } - public void ClearLuaSurfaces() + public void ClearApiHawkSurfaces() { - foreach (var kvp in _luaSurfaceSets) + foreach (var kvp in _apiHawkSurfaceSets) { try { - var surf = PeekLockedLuaSurface(kvp.Key); + var surf = PeekApiHawkLockedSurface(kvp.Key); DisplaySurface surfLocked = null; if (surf == null) { - surfLocked = LockLuaSurface(kvp.Key, true); + surfLocked = LockApiHawkSurface(kvp.Key, true); } if (surfLocked != null) { - UnlockLuaSurface(surfLocked); + UnlockApiHawkSurface(surfLocked); } - _luaSurfaceSets[kvp.Key].SetPending(null); + _apiHawkSurfaceSets[kvp.Key].SetPending(null); } catch (InvalidOperationException) { @@ -1184,17 +1178,17 @@ public void ClearLuaSurfaces() /// unlocks this DisplaySurface which had better have been locked as a lua surface /// already unlocked - public void UnlockLuaSurface(DisplaySurface surface) + public void UnlockApiHawkSurface(DisplaySurface surface) { - if (!_mapLuaSurfaceToName.ContainsKey(surface)) + if (!_apiHawkSurfaceToID.ContainsKey(surface)) { throw new InvalidOperationException("Surface was not locked as a lua surface"); } - string name = _mapLuaSurfaceToName[surface]; - _mapLuaSurfaceToName.Remove(surface); - _mapNameToLuaSurface.Remove(name); - _luaSurfaceSets[name].SetPending(surface); + var surfaceID = _apiHawkSurfaceToID[surface]; + _apiHawkSurfaceToID.Remove(surface); + _apiHawkIDToSurface.Remove(surfaceID); + _apiHawkSurfaceSets[surfaceID].SetPending(surface); } // helper classes: diff --git a/src/BizHawk.Client.EmuHawk/tools/Lua/LuaConsole.cs b/src/BizHawk.Client.EmuHawk/tools/Lua/LuaConsole.cs index 57d7a3578d6..bc194eaffb6 100644 --- a/src/BizHawk.Client.EmuHawk/tools/Lua/LuaConsole.cs +++ b/src/BizHawk.Client.EmuHawk/tools/Lua/LuaConsole.cs @@ -120,7 +120,7 @@ public LuaConsole() { Settings.Columns = LuaListView.AllColumns; - DisplayManager.ClearLuaSurfaces(); + DisplayManager.ClearApiHawkSurfaces(); if (DisplayManager.ClientExtraPadding != (0, 0, 0, 0)) { @@ -134,11 +134,7 @@ public LuaConsole() MainForm.FrameBufferResized(); } - if (LuaImp is Win32LuaLibraries luaLibsImpl) - { - luaLibsImpl.GuiLibrary?.DrawFinish(); - luaLibsImpl.Close(); - } + (LuaImp as Win32LuaLibraries)?.Close(); DisplayManager.OSD.ClearGuiText(); } else @@ -221,11 +217,6 @@ public override void Restart() return; } - if (luaLibsImpl.GuiLibrary != null && luaLibsImpl.GuiLibrary.HasLuaSurface) - { - luaLibsImpl.GuiLibrary.DrawFinish(); - } - runningScripts = luaLibsImpl.ScriptList.Where(lf => lf.Enabled).ToList(); foreach (var file in runningScripts) @@ -572,7 +563,6 @@ protected override void UpdateBefore() } luaLibsImpl.CallFrameBeforeEvent(); - luaLibsImpl.StartLuaDrawing(); } protected override void UpdateAfter() @@ -584,7 +574,6 @@ protected override void UpdateAfter() luaLibsImpl.CallFrameAfterEvent(); ResumeScripts(true); - luaLibsImpl.EndLuaDrawing(); } protected override void FastUpdateBefore() @@ -617,11 +606,6 @@ public void ResumeScripts(bool includeFrameWaiters) return; } - if (luaLibsImpl.GuiLibrary?.SurfaceIsNull == true) - { - luaLibsImpl.GuiLibrary.DrawNew("emu"); - } - foreach (var lf in luaLibsImpl.ScriptList.Where(l => l.Enabled && l.Thread != null && !l.Paused)) { try @@ -955,7 +939,7 @@ private void EnableLuaFile(LuaFile item) item.State = LuaFile.RunState.Disabled; }); - ReDraw(); + // there used to be a call here which did a redraw of the Gui/OSD, which included a call to `Tools.UpdateToolsAfter` --yoshi } catch (IOException) { @@ -967,17 +951,6 @@ private void EnableLuaFile(LuaFile item) } } - private void ReDraw() - { - // Shenanigans - // We want any gui.text messages from a script to immediately update even when paused - DisplayManager.OSD.ClearGuiText(); - Tools.UpdateToolsAfter(); - if (!(LuaImp is Win32LuaLibraries luaLibsImpl)) return; - luaLibsImpl.EndLuaDrawing(); - luaLibsImpl.StartLuaDrawing(); - } - private void PauseScriptMenuItem_Click(object sender, EventArgs e) { foreach (var s in SelectedFiles) @@ -1538,7 +1511,7 @@ protected override bool ProcessTabKey(bool forward) private void EraseToolbarItem_Click(object sender, EventArgs e) { - DisplayManager.ClearLuaSurfaces(); + DisplayManager.ClearApiHawkSurfaces(); } // Stupid designer @@ -1581,7 +1554,7 @@ private void ToggleLuaScript(LuaFile file) luaLibsImpl.RegisteredFunctions.RemoveForFile(file, Emulator); luaLibsImpl.CallExitEvent(file); file.Stop(); - ReDraw(); + // there used to be a call here which did a redraw of the Gui/OSD, which included a call to `Tools.UpdateToolsAfter` --yoshi } } diff --git a/src/BizHawk.Client.EmuHawk/tools/Lua/Win32LuaLibraries.cs b/src/BizHawk.Client.EmuHawk/tools/Lua/Win32LuaLibraries.cs index 91dbab46ece..a933a5c6b3a 100644 --- a/src/BizHawk.Client.EmuHawk/tools/Lua/Win32LuaLibraries.cs +++ b/src/BizHawk.Client.EmuHawk/tools/Lua/Win32LuaLibraries.cs @@ -49,6 +49,7 @@ void EnumerateLuaFunctions(string name, Type type, LuaLibraryBase instance) LuaWait = new AutoResetEvent(false); Docs.Clear(); var apiContainer = ApiManager.RestartLua(serviceProvider, LogToLuaConsole, _mainForm, _displayManager, _inputManager, _mainForm.MovieSession, _mainForm.Tools, config, emulator, game); + _guiAPI = (GuiApi) apiContainer.Gui; // Register lua libraries foreach (var lib in Client.Common.ReflectionCache.Types.Concat(EmuHawk.ReflectionCache.Types) @@ -106,6 +107,8 @@ void EnumerateLuaFunctions(string name, Type type, LuaLibraryBase instance) private readonly IDisplayManagerForApi _displayManager; + private GuiApi _guiAPI; + private readonly InputManager _inputManager; private readonly MainForm _mainForm; @@ -125,8 +128,6 @@ void EnumerateLuaFunctions(string name, Type type, LuaLibraryBase instance) public string EngineName => Lua.WhichLua; - public GuiLuaLibrary GuiLibrary => (GuiLuaLibrary) Libraries[typeof(GuiLuaLibrary)]; - public bool IsRebootingCore { get; set; } public bool IsUpdateSupressed { get; set; } @@ -148,6 +149,7 @@ public void Restart( IGameInfo game) { var apiContainer = ApiManager.RestartLua(newServiceProvider, LogToLuaConsole, _mainForm, _displayManager, _inputManager, _mainForm.MovieSession, _mainForm.Tools, config, emulator, game); + _guiAPI = (GuiApi) apiContainer.Gui; foreach (var lib in Libraries.Values) { lib.APIs = apiContainer; @@ -155,22 +157,6 @@ public void Restart( } } - public void StartLuaDrawing() - { - if (ScriptList.Count != 0 && GuiLibrary.SurfaceIsNull && !IsUpdateSupressed) - { - GuiLibrary.DrawNew("emu"); - } - } - - public void EndLuaDrawing() - { - if (ScriptList.Count != 0 && !IsUpdateSupressed) - { - GuiLibrary.DrawFinish(); - } - } - public bool FrameAdvanceRequested { get; private set; } public LuaFunctionList RegisteredFunctions { get; } = new LuaFunctionList(); @@ -252,7 +238,6 @@ public void Close() FormsLibrary.DestroyAll(); _lua.Close(); _lua = new Lua(); - GuiLibrary.Dispose(); } public INamedLuaFunction CreateAndRegisterNamedFunction(LuaFunction function, string theEvent, Action logCallback, LuaFile luaFile, string name = null) @@ -307,7 +292,9 @@ public void ExecuteString(string command) { LuaLibraryBase.SetCurrentThread(lf); + _guiAPI.LockEmuSurfaceLua(); var execResult = _currThread.Resume(0); + _guiAPI.UnlockEmuSurfaceLua(); _lua.RunScheduledDisposes(); // TODO: I don't think this is needed anymore, we run this regularly anyway @@ -322,6 +309,11 @@ public void ExecuteString(string command) FrameAdvanceRequested = false; return result; } + catch (Exception) + { + _guiAPI.UnlockEmuSurfaceLua(); + throw; + } finally { LuaLibraryBase.ClearCurrentThread();