rosace_core/platform.rs
1//! Global platform provider: `use_platform()` and `set_platform()` (D105).
2//!
3//! Widgets never branch on platform directly — the running platform exists
4//! only to drive THEME resolution (a platform-keyed `Themes` bundle picks
5//! the active `ThemeData` once at startup). This mirrors the `safe_area`
6//! provider's shape exactly: a detected default, overridable, read through a
7//! `GlobalAtom` so it's cheap and consistent everywhere.
8
9use rosace_state::GlobalAtom;
10use rosace_trace::event::AtomId;
11
12/// Reserved atom ID for the platform atom (must not collide with other
13/// reserved IDs — see `rosace_theme::provider::THEME_ATOM_ID` at 0xFFFF and
14/// `safe_area::SAFE_AREA_ATOM_ID` at 0xFFFE).
15const PLATFORM_ATOM_ID: AtomId = AtomId(0xFFFD);
16
17/// The platform ROSACE is running on.
18///
19/// Deliberately flat (no separate "Desktop" catch-all alongside `MacOs`/
20/// `Windows`/`Linux`) — the AppBar proof (Phase 23 Step 3) needs to tell
21/// macOS apart from other desktop OSes (traffic-light inset), so folding
22/// them into one variant would immediately need un-folding.
23#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
24pub enum Platform {
25 MacOs,
26 Windows,
27 Linux,
28 Ios,
29 Android,
30 Web,
31}
32
33impl Platform {
34 /// Detects the platform at compile time — `cfg(target_arch = "wasm32")`
35 /// for web (sufficient on its own: nothing else compiles to wasm32 in
36 /// this codebase, so no runtime `navigator.platform` query is needed),
37 /// `cfg(target_os)` for every native target.
38 pub fn detect() -> Self {
39 #[cfg(target_arch = "wasm32")]
40 return Platform::Web;
41 #[cfg(target_os = "macos")]
42 return Platform::MacOs;
43 #[cfg(target_os = "windows")]
44 return Platform::Windows;
45 #[cfg(target_os = "linux")]
46 return Platform::Linux;
47 #[cfg(target_os = "ios")]
48 return Platform::Ios;
49 #[cfg(target_os = "android")]
50 return Platform::Android;
51 // Unreachable for every target this workspace actually builds for;
52 // kept so the function is total rather than panicking on a future
53 // target this list hasn't been taught about yet.
54 #[allow(unreachable_code)]
55 Platform::Linux
56 }
57
58 pub fn is_desktop(&self) -> bool {
59 matches!(self, Platform::MacOs | Platform::Windows | Platform::Linux)
60 }
61
62 pub fn is_mobile(&self) -> bool {
63 matches!(self, Platform::Ios | Platform::Android)
64 }
65}
66
67static CURRENT_PLATFORM: GlobalAtom<Platform> = GlobalAtom::new(PLATFORM_ATOM_ID, Platform::detect);
68
69/// Returns the currently active platform — the real detected one unless
70/// overridden via [`set_platform`] (e.g. `App::platform(Platform::Ios)` to
71/// preview an iOS theme on desktop).
72pub fn use_platform() -> Platform {
73 CURRENT_PLATFORM.get()
74}
75
76/// Overrides the active platform. Called by `App::platform(..)` at startup;
77/// app code should not normally need this directly.
78pub fn set_platform(p: Platform) {
79 CURRENT_PLATFORM.set(p);
80}