Skip to content

Commit

Permalink
WIP: attempt to run a slint example on Android
Browse files Browse the repository at this point in the history
I just wanted to know if it worked out of the box,  but it doesn't. We
get a panic:

Cannot get the native window, it's null and will always be null before Event::Resumed and after Event::Suspended. Make sure you only call this function between those events.
  • Loading branch information
ogoffart committed Apr 5, 2023
1 parent ab05168 commit 3801345
Show file tree
Hide file tree
Showing 8 changed files with 49 additions and 4 deletions.
3 changes: 3 additions & 0 deletions api/rs/slint/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,9 @@ pub use i_slint_core::sharedvector::SharedVector;
pub use i_slint_core::timers::{Timer, TimerMode};
pub use i_slint_core::{format, string::SharedString};

#[cfg(target_os = "android")]
pub use i_slint_backend_selector::android_init;

pub mod private_unstable_api;

/// Enters the main event loop. This is necessary in order to receive
Expand Down
9 changes: 8 additions & 1 deletion examples/printerdemo/rust/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ path = "main.rs"
name = "printerdemo"

[dependencies]
slint = { path = "../../../api/rs/slint" }
slint = { path = "../../../api/rs/slint", default-features=false, features=["std", "backend-winit", "renderer-winit-femtovg", "compat-1-0"] }

chrono = { version = "0.4", default-features = false, features = ["clock", "std"]}

Expand All @@ -34,3 +34,10 @@ slint-build = { path = "../../../api/rs/build" }
#wasm# wasm-bindgen = { version = "0.2" }
#wasm# web-sys = { version = "0.3", features=["console"] }
#wasm# console_error_panic_hook = "0.1.5"


[lib]
path = "main.rs"
crate-type = ["cdylib"]
[target.'cfg(target_os = "android")'.dependencies]
android-activity = "0.4"
7 changes: 7 additions & 0 deletions examples/printerdemo/rust/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,3 +101,10 @@ pub fn main() {

main_window.run().unwrap();
}

#[cfg(target_os = "android")]
#[no_mangle]
fn android_main(app: android_activity::AndroidApp) {
slint::android_init(app).unwrap();
main();
}
3 changes: 3 additions & 0 deletions internal/backends/selector/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,3 +92,6 @@ pub fn use_modules() {
#[cfg(feature = "i-slint-backend-winit")]
i_slint_backend_winit::use_modules();
}

#[cfg(all(feature = "i-slint-backend-winit", target_os = "android"))]
pub use i_slint_backend_winit::android_init;
2 changes: 1 addition & 1 deletion internal/backends/winit/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ once_cell = "1.5"
pin-weak = "1"
scoped-tls-hkt = "0.1"
# Pinned due to https://github.com/slint-ui/slint/issues/2424
winit = { version = "=0.28.3", default-features = false }
winit = { version = "=0.28.3", default-features = false, features = ["android-native-activity"] }
instant = "0.1"
raw-window-handle = { version = "0.5", features = ["alloc"] }
scopeguard = { version = "1.1.0", default-features = false }
Expand Down
18 changes: 16 additions & 2 deletions internal/backends/winit/event_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,23 @@ struct NotRunningEventLoop {
event_loop_proxy: winit::event_loop::EventLoopProxy<CustomEvent>,
}

#[cfg(target_os = "android")]
pub(crate) static ANDROID_APP: once_cell::sync::OnceCell<
winit::platform::android::activity::AndroidApp,
> = once_cell::sync::OnceCell::new();

impl NotRunningEventLoop {
fn new() -> Self {
let instance = winit::event_loop::EventLoopBuilder::with_user_event().build();
#[allow(unused_mut)]
let mut builder = winit::event_loop::EventLoopBuilder::with_user_event();
#[cfg(target_os = "android")]
{
use winit::platform::android::EventLoopBuilderExtAndroid;
builder.with_android_app(
ANDROID_APP.get().expect("android must be initialized with android_init").clone(),
);
}
let instance = builder.build();
let event_loop_proxy = instance.create_proxy();
let clipboard = RefCell::new(create_clipboard(&instance));
Self { clipboard, instance, event_loop_proxy }
Expand Down Expand Up @@ -468,7 +482,7 @@ pub fn run() -> Result<(), corelib::platform::PlatformError> {
use winit::event_loop::{ControlFlow, EventLoopWindowTarget};

let not_running_loop_instance = MAYBE_LOOP_INSTANCE.with(|loop_instance| {
loop_instance.borrow_mut().take().unwrap_or_else(NotRunningEventLoop::new)
loop_instance.borrow_mut().take().unwrap_or_else(|| NotRunningEventLoop::new())
});

let event_loop_proxy = not_running_loop_instance.event_loop_proxy;
Expand Down
7 changes: 7 additions & 0 deletions internal/backends/winit/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -200,3 +200,10 @@ impl i_slint_core::platform::Platform for Backend {
})
}
}

#[cfg(target_os = "android")]
pub fn android_init(
android_app: winit::platform::android::activity::AndroidApp,
) -> Result<(), String> {
event_loop::ANDROID_APP.set(android_app).map_err(|_| "android_init called twice".into())
}
4 changes: 4 additions & 0 deletions internal/renderers/femtovg/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,10 @@ impl Renderer for FemtoVGRenderer {
&self,
data: &'static [u8],
) -> Result<(), Box<dyn std::error::Error>> {
// FIXME: this panics
#[cfg(target_os = "android")]
return Ok(());

fonts::register_font_from_memory(data)
}

Expand Down

1 comment on commit 3801345

@tronical
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah yes, on Android we can use the window builder only when the activity is "alive", i.e. after we've received Resume and before Suspend. See the glutin examples for how this needs to look like: https://github.com/rust-windowing/glutin/blob/8e0960d7aa8c67ee709897001def551fc1d868bb/glutin_examples/src/lib.rs#L108 . We basically have to delay our show().

IMO the main() entry point is not very well suited for the activity model. We can fake it to a certain degree (as did Qt), but it's ugly (and gets really ugly on iOS).

Please sign in to comment.