summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore3
-rw-r--r--.gitmodules3
-rw-r--r--Cargo.toml11
-rw-r--r--build.rs53
-rw-r--r--src/bindings.rs5
-rw-r--r--src/lib.rs192
m---------vendor/termio0
7 files changed, 267 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..6936990
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,3 @@
+/target
+**/*.rs.bk
+Cargo.lock
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..5b368fe
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "vendor/termio"]
+ path = vendor/termio
+ url = https://github.com/tomsmeding/termio.git
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..8df7006
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,11 @@
+[package]
+name = "termio"
+version = "0.1.0"
+authors = ["tomsmeding <tom.smeding@gmail.com>"]
+edition = "2018"
+
+[dependencies]
+libc = "0.2.48"
+
+[build-dependencies]
+bindgen = "0.47.1"
diff --git a/build.rs b/build.rs
new file mode 100644
index 0000000..e66329c
--- /dev/null
+++ b/build.rs
@@ -0,0 +1,53 @@
+use std::env;
+use std::path::PathBuf;
+use std::process::Command;
+use bindgen;
+
+fn run_command(cmd: &str, args: &[&str]) -> Option<()> {
+ let status = Command::new(cmd).args(args).status().expect("Failed to execute process");
+ if status.success() {
+ Some(())
+ } else {
+ None
+ }
+}
+
+fn check_output(cmd: &str, args: &[&str]) -> Option<String> {
+ let output = Command::new(cmd).args(args).output().expect("Failed to execute process");
+ if output.status.success() {
+ Some(String::from_utf8(output.stdout).unwrap())
+ } else {
+ None
+ }
+}
+
+fn main() {
+ let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
+ run_command("git", &["-C", out_dir.to_str().unwrap(), "submodule", "update", "--init", "--recursive"])
+ .expect("Failed to clone git submodules");
+
+ let status_output =
+ check_output("git", &["-C", out_dir.to_str().unwrap(), "submodule", "status"])
+ .expect("Failed to check git submodule status");
+
+ let sub_repo =
+ out_dir.join(PathBuf::from(status_output.split_whitespace().skip(1).next().unwrap()));
+
+ run_command("make", &["-C", sub_repo.to_str().unwrap()])
+ .expect("Failed to build termio library");
+
+ let bindings = bindgen::Builder::default()
+ .header(sub_repo.join("termio.h").to_str().unwrap())
+ .blacklist_item("true_")
+ .blacklist_item("false_")
+ .blacklist_item("__bool_true_false_are_defined")
+ .generate()
+ .expect("Error generating bindings using bindgen");
+
+ bindings
+ .write_to_file(out_dir.join("bindings.rs"))
+ .expect("Error writing bindings to file");
+
+ println!("cargo:rustc-link-lib=static=termio");
+ println!("cargo:rustc-link-search={}", sub_repo.to_str().unwrap());
+}
diff --git a/src/bindings.rs b/src/bindings.rs
new file mode 100644
index 0000000..565964f
--- /dev/null
+++ b/src/bindings.rs
@@ -0,0 +1,5 @@
+#![allow(non_upper_case_globals)]
+// #![allow(non_camel_case_types)]
+#![allow(non_snake_case)]
+
+include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..5a86bdf
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,192 @@
+use std::char;
+use std::ffi::{self, CStr};
+use std::io;
+use libc;
+
+mod bindings;
+
+// #[cfg(test)]
+// mod tests {
+// #[test]
+// fn it_works() {
+// assert_eq!(2 + 2, 4);
+// }
+// }
+
+// All positions are represented using a tuple (x, y): (u32, u32).
+// Positions are zero-based.
+
+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;
+
+pub const KEY_CTRL: i32 = bindings::KEY_CTRL as i32;
+pub const KEY_SHIFT: i32 = bindings::KEY_SHIFT as i32;
+pub const KEY_ALT: i32 = bindings::KEY_ALT as i32;
+pub const KEY_CTRLALT: i32 = bindings::KEY_CTRLALT as i32;
+pub const KEY_CTRLSHIFT: i32 = bindings::KEY_CTRLSHIFT as i32;
+
+#[derive(Debug, Copy, Clone)]
+pub struct Size {
+ pub w: u32,
+ pub h: u32,
+}
+
+#[derive(Debug, Copy, Clone)]
+pub struct Style {
+ pub fg: u8, // 0-7, 9
+ pub bg: u8, // 0-7, 9
+ pub bold: bool,
+ pub ul: bool
+}
+
+pub fn init_keyboard(nosigkeys: bool) {
+ unsafe { bindings::initkeyboard(nosigkeys); }
+}
+
+pub fn end_keyboard() {
+ unsafe { bindings::endkeyboard(); }
+}
+
+pub fn init_screen() {
+ unsafe { bindings::initscreen(); }
+}
+
+pub fn end_screen() {
+ unsafe { bindings::endscreen(); }
+}
+
+pub fn install_cl_handler(install: bool) {
+ unsafe { bindings::installCLhandler(install); }
+}
+
+pub fn install_redraw_handler(handler: extern "C" fn(full_redraw: bool)) {
+ unsafe { bindings::installredrawhandler(Some(handler)); }
+}
+
+pub fn clear_screen() {
+ unsafe { bindings::clearscreen(); }
+}
+
+pub fn get_term_size() -> Size {
+ let sz = unsafe { bindings::gettermsize() };
+ Size { w: sz.w as u32, h: sz.h as u32 }
+}
+
+pub fn set_style(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); }
+}
+
+pub fn set_fg(fg: u8) {
+ unsafe { bindings::setfg(fg as i32); }
+}
+
+pub fn set_bg(bg: u8) {
+ unsafe { bindings::setbg(bg as i32); }
+}
+
+pub fn set_bold(bold: bool) {
+ unsafe { bindings::setbold(bold); }
+}
+
+pub fn set_ul(ul: bool) {
+ unsafe { bindings::setul(ul); }
+}
+
+pub fn putc(c: char) {
+ unsafe { bindings::tputc(c as i32); }
+}
+
+pub fn print(s: &str) {
+ unsafe { bindings::tprintf(
+ "%s".as_bytes().as_ptr() as *const i8,
+ s.as_bytes().as_ptr() as *const i8); }
+}
+
+pub fn fillrect(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() {
+ unsafe { bindings::redraw(); }
+}
+
+pub fn redraw_full() {
+ unsafe { bindings::redrawfull(); }
+}
+
+pub fn scroll_term(lefttop: (u32, u32), size: (u32, u32), c: char) {
+ unsafe { bindings::scrollterm(
+ lefttop.0 as i32, lefttop.1 as i32,
+ size.0 as i32, size.1 as i32,
+ c as i32); }
+}
+
+pub fn get_buffer_char(pos: (u32, u32)) -> char {
+ unsafe {
+ char::from_u32(
+ bindings::getbufferchar(pos.0 as i32, pos.1 as i32) as u32
+ ).unwrap()
+ }
+}
+
+pub fn move_to(pos: (u32, u32)) {
+ unsafe { bindings::moveto(pos.0 as i32, pos.1 as i32); }
+}
+
+pub fn push_cursor() {
+ unsafe { bindings::pushcursor(); }
+}
+
+pub fn pop_cursor() {
+ unsafe { bindings::popcursor(); }
+}
+
+pub fn bel() {
+ unsafe { bindings::bel(); }
+}
+
+pub fn cursor_visible(visible: bool) {
+ unsafe { bindings::cursorvisible(visible); }
+}
+
+// None on EOF, otherwise a combination of the KEY_* constants
+pub fn get_key() -> 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() -> Option<String> {
+ let ptr = unsafe { bindings::tgetline() };
+ if ptr.is_null() {
+ None
+ } else {
+ let bytes = unsafe { CStr::from_ptr(ptr).to_bytes() };
+ let res = Some(String::from_utf8(bytes.to_vec()).unwrap());
+ unsafe { libc::free(ptr as *mut ffi::c_void); }
+ res
+ }
+}
diff --git a/vendor/termio b/vendor/termio
new file mode 160000
+Subproject e2c2f93cd657ce9a40bcfd6c085328ac54e7a69