clay_viewer/
window.rs

1use std::{
2    time::{Duration, Instant},
3};
4use sdl2::{
5    self,
6    Sdl, EventPump,
7    render::{WindowCanvas, TextureAccess},
8    pixels::PixelFormatEnum,
9    event::Event,
10    keyboard::{Keycode, Scancode},
11    mouse::{MouseState, RelativeMouseState},
12};
13use clay_core::buffer::Image;
14
15use clay_utils::save_screenshot;
16
17
18rental! { mod rent {
19    use sdl2::{
20        video::{WindowContext},
21        render::{TextureCreator, Texture},
22    };
23
24    #[rental_mut]
25    pub struct RentTexture {
26        creator: Box<TextureCreator<WindowContext>>,
27        texture: Texture<'creator>,
28    }
29}}
30use rent::{RentTexture};
31
32
33pub struct Window {
34    context: Sdl,
35    size: (usize, usize),
36    canvas: WindowCanvas,
37
38    texture: Option<RentTexture>,
39    event_pump: Option<EventPump>,
40    state: WindowState,
41}
42
43pub struct WindowState {
44    pub lock: bool,
45    pub capture: bool,
46    pub drop_mouse: bool,
47    // time measurement
48    instant: Instant,
49    pub previous: Duration,
50    pub current: Duration,
51    screenshot: Option<bool>,
52}
53
54pub trait EventHandler {
55    fn handle_keys(&mut self, state: &WindowState, event: &Event) -> clay_core::Result<()>;
56    fn handle_mouse(
57        &mut self, state: &WindowState,
58        ms: &MouseState, rms: &RelativeMouseState,
59    ) -> clay_core::Result<()>;
60}
61
62struct DummyHandler();
63impl EventHandler for DummyHandler {
64    fn handle_keys(&mut self, _state: &WindowState, _event: &Event) -> clay_core::Result<()> { Ok(()) }
65    fn handle_mouse(
66        &mut self, _state: &WindowState,
67        _ms: &MouseState, _rms: &RelativeMouseState,
68    ) -> clay_core::Result<()> { Ok(()) }
69}
70
71impl WindowState {
72    fn new() -> Self {
73        let instant = Instant::now();
74        let time = instant.elapsed();
75        Self {
76            lock: false,
77            capture: true,
78            drop_mouse: true,
79            instant,
80            previous: time,
81            current: time,
82            screenshot: None,
83        }
84    }
85
86    fn step_frame(&mut self) {
87        self.previous = self.current;
88        self.current = self.instant.elapsed();
89    }
90
91    pub fn frame_duration(&self) -> Duration {
92        self.current - self.previous
93    }
94}
95
96impl Window {
97    pub fn new(size: (usize, usize)) -> clay_core::Result<Self> {
98        let context = sdl2::init()?;
99        let video = context.video()?;
100     
101        let window = video.window("Clay", size.0 as u32, size.1 as u32)
102        .position_centered()/*.resizable()*/.build()
103        .map_err(|e| e.to_string())?;
104     
105        let canvas = window.into_canvas().build().map_err(|e| e.to_string())?;
106
107        context.mouse().set_relative_mouse_mode(true);
108
109        let texture_creator = canvas.texture_creator();
110        let texture = Some(RentTexture::try_new_or_drop(
111            Box::new(texture_creator),
112            |tc| {
113                tc.create_texture(
114                    PixelFormatEnum::RGB24,
115                    TextureAccess::Streaming,
116                    size.0 as u32,
117                    size.1 as u32,
118                ).map_err(|e| e.to_string())
119            }
120        )?);
121
122        let event_pump = Some(context.event_pump()?);
123
124        let mut self_ = Self {
125            context, size, canvas,
126            texture, event_pump,
127            state: WindowState::new(),
128        };
129
130        self_.set_capture_mode(false);
131        self_.unlock();
132
133        Ok(self_)
134    }
135
136    pub fn set_capture_mode(&mut self, state: bool) {
137        self.state.capture = state;
138        self.context.mouse().set_relative_mouse_mode(state);
139    }
140
141    pub fn lock(&mut self) {
142        self.state.lock = true;
143        self.context.mouse().set_relative_mouse_mode(false);
144        self.canvas.window_mut().set_title("Clay [LOCKED]").unwrap();
145    }
146
147    pub fn unlock(&mut self) {
148        self.state.lock = false;
149        self.context.mouse().set_relative_mouse_mode(self.state.capture);
150        self.canvas.window_mut().set_title("Clay").unwrap();
151    }
152
153    pub fn locked(&self) -> bool {
154        self.state.lock
155    }
156
157    fn poll_inner(
158        &mut self, handler: &mut dyn EventHandler,
159        event_pump: &mut EventPump,
160    ) -> clay_core::Result<bool> {
161        'event_loop: loop {
162            let event = match event_pump.poll_event() {
163                Some(evt) => evt,
164                None => break 'event_loop,
165            };
166            let kbs = event_pump.keyboard_state();
167            let shift =
168            kbs.is_scancode_pressed(Scancode::LShift) ||
169            kbs.is_scancode_pressed(Scancode::RShift);
170
171            match event {
172                Event::Quit {..} => { return Ok(true); },
173                Event::KeyDown { keycode: Some(key), .. } => match key {
174                    Keycode::Escape => { return Ok(true); },
175                    Keycode::Tab => {
176                        if !self.locked() {
177                            self.set_capture_mode(!self.state.capture);
178                            if self.state.capture {
179                                self.state.drop_mouse = true;
180                            }
181                        }
182                    },
183                    Keycode::P => {
184                        self.state.screenshot = Some(shift);
185                    },
186                    Keycode::L => {
187                        if !shift {
188                            self.lock();
189                        } else {
190                            self.unlock();
191                            if self.state.capture {
192                                self.state.drop_mouse = true;
193                            }
194                        }
195                    },
196                    _ => (),
197                },
198                _ => (),
199            }
200
201            if !self.locked() {
202                handler.handle_keys(&self.state, &event)?;
203            }
204        }
205
206        if !self.locked() {
207            if !self.state.drop_mouse {
208                handler.handle_mouse(
209                    &self.state,
210                    &event_pump.mouse_state(),
211                    &event_pump.relative_mouse_state(),
212                )?;
213            } else {
214                event_pump.relative_mouse_state();
215                self.state.drop_mouse = false;
216            }
217        }
218
219        Ok(false)
220    }
221
222    pub fn poll_with_handler(&mut self, handler: &mut dyn EventHandler) -> clay_core::Result<bool> {
223        let mut event_pump = self.event_pump.take().unwrap();
224        let res = self.poll_inner(handler, &mut event_pump);
225        assert!(self.event_pump.replace(event_pump).is_none());
226        res
227    }
228
229    pub fn poll(&mut self) -> clay_core::Result<bool> {
230        self.poll_with_handler(&mut DummyHandler())
231    }
232
233    pub fn state(&self) -> &WindowState {
234        &self.state
235    }
236
237    pub fn step_frame(&mut self) -> Duration {
238        self.state.step_frame();
239        self.state.frame_duration()
240    }
241
242    pub fn size(&self) -> (usize, usize) {
243        self.size
244    }
245
246    pub fn draw(&mut self, img: &Image) -> clay_core::Result<()> {
247        let mut texture = self.texture.take().unwrap();
248
249        if let Some(ll) = self.state.screenshot {
250            println!("saving screenshot ...");
251            match save_screenshot(img, ll) {
252                Ok(f) => println!("... saved to '{}'", f),
253                Err(e) => eprintln!("error saving screenshot: {}", e),
254            }
255            self.state.screenshot = None;
256        }
257
258        let res = img.read()
259        .and_then(|data| {
260            texture.rent_mut(|texture| {
261                texture.update(None, &data, 3*img.dims().0)
262            }).map_err(|e| clay_core::Error::from(e.to_string()))
263        })
264        .and_then(|()| {
265            //self.canvas.clear();
266            texture.rent(|texture| {
267                self.canvas.copy(texture, None, None)
268                .map_err(|e| clay_core::Error::from(e))
269            })
270            .map(|()| self.canvas.present())
271        });
272
273        assert!(self.texture.replace(texture).is_none());
274
275        res
276    }
277}