From 97f35bccb3e22280d3e9ce5ef46523b4f5946caa Mon Sep 17 00:00:00 2001 From: Leonmmcoset Date: Sat, 4 Apr 2026 18:32:21 +0800 Subject: [PATCH] =?UTF-8?q?OOBE=20GUI=E7=89=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BuildTime.txt | 2 +- GitCommit.txt | 2 +- Gui/Gui.cs | 12 +- Gui/ShellComponents/Oobe.cs | 265 ++++++++++++++++++++++++++++++++++++ System/BootMenu.cs | 12 +- System/UserSystem.cs | 63 +++++++++ 6 files changed, 344 insertions(+), 12 deletions(-) create mode 100644 Gui/ShellComponents/Oobe.cs diff --git a/BuildTime.txt b/BuildTime.txt index b320e0f..00db74f 100644 --- a/BuildTime.txt +++ b/BuildTime.txt @@ -1 +1 @@ -2026-04-04 17:44:58 \ No newline at end of file +2026-04-04 18:04:06 \ No newline at end of file diff --git a/GitCommit.txt b/GitCommit.txt index 1d3f723..fa3a8de 100644 --- a/GitCommit.txt +++ b/GitCommit.txt @@ -1 +1 @@ -c189be5 \ No newline at end of file +1341f10 \ No newline at end of file diff --git a/Gui/Gui.cs b/Gui/Gui.cs index 89d52ee..ca7d32f 100644 --- a/Gui/Gui.cs +++ b/Gui/Gui.cs @@ -212,8 +212,16 @@ namespace CMLeonOS.Gui Logger.Logger.Instance.Info("Gui", "Memory management service started"); - Console.WriteLine("Starting lock screen..."); - ProcessManager.AddProcess(windowManager, new ShellComponents.Lock()).Start(); + if (!Kernel.userSystem.HasUsers) + { + Console.WriteLine("Starting OOBE..."); + ProcessManager.AddProcess(windowManager, new ShellComponents.Oobe()).Start(); + } + else + { + Console.WriteLine("Starting lock screen..."); + ProcessManager.AddProcess(windowManager, new ShellComponents.Lock()).Start(); + } guiRunning = true; diff --git a/Gui/ShellComponents/Oobe.cs b/Gui/ShellComponents/Oobe.cs new file mode 100644 index 0000000..d7fb27b --- /dev/null +++ b/Gui/ShellComponents/Oobe.cs @@ -0,0 +1,265 @@ +using CMLeonOS; +using CMLeonOS.Gui.UILib; +using System.Collections.Generic; +using System.Drawing; +using Sys = Cosmos.System; + +namespace CMLeonOS.Gui.ShellComponents +{ + internal class Oobe : Process + { + internal Oobe() : base("OOBE", ProcessType.Application) { } + + private readonly WindowManager wm = ProcessManager.GetProcess(); + private AppWindow window; + private readonly List stepWindows = new List(); + + private int step = 0; + private bool acceptedTerms = false; + + private TextBox usernameBox; + private TextBox passwordBox; + private TextBox confirmBox; + private TextBox hostnameBox; + private TextBlock statusText; + + private Button backButton; + private Button nextButton; + + private void AddStepWindow(Window w) + { + stepWindows.Add(w); + wm.AddWindow(w); + } + + private void ClearStepWindows() + { + for (int i = 0; i < stepWindows.Count; i++) + { + wm.RemoveWindow(stepWindows[i]); + } + stepWindows.Clear(); + } + + private void ShowStatus(string text, Color color) + { + statusText.Text = text; + statusText.Foreground = color; + statusText.Render(); + } + + private void RenderStep() + { + ClearStepWindows(); + + window.Clear(UITheme.Surface); + window.DrawRectangle(0, 0, window.Width, window.Height, UITheme.SurfaceBorder); + window.DrawString("CMLeonOS Setup", UITheme.TextPrimary, 16, 12); + window.DrawString($"Step {step + 1}/4", UITheme.TextSecondary, window.Width - 92, 12); + + int contentX = 16; + int contentY = 44; + int contentW = window.Width - 32; + int contentH = window.Height - 108; + + Window content = new Window(this, window, contentX, contentY, contentW, contentH); + content.Clear(Color.FromArgb(246, 249, 253)); + content.DrawRectangle(0, 0, content.Width, content.Height, UITheme.SurfaceBorder); + AddStepWindow(content); + + switch (step) + { + case 0: + content.DrawString("Welcome", UITheme.TextPrimary, 12, 12); + content.DrawString("Let's configure your first administrator account.", UITheme.TextSecondary, 12, 34); + content.DrawString("Press Next to continue.", UITheme.TextSecondary, 12, 56); + backButton.Text = "Cancel"; + nextButton.Text = "Next"; + break; + + case 1: + content.DrawString("Terms and Conditions", UITheme.TextPrimary, 12, 12); + content.DrawString("Please read and accept to continue.", UITheme.TextSecondary, 12, 34); + + TextBox terms = new TextBox(content, 12, 58, content.Width - 24, content.Height - 112); + terms.ReadOnly = true; + terms.MultiLine = true; + terms.Text = + "1. This system is provided as-is without warranty.\n" + + "2. You are responsible for your data and backups.\n" + + "3. Unauthorized access attempts may be logged.\n" + + "4. Administrators can access local system data.\n" + + "5. Data is stored locally on this device.\n" + + "6. Updates may change behavior and compatibility.\n" + + "7. Use implies acceptance of these terms."; + AddStepWindow(terms); + + Switch accept = new Switch(content, 12, content.Height - 44, content.Width - 24, 18); + accept.Text = "I accept these terms"; + accept.Checked = acceptedTerms; + accept.CheckBoxChanged = v => acceptedTerms = v; + AddStepWindow(accept); + + backButton.Text = "Back"; + nextButton.Text = "Next"; + break; + + case 2: + content.DrawString("Administrator Account", UITheme.TextPrimary, 12, 12); + content.DrawString("Create your first admin user.", UITheme.TextSecondary, 12, 34); + + TextBlock userLabel = new TextBlock(content, 12, 66, 94, 20); + userLabel.Text = "Username"; + userLabel.Foreground = UITheme.TextSecondary; + userLabel.Background = Color.FromArgb(246, 249, 253); + AddStepWindow(userLabel); + + usernameBox = new TextBox(content, 110, 64, content.Width - 122, 24); + AddStepWindow(usernameBox); + + TextBlock passLabel = new TextBlock(content, 12, 100, 94, 20); + passLabel.Text = "Password"; + passLabel.Foreground = UITheme.TextSecondary; + passLabel.Background = Color.FromArgb(246, 249, 253); + AddStepWindow(passLabel); + + passwordBox = new TextBox(content, 110, 98, content.Width - 122, 24); + passwordBox.Shield = true; + AddStepWindow(passwordBox); + + TextBlock confirmLabel = new TextBlock(content, 12, 134, 94, 20); + confirmLabel.Text = "Confirm"; + confirmLabel.Foreground = UITheme.TextSecondary; + confirmLabel.Background = Color.FromArgb(246, 249, 253); + AddStepWindow(confirmLabel); + + confirmBox = new TextBox(content, 110, 132, content.Width - 122, 24); + confirmBox.Shield = true; + AddStepWindow(confirmBox); + + content.DrawString("Username cannot include: < > : \" | ? * / \\ or space", UITheme.TextSecondary, 12, 168); + + backButton.Text = "Back"; + nextButton.Text = "Next"; + break; + + case 3: + content.DrawString("Device Name", UITheme.TextPrimary, 12, 12); + content.DrawString("Set a hostname for this device.", UITheme.TextSecondary, 12, 34); + + TextBlock hostLabel = new TextBlock(content, 12, 74, 94, 20); + hostLabel.Text = "Hostname"; + hostLabel.Foreground = UITheme.TextSecondary; + hostLabel.Background = Color.FromArgb(246, 249, 253); + AddStepWindow(hostLabel); + + hostnameBox = new TextBox(content, 110, 72, content.Width - 122, 24); + AddStepWindow(hostnameBox); + + content.DrawString("Example: CMLEON-PC", UITheme.TextSecondary, 12, 108); + + backButton.Text = "Back"; + nextButton.Text = "Finish"; + break; + } + + wm.Update(window); + } + + private void BackClicked(int x, int y) + { + if (step == 0) + { + Sys.Power.Reboot(); + return; + } + + step--; + ShowStatus(string.Empty, UITheme.TextSecondary); + RenderStep(); + } + + private void NextClicked(int x, int y) + { + if (step == 1 && !acceptedTerms) + { + ShowStatus("Please accept terms to continue.", UITheme.Warning); + return; + } + + if (step == 2) + { + if (string.IsNullOrWhiteSpace(usernameBox.Text)) + { + ShowStatus("Username cannot be empty.", UITheme.Warning); + return; + } + if (string.IsNullOrWhiteSpace(passwordBox.Text)) + { + ShowStatus("Password cannot be empty.", UITheme.Warning); + return; + } + if (passwordBox.Text != confirmBox.Text) + { + ShowStatus("Passwords do not match.", UITheme.Warning); + return; + } + } + + if (step == 3) + { + string error; + if (!Kernel.userSystem.CreateInitialAdminFromOobe(usernameBox.Text, passwordBox.Text, hostnameBox.Text, out error)) + { + ShowStatus("Setup failed: " + error, Color.FromArgb(198, 72, 72)); + return; + } + + ShowStatus("Setup complete. Rebooting...", UITheme.Success); + Sys.Power.Reboot(); + return; + } + + step++; + ShowStatus(string.Empty, UITheme.TextSecondary); + RenderStep(); + } + + public override void Start() + { + base.Start(); + + int width = 760; + int height = 500; + window = new AppWindow(this, (int)(wm.ScreenWidth / 2 - width / 2), (int)(wm.ScreenHeight / 2 - height / 2), width, height); + window.Title = "CMLeonOS OOBE"; + window.Icon = AppManager.DefaultAppIcon; + window.CanClose = false; + window.CanMove = false; + window.CanResize = false; + wm.AddWindow(window); + + backButton = new Button(window, 16, window.Height - 42, 100, 26); + backButton.Text = "Cancel"; + backButton.OnClick = BackClicked; + wm.AddWindow(backButton); + + nextButton = new Button(window, window.Width - 116, window.Height - 42, 100, 26); + nextButton.Text = "Next"; + nextButton.OnClick = NextClicked; + wm.AddWindow(nextButton); + + statusText = new TextBlock(window, 126, window.Height - 40, window.Width - 252, 20); + statusText.Text = string.Empty; + statusText.Background = UITheme.Surface; + statusText.Foreground = UITheme.TextSecondary; + wm.AddWindow(statusText); + + RenderStep(); + } + + public override void Run() + { + } + } +} diff --git a/System/BootMenu.cs b/System/BootMenu.cs index 791c5d3..74e776e 100644 --- a/System/BootMenu.cs +++ b/System/BootMenu.cs @@ -96,10 +96,7 @@ namespace CMLeonOS Console.BackgroundColor = ConsoleColor.Black; Console.ForegroundColor = ConsoleColor.White; - bool userDatExists = UserDatExists(); - string[] options = userDatExists - ? new[] { "CMLeonOS Shell", "CMLeonOS Desktop", "Reboot", "Shutdown" } - : new[] { "CMLeonOS Shell", "Reboot", "Shutdown" }; + string[] options = new[] { "CMLeonOS Shell", "CMLeonOS Desktop", "Reboot", "Shutdown" }; int width = Console.WindowWidth; int height = Console.WindowHeight; @@ -152,14 +149,13 @@ namespace CMLeonOS Console.CursorVisible = true; - bool userDatExists = UserDatExists(); int optionIndex = 0; if (selIdx == optionIndex++) { return BootMenuAction.NormalBoot; } - if (userDatExists && selIdx == optionIndex++) + if (selIdx == optionIndex++) { return BootMenuAction.GuiBoot; } @@ -179,7 +175,7 @@ namespace CMLeonOS public static BootMenuAction Show() { - if (Settings.SettingsManager.SkipToGui && UserDatExists()) + if (Settings.SettingsManager.SkipToGui) { return BootMenuAction.GuiBoot; } @@ -235,7 +231,7 @@ namespace CMLeonOS } } - int maxOptionIndex = UserDatExists() ? 3 : 2; + int maxOptionIndex = 3; if (selIdx < 0) { diff --git a/System/UserSystem.cs b/System/UserSystem.cs index ed3cb9f..1f8dd5e 100644 --- a/System/UserSystem.cs +++ b/System/UserSystem.cs @@ -827,6 +827,69 @@ namespace CMLeonOS } } + public bool CreateInitialAdminFromOobe(string username, string password, string hostname, out string error) + { + error = string.Empty; + + username = (username ?? string.Empty).Trim(); + password = (password ?? string.Empty).Trim(); + hostname = (hostname ?? string.Empty).Trim(); + + if (string.IsNullOrWhiteSpace(username)) + { + error = "Username cannot be empty."; + return false; + } + + if (ContainsInvalidChars(username)) + { + error = "Username contains invalid characters."; + return false; + } + + if (string.IsNullOrWhiteSpace(password)) + { + error = "Password cannot be empty."; + return false; + } + + if (string.IsNullOrWhiteSpace(hostname)) + { + error = "Hostname cannot be empty."; + return false; + } + + foreach (User user in users) + { + if (user.Username.Equals(username, StringComparison.OrdinalIgnoreCase)) + { + error = "Username already exists."; + return false; + } + } + + try + { + User admin = new User + { + Username = username, + Password = password, + IsAdmin = true, + Hostname = hostname + }; + + users.Add(admin); + SaveUsers(); + CreateUserFolder(username); + return true; + } + catch (Exception ex) + { + error = ex.Message; + return false; + } + } + public bool DeleteUser(string username) { Console.WriteLine("====================================");