Skip to content

Commit

Permalink
Refactor DisplaySurface locking as used by ApiHawk (and Lua) (#2575)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
YoshiRulz authored Jan 17, 2021
1 parent 58d24cd commit 7749d02
Show file tree
Hide file tree
Showing 9 changed files with 281 additions and 197 deletions.
165 changes: 117 additions & 48 deletions src/BizHawk.Client.Common/Api/Classes/GuiApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<string> logCallback, IDisplayManagerForApi displayManager)
Expand All @@ -55,9 +59,9 @@ public GuiApi(Action<string> 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)
{
Expand All @@ -75,25 +79,94 @@ 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)
{
LogCallback(ex.ToString());
}
}

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);
Expand All @@ -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();

Expand Down Expand Up @@ -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);
}
Expand All @@ -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);
}
Expand All @@ -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
{
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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
{
Expand All @@ -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
Expand All @@ -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,
Expand All @@ -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;
Expand All @@ -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,
Expand All @@ -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)),
Expand All @@ -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)
Expand All @@ -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);
Expand All @@ -377,17 +446,17 @@ 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);
var bg = background ?? _defaultBackground;
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
{
Expand All @@ -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
Expand Down Expand Up @@ -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
{
Expand All @@ -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,
Expand Down
Loading

1 comment on commit 7749d02

@YoshiRulz
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This didn't actually fix #2571! >:( It is nearly there though, I just needed to call UnlockSurface when reinitialising the ApiContainer. I did this in Dispose: bba4286, a0ebb2f

Please sign in to comment.