extern crate alloc;
extern crate std;
use alloc::boxed::Box;
use alloc::vec::Vec;
#[cfg(target_os = "android")]
use core::ffi::c_void;
use jni::objects::{Global, JClass, JObject, JObjectArray, JValue};
#[cfg(target_os = "android")]
use jni::sys::jboolean;
use jni::sys::{jfloat, jint, jintArray, jlong, jobject, jobjectArray};
use jni::{Env, EnvUnowned, jni_sig, jni_str};
use nami::SignalExt;
use std::sync::Arc;
use waterui_layout::{
HorizontalAlignment, Layout, ProposalSize, Rect, Size, StretchAxis, SubView, VerticalAlignment,
ViewDimensions, measure_layout, with_memoized_children,
};
use crate::IntoFFI;
use crate::components::layout::WuiLayout;
use waterui_graphics::color::ResolvedColor;
use waterui_text::font::{FontWeight, ResolvedFont};
#[cfg(target_os = "android")]
use ndk_sys::ANativeWindow;
#[cfg(target_os = "android")]
struct AndroidNativeWindow {
ptr: *mut ANativeWindow,
}
#[cfg(target_os = "android")]
impl AndroidNativeWindow {
unsafe fn from_surface(env: *mut jni::sys::JNIEnv, surface: jobject) -> Option<Self> {
let ptr = unsafe { ndk_sys::ANativeWindow_fromSurface(env.cast(), surface.cast()) };
(!ptr.is_null()).then_some(Self { ptr })
}
fn as_void_ptr(&self) -> *mut c_void {
self.ptr.cast::<c_void>()
}
}
#[cfg(target_os = "android")]
impl Drop for AndroidNativeWindow {
fn drop(&mut self) {
unsafe { ndk_sys::ANativeWindow_release(self.ptr) };
}
}
#[cfg(target_os = "android")]
fn require_native_window(
window: Option<AndroidNativeWindow>,
function: &str,
) -> AndroidNativeWindow {
window.unwrap_or_else(|| {
panic!(
"WatcherJni.{} failed to obtain ANativeWindow from Surface",
function
)
})
}
#[unsafe(no_mangle)]
extern "system" fn Java_dev_waterui_android_ffi_WatcherJni_callAction<'local>(
_env: EnvUnowned<'local>,
_class: JClass<'local>,
action_ptr: jlong,
env_ptr: jlong,
) {
let action = action_ptr as *const crate::action::WuiAction;
let wui_env = env_ptr as *const crate::WuiEnv;
unsafe { crate::action::waterui_call_action(action, wui_env) };
}
#[unsafe(no_mangle)]
extern "system" fn Java_dev_waterui_android_ffi_WatcherJni_callSharedAction<'local>(
_env: EnvUnowned<'local>,
_class: JClass<'local>,
action_ptr: jlong,
env_ptr: jlong,
) {
let action = action_ptr as *const crate::WuiSharedAction;
let wui_env = env_ptr as *const crate::WuiEnv;
unsafe { crate::waterui_call_shared_action(action, wui_env) };
}
#[unsafe(no_mangle)]
extern "system" fn Java_dev_waterui_android_ffi_WatcherJni_gestureFromPtr<'local>(
mut env: EnvUnowned<'local>,
_class: JClass<'local>,
gesture_ptr: jlong,
) -> jobject {
use crate::jni::convert::jlong_to_ptr;
let gesture = unsafe { jlong_to_ptr::<crate::gesture::WuiGesture>(gesture_ptr) };
let gesture = unsafe { &*gesture };
super::with_env(&mut env, |env| {
crate::jni::convert::struct_to_java(env, gesture).into_raw()
})
}
#[unsafe(no_mangle)]
extern "system" fn Java_dev_waterui_android_ffi_WatcherJni_dropGesture<'local>(
_env: EnvUnowned<'local>,
_class: JClass<'local>,
gesture_ptr: jlong,
) {
use crate::jni::convert::jlong_to_ptr_mut;
let gesture = unsafe { jlong_to_ptr_mut::<crate::gesture::WuiGesture>(gesture_ptr) };
unsafe { crate::gesture::waterui_drop_gesture(gesture) };
}
#[unsafe(no_mangle)]
extern "system" fn Java_dev_waterui_android_ffi_WatcherJni_callIndexAction<'local>(
_env: EnvUnowned<'local>,
_class: JClass<'local>,
action_ptr: jlong,
env_ptr: jlong,
index: jlong,
) {
let action = action_ptr as *const crate::action::WuiIndexAction;
let wui_env = env_ptr as *const crate::WuiEnv;
unsafe { crate::action::waterui_call_index_action(action, wui_env, index as usize) };
}
#[unsafe(no_mangle)]
extern "system" fn Java_dev_waterui_android_ffi_WatcherJni_callMoveAction<'local>(
_env: EnvUnowned<'local>,
_class: JClass<'local>,
action_ptr: jlong,
env_ptr: jlong,
from_index: jlong,
to_index: jlong,
) {
let action = action_ptr as *const crate::action::WuiMoveAction;
let wui_env = env_ptr as *const crate::WuiEnv;
unsafe {
crate::action::waterui_call_move_action(
action,
wui_env,
from_index as usize,
to_index as usize,
)
};
}
struct JniSubView {
jvm: Arc<jni::JavaVM>,
subview_ref: Global<JObject<'static>>,
stretch_axis: StretchAxis,
priority: i32,
}
impl SubView for JniSubView {
fn measure(&self, proposal: ProposalSize) -> ViewDimensions {
super::with_attached_env(&self.jvm, |env| {
let width = proposal.width.unwrap_or(f32::NAN);
let height = proposal.height.unwrap_or(f32::NAN);
let dimensions_obj = env
.call_method(
&self.subview_ref,
jni_str!("measureForLayout"),
jni_sig!("(FF)Ldev/waterui/android/runtime/ViewDimensionsStruct;"),
&[JValue::Float(width), JValue::Float(height)],
)
.expect("measureForLayout call failed")
.l()
.expect("measureForLayout should return object");
extract_view_dimensions(env, &dimensions_obj)
})
.expect("Failed to attach to JVM")
}
fn stretch_axis(&self) -> StretchAxis {
self.stretch_axis
}
fn priority(&self) -> i32 {
self.priority
}
}
fn horizontal_alignment_to_java<'local>(
env: &mut Env<'local>,
alignment: HorizontalAlignment,
) -> JObject<'local> {
let class = env
.find_class(jni_str!("dev/waterui/android/runtime/HorizontalAlignment"))
.expect("HorizontalAlignment class");
let field_name = if alignment == HorizontalAlignment::Leading {
jni_str!("LEADING")
} else if alignment == HorizontalAlignment::Trailing {
jni_str!("TRAILING")
} else {
jni_str!("CENTER")
};
env.get_static_field(
&class,
field_name,
jni_sig!("Ldev/waterui/android/runtime/HorizontalAlignment;"),
)
.expect("HorizontalAlignment enum value")
.l()
.expect("horizontal alignment is object")
}
fn vertical_alignment_to_java<'local>(
env: &mut Env<'local>,
alignment: VerticalAlignment,
) -> JObject<'local> {
let class = env
.find_class(jni_str!("dev/waterui/android/runtime/VerticalAlignment"))
.expect("VerticalAlignment class");
let field_name = if alignment == VerticalAlignment::Top {
jni_str!("TOP")
} else if alignment == VerticalAlignment::Bottom {
jni_str!("BOTTOM")
} else if alignment == VerticalAlignment::FirstBaseline {
jni_str!("FIRST_BASELINE")
} else if alignment == VerticalAlignment::LastBaseline {
jni_str!("LAST_BASELINE")
} else {
jni_str!("CENTER")
};
env.get_static_field(
&class,
field_name,
jni_sig!("Ldev/waterui/android/runtime/VerticalAlignment;"),
)
.expect("VerticalAlignment enum value")
.l()
.expect("vertical alignment is object")
}
fn extract_horizontal_alignment(env: &mut Env, alignment: &JObject) -> HorizontalAlignment {
let ordinal = env
.call_method(alignment, jni_str!("ordinal"), jni_sig!("()I"), &[])
.expect("horizontal alignment ordinal()")
.i()
.expect("horizontal alignment ordinal is int");
match ordinal {
0 => HorizontalAlignment::Leading,
1 => HorizontalAlignment::Center,
2 => HorizontalAlignment::Trailing,
_ => panic!("unknown HorizontalAlignment ordinal {ordinal}"),
}
}
fn extract_vertical_alignment(env: &mut Env, alignment: &JObject) -> VerticalAlignment {
let ordinal = env
.call_method(alignment, jni_str!("ordinal"), jni_sig!("()I"), &[])
.expect("vertical alignment ordinal()")
.i()
.expect("vertical alignment ordinal is int");
match ordinal {
0 => VerticalAlignment::Top,
1 => VerticalAlignment::Center,
2 => VerticalAlignment::Bottom,
3 => VerticalAlignment::FirstBaseline,
4 => VerticalAlignment::LastBaseline,
_ => panic!("unknown VerticalAlignment ordinal {ordinal}"),
}
}
fn size_to_java<'local>(env: &mut Env<'local>, size: Size) -> JObject<'local> {
let class = env
.find_class(jni_str!("dev/waterui/android/runtime/SizeStruct"))
.expect("SizeStruct class");
env.new_object(
&class,
jni_sig!("(FF)V"),
&[JValue::Float(size.width), JValue::Float(size.height)],
)
.expect("create SizeStruct")
}
fn horizontal_guide_to_java<'local>(
env: &mut Env<'local>,
alignment: HorizontalAlignment,
value: f32,
) -> JObject<'local> {
let class = env
.find_class(jni_str!(
"dev/waterui/android/runtime/HorizontalGuideStruct"
))
.expect("HorizontalGuideStruct class");
let alignment = horizontal_alignment_to_java(env, alignment);
env.new_object(
&class,
jni_sig!("(Ldev/waterui/android/runtime/HorizontalAlignment;F)V"),
&[JValue::Object(&alignment), JValue::Float(value)],
)
.expect("create HorizontalGuideStruct")
}
fn vertical_guide_to_java<'local>(
env: &mut Env<'local>,
alignment: VerticalAlignment,
value: f32,
) -> JObject<'local> {
let class = env
.find_class(jni_str!("dev/waterui/android/runtime/VerticalGuideStruct"))
.expect("VerticalGuideStruct class");
let alignment = vertical_alignment_to_java(env, alignment);
env.new_object(
&class,
jni_sig!("(Ldev/waterui/android/runtime/VerticalAlignment;F)V"),
&[JValue::Object(&alignment), JValue::Float(value)],
)
.expect("create VerticalGuideStruct")
}
fn view_dimensions_to_java<'local>(
env: &mut Env<'local>,
dimensions: &ViewDimensions,
) -> JObject<'local> {
let size = size_to_java(env, dimensions.size);
let horizontal_class = env
.find_class(jni_str!(
"dev/waterui/android/runtime/HorizontalGuideStruct"
))
.expect("HorizontalGuideStruct class");
let horizontal_guides: Vec<_> = dimensions
.explicit_horizontal_guides()
.map(|(alignment, value)| horizontal_guide_to_java(env, alignment, value))
.collect();
let horizontal_array = env
.new_object_array(
super::array_len(horizontal_guides.len()),
&horizontal_class,
JObject::null(),
)
.expect("create HorizontalGuideStruct array");
for (index, guide) in horizontal_guides.iter().enumerate() {
horizontal_array
.set_element(env, index, guide)
.expect("set horizontal guide element");
}
let vertical_class = env
.find_class(jni_str!("dev/waterui/android/runtime/VerticalGuideStruct"))
.expect("VerticalGuideStruct class");
let vertical_guides: Vec<_> = dimensions
.explicit_vertical_guides()
.map(|(alignment, value)| vertical_guide_to_java(env, alignment, value))
.collect();
let vertical_array = env
.new_object_array(
super::array_len(vertical_guides.len()),
&vertical_class,
JObject::null(),
)
.expect("create VerticalGuideStruct array");
for (index, guide) in vertical_guides.iter().enumerate() {
vertical_array
.set_element(env, index, guide)
.expect("set vertical guide element");
}
let dimensions_class = env
.find_class(jni_str!("dev/waterui/android/runtime/ViewDimensionsStruct"))
.expect("ViewDimensionsStruct class");
env.new_object(&dimensions_class, jni_sig!("(Ldev/waterui/android/runtime/SizeStruct;[Ldev/waterui/android/runtime/HorizontalGuideStruct;[Ldev/waterui/android/runtime/VerticalGuideStruct;)V"), &[
JValue::Object(&size),
JValue::Object(&JObject::from(horizontal_array)),
JValue::Object(&JObject::from(vertical_array)),
],)
.expect("create ViewDimensionsStruct")
}
fn extract_view_dimensions(env: &mut Env, dimensions: &JObject) -> ViewDimensions {
let size_obj = env
.get_field(
dimensions,
jni_str!("size"),
jni_sig!("Ldev/waterui/android/runtime/SizeStruct;"),
)
.expect("ViewDimensionsStruct.size")
.l()
.expect("size is object");
let width = env
.get_field(&size_obj, jni_str!("width"), jni_sig!("F"))
.expect("SizeStruct.width")
.f()
.expect("width is float");
let height = env
.get_field(&size_obj, jni_str!("height"), jni_sig!("F"))
.expect("SizeStruct.height")
.f()
.expect("height is float");
let mut result = ViewDimensions::new(Size { width, height });
let horizontal_array = env
.get_field(
dimensions,
jni_str!("horizontalGuides"),
jni_sig!("[Ldev/waterui/android/runtime/HorizontalGuideStruct;"),
)
.expect("ViewDimensionsStruct.horizontalGuides")
.l()
.expect("horizontalGuides is object");
let horizontal_array = env
.cast_local::<JObjectArray>(horizontal_array)
.expect("horizontalGuides must be an object array");
let horizontal_len = horizontal_array
.len(env)
.expect("horizontal guide array length");
for index in 0..horizontal_len {
let guide = horizontal_array
.get_element(env, index)
.expect("horizontal guide element");
let alignment_obj = env
.get_field(
&guide,
jni_str!("alignment"),
jni_sig!("Ldev/waterui/android/runtime/HorizontalAlignment;"),
)
.expect("HorizontalGuideStruct.alignment")
.l()
.expect("alignment is object");
let value = env
.get_field(&guide, jni_str!("value"), jni_sig!("F"))
.expect("HorizontalGuideStruct.value")
.f()
.expect("value is float");
result.set_horizontal(extract_horizontal_alignment(env, &alignment_obj), value);
}
let vertical_array = env
.get_field(
dimensions,
jni_str!("verticalGuides"),
jni_sig!("[Ldev/waterui/android/runtime/VerticalGuideStruct;"),
)
.expect("ViewDimensionsStruct.verticalGuides")
.l()
.expect("verticalGuides is object");
let vertical_array = env
.cast_local::<JObjectArray>(vertical_array)
.expect("verticalGuides must be an object array");
let vertical_len = vertical_array
.len(env)
.expect("vertical guide array length");
for index in 0..vertical_len {
let guide = vertical_array
.get_element(env, index)
.expect("vertical guide element");
let alignment_obj = env
.get_field(
&guide,
jni_str!("alignment"),
jni_sig!("Ldev/waterui/android/runtime/VerticalAlignment;"),
)
.expect("VerticalGuideStruct.alignment")
.l()
.expect("alignment is object");
let value = env
.get_field(&guide, jni_str!("value"), jni_sig!("F"))
.expect("VerticalGuideStruct.value")
.f()
.expect("value is float");
result.set_vertical(extract_vertical_alignment(env, &alignment_obj), value);
}
result
}
fn extract_proposal(env: &mut Env, proposal: &JObject) -> ProposalSize {
let width = env
.get_field(proposal, jni_str!("width"), jni_sig!("F"))
.expect("ProposalStruct.width")
.f()
.expect("width is float");
let height = env
.get_field(proposal, jni_str!("height"), jni_sig!("F"))
.expect("ProposalStruct.height")
.f()
.expect("height is float");
ProposalSize {
width: if width.is_finite() { Some(width) } else { None },
height: if height.is_finite() {
Some(height)
} else {
None
},
}
}
fn extract_rect(env: &mut Env, rect: &JObject) -> Rect {
let x = env
.get_field(rect, jni_str!("x"), jni_sig!("F"))
.expect("RectStruct.x")
.f()
.expect("x is float");
let y = env
.get_field(rect, jni_str!("y"), jni_sig!("F"))
.expect("RectStruct.y")
.f()
.expect("y is float");
let width = env
.get_field(rect, jni_str!("width"), jni_sig!("F"))
.expect("RectStruct.width")
.f()
.expect("width is float");
let height = env
.get_field(rect, jni_str!("height"), jni_sig!("F"))
.expect("RectStruct.height")
.f()
.expect("height is float");
Rect::new(waterui_layout::Point { x, y }, Size { width, height })
}
fn stretch_axis_from_ordinal(ordinal: i32) -> StretchAxis {
match ordinal {
0 => StretchAxis::None,
1 => StretchAxis::Horizontal,
2 => StretchAxis::Vertical,
3 => StretchAxis::Both,
4 => StretchAxis::MainAxis,
5 => StretchAxis::CrossAxis,
value => panic!("unknown StretchAxis ordinal: {value}"),
}
}
fn extract_subviews(env: &mut Env, subviews_array: jobjectArray) -> Vec<JniSubView> {
let array_obj = unsafe { JObjectArray::<JObject>::from_raw(env, subviews_array) };
let len = array_obj.len(env).expect("get array length");
let mut result = Vec::with_capacity(len);
let jvm = Arc::new(
env.get_java_vm()
.expect("SubView extraction failed to access JavaVM"),
);
for i in 0..len {
let subview_obj = array_obj.get_element(env, i).expect("get array element");
let stretch_axis_obj = env
.get_field(
&subview_obj,
jni_str!("stretchAxis"),
jni_sig!("Ldev/waterui/android/runtime/StretchAxis;"),
)
.expect("SubViewStruct.stretchAxis")
.l()
.expect("stretchAxis is object");
let stretch_ordinal = env
.call_method(&stretch_axis_obj, jni_str!("ordinal"), jni_sig!("()I"), &[])
.expect("ordinal()")
.i()
.expect("ordinal is int");
let priority = env
.get_field(&subview_obj, jni_str!("priority"), jni_sig!("I"))
.expect("SubViewStruct.priority")
.i()
.expect("priority is int");
let global_ref = env.new_global_ref(&subview_obj).expect("create global ref");
result.push(JniSubView {
jvm: Arc::clone(&jvm),
subview_ref: global_ref,
stretch_axis: stretch_axis_from_ordinal(stretch_ordinal),
priority,
});
}
result
}
#[cfg(target_os = "android")]
struct AndroidLayoutInvalidationTarget {
jvm: jni::JavaVM,
owner: Global<JObject<'static>>,
}
#[cfg(target_os = "android")]
unsafe extern "C" fn invalidate_android_layout(context: *mut c_void) {
let target = unsafe { &*(context as *const AndroidLayoutInvalidationTarget) };
super::with_attached_env(&target.jvm, |env| {
env.call_method(
&target.owner,
jni_str!("requestLayout"),
jni_sig!("()V"),
&[],
)
.expect("layout invalidation failed to request native layout");
})
.expect("layout invalidation failed to attach to JVM");
}
#[cfg(target_os = "android")]
unsafe extern "C" fn drop_android_layout_invalidation_target(context: *mut c_void) {
unsafe {
drop(Box::from_raw(
context as *mut AndroidLayoutInvalidationTarget,
))
};
}
#[cfg(target_os = "android")]
#[unsafe(no_mangle)]
extern "system" fn Java_dev_waterui_android_ffi_WatcherJni_layoutWatchInvalidation<'local>(
mut env: EnvUnowned<'local>,
_class: JClass<'local>,
layout_ptr: jlong,
owner: JObject<'local>,
) -> jlong {
super::with_env(&mut env, |env| {
let target = Box::new(AndroidLayoutInvalidationTarget {
jvm: env
.get_java_vm()
.expect("layoutWatchInvalidation failed to access JavaVM"),
owner: env
.new_global_ref(owner)
.expect("layoutWatchInvalidation failed to retain owner view"),
});
let context = Box::into_raw(target).cast::<c_void>();
unsafe {
crate::components::layout::waterui_layout_watch_invalidation(
layout_ptr as *const WuiLayout,
context,
invalidate_android_layout,
drop_android_layout_invalidation_target,
) as jlong
}
})
}
#[cfg(target_os = "android")]
#[unsafe(no_mangle)]
extern "system" fn Java_dev_waterui_android_ffi_WatcherJni_layoutWatcherDrop<'local>(
_env: EnvUnowned<'local>,
_class: JClass<'local>,
watcher_ptr: jlong,
) {
unsafe {
crate::components::layout::waterui_layout_watcher_drop(
watcher_ptr as *mut crate::components::layout::WuiLayoutWatcher,
);
}
}
#[unsafe(no_mangle)]
extern "system" fn Java_dev_waterui_android_ffi_WatcherJni_layoutMeasure<'local>(
mut env: EnvUnowned<'local>,
_class: JClass<'local>,
layout_ptr: jlong,
proposal: JObject<'local>,
subviews: jobjectArray,
) -> jobject {
super::with_env(&mut env, |env| {
let layout: &dyn Layout = unsafe { &*(*(layout_ptr as *mut WuiLayout)).0 };
let proposal = extract_proposal(env, &proposal);
let jni_subviews = extract_subviews(env, subviews);
let subview_refs: Vec<&dyn SubView> =
jni_subviews.iter().map(|s| s as &dyn SubView).collect();
let dimensions = measure_layout(layout, proposal, &subview_refs);
view_dimensions_to_java(env, &dimensions).into_raw()
})
}
#[unsafe(no_mangle)]
extern "system" fn Java_dev_waterui_android_ffi_WatcherJni_layoutSizeThatFits<'local>(
mut env: EnvUnowned<'local>,
_class: JClass<'local>,
layout_ptr: jlong,
proposal: JObject<'local>,
subviews: jobjectArray,
) -> jobject {
super::with_env(&mut env, |env| {
let layout: &dyn Layout = unsafe { &*(*(layout_ptr as *mut WuiLayout)).0 };
let proposal = extract_proposal(env, &proposal);
let jni_subviews = extract_subviews(env, subviews);
let subview_refs: Vec<&dyn SubView> =
jni_subviews.iter().map(|s| s as &dyn SubView).collect();
let size =
with_memoized_children(&subview_refs, |refs| layout.size_that_fits(proposal, refs));
env.new_object(
jni_str!("dev/waterui/android/runtime/SizeStruct"),
jni_sig!("(FF)V"),
&[JValue::Float(size.width), JValue::Float(size.height)],
)
.expect("create SizeStruct")
.into_raw()
})
}
#[unsafe(no_mangle)]
extern "system" fn Java_dev_waterui_android_ffi_WatcherJni_layoutPlace<'local>(
mut env: EnvUnowned<'local>,
_class: JClass<'local>,
layout_ptr: jlong,
bounds: JObject<'local>,
subviews: jobjectArray,
) -> jobjectArray {
super::with_env(&mut env, |env| {
let layout: &dyn Layout = unsafe { &*(*(layout_ptr as *mut WuiLayout)).0 };
let bounds = extract_rect(env, &bounds);
let jni_subviews = extract_subviews(env, subviews);
let subview_refs: Vec<&dyn SubView> =
jni_subviews.iter().map(|s| s as &dyn SubView).collect();
let rects = with_memoized_children(&subview_refs, |refs| layout.place(bounds, refs));
let rect_class = env
.find_class(jni_str!("dev/waterui/android/runtime/RectStruct"))
.expect("RectStruct class");
let result_array = env
.new_object_array(super::array_len(rects.len()), &rect_class, JObject::null())
.expect("create RectStruct array");
for (i, rect) in rects.iter().enumerate() {
let rect_obj = env
.new_object(
&rect_class,
jni_sig!("(FFFF)V"),
&[
JValue::Float(rect.x()),
JValue::Float(rect.y()),
JValue::Float(rect.width()),
JValue::Float(rect.height()),
],
)
.expect("create RectStruct");
result_array
.set_element(env, i, rect_obj)
.expect("set array element");
}
result_array.into_raw()
})
}
#[unsafe(no_mangle)]
extern "system" fn Java_dev_waterui_android_ffi_WatcherJni_layoutLazyStackAxis<'local>(
_env: EnvUnowned<'local>,
_class: JClass<'local>,
layout_ptr: jlong,
) -> jint {
unsafe {
crate::components::layout::waterui_layout_lazy_stack_axis(layout_ptr as *mut WuiLayout)
as jint
}
}
#[unsafe(no_mangle)]
extern "system" fn Java_dev_waterui_android_ffi_WatcherJni_layoutLazyStackSpacing<'local>(
_env: EnvUnowned<'local>,
_class: JClass<'local>,
layout_ptr: jlong,
) -> jfloat {
unsafe {
crate::components::layout::waterui_layout_lazy_stack_spacing(layout_ptr as *mut WuiLayout)
}
}
#[unsafe(no_mangle)]
extern "system" fn Java_dev_waterui_android_ffi_WatcherJni_layoutLazyStackHorizontalAlignment<
'local,
>(
_env: EnvUnowned<'local>,
_class: JClass<'local>,
layout_ptr: jlong,
) -> jint {
unsafe {
crate::components::layout::waterui_layout_lazy_stack_horizontal_alignment(
layout_ptr as *mut WuiLayout,
) as jint
}
}
#[unsafe(no_mangle)]
extern "system" fn Java_dev_waterui_android_ffi_WatcherJni_layoutLazyStackVerticalAlignment<
'local,
>(
_env: EnvUnowned<'local>,
_class: JClass<'local>,
layout_ptr: jlong,
) -> jint {
unsafe {
crate::components::layout::waterui_layout_lazy_stack_vertical_alignment(
layout_ptr as *mut WuiLayout,
) as jint
}
}
fn color_slot(slot: jint) -> crate::theme::WuiColorSlot {
match slot {
0 => crate::theme::WuiColorSlot::Background,
1 => crate::theme::WuiColorSlot::Surface,
2 => crate::theme::WuiColorSlot::SurfaceVariant,
3 => crate::theme::WuiColorSlot::Border,
4 => crate::theme::WuiColorSlot::Foreground,
5 => crate::theme::WuiColorSlot::MutedForeground,
6 => crate::theme::WuiColorSlot::Accent,
7 => crate::theme::WuiColorSlot::AccentForeground,
8 => crate::theme::WuiColorSlot::AccentContainer,
9 => crate::theme::WuiColorSlot::Tertiary,
10 => crate::theme::WuiColorSlot::TertiaryContainer,
11 => crate::theme::WuiColorSlot::SelectionContainer,
12 => crate::theme::WuiColorSlot::SelectionForeground,
value => panic!("unknown color slot ordinal: {value}"),
}
}
fn font_slot(slot: jint) -> crate::theme::WuiFontSlot {
match slot {
0 => crate::theme::WuiFontSlot::Body,
1 => crate::theme::WuiFontSlot::Title,
2 => crate::theme::WuiFontSlot::Headline,
3 => crate::theme::WuiFontSlot::Subheadline,
4 => crate::theme::WuiFontSlot::Caption,
5 => crate::theme::WuiFontSlot::Footnote,
value => panic!("unknown font slot ordinal: {value}"),
}
}
#[unsafe(no_mangle)]
extern "system" fn Java_dev_waterui_android_ffi_WatcherJni_themeInstallColor<'local>(
_env: EnvUnowned<'local>,
_class: JClass<'local>,
env_ptr: jlong,
slot: jint,
signal_ptr: jlong,
) {
let wui_env = env_ptr as *mut crate::WuiEnv;
let slot = color_slot(slot);
let signal =
signal_ptr as *mut crate::reactive::WuiComputed<waterui_graphics::color::ResolvedColor>;
unsafe { crate::theme::waterui_theme_install_color(wui_env, slot, signal) };
}
#[unsafe(no_mangle)]
extern "system" fn Java_dev_waterui_android_ffi_WatcherJni_themeInstallFont<'local>(
_env: EnvUnowned<'local>,
_class: JClass<'local>,
env_ptr: jlong,
slot: jint,
signal_ptr: jlong,
) {
let wui_env = env_ptr as *mut crate::WuiEnv;
let slot = font_slot(slot);
let signal = signal_ptr as *mut crate::reactive::WuiComputed<waterui_text::font::ResolvedFont>;
unsafe { crate::theme::waterui_theme_install_font(wui_env, slot, signal) };
}
#[unsafe(no_mangle)]
extern "system" fn Java_dev_waterui_android_ffi_WatcherJni_themeInstallColorScheme<'local>(
_env: EnvUnowned<'local>,
_class: JClass<'local>,
env_ptr: jlong,
signal_ptr: jlong,
) {
let wui_env = env_ptr as *mut crate::WuiEnv;
let signal = signal_ptr as *mut crate::reactive::WuiComputed<waterui::theme::ColorScheme>;
unsafe { crate::theme::waterui_theme_install_color_scheme(wui_env, signal) };
}
#[unsafe(no_mangle)]
extern "system" fn Java_dev_waterui_android_ffi_WatcherJni_themeColor<'local>(
_env: EnvUnowned<'local>,
_class: JClass<'local>,
env_ptr: jlong,
slot: jint,
) -> jlong {
let wui_env = env_ptr as *const crate::WuiEnv;
let slot = color_slot(slot);
unsafe { crate::theme::waterui_theme_color(wui_env, slot) as jlong }
}
#[unsafe(no_mangle)]
extern "system" fn Java_dev_waterui_android_ffi_WatcherJni_themeFont<'local>(
_env: EnvUnowned<'local>,
_class: JClass<'local>,
env_ptr: jlong,
slot: jint,
) -> jlong {
let wui_env = env_ptr as *const crate::WuiEnv;
let slot = font_slot(slot);
unsafe { crate::theme::waterui_theme_font(wui_env, slot) as jlong }
}
#[unsafe(no_mangle)]
extern "system" fn Java_dev_waterui_android_ffi_WatcherJni_envDisabled<'local>(
_env: EnvUnowned<'local>,
_class: JClass<'local>,
env_ptr: jlong,
) -> jlong {
let wui_env = env_ptr as *const crate::WuiEnv;
unsafe { crate::waterui_env_disabled(wui_env) as jlong }
}
#[unsafe(no_mangle)]
extern "system" fn Java_dev_waterui_android_ffi_WatcherJni_themeColorScheme<'local>(
_env: EnvUnowned<'local>,
_class: JClass<'local>,
env_ptr: jlong,
) -> jlong {
let wui_env = env_ptr as *const crate::WuiEnv;
unsafe { crate::theme::waterui_theme_color_scheme(wui_env) as jlong }
}
#[unsafe(no_mangle)]
extern "system" fn Java_dev_waterui_android_ffi_WatcherJni_getAnimationKindDurationPacked<
'local,
>(
_env: EnvUnowned<'local>,
_class: JClass<'local>,
metadata_ptr: jlong,
) -> jlong {
let metadata = metadata_ptr as *const crate::reactive::WuiWatcherMetadata;
let animation = unsafe { crate::animation::waterui_get_animation(metadata) };
let (tag, duration_ms): (u32, u32) = match animation {
crate::animation::WuiAnimation::None => (0, 0),
crate::animation::WuiAnimation::Bezier { duration_ms, .. } => (
1,
u32::try_from(duration_ms)
.expect("Android animation duration exceeds packed JNI range"),
),
crate::animation::WuiAnimation::Spring { .. } => (2, 0),
};
((u64::from(duration_ms) << 32) | u64::from(tag)) as jlong
}
#[unsafe(no_mangle)]
extern "system" fn Java_dev_waterui_android_ffi_WatcherJni_getAnimationParams12Packed<'local>(
_env: EnvUnowned<'local>,
_class: JClass<'local>,
metadata_ptr: jlong,
) -> jlong {
let metadata = metadata_ptr as *const crate::reactive::WuiWatcherMetadata;
let animation = unsafe { crate::animation::waterui_get_animation(metadata) };
let (p1, p2): (f32, f32) = match animation {
crate::animation::WuiAnimation::Bezier { x1, y1, .. } => (x1, y1),
crate::animation::WuiAnimation::Spring { stiffness, damping } => (stiffness, damping),
crate::animation::WuiAnimation::None => (0.0, 0.0),
};
((u64::from(p2.to_bits()) << 32) | u64::from(p1.to_bits())) as jlong
}
#[unsafe(no_mangle)]
extern "system" fn Java_dev_waterui_android_ffi_WatcherJni_getAnimationParams34Packed<'local>(
_env: EnvUnowned<'local>,
_class: JClass<'local>,
metadata_ptr: jlong,
) -> jlong {
let metadata = metadata_ptr as *const crate::reactive::WuiWatcherMetadata;
let animation = unsafe { crate::animation::waterui_get_animation(metadata) };
let (p3, p4): (f32, f32) = match animation {
crate::animation::WuiAnimation::Bezier { x2, y2, .. } => (x2, y2),
crate::animation::WuiAnimation::Spring { .. } | crate::animation::WuiAnimation::None => {
(0.0, 0.0)
}
};
((u64::from(p4.to_bits()) << 32) | u64::from(p3.to_bits())) as jlong
}
struct AnyViewsWatchData {
jvm: jni::JavaVM,
callback: Global<JObject<'static>>,
}
unsafe extern "C" fn anyviews_watch_call(
data: *mut (),
ids: crate::array::WuiArray<crate::id::WuiId>,
metadata_ptr: *mut crate::reactive::WuiWatcherMetadata,
) {
let _metadata = unsafe { Box::from_raw(metadata_ptr) };
let values: Vec<jint> = ids.iter().map(|id| id.inner).collect();
ids.consume();
let watcher_data = unsafe { &*(data as *const AnyViewsWatchData) };
super::with_attached_env(&watcher_data.jvm, |env| {
let ids = env
.new_int_array(values.len())
.expect("anyviews_watch_call: failed to allocate callback IDs");
ids.set_region(env, 0, &values)
.expect("anyviews_watch_call: failed to write callback IDs");
let ids = JObject::from(ids);
env.call_method(
&watcher_data.callback,
jni_str!("onChanged"),
jni_sig!("([I)V"),
&[JValue::Object(&ids)],
)
.expect("anyviews_watch_call: NativeAnyViewsWatcher.onChanged failed");
})
.expect("anyviews_watch_call: failed to attach current thread to JVM");
}
unsafe extern "C" fn anyviews_watch_drop(data: *mut ()) {
unsafe {
let _: Box<AnyViewsWatchData> = Box::from_raw(data as *mut AnyViewsWatchData);
}
}
#[unsafe(no_mangle)]
extern "system" fn Java_dev_waterui_android_ffi_WatcherJni_anyViewsLen<'local>(
_env: EnvUnowned<'local>,
_class: JClass<'local>,
handle: jlong,
) -> jint {
let anyviews = handle as *mut crate::views::WuiAnyViews as *const crate::views::WuiAnyViews;
unsafe { crate::views::waterui_anyviews_len(anyviews) as jint }
}
#[unsafe(no_mangle)]
extern "system" fn Java_dev_waterui_android_ffi_WatcherJni_anyViewsGetIdsInRange<'local>(
mut env: EnvUnowned<'local>,
_class: JClass<'local>,
handle: jlong,
start: jint,
end: jint,
) -> jintArray {
super::with_env(&mut env, |env| {
let anyviews = unsafe { crate::borrow_ffi(handle as *const crate::views::WuiAnyViews) };
let start = start as usize;
let end = end as usize;
let ids = unsafe {
crate::views::waterui_anyviews_get_ids_in_range(anyviews as *const _, start, end)
};
let rust_ids: Vec<waterui_core::id::Id> = unsafe { crate::IntoRust::into_rust(ids) };
let values: Vec<jint> = rust_ids.into_iter().map(i32::from).collect();
let array = env
.new_int_array(values.len())
.expect("anyViewsGetIdsInRange: failed to allocate jintArray");
array
.set_region(env, 0, &values)
.expect("anyViewsGetIdsInRange: failed to write ids into jintArray");
array.into_raw()
})
}
#[unsafe(no_mangle)]
extern "system" fn Java_dev_waterui_android_ffi_WatcherJni_anyViewsGetView<'local>(
_env: EnvUnowned<'local>,
_class: JClass<'local>,
handle: jlong,
index: jint,
) -> jlong {
let anyviews = handle as *mut crate::views::WuiAnyViews as *const crate::views::WuiAnyViews;
unsafe { crate::views::waterui_anyviews_get_view(anyviews, index as usize) as jlong }
}
#[unsafe(no_mangle)]
extern "system" fn Java_dev_waterui_android_ffi_WatcherJni_anyViewsWatch<'local>(
env: EnvUnowned<'local>,
_class: JClass<'local>,
handle: jlong,
callback: JObject<'local>,
) -> jlong {
any_views_watch_range(env, handle, 0, jint::MAX, callback)
}
fn any_views_watch_range<'local>(
mut env: EnvUnowned<'local>,
handle: jlong,
start: jint,
end: jint,
callback: JObject<'local>,
) -> jlong {
super::with_env(&mut env, |env| {
let jvm = env
.get_java_vm()
.expect("WatcherJni.anyViewsWatchRange failed to access JavaVM");
let callback = env
.new_global_ref(&callback)
.expect("WatcherJni.anyViewsWatchRange failed to create callback global reference");
let data = Box::new(AnyViewsWatchData { jvm, callback });
let data_ptr = Box::into_raw(data) as *mut ();
let anyviews = handle as *mut crate::views::WuiAnyViews as *const crate::views::WuiAnyViews;
let watcher = unsafe {
crate::views::waterui_anyviews_watch_range(
anyviews,
start as usize,
end as usize,
data_ptr,
anyviews_watch_call,
anyviews_watch_drop,
)
};
watcher as jlong
})
}
#[unsafe(no_mangle)]
extern "system" fn Java_dev_waterui_android_ffi_WatcherJni_colorFromLinearRgbaHeadroom<'local>(
_env: EnvUnowned<'local>,
_class: JClass<'local>,
red: jfloat,
green: jfloat,
blue: jfloat,
alpha: jfloat,
headroom: jfloat,
) -> jlong {
unsafe {
crate::color::waterui_color_from_linear_rgba_headroom(red, green, blue, alpha, headroom)
as jlong
}
}
#[unsafe(no_mangle)]
extern "system" fn Java_dev_waterui_android_ffi_WatcherJni_resolveColor<'local>(
_env: EnvUnowned<'local>,
_class: JClass<'local>,
color_ptr: jlong,
env_ptr: jlong,
) -> jlong {
let color = color_ptr as *mut crate::color::WuiColor as *const crate::color::WuiColor;
let wui_env = env_ptr as *const crate::WuiEnv;
unsafe { crate::color::waterui_resolve_color(color, wui_env) as jlong }
}
#[unsafe(no_mangle)]
extern "system" fn Java_dev_waterui_android_ffi_WatcherJni_resolveFont<'local>(
_env: EnvUnowned<'local>,
_class: JClass<'local>,
font_ptr: jlong,
env_ptr: jlong,
) -> jlong {
let font = font_ptr as *mut crate::components::text::WuiFont
as *const crate::components::text::WuiFont;
let wui_env = env_ptr as *const crate::WuiEnv;
unsafe { crate::components::text::waterui_resolve_font(font, wui_env) as jlong }
}
#[unsafe(no_mangle)]
extern "system" fn Java_dev_waterui_android_ffi_WatcherJni_callLifecycleHook<'local>(
_env: EnvUnowned<'local>,
_class: JClass<'local>,
handler_ptr: jlong,
env_ptr: jlong,
) {
let handler = handler_ptr as *mut crate::event::WuiLifecycleHookHandler;
let wui_env = env_ptr as *const crate::WuiEnv;
unsafe { crate::event::waterui_call_lifecycle_hook(handler, wui_env) };
}
#[unsafe(no_mangle)]
extern "system" fn Java_dev_waterui_android_ffi_WatcherJni_dropLifecycleHook<'local>(
_env: EnvUnowned<'local>,
_class: JClass<'local>,
handler_ptr: jlong,
) {
let handler_ptr = handler_ptr as *mut crate::event::WuiLifecycleHookHandler;
unsafe { crate::event::waterui_drop_lifecycle_hook(handler_ptr) };
}
#[unsafe(no_mangle)]
extern "system" fn Java_dev_waterui_android_ffi_WatcherJni_callOnEvent<'local>(
_env: EnvUnowned<'local>,
_class: JClass<'local>,
handler_ptr: jlong,
env_ptr: jlong,
) {
let handler = handler_ptr as *const crate::event::WuiOnEventHandler;
let wui_env = env_ptr as *const crate::WuiEnv;
unsafe { crate::event::waterui_call_on_event(handler, wui_env) };
}
#[unsafe(no_mangle)]
extern "system" fn Java_dev_waterui_android_ffi_WatcherJni_callOnHoverEvent<'local>(
_env: EnvUnowned<'local>,
_class: JClass<'local>,
handler_ptr: jlong,
env_ptr: jlong,
x: jfloat,
y: jfloat,
) {
let handler = handler_ptr as *const crate::event::WuiOnEventHandler;
let wui_env = env_ptr as *const crate::WuiEnv;
unsafe { crate::event::waterui_call_on_hover_event(handler, wui_env, x, y) };
}
#[unsafe(no_mangle)]
extern "system" fn Java_dev_waterui_android_ffi_WatcherJni_dropOnEvent<'local>(
_env: EnvUnowned<'local>,
_class: JClass<'local>,
handler_ptr: jlong,
) {
let handler_ptr = handler_ptr as *mut crate::event::WuiOnEventHandler;
unsafe { crate::event::waterui_drop_on_event(handler_ptr) };
}
#[unsafe(no_mangle)]
extern "system" fn Java_dev_waterui_android_ffi_WatcherJni_dropRetain<'local>(
_env: EnvUnowned<'local>,
_class: JClass<'local>,
retain_ptr: jlong,
) {
unsafe { crate::waterui_drop_retain(crate::WuiRetain::from_ptr(retain_ptr as *mut ())) };
}
struct ReactiveColorSchemeState {
binding: waterui::Binding<waterui::theme::ColorScheme>,
}
struct ReactiveColorState {
binding: waterui::Binding<ResolvedColor>,
}
struct ReactiveFontState {
binding: waterui::Binding<ResolvedFont>,
}
struct ReactiveEdgeInsetsState {
binding: waterui::Binding<waterui_layout::padding::EdgeInsets>,
}
#[inline]
fn reactive_state<'a, T>(state_ptr: jlong) -> &'a T {
unsafe { crate::borrow_ffi(state_ptr as *const T) }
}
fn color_scheme_from_ordinal(ordinal: jint) -> waterui::theme::ColorScheme {
match ordinal {
0 => waterui::theme::ColorScheme::Light,
1 => waterui::theme::ColorScheme::Dark,
value => panic!("unknown color scheme ordinal: {value}"),
}
}
fn resolved_color_from_argb(argb: jint) -> ResolvedColor {
let a = ((argb as u32) >> 24) as u8;
let r = ((argb as u32) >> 16) as u8;
let g = ((argb as u32) >> 8) as u8;
let b = (argb as u32) as u8;
let srgb = waterui_graphics::color::Srgb::new_u8(r, g, b);
let mut resolved = ResolvedColor::from_srgb(srgb);
resolved.opacity = a as f32 / 255.0;
resolved
}
fn font_weight_from_ordinal(weight: jint) -> FontWeight {
match weight {
0 => FontWeight::Thin,
1 => FontWeight::UltraLight,
2 => FontWeight::Light,
3 => FontWeight::Normal,
4 => FontWeight::Medium,
5 => FontWeight::SemiBold,
6 => FontWeight::Bold,
7 => FontWeight::UltraBold,
8 => FontWeight::Black,
value => panic!("unknown font weight ordinal: {value}"),
}
}
fn resolved_system_font(size: jfloat, weight: jint) -> ResolvedFont {
ResolvedFont::new(size, font_weight_from_ordinal(weight))
}
#[unsafe(no_mangle)]
extern "system" fn Java_dev_waterui_android_ffi_WatcherJni_createReactiveColorSchemeState<
'local,
>(
_env: EnvUnowned<'local>,
_class: JClass<'local>,
scheme: jint,
) -> jlong {
let state = ReactiveColorSchemeState {
binding: waterui::reactive::binding(color_scheme_from_ordinal(scheme)),
};
Box::into_raw(Box::new(state)) as jlong
}
#[unsafe(no_mangle)]
extern "system" fn Java_dev_waterui_android_ffi_WatcherJni_reactiveColorSchemeStateToComputed<
'local,
>(
_env: EnvUnowned<'local>,
_class: JClass<'local>,
state_ptr: jlong,
) -> jlong {
let state = reactive_state::<ReactiveColorSchemeState>(state_ptr);
let computed = state.binding.computed();
computed.into_ffi() as jlong
}
#[unsafe(no_mangle)]
extern "system" fn Java_dev_waterui_android_ffi_WatcherJni_reactiveColorSchemeStateSet<'local>(
_env: EnvUnowned<'local>,
_class: JClass<'local>,
state_ptr: jlong,
scheme: jint,
) {
let state = reactive_state::<ReactiveColorSchemeState>(state_ptr);
state.binding.set(color_scheme_from_ordinal(scheme));
}
#[unsafe(no_mangle)]
extern "system" fn Java_dev_waterui_android_ffi_WatcherJni_dropReactiveColorSchemeState<'local>(
_env: EnvUnowned<'local>,
_class: JClass<'local>,
state_ptr: jlong,
) {
let state = state_ptr as *mut ReactiveColorSchemeState;
unsafe { drop(Box::from_raw(state)) };
}
#[unsafe(no_mangle)]
extern "system" fn Java_dev_waterui_android_ffi_WatcherJni_createReactiveEdgeInsetsState<'local>(
_env: EnvUnowned<'local>,
_class: JClass<'local>,
top: jfloat,
bottom: jfloat,
leading: jfloat,
trailing: jfloat,
) -> jlong {
let state = ReactiveEdgeInsetsState {
binding: waterui::reactive::binding(waterui_layout::padding::EdgeInsets::new(
top, bottom, leading, trailing,
)),
};
Box::into_raw(Box::new(state)) as jlong
}
#[unsafe(no_mangle)]
extern "system" fn Java_dev_waterui_android_ffi_WatcherJni_reactiveEdgeInsetsStateToComputed<
'local,
>(
_env: EnvUnowned<'local>,
_class: JClass<'local>,
state_ptr: jlong,
) -> jlong {
let state = reactive_state::<ReactiveEdgeInsetsState>(state_ptr);
let computed = state.binding.computed();
computed.into_ffi() as jlong
}
#[unsafe(no_mangle)]
extern "system" fn Java_dev_waterui_android_ffi_WatcherJni_reactiveEdgeInsetsStateSet<'local>(
_env: EnvUnowned<'local>,
_class: JClass<'local>,
state_ptr: jlong,
top: jfloat,
bottom: jfloat,
leading: jfloat,
trailing: jfloat,
) {
let state = reactive_state::<ReactiveEdgeInsetsState>(state_ptr);
state.binding.set(waterui_layout::padding::EdgeInsets::new(
top, bottom, leading, trailing,
));
}
#[unsafe(no_mangle)]
extern "system" fn Java_dev_waterui_android_ffi_WatcherJni_dropReactiveEdgeInsetsState<'local>(
_env: EnvUnowned<'local>,
_class: JClass<'local>,
state_ptr: jlong,
) {
let state = state_ptr as *mut ReactiveEdgeInsetsState;
unsafe { drop(Box::from_raw(state)) };
}
#[unsafe(no_mangle)]
extern "system" fn Java_dev_waterui_android_ffi_WatcherJni_envInstallSafeArea<'local>(
_env: EnvUnowned<'local>,
_class: JClass<'local>,
env_ptr: jlong,
signal_ptr: jlong,
) {
let wui_env = env_ptr as *mut crate::WuiEnv;
let signal =
signal_ptr as *mut crate::reactive::WuiComputed<waterui_layout::padding::EdgeInsets>;
unsafe { crate::runtime::safe_area::waterui_env_install_safe_area(wui_env, signal) };
}
#[unsafe(no_mangle)]
extern "system" fn Java_dev_waterui_android_ffi_WatcherJni_createReactiveColorState<'local>(
_env: EnvUnowned<'local>,
_class: JClass<'local>,
argb: jint,
) -> jlong {
let state = ReactiveColorState {
binding: waterui::reactive::binding(resolved_color_from_argb(argb)),
};
Box::into_raw(Box::new(state)) as jlong
}
#[unsafe(no_mangle)]
extern "system" fn Java_dev_waterui_android_ffi_WatcherJni_reactiveColorStateToComputed<'local>(
_env: EnvUnowned<'local>,
_class: JClass<'local>,
state_ptr: jlong,
) -> jlong {
let state = reactive_state::<ReactiveColorState>(state_ptr);
let computed = state.binding.computed();
computed.into_ffi() as jlong
}
#[unsafe(no_mangle)]
extern "system" fn Java_dev_waterui_android_ffi_WatcherJni_reactiveColorStateSet<'local>(
_env: EnvUnowned<'local>,
_class: JClass<'local>,
state_ptr: jlong,
argb: jint,
) {
let state = reactive_state::<ReactiveColorState>(state_ptr);
state.binding.set(resolved_color_from_argb(argb));
}
#[unsafe(no_mangle)]
extern "system" fn Java_dev_waterui_android_ffi_WatcherJni_dropReactiveColorState<'local>(
_env: EnvUnowned<'local>,
_class: JClass<'local>,
state_ptr: jlong,
) {
let state = state_ptr as *mut ReactiveColorState;
unsafe { drop(Box::from_raw(state)) };
}
#[unsafe(no_mangle)]
extern "system" fn Java_dev_waterui_android_ffi_WatcherJni_createReactiveFontState<'local>(
_env: EnvUnowned<'local>,
_class: JClass<'local>,
size: jfloat,
weight: jint,
) -> jlong {
let state = ReactiveFontState {
binding: waterui::reactive::binding(resolved_system_font(size, weight)),
};
Box::into_raw(Box::new(state)) as jlong
}
#[unsafe(no_mangle)]
extern "system" fn Java_dev_waterui_android_ffi_WatcherJni_reactiveFontStateToComputed<'local>(
_env: EnvUnowned<'local>,
_class: JClass<'local>,
state_ptr: jlong,
) -> jlong {
let state = reactive_state::<ReactiveFontState>(state_ptr);
state.binding.computed().into_ffi() as jlong
}
#[unsafe(no_mangle)]
extern "system" fn Java_dev_waterui_android_ffi_WatcherJni_reactiveFontStateSet<'local>(
_env: EnvUnowned<'local>,
_class: JClass<'local>,
state_ptr: jlong,
size: jfloat,
weight: jint,
) {
let state = reactive_state::<ReactiveFontState>(state_ptr);
state.binding.set(resolved_system_font(size, weight));
}
#[unsafe(no_mangle)]
extern "system" fn Java_dev_waterui_android_ffi_WatcherJni_dropReactiveFontState<'local>(
_env: EnvUnowned<'local>,
_class: JClass<'local>,
state_ptr: jlong,
) {
let state = state_ptr as *mut ReactiveFontState;
unsafe { drop(Box::from_raw(state)) };
}
#[unsafe(no_mangle)]
extern "system" fn Java_dev_waterui_android_ffi_WatcherJni_webviewNativeHandle<'local>(
_env: EnvUnowned<'local>,
_class: JClass<'local>,
webview_ptr: jlong,
) -> jlong {
let webview_ptr =
unsafe { crate::borrow_ffi(webview_ptr as *const crate::components::webview::WuiWebView) }
as *const crate::components::webview::WuiWebView;
unsafe {
crate::components::webview::waterui_webview_native_handle(
webview_ptr as *mut crate::components::webview::WuiWebView,
) as jlong
}
}
#[unsafe(no_mangle)]
extern "system" fn Java_dev_waterui_android_ffi_WatcherJni_webviewNativeView<'local>(
mut _env: EnvUnowned<'local>,
_class: JClass<'local>,
handle_ptr: jlong,
) -> jobject {
super::with_env(&mut _env, |env| {
crate::jni::webview_bridge::webview_native_view(env, handle_ptr)
})
}
#[cfg(all(target_os = "android", feature = "gpu"))]
#[unsafe(no_mangle)]
extern "system" fn Java_dev_waterui_android_ffi_WatcherJni_androidVideoSurfaceHostAttach<'local>(
mut env: EnvUnowned<'local>,
_class: JClass<'local>,
bridge_ptr: jlong,
host: JObject<'local>,
) {
super::with_env(&mut env, |env| {
let bridge =
unsafe { &*(bridge_ptr as *const waterui_video_gpu::AndroidVideoSurfaceBridge) };
unsafe { bridge.attach_host(env, &host) }.unwrap_or_else(|error| {
panic!("WatcherJni.androidVideoSurfaceHostAttach failed: {error}")
});
});
}
#[cfg(all(target_os = "android", feature = "gpu"))]
#[unsafe(no_mangle)]
extern "system" fn Java_dev_waterui_android_ffi_WatcherJni_androidVideoSurfaceHostDrop<'local>(
_env: EnvUnowned<'local>,
_class: JClass<'local>,
bridge_ptr: jlong,
) {
unsafe {
drop(Box::from_raw(
bridge_ptr as *mut waterui_video_gpu::AndroidVideoSurfaceBridge,
));
}
}
#[cfg(all(target_os = "android", feature = "gpu"))]
#[unsafe(no_mangle)]
extern "system" fn Java_dev_waterui_android_ffi_WatcherJni_androidVideoSurfaceHostSurfaceDestroyed<
'local,
>(
_env: EnvUnowned<'local>,
_class: JClass<'local>,
bridge_ptr: jlong,
) {
let bridge = unsafe { &*(bridge_ptr as *const waterui_video_gpu::AndroidVideoSurfaceBridge) };
bridge.surface_destroyed().unwrap_or_else(|error| {
panic!("WatcherJni.androidVideoSurfaceHostSurfaceDestroyed failed: {error}")
});
}
#[cfg(all(target_os = "android", feature = "gpu"))]
struct JniGpuSurfaceState {
state: *mut crate::components::gpu_surface::WuiGpuSurfaceState,
window: Option<AndroidNativeWindow>,
}
#[cfg(all(target_os = "android", feature = "gpu"))]
struct AndroidGpuSurfaceRedrawTarget {
jvm: jni::JavaVM,
owner: Global<JObject<'static>>,
}
#[cfg(all(target_os = "android", feature = "gpu"))]
unsafe extern "C" fn wake_android_gpu_surface(context: *mut c_void) {
let target = unsafe { &*(context as *const AndroidGpuSurfaceRedrawTarget) };
super::with_attached_env(&target.jvm, |env| {
env.call_method(
&target.owner,
jni_str!("requestNativeRedraw"),
jni_sig!("()V"),
&[],
)
.expect("GpuSurface redraw callback failed to call requestNativeRedraw");
})
.expect("GpuSurface redraw callback failed to attach to JVM");
}
#[cfg(all(target_os = "android", feature = "gpu"))]
unsafe extern "C" fn drop_android_gpu_surface_redraw_target(context: *mut c_void) {
unsafe { drop(Box::from_raw(context as *mut AndroidGpuSurfaceRedrawTarget)) };
}
#[cfg(all(target_os = "android", feature = "gpu"))]
#[unsafe(no_mangle)]
extern "system" fn Java_dev_waterui_android_ffi_WatcherJni_gpuSurfaceCreate<'local>(
mut env: EnvUnowned<'local>,
_class: JClass<'local>,
owner: JObject<'local>,
renderer_ptr: jlong,
has_picture_in_picture_host_id: jboolean,
picture_in_picture_host_id: jlong,
wui_env_ptr: jlong,
) -> jlong {
super::with_env(&mut env, |env| {
let mut wui_surface = crate::components::gpu_surface::WuiGpuSurface {
surface: renderer_ptr as *mut c_void,
has_picture_in_picture_host_id,
picture_in_picture_host_id: picture_in_picture_host_id as u64,
};
let state = unsafe {
crate::components::gpu_surface::waterui_gpu_surface_create(
&mut wui_surface,
wui_env_ptr as *const crate::WuiEnv,
)
};
let redraw_target = Box::new(AndroidGpuSurfaceRedrawTarget {
jvm: env
.get_java_vm()
.expect("WatcherJni.gpuSurfaceCreate failed to access JavaVM"),
owner: env
.new_global_ref(owner)
.expect("WatcherJni.gpuSurfaceCreate failed to retain owner view"),
});
let redraw_context = Box::into_raw(redraw_target).cast::<c_void>();
unsafe {
crate::components::gpu_surface::waterui_gpu_surface_set_redraw_callback(
state,
redraw_context,
wake_android_gpu_surface,
drop_android_gpu_surface_redraw_target,
);
}
let wrapper = Box::new(JniGpuSurfaceState {
state,
window: None,
});
Box::into_raw(wrapper) as jlong
})
}
#[cfg(all(target_os = "android", feature = "gpu"))]
#[unsafe(no_mangle)]
extern "system" fn Java_dev_waterui_android_ffi_WatcherJni_gpuSurfaceMeasure<'local>(
mut env: EnvUnowned<'local>,
_class: JClass<'local>,
state_ptr: jlong,
width: jfloat,
height: jfloat,
) -> jobject {
super::with_env(&mut env, |env| {
let state_ptr = state_ptr as *mut JniGpuSurfaceState;
let wrapper = unsafe { &*state_ptr };
let proposal = ProposalSize {
width: width.is_finite().then_some(width),
height: height.is_finite().then_some(height),
};
let dimensions =
unsafe { crate::components::gpu_surface::measure_state(&*wrapper.state, proposal) };
view_dimensions_to_java(env, &dimensions).into_raw()
})
}
#[cfg(all(target_os = "android", feature = "gpu"))]
#[unsafe(no_mangle)]
extern "system" fn Java_dev_waterui_android_ffi_WatcherJni_gpuSurfacePriority<'local>(
_env: EnvUnowned<'local>,
_class: JClass<'local>,
state_ptr: jlong,
) -> jint {
let state_ptr = state_ptr as *mut JniGpuSurfaceState;
let wrapper = unsafe { &*state_ptr };
unsafe { crate::components::gpu_surface::priority_state(&*wrapper.state) }
}
#[cfg(all(target_os = "android", feature = "gpu"))]
#[unsafe(no_mangle)]
extern "system" fn Java_dev_waterui_android_ffi_WatcherJni_gpuSurfaceIsReady<'local>(
_env: EnvUnowned<'local>,
_class: JClass<'local>,
state_ptr: jlong,
) -> jboolean {
let state_ptr = state_ptr as *mut JniGpuSurfaceState;
let wrapper = unsafe { &*state_ptr };
let ready =
unsafe { crate::components::gpu_surface::waterui_gpu_surface_is_ready(wrapper.state) };
ready
}
#[cfg(all(target_os = "android", feature = "gpu"))]
#[unsafe(no_mangle)]
extern "system" fn Java_dev_waterui_android_ffi_WatcherJni_gpuSurfaceAttach<'local>(
mut env: EnvUnowned<'local>,
_class: JClass<'local>,
state_ptr: jlong,
surface: JObject<'local>,
width: jint,
height: jint,
prefers_hdr: jboolean,
) {
super::with_env(&mut env, |env| {
let state_ptr = state_ptr as *mut JniGpuSurfaceState;
let wrapper = unsafe { &mut *state_ptr };
assert!(
wrapper.window.is_none(),
"WatcherJni.gpuSurfaceAttach called while a native surface is already attached"
);
let window = require_native_window(
unsafe { AndroidNativeWindow::from_surface(env.get_raw(), surface.as_raw()) },
"gpuSurfaceAttach",
);
unsafe {
crate::components::gpu_surface::waterui_gpu_surface_attach(
wrapper.state,
window.as_void_ptr(),
width as u32,
height as u32,
prefers_hdr,
);
}
wrapper.window = Some(window);
});
}
#[cfg(all(target_os = "android", feature = "gpu"))]
#[unsafe(no_mangle)]
extern "system" fn Java_dev_waterui_android_ffi_WatcherJni_gpuSurfaceDetach<'local>(
_env: EnvUnowned<'local>,
_class: JClass<'local>,
state_ptr: jlong,
) {
let state_ptr = state_ptr as *mut JniGpuSurfaceState;
let wrapper = unsafe { &mut *state_ptr };
assert!(
wrapper.window.is_some(),
"WatcherJni.gpuSurfaceDetach called without an attached native surface"
);
unsafe {
crate::components::gpu_surface::waterui_gpu_surface_detach(wrapper.state);
}
drop(wrapper.window.take());
}
#[cfg(all(target_os = "android", feature = "gpu"))]
#[unsafe(no_mangle)]
extern "system" fn Java_dev_waterui_android_ffi_WatcherJni_gpuSurfaceRender<'local>(
_env: EnvUnowned<'local>,
_class: JClass<'local>,
state_ptr: jlong,
width: jint,
height: jint,
scale: jfloat,
) -> jboolean {
let state_ptr = state_ptr as *mut JniGpuSurfaceState;
let wrapper = unsafe { &mut *state_ptr };
let needs_redraw = unsafe {
crate::components::gpu_surface::waterui_gpu_surface_render(
wrapper.state,
width as u32,
height as u32,
f64::from(scale),
)
};
needs_redraw
}
#[cfg(all(target_os = "android", feature = "gpu"))]
#[unsafe(no_mangle)]
extern "system" fn Java_dev_waterui_android_ffi_WatcherJni_gpuSurfaceSetInput<'local>(
_env: EnvUnowned<'local>,
_class: JClass<'local>,
state_ptr: jlong,
has_position: jboolean,
x: jfloat,
y: jfloat,
has_hit: jboolean,
hit_x: jfloat,
hit_y: jfloat,
gesture_active: jboolean,
pinch_scale: jfloat,
has_pinch_center: jboolean,
pinch_center_x: jfloat,
pinch_center_y: jfloat,
pan_offset_x: jfloat,
pan_offset_y: jfloat,
double_tap: jboolean,
) {
let state_ptr = state_ptr as *mut JniGpuSurfaceState;
let wrapper = unsafe { &mut *state_ptr };
let input = crate::components::gpu_surface::WuiGpuSurfaceInput {
pointer: crate::components::gpu_surface::WuiPointerState {
has_position,
x,
y,
has_hit,
hit_x,
hit_y,
},
gesture: crate::components::gpu_surface::WuiGestureState {
active: gesture_active,
pinch_scale,
has_pinch_center,
pinch_center_x,
pinch_center_y,
pan_offset_x,
pan_offset_y,
double_tap,
},
};
unsafe {
crate::components::gpu_surface::waterui_gpu_surface_set_input(wrapper.state, input);
}
}
#[cfg(all(target_os = "android", feature = "gpu"))]
#[unsafe(no_mangle)]
extern "system" fn Java_dev_waterui_android_ffi_WatcherJni_gpuSurfaceDrop<'local>(
_env: EnvUnowned<'local>,
_class: JClass<'local>,
state_ptr: jlong,
) {
let state_ptr = state_ptr as *mut JniGpuSurfaceState;
let wrapper = unsafe { Box::from_raw(state_ptr) };
unsafe {
crate::components::gpu_surface::waterui_gpu_surface_drop(wrapper.state);
}
drop(wrapper);
}