diff --git a/Kernel.cs b/Kernel.cs index b04c15c..9d86523 100644 --- a/Kernel.cs +++ b/Kernel.cs @@ -34,8 +34,6 @@ public class Kernel : Sys.Kernel Sys.Mouse.MouseManager.Initialize(); Sys.Keyboard.KeyboardManager.Initialize(); - - _ = ProcessManager.SpawnProcess(); } protected override void Run() @@ -57,13 +55,13 @@ public class Kernel : Sys.Kernel } } - Thread.Sleep(1000); + ProcessManager.TickAllProcesses(); } } public class TestProcess() : Process("Test Process") { - internal override void Run(string[] args) + internal override void Start(string[] args) { Window window = WindowManager.CreateWindow(this, "Test Window", Point.Empty, new Size(200, 150)); window.AutoFlush = true; @@ -78,11 +76,11 @@ public class TestProcess() : Process("Test Process") circle.Radius = 40; SendMessageToAllProcesses(new TestMessage { SenderProcessId = Id }); + } - while (!StopRequested) - { - Thread.Sleep(1000); - } + internal override void Tick() + { + // Example tick logic - could be used for animations, timed events, etc. } internal override void HandleInterProcessMessage(Message message) diff --git a/Processes/CliProcess.cs b/Processes/CliProcess.cs index a38ee3b..db2e213 100644 --- a/Processes/CliProcess.cs +++ b/Processes/CliProcess.cs @@ -5,29 +5,66 @@ namespace RemSox.Processes; internal class CliProcess() : Process("Cli") { - internal override void Run(string[] args) + private string currentInput = string.Empty; + + internal override void Start(string[] args) { Console.Clear(); Console.WriteLine("Welcome RemSox!"); Console.WriteLine("Type 'help' to see available commands."); + Console.Write("> "); + } - while (!StopRequested) + internal override void Tick() + { + while (Console.KeyAvailable) { - Console.Write("> "); + ConsoleKeyInfo key = Console.ReadKey(intercept: true); - string? input = Console.ReadLine(); - - if (string.IsNullOrWhiteSpace(input)) + switch (key.Key) { - continue; - } + case ConsoleKey.Enter: + Console.WriteLine(); - bool handled = CommandManager.TryExecute(input, line => Console.WriteLine(line)); + HandleCommand(currentInput); - if (!handled) - { - Console.WriteLine($"\"{input}\" is not a command"); + currentInput = string.Empty; + Console.Write("> "); + break; + + case ConsoleKey.Backspace: + if (currentInput.Length > 0) + { + currentInput = currentInput[..^1]; + Console.Write("\b \b"); + } + break; + + default: + currentInput += key.KeyChar; + Console.Write(key.KeyChar); + break; } } } -} + + internal override void Stop() + { + Console.Clear(); + } + + private static void HandleCommand(string input) + { + if (string.IsNullOrWhiteSpace(input)) + return; + + bool handled = CommandManager.TryExecute( + input, + line => Console.WriteLine(line)); + + if (!handled) + { + Console.WriteLine($"\"{input}\" is not a command"); + } + } +} \ No newline at end of file diff --git a/Processes/DesktopProcess.cs b/Processes/DesktopProcess.cs index 25fe661..bb7117e 100644 --- a/Processes/DesktopProcess.cs +++ b/Processes/DesktopProcess.cs @@ -10,7 +10,7 @@ internal class DesktopProcess() : Process("Desktop Manager") { private static bool isGraphicsInitialized = false; - internal override void Run(string[] args) + internal override void Start(string[] args) { if (!isGraphicsInitialized) { @@ -54,13 +54,15 @@ internal class DesktopProcess() : Process("Desktop Manager") testWindow.AutoFlush = true; testWindow.Flush(); + } - while (!StopRequested) + internal override void Tick() + { + WindowManager.Update(); + + if (!ProcessManager.IsProcessRunning()) { - WindowManager.Update(); - - // Sleep slightly to yield CPU to the main CLI thread (approx 60 FPS) - //Thread.Sleep(16); + ProcessManager.SpawnProcess(); } } } diff --git a/Processes/TerminalProcess.cs b/Processes/TerminalProcess.cs index 381c253..de71942 100644 --- a/Processes/TerminalProcess.cs +++ b/Processes/TerminalProcess.cs @@ -11,7 +11,7 @@ namespace RemSox.Processes; public class TerminalProcess : Process { - private Window? window; + private Window window = null!; private readonly List history = []; private string currentInput = ""; private readonly List textLines = []; @@ -23,7 +23,7 @@ public class TerminalProcess : Process { } - internal override void Run(string[] args) + internal override void Start(string[] args) { window = WindowManager.CreateWindow(this, "Terminal", new Point(50, 50), new Size(400, 300)); @@ -32,19 +32,20 @@ public class TerminalProcess : Process window.Flush(); window.OnKeyEvent += HandleKey; + } - while (!StopRequested) + internal override void Tick() + { + if (window.Size != lastSize) { - if (window.Size != lastSize) - { - lastSize = window.Size; - UpdateDisplay(); - } - Thread.Sleep(50); + lastSize = window.Size; + UpdateDisplay(); } + } + internal override void Stop() + { window.OnKeyEvent -= HandleKey; - WindowManager.CloseWindow(window); } private void HandleKey(KeyEvent keyEvent) diff --git a/Processing/Process.cs b/Processing/Process.cs index b1a3301..4b1f56b 100644 --- a/Processing/Process.cs +++ b/Processing/Process.cs @@ -15,7 +15,11 @@ public abstract class Process(string name) public bool IsRunning => !StopRequested; - internal abstract void Run(string[] args); + internal virtual void Start(string[] args) { } + + internal abstract void Tick(); + + internal virtual void Stop() { } internal virtual void HandleInterProcessMessage(Message message) { diff --git a/Processing/ProcessManager.cs b/Processing/ProcessManager.cs index 4012e98..16de7f2 100644 --- a/Processing/ProcessManager.cs +++ b/Processing/ProcessManager.cs @@ -2,12 +2,13 @@ using RemSox.Logging; using RemSox.UI.GUI.Windows; using System.Collections.Concurrent; +using System.Diagnostics; namespace RemSox.Processing; public static class ProcessManager { - private static readonly ConcurrentDictionary processes = new(); + private static readonly ConcurrentDictionary processes = new(); private static readonly ConcurrentDictionary> processesByType = new(); @@ -47,40 +48,16 @@ public static class ProcessManager return set; }); - Thread thread = new(() => + try { - try - { - process.Run(args ?? []); - } - catch (Exception ex) - { - logger.Log($"Process {process.Name} (ID: {process.Id}) terminated with an exception: {ex}", LogSeverity.Error); - } - finally - { - _ = processes.TryRemove(id, out _); - - if (processesByType.TryGetValue(typeof(T), out ConcurrentHashSet? set)) - { - _ = set.TryRemove(id); - if (set.Count == 0) - { - _ = processesByType.TryRemove(typeof(T), out _); - } - } - - WindowManager.CloseWindowsForProcess(id); - - logger.Log($"Process {process.Name} (ID: {process.Id}) has stopped.", LogSeverity.Info); - - _ = processLoggers.TryRemove(id, out _); - } - }); - - _ = processes.TryAdd(id, (process, thread)); - thread.Start(); + process.Start(args ?? []); + } + catch (Exception ex) + { + logger.Log($"Process {process.Name} (ID: {process.Id}) terminated with an exception: {ex}", LogSeverity.Error); + } + _ = processes.TryAdd(id, (process, new ProcessMetrics())); logger.Log($"Spawned process {process.Name} of type {typeof(T).Name} with ID {id}.", LogSeverity.Info); return id; @@ -88,7 +65,7 @@ public static class ProcessManager public static void StopProcess(int processId, bool waitForExit = false) { - if (!processes.TryGetValue(processId, out (Process Process, Thread Thread) entry)) + if (!processes.TryGetValue(processId, out (Process Process, ProcessMetrics Metrics) entry)) { return; } @@ -99,21 +76,18 @@ public static class ProcessManager public static async Task StopProcessAndWaitAsync(int processId) { - if (!processes.TryGetValue(processId, out (Process Process, Thread Thread) entry)) + if (!processes.TryGetValue(processId, out (Process Process, ProcessMetrics Metrics) entry)) { return; } logger.Log($"Requesting stop of process {entry.Process.Name} (ID: {entry.Process.Id}).", LogSeverity.Info); entry.Process.RequestStop(); - - logger.Log($"Waiting for process {entry.Process.Name} (ID: {entry.Process.Id}) to stop.", LogSeverity.Info); - await Task.Run(entry.Thread.Join); } public static void StopAllProcesses() { - foreach ((Process Process, Thread Thread) entry in processes.Values) + foreach ((Process Process, ProcessMetrics Metrics) entry in processes.Values) { StopProcess(entry.Process.Id); } @@ -123,7 +97,7 @@ public static class ProcessManager public static Process? GetProcess(int processId) { - if (processes.TryGetValue(processId, out (Process Process, Thread Thread) entry)) + if (processes.TryGetValue(processId, out (Process Process, ProcessMetrics Metrics) entry)) { return entry.Process; } @@ -142,7 +116,7 @@ public static class ProcessManager { foreach (int processId in set) { - if (processes.TryGetValue(processId, out (Process Process, Thread Thread) entry) && entry.Process is T typedProcess) + if (processes.TryGetValue(processId, out (Process Process, ProcessMetrics Metrics) entry) && entry.Process is T typedProcess) { yield return typedProcess; } @@ -152,7 +126,7 @@ public static class ProcessManager public static IEnumerable GetAllProcesses() { - foreach ((Process Process, Thread Thread) entry in processes.Values) + foreach ((Process Process, ProcessMetrics Metrics) entry in processes.Values) { yield return entry.Process; } @@ -160,7 +134,7 @@ public static class ProcessManager public static bool TryGetProcess(int processId, out Process? process) { - if (processes.TryGetValue(processId, out (Process Process, Thread Thread) entry)) + if (processes.TryGetValue(processId, out (Process Process, ProcessMetrics Metrics) entry)) { process = entry.Process; return true; @@ -185,6 +159,58 @@ public static class ProcessManager return []; } + internal static void TickAllProcesses() + { + foreach ((Process Process, ProcessMetrics Metrics) entry in processes.Values) + { + if (entry.Process.IsRunning) + { + Stopwatch stopwatch = Stopwatch.StartNew(); + + try + { + entry.Process.Tick(); + } + catch (Exception ex) + { + logger.Log($"Process {entry.Process.Name} (ID: {entry.Process.Id}) threw an exception during Tick: {ex}", LogSeverity.Error); + } + + stopwatch.Stop(); + + int tickTimeMs = (int)stopwatch.ElapsedMilliseconds; + entry.Metrics.LastTickTimeMs = tickTimeMs; + entry.Metrics.AverageTickTimeMs = (entry.Metrics.AverageTickTimeMs * 7 + tickTimeMs) / 8; + } + else + { + CleanupProcess(entry.Process); + } + } + } + + private static void CleanupProcess(Process process) + { + process.Stop(); + + _ = processes.TryRemove(process.Id, out _); + + if (processesByType.TryGetValue(process.GetType(), out ConcurrentHashSet? set)) + { + _ = set.TryRemove(process.Id); + if (set.Count == 0) + { + _ = processesByType.TryRemove(process.GetType(), out _); + } + } + + WindowManager.CloseWindowsForProcess(process.Id); + + logger.Log($"Process {process.Name} (ID: {process.Id}) has stopped.", LogSeverity.Info); + + _ = processLoggers.TryRemove(process.Id, out _); + } + private static int GetNextProcessId() { return nextProcessId++; diff --git a/Processing/ProcessMetrics.cs b/Processing/ProcessMetrics.cs new file mode 100644 index 0000000..2caaf88 --- /dev/null +++ b/Processing/ProcessMetrics.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace RemSox.Processing +{ + public class ProcessMetrics + { + public int AverageTickTimeMs { get; set; } + + public int LastTickTimeMs { get; set; } + } +} \ No newline at end of file