From 3bc00f5f3f030acc8dbbf8018f6c2a99561ca709 Mon Sep 17 00:00:00 2001 From: tomsmeding Date: Sun, 10 Feb 2019 20:37:02 +0100 Subject: Update organisation and document --- src/core.rs | 292 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 292 insertions(+) create mode 100644 src/core.rs (limited to 'src/core.rs') diff --git a/src/core.rs b/src/core.rs new file mode 100644 index 0000000..9e67722 --- /dev/null +++ b/src/core.rs @@ -0,0 +1,292 @@ +use std::char; +use std::io; +use std::marker::PhantomData; +use std::sync::atomic::{AtomicBool, Ordering}; + +use crate::bindings; +use crate::util; + +pub const KEY_TAB: i32 = bindings::KEY_TAB as i32; +pub const KEY_LF: i32 = bindings::KEY_LF as i32; +pub const KEY_CR: i32 = bindings::KEY_CR as i32; +pub const KEY_ESC: i32 = bindings::KEY_ESC as i32; +pub const KEY_BACKSPACE: i32 = bindings::KEY_BACKSPACE as i32; + +pub const KEY_RIGHT: i32 = bindings::KEY_RIGHT as i32; +pub const KEY_UP: i32 = bindings::KEY_UP as i32; +pub const KEY_LEFT: i32 = bindings::KEY_LEFT as i32; +pub const KEY_DOWN: i32 = bindings::KEY_DOWN as i32; +pub const KEY_PAGEUP: i32 = bindings::KEY_PAGEUP as i32; +pub const KEY_PAGEDOWN: i32 = bindings::KEY_PAGEDOWN as i32; +pub const KEY_DELETE: i32 = bindings::KEY_DELETE as i32; +pub const KEY_SHIFTTAB: i32 = bindings::KEY_SHIFTTAB as i32; + +/// Modifier combinator. +pub const KEY_CTRL: i32 = bindings::KEY_CTRL as i32; +/// Modifier combinator. +pub const KEY_SHIFT: i32 = bindings::KEY_SHIFT as i32; +/// Modifier combinator. +pub const KEY_ALT: i32 = bindings::KEY_ALT as i32; +/// Modifier combinator. +pub const KEY_CTRLALT: i32 = bindings::KEY_CTRLALT as i32; +/// Modifier combinator. +pub const KEY_CTRLSHIFT: i32 = bindings::KEY_CTRLSHIFT as i32; + +/// A style descriptor for a cell on the screen. +#[derive(Debug, Copy, Clone)] +pub struct Style { + /// The foreground color; valid values are 0, 1, ..., 7 (the eight ANSI + /// colors) and 9 (the default color). + pub fg: u8, + + /// The background color; valid values are 0, 1, ..., 7 (the eight ANSI + /// colors) and 9 (the default color). + pub bg: u8, + + /// Whether the character in the cell should be printed bold. If this is + /// enabled, the terminal generally uses the bright version of the + /// foreground color instead of the normal version. Therefore, there are + /// effectively 16, not 8, foreground colors (in addition to the default + /// color, 9). + pub bold: bool, + + /// Whether to display an underline. + pub ul: bool +} + +/// RAII struct for keyboard-input initialization +/// +/// Only one instance of this struct can be active at a time. Attempting to +/// create a second instance will panic. +pub struct Keyboard { + _phantom: PhantomData<*mut u8>, +} + +static KEYBOARD_REFCOUNT: AtomicBool = AtomicBool::new(false); + +impl Keyboard { + /// Grab the keyboard, i.e. enable raw mode. + /// + /// This disables automatic printing of typed characters (echo), and allows + /// reading a single character even when a newline has not yet been + /// entered. + /// + /// If `nosigkeys` is true, interrupt keycodes like ctrl-C, ctrl-\ and + /// ctrl-Z will no longer be handled by the shell but will be available to + /// be read using `get_key()` just like the other keys. + pub fn grab(nosigkeys: bool) -> Self { + let prev = KEYBOARD_REFCOUNT.compare_and_swap( + false, true, Ordering::SeqCst); + if prev { + panic!("Can only hold one Keyboard struct at a time!"); + } + + unsafe { bindings::initkeyboard(nosigkeys); } + Self { _phantom: PhantomData } + } + + /// Explicitly release the keyboard. + /// + /// This prevents further use of the Keyboard functionality and + /// simultaneously forces its lifetime to extend until this call, delaying + /// deinitialization of the keyboard until this call. + pub fn end(self) {} + + // None on EOF, otherwise a combination of the KEY_* constants + pub fn get_key(&self) -> io::Result> { + match unsafe { bindings::tgetkey() } { + -2 => Err(io::Error::last_os_error()), + -1 => Ok(None), + value => Ok(Some(value)), + } + } + + pub fn get_line(&self) -> Option { + let ptr = unsafe { bindings::tgetline() }; + if ptr.is_null() { + None + } else { + unsafe { Some(util::string_from_utf8_charp(ptr)) } + } + } +} + +impl Drop for Keyboard { + fn drop(&mut self) { + let prev = KEYBOARD_REFCOUNT.compare_and_swap( + true, false, Ordering::SeqCst); + if !prev { + panic!("Internal: No Keyboard was held on Drop of a Keyboard?"); + } + + unsafe { bindings::endkeyboard(); } + } +} + +/// RAII struct for screen-drawing initialization +/// +/// Only one instance of this struct can be active at a time. Attempting to +/// create a second instance will panic. +pub struct Screen<'a> { + _keyboard: &'a Keyboard, +} + +static SCREEN_REFCOUNT: AtomicBool = AtomicBool::new(false); + +impl<'a> Screen<'a> { + /// Grab the screen, i.e. switch to the alternative screen and allocate draw + /// buffers. + /// + /// This function takes and stores a Keyboard reference to ensure that the + /// keyboard is initialized while the screen is grabbed. + pub fn grab<'b: 'a>(keyboard: &'b Keyboard) -> Self { + let prev = SCREEN_REFCOUNT.compare_and_swap( + false, true, Ordering::SeqCst); + if prev { + panic!("Can only hold one Screen struct at a time!"); + } + + unsafe { bindings::initscreen(); } + Self { _keyboard: keyboard } + } + + pub fn end(self) {} + + /// Install ctrl-L redraw handler. + /// + /// If the argument is true, then upon reading ctrl-L in + /// `Keyboard::get_key()`, a full redraw is issued, and the the call blocks + /// for the next key. Note that if the user only types ctrl-L, this means + /// that `get_key` may block while you don't expect it. + pub fn install_cl_handler(&self, install: bool) { + unsafe { bindings::installCLhandler(install); } + } + + /// Install redraw handler. + /// + /// The argument indicates whether the redraw is a full redraw. The handler + /// is called before the redraw is performed. Please do not trigger redraws + /// from within the handler. + pub fn on_redraw(&self, handler: extern "C" fn(full_redraw: bool)) { + unsafe { bindings::installredrawhandler(Some(handler)); } + } + + pub fn clear(&self) { + unsafe { bindings::clearscreen(); } + } + + /// Update style state. + /// + /// This modifies the style for the characters printed from now on. Note + /// that this does not write anything to the terminal. + pub fn set_style(&self, style: Style) { + let sty = bindings::Style { + fg: style.fg as i32, + bg: style.bg as i32, + bold: style.bold, + ul: style.ul, + }; + unsafe { bindings::setstyle(&sty as *const bindings::Style); } + } + + /// Update partial style state. see `set_style()`. + pub fn set_fg(&self, fg: u8) { + unsafe { bindings::setfg(fg as i32); } + } + + /// Update partial style state. see `set_style()`. + pub fn set_bg(&self, bg: u8) { + unsafe { bindings::setbg(bg as i32); } + } + + /// Update partial style state. see `set_style()`. + pub fn set_bold(&self, bold: bool) { + unsafe { bindings::setbold(bold); } + } + + /// Update partial style state. see `set_style()`. + pub fn set_ul(&self, ul: bool) { + unsafe { bindings::setul(ul); } + } + + pub fn putc(&self, c: char) { + unsafe { bindings::tputc(c as i32); } + } + + /// Newlines ('\n') return to the column on which printing started. + pub fn print(&self, s: &str) { + unsafe { bindings::tprint(s.as_bytes().as_ptr() as *const i8); } + } + + pub fn fill_rect(&self, lefttop: (u32, u32), size: (u32, u32), c: char) { + unsafe { bindings::fillrect( + lefttop.0 as i32, lefttop.1 as i32, + size.0 as i32, size.1 as i32, + c as i32); } + } + + pub fn redraw(&self) { + unsafe { bindings::redraw(); } + } + + pub fn redraw_full(&self) { + unsafe { bindings::redrawfull(); } + } + + /// Scrolls a rectangle on the screen by a signed number of lines. + pub fn scroll_term(&self, lefttop: (u32, u32), size: (u32, u32), amount: i32) { + unsafe { bindings::scrollterm( + lefttop.0 as i32, lefttop.1 as i32, + size.0 as i32, size.1 as i32, + amount); } + } + + pub fn get_buffer_char(&self, pos: (u32, u32)) -> char { + unsafe { + char::from_u32( + bindings::getbufferchar(pos.0 as i32, pos.1 as i32) as u32 + ).unwrap() + } + } + + /// Updates state; does not actually move the cursor yet (use `redraw()`). + pub fn move_to(&self, pos: (u32, u32)) { + unsafe { bindings::moveto(pos.0 as i32, pos.1 as i32); } + } + + /// Save the (internal) cursor position on a stack. + pub fn push_cursor(&self) { + unsafe { bindings::pushcursor(); } + } + + /// Pop the (internal) cursor position from the stack. + pub fn pop_cursor(&self) { + unsafe { bindings::popcursor(); } + } + + /// Takes effect immediately! + pub fn cursor_visible(&self, visible: bool) { + unsafe { bindings::cursorvisible(visible); } + } +} + +impl<'a> Drop for Screen<'a> { + fn drop(&mut self) { + let prev = SCREEN_REFCOUNT.compare_and_swap( + true, false, Ordering::SeqCst); + if !prev { + panic!("Internal: No Screen was held on Drop of a Screen?"); + } + + unsafe { bindings::endscreen(); } + } +} + +pub fn get_term_size() -> (u32, u32) { + let sz = unsafe { bindings::gettermsize() }; + (sz.w as u32, sz.h as u32) +} + +pub fn bel() { + unsafe { bindings::bel(); } +} -- cgit v1.2.3-54-g00ecf