<commit_before>// Temporary fix before certain constants are used.
#![allow(dead_code)]

use std::io::{Error, ErrorKind};

use term::terminfo::TermInfo;
use term::terminfo::parm;
use term::terminfo::parm::{
    Param,
    Variables,
};

// String constants correspond to terminfo capnames and are used inside the module for convenience.
const ENTER_CA: &'static str = "smcup";
const EXIT_CA: &'static str = "rmcup";
const SHOW_CURSOR: &'static str = "cnorm";
const HIDE_CURSOR: &'static str = "civis";
const SET_CURSOR: &'static str = "cup";
const CLEAR: &'static str = "clear";
const RESET: &'static str = "sgr0";
const UNDERLINE: &'static str = "smul";
const BOLD: &'static str = "bold";
const BLINK: &'static str = "blink";
const REVERSE: &'static str = "rev";
const SETFG: &'static str = "setaf";
const SETBG: &'static str = "setab";

// Terminfo keys. These are arrays because the terminfo database from the `term` crate sometimes
// uses the variable name and othertimes the capname.
//
// Arrays are formatted as ["variable_name", "cap_name"].
const KEY_F1: &'static [&'static str] = &["key_f1", "kf1"];
const KEY_F2: &'static [&'static str] = &["key_f2", "kf2"];
const KEY_F3: &'static [&'static str] = &["key_f3", "kf3"];
const KEY_F4: &'static [&'static str] = &["key_f4", "kf4"];
const KEY_F5: &'static [&'static str] = &["key_f5", "kf5"];
const KEY_F6: &'static [&'static str] = &["key_f6", "kf6"];
const KEY_F7: &'static [&'static str] = &["key_f7", "kf7"];
const KEY_F8: &'static [&'static str] = &["key_f8", "kf8"];
const KEY_F9: &'static [&'static str] = &["key_f9", "kf9"];
const KEY_F10: &'static [&'static str] = &["key_f10", "kf10"];
const KEY_F11: &'static [&'static str] = &["key_f11", "kf11"];
const KEY_F12: &'static [&'static str] = &["key_f12", "kf12"];
const KEY_UP: &'static [&'static str] = &["key_up", "kcuu1"];
const KEY_DOWN: &'static [&'static str] = &["key_down", "kcud1"];
const KEY_LEFT: &'static [&'static str] = &["key_left", "kcub1"];
const KEY_RIGHT: &'static [&'static str] = &["key_right", "kcuf1"];

// Array of terminal keys.
const KEYS: &'static [&'static [&'static str]] = &[
    KEY_F1,
    KEY_F2,
    KEY_F3,
    KEY_F4,
    KEY_F5,
    KEY_F6,
    KEY_F7,
    KEY_F8,
    KEY_F9,
    KEY_F10,
    KEY_F11,
    KEY_F12,
    KEY_UP,
    KEY_DOWN,
    KEY_LEFT,
    KEY_RIGHT,
];

// Array of terminal capabilities. Used as an iterator to test for functionality.
//
// At the moment all functionality is required, however in the future we should implement optional
// functionality checks so the absence of underlining or reverse video doesn't cause initialization
// to fail.
//
// TODO: Optional functionality testing.
const CAPABILITIES: &'static [&'static str] = &[
    ENTER_CA,
    EXIT_CA,
    SHOW_CURSOR,
    HIDE_CURSOR,
    SET_CURSOR,
    CLEAR,
    RESET,
    UNDERLINE,
    BOLD,
    REVERSE,
    SETFG,
    SETBG,
];

// Driver capabilities are an enum instead of string constants (there are string constants private
// to the module however, those are only used for naming convenience and disambiguation)
// to take advantage of compile-time type-checking instead of hoping invalid strings aren't passed.
// This allows us to guarantee that driver accesses will succeed. In addition, using an enum means
// Driver doesn't need hard-coded methods for each capability we want to use.
#[allow(dead_code)]
pub enum DevFn {
    EnterCa,
    ExitCa,
    ShowCursor,
    HideCursor,
    SetCursor(usize, usize),
    Clear,
    Reset,
    Underline,
    Bold,
    Blink,
    Reverse,
    SetFg(u8),
    SetBg(u8),
}

impl DevFn {
    fn as_str(&self) -> &'static str {
        match *self {
            DevFn::EnterCa => ENTER_CA,
            DevFn::ExitCa => EXIT_CA,
            DevFn::ShowCursor => SHOW_CURSOR,
            DevFn::HideCursor => HIDE_CURSOR,
            DevFn::SetCursor(..) => SET_CURSOR,
            DevFn::Clear => CLEAR,
            DevFn::Reset => RESET,
            DevFn::Underline => UNDERLINE,
            DevFn::Bold => BOLD,
            DevFn::Blink => BLINK,
            DevFn::Reverse => REVERSE,
            DevFn::SetFg(..) => SETFG,
            DevFn::SetBg(..) => SETBG,
        }
    }
}

pub struct Driver {
    tinfo: &'static TermInfo,
}

// We will never need to mutate the terminfo database, and it is only used internally as a lookup
// table for the driver. `lazy_static` seems to be the best approach at the moment.
lazy_static! {
    static ref TINFO: TermInfo = {
        TermInfo::from_env().unwrap_or({
            TermInfo {
                names: Default::default(),
                bools: Default::default(),
                numbers: Default::default(),
                strings: Default::default(),
            }
        })
    };
}

// Validates and returns a reference to the terminfo database.
//
// If this function returns Ok(..), the contained terminfo database is guaranteed to have the
// functionality found in CAPABILITIES.
//
// If this function returns Err(..), the terminfo database did not contain all the required
// functionality; the error returned will provide more specific detail.
fn get_tinfo() -> Result<&'static TermInfo, Error> {
    let tinfo = &*TINFO;

    for capname in CAPABILITIES {
        if !tinfo.strings.contains_key(*capname) {
            return Err(Error::new(ErrorKind::NotFound,
                                  format!("terminal missing capability: '{}'", capname)));
        }
    }
    Ok(tinfo)
}

impl Driver {
    // Creates a new `Driver`
    //
    // If successful, the terminfo database is guaranteed to contain all capabilities we support.
    pub fn new() -> Result<Driver, Error> {
        let tinfo = try!(get_tinfo());
        Ok(Driver {
            tinfo: tinfo,
        })
    }

    // Returns the device specific escape sequence for the given `DevFn`.
    //
    // get() will not return an error, and (in theory) should never panic. The `DevFn` enum
    // restricts possible inputs to a subset that will not fail when passed to `parm::expand()`.
    // This can be verified by examining the source of the `parm::expand()` function in the `term`
    // crate.
    //
    // Furthermore, the pre-flight checks on initialization of `Driver` ensure that every
    // capability is present, thus the call to `Hashmap::get()` should never fail.
    pub fn get(&self, dfn: DevFn) -> Vec<u8> {
        let capname = dfn.as_str();
        let cap = self.tinfo.strings.get(capname).unwrap();

        match dfn {
            DevFn::SetFg(attr) | DevFn::SetBg(attr) => {
                let params = &[Param::Number(attr as i32)];
                let mut vars = Variables::new();
                parm::expand(cap, params, &mut vars).unwrap()
            },
            DevFn::SetCursor(x, y) => {
                let params = &[Param::Number(y as i32), Param::Number(x as i32)];
                let mut vars = Variables::new();
                parm::expand(cap, params, &mut vars).unwrap()
            },
            _ => {
                cap.clone()
            },
        }
    }
}

<commit_msg>driver changes<commit_after>// Temporary fix before certain constants are used.
#![allow(dead_code)]

use std::io::{Error, ErrorKind};

use term::terminfo::TermInfo;
use term::terminfo::parm;
use term::terminfo::parm::{Param, Variables};

// Terminfo keys. These are arrays because the terminfo database from the `term` crate sometimes
// uses the variable name and othertimes the capname.
//
// Arrays are formatted as ["variable_name", "cap_name"].
const KEY_F1: &'static [&'static str] = &["key_f1", "kf1"];
const KEY_F2: &'static [&'static str] = &["key_f2", "kf2"];
const KEY_F3: &'static [&'static str] = &["key_f3", "kf3"];
const KEY_F4: &'static [&'static str] = &["key_f4", "kf4"];
const KEY_F5: &'static [&'static str] = &["key_f5", "kf5"];
const KEY_F6: &'static [&'static str] = &["key_f6", "kf6"];
const KEY_F7: &'static [&'static str] = &["key_f7", "kf7"];
const KEY_F8: &'static [&'static str] = &["key_f8", "kf8"];
const KEY_F9: &'static [&'static str] = &["key_f9", "kf9"];
const KEY_F10: &'static [&'static str] = &["key_f10", "kf10"];
const KEY_F11: &'static [&'static str] = &["key_f11", "kf11"];
const KEY_F12: &'static [&'static str] = &["key_f12", "kf12"];
const KEY_UP: &'static [&'static str] = &["key_up", "kcuu1"];
const KEY_DOWN: &'static [&'static str] = &["key_down", "kcud1"];
const KEY_LEFT: &'static [&'static str] = &["key_left", "kcub1"];
const KEY_RIGHT: &'static [&'static str] = &["key_right", "kcuf1"];

// Array of terminal keys.
const KEYS: &'static [&'static [&'static str]] = &[
    KEY_F1,
    KEY_F2,
    KEY_F3,
    KEY_F4,
    KEY_F5,
    KEY_F6,
    KEY_F7,
    KEY_F8,
    KEY_F9,
    KEY_F10,
    KEY_F11,
    KEY_F12,
    KEY_UP,
    KEY_DOWN,
    KEY_LEFT,
    KEY_RIGHT,
];

// String constants correspond to terminfo capnames and are used inside the module for convenience.
const ENTER_CA: &'static str = "smcup";
const EXIT_CA: &'static str = "rmcup";
const SHOW_CURSOR: &'static str = "cnorm";
const HIDE_CURSOR: &'static str = "civis";
const SET_CURSOR: &'static str = "cup";
const CLEAR: &'static str = "clear";
const RESET: &'static str = "sgr0";
const UNDERLINE: &'static str = "smul";
const BOLD: &'static str = "bold";
const BLINK: &'static str = "blink";
const REVERSE: &'static str = "rev";
const SETFG: &'static str = "setaf";
const SETBG: &'static str = "setab";

// Array of terminal capabilities. Used as an iterator to test for functionality.
//
// At the moment all functionality is required, however in the future we should implement optional
// functionality checks so the absence of underlining or reverse video doesn't cause initialization
// to fail.
//
// TODO: Optional functionality testing.
const CAPABILITIES: &'static [&'static str] = &[
    ENTER_CA,
    EXIT_CA,
    SHOW_CURSOR,
    HIDE_CURSOR,
    SET_CURSOR,
    CLEAR,
    RESET,
    UNDERLINE,
    BOLD,
    REVERSE,
    SETFG,
    SETBG,
];

// Driver capabilities are an enum instead of string constants (there are string constants private
// to the module however, those are only used for naming convenience and disambiguation)
// to take advantage of compile-time type-checking instead of hoping invalid strings aren't passed.
// This allows us to guarantee that driver accesses will succeed. In addition, using an enum means
// Driver doesn't need hard-coded methods for each capability we want to use.
pub enum DevFn {
    EnterCa,
    ExitCa,
    ShowCursor,
    HideCursor,
    SetCursor(usize, usize),
    Clear,
    Reset,
    Underline,
    Bold,
    Blink,
    Reverse,
    SetFg(u8),
    SetBg(u8),
}

impl DevFn {
    fn as_str(&self) -> &'static str {
        match *self {
            DevFn::EnterCa => ENTER_CA,
            DevFn::ExitCa => EXIT_CA,
            DevFn::ShowCursor => SHOW_CURSOR,
            DevFn::HideCursor => HIDE_CURSOR,
            DevFn::SetCursor(..) => SET_CURSOR,
            DevFn::Clear => CLEAR,
            DevFn::Reset => RESET,
            DevFn::Underline => UNDERLINE,
            DevFn::Bold => BOLD,
            DevFn::Blink => BLINK,
            DevFn::Reverse => REVERSE,
            DevFn::SetFg(..) => SETFG,
            DevFn::SetBg(..) => SETBG,
        }
    }
}

pub struct Driver {
    tinfo: TermInfo,
}

// Validates and returns a reference to the terminfo database.
//
// If this function returns Ok(..), the contained terminfo database is guaranteed to have the
// functionality found in CAPABILITIES.
//
// If this function returns Err(..), the terminfo database did not contain all the required
// functionality; the error returned will provide more specific detail.
fn get_tinfo() -> Result<TermInfo, Error> {
    let tinfo = TermInfo::from_env().unwrap_or({
        TermInfo {
            names: Default::default(),
            bools: Default::default(),
            numbers: Default::default(),
            strings: Default::default(),
        }
    });

    for capname in CAPABILITIES {
        if !tinfo.strings.contains_key(*capname) {
            return Err(Error::new(ErrorKind::NotFound,
                                  format!("terminal missing capability: '{}'", capname)));
        }
    }
    Ok(tinfo)
}

impl Driver {
    // Creates a new `Driver`
    //
    // If successful, the terminfo database is guaranteed to contain all capabilities we support.
    pub fn new() -> Result<Driver, Error> {
        let tinfo = try!(get_tinfo());
        Ok(Driver {
            tinfo: tinfo,
        })
    }

    // Returns the device specific escape sequence for the given `DevFn`.
    //
    // get() will not return an error, and (in theory) should never panic. The `DevFn` enum
    // restricts possible inputs to a subset that will not fail when passed to `parm::expand()`.
    // This can be verified by examining the source of the `parm::expand()` function in the `term`
    // crate.
    //
    // Furthermore, the pre-flight checks on initialization of `Driver` ensure that every
    // capability is present, thus the call to `Hashmap::get()` should never fail.
    pub fn get(&self, dfn: DevFn) -> Vec<u8> {
        let capname = dfn.as_str();
        let cap = self.tinfo.strings.get(capname).unwrap();

        match dfn {
            DevFn::SetFg(attr) | DevFn::SetBg(attr) => {
                let params = &[Param::Number(attr as i32)];
                let mut vars = Variables::new();
                parm::expand(cap, params, &mut vars).unwrap()
            },
            DevFn::SetCursor(x, y) => {
                let params = &[Param::Number(y as i32), Param::Number(x as i32)];
                let mut vars = Variables::new();
                parm::expand(cap, params, &mut vars).unwrap()
            },
            _ => {
                cap.clone()
            },
        }
    }
}

<|endoftext|>


<commit_before>extern crate libc;

use x11::xlib;
use std::mem;
use std::ffi;
use std::ptr;

use super::super::libx;
use super::container::{self, Container};

const CWX: libc::c_uint = 1<<0;
const CWY: libc::c_uint = 1<<1;
const CWWidth: libc::c_uint = 1<<2;
const CWHeight: libc::c_uint = 1<<3;
const CWBorderWidth: libc::c_uint = 1<<4;
const CWSibling: libc::c_uint =	1<<5;
const CWStackMode: libc::c_uint = 1<<6;

const border: u32 = 1;

#[derive(Debug, Copy, Clone)]
pub struct Rectangle {
    pub x: i32,
    pub y: i32,
    pub width: u32,
    pub height: u32,
}

impl Rectangle {
    pub fn contain(&self, x: i32, y: i32) -> bool{
        if x >= self.x && x <= (self.x + self.width as i32) && y >= self.y && y <= (self.y + self.height as i32) {
            true
        }
        else{
            false
        }
    }
}
#[derive(PartialEq, Clone)]
pub enum Direction {
    Vertical,
    Horizontal,
    Up,
    Down,
    Left,
    Right,
}

pub fn decorate(client: &Container, focused: bool) {
    match client.titlebar {
        Some(rec) => {
            set_titlebar(client, rec, focused);
            set_title(client, rec, focused);
        }
        None => {}
    }
    set_border(client, focused);
}

fn set_titlebar(client: &Container, rec: Rectangle, focused: bool) {
    let context = client.context;
    let gc = context.gc;
    let display = context.display;
    unsafe {
        if focused {
            xlib::XSetBackground(display, gc,
                                 context.focus_bg);
            xlib::XSetForeground(display, gc,
                                 context.focus_fg);
        }
        else{
            xlib::XSetBackground(display, gc,
                                 context.unfocus_bg);
            xlib::XSetForeground(display, gc,
                                 context.unfocus_fg);
        }

        let pid = client.pid();
        let r = xlib::XFillRectangle(display,
                                     pid, gc,
                                     rec.x, rec.y,
                                     rec.width as u32,
                                     rec.height as u32);
    }
}

fn set_title(client: &Container, rec: Rectangle, focused: bool) {
    let context = client.context;
    let gc = context.gc;
    let display = context.display;
    unsafe{
        if focused {
            xlib::XSetBackground(display, gc,
                                 context.focus_fg);
            xlib::XSetForeground(display, gc,
                                 context.font_color);
        }
        else {
            xlib::XSetBackground(display, gc,
                                 context.unfocus_fg);
            xlib::XSetForeground(display, gc,
                                 context.font_color);
        }

        let offset_x = 10;
        let offset_y = 10;

        let res = libx::get_text_property(context, client.raw_id(), xlib::XA_WM_NAME);

        match res {
            Some(s) => {
                let size = s.len() as i32;
                let title = ffi::CString::new(s).unwrap().as_ptr();

                let pid = client.pid();
                let r = xlib::XmbDrawString(display, pid,
                                            context.fontset, gc,
                                            rec.x+offset_x, rec.y+offset_y,
                                            title, size);
            }
            None =>{}
        }
    }
}

fn set_border(client: &Container, focused: bool) {
    let context = client.context;
    let gc = context.gc;
    let display = context.display;

    unsafe {
        if focused {
            xlib::XSetBackground(display, gc,
                                 context.focus_bg);
            xlib::XSetForeground(display, gc,
                                 context.focus_fg);
        }
        else{
            xlib::XSetBackground(display, gc,
                                 context.unfocus_bg);
            xlib::XSetForeground(display, gc,
                                 context.unfocus_fg);
        }
        xlib::XSetLineAttributes(display, gc, border, 0, 0, 0);

        let pid = client.pid();
        let rec = client.rec();
        let r = xlib::XDrawRectangle(display,
                                     pid, gc,
                                     rec.x-border as i32,
                                     rec.y-border as i32,
                                     rec.width+border*2-1 as u32,
                                     rec.height+border*2-1 as u32);
    }
}

#[derive(PartialEq, Clone)]
pub enum Type {
    Tiling,
    Tab,
}

pub fn update_layout(container: &mut Container) {
    match container.layout {
        Type::Tiling => {
            layout_tiling(container);
        }
        Type::Tab => {
            layout_tab(container);
        }
    }
}

fn layout_tiling(container: &mut Container) {
    let size = container.size() as u32;
    if size == 0 {
        return;
    }

    let attrs = container.rec();

    let x_offset = 0; //attrs.x;
    let y_offset = 0; //attrs.y;

    let (focus_id,_) = libx::get_input_focus(container.context);

    let mut x = x_offset;
    let mut y = y_offset;
    let mut w: u32 = 0;
    let mut h: u32 = 0;
    for (i, client) in container.clients.iter_mut().enumerate() {
        let id = client.raw_id();

        match container.direction {
            Direction::Vertical => {
                y = y + h as i32;
                h = (attrs.height as f32 * client.portion) as u32;
                w = attrs.width;
            }
            Direction::Horizontal => {
                x = x + w as i32;
                w = (attrs.width as f32 * client.portion) as u32;
                h = attrs.height;
            }
            _ => {}
        };

        client.titlebar = Some(Rectangle {
            x: x,
            y: y,
            width: w,
            height: client.titlebar_height,
        });

        let titlebar_height = client.titlebar.unwrap().height;
        decorate(client, id==focus_id);;

        client.configure(x+border as i32,
                         y+titlebar_height as i32 + border as i32,
                         w-border*2, h-titlebar_height-border*2);
        // client.map();
    }
}

fn layout_tab(container: &mut Container) {
    let size = container.size() as u32;
    if size == 0{
        return;
    }

    let attrs = container.rec();

    let width = attrs.width / size;
    let x_offset = 0; //attrs.x;
    let y_offset = 0; //attrs.y;
    let (focus_id,_) = libx::get_input_focus(container.context);

    for (i, client) in container.clients.iter_mut().enumerate() {
        let id = client.raw_id();
        client.titlebar = Some(Rectangle {
            x: x_offset + (width * i as u32) as i32,
            y: y_offset as i32,
            width: width,
            height: client.titlebar_height,
        });

        let titlebar_height = client.titlebar.unwrap().height;
        decorate(client, id==focus_id);

        client.configure(x_offset + border as i32,
                         y_offset+titlebar_height as i32 + border as i32,
                         attrs.width - border*2 ,
                         attrs.height -titlebar_height - border*2);
        if focus_id == id {
            // client.map();
            libx::raise_window(client.context, id);
        }
        else{
            // client.unmap();
            // libx::lower_window(client.context, client.id);
        }
    }
}
<commit_msg>refactory<commit_after>extern crate libc;

use x11::xlib;
use std::mem;
use std::ffi;
use std::ptr;

use super::super::libx;
use super::container::{self, Container};

const CWX: libc::c_uint = 1<<0;
const CWY: libc::c_uint = 1<<1;
const CWWidth: libc::c_uint = 1<<2;
const CWHeight: libc::c_uint = 1<<3;
const CWBorderWidth: libc::c_uint = 1<<4;
const CWSibling: libc::c_uint =	1<<5;
const CWStackMode: libc::c_uint = 1<<6;

const border: u32 = 1;

#[derive(Debug, Copy, Clone)]
pub struct Rectangle {
    pub x: i32,
    pub y: i32,
    pub width: u32,
    pub height: u32,
}

impl Rectangle {
    pub fn contain(&self, x: i32, y: i32) -> bool{
        if x >= self.x && x <= (self.x + self.width as i32) && y >= self.y && y <= (self.y + self.height as i32) {
            true
        }
        else{
            false
        }
    }
}
#[derive(PartialEq, Clone)]
pub enum Direction {
    Vertical,
    Horizontal,
    Up,
    Down,
    Left,
    Right,
}

pub fn decorate(client: &Container, focused: bool) {
    match client.titlebar {
        Some(rec) => {
            set_titlebar(client, rec, focused);
            set_title(client, rec, focused);
        }
        None => {}
    }
    set_border(client, focused);
}

fn set_titlebar(client: &Container, rec: Rectangle, focused: bool) {
    let context = client.context;
    let gc = context.gc;
    let display = context.display;

    let (fg, bg) = if focused {
        (context.focus_fg, context.focus_bg)
    }
    else {
        (context.unfocus_fg, context.unfocus_bg)
    };
    unsafe {
        xlib::XSetBackground(display, gc, bg);
        xlib::XSetForeground(display, gc, fg);

        let pid = client.pid();
        let r = xlib::XFillRectangle(display,
                                     pid, gc,
                                     rec.x, rec.y,
                                     rec.width as u32,
                                     rec.height as u32);
    }
}

fn set_title(client: &Container, rec: Rectangle, focused: bool) {
    let context = client.context;
    let gc = context.gc;
    let display = context.display;

    let (fg, bg) = if focused {
        (context.font_color, context.focus_fg)
    }
    else {
        (context.font_color, context.unfocus_fg)
    };
    unsafe{
        xlib::XSetBackground(display, gc, bg);
        xlib::XSetForeground(display, gc, fg);
        let offset_x = 10;
        let offset_y = 10;

        let res = libx::get_text_property(context, client.raw_id(), xlib::XA_WM_NAME);

        match res {
            Some(s) => {
                let x = rec.x+offset_x;
                let y = rec.y+offset_y;
                let pid = client.pid();

                libx::draw_string(context, s, pid, x, y);
            }
            None =>{}
        }
    }
}

fn set_border(client: &Container, focused: bool) {
    let context = client.context;
    let gc = context.gc;
    let display = context.display;

    let (fg, bg) = if focused {
        (context.focus_fg, context.focus_bg)
    }
    else{
        (context.unfocus_fg, context.unfocus_bg)
    };
    unsafe {
        xlib::XSetBackground(display, gc, bg);
        xlib::XSetForeground(display, gc, fg);

        xlib::XSetLineAttributes(display, gc, border, 0, 0, 0);

        let pid = client.pid();
        let rec = client.rec();
        let r = xlib::XDrawRectangle(display,
                                     pid, gc,
                                     rec.x-border as i32,
                                     rec.y-border as i32,
                                     rec.width+border*2-1 as u32,
                                     rec.height+border*2-1 as u32);
    }
}

#[derive(PartialEq, Clone)]
pub enum Type {
    Tiling,
    Tab,
}

pub fn update_layout(container: &mut Container) {
    match container.layout {
        Type::Tiling => {
            layout_tiling(container);
        }
        Type::Tab => {
            layout_tab(container);
        }
    }
}

fn layout_tiling(container: &mut Container) {
    let size = container.size() as u32;
    if size == 0 {
        return;
    }

    let attrs = container.rec();

    let x_offset = 0; //attrs.x;
    let y_offset = 0; //attrs.y;

    let (focus_id,_) = libx::get_input_focus(container.context);

    let mut x = x_offset;
    let mut y = y_offset;
    let mut w: u32 = 0;
    let mut h: u32 = 0;
    for (i, client) in container.clients.iter_mut().enumerate() {
        let id = client.raw_id();

        match container.direction {
            Direction::Vertical => {
                y = y + h as i32;
                h = (attrs.height as f32 * client.portion) as u32;
                w = attrs.width;
            }
            Direction::Horizontal => {
                x = x + w as i32;
                w = (attrs.width as f32 * client.portion) as u32;
                h = attrs.height;
            }
            _ => {}
        };

        client.titlebar = Some(Rectangle {
            x: x,
            y: y,
            width: w,
            height: client.titlebar_height,
        });

        let titlebar_height = client.titlebar.unwrap().height;
        decorate(client, id==focus_id);;

        client.configure(x+border as i32,
                         y+titlebar_height as i32 + border as i32,
                         w-border*2, h-titlebar_height-border*2);
        // client.map();
    }
}

fn layout_tab(container: &mut Container) {
    let size = container.size() as u32;
    if size == 0{
        return;
    }

    let attrs = container.rec();

    let width = attrs.width / size;
    let x_offset = 0; //attrs.x;
    let y_offset = 0; //attrs.y;
    let (focus_id,_) = libx::get_input_focus(container.context);

    for (i, client) in container.clients.iter_mut().enumerate() {
        let id = client.raw_id();
        client.titlebar = Some(Rectangle {
            x: x_offset + (width * i as u32) as i32,
            y: y_offset as i32,
            width: width,
            height: client.titlebar_height,
        });

        let titlebar_height = client.titlebar.unwrap().height;
        decorate(client, id==focus_id);

        client.configure(x_offset + border as i32,
                         y_offset+titlebar_height as i32 + border as i32,
                         attrs.width - border*2 ,
                         attrs.height -titlebar_height - border*2);
        if focus_id == id {
            // client.map();
            libx::raise_window(client.context, id);
        }
        else{
            // client.unmap();
            // libx::lower_window(client.context, client.id);
        }
    }
}
<|endoftext|>


<commit_before>use crate::errors::ParseError;
use crate::parsing_utils::{decode_str, decode_utf16, decode_windows1252, le_i32};

#[derive(Debug, Clone, PartialEq)]
pub struct CoreParser<'a> {
    data: &'a [u8],

    /// Current offset in regards to the whole view of the replay
    col: i32,
}

impl<'a> CoreParser<'a> {
    pub fn new(data: &'a [u8]) -> Self {
        CoreParser { data, col: 0 }
    }

    pub fn bytes_read(&self) -> i32 {
        self.col
    }

    /// Used for skipping some amount of data
    pub fn advance(&mut self, ind: usize) {
        self.col += ind as i32;
        self.data = &self.data[ind..];
    }

    /// Returns a slice of the replay after ensuring there is enough space for the requested slice
    pub fn view_data(&self, size: usize) -> Result<&'a [u8], ParseError> {
        if size > self.data.len() {
            Err(ParseError::InsufficientData(
                size as i32,
                self.data.len() as i32,
            ))
        } else {
            Ok(&self.data[..size])
        }
    }

    /// Take the next `size` of bytes and interpret them in an infallible fashion
    #[inline]
    pub fn take<F, T>(&mut self, size: usize, mut f: F) -> Result<T, ParseError>
    where
        F: FnMut(&'a [u8]) -> T,
    {
        let res = f(self.view_data(size)?);
        self.advance(size);
        Ok(res)
    }

    pub fn skip(&mut self, size: usize) -> Result<(), ParseError> {
        self.take(size, |_| ())
    }

    pub fn take_i32(&mut self, section: &'static str) -> Result<i32, ParseError> {
        self.take(4, le_i32)
            .map_err(|e| ParseError::ParseError(section, self.bytes_read(), Box::new(e)))
    }

    pub fn take_u32(&mut self, section: &'static str) -> Result<u32, ParseError> {
        self.take(4, le_i32)
            .map(|x| x as u32)
            .map_err(|e| ParseError::ParseError(section, self.bytes_read(), Box::new(e)))
    }

    /// Repeatedly parse the same elements from replay until `size` elements parsed
    pub fn repeat<F, T>(size: usize, mut f: F) -> Result<Vec<T>, ParseError>
    where
        F: FnMut() -> Result<T, ParseError>,
    {
        if size > 25_000 {
            return Err(ParseError::ListTooLarge(size));
        }

        let mut res = Vec::with_capacity(size);
        for _ in 0..size {
            res.push(f()?);
        }
        Ok(res)
    }

    pub fn list_of<F, T>(&mut self, mut f: F) -> Result<Vec<T>, ParseError>
    where
        F: FnMut(&mut Self) -> Result<T, ParseError>,
    {
        let size = self.take(4, le_i32)?;
        CoreParser::repeat(size as usize, || f(self))
    }

    pub fn text_list(&mut self) -> Result<Vec<String>, ParseError> {
        self.list_of(CoreParser::parse_text)
    }

    /// Parses UTF-8 string from replay
    pub fn parse_str(&mut self) -> Result<&'a str, ParseError> {
        let mut size = self.take(4, le_i32)? as usize;

        // Replay 6688 has a property name that is listed as having a length of 0x5000000, but it's
        // really the `\0\0\0None` property. I'm guess at some point in Rocket League, this was a
        // bug that was fixed. What's interesting is that I couldn't find this constant in
        // `RocketLeagueReplayParser`, only rattletrap.
        if size == 0x5_000_000 {
            size = 8;
        }
        self.view_data(size).and_then(decode_str)
    }

    /// Parses either UTF-16 or Windows-1252 encoded strings
    pub fn parse_text(&mut self) -> Result<String, ParseError> {
        // The number of bytes that the string is composed of. If negative, the string is UTF-16,
        // else the string is windows 1252 encoded.
        let characters = self.take(4, le_i32)?;

        // size.abs() will panic at min_value, so we eschew it for manual checking
        if characters == 0 {
            Err(ParseError::ZeroSize)
        } else if characters > 10_000 || characters < -10_000 {
            Err(ParseError::TextTooLarge(characters))
        } else if characters < 0 {
            // We're dealing with UTF-16 and each character is two bytes, we
            // multiply the size by 2. The last two bytes included in the count are
            // null terminators
            let size = characters * -2;
            self.view_data(size as usize).and_then(decode_utf16)
        } else {
            self.view_data(characters as usize)
                .and_then(decode_windows1252)
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::errors::ParseError;

    #[test]
    fn parse_text_encoding() {
        // dd skip=16 count=28 if=rumble.replay of=text.replay bs=1
        let data = include_bytes!("../assets/replays/partial/text.replay");
        let mut parser = CoreParser::new(&data[..]);
        assert_eq!(parser.parse_str().unwrap(), "TAGame.Replay_Soccar_TA");
    }

    #[test]
    fn parse_text_encoding_bad() {
        // dd skip=16 count=28 if=rumble.replay of=text.replay bs=1
        let data = include_bytes!("../assets/replays/partial/text.replay");
        let mut parser = CoreParser::new(&data[..data.len() - 1]);
        let res = parser.parse_str();
        assert!(res.is_err());
        let error = res.unwrap_err();
        assert_eq!(error, ParseError::InsufficientData(24, 23));
    }

    #[test]
    fn parse_text_zero_size() {
        let mut parser = CoreParser::new(&[0, 0, 0, 0, 0]);
        let res = parser.parse_str();
        assert!(res.is_err());
        let error = res.unwrap_err();
        assert_eq!(error, ParseError::ZeroSize);
    }

    #[test]
    fn parse_text_zero_size2() {
        let mut parser = CoreParser::new(&[0, 0, 0, 0, 0]);
        let res = parser.parse_text();
        assert!(res.is_err());
        let error = res.unwrap_err();
        assert_eq!(error, ParseError::ZeroSize);
    }

    #[test]
    fn parse_text_too_large() {
        let mut parser = CoreParser::new(&[0xcc, 0xcc, 0xcc, 0xcc, 0xcc]);
        let res = parser.parse_text();
        assert!(res.is_err());
        let error = res.unwrap_err();
        assert_eq!(error, ParseError::TextTooLarge(-858993460));
    }

    #[test]
    fn parse_text_encoding_bad_2() {
        // Test for when there is not enough data to decode text length
        // dd skip=16 count=28 if=rumble.replay of=text.replay bs=1
        let data = include_bytes!("../assets/replays/partial/text.replay");
        let mut parser = CoreParser::new(&data[..2]);
        let res = parser.parse_str();
        assert!(res.is_err());
        let error = res.unwrap_err();
        assert_eq!(error, ParseError::InsufficientData(4, 2));
    }

    #[test]
    fn parse_utf16_string() {
        // dd skip=((0x120)) count=28 if=utf-16.replay of=utf-16-text.replay bs=1
        let data = include_bytes!("../assets/replays/partial/utf-16-text.replay");
        let mut parser = CoreParser::new(&data[..]);
        let res = parser.parse_text().unwrap();
        assert_eq!(res, "\u{2623}D[e]!v1zz\u{2623}");
    }

    #[test]
    fn test_windows1252_string() {
        let data = include_bytes!("../assets/replays/partial/windows_1252.replay");
        let mut parser = CoreParser::new(&data[0x1ad..0x1c4]);
        let res = parser.parse_text().unwrap();
        assert_eq!(res, "caudillman6000\u{b3}(2)");
    }

    /// Define behavior on invalid UTF-16 sequences.
    #[test]
    fn parse_invalid_utf16_string() {
        let data = [0xfd, 0xff, 0xff, 0xff, 0xd8, 0xd8, 0x00, 0x00, 0x00, 0x00];
        let mut parser = CoreParser::new(&data[..]);
        let res = parser.parse_text().unwrap();
        assert_eq!(res, "�\u{0}");
    }
}
<commit_msg>Fix tests by taking data<commit_after>use crate::errors::ParseError;
use crate::parsing_utils::{decode_str, decode_utf16, decode_windows1252, le_i32};

#[derive(Debug, Clone, PartialEq)]
pub struct CoreParser<'a> {
    data: &'a [u8],

    /// Current offset in regards to the whole view of the replay
    col: i32,
}

impl<'a> CoreParser<'a> {
    pub fn new(data: &'a [u8]) -> Self {
        CoreParser { data, col: 0 }
    }

    pub fn bytes_read(&self) -> i32 {
        self.col
    }

    /// Used for skipping some amount of data
    pub fn advance(&mut self, ind: usize) {
        self.col += ind as i32;
        self.data = &self.data[ind..];
    }

    /// Returns a slice of the replay after ensuring there is enough space for the requested slice
    pub fn view_data(&self, size: usize) -> Result<&'a [u8], ParseError> {
        if size > self.data.len() {
            Err(ParseError::InsufficientData(
                size as i32,
                self.data.len() as i32,
            ))
        } else {
            Ok(&self.data[..size])
        }
    }

    pub fn take_data(&mut self, size: usize) -> Result<&'a [u8], ParseError> {
        let res = self.view_data(size)?;
        self.advance(size);
        Ok(res)
    }

    /// Take the next `size` of bytes and interpret them in an infallible fashion
    #[inline]
    pub fn take<F, T>(&mut self, size: usize, mut f: F) -> Result<T, ParseError>
    where
        F: FnMut(&'a [u8]) -> T,
    {
        let res = f(self.view_data(size)?);
        self.advance(size);
        Ok(res)
    }

    pub fn skip(&mut self, size: usize) -> Result<(), ParseError> {
        self.take(size, |_| ())
    }

    pub fn take_i32(&mut self, section: &'static str) -> Result<i32, ParseError> {
        self.take(4, le_i32)
            .map_err(|e| ParseError::ParseError(section, self.bytes_read(), Box::new(e)))
    }

    pub fn take_u32(&mut self, section: &'static str) -> Result<u32, ParseError> {
        self.take(4, le_i32)
            .map(|x| x as u32)
            .map_err(|e| ParseError::ParseError(section, self.bytes_read(), Box::new(e)))
    }

    /// Repeatedly parse the same elements from replay until `size` elements parsed
    pub fn repeat<F, T>(size: usize, mut f: F) -> Result<Vec<T>, ParseError>
    where
        F: FnMut() -> Result<T, ParseError>,
    {
        if size > 25_000 {
            return Err(ParseError::ListTooLarge(size));
        }

        let mut res = Vec::with_capacity(size);
        for _ in 0..size {
            res.push(f()?);
        }
        Ok(res)
    }

    pub fn list_of<F, T>(&mut self, mut f: F) -> Result<Vec<T>, ParseError>
    where
        F: FnMut(&mut Self) -> Result<T, ParseError>,
    {
        let size = self.take(4, le_i32)?;
        CoreParser::repeat(size as usize, || f(self))
    }

    pub fn text_list(&mut self) -> Result<Vec<String>, ParseError> {
        self.list_of(CoreParser::parse_text)
    }

    /// Parses UTF-8 string from replay
    pub fn parse_str(&mut self) -> Result<&'a str, ParseError> {
        let mut size = self.take(4, le_i32)? as usize;

        // Replay 6688 has a property name that is listed as having a length of 0x5000000, but it's
        // really the `\0\0\0None` property. I'm guess at some point in Rocket League, this was a
        // bug that was fixed. What's interesting is that I couldn't find this constant in
        // `RocketLeagueReplayParser`, only rattletrap.
        if size == 0x5_000_000 {
            size = 8;
        }
        self.take_data(size).and_then(decode_str)
    }

    /// Parses either UTF-16 or Windows-1252 encoded strings
    pub fn parse_text(&mut self) -> Result<String, ParseError> {
        // The number of bytes that the string is composed of. If negative, the string is UTF-16,
        // else the string is windows 1252 encoded.
        let characters = self.take(4, le_i32)?;

        // size.abs() will panic at min_value, so we eschew it for manual checking
        if characters == 0 {
            Err(ParseError::ZeroSize)
        } else if characters > 10_000 || characters < -10_000 {
            Err(ParseError::TextTooLarge(characters))
        } else if characters < 0 {
            // We're dealing with UTF-16 and each character is two bytes, we
            // multiply the size by 2. The last two bytes included in the count are
            // null terminators
            let size = characters * -2;
            self.take_data(size as usize).and_then(decode_utf16)
        } else {
            self.take_data(characters as usize)
                .and_then(decode_windows1252)
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::errors::ParseError;

    #[test]
    fn parse_text_encoding() {
        // dd skip=16 count=28 if=rumble.replay of=text.replay bs=1
        let data = include_bytes!("../assets/replays/partial/text.replay");
        let mut parser = CoreParser::new(&data[..]);
        assert_eq!(parser.parse_str().unwrap(), "TAGame.Replay_Soccar_TA");
    }

    #[test]
    fn parse_text_encoding_bad() {
        // dd skip=16 count=28 if=rumble.replay of=text.replay bs=1
        let data = include_bytes!("../assets/replays/partial/text.replay");
        let mut parser = CoreParser::new(&data[..data.len() - 1]);
        let res = parser.parse_str();
        assert!(res.is_err());
        let error = res.unwrap_err();
        assert_eq!(error, ParseError::InsufficientData(24, 23));
    }

    #[test]
    fn parse_text_zero_size() {
        let mut parser = CoreParser::new(&[0, 0, 0, 0, 0]);
        let res = parser.parse_str();
        assert!(res.is_err());
        let error = res.unwrap_err();
        assert_eq!(error, ParseError::ZeroSize);
    }

    #[test]
    fn parse_text_zero_size2() {
        let mut parser = CoreParser::new(&[0, 0, 0, 0, 0]);
        let res = parser.parse_text();
        assert!(res.is_err());
        let error = res.unwrap_err();
        assert_eq!(error, ParseError::ZeroSize);
    }

    #[test]
    fn parse_text_too_large() {
        let mut parser = CoreParser::new(&[0xcc, 0xcc, 0xcc, 0xcc, 0xcc]);
        let res = parser.parse_text();
        assert!(res.is_err());
        let error = res.unwrap_err();
        assert_eq!(error, ParseError::TextTooLarge(-858993460));
    }

    #[test]
    fn parse_text_encoding_bad_2() {
        // Test for when there is not enough data to decode text length
        // dd skip=16 count=28 if=rumble.replay of=text.replay bs=1
        let data = include_bytes!("../assets/replays/partial/text.replay");
        let mut parser = CoreParser::new(&data[..2]);
        let res = parser.parse_str();
        assert!(res.is_err());
        let error = res.unwrap_err();
        assert_eq!(error, ParseError::InsufficientData(4, 2));
    }

    #[test]
    fn parse_utf16_string() {
        // dd skip=((0x120)) count=28 if=utf-16.replay of=utf-16-text.replay bs=1
        let data = include_bytes!("../assets/replays/partial/utf-16-text.replay");
        let mut parser = CoreParser::new(&data[..]);
        let res = parser.parse_text().unwrap();
        assert_eq!(res, "\u{2623}D[e]!v1zz\u{2623}");
    }

    #[test]
    fn test_windows1252_string() {
        let data = include_bytes!("../assets/replays/partial/windows_1252.replay");
        let mut parser = CoreParser::new(&data[0x1ad..0x1c4]);
        let res = parser.parse_text().unwrap();
        assert_eq!(res, "caudillman6000\u{b3}(2)");
    }

    /// Define behavior on invalid UTF-16 sequences.
    #[test]
    fn parse_invalid_utf16_string() {
        let data = [0xfd, 0xff, 0xff, 0xff, 0xd8, 0xd8, 0x00, 0x00, 0x00, 0x00];
        let mut parser = CoreParser::new(&data[..]);
        let res = parser.parse_text().unwrap();
        assert_eq!(res, "�\u{0}");
    }
}
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2017-2018 Ryan Huang
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

use std::sync::mpsc::{self, Receiver, Sender};
use std::thread::{self, JoinHandle};

use model::{Board, Move, Outcome};

const INFINITY: i16 = 0x7FFF;
const NEG_INFINITY: i16 = -0x7FFF;
const LOSE: i16 = -0x4000;
const DRAW: i16 = 0;

pub struct AIHandle {
    pub move_receiver: Receiver<Option<Move>>,
    pub stop_sender: Sender<()>,
    handle: JoinHandle<()>,
}

pub fn ai_move(board: Board, depth: u8, prev_handle: Option<AIHandle>) -> AIHandle {
    assert!(depth != 0);

    let (move_sender, move_receiver) = mpsc::channel();
    let (stop_sender, stop_receiver) = mpsc::channel();

    let handle = thread::spawn(move || {
        if let Some(prev_handle) = prev_handle {
            // If send returns an error, the other thread has already terminated
            if prev_handle.stop_sender.send(()).is_ok() {
                prev_handle.handle.join().expect(
                    "Previous AI thread panicked while new AI thread was waiting for it to finish",
                );
            }
        }

        let mut max_score = NEG_INFINITY;
        let mut best_move = None;
        for mv in board.generate_moves() {
            if stop_receiver.try_recv().is_ok() {
                return;
            }

            let mut new_board = board;
            new_board.apply_move(&mv);

            let score = -alphabeta_negamax(&new_board, NEG_INFINITY, INFINITY, depth - 1);
            if score > max_score {
                max_score = score;
                best_move = Some(mv);
            }
        }
        move_sender.send(best_move).expect("AI failed to send Move");
    });

    AIHandle {
        move_receiver,
        stop_sender,
        handle,
    }
}

fn alphabeta_negamax(board: &Board, mut alpha: i16, beta: i16, depth: u8) -> i16 {
    match board.outcome() {
        Outcome::Draw => return DRAW,
        Outcome::Win(color) => {
            assert!(color != board.turn());
            // TODO: weight by depth to encourage shorter wins
            return LOSE;
        }
        _ => {}
    }

    if depth == 0 {
        evaluate(board)
    } else {
        let moves = board.generate_moves();
        for mv in moves {
            let mut new_board = *board;
            new_board.apply_move(&mv);

            let score = -alphabeta_negamax(&new_board, -beta, -alpha, depth - 1);
            if score >= beta {
                return beta;
            } else if score > alpha {
                alpha = score;
            }
        }
        alpha
    }
}

fn evaluate(board: &Board) -> i16 {
    use model::Color::*;

    let wp = 100 * i16::from(board.pieces(White));
    let bp = 100 * i16::from(board.pieces(Black));
    let wh = 50 * i16::from(board.hexes(White));
    let bh = 50 * i16::from(board.hexes(Black));

    match board.turn() {
        White => (wp + wh) - (bp + bh),
        Black => (bp + bh) - (wp + wh),
    }
}

pub fn perft(board: &Board, depth: u8) -> u32 {
    if depth == 0 {
        1
    } else {
        let mut sum = 0;
        for mv in board.generate_moves() {
            let mut new_board = *board;
            new_board.apply_move(&mv);
            sum += perft(&new_board, depth - 1);
        }
        sum
    }
}
<commit_msg>Add 2-ply iterative deepening, use max_score as beta<commit_after>/*
 * Copyright (C) 2017-2018 Ryan Huang
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

use std::sync::mpsc::{self, Receiver, Sender};
use std::thread::{self, JoinHandle};

use model::{Board, Move, Outcome};

const INFINITY: i16 = 0x7FFF;
const NEG_INFINITY: i16 = -0x7FFF;
const LOSE: i16 = -0x4000;
const DRAW: i16 = 0;

pub struct AIHandle {
    pub move_receiver: Receiver<Option<Move>>,
    pub stop_sender: Sender<()>,
    handle: JoinHandle<()>,
}

pub fn ai_move(board: Board, depth: u8, prev_handle: Option<AIHandle>) -> AIHandle {
    assert!(depth != 0);

    let (move_sender, move_receiver) = mpsc::channel();
    let (stop_sender, stop_receiver) = mpsc::channel();

    let handle = thread::spawn(move || {
        if let Some(prev_handle) = prev_handle {
            // If send returns an error, the other thread has already terminated
            if prev_handle.stop_sender.send(()).is_ok() {
                prev_handle.handle.join().expect(
                    "Previous AI thread panicked while new AI thread was waiting for it to finish",
                );
            }
        }

        // 2-ply iterative deepening
        let mut moves: Vec<(Move, i16)> = board
            .generate_moves()
            .into_iter()
            .map(|mv| {
                let mut new_board = board;
                new_board.apply_move(&mv);

                let score = -alphabeta_negamax(&new_board, NEG_INFINITY, INFINITY, 1);
                (mv, score)
            })
            .collect();

        moves.sort_by(|&(_, a), &(_, b)| b.cmp(&a));

        let mut max_score = NEG_INFINITY;
        let mut best_move = None;
        for (mv, _) in moves {
            if stop_receiver.try_recv().is_ok() {
                return;
            }

            let mut new_board = board;
            new_board.apply_move(&mv);

            let score = -alphabeta_negamax(&new_board, NEG_INFINITY, -max_score, depth - 1);
            if score > max_score {
                max_score = score;
                best_move = Some(mv);
            }
        }
        move_sender.send(best_move).expect("AI failed to send Move");
    });

    AIHandle {
        move_receiver,
        stop_sender,
        handle,
    }
}

fn alphabeta_negamax(board: &Board, mut alpha: i16, beta: i16, depth: u8) -> i16 {
    match board.outcome() {
        Outcome::Draw => return DRAW,
        Outcome::Win(color) => {
            assert!(color != board.turn());
            // TODO: weight by depth to encourage shorter wins
            return LOSE;
        }
        _ => {}
    }

    if depth == 0 {
        evaluate(board)
    } else {
        let moves = board.generate_moves();
        for mv in moves {
            let mut new_board = *board;
            new_board.apply_move(&mv);

            let score = -alphabeta_negamax(&new_board, -beta, -alpha, depth - 1);
            if score >= beta {
                return beta;
            } else if score > alpha {
                alpha = score;
            }
        }
        alpha
    }
}

fn evaluate(board: &Board) -> i16 {
    use model::Color::*;

    let wp = 100 * i16::from(board.pieces(White));
    let bp = 100 * i16::from(board.pieces(Black));
    let wh = 50 * i16::from(board.hexes(White));
    let bh = 50 * i16::from(board.hexes(Black));

    match board.turn() {
        White => (wp + wh) - (bp + bh),
        Black => (bp + bh) - (wp + wh),
    }
}

pub fn perft(board: &Board, depth: u8) -> u32 {
    if depth == 0 {
        1
    } else {
        let mut sum = 0;
        for mv in board.generate_moves() {
            let mut new_board = *board;
            new_board.apply_move(&mv);
            sum += perft(&new_board, depth - 1);
        }
        sum
    }
}
<|endoftext|>


<commit_before>use super::common::Log;

use base64;
use byteorder::{BigEndian, WriteBytesExt};

use futures;
use futures::prelude::*;
use hyper;
use serde_json;
use std::io::Write;
use std::time::Duration;
use tokio_core::reactor::Timeout;

#[derive(Debug, Deserialize)]
pub struct SignedCertificateTimestamp {
    sct_version: u8,
    id: String,
    timestamp: u64,
    extensions: String,
    signature: String,
}

impl SignedCertificateTimestamp {
    pub fn to_raw_bytes(&self) -> Vec<u8> {
        let mut b = Vec::new();
        b.write_u8(self.sct_version).unwrap();

        let log_id = base64::decode(&self.id).unwrap();
        b.write_all(&log_id).unwrap();

        b.write_u64::<BigEndian>(self.timestamp).unwrap();

        let extensions = base64::decode(&self.extensions).unwrap();
        assert!(extensions.len() <= 65_535);
        b.write_u16::<BigEndian>(extensions.len() as u16).unwrap();
        b.write_all(&extensions).unwrap();

        let signature = base64::decode(&self.signature).unwrap();
        b.write_all(&signature).unwrap();

        b
    }
}

fn submit_to_log<C: hyper::client::Connect>(
    http_client: &hyper::Client<C>,
    log: &Log,
    payload: Vec<u8>,
) -> impl Future<Item = SignedCertificateTimestamp, Error = ()> {
    let mut url = "https://".to_string() + &log.url;
    if !url.ends_with('/') {
        url += "/";
    }
    url += "ct/v1/add-chain";
    let mut request = hyper::Request::new(hyper::Method::Post, url.parse().unwrap());
    request.headers_mut().set(
        hyper::header::ContentType::json(),
    );
    request.set_body(payload);
    let r = http_client.request(request);
    async_block! {
        let response = match await!(r) {
            Ok(r) => r,
            // TODO: maybe not all of these should be silently ignored.
            Err(_) => return Err(()),
        };

        // 400, 403, and probably some others generally indicate a log doesn't accept certs from
        // this root, or that the log isn't accepting new submissions. Server errors mean there's
        // nothing we can do.
        if response.status().is_client_error() || response.status().is_server_error() {
            return Err(());
        }

        // Limt the response to 10MB (well above what would ever be needed) to be resilient to DoS
        // in the face of a dumb or malicious log.
        let body = await!(response.body().take(10 * 1024 * 1024).concat2())
            .unwrap();
        Ok(serde_json::from_slice(&body).unwrap())
    }
}

#[derive(Serialize, Deserialize)]
pub struct AddChainRequest {
    pub chain: Vec<String>,
}

pub fn submit_cert_to_logs<C: hyper::client::Connect>(
    http_client: &hyper::Client<C>,
    logs: &[Log],
    cert: &[Vec<u8>],
) -> impl Future<Item = Vec<(usize, SignedCertificateTimestamp)>, Error = ()> {
    let payload = serde_json::to_vec(&AddChainRequest {
        chain: cert.iter().map(|r| base64::encode(r)).collect(),
    }).unwrap();

    let futures = logs.iter()
        .enumerate()
        .map(move |(idx, log)| {
            let timeout = Timeout::new(Duration::from_secs(5), http_client.handle()).unwrap();

            let payload = payload.clone();
            let s = submit_to_log(http_client, log, payload).select2(timeout);
            async_block! {
                match await!(s) {
                    Ok(futures::future::Either::A((sct, _))) => Ok(Some((idx, sct))),
                    _ => Ok(None),
                }
            }
        })
        .collect::<Vec<_>>();

    futures::future::join_all(futures).map(|scts| scts.into_iter().filter_map(|s| s).collect())
}
<commit_msg>Put the timeout constructor in teh async block, not sure it matter<commit_after>use super::common::Log;

use base64;
use byteorder::{BigEndian, WriteBytesExt};

use futures;
use futures::prelude::*;
use hyper;
use serde_json;
use std::io::Write;
use std::time::Duration;
use tokio_core::reactor::Timeout;

#[derive(Debug, Deserialize)]
pub struct SignedCertificateTimestamp {
    sct_version: u8,
    id: String,
    timestamp: u64,
    extensions: String,
    signature: String,
}

impl SignedCertificateTimestamp {
    pub fn to_raw_bytes(&self) -> Vec<u8> {
        let mut b = Vec::new();
        b.write_u8(self.sct_version).unwrap();

        let log_id = base64::decode(&self.id).unwrap();
        b.write_all(&log_id).unwrap();

        b.write_u64::<BigEndian>(self.timestamp).unwrap();

        let extensions = base64::decode(&self.extensions).unwrap();
        assert!(extensions.len() <= 65_535);
        b.write_u16::<BigEndian>(extensions.len() as u16).unwrap();
        b.write_all(&extensions).unwrap();

        let signature = base64::decode(&self.signature).unwrap();
        b.write_all(&signature).unwrap();

        b
    }
}

fn submit_to_log<C: hyper::client::Connect>(
    http_client: &hyper::Client<C>,
    log: &Log,
    payload: Vec<u8>,
) -> impl Future<Item = SignedCertificateTimestamp, Error = ()> {
    let mut url = "https://".to_string() + &log.url;
    if !url.ends_with('/') {
        url += "/";
    }
    url += "ct/v1/add-chain";
    let mut request = hyper::Request::new(hyper::Method::Post, url.parse().unwrap());
    request.headers_mut().set(
        hyper::header::ContentType::json(),
    );
    request.set_body(payload);
    let r = http_client.request(request);
    async_block! {
        let response = match await!(r) {
            Ok(r) => r,
            // TODO: maybe not all of these should be silently ignored.
            Err(_) => return Err(()),
        };

        // 400, 403, and probably some others generally indicate a log doesn't accept certs from
        // this root, or that the log isn't accepting new submissions. Server errors mean there's
        // nothing we can do.
        if response.status().is_client_error() || response.status().is_server_error() {
            return Err(());
        }

        // Limt the response to 10MB (well above what would ever be needed) to be resilient to DoS
        // in the face of a dumb or malicious log.
        let body = await!(response.body().take(10 * 1024 * 1024).concat2())
            .unwrap();
        Ok(serde_json::from_slice(&body).unwrap())
    }
}

#[derive(Serialize, Deserialize)]
pub struct AddChainRequest {
    pub chain: Vec<String>,
}

pub fn submit_cert_to_logs<C: hyper::client::Connect>(
    http_client: &hyper::Client<C>,
    logs: &[Log],
    cert: &[Vec<u8>],
) -> impl Future<Item = Vec<(usize, SignedCertificateTimestamp)>, Error = ()> {
    let payload = serde_json::to_vec(&AddChainRequest {
        chain: cert.iter().map(|r| base64::encode(r)).collect(),
    }).unwrap();

    let futures = logs.iter()
        .enumerate()
        .map(move |(idx, log)| {
            let payload = payload.clone();
            let handle = http_client.handle().clone();
            let s = submit_to_log(http_client, log, payload);
            async_block! {
                let timeout = Timeout::new(Duration::from_secs(5), &handle).unwrap();
                match await!(s.select2(timeout)) {
                    Ok(futures::future::Either::A((sct, _))) => Ok(Some((idx, sct))),
                    _ => Ok(None),
                }
            }
        })
        .collect::<Vec<_>>();

    futures::future::join_all(futures).map(|scts| scts.into_iter().filter_map(|s| s).collect())
}
<|endoftext|>


<commit_before>use pg;
use pg::{PostgresConnection, PostgresStatement, PostgresResult};
use pg::pool::PostgresConnectionPool;
use pg::types::ToSql;

use user;
use package;

fn location() -> String {
    "postgres://postgres:@localhost/cargo.io".to_string()
}

pub fn pool() -> PostgresConnectionPool {
    PostgresConnectionPool::new(location().as_slice(), pg::NoSsl, 5).unwrap()
}

pub fn setup(conn: &PostgresConnection) {
    user::setup(conn);
    package::setup(conn);
}

pub trait Connection {
    fn prepare<'a>(&'a self, query: &str) -> PostgresResult<PostgresStatement<'a>>;
    fn execute(&self, query: &str, params: &[&ToSql]) -> PostgresResult<uint>;
}

impl Connection for pg::PostgresConnection {
    fn prepare<'a>(&'a self, query: &str) -> PostgresResult<PostgresStatement<'a>> {
        self.prepare(query)
    }
    fn execute(&self, query: &str, params: &[&ToSql]) -> PostgresResult<uint> {
        self.execute(query, params)
    }
}

impl Connection for pg::pool::PooledPostgresConnection {
    fn prepare<'a>(&'a self, query: &str) -> PostgresResult<PostgresStatement<'a>> {
        self.prepare(query)
    }
    fn execute(&self, query: &str, params: &[&ToSql]) -> PostgresResult<uint> {
        self.execute(query, params)
    }
}

impl<'a> Connection for pg::PostgresTransaction<'a> {
    fn prepare<'a>(&'a self, query: &str) -> PostgresResult<PostgresStatement<'a>> {
        self.prepare(query)
    }
    fn execute(&self, query: &str, params: &[&ToSql]) -> PostgresResult<uint> {
        self.execute(query, params)
    }
}
<commit_msg>Pick up the db location from an env var<commit_after>use std::os;

use pg;
use pg::{PostgresConnection, PostgresStatement, PostgresResult};
use pg::pool::PostgresConnectionPool;
use pg::types::ToSql;

use user;
use package;

fn location() -> String {
    os::getenv("DATABASE_URL").unwrap()
}

pub fn pool() -> PostgresConnectionPool {
    PostgresConnectionPool::new(location().as_slice(), pg::NoSsl, 5).unwrap()
}

pub fn setup(conn: &PostgresConnection) {
    user::setup(conn);
    package::setup(conn);
}

pub trait Connection {
    fn prepare<'a>(&'a self, query: &str) -> PostgresResult<PostgresStatement<'a>>;
    fn execute(&self, query: &str, params: &[&ToSql]) -> PostgresResult<uint>;
}

impl Connection for pg::PostgresConnection {
    fn prepare<'a>(&'a self, query: &str) -> PostgresResult<PostgresStatement<'a>> {
        self.prepare(query)
    }
    fn execute(&self, query: &str, params: &[&ToSql]) -> PostgresResult<uint> {
        self.execute(query, params)
    }
}

impl Connection for pg::pool::PooledPostgresConnection {
    fn prepare<'a>(&'a self, query: &str) -> PostgresResult<PostgresStatement<'a>> {
        self.prepare(query)
    }
    fn execute(&self, query: &str, params: &[&ToSql]) -> PostgresResult<uint> {
        self.execute(query, params)
    }
}

impl<'a> Connection for pg::PostgresTransaction<'a> {
    fn prepare<'a>(&'a self, query: &str) -> PostgresResult<PostgresStatement<'a>> {
        self.prepare(query)
    }
    fn execute(&self, query: &str, params: &[&ToSql]) -> PostgresResult<uint> {
        self.execute(query, params)
    }
}
<|endoftext|>


<commit_before>// Copyright:: Copyright (c) 2015-2016 The Habitat Maintainers
//
// The terms of the Evaluation Agreement (Habitat) between Chef Software Inc.
// and the party accessing this file ("Licensee") apply to Licensee's use of
// the Software until such time that the Software is made available under an
// open source license such as the Apache 2.0 License.

use std::path::PathBuf;

pub const ROOT_PATH: &'static str = "/opt/bldr";
/// The default download root path for package artifacts, used on package installation
pub const CACHE_ARTIFACT_PATH: &'static str = "/opt/bldr/cache/artifacts";
/// The default path where gpg keys are stored
pub const CACHE_GPG_PATH: &'static str = "/opt/bldr/cache/gpg";
/// The default path where cryptographic keys are stored
pub const CACHE_KEY_PATH: &'static str = "/opt/bldr/cache/keys";
/// The default path where source artifacts are downloaded, extracted, & compiled
pub const CACHE_SRC_PATH: &'static str = "/opt/bldr/cache/src";
/// The root path containing all locally installed packages
pub const PKG_PATH: &'static str = "/opt/bldr/pkgs";
/// The root path containing all runtime service directories and files
pub const SVC_PATH: &'static str = "/opt/bldr/svc";

pub fn service_path(service_name: &str) -> PathBuf {
    PathBuf::from(SVC_PATH).join(service_name)
}
<commit_msg>Use `pkg_svc_config_path` for the path to the service config.<commit_after>// Copyright:: Copyright (c) 2015-2016 The Habitat Maintainers
//
// The terms of the Evaluation Agreement (Habitat) between Chef Software Inc.
// and the party accessing this file ("Licensee") apply to Licensee's use of
// the Software until such time that the Software is made available under an
// open source license such as the Apache 2.0 License.

use std::path::PathBuf;

pub const ROOT_PATH: &'static str = "/opt/bldr";
/// The default download root path for package artifacts, used on package installation
pub const CACHE_ARTIFACT_PATH: &'static str = "/opt/bldr/cache/artifacts";
/// The default path where gpg keys are stored
pub const CACHE_GPG_PATH: &'static str = "/opt/bldr/cache/gpg";
/// The default path where cryptographic keys are stored
pub const CACHE_KEY_PATH: &'static str = "/opt/bldr/cache/keys";
/// The default path where source artifacts are downloaded, extracted, & compiled
pub const CACHE_SRC_PATH: &'static str = "/opt/bldr/cache/src";
/// The root path containing all locally installed packages
pub const PKG_PATH: &'static str = "/opt/bldr/pkgs";
/// The root path containing all runtime service directories and files
pub const SVC_PATH: &'static str = "/opt/bldr/svc";

pub fn svc_path(service_name: &str) -> PathBuf {
    PathBuf::from(SVC_PATH).join(service_name)
}

pub fn svc_config_path(service_name: &str) -> PathBuf {
    svc_path(service_name).join("config")
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::fs::File;
use std::io::Read;

/// IOLoad represents current system block devices IO statistics
#[derive(Debug)]
pub struct IOLoad {
    /// number of read I/Os processed
    /// units: requests
    pub read_io: f64,
    /// number of read I/Os merged with in-queue I/O
    /// units: requests
    pub read_merges: f64,
    /// number of sectors read
    /// units: sectors
    pub read_sectors: f64,
    /// total wait time for read requests
    /// units: milliseconds
    pub read_ticks: f64,
    /// number of write I/Os processed
    /// units: requests
    pub write_io: f64,
    /// number of write I/Os merged with in-queue I/O
    /// units: requests
    pub write_merges: f64,
    /// number of sectors written
    /// units: sectors
    pub write_sectors: f64,
    /// total wait time for write requests
    /// units: milliseconds
    pub write_ticks: f64,
    /// number of I/Os currently in flight
    /// units: requests
    pub in_flight: f64,
    /// total time this block device has been active
    /// units: milliseconds
    pub io_ticks: f64,
    /// total wait time for all requests
    /// units: milliseconds
    pub time_in_queue: f64,
}

impl IOLoad {
    /// Returns the current IO statistics
    ///
    /// # Notes
    ///
    /// Current don't support non-unix operating system
    #[cfg(not(unix))]
    pub fn snapshot() -> HashMap<String, NICLoad> {
        HashMap::new()
    }

    /// Returns the current IO statistics
    #[cfg(unix)]
    pub fn snapshot() -> HashMap<String, IOLoad> {
        let mut result = HashMap::new();
        // https://www.kernel.org/doc/Documentation/block/stat.txt
        if let Ok(dir) = std::fs::read_dir("/sys/block/") {
            for entry in dir {
                if let Ok(entry) = entry {
                    let stat = entry.path().join("stat");
                    let mut s = String::new();
                    if let Err(_) = File::open(stat).and_then(|mut f| f.read_to_string(&mut s)) {
                        continue;
                    };
                    let parts = s
                        .split_whitespace()
                        .map(|w| w.parse().unwrap_or_default())
                        .collect::<Vec<f64>>();
                    if parts.len() != 11 {
                        continue;
                    }
                    let load = IOLoad {
                        read_io: parts[0],
                        read_merges: parts[1],
                        read_sectors: parts[2],
                        read_ticks: parts[3],
                        write_io: parts[4],
                        write_merges: parts[5],
                        write_sectors: parts[6],
                        write_ticks: parts[7],
                        in_flight: parts[8],
                        io_ticks: parts[9],
                        time_in_queue: parts[10],
                    };
                    result.insert(format!("{:?}", entry.file_name()), load);
                }
            }
        }
        result
    }
}
<commit_msg>Fix io issue with modern Linux versions<commit_after>use std::collections::HashMap;
use std::fs::File;
use std::io::Read;

/// IOLoad represents current system block devices IO statistics
#[derive(Debug)]
pub struct IOLoad {
    /// number of read I/Os processed
    /// units: requests
    pub read_io: f64,
    /// number of read I/Os merged with in-queue I/O
    /// units: requests
    pub read_merges: f64,
    /// number of sectors read
    /// units: sectors
    pub read_sectors: f64,
    /// total wait time for read requests
    /// units: milliseconds
    pub read_ticks: f64,
    /// number of write I/Os processed
    /// units: requests
    pub write_io: f64,
    /// number of write I/Os merged with in-queue I/O
    /// units: requests
    pub write_merges: f64,
    /// number of sectors written
    /// units: sectors
    pub write_sectors: f64,
    /// total wait time for write requests
    /// units: milliseconds
    pub write_ticks: f64,
    /// number of I/Os currently in flight
    /// units: requests
    pub in_flight: f64,
    /// total time this block device has been active
    /// units: milliseconds
    pub io_ticks: f64,
    /// total wait time for all requests
    /// units: milliseconds
    pub time_in_queue: f64,
}

impl IOLoad {
    /// Returns the current IO statistics
    ///
    /// # Notes
    ///
    /// Current don't support non-unix operating system
    #[cfg(not(unix))]
    pub fn snapshot() -> HashMap<String, NICLoad> {
        HashMap::new()
    }

    /// Returns the current IO statistics
    #[cfg(unix)]
    pub fn snapshot() -> HashMap<String, IOLoad> {
        let mut result = HashMap::new();
        // https://www.kernel.org/doc/Documentation/block/stat.txt
        if let Ok(dir) = std::fs::read_dir("/sys/block/") {
            for entry in dir {
                if let Ok(entry) = entry {
                    let stat = entry.path().join("stat");
                    let mut s = String::new();
                    if let Err(_) = File::open(stat).and_then(|mut f| f.read_to_string(&mut s)) {
                        continue;
                    };
                    let parts = s
                        .split_whitespace()
                        .map(|w| w.parse().unwrap_or_default())
                        .collect::<Vec<f64>>();
                    if parts.len() < 11 {
                        continue;
                    }
                    let load = IOLoad {
                        read_io: parts[0],
                        read_merges: parts[1],
                        read_sectors: parts[2],
                        read_ticks: parts[3],
                        write_io: parts[4],
                        write_merges: parts[5],
                        write_sectors: parts[6],
                        write_ticks: parts[7],
                        in_flight: parts[8],
                        io_ticks: parts[9],
                        time_in_queue: parts[10],
                    };
                    result.insert(format!("{:?}", entry.file_name()), load);
                }
            }
        }
        result
    }
}
<|endoftext|>


<commit_before>#![feature(core)]
#![feature(std_misc)]
#![feature(fs)]
#![feature(io)]

//! #Introduction
//! **This crate focuses on geting system information.**
//!
//! Now it can get kernel and cpu information, memory and disk usage, average load value and so on.
//!
//! If you have some advice or want the crate supports more system information, contact with me.

use std::ffi;
use std::io::Read;
use std::fs::File;

/// System load average value.
#[repr(C)]
pub struct LoadAvg {
    pub one: f64,
    pub five: f64,
    pub fifteen: f64
}

/// System memory information.
#[repr(C)]
pub struct MemInfo {
    pub total: u64,
    pub free:  u64,
    pub avail: u64,

    pub buffers: u64,
    pub cached:  u64,

    pub swap_total: u64,
    pub swap_free:  u64
}

/// System momory information.
#[repr(C)]
pub struct DiskInfo {
    pub total: u64,
    pub free: u64
}

extern {
    fn get_os_type() -> *const i8;
    fn get_os_release() -> *const i8;

    fn get_cpu_num() -> u32;
    fn get_cpu_speed() -> u64;

    fn get_loadavg() -> LoadAvg;
    fn get_proc_total() -> u64;

    fn get_mem_info() -> MemInfo;
    fn get_disk_info() -> DiskInfo;
}

/// Get operation system type.
///
/// Such as "Linux", "Darwin", "Windows".
pub fn os_type() -> Result<String, String> {
    if cfg!(target_os = "linux") {
        let mut f = File::open("/proc/sys/kernel/ostype").unwrap();
        let mut s = String::new();
        let _ = f.read_to_string(&mut s).unwrap();
        s.pop();  // pop '\n'
        Ok(s)
    } else if cfg!(target_os = "macos") ||
        cfg!(target_os = "windows")
    {
        Ok(unsafe {
            let p = get_os_type();
            String::from_utf8_lossy(ffi::CStr::from_ptr(p).to_bytes()).into_owned()
        })
    } else {
        Err("Unsupported system".to_string())
    }
}

/// Get operation system release version.
///
/// Such as "3.19.0-gentoo"
pub fn os_release() -> Result<String, String> {
    if cfg!(target_os = "linux") {
        let mut f = File::open("/proc/sys/kernel/osrelease").unwrap();
        let mut s = String::new();
        let _ = f.read_to_string(&mut s).unwrap();
        s.pop();
        Ok(s)
    } else if cfg!(target_os = "macos") ||
        cfg!(target_os = "windows")
    {
        Ok(unsafe {
            let p = get_os_release();
            String::from_utf8_lossy(ffi::CStr::from_ptr(p).to_bytes()).into_owned()
        })
    } else {
        Err("Unsupported system".to_string())
    }
}

/// Get cpu num quantity.
///
/// Notice, it returns the logical cpu quantity.
pub fn cpu_num() -> Result<u32, String> {
    if cfg!(target_os = "linux")
        || cfg!(target_os = "macos") || cfg!(target_os = "windows")
    {
        Ok(unsafe { get_cpu_num() })
    } else {
        Err("Unsupported system".to_string())
    }
}

/// Get cpu speed.
///
/// Such as 2500, that is 2500 MHz.
pub fn cpu_speed() -> Result<u64, String> {
    if cfg!(target_os = "linux") {
        // /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_cur_freq
        let mut f = File::open("/proc/cpuinfo").unwrap();
        let mut s = String::new();
        let _ = f.read_to_string(&mut s).unwrap();
        let mut lines = s.as_slice().split('\n');
        Ok({
            for _ in 0..7 {
                lines.next();
            }
            let mut words = lines.next().unwrap().split(':');
            words.next();
            let s = words.next().unwrap().trim().trim_right_matches('\n');
            s.parse::<f64>().unwrap() as u64
        })
    } else if cfg!(target_os = "macos") || cfg!(target_os = "windows") {
        Ok(unsafe { get_cpu_speed() })
    } else {
        Err("Unsupported system".to_string())
    }
}

/// Get system load average value.
///
/// Notice, on windows, one/five/fifteen of the LoadAvg returned are the current load.
pub fn loadavg() -> Result<LoadAvg, String> {
    if cfg!(target_os = "linux") {
        let mut f = File::open("/proc/loadavg").unwrap();
        let mut s = String::new();
        let _ = f.read_to_string(&mut s).unwrap();
        let mut words = s.as_slice().split(' ');
        let one = words.next().unwrap().parse::<f64>().unwrap();
        let five = words.next().unwrap().parse::<f64>().unwrap();
        let fifteen = words.next().unwrap().parse::<f64>().unwrap();
        Ok(LoadAvg { one: one, five: five, fifteen: fifteen} )
    } else if cfg!(target_os = "macos") || cfg!(target_os = "windows") {
        Ok(unsafe { get_loadavg() })
    } else {
        Err("Unsupported system".to_string())
    }
}

/// Get current processes quantity.
///
/// Notice, it temporarily does not support Windows.
pub fn proc_total() -> Result<u64, String> {
    if cfg!(target_os = "linux") {
        let mut f = File::open("/proc/loadavg").unwrap();
        let mut s = String::new();
        let _ = f.read_to_string(&mut s).unwrap();
        Ok({
            let mut words = s.as_slice().splitn(3, ' ');
            for _ in 0..3 {
                words.next();
            }
            let mut words = words.next().unwrap().split('/');
            words.next();
            let mut words = words.next().unwrap().split(' ');
            words.next().unwrap().parse::<u64>().unwrap()
        })
    } else if cfg!(target_os = "macos") || cfg!(target_os = "windows") {
        Ok(unsafe { get_proc_total() })
    } else {
        Err("Unsupported system".to_string())
    }
}

// Analyse number from line.
fn get_mem_num(line: &str) -> u64 {
    let mut line = line.splitn(1, ' ');
    line.next();
    line.next().unwrap().trim_left().split(' ').next().unwrap()
        .parse::<u64>().unwrap()
}

/// Get memory information.
///
/// On Mac OS X and Windows, the buffers and cached variables of the MemInfo returned are zero.
pub fn mem_info() -> Result<MemInfo, String> {
    if cfg!(target_os = "linux") {
        let mut f = File::open("/proc/meminfo").unwrap();
        let mut s = String::new();
        let _ = f.read_to_string(&mut s).unwrap();
        let mut lines = s.as_slice().split('\n');
        let total = get_mem_num(lines.next().unwrap());
        let free = get_mem_num(lines.next().unwrap());
        let avail = get_mem_num(lines.next().unwrap());
        let buffers = get_mem_num(lines.next().unwrap());
        let cached = get_mem_num(lines.next().unwrap());
        let swap_total = {
            for _ in 0..9 {
                lines.next();
            }
            get_mem_num(lines.next().unwrap())
        };
        let swap_free = get_mem_num(lines.next().unwrap());

        Ok(MemInfo{ total: total, free: free, avail: avail,
                    buffers: buffers, cached: cached,
                    swap_total: swap_total, swap_free: swap_free})
    } else if cfg!(target_os = "macos") || cfg!(target_os = "windows") {
        Ok(unsafe { get_mem_info() })
    } else {
        Err("Unsupported system".to_string())
    }
}

/// Get disk information.
///
/// Notice, it just calculate current disk on Windows.
pub fn disk_info() -> Result<DiskInfo, String> {
    if cfg!(target_os = "linux") ||
        cfg!(target_os = "macos") ||
        cfg!(target_os = "windows")
    {
        Ok(unsafe { get_disk_info() })
    } else {
        Err("Unsupported system".to_string())
    }
}

#[test]
fn test_os_type() {
    os_type().unwrap();
}

#[test]
fn test_os_release() {
    os_release().unwrap();
}

#[test]
fn test_cpu_num() {
    cpu_num().unwrap();
}

#[test]
fn test_cpu_speed() {
    cpu_speed().unwrap();
}

#[test]
fn test_loadavg() {
    loadavg().unwrap();
}

#[test]
fn test_proc_total() {
    proc_total().unwrap();
}

#[test]
fn test_mem_info() {
    mem_info().unwrap();
}

#[test]
fn test_disk_info() {
    disk_info().unwrap();
}

<commit_msg>perfect comments<commit_after>#![feature(core)]
#![feature(std_misc)]
#![feature(fs)]
#![feature(io)]

//! #Introduction
//! **This crate focuses on geting system information.**
//!
//! Now it can get kernel and cpu information, memory and disk usage, average load value and so on.
//!
//! If you have some advice or want the crate supports more system information, contact with me.

use std::ffi;
use std::io::Read;
use std::fs::File;

/// System load average value.
#[repr(C)]
pub struct LoadAvg {
    /// Average load value within one minite.
    pub one: f64,
    /// Average load value within five minites.
    pub five: f64,
    /// Average load value within fifteen minites.
    pub fifteen: f64
}

/// System memory information.
#[repr(C)]
pub struct MemInfo {
    /// Total physical memory.
    pub total: u64,
    pub free:  u64,
    pub avail: u64,

    pub buffers: u64,
    pub cached:  u64,

    /// Total swap memory.
    pub swap_total: u64,
    pub swap_free:  u64
}

/// System momory information.
#[repr(C)]
pub struct DiskInfo {
    pub total: u64,
    pub free: u64
}

extern {
    fn get_os_type() -> *const i8;
    fn get_os_release() -> *const i8;

    fn get_cpu_num() -> u32;
    fn get_cpu_speed() -> u64;

    fn get_loadavg() -> LoadAvg;
    fn get_proc_total() -> u64;

    fn get_mem_info() -> MemInfo;
    fn get_disk_info() -> DiskInfo;
}

/// Get operation system type.
///
/// Such as "Linux", "Darwin", "Windows".
pub fn os_type() -> Result<String, String> {
    if cfg!(target_os = "linux") {
        let mut f = File::open("/proc/sys/kernel/ostype").unwrap();
        let mut s = String::new();
        let _ = f.read_to_string(&mut s).unwrap();
        s.pop();  // pop '\n'
        Ok(s)
    } else if cfg!(target_os = "macos") ||
        cfg!(target_os = "windows")
    {
        Ok(unsafe {
            let p = get_os_type();
            String::from_utf8_lossy(ffi::CStr::from_ptr(p).to_bytes()).into_owned()
        })
    } else {
        Err("Unsupported system".to_string())
    }
}

/// Get operation system release version.
///
/// Such as "3.19.0-gentoo"
pub fn os_release() -> Result<String, String> {
    if cfg!(target_os = "linux") {
        let mut f = File::open("/proc/sys/kernel/osrelease").unwrap();
        let mut s = String::new();
        let _ = f.read_to_string(&mut s).unwrap();
        s.pop();
        Ok(s)
    } else if cfg!(target_os = "macos") ||
        cfg!(target_os = "windows")
    {
        Ok(unsafe {
            let p = get_os_release();
            String::from_utf8_lossy(ffi::CStr::from_ptr(p).to_bytes()).into_owned()
        })
    } else {
        Err("Unsupported system".to_string())
    }
}

/// Get cpu num quantity.
///
/// Notice, it returns the logical cpu quantity.
pub fn cpu_num() -> Result<u32, String> {
    if cfg!(target_os = "linux")
        || cfg!(target_os = "macos") || cfg!(target_os = "windows")
    {
        Ok(unsafe { get_cpu_num() })
    } else {
        Err("Unsupported system".to_string())
    }
}

/// Get cpu speed.
///
/// Such as 2500, that is 2500 MHz.
pub fn cpu_speed() -> Result<u64, String> {
    if cfg!(target_os = "linux") {
        // /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_cur_freq
        let mut f = File::open("/proc/cpuinfo").unwrap();
        let mut s = String::new();
        let _ = f.read_to_string(&mut s).unwrap();
        let mut lines = s.as_slice().split('\n');
        Ok({
            for _ in 0..7 {
                lines.next();
            }
            let mut words = lines.next().unwrap().split(':');
            words.next();
            let s = words.next().unwrap().trim().trim_right_matches('\n');
            s.parse::<f64>().unwrap() as u64
        })
    } else if cfg!(target_os = "macos") || cfg!(target_os = "windows") {
        Ok(unsafe { get_cpu_speed() })
    } else {
        Err("Unsupported system".to_string())
    }
}

/// Get system load average value.
///
/// Notice, on windows, one/five/fifteen of the LoadAvg returned are the current load.
pub fn loadavg() -> Result<LoadAvg, String> {
    if cfg!(target_os = "linux") {
        let mut f = File::open("/proc/loadavg").unwrap();
        let mut s = String::new();
        let _ = f.read_to_string(&mut s).unwrap();
        let mut words = s.as_slice().split(' ');
        let one = words.next().unwrap().parse::<f64>().unwrap();
        let five = words.next().unwrap().parse::<f64>().unwrap();
        let fifteen = words.next().unwrap().parse::<f64>().unwrap();
        Ok(LoadAvg { one: one, five: five, fifteen: fifteen} )
    } else if cfg!(target_os = "macos") || cfg!(target_os = "windows") {
        Ok(unsafe { get_loadavg() })
    } else {
        Err("Unsupported system".to_string())
    }
}

/// Get current processes quantity.
///
/// Notice, it temporarily does not support Windows.
pub fn proc_total() -> Result<u64, String> {
    if cfg!(target_os = "linux") {
        let mut f = File::open("/proc/loadavg").unwrap();
        let mut s = String::new();
        let _ = f.read_to_string(&mut s).unwrap();
        Ok({
            let mut words = s.as_slice().splitn(3, ' ');
            for _ in 0..3 {
                words.next();
            }
            let mut words = words.next().unwrap().split('/');
            words.next();
            let mut words = words.next().unwrap().split(' ');
            words.next().unwrap().parse::<u64>().unwrap()
        })
    } else if cfg!(target_os = "macos") || cfg!(target_os = "windows") {
        Ok(unsafe { get_proc_total() })
    } else {
        Err("Unsupported system".to_string())
    }
}

// Analyse number from line.
fn get_mem_num(line: &str) -> u64 {
    let mut line = line.splitn(1, ' ');
    line.next();
    line.next().unwrap().trim_left().split(' ').next().unwrap()
        .parse::<u64>().unwrap()
}

/// Get memory information.
///
/// On Mac OS X and Windows, the buffers and cached variables of the MemInfo returned are zero.
pub fn mem_info() -> Result<MemInfo, String> {
    if cfg!(target_os = "linux") {
        let mut f = File::open("/proc/meminfo").unwrap();
        let mut s = String::new();
        let _ = f.read_to_string(&mut s).unwrap();
        let mut lines = s.as_slice().split('\n');
        let total = get_mem_num(lines.next().unwrap());
        let free = get_mem_num(lines.next().unwrap());
        let avail = get_mem_num(lines.next().unwrap());
        let buffers = get_mem_num(lines.next().unwrap());
        let cached = get_mem_num(lines.next().unwrap());
        let swap_total = {
            for _ in 0..9 {
                lines.next();
            }
            get_mem_num(lines.next().unwrap())
        };
        let swap_free = get_mem_num(lines.next().unwrap());

        Ok(MemInfo{ total: total, free: free, avail: avail,
                    buffers: buffers, cached: cached,
                    swap_total: swap_total, swap_free: swap_free})
    } else if cfg!(target_os = "macos") || cfg!(target_os = "windows") {
        Ok(unsafe { get_mem_info() })
    } else {
        Err("Unsupported system".to_string())
    }
}

/// Get disk information.
///
/// Notice, it just calculate current disk on Windows.
pub fn disk_info() -> Result<DiskInfo, String> {
    if cfg!(target_os = "linux") ||
        cfg!(target_os = "macos") ||
        cfg!(target_os = "windows")
    {
        Ok(unsafe { get_disk_info() })
    } else {
        Err("Unsupported system".to_string())
    }
}

#[test]
fn test_os_type() {
    os_type().unwrap();
}

#[test]
fn test_os_release() {
    os_release().unwrap();
}

#[test]
fn test_cpu_num() {
    cpu_num().unwrap();
}

#[test]
fn test_cpu_speed() {
    cpu_speed().unwrap();
}

#[test]
fn test_loadavg() {
    loadavg().unwrap();
}

#[test]
fn test_proc_total() {
    proc_total().unwrap();
}

#[test]
fn test_mem_info() {
    mem_info().unwrap();
}

#[test]
fn test_disk_info() {
    disk_info().unwrap();
}

<|endoftext|>


<commit_before>extern crate libc;

use libc::{
    c_char,
    c_int,
};

#[repr(C)]
#[derive(Clone, Copy)]
pub struct RawCell {
    pub ch: u32,
    pub fg: u16,
    pub bg: u16,
}

#[repr(C)]
#[derive(Clone, Copy)]
pub struct RawEvent {
    pub etype: u8,
    pub emod: u8,
    pub key: u16,
    pub ch: u32,
    pub w: i32,
    pub h: i32,
    pub x: i32,
    pub y: i32,
}

extern "C" {
    pub fn tb_init() -> c_int;
    pub fn tb_shutdown();
    pub fn tb_width() -> c_int;
    pub fn tb_height() -> c_int;
    pub fn tb_clear();
    pub fn tb_set_clear_attributes(fg: u16, bg: u16);
    pub fn tb_present();
    pub fn tb_set_cursor(cx: c_int, cy: c_int);
    pub fn tb_put_cell(x: c_int, y: c_int, cell: *const RawCell);
    pub fn tb_change_cell(x: c_int, y: c_int, ch: u32, fg: u16, bg: u16);
    pub fn tb_blit(x: c_int, y: c_int, w: c_int, h: c_int, cells: *const RawCell);
    pub fn tb_cell_buffer() -> *mut RawCell;
    pub fn tb_select_input_mode(mode: c_int) -> c_int;
    pub fn tb_select_output_mode(mode: c_int) -> c_int;
    pub fn tb_peek_event(ev: *const ::RawEvent, timeout: c_int) -> c_int;
    pub fn tb_poll_event(ev: *const ::RawEvent) -> c_int;
    pub fn tb_utf8_char_length(c: c_char) -> c_int;
    pub fn tb_utf8_char_to_unicode(out: *mut u32, c: *const c_char) -> c_int;
    pub fn tb_utf8_unicode_to_char(out: *mut c_char, c: u32) -> c_int;
}

pub const TB_KEY_F1: u16 = 0xffff - 0;
pub const TB_KEY_F2: u16 = 0xffff - 1;
pub const TB_KEY_F3: u16 = 0xffff - 2;
pub const TB_KEY_F4: u16 = 0xffff - 3;
pub const TB_KEY_F5: u16 = 0xffff - 4;
pub const TB_KEY_F6: u16 = 0xffff - 5;
pub const TB_KEY_F7: u16 = 0xffff - 6;
pub const TB_KEY_F8: u16 = 0xffff - 7;
pub const TB_KEY_F9: u16 = 0xffff - 8;
pub const TB_KEY_F10: u16 = 0xffff - 9;
pub const TB_KEY_F11: u16 = 0xffff - 10;
pub const TB_KEY_F12: u16 = 0xffff - 11;
pub const TB_KEY_INSERT: u16 = 0xffff - 12;
pub const TB_KEY_DELETE: u16 = 0xffff - 13;
pub const TB_KEY_HOME: u16 = 0xffff - 14;
pub const TB_KEY_END: u16 = 0xffff - 15;
pub const TB_KEY_PGUP: u16 = 0xffff - 16;
pub const TB_KEY_PGDN: u16 = 0xffff - 17;
pub const TB_KEY_ARROW_UP: u16 = 0xffff - 18;
pub const TB_KEY_ARROW_DOWN: u16 = 0xffff - 19;
pub const TB_KEY_ARROW_LEFT: u16 = 0xffff - 20;
pub const TB_KEY_ARROW_RIGHT: u16 = 0xffff - 21;
pub const TB_KEY_MOUSE_LEFT: u16 = 0xffff - 22;
pub const TB_KEY_MOUSE_RIGHT: u16 = 0xffff - 23;
pub const TB_KEY_MOUSE_MIDDLE: u16 = 0xffff - 24;
pub const TB_KEY_MOUSE_RELEASE: u16 = 0xffff - 25;
pub const TB_KEY_MOUSE_WHEEL_UP: u16 = 0xffff - 26;
pub const TB_KEY_MOUSE_WHEEL_DOWN: u16 = 0xffff - 27;

pub const TB_KEY_CTRL_TILDE: u16 = 0x00;
pub const TB_KEY_CTRL_2: u16 = 0x00;
pub const TB_KEY_CTRL_A: u16 = 0x01;
pub const TB_KEY_CTRL_B: u16 = 0x02;
pub const TB_KEY_CTRL_C: u16 = 0x03;
pub const TB_KEY_CTRL_D: u16 = 0x04;
pub const TB_KEY_CTRL_E: u16 = 0x05;
pub const TB_KEY_CTRL_F: u16 = 0x06;
pub const TB_KEY_CTRL_G: u16 = 0x07;
pub const TB_KEY_BACKSPACE: u16 = 0x08;
pub const TB_KEY_CTRL_H: u16 = 0x08;
pub const TB_KEY_TAB: u16 = 0x09;
pub const TB_KEY_CTRL_I: u16 = 0x09;
pub const TB_KEY_CTRL_J: u16 = 0x0a;
pub const TB_KEY_CTRL_K: u16 = 0x0b;
pub const TB_KEY_CTRL_L: u16 = 0x0c;
pub const TB_KEY_ENTER: u16 = 0x0d;
pub const TB_KEY_CTRL_M: u16 = 0x0d;
pub const TB_KEY_CTRL_N: u16 = 0x0e;
pub const TB_KEY_CTRL_O: u16 = 0x0f;
pub const TB_KEY_CTRL_P: u16 = 0x10;
pub const TB_KEY_CTRL_Q: u16 = 0x11;
pub const TB_KEY_CTRL_R: u16 = 0x12;
pub const TB_KEY_CTRL_S: u16 = 0x13;
pub const TB_KEY_CTRL_T: u16 = 0x14;
pub const TB_KEY_CTRL_U: u16 = 0x15;
pub const TB_KEY_CTRL_V: u16 = 0x16;
pub const TB_KEY_CTRL_W: u16 = 0x17;
pub const TB_KEY_CTRL_X: u16 = 0x18;
pub const TB_KEY_CTRL_Y: u16 = 0x19;
pub const TB_KEY_CTRL_Z: u16 = 0x1a;
pub const TB_KEY_ESC: u16 = 0x1b;
pub const TB_KEY_CTRL_LSQ_BRACKET: u16 = 0x1b;
pub const TB_KEY_CTRL_3: u16 = 0x1b;
pub const TB_KEY_CTRL_4: u16 = 0x1c;
pub const TB_KEY_CTRL_BACKSLASH: u16 = 0x1c;
pub const TB_KEY_CTRL_5: u16 = 0x1d;
pub const TB_KEY_CTRL_RSQ_BRACKET: u16 = 0x1d;
pub const TB_KEY_CTRL_6: u16 = 0x1e;
pub const TB_KEY_CTRL_7: u16 = 0x1f;
pub const TB_KEY_CTRL_SLASH: u16 = 0x1f;
pub const TB_KEY_CTRL_UNDERSCORE: u16 = 0x1f;
pub const TB_KEY_SPACE: u16 = 0x20;
pub const TB_KEY_BACKSPACE2: u16 = 0x7f;
pub const TB_KEY_CTRL_8: u16 = 0x7f;

pub const TB_MOD_ALT: u8 = 0x01;

pub const TB_DEFAULT: u16 = 0x00;
pub const TB_BLACK: u16 = 0x01;
pub const TB_RED: u16 = 0x02;
pub const TB_GREEN: u16 = 0x03;
pub const TB_YELLOW: u16 = 0x04;
pub const TB_BLUE: u16 = 0x05;
pub const TB_MAGENTA: u16 = 0x06;
pub const TB_CYAN: u16 = 0x07;
pub const TB_WHITE: u16 = 0x08;

pub const TB_BOLD: u16 = 0x0100;
pub const TB_UNDERLINE: u16 = 0x0200;
pub const TB_REVERSE: u16 = 0x0400;

pub const TB_EVENT_KEY: u8 = 1;
pub const TB_EVENT_RESIZE: u8 = 2;
pub const TB_EVENT_MOUSE: u8 = 3;

pub const TB_EUNSUPPORTED_TERMINAL: c_int = -1;
pub const TB_EFAILED_TO_OPEN_TTY: c_int = -2;
pub const TB_EPIPE_TRAP_ERROR: c_int = -3;

pub const TB_HIDE_CURSOR: c_int = -1;

pub const TB_INPUT_CURRENT: c_int = 0;
pub const TB_INPUT_ESC: c_int = 1;
pub const TB_INPUT_ALT: c_int = 2;
pub const TB_INPUT_MOUSE: c_int = 4;

pub const TB_OUTPUT_CURRENT: c_int = 0;
pub const TB_OUTPUT_NORMAL: c_int = 1;
pub const TB_OUTPUT_256: c_int = 2;
pub const TB_OUTPUT_216: c_int = 3;
pub const TB_OUTPUT_GRAYSCALE: c_int = 4;

pub const TB_EOF: c_int = -1;
<commit_msg>tb_{peek,poll}_event: take a *mut RawEvent<commit_after>extern crate libc;

use libc::{
    c_char,
    c_int,
};

#[repr(C)]
#[derive(Clone, Copy)]
pub struct RawCell {
    pub ch: u32,
    pub fg: u16,
    pub bg: u16,
}

#[repr(C)]
#[derive(Clone, Copy)]
pub struct RawEvent {
    pub etype: u8,
    pub emod: u8,
    pub key: u16,
    pub ch: u32,
    pub w: i32,
    pub h: i32,
    pub x: i32,
    pub y: i32,
}

extern "C" {
    pub fn tb_init() -> c_int;
    pub fn tb_shutdown();
    pub fn tb_width() -> c_int;
    pub fn tb_height() -> c_int;
    pub fn tb_clear();
    pub fn tb_set_clear_attributes(fg: u16, bg: u16);
    pub fn tb_present();
    pub fn tb_set_cursor(cx: c_int, cy: c_int);
    pub fn tb_put_cell(x: c_int, y: c_int, cell: *const RawCell);
    pub fn tb_change_cell(x: c_int, y: c_int, ch: u32, fg: u16, bg: u16);
    pub fn tb_blit(x: c_int, y: c_int, w: c_int, h: c_int, cells: *const RawCell);
    pub fn tb_cell_buffer() -> *mut RawCell;
    pub fn tb_select_input_mode(mode: c_int) -> c_int;
    pub fn tb_select_output_mode(mode: c_int) -> c_int;
    pub fn tb_peek_event(ev: *mut RawEvent, timeout: c_int) -> c_int;
    pub fn tb_poll_event(ev: *mut RawEvent) -> c_int;
    pub fn tb_utf8_char_length(c: c_char) -> c_int;
    pub fn tb_utf8_char_to_unicode(out: *mut u32, c: *const c_char) -> c_int;
    pub fn tb_utf8_unicode_to_char(out: *mut c_char, c: u32) -> c_int;
}

pub const TB_KEY_F1: u16 = 0xffff - 0;
pub const TB_KEY_F2: u16 = 0xffff - 1;
pub const TB_KEY_F3: u16 = 0xffff - 2;
pub const TB_KEY_F4: u16 = 0xffff - 3;
pub const TB_KEY_F5: u16 = 0xffff - 4;
pub const TB_KEY_F6: u16 = 0xffff - 5;
pub const TB_KEY_F7: u16 = 0xffff - 6;
pub const TB_KEY_F8: u16 = 0xffff - 7;
pub const TB_KEY_F9: u16 = 0xffff - 8;
pub const TB_KEY_F10: u16 = 0xffff - 9;
pub const TB_KEY_F11: u16 = 0xffff - 10;
pub const TB_KEY_F12: u16 = 0xffff - 11;
pub const TB_KEY_INSERT: u16 = 0xffff - 12;
pub const TB_KEY_DELETE: u16 = 0xffff - 13;
pub const TB_KEY_HOME: u16 = 0xffff - 14;
pub const TB_KEY_END: u16 = 0xffff - 15;
pub const TB_KEY_PGUP: u16 = 0xffff - 16;
pub const TB_KEY_PGDN: u16 = 0xffff - 17;
pub const TB_KEY_ARROW_UP: u16 = 0xffff - 18;
pub const TB_KEY_ARROW_DOWN: u16 = 0xffff - 19;
pub const TB_KEY_ARROW_LEFT: u16 = 0xffff - 20;
pub const TB_KEY_ARROW_RIGHT: u16 = 0xffff - 21;
pub const TB_KEY_MOUSE_LEFT: u16 = 0xffff - 22;
pub const TB_KEY_MOUSE_RIGHT: u16 = 0xffff - 23;
pub const TB_KEY_MOUSE_MIDDLE: u16 = 0xffff - 24;
pub const TB_KEY_MOUSE_RELEASE: u16 = 0xffff - 25;
pub const TB_KEY_MOUSE_WHEEL_UP: u16 = 0xffff - 26;
pub const TB_KEY_MOUSE_WHEEL_DOWN: u16 = 0xffff - 27;

pub const TB_KEY_CTRL_TILDE: u16 = 0x00;
pub const TB_KEY_CTRL_2: u16 = 0x00;
pub const TB_KEY_CTRL_A: u16 = 0x01;
pub const TB_KEY_CTRL_B: u16 = 0x02;
pub const TB_KEY_CTRL_C: u16 = 0x03;
pub const TB_KEY_CTRL_D: u16 = 0x04;
pub const TB_KEY_CTRL_E: u16 = 0x05;
pub const TB_KEY_CTRL_F: u16 = 0x06;
pub const TB_KEY_CTRL_G: u16 = 0x07;
pub const TB_KEY_BACKSPACE: u16 = 0x08;
pub const TB_KEY_CTRL_H: u16 = 0x08;
pub const TB_KEY_TAB: u16 = 0x09;
pub const TB_KEY_CTRL_I: u16 = 0x09;
pub const TB_KEY_CTRL_J: u16 = 0x0a;
pub const TB_KEY_CTRL_K: u16 = 0x0b;
pub const TB_KEY_CTRL_L: u16 = 0x0c;
pub const TB_KEY_ENTER: u16 = 0x0d;
pub const TB_KEY_CTRL_M: u16 = 0x0d;
pub const TB_KEY_CTRL_N: u16 = 0x0e;
pub const TB_KEY_CTRL_O: u16 = 0x0f;
pub const TB_KEY_CTRL_P: u16 = 0x10;
pub const TB_KEY_CTRL_Q: u16 = 0x11;
pub const TB_KEY_CTRL_R: u16 = 0x12;
pub const TB_KEY_CTRL_S: u16 = 0x13;
pub const TB_KEY_CTRL_T: u16 = 0x14;
pub const TB_KEY_CTRL_U: u16 = 0x15;
pub const TB_KEY_CTRL_V: u16 = 0x16;
pub const TB_KEY_CTRL_W: u16 = 0x17;
pub const TB_KEY_CTRL_X: u16 = 0x18;
pub const TB_KEY_CTRL_Y: u16 = 0x19;
pub const TB_KEY_CTRL_Z: u16 = 0x1a;
pub const TB_KEY_ESC: u16 = 0x1b;
pub const TB_KEY_CTRL_LSQ_BRACKET: u16 = 0x1b;
pub const TB_KEY_CTRL_3: u16 = 0x1b;
pub const TB_KEY_CTRL_4: u16 = 0x1c;
pub const TB_KEY_CTRL_BACKSLASH: u16 = 0x1c;
pub const TB_KEY_CTRL_5: u16 = 0x1d;
pub const TB_KEY_CTRL_RSQ_BRACKET: u16 = 0x1d;
pub const TB_KEY_CTRL_6: u16 = 0x1e;
pub const TB_KEY_CTRL_7: u16 = 0x1f;
pub const TB_KEY_CTRL_SLASH: u16 = 0x1f;
pub const TB_KEY_CTRL_UNDERSCORE: u16 = 0x1f;
pub const TB_KEY_SPACE: u16 = 0x20;
pub const TB_KEY_BACKSPACE2: u16 = 0x7f;
pub const TB_KEY_CTRL_8: u16 = 0x7f;

pub const TB_MOD_ALT: u8 = 0x01;

pub const TB_DEFAULT: u16 = 0x00;
pub const TB_BLACK: u16 = 0x01;
pub const TB_RED: u16 = 0x02;
pub const TB_GREEN: u16 = 0x03;
pub const TB_YELLOW: u16 = 0x04;
pub const TB_BLUE: u16 = 0x05;
pub const TB_MAGENTA: u16 = 0x06;
pub const TB_CYAN: u16 = 0x07;
pub const TB_WHITE: u16 = 0x08;

pub const TB_BOLD: u16 = 0x0100;
pub const TB_UNDERLINE: u16 = 0x0200;
pub const TB_REVERSE: u16 = 0x0400;

pub const TB_EVENT_KEY: u8 = 1;
pub const TB_EVENT_RESIZE: u8 = 2;
pub const TB_EVENT_MOUSE: u8 = 3;

pub const TB_EUNSUPPORTED_TERMINAL: c_int = -1;
pub const TB_EFAILED_TO_OPEN_TTY: c_int = -2;
pub const TB_EPIPE_TRAP_ERROR: c_int = -3;

pub const TB_HIDE_CURSOR: c_int = -1;

pub const TB_INPUT_CURRENT: c_int = 0;
pub const TB_INPUT_ESC: c_int = 1;
pub const TB_INPUT_ALT: c_int = 2;
pub const TB_INPUT_MOUSE: c_int = 4;

pub const TB_OUTPUT_CURRENT: c_int = 0;
pub const TB_OUTPUT_NORMAL: c_int = 1;
pub const TB_OUTPUT_256: c_int = 2;
pub const TB_OUTPUT_216: c_int = 3;
pub const TB_OUTPUT_GRAYSCALE: c_int = 4;

pub const TB_EOF: c_int = -1;
<|endoftext|>