-
-
Notifications
You must be signed in to change notification settings - Fork 3.8k
/
Copy pathlib.rs
399 lines (374 loc) · 17.6 KB
/
lib.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
mod converters;
mod winit_config;
mod winit_windows;
use bevy_input::{
keyboard::KeyboardInput,
mouse::{MouseButtonInput, MouseMotion, MouseScrollUnit, MouseWheel},
touch::TouchInput,
};
pub use winit_config::*;
pub use winit_windows::*;
use bevy_app::{prelude::*, AppExit};
use bevy_ecs::{Resources, World};
use bevy_math::Vec2;
use bevy_utils::tracing::{error, trace};
use bevy_window::{
CreateWindow, CursorEntered, CursorLeft, CursorMoved, ReceivedCharacter, Window,
WindowCloseRequested, WindowCreated, WindowFocused, WindowResized, Windows,
};
use winit::{
event::{self, DeviceEvent, Event, WindowEvent},
event_loop::{ControlFlow, EventLoop, EventLoopWindowTarget},
};
#[derive(Default)]
pub struct WinitPlugin;
impl Plugin for WinitPlugin {
fn build(&self, app: &mut AppBuilder) {
app.init_resource::<WinitWindows>()
.set_runner(winit_runner)
.add_system(change_window);
}
}
fn change_window(_: &mut World, resources: &mut Resources) {
let winit_windows = resources.get::<WinitWindows>().unwrap();
let mut windows = resources.get_mut::<Windows>().unwrap();
for bevy_window in windows.iter_mut() {
let id = bevy_window.id();
for command in bevy_window.drain_commands() {
match command {
bevy_window::WindowCommand::SetWindowMode {
mode,
resolution: (width, height),
} => {
let window = winit_windows.get_window(id).unwrap();
match mode {
bevy_window::WindowMode::BorderlessFullscreen => {
window.set_fullscreen(Some(winit::window::Fullscreen::Borderless(None)))
}
bevy_window::WindowMode::Fullscreen { use_size } => window.set_fullscreen(
Some(winit::window::Fullscreen::Exclusive(match use_size {
true => get_fitting_videomode(
&window.current_monitor().unwrap(),
width,
height,
),
false => get_best_videomode(&window.current_monitor().unwrap()),
})),
),
bevy_window::WindowMode::Windowed => window.set_fullscreen(None),
}
}
bevy_window::WindowCommand::SetTitle { title } => {
let window = winit_windows.get_window(id).unwrap();
window.set_title(&title);
}
bevy_window::WindowCommand::SetResolution {
physical_width,
physical_height,
} => {
let window = winit_windows.get_window(id).unwrap();
window.set_inner_size(winit::dpi::PhysicalSize::new(
physical_width,
physical_height,
));
}
bevy_window::WindowCommand::SetVsync { .. } => (),
bevy_window::WindowCommand::SetResizable { resizable } => {
let window = winit_windows.get_window(id).unwrap();
window.set_resizable(resizable);
}
bevy_window::WindowCommand::SetDecorations { decorations } => {
let window = winit_windows.get_window(id).unwrap();
window.set_decorations(decorations);
}
bevy_window::WindowCommand::SetCursorLockMode { locked } => {
let window = winit_windows.get_window(id).unwrap();
window
.set_cursor_grab(locked)
.unwrap_or_else(|e| error!("Unable to un/grab cursor: {}", e));
}
bevy_window::WindowCommand::SetCursorVisibility { visible } => {
let window = winit_windows.get_window(id).unwrap();
window.set_cursor_visible(visible);
}
bevy_window::WindowCommand::SetCursorPosition { position } => {
let window = winit_windows.get_window(id).unwrap();
let inner_size = window.inner_size().to_logical::<f32>(window.scale_factor());
window
.set_cursor_position(winit::dpi::LogicalPosition::new(
position.x,
inner_size.height - position.y,
))
.unwrap_or_else(|e| error!("Unable to set cursor position: {}", e));
}
bevy_window::WindowCommand::SetMaximized { maximized } => {
let window = winit_windows.get_window(id).unwrap();
window.set_maximized(maximized)
}
}
}
}
}
fn run<F>(event_loop: EventLoop<()>, event_handler: F) -> !
where
F: 'static + FnMut(Event<'_, ()>, &EventLoopWindowTarget<()>, &mut ControlFlow),
{
event_loop.run(event_handler)
}
// TODO: It may be worth moving this cfg into a procedural macro so that it can be referenced by
// a single name instead of being copied around.
// https://gist.github.com/jakerr/231dee4a138f7a5f25148ea8f39b382e seems to work.
#[cfg(any(
target_os = "windows",
target_os = "macos",
target_os = "linux",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd"
))]
fn run_return<F>(event_loop: &mut EventLoop<()>, event_handler: F)
where
F: FnMut(Event<'_, ()>, &EventLoopWindowTarget<()>, &mut ControlFlow),
{
use winit::platform::desktop::EventLoopExtDesktop;
event_loop.run_return(event_handler)
}
#[cfg(not(any(
target_os = "windows",
target_os = "macos",
target_os = "linux",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd"
)))]
fn run_return<F>(_event_loop: &mut EventLoop<()>, _event_handler: F)
where
F: FnMut(Event<'_, ()>, &EventLoopWindowTarget<()>, &mut ControlFlow),
{
panic!("Run return is not supported on this platform!")
}
pub fn winit_runner(mut app: App) {
let mut event_loop = EventLoop::new();
let mut create_window_event_reader = EventReader::<CreateWindow>::default();
let mut app_exit_event_reader = EventReader::<AppExit>::default();
app.resources.insert_thread_local(event_loop.create_proxy());
// Create Windows and WinitWindows resources, so startup systems
// in below app.initialize() have access to them.
handle_create_window_events(
&mut app.resources,
&event_loop,
&mut create_window_event_reader,
);
trace!("Entering winit event loop");
let should_return_from_run = app
.resources
.get::<WinitConfig>()
.map_or(false, |config| config.return_from_run);
let event_handler = move |event: Event<()>,
event_loop: &EventLoopWindowTarget<()>,
control_flow: &mut ControlFlow| {
*control_flow = ControlFlow::Poll;
if let Some(app_exit_events) = app.resources.get_mut::<Events<AppExit>>() {
if app_exit_event_reader.latest(&app_exit_events).is_some() {
*control_flow = ControlFlow::Exit;
}
}
match event {
event::Event::WindowEvent {
event,
window_id: winit_window_id,
..
} => match event {
WindowEvent::Resized(size) => {
let winit_windows = app.resources.get_mut::<WinitWindows>().unwrap();
let mut windows = app.resources.get_mut::<Windows>().unwrap();
let window_id = winit_windows.get_window_id(winit_window_id).unwrap();
let window = windows.get_mut(window_id).unwrap();
window.update_physical_size_from_backend(size.width, size.height);
let mut resize_events =
app.resources.get_mut::<Events<WindowResized>>().unwrap();
resize_events.send(WindowResized {
id: window_id,
height: window.logical_height(),
width: window.logical_width(),
});
}
WindowEvent::CloseRequested => {
let mut window_close_requested_events = app
.resources
.get_mut::<Events<WindowCloseRequested>>()
.unwrap();
let winit_windows = app.resources.get_mut::<WinitWindows>().unwrap();
let window_id = winit_windows.get_window_id(winit_window_id).unwrap();
window_close_requested_events.send(WindowCloseRequested { id: window_id });
}
WindowEvent::KeyboardInput { ref input, .. } => {
let mut keyboard_input_events =
app.resources.get_mut::<Events<KeyboardInput>>().unwrap();
keyboard_input_events.send(converters::convert_keyboard_input(input));
}
WindowEvent::CursorMoved { position, .. } => {
let mut cursor_moved_events =
app.resources.get_mut::<Events<CursorMoved>>().unwrap();
let winit_windows = app.resources.get_mut::<WinitWindows>().unwrap();
let mut windows = app.resources.get_mut::<Windows>().unwrap();
let window_id = winit_windows.get_window_id(winit_window_id).unwrap();
let winit_window = winit_windows.get_window(window_id).unwrap();
let window = windows.get_mut(window_id).unwrap();
let position = position.to_logical(winit_window.scale_factor());
let inner_size = winit_window
.inner_size()
.to_logical::<f32>(winit_window.scale_factor());
// move origin to bottom left
let y_position = inner_size.height - position.y;
let position = Vec2::new(position.x, y_position);
window.update_cursor_position_from_backend(Some(position));
cursor_moved_events.send(CursorMoved {
id: window_id,
position,
});
}
WindowEvent::CursorEntered { .. } => {
let mut cursor_entered_events =
app.resources.get_mut::<Events<CursorEntered>>().unwrap();
let winit_windows = app.resources.get_mut::<WinitWindows>().unwrap();
let window_id = winit_windows.get_window_id(winit_window_id).unwrap();
cursor_entered_events.send(CursorEntered { id: window_id });
}
WindowEvent::CursorLeft { .. } => {
let mut cursor_left_events =
app.resources.get_mut::<Events<CursorLeft>>().unwrap();
let winit_windows = app.resources.get_mut::<WinitWindows>().unwrap();
let mut windows = app.resources.get_mut::<Windows>().unwrap();
let window_id = winit_windows.get_window_id(winit_window_id).unwrap();
let window = windows.get_mut(window_id).unwrap();
window.update_cursor_position_from_backend(None);
cursor_left_events.send(CursorLeft { id: window_id });
}
WindowEvent::MouseInput { state, button, .. } => {
let mut mouse_button_input_events =
app.resources.get_mut::<Events<MouseButtonInput>>().unwrap();
mouse_button_input_events.send(MouseButtonInput {
button: converters::convert_mouse_button(button),
state: converters::convert_element_state(state),
});
}
WindowEvent::MouseWheel { delta, .. } => match delta {
event::MouseScrollDelta::LineDelta(x, y) => {
let mut mouse_wheel_input_events =
app.resources.get_mut::<Events<MouseWheel>>().unwrap();
mouse_wheel_input_events.send(MouseWheel {
unit: MouseScrollUnit::Line,
x,
y,
});
}
event::MouseScrollDelta::PixelDelta(p) => {
let mut mouse_wheel_input_events =
app.resources.get_mut::<Events<MouseWheel>>().unwrap();
mouse_wheel_input_events.send(MouseWheel {
unit: MouseScrollUnit::Pixel,
x: p.x as f32,
y: p.y as f32,
});
}
},
WindowEvent::Touch(touch) => {
let mut touch_input_events =
app.resources.get_mut::<Events<TouchInput>>().unwrap();
let winit_windows = app.resources.get_mut::<WinitWindows>().unwrap();
let windows = app.resources.get_mut::<Windows>().unwrap();
let window_id = winit_windows.get_window_id(winit_window_id).unwrap();
let winit_window = winit_windows.get_window(window_id).unwrap();
let mut location = touch.location.to_logical(winit_window.scale_factor());
// FIXME?: On Android window start is top while on PC/Linux/OSX on bottom
if cfg!(target_os = "android") {
let window_height = windows.get_primary().unwrap().logical_height();
location.y = window_height - location.y;
}
touch_input_events.send(converters::convert_touch_input(touch, location));
}
WindowEvent::ReceivedCharacter(c) => {
let mut char_input_events = app
.resources
.get_mut::<Events<ReceivedCharacter>>()
.unwrap();
let winit_windows = app.resources.get_mut::<WinitWindows>().unwrap();
let window_id = winit_windows.get_window_id(winit_window_id).unwrap();
char_input_events.send(ReceivedCharacter {
id: window_id,
char: c,
})
}
WindowEvent::ScaleFactorChanged {
scale_factor,
new_inner_size,
} => {
let winit_windows = app.resources.get_mut::<WinitWindows>().unwrap();
let mut windows = app.resources.get_mut::<Windows>().unwrap();
let window_id = winit_windows.get_window_id(winit_window_id).unwrap();
let window = windows.get_mut(window_id).unwrap();
window.update_physical_size_from_backend(
new_inner_size.width,
new_inner_size.height,
);
window.update_scale_factor_from_backend(scale_factor);
}
WindowEvent::Focused(focused) => {
let mut focused_events =
app.resources.get_mut::<Events<WindowFocused>>().unwrap();
let winit_windows = app.resources.get_mut::<WinitWindows>().unwrap();
let window_id = winit_windows.get_window_id(winit_window_id).unwrap();
focused_events.send(WindowFocused {
id: window_id,
focused,
});
}
_ => {}
},
event::Event::DeviceEvent {
event: DeviceEvent::MouseMotion { delta },
..
} => {
let mut mouse_motion_events =
app.resources.get_mut::<Events<MouseMotion>>().unwrap();
mouse_motion_events.send(MouseMotion {
delta: Vec2::new(delta.0 as f32, delta.1 as f32),
});
}
event::Event::MainEventsCleared => {
handle_create_window_events(
&mut app.resources,
event_loop,
&mut create_window_event_reader,
);
app.update();
}
_ => (),
}
};
if should_return_from_run {
run_return(&mut event_loop, event_handler);
} else {
run(event_loop, event_handler);
}
}
fn handle_create_window_events(
resources: &mut Resources,
event_loop: &EventLoopWindowTarget<()>,
create_window_event_reader: &mut EventReader<CreateWindow>,
) {
let mut winit_windows = resources.get_mut::<WinitWindows>().unwrap();
let mut windows = resources.get_mut::<Windows>().unwrap();
let create_window_events = resources.get::<Events<CreateWindow>>().unwrap();
let mut window_created_events = resources.get_mut::<Events<WindowCreated>>().unwrap();
for create_window_event in create_window_event_reader.iter(&create_window_events) {
let mut window = Window::new(create_window_event.id, &create_window_event.descriptor);
winit_windows.create_window(event_loop, &mut window);
let window_id = window.id();
windows.add(window);
window_created_events.send(WindowCreated { id: window_id });
}
}