<commit_before>extern crate std;
extern crate num;
extern crate uuid;

use std::net::Ipv4Addr;
use std::net::Ipv6Addr;
use self::uuid::Uuid;
use std::borrow::Cow;
use std::borrow::IntoCow;
use std::ops::Deref;

pub type SendStr = Cow<'static, str>;

#[derive(ToPrimitive, Copy)]
pub enum OpcodeRequest {
    //requests
    OpcodeStartup = 0x01,
    OpcodeCredentials = 0x04,
    OpcodeOptions = 0x05,
    OpcodeQuery = 0x07,
    OpcodePrepare = 0x09,
    OpcodeExecute = 0x0A,
    OpcodeRegister = 0x0B,
    OpcodeBatch = 0x0D
}

#[derive(Debug)]
pub enum OpcodeResponse {
    //responces
    OpcodeError = 0x00,
    OpcodeReady = 0x02,
    OpcodeAuthenticate = 0x03,
    OpcodeSupported = 0x06,
    OpcodeResult = 0x08,
    OpcodeEvent = 0x0C,

    OpcodeUnknown
}

#[derive(Debug)]
#[derive(FromPrimitive)]
pub enum KindResult {
    KindVoid = 0x0001,
    KindRows = 0x0002,
    KindSetKeyspace = 0x0003,
    KindPrepared = 0x0004,
    KindSchemaChange = 0x0005
}

pub fn opcode_response(val: u8) -> OpcodeResponse {
    match val {
        /* requests
        0x01 => OpcodeStartup,
        0x04 => OpcodeCredentials,
        0x05 => OpcodeOptions,
        0x07 => OpcodeQuery,
        0x09 => OpcodePrepare,
        0x0B => OpcodeRegister,
        */

        0x00 => OpcodeResponse::OpcodeError,
        0x02 => OpcodeResponse::OpcodeReady,
        0x03 => OpcodeResponse::OpcodeAuthenticate,
        0x06 => OpcodeResponse::OpcodeSupported,
        0x08 => OpcodeResponse::OpcodeResult,
        // 0x0A => OpcodeExecute,
        0x0C => OpcodeResponse::OpcodeEvent,

        _ => OpcodeResponse::OpcodeUnknown
    }
}

#[derive(ToPrimitive, Copy)]
pub enum Consistency {
    Any = 0x0000,
    One = 0x0001,
    Two = 0x0002,
    Three = 0x0003,
    Quorum = 0x0004,
    All = 0x0005,
    LocalQuorum = 0x0006,
    EachQuorum = 0x0007,
    Unknown,
}

#[derive(ToPrimitive, Copy)]
pub enum BatchType {
    Logged = 0x00,
    Unlogged = 0x01,
    Counter = 0x02
}

#[derive(Debug)]
pub enum CqlValueType {
    ColumnCustom = 0x0000,
    ColumnASCII = 0x0001,
    ColumnBigInt = 0x0002,
    ColumnBlob = 0x0003,
    ColumnBoolean = 0x0004,
    ColumnCounter = 0x0005,
    ColumnDecimal = 0x0006,
    ColumnDouble = 0x0007,
    ColumnFloat = 0x0008,
    ColumnInt = 0x0009,
    ColumnText = 0x000A,
    ColumnTimestamp = 0x000B,
    ColumnUuid = 0x000C,
    ColumnVarChar = 0x000D,
    ColumnVarint = 0x000E,
    ColumnTimeUuid = 0x000F,
    ColumnInet = 0x0010,
    ColumnList = 0x0020,
    ColumnMap = 0x0021,
    ColumnSet = 0x0022,
    ColumnUnknown,
}

pub fn cql_column_type(val: u16) -> CqlValueType {
    match val {
        0x0000 => CqlValueType::ColumnCustom,
        0x0001 => CqlValueType::ColumnASCII,
        0x0002 => CqlValueType::ColumnBigInt,
        0x0003 => CqlValueType::ColumnBlob,
        0x0004 => CqlValueType::ColumnBoolean,
        0x0005 => CqlValueType::ColumnCounter,
        0x0006 => CqlValueType::ColumnDecimal,
        0x0007 => CqlValueType::ColumnDouble,
        0x0008 => CqlValueType::ColumnFloat,
        0x0009 => CqlValueType::ColumnInt,
        0x000A => CqlValueType::ColumnText,
        0x000B => CqlValueType::ColumnTimestamp,
        0x000C => CqlValueType::ColumnUuid,
        0x000D => CqlValueType::ColumnVarChar,
        0x000E => CqlValueType::ColumnVarint,
        0x000F => CqlValueType::ColumnTimeUuid,
        0x0010 => CqlValueType::ColumnInet,
        0x0020 => CqlValueType::ColumnList,
        0x0021 => CqlValueType::ColumnMap,
        0x0022 => CqlValueType::ColumnSet,
        _ => CqlValueType::ColumnUnknown
    }
}

#[derive(Debug)]
pub enum RCErrorType {
    ReadError,
    WriteError,
    SerializeError,
    ConnectionError,
    NoDataError,
    GenericError,
    IOError
}

#[derive(Debug, Display)]
pub struct RCError {
    pub kind: RCErrorType,
    pub desc: SendStr,
}

impl RCError {
    pub fn new<T: IntoCow<'static, str>>(msg: T, kind: RCErrorType) -> RCError {
        RCError {
            kind: kind,
            desc: msg.into_cow()
        }
    }
    pub fn description(&self) -> &str {
        return self.desc.deref();
    }

}

impl std::error::Error for RCError {
    fn description(&self) -> &str {
        return self.desc.deref();
    }

    fn cause(&self) -> Option<&std::error::Error> {
        return None;
    }
}

impl std::fmt::Display for RCError {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
        write!(f, "Error: {}", self.desc)
    }
}

impl std::error::FromError<std::io::Error> for RCError {
    fn from_error(err: std::io::Error) -> RCError {
        RCError { kind: RCErrorType::IOError, desc: String::from_str(err.description()).into_cow() }
    }
}

pub type RCResult<T> = Result<T, RCError>;

#[derive(Debug)]
pub enum IpAddr {
    Ipv4(Ipv4Addr),
    Ipv6(Ipv6Addr)
}

pub struct CqlStringMap {
    pub pairs: Vec<CqlPair>,
}

pub struct CqlPair {
    pub key: &'static str,
    pub value: &'static str,
}

#[derive(Debug, Copy)]
pub enum CqlBytesSize {
   Cqli32,
   Cqli16
}

pub struct CqlTableDesc {
    pub keyspace: String,
    pub tablename: String
}

#[derive(Debug)]
pub struct CqlColMetadata {
    pub keyspace: SendStr,
    pub table: SendStr,
    pub col_name: SendStr,
    pub col_type: CqlValueType,
    pub col_type_aux1: CqlValueType,
    pub col_type_aux2: CqlValueType
}

#[derive(Debug)]
pub struct CqlMetadata {
    pub flags: u32,
    pub column_count: u32,
    pub keyspace: SendStr,
    pub table: SendStr,
    pub row_metadata: Vec<CqlColMetadata>,
}

#[derive(Debug)]
pub struct Pair<T, V> {
    pub key: T,
    pub value: V
}

pub type CQLList = Vec<CqlValue>;
pub type CQLMap = Vec<Pair<CqlValue, CqlValue>>;
pub type CQLSet = Vec<CqlValue>;

#[derive(Debug)]
pub enum CqlValue {
    CqlASCII(Option<SendStr>),
    CqlBigInt(Option<i64>),
    CqlBlob(Option<Vec<u8>>),
    CqlBoolean(Option<bool>),
    CqlCounter(Option<i64>),
    CqlDecimal(Option<num::BigInt>),
    CqlDouble(Option<f64>),
    CqlFloat(Option<f32>),
    CqlInet(Option<IpAddr>),
    CqlInt(Option<i32>),
    CqlList(Option<CQLList>),
    CqlMap(Option<CQLMap>),
    CqlSet(Option<CQLSet>),
    CqlText(Option<SendStr>),
    CqlTimestamp(Option<u64>),
    CqlUuid(Option<Uuid>),
    CqlTimeUuid(Option<Uuid>),
    CqlVarchar(Option<SendStr>),
    CqlVarint(Option<num::BigInt>),
    CqlUnknown,
}

#[derive(Debug)]
pub struct CqlRow {
    pub cols: Vec<CqlValue>,
}

/*
impl CqlRow {
    fn get_column(&self, col_name: &str) -> Option<CqlValue> {
        let mut i = 0;
        let len = self.metadata.row_metadata.len();
        while i < len {
            if self.metadata.row_metadata.get(i).col_name.as_slice() == col_name {
                return Some(*self.cols.get(i));
            }
            i += 1;
        }
        None
    }
}
*/

#[derive(Debug)]
pub struct CqlRows {
    pub metadata: Box<CqlMetadata>,
    pub rows: Vec<CqlRow>,
}

pub struct CqlRequest<'a> {
    pub version: u8,
    pub flags: u8,
    pub stream: i8,
    pub opcode: OpcodeRequest,
    pub body: CqlRequestBody<'a>,
}

pub enum CqlRequestBody<'a> {
    RequestStartup(CqlStringMap),
    RequestCred(&'a Vec<SendStr>),
    RequestQuery(&'a str, Consistency, u8),
    RequestPrepare(&'a str),
    RequestExec(SendStr, &'a [CqlValue], Consistency, u8),
    RequestBatch(Vec<Query>, BatchType, Consistency, u8),
    RequestOptions,
}

#[derive(Debug)]
pub struct CqlResponse {
    pub version: u8,
    pub flags: u8,
    pub stream: i8,
    pub opcode: OpcodeResponse,
    pub body: CqlResponseBody,
}

#[derive(Debug)]
pub enum CqlResponseBody {
    ResponseError(u32, SendStr),
    ResponseReady,
    ResponseAuth(SendStr),

    ResultVoid,
    ResultRows(Box<CqlRows>),
    ResultKeyspace(SendStr),
    ResultPrepared(Box<CqlPreparedStat>),
    ResultSchemaChange(SendStr, SendStr, SendStr),
    ResultUnknown,

    ResponseEmpty,
}

#[derive(Debug)]
pub struct CqlPreparedStat {
    pub id: Vec<u8>,
    pub meta: Box<CqlMetadata>,
    pub meta_result: Option<Box<CqlMetadata>>
}

pub enum Query {
    QueryStr(SendStr),
    QueryPrepared(SendStr, Vec<CqlValue>),
    QueryBatch(Vec<Query>)
}

<commit_msg>Replace usage of `std::error::FromError` by `std::convert::From`<commit_after>extern crate std;
extern crate num;
extern crate uuid;

use std::net::Ipv4Addr;
use std::net::Ipv6Addr;
use self::uuid::Uuid;
use std::borrow::Cow;
use std::borrow::IntoCow;
use std::ops::Deref;

pub type SendStr = Cow<'static, str>;

#[derive(ToPrimitive, Copy)]
pub enum OpcodeRequest {
    //requests
    OpcodeStartup = 0x01,
    OpcodeCredentials = 0x04,
    OpcodeOptions = 0x05,
    OpcodeQuery = 0x07,
    OpcodePrepare = 0x09,
    OpcodeExecute = 0x0A,
    OpcodeRegister = 0x0B,
    OpcodeBatch = 0x0D
}

#[derive(Debug)]
pub enum OpcodeResponse {
    //responces
    OpcodeError = 0x00,
    OpcodeReady = 0x02,
    OpcodeAuthenticate = 0x03,
    OpcodeSupported = 0x06,
    OpcodeResult = 0x08,
    OpcodeEvent = 0x0C,

    OpcodeUnknown
}

#[derive(Debug)]
#[derive(FromPrimitive)]
pub enum KindResult {
    KindVoid = 0x0001,
    KindRows = 0x0002,
    KindSetKeyspace = 0x0003,
    KindPrepared = 0x0004,
    KindSchemaChange = 0x0005
}

pub fn opcode_response(val: u8) -> OpcodeResponse {
    match val {
        /* requests
        0x01 => OpcodeStartup,
        0x04 => OpcodeCredentials,
        0x05 => OpcodeOptions,
        0x07 => OpcodeQuery,
        0x09 => OpcodePrepare,
        0x0B => OpcodeRegister,
        */

        0x00 => OpcodeResponse::OpcodeError,
        0x02 => OpcodeResponse::OpcodeReady,
        0x03 => OpcodeResponse::OpcodeAuthenticate,
        0x06 => OpcodeResponse::OpcodeSupported,
        0x08 => OpcodeResponse::OpcodeResult,
        // 0x0A => OpcodeExecute,
        0x0C => OpcodeResponse::OpcodeEvent,

        _ => OpcodeResponse::OpcodeUnknown
    }
}

#[derive(ToPrimitive, Copy)]
pub enum Consistency {
    Any = 0x0000,
    One = 0x0001,
    Two = 0x0002,
    Three = 0x0003,
    Quorum = 0x0004,
    All = 0x0005,
    LocalQuorum = 0x0006,
    EachQuorum = 0x0007,
    Unknown,
}

#[derive(ToPrimitive, Copy)]
pub enum BatchType {
    Logged = 0x00,
    Unlogged = 0x01,
    Counter = 0x02
}

#[derive(Debug)]
pub enum CqlValueType {
    ColumnCustom = 0x0000,
    ColumnASCII = 0x0001,
    ColumnBigInt = 0x0002,
    ColumnBlob = 0x0003,
    ColumnBoolean = 0x0004,
    ColumnCounter = 0x0005,
    ColumnDecimal = 0x0006,
    ColumnDouble = 0x0007,
    ColumnFloat = 0x0008,
    ColumnInt = 0x0009,
    ColumnText = 0x000A,
    ColumnTimestamp = 0x000B,
    ColumnUuid = 0x000C,
    ColumnVarChar = 0x000D,
    ColumnVarint = 0x000E,
    ColumnTimeUuid = 0x000F,
    ColumnInet = 0x0010,
    ColumnList = 0x0020,
    ColumnMap = 0x0021,
    ColumnSet = 0x0022,
    ColumnUnknown,
}

pub fn cql_column_type(val: u16) -> CqlValueType {
    match val {
        0x0000 => CqlValueType::ColumnCustom,
        0x0001 => CqlValueType::ColumnASCII,
        0x0002 => CqlValueType::ColumnBigInt,
        0x0003 => CqlValueType::ColumnBlob,
        0x0004 => CqlValueType::ColumnBoolean,
        0x0005 => CqlValueType::ColumnCounter,
        0x0006 => CqlValueType::ColumnDecimal,
        0x0007 => CqlValueType::ColumnDouble,
        0x0008 => CqlValueType::ColumnFloat,
        0x0009 => CqlValueType::ColumnInt,
        0x000A => CqlValueType::ColumnText,
        0x000B => CqlValueType::ColumnTimestamp,
        0x000C => CqlValueType::ColumnUuid,
        0x000D => CqlValueType::ColumnVarChar,
        0x000E => CqlValueType::ColumnVarint,
        0x000F => CqlValueType::ColumnTimeUuid,
        0x0010 => CqlValueType::ColumnInet,
        0x0020 => CqlValueType::ColumnList,
        0x0021 => CqlValueType::ColumnMap,
        0x0022 => CqlValueType::ColumnSet,
        _ => CqlValueType::ColumnUnknown
    }
}

#[derive(Debug)]
pub enum RCErrorType {
    ReadError,
    WriteError,
    SerializeError,
    ConnectionError,
    NoDataError,
    GenericError,
    IOError
}

#[derive(Debug, Display)]
pub struct RCError {
    pub kind: RCErrorType,
    pub desc: SendStr,
}

impl RCError {
    pub fn new<T: IntoCow<'static, str>>(msg: T, kind: RCErrorType) -> RCError {
        RCError {
            kind: kind,
            desc: msg.into_cow()
        }
    }
    pub fn description(&self) -> &str {
        return self.desc.deref();
    }

}

impl std::error::Error for RCError {
    fn description(&self) -> &str {
        return self.desc.deref();
    }

    fn cause(&self) -> Option<&std::error::Error> {
        return None;
    }
}

impl std::fmt::Display for RCError {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
        write!(f, "Error: {}", self.desc)
    }
}

impl std::convert::From<std::io::Error> for RCError {
    fn from(err: std::io::Error) -> RCError {
        RCError { kind: RCErrorType::IOError, desc: String::from_str(err.description()).into_cow() }
    }
}

pub type RCResult<T> = Result<T, RCError>;

#[derive(Debug)]
pub enum IpAddr {
    Ipv4(Ipv4Addr),
    Ipv6(Ipv6Addr)
}

pub struct CqlStringMap {
    pub pairs: Vec<CqlPair>,
}

pub struct CqlPair {
    pub key: &'static str,
    pub value: &'static str,
}

#[derive(Debug, Copy)]
pub enum CqlBytesSize {
   Cqli32,
   Cqli16
}

pub struct CqlTableDesc {
    pub keyspace: String,
    pub tablename: String
}

#[derive(Debug)]
pub struct CqlColMetadata {
    pub keyspace: SendStr,
    pub table: SendStr,
    pub col_name: SendStr,
    pub col_type: CqlValueType,
    pub col_type_aux1: CqlValueType,
    pub col_type_aux2: CqlValueType
}

#[derive(Debug)]
pub struct CqlMetadata {
    pub flags: u32,
    pub column_count: u32,
    pub keyspace: SendStr,
    pub table: SendStr,
    pub row_metadata: Vec<CqlColMetadata>,
}

#[derive(Debug)]
pub struct Pair<T, V> {
    pub key: T,
    pub value: V
}

pub type CQLList = Vec<CqlValue>;
pub type CQLMap = Vec<Pair<CqlValue, CqlValue>>;
pub type CQLSet = Vec<CqlValue>;

#[derive(Debug)]
pub enum CqlValue {
    CqlASCII(Option<SendStr>),
    CqlBigInt(Option<i64>),
    CqlBlob(Option<Vec<u8>>),
    CqlBoolean(Option<bool>),
    CqlCounter(Option<i64>),
    CqlDecimal(Option<num::BigInt>),
    CqlDouble(Option<f64>),
    CqlFloat(Option<f32>),
    CqlInet(Option<IpAddr>),
    CqlInt(Option<i32>),
    CqlList(Option<CQLList>),
    CqlMap(Option<CQLMap>),
    CqlSet(Option<CQLSet>),
    CqlText(Option<SendStr>),
    CqlTimestamp(Option<u64>),
    CqlUuid(Option<Uuid>),
    CqlTimeUuid(Option<Uuid>),
    CqlVarchar(Option<SendStr>),
    CqlVarint(Option<num::BigInt>),
    CqlUnknown,
}

#[derive(Debug)]
pub struct CqlRow {
    pub cols: Vec<CqlValue>,
}

/*
impl CqlRow {
    fn get_column(&self, col_name: &str) -> Option<CqlValue> {
        let mut i = 0;
        let len = self.metadata.row_metadata.len();
        while i < len {
            if self.metadata.row_metadata.get(i).col_name.as_slice() == col_name {
                return Some(*self.cols.get(i));
            }
            i += 1;
        }
        None
    }
}
*/

#[derive(Debug)]
pub struct CqlRows {
    pub metadata: Box<CqlMetadata>,
    pub rows: Vec<CqlRow>,
}

pub struct CqlRequest<'a> {
    pub version: u8,
    pub flags: u8,
    pub stream: i8,
    pub opcode: OpcodeRequest,
    pub body: CqlRequestBody<'a>,
}

pub enum CqlRequestBody<'a> {
    RequestStartup(CqlStringMap),
    RequestCred(&'a Vec<SendStr>),
    RequestQuery(&'a str, Consistency, u8),
    RequestPrepare(&'a str),
    RequestExec(SendStr, &'a [CqlValue], Consistency, u8),
    RequestBatch(Vec<Query>, BatchType, Consistency, u8),
    RequestOptions,
}

#[derive(Debug)]
pub struct CqlResponse {
    pub version: u8,
    pub flags: u8,
    pub stream: i8,
    pub opcode: OpcodeResponse,
    pub body: CqlResponseBody,
}

#[derive(Debug)]
pub enum CqlResponseBody {
    ResponseError(u32, SendStr),
    ResponseReady,
    ResponseAuth(SendStr),

    ResultVoid,
    ResultRows(Box<CqlRows>),
    ResultKeyspace(SendStr),
    ResultPrepared(Box<CqlPreparedStat>),
    ResultSchemaChange(SendStr, SendStr, SendStr),
    ResultUnknown,

    ResponseEmpty,
}

#[derive(Debug)]
pub struct CqlPreparedStat {
    pub id: Vec<u8>,
    pub meta: Box<CqlMetadata>,
    pub meta_result: Option<Box<CqlMetadata>>
}

pub enum Query {
    QueryStr(SendStr),
    QueryPrepared(SendStr, Vec<CqlValue>),
    QueryBatch(Vec<Query>)
}

<|endoftext|>


<commit_before>use error::{ToResult};
use std::env;
use std::fs;
use std::path::{PathBuf};

pub fn resolve(pathname: &str) -> ToResult<PathBuf> {
    let mut absolute = try!(env::current_dir());

    return Ok(PathBuf::from("bar"));
}
<commit_msg>Adds functionality and tests for dir::resolve(...)<commit_after>use error::{ToResult};
use std::env;
use std::path::{PathBuf};

pub fn resolve(pathname: &str) -> ToResult<PathBuf> {
    let mut absolute = try!(env::current_dir());
            absolute.push(pathname);

    let canonical = try!(absolute.canonicalize());

    return Ok(canonical);
}

#[cfg(test)]
mod tests {
    extern crate env_logger;

    use std::env;
    use super::*;

    #[test]
    fn relative() {
        let _ = env_logger::init();

        let actual = resolve("src").expect("should not fail");
        let mut expected = env::current_dir().expect("should not fail");
                expected.push("src");
        assert_eq!(actual, expected);

        let actual = resolve("src").expect("should not fail");
        assert_eq!(actual, expected);
    }

    #[test]
    fn dot() {
        let _ = env_logger::init();

        let actual = resolve(".").expect("should not fail");
        let expected = env::current_dir().expect("should not fail");

        assert_eq!(actual, expected);
    }

    #[test]
    fn dot_dot() {
        let _ = env_logger::init();

        let actual = resolve("../to-directory/src").expect("should not fail");
        let mut expected = env::current_dir().expect("should not fail");
                expected.pop();
                expected.push("to-directory");
                expected.push("src");

        assert_eq!(actual, expected);
    }
}
<|endoftext|>


<commit_before>// TODO: Clean code, please
// TODO: Check types used. Try to minimize the memory used
// TODO: Add more tests.
// TODO: Better UI. Colors? Num of matches?
// TODO: Try to do the fuzzy search async?
extern crate scout;
extern crate docopt;
extern crate termios;
extern crate termion;

use std::env;

use termios::{Termios, TCSANOW, ECHO, ICANON, tcsetattr};
use termion::event::Key;
use termion::input::TermRead;
use termion::screen::*;
use termion::{color, style};
use std::io::{self, Read, Write};
use std::os::unix::io::AsRawFd;

const USAGE: &'static str = "
Scout: Small fuzzy finder

This program expects a list of items in the
standard input, so it is better to use it
with pipes.

Usage:
  scout [options]

Options:
  -h --help      Show this screen.
  -v --version   Show version.

Example:
  $ ls | scout
";

fn magic() -> Result<String, io::Error> {
    // Collect initial input
    let mut buffer = String::new();
    try!(io::stdin().read_to_string(&mut buffer));
    let input: Vec<&str> = buffer.split("\n")
        .map(|s| s.trim())
        .filter(|s| !s.is_empty())
        .collect();
    let width = format!("{}", input.len()).len();
    let mut selection = 0; // current selected item

    // I need to transform tty into raw mode to get chars byte by byte.
    // Check termios crate
    // see: http://stackoverflow.com/a/37416107
    let tty = termion::get_tty().unwrap();
    let fd = tty.as_raw_fd();
    let original_tty = Termios::from_fd(fd).unwrap();
    let mut new_tty = original_tty.clone();  // make a mutable copy of termios
                                            // that we will modify
    new_tty.c_lflag &= !(ICANON | ECHO); // no echo and canonical mode
    tcsetattr(fd, TCSANOW, &mut new_tty).unwrap();

    // Then we can use this tty to create our screen with the help
    // of termion crate
    let mut screen = AlternateScreen::from(tty);
    let mut result = String::new();
    let mut query: Vec<char> = vec![];
    let mut buffer: Vec<u8> = vec![];

    'event: loop {
        let s: String = query.iter().cloned().collect();
        let query_chars: Vec<char> = query.iter().cloned().collect();
        let choices = scout::explore(&input, &query_chars);

        // Clear the screen and put the cursor at the beginning
        writeln!(
            &mut screen,
            "{}{}",
            termion::clear::All,
            termion::cursor::Goto(1, 1),
        ).unwrap();

        // Print all the choices
        for (i, choice) in choices.iter().take(21).cloned().enumerate() {
            // Split the string in different areas
            // to highlight the matching part
            let string = choice.to_string();
            let chars = string.char_indices();
            let mut ended = None;
            let mut line: String = chars.map(|(index, ch)| {
                if index == choice.start() && index < choice.end() {
                    format!("{}{}", color::Fg(color::LightGreen), ch)
                } else if index == choice.end() {
                    ended = Some(index);
                    format!("{}{}", color::Fg(color::Reset), ch)
                } else {
                    format!("{}", ch)
                }
            }).collect();

            // Ensure that we stop highlihting things
            if ended.is_none() {
                line = format!("{}{}", line, color::Fg(color::Reset));
            }

            if i == selection {
                writeln!(&mut screen, "{}{}{}", style::Invert, line, style::Reset).unwrap();
            } else {
                writeln!(&mut screen, "{}", line).unwrap();
            }
        }

        // Go to the beginning again and redraw the prompt.
        // This will put the cursor at the end of it
        let prompt = format!("{:width$} > {}", choices.len(), s, width = width);
        write!(&mut screen, "{}{}", termion::cursor::Goto(1, 1), prompt).unwrap();

        screen.flush().unwrap();

        // Read a maximum of 3 bytes to handle special keys like
        // Up and Down arrow keys
        let mut int_buffer = [0;3];
        buffer.clear();
        // Ensure that we read 2 bytes in an intermediate buffer
        //
        // If the amount of real bytes read is less than 2,
        // put only that byte in the buffer so we don't carry
        // junk.
        //
        // If the amount is 2, put both in the buffer
        match screen.read(&mut int_buffer) {
            Ok(n) => {
                buffer = int_buffer.iter().take(n).map(|&x| x).collect()
            },
            Err(_) => {}
        };

        // Now this buffer has the exact amount of bytes from
        // the input so we can use the #keys() function from termion
        // to read and transform those bytes into proper keys
        for c in buffer.keys() {
            match c.unwrap() {
                Key::Backspace => {
                    let _ = query.pop();
                },
                Key::Ctrl('p') | Key::Up => {
                    selection = if selection == 0 {
                        // TODO: This should be only over the visible
                        // window
                        choices.len() - 1
                    } else {
                        selection - 1
                    };
                },
                Key::Ctrl('n') | Key::Down => {
                    // TODO: This should be only over the visible
                    // window
                    // TODO: The loop shouldn't be trigger again,
                    // we should render the screen without
                    // doing a full search. In the next event loop
                    // iteration the selection should be reset to
                    // 0 again
                    selection = if selection == (choices.len() - 1) {
                        0
                    } else {
                        selection + 1
                    };
                },
                Key::Char('\n') | Key::Ctrl('j') | Key::Ctrl('m') => {
                    let choice = choices[selection];
                    result = choice.to_string();

                    break 'event
                },
                Key::Ctrl('u') => {
                    query.clear();
                },
                Key::Char(c) => {
                    query.push(c);
                },
                Key::Esc => break 'event,
                _ => {},
            }
        }
    };

    tcsetattr(fd, TCSANOW, & original_tty).unwrap();  // reset the stdin to

    Ok(result)
}

pub fn main() {
    docopt::Docopt::new(USAGE)
        .and_then(|doc| {
            doc.argv(env::args())
                .version(Some(scout::version()))
                .parse()
        })
        .unwrap_or_else(|e| e.exit());;

    match magic() {
        Ok(result) => println!("{}", result),
        Err(e) => panic!(e),
    }
}
<commit_msg>Be emoji friendly: Read 4 bytes, not 3.<commit_after>// TODO: Clean code, please
// TODO: Check types used. Try to minimize the memory used
// TODO: Add more tests.
// TODO: Better UI. Colors? Num of matches?
// TODO: Try to do the fuzzy search async?
extern crate scout;
extern crate docopt;
extern crate termios;
extern crate termion;

use std::env;

use termios::{Termios, TCSANOW, ECHO, ICANON, tcsetattr};
use termion::event::Key;
use termion::input::TermRead;
use termion::screen::*;
use termion::{color, style};
use std::io::{self, Read, Write};
use std::os::unix::io::AsRawFd;

const USAGE: &'static str = "
Scout: Small fuzzy finder

This program expects a list of items in the
standard input, so it is better to use it
with pipes.

Usage:
  scout [options]

Options:
  -h --help      Show this screen.
  -v --version   Show version.

Example:
  $ ls | scout
";

fn magic() -> Result<String, io::Error> {
    // Collect initial input
    let mut buffer = String::new();
    try!(io::stdin().read_to_string(&mut buffer));
    let input: Vec<&str> = buffer.split("\n")
        .map(|s| s.trim())
        .filter(|s| !s.is_empty())
        .collect();
    let width = format!("{}", input.len()).len();
    let mut selection = 0; // current selected item

    // I need to transform tty into raw mode to get chars byte by byte.
    // Check termios crate
    // see: http://stackoverflow.com/a/37416107
    let tty = termion::get_tty().unwrap();
    let fd = tty.as_raw_fd();
    let original_tty = Termios::from_fd(fd).unwrap();
    let mut new_tty = original_tty.clone();  // make a mutable copy of termios
                                            // that we will modify
    new_tty.c_lflag &= !(ICANON | ECHO); // no echo and canonical mode
    tcsetattr(fd, TCSANOW, &mut new_tty).unwrap();

    // Then we can use this tty to create our screen with the help
    // of termion crate
    let mut screen = AlternateScreen::from(tty);
    let mut result = String::new();
    let mut query: Vec<char> = vec![];
    let mut buffer: Vec<u8> = vec![];

    'event: loop {
        let s: String = query.iter().cloned().collect();
        let query_chars: Vec<char> = query.iter().cloned().collect();
        let choices = scout::explore(&input, &query_chars);

        // Clear the screen and put the cursor at the beginning
        writeln!(
            &mut screen,
            "{}{}",
            termion::clear::All,
            termion::cursor::Goto(1, 1),
        ).unwrap();

        // Print all the choices
        for (i, choice) in choices.iter().take(21).cloned().enumerate() {
            // Split the string in different areas
            // to highlight the matching part
            let string = choice.to_string();
            let chars = string.char_indices();
            let mut ended = None;
            let mut line: String = chars.map(|(index, ch)| {
                if index == choice.start() && index < choice.end() {
                    format!("{}{}", color::Fg(color::LightGreen), ch)
                } else if index == choice.end() {
                    ended = Some(index);
                    format!("{}{}", color::Fg(color::Reset), ch)
                } else {
                    format!("{}", ch)
                }
            }).collect();

            // Ensure that we stop highlihting things
            if ended.is_none() {
                line = format!("{}{}", line, color::Fg(color::Reset));
            }

            if i == selection {
                writeln!(&mut screen, "{}{}{}", style::Invert, line, style::Reset).unwrap();
            } else {
                writeln!(&mut screen, "{}", line).unwrap();
            }
        }

        // Go to the beginning again and redraw the prompt.
        // This will put the cursor at the end of it
        let prompt = format!("{:width$} > {}", choices.len(), s, width = width);
        write!(&mut screen, "{}{}", termion::cursor::Goto(1, 1), prompt).unwrap();

        screen.flush().unwrap();

        // Read a maximum of 4 bytes to handle special keys like
        // Up and Down arrow keys
        let mut int_buffer = [0;4];
        buffer.clear();
        // Ensure that we read 4 bytes in an intermediate buffer
        //
        // If the amount of real bytes read is less than 4,
        // put only that byte in the buffer so we don't carry
        // junk.
        //
        // If the amount is 2, put both in the buffer
        match screen.read(&mut int_buffer) {
            Ok(n) => {
                buffer = int_buffer.iter().take(n).map(|&x| x).collect()
            },
            Err(_) => {}
        };

        // Now this buffer has the exact amount of bytes from
        // the input so we can use the #keys() function from termion
        // to read and transform those bytes into proper keys
        for c in buffer.keys() {
            match c.unwrap() {
                Key::Backspace => {
                    let _ = query.pop();
                },
                Key::Ctrl('p') | Key::Up => {
                    selection = if selection == 0 {
                        // TODO: This should be only over the visible
                        // window
                        choices.len() - 1
                    } else {
                        selection - 1
                    };
                },
                Key::Ctrl('n') | Key::Down => {
                    // TODO: This should be only over the visible
                    // window
                    // TODO: The loop shouldn't be trigger again,
                    // we should render the screen without
                    // doing a full search. In the next event loop
                    // iteration the selection should be reset to
                    // 0 again
                    selection = if selection == (choices.len() - 1) {
                        0
                    } else {
                        selection + 1
                    };
                },
                Key::Char('\n') | Key::Ctrl('j') | Key::Ctrl('m') => {
                    let choice = choices[selection];
                    result = choice.to_string();

                    break 'event
                },
                Key::Ctrl('u') => {
                    query.clear();
                },
                Key::Char(c) => {
                    query.push(c);
                },
                Key::Esc => break 'event,
                _ => {},
            }
        }
    };

    tcsetattr(fd, TCSANOW, & original_tty).unwrap();  // reset the stdin to

    Ok(result)
}

pub fn main() {
    docopt::Docopt::new(USAGE)
        .and_then(|doc| {
            doc.argv(env::args())
                .version(Some(scout::version()))
                .parse()
        })
        .unwrap_or_else(|e| e.exit());;

    match magic() {
        Ok(result) => println!("{}", result),
        Err(e) => panic!(e),
    }
}
<|endoftext|>


<commit_before>
use gfx;
use gfx_device_gl::GlResources;
use graphics::{ Context, Graphics };
use graphics::BACK_END_MAX_VERTEX_COUNT as BUFFER_SIZE;

use Texture;

static VERTEX_SHADER: [&'static [u8]; 2] = [
b"#version 120
uniform vec4 color;

attribute vec2 pos;

void main() {
    gl_Position = vec4(pos, 0.0, 1.0);
}
",
b"#version 150 core
uniform vec4 color;

in vec2 pos;

void main() {
    gl_Position = vec4(pos, 0.0, 1.0);
}
"
];

static FRAGMENT_SHADER: [&'static [u8]; 2] = [
b"#version 120
uniform vec4 color;

void main() {
    gl_FragColor = color;
}
",
b"#version 150 core
uniform vec4 color;

out vec4 o_Color;

void main() {
    o_Color = color;
}
"
];

static VERTEX_SHADER_UV: [&'static [u8]; 2] = [
b"#version 120
uniform sampler2D s_texture;
uniform vec4 color;

attribute vec2 pos;
attribute vec2 uv;

varying vec2 v_UV;

void main() {
    v_UV = uv;
    gl_Position = vec4(pos, 0.0, 1.0);
}
",
b"#version 150 core
uniform sampler2D s_texture;
uniform vec4 color;

in vec2 pos;
in vec2 uv;
out vec2 v_UV;
void main() {
    v_UV = uv;
    gl_Position = vec4(pos, 0.0, 1.0);
}
"
];

static FRAGMENT_SHADER_UV: [&'static [u8]; 2] = [
b"#version 120
uniform sampler2D s_texture;
uniform vec4 color;

varying vec2 v_UV;

void main()
{
    gl_FragColor = texture2D(s_texture, v_UV) * color;
}
",
b"#version 150 core
uniform sampler2D s_texture;
uniform vec4 color;

out vec4 o_Color;

in vec2 v_UV;

void main()
{
    o_Color = texture(s_texture, v_UV) * color;
}
"
];

static POS_COMPONENTS: usize = 2;
static UV_COMPONENTS: usize = 2;

// Boiler plate for automatic attribute construction.
// Needs to be improved on gfx-rs side.
// For some reason, using ``*_COMPONENT` triggers some macros errors.

#[vertex_format]
struct PositionFormat { pos: [f32; 2] }

#[vertex_format]
struct ColorFormat { color: [f32; 4] }

#[vertex_format]
struct TexCoordsFormat { uv: [f32; 2] }

#[shader_param(GlResources)]
struct Params {
    color: [f32; 4],
}

#[shader_param(GlResources)]
struct ParamsUV {
    color: [f32; 4],
    s_texture: gfx::shade::TextureParam<GlResources>,
}

/// The data used for drawing 2D graphics.
pub struct Gfx2d {
    buffer_pos: gfx::BufferHandle<GlResources, f32>,
    buffer_uv: gfx::BufferHandle<GlResources, f32>,
    batch: gfx::batch::OwnedBatch<Params>,
    batch_uv: gfx::batch::OwnedBatch<ParamsUV>,
}

impl Gfx2d {
    /// Creates a new G2D object.
    pub fn new<D>(device: &mut D) -> Gfx2d
        where
            D: gfx::Device<Resources = GlResources>
    {
        use gfx::DeviceExt;

        let shader_model = device.get_capabilities().shader_model;

        let vertex = gfx::ShaderSource {
            glsl_120: Some(VERTEX_SHADER[0]),
            glsl_150: Some(VERTEX_SHADER[1]),
            .. gfx::ShaderSource::empty()
        };
        let fragment = gfx::ShaderSource {
            glsl_120: Some(FRAGMENT_SHADER[0]),
            glsl_150: Some(FRAGMENT_SHADER[1]),
            .. gfx::ShaderSource::empty()
        };

        let program = device.link_program(
            vertex.choose(shader_model).unwrap(),
            fragment.choose(shader_model).unwrap())
            .unwrap();

        let vertex = gfx::ShaderSource {
            glsl_120: Some(VERTEX_SHADER_UV[0]),
            glsl_150: Some(VERTEX_SHADER_UV[1]),
            .. gfx::ShaderSource::empty()
        };
        let fragment = gfx::ShaderSource {
            glsl_120: Some(FRAGMENT_SHADER_UV[0]),
            glsl_150: Some(FRAGMENT_SHADER_UV[1]),
            .. gfx::ShaderSource::empty()
        };

        let program_uv = device.link_program(
            vertex.choose(shader_model).unwrap(),
            fragment.choose(shader_model).unwrap())
            .unwrap();

        let buffer_pos = device.create_buffer(
            POS_COMPONENTS * BUFFER_SIZE,
            gfx::BufferUsage::Dynamic);
        let buffer_uv = device.create_buffer(
            UV_COMPONENTS * BUFFER_SIZE,
            gfx::BufferUsage::Dynamic);

        let mut mesh = gfx::Mesh::new(BUFFER_SIZE as u32);
        mesh.attributes.extend(gfx::VertexFormat::generate(
            None::<&PositionFormat>,
            buffer_pos.raw()
        ).into_iter());

        // Reuse parameters from `mesh`.
        let mut mesh_uv = mesh.clone();
        mesh_uv.attributes.extend(gfx::VertexFormat::generate(
            None::<&TexCoordsFormat>,
            buffer_uv.raw()
        ).into_iter());

        let params = Params {
            color: [1.0; 4],
        };
        let mut batch = gfx::batch::OwnedBatch::new(mesh, program, params)
            .unwrap();

        let sampler = device.create_sampler(
            gfx::tex::SamplerInfo::new(
                gfx::tex::FilterMethod::Trilinear,
                gfx::tex::WrapMode::Clamp)
        );

        // Create a dummy texture
        let texture_info = gfx::tex::TextureInfo {
            width: 1,
            height: 1,
            depth: 1,
            levels: 1,
            kind: gfx::tex::TextureKind::Texture2D,
            format: gfx::tex::RGBA8,
        };
        let image_info = texture_info.to_image_info();
        let texture = device.create_texture(texture_info)
            .unwrap();
        device.update_texture(&texture, &image_info,
                &[0x20u8, 0xA0u8, 0xC0u8, 0x00u8])
            .unwrap();
        let params_uv = ParamsUV {
            color: [1.0; 4],
            s_texture: (texture, Some(sampler))
        };
        let mut batch_uv = gfx::batch::OwnedBatch::new(
            mesh_uv, program_uv, params_uv)
            .unwrap();

        // Disable culling.
        batch.state.primitive.method =
            gfx::state::RasterMethod::Fill(gfx::state::CullMode::Nothing);
        batch_uv.state.primitive.method =
            gfx::state::RasterMethod::Fill(gfx::state::CullMode::Nothing);

        Gfx2d {
            buffer_pos: buffer_pos,
            buffer_uv: buffer_uv,
            batch: batch,
            batch_uv: batch_uv,
        }
    }

    /// Renders graphics to a Gfx renderer.
    pub fn draw<C, F>(
        &mut self,
        renderer: &mut gfx::Renderer<C::CommandBuffer>,
        frame: &gfx::Frame<C::Resources>,
        mut f: F
    )
        where
            C: gfx::Device<Resources = GlResources>,
            F: FnMut(Context, &mut GfxGraphics<C>)
    {
        let ref mut g = GfxGraphics::new(
            renderer,
            frame,
            self
        );
        let c = Context::abs(
            frame.width as f64,
            frame.height as f64
        );
        g.enable_alpha_blend();
        f(c, g);
        g.disable_alpha_blend();
    }
}

/// Used for rendering 2D graphics.
pub struct GfxGraphics<'a, C>
    where
        C: 'a + gfx::Device,
        <C as gfx::Device>::CommandBuffer: 'a,
        <C as gfx::Device>::Resources: 'a,
        <<C as gfx::device::Device>::Resources as gfx::device::Resources>::Sampler: 'a,
        <<C as gfx::device::Device>::Resources as gfx::device::Resources>::Texture: 'a,
        <<C as gfx::device::Device>::Resources as gfx::device::Resources>::Surface: 'a,
        <<C as gfx::device::Device>::Resources as gfx::device::Resources>::FrameBuffer: 'a,
        <<C as gfx::device::Device>::Resources as gfx::device::Resources>::Program: 'a,
        <<C as gfx::device::Device>::Resources as gfx::device::Resources>::Shader: 'a,
        <<C as gfx::device::Device>::Resources as gfx::device::Resources>::ArrayBuffer: 'a,
        <<C as gfx::device::Device>::Resources as gfx::device::Resources>::Buffer: 'a
{
    renderer: &'a mut gfx::Renderer<C::CommandBuffer>,
    frame: &'a gfx::Frame<C::Resources>,
    g2d: &'a mut Gfx2d,
}

impl<'a, C> GfxGraphics<'a, C>
    where
        C: gfx::Device<Resources = GlResources>
{
    /// Creates a new object for rendering 2D graphics.
    pub fn new(renderer: &'a mut gfx::Renderer<C::CommandBuffer>,
               frame: &'a gfx::Frame<C::Resources>,
               g2d: &'a mut Gfx2d) -> GfxGraphics<'a, C> {
        GfxGraphics {
            renderer: renderer,
            frame: frame,
            g2d: g2d,
        }
    }

    /// Returns true if texture has alpha channel.
    pub fn has_texture_alpha(&self, texture: &Texture<C>) -> bool {
        use gfx::tex::Components::RGBA;

        texture.handle.get_info().format.get_components() == Some(RGBA)
    }

    /// Enabled alpha blending.
    pub fn enable_alpha_blend(&mut self) {
        use std::default::Default;
        use gfx::state::InverseFlag::{Normal, Inverse};
        use gfx::state::Factor;
        use gfx::state::BlendValue::SourceAlpha;

        let blend = gfx::state::Blend {
            value: [1.0, 1.0, 1.0, 1.0],
            color: gfx::state::BlendChannel {
                    equation: gfx::state::Equation::Add,
                    source: Factor(Normal, SourceAlpha),
                    destination: Factor(Inverse, SourceAlpha)
                },
            alpha: Default::default()
        };

        self.g2d.batch.state.blend = Some(blend);
        self.g2d.batch_uv.state.blend = Some(blend);
    }

    /// Disables alpha blending.
    pub fn disable_alpha_blend(&mut self) {
        self.g2d.batch.state.blend = None;
        self.g2d.batch_uv.state.blend = None;
    }
}

impl<'a, C> Graphics
for GfxGraphics<'a, C>
    where
        C: gfx::Device<Resources = GlResources>,
        C::Resources: 'a,
{
    type Texture = Texture<C>;

    fn clear(&mut self, color: [f32; 4]) {
        let &mut GfxGraphics {
            ref mut renderer,
            frame,
            ..
        } = self;
        renderer.clear(
                gfx::ClearData {
                    color: color,
                    depth: 0.0,
                    stencil: 0,
                },
                gfx::COLOR,
                frame
            );
    }

    fn tri_list<F>(&mut self, color: &[f32; 4], mut f: F)
        where F: FnMut(&mut FnMut(&[f32]))
    {
        let &mut GfxGraphics {
            ref mut renderer,
            ref frame,
            g2d: &mut Gfx2d {
                ref mut buffer_pos,
                ref mut batch,
                ..
            },
        } = self;

        batch.param.color = *color;

        f(&mut |vertices: &[f32]| {
            renderer.update_buffer_vec(buffer_pos.clone(), vertices, 0);

            let n = vertices.len() / POS_COMPONENTS;
            batch.slice = gfx::Slice {
                    prim_type: gfx::PrimitiveType::TriangleList,
                    start: 0,
                    end: n as u32,
                    kind: gfx::SliceKind::Vertex
                };
            let _ = renderer.draw(batch, *frame);
        })
    }

    fn tri_list_uv<F>(
        &mut self,
        color: &[f32; 4],
        texture: &<Self as Graphics>::Texture,
        mut f: F
    )
        where F: FnMut(&mut FnMut(&[f32], &[f32]))
    {
        let &mut GfxGraphics {
            ref mut renderer,
            ref frame,
            g2d: &mut Gfx2d {
                ref mut buffer_pos,
                ref mut buffer_uv,
                ref mut batch_uv,
                ..
            },
        } = self;

        batch_uv.param.s_texture.0 = texture.handle;
        batch_uv.param.color = *color;

        f(&mut |vertices: &[f32], texture_coords: &[f32]| {
            assert_eq!(
                vertices.len() * UV_COMPONENTS,
                texture_coords.len() * POS_COMPONENTS
            );
            renderer.update_buffer_vec(buffer_pos.clone(), vertices, 0);
            renderer.update_buffer_vec(buffer_uv.clone(), texture_coords, 0);

            let n = vertices.len() / POS_COMPONENTS;
            batch_uv.slice = gfx::Slice {
                    prim_type: gfx::PrimitiveType::TriangleList,
                    start: 0,
                    end: n as u32,
                    kind: gfx::SliceKind::Vertex
                };
            let _ = renderer.draw(batch_uv, *frame);
        })
    }
}
<commit_msg>Updated to latest piston-graphics<commit_after>
use gfx;
use gfx_device_gl::GlResources;
use graphics::{ Context, DrawState, Graphics };
use graphics::BACK_END_MAX_VERTEX_COUNT as BUFFER_SIZE;

use Texture;

static VERTEX_SHADER: [&'static [u8]; 2] = [
b"#version 120
uniform vec4 color;

attribute vec2 pos;

void main() {
    gl_Position = vec4(pos, 0.0, 1.0);
}
",
b"#version 150 core
uniform vec4 color;

in vec2 pos;

void main() {
    gl_Position = vec4(pos, 0.0, 1.0);
}
"
];

static FRAGMENT_SHADER: [&'static [u8]; 2] = [
b"#version 120
uniform vec4 color;

void main() {
    gl_FragColor = color;
}
",
b"#version 150 core
uniform vec4 color;

out vec4 o_Color;

void main() {
    o_Color = color;
}
"
];

static VERTEX_SHADER_UV: [&'static [u8]; 2] = [
b"#version 120
uniform sampler2D s_texture;
uniform vec4 color;

attribute vec2 pos;
attribute vec2 uv;

varying vec2 v_UV;

void main() {
    v_UV = uv;
    gl_Position = vec4(pos, 0.0, 1.0);
}
",
b"#version 150 core
uniform sampler2D s_texture;
uniform vec4 color;

in vec2 pos;
in vec2 uv;
out vec2 v_UV;
void main() {
    v_UV = uv;
    gl_Position = vec4(pos, 0.0, 1.0);
}
"
];

static FRAGMENT_SHADER_UV: [&'static [u8]; 2] = [
b"#version 120
uniform sampler2D s_texture;
uniform vec4 color;

varying vec2 v_UV;

void main()
{
    gl_FragColor = texture2D(s_texture, v_UV) * color;
}
",
b"#version 150 core
uniform sampler2D s_texture;
uniform vec4 color;

out vec4 o_Color;

in vec2 v_UV;

void main()
{
    o_Color = texture(s_texture, v_UV) * color;
}
"
];

static POS_COMPONENTS: usize = 2;
static UV_COMPONENTS: usize = 2;

// Boiler plate for automatic attribute construction.
// Needs to be improved on gfx-rs side.
// For some reason, using ``*_COMPONENT` triggers some macros errors.

#[vertex_format]
struct PositionFormat { pos: [f32; 2] }

#[vertex_format]
struct ColorFormat { color: [f32; 4] }

#[vertex_format]
struct TexCoordsFormat { uv: [f32; 2] }

#[shader_param(GlResources)]
struct Params {
    color: [f32; 4],
}

#[shader_param(GlResources)]
struct ParamsUV {
    color: [f32; 4],
    s_texture: gfx::shade::TextureParam<GlResources>,
}

/// The data used for drawing 2D graphics.
pub struct Gfx2d {
    buffer_pos: gfx::BufferHandle<GlResources, f32>,
    buffer_uv: gfx::BufferHandle<GlResources, f32>,
    batch: gfx::batch::OwnedBatch<Params>,
    batch_uv: gfx::batch::OwnedBatch<ParamsUV>,
}

impl Gfx2d {
    /// Creates a new G2D object.
    pub fn new<D>(device: &mut D) -> Gfx2d
        where
            D: gfx::Device<Resources = GlResources>
    {
        use gfx::DeviceExt;

        let shader_model = device.get_capabilities().shader_model;

        let vertex = gfx::ShaderSource {
            glsl_120: Some(VERTEX_SHADER[0]),
            glsl_150: Some(VERTEX_SHADER[1]),
            .. gfx::ShaderSource::empty()
        };
        let fragment = gfx::ShaderSource {
            glsl_120: Some(FRAGMENT_SHADER[0]),
            glsl_150: Some(FRAGMENT_SHADER[1]),
            .. gfx::ShaderSource::empty()
        };

        let program = device.link_program(
            vertex.choose(shader_model).unwrap(),
            fragment.choose(shader_model).unwrap())
            .unwrap();

        let vertex = gfx::ShaderSource {
            glsl_120: Some(VERTEX_SHADER_UV[0]),
            glsl_150: Some(VERTEX_SHADER_UV[1]),
            .. gfx::ShaderSource::empty()
        };
        let fragment = gfx::ShaderSource {
            glsl_120: Some(FRAGMENT_SHADER_UV[0]),
            glsl_150: Some(FRAGMENT_SHADER_UV[1]),
            .. gfx::ShaderSource::empty()
        };

        let program_uv = device.link_program(
            vertex.choose(shader_model).unwrap(),
            fragment.choose(shader_model).unwrap())
            .unwrap();

        let buffer_pos = device.create_buffer(
            POS_COMPONENTS * BUFFER_SIZE,
            gfx::BufferUsage::Dynamic);
        let buffer_uv = device.create_buffer(
            UV_COMPONENTS * BUFFER_SIZE,
            gfx::BufferUsage::Dynamic);

        let mut mesh = gfx::Mesh::new(BUFFER_SIZE as u32);
        mesh.attributes.extend(gfx::VertexFormat::generate(
            None::<&PositionFormat>,
            buffer_pos.raw()
        ).into_iter());

        // Reuse parameters from `mesh`.
        let mut mesh_uv = mesh.clone();
        mesh_uv.attributes.extend(gfx::VertexFormat::generate(
            None::<&TexCoordsFormat>,
            buffer_uv.raw()
        ).into_iter());

        let params = Params {
            color: [1.0; 4],
        };
        let mut batch = gfx::batch::OwnedBatch::new(mesh, program, params)
            .unwrap();

        let sampler = device.create_sampler(
            gfx::tex::SamplerInfo::new(
                gfx::tex::FilterMethod::Trilinear,
                gfx::tex::WrapMode::Clamp)
        );

        // Create a dummy texture
        let texture_info = gfx::tex::TextureInfo {
            width: 1,
            height: 1,
            depth: 1,
            levels: 1,
            kind: gfx::tex::TextureKind::Texture2D,
            format: gfx::tex::RGBA8,
        };
        let image_info = texture_info.to_image_info();
        let texture = device.create_texture(texture_info)
            .unwrap();
        device.update_texture(&texture, &image_info,
                &[0x20u8, 0xA0u8, 0xC0u8, 0x00u8])
            .unwrap();
        let params_uv = ParamsUV {
            color: [1.0; 4],
            s_texture: (texture, Some(sampler))
        };
        let mut batch_uv = gfx::batch::OwnedBatch::new(
            mesh_uv, program_uv, params_uv)
            .unwrap();

        // Disable culling.
        batch.state.primitive.method =
            gfx::state::RasterMethod::Fill(gfx::state::CullMode::Nothing);
        batch_uv.state.primitive.method =
            gfx::state::RasterMethod::Fill(gfx::state::CullMode::Nothing);

        Gfx2d {
            buffer_pos: buffer_pos,
            buffer_uv: buffer_uv,
            batch: batch,
            batch_uv: batch_uv,
        }
    }

    /// Renders graphics to a Gfx renderer.
    pub fn draw<C, F>(
        &mut self,
        renderer: &mut gfx::Renderer<C::CommandBuffer>,
        frame: &gfx::Frame<C::Resources>,
        mut f: F
    )
        where
            C: gfx::Device<Resources = GlResources>,
            F: FnMut(Context, &mut GfxGraphics<C>)
    {
        let ref mut g = GfxGraphics::new(
            renderer,
            frame,
            self
        );
        let c = Context::abs(
            frame.width as f64,
            frame.height as f64
        );
        g.enable_alpha_blend();
        f(c, g);
        g.disable_alpha_blend();
    }
}

/// Used for rendering 2D graphics.
pub struct GfxGraphics<'a, C>
    where
        C: 'a + gfx::Device,
        <C as gfx::Device>::CommandBuffer: 'a,
        <C as gfx::Device>::Resources: 'a,
        <<C as gfx::device::Device>::Resources as gfx::device::Resources>::Sampler: 'a,
        <<C as gfx::device::Device>::Resources as gfx::device::Resources>::Texture: 'a,
        <<C as gfx::device::Device>::Resources as gfx::device::Resources>::Surface: 'a,
        <<C as gfx::device::Device>::Resources as gfx::device::Resources>::FrameBuffer: 'a,
        <<C as gfx::device::Device>::Resources as gfx::device::Resources>::Program: 'a,
        <<C as gfx::device::Device>::Resources as gfx::device::Resources>::Shader: 'a,
        <<C as gfx::device::Device>::Resources as gfx::device::Resources>::ArrayBuffer: 'a,
        <<C as gfx::device::Device>::Resources as gfx::device::Resources>::Buffer: 'a
{
    renderer: &'a mut gfx::Renderer<C::CommandBuffer>,
    frame: &'a gfx::Frame<C::Resources>,
    g2d: &'a mut Gfx2d,
}

impl<'a, C> GfxGraphics<'a, C>
    where
        C: gfx::Device<Resources = GlResources>
{
    /// Creates a new object for rendering 2D graphics.
    pub fn new(renderer: &'a mut gfx::Renderer<C::CommandBuffer>,
               frame: &'a gfx::Frame<C::Resources>,
               g2d: &'a mut Gfx2d) -> GfxGraphics<'a, C> {
        GfxGraphics {
            renderer: renderer,
            frame: frame,
            g2d: g2d,
        }
    }

    /// Returns true if texture has alpha channel.
    pub fn has_texture_alpha(&self, texture: &Texture<C>) -> bool {
        use gfx::tex::Components::RGBA;

        texture.handle.get_info().format.get_components() == Some(RGBA)
    }

    /// Enabled alpha blending.
    pub fn enable_alpha_blend(&mut self) {
        use std::default::Default;
        use gfx::state::InverseFlag::{Normal, Inverse};
        use gfx::state::Factor;
        use gfx::state::BlendValue::SourceAlpha;

        let blend = gfx::state::Blend {
            value: [1.0, 1.0, 1.0, 1.0],
            color: gfx::state::BlendChannel {
                    equation: gfx::state::Equation::Add,
                    source: Factor(Normal, SourceAlpha),
                    destination: Factor(Inverse, SourceAlpha)
                },
            alpha: Default::default()
        };

        self.g2d.batch.state.blend = Some(blend);
        self.g2d.batch_uv.state.blend = Some(blend);
    }

    /// Disables alpha blending.
    pub fn disable_alpha_blend(&mut self) {
        self.g2d.batch.state.blend = None;
        self.g2d.batch_uv.state.blend = None;
    }
}

impl<'a, C> Graphics
for GfxGraphics<'a, C>
    where
        C: gfx::Device<Resources = GlResources>,
        C::Resources: 'a,
{
    type Texture = Texture<C>;

    fn clear(&mut self, color: [f32; 4]) {
        let &mut GfxGraphics {
            ref mut renderer,
            frame,
            ..
        } = self;
        renderer.clear(
                gfx::ClearData {
                    color: color,
                    depth: 0.0,
                    stencil: 0,
                },
                gfx::COLOR,
                frame
            );
    }

    fn tri_list<F>(
        &mut self,
        _draw_state: &DrawState,
        color: &[f32; 4],
        mut f: F
    )
        where F: FnMut(&mut FnMut(&[f32]))
    {
        let &mut GfxGraphics {
            ref mut renderer,
            ref frame,
            g2d: &mut Gfx2d {
                ref mut buffer_pos,
                ref mut batch,
                ..
            },
        } = self;

        batch.param.color = *color;

        f(&mut |vertices: &[f32]| {
            renderer.update_buffer_vec(buffer_pos.clone(), vertices, 0);

            let n = vertices.len() / POS_COMPONENTS;
            batch.slice = gfx::Slice {
                    prim_type: gfx::PrimitiveType::TriangleList,
                    start: 0,
                    end: n as u32,
                    kind: gfx::SliceKind::Vertex
                };
            let _ = renderer.draw(batch, *frame);
        })
    }

    fn tri_list_uv<F>(
        &mut self,
        _draw_state: &DrawState,
        color: &[f32; 4],
        texture: &<Self as Graphics>::Texture,
        mut f: F
    )
        where F: FnMut(&mut FnMut(&[f32], &[f32]))
    {
        let &mut GfxGraphics {
            ref mut renderer,
            ref frame,
            g2d: &mut Gfx2d {
                ref mut buffer_pos,
                ref mut buffer_uv,
                ref mut batch_uv,
                ..
            },
        } = self;

        batch_uv.param.s_texture.0 = texture.handle;
        batch_uv.param.color = *color;

        f(&mut |vertices: &[f32], texture_coords: &[f32]| {
            assert_eq!(
                vertices.len() * UV_COMPONENTS,
                texture_coords.len() * POS_COMPONENTS
            );
            renderer.update_buffer_vec(buffer_pos.clone(), vertices, 0);
            renderer.update_buffer_vec(buffer_uv.clone(), texture_coords, 0);

            let n = vertices.len() / POS_COMPONENTS;
            batch_uv.slice = gfx::Slice {
                    prim_type: gfx::PrimitiveType::TriangleList,
                    start: 0,
                    end: n as u32,
                    kind: gfx::SliceKind::Vertex
                };
            let _ = renderer.draw(batch_uv, *frame);
        })
    }
}
<|endoftext|>


<commit_before>// The MIT License (MIT)
//
// Copyright (c) 2013 Jeremy Letang (letang.jeremy@gmail.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:

// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

#![allow(dead_code, non_camel_case_types)]

use pa::error::Error;
use libc::{c_char, c_double, c_ulong, c_void};
use std::ffi::{CStr, CString};

use pa::{
    StreamInfo,
    Time,
    StreamCallbackResult
};

pub type DeviceIndex = i32;
pub type HostApiIndex = i32;

// Sample format
pub type SampleFormat = u64;
pub const PA_FLOAT_32        : SampleFormat = 0x00000001;
pub const PA_INT_32          : SampleFormat = 0x00000002;
pub const PA_INT_24          : SampleFormat = 0x00000004;
pub const PA_INT_16          : SampleFormat = 0x00000008;
pub const PA_INT_8           : SampleFormat = 0x00000010;
pub const PA_UINT_8          : SampleFormat = 0x00000020;
pub const PA_CUSTOM_FORMAT   : SampleFormat = 0x00010000;
pub const PA_NON_INTERLEAVED : SampleFormat = 0x80000000;

// Stream flags
pub type StreamFlags = u64;
pub const PA_NO_FLAG                                    : StreamFlags = 0;
pub const PA_CLIP_OFF                                   : StreamFlags = 0x00000001;
pub const PA_DITHER_OFF                                 : StreamFlags = 0x00000002;
pub const PA_NEVER_DROP_INPUT                           : StreamFlags = 0x00000004;
pub const PA_PRIME_OUTPUT_BUFFERS_USING_STREAM_CALLBACK : StreamFlags = 0x00000008;
pub const PA_PLATFORM_SPECIFIC_FLAGS                    : StreamFlags = 0xFFFF0000;

// Stream callback falgs.
pub type StreamCallbackFlags = u64;
pub const INPUT_UNDERFLOW  : StreamCallbackFlags = 0x00000001;
pub const INPUT_OVERFLOW   : StreamCallbackFlags = 0x00000002;
pub const OUTPUT_UNDERFLOW : StreamCallbackFlags = 0x00000004;
pub const OUTPUT_OVERFLOW  : StreamCallbackFlags = 0x00000008;
pub const PRIMING_OUTPUT   : StreamCallbackFlags = 0x00000010;

/// Unchanging unique identifiers for each supported host API
pub type HostApiTypeId = i32;
pub const PA_IN_DEVELOPMENT: HostApiTypeId = 0;
pub const PA_DIRECT_SOUND: HostApiTypeId = 1;
pub const PA_MME: HostApiTypeId = 2;
pub const PA_ASIO: HostApiTypeId = 3;
pub const PA_SOUND_MANAGER: HostApiTypeId = 4;
pub const PA_CORE_AUDIO: HostApiTypeId = 5;
pub const PA_OSS: HostApiTypeId = 7;
pub const PA_ALSA: HostApiTypeId = 8;
pub const PA_AL: HostApiTypeId = 9;
pub const PA_BE_OS: HostApiTypeId = 10;
pub const PA_WDMKS: HostApiTypeId = 11;
pub const PA_JACK: HostApiTypeId = 12;
pub const PA_WASAPI: HostApiTypeId = 13;
pub const PA_AUDIO_SCIENCE_HPI: HostApiTypeId = 14;

#[doc(hidden)]
#[derive(Clone, Copy, Debug)]
#[repr(C)]
pub struct C_PaStreamCallbackTimeInfo {
    pub input_buffer_adc_time: Time,
    pub current_time: Time,
    pub output_buffer_dac_time: Time
}

pub type C_PaStream = c_void;

#[allow(raw_pointer_derive)]
#[derive(Clone, Copy)]
#[repr(C)]
pub struct C_PaStreamParameters {
    pub device : DeviceIndex,
    pub channel_count : i32,
    pub sample_format : SampleFormat,
    pub suggested_latency : Time,
    pub host_api_specific_stream_info : *mut c_void
}

#[repr(C)]
pub struct C_PaDeviceInfo {
    pub struct_version: i32,
    pub name: *const c_char,
    pub host_api: HostApiIndex,
    pub max_input_channels: i32,
    pub max_output_channels: i32,
    pub default_low_input_latency: Time,
    pub default_low_output_latency: Time,
    pub default_high_input_latency: Time,
    pub default_high_output_latency: Time,
    pub default_sample_rate: c_double
}

#[repr(C)]
pub struct C_PaHostErrorInfo {
    pub error_code: u32,
    pub error_text: *const c_char
}

#[repr(C)]
pub struct C_PaHostApiInfo {
    pub struct_version: i32,
    pub host_type: i32,
    pub name: *const c_char,
    pub device_count: i32,
    pub default_input_device: i32,
    pub default_output_device: i32
}

pub type C_PaStreamCallbackFn =
    extern "C" fn(*const c_void,
                  *mut c_void,
                  c_ulong,
                  *const C_PaStreamCallbackTimeInfo,
                  StreamCallbackFlags,
                  *mut c_void) -> StreamCallbackResult;

extern "C" {

    /// PortAudio portable API

    pub fn Pa_GetVersion() -> i32;
    pub fn Pa_GetVersionText() -> *const c_char;
    pub fn Pa_GetErrorText(errorCode : Error) -> *const c_char;
    pub fn Pa_Initialize() -> Error;
    pub fn Pa_Terminate() -> Error;
    pub fn Pa_GetHostApiCount() -> HostApiIndex;
    pub fn Pa_GetDefaultHostApi() -> HostApiIndex;
    pub fn Pa_GetHostApiInfo(hostApi : HostApiIndex) -> *const C_PaHostApiInfo;
    pub fn Pa_HostApiTypeIdToHostApiIndex(type_id : HostApiTypeId) -> HostApiIndex;
    pub fn Pa_HostApiDeviceIndexToDeviceIndex(hostApi : HostApiIndex, hostApiDeviceIndex : i32) -> DeviceIndex;
    pub fn Pa_GetLastHostErrorInfo() -> *const C_PaHostErrorInfo;
    pub fn Pa_GetDeviceCount() -> DeviceIndex;
    pub fn Pa_GetDefaultInputDevice() -> DeviceIndex;
    pub fn Pa_GetDefaultOutputDevice() -> DeviceIndex;
    pub fn Pa_GetDeviceInfo(device : DeviceIndex) -> *const C_PaDeviceInfo;
    pub fn Pa_IsFormatSupported(input_parameters : *const C_PaStreamParameters, outputParameters : *const C_PaStreamParameters, sampleRate : c_double) -> Error;
    pub fn Pa_GetSampleSize(format : SampleFormat) -> i32;
    pub fn Pa_Sleep(msec : i32) -> ();
    pub fn Pa_OpenStream(stream : *mut *mut C_PaStream,
                         inputParameters : *const C_PaStreamParameters,
                         outputParameters : *const C_PaStreamParameters,
                         sampleRate : c_double,
                         framesPerBuffer : u32,
                         streamFlags : StreamFlags,
                         streamCallback : Option<C_PaStreamCallbackFn>,
                         userData : *mut c_void)
                         -> Error;
    pub fn Pa_OpenDefaultStream(stream : *mut *mut C_PaStream,
                                numInputChannels : i32,
                                numOutputChannels : i32,
                                sampleFormat : SampleFormat,
                                sampleRate : c_double,
                                framesPerBuffer : u32,
                                streamCallback : Option<C_PaStreamCallbackFn>,
                                userData : *mut c_void)
                                -> Error;
    pub fn Pa_CloseStream(stream : *mut C_PaStream) -> Error;
    //pub fn Pa_SetStreamFinishedCallback (stream : *PaStream, PaStreamFinishedCallback *streamFinishedCallback) -> Error;
    pub fn Pa_StartStream(stream : *mut C_PaStream) -> i32;
    pub fn Pa_StopStream(stream : *mut C_PaStream) -> i32;
    pub fn Pa_AbortStream(stream : *mut C_PaStream) -> i32;
    pub fn Pa_IsStreamStopped(stream : *mut C_PaStream) -> i32;
    pub fn Pa_IsStreamActive(stream : *mut C_PaStream) -> i32;
    pub fn Pa_GetStreamInfo(stream : *mut C_PaStream) -> *const StreamInfo;
    pub fn Pa_GetStreamTime(stream : *mut C_PaStream) -> Time;
    pub fn Pa_GetStreamCpuLoad(stream : *mut C_PaStream) -> c_double;
    pub fn Pa_ReadStream(stream : *mut C_PaStream, buffer : *mut c_void, frames : u32) -> i32;
    pub fn Pa_WriteStream(stream : *mut C_PaStream, buffer : *mut c_void, frames : u32) -> i32;
    pub fn Pa_GetStreamReadAvailable(stream : *mut C_PaStream) -> i64;
    pub fn Pa_GetStreamWriteAvailable(stream : *mut C_PaStream) -> i64;

    // PortAudio Specific ASIO
    pub fn PaAsio_GetAvailableBufferSizes(device : DeviceIndex, minBufferSizeFrames : *mut i32, maxBufferSizeFrames : *mut i32, preferredBufferSizeFrames : *mut i32, granularity : *mut i32) -> Error;
    pub fn PaAsio_GetInputChannelName(device : DeviceIndex, channelIndex : i32, channelName : *mut *const c_char) -> Error;
    pub fn PaAsio_GetOutputChannelName(device : DeviceIndex, channelIndex : i32, channelName : *mut *const c_char) -> Error;
    pub fn PaAsio_SetStreamSampleRate(stream : *mut C_PaStream, sampleRate : c_double) -> Error;

    // PortAudio Specific MAC_CORE
    pub fn PaMacCore_GetStreamInputDevice(s : *mut C_PaStream) -> DeviceIndex;
    pub fn PaMacCore_GetStreamOutputDevice(s : *mut C_PaStream) -> DeviceIndex;
    // pub fn PaMacCore_GetChannelName (int device, int channelIndex, bool intput) -> *c_char
    pub fn PaMacCore_GetBufferSizeRange(device : DeviceIndex, minBufferSizeFrames : *mut u32, maxBufferSizeFrames : *mut u32) -> Error;
    //pub fn PaMacCore_SetupStreamInfo(PaMacCoreStreamInfo *data, unsigned long flags) -> ();
    //pub fn PaMacCore_SetupChannelMap(PaMacCoreStreamInfo *data, const SInt32 *const channelMap, unsigned long channelMapSize) -> ();
}

/// A function to convert C strings to Rust strings
pub fn c_str_to_string<'a>(c_str: &'a *const c_char) -> String {
    unsafe {
        String::from_utf8_lossy(CStr::from_ptr(*c_str).to_bytes()).into_owned()
    }
}

/// A function to convert Rust strings to C strings
pub fn string_to_c_str(rust_str: &String) -> *const c_char {
    match CString::new(rust_str.as_bytes()) {
        Ok(c_string) => c_string.as_ptr(),
        Err(err) => panic!(err),
    }
}
<commit_msg>Change string conversion functions to str conversions<commit_after>// The MIT License (MIT)
//
// Copyright (c) 2013 Jeremy Letang (letang.jeremy@gmail.com)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:

// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

#![allow(dead_code, non_camel_case_types)]

use pa::error::Error;
use libc::{c_char, c_double, c_ulong, c_void};

use pa::{
    StreamInfo,
    Time,
    StreamCallbackResult
};

pub type DeviceIndex = i32;
pub type HostApiIndex = i32;

// Sample format
pub type SampleFormat = u64;
pub const PA_FLOAT_32        : SampleFormat = 0x00000001;
pub const PA_INT_32          : SampleFormat = 0x00000002;
pub const PA_INT_24          : SampleFormat = 0x00000004;
pub const PA_INT_16          : SampleFormat = 0x00000008;
pub const PA_INT_8           : SampleFormat = 0x00000010;
pub const PA_UINT_8          : SampleFormat = 0x00000020;
pub const PA_CUSTOM_FORMAT   : SampleFormat = 0x00010000;
pub const PA_NON_INTERLEAVED : SampleFormat = 0x80000000;

// Stream flags
pub type StreamFlags = u64;
pub const PA_NO_FLAG                                    : StreamFlags = 0;
pub const PA_CLIP_OFF                                   : StreamFlags = 0x00000001;
pub const PA_DITHER_OFF                                 : StreamFlags = 0x00000002;
pub const PA_NEVER_DROP_INPUT                           : StreamFlags = 0x00000004;
pub const PA_PRIME_OUTPUT_BUFFERS_USING_STREAM_CALLBACK : StreamFlags = 0x00000008;
pub const PA_PLATFORM_SPECIFIC_FLAGS                    : StreamFlags = 0xFFFF0000;

// Stream callback falgs.
pub type StreamCallbackFlags = u64;
pub const INPUT_UNDERFLOW  : StreamCallbackFlags = 0x00000001;
pub const INPUT_OVERFLOW   : StreamCallbackFlags = 0x00000002;
pub const OUTPUT_UNDERFLOW : StreamCallbackFlags = 0x00000004;
pub const OUTPUT_OVERFLOW  : StreamCallbackFlags = 0x00000008;
pub const PRIMING_OUTPUT   : StreamCallbackFlags = 0x00000010;

/// Unchanging unique identifiers for each supported host API
pub type HostApiTypeId = i32;
pub const PA_IN_DEVELOPMENT: HostApiTypeId = 0;
pub const PA_DIRECT_SOUND: HostApiTypeId = 1;
pub const PA_MME: HostApiTypeId = 2;
pub const PA_ASIO: HostApiTypeId = 3;
pub const PA_SOUND_MANAGER: HostApiTypeId = 4;
pub const PA_CORE_AUDIO: HostApiTypeId = 5;
pub const PA_OSS: HostApiTypeId = 7;
pub const PA_ALSA: HostApiTypeId = 8;
pub const PA_AL: HostApiTypeId = 9;
pub const PA_BE_OS: HostApiTypeId = 10;
pub const PA_WDMKS: HostApiTypeId = 11;
pub const PA_JACK: HostApiTypeId = 12;
pub const PA_WASAPI: HostApiTypeId = 13;
pub const PA_AUDIO_SCIENCE_HPI: HostApiTypeId = 14;

#[doc(hidden)]
#[derive(Clone, Copy, Debug)]
#[repr(C)]
pub struct C_PaStreamCallbackTimeInfo {
    pub input_buffer_adc_time: Time,
    pub current_time: Time,
    pub output_buffer_dac_time: Time
}

pub type C_PaStream = c_void;

#[allow(raw_pointer_derive)]
#[derive(Clone, Copy)]
#[repr(C)]
pub struct C_PaStreamParameters {
    pub device : DeviceIndex,
    pub channel_count : i32,
    pub sample_format : SampleFormat,
    pub suggested_latency : Time,
    pub host_api_specific_stream_info : *mut c_void
}

#[allow(raw_pointer_derive)]
#[derive(Clone, Copy, Debug)]
#[repr(C)]
pub struct C_PaDeviceInfo {
    pub struct_version: i32,
    pub name: *const c_char,
    pub host_api: HostApiIndex,
    pub max_input_channels: i32,
    pub max_output_channels: i32,
    pub default_low_input_latency: Time,
    pub default_low_output_latency: Time,
    pub default_high_input_latency: Time,
    pub default_high_output_latency: Time,
    pub default_sample_rate: c_double
}

#[allow(raw_pointer_derive)]
#[derive(Clone, Copy, Debug)]
#[repr(C)]
pub struct C_PaHostErrorInfo {
    pub error_code: u32,
    pub error_text: *const c_char
}

#[allow(raw_pointer_derive)]
#[derive(Clone, Copy, Debug)]
#[repr(C)]
pub struct C_PaHostApiInfo {
    pub struct_version: i32,
    pub host_type: i32,
    pub name: *const c_char,
    pub device_count: i32,
    pub default_input_device: i32,
    pub default_output_device: i32
}

pub type C_PaStreamCallbackFn =
    extern "C" fn(*const c_void,
                  *mut c_void,
                  c_ulong,
                  *const C_PaStreamCallbackTimeInfo,
                  StreamCallbackFlags,
                  *mut c_void) -> StreamCallbackResult;

extern "C" {

    /// PortAudio portable API

    pub fn Pa_GetVersion() -> i32;
    pub fn Pa_GetVersionText() -> *const c_char;
    pub fn Pa_GetErrorText(errorCode : Error) -> *const c_char;
    pub fn Pa_Initialize() -> Error;
    pub fn Pa_Terminate() -> Error;
    pub fn Pa_GetHostApiCount() -> HostApiIndex;
    pub fn Pa_GetDefaultHostApi() -> HostApiIndex;
    pub fn Pa_GetHostApiInfo(hostApi : HostApiIndex) -> *const C_PaHostApiInfo;
    pub fn Pa_HostApiTypeIdToHostApiIndex(type_id : HostApiTypeId) -> HostApiIndex;
    pub fn Pa_HostApiDeviceIndexToDeviceIndex(hostApi : HostApiIndex, hostApiDeviceIndex : i32) -> DeviceIndex;
    pub fn Pa_GetLastHostErrorInfo() -> *const C_PaHostErrorInfo;
    pub fn Pa_GetDeviceCount() -> DeviceIndex;
    pub fn Pa_GetDefaultInputDevice() -> DeviceIndex;
    pub fn Pa_GetDefaultOutputDevice() -> DeviceIndex;
    pub fn Pa_GetDeviceInfo(device : DeviceIndex) -> *const C_PaDeviceInfo;
    pub fn Pa_IsFormatSupported(input_parameters : *const C_PaStreamParameters, outputParameters : *const C_PaStreamParameters, sampleRate : c_double) -> Error;
    pub fn Pa_GetSampleSize(format : SampleFormat) -> i32;
    pub fn Pa_Sleep(msec : i32) -> ();
    pub fn Pa_OpenStream(stream : *mut *mut C_PaStream,
                         inputParameters : *const C_PaStreamParameters,
                         outputParameters : *const C_PaStreamParameters,
                         sampleRate : c_double,
                         framesPerBuffer : u32,
                         streamFlags : StreamFlags,
                         streamCallback : Option<C_PaStreamCallbackFn>,
                         userData : *mut c_void)
                         -> Error;
    pub fn Pa_OpenDefaultStream(stream : *mut *mut C_PaStream,
                                numInputChannels : i32,
                                numOutputChannels : i32,
                                sampleFormat : SampleFormat,
                                sampleRate : c_double,
                                framesPerBuffer : u32,
                                streamCallback : Option<C_PaStreamCallbackFn>,
                                userData : *mut c_void)
                                -> Error;
    pub fn Pa_CloseStream(stream : *mut C_PaStream) -> Error;
    //pub fn Pa_SetStreamFinishedCallback (stream : *PaStream, PaStreamFinishedCallback *streamFinishedCallback) -> Error;
    pub fn Pa_StartStream(stream : *mut C_PaStream) -> i32;
    pub fn Pa_StopStream(stream : *mut C_PaStream) -> i32;
    pub fn Pa_AbortStream(stream : *mut C_PaStream) -> i32;
    pub fn Pa_IsStreamStopped(stream : *mut C_PaStream) -> i32;
    pub fn Pa_IsStreamActive(stream : *mut C_PaStream) -> i32;
    pub fn Pa_GetStreamInfo(stream : *mut C_PaStream) -> *const StreamInfo;
    pub fn Pa_GetStreamTime(stream : *mut C_PaStream) -> Time;
    pub fn Pa_GetStreamCpuLoad(stream : *mut C_PaStream) -> c_double;
    pub fn Pa_ReadStream(stream : *mut C_PaStream, buffer : *mut c_void, frames : u32) -> i32;
    pub fn Pa_WriteStream(stream : *mut C_PaStream, buffer : *mut c_void, frames : u32) -> i32;
    pub fn Pa_GetStreamReadAvailable(stream : *mut C_PaStream) -> i64;
    pub fn Pa_GetStreamWriteAvailable(stream : *mut C_PaStream) -> i64;

    // PortAudio Specific ASIO
    pub fn PaAsio_GetAvailableBufferSizes(device : DeviceIndex, minBufferSizeFrames : *mut i32, maxBufferSizeFrames : *mut i32, preferredBufferSizeFrames : *mut i32, granularity : *mut i32) -> Error;
    pub fn PaAsio_GetInputChannelName(device : DeviceIndex, channelIndex : i32, channelName : *mut *const c_char) -> Error;
    pub fn PaAsio_GetOutputChannelName(device : DeviceIndex, channelIndex : i32, channelName : *mut *const c_char) -> Error;
    pub fn PaAsio_SetStreamSampleRate(stream : *mut C_PaStream, sampleRate : c_double) -> Error;

    // PortAudio Specific MAC_CORE
    pub fn PaMacCore_GetStreamInputDevice(s : *mut C_PaStream) -> DeviceIndex;
    pub fn PaMacCore_GetStreamOutputDevice(s : *mut C_PaStream) -> DeviceIndex;
    // pub fn PaMacCore_GetChannelName (int device, int channelIndex, bool intput) -> *c_char
    pub fn PaMacCore_GetBufferSizeRange(device : DeviceIndex, minBufferSizeFrames : *mut u32, maxBufferSizeFrames : *mut u32) -> Error;
    //pub fn PaMacCore_SetupStreamInfo(PaMacCoreStreamInfo *data, unsigned long flags) -> ();
    //pub fn PaMacCore_SetupChannelMap(PaMacCoreStreamInfo *data, const SInt32 *const channelMap, unsigned long channelMapSize) -> ();
}

/// A function to convert C `*const char` arrays into Rust `&'a str`s.
pub fn c_str_to_str<'a>(c_str: *const c_char) -> Result<&'a str, ::std::str::Utf8Error> {
    unsafe {
        ::std::ffi::CStr::from_ptr(c_str).to_str()
    }
}

/// A function to convert Rust strings to C strings
pub fn str_to_c_str(rust_str: &str) -> *const c_char {
    rust_str.as_ptr() as *const _
}
<|endoftext|>


<commit_before>use std::thread::sleep;
use std::time::Duration;
use super::interconnect::Interconnect;
use super::interconnect::END_RESERVED;

const INSTRUCTION_SIZE: u16 = 2;
const TIMER_DELAY: u64 = 16;

#[derive(Default, Debug)]
pub struct Cpu {
    // Interconnect is used to control system resources like rom and memory.
    interconnect: Interconnect,

    // Program counter.
    pc: u16,

    // The function call stack.
    stack: [u16; 16],

    // Stack pointer.
    sp: u8,

    // General purpose registers v0-vf.
    v0: u8,
    v1: u8,
    v2: u8,
    v3: u8,
    v4: u8,
    v5: u8,
    v6: u8,
    v7: u8,
    v8: u8,
    v9: u8,
    va: u8,
    vb: u8,
    vc: u8,
    vd: u8,
    ve: u8,
    vf: u8,

    // Address register "I".
    i: u16,

    // Timer and sound registers.
    dt: u8,
    st: u8,
}

impl Cpu {
    pub fn new(interconnect: Interconnect) -> Cpu {
        Cpu {
            interconnect: interconnect,
            pc: END_RESERVED as u16,
            ..Cpu::default()
        }
    }

    /// Execute instructions from ram.
    pub fn run(&mut self) {
        loop {
            let word = self.interconnect.read_word(self.pc);

            // Execute until the subroutine ends.
            if self.execute_instruction(word) {
                break
            }
        }
    }

    #[inline(always)]
    fn execute_instruction(&mut self, instr: u16) -> bool {
        let opcode = (instr >> 12) as u8;
        let mut ret: bool = false;

        match opcode {
            0x0 => {
                let identifier = ((instr << 8) >> 8) as u8;

                match identifier {
                    0xE0 => {
                        // 00E0 - CLS
                        // Clears the screen.

                        self.interconnect.clear_display();
                    },
                    0xEE => {
                        // 00EE - RET
                        // Returns from a subroutine.

                        self.sp -= 1;
                        self.pc = self.stack[self.sp as usize];
                        self.stack[self.sp as usize] = 0;

                        // Break out of the execution loop for the current
                        // subroutine.
                        ret = true;
                    },
                    _ => {
                        println!("cpu: {:#?}", self);
                        panic!("Found unknown identifier at instruction: {:#x}, addr: {:#x}", instr, self.pc);
                    },
                }
            },
            0x2 => {
                // 2NNN - CALL NNN
                //
                // Calls subroutine at NNN.

                let addr = ((instr << 4) >> 4) as u16;

                // Add the current program counter to the call stack.
                self.stack[self.sp as usize] = self.pc;
                self.sp += 1;

                // Set the program counter to the call address begin executing
                // the subroutine.
                self.pc = addr;
                self.run();
            },
            0x6 => {
                // 6XNN - LD VX, NN
                //
                // Sets VX to NN.

                let regx = ((instr << 4) >> 12) as u8;
                let byte = ((instr << 8) >> 8) as u8;
                self.set_reg(regx, byte);
            },
            0x7 => {
                // 7XNN - ADD VX, NN
                //
                // Adds NN to VX.

                let regx = ((instr << 4) >> 12) as u8;
                let byte = ((instr << 8) >> 8) as u8;

                let result = self.get_reg(regx) + byte;
                self.set_reg(regx, result);
            },
            0xa => {
                // ANNN - LD I, NNN
                //
                // Sets I to the address NNN.

                let addr = ((instr << 4) >> 4) as u16;
                self.i = addr;
            },
            0xd => {
                // DXYN - DRW VX, VY, N
                //
                // Sprites stored in memory at location in index register (I),
                // 8bits wide. Wraps around the screen. If when drawn, clears a
                // pixel, register VF is set to 1 otherwise it is zero.
                //
                // All drawing is XOR drawing (i.e. it toggles the screen
                // pixels). Sprites are drawn starting at position VX, VY. N is
                // the number of 8bit rows that need to be drawn. If N is
                // greater than 1, second line continues at position VX, VY+1,
                // and so on.

                let regx = ((instr << 4) >> 12) as u8;
                let regy = ((instr << 8) >> 12) as u8;
                let nibble = ((instr << 12) >> 12) as usize;

                // Read N (nibble) bytes out out of memory starting at address
                // register I into our sprite.
                let mut sprite = vec![0 as u8; nibble];
                for i in 0..nibble {
                    sprite[i] = self.interconnect.ram[self.i as usize + i];
                }

                // Get screen coordinates from the requested registers.
                let x = self.get_reg(regx);
                let y = self.get_reg(regy);

                // Draw the sprite and store collision detection results in vf.
                self.vf = self.interconnect.draw(x as usize, y as usize, sprite);
            },
            0xf => {
                let regx = ((instr << 4) >> 12) as u8;
                let identifier = ((instr << 8) >> 8) as u8;

                match identifier {
                    0x07 => {
                        // FX07 - LD VX, DT
                        //
                        // Sets VX to the value of the delay timer.

                        let dt = self.dt;
                        self.set_reg(regx, dt);
                    },
                    0x15 => {
                        // FX15 - LD DT, VX
                        //
                        // Sets the delay timer to VX.

                        let x: u8 = self.get_reg(regx);
                        self.dt = x;
                    },
                    0x29 => {
                        // FX29 - LD F, VX
                        //
                        // Sets I to the location of the sprite for the
                        // character in VX. Characters 0-F (in hexadecimal) are
                        // represented by a 4x5 font.

                        let x: u8 = self.get_reg(regx);
                        self.i = self.interconnect.get_font(x);
                    },
                    0x33 => {
                        // FX33 - LD B, VX
                        //
                        // Stores the Binary-coded decimal representation of VX,
                        // with the most significant of three digits at the
                        // address in I, the middle digit at I plus 1, and the
                        // least significant digit at I plus 2. (In other words,
                        // take the decimal representation of VX, place the
                        // hundreds digit in memory at location in I, the tens
                        // digit at location I+1, and the ones digit at
                        // location I+2.)

                        const DECIMAL_LENGTH: usize = 3;

                        let mut x: u8 = self.get_reg(regx);
                        let mut digits = vec![0 as u8; DECIMAL_LENGTH];
                        let mut digit_count: usize = 0;

                        // Organize the digits in the decimal into a slice.
                        while x > 0 {
                            digit_count += 1;
                            digits[DECIMAL_LENGTH - digit_count] = x % 10;
                            x /= 10;
                        }

                        // Set I, I+1, and I+3 to the values of the digits.
                        let i: usize = self.i as usize;
                        self.interconnect.ram[i] = digits[0];
                        self.interconnect.ram[i + 1] = digits[1];
                        self.interconnect.ram[i + 2] = digits[2];
                    },
                    0x65 => {
                        // FX65 - LD VX, [I]
                        //
                        // Fills V0 to VX with values from memory starting at
                        // address I.

                        let i: usize = self.i as usize;
                        let end_reg = (regx + 1) as usize;

                        for register in 0x0..end_reg {
                            let mem = self.interconnect.ram[i + register];
                            self.set_reg(register as u8, mem);
                        }
                    },
                    _ => {
                        println!("cpu: {:#?}", self);
                        panic!("Found unknown identifier at instruction: {:#x}, addr: {:#x}", instr, self.pc);
                    },
                }
            },
            _ => {
                println!("cpu: {:#?}", self);
                panic!("Found unknown opcode at instruction: {:#x}, addr: {:#x}", instr, self.pc);
            },
        }

        // Increment the program counter to the next instruction.
        if !ret {
            self.pc += INSTRUCTION_SIZE;
        }

        self.handle_timers();

        // By default the execution loop in not broken. True will be returned
        // only by a successful RET instruction is executed.
        ret
    }

    /// Handle the delay timer and play sounds.
    fn handle_timers(&mut self) {
        let dt_enabled = self.dt > 0;
        let st_enabled = self.st > 0;

        if dt_enabled || st_enabled {
            println!("SLEEPY TIME");
            sleep(Duration::from_millis(TIMER_DELAY));

            if dt_enabled {
                self.dt -= 1;
            }
            if st_enabled {
                self.st -= 1;
            }
        }
    }

    /// Gets the value at a specified register.
    fn get_reg(&mut self, reg: u8) -> u8 {
        match reg {
            0x0 => self.v0,
            0x1 => self.v1,
            0x2 => self.v2,
            0x3 => self.v3,
            0x4 => self.v4,
            0x5 => self.v5,
            0x6 => self.v6,
            0x7 => self.v7,
            0x8 => self.v8,
            0x9 => self.v9,
            0xa => self.va,
            0xb => self.vb,
            0xc => self.vc,
            0xd => self.vd,
            0xe => self.ve,
            0xf => self.vf,
            _ => {
                panic!("Cannot get unknown register: V{:X}", reg);
            }
        }
    }

    /// Sets the value of a general purpose register.
    fn set_reg(&mut self, reg: u8, byte: u8) {
        match reg {
            0x0 => self.v0 = byte,
            0x1 => self.v1 = byte,
            0x2 => self.v2 = byte,
            0x3 => self.v3 = byte,
            0x4 => self.v4 = byte,
            0x5 => self.v5 = byte,
            0x6 => self.v6 = byte,
            0x7 => self.v7 = byte,
            0x8 => self.v8 = byte,
            0x9 => self.v9 = byte,
            0xa => self.va = byte,
            0xb => self.vb = byte,
            0xc => self.vc = byte,
            0xd => self.vd = byte,
            0xe => self.ve = byte,
            0xf => self.vf = byte,
            _ => {
                panic!("Cannot set unknown register: V{:X}", reg);
            }
        }
    }
}
<commit_msg>Added 3XNN<commit_after>use std::thread::sleep;
use std::time::Duration;
use super::interconnect::Interconnect;
use super::interconnect::END_RESERVED;

const INSTRUCTION_SIZE: u16 = 2;
const TIMER_DELAY: u64 = 16;

#[derive(Default, Debug)]
pub struct Cpu {
    // Interconnect is used to control system resources like rom and memory.
    interconnect: Interconnect,

    // Program counter.
    pc: u16,

    // The function call stack.
    stack: [u16; 16],

    // Stack pointer.
    sp: u8,

    // General purpose registers v0-vf.
    v0: u8,
    v1: u8,
    v2: u8,
    v3: u8,
    v4: u8,
    v5: u8,
    v6: u8,
    v7: u8,
    v8: u8,
    v9: u8,
    va: u8,
    vb: u8,
    vc: u8,
    vd: u8,
    ve: u8,
    vf: u8,

    // Address register "I".
    i: u16,

    // Timer and sound registers.
    dt: u8,
    st: u8,
}

impl Cpu {
    pub fn new(interconnect: Interconnect) -> Cpu {
        Cpu {
            interconnect: interconnect,
            pc: END_RESERVED as u16,
            ..Cpu::default()
        }
    }

    /// Execute instructions from ram.
    pub fn run(&mut self) {
        loop {
            let word = self.interconnect.read_word(self.pc);

            // Execute until the subroutine ends.
            if self.execute_instruction(word) {
                break
            }
        }
    }

    #[inline(always)]
    fn execute_instruction(&mut self, instr: u16) -> bool {
        let opcode = (instr >> 12) as u8;
        let mut ret: bool = false;

        match opcode {
            0x0 => {
                let identifier = ((instr << 8) >> 8) as u8;

                match identifier {
                    0xE0 => {
                        // 00E0 - CLS
                        // Clears the screen.

                        self.interconnect.clear_display();
                    },
                    0xEE => {
                        // 00EE - RET
                        // Returns from a subroutine.

                        self.sp -= 1;
                        self.pc = self.stack[self.sp as usize];
                        self.stack[self.sp as usize] = 0;

                        // Break out of the execution loop for the current
                        // subroutine.
                        ret = true;
                    },
                    _ => {
                        println!("cpu: {:#?}", self);
                        panic!("Found unknown identifier at instruction: {:#x}, addr: {:#x}", instr, self.pc);
                    },
                }
            },
            0x2 => {
                // 2NNN - CALL NNN
                //
                // Calls subroutine at NNN.

                let addr = ((instr << 4) >> 4) as u16;

                // Add the current program counter to the call stack.
                self.stack[self.sp as usize] = self.pc;
                self.sp += 1;

                // Set the program counter to the call address begin executing
                // the subroutine.
                self.pc = addr;
                self.run();
            },
            0x3 => {
                // 3XNN - SE VX, NN
                //
                // Skips the next instruction if VX equals NN.

                let regx = ((instr << 4) >> 12) as u8;
                let byte = ((instr << 8) >> 8) as u8;
                let x = self.get_reg(regx);

                if x == byte {
                    self.pc += 2;
                }
            }
            0x6 => {
                // 6XNN - LD VX, NN
                //
                // Sets VX to NN.

                let regx = ((instr << 4) >> 12) as u8;
                let byte = ((instr << 8) >> 8) as u8;
                self.set_reg(regx, byte);
            },
            0x7 => {
                // 7XNN - ADD VX, NN
                //
                // Adds NN to VX.

                let regx = ((instr << 4) >> 12) as u8;
                let byte = ((instr << 8) >> 8) as u8;

                let result = self.get_reg(regx) + byte;
                self.set_reg(regx, result);
            },
            0xa => {
                // ANNN - LD I, NNN
                //
                // Sets I to the address NNN.

                let addr = ((instr << 4) >> 4) as u16;
                self.i = addr;
            },
            0xd => {
                // DXYN - DRW VX, VY, N
                //
                // Sprites stored in memory at location in index register (I),
                // 8bits wide. Wraps around the screen. If when drawn, clears a
                // pixel, register VF is set to 1 otherwise it is zero.
                //
                // All drawing is XOR drawing (i.e. it toggles the screen
                // pixels). Sprites are drawn starting at position VX, VY. N is
                // the number of 8bit rows that need to be drawn. If N is
                // greater than 1, second line continues at position VX, VY+1,
                // and so on.

                let regx = ((instr << 4) >> 12) as u8;
                let regy = ((instr << 8) >> 12) as u8;
                let nibble = ((instr << 12) >> 12) as usize;

                // Read N (nibble) bytes out out of memory starting at address
                // register I into our sprite.
                let mut sprite = vec![0 as u8; nibble];
                for i in 0..nibble {
                    sprite[i] = self.interconnect.ram[self.i as usize + i];
                }

                // Get screen coordinates from the requested registers.
                let x = self.get_reg(regx);
                let y = self.get_reg(regy);

                // Draw the sprite and store collision detection results in vf.
                self.vf = self.interconnect.draw(x as usize, y as usize, sprite);
            },
            0xf => {
                let regx = ((instr << 4) >> 12) as u8;
                let identifier = ((instr << 8) >> 8) as u8;

                match identifier {
                    0x07 => {
                        // FX07 - LD VX, DT
                        //
                        // Sets VX to the value of the delay timer.

                        let dt = self.dt;
                        self.set_reg(regx, dt);
                    },
                    0x15 => {
                        // FX15 - LD DT, VX
                        //
                        // Sets the delay timer to VX.

                        let x: u8 = self.get_reg(regx);
                        self.dt = x;
                    },
                    0x29 => {
                        // FX29 - LD F, VX
                        //
                        // Sets I to the location of the sprite for the
                        // character in VX. Characters 0-F (in hexadecimal) are
                        // represented by a 4x5 font.

                        let x: u8 = self.get_reg(regx);
                        self.i = self.interconnect.get_font(x);
                    },
                    0x33 => {
                        // FX33 - LD B, VX
                        //
                        // Stores the Binary-coded decimal representation of VX,
                        // with the most significant of three digits at the
                        // address in I, the middle digit at I plus 1, and the
                        // least significant digit at I plus 2. (In other words,
                        // take the decimal representation of VX, place the
                        // hundreds digit in memory at location in I, the tens
                        // digit at location I+1, and the ones digit at
                        // location I+2.)

                        const DECIMAL_LENGTH: usize = 3;

                        let mut x: u8 = self.get_reg(regx);
                        let mut digits = vec![0 as u8; DECIMAL_LENGTH];
                        let mut digit_count: usize = 0;

                        // Organize the digits in the decimal into a slice.
                        while x > 0 {
                            digit_count += 1;
                            digits[DECIMAL_LENGTH - digit_count] = x % 10;
                            x /= 10;
                        }

                        // Set I, I+1, and I+3 to the values of the digits.
                        let i: usize = self.i as usize;
                        self.interconnect.ram[i] = digits[0];
                        self.interconnect.ram[i + 1] = digits[1];
                        self.interconnect.ram[i + 2] = digits[2];
                    },
                    0x65 => {
                        // FX65 - LD VX, [I]
                        //
                        // Fills V0 to VX with values from memory starting at
                        // address I.

                        let i: usize = self.i as usize;
                        let end_reg = (regx + 1) as usize;

                        for register in 0x0..end_reg {
                            let mem = self.interconnect.ram[i + register];
                            self.set_reg(register as u8, mem);
                        }
                    },
                    _ => {
                        println!("cpu: {:#?}", self);
                        panic!("Found unknown identifier at instruction: {:#x}, addr: {:#x}", instr, self.pc);
                    },
                }
            },
            _ => {
                println!("cpu: {:#?}", self);
                panic!("Found unknown opcode at instruction: {:#x}, addr: {:#x}", instr, self.pc);
            },
        }

        // Increment the program counter to the next instruction.
        if !ret {
            self.pc += INSTRUCTION_SIZE;
        }

        self.handle_timers();

        // By default the execution loop in not broken. True will be returned
        // only by a successful RET instruction is executed.
        ret
    }

    /// Handle the delay timer and play sounds.
    fn handle_timers(&mut self) {
        let dt_enabled = self.dt > 0;
        let st_enabled = self.st > 0;

        if dt_enabled || st_enabled {
            println!("SLEEPY TIME");
            sleep(Duration::from_millis(TIMER_DELAY));

            if dt_enabled {
                self.dt -= 1;
            }
            if st_enabled {
                self.st -= 1;
            }
        }
    }

    /// Gets the value at a specified register.
    fn get_reg(&mut self, reg: u8) -> u8 {
        match reg {
            0x0 => self.v0,
            0x1 => self.v1,
            0x2 => self.v2,
            0x3 => self.v3,
            0x4 => self.v4,
            0x5 => self.v5,
            0x6 => self.v6,
            0x7 => self.v7,
            0x8 => self.v8,
            0x9 => self.v9,
            0xa => self.va,
            0xb => self.vb,
            0xc => self.vc,
            0xd => self.vd,
            0xe => self.ve,
            0xf => self.vf,
            _ => {
                panic!("Cannot get unknown register: V{:X}", reg);
            }
        }
    }

    /// Sets the value of a general purpose register.
    fn set_reg(&mut self, reg: u8, byte: u8) {
        match reg {
            0x0 => self.v0 = byte,
            0x1 => self.v1 = byte,
            0x2 => self.v2 = byte,
            0x3 => self.v3 = byte,
            0x4 => self.v4 = byte,
            0x5 => self.v5 = byte,
            0x6 => self.v6 = byte,
            0x7 => self.v7 = byte,
            0x8 => self.v8 = byte,
            0x9 => self.v9 = byte,
            0xa => self.va = byte,
            0xb => self.vb = byte,
            0xc => self.vc = byte,
            0xd => self.vd = byte,
            0xe => self.ve = byte,
            0xf => self.vf = byte,
            _ => {
                panic!("Cannot set unknown register: V{:X}", reg);
            }
        }
    }
}
<|endoftext|>