tinybit 0.3.0

terminal drawing / game library
Documentation
//! Event handling.
//!
//! ```
//! # use tinybit::*;
//! # use tinybit::events::{Event, EventModel};
//! let (_tx, events) = events::events::<()>(EventModel::Fps(20));
//! for event in events {
//!     match event {
//!         Event::Tick => {
//! #          break
//!         }
//!         _ => {}
//!     }
//! }
//! ```
use std::sync::mpsc::{self, Receiver, Sender};
use std::thread;
use std::time::Duration;

use crossterm::event::{read, Event as CrossTermEvent};

pub use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};

type Rx<T> = Receiver<Event<T>>;
type Tx<T> = Sender<Event<T>>;

/// Event. Either a tick event or a key press event
#[derive(Debug, Clone, Copy)]
pub enum Event<T> {
    /// Generated for every frame
    Tick,

    /// A key press
    Key(KeyEvent),

    /// Terminal resize event
    Resize(u16, u16),

    /// User defined event
    User(T),
}

/// Events producer
pub struct Events<T> {
    rx: Rx<T>,
    blocking: bool,
}

impl<T> Iterator for Events<T> {
    type Item = Event<T>;

    fn next(&mut self) -> Option<Self::Item> {
        if self.blocking {
            self.rx.recv().ok()
        } else {
            self.rx.try_recv().ok()
        }
    }
}

/// The type of events to listen for.
pub enum EventModel {
    /// Generate a tick every N milliseconds
    Fps(u64),
    /// Block until an event is raised
    Blocking,
    /// Non blocking
    NonBlocking,
}

/// Produce events.
///
/// ```
/// # use tinybit::*;
/// # use tinybit::events::{Event, EventModel};
/// let (_tx, events) = events::events::<()> (EventModel::Fps(20)); 
/// for event in events {
///     match event {
///         Event::Tick => {
/// #          break
///         }
///         _ => {}
///     }
/// }
/// ```
pub fn events<T: Send + 'static>(event_model: EventModel) -> (Tx<T>, Events<T>) {
    let (tx, rx) = mpsc::channel();

    // Input events
    let tx_clone = tx.clone();
    thread::spawn(move || loop {
        if let Ok(ev) = read() {
            match ev {
                CrossTermEvent::Key(k) => {
                    let _ = tx_clone.send(Event::Key(k));
                }
                CrossTermEvent::Resize(w, h) => {
                    let _ = tx_clone.send(Event::Resize(w, h));
                }
                _ => {}
            }
        }
    });

    if let EventModel::Fps(fps) = event_model {
        // Frames
        let tx = tx.clone();
        thread::spawn(move || loop {
            let _ = tx.send(Event::Tick);
            thread::sleep(Duration::from_millis(1000 / fps));
        });
    }

    let blocking = match event_model {
        EventModel::NonBlocking => false,
        _ => true,
    };

    (tx, Events { rx, blocking })
}