mango-hal 0.2.1

Hardware Abstraction Layer for the mango operationg system.
Documentation
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();

/// Called by the keyboard interrupt handler
///
/// Must not block or allocate.
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");

    // fast path
    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,
    }
  }
}