use ::alloc::boxed::Box;
use ::core::{ffi::c_void, marker::PhantomData, ptr::NonNull};
use embedded_graphics::prelude::Point;
use crate::warn;
#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Default)]
pub enum InputState {
Pressed,
#[default]
Released,
}
impl InputState {
fn as_lv_indev_state(&self) -> lightvgl_sys::lv_indev_state_t {
match self {
InputState::Pressed => lightvgl_sys::lv_indev_state_t_LV_INDEV_STATE_PRESSED,
InputState::Released => lightvgl_sys::lv_indev_state_t_LV_INDEV_STATE_RELEASED,
}
}
}
#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Default)]
pub enum BufferStatus {
#[default]
Once,
Buffered,
}
#[derive(Clone, Copy, Default)]
pub struct InputEvent<T: InputType> {
pub status: BufferStatus,
pub state: InputState,
pub data: T::DataType,
}
impl<T: InputType> InputEvent<T> {
pub const fn new(data: T::DataType) -> Self {
InputEvent {
status: BufferStatus::Once,
state: InputState::Released,
data,
}
}
}
pub trait InputType {
type DataType;
fn as_lv_indev_type() -> lightvgl_sys::lv_indev_type_t;
fn set_lv_indev_data(event_data: &Self::DataType, data: &mut lightvgl_sys::lv_indev_data_t);
}
#[derive(Clone, Copy, Default)]
pub struct Pointer;
impl InputType for Pointer {
type DataType = Point;
fn as_lv_indev_type() -> lightvgl_sys::lv_indev_type_t {
lightvgl_sys::lv_indev_type_t_LV_INDEV_TYPE_POINTER
}
fn set_lv_indev_data(event: &Self::DataType, data: &mut lightvgl_sys::lv_indev_data_t) {
data.point.x = event.x;
data.point.y = event.y;
}
}
pub struct Keypad;
impl InputType for Keypad {
type DataType = u32;
fn as_lv_indev_type() -> lightvgl_sys::lv_indev_type_t {
lightvgl_sys::lv_indev_type_t_LV_INDEV_TYPE_KEYPAD
}
fn set_lv_indev_data(event_data: &Self::DataType, data: &mut lightvgl_sys::lv_indev_data_t) {
data.key = *event_data;
}
}
pub struct Encoder;
impl InputType for Encoder {
type DataType = ();
fn as_lv_indev_type() -> lightvgl_sys::lv_indev_type_t {
lightvgl_sys::lv_indev_type_t_LV_INDEV_TYPE_ENCODER
}
fn set_lv_indev_data(_event_data: &Self::DataType, _data: &mut lightvgl_sys::lv_indev_data_t) {
unimplemented!("Encoders are not yet supported");
}
}
pub struct Button;
impl InputType for Button {
type DataType = ();
fn as_lv_indev_type() -> lightvgl_sys::lv_indev_type_t {
lightvgl_sys::lv_indev_type_t_LV_INDEV_TYPE_BUTTON
}
fn set_lv_indev_data(_event_data: &Self::DataType, _data: &mut lightvgl_sys::lv_indev_data_t) {
unimplemented!("Buttons are not yet supported");
}
}
#[allow(dead_code)]
pub struct InputDevice<T: InputType> {
raw: NonNull<lightvgl_sys::lv_indev_t>,
r#type: PhantomData<T>,
}
impl<T: InputType> InputDevice<T> {
pub fn create<F>(read_cb: F) -> Self
where
F: FnMut() -> InputEvent<T>,
{
unsafe {
let raw = NonNull::new(lightvgl_sys::lv_indev_create()).unwrap();
lightvgl_sys::lv_indev_set_type(raw.as_ptr(), T::as_lv_indev_type());
lightvgl_sys::lv_indev_set_read_cb(raw.as_ptr(), Some(read_input::<F, T>));
lightvgl_sys::lv_indev_set_user_data(
raw.as_ptr(),
Box::into_raw(Box::new(read_cb)) as *mut c_void,
);
Self {
raw,
r#type: PhantomData,
}
}
}
}
unsafe extern "C" fn read_input<F, T>(
indev: *mut lightvgl_sys::lv_indev_t,
data: *mut lightvgl_sys::lv_indev_data_t,
) where
T: InputType,
F: FnMut() -> InputEvent<T>,
{
unsafe {
if !(*indev).user_data.is_null() {
let callback = &mut *((*indev).user_data as *mut F);
let event = callback();
match event.status {
BufferStatus::Once => {
(*data).continue_reading = false;
}
BufferStatus::Buffered => {
(*data).continue_reading = true;
}
};
T::set_lv_indev_data(&event.data, data.as_mut().unwrap());
(*data).state = event.state.as_lv_indev_state();
} else {
warn!("Input callback user data was null, this should never happen!");
}
}
}