use crate::platform::lifecycle;
use crate::platform::types::{MobileLifecycleState, ScreenOrientation};
use std::sync::OnceLock;
static IME_BRIDGE: OnceLock<crate::platform::ime_macos::MacOsImeBridge> = OnceLock::new();
pub(crate) fn ime_bridge() -> &'static crate::platform::ime_macos::MacOsImeBridge {
IME_BRIDGE.get_or_init(crate::platform::ime_macos::MacOsImeBridge::new)
}
#[no_mangle]
#[allow(clippy::not_unsafe_ptr_arg_deref)]
pub extern "C" fn rw_ios_ime_marked_text(text: *const core::ffi::c_char, cursor_utf16: i32) -> i32 {
if text.is_null() {
return 0;
}
let value = unsafe { core::ffi::CStr::from_ptr(text) };
let Ok(value) = value.to_str() else {
return 0;
};
ime_bridge().set_marked_text(value, cursor_utf16, cursor_utf16);
1
}
#[no_mangle]
#[allow(clippy::not_unsafe_ptr_arg_deref)]
pub extern "C" fn rw_ios_ime_commit(text: *const core::ffi::c_char) -> i32 {
if text.is_null() {
return 0;
}
let value = unsafe { core::ffi::CStr::from_ptr(text) };
let Ok(value) = value.to_str() else {
return 0;
};
ime_bridge().commit_text(value);
1
}
#[no_mangle]
pub extern "C" fn rw_ios_set_lifecycle(state: i32) -> i32 {
let phase = match state {
0 => MobileLifecycleState::Active,
1 => MobileLifecycleState::Inactive,
2 => MobileLifecycleState::Background,
_ => return 0,
};
i32::from(lifecycle::set_state(phase))
}
#[no_mangle]
pub extern "C" fn rw_ios_set_orientation(orientation: i32) -> i32 {
let value = match orientation {
1 => ScreenOrientation::Portrait,
2 => ScreenOrientation::PortraitUpsideDown,
3 => ScreenOrientation::LandscapeLeft,
4 => ScreenOrientation::LandscapeRight,
_ => return 0,
};
i32::from(lifecycle::set_orientation(value))
}
#[no_mangle]
pub extern "C" fn rw_ios_set_active(active: i32) -> i32 {
rw_ios_set_lifecycle(if active != 0 { 0 } else { 2 })
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn an_unknown_lifecycle_code_is_refused() {
let _guard = crate::compat::lock(&crate::platform::lifecycle::TEST_LOCK);
assert_eq!(rw_ios_set_lifecycle(9), 0);
}
#[test]
fn each_known_lifecycle_code_is_accepted() {
let _guard = crate::compat::lock(&crate::platform::lifecycle::TEST_LOCK);
assert_eq!(rw_ios_set_lifecycle(0), 1);
assert_eq!(lifecycle::lifecycle_state(), Some(MobileLifecycleState::Active));
assert_eq!(rw_ios_set_lifecycle(1), 1);
assert_eq!(lifecycle::lifecycle_state(), Some(MobileLifecycleState::Inactive));
assert_eq!(rw_ios_set_lifecycle(2), 1);
assert_eq!(lifecycle::lifecycle_state(), Some(MobileLifecycleState::Background));
let _ = rw_ios_set_lifecycle(0);
}
#[test]
fn the_four_uikit_orientations_map_to_the_four_screen_orientations() {
let _guard = crate::compat::lock(&crate::platform::lifecycle::TEST_LOCK);
assert_eq!(rw_ios_set_orientation(1), 1);
assert_eq!(lifecycle::orientation(), Some(ScreenOrientation::Portrait));
assert_eq!(rw_ios_set_orientation(2), 1);
assert_eq!(lifecycle::orientation(), Some(ScreenOrientation::PortraitUpsideDown));
assert_eq!(rw_ios_set_orientation(3), 1);
assert_eq!(lifecycle::orientation(), Some(ScreenOrientation::LandscapeLeft));
assert_eq!(rw_ios_set_orientation(4), 1);
assert_eq!(lifecycle::orientation(), Some(ScreenOrientation::LandscapeRight));
assert_eq!(rw_ios_set_orientation(5), 0);
}
#[test]
fn the_ime_hooks_accept_utf8_and_refuse_null() {
let _guard = crate::compat::lock(&crate::platform::lifecycle::TEST_LOCK);
let text = std::ffi::CString::new("にほ").expect("no interior nul");
assert_eq!(rw_ios_ime_marked_text(text.as_ptr(), 0), 1);
assert!(ime_bridge().has_marked_text());
assert_eq!(rw_ios_ime_commit(text.as_ptr()), 1);
assert!(!ime_bridge().has_marked_text(), "a commit clears the composition");
assert_eq!(rw_ios_ime_marked_text(std::ptr::null(), 0), 0);
assert_eq!(rw_ios_ime_commit(std::ptr::null()), 0);
}
}