diff options
Diffstat (limited to 'src/Ryujinx.HLE/UI')
-rw-r--r-- | src/Ryujinx.HLE/UI/DynamicTextChangedHandler.cs | 4 | ||||
-rw-r--r-- | src/Ryujinx.HLE/UI/IDynamicTextInputHandler.cs | 16 | ||||
-rw-r--r-- | src/Ryujinx.HLE/UI/IHostUIHandler.cs | 51 | ||||
-rw-r--r-- | src/Ryujinx.HLE/UI/IHostUITheme.cs | 13 | ||||
-rw-r--r-- | src/Ryujinx.HLE/UI/Input/NpadButtonHandler.cs | 6 | ||||
-rw-r--r-- | src/Ryujinx.HLE/UI/Input/NpadReader.cs | 140 | ||||
-rw-r--r-- | src/Ryujinx.HLE/UI/KeyPressedHandler.cs | 6 | ||||
-rw-r--r-- | src/Ryujinx.HLE/UI/KeyReleasedHandler.cs | 6 | ||||
-rw-r--r-- | src/Ryujinx.HLE/UI/RenderingSurfaceInfo.cs | 45 | ||||
-rw-r--r-- | src/Ryujinx.HLE/UI/ThemeColor.cs | 18 |
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; + } + } +} |