termcanvas 0.1.0

A lightweight Rust library for rendering pixels directly in the terminal.
Documentation
#![allow(dead_code)]

use crossterm::event::{self, Event, KeyCode, KeyEvent, KeyEventKind, PopKeyboardEnhancementFlags};
use crossterm::execute;
use crossterm::terminal::{disable_raw_mode, enable_raw_mode};
use std::collections::HashSet;
use std::time::Duration;

pub struct Input {
    held_keys: HashSet<KeyCode>,
    down_keys: HashSet<KeyCode>,
    up_keys: HashSet<KeyCode>,
    kitty_enabled: bool,
}

impl Input {
    pub fn new() -> Self {
        enable_raw_mode().unwrap();

        #[cfg(target_os = "windows")]
        let kitty_enabled = false;

        #[cfg(not(target_os = "windows"))]
        let kitty_enabled = {
            assert!(
                matches!(supports_keyboard_enhancement(), Ok(true)),
                "Terminal does not support kitty keyboard protocol"
            );
            execute!(
                std::io::stdout(),
                PushKeyboardEnhancementFlags(KeyboardEnhancementFlags::REPORT_EVENT_TYPES)
            )
            .unwrap();
            true
        };

        Self {
            held_keys: HashSet::new(),
            down_keys: HashSet::new(),
            up_keys: HashSet::new(),
            kitty_enabled,
        }
    }

    pub fn update(&mut self) -> std::io::Result<()> {
        self.down_keys.clear();
        self.up_keys.clear();
        while event::poll(Duration::from_millis(0))? {
            if let Event::Key(KeyEvent { code, kind, .. }) = event::read()? {
                match kind {
                    KeyEventKind::Press => {
                        if self.held_keys.insert(code) {
                            self.down_keys.insert(code);
                        }
                    }
                    KeyEventKind::Release => {
                        if self.held_keys.remove(&code) {
                            self.up_keys.insert(code);
                        }
                    }
                    _ => {}
                }
            }
        }
        Ok(())
    }

    pub fn is_key_pressed(&self, key: KeyCode) -> bool {
        self.held_keys.contains(&key)
    }

    pub fn is_key_down(&self, key: KeyCode) -> bool {
        self.down_keys.contains(&key)
    }

    pub fn is_key_up(&self, key: KeyCode) -> bool {
        self.up_keys.contains(&key)
    }
}

impl Drop for Input {
    fn drop(&mut self) {
        if self.kitty_enabled {
            let _ = execute!(std::io::stdout(), PopKeyboardEnhancementFlags);
        }
        disable_raw_mode().unwrap();
    }
}