ffi 2D: migrate android.sx safe-insets to declarative #jni_class blocks

`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.
This commit is contained in:
agra
2026-05-20 11:34:04 +03:00
parent 2882748ae6
commit c9db2a8dc0

View File

@@ -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); }
}