Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/PojavLauncher
Path: blob/v3_openjdk/app_pojavlauncher/src/main/jni/input_bridge_v3.c
2128 views
1
/*
2
* V3 input bridge implementation.
3
*
4
* Status:
5
* - Active development
6
* - Works with some bugs:
7
* + Modded versions gives broken stuff..
8
*
9
*
10
* - Implements glfwSetCursorPos() to handle grab camera pos correctly.
11
*/
12
13
#include <assert.h>
14
#include <dlfcn.h>
15
#include <jni.h>
16
#include <libgen.h>
17
#include <stdlib.h>
18
#include <string.h>
19
#include <stdatomic.h>
20
#include <math.h>
21
22
#define TAG __FILE_NAME__
23
#include "log.h"
24
#include "utils.h"
25
#include "environ/environ.h"
26
#include "jvm_hooks/jvm_hooks.h"
27
28
#define EVENT_TYPE_CHAR 1000
29
#define EVENT_TYPE_CHAR_MODS 1001
30
#define EVENT_TYPE_CURSOR_ENTER 1002
31
#define EVENT_TYPE_KEY 1005
32
#define EVENT_TYPE_MOUSE_BUTTON 1006
33
#define EVENT_TYPE_SCROLL 1007
34
35
#define TRY_ATTACH_ENV(env_name, vm, error_message, then) JNIEnv* env_name;\
36
do { \
37
env_name = get_attached_env(vm); \
38
if(env_name == NULL) { \
39
printf(error_message); \
40
then \
41
} \
42
} while(0)
43
44
static void registerFunctions(JNIEnv *env);
45
46
jint JNI_OnLoad(JavaVM* vm, __attribute__((unused)) void* reserved) {
47
if (pojav_environ->dalvikJavaVMPtr == NULL) {
48
LOGI("Saving DVM environ...");
49
//Save dalvik global JavaVM pointer
50
pojav_environ->dalvikJavaVMPtr = vm;
51
JNIEnv *dvEnv;
52
(*vm)->GetEnv(vm, (void**) &dvEnv, JNI_VERSION_1_4);
53
pojav_environ->bridgeClazz = (*dvEnv)->NewGlobalRef(dvEnv,(*dvEnv) ->FindClass(dvEnv,"org/lwjgl/glfw/CallbackBridge"));
54
pojav_environ->method_accessAndroidClipboard = (*dvEnv)->GetStaticMethodID(dvEnv, pojav_environ->bridgeClazz, "accessAndroidClipboard", "(ILjava/lang/String;)Ljava/lang/String;");
55
pojav_environ->method_onGrabStateChanged = (*dvEnv)->GetStaticMethodID(dvEnv, pojav_environ->bridgeClazz, "onGrabStateChanged", "(Z)V");
56
pojav_environ->method_onDirectInputEnable = (*dvEnv)->GetStaticMethodID(dvEnv, pojav_environ->bridgeClazz, "onDirectInputEnable", "()V");
57
pojav_environ->isUseStackQueueCall = JNI_FALSE;
58
} else if (pojav_environ->dalvikJavaVMPtr != vm) {
59
LOGI("Saving JVM environ...");
60
pojav_environ->runtimeJavaVMPtr = vm;
61
JNIEnv *vmEnv;
62
(*vm)->GetEnv(vm, (void**) &vmEnv, JNI_VERSION_1_4);
63
pojav_environ->vmGlfwClass = (*vmEnv)->NewGlobalRef(vmEnv, (*vmEnv)->FindClass(vmEnv, "org/lwjgl/glfw/GLFW"));
64
pojav_environ->method_glftSetWindowAttrib = (*vmEnv)->GetStaticMethodID(vmEnv, pojav_environ->vmGlfwClass, "glfwSetWindowAttrib", "(JII)V");
65
pojav_environ->method_internalWindowSizeChanged = (*vmEnv)->GetStaticMethodID(vmEnv, pojav_environ->vmGlfwClass, "internalWindowSizeChanged", "(J)V");
66
pojav_environ->method_internalChangeMonitorSize = (*vmEnv)->GetStaticMethodID(vmEnv, pojav_environ->vmGlfwClass, "internalChangeMonitorSize", "(II)V");
67
jfieldID field_keyDownBuffer = (*vmEnv)->GetStaticFieldID(vmEnv, pojav_environ->vmGlfwClass, "keyDownBuffer", "Ljava/nio/ByteBuffer;");
68
jobject keyDownBufferJ = (*vmEnv)->GetStaticObjectField(vmEnv, pojav_environ->vmGlfwClass, field_keyDownBuffer);
69
pojav_environ->keyDownBuffer = (*vmEnv)->GetDirectBufferAddress(vmEnv, keyDownBufferJ);
70
jfieldID field_mouseDownBuffer = (*vmEnv)->GetStaticFieldID(vmEnv, pojav_environ->vmGlfwClass, "mouseDownBuffer", "Ljava/nio/ByteBuffer;");
71
jobject mouseDownBufferJ = (*vmEnv)->GetStaticObjectField(vmEnv, pojav_environ->vmGlfwClass, field_mouseDownBuffer);
72
pojav_environ->mouseDownBuffer = (*vmEnv)->GetDirectBufferAddress(vmEnv, mouseDownBufferJ);
73
hookExec(vmEnv);
74
installLwjglDlopenHook(vmEnv);
75
installEMUIIteratorMititgation(vmEnv);
76
}
77
78
if(pojav_environ->dalvikJavaVMPtr == vm) {
79
//perform in all DVM instances, not only during first ever set up
80
JNIEnv *env;
81
(*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_4);
82
registerFunctions(env);
83
}
84
pojav_environ->isGrabbing = JNI_FALSE;
85
86
return JNI_VERSION_1_4;
87
}
88
89
#define ADD_CALLBACK_WWIN(NAME) \
90
JNIEXPORT jlong JNICALL Java_org_lwjgl_glfw_GLFW_nglfwSet##NAME##Callback(JNIEnv * env, jclass cls, jlong window, jlong callbackptr) { \
91
void** oldCallback = (void**) &pojav_environ->GLFW_invoke_##NAME; \
92
pojav_environ->GLFW_invoke_##NAME = (GLFW_invoke_##NAME##_func*) (uintptr_t) callbackptr; \
93
return (jlong) (uintptr_t) *oldCallback; \
94
}
95
96
ADD_CALLBACK_WWIN(Char)
97
ADD_CALLBACK_WWIN(CharMods)
98
ADD_CALLBACK_WWIN(CursorEnter)
99
ADD_CALLBACK_WWIN(CursorPos)
100
ADD_CALLBACK_WWIN(Key)
101
ADD_CALLBACK_WWIN(MouseButton)
102
ADD_CALLBACK_WWIN(Scroll)
103
104
#undef ADD_CALLBACK_WWIN
105
106
void updateMonitorSize(int width, int height) {
107
(*pojav_environ->glfwThreadVmEnv)->CallStaticVoidMethod(pojav_environ->glfwThreadVmEnv, pojav_environ->vmGlfwClass, pojav_environ->method_internalChangeMonitorSize, width, height);
108
}
109
void updateWindowSize(void* window) {
110
(*pojav_environ->glfwThreadVmEnv)->CallStaticVoidMethod(pojav_environ->glfwThreadVmEnv, pojav_environ->vmGlfwClass, pojav_environ->method_internalWindowSizeChanged, (jlong)window);
111
}
112
113
void pojavPumpEvents(void* window) {
114
if(pojav_environ->shouldUpdateMouse) {
115
pojav_environ->GLFW_invoke_CursorPos(window, floor(pojav_environ->cursorX),
116
floor(pojav_environ->cursorY));
117
}
118
if(pojav_environ->shouldUpdateMonitorSize) {
119
updateWindowSize(window);
120
}
121
122
size_t index = pojav_environ->outEventIndex;
123
size_t targetIndex = pojav_environ->outTargetIndex;
124
125
while (targetIndex != index) {
126
GLFWInputEvent event = pojav_environ->events[index];
127
switch (event.type) {
128
case EVENT_TYPE_CHAR:
129
if(pojav_environ->GLFW_invoke_Char) pojav_environ->GLFW_invoke_Char(window, event.i1);
130
break;
131
case EVENT_TYPE_CHAR_MODS:
132
if(pojav_environ->GLFW_invoke_CharMods) pojav_environ->GLFW_invoke_CharMods(window, event.i1, event.i2);
133
break;
134
case EVENT_TYPE_KEY:
135
if(pojav_environ->GLFW_invoke_Key) pojav_environ->GLFW_invoke_Key(window, event.i1, event.i2, event.i3, event.i4);
136
break;
137
case EVENT_TYPE_MOUSE_BUTTON:
138
if(pojav_environ->GLFW_invoke_MouseButton) pojav_environ->GLFW_invoke_MouseButton(window, event.i1, event.i2, event.i3);
139
break;
140
case EVENT_TYPE_SCROLL:
141
if(pojav_environ->GLFW_invoke_Scroll) pojav_environ->GLFW_invoke_Scroll(window, event.i1, event.i2);
142
break;
143
}
144
145
index++;
146
if (index >= EVENT_WINDOW_SIZE)
147
index -= EVENT_WINDOW_SIZE;
148
}
149
150
// The out target index is updated by the rewinder
151
}
152
153
/** Prepare the library for sending out callbacks to all windows */
154
void pojavStartPumping() {
155
size_t counter = atomic_load_explicit(&pojav_environ->eventCounter, memory_order_acquire);
156
size_t index = pojav_environ->outEventIndex;
157
158
unsigned targetIndex = index + counter;
159
if (targetIndex >= EVENT_WINDOW_SIZE)
160
targetIndex -= EVENT_WINDOW_SIZE;
161
162
// Only accessed by one unique thread, no need for atomic store
163
pojav_environ->inEventCount = counter;
164
pojav_environ->outTargetIndex = targetIndex;
165
166
//PumpEvents is called for every window, so this logic should be there in order to correctly distribute events to all windows.
167
if((pojav_environ->cLastX != pojav_environ->cursorX || pojav_environ->cLastY != pojav_environ->cursorY) && pojav_environ->GLFW_invoke_CursorPos) {
168
pojav_environ->cLastX = pojav_environ->cursorX;
169
pojav_environ->cLastY = pojav_environ->cursorY;
170
pojav_environ->shouldUpdateMouse = true;
171
}
172
if(pojav_environ->shouldUpdateMonitorSize) {
173
// Perform a monitor size update here to avoid doing it on every single window
174
updateMonitorSize(pojav_environ->savedWidth, pojav_environ->savedHeight);
175
// Mark the monitor size as consumed (since GLFW was made aware of it)
176
pojav_environ->monitorSizeConsumed = true;
177
}
178
}
179
180
/** Prepare the library for the next round of new events */
181
void pojavStopPumping() {
182
pojav_environ->outEventIndex = pojav_environ->outTargetIndex;
183
184
// New events may have arrived while pumping, so remove only the difference before the start and end of execution
185
atomic_fetch_sub_explicit(&pojav_environ->eventCounter, pojav_environ->inEventCount, memory_order_acquire);
186
// Make sure the next frame won't send mouse or monitor updates if it's unnecessary
187
pojav_environ->shouldUpdateMouse = false;
188
// Only reset the update flag if the monitor size was consumed by pojavStartPumping. This
189
// will delay the update to next frame if it had occured between pojavStartPumping and pojavStopPumping,
190
// but it's better than not having it apply at all
191
if(pojav_environ->shouldUpdateMonitorSize && pojav_environ->monitorSizeConsumed) {
192
pojav_environ->shouldUpdateMonitorSize = false;
193
pojav_environ->monitorSizeConsumed = false;
194
}
195
196
}
197
198
JNIEXPORT void JNICALL
199
Java_org_lwjgl_glfw_GLFW_nglfwGetCursorPos(JNIEnv *env, __attribute__((unused)) jclass clazz, __attribute__((unused)) jlong window, jobject xpos,
200
jobject ypos) {
201
*(double*)(*env)->GetDirectBufferAddress(env, xpos) = pojav_environ->cursorX;
202
*(double*)(*env)->GetDirectBufferAddress(env, ypos) = pojav_environ->cursorY;
203
}
204
205
JNIEXPORT void JNICALL JavaCritical_org_lwjgl_glfw_GLFW_nglfwGetCursorPosA(__attribute__((unused)) jlong window, jint lengthx, jdouble* xpos, jint lengthy, jdouble* ypos) {
206
*xpos = pojav_environ->cursorX;
207
*ypos = pojav_environ->cursorY;
208
}
209
210
JNIEXPORT void JNICALL
211
Java_org_lwjgl_glfw_GLFW_nglfwGetCursorPosA(JNIEnv *env, __attribute__((unused)) jclass clazz, __attribute__((unused)) jlong window,
212
jdoubleArray xpos, jdoubleArray ypos) {
213
(*env)->SetDoubleArrayRegion(env, xpos, 0,1, &pojav_environ->cursorX);
214
(*env)->SetDoubleArrayRegion(env, ypos, 0,1, &pojav_environ->cursorY);
215
}
216
217
JNIEXPORT void JNICALL JavaCritical_org_lwjgl_glfw_GLFW_glfwSetCursorPos(__attribute__((unused)) jlong window, jdouble xpos,
218
jdouble ypos) {
219
pojav_environ->cLastX = pojav_environ->cursorX = xpos;
220
pojav_environ->cLastY = pojav_environ->cursorY = ypos;
221
}
222
223
JNIEXPORT void JNICALL
224
Java_org_lwjgl_glfw_GLFW_glfwSetCursorPos(__attribute__((unused)) JNIEnv *env, __attribute__((unused)) jclass clazz, __attribute__((unused)) jlong window, jdouble xpos,
225
jdouble ypos) {
226
JavaCritical_org_lwjgl_glfw_GLFW_glfwSetCursorPos(window, xpos, ypos);
227
}
228
229
230
231
void sendData(int type, int i1, int i2, int i3, int i4) {
232
GLFWInputEvent *event = &pojav_environ->events[pojav_environ->inEventIndex];
233
event->type = type;
234
event->i1 = i1;
235
event->i2 = i2;
236
event->i3 = i3;
237
event->i4 = i4;
238
239
if (++pojav_environ->inEventIndex >= EVENT_WINDOW_SIZE)
240
pojav_environ->inEventIndex -= EVENT_WINDOW_SIZE;
241
242
atomic_fetch_add_explicit(&pojav_environ->eventCounter, 1, memory_order_acquire);
243
}
244
245
void critical_set_stackqueue(jboolean use_input_stack_queue) {
246
pojav_environ->isUseStackQueueCall = (int) use_input_stack_queue;
247
}
248
249
void noncritical_set_stackqueue(__attribute__((unused)) JNIEnv *env, __attribute__((unused)) jclass clazz, jboolean use_input_stack_queue) {
250
critical_set_stackqueue(use_input_stack_queue);
251
}
252
253
JNIEXPORT jstring JNICALL Java_org_lwjgl_glfw_CallbackBridge_nativeClipboard(JNIEnv* env, __attribute__((unused)) jclass clazz, jint action, jbyteArray copySrc) {
254
#ifdef DEBUG
255
LOGD("Debug: Clipboard access is going on\n", pojav_environ->isUseStackQueueCall);
256
#endif
257
258
JNIEnv *dalvikEnv;
259
(*pojav_environ->dalvikJavaVMPtr)->AttachCurrentThread(pojav_environ->dalvikJavaVMPtr, &dalvikEnv, NULL);
260
assert(dalvikEnv != NULL);
261
assert(pojav_environ->bridgeClazz != NULL);
262
263
LOGD("Clipboard: Converting string\n");
264
char *copySrcC;
265
jstring copyDst = NULL;
266
if (copySrc) {
267
copySrcC = (char *)((*env)->GetByteArrayElements(env, copySrc, NULL));
268
copyDst = (*dalvikEnv)->NewStringUTF(dalvikEnv, copySrcC);
269
}
270
271
LOGD("Clipboard: Calling 2nd\n");
272
jstring pasteDst = convertStringJVM(dalvikEnv, env, (jstring) (*dalvikEnv)->CallStaticObjectMethod(dalvikEnv, pojav_environ->bridgeClazz, pojav_environ->method_accessAndroidClipboard, action, copyDst));
273
274
if (copySrc) {
275
(*dalvikEnv)->DeleteLocalRef(dalvikEnv, copyDst);
276
(*env)->ReleaseByteArrayElements(env, copySrc, (jbyte *)copySrcC, 0);
277
}
278
(*pojav_environ->dalvikJavaVMPtr)->DetachCurrentThread(pojav_environ->dalvikJavaVMPtr);
279
return pasteDst;
280
}
281
282
JNIEXPORT jboolean JNICALL JavaCritical_org_lwjgl_glfw_CallbackBridge_nativeSetInputReady(jboolean inputReady) {
283
#ifdef DEBUG
284
LOGD("Debug: Changing input state, isReady=%d, pojav_environ->isUseStackQueueCall=%d\n", inputReady, pojav_environ->isUseStackQueueCall);
285
#endif
286
LOGI("Input ready: %i", inputReady);
287
pojav_environ->isInputReady = inputReady;
288
return pojav_environ->isUseStackQueueCall;
289
}
290
291
JNIEXPORT jboolean JNICALL Java_org_lwjgl_glfw_CallbackBridge_nativeSetInputReady(__attribute__((unused)) JNIEnv* env, __attribute__((unused)) jclass clazz, jboolean inputReady) {
292
return JavaCritical_org_lwjgl_glfw_CallbackBridge_nativeSetInputReady(inputReady);
293
}
294
295
JNIEXPORT void JNICALL Java_org_lwjgl_glfw_CallbackBridge_nativeSetGrabbing(__attribute__((unused)) JNIEnv* env, __attribute__((unused)) jclass clazz, jboolean grabbing) {
296
TRY_ATTACH_ENV(dvm_env, pojav_environ->dalvikJavaVMPtr, "nativeSetGrabbing failed!\n", return;);
297
(*dvm_env)->CallStaticVoidMethod(dvm_env, pojav_environ->bridgeClazz, pojav_environ->method_onGrabStateChanged, grabbing);
298
pojav_environ->isGrabbing = grabbing;
299
}
300
301
JNIEXPORT jboolean JNICALL
302
Java_org_lwjgl_glfw_CallbackBridge_nativeEnableGamepadDirectInput(__attribute__((unused)) JNIEnv *env, __attribute__((unused)) jclass clazz) {
303
TRY_ATTACH_ENV(dvm_env, pojav_environ->dalvikJavaVMPtr, "nativeEnableGamepadDirectInput failed!\n", return JNI_FALSE;);
304
(*dvm_env)->CallStaticVoidMethod(dvm_env, pojav_environ->bridgeClazz, pojav_environ->method_onDirectInputEnable);
305
return JNI_TRUE;
306
}
307
308
jboolean critical_send_char(jchar codepoint) {
309
if (pojav_environ->GLFW_invoke_Char && pojav_environ->isInputReady) {
310
if (pojav_environ->isUseStackQueueCall) {
311
sendData(EVENT_TYPE_CHAR, codepoint, 0, 0, 0);
312
} else {
313
pojav_environ->GLFW_invoke_Char((void*) pojav_environ->showingWindow, (unsigned int) codepoint);
314
}
315
return JNI_TRUE;
316
}
317
return JNI_FALSE;
318
}
319
320
jboolean noncritical_send_char(__attribute__((unused)) JNIEnv* env, __attribute__((unused)) jclass clazz, jchar codepoint) {
321
return critical_send_char(codepoint);
322
}
323
324
jboolean critical_send_char_mods(jchar codepoint, jint mods) {
325
if (pojav_environ->GLFW_invoke_CharMods && pojav_environ->isInputReady) {
326
if (pojav_environ->isUseStackQueueCall) {
327
sendData(EVENT_TYPE_CHAR_MODS, (int) codepoint, mods, 0, 0);
328
} else {
329
pojav_environ->GLFW_invoke_CharMods((void*) pojav_environ->showingWindow, codepoint, mods);
330
}
331
return JNI_TRUE;
332
}
333
return JNI_FALSE;
334
}
335
336
jboolean noncritical_send_char_mods(__attribute__((unused)) JNIEnv* env, __attribute__((unused)) jclass clazz, jchar codepoint, jint mods) {
337
return critical_send_char_mods(codepoint, mods);
338
}
339
/*
340
JNIEXPORT void JNICALL Java_org_lwjgl_glfw_CallbackBridge_nativeSendCursorEnter(JNIEnv* env, jclass clazz, jint entered) {
341
if (pojav_environ->GLFW_invoke_CursorEnter && pojav_environ->isInputReady) {
342
pojav_environ->GLFW_invoke_CursorEnter(pojav_environ->showingWindow, entered);
343
}
344
}
345
*/
346
347
void critical_send_cursor_pos(jfloat x, jfloat y) {
348
#ifdef DEBUG
349
LOGD("Sending cursor position \n");
350
#endif
351
if (pojav_environ->GLFW_invoke_CursorPos && pojav_environ->isInputReady) {
352
#ifdef DEBUG
353
LOGD("pojav_environ->GLFW_invoke_CursorPos && pojav_environ->isInputReady \n");
354
#endif
355
if (!pojav_environ->isCursorEntered) {
356
if (pojav_environ->GLFW_invoke_CursorEnter) {
357
pojav_environ->isCursorEntered = true;
358
if (pojav_environ->isUseStackQueueCall) {
359
sendData(EVENT_TYPE_CURSOR_ENTER, 1, 0, 0, 0);
360
} else {
361
pojav_environ->GLFW_invoke_CursorEnter((void*) pojav_environ->showingWindow, 1);
362
}
363
} else if (pojav_environ->isGrabbing) {
364
// Some Minecraft versions does not use GLFWCursorEnterCallback
365
// This is a smart check, as Minecraft will not in grab mode if already not.
366
pojav_environ->isCursorEntered = true;
367
}
368
}
369
370
if (!pojav_environ->isUseStackQueueCall) {
371
pojav_environ->GLFW_invoke_CursorPos((void*) pojav_environ->showingWindow, (double) (x), (double) (y));
372
} else {
373
pojav_environ->cursorX = x;
374
pojav_environ->cursorY = y;
375
}
376
}
377
}
378
379
void noncritical_send_cursor_pos(__attribute__((unused)) JNIEnv* env, __attribute__((unused)) jclass clazz, jfloat x, jfloat y) {
380
critical_send_cursor_pos(x, y);
381
}
382
#define max(a,b) \
383
({ __typeof__ (a) _a = (a); \
384
__typeof__ (b) _b = (b); \
385
_a > _b ? _a : _b; })
386
void critical_send_key(jint key, jint scancode, jint action, jint mods) {
387
if (pojav_environ->GLFW_invoke_Key && pojav_environ->isInputReady) {
388
pojav_environ->keyDownBuffer[max(0, key-31)] = (jbyte) action;
389
if (pojav_environ->isUseStackQueueCall) {
390
sendData(EVENT_TYPE_KEY, key, scancode, action, mods);
391
} else {
392
pojav_environ->GLFW_invoke_Key((void*) pojav_environ->showingWindow, key, scancode, action, mods);
393
}
394
}
395
}
396
void noncritical_send_key(__attribute__((unused)) JNIEnv* env, __attribute__((unused)) jclass clazz, jint key, jint scancode, jint action, jint mods) {
397
critical_send_key(key, scancode, action, mods);
398
}
399
400
void critical_send_mouse_button(jint button, jint action, jint mods) {
401
if (pojav_environ->GLFW_invoke_MouseButton && pojav_environ->isInputReady) {
402
pojav_environ->mouseDownBuffer[max(0, button)] = (jbyte) action;
403
if (pojav_environ->isUseStackQueueCall) {
404
sendData(EVENT_TYPE_MOUSE_BUTTON, button, action, mods, 0);
405
} else {
406
pojav_environ->GLFW_invoke_MouseButton((void*) pojav_environ->showingWindow, button, action, mods);
407
}
408
}
409
}
410
411
void noncritical_send_mouse_button(__attribute__((unused)) JNIEnv* env, __attribute__((unused)) jclass clazz, jint button, jint action, jint mods) {
412
critical_send_mouse_button(button, action, mods);
413
}
414
415
void critical_send_screen_size(jint width, jint height) {
416
pojav_environ->savedWidth = width;
417
pojav_environ->savedHeight = height;
418
// Even if there was call to pojavStartPumping that consumed the size, this call
419
// might happen right after it (or right before pojavStopPumping)
420
// So unmark the size as "consumed"
421
pojav_environ->monitorSizeConsumed = false;
422
pojav_environ->shouldUpdateMonitorSize = true;
423
// Don't use the direct updates for screen dimensions.
424
// This is done to ensure that we have predictable conditions to correctly call
425
// updateMonitorSize() and updateWindowSize() while on the render thread with an attached
426
// JNIEnv.
427
}
428
429
void noncritical_send_screen_size(__attribute__((unused)) JNIEnv* env, __attribute__((unused)) jclass clazz, jint width, jint height) {
430
critical_send_screen_size(width, height);
431
}
432
433
void critical_send_scroll(jdouble xoffset, jdouble yoffset) {
434
if (pojav_environ->GLFW_invoke_Scroll && pojav_environ->isInputReady) {
435
if (pojav_environ->isUseStackQueueCall) {
436
sendData(EVENT_TYPE_SCROLL, (int)xoffset, (int)yoffset, 0, 0);
437
} else {
438
pojav_environ->GLFW_invoke_Scroll((void*) pojav_environ->showingWindow, (double) xoffset, (double) yoffset);
439
}
440
}
441
}
442
443
void noncritical_send_scroll(__attribute__((unused)) JNIEnv* env, __attribute__((unused)) jclass clazz, jdouble xoffset, jdouble yoffset) {
444
critical_send_scroll(xoffset, yoffset);
445
}
446
447
448
JNIEXPORT void JNICALL Java_org_lwjgl_glfw_GLFW_nglfwSetShowingWindow(__attribute__((unused)) JNIEnv* env, __attribute__((unused)) jclass clazz, jlong window) {
449
pojav_environ->showingWindow = (jlong) window;
450
}
451
452
JNIEXPORT void JNICALL Java_org_lwjgl_glfw_CallbackBridge_nativeSetWindowAttrib(__attribute__((unused)) JNIEnv* env, __attribute__((unused)) jclass clazz, jint attrib, jint value) {
453
// Check for stack queue no longer necessary here as the JVM crash's origin is resolved
454
if (!pojav_environ->showingWindow) {
455
// If the window is not shown, there is nothing to do yet.
456
return;
457
}
458
459
// We cannot use pojav_environ->runtimeJNIEnvPtr_JRE here because that environment is attached
460
// on the thread that loaded pojavexec (which is the thread that first references the GLFW class)
461
// But this method is only called from the Android UI thread
462
463
// Technically the better solution would be to have a permanently attached env pointer stored
464
// in environ for the Android UI thread but this is the only place that uses it
465
// (very rarely, only in lifecycle callbacks) so i dont care
466
467
TRY_ATTACH_ENV(jvm_env, pojav_environ->runtimeJavaVMPtr, "nativeSetWindowAttrib failed: %i", return;);
468
469
(*jvm_env)->CallStaticVoidMethod(
470
jvm_env, pojav_environ->vmGlfwClass,
471
pojav_environ->method_glftSetWindowAttrib,
472
(jlong) pojav_environ->showingWindow, attrib, value
473
);
474
475
// Attaching every time is annoying, so stick the attachment to the Android GUI thread around
476
}
477
const static JNINativeMethod critical_fcns[] = {
478
{"nativeSetUseInputStackQueue", "(Z)V", critical_set_stackqueue},
479
{"nativeSendChar", "(C)Z", critical_send_char},
480
{"nativeSendCharMods", "(CI)Z", critical_send_char_mods},
481
{"nativeSendKey", "(IIII)V", critical_send_key},
482
{"nativeSendCursorPos", "(FF)V", critical_send_cursor_pos},
483
{"nativeSendMouseButton", "(III)V", critical_send_mouse_button},
484
{"nativeSendScroll", "(DD)V", critical_send_scroll},
485
{"nativeSendScreenSize", "(II)V", critical_send_screen_size}
486
};
487
488
const static JNINativeMethod noncritical_fcns[] = {
489
{"nativeSetUseInputStackQueue", "(Z)V", noncritical_set_stackqueue},
490
{"nativeSendChar", "(C)Z", noncritical_send_char},
491
{"nativeSendCharMods", "(CI)Z", noncritical_send_char_mods},
492
{"nativeSendKey", "(IIII)V", noncritical_send_key},
493
{"nativeSendCursorPos", "(FF)V", noncritical_send_cursor_pos},
494
{"nativeSendMouseButton", "(III)V", noncritical_send_mouse_button},
495
{"nativeSendScroll", "(DD)V", noncritical_send_scroll},
496
{"nativeSendScreenSize", "(II)V", noncritical_send_screen_size}
497
};
498
499
500
static bool criticalNativeAvailable;
501
502
void dvm_testCriticalNative(void* arg0, void* arg1, void* arg2, void* arg3) {
503
if(arg0 != 0 && arg2 == 0 && arg3 == 0) {
504
criticalNativeAvailable = false;
505
}else if (arg0 == 0 && arg1 == 0){
506
criticalNativeAvailable = true;
507
}else {
508
criticalNativeAvailable = false; // just to be safe
509
}
510
}
511
512
static bool tryCriticalNative(JNIEnv *env) {
513
static const JNINativeMethod testJNIMethod[] = {
514
{ "testCriticalNative", "(II)V", dvm_testCriticalNative}
515
};
516
jclass criticalNativeTest = (*env)->FindClass(env, "net/kdt/pojavlaunch/CriticalNativeTest");
517
if(criticalNativeTest == NULL) {
518
LOGD("No CriticalNativeTest class found !");
519
(*env)->ExceptionClear(env);
520
return false;
521
}
522
jmethodID criticalNativeTestMethod = (*env)->GetStaticMethodID(env, criticalNativeTest, "invokeTest", "()V");
523
(*env)->RegisterNatives(env, criticalNativeTest, testJNIMethod, 1);
524
(*env)->CallStaticVoidMethod(env, criticalNativeTest, criticalNativeTestMethod);
525
(*env)->UnregisterNatives(env, criticalNativeTest);
526
return criticalNativeAvailable;
527
}
528
529
static void registerFunctions(JNIEnv *env) {
530
bool use_critical_cc = tryCriticalNative(env);
531
jclass bridge_class = (*env)->FindClass(env, "org/lwjgl/glfw/CallbackBridge");
532
if(use_critical_cc) {
533
LOGI("CriticalNative is available. Enjoy the 4.6x times faster input!");
534
}else{
535
LOGI("CriticalNative is not available. Upgrade, maybe?");
536
}
537
(*env)->RegisterNatives(env,
538
bridge_class,
539
use_critical_cc ? critical_fcns : noncritical_fcns,
540
sizeof(critical_fcns)/sizeof(critical_fcns[0]));
541
}
542
543
JNIEXPORT jlong JNICALL
544
Java_org_lwjgl_glfw_GLFW_internalGetGamepadDataPointer(JNIEnv *env, jclass clazz) {
545
return (jlong) &pojav_environ->gamepadState;
546
}
547
548
JNIEXPORT jobject JNICALL
549
Java_org_lwjgl_glfw_CallbackBridge_nativeCreateGamepadButtonBuffer(JNIEnv *env, jclass clazz) {
550
return (*env)->NewDirectByteBuffer(env, &pojav_environ->gamepadState.buttons, sizeof(pojav_environ->gamepadState.buttons));
551
}
552
553
JNIEXPORT jobject JNICALL
554
Java_org_lwjgl_glfw_CallbackBridge_nativeCreateGamepadAxisBuffer(JNIEnv *env, jclass clazz) {
555
return (*env)->NewDirectByteBuffer(env, &pojav_environ->gamepadState.axes, sizeof(pojav_environ->gamepadState.axes));
556
}
557