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
{