use avr_oxide::hal::generic::port::{Pin, PinMode, InterruptMode, PinIdentity};
use avr_oxide::event::{EventSink, EventSource, OxideEvent, OxideEventEnvelope};
use core::marker::PhantomData;
use core::ops::DerefMut;
use core::cell::RefCell;
use ufmt::derive::uDebug;
use avr_oxide::devices::internal::StaticShareable;
use oxide_macros::Persist;
use avr_oxide::hal::generic::callback::IsrCallback;
#[cfg(feature="alloc_ftr")]
use avr_oxide::alloc::boxed::Box;
use avr_oxide::devices::UsesPin;
use avr_oxide::panic_if_none;
use avr_oxide::util::OwnOrBorrowMut;
#[derive(PartialEq,Eq,Clone,Copy,uDebug,Persist)]
pub enum ButtonState {
Pressed,
Released,
Unknown
}
pub struct Button<'b, P, S>
where
P: 'static + Pin,
S: EventSink
{
pin: OwnOrBorrowMut<'static,P>,
phantom: PhantomData<S>,
on_click: RefCell<Option<Box<dyn FnMut(PinIdentity,ButtonState)->() + 'b>>>
}
impl<'b,P,S> StaticShareable for Button<'b, P, S>
where
P: 'static + Pin,
S: EventSink
{}
impl<'b,P,S> UsesPin<P> for Button<'b,P,S>
where
P: Pin,
S: 'static + EventSink
{
fn using<OP: Into<OwnOrBorrowMut<'static, P>>>(pin: OP) -> Self {
let pin : OwnOrBorrowMut<P> = pin.into();
pin.set_mode(PinMode::InputFloating);
pin.set_interrupt_mode(InterruptMode::BothEdges);
Button {
pin,
phantom: PhantomData::default(),
on_click: RefCell::new(None)
}
}
}
impl<'b,P,S> Button<'b,P,S>
where
P: Pin,
S: 'static + EventSink
{
#[cfg(feature="alloc_ftr")]
pub fn on_click(&self, bf: Box<dyn FnMut(PinIdentity, ButtonState) -> () + 'b>) {
self.on_click.replace(Some(bf));
}
pub fn pullup(self, enable: bool) -> Self {
match enable {
true => self.pin.set_mode(PinMode::InputPullup),
false => self.pin.set_mode(PinMode::InputFloating)
}
self
}
pub fn is_pressed(&self) -> bool {
!self.pin.get()
}
pub fn state(&self) -> ButtonState {
match self.pin.get() {
true => ButtonState::Pressed,
false => ButtonState::Released
}
}
}
impl<P,S> EventSource for Button<'_,P,S>
where
P: Pin,
S: EventSink
{
fn listen(&'static self) {
self.pin.listen(IsrCallback::WithData(|_source, id, state, udata|{
S::event(OxideEventEnvelope::to(unsafe { &*(panic_if_none!(udata) as *const Button<P,S> as *const dyn EventSource) },
OxideEvent::ButtonEvent(id,
match state {
true => ButtonState::Pressed,
false => ButtonState::Released
})));
}, self as *const dyn core::any::Any ))
}
fn process_event(&self, evt: OxideEvent) {
match (self.on_click.borrow_mut().deref_mut(), evt) {
(Some(f), OxideEvent::ButtonEvent(source, state)) => {
(*f)(source,state)
},
_ => {}
}
}
}