diff options
Diffstat (limited to 'src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardApplet.cs')
-rw-r--r-- | src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardApplet.cs | 387 |
1 files changed, 193 insertions, 194 deletions
diff --git a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardApplet.cs b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardApplet.cs index 4337ec44..e0f6e3f0 100644 --- a/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardApplet.cs +++ b/src/Ryujinx.HLE/HOS/Applets/SoftwareKeyboard/SoftwareKeyboardApplet.cs @@ -21,16 +21,16 @@ namespace Ryujinx.HLE.HOS.Applets { private const string DefaultInputText = "Ryujinx"; - private const int StandardBufferSize = 0x7D8; + private const int StandardBufferSize = 0x7D8; private const int InteractiveBufferSize = 0x7D4; - private const int MaxUserWords = 0x1388; - private const int MaxUiTextSize = 100; + private const int MaxUserWords = 0x1388; + private const int MaxUiTextSize = 100; private const Key CycleInputModesKey = Key.F6; private readonly Switch _device; - private SoftwareKeyboardState _foregroundState = SoftwareKeyboardState.Uninitialized; + private SoftwareKeyboardState _foregroundState = SoftwareKeyboardState.Uninitialized; private volatile InlineKeyboardState _backgroundState = InlineKeyboardState.Uninitialized; private bool _isBackground = false; @@ -42,23 +42,25 @@ namespace Ryujinx.HLE.HOS.Applets private SoftwareKeyboardConfig _keyboardForegroundConfig; // Configuration for background (inline) mode. - private SoftwareKeyboardInitialize _keyboardBackgroundInitialize; +#pragma warning disable IDE0052 // Remove unread private member + private SoftwareKeyboardInitialize _keyboardBackgroundInitialize; private SoftwareKeyboardCustomizeDic _keyboardBackgroundDic; - private SoftwareKeyboardDictSet _keyboardBackgroundDictSet; - private SoftwareKeyboardUserWord[] _keyboardBackgroundUserWords; + private SoftwareKeyboardDictSet _keyboardBackgroundDictSet; +#pragma warning restore IDE0052 + private SoftwareKeyboardUserWord[] _keyboardBackgroundUserWords; private byte[] _transferMemory; - private string _textValue = ""; - private int _cursorBegin = 0; - private Encoding _encoding = Encoding.Unicode; - private KeyboardResult _lastResult = KeyboardResult.NotSet; + private string _textValue = ""; + private int _cursorBegin = 0; + private Encoding _encoding = Encoding.Unicode; + private KeyboardResult _lastResult = KeyboardResult.NotSet; private IDynamicTextInputHandler _dynamicTextInputHandler = null; - private SoftwareKeyboardRenderer _keyboardRenderer = null; - private NpadReader _npads = null; - private bool _canAcceptController = false; - private KeyboardInputMode _inputMode = KeyboardInputMode.ControllerAndKeyboard; + private SoftwareKeyboardRenderer _keyboardRenderer = null; + private NpadReader _npads = null; + private bool _canAcceptController = false; + private KeyboardInputMode _inputMode = KeyboardInputMode.ControllerAndKeyboard; private readonly object _lock = new(); @@ -73,12 +75,12 @@ namespace Ryujinx.HLE.HOS.Applets { lock (_lock) { - _normalSession = normalSession; + _normalSession = normalSession; _interactiveSession = interactiveSession; _interactiveSession.DataAvailable += OnInteractiveData; - var launchParams = _normalSession.Pop(); + var launchParams = _normalSession.Pop(); var keyboardConfig = _normalSession.Pop(); _isBackground = keyboardConfig.Length == Unsafe.SizeOf<SoftwareKeyboardInitialize>(); @@ -88,7 +90,7 @@ namespace Ryujinx.HLE.HOS.Applets // Initialize the keyboard applet in background mode. _keyboardBackgroundInitialize = MemoryMarshal.Read<SoftwareKeyboardInitialize>(keyboardConfig); - _backgroundState = InlineKeyboardState.Uninitialized; + _backgroundState = InlineKeyboardState.Uninitialized; if (_device.UiHandler == null) { @@ -99,11 +101,11 @@ namespace Ryujinx.HLE.HOS.Applets // Create a text handler that converts keyboard strokes to strings. _dynamicTextInputHandler = _device.UiHandler.CreateDynamicTextInputHandler(); _dynamicTextInputHandler.TextChangedEvent += HandleTextChangedEvent; - _dynamicTextInputHandler.KeyPressedEvent += HandleKeyPressedEvent; + _dynamicTextInputHandler.KeyPressedEvent += HandleKeyPressedEvent; _npads = new NpadReader(_device); _npads.NpadButtonDownEvent += HandleNpadButtonDownEvent; - _npads.NpadButtonUpEvent += HandleNpadButtonUpEvent; + _npads.NpadButtonUpEvent += HandleNpadButtonUpEvent; _keyboardRenderer = new SoftwareKeyboardRenderer(_device.UiHandler.HostUiTheme); } @@ -217,7 +219,7 @@ namespace Ryujinx.HLE.HOS.Applets _keyboardForegroundConfig.SubmitText : "OK"), StringLengthMin = _keyboardForegroundConfig.StringLengthMin, StringLengthMax = _keyboardForegroundConfig.StringLengthMax, - InitialText = initialText + InitialText = initialText, }; _lastResult = _device.UiHandler.DisplayInputDialog(args, out _textValue) ? KeyboardResult.Accept : KeyboardResult.Cancel; @@ -237,7 +239,7 @@ namespace Ryujinx.HLE.HOS.Applets // we truncate it. if (_textValue.Length > _keyboardForegroundConfig.StringLengthMax) { - _textValue = _textValue.Substring(0, _keyboardForegroundConfig.StringLengthMax); + _textValue = _textValue[.._keyboardForegroundConfig.StringLengthMax]; } // Does the application want to validate the text itself? @@ -319,178 +321,177 @@ namespace Ryujinx.HLE.HOS.Applets // request from the game, this is because the inline keyboard is expected to // keep running in the background sending data by itself. - using (MemoryStream stream = new MemoryStream(data)) - using (BinaryReader reader = new BinaryReader(stream)) - { - var request = (InlineKeyboardRequest)reader.ReadUInt32(); - - long remaining; + using MemoryStream stream = new(data); + using BinaryReader reader = new(stream); - Logger.Debug?.Print(LogClass.ServiceAm, $"Keyboard received command {request} in state {_backgroundState}"); + var request = (InlineKeyboardRequest)reader.ReadUInt32(); - switch (request) - { - case InlineKeyboardRequest.UseChangedStringV2: - Logger.Stub?.Print(LogClass.ServiceAm, "Inline keyboard request UseChangedStringV2"); - break; - case InlineKeyboardRequest.UseMovedCursorV2: - Logger.Stub?.Print(LogClass.ServiceAm, "Inline keyboard request UseMovedCursorV2"); - break; - case InlineKeyboardRequest.SetUserWordInfo: - // Read the user word info data. - remaining = stream.Length - stream.Position; - if (remaining < sizeof(int)) - { - Logger.Warning?.Print(LogClass.ServiceAm, $"Received invalid Software Keyboard User Word Info of {remaining} bytes"); - } - else - { - int wordsCount = reader.ReadInt32(); - int wordSize = Unsafe.SizeOf<SoftwareKeyboardUserWord>(); - remaining = stream.Length - stream.Position; + long remaining; - if (wordsCount > MaxUserWords) - { - Logger.Warning?.Print(LogClass.ServiceAm, $"Received {wordsCount} User Words but the maximum is {MaxUserWords}"); - } - else if (wordsCount * wordSize != remaining) - { - Logger.Warning?.Print(LogClass.ServiceAm, $"Received invalid Software Keyboard User Word Info data of {remaining} bytes for {wordsCount} words"); - } - else - { - _keyboardBackgroundUserWords = new SoftwareKeyboardUserWord[wordsCount]; + Logger.Debug?.Print(LogClass.ServiceAm, $"Keyboard received command {request} in state {_backgroundState}"); - for (int word = 0; word < wordsCount; word++) - { - _keyboardBackgroundUserWords[word] = reader.ReadStruct<SoftwareKeyboardUserWord>(); - } - } - } - _interactiveSession.Push(InlineResponses.ReleasedUserWordInfo(_backgroundState)); - break; - case InlineKeyboardRequest.SetCustomizeDic: - // Read the custom dic data. + switch (request) + { + case InlineKeyboardRequest.UseChangedStringV2: + Logger.Stub?.Print(LogClass.ServiceAm, "Inline keyboard request UseChangedStringV2"); + break; + case InlineKeyboardRequest.UseMovedCursorV2: + Logger.Stub?.Print(LogClass.ServiceAm, "Inline keyboard request UseMovedCursorV2"); + break; + case InlineKeyboardRequest.SetUserWordInfo: + // Read the user word info data. + remaining = stream.Length - stream.Position; + if (remaining < sizeof(int)) + { + Logger.Warning?.Print(LogClass.ServiceAm, $"Received invalid Software Keyboard User Word Info of {remaining} bytes"); + } + else + { + int wordsCount = reader.ReadInt32(); + int wordSize = Unsafe.SizeOf<SoftwareKeyboardUserWord>(); remaining = stream.Length - stream.Position; - if (remaining != Unsafe.SizeOf<SoftwareKeyboardCustomizeDic>()) + + if (wordsCount > MaxUserWords) { - Logger.Warning?.Print(LogClass.ServiceAm, $"Received invalid Software Keyboard Customize Dic of {remaining} bytes"); + Logger.Warning?.Print(LogClass.ServiceAm, $"Received {wordsCount} User Words but the maximum is {MaxUserWords}"); } - else + else if (wordsCount * wordSize != remaining) { - _keyboardBackgroundDic = reader.ReadStruct<SoftwareKeyboardCustomizeDic>(); - } - break; - case InlineKeyboardRequest.SetCustomizedDictionaries: - // Read the custom dictionaries data. - remaining = stream.Length - stream.Position; - if (remaining != Unsafe.SizeOf<SoftwareKeyboardDictSet>()) - { - Logger.Warning?.Print(LogClass.ServiceAm, $"Received invalid Software Keyboard DictSet of {remaining} bytes"); + Logger.Warning?.Print(LogClass.ServiceAm, $"Received invalid Software Keyboard User Word Info data of {remaining} bytes for {wordsCount} words"); } else { - _keyboardBackgroundDictSet = reader.ReadStruct<SoftwareKeyboardDictSet>(); - } - break; - case InlineKeyboardRequest.Calc: - // The Calc request is used to communicate configuration changes and commands to the keyboard. - // Fields in the Calc struct and operations are masked by the Flags field. + _keyboardBackgroundUserWords = new SoftwareKeyboardUserWord[wordsCount]; - // Read the Calc data. - SoftwareKeyboardCalcEx newCalc; - remaining = stream.Length - stream.Position; - if (remaining == Marshal.SizeOf<SoftwareKeyboardCalc>()) - { - var keyboardCalcData = reader.ReadBytes((int)remaining); - var keyboardCalc = ReadStruct<SoftwareKeyboardCalc>(keyboardCalcData); - - newCalc = keyboardCalc.ToExtended(); + for (int word = 0; word < wordsCount; word++) + { + _keyboardBackgroundUserWords[word] = reader.ReadStruct<SoftwareKeyboardUserWord>(); + } } - else if (remaining == Marshal.SizeOf<SoftwareKeyboardCalcEx>() || remaining == SoftwareKeyboardCalcEx.AlternativeSize) - { - var keyboardCalcData = reader.ReadBytes((int)remaining); + } + _interactiveSession.Push(InlineResponses.ReleasedUserWordInfo(_backgroundState)); + break; + case InlineKeyboardRequest.SetCustomizeDic: + // Read the custom dic data. + remaining = stream.Length - stream.Position; + if (remaining != Unsafe.SizeOf<SoftwareKeyboardCustomizeDic>()) + { + Logger.Warning?.Print(LogClass.ServiceAm, $"Received invalid Software Keyboard Customize Dic of {remaining} bytes"); + } + else + { + _keyboardBackgroundDic = reader.ReadStruct<SoftwareKeyboardCustomizeDic>(); + } + break; + case InlineKeyboardRequest.SetCustomizedDictionaries: + // Read the custom dictionaries data. + remaining = stream.Length - stream.Position; + if (remaining != Unsafe.SizeOf<SoftwareKeyboardDictSet>()) + { + Logger.Warning?.Print(LogClass.ServiceAm, $"Received invalid Software Keyboard DictSet of {remaining} bytes"); + } + else + { + _keyboardBackgroundDictSet = reader.ReadStruct<SoftwareKeyboardDictSet>(); + } + break; + case InlineKeyboardRequest.Calc: + // The Calc request is used to communicate configuration changes and commands to the keyboard. + // Fields in the Calc struct and operations are masked by the Flags field. + + // Read the Calc data. + SoftwareKeyboardCalcEx newCalc; + remaining = stream.Length - stream.Position; + if (remaining == Marshal.SizeOf<SoftwareKeyboardCalc>()) + { + var keyboardCalcData = reader.ReadBytes((int)remaining); + var keyboardCalc = ReadStruct<SoftwareKeyboardCalc>(keyboardCalcData); - newCalc = ReadStruct<SoftwareKeyboardCalcEx>(keyboardCalcData); - } - else - { - Logger.Error?.Print(LogClass.ServiceAm, $"Received invalid Software Keyboard Calc of {remaining} bytes"); + newCalc = keyboardCalc.ToExtended(); + } + else if (remaining == Marshal.SizeOf<SoftwareKeyboardCalcEx>() || remaining == SoftwareKeyboardCalcEx.AlternativeSize) + { + var keyboardCalcData = reader.ReadBytes((int)remaining); - newCalc = new SoftwareKeyboardCalcEx(); - } + newCalc = ReadStruct<SoftwareKeyboardCalcEx>(keyboardCalcData); + } + else + { + Logger.Error?.Print(LogClass.ServiceAm, $"Received invalid Software Keyboard Calc of {remaining} bytes"); - // Process each individual operation specified in the flags. + newCalc = new SoftwareKeyboardCalcEx(); + } - bool updateText = false; + // Process each individual operation specified in the flags. - if ((newCalc.Flags & KeyboardCalcFlags.Initialize) != 0) - { - _interactiveSession.Push(InlineResponses.FinishedInitialize(_backgroundState)); + bool updateText = false; - _backgroundState = InlineKeyboardState.Initialized; - } + if ((newCalc.Flags & KeyboardCalcFlags.Initialize) != 0) + { + _interactiveSession.Push(InlineResponses.FinishedInitialize(_backgroundState)); - if ((newCalc.Flags & KeyboardCalcFlags.SetCursorPos) != 0) - { - _cursorBegin = newCalc.CursorPos; - updateText = true; + _backgroundState = InlineKeyboardState.Initialized; + } - Logger.Debug?.Print(LogClass.ServiceAm, $"Cursor position set to {_cursorBegin}"); - } + if ((newCalc.Flags & KeyboardCalcFlags.SetCursorPos) != 0) + { + _cursorBegin = newCalc.CursorPos; + updateText = true; - if ((newCalc.Flags & KeyboardCalcFlags.SetInputText) != 0) - { - _textValue = newCalc.InputText; - updateText = true; + Logger.Debug?.Print(LogClass.ServiceAm, $"Cursor position set to {_cursorBegin}"); + } - Logger.Debug?.Print(LogClass.ServiceAm, $"Input text set to {_textValue}"); - } + if ((newCalc.Flags & KeyboardCalcFlags.SetInputText) != 0) + { + _textValue = newCalc.InputText; + updateText = true; - if ((newCalc.Flags & KeyboardCalcFlags.SetUtf8Mode) != 0) - { - _encoding = newCalc.UseUtf8 ? Encoding.UTF8 : Encoding.Default; + Logger.Debug?.Print(LogClass.ServiceAm, $"Input text set to {_textValue}"); + } - Logger.Debug?.Print(LogClass.ServiceAm, $"Encoding set to {_encoding}"); - } + if ((newCalc.Flags & KeyboardCalcFlags.SetUtf8Mode) != 0) + { + _encoding = newCalc.UseUtf8 ? Encoding.UTF8 : Encoding.Default; - if (updateText) - { - _dynamicTextInputHandler.SetText(_textValue, _cursorBegin); - _keyboardRenderer.UpdateTextState(_textValue, _cursorBegin, _cursorBegin, null, null); - } + Logger.Debug?.Print(LogClass.ServiceAm, $"Encoding set to {_encoding}"); + } - if ((newCalc.Flags & KeyboardCalcFlags.MustShow) != 0) - { - ActivateFrontend(); + if (updateText) + { + _dynamicTextInputHandler.SetText(_textValue, _cursorBegin); + _keyboardRenderer.UpdateTextState(_textValue, _cursorBegin, _cursorBegin, null, null); + } - _backgroundState = InlineKeyboardState.Shown; + if ((newCalc.Flags & KeyboardCalcFlags.MustShow) != 0) + { + ActivateFrontend(); - PushChangedString(_textValue, (uint)_cursorBegin, _backgroundState); - } + _backgroundState = InlineKeyboardState.Shown; - // Send the response to the Calc - _interactiveSession.Push(InlineResponses.Default(_backgroundState)); - break; - case InlineKeyboardRequest.Finalize: - // Destroy the frontend. - DestroyFrontend(); - // The calling application wants to close the keyboard applet and will wait for a state change. - _backgroundState = InlineKeyboardState.Uninitialized; - AppletStateChanged?.Invoke(this, null); - break; - default: - // We shouldn't be able to get here through standard swkbd execution. - Logger.Warning?.Print(LogClass.ServiceAm, $"Invalid Software Keyboard request {request} during state {_backgroundState}"); - _interactiveSession.Push(InlineResponses.Default(_backgroundState)); - break; - } + PushChangedString(_textValue, (uint)_cursorBegin, _backgroundState); + } + + // Send the response to the Calc + _interactiveSession.Push(InlineResponses.Default(_backgroundState)); + break; + case InlineKeyboardRequest.Finalize: + // Destroy the frontend. + DestroyFrontend(); + // The calling application wants to close the keyboard applet and will wait for a state change. + _backgroundState = InlineKeyboardState.Uninitialized; + AppletStateChanged?.Invoke(this, null); + break; + default: + // We shouldn't be able to get here through standard swkbd execution. + Logger.Warning?.Print(LogClass.ServiceAm, $"Invalid Software Keyboard request {request} during state {_backgroundState}"); + _interactiveSession.Push(InlineResponses.Default(_backgroundState)); + break; } } private void ActivateFrontend() { - Logger.Debug?.Print(LogClass.ServiceAm, $"Activating software keyboard frontend"); + Logger.Debug?.Print(LogClass.ServiceAm, "Activating software keyboard frontend"); _inputMode = KeyboardInputMode.ControllerAndKeyboard; @@ -509,9 +510,9 @@ namespace Ryujinx.HLE.HOS.Applets private void DeactivateFrontend() { - Logger.Debug?.Print(LogClass.ServiceAm, $"Deactivating software keyboard frontend"); + Logger.Debug?.Print(LogClass.ServiceAm, "Deactivating software keyboard frontend"); - _inputMode = KeyboardInputMode.ControllerAndKeyboard; + _inputMode = KeyboardInputMode.ControllerAndKeyboard; _canAcceptController = false; _dynamicTextInputHandler.TextProcessingEnabled = false; @@ -520,7 +521,7 @@ namespace Ryujinx.HLE.HOS.Applets private void DestroyFrontend() { - Logger.Debug?.Print(LogClass.ServiceAm, $"Destroying software keyboard frontend"); + Logger.Debug?.Print(LogClass.ServiceAm, "Destroying software keyboard frontend"); _keyboardRenderer?.Dispose(); _keyboardRenderer = null; @@ -528,7 +529,7 @@ namespace Ryujinx.HLE.HOS.Applets if (_dynamicTextInputHandler != null) { _dynamicTextInputHandler.TextChangedEvent -= HandleTextChangedEvent; - _dynamicTextInputHandler.KeyPressedEvent -= HandleKeyPressedEvent; + _dynamicTextInputHandler.KeyPressedEvent -= HandleKeyPressedEvent; _dynamicTextInputHandler.Dispose(); _dynamicTextInputHandler = null; } @@ -536,7 +537,7 @@ namespace Ryujinx.HLE.HOS.Applets if (_npads != null) { _npads.NpadButtonDownEvent -= HandleNpadButtonDownEvent; - _npads.NpadButtonUpEvent -= HandleNpadButtonUpEvent; + _npads.NpadButtonUpEvent -= HandleNpadButtonUpEvent; _npads = null; } } @@ -551,7 +552,7 @@ namespace Ryujinx.HLE.HOS.Applets { AdvanceInputMode(); - bool typingEnabled = InputModeTypingEnabled(); + bool typingEnabled = InputModeTypingEnabled(); bool controllerEnabled = InputModeControllerEnabled(); _dynamicTextInputHandler.TextProcessingEnabled = typingEnabled; @@ -575,14 +576,14 @@ namespace Ryujinx.HLE.HOS.Applets if (text.Length > MaxUiTextSize) { // Limit the text size and change it back. - text = text.Substring(0, MaxUiTextSize); + text = text[..MaxUiTextSize]; cursorBegin = Math.Min(cursorBegin, MaxUiTextSize); - cursorEnd = Math.Min(cursorEnd, MaxUiTextSize); + cursorEnd = Math.Min(cursorEnd, MaxUiTextSize); _dynamicTextInputHandler.SetText(text, cursorBegin, cursorEnd); } - _textValue = text; + _textValue = text; _cursorBegin = cursorBegin; _keyboardRenderer.UpdateTextState(text, cursorBegin, cursorEnd, overwriteMode, null); @@ -646,7 +647,7 @@ namespace Ryujinx.HLE.HOS.Applets private void PushUpdatedState(string text, int cursorBegin, KeyboardResult result) { _lastResult = result; - _textValue = text; + _textValue = text; bool cancel = result == KeyboardResult.Cancel; bool accept = result == KeyboardResult.Accept; @@ -734,33 +735,31 @@ namespace Ryujinx.HLE.HOS.Applets { int bufferSize = interactive ? InteractiveBufferSize : StandardBufferSize; - using (MemoryStream stream = new MemoryStream(new byte[bufferSize])) - using (BinaryWriter writer = new BinaryWriter(stream)) - { - byte[] output = _encoding.GetBytes(_textValue); + using MemoryStream stream = new(new byte[bufferSize]); + using BinaryWriter writer = new(stream); + byte[] output = _encoding.GetBytes(_textValue); - if (!interactive) - { - // Result Code. - writer.Write(_lastResult == KeyboardResult.Accept ? 0U : 1U); - } - else - { - // In interactive mode, we write the length of the text as a long, rather than - // a result code. This field is inclusive of the 64-bit size. - writer.Write((long)output.Length + 8); - } + if (!interactive) + { + // Result Code. + writer.Write(_lastResult == KeyboardResult.Accept ? 0U : 1U); + } + else + { + // In interactive mode, we write the length of the text as a long, rather than + // a result code. This field is inclusive of the 64-bit size. + writer.Write((long)output.Length + 8); + } - writer.Write(output); + writer.Write(output); - if (!interactive) - { - _normalSession.Push(stream.ToArray()); - } - else - { - _interactiveSession.Push(stream.ToArray()); - } + if (!interactive) + { + _normalSession.Push(stream.ToArray()); + } + else + { + _interactiveSession.Push(stream.ToArray()); } } @@ -787,7 +786,7 @@ namespace Ryujinx.HLE.HOS.Applets return string.Empty; } - StringBuilder sb = new StringBuilder(capacity: input.Length); + StringBuilder sb = new(capacity: input.Length); foreach (char c in input) { if (!char.IsControl(c)) |