From 0c43dcac12f606c0ac46b5f87dd3de324ff9ef04 Mon Sep 17 00:00:00 2001 From: Andrew Opalach Date: Tue, 25 Aug 2026 19:49:17 -0400 Subject: Very wip Android stuff Signed-off-by: Andrew Opalach --- src/window_glfm.c | 187 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 186 insertions(+), 1 deletion(-) (limited to 'src/window_glfm.c') diff --git a/src/window_glfm.c b/src/window_glfm.c index a99dbcd..ad8baec 100644 --- a/src/window_glfm.c +++ b/src/window_glfm.c @@ -1,6 +1,9 @@ #define AL_LOG_SECTION "glfm" #include +#include #include +#include +#include #include "window_glfm.h" #include "common.h" @@ -40,14 +43,184 @@ void stl_global_close(void) { } +#define glfm__printException(jni) (*(jni))->ExceptionDescribe(jni) +#define glfm__wasJavaExceptionThrown(jni) \ + ((*(jni))->ExceptionCheck(jni) ? (glfm__printException(jni), (*(jni))->ExceptionClear(jni), true) : false) + +static jmethodID glfm__getJavaMethodID(JNIEnv *jni, jobject object, const char *name, const char *sig) +{ + if (!object) return NULL; + jclass class = (*jni)->GetObjectClass(jni, object); + jmethodID methodID = (*jni)->GetMethodID(jni, class, name, sig); + (*jni)->DeleteLocalRef(jni, class); + return glfm__wasJavaExceptionThrown(jni) ? NULL : methodID; +} + +static jfieldID glfm__getJavaFieldID(JNIEnv *jni, jobject object, const char *name, const char *sig) +{ + if (!object) return NULL; + jclass class = (*jni)->GetObjectClass(jni, object); + jfieldID fieldID = (*jni)->GetFieldID(jni, class, name, sig); + (*jni)->DeleteLocalRef(jni, class); + return glfm__wasJavaExceptionThrown(jni) ? NULL : fieldID; +} + +#define glfm__callJavaMethod(jni, object, methodName, methodSig, returnType, ...) \ + (*(jni))->Call##returnType##Method(jni, object, \ + glfm__getJavaMethodID(jni, object, methodName, methodSig), ##__VA_ARGS__) + +#define glfm__getJavaField(jni, object, fieldName, fieldSig, fieldType) \ + (*(jni))->Get##fieldType##Field(jni, object, \ + glfm__getJavaFieldID(jni, object, fieldName, fieldSig)) + +static s32 jniGetFDFromFileDescriptor(JNIEnv *jni, jobject fileDescriptor) +{ + return glfm__getJavaField(jni, fileDescriptor, "descriptor", "I", Int); +} + +static u32 parse_intent(ANativeActivity *activity, JNIEnv *jni) +{ + u32 main_arg_index = 0; + jobject intent = glfm__callJavaMethod(jni, activity->clazz, "getIntent", + "()Landroid/content/Intent;", + Object); + if (!intent) return 0; + jobject uri = glfm__callJavaMethod(jni, intent, "getData", + "()Landroid/net/Uri;", + Object); + if (!uri) return 0; + jobject scheme = glfm__callJavaMethod(jni, intent, "getScheme", + "()Ljava/lang/String;", + Object); + const char *c_str = (*jni)->GetStringUTFChars(jni, scheme, NULL); + bool is_content = al_strscmp(c_str, "content") == 0; + bool is_http = al_strscmp(c_str, "http") == 0; + (*jni)->ReleaseStringUTFChars(jni, scheme, c_str); + (*jni)->DeleteLocalRef(jni, scheme); + jobject realPath = NULL; + if (is_content) { + jstring extraName = (*jni)->NewStringUTF(jni, "real_path"); + realPath = glfm__callJavaMethod(jni, intent, "getStringExtra", + "(Ljava/lang/String;)Ljava/lang/String;", + Object, extraName); + (*jni)->DeleteLocalRef(jni, extraName); + } + if (realPath) { + c_str = (*jni)->GetStringUTFChars(jni, realPath, NULL); + str main_arg; + al_str_from(&main_arg, c_str); + (*jni)->ReleaseStringUTFChars(jni, realPath, c_str); + (*jni)->DeleteLocalRef(jni, realPath); + u32 last = al_str_rfind(&main_arg, '/'); + if (last != AL_STR_NO_POS) { + str sub = al_str_substr(&main_arg, 0, last); + struct nn_dir dir; + if (nn_dir_open(&dir, &sub)) { + struct nn_dir_entry entry; + while (nn_dir_read(&dir, &entry)) { + if (entry.type == NNWT_ENTRY_FILE) { + str arg; + nn_dir_entry_get_path(&entry, &dir, &arg); + al_array_push(global.args, arg); + } + } + nn_dir_close(&dir); + } + } + if (global.args.count > 0) { + al_array_sort(global.args, str, nn_numerical_compare); + str *arg; + al_array_foreach_ptr(global.args, i, arg) { + if (al_str_eq(arg, &main_arg)) { + main_arg_index = i; + break; + } + } + al_str_free(&main_arg); + } else { + al_array_push(global.args, main_arg); + } + } else { + str arg = AL_STR_NULL; + if (is_http) { + jobject path = glfm__callJavaMethod(jni, uri, "toString", + "()Ljava/lang/String;", + Object); + c_str = (*jni)->GetStringUTFChars(jni, path, NULL); + al_str_cat(&arg, &al_str_cr(c_str)); + (*jni)->ReleaseStringUTFChars(jni, path, c_str); + (*jni)->DeleteLocalRef(jni, path); + al_array_push(global.args, arg); + } else { + // @TODO: Figure out the rules of closing the fd we get. + // https://developer.android.com/reference/android/os/ParcelFileDescriptor#detachFd() + // https://stackoverflow.com/questions/24701029/how-to-properly-pass-an-asset-filedescriptor-to-ffmpeg-using-jni-in-android + jobject contentResolver = glfm__callJavaMethod(jni, activity->clazz, "getContentResolver", + "()Landroid/content/ContentResolver;", + Object); + jstring mode = (*jni)->NewStringUTF(jni, "r"); + jobject assetFileDescriptor = glfm__callJavaMethod(jni, contentResolver, "openAssetFileDescriptor", + "(Landroid/net/Uri;Ljava/lang/String;)Landroid/content/res/AssetFileDescriptor;", + Object, uri, mode); + (*jni)->DeleteLocalRef(jni, mode); + jobject fileDescriptor = glfm__callJavaMethod(jni, assetFileDescriptor, "getFileDescriptor", + "()Ljava/io/FileDescriptor;", + Object); + /* + long startOffset = glfm__callJavaMethod(jni, assetFileDescriptor, "getStartOffset", + "()J", + Long); + long length = glfm__callJavaMethod(jni, assetFileDescriptor, "getLength", + "()J", + Long); + */ + s32 fd = jniGetFDFromFileDescriptor(jni, fileDescriptor); + al_str_from(&arg, "fd://"); + al_str_cat(&arg, al_print_s32(fd)); + al_array_push(global.args, arg); + } + } + return main_arg_index; +} + +static bool get_android_temp_dir(ANativeActivity *activity, JNIEnv *jni) +{ + jobject cacheDir = glfm__callJavaMethod(jni, activity->clazz, "getCacheDir", + "()Ljava/io/File;", + Object); + if (!cacheDir) return false; + jobject absolutePath = glfm__callJavaMethod(jni, cacheDir, "getAbsolutePath", + "()Ljava/lang/String;", + Object); + if (!absolutePath) return false; + const char *c_str = (*jni)->GetStringUTFChars(jni, absolutePath, NULL); + nn_common_set_temp_directory(&al_str_cr(c_str)); + (*jni)->ReleaseStringUTFChars(jni, absolutePath, c_str); + (*jni)->DeleteLocalRef(jni, absolutePath); + return true; +} + static void onSurfaceCreated(GLFMDisplay *display, s32 width, s32 height) { (void)width; (void)height; + + JNIEnv *jni; + ANativeActivity *activity = glfmGetAndroidActivity(display, (void **)&jni); + if (!global.did_main) { - stl_platform_main(0, NULL); + if (!get_android_temp_dir(activity, jni)) { + log_error("Failed to get temp directory."); + } + al_array_init(global.args); + global.main_arg_index = parse_intent(activity, jni); + str argv0; + al_str_from(&argv0, APPLICATION_NAME); + al_array_insert(global.args, 0, argv0); + stl_platform_main(global.args.count, global.args.data, &global.main_arg_index); global.did_main = true; } + GLFMRenderingAPI api = glfmGetRenderingAPI(display); log_info("Created GLES context of version %s.", api == GLFMRenderingAPIOpenGLES32 ? "3.2" : @@ -104,6 +277,7 @@ static bool onKey(GLFMDisplay *display, GLFMKeyCode key, GLFMKeyAction action, s case GLFMKeyCodeVolumeDown: return stl_window_send_key_immediate_event(&glfm->w, mapped_action, STELA_KEY_LEFT); case GLFMKeyCodeNavigationBack: + return false; default: break; } @@ -125,6 +299,16 @@ static bool onTouch(GLFMDisplay *display, s32 touch, GLFMTouchPhase phase, f64 x return true; } +static void onSensor(GLFMDisplay *display, GLFMSensorEvent event) +{ + struct stl_window_glfm *glfm = (struct stl_window_glfm *)glfmGetUserData(display); + if (event.sensor == GLFMSensorGyroscope) + { + stl_window_send_pointer_event(&glfm->w, STELA_POINTER_MOVE, + event.vector.x * -75.0, event.vector.y * 75.0); + } +} + void glfmMain(GLFMDisplay *display) { nn_common_init("glfmMain"); @@ -151,6 +335,7 @@ void glfmMain(GLFMDisplay *display) glfmSetOrientationChangedFunc(display, onOrientationChanged); glfmSetKeyFunc(display, onKey); glfmSetTouchFunc(display, onTouch); + glfmSetSensorFunc(display, GLFMSensorGyroscope, onSensor); } static bool window_glfm_create_window(struct stl_window *window, u32 width, u32 height, u32 flags, -- cgit v1.2.3-101-g0448