use alloc::boxed::Box;
use core::ffi::c_void;
use core::marker::PhantomData;
use core::sync::atomic::{AtomicBool, AtomicU32, AtomicUsize, Ordering};
use oxivgl_sys::*;
use crate::enums::Key;
use crate::group::Group;
use crate::widgets::WidgetError;
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct Point {
pub x: i32,
pub y: i32,
}
pub struct Indev {
ptr: *mut lv_indev_t,
}
impl core::fmt::Debug for Indev {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Indev").finish_non_exhaustive()
}
}
impl Indev {
pub fn active() -> Option<Self> {
let ptr = unsafe { lv_indev_active() };
if ptr.is_null() {
None
} else {
Some(Self { ptr })
}
}
pub fn get_vect(&self) -> Point {
let mut pt: lv_point_t = unsafe { core::mem::zeroed() };
unsafe { lv_indev_get_vect(self.ptr, &mut pt) };
Point { x: pt.x, y: pt.y }
}
pub fn short_click_streak(&self) -> u8 {
unsafe { lv_indev_get_short_click_streak(self.ptr) }
}
}
const KEYPAD_QUEUE_CAP: usize = 8;
#[derive(Debug)]
pub struct KeypadState {
held: AtomicU32,
queue: [AtomicU32; KEYPAD_QUEUE_CAP],
head: AtomicUsize,
tail: AtomicUsize,
release_pending: AtomicBool,
release_key: AtomicU32,
}
impl KeypadState {
pub const fn new() -> Self {
Self {
held: AtomicU32::new(0),
queue: [const { AtomicU32::new(0) }; KEYPAD_QUEUE_CAP],
head: AtomicUsize::new(0),
tail: AtomicUsize::new(0),
release_pending: AtomicBool::new(false),
release_key: AtomicU32::new(0),
}
}
pub fn press(&self, key: Key) {
self.held.store(key.0, Ordering::Relaxed);
}
pub fn release(&self) {
self.held.store(0, Ordering::Relaxed);
}
pub fn send(&self, key: Key) {
let head = self.head.load(Ordering::Acquire);
let tail = self.tail.load(Ordering::Relaxed);
if tail.wrapping_sub(head) >= KEYPAD_QUEUE_CAP {
return; }
self.queue[tail % KEYPAD_QUEUE_CAP].store(key.0, Ordering::Relaxed);
self.tail.store(tail.wrapping_add(1), Ordering::Release);
}
pub fn has_pending(&self) -> bool {
self.head.load(Ordering::Acquire) != self.tail.load(Ordering::Acquire)
|| self.release_pending.load(Ordering::Acquire)
}
fn dequeue(&self) -> Option<u32> {
let tail = self.tail.load(Ordering::Acquire);
let head = self.head.load(Ordering::Relaxed);
if head == tail {
return None;
}
let key = self.queue[head % KEYPAD_QUEUE_CAP].load(Ordering::Relaxed);
self.head.store(head.wrapping_add(1), Ordering::Release);
Some(key)
}
fn queue_nonempty(&self) -> bool {
self.head.load(Ordering::Relaxed) != self.tail.load(Ordering::Acquire)
}
}
impl Default for KeypadState {
fn default() -> Self {
Self::new()
}
}
pub struct KeypadIndev {
ptr: *mut lv_indev_t,
state: &'static KeypadState,
_not_send: PhantomData<*const ()>,
}
impl core::fmt::Debug for KeypadIndev {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("KeypadIndev").finish_non_exhaustive()
}
}
impl KeypadIndev {
pub fn new(state: &'static KeypadState) -> Result<Self, WidgetError> {
Self::create(state, false)
}
pub fn new_event(state: &'static KeypadState) -> Result<Self, WidgetError> {
Self::create(state, true)
}
fn create(state: &'static KeypadState, event_mode: bool) -> Result<Self, WidgetError> {
let ptr = unsafe { lv_indev_create() };
if ptr.is_null() {
return Err(WidgetError::LvglNullPointer);
}
unsafe {
lv_indev_set_type(ptr, lv_indev_type_t_LV_INDEV_TYPE_KEYPAD);
lv_indev_set_read_cb(ptr, Some(keypad_read_cb));
lv_indev_set_user_data(ptr, state as *const KeypadState as *mut c_void);
if event_mode {
lv_indev_set_mode(ptr, lv_indev_mode_t_LV_INDEV_MODE_EVENT);
}
}
Ok(Self { ptr, state, _not_send: PhantomData })
}
pub fn with_repeat(self, after: core::time::Duration, every: core::time::Duration) -> Self {
let after = after.as_millis().min(u16::MAX as u128) as u16;
let every = every.as_millis().min(u16::MAX as u128) as u16;
unsafe {
lv_indev_set_long_press_time(self.ptr, after);
lv_indev_set_long_press_repeat_time(self.ptr, every);
}
self
}
pub fn set_group(&self, group: &Group) -> &Self {
unsafe { lv_indev_set_group(self.ptr, group.raw_ptr()) };
self
}
pub fn read(&self) -> &Self {
let mut budget = 2 * KEYPAD_QUEUE_CAP + 1;
loop {
unsafe { lv_indev_read(self.ptr) };
budget -= 1;
if !self.state.has_pending() || budget == 0 {
break;
}
}
self
}
}
impl Drop for KeypadIndev {
fn drop(&mut self) {
unsafe { lv_indev_delete(self.ptr) };
}
}
unsafe extern "C" fn keypad_read_cb(indev: *mut lv_indev_t, data: *mut lv_indev_data_t) {
if indev.is_null() || data.is_null() {
return;
}
let state = unsafe { lv_indev_get_user_data(indev) } as *const KeypadState;
if state.is_null() {
return;
}
let st = unsafe { &*state };
if st.release_pending.swap(false, Ordering::AcqRel) {
let k = st.release_key.load(Ordering::Relaxed);
unsafe {
(*data).key = k;
(*data).state = lv_indev_state_t_LV_INDEV_STATE_RELEASED;
(*data).continue_reading = st.queue_nonempty();
}
return;
}
if let Some(k) = st.dequeue() {
st.release_key.store(k, Ordering::Relaxed);
st.release_pending.store(true, Ordering::Release);
unsafe {
(*data).key = k;
(*data).state = lv_indev_state_t_LV_INDEV_STATE_PRESSED;
(*data).continue_reading = true; }
return;
}
let h = st.held.load(Ordering::Relaxed);
unsafe {
(*data).key = h;
(*data).state = if h != 0 {
lv_indev_state_t_LV_INDEV_STATE_PRESSED
} else {
lv_indev_state_t_LV_INDEV_STATE_RELEASED
};
(*data).continue_reading = false;
}
}
#[derive(Debug)]
pub struct PointerState {
xy: AtomicU32,
pressed: AtomicBool,
}
impl PointerState {
pub const fn new() -> Self {
Self {
xy: AtomicU32::new(0),
pressed: AtomicBool::new(false),
}
}
pub fn touch(&self, x: u16, y: u16) {
self.xy.store(((x as u32) << 16) | y as u32, Ordering::Relaxed);
self.pressed.store(true, Ordering::Release);
}
pub fn release(&self) {
self.pressed.store(false, Ordering::Release);
}
fn sample(&self) -> (i32, i32, bool) {
let pressed = self.pressed.load(Ordering::Acquire);
let xy = self.xy.load(Ordering::Relaxed);
(((xy >> 16) & 0xffff) as i32, (xy & 0xffff) as i32, pressed)
}
}
impl Default for PointerState {
fn default() -> Self {
Self::new()
}
}
pub struct PointerIndev {
ptr: *mut lv_indev_t,
closure: Option<*mut *mut PointerReadFn>,
_not_send: PhantomData<*const ()>,
}
type PointerReadFn = dyn FnMut() -> Option<(u16, u16)>;
impl core::fmt::Debug for PointerIndev {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("PointerIndev").finish_non_exhaustive()
}
}
impl PointerIndev {
pub fn new(state: &'static PointerState) -> Result<Self, WidgetError> {
let ptr = unsafe { lv_indev_create() };
if ptr.is_null() {
return Err(WidgetError::LvglNullPointer);
}
unsafe {
lv_indev_set_type(ptr, lv_indev_type_t_LV_INDEV_TYPE_POINTER);
lv_indev_set_read_cb(ptr, Some(pointer_state_read_cb));
lv_indev_set_user_data(ptr, state as *const PointerState as *mut c_void);
}
Ok(Self { ptr, closure: None, _not_send: PhantomData })
}
pub fn new_with(read: impl FnMut() -> Option<(u16, u16)> + 'static) -> Result<Self, WidgetError> {
let boxed: Box<PointerReadFn> = Box::new(read);
let raw: *mut PointerReadFn = Box::into_raw(boxed);
let outer = Box::into_raw(Box::new(raw));
let ptr = unsafe { lv_indev_create() };
if ptr.is_null() {
unsafe {
let _ = Box::from_raw(*Box::from_raw(outer));
}
return Err(WidgetError::LvglNullPointer);
}
unsafe {
lv_indev_set_type(ptr, lv_indev_type_t_LV_INDEV_TYPE_POINTER);
lv_indev_set_read_cb(ptr, Some(pointer_closure_read_cb));
lv_indev_set_user_data(ptr, outer as *mut c_void);
}
Ok(Self { ptr, closure: Some(outer), _not_send: PhantomData })
}
}
impl Drop for PointerIndev {
fn drop(&mut self) {
unsafe { lv_indev_delete(self.ptr) };
if let Some(outer) = self.closure {
unsafe {
let inner: Box<*mut PointerReadFn> = Box::from_raw(outer);
let _ = Box::from_raw(*inner);
}
}
}
}
unsafe extern "C" fn pointer_state_read_cb(indev: *mut lv_indev_t, data: *mut lv_indev_data_t) {
if indev.is_null() || data.is_null() {
return;
}
let state = unsafe { lv_indev_get_user_data(indev) } as *const PointerState;
if state.is_null() {
return;
}
let (x, y, pressed) = unsafe { &*state }.sample();
unsafe {
(*data).point.x = x;
(*data).point.y = y;
(*data).state = if pressed {
lv_indev_state_t_LV_INDEV_STATE_PRESSED
} else {
lv_indev_state_t_LV_INDEV_STATE_RELEASED
};
(*data).continue_reading = false;
}
}
unsafe extern "C" fn pointer_closure_read_cb(indev: *mut lv_indev_t, data: *mut lv_indev_data_t) {
if indev.is_null() || data.is_null() {
return;
}
let outer = unsafe { lv_indev_get_user_data(indev) } as *mut *mut PointerReadFn;
if outer.is_null() {
return;
}
let read: &mut PointerReadFn = unsafe { &mut **outer };
let touched = read();
unsafe {
match touched {
Some((x, y)) => {
(*data).point.x = x as i32;
(*data).point.y = y as i32;
(*data).state = lv_indev_state_t_LV_INDEV_STATE_PRESSED;
}
None => {
(*data).state = lv_indev_state_t_LV_INDEV_STATE_RELEASED;
}
}
(*data).continue_reading = false;
}
}