game: improve event handling, update input systems to use new event handling
CI / build (push) Failing after 12m37s
Details
CI / build (push) Failing after 12m37s
Details
This commit is contained in:
parent
9125b91977
commit
9b1cc8c364
|
@ -2107,6 +2107,7 @@ dependencies = [
|
|||
"anyhow",
|
||||
"async-std",
|
||||
"async-trait",
|
||||
"atomic_refcell",
|
||||
"bind_match",
|
||||
"bytemuck",
|
||||
"cfg-if",
|
||||
|
|
|
@ -40,6 +40,7 @@ petgraph = { version = "0.6.5", features = ["matrix_graph"] }
|
|||
bind_match = "0.1.2"
|
||||
round_mult = "0.1.3"
|
||||
fast_poisson = { version = "1.0.0", features = ["single_precision"] }
|
||||
atomic_refcell = "0.1.13"
|
||||
|
||||
[features]
|
||||
tracy = ["dep:tracing-tracy"]
|
||||
|
|
|
@ -0,0 +1,137 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use atomic_refcell::AtomicRefCell;
|
||||
use lyra_ecs::{query::{ResMut, WorldTick}, system::FnArgFetcher, Tick};
|
||||
|
||||
pub trait Event: Clone + Send + Sync + 'static {}
|
||||
impl<T: Clone + Send + Sync + 'static> Event for T {}
|
||||
|
||||
pub struct Events<T: Event> {
|
||||
events: Arc<AtomicRefCell<Vec<T>>>,
|
||||
last_updated_tick: Option<Tick>,
|
||||
}
|
||||
|
||||
impl<T: Event> Default for Events<T> {
|
||||
fn default() -> Self {
|
||||
Self { events: Default::default(), last_updated_tick: None }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Event> Events<T> {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
pub fn push_event(&mut self, event: T) {
|
||||
let mut events = self.events.borrow_mut();
|
||||
events.push(event);
|
||||
}
|
||||
|
||||
pub fn reader(&self) -> EventReader<T> {
|
||||
EventReader {
|
||||
events: self.events.clone(),
|
||||
cursor: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn writer(&self) -> EventWriter<T> {
|
||||
EventWriter {
|
||||
events: self.events.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EventReader<T: Event> {
|
||||
events: Arc<AtomicRefCell<Vec<T>>>,
|
||||
cursor: usize,
|
||||
}
|
||||
|
||||
impl<T: Event> EventReader<T> {
|
||||
pub fn read(&mut self) -> Option<atomic_refcell::AtomicRef<T>> {
|
||||
let events = self.events.borrow();
|
||||
|
||||
if self.cursor >= events.len() {
|
||||
None
|
||||
} else {
|
||||
let e = atomic_refcell::AtomicRef::map(events,
|
||||
|e| e.get(self.cursor).unwrap());
|
||||
self.cursor += 1;
|
||||
Some(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EventWriter<T: Event> {
|
||||
events: Arc<AtomicRefCell<Vec<T>>>,
|
||||
}
|
||||
|
||||
impl<T: Event> EventWriter<T> {
|
||||
pub fn write(&mut self, event: T) {
|
||||
let mut events = self.events.borrow_mut();
|
||||
events.push(event);
|
||||
}
|
||||
}
|
||||
|
||||
/// Clean events of event type `T` every 2 ticks.
|
||||
pub fn event_cleaner_system<T>(tick: WorldTick, mut events: ResMut<Events<T>>) -> anyhow::Result<()>
|
||||
where
|
||||
T: Event
|
||||
{
|
||||
let last_tick = *events.last_updated_tick.unwrap_or(*tick);
|
||||
let world_tick = **tick;
|
||||
|
||||
if last_tick + 2 < world_tick {
|
||||
events.last_updated_tick = Some(*tick);
|
||||
|
||||
let mut events = events.events.borrow_mut();
|
||||
events.clear();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl<T: Event> FnArgFetcher for EventReader<T> {
|
||||
type State = usize;
|
||||
|
||||
type Arg<'a, 'state> = EventReader<T>;
|
||||
|
||||
fn create_state(_: std::ptr::NonNull<lyra_ecs::World>) -> Self::State {
|
||||
0
|
||||
}
|
||||
|
||||
unsafe fn get<'a, 'state>(state: &'state mut Self::State, world: std::ptr::NonNull<lyra_ecs::World>) -> Self::Arg<'a, 'state> {
|
||||
let world = world.as_ref();
|
||||
let events = world.get_resource::<Events<T>>();
|
||||
|
||||
// reset the reader cursor when the events are cleared
|
||||
let world_tick = world.current_tick();
|
||||
if world_tick == events.last_updated_tick.unwrap_or(world_tick) {
|
||||
*state = 0;
|
||||
}
|
||||
|
||||
let mut reader = events.reader();
|
||||
reader.cursor = *state;
|
||||
|
||||
reader
|
||||
}
|
||||
|
||||
fn apply_deferred(_: Self::State, _: std::ptr::NonNull<lyra_ecs::World>) { }
|
||||
}
|
||||
|
||||
impl<T: Event> FnArgFetcher for EventWriter<T> {
|
||||
type State = ();
|
||||
|
||||
type Arg<'a, 'state> = EventWriter<T>;
|
||||
|
||||
fn create_state(_: std::ptr::NonNull<lyra_ecs::World>) -> Self::State {
|
||||
()
|
||||
}
|
||||
|
||||
unsafe fn get<'a, 'state>(_: &'state mut Self::State, world: std::ptr::NonNull<lyra_ecs::World>) -> Self::Arg<'a, 'state> {
|
||||
let world = world.as_ref();
|
||||
let events = world.get_resource::<Events<T>>();
|
||||
events.writer()
|
||||
}
|
||||
|
||||
fn apply_deferred(_: Self::State, _: std::ptr::NonNull<lyra_ecs::World>) { }
|
||||
}
|
|
@ -1,202 +0,0 @@
|
|||
use std::{any::{Any, TypeId}, collections::{HashMap, VecDeque}, marker::PhantomData, mem::{self, MaybeUninit}, ops::Range};
|
||||
|
||||
use crate::plugin::Plugin;
|
||||
|
||||
pub trait Event: Clone + Send + Sync + 'static {}
|
||||
impl<T: Clone + Send + Sync + 'static> Event for T {}
|
||||
|
||||
pub type Events<T> = VecDeque<T>;
|
||||
|
||||
#[derive(Clone)]
|
||||
struct TypeErasedBuffer {
|
||||
type_id: TypeId,
|
||||
item_size: usize,
|
||||
data: Vec<MaybeUninit<u8>>,
|
||||
fn_drop_item: fn(item: *mut ()),
|
||||
}
|
||||
|
||||
impl Drop for TypeErasedBuffer {
|
||||
fn drop(&mut self) {
|
||||
let range = self.data.as_mut_ptr_range();
|
||||
let mut current = range.start;
|
||||
let end = range.end;
|
||||
|
||||
while current < end {
|
||||
// SAFETY: current pointer will either be the start of the buffer, or at the start of
|
||||
// the next item
|
||||
(self.fn_drop_item)(current.cast());
|
||||
|
||||
// SAFETY: the items are placed right after eachother
|
||||
current = unsafe { current.add(self.item_size) };
|
||||
}
|
||||
|
||||
// Safety: all of the events were just dropped
|
||||
unsafe { self.data.set_len(0) };
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeErasedBuffer {
|
||||
pub fn new<T: 'static>() -> Self {
|
||||
Self {
|
||||
type_id: TypeId::of::<T>(),
|
||||
item_size: mem::size_of::<T>(),
|
||||
data: Default::default(),
|
||||
// dropped immediately after the read
|
||||
fn_drop_item: |item| unsafe { item.cast::<T>().drop_in_place() },
|
||||
}
|
||||
}
|
||||
|
||||
pub fn push<T: 'static>(&mut self, value: T) {
|
||||
debug_assert!(TypeId::of::<T>() == self.type_id, "The type of the buffer does not match the type that was added to it");
|
||||
|
||||
// reserve enough bytes to store T
|
||||
let old_len = self.data.len();
|
||||
self.data.reserve(mem::size_of::<T>());
|
||||
|
||||
// Get a pointer to the end of the data.
|
||||
// SAFETY: we just reserved enough memory to store the data.
|
||||
let end_ptr = unsafe { self.data.as_mut_ptr().add(old_len) };
|
||||
|
||||
unsafe {
|
||||
// write the command and its runner into the buffer
|
||||
end_ptr.cast::<T>()
|
||||
// written unaligned to keep everything packed
|
||||
.write_unaligned(value);
|
||||
|
||||
// we wrote to the vec's buffer without using its api, so we need manually
|
||||
// set the length of the vec.
|
||||
self.data.set_len(old_len + mem::size_of::<T>());
|
||||
}
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.data.len() / self.item_size
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EventQueue {
|
||||
events: HashMap<TypeId, TypeErasedBuffer>,
|
||||
event_write_queue: HashMap<TypeId, TypeErasedBuffer>,
|
||||
}
|
||||
|
||||
impl Default for EventQueue {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl EventQueue {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
event_write_queue: HashMap::new(),
|
||||
events: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Trigger an event
|
||||
pub fn trigger_event<E>(&mut self, event: E)
|
||||
where
|
||||
E: Event
|
||||
{
|
||||
// the compiler wants me to explicit right here for some reason
|
||||
/* let default = || RefCell::new(Box::<Events::<E>>::default() as Box<dyn AsSyncAny>);
|
||||
|
||||
// Get, or create, a list of events of this type
|
||||
let type_id = event.type_id();
|
||||
let mut events = self.event_write_queue.entry(type_id)
|
||||
.or_insert_with(default)
|
||||
.borrow_mut();
|
||||
|
||||
// downcast resource as an events storage
|
||||
let e: &mut Events<E> = events.as_mut().as_any_mut().downcast_mut().unwrap();
|
||||
e.push_back(event); */
|
||||
|
||||
let type_id = event.type_id();
|
||||
let events = self.event_write_queue.entry(type_id)
|
||||
.or_insert_with(|| TypeErasedBuffer::new::<E>());
|
||||
|
||||
events.push(event);
|
||||
}
|
||||
|
||||
// Clear events, this should happen at the start of every tick since events are cloned
|
||||
// before being given to the reader.
|
||||
pub fn update_events(&mut self) {
|
||||
self.events.clear();
|
||||
|
||||
// get all keys of events
|
||||
let keys: Vec<TypeId> = self.event_write_queue.keys().copied().collect();
|
||||
|
||||
// remove all elements from self.event_write_queue and insert them to events
|
||||
for k in keys.into_iter() {
|
||||
let v = self.event_write_queue.remove(&k).unwrap();
|
||||
self.events.insert(k, v);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_events<E>(&self) -> Option<EventReader<E>>
|
||||
where
|
||||
E: Event
|
||||
{
|
||||
self.events.get(&TypeId::of::<E>())
|
||||
.map(|event| EventReader::new(event.clone()))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EventReader<E: Event> {
|
||||
buf: TypeErasedBuffer,
|
||||
range: Option<Range<*mut MaybeUninit<u8>>>,
|
||||
_marker: PhantomData<E>,
|
||||
}
|
||||
|
||||
impl<E: Event> EventReader<E> {
|
||||
fn new(buffer: TypeErasedBuffer) -> Self {
|
||||
Self {
|
||||
buf: buffer,
|
||||
range: None,
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.buf.len()
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Event> Iterator for EventReader<E> {
|
||||
type Item = E;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.range.is_none() {
|
||||
self.range = Some(self.buf.data.as_mut_ptr_range());
|
||||
}
|
||||
|
||||
let range = self.range.as_mut().unwrap();
|
||||
|
||||
if range.start < range.end {
|
||||
// Retrieve the event in the buffer.
|
||||
// SAFETY: current pointer will either be the start of the buffer, or at the start
|
||||
// of the next event.
|
||||
let event = unsafe { range.start.cast::<E>().read_unaligned() };
|
||||
|
||||
// Advance the pointer to be after this event.
|
||||
range.start = unsafe { range.start.add(mem::size_of::<E>()) };
|
||||
|
||||
Some(event)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct EventsPlugin;
|
||||
|
||||
impl Plugin for EventsPlugin {
|
||||
fn setup(&mut self, app: &mut crate::game::App) {
|
||||
app.world.add_resource(EventQueue::new());
|
||||
}
|
||||
}
|
|
@ -10,7 +10,7 @@ use tracing_subscriber::{
|
|||
util::SubscriberInitExt, fmt,
|
||||
};
|
||||
|
||||
use crate::{plugin::Plugin, render::renderer::Renderer, Stage, StagedExecutor};
|
||||
use crate::{event_cleaner_system, plugin::Plugin, render::renderer::Renderer, Event, Events, Stage, StagedExecutor};
|
||||
|
||||
#[derive(Clone, Copy, Hash, Debug)]
|
||||
pub enum GameStages {
|
||||
|
@ -214,4 +214,21 @@ impl App {
|
|||
.expect("No run function set");
|
||||
f(self);
|
||||
}
|
||||
|
||||
pub fn register_event<T: Event>(&mut self) {
|
||||
let world = &mut self.world;
|
||||
// only register the event if it isn't already registered.
|
||||
if !world.has_resource::<Events<T>>() {
|
||||
world.add_resource_default::<Events<T>>();
|
||||
let sys_name = format!("{}_event_cleaner_system", std::any::type_name::<T>().to_lowercase());
|
||||
self.add_system_to_stage(GameStages::First, &sys_name, event_cleaner_system::<T>, &[]);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn push_event<T: Event>(&mut self, event: T) {
|
||||
let world = &mut self.world;
|
||||
let mut events = world.try_get_resource_mut::<Events<T>>()
|
||||
.expect("missing events for event type! Must use `App::register_event` first");
|
||||
events.push_event(event);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
use std::{collections::HashMap, ops::Deref, hash::{Hash, DefaultHasher, Hasher}, fmt::Debug};
|
||||
use std::{collections::HashMap, hash::{Hash, DefaultHasher, Hasher}, fmt::Debug};
|
||||
|
||||
use glam::Vec2;
|
||||
use lyra_ecs::World;
|
||||
use lyra_ecs::query::{Res, ResMut};
|
||||
use lyra_reflect::Reflect;
|
||||
|
||||
use crate::{plugin::Plugin, game::GameStages, EventQueue};
|
||||
use crate::{game::GameStages, plugin::Plugin, EventReader};
|
||||
|
||||
use super::{Button, InputButtons, KeyCode, MouseButton, MouseMotion};
|
||||
|
||||
|
@ -497,64 +497,64 @@ impl ActionHandlerBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
fn actions_system(world: &mut World) -> anyhow::Result<()> {
|
||||
let keys = world.try_get_resource::<InputButtons<KeyCode>>()
|
||||
.map(|r| r.deref().clone());
|
||||
let mouse_events = world
|
||||
.try_get_resource::<EventQueue>()
|
||||
.and_then(|q| q.read_events::<MouseMotion>());
|
||||
//let mouse = world.try_get_resource()
|
||||
|
||||
//fn actions_system(world: &mut World) -> anyhow::Result<()> {
|
||||
fn actions_system(
|
||||
input_btns: Res<InputButtons<KeyCode>>,
|
||||
mut mouse_ev: EventReader<MouseMotion>,
|
||||
mut handler: ResMut<ActionHandler>,
|
||||
) -> anyhow::Result<()> {
|
||||
// clear the states of all axises each frame
|
||||
{
|
||||
let mut handler = world.try_get_resource_mut::<ActionHandler>()
|
||||
.expect("No Input Action handler was created in the world!");
|
||||
|
||||
let layout = handler.layouts.get(&handler.current_layout).expect("No active layout");
|
||||
let mapping = layout.mappings.get(&layout.active_mapping).expect("No active mapping");
|
||||
|
||||
for (action, _) in mapping.action_binds.clone().iter() {
|
||||
let action = handler.actions.get_mut(action).expect("Action name for binding is invalid!");
|
||||
if action.kind == ActionKind::Axis {
|
||||
action.state = ActionState::Axis(0.0);
|
||||
}
|
||||
let layout = handler.layouts.get(&handler.current_layout).expect("No active layout");
|
||||
let mapping = layout.mappings.get(&layout.active_mapping).expect("No active mapping");
|
||||
for (action, _) in mapping.action_binds.clone().iter() {
|
||||
let action = handler.actions.get_mut(action).expect("Action name for binding is invalid!");
|
||||
if action.kind == ActionKind::Axis {
|
||||
action.state = ActionState::Axis(0.0);
|
||||
}
|
||||
}
|
||||
|
||||
let motion_avg = if let Some(mouse_events) = mouse_events {
|
||||
// collect all events to a list
|
||||
let mouse_events = {
|
||||
let mut evs = vec![];
|
||||
while let Some(ev) = mouse_ev.read() {
|
||||
evs.push(ev.clone());
|
||||
}
|
||||
evs
|
||||
};
|
||||
|
||||
// get the average motion from the events that were collected last frame
|
||||
let motion_avg = if !mouse_events.is_empty() {
|
||||
let count = mouse_events.len();
|
||||
let mut sum = Vec2::ZERO;
|
||||
|
||||
for mm in mouse_events {
|
||||
sum += mm.delta;
|
||||
}
|
||||
Some(sum / count as f32)
|
||||
} else { None };
|
||||
|
||||
let mut handler = world.try_get_resource_mut::<ActionHandler>()
|
||||
.expect("No Input Action handler was created in the world!");
|
||||
sum / count as f32
|
||||
} else { Vec2::new(0.0, 0.0) };
|
||||
|
||||
let layout = handler.layouts.get(&handler.current_layout).expect("No active layout");
|
||||
let mapping = layout.mappings.get(&layout.active_mapping).expect("No active mapping");
|
||||
|
||||
for (action_lbl, binds) in mapping.action_binds.clone().iter() {
|
||||
let action = handler.actions.get_mut(action_lbl).expect("Action name for binding is invalid!");
|
||||
let mut new_state = None;
|
||||
|
||||
for bind in binds.iter() {
|
||||
match bind.source {
|
||||
ActionSource::Keyboard(key) => if let Some(keys) = &keys {
|
||||
ActionSource::Keyboard(key) => {
|
||||
// JustPressed needs to be first, since is_pressed includes buttons that
|
||||
// were just pressed.
|
||||
match action.kind {
|
||||
ActionKind::Button => {
|
||||
if keys.was_just_pressed(key) {
|
||||
if input_btns.was_just_pressed(key) {
|
||||
new_state = Some(ActionState::JustPressed(bind.modifier));
|
||||
} else if keys.is_pressed(key) {
|
||||
} else if input_btns.is_pressed(key) {
|
||||
new_state = Some(ActionState::Pressed(bind.modifier));
|
||||
}
|
||||
},
|
||||
ActionKind::Axis => {
|
||||
if keys.is_pressed(key) {
|
||||
if input_btns.is_pressed(key) {
|
||||
new_state = Some(ActionState::Axis(bind.modifier));
|
||||
}
|
||||
}
|
||||
|
@ -564,7 +564,7 @@ fn actions_system(world: &mut World) -> anyhow::Result<()> {
|
|||
ActionSource::Gamepad(_, _) => todo!(),
|
||||
ActionSource::Mouse(m) => match m {
|
||||
MouseInput::Button(_) => todo!(),
|
||||
MouseInput::Axis(a) => if let Some(motion_avg) = motion_avg {
|
||||
MouseInput::Axis(a) => {
|
||||
match a {
|
||||
MouseAxis::X => {
|
||||
new_state = Some(ActionState::Axis(motion_avg.x));
|
||||
|
|
|
@ -1,61 +1,63 @@
|
|||
use std::ptr::NonNull;
|
||||
use std::ops::Deref;
|
||||
|
||||
use glam::Vec2;
|
||||
use lyra_ecs::{World, system::IntoSystem};
|
||||
use winit::{event::MouseScrollDelta, keyboard::PhysicalKey};
|
||||
use lyra_ecs::query::ResMut;
|
||||
use winit::{event::{MouseScrollDelta, WindowEvent}, keyboard::PhysicalKey};
|
||||
|
||||
use crate::{EventQueue, plugin::Plugin, game::GameStages};
|
||||
use crate::{game::GameStages, plugin::Plugin, winit::DeviceEventPair, EventReader, EventWriter};
|
||||
|
||||
use super::{events::*, InputButtons, InputEvent};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct InputSystem;
|
||||
fn write_scroll_delta(mouse_scroll_ev: &mut EventWriter<MouseScroll>, delta: &MouseScrollDelta) {
|
||||
let event = match delta {
|
||||
MouseScrollDelta::LineDelta(x, y) => MouseScroll {
|
||||
unit: MouseScrollUnit::Line(Vec2::new(*x, *y)),
|
||||
},
|
||||
MouseScrollDelta::PixelDelta(delta) => MouseScroll {
|
||||
unit: MouseScrollUnit::Pixel(Vec2::new(delta.x as f32, delta.y as f32)),
|
||||
},
|
||||
};
|
||||
|
||||
impl InputSystem {
|
||||
pub fn process_event(&mut self, world: &mut World, event: &InputEvent) -> bool {
|
||||
let event_queue = world.try_get_resource_mut::<EventQueue>();
|
||||
if event_queue.is_none() {
|
||||
return false;
|
||||
}
|
||||
let mut event_queue = event_queue.unwrap();
|
||||
mouse_scroll_ev.write(event);
|
||||
}
|
||||
|
||||
match event {
|
||||
InputEvent::KeyboardInput { device_id, event, .. } => {
|
||||
pub fn input_system(
|
||||
mut key_code_res: ResMut<InputButtons<winit::keyboard::KeyCode>>,
|
||||
mut mouse_btn_res: ResMut<InputButtons<MouseButton>>,
|
||||
mut touches_res: ResMut<Touches>,
|
||||
mut window_ev: EventReader<WindowEvent>,
|
||||
mut device_ev: EventReader<DeviceEventPair>,
|
||||
mut mouse_scroll_ev: EventWriter<MouseScroll>,
|
||||
mut mouse_btn_ev: EventWriter<MouseButton>,
|
||||
mut mouse_exact_ev: EventWriter<MouseExact>,
|
||||
mut mouse_entered_ev: EventWriter<CursorEnteredWindow>,
|
||||
mut mouse_left_ev: EventWriter<CursorLeftWindow>,
|
||||
mut mouse_motion_ev: EventWriter<MouseMotion>,
|
||||
) -> anyhow::Result<()> {
|
||||
while let Some(event) = window_ev.read() {
|
||||
match event.deref() {
|
||||
WindowEvent::KeyboardInput { event, .. } => {
|
||||
if let PhysicalKey::Code(code) = event.physical_key {
|
||||
drop(event_queue);
|
||||
let mut e = world.get_resource_or_else(InputButtons::<winit::keyboard::KeyCode>::new);
|
||||
//let mut e = with_resource_mut(world, || InputButtons::<KeyCode>::new());
|
||||
e.add_input_from_winit(code, event.state);
|
||||
key_code_res.add_input_from_winit(code, event.state);
|
||||
}
|
||||
},
|
||||
InputEvent::CursorMoved { position, .. } => {
|
||||
WindowEvent::CursorMoved { position, .. } => {
|
||||
let exact = MouseExact {
|
||||
pos: Vec2::new(position.x as f32, position.y as f32)
|
||||
};
|
||||
|
||||
event_queue.trigger_event(exact);
|
||||
mouse_exact_ev.write(exact);
|
||||
},
|
||||
InputEvent::CursorEntered { .. } => {
|
||||
let event = CursorEnteredWindow {};
|
||||
event_queue.trigger_event(event);
|
||||
WindowEvent::CursorEntered { .. } => {
|
||||
mouse_entered_ev.write(CursorEnteredWindow);
|
||||
},
|
||||
InputEvent::CursorLeft { .. } => {
|
||||
let event = CursorLeftWindow {};
|
||||
event_queue.trigger_event(event);
|
||||
WindowEvent::CursorLeft { .. } => {
|
||||
mouse_left_ev.write(CursorLeftWindow);
|
||||
},
|
||||
InputEvent::MouseWheel { delta, .. } => {
|
||||
let event = match delta {
|
||||
MouseScrollDelta::LineDelta(x, y) => MouseScroll {
|
||||
unit: MouseScrollUnit::Line(Vec2::new(*x, *y)),
|
||||
},
|
||||
MouseScrollDelta::PixelDelta(delta) => MouseScroll {
|
||||
unit: MouseScrollUnit::Pixel(Vec2::new(delta.x as f32, delta.y as f32)),
|
||||
},
|
||||
};
|
||||
|
||||
event_queue.trigger_event(event);
|
||||
}, //MouseButton
|
||||
InputEvent::MouseInput { button, state, .. } => {
|
||||
WindowEvent::MouseWheel { delta, .. } => {
|
||||
write_scroll_delta(&mut mouse_scroll_ev, delta);
|
||||
},
|
||||
WindowEvent::MouseInput { button, state, .. } => {
|
||||
let button_event = match button {
|
||||
winit::event::MouseButton::Left => MouseButton::Left,
|
||||
winit::event::MouseButton::Right => MouseButton::Right,
|
||||
|
@ -65,15 +67,10 @@ impl InputSystem {
|
|||
winit::event::MouseButton::Other(v) => MouseButton::Other(*v),
|
||||
};
|
||||
|
||||
event_queue.trigger_event(button_event);
|
||||
drop(event_queue);
|
||||
|
||||
let mut e = world.get_resource_or_else(InputButtons::<MouseButton>::new);
|
||||
e.add_input_from_winit(button_event, *state);
|
||||
mouse_btn_ev.write(button_event);
|
||||
mouse_btn_res.add_input_from_winit(button_event, *state);
|
||||
},
|
||||
InputEvent::Touch(t) => {
|
||||
drop(event_queue);
|
||||
|
||||
WindowEvent::Touch(t) => {
|
||||
let touch = Touch {
|
||||
phase: TouchPhase::from(t.phase),
|
||||
location: Vec2::new(t.location.x as f32, t.location.y as f32),
|
||||
|
@ -81,53 +78,35 @@ impl InputSystem {
|
|||
finger_id: t.id,
|
||||
};
|
||||
|
||||
let mut touches = world.get_resource_or_else(Touches::new);
|
||||
touches.touches.push(touch);
|
||||
touches_res.touches.push(touch);
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::ecs::system::System for InputSystem {
|
||||
fn execute(&mut self, mut world: NonNull<World>) -> anyhow::Result<()> {
|
||||
let world = unsafe { world.as_mut() };
|
||||
let queue = world.try_get_resource_mut::<EventQueue>()
|
||||
.and_then(|q| q.read_events::<InputEvent>());
|
||||
while let Some(device) = device_ev.read() {
|
||||
match &device.event {
|
||||
winit::event::DeviceEvent::Motion { .. } => {
|
||||
// TODO: handle device motion events
|
||||
// A todo! isn't used since these are triggered alongside MouseMotion events
|
||||
}
|
||||
winit::event::DeviceEvent::MouseMotion { delta } => {
|
||||
let delta = MouseMotion {
|
||||
delta: Vec2::new(delta.0 as f32, delta.1 as f32)
|
||||
};
|
||||
|
||||
let mut e = world.get_resource_or_else(InputButtons::<winit::keyboard::KeyCode>::new);
|
||||
e.update();
|
||||
drop(e);
|
||||
|
||||
if queue.is_none() {
|
||||
return Ok(());
|
||||
mouse_motion_ev.write(delta);
|
||||
},
|
||||
winit::event::DeviceEvent::MouseWheel { delta } => {
|
||||
write_scroll_delta(&mut mouse_scroll_ev, delta);
|
||||
},
|
||||
_ => {
|
||||
todo!("unhandled device event: {:?}", device.event);
|
||||
}
|
||||
}
|
||||
|
||||
let events = queue.unwrap();
|
||||
for event in events {
|
||||
self.process_event(world, &event);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn world_access(&self) -> lyra_ecs::Access {
|
||||
lyra_ecs::Access::Write
|
||||
}
|
||||
|
||||
fn execute_deferred(&mut self, _: NonNull<World>) -> anyhow::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoSystem<()> for InputSystem {
|
||||
type System = Self;
|
||||
|
||||
fn into_system(self) -> Self::System {
|
||||
self
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Plugin that runs InputSystem
|
||||
|
@ -136,6 +115,18 @@ pub struct InputPlugin;
|
|||
|
||||
impl Plugin for InputPlugin {
|
||||
fn setup(&mut self, app: &mut crate::game::App) {
|
||||
app.add_system_to_stage(GameStages::PreUpdate, "input", InputSystem, &[]);
|
||||
app.add_resource(InputButtons::<winit::keyboard::KeyCode>::default());
|
||||
app.add_resource(InputButtons::<MouseButton>::default());
|
||||
app.add_resource(Touches::default());
|
||||
|
||||
app.register_event::<InputEvent>();
|
||||
app.register_event::<MouseScroll>();
|
||||
app.register_event::<MouseButton>();
|
||||
app.register_event::<MouseMotion>();
|
||||
app.register_event::<MouseExact>();
|
||||
app.register_event::<CursorEnteredWindow>();
|
||||
app.register_event::<CursorLeftWindow>();
|
||||
|
||||
app.add_system_to_stage(GameStages::PreUpdate, "input", input_system, &[]);
|
||||
}
|
||||
}
|
|
@ -1,5 +1,4 @@
|
|||
#![feature(hash_extract_if)]
|
||||
#![feature(lint_reasons)]
|
||||
#![feature(trait_alias)]
|
||||
#![feature(map_many_mut)]
|
||||
|
||||
|
@ -14,8 +13,8 @@ pub mod as_any;
|
|||
pub mod plugin;
|
||||
pub mod change_tracker;
|
||||
|
||||
pub mod events;
|
||||
pub use events::*;
|
||||
mod event;
|
||||
pub use event::*;
|
||||
|
||||
pub mod stage;
|
||||
pub use stage::*;
|
||||
|
|
|
@ -3,7 +3,6 @@ use lyra_resource::ResourceManager;
|
|||
|
||||
use crate::game::App;
|
||||
use crate::winit::WinitPlugin;
|
||||
use crate::EventsPlugin;
|
||||
use crate::DeltaTimePlugin;
|
||||
use crate::input::InputPlugin;
|
||||
use crate::render::window::WindowPlugin;
|
||||
|
@ -113,7 +112,6 @@ impl Plugin for DefaultPlugins {
|
|||
fn setup(&mut self, app: &mut App) {
|
||||
WinitPlugin::default().setup(app);
|
||||
CommandQueuePlugin.setup(app);
|
||||
EventsPlugin.setup(app);
|
||||
InputPlugin.setup(app);
|
||||
ResourceManagerPlugin.setup(app);
|
||||
WindowPlugin::default().setup(app);
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
use lyra_ecs::{query::{filter::Changed, Entities, ResMut}, Component, World};
|
||||
use lyra_ecs::{query::{filter::Changed, Entities, Res, View}, Component};
|
||||
use lyra_resource::Image;
|
||||
use tracing::error;
|
||||
use winit::window::{CustomCursor, Fullscreen, Window};
|
||||
|
||||
pub use winit::window::{CursorGrabMode, CursorIcon, Icon, Theme, WindowButtons, WindowLevel};
|
||||
|
||||
use crate::{change_tracker::Ct, plugin::Plugin, winit::WinitWindows};
|
||||
use crate::{plugin::Plugin, winit::WinitWindows};
|
||||
|
||||
#[derive(Default, Clone, Copy, PartialEq)]
|
||||
pub struct Area {
|
||||
|
@ -485,8 +485,12 @@ impl WindowOptions {
|
|||
}
|
||||
}
|
||||
|
||||
/// The state of the window last time it was changed.
|
||||
///
|
||||
/// This is used in [`window_sync_system`] to see what fields of [`WindowOptions`] changed
|
||||
/// when syncing the winit window with the component.
|
||||
#[derive(Clone, Component)]
|
||||
struct LastWindow {
|
||||
pub struct LastWindow {
|
||||
last: WindowOptions,
|
||||
}
|
||||
|
||||
|
@ -511,8 +515,8 @@ pub struct WindowPlugin {
|
|||
}
|
||||
|
||||
/// A system that syncs Winit Windows with [`WindowOptions`] components.
|
||||
pub fn window_sync_system(world: &mut World) -> anyhow::Result<()> {
|
||||
for (entity, opts, mut last, windows) in world.filtered_view_iter::<(Entities, &WindowOptions, &mut LastWindow, ResMut<WinitWindows>), Changed<WindowOptions>>() {
|
||||
pub fn window_sync_system(windows: Res<WinitWindows>, view: View<(Entities, &WindowOptions, &mut LastWindow), Changed<WindowOptions>>) -> anyhow::Result<()> {
|
||||
for (entity, opts, mut last) in view.iter() {
|
||||
let window = windows.get_entity_window(entity)
|
||||
.expect("entity's window is missing");
|
||||
|
||||
|
@ -644,9 +648,6 @@ pub fn window_sync_system(world: &mut World) -> anyhow::Result<()> {
|
|||
|
||||
impl Plugin for WindowPlugin {
|
||||
fn setup(&mut self, app: &mut crate::game::App) {
|
||||
let window_options = WindowOptions::default();
|
||||
|
||||
app.world.add_resource(Ct::new(window_options));
|
||||
app.with_system("window_sync", window_sync_system, &[]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,14 +4,34 @@ use async_std::task::block_on;
|
|||
use glam::IVec2;
|
||||
use lyra_ecs::Entity;
|
||||
use rustc_hash::FxHashMap;
|
||||
use tracing::{debug, error, info, warn};
|
||||
use winit::{application::ApplicationHandler, event::WindowEvent, event_loop::{ActiveEventLoop, EventLoop}, window::{Window, WindowAttributes, WindowId}};
|
||||
use tracing::{debug, error, warn};
|
||||
use winit::{
|
||||
application::ApplicationHandler,
|
||||
event::WindowEvent,
|
||||
event_loop::{ActiveEventLoop, EventLoop},
|
||||
window::{Window, WindowAttributes, WindowId},
|
||||
};
|
||||
|
||||
use crate::{game::{App, WindowState}, input::InputEvent, plugin::Plugin, render::{renderer::BasicRenderer, window::{PrimaryWindow, Size, WindowOptions}}, EventQueue};
|
||||
use crate::{
|
||||
game::{App, WindowState},
|
||||
plugin::Plugin,
|
||||
render::{
|
||||
renderer::BasicRenderer,
|
||||
window::{PrimaryWindow, Size, WindowOptions},
|
||||
},
|
||||
};
|
||||
|
||||
/// A struct that contains a [`DeviceEvent`](winit::event::DeviceEvent) with its source
|
||||
/// [`DeviceId`](winit::event::DeviceId).
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DeviceEventPair {
|
||||
pub device_src: winit::event::DeviceId,
|
||||
pub event: winit::event::DeviceEvent,
|
||||
}
|
||||
|
||||
pub struct WinitPlugin {
|
||||
/// The primary window that will be created.
|
||||
///
|
||||
///
|
||||
/// This will become `None` after the window is created. If you want to get the
|
||||
/// primary world later, query for an entity with the [`PrimaryWindow`] and
|
||||
/// [`WindowOptions`] components.
|
||||
|
@ -20,32 +40,32 @@ pub struct WinitPlugin {
|
|||
|
||||
impl Default for WinitPlugin {
|
||||
fn default() -> Self {
|
||||
Self { primary_window: Some(WindowOptions::default()) }
|
||||
Self {
|
||||
primary_window: Some(WindowOptions::default()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Plugin for WinitPlugin {
|
||||
fn setup(&mut self, app: &mut crate::game::App) {
|
||||
app.set_run_fn(winit_app_runner);
|
||||
|
||||
app.register_event::<WindowEvent>();
|
||||
app.register_event::<DeviceEventPair>();
|
||||
|
||||
if let Some(prim) = self.primary_window.take() {
|
||||
app.add_resource(WinitWindows::with_window(prim));
|
||||
} else {
|
||||
app.add_resource(WinitWindows::default());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn is_ready(&self, _app: &mut crate::game::App) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn complete(&self, _app: &mut crate::game::App) {
|
||||
|
||||
}
|
||||
|
||||
fn cleanup(&self, _app: &mut crate::game::App) {
|
||||
|
||||
}
|
||||
|
||||
fn complete(&self, _app: &mut crate::game::App) {}
|
||||
|
||||
fn cleanup(&self, _app: &mut crate::game::App) {}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
|
@ -65,7 +85,12 @@ impl WinitWindows {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn create_window(&mut self, event_loop: &ActiveEventLoop, entity: Entity, attr: WindowAttributes) -> Result<WindowId, winit::error::OsError> {
|
||||
pub fn create_window(
|
||||
&mut self,
|
||||
event_loop: &ActiveEventLoop,
|
||||
entity: Entity,
|
||||
attr: WindowAttributes,
|
||||
) -> Result<WindowId, winit::error::OsError> {
|
||||
let win = event_loop.create_window(attr)?;
|
||||
let id = win.id();
|
||||
|
||||
|
@ -77,37 +102,34 @@ impl WinitWindows {
|
|||
}
|
||||
|
||||
pub fn get_entity_window(&self, entity: Entity) -> Option<&Arc<Window>> {
|
||||
self.entity_to_window.get(&entity)
|
||||
self.entity_to_window
|
||||
.get(&entity)
|
||||
.and_then(|id| self.windows.get(id))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn winit_app_runner(app: App) {
|
||||
let evloop = EventLoop::new()
|
||||
.expect("failed to create winit EventLoop");
|
||||
let evloop = EventLoop::new().expect("failed to create winit EventLoop");
|
||||
|
||||
let mut winit_runner = WinitRunner {
|
||||
app,
|
||||
};
|
||||
evloop.run_app(&mut winit_runner)
|
||||
.expect("loop error");
|
||||
let mut winit_runner = WinitRunner { app };
|
||||
evloop.run_app(&mut winit_runner).expect("loop error");
|
||||
}
|
||||
|
||||
struct WinitRunner {
|
||||
app: App
|
||||
app: App,
|
||||
}
|
||||
|
||||
impl ApplicationHandler for WinitRunner {
|
||||
fn about_to_wait(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) {
|
||||
self.app.update();
|
||||
|
||||
let renderer = self.app.renderer.get_mut().expect("renderer was not initialized");
|
||||
let renderer = self
|
||||
.app
|
||||
.renderer
|
||||
.get_mut()
|
||||
.expect("renderer was not initialized");
|
||||
renderer.prepare(&mut self.app.world);
|
||||
|
||||
if let Some(mut event_queue) = self.app.world.try_get_resource_mut::<EventQueue>() {
|
||||
event_queue.update_events();
|
||||
}
|
||||
|
||||
match renderer.render() {
|
||||
Ok(_) => {}
|
||||
// Reconfigure the surface if lost
|
||||
|
@ -149,86 +171,107 @@ impl ApplicationHandler for WinitRunner {
|
|||
}
|
||||
}
|
||||
|
||||
fn device_event(
|
||||
&mut self,
|
||||
_: &ActiveEventLoop,
|
||||
device_src: winit::event::DeviceId,
|
||||
event: winit::event::DeviceEvent,
|
||||
) {
|
||||
self.app.push_event(DeviceEventPair { device_src, event });
|
||||
}
|
||||
|
||||
fn window_event(
|
||||
&mut self,
|
||||
event_loop: &winit::event_loop::ActiveEventLoop,
|
||||
window_id: winit::window::WindowId,
|
||||
event: WindowEvent,
|
||||
) {
|
||||
let windows = self.app.world.get_resource::<WinitWindows>();
|
||||
/* let windows = self.app.world.get_resource::<WinitWindows>();
|
||||
let window = match windows.windows.get(&window_id) {
|
||||
Some(w) => w.clone(),
|
||||
None => return,
|
||||
};
|
||||
drop(windows);
|
||||
drop(windows); */
|
||||
|
||||
// If try_from failed, that means that the WindowEvent is not an
|
||||
// input related event.
|
||||
if let Some(input_ev) = InputEvent::from_window_event(&event) {
|
||||
// Its possible to receive multiple input events before the update event for
|
||||
// the InputSystem is called, so we must use a queue for the events.
|
||||
if let Some(mut event_queue) = self.app.world.try_get_resource_mut::<EventQueue>() {
|
||||
event_queue.trigger_event(input_ev.clone());
|
||||
self.app.push_event(event.clone());
|
||||
match event {
|
||||
WindowEvent::ActivationTokenDone { .. } => todo!(),
|
||||
WindowEvent::Resized(physical_size) => {
|
||||
self.app.on_resize(physical_size);
|
||||
|
||||
let mut window_opts = self
|
||||
.app
|
||||
.world
|
||||
.get_resource::<WinitWindows>()
|
||||
.window_to_entity
|
||||
.get(&window_id)
|
||||
.and_then(|e| self.app.world.view_one::<&mut WindowOptions>(*e).get())
|
||||
.unwrap();
|
||||
window_opts.size = Size::new_physical(physical_size.width, physical_size.height);
|
||||
}
|
||||
} else {
|
||||
|
||||
match event {
|
||||
WindowEvent::ActivationTokenDone { serial, token } => todo!(),
|
||||
WindowEvent::Resized(physical_size) => {
|
||||
self.app.on_resize(physical_size);
|
||||
|
||||
let mut window_opts = self.app.world.get_resource::<WinitWindows>()
|
||||
.window_to_entity.get(&window_id)
|
||||
.and_then(|e| self.app.world.view_one::<&mut WindowOptions>(*e).get())
|
||||
.unwrap();
|
||||
window_opts.size = Size::new_physical(physical_size.width, physical_size.height);
|
||||
},
|
||||
WindowEvent::Moved(physical_position) => {
|
||||
let mut state = self.app.world.get_resource_or_else(WindowState::new);
|
||||
state.position = IVec2::new(physical_position.x, physical_position.y);
|
||||
},
|
||||
WindowEvent::CloseRequested => {
|
||||
self.app.on_exit();
|
||||
event_loop.exit();
|
||||
},
|
||||
WindowEvent::Destroyed => todo!(),
|
||||
WindowEvent::DroppedFile(path_buf) => todo!(),
|
||||
WindowEvent::HoveredFile(path_buf) => todo!(),
|
||||
WindowEvent::HoveredFileCancelled => todo!(),
|
||||
WindowEvent::Focused(focused) => {
|
||||
let mut window_opts = self.app.world.get_resource::<WinitWindows>()
|
||||
.window_to_entity.get(&window_id)
|
||||
.and_then(|e| self.app.world.view_one::<&mut WindowOptions>(*e).get())
|
||||
.unwrap();
|
||||
window_opts.focused = focused;
|
||||
},
|
||||
WindowEvent::ModifiersChanged(modifiers) => debug!("modifiers changed: {:?}", modifiers),
|
||||
WindowEvent::ScaleFactorChanged { scale_factor, .. } => {
|
||||
let mut window_opts = self.app.world.get_resource::<WinitWindows>()
|
||||
.window_to_entity.get(&window_id)
|
||||
.and_then(|e| self.app.world.view_one::<&mut WindowOptions>(*e).get())
|
||||
.unwrap();
|
||||
window_opts.scale_factor = scale_factor;
|
||||
},
|
||||
WindowEvent::ThemeChanged(theme) => {
|
||||
let mut window_opts = self.app.world.get_resource::<WinitWindows>()
|
||||
.window_to_entity.get(&window_id)
|
||||
.and_then(|e| self.app.world.view_one::<&mut WindowOptions>(*e).get())
|
||||
.unwrap();
|
||||
window_opts.theme = Some(theme);
|
||||
},
|
||||
WindowEvent::Occluded(occ) => {
|
||||
let mut window_opts = self.app.world.get_resource::<WinitWindows>()
|
||||
.window_to_entity.get(&window_id)
|
||||
.and_then(|e| self.app.world.view_one::<&mut WindowOptions>(*e).get())
|
||||
.unwrap();
|
||||
window_opts.occluded = occ;
|
||||
},
|
||||
WindowEvent::RedrawRequested => {
|
||||
//debug!("should redraw");
|
||||
},
|
||||
_ => {}
|
||||
WindowEvent::Moved(physical_position) => {
|
||||
let mut state = self.app.world.get_resource_or_else(WindowState::new);
|
||||
state.position = IVec2::new(physical_position.x, physical_position.y);
|
||||
}
|
||||
WindowEvent::CloseRequested => {
|
||||
self.app.on_exit();
|
||||
event_loop.exit();
|
||||
}
|
||||
WindowEvent::Destroyed => todo!(),
|
||||
WindowEvent::DroppedFile(_path_buf) => todo!(),
|
||||
WindowEvent::HoveredFile(_path_buf) => todo!(),
|
||||
WindowEvent::HoveredFileCancelled => todo!(),
|
||||
WindowEvent::Focused(focused) => {
|
||||
let mut window_opts = self
|
||||
.app
|
||||
.world
|
||||
.get_resource::<WinitWindows>()
|
||||
.window_to_entity
|
||||
.get(&window_id)
|
||||
.and_then(|e| self.app.world.view_one::<&mut WindowOptions>(*e).get())
|
||||
.unwrap();
|
||||
window_opts.focused = focused;
|
||||
}
|
||||
WindowEvent::ModifiersChanged(modifiers) => {
|
||||
debug!("modifiers changed: {:?}", modifiers)
|
||||
}
|
||||
WindowEvent::ScaleFactorChanged { scale_factor, .. } => {
|
||||
let mut window_opts = self
|
||||
.app
|
||||
.world
|
||||
.get_resource::<WinitWindows>()
|
||||
.window_to_entity
|
||||
.get(&window_id)
|
||||
.and_then(|e| self.app.world.view_one::<&mut WindowOptions>(*e).get())
|
||||
.unwrap();
|
||||
window_opts.scale_factor = scale_factor;
|
||||
}
|
||||
WindowEvent::ThemeChanged(theme) => {
|
||||
let mut window_opts = self
|
||||
.app
|
||||
.world
|
||||
.get_resource::<WinitWindows>()
|
||||
.window_to_entity
|
||||
.get(&window_id)
|
||||
.and_then(|e| self.app.world.view_one::<&mut WindowOptions>(*e).get())
|
||||
.unwrap();
|
||||
window_opts.theme = Some(theme);
|
||||
}
|
||||
WindowEvent::Occluded(occ) => {
|
||||
let mut window_opts = self
|
||||
.app
|
||||
.world
|
||||
.get_resource::<WinitWindows>()
|
||||
.window_to_entity
|
||||
.get(&window_id)
|
||||
.and_then(|e| self.app.world.view_one::<&mut WindowOptions>(*e).get())
|
||||
.unwrap();
|
||||
window_opts.occluded = occ;
|
||||
}
|
||||
WindowEvent::RedrawRequested => {
|
||||
//debug!("should redraw");
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue