Archived
1
0
forked from MeloNX/MeloNX

android - update ui handler to call back into java

This commit is contained in:
Emmanuel Hansen 2024-09-09 19:09:39 +00:00
parent b7deaefff7
commit e2f584a7ff
15 changed files with 316 additions and 531 deletions

View File

@ -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();
}
}
}

View File

@ -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<Byte>(Encoding.UTF8.GetBytes(BaseClassName));
using (IReadOnlyFixedMemory<Byte>.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<Byte>(Encoding.UTF8.GetBytes(name));
var descriptorId = new ReadOnlySpan<Byte>(Encoding.UTF8.GetBytes(descriptor));
using (IReadOnlyFixedMemory<Byte>.IDisposable mName = methodName.GetUnsafeValPtr()
.GetUnsafeFixedContext(methodName.Length))
using (IReadOnlyFixedMemory<Byte>.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<byte> 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<Byte> 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;
}
}
}
}

View File

@ -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>(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")]

View File

@ -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)
{

View File

@ -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

View File

@ -20,7 +20,6 @@
#include <fcntl.h>
#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

View File

@ -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;
}

View File

@ -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;
}

View File

@ -1,29 +0,0 @@
//
// Created by Emmanuel Hansen on 10/30/2023.
//
#ifndef RYUJINXANDROID_STRING_HELPER_H
#define RYUJINXANDROID_STRING_HELPER_H
#include <string>
#include <unordered_map>
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<long,string>();
current_id = 0;
}
private:
unordered_map<long, string> _map;
long current_id;
};
#endif //RYUJINXANDROID_STRING_HELPER_H

View File

@ -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()

View File

@ -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

View File

@ -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
}

View File

@ -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<String>
@ -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);
}
}
}
}

View File

@ -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) {

View File

@ -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.