Skip to content

Commit 7749d02

Browse files
authored
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
1 parent 58d24cd commit 7749d02

File tree

9 files changed

+281
-197
lines changed

9 files changed

+281
-197
lines changed

src/BizHawk.Client.Common/Api/Classes/GuiApi.cs

Lines changed: 117 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,14 @@ public sealed class GuiApi : IGuiApi
3939

4040
private Color? _defaultTextBackground = Color.FromArgb(128, 0, 0, 0);
4141

42+
private DisplaySurface _clientSurface;
43+
4244
private DisplaySurface _GUISurface;
4345

4446
private (int Left, int Top, int Right, int Bottom) _padding = (0, 0, 0, 0);
4547

48+
private DisplaySurfaceID? _usingSurfaceID = null;
49+
4650
public bool HasGUISurface => _GUISurface != null;
4751

4852
public GuiApi(Action<string> logCallback, IDisplayManagerForApi displayManager)
@@ -55,9 +59,9 @@ public GuiApi(Action<string> logCallback, IDisplayManagerForApi displayManager)
5559

5660
private Pen GetPen(Color color) => _pens.TryGetValue(color, out var p) ? p : (_pens[color] = new Pen(color));
5761

58-
private Graphics GetGraphics()
62+
private Graphics GetGraphics(DisplaySurfaceID? surfaceID)
5963
{
60-
var g = _GUISurface?.GetGraphics() ?? Graphics.FromImage(_nullGraphicsBitmap);
64+
var g = GetRelevantSurface(surfaceID)?.GetGraphics() ?? Graphics.FromImage(_nullGraphicsBitmap);
6165
var (tx, ty) = Emulator.ScreenLogicalOffsets();
6266
if (tx != 0 || ty != 0)
6367
{
@@ -75,25 +79,94 @@ private Graphics GetGraphics()
7579

7680
public void SetAttributes(ImageAttributes a) => _attributes = a;
7781

78-
public void DrawNew(string name, bool clear)
82+
private DisplaySurface GetRelevantSurface(DisplaySurfaceID? surfaceID) => (surfaceID ?? _usingSurfaceID) switch
83+
{
84+
DisplaySurfaceID.EmuCore => _GUISurface,
85+
DisplaySurfaceID.Client => _clientSurface,
86+
_ => throw new Exception()
87+
};
88+
89+
private void LockSurface(DisplaySurfaceID surfaceID)
90+
{
91+
switch (surfaceID)
92+
{
93+
case DisplaySurfaceID.EmuCore:
94+
if (_GUISurface != null) throw new InvalidOperationException("attempt to lock surface without unlocking previous");
95+
_GUISurface = _displayManager.LockApiHawkSurface(surfaceID, clear: true);
96+
break;
97+
case DisplaySurfaceID.Client:
98+
if (_clientSurface != null) throw new InvalidOperationException("attempt to lock surface without unlocking previous");
99+
_clientSurface = _displayManager.LockApiHawkSurface(surfaceID, clear: true);
100+
break;
101+
default:
102+
throw new ArgumentException(message: "not a valid enum member", paramName: nameof(surfaceID));
103+
}
104+
}
105+
106+
private void UnlockSurface(DisplaySurfaceID surfaceID)
107+
{
108+
switch (surfaceID)
109+
{
110+
case DisplaySurfaceID.EmuCore:
111+
if (_GUISurface != null) _displayManager.UnlockApiHawkSurface(_GUISurface);
112+
_GUISurface = null;
113+
break;
114+
case DisplaySurfaceID.Client:
115+
if (_clientSurface != null) _displayManager.UnlockApiHawkSurface(_clientSurface);
116+
_clientSurface = null;
117+
break;
118+
default:
119+
throw new ArgumentException(message: "not a valid enum member", paramName: nameof(surfaceID));
120+
}
121+
}
122+
123+
public void WithSurface(DisplaySurfaceID surfaceID, Action drawingCallsFunc)
124+
{
125+
LockSurface(surfaceID);
126+
_usingSurfaceID = surfaceID;
127+
try
128+
{
129+
drawingCallsFunc();
130+
}
131+
finally
132+
{
133+
_usingSurfaceID = null;
134+
UnlockSurface(surfaceID);
135+
}
136+
}
137+
138+
public void LockEmuSurfaceLua()
79139
{
80140
try
81141
{
82-
DrawFinish();
83-
_GUISurface = _displayManager.LockLuaSurface(name, clear);
142+
UnlockSurface(DisplaySurfaceID.EmuCore);
143+
LockSurface(DisplaySurfaceID.EmuCore);
84144
}
85145
catch (InvalidOperationException ex)
86146
{
87147
LogCallback(ex.ToString());
88148
}
89149
}
90150

91-
public void DrawFinish()
151+
public void UnlockEmuSurfaceLua() => UnlockSurface(DisplaySurfaceID.EmuCore);
152+
153+
public void DrawNew(string name, bool clear)
92154
{
93-
if (_GUISurface != null) _displayManager.UnlockLuaSurface(_GUISurface);
94-
_GUISurface = null;
155+
switch (name)
156+
{
157+
case null:
158+
case "emu":
159+
LogCallback("the `DrawNew(\"emu\")` function has been deprecated");
160+
return;
161+
case "native":
162+
throw new InvalidOperationException("the ability to draw in the margins with `DrawNew(\"native\")` has been removed");
163+
default:
164+
throw new InvalidOperationException("invalid surface name");
165+
}
95166
}
96167

168+
public void DrawFinish() => LogCallback("the `DrawFinish()` function has been deprecated");
169+
97170
public void SetPadding(int all) => _padding = (all, all, all, all);
98171

99172
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()
104177

105178
public void AddMessage(string message) => _displayManager.OSD.AddMessage(message);
106179

107-
public void ClearGraphics()
108-
{
109-
_GUISurface.Clear();
110-
DrawFinish();
111-
}
180+
public void ClearGraphics(DisplaySurfaceID? surfaceID = null) => GetRelevantSurface(surfaceID).Clear();
112181

113182
public void ClearText() => _displayManager.OSD.ClearGuiText();
114183

@@ -138,11 +207,11 @@ public void SetDefaultPixelFont(string fontfamily)
138207
}
139208
}
140209

141-
public void DrawBezier(Point p1, Point p2, Point p3, Point p4, Color? color = null)
210+
public void DrawBezier(Point p1, Point p2, Point p3, Point p4, Color? color = null, DisplaySurfaceID? surfaceID = null)
142211
{
143212
try
144213
{
145-
using var g = GetGraphics();
214+
using var g = GetGraphics(surfaceID);
146215
g.CompositingMode = _compositingMode;
147216
g.DrawBezier(GetPen(color ?? _defaultForeground), p1, p2, p3, p4);
148217
}
@@ -152,11 +221,11 @@ public void DrawBezier(Point p1, Point p2, Point p3, Point p4, Color? color = nu
152221
}
153222
}
154223

155-
public void DrawBeziers(Point[] points, Color? color = null)
224+
public void DrawBeziers(Point[] points, Color? color = null, DisplaySurfaceID? surfaceID = null)
156225
{
157226
try
158227
{
159-
using var g = GetGraphics();
228+
using var g = GetGraphics(surfaceID);
160229
g.CompositingMode = _compositingMode;
161230
g.DrawBeziers(GetPen(color ?? _defaultForeground), points);
162231
}
@@ -166,7 +235,7 @@ public void DrawBeziers(Point[] points, Color? color = null)
166235
}
167236
}
168237

169-
public void DrawBox(int x, int y, int x2, int y2, Color? line = null, Color? background = null)
238+
public void DrawBox(int x, int y, int x2, int y2, Color? line = null, Color? background = null, DisplaySurfaceID? surfaceID = null)
170239
{
171240
try
172241
{
@@ -192,7 +261,7 @@ public void DrawBox(int x, int y, int x2, int y2, Color? line = null, Color? bac
192261
y -= y2;
193262
h = Math.Max(y2, 0.1f);
194263
}
195-
using var g = GetGraphics();
264+
using var g = GetGraphics(surfaceID);
196265
g.CompositingMode = _compositingMode;
197266
g.DrawRectangle(GetPen(line ?? _defaultForeground), x, y, w, h);
198267
var bg = background ?? _defaultBackground;
@@ -204,11 +273,11 @@ public void DrawBox(int x, int y, int x2, int y2, Color? line = null, Color? bac
204273
}
205274
}
206275

207-
public void DrawEllipse(int x, int y, int width, int height, Color? line = null, Color? background = null)
276+
public void DrawEllipse(int x, int y, int width, int height, Color? line = null, Color? background = null, DisplaySurfaceID? surfaceID = null)
208277
{
209278
try
210279
{
211-
using var g = GetGraphics();
280+
using var g = GetGraphics(surfaceID);
212281
var bg = background ?? _defaultBackground;
213282
if (bg != null) g.FillEllipse(GetBrush(bg.Value), x, y, width, height);
214283
g.CompositingMode = _compositingMode;
@@ -220,7 +289,7 @@ public void DrawEllipse(int x, int y, int width, int height, Color? line = null,
220289
}
221290
}
222291

223-
public void DrawIcon(string path, int x, int y, int? width = null, int? height = null)
292+
public void DrawIcon(string path, int x, int y, int? width = null, int? height = null, DisplaySurfaceID? surfaceID = null)
224293
{
225294
try
226295
{
@@ -229,7 +298,7 @@ public void DrawIcon(string path, int x, int y, int? width = null, int? height =
229298
AddMessage($"File not found: {path}");
230299
return;
231300
}
232-
using var g = GetGraphics();
301+
using var g = GetGraphics(surfaceID);
233302
g.CompositingMode = _compositingMode;
234303
g.DrawIcon(
235304
width != null && height != null
@@ -245,9 +314,9 @@ public void DrawIcon(string path, int x, int y, int? width = null, int? height =
245314
}
246315
}
247316

248-
public void DrawImage(Image img, int x, int y, int? width = null, int? height = null, bool cache = true)
317+
public void DrawImage(Image img, int x, int y, int? width = null, int? height = null, bool cache = true, DisplaySurfaceID? surfaceID = null)
249318
{
250-
using var g = GetGraphics();
319+
using var g = GetGraphics(surfaceID);
251320
g.CompositingMode = _compositingMode;
252321
g.DrawImage(
253322
img,
@@ -260,14 +329,14 @@ public void DrawImage(Image img, int x, int y, int? width = null, int? height =
260329
_attributes
261330
);
262331
}
263-
public void DrawImage(string path, int x, int y, int? width = null, int? height = null, bool cache = true)
332+
public void DrawImage(string path, int x, int y, int? width = null, int? height = null, bool cache = true, DisplaySurfaceID? surfaceID = null)
264333
{
265334
if (!File.Exists(path))
266335
{
267336
LogCallback($"File not found: {path}");
268337
return;
269338
}
270-
using var g = GetGraphics();
339+
using var g = GetGraphics(surfaceID);
271340
var isCached = _imageCache.ContainsKey(path);
272341
var img = isCached ? _imageCache[path] : Image.FromFile(path);
273342
if (!isCached && cache) _imageCache[path] = img;
@@ -290,9 +359,9 @@ public void ClearImageCache()
290359
_imageCache.Clear();
291360
}
292361

293-
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)
362+
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)
294363
{
295-
using var g = GetGraphics();
364+
using var g = GetGraphics(surfaceID);
296365
g.CompositingMode = _compositingMode;
297366
g.DrawImage(
298367
img,
@@ -306,14 +375,14 @@ public void DrawImageRegion(Image img, int source_x, int source_y, int source_wi
306375
);
307376
}
308377

309-
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)
378+
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)
310379
{
311380
if (!File.Exists(path))
312381
{
313382
LogCallback($"File not found: {path}");
314383
return;
315384
}
316-
using var g = GetGraphics();
385+
using var g = GetGraphics(surfaceID);
317386
g.CompositingMode = _compositingMode;
318387
g.DrawImage(
319388
_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_
327396
);
328397
}
329398

330-
public void DrawLine(int x1, int y1, int x2, int y2, Color? color = null)
399+
public void DrawLine(int x1, int y1, int x2, int y2, Color? color = null, DisplaySurfaceID? surfaceID = null)
331400
{
332-
using var g = GetGraphics();
401+
using var g = GetGraphics(surfaceID);
333402
g.CompositingMode = _compositingMode;
334403
g.DrawLine(GetPen(color ?? _defaultForeground), x1, y1, x2, y2);
335404
}
336405

337-
public void DrawAxis(int x, int y, int size, Color? color = null)
406+
public void DrawAxis(int x, int y, int size, Color? color = null, DisplaySurfaceID? surfaceID = null)
338407
{
339-
DrawLine(x + size, y, x - size, y, color ?? _defaultForeground);
340-
DrawLine(x, y + size, x, y - size, color ?? _defaultForeground);
408+
DrawLine(x + size, y, x - size, y, color ?? _defaultForeground, surfaceID: surfaceID);
409+
DrawLine(x, y + size, x, y - size, color ?? _defaultForeground, surfaceID: surfaceID);
341410
}
342411

343-
public void DrawPie(int x, int y, int width, int height, int startangle, int sweepangle, Color? line = null, Color? background = null)
412+
public void DrawPie(int x, int y, int width, int height, int startangle, int sweepangle, Color? line = null, Color? background = null, DisplaySurfaceID? surfaceID = null)
344413
{
345-
using var g = GetGraphics();
414+
using var g = GetGraphics(surfaceID);
346415
g.CompositingMode = _compositingMode;
347416
var bg = background ?? _defaultBackground;
348417
if (bg != null) g.FillPie(GetBrush(bg.Value), x, y, width, height, startangle, sweepangle);
349418
g.DrawPie(GetPen(line ?? _defaultForeground), x + 1, y + 1, width - 1, height - 1, startangle, sweepangle);
350419
}
351420

352-
public void DrawPixel(int x, int y, Color? color = null)
421+
public void DrawPixel(int x, int y, Color? color = null, DisplaySurfaceID? surfaceID = null)
353422
{
354423
try
355424
{
356-
using var g = GetGraphics();
425+
using var g = GetGraphics(surfaceID);
357426
g.DrawLine(GetPen(color ?? _defaultForeground), x, y, x + 0.1F, y);
358427
}
359428
catch (Exception)
@@ -362,11 +431,11 @@ public void DrawPixel(int x, int y, Color? color = null)
362431
}
363432
}
364433

365-
public void DrawPolygon(Point[] points, Color? line = null, Color? background = null)
434+
public void DrawPolygon(Point[] points, Color? line = null, Color? background = null, DisplaySurfaceID? surfaceID = null)
366435
{
367436
try
368437
{
369-
using var g = GetGraphics();
438+
using var g = GetGraphics(surfaceID);
370439
g.DrawPolygon(GetPen(line ?? _defaultForeground), points);
371440
var bg = background ?? _defaultBackground;
372441
if (bg != null) g.FillPolygon(GetBrush(bg.Value), points);
@@ -377,17 +446,17 @@ public void DrawPolygon(Point[] points, Color? line = null, Color? background =
377446
}
378447
}
379448

380-
public void DrawRectangle(int x, int y, int width, int height, Color? line = null, Color? background = null)
449+
public void DrawRectangle(int x, int y, int width, int height, Color? line = null, Color? background = null, DisplaySurfaceID? surfaceID = null)
381450
{
382-
using var g = GetGraphics();
451+
using var g = GetGraphics(surfaceID);
383452
var w = Math.Max(width, 0.1F);
384453
var h = Math.Max(height, 0.1F);
385454
g.DrawRectangle(GetPen(line ?? _defaultForeground), x, y, w, h);
386455
var bg = background ?? _defaultBackground;
387456
if (bg != null) g.FillRectangle(GetBrush(bg.Value), x + 1, y + 1, Math.Max(w - 1, 0), Math.Max(h - 1, 0));
388457
}
389458

390-
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)
459+
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)
391460
{
392461
try
393462
{
@@ -402,7 +471,7 @@ public void DrawString(int x, int y, string message, Color? forecolor = null, Co
402471
_ => FontStyle.Regular
403472
};
404473

405-
using var g = GetGraphics();
474+
using var g = GetGraphics(surfaceID);
406475

407476
// The text isn't written out using GenericTypographic, so measuring it using GenericTypographic seemed to make it worse.
408477
// 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
455524
}
456525
}
457526

458-
public void DrawText(int x, int y, string message, Color? forecolor = null, Color? backcolor = null, string fontfamily = null)
527+
public void DrawText(int x, int y, string message, Color? forecolor = null, Color? backcolor = null, string fontfamily = null, DisplaySurfaceID? surfaceID = null)
459528
{
460529
try
461530
{
@@ -479,7 +548,7 @@ public void DrawText(int x, int y, string message, Color? forecolor = null, Colo
479548
index = _defaultPixelFont;
480549
break;
481550
}
482-
using var g = GetGraphics();
551+
using var g = GetGraphics(surfaceID);
483552
var font = new Font(_displayManager.CustomFonts.Families[index], 8, FontStyle.Regular, GraphicsUnit.Pixel);
484553
var sizeOfText = g.MeasureString(
485554
message,

0 commit comments

Comments
 (0)