Implement application launching
Co-authored-by: Alula <6276139+alula@users.noreply.github.com>
This commit is contained in:
parent
50926d6a2f
commit
ffcc13e1cc
227
src/Ryujinx.HLE/HOS/Applets/EventObserver.cs
Normal file
227
src/Ryujinx.HLE/HOS/Applets/EventObserver.cs
Normal file
@ -0,0 +1,227 @@
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.HLE.HOS.Applets.Types;
|
||||
using Ryujinx.HLE.HOS.Kernel.Process;
|
||||
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||
using Ryujinx.Horizon;
|
||||
using Ryujinx.Horizon.Sdk.OsTypes;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Applets
|
||||
{
|
||||
public class EventObserver
|
||||
{
|
||||
private readonly object _lock = new();
|
||||
|
||||
private Horizon _system;
|
||||
private WindowSystem _windowSystem;
|
||||
|
||||
// Guest event handle to wake up the event loop
|
||||
internal SystemEventType _wakeupEvent;
|
||||
internal MultiWaitHolder _wakeupHolder;
|
||||
private KWritableEvent _wakeupEventObj;
|
||||
|
||||
// List of owned process holders
|
||||
private readonly LinkedList<ProcessHolder> _processHolders = new();
|
||||
|
||||
// Multi-wait objects for new tasks
|
||||
private readonly MultiWait _multiWait;
|
||||
private readonly MultiWait _deferredWaitList;
|
||||
|
||||
// Processing thread
|
||||
private KThread _thread;
|
||||
private readonly CancellationTokenSource _cts = new();
|
||||
private bool _initialized;
|
||||
|
||||
public EventObserver(Horizon system, WindowSystem windowSystem)
|
||||
{
|
||||
_system = system;
|
||||
_windowSystem = windowSystem;
|
||||
|
||||
_multiWait = new();
|
||||
_deferredWaitList = new();
|
||||
|
||||
_windowSystem.SetEventObserver(this);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (!_initialized)
|
||||
return;
|
||||
|
||||
// Signal and wait for the thread to finish
|
||||
_cts.Cancel();
|
||||
// Os.SignalSystemEvent(ref _wakeupEvent);
|
||||
_wakeupEventObj?.Signal();
|
||||
// _thread.Join();
|
||||
|
||||
_processHolders.Clear();
|
||||
}
|
||||
|
||||
private void Initialize()
|
||||
{
|
||||
if (_initialized)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_initialized = true;
|
||||
|
||||
var holderProcess = _system.ViServer.SelfProcess;
|
||||
_thread = new KThread(_system.KernelContext);
|
||||
_thread.SetName("am:EventObserver");
|
||||
_thread.Initialize(
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
3,
|
||||
holderProcess,
|
||||
ThreadType.Kernel,
|
||||
ThreadFunc);
|
||||
_thread.Owner.HandleTable.GenerateHandle(_thread, out int threadHandle).AbortOnFailure();
|
||||
_thread.SetEntryArguments(0, threadHandle);
|
||||
_thread.Start();
|
||||
}
|
||||
|
||||
internal void TrackAppletProcess(RealApplet applet)
|
||||
{
|
||||
Initialize();
|
||||
|
||||
if (applet.ProcessHandle == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_thread.Owner.HandleTable.GenerateHandle(applet.ProcessHandle, out int processHandle).AbortOnFailure();
|
||||
ProcessHolder holder = new(applet, applet.ProcessHandle, processHandle);
|
||||
holder.UserData = UserDataTag.AppletProcess;
|
||||
|
||||
// lock (_lock)
|
||||
{
|
||||
_processHolders.AddLast(holder);
|
||||
_deferredWaitList.LinkMultiWaitHolder(holder);
|
||||
}
|
||||
|
||||
RequestUpdate();
|
||||
}
|
||||
|
||||
public void RequestUpdate()
|
||||
{
|
||||
Initialize();
|
||||
|
||||
// Os.SignalSystemEvent(ref _wakeupEvent);
|
||||
// lock (_lock)
|
||||
{
|
||||
_wakeupEventObj?.Signal();
|
||||
}
|
||||
}
|
||||
|
||||
public void LinkDeferred()
|
||||
{
|
||||
// lock (_lock)
|
||||
{
|
||||
_multiWait.MoveAllFrom(_deferredWaitList);
|
||||
}
|
||||
}
|
||||
|
||||
private MultiWaitHolder WaitSignaled()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
LinkDeferred();
|
||||
|
||||
if (_cts.Token.IsCancellationRequested)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var selected = _multiWait.WaitAny();
|
||||
// Logger.Warning?.Print(LogClass.ServiceAm, $"*** Selected={selected}");
|
||||
if (selected != _wakeupHolder)
|
||||
{
|
||||
selected.UnlinkFromMultiWaitHolder();
|
||||
}
|
||||
|
||||
return selected;
|
||||
}
|
||||
}
|
||||
|
||||
private void Process(MultiWaitHolder holder)
|
||||
{
|
||||
switch (holder.UserData)
|
||||
{
|
||||
case UserDataTag.WakeupEvent:
|
||||
OnWakeupEvent(holder);
|
||||
break;
|
||||
case UserDataTag.AppletProcess:
|
||||
OnProcessEvent((ProcessHolder)holder);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnWakeupEvent(MultiWaitHolder holder)
|
||||
{
|
||||
// Os.ClearSystemEvent(ref _wakeupEvent);
|
||||
Logger.Warning?.Print(LogClass.ServiceAm, "*** Wakeup event");
|
||||
// lock (_lock)
|
||||
{
|
||||
_wakeupEventObj?.Clear();
|
||||
}
|
||||
_windowSystem.Update();
|
||||
}
|
||||
|
||||
private void OnProcessEvent(ProcessHolder holder)
|
||||
{
|
||||
var applet = holder.Applet;
|
||||
var process = holder.ProcessHandle;
|
||||
Logger.Warning?.Print(LogClass.ServiceAm, $"*** Applet={applet.AppletId}, Process={process.Pid}, State={process.State}");
|
||||
|
||||
// lock (_lock)
|
||||
{
|
||||
if (process.State == ProcessState.Exited)
|
||||
{
|
||||
_processHolders.Remove(holder);
|
||||
}
|
||||
else
|
||||
{
|
||||
process.ClearIfNotExited();
|
||||
_deferredWaitList.LinkMultiWaitHolder(holder);
|
||||
}
|
||||
|
||||
applet.IsProcessRunning = process.IsRunning();
|
||||
}
|
||||
_windowSystem.Update();
|
||||
}
|
||||
|
||||
private void ThreadFunc()
|
||||
{
|
||||
HorizonStatic.Register(
|
||||
default,
|
||||
_system.KernelContext.Syscall,
|
||||
null,
|
||||
_thread.ThreadContext,
|
||||
(int)_thread.ThreadContext.GetX(1));
|
||||
|
||||
// lock (_lock)
|
||||
{
|
||||
Os.CreateSystemEvent(out _wakeupEvent, EventClearMode.ManualClear, true).AbortOnFailure();
|
||||
_wakeupEventObj = _thread.Owner.HandleTable.GetObject<KWritableEvent>(Os.GetWritableHandleOfSystemEvent(ref _wakeupEvent));
|
||||
|
||||
_wakeupHolder = new MultiWaitHolderOfInterProcessEvent(_wakeupEvent.InterProcessEvent);
|
||||
_wakeupHolder.UserData = UserDataTag.WakeupEvent;
|
||||
_multiWait.LinkMultiWaitHolder(_wakeupHolder);
|
||||
}
|
||||
|
||||
while (!_cts.Token.IsCancellationRequested)
|
||||
{
|
||||
var holder = WaitSignaled();
|
||||
if (holder == null)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
Process(holder);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
31
src/Ryujinx.HLE/HOS/Applets/IRealApplet.cs
Normal file
31
src/Ryujinx.HLE/HOS/Applets/IRealApplet.cs
Normal file
@ -0,0 +1,31 @@
|
||||
using Ryujinx.HLE.HOS.Services.Am.AppletAE;
|
||||
using Ryujinx.HLE.UI;
|
||||
using Ryujinx.Memory;
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Applets
|
||||
{
|
||||
interface IRealApplet
|
||||
{
|
||||
event EventHandler AppletStateChanged;
|
||||
|
||||
ResultCode Start(AppletChannel inChannel,
|
||||
AppletChannel outChannel,
|
||||
AppletChannel interactiveInChannel,
|
||||
AppletChannel interactiveOutChannel,
|
||||
AppletChannel contextChannel);
|
||||
|
||||
ResultCode GetResult();
|
||||
|
||||
bool DrawTo(RenderingSurfaceInfo surfaceInfo, IVirtualMemoryManager destination, ulong position)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static T ReadStruct<T>(ReadOnlySpan<byte> data) where T : unmanaged
|
||||
{
|
||||
return MemoryMarshal.Cast<byte, T>(data)[0];
|
||||
}
|
||||
}
|
||||
}
|
18
src/Ryujinx.HLE/HOS/Applets/ProcessHolder.cs
Normal file
18
src/Ryujinx.HLE/HOS/Applets/ProcessHolder.cs
Normal file
@ -0,0 +1,18 @@
|
||||
using Ryujinx.HLE.HOS.Kernel.Process;
|
||||
using Ryujinx.Horizon.Sdk.OsTypes;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Applets
|
||||
{
|
||||
internal class ProcessHolder : MultiWaitHolderOfHandle
|
||||
{
|
||||
public RealApplet Applet { get; private set; }
|
||||
public KProcess ProcessHandle { get; private set; }
|
||||
|
||||
public ProcessHolder(RealApplet applet, KProcess kProcess, int processHandle)
|
||||
: base(processHandle)
|
||||
{
|
||||
Applet = applet;
|
||||
ProcessHandle = kProcess;
|
||||
}
|
||||
}
|
||||
}
|
254
src/Ryujinx.HLE/HOS/Applets/RealApplet.cs
Normal file
254
src/Ryujinx.HLE/HOS/Applets/RealApplet.cs
Normal file
@ -0,0 +1,254 @@
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.HLE.HOS.Kernel.Process;
|
||||
using Ryujinx.HLE.HOS.Services.Am.AppletAE;
|
||||
using Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.SystemAppletProxy;
|
||||
using Ryujinx.HLE.HOS.SystemState;
|
||||
using Ryujinx.HLE.Loaders.Processes;
|
||||
using Ryujinx.Horizon.Sdk.Applet;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Applets
|
||||
{
|
||||
internal class RealApplet : IRealApplet
|
||||
{
|
||||
// 0x02 010000000000100C OverlayApplet (overlayDisp)
|
||||
// 0x03 0100000000001000 SystemAppletMenu (qlaunch)
|
||||
// 0x04 0100000000001012 SystemApplication (starter)
|
||||
// 0x0A 0100000000001001 LibraryAppletAuth (auth)
|
||||
// 0x0B 0100000000001002 LibraryAppletCabinet (cabinet)
|
||||
// 0x0C 0100000000001003 LibraryAppletController (controller)
|
||||
// 0x0D 0100000000001004 LibraryAppletDataErase (dataErase)
|
||||
// 0x0E 0100000000001005 LibraryAppletError (error)
|
||||
// 0x0F 0100000000001006 LibraryAppletNetConnect (netConnect)
|
||||
// 0x10 0100000000001007 LibraryAppletPlayerSelect (playerSelect)
|
||||
// 0x11 0100000000001008 LibraryAppletSwkbd (swkbd)
|
||||
// 0x12 0100000000001009 LibraryAppletMiiEdit (miiEdit)
|
||||
// 0x13 010000000000100A LibraryAppletWeb (web)
|
||||
// 0x14 010000000000100B LibraryAppletShop (shop)
|
||||
// 0x15 010000000000100D LibraryAppletPhotoViewer (photoViewer)
|
||||
// 0x16 010000000000100E LibraryAppletSet (set)
|
||||
// 0x17 010000000000100F LibraryAppletOfflineWeb (offlineWeb)
|
||||
// 0x18 0100000000001010 LibraryAppletLoginShare (loginShare)
|
||||
// 0x19 0100000000001011 LibraryAppletWifiWebAuth (wifiWebAuth)
|
||||
// 0x1A 0100000000001013 LibraryAppletMyPage (myPage)
|
||||
// 0x1B 010000000000101A LibraryAppletGift (gift)
|
||||
// 0x1C 010000000000101C LibraryAppletUserMigration (userMigration)
|
||||
// 0x1D 010000000000101D [9.0.0+] LibraryAppletPreomiaSys (EncounterSys)
|
||||
// 0x1E 0100000000001020 [9.0.0+] LibraryAppletStory (story)
|
||||
// 0x1F 010070000E3C0000 [9.0.0+] LibraryAppletPreomiaUsr (EncounterUsr)
|
||||
// 0x20 010086000E49C000 [9.0.0+] LibraryAppletPreomiaUsrDummy (EncounterUsrDummy)
|
||||
// 0x21 0100000000001038 [10.0.0+] LibraryAppletSample (sample)
|
||||
// 0x22 0100000000001007 [13.0.0+] LibraryAppletPromoteQualification (playerSelect)
|
||||
// 0x32 010000000000100F [17.0.0+] LibraryAppletOfflineWeb (offlineWeb)
|
||||
// 0x33 010000000000100F [17.0.0+] LibraryAppletOfflineWeb (offlineWeb)
|
||||
// 0x35 [17.0.0+] 0100000000001010 ([16.0.0-16.1.0] 0100000000001042) [17.0.0+] LibraryAppletLoginShare (loginShare) ([16.0.0-16.1.0] )
|
||||
// 0x36 [17.0.0+] 0100000000001010 ([16.0.0-16.1.0] 0100000000001042) [17.0.0+] LibraryAppletLoginShare (loginShare) ([16.0.0-16.1.0] )
|
||||
// 0x37 [17.0.0+] 0100000000001010 ([16.0.0-16.1.0] 0100000000001042) [17.0.0+] LibraryAppletLoginShare (loginShare) ([16.0.0-16.1.0] )
|
||||
private static readonly Dictionary<RealAppletId, ulong> _appletTitles = new Dictionary<RealAppletId, ulong>
|
||||
{
|
||||
{ RealAppletId.SystemAppletMenu, 0x0100000000001000 },
|
||||
{ RealAppletId.LibraryAppletAuth, 0x0100000000001001 },
|
||||
{ RealAppletId.LibraryAppletCabinet, 0x0100000000001002 },
|
||||
{ RealAppletId.LibraryAppletController, 0x0100000000001003 },
|
||||
{ RealAppletId.LibraryAppletDataErase, 0x0100000000001004 },
|
||||
{ RealAppletId.LibraryAppletError, 0x0100000000001005 },
|
||||
{ RealAppletId.LibraryAppletNetConnect, 0x0100000000001006 },
|
||||
{ RealAppletId.LibraryAppletPlayerSelect, 0x0100000000001007 },
|
||||
{ RealAppletId.LibraryAppletSwkbd, 0x0100000000001008 },
|
||||
{ RealAppletId.LibraryAppletMiiEdit, 0x0100000000001009 },
|
||||
{ RealAppletId.LibraryAppletWeb, 0x010000000000100A },
|
||||
{ RealAppletId.LibraryAppletShop, 0x010000000000100B },
|
||||
{ RealAppletId.OverlayApplet, 0x010000000000100C },
|
||||
{ RealAppletId.LibraryAppletPhotoViewer, 0x010000000000100D },
|
||||
{ RealAppletId.LibraryAppletSet, 0x010000000000100E },
|
||||
{ RealAppletId.LibraryAppletOfflineWeb, 0x010000000000100F },
|
||||
{ RealAppletId.LibraryAppletLoginShare, 0x0100000000001010 },
|
||||
{ RealAppletId.LibraryAppletWifiWebAuth, 0x0100000000001011 },
|
||||
{ RealAppletId.SystemApplication, 0x0100000000001012 },
|
||||
{ RealAppletId.LibraryAppletMyPage, 0x0100000000001013 },
|
||||
{ RealAppletId.LibraryAppletGift, 0x010000000000101A },
|
||||
{ RealAppletId.LibraryAppletUserMigration, 0x010000000000101C },
|
||||
{ RealAppletId.LibraryAppletPreomiaSys, 0x010000000000101D },
|
||||
{ RealAppletId.LibraryAppletStory, 0x0100000000001020 },
|
||||
{ RealAppletId.LibraryAppletPreomiaUsr, 0x010070000E3C0000 },
|
||||
{ RealAppletId.LibraryAppletPreomiaUsrDummy,0x010086000E49C000 },
|
||||
{ RealAppletId.LibraryAppletSample, 0x0100000000001038 },
|
||||
{ RealAppletId.LibraryAppletPromoteQualification, 0x0100000000001007 },
|
||||
{ RealAppletId.LibraryAppletOfflineWebFw17, 0x0100000000001010 },
|
||||
{ RealAppletId.LibraryAppletOfflineWeb2Fw17, 0x0100000000001010 },
|
||||
};
|
||||
|
||||
internal static RealAppletId GetAppletIdFromProgramId(ulong programId)
|
||||
{
|
||||
foreach (var applet in _appletTitles)
|
||||
{
|
||||
if (applet.Value == programId)
|
||||
{
|
||||
return applet.Key;
|
||||
}
|
||||
}
|
||||
|
||||
return RealAppletId.Application;
|
||||
}
|
||||
|
||||
internal static ulong GetProgramIdFromAppletId(RealAppletId appletId)
|
||||
{
|
||||
return _appletTitles[appletId];
|
||||
}
|
||||
|
||||
private readonly object _lock = new();
|
||||
public object Lock => _lock;
|
||||
|
||||
private readonly Horizon _system;
|
||||
internal RealAppletId AppletId { get; private set; }
|
||||
internal LibraryAppletMode LibraryAppletMode { get; private set; }
|
||||
internal ulong AppletResourceUserId { get; private set; }
|
||||
|
||||
internal AppletChannel InChannel { get; private set; }
|
||||
internal AppletChannel OutChannel { get; private set; }
|
||||
internal AppletChannel InteractiveInChannel { get; private set; }
|
||||
internal AppletChannel InteractiveOutChannel { get; private set; }
|
||||
internal AppletChannel ContextChannel { get; private set; }
|
||||
internal RealApplet CallerApplet = null;
|
||||
internal LinkedList<RealApplet> ChildApplets = new();
|
||||
internal bool IsCompleted = false;
|
||||
|
||||
internal bool IsActivityRunnable = false;
|
||||
internal bool IsInteractable = true;
|
||||
internal bool WindowVisible = true;
|
||||
internal bool ExitLocked = false;
|
||||
|
||||
internal AppletStateMgr AppletState { get; private set; }
|
||||
public event EventHandler AppletStateChanged;
|
||||
internal AppletProcessLaunchReason LaunchReason = default;
|
||||
internal ResultCode TerminateResult = ResultCode.Success;
|
||||
|
||||
internal KProcess ProcessHandle;
|
||||
internal bool IsProcessRunning;
|
||||
|
||||
public RealApplet(ulong pid, bool isApplication, Horizon system)
|
||||
{
|
||||
_system = system;
|
||||
AppletState = new AppletStateMgr(system, isApplication);
|
||||
ProcessHandle = _system.KernelContext.Processes[pid];
|
||||
AppletResourceUserId = ProcessHandle.Pid;
|
||||
AppletId = GetAppletIdFromProgramId(ProcessHandle.TitleId);
|
||||
}
|
||||
|
||||
public void RegisterChild(RealApplet applet)
|
||||
{
|
||||
if (applet == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (applet == this)
|
||||
{
|
||||
throw new InvalidOperationException("Cannot register self as child.");
|
||||
}
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
ChildApplets.AddLast(applet);
|
||||
}
|
||||
}
|
||||
|
||||
public ResultCode Start(AppletChannel inChannel,
|
||||
AppletChannel outChannel,
|
||||
AppletChannel interactiveInChannel,
|
||||
AppletChannel interactiveOutChannel,
|
||||
AppletChannel contextChannel)
|
||||
{
|
||||
InChannel = inChannel;
|
||||
OutChannel = outChannel;
|
||||
InteractiveInChannel = interactiveInChannel;
|
||||
InteractiveOutChannel = interactiveOutChannel;
|
||||
ContextChannel = contextChannel;
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
public ResultCode StartApplication()
|
||||
{
|
||||
InChannel = new AppletChannel();
|
||||
OutChannel = new AppletChannel();
|
||||
InteractiveInChannel = new AppletChannel();
|
||||
InteractiveOutChannel = new AppletChannel();
|
||||
ContextChannel = new AppletChannel();
|
||||
|
||||
// ProcessHandle = _system.KernelContext.Processes[pid];
|
||||
// AppletResourceUserId = ProcessHandle.Pid;
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
public ResultCode GetResult()
|
||||
{
|
||||
return TerminateResult;
|
||||
}
|
||||
|
||||
public void InvokeAppletStateChanged()
|
||||
{
|
||||
AppletStateChanged?.Invoke(this, null);
|
||||
}
|
||||
|
||||
internal void UpdateSuspensionStateLocked(bool forceMessage)
|
||||
{
|
||||
// Remove any forced resumption.
|
||||
AppletState.RemoveForceResumeIfPossible();
|
||||
|
||||
// Check if we're runnable.
|
||||
bool currActivityRunnable = AppletState.IsRunnable();
|
||||
bool prevActivityRunnable = IsActivityRunnable;
|
||||
bool wasChanged = currActivityRunnable != prevActivityRunnable;
|
||||
|
||||
if (wasChanged)
|
||||
{
|
||||
if (currActivityRunnable)
|
||||
{
|
||||
ProcessHandle.SetActivity(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
ProcessHandle.SetActivity(true);
|
||||
AppletState.RequestResumeNotification();
|
||||
}
|
||||
|
||||
IsActivityRunnable = currActivityRunnable;
|
||||
}
|
||||
|
||||
if (AppletState.ForcedSuspend)
|
||||
{
|
||||
// TODO: why is this allowed?
|
||||
return;
|
||||
}
|
||||
|
||||
// Signal if the focus state was changed or the process state was changed.
|
||||
if (AppletState.UpdateRequestedFocusState() || wasChanged || forceMessage)
|
||||
{
|
||||
AppletState.SignalEventIfNeeded();
|
||||
}
|
||||
}
|
||||
|
||||
internal void SetInteractibleLocked(bool interactible)
|
||||
{
|
||||
if (IsInteractable == interactible)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
IsInteractable = interactible;
|
||||
|
||||
// _hidRegistration.EnableAppletToGetInput(interactible && !lifecycle_manager.GetExitRequested());
|
||||
}
|
||||
|
||||
internal void OnProcessTerminatedLocked()
|
||||
{
|
||||
IsProcessRunning = false;
|
||||
ProcessHandle = null;
|
||||
InvokeAppletStateChanged();
|
||||
}
|
||||
}
|
||||
}
|
10
src/Ryujinx.HLE/HOS/Applets/Types/ActivityState.cs
Normal file
10
src/Ryujinx.HLE/HOS/Applets/Types/ActivityState.cs
Normal file
@ -0,0 +1,10 @@
|
||||
namespace Ryujinx.HLE.HOS.Applets.Types
|
||||
{
|
||||
public enum ActivityState
|
||||
{
|
||||
ForegroundVisible = 0,
|
||||
ForegroundObscured = 1,
|
||||
BackgroundVisible = 2,
|
||||
BackgroundObscured = 3,
|
||||
}
|
||||
}
|
9
src/Ryujinx.HLE/HOS/Applets/Types/SuspendMode.cs
Normal file
9
src/Ryujinx.HLE/HOS/Applets/Types/SuspendMode.cs
Normal file
@ -0,0 +1,9 @@
|
||||
namespace Ryujinx.HLE.HOS.Applets.Types
|
||||
{
|
||||
public enum SuspendMode
|
||||
{
|
||||
NoOverride = 0,
|
||||
ForceResume = 1,
|
||||
ForceSuspend = 2,
|
||||
}
|
||||
}
|
8
src/Ryujinx.HLE/HOS/Applets/Types/UserDataTag.cs
Normal file
8
src/Ryujinx.HLE/HOS/Applets/Types/UserDataTag.cs
Normal file
@ -0,0 +1,8 @@
|
||||
namespace Ryujinx.HLE.HOS.Applets.Types
|
||||
{
|
||||
enum UserDataTag : uint
|
||||
{
|
||||
WakeupEvent,
|
||||
AppletProcess,
|
||||
}
|
||||
}
|
524
src/Ryujinx.HLE/HOS/Applets/WindowSystem.cs
Normal file
524
src/Ryujinx.HLE/HOS/Applets/WindowSystem.cs
Normal file
@ -0,0 +1,524 @@
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.HLE.HOS.Kernel.Process;
|
||||
using Ryujinx.HLE.HOS.Services.Am.AppletAE;
|
||||
using Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.SystemAppletProxy;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Ryujinx.Horizon.Sdk.Applet;
|
||||
using Ryujinx.Common;
|
||||
using Ryujinx.HLE.HOS.Applets.Types;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Applets
|
||||
{
|
||||
public class WindowSystem
|
||||
{
|
||||
private Horizon _system;
|
||||
private readonly object _lock = new();
|
||||
EventObserver _eventObserver = null;
|
||||
|
||||
// Foreground roots
|
||||
RealApplet _homeMenu = null;
|
||||
RealApplet _overlayDisp = null;
|
||||
RealApplet _application = null;
|
||||
|
||||
// Home menu state
|
||||
bool _homeMenuForegroundLocked = false;
|
||||
RealApplet _foregroundRequestedApplet = null;
|
||||
|
||||
|
||||
// aruid -> applet map
|
||||
Dictionary<ulong, RealApplet> _applets = new();
|
||||
List<RealApplet> _rootApplets = new();
|
||||
|
||||
internal ButtonPressTracker ButtonPressTracker { get; }
|
||||
|
||||
public WindowSystem(Horizon system)
|
||||
{
|
||||
_system = system;
|
||||
ButtonPressTracker = new ButtonPressTracker(system);
|
||||
}
|
||||
|
||||
void Dispose()
|
||||
{
|
||||
// SetWindowSystem(null);
|
||||
}
|
||||
|
||||
internal void SetEventObserver(EventObserver eventObserver)
|
||||
{
|
||||
_eventObserver = eventObserver;
|
||||
// SetWindowSystem(this);
|
||||
}
|
||||
|
||||
internal void Update()
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
PruneTerminatedAppletsLocked();
|
||||
|
||||
if (LockHomeMenuIntoForegroundLocked())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_foregroundRequestedApplet == null && _rootApplets.Count != 0)
|
||||
{
|
||||
_foregroundRequestedApplet = _rootApplets.Last();
|
||||
}
|
||||
|
||||
foreach (var applet in _rootApplets)
|
||||
{
|
||||
UpdateAppletStateLocked(applet, _foregroundRequestedApplet == applet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal RealApplet TrackProcess(ulong pid, ulong callerPid, bool isApplication)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
if (_applets.TryGetValue(pid, out var applet))
|
||||
{
|
||||
Logger.Info?.Print(LogClass.ServiceAm, $"TrackProcess() called on existing applet {pid} - caller {callerPid}");
|
||||
return applet;
|
||||
}
|
||||
|
||||
Logger.Info?.Print(LogClass.ServiceAm, $"Tracking process {pid} as {(isApplication ? "application" : "applet")} - caller {callerPid}");
|
||||
if (_system.KernelContext.Processes.TryGetValue(pid, out var _process))
|
||||
{
|
||||
applet = new RealApplet(pid, isApplication, _system);
|
||||
|
||||
if (callerPid == 0)
|
||||
{
|
||||
_rootApplets.Add(applet);
|
||||
}
|
||||
else
|
||||
{
|
||||
var callerApplet = _applets[callerPid];
|
||||
applet.CallerApplet = callerApplet;
|
||||
callerApplet.RegisterChild(applet);
|
||||
}
|
||||
|
||||
TrackApplet(applet, isApplication);
|
||||
return applet;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void TrackApplet(RealApplet applet, bool isApplication)
|
||||
{
|
||||
if (_applets.ContainsKey(applet.AppletResourceUserId))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (applet.AppletId == RealAppletId.SystemAppletMenu)
|
||||
{
|
||||
_homeMenu = applet;
|
||||
_foregroundRequestedApplet = applet;
|
||||
}
|
||||
else if (applet.AppletId == RealAppletId.OverlayApplet)
|
||||
{
|
||||
_overlayDisp = applet;
|
||||
}
|
||||
else if (isApplication)
|
||||
{
|
||||
_application = applet;
|
||||
}
|
||||
|
||||
_applets[applet.AppletResourceUserId] = applet;
|
||||
_eventObserver.TrackAppletProcess(applet);
|
||||
|
||||
if (_applets.Count == 1 || applet.AppletId == RealAppletId.SystemAppletMenu || applet.AppletId == RealAppletId.OverlayApplet)
|
||||
{
|
||||
SetupFirstApplet(applet);
|
||||
}
|
||||
|
||||
// _foregroundRequestedApplet = applet;
|
||||
// applet.AppletState.SetFocusState(FocusState.InFocus);
|
||||
|
||||
_eventObserver.RequestUpdate();
|
||||
}
|
||||
|
||||
private void SetupFirstApplet(RealApplet applet)
|
||||
{
|
||||
if (applet.AppletId == RealAppletId.SystemAppletMenu)
|
||||
{
|
||||
//applet.AppletState.SetFocusHandlingMode(false);
|
||||
applet.AppletState.SetOutOfFocusSuspendingEnabled(false);
|
||||
RequestHomeMenuToGetForeground();
|
||||
}
|
||||
else if (applet.AppletId == RealAppletId.OverlayApplet)
|
||||
{
|
||||
applet.AppletState.SetOutOfFocusSuspendingEnabled(false);
|
||||
applet.AppletState.SetFocusState(FocusState.OutOfFocus);
|
||||
}
|
||||
else
|
||||
{
|
||||
applet.AppletState.SetFocusState(FocusState.InFocus);
|
||||
_foregroundRequestedApplet = applet;
|
||||
RequestApplicationToGetForeground();
|
||||
}
|
||||
|
||||
applet.UpdateSuspensionStateLocked(true);
|
||||
}
|
||||
|
||||
internal RealApplet GetByAruId(ulong aruid)
|
||||
{
|
||||
// lock (_lock)
|
||||
{
|
||||
if (_applets.TryGetValue(aruid, out RealApplet applet))
|
||||
{
|
||||
return applet;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
internal RealApplet GetMainApplet()
|
||||
{
|
||||
// lock (_lock)
|
||||
{
|
||||
if (_application != null)
|
||||
{
|
||||
if (_applets.TryGetValue(_application.AppletResourceUserId, out RealApplet applet))
|
||||
{
|
||||
return applet;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
internal void RequestHomeMenuToGetForeground()
|
||||
{
|
||||
// lock (_lock)
|
||||
{
|
||||
_foregroundRequestedApplet = _homeMenu;
|
||||
}
|
||||
|
||||
_eventObserver.RequestUpdate();
|
||||
}
|
||||
|
||||
internal void RequestApplicationToGetForeground()
|
||||
{
|
||||
// lock (_lock)
|
||||
{
|
||||
_foregroundRequestedApplet = _application;
|
||||
}
|
||||
|
||||
_eventObserver.RequestUpdate();
|
||||
}
|
||||
|
||||
internal void RequestLockHomeMenuIntoForeground()
|
||||
{
|
||||
// lock (_lock)
|
||||
{
|
||||
_homeMenuForegroundLocked = true;
|
||||
}
|
||||
|
||||
_eventObserver.RequestUpdate();
|
||||
}
|
||||
|
||||
internal void RequestUnlockHomeMenuFromForeground()
|
||||
{
|
||||
// lock (_lock)
|
||||
{
|
||||
_homeMenuForegroundLocked = false;
|
||||
}
|
||||
|
||||
_eventObserver.RequestUpdate();
|
||||
}
|
||||
|
||||
internal void RequestAppletVisibilityState(RealApplet applet, bool isVisible)
|
||||
{
|
||||
lock (applet.Lock)
|
||||
{
|
||||
applet.WindowVisible = isVisible;
|
||||
}
|
||||
|
||||
_eventObserver.RequestUpdate();
|
||||
}
|
||||
|
||||
internal void OnOperationModeChanged()
|
||||
{
|
||||
// lock (_lock)
|
||||
{
|
||||
foreach (var (aruid, applet) in _applets)
|
||||
{
|
||||
lock (applet.Lock)
|
||||
{
|
||||
applet.AppletState.OnOperationAndPerformanceModeChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void OnExitRequested()
|
||||
{
|
||||
// lock (_lock)
|
||||
{
|
||||
foreach (var (aruid, applet) in _applets)
|
||||
{
|
||||
lock (applet.Lock)
|
||||
{
|
||||
applet.AppletState.OnExitRequested();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal void OnSystemButtonPress(SystemButtonType type)
|
||||
{
|
||||
// lock (_lock)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case SystemButtonType.PerformHomeButtonShortPressing:
|
||||
SendButtonAppletMessageLocked(AppletMessage.DetectShortPressingHomeButton);
|
||||
break;
|
||||
case SystemButtonType.PerformHomeButtonLongPressing:
|
||||
SendButtonAppletMessageLocked(AppletMessage.DetectLongPressingHomeButton);
|
||||
break;
|
||||
case SystemButtonType.PerformCaptureButtonShortPressing:
|
||||
SendButtonAppletMessageLocked(AppletMessage.DetectShortPressingCaptureButton);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SendButtonAppletMessageLocked(AppletMessage message)
|
||||
{
|
||||
if (_homeMenu != null)
|
||||
{
|
||||
lock (_homeMenu.Lock)
|
||||
{
|
||||
_homeMenu.AppletState.PushUnorderedMessage(message);
|
||||
}
|
||||
}
|
||||
|
||||
if (_overlayDisp != null)
|
||||
{
|
||||
lock (_overlayDisp.Lock)
|
||||
{
|
||||
_overlayDisp.AppletState.PushUnorderedMessage(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void PruneTerminatedAppletsLocked()
|
||||
{
|
||||
foreach (var (aruid, applet) in _applets)
|
||||
{
|
||||
lock (applet.Lock)
|
||||
{
|
||||
if (applet.ProcessHandle.State != ProcessState.Exited)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (applet.ChildApplets.Count != 0)
|
||||
{
|
||||
TerminateChildAppletsLocked(applet);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (applet.CallerApplet != null)
|
||||
{
|
||||
applet.CallerApplet.ChildApplets.Remove(applet);
|
||||
applet.CallerApplet = null;
|
||||
}
|
||||
|
||||
if (applet == _foregroundRequestedApplet)
|
||||
{
|
||||
_foregroundRequestedApplet = null;
|
||||
}
|
||||
|
||||
if (applet == _homeMenu)
|
||||
{
|
||||
_homeMenu = null;
|
||||
_foregroundRequestedApplet = null;
|
||||
}
|
||||
|
||||
if (applet == _application)
|
||||
{
|
||||
_application = null;
|
||||
_foregroundRequestedApplet = null;
|
||||
|
||||
if (_homeMenu != null)
|
||||
{
|
||||
_homeMenu.AppletState.PushUnorderedMessage(AppletMessage.ApplicationExited);
|
||||
}
|
||||
}
|
||||
|
||||
applet.OnProcessTerminatedLocked();
|
||||
|
||||
_eventObserver.RequestUpdate();
|
||||
_applets.Remove(aruid);
|
||||
_rootApplets.Remove(applet);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool LockHomeMenuIntoForegroundLocked()
|
||||
{
|
||||
// If the home menu is not locked into the foreground, then there's nothing to do.
|
||||
if (_homeMenu == null || !_homeMenuForegroundLocked)
|
||||
{
|
||||
_homeMenuForegroundLocked = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
lock (_homeMenu.Lock)
|
||||
{
|
||||
TerminateChildAppletsLocked(_homeMenu);
|
||||
|
||||
if (_homeMenu.ChildApplets.Count == 0)
|
||||
{
|
||||
_homeMenu.WindowVisible = true;
|
||||
_foregroundRequestedApplet = _homeMenu;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void TerminateChildAppletsLocked(RealApplet parent)
|
||||
{
|
||||
foreach (var child in parent.ChildApplets)
|
||||
{
|
||||
if (child.ProcessHandle.State != ProcessState.Exited)
|
||||
{
|
||||
child.ProcessHandle.Terminate();
|
||||
child.TerminateResult = (ResultCode)Services.Am.ResultCode.LibraryAppletTerminated;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateAppletStateLocked(RealApplet applet, bool isForeground)
|
||||
{
|
||||
if (applet == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
lock (applet.Lock)
|
||||
{
|
||||
var inheritedForeground = applet.IsProcessRunning && isForeground;
|
||||
var visibleState = inheritedForeground ? ActivityState.ForegroundVisible : ActivityState.BackgroundVisible;
|
||||
var obscuredState = inheritedForeground ? ActivityState.ForegroundObscured : ActivityState.BackgroundObscured;
|
||||
|
||||
var hasObscuringChildApplets = applet.ChildApplets.Any(child =>
|
||||
{
|
||||
lock (child.Lock)
|
||||
{
|
||||
var mode = child.LibraryAppletMode;
|
||||
if (child.IsProcessRunning && child.WindowVisible &&
|
||||
(mode == LibraryAppletMode.AllForeground || mode == LibraryAppletMode.AllForegroundInitiallyHidden))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
// TODO: Update visibility state
|
||||
|
||||
applet.SetInteractibleLocked(isForeground && applet.WindowVisible);
|
||||
|
||||
var isObscured = hasObscuringChildApplets || !applet.WindowVisible;
|
||||
var state = applet.AppletState.ActivityState;
|
||||
|
||||
if (isObscured && state != obscuredState)
|
||||
{
|
||||
applet.AppletState.ActivityState = obscuredState;
|
||||
applet.UpdateSuspensionStateLocked(true);
|
||||
}
|
||||
else if (!isObscured && state != visibleState)
|
||||
{
|
||||
applet.AppletState.ActivityState = visibleState;
|
||||
applet.UpdateSuspensionStateLocked(true);
|
||||
}
|
||||
|
||||
Logger.Info?.Print(LogClass.ServiceAm, $"Updating applet state for {applet.AppletId}: visible={applet.WindowVisible}, foreground={isForeground}, obscured={isObscured}, reqFState={applet.AppletState.RequestedFocusState}, ackFState={applet.AppletState.AcknowledgedFocusState}, runnable={applet.AppletState.IsRunnable()}");
|
||||
|
||||
|
||||
// Recurse into child applets
|
||||
foreach (var child in applet.ChildApplets)
|
||||
{
|
||||
UpdateAppletStateLocked(child, isForeground);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class ButtonPressTracker
|
||||
{
|
||||
private Horizon _system;
|
||||
bool _homeButtonPressed = false;
|
||||
long _homeButtonPressedTimeStart = 0;
|
||||
bool _captureButtonPressed = false;
|
||||
long _captureButtonPressedTime = 0;
|
||||
|
||||
public ButtonPressTracker(Horizon system)
|
||||
{
|
||||
_system = system;
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
// TODO: properly implement this
|
||||
ref var shared = ref _system.Device.Hid.SharedMemory;
|
||||
bool homeDown = shared.HomeButton.GetCurrentEntryRef().Buttons != 0;
|
||||
bool captureDown = shared.CaptureButton.GetCurrentEntryRef().Buttons != 0;
|
||||
|
||||
int homeButtonPressDuration = 0;
|
||||
int captureButtonPressDuration = 0;
|
||||
|
||||
if (_homeButtonPressed && !homeDown)
|
||||
{
|
||||
_homeButtonPressed = false;
|
||||
homeButtonPressDuration = (int)(PerformanceCounter.ElapsedMilliseconds - _homeButtonPressedTimeStart);
|
||||
}
|
||||
else if (!_homeButtonPressed && homeDown)
|
||||
{
|
||||
_homeButtonPressed = true;
|
||||
_homeButtonPressedTimeStart = PerformanceCounter.ElapsedMilliseconds;
|
||||
}
|
||||
|
||||
if (_captureButtonPressed && !captureDown)
|
||||
{
|
||||
_captureButtonPressed = false;
|
||||
captureButtonPressDuration = (int)(PerformanceCounter.ElapsedMilliseconds - _captureButtonPressedTime);
|
||||
}
|
||||
else if (!_captureButtonPressed && captureDown)
|
||||
{
|
||||
_captureButtonPressed = true;
|
||||
_captureButtonPressedTime = PerformanceCounter.ElapsedMilliseconds;
|
||||
}
|
||||
|
||||
if (homeButtonPressDuration > 500)
|
||||
{
|
||||
_system.WindowSystem.OnSystemButtonPress(SystemButtonType.PerformHomeButtonLongPressing);
|
||||
}
|
||||
else if (homeButtonPressDuration > 20)
|
||||
{
|
||||
_system.WindowSystem.OnSystemButtonPress(SystemButtonType.PerformHomeButtonShortPressing);
|
||||
}
|
||||
|
||||
if (captureButtonPressDuration > 500)
|
||||
{
|
||||
_system.WindowSystem.OnSystemButtonPress(SystemButtonType.PerformCaptureButtonLongPressing);
|
||||
}
|
||||
else if (captureButtonPressDuration > 20)
|
||||
{
|
||||
_system.WindowSystem.OnSystemButtonPress(SystemButtonType.PerformCaptureButtonShortPressing);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -4,8 +4,10 @@ using LibHac.Fs;
|
||||
using LibHac.Fs.Shim;
|
||||
using LibHac.FsSystem;
|
||||
using LibHac.Tools.FsSystem;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Cpu;
|
||||
using Ryujinx.HLE.FileSystem;
|
||||
using Ryujinx.HLE.HOS.Applets;
|
||||
using Ryujinx.HLE.HOS.Kernel;
|
||||
using Ryujinx.HLE.HOS.Kernel.Memory;
|
||||
using Ryujinx.HLE.HOS.Kernel.Process;
|
||||
@ -61,7 +63,37 @@ namespace Ryujinx.HLE.HOS
|
||||
|
||||
internal PerformanceState PerformanceState { get; private set; }
|
||||
|
||||
internal AppletStateMgr AppletState { get; private set; }
|
||||
internal AppletStateMgr IntialAppletState { get; private set; }
|
||||
|
||||
internal AppletStateMgr AppletState
|
||||
{
|
||||
get
|
||||
{
|
||||
ulong processId = 0;
|
||||
if (Device?.Processes?.ActiveApplication != null)
|
||||
{
|
||||
processId = Device.Processes.ActiveApplication.ProcessId;
|
||||
}
|
||||
if (WindowSystem?.GetByAruId(processId) != null)
|
||||
{
|
||||
Logger.Info?.Print(LogClass.Application, "Real applet instance found");
|
||||
return WindowSystem.GetByAruId(processId).AppletState;
|
||||
}
|
||||
|
||||
return IntialAppletState;
|
||||
}
|
||||
set
|
||||
{
|
||||
if (value != null)
|
||||
{
|
||||
IntialAppletState = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal WindowSystem WindowSystem { get; private set; }
|
||||
|
||||
internal EventObserver EventObserver { get; private set; }
|
||||
|
||||
internal List<NfpDevice> NfpDevices { get; private set; }
|
||||
|
||||
@ -172,7 +204,10 @@ namespace Ryujinx.HLE.HOS
|
||||
|
||||
AppletCaptureBufferTransfer = new KTransferMemory(KernelContext, appletCaptureBufferStorage);
|
||||
|
||||
AppletState = new AppletStateMgr(this);
|
||||
AppletState = new AppletStateMgr(this, true);
|
||||
|
||||
WindowSystem = new WindowSystem(this);
|
||||
EventObserver = new EventObserver(this, WindowSystem);
|
||||
|
||||
AppletState.SetFocus(true);
|
||||
|
||||
@ -317,9 +352,7 @@ namespace Ryujinx.HLE.HOS
|
||||
State.DockedMode = newState;
|
||||
PerformanceState.PerformanceMode = State.DockedMode ? PerformanceMode.Boost : PerformanceMode.Default;
|
||||
|
||||
AppletState.Messages.Enqueue(AppletMessage.OperationModeChanged);
|
||||
AppletState.Messages.Enqueue(AppletMessage.PerformanceModeChanged);
|
||||
AppletState.MessageEvent.ReadableEvent.Signal();
|
||||
WindowSystem.OnOperationModeChanged();
|
||||
|
||||
SignalDisplayResolutionChange();
|
||||
|
||||
@ -334,8 +367,8 @@ namespace Ryujinx.HLE.HOS
|
||||
|
||||
public void SimulateWakeUpMessage()
|
||||
{
|
||||
AppletState.Messages.Enqueue(AppletMessage.Resume);
|
||||
AppletState.MessageEvent.ReadableEvent.Signal();
|
||||
// AppletState.Messages.Enqueue(AppletMessage.Resume);
|
||||
// AppletState.MessageEvent.ReadableEvent.Signal();
|
||||
}
|
||||
|
||||
public void ScanAmiibo(int nfpDeviceId, string amiiboId, bool useRandomUuid)
|
||||
|
@ -1175,5 +1175,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||
{
|
||||
return Capabilities.IsSvcPermitted(svcId);
|
||||
}
|
||||
|
||||
public bool IsRunning()
|
||||
{
|
||||
return State == ProcessState.Started || State == ProcessState.Attached || State == ProcessState.DebugSuspended;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -87,7 +87,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService
|
||||
// GetApplicationCreator() -> object<nn::am::service::IApplicationCreator>
|
||||
public ResultCode GetApplicationCreator(ServiceCtx context)
|
||||
{
|
||||
MakeObject(context, new IApplicationCreator());
|
||||
MakeObject(context, new IApplicationCreator(context.Device.Processes.ActiveApplication.ProcessId));
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
@ -0,0 +1,107 @@
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.HLE.HOS.Ipc;
|
||||
using Ryujinx.HLE.HOS.Kernel;
|
||||
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||
using Ryujinx.HLE.HOS.Services.Account.Acc;
|
||||
using Ryujinx.HLE.Loaders.Processes;
|
||||
using Ryujinx.Horizon.Common;
|
||||
using System;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.SystemAppletProxy
|
||||
{
|
||||
class IApplicationAccessor : IpcService
|
||||
{
|
||||
private readonly KernelContext _kernelContext;
|
||||
private readonly ulong _callerPid;
|
||||
private readonly ulong _applicationId;
|
||||
private readonly string _contentPath;
|
||||
|
||||
private readonly KEvent _stateChangedEvent;
|
||||
private int _stateChangedEventHandle;
|
||||
|
||||
public IApplicationAccessor(ulong pid, ulong applicationId, string contentPath, Horizon system)
|
||||
{
|
||||
_callerPid = pid;
|
||||
_kernelContext = system.KernelContext;
|
||||
_applicationId = applicationId;
|
||||
_contentPath = contentPath;
|
||||
|
||||
_stateChangedEvent = new KEvent(system.KernelContext);
|
||||
}
|
||||
|
||||
|
||||
[CommandCmif(0)]
|
||||
// GetAppletStateChangedEvent() -> handle<copy>
|
||||
public ResultCode GetAppletStateChangedEvent(ServiceCtx context)
|
||||
{
|
||||
if (_stateChangedEventHandle == 0)
|
||||
{
|
||||
if (context.Process.HandleTable.GenerateHandle(_stateChangedEvent.ReadableEvent, out _stateChangedEventHandle) != Result.Success)
|
||||
{
|
||||
throw new InvalidOperationException("Out of handles!");
|
||||
}
|
||||
}
|
||||
|
||||
context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_stateChangedEventHandle);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
|
||||
[CommandCmif(10)]
|
||||
// Start()
|
||||
public ResultCode Start(ServiceCtx context)
|
||||
{
|
||||
_stateChangedEvent.ReadableEvent.Signal();
|
||||
|
||||
Logger.Info?.Print(LogClass.ServiceAm, $"Application 0x{_applicationId:X}:{_contentPath} start requested.");
|
||||
ProcessResult processResult = null;
|
||||
bool isApplet = false;
|
||||
if (_contentPath.EndsWith("nsp"))
|
||||
{
|
||||
context.Device.Processes.LoadNsp(_contentPath,_applicationId, out processResult);
|
||||
}
|
||||
else if (_contentPath.EndsWith("xci"))
|
||||
{
|
||||
context.Device.Processes.LoadXci(_contentPath,_applicationId, out processResult);
|
||||
}
|
||||
else
|
||||
{
|
||||
context.Device.Processes.LoadNca(_contentPath, out processResult);
|
||||
isApplet = true;
|
||||
}
|
||||
|
||||
var applet = context.Device.System.WindowSystem.TrackProcess(processResult.ProcessId, 0, !isApplet);
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(101)]
|
||||
// RequestForApplicationToGetForeground()
|
||||
public ResultCode RequestForApplicationToGetForeground(ServiceCtx context)
|
||||
{
|
||||
// _stateChangedEvent.ReadableEvent.Signal();
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceAm);
|
||||
context.Device.System.ReturnFocus();
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(121)]
|
||||
// PushLaunchParameter(u32) -> IStorage
|
||||
public ResultCode PushLaunchParameter(ServiceCtx context)
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceAm);
|
||||
MakeObject(context, new IStorage(new byte[0],false));
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(130)]
|
||||
// SetUsers()
|
||||
public ResultCode SetUsers(ServiceCtx context)
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceAm);
|
||||
bool enable = context.RequestData.ReadBoolean();
|
||||
return ResultCode.Success;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,7 +1,54 @@
|
||||
using LibHac.Ncm;
|
||||
using LibHac.Tools.FsSystem.NcaUtils;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.HLE.FileSystem;
|
||||
using Ryujinx.HLE.HOS.Applets;
|
||||
using System.Linq;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.SystemAppletProxy
|
||||
{
|
||||
class IApplicationCreator : IpcService
|
||||
{
|
||||
public IApplicationCreator() { }
|
||||
private readonly ulong _pid;
|
||||
|
||||
public IApplicationCreator(ulong pid)
|
||||
{
|
||||
_pid = pid;
|
||||
}
|
||||
|
||||
[CommandCmif(0)]
|
||||
// CreateApplication(nn::ncm::ApplicationId) -> object<nn::am::service::IApplicationAccessor>
|
||||
public ResultCode CreateApplication(ServiceCtx context)
|
||||
{
|
||||
ulong applicationId = context.RequestData.ReadUInt64();
|
||||
Horizon system = context.Device.System;
|
||||
string contentPath = system.Device.Configuration.Titles.FirstOrDefault(t => t.AppId.Value == applicationId).Path;
|
||||
|
||||
MakeObject(context, new IApplicationAccessor(_pid, applicationId, contentPath, context.Device.System));
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(10)]
|
||||
// CreateSystemApplication(nn::ncm::SystemApplicationId) -> object<nn::am::service::IApplicationAccessor>
|
||||
public ResultCode CreateSystemApplication(ServiceCtx context)
|
||||
{
|
||||
var applicationId = context.RequestData.ReadUInt64();
|
||||
var system = context.Device.System;
|
||||
string contentPath = system.ContentManager.GetInstalledContentPath(applicationId, StorageId.BuiltInSystem, NcaContentType.Program);
|
||||
|
||||
if (contentPath.Length == 0)
|
||||
{
|
||||
return ResultCode.TitleIdNotFound;
|
||||
}
|
||||
|
||||
if (contentPath.StartsWith("@SystemContent"))
|
||||
{
|
||||
contentPath = VirtualFileSystem.SwitchPathToSystemPath(contentPath);
|
||||
}
|
||||
|
||||
MakeObject(context, new IApplicationAccessor(_pid, applicationId, contentPath, context.Device.System));
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -61,27 +61,12 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
|
||||
// ReceiveMessage() -> nn::am::AppletMessage
|
||||
public ResultCode ReceiveMessage(ServiceCtx context)
|
||||
{
|
||||
if (!context.Device.System.AppletState.Messages.TryDequeue(out AppletMessage message))
|
||||
if (!context.Device.System.AppletState.PopMessage(out AppletMessage message))
|
||||
{
|
||||
return ResultCode.NoMessages;
|
||||
}
|
||||
|
||||
KEvent messageEvent = context.Device.System.AppletState.MessageEvent;
|
||||
|
||||
// NOTE: Service checks if current states are different than the stored ones.
|
||||
// Since we don't support any states for now, it's fine to check if there is still messages available.
|
||||
|
||||
if (context.Device.System.AppletState.Messages.IsEmpty)
|
||||
{
|
||||
messageEvent.ReadableEvent.Clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
messageEvent.ReadableEvent.Signal();
|
||||
}
|
||||
|
||||
context.ResponseData.Write((int)message);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
@ -120,7 +105,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
|
||||
// GetCurrentFocusState() -> u8
|
||||
public ResultCode GetCurrentFocusState(ServiceCtx context)
|
||||
{
|
||||
context.ResponseData.Write((byte)context.Device.System.AppletState.FocusState);
|
||||
context.ResponseData.Write((byte)context.Device.System.AppletState.AcknowledgedFocusState);
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
@ -22,6 +22,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
|
||||
public ResultCode RequestToGetForeground(ServiceCtx context)
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceAm);
|
||||
context.Device.System.WindowSystem.RequestApplicationToGetForeground();
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
@ -31,6 +32,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
|
||||
public ResultCode LockForeground(ServiceCtx context)
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceAm);
|
||||
context.Device.System.WindowSystem.RequestLockHomeMenuIntoForeground();
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
@ -39,6 +41,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
|
||||
public ResultCode UnlockForeground(ServiceCtx context)
|
||||
{
|
||||
Logger.Stub?.PrintStub(LogClass.ServiceAm);
|
||||
context.Device.System.WindowSystem.RequestUnlockHomeMenuFromForeground();
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
|
||||
// GetAppletResourceUserId() -> nn::applet::AppletResourceUserId
|
||||
public ResultCode GetAppletResourceUserId(ServiceCtx context)
|
||||
{
|
||||
long appletResourceUserId = context.Device.System.AppletState.AppletResourceUserIds.Add(_pid);
|
||||
ulong appletResourceUserId = _pid;
|
||||
|
||||
context.ResponseData.Write(appletResourceUserId);
|
||||
|
||||
|
@ -4,5 +4,6 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
|
||||
{
|
||||
InFocus = 1,
|
||||
OutOfFocus = 2,
|
||||
Background = 3,
|
||||
}
|
||||
}
|
||||
|
85
src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AppletChannel.cs
Normal file
85
src/Ryujinx.HLE/HOS/Services/Am/AppletAE/AppletChannel.cs
Normal file
@ -0,0 +1,85 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.Services.Am.AppletAE
|
||||
{
|
||||
internal class AppletChannel
|
||||
{
|
||||
private readonly IAppletFifo<byte[]> _data;
|
||||
|
||||
public event EventHandler DataAvailable;
|
||||
|
||||
public AppletChannel()
|
||||
: this(new AppletFifo<byte[]>())
|
||||
{ }
|
||||
|
||||
public AppletChannel(
|
||||
IAppletFifo<byte[]> data)
|
||||
{
|
||||
_data = data;
|
||||
|
||||
_data.DataAvailable += OnDataAvailable;
|
||||
}
|
||||
|
||||
private void OnDataAvailable(object sender, EventArgs e)
|
||||
{
|
||||
DataAvailable?.Invoke(this, null);
|
||||
}
|
||||
|
||||
public void PushData(byte[] item)
|
||||
{
|
||||
if (!this.TryPushData(item))
|
||||
{
|
||||
// TODO(jduncanator): Throw a proper exception
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
public bool TryPushData(byte[] item)
|
||||
{
|
||||
return _data.TryAdd(item);
|
||||
}
|
||||
|
||||
public byte[] PopData()
|
||||
{
|
||||
if (this.TryPopData(out byte[] item))
|
||||
{
|
||||
return item;
|
||||
}
|
||||
|
||||
throw new InvalidOperationException("Input data empty.");
|
||||
}
|
||||
|
||||
public bool TryPopData(out byte[] item)
|
||||
{
|
||||
return _data.TryTake(out item);
|
||||
}
|
||||
|
||||
public void InsertFrontData(byte[] item)
|
||||
{
|
||||
if (!this.TryInsertFrontData(item))
|
||||
{
|
||||
// TODO: Throw a proper exception
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
public bool TryInsertFrontData(byte[] item)
|
||||
{
|
||||
List<byte[]> items = new List<byte[]>();
|
||||
while (_data.TryTake(out byte[] i))
|
||||
{
|
||||
items.Add(i);
|
||||
}
|
||||
|
||||
items.Insert(0, item);
|
||||
|
||||
foreach (byte[] i in items)
|
||||
{
|
||||
_data.TryAdd(i);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
71
src/Ryujinx.HLE/HOS/Services/Am/AppletAE/RealAppletId.cs
Normal file
71
src/Ryujinx.HLE/HOS/Services/Am/AppletAE/RealAppletId.cs
Normal file
@ -0,0 +1,71 @@
|
||||
namespace Ryujinx.Horizon.Sdk.Applet
|
||||
{
|
||||
public enum RealAppletId : uint
|
||||
{
|
||||
None = 0x00,
|
||||
Application = 0x01,
|
||||
OverlayApplet = 0x02,
|
||||
SystemAppletMenu = 0x03,
|
||||
SystemApplication = 0x04,
|
||||
LibraryAppletAuth = 0x0A,
|
||||
LibraryAppletCabinet = 0x0B,
|
||||
LibraryAppletController = 0x0C,
|
||||
LibraryAppletDataErase = 0x0D,
|
||||
LibraryAppletError = 0x0E,
|
||||
LibraryAppletNetConnect = 0x0F,
|
||||
LibraryAppletPlayerSelect = 0x10,
|
||||
LibraryAppletSwkbd = 0x11,
|
||||
LibraryAppletMiiEdit = 0x12,
|
||||
LibraryAppletWeb = 0x13,
|
||||
LibraryAppletShop = 0x14,
|
||||
LibraryAppletPhotoViewer = 0x15,
|
||||
LibraryAppletSet = 0x16,
|
||||
LibraryAppletOfflineWeb = 0x17,
|
||||
LibraryAppletLoginShare = 0x18,
|
||||
LibraryAppletWifiWebAuth = 0x19,
|
||||
LibraryAppletMyPage = 0x1A,
|
||||
LibraryAppletGift = 0x1B,
|
||||
LibraryAppletUserMigration = 0x1C,
|
||||
LibraryAppletPreomiaSys = 0x1D,
|
||||
LibraryAppletStory = 0x1E,
|
||||
LibraryAppletPreomiaUsr = 0x1F,
|
||||
LibraryAppletPreomiaUsrDummy = 0x20,
|
||||
LibraryAppletSample = 0x21,
|
||||
LibraryAppletPromoteQualification = 0x22,
|
||||
LibraryAppletOfflineWebFw17 = 0x32,
|
||||
LibraryAppletOfflineWeb2Fw17 = 0x33,
|
||||
LibraryAppletLoginShareFw17 = 0x35,
|
||||
LibraryAppletLoginShare2Fw17 = 0x36,
|
||||
LibraryAppletLoginShare3Fw17 = 0x37,
|
||||
Unknown38 = 0x38,
|
||||
DevlopmentTool = 0x3E8,
|
||||
CombinationLA = 0x3F1,
|
||||
AeSystemApplet = 0x3F2,
|
||||
AeOverlayApplet = 0x3F3,
|
||||
AeStarter = 0x3F4,
|
||||
AeLibraryAppletAlone = 0x3F5,
|
||||
AeLibraryApplet1 = 0x3F6,
|
||||
AeLibraryApplet2 = 0x3F7,
|
||||
AeLibraryApplet3 = 0x3F8,
|
||||
AeLibraryApplet4 = 0x3F9,
|
||||
AppletISA = 0x3FA,
|
||||
AppletIOA = 0x3FB,
|
||||
AppletISTA = 0x3FC,
|
||||
AppletILA1 = 0x3FD,
|
||||
AppletILA2 = 0x3FE,
|
||||
CombinationLAFw17 = 0x700000DC,
|
||||
AeSystemAppletFw17 = 0x700000E6,
|
||||
AeOverlayAppletFw17 = 0x700000E7,
|
||||
AeStarterFw17 = 0x700000E8,
|
||||
AeLibraryAppletAloneFw17 = 0x700000E9,
|
||||
AeLibraryApplet1Fw17 = 0x700000EA,
|
||||
AeLibraryApplet2Fw17 = 0x700000EB,
|
||||
AeLibraryApplet3Fw17 = 0x700000EC,
|
||||
AeLibraryApplet4Fw17 = 0x700000ED,
|
||||
AppletISAFw17 = 0x700000F0,
|
||||
AppletIOAFw17 = 0x700000F1,
|
||||
AppletISTAFw17 = 0x700000F2,
|
||||
AppletILA1Fw17 = 0x700000F3,
|
||||
AppletILA2Fw17 = 0x700000F4,
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
namespace Ryujinx.HLE.HOS.Services.Am.AppletAE
|
||||
{
|
||||
enum FocusHandlingMode
|
||||
{
|
||||
AlwaysSuspend = 0,
|
||||
SuspendHomeSleep = 1,
|
||||
NoSuspend = 2,
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
namespace Ryujinx.HLE.HOS.Services.Am.AppletAE
|
||||
{
|
||||
public enum SystemButtonType
|
||||
{
|
||||
PerformHomeButtonShortPressing = 1,
|
||||
PerformHomeButtonLongPressing = 2,
|
||||
PerformSystemButtonPressing = 3,
|
||||
PerformSystemButtonLongPressing = 4,
|
||||
ShutdownSystem = 5,
|
||||
PerformCaptureButtonShortPressing = 6,
|
||||
PerformCaptureButtonLongPressing = 7
|
||||
}
|
||||
}
|
@ -9,6 +9,7 @@ namespace Ryujinx.HLE.HOS.Services.Am
|
||||
|
||||
NotAvailable = (2 << ErrorCodeShift) | ModuleId,
|
||||
NoMessages = (3 << ErrorCodeShift) | ModuleId,
|
||||
LibraryAppletTerminated = (22 << ErrorCodeShift) | ModuleId,
|
||||
AppletLaunchFailed = (35 << ErrorCodeShift) | ModuleId,
|
||||
TitleIdNotFound = (37 << ErrorCodeShift) | ModuleId,
|
||||
ObjectInvalid = (500 << ErrorCodeShift) | ModuleId,
|
||||
|
@ -13,6 +13,7 @@ using LibHac.Tools.FsSystem.NcaUtils;
|
||||
using Ryujinx.Common;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy;
|
||||
using Ryujinx.HLE.HOS.Services.Pcv;
|
||||
using Ryujinx.Memory;
|
||||
using System;
|
||||
using System.IO;
|
||||
@ -676,6 +677,14 @@ namespace Ryujinx.HLE.HOS.Services.Fs
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
[CommandCmif(83)]
|
||||
// OpenSaveDataTransferProhibiter(u64) -> nn::fssrv::sf::ISaveDataTransferProhibiter
|
||||
public ResultCode OpenSaveDataTransferProhibiter(ServiceCtx context)
|
||||
{
|
||||
MakeObject(context, new IPcvService(context));
|
||||
|
||||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
[CommandCmif(84)]
|
||||
public ResultCode ListAccessibleSaveDataOwnerId(ServiceCtx context)
|
||||
|
@ -59,6 +59,9 @@ namespace Ryujinx.HLE.HOS.Services
|
||||
public string Name { get; }
|
||||
public Func<IpcService> SmObjectFactory { get; }
|
||||
|
||||
internal KProcess SelfProcess => _selfProcess;
|
||||
internal KThread SelfThread => _selfThread;
|
||||
|
||||
public ServerBase(KernelContext context, string name, Func<IpcService> smObjectFactory = null)
|
||||
{
|
||||
_context = context;
|
||||
|
@ -1,33 +1,569 @@
|
||||
using Ryujinx.HLE.HOS.Applets;
|
||||
using Ryujinx.HLE.HOS.Applets.Types;
|
||||
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||
using Ryujinx.HLE.HOS.Services.Am.AppletAE;
|
||||
using Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.SystemAppletProxy;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
namespace Ryujinx.HLE.HOS.SystemState
|
||||
{
|
||||
class AppletStateMgr
|
||||
{
|
||||
public ConcurrentQueue<AppletMessage> Messages { get; }
|
||||
public ConcurrentQueue<AppletMessage> Messages;
|
||||
|
||||
public FocusState FocusState { get; private set; }
|
||||
public bool ForcedSuspend { get; private set; }
|
||||
public FocusState AcknowledgedFocusState { get; private set; } = FocusState.Background;
|
||||
public FocusState RequestedFocusState { get; private set; } = FocusState.Background;
|
||||
|
||||
public bool ResumeNotificationEnabled { get; set; } = false;
|
||||
public SuspendMode SuspendMode { get; set; } = SuspendMode.NoOverride;
|
||||
public ActivityState ActivityState { get; set; } = ActivityState.ForegroundVisible;
|
||||
|
||||
public KEvent MessageEvent { get; }
|
||||
public KEvent OperationModeChangedEvent { get; }
|
||||
public KEvent LaunchableEvent { get; }
|
||||
|
||||
public IdDictionary AppletResourceUserIds { get; }
|
||||
|
||||
public IdDictionary IndirectLayerHandles { get; }
|
||||
|
||||
public AppletStateMgr(Horizon system)
|
||||
internal bool IsApplication { get; }
|
||||
private bool _focusStateChangedNotificationEnabled = true;
|
||||
private bool _operationModeChangedNotificationEnabled = true;
|
||||
private bool _performanceModeChangedNotificationEnabled = true;
|
||||
private bool _hasRequestedExit = false;
|
||||
private bool _hasAcknowledgedExit = false;
|
||||
private bool _requestedRequestToDisplayState = false;
|
||||
private bool _acknowledgedRequestToDisplayState = false;
|
||||
private bool _hasRequestedRequestToPrepareSleep = false;
|
||||
private bool _hasAcknowledgedRequestToPrepareSleep = false;
|
||||
private bool _hasOperationModeChanged = false;
|
||||
private bool _hasPerformanceModeChanged = false;
|
||||
private bool _hasResume = false;
|
||||
private bool _hasFocusStateChanged = false;
|
||||
private bool _hasAlbumRecordingSaved = false;
|
||||
private bool _hasAlbumScreenShotTaken = false;
|
||||
private bool _hasAutoPowerDown = false;
|
||||
private bool _hasSleepRequiredByLowBattery = false;
|
||||
private bool _hasSleepRequiredByHighTemperature = false;
|
||||
private bool _hasSdCardRemoved = false;
|
||||
private bool _eventSignaled = false;
|
||||
private FocusHandlingMode _focusHandlingMode = FocusHandlingMode.NoSuspend;
|
||||
|
||||
public bool HasRequestedExit => _hasRequestedExit;
|
||||
|
||||
public bool FocusStateChangedNotificationEnabled
|
||||
{
|
||||
get => _focusStateChangedNotificationEnabled;
|
||||
set
|
||||
{
|
||||
_focusStateChangedNotificationEnabled = value;
|
||||
// SignalEventIfNeeded();
|
||||
}
|
||||
}
|
||||
|
||||
public bool OperationModeChangedNotificationEnabled
|
||||
{
|
||||
get => _operationModeChangedNotificationEnabled;
|
||||
set
|
||||
{
|
||||
_operationModeChangedNotificationEnabled = value;
|
||||
SignalEventIfNeeded();
|
||||
}
|
||||
}
|
||||
|
||||
public bool PerformanceModeChangedNotificationEnabled
|
||||
{
|
||||
get => _performanceModeChangedNotificationEnabled;
|
||||
set
|
||||
{
|
||||
_performanceModeChangedNotificationEnabled = value;
|
||||
SignalEventIfNeeded();
|
||||
}
|
||||
}
|
||||
|
||||
public AppletStateMgr(Horizon system, bool isApplication)
|
||||
{
|
||||
IsApplication = isApplication;
|
||||
Messages = new ConcurrentQueue<AppletMessage>();
|
||||
MessageEvent = new KEvent(system.KernelContext);
|
||||
OperationModeChangedEvent = new KEvent(system.KernelContext);
|
||||
LaunchableEvent = new KEvent(system.KernelContext);
|
||||
|
||||
AppletResourceUserIds = new IdDictionary();
|
||||
IndirectLayerHandles = new IdDictionary();
|
||||
}
|
||||
|
||||
public void SetFocusState(FocusState state)
|
||||
{
|
||||
if (RequestedFocusState != state)
|
||||
{
|
||||
RequestedFocusState = state;
|
||||
_hasFocusStateChanged = true;
|
||||
}
|
||||
|
||||
SignalEventIfNeeded();
|
||||
}
|
||||
|
||||
public FocusState GetAndClearFocusState()
|
||||
{
|
||||
AcknowledgedFocusState = RequestedFocusState;
|
||||
return AcknowledgedFocusState;
|
||||
}
|
||||
|
||||
public void PushUnorderedMessage(AppletMessage message)
|
||||
{
|
||||
Messages.Enqueue(message);
|
||||
|
||||
SignalEventIfNeeded();
|
||||
}
|
||||
|
||||
public bool PopMessage(out AppletMessage message)
|
||||
{
|
||||
message = GetNextMessage();
|
||||
SignalEventIfNeeded();
|
||||
return message != AppletMessage.None;
|
||||
}
|
||||
|
||||
private AppletMessage GetNextMessage()
|
||||
{
|
||||
if (_hasResume)
|
||||
{
|
||||
_hasResume = false;
|
||||
return AppletMessage.Resume;
|
||||
}
|
||||
|
||||
if (_hasRequestedExit != _hasAcknowledgedExit)
|
||||
{
|
||||
_hasAcknowledgedExit = _hasRequestedExit;
|
||||
return AppletMessage.Exit;
|
||||
}
|
||||
|
||||
if (_focusStateChangedNotificationEnabled)
|
||||
{
|
||||
if (IsApplication)
|
||||
{
|
||||
if (_hasFocusStateChanged)
|
||||
{
|
||||
_hasFocusStateChanged = false;
|
||||
return AppletMessage.FocusStateChanged;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (RequestedFocusState != AcknowledgedFocusState)
|
||||
{
|
||||
AcknowledgedFocusState = RequestedFocusState;
|
||||
|
||||
switch (RequestedFocusState)
|
||||
{
|
||||
case FocusState.InFocus:
|
||||
return AppletMessage.ChangeIntoForeground;
|
||||
case FocusState.OutOfFocus:
|
||||
return AppletMessage.ChangeIntoBackground;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_hasRequestedRequestToPrepareSleep != _hasAcknowledgedRequestToPrepareSleep)
|
||||
{
|
||||
_hasAcknowledgedRequestToPrepareSleep = true;
|
||||
return AppletMessage.RequestToPrepareSleep;
|
||||
}
|
||||
|
||||
if (_requestedRequestToDisplayState != _acknowledgedRequestToDisplayState)
|
||||
{
|
||||
_acknowledgedRequestToDisplayState = _requestedRequestToDisplayState;
|
||||
return AppletMessage.RequestToDisplay;
|
||||
}
|
||||
|
||||
if (_hasOperationModeChanged)
|
||||
{
|
||||
_hasOperationModeChanged = false;
|
||||
return AppletMessage.OperationModeChanged;
|
||||
}
|
||||
|
||||
if (_hasPerformanceModeChanged)
|
||||
{
|
||||
_hasPerformanceModeChanged = false;
|
||||
return AppletMessage.PerformanceModeChanged;
|
||||
}
|
||||
|
||||
if (_hasSdCardRemoved)
|
||||
{
|
||||
_hasSdCardRemoved = false;
|
||||
return AppletMessage.SdCardRemoved;
|
||||
}
|
||||
|
||||
if (_hasSleepRequiredByHighTemperature)
|
||||
{
|
||||
_hasSleepRequiredByHighTemperature = false;
|
||||
return AppletMessage.SleepRequiredByHighTemperature;
|
||||
}
|
||||
|
||||
if (_hasSleepRequiredByLowBattery)
|
||||
{
|
||||
_hasSleepRequiredByLowBattery = false;
|
||||
return AppletMessage.SleepRequiredByLowBattery;
|
||||
}
|
||||
|
||||
if (_hasAutoPowerDown)
|
||||
{
|
||||
_hasAutoPowerDown = false;
|
||||
return AppletMessage.AutoPowerDown;
|
||||
}
|
||||
|
||||
if (_hasAlbumScreenShotTaken)
|
||||
{
|
||||
_hasAlbumScreenShotTaken = false;
|
||||
return AppletMessage.AlbumScreenShotTaken;
|
||||
}
|
||||
|
||||
if (_hasAlbumRecordingSaved)
|
||||
{
|
||||
_hasAlbumRecordingSaved = false;
|
||||
return AppletMessage.AlbumRecordingSaved;
|
||||
}
|
||||
|
||||
if (Messages.TryDequeue(out AppletMessage message))
|
||||
{
|
||||
return message;
|
||||
}
|
||||
|
||||
return AppletMessage.None;
|
||||
}
|
||||
|
||||
internal void SignalEventIfNeeded()
|
||||
{
|
||||
var shouldSignal = ShouldSignalEvent();
|
||||
|
||||
if (_eventSignaled != shouldSignal)
|
||||
{
|
||||
if (_eventSignaled)
|
||||
{
|
||||
MessageEvent.ReadableEvent.Clear();
|
||||
_eventSignaled = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageEvent.ReadableEvent.Signal();
|
||||
_eventSignaled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool ShouldSignalEvent()
|
||||
{
|
||||
bool focusStateChanged = false;
|
||||
if (_focusStateChangedNotificationEnabled)
|
||||
{
|
||||
if (IsApplication)
|
||||
{
|
||||
if (_hasFocusStateChanged)
|
||||
{
|
||||
focusStateChanged = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (RequestedFocusState != AcknowledgedFocusState)
|
||||
{
|
||||
focusStateChanged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
FocusState = isFocused ? FocusState.InFocus : FocusState.OutOfFocus;
|
||||
AcknowledgedFocusState = isFocused ? FocusState.InFocus : FocusState.OutOfFocus;
|
||||
|
||||
Messages.Enqueue(AppletMessage.FocusStateChanged);
|
||||
|
||||
@ -38,5 +574,6 @@ namespace Ryujinx.HLE.HOS.SystemState
|
||||
|
||||
MessageEvent.ReadableEvent.Signal();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -31,8 +31,11 @@ namespace Ryujinx.HLE.Loaders.Processes
|
||||
get
|
||||
{
|
||||
if (!_processesByPid.TryGetValue(_latestPid, out ProcessResult value))
|
||||
throw new RyujinxException(
|
||||
$"The HLE Process map did not have a process with ID {_latestPid}. Are you missing firmware?");
|
||||
{
|
||||
return null;
|
||||
}
|
||||
// throw new RyujinxException(
|
||||
// $"The HLE Process map did not have a process with ID {_latestPid}. Are you missing firmware?");
|
||||
|
||||
return value;
|
||||
}
|
||||
@ -80,6 +83,42 @@ namespace Ryujinx.HLE.Loaders.Processes
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool LoadXci(string path, ulong applicationId, out ProcessResult processResult)
|
||||
{
|
||||
FileStream stream = new(path, FileMode.Open, FileAccess.Read);
|
||||
Xci xci = new(_device.Configuration.VirtualFileSystem.KeySet, stream.AsStorage());
|
||||
|
||||
if (!xci.HasPartition(XciPartitionType.Secure))
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Loader, "Unable to load XCI: Could not find XCI Secure partition");
|
||||
processResult = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
(bool success, processResult) = xci.OpenPartition(XciPartitionType.Secure).TryLoad(_device, path, applicationId, out string errorMessage);
|
||||
|
||||
if (!success)
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Loader, errorMessage, nameof(PartitionFileSystemExtensions.TryLoad));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (processResult.ProcessId != 0 && _processesByPid.TryAdd(processResult.ProcessId, processResult))
|
||||
{
|
||||
if (processResult.Start(_device))
|
||||
{
|
||||
_latestPid = processResult.ProcessId;
|
||||
|
||||
TitleIDs.CurrentApplication.Value = processResult.ProgramIdText;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool LoadNsp(string path, ulong applicationId)
|
||||
{
|
||||
FileStream file = new(path, FileMode.Open, FileAccess.Read);
|
||||
@ -114,6 +153,40 @@ namespace Ryujinx.HLE.Loaders.Processes
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool LoadNsp(string path, ulong applicationId, out ProcessResult processResult)
|
||||
{
|
||||
FileStream file = new(path, FileMode.Open, FileAccess.Read);
|
||||
PartitionFileSystem partitionFileSystem = new();
|
||||
partitionFileSystem.Initialize(file.AsStorage()).ThrowIfFailure();
|
||||
|
||||
(bool success, processResult) = partitionFileSystem.TryLoad(_device, path, applicationId, out string errorMessage);
|
||||
|
||||
if (processResult.ProcessId == 0)
|
||||
{
|
||||
// This is not a normal NSP, it's actually a ExeFS as a NSP
|
||||
processResult = partitionFileSystem.Load(_device, new BlitStruct<ApplicationControlProperty>(1), partitionFileSystem.GetNpdm(), 0, true);
|
||||
}
|
||||
|
||||
if (processResult.ProcessId != 0 && _processesByPid.TryAdd(processResult.ProcessId, processResult))
|
||||
{
|
||||
if (processResult.Start(_device))
|
||||
{
|
||||
_latestPid = processResult.ProcessId;
|
||||
|
||||
TitleIDs.CurrentApplication.Value = processResult.ProgramIdText;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!success)
|
||||
{
|
||||
Logger.Error?.Print(LogClass.Loader, errorMessage, nameof(PartitionFileSystemExtensions.TryLoad));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool LoadNca(string path, BlitStruct<ApplicationControlProperty>? customNacpData = null)
|
||||
{
|
||||
FileStream file = new(path, FileMode.Open, FileAccess.Read);
|
||||
@ -140,6 +213,32 @@ namespace Ryujinx.HLE.Loaders.Processes
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool LoadNca(string path, out ProcessResult processResult)
|
||||
{
|
||||
FileStream file = new(path, FileMode.Open, FileAccess.Read);
|
||||
Nca nca = new(_device.Configuration.VirtualFileSystem.KeySet, file.AsStorage(false));
|
||||
|
||||
processResult = nca.Load(_device, null, null, null);
|
||||
|
||||
if (processResult.ProcessId != 0 && _processesByPid.TryAdd(processResult.ProcessId, processResult))
|
||||
{
|
||||
if (processResult.Start(_device))
|
||||
{
|
||||
// NOTE: Check if process is SystemApplicationId or ApplicationId
|
||||
if (processResult.ProgramId > 0x01000000000007FF)
|
||||
{
|
||||
_latestPid = processResult.ProcessId;
|
||||
|
||||
TitleIDs.CurrentApplication.Value = processResult.ProgramIdText;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool LoadUnpackedNca(string exeFsDirPath, string romFsPath = null)
|
||||
{
|
||||
ProcessResult processResult = new LocalFileSystem(exeFsDirPath).Load(_device, romFsPath);
|
||||
@ -266,5 +365,10 @@ namespace Ryujinx.HLE.Loaders.Processes
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void SetActivePID(ulong lastActivePid)
|
||||
{
|
||||
_latestPid = lastActivePid;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ using LibHac.Loader;
|
||||
using LibHac.Ns;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Cpu;
|
||||
using Ryujinx.HLE.HOS.Applets;
|
||||
using Ryujinx.HLE.HOS.SystemState;
|
||||
using Ryujinx.HLE.Loaders.Processes.Extensions;
|
||||
using Ryujinx.Horizon.Common;
|
||||
|
@ -1,6 +1,6 @@
|
||||
namespace Ryujinx.Horizon.Sdk.OsTypes
|
||||
{
|
||||
enum EventClearMode
|
||||
public enum EventClearMode
|
||||
{
|
||||
ManualClear,
|
||||
AutoClear,
|
||||
|
@ -2,7 +2,7 @@ using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Horizon.Sdk.OsTypes
|
||||
{
|
||||
struct EventType
|
||||
public struct EventType
|
||||
{
|
||||
public LinkedList<MultiWaitHolderBase> MultiWaitHolders;
|
||||
public bool Signaled;
|
||||
|
@ -6,7 +6,7 @@ using System.Threading;
|
||||
|
||||
namespace Ryujinx.Horizon.Sdk.OsTypes.Impl
|
||||
{
|
||||
class MultiWaitImpl
|
||||
public class MultiWaitImpl
|
||||
{
|
||||
private const int WaitTimedOut = -1;
|
||||
private const int WaitCancelled = -2;
|
||||
|
@ -1,6 +1,6 @@
|
||||
namespace Ryujinx.Horizon.Sdk.OsTypes
|
||||
{
|
||||
enum InitializationState : byte
|
||||
public enum InitializationState : byte
|
||||
{
|
||||
NotInitialized,
|
||||
Initialized,
|
||||
|
@ -1,6 +1,6 @@
|
||||
namespace Ryujinx.Horizon.Sdk.OsTypes
|
||||
{
|
||||
struct InterProcessEventType
|
||||
public struct InterProcessEventType
|
||||
{
|
||||
public readonly bool AutoClear;
|
||||
public InitializationState State;
|
||||
|
@ -3,7 +3,7 @@ using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Horizon.Sdk.OsTypes
|
||||
{
|
||||
class MultiWait
|
||||
public class MultiWait
|
||||
{
|
||||
private readonly MultiWaitImpl _impl;
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
namespace Ryujinx.Horizon.Sdk.OsTypes
|
||||
{
|
||||
class MultiWaitHolder : MultiWaitHolderBase
|
||||
public class MultiWaitHolder : MultiWaitHolderBase
|
||||
{
|
||||
public object UserData { get; set; }
|
||||
|
||||
|
@ -2,9 +2,9 @@ using Ryujinx.Horizon.Sdk.OsTypes.Impl;
|
||||
|
||||
namespace Ryujinx.Horizon.Sdk.OsTypes
|
||||
{
|
||||
class MultiWaitHolderBase
|
||||
public class MultiWaitHolderBase
|
||||
{
|
||||
protected MultiWaitImpl MultiWait;
|
||||
public MultiWaitImpl MultiWait;
|
||||
|
||||
public bool IsLinked => MultiWait != null;
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
namespace Ryujinx.Horizon.Sdk.OsTypes
|
||||
{
|
||||
class MultiWaitHolderOfHandle : MultiWaitHolder
|
||||
public class MultiWaitHolderOfHandle : MultiWaitHolder
|
||||
{
|
||||
private readonly int _handle;
|
||||
|
||||
|
@ -0,0 +1,26 @@
|
||||
using Ryujinx.Horizon.Sdk.OsTypes.Impl;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Ryujinx.Horizon.Sdk.OsTypes
|
||||
{
|
||||
public class MultiWaitHolderOfInterProcessEvent : MultiWaitHolder
|
||||
{
|
||||
private readonly InterProcessEventType _event;
|
||||
private LinkedListNode<MultiWaitHolderBase> _node;
|
||||
|
||||
public override TriBool Signaled
|
||||
{
|
||||
get
|
||||
{
|
||||
return TriBool.Undefined;
|
||||
}
|
||||
}
|
||||
|
||||
public override int Handle => _event.ReadableHandle;
|
||||
|
||||
public MultiWaitHolderOfInterProcessEvent(InterProcessEventType evnt)
|
||||
{
|
||||
_event = evnt;
|
||||
}
|
||||
}
|
||||
}
|
@ -3,7 +3,7 @@ using System.Threading;
|
||||
|
||||
namespace Ryujinx.Horizon.Sdk.OsTypes
|
||||
{
|
||||
static partial class Os
|
||||
public static partial class Os
|
||||
{
|
||||
public static void InitializeEvent(out EventType evnt, bool signaled, EventClearMode clearMode)
|
||||
{
|
||||
|
@ -1,6 +1,6 @@
|
||||
namespace Ryujinx.Horizon.Sdk.OsTypes
|
||||
{
|
||||
struct SystemEventType
|
||||
public struct SystemEventType
|
||||
{
|
||||
public enum InitializationState : byte
|
||||
{
|
||||
|
@ -1,6 +1,6 @@
|
||||
namespace Ryujinx.Horizon.Sdk.OsTypes
|
||||
{
|
||||
enum TriBool
|
||||
public enum TriBool
|
||||
{
|
||||
False,
|
||||
True,
|
||||
|
Loading…
x
Reference in New Issue
Block a user