mirror of
https://github.com/Stone-Red-Code/RemSox.git
synced 2026-09-10 07:46:31 +02:00
Add thread-safety with locks to window manager and rendering code
This commit is contained in:
@@ -16,6 +16,7 @@ public class TerminalProcess : Process
|
||||
private readonly List<string> history = new();
|
||||
private string currentInput = "";
|
||||
private readonly List<Text> textLines = new();
|
||||
private readonly object textLinesLock = new object();
|
||||
private const int LineHeight = 30;
|
||||
private Size lastSize = new Size(-1, -1);
|
||||
|
||||
@@ -26,11 +27,11 @@ public class TerminalProcess : Process
|
||||
internal override void Run()
|
||||
{
|
||||
window = WindowManager.CreateWindow(this, "Terminal", new Point(50, 50), new Size(400, 300));
|
||||
window.AutoFlush = true;
|
||||
|
||||
PrintLine("RemSox GUI Terminal v1.0");
|
||||
PrintLine("Type 'help' for commands.");
|
||||
|
||||
window.Flush();
|
||||
window.OnKeyEvent += HandleKey;
|
||||
|
||||
while (!StopRequested)
|
||||
@@ -40,7 +41,7 @@ public class TerminalProcess : Process
|
||||
lastSize = window.Size;
|
||||
UpdateDisplay();
|
||||
}
|
||||
System.Threading.Thread.Sleep(50);
|
||||
Thread.Sleep(50);
|
||||
}
|
||||
|
||||
window.OnKeyEvent -= HandleKey;
|
||||
@@ -106,6 +107,8 @@ public class TerminalProcess : Process
|
||||
|
||||
if (maxLines < 1) maxLines = 1;
|
||||
|
||||
lock (textLinesLock)
|
||||
{
|
||||
// Ensure we have enough Text elements for maxLines + 1 (input line)
|
||||
while (textLines.Count <= maxLines)
|
||||
{
|
||||
@@ -143,5 +146,8 @@ public class TerminalProcess : Process
|
||||
{
|
||||
textLines[i].Content = "";
|
||||
}
|
||||
|
||||
window.Flush();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,8 +17,11 @@ public sealed class CanvasRenderSource : IRenderSource
|
||||
private static Point lastPointerPosition = new Point(-1, -1);
|
||||
private static List<int> orderedWindowsCache = new();
|
||||
private static bool isZOrderDirty = true;
|
||||
private static readonly object renderLock = new object();
|
||||
|
||||
public void Render(IEnumerable<RenderCommand> commands)
|
||||
{
|
||||
lock (renderLock)
|
||||
{
|
||||
bool changed = false;
|
||||
foreach (RenderCommand command in commands)
|
||||
@@ -112,8 +115,11 @@ public sealed class CanvasRenderSource : IRenderSource
|
||||
isDirty = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void CompositeAndDisplay(Canvas screenCanvas, Point pointerPosition)
|
||||
{
|
||||
lock (renderLock)
|
||||
{
|
||||
if (!isDirty && pointerPosition == lastPointerPosition)
|
||||
{
|
||||
@@ -142,6 +148,7 @@ public sealed class CanvasRenderSource : IRenderSource
|
||||
lastPointerPosition = pointerPosition;
|
||||
isDirty = false;
|
||||
}
|
||||
}
|
||||
|
||||
private static void RenderWindow(Canvas canvas, RenderCommand command)
|
||||
{
|
||||
|
||||
@@ -46,6 +46,7 @@ public sealed class Window(string title, int processId, int id, IRenderSource re
|
||||
|
||||
public bool IsDragging => currentInteraction == InteractionMode.Drag;
|
||||
|
||||
private readonly object uiElementsLock = new object();
|
||||
private readonly Dictionary<int, UIElement> uiElements = [];
|
||||
|
||||
private int nextUIElementId = 1;
|
||||
@@ -87,7 +88,10 @@ public sealed class Window(string title, int processId, int id, IRenderSource re
|
||||
Flush();
|
||||
}
|
||||
|
||||
lock (uiElementsLock)
|
||||
{
|
||||
uiElements.Add(uiElementId, uiElement);
|
||||
}
|
||||
|
||||
return uiElement;
|
||||
}
|
||||
@@ -105,8 +109,16 @@ public sealed class Window(string title, int processId, int id, IRenderSource re
|
||||
return;
|
||||
}
|
||||
|
||||
bool anyChildChanged;
|
||||
List<UIElement> elementsCopy;
|
||||
|
||||
lock (uiElementsLock)
|
||||
{
|
||||
anyChildChanged = uiElements.Values.Any(e => e.AnyPropertyChanged);
|
||||
elementsCopy = uiElements.Values.ToList();
|
||||
}
|
||||
|
||||
bool windowStateChanged = isFirstRender || Size != lastRenderedSize || IsFocused != lastRenderedIsFocused || Title != lastRenderedTitle;
|
||||
bool anyChildChanged = uiElements.Values.Any(e => e.AnyPropertyChanged);
|
||||
bool positionChanged = Position != lastRenderedPosition;
|
||||
bool zIndexChanged = ZIndex != lastRenderedZIndex;
|
||||
|
||||
@@ -133,7 +145,7 @@ public sealed class Window(string title, int processId, int id, IRenderSource re
|
||||
}
|
||||
});
|
||||
|
||||
foreach (UIElement element in uiElements.Values)
|
||||
foreach (UIElement element in elementsCopy)
|
||||
{
|
||||
commands.Add(new RenderCommand
|
||||
{
|
||||
|
||||
@@ -13,8 +13,9 @@ namespace RemSox.UI.GUI.Windows;
|
||||
|
||||
public static class WindowManager
|
||||
{
|
||||
private static readonly object windowsLock = new object();
|
||||
// Process ID to list of windows
|
||||
private static readonly ConcurrentDictionary<int, List<Window>> windows = new();
|
||||
private static readonly Dictionary<int, List<Window>> windows = new();
|
||||
|
||||
private static int nextWindowId = 1;
|
||||
private static int nextZIndex = 1;
|
||||
@@ -76,39 +77,51 @@ public static class WindowManager
|
||||
ZIndex = nextZIndex++
|
||||
};
|
||||
|
||||
lock (windowsLock)
|
||||
{
|
||||
if (!windows.ContainsKey(process.Id))
|
||||
{
|
||||
windows[process.Id] = [];
|
||||
}
|
||||
|
||||
windows[process.Id].Add(window);
|
||||
}
|
||||
|
||||
return window;
|
||||
}
|
||||
|
||||
public static void CloseWindow(Window window)
|
||||
{
|
||||
lock (windowsLock)
|
||||
{
|
||||
if (windows.TryGetValue(window.ProcessId, out var processWindows))
|
||||
{
|
||||
processWindows.Remove(window);
|
||||
}
|
||||
}
|
||||
|
||||
renderSource.Render(new[] { new RenderCommand { WindowId = window.Id, ElementId = window.Id, ElementType = "WindowClose", Position = window.Position, Properties = new Dictionary<string, object?>() } });
|
||||
}
|
||||
|
||||
public static List<Window> GetWindowsForProcess(Process process)
|
||||
{
|
||||
lock (windowsLock)
|
||||
{
|
||||
if (windows.TryGetValue(process.Id, out var processWindows))
|
||||
{
|
||||
return processWindows;
|
||||
return processWindows.ToList();
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
public static void CloseWindowsForProcess(Process process)
|
||||
{
|
||||
windows.TryRemove(process.Id, out _);
|
||||
lock (windowsLock)
|
||||
{
|
||||
windows.Remove(process.Id);
|
||||
}
|
||||
}
|
||||
|
||||
public static void FocusWindow(Window? window)
|
||||
@@ -137,7 +150,12 @@ public static class WindowManager
|
||||
|
||||
public static Window? TryBeginInteract(Point pointerPosition)
|
||||
{
|
||||
var allWindows = windows.Values.SelectMany(w => w).OrderByDescending(w => w.ZIndex);
|
||||
List<Window> allWindows;
|
||||
lock (windowsLock)
|
||||
{
|
||||
allWindows = windows.Values.SelectMany(w => w).OrderByDescending(w => w.ZIndex).ToList();
|
||||
}
|
||||
|
||||
foreach (var window in allWindows)
|
||||
{
|
||||
if (window.TryBeginInteract(pointerPosition))
|
||||
@@ -150,14 +168,17 @@ public static class WindowManager
|
||||
|
||||
public static void InvalidateAll()
|
||||
{
|
||||
foreach (var processWindows in windows.Values)
|
||||
List<Window> allWindows;
|
||||
lock (windowsLock)
|
||||
{
|
||||
foreach (var window in processWindows)
|
||||
allWindows = windows.Values.SelectMany(w => w).ToList();
|
||||
}
|
||||
|
||||
foreach (var window in allWindows)
|
||||
{
|
||||
window.Invalidate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static int GetNextWindowId()
|
||||
{
|
||||
@@ -166,13 +187,33 @@ public static class WindowManager
|
||||
|
||||
sealed private class MuliRenderSource(List<IRenderSource> sources) : IRenderSource
|
||||
{
|
||||
public void AddSource(IRenderSource source) => sources.Add(source);
|
||||
private readonly object sourcesLock = new object();
|
||||
|
||||
public void RemoveSource(IRenderSource source) => sources.Remove(source);
|
||||
public void AddSource(IRenderSource source)
|
||||
{
|
||||
lock (sourcesLock)
|
||||
{
|
||||
sources.Add(source);
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveSource(IRenderSource source)
|
||||
{
|
||||
lock (sourcesLock)
|
||||
{
|
||||
sources.Remove(source);
|
||||
}
|
||||
}
|
||||
|
||||
public void Render(IEnumerable<RenderCommand> commands)
|
||||
{
|
||||
foreach (IRenderSource source in sources)
|
||||
List<IRenderSource> sourcesCopy;
|
||||
lock (sourcesLock)
|
||||
{
|
||||
sourcesCopy = sources.ToList();
|
||||
}
|
||||
|
||||
foreach (IRenderSource source in sourcesCopy)
|
||||
{
|
||||
source.Render(commands);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user