futtl 0.1.3

Rust bindings for the FUTTL terminal UI library
use crate::bindings::*;
use std::ffi::CString;
use bitflags::bitflags;

#[allow(warnings)]
mod bindings;

type FuttlCtx = bindings::futtl_ctx;

pub struct FuttlWin {
    ctx: FuttlCtx,
    children: Vec<Box<FuttlBox>>
}

pub struct FuttlBox {
    ctx: FuttlCtx,
    children: Vec<Box<FuttlBox>>,
    owned_by_parent: bool,
}

#[derive(Clone, Copy)]
pub struct FuttlColor {
    pub r: u8,
    pub g: u8,
    pub b: u8
}

pub struct FuttlColorSet {
    pub fg: FuttlColor,
    pub bg: FuttlColor
}

impl FuttlColor {
    pub fn new(r: u8, g: u8, b: u8) -> Self {
        Self { r, g, b }
    }
}

impl FuttlColorSet {
    pub fn new(fg: FuttlColor, bg: FuttlColor) -> Self {
        Self { fg, bg }
    }
}

bitflags! {
    #[repr(transparent)]
    pub struct Attr: i32 {
        const NONE          = FUTTL_ATTR_NONE as i32;
        const BOLD          = FUTTL_ATTR_BOLD as i32;
        const DIM           = FUTTL_ATTR_DIM as i32;
        const ITALIC        = FUTTL_ATTR_ITALIC as i32;
        const UNDERLINE     = FUTTL_ATTR_UNDERLINE as i32;
        const BLINK         = FUTTL_ATTR_BLINK as i32;
        const INVERSE       = FUTTL_ATTR_INVERSE as i32;
        const HIDDEN        = FUTTL_ATTR_HIDDEN as i32;
        const STRIKETHROUGH = FUTTL_ATTR_STRIKETHROUGH as i32;
        const IGNORE_BG     = FUTTL_ATTR_IGNORE_BG as i32;
        const IGNORE_FG     = FUTTL_ATTR_IGNORE_FG as i32;
        const IGNORE_COLOR  = FUTTL_ATTR_IGNORE_COLOR as i32;
        const ALL           = FUTTL_ATTR_ALL as i32;
    }
}

pub enum InputMode {
    Blocking = FUTTL_INPUT_BLOCKING as isize,
    NonBlocking = FUTTL_INPUT_NONBLOCKING as isize
}

#[allow(dead_code)]
pub fn set_input_blocking(input_blocking: InputMode) {
    unsafe {futtl_set_input_blocking(input_blocking as i32)}
}

pub trait Futtl {
    fn ctx(&mut self) -> &mut FuttlCtx;
    fn children(&mut self) -> &mut Vec<Box<FuttlBox>>;

    fn get_char(&mut self) -> char {
        unsafe {bindings::futtl_get_char(self.ctx()) as u8 as char}
    }

    fn write_char(&mut self, c: char) {
        unsafe {bindings::futtl_write_char(self.ctx(), c as i8)}
    }

    fn put_char(&mut self, c: char) {
        unsafe {bindings::futtl_put_char(self.ctx(), c as i8)}
    }

    fn write_unicode(&mut self, c: u32) {
        unsafe {bindings::futtl_write_unicode(self.ctx(), c)}
    }

    fn put_unicode(&mut self, c: u32) {
        unsafe {bindings::futtl_put_unicode(self.ctx(), c)}
    }

    fn write_str(&mut self, s: &str) -> i32 {
        let c_str = CString::new(s).unwrap();
        unsafe {bindings::futtl_write_str(self.ctx(), c_str.as_ptr())}
    }

    fn put_str(&mut self, s: &str) -> i32 {
        let c_str = CString::new(s).unwrap();
        unsafe {bindings::futtl_put_str(self.ctx(), c_str.as_ptr())}
    }

    fn write_wrap_str(&mut self, s: &str) {
        let c_str = CString::new(s).unwrap();
        unsafe {bindings::futtl_write_wrap_str(self.ctx(), c_str.as_ptr())}
    }

    fn put_wrap_str(&mut self, s: &str) {
        let c_str = CString::new(s).unwrap();
        unsafe {bindings::futtl_put_wrap_str(self.ctx(), c_str.as_ptr())}
    }

    fn add_color(&mut self, cs: FuttlColorSet, idx: usize) {
        if idx >= (self.ctx().color_count as usize) {
            return;
        }

        let c_fg: bindings::futtl_color = bindings::futtl_color {
            r: cs.fg.r,
            g: cs.fg.g,
            b: cs.fg.b,
        };
        let c_bg: bindings::futtl_color = bindings::futtl_color {
            r: cs.bg.r,
            g: cs.bg.g,
            b: cs.bg.b,
        };

        let c_cs: bindings::futtl_color_set = bindings::futtl_color_set {
            fg: c_fg,
            bg: c_bg
        };
        unsafe {
            *self.ctx().colors.add(idx) = c_cs;
        }
    }

    fn set_color(&mut self, idx: usize) {
        unsafe {futtl_set_color(self.ctx(), idx as i32)}
    }

    fn add_attribute(&mut self, attribute: Attr) {
        unsafe {futtl_add_attribute(self.ctx(), attribute.bits())}
    }

    fn remove_attribute(&mut self, attribute: Attr) {
        unsafe {futtl_remove_attribute(self.ctx(), attribute.bits())}
    }

    fn clear(&mut self) {
        unsafe {futtl_clear(self.ctx())}
    }

    fn set_curs(&mut self, x: i32, y: i32) -> i32 {
        unsafe {futtl_set_curs(self.ctx(), x, y)}
    }

    fn get_in(&mut self) -> i32 {
        unsafe {futtl_get_in(self.ctx())}
    }

    fn add_child(&mut self, child: &mut FuttlBox) -> i32 {
        unsafe {futtl_add_child(self.ctx(), child.ctx() as *mut FuttlCtx)}
    }

    fn remove_child(&mut self, index: usize) -> i32 {
        if index >= self.children().len() {
            return -1;
        }

        let result = unsafe {
            futtl_remove_child(
                self.ctx() as *mut FuttlCtx,
                self.children()[index].ctx().child_index
            )
        };

        if result == 0 {
            let mut child = self.children().remove(index);
            child.owned_by_parent = false;
        }

        result
    }

    fn create_box(&mut self, width: i32, height: i32) -> usize {
        let mut child = Box::new(FuttlBox {
            ctx: unsafe { std::mem::zeroed() },
            children: Vec::new(),
            owned_by_parent: true,
        });

        unsafe {
            bindings::futtl_box_init(
                &mut child.ctx,
                width,
                height,
                self.ctx() as *mut FuttlCtx,
            );
        }

        self.children().push(child);

        self.children().len() - 1
    }
}

impl Futtl for FuttlWin {
    fn ctx(&mut self) -> &mut FuttlCtx {
        &mut self.ctx
    }

    fn children(&mut self) -> &mut Vec<Box<FuttlBox>> {
        &mut self.children
    }
}

impl Futtl for FuttlBox {
    fn ctx(&mut self) -> &mut FuttlCtx {
        &mut self.ctx
    }

    fn children(&mut self) -> &mut Vec<Box<FuttlBox>> {
        &mut self.children
    }
}

impl FuttlWin {
    pub fn get_ctx(&mut self) -> &mut FuttlCtx {
        self.ctx()
    }

    pub fn init(&mut self, color_count: i32, default_attribute: i32) {
        unsafe {
            bindings::futtl_init(
                self.ctx(),
                color_count,
                default_attribute,
            );
        }
    }

    pub fn new(color_count: i32, default_attribute: Attr) -> Self {
        let mut this = Self {
            ctx: unsafe {std::mem::zeroed::<FuttlCtx>()},
            children: Vec::new(),
        };
        
        this.init(color_count, default_attribute.bits());

        this
    }

    pub fn show(&mut self) {
        unsafe {futtl_show(self.ctx())}
    }
}

impl FuttlBox {
    pub fn get_ctx(&mut self) -> &mut FuttlCtx {
        self.ctx()
    }

    pub fn init(&mut self, width: i32, height: i32, parent: &mut dyn Futtl) {
        unsafe {
            bindings::futtl_box_init(
                self.ctx(),
                width,
                height,
                parent.ctx(),
            );
        }
    }

    pub fn show(&mut self, x: i32, y: i32) {
        unsafe {futtl_box_show(self.ctx(),x ,y)}
    }
}

impl Drop for FuttlWin {
    fn drop(&mut self) {
        unsafe {
            bindings::futtl_deinit(self.ctx());
        }
    }
}
impl Drop for FuttlBox {
    fn drop(&mut self) {
        if !self.owned_by_parent {
            unsafe {
                bindings::futtl_box_deinit(self.ctx());
            }
        }
    }
}

#[cfg(test)]
mod tests;