embedded_graphics_simulator/window/
sdl_window.rs

1use std::cell::{RefCell, RefMut};
2
3use embedded_graphics::{
4    pixelcolor::Rgb888,
5    prelude::{Point, Size},
6};
7use sdl2::{
8    event::Event,
9    keyboard::{Keycode, Mod},
10    mouse::{MouseButton, MouseWheelDirection},
11    pixels::PixelFormatEnum,
12    render::{Canvas, Texture, TextureCreator},
13    video::WindowContext,
14    EventPump,
15};
16
17use crate::{OutputImage, OutputSettings};
18
19/// A derivation of [`sdl2::event::Event`] mapped to embedded-graphics coordinates
20#[derive(Copy, Clone, Debug, PartialEq, Eq)]
21pub enum SimulatorEvent {
22    /// A keypress event, fired on keyUp
23    KeyUp {
24        /// The key being released
25        keycode: Keycode,
26        /// Any modifier being held at the time of keyup
27        keymod: Mod,
28        /// Whether the key is repeating
29        repeat: bool,
30    },
31    /// A keypress event, fired on keyDown
32    KeyDown {
33        /// The key being pressed
34        keycode: Keycode,
35        /// Any modifier being held at the time of keydown
36        keymod: Mod,
37        /// Whether the key is repeating
38        repeat: bool,
39    },
40    /// A mouse click event, fired on mouseUp
41    MouseButtonUp {
42        /// The mouse button being released
43        mouse_btn: MouseButton,
44        /// The location of the mouse in Simulator coordinates
45        point: Point,
46    },
47    /// A mouse click event, fired on mouseDown
48    MouseButtonDown {
49        /// The mouse button being pressed
50        mouse_btn: MouseButton,
51        /// The location of the mouse in Simulator coordinates
52        point: Point,
53    },
54    /// A mouse wheel event
55    MouseWheel {
56        /// The scroll wheel delta in the x and y direction
57        scroll_delta: Point,
58        /// The directionality of the scroll (normal or flipped)
59        direction: MouseWheelDirection,
60    },
61    /// Mouse move event
62    MouseMove {
63        /// The current mouse position
64        point: Point,
65    },
66    /// An exit event
67    Quit,
68}
69
70/// Iterator over simulator events.
71///
72/// See [`Window::events`](crate::Window::events) and
73/// [`MultiWindow::events`](crate::MultiWindow::events) for more details.
74pub struct SimulatorEventsIter<'a> {
75    event_pump: RefMut<'a, EventPump>,
76    output_settings: OutputSettings,
77}
78
79impl Iterator for SimulatorEventsIter<'_> {
80    type Item = SimulatorEvent;
81
82    fn next(&mut self) -> Option<Self::Item> {
83        while let Some(event) = self.event_pump.poll_event() {
84            match event {
85                Event::Quit { .. }
86                | Event::KeyDown {
87                    keycode: Some(Keycode::Escape),
88                    ..
89                } => return Some(SimulatorEvent::Quit),
90                Event::KeyDown {
91                    keycode,
92                    keymod,
93                    repeat,
94                    ..
95                } => {
96                    return keycode.map(|valid_keycode| SimulatorEvent::KeyDown {
97                        keycode: valid_keycode,
98                        keymod,
99                        repeat,
100                    })
101                }
102                Event::KeyUp {
103                    keycode,
104                    keymod,
105                    repeat,
106                    ..
107                } => {
108                    return keycode.map(|valid_keycode| SimulatorEvent::KeyUp {
109                        keycode: valid_keycode,
110                        keymod,
111                        repeat,
112                    })
113                }
114                Event::MouseButtonUp {
115                    x, y, mouse_btn, ..
116                } => {
117                    let point = self.output_settings.output_to_display(Point::new(x, y));
118                    return Some(SimulatorEvent::MouseButtonUp { point, mouse_btn });
119                }
120                Event::MouseButtonDown {
121                    x, y, mouse_btn, ..
122                } => {
123                    let point = self.output_settings.output_to_display(Point::new(x, y));
124                    return Some(SimulatorEvent::MouseButtonDown { point, mouse_btn });
125                }
126                Event::MouseMotion { x, y, .. } => {
127                    let point = self.output_settings.output_to_display(Point::new(x, y));
128                    return Some(SimulatorEvent::MouseMove { point });
129                }
130                Event::MouseWheel {
131                    x, y, direction, ..
132                } => {
133                    return Some(SimulatorEvent::MouseWheel {
134                        scroll_delta: Point::new(x, y),
135                        direction,
136                    })
137                }
138                _ => {
139                    // ignore other events and check next event
140                }
141            }
142        }
143
144        None
145    }
146}
147
148pub struct SdlWindow {
149    canvas: Canvas<sdl2::video::Window>,
150    event_pump: RefCell<EventPump>,
151    window_texture: SdlWindowTexture,
152    size: Size,
153}
154
155impl SdlWindow {
156    pub fn new(title: &str, size: Size) -> Self {
157        let sdl_context = sdl2::init().unwrap();
158        let video_subsystem = sdl_context.video().unwrap();
159
160        let window = video_subsystem
161            .window(title, size.width, size.height)
162            .position_centered()
163            .build()
164            .unwrap();
165
166        let canvas = window.into_canvas().build().unwrap();
167        let event_pump = sdl_context.event_pump().unwrap();
168
169        let window_texture = SdlWindowTextureBuilder {
170            texture_creator: canvas.texture_creator(),
171            texture_builder: |creator: &TextureCreator<WindowContext>| {
172                creator
173                    .create_texture_streaming(PixelFormatEnum::RGB24, size.width, size.height)
174                    .unwrap()
175            },
176        }
177        .build();
178
179        Self {
180            canvas,
181            event_pump: RefCell::new(event_pump),
182            window_texture,
183            size,
184        }
185    }
186
187    pub fn update(&mut self, framebuffer: &OutputImage<Rgb888>) {
188        self.window_texture.with_mut(|fields| {
189            fields
190                .texture
191                .update(
192                    None,
193                    framebuffer.data.as_ref(),
194                    self.size.width as usize * 3,
195                )
196                .unwrap();
197        });
198
199        self.canvas
200            .copy(self.window_texture.borrow_texture(), None, None)
201            .unwrap();
202        self.canvas.present();
203    }
204
205    /// Handle events
206    /// Return an iterator of all captured SimulatorEvent
207    pub fn events(&self, output_settings: &OutputSettings) -> SimulatorEventsIter<'_> {
208        SimulatorEventsIter {
209            event_pump: self.event_pump.borrow_mut(),
210            output_settings: output_settings.clone(),
211        }
212    }
213}
214
215#[ouroboros::self_referencing]
216struct SdlWindowTexture {
217    texture_creator: TextureCreator<WindowContext>,
218    #[borrows(texture_creator)]
219    #[covariant]
220    texture: Texture<'this>,
221}