iv/src/io/:
Steins7 e3fdbb142c Got the Controller working
* reworked iput system again
+ added test functions
2021-01-18 10:29:08 +01:00

178 lines
5.4 KiB
Plaintext

#[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<i32>,
id: usize,
receiver: mpsc::Receiver<Key>,
window: Window,
event_loop_proxy: EventLoopProxy<Signal>,
}
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<i32>) -> Result<WinitWindow, &'static str> {
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<Window> 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<i32> { &mut self.size }
fn window(&self) -> &Window { &self.window }
}
impl Input for WinitWindow {
fn read(&self, timeout: Duration) -> Result<Key, ReadError> {
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));
}
}