596 lines
21 KiB
Objective-C
596 lines
21 KiB
Objective-C
/*
|
|
* V3 input bridge implementation.
|
|
*
|
|
* Status:
|
|
* - Active development
|
|
* - Works with some bugs:
|
|
* + Modded versions gives broken stuff..
|
|
*/
|
|
|
|
#import <UIKit/UIKit.h>
|
|
#import "AppDelegate.h"
|
|
#import "SurfaceViewController.h"
|
|
|
|
#include <assert.h>
|
|
#include <dlfcn.h>
|
|
#include <libgen.h>
|
|
#include <stdlib.h>
|
|
#include <stdatomic.h>
|
|
|
|
#include "jni.h"
|
|
#include "glfw_keycodes.h"
|
|
#include "ios_uikit_bridge.h"
|
|
#include "utils.h"
|
|
|
|
#include "JavaLauncher.h"
|
|
|
|
jint (*orig_ProcessImpl_forkAndExec)(JNIEnv *env, jobject process, jint mode, jbyteArray helperpath, jbyteArray prog, jbyteArray argBlock, jint argc, jbyteArray envBlock, jint envc, jbyteArray dir, jintArray std_fds, jboolean redirectErrorStream);
|
|
jlong (*orig_ProcessHandleImpl_isAlive0)(JNIEnv *env, jclass clazz, jlong jpid);
|
|
|
|
NSString* processPath(NSString* path) {
|
|
if ([path hasPrefix:@"file:"]) {
|
|
path = [path substringFromIndex:5].stringByRemovingPercentEncoding;
|
|
}
|
|
path = path.stringByResolvingSymlinksInPath;
|
|
|
|
NSString *prefix = @"file";
|
|
if ([UIApplication.sharedApplication canOpenURL:[NSURL URLWithString:@"shareddocuments://"]] &&
|
|
![path hasPrefix:@"/var/mobile/Documents"]) {
|
|
// Prefer opening in Files if containerized
|
|
prefix = @"shareddocuments";
|
|
} else if ([UIApplication.sharedApplication canOpenURL:[NSURL URLWithString:@"filza://"]]) {
|
|
// Open in Filza if installed
|
|
prefix = @"filza";
|
|
} else if ([UIApplication.sharedApplication canOpenURL:[NSURL URLWithString:@"santander://"]]) {
|
|
// Open in Santander if installed
|
|
prefix = @"santander";
|
|
}
|
|
|
|
return [NSString stringWithFormat:@"%@://%@", prefix, path];
|
|
}
|
|
|
|
void openURLGlobal(NSString *path) {
|
|
dispatch_group_t group = dispatch_group_create();
|
|
dispatch_group_enter(group);
|
|
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
if ([path hasPrefix:@"http"]) {
|
|
openLink(UIWindow.mainWindow.rootViewController, [NSURL URLWithString:path]);
|
|
dispatch_group_leave(group);
|
|
return;
|
|
}
|
|
NSString *realPath = processPath(path);
|
|
[UIApplication.sharedApplication openURL:[NSURL URLWithString:realPath] options:@{} completionHandler:^(BOOL success) {
|
|
if (success) {
|
|
NSLog(@"Opened \"%@\"", realPath);
|
|
} else {
|
|
NSLog(@"Failed to open \"%@\"", realPath);
|
|
}
|
|
dispatch_group_leave(group);
|
|
}];
|
|
});
|
|
|
|
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
|
|
}
|
|
|
|
/**
|
|
* Hooked version of java.lang.UNIXProcess.forkAndExec()
|
|
* which is used to handle the "open" command.
|
|
*/
|
|
jint
|
|
hooked_ProcessImpl_forkAndExec(JNIEnv *env, jobject process, jint mode, jbyteArray helperpath, jbyteArray prog, jbyteArray argBlock, jint argc, jbyteArray envBlock, jint envc, jbyteArray dir, jintArray std_fds, jboolean redirectErrorStream) {
|
|
char *pProg = (char *)((*env)->GetByteArrayElements(env, prog, NULL));
|
|
|
|
// Here we only handle the "open" command
|
|
if (strcmp(basename(pProg), "open")) {
|
|
(*env)->ReleaseByteArrayElements(env, prog, (jbyte *)pProg, 0);
|
|
return orig_ProcessImpl_forkAndExec(env, process, mode, helperpath, prog, argBlock, argc, envBlock, envc, dir, std_fds, redirectErrorStream);
|
|
}
|
|
|
|
char *path = (char *)((*env)->GetByteArrayElements(env, argBlock, NULL));
|
|
openURLGlobal(@(path));
|
|
|
|
(*env)->ReleaseByteArrayElements(env, prog, (jbyte *)pProg, 0);
|
|
(*env)->ReleaseByteArrayElements(env, argBlock, (jbyte *)path, 0);
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Hooked version of java.lang.ProcessHandleImpl.isAlive0()
|
|
* which is used to ignore "Operation not permitted"
|
|
*/
|
|
jlong hooked_ProcessHandleImpl_isAlive0(JNIEnv *env, jclass clazz, jlong jpid) {
|
|
jlong result = orig_ProcessHandleImpl_isAlive0(env, clazz, jpid);
|
|
if ((*env)->ExceptionOccurred(env)) {
|
|
(*env)->ExceptionClear(env);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
// Part of awt_bridge
|
|
void CTCClipboard_nQuerySystemClipboard(JNIEnv *env, jclass clazz) {
|
|
if(method_SystemClipboardDataReceived == NULL) {
|
|
class_CTCClipboard = (*env)->NewGlobalRef(env, clazz);
|
|
method_SystemClipboardDataReceived = (*env)->GetStaticMethodID(env, clazz, "systemClipboardDataReceived", "(Ljava/lang/String;Ljava/lang/String;)V");
|
|
}
|
|
// From Java_net_kdt_pojavlaunch_AWTInputBridge_nativeClipboardReceived
|
|
// Note: we cannot use main_queue here as it will cause deadlock
|
|
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
|
JNIEnv *env;
|
|
(*runtimeJavaVMPtr)->AttachCurrentThread(runtimeJavaVMPtr, &env, NULL);
|
|
const char* mimeChars = "text/plain";
|
|
(*env)->CallStaticVoidMethod(env, class_CTCClipboard, method_SystemClipboardDataReceived,
|
|
UIKit_accessClipboard(env, CLIPBOARD_PASTE, NULL),
|
|
(*env)->NewStringUTF(env, mimeChars));
|
|
(*runtimeJavaVMPtr)->DetachCurrentThread(runtimeJavaVMPtr);
|
|
});
|
|
}
|
|
|
|
void CTCClipboard_nPutClipboardData(JNIEnv* env, jclass clazz, jstring clipboardData, jstring clipboardDataMime) {
|
|
// TODO: handle non-text data(?)
|
|
UIKit_accessClipboard(env, CLIPBOARD_COPY, clipboardData);
|
|
}
|
|
|
|
void CTCDesktopPeer_openGlobal(JNIEnv *env, jclass clazz, jstring path) {
|
|
const char* stringChars = (*env)->GetStringUTFChars(env, path, NULL);
|
|
openURLGlobal(@(stringChars));
|
|
(*env)->ReleaseStringUTFChars(env, path, stringChars);
|
|
}
|
|
|
|
void registerOpenHandler(JNIEnv *env) {
|
|
jclass cls;
|
|
|
|
// Hook forkAndExec
|
|
orig_ProcessImpl_forkAndExec = dlsym(RTLD_DEFAULT, "Java_java_lang_UNIXProcess_forkAndExec");
|
|
if (!orig_ProcessImpl_forkAndExec) {
|
|
orig_ProcessImpl_forkAndExec = dlsym(RTLD_DEFAULT, "Java_java_lang_ProcessImpl_forkAndExec");
|
|
cls = (*env)->FindClass(env, "java/lang/ProcessImpl");
|
|
} else {
|
|
cls = (*env)->FindClass(env, "java/lang/UNIXProcess");
|
|
}
|
|
JNINativeMethod forkAndExecMethod[] = {
|
|
{"forkAndExec", "(I[B[B[BI[BI[B[IZ)I", (void *)&hooked_ProcessImpl_forkAndExec}
|
|
};
|
|
(*env)->RegisterNatives(env, cls, forkAndExecMethod, 1);
|
|
|
|
// (Java 17 only) Hook isAlive0
|
|
cls = (*env)->FindClass(env, "java/lang/ProcessHandleImpl");
|
|
if ((*env)->ExceptionOccurred(env)) {
|
|
// Java 8
|
|
(*env)->ExceptionClear(env);
|
|
} else {
|
|
orig_ProcessHandleImpl_isAlive0 = dlsym(RTLD_DEFAULT, "Java_java_lang_ProcessHandleImpl_isAlive0");
|
|
JNINativeMethod isAlive0Method[] = {
|
|
{"isAlive0", "(J)J", (void *)&hooked_ProcessHandleImpl_isAlive0}
|
|
};
|
|
(*env)->RegisterNatives(env, cls, isAlive0Method, 1);
|
|
}
|
|
|
|
// Register CTCClipboard natives
|
|
cls = (*env)->FindClass(env, "net/java/openjdk/cacio/ctc/CTCClipboard");
|
|
if ((*env)->ExceptionOccurred(env)) {
|
|
// Java 17
|
|
(*env)->ExceptionClear(env);
|
|
cls = (*env)->FindClass(env, "com/github/caciocavallosilano/cacio/ctc/CTCClipboard");
|
|
}
|
|
JNINativeMethod clipboardMethods[] = {
|
|
{"nQuerySystemClipboard", "()V", (void *)&CTCClipboard_nQuerySystemClipboard},
|
|
{"nPutClipboardData", "(Ljava/lang/String;Ljava/lang/String;)V", (void *)&CTCClipboard_nPutClipboardData}
|
|
};
|
|
(*env)->RegisterNatives(env, cls, clipboardMethods, 2);
|
|
|
|
// Register CTCDesktopPeer natives
|
|
cls = (*env)->FindClass(env, "net/java/openjdk/cacio/ctc/CTCDesktopPeer");
|
|
if ((*env)->ExceptionOccurred(env)) {
|
|
// Java 17, not available
|
|
//(*env)->ExceptionDescribe(env);
|
|
(*env)->ExceptionClear(env);
|
|
return;
|
|
}
|
|
JNINativeMethod peerOpenMethods[] = {
|
|
{"openFile", "(Ljava/lang/String;)V", (void *)&CTCDesktopPeer_openGlobal},
|
|
{"openUri", "(Ljava/lang/String;)V", (void *)&CTCDesktopPeer_openGlobal}
|
|
};
|
|
(*env)->RegisterNatives(env, cls, peerOpenMethods, 2);
|
|
}
|
|
|
|
// JNI_OnLoad
|
|
void JNI_OnLoadGLFW() {
|
|
vmGlfwClass = (*runtimeJNIEnvPtr)->NewGlobalRef(runtimeJNIEnvPtr, (*runtimeJNIEnvPtr)->FindClass(runtimeJNIEnvPtr, "org/lwjgl/glfw/GLFW"));
|
|
method_internalWindowSizeChanged = (*runtimeJNIEnvPtr)->GetStaticMethodID(runtimeJNIEnvPtr, vmGlfwClass, "internalWindowSizeChanged", "(JII)V");
|
|
jfieldID field_keyDownBuffer = (*runtimeJNIEnvPtr)->GetStaticFieldID(runtimeJNIEnvPtr, vmGlfwClass, "keyDownBuffer", "Ljava/nio/ByteBuffer;");
|
|
jobject keyDownBufferJ = (*runtimeJNIEnvPtr)->GetStaticObjectField(runtimeJNIEnvPtr, vmGlfwClass, field_keyDownBuffer);
|
|
keyDownBuffer = (*runtimeJNIEnvPtr)->GetDirectBufferAddress(runtimeJNIEnvPtr, keyDownBufferJ);
|
|
}
|
|
|
|
jint JNI_OnLoad(JavaVM* vm, void* reserved) {
|
|
runtimeJavaVMPtr = vm;
|
|
|
|
JNIEnv *env;
|
|
(*runtimeJavaVMPtr)->GetEnv(runtimeJavaVMPtr, (void **)&env, JNI_VERSION_1_4);
|
|
registerOpenHandler(env);
|
|
if (!getenv("POJAV_SKIP_JNI_GLFW")) {
|
|
runtimeJNIEnvPtr = env;
|
|
JNI_OnLoadGLFW();
|
|
}
|
|
|
|
return JNI_VERSION_1_4;
|
|
}
|
|
|
|
// Should be?
|
|
void JNI_OnUnload(JavaVM* vm, void* reserved) {
|
|
runtimeJNIEnvPtr = NULL;
|
|
}
|
|
|
|
#define ADD_CALLBACK_WWIN(NAME) \
|
|
JNIEXPORT jlong JNICALL Java_org_lwjgl_glfw_GLFW_nglfwSet##NAME##Callback(JNIEnv * env, jclass cls, jlong window, jlong callbackptr) { \
|
|
void** oldCallback = (void**) &GLFW_invoke_##NAME; \
|
|
GLFW_invoke_##NAME = (GLFW_invoke_##NAME##_func*) (uintptr_t) callbackptr; \
|
|
return (jlong) (uintptr_t) *oldCallback; \
|
|
}
|
|
|
|
ADD_CALLBACK_WWIN(Char)
|
|
ADD_CALLBACK_WWIN(CharMods)
|
|
ADD_CALLBACK_WWIN(CursorEnter)
|
|
ADD_CALLBACK_WWIN(CursorPos)
|
|
ADD_CALLBACK_WWIN(FramebufferSize)
|
|
ADD_CALLBACK_WWIN(Key)
|
|
ADD_CALLBACK_WWIN(MouseButton)
|
|
ADD_CALLBACK_WWIN(Scroll)
|
|
ADD_CALLBACK_WWIN(WindowPos)
|
|
ADD_CALLBACK_WWIN(WindowSize)
|
|
|
|
#undef ADD_CALLBACK_WWIN
|
|
|
|
void handleFramebufferSizeJava(void* window, int w, int h) {
|
|
if(GLFW_invoke_CursorEnter)GLFW_invoke_CursorEnter(window, 1);
|
|
if(GLFW_invoke_WindowPos)GLFW_invoke_WindowPos(window, 0, 0);
|
|
(*runtimeJNIEnvPtr)->CallStaticVoidMethod(runtimeJNIEnvPtr, vmGlfwClass, method_internalWindowSizeChanged, (long)window, w, h);
|
|
}
|
|
|
|
void pojavPumpEvents(void* window) {
|
|
CallbackBridge_nativeSetInputReady(YES);
|
|
//__android_log_print(ANDROID_LOG_INFO, "input_bridge_v3", "pojavPumpevents %d", eventCounter);
|
|
size_t counter = atomic_load_explicit(&eventCounter, memory_order_acquire);
|
|
if((cLastX != cursorX || cLastY != cursorY) && GLFW_invoke_CursorPos) {
|
|
cLastX = cursorX;
|
|
cLastY = cursorY;
|
|
if (isUseStackQueueCall)
|
|
GLFW_invoke_CursorPos(window, cursorX, cursorY);
|
|
}
|
|
for(size_t i = 0; i < counter; i++) {
|
|
GLFWInputEvent event = events[i];
|
|
switch(event.type) {
|
|
case EVENT_TYPE_CHAR:
|
|
if(GLFW_invoke_Char) GLFW_invoke_Char(window, event.i1);
|
|
break;
|
|
case EVENT_TYPE_CHAR_MODS:
|
|
if(GLFW_invoke_CharMods) GLFW_invoke_CharMods(window, event.i1, event.i2);
|
|
break;
|
|
case EVENT_TYPE_KEY:
|
|
if(GLFW_invoke_Key) GLFW_invoke_Key(window, event.i1, event.i2, event.i3, event.i4);
|
|
break;
|
|
case EVENT_TYPE_MOUSE_BUTTON:
|
|
if(GLFW_invoke_MouseButton) GLFW_invoke_MouseButton(window, event.i1, event.i2, event.i3);
|
|
break;
|
|
case EVENT_TYPE_SCROLL:
|
|
if(GLFW_invoke_Scroll) GLFW_invoke_Scroll(window, event.f1, event.f2);
|
|
break;
|
|
case EVENT_TYPE_FRAMEBUFFER_SIZE:
|
|
handleFramebufferSizeJava(window, event.i1, event.i2);
|
|
if(GLFW_invoke_FramebufferSize) GLFW_invoke_FramebufferSize(window, event.i1, event.i2);
|
|
break;
|
|
case EVENT_TYPE_WINDOW_SIZE:
|
|
handleFramebufferSizeJava(window, event.i1, event.i2);
|
|
if(GLFW_invoke_WindowSize) GLFW_invoke_WindowSize(window, event.i1, event.i2);
|
|
break;
|
|
}
|
|
}
|
|
atomic_store_explicit(&eventCounter, counter, memory_order_release);
|
|
}
|
|
void pojavRewindEvents() {
|
|
atomic_store_explicit(&eventCounter, 0, memory_order_release);
|
|
}
|
|
|
|
JNIEXPORT void JNICALL
|
|
Java_org_lwjgl_glfw_GLFW_nglfwGetCursorPos(JNIEnv *env, jclass clazz, jlong window, jobject xpos,
|
|
jobject ypos) {
|
|
*(double*)(*env)->GetDirectBufferAddress(env, xpos) = cursorX;
|
|
*(double*)(*env)->GetDirectBufferAddress(env, ypos) = cursorY;
|
|
}
|
|
|
|
JNIEXPORT void JNICALL
|
|
Java_org_lwjgl_glfw_GLFW_nglfwGetCursorPosA(JNIEnv *env, jclass clazz, jlong window,
|
|
jdoubleArray xpos, jdoubleArray ypos) {
|
|
(*env)->SetDoubleArrayRegion(env, xpos, 0,1, &cursorX);
|
|
(*env)->SetDoubleArrayRegion(env, ypos, 0,1, &cursorY);
|
|
}
|
|
|
|
JNIEXPORT void JNICALL
|
|
Java_org_lwjgl_glfw_GLFW_glfwSetCursorPos(JNIEnv *env, jclass clazz, jlong window, jdouble xpos,
|
|
jdouble ypos) {
|
|
cLastX = cursorX = xpos;
|
|
cLastY = cursorY = ypos;
|
|
}
|
|
|
|
void sendData(short type, int i1, int i2, short i3, short i4) {
|
|
size_t counter = atomic_load_explicit(&eventCounter, memory_order_acquire);
|
|
if (counter < 7999) {
|
|
GLFWInputEvent *event = &events[counter++];
|
|
event->type = type;
|
|
event->i1 = i1;
|
|
event->i2 = i2;
|
|
event->i3 = i3;
|
|
event->i4 = i4;
|
|
}
|
|
atomic_store_explicit(&eventCounter, counter, memory_order_release);
|
|
}
|
|
|
|
void sendDataFloat(short type, float i1, float i2, short i3, short i4) {
|
|
size_t counter = atomic_load_explicit(&eventCounter, memory_order_acquire);
|
|
if (counter < 7999) {
|
|
GLFWInputEvent *event = &events[counter++];
|
|
event->type = type;
|
|
event->f1 = i1;
|
|
event->f2 = i2;
|
|
event->i3 = i3;
|
|
event->i4 = i4;
|
|
}
|
|
atomic_store_explicit(&eventCounter, counter, memory_order_release);
|
|
}
|
|
|
|
void closeGLFWWindow() {
|
|
NSLog(@"Closing GLFW window");
|
|
|
|
/*
|
|
jclass glfwClazz = (*runtimeJNIEnvPtr)->FindClass(runtimeJNIEnvPtr, "org/lwjgl/glfw/GLFW");
|
|
assert(glfwClazz != NULL);
|
|
jmethodID glfwMethod = (*runtimeJNIEnvPtr)->GetStaticMethodID(runtimeJNIEnvPtr, glfwMethod, "glfwSetWindowShouldClose", "(JZ)V");
|
|
assert(glfwMethod != NULL);
|
|
|
|
(*runtimeJNIEnvPtr)->CallStaticVoidMethod(
|
|
runtimeJNIEnvPtr,
|
|
glfwClazz, glfwMethod,
|
|
(jlong) showingWindow, JNI_TRUE
|
|
);
|
|
*/
|
|
exit(-1);
|
|
}
|
|
|
|
const int hotbarKeys[9] = {
|
|
GLFW_KEY_1, GLFW_KEY_2, GLFW_KEY_3,
|
|
GLFW_KEY_4, GLFW_KEY_5, GLFW_KEY_6,
|
|
GLFW_KEY_7, GLFW_KEY_8, GLFW_KEY_9
|
|
};
|
|
int guiScale = 1;
|
|
int mcscale(CGFloat input) {
|
|
return (int)((guiScale * input)/resolutionScale);
|
|
}
|
|
int callback_SurfaceViewController_touchHotbar(CGFloat x, CGFloat y) {
|
|
if (isGrabbing == JNI_FALSE) {
|
|
return -1;
|
|
}
|
|
|
|
int barHeight = mcscale(20);
|
|
int barY = physicalHeight - barHeight;
|
|
if (y < barY) return -1;
|
|
|
|
int barWidth = mcscale(180);
|
|
int barX = (physicalWidth / 2) - (barWidth / 2);
|
|
if (x < barX || x >= barX + barWidth) return -1;
|
|
|
|
return hotbarKeys[(int) MathUtils_map(x, barX, barX + barWidth, 0, 9)];
|
|
}
|
|
|
|
JNIEXPORT void JNICALL Java_net_kdt_pojavlaunch_uikit_UIKit_updateMCGuiScale(JNIEnv* env, jclass clazz, jint scale) {
|
|
guiScale = scale;
|
|
}
|
|
|
|
JNIEXPORT jstring JNICALL Java_org_lwjgl_glfw_CallbackBridge_nativeClipboard(JNIEnv* env, jclass clazz, jint action, jstring copySrc) {
|
|
NSDebugLog(@"Debug: Clipboard access is going on\n");
|
|
return UIKit_accessClipboard(env, action, copySrc);
|
|
}
|
|
|
|
JNIEXPORT void JNICALL Java_org_lwjgl_glfw_CallbackBridge_nativeSetGrabbing(JNIEnv* env, jclass clazz, jboolean grabbing, jfloat xset, jfloat yset) {
|
|
isGrabbing = grabbing;
|
|
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
SurfaceViewController *vc = ((SurfaceViewController *)UIWindow.mainWindow.rootViewController);
|
|
[vc updateGrabState];
|
|
});
|
|
}
|
|
|
|
JNIEXPORT jboolean JNICALL Java_org_lwjgl_glfw_CallbackBridge_nativeIsGrabbing(JNIEnv* env, jclass clazz) {
|
|
return isGrabbing;
|
|
}
|
|
|
|
void CallbackBridge_nativeSetInputReady(BOOL inputReady) {
|
|
isInputReady = inputReady;
|
|
if (inputReady) {
|
|
if (GLFW_invoke_FramebufferSize) {
|
|
GLFW_invoke_FramebufferSize((void*) showingWindow, windowWidth, windowHeight);
|
|
}
|
|
if (GLFW_invoke_WindowSize) {
|
|
GLFW_invoke_FramebufferSize((void*) showingWindow, windowWidth, windowHeight);
|
|
}
|
|
}
|
|
}
|
|
|
|
BOOL CallbackBridge_nativeSendChar(jchar codepoint /* jint codepoint */) {
|
|
if (GLFW_invoke_Char && isInputReady) {
|
|
if (isUseStackQueueCall) {
|
|
sendData(EVENT_TYPE_CHAR, codepoint, 0, 0, 0);
|
|
} else {
|
|
GLFW_invoke_Char((void*) showingWindow, (unsigned int) codepoint);
|
|
// return lwjgl2_triggerCharEvent(codepoint);
|
|
}
|
|
return YES;
|
|
}
|
|
return NO;
|
|
}
|
|
|
|
BOOL CallbackBridge_nativeSendCharMods(jchar codepoint, int mods) {
|
|
if (GLFW_invoke_CharMods && isInputReady) {
|
|
if (isUseStackQueueCall) {
|
|
sendData(EVENT_TYPE_CHAR_MODS, (unsigned int) codepoint, mods, 0, 0);
|
|
} else {
|
|
GLFW_invoke_CharMods((void*) showingWindow, codepoint, mods);
|
|
}
|
|
return YES;
|
|
}
|
|
return NO;
|
|
}
|
|
/*
|
|
JNIEXPORT void JNICALL Java_org_lwjgl_glfw_CallbackBridge_nativeSendCursorEnter(JNIEnv* env, jclass clazz, jint entered) {
|
|
if (GLFW_invoke_CursorEnter && isInputReady) {
|
|
GLFW_invoke_CursorEnter(showingWindow, entered);
|
|
}
|
|
}
|
|
*/
|
|
void CallbackBridge_nativeSendCursorPos(char event, CGFloat x, CGFloat y) {
|
|
if (!GLFW_invoke_CursorPos || !isInputReady) return;
|
|
|
|
switch (event) {
|
|
case ACTION_DOWN:
|
|
case ACTION_UP:
|
|
if (!isGrabbing) {
|
|
cursorX = x;
|
|
cursorY = y;
|
|
}
|
|
break;
|
|
|
|
case ACTION_MOVE:
|
|
if (isGrabbing) {
|
|
cursorX += x - cLastX;
|
|
cursorY += y - cLastY;
|
|
} else {
|
|
cursorX = x;
|
|
cursorY = y;
|
|
}
|
|
break;
|
|
|
|
case ACTION_MOVE_MOTION:
|
|
cursorX += x;
|
|
cursorY += y;
|
|
break;
|
|
}
|
|
|
|
if (!isUseStackQueueCall) {
|
|
GLFW_invoke_CursorPos((void*) showingWindow, (double) cursorX, (double) cursorY);
|
|
}
|
|
}
|
|
|
|
char getKeyModifiers(int key, int action) {
|
|
static char currMods;
|
|
char mod;
|
|
switch (key) {
|
|
case GLFW_KEY_LEFT_SHIFT:
|
|
mod = GLFW_MOD_SHIFT;
|
|
break;
|
|
case GLFW_KEY_LEFT_CONTROL:
|
|
mod = GLFW_MOD_CONTROL;
|
|
break;
|
|
case GLFW_KEY_LEFT_ALT:
|
|
mod = GLFW_MOD_ALT;
|
|
break;
|
|
case GLFW_KEY_CAPS_LOCK:
|
|
mod = GLFW_MOD_CAPS_LOCK;
|
|
break;
|
|
case GLFW_KEY_NUM_LOCK:
|
|
mod = GLFW_MOD_NUM_LOCK;
|
|
break;
|
|
default:
|
|
return currMods;
|
|
}
|
|
if (action) {
|
|
currMods |= mod;
|
|
} else {
|
|
currMods &= ~mod;
|
|
}
|
|
return currMods;
|
|
}
|
|
|
|
void CallbackBridge_nativeSendKey(int key, int scancode, int action, int mods) {
|
|
if (GLFW_invoke_Key && isInputReady) {
|
|
keyDownBuffer[MAX(0, key-31)]=(jbyte)action;
|
|
if (mods == 0) {
|
|
mods = getKeyModifiers(key, action);
|
|
}
|
|
|
|
if (isUseStackQueueCall) {
|
|
sendData(EVENT_TYPE_KEY, key, scancode, action, mods);
|
|
} else {
|
|
GLFW_invoke_Key((void*) showingWindow, key, scancode, action, mods);
|
|
}
|
|
}
|
|
|
|
// On macOS, Minecraft expects the Command key
|
|
if (key == GLFW_KEY_LEFT_CONTROL) {
|
|
CallbackBridge_nativeSendKey(GLFW_KEY_LEFT_SUPER, 0, action, mods);
|
|
} else if (key == GLFW_KEY_RIGHT_CONTROL) {
|
|
CallbackBridge_nativeSendKey(GLFW_KEY_RIGHT_SUPER, 0, action, mods);
|
|
}
|
|
}
|
|
|
|
void CallbackBridge_nativeSendMouseButton(int button, int action, int mods) {
|
|
if (isInputReady) {
|
|
if (button == -1) {
|
|
} else if (GLFW_invoke_MouseButton) {
|
|
if (mods == 0) {
|
|
mods = getKeyModifiers(0, action);
|
|
}
|
|
|
|
if (isUseStackQueueCall) {
|
|
sendData(EVENT_TYPE_MOUSE_BUTTON, button, action, mods, 0);
|
|
} else {
|
|
GLFW_invoke_MouseButton((void*) showingWindow, button, action, mods);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void CallbackBridge_nativeSendScreenSize(int width, int height) {
|
|
windowWidth = width;
|
|
windowHeight = height;
|
|
|
|
if (isInputReady) {
|
|
if (GLFW_invoke_FramebufferSize) {
|
|
if (isUseStackQueueCall) {
|
|
sendData(EVENT_TYPE_FRAMEBUFFER_SIZE, width, height, 0, 0);
|
|
} else {
|
|
GLFW_invoke_FramebufferSize((void*) showingWindow, width, height);
|
|
}
|
|
}
|
|
if (GLFW_invoke_WindowSize) {
|
|
if (isUseStackQueueCall) {
|
|
sendData(EVENT_TYPE_WINDOW_SIZE, width, height, 0, 0);
|
|
} else {
|
|
GLFW_invoke_WindowSize((void*) showingWindow, width, height);
|
|
}
|
|
}
|
|
}
|
|
|
|
// return (isInputReady && (GLFW_invoke_FramebufferSize || GLFW_invoke_WindowSize));
|
|
}
|
|
|
|
void CallbackBridge_nativeSendScroll(CGFloat xoffset, CGFloat yoffset) {
|
|
if (GLFW_invoke_Scroll && isInputReady) {
|
|
if (isUseStackQueueCall) {
|
|
sendDataFloat(EVENT_TYPE_SCROLL, xoffset, yoffset, 0, 0);
|
|
} else {
|
|
GLFW_invoke_Scroll((void*) showingWindow, (double) xoffset, (double) yoffset);
|
|
}
|
|
}
|
|
}
|
|
|
|
JNIEXPORT void JNICALL Java_org_lwjgl_glfw_GLFW_nglfwSetShowingWindow(JNIEnv* env, jclass clazz, jlong window) {
|
|
showingWindow = (long) window;
|
|
}
|
|
|
|
void CallbackBridge_pauseGameIfNeed() {
|
|
if (isGrabbing) {
|
|
CallbackBridge_nativeSendKey(GLFW_KEY_ESCAPE, 0, 1, 0);
|
|
CallbackBridge_nativeSendKey(GLFW_KEY_ESCAPE, 0, 0, 0);
|
|
}
|
|
}
|