cala/
window.rs

1//! Display graphics onto the screen, usually via a window.
2//!
3//! # Getting Started
4//! **TODO**
5
6use crate::graphics::Canvas;
7use crate::graphics::*;
8use pix::chan::Channel;
9use pix::el::Pixel;
10use std::{
11    future::Future,
12    pin::Pin,
13    sync::{
14        atomic::{AtomicU32, Ordering},
15        Arc, Condvar, Mutex,
16    },
17    task::{Context, Poll},
18};
19
20static BACKGROUND_RED: AtomicU32 = AtomicU32::new(0);
21static BACKGROUND_GREEN: AtomicU32 = AtomicU32::new(0);
22static BACKGROUND_BLUE: AtomicU32 = AtomicU32::new(0);
23
24struct FrameFuture;
25
26pub use window::input::input;
27
28impl Future for FrameFuture {
29    type Output = (std::time::Duration, f32, bool);
30
31    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
32        let internal = Internal::new_lazy();
33        let mut lock = internal.frame.lock().unwrap();
34        if let Some(secs) = lock.frame.take() {
35            Poll::Ready(secs)
36        } else {
37            lock.waker = Some(cx.waker().clone());
38            Poll::Pending
39        }
40    }
41}
42
43/// A Canvas to draw on.
44pub struct Frame {
45    // For when drop'd; to notify graphics thread
46    pair: Arc<(Mutex<bool>, Condvar)>,
47    // Delta time since previous frame
48    elapsed: std::time::Duration,
49    // Aspect ratio
50    aspect: f32,
51    // If resized
52    resized: bool,
53}
54
55impl Frame {
56    /// Wait for the GPU to request a new frame.
57    #[allow(clippy::useless_let_if_seq)] // Clippy doesn't understand
58    pub async fn new<P: pix::el::Pixel>(color: P) -> Frame
59    where
60        pix::chan::Ch32: From<<P as pix::el::Pixel>::Chan>,
61    {
62        let color: pix::rgb::SRgb32 = color.convert();
63        let red = color.one().to_f32();
64        let green = color.two().to_f32();
65        let blue = color.three().to_f32();
66        let red_u32 = u32::from_ne_bytes(red.to_ne_bytes());
67        let green_u32 = u32::from_ne_bytes(green.to_ne_bytes());
68        let blue_u32 = u32::from_ne_bytes(blue.to_ne_bytes());
69
70        let mut bg_changed = false;
71        if BACKGROUND_RED.load(Ordering::Relaxed) != red_u32 {
72            BACKGROUND_RED.store(red_u32, Ordering::Relaxed);
73            bg_changed = true;
74        }
75        if BACKGROUND_GREEN.load(Ordering::Relaxed) != blue_u32 {
76            BACKGROUND_GREEN.store(green_u32, Ordering::Relaxed);
77            bg_changed = true;
78        }
79        if BACKGROUND_BLUE.load(Ordering::Relaxed) != green_u32 {
80            BACKGROUND_BLUE.store(blue_u32, Ordering::Relaxed);
81            bg_changed = true;
82        }
83        let secs = FrameFuture.await;
84        let internal = Internal::new_lazy();
85        let mut cmds = internal.cmds.lock().unwrap();
86        let pair = internal.pair.clone();
87        if bg_changed {
88            cmds.push(GpuCmd::Background(red, green, blue));
89        }
90        Frame {
91            pair,
92            elapsed: secs.0,
93            aspect: secs.1,
94            resized: secs.2,
95        }
96    }
97}
98
99impl Canvas for Frame {
100    fn draw(&mut self, shader: &Shader, group: &Group) {
101        let internal = Internal::new_lazy();
102        let mut cmds = internal.cmds.lock().unwrap();
103        cmds.push(GpuCmd::Draw(shader.0, group.0));
104    }
105
106    fn set_camera(&mut self, camera: Transform) {
107        let internal = Internal::new_lazy();
108        let mut cmds = internal.cmds.lock().unwrap();
109        cmds.push(GpuCmd::SetCamera(camera));
110    }
111
112    fn set_tint<P: pix::el::Pixel>(&mut self, shader: &Shader, tint: P)
113    where
114        pix::chan::Ch32: From<<P as pix::el::Pixel>::Chan>,
115    {
116        let internal = Internal::new_lazy();
117        let mut cmds = internal.cmds.lock().unwrap();
118        let color: pix::rgb::SRgba32 = tint.convert();
119        let red = color.one().to_f32();
120        let green = color.two().to_f32();
121        let blue = color.three().to_f32();
122        let alpha = color.four().to_f32();
123        cmds.push(GpuCmd::SetTint(shader.0, [red, green, blue, alpha]));
124    }
125
126    fn draw_graphic(
127        &mut self,
128        shader: &Shader,
129        group: &Group,
130        graphic: &Texture,
131    ) {
132        let internal = Internal::new_lazy();
133        let mut cmds = internal.cmds.lock().unwrap();
134        cmds.push(GpuCmd::DrawGraphic(shader.0, group.0, graphic.0));
135    }
136
137    fn elapsed(&self) -> std::time::Duration {
138        self.elapsed
139    }
140
141    fn height(&self) -> f32 {
142        self.aspect
143    }
144
145    fn resized(&self) -> bool {
146        self.resized
147    }
148}
149
150impl Drop for Frame {
151    fn drop(&mut self) {
152        let (lock, cvar) = &*self.pair;
153        let mut started = lock.lock().unwrap();
154        *started = true;
155        // We notify the condvar that the value has changed.
156        cvar.notify_one();
157    }
158}