diff --git a/Cargo.toml b/Cargo.toml index 1af7d4d23e..0e22b4d19a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,9 +37,9 @@ image = "0.23.12" simple_logger = "1.9" [target.'cfg(target_os = "android")'.dependencies] -ndk = "0.3" -ndk-sys = "0.2.0" -ndk-glue = "0.3" +ndk = { path = "../android-ndk-rs/ndk" } +ndk-sys = { path = "../android-ndk-rs/ndk-sys" } +ndk-glue = { path = "../android-ndk-rs/ndk-glue" } [target.'cfg(any(target_os = "ios", target_os = "macos"))'.dependencies] objc = "0.2.7" diff --git a/src/platform_impl/android/mod.rs b/src/platform_impl/android/mod.rs index 543bca1b03..72570766cc 100644 --- a/src/platform_impl/android/mod.rs +++ b/src/platform_impl/android/mod.rs @@ -10,11 +10,13 @@ use ndk::{ configuration::Configuration, event::{InputEvent, KeyAction, MotionAction}, looper::{ForeignLooper, Poll, ThreadLooper}, + native_window::NativeWindow, }; use ndk_glue::{Event, Rect}; +use raw_window_handle::RawWindowHandle; use std::{ collections::VecDeque, - sync::{Arc, Mutex, RwLock}, + sync::{Arc, Mutex, RwLock, RwLockReadGuard}, time::{Duration, Instant}, }; @@ -43,6 +45,12 @@ fn poll(poll: Poll) -> Option { pub struct EventLoop { window_target: event_loop::EventLoopWindowTarget, + /// This read guard will be held between each pair of + /// [`event::Event::Resumed`] and [`event::Event::Suspended`] events to + /// ensure that [`ndk_glue`] does not release the [`NativeWindow`] + /// prematurely, invalidating raw handles obtained by the user through + /// [`Window::raw_window_handle()`]. + native_window_lock: Option>>, user_queue: Arc>>, first_event: Option, start_cause: event::StartCause, @@ -65,10 +73,12 @@ impl EventLoop { Self { window_target: event_loop::EventLoopWindowTarget { p: EventLoopWindowTarget { + raw_window_handle: Default::default(), _marker: std::marker::PhantomData, }, _marker: std::marker::PhantomData, }, + native_window_lock: None, user_queue: Default::default(), first_event: None, start_cause: event::StartCause::Init, @@ -106,22 +116,36 @@ impl EventLoop { match self.first_event.take() { Some(EventSource::Callback) => match ndk_glue::poll_events().unwrap() { Event::WindowCreated => { - call_event_handler!( - event_handler, - self.window_target(), - control_flow, - event::Event::Resumed - ); + let native_window_lock = ndk_glue::native_window(); + // The window could have gone away before we got the message + if native_window_lock.is_some() { + self.window_target + .p + .update_native_window(native_window_lock.as_ref()); + self.native_window_lock = Some(native_window_lock); + call_event_handler!( + event_handler, + self.window_target(), + control_flow, + event::Event::Resumed + ); + } } Event::WindowResized => resized = true, Event::WindowRedrawNeeded => redraw = true, Event::WindowDestroyed => { - call_event_handler!( - event_handler, - self.window_target(), - control_flow, - event::Event::Suspended - ); + let native_window_lock = self.native_window_lock.take(); + // This event is ignored if no window was actually grabbed onto + // in `WindowCreated` above + if native_window_lock.is_some() { + call_event_handler!( + event_handler, + self.window_target(), + control_flow, + event::Event::Suspended + ); + } + self.window_target.p.update_native_window(None); } Event::Pause => self.running = false, Event::Resume => self.running = true, @@ -400,10 +424,19 @@ impl Clone for EventLoopProxy { } pub struct EventLoopWindowTarget { + raw_window_handle: Arc>>, _marker: std::marker::PhantomData, } impl EventLoopWindowTarget { + fn update_native_window(&self, native_window: Option<&NativeWindow>) { + *self.raw_window_handle.lock().unwrap() = native_window.map(|native_window| { + let mut handle = raw_window_handle::android::AndroidHandle::empty(); + handle.a_native_window = unsafe { native_window.ptr().as_mut() as *mut _ as *mut _ }; + RawWindowHandle::Android(handle) + }); + } + pub fn primary_monitor(&self) -> Option { Some(monitor::MonitorHandle { inner: MonitorHandle, @@ -438,16 +471,20 @@ impl DeviceId { #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] pub struct PlatformSpecificWindowBuilderAttributes; -pub struct Window; +pub struct Window { + raw_window_handle: Arc>>, +} impl Window { pub fn new( - _el: &EventLoopWindowTarget, + el: &EventLoopWindowTarget, _window_attrs: window::WindowAttributes, _: PlatformSpecificWindowBuilderAttributes, ) -> Result { // FIXME this ignores requested window attributes - Ok(Self) + Ok(Self { + raw_window_handle: Arc::clone(&el.raw_window_handle), + }) } pub fn id(&self) -> WindowId { @@ -563,14 +600,14 @@ impl Window { } pub fn raw_window_handle(&self) -> raw_window_handle::RawWindowHandle { - let a_native_window = if let Some(native_window) = ndk_glue::native_window().as_ref() { - unsafe { native_window.ptr().as_mut() as *mut _ as *mut _ } - } else { - 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."); - }; - let mut handle = raw_window_handle::android::AndroidHandle::empty(); - handle.a_native_window = a_native_window; - raw_window_handle::RawWindowHandle::Android(handle) + self.raw_window_handle + .lock() + .unwrap() + .as_ref() + .expect( + "The window can be obtained only between `Event::Resumed` and `Event::Suspended`!", + ) + .clone() } pub fn config(&self) -> Configuration {