aboutsummaryrefslogtreecommitdiff
path: root/src/Ryujinx.HLE/UI
diff options
context:
space:
mode:
Diffstat (limited to 'src/Ryujinx.HLE/UI')
-rw-r--r--src/Ryujinx.HLE/UI/DynamicTextChangedHandler.cs4
-rw-r--r--src/Ryujinx.HLE/UI/IDynamicTextInputHandler.cs16
-rw-r--r--src/Ryujinx.HLE/UI/IHostUIHandler.cs51
-rw-r--r--src/Ryujinx.HLE/UI/IHostUITheme.cs13
-rw-r--r--src/Ryujinx.HLE/UI/Input/NpadButtonHandler.cs6
-rw-r--r--src/Ryujinx.HLE/UI/Input/NpadReader.cs140
-rw-r--r--src/Ryujinx.HLE/UI/KeyPressedHandler.cs6
-rw-r--r--src/Ryujinx.HLE/UI/KeyReleasedHandler.cs6
-rw-r--r--src/Ryujinx.HLE/UI/RenderingSurfaceInfo.cs45
-rw-r--r--src/Ryujinx.HLE/UI/ThemeColor.cs18
10 files changed, 305 insertions, 0 deletions
diff --git a/src/Ryujinx.HLE/UI/DynamicTextChangedHandler.cs b/src/Ryujinx.HLE/UI/DynamicTextChangedHandler.cs
new file mode 100644
index 00000000..c0945259
--- /dev/null
+++ b/src/Ryujinx.HLE/UI/DynamicTextChangedHandler.cs
@@ -0,0 +1,4 @@
+namespace Ryujinx.HLE.UI
+{
+ public delegate void DynamicTextChangedHandler(string text, int cursorBegin, int cursorEnd, bool overwriteMode);
+}
diff --git a/src/Ryujinx.HLE/UI/IDynamicTextInputHandler.cs b/src/Ryujinx.HLE/UI/IDynamicTextInputHandler.cs
new file mode 100644
index 00000000..1ff451d1
--- /dev/null
+++ b/src/Ryujinx.HLE/UI/IDynamicTextInputHandler.cs
@@ -0,0 +1,16 @@
+using System;
+
+namespace Ryujinx.HLE.UI
+{
+ public interface IDynamicTextInputHandler : IDisposable
+ {
+ event DynamicTextChangedHandler TextChangedEvent;
+ event KeyPressedHandler KeyPressedEvent;
+ event KeyReleasedHandler KeyReleasedEvent;
+
+ bool TextProcessingEnabled { get; set; }
+
+ void SetText(string text, int cursorBegin);
+ void SetText(string text, int cursorBegin, int cursorEnd);
+ }
+}
diff --git a/src/Ryujinx.HLE/UI/IHostUIHandler.cs b/src/Ryujinx.HLE/UI/IHostUIHandler.cs
new file mode 100644
index 00000000..3b3a430e
--- /dev/null
+++ b/src/Ryujinx.HLE/UI/IHostUIHandler.cs
@@ -0,0 +1,51 @@
+using Ryujinx.HLE.HOS.Applets;
+using Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.ApplicationProxy.Types;
+
+namespace Ryujinx.HLE.UI
+{
+ public interface IHostUIHandler
+ {
+ /// <summary>
+ /// Displays an Input Dialog box to the user and blocks until text is entered.
+ /// </summary>
+ /// <param name="userText">Text that the user entered. Set to `null` on internal errors</param>
+ /// <returns>True when OK is pressed, False otherwise. Also returns True on internal errors</returns>
+ bool DisplayInputDialog(SoftwareKeyboardUIArgs args, out string userText);
+
+ /// <summary>
+ /// Displays a Message Dialog box to the user and blocks until it is closed.
+ /// </summary>
+ /// <returns>True when OK is pressed, False otherwise.</returns>
+ bool DisplayMessageDialog(string title, string message);
+
+ /// <summary>
+ /// Displays a Message Dialog box specific to Controller Applet and blocks until it is closed.
+ /// </summary>
+ /// <returns>True when OK is pressed, False otherwise.</returns>
+ bool DisplayMessageDialog(ControllerAppletUIArgs args);
+
+ /// <summary>
+ /// Tell the UI that we need to transisition to another program.
+ /// </summary>
+ /// <param name="device">The device instance.</param>
+ /// <param name="kind">The program kind.</param>
+ /// <param name="value">The value associated to the <paramref name="kind"/>.</param>
+ void ExecuteProgram(Switch device, ProgramSpecifyKind kind, ulong value);
+
+ /// Displays a Message Dialog box specific to Error Applet and blocks until it is closed.
+ /// </summary>
+ /// <returns>False when OK is pressed, True when another button (Details) is pressed.</returns>
+ bool DisplayErrorAppletDialog(string title, string message, string[] buttonsText);
+
+ /// <summary>
+ /// Creates a handler to process keyboard inputs into text strings.
+ /// </summary>
+ /// <returns>An instance of the text handler.</returns>
+ IDynamicTextInputHandler CreateDynamicTextInputHandler();
+
+ /// <summary>
+ /// Gets fonts and colors used by the host.
+ /// </summary>
+ IHostUITheme HostUITheme { get; }
+ }
+}
diff --git a/src/Ryujinx.HLE/UI/IHostUITheme.cs b/src/Ryujinx.HLE/UI/IHostUITheme.cs
new file mode 100644
index 00000000..3b054400
--- /dev/null
+++ b/src/Ryujinx.HLE/UI/IHostUITheme.cs
@@ -0,0 +1,13 @@
+namespace Ryujinx.HLE.UI
+{
+ public interface IHostUITheme
+ {
+ string FontFamily { get; }
+
+ ThemeColor DefaultBackgroundColor { get; }
+ ThemeColor DefaultForegroundColor { get; }
+ ThemeColor DefaultBorderColor { get; }
+ ThemeColor SelectionBackgroundColor { get; }
+ ThemeColor SelectionForegroundColor { get; }
+ }
+}
diff --git a/src/Ryujinx.HLE/UI/Input/NpadButtonHandler.cs b/src/Ryujinx.HLE/UI/Input/NpadButtonHandler.cs
new file mode 100644
index 00000000..73c30661
--- /dev/null
+++ b/src/Ryujinx.HLE/UI/Input/NpadButtonHandler.cs
@@ -0,0 +1,6 @@
+using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad;
+
+namespace Ryujinx.HLE.UI.Input
+{
+ delegate void NpadButtonHandler(int npadIndex, NpadButton button);
+}
diff --git a/src/Ryujinx.HLE/UI/Input/NpadReader.cs b/src/Ryujinx.HLE/UI/Input/NpadReader.cs
new file mode 100644
index 00000000..8276d616
--- /dev/null
+++ b/src/Ryujinx.HLE/UI/Input/NpadReader.cs
@@ -0,0 +1,140 @@
+using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common;
+using Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Npad;
+
+namespace Ryujinx.HLE.UI.Input
+{
+ /// <summary>
+ /// Class that converts Hid entries for the Npad into pressed / released events.
+ /// </summary>
+ class NpadReader
+ {
+ private readonly Switch _device;
+ private readonly NpadCommonState[] _lastStates;
+
+ public event NpadButtonHandler NpadButtonUpEvent;
+ public event NpadButtonHandler NpadButtonDownEvent;
+
+ public NpadReader(Switch device)
+ {
+ _device = device;
+ _lastStates = new NpadCommonState[_device.Hid.SharedMemory.Npads.Length];
+ }
+
+ public NpadButton GetCurrentButtonsOfNpad(int npadIndex)
+ {
+ return _lastStates[npadIndex].Buttons;
+ }
+
+ public NpadButton GetCurrentButtonsOfAllNpads()
+ {
+ NpadButton buttons = 0;
+
+ foreach (var state in _lastStates)
+ {
+ buttons |= state.Buttons;
+ }
+
+ return buttons;
+ }
+
+ private static ref RingLifo<NpadCommonState> GetCommonStateLifo(ref NpadInternalState npad)
+ {
+ switch (npad.StyleSet)
+ {
+ case NpadStyleTag.FullKey:
+ return ref npad.FullKey;
+ case NpadStyleTag.Handheld:
+ return ref npad.Handheld;
+ case NpadStyleTag.JoyDual:
+ return ref npad.JoyDual;
+ case NpadStyleTag.JoyLeft:
+ return ref npad.JoyLeft;
+ case NpadStyleTag.JoyRight:
+ return ref npad.JoyRight;
+ case NpadStyleTag.Palma:
+ return ref npad.Palma;
+ default:
+ return ref npad.SystemExt;
+ }
+ }
+
+ public void Update(bool supressEvents = false)
+ {
+ ref var npads = ref _device.Hid.SharedMemory.Npads;
+
+ // Process each input individually.
+ for (int npadIndex = 0; npadIndex < npads.Length; npadIndex++)
+ {
+ UpdateNpad(npadIndex, supressEvents);
+ }
+ }
+
+ private void UpdateNpad(int npadIndex, bool supressEvents)
+ {
+ const int MaxEntries = 1024;
+
+ ref var npadState = ref _device.Hid.SharedMemory.Npads[npadIndex];
+ ref var lastEntry = ref _lastStates[npadIndex];
+
+ var fullKeyEntries = GetCommonStateLifo(ref npadState.InternalState).ReadEntries(MaxEntries);
+
+ int firstEntryNum;
+
+ // Scan the LIFO for the first entry that is newer that what's already processed.
+ for (firstEntryNum = fullKeyEntries.Length - 1;
+ firstEntryNum >= 0 && fullKeyEntries[firstEntryNum].Object.SamplingNumber <= lastEntry.SamplingNumber;
+ firstEntryNum--)
+ {
+ }
+
+ if (firstEntryNum == -1)
+ {
+ return;
+ }
+
+ for (; firstEntryNum >= 0; firstEntryNum--)
+ {
+ var entry = fullKeyEntries[firstEntryNum];
+
+ // The interval of valid entries should be contiguous.
+ if (entry.SamplingNumber < lastEntry.SamplingNumber)
+ {
+ break;
+ }
+
+ if (!supressEvents)
+ {
+ ProcessNpadButtons(npadIndex, entry.Object.Buttons);
+ }
+
+ lastEntry = entry.Object;
+ }
+ }
+
+ private void ProcessNpadButtons(int npadIndex, NpadButton buttons)
+ {
+ NpadButton lastButtons = _lastStates[npadIndex].Buttons;
+
+ for (ulong buttonMask = 1; buttonMask != 0; buttonMask <<= 1)
+ {
+ NpadButton currentButton = (NpadButton)buttonMask & buttons;
+ NpadButton lastButton = (NpadButton)buttonMask & lastButtons;
+
+ if (lastButton != 0)
+ {
+ if (currentButton == 0)
+ {
+ NpadButtonUpEvent?.Invoke(npadIndex, lastButton);
+ }
+ }
+ else
+ {
+ if (currentButton != 0)
+ {
+ NpadButtonDownEvent?.Invoke(npadIndex, currentButton);
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/Ryujinx.HLE/UI/KeyPressedHandler.cs b/src/Ryujinx.HLE/UI/KeyPressedHandler.cs
new file mode 100644
index 00000000..6feb11bd
--- /dev/null
+++ b/src/Ryujinx.HLE/UI/KeyPressedHandler.cs
@@ -0,0 +1,6 @@
+using Ryujinx.Common.Configuration.Hid;
+
+namespace Ryujinx.HLE.UI
+{
+ public delegate bool KeyPressedHandler(Key key);
+}
diff --git a/src/Ryujinx.HLE/UI/KeyReleasedHandler.cs b/src/Ryujinx.HLE/UI/KeyReleasedHandler.cs
new file mode 100644
index 00000000..3de89d0c
--- /dev/null
+++ b/src/Ryujinx.HLE/UI/KeyReleasedHandler.cs
@@ -0,0 +1,6 @@
+using Ryujinx.Common.Configuration.Hid;
+
+namespace Ryujinx.HLE.UI
+{
+ public delegate bool KeyReleasedHandler(Key key);
+}
diff --git a/src/Ryujinx.HLE/UI/RenderingSurfaceInfo.cs b/src/Ryujinx.HLE/UI/RenderingSurfaceInfo.cs
new file mode 100644
index 00000000..af0a0d44
--- /dev/null
+++ b/src/Ryujinx.HLE/UI/RenderingSurfaceInfo.cs
@@ -0,0 +1,45 @@
+using Ryujinx.HLE.HOS.Services.SurfaceFlinger;
+using System;
+
+namespace Ryujinx.HLE.UI
+{
+ /// <summary>
+ /// Information about the indirect layer that is being drawn to.
+ /// </summary>
+ class RenderingSurfaceInfo : IEquatable<RenderingSurfaceInfo>
+ {
+ public ColorFormat ColorFormat { get; }
+ public uint Width { get; }
+ public uint Height { get; }
+ public uint Pitch { get; }
+ public uint Size { get; }
+
+ public RenderingSurfaceInfo(ColorFormat colorFormat, uint width, uint height, uint pitch, uint size)
+ {
+ ColorFormat = colorFormat;
+ Width = width;
+ Height = height;
+ Pitch = pitch;
+ Size = size;
+ }
+
+ public bool Equals(RenderingSurfaceInfo other)
+ {
+ return ColorFormat == other.ColorFormat &&
+ Width == other.Width &&
+ Height == other.Height &&
+ Pitch == other.Pitch &&
+ Size == other.Size;
+ }
+
+ public override bool Equals(object obj)
+ {
+ return obj is RenderingSurfaceInfo info && Equals(info);
+ }
+
+ public override int GetHashCode()
+ {
+ return BitConverter.ToInt32(BitConverter.GetBytes(((ulong)ColorFormat) ^ Width ^ Height ^ Pitch ^ Size));
+ }
+ }
+}
diff --git a/src/Ryujinx.HLE/UI/ThemeColor.cs b/src/Ryujinx.HLE/UI/ThemeColor.cs
new file mode 100644
index 00000000..c5cfb147
--- /dev/null
+++ b/src/Ryujinx.HLE/UI/ThemeColor.cs
@@ -0,0 +1,18 @@
+namespace Ryujinx.HLE.UI
+{
+ public readonly struct ThemeColor
+ {
+ public float A { get; }
+ public float R { get; }
+ public float G { get; }
+ public float B { get; }
+
+ public ThemeColor(float a, float r, float g, float b)
+ {
+ A = a;
+ R = r;
+ G = g;
+ B = b;
+ }
+ }
+}