pixels_graphics_lib/integration/
pixels_winit.rs1use crate::prelude::*;
2use crate::GraphicsError::LoadingWindowPref;
3use crate::{GraphicsError, MouseData, Options, System};
4use buffer_graphics_lib::Graphics;
5use pixels::PixelsBuilder;
6use pixels::{Pixels, SurfaceTexture};
7use simple_game_utils::prelude::Timing;
8use winit::dpi::LogicalSize;
9use winit::dpi::PhysicalSize;
10use winit::event::{Event, MouseButton, WindowEvent};
11use winit::event_loop::EventLoop;
12use winit::window::CursorGrabMode;
13use winit::window::WindowBuilder;
14use winit_input_helper::WinitInputHelper;
15
16fn create_window(
17 size: (u32, u32),
18 title: &str,
19 scale: WindowScaling,
20 event_loop: &EventLoop<()>,
21) -> Result<Window, GraphicsError> {
22 let window = WindowBuilder::new()
23 .with_visible(false)
24 .with_title(title)
25 .build(event_loop)
26 .map_err(|err| GraphicsError::WindowInit(format!("{err:?}")))?;
27 let factor = match scale {
28 WindowScaling::Native => window.scale_factor(),
29 WindowScaling::Double => window.scale_factor() + 2.0,
30 WindowScaling::Quad => window.scale_factor() + 4.0,
31 };
32
33 let px_size: PhysicalSize<u32> = LogicalSize::new(size.0, size.1).to_physical(factor);
34
35 window.set_min_inner_size(Some(px_size));
36 let _ = window.request_inner_size(px_size);
37 window.set_visible(true);
38
39 Ok(window)
40}
41
42fn setup(
70 canvas_size: (u32, u32),
71 options: &Options,
72 title: &str,
73 event_loop: &EventLoop<()>,
74) -> Result<(Window, Pixels), GraphicsError> {
75 let win = create_window(canvas_size, title, options.scaling, event_loop)?;
76 let surface = SurfaceTexture::new(win.inner_size().width, win.inner_size().height, &win);
77 let pixels = PixelsBuilder::new(canvas_size.0, canvas_size.1, surface)
78 .enable_vsync(options.vsync)
79 .build()
80 .map_err(GraphicsError::PixelsInit)?;
81 Ok((win, pixels))
82}
83
84pub fn run(
99 width: usize,
100 height: usize,
101 title: &str,
102 mut system: Box<dyn System>,
103 options: Options,
104) -> Result<(), GraphicsError> {
105 let event_loop = EventLoop::new().expect("Failed to setup event loop");
106 let mut input = WinitInputHelper::new();
107 let (mut window, mut pixels) =
108 setup((width as u32, height as u32), &options, title, &event_loop)?;
109
110 if options.confine_cursor {
111 #[cfg(target_os = "macos")]
112 let _ = window.set_cursor_grab(CursorGrabMode::Locked);
113 #[cfg(not(target_os = "macos"))]
114 let _ = window.set_cursor_grab(CursorGrabMode::Confined);
115 }
116
117 if options.hide_cursor {
118 window.set_cursor_visible(false);
119 }
120
121 #[cfg(feature = "window_prefs")]
122 if let Some(mut prefs) = system.window_prefs() {
123 prefs.load().map_err(|e| LoadingWindowPref(e.to_string()))?;
124 prefs.restore(&mut window);
125 }
126
127 let mut timing = Timing::new(options.ups);
128 let mut mouse = MouseData::default();
129
130 event_loop
131 .run(move |event, target| {
132 timing.update();
133 match &event {
134 Event::LoopExiting => {
135 system.on_window_closed();
136 #[cfg(feature = "window_prefs")]
137 if let Some(mut prefs) = system.window_prefs() {
138 prefs.store(&window);
139 let _ = prefs
141 .save()
142 .map_err(|err| eprintln!("Unable to save prefs: {err:?}"));
143 }
144 }
145 Event::WindowEvent { event, .. } => match event {
146 WindowEvent::Occluded(hidden) => system.on_visibility_changed(!hidden),
147 WindowEvent::Focused(focused) => system.on_focus_changed(*focused),
148 WindowEvent::RedrawRequested => {
149 let mut graphics = Graphics::new_u8_rgba(pixels.frame_mut(), width, height)
150 .expect("Creating graphics wrapper");
151 system.render(&mut graphics);
152 timing.renders += 1;
153 if pixels
154 .render()
155 .map_err(|e| eprintln!("pixels.render() failed: {e:?}"))
156 .is_err()
157 {
158 system.on_window_closed();
159 target.exit();
160 return;
161 }
162 }
163 _ => {}
164 },
165 _ => {}
166 }
167
168 timing.accumulated_time += timing.delta;
169 while timing.accumulated_time >= timing.fixed_time_step {
170 system.update(&timing, &window);
171 timing.accumulated_time -= timing.fixed_time_step;
172 timing.updates += 1;
173 }
174
175 if input.update(&event) {
176 if input.close_requested() || input.destroyed() {
177 system.on_window_closed();
178 target.exit();
179 return;
180 }
181
182 if let Some(size) = input.window_resized() {
183 pixels
184 .resize_surface(size.width, size.height)
185 .expect("Unable to resize buffer");
186 }
187
188 if let Some(mc) = input.cursor() {
189 let (x, y) = pixels
190 .window_pos_to_pixel(mc)
191 .unwrap_or_else(|pos| pixels.clamp_pixel_pos(pos));
192 mouse.xy = coord!(x, y);
193 system.on_mouse_move(&mouse);
194 }
195
196 let mut held_buttons = vec![];
197 for button in system.keys_used() {
198 if input.key_held(*button) {
199 held_buttons.push(*button);
200 }
201 }
202 if !held_buttons.is_empty() {
203 system.on_key_down(held_buttons);
204 }
205
206 let mut released_buttons = vec![];
207 for button in system.keys_used() {
208 if input.key_released(*button) {
209 released_buttons.push(*button);
210 }
211 }
212 if !released_buttons.is_empty() {
213 system.on_key_up(released_buttons);
214 }
215
216 if input.mouse_pressed(MouseButton::Left) {
217 mouse.add_down(mouse.xy, MouseButton::Left);
218 system.on_mouse_down(&mouse, MouseButton::Left);
219 }
220 if input.mouse_pressed(MouseButton::Right) {
221 mouse.add_down(mouse.xy, MouseButton::Right);
222 system.on_mouse_down(&mouse, MouseButton::Right);
223 }
224 if input.mouse_pressed(MouseButton::Middle) {
225 mouse.add_down(mouse.xy, MouseButton::Middle);
226 system.on_mouse_down(&mouse, MouseButton::Middle);
227 }
228
229 if input.mouse_released(MouseButton::Left) {
230 mouse.add_up(MouseButton::Left);
231 system.on_mouse_up(&mouse, MouseButton::Left);
232 }
233 if input.mouse_released(MouseButton::Right) {
234 mouse.add_up(MouseButton::Right);
235 system.on_mouse_up(&mouse, MouseButton::Right);
236 }
237 if input.mouse_released(MouseButton::Middle) {
238 mouse.add_up(MouseButton::Middle);
239 system.on_mouse_up(&mouse, MouseButton::Middle);
240 }
241
242 let scroll = input.scroll_diff();
243 if scroll.0 != 0.0 || scroll.1 != 0.0 {
244 system.on_scroll(&mouse, scroll.0.trunc() as isize, scroll.1.trunc() as isize);
245 }
246
247 window.request_redraw();
248 }
249
250 if system.should_exit() {
251 target.exit();
252 }
253
254 timing.update_fps();
255
256 timing.last = timing.now;
257 })
258 .expect("Error when executing event loop");
259
260 Ok(())
261}