use std::{
sync::mpsc,
thread,
time::Duration,
iter,
panic::{
catch_unwind,
AssertUnwindSafe,
},
};
use winit::{
event_loop::{
EventLoop,
EventLoopProxy,
ControlFlow,
},
event::Event,
monitor::MonitorHandle,
window::{
WindowAttributes,
Window,
},
error::OsError,
};
use crate::request::{
Request,
RequestMessage,
RequestCallback,
GetAvailableMonitors,
GetPrimaryMonitor,
CreateWindow,
};
#[cfg(feature = "proc")]
pub use winit_main_proc::main;
mod request;
pub mod reexports {
pub use winit::{
dpi,
error,
event,
monitor,
platform,
window,
};
}
enum Message {
Request(RequestMessage),
Exit,
Unblock,
}
#[derive(Clone)]
pub struct EventLoopHandle {
wake_sender: EventLoopProxy<()>,
msg_send: mpsc::Sender<Message>,
}
fn sleep_forever() -> ! {
loop {
thread::sleep(Duration::new(u64::MAX, 1_000_000_000 - 1));
}
}
impl EventLoopHandle {
fn request_wait<R>(&self, request: R) -> R::Response
where
R: Request,
RequestMessage: From<RequestCallback<R>>,
{
let (send_response, recv_response) = mpsc::channel();
let request = RequestMessage::from(RequestCallback {
request,
callback: send_response,
});
let _ = self.msg_send.send(Message::Request(request));
let _ = self.wake_sender.send_event(());
match recv_response.recv() {
Ok(response) => response,
Err(mpsc::RecvError) => sleep_forever(),
}
}
pub fn available_monitors(&self) -> Vec<MonitorHandle> {
self.request_wait(GetAvailableMonitors)
}
pub fn primary_monitor(&self) -> Option<MonitorHandle> {
self.request_wait(GetPrimaryMonitor)
}
pub fn create_window(&self, attributes: WindowAttributes) -> Result<Window, OsError> {
self.request_wait(CreateWindow(attributes))
}
}
pub struct Blocker(mpsc::Sender<Message>);
impl Drop for Blocker {
fn drop(&mut self) {
let _ = self.0.send(Message::Unblock);
}
}
impl Blocker {
pub fn unblock(self) {
drop(self)
}
}
pub struct EventReceiver(mpsc::Receiver<Event<'static, Blocker>>);
impl EventReceiver {
pub fn recv(&self) -> Event<'static, Blocker> {
match self.0.recv() {
Ok(event) => event,
Err(mpsc::RecvError) => sleep_forever(),
}
}
pub fn recv_timeout(&self, timeout: Duration) -> Option<Event<'static, Blocker>> {
match self.0.recv_timeout(timeout) {
Ok(event) => Some(event),
Err(mpsc::RecvTimeoutError::Timeout) => None,
Err(mpsc::RecvTimeoutError::Disconnected) => sleep_forever(),
}
}
pub fn try_recv(&self) -> Option<Event<'static, Blocker>> {
match self.0.try_recv() {
Ok(event) => Some(event),
Err(mpsc::TryRecvError::Empty) => None,
Err(mpsc::TryRecvError::Disconnected) => sleep_forever(),
}
}
pub fn iter<'a>(&'a self) -> impl Iterator<Item=Event<'static, Blocker>> + 'a {
iter::from_fn(move || Some(self.recv()))
}
pub fn try_iter<'a>(&'a self) -> impl Iterator<Item=Event<'static, Blocker>> + 'a {
iter::from_fn(move || self.try_recv())
}
}
pub fn run<F>(f: F) -> !
where
F: FnOnce(EventLoopHandle, EventReceiver) + Send + 'static
{
let event_loop = EventLoop::with_user_event();
let (event_send, event_recv) = mpsc::channel();
let (msg_send, msg_recv) = mpsc::channel();
let msg_send_1 = msg_send;
let msg_send_2 = msg_send_1.clone();
let msg_send_3 = msg_send_1.clone();
let wake_sender_1 = event_loop.create_proxy();
let wake_sender_2 = event_loop.create_proxy();
thread::spawn(move || {
let handle = EventLoopHandle {
wake_sender: wake_sender_1,
msg_send: msg_send_1,
};
let receiver = EventReceiver(event_recv);
let _ = catch_unwind(AssertUnwindSafe(move || f(handle, receiver)));
let _ = msg_send_2.send(Message::Exit);
let _ = wake_sender_2.send_event(());
});
event_loop.run(move |event, window_target, control_flow| {
*control_flow = ControlFlow::Wait;
let event = match event.to_static() {
Some(event) => event,
None => return, };
match event.map_nonuser_event() {
Ok(nonuser_event) => {
let triggers_block = matches!(
&nonuser_event,
&Event::RedrawRequested(_)
);
let _ = event_send.send(nonuser_event);
if triggers_block {
let blocker = Blocker(msg_send_3.clone());
let _ = event_send.send(Event::UserEvent(blocker));
'block: for msg in msg_recv.iter() {
match msg {
Message::Request(request) => {
request.run_respond(window_target);
}
Message::Unblock => {
break 'block;
},
Message::Exit => {
*control_flow = ControlFlow::Exit;
}
};
}
}
}
Err(Event::UserEvent(())) => {
for msg in msg_recv.try_iter() {
match msg {
Message::Request(request) => {
request.run_respond(window_target);
}
Message::Unblock => unreachable!("not blocked"),
Message::Exit => {
*control_flow = ControlFlow::Exit;
}
};
}
}
Err(_) => unreachable!(),
};
});
}