From f67cf6a87c07d1192a122cd0b946daf51b635533 Mon Sep 17 00:00:00 2001 From: Jacobwasbeast Date: Mon, 10 Feb 2025 08:30:48 -0600 Subject: [PATCH] Implement application switching Co-authored-by: Alula <6276139+alula@users.noreply.github.com> --- .../Hid/Controller/GamepadInputId.cs | 3 + .../Configuration/Hid/InputConfig.cs | 2 +- .../Hid/LeftJoyconCommonConfig.cs | 1 + .../Hid/RightJoyconCommonConfig.cs | 1 + src/Ryujinx.HLE/HOS/Applets/EventObserver.cs | 57 +- src/Ryujinx.HLE/HOS/Applets/RealApplet.cs | 4 +- src/Ryujinx.HLE/HOS/Applets/WindowSystem.cs | 325 ++++++--- src/Ryujinx.HLE/HOS/Horizon.cs | 60 +- .../ILibraryAppletProxy.cs | 19 +- .../IOverlayAppletProxy.cs | 105 +++ .../ISystemAppletProxy.cs | 4 +- .../ILibraryAppletAccessor.cs | 8 +- .../OverlayAppletProxy/IOverlayFunctions.cs | 175 +++++ .../SystemAppletProxy/IApplicationAccessor.cs | 83 ++- .../SystemAppletProxy/ICommonStateGetter.cs | 47 +- .../SystemAppletProxy/IHomeMenuFunctions.cs | 14 +- .../ILibraryAppletCreator.cs | 10 +- .../IRemoteStorageController.cs | 7 + .../SystemAppletProxy/IWindowController.cs | 2 + .../IAllSystemAppletProxiesService.cs | 15 +- .../IApplicationProxy.cs | 5 +- src/Ryujinx.HLE/HOS/Services/Hid/Hid.cs | 2 +- .../Services/Hid/Types/Npad/ControllerKeys.cs | 4 +- .../Hid/Types/SharedMemory/Common/RingLifo.cs | 2 +- .../Hid/Types/SharedMemory/Npad/NpadButton.cs | 9 +- .../Hid/Types/SharedMemory/SharedMemory.cs | 3 + .../HOS/Services/Npns/INpnsUser.cs | 29 +- .../Ns/IApplicationManagerInterface.cs | 35 +- .../Services/Ns/IDynamicRightsInterface.cs | 12 + .../Olsc/IOlscServiceForSystemService.cs | 10 + .../IManagerDisplayService.cs | 2 +- .../RootService/IApplicationDisplayService.cs | 2 +- .../HOS/SystemState/AppletStateMgr.cs | 684 ++++++++---------- src/Ryujinx.HLE/Switch.cs | 22 + src/Ryujinx.Horizon/Ovln/Ipc/Receiver.cs | 65 ++ .../Ovln/Ipc/ReceiverService.cs | 13 + src/Ryujinx.Horizon/Ovln/Ipc/Sender.cs | 13 +- src/Ryujinx.Horizon/Sdk/Ovln/IReceiver.cs | 12 + src/Ryujinx.Horizon/Sdk/Ovln/SourceName.cs | 10 + src/Ryujinx.Input.SDL2/SDL2Gamepad.cs | 2 + src/Ryujinx.Input.SDL2/SDL2Keyboard.cs | 2 + src/Ryujinx.Input/GamepadButtonInputId.cs | 3 + src/Ryujinx.Input/HLE/NpadController.cs | 4 +- src/Ryujinx.Input/HLE/NpadManager.cs | 12 + src/Ryujinx/AppHost.cs | 1 + src/Ryujinx/Assets/locales.json | 50 ++ src/Ryujinx/Headless/HeadlessRyujinx.Init.cs | 4 + src/Ryujinx/Input/AvaloniaKeyboard.cs | 2 + .../UI/Models/Input/GamepadInputConfig.cs | 8 +- .../UI/Models/Input/KeyboardInputConfig.cs | 6 + .../UI/ViewModels/Input/InputViewModel.cs | 4 + .../UI/ViewModels/MainWindowViewModel.cs | 2 + .../UI/Views/Input/ControllerInputView.axaml | 30 + .../Views/Input/ControllerInputView.axaml.cs | 6 + .../UI/Views/Input/KeyboardInputView.axaml | 30 + .../UI/Views/Input/KeyboardInputView.axaml.cs | 8 +- .../ConfigurationState.Migration.cs | 2 + .../Configuration/ConfigurationState.cs | 2 + 58 files changed, 1458 insertions(+), 596 deletions(-) create mode 100644 src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/IOverlayAppletProxy.cs create mode 100644 src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/OverlayAppletProxy/IOverlayFunctions.cs create mode 100644 src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IRemoteStorageController.cs create mode 100644 src/Ryujinx.Horizon/Ovln/Ipc/Receiver.cs create mode 100644 src/Ryujinx.Horizon/Sdk/Ovln/IReceiver.cs create mode 100644 src/Ryujinx.Horizon/Sdk/Ovln/SourceName.cs diff --git a/src/Ryujinx.Common/Configuration/Hid/Controller/GamepadInputId.cs b/src/Ryujinx.Common/Configuration/Hid/Controller/GamepadInputId.cs index 1380813b0..c8cf945c1 100644 --- a/src/Ryujinx.Common/Configuration/Hid/Controller/GamepadInputId.cs +++ b/src/Ryujinx.Common/Configuration/Hid/Controller/GamepadInputId.cs @@ -37,6 +37,9 @@ namespace Ryujinx.Common.Configuration.Hid.Controller Guide, Misc1, + Home = Guide, + Capture = Misc1, + // Xbox Elite paddle Paddle1, Paddle2, diff --git a/src/Ryujinx.Common/Configuration/Hid/InputConfig.cs b/src/Ryujinx.Common/Configuration/Hid/InputConfig.cs index a93b721ad..061c2fe44 100644 --- a/src/Ryujinx.Common/Configuration/Hid/InputConfig.cs +++ b/src/Ryujinx.Common/Configuration/Hid/InputConfig.cs @@ -10,7 +10,7 @@ namespace Ryujinx.Common.Configuration.Hid /// /// The current version of the input file format /// - public const int CurrentVersion = 1; + public const int CurrentVersion = 2; public int Version { get; set; } diff --git a/src/Ryujinx.Common/Configuration/Hid/LeftJoyconCommonConfig.cs b/src/Ryujinx.Common/Configuration/Hid/LeftJoyconCommonConfig.cs index 140453555..f98aedcad 100644 --- a/src/Ryujinx.Common/Configuration/Hid/LeftJoyconCommonConfig.cs +++ b/src/Ryujinx.Common/Configuration/Hid/LeftJoyconCommonConfig.cs @@ -7,6 +7,7 @@ namespace Ryujinx.Common.Configuration.Hid public TButton ButtonZl { get; set; } public TButton ButtonSl { get; set; } public TButton ButtonSr { get; set; } + public TButton ButtonCapture { get; set; } public TButton DpadUp { get; set; } public TButton DpadDown { get; set; } public TButton DpadLeft { get; set; } diff --git a/src/Ryujinx.Common/Configuration/Hid/RightJoyconCommonConfig.cs b/src/Ryujinx.Common/Configuration/Hid/RightJoyconCommonConfig.cs index 1fb99104f..4002e2482 100644 --- a/src/Ryujinx.Common/Configuration/Hid/RightJoyconCommonConfig.cs +++ b/src/Ryujinx.Common/Configuration/Hid/RightJoyconCommonConfig.cs @@ -11,5 +11,6 @@ namespace Ryujinx.Common.Configuration.Hid public TButton ButtonB { get; set; } public TButton ButtonY { get; set; } public TButton ButtonA { get; set; } + public TButton ButtonHome { get; set; } } } diff --git a/src/Ryujinx.HLE/HOS/Applets/EventObserver.cs b/src/Ryujinx.HLE/HOS/Applets/EventObserver.cs index b7fb73cdd..35e33e939 100644 --- a/src/Ryujinx.HLE/HOS/Applets/EventObserver.cs +++ b/src/Ryujinx.HLE/HOS/Applets/EventObserver.cs @@ -4,6 +4,7 @@ using Ryujinx.HLE.HOS.Kernel.Process; using Ryujinx.HLE.HOS.Kernel.Threading; using Ryujinx.Horizon; using Ryujinx.Horizon.Sdk.OsTypes; +using System; using System.Collections.Generic; using System.Threading; @@ -17,8 +18,8 @@ namespace Ryujinx.HLE.HOS.Applets private WindowSystem _windowSystem; // Guest event handle to wake up the event loop - internal SystemEventType _wakeupEvent; - internal MultiWaitHolder _wakeupHolder; + private SystemEventType _wakeupEvent; + private MultiWaitHolder _wakeupHolder; private KWritableEvent _wakeupEventObj; // List of owned process holders @@ -195,32 +196,42 @@ namespace Ryujinx.HLE.HOS.Applets private void ThreadFunc() { - HorizonStatic.Register( - default, - _system.KernelContext.Syscall, - null, - _thread.ThreadContext, - (int)_thread.ThreadContext.GetX(1)); - - // lock (_lock) + try { - Os.CreateSystemEvent(out _wakeupEvent, EventClearMode.ManualClear, true).AbortOnFailure(); - _wakeupEventObj = _thread.Owner.HandleTable.GetObject(Os.GetWritableHandleOfSystemEvent(ref _wakeupEvent)); + HorizonStatic.Register( + default, + _system.KernelContext.Syscall, + null, + _thread.ThreadContext, + (int)_thread.ThreadContext.GetX(1)); - _wakeupHolder = new MultiWaitHolderOfInterProcessEvent(_wakeupEvent.InterProcessEvent); - _wakeupHolder.UserData = UserDataTag.WakeupEvent; - _multiWait.LinkMultiWaitHolder(_wakeupHolder); - } - - while (!_cts.Token.IsCancellationRequested) - { - var holder = WaitSignaled(); - if (holder == null) + lock (_lock) { - break; + Os.CreateSystemEvent(out _wakeupEvent, EventClearMode.ManualClear, true).AbortOnFailure(); + _wakeupEventObj = _thread.Owner.HandleTable.GetObject( + Os.GetWritableHandleOfSystemEvent(ref _wakeupEvent)); + + _wakeupHolder = new MultiWaitHolderOfInterProcessEvent(_wakeupEvent.InterProcessEvent) + { + UserData = UserDataTag.WakeupEvent + }; + _multiWait.LinkMultiWaitHolder(_wakeupHolder); } - Process(holder); + while (!_cts.Token.IsCancellationRequested) + { + var holder = WaitSignaled(); + if (holder == null) + { + break; + } + + Process(holder); + } + } + catch (Exception ex) + { + Logger.Error?.Print(LogClass.ServiceAm, $"EventObserver thread encountered an exception: {ex}"); } } } diff --git a/src/Ryujinx.HLE/HOS/Applets/RealApplet.cs b/src/Ryujinx.HLE/HOS/Applets/RealApplet.cs index cb0c7f169..ec2f5d9cc 100644 --- a/src/Ryujinx.HLE/HOS/Applets/RealApplet.cs +++ b/src/Ryujinx.HLE/HOS/Applets/RealApplet.cs @@ -120,6 +120,7 @@ namespace Ryujinx.HLE.HOS.Applets internal bool IsInteractable = true; internal bool WindowVisible = true; internal bool ExitLocked = false; + internal bool IsApplication { get; } internal AppletStateMgr AppletState { get; private set; } public event EventHandler AppletStateChanged; @@ -132,10 +133,11 @@ namespace Ryujinx.HLE.HOS.Applets public RealApplet(ulong pid, bool isApplication, Horizon system) { _system = system; - AppletState = new AppletStateMgr(system, isApplication); + AppletState = new AppletStateMgr(system); ProcessHandle = _system.KernelContext.Processes[pid]; AppletResourceUserId = ProcessHandle.Pid; AppletId = GetAppletIdFromProgramId(ProcessHandle.TitleId); + IsApplication = isApplication; } public void RegisterChild(RealApplet applet) diff --git a/src/Ryujinx.HLE/HOS/Applets/WindowSystem.cs b/src/Ryujinx.HLE/HOS/Applets/WindowSystem.cs index 7e4db975e..7aacec764 100644 --- a/src/Ryujinx.HLE/HOS/Applets/WindowSystem.cs +++ b/src/Ryujinx.HLE/HOS/Applets/WindowSystem.cs @@ -7,6 +7,7 @@ using System.Linq; using Ryujinx.Horizon.Sdk.Applet; using Ryujinx.Common; using Ryujinx.HLE.HOS.Applets.Types; +using System.Threading; namespace Ryujinx.HLE.HOS.Applets { @@ -14,21 +15,20 @@ namespace Ryujinx.HLE.HOS.Applets { private Horizon _system; private readonly object _lock = new(); - EventObserver _eventObserver = null; + private EventObserver _eventObserver = null; - // Foreground roots - RealApplet _homeMenu = null; - RealApplet _overlayDisp = null; - RealApplet _application = null; + // Foreground roots. + private RealApplet _homeMenu = null; + private RealApplet _overlayDisp = null; + // Removed single application field to allow multiple applications. - // Home menu state - bool _homeMenuForegroundLocked = false; - RealApplet _foregroundRequestedApplet = null; + // Home menu state. + private bool _homeMenuForegroundLocked = false; + private RealApplet _foregroundRequestedApplet = null; - - // aruid -> applet map - Dictionary _applets = new(); - List _rootApplets = new(); + // aruid -> applet map. + private Dictionary _applets = new(); + private List _rootApplets = new(); internal ButtonPressTracker ButtonPressTracker { get; } @@ -60,6 +60,7 @@ namespace Ryujinx.HLE.HOS.Applets return; } + // If no foreground applet is explicitly requested, choose the last root applet. if (_foregroundRequestedApplet == null && _rootApplets.Count != 0) { _foregroundRequestedApplet = _rootApplets.Last(); @@ -72,6 +73,9 @@ namespace Ryujinx.HLE.HOS.Applets } } + /// + /// Tracks a new process as an applet. + /// internal RealApplet TrackProcess(ulong pid, ulong callerPid, bool isApplication) { lock (_lock) @@ -106,6 +110,9 @@ namespace Ryujinx.HLE.HOS.Applets } } + /// + /// Registers the applet in the global tracking data structures. + /// private void TrackApplet(RealApplet applet, bool isApplication) { if (_applets.ContainsKey(applet.AppletResourceUserId)) @@ -122,30 +129,33 @@ namespace Ryujinx.HLE.HOS.Applets { _overlayDisp = applet; } - else if (isApplication) - { - _application = applet; - } + // For application applets, we no longer assign a unique field. + // They are simply tracked as root applets (if callerPid == 0) and in the _applets dictionary. _applets[applet.AppletResourceUserId] = applet; _eventObserver.TrackAppletProcess(applet); - if (_applets.Count == 1 || applet.AppletId == RealAppletId.SystemAppletMenu || applet.AppletId == RealAppletId.OverlayApplet) + // If this is the first applet being tracked, or if it is one of the special system applets, + // perform initial setup. + if (_applets.Count == 1 || + applet.AppletId == RealAppletId.SystemAppletMenu || + applet.AppletId == RealAppletId.OverlayApplet) { SetupFirstApplet(applet); + _foregroundRequestedApplet = applet; + applet.AppletState.SetFocus(false); } - // _foregroundRequestedApplet = applet; - // applet.AppletState.SetFocusState(FocusState.InFocus); - _eventObserver.RequestUpdate(); } + /// + /// Performs initial setup for the first tracked applet. + /// private void SetupFirstApplet(RealApplet applet) { if (applet.AppletId == RealAppletId.SystemAppletMenu) { - //applet.AppletState.SetFocusHandlingMode(false); applet.AppletState.SetOutOfFocusSuspendingEnabled(false); RequestHomeMenuToGetForeground(); } @@ -158,7 +168,7 @@ namespace Ryujinx.HLE.HOS.Applets { applet.AppletState.SetFocusState(FocusState.InFocus); _foregroundRequestedApplet = applet; - RequestApplicationToGetForeground(); + RequestApplicationToGetForeground(applet.ProcessHandle.Pid); } applet.UpdateSuspensionStateLocked(true); @@ -166,70 +176,94 @@ namespace Ryujinx.HLE.HOS.Applets internal RealApplet GetByAruId(ulong aruid) { - // lock (_lock) + if (_applets.TryGetValue(aruid, out RealApplet applet)) { - if (_applets.TryGetValue(aruid, out RealApplet applet)) - { - return applet; - } - - return null; + return applet; } + + return null; } + /// + /// Returns the current foreground application. + /// If none is explicitly set, the first tracked application is returned. + /// internal RealApplet GetMainApplet() { - // lock (_lock) + lock (_lock) { - if (_application != null) + if (_foregroundRequestedApplet != null && _foregroundRequestedApplet.IsApplication) { - if (_applets.TryGetValue(_application.AppletResourceUserId, out RealApplet applet)) - { - return applet; - } + return _foregroundRequestedApplet; } - return null; + return _rootApplets.FirstOrDefault(applet => applet.IsApplication); } } internal void RequestHomeMenuToGetForeground() { - // lock (_lock) - { - _foregroundRequestedApplet = _homeMenu; - } - + _foregroundRequestedApplet = _homeMenu; _eventObserver.RequestUpdate(); } - internal void RequestApplicationToGetForeground() + /// + /// Requests that the home menu be focused. + /// The PID provided must match the home menu’s PID. + /// + internal void RequestHomeMenuToGetForeground(ulong pid) { - // lock (_lock) + lock (_lock) { - _foregroundRequestedApplet = _application; + if (_homeMenu != null && _homeMenu.ProcessHandle.Pid == pid) + { + _foregroundRequestedApplet = _homeMenu; + } + else + { + Logger.Warning?.Print(LogClass.ServiceAm, $"RequestHomeMenuToGetForeground: Provided pid {pid} does not match the home menu."); + } } + _eventObserver.RequestUpdate(); + } + /// + /// Requests that an application be focused. + /// The PID provided must belong to an application applet. + /// + internal void RequestApplicationToGetForeground(ulong pid) + { + lock (_lock) + { + if (_applets.TryGetValue(pid, out var applet)) + { + _foregroundRequestedApplet = applet; + } + else + { + if (pid == _homeMenu?.ProcessHandle.Pid) + { + _foregroundRequestedApplet.AppletState.SetFocusForce(false,true); + _foregroundRequestedApplet = _homeMenu; + } + else + { + Logger.Warning?.Print(LogClass.ServiceAm, $"RequestApplicationToGetForeground: Provided pid {pid} is not an application applet."); + } + } + } _eventObserver.RequestUpdate(); } internal void RequestLockHomeMenuIntoForeground() { - // lock (_lock) - { - _homeMenuForegroundLocked = true; - } - + _homeMenuForegroundLocked = true; _eventObserver.RequestUpdate(); } internal void RequestUnlockHomeMenuFromForeground() { - // lock (_lock) - { - _homeMenuForegroundLocked = false; - } - + _homeMenuForegroundLocked = false; _eventObserver.RequestUpdate(); } @@ -245,48 +279,39 @@ namespace Ryujinx.HLE.HOS.Applets internal void OnOperationModeChanged() { - // lock (_lock) + foreach (var (_, applet) in _applets) { - foreach (var (aruid, applet) in _applets) + lock (applet.Lock) { - lock (applet.Lock) - { - applet.AppletState.OnOperationAndPerformanceModeChanged(); - } + applet.AppletState.OnOperationAndPerformanceModeChanged(); } } } internal void OnExitRequested() { - // lock (_lock) + foreach (var (_, applet) in _applets) { - foreach (var (aruid, applet) in _applets) + lock (applet.Lock) { - lock (applet.Lock) - { - applet.AppletState.OnExitRequested(); - } + applet.AppletState.OnExitRequested(); } } } internal void OnSystemButtonPress(SystemButtonType type) { - // lock (_lock) + switch (type) { - switch (type) - { - case SystemButtonType.PerformHomeButtonShortPressing: - SendButtonAppletMessageLocked(AppletMessage.DetectShortPressingHomeButton); - break; - case SystemButtonType.PerformHomeButtonLongPressing: - SendButtonAppletMessageLocked(AppletMessage.DetectLongPressingHomeButton); - break; - case SystemButtonType.PerformCaptureButtonShortPressing: - SendButtonAppletMessageLocked(AppletMessage.DetectShortPressingCaptureButton); - break; - } + case SystemButtonType.PerformHomeButtonShortPressing: + SendButtonAppletMessageLocked(AppletMessage.DetectShortPressingHomeButton); + break; + case SystemButtonType.PerformHomeButtonLongPressing: + SendButtonAppletMessageLocked(AppletMessage.DetectLongPressingHomeButton); + break; + case SystemButtonType.PerformCaptureButtonShortPressing: + SendButtonAppletMessageLocked(AppletMessage.DetectShortPressingCaptureButton); + break; } } @@ -297,6 +322,16 @@ namespace Ryujinx.HLE.HOS.Applets lock (_homeMenu.Lock) { _homeMenu.AppletState.PushUnorderedMessage(message); + if (message == AppletMessage.DetectShortPressingHomeButton) + { + foreach (var applet in _applets.Values) + { + if (applet != _homeMenu) + { + applet.ProcessHandle.SetActivity(true); + } + } + } } } @@ -309,9 +344,13 @@ namespace Ryujinx.HLE.HOS.Applets } } + /// + /// Removes terminated applets from tracking. + /// private void PruneTerminatedAppletsLocked() { - foreach (var (aruid, applet) in _applets) + // We need to iterate over a copy of the dictionary keys because we might remove items. + foreach (var (aruid, applet) in _applets.ToList()) { lock (applet.Lock) { @@ -320,12 +359,14 @@ namespace Ryujinx.HLE.HOS.Applets continue; } + // If the applet has child applets still, terminate them first. if (applet.ChildApplets.Count != 0) { TerminateChildAppletsLocked(applet); continue; } + // If this applet was started by another, remove it from its caller’s child list. if (applet.CallerApplet != null) { applet.CallerApplet.ChildApplets.Remove(applet); @@ -343,10 +384,14 @@ namespace Ryujinx.HLE.HOS.Applets _foregroundRequestedApplet = null; } - if (applet == _application) + // For application applets, clear the foreground reference if necessary and + // notify the home menu that an application has exited. + if (applet.IsApplication) { - _application = null; - _foregroundRequestedApplet = null; + if (_foregroundRequestedApplet == applet) + { + _foregroundRequestedApplet = null; + } if (_homeMenu != null) { @@ -363,9 +408,26 @@ namespace Ryujinx.HLE.HOS.Applets } } + /// + /// Terminates any child applets of the specified parent. + /// + private void TerminateChildAppletsLocked(RealApplet parent) + { + foreach (var child in parent.ChildApplets) + { + if (child.ProcessHandle.State != ProcessState.Exited) + { + child.ProcessHandle.Terminate(); + child.TerminateResult = (ResultCode)Services.Am.ResultCode.LibraryAppletTerminated; + } + } + } + + /// + /// If the home menu is locked into the foreground, ensure it remains in front. + /// private bool LockHomeMenuIntoForegroundLocked() { - // If the home menu is not locked into the foreground, then there's nothing to do. if (_homeMenu == null || !_homeMenuForegroundLocked) { _homeMenuForegroundLocked = false; @@ -387,18 +449,9 @@ namespace Ryujinx.HLE.HOS.Applets return true; } - private void TerminateChildAppletsLocked(RealApplet parent) - { - foreach (var child in parent.ChildApplets) - { - if (child.ProcessHandle.State != ProcessState.Exited) - { - child.ProcessHandle.Terminate(); - child.TerminateResult = (ResultCode)Services.Am.ResultCode.LibraryAppletTerminated; - } - } - } - + /// + /// Updates the state of the specified applet and its children. + /// private void UpdateAppletStateLocked(RealApplet applet, bool isForeground) { if (applet == null) @@ -427,7 +480,7 @@ namespace Ryujinx.HLE.HOS.Applets return false; }); - // TODO: Update visibility state + // TODO: Update visibility state if needed. applet.SetInteractibleLocked(isForeground && applet.WindowVisible); @@ -445,13 +498,81 @@ namespace Ryujinx.HLE.HOS.Applets applet.UpdateSuspensionStateLocked(true); } - Logger.Info?.Print(LogClass.ServiceAm, $"Updating applet state for {applet.AppletId}: visible={applet.WindowVisible}, foreground={isForeground}, obscured={isObscured}, reqFState={applet.AppletState.RequestedFocusState}, ackFState={applet.AppletState.AcknowledgedFocusState}, runnable={applet.AppletState.IsRunnable()}"); - - - // Recurse into child applets + Logger.Info?.Print(LogClass.ServiceAm, + $"Updating applet state for {applet.AppletId}: visible={applet.WindowVisible}, foreground={isForeground}, obscured={isObscured}, reqFState={applet.AppletState.RequestedFocusState}, ackFState={applet.AppletState.AcknowledgedFocusState}, runnable={applet.AppletState.IsRunnable()}"); + + // Recurse into child applets. foreach (var child in applet.ChildApplets) { - UpdateAppletStateLocked(child, isForeground); + if (child == _foregroundRequestedApplet) + { + UpdateAppletStateLocked(child, true); + } + else + { + UpdateAppletStateLocked(child, isForeground); + } + } + } + } + + /// + /// Returns the process identifier of the currently focused applet. + /// + public ulong GetFocusedApp() + { + if (_foregroundRequestedApplet == null) + { + return _homeMenu == null ? 0 : _homeMenu.ProcessHandle.Pid; + } + return _foregroundRequestedApplet.ProcessHandle.Pid; + } + + internal RealApplet GetFirstApplet() + { + lock (_lock) + { + if (_applets.Count == 0) + { + return null; + } + ulong oldestPID = _applets.Keys.Min(); + return _applets[oldestPID]; + } + } + + internal bool IsFocusedApplet(RealApplet applet) + { + return _foregroundRequestedApplet == applet; + } + + internal RealApplet GetApplicationApplet() + { + RealApplet applet = null; + lock (_lock) + { + foreach (var (_, value) in _applets) + { + if (value.IsApplication) + { + applet = value; + break; + } + } + } + + return applet; + } + + public void RemoveProcess(ulong processHandlePid) + { + lock (_lock) + { + if (_applets.TryGetValue(processHandlePid, out RealApplet applet)) + { + _applets.Remove(processHandlePid); + _rootApplets.Remove(applet); + _eventObserver.RequestUpdate(); } } } diff --git a/src/Ryujinx.HLE/HOS/Horizon.cs b/src/Ryujinx.HLE/HOS/Horizon.cs index f04bf99ad..747214729 100644 --- a/src/Ryujinx.HLE/HOS/Horizon.cs +++ b/src/Ryujinx.HLE/HOS/Horizon.cs @@ -64,31 +64,15 @@ namespace Ryujinx.HLE.HOS internal PerformanceState PerformanceState { get; private set; } internal AppletStateMgr IntialAppletState { get; private set; } - - internal AppletStateMgr AppletState + + internal AppletStateMgr GetAppletState(ulong processId) { - get + if (WindowSystem?.GetByAruId(processId) != null) { - ulong processId = 0; - if (Device?.Processes?.ActiveApplication != null) - { - processId = Device.Processes.ActiveApplication.ProcessId; - } - if (WindowSystem?.GetByAruId(processId) != null) - { - Logger.Info?.Print(LogClass.Application, "Real applet instance found"); - return WindowSystem.GetByAruId(processId).AppletState; - } + return WindowSystem.GetByAruId(processId).AppletState; + } - return IntialAppletState; - } - set - { - if (value != null) - { - IntialAppletState = value; - } - } + return IntialAppletState; } internal WindowSystem WindowSystem { get; private set; } @@ -122,6 +106,9 @@ namespace Ryujinx.HLE.HOS internal CaptureManager CaptureManager { get; private set; } internal KEvent VsyncEvent { get; private set; } + + internal KEvent GeneralChannelEvent { get; private set; } + internal Queue GeneralChannelData { get; private set; } = new(); internal KEvent DisplayResolutionChangeEvent { get; private set; } @@ -204,16 +191,13 @@ namespace Ryujinx.HLE.HOS AppletCaptureBufferTransfer = new KTransferMemory(KernelContext, appletCaptureBufferStorage); - AppletState = new AppletStateMgr(this, true); - WindowSystem = new WindowSystem(this); EventObserver = new EventObserver(this, WindowSystem); - - AppletState.SetFocus(true); VsyncEvent = new KEvent(KernelContext); DisplayResolutionChangeEvent = new KEvent(KernelContext); + GeneralChannelEvent = new KEvent(KernelContext); SharedFontManager = new SharedFontManager(device, fontStorage); AccountManager = device.Configuration.AccountManager; @@ -362,13 +346,24 @@ namespace Ryujinx.HLE.HOS public void ReturnFocus() { - AppletState.SetFocus(true); + GetAppletState(WindowSystem.GetFocusedApp()).SetFocus(true); } public void SimulateWakeUpMessage() { - // AppletState.Messages.Enqueue(AppletMessage.Resume); - // AppletState.MessageEvent.ReadableEvent.Signal(); + PushToGeneralChannel(new byte[] { + 0x53, 0x41, 0x4D, 0x53, 0x01, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + }); + } + + public void PushToGeneralChannel(byte[] data) + { + if (data.Length > 0) + { + GeneralChannelData.Enqueue(data); + GeneralChannelEvent.ReadableEvent.Signal(); + } } public void ScanAmiibo(int nfpDeviceId, string amiiboId, bool useRandomUuid) @@ -530,5 +525,12 @@ namespace Ryujinx.HLE.HOS } IsPaused = pause; } + + public void SetupFirst(ulong ProgramId) + { + bool isApp = ProgramId > 0x01000000000007FF; + RealApplet app = WindowSystem.TrackProcess(ProgramId, 0, isApp); + app.AppletState.SetFocusForce(true); + } } } diff --git a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/ILibraryAppletProxy.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/ILibraryAppletProxy.cs index 04bde3d74..aa43e93a2 100644 --- a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/ILibraryAppletProxy.cs +++ b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/ILibraryAppletProxy.cs @@ -1,3 +1,5 @@ +using Ryujinx.Common.Logging; +using Ryujinx.HLE.HOS.Applets; using Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.LibraryAppletProxy; using Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.SystemAppletProxy; @@ -6,17 +8,24 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService class ILibraryAppletProxy : IpcService { private readonly ulong _pid; + private readonly ServiceCtx _context; - public ILibraryAppletProxy(ulong pid) + public ILibraryAppletProxy(ServiceCtx context, ulong pid) { + _context = context; _pid = pid; } + private RealApplet GetApplet() + { + return _context.Device.System.WindowSystem.GetByAruId(_pid); + } + [CommandCmif(0)] // GetCommonStateGetter() -> object public ResultCode GetCommonStateGetter(ServiceCtx context) { - MakeObject(context, new ICommonStateGetter(context)); + MakeObject(context, new ICommonStateGetter(context, _pid)); return ResultCode.Success; } @@ -70,7 +79,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService // GetLibraryAppletCreator() -> object public ResultCode GetLibraryAppletCreator(ServiceCtx context) { - MakeObject(context, new ILibraryAppletCreator()); + MakeObject(context, new ILibraryAppletCreator(context, _pid)); return ResultCode.Success; } @@ -92,12 +101,13 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService return ResultCode.Success; } - + [CommandCmif(22)] // GetHomeMenuFunctions() -> object public ResultCode GetHomeMenuFunctions(ServiceCtx context) { MakeObject(context, new IHomeMenuFunctions(context.Device.System)); + return ResultCode.Success; } @@ -106,6 +116,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService public ResultCode GetGlobalStateController(ServiceCtx context) { MakeObject(context, new IGlobalStateController(context)); + return ResultCode.Success; } diff --git a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/IOverlayAppletProxy.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/IOverlayAppletProxy.cs new file mode 100644 index 000000000..091d63d35 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/IOverlayAppletProxy.cs @@ -0,0 +1,105 @@ +using Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.OverlayAppletProxy; +using Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.SystemAppletProxy; + +namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService +{ + class IOverlayAppletProxy : IpcService + { + private readonly ulong _pid; + + public IOverlayAppletProxy(ulong pid) + { + _pid = pid; + } + + [CommandCmif(0)] + // GetCommonStateGetter() -> object + public ResultCode GetCommonStateGetter(ServiceCtx context) + { + MakeObject(context, new ICommonStateGetter(context, _pid)); + + return ResultCode.Success; + } + + [CommandCmif(1)] + // GetSelfController() -> object + public ResultCode GetSelfController(ServiceCtx context) + { + MakeObject(context, new ISelfController(context, _pid)); + + return ResultCode.Success; + } + + [CommandCmif(2)] + // GetWindowController() -> object + public ResultCode GetWindowController(ServiceCtx context) + { + MakeObject(context, new IWindowController(_pid)); + + return ResultCode.Success; + } + + [CommandCmif(3)] + // GetAudioController() -> object + public ResultCode GetAudioController(ServiceCtx context) + { + MakeObject(context, new IAudioController()); + + return ResultCode.Success; + } + + [CommandCmif(4)] + // GetDisplayController() -> object + public ResultCode GetDisplayController(ServiceCtx context) + { + MakeObject(context, new IDisplayController(context)); + + return ResultCode.Success; + } + + [CommandCmif(11)] + // GetLibraryAppletCreator() -> object + public ResultCode GetLibraryAppletCreator(ServiceCtx context) + { + MakeObject(context, new ILibraryAppletCreator(context, _pid)); + + return ResultCode.Success; + } + + [CommandCmif(20)] + // GetOverlayFunctions() -> object + public ResultCode GetOverlayFunctions(ServiceCtx context) + { + MakeObject(context, new IOverlayFunctions(context.Device.System)); + + return ResultCode.Success; + } + + [CommandCmif(21)] + // GetAppletCommonFunctions() -> object + public ResultCode GetAppletCommonFunctions(ServiceCtx context) + { + MakeObject(context, new IAppletCommonFunctions()); + + return ResultCode.Success; + } + + [CommandCmif(23)] + // GetGlobalStateController() -> object + public ResultCode GetGlobalStateController(ServiceCtx context) + { + MakeObject(context, new IGlobalStateController(context)); + + return ResultCode.Success; + } + + [CommandCmif(1000)] + // GetDebugFunctions() -> object + public ResultCode GetDebugFunctions(ServiceCtx context) + { + MakeObject(context, new IDebugFunctions()); + + return ResultCode.Success; + } + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/ISystemAppletProxy.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/ISystemAppletProxy.cs index f83e362c0..3a66bdf9c 100644 --- a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/ISystemAppletProxy.cs +++ b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/ISystemAppletProxy.cs @@ -15,7 +15,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService // GetCommonStateGetter() -> object public ResultCode GetCommonStateGetter(ServiceCtx context) { - MakeObject(context, new ICommonStateGetter(context)); + MakeObject(context, new ICommonStateGetter(context,_pid)); return ResultCode.Success; } @@ -60,7 +60,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService // GetLibraryAppletCreator() -> object public ResultCode GetLibraryAppletCreator(ServiceCtx context) { - MakeObject(context, new ILibraryAppletCreator()); + MakeObject(context, new ILibraryAppletCreator(context,_pid)); return ResultCode.Success; } diff --git a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletCreator/ILibraryAppletAccessor.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletCreator/ILibraryAppletAccessor.cs index ffeddbb72..dd178082f 100644 --- a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletCreator/ILibraryAppletAccessor.cs +++ b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/LibraryAppletCreator/ILibraryAppletAccessor.cs @@ -26,9 +26,11 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Lib private int _interactiveOutDataEventHandle; private int _indirectLayerHandle; + public ulong _pid = 0; - public ILibraryAppletAccessor(AppletId appletId, Horizon system) + public ILibraryAppletAccessor(AppletId appletId, Horizon system, ulong processId) { + _pid = processId; _kernelContext = system.KernelContext; _stateChangedEvent = new KEvent(system.KernelContext); @@ -226,7 +228,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Lib { Horizon horizon = _kernelContext.Device.System; - _indirectLayerHandle = horizon.AppletState.IndirectLayerHandles.Add(_applet); + _indirectLayerHandle = horizon.GetAppletState(_pid).IndirectLayerHandles.Add(_applet); context.ResponseData.Write((ulong)_indirectLayerHandle); @@ -255,7 +257,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Lib Horizon horizon = _kernelContext.Device.System; - horizon.AppletState.IndirectLayerHandles.Delete(_indirectLayerHandle); + horizon.GetAppletState(_pid).IndirectLayerHandles.Delete(_indirectLayerHandle); } } } diff --git a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/OverlayAppletProxy/IOverlayFunctions.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/OverlayAppletProxy/IOverlayFunctions.cs new file mode 100644 index 000000000..116b430e6 --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/OverlayAppletProxy/IOverlayFunctions.cs @@ -0,0 +1,175 @@ +using LibHac.Util; +using Ryujinx.Common.Logging; +using Ryujinx.HLE.HOS.Ipc; +using Ryujinx.Horizon.Common; +using System; + +namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.OverlayAppletProxy +{ + // Cmd Name + // 0 #BeginToWatchShortHomeButtonMessage + // 1 #EndToWatchShortHomeButtonMessage + // 2 #GetApplicationIdForLogo + // 3 #SetGpuTimeSliceBoost + // 4 [2.0.0+] #SetAutoSleepTimeAndDimmingTimeEnabled + // 5 [2.0.0+] #TerminateApplicationAndSetReason + // 6 [3.0.0+] #SetScreenShotPermissionGlobally + // 10 [6.0.0+] #StartShutdownSequenceForOverlay + // 11 [6.0.0+] #StartRebootSequenceForOverlay + // 20 [8.0.0+] #SetHandlingHomeButtonShortPressedEnabled + // 21 [14.0.0+] SetHandlingTouchScreenInputEnabled + // 30 [9.0.0+] #SetHealthWarningShowingState + // 31 [10.0.0+] #IsHealthWarningRequired + // 40 [18.0.0+] GetApplicationNintendoLogo + // 41 [18.0.0+] GetApplicationStartupMovie + // 50 [19.0.0+] SetGpuTimeSliceBoostForApplication + // 60 [19.0.0+] + // 90 [7.0.0+] #SetRequiresGpuResourceUse + // 101 [5.0.0+] #BeginToObserveHidInputForDevelop + class IOverlayFunctions : IpcService + { + + public IOverlayFunctions(Horizon system) + { + } + + [CommandCmif(0)] + // BeginToWatchShortHomeButtonMessage() + public ResultCode BeginToWatchShortHomeButtonMessage(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceAm); + + return ResultCode.Success; + } + + [CommandCmif(1)] + // EndToWatchShortHomeButtonMessage() + public ResultCode EndToWatchShortHomeButtonMessage(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceAm); + + return ResultCode.Success; + } + + [CommandCmif(2)] + // GetApplicationIdForLogo() -> nn::ncm::ApplicationId + public ResultCode GetApplicationIdForLogo(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceAm); + + context.ResponseData.Write(0L); + + return ResultCode.Success; + } + + [CommandCmif(3)] + // SetGpuTimeSliceBoost(u64) + public ResultCode SetGpuTimeSliceBoost(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceAm); + + return ResultCode.Success; + } + + [CommandCmif(4)] + // SetAutoSleepTimeAndDimmingTimeEnabled(u8) + public ResultCode SetAutoSleepTimeAndDimmingTimeEnabled(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceAm); + + return ResultCode.Success; + } + + [CommandCmif(5)] + // TerminateApplicationAndSetReason(u32) + public ResultCode TerminateApplicationAndSetReason(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceAm); + + return ResultCode.Success; + } + + [CommandCmif(6)] + // SetScreenShotPermissionGlobally(u8) + public ResultCode SetScreenShotPermissionGlobally(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceAm); + + return ResultCode.Success; + } + + [CommandCmif(10)] + // StartShutdownSequenceForOverlay() + public ResultCode StartShutdownSequenceForOverlay(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceAm); + + return ResultCode.Success; + } + + [CommandCmif(11)] + // StartRebootSequenceForOverlay() + public ResultCode StartRebootSequenceForOverlay(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceAm); + + return ResultCode.Success; + } + + [CommandCmif(20)] + // SetHandlingHomeButtonShortPressedEnabled(u8) + public ResultCode SetHandlingHomeButtonShortPressedEnabled(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceAm); + + return ResultCode.Success; + } + + [CommandCmif(21)] + // SetHandlingTouchScreenInputEnabled(u8) + public ResultCode SetHandlingTouchScreenInputEnabled(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceAm); + + return ResultCode.Success; + } + + [CommandCmif(30)] + // SetHealthWarningShowingState(u8) + public ResultCode SetHealthWarningShowingState(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceAm); + + return ResultCode.Success; + } + + [CommandCmif(31)] + // IsHealthWarningRequired() -> bool + public ResultCode IsHealthWarningRequired(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceAm); + + context.ResponseData.Write(false); + + return ResultCode.Success; + } + + [CommandCmif(90)] + // SetRequiresGpuResourceUse(u8) + public ResultCode SetRequiresGpuResourceUse(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceAm); + + return ResultCode.Success; + } + + [CommandCmif(101)] + // BeginToObserveHidInputForDevelop() + public ResultCode BeginToObserveHidInputForDevelop(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceAm); + + return ResultCode.Success; + } + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IApplicationAccessor.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IApplicationAccessor.cs index 450f34334..484ba5130 100644 --- a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IApplicationAccessor.cs +++ b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IApplicationAccessor.cs @@ -1,11 +1,16 @@ -using Ryujinx.Common.Logging; +using LibHac.Ns; +using Ryujinx.Common.Logging; +using Ryujinx.Common.Utilities; +using Ryujinx.HLE.HOS.Applets; using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.Kernel; using Ryujinx.HLE.HOS.Kernel.Threading; using Ryujinx.HLE.HOS.Services.Account.Acc; +using Ryujinx.HLE.HOS.Services.Ns.Types; using Ryujinx.HLE.Loaders.Processes; using Ryujinx.Horizon.Common; using System; +using System.Threading; namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.SystemAppletProxy { @@ -18,6 +23,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys private readonly KEvent _stateChangedEvent; private int _stateChangedEventHandle; + public RealApplet applet; public IApplicationAccessor(ulong pid, ulong applicationId, string contentPath, Horizon system) { @@ -70,19 +76,42 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys context.Device.Processes.LoadNca(_contentPath, out processResult); isApplet = true; } - - var applet = context.Device.System.WindowSystem.TrackProcess(processResult.ProcessId, 0, !isApplet); + + ulong caller = 0; + if (context.Device.System.WindowSystem.GetFirstApplet() != null) + { + caller = context.Device.System.WindowSystem.GetFirstApplet().ProcessHandle.Pid; + } + applet = context.Device.System.WindowSystem.TrackProcess(processResult.ProcessId, caller, !isApplet); + applet.AppletState.SetFocusHandlingMode(true); return ResultCode.Success; } + [CommandCmif(20)] + // RequestExit() + public ResultCode RequestExit(ServiceCtx context) + { + applet.ProcessHandle.SetActivity(false); + applet.AppletState.OnExitRequested(); + applet?.ProcessHandle.Terminate(); + Logger.Stub?.PrintStub(LogClass.ServiceAm); + + return ResultCode.Success; + } + + [CommandCmif(101)] // RequestForApplicationToGetForeground() public ResultCode RequestForApplicationToGetForeground(ServiceCtx context) { - // _stateChangedEvent.ReadableEvent.Signal(); Logger.Stub?.PrintStub(LogClass.ServiceAm); - context.Device.System.ReturnFocus(); - + applet.AppletState.SetFocusForce(true); + if (applet.ProcessHandle.IsPaused) + { + applet.ProcessHandle.SetActivity(false); + } + context.Device.System.WindowSystem.RequestApplicationToGetForeground(applet.ProcessHandle.Pid); + return ResultCode.Success; } @@ -95,6 +124,28 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys return ResultCode.Success; } + [CommandCmif(122)] + // GetApplicationControlProperty() -> NACP + public ResultCode GetApplicationControlProperty(ServiceCtx context) + { + ulong titleId = context.Device.System.WindowSystem.GetApplicationApplet().ProcessHandle.TitleId; + ApplicationControlProperty nacp = context.Device.Processes.ActiveApplication.ApplicationControlProperties; + ulong position = context.Request.ReceiveBuff[0].Position; + foreach (RyuApplicationData ryuApplicationData in context.Device.Configuration.Titles) + { + if (ryuApplicationData.AppId.Value != titleId) + { + continue; + } + + nacp = ryuApplicationData.Nacp; + nacp.Title[1] = ryuApplicationData.Nacp.Title[0]; + break; + } + context.Memory.Write(position, SpanHelpers.AsByteSpan(ref nacp).ToArray()); + return ResultCode.Success; + } + [CommandCmif(130)] // SetUsers() public ResultCode SetUsers(ServiceCtx context) @@ -103,5 +154,25 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys bool enable = context.RequestData.ReadBoolean(); return ResultCode.Success; } + + [CommandCmif(131)] + // CheckRightsEnvironmentAvailable() -> bool + public ResultCode CheckRightsEnvironmentAvailable(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceAm); + context.ResponseData.Write(true); + return ResultCode.Success; + } + + [CommandCmif(132)] + // GetNsRightsEnvironmentHandle() -> u32 + public ResultCode GetNsRightsEnvironmentHandle(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceAm); + KEvent eventObj = new KEvent(_kernelContext); + context.Process.HandleTable.GenerateHandle(eventObj.ReadableEvent, out int handle); + context.ResponseData.Write(handle); + return ResultCode.Success; + } } } diff --git a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ICommonStateGetter.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ICommonStateGetter.cs index 5a1ab9ef6..8f9f7ab0e 100644 --- a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ICommonStateGetter.cs +++ b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ICommonStateGetter.cs @@ -1,4 +1,5 @@ using Ryujinx.Common.Logging; +using Ryujinx.HLE.HOS.Applets; using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.Kernel.Threading; using Ryujinx.HLE.HOS.Services.Settings.Types; @@ -16,6 +17,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys private readonly Apm.ManagerServer _apmManagerServer; private readonly Apm.SystemManagerServer _apmSystemManagerServer; + private readonly RealApplet _applet; private bool _vrModeEnabled; #pragma warning disable CS0414, IDE0052 // Remove unread private member @@ -28,9 +30,10 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys private readonly KEvent _acquiredSleepLockEvent; private int _acquiredSleepLockEventHandle; - public ICommonStateGetter(ServiceCtx context) + public ICommonStateGetter(ServiceCtx context, ulong pid) { _context = context; + _applet = context.Device.System.WindowSystem.GetByAruId(pid); _apmManagerServer = new Apm.ManagerServer(context); _apmSystemManagerServer = new Apm.SystemManagerServer(context); @@ -42,7 +45,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys // GetEventHandle() -> handle public ResultCode GetEventHandle(ServiceCtx context) { - KEvent messageEvent = context.Device.System.AppletState.MessageEvent; + KEvent messageEvent = _applet.AppletState.MessageEvent; if (_messageEventHandle == 0) { @@ -61,12 +64,14 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys // ReceiveMessage() -> nn::am::AppletMessage public ResultCode ReceiveMessage(ServiceCtx context) { - if (!context.Device.System.AppletState.PopMessage(out AppletMessage message)) + if (!_applet.AppletState.PopMessage(out AppletMessage message)) { return ResultCode.NoMessages; } - + + Logger.Info?.Print(LogClass.ServiceAm, $"pid: {_applet.ProcessHandle.Pid}, msg={message}"); context.ResponseData.Write((int)message); + return ResultCode.Success; } @@ -94,7 +99,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys // GetBootMode() -> u8 public ResultCode GetBootMode(ServiceCtx context) { - context.ResponseData.Write((byte)0); //Unknown value. + context.ResponseData.Write((byte)0); // PmBootMode_Normal Logger.Stub?.PrintStub(LogClass.ServiceAm); @@ -105,7 +110,19 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys // GetCurrentFocusState() -> u8 public ResultCode GetCurrentFocusState(ServiceCtx context) { - context.ResponseData.Write((byte)context.Device.System.AppletState.AcknowledgedFocusState); + FocusState focusState; + lock (_applet.Lock) + { + focusState = _applet.AppletState.GetAndClearFocusState(); + } + + if (context.Device.System.WindowSystem.IsFocusedApplet(_applet)) + { + focusState = FocusState.InFocus; + } + + Logger.Info?.Print(LogClass.ServiceAm, $"pid: {_applet.ProcessHandle.Pid}, GetCurrentFocusState():{focusState}"); + context.ResponseData.Write((byte)focusState); return ResultCode.Success; } @@ -116,6 +133,8 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys { Logger.Stub?.PrintStub(LogClass.ServiceAm); + _acquiredSleepLockEvent.ReadableEvent.Signal(); + return ResultCode.Success; } @@ -132,15 +151,21 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys } context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_acquiredSleepLockEventHandle); - // NOTE: This needs to be signaled when sleep lock is acquired so it does not just wait forever. - // However, since we don't support sleep lock yet, it's fine to signal immediately. - _acquiredSleepLockEvent.ReadableEvent.Signal(); + + return ResultCode.Success; + } + + [CommandCmif(20)] + // PushToGeneralChannel(object) + public ResultCode PushInData(ServiceCtx context) + { + IStorage data = GetObject(context, 0); Logger.Stub?.PrintStub(LogClass.ServiceAm); return ResultCode.Success; } - + [CommandCmif(31)] [CommandCmif(32)] // GetReaderLockAccessorEx(u32) -> object @@ -216,7 +241,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys _vrModeEnabled = vrModeEnabled; - using LblApi lblApi = new(); + using var lblApi = new LblApi(); if (vrModeEnabled) { diff --git a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IHomeMenuFunctions.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IHomeMenuFunctions.cs index 68bf5199a..6be8ca9f6 100644 --- a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IHomeMenuFunctions.cs +++ b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IHomeMenuFunctions.cs @@ -8,21 +8,16 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys { class IHomeMenuFunctions : IpcService { - private readonly KEvent _channelEvent; private int _channelEventHandle; - public IHomeMenuFunctions(Horizon system) - { - // TODO: Signal this Event somewhere in future. - _channelEvent = new KEvent(system.KernelContext); - } + public IHomeMenuFunctions(Horizon system) { } [CommandCmif(10)] // RequestToGetForeground() public ResultCode RequestToGetForeground(ServiceCtx context) { Logger.Stub?.PrintStub(LogClass.ServiceAm); - context.Device.System.WindowSystem.RequestApplicationToGetForeground(); + context.Device.System.WindowSystem.RequestApplicationToGetForeground(context.Process.Pid); return ResultCode.Success; } @@ -36,6 +31,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys return ResultCode.Success; } + [CommandCmif(12)] // UnlockForeground() public ResultCode UnlockForeground(ServiceCtx context) @@ -52,7 +48,9 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys { if (_channelEventHandle == 0) { - if (context.Process.HandleTable.GenerateHandle(_channelEvent.ReadableEvent, out _channelEventHandle) != Result.Success) + if (context.Process.HandleTable.GenerateHandle( + context.Device.System.GeneralChannelEvent.ReadableEvent, + out _channelEventHandle) != Result.Success) { throw new InvalidOperationException("Out of handles!"); } diff --git a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ILibraryAppletCreator.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ILibraryAppletCreator.cs index 23ba99b04..7fda13737 100644 --- a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ILibraryAppletCreator.cs +++ b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ILibraryAppletCreator.cs @@ -1,11 +1,17 @@ using Ryujinx.HLE.HOS.Kernel.Memory; using Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.LibraryAppletCreator; +using Ryujinx.Horizon.Sdk.Applet; namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.SystemAppletProxy { class ILibraryAppletCreator : IpcService { - public ILibraryAppletCreator() { } + private readonly ulong _pid; + + public ILibraryAppletCreator(ServiceCtx context, ulong pid) + { + _pid = pid; + } [CommandCmif(0)] // CreateLibraryApplet(u32, u32) -> object @@ -16,7 +22,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys int libraryAppletMode = context.RequestData.ReadInt32(); #pragma warning restore IDE0059 - MakeObject(context, new ILibraryAppletAccessor(appletId, context.Device.System)); + MakeObject(context, new ILibraryAppletAccessor(appletId, context.Device.System, _pid)); return ResultCode.Success; } diff --git a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IRemoteStorageController.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IRemoteStorageController.cs new file mode 100644 index 000000000..c864965bd --- /dev/null +++ b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IRemoteStorageController.cs @@ -0,0 +1,7 @@ +namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.SystemAppletProxy +{ + class IRemoteStorageController : IpcService + { + + } +} diff --git a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IWindowController.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IWindowController.cs index 2bca8ba80..895ef8d44 100644 --- a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IWindowController.cs +++ b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/IWindowController.cs @@ -24,6 +24,8 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys return ResultCode.Success; } + + [CommandCmif(10)] // AcquireForegroundRights() public ResultCode AcquireForegroundRights(ServiceCtx context) diff --git a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/IAllSystemAppletProxiesService.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/IAllSystemAppletProxiesService.cs index df0d72deb..edf15b135 100644 --- a/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/IAllSystemAppletProxiesService.cs +++ b/src/Ryujinx.HLE/HOS/Services/Am/AppletAE/IAllSystemAppletProxiesService.cs @@ -12,6 +12,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE // OpenSystemAppletProxy(u64, pid, handle) -> object public ResultCode OpenSystemAppletProxy(ServiceCtx context) { + context.Device.System.WindowSystem.TrackProcess(context.Request.HandleDesc.PId, 0, false); MakeObject(context, new ISystemAppletProxy(context.Request.HandleDesc.PId)); return ResultCode.Success; @@ -22,7 +23,18 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE // OpenLibraryAppletProxy(u64, pid, handle) -> object public ResultCode OpenLibraryAppletProxy(ServiceCtx context) { - MakeObject(context, new ILibraryAppletProxy(context.Request.HandleDesc.PId)); + context.Device.System.WindowSystem.TrackProcess(context.Request.HandleDesc.PId, 0, false); + MakeObject(context, new ILibraryAppletProxy(context,context.Request.HandleDesc.PId)); + + return ResultCode.Success; + } + + [CommandCmif(300)] + // OpenOverlayAppletProxy(pid, handle) -> object + public ResultCode OpenOverlayAppletProxy(ServiceCtx context) + { + context.Device.System.WindowSystem.TrackProcess(context.Request.HandleDesc.PId, 0, false); + MakeObject(context, new IOverlayAppletProxy(context.Request.HandleDesc.PId)); return ResultCode.Success; } @@ -31,6 +43,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE // OpenSystemApplicationProxy(u64, pid, handle) -> object public ResultCode OpenSystemApplicationProxy(ServiceCtx context) { + context.Device.System.WindowSystem.TrackProcess(context.Request.HandleDesc.PId, 0, false); MakeObject(context, new IApplicationProxy(context.Request.HandleDesc.PId)); return ResultCode.Success; diff --git a/src/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/IApplicationProxy.cs b/src/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/IApplicationProxy.cs index b24e1bf4f..0ea54bced 100644 --- a/src/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/IApplicationProxy.cs +++ b/src/Ryujinx.HLE/HOS/Services/Am/AppletOE/ApplicationProxyService/IApplicationProxy.cs @@ -1,3 +1,4 @@ +using Ryujinx.HLE.HOS.Applets; using Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.SystemAppletProxy; using Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.ApplicationProxy; @@ -16,7 +17,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService // GetCommonStateGetter() -> object public ResultCode GetCommonStateGetter(ServiceCtx context) { - MakeObject(context, new ICommonStateGetter(context)); + MakeObject(context, new ICommonStateGetter(context, _pid)); return ResultCode.Success; } @@ -61,7 +62,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService // GetLibraryAppletCreator() -> object public ResultCode GetLibraryAppletCreator(ServiceCtx context) { - MakeObject(context, new ILibraryAppletCreator()); + MakeObject(context, new ILibraryAppletCreator(context, _pid)); return ResultCode.Success; } diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/Hid.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Hid.cs index 1a0280648..c6f0797ec 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/Hid.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/Hid.cs @@ -25,7 +25,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid internal ref SharedMemory SharedMemory => ref _storage.GetRef(0); - internal const int SharedMemEntryCount = 17; + internal const int SharedMemEntryCount = 16; public DebugPadDevice DebugPad; public TouchDevice Touchscreen; diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/ControllerKeys.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/ControllerKeys.cs index b43381e6d..949fb1246 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/ControllerKeys.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/Types/Npad/ControllerKeys.cs @@ -33,7 +33,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid SrLeft = 1 << 25, SlRight = 1 << 26, SrRight = 1 << 27, - + Capture = 1 << 28, + Home = 1 << 29, + // Generic Catch-all Up = DpadUp | LStickUp | RStickUp, Down = DpadDown | LStickDown | RStickDown, diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Common/RingLifo.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Common/RingLifo.cs index 99f2f59e4..f2782755b 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Common/RingLifo.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Common/RingLifo.cs @@ -7,7 +7,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common { struct RingLifo where T : unmanaged, ISampledDataStruct { - private const ulong MaxEntries = 17; + private const ulong MaxEntries = 16; #pragma warning disable IDE0051, CS0169 // Remove unused private member private readonly ulong _unused; diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadButton.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadButton.cs index c20db8e0b..310dd5843 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadButton.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/Npad/NpadButton.cs @@ -35,10 +35,11 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad RightSL = 1 << 26, RightSR = 1 << 27, Palma = 1 << 28, - - // FIXME: Probably a button on Lark. - Unknown29 = 1 << 29, - + Verification = 1 << 29, HandheldLeftB = 1 << 30, + LeftC = 1UL << 31, + UpC = 1UL << 32, + RightC = 1UL << 33, + DownC = 1UL << 34, } } diff --git a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/SharedMemory.cs b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/SharedMemory.cs index 98952f1d1..6915a5246 100644 --- a/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/SharedMemory.cs +++ b/src/Ryujinx.HLE/HOS/Services/Hid/Types/SharedMemory/SharedMemory.cs @@ -79,6 +79,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory TouchScreen = RingLifo.Create(), Mouse = RingLifo.Create(), Keyboard = RingLifo.Create(), + HomeButton = RingLifo.Create(), + SleepButton = RingLifo.Create(), + CaptureButton = RingLifo.Create(), }; for (int i = 0; i < result.Npads.Length; i++) diff --git a/src/Ryujinx.HLE/HOS/Services/Npns/INpnsUser.cs b/src/Ryujinx.HLE/HOS/Services/Npns/INpnsUser.cs index 9a794709e..08cbb0366 100644 --- a/src/Ryujinx.HLE/HOS/Services/Npns/INpnsUser.cs +++ b/src/Ryujinx.HLE/HOS/Services/Npns/INpnsUser.cs @@ -1,8 +1,35 @@ +using Ryujinx.HLE.HOS.Ipc; +using Ryujinx.HLE.HOS.Kernel.Threading; +using Ryujinx.Horizon.Common; +using System; + namespace Ryujinx.HLE.HOS.Services.Npns { [Service("npns:u")] class INpnsUser : IpcService { - public INpnsUser(ServiceCtx context) { } + public KEvent receiveEvent; + public int receiveEventHandle = 0; + + public INpnsUser(ServiceCtx context) + { + receiveEvent = new KEvent(context.Device.System.KernelContext); + } + + [CommandCmif(5)] + // GetReceiveEvent() -> handle(copy) + public ResultCode GetReceiveEvent(ServiceCtx context) + { + if (receiveEventHandle == 0) + { + if (context.Process.HandleTable.GenerateHandle(receiveEvent.ReadableEvent, out receiveEventHandle) != + Result.Success) + { + throw new InvalidOperationException("Out of handles"); + } + } + context.Response.HandleDesc = IpcHandleDesc.MakeCopy(receiveEventHandle); + return ResultCode.Success; + } } } diff --git a/src/Ryujinx.HLE/HOS/Services/Ns/IApplicationManagerInterface.cs b/src/Ryujinx.HLE/HOS/Services/Ns/IApplicationManagerInterface.cs index 6954a54cf..949c7db68 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ns/IApplicationManagerInterface.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ns/IApplicationManagerInterface.cs @@ -26,14 +26,18 @@ namespace Ryujinx.HLE.HOS.Services.Ns private int _gameCardUpdateDetectionEventHandle; private KEvent _gameCardMountFailureEvent; - private int _gameCardMountFailureEventHandle; + private int _gameCardMountFailureEventHandle; + + private KEvent _gameCardWakeEvent; + private int _gameCardWakeEventHandle; public IApplicationManagerInterface(ServiceCtx context) { _applicationRecordUpdateSystemEvent = new KEvent(context.Device.System.KernelContext); _sdCardMountStatusChangedEvent = new KEvent(context.Device.System.KernelContext); _gameCardUpdateDetectionEvent = new KEvent(context.Device.System.KernelContext); - _gameCardMountFailureEvent = new KEvent(context.Device.System.KernelContext); + _gameCardMountFailureEvent = new KEvent(context.Device.System.KernelContext); + _gameCardWakeEvent = new KEvent(context.Device.System.KernelContext); } @@ -262,6 +266,33 @@ namespace Ryujinx.HLE.HOS.Services.Ns Logger.Stub?.PrintStub(LogClass.Service); return ResultCode.Success; } + + [CommandCmif(511)] + // GetGameCardWakenReadyEvent() -> handle + public ResultCode GetGameCardWakenReadyEvent(ServiceCtx context) + { + if (_gameCardWakeEventHandle == 0) + { + if (context.Process.HandleTable.GenerateHandle(_gameCardWakeEvent.ReadableEvent, out _gameCardWakeEventHandle) != Result.Success) + { + throw new InvalidOperationException("Out of handles!"); + } + } + + context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_gameCardWakeEventHandle); + _gameCardWakeEvent.ReadableEvent.Signal(); + + return ResultCode.Success; + } + + [CommandCmif(512)] + // IsGameCardApplicationRunning() -> bool + public ResultCode IsGameCardApplicationRunning(ServiceCtx context) + { + Logger.Stub?.PrintStub(LogClass.ServiceNs); + context.ResponseData.Write(true); + return ResultCode.Success; + } [CommandCmif(1701)] // GetApplicationView(buffer) -> buffer diff --git a/src/Ryujinx.HLE/HOS/Services/Ns/IDynamicRightsInterface.cs b/src/Ryujinx.HLE/HOS/Services/Ns/IDynamicRightsInterface.cs index 22e66ba20..672f5b1df 100644 --- a/src/Ryujinx.HLE/HOS/Services/Ns/IDynamicRightsInterface.cs +++ b/src/Ryujinx.HLE/HOS/Services/Ns/IDynamicRightsInterface.cs @@ -2,6 +2,18 @@ { class IDynamicRightsInterface : IpcService { + [CommandCmif(5)] + // VerifyActivatedRightsOwners(u64) + public ResultCode VerifyActivatedRightsOwners(ServiceCtx context) => ResultCode.Success; + + + [CommandCmif(13)] + // GetRunningApplicationStatus() -> nn::ns::RunningApplicationStatus + public ResultCode GetRunningApplicationStatus(ServiceCtx context) + { + context.ResponseData.Write(0); + return ResultCode.Success; + } } } diff --git a/src/Ryujinx.HLE/HOS/Services/Olsc/IOlscServiceForSystemService.cs b/src/Ryujinx.HLE/HOS/Services/Olsc/IOlscServiceForSystemService.cs index d8fc39e72..8434b3b6a 100644 --- a/src/Ryujinx.HLE/HOS/Services/Olsc/IOlscServiceForSystemService.cs +++ b/src/Ryujinx.HLE/HOS/Services/Olsc/IOlscServiceForSystemService.cs @@ -1,3 +1,4 @@ +using Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.SystemAppletProxy; using Ryujinx.HLE.HOS.Services.Olsc.OlscServiceForSystemService; namespace Ryujinx.HLE.HOS.Services.Olsc @@ -16,6 +17,15 @@ namespace Ryujinx.HLE.HOS.Services.Olsc return ResultCode.Success; } + [CommandCmif(1)] + // GetRemoteStorageController() -> object + public ResultCode GetRemoteStorageController(ServiceCtx context) + { + MakeObject(context, new IRemoteStorageController()); + + return ResultCode.Success; + } + [CommandCmif(2)] // GetDaemonController() -> object public ResultCode GetDaemonController(ServiceCtx context) diff --git a/src/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/IManagerDisplayService.cs b/src/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/IManagerDisplayService.cs index 837d6230e..7a234095f 100644 --- a/src/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/IManagerDisplayService.cs +++ b/src/Ryujinx.HLE/HOS/Services/Vi/RootService/ApplicationDisplayService/IManagerDisplayService.cs @@ -37,7 +37,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService.ApplicationDisplayService #pragma warning restore IDE0059 long appletResourceUserId = context.RequestData.ReadInt64(); - ulong pid = context.Device.System.AppletState.AppletResourceUserIds.GetData((int)appletResourceUserId); + ulong pid = context.Device.System.GetAppletState(context.Process.Pid).AppletResourceUserIds.GetData((int)appletResourceUserId); context.Device.System.SurfaceFlinger.CreateLayer(out long layerId, pid); diff --git a/src/Ryujinx.HLE/HOS/Services/Vi/RootService/IApplicationDisplayService.cs b/src/Ryujinx.HLE/HOS/Services/Vi/RootService/IApplicationDisplayService.cs index 3bec26386..c64013d02 100644 --- a/src/Ryujinx.HLE/HOS/Services/Vi/RootService/IApplicationDisplayService.cs +++ b/src/Ryujinx.HLE/HOS/Services/Vi/RootService/IApplicationDisplayService.cs @@ -388,7 +388,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService RenderingSurfaceInfo surfaceInfo = new(ColorFormat.A8B8G8R8, (uint)layerWidth, (uint)layerHeight, (uint)pitch, (uint)layerBuffSize); // Get the applet associated with the handle. - object appletObject = context.Device.System.AppletState.IndirectLayerHandles.GetData((int)layerHandle); + object appletObject = context.Device.System.GetAppletState(context.Process.Pid).IndirectLayerHandles.GetData((int)layerHandle); if (appletObject == null) { diff --git a/src/Ryujinx.HLE/HOS/SystemState/AppletStateMgr.cs b/src/Ryujinx.HLE/HOS/SystemState/AppletStateMgr.cs index dbc1bb339..4105962b1 100644 --- a/src/Ryujinx.HLE/HOS/SystemState/AppletStateMgr.cs +++ b/src/Ryujinx.HLE/HOS/SystemState/AppletStateMgr.cs @@ -1,18 +1,23 @@ +using System; +using System.Collections.Concurrent; using Ryujinx.HLE.HOS.Applets; using Ryujinx.HLE.HOS.Applets.Types; using Ryujinx.HLE.HOS.Kernel.Threading; using Ryujinx.HLE.HOS.Services.Am.AppletAE; using Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.SystemAppletProxy; -using System; -using System.Collections.Concurrent; namespace Ryujinx.HLE.HOS.SystemState { class AppletStateMgr { - public ConcurrentQueue Messages; + #region Public Properties and Fields - public bool ForcedSuspend { get; private set; } + /// + /// Queue used for unordered messages. + /// + public ConcurrentQueue Messages { get; } + + public bool ForcedSuspend { get; set; } public FocusState AcknowledgedFocusState { get; private set; } = FocusState.Background; public FocusState RequestedFocusState { get; private set; } = FocusState.Background; @@ -25,33 +30,49 @@ namespace Ryujinx.HLE.HOS.SystemState public KEvent LaunchableEvent { get; } public IdDictionary AppletResourceUserIds { get; } - public IdDictionary IndirectLayerHandles { get; } - internal bool IsApplication { get; } + /// + /// Indicates that an exit has been requested. + /// + public bool HasRequestedExit => _hasRequestedExit; + + #endregion + + #region Private Fields + + // Flags used for pending notifications. + private bool _hasRequestedExit = false; + private bool _hasAcknowledgedExit = false; + private bool _hasResume = false; + private bool _hasFocusStateChanged = false; + private bool _hasRequestedRequestToPrepareSleep = false; + private bool _hasAcknowledgedRequestToPrepareSleep = false; + private bool _requestedRequestToDisplayState = false; + private bool _acknowledgedRequestToDisplayState = false; + private bool _hasOperationModeChanged = false; + private bool _hasPerformanceModeChanged = false; + private bool _hasSdCardRemoved = false; + private bool _hasSleepRequiredByHighTemperature = false; + private bool _hasSleepRequiredByLowBattery = false; + private bool _hasAutoPowerDown = false; + private bool _hasAlbumScreenShotTaken = false; + private bool _hasAlbumRecordingSaved = false; + + // Controls whether notifications for particular events are enabled. private bool _focusStateChangedNotificationEnabled = true; private bool _operationModeChangedNotificationEnabled = true; private bool _performanceModeChangedNotificationEnabled = true; - private bool _hasRequestedExit = false; - private bool _hasAcknowledgedExit = false; - private bool _requestedRequestToDisplayState = false; - private bool _acknowledgedRequestToDisplayState = false; - private bool _hasRequestedRequestToPrepareSleep = false; - private bool _hasAcknowledgedRequestToPrepareSleep = false; - private bool _hasOperationModeChanged = false; - private bool _hasPerformanceModeChanged = false; - private bool _hasResume = false; - private bool _hasFocusStateChanged = false; - private bool _hasAlbumRecordingSaved = false; - private bool _hasAlbumScreenShotTaken = false; - private bool _hasAutoPowerDown = false; - private bool _hasSleepRequiredByLowBattery = false; - private bool _hasSleepRequiredByHighTemperature = false; - private bool _hasSdCardRemoved = false; + + // Internal event state for message signaling. private bool _eventSignaled = false; + + // Indicates how the applet handles focus and suspension. private FocusHandlingMode _focusHandlingMode = FocusHandlingMode.NoSuspend; - public bool HasRequestedExit => _hasRequestedExit; + #endregion + + #region Properties with Custom Logic public bool FocusStateChangedNotificationEnabled { @@ -59,7 +80,7 @@ namespace Ryujinx.HLE.HOS.SystemState set { _focusStateChangedNotificationEnabled = value; - // SignalEventIfNeeded(); + SignalEventIfNeeded(); } } @@ -83,10 +104,15 @@ namespace Ryujinx.HLE.HOS.SystemState } } - public AppletStateMgr(Horizon system, bool isApplication) + #endregion + + #region Constructor + + // Note: The constructor no longer takes an "isApplication" parameter. + public AppletStateMgr(Horizon system) { - IsApplication = isApplication; Messages = new ConcurrentQueue(); + MessageEvent = new KEvent(system.KernelContext); OperationModeChangedEvent = new KEvent(system.KernelContext); LaunchableEvent = new KEvent(system.KernelContext); @@ -95,15 +121,18 @@ namespace Ryujinx.HLE.HOS.SystemState IndirectLayerHandles = new IdDictionary(); } + #endregion + + #region Public Methods + public void SetFocusState(FocusState state) { if (RequestedFocusState != state) { RequestedFocusState = state; _hasFocusStateChanged = true; + SignalEventIfNeeded(); } - - SignalEventIfNeeded(); } public FocusState GetAndClearFocusState() @@ -115,10 +144,13 @@ namespace Ryujinx.HLE.HOS.SystemState public void PushUnorderedMessage(AppletMessage message) { Messages.Enqueue(message); - SignalEventIfNeeded(); } + /// + /// Attempts to pop the next pending message. If additional messages remain in the queue, + /// signals the event so that consumers can continue processing. + /// public bool PopMessage(out AppletMessage message) { message = GetNextMessage(); @@ -126,6 +158,201 @@ namespace Ryujinx.HLE.HOS.SystemState return message != AppletMessage.None; } + public void OnOperationAndPerformanceModeChanged() + { + if (_operationModeChangedNotificationEnabled) + { + _hasOperationModeChanged = true; + } + if (_performanceModeChangedNotificationEnabled) + { + _hasPerformanceModeChanged = true; + } + OperationModeChangedEvent.ReadableEvent.Signal(); + SignalEventIfNeeded(); + } + + public void OnExitRequested() + { + _hasRequestedExit = true; + SignalEventIfNeeded(); + } + + public void SetFocusHandlingMode(bool suspend) + { + // Adjust the focus handling mode based on the desired suspend state. + _focusHandlingMode = _focusHandlingMode switch + { + FocusHandlingMode.AlwaysSuspend or FocusHandlingMode.SuspendHomeSleep when !suspend => FocusHandlingMode.NoSuspend, + FocusHandlingMode.NoSuspend when suspend => FocusHandlingMode.SuspendHomeSleep, + _ => _focusHandlingMode, + }; + SignalEventIfNeeded(); + } + + public void RequestResumeNotification() + { + // Note: There is a known bug in AM whereby concurrent resume notifications + // may cause the first notification to be lost. + if (ResumeNotificationEnabled) + { + _hasResume = true; + SignalEventIfNeeded(); + } + } + + public void SetOutOfFocusSuspendingEnabled(bool enabled) + { + _focusHandlingMode = _focusHandlingMode switch + { + FocusHandlingMode.AlwaysSuspend when !enabled => FocusHandlingMode.SuspendHomeSleep, + FocusHandlingMode.SuspendHomeSleep or FocusHandlingMode.NoSuspend when enabled => FocusHandlingMode.AlwaysSuspend, + _ => _focusHandlingMode, + }; + SignalEventIfNeeded(); + } + + public void RemoveForceResumeIfPossible() + { + if (SuspendMode != SuspendMode.ForceResume) + { + return; + } + + // If the activity is already resumed, we can remove the forced state. + if (ActivityState == ActivityState.ForegroundVisible || + ActivityState == ActivityState.ForegroundObscured) + { + SuspendMode = SuspendMode.NoOverride; + return; + } + + // Without a separate application flag, simply remove forced resume. + SuspendMode = SuspendMode.NoOverride; + } + + public bool IsRunnable() + { + if (ForcedSuspend) + { + return false; + } + + switch (SuspendMode) + { + case SuspendMode.ForceResume: + return _hasRequestedExit; // During forced resume, only exit requests make it runnable. + case SuspendMode.ForceSuspend: + return false; + } + + if (_hasRequestedExit) + { + return true; + } + + if (ActivityState == ActivityState.ForegroundVisible) + { + return true; + } + + if (ActivityState == ActivityState.ForegroundObscured) + { + return _focusHandlingMode switch + { + FocusHandlingMode.AlwaysSuspend => false, + FocusHandlingMode.SuspendHomeSleep => true, + FocusHandlingMode.NoSuspend => true, + _ => false, + }; + } + + // When not in the foreground, run only if suspension is disabled. + return _focusHandlingMode == FocusHandlingMode.NoSuspend; + } + + public FocusState GetFocusStateWhileForegroundObscured() => + _focusHandlingMode switch + { + FocusHandlingMode.AlwaysSuspend => FocusState.InFocus, + FocusHandlingMode.SuspendHomeSleep => FocusState.OutOfFocus, + FocusHandlingMode.NoSuspend => FocusState.OutOfFocus, + _ => throw new IndexOutOfRangeException() + }; + + public FocusState GetFocusStateWhileBackground(bool isObscured) => + _focusHandlingMode switch + { + FocusHandlingMode.AlwaysSuspend => FocusState.InFocus, + FocusHandlingMode.SuspendHomeSleep => isObscured ? FocusState.OutOfFocus : FocusState.InFocus, + // Without an application flag, default to Background. + FocusHandlingMode.NoSuspend => FocusState.Background, + _ => throw new IndexOutOfRangeException(), + }; + + public bool UpdateRequestedFocusState() + { + FocusState newState; + + if (SuspendMode == SuspendMode.NoOverride) + { + newState = ActivityState switch + { + ActivityState.ForegroundVisible => FocusState.InFocus, + ActivityState.ForegroundObscured => GetFocusStateWhileForegroundObscured(), + ActivityState.BackgroundVisible => GetFocusStateWhileBackground(false), + ActivityState.BackgroundObscured => GetFocusStateWhileBackground(true), + _ => throw new IndexOutOfRangeException(), + }; + } + else + { + newState = GetFocusStateWhileBackground(false); + } + + if (newState != RequestedFocusState) + { + RequestedFocusState = newState; + _hasFocusStateChanged = true; + SignalEventIfNeeded(); + return true; + } + + return false; + } + + public void SetFocus(bool isFocused) + { + SetFocusHandlingMode(false); + FocusState focusState = isFocused ? FocusState.InFocus : FocusState.OutOfFocus; + SetFocusState(focusState); + } + + public void SetFocusForce(bool isFocused, bool shouldSuspend = false) + { + Messages.Clear(); + SetFocusHandlingMode(shouldSuspend); + RequestedFocusState = isFocused ? FocusState.InFocus : FocusState.OutOfFocus; + Messages.Enqueue(AppletMessage.FocusStateChanged); + if (isFocused) + { + Messages.Enqueue(AppletMessage.ChangeIntoForeground); + } + else + { + Messages.Enqueue(AppletMessage.ChangeIntoBackground); + } + MessageEvent.ReadableEvent.Signal(); + } + + #endregion + + #region Private Methods + + /// + /// Checks various flags and the message queue in order to return the next pending message. + /// Flags are cleared as soon as their corresponding message is returned. + /// private AppletMessage GetNextMessage() { if (_hasResume) @@ -140,31 +367,17 @@ namespace Ryujinx.HLE.HOS.SystemState return AppletMessage.Exit; } - if (_focusStateChangedNotificationEnabled) + // Unify focus state change handling: if the acknowledged focus state does not match the requested one, + // update it and return the appropriate foreground/background message. + if (_focusStateChangedNotificationEnabled && RequestedFocusState != AcknowledgedFocusState) { - if (IsApplication) + AcknowledgedFocusState = RequestedFocusState; + return RequestedFocusState switch { - if (_hasFocusStateChanged) - { - _hasFocusStateChanged = false; - return AppletMessage.FocusStateChanged; - } - } - else - { - if (RequestedFocusState != AcknowledgedFocusState) - { - AcknowledgedFocusState = RequestedFocusState; - - switch (RequestedFocusState) - { - case FocusState.InFocus: - return AppletMessage.ChangeIntoForeground; - case FocusState.OutOfFocus: - return AppletMessage.ChangeIntoBackground; - } - } - } + FocusState.InFocus => AppletMessage.ChangeIntoForeground, + FocusState.OutOfFocus => AppletMessage.ChangeIntoBackground, + _ => AppletMessage.FocusStateChanged, + }; } if (_hasRequestedRequestToPrepareSleep != _hasAcknowledgedRequestToPrepareSleep) @@ -227,353 +440,54 @@ namespace Ryujinx.HLE.HOS.SystemState return AppletMessage.AlbumRecordingSaved; } - if (Messages.TryDequeue(out AppletMessage message)) - { - return message; - } - - return AppletMessage.None; + return Messages.TryDequeue(out var message) ? message : AppletMessage.None; } - internal void SignalEventIfNeeded() + /// + /// Determines whether the internal event should be signaled based on the state flags and message queue. + /// + private bool ShouldSignalEvent() { - var shouldSignal = ShouldSignalEvent(); + bool focusStateChanged = _focusStateChangedNotificationEnabled && + (RequestedFocusState != AcknowledgedFocusState); + + return !Messages.IsEmpty || + focusStateChanged || + _hasResume || + (_hasRequestedExit != _hasAcknowledgedExit) || + (_hasRequestedRequestToPrepareSleep != _hasAcknowledgedRequestToPrepareSleep) || + _hasOperationModeChanged || + _hasPerformanceModeChanged || + _hasSdCardRemoved || + _hasSleepRequiredByHighTemperature || + _hasSleepRequiredByLowBattery || + _hasAutoPowerDown || + (_requestedRequestToDisplayState != _acknowledgedRequestToDisplayState) || + _hasAlbumScreenShotTaken || + _hasAlbumRecordingSaved; + } + + /// + /// Signals (or clears) the MessageEvent depending on whether there is any pending work. + /// + public void SignalEventIfNeeded() + { + bool shouldSignal = ShouldSignalEvent(); if (_eventSignaled != shouldSignal) { - if (_eventSignaled) - { - MessageEvent.ReadableEvent.Clear(); - _eventSignaled = false; - } - else + if (shouldSignal) { MessageEvent.ReadableEvent.Signal(); - _eventSignaled = true; - } - } - } - - private bool ShouldSignalEvent() - { - bool focusStateChanged = false; - if (_focusStateChangedNotificationEnabled) - { - if (IsApplication) - { - if (_hasFocusStateChanged) - { - focusStateChanged = true; - } } else { - if (RequestedFocusState != AcknowledgedFocusState) - { - focusStateChanged = true; - } + MessageEvent.ReadableEvent.Clear(); } - } - - return !Messages.IsEmpty - || focusStateChanged - || _hasResume - || _hasRequestedExit != _hasAcknowledgedExit - || _hasRequestedRequestToPrepareSleep != _hasAcknowledgedRequestToPrepareSleep - || _hasOperationModeChanged - || _hasPerformanceModeChanged - || _hasSdCardRemoved - || _hasSleepRequiredByHighTemperature - || _hasSleepRequiredByLowBattery - || _hasAutoPowerDown - || _requestedRequestToDisplayState != _acknowledgedRequestToDisplayState - || _hasAlbumScreenShotTaken - || _hasAlbumRecordingSaved; - } - - public void OnOperationAndPerformanceModeChanged() - { - if (_operationModeChangedNotificationEnabled) - { - _hasOperationModeChanged = true; - } - - if (_performanceModeChangedNotificationEnabled) - { - _hasPerformanceModeChanged = true; - } - - OperationModeChangedEvent.ReadableEvent.Signal(); - SignalEventIfNeeded(); - } - - public void OnExitRequested() - { - _hasRequestedExit = true; - SignalEventIfNeeded(); - } - - public void SetFocusHandlingMode(bool suspend) - { - switch (_focusHandlingMode) - { - case FocusHandlingMode.AlwaysSuspend: - case FocusHandlingMode.SuspendHomeSleep: - if (!suspend) - { - // Disallow suspension - _focusHandlingMode = FocusHandlingMode.NoSuspend; - } - break; - case FocusHandlingMode.NoSuspend: - if (suspend) - { - // Allow suspension temporarily. - _focusHandlingMode = FocusHandlingMode.SuspendHomeSleep; - } - break; - } - - // SignalEventIfNeeded(); - } - - public void RequestResumeNotification() - { - // NOTE: this appears to be a bug in am. - // If an applet makes a concurrent request to receive resume notifications - // while it is being suspended, the first resume notification will be lost. - // This is not the case with other notification types. - if (ResumeNotificationEnabled) - { - _hasResume = true; + _eventSignaled = shouldSignal; } } - public void SetOutOfFocusSuspendingEnabled(bool enabled) - { - switch (_focusHandlingMode) - { - case FocusHandlingMode.AlwaysSuspend: - if (!enabled) - { - // Allow suspension temporarily. - _focusHandlingMode = FocusHandlingMode.SuspendHomeSleep; - } - break; - case FocusHandlingMode.SuspendHomeSleep: - case FocusHandlingMode.NoSuspend: - if (enabled) - { - // Allow suspension - _focusHandlingMode = FocusHandlingMode.AlwaysSuspend; - } - break; - } - - SignalEventIfNeeded(); - } - - public void RemoveForceResumeIfPossible() - { - // If resume is not forced, we have nothing to do. - if (SuspendMode != SuspendMode.ForceResume) - { - return; - } - - // Check activity state. - // If we are already resumed, we can remove the forced state. - switch (ActivityState) - { - case ActivityState.ForegroundVisible: - case ActivityState.ForegroundObscured: - SuspendMode = SuspendMode.NoOverride; - return; - } - - // Check focus handling mode. - switch (_focusHandlingMode) - { - case FocusHandlingMode.AlwaysSuspend: - case FocusHandlingMode.SuspendHomeSleep: - // If the applet allows suspension, we can remove the forced state. - SuspendMode = SuspendMode.NoOverride; - break; - case FocusHandlingMode.NoSuspend: - // If the applet is not an application, we can remove the forced state. - // Only applications can be forced to resume. - if (!IsApplication) - { - SuspendMode = SuspendMode.NoOverride; - } - break; - } - } - - public bool IsRunnable() - { - // If suspend is forced, return that. - if (ForcedSuspend) - { - return false; - } - - // Check suspend mode override. - switch (SuspendMode) - { - case SuspendMode.NoOverride: - // Continue processing. - break; - - case SuspendMode.ForceResume: - // The applet is runnable during forced resumption when its exit is requested. - return _hasRequestedExit; - - case SuspendMode.ForceSuspend: - // The applet is never runnable during forced suspension. - return false; - } - - // Always run if exit is requested. - if (_hasRequestedExit) - { - return true; - } - - if (ActivityState == ActivityState.ForegroundVisible) - { - // The applet is runnable now. - return true; - } - - if (ActivityState == ActivityState.ForegroundObscured) - { - switch (_focusHandlingMode) - { - case FocusHandlingMode.AlwaysSuspend: - // The applet is not runnable while running the applet. - return false; - - case FocusHandlingMode.SuspendHomeSleep: - // The applet is runnable while running the applet. - return true; - - case FocusHandlingMode.NoSuspend: - // The applet is always runnable. - return true; - } - } - - // The activity is a suspended one. - // The applet should be suspended unless it has disabled suspension. - return _focusHandlingMode == FocusHandlingMode.NoSuspend; - } - - public FocusState GetFocusStateWhileForegroundObscured() - { - switch (_focusHandlingMode) - { - case FocusHandlingMode.AlwaysSuspend: - // The applet never learns it has lost focus. - return FocusState.InFocus; - - case FocusHandlingMode.SuspendHomeSleep: - // The applet learns it has lost focus when launching a child applet. - return FocusState.OutOfFocus; - - case FocusHandlingMode.NoSuspend: - // The applet always learns it has lost focus. - return FocusState.OutOfFocus; - - default: - throw new IndexOutOfRangeException(); - } - } - - public FocusState GetFocusStateWhileBackground(bool isObscured) - { - switch (_focusHandlingMode) - { - case FocusHandlingMode.AlwaysSuspend: - // The applet never learns it has lost focus. - return FocusState.InFocus; - - case FocusHandlingMode.SuspendHomeSleep: - // The applet learns it has lost focus when launching a child applet. - return isObscured ? FocusState.OutOfFocus : FocusState.InFocus; - - case FocusHandlingMode.NoSuspend: - // The applet always learns it has lost focus. - return IsApplication ? FocusState.Background : FocusState.OutOfFocus; - - default: - throw new IndexOutOfRangeException(); - } - } - - public bool UpdateRequestedFocusState() - { - FocusState newState; - - if (SuspendMode == SuspendMode.NoOverride) - { - // With no forced suspend or resume, we take the focus state designated - // by the combination of the activity flag and the focus handling mode. - switch (ActivityState) - { - case ActivityState.ForegroundVisible: - newState = FocusState.InFocus; - break; - - case ActivityState.ForegroundObscured: - newState = GetFocusStateWhileForegroundObscured(); - break; - - case ActivityState.BackgroundVisible: - newState = GetFocusStateWhileBackground(false); - break; - - case ActivityState.BackgroundObscured: - newState = GetFocusStateWhileBackground(true); - break; - - default: - throw new IndexOutOfRangeException(); - } - } - else - { - // With forced suspend or resume, the applet is guaranteed to be background. - newState = GetFocusStateWhileBackground(false); - } - - if (newState != RequestedFocusState) - { - // Mark the focus state as ready for update. - RequestedFocusState = newState; - _hasFocusStateChanged = true; - - // We changed the focus state. - return true; - } - - // We didn't change the focus state. - return false; - } - - public void SetFocus(bool isFocused) - { - AcknowledgedFocusState = isFocused ? FocusState.InFocus : FocusState.OutOfFocus; - - Messages.Enqueue(AppletMessage.FocusStateChanged); - - if (isFocused) - { - Messages.Enqueue(AppletMessage.ChangeIntoForeground); - } - - MessageEvent.ReadableEvent.Signal(); - } - + #endregion } } diff --git a/src/Ryujinx.HLE/Switch.cs b/src/Ryujinx.HLE/Switch.cs index 86b04061e..06aee354b 100644 --- a/src/Ryujinx.HLE/Switch.cs +++ b/src/Ryujinx.HLE/Switch.cs @@ -1,10 +1,14 @@ using LibHac.Common; +using LibHac.Ncm; using LibHac.Ns; +using LibHac.Tools.FsSystem.NcaUtils; using Ryujinx.Audio.Backends.CompatLayer; using Ryujinx.Audio.Integration; using Ryujinx.Common; using Ryujinx.Common.Configuration; +using Ryujinx.Common.Logging; using Ryujinx.Graphics.Gpu; +using Ryujinx.HLE.Exceptions; using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.HOS; using Ryujinx.HLE.HOS.Services.Apm; @@ -158,5 +162,23 @@ namespace Ryujinx.HLE Shared = null; } } + + public void UpdateWindowSystemInput() + { + System.WindowSystem.ButtonPressTracker.Update(); + } + + public bool LoadSystemProgramId(ulong programId) + { + string contentPath = System.ContentManager.GetInstalledContentPath(programId, StorageId.BuiltInSystem, NcaContentType.Program); + string filePath = VirtualFileSystem.SwitchPathToSystemPath(contentPath); + + if (string.IsNullOrWhiteSpace(filePath)) + { + throw new InvalidSystemResourceException("Specified title ID is not installed on the system."); + } + + return Processes.LoadNca(filePath); + } } } diff --git a/src/Ryujinx.Horizon/Ovln/Ipc/Receiver.cs b/src/Ryujinx.Horizon/Ovln/Ipc/Receiver.cs new file mode 100644 index 000000000..21b4e717b --- /dev/null +++ b/src/Ryujinx.Horizon/Ovln/Ipc/Receiver.cs @@ -0,0 +1,65 @@ +using LibHac.Util; +using Ryujinx.Common.Logging; +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.OsTypes; +using Ryujinx.Horizon.Sdk.Ovln; +using Ryujinx.Horizon.Sdk.Sf; +using System.Threading; + +namespace Ryujinx.Horizon.Ovln.Ipc +{ + partial class Receiver : IReceiver + { + private int _handle; + private SystemEventType _recvEvent; + private int _disposalState; + + [CmifCommand(0)] + public Result AddSource(SourceName name) + { + Logger.Debug?.Print(LogClass.ServiceOvln, $"SourceName: {name.Unknown.ToHexString()}"); + + return Result.Success; + } + + [CmifCommand(1)] + public Result RemoveSource(SourceName name) + { + Logger.Debug?.Print(LogClass.ServiceOvln, $"SourceName: {name.Unknown.ToHexString()}"); + + return Result.Success; + } + + [CmifCommand(2)] + public Result GetReceiveEventHandle([CopyHandle] out int handle) + { + if (_handle == 0) + { + Os.CreateSystemEvent(out _recvEvent, EventClearMode.ManualClear, true).AbortOnFailure(); + + _handle = Os.GetReadableHandleOfSystemEvent(ref _recvEvent); + + Os.SignalSystemEvent(ref _recvEvent); + } + + handle = _handle; + + return Result.Success; + } + + [CmifCommand(4)] + public Result Receive() + { + return Result.Success; + } + + + public void Dispose() + { + if (_handle != 0 && Interlocked.Exchange(ref _disposalState, 1) == 0) + { + Os.DestroySystemEvent(ref _recvEvent); + } + } + } +} diff --git a/src/Ryujinx.Horizon/Ovln/Ipc/ReceiverService.cs b/src/Ryujinx.Horizon/Ovln/Ipc/ReceiverService.cs index b74d3cd2b..5ff14c19d 100644 --- a/src/Ryujinx.Horizon/Ovln/Ipc/ReceiverService.cs +++ b/src/Ryujinx.Horizon/Ovln/Ipc/ReceiverService.cs @@ -1,8 +1,21 @@ +using LibHac; +using Ryujinx.Common.Logging; using Ryujinx.Horizon.Sdk.Ovln; +using Ryujinx.Horizon.Sdk.Sf; namespace Ryujinx.Horizon.Ovln.Ipc { partial class ReceiverService : IReceiverService { + [CmifCommand(0)] + // OpenReceiver() -> object + public Result OpenReceiver(out IReceiver service) + { + service = new Receiver(); + + Logger.Stub?.PrintStub(LogClass.ServiceOvln); + + return Result.Success; + } } } diff --git a/src/Ryujinx.Horizon/Ovln/Ipc/Sender.cs b/src/Ryujinx.Horizon/Ovln/Ipc/Sender.cs index 20f07d067..b352dd233 100644 --- a/src/Ryujinx.Horizon/Ovln/Ipc/Sender.cs +++ b/src/Ryujinx.Horizon/Ovln/Ipc/Sender.cs @@ -1,4 +1,5 @@ -using Ryujinx.Horizon.Common; +using Ryujinx.Common.Logging; +using Ryujinx.Horizon.Common; using Ryujinx.Horizon.Sdk.Ovln; using Ryujinx.Horizon.Sdk.Sf; @@ -6,5 +7,15 @@ namespace Ryujinx.Horizon.Ovln.Ipc { partial class Sender : ISender { + [CmifCommand(0)] + // OpenSender() -> object + public Result OpenSender(out ISender service) + { + service = new Sender(); + + Logger.Stub?.PrintStub(LogClass.ServiceOvln); + + return Result.Success; + } } } diff --git a/src/Ryujinx.Horizon/Sdk/Ovln/IReceiver.cs b/src/Ryujinx.Horizon/Sdk/Ovln/IReceiver.cs new file mode 100644 index 000000000..1d5d77951 --- /dev/null +++ b/src/Ryujinx.Horizon/Sdk/Ovln/IReceiver.cs @@ -0,0 +1,12 @@ +using Ryujinx.Horizon.Common; +using Ryujinx.Horizon.Sdk.Sf; + +namespace Ryujinx.Horizon.Sdk.Ovln +{ + interface IReceiver : IServiceObject + { + Result AddSource(SourceName name); + Result RemoveSource(SourceName name); + Result GetReceiveEventHandle(out int handle); + } +} diff --git a/src/Ryujinx.Horizon/Sdk/Ovln/SourceName.cs b/src/Ryujinx.Horizon/Sdk/Ovln/SourceName.cs new file mode 100644 index 000000000..2634c8a44 --- /dev/null +++ b/src/Ryujinx.Horizon/Sdk/Ovln/SourceName.cs @@ -0,0 +1,10 @@ +using System.Runtime.InteropServices; + +namespace Ryujinx.Horizon.Sdk.Ovln +{ + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct SourceName { + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x10)] + public byte[] Unknown; + } +} diff --git a/src/Ryujinx.Input.SDL2/SDL2Gamepad.cs b/src/Ryujinx.Input.SDL2/SDL2Gamepad.cs index 3b243eed0..b5fe7fcf8 100644 --- a/src/Ryujinx.Input.SDL2/SDL2Gamepad.cs +++ b/src/Ryujinx.Input.SDL2/SDL2Gamepad.cs @@ -258,6 +258,7 @@ namespace Ryujinx.Input.SDL2 _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.LeftTrigger, (GamepadButtonInputId)_configuration.LeftJoycon.ButtonZl)); _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleRightTrigger0, (GamepadButtonInputId)_configuration.LeftJoycon.ButtonSr)); _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleLeftTrigger0, (GamepadButtonInputId)_configuration.LeftJoycon.ButtonSl)); + _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.Capture, (GamepadButtonInputId)_configuration.LeftJoycon.ButtonCapture)); // Finally right joycon _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.RightStick, (GamepadButtonInputId)_configuration.RightJoyconStick.StickButton)); @@ -270,6 +271,7 @@ namespace Ryujinx.Input.SDL2 _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.RightTrigger, (GamepadButtonInputId)_configuration.RightJoycon.ButtonZr)); _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleRightTrigger1, (GamepadButtonInputId)_configuration.RightJoycon.ButtonSr)); _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleLeftTrigger1, (GamepadButtonInputId)_configuration.RightJoycon.ButtonSl)); + _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.Home, (GamepadButtonInputId)_configuration.RightJoycon.ButtonHome)); SetTriggerThreshold(_configuration.TriggerThreshold); } diff --git a/src/Ryujinx.Input.SDL2/SDL2Keyboard.cs b/src/Ryujinx.Input.SDL2/SDL2Keyboard.cs index 6f4f839a5..710ae2e13 100644 --- a/src/Ryujinx.Input.SDL2/SDL2Keyboard.cs +++ b/src/Ryujinx.Input.SDL2/SDL2Keyboard.cs @@ -371,6 +371,7 @@ namespace Ryujinx.Input.SDL2 _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.LeftTrigger, (Key)_configuration.LeftJoycon.ButtonZl)); _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleRightTrigger0, (Key)_configuration.LeftJoycon.ButtonSr)); _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleLeftTrigger0, (Key)_configuration.LeftJoycon.ButtonSl)); + _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.Capture, (Key)_configuration.LeftJoycon.ButtonCapture)); // Finally configure right joycon _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.RightStick, (Key)_configuration.RightJoyconStick.StickButton)); @@ -383,6 +384,7 @@ namespace Ryujinx.Input.SDL2 _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.RightTrigger, (Key)_configuration.RightJoycon.ButtonZr)); _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleRightTrigger1, (Key)_configuration.RightJoycon.ButtonSr)); _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleLeftTrigger1, (Key)_configuration.RightJoycon.ButtonSl)); + _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.Home, (Key)_configuration.RightJoycon.ButtonHome)); } } diff --git a/src/Ryujinx.Input/GamepadButtonInputId.cs b/src/Ryujinx.Input/GamepadButtonInputId.cs index 59baa22b4..9bf531e2d 100644 --- a/src/Ryujinx.Input/GamepadButtonInputId.cs +++ b/src/Ryujinx.Input/GamepadButtonInputId.cs @@ -35,6 +35,9 @@ namespace Ryujinx.Input Guide, Misc1, + + Home = Guide, + Capture = Misc1, // Xbox Elite paddle Paddle1, diff --git a/src/Ryujinx.Input/HLE/NpadController.cs b/src/Ryujinx.Input/HLE/NpadController.cs index b77ae5662..312a56693 100644 --- a/src/Ryujinx.Input/HLE/NpadController.cs +++ b/src/Ryujinx.Input/HLE/NpadController.cs @@ -49,7 +49,9 @@ namespace Ryujinx.Input.HLE new(GamepadButtonInputId.SingleLeftTrigger0, ControllerKeys.SlLeft), new(GamepadButtonInputId.SingleRightTrigger0, ControllerKeys.SrLeft), new(GamepadButtonInputId.SingleLeftTrigger1, ControllerKeys.SlRight), - new(GamepadButtonInputId.SingleRightTrigger1, ControllerKeys.SrRight) + new(GamepadButtonInputId.SingleRightTrigger1, ControllerKeys.SrRight), + new(GamepadButtonInputId.Capture, ControllerKeys.Capture), + new(GamepadButtonInputId.Home, ControllerKeys.Home) ]; private class HLEKeyboardMappingEntry diff --git a/src/Ryujinx.Input/HLE/NpadManager.cs b/src/Ryujinx.Input/HLE/NpadManager.cs index 4a54b7ead..9d12bb393 100644 --- a/src/Ryujinx.Input/HLE/NpadManager.cs +++ b/src/Ryujinx.Input/HLE/NpadManager.cs @@ -209,6 +209,9 @@ namespace Ryujinx.Input.HLE List hleMotionStates = new(NpadDevices.MaxControllers); KeyboardInput? hleKeyboardInput = null; + + bool homeDown = false; + bool captureDown = false; foreach (InputConfig inputConfig in _inputConfig) { @@ -238,6 +241,11 @@ namespace Ryujinx.Input.HLE SixAxisInput altMotionState = isJoyconPair ? controller.GetHLEMotionState(true) : default; motionState = (controller.GetHLEMotionState(), altMotionState); + + homeDown |= inputState.Buttons.HasFlag(ControllerKeys.Home); + captureDown |= inputState.Buttons.HasFlag(ControllerKeys.Capture); + + inputState.Buttons &= ~(ControllerKeys.Home | ControllerKeys.Capture); } else { @@ -266,6 +274,9 @@ namespace Ryujinx.Input.HLE _device.Hid.Npads.Update(hleInputStates); _device.Hid.Npads.UpdateSixAxis(hleMotionStates); + + _device.Hid.HomeButton.Update(homeDown); + _device.Hid.CaptureButton.Update(captureDown); if (hleKeyboardInput.HasValue) { @@ -315,6 +326,7 @@ namespace Ryujinx.Input.HLE } _device.TamperMachine.UpdateInput(hleInputStates); + _device.UpdateWindowSystemInput(); } } diff --git a/src/Ryujinx/AppHost.cs b/src/Ryujinx/AppHost.cs index 46dac3582..d232790dd 100644 --- a/src/Ryujinx/AppHost.cs +++ b/src/Ryujinx/AppHost.cs @@ -967,6 +967,7 @@ namespace Ryujinx.Ava ConfigurationState.Instance.Multiplayer.LdnServer, ConfigurationState.Instance.Graphics.CustomVSyncInterval.Value, ConfigurationState.Instance.Hacks.ShowDirtyHacks ? ConfigurationState.Instance.Hacks.EnabledHacks : null)); + //Device.LoadSystemProgramId(0x010000000000100C); } private static IHardwareDeviceDriver InitializeAudio() diff --git a/src/Ryujinx/Assets/locales.json b/src/Ryujinx/Assets/locales.json index 28bebb46d..e4b94ae4d 100644 --- a/src/Ryujinx/Assets/locales.json +++ b/src/Ryujinx/Assets/locales.json @@ -7122,6 +7122,56 @@ "zh_TW": "" } }, + { + "ID": "ControllerSettingsButtonHome", + "Translations": { + "ar_SA": "", + "de_DE": "", + "el_GR": "", + "en_US": "Home", + "es_ES": "", + "fr_FR": "", + "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": "ControllerSettingsButtonCapture", + "Translations": { + "ar_SA": "", + "de_DE": "", + "el_GR": "", + "en_US": "Capture", + "es_ES": "", + "fr_FR": "", + "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": "ControllerSettingsDPad", "Translations": { diff --git a/src/Ryujinx/Headless/HeadlessRyujinx.Init.cs b/src/Ryujinx/Headless/HeadlessRyujinx.Init.cs index e873f4c74..a1088c8c1 100644 --- a/src/Ryujinx/Headless/HeadlessRyujinx.Init.cs +++ b/src/Ryujinx/Headless/HeadlessRyujinx.Init.cs @@ -118,6 +118,7 @@ namespace Ryujinx.Headless ButtonZl = Key.Q, ButtonSl = Key.Unbound, ButtonSr = Key.Unbound, + ButtonCapture = Key.Unbound, }, LeftJoyconStick = new JoyconConfigKeyboardStick @@ -140,6 +141,7 @@ namespace Ryujinx.Headless ButtonZr = Key.O, ButtonSl = Key.Unbound, ButtonSr = Key.Unbound, + ButtonHome = Key.Unbound, }, RightJoyconStick = new JoyconConfigKeyboardStick @@ -178,6 +180,7 @@ namespace Ryujinx.Headless ButtonZl = ConfigGamepadInputId.LeftTrigger, ButtonSl = ConfigGamepadInputId.Unbound, ButtonSr = ConfigGamepadInputId.Unbound, + ButtonCapture = ConfigGamepadInputId.Capture, }, LeftJoyconStick = new JoyconConfigControllerStick @@ -200,6 +203,7 @@ namespace Ryujinx.Headless ButtonZr = ConfigGamepadInputId.RightTrigger, ButtonSl = ConfigGamepadInputId.Unbound, ButtonSr = ConfigGamepadInputId.Unbound, + ButtonHome = ConfigGamepadInputId.Home, }, RightJoyconStick = new JoyconConfigControllerStick diff --git a/src/Ryujinx/Input/AvaloniaKeyboard.cs b/src/Ryujinx/Input/AvaloniaKeyboard.cs index 031d8b033..6ab1712ed 100644 --- a/src/Ryujinx/Input/AvaloniaKeyboard.cs +++ b/src/Ryujinx/Input/AvaloniaKeyboard.cs @@ -128,6 +128,7 @@ namespace Ryujinx.Ava.Input _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.LeftTrigger, (Key)_configuration.LeftJoycon.ButtonZl)); _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleRightTrigger0, (Key)_configuration.LeftJoycon.ButtonSr)); _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleLeftTrigger0, (Key)_configuration.LeftJoycon.ButtonSl)); + _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.Capture, (Key)_configuration.LeftJoycon.ButtonCapture)); // Right JoyCon _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.RightStick, (Key)_configuration.RightJoyconStick.StickButton)); @@ -140,6 +141,7 @@ namespace Ryujinx.Ava.Input _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.RightTrigger, (Key)_configuration.RightJoycon.ButtonZr)); _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleRightTrigger1, (Key)_configuration.RightJoycon.ButtonSr)); _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleLeftTrigger1, (Key)_configuration.RightJoycon.ButtonSl)); + _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.Home, (Key)_configuration.RightJoycon.ButtonHome)); #pragma warning restore IDE0055 } } diff --git a/src/Ryujinx/UI/Models/Input/GamepadInputConfig.cs b/src/Ryujinx/UI/Models/Input/GamepadInputConfig.cs index 300d7977c..82b1e16c6 100644 --- a/src/Ryujinx/UI/Models/Input/GamepadInputConfig.cs +++ b/src/Ryujinx/UI/Models/Input/GamepadInputConfig.cs @@ -43,7 +43,9 @@ namespace Ryujinx.Ava.UI.Models.Input [ObservableProperty] private GamepadInputId _dpadRight; [ObservableProperty] private GamepadInputId _buttonMinus; - [ObservableProperty] private GamepadInputId _buttonPlus; + [ObservableProperty] private GamepadInputId _buttonPlus; + [ObservableProperty] private GamepadInputId _buttonCapture; + [ObservableProperty] private GamepadInputId _buttonHome; [ObservableProperty] private GamepadInputId _buttonA; [ObservableProperty] private GamepadInputId _buttonB; @@ -140,6 +142,7 @@ namespace Ryujinx.Ava.UI.Models.Input LeftButtonSl = controllerInput.LeftJoycon.ButtonSl; LeftButtonSr = controllerInput.LeftJoycon.ButtonSr; ButtonZl = controllerInput.LeftJoycon.ButtonZl; + ButtonCapture = controllerInput.LeftJoycon.ButtonCapture; ButtonA = controllerInput.RightJoycon.ButtonA; ButtonB = controllerInput.RightJoycon.ButtonB; @@ -150,6 +153,7 @@ namespace Ryujinx.Ava.UI.Models.Input RightButtonSl = controllerInput.RightJoycon.ButtonSl; RightButtonSr = controllerInput.RightJoycon.ButtonSr; ButtonZr = controllerInput.RightJoycon.ButtonZr; + ButtonHome = controllerInput.RightJoycon.ButtonHome; DeadzoneLeft = controllerInput.DeadzoneLeft; DeadzoneRight = controllerInput.DeadzoneRight; @@ -215,6 +219,7 @@ namespace Ryujinx.Ava.UI.Models.Input ButtonSl = LeftButtonSl, ButtonSr = LeftButtonSr, ButtonZl = ButtonZl, + ButtonCapture = ButtonCapture, }, RightJoycon = new RightJoyconCommonConfig { @@ -227,6 +232,7 @@ namespace Ryujinx.Ava.UI.Models.Input ButtonSr = RightButtonSr, ButtonR = ButtonR, ButtonZr = ButtonZr, + ButtonHome = ButtonHome, }, LeftJoyconStick = new JoyconConfigControllerStick { diff --git a/src/Ryujinx/UI/Models/Input/KeyboardInputConfig.cs b/src/Ryujinx/UI/Models/Input/KeyboardInputConfig.cs index 2d2e95773..098e8f7a6 100644 --- a/src/Ryujinx/UI/Models/Input/KeyboardInputConfig.cs +++ b/src/Ryujinx/UI/Models/Input/KeyboardInputConfig.cs @@ -30,6 +30,8 @@ namespace Ryujinx.Ava.UI.Models.Input [ObservableProperty] private Key _buttonMinus; [ObservableProperty] private Key _buttonPlus; + [ObservableProperty] private Key _buttonCapture; + [ObservableProperty] private Key _buttonHome; [ObservableProperty] private Key _buttonA; [ObservableProperty] private Key _buttonB; @@ -82,6 +84,7 @@ namespace Ryujinx.Ava.UI.Models.Input LeftButtonSl = keyboardConfig.LeftJoycon.ButtonSl; LeftButtonSr = keyboardConfig.LeftJoycon.ButtonSr; ButtonZl = keyboardConfig.LeftJoycon.ButtonZl; + ButtonCapture = keyboardConfig.LeftJoycon.ButtonCapture; ButtonA = keyboardConfig.RightJoycon.ButtonA; ButtonB = keyboardConfig.RightJoycon.ButtonB; @@ -92,6 +95,7 @@ namespace Ryujinx.Ava.UI.Models.Input RightButtonSl = keyboardConfig.RightJoycon.ButtonSl; RightButtonSr = keyboardConfig.RightJoycon.ButtonSr; ButtonZr = keyboardConfig.RightJoycon.ButtonZr; + ButtonHome = keyboardConfig.RightJoycon.ButtonHome; } } @@ -114,6 +118,7 @@ namespace Ryujinx.Ava.UI.Models.Input ButtonZl = ButtonZl, ButtonSl = LeftButtonSl, ButtonSr = LeftButtonSr, + ButtonCapture = ButtonCapture, }, RightJoycon = new RightJoyconCommonConfig { @@ -126,6 +131,7 @@ namespace Ryujinx.Ava.UI.Models.Input ButtonSr = RightButtonSr, ButtonR = ButtonR, ButtonZr = ButtonZr, + ButtonHome = ButtonHome, }, LeftJoyconStick = new JoyconConfigKeyboardStick { diff --git a/src/Ryujinx/UI/ViewModels/Input/InputViewModel.cs b/src/Ryujinx/UI/ViewModels/Input/InputViewModel.cs index 5b7bcfd32..70ecfe373 100644 --- a/src/Ryujinx/UI/ViewModels/Input/InputViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/Input/InputViewModel.cs @@ -561,6 +561,7 @@ namespace Ryujinx.Ava.UI.ViewModels.Input ButtonZl = Key.Q, ButtonSl = Key.Unbound, ButtonSr = Key.Unbound, + ButtonCapture = Key.Unbound, }, LeftJoyconStick = new JoyconConfigKeyboardStick @@ -582,6 +583,7 @@ namespace Ryujinx.Ava.UI.ViewModels.Input ButtonZr = Key.O, ButtonSl = Key.Unbound, ButtonSr = Key.Unbound, + ButtonHome = Key.Unbound, }, RightJoyconStick = new JoyconConfigKeyboardStick { @@ -621,6 +623,7 @@ namespace Ryujinx.Ava.UI.ViewModels.Input ButtonZl = ConfigGamepadInputId.LeftTrigger, ButtonSl = ConfigGamepadInputId.Unbound, ButtonSr = ConfigGamepadInputId.Unbound, + ButtonCapture = ConfigGamepadInputId.Capture, }, LeftJoyconStick = new JoyconConfigControllerStick { @@ -640,6 +643,7 @@ namespace Ryujinx.Ava.UI.ViewModels.Input ButtonZr = ConfigGamepadInputId.RightTrigger, ButtonSl = ConfigGamepadInputId.Unbound, ButtonSr = ConfigGamepadInputId.Unbound, + ButtonHome = ConfigGamepadInputId.Home, }, RightJoyconStick = new JoyconConfigControllerStick { diff --git a/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs b/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs index 499eedc8d..1029cad0e 100644 --- a/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/MainWindowViewModel.cs @@ -1568,6 +1568,8 @@ namespace Ryujinx.Ava.UI.ViewModels return; } + + AppHost.Device.System.SetupFirst(AppHost.Device.Processes.ActiveApplication.ProcessId); CanUpdate = false; diff --git a/src/Ryujinx/UI/Views/Input/ControllerInputView.axaml b/src/Ryujinx/UI/Views/Input/ControllerInputView.axaml index 49c2cfd4c..0e0a7edb3 100644 --- a/src/Ryujinx/UI/Views/Input/ControllerInputView.axaml +++ b/src/Ryujinx/UI/Views/Input/ControllerInputView.axaml @@ -309,6 +309,21 @@ + + + + + + + + + + + + diff --git a/src/Ryujinx/UI/Views/Input/ControllerInputView.axaml.cs b/src/Ryujinx/UI/Views/Input/ControllerInputView.axaml.cs index d04085a89..6ae10452a 100644 --- a/src/Ryujinx/UI/Views/Input/ControllerInputView.axaml.cs +++ b/src/Ryujinx/UI/Views/Input/ControllerInputView.axaml.cs @@ -184,6 +184,12 @@ namespace Ryujinx.Ava.UI.Views.Input case "RightJoystick": viewModel.Config.RightJoystick = buttonValue.AsHidType(); break; + case "ButtonCapture": + viewModel.Config.ButtonCapture = buttonValue.AsHidType(); + break; + case "ButtonHome": + viewModel.Config.ButtonHome = buttonValue.AsHidType(); + break; } } }; diff --git a/src/Ryujinx/UI/Views/Input/KeyboardInputView.axaml b/src/Ryujinx/UI/Views/Input/KeyboardInputView.axaml index e830412c1..ca0a2fc41 100644 --- a/src/Ryujinx/UI/Views/Input/KeyboardInputView.axaml +++ b/src/Ryujinx/UI/Views/Input/KeyboardInputView.axaml @@ -302,6 +302,21 @@ + + + + + + + + + + + + diff --git a/src/Ryujinx/UI/Views/Input/KeyboardInputView.axaml.cs b/src/Ryujinx/UI/Views/Input/KeyboardInputView.axaml.cs index 99e424d4f..b4b76d9b9 100644 --- a/src/Ryujinx/UI/Views/Input/KeyboardInputView.axaml.cs +++ b/src/Ryujinx/UI/Views/Input/KeyboardInputView.axaml.cs @@ -85,7 +85,7 @@ namespace Ryujinx.Ava.UI.Views.Input break; case "ButtonMinus": viewModel.Config.ButtonMinus = buttonValue.AsHidType(); - break; + break; case "LeftStickButton": viewModel.Config.LeftStickButton = buttonValue.AsHidType(); break; @@ -161,6 +161,12 @@ namespace Ryujinx.Ava.UI.Views.Input case "RightStickLeft": viewModel.Config.RightStickLeft = buttonValue.AsHidType(); break; + case "ButtonCapture": + viewModel.Config.ButtonCapture = buttonValue.AsHidType(); + break; + case "ButtonHome": + viewModel.Config.ButtonHome = buttonValue.AsHidType(); + break; } } }; diff --git a/src/Ryujinx/Utilities/Configuration/ConfigurationState.Migration.cs b/src/Ryujinx/Utilities/Configuration/ConfigurationState.Migration.cs index 0fb3fb754..33ae72f14 100644 --- a/src/Ryujinx/Utilities/Configuration/ConfigurationState.Migration.cs +++ b/src/Ryujinx/Utilities/Configuration/ConfigurationState.Migration.cs @@ -223,6 +223,7 @@ namespace Ryujinx.Ava.Utilities.Configuration ButtonZl = Key.Q, ButtonSl = Key.Unbound, ButtonSr = Key.Unbound, + ButtonCapture = Key.Unbound, }, LeftJoyconStick = new JoyconConfigKeyboardStick { @@ -243,6 +244,7 @@ namespace Ryujinx.Ava.Utilities.Configuration ButtonZr = Key.O, ButtonSl = Key.Unbound, ButtonSr = Key.Unbound, + ButtonHome = Key.Unbound, }, RightJoyconStick = new JoyconConfigKeyboardStick { diff --git a/src/Ryujinx/Utilities/Configuration/ConfigurationState.cs b/src/Ryujinx/Utilities/Configuration/ConfigurationState.cs index 4ab77a60f..1fe04d40a 100644 --- a/src/Ryujinx/Utilities/Configuration/ConfigurationState.cs +++ b/src/Ryujinx/Utilities/Configuration/ConfigurationState.cs @@ -275,6 +275,7 @@ namespace Ryujinx.Ava.Utilities.Configuration ButtonZl = Key.Q, ButtonSl = Key.Unbound, ButtonSr = Key.Unbound, + ButtonCapture = Key.Unbound, }, LeftJoyconStick = new JoyconConfigKeyboardStick { @@ -295,6 +296,7 @@ namespace Ryujinx.Ava.Utilities.Configuration ButtonZr = Key.O, ButtonSl = Key.Unbound, ButtonSr = Key.Unbound, + ButtonHome = Key.Unbound, }, RightJoyconStick = new JoyconConfigKeyboardStick {