use conquer_once::spin::OnceCell;
use core::{
pin::Pin,
task::{Context, Poll},
};
use crossbeam_queue::ArrayQueue;
use futures_util::{stream::Stream, task::AtomicWaker};
static SCANCODE_QUEUE: OnceCell<ArrayQueue<u8>> = OnceCell::uninit();
static WAKER: AtomicWaker = AtomicWaker::new();
pub(crate) fn add_scancode(scancode: u8)
{
if let Ok(queue) = SCANCODE_QUEUE.try_get()
{
if let Err(_) = queue.push(scancode)
{
mango_core::println!("WARNING: scancode queue full; dropping keyboard input");
}
else
{
WAKER.wake();
}
}
else
{
mango_core::println!("WARNING: scancode queue uninitialized");
}
}
pub struct ScancodeStream
{
_private: (),
}
impl ScancodeStream
{
pub fn new() -> Self
{
if !SCANCODE_QUEUE.is_initialized()
{
SCANCODE_QUEUE
.try_init_once(|| ArrayQueue::new(100))
.expect("ScancodeStream::new should only be called once");
}
ScancodeStream { _private: () }
}
}
impl Stream for ScancodeStream
{
type Item = u8;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Option<u8>>
{
let queue = SCANCODE_QUEUE
.try_get()
.expect("scancode queue not initialized");
if let Some(scancode) = queue.pop()
{
return Poll::Ready(Some(scancode));
}
WAKER.register(&cx.waker());
match queue.pop()
{
Some(scancode) =>
{
WAKER.take();
Poll::Ready(Some(scancode))
}
None => Poll::Pending,
}
}
}