use std::cell::{Cell, RefCell};
use std::collections::HashMap;
use std::ffi::{c_int, c_void, CStr, CString};
use std::marker::PhantomData;
use std::os::raw::c_char;
use std::ptr;
use gtcaca_sys as sys;
pub use gtcaca_sys as ffi;
#[derive(Debug)]
pub enum Error {
InitFailed,
AlreadyInitialized,
InteriorNul,
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Error::InitFailed => f.write_str(
"gtcaca_init failed (no usable terminal display?)",
),
Error::AlreadyInitialized => {
f.write_str("a Gtcaca context already exists")
}
Error::InteriorNul => f.write_str("string contained a NUL byte"),
}
}
}
impl std::error::Error for Error {}
pub(crate) fn cstring(s: &str) -> Result<CString, Error> {
CString::new(s).map_err(|_| Error::InteriorNul)
}
mod widgets;
pub use widgets::*;
pub mod tutorials {
#[doc = include_str!("../doc/tutorial_spreadsheet.md")]
pub mod spreadsheet {}
#[doc = include_str!("../doc/tutorial_mindmap.md")]
pub mod mindmap {}
}
type KeyHandler = Box<dyn FnMut(i32) -> bool>;
thread_local! {
static HANDLERS: RefCell<HashMap<usize, KeyHandler>> =
RefCell::new(HashMap::new());
static ACTIVE: Cell<bool> = const { Cell::new(false) };
}
fn dispatch_key(ptr: usize, key: c_int) -> c_int {
HANDLERS.with(|h| {
let taken = h.borrow_mut().remove(&ptr);
if let Some(mut cb) = taken {
let consumed = cb(key);
h.borrow_mut().entry(ptr).or_insert(cb);
return if consumed { 1 } else { 0 };
}
0
})
}
pub struct Gtcaca {
_not_send: PhantomData<*const ()>,
}
impl Gtcaca {
pub fn init() -> Result<Self, Error> {
if ACTIVE.with(|a| a.get()) {
return Err(Error::AlreadyInitialized);
}
let mut argc: c_int = 0;
let mut argv: *mut *mut c_char = ptr::null_mut();
let rc = unsafe { sys::gtcaca_init(&mut argc, &mut argv) };
if rc != 0 {
return Err(Error::InitFailed);
}
ACTIVE.with(|a| a.set(true));
Ok(Gtcaca { _not_send: PhantomData })
}
pub fn run(&self) {
unsafe { sys::gtcaca_main() }
}
pub fn quit(&self) {
unsafe { sys::gtcaca_main_quit() }
}
pub fn redraw(&self) {
unsafe { sys::gtcaca_redraw() }
}
pub fn clear(&self) {
unsafe { sys::gtcaca_clear_screen() }
}
pub fn flush(&self) {
unsafe { sys::gtcaca_refresh() }
}
pub fn terminal_supports_blocks(&self) -> bool {
unsafe { sys::gtcaca_terminal_supports_blocks() != 0 }
}
}
pub fn quit() {
unsafe { sys::gtcaca_main_quit() }
}
pub fn redraw() {
unsafe { sys::gtcaca_redraw() }
}
impl Drop for Gtcaca {
fn drop(&mut self) {
unsafe { sys::gtcaca_shutdown() };
HANDLERS.with(|h| h.borrow_mut().clear());
ACTIVE.with(|a| a.set(false));
}
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct Rect {
pub x: i32,
pub y: i32,
pub width: i32,
pub height: i32,
}
pub trait Widget {
fn as_widget_ptr(&self) -> *mut sys::gtcaca_widget_t;
fn geometry(&self) -> Rect {
let w = self.as_widget_ptr();
unsafe {
Rect {
x: (*w).x,
y: (*w).y,
width: (*w).width,
height: (*w).height,
}
}
}
fn send_key(&self, key: i32) -> bool {
unsafe { sys::gtcaca_widget_send_key(self.as_widget_ptr(), key) != 0 }
}
fn set_focus(&self, focused: bool) {
unsafe { (*self.as_widget_ptr()).has_focus = focused as c_int };
}
fn set_width(&self, width: i32) {
unsafe { (*self.as_widget_ptr()).width = width };
}
fn set_height(&self, height: i32) {
unsafe { (*self.as_widget_ptr()).height = height };
}
fn colors(&self) -> (u8, u8, u8, u8) {
let w = self.as_widget_ptr();
unsafe {
(
(*w).color_focus_fg,
(*w).color_focus_bg,
(*w).color_nonfocus_fg,
(*w).color_nonfocus_bg,
)
}
}
fn set_colors(&self, focus_fg: u8, focus_bg: u8, nonfocus_fg: u8, nonfocus_bg: u8) {
unsafe {
let w = self.as_widget_ptr();
(*w).color_focus_fg = focus_fg;
(*w).color_focus_bg = focus_bg;
(*w).color_nonfocus_fg = nonfocus_fg;
(*w).color_nonfocus_bg = nonfocus_bg;
}
}
fn paint(&self) {
unsafe { sys::gtcaca_widget_draw(self.as_widget_ptr()) };
}
}
fn store_handler<F: FnMut(i32) -> bool + 'static>(ptr: usize, cb: F) {
HANDLERS.with(|h| {
h.borrow_mut().insert(ptr, Box::new(cb));
});
}
pub struct Application<'a> {
ptr: *mut sys::gtcaca_application_widget_t,
_ctx: PhantomData<&'a Gtcaca>,
}
impl<'a> Application<'a> {
pub fn new(_ctx: &'a Gtcaca, title: &str) -> Application<'a> {
let c = cstring(title).unwrap_or_default();
let ptr = unsafe { sys::gtcaca_application_new(c.as_ptr()) };
assert!(!ptr.is_null(), "gtcaca_application_new returned NULL");
Application { ptr, _ctx: PhantomData }
}
}
impl Widget for Application<'_> {
fn as_widget_ptr(&self) -> *mut sys::gtcaca_widget_t {
self.ptr as *mut sys::gtcaca_widget_t
}
}
pub struct Window<'a> {
ptr: *mut sys::gtcaca_window_widget_t,
_parent: PhantomData<&'a ()>,
}
impl<'a> Window<'a> {
pub fn new(
parent: &'a impl Widget,
title: Option<&str>,
x: i32,
y: i32,
width: i32,
height: i32,
) -> Window<'a> {
let c = title.map(|t| cstring(t).unwrap_or_default());
let title_ptr = c.as_ref().map_or(ptr::null(), |s| s.as_ptr());
let ptr = unsafe {
sys::gtcaca_window_new(
parent.as_widget_ptr(),
title_ptr,
x,
y,
width,
height,
)
};
assert!(!ptr.is_null(), "gtcaca_window_new returned NULL");
Window { ptr, _parent: PhantomData }
}
pub fn fullscreen(parent: &'a impl Widget, title: Option<&str>) -> Window<'a> {
let g = parent.geometry();
Window::new(parent, title, g.x, g.y, g.width, g.height)
}
pub fn centered(
parent: &'a impl Widget,
title: Option<&str>,
width: i32,
height: i32,
) -> Window<'a> {
let c = title.map(|t| cstring(t).unwrap_or_default());
let title_ptr = c.as_ref().map_or(ptr::null(), |s| s.as_ptr());
let ptr = unsafe {
sys::gtcaca_window_new_centered(
parent.as_widget_ptr(),
title_ptr,
width,
height,
)
};
assert!(!ptr.is_null(), "gtcaca_window_new_centered returned NULL");
Window { ptr, _parent: PhantomData }
}
pub fn centered_fraction(
parent: &'a impl Widget,
title: Option<&str>,
wfrac: f64,
hfrac: f64,
) -> Window<'a> {
let g = parent.geometry();
let wf = wfrac.clamp(0.1, 1.0);
let hf = hfrac.clamp(0.1, 1.0);
let w = ((g.width as f64 * wf) as i32).clamp(10, (g.width - 2).max(10));
let h = ((g.height as f64 * hf) as i32).clamp(6, (g.height - 2).max(6));
Window::centered(parent, title, w, h)
}
pub fn content(&self, margin: i32) -> Rect {
let g = self.geometry();
let m = margin.max(0);
Rect {
x: 1 + m,
y: 1 + m,
width: (g.width - 2 - 2 * m).max(1),
height: (g.height - 2 - 2 * m).max(1),
}
}
pub fn set_focus(&self) {
unsafe { sys::gtcaca_window_set_focus(self.ptr) }
}
pub fn close(&self) {
unsafe { sys::gtcaca_window_close(self.ptr) }
}
pub fn draw(&self) {
unsafe { sys::gtcaca_window_draw(self.ptr) }
}
}
impl Widget for Window<'_> {
fn as_widget_ptr(&self) -> *mut sys::gtcaca_widget_t {
self.ptr as *mut sys::gtcaca_widget_t
}
}
pub struct Label<'a> {
ptr: *mut sys::gtcaca_label_widget_t,
_parent: PhantomData<&'a ()>,
}
impl<'a> Label<'a> {
pub fn new(parent: &'a impl Widget, text: &str, x: i32, y: i32) -> Label<'a> {
let c = cstring(text).unwrap_or_default();
let ptr =
unsafe { sys::gtcaca_label_new(parent.as_widget_ptr(), c.as_ptr(), x, y) };
assert!(!ptr.is_null(), "gtcaca_label_new returned NULL");
Label { ptr, _parent: PhantomData }
}
}
impl Widget for Label<'_> {
fn as_widget_ptr(&self) -> *mut sys::gtcaca_widget_t {
self.ptr as *mut sys::gtcaca_widget_t
}
}
pub struct Button<'a> {
ptr: *mut sys::gtcaca_button_widget_t,
_parent: PhantomData<&'a ()>,
}
unsafe extern "C" fn button_tramp(
w: *mut sys::gtcaca_button_widget_t,
key: c_int,
_ud: *mut c_void,
) -> c_int {
dispatch_key(w as usize, key)
}
impl<'a> Button<'a> {
pub fn new(parent: &'a impl Widget, label: &str, x: i32, y: i32) -> Button<'a> {
let c = cstring(label).unwrap_or_default();
let ptr =
unsafe { sys::gtcaca_button_new(parent.as_widget_ptr(), c.as_ptr(), x, y) };
assert!(!ptr.is_null(), "gtcaca_button_new returned NULL");
Button { ptr, _parent: PhantomData }
}
pub fn on_key<F: FnMut(i32) -> bool + 'static>(&self, cb: F) {
store_handler(self.ptr as usize, cb);
unsafe { sys::gtcaca_button_key_cb_register(self.ptr, Some(button_tramp)) };
}
}
impl Widget for Button<'_> {
fn as_widget_ptr(&self) -> *mut sys::gtcaca_widget_t {
self.ptr as *mut sys::gtcaca_widget_t
}
}
pub struct Entry<'a> {
ptr: *mut sys::gtcaca_entry_widget_t,
_parent: PhantomData<&'a ()>,
}
unsafe extern "C" fn entry_tramp(
w: *mut sys::gtcaca_entry_widget_t,
key: c_int,
_ud: *mut c_void,
) -> c_int {
dispatch_key(w as usize, key)
}
impl<'a> Entry<'a> {
pub fn new(parent: &'a impl Widget, x: i32, y: i32, width: i32) -> Entry<'a> {
let ptr = unsafe { sys::gtcaca_entry_new(parent.as_widget_ptr(), x, y, width) };
assert!(!ptr.is_null(), "gtcaca_entry_new returned NULL");
Entry { ptr, _parent: PhantomData }
}
pub fn text(&self) -> String {
let p = unsafe { sys::gtcaca_entry_get_text(self.ptr) };
if p.is_null() {
return String::new();
}
unsafe { CStr::from_ptr(p) }.to_string_lossy().into_owned()
}
pub fn set_text(&self, text: &str) {
let c = cstring(text).unwrap_or_default();
unsafe { sys::gtcaca_entry_set_text(self.ptr, c.as_ptr()) }
}
pub fn set_secret(&self, secret: bool) {
unsafe { sys::gtcaca_entry_set_secret(self.ptr, secret as c_int) }
}
pub fn on_key<F: FnMut(i32) -> bool + 'static>(&self, cb: F) {
store_handler(self.ptr as usize, cb);
unsafe {
sys::gtcaca_entry_key_cb_register(self.ptr, Some(entry_tramp), ptr::null_mut())
};
}
}
impl Widget for Entry<'_> {
fn as_widget_ptr(&self) -> *mut sys::gtcaca_widget_t {
self.ptr as *mut sys::gtcaca_widget_t
}
}
pub struct Checkbox<'a> {
ptr: *mut sys::gtcaca_checkbox_widget_t,
_parent: PhantomData<&'a ()>,
}
unsafe extern "C" fn checkbox_tramp(
w: *mut sys::gtcaca_checkbox_widget_t,
key: c_int,
_ud: *mut c_void,
) -> c_int {
dispatch_key(w as usize, key)
}
impl<'a> Checkbox<'a> {
pub fn new(parent: &'a impl Widget, label: &str, x: i32, y: i32) -> Checkbox<'a> {
let c = cstring(label).unwrap_or_default();
let ptr =
unsafe { sys::gtcaca_checkbox_new(parent.as_widget_ptr(), c.as_ptr(), x, y) };
assert!(!ptr.is_null(), "gtcaca_checkbox_new returned NULL");
Checkbox { ptr, _parent: PhantomData }
}
pub fn is_checked(&self) -> bool {
unsafe { sys::gtcaca_checkbox_get_checked(self.ptr) != 0 }
}
pub fn set_checked(&self, checked: bool) {
unsafe { sys::gtcaca_checkbox_set_checked(self.ptr, checked as c_int) }
}
pub fn on_key<F: FnMut(i32) -> bool + 'static>(&self, cb: F) {
store_handler(self.ptr as usize, cb);
unsafe {
sys::gtcaca_checkbox_key_cb_register(
self.ptr,
Some(checkbox_tramp),
ptr::null_mut(),
)
};
}
}
impl Widget for Checkbox<'_> {
fn as_widget_ptr(&self) -> *mut sys::gtcaca_widget_t {
self.ptr as *mut sys::gtcaca_widget_t
}
}
pub mod color {
pub const BLACK: u8 = 0;
pub const BLUE: u8 = 1;
pub const GREEN: u8 = 2;
pub const CYAN: u8 = 3;
pub const RED: u8 = 4;
pub const MAGENTA: u8 = 5;
pub const BROWN: u8 = 6;
pub const LIGHTGRAY: u8 = 7;
pub const DARKGRAY: u8 = 8;
pub const LIGHTBLUE: u8 = 9;
pub const LIGHTGREEN: u8 = 10;
pub const LIGHTCYAN: u8 = 11;
pub const LIGHTRED: u8 = 12;
pub const LIGHTMAGENTA: u8 = 13;
pub const YELLOW: u8 = 14;
pub const WHITE: u8 = 15;
}
pub mod linechart {
pub const LINE: i32 = 0;
pub const IMPULSE: i32 = 1;
pub const BAR: i32 = 2;
pub const DOT: i32 = 3;
}
pub mod key {
pub const BACKSPACE: i32 = 0x08;
pub const TAB: i32 = 0x09;
pub const RETURN: i32 = 0x0d;
pub const ESCAPE: i32 = 0x1b;
pub const DELETE: i32 = 0x7f;
pub const UP: i32 = 0x111;
pub const DOWN: i32 = 0x112;
pub const LEFT: i32 = 0x113;
pub const RIGHT: i32 = 0x114;
pub const INSERT: i32 = 0x115;
pub const HOME: i32 = 0x116;
pub const END: i32 = 0x117;
pub const PAGE_UP: i32 = 0x118;
pub const PAGE_DOWN: i32 = 0x119;
}
pub fn poll_key(timeout_ms: i32) -> Option<i32> {
unsafe {
let dp = sys::gmo.dp;
if dp.is_null() {
return None;
}
let mut buf = [0u64; 8];
let evp = buf.as_mut_ptr() as *mut sys::caca_event_t;
let got = sys::caca_get_event(
dp,
sys::caca_event_type_CACA_EVENT_KEY_PRESS as c_int,
evp,
timeout_ms,
);
if got == 0 {
None
} else {
Some(sys::caca_get_event_key_ch(evp) as i32)
}
}
}
use std::os::raw::c_long;
unsafe fn fill_cbuf(s: &str, buf: *mut c_char, buflen: c_int) {
if buf.is_null() || buflen <= 0 {
return;
}
let cap = (buflen as usize).saturating_sub(1);
let bytes = s.as_bytes();
let n = bytes.len().min(cap);
std::ptr::copy_nonoverlapping(bytes.as_ptr(), buf as *mut u8, n);
*buf.add(n) = 0;
}
pub trait TableModel {
fn row_count(&self) -> i64;
fn headers(&self) -> Vec<String>;
fn cell(&self, row: i64, col: i32) -> String;
fn row_color(&self, _row: i64) -> Option<(u8, u8)> {
None
}
}
#[repr(C)]
struct TableModelC {
row_count: Option<extern "C" fn(*mut TableModelC) -> c_long>,
col_count: Option<extern "C" fn(*mut TableModelC) -> c_int>,
header: Option<extern "C" fn(*mut TableModelC, c_int, *mut c_char, c_int)>,
cell: Option<extern "C" fn(*mut TableModelC, c_long, c_int, *mut c_char, c_int)>,
userdata: *mut c_void,
row_color: Option<extern "C" fn(*mut TableModelC, c_long, *mut u8, *mut u8) -> c_int>,
}
#[inline]
unsafe fn table_model<'a>(m: *mut TableModelC) -> &'a dyn TableModel {
&**((*m).userdata as *const Box<dyn TableModel>)
}
extern "C" fn tbl_row_count(m: *mut TableModelC) -> c_long {
unsafe { table_model(m).row_count() as c_long }
}
extern "C" fn tbl_col_count(m: *mut TableModelC) -> c_int {
unsafe { table_model(m).headers().len() as c_int }
}
extern "C" fn tbl_header(m: *mut TableModelC, col: c_int, buf: *mut c_char, n: c_int) {
unsafe {
let h = table_model(m).headers();
let s = h.get(col as usize).map(String::as_str).unwrap_or("");
fill_cbuf(s, buf, n);
}
}
extern "C" fn tbl_cell(m: *mut TableModelC, row: c_long, col: c_int, buf: *mut c_char, n: c_int) {
unsafe {
let s = table_model(m).cell(row as i64, col as i32);
fill_cbuf(&s, buf, n);
}
}
extern "C" fn tbl_row_color(m: *mut TableModelC, row: c_long, fg: *mut u8, bg: *mut u8) -> c_int {
unsafe {
match table_model(m).row_color(row as i64) {
Some((f, b)) => {
if !fg.is_null() {
*fg = f;
}
if !bg.is_null() {
*bg = b;
}
1
}
None => 0,
}
}
}
pub struct Table<'a> {
ptr: *mut sys::gtcaca_table_widget_t,
cmodel: Box<TableModelC>,
ud: *mut Box<dyn TableModel>,
_p: PhantomData<&'a ()>,
}
impl<'a> Table<'a> {
pub fn new(
parent: &'a impl Widget,
x: i32,
y: i32,
width: i32,
height: i32,
model: Box<dyn TableModel>,
) -> Table<'a> {
let ptr = unsafe { sys::gtcaca_table_new(parent.as_widget_ptr(), x, y, width, height) };
assert!(!ptr.is_null(), "gtcaca_table_new returned NULL");
let ud = Box::into_raw(Box::new(model));
let mut cmodel = Box::new(TableModelC {
row_count: Some(tbl_row_count),
col_count: Some(tbl_col_count),
header: Some(tbl_header),
cell: Some(tbl_cell),
userdata: ud as *mut c_void,
row_color: Some(tbl_row_color),
});
unsafe {
sys::gtcaca_table_set_model(
ptr,
cmodel.as_mut() as *mut TableModelC as *mut sys::gtcaca_table_model_t,
);
}
Table { ptr, cmodel, ud, _p: PhantomData }
}
pub fn set_column_widths(&self, widths: &[i32]) {
unsafe { sys::gtcaca_table_set_column_widths(self.ptr, widths.as_ptr(), widths.len() as c_int) };
}
pub fn set_current(&self, row: i64, col: i32) {
unsafe { sys::gtcaca_table_set_current(self.ptr, row as c_long, col) };
}
pub fn current_col(&self) -> i32 {
unsafe { sys::gtcaca_table_current_col(self.ptr) }
}
pub fn current_row(&self) -> i64 {
unsafe { sys::gtcaca_table_current_row(self.ptr) as i64 }
}
pub fn set_title(&self, title: &str) {
let c = cstring(title).unwrap_or_default();
unsafe { sys::gtcaca_table_set_title(self.ptr, c.as_ptr()) };
}
pub fn key(&self, key: i32) -> bool {
unsafe { sys::gtcaca_table_key(self.ptr, key, ptr::null_mut()) != 0 }
}
pub fn draw(&self) {
unsafe { sys::gtcaca_table_draw(self.ptr) };
}
}
impl Widget for Table<'_> {
fn as_widget_ptr(&self) -> *mut sys::gtcaca_widget_t {
self.ptr as *mut sys::gtcaca_widget_t
}
}
impl Drop for Table<'_> {
fn drop(&mut self) {
let _ = &self.cmodel;
unsafe { drop(Box::from_raw(self.ud)) };
}
}
pub trait TreeModel {
fn child_count(&self, node: *mut c_void) -> i64;
fn child(&self, node: *mut c_void, index: i64) -> *mut c_void;
fn has_children(&self, node: *mut c_void) -> bool;
fn label(&self, node: *mut c_void) -> String;
}
#[repr(C)]
struct TreeModelC {
child_count: Option<extern "C" fn(*mut TreeModelC, *mut c_void) -> c_long>,
child: Option<extern "C" fn(*mut TreeModelC, *mut c_void, c_long) -> *mut c_void>,
label: Option<extern "C" fn(*mut TreeModelC, *mut c_void, *mut c_char, c_int)>,
has_children: Option<extern "C" fn(*mut TreeModelC, *mut c_void) -> c_int>,
userdata: *mut c_void,
draw_row:
Option<extern "C" fn(*mut TreeModelC, *mut c_void, c_int, c_int, c_int, c_int)>,
}
#[inline]
unsafe fn tree_model<'a>(m: *mut TreeModelC) -> &'a dyn TreeModel {
&**((*m).userdata as *const Box<dyn TreeModel>)
}
extern "C" fn tr_child_count(m: *mut TreeModelC, node: *mut c_void) -> c_long {
unsafe { tree_model(m).child_count(node) as c_long }
}
extern "C" fn tr_child(m: *mut TreeModelC, node: *mut c_void, i: c_long) -> *mut c_void {
unsafe { tree_model(m).child(node, i as i64) }
}
extern "C" fn tr_label(m: *mut TreeModelC, node: *mut c_void, buf: *mut c_char, n: c_int) {
unsafe { fill_cbuf(&tree_model(m).label(node), buf, n) }
}
extern "C" fn tr_has_children(m: *mut TreeModelC, node: *mut c_void) -> c_int {
unsafe { tree_model(m).has_children(node) as c_int }
}
pub struct Tree<'a> {
ptr: *mut sys::gtcaca_tree_widget_t,
cmodel: Box<TreeModelC>,
ud: *mut Box<dyn TreeModel>,
_p: PhantomData<&'a ()>,
}
impl<'a> Tree<'a> {
pub fn new(
parent: &'a impl Widget,
x: i32,
y: i32,
width: i32,
height: i32,
root: *mut c_void,
model: Box<dyn TreeModel>,
) -> Tree<'a> {
let ptr = unsafe { sys::gtcaca_tree_new(parent.as_widget_ptr(), x, y, width, height) };
assert!(!ptr.is_null(), "gtcaca_tree_new returned NULL");
let ud = Box::into_raw(Box::new(model));
let mut cmodel = Box::new(TreeModelC {
child_count: Some(tr_child_count),
child: Some(tr_child),
label: Some(tr_label),
has_children: Some(tr_has_children),
userdata: ud as *mut c_void,
draw_row: None,
});
unsafe {
sys::gtcaca_tree_set_model(
ptr,
cmodel.as_mut() as *mut TreeModelC as *mut sys::gtcaca_tree_model_t,
);
set_tree_root(ptr, root);
}
Tree { ptr, cmodel, ud, _p: PhantomData }
}
pub fn reload(&self) {
let m = &*self.cmodel as *const TreeModelC as *mut sys::gtcaca_tree_model_t;
unsafe { sys::gtcaca_tree_set_model(self.ptr, m) };
}
pub fn selected(&self) -> *mut c_void {
unsafe { sys::gtcaca_tree_selected_node(self.ptr) }
}
pub fn select(&self, node: *mut c_void) {
unsafe { sys::gtcaca_tree_select(self.ptr, node) };
}
pub fn set_title(&self, title: &str) {
let c = cstring(title).unwrap_or_default();
unsafe { sys::gtcaca_tree_set_title(self.ptr, c.as_ptr()) };
}
pub fn key(&self, key: i32) -> bool {
unsafe { sys::gtcaca_tree_key(self.ptr, key, ptr::null_mut()) != 0 }
}
pub fn draw(&self) {
unsafe { sys::gtcaca_tree_draw(self.ptr) };
}
}
impl Widget for Tree<'_> {
fn as_widget_ptr(&self) -> *mut sys::gtcaca_widget_t {
self.ptr as *mut sys::gtcaca_widget_t
}
}
impl Drop for Tree<'_> {
fn drop(&mut self) {
let _ = &self.cmodel;
unsafe { drop(Box::from_raw(self.ud)) };
}
}
unsafe fn set_tree_root(_ptr: *mut sys::gtcaca_tree_widget_t, _root: *mut c_void) {
}
pub struct Hexview<'a> {
ptr: *mut sys::gtcaca_hexview_widget_t,
_p: PhantomData<&'a ()>,
}
impl<'a> Hexview<'a> {
pub fn new(parent: &'a impl Widget, x: i32, y: i32, width: i32, height: i32) -> Hexview<'a> {
let ptr = unsafe { sys::gtcaca_hexview_new(parent.as_widget_ptr(), x, y, width, height) };
assert!(!ptr.is_null(), "gtcaca_hexview_new returned NULL");
Hexview { ptr, _p: PhantomData }
}
pub fn set_data(&self, data: &[u8]) {
unsafe { sys::gtcaca_hexview_set_data(self.ptr, data.as_ptr(), data.len() as c_int) };
}
pub fn set_highlight(&self, off: i32, len: i32) {
unsafe { sys::gtcaca_hexview_set_highlight(self.ptr, off, len) };
}
pub fn cursor(&self) -> Option<usize> {
let c = unsafe { sys::gtcaca_hexview_cursor(self.ptr) };
if c < 0 {
None
} else {
Some(c as usize)
}
}
pub fn set_title(&self, title: &str) {
let c = cstring(title).unwrap_or_default();
unsafe { sys::gtcaca_hexview_set_title(self.ptr, c.as_ptr()) };
}
pub fn key(&self, key: i32) -> bool {
unsafe { sys::gtcaca_hexview_key(self.ptr, key, ptr::null_mut()) != 0 }
}
pub fn draw(&self) {
unsafe { sys::gtcaca_hexview_draw(self.ptr) };
}
}
impl Widget for Hexview<'_> {
fn as_widget_ptr(&self) -> *mut sys::gtcaca_widget_t {
self.ptr as *mut sys::gtcaca_widget_t
}
}
pub struct Statusbar {
ptr: *mut sys::gtcaca_statusbar_widget_t,
}
impl Statusbar {
pub fn new(text: &str) -> Statusbar {
let c = cstring(text).unwrap_or_default();
let ptr = unsafe { sys::gtcaca_statusbar_new(c.as_ptr()) };
assert!(!ptr.is_null(), "gtcaca_statusbar_new returned NULL");
Statusbar { ptr }
}
pub fn set_text(&self, text: &str) {
let c = cstring(text).unwrap_or_default();
unsafe { sys::gtcaca_statusbar_set_text(self.ptr, c.as_ptr()) };
}
pub fn draw(&self) {
unsafe { sys::gtcaca_statusbar_draw(self.ptr) };
}
}
pub struct Menu {
ptr: *mut sys::gtcaca_menu_widget_t,
}
impl Menu {
pub fn new() -> Menu {
let ptr = unsafe { sys::gtcaca_menu_new() };
assert!(!ptr.is_null(), "gtcaca_menu_new returned NULL");
Menu { ptr }
}
pub fn add_entry(&self, title: &str) -> i32 {
let c = cstring(title).unwrap_or_default();
unsafe { sys::gtcaca_menu_add_entry(self.ptr, c.as_ptr()) }
}
pub fn add_item(&self, entry: i32, label: &str, shortcut: &str, action: extern "C" fn(*mut c_void), userdata: *mut c_void) -> i32 {
let l = cstring(label).unwrap_or_default();
let s = cstring(shortcut).unwrap_or_default();
unsafe {
sys::gtcaca_menu_add_item(
self.ptr,
entry,
l.as_ptr(),
s.as_ptr(),
Some(std::mem::transmute::<
extern "C" fn(*mut c_void),
unsafe extern "C" fn(*mut c_void),
>(action)),
userdata,
)
}
}
pub fn add_separator(&self, entry: i32) {
unsafe { sys::gtcaca_menu_add_separator(self.ptr, entry) };
}
pub fn handle_key(&self, key: i32) -> bool {
unsafe { sys::gtcaca_menu_handle_key(self.ptr, key) != 0 }
}
pub fn set_focus(&self, on: bool) {
unsafe { sys::gtcaca_menu_set_focus(self.ptr, on as c_int) };
}
pub fn is_focused(&self) -> bool {
unsafe { sys::gtcaca_menu_is_focused(self.ptr) != 0 }
}
pub fn set_item_enabled(&self, entry: i32, item: i32, enabled: bool) {
unsafe { sys::gtcaca_menu_set_item_enabled(self.ptr, entry, item, enabled as c_int) };
}
pub fn draw(&self) {
unsafe { sys::gtcaca_menu_draw(self.ptr) };
}
pub fn as_ptr(&self) -> *mut sys::gtcaca_menu_widget_t {
self.ptr
}
}
impl Default for Menu {
fn default() -> Self {
Menu::new()
}
}
pub struct Linechart<'a> {
ptr: *mut sys::gtcaca_linechart_widget_t,
_p: PhantomData<&'a ()>,
}
impl<'a> Linechart<'a> {
pub fn new(parent: &'a impl Widget, x: i32, y: i32, width: i32, height: i32) -> Linechart<'a> {
let ptr = unsafe { sys::gtcaca_linechart_new(parent.as_widget_ptr(), x, y, width, height) };
assert!(!ptr.is_null(), "gtcaca_linechart_new returned NULL");
Linechart { ptr, _p: PhantomData }
}
pub fn add_series(&self, y: &[f64], colour: u8) -> i32 {
unsafe { sys::gtcaca_linechart_add_series(self.ptr, y.as_ptr(), y.len() as c_int, colour) }
}
pub fn add_series_styled(&self, y: &[f64], colour: u8, style: i32, name: &str) -> i32 {
let c = cstring(name).unwrap_or_default();
unsafe {
sys::gtcaca_linechart_add_series_styled(
self.ptr,
y.as_ptr(),
y.len() as c_int,
colour,
style,
c.as_ptr(),
)
}
}
pub fn set_log_y(&self, on: bool) {
unsafe { sys::gtcaca_linechart_set_log_y(self.ptr, on as c_int) };
}
pub fn clear(&self) {
unsafe { sys::gtcaca_linechart_clear(self.ptr) };
}
pub fn set_range(&self, ymin: f64, ymax: f64) {
unsafe { sys::gtcaca_linechart_set_range(self.ptr, ymin, ymax) };
}
pub fn set_xspan(&self, xspan: f64, unit: &str) {
let c = cstring(unit).unwrap_or_default();
unsafe { sys::gtcaca_linechart_set_xspan(self.ptr, xspan, c.as_ptr()) };
}
pub fn set_title(&self, title: &str) {
let c = cstring(title).unwrap_or_default();
unsafe { sys::gtcaca_linechart_set_title(self.ptr, c.as_ptr()) };
}
pub fn draw(&self) {
unsafe { sys::gtcaca_linechart_draw(self.ptr) };
}
}
impl Widget for Linechart<'_> {
fn as_widget_ptr(&self) -> *mut sys::gtcaca_widget_t {
self.ptr as *mut sys::gtcaca_widget_t
}
}
pub struct Image<'a> {
ptr: *mut sys::gtcaca_image_widget_t,
_p: PhantomData<&'a ()>,
}
impl<'a> Image<'a> {
pub fn new(parent: &'a impl Widget, x: i32, y: i32, width: i32, height: i32) -> Image<'a> {
let ptr = unsafe { sys::gtcaca_image_new(parent.as_widget_ptr(), x, y, width, height) };
assert!(!ptr.is_null(), "gtcaca_image_new returned NULL");
Image { ptr, _p: PhantomData }
}
pub fn load(&self, path: &str) -> bool {
let c = cstring(path).unwrap_or_default();
unsafe { sys::gtcaca_image_load(self.ptr, c.as_ptr()) != 0 }
}
pub fn draw(&self) {
unsafe { sys::gtcaca_image_draw(self.ptr) };
}
}
impl Widget for Image<'_> {
fn as_widget_ptr(&self) -> *mut sys::gtcaca_widget_t {
self.ptr as *mut sys::gtcaca_widget_t
}
}
pub struct Textlist<'a> {
ptr: *mut sys::gtcaca_textlist_widget_t,
_p: PhantomData<&'a ()>,
}
impl<'a> Textlist<'a> {
pub fn new(parent: &'a impl Widget, x: i32, y: i32) -> Textlist<'a> {
let ptr = unsafe { sys::gtcaca_textlist_new(parent.as_widget_ptr(), x, y) };
assert!(!ptr.is_null(), "gtcaca_textlist_new returned NULL");
Textlist { ptr, _p: PhantomData }
}
pub fn set_view_size(&self, rows: u32) {
unsafe { sys::gtcaca_textlist_widget_set_view_size(self.ptr, rows) };
}
pub fn append(&self, item: &str) {
let c = cstring(item).unwrap_or_default();
unsafe { sys::gtcaca_textlist_append(self.ptr, c.as_ptr() as *mut c_char) };
}
pub fn clear(&self) {
unsafe { sys::gtcaca_textlist_clear(self.ptr) };
}
pub fn selection_up(&self) {
unsafe { sys::gtcaca_textlist_selection_up(self.ptr) };
}
pub fn selection_down(&self) {
unsafe { sys::gtcaca_textlist_selection_down(self.ptr) };
}
pub fn set_search_enabled(&self, enabled: bool) {
unsafe { sys::gtcaca_textlist_set_search_enabled(self.ptr, enabled as c_int) };
}
pub fn is_searching(&self) -> bool {
unsafe { sys::gtcaca_textlist_is_searching(self.ptr) != 0 }
}
pub fn selected(&self) -> Option<String> {
let p = unsafe { sys::gtcaca_textlist_get_text_selected(self.ptr) };
if p.is_null() {
None
} else {
Some(unsafe { CStr::from_ptr(p) }.to_string_lossy().into_owned())
}
}
pub fn draw(&self) {
unsafe { sys::gtcaca_textlist_draw(self.ptr) };
}
}
impl Widget for Textlist<'_> {
fn as_widget_ptr(&self) -> *mut sys::gtcaca_widget_t {
self.ptr as *mut sys::gtcaca_widget_t
}
}