From 808380690c684a7598efad791f560e02ce70bc82 Mon Sep 17 00:00:00 2001
From: Ac_K <Acoustik666@gmail.com>
Date: Wed, 16 Dec 2020 01:41:42 +0100
Subject: [PATCH] am/gui: Implement Wake-up message (#1750)

* am/gui: Implement Wake-up message.

This implement the ability to send a Wake-up (Resume) message to the guest.
Sometime games needs to Sleep and Wake-up the switch to unlock some ingame features.

* Address gdkchan feedback
---
 Ryujinx.HLE/HOS/Horizon.cs                    | 12 +++++++--
 .../SystemAppletProxy/ICommonStateGetter.cs   | 16 ++++++++++-
 .../SystemAppletProxy/Types/MessageInfo.cs    |  1 +
 Ryujinx.HLE/HOS/SystemState/AppletStateMgr.cs | 27 +++----------------
 Ryujinx/Ui/MainWindow.cs                      |  5 ++++
 Ryujinx/Ui/MainWindow.glade                   | 10 +++++++
 6 files changed, 45 insertions(+), 26 deletions(-)

diff --git a/Ryujinx.HLE/HOS/Horizon.cs b/Ryujinx.HLE/HOS/Horizon.cs
index 16b3b8073..e6e9cbb0b 100644
--- a/Ryujinx.HLE/HOS/Horizon.cs
+++ b/Ryujinx.HLE/HOS/Horizon.cs
@@ -278,8 +278,10 @@ namespace Ryujinx.HLE.HOS
                 State.DockedMode = e.NewValue;
                 PerformanceState.PerformanceMode = State.DockedMode ? PerformanceMode.Boost : PerformanceMode.Default;
 
-                AppletState.EnqueueMessage(MessageInfo.OperationModeChanged);
-                AppletState.EnqueueMessage(MessageInfo.PerformanceModeChanged);
+                AppletState.Messages.Enqueue(MessageInfo.OperationModeChanged);
+                AppletState.Messages.Enqueue(MessageInfo.PerformanceModeChanged);
+                AppletState.MessageEvent.ReadableEvent.Signal();
+
                 SignalDisplayResolutionChange();
 
                 // Reconfigure controllers
@@ -287,6 +289,12 @@ namespace Ryujinx.HLE.HOS
             }
         }
 
+        public void SimulateWakeUpMessage()
+        {
+            AppletState.Messages.Enqueue(MessageInfo.Resume);
+            AppletState.MessageEvent.ReadableEvent.Signal();
+        }
+
         public void SignalDisplayResolutionChange()
         {
             DisplayResolutionChangeEvent.ReadableEvent.Signal();
diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ICommonStateGetter.cs b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ICommonStateGetter.cs
index e3b73c294..0d003abaa 100644
--- a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ICommonStateGetter.cs
+++ b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/ICommonStateGetter.cs
@@ -46,11 +46,25 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
         // ReceiveMessage() -> nn::am::AppletMessage
         public ResultCode ReceiveMessage(ServiceCtx context)
         {
-            if (!context.Device.System.AppletState.TryDequeueMessage(out MessageInfo message))
+            if (!context.Device.System.AppletState.Messages.TryDequeue(out MessageInfo message))
             {
                 return ResultCode.NoMessages;
             }
 
+            KEvent messageEvent = context.Device.System.AppletState.MessageEvent;
+
+            // NOTE: Service checks if current states are different than the stored ones.
+            //       Since we don't support any states for now, it's fine to check if there is still messages available.
+
+            if (context.Device.System.AppletState.Messages.IsEmpty)
+            {
+                messageEvent.ReadableEvent.Clear();
+            }
+            else
+            {
+                messageEvent.ReadableEvent.Signal();
+            }
+
             context.ResponseData.Write((int)message);
 
             return ResultCode.Success;
diff --git a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/Types/MessageInfo.cs b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/Types/MessageInfo.cs
index ff699315a..0291b9bc1 100644
--- a/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/Types/MessageInfo.cs
+++ b/Ryujinx.HLE/HOS/Services/Am/AppletAE/AllSystemAppletProxiesService/SystemAppletProxy/Types/MessageInfo.cs
@@ -3,6 +3,7 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
     enum MessageInfo
     {
         FocusStateChanged      = 0xf,
+        Resume                 = 0x10,
         OperationModeChanged   = 0x1e,
         PerformanceModeChanged = 0x1f
     }
diff --git a/Ryujinx.HLE/HOS/SystemState/AppletStateMgr.cs b/Ryujinx.HLE/HOS/SystemState/AppletStateMgr.cs
index b9229b330..8fd2f08d8 100644
--- a/Ryujinx.HLE/HOS/SystemState/AppletStateMgr.cs
+++ b/Ryujinx.HLE/HOS/SystemState/AppletStateMgr.cs
@@ -6,7 +6,7 @@ namespace Ryujinx.HLE.HOS.SystemState
 {
     class AppletStateMgr
     {
-        private ConcurrentQueue<MessageInfo> _messages;
+        public ConcurrentQueue<MessageInfo> Messages { get; }
 
         public FocusState FocusState { get; private set; }
 
@@ -16,8 +16,7 @@ namespace Ryujinx.HLE.HOS.SystemState
 
         public AppletStateMgr(Horizon system)
         {
-            _messages = new ConcurrentQueue<MessageInfo>();
-
+            Messages     = new ConcurrentQueue<MessageInfo>();
             MessageEvent = new KEvent(system.KernelContext);
 
             AppletResourceUserIds = new IdDictionary();
@@ -25,28 +24,10 @@ namespace Ryujinx.HLE.HOS.SystemState
 
         public void SetFocus(bool isFocused)
         {
-            FocusState = isFocused
-                ? FocusState.InFocus
-                : FocusState.OutOfFocus;
-
-            EnqueueMessage(MessageInfo.FocusStateChanged);
-        }
-
-        public void EnqueueMessage(MessageInfo message)
-        {
-            _messages.Enqueue(message);
+            FocusState = isFocused ? FocusState.InFocus : FocusState.OutOfFocus;
 
+            Messages.Enqueue(MessageInfo.FocusStateChanged);
             MessageEvent.ReadableEvent.Signal();
         }
-
-        public bool TryDequeueMessage(out MessageInfo message)
-        {
-            if (_messages.Count < 2)
-            {
-                MessageEvent.ReadableEvent.Clear();
-            }
-
-            return _messages.TryDequeue(out message);
-        }
     }
 }
\ No newline at end of file
diff --git a/Ryujinx/Ui/MainWindow.cs b/Ryujinx/Ui/MainWindow.cs
index b54ba6249..245d4b09c 100644
--- a/Ryujinx/Ui/MainWindow.cs
+++ b/Ryujinx/Ui/MainWindow.cs
@@ -1205,6 +1205,11 @@ namespace Ryujinx.Ui
             settingsWin.Show();
         }
 
+        private void Simulate_WakeUp_Message_Pressed(object sender, EventArgs args)
+        {
+            _emulationContext.System.SimulateWakeUpMessage();
+        }
+
         private void Update_Pressed(object sender, EventArgs args)
         {
             if (Updater.CanUpdate(true))
diff --git a/Ryujinx/Ui/MainWindow.glade b/Ryujinx/Ui/MainWindow.glade
index 3b9348610..24c6d1e59 100644
--- a/Ryujinx/Ui/MainWindow.glade
+++ b/Ryujinx/Ui/MainWindow.glade
@@ -137,6 +137,16 @@
                         <signal name="activate" handler="StopEmulation_Pressed" swapped="no"/>
                       </object>
                     </child>
+                    <child>
+                      <object class="GtkMenuItem" id="SimulateWakeUpMessage">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="tooltip_text" translatable="yes">Simulate a Wake-up Message</property>
+                        <property name="label" translatable="yes">Simulate Wake-up Message</property>
+                        <property name="use_underline">True</property>
+                        <signal name="activate" handler="Simulate_WakeUp_Message_Pressed" swapped="no"/>
+                      </object>
+                    </child>
                     <child>
                       <object class="GtkSeparatorMenuItem">
                         <property name="visible">True</property>