From 4d0cd61b6aa9de5e906f0f07ab2418a5f83551d8 Mon Sep 17 00:00:00 2001 From: Babib3l Date: Tue, 5 May 2026 03:48:23 +0000 Subject: [PATCH] Fix Windows console hide path targeting the foreground window (#32) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR addresses [Ryubing/Issues#345](https://github.com/Ryubing/Issues/issues/345) by fixing the Windows console hide/show path so it only acts on Ryujinx’s own console window instead of whatever window happens to be focused during startup. Previously, when Show Console was disabled, the helper could race with focus changes and end up affecting another app or shell window while leaving the console visible; this change removes that foreground-window dependency and keeps the startup behavior scoped to the Ryujinx console. Reviewed-on: https://git.ryujinx.app/projects/Ryubing/pulls/32 --- assets/Locales/Root.json | 25 +++++++++++ src/Ryujinx.Common/Helpers/ConsoleHelper.cs | 43 ++++++++++--------- src/Ryujinx.Common/Logging/Logger.cs | 21 ++++++--- .../UI/ViewModels/MainWindowViewModel.cs | 9 ++++ 4 files changed, 72 insertions(+), 26 deletions(-) diff --git a/assets/Locales/Root.json b/assets/Locales/Root.json index a11aad25a..0b1115cdc 100644 --- a/assets/Locales/Root.json +++ b/assets/Locales/Root.json @@ -21425,6 +21425,31 @@ "zh_TW": "需要重新啟動 Ryujinx" } }, + { + "ID": "SettingsShowConsoleRestartMessage", + "Translations": { + "ar_SA": "", + "de_DE": "", + "el_GR": "", + "en_US": "The console will be available the next time Ryujinx starts.", + "es_ES": "La consola estará disponible la próxima vez que se inicie Ryujinx.", + "fr_FR": "La console sera disponible au prochain démarrage de Ryujinx.", + "he_IL": "", + "it_IT": "", + "ja_JP": "", + "ko_KR": "", + "no_NO": "", + "pl_PL": "", + "pt_BR": "", + "ru_RU": "", + "sv_SE": "", + "th_TH": "", + "tr_TR": "", + "uk_UA": "", + "zh_CN": "", + "zh_TW": "" + } + }, { "ID": "SettingsGpuBackendRestartMessage", "Translations": { diff --git a/src/Ryujinx.Common/Helpers/ConsoleHelper.cs b/src/Ryujinx.Common/Helpers/ConsoleHelper.cs index 2b3c11b1e..75954110c 100644 --- a/src/Ryujinx.Common/Helpers/ConsoleHelper.cs +++ b/src/Ryujinx.Common/Helpers/ConsoleHelper.cs @@ -12,20 +12,12 @@ namespace Ryujinx.Common.Helper private static partial nint GetConsoleWindow(); [SupportedOSPlatform("windows")] - [LibraryImport("user32")] + [LibraryImport("kernel32", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] - private static partial bool ShowWindow(nint hWnd, int nCmdShow); - - [SupportedOSPlatform("windows")] - [LibraryImport("user32")] - private static partial nint GetForegroundWindow(); - - [SupportedOSPlatform("windows")] - [LibraryImport("user32")] - [return: MarshalAs(UnmanagedType.Bool)] - private static partial bool SetForegroundWindow(nint hWnd); + private static partial bool FreeConsole(); public static bool SetConsoleWindowStateSupported => OperatingSystem.IsWindows(); + public static bool HasConsoleWindow => OperatingSystem.IsWindows() && GetConsoleWindow() != nint.Zero; public static void SetConsoleWindowState(bool show) { @@ -42,22 +34,31 @@ namespace Ryujinx.Common.Helper [SupportedOSPlatform("windows")] private static void SetConsoleWindowStateWindows(bool show) { - const int SW_HIDE = 0; - const int SW_SHOW = 5; - - nint hWnd = GetConsoleWindow(); - - if (hWnd == nint.Zero) + if (show) { - Logger.Warning?.Print(LogClass.Application, "Attempted to show/hide console window but console window does not exist"); + if (GetConsoleWindow() != nint.Zero) + { + Logger.SetConsoleTargetEnabled(true); + } return; } - SetForegroundWindow(hWnd); + Logger.SetConsoleTargetEnabled(false); + DetachConsole(); + } - hWnd = GetForegroundWindow(); + [SupportedOSPlatform("windows")] + private static void DetachConsole() + { + if (GetConsoleWindow() == nint.Zero) + { + return; + } - ShowWindow(hWnd, show ? SW_SHOW : SW_HIDE); + if (!FreeConsole()) + { + Logger.Warning?.Print(LogClass.Application, "Attempted to detach console window but the operation failed"); + } } } } diff --git a/src/Ryujinx.Common/Logging/Logger.cs b/src/Ryujinx.Common/Logging/Logger.cs index 64a76a3e4..b5450c94b 100644 --- a/src/Ryujinx.Common/Logging/Logger.cs +++ b/src/Ryujinx.Common/Logging/Logger.cs @@ -136,11 +136,7 @@ namespace Ryujinx.Common.Logging _time = Stopwatch.StartNew(); - // Logger should log to console by default - AddTarget(new AsyncLogTargetWrapper( - new ConsoleLogTarget("console"), - 1000, - AsyncLogTargetOverflowAction.Discard)); + SetConsoleTargetEnabled(true); Notice = new Log(LogLevel.Notice); @@ -173,6 +169,21 @@ namespace Ryujinx.Common.Logging Updated += target.Log; } + public static void SetConsoleTargetEnabled(bool enabled) + { + if (enabled) + { + AddTarget(new AsyncLogTargetWrapper( + new ConsoleLogTarget("console"), + 1000, + AsyncLogTargetOverflowAction.Discard)); + } + else + { + RemoveTarget("console"); + } + } + public static void RemoveTarget(string target) { ILogTarget logTarget = GetTarget(target); diff --git a/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs b/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs index 9e139c186..57fd825b3 100644 --- a/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs @@ -656,10 +656,19 @@ namespace Ryujinx.Ava.UI.ViewModels get => ConfigurationState.Instance.UI.ShowConsole; set { + bool restartRequired = value && !ConsoleHelper.HasConsoleWindow; + ConfigurationState.Instance.UI.ShowConsole.Value = value; ConfigurationState.Instance.ToFileFormat().SaveConfig(Program.ConfigurationPath); + if (restartRequired) + { + NotificationHelper.ShowInformation( + LocaleManager.Instance[LocaleKeys.SettingsAppRequiredRestartMessage], + LocaleManager.Instance[LocaleKeys.SettingsShowConsoleRestartMessage]); + } + OnPropertyChanged(); } }