<commit_before>import std::io;
import std::_vec;
import std::_str;

tag boxtype {box_h; box_v; box_hv; box_align;}
tag contexttype {cx_h; cx_v;}
tag scantype {scan_hv; scan_h; scan_none;}

tag token {
  brk(uint);
  hardbrk;
  word(str);
  cword(str); // closing token
  open(boxtype, uint);
  close;
}

type context = rec(contexttype tp, uint indent);

type ps = @rec(mutable vec[context] context,
               uint width,
               io::writer out,
               mutable uint col,
               mutable uint spaces,
               mutable vec[token] buffered,
               mutable scantype scanning,
               mutable vec[boxtype] scandepth,
               mutable uint scancol,
               mutable bool start_of_line,
               mutable bool start_of_box,
               mutable bool potential_brk);

fn mkstate(io::writer out, uint width) -> ps {
  let vec[context] stack = vec(rec(tp=cx_v, indent=0u));
  let vec[token] buff = vec();
  let vec[boxtype] sd = vec();
  ret @rec(mutable context=stack,
           width=width,
           out=out,
           mutable col=0u,
           mutable spaces=0u,
           mutable buffered=buff,
           mutable scanning=scan_none,
           mutable scandepth=sd,
           mutable scancol=0u,
           mutable start_of_line=true,
           mutable start_of_box=true,
           mutable potential_brk=false);
}

fn write_spaces(ps p, uint i) {
  while (i > 0u) {
    i -= 1u;
    p.out.write_str(" ");
  }
}

fn push_context(ps p, contexttype tp, uint indent) {
  before_print(p, false);
  _vec::push[context](p.context, rec(tp=tp, indent=indent));
  p.start_of_box = true;
}

fn pop_context(ps p) {
  _vec::pop[context](p.context);
}

fn add_token(ps p, token tok) {
  if (p.width == 0u) {direct_token(p, tok);}
  else if (p.scanning == scan_none) {do_token(p, tok);}
  else {buffer_token(p, tok);}
}

fn direct_token(ps p, token tok) {
  alt (tok) {
    case (brk(?sz)) {write_spaces(p, sz);}
    case (word(?w)) {p.out.write_str(w);}
    case (cword(?w)) {p.out.write_str(w);}
    case (_) {}
  }
}

fn buffer_token(ps p, token tok) {
  p.buffered += vec(tok);
  auto col = p.scancol;
  p.scancol = col + token_size(tok);
  if (p.scancol > p.width) {
    finish_scan(p, false);
  } else {
    alt (tok) {
      case (open(?tp,_)) {
        _vec::push[boxtype](p.scandepth, tp);
        if (p.scanning == scan_h) {
          if (tp == box_h) {
            check_potential_brk(p);
          }
        }
      }
      case (close) {
        _vec::pop[boxtype](p.scandepth);
        if (_vec::len[boxtype](p.scandepth) == 0u) {
          finish_scan(p, true);
        }
      }
      case (brk(_)) {
        if (p.scanning == scan_h) {
          if (p.scandepth.(_vec::len[boxtype](p.scandepth)-1u) == box_v) {
            finish_scan(p, true);
          }
        }
      }
      case (_) {}
    }
  }
}

fn check_potential_brk(ps p) {
  for (boxtype tp in p.scandepth) {
    if (tp != box_h) {ret;}
  }
  p.potential_brk = true;
}

fn finish_scan(ps p, bool fits) {
  auto buf = p.buffered;
  auto front = _vec::shift[token](buf);
  auto chosen_tp = cx_h;
  if (!fits) {chosen_tp = cx_v;}
  alt (front) {
    case (open(box_hv, ?ind)) {
      push_context(p, chosen_tp, base_indent(p) + ind);
    }
    case (open(box_align, _)) {
      push_context(p, chosen_tp, p.col);
    }
    case (open(box_h, ?ind)) {
      if (!fits && !p.start_of_box && !p.start_of_line && !p.potential_brk) {
        line_break(p);
      }
      push_context(p, cx_h, base_indent(p) + ind);
    }
  }
  p.scandepth = vec();
  p.scanning = scan_none;
  for (token t in buf) { add_token(p, t); }
}

fn start_scan(ps p, token tok, scantype tp) {
  p.buffered = vec();
  p.scancol = p.col;
  p.scanning = tp;
  buffer_token(p, tok);
  p.potential_brk = false;
}

fn cur_context(ps p) -> context {
  ret p.context.(_vec::len[context](p.context)-1u);
}
fn base_indent(ps p) -> uint {
  auto i = _vec::len[context](p.context);
  while (i > 0u) {
    i -= 1u;
    auto cx = p.context.(i);
    if (cx.tp == cx_v) {ret cx.indent;}
  }
}

fn cx_is(contexttype a, contexttype b) -> bool {
  if (a == b) {ret true;}
  else {ret false;}
}
fn box_is(boxtype a, boxtype b) -> bool {
  if (a == b) {ret true;}
  else {ret false;}
}

fn do_token(ps p, token tok) {
  auto start_of_box = p.start_of_box;
  p.start_of_box = false;
  alt (tok) {
    case (brk(?sz)) {
      if (cx_is(cur_context(p).tp, cx_v) || sz + p.col > p.width) {
        line_break(p);
      }
      else {
        p.spaces += sz;
      }
    }
    case (hardbrk) {
      line_break(p);
    }
    case (word(?w)) {
      auto len = _str::char_len(w);
      if (len + p.col + p.spaces > p.width && !start_of_box &&
          !p.start_of_line) {
        line_break(p);
      }
      before_print(p, false);
      p.out.write_str(w);
      p.col += len;
    }
    case (cword(?w)) {
      before_print(p, true);
      p.out.write_str(w);
      p.col += _str::char_len(w);
    }
    case (open(?tp, ?indent)) {
      if (tp == box_v) {
        push_context(p, cx_v, base_indent(p) + indent);
      } else if (box_is(tp, box_h) && cx_is(cur_context(p).tp, cx_v)) {
        push_context(p, cx_h, base_indent(p) + indent);
      } else if (tp == box_h) {
        p.start_of_box = start_of_box;
        start_scan(p, tok, scan_h);
      } else {
        p.start_of_box = start_of_box;
        start_scan(p, tok, scan_hv);
      }
    }
    case (close) {
      pop_context(p);
    }
  }
}

fn line_break(ps p) {
  p.out.write_str("\n");
  p.col = 0u;
  p.spaces = cur_context(p).indent;
  p.start_of_line = true;
}

fn before_print(ps p, bool closing) {
  if (p.start_of_line) {
    p.start_of_line = false;
    if (closing) {p.spaces = base_indent(p);}
    else {p.spaces = cur_context(p).indent;}
  }
  if (p.spaces > 0u) {
    write_spaces(p, p.spaces);
    p.col += p.spaces;
    p.spaces = 0u;
  }
}

fn token_size(token tok) -> uint {
  alt (tok) {
    case (brk(?sz)) {ret sz;}
    case (hardbrk) {ret 0xFFFFFFu;}
    case (word(?w)) {ret _str::char_len(w);}
    case (cword(?w)) {ret _str::char_len(w);}
    case (open(_, _)) {ret 0u;}
    case (close) {ret 0u;}
  }
}

fn box(ps p, uint indent) {add_token(p, open(box_hv, indent));}
fn abox(ps p) {add_token(p, open(box_align, 0u));}
fn vbox(ps p, uint indent) {add_token(p, open(box_v, indent));}
fn hbox(ps p, uint indent) {add_token(p, open(box_h, indent));}
fn end(ps p) {add_token(p, close);}
fn wrd(ps p, str wrd) {add_token(p, word(wrd));}
fn cwrd(ps p, str wrd) {add_token(p, cword(wrd));}
fn space(ps p) {add_token(p, brk(1u));}
fn spaces(ps p, uint n) {add_token(p, brk(n));}
fn line(ps p) {add_token(p, brk(0u));}
fn hardbreak(ps p) {add_token(p, hardbrk);}
<commit_msg>make the return-checker happy about pretty::pp::base_indent<commit_after>import std::io;
import std::_vec;
import std::_str;

tag boxtype {box_h; box_v; box_hv; box_align;}
tag contexttype {cx_h; cx_v;}
tag scantype {scan_hv; scan_h; scan_none;}

tag token {
  brk(uint);
  hardbrk;
  word(str);
  cword(str); // closing token
  open(boxtype, uint);
  close;
}

type context = rec(contexttype tp, uint indent);

type ps = @rec(mutable vec[context] context,
               uint width,
               io::writer out,
               mutable uint col,
               mutable uint spaces,
               mutable vec[token] buffered,
               mutable scantype scanning,
               mutable vec[boxtype] scandepth,
               mutable uint scancol,
               mutable bool start_of_line,
               mutable bool start_of_box,
               mutable bool potential_brk);

fn mkstate(io::writer out, uint width) -> ps {
  let vec[context] stack = vec(rec(tp=cx_v, indent=0u));
  let vec[token] buff = vec();
  let vec[boxtype] sd = vec();
  ret @rec(mutable context=stack,
           width=width,
           out=out,
           mutable col=0u,
           mutable spaces=0u,
           mutable buffered=buff,
           mutable scanning=scan_none,
           mutable scandepth=sd,
           mutable scancol=0u,
           mutable start_of_line=true,
           mutable start_of_box=true,
           mutable potential_brk=false);
}

fn write_spaces(ps p, uint i) {
  while (i > 0u) {
    i -= 1u;
    p.out.write_str(" ");
  }
}

fn push_context(ps p, contexttype tp, uint indent) {
  before_print(p, false);
  _vec::push[context](p.context, rec(tp=tp, indent=indent));
  p.start_of_box = true;
}

fn pop_context(ps p) {
  _vec::pop[context](p.context);
}

fn add_token(ps p, token tok) {
  if (p.width == 0u) {direct_token(p, tok);}
  else if (p.scanning == scan_none) {do_token(p, tok);}
  else {buffer_token(p, tok);}
}

fn direct_token(ps p, token tok) {
  alt (tok) {
    case (brk(?sz)) {write_spaces(p, sz);}
    case (word(?w)) {p.out.write_str(w);}
    case (cword(?w)) {p.out.write_str(w);}
    case (_) {}
  }
}

fn buffer_token(ps p, token tok) {
  p.buffered += vec(tok);
  auto col = p.scancol;
  p.scancol = col + token_size(tok);
  if (p.scancol > p.width) {
    finish_scan(p, false);
  } else {
    alt (tok) {
      case (open(?tp,_)) {
        _vec::push[boxtype](p.scandepth, tp);
        if (p.scanning == scan_h) {
          if (tp == box_h) {
            check_potential_brk(p);
          }
        }
      }
      case (close) {
        _vec::pop[boxtype](p.scandepth);
        if (_vec::len[boxtype](p.scandepth) == 0u) {
          finish_scan(p, true);
        }
      }
      case (brk(_)) {
        if (p.scanning == scan_h) {
          if (p.scandepth.(_vec::len[boxtype](p.scandepth)-1u) == box_v) {
            finish_scan(p, true);
          }
        }
      }
      case (_) {}
    }
  }
}

fn check_potential_brk(ps p) {
  for (boxtype tp in p.scandepth) {
    if (tp != box_h) {ret;}
  }
  p.potential_brk = true;
}

fn finish_scan(ps p, bool fits) {
  auto buf = p.buffered;
  auto front = _vec::shift[token](buf);
  auto chosen_tp = cx_h;
  if (!fits) {chosen_tp = cx_v;}
  alt (front) {
    case (open(box_hv, ?ind)) {
      push_context(p, chosen_tp, base_indent(p) + ind);
    }
    case (open(box_align, _)) {
      push_context(p, chosen_tp, p.col);
    }
    case (open(box_h, ?ind)) {
      if (!fits && !p.start_of_box && !p.start_of_line && !p.potential_brk) {
        line_break(p);
      }
      push_context(p, cx_h, base_indent(p) + ind);
    }
  }
  p.scandepth = vec();
  p.scanning = scan_none;
  for (token t in buf) { add_token(p, t); }
}

fn start_scan(ps p, token tok, scantype tp) {
  p.buffered = vec();
  p.scancol = p.col;
  p.scanning = tp;
  buffer_token(p, tok);
  p.potential_brk = false;
}

fn cur_context(ps p) -> context {
  ret p.context.(_vec::len[context](p.context)-1u);
}
fn base_indent(ps p) -> uint {
  auto i = _vec::len[context](p.context);
  while (i > 0u) {
    i -= 1u;
    auto cx = p.context.(i);
    if (cx.tp == cx_v) {ret cx.indent;}
  }
  ret 0u;
}

fn cx_is(contexttype a, contexttype b) -> bool {
  if (a == b) {ret true;}
  else {ret false;}
}
fn box_is(boxtype a, boxtype b) -> bool {
  if (a == b) {ret true;}
  else {ret false;}
}

fn do_token(ps p, token tok) {
  auto start_of_box = p.start_of_box;
  p.start_of_box = false;
  alt (tok) {
    case (brk(?sz)) {
      if (cx_is(cur_context(p).tp, cx_v) || sz + p.col > p.width) {
        line_break(p);
      }
      else {
        p.spaces += sz;
      }
    }
    case (hardbrk) {
      line_break(p);
    }
    case (word(?w)) {
      auto len = _str::char_len(w);
      if (len + p.col + p.spaces > p.width && !start_of_box &&
          !p.start_of_line) {
        line_break(p);
      }
      before_print(p, false);
      p.out.write_str(w);
      p.col += len;
    }
    case (cword(?w)) {
      before_print(p, true);
      p.out.write_str(w);
      p.col += _str::char_len(w);
    }
    case (open(?tp, ?indent)) {
      if (tp == box_v) {
        push_context(p, cx_v, base_indent(p) + indent);
      } else if (box_is(tp, box_h) && cx_is(cur_context(p).tp, cx_v)) {
        push_context(p, cx_h, base_indent(p) + indent);
      } else if (tp == box_h) {
        p.start_of_box = start_of_box;
        start_scan(p, tok, scan_h);
      } else {
        p.start_of_box = start_of_box;
        start_scan(p, tok, scan_hv);
      }
    }
    case (close) {
      pop_context(p);
    }
  }
}

fn line_break(ps p) {
  p.out.write_str("\n");
  p.col = 0u;
  p.spaces = cur_context(p).indent;
  p.start_of_line = true;
}

fn before_print(ps p, bool closing) {
  if (p.start_of_line) {
    p.start_of_line = false;
    if (closing) {p.spaces = base_indent(p);}
    else {p.spaces = cur_context(p).indent;}
  }
  if (p.spaces > 0u) {
    write_spaces(p, p.spaces);
    p.col += p.spaces;
    p.spaces = 0u;
  }
}

fn token_size(token tok) -> uint {
  alt (tok) {
    case (brk(?sz)) {ret sz;}
    case (hardbrk) {ret 0xFFFFFFu;}
    case (word(?w)) {ret _str::char_len(w);}
    case (cword(?w)) {ret _str::char_len(w);}
    case (open(_, _)) {ret 0u;}
    case (close) {ret 0u;}
  }
}

fn box(ps p, uint indent) {add_token(p, open(box_hv, indent));}
fn abox(ps p) {add_token(p, open(box_align, 0u));}
fn vbox(ps p, uint indent) {add_token(p, open(box_v, indent));}
fn hbox(ps p, uint indent) {add_token(p, open(box_h, indent));}
fn end(ps p) {add_token(p, close);}
fn wrd(ps p, str wrd) {add_token(p, word(wrd));}
fn cwrd(ps p, str wrd) {add_token(p, cword(wrd));}
fn space(ps p) {add_token(p, brk(1u));}
fn spaces(ps p, uint n) {add_token(p, brk(n));}
fn line(ps p) {add_token(p, brk(0u));}
fn hardbreak(ps p) {add_token(p, hardbrk);}
<|endoftext|>


<commit_before>#![feature(phase, if_let)]

extern crate quire;
extern crate argparse;
extern crate serialize;
extern crate regex;
#[phase(plugin)] extern crate regex_macros;
#[phase(plugin, link)] extern crate log;

extern crate config;
extern crate container;

use std::io::stderr;
use std::os::{getcwd, set_exit_status};
use config::find_config;
use container::signal;
use argparse::{ArgumentParser, Store, List};

mod list;
mod user;
mod network;

pub fn run() -> int {
    let mut err = stderr();
    let mut cname = "".to_string();
    let mut args = vec!();
    {
        let mut ap = ArgumentParser::new();
        ap.set_description("
            Runs a command in container, optionally builds container if that
            does not exists or outdated.

            Run `vagga` without arguments to see the list of commands.
            ");
        ap.refer(&mut cname)
          .add_argument("command", box Store::<String>,
                "A vagga command to run");
        ap.refer(&mut args)
          .add_argument("args", box List::<String>,
                "Arguments for the command");
        ap.stop_on_first_argument(true);
        match ap.parse_args() {
            Ok(()) => {}
            Err(0) => return 0,
            Err(_) => return 122,
        }
    }

    let workdir = getcwd();

    let (config, cfg_dir) = match find_config(&workdir) {
        Ok(tup) => tup,
        Err(e) => {
            err.write_line(e.as_slice()).ok();
            return 126;
        }
    };
    let int_workdir = workdir.path_relative_from(&cfg_dir)
                             .unwrap_or(Path::new("."));

    let result:Result<int, String> = match cname.as_slice() {
        "" => {
            err.write_line("Available commands:").ok();
            for (k, cmd) in config.commands.iter() {
                err.write_str("    ").ok();
                err.write_str(k.as_slice()).ok();
                match cmd.description() {
                    Some(ref val) => {
                        if k.len() > 19 {
                            err.write_str("\n                        ").ok();
                        } else {
                            for _ in range(k.len(), 19) {
                                err.write_char(' ').ok();
                            }
                            err.write_char(' ').ok();
                        }
                        err.write_str(val.as_slice()).ok();
                    }
                    None => {}
                }
                err.write_char('\n').ok();
            }
            return 127;
        }
        "_create_netns" => {
            network::create_netns(&config, args)
        }
        "_list" => {
            list::print_list(&config, args)
        }
        "_run" | "_build" => {
            user::run_simple_command(&int_workdir, cname, args)
        }
        _ => {
            user::run_user_command(&config, &int_workdir, cname, args)
        }
    };

    match result {
        Ok(rc) => {
            return rc;
        }
        Err(text) =>  {
            err.write_line(text.as_slice()).ok();
            return 121;
        }
    }
}

fn main() {
    signal::block_all();
    let val = run();
    set_exit_status(val);
}
<commit_msg>Fix _build_shell command<commit_after>#![feature(phase, if_let)]

extern crate quire;
extern crate argparse;
extern crate serialize;
extern crate libc;
extern crate regex;
#[phase(plugin)] extern crate regex_macros;
#[phase(plugin, link)] extern crate log;

extern crate config;
extern crate container;

use std::io::stderr;
use std::os::{getcwd, set_exit_status};
use config::find_config;
use container::signal;
use argparse::{ArgumentParser, Store, List};

mod list;
mod user;
mod network;

pub fn run() -> int {
    let mut err = stderr();
    let mut cname = "".to_string();
    let mut args = vec!();
    {
        let mut ap = ArgumentParser::new();
        ap.set_description("
            Runs a command in container, optionally builds container if that
            does not exists or outdated.

            Run `vagga` without arguments to see the list of commands.
            ");
        ap.refer(&mut cname)
          .add_argument("command", box Store::<String>,
                "A vagga command to run");
        ap.refer(&mut args)
          .add_argument("args", box List::<String>,
                "Arguments for the command");
        ap.stop_on_first_argument(true);
        match ap.parse_args() {
            Ok(()) => {}
            Err(0) => return 0,
            Err(_) => return 122,
        }
    }

    let workdir = getcwd();

    let (config, cfg_dir) = match find_config(&workdir) {
        Ok(tup) => tup,
        Err(e) => {
            err.write_line(e.as_slice()).ok();
            return 126;
        }
    };
    let int_workdir = workdir.path_relative_from(&cfg_dir)
                             .unwrap_or(Path::new("."));

    let result:Result<int, String> = match cname.as_slice() {
        "" => {
            err.write_line("Available commands:").ok();
            for (k, cmd) in config.commands.iter() {
                err.write_str("    ").ok();
                err.write_str(k.as_slice()).ok();
                match cmd.description() {
                    Some(ref val) => {
                        if k.len() > 19 {
                            err.write_str("\n                        ").ok();
                        } else {
                            for _ in range(k.len(), 19) {
                                err.write_char(' ').ok();
                            }
                            err.write_char(' ').ok();
                        }
                        err.write_str(val.as_slice()).ok();
                    }
                    None => {}
                }
                err.write_char('\n').ok();
            }
            return 127;
        }
        "_create_netns" => {
            network::create_netns(&config, args)
        }
        "_list" => {
            list::print_list(&config, args)
        }
        "_run" | "_build" | "_build_shell" => {
            user::run_simple_command(&int_workdir, cname, args)
        }
        _ => {
            user::run_user_command(&config, &int_workdir, cname, args)
        }
    };

    match result {
        Ok(rc) => {
            return rc;
        }
        Err(text) =>  {
            err.write_line(text.as_slice()).ok();
            return 121;
        }
    }
}

fn main() {
    signal::block_all();
    let val = run();
    set_exit_status(val);
}
<|endoftext|>


<commit_before>#![feature(asm, unboxed_closures, unsafe_destructor)]

use std::collections::RingBuf;
use std::default::Default;
use std::mem;
use std::rt::heap::allocate;
use global::global;
use spinlock::{Spinlock, SpinlockGuard};
use thread::{Thread, ThreadState};
use fn_box::FnBox;

mod thread;
mod fn_box;
mod global;
mod thread_local;
mod spinlock;

pub fn spawn<F, R>(f:F) -> Future<R> where F: FnOnce()->R, F:Send {
    global().scheduler.spawn(f)
}

/// Can we park the current thread or does it hold an important lock or borrows thread local data
fn current_thread_parkable() -> bool {
    global().scheduler.locked_by_current_thread() ||
    thread_local::data().try_borrow_mut().map_or(false, |d| d.parkable)
}

pub unsafe fn init() {
    global::init();
    thread_local::init(thread_local::Data{
        current_thread: Thread{
            id: mem::transmute(1us),
            state: ThreadState::Running,
        },
        parkable: true,
    })
}

pub unsafe fn reschedule(current_rsp: uint) -> ! {
    if !current_thread_parkable() {
        pop_registers_and_iret(current_rsp)
    } else {
        inner(current_rsp)
    }

    /* OLD */
    // we must not be interrupted while using the scheduler stack
    // TODO ::disable_interrupts();
    // switch stack so we can park current thread
    //call_on_stack(inner, current_rsp, scheduler_stack_top());

    fn inner(current_rsp: uint) -> ! {

        let current_thread = &mut thread_local::data().borrow_mut().current_thread;
        let scheduler = &global().scheduler;

        current_thread.state = ThreadState::Active{rsp: current_rsp};

        let current = mem::replace(current_thread, scheduler.schedule());
        scheduler.park(current);

        start_current_thread()
    }
}

pub unsafe fn schedule() -> ! {
    assert!(current_thread_parkable());
    inner();
    /* OLD */
    // we must not be interrupted while using the scheduler stack
    // TODO ::disable_interrupts();
    // switch to scheduler stack (the stack of current thread could be to full/small)
    //call_on_stack(inner, scheduler_stack_top());

    /*
    unsafe fn call_on_stack(function: fn() -> !, stack_top: uint) -> ! {
        asm!("call $0;" :: "r"(function), "{rsp}"(stack_top) :: "intel", "volatile");
        panic!("diverging fn returned");
    }
    */

    fn inner() -> ! {
        let old = mem::replace(&mut thread_local::data().borrow_mut().current_thread,
            global().scheduler.schedule());
        // TODO FIXME kill old thread and deallocate stack
        start_current_thread()
    }
}

#[allow(improper_ctypes)]
extern {
    fn pop_registers_and_iret(rsp: uint) -> !;
}

fn start_current_thread() -> ! {
    fn new_stack() -> uint {
        const NEW_STACK_SIZE: uint = 4096*2;
        let stack_bottom = unsafe{allocate(NEW_STACK_SIZE, 4096)};
        let stack_top = stack_bottom as uint + NEW_STACK_SIZE;
        stack_top
    }

    fn invoke(function: Box<FnBox() + Send>) -> ! {
        // TODO unsafe{::enable_interrupts()};
        function.call_once(());
        unsafe{asm!("int $$66" :::: "volatile")};
        unreachable!();
    }
  let current_state = mem::replace(&mut thread_local::data().borrow_mut().current_thread.state,
        ThreadState::Running);
    match current_state {
        ThreadState::Active{rsp} => {
            unsafe{pop_registers_and_iret(rsp)}
        },
        ThreadState::New{function} => {
            println!("new");
            let new_stack_top = new_stack();
            unsafe{call_on_stack(invoke, function, new_stack_top)}
        },
        ThreadState::Running => panic!("current thread must not be running"),
    }
}

/*
fn scheduler_stack_top() -> uint {
    const SCHEDULER_STACK_SIZE: uint = 4096;
    static SCHEDULER_STACK: [u8; SCHEDULER_STACK_SIZE] = [0; SCHEDULER_STACK_SIZE];

    &SCHEDULER_STACK as *const [u8; 4096] as uint + SCHEDULER_STACK_SIZE
}
*/

#[inline(never)]
unsafe fn call_on_stack<Arg>(function: fn(Arg) -> !, arg: Arg, stack_top: uint) -> ! {
    asm!("call $0;" :: "r"(function), "{rdi}"(arg), "{rsp}"(stack_top) :
        : "intel", "volatile");
    panic!("diverging fn returned");
}

pub struct Future<T>;

pub struct GlobalScheduler {
    threads: Spinlock<RingBuf<Thread>>,
}

impl GlobalScheduler {
    pub fn new() -> GlobalScheduler {
        GlobalScheduler{
            threads: Spinlock::new(RingBuf::new()),
        }
    }

    fn spawn<F, R>(&self, f:F) -> Future<R> where F: FnOnce()->R, F: Send {

        //let (tx, rx) = channel();

        self.threads.lock().push_back(Thread::new(move |:| {
            /*tx.send_opt(*/ f();
        }));

        Future/*::from_receiver(rx)*/
    }

    fn park(&self, thread: Thread) {
        self.threads.lock().push_back(thread)
    }

    fn schedule(&self) -> Thread {
        self.threads.lock().pop_front().unwrap_or_default()
    }

    fn locked_by_current_thread(&self) -> bool {
        self.threads.held_by_current_thread()
    }
}<commit_msg>only reschedule if there is a waiting thread + some minor changes:<commit_after>#![feature(asm, unboxed_closures, unsafe_destructor)]

use std::collections::RingBuf;
use std::default::Default;
use std::mem;
use std::rt::heap::allocate;
use global::global;
use spinlock::{Spinlock, SpinlockGuard};
use thread::{Thread, ThreadState};
use fn_box::FnBox;

mod thread;
mod fn_box;
mod global;
mod thread_local;
mod spinlock;

pub fn spawn<F, R>(f:F) -> Future<R> where F: FnOnce()->R, F:Send {
    global().scheduler.spawn(f)
}

/// Can we park the current thread or does it hold an important lock or borrows thread local data
fn current_thread_parkable() -> bool {
    global().scheduler.locked_by_current_thread() ||
    thread_local::data().try_borrow_mut().map_or(false, |d| d.parkable)
}

pub unsafe fn init() {
    global::init();
    thread_local::init(thread_local::Data{
        current_thread: Thread{
            id: mem::transmute(1us),
            state: ThreadState::Running,
        },
        parkable: true,
    })
}

pub unsafe fn reschedule(current_rsp: uint) {
    if current_thread_parkable() {
        if let Some(new_thread) = global().scheduler.schedule() {

            let mut old_thread = replace_current_thread(new_thread);
            old_thread.state = ThreadState::Active{rsp: current_rsp};
            global().scheduler.park(old_thread);

            start_current_thread()
        }
    }
}

pub unsafe fn schedule() -> ! {
    if current_thread_parkable() {
        let new_thread = if let Some(thread) = global().scheduler.schedule() {
            thread
        } else {
            Default::default()
        };
        let old_thread = replace_current_thread(new_thread);
        // TODO FIXME kill old thread and deallocate stack
        start_current_thread()
    }
    panic!();
}

/// replaces the current thread with a new thread from scheduler and returns the old thread
fn replace_current_thread(new_thread: Thread) -> Thread {
    //println!("scheduling {:?}", new_thread.id);
    let current_thread = &mut thread_local::data().borrow_mut().current_thread;
    mem::replace(current_thread, new_thread)
}

#[allow(improper_ctypes)]
extern {
    fn pop_registers_and_iret(rsp: uint) -> !;
}

fn start_current_thread() -> ! {
    fn new_stack() -> uint {
        const NEW_STACK_SIZE: uint = 4096*2;
        let stack_bottom = unsafe{allocate(NEW_STACK_SIZE, 4096)};
        let stack_top = stack_bottom as uint + NEW_STACK_SIZE;
        stack_top
    }

    #[inline(never)]
    unsafe fn call_on_stack<Arg>(function: fn(Arg) -> !, arg: Arg, stack_top: uint) -> ! {
        asm!("call $0;" :: "r"(function), "{rdi}"(arg), "{rsp}"(stack_top) :
            : "intel", "volatile");
        panic!("diverging fn returned");
    }

    fn invoke(function: Box<FnBox() + Send>) -> ! {
        // TODO unsafe{::enable_interrupts()};
        function.call_once(());
        unsafe{asm!("int $$66" :::: "volatile")};
        unreachable!();
    }

    unsafe fn enable_interrupts() {
        asm!("sti" :::: "volatile");
    }

    let current_state = mem::replace(&mut thread_local::data().borrow_mut().current_thread.state,
        ThreadState::Running);

    unsafe{enable_interrupts()};

    match current_state {
        ThreadState::Active{rsp} => {
            unsafe{pop_registers_and_iret(rsp)}
        },
        ThreadState::New{function} => {
            //println!("new");
            let new_stack_top = new_stack();
            unsafe{call_on_stack(invoke, function, new_stack_top)}
        },
        ThreadState::Running => panic!("current thread must not be running"),
    }
}

pub struct Future<T>;

pub struct GlobalScheduler {
    threads: Spinlock<RingBuf<Thread>>,
}

impl GlobalScheduler {
    pub fn new() -> GlobalScheduler {
        GlobalScheduler{
            threads: Spinlock::new(RingBuf::new()),
        }
    }

    fn spawn<F, R>(&self, f:F) -> Future<R> where F: FnOnce()->R, F: Send {

        //let (tx, rx) = channel();

        self.threads.lock().push_back(Thread::new(move |:| {
            /*tx.send_opt(*/ f();
        }));

        Future/*::from_receiver(rx)*/
    }

    fn park(&self, thread: Thread) {
        self.threads.lock().push_back(thread)
    }

    fn schedule(&self) -> Option<Thread> {
        self.threads.lock().pop_front()
    }

    fn locked_by_current_thread(&self) -> bool {
        self.threads.held_by_current_thread()
    }
}<|endoftext|>


<commit_before>use std::f32::consts::PI;
use std::sync::Arc;

use uuid::Uuid;

use {Point2f, Point3f, Transform, Vector3f};
use interaction::{Interaction, SurfaceInteraction};
use light::{Light, LightFlags, VisibilityTester};
use paramset::ParamSet;
use scene::Scene;
use spectrum::Spectrum;

#[derive(Debug)]
pub struct DistantLight {
    pub id: Uuid,
    pub dir: Vector3f,
    pub emission_colour: Spectrum,
    w_center: Point3f,
    w_radius: f32,
}

impl DistantLight {
    pub fn new(dir: Vector3f, ec: Spectrum) -> DistantLight {
        DistantLight {
            id: Uuid::new_v4(),
            dir: dir.normalize(),
            emission_colour: ec,
            w_center: Point3f::new(0.0, 0.0, 0.0),
            w_radius: 100.0, // TODO
        }
    }

    pub fn create(l2w: &Transform, params: &mut ParamSet) -> Arc<Light + Send + Sync> {
        let L = params.find_one_spectrum("L", Spectrum::white());
        let scale = params.find_one_spectrum("scale", Spectrum::white());
        let from = params.find_one_point3f("from", Point3f::origin());
        let to = params.find_one_point3f("to", Point3f::new(0.0, 0.0, 1.0));
        let dir = from - to;
        Arc::new(DistantLight::new(l2w * &dir, L * scale))
    }
}

impl Light for DistantLight {
    fn id(&self) -> Uuid {
        self.id
    }

    fn preprocess(&mut self, scene: &Scene) {
        let (w_center, w_radius) = scene.world_bounds().bounding_sphere();
        self.w_center = w_center;
        self.w_radius = w_radius;
    }

    fn sample_li(
        &self,
        isect: &SurfaceInteraction,
        _u: &Point2f,
    ) -> (Spectrum, Vector3f, f32, VisibilityTester) {
        let p_outside = isect.p - self.dir * (2.0 * self.w_radius);
        (
            self.emission_colour,
            self.dir,
            1.0,
            VisibilityTester::new(isect.into(), Interaction::from_point(&p_outside)),
        )
    }

    fn pdf_li(&self, _si: &SurfaceInteraction, _wi: &Vector3f) -> f32 {
        0.0
    }

    fn n_samples(&self) -> u32 {
        1
    }

    fn flags(&self) -> LightFlags {
        LightFlags::DELTA_DIRECTION
    }

    fn power(&self) -> Spectrum {
        self.emission_colour * PI * self.w_radius * self.w_radius
    }
}
<commit_msg>Another fix for DistantLight<commit_after>use std::f32::consts::PI;
use std::sync::Arc;

use uuid::Uuid;

use {Point2f, Point3f, Transform, Vector3f};
use interaction::{Interaction, SurfaceInteraction};
use light::{Light, LightFlags, VisibilityTester};
use paramset::ParamSet;
use scene::Scene;
use spectrum::Spectrum;

#[derive(Debug)]
pub struct DistantLight {
    pub id: Uuid,
    pub dir: Vector3f,
    pub emission_colour: Spectrum,
    w_center: Point3f,
    w_radius: f32,
}

impl DistantLight {
    pub fn new(dir: Vector3f, ec: Spectrum) -> DistantLight {
        DistantLight {
            id: Uuid::new_v4(),
            dir: dir.normalize(),
            emission_colour: ec,
            w_center: Point3f::new(0.0, 0.0, 0.0),
            w_radius: 100.0, // TODO
        }
    }

    pub fn create(l2w: &Transform, params: &mut ParamSet) -> Arc<Light + Send + Sync> {
        let L = params.find_one_spectrum("L", Spectrum::white());
        let scale = params.find_one_spectrum("scale", Spectrum::white());
        let from = params.find_one_point3f("from", Point3f::origin());
        let to = params.find_one_point3f("to", Point3f::new(0.0, 0.0, 1.0));
        let dir = from - to;
        Arc::new(DistantLight::new(l2w * &dir, L * scale))
    }
}

impl Light for DistantLight {
    fn id(&self) -> Uuid {
        self.id
    }

    fn preprocess(&mut self, scene: &Scene) {
        let (w_center, w_radius) = scene.world_bounds().bounding_sphere();
        self.w_center = w_center;
        self.w_radius = w_radius;
    }

    fn sample_li(
        &self,
        isect: &SurfaceInteraction,
        _u: &Point2f,
    ) -> (Spectrum, Vector3f, f32, VisibilityTester) {
        let p_outside = isect.p + self.dir * (2.0 * self.w_radius);
        (
            self.emission_colour,
            self.dir,
            1.0,
            VisibilityTester::new(isect.into(), Interaction::from_point(&p_outside)),
        )
    }

    fn pdf_li(&self, _si: &SurfaceInteraction, _wi: &Vector3f) -> f32 {
        0.0
    }

    fn n_samples(&self) -> u32 {
        1
    }

    fn flags(&self) -> LightFlags {
        LightFlags::DELTA_DIRECTION
    }

    fn power(&self) -> Spectrum {
        self.emission_colour * PI * self.w_radius * self.w_radius
    }
}
<|endoftext|>


<commit_before>use serde_json::from_str;
use std::collections::HashMap;
use std::io::Read;
use std::fs::File;
use std::sync::mpsc;
use std::thread;
use std::result::Result;
use std::time::Duration;
use std::sync::Arc;

use {config, context, Message, Response};
use condition::Condition;
use context::base::BaseContextBuilder;
use context::{Context, ContextMap};
use context::linear::LinearContext;
use context::map::MapContext;
use dispatcher::request::Request;
use dispatcher::reactor::RequestReactor;
use dispatcher::{ResponseSender, ResponseHandle};
use dispatcher::demux::Demultiplexer;
use dispatcher::handlers;
pub use self::error::Error;
use reactor::{Event, Reactor, EventHandler};
use timer::Timer;

use self::exit_handler::ExitHandler;

const TIMER_STEP_MS: u64 = 100;

pub mod error;
mod exit_handler;
#[cfg(test)]
mod test;

pub struct Correlator {
    dispatcher_input_channel: mpsc::Sender<Request>,
    dispatcher_output_channel: mpsc::Receiver<Response>,
    dispatcher_thread_handle: thread::JoinHandle<()>,
    handlers: HashMap<ResponseHandle, Box<EventHandler<Response, mpsc::Sender<Request>>>>,
}

fn create_context(config_context: config::Context) -> Context {
    let config::Context{name, uuid, conditions, context_id, actions} = config_context;

    let base = BaseContextBuilder::new(uuid, conditions);
    let base = base.name(name);
    let base = base.actions(actions);
    let base = base.build();
    if let Some(context_id) = context_id {
        Context::Map(MapContext::new(base, context_id))
    } else {
        Context::Linear(LinearContext::from(base))
    }
}

fn create_context_map(contexts: Vec<config::Context>) -> ContextMap {
    let mut context_map = ContextMap::new();
    for i in contexts.into_iter() {
        let context: context::Context = create_context(i);
        context_map.insert(context);
    }
    context_map
}

impl Correlator {
    pub fn from_path(path: &str) -> Result<Correlator, Error> {
        let mut file = try!(File::open(path));
        let mut buffer = String::new();
        try!(file.read_to_string(&mut buffer));
        let contexts = try!(from_str::<Vec<config::Context>>(&buffer));
        trace!("Correlator: loading contexts from file; len={}",
               contexts.len());
        Ok(Correlator::new(contexts))
    }

    pub fn new(contexts: Vec<config::Context>) -> Correlator {
        let (dispatcher_input_channel, rx) = mpsc::channel();
        let (dispatcher_output_channel_tx, dispatcher_output_channel_rx) = mpsc::channel();
        let _ = Timer::from_chan(Duration::from_millis(TIMER_STEP_MS),
                                 dispatcher_input_channel.clone());

        let handle = thread::spawn(move || {
            let exit_condition = Condition::new(false);
            let dmux = Demultiplexer::new(rx, exit_condition.clone());
            let response_sender = Box::new(ResponseSender::new(dispatcher_output_channel_tx));

            let exit_handler = Box::new(handlers::exit::ExitEventHandler::new(exit_condition));
            let timer_event_handler = Box::new(handlers::timer::TimerEventHandler::new());
            let message_event_handler = Box::new(handlers::message::MessageEventHandler::new());

            let context_map = create_context_map(contexts);
            let mut reactor = RequestReactor::new(dmux, context_map, response_sender);
            reactor.register_handler(exit_handler);
            reactor.register_handler(timer_event_handler);
            reactor.register_handler(message_event_handler);
            reactor.handle_events();
            trace!("Correlator: dispatcher thread exited");
        });

        Correlator {
            dispatcher_input_channel: dispatcher_input_channel,
            dispatcher_output_channel: dispatcher_output_channel_rx,
            dispatcher_thread_handle: handle,
            handlers: HashMap::new(),
        }
    }

    pub fn register_handler(&mut self,
                            handler: Box<EventHandler<Response, mpsc::Sender<Request>>>) {
        self.handlers.insert(handler.handle(), handler);
    }

    pub fn push_message(&mut self, message: Message) -> Result<(), mpsc::SendError<Request>> {
        self.handle_events();
        self.dispatcher_input_channel.send(Request::Message(Arc::new(message)))
    }

    fn handle_event(&mut self, event: Response) {
        if let Some(handler) = self.handlers.get_mut(&event.handle()) {
            handler.handle_event(event, &mut self.dispatcher_input_channel);
        } else {
            trace!("no event handler found for handling a Response");
        }
    }

    pub fn handle_events(&mut self) {
        for i in self.dispatcher_output_channel.try_recv() {
            self.handle_event(i);
        }
    }

    pub fn stop(mut self) -> thread::Result<()> {
        self.handle_events();
        self.stop_dispatcher();
        self.dispatcher_thread_handle.join()
    }

    fn stop_dispatcher(&mut self) {
        let exit_condition = Condition::new(false);
        let exit_handler = Box::new(ExitHandler::new(exit_condition.clone()));
        self.register_handler(exit_handler);
        let _ = self.dispatcher_input_channel.send(Request::Exit);
        while !exit_condition.is_active() {
            if let Ok(event) = self.dispatcher_output_channel.recv() {
                self.handle_event(event);
            }
        }
    }
}
<commit_msg>correlator: optimize imports<commit_after>use serde_json::from_str;
use std::collections::HashMap;
use std::io::Read;
use std::fs::File;
use std::sync::mpsc;
use std::thread;
use std::result::Result;
use std::time::Duration;
use std::sync::Arc;

use {config, Message, Response};
use condition::Condition;
use context::base::BaseContextBuilder;
use context::{Context, ContextMap};
use context::linear::LinearContext;
use context::map::MapContext;
use dispatcher::request::Request;
use dispatcher::reactor::RequestReactor;
use dispatcher::{ResponseSender, ResponseHandle};
use dispatcher::demux::Demultiplexer;
use dispatcher::handlers;
pub use self::error::Error;
use reactor::{Event, Reactor, EventHandler};
use timer::Timer;

use self::exit_handler::ExitHandler;

const TIMER_STEP_MS: u64 = 100;

pub mod error;
mod exit_handler;
#[cfg(test)]
mod test;

pub struct Correlator {
    dispatcher_input_channel: mpsc::Sender<Request>,
    dispatcher_output_channel: mpsc::Receiver<Response>,
    dispatcher_thread_handle: thread::JoinHandle<()>,
    handlers: HashMap<ResponseHandle, Box<EventHandler<Response, mpsc::Sender<Request>>>>,
}

fn create_context(config_context: config::Context) -> Context {
    let config::Context{name, uuid, conditions, context_id, actions} = config_context;

    let base = BaseContextBuilder::new(uuid, conditions);
    let base = base.name(name);
    let base = base.actions(actions);
    let base = base.build();
    if let Some(context_id) = context_id {
        Context::Map(MapContext::new(base, context_id))
    } else {
        Context::Linear(LinearContext::from(base))
    }
}

fn create_context_map(contexts: Vec<config::Context>) -> ContextMap {
    let mut context_map = ContextMap::new();
    for i in contexts.into_iter() {
        let context: Context = create_context(i);
        context_map.insert(context);
    }
    context_map
}

impl Correlator {
    pub fn from_path(path: &str) -> Result<Correlator, Error> {
        let mut file = try!(File::open(path));
        let mut buffer = String::new();
        try!(file.read_to_string(&mut buffer));
        let contexts = try!(from_str::<Vec<config::Context>>(&buffer));
        trace!("Correlator: loading contexts from file; len={}",
               contexts.len());
        Ok(Correlator::new(contexts))
    }

    pub fn new(contexts: Vec<config::Context>) -> Correlator {
        let (dispatcher_input_channel, rx) = mpsc::channel();
        let (dispatcher_output_channel_tx, dispatcher_output_channel_rx) = mpsc::channel();
        let _ = Timer::from_chan(Duration::from_millis(TIMER_STEP_MS),
                                 dispatcher_input_channel.clone());

        let handle = thread::spawn(move || {
            let exit_condition = Condition::new(false);
            let dmux = Demultiplexer::new(rx, exit_condition.clone());
            let response_sender = Box::new(ResponseSender::new(dispatcher_output_channel_tx));

            let exit_handler = Box::new(handlers::exit::ExitEventHandler::new(exit_condition));
            let timer_event_handler = Box::new(handlers::timer::TimerEventHandler::new());
            let message_event_handler = Box::new(handlers::message::MessageEventHandler::new());

            let context_map = create_context_map(contexts);
            let mut reactor = RequestReactor::new(dmux, context_map, response_sender);
            reactor.register_handler(exit_handler);
            reactor.register_handler(timer_event_handler);
            reactor.register_handler(message_event_handler);
            reactor.handle_events();
            trace!("Correlator: dispatcher thread exited");
        });

        Correlator {
            dispatcher_input_channel: dispatcher_input_channel,
            dispatcher_output_channel: dispatcher_output_channel_rx,
            dispatcher_thread_handle: handle,
            handlers: HashMap::new(),
        }
    }

    pub fn register_handler(&mut self,
                            handler: Box<EventHandler<Response, mpsc::Sender<Request>>>) {
        self.handlers.insert(handler.handle(), handler);
    }

    pub fn push_message(&mut self, message: Message) -> Result<(), mpsc::SendError<Request>> {
        self.handle_events();
        self.dispatcher_input_channel.send(Request::Message(Arc::new(message)))
    }

    fn handle_event(&mut self, event: Response) {
        if let Some(handler) = self.handlers.get_mut(&event.handle()) {
            handler.handle_event(event, &mut self.dispatcher_input_channel);
        } else {
            trace!("no event handler found for handling a Response");
        }
    }

    pub fn handle_events(&mut self) {
        for i in self.dispatcher_output_channel.try_recv() {
            self.handle_event(i);
        }
    }

    pub fn stop(mut self) -> thread::Result<()> {
        self.handle_events();
        self.stop_dispatcher();
        self.dispatcher_thread_handle.join()
    }

    fn stop_dispatcher(&mut self) {
        let exit_condition = Condition::new(false);
        let exit_handler = Box::new(ExitHandler::new(exit_condition.clone()));
        self.register_handler(exit_handler);
        let _ = self.dispatcher_input_channel.send(Request::Exit);
        while !exit_condition.is_active() {
            if let Ok(event) = self.dispatcher_output_channel.recv() {
                self.handle_event(event);
            }
        }
    }
}
<|endoftext|>


<commit_before>extern crate gstreamer as gst;
use gstreamer::*;

extern crate glib;
use glib::ObjectExt;

extern crate url;
use url::Url;

use std::path::PathBuf;

use std::sync::mpsc::{Receiver, Sender};
use std::sync::{Arc, Mutex};

use super::{AlignedImage, AudioBuffer, AudioCaps, Chapter, MediaInfo, Timestamp};

pub enum ContextMessage {
    AsyncDone,
    Eos,
    FailedToOpenMedia,
    GotVideoWidget,
    HaveAudioBuffer(AudioBuffer),
    HaveVideoWidget(glib::Value),
    InitDone,
}

pub struct Context {
    pub pipeline: gst::Pipeline,

    pub path: PathBuf,
    pub file_name: String,
    pub name: String,

    pub info: Arc<Mutex<MediaInfo>>,
}

macro_rules! assign_str_tag(
    ($target:expr, $tags:expr, $TagType:ty) => {
        if $target.is_empty() {
            if let Some(tag) = $tags.get::<$TagType>() {
                $target = tag.get().unwrap().to_owned();
            }
        }
    };
);

impl Context {
    fn new(path: PathBuf) -> Self {
        Context{
            pipeline: gst::Pipeline::new("pipeline"),

            file_name: String::from(path.file_name().unwrap().to_str().unwrap()),
            name: String::from(path.file_stem().unwrap().to_str().unwrap()),
            path: path,

            info: Arc::new(Mutex::new(MediaInfo::new())),
        }
    }

    pub fn open_media_path(
        path: PathBuf,
        ctx_tx: Sender<ContextMessage>,
        ui_rx: Receiver<ContextMessage>,
    ) -> Result<Context, String>
    {
        println!("\n\n* Attempting to open {:?}", path);

        let ctx = Context::new(path);
        ctx.build_pipeline(&ctx_tx, ui_rx);
        ctx.register_bus_inspector(ctx_tx);

        match ctx.pause() {
            Ok(_) => Ok(ctx),
            Err(error) => Err(error),
        }
    }

    pub fn get_duration(&self) -> Timestamp {
        match self.pipeline.query_duration(gst::Format::Time) {
            Some(duration) => Timestamp::from_signed_nano(duration),
            None => Timestamp::new(),
        }
    }

    pub fn play(&self) -> Result<(), String> {
        if self.pipeline.set_state(gst::State::Playing) == gst::StateChangeReturn::Failure {
            return Err("Could not set media in palying state".into());
        }
        Ok(())
    }

    pub fn pause(&self) -> Result<(), String> {
        if self.pipeline.set_state(gst::State::Paused) == gst::StateChangeReturn::Failure {
            println!("could not set media in Paused state");
            return Err("Could not set media in Paused state".into());
        }
        Ok(())
    }

    pub fn stop(&self) {
        if self.pipeline.set_state(gst::State::Null) == gst::StateChangeReturn::Failure {
            println!("Could not set media in Null state");
            //return Err("could not set media in Null state".into());
        }
    }

    // TODO: handle errors
    // Uses ctx_tx & ui_rx to allow the UI integrate the GstGtkWidget
    fn build_pipeline(&self,
        ctx_tx: &Sender<ContextMessage>,
        ui_rx: Receiver<ContextMessage>
    )
    {
        let dec = gst::ElementFactory::make("uridecodebin", "input").unwrap();
        let url = match Url::from_file_path(self.path.as_path()) {
            Ok(url) => url.into_string(),
            Err(_) => "Failed to convert path to URL".to_owned(),
        };
        dec.set_property("uri", &gst::Value::from(&url)).unwrap();
        self.pipeline.add(&dec).unwrap();

        let pipeline_clone = self.pipeline.clone();
        // Need an Arc Mutex on ctx_tx because Rust considers this method
        // as a candidate for being called by multiple threads
        let ctx_tx_arc_mtx = Arc::new(Mutex::new(ctx_tx.clone()));
        let ui_rx_arc_mtx = Arc::new(Mutex::new(ui_rx));
        dec.connect_pad_added(move |_, src_pad| {
            let pipeline = &pipeline_clone;

            let caps = src_pad.get_current_caps().unwrap();
            let structure = caps.get_structure(0).unwrap();
            let name = structure.get_name();

            // TODO: build only one queue by stream type (audio / video)
            if name.starts_with("audio/") {
                let queue = gst::ElementFactory::make("queue", None).unwrap();
                let convert = gst::ElementFactory::make("audioconvert", None).unwrap();
                let resample = gst::ElementFactory::make("audioresample", None).unwrap();
                let sink = gst::ElementFactory::make("autoaudiosink", "audio_sink").unwrap();

                let elements = &[&queue, &convert, &resample, &sink];
                pipeline.add_many(elements).unwrap();
                gst::Element::link_many(elements).unwrap();

                for e in elements { e.sync_state_with_parent().unwrap(); }

                let sink_pad = queue.get_static_pad("sink").unwrap();
                assert_eq!(src_pad.link(&sink_pad), gst::PadLinkReturn::Ok);

                let audio_caps_mtx: Mutex<Option<AudioCaps>> = Mutex::new(None);
                let ctx_tx_arc_mtx_clone = ctx_tx_arc_mtx.clone();
                sink_pad.add_probe(PAD_PROBE_TYPE_BUFFER, move |sink_pad, probe_info| {
                    if let Some(PadProbeData::Buffer(ref buffer)) = probe_info.data {
                        // Retrieve audio caps the first time only
                        let mut audio_caps_opt = audio_caps_mtx.lock()
                            .expect("Failed to lock audio caps while receiving buffer");
                        let audio_caps = audio_caps_opt
                            .get_or_insert_with(|| AudioCaps::from_sink_pad(sink_pad));

                        let audio_buffer = AudioBuffer::from_gst_buffer(audio_caps, buffer);

                        ctx_tx_arc_mtx_clone.lock()
                            .expect("Failed to lock ctx_tx mutex, while transmitting audio buffer")
                            .send(ContextMessage::HaveAudioBuffer(audio_buffer))
                                .expect("Failed to transmit audio buffer");
                    };

                    PadProbeReturn::Ok
                });
            } else if name.starts_with("video/") {
                let queue = gst::ElementFactory::make("queue", None).unwrap();
                let convert = gst::ElementFactory::make("videoconvert", None).unwrap();
                let scale = gst::ElementFactory::make("videoscale", None).unwrap();

                let (sink, widget_val) = if let Some(gtkglsink) = ElementFactory::make("gtkglsink", None) {
                    let glsinkbin = ElementFactory::make("glsinkbin", "video_sink").unwrap();
                    glsinkbin.set_property("sink", &gtkglsink.to_value()).unwrap();
                    let widget_val = gtkglsink.get_property("widget").unwrap();
                    (glsinkbin, widget_val)
                } else {
                    let sink = ElementFactory::make("gtksink", "video_sink").unwrap();
                    let widget_val = sink.get_property("widget").unwrap();
                    (sink, widget_val)
                };

                // Pass the video_sink for integration in the UI
                ctx_tx_arc_mtx.lock()
                    .expect("Failed to lock ctx_tx mutex, while building the video queue")
                    .send(ContextMessage::HaveVideoWidget(widget_val))
                        .expect("Failed to transmit GstGtkWidget");
                // Wait for the widget to be added to the UI, otherwise the pipeline
                // embeds it in a default window
                match ui_rx_arc_mtx.lock()
                    .expect("Failed to lock ui_rx mutex, while building the video queue")
                    .recv() {
                        Ok(message) => match message {
                            ContextMessage::GotVideoWidget => (),
                            _ => panic!("Unexpected message while waiting for GotVideoWidget"),
                        },
                        Err(error) => panic!("Error while waiting for GotVideoWidget: {:?}", error),
                    };

                let elements = &[&queue, &convert, &scale, &sink];
                pipeline.add_many(elements).unwrap();
                gst::Element::link_many(elements).unwrap();

                for e in elements { e.sync_state_with_parent().unwrap(); }

                let sink_pad = queue.get_static_pad("sink").unwrap();
                assert_eq!(src_pad.link(&sink_pad), gst::PadLinkReturn::Ok);
            }
        });
    }

    // Uses ctx_tx to notify the UI controllers about the inspection process
    fn register_bus_inspector(&self, ctx_tx: Sender<ContextMessage>) {
        let info_arc_mtx = self.info.clone();
        let mut init_done = false;
        let bus = self.pipeline.get_bus().unwrap();
        bus.add_watch(move |_, msg| {
            let mut keep_going = true;
            // TODO: exit when pipeline status is null
            // or can we reuse the inspector for subsequent plays?
            match msg.view() {
                MessageView::Eos(..) => {
                    ctx_tx.send(ContextMessage::Eos)
                        .expect("Failed to notify UI");
                    keep_going = false;
                },
                MessageView::Error(err) => {
                    eprintln!("Error from {}: {} ({:?})",
                        msg.get_src().get_path_string(),
                        err.get_error(), err.get_debug()
                    );
                    ctx_tx.send(ContextMessage::FailedToOpenMedia)
                        .expect("Failed to notify UI");
                    keep_going = false;
                },
                MessageView::AsyncDone(_) => {
                    if !init_done {
                        ctx_tx.send(ContextMessage::InitDone)
                            .expect("Failed to notify UI");
                        init_done = true;
                    }
                    else {
                        ctx_tx.send(ContextMessage::AsyncDone)
                            .expect("Failed to notify UI");
                    }
                },
                MessageView::Tag(msg_tag) => {
                    if !init_done {
                        let tags = msg_tag.get_tags();
                        let info = &mut info_arc_mtx.lock()
                            .expect("Failed to lock media info while reading tag data");
                        assign_str_tag!(info.title, tags, Title);
                        assign_str_tag!(info.artist, tags, Artist);
                        assign_str_tag!(info.artist, tags, AlbumArtist);
                        assign_str_tag!(info.video_codec, tags, VideoCodec);
                        assign_str_tag!(info.audio_codec, tags, AudioCodec);

                        /*match tags.get::<PreviewImage>() {
                            // TODO: check if that happens, that would be handy for videos
                            Some(preview_tag) => println!("Found a PreviewImage tag"),
                            None => (),
                        };*/

                        // TODO: distinguish front/back cover (take the first one?)
                        if let Some(image_tag) = tags.get::<Image>() {
                            if let Some(sample) = image_tag.get() {
                                if let Some(buffer) = sample.get_buffer() {
                                    if let Some(map) = buffer.map_read() {
                                        info.thumbnail = AlignedImage::from_uknown_buffer(
                                                map.as_slice()
                                            ).ok();
                                    }
                                }
                            }
                        }
                    }
                },
                MessageView::Toc(msg_toc) => {
                    if !init_done {
                        let (toc, _) = msg_toc.get_toc();
                        if toc.get_scope() == TocScope::Global {
                            let info = &mut info_arc_mtx.lock()
                                .expect("Failed to lock media info while reading toc data");
                            if info.chapters.is_empty() {
                                for entry in toc.get_entries() {
                                    if entry.get_entry_type() == TocEntryType::Edition {
                                        for sub_entry in entry.get_sub_entries() {
                                            if sub_entry.get_entry_type() == TocEntryType::Chapter {
                                                if let Some((start, stop)) = sub_entry.get_start_stop_times() {
                                                    let mut title = String::new();
                                                    if let Some(tags) = sub_entry.get_tags() {
                                                        if let Some(tag) = tags.get::<Title>() {
                                                            title = tag.get().unwrap().to_owned();
                                                        };
                                                    };
                                                    info.chapters.push(Chapter::new(
                                                        sub_entry.get_uid(),
                                                        &title,
                                                        Timestamp::from_signed_nano(start),
                                                        Timestamp::from_signed_nano(stop)
                                                    ));
                                                }
                                            }
                                            else {
                                                println!("Warning: Skipping toc sub entry with entry type: {:?}",
                                                    sub_entry.get_entry_type()
                                                );
                                            }
                                        }
                                    }
                                    else {
                                        println!("Warning: Skipping toc entry with entry type: {:?}",
                                            entry.get_entry_type()
                                        );
                                    }
                                }
                            }
                            // else chapters already retrieved
                            // TODO: check if there are medias with some sort of
                            // incremental tocs (not likely for files)
                            // or maybe the updated flag (_ above) should be used
                        }
                        else {
                            println!("Warning: Skipping toc with scope: {:?}", toc.get_scope());
                        }
                    }
                }
                _ => (),
            };

            glib::Continue(keep_going)
        });
    }
}
<commit_msg>Cleanup (#7)<commit_after>extern crate gstreamer as gst;
use gstreamer::*;

extern crate glib;
use glib::ObjectExt;

extern crate url;
use url::Url;

use std::path::PathBuf;

use std::sync::mpsc::{Receiver, Sender};
use std::sync::{Arc, Mutex};

use super::{AlignedImage, AudioBuffer, AudioCaps, Chapter, MediaInfo, Timestamp};

pub enum ContextMessage {
    AsyncDone,
    Eos,
    FailedToOpenMedia,
    GotVideoWidget,
    HaveAudioBuffer(AudioBuffer),
    HaveVideoWidget(glib::Value),
    InitDone,
}

pub struct Context {
    pub pipeline: gst::Pipeline,

    pub path: PathBuf,
    pub file_name: String,
    pub name: String,

    pub info: Arc<Mutex<MediaInfo>>,
}

macro_rules! assign_str_tag(
    ($target:expr, $tags:expr, $TagType:ty) => {
        if $target.is_empty() {
            if let Some(tag) = $tags.get::<$TagType>() {
                $target = tag.get().unwrap().to_owned();
            }
        }
    };
);

impl Context {
    fn new(path: PathBuf) -> Self {
        Context{
            pipeline: gst::Pipeline::new("pipeline"),

            file_name: String::from(path.file_name().unwrap().to_str().unwrap()),
            name: String::from(path.file_stem().unwrap().to_str().unwrap()),
            path: path,

            info: Arc::new(Mutex::new(MediaInfo::new())),
        }
    }

    pub fn open_media_path(
        path: PathBuf,
        ctx_tx: Sender<ContextMessage>,
        ui_rx: Receiver<ContextMessage>,
    ) -> Result<Context, String>
    {
        println!("\n\n* Attempting to open {:?}", path);

        let ctx = Context::new(path);
        ctx.build_pipeline(&ctx_tx, ui_rx);
        ctx.register_bus_inspector(ctx_tx);

        match ctx.pause() {
            Ok(_) => Ok(ctx),
            Err(error) => Err(error),
        }
    }

    pub fn get_duration(&self) -> Timestamp {
        match self.pipeline.query_duration(gst::Format::Time) {
            Some(duration) => Timestamp::from_signed_nano(duration),
            None => Timestamp::new(),
        }
    }

    pub fn play(&self) -> Result<(), String> {
        if self.pipeline.set_state(gst::State::Playing) == gst::StateChangeReturn::Failure {
            return Err("Could not set media in palying state".into());
        }
        Ok(())
    }

    pub fn pause(&self) -> Result<(), String> {
        if self.pipeline.set_state(gst::State::Paused) == gst::StateChangeReturn::Failure {
            println!("could not set media in Paused state");
            return Err("Could not set media in Paused state".into());
        }
        Ok(())
    }

    pub fn stop(&self) {
        if self.pipeline.set_state(gst::State::Null) == gst::StateChangeReturn::Failure {
            println!("Could not set media in Null state");
            //return Err("could not set media in Null state".into());
        }
    }

    // TODO: handle errors
    // Uses ctx_tx & ui_rx to allow the UI integrate the GstGtkWidget
    fn build_pipeline(&self,
        ctx_tx: &Sender<ContextMessage>,
        ui_rx: Receiver<ContextMessage>
    )
    {
        let dec = gst::ElementFactory::make("uridecodebin", "input").unwrap();
        let url = match Url::from_file_path(self.path.as_path()) {
            Ok(url) => url.into_string(),
            Err(_) => "Failed to convert path to URL".to_owned(),
        };
        dec.set_property("uri", &gst::Value::from(&url)).unwrap();
        self.pipeline.add(&dec).unwrap();

        let pipeline_clone = self.pipeline.clone();
        let ctx_tx_arc_mtx = Arc::new(Mutex::new(ctx_tx.clone()));
        let ui_rx_arc_mtx = Arc::new(Mutex::new(ui_rx));
        dec.connect_pad_added(move |_, src_pad| {
            let pipeline = &pipeline_clone;

            let caps = src_pad.get_current_caps().unwrap();
            let structure = caps.get_structure(0).unwrap();
            let name = structure.get_name();

            // TODO: build only one queue by stream type (audio / video)
            if name.starts_with("audio/") {
                let queue = gst::ElementFactory::make("queue", None).unwrap();
                let convert = gst::ElementFactory::make("audioconvert", None).unwrap();
                let resample = gst::ElementFactory::make("audioresample", None).unwrap();
                let sink = gst::ElementFactory::make("autoaudiosink", "audio_sink").unwrap();

                let elements = &[&queue, &convert, &resample, &sink];
                pipeline.add_many(elements).unwrap();
                gst::Element::link_many(elements).unwrap();

                for e in elements { e.sync_state_with_parent().unwrap(); }

                let sink_pad = queue.get_static_pad("sink").unwrap();
                assert_eq!(src_pad.link(&sink_pad), gst::PadLinkReturn::Ok);

                let audio_caps_mtx: Mutex<Option<AudioCaps>> = Mutex::new(None);
                let ctx_tx_arc_mtx_clone = ctx_tx_arc_mtx.clone();
                sink_pad.add_probe(PAD_PROBE_TYPE_BUFFER, move |sink_pad, probe_info| {
                    if let Some(PadProbeData::Buffer(ref buffer)) = probe_info.data {
                        // Retrieve audio caps the first time only
                        let mut audio_caps_opt = audio_caps_mtx.lock()
                            .expect("Failed to lock audio caps while receiving buffer");
                        let audio_caps = audio_caps_opt
                            .get_or_insert_with(|| AudioCaps::from_sink_pad(sink_pad));

                        let audio_buffer = AudioBuffer::from_gst_buffer(audio_caps, buffer);

                        ctx_tx_arc_mtx_clone.lock()
                            .expect("Failed to lock ctx_tx mutex, while transmitting audio buffer")
                            .send(ContextMessage::HaveAudioBuffer(audio_buffer))
                                .expect("Failed to transmit audio buffer");
                    };

                    PadProbeReturn::Ok
                });
            } else if name.starts_with("video/") {
                let queue = gst::ElementFactory::make("queue", None).unwrap();
                let convert = gst::ElementFactory::make("videoconvert", None).unwrap();
                let scale = gst::ElementFactory::make("videoscale", None).unwrap();

                let (sink, widget_val) = if let Some(gtkglsink) = ElementFactory::make("gtkglsink", None) {
                    let glsinkbin = ElementFactory::make("glsinkbin", "video_sink").unwrap();
                    glsinkbin.set_property("sink", &gtkglsink.to_value()).unwrap();
                    let widget_val = gtkglsink.get_property("widget").unwrap();
                    (glsinkbin, widget_val)
                } else {
                    let sink = ElementFactory::make("gtksink", "video_sink").unwrap();
                    let widget_val = sink.get_property("widget").unwrap();
                    (sink, widget_val)
                };

                // Pass the video widget for integration in the UI
                ctx_tx_arc_mtx.lock()
                    .expect("Failed to lock ctx_tx mutex, while building the video queue")
                    .send(ContextMessage::HaveVideoWidget(widget_val))
                        .expect("Failed to transmit GstGtkWidget");
                // Wait for the widget to get added to the UI, otherwise the pipeline
                // embeds it in a default window
                match ui_rx_arc_mtx.lock()
                    .expect("Failed to lock ui_rx mutex, while building the video queue")
                    .recv() {
                        Ok(message) => match message {
                            ContextMessage::GotVideoWidget => (),
                            _ => panic!("Unexpected message while waiting for GotVideoWidget"),
                        },
                        Err(error) => panic!("Error while waiting for GotVideoWidget: {:?}", error),
                    };

                let elements = &[&queue, &convert, &scale, &sink];
                pipeline.add_many(elements).unwrap();
                gst::Element::link_many(elements).unwrap();

                for e in elements { e.sync_state_with_parent().unwrap(); }

                let sink_pad = queue.get_static_pad("sink").unwrap();
                assert_eq!(src_pad.link(&sink_pad), gst::PadLinkReturn::Ok);
            }
        });
    }

    // Uses ctx_tx to notify the UI controllers about the inspection process
    fn register_bus_inspector(&self, ctx_tx: Sender<ContextMessage>) {
        let info_arc_mtx = self.info.clone();
        let mut init_done = false;
        let bus = self.pipeline.get_bus().unwrap();
        bus.add_watch(move |_, msg| {
            // TODO: exit when pipeline status is null
            // or can we reuse the inspector for subsequent plays?
            match msg.view() {
                MessageView::Eos(..) => {
                    ctx_tx.send(ContextMessage::Eos)
                        .expect("Failed to notify UI");
                    glib::Continue(false)
                },
                MessageView::Error(err) => {
                    eprintln!("Error from {}: {} ({:?})",
                        msg.get_src().get_path_string(),
                        err.get_error(), err.get_debug()
                    );
                    ctx_tx.send(ContextMessage::FailedToOpenMedia)
                        .expect("Failed to notify UI");
                    glib::Continue(false)
                },
                MessageView::AsyncDone(_) => {
                    if !init_done {
                        ctx_tx.send(ContextMessage::InitDone)
                            .expect("Failed to notify UI");
                        init_done = true;
                    }
                    else {
                        ctx_tx.send(ContextMessage::AsyncDone)
                            .expect("Failed to notify UI");
                    }
                    glib::Continue(true)
                },
                MessageView::Tag(msg_tag) => {
                    if !init_done {
                        let tags = msg_tag.get_tags();
                        let info = &mut info_arc_mtx.lock()
                            .expect("Failed to lock media info while reading tag data");
                        assign_str_tag!(info.title, tags, Title);
                        assign_str_tag!(info.artist, tags, Artist);
                        assign_str_tag!(info.artist, tags, AlbumArtist);
                        assign_str_tag!(info.video_codec, tags, VideoCodec);
                        assign_str_tag!(info.audio_codec, tags, AudioCodec);

                        match tags.get::<PreviewImage>() {
                            // TODO: check if that happens, that would be handy for videos
                            Some(preview_tag) => println!("** Found a PreviewImage tag **"),
                            None => (),
                        };

                        // TODO: distinguish front/back cover (take the first one?)
                        if let Some(image_tag) = tags.get::<Image>() {
                            if let Some(sample) = image_tag.get() {
                                if let Some(buffer) = sample.get_buffer() {
                                    if let Some(map) = buffer.map_read() {
                                        info.thumbnail = AlignedImage::from_uknown_buffer(
                                                map.as_slice()
                                            ).ok();
                                    }
                                }
                            }
                        }
                    }
                    glib::Continue(true)
                },
                MessageView::Toc(msg_toc) => {
                    if init_done {
                        return glib::Continue(true);
                    }
                    let (toc, _) = msg_toc.get_toc();
                    if toc.get_scope() != TocScope::Global {
                        println!("Warning: Skipping toc with scope: {:?}", toc.get_scope());
                        return glib::Continue(true);
                    }

                    let info = &mut info_arc_mtx.lock()
                        .expect("Failed to lock media info while reading toc data");
                    if !info.chapters.is_empty() {
                        // chapters already retrieved
                        // TODO: check if there are medias with some sort of
                        // incremental tocs (not likely for files)
                        // or maybe the updated flag (_ above) should be used
                        return glib::Continue(true);
                    }

                    for entry in toc.get_entries() {
                        if entry.get_entry_type() == TocEntryType::Edition {
                            for sub_entry in entry.get_sub_entries() {
                                if sub_entry.get_entry_type() == TocEntryType::Chapter {
                                    if let Some((start, stop)) = sub_entry.get_start_stop_times() {
                                        let mut title = String::new();
                                        if let Some(tags) = sub_entry.get_tags() {
                                            if let Some(tag) = tags.get::<Title>() {
                                                title = tag.get().unwrap().to_owned();
                                            };
                                        };
                                        info.chapters.push(Chapter::new(
                                            sub_entry.get_uid(),
                                            &title,
                                            Timestamp::from_signed_nano(start),
                                            Timestamp::from_signed_nano(stop)
                                        ));
                                    }
                                }
                                else {
                                    println!("Warning: Skipping toc sub entry with entry type: {:?}",
                                        sub_entry.get_entry_type()
                                    );
                                }
                            }
                        }
                        else {
                            println!("Warning: Skipping toc entry with entry type: {:?}",
                                entry.get_entry_type()
                            );
                        }
                    }

                    glib::Continue(true)
                }
                _ => glib::Continue(true),
            }
        });
    }
}
<|endoftext|>


<commit_before>use bullet;
use character;
use critter::CharacterSprite;
use gfx;
use gfx_app::{ColorFormat, DepthFormat};
use gfx_app::renderer::EncoderQueue;
use graphics::DeltaTime;
use graphics::orientation::Stance;
use specs;
use specs::{Fetch, WriteStorage};
use std::time::Instant;
use terrain;
use terrain_object;
use zombie;

pub struct DrawSystem<D: gfx::Device> {
  render_target_view: gfx::handle::RenderTargetView<D::Resources, ColorFormat>,
  depth_stencil_view: gfx::handle::DepthStencilView<D::Resources, DepthFormat>,
  terrain_system: terrain::TerrainDrawSystem<D::Resources>,
  character_system: character::CharacterDrawSystem<D::Resources>,
  zombie_system: zombie::ZombieDrawSystem<D::Resources>,
  bullet_system: bullet::BulletDrawSystem<D::Resources>,
  terrain_object_system: terrain_object::TerrainObjectDrawSystem<D::Resources>,
  encoder_queue: EncoderQueue<D>,
  game_time: Instant,
  frames: u32,
  cool_down: f64,
  fire_cool_down: f64,
}

impl<D: gfx::Device> DrawSystem<D> {
  pub fn new<F>(factory: &mut F,
                rtv: &gfx::handle::RenderTargetView<D::Resources, ColorFormat>,
                dsv: &gfx::handle::DepthStencilView<D::Resources, DepthFormat>,
                encoder_queue: EncoderQueue<D>)
                -> DrawSystem<D>
                where F: gfx::Factory<D::Resources> {
    DrawSystem {
      render_target_view: rtv.clone(),
      depth_stencil_view: dsv.clone(),
      terrain_system: terrain::TerrainDrawSystem::new(factory, rtv.clone(), dsv.clone()),
      character_system: character::CharacterDrawSystem::new(factory, rtv.clone(), dsv.clone()),
      zombie_system: zombie::ZombieDrawSystem::new(factory, rtv.clone(), dsv.clone()),
      bullet_system: bullet::BulletDrawSystem::new(factory, rtv.clone(), dsv.clone()),
      terrain_object_system: terrain_object::TerrainObjectDrawSystem::new(factory, rtv.clone(), dsv.clone()),
      encoder_queue,
      game_time: Instant::now(),
      frames: 0,
      cool_down: 1.0,
      fire_cool_down: 1.0,
    }
  }
}

impl<'a, D> specs::System<'a> for DrawSystem<D>
  where D: gfx::Device,
        D::CommandBuffer: Send {
  #[cfg_attr(feature = "cargo-clippy", allow(type_complexity))]
  type SystemData = (WriteStorage<'a, terrain::TerrainDrawable>,
                     WriteStorage<'a, character::CharacterDrawable>,
                     WriteStorage<'a, CharacterSprite>,
                     WriteStorage<'a, zombie::zombies::Zombies>,
                     WriteStorage<'a, bullet::bullets::Bullets>,
                     WriteStorage<'a, terrain_object::terrain_objects::TerrainObjects>,
                     Fetch<'a, DeltaTime>);

  fn run(&mut self, (mut terrain, mut character, mut character_sprite, mut zombies, mut bullets, mut terrain_objects, d): Self::SystemData) {
    use specs::Join;
    let mut encoder = self.encoder_queue.receiver.recv().unwrap();

    let delta = d.0;

    if self.cool_down == 0.0 {
      self.cool_down += 0.1;
    }
    if self.fire_cool_down == 0.0 {
      self.fire_cool_down += 0.2;
    }
    self.cool_down = (self.cool_down - delta).max(0.0);
    self.fire_cool_down = (self.fire_cool_down - delta).max(0.0);

    let current_time = Instant::now();
    self.frames += 1;
    if cfg!(feature = "fps") && (current_time.duration_since(self.game_time).as_secs()) >= 1 {
      println!("{:?} ms/frames", 1000.0 / f64::from(self.frames));
      self.frames = 0;
      self.game_time = Instant::now();
    }

    encoder.clear(&self.render_target_view, [16.0 / 256.0, 16.0 / 256.0, 20.0 / 256.0, 1.0]);
    encoder.clear_depth(&self.depth_stencil_view, 1.0);

    for (t, c, cs, zs, bs, obj) in (&mut terrain, &mut character, &mut character_sprite, &mut zombies, &mut bullets, &mut terrain_objects).join() {
      self.terrain_system.draw(t, &mut encoder);

      if self.cool_down == 0.0 {
        if c.stance == Stance::Walking {
          cs.update_run();
        }
        for mut z in &mut zs.zombies {
          match z.stance {
            Stance::NormalDeath => z.update_normal_death(),
            Stance::CriticalDeath => z.update_critical_death(),
            Stance::Walking => z.update_walk(),
            Stance::Still => z.update_still(),
            _ => ()
          };
        }
      } else if self.fire_cool_down == 0.0 && c.stance == Stance::Firing {
        cs.update_fire();
      }
      for mut z in &mut zs.zombies {
        if c.position.position[1] <= z.position.position[1] {
          self.zombie_system.draw(&mut z, &mut encoder);
        }
      }
      for mut o in &mut obj.objects {
        if c.position.position[1] <= o.position.position[1] {
          self.terrain_object_system.draw(&o, &mut encoder);
        }
      }
      self.character_system.draw(c, cs, &mut encoder);
      for mut z in &mut zs.zombies {
        if c.position.position[1] > z.position.position[1] {
          self.zombie_system.draw(&mut z, &mut encoder);
        }
      }
      for mut o in &mut obj.objects {
        if c.position.position[1] > o.position.position[1] {
          self.terrain_object_system.draw(&o, &mut encoder);
        }
      }

      for b in &mut bs.bullets {
        self.bullet_system.draw(b, &mut encoder);
      }
    }

    if let Err(e) = self.encoder_queue.sender.send(encoder) {
      panic!("Disconnected, cannot return encoder to mpsc: {}", e);
    };
  }
}
<commit_msg>Remove unnecessary object reference<commit_after>use bullet;
use character;
use critter::CharacterSprite;
use gfx;
use gfx_app::{ColorFormat, DepthFormat};
use gfx_app::renderer::EncoderQueue;
use graphics::DeltaTime;
use graphics::orientation::Stance;
use specs;
use specs::{Fetch, WriteStorage};
use std::time::Instant;
use terrain;
use terrain_object;
use zombie;

pub struct DrawSystem<D: gfx::Device> {
  render_target_view: gfx::handle::RenderTargetView<D::Resources, ColorFormat>,
  depth_stencil_view: gfx::handle::DepthStencilView<D::Resources, DepthFormat>,
  terrain_system: terrain::TerrainDrawSystem<D::Resources>,
  character_system: character::CharacterDrawSystem<D::Resources>,
  zombie_system: zombie::ZombieDrawSystem<D::Resources>,
  bullet_system: bullet::BulletDrawSystem<D::Resources>,
  terrain_object_system: terrain_object::TerrainObjectDrawSystem<D::Resources>,
  encoder_queue: EncoderQueue<D>,
  game_time: Instant,
  frames: u32,
  cool_down: f64,
  fire_cool_down: f64,
}

impl<D: gfx::Device> DrawSystem<D> {
  pub fn new<F>(factory: &mut F,
                rtv: &gfx::handle::RenderTargetView<D::Resources, ColorFormat>,
                dsv: &gfx::handle::DepthStencilView<D::Resources, DepthFormat>,
                encoder_queue: EncoderQueue<D>)
                -> DrawSystem<D>
                where F: gfx::Factory<D::Resources> {
    DrawSystem {
      render_target_view: rtv.clone(),
      depth_stencil_view: dsv.clone(),
      terrain_system: terrain::TerrainDrawSystem::new(factory, rtv.clone(), dsv.clone()),
      character_system: character::CharacterDrawSystem::new(factory, rtv.clone(), dsv.clone()),
      zombie_system: zombie::ZombieDrawSystem::new(factory, rtv.clone(), dsv.clone()),
      bullet_system: bullet::BulletDrawSystem::new(factory, rtv.clone(), dsv.clone()),
      terrain_object_system: terrain_object::TerrainObjectDrawSystem::new(factory, rtv.clone(), dsv.clone()),
      encoder_queue,
      game_time: Instant::now(),
      frames: 0,
      cool_down: 1.0,
      fire_cool_down: 1.0,
    }
  }
}

impl<'a, D> specs::System<'a> for DrawSystem<D>
  where D: gfx::Device,
        D::CommandBuffer: Send {
  #[cfg_attr(feature = "cargo-clippy", allow(type_complexity))]
  type SystemData = (WriteStorage<'a, terrain::TerrainDrawable>,
                     WriteStorage<'a, character::CharacterDrawable>,
                     WriteStorage<'a, CharacterSprite>,
                     WriteStorage<'a, zombie::zombies::Zombies>,
                     WriteStorage<'a, bullet::bullets::Bullets>,
                     WriteStorage<'a, terrain_object::terrain_objects::TerrainObjects>,
                     Fetch<'a, DeltaTime>);

  fn run(&mut self, (mut terrain, mut character, mut character_sprite, mut zombies, mut bullets, mut terrain_objects, d): Self::SystemData) {
    use specs::Join;
    let mut encoder = self.encoder_queue.receiver.recv().unwrap();

    let delta = d.0;

    if self.cool_down == 0.0 {
      self.cool_down += 0.1;
    }
    if self.fire_cool_down == 0.0 {
      self.fire_cool_down += 0.2;
    }
    self.cool_down = (self.cool_down - delta).max(0.0);
    self.fire_cool_down = (self.fire_cool_down - delta).max(0.0);

    let current_time = Instant::now();
    self.frames += 1;
    if cfg!(feature = "fps") && (current_time.duration_since(self.game_time).as_secs()) >= 1 {
      println!("{:?} ms/frames", 1000.0 / f64::from(self.frames));
      self.frames = 0;
      self.game_time = Instant::now();
    }

    encoder.clear(&self.render_target_view, [16.0 / 256.0, 16.0 / 256.0, 20.0 / 256.0, 1.0]);
    encoder.clear_depth(&self.depth_stencil_view, 1.0);

    for (t, c, cs, zs, bs, obj) in (&mut terrain, &mut character, &mut character_sprite, &mut zombies, &mut bullets, &mut terrain_objects).join() {
      self.terrain_system.draw(t, &mut encoder);

      if self.cool_down == 0.0 {
        if c.stance == Stance::Walking {
          cs.update_run();
        }
        for mut z in &mut zs.zombies {
          match z.stance {
            Stance::NormalDeath => z.update_normal_death(),
            Stance::CriticalDeath => z.update_critical_death(),
            Stance::Walking => z.update_walk(),
            Stance::Still => z.update_still(),
            _ => ()
          };
        }
      } else if self.fire_cool_down == 0.0 && c.stance == Stance::Firing {
        cs.update_fire();
      }
      for mut z in &mut zs.zombies {
        if c.position.position[1] <= z.position.position[1] {
          self.zombie_system.draw(&mut z, &mut encoder);
        }
      }
      for mut o in &mut obj.objects {
        if c.position.position[1] <= o.position.position[1] {
          self.terrain_object_system.draw(o, &mut encoder);
        }
      }
      self.character_system.draw(c, cs, &mut encoder);
      for mut z in &mut zs.zombies {
        if c.position.position[1] > z.position.position[1] {
          self.zombie_system.draw(&mut z, &mut encoder);
        }
      }
      for mut o in &mut obj.objects {
        if c.position.position[1] > o.position.position[1] {
          self.terrain_object_system.draw(o, &mut encoder);
        }
      }

      for b in &mut bs.bullets {
        self.bullet_system.draw(b, &mut encoder);
      }
    }

    if let Err(e) = self.encoder_queue.sender.send(encoder) {
      panic!("Disconnected, cannot return encoder to mpsc: {}", e);
    };
  }
}
<|endoftext|>