using System.Text; using XFESystem.Console; using XFESystem.Core; using XFESystem.Desktop; using XFESystem.ManagedRuntime; using XFESystem.NativeKernel; using XFESystem.NativeRuntime.Application; namespace XFESystem.NativeRuntime.ConsoleApplication; internal static class Program { public const string Version = "0.3.0-dev.4"; private const string ManagedDemoPath = "X:\\Programs\\ManagedHello\\ManagedHello.dll"; private static int Main() { NativePlatform.Write("XFE_INDEPENDENT_APPLICATION_ENTER\nXFE_SYSTEM_EDITION|Console\nXFE_INDEPENDENT_EDITION|Console\n"); try { global::System.Console.SetOut(new NativeDiagnosticWriter()); global::System.Console.SetError(new NativeDiagnosticWriter()); NativePlatform.RequireClock(); RuntimeSelfCheck.Run(); var renderer = new ClassicConsoleRenderer(new ConsoleMemoryFramebuffer(NativePlatform.ReadFramebuffer(), NativePlatform.CompleteBoot)); var workspace = CreateWorkspace(); workspace.CreateDirectory("X:\\System\\Diagnostics"); string gpuReport = NativeGpuDiagnostics.ReadReport(out bool hasRtx5090); workspace.WriteText("X:\\System\\Diagnostics\\Gpu.txt", gpuReport); workspace.WriteText("X:\\System\\Diagnostics\\Usb.txt", NativeUsbDiagnostics.ReadReport()); var shell = new TerminalShell(workspace, Version, renderer.Columns, Math.Clamp(renderer.Rows * 8, 128, 512)); shell.ProgramOutput.WriteLine("纯控制台版 / Console edition — C# independent kernel"); shell.ProgramOutput.WriteLine(workspace.IsPersistent ? "U 盘工作区,文件和设置写入物理介质。\r\n" : "RAM 会话:启动 U 盘存储不可用,关机后文件不会保留。\r\n"); if (hasRtx5090) shell.ProgramOutput.WriteLine(gpuReport); renderer.Render(shell); NativePlatform.Write("XFE_INDEPENDENT_CONSOLE_FIRST_FRAME|Width|" + renderer.Width + "|Height|" + renderer.Height + "|ScalePercent|" + renderer.ScalePercent + "|Presented|" + renderer.FramesPresented + "\n"); uint capabilities = NativePlatform.InputCapabilities(); NativePlatform.Write("XFE_INDEPENDENT_INPUT|Connected|" + ((capabilities & 1) != 0 ? 1 : 0) + "|Keyboard|" + (capabilities & 1) + "|Mouse|0|Wheel|" + ((capabilities >> 2) & 1) + "|Backend|PS2+xHCI|UsbHid|" + NativeUsbDiagnostics.KeyboardMouse + "\n"); NativePlatform.Write("XFE_INDEPENDENT_CAPABILITIES|RAMOnly|"+(workspace.IsPersistent?0:1)+"|NativeNetwork|0|Gpu|0|Cuda|0|Desktop|0|Browser|0|ConsoleIL|1\n"); Report(shell, renderer); Pump(shell, workspace, renderer); return 0; } catch (Exception error) { NativePlatform.Write("XFE_INDEPENDENT_APPLICATION_FAILED|" + error.Message + "\n"); NativePlatform.Halt("Managed console failed: " + error.Message); return 1; } } private static IWorkspaceBinaryStorage CreateWorkspace() { var workspace = NativeWorkspace.Open(); string readme=SystemPaths.Combine(SystemPaths.Documents,"Readme.txt"); if(!workspace.Exists(readme))workspace.WriteText(readme, """ XFESystem 纯控制台版 / Console edition 自研 C# 独立内核,官方 Microsoft .NET 10 NativeAOT。 参考以前 Rust 控制台的命令工作台、字体和配色。 help 查看命令;上下箭头查看历史;左右/Home/End 编辑;Ctrl+C 取消。 PageUp/PageDown 或鼠标滚轮回看输出;输入命令后返回最新输出。 open 文本文件会在控制台显示;本版没有桌面、浏览器和图形编辑器。 run X:\Programs\ManagedHello\ManagedHello.dll 运行 ConsoleIL 示例。 U 盘可用时文件和设置持久保存;存储不可用时明确回退到 RAM 会话。 X:\UsbFiles 映射启动 U 盘的 UserFiles 文件夹,可读取其中的 .NET 控制台程序。 """); workspace.CreateDirectory("X:\\Programs\\ManagedHello"); using var sample = typeof(Program).Assembly.GetManifestResourceStream("ApplicationSamples/ManagedHello.dll") ?? throw new InvalidOperationException("The embedded ConsoleIL sample is missing."); if (sample.Length > ManagedRuntimeCapabilities.MaxImageBytes) throw new InvalidDataException("Managed sample exceeds its image budget."); byte[] image = new byte[checked((int)sample.Length)]; sample.ReadExactly(image); if(!workspace.Exists(ManagedDemoPath))workspace.WriteBytes(ManagedDemoPath, image); string samplePath=SystemPaths.Combine(SystemPaths.Documents,"ManagedHello.dll"); if(!workspace.Exists(samplePath))workspace.WriteBytes(samplePath,image); return workspace; } private static void Pump(TerminalShell shell, IWorkspaceBinaryStorage workspace, ClassicConsoleRenderer renderer) { long previous = NativePlatform.Milliseconds(), nextBlink = previous + 500, nextUsb = previous + 2000; bool cursor = true, dirty = false; int scrollRows = 0; for (;;) { long now = NativePlatform.Milliseconds(); if (now < previous) throw new InvalidOperationException("The native monotonic clock moved backward."); previous = now; if (now >= nextUsb) { nextUsb = now + 2000; workspace.WriteText("X:\\System\\Diagnostics\\Usb.txt", NativeUsbDiagnostics.ReadReport()); } for (int count = 0; count < 256 && NativePlatform.ReadInput(out var report); count++) { if (report.Kind == Ps2Decoder.Pointer && report.Wheel != 0) { scrollRows = Math.Clamp(scrollRows + report.Wheel * 3, 0, 512); dirty = true; continue; } if (report.Kind == Ps2Decoder.KeyDown && report.Unicode == 0 && report.KeyCode is 0x49 or 0x149 or 0x51 or 0x151) { scrollRows = Math.Clamp(scrollRows + (report.KeyCode is 0x49 or 0x149 ? 1 : -1) * Math.Max(1, renderer.Rows - 2), 0, 512); dirty = true; continue; } if (ConsoleKeyboard.Translate(report) is not { } key) continue; scrollRows = 0; cursor = true; nextBlink = now + 500; dirty = true; if (shell.HandleKey(key.Key, key.Text) is not { } result) continue; if (result.OpenPath is { } opened) { // Console files stay in this text workspace; there is no hidden GUI launch path. try { shell.ProgramOutput.WriteLine(workspace.ReadText(opened).Replace("\r\n", "\n", StringComparison.Ordinal).Replace("\n", "\r\n", StringComparison.Ordinal)); } catch (IOException error) { shell.ProgramOutput.WriteLine("Open failed: " + error.Message); } } if (result.ManagedProgramPath is { } path) { renderer.Render(shell, busy: true); Report(shell, renderer, busy: true); ExecuteManaged(workspace, shell.ProgramOutput, path); renderer.Render(shell); Report(shell, renderer); NativePlatform.Write("XFE_INDEPENDENT_CONSOLE_PRESENTED|Path|" + path + "|Frames|" + renderer.FramesPresented + "\n"); } if (result.CloseRequested) shell.ProgramOutput.WriteLine("这是系统控制台会话。可使用机器的关机或重启控制结束会话。"); } if (now >= nextBlink) { cursor = !cursor; nextBlink = now + 500; dirty = true; } if (dirty) { renderer.Render(shell, cursorVisible: cursor, scrollRows: scrollRows); Report(shell, renderer); dirty = false; } Thread.Sleep(1); } } private static void ExecuteManaged(IWorkspaceBinaryStorage workspace, TerminalTextWriter output, string path) { try { var result = new ManagedApplicationRunner().Execute(workspace.ReadBytes(path), new TerminalManagedConsole(output)); output.Complete(); output.WriteLine("Managed ConsoleIL exit: " + result.ExitCode); string text = string.Concat(output.Snapshot().Cells.Select(cell => cell.Text)); if (result.ExitCode == 0 && text.Contains("XFE_MANAGED_APP_OK", StringComparison.Ordinal) && text.Contains("XFE_MANAGED_XFECONSOLE_OK", StringComparison.Ordinal)) NativePlatform.Write("XFE_INDEPENDENT_CONSOLE_IL_OK actual_embedded_net10_PE bounded_interpreter\n"); } catch (Exception error) when (error is not OutOfMemoryException and not StackOverflowException) { output.Complete(); output.WriteLine("Managed program failed: " + error.Message); } } private static string lastState = ""; private static void Report(TerminalShell shell, ClassicConsoleRenderer renderer, bool busy = false) { var snapshot = shell.ReadOutput(); var text = new StringBuilder(); for (int row = Math.Max(0, snapshot.CursorRow - 63); row <= Math.Min(snapshot.CursorRow, snapshot.Rows - 1); row++) { var line = new StringBuilder(); for (int column = 0; column < snapshot.Columns; column++) { var cell = snapshot.GetCell(column, row); if (!cell.IsContinuation) line.Append(cell.Text); } text.AppendLine(line.ToString().TrimEnd()); } string json = "{\"Text\":" + Quote(text.ToString()) + ",\"Input\":" + Quote(shell.Input) + ",\"Prompt\":" + Quote(shell.Prompt) + ",\"Busy\":" + (busy ? "true" : "false") + ",\"Version\":" + Quote(shell.Version) + ",\"LastCommand\":" + Quote(shell.History.LastOrDefault() ?? "") + ",\"Caret\":" + shell.Caret + ",\"Columns\":" + renderer.Columns + ",\"Rows\":" + renderer.Rows + "}"; if (json == lastState) return; lastState = json; NativePlatform.Write("XFE_CONSOLE_SNAPSHOT_BEGIN\n" + json + "\nXFE_CONSOLE_SNAPSHOT_END\n"); } private static string Quote(string value) { var result = new StringBuilder("\""); foreach (char character in value) switch (character) { case '"': result.Append("\\\""); break; case '\\': result.Append("\\\\"); break; case '\n': result.Append("\\n"); break; case '\r': result.Append("\\r"); break; case '\t': result.Append("\\t"); break; default: if (character < 32) result.Append("\\u" + ((int)character).ToString("x4")); else result.Append(character); break; } return result.Append('"').ToString(); } }