diff options
-rw-r--r-- | .gitignore | 3 | ||||
-rw-r--r-- | .gitmodules | 3 | ||||
-rw-r--r-- | Cargo.toml | 11 | ||||
-rw-r--r-- | build.rs | 53 | ||||
-rw-r--r-- | src/bindings.rs | 5 | ||||
-rw-r--r-- | src/lib.rs | 192 | ||||
m--------- | vendor/termio | 0 |
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 |