summaryrefslogtreecommitdiff
path: root/src/core.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/core.rs')
-rw-r--r--src/core.rs292
1 files changed, 292 insertions, 0 deletions
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<Option<i32>> {
+ match unsafe { bindings::tgetkey() } {
+ -2 => Err(io::Error::last_os_error()),
+ -1 => Ok(None),
+ value => Ok(Some(value)),
+ }
+ }
+
+ pub fn get_line(&self) -> Option<String> {
+ 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(); }
+}