<commit_before>
extern crate regex;

use std::process::{Command, Stdio, Child};
use std::io::prelude::*;
use std::io::BufReader;
use std::convert::Into;
use std::path::PathBuf;
use std::str;

use lib::DownloadError;

use mysql::conn::pool::{MyPool,MyPooledConn};
use mysql::conn::Stmt;

macro_rules! regex(
    ($s:expr) => (regex::Regex::new($s).unwrap());
);

pub struct Converter<'a> {
    ffmpeg_dir: PathBuf,
    mp3_quality: &'a i16,
    pool: MyPool,
}

impl<'a> Converter<'a> {
    pub fn new(ffmpeg_dir: &'a str,mp3_quality: &'a i16, pool: MyPool) -> Converter<'a> {
    	debug!("ffmpeg dir: {}",ffmpeg_dir);
        Converter{ffmpeg_dir: PathBuf::from(ffmpeg_dir),mp3_quality: mp3_quality, pool: pool}
    }

    /// Merge audo & video files to one
    /// As ffmpeg uses \r for progress updates, we'll have to read untill this delimiter
    /// ffmpeg prints only to stderr
    pub fn merge_files(&self, qid: &i64, video_file: &'a str,audio_file: &'a str, output_file: &'a str, show_progress: bool) -> Result<(), DownloadError>{
        trace!("progress {}",show_progress);
        let max_frames: f64 = try!(self.get_max_frames(video_file));
        trace!("Total frames: {}",max_frames);

        let mut child = try!(self.create_merge_cmd(audio_file, video_file, output_file));
        trace!("started merge process");
        let mut stdout = BufReader::new(child.stderr.take().unwrap());

        let mut conn = self.pool.get_conn().unwrap();
        let mut statement = self.prepare_progress_updater(&mut conn);
        let re = regex!(r"frame=\s*(\d+)");

		let mut buf = vec![];
		buf.reserve(128);

		let mut cur_frame: f64;

		loop {
			match stdout.read_until(b'\r', &mut buf) {
				Ok(0) => break,
				Ok(_) => {},
				Err(why) => {error!("couldn't read cmd stdout: {}", why); panic!(); }
			}

			{
				let line = str::from_utf8(&buf).unwrap();
				debug!("ffmpeg: {}",line);
				if re.is_match(&line) {
					let cap = re.captures(&line).unwrap();
	                debug!("frame: {}", cap.at(1).unwrap());
	                cur_frame = cap.at(1).unwrap().parse::<f64>().unwrap();
	                try!(self.update_progress(&mut statement, format!("{:.2}",(cur_frame / max_frames) * 100.0), qid)
	                );
				}
			}

			buf.clear();

		}

        try!(child.wait());

        Ok(())
    }

    /// Extract audio from video files
    /// If set audio will be converted to mp3 on the fly
    pub fn extract_audio(&self, video_file: &'a str, output_file: &'a str, convert_mp3: bool) -> Result<(), DownloadError> {
//        let duration =

        let mut child;
        if convert_mp3 {
            child = try!(self.create_audio_extrac_mp3_convert_cmd(video_file, output_file));
        }else {
            child = try!(self.create_audio_extract_cmd(video_file, output_file));
        }

        let mut stdout = BufReader::new(child.stderr.take().unwrap());

		let mut buf = vec![];
		buf.reserve(128);

		loop {
			match stdout.read_until(b'\r', &mut buf) {
				Ok(0) => break,
				Ok(_) => {},
				Err(why) => {error!("couldn't read cmd stdout: {}", why); panic!(); }
			}

			{
				let line = str::from_utf8(&buf).unwrap();
				debug!("ffmpeg: {}",line);

			}

			buf.clear();

		}

        try!(child.wait());

        Ok(())
    }

    /// Retrive the max frames from video file for percentual progress calculation
    fn get_max_frames(&self, video_file: &str) -> Result<f64,DownloadError> {
        let mut child = try!(self.create_fps_get_cmd(video_file));
        let mut stdout_buffer = BufReader::new(child.stderr.take().unwrap());
        let mut stdout: String = String::new();
        try!(stdout_buffer.read_to_string(&mut stdout));
        println!("total frames stdout: {:?}", stdout.trim());
        try!(child.wait());

        let regex_duration = regex!(r"Duration: ((\d\d):(\d\d):(\d\d)\.\d\d)");
        let regex_fps = regex!(r"(\d+)\sfps");

        if regex_duration.is_match(&stdout) && regex_fps.is_match(&stdout) {
            let cap_fps = regex_fps.captures(&stdout).unwrap();

            let cap_duration = regex_duration.captures(&stdout).unwrap();
            trace!("Found duration: {}",cap_duration.at(0).unwrap());
            let fps = cap_fps.at(1).unwrap().parse::<i64>().unwrap();
            let mut seconds = cap_duration.at(4).unwrap().parse::<i64>().unwrap();
            seconds += cap_duration.at(3).unwrap().parse::<i64>().unwrap() * 60;
            seconds += cap_duration.at(2).unwrap().parse::<i64>().unwrap() * 60 * 60 ;
            Ok((seconds * fps) as f64)
        }else{
            Err(DownloadError::FFMPEGError(format!("Couldn't get max frames {}",stdout)))
        }
    }

    /// Merges an audio & an video file together.
    /// ffmpeg uses \r as \n
    fn create_merge_cmd(&self, audio_file: &str, video_file: &str, output_file: &str) -> Result<Child,DownloadError> {
    	let mut command = self.create_ffmpeg_base("ffmpeg");
    	command.args(&["-threads","0"]);
    	command.args(&["-i",video_file]);
    	command.args(&["-i",audio_file]);
    	command.args(&["-map","0"]);
    	command.args(&["-map","1"]);
    	command.args(&["-codec","copy"]);
    	command.arg("-shortest");
    	command.arg(output_file);
    	//-stats -threads 0 -i "{}" -i "{}" -map 0 -map 1 -codec copy -shortest "{}"
    	match command.spawn() {
    		Err(why) => Err(why.into()),
            Ok(process) => Ok(process),
    	}
    }

    ///Creates a cmd to gain the amount of frames in a video, used in progress calculation
    fn create_fps_get_cmd(&self, video_file: &str) -> Result<Child, DownloadError> {
    	let mut command = self.create_ffmpeg_base("ffprobe");
    	command.args(&["-i",video_file]);

    	match command.spawn() {
    		Err(why) => Err(why.into()),
            Ok(process) => Ok(process),
    	}
    }

    ///Create a ffmpeg instance with the audio extract cmd
    fn create_audio_extract_cmd(&self, video_file: &str, output_file: &str) -> Result<Child, DownloadError> {
        let mut command = self.create_ffmpeg_base("ffmpeg");
        command.args(&["-threads", "0"]);
        command.args(&["-i",video_file]);
        command.args(&["-vn", "-acodec","copy"]);
        command.arg(output_file);

        match command.spawn() {
    		Err(why) => Err(why.into()),
            Ok(process) => Ok(process),
    	}
    }

    fn create_audio_extrac_mp3_convert_cmd(&self, video_file: &str, output_file: &str) -> Result<Child, DownloadError> {
        let mut command = self.create_ffmpeg_base("ffmpeg");
        command.args(&["-threads", "0"]);
        command.args(&["-i",video_file]);
        command.args(&["-codec:a", "libmp3lame"]);
        command.args(&["-qscale:a",&self.mp3_quality.to_string()]);
        command.arg(output_file);

        match command.spawn() {
    		Err(why) => Err(why.into()),
            Ok(process) => Ok(process),
    	}
    }

    /// Create FFMPEG basic command
    /// executable is the called ffmpeg binary
    fn create_ffmpeg_base(&self, executable: &'static str) -> Command {
    	let mut cmd = Command::new(self.ffmpeg_dir.join(executable));
    	cmd.stdin(Stdio::null());
    	cmd.stdout(Stdio::null());
        cmd.stderr(Stdio::piped());
    	cmd
    }

    // MyPooledConn does only live when MyOpts is alive -> lifetime needs to be declared
    //TODO: think about generalizing this, while using the local pool
    fn prepare_progress_updater(&'a self,conn: &'a mut MyPooledConn) -> Stmt<'a> { // no livetime needed: struct livetime used
        conn.prepare("UPDATE querydetails SET progress = ? WHERE qid = ?").unwrap()
    }

    ///updater called from the stdout progress
    fn update_progress(&self,stmt: &mut Stmt, progress: String, qid: &i64) -> Result<(),DownloadError>{
    	trace!("updating progress {}",progress);
        try!(stmt.execute((&progress,qid)).map(|_| Ok(())))
    }
}<commit_msg>implementes ffmpeg progress updates for audio extraction / conversion reimplements File_info via run_file_probe<commit_after>
extern crate regex;

use std::process::{Command, Stdio, Child};
use std::io::prelude::*;
use std::io::BufReader;
use std::convert::Into;
use std::path::PathBuf;
use std::str;

use lib::DownloadError;

use mysql::conn::pool::{MyPool,MyPooledConn};
use mysql::conn::Stmt;

macro_rules! regex(
    ($s:expr) => (regex::Regex::new($s).unwrap());
);

pub struct Converter<'a> {
    ffmpeg_dir: PathBuf,
    mp3_quality: &'a i16,
    pool: MyPool,
}

struct FileInfo {
	duration: f32, // seconds
	frames: f32,
}

impl<'a> Converter<'a> {
    pub fn new(ffmpeg_dir: &'a str,mp3_quality: &'a i16, pool: MyPool) -> Converter<'a> {
    	debug!("ffmpeg dir: {}",ffmpeg_dir);
        Converter{ffmpeg_dir: PathBuf::from(ffmpeg_dir),mp3_quality: mp3_quality, pool: pool}
    }

    /// Merge audo & video files to one
    /// As ffmpeg uses \r for progress updates, we'll have to read untill this delimiter
    /// ffmpeg prints only to stderr
    pub fn merge_files(&self, qid: &i64, video_file: &'a str,audio_file: &'a str, output_file: &'a str, show_progress: bool) -> Result<(), DownloadError>{
        trace!("progress {}",show_progress);
        let file_info = try!(self.get_file_info(video_file));
        trace!("Total frames: {}",file_info.frames);

        let mut child = try!(self.run_merge_cmd(audio_file, video_file, output_file));
        trace!("started merge process");
        let mut stdout = BufReader::new(child.stderr.take().unwrap());

        let mut conn = self.pool.get_conn().unwrap();
        let mut statement = self.prepare_progress_updater(&mut conn);
        let re = regex!(r"frame=\s*(\d+)");

		let mut buf = vec![];
		buf.reserve(128);

		let mut cur_frame: f32;

		loop {
			match stdout.read_until(b'\r', &mut buf) {
				Ok(0) => break,
				Ok(_) => {},
				Err(why) => {error!("couldn't read cmd stdout: {}", why); panic!(); }
			}

			{
				let line = str::from_utf8(&buf).unwrap();
				debug!("ffmpeg: {}",line);
				if let Some(cap) = re.captures(&line) {
	                debug!("frame: {}", cap.at(1).unwrap());
	                cur_frame = cap.at(1).unwrap().parse::<f32>().unwrap();
	                try!(self.update_progress(&mut statement, format!("{:.2}",(cur_frame / file_info.frames ) * 100.0), qid)
	                );
				}
			}

			buf.clear();

		}

        try!(child.wait());

        Ok(())
    }

    /// Extract audio from video files
    /// If set audio will be converted to mp3 on the fly
    pub fn extract_audio(&self, qid: &i64, video_file: &'a str, output_file: &'a str, convert_mp3: bool) -> Result<(), DownloadError> {
		let file_info = try!(self.get_file_info(video_file));
		debug!("duration: {}",file_info.duration);

        let mut child;
        if convert_mp3 {
            child = try!(self.run_audio_extract_to_mp3(video_file, output_file));
        }else {
            child = try!(self.run_audio_extract(video_file, output_file));
        }

        let mut stdout = BufReader::new(child.stderr.take().unwrap());

        let mut conn = self.pool.get_conn().unwrap();
        let mut statement = self.prepare_progress_updater(&mut conn);
        let re = regex!(r"time=(\d+):(\d+):(\d+.?\d*)");

		let mut buf = vec![];
		buf.reserve(128);

		loop {
			match stdout.read_until(b'\r', &mut buf) {
				Ok(0) => break,
				Ok(_) => {},
				Err(why) => {error!("couldn't read cmd stdout: {}", why); panic!(); }
			}

			{
				let line = str::from_utf8(&buf).unwrap();
				debug!("ffmpeg: {}",line);
				if let Some(cap) = re.captures(&line) {
					let mut seconds: i32 = cap.at(2).unwrap().parse::<i32>().unwrap() * 60; // minutes
		            seconds += cap.at(1).unwrap().parse::<i32>().unwrap() * 60 * 60 ; // hours

		            let seconds: f32 = seconds as f32 + cap.at(3).unwrap().parse::<f32>().unwrap();
		            try!(self.update_progress(&mut statement, format!("{:.2}",(seconds / file_info.duration ) * 100.0), qid)
	                );
				}

			}

			buf.clear();

		}

        try!(child.wait());

        Ok(())
    }

    /// Retrive file information
    fn get_file_info(&self, video_file: &str) -> Result<FileInfo,DownloadError> {
        let stdout = try!(self.run_file_probe(video_file));

		let regex_duration = regex!(r"duration=(\d+\.?\d*)");
		let regex_fps = regex!(r"r_frame_rate=(\d+)/(\d+)");

		if let Some(cap) = regex_duration.captures(&stdout) {
			let mut file_info = FileInfo { duration: -1.0, frames: -1.0 };
			file_info.duration = cap.at(1).unwrap().parse::<f32>().unwrap();

			if let Some(cap) = regex_fps.captures(&stdout) {
				let x: f32 = cap.at(1).unwrap().parse::<f32>().unwrap();
				let y: f32 = cap.at(2).unwrap().parse::<f32>().unwrap();
				file_info.frames =  ( x / y ) * file_info.duration;
			}

			Ok(file_info)
		}else{
			Err(DownloadError::FFMPEGError(format!("Couldn't get max frames: {}",stdout)))
		}

//        let regex_duration = regex!(r"Duration: ((\d\d):(\d\d):(\d\d)\.\d\d)");
//        let regex_fps = regex!(r"(\d+)\sfps");
//
//        if regex_fps.is_match(&stdout) {
//            let cap_fps = regex_fps.captures(&stdout).unwrap();
//
//            let cap_duration = regex_duration.captures(&stdout).unwrap();
//            trace!("Found duration: {}",cap_duration.at(0).unwrap());
//            let fps = cap_fps.at(1).unwrap().parse::<i64>().unwrap();
//            let mut seconds = cap_duration.at(4).unwrap().parse::<i64>().unwrap();
//            seconds += cap_duration.at(3).unwrap().parse::<i64>().unwrap() * 60;
//            seconds += cap_duration.at(2).unwrap().parse::<i64>().unwrap() * 60 * 60 ;
//            Ok((seconds * fps) as f64)
//        }else{
//            Err(DownloadError::FFMPEGError(format!("Couldn't get max frames {}",stdout)))
//        }
    }

    /// Runs a file probe and returns its output, used in progress calculation
    fn run_file_probe(&self, video_file: &str) -> Result<String, DownloadError> {
    	let mut command = self.create_ffmpeg_base("ffprobe");
    	command.args(&["-v","error"]);
    	command.args(&["-select_streams","0"]);
    	command.args(&["-show_entries","stream=duration,r_frame_rate"]);
    	command.args(&["-of","default=noprint_wrappers=1"]);
    	command.arg(video_file);

    	match command.spawn() {
    		Err(why) => Err(why.into()),
            Ok(mut process) => {
            	let mut stdout_buffer = BufReader::new(process.stdout.take().unwrap());
	        	let mut stdout: String = String::new();
	      		try!(stdout_buffer.read_to_string(&mut stdout));
	      		try!(process.wait());
	      		debug!("ffprobe: {}",stdout);
	      		Ok(stdout)
            },
    	}
    }

    /// Merges an audio & an video file together.
    /// ffmpeg uses \r as \n
    fn run_merge_cmd(&self, audio_file: &str, video_file: &str, output_file: &str) -> Result<Child,DownloadError> {
    	let mut command = self.create_ffmpeg_base("ffmpeg");
    	command.args(&["-threads","0"]);
    	command.args(&["-i",video_file]);
    	command.args(&["-i",audio_file]);
    	command.args(&["-map","0"]);
    	command.args(&["-map","1"]);
    	command.args(&["-codec","copy"]);
    	command.arg("-shortest");
    	command.arg(output_file);
    	//-stats -threads 0 -i "{}" -i "{}" -map 0 -map 1 -codec copy -shortest "{}"
    	match command.spawn() {
    		Err(why) => Err(why.into()),
            Ok(process) => Ok(process),
    	}
    }

    ///Create a ffmpeg instance with the audio extract cmd
    fn run_audio_extract(&self, video_file: &str, output_file: &str) -> Result<Child, DownloadError> {
        let mut command = self.create_ffmpeg_base("ffmpeg");
        command.args(&["-threads", "0"]);
        command.args(&["-i",video_file]);
        command.args(&["-vn", "-acodec","copy"]);
        command.arg(output_file);

        match command.spawn() {
    		Err(why) => Err(why.into()),
            Ok(process) => Ok(process),
    	}
    }

    fn run_audio_extract_to_mp3(&self, video_file: &str, output_file: &str) -> Result<Child, DownloadError> {
        let mut command = self.create_ffmpeg_base("ffmpeg");
        command.args(&["-threads", "0"]);
        command.args(&["-i",video_file]);
        command.args(&["-codec:a", "libmp3lame"]);
        command.args(&["-qscale:a",&self.mp3_quality.to_string()]);
        command.arg(output_file);

        match command.spawn() {
    		Err(why) => Err(why.into()),
            Ok(process) => Ok(process),
    	}
    }

    /// Create FFMPEG basic command
    /// executable is the called ffmpeg binary
    fn create_ffmpeg_base(&self, executable: &'static str) -> Command {
    	let mut cmd = Command::new(self.ffmpeg_dir.join(executable));
    	cmd.stdin(Stdio::null());
    	cmd.stdout(Stdio::piped());
        cmd.stderr(Stdio::piped());
    	cmd
    }

    // MyPooledConn does only live when MyOpts is alive -> lifetime needs to be declared
    //TODO: think about generalizing this, while using the local pool
    fn prepare_progress_updater(&'a self,conn: &'a mut MyPooledConn) -> Stmt<'a> { // no livetime needed: struct livetime used
        conn.prepare("UPDATE querydetails SET progress = ? WHERE qid = ?").unwrap()
    }

    ///updater called from the stdout progress
    fn update_progress(&self,stmt: &mut Stmt, progress: String, qid: &i64) -> Result<(),DownloadError>{
    	trace!("updating progress {}",progress);
        try!(stmt.execute((&progress,qid)).map(|_| Ok(())))
    }

}<|endoftext|>


<commit_before>// The MIT License (MIT)
//
// Copyright (c) 2014 J.C. Moyer
//
// 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.

// corresponds to lauxlib.h
use libc::{c_int, c_long, c_char, c_void, size_t};
use ffi::lua;
use ffi::lua::{lua_State, lua_CFunction, lua_Integer, lua_Number};
use ffi::luaconf::LUAL_BUFFERSIZE;
use std::ptr;

pub use super::glue::LUAL_NUMSIZES;

// extra error code for 'luaL_load'
pub const LUA_ERRFILE: c_int = lua::LUA_ERRERR + 1;

#[repr(C)]
pub struct luaL_Reg {
  name: *const char,
  func: lua_CFunction,
}

#[inline(always)]
pub unsafe fn luaL_checkversion(L: *mut lua_State) {
  luaL_checkversion_(L, lua::LUA_VERSION_NUM as lua_Number, LUAL_NUMSIZES as size_t)
}

extern {
  pub fn luaL_checkversion_(L: *mut lua_State, ver: lua_Number, sz: size_t);

  pub fn luaL_getmetafield(L: *mut lua_State, obj: c_int, e: *const c_char) -> c_int;
  pub fn luaL_callmeta(L: *mut lua_State, obj: c_int, e: *const c_char) -> c_int;
  pub fn luaL_tolstring(L: *mut lua_State, idx: c_int, len: *mut size_t) -> *const c_char;
  pub fn luaL_argerror(L: *mut lua_State, arg: c_int, l: *const c_char) -> c_int;
  pub fn luaL_checklstring(L: *mut lua_State, arg: c_int, l: *mut size_t) -> *const c_char;
  pub fn luaL_optlstring(L: *mut lua_State, arg: c_int, def: *const c_char, l: *mut size_t) -> *const c_char;
  pub fn luaL_checknumber(L: *mut lua_State, arg: c_int) -> lua_Number;
  pub fn luaL_optnumber(L: *mut lua_State, arg: c_int, def: lua_Number) -> lua_Number;
  pub fn luaL_checkinteger(L: *mut lua_State, arg: c_int) -> lua_Integer;
  pub fn luaL_optinteger(L: *mut lua_State, arg: c_int, def: lua_Integer) -> lua_Integer;

  pub fn luaL_checkstack(L: *mut lua_State, sz: c_int, msg: *const c_char);
  pub fn luaL_checktype(L: *mut lua_State, arg: c_int, t: c_int);
  pub fn luaL_checkany(L: *mut lua_State, arg: c_int);

  pub fn luaL_newmetatable(L: *mut lua_State, tname: *const c_char) -> c_int;
  pub fn luaL_setmetatable(L: *mut lua_State, tname: *const c_char);
  pub fn luaL_testudata(L: *mut lua_State, ud: c_int, tname: *const c_char) -> *mut c_void;
  pub fn luaL_checkudata(L: *mut lua_State, ud: c_int, tname: *const c_char) -> *mut c_void;

  pub fn luaL_where(L: *mut lua_State, lvl: c_int) -> c_int;
  // TODO: luaL_error glue?

  // TODO: test this
  pub fn luaL_checkoption(L: *mut lua_State, arg: c_int, def: *const c_char, lst: [*const c_char]) -> c_int;

  pub fn luaL_fileresult(L: *mut lua_State, stat: c_int, fname: *const c_char) -> c_int;
  pub fn luaL_execresult(L: *mut lua_State, stat: c_int) -> c_int;
}

// pre-defined references
pub const LUA_NOREF: c_int = -2;
pub const LUA_REFNIL: c_int = -1;

extern {
  pub fn luaL_ref(L: *mut lua_State, t: c_int) -> c_int;
  pub fn luaL_unref(L: *mut lua_State, t: c_int, r: c_int);

  pub fn luaL_loadfilex(L: *mut lua_State, filename: *const c_char, mode: *const c_char) -> c_int;
}

#[inline(always)]
pub unsafe fn luaL_loadfile(L: *mut lua_State, f: *const c_char) -> c_int {
  luaL_loadfilex(L, f, ptr::null())
}

extern {
  pub fn luaL_loadbufferx(L: *mut lua_State, buff: *const c_char, sz: size_t, name: *const c_char, mode: *const c_char) -> c_int;
  pub fn luaL_loadstring(L: *mut lua_State, s: *const c_char) -> c_int;

  pub fn luaL_newstate() -> *mut lua_State;

  pub fn luaL_len(L: *mut lua_State, idx: c_int) -> lua_Integer;

  pub fn luaL_gsub(L: *mut lua_State, s: *const c_char, p: *const c_char, r: *const c_char) -> *const c_char;

  pub fn luaL_setfuncs(L: *mut lua_State, l: *const luaL_Reg, nup: c_int);

  pub fn luaL_getsubtable(L: *mut lua_State, idx: c_int, fname: *const c_char) -> c_int;

  pub fn luaL_traceback(L: *mut lua_State, L1: *mut lua_State, msg: *const c_char, level: c_int);

  pub fn luaL_requiref(L: *mut lua_State, modname: *const c_char, openf: lua_CFunction, glb: c_int);
}

#[inline(always)]
#[allow(unused_variable)]
pub unsafe fn luaL_newlibtable(L: *mut lua_State, l: *const luaL_Reg) {
  // TODO: figure out how to pass an appropriate hint for the second param
  // this involves correcting the second parameter's type; in C this is
  // sizeof(l)/sizeof(l[0])
  lua::lua_createtable(L, 0, 0)
}

#[inline(always)]
pub unsafe fn luaL_newlib(L: *mut lua_State, l: *const luaL_Reg) {
  luaL_checkversion(L);
  luaL_newlibtable(L, l);
  luaL_setfuncs(L, l, 0)
}

#[inline(always)]
pub unsafe fn luaL_argcheck(L: *mut lua_State, cond: c_int, arg: c_int, extramsg: *const c_char) {
  if cond == 0 {
    luaL_argerror(L, arg, extramsg);
  }
}

#[inline(always)]
pub unsafe fn luaL_checkstring(L: *mut lua_State, n: c_int) -> *const c_char {
  luaL_checklstring(L, n, ptr::null_mut())
}

#[inline(always)]
pub unsafe fn luaL_optstring(L: *mut lua_State, n: c_int, d: *const c_char) -> *const c_char {
  luaL_optlstring(L, n, d, ptr::null_mut())
}

// From 5.3 user manual:
// Macros to project non-default integer types (luaL_checkint, luaL_optint,
// luaL_checklong, luaL_optlong) were deprecated. Use their equivalent over
// lua_Integer with a type cast (or, when possible, use lua_Integer in your
// code).
#[inline(always)]
#[deprecated]
pub unsafe fn luaL_checkint(L: *mut lua_State, n: c_int) -> c_int {
  luaL_checkinteger(L, n) as c_int
}

#[inline(always)]
#[deprecated]
pub unsafe fn luaL_optint(L: *mut lua_State, n: c_int, d: c_int) -> c_int {
  luaL_optinteger(L, n, d as lua_Integer) as c_int
}

#[inline(always)]
#[deprecated]
pub unsafe fn luaL_checklong(L: *mut lua_State, n: c_int) -> c_long {
  luaL_checkinteger(L, n) as c_int
}

#[inline(always)]
#[deprecated]
pub unsafe fn luaL_optlong(L: *mut lua_State, n: c_int, d: c_long) -> c_long {
  luaL_optinteger(L, n, d as lua_Integer) as c_int
}

#[inline(always)]
pub unsafe fn luaL_typename(L: *mut lua_State, i: c_int) -> *const c_char {
  lua::lua_typename(L, lua::lua_type(L, i))
}

#[inline(always)]
pub unsafe fn luaL_dofile(L: *mut lua_State, filename: *const c_char) -> c_int {
  let status = luaL_loadfile(L, filename);
  if status == 0 {
    lua::lua_pcall(L, 0, lua::LUA_MULTRET, 0)
  } else {
    status
  }
}

#[inline(always)]
pub unsafe fn luaL_dostring(L: *mut lua_State, s: *const c_char) -> c_int {
  let status = luaL_loadstring(L, s);
  if status == 0 {
    lua::lua_pcall(L, 0, lua::LUA_MULTRET, 0)
  } else {
    status
  }
}

#[inline(always)]
pub unsafe fn luaL_getmetatable(L: *mut lua_State, n: *const c_char) {
  lua::lua_getfield(L, lua::LUA_REGISTRYINDEX, n);
}

// luaL_opt would be implemented here but it is undocumented, so it's omitted

#[inline(always)]
pub unsafe fn luaL_loadbuffer(L: *mut lua_State, s: *const c_char, sz: size_t, n: *const c_char) -> c_int {
  luaL_loadbufferx(L, s, sz, n, ptr::null())
}

#[repr(C)]
pub struct luaL_Buffer {
  b: *mut c_char,
  size: size_t,
  n: size_t,
  L: *mut lua_State,
  initb: [c_char, ..LUAL_BUFFERSIZE as uint]
}

// TODO: Test this thoroughly
#[inline(always)]
pub unsafe fn luaL_addchar(B: *mut luaL_Buffer, c: c_char) {
  // (B)->n < (B) -> size || luaL_prepbuffsize((B), 1)
  if (*B).n < (*B).size {
    luaL_prepbuffsize(B, 1);
  }
  // (B)->b[(B)->n++] = (c)
  let offset = (*B).b.offset((*B).n as int);
  ptr::write(offset, c);
  (*B).n += 1;
}

#[inline(always)]
pub unsafe fn luaL_addsize(B: *mut luaL_Buffer, s: size_t) {
  (*B).n += s;
}

extern {
  pub fn luaL_buffinit(L: *mut lua_State, B: *mut luaL_Buffer);
  pub fn luaL_prepbuffsize(B: *mut luaL_Buffer, sz: size_t) -> *mut c_char;
  pub fn luaL_addlstring(B: *mut luaL_Buffer, s: *const char, l: size_t);
  pub fn luaL_addstring(B: *mut luaL_Buffer, s: *const char);
  pub fn luaL_addvalue(B: *mut luaL_Buffer);
  pub fn luaL_pushresult(B: *mut luaL_Buffer);
  pub fn luaL_pushresultsize(B: *mut luaL_Buffer, sz: size_t);
  pub fn luaL_buffinitsize(L: *mut lua_State, B: *mut luaL_Buffer, sz: size_t) -> *mut c_char;
}

pub unsafe fn luaL_prepbuffer(B: *mut luaL_Buffer) -> *mut c_char {
  luaL_prepbuffsize(B, LUAL_BUFFERSIZE as size_t)
}

// omitted: LUA_FILEHANDLE

#[repr(C)]
pub struct luaL_Stream {
  f: *mut c_void,
  closef: lua_CFunction
}

// omitted: old module system compatibility
<commit_msg>Add luaL_error<commit_after>// The MIT License (MIT)
//
// Copyright (c) 2014 J.C. Moyer
//
// 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.

// corresponds to lauxlib.h
use libc::{c_int, c_long, c_char, c_void, size_t};
use ffi::lua;
use ffi::lua::{lua_State, lua_CFunction, lua_Integer, lua_Number};
use ffi::luaconf::LUAL_BUFFERSIZE;
use std::ptr;

pub use super::glue::LUAL_NUMSIZES;

// extra error code for 'luaL_load'
pub const LUA_ERRFILE: c_int = lua::LUA_ERRERR + 1;

#[repr(C)]
pub struct luaL_Reg {
  name: *const char,
  func: lua_CFunction,
}

#[inline(always)]
pub unsafe fn luaL_checkversion(L: *mut lua_State) {
  luaL_checkversion_(L, lua::LUA_VERSION_NUM as lua_Number, LUAL_NUMSIZES as size_t)
}

extern {
  pub fn luaL_checkversion_(L: *mut lua_State, ver: lua_Number, sz: size_t);

  pub fn luaL_getmetafield(L: *mut lua_State, obj: c_int, e: *const c_char) -> c_int;
  pub fn luaL_callmeta(L: *mut lua_State, obj: c_int, e: *const c_char) -> c_int;
  pub fn luaL_tolstring(L: *mut lua_State, idx: c_int, len: *mut size_t) -> *const c_char;
  pub fn luaL_argerror(L: *mut lua_State, arg: c_int, l: *const c_char) -> c_int;
  pub fn luaL_checklstring(L: *mut lua_State, arg: c_int, l: *mut size_t) -> *const c_char;
  pub fn luaL_optlstring(L: *mut lua_State, arg: c_int, def: *const c_char, l: *mut size_t) -> *const c_char;
  pub fn luaL_checknumber(L: *mut lua_State, arg: c_int) -> lua_Number;
  pub fn luaL_optnumber(L: *mut lua_State, arg: c_int, def: lua_Number) -> lua_Number;
  pub fn luaL_checkinteger(L: *mut lua_State, arg: c_int) -> lua_Integer;
  pub fn luaL_optinteger(L: *mut lua_State, arg: c_int, def: lua_Integer) -> lua_Integer;

  pub fn luaL_checkstack(L: *mut lua_State, sz: c_int, msg: *const c_char);
  pub fn luaL_checktype(L: *mut lua_State, arg: c_int, t: c_int);
  pub fn luaL_checkany(L: *mut lua_State, arg: c_int);

  pub fn luaL_newmetatable(L: *mut lua_State, tname: *const c_char) -> c_int;
  pub fn luaL_setmetatable(L: *mut lua_State, tname: *const c_char);
  pub fn luaL_testudata(L: *mut lua_State, ud: c_int, tname: *const c_char) -> *mut c_void;
  pub fn luaL_checkudata(L: *mut lua_State, ud: c_int, tname: *const c_char) -> *mut c_void;

  pub fn luaL_where(L: *mut lua_State, lvl: c_int) -> c_int;
  pub fn luaL_error(L: *mut lua_State, fmt: *const char, ...) -> c_int;

  // TODO: test this
  pub fn luaL_checkoption(L: *mut lua_State, arg: c_int, def: *const c_char, lst: [*const c_char]) -> c_int;

  pub fn luaL_fileresult(L: *mut lua_State, stat: c_int, fname: *const c_char) -> c_int;
  pub fn luaL_execresult(L: *mut lua_State, stat: c_int) -> c_int;
}

// pre-defined references
pub const LUA_NOREF: c_int = -2;
pub const LUA_REFNIL: c_int = -1;

extern {
  pub fn luaL_ref(L: *mut lua_State, t: c_int) -> c_int;
  pub fn luaL_unref(L: *mut lua_State, t: c_int, r: c_int);

  pub fn luaL_loadfilex(L: *mut lua_State, filename: *const c_char, mode: *const c_char) -> c_int;
}

#[inline(always)]
pub unsafe fn luaL_loadfile(L: *mut lua_State, f: *const c_char) -> c_int {
  luaL_loadfilex(L, f, ptr::null())
}

extern {
  pub fn luaL_loadbufferx(L: *mut lua_State, buff: *const c_char, sz: size_t, name: *const c_char, mode: *const c_char) -> c_int;
  pub fn luaL_loadstring(L: *mut lua_State, s: *const c_char) -> c_int;

  pub fn luaL_newstate() -> *mut lua_State;

  pub fn luaL_len(L: *mut lua_State, idx: c_int) -> lua_Integer;

  pub fn luaL_gsub(L: *mut lua_State, s: *const c_char, p: *const c_char, r: *const c_char) -> *const c_char;

  pub fn luaL_setfuncs(L: *mut lua_State, l: *const luaL_Reg, nup: c_int);

  pub fn luaL_getsubtable(L: *mut lua_State, idx: c_int, fname: *const c_char) -> c_int;

  pub fn luaL_traceback(L: *mut lua_State, L1: *mut lua_State, msg: *const c_char, level: c_int);

  pub fn luaL_requiref(L: *mut lua_State, modname: *const c_char, openf: lua_CFunction, glb: c_int);
}

#[inline(always)]
#[allow(unused_variable)]
pub unsafe fn luaL_newlibtable(L: *mut lua_State, l: *const luaL_Reg) {
  // TODO: figure out how to pass an appropriate hint for the second param
  // this involves correcting the second parameter's type; in C this is
  // sizeof(l)/sizeof(l[0])
  lua::lua_createtable(L, 0, 0)
}

#[inline(always)]
pub unsafe fn luaL_newlib(L: *mut lua_State, l: *const luaL_Reg) {
  luaL_checkversion(L);
  luaL_newlibtable(L, l);
  luaL_setfuncs(L, l, 0)
}

#[inline(always)]
pub unsafe fn luaL_argcheck(L: *mut lua_State, cond: c_int, arg: c_int, extramsg: *const c_char) {
  if cond == 0 {
    luaL_argerror(L, arg, extramsg);
  }
}

#[inline(always)]
pub unsafe fn luaL_checkstring(L: *mut lua_State, n: c_int) -> *const c_char {
  luaL_checklstring(L, n, ptr::null_mut())
}

#[inline(always)]
pub unsafe fn luaL_optstring(L: *mut lua_State, n: c_int, d: *const c_char) -> *const c_char {
  luaL_optlstring(L, n, d, ptr::null_mut())
}

// From 5.3 user manual:
// Macros to project non-default integer types (luaL_checkint, luaL_optint,
// luaL_checklong, luaL_optlong) were deprecated. Use their equivalent over
// lua_Integer with a type cast (or, when possible, use lua_Integer in your
// code).
#[inline(always)]
#[deprecated]
pub unsafe fn luaL_checkint(L: *mut lua_State, n: c_int) -> c_int {
  luaL_checkinteger(L, n) as c_int
}

#[inline(always)]
#[deprecated]
pub unsafe fn luaL_optint(L: *mut lua_State, n: c_int, d: c_int) -> c_int {
  luaL_optinteger(L, n, d as lua_Integer) as c_int
}

#[inline(always)]
#[deprecated]
pub unsafe fn luaL_checklong(L: *mut lua_State, n: c_int) -> c_long {
  luaL_checkinteger(L, n) as c_int
}

#[inline(always)]
#[deprecated]
pub unsafe fn luaL_optlong(L: *mut lua_State, n: c_int, d: c_long) -> c_long {
  luaL_optinteger(L, n, d as lua_Integer) as c_int
}

#[inline(always)]
pub unsafe fn luaL_typename(L: *mut lua_State, i: c_int) -> *const c_char {
  lua::lua_typename(L, lua::lua_type(L, i))
}

#[inline(always)]
pub unsafe fn luaL_dofile(L: *mut lua_State, filename: *const c_char) -> c_int {
  let status = luaL_loadfile(L, filename);
  if status == 0 {
    lua::lua_pcall(L, 0, lua::LUA_MULTRET, 0)
  } else {
    status
  }
}

#[inline(always)]
pub unsafe fn luaL_dostring(L: *mut lua_State, s: *const c_char) -> c_int {
  let status = luaL_loadstring(L, s);
  if status == 0 {
    lua::lua_pcall(L, 0, lua::LUA_MULTRET, 0)
  } else {
    status
  }
}

#[inline(always)]
pub unsafe fn luaL_getmetatable(L: *mut lua_State, n: *const c_char) {
  lua::lua_getfield(L, lua::LUA_REGISTRYINDEX, n);
}

// luaL_opt would be implemented here but it is undocumented, so it's omitted

#[inline(always)]
pub unsafe fn luaL_loadbuffer(L: *mut lua_State, s: *const c_char, sz: size_t, n: *const c_char) -> c_int {
  luaL_loadbufferx(L, s, sz, n, ptr::null())
}

#[repr(C)]
pub struct luaL_Buffer {
  b: *mut c_char,
  size: size_t,
  n: size_t,
  L: *mut lua_State,
  initb: [c_char, ..LUAL_BUFFERSIZE as uint]
}

// TODO: Test this thoroughly
#[inline(always)]
pub unsafe fn luaL_addchar(B: *mut luaL_Buffer, c: c_char) {
  // (B)->n < (B) -> size || luaL_prepbuffsize((B), 1)
  if (*B).n < (*B).size {
    luaL_prepbuffsize(B, 1);
  }
  // (B)->b[(B)->n++] = (c)
  let offset = (*B).b.offset((*B).n as int);
  ptr::write(offset, c);
  (*B).n += 1;
}

#[inline(always)]
pub unsafe fn luaL_addsize(B: *mut luaL_Buffer, s: size_t) {
  (*B).n += s;
}

extern {
  pub fn luaL_buffinit(L: *mut lua_State, B: *mut luaL_Buffer);
  pub fn luaL_prepbuffsize(B: *mut luaL_Buffer, sz: size_t) -> *mut c_char;
  pub fn luaL_addlstring(B: *mut luaL_Buffer, s: *const char, l: size_t);
  pub fn luaL_addstring(B: *mut luaL_Buffer, s: *const char);
  pub fn luaL_addvalue(B: *mut luaL_Buffer);
  pub fn luaL_pushresult(B: *mut luaL_Buffer);
  pub fn luaL_pushresultsize(B: *mut luaL_Buffer, sz: size_t);
  pub fn luaL_buffinitsize(L: *mut lua_State, B: *mut luaL_Buffer, sz: size_t) -> *mut c_char;
}

pub unsafe fn luaL_prepbuffer(B: *mut luaL_Buffer) -> *mut c_char {
  luaL_prepbuffsize(B, LUAL_BUFFERSIZE as size_t)
}

// omitted: LUA_FILEHANDLE

#[repr(C)]
pub struct luaL_Stream {
  f: *mut c_void,
  closef: lua_CFunction
}

// omitted: old module system compatibility
<|endoftext|>


<commit_before>
use std::io::Cursor;

use byteorder::{ByteOrder, LittleEndian};

use gameboy::registers;

pub struct Cpu {
    rom: Vec<u8>,
    pub registers: registers::Registers,
}

impl Cpu {
    pub fn new(gameboy_color: bool, rom: Vec<u8>) -> Cpu {
        Cpu {
            rom: rom,
            registers: registers::Registers::new(gameboy_color),
        }
    }

    pub fn step(&mut self) {
        let opcode = self.rom[self.registers.pc];
        self.registers.pc += 1;

        match opcode {
            0b00110001 => {
                // LD SP, <u16>
                let val = LittleEndian::read_u16(&self.rom[self.registers.pc..]);
                self.registers.sp = val as usize;
                self.registers.pc += 0x03;
                println!("Stack pointer: {:#X}", self.registers.sp);
            }
            0b10101111 => {
                // XOR A
                self.registers.a = self.registers.a ^ self.registers.a;
            }
            _ => panic!("Unknown opcode: {:#X}", opcode),
        }
    }
}

#[cfg(test)]
pub mod tests {
    use super::*;

    #[test]
    pub fn xor_a_zeros_a_and_sets_zero_flag() {
        let mut cpu = Cpu::new(true, vec![0xAF]);

        cpu.step();

        assert_eq!(true, cpu.registers.flags.zero);
        assert_eq!(0, cpu.registers.a);
    }
}
<commit_msg>Changed format of test<commit_after>
use std::io::Cursor;

use byteorder::{ByteOrder, LittleEndian};

use gameboy::registers;

pub struct Cpu {
    rom: Vec<u8>,
    pub registers: registers::Registers,
}

impl Cpu {
    pub fn new(gameboy_color: bool, rom: Vec<u8>) -> Cpu {
        Cpu {
            rom: rom,
            registers: registers::Registers::new(gameboy_color),
        }
    }

    pub fn step(&mut self) {
        let opcode = self.rom[self.registers.pc];
        self.registers.pc += 1;

        match opcode {
            0b00110001 => {
                // LD SP, <u16>
                let val = LittleEndian::read_u16(&self.rom[self.registers.pc..]);
                self.registers.sp = val as usize;
                self.registers.pc += 0x03;
                println!("Stack pointer: {:#X}", self.registers.sp);
            }
            0b10101111 => {
                // XOR A
                self.registers.a = self.registers.a ^ self.registers.a;
            }
            _ => panic!("Unknown opcode: {:#X}", opcode),
        }
    }
}

#[cfg(test)]
pub mod tests {
    use super::*;

    #[test]
    pub fn xor_a_zeros_a_and_sets_zero_flag() {
        let xor_a = vec![0xAF];
        let mut cpu = Cpu::new(true, xor_a);

        cpu.step();

        assert_eq!(true, cpu.registers.flags.zero);
        assert_eq!(0, cpu.registers.a);
    }
}
<|endoftext|>


<commit_before>use glutin;
use gfx;
use gfx_device_gl;
use gfx_window_glutin;
use glutin::GlContext;
use std::process;
use game::constants::{RESOLUTION_X, RESOLUTION_Y};

pub mod init;
pub mod renderer;
pub mod system;
pub mod controls;
pub mod mouse_controls;
pub mod graphics;

pub type ColorFormat = gfx::format::Rgba8;
pub type DepthFormat = gfx::format::DepthStencil;

pub struct GlutinWindow {
  window: glutin::GlWindow,
  controls: Option<controls::TilemapControls>,
  events_loop: glutin::EventsLoop,
  device: gfx_device_gl::Device,
  factory: gfx_device_gl::Factory,
  render_target_view: gfx::handle::RenderTargetView<gfx_device_gl::Resources, ColorFormat>,
  depth_stencil_view: gfx::handle::DepthStencilView<gfx_device_gl::Resources, DepthFormat>,
  mouse_pos: (f64, f64),
}

impl GlutinWindow {
  pub fn new() -> GlutinWindow {
    let events_loop = glutin::EventsLoop::new();

    let window_title = glutin::WindowBuilder::new()
      .with_title("Zombie shooter");

    let builder = if cfg!(feature = "dev") {
      window_title.with_dimensions(RESOLUTION_X, RESOLUTION_Y)
    } else {
      let monitor = {
        glutin::get_available_monitors().nth(0).expect("Please enter a valid ID")
      };
      window_title.with_fullscreen(monitor)
        .with_dimensions(RESOLUTION_X, RESOLUTION_Y)
    };

    let context = glutin::ContextBuilder::new()
      .with_pixel_format(24, 8)
      .with_gl(glutin::GlRequest::GlThenGles {
        opengles_version: (3, 0),
        opengl_version: (4, 1),
      });

    let (window, device, factory, rtv, dsv) = gfx_window_glutin::init::<ColorFormat,
      DepthFormat>(builder, context, &events_loop);

    GlutinWindow {
      window,
      controls: None,
      events_loop,
      device,
      factory,
      render_target_view: rtv,
      depth_stencil_view: dsv,
      mouse_pos: (0.0, 0.0)
    }
  }
}

#[allow(dead_code)]
#[derive(Debug, PartialEq, Eq)]
pub enum GameStatus {
  Render,
  Quit,
}

pub trait Window<D: gfx::Device, F: gfx::Factory<D::Resources>> {
  fn swap_window(&mut self);
  fn poll_events(&mut self) -> Option<GameStatus>;

  fn create_buffers(&mut self, count: usize) -> Vec<D::CommandBuffer>;
  fn set_controls(&mut self, controls: controls::TilemapControls);

  fn get_viewport_size(&mut self) -> (u32, u32);
  fn get_device(&mut self) -> &mut D;
  fn get_factory(&mut self) -> &mut F;
  fn get_render_target_view(&mut self) -> gfx::handle::RenderTargetView<D::Resources, ColorFormat>;
  fn get_depth_stencil_view(&mut self) -> gfx::handle::DepthStencilView<D::Resources, DepthFormat>;
}

impl Window<gfx_device_gl::Device, gfx_device_gl::Factory> for GlutinWindow {
  fn swap_window(&mut self) {
    use gfx::Device;
    self.window
      .swap_buffers()
      .expect("Unable to swap buffers");
    self.device.cleanup();
  }

  fn create_buffers(&mut self, count: usize) -> Vec<gfx_device_gl::CommandBuffer> {
    let mut bufs = Vec::new();
    for _ in 0..count {
      bufs.push(self.factory.create_command_buffer());
    }
    bufs
  }

  fn set_controls(&mut self, controls: controls::TilemapControls) {
    self.controls = Some(controls);
  }

  fn get_viewport_size(&mut self) -> (u32, u32) {
    self.window
      .get_inner_size_pixels()
      .unwrap_or((RESOLUTION_X, RESOLUTION_Y))
  }

  fn get_device(&mut self) -> &mut gfx_device_gl::Device {
    &mut self.device
  }

  fn get_factory(&mut self) -> &mut gfx_device_gl::Factory {
    &mut self.factory
  }

  fn get_render_target_view(&mut self) -> gfx::handle::RenderTargetView<gfx_device_gl::Resources, ColorFormat> {
    self.render_target_view.clone()
  }

  fn get_depth_stencil_view(&mut self) -> gfx::handle::DepthStencilView<gfx_device_gl::Resources, DepthFormat> {
    self.depth_stencil_view.clone()
  }

  fn poll_events(&mut self) -> Option<GameStatus> {
    use glutin::KeyboardInput;
    use glutin::MouseButton;
    use glutin::WindowEvent::{Resized, Closed, MouseMoved, MouseInput};
    use glutin::ElementState::{Pressed, Released};
    use glutin::VirtualKeyCode::{Escape, Minus, Equals, W, A, S, D};

    let controls = match self.controls {
      Some(ref mut c) => c,
      None => panic!("Terrain controls have not been initialized"),
    };
    let ref w = self.window;
    let ref mut rtv = self.render_target_view;
    let ref mut dsv = self.depth_stencil_view;
    let ref mut m_pos = self.mouse_pos;

    self.events_loop.poll_events(|event| {
      match event {
        glutin::Event::WindowEvent { event, .. } => match event {
          glutin::WindowEvent::KeyboardInput { input, .. } => match input {
            KeyboardInput { state: _, virtual_keycode: Some(Escape), modifiers: _, scancode: _ } => process::exit(0),
            KeyboardInput { state: Pressed, scancode: _, modifiers: _, virtual_keycode: Some(Minus) } => controls.zoom_out(),
            KeyboardInput { state: Pressed, scancode: _, modifiers: _, virtual_keycode: Some(Equals) } => controls.zoom_in(),
            KeyboardInput { state: Released, scancode: _, modifiers: _, virtual_keycode: Some(Minus) } |
            KeyboardInput { state: Released, scancode: _, modifiers: _, virtual_keycode: Some(Equals) } => controls.zoom_stop(),
            KeyboardInput { state: Pressed, scancode: _, modifiers: _, virtual_keycode: Some(W) } => {controls.move_character_up(); controls.move_map_up();},
            KeyboardInput { state: Pressed, scancode: _, modifiers: _, virtual_keycode: Some(S) } => {controls.move_character_down(); controls.move_map_down();},
            KeyboardInput { state: Released, scancode: _, modifiers: _, virtual_keycode: Some(W) } |
            KeyboardInput { state: Released, scancode: _, modifiers: _, virtual_keycode: Some(S) } => {controls.stop_character_y(); controls.stop_map_y();},
            KeyboardInput { state: Pressed, scancode: _, modifiers: _, virtual_keycode: Some(A) } => {controls.move_character_left(); controls.move_map_left();},
            KeyboardInput { state: Pressed, scancode: _, modifiers: _, virtual_keycode: Some(D) } => {controls.move_character_right(); controls.move_map_right();},
            KeyboardInput { state: Released, scancode: _, modifiers: _, virtual_keycode: Some(A) } |
            KeyboardInput { state: Released, scancode: _, modifiers: _, virtual_keycode: Some(D) } => {controls.stop_character_x(); controls.stop_map_x();},
            _ => (),
          },
          MouseInput {device_id: _, state: Pressed, button: MouseButton::Left} => controls.mouse_left_click(Some(*m_pos)),
          MouseInput {device_id: _, state: Released, button: MouseButton::Left} => controls.mouse_left_click(None),
          MouseInput {device_id: _, state: Pressed, button: MouseButton::Right} => controls.mouse_right_click(Some(*m_pos)),
          MouseInput {device_id: _, state: Released, button: MouseButton::Right} => controls.mouse_right_click(None),
          MouseMoved {device_id: _, position} => {
            *m_pos = position;
          },
          Closed => process::exit(0),
          Resized(_, _) => gfx_window_glutin::update_views(w, rtv, dsv),
          _ => (),
        },
        _ => (),
      }
    });
    None
  }
}

<commit_msg>Remove extra variables<commit_after>use glutin;
use gfx;
use gfx_device_gl;
use gfx_window_glutin;
use glutin::GlContext;
use std::process;
use game::constants::{RESOLUTION_X, RESOLUTION_Y};

pub mod init;
pub mod renderer;
pub mod system;
pub mod controls;
pub mod mouse_controls;
pub mod graphics;

pub type ColorFormat = gfx::format::Rgba8;
pub type DepthFormat = gfx::format::DepthStencil;

pub struct GlutinWindow {
  window: glutin::GlWindow,
  controls: Option<controls::TilemapControls>,
  events_loop: glutin::EventsLoop,
  device: gfx_device_gl::Device,
  factory: gfx_device_gl::Factory,
  render_target_view: gfx::handle::RenderTargetView<gfx_device_gl::Resources, ColorFormat>,
  depth_stencil_view: gfx::handle::DepthStencilView<gfx_device_gl::Resources, DepthFormat>,
  mouse_pos: (f64, f64),
}

impl GlutinWindow {
  pub fn new() -> GlutinWindow {
    let events_loop = glutin::EventsLoop::new();

    let window_title = glutin::WindowBuilder::new()
      .with_title("Zombie shooter");

    let builder = if cfg!(feature = "dev") {
      window_title.with_dimensions(RESOLUTION_X, RESOLUTION_Y)
    } else {
      let monitor = {
        glutin::get_available_monitors().nth(0).expect("Please enter a valid ID")
      };
      window_title.with_fullscreen(monitor)
        .with_dimensions(RESOLUTION_X, RESOLUTION_Y)
    };

    let context = glutin::ContextBuilder::new()
      .with_pixel_format(24, 8)
      .with_gl(glutin::GlRequest::GlThenGles {
        opengles_version: (3, 0),
        opengl_version: (4, 1),
      });

    let (window, device, factory, rtv, dsv) = gfx_window_glutin::init::<ColorFormat,
      DepthFormat>(builder, context, &events_loop);

    GlutinWindow {
      window,
      controls: None,
      events_loop,
      device,
      factory,
      render_target_view: rtv,
      depth_stencil_view: dsv,
      mouse_pos: (0.0, 0.0)
    }
  }
}

#[allow(dead_code)]
#[derive(Debug, PartialEq, Eq)]
pub enum GameStatus {
  Render,
  Quit,
}

pub trait Window<D: gfx::Device, F: gfx::Factory<D::Resources>> {
  fn swap_window(&mut self);
  fn poll_events(&mut self) -> Option<GameStatus>;

  fn create_buffers(&mut self, count: usize) -> Vec<D::CommandBuffer>;
  fn set_controls(&mut self, controls: controls::TilemapControls);

  fn get_viewport_size(&mut self) -> (u32, u32);
  fn get_device(&mut self) -> &mut D;
  fn get_factory(&mut self) -> &mut F;
  fn get_render_target_view(&mut self) -> gfx::handle::RenderTargetView<D::Resources, ColorFormat>;
  fn get_depth_stencil_view(&mut self) -> gfx::handle::DepthStencilView<D::Resources, DepthFormat>;
}

impl Window<gfx_device_gl::Device, gfx_device_gl::Factory> for GlutinWindow {
  fn swap_window(&mut self) {
    use gfx::Device;
    self.window
      .swap_buffers()
      .expect("Unable to swap buffers");
    self.device.cleanup();
  }

  fn create_buffers(&mut self, count: usize) -> Vec<gfx_device_gl::CommandBuffer> {
    let mut bufs = Vec::new();
    for _ in 0..count {
      bufs.push(self.factory.create_command_buffer());
    }
    bufs
  }

  fn set_controls(&mut self, controls: controls::TilemapControls) {
    self.controls = Some(controls);
  }

  fn get_viewport_size(&mut self) -> (u32, u32) {
    self.window
      .get_inner_size_pixels()
      .unwrap_or((RESOLUTION_X, RESOLUTION_Y))
  }

  fn get_device(&mut self) -> &mut gfx_device_gl::Device {
    &mut self.device
  }

  fn get_factory(&mut self) -> &mut gfx_device_gl::Factory {
    &mut self.factory
  }

  fn get_render_target_view(&mut self) -> gfx::handle::RenderTargetView<gfx_device_gl::Resources, ColorFormat> {
    self.render_target_view.clone()
  }

  fn get_depth_stencil_view(&mut self) -> gfx::handle::DepthStencilView<gfx_device_gl::Resources, DepthFormat> {
    self.depth_stencil_view.clone()
  }

  fn poll_events(&mut self) -> Option<GameStatus> {
    use glutin::KeyboardInput;
    use glutin::MouseButton;
    use glutin::WindowEvent::{Resized, Closed, MouseMoved, MouseInput};
    use glutin::ElementState::{Pressed, Released};
    use glutin::VirtualKeyCode::{Escape, Minus, Equals, W, A, S, D};

    let controls = match self.controls {
      Some(ref mut c) => c,
      None => panic!("Terrain controls have not been initialized"),
    };
    let ref w = self.window;
    let ref mut rtv = self.render_target_view;
    let ref mut dsv = self.depth_stencil_view;
    let ref mut m_pos = self.mouse_pos;

    self.events_loop.poll_events(|event| {
      match event {
        glutin::Event::WindowEvent { event, .. } => match event {
          glutin::WindowEvent::KeyboardInput { input, .. } => match input {
            KeyboardInput { virtual_keycode: Some(Escape), .. } => process::exit(0),
            KeyboardInput { state: Pressed, virtual_keycode: Some(Minus), .. } => controls.zoom_out(),
            KeyboardInput { state: Pressed, virtual_keycode: Some(Equals), .. } => controls.zoom_in(),
            KeyboardInput { state: Released, virtual_keycode: Some(Minus), .. } |
            KeyboardInput { state: Released, virtual_keycode: Some(Equals), .. } => controls.zoom_stop(),
            KeyboardInput { state: Pressed, virtual_keycode: Some(W), .. } => {controls.move_character_up(); controls.move_map_up();},
            KeyboardInput { state: Pressed, virtual_keycode: Some(S), .. } => {controls.move_character_down(); controls.move_map_down();},
            KeyboardInput { state: Released, virtual_keycode: Some(W), .. } |
            KeyboardInput { state: Released, virtual_keycode: Some(S), .. } => {controls.stop_character_y(); controls.stop_map_y();},
            KeyboardInput { state: Pressed, virtual_keycode: Some(A), .. } => {controls.move_character_left(); controls.move_map_left();},
            KeyboardInput { state: Pressed, virtual_keycode: Some(D), .. } => {controls.move_character_right(); controls.move_map_right();},
            KeyboardInput { state: Released, virtual_keycode: Some(A), .. } |
            KeyboardInput { state: Released, virtual_keycode: Some(D), .. } => {controls.stop_character_x(); controls.stop_map_x();},
            _ => (),
          },
          MouseInput {state: Pressed, button: MouseButton::Left, ..} => controls.mouse_left_click(Some(*m_pos)),
          MouseInput {state: Released, button: MouseButton::Left, ..} => controls.mouse_left_click(None),
          MouseInput {state: Pressed, button: MouseButton::Right, ..} => controls.mouse_right_click(Some(*m_pos)),
          MouseInput {state: Released, button: MouseButton::Right, ..} => controls.mouse_right_click(None),
          MouseMoved {position, ..} => {
            *m_pos = position;
          },
          Closed => process::exit(0),
          Resized(_, _) => gfx_window_glutin::update_views(w, rtv, dsv),
          _ => (),
        },
        _ => (),
      }
    });
    None
  }
}

<|endoftext|>


<commit_before>use game::constants::{RESOLUTION_X, RESOLUTION_Y};
use gfx;
use gfx_device_gl;
use gfx_window_glutin;
use glutin;
use glutin::GlContext;

pub mod init;
pub mod renderer;
pub mod system;
pub mod controls;
pub mod mouse_controls;

pub type ColorFormat = gfx::format::Rgba8;
pub type DepthFormat = gfx::format::DepthStencil;

pub struct GlutinWindow {
  window: glutin::GlWindow,
  controls: Option<controls::TilemapControls>,
  events_loop: glutin::EventsLoop,
  device: gfx_device_gl::Device,
  factory: gfx_device_gl::Factory,
  render_target_view: gfx::handle::RenderTargetView<gfx_device_gl::Resources, ColorFormat>,
  depth_stencil_view: gfx::handle::DepthStencilView<gfx_device_gl::Resources, DepthFormat>,
  mouse_pos: (f64, f64),
}

impl GlutinWindow {
  pub fn new() -> GlutinWindow {
    let events_loop = glutin::EventsLoop::new();

    let window_title = glutin::WindowBuilder::new()
      .with_title("Hinterland");

    let builder = if cfg!(feature = "windowed") {
      window_title
        .with_dimensions(RESOLUTION_X, RESOLUTION_Y)
        .with_min_dimensions(RESOLUTION_X, RESOLUTION_Y)
        .with_max_dimensions(RESOLUTION_X, RESOLUTION_Y)
    } else {
      let monitor = {
        events_loop.get_available_monitors().nth(0).expect("Please enter a valid ID")
      };
      window_title.with_fullscreen(Some(monitor))
        .with_dimensions(RESOLUTION_X, RESOLUTION_Y)
        .with_min_dimensions(RESOLUTION_X, RESOLUTION_Y)
        .with_max_dimensions(RESOLUTION_X, RESOLUTION_Y)
    };

    let context = glutin::ContextBuilder::new()
      .with_vsync(true)
      .with_pixel_format(24, 8)
      .with_gl(glutin::GlRequest::GlThenGles {
        opengles_version: (3, 0),
        opengl_version: (4, 1),
      });

    let (window, device, factory, rtv, dsv) = gfx_window_glutin::init::<ColorFormat, DepthFormat>(builder, context, &events_loop);

    GlutinWindow {
      window,
      controls: None,
      events_loop,
      device,
      factory,
      render_target_view: rtv,
      depth_stencil_view: dsv,
      mouse_pos: (0.0, 0.0),
    }
  }
}

#[derive(Debug, PartialEq, Eq)]
pub enum WindowStatus {
  Open,
  Close,
}

pub trait Window<D: gfx::Device, F: gfx::Factory<D::Resources>> {
  fn swap_window(&mut self);
  fn create_buffers(&mut self, count: usize) -> Vec<D::CommandBuffer>;
  fn set_controls(&mut self, controls: controls::TilemapControls);
  fn get_viewport_size(&mut self) -> (f32, f32);
  fn get_device(&mut self) -> &mut D;
  fn get_factory(&mut self) -> &mut F;
  fn get_hidpi_factor(&mut self) -> f32;
  fn get_render_target_view(&mut self) -> gfx::handle::RenderTargetView<D::Resources, ColorFormat>;
  fn get_depth_stencil_view(&mut self) -> gfx::handle::DepthStencilView<D::Resources, DepthFormat>;
  fn poll_events(&mut self) -> WindowStatus;
}

impl Window<gfx_device_gl::Device, gfx_device_gl::Factory> for GlutinWindow {
  fn swap_window(&mut self) {
    use gfx::Device;
    self.window
        .swap_buffers()
        .expect("Unable to swap buffers");
    self.device.cleanup();
  }

  fn create_buffers(&mut self, count: usize) -> Vec<gfx_device_gl::CommandBuffer> {
    let mut bufs = Vec::new();
    for _ in 0..count {
      bufs.push(self.factory.create_command_buffer());
    }
    bufs
  }

  fn set_controls(&mut self, controls: controls::TilemapControls) {
    self.controls = Some(controls);
  }

  fn get_viewport_size(&mut self) -> (f32, f32) {
    (RESOLUTION_X as f32, RESOLUTION_Y as f32)
  }

  fn get_device(&mut self) -> &mut gfx_device_gl::Device {
    &mut self.device
  }

  fn get_factory(&mut self) -> &mut gfx_device_gl::Factory {
    &mut self.factory
  }

  fn get_hidpi_factor(&mut self) -> f32 {
    self.window.hidpi_factor()
  }

  fn get_render_target_view(&mut self) -> gfx::handle::RenderTargetView<gfx_device_gl::Resources, ColorFormat> {
    self.render_target_view.clone()
  }

  fn get_depth_stencil_view(&mut self) -> gfx::handle::DepthStencilView<gfx_device_gl::Resources, DepthFormat> {
    self.depth_stencil_view.clone()
  }
  fn poll_events(&mut self) -> WindowStatus {
    use glutin::KeyboardInput;
    use glutin::MouseButton;
    use glutin::WindowEvent::{CloseRequested, CursorMoved, MouseInput};
    use glutin::ElementState::{Pressed, Released};
    use glutin::VirtualKeyCode::{Escape, Z, X, W, A, S, D};

    let controls = match self.controls {
      Some(ref mut c) => c,
      None => panic!("Terrain controls have not been initialized"),
    };

    let m_pos = &mut self.mouse_pos;

    let mut game_status = WindowStatus::Open;

    self.events_loop.poll_events(|event| {
      game_status = if let glutin::Event::WindowEvent { event, .. } = event {
        match event {
          glutin::WindowEvent::KeyboardInput { input, .. } => match input {
            KeyboardInput { virtual_keycode: Some(Escape), .. } => WindowStatus::Close,
            KeyboardInput { state: Pressed, virtual_keycode: Some(Z), .. } => {
              controls.zoom_out();
              WindowStatus::Open
            }
            KeyboardInput { state: Pressed, virtual_keycode: Some(X), .. } => {
              controls.zoom_in();
              WindowStatus::Open
            }
            KeyboardInput { state: Released, virtual_keycode: Some(Z), .. } |
            KeyboardInput { state: Released, virtual_keycode: Some(X), .. } => {
              controls.zoom_stop();
              WindowStatus::Open
            }
            KeyboardInput { state: Pressed, virtual_keycode: Some(W), .. } => {
              controls.move_character_up();
              WindowStatus::Open
            }
            KeyboardInput { state: Pressed, virtual_keycode: Some(S), .. } => {
              controls.move_character_down();
              WindowStatus::Open
            }
            KeyboardInput { state: Released, virtual_keycode: Some(W), .. } |
            KeyboardInput { state: Released, virtual_keycode: Some(S), .. } => {
              controls.stop_character_y();
              WindowStatus::Open
            }
            KeyboardInput { state: Pressed, virtual_keycode: Some(A), .. } => {
              controls.move_character_left();
              WindowStatus::Open
            }
            KeyboardInput { state: Pressed, virtual_keycode: Some(D), .. } => {
              controls.move_character_right();
              WindowStatus::Open
            }
            KeyboardInput { state: Released, virtual_keycode: Some(A), .. } |
            KeyboardInput { state: Released, virtual_keycode: Some(D), .. } => {
              controls.stop_character_x();
              WindowStatus::Open
            }
            KeyboardInput { state: Pressed, modifiers, .. } => {
              if modifiers.ctrl {
                controls.ctrl_pressed();
              }
              WindowStatus::Open
            }
            KeyboardInput { state: Released, modifiers, .. } => {
              if !modifiers.ctrl {
                controls.ctrl_released();
              }
              WindowStatus::Open
            }
          },
          MouseInput { state: Pressed, button: MouseButton::Left, .. } => {
            controls.mouse_left_click(Some(*m_pos));
            WindowStatus::Open
          }
          MouseInput { state: Released, button: MouseButton::Left, .. } => {
            controls.mouse_left_click(None);
            WindowStatus::Open
          }
          MouseInput { state: Pressed, button: MouseButton::Right, .. } => {
            controls.mouse_right_click(Some(*m_pos));
            WindowStatus::Open
          }
          MouseInput { state: Released, button: MouseButton::Right, .. } => {
            controls.mouse_right_click(None);
            WindowStatus::Open
          }
          CursorMoved { position, .. } => {
            *m_pos = position;
            WindowStatus::Open
          }
          CloseRequested => WindowStatus::Close,
          _ => WindowStatus::Open,
        }
      } else {
        WindowStatus::Open
      };
    });
    game_status
  }
}

<commit_msg>Reformat gfx_app/mod.rs<commit_after>use game::constants::{RESOLUTION_X, RESOLUTION_Y};
use gfx;
use gfx_device_gl;
use gfx_window_glutin;
use glutin;
use glutin::GlContext;

pub mod init;
pub mod renderer;
pub mod system;
pub mod controls;
pub mod mouse_controls;

pub type ColorFormat = gfx::format::Rgba8;
pub type DepthFormat = gfx::format::DepthStencil;

pub struct GlutinWindow {
  window: glutin::GlWindow,
  controls: Option<controls::TilemapControls>,
  events_loop: glutin::EventsLoop,
  device: gfx_device_gl::Device,
  factory: gfx_device_gl::Factory,
  render_target_view: gfx::handle::RenderTargetView<gfx_device_gl::Resources, ColorFormat>,
  depth_stencil_view: gfx::handle::DepthStencilView<gfx_device_gl::Resources, DepthFormat>,
  mouse_pos: (f64, f64),
}

impl GlutinWindow {
  pub fn new() -> GlutinWindow {
    let events_loop = glutin::EventsLoop::new();

    let window_title = glutin::WindowBuilder::new()
      .with_title("Hinterland");

    let builder = if cfg!(feature = "windowed") {
      window_title
        .with_dimensions(RESOLUTION_X, RESOLUTION_Y)
        .with_min_dimensions(RESOLUTION_X, RESOLUTION_Y)
        .with_max_dimensions(RESOLUTION_X, RESOLUTION_Y)
    } else {
      let monitor = {
        events_loop.get_available_monitors().nth(0).expect("Please enter a valid ID")
      };
      window_title.with_fullscreen(Some(monitor))
        .with_dimensions(RESOLUTION_X, RESOLUTION_Y)
        .with_min_dimensions(RESOLUTION_X, RESOLUTION_Y)
        .with_max_dimensions(RESOLUTION_X, RESOLUTION_Y)
    };

    let context = glutin::ContextBuilder::new()
      .with_vsync(true)
      .with_pixel_format(24, 8)
      .with_gl(glutin::GlRequest::GlThenGles {
        opengles_version: (3, 0),
        opengl_version: (4, 1),
      });

    let (window, device, factory, rtv, dsv) =
      gfx_window_glutin::init::<ColorFormat, DepthFormat>(builder, context, &events_loop);

    GlutinWindow {
      window,
      controls: None,
      events_loop,
      device,
      factory,
      render_target_view: rtv,
      depth_stencil_view: dsv,
      mouse_pos: (0.0, 0.0),
    }
  }
}

#[derive(Debug, PartialEq, Eq)]
pub enum WindowStatus {
  Open,
  Close,
}

pub trait Window<D: gfx::Device, F: gfx::Factory<D::Resources>> {
  fn swap_window(&mut self);
  fn create_buffers(&mut self, count: usize) -> Vec<D::CommandBuffer>;
  fn set_controls(&mut self, controls: controls::TilemapControls);
  fn get_viewport_size(&mut self) -> (f32, f32);
  fn get_device(&mut self) -> &mut D;
  fn get_factory(&mut self) -> &mut F;
  fn get_hidpi_factor(&mut self) -> f32;
  fn get_render_target_view(&mut self) -> gfx::handle::RenderTargetView<D::Resources, ColorFormat>;
  fn get_depth_stencil_view(&mut self) -> gfx::handle::DepthStencilView<D::Resources, DepthFormat>;
  fn poll_events(&mut self) -> WindowStatus;
}

impl Window<gfx_device_gl::Device, gfx_device_gl::Factory> for GlutinWindow {
  fn swap_window(&mut self) {
    use gfx::Device;
    self.window
        .swap_buffers()
        .expect("Unable to swap buffers");
    self.device.cleanup();
  }

  fn create_buffers(&mut self, count: usize) -> Vec<gfx_device_gl::CommandBuffer> {
    let mut bufs = Vec::new();
    for _ in 0..count {
      bufs.push(self.factory.create_command_buffer());
    }
    bufs
  }

  fn set_controls(&mut self, controls: controls::TilemapControls) {
    self.controls = Some(controls);
  }

  fn get_viewport_size(&mut self) -> (f32, f32) {
    (RESOLUTION_X as f32, RESOLUTION_Y as f32)
  }

  fn get_device(&mut self) -> &mut gfx_device_gl::Device {
    &mut self.device
  }

  fn get_factory(&mut self) -> &mut gfx_device_gl::Factory {
    &mut self.factory
  }

  fn get_hidpi_factor(&mut self) -> f32 {
    self.window.hidpi_factor()
  }

  fn get_render_target_view(&mut self) -> gfx::handle::RenderTargetView<gfx_device_gl::Resources, ColorFormat> {
    self.render_target_view.clone()
  }

  fn get_depth_stencil_view(&mut self) -> gfx::handle::DepthStencilView<gfx_device_gl::Resources, DepthFormat> {
    self.depth_stencil_view.clone()
  }

  fn poll_events(&mut self) -> WindowStatus {
    use glutin::KeyboardInput;
    use glutin::MouseButton;
    use glutin::WindowEvent::{CloseRequested, CursorMoved, MouseInput};
    use glutin::ElementState::{Pressed, Released};
    use glutin::VirtualKeyCode::{Escape, Z, X, W, A, S, D};

    let controls = match self.controls {
      Some(ref mut c) => c,
      None => panic!("Terrain controls have not been initialized"),
    };

    let m_pos = &mut self.mouse_pos;

    let mut game_status = WindowStatus::Open;

    self.events_loop.poll_events(|event| {
      game_status = if let glutin::Event::WindowEvent { event, .. } = event {
        match event {
          glutin::WindowEvent::KeyboardInput { input, .. } => match input {
            KeyboardInput { virtual_keycode: Some(Escape), .. } => WindowStatus::Close,
            KeyboardInput { state: Pressed, virtual_keycode: Some(Z), .. } => {
              controls.zoom_out();
              WindowStatus::Open
            }
            KeyboardInput { state: Pressed, virtual_keycode: Some(X), .. } => {
              controls.zoom_in();
              WindowStatus::Open
            }
            KeyboardInput { state: Released, virtual_keycode: Some(Z), .. } |
            KeyboardInput { state: Released, virtual_keycode: Some(X), .. } => {
              controls.zoom_stop();
              WindowStatus::Open
            }
            KeyboardInput { state: Pressed, virtual_keycode: Some(W), .. } => {
              controls.move_character_up();
              WindowStatus::Open
            }
            KeyboardInput { state: Pressed, virtual_keycode: Some(S), .. } => {
              controls.move_character_down();
              WindowStatus::Open
            }
            KeyboardInput { state: Released, virtual_keycode: Some(W), .. } |
            KeyboardInput { state: Released, virtual_keycode: Some(S), .. } => {
              controls.stop_character_y();
              WindowStatus::Open
            }
            KeyboardInput { state: Pressed, virtual_keycode: Some(A), .. } => {
              controls.move_character_left();
              WindowStatus::Open
            }
            KeyboardInput { state: Pressed, virtual_keycode: Some(D), .. } => {
              controls.move_character_right();
              WindowStatus::Open
            }
            KeyboardInput { state: Released, virtual_keycode: Some(A), .. } |
            KeyboardInput { state: Released, virtual_keycode: Some(D), .. } => {
              controls.stop_character_x();
              WindowStatus::Open
            }
            KeyboardInput { state: Pressed, modifiers, .. } => {
              if modifiers.ctrl {
                controls.ctrl_pressed();
              }
              WindowStatus::Open
            }
            KeyboardInput { state: Released, modifiers, .. } => {
              if !modifiers.ctrl {
                controls.ctrl_released();
              }
              WindowStatus::Open
            }
          },
          MouseInput { state: Pressed, button: MouseButton::Left, .. } => {
            controls.mouse_left_click(Some(*m_pos));
            WindowStatus::Open
          }
          MouseInput { state: Released, button: MouseButton::Left, .. } => {
            controls.mouse_left_click(None);
            WindowStatus::Open
          }
          MouseInput { state: Pressed, button: MouseButton::Right, .. } => {
            controls.mouse_right_click(Some(*m_pos));
            WindowStatus::Open
          }
          MouseInput { state: Released, button: MouseButton::Right, .. } => {
            controls.mouse_right_click(None);
            WindowStatus::Open
          }
          CursorMoved { position, .. } => {
            *m_pos = position;
            WindowStatus::Open
          }
          CloseRequested => WindowStatus::Close,
          _ => WindowStatus::Open,
        }
      } else {
        WindowStatus::Open
      };
    });
    game_status
  }
}

<|endoftext|>


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

extern crate clap;
use clap::{Arg, App, SubCommand};
extern crate toml;

// extern crate hyper;
// extern crate hyper_rustls;
// use hyper::Client;
// use hyper::net::HttpsConnector;
// use hyper_rustls::TlsClient;
// use hyper::header::Authorization;
// use hyper::header::{Accept, qitem};
// use hyper::mime::{Mime, TopLevel, SubLevel};

extern crate reqwest;
use reqwest::header::Authorization;

extern crate serde;
extern crate serde_json;
#[macro_use]
extern crate serde_derive;

#[derive(Deserialize, Debug)]
struct Config {
    token: String,
    org: String,
    team: String,
    #[serde(default="default_endpoint")]
    endpoint: String,
    #[serde(default="default_home")]
    home: String,
    #[serde(default="default_gid")]
    gid: u64,
    #[serde(default="default_sh")]
    sh: String,
    group: Option<String>,
}

fn default_endpoint() -> String { String::from("https://api.github.com") }
fn default_home() -> String { String::from("/home/{}") }
fn default_gid() -> u64 { 2000 }
fn default_sh() -> String { String::from("/bin/bash") }

#[derive(Serialize, Deserialize, Debug)]
struct Team {
    id: u64,
    name: String,
}

#[derive(Serialize, Deserialize, Debug)]
struct Member {
    id: u64,
    login: String,
}

#[derive(Serialize, Deserialize, Debug)]
struct PublicKey {
    id: u64,
    key: String,
}

#[derive(Debug)]
enum CliError {
    Serde(serde_json::Error),
    Reqwest(reqwest::Error),
    Io(std::io::Error)
}

impl From<serde_json::Error> for CliError {fn from(err: serde_json::Error) -> CliError { CliError::Serde(err) }}
impl From<reqwest::Error> for CliError {fn from(err: reqwest::Error) -> CliError { CliError::Reqwest(err) }}
impl From<std::io::Error> for CliError {fn from(err: std::io::Error) -> CliError { CliError::Io(err) }}

fn main() {

    let matches = App::new("ghteam-auth")
                      .version("0.1")
                      .author("Yasuyuki YAMADA <yasuyuki.ymd@gmail.com>")
                      .about("")
                      .arg(Arg::with_name("config")
                               .short("c")
                               .long("config")
                               .value_name("FILE")
                               .help("Sets a custom config file (toml)")
                               .takes_value(true))
                      .arg(Arg::with_name("v")
                               .short("v")
                               .multiple(true)
                               .help("Sets the level of verbosity"))
                      .subcommand(SubCommand::with_name("key")
                                             .about("get user public key")
                                             .arg(Arg::with_name("USER")
                                                      .required(true)
                                                      .index(1)
                                                      .help("user name")))
                      .subcommand(SubCommand::with_name("pam")
                                             .about("get user public key")
                                             .arg(Arg::with_name("USER")
                                                      .required(false)
                                                      .index(1)
                                                      .help("user name")))
                      .subcommand(SubCommand::with_name("passwd")
                                             .about("get passwd"))
                      .subcommand(SubCommand::with_name("shadow")
                                             .about("get shadow"))
                      .subcommand(SubCommand::with_name("group")
                                             .about("get group"))
                      .subcommand(SubCommand::with_name("refresh")
                                             .about("refresh cache"))
                      .get_matches();

    let config = matches.value_of("config").unwrap_or("/etc/ghteam-auth.conf");
    let mut file = File::open(config).unwrap();
    let mut contents = String::new();
    file.read_to_string(&mut contents).unwrap();
    let config = toml::from_str::<Config>(contents.as_str()).unwrap();

    // let client = Client::with_connector(HttpsConnector::new(TlsClient::new()));
    let client = reqwest::Client::new().unwrap();
    let client = GithubClient::new(client, config);

    if let Some(matches) = matches.subcommand_matches("key") {
        client.print_user_public_key(matches.value_of("USER").unwrap()).unwrap();
    } else if let Some(_) = matches.subcommand_matches("passwd") {
        client.get_passwd().unwrap();
    } else if let Some(_) = matches.subcommand_matches("shadow") {
        client.get_shadow().unwrap();
    } else if let Some(_) = matches.subcommand_matches("group") {
        client.get_group().unwrap();
    } else if let Some(_) = matches.subcommand_matches("pam") {
        match std::env::var("PAM_USER") {
            Ok(user) => {
                if client.check_pam(&user).unwrap() { std::process::exit(0); }
                else { std::process::exit(1) }
            }
            Err(e) => println!("couldn't interpret PAM_USER: {}", e),
        }
    }

}

struct GithubClient {
    // client: hyper::Client,
    client: reqwest::Client,
    conf: Config
}

impl GithubClient {
    fn new(client:reqwest::Client, conf:Config) -> GithubClient {
        GithubClient {client:client, conf:conf}
    }

    fn get_url_content(&self, url:&String) -> Result<String,CliError> {
        // println!("GET {}", url);
        let token = String::from("token ") + self.conf.token.clone().as_str();
        let res = self.client.get(url.as_str()).header(Authorization(token)).send();
        let mut content = String::new();
        res?.read_to_string(&mut content)?;
        Ok(content)
    }

    fn print_user_public_key(&self, user:&str) -> Result<(), CliError> {
        let keys = self.get_user_public_key(user)?;
        println!("{}", keys);
        Ok(())
    }

    fn get_user_public_key(&self, user:&str) -> Result<String, CliError> {
        let url = format!("{}/users/{}/keys", self.conf.endpoint.clone(),user);
        let content = self.get_url_content(&url);
        let keys = serde_json::from_str::<Vec<PublicKey>>(content?.as_str())?;
        Ok(keys.iter().map(|k|{k.key.clone()}).collect::<Vec<String>>().join("\n"))
    }

    fn check_pam(&self, user:&String) -> Result<bool, CliError> {
        let teams:HashMap<String,Team> = self.get_teams()?;
        if let Some(team) = teams.get(&self.conf.team.clone()) {
            for member in self.get_members(team.id)? {
                if member.login==*user { return Ok(true) }
            }
        }
        Ok(false)
    }

    fn get_passwd(&self) -> Result<(), CliError> {
        let teams:HashMap<String,Team> = self.get_teams()?;
        if let Some(team) = teams.get(&self.conf.team.clone()) {
            for member in self.get_members(team.id)? {
                println!("{}", self.create_passwd_line(&member));
            }
            Ok(())
        } else {
            Ok(())
        }
    }

    fn get_shadow(&self) -> Result<(), CliError> {
        let teams:HashMap<String,Team> = self.get_teams()?;
        if let Some(team) = teams.get(&self.conf.team.clone()) {
            for member in self.get_members(team.id)? {
                println!("{}", self.create_shadow_line(&member));
            }
            Ok(())
        } else {
            Ok(())
        }
    }

    fn get_group(&self) -> Result<(), CliError> {
        let teams:HashMap<String,Team> = self.get_teams()?;
        if let Some(team) = teams.get(&self.conf.team.clone()) {
            let members = self.get_members(team.id)?;
            println!("{}", self.create_group_line(&team.name, self.conf.gid, &members));
            Ok(())
        } else {
            Ok(())
        }
    }

    fn create_passwd_line(&self, member:&Member) -> String {
        format!("{login}:x:{uid}:{gid}:user@{org}:{home}:{sh}",
                login=member.login,
                uid=member.id,
                gid=self.conf.gid,
                org=self.conf.org,
                home=self.conf.home.replace("{}",member.login.as_str()),
                sh=self.conf.sh,
                )
    }

    fn create_shadow_line(&self, member:&Member) -> String {
        format!("{login}:!!:::::::", login=member.login )
    }

    fn create_group_line(&self, name:&String, id:u64, members:&Vec<Member>) -> String {
        format!("{name}:x:{id}:{members}", name=name, id=id,
                members=members.iter().map(|m|{m.login.clone()}).collect::<Vec<String>>().join(","))
    }

    fn get_teams(&self) -> Result<HashMap<String,Team>, CliError> {
        let url = format!("{}/orgs/{}/teams",self.conf.endpoint.clone(), self.conf.org.clone());
        let content = self.get_url_content(&url)?;
        let teams = serde_json::from_str::<Vec<Team>>(content.as_str())?;
        let mut team_map = HashMap::new();
        for team in teams { team_map.insert(team.name.clone(), team); }
        Ok(team_map)
    }

    fn get_members(&self, mid:u64) -> Result<Vec<Member>, CliError> {
        let url = format!("{}/teams/{}/members",self.conf.endpoint.clone(), mid);
        let content = self.get_url_content(&url)?;
        let members = serde_json::from_str::<Vec<Member>>(content.as_str())?;
        Ok(members)
    }

}
<commit_msg>Add default SSL_CERT_FILE path<commit_after>use std::fs::File;
use std::io::prelude::*;
use std::collections::HashMap;

extern crate clap;
use clap::{Arg, App, SubCommand};
extern crate toml;

// extern crate hyper;
// extern crate hyper_rustls;
// use hyper::Client;
// use hyper::net::HttpsConnector;
// use hyper_rustls::TlsClient;
// use hyper::header::Authorization;
// use hyper::header::{Accept, qitem};
// use hyper::mime::{Mime, TopLevel, SubLevel};

extern crate reqwest;
use reqwest::header::Authorization;

extern crate serde;
extern crate serde_json;
#[macro_use]
extern crate serde_derive;

#[derive(Deserialize, Debug)]
struct Config {
    token: String,
    org: String,
    team: String,
    #[serde(default="default_endpoint")]
    endpoint: String,
    #[serde(default="default_home")]
    home: String,
    #[serde(default="default_gid")]
    gid: u64,
    #[serde(default="default_sh")]
    sh: String,
    group: Option<String>,
    #[serde(default="default_cert_path")]
    cert_path: String
}

fn default_endpoint() -> String { String::from("https://api.github.com") }
fn default_home() -> String { String::from("/home/{}") }
fn default_gid() -> u64 { 2000 }
fn default_sh() -> String { String::from("/bin/bash") }
fn default_cert_path() -> String { String::from("/etc/ssl/certs/ca-certificates.crt") }

#[derive(Serialize, Deserialize, Debug)]
struct Team {
    id: u64,
    name: String,
}

#[derive(Serialize, Deserialize, Debug)]
struct Member {
    id: u64,
    login: String,
}

#[derive(Serialize, Deserialize, Debug)]
struct PublicKey {
    id: u64,
    key: String,
}

#[derive(Debug)]
enum CliError {
    Serde(serde_json::Error),
    Reqwest(reqwest::Error),
    Io(std::io::Error)
}

impl From<serde_json::Error> for CliError {fn from(err: serde_json::Error) -> CliError { CliError::Serde(err) }}
impl From<reqwest::Error> for CliError {fn from(err: reqwest::Error) -> CliError { CliError::Reqwest(err) }}
impl From<std::io::Error> for CliError {fn from(err: std::io::Error) -> CliError { CliError::Io(err) }}

fn main() {

    let matches = App::new("ghteam-auth")
                      .version("0.1")
                      .author("Yasuyuki YAMADA <yasuyuki.ymd@gmail.com>")
                      .about("")
                      .arg(Arg::with_name("config")
                               .short("c")
                               .long("config")
                               .value_name("FILE")
                               .help("Sets a custom config file (toml)")
                               .takes_value(true))
                      .arg(Arg::with_name("v")
                               .short("v")
                               .multiple(true)
                               .help("Sets the level of verbosity"))
                      .subcommand(SubCommand::with_name("key")
                                             .about("get user public key")
                                             .arg(Arg::with_name("USER")
                                                      .required(true)
                                                      .index(1)
                                                      .help("user name")))
                      .subcommand(SubCommand::with_name("pam")
                                             .about("get user public key")
                                             .arg(Arg::with_name("USER")
                                                      .required(false)
                                                      .index(1)
                                                      .help("user name")))
                      .subcommand(SubCommand::with_name("passwd")
                                             .about("get passwd"))
                      .subcommand(SubCommand::with_name("shadow")
                                             .about("get shadow"))
                      .subcommand(SubCommand::with_name("group")
                                             .about("get group"))
                      .subcommand(SubCommand::with_name("refresh")
                                             .about("refresh cache"))
                      .get_matches();

    let config = matches.value_of("config").unwrap_or("/etc/ghteam-auth.conf");
    let mut file = File::open(config).unwrap();
    let mut contents = String::new();
    file.read_to_string(&mut contents).unwrap();
    let config = toml::from_str::<Config>(contents.as_str()).unwrap();

    match std::env::var("SSL_CERT_FILE") {
        Ok(_) => (),
        Err(_) => std::env::set_var("SSL_CERT_FILE", config.cert_path.clone()),
    }

    // let client = Client::with_connector(HttpsConnector::new(TlsClient::new()));
    let client = reqwest::Client::new().unwrap();
    let client = GithubClient::new(client, config);

    if let Some(matches) = matches.subcommand_matches("key") {
        client.print_user_public_key(matches.value_of("USER").unwrap()).unwrap();
    } else if let Some(_) = matches.subcommand_matches("passwd") {
        client.get_passwd().unwrap();
    } else if let Some(_) = matches.subcommand_matches("shadow") {
        client.get_shadow().unwrap();
    } else if let Some(_) = matches.subcommand_matches("group") {
        client.get_group().unwrap();
    } else if let Some(_) = matches.subcommand_matches("pam") {
        match std::env::var("PAM_USER") {
            Ok(user) => {
                if client.check_pam(&user).unwrap() { std::process::exit(0); }
                else { std::process::exit(1) }
            }
            Err(e) => println!("couldn't interpret PAM_USER: {}", e),
        }
    }

}

struct GithubClient {
    // client: hyper::Client,
    client: reqwest::Client,
    conf: Config
}

impl GithubClient {
    fn new(client:reqwest::Client, conf:Config) -> GithubClient {
        GithubClient {client:client, conf:conf}
    }

    fn get_url_content(&self, url:&String) -> Result<String,CliError> {
        // println!("GET {}", url);
        let token = String::from("token ") + self.conf.token.clone().as_str();
        let res = self.client.get(url.as_str()).header(Authorization(token)).send();
        let mut content = String::new();
        res?.read_to_string(&mut content)?;
        Ok(content)
    }

    fn print_user_public_key(&self, user:&str) -> Result<(), CliError> {
        let keys = self.get_user_public_key(user)?;
        println!("{}", keys);
        Ok(())
    }

    fn get_user_public_key(&self, user:&str) -> Result<String, CliError> {
        let url = format!("{}/users/{}/keys", self.conf.endpoint.clone(),user);
        let content = self.get_url_content(&url);
        let keys = serde_json::from_str::<Vec<PublicKey>>(content?.as_str())?;
        Ok(keys.iter().map(|k|{k.key.clone()}).collect::<Vec<String>>().join("\n"))
    }

    fn check_pam(&self, user:&String) -> Result<bool, CliError> {
        let teams:HashMap<String,Team> = self.get_teams()?;
        if let Some(team) = teams.get(&self.conf.team.clone()) {
            for member in self.get_members(team.id)? {
                if member.login==*user { return Ok(true) }
            }
        }
        Ok(false)
    }

    fn get_passwd(&self) -> Result<(), CliError> {
        let teams:HashMap<String,Team> = self.get_teams()?;
        if let Some(team) = teams.get(&self.conf.team.clone()) {
            for member in self.get_members(team.id)? {
                println!("{}", self.create_passwd_line(&member));
            }
            Ok(())
        } else {
            Ok(())
        }
    }

    fn get_shadow(&self) -> Result<(), CliError> {
        let teams:HashMap<String,Team> = self.get_teams()?;
        if let Some(team) = teams.get(&self.conf.team.clone()) {
            for member in self.get_members(team.id)? {
                println!("{}", self.create_shadow_line(&member));
            }
            Ok(())
        } else {
            Ok(())
        }
    }

    fn get_group(&self) -> Result<(), CliError> {
        let teams:HashMap<String,Team> = self.get_teams()?;
        if let Some(team) = teams.get(&self.conf.team.clone()) {
            let members = self.get_members(team.id)?;
            println!("{}", self.create_group_line(&team.name, self.conf.gid, &members));
            Ok(())
        } else {
            Ok(())
        }
    }

    fn create_passwd_line(&self, member:&Member) -> String {
        format!("{login}:x:{uid}:{gid}:user@{org}:{home}:{sh}",
                login=member.login,
                uid=member.id,
                gid=self.conf.gid,
                org=self.conf.org,
                home=self.conf.home.replace("{}",member.login.as_str()),
                sh=self.conf.sh,
                )
    }

    fn create_shadow_line(&self, member:&Member) -> String {
        format!("{login}:!!:::::::", login=member.login )
    }

    fn create_group_line(&self, name:&String, id:u64, members:&Vec<Member>) -> String {
        format!("{name}:x:{id}:{members}", name=name, id=id,
                members=members.iter().map(|m|{m.login.clone()}).collect::<Vec<String>>().join(","))
    }

    fn get_teams(&self) -> Result<HashMap<String,Team>, CliError> {
        let url = format!("{}/orgs/{}/teams",self.conf.endpoint.clone(), self.conf.org.clone());
        let content = self.get_url_content(&url)?;
        let teams = serde_json::from_str::<Vec<Team>>(content.as_str())?;
        let mut team_map = HashMap::new();
        for team in teams { team_map.insert(team.name.clone(), team); }
        Ok(team_map)
    }

    fn get_members(&self, mid:u64) -> Result<Vec<Member>, CliError> {
        let url = format!("{}/teams/{}/members",self.conf.endpoint.clone(), mid);
        let content = self.get_url_content(&url)?;
        let members = serde_json::from_str::<Vec<Member>>(content.as_str())?;
        Ok(members)
    }

}
<|endoftext|>