From e59693fe679dc5ab0518b3bef5839683c6fbf1af Mon Sep 17 00:00:00 2001 From: Carter Anderson Date: Sun, 19 Apr 2020 12:13:04 -0700 Subject: [PATCH] exit events and systems --- bevy_app/src/app_builder.rs | 3 +- bevy_app/src/{ => event}/event.rs | 0 bevy_app/src/event/mod.rs | 5 ++ bevy_app/src/schedule_runner.rs | 36 +++++++++----- bevy_input/Cargo.toml | 1 + bevy_input/src/keyboard.rs | 2 +- bevy_input/src/lib.rs | 1 + bevy_input/src/system.rs | 19 ++++++++ .../draw_targets/ui_draw_target.rs | 28 ++++++----- bevy_render/src/mesh.rs | 9 ++-- .../pipeline/pipelines/forward/forward.frag | 1 + bevy_render/src/vertex.rs | 1 - bevy_window/Cargo.toml | 1 + bevy_window/src/event.rs | 37 ++++++++++++++ bevy_window/src/events.rs | 20 -------- bevy_window/src/lib.rs | 15 +++++- bevy_window/src/system.rs | 22 +++++++++ bevy_window/src/windows.rs | 6 +++ bevy_winit/src/lib.rs | 48 ++++++++++++------- examples/scene.rs | 1 + src/add_default_plugins.rs | 34 +++++++++++++ src/lib.rs | 39 ++------------- 22 files changed, 222 insertions(+), 107 deletions(-) rename bevy_app/src/{ => event}/event.rs (100%) create mode 100644 bevy_app/src/event/mod.rs create mode 100644 bevy_input/src/system.rs create mode 100644 bevy_window/src/event.rs delete mode 100644 bevy_window/src/events.rs create mode 100644 bevy_window/src/system.rs create mode 100644 src/add_default_plugins.rs diff --git a/bevy_app/src/app_builder.rs b/bevy_app/src/app_builder.rs index 5b2ca4409f..445e8dfa80 100644 --- a/bevy_app/src/app_builder.rs +++ b/bevy_app/src/app_builder.rs @@ -1,7 +1,7 @@ use crate::{ plugin::{load_plugin, AppPlugin}, schedule_plan::{SchedulePlan, System}, - stage, App, Events, + stage, App, Events, AppExit, }; use legion::prelude::{Resources, Universe, World}; @@ -23,6 +23,7 @@ impl Default for AppBuilder { }; app_builder.add_default_stages(); + app_builder.add_event::(); app_builder } } diff --git a/bevy_app/src/event.rs b/bevy_app/src/event/event.rs similarity index 100% rename from bevy_app/src/event.rs rename to bevy_app/src/event/event.rs diff --git a/bevy_app/src/event/mod.rs b/bevy_app/src/event/mod.rs new file mode 100644 index 0000000000..d20ea01f3a --- /dev/null +++ b/bevy_app/src/event/mod.rs @@ -0,0 +1,5 @@ +mod event; +pub use event::*; + +/// An event that indicates the app should exit. This will fully exit the app process. +pub struct AppExit; \ No newline at end of file diff --git a/bevy_app/src/schedule_runner.rs b/bevy_app/src/schedule_runner.rs index 05a1d37edd..3a00e7ce7b 100644 --- a/bevy_app/src/schedule_runner.rs +++ b/bevy_app/src/schedule_runner.rs @@ -1,4 +1,5 @@ -use super::{App, AppBuilder, AppPlugin}; +use super::{App, AppBuilder, AppPlugin, GetEventReader}; +use crate::{AppExit, Events}; use std::{thread, time::Duration}; #[derive(Copy, Clone, Debug)] @@ -21,20 +22,29 @@ pub struct ScheduleRunnerPlugin { impl AppPlugin for ScheduleRunnerPlugin { fn build(&self, app: &mut AppBuilder) { let run_mode = self.run_mode; - app.set_runner(move |mut app: App| match run_mode { - RunMode::Once => { - if let Some(ref mut schedule) = app.schedule { - schedule.execute(&mut app.world, &mut app.resources); + app.set_runner(move |mut app: App| { + let mut app_exit_event_reader = app.resources.get_event_reader::(); + match run_mode { + RunMode::Once => { + if let Some(ref mut schedule) = app.schedule { + schedule.execute(&mut app.world, &mut app.resources); + } } + RunMode::Loop { wait } => loop { + if let Some(app_exit_events) = app.resources.get_mut::>() { + if app_exit_events.latest(&mut app_exit_event_reader).is_some() { + break; + } + } + + if let Some(ref mut schedule) = app.schedule { + schedule.execute(&mut app.world, &mut app.resources); + } + if let Some(wait) = wait { + thread::sleep(wait); + } + }, } - RunMode::Loop { wait } => loop { - if let Some(ref mut schedule) = app.schedule { - schedule.execute(&mut app.world, &mut app.resources); - } - if let Some(wait) = wait { - thread::sleep(wait); - } - }, }); } } diff --git a/bevy_input/Cargo.toml b/bevy_input/Cargo.toml index 6efbcc8869..bb13b3bb80 100644 --- a/bevy_input/Cargo.toml +++ b/bevy_input/Cargo.toml @@ -6,3 +6,4 @@ edition = "2018" [dependencies] bevy_app = { path = "../bevy_app" } +legion = { path = "../bevy_legion" } diff --git a/bevy_input/src/keyboard.rs b/bevy_input/src/keyboard.rs index 5d9c6046ef..39e6ba6779 100644 --- a/bevy_input/src/keyboard.rs +++ b/bevy_input/src/keyboard.rs @@ -5,7 +5,7 @@ pub struct KeyboardInput { pub state: ElementState, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Eq, PartialEq)] pub enum ElementState { Pressed, Released, diff --git a/bevy_input/src/lib.rs b/bevy_input/src/lib.rs index 45450c83d5..0208802318 100644 --- a/bevy_input/src/lib.rs +++ b/bevy_input/src/lib.rs @@ -1,5 +1,6 @@ pub mod keyboard; pub mod mouse; +pub mod system; use bevy_app::{AppBuilder, AppPlugin}; use keyboard::KeyboardInput; diff --git a/bevy_input/src/system.rs b/bevy_input/src/system.rs new file mode 100644 index 0000000000..15f02c0dad --- /dev/null +++ b/bevy_input/src/system.rs @@ -0,0 +1,19 @@ +use bevy_app::{Events, GetEventReader, AppExit}; +use legion::prelude::*; +use crate::keyboard::{VirtualKeyCode, KeyboardInput, ElementState}; + +pub fn exit_on_esc_system(resources: &mut Resources) -> Box { + let mut keyboard_input_event_reader = resources.get_event_reader::(); + SystemBuilder::new("exit_on_esc") + .read_resource::>() + .write_resource::>() + .build(move |_, _, (ref keyboard_input_events, ref mut app_exit_events), _| { + for event in keyboard_input_events.iter(&mut keyboard_input_event_reader) { + if let Some(virtual_key_code) = event.virtual_key_code { + if event.state == ElementState::Pressed && virtual_key_code == VirtualKeyCode::Escape { + app_exit_events.send(AppExit); + } + } + } + }) +} \ No newline at end of file diff --git a/bevy_render/src/draw_target/draw_targets/ui_draw_target.rs b/bevy_render/src/draw_target/draw_targets/ui_draw_target.rs index e6605e3c78..01e2a4641b 100644 --- a/bevy_render/src/draw_target/draw_targets/ui_draw_target.rs +++ b/bevy_render/src/draw_target/draw_targets/ui_draw_target.rs @@ -9,11 +9,9 @@ use crate::{ }, renderer_2::RenderContext, }; -use bevy_asset::{Asset, Handle}; +use bevy_asset::Handle; use legion::prelude::*; -use zerocopy::AsBytes; - #[derive(Default)] pub struct UiDrawTarget { pub mesh_vertex_buffer: Option, @@ -86,15 +84,17 @@ impl DrawTarget for UiDrawTarget { let quad = Mesh::from(Quad { size: glam::vec2(1.0, 1.0), }); - let vertex_buffer_bytes = quad.get_vertex_buffer_bytes( - pipeline_descriptor - .get_layout() - .unwrap() - .vertex_buffer_descriptors - .first() - .as_ref() - .unwrap(), - ).unwrap(); + let vertex_buffer_bytes = quad + .get_vertex_buffer_bytes( + pipeline_descriptor + .get_layout() + .unwrap() + .vertex_buffer_descriptors + .first() + .as_ref() + .unwrap(), + ) + .unwrap(); self.mesh_vertex_buffer = Some(render_context.resources_mut().create_buffer_with_data( BufferInfo { buffer_usage: BufferUsage::VERTEX, @@ -103,7 +103,9 @@ impl DrawTarget for UiDrawTarget { &vertex_buffer_bytes, )); - let index_buffer_bytes = quad.get_index_buffer_bytes(pipeline_descriptor.index_format).unwrap(); + let index_buffer_bytes = quad + .get_index_buffer_bytes(pipeline_descriptor.index_format) + .unwrap(); self.mesh_index_buffer = Some(render_context.resources_mut().create_buffer_with_data( BufferInfo { buffer_usage: BufferUsage::INDEX, diff --git a/bevy_render/src/mesh.rs b/bevy_render/src/mesh.rs index 31d403be74..1551833aa4 100644 --- a/bevy_render/src/mesh.rs +++ b/bevy_render/src/mesh.rs @@ -4,9 +4,9 @@ use crate::{ VertexBufferDescriptor, VertexFormat, }, render_resource::AssetBatchers, - Renderable, Vertex, + Renderable, }; -use bevy_asset::{Asset, Handle}; +use bevy_asset::Handle; use glam::*; use legion::prelude::*; use std::borrow::Cow; @@ -301,8 +301,9 @@ pub mod shape { impl From for Mesh { fn from(plane: Plane) -> Self { Quad { - size: Vec2::new(plane.size, plane.size) - }.into() + size: Vec2::new(plane.size, plane.size), + } + .into() } } } diff --git a/bevy_render/src/pipeline/pipelines/forward/forward.frag b/bevy_render/src/pipeline/pipelines/forward/forward.frag index 302ba05210..a3b3d934fa 100644 --- a/bevy_render/src/pipeline/pipelines/forward/forward.frag +++ b/bevy_render/src/pipeline/pipelines/forward/forward.frag @@ -53,4 +53,5 @@ void main() { } // multiply the light by material color o_Target = vec4(color, 1.0) * albedo; + o_Target = vec4(v_Normal, 1.0); } diff --git a/bevy_render/src/vertex.rs b/bevy_render/src/vertex.rs index 76d6d76a9e..e38f87651e 100644 --- a/bevy_render/src/vertex.rs +++ b/bevy_render/src/vertex.rs @@ -1,4 +1,3 @@ -use std::convert::From; use zerocopy::{AsBytes, FromBytes}; use bevy_asset; diff --git a/bevy_window/Cargo.toml b/bevy_window/Cargo.toml index 691877ea9d..bb2825547b 100644 --- a/bevy_window/Cargo.toml +++ b/bevy_window/Cargo.toml @@ -6,4 +6,5 @@ edition = "2018" [dependencies] bevy_app = { path = "../bevy_app" } +legion = { path = "../bevy_legion" } uuid = { version = "0.8", features = ["v4", "serde"] } \ No newline at end of file diff --git a/bevy_window/src/event.rs b/bevy_window/src/event.rs new file mode 100644 index 0000000000..fb6b6e9322 --- /dev/null +++ b/bevy_window/src/event.rs @@ -0,0 +1,37 @@ +use super::{WindowDescriptor, WindowId}; + +/// A window event that is sent whenever a window has been resized. +#[derive(Debug, Clone)] +pub struct WindowResized { + pub id: WindowId, + pub width: u32, + pub height: u32, + pub is_primary: bool, +} + +/// An event that indicates that a new window should be created. +#[derive(Debug, Clone)] +pub struct CreateWindow { + pub descriptor: WindowDescriptor, +} + +/// An event that indicates a window should be closed. +#[derive(Debug, Clone)] +pub struct CloseWindow { + pub id: WindowId, +} + +/// An event that is sent whenever a new window is created. +#[derive(Debug, Clone)] +pub struct WindowCreated { + pub id: WindowId, + pub is_primary: bool, +} + +/// An event that is sent whenever a close was requested for a window. For example: when the "close" button +/// is pressed on a window. +#[derive(Debug, Clone)] +pub struct WindowCloseRequested { + pub id: WindowId, + pub is_primary: bool, +} \ No newline at end of file diff --git a/bevy_window/src/events.rs b/bevy_window/src/events.rs deleted file mode 100644 index a417053a20..0000000000 --- a/bevy_window/src/events.rs +++ /dev/null @@ -1,20 +0,0 @@ -use super::{WindowDescriptor, WindowId}; - -#[derive(Debug, Clone)] -pub struct WindowResized { - pub id: WindowId, - pub width: u32, - pub height: u32, - pub is_primary: bool, -} - -#[derive(Debug, Clone)] -pub struct CreateWindow { - pub descriptor: WindowDescriptor, -} - -#[derive(Debug, Clone)] -pub struct WindowCreated { - pub id: WindowId, - pub is_primary: bool, -} diff --git a/bevy_window/src/lib.rs b/bevy_window/src/lib.rs index d7e876a0df..0735500ec4 100644 --- a/bevy_window/src/lib.rs +++ b/bevy_window/src/lib.rs @@ -1,8 +1,10 @@ -mod events; +mod event; +mod system; mod window; mod windows; -pub use events::*; +pub use event::*; +pub use system::*; pub use window::*; pub use windows::*; @@ -10,12 +12,14 @@ use bevy_app::{AppBuilder, AppPlugin, Events}; pub struct WindowPlugin { pub primary_window: Option, + pub exit_on_close: bool, } impl Default for WindowPlugin { fn default() -> Self { WindowPlugin { primary_window: Some(WindowDescriptor::default()), + exit_on_close: true, } } } @@ -25,6 +29,8 @@ impl AppPlugin for WindowPlugin { app.add_event::() .add_event::() .add_event::() + .add_event::() + .add_event::() .add_resource(Windows::default()); if let Some(ref primary_window_descriptor) = self.primary_window { @@ -34,5 +40,10 @@ impl AppPlugin for WindowPlugin { descriptor: primary_window_descriptor.clone(), }); } + + if self.exit_on_close { + let exit_on_close_system = exit_on_window_close_system(app.resources_mut(), None); + app.add_system(exit_on_close_system); + } } } diff --git a/bevy_window/src/system.rs b/bevy_window/src/system.rs new file mode 100644 index 0000000000..75a2ce2d57 --- /dev/null +++ b/bevy_window/src/system.rs @@ -0,0 +1,22 @@ +use legion::prelude::*; +use bevy_app::{Events, AppExit, GetEventReader}; +use crate::{WindowId, WindowCloseRequested}; + +pub fn exit_on_window_close_system(resources: &mut Resources, window_id: Option) -> Box { + let mut window_close_requested_event_reader = resources.get_event_reader::(); + SystemBuilder::new("exit_on_window_close") + .read_resource::>() + .write_resource::>() + .build(move |_, _, (ref window_close_requested_events, ref mut app_exit_events), _| { + for window_close_requested_event in window_close_requested_events.iter(&mut window_close_requested_event_reader) { + match window_id.as_ref() { + Some(window_id) => if *window_id == window_close_requested_event.id { + app_exit_events.send(AppExit); + }, + None => if window_close_requested_event.is_primary { + app_exit_events.send(AppExit); + } + } + } + }) +} \ No newline at end of file diff --git a/bevy_window/src/windows.rs b/bevy_window/src/windows.rs index 05ade50d99..51f34c36db 100644 --- a/bevy_window/src/windows.rs +++ b/bevy_window/src/windows.rs @@ -29,6 +29,12 @@ impl Windows { .and_then(|primary| self.windows.get(&primary)) } + pub fn is_primary(&self, window_id: WindowId) -> bool { + self.get_primary() + .map(|primary_window| primary_window.id == window_id) + .unwrap_or(false) + } + pub fn iter(&self) -> impl Iterator { self.windows.values() } diff --git a/bevy_winit/src/lib.rs b/bevy_winit/src/lib.rs index 3554368b9e..d69c51babe 100644 --- a/bevy_winit/src/lib.rs +++ b/bevy_winit/src/lib.rs @@ -7,8 +7,10 @@ use bevy_input::{ mouse::{MouseButtonInput, MouseMotion}, }; -use bevy_app::{App, AppBuilder, AppPlugin, EventReader, Events, GetEventReader}; -use bevy_window::{CreateWindow, Window, WindowCreated, WindowResized, Windows}; +use bevy_app::{App, AppBuilder, AppExit, AppPlugin, EventReader, Events, GetEventReader}; +use bevy_window::{ + CreateWindow, Window, WindowCloseRequested, WindowCreated, WindowResized, Windows, +}; use legion::prelude::*; use winit::{ event, @@ -33,6 +35,7 @@ impl AppPlugin for WinitPlugin { pub fn winit_runner(mut app: App) { let event_loop = EventLoop::new(); let mut create_window_event_reader = app.resources.get_event_reader::(); + let mut app_exit_event_reader = app.resources.get_event_reader::(); handle_create_window_events( &mut app.resources, @@ -47,6 +50,13 @@ pub fn winit_runner(mut app: App) { } else { ControlFlow::Poll }; + + if let Some(app_exit_events) = app.resources.get_mut::>() { + if app_exit_events.latest(&mut app_exit_event_reader).is_some() { + *control_flow = ControlFlow::Exit; + } + } + match event { event::Event::WindowEvent { event: WindowEvent::Resized(size), @@ -56,25 +66,35 @@ pub fn winit_runner(mut app: App) { let winit_windows = app.resources.get_mut::().unwrap(); let mut windows = app.resources.get_mut::().unwrap(); let window_id = winit_windows.get_window_id(winit_window_id).unwrap(); - let is_primary = windows - .get_primary() - .map(|primary_window| primary_window.id == window_id) - .unwrap_or(false); let mut window = windows.get_mut(window_id).unwrap(); window.width = size.width; window.height = size.height; - let mut resize_event = app.resources.get_mut::>().unwrap(); - resize_event.send(WindowResized { + let mut resize_events = app.resources.get_mut::>().unwrap(); + resize_events.send(WindowResized { id: window_id, height: window.height, width: window.width, - is_primary, + is_primary: windows.is_primary(window_id), }); } - event::Event::WindowEvent { event, .. } => match event { + event::Event::WindowEvent { + event, + window_id: winit_window_id, + .. + } => match event { WindowEvent::CloseRequested => { - *control_flow = ControlFlow::Exit; + let mut window_close_requested_events = app + .resources + .get_mut::>() + .unwrap(); + let windows = app.resources.get_mut::().unwrap(); + let winit_windows = app.resources.get_mut::().unwrap(); + let window_id = winit_windows.get_window_id(winit_window_id).unwrap(); + window_close_requested_events.send(WindowCloseRequested { + id: window_id, + is_primary: windows.is_primary(window_id), + }); } WindowEvent::KeyboardInput { ref input, .. } => { let mut keyboard_input_events = @@ -126,13 +146,9 @@ fn handle_create_window_events( winit_windows.create_window(event_loop, &window); let window_id = window.id; windows.add(window); - let is_primary = windows - .get_primary() - .map(|primary| primary.id == window_id) - .unwrap_or(false); window_created_events.send(WindowCreated { id: window_id, - is_primary, + is_primary: windows.is_primary(window_id), }); } } diff --git a/examples/scene.rs b/examples/scene.rs index 0d11fb793a..619e3296b7 100644 --- a/examples/scene.rs +++ b/examples/scene.rs @@ -4,6 +4,7 @@ fn main() { App::build() .add_default_plugins() .add_startup_system(setup) + .add_system_init(bevy::input::system::exit_on_esc_system) .run(); } diff --git a/src/add_default_plugins.rs b/src/add_default_plugins.rs new file mode 100644 index 0000000000..e5fb21c93a --- /dev/null +++ b/src/add_default_plugins.rs @@ -0,0 +1,34 @@ +use crate::app::AppBuilder; + +pub trait AddDefaultPlugins { + fn add_default_plugins(&mut self) -> &mut Self; +} + +impl AddDefaultPlugins for AppBuilder { + fn add_default_plugins(&mut self) -> &mut Self { + #[cfg(feature = "core")] + self.add_plugin(bevy_core::CorePlugin::default()); + + #[cfg(feature = "input")] + self.add_plugin(bevy_input::InputPlugin::default()); + + #[cfg(feature = "window")] + self.add_plugin(bevy_window::WindowPlugin::default()); + + #[cfg(feature = "render")] + self.add_plugin(bevy_render::RenderPlugin::default()); + + #[cfg(feature = "ui")] + self.add_plugin(bevy_ui::UiPlugin::default()); + + #[cfg(feature = "winit")] + self.add_plugin(bevy_winit::WinitPlugin::default()); + #[cfg(not(feature = "winit"))] + self.add_plugin(bevy_app::schedule_runner::ScheduleRunnerPlugin::default()); + + #[cfg(feature = "wgpu")] + self.add_plugin(bevy_wgpu::WgpuPlugin::default()); + + self + } +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 364b0f5995..dd5d977033 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -34,7 +34,9 @@ #![feature(min_specialization)] pub mod prelude; +mod add_default_plugins; +pub use add_default_plugins::*; pub use bevy_app as app; pub use glam as math; pub use legion; @@ -60,39 +62,4 @@ pub use bevy_transform as transform; #[cfg(feature = "ui")] pub use bevy_ui as ui; #[cfg(feature = "window")] -pub use bevy_window as window; - -use app::AppBuilder; - -pub trait AddDefaultPlugins { - fn add_default_plugins(&mut self) -> &mut Self; -} - -impl AddDefaultPlugins for AppBuilder { - fn add_default_plugins(&mut self) -> &mut Self { - #[cfg(feature = "core")] - self.add_plugin(bevy_core::CorePlugin::default()); - - #[cfg(feature = "input")] - self.add_plugin(bevy_input::InputPlugin::default()); - - #[cfg(feature = "window")] - self.add_plugin(bevy_window::WindowPlugin::default()); - - #[cfg(feature = "render")] - self.add_plugin(bevy_render::RenderPlugin::default()); - - #[cfg(feature = "ui")] - self.add_plugin(ui::UiPlugin::default()); - - #[cfg(feature = "winit")] - self.add_plugin(bevy_winit::WinitPlugin::default()); - #[cfg(not(feature = "winit"))] - self.add_plugin(bevy_app::schedule_runner::ScheduleRunnerPlugin::default()); - - #[cfg(feature = "wgpu")] - self.add_plugin(bevy_wgpu::WgpuPlugin::default()); - - self - } -} +pub use bevy_window as window; \ No newline at end of file