<commit_before>fn main() {
    println!("Hello, world!");
}
<commit_msg>Test example log.rs<commit_after>/*
 * libgit2 "log" example - shows how to walk history and get commit info
 *
 * Written by the libgit2 contributors
 *
 * To the extent possible under law, the author(s) have dedicated all copyright
 * and related and neighboring rights to this software to the public domain
 * worldwide. This software is distributed without any warranty.
 *
 * You should have received a copy of the CC0 Public Domain Dedication along
 * with this software. If not, see
 * <http://creativecommons.org/publicdomain/zero/1.0/>.
 */

#![deny(warnings)]

extern crate rustc_serialize;
extern crate docopt;
extern crate git2;
extern crate time;

use std::str;
use docopt::Docopt;
use git2::{Repository, Signature, Commit, ObjectType, Time, DiffOptions};
use git2::{Pathspec, Diff, Error, DiffFormat};

#[derive(RustcDecodable)]
struct Args {
    arg_commit: Vec<String>,
    arg_spec: Vec<String>,
    flag_topo_order: bool,
    flag_date_order: bool,
    flag_reverse: bool,
    flag_author: Option<String>,
    flag_committer: Option<String>,
    flag_grep: Option<String>,
    flag_git_dir: Option<String>,
    flag_skip: Option<usize>,
    flag_max_count: Option<usize>,
    flag_merges: bool,
    flag_no_merges: bool,
    flag_no_min_parents: bool,
    flag_no_max_parents: bool,
    flag_max_parents: Option<usize>,
    flag_min_parents: Option<usize>,
    flag_patch: bool,
}

fn run(args: &Args) -> Result<(), Error> {
    let path = args.flag_git_dir.as_ref().map(|s| &s[..]).unwrap_or(".");
    let repo = try!(Repository::open(path));
    let mut revwalk = try!(repo.revwalk());

    // Prepare the revwalk based on CLI parameters
    let base = if args.flag_reverse {git2::SORT_REVERSE} else {git2::SORT_NONE};
    revwalk.set_sorting(base | if args.flag_topo_order {
        git2::SORT_TOPOLOGICAL
    } else if args.flag_date_order {
        git2::SORT_TIME
    } else {
        git2::SORT_NONE
    });
    for commit in args.arg_commit.iter() {
        if commit.starts_with("^") {
            let obj = try!(repo.revparse_single(&commit[1..]));
            try!(revwalk.hide(obj.id()));
            continue
        }
        let revspec = try!(repo.revparse(&commit));
        if revspec.mode().contains(git2::REVPARSE_SINGLE) {
            try!(revwalk.push(revspec.from().unwrap().id()));
        } else {
            let from = revspec.from().unwrap().id();
            let to = revspec.to().unwrap().id();
            try!(revwalk.push(to));
            if revspec.mode().contains(git2::REVPARSE_MERGE_BASE) {
                let base = try!(repo.merge_base(from, to));
                let o = try!(repo.find_object(base, Some(ObjectType::Commit)));
                try!(revwalk.push(o.id()));
            }
            try!(revwalk.hide(from));
        }
    }
    if args.arg_commit.len() == 0 {
        try!(revwalk.push_head());
    }

    // Prepare our diff options and pathspec matcher
    let (mut diffopts, mut diffopts2) = (DiffOptions::new(), DiffOptions::new());
    for spec in args.arg_spec.iter() {
        diffopts.pathspec(spec);
        diffopts2.pathspec(spec);
    }
    let ps = try!(Pathspec::new(args.arg_spec.iter()));

    // Filter our revwalk based on the CLI parameters
    macro_rules! filter_try {
        ($e:expr) => (match $e { Ok(t) => t, Err(e) => return Some(Err(e)) })
    }
    let revwalk = revwalk.filter_map(|id| {
        let commit = filter_try!(repo.find_commit(id));
        let parents = commit.parents().len();
        if parents < args.min_parents() { return None }
        if let Some(n) = args.max_parents() {
            if parents >= n { return None }
        }
        if args.arg_spec.len() > 0 {
            match commit.parents().len() {
                0 => {
                    let tree = filter_try!(commit.tree());
                    let flags = git2::PATHSPEC_NO_MATCH_ERROR;
                    if ps.match_tree(&tree, flags).is_err() { return None }
                }
                _ => {
                    let m = commit.parents().all(|parent| {
                        match_with_parent(&repo, &commit, &parent, &mut diffopts)
                                        .unwrap_or(false)
                    });
                    if !m { return None }
                }
            }
        }
        if !sig_matches(commit.author(), &args.flag_author) { return None }
        if !sig_matches(commit.committer(), &args.flag_committer) { return None }
        if !log_message_matches(commit.message(), &args.flag_grep) { return None }
        Some(Ok(commit))
    }).skip(args.flag_skip.unwrap_or(0)).take(args.flag_max_count.unwrap_or(!0));

    // print!
    for commit in revwalk {
        let commit = try!(commit);
        print_commit(&commit);
        if !args.flag_patch || commit.parents().len() > 1 { continue }
        let a = if commit.parents().len() == 1 {
            let parent = try!(commit.parent(0));
            Some(try!(parent.tree()))
        } else {
            None
        };
        let b = try!(commit.tree());
        let diff = try!(Diff::tree_to_tree(&repo, a.as_ref(), Some(&b),
                                           Some(&mut diffopts2)));
        try!(diff.print(DiffFormat::Patch, |_delta, _hunk, line| {
            match line.origin() {
                ' ' | '+' | '-' => print!("{}", line.origin()),
                _ => {}
            }
            print!("{}", str::from_utf8(line.content()).unwrap());
            true
        }));
    }

    Ok(())
}

fn sig_matches(sig: Signature, arg: &Option<String>) -> bool {
    match *arg {
        Some(ref s) => {
            sig.name().map(|n| n.contains(s)).unwrap_or(false) ||
                sig.email().map(|n| n.contains(s)).unwrap_or(false)
        }
        None => true
    }
}

fn log_message_matches(msg: Option<&str>, grep: &Option<String>) -> bool {
    match (grep, msg) {
        (&None, _) => true,
        (&Some(_), None) => false,
        (&Some(ref s), Some(msg)) => msg.contains(s),
    }
}

fn print_commit(commit: &Commit) {
    println!("commit {}", commit.id());

    if commit.parents().len() > 1 {
        print!("Merge:");
        for id in commit.parent_ids() {
            print!(" {:.8}", id);
        }
        println!("");
    }

    let author = commit.author();
    println!("Author: {}", author);
    print_time(&author.when(), "Date:   ");
    println!("");

    for line in String::from_utf8_lossy(commit.message_bytes()).lines() {
        println!("    {}", line);
    }
    println!("");
}

fn print_time(time: &Time, prefix: &str) {
    let (offset, sign) = match time.offset_minutes() {
        n if n < 0 => (-n, '-'),
        n => (n, '+'),
    };
    let (hours, minutes) = (offset / 60, offset % 60);
    let ts = time::Timespec::new(time.seconds() +
                                 (time.offset_minutes() as i64) * 60, 0);
    let time = time::at(ts);

    println!("{}{} {}{:02}{:02}", prefix,
             time.strftime("%a %b %e %T %Y").unwrap(), sign, hours, minutes);

}

fn match_with_parent(repo: &Repository, commit: &Commit, parent: &Commit,
                     opts: &mut DiffOptions) -> Result<bool, Error> {
    let a = try!(parent.tree());
    let b = try!(commit.tree());
    let diff = try!(Diff::tree_to_tree(repo, Some(&a), Some(&b), Some(opts)));
    Ok(diff.deltas().len() > 0)
}

impl Args {
    fn min_parents(&self) -> usize {
        if self.flag_no_min_parents { return 0 }
        self.flag_min_parents.unwrap_or(if self.flag_merges {2} else {0})
    }

    fn max_parents(&self) -> Option<usize> {
        if self.flag_no_max_parents { return None }
        self.flag_max_parents.or(if self.flag_no_merges {Some(1)} else {None})
    }
}

fn main() {
    const USAGE: &'static str = "
usage: log [options] [<commit>..] [--] [<spec>..]

Options:
    --topo-order            sort commits in topological order
    --date-order            sort commits in date order
    --reverse               sort commits in reverse
    --author <user>         author to sort by
    --committer <user>      committer to sort by
    --grep <pat>            pattern to filter commit messages by
    --git-dir <dir>         alternative git directory to use
    --skip <n>              number of commits to skip
    -n, --max-count <n>     maximum number of commits to show
    --merges                only show merge commits
    --no-merges             don't show merge commits
    --no-min-parents        don't require a minimum number of parents
    --no-max-parents        don't require a maximum number of parents
    --max-parents <n>       specify a maximum number of parents for a commit
    --min-parents <n>       specify a minimum number of parents for a commit
    -p, --patch             show commit diff
    -h, --help              show this message
";

    let args = Docopt::new(USAGE).and_then(|d| d.decode())
                                 .unwrap_or_else(|e| e.exit());
    match run(&args) {
        Ok(()) => {}
        Err(e) => println!("error: {}", e),
    }
}
<|endoftext|>


<commit_before>extern crate git2;
extern crate clap;

use git2::*;
use std::env;
use std::io::Write;
use std::path::Path;
use std::path::PathBuf;
use std::process::Command;
use std::process::exit;

const CENTRAL_NAME:    &'static str = "bsw/central";
const AUTOMATION_USER: &'static str = "automation";

// FIXME: hardcoded path
const TMP_REPO_DIR:    &'static str = "/home/christian/gerrit_testsite/tmp_automation_repo";

fn module_review_upload(project: &str, newrev: &str) {
  let project_path = Path::new(project);
  let subdir = project_path.components().nth(0).unwrap();
  let module_name = project_path.components().last().unwrap();

  let tmp_repo = Repository::init_bare(TMP_REPO_DIR).unwrap();

  transfer_to_tmp(newrev);

  let module_commit_obj = tmp_repo.revparse_single(newrev).unwrap();
  let module_commit = module_commit_obj.as_commit().unwrap();
  let module_tree = module_commit.tree().unwrap();

  let master_commit_obj = tmp_repo.revparse_single(CENTRAL_NAME).unwrap();
  let master_commit = master_commit_obj.as_commit().unwrap();
  let master_tree = master_commit_obj.as_commit().unwrap().tree().unwrap();

  let new_tree_oid = module_to_subfolder(Path::new(module_name.as_ref()), &module_tree, &master_tree);
  let new_tree = tmp_repo.find_tree(new_tree_oid).unwrap();

  let parents = vec!(master_commit);
  let central_commit = make_commit(&tmp_repo, &new_tree, module_commit, &parents);
  println!(""); println!("");
  println!("===================== Doing actual upload in central git ========================");
  let x = push_from_tmp(
    &tmp_repo,
    &tmp_repo.find_commit(central_commit.unwrap()).unwrap(),
    CENTRAL_NAME,
    "refs/for/master"
  );
  println!("{}", x);
  println!("==== The review upload may have worked, even if it says error below. Look UP! ====");
}

fn central_submit(newrev: &str) {
  println!("central_submit");
  let mpath = Path::new("modules");

  let module_names = get_module_names(newrev);
  let tmp_repo = setup_tmp_repo(&module_names);
  transfer_to_tmp(newrev);

  let central_commit_obj = tmp_repo.revparse_single(newrev).unwrap();
  let central_commit = central_commit_obj.as_commit().unwrap();
  let central_tree = central_commit.tree().unwrap();
  let modules = tmp_repo.find_tree(central_tree.get_path(mpath).unwrap().id());
  tmp_repo.branch(CENTRAL_NAME,central_commit,true);

  for module_name in module_names {
    let module_master_commit_obj =
      tmp_repo.revparse_single(&format!("remotes/modules/{}/master",module_name)).unwrap();
    let module_master_commit =
      module_master_commit_obj.as_commit().unwrap();
    tmp_repo.branch(&format!("modules/{}",module_name),module_master_commit,true);

    let parents = vec!(module_master_commit);
    let old_tree_oid = module_master_commit.tree().unwrap().id();

    let module_path = { let mut p = PathBuf::new(); p.push("modules"); p.push(&module_name); p };

    let new_tree_oid = central_tree.get_path(&module_path).unwrap().id();

    if new_tree_oid != old_tree_oid {

      let new_tree = tmp_repo.find_tree(new_tree_oid).unwrap();

      let module_commit = make_commit(&tmp_repo, &new_tree, central_commit, &parents);
      let x = push_from_tmp(
        &tmp_repo,
        &tmp_repo.find_commit(module_commit.unwrap()).unwrap(),
        &format!("bsw/modules/{}",module_name),
        "master"
      );
      println!("{}", x);
    }
  }
}

fn transfer_to_tmp(rev: &str) {
  Command::new("git")
    .arg("branch").arg("-f").arg("tmp").arg(rev)
  .output().expect("failed to call git");

  Command::new("git")
    .arg("push").arg("--force").arg(TMP_REPO_DIR).arg("tmp")
  .output().expect("failed to call git");

  Command::new("git")
    .arg("branch").arg("-D").arg("tmp")
  .output().expect("failed to call git");
}

fn in_tmp_repo(cmd: &str) -> String {
  let args: Vec<&str> = cmd.split(" ").collect();
  let output = Command::new("git")
    .env("GIT_DIR",TMP_REPO_DIR)
    .args(&args).output().unwrap();
  return format!("{}", String::from_utf8_lossy(&output.stderr));
}

fn setup_tmp_repo(modules: &Vec<String>) -> Repository {
  let repo = Repository::init_bare(TMP_REPO_DIR).unwrap();

  if !repo.find_remote("central_repo").is_ok() {
    repo.remote("central_repo",
      &format!("ssh://{}@gerrit-test-git/{}.git",AUTOMATION_USER,CENTRAL_NAME)
    );
  }

  for module in modules.iter() {
    let output = Command::new("ssh")
      .arg("-p").arg("29418")
      .arg("gerrit-test-git")
        .arg("gerrit")
        .arg("create-project")
          .arg(format!("bsw/modules/{}",module))
          .arg("--empty-commit")
      .output()
      .expect("failed to create project");

    println!("create-project: {}", String::from_utf8_lossy(&output.stderr));

    let remote_url = format!("ssh://{}@gerrit-test-git:29418/bsw/modules/{}.git",
      AUTOMATION_USER,
      module
    );

    let remote_name = format!("modules/{}",module);
    if !repo.find_remote(&remote_name).is_ok() {
        repo.remote(&remote_name, &remote_url);
    }
  }

  in_tmp_repo("fetch --all");

  return repo;
}

fn module_to_subfolder(path: &Path, module_tree: &Tree, master_tree: &Tree) -> Oid {
  let mpath = Path::new("modules");
  let modules_oid = master_tree.get_path(mpath).unwrap().id();
  let tmp_repo = Repository::init_bare(TMP_REPO_DIR).unwrap();

  let modules_tree = tmp_repo.find_tree(modules_oid).unwrap();
  let mut mbuilder = tmp_repo.treebuilder(Some(&modules_tree)).unwrap();
  mbuilder.insert(path, module_tree.id(), 0o0040000).expect("mbuilder insert failed"); // GIT_FILEMODE_TREE
  let mtree = mbuilder.write().unwrap();

  let mut builder = tmp_repo.treebuilder(Some(master_tree)).unwrap();
  builder.insert(mpath, mtree, 0o0040000).expect("builder insert failed"); // GIT_FILEMODE_TREE
  let r = builder.write().unwrap();
  println!("module_to_subfolder {}", r);
  return r;
}

fn get_module_names(rev: &str) -> Vec<String> {
  let central_repo = Repository::open(".").unwrap();

  let object = central_repo.revparse_single(rev).unwrap();
  let commit = object.as_commit().unwrap();
  let tree = commit.tree().unwrap();

  let modules_o = tree.get_path(&Path::new("modules")).unwrap()
                      .to_object(&central_repo).unwrap();
  let modules = modules_o.as_tree().unwrap();

  let mut names = Vec::<String>::new();
  for module in modules.iter() {
    names.push(module.name().unwrap().to_string());
  }
  return names;
}

fn push_from_tmp(tmp_repo: &Repository, commit: &Commit,repo: &str ,to: &str) -> String {
  tmp_repo.set_head_detached(commit.id());
  return in_tmp_repo(
    &format!("push ssh://{}@gerrit-test-git:29418/{}.git HEAD:{}",
      AUTOMATION_USER,
      repo,
      to
    )
  )
}

fn make_commit(repo: &Repository, tree: &Tree, base: &Commit, parents: &[&Commit]) -> Option<Oid> {
  if parents.len() != 0 {
    repo.set_head_detached(parents[0].id());
  }
  return repo.commit(
    Some("HEAD"),
    &base.author(),
    &base.committer(),
    &base.message().unwrap_or("no message"),
    tree,
    parents
  ).ok();
}

fn main() { exit(main_ret()); } fn main_ret() -> i32 {

  let args = clap::App::new("centralgithook")
      .arg(clap::Arg::with_name("oldrev").long("oldrev").takes_value(true))
      .arg(clap::Arg::with_name("newrev").long("newrev").takes_value(true))
      .arg(clap::Arg::with_name("project").long("project").takes_value(true))
      .arg(clap::Arg::with_name("refname").long("refname").takes_value(true))
      .arg(clap::Arg::with_name("uploader").long("uploader").takes_value(true))
      .arg(clap::Arg::with_name("commit").long("commit").takes_value(true))
      .arg(clap::Arg::with_name("change").long("change").takes_value(true))
      .arg(clap::Arg::with_name("change-url").long("change-url").takes_value(true))
      .arg(clap::Arg::with_name("change-owner").long("change-owner").takes_value(true))
      .arg(clap::Arg::with_name("branch").long("branch").takes_value(true))
      .arg(clap::Arg::with_name("submitter").long("submitter").takes_value(true))
      .arg(clap::Arg::with_name("topic").long("topic").takes_value(true))
      .get_matches();

  let newrev = args.value_of("newrev").unwrap_or("");
  let project = args.value_of("project").unwrap_or("");
  let refname = args.value_of("refname").unwrap_or("");
  let uploader = args.value_of("uploader").unwrap_or("");
  let commit = args.value_of("commit").unwrap_or("");

  if let Some(hook) = env::args().nth(0) {
    let is_review = refname == "refs/for/master";
    let is_automation = uploader.contains("Automation");
    let is_module = project != CENTRAL_NAME;
    let is_update = hook.ends_with("ref-update");
    let is_submit = hook.ends_with("change-merged");

    // // TODO
    // if !is_review && !is_automation {
    //   println!("only push to refs/for/master");
    //   return 1;
    // }

    if is_submit { central_submit(commit); }
    else if !is_module && is_update && !is_review { central_submit(newrev); }
    else if is_module && is_update && is_review { module_review_upload(project,newrev); return 1; }
  }

  return 0;
}

<commit_msg>support for chained commits<commit_after>extern crate git2;
extern crate clap;

use git2::*;
use std::env;
use std::io::Write;
use std::path::Path;
use std::path::PathBuf;
use std::process::Command;
use std::process::exit;

const CENTRAL_NAME:    &'static str = "bsw/central";
const AUTOMATION_USER: &'static str = "automation";

// FIXME: hardcoded path
const TMP_REPO_DIR:    &'static str = "/home/christian/gerrit_testsite/tmp_automation_repo";

fn module_review_upload(project: &str, newrev: &str) {
  let project_path = Path::new(project);
  let subdir = project_path.components().nth(0).unwrap();
  let module_name = project_path.components().last().unwrap();

  let tmp_repo = Repository::init_bare(TMP_REPO_DIR).unwrap();

  transfer_to_tmp(newrev);
  let parent_commit_obj = tmp_repo.revparse_single(CENTRAL_NAME).unwrap();
  let mut parent_commit_oid = parent_commit_obj.as_commit().unwrap().id();

  let walk = {
    let mut walk = tmp_repo.revwalk().unwrap();
    walk.set_sorting( Sort::from_bits(5).unwrap());
    walk.push_range(
      &format!("remotes/modules/{}/master..{}",
        module_name.as_os_str().to_str().unwrap(),
        newrev,
      )
    );
    walk
  };

  for rev in walk.skip(1) {
    let newrev = format!("{}",rev.unwrap());

    let module_commit_obj = tmp_repo.revparse_single(&newrev).unwrap();
    let module_commit = module_commit_obj.as_commit().unwrap();
    let module_tree = module_commit.tree().unwrap();

    let parent_commit = tmp_repo.find_commit(parent_commit_oid).unwrap();

    let new_tree = {
      let master_tree = parent_commit.tree().unwrap();
      let new_tree_oid = module_to_subfolder(Path::new(module_name.as_ref()), &module_tree, &master_tree);
      tmp_repo.find_tree(new_tree_oid).unwrap()
    };

    parent_commit_oid = make_commit(&tmp_repo, &new_tree, module_commit, &vec!(&parent_commit)).unwrap();

  }

  println!(""); println!("");
  println!("===================== Doing actual upload in central git ========================");
  let x = push_from_tmp(
    &tmp_repo,
    &tmp_repo.find_commit(parent_commit_oid).unwrap(),
    CENTRAL_NAME,
    "refs/for/master"
  );
  println!("{}", x);
  println!("==== The review upload may have worked, even if it says error below. Look UP! ====");
}

fn central_submit(newrev: &str) {
  println!("central_submit");
  let mpath = Path::new("modules");

  let module_names = get_module_names(newrev);
  let tmp_repo = setup_tmp_repo(&module_names);
  transfer_to_tmp(newrev);

  let central_commit_obj = tmp_repo.revparse_single(newrev).unwrap();
  let central_commit = central_commit_obj.as_commit().unwrap();
  let central_tree = central_commit.tree().unwrap();
  let modules = tmp_repo.find_tree(central_tree.get_path(mpath).unwrap().id());
  tmp_repo.branch(CENTRAL_NAME,central_commit,true);

  for module_name in module_names {
    let module_master_commit_obj =
      tmp_repo.revparse_single(&format!("remotes/modules/{}/master",module_name)).unwrap();
    let module_master_commit =
      module_master_commit_obj.as_commit().unwrap();
    tmp_repo.branch(&format!("modules/{}",module_name),module_master_commit,true);

    let parents = vec!(module_master_commit);
    let old_tree_oid = module_master_commit.tree().unwrap().id();

    let module_path = { let mut p = PathBuf::new(); p.push("modules"); p.push(&module_name); p };

    let new_tree_oid = central_tree.get_path(&module_path).unwrap().id();

    if new_tree_oid != old_tree_oid {

      let new_tree = tmp_repo.find_tree(new_tree_oid).unwrap();

      let module_commit = make_commit(&tmp_repo, &new_tree, central_commit, &parents);
      let x = push_from_tmp(
        &tmp_repo,
        &tmp_repo.find_commit(module_commit.unwrap()).unwrap(),
        &format!("bsw/modules/{}",module_name),
        "master"
      );
      println!("{}", x);
    }
  }
}

fn transfer_to_tmp(rev: &str) {
  Command::new("git")
    .arg("branch").arg("-f").arg("tmp").arg(rev)
  .output().expect("failed to call git");

  Command::new("git")
    .arg("push").arg("--force").arg(TMP_REPO_DIR).arg("tmp")
  .output().expect("failed to call git");

  Command::new("git")
    .arg("branch").arg("-D").arg("tmp")
  .output().expect("failed to call git");
}

fn in_tmp_repo(cmd: &str) -> String {
  let args: Vec<&str> = cmd.split(" ").collect();
  let output = Command::new("git")
    .env("GIT_DIR",TMP_REPO_DIR)
    .args(&args).output().unwrap();
  return format!("{}", String::from_utf8_lossy(&output.stderr));
}

fn setup_tmp_repo(modules: &Vec<String>) -> Repository {
  let repo = Repository::init_bare(TMP_REPO_DIR).unwrap();

  if !repo.find_remote("central_repo").is_ok() {
    repo.remote("central_repo",
      &format!("ssh://{}@gerrit-test-git/{}.git",AUTOMATION_USER,CENTRAL_NAME)
    );
  }

  for module in modules.iter() {
    let output = Command::new("ssh")
      .arg("-p").arg("29418")
      .arg("gerrit-test-git")
        .arg("gerrit")
        .arg("create-project")
          .arg(format!("bsw/modules/{}",module))
          .arg("--empty-commit")
      .output()
      .expect("failed to create project");

    println!("create-project: {}", String::from_utf8_lossy(&output.stderr));

    let remote_url = format!("ssh://{}@gerrit-test-git:29418/bsw/modules/{}.git",
      AUTOMATION_USER,
      module
    );

    let remote_name = format!("modules/{}",module);
    if !repo.find_remote(&remote_name).is_ok() {
        repo.remote(&remote_name, &remote_url);
    }
  }

  in_tmp_repo("fetch --all");

  return repo;
}

fn module_to_subfolder(path: &Path, module_tree: &Tree, master_tree: &Tree) -> Oid {
  let mpath = Path::new("modules");
  let modules_oid = master_tree.get_path(mpath).unwrap().id();
  let tmp_repo = Repository::init_bare(TMP_REPO_DIR).unwrap();

  let modules_tree = tmp_repo.find_tree(modules_oid).unwrap();
  let mut mbuilder = tmp_repo.treebuilder(Some(&modules_tree)).unwrap();
  mbuilder.insert(path, module_tree.id(), 0o0040000).expect("mbuilder insert failed"); // GIT_FILEMODE_TREE
  let mtree = mbuilder.write().unwrap();

  let mut builder = tmp_repo.treebuilder(Some(master_tree)).unwrap();
  builder.insert(mpath, mtree, 0o0040000).expect("builder insert failed"); // GIT_FILEMODE_TREE
  let r = builder.write().unwrap();
  println!("module_to_subfolder {}", r);
  return r;
}

fn get_module_names(rev: &str) -> Vec<String> {
  let central_repo = Repository::open(".").unwrap();

  let object = central_repo.revparse_single(rev).unwrap();
  let commit = object.as_commit().unwrap();
  let tree = commit.tree().unwrap();

  let modules_o = tree.get_path(&Path::new("modules")).unwrap()
                      .to_object(&central_repo).unwrap();
  let modules = modules_o.as_tree().unwrap();

  let mut names = Vec::<String>::new();
  for module in modules.iter() {
    names.push(module.name().unwrap().to_string());
  }
  return names;
}

fn push_from_tmp(tmp_repo: &Repository, commit: &Commit,repo: &str ,to: &str) -> String {
  tmp_repo.set_head_detached(commit.id());
  return in_tmp_repo(
    &format!("push ssh://{}@gerrit-test-git:29418/{}.git HEAD:{}",
      AUTOMATION_USER,
      repo,
      to
    )
  )
}

fn make_commit(repo: &Repository, tree: &Tree, base: &Commit, parents: &[&Commit]) -> Option<Oid> {
  if parents.len() != 0 {
    repo.set_head_detached(parents[0].id());
  }
  return repo.commit(
    Some("HEAD"),
    &base.author(),
    &base.committer(),
    &base.message().unwrap_or("no message"),
    tree,
    parents
  ).ok();
}

fn main() { exit(main_ret()); } fn main_ret() -> i32 {

  let args = clap::App::new("centralgithook")
      .arg(clap::Arg::with_name("oldrev").long("oldrev").takes_value(true))
      .arg(clap::Arg::with_name("newrev").long("newrev").takes_value(true))
      .arg(clap::Arg::with_name("project").long("project").takes_value(true))
      .arg(clap::Arg::with_name("refname").long("refname").takes_value(true))
      .arg(clap::Arg::with_name("uploader").long("uploader").takes_value(true))
      .arg(clap::Arg::with_name("commit").long("commit").takes_value(true))
      .arg(clap::Arg::with_name("change").long("change").takes_value(true))
      .arg(clap::Arg::with_name("change-url").long("change-url").takes_value(true))
      .arg(clap::Arg::with_name("change-owner").long("change-owner").takes_value(true))
      .arg(clap::Arg::with_name("branch").long("branch").takes_value(true))
      .arg(clap::Arg::with_name("submitter").long("submitter").takes_value(true))
      .arg(clap::Arg::with_name("topic").long("topic").takes_value(true))
      .get_matches();

  let newrev = args.value_of("newrev").unwrap_or("");
  let project = args.value_of("project").unwrap_or("");
  let refname = args.value_of("refname").unwrap_or("");
  let uploader = args.value_of("uploader").unwrap_or("");
  let commit = args.value_of("commit").unwrap_or("");

  if let Some(hook) = env::args().nth(0) {
    let is_review = refname == "refs/for/master";
    let is_automation = uploader.contains("Automation");
    let is_module = project != CENTRAL_NAME;
    let is_update = hook.ends_with("ref-update");
    let is_submit = hook.ends_with("change-merged");

    // // TODO
    // if !is_review && !is_automation {
    //   println!("only push to refs/for/master");
    //   return 1;
    // }

    if is_submit { central_submit(commit); }
    else if !is_module && is_update && !is_review { central_submit(newrev); }
    else if is_module && is_update && is_review { module_review_upload(project,newrev); return 1; }
  }

  return 0;
}

<|endoftext|>


<commit_before>#![cfg_attr(feature = "clippy", feature(plugin))]
#![cfg_attr(feature = "clippy", plugin(clippy))]
extern crate getopts;
extern crate image;
extern crate time;

extern crate rusttracer;

use std::env;
use std::f32::consts::PI;
use std::path::Path;
use std::rc::Rc;

use getopts::Options;

#[cfg(feature = "stats")]
use rusttracer::geometry::triangle::stats;
use rusttracer::mesh_loader::MeshLoader;
use rusttracer::texture;
use rusttracer::{Camera, Color, Config, IllumninationModel, Material, MaterialTemplate, Renderer,
                 Scene, SuperSampling};

#[cfg(feature = "stats")]
fn print_triangle_stats() {
    let number_of_tests = stats::number_of_triangle_intersections();
    let number_of_hits = stats::number_of_triangle_hits();

    println!("Total number of ray-triangle tests: {}", number_of_tests);
    println!("Total number of ray-triangle hits: {}", number_of_hits);
    println!(
        "Efficiency: {:.5}%",
        (f64::from(number_of_hits as u32) / f64::from(number_of_tests as u32)) * 100.0
    );
}

fn print_usage(program: &str, opts: &Options) {
    let brief = format!("Usage: {} [options]", program);
    print!("{}", opts.usage(&brief));
}

fn main() {
    let args: Vec<String> = env::args().collect();

    let program = args[0].clone();
    let mut opts = Options::new();
    opts.optopt("c", "config-path", "config file path", "CONFIG_PATH");
    opts.optflag(
        "b",
        "benchmark",
        "Benchmark by rendering the scene multiple times",
    );
    opts.optflag("h", "help", "prints this help menu");

    let matches = match opts.parse(&args[1..]) {
        Ok(m) => m,
        Err(f) => panic!(f.to_string()),
    };

    if matches.opt_present("h") {
        print_usage(&program, &opts);
        return;
    }

    let benchmark = matches.opt_present("b");
    let config_path = matches.opt_str("c").expect("No config provided");

    let config = Config::new_from_file(&config_path).expect("Invalid configuration");

    let scene_path = Path::new(&config_path).parent().unwrap();
    let mesh_loader = MeshLoader::new(scene_path.to_path_buf());
    let template = MaterialTemplate::new(
        Color::blue() * 0.02,
        Color::black(),
        Color::black(),
        0.0,
        IllumninationModel::DiffuseSpecular,
        None,
        None,
    );
    let fallback_material = Rc::new(template.build_material(|material| {
        material.ambient_color = Color::white() * 0.05;
        material.diffuse_color = Color::white() * 0.0;
        material.ambient_texture = Some(Box::new(texture::Procedural::new(|uv| {
            Color::new_f32(
                ((uv.x * 32.0 * PI).sin() + (uv.y * 32.0 * PI).cos() + 1.0) * 0.5,
                0.0,
                0.0,
            )
        })));
    }));

    let materials = config
        .materials
        .iter()
        .map(|material_config| {
            (
                material_config.name.to_owned(),
                Rc::new(Material::new_from_config(material_config)),
            )
        })
        .collect();
    let scene = Scene::new_from_config(
        &config.scenes.first().unwrap(),
        &materials,
        &mesh_loader,
        fallback_material,
    ).expect("Invalid scene");
    let camera_config = config.cameras.first().unwrap();
    let camera = Camera::from(camera_config);
    let renderer = Renderer::new(&scene, &camera, SuperSampling::On(2));

    if benchmark {
        for _ in 0..10 {
            let _ = renderer.render(config.max_depth);
        }

        return;
    }

    let buffer = renderer.render(config.max_depth);

    #[cfg(feature = "stats")]
    print_triangle_stats();

    let timestamp = time::get_time().sec;
    let filename = format!("images/{}.png", timestamp);
    image::save_buffer(
        &Path::new(&filename),
        &buffer[..],
        camera_config.width,
        camera_config.height,
        image::RGB(8),
    ).unwrap();
}
<commit_msg>[Misc] Improve error messaging<commit_after>#![cfg_attr(feature = "clippy", feature(plugin))]
#![cfg_attr(feature = "clippy", plugin(clippy))]
extern crate getopts;
extern crate image;
extern crate time;

extern crate rusttracer;

use std::env;
use std::f32::consts::PI;
use std::path::Path;
use std::rc::Rc;

use getopts::Options;

#[cfg(feature = "stats")]
use rusttracer::geometry::triangle::stats;
use rusttracer::mesh_loader::MeshLoader;
use rusttracer::texture;
use rusttracer::{Camera, Color, Config, IllumninationModel, Material, MaterialTemplate, Renderer,
                 Scene, SuperSampling};

#[cfg(feature = "stats")]
fn print_triangle_stats() {
    let number_of_tests = stats::number_of_triangle_intersections();
    let number_of_hits = stats::number_of_triangle_hits();

    println!("Total number of ray-triangle tests: {}", number_of_tests);
    println!("Total number of ray-triangle hits: {}", number_of_hits);
    println!(
        "Efficiency: {:.5}%",
        (f64::from(number_of_hits as u32) / f64::from(number_of_tests as u32)) * 100.0
    );
}

fn print_usage(program: &str, opts: &Options) {
    let brief = format!("Usage: {} [options]", program);
    print!("{}", opts.usage(&brief));
}

fn main() {
    let args: Vec<String> = env::args().collect();

    let program = args[0].clone();
    let mut opts = Options::new();
    opts.optopt("c", "config-path", "config file path", "CONFIG_PATH");
    opts.optflag(
        "b",
        "benchmark",
        "Benchmark by rendering the scene multiple times",
    );
    opts.optflag("h", "help", "prints this help menu");

    let matches = match opts.parse(&args[1..]) {
        Ok(m) => m,
        Err(f) => panic!(f.to_string()),
    };

    if matches.opt_present("h") {
        print_usage(&program, &opts);
        return;
    }

    let benchmark = matches.opt_present("b");
    let config_path = matches.opt_str("c").expect("No config provided");

    let config = Config::new_from_file(&config_path).expect("Invalid configuration");

    let scene_path = Path::new(&config_path).parent().unwrap();
    let mesh_loader = MeshLoader::new(scene_path.to_path_buf());
    let template = MaterialTemplate::new(
        Color::blue() * 0.02,
        Color::black(),
        Color::black(),
        0.0,
        IllumninationModel::DiffuseSpecular,
        None,
        None,
    );
    let fallback_material = Rc::new(template.build_material(|material| {
        material.ambient_color = Color::white() * 0.05;
        material.diffuse_color = Color::white() * 0.0;
        material.ambient_texture = Some(Box::new(texture::Procedural::new(|uv| {
            Color::new_f32(
                ((uv.x * 32.0 * PI).sin() + (uv.y * 32.0 * PI).cos() + 1.0) * 0.5,
                0.0,
                0.0,
            )
        })));
    }));

    let materials = config
        .materials
        .iter()
        .map(|material_config| {
            (
                material_config.name.to_owned(),
                Rc::new(Material::new_from_config(material_config)),
            )
        })
        .collect();
    let scene = Scene::new_from_config(
        &config.scenes.first().unwrap(),
        &materials,
        &mesh_loader,
        fallback_material,
    ).expect("Invalid scene");
    let camera_config = config
        .cameras
        .first()
        .expect("Config should contain at least one valid camera");
    let camera = Camera::from(camera_config);
    let renderer = Renderer::new(&scene, &camera, SuperSampling::On(2));

    if benchmark {
        for _ in 0..10 {
            let _ = renderer.render(config.max_depth);
        }

        return;
    }

    let buffer = renderer.render(config.max_depth);

    #[cfg(feature = "stats")]
    print_triangle_stats();

    let timestamp = time::get_time().sec;
    let filename = format!("images/{}.png", timestamp);
    image::save_buffer(
        &Path::new(&filename),
        &buffer[..],
        camera_config.width,
        camera_config.height,
        image::RGB(8),
    ).unwrap();
}
<|endoftext|>


<commit_before>extern crate liner;

use liner::Context;
use liner::KeyBindings;
use std::collections::HashMap;
use std::io::ErrorKind;

fn main() {
    let mut cache = Box::new(HashMap::new());

    let empty_value = String::from("");

    let mut con = Context::new();

    loop {
        let res = con.read_line("> ", &mut |_| {});

        match res {
            Ok(res) => {
                match res.as_str() {
                    s if s.trim() == "" =>  {}
                    s if s.starts_with("get ") =>  {
                        let (_, key) = s.split_at(4);
                        let key_trimmed = String::from(key.trim());
                        if key_trimmed.find(' ').is_some() {
                            println!("key cannot contain space");
                            continue
                        }
                        println!("{}", cache.get(&key_trimmed).unwrap_or(&empty_value));
                    }
                    s if s.starts_with("del ") =>  {
                        let (_, key) = s.split_at(4);
                        let key_trimmed = String::from(key.trim());
                        if key_trimmed.find(' ').is_some() {
                            println!("key cannot contain space");
                            continue
                        }
                        let _ = cache.remove(&key_trimmed);
                    }
                    s if s.starts_with("set ") =>  {
                        let (_, key_value) = s.split_at(4);
                        match key_value.find(' ') {
                            Some(i) => {
                                let (key, value) = key_value.split_at(i);
                                let key_trimmed = String::from(key.trim());
                                let value_trimmed = String::from(value.trim());
                                cache.insert(key_trimmed, value_trimmed);
                            }
                            None => println!("no value specified")
                        }
                    }
                    "keys" =>  {
                        for key in cache.keys() {
                            println!("{}", key);
                        }
                    }
                    "emacs" => {
                        con.key_bindings = KeyBindings::Emacs;
                        println!("emacs mode");
                    }
                    "vi" | "vim" =>  {
                        con.key_bindings = KeyBindings::Vi;
                        println!("vi mode");
                    }
                    "exit" =>  {
                        break;
                    }
                    _ => {
                        println!("operation not defined")
                    }
                }

                con.history.push(res.into()).unwrap();
            }
            Err(e) => {
                match e.kind() {
                    // ctrl-c pressed
                    ErrorKind::Interrupted => {}
                    // ctrl-d pressed
                    ErrorKind::UnexpectedEof => {
                        break;
                    }
                    _ => panic!("error: {:?}", e),
                }
            }
        }
    }
}
<commit_msg>Add size operation<commit_after>extern crate liner;

use liner::Context;
use liner::KeyBindings;
use std::collections::HashMap;
use std::io::ErrorKind;
use std::mem;

fn main() {
    let mut cache = Box::new(HashMap::new());
    let mut bytes_used:usize = 0;

    let empty_value = String::from("");

    let mut con = Context::new();

    loop {
        let res = con.read_line("> ", &mut |_| {});

        match res {
            Ok(res) => {
                match res.as_str() {
                    s if s.trim() == "" =>  {}
                    s if s.starts_with("get ") =>  {
                        let (_, key) = s.split_at(4);
                        let key_trimmed = String::from(key.trim());
                        if key_trimmed.find(' ').is_some() {
                            println!("key cannot contain space");
                            continue
                        }
                        println!("{}", cache.get(&key_trimmed).unwrap_or(&empty_value));
                    }
                    s if s.starts_with("del ") =>  {
                        let (_, key) = s.split_at(4);
                        let key_trimmed = String::from(key.trim());
                        if key_trimmed.find(' ').is_some() {
                            println!("key cannot contain space");
                            continue
                        }
                        let value = cache.remove(&key_trimmed);
                        bytes_used -= mem::size_of_val(&key_trimmed) + mem::size_of_val(&value);
                    }
                    s if s.starts_with("set ") =>  {
                        let (_, key_value) = s.split_at(4);
                        match key_value.find(' ') {
                            Some(i) => {
                                let (key, value) = key_value.split_at(i);
                                let key_trimmed = String::from(key.trim());
                                let value_trimmed = String::from(value.trim());
                                bytes_used += mem::size_of_val(&key_trimmed) + mem::size_of_val(&value_trimmed);
                                cache.insert(key_trimmed, value_trimmed);
                            }
                            None => println!("no value specified")
                        }
                    }
                    "size" =>  {
                        println!("{} bytes", bytes_used);
                    }
                    "keys" =>  {
                        for key in cache.keys() {
                            println!("{}", key);
                        }
                    }
                    "emacs" => {
                        con.key_bindings = KeyBindings::Emacs;
                        println!("emacs mode");
                    }
                    "vi" | "vim" =>  {
                        con.key_bindings = KeyBindings::Vi;
                        println!("vi mode");
                    }
                    "exit" =>  {
                        break;
                    }
                    _ => {
                        println!("operation not defined")
                    }
                }

                con.history.push(res.into()).unwrap();
            }
            Err(e) => {
                match e.kind() {
                    // ctrl-c pressed
                    ErrorKind::Interrupted => {}
                    // ctrl-d pressed
                    ErrorKind::UnexpectedEof => {
                        break;
                    }
                    _ => panic!("error: {:?}", e),
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>use macroquad::prelude::*;

mod colour;
mod grid;
mod graph;

#[macroquad::main("BasicShapes")]
async fn main() {
    loop {
        clear_background(RED);

        draw_line(40.0, 40.0, 100.0, 200.0, 15.0, BLUE);
        draw_rectangle(screen_width() / 2.0 - 60.0, 100.0, 120.0, 60.0, GREEN);
        draw_circle(screen_width() - 30.0, screen_height() - 30.0, 15.0, YELLOW);
        draw_text("HELLO", 20.0, 20.0, 20.0, DARKGRAY);

        next_frame().await
    }
}
<commit_msg>Generate a grid and graph in main()<commit_after>use macroquad::prelude::*;

use grid::Grid;
use graph::Graph;

mod colour;
mod grid;
mod graph;

#[macroquad::main("BasicShapes")]
async fn main() {
    let grid = Grid::generate(8);
    let mut graph = Graph::create(&grid);

    loop {
        clear_background(RED);

        draw_line(40.0, 40.0, 100.0, 200.0, 15.0, BLUE);
        draw_rectangle(screen_width() / 2.0 - 60.0, 100.0, 120.0, 60.0, GREEN);
        draw_circle(screen_width() - 30.0, screen_height() - 30.0, 15.0, YELLOW);
        draw_text("HELLO", 20.0, 20.0, 20.0, DARKGRAY);

        next_frame().await
    }
}
<|endoftext|>


<commit_before>mod render;

extern crate piston;
extern crate graphics;
extern crate wrapped2d;
extern crate rand;
extern crate cgmath;

#[macro_use]
extern crate gfx;
extern crate gfx_device_gl;
extern crate gfx_window_glutin;
extern crate glutin;
extern crate genmesh;

use std::time::SystemTime;
use rand::Rng;

use piston::window::WindowSettings;
use piston::event_loop::*;
use piston::input::*;
use piston::input::Input::*;

use gfx::Device;
use gfx::traits::FactoryExt;

use cgmath::{Point3, Vector3, Matrix4, EuclideanVector};
use cgmath::{Transform, AffineMatrix3};
use genmesh::generators::SphereUV;
use genmesh::{Triangulate, MapToVertices, Vertices};

use wrapped2d::b2;
use std::f64::consts;

pub struct Viewport {
	width: u32,
	height: u32,
	scale: f32,
}

pub struct InputState {
	left_button_pressed: bool,
	mouse_position: b2::Vec2,
}

use render::VertexPosNormal as Vertex;
use render::ColorFormat;
use render::DepthFormat;

const TRIANGLE: [Vertex; 3] = [Vertex {
	                               pos: [-1.0, -1.0, 0.0],
	                               normal: [0.0, 0.0, 1.0],
	                               tex_coord: [1.0, 0.0],
                               },
                               Vertex {
	                               pos: [1.0, -1.0, 0.0],
	                               normal: [0.0, 0.0, 1.0],
	                               tex_coord: [1.0, 0.0],
                               },
                               Vertex {
	                               pos: [0.0, 1.0, 0.0],
	                               normal: [0.0, 0.0, 1.0],
	                               tex_coord: [1.0, 0.0],
                               }];

fn new_ball(world: &mut b2::World, pos: b2::Vec2) {
	let mut rng = rand::thread_rng();
	let radius: f32 = (rng.gen::<f32>() * 1.0) + 1.0;

	let mut circle_shape = b2::CircleShape::new();
	circle_shape.set_radius(radius);

	let mut f_def = b2::FixtureDef::new();
	f_def.density = (rng.gen::<f32>() * 1.0) + 1.0;
	f_def.restitution = 0.2;
	f_def.friction = 0.3;

	let mut b_def = b2::BodyDef::new();
	b_def.body_type = b2::BodyType::Dynamic;
	b_def.position = pos;
	let handle = world.create_body(&b_def);
	world.get_body_mut(handle)
		.create_fixture(&circle_shape, &mut f_def);
}
struct App {
	input_state: InputState,
	world: b2::World,
}

impl App {
	fn on_click(&mut self, btn: MouseButton, pos: b2::Vec2) {
		match btn {
			MouseButton::Left => {
				self.input_state.left_button_pressed = true;
				new_ball(&mut self.world, pos);
			}
			_ => (),
		}
	}

	fn on_drag(&mut self, pos: b2::Vec2) {
		new_ball(&mut self.world, pos);
	}

	fn on_release(&mut self, btn: MouseButton, _: b2::Vec2) {
		match btn {
			MouseButton::Left => {
				self.input_state.left_button_pressed = false;
			}
			_ => (),
		}
	}

	fn input(&mut self, i: &Input) {
		match *i {
			Release(Button::Mouse(b)) => {
				let pos = self.input_state.mouse_position;
				self.on_release(b, pos);
			}
			Press(Button::Mouse(b)) => {
				let pos = self.input_state.mouse_position;
				self.on_click(b, pos);
			}
			Move(Motion::MouseCursor(x, y)) => {
				fn transform_pos(viewport: &Viewport, x: f64, y: f64) -> b2::Vec2 {
					let viewport = &viewport;
					let tx = (x as f32 - (viewport.width as f32 * 0.5)) / viewport.scale;
					let ty = ((viewport.height as f32 * 0.5) - y as f32) / viewport.scale;
					return b2::Vec2 { x: tx, y: ty };
				}
			}
			_ => (),
		}
	}

	fn render(&mut self, args: &RenderArgs) {
		use graphics::*;

		// self.gl.draw(args.viewport(), |c, g| {
		// clear(WHITE, g);
		//
		// for (_, b) in world.bodies() {
		// let body = b.borrow();
		// let position = (*body).position();
		// let angle = (*body).angle();
		//
		// let transform = c.transform // transform compose right to left
		// .trans((viewport.width as f64 * 0.5), (viewport.height as f64 * 0.5))
		// .scale(viewport.scale as f64, -viewport.scale as f64)
		// .trans(position.x as f64, position.y as f64)
		// .rot_rad(angle as f64);
		//
		// for (_, f) in body.fixtures() {
		// let fixture = f.borrow();
		// let shape = (*fixture).shape();
		// let density = (*fixture).density();
		//
		// match *shape {
		// b2::UnknownShape::Circle(ref s) => {
		// let p = s.position();
		// let r = s.radius() as f64;
		// let extent = rectangle::square(p.x as f64 - r, p.y as f64 - r, r * 2.0);
		// let colour = [1.0, 0.0, 0.0, density - 1.0];
		// Ellipse::new(colour).draw(extent, &DrawState::default(), transform, g);
		// }
		// b2::UnknownShape::Polygon(ref s) => {
		// let n = s.vertex_count();
		// let mut v = Vec::with_capacity(n as usize);
		// for i in 0..n {
		// let vertex = s.vertex(i);
		// v.push([vertex.x as f64, vertex.y as f64]);
		// }
		// Polygon::new(BLACK)
		// .draw(v.as_slice(), &DrawState::default(), transform, g);
		// }
		// _ => (),
		// }
		// }
		// }
		// });
		//
	}

	fn update(&mut self, dt: f32) {
		let world = &mut self.world;
		world.step(dt, 8, 3);
		const MAX_RADIUS: f32 = 5.0;
		let edge = 0.0f32; //-(self.viewport.height as f32) / 2.0 / self.viewport.scale - MAX_RADIUS;
		let mut v = Vec::new();
		for (h, b) in world.bodies() {
			let body = b.borrow();
			let position = (*body).position();
			if position.y < edge {
				v.push(h);
			}
		}
		for h in v {
			world.destroy_body(h);
		}
	}
}

fn new_world() -> b2::World {
	let mut world = b2::World::new(&b2::Vec2 { x: 0.0, y: -9.8 });

	let mut b_def = b2::BodyDef::new();
	b_def.body_type = b2::BodyType::Static;
	b_def.position = b2::Vec2 { x: 0.0, y: -20.0 };

	let mut ground_box = b2::PolygonShape::new();
	{
		ground_box.set_as_box(20.0, 1.0);
		let ground_handle = world.create_body(&b_def);
		let ground = &mut world.get_body_mut(ground_handle);
		ground.create_fast_fixture(&ground_box, 0.);

		ground_box.set_as_oriented_box(1.0,
		                               5.0,
		                               &b2::Vec2 { x: 21.0, y: 5.0 },
		                               (-consts::FRAC_PI_8) as f32);
		ground.create_fast_fixture(&ground_box, 0.);

		ground_box.set_as_oriented_box(1.0,
		                               5.0,
		                               &b2::Vec2 { x: -21.0, y: 5.0 },
		                               (consts::FRAC_PI_8) as f32);
		ground.create_fast_fixture(&ground_box, 0.);
	}
	world
}

fn main() {
	const WIDTH: u32 = 1000;
	const HEIGHT: u32 = 1000;

	let builder = glutin::WindowBuilder::new()
		.with_title("Box2d + GFX".to_string())
		.with_dimensions(WIDTH, HEIGHT)
		.with_vsync();

	let (window, mut device, mut factory, main_color, main_depth) = gfx_window_glutin::init::<ColorFormat,
	                                                                               DepthFormat>(builder);
	let (w, h, _, _) = main_color.get_dimensions();

	let renderer = render::DrawShaded::new(&mut factory);

	// Create a new game and run it.
	let mut app = App {
		input_state: InputState {
			left_button_pressed: false,
			mouse_position: b2::Vec2 { x: 0.0, y: 0.0 },
		},
		world: new_world(),
	};

	let mut encoder: gfx::Encoder<_, _> = factory.create_command_buffer().into();

	let (vertex_buffer, slice) = factory.create_vertex_buffer_with_slice(&TRIANGLE, ());

	let camera = render::Camera {
		projection: cgmath::ortho(-10.0f32, 10.0, -10.0, 10.0, 10.0, -10.0).into(),
		view: Matrix4::look_at(cgmath::Point3::new(0.0, 0.0, 1.0),
		                       cgmath::Point3::new(0.0, 0.0, 0.0),
		                       cgmath::Vector3::unit_y())
			.into(),
	};

	let lights: Vec<render::PointLight> = vec![render::PointLight {
		                                           propagation: [1.0; 4],
		                                           center: [0.0, 0.0, 1.0, 1.0],
		                                           color: [1.0, 0.0, 0.0, 1.0],
	                                           }];

	let mut elapsed = 0.0f32;
	let mut start = SystemTime::now();
	let mut encoder: gfx::Encoder<_, _> = factory.create_command_buffer().into();
	'main: loop {
		renderer.setup(&mut encoder, &camera, &lights);

		// events
		for event in window.poll_events() {
			match event {
				// glutin::Event::Input(e) => app.input(e),
				glutin::Event::Closed => break 'main,
				_ => {}
			}
		}

		// update
		match start.elapsed() {
			Ok(dt) => {
				let frame = (dt.as_secs() as f32) + (dt.subsec_nanos() as f32) * 1e-9;
				app.update(frame);
				elapsed += frame;
			}
			Err(_) => {}
		}

		// draw a frame
		renderer.begin_frame(&mut encoder, &main_color, &main_depth);
		use cgmath::Rotation3;
		for i in -2..3 {
			for j in -2..3 {
				let angle = cgmath::rad(elapsed * 5. + i as f32 + j as f32);
				let rot = Matrix4::from(cgmath::Quaternion::from_axis_angle(cgmath::Vector3::unit_y(), angle));
				let trans = Matrix4::from_translation(cgmath::Vector3::new(i as f32, j as f32, 0.0));

				let transform = trans * rot;

				renderer.draw(&mut encoder,
				              &vertex_buffer,
				              &slice,
				              &transform.into(),
				              &main_color,
				              &main_depth);
			}
		}
		renderer.end_frame(&mut encoder, &mut device);

		window.swap_buffers().unwrap();

		renderer.cleanup(&mut device);

		start = SystemTime::now();
	}

}
<commit_msg>Quads instead of triangles now<commit_after>mod render;

extern crate piston;
extern crate graphics;
extern crate wrapped2d;
extern crate rand;
extern crate cgmath;

#[macro_use]
extern crate gfx;
extern crate gfx_device_gl;
extern crate gfx_window_glutin;
extern crate glutin;
extern crate genmesh;

use std::time::SystemTime;
use rand::Rng;

use piston::window::WindowSettings;
use piston::event_loop::*;
use piston::input::*;
use piston::input::Input::*;

use gfx::Device;
use gfx::traits::FactoryExt;

use cgmath::{Point3, Vector3, Matrix4, EuclideanVector};
use cgmath::{Transform, AffineMatrix3};
use genmesh::generators::SphereUV;
use genmesh::{Triangulate, MapToVertices, Vertices};

use wrapped2d::b2;
use std::f64::consts;

pub struct Viewport {
	width: u32,
	height: u32,
	scale: f32,
}

pub struct InputState {
	left_button_pressed: bool,
	mouse_position: b2::Vec2,
}

use render::VertexPosNormal as Vertex;
use render::ColorFormat;
use render::DepthFormat;

const QUAD: [Vertex; 6] = [Vertex {
	                               pos: [-1.0, -1.0, 0.0],
	                               normal: [0.0, 0.0, 1.0],
	                               tex_coord: [0.0, 0.0],
                               },
                               Vertex {
	                               pos: [1.0, -1.0, 0.0],
	                               normal: [0.0, 0.0, 1.0],
	                               tex_coord: [1.0, 0.0],
                               },
                               Vertex {
	                               pos: [1.0, 1.0, 0.0],
	                               normal: [0.0, 0.0, 1.0],
	                               tex_coord: [1.0, 1.0],
                               },
                               Vertex {
	                               pos: [-1.0, -1.0, 0.0],
	                               normal: [0.0, 0.0, 1.0],
	                               tex_coord: [0.0, 0.0],
                               },
                               Vertex {
	                               pos: [-1.0, 1.0, 0.0],
	                               normal: [0.0, 0.0, 1.0],
	                               tex_coord: [0.0, 1.0],
                               },
                               Vertex {
	                               pos: [1.0, 1.0, 0.0],
	                               normal: [0.0, 0.0, 1.0],
	                               tex_coord: [1.0, 1.0],
                               }];

fn new_ball(world: &mut b2::World, pos: b2::Vec2) {
	let mut rng = rand::thread_rng();
	let radius: f32 = (rng.gen::<f32>() * 1.0) + 1.0;

	let mut circle_shape = b2::CircleShape::new();
	circle_shape.set_radius(radius);

	let mut f_def = b2::FixtureDef::new();
	f_def.density = (rng.gen::<f32>() * 1.0) + 1.0;
	f_def.restitution = 0.2;
	f_def.friction = 0.3;

	let mut b_def = b2::BodyDef::new();
	b_def.body_type = b2::BodyType::Dynamic;
	b_def.position = pos;
	let handle = world.create_body(&b_def);
	world.get_body_mut(handle)
		.create_fixture(&circle_shape, &mut f_def);
}
struct App {
	input_state: InputState,
	world: b2::World,
}

impl App {
	fn on_click(&mut self, btn: MouseButton, pos: b2::Vec2) {
		match btn {
			MouseButton::Left => {
				self.input_state.left_button_pressed = true;
				new_ball(&mut self.world, pos);
			}
			_ => (),
		}
	}

	fn on_drag(&mut self, pos: b2::Vec2) {
		new_ball(&mut self.world, pos);
	}

	fn on_release(&mut self, btn: MouseButton, _: b2::Vec2) {
		match btn {
			MouseButton::Left => {
				self.input_state.left_button_pressed = false;
			}
			_ => (),
		}
	}

	fn input(&mut self, i: &Input) {
		match *i {
			Release(Button::Mouse(b)) => {
				let pos = self.input_state.mouse_position;
				self.on_release(b, pos);
			}
			Press(Button::Mouse(b)) => {
				let pos = self.input_state.mouse_position;
				self.on_click(b, pos);
			}
			Move(Motion::MouseCursor(x, y)) => {
				fn transform_pos(viewport: &Viewport, x: f64, y: f64) -> b2::Vec2 {
					let viewport = &viewport;
					let tx = (x as f32 - (viewport.width as f32 * 0.5)) / viewport.scale;
					let ty = ((viewport.height as f32 * 0.5) - y as f32) / viewport.scale;
					return b2::Vec2 { x: tx, y: ty };
				}
			}
			_ => (),
		}
	}

	fn render(&mut self, args: &RenderArgs) {
		use graphics::*;

		// self.gl.draw(args.viewport(), |c, g| {
		// clear(WHITE, g);
		//
		// for (_, b) in world.bodies() {
		// let body = b.borrow();
		// let position = (*body).position();
		// let angle = (*body).angle();
		//
		// let transform = c.transform // transform compose right to left
		// .trans((viewport.width as f64 * 0.5), (viewport.height as f64 * 0.5))
		// .scale(viewport.scale as f64, -viewport.scale as f64)
		// .trans(position.x as f64, position.y as f64)
		// .rot_rad(angle as f64);
		//
		// for (_, f) in body.fixtures() {
		// let fixture = f.borrow();
		// let shape = (*fixture).shape();
		// let density = (*fixture).density();
		//
		// match *shape {
		// b2::UnknownShape::Circle(ref s) => {
		// let p = s.position();
		// let r = s.radius() as f64;
		// let extent = rectangle::square(p.x as f64 - r, p.y as f64 - r, r * 2.0);
		// let colour = [1.0, 0.0, 0.0, density - 1.0];
		// Ellipse::new(colour).draw(extent, &DrawState::default(), transform, g);
		// }
		// b2::UnknownShape::Polygon(ref s) => {
		// let n = s.vertex_count();
		// let mut v = Vec::with_capacity(n as usize);
		// for i in 0..n {
		// let vertex = s.vertex(i);
		// v.push([vertex.x as f64, vertex.y as f64]);
		// }
		// Polygon::new(BLACK)
		// .draw(v.as_slice(), &DrawState::default(), transform, g);
		// }
		// _ => (),
		// }
		// }
		// }
		// });
		//
	}

	fn update(&mut self, dt: f32) {
		let world = &mut self.world;
		world.step(dt, 8, 3);
		const MAX_RADIUS: f32 = 5.0;
		let edge = 0.0f32; //-(self.viewport.height as f32) / 2.0 / self.viewport.scale - MAX_RADIUS;
		let mut v = Vec::new();
		for (h, b) in world.bodies() {
			let body = b.borrow();
			let position = (*body).position();
			if position.y < edge {
				v.push(h);
			}
		}
		for h in v {
			world.destroy_body(h);
		}
	}
}

fn new_world() -> b2::World {
	let mut world = b2::World::new(&b2::Vec2 { x: 0.0, y: -9.8 });

	let mut b_def = b2::BodyDef::new();
	b_def.body_type = b2::BodyType::Static;
	b_def.position = b2::Vec2 { x: 0.0, y: -20.0 };

	let mut ground_box = b2::PolygonShape::new();
	{
		ground_box.set_as_box(20.0, 1.0);
		let ground_handle = world.create_body(&b_def);
		let ground = &mut world.get_body_mut(ground_handle);
		ground.create_fast_fixture(&ground_box, 0.);

		ground_box.set_as_oriented_box(1.0,
		                               5.0,
		                               &b2::Vec2 { x: 21.0, y: 5.0 },
		                               (-consts::FRAC_PI_8) as f32);
		ground.create_fast_fixture(&ground_box, 0.);

		ground_box.set_as_oriented_box(1.0,
		                               5.0,
		                               &b2::Vec2 { x: -21.0, y: 5.0 },
		                               (consts::FRAC_PI_8) as f32);
		ground.create_fast_fixture(&ground_box, 0.);
	}
	world
}

fn main() {
	const WIDTH: u32 = 1000;
	const HEIGHT: u32 = 1000;

	let builder = glutin::WindowBuilder::new()
		.with_title("Box2d + GFX".to_string())
		.with_dimensions(WIDTH, HEIGHT)
		.with_vsync();

	let (window, mut device, mut factory, main_color, main_depth) = gfx_window_glutin::init::<ColorFormat,
	                                                                               DepthFormat>(builder);
	let (w, h, _, _) = main_color.get_dimensions();

	let renderer = render::DrawShaded::new(&mut factory);

	// Create a new game and run it.
	let mut app = App {
		input_state: InputState {
			left_button_pressed: false,
			mouse_position: b2::Vec2 { x: 0.0, y: 0.0 },
		},
		world: new_world(),
	};

	let (vertex_buffer, slice) = factory.create_vertex_buffer_with_slice(&QUAD, ());

	let camera = render::Camera {
		projection: cgmath::ortho(-10.0f32, 10.0, -10.0, 10.0, 10.0, -10.0).into(),
		view: Matrix4::look_at(cgmath::Point3::new(0.0, 0.0, 1.0),
		                       cgmath::Point3::new(0.0, 0.0, 0.0),
		                       cgmath::Vector3::unit_y())
			.into(),
	};

	let lights: Vec<render::PointLight> = vec![render::PointLight {
		                                           propagation: [1.0; 4],
		                                           center: [0.0, 0.0, 1.0, 1.0],
		                                           color: [1.0, 0.0, 0.0, 1.0],
	                                           }];

	let mut elapsed = 0.0f32;
	let mut start = SystemTime::now();
	let mut encoder: gfx::Encoder<_, _> = factory.create_command_buffer().into();
	'main: loop {
		renderer.setup(&mut encoder, &camera, &lights);

		// events
		for event in window.poll_events() {
			match event {
				// glutin::Event::Input(e) => app.input(e),
				glutin::Event::Closed => break 'main,
				_ => {}
			}
		}

		// update
		match start.elapsed() {
			Ok(dt) => {
				let frame = (dt.as_secs() as f32) + (dt.subsec_nanos() as f32) * 1e-9;
				app.update(frame);
				elapsed += frame;
			}
			Err(_) => {}
		}

		// draw a frame
		renderer.begin_frame(&mut encoder, &main_color, &main_depth);
		use cgmath::Rotation3;
		for i in -5..6 {
			for j in -5..6 {
				let angle = cgmath::rad(elapsed * 5. + i as f32 + j as f32);
				let rot = Matrix4::from(cgmath::Quaternion::from_axis_angle(cgmath::Vector3::unit_y(), angle));
				let trans = Matrix4::from_translation(cgmath::Vector3::new(i as f32, j as f32, 0.0));

				let transform = trans * rot;

				renderer.draw(&mut encoder,
				              &vertex_buffer,
				              &slice,
				              &transform.into(),
				              &main_color,
				              &main_depth);
			}
		}
		renderer.end_frame(&mut encoder, &mut device);
		window.swap_buffers().unwrap();
		renderer.cleanup(&mut device);

		start = SystemTime::now();
	}

}
<|endoftext|>


<commit_before>// The MIT License (MIT)
//
// Copyright (c) 2015 Johan Johansson
//
// 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.

// TODO: TESTS!

#![feature(libc, core, std_misc, io, unboxed_closures, box_syntax, path, old_io, fs)]

extern crate libc;
extern crate "rustc-serialize" as rustc_serialize;
extern crate time;
extern crate "serial-rust" as serial;

use config::parse_led_indices;
use color::{RGB8,
	RgbTransformer,
	Pixel};
use capture::Capturer;

use std::iter::repeat;
use std::old_io::timer;
use std::time::Duration;
use std::sync::mpsc::channel;
use std::thread;
use std::cmp::{max, partial_max};

mod config;
mod color;
mod capture;

fn new_pixel_buf_header(n_leds: u16) -> Vec<u8> {
	// A special header is expected by the corresponding LED streaming code
	// running on the Arduino. This only needs to be initialized once because the number of
	// LEDs remains constant.

	// In the two below, not sure why the -1 in `(n_leds - 1)` is needed,
	// but that's how LEDstream on the Arduino expects it
	let count_high = ((n_leds - 1) >> 8) as u8;  // LED count high byte
	let count_low = ((n_leds - 1) & 0xff) as u8; // LED count low byte
	vec![
		'A' as u8,
		'd' as u8,
		'a' as u8,

		count_high,
		count_low,

		count_high ^ count_low ^ 0x55, // Checksum
	]
}

struct FrameTimer {
	before: f64,
	last_frame_dt: f64,
}
impl FrameTimer {
	fn new() -> FrameTimer {
		FrameTimer{ before: time::precise_time_s(), last_frame_dt: 0.0}
	}

	fn last_frame_dt(&self) -> f64 {
		self.last_frame_dt
	}

	fn dt_to_now(&mut self) -> f64 {
		let now = time::precise_time_s();
		let dt = now - self.before;
		if dt >= 0.0 {
			dt
		} else {
			self.before = now;
			0.0
		}
	}

	fn tick(&mut self) {
		let now = time::precise_time_s();
		self.last_frame_dt = partial_max(now - self.before, 0.0).unwrap_or(0.0);
		self.before = now;
	}
}

fn main() {
	use std::io::Write;
	use capture::CaptureResult::*;

	// Parse the HyperCon json config
	let config = config::parse_config();
	let leds = config.leds.as_slice();

	let mut led_transformers: Vec<_> = repeat(Vec::with_capacity(2)).take(leds.len()).collect();

	// Add transforms from config to each led in matching vec
	for transform_conf in config.color.transform.iter() {
		let hsv_transformer = if !transform_conf.hsv.is_default() {
			Some(&transform_conf.hsv)
		} else { None };

		let rgb_transformer = if !(transform_conf.red.is_default() &&
			transform_conf.green.is_default() &&
			transform_conf.red.is_default())
		{
			Some(RgbTransformer{r: &transform_conf.red,
				g: &transform_conf.green,
				b: &transform_conf.blue})
		} else { None };

		for range in parse_led_indices(transform_conf.leds.as_slice(), leds.len()).iter() {
			for transforms in led_transformers[*range].iter_mut() {
				transforms.push((rgb_transformer.clone(), hsv_transformer));
			}
		}
	}

	// Do serial writing on own thread as to not block.
	let (write_thread_tx, write_thread_rx) = {
		let mut serial_con =
			serial::Connection::new(config.device.output.clone(), config.device.rate);
		serial_con.open().unwrap();

		// Header to write before led data
		let out_header = new_pixel_buf_header(leds.len() as u16);

		let out_pixels = repeat(RGB8{r: 0, g: 0, b: 0})
			.take(leds.len())
			.collect();

		let (from_write_thread_tx, from_write_thread_rx) = channel();
		let (to_write_thread_tx, to_write_thread_rx) = channel::<Vec<RGB8>>();

		thread::spawn(move || {
			loop {
				let out_bytes = color::rgbs_to_bytes(
					to_write_thread_rx.recv().unwrap());

				serial_con.write(out_header.as_slice())
					.and(serial_con.write(out_bytes.as_slice()))
					.unwrap_or_else(|e| {
						println!("Failed to write serial data\n\
							{}", e);
						0
					});

				from_write_thread_tx.send(color::bytes_to_rgbs(out_bytes)).unwrap();
			}
		});
		to_write_thread_tx.send(out_pixels).unwrap();

		(to_write_thread_tx, from_write_thread_rx)
	};

	let mut capturer = Capturer::new();
	capturer.frame.set_resize_dimensions(
		(config.framegrabber.width, config.framegrabber.height));

	let capture_frame_interval = 1.0 / config.framegrabber.frequency_Hz;
	capturer.set_timeout((1000.0 * capture_frame_interval) as u32);
	let led_refresh_interval = 1.0 / config.color.smoothing.update_frequency;

	// Function to use when smoothing led colors
	type SmoothFn = fn(&RGB8, RGB8, f64) -> RGB8;
	let smooth = match config.color.smoothing.type_.as_slice() {
		"linear" => color::linear_smooth as SmoothFn,
		_ => color::no_smooth as SmoothFn,
	};
	// max w/ 1 to avoid divide by zero
	let smooth_time_const = max(config.color.smoothing.time_ms, 1) as f64 / 1000.0;

	let mut capture_timer = FrameTimer::new();
	let mut led_refresh_timer = FrameTimer::new();
	let mut diag_timer = FrameTimer::new();
	let mut diag_i = 0;
	loop {
		led_refresh_timer.tick();

		// Don't capture frame if going faster than frame limit,
		// but still proceed to smooth leds
		if capture_timer.dt_to_now() > capture_frame_interval {
			// If something goes wrong, reuse last frame
			match capturer.capture_frame() {
				CrOk => (),
				// Access Denied means we are probably in fullscreen app with
				// restricted access, sleep until we have access again
				CrAccessDenied => {
					println!("Access Denied");
					timer::sleep(Duration::seconds(2))
				},
				// Should be handled automatically in DXGCap
				CrAccessLost => println!("Access to desktop duplication lost"),
				CrTimeout => (),
				CrFail => println!("Unexpected failure when capturing screen"),
			}
			capture_timer.tick();
		}

		let diag_bap = time::precise_time_s();

		let mut out_pixels = write_thread_rx.recv().unwrap();

		let smooth_factor = led_refresh_timer.last_frame_dt() / smooth_time_const;
		for (to_pixel, out_buf_pixel) in leds.iter()
			.map(|led| {
				capturer.frame.average_color(&led)
			})
			.zip(led_transformers.iter())
			.map(|(led_rgb, transformers)|
				transformers.iter().fold(box led_rgb as Box<Pixel>,
					|mut led_color, &(ref opt_rgb_tr, ref opt_hsv_tr)|
				{
					if let &Some(ref rgb_tr) = opt_rgb_tr {
						led_color = box led_color.rgb_transform(rgb_tr)
							as Box<Pixel>;
					}
					if let &Some(ref hsv_tr) = opt_hsv_tr {
						box led_color.hsv_transform(hsv_tr)
							as Box<Pixel>
					} else {
						led_color
					}
				})
				.to_rgb())
			.zip(out_pixels.iter_mut())
		{
			*out_buf_pixel = smooth(out_buf_pixel, to_pixel, smooth_factor);
		}

		let diag_aap = time::precise_time_s();

		write_thread_tx.send(out_pixels).unwrap();

		diag_i += 1;
		if diag_i >= 120 {
			diag_timer.tick();
			println!("ap fps: {}", 1.0 / (diag_aap-diag_bap));
			println!("avg fps: {}\n", 1.0 / (diag_timer.last_frame_dt() / 120.0));
			diag_i = 0;
		}

		let time_left = led_refresh_interval - led_refresh_timer.dt_to_now();
		if time_left > 0.0 {
			timer::sleep(Duration::microseconds((time_left * 1_000_000.0) as i64));
		}
	}
}<commit_msg>init led serial write thread in init_write_thread<commit_after>// The MIT License (MIT)
//
// Copyright (c) 2015 Johan Johansson
//
// 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.

// TODO: TESTS!

#![feature(libc, core, std_misc, io, unboxed_closures, box_syntax, path, old_io, fs)]

extern crate libc;
extern crate "rustc-serialize" as rustc_serialize;
extern crate time;
extern crate "serial-rust" as serial;

use config::parse_led_indices;
use color::{RGB8,
	RgbTransformer,
	Pixel};
use capture::Capturer;

use std::iter::repeat;
use std::old_io::timer;
use std::time::Duration;
use std::sync::mpsc::{Sender,
	Receiver,
	channel};
use std::thread;
use std::cmp::{max, partial_max};

mod config;
mod color;
mod capture;

fn new_pixel_buf_header(n_leds: u16) -> Vec<u8> {
	// A special header is expected by the corresponding LED streaming code
	// running on the Arduino. This only needs to be initialized once because the number of
	// LEDs remains constant.

	// In the two below, not sure why the -1 in `(n_leds - 1)` is needed,
	// but that's how LEDstream on the Arduino expects it
	let count_high = ((n_leds - 1) >> 8) as u8;  // LED count high byte
	let count_low = ((n_leds - 1) & 0xff) as u8; // LED count low byte
	vec![
		'A' as u8,
		'd' as u8,
		'a' as u8,

		count_high,
		count_low,

		count_high ^ count_low ^ 0x55, // Checksum
	]
}

struct FrameTimer {
	before: f64,
	last_frame_dt: f64,
}
impl FrameTimer {
	fn new() -> FrameTimer {
		FrameTimer{ before: time::precise_time_s(), last_frame_dt: 0.0}
	}

	fn last_frame_dt(&self) -> f64 {
		self.last_frame_dt
	}

	fn dt_to_now(&mut self) -> f64 {
		let now = time::precise_time_s();
		let dt = now - self.before;
		if dt >= 0.0 {
			dt
		} else {
			self.before = now;
			0.0
		}
	}

	fn tick(&mut self) {
		let now = time::precise_time_s();
		self.last_frame_dt = partial_max(now - self.before, 0.0).unwrap_or(0.0);
		self.before = now;
	}
}

// Initialize a thread for serial writing given a serial port, baud rate, header to write before
// each data write, and buffer with led colors.
fn init_write_thread(port: String, baud_rate: u32, header: Vec<u8>, pixel_buf: Vec<RGB8>)
	-> (Sender<Vec<RGB8>>, Receiver<Vec<RGB8>>)
{
	use std::io::Write;

	let mut serial_con = serial::Connection::new(port, baud_rate);
	serial_con.open().unwrap();

	let (from_write_thread_tx, from_write_thread_rx) = channel();
	let (to_write_thread_tx, to_write_thread_rx) = channel::<Vec<RGB8>>();

	thread::spawn(move || loop {
		let pixel_buf = color::rgbs_to_bytes(to_write_thread_rx.recv().unwrap());

		match serial_con.write(header.as_slice()) {
			Ok(hn) if hn == header.len() => match serial_con.write(pixel_buf.as_slice())
			{
				Ok(bn) if bn == pixel_buf.len() => (),
				Ok(_) => println!("Failed to write all bytes of RGB data"),
				Err(e) => println!("Failed to write RGB data, {}", e)
			},
			Ok(_) => println!("Failed to write all bytes in header"),
			Err(e) => println!("Failed to write header, {}", e)
		}

		from_write_thread_tx.send(color::bytes_to_rgbs(pixel_buf)).unwrap();
	});
	to_write_thread_tx.send(pixel_buf).unwrap();

	(to_write_thread_tx, from_write_thread_rx)
}

fn main() {
	use capture::CaptureResult::*;

	// Parse the HyperCon json config
	let config = config::parse_config();
	let leds = config.leds.as_slice();

	let mut led_transformers: Vec<_> = repeat(Vec::with_capacity(2)).take(leds.len()).collect();

	// Add transforms from config to each led in matching vec
	for transform_conf in config.color.transform.iter() {
		let hsv_transformer = if !transform_conf.hsv.is_default() {
			Some(&transform_conf.hsv)
		} else { None };

		let rgb_transformer = if !(transform_conf.red.is_default() &&
			transform_conf.green.is_default() &&
			transform_conf.red.is_default())
		{
			Some(RgbTransformer{r: &transform_conf.red,
				g: &transform_conf.green,
				b: &transform_conf.blue})
		} else { None };

		for range in parse_led_indices(transform_conf.leds.as_slice(), leds.len()).iter() {
			for transforms in led_transformers[*range].iter_mut() {
				transforms.push((rgb_transformer.clone(), hsv_transformer));
			}
		}
	}

	// Do serial writing on own thread as to not block.
	let (write_thread_tx, write_thread_rx) = {
		// Header to write before led data
		let out_header = new_pixel_buf_header(leds.len() as u16);

		// Skeleton for the output led pixel buffer to write to arduino
		let out_pixels = repeat(RGB8{r: 0, g: 0, b: 0}).take(leds.len()).collect();

		init_write_thread(config.device.output.clone(), config.device.rate.clone(),
			out_header, out_pixels)
	};

	let mut capturer = Capturer::new();
	capturer.frame.set_resize_dimensions(
		(config.framegrabber.width, config.framegrabber.height));

	let capture_frame_interval = 1.0 / config.framegrabber.frequency_Hz;
	capturer.set_timeout((1000.0 * capture_frame_interval) as u32);
	let led_refresh_interval = 1.0 / config.color.smoothing.update_frequency;

	// Function to use when smoothing led colors
	type SmoothFn = fn(&RGB8, RGB8, f64) -> RGB8;
	let smooth = match config.color.smoothing.type_.as_slice() {
		"linear" => color::linear_smooth as SmoothFn,
		_ => color::no_smooth as SmoothFn,
	};
	// max w/ 1 to avoid divide by zero
	let smooth_time_const = max(config.color.smoothing.time_ms, 1) as f64 / 1000.0;

	let mut capture_timer = FrameTimer::new();
	let mut led_refresh_timer = FrameTimer::new();
	let mut diag_timer = FrameTimer::new();
	let mut diag_i = 0;
	loop {
		led_refresh_timer.tick();

		// Don't capture frame if going faster than frame limit,
		// but still proceed to smooth leds
		if capture_timer.dt_to_now() > capture_frame_interval {
			// If something goes wrong, reuse last frame
			match capturer.capture_frame() {
				CrOk => (),
				// Access Denied means we are probably in fullscreen app with
				// restricted access, sleep until we have access again
				CrAccessDenied => {
					println!("Access Denied");
					timer::sleep(Duration::seconds(2))
				},
				// Should be handled automatically in DXGCap
				CrAccessLost => println!("Access to desktop duplication lost"),
				CrTimeout => (),
				CrFail => println!("Unexpected failure when capturing screen"),
			}
			capture_timer.tick();
		}

		let diag_bap = time::precise_time_s();

		let mut out_pixels = write_thread_rx.recv().unwrap();

		let smooth_factor = led_refresh_timer.last_frame_dt() / smooth_time_const;
		for (to_pixel, out_buf_pixel) in leds.iter()
			.map(|led| {
				capturer.frame.average_color(&led)
			})
			.zip(led_transformers.iter())
			.map(|(led_rgb, transformers)|
				transformers.iter().fold(box led_rgb as Box<Pixel>,
					|mut led_color, &(ref opt_rgb_tr, ref opt_hsv_tr)|
				{
					if let &Some(ref rgb_tr) = opt_rgb_tr {
						led_color = box led_color.rgb_transform(rgb_tr)
							as Box<Pixel>;
					}
					if let &Some(ref hsv_tr) = opt_hsv_tr {
						box led_color.hsv_transform(hsv_tr)
							as Box<Pixel>
					} else {
						led_color
					}
				})
				.to_rgb())
			.zip(out_pixels.iter_mut())
		{
			*out_buf_pixel = smooth(out_buf_pixel, to_pixel, smooth_factor);
		}

		let diag_aap = time::precise_time_s();

		write_thread_tx.send(out_pixels).unwrap();

		diag_i += 1;
		if diag_i >= 120 {
			diag_timer.tick();
			println!("ap fps: {}", 1.0 / (diag_aap-diag_bap));
			println!("avg fps: {}\n", 1.0 / (diag_timer.last_frame_dt() / 120.0));
			diag_i = 0;
		}

		let time_left = led_refresh_interval - led_refresh_timer.dt_to_now();
		if time_left > 0.0 {
			timer::sleep(Duration::microseconds((time_left * 1_000_000.0) as i64));
		}
	}
}<|endoftext|>