diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 9dd79de0..6b2e3e9f 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,6 +1,5 @@ # Android -/src/platform/android.rs @MarijnS95 -/src/platform_impl/android @MarijnS95 +/winit-android @MarijnS95 # Apple (AppKit + UIKit) /src/platform/ios.rs @madsmtm diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f0190bf5..819666cf 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -182,6 +182,10 @@ jobs: - name: Test winit core run: cargo test -p winit-core + - name: Test winit Android + if: contains(matrix.platform.target, 'android') + run: cargo $CMD test -p winit-android --target=${{ matrix.platform.target }} --features native-activity --no-run + - name: Test winit Orbital if: contains(matrix.platform.target, 'redox') run: cargo test -p winit-orbital diff --git a/Cargo.toml b/Cargo.toml index 458cf415..7d7a2457 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [workspace] -members = ["dpi", "winit-core", "winit-orbital"] +members = ["dpi", "winit-*"] resolver = "2" [workspace.package] @@ -12,6 +12,7 @@ rust-version = "1.80" # Workspace dependencies. # `winit` has no version here to allow using it in dev deps for docs. winit = { path = "." } +winit-android = { version = "0.0.0", path = "winit-android" } winit-core = { version = "0.0.0", path = "winit-core" } winit-orbital = { version = "0.0.0", path = "winit-orbital" } @@ -149,9 +150,10 @@ targets = [ # Features are documented in either `lib.rs` or under `winit::platform`. [features] -android-game-activity = ["android-activity/game-activity"] -android-native-activity = ["android-activity/native-activity"] default = ["x11", "wayland", "wayland-dlopen", "wayland-csd-adwaita"] + +android-game-activity = ["winit-android/game-activity"] +android-native-activity = ["winit-android/native-activity"] mint = ["dpi/mint"] serde = [ "dep:serde", @@ -197,10 +199,8 @@ tracing-subscriber = { workspace = true, features = ["env-filter"] } [target.'cfg(not(target_os = "android"))'.dev-dependencies] softbuffer.workspace = true -# Android [target.'cfg(target_os = "android")'.dependencies] -android-activity.workspace = true -ndk.workspace = true +winit-android.workspace = true # AppKit or UIKit [target.'cfg(target_vendor = "apple")'.dependencies] diff --git a/src/event_loop.rs b/src/event_loop.rs index f0044059..c76565a5 100644 --- a/src/event_loop.rs +++ b/src/event_loop.rs @@ -328,6 +328,26 @@ impl winit_core::event_loop::run_on_demand::EventLoopExtRunOnDemand for EventLoo } } +#[cfg(android_platform)] +impl winit_android::EventLoopExtAndroid for EventLoop { + fn android_app(&self) -> &winit_android::activity::AndroidApp { + &self.event_loop.android_app + } +} + +#[cfg(android_platform)] +impl winit_android::EventLoopBuilderExtAndroid for EventLoopBuilder { + fn with_android_app(&mut self, app: winit_android::activity::AndroidApp) -> &mut Self { + self.platform_specific.android_app = Some(app); + self + } + + fn handle_volume_keys(&mut self) -> &mut Self { + self.platform_specific.ignore_volume_keys = false; + self + } +} + /// ```compile_error /// use winit::event_loop::run_on_demand::EventLoopExtRunOnDemand; /// use winit::event_loop::EventLoop; diff --git a/src/platform/mod.rs b/src/platform/mod.rs index f2f5159e..26456d81 100644 --- a/src/platform/mod.rs +++ b/src/platform/mod.rs @@ -3,7 +3,7 @@ //! Only the modules corresponding to the platform you're compiling to will be available. #[cfg(android_platform)] -pub mod android; +pub use winit_android as android; #[cfg(ios_platform)] pub mod ios; #[cfg(macos_platform)] diff --git a/src/platform_impl/mod.rs b/src/platform_impl/mod.rs index 1242021e..45339da9 100644 --- a/src/platform_impl/mod.rs +++ b/src/platform_impl/mod.rs @@ -1,5 +1,5 @@ #[cfg(android_platform)] -mod android; +pub(crate) use winit_android as platform; #[cfg(target_vendor = "apple")] mod apple; #[cfg(any(x11_platform, wayland_platform))] @@ -11,8 +11,6 @@ mod web; #[cfg(windows_platform)] mod windows; -#[cfg(android_platform)] -use self::android as platform; #[cfg(target_vendor = "apple")] use self::apple as platform; #[cfg(any(x11_platform, wayland_platform))] diff --git a/winit-android/Cargo.toml b/winit-android/Cargo.toml new file mode 100644 index 00000000..d1b50f6a --- /dev/null +++ b/winit-android/Cargo.toml @@ -0,0 +1,35 @@ +[package] +description = "Winit's Android backend" +documentation = "https://docs.rs/winit-android" +edition.workspace = true +license.workspace = true +name = "winit-android" +repository.workspace = true +rust-version.workspace = true +version = "0.0.0" + +[features] +game-activity = ["android-activity/game-activity"] +native-activity = ["android-activity/native-activity"] +serde = ["dep:serde", "bitflags/serde", "smol_str/serde", "dpi/serde", "winit-core/serde"] + +[dependencies] +bitflags.workspace = true +dpi.workspace = true +rwh_06.workspace = true +serde = { workspace = true, optional = true } +smol_str.workspace = true +tracing.workspace = true +winit-core.workspace = true + +# Platform-specific +[target.'cfg(target_os = "android")'.dependencies] +android-activity.workspace = true +ndk.workspace = true + +[dev-dependencies] +winit.workspace = true + +[package.metadata.docs.rs] +features = ["serde", "native-activity"] +targets = ["aarch64-linux-android"] diff --git a/winit-android/README.md b/winit-android/README.md new file mode 120000 index 00000000..32d46ee8 --- /dev/null +++ b/winit-android/README.md @@ -0,0 +1 @@ +../README.md \ No newline at end of file diff --git a/src/platform_impl/android/mod.rs b/winit-android/src/event_loop.rs similarity index 97% rename from src/platform_impl/android/mod.rs rename to winit-android/src/event_loop.rs index bfc8d409..45c1654d 100644 --- a/src/platform_impl/android/mod.rs +++ b/winit-android/src/event_loop.rs @@ -1,4 +1,5 @@ use std::cell::Cell; +use std::fmt; use std::hash::Hash; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex}; @@ -26,7 +27,7 @@ use winit_core::window::{ WindowAttributes, WindowButtons, WindowId, WindowLevel, }; -mod keycodes; +use crate::keycodes; static HAS_FOCUS: AtomicBool = AtomicBool::new(true); @@ -42,7 +43,7 @@ struct SharedFlagSetter { flag: Arc, } impl SharedFlagSetter { - pub fn set(&self) -> bool { + fn set(&self) -> bool { self.flag.compare_exchange(false, true, Ordering::AcqRel, Ordering::Relaxed).is_ok() } } @@ -57,21 +58,21 @@ struct SharedFlag { // we just need to know at the start of a main loop iteration if a redraw // was queued and be able to read and clear the state atomically) impl SharedFlag { - pub fn new() -> Self { + fn new() -> Self { Self { flag: Arc::new(AtomicBool::new(false)) } } - pub fn setter(&self) -> SharedFlagSetter { + fn setter(&self) -> SharedFlagSetter { SharedFlagSetter { flag: self.flag.clone() } } - pub fn get_and_reset(&self) -> bool { + fn get_and_reset(&self) -> bool { self.flag.swap(false, std::sync::atomic::Ordering::AcqRel) } } #[derive(Clone)] -pub struct RedrawRequester { +struct RedrawRequester { flag: SharedFlagSetter, waker: AndroidAppWaker, } @@ -87,7 +88,7 @@ impl RedrawRequester { RedrawRequester { flag: flag.setter(), waker } } - pub fn request_redraw(&self) { + fn request_redraw(&self) { if self.flag.set() { // Only explicitly try to wake up the main loop when the flag // value changes @@ -98,7 +99,7 @@ impl RedrawRequester { #[derive(Debug)] pub struct EventLoop { - pub(crate) android_app: AndroidApp, + pub android_app: AndroidApp, window_target: ActiveEventLoop, redraw_flag: SharedFlag, loop_running: bool, // Dispatched `NewEvents` @@ -111,9 +112,9 @@ pub struct EventLoop { } #[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub(crate) struct PlatformSpecificEventLoopAttributes { - pub(crate) android_app: Option, - pub(crate) ignore_volume_keys: bool, +pub struct PlatformSpecificEventLoopAttributes { + pub android_app: Option, + pub ignore_volume_keys: bool, } impl Default for PlatformSpecificEventLoopAttributes { @@ -126,9 +127,7 @@ impl Default for PlatformSpecificEventLoopAttributes { const GLOBAL_WINDOW: WindowId = WindowId::from_raw(0); impl EventLoop { - pub(crate) fn new( - attributes: &PlatformSpecificEventLoopAttributes, - ) -> Result { + pub fn new(attributes: &PlatformSpecificEventLoopAttributes) -> Result { let android_app = attributes.android_app.as_ref().expect( "An `AndroidApp` as passed to android_main() is required to create an `EventLoop` on \ Android", @@ -158,7 +157,7 @@ impl EventLoop { }) } - pub(crate) fn window_target(&self) -> &dyn RootActiveEventLoop { + pub fn window_target(&self) -> &dyn RootActiveEventLoop { &self.window_target } @@ -751,7 +750,7 @@ impl rwh_06::HasDisplayHandle for OwnedDisplayHandle { pub struct PlatformSpecificWindowAttributes; #[derive(Debug)] -pub(crate) struct Window { +pub struct Window { app: AndroidApp, redraw_requester: RedrawRequester, } @@ -766,11 +765,11 @@ impl Window { Ok(Self { app: el.app.clone(), redraw_requester: el.redraw_requester.clone() }) } - pub fn config(&self) -> ConfigurationRef { + pub(crate) fn config(&self) -> ConfigurationRef { self.app.config() } - pub fn content_rect(&self) -> Rect { + pub(crate) fn content_rect(&self) -> Rect { self.app.content_rect() } @@ -999,16 +998,6 @@ impl CoreWindow for Window { } } -#[derive(Default, Clone, Debug)] -pub struct OsError; - -use std::fmt::{self, Display, Formatter}; -impl Display for OsError { - fn fmt(&self, fmt: &mut Formatter<'_>) -> Result<(), fmt::Error> { - write!(fmt, "Android OS Error") - } -} - fn screen_size(app: &AndroidApp) -> PhysicalSize { if let Some(native_window) = app.native_window() { PhysicalSize::new(native_window.width() as _, native_window.height() as _) diff --git a/src/platform_impl/android/keycodes.rs b/winit-android/src/keycodes.rs similarity index 100% rename from src/platform_impl/android/keycodes.rs rename to winit-android/src/keycodes.rs diff --git a/src/platform/android.rs b/winit-android/src/lib.rs similarity index 85% rename from src/platform/android.rs rename to winit-android/src/lib.rs index 75c7e24f..b339ebd1 100644 --- a/src/platform/android.rs +++ b/winit-android/src/lib.rs @@ -69,10 +69,19 @@ //! logging as above). //! 4. Pass a clone of the `AndroidApp` that your application receives to Winit when building your //! event loop (as shown above). +#![cfg(target_os = "android")] + +mod event_loop; +mod keycodes; + +use winit_core::event_loop::ActiveEventLoop as CoreActiveEventLoop; +use winit_core::window::Window as CoreWindow; use self::activity::{AndroidApp, ConfigurationRef, Rect}; -use crate::event_loop::{ActiveEventLoop, EventLoop, EventLoopBuilder}; -use crate::window::Window; +pub use crate::event_loop::{ + ActiveEventLoop, EventLoop, EventLoopProxy, PlatformSpecificEventLoopAttributes, + PlatformSpecificWindowAttributes, Window, +}; /// Additional methods on [`EventLoop`] that are specific to Android. pub trait EventLoopExtAndroid { @@ -80,12 +89,6 @@ pub trait EventLoopExtAndroid { fn android_app(&self) -> &AndroidApp; } -impl EventLoopExtAndroid for EventLoop { - fn android_app(&self) -> &AndroidApp { - &self.event_loop.android_app - } -} - /// Additional methods on [`ActiveEventLoop`] that are specific to Android. pub trait ActiveEventLoopExtAndroid { /// Get the [`AndroidApp`] which was used to create this event loop. @@ -99,21 +102,21 @@ pub trait WindowExtAndroid { fn config(&self) -> ConfigurationRef; } -impl WindowExtAndroid for dyn Window + '_ { +impl WindowExtAndroid for dyn CoreWindow + '_ { fn content_rect(&self) -> Rect { - let window = self.cast_ref::().unwrap(); + let window = self.cast_ref::().unwrap(); window.content_rect() } fn config(&self) -> ConfigurationRef { - let window = self.cast_ref::().unwrap(); + let window = self.cast_ref::().unwrap(); window.config() } } -impl ActiveEventLoopExtAndroid for dyn ActiveEventLoop + '_ { +impl ActiveEventLoopExtAndroid for dyn CoreActiveEventLoop + '_ { fn android_app(&self) -> &AndroidApp { - let event_loop = self.cast_ref::().unwrap(); + let event_loop = self.cast_ref::().unwrap(); &event_loop.app } } @@ -130,18 +133,6 @@ pub trait EventLoopBuilderExtAndroid { fn handle_volume_keys(&mut self) -> &mut Self; } -impl EventLoopBuilderExtAndroid for EventLoopBuilder { - fn with_android_app(&mut self, app: AndroidApp) -> &mut Self { - self.platform_specific.android_app = Some(app); - self - } - - fn handle_volume_keys(&mut self) -> &mut Self { - self.platform_specific.ignore_volume_keys = false; - self - } -} - /// Re-export of the `android_activity` API /// /// Winit re-exports the `android_activity` API for convenience so that most @@ -170,16 +161,5 @@ pub mod activity { // feature enabled, so we avoid inlining it so that they're forced to view // it on the crate's own docs.rs page. #[doc(no_inline)] - #[cfg(android_platform)] pub use android_activity::*; - - #[cfg(not(android_platform))] - #[doc(hidden)] - pub struct Rect; - #[cfg(not(android_platform))] - #[doc(hidden)] - pub struct ConfigurationRef; - #[cfg(not(android_platform))] - #[doc(hidden)] - pub struct AndroidApp; }