diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/lib.rs | 9 | ||||
-rw-r--r-- | src/util.rs | 25 | ||||
-rw-r--r-- | src/widgets.rs | 3 | ||||
-rw-r--r-- | src/widgets/log.rs | 75 | ||||
-rw-r--r-- | src/widgets/menu.rs | 80 | ||||
-rw-r--r-- | src/widgets/prompt.rs | 68 |
6 files changed, 254 insertions, 6 deletions
@@ -1,9 +1,9 @@ use std::char; -use std::ffi::{self, CStr}; use std::io; -use libc; mod bindings; +mod util; +pub mod widgets; // #[cfg(test)] // mod tests { @@ -184,9 +184,6 @@ pub fn get_line() -> Option<String> { 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 + unsafe { Some(util::string_from_utf8_charp(ptr)) } } } diff --git a/src/util.rs b/src/util.rs new file mode 100644 index 0000000..8bcc028 --- /dev/null +++ b/src/util.rs @@ -0,0 +1,25 @@ +use std::ffi::{self, CStr}; +use std::ptr; +use libc; + +/// Consumes and frees the input pointer, and assumes it points to +/// a nul-terminated C-string that contains valid UTF8. +pub unsafe fn string_from_utf8_charp(p: *mut i8) -> String { + let bytes = CStr::from_ptr(p).to_bytes(); + let res = String::from_utf8(bytes.to_vec()).unwrap(); + libc::free(p as *mut ffi::c_void); + res +} + +pub fn strdup(s: &str) -> *mut i8 { + unsafe { + let p: *mut i8 = malloc(s.len() + 1); + ptr::copy_nonoverlapping(s.as_ptr(), p as *mut u8, s.len()); + *p.add(s.len()) = 0; + p + } +} + +pub unsafe fn malloc<T>(count: usize) -> *mut T { + libc::malloc(count * std::mem::size_of::<T>()) as *mut T +} diff --git a/src/widgets.rs b/src/widgets.rs new file mode 100644 index 0000000..d8c8e2e --- /dev/null +++ b/src/widgets.rs @@ -0,0 +1,3 @@ +pub mod log; +pub mod prompt; +pub mod menu; diff --git a/src/widgets/log.rs b/src/widgets/log.rs new file mode 100644 index 0000000..8bd240a --- /dev/null +++ b/src/widgets/log.rs @@ -0,0 +1,75 @@ +use std::ptr; + +use crate::bindings; + +pub struct Log { + ptr: *mut bindings::Logwidget, +} + +pub struct LogBuilder<'a> { + lefttop: (u32, u32), + size: (u32, u32), + title: Option<&'a str>, + timestamps: bool, +} + +impl<'a> LogBuilder<'a> { + pub fn add_title<'s: 'a>(&'a mut self, title: &'s str) + -> &'a mut LogBuilder<'a> { + self.title = Some(title); + self + } + + pub fn set_timestamps(&'a mut self, timestamps: bool) + -> &'a mut LogBuilder<'a> { + self.timestamps = timestamps; + self + } + + pub fn create(&self) -> Log { + Log { + ptr: unsafe { + bindings::lgw_make( + self.lefttop.0 as i32, self.lefttop.1 as i32, + self.size.0 as i32, self.size.1 as i32, + match self.title { + None => ptr::null(), + Some(s) => s.as_ptr() as *const i8, + }, + self.timestamps) + } + } + } +} + +impl Log { + pub fn new<'a>(lefttop: (u32, u32), size: (u32, u32)) -> LogBuilder<'a> { + LogBuilder { + lefttop, size, + title: None, + timestamps: false, + } + } + + pub fn redraw(&self) { + unsafe { bindings::lgw_redraw(self.ptr); } + } + + pub fn add(&mut self, line: &str) { + unsafe { bindings::lgw_add(self.ptr, line.as_ptr() as *const i8); } + } + + pub fn clear(&mut self) { + unsafe { bindings::lgw_clear(self.ptr); } + } + + pub fn change_title(&mut self, title: &str) { + unsafe { bindings::lgw_changetitle(self.ptr, title.as_ptr() as *const i8); } + } +} + +impl Drop for Log { + fn drop(&mut self) { + unsafe { bindings::lgw_destroy(self.ptr); } + } +} diff --git a/src/widgets/menu.rs b/src/widgets/menu.rs new file mode 100644 index 0000000..fe899e3 --- /dev/null +++ b/src/widgets/menu.rs @@ -0,0 +1,80 @@ +use std::ffi::c_void; + +use crate::bindings; +use crate::util; + +pub struct Menu { + ptr: *mut bindings::Menuwidget, + data: *mut bindings::Menudata, +} + +pub struct Data { + items: Vec<Item>, +} + +pub struct Item { + text: String, + hotkey: i32, + func: Option<unsafe extern "C" fn(index: i32)>, +} + +pub enum KeyResult { + Handled, Ignored, Quit, Called, +} + +impl Menu { + pub fn new(lefttop: (u32, u32), data: Data) -> Menu { + unsafe { + let mdata = util::malloc::<bindings::Menudata>(1); + let mitems = util::malloc::<bindings::Menuitem>(data.items.len()); + + (*mdata).nitems = data.items.len() as i32; + (*mdata).items = mitems; + + for i in 0..data.items.len() { + (*mitems.add(i)).text = util::strdup(&data.items[i].text); + (*mitems.add(i)).hotkey = data.items[i].hotkey; + (*mitems.add(i)).func = data.items[i].func; + } + + Menu { + ptr: bindings::menu_make( + lefttop.0 as i32, lefttop.1 as i32, + mdata), + data: mdata, + } + } + } + + pub fn redraw(&self) { + unsafe { bindings::menu_redraw(self.ptr); } + } + + pub fn handle_key(&mut self, key: i32) -> KeyResult { + match unsafe { bindings::menu_handlekey(self.ptr, key) } { + bindings::Menukey_MENUKEY_HANDLED => KeyResult::Handled, + bindings::Menukey_MENUKEY_IGNORED => KeyResult::Ignored, + bindings::Menukey_MENUKEY_QUIT => KeyResult::Quit, + bindings::Menukey_MENUKEY_CALLED => KeyResult::Called, + _ => panic!("Invalid Menukey received from termio"), + } + } +} + +impl Drop for Menu { + fn drop(&mut self) { + unsafe { + bindings::menu_destroy(self.ptr); + + let nitems = (*self.data).nitems as usize; + let mitems = (*self.data).items; + + for i in 0..nitems { + libc::free((*mitems.add(i)).text as *mut c_void); + } + + libc::free(mitems as *mut c_void); + libc::free(self.data as *mut c_void); + } + } +} diff --git a/src/widgets/prompt.rs b/src/widgets/prompt.rs new file mode 100644 index 0000000..0690840 --- /dev/null +++ b/src/widgets/prompt.rs @@ -0,0 +1,68 @@ +use std::ptr; + +use crate::bindings; +use crate::util; + +pub struct Prompt { + ptr: *mut bindings::Promptwidget, +} + +pub struct PromptBuilder<'a> { + lefttop: (u32, u32), + width: u32, + title: Option<&'a str>, +} + +impl<'a> PromptBuilder<'a> { + pub fn add_title<'s: 'a>(&'a mut self, title: &'s str) + -> &'a mut PromptBuilder<'a> { + self.title = Some(title); + self + } + + pub fn create(&self) -> Prompt { + Prompt { + ptr: unsafe { + bindings::prw_make( + self.lefttop.0 as i32, self.lefttop.1 as i32, + self.width as i32, + match self.title { + None => ptr::null(), + Some(s) => s.as_ptr() as *const i8, + }) + } + } + } +} + +impl Prompt { + pub fn new<'a>(lefttop: (u32, u32), width: u32) -> PromptBuilder<'a> { + PromptBuilder { + lefttop, width, + title: None, + } + } + + pub fn redraw(&self) { + unsafe { bindings::prw_redraw(self.ptr); } + } + + pub fn handle_key(&mut self, key: i32) -> Option<String> { + let ptr = unsafe { bindings::prw_handlekey(self.ptr, key) }; + if ptr.is_null() { + None + } else { + unsafe { Some(util::string_from_utf8_charp(ptr)) } + } + } + + pub fn change_title(&mut self, title: &str) { + unsafe { bindings::prw_changetitle(self.ptr, title.as_ptr() as *const i8); } + } +} + +impl Drop for Prompt { + fn drop(&mut self) { + unsafe { bindings::prw_destroy(self.ptr); } + } +} |