use std::sync::Arc;
use std::sync::atomic::{AtomicU8, Ordering};
use std::thread::spawn;
use winit::window::WindowBuilder;
use crossbeam_utils::atomic::AtomicCell;
use flume::{TryRecvError, TrySendError, unbounded};
use crate::event_loop::{ControlFlow, SharedControlFlow};
use crate::event::{Event, UserEvent};
use crate::messages::{AppProxyRegisterInfo, ProxyRegister, ProxyRegisterBody, ProxyRegisterInfo, ProxyRequest, ProxyResponse, REGISTER_PROXY};
pub fn run(rest: impl FnOnce() + Send + 'static) -> ! {
let (register_proxy, recv_register) = unbounded();
unsafe {
REGISTER_PROXY = Some(register_proxy);
}
let mut proxy_channels = Vec::new();
EXIT_FLAG.with(|exit_flag| exit_flag.store(1, Ordering::Release));
spawn(rest);
winit::event_loop::EventLoop::<UserEvent>::with_user_event().run(move |event, window_target, control_flow| {
let (event, physical_size) = Event::from(event);
for ProxyRegister(info) in recv_register.try_iter() {
if let Some(info) = info.upgrade() {
let control_flow = Arc::new(AtomicCell::new(ControlFlow::Poll));
let (proxy_send, recv_from_proxy) = unbounded();
let (send_to_proxy, proxy_recv) = unbounded();
proxy_channels.push(AppProxyRegisterInfo {
recv_from_proxy,
send_to_proxy,
control_flow: control_flow.clone()
});
match info.take() {
ProxyRegisterBody::Init => {},
ProxyRegisterBody::Polled { waker } => waker.wake(),
ProxyRegisterBody::Ready { info: _ } => unreachable!("proxy event loop registered twice")
}
info.store(ProxyRegisterBody::Ready {
info: ProxyRegisterInfo {
control_flow,
send: proxy_send,
recv: proxy_recv,
}
});
}
}
let mut shared_control_flow = SharedControlFlow::Wait;
let mut proxy_idxs_to_remove = Vec::new();
for (proxy_idx, AppProxyRegisterInfo { control_flow, recv_from_proxy, send_to_proxy}) in proxy_channels.iter_mut().enumerate() {
loop {
let request = match recv_from_proxy.try_recv() {
Ok(request) => request,
Err(TryRecvError::Empty) => break,
Err(TryRecvError::Disconnected) => {
proxy_idxs_to_remove.push(proxy_idx);
break
}
};
let response = match request {
ProxyRequest::SpawnWindow { configure } => {
ProxyResponse::SpawnWindow { result: configure(WindowBuilder::new()).build(&window_target) }
}
ProxyRequest::RunOnMainThread { action } => {
ProxyResponse::RunOnMainThread { return_value: action() }
}
};
match send_to_proxy.try_send(response) {
Ok(_) => (),
Err(TrySendError::Full(_)) => unreachable!("event loop channel (unbounded) full?"),
Err(TrySendError::Disconnected(_)) => {
proxy_idxs_to_remove.push(proxy_idx);
break
}
}
}
match send_to_proxy.try_send(ProxyResponse::Event(event.clone())) {
Ok(_) => (),
Err(TrySendError::Full(_)) => unreachable!("event loop channel (unbounded) full?"),
Err(TrySendError::Disconnected(_)) => proxy_idxs_to_remove.push(proxy_idx)
}
match control_flow.load() {
ControlFlow::Poll => shared_control_flow = shared_control_flow.min(SharedControlFlow::Poll),
ControlFlow::Wait => shared_control_flow = shared_control_flow.min(SharedControlFlow::Wait),
ControlFlow::WaitUntil(instant) => shared_control_flow = shared_control_flow.min(SharedControlFlow::WaitUntil(instant)),
ControlFlow::ExitLocal => {
}
ControlFlow::ExitApp => shared_control_flow = shared_control_flow.min(SharedControlFlow::ExitApp),
}
}
for proxy_to_remove in proxy_idxs_to_remove.into_iter().rev() {
proxy_channels.remove(proxy_to_remove);
}
event.into(physical_size);
*control_flow = match shared_control_flow {
SharedControlFlow::Wait => winit::event_loop::ControlFlow::Wait,
SharedControlFlow::Poll => winit::event_loop::ControlFlow::Poll,
SharedControlFlow::WaitUntil(instant) => winit::event_loop::ControlFlow::WaitUntil(instant),
SharedControlFlow::ExitApp => winit::event_loop::ControlFlow::Exit,
};
if EXIT_FLAG.with(|exit_flag| exit_flag.load(Ordering::Acquire)) == 2 {
*control_flow = winit::event_loop::ControlFlow::Exit;
}
})
}
pub fn exit() {
if EXIT_FLAG.with(|exit_flag| exit_flag.load(Ordering::Acquire)) == 0 {
std::process::exit(0);
}
EXIT_FLAG.with(|exit_flag| exit_flag.store(2, Ordering::Release));
}
thread_local! {
static EXIT_FLAG: Arc<AtomicU8> = Arc::new(AtomicU8::new(0));
}