Add Software Keyboard, Edit maxSets and more

This commit is contained in:
Stossy11 2025-02-11 20:22:50 +11:00
parent c3ade6f5cd
commit eb4a4593ea
28 changed files with 283 additions and 1709 deletions

View File

@ -67,6 +67,7 @@
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
4E7023A52D5A98E2002C7183 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
4E80A98D2CD6F54500029585 /* MeloNX.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MeloNX.app; sourceTree = BUILT_PRODUCTS_DIR; };
4E80A99D2CD6F54700029585 /* MeloNXTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MeloNXTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
4E80A9A72CD6F54700029585 /* MeloNXUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MeloNXUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
@ -96,7 +97,7 @@
"Dependencies/Dynamic Libraries/Ryujinx.Headless.SDL2.dylib" = (
CodeSignOnCopy,
);
"Dependencies/Dynamic Libraries/SoftwareKeyboard.framework" = (
"Dependencies/Dynamic Libraries/RyujinxKeyboard.framework" = (
CodeSignOnCopy,
RemoveHeadersOnCopy,
);
@ -157,7 +158,7 @@
"Dependencies/Dynamic Libraries/libavutil.dylib",
"Dependencies/Dynamic Libraries/libMoltenVK.dylib",
"Dependencies/Dynamic Libraries/Ryujinx.Headless.SDL2.dylib",
"Dependencies/Dynamic Libraries/SoftwareKeyboard.framework",
"Dependencies/Dynamic Libraries/RyujinxKeyboard.framework",
Dependencies/XCFrameworks/libavcodec.xcframework,
Dependencies/XCFrameworks/libavfilter.xcframework,
Dependencies/XCFrameworks/libavformat.xcframework,
@ -232,6 +233,7 @@
4E80AA192CD700F500029585 /* Frameworks */ = {
isa = PBXGroup;
children = (
4E7023A52D5A98E2002C7183 /* UIKit.framework */,
4E80AA622CD7122800029585 /* GameController.framework */,
);
name = Frameworks;
@ -634,6 +636,10 @@
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
);
GCC_OPTIMIZATION_LEVEL = fast;
GENERATE_INFOPLIST_FILE = YES;
@ -677,6 +683,10 @@
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
);
MARKETING_VERSION = 0.0.8;
PRODUCT_BUNDLE_IDENTIFIER = com.stossy11.MeloNX;
@ -713,6 +723,10 @@
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
);
GCC_OPTIMIZATION_LEVEL = fast;
GENERATE_INFOPLIST_FILE = YES;
@ -756,6 +770,10 @@
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
"$(PROJECT_DIR)/MeloNX/Dependencies/Dynamic\\ Libraries",
);
MARKETING_VERSION = 0.0.8;
PRODUCT_BUNDLE_IDENTIFIER = com.stossy11.MeloNX;

View File

@ -12,12 +12,12 @@
<key>Ryujinx.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>2</integer>
<integer>1</integer>
</dict>
<key>com.Stossy11.MeloNX.RyujinxAg.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>1</integer>
<integer>2</integer>
</dict>
</dict>
<key>SuppressBuildableAutocreation</key>

View File

@ -5,7 +5,7 @@
// Created by Stossy11 on 3/11/2024.
//
#define DRM 1
#define DRM 0
#define CS_DEBUGGED 0x10000000
#ifndef RyujinxHeader

View File

@ -61,8 +61,8 @@ struct ContentView: View {
// Metal Private API isn't needed and causes more stutters
MoltenVKSettings(string: "MVK_USE_METAL_PRIVATE_API", value: "1"),
MoltenVKSettings(string: "MVK_CONFIG_USE_METAL_PRIVATE_API", value: "1"),
MoltenVKSettings(string: "MVK_DEBUG", value: "1"),
MoltenVKSettings(string: "MVK_CONFIG_LOG_LEVEL", value: "2"),
MoltenVKSettings(string: "MVK_DEBUG", value: "0"),
// MoltenVKSettings(string: "MVK_CONFIG_LOG_LEVEL", value: "0"),
// MVK_CONFIG_LOG_LEVEL
//MVK_DEBUG
// Uses more ram but makes performance higher, may add an option in settings to change or enable / disable this value (default 64 or 192 depending on what i decide)
@ -116,6 +116,8 @@ struct ContentView: View {
.onAppear() {
quits = false
initControllerObservers() // This initializes the Controller Observers that refreshes the controller list when a new controller connecvts.
}
.onOpenURL() { url in

View File

@ -195,7 +195,11 @@ struct GameLibraryView: View {
Button {
let documentsUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let sharedurl = documentsUrl.absoluteString.replacingOccurrences(of: "file://", with: "shareddocuments://")
var sharedurl = documentsUrl.absoluteString.replacingOccurrences(of: "file://", with: "shareddocuments://")
if ProcessInfo.processInfo.isiOSAppOnMac {
sharedurl = documentsUrl.absoluteString
}
print(sharedurl)
let furl = URL(string: sharedurl)!
if UIApplication.shared.canOpenURL(furl) {
UIApplication.shared.open(furl, options: [:])

View File

@ -0,0 +1,18 @@
//
// RyujinxKeyboard.h
// RyujinxKeyboard
//
// Created by Stossy11 on 11/02/2025.
//
#import <Foundation/Foundation.h>
//! Project version number for RyujinxKeyboard.
FOUNDATION_EXPORT double RyujinxKeyboardVersionNumber;
//! Project version string for RyujinxKeyboard.
FOUNDATION_EXPORT const unsigned char RyujinxKeyboardVersionString[];
// In this header, you should import all the public headers of your framework using statements like #import <RyujinxKeyboard/PublicHeader.h>

View File

@ -0,0 +1,6 @@
framework module RyujinxKeyboard {
umbrella header "RyujinxKeyboard.h"
export *
module * { export * }
}

View File

@ -0,0 +1,124 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>files</key>
<dict>
<key>Headers/RyujinxKeyboard.h</key>
<data>
5P7GN4g050n199pV6/+SpfMBgJc=
</data>
<key>Info.plist</key>
<data>
hYdI/ktAKwjBSfaJpt6Yc8UKLCY=
</data>
<key>Modules/module.modulemap</key>
<data>
0kFAMoTn+4Q1J/dM6uMLe3EhbL0=
</data>
</dict>
<key>files2</key>
<dict>
<key>Headers/RyujinxKeyboard.h</key>
<dict>
<key>hash2</key>
<data>
/yGmHq9NdBF/ruesISIj7vml0ySgoJkrFOcrw0vaIxQ=
</data>
</dict>
<key>Modules/module.modulemap</key>
<dict>
<key>hash2</key>
<data>
K+ZyxKhTI4bMVZuHBIspvd2PFqvCOlVUFYmwF96O5NQ=
</data>
</dict>
</dict>
<key>rules</key>
<dict>
<key>^.*</key>
<true/>
<key>^.*\.lproj/</key>
<dict>
<key>optional</key>
<true/>
<key>weight</key>
<real>1000</real>
</dict>
<key>^.*\.lproj/locversion.plist$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>1100</real>
</dict>
<key>^Base\.lproj/</key>
<dict>
<key>weight</key>
<real>1010</real>
</dict>
<key>^version.plist$</key>
<true/>
</dict>
<key>rules2</key>
<dict>
<key>.*\.dSYM($|/)</key>
<dict>
<key>weight</key>
<real>11</real>
</dict>
<key>^(.*/)?\.DS_Store$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>2000</real>
</dict>
<key>^.*</key>
<true/>
<key>^.*\.lproj/</key>
<dict>
<key>optional</key>
<true/>
<key>weight</key>
<real>1000</real>
</dict>
<key>^.*\.lproj/locversion.plist$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>1100</real>
</dict>
<key>^Base\.lproj/</key>
<dict>
<key>weight</key>
<real>1010</real>
</dict>
<key>^Info\.plist$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>20</real>
</dict>
<key>^PkgInfo$</key>
<dict>
<key>omit</key>
<true/>
<key>weight</key>
<real>20</real>
</dict>
<key>^embedded\.provisionprofile$</key>
<dict>
<key>weight</key>
<real>20</real>
</dict>
<key>^version\.plist$</key>
<dict>
<key>weight</key>
<real>20</real>
</dict>
</dict>
</dict>
</plist>

View File

@ -1,18 +0,0 @@
//
// SoftwareKeyboard.h
// SoftwareKeyboard
//
// Created by Stossy11 on 19/12/2024.
//
#import <Foundation/Foundation.h>
//! Project version number for SoftwareKeyboard.
FOUNDATION_EXPORT double SoftwareKeyboardVersionNumber;
//! Project version string for SoftwareKeyboard.
FOUNDATION_EXPORT const unsigned char SoftwareKeyboardVersionString[];
// In this header, you should import all the public headers of your framework using statements like #import <SoftwareKeyboard/PublicHeader.h>

View File

@ -1,38 +0,0 @@
// swift-interface-format-version: 1.0
// swift-compiler-version: Apple Swift version 6.0.3 effective-5.10 (swiftlang-6.0.3.1.4 clang-1600.0.30)
// swift-module-flags: -target arm64-apple-ios14.0 -enable-objc-interop -enable-library-evolution -swift-version 5 -enforce-exclusivity=checked -Onone -enable-experimental-feature OpaqueTypeErasure -enable-bare-slash-regex -module-name SoftwareKeyboard
@_exported import SoftwareKeyboard
import Swift
import UIKit
import _Concurrency
import _StringProcessing
import _SwiftConcurrencyShims
@objc public enum KeyboardMode : Swift.UInt32 {
case `default` = 0
case numeric = 1
case ascii = 2
case fullLatin = 3
case alphabet = 4
case simplifiedChinese = 5
case traditionalChinese = 6
case korean = 7
case languageSet2 = 8
case languageSet2Latin = 9
public init?(rawValue: Swift.UInt32)
public typealias RawValue = Swift.UInt32
public var rawValue: Swift.UInt32 {
get
}
}
public struct SoftwareKeyboardUiArgs {
public var keyboardMode: SoftwareKeyboard.KeyboardMode
public var headerText: Swift.String
public var subtitleText: Swift.String
public var submitText: Swift.String
public var stringLengthMin: Swift.Int32
public var stringLengthMax: Swift.Int32
public var initialText: Swift.String?
}
extension SoftwareKeyboard.KeyboardMode : Swift.Equatable {}
extension SoftwareKeyboard.KeyboardMode : Swift.Hashable {}
extension SoftwareKeyboard.KeyboardMode : Swift.RawRepresentable {}

View File

@ -1,38 +0,0 @@
// swift-interface-format-version: 1.0
// swift-compiler-version: Apple Swift version 6.0.3 effective-5.10 (swiftlang-6.0.3.1.4 clang-1600.0.30)
// swift-module-flags: -target arm64-apple-ios14.0 -enable-objc-interop -enable-library-evolution -swift-version 5 -enforce-exclusivity=checked -Onone -enable-experimental-feature OpaqueTypeErasure -enable-bare-slash-regex -module-name SoftwareKeyboard
@_exported import SoftwareKeyboard
import Swift
import UIKit
import _Concurrency
import _StringProcessing
import _SwiftConcurrencyShims
@objc public enum KeyboardMode : Swift.UInt32 {
case `default` = 0
case numeric = 1
case ascii = 2
case fullLatin = 3
case alphabet = 4
case simplifiedChinese = 5
case traditionalChinese = 6
case korean = 7
case languageSet2 = 8
case languageSet2Latin = 9
public init?(rawValue: Swift.UInt32)
public typealias RawValue = Swift.UInt32
public var rawValue: Swift.UInt32 {
get
}
}
public struct SoftwareKeyboardUiArgs {
public var keyboardMode: SoftwareKeyboard.KeyboardMode
public var headerText: Swift.String
public var subtitleText: Swift.String
public var submitText: Swift.String
public var stringLengthMin: Swift.Int32
public var stringLengthMax: Swift.Int32
public var initialText: Swift.String?
}
extension SoftwareKeyboard.KeyboardMode : Swift.Equatable {}
extension SoftwareKeyboard.KeyboardMode : Swift.Hashable {}
extension SoftwareKeyboard.KeyboardMode : Swift.RawRepresentable {}

View File

@ -1,6 +0,0 @@
framework module SoftwareKeyboard {
umbrella header "SoftwareKeyboard.h"
export *
module * { export * }
}

View File

@ -21,7 +21,7 @@ struct MeloNXApp: App {
var body: some Scene {
WindowGroup {
ZStack {
if showed {
if showed || DRM != 1 {
ContentView()
} else {
Group {
@ -137,10 +137,12 @@ struct MeloNXApp: App {
}
func showDMCAAlert() -> UIAlertController? {
if let mainWindow = UIApplication.shared.windows.last {
if let mainWindow = UIApplication.shared.windows.first {
let alertController = UIAlertController(title: "Unauthorized Copy Notice", message: "This app was illegally leaked. Please report the download on the MeloNX Discord. In the meantime, check out Pomelo! \n -Stossy11", preferredStyle: .alert)
mainWindow.rootViewController!.present(alertController, animated: true, completion: nil)
DispatchQueue.main.async {
mainWindow.rootViewController!.present(alertController, animated: true, completion: nil)
}
return alertController
} else {

View File

@ -98,43 +98,10 @@ namespace Ryujinx.Ava.UI.Applet
return okPressed;
}
public bool DisplayInputDialog(SoftwareKeyboardUiArgs args, out string userText)
public void DisplayInputDialog(SoftwareKeyboardUiArgs args, Action<string> onTextEntered)
{
ManualResetEvent dialogCloseEvent = new(false);
bool okPressed = false;
bool error = false;
string inputText = args.InitialText ?? "";
Dispatcher.UIThread.InvokeAsync(async () =>
{
try
{
var response = await SwkbdAppletDialog.ShowInputDialog(LocaleManager.Instance[LocaleKeys.SoftwareKeyboard], args);
if (response.Result == UserResult.Ok)
{
inputText = response.Input;
okPressed = true;
}
}
catch (Exception ex)
{
error = true;
await ContentDialogHelper.CreateErrorDialog(LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.DialogSoftwareKeyboardErrorExceptionMessage, ex));
}
finally
{
dialogCloseEvent.Set();
}
});
dialogCloseEvent.WaitOne();
userText = error ? null : inputText;
return error || okPressed;
onTextEntered?.Invoke("MeloNX");
return;
}
public void ExecuteProgram(Switch device, ProgramSpecifyKind kind, ulong value)

View File

@ -6,7 +6,7 @@ namespace Ryujinx.Graphics.Vulkan
{
class DescriptorSetManager : IDisposable
{
public const uint MaxSets = 16;
public const uint MaxSets = 32;
public class DescriptorPoolHolder : IDisposable
{

View File

@ -14,6 +14,8 @@ using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace Ryujinx.HLE.HOS.Applets
{
@ -51,10 +53,10 @@ namespace Ryujinx.HLE.HOS.Applets
private byte[] _transferMemory;
private string _textValue = "";
public string _textValue = "";
private int _cursorBegin = 0;
private Encoding _encoding = Encoding.Unicode;
private KeyboardResult _lastResult = KeyboardResult.NotSet;
public KeyboardResult _lastResult = KeyboardResult.NotSet;
private IDynamicTextInputHandler _dynamicTextInputHandler = null;
private SoftwareKeyboardRenderer _keyboardRenderer = null;
@ -180,9 +182,6 @@ namespace Ryujinx.HLE.HOS.Applets
return _keyboardRenderer?.DrawTo(destination, position) ?? false;
}
[DllImport("SoftwareKeyboard.framework/SoftwareKeyboard", EntryPoint = "displayInputDialog", CallingConvention = CallingConvention.Cdecl)]
public static extern void DisplayInputDialog(ref SoftwareKeyboardUiArgs args, out IntPtr userInput);
private void ExecuteForegroundKeyboard()
{
@ -223,26 +222,8 @@ namespace Ryujinx.HLE.HOS.Applets
InitialText = initialText,
};
IntPtr userInputPtr;
DisplayInputDialog(ref args, out userInputPtr);
if (userInputPtr != IntPtr.Zero)
{
// Convert the IntPtr to a string
string userInput = Marshal.PtrToStringAnsi(userInputPtr);
_textValue = userInput ?? DefaultInputText;
_lastResult = KeyboardResult.Accept;
Console.WriteLine($"User input: {userInput}");
}
else
{
Console.WriteLine("No input was received or input was canceled.");
_textValue = DefaultInputText;
_lastResult = KeyboardResult.Cancel;
}
_textValue = DefaultInputText;
_lastResult = KeyboardResult.Cancel;
}
else
{
@ -259,37 +240,40 @@ namespace Ryujinx.HLE.HOS.Applets
StringLengthMax = _keyboardForegroundConfig.StringLengthMax,
InitialText = initialText,
};
_device.UiHandler.DisplayInputDialog(args, inputText =>
{
Console.WriteLine($"User entered: {inputText}");
_lastResult = _device.UiHandler.DisplayInputDialog(args, out _textValue) ? KeyboardResult.Accept : KeyboardResult.Cancel;
_textValue ??= initialText ?? DefaultInputText;
}
_textValue = inputText ?? initialText ?? DefaultInputText;
_lastResult = !string.IsNullOrEmpty(inputText) ? KeyboardResult.Accept : KeyboardResult.Cancel;
// Ensure the text meets the minimum length requirement
while (_textValue.Length < _keyboardForegroundConfig.StringLengthMin)
{
_textValue = string.Join(" ", _textValue, _textValue);
}
while (_textValue.Length < _keyboardForegroundConfig.StringLengthMin)
{
_textValue = string.Join(" ", _textValue, _textValue);
}
// Truncate the text if it exceeds the maximum length
if (_textValue.Length > _keyboardForegroundConfig.StringLengthMax)
{
_textValue = _textValue[.._keyboardForegroundConfig.StringLengthMax];
}
// Truncate the text if it exceeds the maximum length
if (_textValue.Length > _keyboardForegroundConfig.StringLengthMax)
{
_textValue = _textValue[.._keyboardForegroundConfig.StringLengthMax];
}
// Handle text validation if required
if (_keyboardForegroundConfig.CheckText)
{
// Submit text for validation
_foregroundState = SoftwareKeyboardState.ValidationPending;
PushForegroundResponse(true);
}
else
{
// Submit text as complete
_foregroundState = SoftwareKeyboardState.Complete;
PushForegroundResponse(false);
// Handle text validation if required
if (_keyboardForegroundConfig.CheckText)
{
// Submit text for validation
_foregroundState = SoftwareKeyboardState.ValidationPending;
PushForegroundResponse(true);
}
else
{
// Submit text as complete
_foregroundState = SoftwareKeyboardState.Complete;
PushForegroundResponse(false);
AppletStateChanged?.Invoke(this, null);
AppletStateChanged?.Invoke(this, null);
}
});
}
}

View File

@ -1,5 +1,6 @@
using Ryujinx.HLE.HOS.Applets;
using Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.ApplicationProxy.Types;
using System;
namespace Ryujinx.HLE.Ui
{
@ -10,7 +11,7 @@ namespace Ryujinx.HLE.Ui
/// </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);
public void DisplayInputDialog(SoftwareKeyboardUiArgs args, Action<string> onTextEntered);
/// <summary>
/// Displays a Message Dialog box to the user and blocks until it is closed.

View File

@ -0,0 +1,42 @@
using System;
using System.Runtime.InteropServices;
using Ryujinx.Ui.Common.Helper;
using System.Threading;
namespace Ryujinx.Headless.SDL2
{
public static class AlertHelper
{
[DllImport("RyujinxKeyboard.framework/RyujinxKeyboard", CallingConvention = CallingConvention.Cdecl)]
public static extern void showKeyboardAlert(string title, string message, string placeholder);
[DllImport("RyujinxKeyboard.framework/RyujinxKeyboard", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr getKeyboardInput();
[DllImport("RyujinxKeyboard.framework/RyujinxKeyboard", CallingConvention = CallingConvention.Cdecl)]
private static extern void clearKeyboardInput();
public static void ShowAlertWithTextInput(string title, string message, string placeholder, Action<string> onTextEntered)
{
showKeyboardAlert(title, message, placeholder);
ThreadPool.QueueUserWorkItem(_ =>
{
string result = null;
while (result == null)
{
Thread.Sleep(100);
IntPtr inputPtr = getKeyboardInput();
if (inputPtr != IntPtr.Zero)
{
result = Marshal.PtrToStringAnsi(inputPtr);
clearKeyboardInput();
onTextEntered?.Invoke(result);
}
}
});
}
}
}

View File

@ -460,12 +460,19 @@ namespace Ryujinx.Headless.SDL2
Exit();
}
public bool DisplayInputDialog(SoftwareKeyboardUiArgs args, out string userText)
public void DisplayInputDialog(SoftwareKeyboardUiArgs args, Action<string> onTextEntered)
{
// SDL2 doesn't support input dialogs
userText = "Ryujinx";
return true;
// Trying to use Objective-C on iDevices
if (OperatingSystem.IsIOS())
{
AlertHelper.ShowAlertWithTextInput(args.HeaderText, args.SubtitleText, args.GuideText, (inputText) =>
{
onTextEntered?.Invoke(inputText);
});
} else {
onTextEntered?.Invoke("");
}
}
public bool DisplayMessageDialog(string title, string message)

View File

@ -81,57 +81,10 @@ namespace Ryujinx.Ui.Applet
return okPressed;
}
public bool DisplayInputDialog(SoftwareKeyboardUiArgs args, out string userText)
public void DisplayInputDialog(SoftwareKeyboardUiArgs args, Action<string> onTextEntered)
{
ManualResetEvent dialogCloseEvent = new(false);
bool okPressed = false;
bool error = false;
string inputText = args.InitialText ?? "";
Application.Invoke(delegate
{
try
{
var swkbdDialog = new SwkbdAppletDialog(_parent)
{
Title = "Software Keyboard",
Text = args.HeaderText,
SecondaryText = args.SubtitleText,
};
swkbdDialog.InputEntry.Text = inputText;
swkbdDialog.InputEntry.PlaceholderText = args.GuideText;
swkbdDialog.OkButton.Label = args.SubmitText;
swkbdDialog.SetInputLengthValidation(args.StringLengthMin, args.StringLengthMax);
swkbdDialog.SetInputValidation(args.KeyboardMode);
if (swkbdDialog.Run() == (int)ResponseType.Ok)
{
inputText = swkbdDialog.InputEntry.Text;
okPressed = true;
}
swkbdDialog.Dispose();
}
catch (Exception ex)
{
error = true;
GtkDialog.CreateErrorDialog($"Error displaying Software Keyboard: {ex}");
}
finally
{
dialogCloseEvent.Set();
}
});
dialogCloseEvent.WaitOne();
userText = error ? null : inputText;
return error || okPressed;
onTextEntered?.Invoke("MeloNX");
return;
}
public void ExecuteProgram(HLE.Switch device, ProgramSpecifyKind kind, ulong value)