use std::ops::Deref; use std::sync::mpsc::{self, SyncSender}; use std::sync::{OnceLock, PoisonError}; use intentional::Assert; use kludgine::app::winit::event::Modifiers; use kludgine::app::winit::keyboard::ModifiersState; /// Invokes the provided macro with a pattern that can be matched using this /// `macro_rules!` expression: `$($type:ident $field:tt $var:ident),+`, where `$type` is an /// identifier to use for the generic parameter and `$field` is the field index /// inside of the tuple. macro_rules! impl_all_tuples { ($macro_name:ident) => { $macro_name!(T0 0 t0); $macro_name!(T0 0 t0, T1 1 t1); $macro_name!(T0 0 t0, T1 1 t1, T2 2 t2); $macro_name!(T0 0 t0, T1 1 t1, T2 2 t2, T3 3 t3); $macro_name!(T0 0 t0, T1 1 t1, T2 2 t2, T3 3 t3, T4 4 t4); $macro_name!(T0 0 t0, T1 1 t1, T2 2 t2, T3 3 t3, T4 4 t4, T5 5 t5); } } /// Invokes a function with a clone of `self`. pub trait WithClone: Sized { /// The type that results from cloning. type Cloned; /// Maps `with` with the results of cloning `self`. fn with_clone(&self, with: impl FnOnce(Self::Cloned) -> R) -> R; } macro_rules! impl_with_clone { ($($name:ident $field:tt $var:ident),+) => { impl<'a, $($name: Clone,)+> WithClone for ($(&'a $name,)+) { type Cloned = ($($name,)+); fn with_clone(&self, with: impl FnOnce(Self::Cloned) -> R) -> R { with(($(self.$field.clone(),)+)) } } }; } impl<'a, T> WithClone for &'a T where T: Clone, { type Cloned = T; fn with_clone(&self, with: impl FnOnce(Self::Cloned) -> R) -> R { with((*self).clone()) } } impl_all_tuples!(impl_with_clone); pub trait ModifiersExt { fn primary(&self) -> bool; fn word_select(&self) -> bool; fn possible_shortcut(&self) -> bool; } impl ModifiersExt for ModifiersState { #[cfg(any(target_os = "macos", target_os = "ios"))] fn primary(&self) -> bool { self.super_key() } #[cfg(not(any(target_os = "macos", target_os = "ios")))] fn primary(&self) -> bool { self.control_key() } #[cfg(any(target_os = "macos", target_os = "ios"))] fn word_select(&self) -> bool { self.alt_key() } #[cfg(not(any(target_os = "macos", target_os = "ios")))] fn word_select(&self) -> bool { self.control_key() } fn possible_shortcut(&self) -> bool { self.control_key() || self.alt_key() || self.super_key() } } impl ModifiersExt for Modifiers { fn primary(&self) -> bool { self.state().primary() } fn word_select(&self) -> bool { self.state().word_select() } fn possible_shortcut(&self) -> bool { self.state().word_select() } } /// A [`OnceLock`]-based lazy initializer. pub struct Lazy { init: fn() -> T, once: OnceLock, } impl Lazy { /// Returns a type that initializes itself once upon being accessed. /// /// `init` is guaranteed to be called only once, but this type can't accept /// `FnOnce` generic types due to being unable to allocate a `Box` in /// `const` or being able to give a name to the type of a function so that /// users could use this type in static variables. pub const fn new(init: fn() -> T) -> Self { Self { init, once: OnceLock::new(), } } } impl Deref for Lazy { type Target = T; fn deref(&self) -> &Self::Target { self.once.get_or_init(self.init) } } pub trait IgnorePoison { type Unwrapped; fn ignore_poison(self) -> Self::Unwrapped; } impl IgnorePoison for Result> { type Unwrapped = T; fn ignore_poison(self) -> Self::Unwrapped { self.map_or_else(PoisonError::into_inner, |g| g) } } pub trait BgFunction: FnOnce() + Send + 'static {} pub fn run_in_bg(f: F) where F: BgFunction, { static BG_THREAD: Lazy>> = Lazy::new(|| { let (sender, receiver) = mpsc::sync_channel::>(16); std::thread::Builder::new() .name(String::from("background")) .spawn(move || { while let Ok(callback) = receiver.recv() { (callback)(); } }) .assert("error spawning bg thread"); sender }); BG_THREAD .send(Box::new(f)) .assert("background thread not running"); } impl BgFunction for T where T: FnOnce() + Send + 'static {}