#[allow(unused_imports)] use log::{debug, error, info, trace, warn}; use std::{ thread, sync::mpsc, time::Duration, }; use winit::{ dpi::PhysicalSize, event_loop::{EventLoop, EventLoopProxy}, window::{WindowBuilder, Window}, }; //TODO fix that use crate::{ io::{Output, Input, Key, Signal, ReadError}, utils::Rect, }; #[derive(Debug)] pub struct WinitWindow { pub name: String, size: Rect, id: usize, receiver: mpsc::Receiver, window: Window, event_loop_proxy: EventLoopProxy, } impl Drop for WinitWindow { fn drop(&mut self) { use winit::event::Event; // kill event_loop if self.event_loop_proxy.send_event(Signal::Exit).is_err() { warn!("EventLoop thread is dead before Exit signal"); } //while match self.read(Duration::from_millis(1)) { // Ok(Key::Closed) => false, // _ => true, //} {} } } impl WinitWindow { pub fn new(title: &str, size: Rect) -> Result { use winit::platform::unix::EventLoopExtUnix; debug!("Creating window"); let name = title.to_string(); let id = 0; //Since we can't move the EventLoop from one thread to another, we need to create it in the //right thread and then move the Window back to the main thread instead let cloned_name = name.clone(); let (tx, rx) = mpsc::channel(); let (tmp_tx, tmp_rx) = mpsc::sync_channel(1); let builder = thread::Builder::new().name(title); //the EventLoop hijacks the thread so there is no need to join it later... thread::spawn(move || { trace!("Creating Window in EventLoop thread"); //winit doesn't like us creating the EventLoop in another thread either so we have to //drop crossplatform compatibility :/ let event_loop = EventLoop::new_any_thread(); let window = WindowBuilder::new() .with_inner_size(PhysicalSize {width: size.w, height: size.h}) .with_title(cloned_name) .build(&event_loop).unwrap(); let event_loop_proxy = event_loop.create_proxy(); trace!("Sending Window back to main thread"); tmp_tx.send((window, event_loop_proxy)).unwrap(); event_loop.run(move |event: winit::event::Event<'_, Signal>, _, control_flow| { use winit::{ event_loop::ControlFlow, event::Event, event, }; *control_flow = ControlFlow::Wait; match event { Event::LoopDestroyed => { tx.send(Key::Closed).unwrap(); debug!("Closed EventLoop"); }, Event::WindowEvent{window_id: _, event} => match event { event::WindowEvent::CloseRequested => { debug!("Close requested"); tx.send(Key::Close).unwrap(); }, event::WindowEvent::CursorMoved{position, ..} => { tx.send(Key::MouseMove{ x: position.x, y: position.y, }).unwrap(); }, _ => (), }, Event::UserEvent(signal) => match signal { Signal::Exit => { debug!("Stopping input thread..."); *control_flow = ControlFlow::Exit; }, Signal::Test => { tx.send(Key::Test).unwrap(); }, }, _ => (), }}) }); let (window, event_loop_proxy) = tmp_rx.recv().unwrap(); trace!("Received Window in main thread"); Ok(Self { name, size, id, receiver: rx, window, event_loop_proxy, }) } } impl Output for WinitWindow { fn get_id(&self) -> usize { self.id } fn set_id(&mut self, id: usize) { self.id = id; } fn size(&mut self) -> &mut Rect { &mut self.size } fn window(&self) -> &Window { &self.window } } impl Input for WinitWindow { fn read(&self, timeout: Duration) -> Result { match self.receiver.recv_timeout(timeout) { Ok(key) => Ok(key), Err(_) => Err(ReadError::Timeout), } } fn write(&self, signal: Signal) { self.event_loop_proxy.send_event(signal) .map_err(|_| "Could not send Signal to EventLoop").unwrap(); } } #[cfg(test)] mod tests { use super::*; use crate::utils::Rect; #[test] fn test_new_drop() { let _window1 = WinitWindow::new("IV", Rect {w: 1280, h: 720}).unwrap(); let _window2 = WinitWindow::new("IV", Rect {w: 1280, h: 720}).unwrap(); panic!("test"); } #[test] fn test_read_write() { let window = WinitWindow::new("IV", Rect {w: 1280, h: 720}).unwrap(); window.write(Signal::Test); let input = window.read(Duration::from_millis(1)).unwrap(); assert!(matches!(input, Key::Test)); } }