agg_gui/fullscreen.rs
1//! # Fullscreen toggle requests — app → platform shell
2//!
3//! Apps can't touch the winit window or the browser document, so a
4//! fullscreen toggle is a request: the app calls [`request_toggle`]
5//! (from a click or keydown handler — browsers demand a user gesture),
6//! and the platform shell polls [`take_request`] once per frame and
7//! performs the switch (winit `set_fullscreen` natively,
8//! `requestFullscreen`/`exitFullscreen` on the canvas in a browser).
9//! Shells report the resulting state through [`set_active`] so apps
10//! can render the right icon via [`is_active`].
11
12use std::sync::atomic::{AtomicBool, Ordering};
13
14static PENDING: AtomicBool = AtomicBool::new(false);
15static ACTIVE: AtomicBool = AtomicBool::new(false);
16
17/// Ask the platform shell to toggle fullscreen (e.g. from an Alt+Enter
18/// handler or a UI button). Coalesces: several requests in one frame
19/// still toggle once.
20pub fn request_toggle() {
21 PENDING.store(true, Ordering::Relaxed);
22}
23
24/// Shell side: consume a pending toggle request, if any.
25pub fn take_request() -> bool {
26 PENDING.swap(false, Ordering::Relaxed)
27}
28
29/// Shell side: record the actual state after switching (or after the
30/// user left fullscreen through the OS/browser, e.g. Esc).
31pub fn set_active(active: bool) {
32 ACTIVE.store(active, Ordering::Relaxed);
33}
34
35/// The last state a shell reported — drives expand/compress icons.
36pub fn is_active() -> bool {
37 ACTIVE.load(Ordering::Relaxed)
38}
39
40#[cfg(test)]
41mod tests {
42 use super::*;
43
44 #[test]
45 fn request_is_consumed_once_and_state_tracks() {
46 assert!(!take_request());
47 request_toggle();
48 request_toggle();
49 assert!(take_request(), "request should be pending");
50 assert!(!take_request(), "request must coalesce and clear");
51 set_active(true);
52 assert!(is_active());
53 set_active(false);
54 assert!(!is_active());
55 }
56}