Path: blob/v3_openjdk/app_pojavlauncher/src/main/jni/input_bridge_v3.c
2128 views
/*1* V3 input bridge implementation.2*3* Status:4* - Active development5* - Works with some bugs:6* + Modded versions gives broken stuff..7*8*9* - Implements glfwSetCursorPos() to handle grab camera pos correctly.10*/1112#include <assert.h>13#include <dlfcn.h>14#include <jni.h>15#include <libgen.h>16#include <stdlib.h>17#include <string.h>18#include <stdatomic.h>19#include <math.h>2021#define TAG __FILE_NAME__22#include "log.h"23#include "utils.h"24#include "environ/environ.h"25#include "jvm_hooks/jvm_hooks.h"2627#define EVENT_TYPE_CHAR 100028#define EVENT_TYPE_CHAR_MODS 100129#define EVENT_TYPE_CURSOR_ENTER 100230#define EVENT_TYPE_KEY 100531#define EVENT_TYPE_MOUSE_BUTTON 100632#define EVENT_TYPE_SCROLL 10073334#define TRY_ATTACH_ENV(env_name, vm, error_message, then) JNIEnv* env_name;\35do { \36env_name = get_attached_env(vm); \37if(env_name == NULL) { \38printf(error_message); \39then \40} \41} while(0)4243static void registerFunctions(JNIEnv *env);4445jint JNI_OnLoad(JavaVM* vm, __attribute__((unused)) void* reserved) {46if (pojav_environ->dalvikJavaVMPtr == NULL) {47LOGI("Saving DVM environ...");48//Save dalvik global JavaVM pointer49pojav_environ->dalvikJavaVMPtr = vm;50JNIEnv *dvEnv;51(*vm)->GetEnv(vm, (void**) &dvEnv, JNI_VERSION_1_4);52pojav_environ->bridgeClazz = (*dvEnv)->NewGlobalRef(dvEnv,(*dvEnv) ->FindClass(dvEnv,"org/lwjgl/glfw/CallbackBridge"));53pojav_environ->method_accessAndroidClipboard = (*dvEnv)->GetStaticMethodID(dvEnv, pojav_environ->bridgeClazz, "accessAndroidClipboard", "(ILjava/lang/String;)Ljava/lang/String;");54pojav_environ->method_onGrabStateChanged = (*dvEnv)->GetStaticMethodID(dvEnv, pojav_environ->bridgeClazz, "onGrabStateChanged", "(Z)V");55pojav_environ->method_onDirectInputEnable = (*dvEnv)->GetStaticMethodID(dvEnv, pojav_environ->bridgeClazz, "onDirectInputEnable", "()V");56pojav_environ->isUseStackQueueCall = JNI_FALSE;57} else if (pojav_environ->dalvikJavaVMPtr != vm) {58LOGI("Saving JVM environ...");59pojav_environ->runtimeJavaVMPtr = vm;60JNIEnv *vmEnv;61(*vm)->GetEnv(vm, (void**) &vmEnv, JNI_VERSION_1_4);62pojav_environ->vmGlfwClass = (*vmEnv)->NewGlobalRef(vmEnv, (*vmEnv)->FindClass(vmEnv, "org/lwjgl/glfw/GLFW"));63pojav_environ->method_glftSetWindowAttrib = (*vmEnv)->GetStaticMethodID(vmEnv, pojav_environ->vmGlfwClass, "glfwSetWindowAttrib", "(JII)V");64pojav_environ->method_internalWindowSizeChanged = (*vmEnv)->GetStaticMethodID(vmEnv, pojav_environ->vmGlfwClass, "internalWindowSizeChanged", "(J)V");65pojav_environ->method_internalChangeMonitorSize = (*vmEnv)->GetStaticMethodID(vmEnv, pojav_environ->vmGlfwClass, "internalChangeMonitorSize", "(II)V");66jfieldID field_keyDownBuffer = (*vmEnv)->GetStaticFieldID(vmEnv, pojav_environ->vmGlfwClass, "keyDownBuffer", "Ljava/nio/ByteBuffer;");67jobject keyDownBufferJ = (*vmEnv)->GetStaticObjectField(vmEnv, pojav_environ->vmGlfwClass, field_keyDownBuffer);68pojav_environ->keyDownBuffer = (*vmEnv)->GetDirectBufferAddress(vmEnv, keyDownBufferJ);69jfieldID field_mouseDownBuffer = (*vmEnv)->GetStaticFieldID(vmEnv, pojav_environ->vmGlfwClass, "mouseDownBuffer", "Ljava/nio/ByteBuffer;");70jobject mouseDownBufferJ = (*vmEnv)->GetStaticObjectField(vmEnv, pojav_environ->vmGlfwClass, field_mouseDownBuffer);71pojav_environ->mouseDownBuffer = (*vmEnv)->GetDirectBufferAddress(vmEnv, mouseDownBufferJ);72hookExec(vmEnv);73installLwjglDlopenHook(vmEnv);74installEMUIIteratorMititgation(vmEnv);75}7677if(pojav_environ->dalvikJavaVMPtr == vm) {78//perform in all DVM instances, not only during first ever set up79JNIEnv *env;80(*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_4);81registerFunctions(env);82}83pojav_environ->isGrabbing = JNI_FALSE;8485return JNI_VERSION_1_4;86}8788#define ADD_CALLBACK_WWIN(NAME) \89JNIEXPORT jlong JNICALL Java_org_lwjgl_glfw_GLFW_nglfwSet##NAME##Callback(JNIEnv * env, jclass cls, jlong window, jlong callbackptr) { \90void** oldCallback = (void**) &pojav_environ->GLFW_invoke_##NAME; \91pojav_environ->GLFW_invoke_##NAME = (GLFW_invoke_##NAME##_func*) (uintptr_t) callbackptr; \92return (jlong) (uintptr_t) *oldCallback; \93}9495ADD_CALLBACK_WWIN(Char)96ADD_CALLBACK_WWIN(CharMods)97ADD_CALLBACK_WWIN(CursorEnter)98ADD_CALLBACK_WWIN(CursorPos)99ADD_CALLBACK_WWIN(Key)100ADD_CALLBACK_WWIN(MouseButton)101ADD_CALLBACK_WWIN(Scroll)102103#undef ADD_CALLBACK_WWIN104105void updateMonitorSize(int width, int height) {106(*pojav_environ->glfwThreadVmEnv)->CallStaticVoidMethod(pojav_environ->glfwThreadVmEnv, pojav_environ->vmGlfwClass, pojav_environ->method_internalChangeMonitorSize, width, height);107}108void updateWindowSize(void* window) {109(*pojav_environ->glfwThreadVmEnv)->CallStaticVoidMethod(pojav_environ->glfwThreadVmEnv, pojav_environ->vmGlfwClass, pojav_environ->method_internalWindowSizeChanged, (jlong)window);110}111112void pojavPumpEvents(void* window) {113if(pojav_environ->shouldUpdateMouse) {114pojav_environ->GLFW_invoke_CursorPos(window, floor(pojav_environ->cursorX),115floor(pojav_environ->cursorY));116}117if(pojav_environ->shouldUpdateMonitorSize) {118updateWindowSize(window);119}120121size_t index = pojav_environ->outEventIndex;122size_t targetIndex = pojav_environ->outTargetIndex;123124while (targetIndex != index) {125GLFWInputEvent event = pojav_environ->events[index];126switch (event.type) {127case EVENT_TYPE_CHAR:128if(pojav_environ->GLFW_invoke_Char) pojav_environ->GLFW_invoke_Char(window, event.i1);129break;130case EVENT_TYPE_CHAR_MODS:131if(pojav_environ->GLFW_invoke_CharMods) pojav_environ->GLFW_invoke_CharMods(window, event.i1, event.i2);132break;133case EVENT_TYPE_KEY:134if(pojav_environ->GLFW_invoke_Key) pojav_environ->GLFW_invoke_Key(window, event.i1, event.i2, event.i3, event.i4);135break;136case EVENT_TYPE_MOUSE_BUTTON:137if(pojav_environ->GLFW_invoke_MouseButton) pojav_environ->GLFW_invoke_MouseButton(window, event.i1, event.i2, event.i3);138break;139case EVENT_TYPE_SCROLL:140if(pojav_environ->GLFW_invoke_Scroll) pojav_environ->GLFW_invoke_Scroll(window, event.i1, event.i2);141break;142}143144index++;145if (index >= EVENT_WINDOW_SIZE)146index -= EVENT_WINDOW_SIZE;147}148149// The out target index is updated by the rewinder150}151152/** Prepare the library for sending out callbacks to all windows */153void pojavStartPumping() {154size_t counter = atomic_load_explicit(&pojav_environ->eventCounter, memory_order_acquire);155size_t index = pojav_environ->outEventIndex;156157unsigned targetIndex = index + counter;158if (targetIndex >= EVENT_WINDOW_SIZE)159targetIndex -= EVENT_WINDOW_SIZE;160161// Only accessed by one unique thread, no need for atomic store162pojav_environ->inEventCount = counter;163pojav_environ->outTargetIndex = targetIndex;164165//PumpEvents is called for every window, so this logic should be there in order to correctly distribute events to all windows.166if((pojav_environ->cLastX != pojav_environ->cursorX || pojav_environ->cLastY != pojav_environ->cursorY) && pojav_environ->GLFW_invoke_CursorPos) {167pojav_environ->cLastX = pojav_environ->cursorX;168pojav_environ->cLastY = pojav_environ->cursorY;169pojav_environ->shouldUpdateMouse = true;170}171if(pojav_environ->shouldUpdateMonitorSize) {172// Perform a monitor size update here to avoid doing it on every single window173updateMonitorSize(pojav_environ->savedWidth, pojav_environ->savedHeight);174// Mark the monitor size as consumed (since GLFW was made aware of it)175pojav_environ->monitorSizeConsumed = true;176}177}178179/** Prepare the library for the next round of new events */180void pojavStopPumping() {181pojav_environ->outEventIndex = pojav_environ->outTargetIndex;182183// New events may have arrived while pumping, so remove only the difference before the start and end of execution184atomic_fetch_sub_explicit(&pojav_environ->eventCounter, pojav_environ->inEventCount, memory_order_acquire);185// Make sure the next frame won't send mouse or monitor updates if it's unnecessary186pojav_environ->shouldUpdateMouse = false;187// Only reset the update flag if the monitor size was consumed by pojavStartPumping. This188// will delay the update to next frame if it had occured between pojavStartPumping and pojavStopPumping,189// but it's better than not having it apply at all190if(pojav_environ->shouldUpdateMonitorSize && pojav_environ->monitorSizeConsumed) {191pojav_environ->shouldUpdateMonitorSize = false;192pojav_environ->monitorSizeConsumed = false;193}194195}196197JNIEXPORT void JNICALL198Java_org_lwjgl_glfw_GLFW_nglfwGetCursorPos(JNIEnv *env, __attribute__((unused)) jclass clazz, __attribute__((unused)) jlong window, jobject xpos,199jobject ypos) {200*(double*)(*env)->GetDirectBufferAddress(env, xpos) = pojav_environ->cursorX;201*(double*)(*env)->GetDirectBufferAddress(env, ypos) = pojav_environ->cursorY;202}203204JNIEXPORT void JNICALL JavaCritical_org_lwjgl_glfw_GLFW_nglfwGetCursorPosA(__attribute__((unused)) jlong window, jint lengthx, jdouble* xpos, jint lengthy, jdouble* ypos) {205*xpos = pojav_environ->cursorX;206*ypos = pojav_environ->cursorY;207}208209JNIEXPORT void JNICALL210Java_org_lwjgl_glfw_GLFW_nglfwGetCursorPosA(JNIEnv *env, __attribute__((unused)) jclass clazz, __attribute__((unused)) jlong window,211jdoubleArray xpos, jdoubleArray ypos) {212(*env)->SetDoubleArrayRegion(env, xpos, 0,1, &pojav_environ->cursorX);213(*env)->SetDoubleArrayRegion(env, ypos, 0,1, &pojav_environ->cursorY);214}215216JNIEXPORT void JNICALL JavaCritical_org_lwjgl_glfw_GLFW_glfwSetCursorPos(__attribute__((unused)) jlong window, jdouble xpos,217jdouble ypos) {218pojav_environ->cLastX = pojav_environ->cursorX = xpos;219pojav_environ->cLastY = pojav_environ->cursorY = ypos;220}221222JNIEXPORT void JNICALL223Java_org_lwjgl_glfw_GLFW_glfwSetCursorPos(__attribute__((unused)) JNIEnv *env, __attribute__((unused)) jclass clazz, __attribute__((unused)) jlong window, jdouble xpos,224jdouble ypos) {225JavaCritical_org_lwjgl_glfw_GLFW_glfwSetCursorPos(window, xpos, ypos);226}227228229230void sendData(int type, int i1, int i2, int i3, int i4) {231GLFWInputEvent *event = &pojav_environ->events[pojav_environ->inEventIndex];232event->type = type;233event->i1 = i1;234event->i2 = i2;235event->i3 = i3;236event->i4 = i4;237238if (++pojav_environ->inEventIndex >= EVENT_WINDOW_SIZE)239pojav_environ->inEventIndex -= EVENT_WINDOW_SIZE;240241atomic_fetch_add_explicit(&pojav_environ->eventCounter, 1, memory_order_acquire);242}243244void critical_set_stackqueue(jboolean use_input_stack_queue) {245pojav_environ->isUseStackQueueCall = (int) use_input_stack_queue;246}247248void noncritical_set_stackqueue(__attribute__((unused)) JNIEnv *env, __attribute__((unused)) jclass clazz, jboolean use_input_stack_queue) {249critical_set_stackqueue(use_input_stack_queue);250}251252JNIEXPORT jstring JNICALL Java_org_lwjgl_glfw_CallbackBridge_nativeClipboard(JNIEnv* env, __attribute__((unused)) jclass clazz, jint action, jbyteArray copySrc) {253#ifdef DEBUG254LOGD("Debug: Clipboard access is going on\n", pojav_environ->isUseStackQueueCall);255#endif256257JNIEnv *dalvikEnv;258(*pojav_environ->dalvikJavaVMPtr)->AttachCurrentThread(pojav_environ->dalvikJavaVMPtr, &dalvikEnv, NULL);259assert(dalvikEnv != NULL);260assert(pojav_environ->bridgeClazz != NULL);261262LOGD("Clipboard: Converting string\n");263char *copySrcC;264jstring copyDst = NULL;265if (copySrc) {266copySrcC = (char *)((*env)->GetByteArrayElements(env, copySrc, NULL));267copyDst = (*dalvikEnv)->NewStringUTF(dalvikEnv, copySrcC);268}269270LOGD("Clipboard: Calling 2nd\n");271jstring pasteDst = convertStringJVM(dalvikEnv, env, (jstring) (*dalvikEnv)->CallStaticObjectMethod(dalvikEnv, pojav_environ->bridgeClazz, pojav_environ->method_accessAndroidClipboard, action, copyDst));272273if (copySrc) {274(*dalvikEnv)->DeleteLocalRef(dalvikEnv, copyDst);275(*env)->ReleaseByteArrayElements(env, copySrc, (jbyte *)copySrcC, 0);276}277(*pojav_environ->dalvikJavaVMPtr)->DetachCurrentThread(pojav_environ->dalvikJavaVMPtr);278return pasteDst;279}280281JNIEXPORT jboolean JNICALL JavaCritical_org_lwjgl_glfw_CallbackBridge_nativeSetInputReady(jboolean inputReady) {282#ifdef DEBUG283LOGD("Debug: Changing input state, isReady=%d, pojav_environ->isUseStackQueueCall=%d\n", inputReady, pojav_environ->isUseStackQueueCall);284#endif285LOGI("Input ready: %i", inputReady);286pojav_environ->isInputReady = inputReady;287return pojav_environ->isUseStackQueueCall;288}289290JNIEXPORT jboolean JNICALL Java_org_lwjgl_glfw_CallbackBridge_nativeSetInputReady(__attribute__((unused)) JNIEnv* env, __attribute__((unused)) jclass clazz, jboolean inputReady) {291return JavaCritical_org_lwjgl_glfw_CallbackBridge_nativeSetInputReady(inputReady);292}293294JNIEXPORT void JNICALL Java_org_lwjgl_glfw_CallbackBridge_nativeSetGrabbing(__attribute__((unused)) JNIEnv* env, __attribute__((unused)) jclass clazz, jboolean grabbing) {295TRY_ATTACH_ENV(dvm_env, pojav_environ->dalvikJavaVMPtr, "nativeSetGrabbing failed!\n", return;);296(*dvm_env)->CallStaticVoidMethod(dvm_env, pojav_environ->bridgeClazz, pojav_environ->method_onGrabStateChanged, grabbing);297pojav_environ->isGrabbing = grabbing;298}299300JNIEXPORT jboolean JNICALL301Java_org_lwjgl_glfw_CallbackBridge_nativeEnableGamepadDirectInput(__attribute__((unused)) JNIEnv *env, __attribute__((unused)) jclass clazz) {302TRY_ATTACH_ENV(dvm_env, pojav_environ->dalvikJavaVMPtr, "nativeEnableGamepadDirectInput failed!\n", return JNI_FALSE;);303(*dvm_env)->CallStaticVoidMethod(dvm_env, pojav_environ->bridgeClazz, pojav_environ->method_onDirectInputEnable);304return JNI_TRUE;305}306307jboolean critical_send_char(jchar codepoint) {308if (pojav_environ->GLFW_invoke_Char && pojav_environ->isInputReady) {309if (pojav_environ->isUseStackQueueCall) {310sendData(EVENT_TYPE_CHAR, codepoint, 0, 0, 0);311} else {312pojav_environ->GLFW_invoke_Char((void*) pojav_environ->showingWindow, (unsigned int) codepoint);313}314return JNI_TRUE;315}316return JNI_FALSE;317}318319jboolean noncritical_send_char(__attribute__((unused)) JNIEnv* env, __attribute__((unused)) jclass clazz, jchar codepoint) {320return critical_send_char(codepoint);321}322323jboolean critical_send_char_mods(jchar codepoint, jint mods) {324if (pojav_environ->GLFW_invoke_CharMods && pojav_environ->isInputReady) {325if (pojav_environ->isUseStackQueueCall) {326sendData(EVENT_TYPE_CHAR_MODS, (int) codepoint, mods, 0, 0);327} else {328pojav_environ->GLFW_invoke_CharMods((void*) pojav_environ->showingWindow, codepoint, mods);329}330return JNI_TRUE;331}332return JNI_FALSE;333}334335jboolean noncritical_send_char_mods(__attribute__((unused)) JNIEnv* env, __attribute__((unused)) jclass clazz, jchar codepoint, jint mods) {336return critical_send_char_mods(codepoint, mods);337}338/*339JNIEXPORT void JNICALL Java_org_lwjgl_glfw_CallbackBridge_nativeSendCursorEnter(JNIEnv* env, jclass clazz, jint entered) {340if (pojav_environ->GLFW_invoke_CursorEnter && pojav_environ->isInputReady) {341pojav_environ->GLFW_invoke_CursorEnter(pojav_environ->showingWindow, entered);342}343}344*/345346void critical_send_cursor_pos(jfloat x, jfloat y) {347#ifdef DEBUG348LOGD("Sending cursor position \n");349#endif350if (pojav_environ->GLFW_invoke_CursorPos && pojav_environ->isInputReady) {351#ifdef DEBUG352LOGD("pojav_environ->GLFW_invoke_CursorPos && pojav_environ->isInputReady \n");353#endif354if (!pojav_environ->isCursorEntered) {355if (pojav_environ->GLFW_invoke_CursorEnter) {356pojav_environ->isCursorEntered = true;357if (pojav_environ->isUseStackQueueCall) {358sendData(EVENT_TYPE_CURSOR_ENTER, 1, 0, 0, 0);359} else {360pojav_environ->GLFW_invoke_CursorEnter((void*) pojav_environ->showingWindow, 1);361}362} else if (pojav_environ->isGrabbing) {363// Some Minecraft versions does not use GLFWCursorEnterCallback364// This is a smart check, as Minecraft will not in grab mode if already not.365pojav_environ->isCursorEntered = true;366}367}368369if (!pojav_environ->isUseStackQueueCall) {370pojav_environ->GLFW_invoke_CursorPos((void*) pojav_environ->showingWindow, (double) (x), (double) (y));371} else {372pojav_environ->cursorX = x;373pojav_environ->cursorY = y;374}375}376}377378void noncritical_send_cursor_pos(__attribute__((unused)) JNIEnv* env, __attribute__((unused)) jclass clazz, jfloat x, jfloat y) {379critical_send_cursor_pos(x, y);380}381#define max(a,b) \382({ __typeof__ (a) _a = (a); \383__typeof__ (b) _b = (b); \384_a > _b ? _a : _b; })385void critical_send_key(jint key, jint scancode, jint action, jint mods) {386if (pojav_environ->GLFW_invoke_Key && pojav_environ->isInputReady) {387pojav_environ->keyDownBuffer[max(0, key-31)] = (jbyte) action;388if (pojav_environ->isUseStackQueueCall) {389sendData(EVENT_TYPE_KEY, key, scancode, action, mods);390} else {391pojav_environ->GLFW_invoke_Key((void*) pojav_environ->showingWindow, key, scancode, action, mods);392}393}394}395void noncritical_send_key(__attribute__((unused)) JNIEnv* env, __attribute__((unused)) jclass clazz, jint key, jint scancode, jint action, jint mods) {396critical_send_key(key, scancode, action, mods);397}398399void critical_send_mouse_button(jint button, jint action, jint mods) {400if (pojav_environ->GLFW_invoke_MouseButton && pojav_environ->isInputReady) {401pojav_environ->mouseDownBuffer[max(0, button)] = (jbyte) action;402if (pojav_environ->isUseStackQueueCall) {403sendData(EVENT_TYPE_MOUSE_BUTTON, button, action, mods, 0);404} else {405pojav_environ->GLFW_invoke_MouseButton((void*) pojav_environ->showingWindow, button, action, mods);406}407}408}409410void noncritical_send_mouse_button(__attribute__((unused)) JNIEnv* env, __attribute__((unused)) jclass clazz, jint button, jint action, jint mods) {411critical_send_mouse_button(button, action, mods);412}413414void critical_send_screen_size(jint width, jint height) {415pojav_environ->savedWidth = width;416pojav_environ->savedHeight = height;417// Even if there was call to pojavStartPumping that consumed the size, this call418// might happen right after it (or right before pojavStopPumping)419// So unmark the size as "consumed"420pojav_environ->monitorSizeConsumed = false;421pojav_environ->shouldUpdateMonitorSize = true;422// Don't use the direct updates for screen dimensions.423// This is done to ensure that we have predictable conditions to correctly call424// updateMonitorSize() and updateWindowSize() while on the render thread with an attached425// JNIEnv.426}427428void noncritical_send_screen_size(__attribute__((unused)) JNIEnv* env, __attribute__((unused)) jclass clazz, jint width, jint height) {429critical_send_screen_size(width, height);430}431432void critical_send_scroll(jdouble xoffset, jdouble yoffset) {433if (pojav_environ->GLFW_invoke_Scroll && pojav_environ->isInputReady) {434if (pojav_environ->isUseStackQueueCall) {435sendData(EVENT_TYPE_SCROLL, (int)xoffset, (int)yoffset, 0, 0);436} else {437pojav_environ->GLFW_invoke_Scroll((void*) pojav_environ->showingWindow, (double) xoffset, (double) yoffset);438}439}440}441442void noncritical_send_scroll(__attribute__((unused)) JNIEnv* env, __attribute__((unused)) jclass clazz, jdouble xoffset, jdouble yoffset) {443critical_send_scroll(xoffset, yoffset);444}445446447JNIEXPORT void JNICALL Java_org_lwjgl_glfw_GLFW_nglfwSetShowingWindow(__attribute__((unused)) JNIEnv* env, __attribute__((unused)) jclass clazz, jlong window) {448pojav_environ->showingWindow = (jlong) window;449}450451JNIEXPORT void JNICALL Java_org_lwjgl_glfw_CallbackBridge_nativeSetWindowAttrib(__attribute__((unused)) JNIEnv* env, __attribute__((unused)) jclass clazz, jint attrib, jint value) {452// Check for stack queue no longer necessary here as the JVM crash's origin is resolved453if (!pojav_environ->showingWindow) {454// If the window is not shown, there is nothing to do yet.455return;456}457458// We cannot use pojav_environ->runtimeJNIEnvPtr_JRE here because that environment is attached459// on the thread that loaded pojavexec (which is the thread that first references the GLFW class)460// But this method is only called from the Android UI thread461462// Technically the better solution would be to have a permanently attached env pointer stored463// in environ for the Android UI thread but this is the only place that uses it464// (very rarely, only in lifecycle callbacks) so i dont care465466TRY_ATTACH_ENV(jvm_env, pojav_environ->runtimeJavaVMPtr, "nativeSetWindowAttrib failed: %i", return;);467468(*jvm_env)->CallStaticVoidMethod(469jvm_env, pojav_environ->vmGlfwClass,470pojav_environ->method_glftSetWindowAttrib,471(jlong) pojav_environ->showingWindow, attrib, value472);473474// Attaching every time is annoying, so stick the attachment to the Android GUI thread around475}476const static JNINativeMethod critical_fcns[] = {477{"nativeSetUseInputStackQueue", "(Z)V", critical_set_stackqueue},478{"nativeSendChar", "(C)Z", critical_send_char},479{"nativeSendCharMods", "(CI)Z", critical_send_char_mods},480{"nativeSendKey", "(IIII)V", critical_send_key},481{"nativeSendCursorPos", "(FF)V", critical_send_cursor_pos},482{"nativeSendMouseButton", "(III)V", critical_send_mouse_button},483{"nativeSendScroll", "(DD)V", critical_send_scroll},484{"nativeSendScreenSize", "(II)V", critical_send_screen_size}485};486487const static JNINativeMethod noncritical_fcns[] = {488{"nativeSetUseInputStackQueue", "(Z)V", noncritical_set_stackqueue},489{"nativeSendChar", "(C)Z", noncritical_send_char},490{"nativeSendCharMods", "(CI)Z", noncritical_send_char_mods},491{"nativeSendKey", "(IIII)V", noncritical_send_key},492{"nativeSendCursorPos", "(FF)V", noncritical_send_cursor_pos},493{"nativeSendMouseButton", "(III)V", noncritical_send_mouse_button},494{"nativeSendScroll", "(DD)V", noncritical_send_scroll},495{"nativeSendScreenSize", "(II)V", noncritical_send_screen_size}496};497498499static bool criticalNativeAvailable;500501void dvm_testCriticalNative(void* arg0, void* arg1, void* arg2, void* arg3) {502if(arg0 != 0 && arg2 == 0 && arg3 == 0) {503criticalNativeAvailable = false;504}else if (arg0 == 0 && arg1 == 0){505criticalNativeAvailable = true;506}else {507criticalNativeAvailable = false; // just to be safe508}509}510511static bool tryCriticalNative(JNIEnv *env) {512static const JNINativeMethod testJNIMethod[] = {513{ "testCriticalNative", "(II)V", dvm_testCriticalNative}514};515jclass criticalNativeTest = (*env)->FindClass(env, "net/kdt/pojavlaunch/CriticalNativeTest");516if(criticalNativeTest == NULL) {517LOGD("No CriticalNativeTest class found !");518(*env)->ExceptionClear(env);519return false;520}521jmethodID criticalNativeTestMethod = (*env)->GetStaticMethodID(env, criticalNativeTest, "invokeTest", "()V");522(*env)->RegisterNatives(env, criticalNativeTest, testJNIMethod, 1);523(*env)->CallStaticVoidMethod(env, criticalNativeTest, criticalNativeTestMethod);524(*env)->UnregisterNatives(env, criticalNativeTest);525return criticalNativeAvailable;526}527528static void registerFunctions(JNIEnv *env) {529bool use_critical_cc = tryCriticalNative(env);530jclass bridge_class = (*env)->FindClass(env, "org/lwjgl/glfw/CallbackBridge");531if(use_critical_cc) {532LOGI("CriticalNative is available. Enjoy the 4.6x times faster input!");533}else{534LOGI("CriticalNative is not available. Upgrade, maybe?");535}536(*env)->RegisterNatives(env,537bridge_class,538use_critical_cc ? critical_fcns : noncritical_fcns,539sizeof(critical_fcns)/sizeof(critical_fcns[0]));540}541542JNIEXPORT jlong JNICALL543Java_org_lwjgl_glfw_GLFW_internalGetGamepadDataPointer(JNIEnv *env, jclass clazz) {544return (jlong) &pojav_environ->gamepadState;545}546547JNIEXPORT jobject JNICALL548Java_org_lwjgl_glfw_CallbackBridge_nativeCreateGamepadButtonBuffer(JNIEnv *env, jclass clazz) {549return (*env)->NewDirectByteBuffer(env, &pojav_environ->gamepadState.buttons, sizeof(pojav_environ->gamepadState.buttons));550}551552JNIEXPORT jobject JNICALL553Java_org_lwjgl_glfw_CallbackBridge_nativeCreateGamepadAxisBuffer(JNIEnv *env, jclass clazz) {554return (*env)->NewDirectByteBuffer(env, &pojav_environ->gamepadState.axes, sizeof(pojav_environ->gamepadState.axes));555}556557