use super::{AppData, GraphicsInstance, Platform, ProxyAction, Result, Shared};
use crate::config::{Config, ConfigFactory};
#[allow(unused)] use crate::event::ConfigCx;
use crate::theme::Theme;
use crate::window::{WindowId, WindowIdFactory};
use std::cell::RefCell;
use std::rc::Rc;
use std::sync::mpsc;
use winit::event_loop::{EventLoop, EventLoopProxy};
#[cfg_attr(not(feature = "internal_doc"), doc(hidden))]
#[cfg_attr(docsrs, doc(cfg(internal_doc)))]
pub struct PreLaunchState {
config: Rc<RefCell<Config>>,
config_writer: Option<Box<dyn FnMut(&Config)>>,
el: EventLoop,
platform: Platform,
proxy_tx: mpsc::SyncSender<ProxyAction>,
proxy_rx: mpsc::Receiver<ProxyAction>,
window_id_factory: WindowIdFactory,
}
impl PreLaunchState {
pub fn new<C: ConfigFactory>(config: C) -> Result<Self> {
let mut cf = config;
let config = cf.read_config()?;
config.borrow_mut().init();
let el = EventLoop::new()?;
let platform = Platform::new(&el);
let (proxy_tx, proxy_rx) = mpsc::sync_channel(16);
Ok(PreLaunchState {
config,
config_writer: cf.writer(),
el,
platform,
proxy_tx,
proxy_rx,
window_id_factory: Default::default(),
})
}
#[inline]
pub fn config(&self) -> &Rc<RefCell<Config>> {
&self.config
}
#[inline]
pub fn next_window_id(&mut self) -> WindowId {
self.window_id_factory.make_next()
}
#[inline]
pub fn platform(&self) -> Platform {
self.platform
}
pub fn create_proxy(&self) -> Proxy {
Proxy::new(self.proxy_tx.clone(), self.el.create_proxy())
}
pub fn run<Data: AppData, G: GraphicsInstance + 'static, T: Theme<G::Shared> + 'static>(
self,
data: Data,
graphical: G,
theme: T,
windows: Vec<Box<super::Window<Data, G, T>>>,
) -> Result<()> {
let shared = Shared::<Data, _, _>::new(
self.platform,
graphical,
theme,
self.config,
self.config_writer,
create_waker(&self.el),
self.proxy_rx,
self.window_id_factory,
)?;
let l = super::Loop::new(windows, shared, data);
self.el.run_app(l)?;
Ok(())
}
}
impl Platform {
#[allow(clippy::needless_return)]
fn new(_el: &EventLoop) -> Platform {
#[cfg(target_os = "windows")]
return Platform::Windows;
#[cfg(any(
target_os = "linux",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd"
))]
{
cfg_if::cfg_if! {
if #[cfg(all(feature = "wayland", feature = "x11"))] {
use winit::platform::wayland::EventLoopExtWayland;
return if _el.is_wayland() {
Platform::Wayland
} else {
Platform::X11
};
} else if #[cfg(feature = "wayland")] {
return Platform::Wayland;
} else if #[cfg(feature = "x11")] {
return Platform::X11;
} else {
compile_error!("Please select a feature to build for unix: `x11`, `wayland`");
}
}
}
#[cfg(target_os = "macos")]
return Platform::MacOS;
#[cfg(target_os = "android")]
return Platform::Android;
#[cfg(target_os = "ios")]
return Platform::IOS;
#[cfg(target_arch = "wasm32")]
return Platform::Web;
}
}
fn create_waker(el: &EventLoop) -> std::task::Waker {
use std::sync::Arc;
use std::task::{RawWaker, RawWakerVTable, Waker};
let proxy = el.create_proxy();
let a: Arc<EventLoopProxy> = Arc::new(proxy);
let data = Arc::into_raw(a);
fn wake_async(proxy: &EventLoopProxy) {
proxy.wake_up();
}
const VTABLE: RawWakerVTable = RawWakerVTable::new(clone, wake, wake_by_ref, drop);
unsafe fn clone(data: *const ()) -> RawWaker {
unsafe {
let a = Arc::from_raw(data as *const EventLoopProxy);
let c = Arc::into_raw(a.clone());
let _do_not_drop = Arc::into_raw(a);
RawWaker::new(c as *const (), &VTABLE)
}
}
unsafe fn wake(data: *const ()) {
unsafe {
let a = Arc::from_raw(data as *const EventLoopProxy);
wake_async(&a);
}
}
unsafe fn wake_by_ref(data: *const ()) {
unsafe {
let a = Arc::from_raw(data as *const EventLoopProxy);
wake_async(&a);
let _do_not_drop = Arc::into_raw(a);
}
}
unsafe fn drop(data: *const ()) {
unsafe {
let _ = Arc::from_raw(data as *const EventLoopProxy);
}
}
let raw_waker = RawWaker::new(data as *const (), &VTABLE);
unsafe { Waker::from_raw(raw_waker) }
}
#[derive(Clone)]
pub struct Proxy {
tx: mpsc::SyncSender<ProxyAction>,
waker: EventLoopProxy,
}
pub struct ClosedError;
impl Proxy {
#[inline]
fn new(tx: mpsc::SyncSender<ProxyAction>, waker: EventLoopProxy) -> Self {
Proxy { tx, waker }
}
pub fn close(&self, id: WindowId) -> std::result::Result<(), ClosedError> {
self.tx
.send(ProxyAction::Close(id))
.map_err(|_| ClosedError)?;
self.waker.wake_up();
Ok(())
}
pub fn close_all(&self) -> std::result::Result<(), ClosedError> {
self.tx
.send(ProxyAction::CloseAll)
.map_err(|_| ClosedError)?;
self.waker.wake_up();
Ok(())
}
pub fn push<M: std::fmt::Debug + Send + 'static>(
&mut self,
msg: M,
) -> std::result::Result<(), ClosedError> {
self.tx
.send(ProxyAction::Message(kas::messages::SendErased::new(msg)))
.map_err(|_| ClosedError)?;
self.waker.wake_up();
Ok(())
}
}