diff --git a/BuildTime.txt b/BuildTime.txt index 8db768d..55bd7c8 100644 --- a/BuildTime.txt +++ b/BuildTime.txt @@ -1 +1 @@ -2026-04-04 18:52:53 \ No newline at end of file +2026-04-04 19:40:48 \ No newline at end of file diff --git a/GitCommit.txt b/GitCommit.txt index 723ab8c..3945076 100644 --- a/GitCommit.txt +++ b/GitCommit.txt @@ -1 +1 @@ -97f35bc \ No newline at end of file +7b97977 \ No newline at end of file diff --git a/Gui/AppManager.cs b/Gui/AppManager.cs index 6ff11c0..6bf880d 100644 --- a/Gui/AppManager.cs +++ b/Gui/AppManager.cs @@ -149,6 +149,7 @@ namespace CMLeonOS.Gui RegisterApp(new AppMetadata("Sheet Editor", () => { return new SheetEditor(); }, Icons.Icon_Default, Color.FromArgb(25, 25, 25))); RegisterApp(new AppMetadata("Tetris", () => { return new Tetris(); }, Icons.Icon_Default, Color.FromArgb(25, 25, 25))); RegisterApp(new AppMetadata("Ping Pong", () => { return new PingPong(); }, Icons.Icon_Default, Color.FromArgb(25, 25, 25))); + RegisterApp(new AppMetadata("Terminal", () => { return new TerminalApp(); }, Icons.Icon_Default, Color.FromArgb(25, 25, 25))); Logger.Logger.Instance.Info("AppManager", $"{AppMetadatas.Count} apps were registered."); diff --git a/Gui/Apps/TerminalApp.cs b/Gui/Apps/TerminalApp.cs new file mode 100644 index 0000000..12d3873 --- /dev/null +++ b/Gui/Apps/TerminalApp.cs @@ -0,0 +1,271 @@ +using Cosmos.System; +using CMLeonOS; +using System; +using System.Collections.Generic; +using System.Drawing; +using System.IO; + +namespace CMLeonOS.Gui.Apps +{ + internal class TerminalApp : Process + { + internal TerminalApp() : base("Terminal", ProcessType.Application) { } + + private const int Cols = 80; + private const int Rows = 25; + private const int CharW = 8; + private const int CharH = 16; + private const int Padding = 8; + + private readonly WindowManager wm = ProcessManager.GetProcess(); + + private UILib.AppWindow window; + private Window surface; + private UILib.TextBox inputBox; + + private readonly List lines = new List(); + private string cwd = @"0:\"; + private bool dirty = true; + + private int ContentWidth => Cols * CharW; + private int ContentHeight => Rows * CharH; + + private void PushLine(string text) + { + text = text ?? string.Empty; + if (text.Length <= Cols) + { + lines.Add(text); + return; + } + + int i = 0; + while (i < text.Length) + { + int take = Math.Min(Cols, text.Length - i); + lines.Add(text.Substring(i, take)); + i += take; + } + } + + private void PrintPromptLine() + { + string user = UserSystem.CurrentLoggedInUser?.Username ?? "user"; + PushLine($"{user}@CMLeonOS {cwd}"); + } + + private void ClearScreen() + { + lines.Clear(); + PushLine("CMLeonOS GUI Terminal"); + PushLine("Type 'help' for commands."); + } + + private void ExecuteCommand(string raw) + { + string commandLine = (raw ?? string.Empty).Trim(); + if (commandLine.Length == 0) + { + return; + } + + string[] parts = commandLine.Split(new char[] { ' ' }, 2, StringSplitOptions.RemoveEmptyEntries); + string cmd = parts[0].ToLower(); + string args = parts.Length > 1 ? parts[1].Trim() : string.Empty; + + try + { + switch (cmd) + { + case "help": + PushLine("help clear echo time date whoami ver pwd cd ls"); + break; + case "clear": + case "cls": + ClearScreen(); + break; + case "echo": + PushLine(args); + break; + case "time": + PushLine(DateTime.Now.ToString("HH:mm:ss")); + break; + case "date": + PushLine(DateTime.Now.ToString("yyyy-MM-dd")); + break; + case "whoami": + PushLine(UserSystem.CurrentLoggedInUser?.Username ?? "Not logged in"); + break; + case "ver": + case "version": + PushLine(Kernel.Version); + break; + case "pwd": + PushLine(cwd); + break; + case "cd": + ChangeDirectory(args); + break; + case "ls": + ListDirectory(args); + break; + default: + PushLine("Unknown command: " + cmd); + break; + } + } + catch (Exception ex) + { + PushLine("Error: " + ex.Message); + } + } + + private void ChangeDirectory(string path) + { + if (string.IsNullOrWhiteSpace(path)) + { + PushLine(cwd); + return; + } + + string target = path.Trim(); + string full; + + if (target == ".") + { + full = cwd; + } + else if (target == "..") + { + DirectoryInfo parent = Directory.GetParent(cwd); + full = parent == null ? cwd : parent.FullName; + } + else if (target.StartsWith(@"0:\") || target.StartsWith(@"1:\")) + { + full = target; + } + else + { + full = Path.Combine(cwd, target); + } + + if (!Directory.Exists(full)) + { + PushLine("Directory not found: " + full); + return; + } + + cwd = full; + PushLine(cwd); + } + + private void ListDirectory(string path) + { + string target = string.IsNullOrWhiteSpace(path) ? cwd : path.Trim(); + string full; + + if (target.StartsWith(@"0:\") || target.StartsWith(@"1:\")) + { + full = target; + } + else + { + full = Path.Combine(cwd, target); + } + + if (!Directory.Exists(full)) + { + PushLine("Directory not found: " + full); + return; + } + + string[] dirs = Directory.GetDirectories(full); + string[] files = Directory.GetFiles(full); + + PushLine("[" + full + "]"); + for (int i = 0; i < dirs.Length; i++) + { + PushLine(" " + Path.GetFileName(dirs[i])); + } + for (int i = 0; i < files.Length; i++) + { + PushLine(" " + Path.GetFileName(files[i])); + } + } + + private void Render() + { + surface.Clear(Color.Black); + + int visibleHistoryRows = Rows - 1; + int start = Math.Max(0, lines.Count - visibleHistoryRows); + for (int i = 0; i < visibleHistoryRows; i++) + { + int idx = start + i; + if (idx >= lines.Count) + { + break; + } + + string text = lines[idx]; + if (text.Length > Cols) + { + text = text.Substring(0, Cols); + } + surface.DrawString(text, Color.White, 0, i * CharH); + } + + wm.Update(surface); + dirty = false; + } + + public override void Start() + { + base.Start(); + + int width = ContentWidth + (Padding * 2); + int inputHeight = 24; + int height = ContentHeight + (Padding * 3) + inputHeight + 24; + window = new UILib.AppWindow(this, 120, 60, width, height); + window.Title = "Terminal 80x25"; + window.Icon = AppManager.DefaultAppIcon; + window.CanResize = false; + window.Closing = TryStop; + wm.AddWindow(window); + + surface = new Window(this, window, Padding, Padding, ContentWidth, ContentHeight); + wm.AddWindow(surface); + + inputBox = new UILib.TextBox(window, Padding, Padding + ContentHeight + Padding, ContentWidth, inputHeight); + inputBox.PlaceholderText = "Enter command and press Enter..."; + inputBox.Submitted = () => + { + string command = (inputBox.Text ?? string.Empty).Trim(); + if (command.Length > 0) + { + PushLine("> " + command); + ExecuteCommand(command); + } + inputBox.Text = string.Empty; + dirty = true; + }; + wm.AddWindow(inputBox); + + wm.BringToFrontAndFocus(window); + wm.SetFocus(inputBox); + + ClearScreen(); + PrintPromptLine(); + dirty = true; + Render(); + } + + public override void Run() + { + if (dirty) + { + Render(); + } + } + } +}