embedded_graphics_simulator/window/
sdl_window.rs

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