use std::{pin::Pin, sync::Arc, task::{Context, Poll, Waker}};
use crossbeam::channel::{Receiver, Sender, unbounded};
use futures::Stream;
use parking_lot::RwLock;
use sdl2_sys::*;
use xecs::{EntityId, System, World};
#[derive(Debug, Copy, Clone)]
pub struct EventInfo {
pub timestamp: u32,
pub window_id : EntityId,
pub event_type: WindowEventType,
}
#[repr(u32)]
#[derive(Debug, Copy, Clone)]
pub enum WindowEventType {
None,
Shown,
Hidden,
Exposed,
Moved(u32, u32),
Resized(u32, u32),
SizeChanged(u32, u32),
Minimized,
Maximized,
Restored,
Enter,
Leave,
FocusGained,
FocusLost,
Close,
TakeFocus,
}
impl EventInfo {
pub(in crate) fn from_sdl_event(window_id : EntityId,event : SDL_WindowEvent) -> Self {
EventInfo {
timestamp: event.timestamp,
window_id,
event_type : match event.event as u32 {
x if x == SDL_WindowEventID::SDL_WINDOWEVENT_SHOWN as u32 => WindowEventType::Shown,
x if x == SDL_WindowEventID::SDL_WINDOWEVENT_HIDDEN as u32 => WindowEventType::Hidden,
x if x == SDL_WindowEventID::SDL_WINDOWEVENT_EXPOSED as u32 => WindowEventType::Exposed,
x if x == SDL_WindowEventID::SDL_WINDOWEVENT_MOVED as u32 => {
WindowEventType::Moved(event.data1 as _, event.data2 as _)
}
x if x == SDL_WindowEventID::SDL_WINDOWEVENT_RESIZED as u32 => {
WindowEventType::Resized(event.data1 as _, event.data2 as _)
}
x if x == SDL_WindowEventID::SDL_WINDOWEVENT_SIZE_CHANGED as u32 => {
WindowEventType::SizeChanged(event.data1 as _, event.data2 as _)
}
x if x == SDL_WindowEventID::SDL_WINDOWEVENT_MINIMIZED as u32 => {
WindowEventType::Minimized
}
x if x == SDL_WindowEventID::SDL_WINDOWEVENT_MAXIMIZED as u32 => {
WindowEventType::Maximized
}
x if x == SDL_WindowEventID::SDL_WINDOWEVENT_RESTORED as u32 => {
WindowEventType::Restored
}
x if x == SDL_WindowEventID::SDL_WINDOWEVENT_ENTER as u32 => WindowEventType::Enter,
x if x == SDL_WindowEventID::SDL_WINDOWEVENT_LEAVE as u32 => WindowEventType::Leave,
x if x == SDL_WindowEventID::SDL_WINDOWEVENT_FOCUS_GAINED as u32 => {
WindowEventType::FocusGained
}
x if x == SDL_WindowEventID::SDL_WINDOWEVENT_FOCUS_LOST as u32 => {
WindowEventType::FocusLost
}
x if x == SDL_WindowEventID::SDL_WINDOWEVENT_CLOSE as u32 => WindowEventType::Close,
x if x == SDL_WindowEventID::SDL_WINDOWEVENT_TAKE_FOCUS as u32 => {
WindowEventType::TakeFocus
}
_ => WindowEventType::None,
},
}
}
}
pub(in crate) struct WindowEventInner{
pub(in crate) tx : Sender<EventInfo>,
pub(in crate) waker : Waker
}
pub struct WindowEvent{
world : Arc<RwLock<World>>,
rx : Option<(EntityId,Receiver<EventInfo>)>
}
impl WindowEvent {
pub(in crate) fn from_world(world : Arc<RwLock<World>>) -> Self {
WindowEvent{
world,
rx : Option::None
}
}
}
impl Stream for WindowEvent {
type Item = EventInfo;
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
if self.rx.is_none() {
let (tx,rx) = unbounded();
let id = {
let world = self.world.read();
let waker = cx.waker().clone();
world.create_entity()
.attach(WindowEventInner{ tx, waker })
.into_id()
};
self.rx.replace((id,rx));
}
let (_,rx) = self.rx.as_ref().unwrap();
if let Ok(info) = rx.try_recv() {
Poll::Ready(Some(info))
} else {
Poll::Pending
}
}
}
impl System for WindowEvent {
fn world(&self) -> Arc<RwLock<World>> {
self.world.clone()
}
}
impl Drop for WindowEvent{
fn drop(&mut self) {
let world = self.world.read();
if let Some((id,_)) = self.rx {
world.remove_entity(id)
}
}
}