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 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;
}
}