From c9db2a8dc08a7308bd4a1028c5e634d2a3def468 Mon Sep 17 00:00:00 2001 From: agra Date: Wed, 20 May 2026 11:34:04 +0300 Subject: [PATCH] ffi 2D: migrate android.sx safe-insets to declarative `#jni_class` blocks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `sx_query_safe_insets_jni`'s body — previously seven hand-rolled `#jni_call` sites with verbose JNI descriptor literals — now uses four `#jni_class` declarations and the DSL method-call form inside a `#jni_env(env) { ... }` scope. The new shape: ``` WindowInsets :: #jni_class("android/view/WindowInsets") { getSystemWindowInsetTop :: (self: *Self) -> s32; ... } ... Activity / Window / View ... #jni_env(env) { window := activity.getWindow(); decor := window.getDecorView(); insets := decor.getRootWindowInsets(); top.* = insets.getSystemWindowInsetTop(); ... } ``` Descriptor derivation happens at lower time (jni_descriptor.zig); slot interning + vtable dispatch shape match the Phase 1C hand-rolled form byte-for-byte. The function param signature changes from `activity: *void` to `activity: *Activity` so the DSL can resolve method names through `foreign_class_map`; the AndroidPlatform.safe_insets caller adds an `xx` cast at the call site. Net body shrinks from 14 dispatch lines to 12 (slightly shorter but the win is type safety + readability — the foreign descriptor strings are gone). On-device chess regression is the remaining verification step (Pixel device with safe-area-driven board layout). Verified locally: zig build, run_examples (129/129), cross_compile (3/3 — incl. examples/99-android-egl-clear.sx cross-compile to android target succeeds and produces a valid .o). Naming caveat: `Activity` / `Window` / `View` / `WindowInsets` are now top-level names exported by `modules/platform/android.sx`. User code that imports this module shouldn't redefine these aliases. --- library/modules/platform/android.sx | 61 ++++++++++++++++++++--------- 1 file changed, 43 insertions(+), 18 deletions(-) diff --git a/library/modules/platform/android.sx b/library/modules/platform/android.sx index 180dd4d..98885ba 100644 --- a/library/modules/platform/android.sx +++ b/library/modules/platform/android.sx @@ -134,31 +134,56 @@ sx_android_activity_clazz :: (activity: *void) -> *void { sx_load_ptr_at(activity, ANATIVEACTIVITY_CLAZZ_OFFSET); } -// sx-side reimplementation of the JNI dispatch chain inside -// `sx_android_query_safe_insets`. Caller provides an already-attached -// `JNIEnv*` and the activity's `clazz` jobject. Outputs physical-pixel -// insets just like the C foreign helper above. Eventually replaces -// the foreign call once the JavaVM env-attach plumbing has a sx -// equivalent or a thinner C shim. -sx_query_safe_insets_jni :: (env: *void, activity: *void, top: *s32, left: *s32, bottom: *s32, right: *s32) -> void { +// Declarative JNI class bindings for the safe-insets dispatch chain +// (Phase 2D migration). Each `#jni_class` declares the sx-side alias, +// the JNI class path, and the methods we use — `inst.method(args)` +// inside a `#jni_env(env) { ... }` scope lowers to the same JNI vtable +// indirection as the hand-rolled `#jni_call` form, with the descriptor +// auto-derived from the sx signature (see [src/ir/jni_descriptor.zig]). +WindowInsets :: #jni_class("android/view/WindowInsets") { + getSystemWindowInsetTop :: (self: *Self) -> s32; + getSystemWindowInsetLeft :: (self: *Self) -> s32; + getSystemWindowInsetBottom :: (self: *Self) -> s32; + getSystemWindowInsetRight :: (self: *Self) -> s32; +} + +View :: #jni_class("android/view/View") { + getRootWindowInsets :: (self: *Self) -> *WindowInsets; +} + +Window :: #jni_class("android/view/Window") { + getDecorView :: (self: *Self) -> *View; +} + +Activity :: #jni_class("android/app/Activity") { + getWindow :: (self: *Self) -> *Window; +} + +// sx-side reimplementation of the JNI dispatch chain. Caller provides +// an already-attached `JNIEnv*` and the activity's `clazz` jobject +// (cast to `*Activity` so the method-call DSL can find it in the +// foreign-class registry). Outputs physical-pixel insets. +sx_query_safe_insets_jni :: (env: *void, activity: *Activity, top: *s32, left: *s32, bottom: *s32, right: *s32) -> void { inline if OS != .android { return; } top.* = 0; left.* = 0; bottom.* = 0; right.* = 0; if activity == null { return; } - window := #jni_call(*void)(env, activity, "getWindow", "()Landroid/view/Window;"); - if window == null { return; } + #jni_env(env) { + window := activity.getWindow(); + if window == null { return; } - decor := #jni_call(*void)(env, window, "getDecorView", "()Landroid/view/View;"); - if decor == null { return; } + decor := window.getDecorView(); + if decor == null { return; } - insets := #jni_call(*void)(env, decor, "getRootWindowInsets", "()Landroid/view/WindowInsets;"); - if insets == null { return; } + insets := decor.getRootWindowInsets(); + if insets == null { return; } - top.* = #jni_call(s32)(env, insets, "getSystemWindowInsetTop", "()I"); - left.* = #jni_call(s32)(env, insets, "getSystemWindowInsetLeft", "()I"); - bottom.* = #jni_call(s32)(env, insets, "getSystemWindowInsetBottom", "()I"); - right.* = #jni_call(s32)(env, insets, "getSystemWindowInsetRight", "()I"); + top.* = insets.getSystemWindowInsetTop(); + left.* = insets.getSystemWindowInsetLeft(); + bottom.* = insets.getSystemWindowInsetBottom(); + right.* = insets.getSystemWindowInsetRight(); + } } // EGL — display/surface/context/config are opaque to us. @@ -396,7 +421,7 @@ impl Platform for AndroidPlatform { attached : bool = false; env := sx_android_get_env(g_android_activity, @attached); if env != null { - clazz := sx_android_activity_clazz(g_android_activity); + clazz : *Activity = xx sx_android_activity_clazz(g_android_activity); sx_query_safe_insets_jni(env, clazz, @t, @l, @b, @r); if attached { sx_android_detach_env(g_android_activity); } }