Skip to main content

cranpose/
lib.rs

1#![deny(missing_docs)]
2
3//! High level utilities for running Cranpose applications with minimal boilerplate.
4
5#[cfg(all(feature = "android", target_os = "android"))]
6mod android_file_picker;
7#[cfg(any(
8    all(feature = "android", target_os = "android"),
9    all(feature = "ios", feature = "renderer-wgpu", target_os = "ios")
10))]
11mod chunked_read;
12mod scoped_weak_stack;
13#[cfg(all(feature = "android", target_os = "android"))]
14pub use android_file_picker::open_content_uri;
15#[cfg(any(
16    test,
17    all(feature = "desktop-shell", feature = "renderer-wgpu"),
18    all(feature = "android", feature = "renderer-wgpu", target_os = "android"),
19    all(feature = "ios", feature = "renderer-wgpu", target_os = "ios"),
20    all(feature = "web", feature = "renderer-wgpu", target_arch = "wasm32")
21))]
22mod accessibility;
23#[cfg(any(
24    test,
25    all(feature = "android", feature = "renderer-wgpu", target_os = "android")
26))]
27mod accessibility_publish_policy;
28#[cfg(all(feature = "android", feature = "renderer-wgpu", target_os = "android"))]
29mod android_accessibility;
30#[cfg(any(
31    test,
32    all(feature = "android", feature = "renderer-wgpu", target_os = "android")
33))]
34mod android_accessibility_wire;
35#[cfg(all(feature = "android", target_os = "android"))]
36mod android_app_info;
37#[cfg(all(feature = "android", feature = "renderer-wgpu", target_os = "android"))]
38mod android_app_update;
39#[cfg(all(feature = "android", target_os = "android"))]
40mod android_camera;
41#[cfg(all(feature = "android", feature = "renderer-wgpu", target_os = "android"))]
42mod android_display_timing;
43mod android_entry;
44#[cfg(all(feature = "android", target_os = "android"))]
45mod android_finish;
46#[cfg(all(feature = "android", target_os = "android"))]
47mod android_font_scale;
48#[cfg(all(feature = "android", target_os = "android"))]
49mod android_frame_rate;
50#[cfg(all(feature = "android", target_os = "android"))]
51mod android_frame_telemetry;
52#[cfg(any(test, all(feature = "android", target_os = "android")))]
53mod android_frame_work;
54#[cfg(any(
55    test,
56    all(feature = "android", feature = "renderer-wgpu", target_os = "android")
57))]
58mod android_haptics_queue;
59#[cfg(all(feature = "android", target_os = "android"))]
60mod android_host;
61#[cfg_attr(
62    not(all(feature = "android", target_os = "android")),
63    expect(dead_code)
64)]
65mod android_host_window;
66mod android_input;
67#[cfg(all(feature = "android", target_os = "android"))]
68mod android_jni;
69#[cfg(all(feature = "android", feature = "renderer-wgpu", target_os = "android"))]
70mod android_keyboard;
71#[cfg(any(test, all(feature = "android", target_os = "android")))]
72mod android_launch_args;
73#[cfg(all(feature = "android", feature = "media", target_os = "android"))]
74mod android_media;
75#[cfg(all(feature = "android", target_os = "android"))]
76mod android_overlay_window;
77#[cfg(any(test, all(feature = "android", target_os = "android")))]
78mod android_panic_hook;
79#[cfg(all(feature = "android", target_os = "android"))]
80mod android_perf_hint;
81#[cfg(any(
82    test,
83    all(feature = "android", feature = "renderer-wgpu", target_os = "android")
84))]
85mod android_poll;
86#[cfg(any(
87    test,
88    all(feature = "android", feature = "renderer-wgpu", target_os = "android")
89))]
90mod android_present_thread;
91#[cfg(any(
92    test,
93    all(feature = "android", feature = "playbilling", target_os = "android")
94))]
95mod android_purchase_wire;
96#[cfg(all(
97    feature = "android",
98    feature = "playbilling",
99    feature = "renderer-wgpu",
100    target_os = "android"
101))]
102mod android_purchases;
103#[cfg(all(feature = "android", feature = "renderer-wgpu", target_os = "android"))]
104mod android_services;
105#[cfg(all(feature = "android", feature = "renderer-wgpu", target_os = "android"))]
106mod android_surface;
107#[cfg(all(feature = "android", feature = "renderer-wgpu", target_os = "android"))]
108mod android_text_input;
109#[cfg(all(feature = "android", target_os = "android"))]
110mod android_vsync;
111#[cfg(any(test, all(feature = "android", target_os = "android")))]
112mod android_wire_escape;
113#[cfg(all(feature = "android", target_os = "android"))]
114mod android_writable_folder;
115mod app_launcher;
116#[cfg(feature = "preview")]
117pub mod preview;
118#[cfg(feature = "preview")]
119pub use cranpose_macros::preview;
120#[cfg(feature = "embed")]
121pub mod inspection;
122#[cfg(feature = "renderer-wgpu")]
123pub use cranpose_app_shell::inspector::{
124    InspectorAction, InspectorControl, InspectorMode, InspectorNode, InspectorState,
125};
126mod host_environment;
127#[cfg(all(feature = "ios", target_os = "ios"))]
128mod ios_host;
129mod native_window;
130mod window_local;
131#[cfg(all(
132    feature = "native-windows",
133    feature = "renderer-wgpu",
134    not(target_arch = "wasm32")
135))]
136mod window_node;
137/// The activity handle `NativeActivity` hands to the entry point. Re-exported so
138/// an application declares its entry point with [`android_main!`] and never
139/// depends on `android_activity` for a parameter type.
140#[cfg(all(feature = "android", target_os = "android"))]
141pub use android_activity::AndroidApp;
142#[cfg(all(feature = "android", feature = "renderer-wgpu", target_os = "android"))]
143pub use android_host_window::{
144    AndroidHostWindowPositionError, AndroidHostWindowSizeError, AndroidHostWindowSizeStatus,
145    AndroidHostWindowState, rememberAndroidHostWindowState,
146};
147#[cfg(all(
148    feature = "renderer-wgpu",
149    any(feature = "desktop-shell", all(feature = "ios", target_os = "ios"))
150))]
151pub use app_launcher::LaunchError;
152pub use app_launcher::{
153    AndroidGpuBackend, AndroidOverlayWindowOptions, AppFonts, AppLauncher, AppSettings,
154    CustomCursorSize, DefaultFont, LauncherFonts,
155};
156/// Font registration vocabulary named by [`AppLauncher`]'s font methods:
157/// the platform font directory [`AppLauncher::with_system_font_family`] wants,
158/// the weight set it registers, the registry and error
159/// [`AppLauncher::with_fonts_from`] hands out, and the bytes
160/// [`AppLauncher::with_font_face_bytes`] takes.
161pub use cranpose_render_common::font_source::{
162    ANDROID_SYSTEM_FONT_DIR, DEFAULT_SYSTEM_FAMILY_WEIGHTS, FontLoadError, SoftwareTextFontRegistry,
163};
164pub use cranpose_render_common::software_text_raster::FontBytes;
165pub use host_environment::{host_density, system_font_directory};
166pub use native_window::{
167    WindowConfig, WindowFocus, WindowModifierExt, WindowResizeDirection, WindowState,
168    rememberWindowState, rememberWindowStateAt,
169};
170pub use window_local::LocalWindowState;
171/// Brings in what this crate's build script declared.
172///
173/// Writes `pub const CAPABILITIES: cranpose::capabilities::Capabilities`, read
174/// from the file `cranpose::capabilities::Declaration::emit` wrote:
175///
176/// ```ignore
177/// cranpose::app_capabilities!();
178///
179/// cranpose::android_main! {
180///     launcher: cranpose::AppLauncher::new().with_capabilities(&CAPABILITIES),
181///     content: my_app::Root,
182/// }
183/// ```
184#[macro_export]
185macro_rules! app_capabilities {
186    () => {
187        #[doc(hidden)]
188        mod cranpose_declared_capabilities {
189            use $crate::capabilities as cranpose_capabilities;
190
191            include!(concat!(env!("OUT_DIR"), "/cranpose_capabilities.rs"));
192        }
193        pub use cranpose_declared_capabilities::CAPABILITIES;
194    };
195}
196
197macro_rules! renderer_wgpu_platform_modules {
198    ($($name:ident),+ $(,)?) => {
199        $(
200            #[cfg(all(
201                feature = "renderer-wgpu",
202                any(
203                    feature = "desktop-shell",
204                    all(feature = "android", target_os = "android"),
205                    all(feature = "ios", target_os = "ios"),
206                    all(feature = "web", target_arch = "wasm32")
207                )
208            ))]
209            mod $name;
210        )+
211    };
212}
213
214renderer_wgpu_platform_modules!(present_mode, surface_format, wgpu_surface);
215
216/// The real-time audio engine that backs `cranpose_services::audio`. Call
217/// [`install_audio`] once at startup; Android installs it automatically.
218#[cfg(feature = "audio")]
219pub use cranpose_audio::{AudioEngine, install as install_audio};
220/// What an application asks of a device: the services it uses and the
221/// hardware it cannot run without, declared in its build script with
222/// `cranpose::capabilities::declare` and read back with
223/// [`app_capabilities!`](crate::app_capabilities).
224pub use cranpose_capabilities as capabilities;
225/// Core runtime helpers commonly used by applications.
226pub use cranpose_core::{
227    CoroutineScope, DisposableEffect, DisposableEffectResult, DisposableEffectScope,
228    LaunchedEffect, LaunchedEffectAsync, LaunchedEffectScope, MovableContent, MutableState,
229    SnapshotStateList, SnapshotStateMap, State, delay, forget_movable, interval, key,
230    launchBlocking, movable, movableContentOf, mutableStateList, mutableStateListOf,
231    mutableStateMap, mutableStateMapOf, mutableStateOf, mutableStateOfNeverEqual, produceState,
232    remember, rememberCoroutineScope, rememberKeyed, rememberMovableContentOf,
233    rememberMutableStateOf, rememberMutableStateOfNeverEqual, rememberUpdatedState,
234};
235/// Liquid UI — the first-party glass component library
236/// (`use cranpose::liquid::prelude::*;`).
237pub use cranpose_liquid as liquid;
238/// The in-process media backend that backs `cranpose_services::media`.
239/// Installed automatically by the desktop shell and, wrapped in the platform
240/// media session, by the Android one; iOS and the web install their own
241/// platform backend instead. [`uri_for_path`] builds the `file:` URI a
242/// [`cranpose_services::MediaItem`] takes from a path.
243#[cfg(feature = "media")]
244pub use cranpose_media::{SoftwareMediaPlayer, path_from_uri, uri_for_path};
245/// Whether the host's current application theme is dark.
246pub use cranpose_services::isSystemInDarkTheme;
247/// Re-export framework services (HTTP, URI, etc.) from the dedicated services crate.
248pub use cranpose_services::*;
249/// Re-export the UI crate so applications can depend on a single crate.
250pub use cranpose_ui::*;
251
252static KEEP_SCREEN_ON_EFFECTS: std::sync::atomic::AtomicUsize =
253    std::sync::atomic::AtomicUsize::new(0);
254
255/// Keeps the platform display awake while this call remains in composition and
256/// `enabled` is true. Multiple active callers are reference-counted.
257#[expect(non_snake_case)]
258#[track_caller]
259pub fn KeepScreenOn(enabled: bool) {
260    cranpose_core::__disposable_effect_impl(
261        cranpose_core::caller_location_key()
262            ^ cranpose_core::location_key(file!(), line!(), column!()),
263        enabled,
264        move |scope| {
265            if !enabled {
266                return cranpose_core::DisposableEffectResult::default();
267            }
268            if KEEP_SCREEN_ON_EFFECTS.fetch_add(1, std::sync::atomic::Ordering::AcqRel) == 0 {
269                cranpose_services::set_keep_screen_on(true);
270            }
271            scope.on_dispose(move || {
272                if KEEP_SCREEN_ON_EFFECTS.fetch_sub(1, std::sync::atomic::Ordering::AcqRel) == 1 {
273                    cranpose_services::set_keep_screen_on(false);
274                }
275            })
276        },
277    );
278}
279
280/// Installs a declared bundled-asset set on a worker and returns the outcome
281/// on the UI runtime. Work is cancelled with the owning composition.
282#[cfg(not(target_arch = "wasm32"))]
283#[expect(non_snake_case)]
284#[track_caller]
285pub fn BundledAssetInstallEffect<K: PartialEq + 'static>(
286    keys: K,
287    spec: cranpose_services::BundledAssetInstallSpec,
288    on_result: impl FnOnce(
289        Result<cranpose_services::BundledAssetInstallOutcome, cranpose_services::BundledAssetError>,
290    ) + 'static,
291) {
292    cranpose_core::__launched_effect_impl(
293        cranpose_core::caller_location_key()
294            ^ cranpose_core::location_key(file!(), line!(), column!()),
295        keys,
296        move |scope| {
297            scope.launch_background(
298                move |_token| async move { cranpose_services::install_bundled_asset_set(&spec) },
299                on_result,
300            );
301        },
302    );
303}
304
305/// Registers a lifecycle observer for the lifetime of the current composition.
306///
307/// Screens that only need the current state read
308/// [`cranpose_services::local_lifecycle_state`] instead; this is for work that
309/// must react to a *transition*.
310#[expect(non_snake_case)]
311#[track_caller]
312pub fn LifecycleEffect<K: PartialEq + 'static>(
313    keys: K,
314    observer: impl FnMut(cranpose_services::LifecycleEvent) + 'static,
315) {
316    let transitions = cranpose_services::rememberLifecycleEvents();
317    cranpose_core::CollectEvents(transitions, keys, observer);
318}
319
320/// Remembers observable application update state for the current composition.
321#[expect(non_snake_case)]
322#[track_caller]
323pub fn rememberAppUpdateState() -> cranpose_core::State<cranpose_services::AppUpdateStatus> {
324    let updates = cranpose_core::rememberEventStream((), |sender| {
325        cranpose_services::observe_app_update_status(move |status| sender.send(status))
326    });
327    cranpose_core::collectAsState(updates, (), cranpose_services::app_update_status())
328}
329
330/// Runs `on_frame` on every animation frame while `running` is true.
331///
332/// This is the framework-owned animation loop for games and other custom-drawn
333/// content. `running` is ordinary observable state the caller derives — a
334/// simulation flag, "the window is visible", "a gesture is in progress" — and
335/// the loop starts and stops with it. There is no wake handle to hold and no
336/// scheduler to poke: stopping is a state change like any other.
337#[expect(non_snake_case)]
338#[track_caller]
339pub fn FrameEffect<K: PartialEq + 'static>(
340    keys: K,
341    running: bool,
342    on_frame: impl FnMut(u64) + 'static,
343) {
344    let on_frame: std::rc::Rc<std::cell::RefCell<dyn FnMut(u64)>> =
345        std::rc::Rc::new(std::cell::RefCell::new(on_frame));
346    let on_frame = cranpose_core::rememberUpdatedState(on_frame);
347    cranpose_core::__launched_effect_async_impl(
348        cranpose_core::caller_location_key()
349            ^ cranpose_core::location_key(file!(), line!(), column!()),
350        std::panic::Location::caller().into(),
351        (keys, running),
352        move |scope| {
353            Box::pin(async move {
354                if !running {
355                    return;
356                }
357                let clock = scope.runtime().frame_clock();
358                while scope.is_active() {
359                    let now = clock.next_frame().await;
360                    if !scope.is_active() {
361                        break;
362                    }
363                    (on_frame.value().borrow_mut())(now);
364                }
365            })
366        },
367    );
368}
369
370#[doc(hidden)]
371pub use cranpose_core::{
372    __branch_group_scope_deferred, __source_scope, CallbackHolder, Composer, Key, ParamState,
373    ReturnSlot, ValueSlotHandle, branch_location_key, cached_branch_location_key,
374    cached_composable_definition_key, caller_location_key, composable_definition_key,
375    composable_identity_key, debug_label_current_scope, location_key, with_current_composer,
376};
377
378#[cfg(all(
379    feature = "desktop-shell",
380    feature = "robot",
381    feature = "renderer-wgpu"
382))]
383#[doc(hidden)]
384pub type RobotAppHook = dyn FnMut(String, String) -> Result<Option<String>, String>;
385
386/// Guides for using Cranpose, compiled with the crate so they cannot drift from
387/// it. Built only for documentation, so they cost a reader nothing at runtime.
388#[cfg(doc)]
389pub mod _docs;
390
391/// Convenience imports for Cranpose applications.
392pub mod prelude {
393    pub use cranpose_core::{
394        CoroutineScope, DisposableEffect, DisposableEffectResult, DisposableEffectScope,
395        LaunchedEffect, LaunchedEffectAsync, LaunchedEffectScope, MovableContent, MutableState,
396        SnapshotStateList, SnapshotStateMap, State, delay, forget_movable, interval, key,
397        launchBlocking, movable, movableContentOf, mutableStateList, mutableStateListOf,
398        mutableStateMap, mutableStateMapOf, mutableStateOf, mutableStateOfNeverEqual, produceState,
399        remember, rememberCoroutineScope, rememberKeyed, rememberMovableContentOf,
400        rememberMutableStateOf, rememberMutableStateOfNeverEqual, rememberUpdatedState,
401    };
402    pub use cranpose_services::*;
403    pub use cranpose_ui::*;
404
405    #[cfg(all(feature = "android", feature = "renderer-wgpu", target_os = "android"))]
406    pub use crate::{
407        AndroidHostWindowPositionError, AndroidHostWindowSizeError, AndroidHostWindowSizeStatus,
408        AndroidHostWindowState, rememberAndroidHostWindowState,
409    };
410    pub use crate::{
411        AndroidOverlayWindowOptions, AppFonts, AppLauncher, AppSettings, WindowConfig,
412        WindowModifierExt, WindowResizeDirection, WindowState, rememberWindowState,
413        rememberWindowStateAt,
414    };
415}
416
417#[cfg(any(
418    all(feature = "desktop-shell", feature = "renderer-wgpu"),
419    all(feature = "android", feature = "renderer-wgpu", target_os = "android"),
420    all(feature = "ios", feature = "renderer-wgpu", target_os = "ios"),
421    all(feature = "web", feature = "renderer-wgpu", target_arch = "wasm32"),
422    feature = "embed"
423))]
424pub(crate) mod platform_env;
425
426#[cfg(any(
427    all(feature = "desktop-shell", feature = "renderer-wgpu"),
428    all(feature = "android", target_os = "android"),
429    all(feature = "ios", target_os = "ios"),
430    feature = "embed"
431))]
432mod pipeline_cache_file;
433
434#[cfg(any(
435    all(feature = "desktop-shell", feature = "renderer-wgpu"),
436    feature = "embed"
437))]
438mod application_id;
439
440#[cfg(feature = "embed")]
441pub mod embed;
442#[cfg(feature = "embed")]
443mod embed_frame;
444#[cfg(feature = "embed")]
445mod embed_input;
446#[cfg(feature = "embed")]
447mod embed_overlay;
448#[cfg(feature = "embed")]
449mod embed_protocol;
450#[cfg(feature = "embed")]
451mod embed_surfaces;
452
453#[cfg(all(feature = "android", feature = "renderer-wgpu", target_os = "android"))]
454pub mod android;
455#[cfg(any(
456    test,
457    all(feature = "android", feature = "renderer-wgpu", target_os = "android")
458))]
459mod frame_lead;
460#[cfg(any(
461    test,
462    all(feature = "android", feature = "renderer-wgpu", target_os = "android")
463))]
464mod frame_pacer;
465#[cfg(feature = "renderer-wgpu")]
466#[cfg_attr(
467    not(any(
468        all(feature = "android", target_os = "android"),
469        all(feature = "ios", target_os = "ios")
470    )),
471    allow(dead_code)
472)]
473pub(crate) mod gpu_limits;
474#[cfg(any(test, all(feature = "android", target_os = "android")))]
475mod vsync_period;
476
477#[cfg(all(feature = "desktop-shell", feature = "renderer-wgpu"))]
478mod cursor_scale;
479#[cfg(all(feature = "desktop-shell", feature = "renderer-wgpu"))]
480pub mod desktop;
481#[cfg(all(feature = "desktop-shell", feature = "renderer-wgpu"))]
482mod desktop_accessibility;
483#[cfg(all(feature = "desktop-shell", feature = "renderer-wgpu"))]
484mod desktop_accessibility_options;
485#[cfg(all(feature = "desktop-shell", feature = "renderer-wgpu"))]
486mod desktop_bundled_assets;
487#[cfg(all(feature = "desktop-shell", feature = "renderer-wgpu"))]
488mod desktop_cursor;
489#[cfg(all(feature = "desktop-shell", feature = "renderer-wgpu"))]
490mod desktop_host_surface;
491#[cfg(all(feature = "desktop-shell", feature = "renderer-wgpu"))]
492mod desktop_incoming;
493#[cfg(all(feature = "desktop-shell", feature = "renderer-wgpu"))]
494mod desktop_input;
495#[cfg(all(feature = "desktop-shell", feature = "renderer-wgpu"))]
496mod desktop_lifecycle;
497#[cfg(all(feature = "desktop-shell", feature = "renderer-wgpu"))]
498mod desktop_power;
499#[cfg(any(
500    all(feature = "desktop-shell", feature = "renderer-wgpu"),
501    all(feature = "web", feature = "renderer-wgpu", target_arch = "wasm32")
502))]
503mod host_surface_resize;
504#[cfg(all(
505    feature = "desktop-shell",
506    feature = "renderer-wgpu",
507    target_os = "macos"
508))]
509mod macos_cursor;
510#[cfg(all(
511    feature = "desktop-shell",
512    feature = "renderer-wgpu",
513    target_os = "windows"
514))]
515mod windows_cursor;
516
517#[cfg(all(
518    unix,
519    feature = "renderer-wgpu",
520    any(
521        feature = "desktop-shell",
522        all(feature = "android", target_os = "android"),
523        all(feature = "ios", target_os = "ios")
524    )
525))]
526mod process_info;
527
528#[cfg(any(
529    all(feature = "desktop-shell", feature = "renderer-wgpu"),
530    all(feature = "ios", feature = "renderer-wgpu", target_os = "ios")
531))]
532mod winit_pointer;
533
534#[cfg(any(
535    all(feature = "desktop-shell", feature = "renderer-wgpu"),
536    all(feature = "ios", feature = "renderer-wgpu", target_os = "ios")
537))]
538#[cfg_attr(not(all(feature = "ios", target_os = "ios")), expect(dead_code))]
539mod winit_touch;
540
541#[cfg(all(feature = "desktop-shell", feature = "renderer-wgpu"))]
542mod winit_wheel;
543
544#[cfg(all(
545    feature = "robot",
546    feature = "desktop-shell",
547    feature = "renderer-wgpu"
548))]
549mod robot;
550
551#[cfg(all(feature = "ios", feature = "renderer-wgpu", target_os = "ios"))]
552pub mod ios;
553
554#[cfg(all(feature = "ios", feature = "renderer-wgpu", target_os = "ios"))]
555mod ios_accessibility;
556
557#[cfg(all(feature = "ios", feature = "renderer-wgpu", target_os = "ios"))]
558mod ios_pick_future;
559
560#[cfg(all(feature = "ios", feature = "renderer-wgpu", target_os = "ios"))]
561mod ios_file_picker;
562
563#[cfg(all(feature = "ios", feature = "renderer-wgpu", target_os = "ios"))]
564mod ios_uri_handler;
565
566#[cfg(all(feature = "ios", feature = "renderer-wgpu", target_os = "ios"))]
567mod ios_clipboard;
568
569#[cfg(all(feature = "ios", feature = "renderer-wgpu", target_os = "ios"))]
570mod ios_share_sheet;
571
572#[cfg(all(feature = "ios", feature = "renderer-wgpu", target_os = "ios"))]
573mod ios_image_picker;
574
575#[cfg(all(feature = "ios", feature = "renderer-wgpu", target_os = "ios"))]
576mod ios_notifier;
577
578#[cfg(all(feature = "ios", feature = "renderer-wgpu", target_os = "ios"))]
579mod ios_haptics;
580
581#[cfg(all(feature = "ios", feature = "renderer-wgpu", target_os = "ios"))]
582mod ios_media;
583
584#[cfg(all(feature = "ios", feature = "renderer-wgpu", target_os = "ios"))]
585mod ios_app_info;
586
587#[cfg(all(feature = "ios", feature = "renderer-wgpu", target_os = "ios"))]
588mod ios_device_info;
589
590#[cfg(any(
591    all(feature = "ios", feature = "renderer-wgpu", target_os = "ios"),
592    all(
593        feature = "desktop-shell",
594        feature = "renderer-wgpu",
595        target_os = "macos"
596    )
597))]
598mod apple_thermal;
599
600#[cfg(all(feature = "ios", feature = "renderer-wgpu", target_os = "ios"))]
601mod ios_writable_folder;
602
603#[cfg(any(
604    all(feature = "ios", feature = "renderer-wgpu", target_os = "ios"),
605    all(
606        feature = "camera-desktop",
607        feature = "desktop-shell",
608        feature = "renderer-wgpu",
609        target_os = "macos"
610    )
611))]
612mod apple_camera;
613
614#[cfg(all(feature = "ios", feature = "renderer-wgpu", target_os = "ios"))]
615mod ios_keyboard;
616
617#[cfg(all(feature = "ios", feature = "renderer-wgpu", target_os = "ios"))]
618mod ios_back_gesture;
619
620#[cfg(all(feature = "ios", feature = "renderer-wgpu", target_os = "ios"))]
621mod ios_scene;
622
623#[cfg(all(feature = "ios", feature = "renderer-wgpu", target_os = "ios"))]
624mod ios_background;
625
626#[cfg(all(feature = "ios", feature = "renderer-wgpu", target_os = "ios"))]
627mod ios_bundled_assets;
628
629#[cfg(all(feature = "desktop-shell", feature = "renderer-wgpu"))]
630pub mod recorder;
631
632#[cfg(all(feature = "web", feature = "renderer-wgpu", target_arch = "wasm32"))]
633pub mod web;
634
635#[cfg(any(
636    all(feature = "web", feature = "renderer-wgpu", target_arch = "wasm32"),
637    test
638))]
639mod web_canvas_layout;
640
641#[cfg(any(
642    all(feature = "web", feature = "renderer-wgpu", target_arch = "wasm32"),
643    test
644))]
645mod web_surface_scale;
646
647#[cfg(any(
648    all(feature = "web", feature = "renderer-wgpu", target_arch = "wasm32"),
649    test
650))]
651mod web_floating_window;
652
653#[cfg(any(
654    all(feature = "web", feature = "renderer-wgpu", target_arch = "wasm32"),
655    test
656))]
657mod web_wheel;
658
659#[cfg(all(feature = "web", feature = "renderer-wgpu", target_arch = "wasm32"))]
660mod web_accessibility;
661#[cfg(all(feature = "web", feature = "renderer-wgpu", target_arch = "wasm32"))]
662mod web_accessibility_options;
663#[cfg(all(feature = "web", feature = "renderer-wgpu", target_arch = "wasm32"))]
664mod web_lifecycle;
665
666#[cfg(all(feature = "web", feature = "renderer-wgpu", target_arch = "wasm32"))]
667mod web_clipboard;
668
669#[cfg(all(feature = "web", feature = "renderer-wgpu", target_arch = "wasm32"))]
670mod web_cursor;
671
672#[cfg(all(feature = "web", feature = "renderer-wgpu", target_arch = "wasm32"))]
673mod web_canvas_watch;
674#[cfg(all(feature = "web", feature = "renderer-wgpu", target_arch = "wasm32"))]
675mod web_frame_host;
676#[cfg(all(feature = "web", feature = "renderer-wgpu", target_arch = "wasm32"))]
677mod web_host_surface;
678
679#[cfg(all(feature = "web", feature = "renderer-wgpu", target_arch = "wasm32"))]
680mod web_media;
681
682#[cfg(all(feature = "web", feature = "renderer-wgpu", target_arch = "wasm32"))]
683mod web_services;
684
685#[cfg(all(feature = "web", feature = "renderer-wgpu", target_arch = "wasm32"))]
686mod web_power;
687
688#[cfg(all(feature = "web", feature = "renderer-wgpu", target_arch = "wasm32"))]
689mod web_drop;
690
691/// Development frame pacing and FPS statistics types.
692#[cfg(all(feature = "desktop-shell", feature = "renderer-wgpu"))]
693pub use cranpose_app_shell::{DevOptions, FpsStats, FramePacingMode};
694#[cfg(all(
695    feature = "desktop-shell",
696    feature = "robot",
697    feature = "renderer-wgpu"
698))]
699pub use cranpose_render_wgpu::{pipelines_created, pipelines_created_off_frame};
700#[cfg(all(
701    feature = "desktop-shell",
702    feature = "robot",
703    feature = "renderer-wgpu"
704))]
705pub use robot::{
706    Robot, RobotPresentationInfo, RobotScreenshot, RobotTimelineAction, RobotTimelineStep,
707    SemanticElement, SemanticRect,
708};
709
710#[cfg(all(test, feature = "desktop-shell", feature = "renderer-wgpu"))]
711pub(crate) fn test_scratch_dir(tag: &str) -> std::path::PathBuf {
712    cranpose_core::test_scratch_dir(env!("CARGO_MANIFEST_DIR"), tag)
713}