skyline/app/src/main/cpp/skyline/jvm.cpp
Dima 54041ceba1 Implement GetCurrentIpConfigInfo
Needed for almost every LAN game
2023-05-02 19:04:59 +03:00

169 lines
7.5 KiB
C++

// SPDX-License-Identifier: MPL-2.0
// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
#include "jvm.h"
namespace skyline {
std::string JniString::GetJString(JNIEnv *env, jstring jString) {
auto utf{env->GetStringUTFChars(jString, nullptr)};
std::string string{utf};
env->ReleaseStringUTFChars(jString, utf);
return string;
}
/*
* @brief A thread-local wrapper over JNIEnv and JavaVM which automatically handles attaching and detaching threads
*/
struct JniEnvironment {
JNIEnv *env{};
static inline JavaVM *vm{};
bool attached{};
void Initialize(JNIEnv *environment) {
env = environment;
if (env->GetJavaVM(&vm) < 0)
throw exception("Cannot get JavaVM from environment");
attached = true;
}
JniEnvironment() {
if (vm && !attached) {
vm->AttachCurrentThread(&env, nullptr);
attached = true;
}
}
~JniEnvironment() {
if (vm && attached)
vm->DetachCurrentThread();
}
operator JNIEnv *() {
if (!attached)
throw exception("Not attached");
return env;
}
JNIEnv *operator->() {
if (!attached)
throw exception("Not attached");
return env;
}
};
thread_local inline JniEnvironment env;
JvmManager::JvmManager(JNIEnv *environ, jobject instance)
: instance{environ->NewGlobalRef(instance)},
instanceClass{reinterpret_cast<jclass>(environ->NewGlobalRef(environ->GetObjectClass(instance)))},
initializeControllersId{environ->GetMethodID(instanceClass, "initializeControllers", "()V")},
vibrateDeviceId{environ->GetMethodID(instanceClass, "vibrateDevice", "(I[J[I)V")},
clearVibrationDeviceId{environ->GetMethodID(instanceClass, "clearVibrationDevice", "(I)V")},
showKeyboardId{environ->GetMethodID(instanceClass, "showKeyboard", "(Ljava/nio/ByteBuffer;Ljava/lang/String;)Lemu/skyline/applet/swkbd/SoftwareKeyboardDialog;")},
waitForSubmitOrCancelId{environ->GetMethodID(instanceClass, "waitForSubmitOrCancel", "(Lemu/skyline/applet/swkbd/SoftwareKeyboardDialog;)[Ljava/lang/Object;")},
closeKeyboardId{environ->GetMethodID(instanceClass, "closeKeyboard", "(Lemu/skyline/applet/swkbd/SoftwareKeyboardDialog;)V")},
showValidationResultId{environ->GetMethodID(instanceClass, "showValidationResult", "(Lemu/skyline/applet/swkbd/SoftwareKeyboardDialog;ILjava/lang/String;)I")},
getVersionCodeId{environ->GetMethodID(instanceClass, "getVersionCode", "()I")},
getIntegerValueId{environ->GetMethodID(environ->FindClass("java/lang/Integer"), "intValue", "()I")},
getDhcpInfoId{environ->GetMethodID(instanceClass, "getDhcpInfo", "()Landroid/net/DhcpInfo;")} {
env.Initialize(environ);
}
JvmManager::~JvmManager() {
env->DeleteGlobalRef(instanceClass);
env->DeleteGlobalRef(instance);
}
JNIEnv *JvmManager::GetEnv() {
return env;
}
jobject JvmManager::GetField(const char *key, const char *signature) {
return env->GetObjectField(instance, env->GetFieldID(instanceClass, key, signature));
}
bool JvmManager::CheckNull(const char *key, const char *signature) {
return env->IsSameObject(env->GetObjectField(instance, env->GetFieldID(instanceClass, key, signature)), nullptr);
}
bool JvmManager::CheckNull(jobject &object) {
return env->IsSameObject(object, nullptr);
}
void JvmManager::InitializeControllers() {
env->CallVoidMethod(instance, initializeControllersId);
}
void JvmManager::VibrateDevice(jint index, const span<jlong> &timings, const span<jint> &amplitudes) {
auto jTimings{env->NewLongArray(static_cast<jsize>(timings.size()))};
env->SetLongArrayRegion(jTimings, 0, static_cast<jsize>(timings.size()), timings.data());
auto jAmplitudes{env->NewIntArray(static_cast<jsize>(amplitudes.size()))};
env->SetIntArrayRegion(jAmplitudes, 0, static_cast<jsize>(amplitudes.size()), amplitudes.data());
env->CallVoidMethod(instance, vibrateDeviceId, index, jTimings, jAmplitudes);
env->DeleteLocalRef(jTimings);
env->DeleteLocalRef(jAmplitudes);
}
void JvmManager::ClearVibrationDevice(jint index) {
env->CallVoidMethod(instance, clearVibrationDeviceId, index);
}
jobject JvmManager::ShowKeyboard(KeyboardConfig &config, std::u16string initialText) {
auto buffer{env->NewDirectByteBuffer(&config, sizeof(KeyboardConfig))};
auto str{env->NewString(reinterpret_cast<const jchar *>(initialText.data()), static_cast<int>(initialText.length()))};
jobject localKeyboardDialog{env->CallObjectMethod(instance, showKeyboardId, buffer, str)};
env->DeleteLocalRef(buffer);
env->DeleteLocalRef(str);
auto keyboardDialog{env->NewGlobalRef(localKeyboardDialog)};
env->DeleteLocalRef(localKeyboardDialog);
return keyboardDialog;
}
std::pair<JvmManager::KeyboardCloseResult, std::u16string> JvmManager::WaitForSubmitOrCancel(jobject keyboardDialog) {
auto returnArray{reinterpret_cast<jobjectArray>(env->CallObjectMethod(instance, waitForSubmitOrCancelId, keyboardDialog))};
auto buttonInteger{env->GetObjectArrayElement(returnArray, 0)};
auto inputJString{reinterpret_cast<jstring>(env->GetObjectArrayElement(returnArray, 1))};
auto stringChars{env->GetStringChars(inputJString, nullptr)};
std::u16string input{stringChars, stringChars + env->GetStringLength(inputJString)};
env->ReleaseStringChars(inputJString, stringChars);
return {static_cast<KeyboardCloseResult>(env->CallIntMethod(buttonInteger, getIntegerValueId)), input};
}
DhcpInfo JvmManager::GetDhcpInfo() {
jobject dhcpInfo{env->CallObjectMethod(instance, getDhcpInfoId)};
jclass dhcpInfoClass{env->GetObjectClass(dhcpInfo)};
jfieldID ipAddressFieldId{env->GetFieldID(dhcpInfoClass, "ipAddress", "I")};
jfieldID subnetFieldId{env->GetFieldID(dhcpInfoClass, "netmask", "I")};
jfieldID gatewayFieldId{env->GetFieldID(dhcpInfoClass, "gateway", "I")};
jfieldID dns1FieldId{env->GetFieldID(dhcpInfoClass, "dns1", "I")};
jfieldID dns2FieldId{env->GetFieldID(dhcpInfoClass, "dns2", "I")};
jint ipAddress{env->GetIntField(dhcpInfo, ipAddressFieldId)};
jint subnet{env->GetIntField(dhcpInfo, subnetFieldId)};
jint gateway{env->GetIntField(dhcpInfo, gatewayFieldId)};
jint dns1{env->GetIntField(dhcpInfo, dns1FieldId)};
jint dns2{env->GetIntField(dhcpInfo, dns2FieldId)};
return DhcpInfo{ipAddress, subnet, gateway, dns1, dns2};
}
void JvmManager::CloseKeyboard(jobject dialog) {
env->CallVoidMethod(instance, closeKeyboardId, dialog);
env->DeleteGlobalRef(dialog);
}
i32 JvmManager::GetVersionCode() {
return env->CallIntMethod(instance, getVersionCodeId);
}
JvmManager::KeyboardCloseResult JvmManager::ShowValidationResult(jobject dialog, KeyboardTextCheckResult checkResult, std::u16string message) {
auto str{env->NewString(reinterpret_cast<const jchar *>(message.data()), static_cast<int>(message.length()))};
auto result{static_cast<KeyboardCloseResult>(env->CallIntMethod(instance, showValidationResultId, dialog, checkResult, str))};
env->DeleteLocalRef(str);
return result;
}
}