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,
Misc1,
Home = Guide,
Capture = Misc1,
// Xbox Elite paddle
Paddle1,
Paddle2,

View File

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

View File

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

View File

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

View File

@ -4,6 +4,7 @@ using Ryujinx.HLE.HOS.Kernel.Process;
using Ryujinx.HLE.HOS.Kernel.Threading;
using Ryujinx.Horizon;
using Ryujinx.Horizon.Sdk.OsTypes;
using System;
using System.Collections.Generic;
using System.Threading;
@ -17,8 +18,8 @@ namespace Ryujinx.HLE.HOS.Applets
private WindowSystem _windowSystem;
// Guest event handle to wake up the event loop
internal SystemEventType _wakeupEvent;
internal MultiWaitHolder _wakeupHolder;
private SystemEventType _wakeupEvent;
private MultiWaitHolder _wakeupHolder;
private KWritableEvent _wakeupEventObj;
// List of owned process holders
@ -195,32 +196,42 @@ namespace Ryujinx.HLE.HOS.Applets
private void ThreadFunc()
{
HorizonStatic.Register(
default,
_system.KernelContext.Syscall,
null,
_thread.ThreadContext,
(int)_thread.ThreadContext.GetX(1));
// lock (_lock)
try
{
Os.CreateSystemEvent(out _wakeupEvent, EventClearMode.ManualClear, true).AbortOnFailure();
_wakeupEventObj = _thread.Owner.HandleTable.GetObject<KWritableEvent>(Os.GetWritableHandleOfSystemEvent(ref _wakeupEvent));
HorizonStatic.Register(
default,
_system.KernelContext.Syscall,
null,
_thread.ThreadContext,
(int)_thread.ThreadContext.GetX(1));
_wakeupHolder = new MultiWaitHolderOfInterProcessEvent(_wakeupEvent.InterProcessEvent);
_wakeupHolder.UserData = UserDataTag.WakeupEvent;
_multiWait.LinkMultiWaitHolder(_wakeupHolder);
}
while (!_cts.Token.IsCancellationRequested)
{
var holder = WaitSignaled();
if (holder == null)
lock (_lock)
{
break;
Os.CreateSystemEvent(out _wakeupEvent, EventClearMode.ManualClear, true).AbortOnFailure();
_wakeupEventObj = _thread.Owner.HandleTable.GetObject<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 WindowVisible = true;
internal bool ExitLocked = false;
internal bool IsApplication { get; }
internal AppletStateMgr AppletState { get; private set; }
public event EventHandler AppletStateChanged;
@ -132,10 +133,11 @@ namespace Ryujinx.HLE.HOS.Applets
public RealApplet(ulong pid, bool isApplication, Horizon system)
{
_system = system;
AppletState = new AppletStateMgr(system, isApplication);
AppletState = new AppletStateMgr(system);
ProcessHandle = _system.KernelContext.Processes[pid];
AppletResourceUserId = ProcessHandle.Pid;
AppletId = GetAppletIdFromProgramId(ProcessHandle.TitleId);
IsApplication = isApplication;
}
public void RegisterChild(RealApplet applet)

View File

@ -7,6 +7,7 @@ using System.Linq;
using Ryujinx.Horizon.Sdk.Applet;
using Ryujinx.Common;
using Ryujinx.HLE.HOS.Applets.Types;
using System.Threading;
namespace Ryujinx.HLE.HOS.Applets
{
@ -14,21 +15,20 @@ namespace Ryujinx.HLE.HOS.Applets
{
private Horizon _system;
private readonly object _lock = new();
EventObserver _eventObserver = null;
private EventObserver _eventObserver = null;
// Foreground roots
RealApplet _homeMenu = null;
RealApplet _overlayDisp = null;
RealApplet _application = null;
// Foreground roots.
private RealApplet _homeMenu = null;
private RealApplet _overlayDisp = null;
// Removed single application field to allow multiple applications.
// Home menu state
bool _homeMenuForegroundLocked = false;
RealApplet _foregroundRequestedApplet = null;
// Home menu state.
private bool _homeMenuForegroundLocked = false;
private RealApplet _foregroundRequestedApplet = null;
// aruid -> applet map
Dictionary<ulong, RealApplet> _applets = new();
List<RealApplet> _rootApplets = new();
// aruid -> applet map.
private Dictionary<ulong, RealApplet> _applets = new();
private List<RealApplet> _rootApplets = new();
internal ButtonPressTracker ButtonPressTracker { get; }
@ -60,6 +60,7 @@ namespace Ryujinx.HLE.HOS.Applets
return;
}
// If no foreground applet is explicitly requested, choose the last root applet.
if (_foregroundRequestedApplet == null && _rootApplets.Count != 0)
{
_foregroundRequestedApplet = _rootApplets.Last();
@ -72,6 +73,9 @@ namespace Ryujinx.HLE.HOS.Applets
}
}
/// <summary>
/// Tracks a new process as an applet.
/// </summary>
internal RealApplet TrackProcess(ulong pid, ulong callerPid, bool isApplication)
{
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)
{
if (_applets.ContainsKey(applet.AppletResourceUserId))
@ -122,30 +129,33 @@ namespace Ryujinx.HLE.HOS.Applets
{
_overlayDisp = applet;
}
else if (isApplication)
{
_application = applet;
}
// For application applets, we no longer assign a unique field.
// They are simply tracked as root applets (if callerPid == 0) and in the _applets dictionary.
_applets[applet.AppletResourceUserId] = applet;
_eventObserver.TrackAppletProcess(applet);
if (_applets.Count == 1 || applet.AppletId == RealAppletId.SystemAppletMenu || applet.AppletId == RealAppletId.OverlayApplet)
// If this is the first applet being tracked, or if it is one of the special system applets,
// perform initial setup.
if (_applets.Count == 1 ||
applet.AppletId == RealAppletId.SystemAppletMenu ||
applet.AppletId == RealAppletId.OverlayApplet)
{
SetupFirstApplet(applet);
_foregroundRequestedApplet = applet;
applet.AppletState.SetFocus(false);
}
// _foregroundRequestedApplet = applet;
// applet.AppletState.SetFocusState(FocusState.InFocus);
_eventObserver.RequestUpdate();
}
/// <summary>
/// Performs initial setup for the first tracked applet.
/// </summary>
private void SetupFirstApplet(RealApplet applet)
{
if (applet.AppletId == RealAppletId.SystemAppletMenu)
{
//applet.AppletState.SetFocusHandlingMode(false);
applet.AppletState.SetOutOfFocusSuspendingEnabled(false);
RequestHomeMenuToGetForeground();
}
@ -158,7 +168,7 @@ namespace Ryujinx.HLE.HOS.Applets
{
applet.AppletState.SetFocusState(FocusState.InFocus);
_foregroundRequestedApplet = applet;
RequestApplicationToGetForeground();
RequestApplicationToGetForeground(applet.ProcessHandle.Pid);
}
applet.UpdateSuspensionStateLocked(true);
@ -166,70 +176,94 @@ namespace Ryujinx.HLE.HOS.Applets
internal RealApplet GetByAruId(ulong aruid)
{
// lock (_lock)
if (_applets.TryGetValue(aruid, out RealApplet applet))
{
if (_applets.TryGetValue(aruid, out RealApplet applet))
{
return applet;
}
return null;
return applet;
}
return null;
}
/// <summary>
/// Returns the current foreground application.
/// If none is explicitly set, the first tracked application is returned.
/// </summary>
internal RealApplet GetMainApplet()
{
// lock (_lock)
lock (_lock)
{
if (_application != null)
if (_foregroundRequestedApplet != null && _foregroundRequestedApplet.IsApplication)
{
if (_applets.TryGetValue(_application.AppletResourceUserId, out RealApplet applet))
{
return applet;
}
return _foregroundRequestedApplet;
}
return null;
return _rootApplets.FirstOrDefault(applet => applet.IsApplication);
}
}
internal void RequestHomeMenuToGetForeground()
{
// lock (_lock)
{
_foregroundRequestedApplet = _homeMenu;
}
_foregroundRequestedApplet = _homeMenu;
_eventObserver.RequestUpdate();
}
internal void RequestApplicationToGetForeground()
/// <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();
}
internal void RequestLockHomeMenuIntoForeground()
{
// lock (_lock)
{
_homeMenuForegroundLocked = true;
}
_homeMenuForegroundLocked = true;
_eventObserver.RequestUpdate();
}
internal void RequestUnlockHomeMenuFromForeground()
{
// lock (_lock)
{
_homeMenuForegroundLocked = false;
}
_homeMenuForegroundLocked = false;
_eventObserver.RequestUpdate();
}
@ -245,48 +279,39 @@ namespace Ryujinx.HLE.HOS.Applets
internal void OnOperationModeChanged()
{
// lock (_lock)
foreach (var (_, applet) in _applets)
{
foreach (var (aruid, applet) in _applets)
lock (applet.Lock)
{
lock (applet.Lock)
{
applet.AppletState.OnOperationAndPerformanceModeChanged();
}
applet.AppletState.OnOperationAndPerformanceModeChanged();
}
}
}
internal void OnExitRequested()
{
// lock (_lock)
foreach (var (_, applet) in _applets)
{
foreach (var (aruid, applet) in _applets)
lock (applet.Lock)
{
lock (applet.Lock)
{
applet.AppletState.OnExitRequested();
}
applet.AppletState.OnExitRequested();
}
}
}
internal void OnSystemButtonPress(SystemButtonType type)
{
// lock (_lock)
switch (type)
{
switch (type)
{
case SystemButtonType.PerformHomeButtonShortPressing:
SendButtonAppletMessageLocked(AppletMessage.DetectShortPressingHomeButton);
break;
case SystemButtonType.PerformHomeButtonLongPressing:
SendButtonAppletMessageLocked(AppletMessage.DetectLongPressingHomeButton);
break;
case SystemButtonType.PerformCaptureButtonShortPressing:
SendButtonAppletMessageLocked(AppletMessage.DetectShortPressingCaptureButton);
break;
}
case SystemButtonType.PerformHomeButtonShortPressing:
SendButtonAppletMessageLocked(AppletMessage.DetectShortPressingHomeButton);
break;
case SystemButtonType.PerformHomeButtonLongPressing:
SendButtonAppletMessageLocked(AppletMessage.DetectLongPressingHomeButton);
break;
case SystemButtonType.PerformCaptureButtonShortPressing:
SendButtonAppletMessageLocked(AppletMessage.DetectShortPressingCaptureButton);
break;
}
}
@ -297,6 +322,16 @@ namespace Ryujinx.HLE.HOS.Applets
lock (_homeMenu.Lock)
{
_homeMenu.AppletState.PushUnorderedMessage(message);
if (message == AppletMessage.DetectShortPressingHomeButton)
{
foreach (var applet in _applets.Values)
{
if (applet != _homeMenu)
{
applet.ProcessHandle.SetActivity(true);
}
}
}
}
}
@ -309,9 +344,13 @@ namespace Ryujinx.HLE.HOS.Applets
}
}
/// <summary>
/// Removes terminated applets from tracking.
/// </summary>
private void PruneTerminatedAppletsLocked()
{
foreach (var (aruid, applet) in _applets)
// We need to iterate over a copy of the dictionary keys because we might remove items.
foreach (var (aruid, applet) in _applets.ToList())
{
lock (applet.Lock)
{
@ -320,12 +359,14 @@ namespace Ryujinx.HLE.HOS.Applets
continue;
}
// If the applet has child applets still, terminate them first.
if (applet.ChildApplets.Count != 0)
{
TerminateChildAppletsLocked(applet);
continue;
}
// If this applet was started by another, remove it from its callers child list.
if (applet.CallerApplet != null)
{
applet.CallerApplet.ChildApplets.Remove(applet);
@ -343,10 +384,14 @@ namespace Ryujinx.HLE.HOS.Applets
_foregroundRequestedApplet = null;
}
if (applet == _application)
// For application applets, clear the foreground reference if necessary and
// notify the home menu that an application has exited.
if (applet.IsApplication)
{
_application = null;
_foregroundRequestedApplet = null;
if (_foregroundRequestedApplet == applet)
{
_foregroundRequestedApplet = null;
}
if (_homeMenu != null)
{
@ -363,9 +408,26 @@ namespace Ryujinx.HLE.HOS.Applets
}
}
/// <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()
{
// If the home menu is not locked into the foreground, then there's nothing to do.
if (_homeMenu == null || !_homeMenuForegroundLocked)
{
_homeMenuForegroundLocked = false;
@ -387,18 +449,9 @@ namespace Ryujinx.HLE.HOS.Applets
return true;
}
private void TerminateChildAppletsLocked(RealApplet parent)
{
foreach (var child in parent.ChildApplets)
{
if (child.ProcessHandle.State != ProcessState.Exited)
{
child.ProcessHandle.Terminate();
child.TerminateResult = (ResultCode)Services.Am.ResultCode.LibraryAppletTerminated;
}
}
}
/// <summary>
/// Updates the state of the specified applet and its children.
/// </summary>
private void UpdateAppletStateLocked(RealApplet applet, bool isForeground)
{
if (applet == null)
@ -427,7 +480,7 @@ namespace Ryujinx.HLE.HOS.Applets
return false;
});
// TODO: Update visibility state
// TODO: Update visibility state if needed.
applet.SetInteractibleLocked(isForeground && applet.WindowVisible);
@ -445,13 +498,81 @@ namespace Ryujinx.HLE.HOS.Applets
applet.UpdateSuspensionStateLocked(true);
}
Logger.Info?.Print(LogClass.ServiceAm, $"Updating applet state for {applet.AppletId}: visible={applet.WindowVisible}, foreground={isForeground}, obscured={isObscured}, reqFState={applet.AppletState.RequestedFocusState}, ackFState={applet.AppletState.AcknowledgedFocusState}, runnable={applet.AppletState.IsRunnable()}");
// Recurse into child applets
Logger.Info?.Print(LogClass.ServiceAm,
$"Updating applet state for {applet.AppletId}: visible={applet.WindowVisible}, foreground={isForeground}, obscured={isObscured}, reqFState={applet.AppletState.RequestedFocusState}, ackFState={applet.AppletState.AcknowledgedFocusState}, runnable={applet.AppletState.IsRunnable()}");
// Recurse into child applets.
foreach (var child in applet.ChildApplets)
{
UpdateAppletStateLocked(child, isForeground);
if (child == _foregroundRequestedApplet)
{
UpdateAppletStateLocked(child, true);
}
else
{
UpdateAppletStateLocked(child, isForeground);
}
}
}
}
/// <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 AppletStateMgr IntialAppletState { get; private set; }
internal AppletStateMgr AppletState
internal AppletStateMgr GetAppletState(ulong processId)
{
get
if (WindowSystem?.GetByAruId(processId) != null)
{
ulong processId = 0;
if (Device?.Processes?.ActiveApplication != null)
{
processId = Device.Processes.ActiveApplication.ProcessId;
}
if (WindowSystem?.GetByAruId(processId) != null)
{
Logger.Info?.Print(LogClass.Application, "Real applet instance found");
return WindowSystem.GetByAruId(processId).AppletState;
}
return WindowSystem.GetByAruId(processId).AppletState;
}
return IntialAppletState;
}
set
{
if (value != null)
{
IntialAppletState = value;
}
}
return IntialAppletState;
}
internal WindowSystem WindowSystem { get; private set; }
@ -122,6 +106,9 @@ namespace Ryujinx.HLE.HOS
internal CaptureManager CaptureManager { get; private set; }
internal KEvent VsyncEvent { get; private set; }
internal KEvent GeneralChannelEvent { get; private set; }
internal Queue<byte[]> GeneralChannelData { get; private set; } = new();
internal KEvent DisplayResolutionChangeEvent { get; private set; }
@ -204,16 +191,13 @@ namespace Ryujinx.HLE.HOS
AppletCaptureBufferTransfer = new KTransferMemory(KernelContext, appletCaptureBufferStorage);
AppletState = new AppletStateMgr(this, true);
WindowSystem = new WindowSystem(this);
EventObserver = new EventObserver(this, WindowSystem);
AppletState.SetFocus(true);
VsyncEvent = new KEvent(KernelContext);
DisplayResolutionChangeEvent = new KEvent(KernelContext);
GeneralChannelEvent = new KEvent(KernelContext);
SharedFontManager = new SharedFontManager(device, fontStorage);
AccountManager = device.Configuration.AccountManager;
@ -362,13 +346,24 @@ namespace Ryujinx.HLE.HOS
public void ReturnFocus()
{
AppletState.SetFocus(true);
GetAppletState(WindowSystem.GetFocusedApp()).SetFocus(true);
}
public void SimulateWakeUpMessage()
{
// AppletState.Messages.Enqueue(AppletMessage.Resume);
// AppletState.MessageEvent.ReadableEvent.Signal();
PushToGeneralChannel(new byte[] {
0x53, 0x41, 0x4D, 0x53, 0x01, 0x00, 0x00, 0x00,
0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
});
}
public void PushToGeneralChannel(byte[] data)
{
if (data.Length > 0)
{
GeneralChannelData.Enqueue(data);
GeneralChannelEvent.ReadableEvent.Signal();
}
}
public void ScanAmiibo(int nfpDeviceId, string amiiboId, bool useRandomUuid)
@ -530,5 +525,12 @@ namespace Ryujinx.HLE.HOS
}
IsPaused = pause;
}
public void SetupFirst(ulong ProgramId)
{
bool isApp = ProgramId > 0x01000000000007FF;
RealApplet app = WindowSystem.TrackProcess(ProgramId, 0, isApp);
app.AppletState.SetFocusForce(true);
}
}
}

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

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

View File

@ -26,9 +26,11 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Lib
private int _interactiveOutDataEventHandle;
private int _indirectLayerHandle;
public ulong _pid = 0;
public ILibraryAppletAccessor(AppletId appletId, Horizon system)
public ILibraryAppletAccessor(AppletId appletId, Horizon system, ulong processId)
{
_pid = processId;
_kernelContext = system.KernelContext;
_stateChangedEvent = new KEvent(system.KernelContext);
@ -226,7 +228,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Lib
{
Horizon horizon = _kernelContext.Device.System;
_indirectLayerHandle = horizon.AppletState.IndirectLayerHandles.Add(_applet);
_indirectLayerHandle = horizon.GetAppletState(_pid).IndirectLayerHandles.Add(_applet);
context.ResponseData.Write((ulong)_indirectLayerHandle);
@ -255,7 +257,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Lib
Horizon horizon = _kernelContext.Device.System;
horizon.AppletState.IndirectLayerHandles.Delete(_indirectLayerHandle);
horizon.GetAppletState(_pid).IndirectLayerHandles.Delete(_indirectLayerHandle);
}
}
}

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.Kernel;
using Ryujinx.HLE.HOS.Kernel.Threading;
using Ryujinx.HLE.HOS.Services.Account.Acc;
using Ryujinx.HLE.HOS.Services.Ns.Types;
using Ryujinx.HLE.Loaders.Processes;
using Ryujinx.Horizon.Common;
using System;
using System.Threading;
namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.SystemAppletProxy
{
@ -18,6 +23,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
private readonly KEvent _stateChangedEvent;
private int _stateChangedEventHandle;
public RealApplet applet;
public IApplicationAccessor(ulong pid, ulong applicationId, string contentPath, Horizon system)
{
@ -70,19 +76,42 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
context.Device.Processes.LoadNca(_contentPath, out processResult);
isApplet = true;
}
var applet = context.Device.System.WindowSystem.TrackProcess(processResult.ProcessId, 0, !isApplet);
ulong caller = 0;
if (context.Device.System.WindowSystem.GetFirstApplet() != null)
{
caller = context.Device.System.WindowSystem.GetFirstApplet().ProcessHandle.Pid;
}
applet = context.Device.System.WindowSystem.TrackProcess(processResult.ProcessId, caller, !isApplet);
applet.AppletState.SetFocusHandlingMode(true);
return ResultCode.Success;
}
[CommandCmif(20)]
// RequestExit()
public ResultCode RequestExit(ServiceCtx context)
{
applet.ProcessHandle.SetActivity(false);
applet.AppletState.OnExitRequested();
applet?.ProcessHandle.Terminate();
Logger.Stub?.PrintStub(LogClass.ServiceAm);
return ResultCode.Success;
}
[CommandCmif(101)]
// RequestForApplicationToGetForeground()
public ResultCode RequestForApplicationToGetForeground(ServiceCtx context)
{
// _stateChangedEvent.ReadableEvent.Signal();
Logger.Stub?.PrintStub(LogClass.ServiceAm);
context.Device.System.ReturnFocus();
applet.AppletState.SetFocusForce(true);
if (applet.ProcessHandle.IsPaused)
{
applet.ProcessHandle.SetActivity(false);
}
context.Device.System.WindowSystem.RequestApplicationToGetForeground(applet.ProcessHandle.Pid);
return ResultCode.Success;
}
@ -95,6 +124,28 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
return ResultCode.Success;
}
[CommandCmif(122)]
// GetApplicationControlProperty() -> NACP
public ResultCode GetApplicationControlProperty(ServiceCtx context)
{
ulong titleId = context.Device.System.WindowSystem.GetApplicationApplet().ProcessHandle.TitleId;
ApplicationControlProperty nacp = context.Device.Processes.ActiveApplication.ApplicationControlProperties;
ulong position = context.Request.ReceiveBuff[0].Position;
foreach (RyuApplicationData ryuApplicationData in context.Device.Configuration.Titles)
{
if (ryuApplicationData.AppId.Value != titleId)
{
continue;
}
nacp = ryuApplicationData.Nacp;
nacp.Title[1] = ryuApplicationData.Nacp.Title[0];
break;
}
context.Memory.Write(position, SpanHelpers.AsByteSpan(ref nacp).ToArray());
return ResultCode.Success;
}
[CommandCmif(130)]
// SetUsers()
public ResultCode SetUsers(ServiceCtx context)
@ -103,5 +154,25 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
bool enable = context.RequestData.ReadBoolean();
return ResultCode.Success;
}
[CommandCmif(131)]
// CheckRightsEnvironmentAvailable() -> bool
public ResultCode CheckRightsEnvironmentAvailable(ServiceCtx context)
{
Logger.Stub?.PrintStub(LogClass.ServiceAm);
context.ResponseData.Write(true);
return ResultCode.Success;
}
[CommandCmif(132)]
// GetNsRightsEnvironmentHandle() -> u32
public ResultCode GetNsRightsEnvironmentHandle(ServiceCtx context)
{
Logger.Stub?.PrintStub(LogClass.ServiceAm);
KEvent eventObj = new KEvent(_kernelContext);
context.Process.HandleTable.GenerateHandle(eventObj.ReadableEvent, out int handle);
context.ResponseData.Write(handle);
return ResultCode.Success;
}
}
}

View File

@ -1,4 +1,5 @@
using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Applets;
using Ryujinx.HLE.HOS.Ipc;
using Ryujinx.HLE.HOS.Kernel.Threading;
using Ryujinx.HLE.HOS.Services.Settings.Types;
@ -16,6 +17,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
private readonly Apm.ManagerServer _apmManagerServer;
private readonly Apm.SystemManagerServer _apmSystemManagerServer;
private readonly RealApplet _applet;
private bool _vrModeEnabled;
#pragma warning disable CS0414, IDE0052 // Remove unread private member
@ -28,9 +30,10 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
private readonly KEvent _acquiredSleepLockEvent;
private int _acquiredSleepLockEventHandle;
public ICommonStateGetter(ServiceCtx context)
public ICommonStateGetter(ServiceCtx context, ulong pid)
{
_context = context;
_applet = context.Device.System.WindowSystem.GetByAruId(pid);
_apmManagerServer = new Apm.ManagerServer(context);
_apmSystemManagerServer = new Apm.SystemManagerServer(context);
@ -42,7 +45,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
// GetEventHandle() -> handle<copy>
public ResultCode GetEventHandle(ServiceCtx context)
{
KEvent messageEvent = context.Device.System.AppletState.MessageEvent;
KEvent messageEvent = _applet.AppletState.MessageEvent;
if (_messageEventHandle == 0)
{
@ -61,12 +64,14 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
// ReceiveMessage() -> nn::am::AppletMessage
public ResultCode ReceiveMessage(ServiceCtx context)
{
if (!context.Device.System.AppletState.PopMessage(out AppletMessage message))
if (!_applet.AppletState.PopMessage(out AppletMessage message))
{
return ResultCode.NoMessages;
}
Logger.Info?.Print(LogClass.ServiceAm, $"pid: {_applet.ProcessHandle.Pid}, msg={message}");
context.ResponseData.Write((int)message);
return ResultCode.Success;
}
@ -94,7 +99,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
// GetBootMode() -> u8
public ResultCode GetBootMode(ServiceCtx context)
{
context.ResponseData.Write((byte)0); //Unknown value.
context.ResponseData.Write((byte)0); // PmBootMode_Normal
Logger.Stub?.PrintStub(LogClass.ServiceAm);
@ -105,7 +110,19 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
// GetCurrentFocusState() -> u8
public ResultCode GetCurrentFocusState(ServiceCtx context)
{
context.ResponseData.Write((byte)context.Device.System.AppletState.AcknowledgedFocusState);
FocusState focusState;
lock (_applet.Lock)
{
focusState = _applet.AppletState.GetAndClearFocusState();
}
if (context.Device.System.WindowSystem.IsFocusedApplet(_applet))
{
focusState = FocusState.InFocus;
}
Logger.Info?.Print(LogClass.ServiceAm, $"pid: {_applet.ProcessHandle.Pid}, GetCurrentFocusState():{focusState}");
context.ResponseData.Write((byte)focusState);
return ResultCode.Success;
}
@ -116,6 +133,8 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
{
Logger.Stub?.PrintStub(LogClass.ServiceAm);
_acquiredSleepLockEvent.ReadableEvent.Signal();
return ResultCode.Success;
}
@ -132,15 +151,21 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
}
context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_acquiredSleepLockEventHandle);
// NOTE: This needs to be signaled when sleep lock is acquired so it does not just wait forever.
// However, since we don't support sleep lock yet, it's fine to signal immediately.
_acquiredSleepLockEvent.ReadableEvent.Signal();
return ResultCode.Success;
}
[CommandCmif(20)]
// PushToGeneralChannel(object<nn::am::service::IStorage>)
public ResultCode PushInData(ServiceCtx context)
{
IStorage data = GetObject<IStorage>(context, 0);
Logger.Stub?.PrintStub(LogClass.ServiceAm);
return ResultCode.Success;
}
[CommandCmif(31)]
[CommandCmif(32)]
// GetReaderLockAccessorEx(u32) -> object<nn::am::service::ILockAccessor>
@ -216,7 +241,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
_vrModeEnabled = vrModeEnabled;
using LblApi lblApi = new();
using var lblApi = new LblApi();
if (vrModeEnabled)
{

View File

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

View File

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

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

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.AppletOE.ApplicationProxyService.ApplicationProxy;
@ -16,7 +17,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService
// GetCommonStateGetter() -> object<nn::am::service::ICommonStateGetter>
public ResultCode GetCommonStateGetter(ServiceCtx context)
{
MakeObject(context, new ICommonStateGetter(context));
MakeObject(context, new ICommonStateGetter(context, _pid));
return ResultCode.Success;
}
@ -61,7 +62,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService
// GetLibraryAppletCreator() -> object<nn::am::service::ILibraryAppletCreator>
public ResultCode GetLibraryAppletCreator(ServiceCtx context)
{
MakeObject(context, new ILibraryAppletCreator());
MakeObject(context, new ILibraryAppletCreator(context, _pid));
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 const int SharedMemEntryCount = 17;
internal const int SharedMemEntryCount = 16;
public DebugPadDevice DebugPad;
public TouchDevice Touchscreen;

View File

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

View File

@ -7,7 +7,7 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common
{
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
private readonly ulong _unused;

View File

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

View File

@ -79,6 +79,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory
TouchScreen = RingLifo<TouchScreenState>.Create(),
Mouse = RingLifo<MouseState>.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++)

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
{
[Service("npns:u")]
class INpnsUser : IpcService
{
public INpnsUser(ServiceCtx context) { }
public KEvent receiveEvent;
public int receiveEventHandle = 0;
public INpnsUser(ServiceCtx context)
{
receiveEvent = new KEvent(context.Device.System.KernelContext);
}
[CommandCmif(5)]
// GetReceiveEvent() -> handle(copy)
public ResultCode GetReceiveEvent(ServiceCtx context)
{
if (receiveEventHandle == 0)
{
if (context.Process.HandleTable.GenerateHandle(receiveEvent.ReadableEvent, out receiveEventHandle) !=
Result.Success)
{
throw new InvalidOperationException("Out of handles");
}
}
context.Response.HandleDesc = IpcHandleDesc.MakeCopy(receiveEventHandle);
return ResultCode.Success;
}
}
}

View File

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

View File

@ -2,6 +2,18 @@
{
class IDynamicRightsInterface : IpcService
{
[CommandCmif(5)]
// VerifyActivatedRightsOwners(u64)
public ResultCode VerifyActivatedRightsOwners(ServiceCtx context) => ResultCode.Success;
[CommandCmif(13)]
// GetRunningApplicationStatus() -> nn::ns::RunningApplicationStatus
public ResultCode GetRunningApplicationStatus(ServiceCtx context)
{
context.ResponseData.Write(0);
return ResultCode.Success;
}
}
}

View File

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

View File

@ -37,7 +37,7 @@ namespace Ryujinx.HLE.HOS.Services.Vi.RootService.ApplicationDisplayService
#pragma warning restore IDE0059
long appletResourceUserId = context.RequestData.ReadInt64();
ulong pid = context.Device.System.AppletState.AppletResourceUserIds.GetData<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);

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);
// 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)
{

View File

@ -1,18 +1,23 @@
using System;
using System.Collections.Concurrent;
using Ryujinx.HLE.HOS.Applets;
using Ryujinx.HLE.HOS.Applets.Types;
using Ryujinx.HLE.HOS.Kernel.Threading;
using Ryujinx.HLE.HOS.Services.Am.AppletAE;
using Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.SystemAppletProxy;
using System;
using System.Collections.Concurrent;
namespace Ryujinx.HLE.HOS.SystemState
{
class AppletStateMgr
{
public ConcurrentQueue<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 RequestedFocusState { get; private set; } = FocusState.Background;
@ -25,33 +30,49 @@ namespace Ryujinx.HLE.HOS.SystemState
public KEvent LaunchableEvent { get; }
public IdDictionary AppletResourceUserIds { get; }
public IdDictionary IndirectLayerHandles { get; }
internal bool IsApplication { get; }
/// <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 _operationModeChangedNotificationEnabled = true;
private bool _performanceModeChangedNotificationEnabled = true;
private bool _hasRequestedExit = false;
private bool _hasAcknowledgedExit = false;
private bool _requestedRequestToDisplayState = false;
private bool _acknowledgedRequestToDisplayState = false;
private bool _hasRequestedRequestToPrepareSleep = false;
private bool _hasAcknowledgedRequestToPrepareSleep = false;
private bool _hasOperationModeChanged = false;
private bool _hasPerformanceModeChanged = false;
private bool _hasResume = false;
private bool _hasFocusStateChanged = false;
private bool _hasAlbumRecordingSaved = false;
private bool _hasAlbumScreenShotTaken = false;
private bool _hasAutoPowerDown = false;
private bool _hasSleepRequiredByLowBattery = false;
private bool _hasSleepRequiredByHighTemperature = false;
private bool _hasSdCardRemoved = false;
// Internal event state for message signaling.
private bool _eventSignaled = false;
// Indicates how the applet handles focus and suspension.
private FocusHandlingMode _focusHandlingMode = FocusHandlingMode.NoSuspend;
public bool HasRequestedExit => _hasRequestedExit;
#endregion
#region Properties with Custom Logic
public bool FocusStateChangedNotificationEnabled
{
@ -59,7 +80,7 @@ namespace Ryujinx.HLE.HOS.SystemState
set
{
_focusStateChangedNotificationEnabled = value;
// SignalEventIfNeeded();
SignalEventIfNeeded();
}
}
@ -83,10 +104,15 @@ namespace Ryujinx.HLE.HOS.SystemState
}
}
public AppletStateMgr(Horizon system, bool isApplication)
#endregion
#region Constructor
// Note: The constructor no longer takes an "isApplication" parameter.
public AppletStateMgr(Horizon system)
{
IsApplication = isApplication;
Messages = new ConcurrentQueue<AppletMessage>();
MessageEvent = new KEvent(system.KernelContext);
OperationModeChangedEvent = new KEvent(system.KernelContext);
LaunchableEvent = new KEvent(system.KernelContext);
@ -95,15 +121,18 @@ namespace Ryujinx.HLE.HOS.SystemState
IndirectLayerHandles = new IdDictionary();
}
#endregion
#region Public Methods
public void SetFocusState(FocusState state)
{
if (RequestedFocusState != state)
{
RequestedFocusState = state;
_hasFocusStateChanged = true;
SignalEventIfNeeded();
}
SignalEventIfNeeded();
}
public FocusState GetAndClearFocusState()
@ -115,10 +144,13 @@ namespace Ryujinx.HLE.HOS.SystemState
public void PushUnorderedMessage(AppletMessage message)
{
Messages.Enqueue(message);
SignalEventIfNeeded();
}
/// <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)
{
message = GetNextMessage();
@ -126,6 +158,201 @@ namespace Ryujinx.HLE.HOS.SystemState
return message != AppletMessage.None;
}
public void OnOperationAndPerformanceModeChanged()
{
if (_operationModeChangedNotificationEnabled)
{
_hasOperationModeChanged = true;
}
if (_performanceModeChangedNotificationEnabled)
{
_hasPerformanceModeChanged = true;
}
OperationModeChangedEvent.ReadableEvent.Signal();
SignalEventIfNeeded();
}
public void OnExitRequested()
{
_hasRequestedExit = true;
SignalEventIfNeeded();
}
public void SetFocusHandlingMode(bool suspend)
{
// Adjust the focus handling mode based on the desired suspend state.
_focusHandlingMode = _focusHandlingMode switch
{
FocusHandlingMode.AlwaysSuspend or FocusHandlingMode.SuspendHomeSleep when !suspend => FocusHandlingMode.NoSuspend,
FocusHandlingMode.NoSuspend when suspend => FocusHandlingMode.SuspendHomeSleep,
_ => _focusHandlingMode,
};
SignalEventIfNeeded();
}
public void RequestResumeNotification()
{
// Note: There is a known bug in AM whereby concurrent resume notifications
// may cause the first notification to be lost.
if (ResumeNotificationEnabled)
{
_hasResume = true;
SignalEventIfNeeded();
}
}
public void SetOutOfFocusSuspendingEnabled(bool enabled)
{
_focusHandlingMode = _focusHandlingMode switch
{
FocusHandlingMode.AlwaysSuspend when !enabled => FocusHandlingMode.SuspendHomeSleep,
FocusHandlingMode.SuspendHomeSleep or FocusHandlingMode.NoSuspend when enabled => FocusHandlingMode.AlwaysSuspend,
_ => _focusHandlingMode,
};
SignalEventIfNeeded();
}
public void RemoveForceResumeIfPossible()
{
if (SuspendMode != SuspendMode.ForceResume)
{
return;
}
// If the activity is already resumed, we can remove the forced state.
if (ActivityState == ActivityState.ForegroundVisible ||
ActivityState == ActivityState.ForegroundObscured)
{
SuspendMode = SuspendMode.NoOverride;
return;
}
// Without a separate application flag, simply remove forced resume.
SuspendMode = SuspendMode.NoOverride;
}
public bool IsRunnable()
{
if (ForcedSuspend)
{
return false;
}
switch (SuspendMode)
{
case SuspendMode.ForceResume:
return _hasRequestedExit; // During forced resume, only exit requests make it runnable.
case SuspendMode.ForceSuspend:
return false;
}
if (_hasRequestedExit)
{
return true;
}
if (ActivityState == ActivityState.ForegroundVisible)
{
return true;
}
if (ActivityState == ActivityState.ForegroundObscured)
{
return _focusHandlingMode switch
{
FocusHandlingMode.AlwaysSuspend => false,
FocusHandlingMode.SuspendHomeSleep => true,
FocusHandlingMode.NoSuspend => true,
_ => false,
};
}
// When not in the foreground, run only if suspension is disabled.
return _focusHandlingMode == FocusHandlingMode.NoSuspend;
}
public FocusState GetFocusStateWhileForegroundObscured() =>
_focusHandlingMode switch
{
FocusHandlingMode.AlwaysSuspend => FocusState.InFocus,
FocusHandlingMode.SuspendHomeSleep => FocusState.OutOfFocus,
FocusHandlingMode.NoSuspend => FocusState.OutOfFocus,
_ => throw new IndexOutOfRangeException()
};
public FocusState GetFocusStateWhileBackground(bool isObscured) =>
_focusHandlingMode switch
{
FocusHandlingMode.AlwaysSuspend => FocusState.InFocus,
FocusHandlingMode.SuspendHomeSleep => isObscured ? FocusState.OutOfFocus : FocusState.InFocus,
// Without an application flag, default to Background.
FocusHandlingMode.NoSuspend => FocusState.Background,
_ => throw new IndexOutOfRangeException(),
};
public bool UpdateRequestedFocusState()
{
FocusState newState;
if (SuspendMode == SuspendMode.NoOverride)
{
newState = ActivityState switch
{
ActivityState.ForegroundVisible => FocusState.InFocus,
ActivityState.ForegroundObscured => GetFocusStateWhileForegroundObscured(),
ActivityState.BackgroundVisible => GetFocusStateWhileBackground(false),
ActivityState.BackgroundObscured => GetFocusStateWhileBackground(true),
_ => throw new IndexOutOfRangeException(),
};
}
else
{
newState = GetFocusStateWhileBackground(false);
}
if (newState != RequestedFocusState)
{
RequestedFocusState = newState;
_hasFocusStateChanged = true;
SignalEventIfNeeded();
return true;
}
return false;
}
public void SetFocus(bool isFocused)
{
SetFocusHandlingMode(false);
FocusState focusState = isFocused ? FocusState.InFocus : FocusState.OutOfFocus;
SetFocusState(focusState);
}
public void SetFocusForce(bool isFocused, bool shouldSuspend = false)
{
Messages.Clear();
SetFocusHandlingMode(shouldSuspend);
RequestedFocusState = isFocused ? FocusState.InFocus : FocusState.OutOfFocus;
Messages.Enqueue(AppletMessage.FocusStateChanged);
if (isFocused)
{
Messages.Enqueue(AppletMessage.ChangeIntoForeground);
}
else
{
Messages.Enqueue(AppletMessage.ChangeIntoBackground);
}
MessageEvent.ReadableEvent.Signal();
}
#endregion
#region Private Methods
/// <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()
{
if (_hasResume)
@ -140,31 +367,17 @@ namespace Ryujinx.HLE.HOS.SystemState
return AppletMessage.Exit;
}
if (_focusStateChangedNotificationEnabled)
// Unify focus state change handling: if the acknowledged focus state does not match the requested one,
// update it and return the appropriate foreground/background message.
if (_focusStateChangedNotificationEnabled && RequestedFocusState != AcknowledgedFocusState)
{
if (IsApplication)
AcknowledgedFocusState = RequestedFocusState;
return RequestedFocusState switch
{
if (_hasFocusStateChanged)
{
_hasFocusStateChanged = false;
return AppletMessage.FocusStateChanged;
}
}
else
{
if (RequestedFocusState != AcknowledgedFocusState)
{
AcknowledgedFocusState = RequestedFocusState;
switch (RequestedFocusState)
{
case FocusState.InFocus:
return AppletMessage.ChangeIntoForeground;
case FocusState.OutOfFocus:
return AppletMessage.ChangeIntoBackground;
}
}
}
FocusState.InFocus => AppletMessage.ChangeIntoForeground,
FocusState.OutOfFocus => AppletMessage.ChangeIntoBackground,
_ => AppletMessage.FocusStateChanged,
};
}
if (_hasRequestedRequestToPrepareSleep != _hasAcknowledgedRequestToPrepareSleep)
@ -227,353 +440,54 @@ namespace Ryujinx.HLE.HOS.SystemState
return AppletMessage.AlbumRecordingSaved;
}
if (Messages.TryDequeue(out AppletMessage message))
{
return message;
}
return AppletMessage.None;
return Messages.TryDequeue(out var message) ? message : AppletMessage.None;
}
internal void SignalEventIfNeeded()
/// <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)
{
MessageEvent.ReadableEvent.Clear();
_eventSignaled = false;
}
else
if (shouldSignal)
{
MessageEvent.ReadableEvent.Signal();
_eventSignaled = true;
}
}
}
private bool ShouldSignalEvent()
{
bool focusStateChanged = false;
if (_focusStateChangedNotificationEnabled)
{
if (IsApplication)
{
if (_hasFocusStateChanged)
{
focusStateChanged = true;
}
}
else
{
if (RequestedFocusState != AcknowledgedFocusState)
{
focusStateChanged = true;
}
MessageEvent.ReadableEvent.Clear();
}
}
return !Messages.IsEmpty
|| focusStateChanged
|| _hasResume
|| _hasRequestedExit != _hasAcknowledgedExit
|| _hasRequestedRequestToPrepareSleep != _hasAcknowledgedRequestToPrepareSleep
|| _hasOperationModeChanged
|| _hasPerformanceModeChanged
|| _hasSdCardRemoved
|| _hasSleepRequiredByHighTemperature
|| _hasSleepRequiredByLowBattery
|| _hasAutoPowerDown
|| _requestedRequestToDisplayState != _acknowledgedRequestToDisplayState
|| _hasAlbumScreenShotTaken
|| _hasAlbumRecordingSaved;
}
public void OnOperationAndPerformanceModeChanged()
{
if (_operationModeChangedNotificationEnabled)
{
_hasOperationModeChanged = true;
}
if (_performanceModeChangedNotificationEnabled)
{
_hasPerformanceModeChanged = true;
}
OperationModeChangedEvent.ReadableEvent.Signal();
SignalEventIfNeeded();
}
public void OnExitRequested()
{
_hasRequestedExit = true;
SignalEventIfNeeded();
}
public void SetFocusHandlingMode(bool suspend)
{
switch (_focusHandlingMode)
{
case FocusHandlingMode.AlwaysSuspend:
case FocusHandlingMode.SuspendHomeSleep:
if (!suspend)
{
// Disallow suspension
_focusHandlingMode = FocusHandlingMode.NoSuspend;
}
break;
case FocusHandlingMode.NoSuspend:
if (suspend)
{
// Allow suspension temporarily.
_focusHandlingMode = FocusHandlingMode.SuspendHomeSleep;
}
break;
}
// SignalEventIfNeeded();
}
public void RequestResumeNotification()
{
// NOTE: this appears to be a bug in am.
// If an applet makes a concurrent request to receive resume notifications
// while it is being suspended, the first resume notification will be lost.
// This is not the case with other notification types.
if (ResumeNotificationEnabled)
{
_hasResume = true;
_eventSignaled = shouldSignal;
}
}
public void SetOutOfFocusSuspendingEnabled(bool enabled)
{
switch (_focusHandlingMode)
{
case FocusHandlingMode.AlwaysSuspend:
if (!enabled)
{
// Allow suspension temporarily.
_focusHandlingMode = FocusHandlingMode.SuspendHomeSleep;
}
break;
case FocusHandlingMode.SuspendHomeSleep:
case FocusHandlingMode.NoSuspend:
if (enabled)
{
// Allow suspension
_focusHandlingMode = FocusHandlingMode.AlwaysSuspend;
}
break;
}
SignalEventIfNeeded();
}
public void RemoveForceResumeIfPossible()
{
// If resume is not forced, we have nothing to do.
if (SuspendMode != SuspendMode.ForceResume)
{
return;
}
// Check activity state.
// If we are already resumed, we can remove the forced state.
switch (ActivityState)
{
case ActivityState.ForegroundVisible:
case ActivityState.ForegroundObscured:
SuspendMode = SuspendMode.NoOverride;
return;
}
// Check focus handling mode.
switch (_focusHandlingMode)
{
case FocusHandlingMode.AlwaysSuspend:
case FocusHandlingMode.SuspendHomeSleep:
// If the applet allows suspension, we can remove the forced state.
SuspendMode = SuspendMode.NoOverride;
break;
case FocusHandlingMode.NoSuspend:
// If the applet is not an application, we can remove the forced state.
// Only applications can be forced to resume.
if (!IsApplication)
{
SuspendMode = SuspendMode.NoOverride;
}
break;
}
}
public bool IsRunnable()
{
// If suspend is forced, return that.
if (ForcedSuspend)
{
return false;
}
// Check suspend mode override.
switch (SuspendMode)
{
case SuspendMode.NoOverride:
// Continue processing.
break;
case SuspendMode.ForceResume:
// The applet is runnable during forced resumption when its exit is requested.
return _hasRequestedExit;
case SuspendMode.ForceSuspend:
// The applet is never runnable during forced suspension.
return false;
}
// Always run if exit is requested.
if (_hasRequestedExit)
{
return true;
}
if (ActivityState == ActivityState.ForegroundVisible)
{
// The applet is runnable now.
return true;
}
if (ActivityState == ActivityState.ForegroundObscured)
{
switch (_focusHandlingMode)
{
case FocusHandlingMode.AlwaysSuspend:
// The applet is not runnable while running the applet.
return false;
case FocusHandlingMode.SuspendHomeSleep:
// The applet is runnable while running the applet.
return true;
case FocusHandlingMode.NoSuspend:
// The applet is always runnable.
return true;
}
}
// The activity is a suspended one.
// The applet should be suspended unless it has disabled suspension.
return _focusHandlingMode == FocusHandlingMode.NoSuspend;
}
public FocusState GetFocusStateWhileForegroundObscured()
{
switch (_focusHandlingMode)
{
case FocusHandlingMode.AlwaysSuspend:
// The applet never learns it has lost focus.
return FocusState.InFocus;
case FocusHandlingMode.SuspendHomeSleep:
// The applet learns it has lost focus when launching a child applet.
return FocusState.OutOfFocus;
case FocusHandlingMode.NoSuspend:
// The applet always learns it has lost focus.
return FocusState.OutOfFocus;
default:
throw new IndexOutOfRangeException();
}
}
public FocusState GetFocusStateWhileBackground(bool isObscured)
{
switch (_focusHandlingMode)
{
case FocusHandlingMode.AlwaysSuspend:
// The applet never learns it has lost focus.
return FocusState.InFocus;
case FocusHandlingMode.SuspendHomeSleep:
// The applet learns it has lost focus when launching a child applet.
return isObscured ? FocusState.OutOfFocus : FocusState.InFocus;
case FocusHandlingMode.NoSuspend:
// The applet always learns it has lost focus.
return IsApplication ? FocusState.Background : FocusState.OutOfFocus;
default:
throw new IndexOutOfRangeException();
}
}
public bool UpdateRequestedFocusState()
{
FocusState newState;
if (SuspendMode == SuspendMode.NoOverride)
{
// With no forced suspend or resume, we take the focus state designated
// by the combination of the activity flag and the focus handling mode.
switch (ActivityState)
{
case ActivityState.ForegroundVisible:
newState = FocusState.InFocus;
break;
case ActivityState.ForegroundObscured:
newState = GetFocusStateWhileForegroundObscured();
break;
case ActivityState.BackgroundVisible:
newState = GetFocusStateWhileBackground(false);
break;
case ActivityState.BackgroundObscured:
newState = GetFocusStateWhileBackground(true);
break;
default:
throw new IndexOutOfRangeException();
}
}
else
{
// With forced suspend or resume, the applet is guaranteed to be background.
newState = GetFocusStateWhileBackground(false);
}
if (newState != RequestedFocusState)
{
// Mark the focus state as ready for update.
RequestedFocusState = newState;
_hasFocusStateChanged = true;
// We changed the focus state.
return true;
}
// We didn't change the focus state.
return false;
}
public void SetFocus(bool isFocused)
{
AcknowledgedFocusState = isFocused ? FocusState.InFocus : FocusState.OutOfFocus;
Messages.Enqueue(AppletMessage.FocusStateChanged);
if (isFocused)
{
Messages.Enqueue(AppletMessage.ChangeIntoForeground);
}
MessageEvent.ReadableEvent.Signal();
}
#endregion
}
}

View File

@ -1,10 +1,14 @@
using LibHac.Common;
using LibHac.Ncm;
using LibHac.Ns;
using LibHac.Tools.FsSystem.NcaUtils;
using Ryujinx.Audio.Backends.CompatLayer;
using Ryujinx.Audio.Integration;
using Ryujinx.Common;
using Ryujinx.Common.Configuration;
using Ryujinx.Common.Logging;
using Ryujinx.Graphics.Gpu;
using Ryujinx.HLE.Exceptions;
using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.HOS;
using Ryujinx.HLE.HOS.Services.Apm;
@ -158,5 +162,23 @@ namespace Ryujinx.HLE
Shared = null;
}
}
public void UpdateWindowSystemInput()
{
System.WindowSystem.ButtonPressTracker.Update();
}
public bool LoadSystemProgramId(ulong programId)
{
string contentPath = System.ContentManager.GetInstalledContentPath(programId, StorageId.BuiltInSystem, NcaContentType.Program);
string filePath = VirtualFileSystem.SwitchPathToSystemPath(contentPath);
if (string.IsNullOrWhiteSpace(filePath))
{
throw new InvalidSystemResourceException("Specified title ID is not installed on the system.");
}
return Processes.LoadNca(filePath);
}
}
}

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.Sf;
namespace Ryujinx.Horizon.Ovln.Ipc
{
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.Sf;
@ -6,5 +7,15 @@ namespace Ryujinx.Horizon.Ovln.Ipc
{
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.SingleRightTrigger0, (GamepadButtonInputId)_configuration.LeftJoycon.ButtonSr));
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleLeftTrigger0, (GamepadButtonInputId)_configuration.LeftJoycon.ButtonSl));
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.Capture, (GamepadButtonInputId)_configuration.LeftJoycon.ButtonCapture));
// Finally right joycon
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.RightStick, (GamepadButtonInputId)_configuration.RightJoyconStick.StickButton));
@ -270,6 +271,7 @@ namespace Ryujinx.Input.SDL2
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.RightTrigger, (GamepadButtonInputId)_configuration.RightJoycon.ButtonZr));
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleRightTrigger1, (GamepadButtonInputId)_configuration.RightJoycon.ButtonSr));
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleLeftTrigger1, (GamepadButtonInputId)_configuration.RightJoycon.ButtonSl));
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.Home, (GamepadButtonInputId)_configuration.RightJoycon.ButtonHome));
SetTriggerThreshold(_configuration.TriggerThreshold);
}

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

View File

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

View File

@ -49,7 +49,9 @@ namespace Ryujinx.Input.HLE
new(GamepadButtonInputId.SingleLeftTrigger0, ControllerKeys.SlLeft),
new(GamepadButtonInputId.SingleRightTrigger0, ControllerKeys.SrLeft),
new(GamepadButtonInputId.SingleLeftTrigger1, ControllerKeys.SlRight),
new(GamepadButtonInputId.SingleRightTrigger1, ControllerKeys.SrRight)
new(GamepadButtonInputId.SingleRightTrigger1, ControllerKeys.SrRight),
new(GamepadButtonInputId.Capture, ControllerKeys.Capture),
new(GamepadButtonInputId.Home, ControllerKeys.Home)
];
private class HLEKeyboardMappingEntry

View File

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

View File

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

View File

@ -7122,6 +7122,56 @@
"zh_TW": ""
}
},
{
"ID": "ControllerSettingsButtonHome",
"Translations": {
"ar_SA": "",
"de_DE": "",
"el_GR": "",
"en_US": "Home",
"es_ES": "",
"fr_FR": "",
"he_IL": "",
"it_IT": "",
"ja_JP": "",
"ko_KR": "",
"no_NO": "",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"sv_SE": "",
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
"zh_CN": "",
"zh_TW": ""
}
},
{
"ID": "ControllerSettingsButtonCapture",
"Translations": {
"ar_SA": "",
"de_DE": "",
"el_GR": "",
"en_US": "Capture",
"es_ES": "",
"fr_FR": "",
"he_IL": "",
"it_IT": "",
"ja_JP": "",
"ko_KR": "",
"no_NO": "",
"pl_PL": "",
"pt_BR": "",
"ru_RU": "",
"sv_SE": "",
"th_TH": "",
"tr_TR": "",
"uk_UA": "",
"zh_CN": "",
"zh_TW": ""
}
},
{
"ID": "ControllerSettingsDPad",
"Translations": {

View File

@ -118,6 +118,7 @@ namespace Ryujinx.Headless
ButtonZl = Key.Q,
ButtonSl = Key.Unbound,
ButtonSr = Key.Unbound,
ButtonCapture = Key.Unbound,
},
LeftJoyconStick = new JoyconConfigKeyboardStick<Key>
@ -140,6 +141,7 @@ namespace Ryujinx.Headless
ButtonZr = Key.O,
ButtonSl = Key.Unbound,
ButtonSr = Key.Unbound,
ButtonHome = Key.Unbound,
},
RightJoyconStick = new JoyconConfigKeyboardStick<Key>
@ -178,6 +180,7 @@ namespace Ryujinx.Headless
ButtonZl = ConfigGamepadInputId.LeftTrigger,
ButtonSl = ConfigGamepadInputId.Unbound,
ButtonSr = ConfigGamepadInputId.Unbound,
ButtonCapture = ConfigGamepadInputId.Capture,
},
LeftJoyconStick = new JoyconConfigControllerStick<ConfigGamepadInputId, ConfigStickInputId>
@ -200,6 +203,7 @@ namespace Ryujinx.Headless
ButtonZr = ConfigGamepadInputId.RightTrigger,
ButtonSl = ConfigGamepadInputId.Unbound,
ButtonSr = ConfigGamepadInputId.Unbound,
ButtonHome = ConfigGamepadInputId.Home,
},
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.SingleRightTrigger0, (Key)_configuration.LeftJoycon.ButtonSr));
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleLeftTrigger0, (Key)_configuration.LeftJoycon.ButtonSl));
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.Capture, (Key)_configuration.LeftJoycon.ButtonCapture));
// Right JoyCon
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.RightStick, (Key)_configuration.RightJoyconStick.StickButton));
@ -140,6 +141,7 @@ namespace Ryujinx.Ava.Input
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.RightTrigger, (Key)_configuration.RightJoycon.ButtonZr));
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleRightTrigger1, (Key)_configuration.RightJoycon.ButtonSr));
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.SingleLeftTrigger1, (Key)_configuration.RightJoycon.ButtonSl));
_buttonsUserMapping.Add(new ButtonMappingEntry(GamepadButtonInputId.Home, (Key)_configuration.RightJoycon.ButtonHome));
#pragma warning restore IDE0055
}
}

View File

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

View File

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

View File

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

View File

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

View File

@ -309,6 +309,21 @@
</StackPanel>
</StackPanel>
</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>
<!-- Triggers & Side Buttons -->
<StackPanel
@ -783,6 +798,21 @@
</StackPanel>
</StackPanel>
</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>
</Grid>
</StackPanel>

View File

@ -184,6 +184,12 @@ namespace Ryujinx.Ava.UI.Views.Input
case "RightJoystick":
viewModel.Config.RightJoystick = buttonValue.AsHidType<StickInputId>();
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>
</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>
<!-- Triggers & Side Buttons -->
<StackPanel
@ -666,6 +681,21 @@
</StackPanel>
</StackPanel>
</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>
</Grid>
</StackPanel>

View File

@ -85,7 +85,7 @@ namespace Ryujinx.Ava.UI.Views.Input
break;
case "ButtonMinus":
viewModel.Config.ButtonMinus = buttonValue.AsHidType<Key>();
break;
break;
case "LeftStickButton":
viewModel.Config.LeftStickButton = buttonValue.AsHidType<Key>();
break;
@ -161,6 +161,12 @@ namespace Ryujinx.Ava.UI.Views.Input
case "RightStickLeft":
viewModel.Config.RightStickLeft = buttonValue.AsHidType<Key>();
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,
ButtonSl = Key.Unbound,
ButtonSr = Key.Unbound,
ButtonCapture = Key.Unbound,
},
LeftJoyconStick = new JoyconConfigKeyboardStick<Key>
{
@ -243,6 +244,7 @@ namespace Ryujinx.Ava.Utilities.Configuration
ButtonZr = Key.O,
ButtonSl = Key.Unbound,
ButtonSr = Key.Unbound,
ButtonHome = Key.Unbound,
},
RightJoyconStick = new JoyconConfigKeyboardStick<Key>
{

View File

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