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 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().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 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}