Implement application switching

Co-authored-by: Alula <6276139+alula@users.noreply.github.com>
This commit is contained in:
Jacobwasbeast 2025-02-10 08:30:48 -06:00
parent ffcc13e1cc
commit f67cf6a87c
58 changed files with 1458 additions and 596 deletions

View File

@ -37,6 +37,9 @@ namespace Ryujinx.Common.Configuration.Hid.Controller
Guide, Guide,
Misc1, Misc1,
Home = Guide,
Capture = Misc1,
// Xbox Elite paddle // Xbox Elite paddle
Paddle1, Paddle1,
Paddle2, Paddle2,

View File

@ -10,7 +10,7 @@ namespace Ryujinx.Common.Configuration.Hid
/// <summary> /// <summary>
/// The current version of the input file format /// The current version of the input file format
/// </summary> /// </summary>
public const int CurrentVersion = 1; public const int CurrentVersion = 2;
public int Version { get; set; } public int Version { get; set; }

View File

@ -7,6 +7,7 @@ namespace Ryujinx.Common.Configuration.Hid
public TButton ButtonZl { get; set; } public TButton ButtonZl { get; set; }
public TButton ButtonSl { get; set; } public TButton ButtonSl { get; set; }
public TButton ButtonSr { get; set; } public TButton ButtonSr { get; set; }
public TButton ButtonCapture { get; set; }
public TButton DpadUp { get; set; } public TButton DpadUp { get; set; }
public TButton DpadDown { get; set; } public TButton DpadDown { get; set; }
public TButton DpadLeft { get; set; } public TButton DpadLeft { get; set; }

View File

@ -11,5 +11,6 @@ namespace Ryujinx.Common.Configuration.Hid
public TButton ButtonB { get; set; } public TButton ButtonB { get; set; }
public TButton ButtonY { get; set; } public TButton ButtonY { get; set; }
public TButton ButtonA { get; set; } public TButton ButtonA { get; set; }
public TButton ButtonHome { get; set; }
} }
} }

View File

@ -4,6 +4,7 @@ using Ryujinx.HLE.HOS.Kernel.Process;
using Ryujinx.HLE.HOS.Kernel.Threading; using Ryujinx.HLE.HOS.Kernel.Threading;
using Ryujinx.Horizon; using Ryujinx.Horizon;
using Ryujinx.Horizon.Sdk.OsTypes; using Ryujinx.Horizon.Sdk.OsTypes;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading; using System.Threading;
@ -17,8 +18,8 @@ namespace Ryujinx.HLE.HOS.Applets
private WindowSystem _windowSystem; private WindowSystem _windowSystem;
// Guest event handle to wake up the event loop // Guest event handle to wake up the event loop
internal SystemEventType _wakeupEvent; private SystemEventType _wakeupEvent;
internal MultiWaitHolder _wakeupHolder; private MultiWaitHolder _wakeupHolder;
private KWritableEvent _wakeupEventObj; private KWritableEvent _wakeupEventObj;
// List of owned process holders // List of owned process holders
@ -195,32 +196,42 @@ namespace Ryujinx.HLE.HOS.Applets
private void ThreadFunc() private void ThreadFunc()
{ {
HorizonStatic.Register( try
default,
_system.KernelContext.Syscall,
null,
_thread.ThreadContext,
(int)_thread.ThreadContext.GetX(1));
// lock (_lock)
{ {
Os.CreateSystemEvent(out _wakeupEvent, EventClearMode.ManualClear, true).AbortOnFailure(); HorizonStatic.Register(
_wakeupEventObj = _thread.Owner.HandleTable.GetObject<KWritableEvent>(Os.GetWritableHandleOfSystemEvent(ref _wakeupEvent)); default,
_system.KernelContext.Syscall,
null,
_thread.ThreadContext,
(int)_thread.ThreadContext.GetX(1));
_wakeupHolder = new MultiWaitHolderOfInterProcessEvent(_wakeupEvent.InterProcessEvent); lock (_lock)
_wakeupHolder.UserData = UserDataTag.WakeupEvent;
_multiWait.LinkMultiWaitHolder(_wakeupHolder);
}
while (!_cts.Token.IsCancellationRequested)
{
var holder = WaitSignaled();
if (holder == null)
{ {
break; Os.CreateSystemEvent(out _wakeupEvent, EventClearMode.ManualClear, true).AbortOnFailure();
_wakeupEventObj = _thread.Owner.HandleTable.GetObject<KWritableEvent>(
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}");
} }
} }
} }

View File

@ -120,6 +120,7 @@ namespace Ryujinx.HLE.HOS.Applets
internal bool IsInteractable = true; internal bool IsInteractable = true;
internal bool WindowVisible = true; internal bool WindowVisible = true;
internal bool ExitLocked = false; internal bool ExitLocked = false;
internal bool IsApplication { get; }
internal AppletStateMgr AppletState { get; private set; } internal AppletStateMgr AppletState { get; private set; }
public event EventHandler AppletStateChanged; public event EventHandler AppletStateChanged;
@ -132,10 +133,11 @@ namespace Ryujinx.HLE.HOS.Applets
public RealApplet(ulong pid, bool isApplication, Horizon system) public RealApplet(ulong pid, bool isApplication, Horizon system)
{ {
_system = system; _system = system;
AppletState = new AppletStateMgr(system, isApplication); AppletState = new AppletStateMgr(system);
ProcessHandle = _system.KernelContext.Processes[pid]; ProcessHandle = _system.KernelContext.Processes[pid];
AppletResourceUserId = ProcessHandle.Pid; AppletResourceUserId = ProcessHandle.Pid;
AppletId = GetAppletIdFromProgramId(ProcessHandle.TitleId); AppletId = GetAppletIdFromProgramId(ProcessHandle.TitleId);
IsApplication = isApplication;
} }
public void RegisterChild(RealApplet applet) public void RegisterChild(RealApplet applet)

View File

@ -7,6 +7,7 @@ using System.Linq;
using Ryujinx.Horizon.Sdk.Applet; using Ryujinx.Horizon.Sdk.Applet;
using Ryujinx.Common; using Ryujinx.Common;
using Ryujinx.HLE.HOS.Applets.Types; using Ryujinx.HLE.HOS.Applets.Types;
using System.Threading;
namespace Ryujinx.HLE.HOS.Applets namespace Ryujinx.HLE.HOS.Applets
{ {
@ -14,21 +15,20 @@ namespace Ryujinx.HLE.HOS.Applets
{ {
private Horizon _system; private Horizon _system;
private readonly object _lock = new(); private readonly object _lock = new();
EventObserver _eventObserver = null; private EventObserver _eventObserver = null;
// Foreground roots // Foreground roots.
RealApplet _homeMenu = null; private RealApplet _homeMenu = null;
RealApplet _overlayDisp = null; private RealApplet _overlayDisp = null;
RealApplet _application = null; // Removed single application field to allow multiple applications.
// Home menu state // Home menu state.
bool _homeMenuForegroundLocked = false; private bool _homeMenuForegroundLocked = false;
RealApplet _foregroundRequestedApplet = null; private RealApplet _foregroundRequestedApplet = null;
// aruid -> applet map.
// aruid -> applet map private Dictionary<ulong, RealApplet> _applets = new();
Dictionary<ulong, RealApplet> _applets = new(); private List<RealApplet> _rootApplets = new();
List<RealApplet> _rootApplets = new();
internal ButtonPressTracker ButtonPressTracker { get; } internal ButtonPressTracker ButtonPressTracker { get; }
@ -60,6 +60,7 @@ namespace Ryujinx.HLE.HOS.Applets
return; return;
} }
// If no foreground applet is explicitly requested, choose the last root applet.
if (_foregroundRequestedApplet == null && _rootApplets.Count != 0) if (_foregroundRequestedApplet == null && _rootApplets.Count != 0)
{ {
_foregroundRequestedApplet = _rootApplets.Last(); _foregroundRequestedApplet = _rootApplets.Last();
@ -72,6 +73,9 @@ namespace Ryujinx.HLE.HOS.Applets
} }
} }
/// <summary>
/// Tracks a new process as an applet.
/// </summary>
internal RealApplet TrackProcess(ulong pid, ulong callerPid, bool isApplication) internal RealApplet TrackProcess(ulong pid, ulong callerPid, bool isApplication)
{ {
lock (_lock) lock (_lock)
@ -106,6 +110,9 @@ namespace Ryujinx.HLE.HOS.Applets
} }
} }
/// <summary>
/// Registers the applet in the global tracking data structures.
/// </summary>
private void TrackApplet(RealApplet applet, bool isApplication) private void TrackApplet(RealApplet applet, bool isApplication)
{ {
if (_applets.ContainsKey(applet.AppletResourceUserId)) if (_applets.ContainsKey(applet.AppletResourceUserId))
@ -122,30 +129,33 @@ namespace Ryujinx.HLE.HOS.Applets
{ {
_overlayDisp = applet; _overlayDisp = applet;
} }
else if (isApplication) // 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.
_application = applet;
}
_applets[applet.AppletResourceUserId] = applet; _applets[applet.AppletResourceUserId] = applet;
_eventObserver.TrackAppletProcess(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); SetupFirstApplet(applet);
_foregroundRequestedApplet = applet;
applet.AppletState.SetFocus(false);
} }
// _foregroundRequestedApplet = applet;
// applet.AppletState.SetFocusState(FocusState.InFocus);
_eventObserver.RequestUpdate(); _eventObserver.RequestUpdate();
} }
/// <summary>
/// Performs initial setup for the first tracked applet.
/// </summary>
private void SetupFirstApplet(RealApplet applet) private void SetupFirstApplet(RealApplet applet)
{ {
if (applet.AppletId == RealAppletId.SystemAppletMenu) if (applet.AppletId == RealAppletId.SystemAppletMenu)
{ {
//applet.AppletState.SetFocusHandlingMode(false);
applet.AppletState.SetOutOfFocusSuspendingEnabled(false); applet.AppletState.SetOutOfFocusSuspendingEnabled(false);
RequestHomeMenuToGetForeground(); RequestHomeMenuToGetForeground();
} }
@ -158,7 +168,7 @@ namespace Ryujinx.HLE.HOS.Applets
{ {
applet.AppletState.SetFocusState(FocusState.InFocus); applet.AppletState.SetFocusState(FocusState.InFocus);
_foregroundRequestedApplet = applet; _foregroundRequestedApplet = applet;
RequestApplicationToGetForeground(); RequestApplicationToGetForeground(applet.ProcessHandle.Pid);
} }
applet.UpdateSuspensionStateLocked(true); applet.UpdateSuspensionStateLocked(true);
@ -166,70 +176,94 @@ namespace Ryujinx.HLE.HOS.Applets
internal RealApplet GetByAruId(ulong aruid) internal RealApplet GetByAruId(ulong aruid)
{ {
// lock (_lock) if (_applets.TryGetValue(aruid, out RealApplet applet))
{ {
if (_applets.TryGetValue(aruid, out RealApplet applet)) return applet;
{
return applet;
}
return null;
} }
return null;
} }
/// <summary>
/// Returns the current foreground application.
/// If none is explicitly set, the first tracked application is returned.
/// </summary>
internal RealApplet GetMainApplet() internal RealApplet GetMainApplet()
{ {
// lock (_lock) lock (_lock)
{ {
if (_application != null) if (_foregroundRequestedApplet != null && _foregroundRequestedApplet.IsApplication)
{ {
if (_applets.TryGetValue(_application.AppletResourceUserId, out RealApplet applet)) return _foregroundRequestedApplet;
{
return applet;
}
} }
return null; return _rootApplets.FirstOrDefault(applet => applet.IsApplication);
} }
} }
internal void RequestHomeMenuToGetForeground() internal void RequestHomeMenuToGetForeground()
{ {
// lock (_lock) _foregroundRequestedApplet = _homeMenu;
{
_foregroundRequestedApplet = _homeMenu;
}
_eventObserver.RequestUpdate(); _eventObserver.RequestUpdate();
} }
internal void RequestApplicationToGetForeground() /// <summary>
/// Requests that the home menu be focused.
/// The PID provided must match the home menus PID.
/// </summary>
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();
}
/// <summary>
/// Requests that an application be focused.
/// The PID provided must belong to an application applet.
/// </summary>
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(); _eventObserver.RequestUpdate();
} }
internal void RequestLockHomeMenuIntoForeground() internal void RequestLockHomeMenuIntoForeground()
{ {
// lock (_lock) _homeMenuForegroundLocked = true;
{
_homeMenuForegroundLocked = true;
}
_eventObserver.RequestUpdate(); _eventObserver.RequestUpdate();
} }
internal void RequestUnlockHomeMenuFromForeground() internal void RequestUnlockHomeMenuFromForeground()
{ {
// lock (_lock) _homeMenuForegroundLocked = false;
{
_homeMenuForegroundLocked = false;
}
_eventObserver.RequestUpdate(); _eventObserver.RequestUpdate();
} }
@ -245,48 +279,39 @@ namespace Ryujinx.HLE.HOS.Applets
internal void OnOperationModeChanged() 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() 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) internal void OnSystemButtonPress(SystemButtonType type)
{ {
// lock (_lock) switch (type)
{ {
switch (type) case SystemButtonType.PerformHomeButtonShortPressing:
{ SendButtonAppletMessageLocked(AppletMessage.DetectShortPressingHomeButton);
case SystemButtonType.PerformHomeButtonShortPressing: break;
SendButtonAppletMessageLocked(AppletMessage.DetectShortPressingHomeButton); case SystemButtonType.PerformHomeButtonLongPressing:
break; SendButtonAppletMessageLocked(AppletMessage.DetectLongPressingHomeButton);
case SystemButtonType.PerformHomeButtonLongPressing: break;
SendButtonAppletMessageLocked(AppletMessage.DetectLongPressingHomeButton); case SystemButtonType.PerformCaptureButtonShortPressing:
break; SendButtonAppletMessageLocked(AppletMessage.DetectShortPressingCaptureButton);
case SystemButtonType.PerformCaptureButtonShortPressing: break;
SendButtonAppletMessageLocked(AppletMessage.DetectShortPressingCaptureButton);
break;
}
} }
} }
@ -297,6 +322,16 @@ namespace Ryujinx.HLE.HOS.Applets
lock (_homeMenu.Lock) lock (_homeMenu.Lock)
{ {
_homeMenu.AppletState.PushUnorderedMessage(message); _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
} }
} }
/// <summary>
/// Removes terminated applets from tracking.
/// </summary>
private void PruneTerminatedAppletsLocked() 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) lock (applet.Lock)
{ {
@ -320,12 +359,14 @@ namespace Ryujinx.HLE.HOS.Applets
continue; continue;
} }
// If the applet has child applets still, terminate them first.
if (applet.ChildApplets.Count != 0) if (applet.ChildApplets.Count != 0)
{ {
TerminateChildAppletsLocked(applet); TerminateChildAppletsLocked(applet);
continue; continue;
} }
// If this applet was started by another, remove it from its callers child list.
if (applet.CallerApplet != null) if (applet.CallerApplet != null)
{ {
applet.CallerApplet.ChildApplets.Remove(applet); applet.CallerApplet.ChildApplets.Remove(applet);
@ -343,10 +384,14 @@ namespace Ryujinx.HLE.HOS.Applets
_foregroundRequestedApplet = null; _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; if (_foregroundRequestedApplet == applet)
_foregroundRequestedApplet = null; {
_foregroundRequestedApplet = null;
}
if (_homeMenu != null) if (_homeMenu != null)
{ {
@ -363,9 +408,26 @@ namespace Ryujinx.HLE.HOS.Applets
} }
} }
/// <summary>
/// Terminates any child applets of the specified parent.
/// </summary>
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;
}
}
}
/// <summary>
/// If the home menu is locked into the foreground, ensure it remains in front.
/// </summary>
private bool LockHomeMenuIntoForegroundLocked() private bool LockHomeMenuIntoForegroundLocked()
{ {
// If the home menu is not locked into the foreground, then there's nothing to do.
if (_homeMenu == null || !_homeMenuForegroundLocked) if (_homeMenu == null || !_homeMenuForegroundLocked)
{ {
_homeMenuForegroundLocked = false; _homeMenuForegroundLocked = false;
@ -387,18 +449,9 @@ namespace Ryujinx.HLE.HOS.Applets
return true; return true;
} }
private void TerminateChildAppletsLocked(RealApplet parent) /// <summary>
{ /// Updates the state of the specified applet and its children.
foreach (var child in parent.ChildApplets) /// </summary>
{
if (child.ProcessHandle.State != ProcessState.Exited)
{
child.ProcessHandle.Terminate();
child.TerminateResult = (ResultCode)Services.Am.ResultCode.LibraryAppletTerminated;
}
}
}
private void UpdateAppletStateLocked(RealApplet applet, bool isForeground) private void UpdateAppletStateLocked(RealApplet applet, bool isForeground)
{ {
if (applet == null) if (applet == null)
@ -427,7 +480,7 @@ namespace Ryujinx.HLE.HOS.Applets
return false; return false;
}); });
// TODO: Update visibility state // TODO: Update visibility state if needed.
applet.SetInteractibleLocked(isForeground && applet.WindowVisible); applet.SetInteractibleLocked(isForeground && applet.WindowVisible);
@ -445,13 +498,81 @@ namespace Ryujinx.HLE.HOS.Applets
applet.UpdateSuspensionStateLocked(true); 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()}"); 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 // Recurse into child applets.
foreach (var child in applet.ChildApplets) foreach (var child in applet.ChildApplets)
{ {
UpdateAppletStateLocked(child, isForeground); if (child == _foregroundRequestedApplet)
{
UpdateAppletStateLocked(child, true);
}
else
{
UpdateAppletStateLocked(child, isForeground);
}
}
}
}
/// <summary>
/// Returns the process identifier of the currently focused applet.
/// </summary>
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();
} }
} }
} }

View File

@ -64,31 +64,15 @@ namespace Ryujinx.HLE.HOS
internal PerformanceState PerformanceState { get; private set; } internal PerformanceState PerformanceState { get; private set; }
internal AppletStateMgr IntialAppletState { 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; return WindowSystem.GetByAruId(processId).AppletState;
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 IntialAppletState; return IntialAppletState;
}
set
{
if (value != null)
{
IntialAppletState = value;
}
}
} }
internal WindowSystem WindowSystem { get; private set; } internal WindowSystem WindowSystem { get; private set; }
@ -122,6 +106,9 @@ namespace Ryujinx.HLE.HOS
internal CaptureManager CaptureManager { get; private set; } internal CaptureManager CaptureManager { get; private set; }
internal KEvent VsyncEvent { get; private set; } internal KEvent VsyncEvent { get; private set; }
internal KEvent GeneralChannelEvent { get; private set; }
internal Queue<byte[]> GeneralChannelData { get; private set; } = new();
internal KEvent DisplayResolutionChangeEvent { get; private set; } internal KEvent DisplayResolutionChangeEvent { get; private set; }
@ -204,16 +191,13 @@ namespace Ryujinx.HLE.HOS
AppletCaptureBufferTransfer = new KTransferMemory(KernelContext, appletCaptureBufferStorage); AppletCaptureBufferTransfer = new KTransferMemory(KernelContext, appletCaptureBufferStorage);
AppletState = new AppletStateMgr(this, true);
WindowSystem = new WindowSystem(this); WindowSystem = new WindowSystem(this);
EventObserver = new EventObserver(this, WindowSystem); EventObserver = new EventObserver(this, WindowSystem);
AppletState.SetFocus(true);
VsyncEvent = new KEvent(KernelContext); VsyncEvent = new KEvent(KernelContext);
DisplayResolutionChangeEvent = new KEvent(KernelContext); DisplayResolutionChangeEvent = new KEvent(KernelContext);
GeneralChannelEvent = new KEvent(KernelContext);
SharedFontManager = new SharedFontManager(device, fontStorage); SharedFontManager = new SharedFontManager(device, fontStorage);
AccountManager = device.Configuration.AccountManager; AccountManager = device.Configuration.AccountManager;
@ -362,13 +346,24 @@ namespace Ryujinx.HLE.HOS
public void ReturnFocus() public void ReturnFocus()
{ {
AppletState.SetFocus(true); GetAppletState(WindowSystem.GetFocusedApp()).SetFocus(true);
} }
public void SimulateWakeUpMessage() public void SimulateWakeUpMessage()
{ {
// AppletState.Messages.Enqueue(AppletMessage.Resume); PushToGeneralChannel(new byte[] {
// AppletState.MessageEvent.ReadableEvent.Signal(); 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) public void ScanAmiibo(int nfpDeviceId, string amiiboId, bool useRandomUuid)
@ -530,5 +525,12 @@ namespace Ryujinx.HLE.HOS
} }
IsPaused = pause; IsPaused = pause;
} }
public void SetupFirst(ulong ProgramId)
{
bool isApp = ProgramId > 0x01000000000007FF;
RealApplet app = WindowSystem.TrackProcess(ProgramId, 0, isApp);
app.AppletState.SetFocusForce(true);
}
} }
} }

View File

@ -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.LibraryAppletProxy;
using Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.SystemAppletProxy; using Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.SystemAppletProxy;
@ -6,17 +8,24 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService
class ILibraryAppletProxy : IpcService class ILibraryAppletProxy : IpcService
{ {
private readonly ulong _pid; private readonly ulong _pid;
private readonly ServiceCtx _context;
public ILibraryAppletProxy(ulong pid) public ILibraryAppletProxy(ServiceCtx context, ulong pid)
{ {
_context = context;
_pid = pid; _pid = pid;
} }
private RealApplet GetApplet()
{
return _context.Device.System.WindowSystem.GetByAruId(_pid);
}
[CommandCmif(0)] [CommandCmif(0)]
// GetCommonStateGetter() -> object<nn::am::service::ICommonStateGetter> // GetCommonStateGetter() -> object<nn::am::service::ICommonStateGetter>
public ResultCode GetCommonStateGetter(ServiceCtx context) public ResultCode GetCommonStateGetter(ServiceCtx context)
{ {
MakeObject(context, new ICommonStateGetter(context)); MakeObject(context, new ICommonStateGetter(context, _pid));
return ResultCode.Success; return ResultCode.Success;
} }
@ -70,7 +79,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService
// GetLibraryAppletCreator() -> object<nn::am::service::ILibraryAppletCreator> // GetLibraryAppletCreator() -> object<nn::am::service::ILibraryAppletCreator>
public ResultCode GetLibraryAppletCreator(ServiceCtx context) public ResultCode GetLibraryAppletCreator(ServiceCtx context)
{ {
MakeObject(context, new ILibraryAppletCreator()); MakeObject(context, new ILibraryAppletCreator(context, _pid));
return ResultCode.Success; return ResultCode.Success;
} }
@ -92,12 +101,13 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService
return ResultCode.Success; return ResultCode.Success;
} }
[CommandCmif(22)] [CommandCmif(22)]
// GetHomeMenuFunctions() -> object<nn::am::service::IHomeMenuFunctions> // GetHomeMenuFunctions() -> object<nn::am::service::IHomeMenuFunctions>
public ResultCode GetHomeMenuFunctions(ServiceCtx context) public ResultCode GetHomeMenuFunctions(ServiceCtx context)
{ {
MakeObject(context, new IHomeMenuFunctions(context.Device.System)); MakeObject(context, new IHomeMenuFunctions(context.Device.System));
return ResultCode.Success; return ResultCode.Success;
} }
@ -106,6 +116,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService
public ResultCode GetGlobalStateController(ServiceCtx context) public ResultCode GetGlobalStateController(ServiceCtx context)
{ {
MakeObject(context, new IGlobalStateController(context)); MakeObject(context, new IGlobalStateController(context));
return ResultCode.Success; return ResultCode.Success;
} }

View File

@ -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<nn::am::service::ICommonStateGetter>
public ResultCode GetCommonStateGetter(ServiceCtx context)
{
MakeObject(context, new ICommonStateGetter(context, _pid));
return ResultCode.Success;
}
[CommandCmif(1)]
// GetSelfController() -> object<nn::am::service::ISelfController>
public ResultCode GetSelfController(ServiceCtx context)
{
MakeObject(context, new ISelfController(context, _pid));
return ResultCode.Success;
}
[CommandCmif(2)]
// GetWindowController() -> object<nn::am::service::IWindowController>
public ResultCode GetWindowController(ServiceCtx context)
{
MakeObject(context, new IWindowController(_pid));
return ResultCode.Success;
}
[CommandCmif(3)]
// GetAudioController() -> object<nn::am::service::IAudioController>
public ResultCode GetAudioController(ServiceCtx context)
{
MakeObject(context, new IAudioController());
return ResultCode.Success;
}
[CommandCmif(4)]
// GetDisplayController() -> object<nn::am::service::IDisplayController>
public ResultCode GetDisplayController(ServiceCtx context)
{
MakeObject(context, new IDisplayController(context));
return ResultCode.Success;
}
[CommandCmif(11)]
// GetLibraryAppletCreator() -> object<nn::am::service::ILibraryAppletCreator>
public ResultCode GetLibraryAppletCreator(ServiceCtx context)
{
MakeObject(context, new ILibraryAppletCreator(context, _pid));
return ResultCode.Success;
}
[CommandCmif(20)]
// GetOverlayFunctions() -> object<nn::am::service::IOverlayFunctions>
public ResultCode GetOverlayFunctions(ServiceCtx context)
{
MakeObject(context, new IOverlayFunctions(context.Device.System));
return ResultCode.Success;
}
[CommandCmif(21)]
// GetAppletCommonFunctions() -> object<nn::am::service::IAppletCommonFunctions>
public ResultCode GetAppletCommonFunctions(ServiceCtx context)
{
MakeObject(context, new IAppletCommonFunctions());
return ResultCode.Success;
}
[CommandCmif(23)]
// GetGlobalStateController() -> object<nn::am::service::IGlobalStateController>
public ResultCode GetGlobalStateController(ServiceCtx context)
{
MakeObject(context, new IGlobalStateController(context));
return ResultCode.Success;
}
[CommandCmif(1000)]
// GetDebugFunctions() -> object<nn::am::service::IDebugFunctions>
public ResultCode GetDebugFunctions(ServiceCtx context)
{
MakeObject(context, new IDebugFunctions());
return ResultCode.Success;
}
}
}

View File

@ -15,7 +15,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService
// GetCommonStateGetter() -> object<nn::am::service::ICommonStateGetter> // GetCommonStateGetter() -> object<nn::am::service::ICommonStateGetter>
public ResultCode GetCommonStateGetter(ServiceCtx context) public ResultCode GetCommonStateGetter(ServiceCtx context)
{ {
MakeObject(context, new ICommonStateGetter(context)); MakeObject(context, new ICommonStateGetter(context,_pid));
return ResultCode.Success; return ResultCode.Success;
} }
@ -60,7 +60,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService
// GetLibraryAppletCreator() -> object<nn::am::service::ILibraryAppletCreator> // GetLibraryAppletCreator() -> object<nn::am::service::ILibraryAppletCreator>
public ResultCode GetLibraryAppletCreator(ServiceCtx context) public ResultCode GetLibraryAppletCreator(ServiceCtx context)
{ {
MakeObject(context, new ILibraryAppletCreator()); MakeObject(context, new ILibraryAppletCreator(context,_pid));
return ResultCode.Success; return ResultCode.Success;
} }

View File

@ -26,9 +26,11 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Lib
private int _interactiveOutDataEventHandle; private int _interactiveOutDataEventHandle;
private int _indirectLayerHandle; 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; _kernelContext = system.KernelContext;
_stateChangedEvent = new KEvent(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; Horizon horizon = _kernelContext.Device.System;
_indirectLayerHandle = horizon.AppletState.IndirectLayerHandles.Add(_applet); _indirectLayerHandle = horizon.GetAppletState(_pid).IndirectLayerHandles.Add(_applet);
context.ResponseData.Write((ulong)_indirectLayerHandle); context.ResponseData.Write((ulong)_indirectLayerHandle);
@ -255,7 +257,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Lib
Horizon horizon = _kernelContext.Device.System; Horizon horizon = _kernelContext.Device.System;
horizon.AppletState.IndirectLayerHandles.Delete(_indirectLayerHandle); horizon.GetAppletState(_pid).IndirectLayerHandles.Delete(_indirectLayerHandle);
} }
} }
} }

View File

@ -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;
}
}
}

View File

@ -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.Ipc;
using Ryujinx.HLE.HOS.Kernel; using Ryujinx.HLE.HOS.Kernel;
using Ryujinx.HLE.HOS.Kernel.Threading; using Ryujinx.HLE.HOS.Kernel.Threading;
using Ryujinx.HLE.HOS.Services.Account.Acc; using Ryujinx.HLE.HOS.Services.Account.Acc;
using Ryujinx.HLE.HOS.Services.Ns.Types;
using Ryujinx.HLE.Loaders.Processes; using Ryujinx.HLE.Loaders.Processes;
using Ryujinx.Horizon.Common; using Ryujinx.Horizon.Common;
using System; using System;
using System.Threading;
namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.SystemAppletProxy 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 readonly KEvent _stateChangedEvent;
private int _stateChangedEventHandle; private int _stateChangedEventHandle;
public RealApplet applet;
public IApplicationAccessor(ulong pid, ulong applicationId, string contentPath, Horizon system) 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); context.Device.Processes.LoadNca(_contentPath, out processResult);
isApplet = true; 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; 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)] [CommandCmif(101)]
// RequestForApplicationToGetForeground() // RequestForApplicationToGetForeground()
public ResultCode RequestForApplicationToGetForeground(ServiceCtx context) public ResultCode RequestForApplicationToGetForeground(ServiceCtx context)
{ {
// _stateChangedEvent.ReadableEvent.Signal();
Logger.Stub?.PrintStub(LogClass.ServiceAm); 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; return ResultCode.Success;
} }
@ -95,6 +124,28 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
return ResultCode.Success; 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)] [CommandCmif(130)]
// SetUsers() // SetUsers()
public ResultCode SetUsers(ServiceCtx context) public ResultCode SetUsers(ServiceCtx context)
@ -103,5 +154,25 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
bool enable = context.RequestData.ReadBoolean(); bool enable = context.RequestData.ReadBoolean();
return ResultCode.Success; 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;
}
} }
} }

View File

@ -1,4 +1,5 @@
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Applets;
using Ryujinx.HLE.HOS.Ipc; using Ryujinx.HLE.HOS.Ipc;
using Ryujinx.HLE.HOS.Kernel.Threading; using Ryujinx.HLE.HOS.Kernel.Threading;
using Ryujinx.HLE.HOS.Services.Settings.Types; 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.ManagerServer _apmManagerServer;
private readonly Apm.SystemManagerServer _apmSystemManagerServer; private readonly Apm.SystemManagerServer _apmSystemManagerServer;
private readonly RealApplet _applet;
private bool _vrModeEnabled; private bool _vrModeEnabled;
#pragma warning disable CS0414, IDE0052 // Remove unread private member #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 readonly KEvent _acquiredSleepLockEvent;
private int _acquiredSleepLockEventHandle; private int _acquiredSleepLockEventHandle;
public ICommonStateGetter(ServiceCtx context) public ICommonStateGetter(ServiceCtx context, ulong pid)
{ {
_context = context; _context = context;
_applet = context.Device.System.WindowSystem.GetByAruId(pid);
_apmManagerServer = new Apm.ManagerServer(context); _apmManagerServer = new Apm.ManagerServer(context);
_apmSystemManagerServer = new Apm.SystemManagerServer(context); _apmSystemManagerServer = new Apm.SystemManagerServer(context);
@ -42,7 +45,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
// GetEventHandle() -> handle<copy> // GetEventHandle() -> handle<copy>
public ResultCode GetEventHandle(ServiceCtx context) public ResultCode GetEventHandle(ServiceCtx context)
{ {
KEvent messageEvent = context.Device.System.AppletState.MessageEvent; KEvent messageEvent = _applet.AppletState.MessageEvent;
if (_messageEventHandle == 0) if (_messageEventHandle == 0)
{ {
@ -61,12 +64,14 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
// ReceiveMessage() -> nn::am::AppletMessage // ReceiveMessage() -> nn::am::AppletMessage
public ResultCode ReceiveMessage(ServiceCtx context) public ResultCode ReceiveMessage(ServiceCtx context)
{ {
if (!context.Device.System.AppletState.PopMessage(out AppletMessage message)) if (!_applet.AppletState.PopMessage(out AppletMessage message))
{ {
return ResultCode.NoMessages; return ResultCode.NoMessages;
} }
Logger.Info?.Print(LogClass.ServiceAm, $"pid: {_applet.ProcessHandle.Pid}, msg={message}");
context.ResponseData.Write((int)message); context.ResponseData.Write((int)message);
return ResultCode.Success; return ResultCode.Success;
} }
@ -94,7 +99,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
// GetBootMode() -> u8 // GetBootMode() -> u8
public ResultCode GetBootMode(ServiceCtx context) public ResultCode GetBootMode(ServiceCtx context)
{ {
context.ResponseData.Write((byte)0); //Unknown value. context.ResponseData.Write((byte)0); // PmBootMode_Normal
Logger.Stub?.PrintStub(LogClass.ServiceAm); Logger.Stub?.PrintStub(LogClass.ServiceAm);
@ -105,7 +110,19 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
// GetCurrentFocusState() -> u8 // GetCurrentFocusState() -> u8
public ResultCode GetCurrentFocusState(ServiceCtx context) 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; return ResultCode.Success;
} }
@ -116,6 +133,8 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
{ {
Logger.Stub?.PrintStub(LogClass.ServiceAm); Logger.Stub?.PrintStub(LogClass.ServiceAm);
_acquiredSleepLockEvent.ReadableEvent.Signal();
return ResultCode.Success; return ResultCode.Success;
} }
@ -132,15 +151,21 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
} }
context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_acquiredSleepLockEventHandle); 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. return ResultCode.Success;
_acquiredSleepLockEvent.ReadableEvent.Signal(); }
[CommandCmif(20)]
// PushToGeneralChannel(object<nn::am::service::IStorage>)
public ResultCode PushInData(ServiceCtx context)
{
IStorage data = GetObject<IStorage>(context, 0);
Logger.Stub?.PrintStub(LogClass.ServiceAm); Logger.Stub?.PrintStub(LogClass.ServiceAm);
return ResultCode.Success; return ResultCode.Success;
} }
[CommandCmif(31)] [CommandCmif(31)]
[CommandCmif(32)] [CommandCmif(32)]
// GetReaderLockAccessorEx(u32) -> object<nn::am::service::ILockAccessor> // GetReaderLockAccessorEx(u32) -> object<nn::am::service::ILockAccessor>
@ -216,7 +241,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
_vrModeEnabled = vrModeEnabled; _vrModeEnabled = vrModeEnabled;
using LblApi lblApi = new(); using var lblApi = new LblApi();
if (vrModeEnabled) if (vrModeEnabled)
{ {

View File

@ -8,21 +8,16 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
{ {
class IHomeMenuFunctions : IpcService class IHomeMenuFunctions : IpcService
{ {
private readonly KEvent _channelEvent;
private int _channelEventHandle; private int _channelEventHandle;
public IHomeMenuFunctions(Horizon system) public IHomeMenuFunctions(Horizon system) { }
{
// TODO: Signal this Event somewhere in future.
_channelEvent = new KEvent(system.KernelContext);
}
[CommandCmif(10)] [CommandCmif(10)]
// RequestToGetForeground() // RequestToGetForeground()
public ResultCode RequestToGetForeground(ServiceCtx context) public ResultCode RequestToGetForeground(ServiceCtx context)
{ {
Logger.Stub?.PrintStub(LogClass.ServiceAm); Logger.Stub?.PrintStub(LogClass.ServiceAm);
context.Device.System.WindowSystem.RequestApplicationToGetForeground(); context.Device.System.WindowSystem.RequestApplicationToGetForeground(context.Process.Pid);
return ResultCode.Success; return ResultCode.Success;
} }
@ -36,6 +31,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
return ResultCode.Success; return ResultCode.Success;
} }
[CommandCmif(12)] [CommandCmif(12)]
// UnlockForeground() // UnlockForeground()
public ResultCode UnlockForeground(ServiceCtx context) public ResultCode UnlockForeground(ServiceCtx context)
@ -52,7 +48,9 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
{ {
if (_channelEventHandle == 0) 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!"); throw new InvalidOperationException("Out of handles!");
} }

View File

@ -1,11 +1,17 @@
using Ryujinx.HLE.HOS.Kernel.Memory; using Ryujinx.HLE.HOS.Kernel.Memory;
using Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.LibraryAppletCreator; using Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.LibraryAppletCreator;
using Ryujinx.Horizon.Sdk.Applet;
namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.SystemAppletProxy namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.SystemAppletProxy
{ {
class ILibraryAppletCreator : IpcService class ILibraryAppletCreator : IpcService
{ {
public ILibraryAppletCreator() { } private readonly ulong _pid;
public ILibraryAppletCreator(ServiceCtx context, ulong pid)
{
_pid = pid;
}
[CommandCmif(0)] [CommandCmif(0)]
// CreateLibraryApplet(u32, u32) -> object<nn::am::service::ILibraryAppletAccessor> // CreateLibraryApplet(u32, u32) -> object<nn::am::service::ILibraryAppletAccessor>
@ -16,7 +22,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
int libraryAppletMode = context.RequestData.ReadInt32(); int libraryAppletMode = context.RequestData.ReadInt32();
#pragma warning restore IDE0059 #pragma warning restore IDE0059
MakeObject(context, new ILibraryAppletAccessor(appletId, context.Device.System)); MakeObject(context, new ILibraryAppletAccessor(appletId, context.Device.System, _pid));
return ResultCode.Success; return ResultCode.Success;
} }

View File

@ -0,0 +1,7 @@
namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.SystemAppletProxy
{
class IRemoteStorageController : IpcService
{
}
}

View File

@ -24,6 +24,8 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
return ResultCode.Success; return ResultCode.Success;
} }
[CommandCmif(10)] [CommandCmif(10)]
// AcquireForegroundRights() // AcquireForegroundRights()
public ResultCode AcquireForegroundRights(ServiceCtx context) public ResultCode AcquireForegroundRights(ServiceCtx context)

View File

@ -12,6 +12,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE
// OpenSystemAppletProxy(u64, pid, handle<copy>) -> object<nn::am::service::ISystemAppletProxy> // OpenSystemAppletProxy(u64, pid, handle<copy>) -> object<nn::am::service::ISystemAppletProxy>
public ResultCode OpenSystemAppletProxy(ServiceCtx context) public ResultCode OpenSystemAppletProxy(ServiceCtx context)
{ {
context.Device.System.WindowSystem.TrackProcess(context.Request.HandleDesc.PId, 0, false);
MakeObject(context, new ISystemAppletProxy(context.Request.HandleDesc.PId)); MakeObject(context, new ISystemAppletProxy(context.Request.HandleDesc.PId));
return ResultCode.Success; return ResultCode.Success;
@ -22,7 +23,18 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE
// OpenLibraryAppletProxy(u64, pid, handle<copy>) -> object<nn::am::service::ILibraryAppletProxy> // OpenLibraryAppletProxy(u64, pid, handle<copy>) -> object<nn::am::service::ILibraryAppletProxy>
public ResultCode OpenLibraryAppletProxy(ServiceCtx context) 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<copy>) -> object<nn::am::service::IOverlayAppletProxy>
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; return ResultCode.Success;
} }
@ -31,6 +43,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE
// OpenSystemApplicationProxy(u64, pid, handle<copy>) -> object<nn::am::service::IApplicationProxy> // OpenSystemApplicationProxy(u64, pid, handle<copy>) -> object<nn::am::service::IApplicationProxy>
public ResultCode OpenSystemApplicationProxy(ServiceCtx context) public ResultCode OpenSystemApplicationProxy(ServiceCtx context)
{ {
context.Device.System.WindowSystem.TrackProcess(context.Request.HandleDesc.PId, 0, false);
MakeObject(context, new IApplicationProxy(context.Request.HandleDesc.PId)); MakeObject(context, new IApplicationProxy(context.Request.HandleDesc.PId));
return ResultCode.Success; return ResultCode.Success;

View File

@ -1,3 +1,4 @@
using Ryujinx.HLE.HOS.Applets;
using Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.SystemAppletProxy; using Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.SystemAppletProxy;
using Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.ApplicationProxy; using Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.ApplicationProxy;
@ -16,7 +17,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService
// GetCommonStateGetter() -> object<nn::am::service::ICommonStateGetter> // GetCommonStateGetter() -> object<nn::am::service::ICommonStateGetter>
public ResultCode GetCommonStateGetter(ServiceCtx context) public ResultCode GetCommonStateGetter(ServiceCtx context)
{ {
MakeObject(context, new ICommonStateGetter(context)); MakeObject(context, new ICommonStateGetter(context, _pid));
return ResultCode.Success; return ResultCode.Success;
} }
@ -61,7 +62,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService
// GetLibraryAppletCreator() -> object<nn::am::service::ILibraryAppletCreator> // GetLibraryAppletCreator() -> object<nn::am::service::ILibraryAppletCreator>
public ResultCode GetLibraryAppletCreator(ServiceCtx context) public ResultCode GetLibraryAppletCreator(ServiceCtx context)
{ {
MakeObject(context, new ILibraryAppletCreator()); MakeObject(context, new ILibraryAppletCreator(context, _pid));
return ResultCode.Success; return ResultCode.Success;
} }

View File

@ -25,7 +25,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid
internal ref SharedMemory SharedMemory => ref _storage.GetRef<SharedMemory>(0); internal ref SharedMemory SharedMemory => ref _storage.GetRef<SharedMemory>(0);
internal const int SharedMemEntryCount = 17; internal const int SharedMemEntryCount = 16;
public DebugPadDevice DebugPad; public DebugPadDevice DebugPad;
public TouchDevice Touchscreen; public TouchDevice Touchscreen;

View File

@ -33,7 +33,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid
SrLeft = 1 << 25, SrLeft = 1 << 25,
SlRight = 1 << 26, SlRight = 1 << 26,
SrRight = 1 << 27, SrRight = 1 << 27,
Capture = 1 << 28,
Home = 1 << 29,
// Generic Catch-all // Generic Catch-all
Up = DpadUp | LStickUp | RStickUp, Up = DpadUp | LStickUp | RStickUp,
Down = DpadDown | LStickDown | RStickDown, Down = DpadDown | LStickDown | RStickDown,

View File

@ -7,7 +7,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common
{ {
struct RingLifo<T> where T : unmanaged, ISampledDataStruct struct RingLifo<T> where T : unmanaged, ISampledDataStruct
{ {
private const ulong MaxEntries = 17; private const ulong MaxEntries = 16;
#pragma warning disable IDE0051, CS0169 // Remove unused private member #pragma warning disable IDE0051, CS0169 // Remove unused private member
private readonly ulong _unused; private readonly ulong _unused;

View File

@ -35,10 +35,11 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad
RightSL = 1 << 26, RightSL = 1 << 26,
RightSR = 1 << 27, RightSR = 1 << 27,
Palma = 1 << 28, Palma = 1 << 28,
Verification = 1 << 29,
// FIXME: Probably a button on Lark.
Unknown29 = 1 << 29,
HandheldLeftB = 1 << 30, HandheldLeftB = 1 << 30,
LeftC = 1UL << 31,
UpC = 1UL << 32,
RightC = 1UL << 33,
DownC = 1UL << 34,
} }
} }

View File

@ -79,6 +79,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory
TouchScreen = RingLifo<TouchScreenState>.Create(), TouchScreen = RingLifo<TouchScreenState>.Create(),
Mouse = RingLifo<MouseState>.Create(), Mouse = RingLifo<MouseState>.Create(),
Keyboard = RingLifo<KeyboardState>.Create(), Keyboard = RingLifo<KeyboardState>.Create(),
HomeButton = RingLifo<ButtonState>.Create(),
SleepButton = RingLifo<ButtonState>.Create(),
CaptureButton = RingLifo<ButtonState>.Create(),
}; };
for (int i = 0; i < result.Npads.Length; i++) for (int i = 0; i < result.Npads.Length; i++)

View File

@ -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 namespace Ryujinx.HLE.HOS.Services.Npns
{ {
[Service("npns:u")] [Service("npns:u")]
class INpnsUser : IpcService 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;
}
} }
} }

View File

@ -26,14 +26,18 @@ namespace Ryujinx.HLE.HOS.Services.Ns
private int _gameCardUpdateDetectionEventHandle; private int _gameCardUpdateDetectionEventHandle;
private KEvent _gameCardMountFailureEvent; private KEvent _gameCardMountFailureEvent;
private int _gameCardMountFailureEventHandle; private int _gameCardMountFailureEventHandle;
private KEvent _gameCardWakeEvent;
private int _gameCardWakeEventHandle;
public IApplicationManagerInterface(ServiceCtx context) public IApplicationManagerInterface(ServiceCtx context)
{ {
_applicationRecordUpdateSystemEvent = new KEvent(context.Device.System.KernelContext); _applicationRecordUpdateSystemEvent = new KEvent(context.Device.System.KernelContext);
_sdCardMountStatusChangedEvent = new KEvent(context.Device.System.KernelContext); _sdCardMountStatusChangedEvent = new KEvent(context.Device.System.KernelContext);
_gameCardUpdateDetectionEvent = 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); Logger.Stub?.PrintStub(LogClass.Service);
return ResultCode.Success; return ResultCode.Success;
} }
[CommandCmif(511)]
// GetGameCardWakenReadyEvent() -> handle<copy>
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)] [CommandCmif(1701)]
// GetApplicationView(buffer<unknown, 5>) -> buffer<unknown, 6> // GetApplicationView(buffer<unknown, 5>) -> buffer<unknown, 6>

View File

@ -2,6 +2,18 @@
{ {
class IDynamicRightsInterface : IpcService 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;
}
} }
} }

View File

@ -1,3 +1,4 @@
using Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.SystemAppletProxy;
using Ryujinx.HLE.HOS.Services.Olsc.OlscServiceForSystemService; using Ryujinx.HLE.HOS.Services.Olsc.OlscServiceForSystemService;
namespace Ryujinx.HLE.HOS.Services.Olsc namespace Ryujinx.HLE.HOS.Services.Olsc
@ -16,6 +17,15 @@ namespace Ryujinx.HLE.HOS.Services.Olsc
return ResultCode.Success; return ResultCode.Success;
} }
[CommandCmif(1)]
// GetRemoteStorageController() -> object<nn::olsc::IRemoteStorageController. >
public ResultCode GetRemoteStorageController(ServiceCtx context)
{
MakeObject(context, new IRemoteStorageController());
return ResultCode.Success;
}
[CommandCmif(2)] [CommandCmif(2)]
// GetDaemonController() -> object<nn::olsc::IDaemonController> // GetDaemonController() -> object<nn::olsc::IDaemonController>
public ResultCode GetDaemonController(ServiceCtx context) public ResultCode GetDaemonController(ServiceCtx context)

View File

@ -37,7 +37,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService.ApplicationDisplayService
#pragma warning restore IDE0059 #pragma warning restore IDE0059
long appletResourceUserId = context.RequestData.ReadInt64(); long appletResourceUserId = context.RequestData.ReadInt64();
ulong pid = context.Device.System.AppletState.AppletResourceUserIds.GetData<ulong>((int)appletResourceUserId); ulong pid = context.Device.System.GetAppletState(context.Process.Pid).AppletResourceUserIds.GetData<ulong>((int)appletResourceUserId);
context.Device.System.SurfaceFlinger.CreateLayer(out long layerId, pid); context.Device.System.SurfaceFlinger.CreateLayer(out long layerId, pid);

View File

@ -388,7 +388,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService
RenderingSurfaceInfo surfaceInfo = new(ColorFormat.A8B8G8R8, (uint)layerWidth, (uint)layerHeight, (uint)pitch, (uint)layerBuffSize); RenderingSurfaceInfo surfaceInfo = new(ColorFormat.A8B8G8R8, (uint)layerWidth, (uint)layerHeight, (uint)pitch, (uint)layerBuffSize);
// Get the applet associated with the handle. // 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) if (appletObject == null)
{ {

View File

@ -1,18 +1,23 @@
using System;
using System.Collections.Concurrent;
using Ryujinx.HLE.HOS.Applets; using Ryujinx.HLE.HOS.Applets;
using Ryujinx.HLE.HOS.Applets.Types; using Ryujinx.HLE.HOS.Applets.Types;
using Ryujinx.HLE.HOS.Kernel.Threading; using Ryujinx.HLE.HOS.Kernel.Threading;
using Ryujinx.HLE.HOS.Services.Am.AppletAE; using Ryujinx.HLE.HOS.Services.Am.AppletAE;
using Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.SystemAppletProxy; using Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.SystemAppletProxy;
using System;
using System.Collections.Concurrent;
namespace Ryujinx.HLE.HOS.SystemState namespace Ryujinx.HLE.HOS.SystemState
{ {
class AppletStateMgr class AppletStateMgr
{ {
public ConcurrentQueue<AppletMessage> Messages; #region Public Properties and Fields
public bool ForcedSuspend { get; private set; } /// <summary>
/// Queue used for unordered messages.
/// </summary>
public ConcurrentQueue<AppletMessage> Messages { get; }
public bool ForcedSuspend { get; set; }
public FocusState AcknowledgedFocusState { get; private set; } = FocusState.Background; public FocusState AcknowledgedFocusState { get; private set; } = FocusState.Background;
public FocusState RequestedFocusState { 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 KEvent LaunchableEvent { get; }
public IdDictionary AppletResourceUserIds { get; } public IdDictionary AppletResourceUserIds { get; }
public IdDictionary IndirectLayerHandles { get; } public IdDictionary IndirectLayerHandles { get; }
internal bool IsApplication { get; } /// <summary>
/// Indicates that an exit has been requested.
/// </summary>
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 _focusStateChangedNotificationEnabled = true;
private bool _operationModeChangedNotificationEnabled = true; private bool _operationModeChangedNotificationEnabled = true;
private bool _performanceModeChangedNotificationEnabled = true; private bool _performanceModeChangedNotificationEnabled = true;
private bool _hasRequestedExit = false;
private bool _hasAcknowledgedExit = false; // Internal event state for message signaling.
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;
private bool _eventSignaled = false; private bool _eventSignaled = false;
// Indicates how the applet handles focus and suspension.
private FocusHandlingMode _focusHandlingMode = FocusHandlingMode.NoSuspend; private FocusHandlingMode _focusHandlingMode = FocusHandlingMode.NoSuspend;
public bool HasRequestedExit => _hasRequestedExit; #endregion
#region Properties with Custom Logic
public bool FocusStateChangedNotificationEnabled public bool FocusStateChangedNotificationEnabled
{ {
@ -59,7 +80,7 @@ namespace Ryujinx.HLE.HOS.SystemState
set set
{ {
_focusStateChangedNotificationEnabled = value; _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<AppletMessage>(); Messages = new ConcurrentQueue<AppletMessage>();
MessageEvent = new KEvent(system.KernelContext); MessageEvent = new KEvent(system.KernelContext);
OperationModeChangedEvent = new KEvent(system.KernelContext); OperationModeChangedEvent = new KEvent(system.KernelContext);
LaunchableEvent = new KEvent(system.KernelContext); LaunchableEvent = new KEvent(system.KernelContext);
@ -95,15 +121,18 @@ namespace Ryujinx.HLE.HOS.SystemState
IndirectLayerHandles = new IdDictionary(); IndirectLayerHandles = new IdDictionary();
} }
#endregion
#region Public Methods
public void SetFocusState(FocusState state) public void SetFocusState(FocusState state)
{ {
if (RequestedFocusState != state) if (RequestedFocusState != state)
{ {
RequestedFocusState = state; RequestedFocusState = state;
_hasFocusStateChanged = true; _hasFocusStateChanged = true;
SignalEventIfNeeded();
} }
SignalEventIfNeeded();
} }
public FocusState GetAndClearFocusState() public FocusState GetAndClearFocusState()
@ -115,10 +144,13 @@ namespace Ryujinx.HLE.HOS.SystemState
public void PushUnorderedMessage(AppletMessage message) public void PushUnorderedMessage(AppletMessage message)
{ {
Messages.Enqueue(message); Messages.Enqueue(message);
SignalEventIfNeeded(); SignalEventIfNeeded();
} }
/// <summary>
/// Attempts to pop the next pending message. If additional messages remain in the queue,
/// signals the event so that consumers can continue processing.
/// </summary>
public bool PopMessage(out AppletMessage message) public bool PopMessage(out AppletMessage message)
{ {
message = GetNextMessage(); message = GetNextMessage();
@ -126,6 +158,201 @@ namespace Ryujinx.HLE.HOS.SystemState
return message != AppletMessage.None; 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
/// <summary>
/// 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.
/// </summary>
private AppletMessage GetNextMessage() private AppletMessage GetNextMessage()
{ {
if (_hasResume) if (_hasResume)
@ -140,31 +367,17 @@ namespace Ryujinx.HLE.HOS.SystemState
return AppletMessage.Exit; 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) FocusState.InFocus => AppletMessage.ChangeIntoForeground,
{ FocusState.OutOfFocus => AppletMessage.ChangeIntoBackground,
_hasFocusStateChanged = false; _ => AppletMessage.FocusStateChanged,
return AppletMessage.FocusStateChanged; };
}
}
else
{
if (RequestedFocusState != AcknowledgedFocusState)
{
AcknowledgedFocusState = RequestedFocusState;
switch (RequestedFocusState)
{
case FocusState.InFocus:
return AppletMessage.ChangeIntoForeground;
case FocusState.OutOfFocus:
return AppletMessage.ChangeIntoBackground;
}
}
}
} }
if (_hasRequestedRequestToPrepareSleep != _hasAcknowledgedRequestToPrepareSleep) if (_hasRequestedRequestToPrepareSleep != _hasAcknowledgedRequestToPrepareSleep)
@ -227,353 +440,54 @@ namespace Ryujinx.HLE.HOS.SystemState
return AppletMessage.AlbumRecordingSaved; return AppletMessage.AlbumRecordingSaved;
} }
if (Messages.TryDequeue(out AppletMessage message)) return Messages.TryDequeue(out var message) ? message : AppletMessage.None;
{
return message;
}
return AppletMessage.None;
} }
internal void SignalEventIfNeeded() /// <summary>
/// Determines whether the internal event should be signaled based on the state flags and message queue.
/// </summary>
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;
}
/// <summary>
/// Signals (or clears) the MessageEvent depending on whether there is any pending work.
/// </summary>
public void SignalEventIfNeeded()
{
bool shouldSignal = ShouldSignalEvent();
if (_eventSignaled != shouldSignal) if (_eventSignaled != shouldSignal)
{ {
if (_eventSignaled) if (shouldSignal)
{
MessageEvent.ReadableEvent.Clear();
_eventSignaled = false;
}
else
{ {
MessageEvent.ReadableEvent.Signal(); MessageEvent.ReadableEvent.Signal();
_eventSignaled = true;
}
}
}
private bool ShouldSignalEvent()
{
bool focusStateChanged = false;
if (_focusStateChangedNotificationEnabled)
{
if (IsApplication)
{
if (_hasFocusStateChanged)
{
focusStateChanged = true;
}
} }
else else
{ {
if (RequestedFocusState != AcknowledgedFocusState) MessageEvent.ReadableEvent.Clear();
{
focusStateChanged = true;
}
} }
} _eventSignaled = shouldSignal;
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;
} }
} }
public void SetOutOfFocusSuspendingEnabled(bool enabled) #endregion
{
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();
}
} }
} }

View File

@ -1,10 +1,14 @@
using LibHac.Common; using LibHac.Common;
using LibHac.Ncm;
using LibHac.Ns; using LibHac.Ns;
using LibHac.Tools.FsSystem.NcaUtils;
using Ryujinx.Audio.Backends.CompatLayer; using Ryujinx.Audio.Backends.CompatLayer;
using Ryujinx.Audio.Integration; using Ryujinx.Audio.Integration;
using Ryujinx.Common; using Ryujinx.Common;
using Ryujinx.Common.Configuration; using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging;
using Ryujinx.Graphics.Gpu; using Ryujinx.Graphics.Gpu;
using Ryujinx.HLE.Exceptions;
using Ryujinx.HLE.FileSystem; using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.HOS; using Ryujinx.HLE.HOS;
using Ryujinx.HLE.HOS.Services.Apm; using Ryujinx.HLE.HOS.Services.Apm;
@ -158,5 +162,23 @@ namespace Ryujinx.HLE
Shared = null; 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);
}
} }
} }

View File

@ -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);
}
}
}
}

View File

@ -1,8 +1,21 @@
using LibHac;
using Ryujinx.Common.Logging;
using Ryujinx.Horizon.Sdk.Ovln; using Ryujinx.Horizon.Sdk.Ovln;
using Ryujinx.Horizon.Sdk.Sf;
namespace Ryujinx.Horizon.Ovln.Ipc namespace Ryujinx.Horizon.Ovln.Ipc
{ {
partial class ReceiverService : IReceiverService partial class ReceiverService : IReceiverService
{ {
[CmifCommand(0)]
// OpenReceiver() -> object<nn::ovln::IReceiver>
public Result OpenReceiver(out IReceiver service)
{
service = new Receiver();
Logger.Stub?.PrintStub(LogClass.ServiceOvln);
return Result.Success;
}
} }
} }

View File

@ -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.Ovln;
using Ryujinx.Horizon.Sdk.Sf; using Ryujinx.Horizon.Sdk.Sf;
@ -6,5 +7,15 @@ namespace Ryujinx.Horizon.Ovln.Ipc
{ {
partial class Sender : ISender partial class Sender : ISender
{ {
[CmifCommand(0)]
// OpenSender() -> object<nn::ovln::ISender>
public Result OpenSender(out ISender service)
{
service = new Sender();
Logger.Stub?.PrintStub(LogClass.ServiceOvln);
return Result.Success;
}
} }
} }

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -258,6 +258,7 @@ namespace Ryujinx.Input.SDL2
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.LeftTrigger, (GamepadButtonInputId)_configuration.LeftJoycon.ButtonZl)); _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.SingleRightTrigger0, (GamepadButtonInputId)_configuration.LeftJoycon.ButtonSr));
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleLeftTrigger0, (GamepadButtonInputId)_configuration.LeftJoycon.ButtonSl)); _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleLeftTrigger0, (GamepadButtonInputId)_configuration.LeftJoycon.ButtonSl));
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.Capture, (GamepadButtonInputId)_configuration.LeftJoycon.ButtonCapture));
// Finally right joycon // Finally right joycon
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.RightStick, (GamepadButtonInputId)_configuration.RightJoyconStick.StickButton)); _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.RightTrigger, (GamepadButtonInputId)_configuration.RightJoycon.ButtonZr));
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleRightTrigger1, (GamepadButtonInputId)_configuration.RightJoycon.ButtonSr)); _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.SingleLeftTrigger1, (GamepadButtonInputId)_configuration.RightJoycon.ButtonSl));
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.Home, (GamepadButtonInputId)_configuration.RightJoycon.ButtonHome));
SetTriggerThreshold(_configuration.TriggerThreshold); SetTriggerThreshold(_configuration.TriggerThreshold);
} }

View File

@ -371,6 +371,7 @@ namespace Ryujinx.Input.SDL2
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.LeftTrigger, (Key)_configuration.LeftJoycon.ButtonZl)); _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.SingleRightTrigger0, (Key)_configuration.LeftJoycon.ButtonSr));
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleLeftTrigger0, (Key)_configuration.LeftJoycon.ButtonSl)); _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleLeftTrigger0, (Key)_configuration.LeftJoycon.ButtonSl));
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.Capture, (Key)_configuration.LeftJoycon.ButtonCapture));
// Finally configure right joycon // Finally configure right joycon
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.RightStick, (Key)_configuration.RightJoyconStick.StickButton)); _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.RightTrigger, (Key)_configuration.RightJoycon.ButtonZr));
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleRightTrigger1, (Key)_configuration.RightJoycon.ButtonSr)); _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.SingleLeftTrigger1, (Key)_configuration.RightJoycon.ButtonSl));
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.Home, (Key)_configuration.RightJoycon.ButtonHome));
} }
} }

View File

@ -35,6 +35,9 @@ namespace Ryujinx.Input
Guide, Guide,
Misc1, Misc1,
Home = Guide,
Capture = Misc1,
// Xbox Elite paddle // Xbox Elite paddle
Paddle1, Paddle1,

View File

@ -49,7 +49,9 @@ namespace Ryujinx.Input.HLE
new(GamepadButtonInputId.SingleLeftTrigger0, ControllerKeys.SlLeft), new(GamepadButtonInputId.SingleLeftTrigger0, ControllerKeys.SlLeft),
new(GamepadButtonInputId.SingleRightTrigger0, ControllerKeys.SrLeft), new(GamepadButtonInputId.SingleRightTrigger0, ControllerKeys.SrLeft),
new(GamepadButtonInputId.SingleLeftTrigger1, ControllerKeys.SlRight), 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 private class HLEKeyboardMappingEntry

View File

@ -209,6 +209,9 @@ namespace Ryujinx.Input.HLE
List<SixAxisInput> hleMotionStates = new(NpadDevices.MaxControllers); List<SixAxisInput> hleMotionStates = new(NpadDevices.MaxControllers);
KeyboardInput? hleKeyboardInput = null; KeyboardInput? hleKeyboardInput = null;
bool homeDown = false;
bool captureDown = false;
foreach (InputConfig inputConfig in _inputConfig) foreach (InputConfig inputConfig in _inputConfig)
{ {
@ -238,6 +241,11 @@ namespace Ryujinx.Input.HLE
SixAxisInput altMotionState = isJoyconPair ? controller.GetHLEMotionState(true) : default; SixAxisInput altMotionState = isJoyconPair ? controller.GetHLEMotionState(true) : default;
motionState = (controller.GetHLEMotionState(), altMotionState); motionState = (controller.GetHLEMotionState(), altMotionState);
homeDown |= inputState.Buttons.HasFlag(ControllerKeys.Home);
captureDown |= inputState.Buttons.HasFlag(ControllerKeys.Capture);
inputState.Buttons &= ~(ControllerKeys.Home | ControllerKeys.Capture);
} }
else else
{ {
@ -266,6 +274,9 @@ namespace Ryujinx.Input.HLE
_device.Hid.Npads.Update(hleInputStates); _device.Hid.Npads.Update(hleInputStates);
_device.Hid.Npads.UpdateSixAxis(hleMotionStates); _device.Hid.Npads.UpdateSixAxis(hleMotionStates);
_device.Hid.HomeButton.Update(homeDown);
_device.Hid.CaptureButton.Update(captureDown);
if (hleKeyboardInput.HasValue) if (hleKeyboardInput.HasValue)
{ {
@ -315,6 +326,7 @@ namespace Ryujinx.Input.HLE
} }
_device.TamperMachine.UpdateInput(hleInputStates); _device.TamperMachine.UpdateInput(hleInputStates);
_device.UpdateWindowSystemInput();
} }
} }

View File

@ -967,6 +967,7 @@ namespace Ryujinx.Ava
ConfigurationState.Instance.Multiplayer.LdnServer, ConfigurationState.Instance.Multiplayer.LdnServer,
ConfigurationState.Instance.Graphics.CustomVSyncInterval.Value, ConfigurationState.Instance.Graphics.CustomVSyncInterval.Value,
ConfigurationState.Instance.Hacks.ShowDirtyHacks ? ConfigurationState.Instance.Hacks.EnabledHacks : null)); ConfigurationState.Instance.Hacks.ShowDirtyHacks ? ConfigurationState.Instance.Hacks.EnabledHacks : null));
//Device.LoadSystemProgramId(0x010000000000100C);
} }
private static IHardwareDeviceDriver InitializeAudio() private static IHardwareDeviceDriver InitializeAudio()

View File

@ -7122,6 +7122,56 @@
"zh_TW": "" "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", "ID": "ControllerSettingsDPad",
"Translations": { "Translations": {

View File

@ -118,6 +118,7 @@ namespace Ryujinx.Headless
ButtonZl = Key.Q, ButtonZl = Key.Q,
ButtonSl = Key.Unbound, ButtonSl = Key.Unbound,
ButtonSr = Key.Unbound, ButtonSr = Key.Unbound,
ButtonCapture = Key.Unbound,
}, },
LeftJoyconStick = new JoyconConfigKeyboardStick<Key> LeftJoyconStick = new JoyconConfigKeyboardStick<Key>
@ -140,6 +141,7 @@ namespace Ryujinx.Headless
ButtonZr = Key.O, ButtonZr = Key.O,
ButtonSl = Key.Unbound, ButtonSl = Key.Unbound,
ButtonSr = Key.Unbound, ButtonSr = Key.Unbound,
ButtonHome = Key.Unbound,
}, },
RightJoyconStick = new JoyconConfigKeyboardStick<Key> RightJoyconStick = new JoyconConfigKeyboardStick<Key>
@ -178,6 +180,7 @@ namespace Ryujinx.Headless
ButtonZl = ConfigGamepadInputId.LeftTrigger, ButtonZl = ConfigGamepadInputId.LeftTrigger,
ButtonSl = ConfigGamepadInputId.Unbound, ButtonSl = ConfigGamepadInputId.Unbound,
ButtonSr = ConfigGamepadInputId.Unbound, ButtonSr = ConfigGamepadInputId.Unbound,
ButtonCapture = ConfigGamepadInputId.Capture,
}, },
LeftJoyconStick = new JoyconConfigControllerStick<ConfigGamepadInputId, ConfigStickInputId> LeftJoyconStick = new JoyconConfigControllerStick<ConfigGamepadInputId, ConfigStickInputId>
@ -200,6 +203,7 @@ namespace Ryujinx.Headless
ButtonZr = ConfigGamepadInputId.RightTrigger, ButtonZr = ConfigGamepadInputId.RightTrigger,
ButtonSl = ConfigGamepadInputId.Unbound, ButtonSl = ConfigGamepadInputId.Unbound,
ButtonSr = ConfigGamepadInputId.Unbound, ButtonSr = ConfigGamepadInputId.Unbound,
ButtonHome = ConfigGamepadInputId.Home,
}, },
RightJoyconStick = new JoyconConfigControllerStick<ConfigGamepadInputId, ConfigStickInputId> RightJoyconStick = new JoyconConfigControllerStick<ConfigGamepadInputId, ConfigStickInputId>

View File

@ -128,6 +128,7 @@ namespace Ryujinx.Ava.Input
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.LeftTrigger, (Key)_configuration.LeftJoycon.ButtonZl)); _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.SingleRightTrigger0, (Key)_configuration.LeftJoycon.ButtonSr));
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleLeftTrigger0, (Key)_configuration.LeftJoycon.ButtonSl)); _buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleLeftTrigger0, (Key)_configuration.LeftJoycon.ButtonSl));
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.Capture, (Key)_configuration.LeftJoycon.ButtonCapture));
// Right JoyCon // Right JoyCon
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.RightStick, (Key)_configuration.RightJoyconStick.StickButton)); _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.RightTrigger, (Key)_configuration.RightJoycon.ButtonZr));
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleRightTrigger1, (Key)_configuration.RightJoycon.ButtonSr)); _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.SingleLeftTrigger1, (Key)_configuration.RightJoycon.ButtonSl));
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.Home, (Key)_configuration.RightJoycon.ButtonHome));
#pragma warning restore IDE0055 #pragma warning restore IDE0055
} }
} }

View File

@ -43,7 +43,9 @@ namespace Ryujinx.Ava.UI.Models.Input
[ObservableProperty] private GamepadInputId _dpadRight; [ObservableProperty] private GamepadInputId _dpadRight;
[ObservableProperty] private GamepadInputId _buttonMinus; [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 _buttonA;
[ObservableProperty] private GamepadInputId _buttonB; [ObservableProperty] private GamepadInputId _buttonB;
@ -140,6 +142,7 @@ namespace Ryujinx.Ava.UI.Models.Input
LeftButtonSl = controllerInput.LeftJoycon.ButtonSl; LeftButtonSl = controllerInput.LeftJoycon.ButtonSl;
LeftButtonSr = controllerInput.LeftJoycon.ButtonSr; LeftButtonSr = controllerInput.LeftJoycon.ButtonSr;
ButtonZl = controllerInput.LeftJoycon.ButtonZl; ButtonZl = controllerInput.LeftJoycon.ButtonZl;
ButtonCapture = controllerInput.LeftJoycon.ButtonCapture;
ButtonA = controllerInput.RightJoycon.ButtonA; ButtonA = controllerInput.RightJoycon.ButtonA;
ButtonB = controllerInput.RightJoycon.ButtonB; ButtonB = controllerInput.RightJoycon.ButtonB;
@ -150,6 +153,7 @@ namespace Ryujinx.Ava.UI.Models.Input
RightButtonSl = controllerInput.RightJoycon.ButtonSl; RightButtonSl = controllerInput.RightJoycon.ButtonSl;
RightButtonSr = controllerInput.RightJoycon.ButtonSr; RightButtonSr = controllerInput.RightJoycon.ButtonSr;
ButtonZr = controllerInput.RightJoycon.ButtonZr; ButtonZr = controllerInput.RightJoycon.ButtonZr;
ButtonHome = controllerInput.RightJoycon.ButtonHome;
DeadzoneLeft = controllerInput.DeadzoneLeft; DeadzoneLeft = controllerInput.DeadzoneLeft;
DeadzoneRight = controllerInput.DeadzoneRight; DeadzoneRight = controllerInput.DeadzoneRight;
@ -215,6 +219,7 @@ namespace Ryujinx.Ava.UI.Models.Input
ButtonSl = LeftButtonSl, ButtonSl = LeftButtonSl,
ButtonSr = LeftButtonSr, ButtonSr = LeftButtonSr,
ButtonZl = ButtonZl, ButtonZl = ButtonZl,
ButtonCapture = ButtonCapture,
}, },
RightJoycon = new RightJoyconCommonConfig<GamepadInputId> RightJoycon = new RightJoyconCommonConfig<GamepadInputId>
{ {
@ -227,6 +232,7 @@ namespace Ryujinx.Ava.UI.Models.Input
ButtonSr = RightButtonSr, ButtonSr = RightButtonSr,
ButtonR = ButtonR, ButtonR = ButtonR,
ButtonZr = ButtonZr, ButtonZr = ButtonZr,
ButtonHome = ButtonHome,
}, },
LeftJoyconStick = new JoyconConfigControllerStick<GamepadInputId, StickInputId> LeftJoyconStick = new JoyconConfigControllerStick<GamepadInputId, StickInputId>
{ {

View File

@ -30,6 +30,8 @@ namespace Ryujinx.Ava.UI.Models.Input
[ObservableProperty] private Key _buttonMinus; [ObservableProperty] private Key _buttonMinus;
[ObservableProperty] private Key _buttonPlus; [ObservableProperty] private Key _buttonPlus;
[ObservableProperty] private Key _buttonCapture;
[ObservableProperty] private Key _buttonHome;
[ObservableProperty] private Key _buttonA; [ObservableProperty] private Key _buttonA;
[ObservableProperty] private Key _buttonB; [ObservableProperty] private Key _buttonB;
@ -82,6 +84,7 @@ namespace Ryujinx.Ava.UI.Models.Input
LeftButtonSl = keyboardConfig.LeftJoycon.ButtonSl; LeftButtonSl = keyboardConfig.LeftJoycon.ButtonSl;
LeftButtonSr = keyboardConfig.LeftJoycon.ButtonSr; LeftButtonSr = keyboardConfig.LeftJoycon.ButtonSr;
ButtonZl = keyboardConfig.LeftJoycon.ButtonZl; ButtonZl = keyboardConfig.LeftJoycon.ButtonZl;
ButtonCapture = keyboardConfig.LeftJoycon.ButtonCapture;
ButtonA = keyboardConfig.RightJoycon.ButtonA; ButtonA = keyboardConfig.RightJoycon.ButtonA;
ButtonB = keyboardConfig.RightJoycon.ButtonB; ButtonB = keyboardConfig.RightJoycon.ButtonB;
@ -92,6 +95,7 @@ namespace Ryujinx.Ava.UI.Models.Input
RightButtonSl = keyboardConfig.RightJoycon.ButtonSl; RightButtonSl = keyboardConfig.RightJoycon.ButtonSl;
RightButtonSr = keyboardConfig.RightJoycon.ButtonSr; RightButtonSr = keyboardConfig.RightJoycon.ButtonSr;
ButtonZr = keyboardConfig.RightJoycon.ButtonZr; ButtonZr = keyboardConfig.RightJoycon.ButtonZr;
ButtonHome = keyboardConfig.RightJoycon.ButtonHome;
} }
} }
@ -114,6 +118,7 @@ namespace Ryujinx.Ava.UI.Models.Input
ButtonZl = ButtonZl, ButtonZl = ButtonZl,
ButtonSl = LeftButtonSl, ButtonSl = LeftButtonSl,
ButtonSr = LeftButtonSr, ButtonSr = LeftButtonSr,
ButtonCapture = ButtonCapture,
}, },
RightJoycon = new RightJoyconCommonConfig<Key> RightJoycon = new RightJoyconCommonConfig<Key>
{ {
@ -126,6 +131,7 @@ namespace Ryujinx.Ava.UI.Models.Input
ButtonSr = RightButtonSr, ButtonSr = RightButtonSr,
ButtonR = ButtonR, ButtonR = ButtonR,
ButtonZr = ButtonZr, ButtonZr = ButtonZr,
ButtonHome = ButtonHome,
}, },
LeftJoyconStick = new JoyconConfigKeyboardStick<Key> LeftJoyconStick = new JoyconConfigKeyboardStick<Key>
{ {

View File

@ -561,6 +561,7 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
ButtonZl = Key.Q, ButtonZl = Key.Q,
ButtonSl = Key.Unbound, ButtonSl = Key.Unbound,
ButtonSr = Key.Unbound, ButtonSr = Key.Unbound,
ButtonCapture = Key.Unbound,
}, },
LeftJoyconStick = LeftJoyconStick =
new JoyconConfigKeyboardStick<Key> new JoyconConfigKeyboardStick<Key>
@ -582,6 +583,7 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
ButtonZr = Key.O, ButtonZr = Key.O,
ButtonSl = Key.Unbound, ButtonSl = Key.Unbound,
ButtonSr = Key.Unbound, ButtonSr = Key.Unbound,
ButtonHome = Key.Unbound,
}, },
RightJoyconStick = new JoyconConfigKeyboardStick<Key> RightJoyconStick = new JoyconConfigKeyboardStick<Key>
{ {
@ -621,6 +623,7 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
ButtonZl = ConfigGamepadInputId.LeftTrigger, ButtonZl = ConfigGamepadInputId.LeftTrigger,
ButtonSl = ConfigGamepadInputId.Unbound, ButtonSl = ConfigGamepadInputId.Unbound,
ButtonSr = ConfigGamepadInputId.Unbound, ButtonSr = ConfigGamepadInputId.Unbound,
ButtonCapture = ConfigGamepadInputId.Capture,
}, },
LeftJoyconStick = new JoyconConfigControllerStick<ConfigGamepadInputId, ConfigStickInputId> LeftJoyconStick = new JoyconConfigControllerStick<ConfigGamepadInputId, ConfigStickInputId>
{ {
@ -640,6 +643,7 @@ namespace Ryujinx.Ava.UI.ViewModels.Input
ButtonZr = ConfigGamepadInputId.RightTrigger, ButtonZr = ConfigGamepadInputId.RightTrigger,
ButtonSl = ConfigGamepadInputId.Unbound, ButtonSl = ConfigGamepadInputId.Unbound,
ButtonSr = ConfigGamepadInputId.Unbound, ButtonSr = ConfigGamepadInputId.Unbound,
ButtonHome = ConfigGamepadInputId.Home,
}, },
RightJoyconStick = new JoyconConfigControllerStick<ConfigGamepadInputId, ConfigStickInputId> RightJoyconStick = new JoyconConfigControllerStick<ConfigGamepadInputId, ConfigStickInputId>
{ {

View File

@ -1568,6 +1568,8 @@ namespace Ryujinx.Ava.UI.ViewModels
return; return;
} }
AppHost.Device.System.SetupFirst(AppHost.Device.Processes.ActiveApplication.ProcessId);
CanUpdate = false; CanUpdate = false;

View File

@ -309,6 +309,21 @@
</StackPanel> </StackPanel>
</StackPanel> </StackPanel>
</Border> </Border>
<StackPanel
Margin="10"
Orientation="Horizontal">
<TextBlock
Width="50"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Text="{ext:Locale ControllerSettingsButtonCapture}"
TextAlignment="Center" />
<ToggleButton Name="ButtonCapture">
<TextBlock
Text="{Binding Config.ButtonCapture, Converter={x:Static helpers:KeyValueConverter.Instance}}"
TextAlignment="Center" />
</ToggleButton>
</StackPanel>
</StackPanel> </StackPanel>
<!-- Triggers & Side Buttons --> <!-- Triggers & Side Buttons -->
<StackPanel <StackPanel
@ -783,6 +798,21 @@
</StackPanel> </StackPanel>
</StackPanel> </StackPanel>
</Border> </Border>
<StackPanel
Margin="10"
Orientation="Horizontal">
<ToggleButton Name="ButtonHome">
<TextBlock
Text="{Binding Config.ButtonHome, Converter={x:Static helpers:KeyValueConverter.Instance}}"
TextAlignment="Center" />
</ToggleButton>
<TextBlock
Width="40"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Text="{ext:Locale ControllerSettingsButtonHome}"
TextAlignment="Center" />
</StackPanel>
</StackPanel> </StackPanel>
</Grid> </Grid>
</StackPanel> </StackPanel>

View File

@ -184,6 +184,12 @@ namespace Ryujinx.Ava.UI.Views.Input
case "RightJoystick": case "RightJoystick":
viewModel.Config.RightJoystick = buttonValue.AsHidType<StickInputId>(); viewModel.Config.RightJoystick = buttonValue.AsHidType<StickInputId>();
break; break;
case "ButtonCapture":
viewModel.Config.ButtonCapture = buttonValue.AsHidType<GamepadInputId>();
break;
case "ButtonHome":
viewModel.Config.ButtonHome = buttonValue.AsHidType<GamepadInputId>();
break;
} }
} }
}; };

View File

@ -302,6 +302,21 @@
</StackPanel> </StackPanel>
</StackPanel> </StackPanel>
</Border> </Border>
<StackPanel
Margin="10"
Orientation="Horizontal">
<TextBlock
Width="50"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Text="{ext:Locale ControllerSettingsButtonCapture}"
TextAlignment="Center" />
<ToggleButton Name="ButtonCapture">
<TextBlock
Text="{Binding Config.ButtonCapture, Converter={x:Static helpers:KeyValueConverter.Instance}}"
TextAlignment="Center" />
</ToggleButton>
</StackPanel>
</StackPanel> </StackPanel>
<!-- Triggers & Side Buttons --> <!-- Triggers & Side Buttons -->
<StackPanel <StackPanel
@ -666,6 +681,21 @@
</StackPanel> </StackPanel>
</StackPanel> </StackPanel>
</Border> </Border>
<StackPanel
Margin="10"
Orientation="Horizontal">
<ToggleButton Name="ButtonHome">
<TextBlock
Text="{Binding Config.ButtonHome, Converter={x:Static helpers:KeyValueConverter.Instance}}"
TextAlignment="Center" />
</ToggleButton>
<TextBlock
Width="40"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Text="{ext:Locale ControllerSettingsButtonHome}"
TextAlignment="Center" />
</StackPanel>
</StackPanel> </StackPanel>
</Grid> </Grid>
</StackPanel> </StackPanel>

View File

@ -85,7 +85,7 @@ namespace Ryujinx.Ava.UI.Views.Input
break; break;
case "ButtonMinus": case "ButtonMinus":
viewModel.Config.ButtonMinus = buttonValue.AsHidType<Key>(); viewModel.Config.ButtonMinus = buttonValue.AsHidType<Key>();
break; break;
case "LeftStickButton": case "LeftStickButton":
viewModel.Config.LeftStickButton = buttonValue.AsHidType<Key>(); viewModel.Config.LeftStickButton = buttonValue.AsHidType<Key>();
break; break;
@ -161,6 +161,12 @@ namespace Ryujinx.Ava.UI.Views.Input
case "RightStickLeft": case "RightStickLeft":
viewModel.Config.RightStickLeft = buttonValue.AsHidType<Key>(); viewModel.Config.RightStickLeft = buttonValue.AsHidType<Key>();
break; break;
case "ButtonCapture":
viewModel.Config.ButtonCapture = buttonValue.AsHidType<Key>();
break;
case "ButtonHome":
viewModel.Config.ButtonHome = buttonValue.AsHidType<Key>();
break;
} }
} }
}; };

View File

@ -223,6 +223,7 @@ namespace Ryujinx.Ava.Utilities.Configuration
ButtonZl = Key.Q, ButtonZl = Key.Q,
ButtonSl = Key.Unbound, ButtonSl = Key.Unbound,
ButtonSr = Key.Unbound, ButtonSr = Key.Unbound,
ButtonCapture = Key.Unbound,
}, },
LeftJoyconStick = new JoyconConfigKeyboardStick<Key> LeftJoyconStick = new JoyconConfigKeyboardStick<Key>
{ {
@ -243,6 +244,7 @@ namespace Ryujinx.Ava.Utilities.Configuration
ButtonZr = Key.O, ButtonZr = Key.O,
ButtonSl = Key.Unbound, ButtonSl = Key.Unbound,
ButtonSr = Key.Unbound, ButtonSr = Key.Unbound,
ButtonHome = Key.Unbound,
}, },
RightJoyconStick = new JoyconConfigKeyboardStick<Key> RightJoyconStick = new JoyconConfigKeyboardStick<Key>
{ {

View File

@ -275,6 +275,7 @@ namespace Ryujinx.Ava.Utilities.Configuration
ButtonZl = Key.Q, ButtonZl = Key.Q,
ButtonSl = Key.Unbound, ButtonSl = Key.Unbound,
ButtonSr = Key.Unbound, ButtonSr = Key.Unbound,
ButtonCapture = Key.Unbound,
}, },
LeftJoyconStick = new JoyconConfigKeyboardStick<Key> LeftJoyconStick = new JoyconConfigKeyboardStick<Key>
{ {
@ -295,6 +296,7 @@ namespace Ryujinx.Ava.Utilities.Configuration
ButtonZr = Key.O, ButtonZr = Key.O,
ButtonSl = Key.Unbound, ButtonSl = Key.Unbound,
ButtonSr = Key.Unbound, ButtonSr = Key.Unbound,
ButtonHome = Key.Unbound,
}, },
RightJoyconStick = new JoyconConfigKeyboardStick<Key> RightJoyconStick = new JoyconConfigKeyboardStick<Key>
{ {