use crate::core::Context;
use crate::input::{Gamepad, Gamepads, Keyboard};
use std::cell::RefCell;
use std::cmp::Ordering;
use std::fmt::{Debug, Formatter};
use std::ops::Deref;
use std::rc::Rc;
#[derive(Clone)]
pub struct VirtualSource(Rc<Inner>);
impl Debug for VirtualSource {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("GamepadSource").finish_non_exhaustive()
}
}
impl PartialEq for VirtualSource {
#[inline]
fn eq(&self, other: &Self) -> bool {
Rc::ptr_eq(&self.0, &other.0)
}
}
impl PartialOrd for VirtualSource {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Rc::as_ptr(&self.0).partial_cmp(&Rc::as_ptr(&other.0))
}
}
struct Inner {
pub keyboard: Keyboard,
pub gamepads: Gamepads,
pub selector: RefCell<GamepadSelector>,
}
#[derive(Debug, Clone, PartialEq)]
pub enum GamepadSelector {
LastActive,
Specific(Option<Gamepad>),
}
impl VirtualSource {
pub fn new(ctx: &Context, selector: GamepadSelector) -> Self {
Self::new_ext(&ctx.keyboard, &ctx.gamepads, selector)
}
pub fn new_ext(keyboard: &Keyboard, gamepads: &Gamepads, selector: GamepadSelector) -> Self {
Self(Rc::new(Inner {
keyboard: keyboard.clone(),
gamepads: gamepads.clone(),
selector: RefCell::new(selector),
}))
}
#[inline]
pub(crate) fn keyboard(&self) -> &Keyboard {
&self.0.keyboard
}
pub fn last_active(ctx: &Context) -> Self {
Self::new(ctx, GamepadSelector::LastActive)
}
pub fn specific(ctx: &Context, gamepad: impl Into<Option<Gamepad>>) -> Self {
Self::new(ctx, GamepadSelector::Specific(gamepad.into()))
}
pub fn set_selector(&self, selector: GamepadSelector) {
self.0.selector.replace(selector);
}
pub fn set_specific(&self, gamepad: impl Into<Option<Gamepad>>) {
self.set_selector(GamepadSelector::Specific(gamepad.into()));
}
pub fn set_last_active(&self) {
self.0.selector.replace(GamepadSelector::LastActive);
}
pub fn read<R>(&self, f: impl FnOnce(&Gamepad) -> R) -> Option<R> {
match self.0.selector.borrow().deref() {
GamepadSelector::LastActive => self.0.gamepads.last_active().as_ref().map(f),
GamepadSelector::Specific(pad) => pad.as_ref().map(f),
}
}
}