diff --git a/LanMountainDesktop/App.axaml.cs b/LanMountainDesktop/App.axaml.cs index e0cae3d..f00156c 100644 --- a/LanMountainDesktop/App.axaml.cs +++ b/LanMountainDesktop/App.axaml.cs @@ -101,42 +101,12 @@ public partial class App : Application return; } - if (TryStartCurrentProcess()) + if (AppRestartService.TryRestartCurrentProcess()) { desktop.Shutdown(); } } - private static bool TryStartCurrentProcess() - { - try - { - var args = Environment.GetCommandLineArgs(); - if (args.Length == 0 || string.IsNullOrWhiteSpace(args[0])) - { - return false; - } - - var startInfo = new ProcessStartInfo - { - FileName = args[0], - UseShellExecute = false - }; - - for (var i = 1; i < args.Length; i++) - { - startInfo.ArgumentList.Add(args[i]); - } - - Process.Start(startInfo); - return true; - } - catch - { - return false; - } - } - private void DisableAvaloniaDataAnnotationValidation() { // Get an array of plugins to remove diff --git a/LanMountainDesktop/Localization/en-US.json b/LanMountainDesktop/Localization/en-US.json index eb3eb74..c1a6afc 100644 --- a/LanMountainDesktop/Localization/en-US.json +++ b/LanMountainDesktop/Localization/en-US.json @@ -244,6 +244,11 @@ "settings.about.render_mode.angle_egl": "angleEgl", "settings.about.render_mode.wgl": "WGL", "settings.about.render_mode.vulkan": "Vulkan", + "settings.about.render_mode.unknown": "Unknown", + "settings.about.render_mode.current_label": "Current actual backend", + "settings.about.render_mode.current_format": "Current backend: {0}", + "settings.about.render_mode.impl_format": "Runtime implementation: {0}", + "settings.about.render_mode.impl_unavailable": "Runtime implementation details are unavailable.", "settings.footer": "LanMountainDesktop Settings", "filepicker.title": "Select wallpaper", "filepicker.image_files": "Image files", diff --git a/LanMountainDesktop/Localization/zh-CN.json b/LanMountainDesktop/Localization/zh-CN.json index a940684..c2e96a0 100644 --- a/LanMountainDesktop/Localization/zh-CN.json +++ b/LanMountainDesktop/Localization/zh-CN.json @@ -244,6 +244,11 @@ "settings.about.render_mode.angle_egl": "angleEgl", "settings.about.render_mode.wgl": "WGL", "settings.about.render_mode.vulkan": "Vulkan", + "settings.about.render_mode.unknown": "未知", + "settings.about.render_mode.current_label": "当前实际渲染后端", + "settings.about.render_mode.current_format": "当前后端:{0}", + "settings.about.render_mode.impl_format": "运行时实现:{0}", + "settings.about.render_mode.impl_unavailable": "当前无法获取运行时实现信息。", "settings.footer": "LanMountainDesktop 设置", "filepicker.title": "选择壁纸", "filepicker.image_files": "图片文件", diff --git a/LanMountainDesktop/Services/AppRenderBackendDiagnostics.cs b/LanMountainDesktop/Services/AppRenderBackendDiagnostics.cs new file mode 100644 index 0000000..50924c2 --- /dev/null +++ b/LanMountainDesktop/Services/AppRenderBackendDiagnostics.cs @@ -0,0 +1,73 @@ +using System; +using System.Reflection; +using Avalonia; +using Avalonia.Platform; + +namespace LanMountainDesktop.Services; + +public readonly record struct AppRenderBackendInfo( + string ActualBackend, + string? ImplementationTypeName); + +public static class AppRenderBackendDiagnostics +{ + public const string Unknown = "Unknown"; + + public static AppRenderBackendInfo Detect() + { + var platformGraphics = GetPlatformGraphics(); + var implementationTypeName = platformGraphics?.GetType().FullName; + var actualBackend = DetectBackendFromImplementationType(implementationTypeName, platformGraphics is null); + + return new AppRenderBackendInfo(actualBackend, implementationTypeName); + } + + private static object? GetPlatformGraphics() + { + var currentResolver = typeof(AvaloniaLocator) + .GetProperty("Current", BindingFlags.Public | BindingFlags.Static) + ?.GetValue(null); + + var getServiceMethod = currentResolver? + .GetType() + .GetMethod( + "GetService", + BindingFlags.Public | BindingFlags.Instance, + binder: null, + types: [typeof(Type)], + modifiers: null); + + return getServiceMethod?.Invoke(currentResolver, [typeof(IPlatformGraphics)]); + } + + private static string DetectBackendFromImplementationType(string? implementationTypeName, bool isSoftwareFallback) + { + if (isSoftwareFallback) + { + return AppRenderingModeHelper.Software; + } + + if (string.IsNullOrWhiteSpace(implementationTypeName)) + { + return Unknown; + } + + if (implementationTypeName.Contains("Vulkan", StringComparison.OrdinalIgnoreCase)) + { + return AppRenderingModeHelper.Vulkan; + } + + if (implementationTypeName.Contains("Wgl", StringComparison.OrdinalIgnoreCase)) + { + return AppRenderingModeHelper.Wgl; + } + + if (implementationTypeName.Contains("Angle", StringComparison.OrdinalIgnoreCase) || + implementationTypeName.Contains("Egl", StringComparison.OrdinalIgnoreCase)) + { + return AppRenderingModeHelper.AngleEgl; + } + + return Unknown; + } +} diff --git a/LanMountainDesktop/Services/AppRestartService.cs b/LanMountainDesktop/Services/AppRestartService.cs new file mode 100644 index 0000000..3ae0b01 --- /dev/null +++ b/LanMountainDesktop/Services/AppRestartService.cs @@ -0,0 +1,154 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Reflection; + +namespace LanMountainDesktop.Services; + +public static class AppRestartService +{ + public static bool TryRestartCurrentProcess() + { + try + { + var startInfo = CreateRestartStartInfo(); + if (startInfo is null) + { + Debug.WriteLine("[AppRestart] Failed to resolve restart start info."); + return false; + } + + Process.Start(startInfo); + return true; + } + catch (Exception ex) + { + Debug.WriteLine($"[AppRestart] Failed to restart app: {ex}"); + return false; + } + } + + public static ProcessStartInfo? CreateRestartStartInfo( + string[]? commandLineArgs = null, + string? processPath = null, + string? entryAssemblyLocation = null) + { + var args = commandLineArgs ?? Environment.GetCommandLineArgs(); + var resolvedProcessPath = NormalizeExistingPath(processPath ?? Environment.ProcessPath); + var resolvedEntryAssemblyPath = NormalizeExistingPath( + entryAssemblyLocation ?? Assembly.GetEntryAssembly()?.Location); + + if (IsDotnetHost(resolvedProcessPath)) + { + return CreateDotnetStartInfo( + resolvedProcessPath!, + resolvedEntryAssemblyPath, + args); + } + + if (!string.IsNullOrWhiteSpace(resolvedProcessPath)) + { + return CreateExecutableStartInfo( + resolvedProcessPath, + resolvedEntryAssemblyPath, + args); + } + + if (!string.IsNullOrWhiteSpace(resolvedEntryAssemblyPath) && + string.Equals(Path.GetExtension(resolvedEntryAssemblyPath), ".dll", StringComparison.OrdinalIgnoreCase)) + { + return CreateDotnetStartInfo( + "dotnet", + resolvedEntryAssemblyPath, + args); + } + + return null; + } + + private static ProcessStartInfo CreateExecutableStartInfo( + string executablePath, + string? entryAssemblyPath, + IReadOnlyList commandLineArgs) + { + var startInfo = new ProcessStartInfo + { + FileName = executablePath, + UseShellExecute = false, + WorkingDirectory = ResolveWorkingDirectory(executablePath, entryAssemblyPath) + }; + + AppendArguments(startInfo, commandLineArgs); + return startInfo; + } + + private static ProcessStartInfo? CreateDotnetStartInfo( + string dotnetHostPath, + string? entryAssemblyPath, + IReadOnlyList commandLineArgs) + { + if (string.IsNullOrWhiteSpace(entryAssemblyPath)) + { + return null; + } + + var startInfo = new ProcessStartInfo + { + FileName = dotnetHostPath, + UseShellExecute = false, + WorkingDirectory = ResolveWorkingDirectory(dotnetHostPath, entryAssemblyPath) + }; + + startInfo.ArgumentList.Add(entryAssemblyPath); + AppendArguments(startInfo, commandLineArgs); + return startInfo; + } + + private static void AppendArguments(ProcessStartInfo startInfo, IReadOnlyList commandLineArgs) + { + for (var i = 1; i < commandLineArgs.Count; i++) + { + startInfo.ArgumentList.Add(commandLineArgs[i]); + } + } + + private static string? NormalizeExistingPath(string? path) + { + if (string.IsNullOrWhiteSpace(path)) + { + return null; + } + + try + { + var fullPath = Path.GetFullPath(path); + return File.Exists(fullPath) ? fullPath : null; + } + catch + { + return null; + } + } + + private static bool IsDotnetHost(string? processPath) + { + if (string.IsNullOrWhiteSpace(processPath)) + { + return false; + } + + var fileName = Path.GetFileName(processPath); + return string.Equals(fileName, "dotnet", StringComparison.OrdinalIgnoreCase) || + string.Equals(fileName, "dotnet.exe", StringComparison.OrdinalIgnoreCase); + } + + private static string ResolveWorkingDirectory(string launchPath, string? entryAssemblyPath) + { + var basePath = !string.IsNullOrWhiteSpace(entryAssemblyPath) + ? entryAssemblyPath + : launchPath; + + return Path.GetDirectoryName(basePath) ?? AppContext.BaseDirectory; + } +} diff --git a/LanMountainDesktop/Views/MainWindow.Localization.cs b/LanMountainDesktop/Views/MainWindow.Localization.cs index a78c4ed..0c6572c 100644 --- a/LanMountainDesktop/Views/MainWindow.Localization.cs +++ b/LanMountainDesktop/Views/MainWindow.Localization.cs @@ -326,6 +326,7 @@ public partial class MainWindow SetAppRenderModeComboItemContent(AppRenderingModeHelper.AngleEgl, L("settings.about.render_mode.angle_egl", "angleEgl")); SetAppRenderModeComboItemContent(AppRenderingModeHelper.Wgl, L("settings.about.render_mode.wgl", "WGL")); SetAppRenderModeComboItemContent(AppRenderingModeHelper.Vulkan, L("settings.about.render_mode.vulkan", "Vulkan")); + UpdateCurrentRenderBackendStatus(); if (WallpaperPlacementComboBox?.ItemCount >= 5) { diff --git a/LanMountainDesktop/Views/MainWindow.RenderBackend.cs b/LanMountainDesktop/Views/MainWindow.RenderBackend.cs new file mode 100644 index 0000000..0c07c9e --- /dev/null +++ b/LanMountainDesktop/Views/MainWindow.RenderBackend.cs @@ -0,0 +1,42 @@ +using System; +using LanMountainDesktop.Services; + +namespace LanMountainDesktop.Views; + +public partial class MainWindow +{ + private void UpdateCurrentRenderBackendStatus() + { + var backendInfo = AppRenderBackendDiagnostics.Detect(); + var localizedBackend = GetLocalizedRenderBackendName(backendInfo.ActualBackend); + + CurrentRenderBackendLabelTextBlock.Text = L( + "settings.about.render_mode.current_label", + "Current actual backend"); + CurrentRenderBackendValueTextBlock.Text = Lf( + "settings.about.render_mode.current_format", + "Current backend: {0}", + localizedBackend); + CurrentRenderBackendImplementationTextBlock.Text = string.IsNullOrWhiteSpace(backendInfo.ImplementationTypeName) + ? L( + "settings.about.render_mode.impl_unavailable", + "Runtime implementation is unavailable.") + : Lf( + "settings.about.render_mode.impl_format", + "Runtime implementation: {0}", + backendInfo.ImplementationTypeName); + } + + private string GetLocalizedRenderBackendName(string renderBackend) + { + return renderBackend switch + { + AppRenderingModeHelper.Default => L("settings.about.render_mode.default", "Default"), + AppRenderingModeHelper.Software => L("settings.about.render_mode.software", "Software"), + AppRenderingModeHelper.AngleEgl => L("settings.about.render_mode.angle_egl", "angleEgl"), + AppRenderingModeHelper.Wgl => L("settings.about.render_mode.wgl", "WGL"), + AppRenderingModeHelper.Vulkan => L("settings.about.render_mode.vulkan", "Vulkan"), + _ => L("settings.about.render_mode.unknown", "Unknown") + }; + } +} diff --git a/LanMountainDesktop/Views/MainWindow.Settings.cs b/LanMountainDesktop/Views/MainWindow.Settings.cs index 1489cb3..4254d6e 100644 --- a/LanMountainDesktop/Views/MainWindow.Settings.cs +++ b/LanMountainDesktop/Views/MainWindow.Settings.cs @@ -2701,6 +2701,9 @@ public partial class MainWindow internal FluentAvalonia.UI.Controls.SettingsExpander AboutRenderModeSettingsExpander => AboutSettingsPanel.FindControl("AboutRenderModeSettingsExpander")!; internal ToggleSwitch AutoStartWithWindowsToggleSwitch => AboutSettingsPanel.FindControl("AutoStartWithWindowsToggleSwitch")!; internal ComboBox AppRenderModeComboBox => AboutSettingsPanel.FindControl("AppRenderModeComboBox")!; + internal TextBlock CurrentRenderBackendLabelTextBlock => AboutSettingsPanel.FindControl("CurrentRenderBackendLabelTextBlock")!; + internal TextBlock CurrentRenderBackendValueTextBlock => AboutSettingsPanel.FindControl("CurrentRenderBackendValueTextBlock")!; + internal TextBlock CurrentRenderBackendImplementationTextBlock => AboutSettingsPanel.FindControl("CurrentRenderBackendImplementationTextBlock")!; internal TextBlock VersionTextBlock => AboutSettingsPanel.FindControl("VersionTextBlock")!; internal TextBlock CodeNameTextBlock => AboutSettingsPanel.FindControl("CodeNameTextBlock")!; internal TextBlock FontInfoTextBlock => AboutSettingsPanel.FindControl("FontInfoTextBlock")!; diff --git a/LanMountainDesktop/Views/SettingsPages/AboutSettingsPage.axaml b/LanMountainDesktop/Views/SettingsPages/AboutSettingsPage.axaml index b0d9d29..64606ab 100644 --- a/LanMountainDesktop/Views/SettingsPages/AboutSettingsPage.axaml +++ b/LanMountainDesktop/Views/SettingsPages/AboutSettingsPage.axaml @@ -43,6 +43,24 @@ + + + + + AboutSettingsPanel.FindControl("AboutRenderModeSettingsExpander")!; internal ToggleSwitch AutoStartWithWindowsToggleSwitch => AboutSettingsPanel.FindControl("AutoStartWithWindowsToggleSwitch")!; internal ComboBox AppRenderModeComboBox => AboutSettingsPanel.FindControl("AppRenderModeComboBox")!; + internal TextBlock CurrentRenderBackendLabelTextBlock => AboutSettingsPanel.FindControl("CurrentRenderBackendLabelTextBlock")!; + internal TextBlock CurrentRenderBackendValueTextBlock => AboutSettingsPanel.FindControl("CurrentRenderBackendValueTextBlock")!; + internal TextBlock CurrentRenderBackendImplementationTextBlock => AboutSettingsPanel.FindControl("CurrentRenderBackendImplementationTextBlock")!; internal TextBlock VersionTextBlock => AboutSettingsPanel.FindControl("VersionTextBlock")!; internal TextBlock CodeNameTextBlock => AboutSettingsPanel.FindControl("CodeNameTextBlock")!; internal TextBlock FontInfoTextBlock => AboutSettingsPanel.FindControl("FontInfoTextBlock")!; diff --git a/LanMountainDesktop/Views/SettingsWindow.Localization.cs b/LanMountainDesktop/Views/SettingsWindow.Localization.cs index 187cc15..15cd30a 100644 --- a/LanMountainDesktop/Views/SettingsWindow.Localization.cs +++ b/LanMountainDesktop/Views/SettingsWindow.Localization.cs @@ -136,6 +136,7 @@ public partial class SettingsWindow SetAppRenderModeComboItemContent(AppRenderingModeHelper.AngleEgl, L("settings.about.render_mode.angle_egl", "angleEgl")); SetAppRenderModeComboItemContent(AppRenderingModeHelper.Wgl, L("settings.about.render_mode.wgl", "WGL")); SetAppRenderModeComboItemContent(AppRenderingModeHelper.Vulkan, L("settings.about.render_mode.vulkan", "Vulkan")); + UpdateCurrentRenderBackendStatus(); var placementItems = WallpaperPlacementComboBox.Items.OfType().ToList(); if (placementItems.Count >= 5) diff --git a/LanMountainDesktop/Views/SettingsWindow.RenderBackend.cs b/LanMountainDesktop/Views/SettingsWindow.RenderBackend.cs new file mode 100644 index 0000000..d2fc708 --- /dev/null +++ b/LanMountainDesktop/Views/SettingsWindow.RenderBackend.cs @@ -0,0 +1,42 @@ +using System; +using LanMountainDesktop.Services; + +namespace LanMountainDesktop.Views; + +public partial class SettingsWindow +{ + private void UpdateCurrentRenderBackendStatus() + { + var backendInfo = AppRenderBackendDiagnostics.Detect(); + var localizedBackend = GetLocalizedRenderBackendName(backendInfo.ActualBackend); + + CurrentRenderBackendLabelTextBlock.Text = L( + "settings.about.render_mode.current_label", + "Current actual backend"); + CurrentRenderBackendValueTextBlock.Text = Lf( + "settings.about.render_mode.current_format", + "Current backend: {0}", + localizedBackend); + CurrentRenderBackendImplementationTextBlock.Text = string.IsNullOrWhiteSpace(backendInfo.ImplementationTypeName) + ? L( + "settings.about.render_mode.impl_unavailable", + "Runtime implementation is unavailable.") + : Lf( + "settings.about.render_mode.impl_format", + "Runtime implementation: {0}", + backendInfo.ImplementationTypeName); + } + + private string GetLocalizedRenderBackendName(string renderBackend) + { + return renderBackend switch + { + AppRenderingModeHelper.Default => L("settings.about.render_mode.default", "Default"), + AppRenderingModeHelper.Software => L("settings.about.render_mode.software", "Software"), + AppRenderingModeHelper.AngleEgl => L("settings.about.render_mode.angle_egl", "angleEgl"), + AppRenderingModeHelper.Wgl => L("settings.about.render_mode.wgl", "WGL"), + AppRenderingModeHelper.Vulkan => L("settings.about.render_mode.vulkan", "Vulkan"), + _ => L("settings.about.render_mode.unknown", "Unknown") + }; + } +}