diff --git a/src/LibRyujinx/Android/AndroidUiHandler.cs b/src/LibRyujinx/Android/AndroidUiHandler.cs index 197eda652..6d1a6d8ba 100644 --- a/src/LibRyujinx/Android/AndroidUiHandler.cs +++ b/src/LibRyujinx/Android/AndroidUiHandler.cs @@ -2,6 +2,7 @@ using LibHac.Tools.Fs; using Ryujinx.Common.Logging; using Ryujinx.HLE; using Ryujinx.HLE.HOS.Applets; +using Ryujinx.HLE.HOS.Applets.SoftwareKeyboard; using Ryujinx.HLE.HOS.Services.Am.AppletOE.ApplicationProxyService.ApplicationProxy.Types; using Ryujinx.HLE.UI; using System; @@ -15,20 +16,13 @@ namespace LibRyujinx.Android { internal class AndroidUIHandler : IHostUIHandler, IDisposable { - public ManualResetEvent _waitEvent; - public ManualResetEvent _responseEvent; private bool _isDisposed; private bool _isOkPressed; - private long _input; + private string? _input; + private ManualResetEvent _resetEvent = new ManualResetEvent(false); public IHostUITheme HostUITheme => throw new NotImplementedException(); - public AndroidUIHandler() - { - _waitEvent = new ManualResetEvent(false); - _responseEvent = new ManualResetEvent(false); - } - public IDynamicTextInputHandler CreateDynamicTextInputHandler() { throw new NotImplementedException(); @@ -36,47 +30,51 @@ namespace LibRyujinx.Android public bool DisplayErrorAppletDialog(string title, string message, string[] buttonsText) { - LibRyujinx.setUiHandlerTitle(LibRyujinx.storeString(title ?? "")); - LibRyujinx.setUiHandlerMessage(LibRyujinx.storeString(message ?? "")); - LibRyujinx.setUiHandlerType(1); - - _responseEvent.Reset(); - Set(); - _responseEvent.WaitOne(); + Interop.UpdateUiHandler(title ?? "", + message ?? "", + "", + 1, + 0, + 0, + KeyboardMode.Default, + "", + ""); return _isOkPressed; } public bool DisplayInputDialog(SoftwareKeyboardUIArgs args, out string userText) { - LibRyujinx.setUiHandlerTitle(LibRyujinx.storeString("Software Keyboard")); - LibRyujinx.setUiHandlerMessage(LibRyujinx.storeString(args.HeaderText ?? "")); - LibRyujinx.setUiHandlerWatermark(LibRyujinx.storeString(args.GuideText ?? "")); - LibRyujinx.setUiHandlerSubtitle(LibRyujinx.storeString(args.SubtitleText ?? "")); - LibRyujinx.setUiHandlerInitialText(LibRyujinx.storeString(args.InitialText ?? "")); - LibRyujinx.setUiHandlerMinLength(args.StringLengthMin); - LibRyujinx.setUiHandlerMaxLength(args.StringLengthMax); - LibRyujinx.setUiHandlerType(2); - LibRyujinx.setUiHandlerKeyboardMode((int)args.KeyboardMode); + _input = null; + _resetEvent.Reset(); + Interop.UpdateUiHandler("Software Keyboard", + args.HeaderText ?? "", + args.GuideText ?? "", + 2, + args.StringLengthMin, + args.StringLengthMax, + args.KeyboardMode, + args.SubtitleText ?? "", + args.InitialText ?? ""); - _responseEvent.Reset(); - Set(); - _responseEvent.WaitOne(); + _resetEvent.WaitOne(); - userText = _input != -1 ? LibRyujinx.GetStoredString(_input) : ""; + userText = _input ?? ""; return _isOkPressed; } public bool DisplayMessageDialog(string title, string message) { - LibRyujinx.setUiHandlerTitle(LibRyujinx.storeString(title ?? "")); - LibRyujinx.setUiHandlerMessage(LibRyujinx.storeString(message ?? "")); - LibRyujinx.setUiHandlerType(1); - - _responseEvent.Reset(); - Set(); - _responseEvent.WaitOne(); + Interop.UpdateUiHandler(title ?? "", + message ?? "", + "", + 1, + 0, + 0, + KeyboardMode.Default, + "", + ""); return _isOkPressed; } @@ -99,38 +97,18 @@ namespace LibRyujinx.Android // throw new NotImplementedException(); } - internal void Wait() - { - if (_isDisposed) - return; - _waitEvent.Reset(); - _waitEvent.WaitOne(); - _waitEvent.Reset(); - } - - internal void Set() - { - if (_isDisposed) - return; - _waitEvent.Set(); - } - - internal void SetResponse(bool isOkPressed, long input) + internal void SetResponse(bool isOkPressed, string input) { if (_isDisposed) return; _isOkPressed = isOkPressed; _input = input; - _responseEvent.Set(); + _resetEvent.Set(); } public void Dispose() { _isDisposed = true; - _waitEvent.Set(); - _waitEvent.Set(); - _responseEvent.Dispose(); - _waitEvent.Dispose(); } } } diff --git a/src/LibRyujinx/Android/Interop.cs b/src/LibRyujinx/Android/Interop.cs new file mode 100644 index 000000000..0b3b58ec5 --- /dev/null +++ b/src/LibRyujinx/Android/Interop.cs @@ -0,0 +1,190 @@ +using LibRyujinx.Jni; +using LibRyujinx.Jni.Identifiers; +using LibRyujinx.Jni.Pointers; +using LibRyujinx.Jni.Primitives; +using LibRyujinx.Jni.References; +using LibRyujinx.Jni.Values; +using Rxmxnx.PInvoke; +using Ryujinx.Common.Logging; +using Ryujinx.Common.Memory; +using Ryujinx.HLE.HOS.Applets.SoftwareKeyboard; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; +using System.Xml.Linq; + +namespace LibRyujinx.Android +{ + internal unsafe static class Interop + { + internal const string BaseClassName = "org/ryujinx/android/RyujinxNative"; + private static JGlobalRef? _classId; + private static ConcurrentDictionary<(string method, string descriptor), JMethodId> _methodCache = new ConcurrentDictionary<(string method, string descriptor), JMethodId>(); + private static (string name, string descriptor)[] _methods = new[] + { + ("test", "()V"), + ("updateUiHandler", "(JJJIIIIJJ)V") + }; + + internal static void Initialize(JEnvRef jniEnv) + { + var vm = JniHelper.GetVirtualMachine(jniEnv); + if (_classId == null) + { + var className = new ReadOnlySpan(Encoding.UTF8.GetBytes(BaseClassName)); + using (IReadOnlyFixedMemory.IDisposable cName = className.GetUnsafeValPtr() + .GetUnsafeFixedContext(className.Length)) + { + _classId = JniHelper.GetGlobalClass(jniEnv, cName); + if (_classId == null) + { + Logger.Info?.Print(LogClass.Application, $"Class Id {BaseClassName} not found"); + return; + } + } + } + + foreach (var x in _methods) + { + CacheMethod(jniEnv, x.name, x.descriptor); + } + + JniEnv._jvm = vm; + } + + private static void CacheMethod(JEnvRef jEnv, string name, string descriptor) + { + if (!_methodCache.TryGetValue((name, descriptor), out var method)) + { + var methodName = new ReadOnlySpan(Encoding.UTF8.GetBytes(name)); + var descriptorId = new ReadOnlySpan(Encoding.UTF8.GetBytes(descriptor)); + using (IReadOnlyFixedMemory.IDisposable mName = methodName.GetUnsafeValPtr() + .GetUnsafeFixedContext(methodName.Length)) + using (IReadOnlyFixedMemory.IDisposable dName = descriptorId.GetUnsafeValPtr() + .GetUnsafeFixedContext(descriptorId.Length)) + { + var methodId = JniHelper.GetStaticMethodId(jEnv, (JClassLocalRef)(_classId.Value.Value), mName, dName); + if (methodId == null) + { + Logger.Warning?.Print(LogClass.Application, $"Java Method Id {name} not found"); + return; + } + + method = methodId.Value; + _methodCache[(name, descriptor)] = method; + } + } + } + + private static void CallMethod(string name, string descriptor, params JValue[] values) + { + using var env = JniEnv.Create(); + if (_methodCache.TryGetValue((name, descriptor), out var method)) + { + if (descriptor.EndsWith("V")) + { + JniHelper.CallStaticVoidMethod(env.Env, (JClassLocalRef)(_classId.Value.Value), method, values); + } + } + } + + public static void Test() + { + CallMethod("test", "()V"); + } + + public static void UpdateUiHandler(string newTitle, + string newMessage, + string newWatermark, + int newType, + int min, + int max, + KeyboardMode nMode, + string newSubtitle, + string newInitialText) + { + using var titlePointer = new TempNativeString(newTitle); + using var messagePointer = new TempNativeString(newMessage); + using var watermarkPointer = new TempNativeString(newWatermark); + using var subtitlePointer = new TempNativeString(newSubtitle); + using var newInitialPointer = new TempNativeString(newInitialText); + CallMethod("updateUiHandler", "(JJJIIIIJJ)V", new JValue[] + { + JValue.Create(titlePointer.AsBytes()), + JValue.Create(messagePointer.AsBytes()), + JValue.Create(watermarkPointer.AsBytes()), + JValue.Create(newType.AsBytes()), + JValue.Create(min.AsBytes()), + JValue.Create(max.AsBytes()), + JValue.Create(nMode.AsBytes()), + JValue.Create(subtitlePointer.AsBytes()), + JValue.Create(newInitialPointer.AsBytes()) + }); + } + + private class TempNativeString : IDisposable + { + private JLong _jPointer; + + public TempNativeString(string value) + { + Pointer = Marshal.StringToHGlobalAuto(value); + JPointer = (JLong)Pointer; + } + + public nint Pointer { get; private set; } + public JLong JPointer { get => _jPointer; private set => _jPointer = value; } + + public Span AsBytes() + { + return _jPointer.AsBytes(); + } + + public void Dispose() + { + if (Pointer != IntPtr.Zero) + { + Marshal.FreeHGlobal(Pointer); + } + Pointer = IntPtr.Zero; + } + } + + private class JniEnv : IDisposable + { + internal static JavaVMRef? _jvm; + private readonly JEnvRef _env; + private readonly bool _newAttach; + + public JEnvRef Env => _env; + + private JniEnv(JEnvRef env, bool newAttach) + { + _env = env; + _newAttach = newAttach; + } + + public void Dispose() + { + if(_newAttach) + { + JniHelper.Detach(_jvm!.Value); + } + } + + public static JniEnv? Create() + { + bool newAttach = false; + ReadOnlySpan threadName = "JvmCall"u8; + var env = _jvm == null ? default : JniHelper.Attach(_jvm.Value, threadName.GetUnsafeValPtr().GetUnsafeFixedContext(threadName.Length), + out newAttach); + + return env != null ? new JniEnv(env.Value, newAttach) : null; + } + } + } +} diff --git a/src/LibRyujinx/Android/JniExportedMethods.cs b/src/LibRyujinx/Android/JniExportedMethods.cs index b3dc42418..6cf774255 100644 --- a/src/LibRyujinx/Android/JniExportedMethods.cs +++ b/src/LibRyujinx/Android/JniExportedMethods.cs @@ -1,5 +1,5 @@ +using LibRyujinx.Android; using LibRyujinx.Jni.Pointers; -using LibRyujinx.Jni.References; using Ryujinx.Audio.Backends.OpenAL; using Ryujinx.Common; using Ryujinx.Common.Configuration; @@ -14,9 +14,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Numerics; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using System.Text; using System.Threading; namespace LibRyujinx @@ -29,56 +27,6 @@ namespace LibRyujinx public static VulkanLoader? VulkanLoader { get; private set; } - [DllImport("libryujinxjni")] - private extern static IntPtr getStringPointer(JEnvRef jEnv, JStringLocalRef s); - - [DllImport("libryujinxjni")] - private extern static JStringLocalRef createString(JEnvRef jEnv, IntPtr ch); - - [DllImport("libryujinxjni")] - internal extern static long storeString(string ch); - - [DllImport("libryujinxjni")] - internal extern static IntPtr getString(long id); - - [DllImport("libryujinxjni")] - internal extern static long setUiHandlerTitle(long title); - - [DllImport("libryujinxjni")] - internal extern static long setUiHandlerMessage(long message); - [DllImport("libryujinxjni")] - internal extern static long setUiHandlerWatermark(long watermark); - [DllImport("libryujinxjni")] - internal extern static long setUiHandlerInitialText(long text); - [DllImport("libryujinxjni")] - internal extern static long setUiHandlerSubtitle(long text); - - [DllImport("libryujinxjni")] - internal extern static long setUiHandlerType(int type); - - [DllImport("libryujinxjni")] - internal extern static long setUiHandlerKeyboardMode(int mode); - - [DllImport("libryujinxjni")] - internal extern static long setUiHandlerMinLength(int lenght); - - [DllImport("libryujinxjni")] - internal extern static long setUiHandlerMaxLength(int lenght); - - internal static string GetStoredString(long id) - { - var pointer = getString(id); - if (pointer != IntPtr.Zero) - { - var str = Marshal.PtrToStringAnsi(pointer) ?? ""; - - Marshal.FreeHGlobal(pointer); - return str; - } - - return ""; - } - [DllImport("libryujinxjni")] internal extern static void setRenderingThread(); @@ -97,7 +45,7 @@ namespace LibRyujinx public delegate IntPtr JniCreateSurface(IntPtr native_surface, IntPtr instance); [UnmanagedCallersOnly(EntryPoint = "javaInitialize")] - public static bool JniInitialize(IntPtr jpathId) + public unsafe static bool JniInitialize(IntPtr jpathId, IntPtr jniEnv) { Logger.Trace?.Print(LogClass.Application, "Jni Function Call"); PlatformInfo.IsBionic = true; @@ -113,6 +61,10 @@ namespace LibRyujinx var init = Initialize(path); + Interop.Initialize(new JEnvRef(jniEnv)); + + Interop.Test(); + _surfaceEvent?.Set(); _surfaceEvent = new ManualResetEvent(false); @@ -120,15 +72,6 @@ namespace LibRyujinx return init; } - private static string? GetString(JEnvRef jEnv, JStringLocalRef jString) - { - var stringPtr = getStringPointer(jEnv, jString); - - var s = Marshal.PtrToStringAnsi(stringPtr); - Marshal.FreeHGlobal(stringPtr); - return s; - } - [UnmanagedCallersOnly(EntryPoint = "deviceReloadFilesystem")] public static void JnaReloadFileSystem() { @@ -192,20 +135,6 @@ namespace LibRyujinx return stats; } - [UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_deviceLoad")] - public static bool JniLoadApplicationNative(JEnvRef jEnv, JObjectLocalRef jObj, JStringLocalRef pathPtr) - { - Logger.Trace?.Print(LogClass.Application, "Jni Function Call"); - if (SwitchDevice?.EmulationContext == null) - { - return false; - } - - var path = GetString(jEnv, pathPtr); - - return LoadApplication(path); - } - [UnmanagedCallersOnly(EntryPoint = "deviceLaunchMiiEditor")] public static bool JNALaunchMiiEditApplet() { @@ -336,11 +265,6 @@ namespace LibRyujinx }); } - private static CCharSequence GetCCharSequence(string s) - { - return Encoding.UTF8.GetBytes(s).AsSpan(); - } - [UnmanagedCallersOnly(EntryPoint = "graphicsSetSurface")] public static void JniSetSurface(long surfacePtr, long window) { @@ -455,13 +379,6 @@ namespace LibRyujinx SetVsyncState(enabled); } - [UnmanagedCallersOnly(EntryPoint = "Java_org_ryujinx_android_RyujinxNative_graphicsRendererSetSwapBufferCallback")] - public static void JniSetSwapBuffersCallbackNative(JEnvRef jEnv, JObjectLocalRef jObj, IntPtr swapBuffersCallback) - { - Logger.Trace?.Print(LogClass.Application, "Jni Function Call"); - _swapBuffersCallback = Marshal.GetDelegateForFunctionPointer(swapBuffersCallback); - } - [UnmanagedCallersOnly(EntryPoint = "inputInitialize")] public static void JnaInitializeInput(int width, int height) { @@ -621,22 +538,10 @@ namespace LibRyujinx SetupUiHandler(); } - [UnmanagedCallersOnly(EntryPoint = "uiHandlerWait")] - public static void JniWaitUiHandler() - { - WaitUiHandler(); - } - - [UnmanagedCallersOnly(EntryPoint = "uiHandlerStopWait")] - public static void JniStopUiHandlerWait() - { - StopUiHandlerWait(); - } - [UnmanagedCallersOnly(EntryPoint = "uiHandlerSetResponse")] - public static void JniSetUiHandlerResponse(bool isOkPressed, long input) + public static void JniSetUiHandlerResponse(bool isOkPressed, IntPtr input) { - SetUiHandlerResponse(isOkPressed, input); + SetUiHandlerResponse(isOkPressed, Marshal.PtrToStringAnsi(input) ?? ""); } [UnmanagedCallersOnly(EntryPoint = "userOpenUser")] diff --git a/src/LibRyujinx/LibRyujinx.cs b/src/LibRyujinx/LibRyujinx.cs index 5ddbff16b..1074a047b 100644 --- a/src/LibRyujinx/LibRyujinx.cs +++ b/src/LibRyujinx/LibRyujinx.cs @@ -667,23 +667,7 @@ namespace LibRyujinx } } - public static void WaitUiHandler() - { - if (SwitchDevice?.HostUiHandler is AndroidUIHandler uiHandler) - { - uiHandler.Wait(); - } - } - - public static void StopUiHandlerWait() - { - if (SwitchDevice?.HostUiHandler is AndroidUIHandler uiHandler) - { - uiHandler.Set(); - } - } - - public static void SetUiHandlerResponse(bool isOkPressed, long input) + public static void SetUiHandlerResponse(bool isOkPressed, string input) { if (SwitchDevice?.HostUiHandler is AndroidUIHandler uiHandler) { diff --git a/src/RyujinxAndroid/app/src/main/cpp/CMakeLists.txt b/src/RyujinxAndroid/app/src/main/cpp/CMakeLists.txt index 14b0c57f8..07f54ced9 100644 --- a/src/RyujinxAndroid/app/src/main/cpp/CMakeLists.txt +++ b/src/RyujinxAndroid/app/src/main/cpp/CMakeLists.txt @@ -35,8 +35,7 @@ add_library( # Sets the name of the library. # Provides a relative path to your source file(s). vulkan_wrapper.cpp - string_helper.cpp - ryujinx.cpp) + ryujinx.cpp) # Searches for a specified prebuilt library and stores the path as a # variable. Because CMake includes system libraries in the search path by diff --git a/src/RyujinxAndroid/app/src/main/cpp/ryuijnx.h b/src/RyujinxAndroid/app/src/main/cpp/ryuijnx.h index 0a22ddd9d..cbcbe0cbb 100644 --- a/src/RyujinxAndroid/app/src/main/cpp/ryuijnx.h +++ b/src/RyujinxAndroid/app/src/main/cpp/ryuijnx.h @@ -20,7 +20,6 @@ #include #include "adrenotools/driver.h" #include "native_window.h" -#include "string_helper.h" // A macro to pass call to Vulkan and check for return value for success #define CALL_VK(func) \ @@ -39,49 +38,6 @@ void *_ryujinxNative = NULL; -class UiHandler { -public: - void setTitle(long storedTitle); - - void setMessage(long storedMessage); - - void setWatermark(long wm); - - void setType(int t); - - void setMode(int t); - - void setMinLength(int t); - - void setMaxLength(int t); - - void setInitialText(long text); - - void setSubtitle(long text); - - long getTitle(); - - long getMessage(); - - long getWatermark(); - - long getInitialText(); - - long getSubtitle(); - - int type = 0; - int keyboardMode = 0; - int min_length = -1; - int max_length = -1; - -private: - long title = -1; - long message = -1; - long watermark = -1; - long initialText = -1; - long subtitle = -1; -}; - // Ryujinx imported functions bool (*initialize)(char *) = NULL; @@ -89,7 +45,5 @@ long _renderingThreadId = 0; JavaVM *_vm = nullptr; jobject _mainActivity = nullptr; jclass _mainActivityClass = nullptr; -string_helper str_helper = string_helper(); -UiHandler ui_handler = UiHandler(); #endif //RYUJINXNATIVE_RYUIJNX_H diff --git a/src/RyujinxAndroid/app/src/main/cpp/ryujinx.cpp b/src/RyujinxAndroid/app/src/main/cpp/ryujinx.cpp index e4b4cef78..cb6ef4e0b 100644 --- a/src/RyujinxAndroid/app/src/main/cpp/ryujinx.cpp +++ b/src/RyujinxAndroid/app/src/main/cpp/ryujinx.cpp @@ -311,61 +311,10 @@ Java_org_ryujinx_android_NativeHelpers_getProgressInfo(JNIEnv *env, jobject thiz return createStringFromStdString(env, progressInfo); } -extern "C" -long storeString(char *str) { - return str_helper.store_cstring(str); -} - -extern "C" -const char *getString(long id) { - auto str = str_helper.get_stored(id); - auto cstr = (char *) ::malloc(str.length() + 1); - ::strcpy(cstr, str.c_str()); - return cstr; -} - -extern "C" -{ -void setUiHandlerTitle(long title) { - ui_handler.setTitle(title); -} -void setUiHandlerMessage(long message) { - ui_handler.setMessage(message); -} -void setUiHandlerWatermark(long wm) { - ui_handler.setWatermark(wm); -} -void setUiHandlerType(int type) { - ui_handler.setType(type); -} -void setUiHandlerKeyboardMode(int mode) { - ui_handler.setMode(mode); -} -void setUiHandlerMinLength(int length) { - ui_handler.setMinLength(length); -} -void setUiHandlerMaxLength(int length) { - ui_handler.setMaxLength(length); -} -void setUiHandlerInitialText(long text) { - ui_handler.setInitialText(text); -} -void setUiHandlerSubtitle(long text) { - ui_handler.setSubtitle(text); -} -} - -extern "C" -JNIEXPORT jlong JNICALL -Java_org_ryujinx_android_NativeHelpers_storeStringJava(JNIEnv *env, jobject thiz, jstring string) { - auto str = getStringPointer(env, string); - return str_helper.store_cstring(str); -} - extern "C" JNIEXPORT jstring JNICALL -Java_org_ryujinx_android_NativeHelpers_getStringJava(JNIEnv *env, jobject thiz, jlong id) { - return createStringFromStdString(env, id > -1 ? str_helper.get_stored(id) : ""); +Java_org_ryujinx_android_NativeHelpers_getStringJava(JNIEnv *env, jobject thiz, jlong ptr) { + return createString(env, (char*)ptr); } extern "C" @@ -374,145 +323,3 @@ Java_org_ryujinx_android_NativeHelpers_setIsInitialOrientationFlipped(JNIEnv *en jboolean is_flipped) { isInitialOrientationFlipped = is_flipped; } - -extern "C" -JNIEXPORT jint JNICALL -Java_org_ryujinx_android_NativeHelpers_getUiHandlerRequestType(JNIEnv *env, jobject thiz) { - return ui_handler.type; -} -extern "C" -JNIEXPORT jlong JNICALL -Java_org_ryujinx_android_NativeHelpers_getUiHandlerRequestTitle(JNIEnv *env, jobject thiz) { - return ui_handler.getTitle(); -} -extern "C" -JNIEXPORT jlong JNICALL -Java_org_ryujinx_android_NativeHelpers_getUiHandlerRequestMessage(JNIEnv *env, jobject thiz) { - return ui_handler.getMessage(); -} - - -void UiHandler::setTitle(long storedTitle) { - if (title != -1) { - str_helper.get_stored(title); - title = -1; - } - - title = storedTitle; -} - -void UiHandler::setMessage(long storedMessage) { - if (message != -1) { - str_helper.get_stored(message); - message = -1; - } - - message = storedMessage; -} - -void UiHandler::setType(int t) { - this->type = t; -} - -long UiHandler::getTitle() { - auto v = title; - title = -1; - return v; -} - -long UiHandler::getMessage() { - auto v = message; - message = -1; - return v; -} - -void UiHandler::setWatermark(long wm) { - if (watermark != -1) { - str_helper.get_stored(watermark); - watermark = -1; - } - - watermark = wm; -} - -void UiHandler::setMinLength(int t) { - this->min_length = t; -} - -void UiHandler::setMaxLength(int t) { - this->max_length = t; -} - -long UiHandler::getWatermark() { - auto v = watermark; - watermark = -1; - return v; -} - -void UiHandler::setInitialText(long text) { - if (initialText != -1) { - str_helper.get_stored(watermark); - initialText = -1; - } - - initialText = text; -} - -void UiHandler::setSubtitle(long text) { - if (subtitle != -1) { - str_helper.get_stored(subtitle); - subtitle = -1; - } - - subtitle = text; -} - -long UiHandler::getInitialText() { - auto v = initialText; - initialText = -1; - return v; -} - -long UiHandler::getSubtitle() { - auto v = subtitle; - subtitle = -1; - return v; -} - -void UiHandler::setMode(int t) { - keyboardMode = t; -} - -extern "C" -JNIEXPORT jint JNICALL -Java_org_ryujinx_android_NativeHelpers_getUiHandlerMinLength(JNIEnv *env, jobject thiz) { - return ui_handler.min_length; -} -extern "C" -JNIEXPORT jint JNICALL -Java_org_ryujinx_android_NativeHelpers_getUiHandlerMaxLength(JNIEnv *env, jobject thiz) { - return ui_handler.max_length; -} - -extern "C" -JNIEXPORT jlong JNICALL -Java_org_ryujinx_android_NativeHelpers_getUiHandlerRequestWatermark(JNIEnv *env, jobject thiz) { - return ui_handler.getWatermark(); -} - -extern "C" -JNIEXPORT jlong JNICALL -Java_org_ryujinx_android_NativeHelpers_getUiHandlerRequestInitialText(JNIEnv *env, jobject thiz) { - return ui_handler.getInitialText(); -} -extern "C" -JNIEXPORT jlong JNICALL -Java_org_ryujinx_android_NativeHelpers_getUiHandlerRequestSubtitle(JNIEnv *env, jobject thiz) { - return ui_handler.getSubtitle(); -} - -extern "C" -JNIEXPORT jint JNICALL -Java_org_ryujinx_android_NativeHelpers_getUiHandlerKeyboardMode(JNIEnv *env, jobject thiz) { - return ui_handler.keyboardMode; -} diff --git a/src/RyujinxAndroid/app/src/main/cpp/string_helper.cpp b/src/RyujinxAndroid/app/src/main/cpp/string_helper.cpp deleted file mode 100644 index e8e8483b0..000000000 --- a/src/RyujinxAndroid/app/src/main/cpp/string_helper.cpp +++ /dev/null @@ -1,24 +0,0 @@ -// -// Created by Emmanuel Hansen on 10/30/2023. -// - -#include "string_helper.h" - -long string_helper::store_cstring(const char *cstr) { - auto id = ++current_id; - _map.insert({id, cstr}); - return id; -} - -long string_helper::store_string(const string& str) { - auto id = ++current_id; - _map.insert({id, str}); - return id; -} - -string string_helper::get_stored(long id) { - auto str = _map[id]; - _map.erase(id); - - return str; -} diff --git a/src/RyujinxAndroid/app/src/main/cpp/string_helper.h b/src/RyujinxAndroid/app/src/main/cpp/string_helper.h deleted file mode 100644 index b43993d29..000000000 --- a/src/RyujinxAndroid/app/src/main/cpp/string_helper.h +++ /dev/null @@ -1,29 +0,0 @@ -// -// Created by Emmanuel Hansen on 10/30/2023. -// - -#ifndef RYUJINXANDROID_STRING_HELPER_H -#define RYUJINXANDROID_STRING_HELPER_H - -#include -#include -using namespace std; -class string_helper { -public: - long store_cstring(const char * cstr); - long store_string(const string& str); - - string get_stored(long id); - - string_helper(){ - _map = unordered_map(); - current_id = 0; - } - -private: - unordered_map _map; - long current_id; -}; - - -#endif //RYUJINXANDROID_STRING_HELPER_H diff --git a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/GameHost.kt b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/GameHost.kt index 4a0741a1e..2fa058b3c 100644 --- a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/GameHost.kt +++ b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/GameHost.kt @@ -72,7 +72,7 @@ class GameHost(context: Context?, private val mainViewModel: MainViewModel) : Su _isInit = false _isStarted = false - mainViewModel.activity.uiHandler.stop() + RyujinxNative.jnaInstance.uiHandlerSetResponse(false, "") _updateThread?.join() _renderingThreadWatcher?.join() @@ -142,10 +142,6 @@ class GameHost(context: Context?, private val mainViewModel: MainViewModel) : Su } private fun runGame() { - - thread { - mainViewModel.activity.uiHandler.listen() - } RyujinxNative.jnaInstance.graphicsRendererRunLoop() game?.close() diff --git a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/MainActivity.kt b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/MainActivity.kt index 1c365b5d5..3553de774 100644 --- a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/MainActivity.kt +++ b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/MainActivity.kt @@ -16,6 +16,7 @@ import androidx.core.view.WindowCompat import androidx.core.view.WindowInsetsCompat import androidx.core.view.WindowInsetsControllerCompat import com.anggrayudi.storage.SimpleStorageHelper +import com.sun.jna.JNIEnv import org.ryujinx.android.ui.theme.RyujinxAndroidTheme import org.ryujinx.android.viewmodels.MainViewModel import org.ryujinx.android.viewmodels.QuickSettings @@ -101,7 +102,7 @@ class MainActivity : BaseActivity() { quickSettings.enableTraceLogs ) val success = - RyujinxNative.jnaInstance.javaInitialize(appPath) + RyujinxNative.jnaInstance.javaInitialize(appPath, JNIEnv.CURRENT) uiHandler = UiHandler() _isInit = success diff --git a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/NativeHelpers.kt b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/NativeHelpers.kt index e3ffc258e..917b9fa3c 100644 --- a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/NativeHelpers.kt +++ b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/NativeHelpers.kt @@ -30,16 +30,6 @@ class NativeHelpers { external fun setSwapInterval(nativeWindow: Long, swapInterval: Int): Int external fun getProgressInfo(): String external fun getProgressValue(): Float - external fun storeStringJava(string: String): Long - external fun getStringJava(id: Long): String + external fun getStringJava(ptr: Long): String external fun setIsInitialOrientationFlipped(isFlipped: Boolean) - external fun getUiHandlerRequestType(): Int - external fun getUiHandlerRequestTitle(): Long - external fun getUiHandlerRequestMessage(): Long - external fun getUiHandlerMinLength(): Int - external fun getUiHandlerMaxLength(): Int - external fun getUiHandlerKeyboardMode(): Int - external fun getUiHandlerRequestWatermark(): Long - external fun getUiHandlerRequestInitialText(): Long - external fun getUiHandlerRequestSubtitle(): Long } diff --git a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/RyujinxNative.kt b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/RyujinxNative.kt index 13df592a0..8423451f2 100644 --- a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/RyujinxNative.kt +++ b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/RyujinxNative.kt @@ -1,8 +1,10 @@ package org.ryujinx.android +import com.sun.jna.JNIEnv import com.sun.jna.Library import com.sun.jna.Native import org.ryujinx.android.viewmodels.GameInfo +import java.util.Collections interface RyujinxNativeJna : Library { fun deviceInitialize( @@ -35,7 +37,7 @@ interface RyujinxNativeJna : Library { driver: Long ): Boolean - fun javaInitialize(appPath: String): Boolean + fun javaInitialize(appPath: String, env: JNIEnv): Boolean fun deviceLaunchMiiEditor(): Boolean fun deviceGetGameFrameRate(): Double fun deviceGetGameFrameTime(): Double @@ -73,9 +75,7 @@ interface RyujinxNativeJna : Library { fun deviceInstallFirmware(fileDescriptor: Int, isXci: Boolean) fun deviceGetInstalledFirmwareVersion(): String fun uiHandlerSetup() - fun uiHandlerWait() - fun uiHandlerStopWait() - fun uiHandlerSetResponse(isOkPressed: Boolean, input: Long) + fun uiHandlerSetResponse(isOkPressed: Boolean, input: String) fun deviceGetDlcTitleId(path: String, ncaPath: String): String fun deviceGetGameInfo(fileDescriptor: Int, extension: String, info: GameInfo) fun userGetAllUsers(): Array @@ -88,7 +88,47 @@ class RyujinxNative { companion object { val jnaInstance: RyujinxNativeJna = Native.load( "ryujinx", - RyujinxNativeJna::class.java + RyujinxNativeJna::class.java, + Collections.singletonMap(Library.OPTION_ALLOW_OBJECTS, true) ) + + @JvmStatic + fun test() + { + val i = 0 + } + + @JvmStatic + fun updateUiHandler( + newTitlePointer: Long, + newMessagePointer: Long, + newWatermarkPointer: Long, + newType: Int, + min: Int, + max: Int, + nMode: Int, + newSubtitlePointer: Long, + newInitialTextPointer: Long + ) + { + var uiHandler = MainActivity.mainViewModel?.activity?.uiHandler + uiHandler?.apply { + val newTitle = NativeHelpers.instance.getStringJava(newTitlePointer) + val newMessage = NativeHelpers.instance.getStringJava(newMessagePointer) + val newWatermark = NativeHelpers.instance.getStringJava(newWatermarkPointer) + val newSubtitle = NativeHelpers.instance.getStringJava(newSubtitlePointer) + val newInitialText = NativeHelpers.instance.getStringJava(newInitialTextPointer) + val newMode = KeyboardMode.entries[nMode] + update(newTitle, + newMessage, + newWatermark, + newType, + min, + max, + newMode, + newSubtitle, + newInitialText); + } + } } } diff --git a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/UiHandler.kt b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/UiHandler.kt index a2ab87ec4..800fb0c4b 100644 --- a/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/UiHandler.kt +++ b/src/RyujinxAndroid/app/src/main/java/org/ryujinx/android/UiHandler.kt @@ -12,7 +12,6 @@ import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.verticalScroll -import androidx.compose.material3.AlertDialog import androidx.compose.material3.AlertDialogDefaults import androidx.compose.material3.BasicAlertDialog import androidx.compose.material3.Button @@ -32,7 +31,7 @@ import androidx.compose.ui.window.DialogProperties import com.halilibo.richtext.markdown.Markdown import com.halilibo.richtext.ui.material3.RichText -internal enum class KeyboardMode { +enum class KeyboardMode { Default, Numeric, ASCII, FullLatin, Alphabet, SimplifiedChinese, TraditionalChinese, Korean, LanguageSet2, LanguageSet2Latin } @@ -48,39 +47,34 @@ class UiHandler { val inputText = mutableStateOf("") var title: String = "" var message: String = "" - var shouldListen = true init { RyujinxNative.jnaInstance.uiHandlerSetup() } - fun listen() { - showMessage.value = false - while (shouldListen) { - RyujinxNative.jnaInstance.uiHandlerWait() - - title = - NativeHelpers.instance.getStringJava(NativeHelpers.instance.getUiHandlerRequestTitle()) - message = - NativeHelpers.instance.getStringJava(NativeHelpers.instance.getUiHandlerRequestMessage()) - watermark = - NativeHelpers.instance.getStringJava(NativeHelpers.instance.getUiHandlerRequestWatermark()) - type = NativeHelpers.instance.getUiHandlerRequestType() - minLength = NativeHelpers.instance.getUiHandlerMinLength() - maxLength = NativeHelpers.instance.getUiHandlerMaxLength() - mode = KeyboardMode.values()[NativeHelpers.instance.getUiHandlerKeyboardMode()] - subtitle = - NativeHelpers.instance.getStringJava(NativeHelpers.instance.getUiHandlerRequestSubtitle()) - initialText = - NativeHelpers.instance.getStringJava(NativeHelpers.instance.getUiHandlerRequestInitialText()) - inputText.value = initialText - showMessage.value = type > 0 - } - } - - fun stop() { - shouldListen = false - RyujinxNative.jnaInstance.uiHandlerStopWait() + fun update( + newTitle: String, + newMessage: String, + newWatermark: String, + newType: Int, + min: Int, + max: Int, + newMode: KeyboardMode, + newSubtitle: String, + newInitialText: String + ) + { + title = newTitle + message = newMessage + watermark = newWatermark + type = newType + minLength = min + maxLength = max + mode = newMode + subtitle = newSubtitle + initialText = newInitialText + inputText.value = initialText + showMessage.value = type > 0 } @OptIn(ExperimentalMaterial3Api::class) @@ -119,15 +113,15 @@ class UiHandler { } fun submit() { - var input: Long = -1 if (type == 2) { if (inputListener.value.length < minLength || inputListener.value.length > maxLength) return - input = - NativeHelpers.instance.storeStringJava(inputListener.value) } + RyujinxNative.jnaInstance.uiHandlerSetResponse( + true, + if (type == 2) inputListener.value else "" + ) showMessageListener.value = false - RyujinxNative.jnaInstance.uiHandlerSetResponse(true, input) } if (showMessageListener.value) { diff --git a/src/RyujinxAndroid/gradle.properties b/src/RyujinxAndroid/gradle.properties index cb15bc25e..c44b8e8e2 100644 --- a/src/RyujinxAndroid/gradle.properties +++ b/src/RyujinxAndroid/gradle.properties @@ -24,7 +24,7 @@ android.nonTransitiveRClass=true # Build configuration # It needs to be set to either "debug" or "release" and can also be overriden on a per build basis # by adding -Dorg.ryujinx.config=NAME to the command line. -org.ryujinx.config=release +org.ryujinx.config=debug # Controls stripping of symbols from libryujinx # Setting this property to auto causes symbols to be stripped for release builds, # but not for debug builds.