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.Fs.Shim;
|
||||||
using LibHac.FsSystem;
|
using LibHac.FsSystem;
|
||||||
using LibHac.Tools.FsSystem;
|
using LibHac.Tools.FsSystem;
|
||||||
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.Cpu;
|
using Ryujinx.Cpu;
|
||||||
using Ryujinx.HLE.FileSystem;
|
using Ryujinx.HLE.FileSystem;
|
||||||
|
using Ryujinx.HLE.HOS.Applets;
|
||||||
using Ryujinx.HLE.HOS.Kernel;
|
using Ryujinx.HLE.HOS.Kernel;
|
||||||
using Ryujinx.HLE.HOS.Kernel.Memory;
|
using Ryujinx.HLE.HOS.Kernel.Memory;
|
||||||
using Ryujinx.HLE.HOS.Kernel.Process;
|
using Ryujinx.HLE.HOS.Kernel.Process;
|
||||||
@ -61,7 +63,37 @@ namespace Ryujinx.HLE.HOS
|
|||||||
|
|
||||||
internal PerformanceState PerformanceState { get; private set; }
|
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; }
|
internal List<NfpDevice> NfpDevices { get; private set; }
|
||||||
|
|
||||||
@ -172,7 +204,10 @@ namespace Ryujinx.HLE.HOS
|
|||||||
|
|
||||||
AppletCaptureBufferTransfer = new KTransferMemory(KernelContext, appletCaptureBufferStorage);
|
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);
|
AppletState.SetFocus(true);
|
||||||
|
|
||||||
@ -317,9 +352,7 @@ namespace Ryujinx.HLE.HOS
|
|||||||
State.DockedMode = newState;
|
State.DockedMode = newState;
|
||||||
PerformanceState.PerformanceMode = State.DockedMode ? PerformanceMode.Boost : PerformanceMode.Default;
|
PerformanceState.PerformanceMode = State.DockedMode ? PerformanceMode.Boost : PerformanceMode.Default;
|
||||||
|
|
||||||
AppletState.Messages.Enqueue(AppletMessage.OperationModeChanged);
|
WindowSystem.OnOperationModeChanged();
|
||||||
AppletState.Messages.Enqueue(AppletMessage.PerformanceModeChanged);
|
|
||||||
AppletState.MessageEvent.ReadableEvent.Signal();
|
|
||||||
|
|
||||||
SignalDisplayResolutionChange();
|
SignalDisplayResolutionChange();
|
||||||
|
|
||||||
@ -334,8 +367,8 @@ namespace Ryujinx.HLE.HOS
|
|||||||
|
|
||||||
public void SimulateWakeUpMessage()
|
public void SimulateWakeUpMessage()
|
||||||
{
|
{
|
||||||
AppletState.Messages.Enqueue(AppletMessage.Resume);
|
// AppletState.Messages.Enqueue(AppletMessage.Resume);
|
||||||
AppletState.MessageEvent.ReadableEvent.Signal();
|
// AppletState.MessageEvent.ReadableEvent.Signal();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ScanAmiibo(int nfpDeviceId, string amiiboId, bool useRandomUuid)
|
public void ScanAmiibo(int nfpDeviceId, string amiiboId, bool useRandomUuid)
|
||||||
|
@ -1175,5 +1175,10 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
|||||||
{
|
{
|
||||||
return Capabilities.IsSvcPermitted(svcId);
|
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>
|
// GetApplicationCreator() -> object<nn::am::service::IApplicationCreator>
|
||||||
public ResultCode GetApplicationCreator(ServiceCtx context)
|
public ResultCode GetApplicationCreator(ServiceCtx context)
|
||||||
{
|
{
|
||||||
MakeObject(context, new IApplicationCreator());
|
MakeObject(context, new IApplicationCreator(context.Device.Processes.ActiveApplication.ProcessId));
|
||||||
|
|
||||||
return ResultCode.Success;
|
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
|
namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.SystemAppletProxy
|
||||||
{
|
{
|
||||||
class IApplicationCreator : IpcService
|
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
|
// ReceiveMessage() -> nn::am::AppletMessage
|
||||||
public ResultCode ReceiveMessage(ServiceCtx context)
|
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;
|
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);
|
context.ResponseData.Write((int)message);
|
||||||
|
|
||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -120,7 +105,7 @@ 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.FocusState);
|
context.ResponseData.Write((byte)context.Device.System.AppletState.AcknowledgedFocusState);
|
||||||
|
|
||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
|
|||||||
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();
|
||||||
|
|
||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
@ -31,6 +32,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
|
|||||||
public ResultCode LockForeground(ServiceCtx context)
|
public ResultCode LockForeground(ServiceCtx context)
|
||||||
{
|
{
|
||||||
Logger.Stub?.PrintStub(LogClass.ServiceAm);
|
Logger.Stub?.PrintStub(LogClass.ServiceAm);
|
||||||
|
context.Device.System.WindowSystem.RequestLockHomeMenuIntoForeground();
|
||||||
|
|
||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
@ -39,6 +41,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
|
|||||||
public ResultCode UnlockForeground(ServiceCtx context)
|
public ResultCode UnlockForeground(ServiceCtx context)
|
||||||
{
|
{
|
||||||
Logger.Stub?.PrintStub(LogClass.ServiceAm);
|
Logger.Stub?.PrintStub(LogClass.ServiceAm);
|
||||||
|
context.Device.System.WindowSystem.RequestUnlockHomeMenuFromForeground();
|
||||||
|
|
||||||
return ResultCode.Success;
|
return ResultCode.Success;
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
|
|||||||
// GetAppletResourceUserId() -> nn::applet::AppletResourceUserId
|
// GetAppletResourceUserId() -> nn::applet::AppletResourceUserId
|
||||||
public ResultCode GetAppletResourceUserId(ServiceCtx context)
|
public ResultCode GetAppletResourceUserId(ServiceCtx context)
|
||||||
{
|
{
|
||||||
long appletResourceUserId = context.Device.System.AppletState.AppletResourceUserIds.Add(_pid);
|
ulong appletResourceUserId = _pid;
|
||||||
|
|
||||||
context.ResponseData.Write(appletResourceUserId);
|
context.ResponseData.Write(appletResourceUserId);
|
||||||
|
|
||||||
|
@ -4,5 +4,6 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
|
|||||||
{
|
{
|
||||||
InFocus = 1,
|
InFocus = 1,
|
||||||
OutOfFocus = 2,
|
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,
|
NotAvailable = (2 << ErrorCodeShift) | ModuleId,
|
||||||
NoMessages = (3 << ErrorCodeShift) | ModuleId,
|
NoMessages = (3 << ErrorCodeShift) | ModuleId,
|
||||||
|
LibraryAppletTerminated = (22 << ErrorCodeShift) | ModuleId,
|
||||||
AppletLaunchFailed = (35 << ErrorCodeShift) | ModuleId,
|
AppletLaunchFailed = (35 << ErrorCodeShift) | ModuleId,
|
||||||
TitleIdNotFound = (37 << ErrorCodeShift) | ModuleId,
|
TitleIdNotFound = (37 << ErrorCodeShift) | ModuleId,
|
||||||
ObjectInvalid = (500 << ErrorCodeShift) | ModuleId,
|
ObjectInvalid = (500 << ErrorCodeShift) | ModuleId,
|
||||||
|
@ -13,6 +13,7 @@ using LibHac.Tools.FsSystem.NcaUtils;
|
|||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy;
|
using Ryujinx.HLE.HOS.Services.Fs.FileSystemProxy;
|
||||||
|
using Ryujinx.HLE.HOS.Services.Pcv;
|
||||||
using Ryujinx.Memory;
|
using Ryujinx.Memory;
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
@ -676,6 +677,14 @@ namespace Ryujinx.HLE.HOS.Services.Fs
|
|||||||
|
|
||||||
return ResultCode.Success;
|
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)]
|
[CommandCmif(84)]
|
||||||
public ResultCode ListAccessibleSaveDataOwnerId(ServiceCtx context)
|
public ResultCode ListAccessibleSaveDataOwnerId(ServiceCtx context)
|
||||||
|
@ -59,6 +59,9 @@ namespace Ryujinx.HLE.HOS.Services
|
|||||||
public string Name { get; }
|
public string Name { get; }
|
||||||
public Func<IpcService> SmObjectFactory { get; }
|
public Func<IpcService> SmObjectFactory { get; }
|
||||||
|
|
||||||
|
internal KProcess SelfProcess => _selfProcess;
|
||||||
|
internal KThread SelfThread => _selfThread;
|
||||||
|
|
||||||
public ServerBase(KernelContext context, string name, Func<IpcService> smObjectFactory = null)
|
public ServerBase(KernelContext context, string name, Func<IpcService> smObjectFactory = null)
|
||||||
{
|
{
|
||||||
_context = context;
|
_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.Kernel.Threading;
|
||||||
|
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;
|
using System.Collections.Concurrent;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.HOS.SystemState
|
namespace Ryujinx.HLE.HOS.SystemState
|
||||||
{
|
{
|
||||||
class AppletStateMgr
|
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 MessageEvent { get; }
|
||||||
|
public KEvent OperationModeChangedEvent { get; }
|
||||||
|
public KEvent LaunchableEvent { get; }
|
||||||
|
|
||||||
public IdDictionary AppletResourceUserIds { get; }
|
public IdDictionary AppletResourceUserIds { get; }
|
||||||
|
|
||||||
public IdDictionary IndirectLayerHandles { 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>();
|
Messages = new ConcurrentQueue<AppletMessage>();
|
||||||
MessageEvent = new KEvent(system.KernelContext);
|
MessageEvent = new KEvent(system.KernelContext);
|
||||||
|
OperationModeChangedEvent = new KEvent(system.KernelContext);
|
||||||
|
LaunchableEvent = new KEvent(system.KernelContext);
|
||||||
|
|
||||||
AppletResourceUserIds = new IdDictionary();
|
AppletResourceUserIds = new IdDictionary();
|
||||||
IndirectLayerHandles = 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)
|
public void SetFocus(bool isFocused)
|
||||||
{
|
{
|
||||||
FocusState = isFocused ? FocusState.InFocus : FocusState.OutOfFocus;
|
AcknowledgedFocusState = isFocused ? FocusState.InFocus : FocusState.OutOfFocus;
|
||||||
|
|
||||||
Messages.Enqueue(AppletMessage.FocusStateChanged);
|
Messages.Enqueue(AppletMessage.FocusStateChanged);
|
||||||
|
|
||||||
@ -38,5 +574,6 @@ namespace Ryujinx.HLE.HOS.SystemState
|
|||||||
|
|
||||||
MessageEvent.ReadableEvent.Signal();
|
MessageEvent.ReadableEvent.Signal();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,8 +31,11 @@ namespace Ryujinx.HLE.Loaders.Processes
|
|||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (!_processesByPid.TryGetValue(_latestPid, out ProcessResult value))
|
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;
|
return value;
|
||||||
}
|
}
|
||||||
@ -80,6 +83,42 @@ namespace Ryujinx.HLE.Loaders.Processes
|
|||||||
return false;
|
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)
|
public bool LoadNsp(string path, ulong applicationId)
|
||||||
{
|
{
|
||||||
FileStream file = new(path, FileMode.Open, FileAccess.Read);
|
FileStream file = new(path, FileMode.Open, FileAccess.Read);
|
||||||
@ -114,6 +153,40 @@ namespace Ryujinx.HLE.Loaders.Processes
|
|||||||
return false;
|
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)
|
public bool LoadNca(string path, BlitStruct<ApplicationControlProperty>? customNacpData = null)
|
||||||
{
|
{
|
||||||
FileStream file = new(path, FileMode.Open, FileAccess.Read);
|
FileStream file = new(path, FileMode.Open, FileAccess.Read);
|
||||||
@ -140,6 +213,32 @@ namespace Ryujinx.HLE.Loaders.Processes
|
|||||||
return false;
|
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)
|
public bool LoadUnpackedNca(string exeFsDirPath, string romFsPath = null)
|
||||||
{
|
{
|
||||||
ProcessResult processResult = new LocalFileSystem(exeFsDirPath).Load(_device, romFsPath);
|
ProcessResult processResult = new LocalFileSystem(exeFsDirPath).Load(_device, romFsPath);
|
||||||
@ -266,5 +365,10 @@ namespace Ryujinx.HLE.Loaders.Processes
|
|||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetActivePID(ulong lastActivePid)
|
||||||
|
{
|
||||||
|
_latestPid = lastActivePid;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ using LibHac.Loader;
|
|||||||
using LibHac.Ns;
|
using LibHac.Ns;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.Cpu;
|
using Ryujinx.Cpu;
|
||||||
|
using Ryujinx.HLE.HOS.Applets;
|
||||||
using Ryujinx.HLE.HOS.SystemState;
|
using Ryujinx.HLE.HOS.SystemState;
|
||||||
using Ryujinx.HLE.Loaders.Processes.Extensions;
|
using Ryujinx.HLE.Loaders.Processes.Extensions;
|
||||||
using Ryujinx.Horizon.Common;
|
using Ryujinx.Horizon.Common;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
namespace Ryujinx.Horizon.Sdk.OsTypes
|
namespace Ryujinx.Horizon.Sdk.OsTypes
|
||||||
{
|
{
|
||||||
enum EventClearMode
|
public enum EventClearMode
|
||||||
{
|
{
|
||||||
ManualClear,
|
ManualClear,
|
||||||
AutoClear,
|
AutoClear,
|
||||||
|
@ -2,7 +2,7 @@ using System.Collections.Generic;
|
|||||||
|
|
||||||
namespace Ryujinx.Horizon.Sdk.OsTypes
|
namespace Ryujinx.Horizon.Sdk.OsTypes
|
||||||
{
|
{
|
||||||
struct EventType
|
public struct EventType
|
||||||
{
|
{
|
||||||
public LinkedList<MultiWaitHolderBase> MultiWaitHolders;
|
public LinkedList<MultiWaitHolderBase> MultiWaitHolders;
|
||||||
public bool Signaled;
|
public bool Signaled;
|
||||||
|
@ -6,7 +6,7 @@ using System.Threading;
|
|||||||
|
|
||||||
namespace Ryujinx.Horizon.Sdk.OsTypes.Impl
|
namespace Ryujinx.Horizon.Sdk.OsTypes.Impl
|
||||||
{
|
{
|
||||||
class MultiWaitImpl
|
public class MultiWaitImpl
|
||||||
{
|
{
|
||||||
private const int WaitTimedOut = -1;
|
private const int WaitTimedOut = -1;
|
||||||
private const int WaitCancelled = -2;
|
private const int WaitCancelled = -2;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
namespace Ryujinx.Horizon.Sdk.OsTypes
|
namespace Ryujinx.Horizon.Sdk.OsTypes
|
||||||
{
|
{
|
||||||
enum InitializationState : byte
|
public enum InitializationState : byte
|
||||||
{
|
{
|
||||||
NotInitialized,
|
NotInitialized,
|
||||||
Initialized,
|
Initialized,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
namespace Ryujinx.Horizon.Sdk.OsTypes
|
namespace Ryujinx.Horizon.Sdk.OsTypes
|
||||||
{
|
{
|
||||||
struct InterProcessEventType
|
public struct InterProcessEventType
|
||||||
{
|
{
|
||||||
public readonly bool AutoClear;
|
public readonly bool AutoClear;
|
||||||
public InitializationState State;
|
public InitializationState State;
|
||||||
|
@ -3,7 +3,7 @@ using System.Collections.Generic;
|
|||||||
|
|
||||||
namespace Ryujinx.Horizon.Sdk.OsTypes
|
namespace Ryujinx.Horizon.Sdk.OsTypes
|
||||||
{
|
{
|
||||||
class MultiWait
|
public class MultiWait
|
||||||
{
|
{
|
||||||
private readonly MultiWaitImpl _impl;
|
private readonly MultiWaitImpl _impl;
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
namespace Ryujinx.Horizon.Sdk.OsTypes
|
namespace Ryujinx.Horizon.Sdk.OsTypes
|
||||||
{
|
{
|
||||||
class MultiWaitHolder : MultiWaitHolderBase
|
public class MultiWaitHolder : MultiWaitHolderBase
|
||||||
{
|
{
|
||||||
public object UserData { get; set; }
|
public object UserData { get; set; }
|
||||||
|
|
||||||
|
@ -2,9 +2,9 @@ using Ryujinx.Horizon.Sdk.OsTypes.Impl;
|
|||||||
|
|
||||||
namespace Ryujinx.Horizon.Sdk.OsTypes
|
namespace Ryujinx.Horizon.Sdk.OsTypes
|
||||||
{
|
{
|
||||||
class MultiWaitHolderBase
|
public class MultiWaitHolderBase
|
||||||
{
|
{
|
||||||
protected MultiWaitImpl MultiWait;
|
public MultiWaitImpl MultiWait;
|
||||||
|
|
||||||
public bool IsLinked => MultiWait != null;
|
public bool IsLinked => MultiWait != null;
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
namespace Ryujinx.Horizon.Sdk.OsTypes
|
namespace Ryujinx.Horizon.Sdk.OsTypes
|
||||||
{
|
{
|
||||||
class MultiWaitHolderOfHandle : MultiWaitHolder
|
public class MultiWaitHolderOfHandle : MultiWaitHolder
|
||||||
{
|
{
|
||||||
private readonly int _handle;
|
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
|
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)
|
public static void InitializeEvent(out EventType evnt, bool signaled, EventClearMode clearMode)
|
||||||
{
|
{
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
namespace Ryujinx.Horizon.Sdk.OsTypes
|
namespace Ryujinx.Horizon.Sdk.OsTypes
|
||||||
{
|
{
|
||||||
struct SystemEventType
|
public struct SystemEventType
|
||||||
{
|
{
|
||||||
public enum InitializationState : byte
|
public enum InitializationState : byte
|
||||||
{
|
{
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
namespace Ryujinx.Horizon.Sdk.OsTypes
|
namespace Ryujinx.Horizon.Sdk.OsTypes
|
||||||
{
|
{
|
||||||
enum TriBool
|
public enum TriBool
|
||||||
{
|
{
|
||||||
False,
|
False,
|
||||||
True,
|
True,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user