use std::marker::PhantomData;
use std::sync::Arc;
use arboard::Clipboard;
use kludgine::app::{AppEvent, AsApplication};
use parking_lot::{Mutex, MutexGuard};
use crate::animation;
use crate::fonts::FontCollection;
use crate::window::sealed::WindowCommand;
use crate::window::WindowHandle;
pub struct PendingApp {
app: kludgine::app::PendingApp<WindowCommand>,
cushy: Cushy,
}
impl PendingApp {
pub fn new<Runtime: AppRuntime>(runtime: Runtime) -> Self {
Self {
app: kludgine::app::PendingApp::default(),
cushy: Cushy::new(BoxedRuntime(Box::new(runtime))),
}
}
#[must_use]
pub const fn cushy(&self) -> &Cushy {
&self.cushy
}
}
impl Run for PendingApp {
fn run(self) -> crate::Result {
let _guard = self.cushy.enter_runtime();
animation::spawn(self.cushy.clone());
self.app.run()
}
}
impl Default for PendingApp {
fn default() -> Self {
Self::new(DefaultRuntime::default())
}
}
impl AsApplication<AppEvent<WindowCommand>> for PendingApp {
fn as_application(&self) -> &dyn kludgine::app::Application<AppEvent<WindowCommand>> {
self.app.as_application()
}
fn as_application_mut(&mut self) -> &mut dyn kludgine::app::Application<AppEvent<WindowCommand>>
where
AppEvent<WindowCommand>: kludgine::app::Message,
{
self.app.as_application_mut()
}
}
pub trait AppRuntime: Send + Clone + 'static {
type Guard<'a>;
fn enter(&self) -> Self::Guard<'_>;
}
#[derive(Debug, Clone, Default)]
pub struct DefaultRuntime {
#[cfg(feature = "tokio")]
tokio: TokioRuntime,
_private: (),
}
impl AppRuntime for DefaultRuntime {
type Guard<'a> = DefaultRuntimeGuard<'a>;
fn enter(&self) -> Self::Guard<'_> {
DefaultRuntimeGuard {
#[cfg(feature = "tokio")]
_tokio: self.tokio.enter(),
_phantom: PhantomData,
}
}
}
pub struct DefaultRuntimeGuard<'a> {
#[cfg(feature = "tokio")]
_tokio: ::tokio::runtime::EnterGuard<'a>,
_phantom: PhantomData<&'a ()>,
}
#[cfg(feature = "tokio")]
mod tokio {
use std::future::Future;
use std::ops::Deref;
use std::task::Poll;
use std::thread;
use tokio::runtime::{self, Handle};
use super::AppRuntime;
use crate::Lazy;
#[derive(Debug, Clone)]
pub struct TokioRuntime {
pub(crate) handle: Handle,
}
impl From<Handle> for TokioRuntime {
fn from(handle: Handle) -> Self {
Self { handle }
}
}
static TOKIO: Lazy<Handle> = Lazy::new(|| {
#[cfg(feature = "tokio-multi-thread")]
let mut rt = runtime::Builder::new_multi_thread();
#[cfg(not(feature = "tokio-multi-thread"))]
let mut rt = runtime::Builder::new_current_thread();
let runtime = rt
.enable_all()
.build()
.expect("failure to initialize tokio");
let handle = runtime.handle().clone();
thread::Builder::new()
.name(String::from("tokio"))
.spawn(move || {
runtime.block_on(BlockForever);
})
.expect("error spawning tokio thread");
handle
});
impl Default for TokioRuntime {
fn default() -> Self {
Self {
handle: TOKIO.clone(),
}
}
}
impl Deref for TokioRuntime {
type Target = Handle;
fn deref(&self) -> &Self::Target {
&self.handle
}
}
struct BlockForever;
impl Future for BlockForever {
type Output = ();
fn poll(
self: std::pin::Pin<&mut Self>,
_cx: &mut std::task::Context<'_>,
) -> Poll<Self::Output> {
Poll::<()>::Pending
}
}
impl AppRuntime for TokioRuntime {
type Guard<'a> = tokio::runtime::EnterGuard<'a>;
fn enter(&self) -> Self::Guard<'_> {
self.handle.enter()
}
}
}
#[cfg(feature = "tokio")]
pub use tokio::TokioRuntime;
struct BoxedRuntime(Box<dyn BoxableRuntime>);
impl Clone for BoxedRuntime {
fn clone(&self) -> Self {
self.0.cloned()
}
}
trait BoxableRuntime: Send {
fn enter_runtime(&self) -> RuntimeGuard<'_>;
fn cloned(&self) -> BoxedRuntime;
}
impl<T> BoxableRuntime for T
where
T: AppRuntime,
for<'a> T::Guard<'a>: BoxableGuard<'a>,
{
fn enter_runtime(&self) -> RuntimeGuard<'_> {
RuntimeGuard(Box::new(AppRuntime::enter(self)))
}
fn cloned(&self) -> BoxedRuntime {
BoxedRuntime(Box::new(self.clone()))
}
}
#[allow(dead_code)]
pub struct RuntimeGuard<'a>(Box<dyn BoxableGuard<'a> + 'a>);
trait BoxableGuard<'a> {}
impl<'a, T> BoxableGuard<'a> for T {}
#[derive(Clone)]
pub struct Cushy {
pub(crate) clipboard: Option<Arc<Mutex<Clipboard>>>,
pub(crate) fonts: FontCollection,
runtime: BoxedRuntime,
}
impl Cushy {
fn new(runtime: BoxedRuntime) -> Self {
Self {
clipboard: Clipboard::new()
.ok()
.map(|clipboard| Arc::new(Mutex::new(clipboard))),
fonts: FontCollection::default(),
runtime,
}
}
#[must_use]
pub fn clipboard_guard(&self) -> Option<MutexGuard<'_, Clipboard>> {
self.clipboard.as_ref().map(|mutex| mutex.lock())
}
#[must_use]
pub fn fonts(&self) -> &FontCollection {
&self.fonts
}
#[must_use]
pub fn enter_runtime(&self) -> RuntimeGuard<'_> {
self.runtime.0.enter_runtime()
}
}
impl Default for Cushy {
fn default() -> Self {
Self::new(BoxedRuntime(Box::<DefaultRuntime>::default()))
}
}
pub trait Application: AsApplication<AppEvent<WindowCommand>> {
fn cushy(&self) -> &Cushy;
fn as_app(&self) -> App;
}
impl Application for PendingApp {
fn cushy(&self) -> &Cushy {
&self.cushy
}
fn as_app(&self) -> App {
App {
app: Some(self.app.as_app()),
cushy: self.cushy.clone(),
}
}
}
#[derive(Clone)]
pub struct App {
app: Option<kludgine::app::App<WindowCommand>>,
cushy: Cushy,
}
impl Application for App {
fn cushy(&self) -> &Cushy {
&self.cushy
}
fn as_app(&self) -> App {
self.clone()
}
}
impl AsApplication<AppEvent<WindowCommand>> for App {
fn as_application(&self) -> &dyn kludgine::app::Application<AppEvent<WindowCommand>> {
self.app
.as_ref()
.map(AsApplication::as_application)
.expect("no app")
}
fn as_application_mut(&mut self) -> &mut dyn kludgine::app::Application<AppEvent<WindowCommand>>
where
AppEvent<WindowCommand>: kludgine::app::Message,
{
self.app
.as_mut()
.map(AsApplication::as_application_mut)
.expect("no app")
}
}
pub trait Run: Sized {
fn run(self) -> crate::Result;
}
pub trait Open: Sized {
fn open<App>(self, app: &mut App) -> crate::Result<Option<WindowHandle>>
where
App: Application + ?Sized;
fn run_in(self, app: PendingApp) -> crate::Result;
}