diff --git a/Directory.Packages.props b/Directory.Packages.props
index a480d3d29..c2ac358ed 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -3,13 +3,13 @@
true
-
-
-
-
-
-
-
+
+
+
+
+
+
+
@@ -18,7 +18,7 @@
-
+
@@ -26,7 +26,7 @@
-
+
@@ -48,11 +48,11 @@
-
-
-
-
-
+
+
+
+
+
diff --git a/src/Ryujinx.Graphics.Vulkan/Vendor.cs b/src/Ryujinx.Graphics.Vulkan/Vendor.cs
index 55ae0cd81..6a2a76a88 100644
--- a/src/Ryujinx.Graphics.Vulkan/Vendor.cs
+++ b/src/Ryujinx.Graphics.Vulkan/Vendor.cs
@@ -92,7 +92,7 @@ namespace Ryujinx.Graphics.Vulkan
DriverId.MesaDozen => "Dozen",
DriverId.MesaNvk => "NVK",
DriverId.ImaginationOpenSourceMesa => "Imagination (Open)",
- DriverId.MesaAgxv => "Honeykrisp",
+ DriverId.MesaHoneykrisp => "Honeykrisp",
_ => id.ToString(),
};
}
diff --git a/src/Ryujinx.Input.SDL2/SDL2Gamepad.cs b/src/Ryujinx.Input.SDL2/SDL2Gamepad.cs
index 12bfab4bb..7e8bd4167 100644
--- a/src/Ryujinx.Input.SDL2/SDL2Gamepad.cs
+++ b/src/Ryujinx.Input.SDL2/SDL2Gamepad.cs
@@ -253,11 +253,23 @@ namespace Ryujinx.Input.SDL2
return IGamepad.GetStateSnapshot(this);
}
+ private static bool hotButtonMinus = false;
+ private static bool hotExit = false;
+
+ public bool SpecialExit()
+ {
+ if (hotButtonMinus)
+ {
+ hotButtonMinus = false;
+ return hotExit;
+ }
+ return hotExit = false;
+ }
+
public GamepadStateSnapshot GetMappedStateSnapshot()
{
GamepadStateSnapshot rawState = GetStateSnapshot();
GamepadStateSnapshot result = default;
-
lock (_userMappingLock)
{
if (_buttonsUserMapping.Count == 0)
@@ -270,6 +282,28 @@ namespace Ryujinx.Input.SDL2
if (!entry.IsValid)
continue;
+ if (GamepadButtonInputId.Minus == entry.To)
+ {
+ if (rawState.IsPressed(entry.From) && !hotButtonMinus)
+ {
+ hotButtonMinus = true;
+ }
+ else if (!result.IsPressed(entry.From) && hotButtonMinus)
+ {
+ hotButtonMinus = false;
+ }
+ }
+
+ if (GamepadButtonInputId.Plus == entry.To)
+ {
+ if (rawState.IsPressed(entry.To) && hotButtonMinus)
+ {
+
+ hotExit = true;
+ }
+
+ }
+
// Do not touch state of button already pressed
if (!result.IsPressed(entry.To))
{
@@ -376,5 +410,7 @@ namespace Ryujinx.Input.SDL2
return SDL_GameControllerGetButton(_gamepadHandle, _buttonsDriverMapping[(int)inputId]) == 1;
}
+
+
}
}
diff --git a/src/Ryujinx.Input.SDL2/SDL2Keyboard.cs b/src/Ryujinx.Input.SDL2/SDL2Keyboard.cs
index 8d6a30d11..ab01a9bbc 100644
--- a/src/Ryujinx.Input.SDL2/SDL2Keyboard.cs
+++ b/src/Ryujinx.Input.SDL2/SDL2Keyboard.cs
@@ -329,6 +329,11 @@ namespace Ryujinx.Input.SDL2
return result;
}
+ public bool SpecialExit()
+ {
+ return false;
+ }
+
public GamepadStateSnapshot GetStateSnapshot()
{
throw new NotSupportedException();
diff --git a/src/Ryujinx.Input.SDL2/SDL2Mouse.cs b/src/Ryujinx.Input.SDL2/SDL2Mouse.cs
index 37b356b76..da0622db3 100644
--- a/src/Ryujinx.Input.SDL2/SDL2Mouse.cs
+++ b/src/Ryujinx.Input.SDL2/SDL2Mouse.cs
@@ -25,6 +25,10 @@ namespace Ryujinx.Input.SDL2
{
_driver = driver;
}
+ public bool SpecialExit()
+ {
+ return false;
+ }
public Vector2 GetPosition()
{
diff --git a/src/Ryujinx.Input/HLE/NpadController.cs b/src/Ryujinx.Input/HLE/NpadController.cs
index 53426f71a..18601f6c7 100644
--- a/src/Ryujinx.Input/HLE/NpadController.cs
+++ b/src/Ryujinx.Input/HLE/NpadController.cs
@@ -3,6 +3,7 @@ using Ryujinx.Common.Configuration.Hid;
using Ryujinx.Common.Configuration.Hid.Controller;
using Ryujinx.Common.Configuration.Hid.Controller.Motion;
using Ryujinx.Common.Logging;
+using Ryujinx.Graphics.Gpu;
using Ryujinx.HLE.HOS.Services.Hid;
using System;
using System.Collections.Concurrent;
@@ -274,15 +275,21 @@ namespace Ryujinx.Input.HLE
}
}
- public void Update()
+ public bool Update()
{
+
// _gamepad may be altered by other threads
var gamepad = _gamepad;
-
+
if (gamepad != null && GamepadDriver != null)
{
State = gamepad.GetMappedStateSnapshot();
+ if (gamepad.SpecialExit())
+ {
+ return true;
+ }
+
if (_config is StandardControllerInputConfig controllerConfig && controllerConfig.Motion.EnableMotion)
{
if (controllerConfig.Motion.MotionBackend == MotionInputBackendType.GamepadDriver)
@@ -349,6 +356,7 @@ namespace Ryujinx.Input.HLE
_leftMotionInput = null;
_rightMotionInput = null;
}
+ return false;
}
public GamepadInput GetHLEInputState()
diff --git a/src/Ryujinx.Input/HLE/NpadManager.cs b/src/Ryujinx.Input/HLE/NpadManager.cs
index 08f222a91..4ebb8401d 100644
--- a/src/Ryujinx.Input/HLE/NpadManager.cs
+++ b/src/Ryujinx.Input/HLE/NpadManager.cs
@@ -200,8 +200,10 @@ namespace Ryujinx.Input.HLE
ReloadConfiguration(inputConfig, enableKeyboard, enableMouse);
}
- public void Update(float aspectRatio = 1)
+ public bool Update(float aspectRatio = 1)
{
+ bool specialExit = false;
+
lock (_lock)
{
List hleInputStates = new();
@@ -225,9 +227,10 @@ namespace Ryujinx.Input.HLE
DriverConfigurationUpdate(ref controller, inputConfig);
controller.UpdateUserConfiguration(inputConfig);
- controller.Update();
- controller.UpdateRumble(_device.Hid.Npads.GetRumbleQueue(playerIndex));
+ specialExit = controller.Update(); //hotkey press check
+ controller.UpdateRumble(_device.Hid.Npads.GetRumbleQueue(playerIndex));
+
inputState = controller.GetHLEInputState();
inputState.Buttons |= _device.Hid.UpdateStickButtons(inputState.LStick, inputState.RStick);
@@ -315,6 +318,8 @@ namespace Ryujinx.Input.HLE
_device.TamperMachine.UpdateInput(hleInputStates);
}
+
+ return specialExit;
}
internal InputConfig GetPlayerInputConfigByIndex(int index)
diff --git a/src/Ryujinx.Input/IGamepad.cs b/src/Ryujinx.Input/IGamepad.cs
index 3853f2819..f52703e19 100644
--- a/src/Ryujinx.Input/IGamepad.cs
+++ b/src/Ryujinx.Input/IGamepad.cs
@@ -79,6 +79,12 @@ namespace Ryujinx.Input
/// A remapped snaphost of the state of the gamepad.
GamepadStateSnapshot GetMappedStateSnapshot();
+ ///
+ /// Gets the state if the minus and plus buttons were pressed on the gamepad.
+ ///
+ /// returns true if the buttons were pressed.
+ bool SpecialExit();
+
///
/// Get a snaphost of the state of the gamepad.
///
diff --git a/src/Ryujinx/AppHost.cs b/src/Ryujinx/AppHost.cs
index a35a79e86..c94e869d5 100644
--- a/src/Ryujinx/AppHost.cs
+++ b/src/Ryujinx/AppHost.cs
@@ -18,6 +18,7 @@ using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.Models;
using Ryujinx.Ava.UI.Renderer;
using Ryujinx.Ava.UI.ViewModels;
+using Ryujinx.Ava.UI.Views.Main;
using Ryujinx.Ava.UI.Windows;
using Ryujinx.Ava.Utilities;
using Ryujinx.Ava.Utilities.AppLibrary;
@@ -70,6 +71,7 @@ namespace Ryujinx.Ava
private const float MaxResolutionScale = 4.0f; // Max resolution hotkeys can scale to before wrapping.
private const int TargetFps = 60;
private const float VolumeDelta = 0.05f;
+ static bool SpecialExit = false;
private static readonly Cursor _invisibleCursor = new(StandardCursorType.None);
private readonly nint _invisibleCursorWin;
@@ -96,6 +98,7 @@ namespace Ryujinx.Ava
private bool _isCursorInRenderer = true;
private bool _ignoreCursorState = false;
+
private enum CursorStates
{
CursorIsHidden,
@@ -503,10 +506,15 @@ namespace Ryujinx.Ava
_viewModel.Volume = ConfigurationState.Instance.System.AudioVolume.Value;
MainLoop();
-
+
Exit();
}
+ public bool IsSpecialExit()
+ {
+ return SpecialExit;
+ }
+
private void UpdateIgnoreMissingServicesState(object sender, ReactiveEventArgs args)
{
if (Device != null)
@@ -589,6 +597,7 @@ namespace Ryujinx.Ava
_isStopped = true;
Stop();
+
}
public void DisposeContext()
@@ -1135,6 +1144,7 @@ namespace Ryujinx.Ava
string dockedMode = ConfigurationState.Instance.System.EnableDockedMode ? LocaleManager.Instance[LocaleKeys.Docked] : LocaleManager.Instance[LocaleKeys.Handheld];
string vSyncMode = Device.VSyncMode.ToString();
+
UpdateShaderCount();
if (GraphicsConfig.ResScale != 1)
@@ -1200,7 +1210,17 @@ namespace Ryujinx.Ava
return false;
}
- NpadManager.Update(ConfigurationState.Instance.Graphics.AspectRatio.Value.ToFloat());
+ if (NpadManager.Update(ConfigurationState.Instance.Graphics.AspectRatio.Value.ToFloat()))
+ {
+ if (ConfigurationState.Instance.Hid.SpecialExitEmulator.Value == 1)
+ {
+ SpecialExit = true; // close App
+ }
+ if (ConfigurationState.Instance.Hid.SpecialExitEmulator.Value > 0)
+ {
+ _isActive = false; //close game
+ }
+ }
if (_viewModel.IsActive)
{
@@ -1335,6 +1355,8 @@ namespace Ryujinx.Ava
Device.Hid.DebugPad.Update();
+
+
return true;
}
diff --git a/src/Ryujinx/Assets/locales.json b/src/Ryujinx/Assets/locales.json
index 221c898ea..753752e0d 100644
--- a/src/Ryujinx/Assets/locales.json
+++ b/src/Ryujinx/Assets/locales.json
@@ -147,6 +147,31 @@
"zh_TW": "滑鼠直接存取"
}
},
+ {
+ "ID": "SettingsExtraCloseApp",
+ "Translations": {
+ "ar_SA": "خروج سريع من التطبيق",
+ "de_DE": "Schneller Ausstieg aus der Anwendung",
+ "el_GR": "Γρήγορη έξοδος από την εφαρμογή",
+ "en_US": "Quick Exit from Application",
+ "es_ES": "Salida rápida de la aplicación",
+ "fr_FR": "Sortie rapide de l'application",
+ "he_IL": "יציאה מהירה מהאפליקציה",
+ "it_IT": "Uscita rapida dall'applicazione",
+ "ja_JP": "アプリケーションからの迅速な終了",
+ "ko_KR": "애플리케이션에서 빠른 종료",
+ "no_NO": "Rask avslutning av applikasjonen",
+ "pl_PL": "Szybkie wyjście z aplikacji",
+ "pt_BR": "Saída rápida do aplicativo",
+ "ru_RU": "Быстрый выход из приложения",
+ "sv_SE": "Snabb avslutning från applikationen",
+ "th_TH": "ออกจากแอปพลิเคชันอย่างรวดเร็ว",
+ "tr_TR": "Uygulamadan Hızlı Çıkış",
+ "uk_UA": "Швидкий вихід з програми",
+ "zh_CN": "快速退出应用程序",
+ "zh_TW": "快速退出應用程式"
+ }
+ },
{
"ID": "SettingsTabSystemMemoryManagerMode",
"Translations": {
@@ -11047,6 +11072,81 @@
"zh_TW": "淺色"
}
},
+ {
+ "ID": "SettingsTabInputDisableExitHotKey",
+ "Translations": {
+ "ar_SA": "الخروج السريع معطل",
+ "de_DE": "Schneller Ausstieg deaktiviert",
+ "el_GR": "Η γρήγορη έξοδος είναι απενεργοποιημένη",
+ "en_US": "Quick exit disabled",
+ "es_ES": "Salida rápida desactivada",
+ "fr_FR": "Sortie rapide désactivée",
+ "he_IL": "יציאה מהירה מושבתת",
+ "it_IT": "Uscita rapida disabilitata",
+ "ja_JP": "クイック終了が無効です",
+ "ko_KR": "빠른 종료 비활성화됨",
+ "no_NO": "Rask avslutning er deaktivert",
+ "pl_PL": "Szybkie wyjście wyłączone",
+ "pt_BR": "Saída rápida desativada",
+ "ru_RU": "Быстрый выход выключен",
+ "sv_SE": "Snabb avslutning inaktiverad",
+ "th_TH": "ปิดใช้งานออกอย่างรวดเร็ว",
+ "tr_TR": "Hızlı çıkış devre dışı bırakıldı",
+ "uk_UA": "Швидкий вихід вимкнено",
+ "zh_CN": "快速退出已禁用",
+ "zh_TW": "快速退出已停用"
+ }
+ },
+ {
+ "ID": "SettingsTabInputHotkeyIsCloseApp",
+ "Translations": {
+ "ar_SA": "إغلاق التطبيق بالضغط على الزرين '+' و '-'.",
+ "de_DE": "App schließen mit den '+' und '-' Tasten.",
+ "el_GR": "Κλείσιμο της εφαρμογής με τα κουμπιά '+' και '-'.",
+ "en_US": "Close app by '+' and '-' buttons.",
+ "es_ES": "Cerrar la aplicación pulsando los botones '+' y '-'.",
+ "fr_FR": "Fermer l'application avec les boutons '+' et '-'.",
+ "he_IL": "סגור את האפליקציה בלחיצה על '+' ו-'-'.",
+ "it_IT": "Chiudi l'app premendo i pulsanti '+' e '-'.",
+ "ja_JP": "「+」と「-」ボタンを押してアプリを終了します。",
+ "ko_KR": "'+' 및 '-' 버튼을 눌러 앱을 종료합니다.",
+ "no_NO": "Lukk appen med '+' og '-' knappene.",
+ "pl_PL": "Zamknij aplikację przyciskiem '+' i '-'.",
+ "pt_BR": "Fechar o aplicativo pressionando os botões '+' e '-'.",
+ "ru_RU": "Закрыть приложение нажатием '+' и '-'.",
+ "sv_SE": "Stäng appen med '+' och '-' knapparna.",
+ "th_TH": "ปิดแอปโดยกดปุ่ม '+' และ '-'.",
+ "tr_TR": "'+' ve '-' düğmelerine basarak uygulamayı kapatın.",
+ "uk_UA": "Закрити додаток натисканням '+' та '-'.",
+ "zh_CN": "",
+ "zh_TW": ""
+ }
+ },
+ {
+ "ID": "SettingsTabInputHotkeyIsCloseGame",
+ "Translations": {
+ "ar_SA": "الخروج من اللعبة بالضغط على الزرين '+' و '-'.",
+ "de_DE": "Spiel beenden mit den '+' und '-' Tasten.",
+ "el_GR": "Έξοδος από το παιχνίδι με τα κουμπιά '+' και '-'.",
+ "en_US": "Exit game by '+' and '-' buttons.",
+ "es_ES": "Salir del juego pulsando los botones '+' y '-'.",
+ "fr_FR": "Quitter le jeu avec les boutons '+' et '-'.",
+ "he_IL": "יציאה מהמשחק בלחיצה על '+' ו-'-'.",
+ "it_IT": "Esci dal gioco premendo i pulsanti '+' e '-'.",
+ "ja_JP": "「+」と「-」ボタンを押してゲームを終了します。",
+ "ko_KR": "'+' 및 '-' 버튼을 눌러 게임을 종료합니다.",
+ "no_NO": "Avslutt spillet med '+' og '-' knappene.",
+ "pl_PL": "Wyjście z gry przyciskiem '+' i '-'.",
+ "pt_BR": "Sair do jogo pressionando os botões '+' e '-'.",
+ "ru_RU": "Выйти из игры нажатием '+' и '-'.",
+ "sv_SE": "Avsluta spelet med '+' och '-' knapparna.",
+ "th_TH": "ออกจากเกมโดยกดปุ่ม '+' และ '-'.",
+ "tr_TR": "'+' ve '-' düğmelerine basarak oyundan çıkın.",
+ "uk_UA": "Вийти з гри натисканням '+' та '-'.",
+ "zh_CN": "",
+ "zh_TW": ""
+ }
+ },
{
"ID": "ControllerSettingsConfigureGeneral",
"Translations": {
@@ -15147,6 +15247,31 @@
"zh_TW": "支援滑鼠直接存取 (HID)。遊戲可將滑鼠作為指向裝置使用。\n\n僅適用於在 Switch 硬體上原生支援滑鼠控制的遊戲,這類遊戲很少。\n\n啟用後,觸控螢幕功能可能無法使用。\n\n如果不確定,請保持關閉狀態。"
}
},
+ {
+ "ID": "SpecialExitTooltip",
+ "Translations": {
+ "ar_SA": "يقوم بتفعيل مفاتيح الاختصار 'زائد' و 'ناقص'.\nاضغط على زرّي زائد وناقص في نفس الوقت لتنفيذ إحدى العمليات:\n\n1) إغلاق التطبيق باستخدام مفاتيح الاختصار.\n2) الخروج من اللعبة دون إغلاق التطبيق.\n\nيعمل فقط مع أذرع التحكم.",
+ "de_DE": "Aktiviert die Hotkeys 'Plus' und 'Minus'.\nDrücken Sie gleichzeitig die Plus- und Minus-Tasten, um eine der folgenden Aktionen auszuführen:\n\n1) Schließt die Anwendung durch Drücken der Hotkeys.\n2) Beendet das Spiel, ohne die Anwendung zu schließen.\n\nFunktioniert nur mit Gamepads.",
+ "el_GR": "Ενεργοποιεί τα πλήκτρα πρόσβασης 'συν' και 'πλην'.\nΠατήστε ταυτόχρονα τα κουμπιά συν και πλην για μία από τις ενέργειες:\n\n1) Κλείνει την εφαρμογή πατώντας τα πλήκτρα πρόσβασης.\n2) Εξέρχεται από το παιχνίδι χωρίς να κλείσει η εφαρμογή.\n\nΛειτουργεί μόνο με gamepads.",
+ "en_US": "Activates the hot keys 'plus' and 'minus'.\nPress buttons plus and minus at the same time to get one of the actions:\n\n1) Closes the application by pressing the hot keys.\n2) Exits the game without closing the application.\n\nWorks only with gamepads.",
+ "es_ES": "Activa las teclas rápidas 'más' y 'menos'.\nPresiona los botones más y menos al mismo tiempo para realizar una de las siguientes acciones:\n\n1) Cierra la aplicación presionando las teclas rápidas.\n2) Salir del juego sin cerrar la aplicación.\n\nFunciona solo con mandos.",
+ "fr_FR": "Active les raccourcis 'plus' et 'moins'.\nAppuyez simultanément sur les boutons plus et moins pour effectuer l'une des actions suivantes :\n\n1) Ferme l'application en appuyant sur les raccourcis.\n2) Quitte le jeu sans fermer l'application.\n\nFonctionne uniquement avec les manettes.",
+ "he_IL": "מפעיל את המקשים הקצרים 'פלוס' ו-'מינוס'.\nלחץ על הכפתורים פלוס ומינוס בו זמנית כדי לבצע אחת מהפעולות:\n\n1) סוגר את היישום באמצעות המקשים הקצרים.\n2) יוצא מהמשחק מבלי לסגור את היישום.\n\nפועל רק עם בקרי משחק.",
+ "it_IT": "Attiva i tasti rapidi 'più' e 'meno'.\nPremere i pulsanti più e meno contemporaneamente per eseguire una delle seguenti azioni:\n\n1) Chiude l'applicazione premendo i tasti rapidi.\n2) Esce dal gioco senza chiudere l'applicazione.\n\nFunziona solo con i gamepad.",
+ "ja_JP": "ホットキー「プラス」と「マイナス」を有効化します。\nプラスとマイナスのボタンを同時に押して、次のいずれかの操作を実行します:\n\n1) ホットキーを押すことでアプリを閉じます。\n2) アプリを閉じずにゲームを終了します。\n\nゲームパッドでのみ動作します。",
+ "ko_KR": "'플러스' 및 '마이너스' 단축키를 활성화합니다.\n플러스 및 마이너스 버튼을 동시에 눌러 다음 작업 중 하나를 수행합니다:\n\n1) 단축키를 눌러 애플리케이션을 닫습니다.\n2) 애플리케이션을 닫지 않고 게임을 종료합니다.\n\n게임패드에서만 작동합니다.",
+ "no_NO": "Aktiverer hurtigtastene 'pluss' og 'minus'.\nTrykk på knappene pluss og minus samtidig for å utføre en av følgende handlinger:\n\n1) Lukker applikasjonen ved å trykke på hurtigtastene.\n2) Avslutter spillet uten å lukke applikasjonen.\n\nFungerer kun med spillkontroller.",
+ "pl_PL": "Aktywuje klawisze skrótu 'plus' i 'minus'.\nNaciśnij jednocześnie przyciski plus i minus, aby wykonać jedną z akcji:\n\n1) Zamknij aplikację, naciskając klawisze skrótu.\n2) Wyjdź z gry bez zamykania aplikacji.\n\nDziała tylko z gamepadami.",
+ "pt_BR": "Ativa as teclas de atalho 'mais' e 'menos'.\nPressione os botões mais e menos ao mesmo tempo para realizar uma das ações:\n\n1) Fecha o aplicativo pressionando as teclas de atalho.\n2) Sai do jogo sem fechar o aplicativo.\n\nFunciona apenas com gamepads.",
+ "ru_RU": "Активирует горячие клавиши 'плюс' и 'минус'.\nНажмите одновременно кнопки плюс и минус чтобы получить одно из действий:\n\n1) Закрывает приложение по нажатию горячих кнопок.\n2) Выходит из игры без закрытия приложения.\n\nРаботает только с геймпадами.",
+ "sv_SE": "Aktiverar snabbtangenterna 'plus' och 'minus'.\nTryck samtidigt på plus- och minusknapparna för att utföra en av följande åtgärder:\n\n1) Stänger applikationen med snabbtangenterna.\n2) Avslutar spelet utan att stänga applikationen.\n\nFungerar bara med spelkontroller.",
+ "th_TH": "เปิดใช้งานปุ่มลัด '+' และ '-'.\nกดปุ่ม '+' และ '-' พร้อมกันเพื่อทำการอย่างใดอย่างหนึ่ง:\n\n1) ปิดแอปพลิเคชันด้วยการกดปุ่มลัด\n2) ออกจากเกมโดยไม่ปิดแอปพลิเคชัน\n\nใช้งานได้เฉพาะกับจอยเกม",
+ "tr_TR": "'Artı' ve 'eksi' kısayol tuşlarını etkinleştirir.\nArtı ve eksi tuşlarına aynı anda basarak aşağıdaki işlemlerden birini gerçekleştirin:\n\n1) Kısayol tuşlarına basarak uygulamayı kapatır.\n2) Uygulamayı kapatmadan oyundan çıkar.\n\nYalnızca gamepad'lerle çalışır.",
+ "uk_UA": "Активує гарячі клавіші '+' та '-'.\nНатисніть одночасно кнопки плюс та мінус для виконання однієї з дій:\n\n1) Закриває додаток за допомогою гарячих клавіш.\n2) Виходить із гри без закриття додатка.\n\nПрацює лише з геймпадами.",
+ "zh_CN": "激活快捷键“加号”和“减号”。\n同时按下加号和减号按钮以执行以下操作之一:\n\n1) 按快捷键关闭应用程序。\n2) 退出游戏而不关闭应用程序。\n\n仅适用于游戏手柄。",
+ "zh_TW": "啟用快捷鍵「加號」和「減號」。\n同時按下加號和減號按鈕以執行以下其中一項操作:\n\n1) 按快捷鍵關閉應用程式。\n2) 離開遊戲而不關閉應用程式。\n\n僅適用於遊戲手柄。"
+ }
+ },
{
"ID": "RegionTooltip",
"Translations": {
diff --git a/src/Ryujinx/Headless/HeadlessRyujinx.cs b/src/Ryujinx/Headless/HeadlessRyujinx.cs
index 5730254f7..98636d4ff 100644
--- a/src/Ryujinx/Headless/HeadlessRyujinx.cs
+++ b/src/Ryujinx/Headless/HeadlessRyujinx.cs
@@ -350,11 +350,11 @@ namespace Ryujinx.Headless
{
return options.GraphicsBackend switch
{
- GraphicsBackend.Vulkan => new VulkanWindow(_inputManager, options.LoggingGraphicsDebugLevel, options.AspectRatio, options.EnableMouse, options.HideCursorMode, options.IgnoreControllerApplet),
+ GraphicsBackend.Vulkan => new VulkanWindow(_inputManager, options.LoggingGraphicsDebugLevel, options.AspectRatio, options.EnableMouse, options.HideCursorMode, options.IgnoreControllerApplet, options.SpecialExit),
GraphicsBackend.Metal => OperatingSystem.IsMacOS() ?
- new MetalWindow(_inputManager, options.LoggingGraphicsDebugLevel, options.AspectRatio, options.EnableKeyboard, options.HideCursorMode, options.IgnoreControllerApplet) :
+ new MetalWindow(_inputManager, options.LoggingGraphicsDebugLevel, options.AspectRatio, options.EnableKeyboard, options.HideCursorMode, options.IgnoreControllerApplet, options.SpecialExit) :
throw new Exception("Attempted to use Metal renderer on non-macOS platform!"),
- _ => new OpenGLWindow(_inputManager, options.LoggingGraphicsDebugLevel, options.AspectRatio, options.EnableMouse, options.HideCursorMode, options.IgnoreControllerApplet)
+ _ => new OpenGLWindow(_inputManager, options.LoggingGraphicsDebugLevel, options.AspectRatio, options.EnableMouse, options.HideCursorMode, options.IgnoreControllerApplet, options.SpecialExit)
};
}
diff --git a/src/Ryujinx/Headless/Metal/MetalWindow.cs b/src/Ryujinx/Headless/Metal/MetalWindow.cs
index a2693c69d..1ae8f5ee4 100644
--- a/src/Ryujinx/Headless/Metal/MetalWindow.cs
+++ b/src/Ryujinx/Headless/Metal/MetalWindow.cs
@@ -23,8 +23,9 @@ namespace Ryujinx.Headless
AspectRatio aspectRatio,
bool enableMouse,
HideCursorMode hideCursorMode,
- bool ignoreControllerApplet)
- : base(inputManager, glLogLevel, aspectRatio, enableMouse, hideCursorMode, ignoreControllerApplet) { }
+ bool ignoreControllerApplet,
+ int specialExitEmulator)
+ : base(inputManager, glLogLevel, aspectRatio, enableMouse, hideCursorMode, ignoreControllerApplet, specialExitEmulator) { }
public override SDL_WindowFlags GetWindowFlags() => SDL_WindowFlags.SDL_WINDOW_METAL;
diff --git a/src/Ryujinx/Headless/OpenGL/OpenGLWindow.cs b/src/Ryujinx/Headless/OpenGL/OpenGLWindow.cs
index c00a0648f..ca4f48861 100644
--- a/src/Ryujinx/Headless/OpenGL/OpenGLWindow.cs
+++ b/src/Ryujinx/Headless/OpenGL/OpenGLWindow.cs
@@ -118,8 +118,9 @@ namespace Ryujinx.Headless
AspectRatio aspectRatio,
bool enableMouse,
HideCursorMode hideCursorMode,
- bool ignoreControllerApplet)
- : base(inputManager, glLogLevel, aspectRatio, enableMouse, hideCursorMode, ignoreControllerApplet)
+ bool ignoreControllerApplet,
+ int specialExitEmulator)
+ : base(inputManager, glLogLevel, aspectRatio, enableMouse, hideCursorMode, ignoreControllerApplet, specialExitEmulator)
{
_glLogLevel = glLogLevel;
}
diff --git a/src/Ryujinx/Headless/Options.cs b/src/Ryujinx/Headless/Options.cs
index 0d7e46285..c575416be 100644
--- a/src/Ryujinx/Headless/Options.cs
+++ b/src/Ryujinx/Headless/Options.cs
@@ -150,7 +150,10 @@ namespace Ryujinx.Headless
if (NeedsOverride(nameof(IgnoreControllerApplet)))
IgnoreControllerApplet = configurationState.IgnoreApplet;
-
+
+ if (NeedsOverride(nameof(SpecialExit)))
+ SpecialExit = configurationState.Hid.SpecialExitEmulator;
+
return;
bool NeedsOverride(string argKey) => originalArgs.None(arg => arg.TrimStart('-').EqualsIgnoreCase(OptionName(argKey)));
@@ -274,6 +277,9 @@ namespace Ryujinx.Headless
[Option("enable-mouse", Required = false, Default = false, HelpText = "Enable or disable mouse support.")]
public bool EnableMouse { get; set; }
+ [Option("enable-press-hotkeys-to-exit", Required = false, Default = 0, HelpText = "press the minus and plus buttons to: 0 -disable, 1 - exit app, 2 - exit game.")]
+ public int SpecialExit { get; set; }
+
[Option("hide-cursor", Required = false, Default = HideCursorMode.OnIdle, HelpText = "Change when the cursor gets hidden.")]
public HideCursorMode HideCursorMode { get; set; }
@@ -414,6 +420,7 @@ namespace Ryujinx.Headless
[Option("ignore-controller-applet", Required = false, Default = false, HelpText = "Enable ignoring the controller applet when your game loses connection to your controller.")]
public bool IgnoreControllerApplet { get; set; }
+
// Values
[Value(0, MetaName = "input", HelpText = "Input to load.", Required = true)]
diff --git a/src/Ryujinx/Headless/Vulkan/VulkanWindow.cs b/src/Ryujinx/Headless/Vulkan/VulkanWindow.cs
index 92caad34e..9819bbc9b 100644
--- a/src/Ryujinx/Headless/Vulkan/VulkanWindow.cs
+++ b/src/Ryujinx/Headless/Vulkan/VulkanWindow.cs
@@ -18,8 +18,9 @@ namespace Ryujinx.Headless
AspectRatio aspectRatio,
bool enableMouse,
HideCursorMode hideCursorMode,
- bool ignoreControllerApplet)
- : base(inputManager, glLogLevel, aspectRatio, enableMouse, hideCursorMode, ignoreControllerApplet)
+ bool ignoreControllerApplet,
+ int specialExitEmulator)
+ : base(inputManager, glLogLevel, aspectRatio, enableMouse, hideCursorMode, ignoreControllerApplet, specialExitEmulator)
{
_glLogLevel = glLogLevel;
}
diff --git a/src/Ryujinx/Headless/WindowBase.cs b/src/Ryujinx/Headless/WindowBase.cs
index d89638cc1..4e4d5a4d4 100644
--- a/src/Ryujinx/Headless/WindowBase.cs
+++ b/src/Ryujinx/Headless/WindowBase.cs
@@ -88,6 +88,7 @@ namespace Ryujinx.Headless
private readonly AspectRatio _aspectRatio;
private readonly bool _enableMouse;
+ private readonly int _specialExitEmulator;
private readonly bool _ignoreControllerApplet;
public WindowBase(
@@ -96,7 +97,8 @@ namespace Ryujinx.Headless
AspectRatio aspectRatio,
bool enableMouse,
HideCursorMode hideCursorMode,
- bool ignoreControllerApplet)
+ bool ignoreControllerApplet,
+ int specialExitEmulator)
{
MouseDriver = new SDL2MouseDriver(hideCursorMode);
_inputManager = inputManager;
@@ -112,6 +114,7 @@ namespace Ryujinx.Headless
_gpuDoneEvent = new ManualResetEvent(false);
_aspectRatio = aspectRatio;
_enableMouse = enableMouse;
+ _specialExitEmulator = specialExitEmulator;
_ignoreControllerApplet = ignoreControllerApplet;
HostUITheme = new HeadlessHostUiTheme();
diff --git a/src/Ryujinx/Input/AvaloniaKeyboard.cs b/src/Ryujinx/Input/AvaloniaKeyboard.cs
index 0b63af2d9..8936513ca 100644
--- a/src/Ryujinx/Input/AvaloniaKeyboard.cs
+++ b/src/Ryujinx/Input/AvaloniaKeyboard.cs
@@ -30,6 +30,11 @@ namespace Ryujinx.Ava.Input
public readonly Key From = from;
}
+ public bool SpecialExit()
+ {
+ return false;
+ }
+
public AvaloniaKeyboard(AvaloniaKeyboardDriver driver, string id, string name)
{
_buttonsUserMapping = [];
diff --git a/src/Ryujinx/Input/AvaloniaMouse.cs b/src/Ryujinx/Input/AvaloniaMouse.cs
index 1aa2d586a..cdcdc2106 100644
--- a/src/Ryujinx/Input/AvaloniaMouse.cs
+++ b/src/Ryujinx/Input/AvaloniaMouse.cs
@@ -13,6 +13,11 @@ namespace Ryujinx.Ava.Input
public string Id => "0";
public string Name => "AvaloniaMouse";
+ public bool SpecialExit()
+ {
+ return false;
+ }
+
public bool IsConnected => true;
public GamepadFeaturesFlag Features => throw new NotImplementedException();
public bool[] Buttons => _driver.PressedButtons;
diff --git a/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs b/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs
index 17b9ea98c..5ae9ef9ef 100644
--- a/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs
+++ b/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs
@@ -1049,6 +1049,7 @@ namespace Ryujinx.Ava.UI.ViewModels
private void InitializeGame()
{
+
RendererHostControl.WindowCreated += RendererHost_Created;
AppHost.StatusUpdatedEvent += Update_StatusBar;
@@ -1058,7 +1059,13 @@ namespace Ryujinx.Ava.UI.ViewModels
AppHost?.Start();
+ if (AppHost?.IsSpecialExit() == true)
+ {
+ Window.ForceExit();
+ }
+
AppHost?.DisposeContext();
+
}
private async Task HandleRelaunch()
diff --git a/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs b/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs
index 2678bbf98..ad82b4d62 100644
--- a/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs
+++ b/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs
@@ -128,6 +128,7 @@ namespace Ryujinx.Ava.UI.ViewModels
public bool EnableDockedMode { get; set; }
public bool EnableKeyboard { get; set; }
public bool EnableMouse { get; set; }
+ public int EnableSpecialExit { get; set; }
public VSyncMode VSyncMode
{
get => _vSyncMode;
@@ -259,6 +260,8 @@ namespace Ryujinx.Ava.UI.ViewModels
public int OpenglDebugLevel { get; set; }
public int MemoryMode { get; set; }
public int BaseStyleIndex { get; set; }
+
+
public int GraphicsBackendIndex
{
get => _graphicsBackendIndex;
@@ -511,6 +514,13 @@ namespace Ryujinx.Ava.UI.ViewModels
EnableDockedMode = config.System.EnableDockedMode;
EnableKeyboard = config.Hid.EnableKeyboard;
EnableMouse = config.Hid.EnableMouse;
+ EnableSpecialExit = config.Hid.SpecialExitEmulator.Value switch
+ {
+ 0 => 0, // "Hotkey 'Exit' is Disabled"
+ 1 => 1, // "Close app. by hotkey"
+ 2 => 2, // "Close game by hotkey"
+ _ => 0
+ };
// Keyboard Hotkeys
KeyboardHotkey = new HotkeyConfig(config.Hid.Hotkeys.Value);
@@ -618,6 +628,13 @@ namespace Ryujinx.Ava.UI.ViewModels
config.System.EnableDockedMode.Value = EnableDockedMode;
config.Hid.EnableKeyboard.Value = EnableKeyboard;
config.Hid.EnableMouse.Value = EnableMouse;
+ config.Hid.SpecialExitEmulator.Value = EnableSpecialExit switch
+ {
+ 0 => 0, // "Hotkey 'Exit' is Disabled",
+ 1 => 1, // "Close app. by hotkey",
+ 2 => 2, // "Close game by hotkey",
+ _ => 0
+ };
// Keyboard Hotkeys
config.Hid.Hotkeys.Value = KeyboardHotkey.GetConfig();
diff --git a/src/Ryujinx/UI/Views/Settings/SettingsInputView.axaml b/src/Ryujinx/UI/Views/Settings/SettingsInputView.axaml
index b0edc51a5..033352548 100644
--- a/src/Ryujinx/UI/Views/Settings/SettingsInputView.axaml
+++ b/src/Ryujinx/UI/Views/Settings/SettingsInputView.axaml
@@ -1,4 +1,4 @@
-
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Ryujinx/UI/Windows/MainWindow.axaml.cs b/src/Ryujinx/UI/Windows/MainWindow.axaml.cs
index 2aaac4098..3487da385 100644
--- a/src/Ryujinx/UI/Windows/MainWindow.axaml.cs
+++ b/src/Ryujinx/UI/Windows/MainWindow.axaml.cs
@@ -45,6 +45,7 @@ namespace Ryujinx.Ava.UI.Windows
internal readonly AvaHostUIHandler UiHandler;
private bool _isLoading;
+ private bool _isExitWithoutConfirm = false;
private bool _applicationsLoadedOnce;
private UserChannelPersistence _userChannelPersistence;
@@ -571,11 +572,11 @@ namespace Ryujinx.Ava.UI.Windows
protected override void OnClosing(WindowClosingEventArgs e)
{
- if (!ViewModel.IsClosing && ViewModel.AppHost != null && ConfigurationState.Instance.ShowConfirmExit)
+ if (!ViewModel.IsClosing && ViewModel.AppHost != null && ConfigurationState.Instance.ShowConfirmExit && !_isExitWithoutConfirm)
{
e.Cancel = true;
- ConfirmExit();
+ ConfirmExit();
return;
}
@@ -616,6 +617,12 @@ namespace Ryujinx.Ava.UI.Windows
base.OnClosing(e);
}
+ public void ForceExit()
+ {
+ _isExitWithoutConfirm = true;
+ Close();
+ }
+
private void ConfirmExit()
{
Dispatcher.UIThread.InvokeAsync(async () =>
diff --git a/src/Ryujinx/Utilities/Configuration/ConfigurationFileFormat.cs b/src/Ryujinx/Utilities/Configuration/ConfigurationFileFormat.cs
index 947dd5c8f..a0cfaec0c 100644
--- a/src/Ryujinx/Utilities/Configuration/ConfigurationFileFormat.cs
+++ b/src/Ryujinx/Utilities/Configuration/ConfigurationFileFormat.cs
@@ -17,7 +17,7 @@ namespace Ryujinx.Ava.Utilities.Configuration
///
/// The current version of the file format
///
- public const int CurrentVersion = 59;
+ public const int CurrentVersion = 60;
///
/// Version of the configuration file format
@@ -366,6 +366,12 @@ namespace Ryujinx.Ava.Utilities.Configuration
///
public bool EnableMouse { get; set; }
+ ///
+ /// Allows you to choose one of several behaviors when pressing hotkeys:
+ /// 0 - Do nothing, 1 - Close the emulator application, 2 - Exit the game.
+ ///
+ public int SpecialExitEmulator { get; set; }
+
///
/// Hotkey Keyboard Bindings
///
diff --git a/src/Ryujinx/Utilities/Configuration/ConfigurationState.Migration.cs b/src/Ryujinx/Utilities/Configuration/ConfigurationState.Migration.cs
index ec66bcaac..f341c5f15 100644
--- a/src/Ryujinx/Utilities/Configuration/ConfigurationState.Migration.cs
+++ b/src/Ryujinx/Utilities/Configuration/ConfigurationState.Migration.cs
@@ -136,6 +136,7 @@ namespace Ryujinx.Ava.Utilities.Configuration
Hid.EnableKeyboard.Value = cff.EnableKeyboard;
Hid.EnableMouse.Value = cff.EnableMouse;
+ Hid.SpecialExitEmulator.Value = cff.SpecialExitEmulator;
Hid.Hotkeys.Value = cff.Hotkeys;
Hid.InputConfig.Value = cff.InputConfig ?? [];
@@ -414,6 +415,10 @@ namespace Ryujinx.Ava.Utilities.Configuration
// This was accidentally enabled by default when it was PRed. That is not what we want,
// so as a compromise users who want to use it will simply need to re-enable it once after updating.
cff.IgnoreApplet = false;
+ }),
+ (60, static cff =>
+ {
+ cff.SpecialExitEmulator = 0;
})
);
}
diff --git a/src/Ryujinx/Utilities/Configuration/ConfigurationState.Model.cs b/src/Ryujinx/Utilities/Configuration/ConfigurationState.Model.cs
index fe5f2c3ad..85bede2d5 100644
--- a/src/Ryujinx/Utilities/Configuration/ConfigurationState.Model.cs
+++ b/src/Ryujinx/Utilities/Configuration/ConfigurationState.Model.cs
@@ -420,6 +420,13 @@ namespace Ryujinx.Ava.Utilities.Configuration
///
public ReactiveObject EnableMouse { get; private set; }
+ ///
+ /// Allows you to choose one of several behaviors when pressing hotkeys:
+ /// 0 - Do nothing, 1 - Close the emulator application, 2 - Exit the game.
+ ///
+ public ReactiveObject SpecialExitEmulator { get; private set; }
+
+
///
/// Hotkey Keyboard Bindings
///
@@ -436,6 +443,7 @@ namespace Ryujinx.Ava.Utilities.Configuration
{
EnableKeyboard = new ReactiveObject();
EnableMouse = new ReactiveObject();
+ SpecialExitEmulator = new ReactiveObject();
Hotkeys = new ReactiveObject();
InputConfig = new ReactiveObject>();
}
diff --git a/src/Ryujinx/Utilities/Configuration/ConfigurationState.cs b/src/Ryujinx/Utilities/Configuration/ConfigurationState.cs
index 95ec62e83..b68f97826 100644
--- a/src/Ryujinx/Utilities/Configuration/ConfigurationState.cs
+++ b/src/Ryujinx/Utilities/Configuration/ConfigurationState.cs
@@ -128,6 +128,7 @@ namespace Ryujinx.Ava.Utilities.Configuration
ShowConsole = UI.ShowConsole,
EnableKeyboard = Hid.EnableKeyboard,
EnableMouse = Hid.EnableMouse,
+ SpecialExitEmulator = Hid.SpecialExitEmulator,
Hotkeys = Hid.Hotkeys,
KeyboardConfig = [],
ControllerConfig = [],
@@ -241,6 +242,7 @@ namespace Ryujinx.Ava.Utilities.Configuration
UI.WindowStartup.WindowMaximized.Value = false;
Hid.EnableKeyboard.Value = false;
Hid.EnableMouse.Value = false;
+ Hid.SpecialExitEmulator.Value = 0;
Hid.Hotkeys.Value = new KeyboardHotkeys
{
ToggleVSyncMode = Key.F1,