Implement Software Keyboard GTK frontend (#1434)

* Implement SwKbd GUI

* Relocate UI handler to Emu Context from Config

Also create a common interface for UI handlers in the context and specialize for Gtk

Add basic input length validation in InputDialog

* Add Transfer Memory support to AppletCreator

Read Initial Text for SwKbd using Transfer Memory

* Improve InputDialog widget

Improve length validation
Has extra label to show validition info
Handle potential errors and log them

* Misc improvements

* Improve string validation
* Improve error handling
* Remove tuple in struct
* Address formatting nits

* Add proper Cancel functionality

Also handle GUI errors in UI handler

* Address jD's comments

* Fix _uiHandler init

* Address AcK's comments
This commit is contained in:
mageven 2020-08-03 07:00:58 +05:30 committed by GitHub
parent f0c91d9efb
commit c11855565e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 245 additions and 15 deletions

View file

@ -1,4 +1,5 @@
using Ryujinx.HLE.HOS.Applets.SoftwareKeyboard;
using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Applets.SoftwareKeyboard;
using Ryujinx.HLE.HOS.Services.Am.AppletAE;
using System;
using System.IO;
@ -9,9 +10,10 @@ namespace Ryujinx.HLE.HOS.Applets
{
internal class SoftwareKeyboardApplet : IApplet
{
private const string DefaultNumb = "1";
private const string DefaultText = "Ryujinx";
private readonly Switch _device;
private const int StandardBufferSize = 0x7D8;
private const int InteractiveBufferSize = 0x7D4;
@ -21,13 +23,18 @@ namespace Ryujinx.HLE.HOS.Applets
private AppletSession _interactiveSession;
private SoftwareKeyboardConfig _keyboardConfig;
private byte[] _transferMemory;
private string _textValue = DefaultText;
private string _textValue = null;
private bool _okPressed = false;
private Encoding _encoding = Encoding.Unicode;
public event EventHandler AppletStateChanged;
public SoftwareKeyboardApplet(Horizon system) { }
public SoftwareKeyboardApplet(Horizon system)
{
_device = system.Device;
}
public ResultCode Start(AppletSession normalSession,
AppletSession interactiveSession)
@ -39,9 +46,20 @@ namespace Ryujinx.HLE.HOS.Applets
var launchParams = _normalSession.Pop();
var keyboardConfig = _normalSession.Pop();
var transferMemory = _normalSession.Pop();
_keyboardConfig = ReadStruct<SoftwareKeyboardConfig>(keyboardConfig);
if (keyboardConfig.Length < Marshal.SizeOf<SoftwareKeyboardConfig>())
{
Logger.PrintError(LogClass.ServiceAm, $"SoftwareKeyboardConfig size mismatch. Expected {Marshal.SizeOf<SoftwareKeyboardConfig>():x}. Got {keyboardConfig.Length:x}");
}
else
{
_keyboardConfig = ReadStruct<SoftwareKeyboardConfig>(keyboardConfig);
}
if (!_normalSession.TryPop(out _transferMemory))
{
Logger.PrintError(LogClass.ServiceAm, "SwKbd Transfer Memory is null");
}
if (_keyboardConfig.UseUtf8)
{
@ -62,11 +80,13 @@ namespace Ryujinx.HLE.HOS.Applets
private void Execute()
{
// If the keyboard type is numbers only, we swap to a default
// text that only contains numbers.
if (_keyboardConfig.Mode == KeyboardMode.NumbersOnly)
string initialText = null;
// Initial Text is always encoded as a UTF-16 string in the work buffer (passed as transfer memory)
// InitialStringOffset points to the memory offset and InitialStringLength is the number of UTF-16 characters
if (_transferMemory != null && _keyboardConfig.InitialStringLength > 0)
{
_textValue = DefaultNumb;
initialText = Encoding.Unicode.GetString(_transferMemory, _keyboardConfig.InitialStringOffset, 2 * _keyboardConfig.InitialStringLength);
}
// If the max string length is 0, we set it to a large default
@ -76,6 +96,30 @@ namespace Ryujinx.HLE.HOS.Applets
_keyboardConfig.StringLengthMax = 100;
}
var args = new SoftwareKeyboardUiArgs
{
HeaderText = _keyboardConfig.HeaderText,
SubtitleText = _keyboardConfig.SubtitleText,
GuideText = _keyboardConfig.GuideText,
SubmitText = (!string.IsNullOrWhiteSpace(_keyboardConfig.SubmitText) ? _keyboardConfig.SubmitText : "OK"),
StringLengthMin = _keyboardConfig.StringLengthMin,
StringLengthMax = _keyboardConfig.StringLengthMax,
InitialText = initialText
};
// Call the configured GUI handler to get user's input
if (_device.UiHandler == null)
{
Logger.PrintWarning(LogClass.Application, $"GUI Handler is not set. Falling back to default");
_okPressed = true;
}
else
{
_okPressed = _device.UiHandler.DisplayInputDialog(args, out _textValue);
}
_textValue ??= initialText ?? DefaultText;
// If the game requests a string with a minimum length less
// than our default text, repeat our default text until we meet
// the minimum length requirement.
@ -162,7 +206,7 @@ namespace Ryujinx.HLE.HOS.Applets
if (!interactive)
{
// Result Code
writer.Write((uint)0);
writer.Write(_okPressed ? 0U : 1U);
}
else
{