Implement application switching
Co-authored-by: Alula <6276139+alula@users.noreply.github.com>
This commit is contained in:
parent
ffcc13e1cc
commit
f67cf6a87c
@ -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,
|
||||||
|
@ -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; }
|
||||||
|
|
||||||
|
@ -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; }
|
||||||
|
@ -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; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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 menu’s 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 caller’s 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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
{
|
{
|
||||||
|
@ -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!");
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,7 @@
|
|||||||
|
namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.SystemAppletProxy
|
||||||
|
{
|
||||||
|
class IRemoteStorageController : IpcService
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -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)
|
||||||
|
@ -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;
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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,
|
||||||
|
@ -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;
|
||||||
|
@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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++)
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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>
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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)
|
||||||
{
|
{
|
||||||
|
@ -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();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
65
src/Ryujinx.Horizon/Ovln/Ipc/Receiver.cs
Normal file
65
src/Ryujinx.Horizon/Ovln/Ipc/Receiver.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
12
src/Ryujinx.Horizon/Sdk/Ovln/IReceiver.cs
Normal file
12
src/Ryujinx.Horizon/Sdk/Ovln/IReceiver.cs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
10
src/Ryujinx.Horizon/Sdk/Ovln/SourceName.cs
Normal file
10
src/Ryujinx.Horizon/Sdk/Ovln/SourceName.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,6 +35,9 @@ namespace Ryujinx.Input
|
|||||||
|
|
||||||
Guide,
|
Guide,
|
||||||
Misc1,
|
Misc1,
|
||||||
|
|
||||||
|
Home = Guide,
|
||||||
|
Capture = Misc1,
|
||||||
|
|
||||||
// Xbox Elite paddle
|
// Xbox Elite paddle
|
||||||
Paddle1,
|
Paddle1,
|
||||||
|
@ -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
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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()
|
||||||
|
@ -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": {
|
||||||
|
@ -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>
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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>
|
||||||
{
|
{
|
||||||
|
@ -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>
|
||||||
{
|
{
|
||||||
|
@ -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>
|
||||||
{
|
{
|
||||||
|
@ -1568,6 +1568,8 @@ namespace Ryujinx.Ava.UI.ViewModels
|
|||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AppHost.Device.System.SetupFirst(AppHost.Device.Processes.ActiveApplication.ProcessId);
|
||||||
|
|
||||||
CanUpdate = false;
|
CanUpdate = false;
|
||||||
|
|
||||||
|
@ -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>
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -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>
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -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>
|
||||||
{
|
{
|
||||||
|
@ -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>
|
||||||
{
|
{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user