Context
Phase D of #185 shipped live style editing via the geisterhand inspector at localhost:7676/. v0.5.350 wired the macOS dispatcher; v0.5.360 extended to iOS / tvOS / GTK4 / Windows. Android was deferred — JNI marshaling for the dispatcher signature has caveats that warrant a focused look rather than a copy-paste.
What's tricky
The dispatcher signature is extern "C" fn(handle: i64, prop_id: u32, a0: f64, a1: f64, a2: f64, a3: f64). On the other platforms this is a plain function pointer that can be registered via perry_geisterhand_register_apply_style(fn) and invoked from the geisterhand pump's main thread.
On Android the per-widget setters (perry_ui_widget_set_background_color, etc.) live on the JVM side — the Rust FFI is a thin JNI bridge. So the dispatcher needs to either:
- Call the JNI bridge functions from the geisterhand pump thread (must be on the UI thread; the existing pump already runs main-thread, so this should work — but the JNI env handling needs validation).
- Marshal once more through Android's
Handler.post() to the UI thread.
Option 1 is cleaner if the existing pump path is already on the UI thread. The pump on Android is plumbed via crates/perry-ui-android/src/app.rs — needs an audit.
Deliverables
- Add
crates/perry-ui-android/src/geisterhand_style.rs (cargo-cult crates/perry-ui-macos/src/geisterhand_style.rs shape, but call the per-platform Rust FFI helpers that wrap the JNI bridges).
- Add
pub mod geisterhand_style; (gated on feature = "geisterhand") to crates/perry-ui-android/src/lib.rs.
- Add
perry_geisterhand_register_apply_style(crate::geisterhand_style::apply_style) to the existing #[cfg(feature = "geisterhand")] block in crates/perry-ui-android/src/app.rs (alongside state_set / screenshot_capture / textfield_set_string registrations).
Verification
Build with cargo build --release -p perry-ui-android --features geisterhand --target aarch64-linux-android. Launch a Perry-built Android app on a physical device or emulator, hit localhost:7676/style/1 over adb forward tcp:7676 tcp:7676, verify the apply lands without JNI exceptions in adb logcat.
Closes
Phase D fully cross-platform on every Perry-supported platform.
Context
Phase D of #185 shipped live style editing via the geisterhand inspector at
localhost:7676/. v0.5.350 wired the macOS dispatcher; v0.5.360 extended to iOS / tvOS / GTK4 / Windows. Android was deferred — JNI marshaling for the dispatcher signature has caveats that warrant a focused look rather than a copy-paste.What's tricky
The dispatcher signature is
extern "C" fn(handle: i64, prop_id: u32, a0: f64, a1: f64, a2: f64, a3: f64). On the other platforms this is a plain function pointer that can be registered viaperry_geisterhand_register_apply_style(fn)and invoked from the geisterhand pump's main thread.On Android the per-widget setters (
perry_ui_widget_set_background_color, etc.) live on the JVM side — the Rust FFI is a thin JNI bridge. So the dispatcher needs to either:Handler.post()to the UI thread.Option 1 is cleaner if the existing pump path is already on the UI thread. The pump on Android is plumbed via
crates/perry-ui-android/src/app.rs— needs an audit.Deliverables
crates/perry-ui-android/src/geisterhand_style.rs(cargo-cultcrates/perry-ui-macos/src/geisterhand_style.rsshape, but call the per-platform Rust FFI helpers that wrap the JNI bridges).pub mod geisterhand_style;(gated onfeature = "geisterhand") tocrates/perry-ui-android/src/lib.rs.perry_geisterhand_register_apply_style(crate::geisterhand_style::apply_style)to the existing#[cfg(feature = "geisterhand")]block incrates/perry-ui-android/src/app.rs(alongsidestate_set/screenshot_capture/textfield_set_stringregistrations).Verification
Build with
cargo build --release -p perry-ui-android --features geisterhand --target aarch64-linux-android. Launch a Perry-built Android app on a physical device or emulator, hitlocalhost:7676/style/1overadb forward tcp:7676 tcp:7676, verify the apply lands without JNI exceptions inadb logcat.Closes
Phase D fully cross-platform on every Perry-supported platform.