// JNI helpers used by modules/platform/android.sx. Kept in the library // so consumers don't need to vendor an identically-named copy. The sx // compiler resolves `#source "vendors/..."` against the stdlib search // paths in addition to the consumer's project root. #ifdef __ANDROID__ #include #include #include // Mirror of struct android_app (NDK 29 / arm64) up to the fields we touch. // Avoids depending on the glue header in the sx library compile path. struct sx_android_app_min { void* userData; void (*onAppCmd)(struct sx_android_app_min* app, int cmd); int (*onInputEvent)(struct sx_android_app_min* app, AInputEvent* event); // ...rest of struct ignored; we only assign onInputEvent. }; // Install an sx-side handler as `app->onInputEvent`. native_app_glue's // process_input loop calls this for every AInputEvent it pulls off the // input queue. Returning 1 marks the event as consumed. void sx_android_install_input_handler(void* app, int (*handler)(void* app, void* event)) { if (app == 0) return; struct sx_android_app_min* a = (struct sx_android_app_min*)app; a->onInputEvent = (int (*)(struct sx_android_app_min*, AInputEvent*))handler; } // Query system-bar insets (status bar, nav bar) via JNI: // activity → getWindow() → getDecorView() // → getRootWindowInsets() // → getSystemWindowInset[Top|Left|Bottom|Right]() // Out params receive physical-pixel insets; the sx caller divides by // dpi_scale to convert to logical units. Falls back to zeros if the // view isn't attached yet (e.g. called before the first frame). void sx_android_query_safe_insets(void* activity_ptr, int* out_top, int* out_left, int* out_bottom, int* out_right) { *out_top = 0; *out_left = 0; *out_bottom = 0; *out_right = 0; if (activity_ptr == 0) return; ANativeActivity* act = (ANativeActivity*)activity_ptr; JavaVM* vm = act->vm; if (vm == 0) return; JNIEnv* env = 0; int already_attached = 1; if ((*vm)->GetEnv(vm, (void**)&env, JNI_VERSION_1_6) != JNI_OK) { already_attached = 0; if ((*vm)->AttachCurrentThread(vm, &env, 0) != 0) return; } jobject activity_obj = act->clazz; jclass activity_cls = (*env)->GetObjectClass(env, activity_obj); jmethodID m_getWindow = (*env)->GetMethodID(env, activity_cls, "getWindow", "()Landroid/view/Window;"); if (m_getWindow == 0) goto done; jobject window = (*env)->CallObjectMethod(env, activity_obj, m_getWindow); if (window == 0) goto done; jclass window_cls = (*env)->GetObjectClass(env, window); jmethodID m_getDecorView = (*env)->GetMethodID(env, window_cls, "getDecorView", "()Landroid/view/View;"); if (m_getDecorView == 0) goto done; jobject decor = (*env)->CallObjectMethod(env, window, m_getDecorView); if (decor == 0) goto done; jclass view_cls = (*env)->GetObjectClass(env, decor); jmethodID m_getRootInsets = (*env)->GetMethodID(env, view_cls, "getRootWindowInsets", "()Landroid/view/WindowInsets;"); if (m_getRootInsets == 0) goto done; jobject insets = (*env)->CallObjectMethod(env, decor, m_getRootInsets); if (insets == 0) goto done; jclass insets_cls = (*env)->GetObjectClass(env, insets); jmethodID m_top = (*env)->GetMethodID(env, insets_cls, "getSystemWindowInsetTop", "()I"); jmethodID m_left = (*env)->GetMethodID(env, insets_cls, "getSystemWindowInsetLeft", "()I"); jmethodID m_bottom = (*env)->GetMethodID(env, insets_cls, "getSystemWindowInsetBottom", "()I"); jmethodID m_right = (*env)->GetMethodID(env, insets_cls, "getSystemWindowInsetRight", "()I"); if (m_top) *out_top = (*env)->CallIntMethod(env, insets, m_top); if (m_left) *out_left = (*env)->CallIntMethod(env, insets, m_left); if (m_bottom) *out_bottom = (*env)->CallIntMethod(env, insets, m_bottom); if (m_right) *out_right = (*env)->CallIntMethod(env, insets, m_right); done: if (!already_attached) (*vm)->DetachCurrentThread(vm); } #endif