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

use app_result::{AppResult, app_err};

use types::{
   TagsRoot,
   TagsRoots,
   SourceKind
};

/// Reads the dependecies from the `Cargo.toml` located in `cargo_toml_dir`
pub fn read_dependencies(cargo_toml_dir: &Path) -> AppResult<TagsRoots>
{
   let mut cargo_toml = cargo_toml_dir.clone();
   cargo_toml.push("Cargo.toml");

   let toml_string = try!(io::File::open(&cargo_toml).read_to_string());
   let mut toml_parser = toml::Parser::new(&*toml_string);

   let toml_table = try!(
      toml_parser.parse()
         .ok_or_else(|| app_err(format!("Couldn't parse '{}': {}", cargo_toml.display(), toml_parser.errors)))
   );

   let mut tags_roots: TagsRoots = Vec::new();
   let deps_string = "dependencies".to_string();
   if ! toml_table.contains_key(&deps_string) {
      return Ok(tags_roots);
   }

   let deps_table = try!(
      toml_table.get(&deps_string)
         .and_then(|value| value.as_table())
         .ok_or(app_err(format!("Couldn't get toml::Table entry for 'dependency'!")))
   );

   let mut cargo_lock = cargo_toml_dir.clone();
   cargo_lock.push("Cargo.lock");

   let lock_string = try!(io::File::open(&cargo_lock).read_to_string());
   let mut lock_parser = toml::Parser::new(&*lock_string);

   let lock_table = try!(
      lock_parser.parse()
         .ok_or_else(|| app_err(format!("Couldn't parse '{}': {}", cargo_lock.display(), lock_parser.errors)))
   );

   let packages = try!(
      lock_table.get(&"package".to_string())
         .and_then(|value| value.as_slice())
         .and_then(|slice| {
            let tables = slice.iter()
               .filter_map(|value| value.as_table())
               .collect::<Vec<&toml::Table>>();

            if ! tables.is_empty() { Some(tables) } else { None }
         })
         .ok_or(app_err(format!("Couldn't get Array of Tables entry for 'package'!")))
   );

   for package in packages.iter() {
      let lib_name = try!(
         package.get(&"name".to_string())
            .and_then(|n| n.as_str().map(|n| n.to_string()))
            .ok_or_else(|| app_err(format!("Couldn't find name string in package: '{}'!", package)))
      );

      let dep_names = try!(get_dependencies(*package));
      let mut dep_src_kinds: Vec<SourceKind> = Vec::new();
      for dep_name in dep_names.iter() {
         let dep_package = try!(find_package(&packages, dep_name));
         dep_src_kinds.push(try!(get_source_kind(dep_package, dep_name)));
      }

      let src_kind = try!(get_source_kind(*package, &lib_name));
      tags_roots.push(TagsRoot::Lib { src_kind: src_kind, dependencies: dep_src_kinds });
   }

   let mut lib_src_kinds: Vec<SourceKind> = Vec::new();
   for (lib_name, value) in deps_table.iter() {
      match *value {
         toml::Value::String(_) | toml::Value::Table(_) => {
            let lib_package = try!(find_package(&packages, lib_name));
            lib_src_kinds.push(try!(get_source_kind(lib_package, lib_name)));
         }

         _ => {
            return Err(app_err(format!(
               "Expected a String or a Table for the dependency with the name '{}', but got: '{}'", lib_name, value
            )));
         }
      }
   }

   tags_roots.push(TagsRoot::Src { src_dir: cargo_toml_dir.clone(), dependencies: lib_src_kinds });

   Ok(tags_roots)
}

fn get_source_kind(lib_package: &toml::Table, lib_name: &String) -> AppResult<SourceKind>
{
   let version_str = &String::from_str("version");
   let source_str = &String::from_str("source");

   let source = try!(
      lib_package.get(source_str)
         .and_then(|src| src.as_str())
         .ok_or_else(|| app_err(format!("Couldn't find source string in package: '{}'!", lib_package)))
   );

   let src_type = try!(
      source.split('+')
         .nth(0)
         .ok_or_else(|| app_err(format!("Couldn't find source type in: '{}'!", source)))
   );

   let version = try!(
      lib_package.get(version_str)
         .and_then(|vers| vers.as_str())
         .ok_or_else(|| app_err(format!("Couldn't find version string in package: '{}'!", lib_package)))
   );

   match src_type {
      "git" => {
         let commit_hash = try!(
            source.split('#')
               .last()
               .ok_or_else(|| app_err(format!("Couldn't find commit hash in source entry: '{}'!", source)))
         );

         Ok(SourceKind::Git { lib_name: lib_name.clone(), commit_hash: commit_hash.to_string() })
      },

      "registry" => {
         Ok(SourceKind::CratesIo { lib_name: lib_name.clone(), version: version.to_string() })
      }

      _ => {
         Err(app_err(format!("Unexpected source type '{}' in package: '{}'!", src_type, lib_package)))
      }
   }
}

fn get_dependencies(lib_package: &toml::Table) -> AppResult<Vec<String>>
{
   let deps_str = &String::from_str("dependencies");
   let dep_strs = match lib_package.get(deps_str) {
      None => {
         Vec::<&str>::new()
      },

      Some(value) => {
         try!(
            value.as_slice()
               .and_then(|slice| {
                  let ds = slice.iter()
                     .filter_map(|v| v.as_str())
                     .collect::<Vec<&str>>();

                  if ! ds.is_empty() { Some(ds) } else { None }
               })
               .ok_or_else(|| app_err(format!("Couldn't get Array of Strings for 'dependencies' entry: '{}'!", lib_package)))
         )
      }
   };

   let mut dep_names: Vec<String> = Vec::new();
   for dep_str in dep_strs.iter() {
      let dep_name = try!(
         dep_str.split(' ')
            .nth(0)
            .map(|d| d.to_string())
            .ok_or_else(|| app_err(format!("Couldn't get name from dependency: '{}'!", dep_str)))
      );

      dep_names.push(dep_name);
   }

   Ok(dep_names)
}

fn find_package<'a>(packages: &'a Vec<&toml::Table>, lib_name: &String) -> AppResult<&'a toml::Table>
{
   let name_str = &String::from_str("name");

   let package = try!(
      packages.iter()
         .find(|t| t.get(name_str).and_then(|n| n.as_str()) == Some(lib_name[]))
         .ok_or_else(|| app_err(format!("Couldn't find package with name = '{}'!", lib_name)))
   );

   Ok(*package)
}
<commit_msg>Create tags for projects without dependencies<commit_after>use std::io;
use toml;

use app_result::{AppResult, app_err};

use types::{
   TagsRoot,
   TagsRoots,
   SourceKind
};

/// Reads the dependecies from the `Cargo.toml` located in `cargo_toml_dir`
pub fn read_dependencies(cargo_toml_dir: &Path) -> AppResult<TagsRoots>
{
   let mut cargo_toml = cargo_toml_dir.clone();
   cargo_toml.push("Cargo.toml");

   let toml_string = try!(io::File::open(&cargo_toml).read_to_string());
   let mut toml_parser = toml::Parser::new(&*toml_string);

   let toml_table = try!(
      toml_parser.parse()
         .ok_or_else(|| app_err(format!("Couldn't parse '{}': {}", cargo_toml.display(), toml_parser.errors)))
   );

   let mut tags_roots: TagsRoots = Vec::new();
   let deps_string = "dependencies".to_string();
   if ! toml_table.contains_key(&deps_string) {
      tags_roots.push(TagsRoot::Src { src_dir: cargo_toml_dir.clone(), dependencies: Vec::new() });
      return Ok(tags_roots);
   }

   let deps_table = try!(
      toml_table.get(&deps_string)
         .and_then(|value| value.as_table())
         .ok_or(app_err(format!("Couldn't get toml::Table entry for 'dependency'!")))
   );

   let mut cargo_lock = cargo_toml_dir.clone();
   cargo_lock.push("Cargo.lock");

   let lock_string = try!(io::File::open(&cargo_lock).read_to_string());
   let mut lock_parser = toml::Parser::new(&*lock_string);

   let lock_table = try!(
      lock_parser.parse()
         .ok_or_else(|| app_err(format!("Couldn't parse '{}': {}", cargo_lock.display(), lock_parser.errors)))
   );

   let packages = try!(
      lock_table.get(&"package".to_string())
         .and_then(|value| value.as_slice())
         .and_then(|slice| {
            let tables = slice.iter()
               .filter_map(|value| value.as_table())
               .collect::<Vec<&toml::Table>>();

            if ! tables.is_empty() { Some(tables) } else { None }
         })
         .ok_or(app_err(format!("Couldn't get Array of Tables entry for 'package'!")))
   );

   for package in packages.iter() {
      let lib_name = try!(
         package.get(&"name".to_string())
            .and_then(|n| n.as_str().map(|n| n.to_string()))
            .ok_or_else(|| app_err(format!("Couldn't find name string in package: '{}'!", package)))
      );

      let dep_names = try!(get_dependencies(*package));
      let mut dep_src_kinds: Vec<SourceKind> = Vec::new();
      for dep_name in dep_names.iter() {
         let dep_package = try!(find_package(&packages, dep_name));
         dep_src_kinds.push(try!(get_source_kind(dep_package, dep_name)));
      }

      let src_kind = try!(get_source_kind(*package, &lib_name));
      tags_roots.push(TagsRoot::Lib { src_kind: src_kind, dependencies: dep_src_kinds });
   }

   let mut lib_src_kinds: Vec<SourceKind> = Vec::new();
   for (lib_name, value) in deps_table.iter() {
      match *value {
         toml::Value::String(_) | toml::Value::Table(_) => {
            let lib_package = try!(find_package(&packages, lib_name));
            lib_src_kinds.push(try!(get_source_kind(lib_package, lib_name)));
         }

         _ => {
            return Err(app_err(format!(
               "Expected a String or a Table for the dependency with the name '{}', but got: '{}'", lib_name, value
            )));
         }
      }
   }

   tags_roots.push(TagsRoot::Src { src_dir: cargo_toml_dir.clone(), dependencies: lib_src_kinds });

   Ok(tags_roots)
}

fn get_source_kind(lib_package: &toml::Table, lib_name: &String) -> AppResult<SourceKind>
{
   let version_str = &String::from_str("version");
   let source_str = &String::from_str("source");

   let source = try!(
      lib_package.get(source_str)
         .and_then(|src| src.as_str())
         .ok_or_else(|| app_err(format!("Couldn't find source string in package: '{}'!", lib_package)))
   );

   let src_type = try!(
      source.split('+')
         .nth(0)
         .ok_or_else(|| app_err(format!("Couldn't find source type in: '{}'!", source)))
   );

   let version = try!(
      lib_package.get(version_str)
         .and_then(|vers| vers.as_str())
         .ok_or_else(|| app_err(format!("Couldn't find version string in package: '{}'!", lib_package)))
   );

   match src_type {
      "git" => {
         let commit_hash = try!(
            source.split('#')
               .last()
               .ok_or_else(|| app_err(format!("Couldn't find commit hash in source entry: '{}'!", source)))
         );

         Ok(SourceKind::Git { lib_name: lib_name.clone(), commit_hash: commit_hash.to_string() })
      },

      "registry" => {
         Ok(SourceKind::CratesIo { lib_name: lib_name.clone(), version: version.to_string() })
      }

      _ => {
         Err(app_err(format!("Unexpected source type '{}' in package: '{}'!", src_type, lib_package)))
      }
   }
}

fn get_dependencies(lib_package: &toml::Table) -> AppResult<Vec<String>>
{
   let deps_str = &String::from_str("dependencies");
   let dep_strs = match lib_package.get(deps_str) {
      None => {
         Vec::<&str>::new()
      },

      Some(value) => {
         try!(
            value.as_slice()
               .and_then(|slice| {
                  let ds = slice.iter()
                     .filter_map(|v| v.as_str())
                     .collect::<Vec<&str>>();

                  if ! ds.is_empty() { Some(ds) } else { None }
               })
               .ok_or_else(|| app_err(format!("Couldn't get Array of Strings for 'dependencies' entry: '{}'!", lib_package)))
         )
      }
   };

   let mut dep_names: Vec<String> = Vec::new();
   for dep_str in dep_strs.iter() {
      let dep_name = try!(
         dep_str.split(' ')
            .nth(0)
            .map(|d| d.to_string())
            .ok_or_else(|| app_err(format!("Couldn't get name from dependency: '{}'!", dep_str)))
      );

      dep_names.push(dep_name);
   }

   Ok(dep_names)
}

fn find_package<'a>(packages: &'a Vec<&toml::Table>, lib_name: &String) -> AppResult<&'a toml::Table>
{
   let name_str = &String::from_str("name");

   let package = try!(
      packages.iter()
         .find(|t| t.get(name_str).and_then(|n| n.as_str()) == Some(lib_name[]))
         .ok_or_else(|| app_err(format!("Couldn't find package with name = '{}'!", lib_name)))
   );

   Ok(*package)
}
<|endoftext|>


<commit_before>//! Construct bitmap font using FreeType library.
//! Generates raw texture data for the given font and collects information
//! about available font characters to map them into texture.

use std::cmp::max;
use std::iter::{repeat, FromIterator};
use std::collections::{HashMap, HashSet};
use std::char::from_u32;
use ::freetype as ft;
use ::freetype::Face;

#[derive(Debug)]
pub struct BitmapFont {
    width: u16,
    height: u16,
    chars: HashMap<char, BitmapChar>,
    image: Vec<u8>,
}

#[derive(Debug)]
pub struct BitmapChar {
    // Real glyph's coordinates in pixels.
    pub x_offset: i32,
    pub y_offset: i32,
    pub x_advance: i32,
    pub width: i32,
    pub height: i32,
    // Precalculated scaled positions in texture.
    pub tex: [f32; 2],
    pub tex_width: f32,
    pub tex_height: f32,
    // This field is used only while building the texture.
    data: Option<Vec<u8>>,
}

/// Represents possible errors that may occur during the font loading.
#[derive(Debug)]
pub enum FontError {
    /// FreeType library error
    FreetypeError(ft::Error),
}

impl From<ft::Error> for FontError {
    fn from(e: ft::Error) -> FontError { FontError::FreetypeError(e) }
}

pub type FontResult = Result<BitmapFont, FontError>;

impl BitmapFont {
    pub fn from_path(path: &str, font_size: u8, chars: Option<&[char]>) -> FontResult {
        let library = try!(ft::Library::init());
        let face = try!(library.new_face(path, 0));
        Self::new(face, font_size, chars)
    }

    pub fn from_bytes(data: &[u8], font_size: u8, chars: Option<&[char]>) -> FontResult {
        let library = try!(ft::Library::init());
        let face = try!(library.new_memory_face(data, 0));
        Self::new(face, font_size, chars)
    }

    fn get_all_face_chars<'a>(face: &mut Face<'a>) -> HashSet<char> {
        let mut result = HashSet::new();
        let mut index = 0;
        let mut face_ptr = face.raw_mut();
        unsafe {
            let mut code = ft::ffi::FT_Get_First_Char(face_ptr, &mut index);
            while index != 0 {
                from_u32(code as u32).map(|ch| result.insert(ch));
                code = ft::ffi::FT_Get_Next_Char(face_ptr, code, &mut index);
            }
        }
        result
    }

    // FIXME(Kagami): Profile and optimize this function!
    // TODO(Kagami): Limit too huge textures? We should keep it less than
    // 8k X 8k in general.
    // TODO(Kagami): Add bunch of asserts to check for negative values and
    // overflows.
    /// Construct new BitMap font using provided parameters (this is general
    /// method, called via `from_` helpers).
    fn new<'a>(mut face: ft::Face<'a>, font_size: u8, chars: Option<&[char]>) -> FontResult {
        let needed_chars = chars
            .map(|sl| HashSet::from_iter(sl.iter().cloned()))
            .unwrap_or_else(|| Self::get_all_face_chars(&mut face));
        if needed_chars.is_empty() {
            // Short-circuit.
            return Ok(BitmapFont {
                width: 0,
                height: 0,
                chars: HashMap::new(),
                image: Vec::new(),
            })
        }

        try!(face.set_pixel_sizes(0, font_size as u32));

        // FreeType representation of rendered glyph 'j':
        //
        // b_left   w
        // +-----+-----+-----+
        // |     |     |     | font_size - bitmap_top()
        // +-----+-----+-----+
        // |     |  x  |     |
        // |     |     |     |
        // |     |  x  |     | bitmap_top()
        // |     |  x  |     |
        // |     |  x  |     |
        // |     |  x  |     |
        // +-----+--x--+-----+
        // |     | x   |     | rows() - bitmap_top()
        // |     |x    |     |
        // +-----------+-----+
        //      advance.x
        //
        // (Read <http://www.freetype.org/freetype2/docs/glyphs/glyphs-3.html>
        // for more details.)
        //
        // Notes:
        // * Width/height of the rendered glyph generally smaller than the the
        //   specified font size
        // * But if we add x/y offsets to the real glyph's dimensions it might
        //   go beyound that limits (e.g. chars like 'j', 'q')
        // * `bottom_left()` may be less than zero for some tight characters
        //   (too push it to the previous one)
        // * Theoretically `bitmap_top()` may be bigger than the `font_size`
        //
        // For simplicity we use fixed box height to store characters in the
        // texture (extended with blank pixels downwards), but width may vary:
        //
        //         width()
        //  +-----+-------+
        //  |  x  |   x   |
        //  |     |       |
        //  |  x  |   x   |
        //  |  x  |   x   | rows()
        //  |  x  |   x   |
        //  |  x  |   x   |
        //  +-----+  x    |
        //  |     | x     |
        //  |     +-------+
        //  |     |       | ch_box_height - rows()
        //  +-----+-------+
        //
        // To construct the optimal texture (i.e. square enought and with box
        // height of the highest character) we need to do several passes.

        // In first pass we collect information about the chars and store their
        // raw bitmap data. It gives us max character height and summary width
        // of all characters.

        let chars_len = needed_chars.len();
        let mut chars_info = HashMap::with_capacity(chars_len);
        let mut sum_image_width = 0;
        let mut max_ch_width = 0;
        let mut ch_box_height = 0;

        debug!("Start building the bitmap (chars: {})", chars_len);

        for ch in needed_chars {
            try!(face.load_char(ch as usize, ft::face::RENDER));
            let glyph = face.glyph();
            let bitmap = glyph.bitmap();
            let ch_width = bitmap.width();
            let ch_height = bitmap.rows();
            let ch_x_offset = glyph.bitmap_left();
            let ch_y_offset = font_size as i32 - glyph.bitmap_top();
            let ch_x_advance = (glyph.advance().x >> 6) as i32;
            let buffer = bitmap.buffer();
            let ch_data = Vec::from(buffer);

            chars_info.insert(ch, BitmapChar {
                x_offset: ch_x_offset,
                y_offset: ch_y_offset,
                x_advance: ch_x_advance,
                width: ch_width,
                height: ch_height,
                // We'll need to fix that fields later:
                tex: [0.0, 0.0],
                tex_width: 0.0,
                tex_height: 0.0,
                data: Some(ch_data),
            });

            sum_image_width += ch_width;
            max_ch_width = max(max_ch_width, ch_width);
            ch_box_height = max(ch_box_height, ch_height);
        }

        // In second pass we map character boxes with varying width onto the
        // fixed quad texture image and build the final texture image.
        //
        // We start with optimist (square) assumption about texture dimensions
        // and adjust the image's height and size while filling the rows.
        //
        // TODO(Kagami): We may try some cool CS algorithm to fit char boxes
        // into the quad texture space with the best level of compression.
        // Though current level of inefficiency is good enough.

        let ideal_image_size = sum_image_width * ch_box_height;
        let ideal_image_width = (ideal_image_size as f32).sqrt() as i32;
        let image_width = max(max_ch_width, ideal_image_width);
        let image_width = (image_width + 3) & !3; // aligning to 4
        let assumed_size = ideal_image_size as f32 * 1.5;
        let assumed_ch_in_row = image_width as f32 / max_ch_width as f32;
        let mut image = Vec::with_capacity(assumed_size as usize);
        let mut chars_row = Vec::with_capacity(assumed_ch_in_row as usize);
        let mut cursor_x = 0;
        let mut image_height = 0;

        let dump_row = |image: &mut Vec<u8>, chars_row: &Vec<(i32, i32, Vec<u8>)>| {
            // Copy character data into the image row by row:
            //
            //       image_width
            // +-------+---------+---+
            // |   x   |    x    |   |
            // |       |         |   |
            // |   x   |    x    |   | ch_box_height
            // |   x   |    x    |   |
            // |   x   |    x    |   |
            // |   x   |   x     |   |
            // |       |  x      |   |
            // +-------+---------+---+
            //                     ^--- image_width - width_ch_i - width_ch_j
            for i in 0..ch_box_height {
                let mut x = 0;
                for &(width, height, ref data) in chars_row {
                   if i >= height {
                       image.extend(repeat(0).take(width as usize));
                   } else {
                       let skip = i * width;
                       debug_assert!(data.len() >= (skip + width) as usize);
                       let line = data.iter().skip(skip as usize).take(width as usize);
                       image.extend(line.cloned());
                   };
                   x += width;
                }
                let cols_to_fill = image_width - x;
                image.extend(repeat(0).take(cols_to_fill as usize));
            }
        };

        debug!("Placing chars onto a plane");

        // Hashmap doesn't preserve the order but we don't need it anyway.
        for (_, ch_info) in chars_info.iter_mut() {
            if cursor_x + ch_info.width > image_width {
                dump_row(&mut image, &chars_row);
                chars_row.clear();
                cursor_x = 0;
                image_height += ch_box_height;
            }
            let ch_data = ch_info.data.take().unwrap();
            chars_row.push((ch_info.width, ch_info.height, ch_data));
            ch_info.tex = [cursor_x as f32, image_height as f32];
            cursor_x += ch_info.width;
        }
        dump_row(&mut image, &chars_row);
        image_height += ch_box_height;

        // Finally, we just precalculate some fields to make it easier to use
        // our font.

        for (_, ch_info) in chars_info.iter_mut() {
            ch_info.tex[0] /= image_width as f32;
            ch_info.tex[1] /= image_height as f32;
            ch_info.tex_width = ch_info.width as f32 / image_width as f32;
            ch_info.tex_height = ch_info.height as f32 / image_height as f32;
        }

        info!("Image width: {}, image height: {}, total size: {}",
            image_width, image_height, image.len());

        Ok(BitmapFont {
            width: image_width as u16,
            height: image_height as u16,
            chars: chars_info,
            image: image,
        })
    }

    pub fn get_width(&self) -> u16 {
        self.width
    }

    pub fn get_height(&self) -> u16 {
        self.height
    }

    /// Return 8-bit texture raw data (grayscale).
    pub fn get_image(&self) -> &[u8] {
        &self.image
    }

    pub fn find_char(&self, ch: char) -> Option<&BitmapChar> {
        self.chars.get(&ch)
    }
}
<commit_msg>Return error on empty font since it's not usable<commit_after>//! Construct bitmap font using FreeType library.
//! Generates raw texture data for the given font and collects information
//! about available font characters to map them into texture.

use std::cmp::max;
use std::iter::{repeat, FromIterator};
use std::collections::{HashMap, HashSet};
use std::char::from_u32;
use ::freetype as ft;
use ::freetype::Face;

#[derive(Debug)]
pub struct BitmapFont {
    width: u16,
    height: u16,
    chars: HashMap<char, BitmapChar>,
    image: Vec<u8>,
}

#[derive(Debug)]
pub struct BitmapChar {
    // Real glyph's coordinates in pixels.
    pub x_offset: i32,
    pub y_offset: i32,
    pub x_advance: i32,
    pub width: i32,
    pub height: i32,
    // Precalculated scaled positions in texture.
    pub tex: [f32; 2],
    pub tex_width: f32,
    pub tex_height: f32,
    // This field is used only while building the texture.
    data: Option<Vec<u8>>,
}

/// Represents possible errors that may occur during the font loading.
#[derive(Debug)]
pub enum FontError {
    /// Character set is empty
    EmptyFont,
    /// FreeType library error
    FreetypeError(ft::Error),
}

impl From<ft::Error> for FontError {
    fn from(e: ft::Error) -> FontError { FontError::FreetypeError(e) }
}

pub type FontResult = Result<BitmapFont, FontError>;

impl BitmapFont {
    pub fn from_path(path: &str, font_size: u8, chars: Option<&[char]>) -> FontResult {
        let library = try!(ft::Library::init());
        let face = try!(library.new_face(path, 0));
        Self::new(face, font_size, chars)
    }

    pub fn from_bytes(data: &[u8], font_size: u8, chars: Option<&[char]>) -> FontResult {
        let library = try!(ft::Library::init());
        let face = try!(library.new_memory_face(data, 0));
        Self::new(face, font_size, chars)
    }

    fn get_all_face_chars<'a>(face: &mut Face<'a>) -> HashSet<char> {
        let mut result = HashSet::new();
        let mut index = 0;
        let mut face_ptr = face.raw_mut();
        unsafe {
            let mut code = ft::ffi::FT_Get_First_Char(face_ptr, &mut index);
            while index != 0 {
                from_u32(code as u32).map(|ch| result.insert(ch));
                code = ft::ffi::FT_Get_Next_Char(face_ptr, code, &mut index);
            }
        }
        result
    }

    // FIXME(Kagami): Profile and optimize this function!
    // TODO(Kagami): Limit too huge textures? We should keep it less than
    // 8k X 8k in general.
    // TODO(Kagami): Add bunch of asserts to check for negative values and
    // overflows.
    /// Construct new BitMap font using provided parameters (this is general
    /// method, called via `from_` helpers).
    fn new<'a>(mut face: ft::Face<'a>, font_size: u8, chars: Option<&[char]>) -> FontResult {
        let needed_chars = chars
            .map(|sl| HashSet::from_iter(sl.iter().cloned()))
            .unwrap_or_else(|| Self::get_all_face_chars(&mut face));
        // Short-circuit.
        if needed_chars.is_empty() {
            return Err(FontError::EmptyFont);
        }

        try!(face.set_pixel_sizes(0, font_size as u32));

        // FreeType representation of rendered glyph 'j':
        //
        // b_left   w
        // +-----+-----+-----+
        // |     |     |     | font_size - bitmap_top()
        // +-----+-----+-----+
        // |     |  x  |     |
        // |     |     |     |
        // |     |  x  |     | bitmap_top()
        // |     |  x  |     |
        // |     |  x  |     |
        // |     |  x  |     |
        // +-----+--x--+-----+
        // |     | x   |     | rows() - bitmap_top()
        // |     |x    |     |
        // +-----------+-----+
        //      advance.x
        //
        // (Read <http://www.freetype.org/freetype2/docs/glyphs/glyphs-3.html>
        // for more details.)
        //
        // Notes:
        // * Width/height of the rendered glyph generally smaller than the the
        //   specified font size
        // * But if we add x/y offsets to the real glyph's dimensions it might
        //   go beyound that limits (e.g. chars like 'j', 'q')
        // * `bottom_left()` may be less than zero for some tight characters
        //   (too push it to the previous one)
        // * Theoretically `bitmap_top()` may be bigger than the `font_size`
        //
        // For simplicity we use fixed box height to store characters in the
        // texture (extended with blank pixels downwards), but width may vary:
        //
        //         width()
        //  +-----+-------+
        //  |  x  |   x   |
        //  |     |       |
        //  |  x  |   x   |
        //  |  x  |   x   | rows()
        //  |  x  |   x   |
        //  |  x  |   x   |
        //  +-----+  x    |
        //  |     | x     |
        //  |     +-------+
        //  |     |       | ch_box_height - rows()
        //  +-----+-------+
        //
        // To construct the optimal texture (i.e. square enought and with box
        // height of the highest character) we need to do several passes.

        // In first pass we collect information about the chars and store their
        // raw bitmap data. It gives us max character height and summary width
        // of all characters.

        let chars_len = needed_chars.len();
        let mut chars_info = HashMap::with_capacity(chars_len);
        let mut sum_image_width = 0;
        let mut max_ch_width = 0;
        let mut ch_box_height = 0;

        debug!("Start building the bitmap (chars: {})", chars_len);

        for ch in needed_chars {
            try!(face.load_char(ch as usize, ft::face::RENDER));
            let glyph = face.glyph();
            let bitmap = glyph.bitmap();
            let ch_width = bitmap.width();
            let ch_height = bitmap.rows();
            let ch_x_offset = glyph.bitmap_left();
            let ch_y_offset = font_size as i32 - glyph.bitmap_top();
            let ch_x_advance = (glyph.advance().x >> 6) as i32;
            let buffer = bitmap.buffer();
            let ch_data = Vec::from(buffer);

            chars_info.insert(ch, BitmapChar {
                x_offset: ch_x_offset,
                y_offset: ch_y_offset,
                x_advance: ch_x_advance,
                width: ch_width,
                height: ch_height,
                // We'll need to fix that fields later:
                tex: [0.0, 0.0],
                tex_width: 0.0,
                tex_height: 0.0,
                data: Some(ch_data),
            });

            sum_image_width += ch_width;
            max_ch_width = max(max_ch_width, ch_width);
            ch_box_height = max(ch_box_height, ch_height);
        }

        // In second pass we map character boxes with varying width onto the
        // fixed quad texture image and build the final texture image.
        //
        // We start with optimist (square) assumption about texture dimensions
        // and adjust the image's height and size while filling the rows.
        //
        // TODO(Kagami): We may try some cool CS algorithm to fit char boxes
        // into the quad texture space with the best level of compression.
        // Though current level of inefficiency is good enough.

        let ideal_image_size = sum_image_width * ch_box_height;
        let ideal_image_width = (ideal_image_size as f32).sqrt() as i32;
        let image_width = max(max_ch_width, ideal_image_width);
        let image_width = (image_width + 3) & !3; // aligning to 4
        let assumed_size = ideal_image_size as f32 * 1.5;
        let assumed_ch_in_row = image_width as f32 / max_ch_width as f32;
        let mut image = Vec::with_capacity(assumed_size as usize);
        let mut chars_row = Vec::with_capacity(assumed_ch_in_row as usize);
        let mut cursor_x = 0;
        let mut image_height = 0;

        let dump_row = |image: &mut Vec<u8>, chars_row: &Vec<(i32, i32, Vec<u8>)>| {
            // Copy character data into the image row by row:
            //
            //       image_width
            // +-------+---------+---+
            // |   x   |    x    |   |
            // |       |         |   |
            // |   x   |    x    |   | ch_box_height
            // |   x   |    x    |   |
            // |   x   |    x    |   |
            // |   x   |   x     |   |
            // |       |  x      |   |
            // +-------+---------+---+
            //                     ^--- image_width - width_ch_i - width_ch_j
            for i in 0..ch_box_height {
                let mut x = 0;
                for &(width, height, ref data) in chars_row {
                   if i >= height {
                       image.extend(repeat(0).take(width as usize));
                   } else {
                       let skip = i * width;
                       debug_assert!(data.len() >= (skip + width) as usize);
                       let line = data.iter().skip(skip as usize).take(width as usize);
                       image.extend(line.cloned());
                   };
                   x += width;
                }
                let cols_to_fill = image_width - x;
                image.extend(repeat(0).take(cols_to_fill as usize));
            }
        };

        debug!("Placing chars onto a plane");

        // Hashmap doesn't preserve the order but we don't need it anyway.
        for (_, ch_info) in chars_info.iter_mut() {
            if cursor_x + ch_info.width > image_width {
                dump_row(&mut image, &chars_row);
                chars_row.clear();
                cursor_x = 0;
                image_height += ch_box_height;
            }
            let ch_data = ch_info.data.take().unwrap();
            chars_row.push((ch_info.width, ch_info.height, ch_data));
            ch_info.tex = [cursor_x as f32, image_height as f32];
            cursor_x += ch_info.width;
        }
        dump_row(&mut image, &chars_row);
        image_height += ch_box_height;

        // Finally, we just precalculate some fields to make it easier to use
        // our font.

        for (_, ch_info) in chars_info.iter_mut() {
            ch_info.tex[0] /= image_width as f32;
            ch_info.tex[1] /= image_height as f32;
            ch_info.tex_width = ch_info.width as f32 / image_width as f32;
            ch_info.tex_height = ch_info.height as f32 / image_height as f32;
        }

        info!("Image width: {}, image height: {}, total size: {}",
            image_width, image_height, image.len());

        Ok(BitmapFont {
            width: image_width as u16,
            height: image_height as u16,
            chars: chars_info,
            image: image,
        })
    }

    pub fn get_width(&self) -> u16 {
        self.width
    }

    pub fn get_height(&self) -> u16 {
        self.height
    }

    /// Return 8-bit texture raw data (grayscale).
    pub fn get_image(&self) -> &[u8] {
        &self.image
    }

    pub fn find_char(&self, ch: char) -> Option<&BitmapChar> {
        self.chars.get(&ch)
    }
}
<|endoftext|>


<commit_before>//! The `game` module contains traits and structs to actually run your game mainloop
//! and handle top-level state.

use context::Context;
use GameResult;
use conf;
use timer;

use std::time::Duration;

use super::event as gevent;

use sdl2::event::Event::*;
use sdl2::event;
use sdl2::mouse;
use sdl2::keyboard;

/// A trait for defining a game state.
/// Implement `load()`, `update()` and `draw()` callbacks on this trait
/// and create a `Game` object using your gamestate type.
/// You may also implement the `*_event` callbacks if you wish to handle
/// those events.
///
/// The default event handlers do nothing, apart from `key_down_event()`,
/// which *should* by default exit the game if escape is pressed.
/// (Once we work around some event bugs in rust-sdl2.)
pub trait GameState: EventHandler {
    // Tricksy trait and lifetime magic happens in load()'s
    // signature.
    // It doesn't look complicated but is easy to get wrong.
    // Much thanks to aatch on #rust-beginners for helping make this work.

    /// Called to initially create your `GameState` object
    /// after all hardware initialization has been done.
    /// It is handed a `Context` to load resources from,
    /// and the `Conf` object that has either been loaded
    /// from your `resources/conf.toml` file or the default
    /// that has been provided to `Game::new()` if no conf
    /// file exists.
    fn load(ctx: &mut Context) -> GameResult<Self> where Self: Sized;
}

pub trait EventHandler {
    /// Called upon each physics update to the game.
    /// This should be where the game's logic takes place.
    fn update(&mut self, ctx: &mut Context, dt: Duration) -> GameResult<()>;

    /// Called to do the drawing of your game.
    /// You probably want to start this with
    /// `graphics::clear()` and end it with
    /// `graphics::present()` and `timer::sleep_until_next_frame()`
    fn draw(&mut self, ctx: &mut Context) -> GameResult<()>;

    // You don't have to override these if you don't want to; the defaults
    // do nothing.
    // It might be nice to be able to have custom event types and a map or
    // such of handlers?  Hmm, maybe later.
    fn mouse_button_down_event(&mut self, _button: mouse::MouseButton, _x: i32, _y: i32) {}

    fn mouse_button_up_event(&mut self, _button: mouse::MouseButton, _x: i32, _y: i32) {}

    fn mouse_motion_event(&mut self,
                          _state: mouse::MouseState,
                          _x: i32,
                          _y: i32,
                          _xrel: i32,
                          _yrel: i32) {
    }

    fn mouse_wheel_event(&mut self, _x: i32, _y: i32) {}

    fn key_down_event(&mut self,
                      _keycode: Option<gevent::Keycode>,
                      _keymod: gevent::Mod,
                      _repeat: bool) {
    }

    fn key_up_event(&mut self,
                    _keycode: Option<gevent::Keycode>,
                    _keymod: gevent::Mod,
                    _repeat: bool) {
    }

    fn focus_event(&mut self, _gained: bool) {}

    /// Called upon a quit event.  If it returns true,
    /// the game does not exit.
    fn quit_event(&mut self) -> bool {
        println!("Quitting game");
        false
    }
}

/// The `Game` struct takes an object you define that
/// implements the `GameState` trait
/// and does the actual work of running a gameloop,
/// passing events to your handlers, and all that stuff.
#[derive(Debug)]
pub struct Game<'a, S> {
    state: S,
    context: Context<'a>,
}

impl<'a, S> Game<'a, S> {
    pub fn from_state(ctx: Context<'a>, state: S) -> Game<S> {
        Game {
            state: state,
            context: ctx,
        }
    }

    /// Replaces the gamestate with the given one without
    /// having to re-initialize the hardware context.
    pub fn replace_state(&mut self, state: S) {
        self.state = state;
    }

    /// Calls the given function to create a new gamestate, and replaces
    /// the current one with it.
    pub fn replace_state_with<F>(&mut self, f: &F) -> GameResult<()>
        where F: Fn(&mut Context) -> GameResult<S>
    {
        let newstate = f(&mut self.context)?;
        self.state = newstate;
        Ok(())
    }
}

impl<'a, S: GameState + 'static> Game<'a, S> {
    /// Creates a new `Game` with the given  default config
    /// (which will be used if there is no config file).
    /// It will initialize a hardware context and call the `load()` method of
    /// the given `GameState` type to create a new `GameState`.
    ///
    /// The `id` field is a unique identifier for your game that will
    /// be used to create a save directory to write files to.
    pub fn new(id: &str, default_config: conf::Conf) -> GameResult<Game<'a, S>> {
        let mut context = Context::load_from_conf(id, default_config)?;

        let init_state = S::load(&mut context)?;

        Ok(Game {
            state: init_state,
            context: context,
        })
    }

    /// Re-creates a fresh `GameState` using the existing one's `::load()` method.
    pub fn reload_state(&mut self) -> GameResult<()> {
        let newstate = S::load(&mut self.context)?;
        self.state = newstate;
        Ok(())
    }
}

impl<'a, S: EventHandler + 'static> Game<'a, S> {
    /// Runs the game's mainloop.
    /// Continues until a `Quit` event is created, for instance
    /// via `Context::quit()`
    pub fn run(&mut self) -> GameResult<()> {
        let ctx = &mut self.context;
        let mut event_pump = try!(ctx.sdl_context.event_pump());

        let mut continuing = true;
        while continuing {
            ctx.timer_context.tick();

            for event in event_pump.poll_iter() {
                match event {
                    Quit { .. } => {
                        continuing = self.state.quit_event();
                        // println!("Quit event: {:?}", t);
                    }
                    // TODO: We need a good way to have
                    // a default like this, while still allowing
                    // it to be overridden.
                    // Bah, just put it in the GameState trait
                    // as the default function.
                    // But it doesn't have access to the context
                    // to call quit!  Bah.
                    KeyDown { keycode, keymod, repeat, .. } => {
                        match keycode {
                            Some(keyboard::Keycode::Escape) => {
                                try!(ctx.quit());
                            }
                            _ => self.state.key_down_event(keycode, keymod, repeat),
                        }
                    }
                    KeyUp { keycode, keymod, repeat, .. } => {
                        self.state.key_up_event(keycode, keymod, repeat)
                    }
                    MouseButtonDown { mouse_btn, x, y, .. } => {
                        self.state.mouse_button_down_event(mouse_btn, x, y)
                    }
                    MouseButtonUp { mouse_btn, x, y, .. } => {
                        self.state.mouse_button_up_event(mouse_btn, x, y)
                    }
                    MouseMotion { mousestate, x, y, xrel, yrel, .. } => {
                        self.state.mouse_motion_event(mousestate, x, y, xrel, yrel)
                    }
                    MouseWheel { x, y, .. } => self.state.mouse_wheel_event(x, y),
                    Window { win_event: event::WindowEvent::FocusGained, .. } => {
                        self.state.focus_event(true)
                    }
                    Window { win_event: event::WindowEvent::FocusLost, .. } => {
                        self.state.focus_event(false)
                    }
                    _ => {}
                }
            }

            // TODO: Currently, logic and display are locked
            // together to the same framerate; we should probably
            // change that.
            // How does Love2D do it though?
            // Love2D does it the simple and dumb way and just does
            // sleep(0.001) after each draw; see
            // http://www.love2d.org/wiki/love.run
            let dt = timer::get_delta(ctx);
            try!(self.state.update(ctx, dt));
            try!(self.state.draw(ctx));
        }

        Ok(())
    }
}
<commit_msg>Experimental fixed-framerate handling.<commit_after>//! The `game` module contains traits and structs to actually run your game mainloop
//! and handle top-level state.

use context::Context;
use GameResult;
use conf;
use timer;

use std::time::Duration;

use super::event as gevent;

use sdl2::event::Event::*;
use sdl2::event;
use sdl2::mouse;
use sdl2::keyboard;

/// A trait for defining a game state.
/// Implement `load()`, `update()` and `draw()` callbacks on this trait
/// and create a `Game` object using your gamestate type.
/// You may also implement the `*_event` callbacks if you wish to handle
/// those events.
///
/// The default event handlers do nothing, apart from `key_down_event()`,
/// which *should* by default exit the game if escape is pressed.
/// (Once we work around some event bugs in rust-sdl2.)
pub trait GameState: EventHandler {
    // Tricksy trait and lifetime magic happens in load()'s
    // signature.
    // It doesn't look complicated but is easy to get wrong.
    // Much thanks to aatch on #rust-beginners for helping make this work.

    /// Called to initially create your `GameState` object
    /// after all hardware initialization has been done.
    /// It is handed a `Context` to load resources from,
    /// and the `Conf` object that has either been loaded
    /// from your `resources/conf.toml` file or the default
    /// that has been provided to `Game::new()` if no conf
    /// file exists.
    fn load(ctx: &mut Context) -> GameResult<Self> where Self: Sized;
}

pub trait EventHandler {
    /// Called upon each physics update to the game.
    /// This should be where the game's logic takes place.
    fn update(&mut self, ctx: &mut Context, dt: Duration) -> GameResult<()>;

    /// Called to do the drawing of your game.
    /// You probably want to start this with
    /// `graphics::clear()` and end it with
    /// `graphics::present()` and `timer::sleep_until_next_frame()`
    fn draw(&mut self, ctx: &mut Context) -> GameResult<()>;

    // You don't have to override these if you don't want to; the defaults
    // do nothing.
    // It might be nice to be able to have custom event types and a map or
    // such of handlers?  Hmm, maybe later.
    fn mouse_button_down_event(&mut self, _button: mouse::MouseButton, _x: i32, _y: i32) {}

    fn mouse_button_up_event(&mut self, _button: mouse::MouseButton, _x: i32, _y: i32) {}

    fn mouse_motion_event(&mut self,
                          _state: mouse::MouseState,
                          _x: i32,
                          _y: i32,
                          _xrel: i32,
                          _yrel: i32) {
    }

    fn mouse_wheel_event(&mut self, _x: i32, _y: i32) {}

    fn key_down_event(&mut self,
                      _keycode: Option<gevent::Keycode>,
                      _keymod: gevent::Mod,
                      _repeat: bool) {
    }

    fn key_up_event(&mut self,
                    _keycode: Option<gevent::Keycode>,
                    _keymod: gevent::Mod,
                    _repeat: bool) {
    }

    fn focus_event(&mut self, _gained: bool) {}

    /// Called upon a quit event.  If it returns true,
    /// the game does not exit.
    fn quit_event(&mut self) -> bool {
        println!("Quitting game");
        false
    }
}

/// The `Game` struct takes an object you define that
/// implements the `GameState` trait
/// and does the actual work of running a gameloop,
/// passing events to your handlers, and all that stuff.
#[derive(Debug)]
pub struct Game<'a, S> {
    state: S,
    context: Context<'a>,
}

impl<'a, S> Game<'a, S> {
    pub fn from_state(ctx: Context<'a>, state: S) -> Game<S> {
        Game {
            state: state,
            context: ctx,
        }
    }

    /// Replaces the gamestate with the given one without
    /// having to re-initialize the hardware context.
    pub fn replace_state(&mut self, state: S) {
        self.state = state;
    }

    /// Calls the given function to create a new gamestate, and replaces
    /// the current one with it.
    pub fn replace_state_with<F>(&mut self, f: &F) -> GameResult<()>
        where F: Fn(&mut Context) -> GameResult<S>
    {
        let newstate = f(&mut self.context)?;
        self.state = newstate;
        Ok(())
    }
}

impl<'a, S: GameState + 'static> Game<'a, S> {
    /// Creates a new `Game` with the given  default config
    /// (which will be used if there is no config file).
    /// It will initialize a hardware context and call the `load()` method of
    /// the given `GameState` type to create a new `GameState`.
    ///
    /// The `id` field is a unique identifier for your game that will
    /// be used to create a save directory to write files to.
    pub fn new(id: &str, default_config: conf::Conf) -> GameResult<Game<'a, S>> {
        let mut context = Context::load_from_conf(id, default_config)?;

        let init_state = S::load(&mut context)?;

        Ok(Game {
            state: init_state,
            context: context,
        })
    }

    /// Re-creates a fresh `GameState` using the existing one's `::load()` method.
    pub fn reload_state(&mut self) -> GameResult<()> {
        let newstate = S::load(&mut self.context)?;
        self.state = newstate;
        Ok(())
    }
}

impl<'a, S: EventHandler + 'static> Game<'a, S> {
    /// Runs the game's mainloop.
    /// Continues until a `Quit` event is created, for instance
    /// via `Context::quit()`
    pub fn run(&mut self) -> GameResult<()> {
        let ctx = &mut self.context;
        let mut event_pump = ctx.sdl_context.event_pump()?;

        let mut continuing = true;
        let mut residual_update_dt = Duration::new(0, 0);
        let mut residual_draw_dt = Duration::new(0, 0);
        while continuing {
            ctx.timer_context.tick();

            for event in event_pump.poll_iter() {
                match event {
                    Quit { .. } => {
                        continuing = self.state.quit_event();
                        // println!("Quit event: {:?}", t);
                    }
                    // TODO: We need a good way to have
                    // a default like this, while still allowing
                    // it to be overridden.
                    // Bah, just put it in the GameState trait
                    // as the default function.
                    // But it doesn't have access to the context
                    // to call quit!  Bah.
                    KeyDown { keycode, keymod, repeat, .. } => {
                        match keycode {
                            Some(keyboard::Keycode::Escape) => {
                                ctx.quit()?;
                            }
                            _ => self.state.key_down_event(keycode, keymod, repeat),
                        }
                    }
                    KeyUp { keycode, keymod, repeat, .. } => {
                        self.state.key_up_event(keycode, keymod, repeat)
                    }
                    MouseButtonDown { mouse_btn, x, y, .. } => {
                        self.state.mouse_button_down_event(mouse_btn, x, y)
                    }
                    MouseButtonUp { mouse_btn, x, y, .. } => {
                        self.state.mouse_button_up_event(mouse_btn, x, y)
                    }
                    MouseMotion { mousestate, x, y, xrel, yrel, .. } => {
                        self.state.mouse_motion_event(mousestate, x, y, xrel, yrel)
                    }
                    MouseWheel { x, y, .. } => self.state.mouse_wheel_event(x, y),
                    Window { win_event: event::WindowEvent::FocusGained, .. } => {
                        self.state.focus_event(true)
                    }
                    Window { win_event: event::WindowEvent::FocusLost, .. } => {
                        self.state.focus_event(false)
                    }
                    _ => {}
                }
            }

            let update_dt = Duration::from_millis(10);
            let draw_dt = Duration::new(0, 16_666_666);
            let dt = timer::get_delta(ctx);
            {
                let mut current_dt = dt + residual_update_dt;
                while current_dt > update_dt {
                    self.state.update(ctx, update_dt)?;
                    current_dt -= update_dt;
                }
                residual_update_dt = current_dt;
            }

            {
                let mut current_dt = dt + residual_draw_dt;
                while current_dt > draw_dt {
                    self.state.draw(ctx)?;
                    current_dt -= draw_dt;
                }
                residual_draw_dt = draw_dt;
            }
            timer::sleep(Duration::new(0, 0));
        }

        Ok(())
    }
}
<|endoftext|>


<commit_before>//! Game loop.

// External crates.
use graphics::*;
use gl;
use gl::types::GLint;
use time;

// Local crate.
use Gl = gl_back_end::Gl;
use GlData = gl_back_end::GlData;
use GameWindow = game_window::GameWindow;
use AssetStore;
use keyboard;
use event;
use mouse;

/// Implemented by game applications.
pub trait Game {
    /// Render graphics.
    ///
    /// `context` is a Rust-Graphics context.
    /// `gl` is the Piston OpenGL back-end for Rust-Graphics.
    fn render(&self, _ext_dt: f64, _context: &Context, _gl: &mut Gl) {}

    /// Update the physical state of the game.
    ///
    /// `dt` is the delta time from last update in seconds.
    fn update(&mut self, _dt: f64, _asset_store: &mut AssetStore) {}

    /// Perform tasks for loading before showing anything.
    fn load(&mut self, _asset_store: &mut AssetStore) {}

    /// User pressed a key.
    ///
    /// This can be overridden to handle key pressed events.
    fn key_press(
        &mut self,
        _key: keyboard::Key,
        _asset_store: &mut AssetStore
    ) {}

    /// User released a key.
    ///
    /// This can be overridden to handle key released events.
    fn key_release(
        &mut self,
        _key: keyboard::Key,
        _asset_store: &mut AssetStore
    ) {}

    /// Pressed a mouse button.
    fn mouse_press(
        &mut self,
        _button: mouse::Button,
        _asset_store: &mut AssetStore
    ) {}

    /// Released a mouse button.
    fn mouse_release(
        &mut self,
        _button: mouse::Button,
        _asset_store: &mut AssetStore
    ) {}

    /// Moved mouse cursor.
    fn mouse_move(
        &mut self,
        _x: f64,
        _y: f64,
        _asset_store: &mut AssetStore
    ) {}

    /// Moved mouse relative, not bounded by cursor.
    fn mouse_relative_move(
        &mut self,
        _dx: f64,
        _dy: f64,
        _asset_store: &mut AssetStore
    ) {}

    /// Sets up viewport.
    ///
    /// A viewport is the region of the window where graphics is rendered.
    #[inline(always)]
    fn viewport<W: GameWindow>(&self, game_window: &W) {
        let (w, h) = game_window.get_size();
        gl::Viewport(0, 0, w as GLint, h as GLint);
    }

    /// Whether the window should be closed.
    ///
    /// When this is `true` the application shuts down.
    /// This can be overridden to emulate a user closing the window.
    /// One can also override this method to prevent window from closing.
    fn should_close<W: GameWindow>(&self, game_window: &W) -> bool {
        game_window.should_close()
    }

    /// Swaps the front buffer with the back buffer.
    ///
    /// When called, This shows the next frame.
    /// The graphics is rendered to the back buffer.
    /// The front buffer is displayed on the screen.
    fn swap_buffers<W: GameWindow>(&self, game_window: &W) {
        game_window.swap_buffers()
    }

    /// Handles events using current game window settings.
    ///
    /// This can be overriden to do custom event handling.
    fn handle_events<W: GameWindow>(
        &mut self,
        game_window: &mut W,
        asset_store: &mut AssetStore
    ) {
        loop {
            match game_window.poll_event() {
                event::KeyPressed(keycode) => {
                    self.key_press(keycode, asset_store)
                },
                event::KeyReleased(keycode) => {
                    self.key_release(keycode, asset_store)
                },
                event::MouseButtonPressed(mouse_button) => {
                    self.mouse_press(mouse_button, asset_store)
                },
                event::MouseButtonReleased(mouse_button) => {
                    self.mouse_release(mouse_button, asset_store)
                },
                event::MouseMoved(x, y, relative_move) => {
                    self.mouse_move(x, y, asset_store);
                    match relative_move {
                        Some((dx, dy)) =>
                            self.mouse_relative_move(dx, dy, asset_store),
                        None => {},
                    }
                },
                event::NoEvent => {
                    break
                },
            }
        }
    }

    /// Executes a game loop.
    ///
    /// The loop continues until `should_close` returns true.
    fn run<W: GameWindow>(
        &mut self,
        game_window: &mut W,
        asset_store: &mut AssetStore
    ) {
        use graphics::{Clear, AddColor};

        self.load(asset_store);
        let mut gl_data = GlData::new();
        let context = Context::new();
        let bg = game_window.get_settings().background_color;
        let bg = context.rgba(bg[0], bg[1], bg[2], bg[3]);
        let updates_per_second: f64 = 120.0;
        let max_updates_per_frame: u32 = 240;
        let max_frames_per_second: f64 = 60.0;

        let billion: u64 = 1_000_000_000;
        let dt: f64 = 1.0 / updates_per_second;
        let update_time_in_ns: u64 = billion / updates_per_second as u64;

        let start = time::precise_time_ns();
        let min_ns_per_frame = (billion as f64 / max_frames_per_second) as u64;
        let mut next_render = start;
        let mut last_update = start;
        // Set updated to 'true' to trigger rendering before first update.
        let mut updated = true;
        while !self.should_close(game_window) {
            let now = time::precise_time_ns();

            if updated && now >= next_render {
                // Render.
                let (w, h) = game_window.get_size();
                if w != 0 && h != 0 {
                    self.viewport(game_window);
                    let mut gl = Gl::new(&mut gl_data, asset_store);
                    bg.clear(&mut gl);
                    // Extrapolate time forward to allow smooth motion.
                    // 'now' is always bigger than 'last_update'.
                    let ext_dt = (now - last_update) as f64 / billion as f64;
                    self.render(
                        ext_dt,
                        &context
                        .trans(-1.0, 1.0)
                        .scale(2.0 / w as f64, -2.0 / h as f64)
                        .store_view(),
                        &mut gl
                    );
                    self.swap_buffers(game_window);
                }

                // Set moment for next rendering.
                next_render = now + min_ns_per_frame;
            }

            // Perform updates by fixed time step until it catches up.
            updated = false;
            for _ in range(0, max_updates_per_frame) {
                // Break when catching up to next frame.
                if next_render <= last_update { break; }

                // Handle user input.
                // This is handled every update to make it more responsive.
                self.handle_events(game_window, asset_store);

                // Update application state.
                self.update(dt, asset_store);
                last_update += update_time_in_ns;
                updated = true;
            }
        }
    }
}

<commit_msg>Rework game loop logic to fix speed problems<commit_after>//! Game loop.

// External crates.
use graphics::*;
use gl;
use gl::types::GLint;
use time;

// Local crate.
use Gl = gl_back_end::Gl;
use GlData = gl_back_end::GlData;
use GameWindow = game_window::GameWindow;
use AssetStore;
use keyboard;
use event;
use mouse;

/// Implemented by game applications.
pub trait Game {
    /// Render graphics.
    ///
    /// `context` is a Rust-Graphics context.
    /// `gl` is the Piston OpenGL back-end for Rust-Graphics.
    fn render(&self, _ext_dt: f64, _context: &Context, _gl: &mut Gl) {}

    /// Update the physical state of the game.
    ///
    /// `dt` is the delta time from last update in seconds.
    fn update(&mut self, _dt: f64, _asset_store: &mut AssetStore) {}

    /// Perform tasks for loading before showing anything.
    fn load(&mut self, _asset_store: &mut AssetStore) {}

    /// User pressed a key.
    ///
    /// This can be overridden to handle key pressed events.
    fn key_press(
        &mut self,
        _key: keyboard::Key,
        _asset_store: &mut AssetStore
    ) {}

    /// User released a key.
    ///
    /// This can be overridden to handle key released events.
    fn key_release(
        &mut self,
        _key: keyboard::Key,
        _asset_store: &mut AssetStore
    ) {}

    /// Pressed a mouse button.
    fn mouse_press(
        &mut self,
        _button: mouse::Button,
        _asset_store: &mut AssetStore
    ) {}

    /// Released a mouse button.
    fn mouse_release(
        &mut self,
        _button: mouse::Button,
        _asset_store: &mut AssetStore
    ) {}

    /// Moved mouse cursor.
    fn mouse_move(
        &mut self,
        _x: f64,
        _y: f64,
        _asset_store: &mut AssetStore
    ) {}

    /// Moved mouse relative, not bounded by cursor.
    fn mouse_relative_move(
        &mut self,
        _dx: f64,
        _dy: f64,
        _asset_store: &mut AssetStore
    ) {}

    /// Sets up viewport.
    ///
    /// A viewport is the region of the window where graphics is rendered.
    #[inline(always)]
    fn viewport<W: GameWindow>(&self, game_window: &W) {
        let (w, h) = game_window.get_size();
        gl::Viewport(0, 0, w as GLint, h as GLint);
    }

    /// Whether the window should be closed.
    ///
    /// When this is `true` the application shuts down.
    /// This can be overridden to emulate a user closing the window.
    /// One can also override this method to prevent window from closing.
    fn should_close<W: GameWindow>(&self, game_window: &W) -> bool {
        game_window.should_close()
    }

    /// Swaps the front buffer with the back buffer.
    ///
    /// When called, This shows the next frame.
    /// The graphics is rendered to the back buffer.
    /// The front buffer is displayed on the screen.
    fn swap_buffers<W: GameWindow>(&self, game_window: &W) {
        game_window.swap_buffers()
    }

    /// Handles events using current game window settings.
    ///
    /// This can be overriden to do custom event handling.
    fn handle_events<W: GameWindow>(
        &mut self,
        game_window: &mut W,
        asset_store: &mut AssetStore
    ) {
        loop {
            match game_window.poll_event() {
                event::KeyPressed(keycode) => {
                    self.key_press(keycode, asset_store)
                },
                event::KeyReleased(keycode) => {
                    self.key_release(keycode, asset_store)
                },
                event::MouseButtonPressed(mouse_button) => {
                    self.mouse_press(mouse_button, asset_store)
                },
                event::MouseButtonReleased(mouse_button) => {
                    self.mouse_release(mouse_button, asset_store)
                },
                event::MouseMoved(x, y, relative_move) => {
                    self.mouse_move(x, y, asset_store);
                    match relative_move {
                        Some((dx, dy)) =>
                            self.mouse_relative_move(dx, dy, asset_store),
                        None => {},
                    }
                },
                event::NoEvent => {
                    break
                },
            }
        }
    }

    /// Executes a game loop.
    ///
    /// The loop continues until `should_close` returns true.
    fn run<W: GameWindow>(
        &mut self,
        game_window: &mut W,
        asset_store: &mut AssetStore
    ) {
        use graphics::{Clear, AddColor};

        self.load(asset_store);
        let mut gl_data = GlData::new();
        let context = Context::new();
        let bg = game_window.get_settings().background_color;
        let bg = context.rgba(bg[0], bg[1], bg[2], bg[3]);
        let updates_per_second: f64 = 120.0;
        let max_frames_per_second: f64 = 60.0;

        let billion: u64 = 1_000_000_000;
        let dt: f64 = 1.0 / updates_per_second;
        let update_time_in_ns: u64 = billion / updates_per_second as u64;

        let start = time::precise_time_ns();
        let min_ns_per_frame = (billion as f64 / max_frames_per_second) as u64;
        let mut next_render = start;
        let mut last_update = start;
        // Set updated to 'true' to trigger rendering before first update.
        while !self.should_close(game_window) {
            let now = time::precise_time_ns();

            if now >= next_render {
                // Render.
                let (w, h) = game_window.get_size();
                if w != 0 && h != 0 {
                    self.viewport(game_window);
                    let mut gl = Gl::new(&mut gl_data, asset_store);
                    bg.clear(&mut gl);
                    // Extrapolate time forward to allow smooth motion.
                    // 'now' is always bigger than 'last_update'.
                    let ext_dt = (now - last_update) as f64 / billion as f64;
                    self.render(
                        ext_dt,
                        &context
                        .trans(-1.0, 1.0)
                        .scale(2.0 / w as f64, -2.0 / h as f64)
                        .store_view(),
                        &mut gl
                    );
                    self.swap_buffers(game_window);
                }

                // Set moment for next rendering.
                next_render = now + min_ns_per_frame;
            }

            // Perform updates by fixed time step until it catches up.
            while last_update < next_render &&
                  time::precise_time_ns() < next_render {
                // Handle user input.
                // This is handled every update to make it more responsive.
                self.handle_events(game_window, asset_store);

                // Update application state.
                self.update(dt, asset_store);
                last_update += update_time_in_ns;
            }
        }
    }
}

<|endoftext|>


<commit_before>//! Provides macros for convenient implementation of NN operations.

#[macro_export]
macro_rules! impl_ops_sigmoid_for {
    ($t:ident, $b:ty) => (
        fn sigmoid(
            &self,
            x: &mut ::co::tensor::SharedTensor<$t>,
            result: &mut ::co::tensor::SharedTensor<$t>
        ) -> Result<(), ::co::error::Error> {
            match x.add_device(self.device()) { _ => try!(x.sync(self.device())) }
            match result.add_device(self.device()) { _ => () }
            let src_desc = try!(x.get_cudnn_desc());
            let src_data = try!(try!(x.get(self.device()).ok_or(::co::plugin::Error::MissingMemoryForDevice("Unable to resolve memory for `x`")))
            .as_cuda().ok_or(::co::plugin::Error::MissingMemoryForDevice("Unable to receive native memory for `x`.")))
            .id_c();
            let dest_desc = try!(result.get_cudnn_desc());
            let dest_data = try!(try!(result.get_mut(self.device()).ok_or(::co::plugin::Error::MissingMemoryForDevice("Unable to resolve memory for `result`")))
            .as_cuda().ok_or(::co::plugin::Error::MissingMemoryForDevice("Unable to receive native memory for `result`.")))
            .id_c();

            Ok(try!(match self.binary().cudnn().sigmoid_forward(
                &src_desc, unsafe { ::std::mem::transmute::<u64, *const ::libc::c_void>(src_data) },
                &dest_desc, unsafe { ::std::mem::transmute::<u64, *mut ::libc::c_void>(dest_data) },
                ::cudnn::ScalParams::<$t>::default()
            ) {
                Ok(_) => Ok(()),
                Err(err) => {
                    println!("{:?}", err);
                    Err(::co::plugin::Error::Operation("Unable to execute CUDA cuDNN Activation Sigmoid Forward."))
                }
            }))
        }

        fn sigmoid_plain(
            &self,
            x: &::co::tensor::SharedTensor<$t>,
            result: &mut ::co::tensor::SharedTensor<$t>
        ) -> Result<(), ::co::error::Error> {
            let src_desc = try!(x.get_cudnn_desc());
            let src_data = try!(try!(x.get(self.device()).ok_or(::co::plugin::Error::MissingMemoryForDevice("Unable to resolve memory for `x`")))
            .as_cuda().ok_or(::co::plugin::Error::MissingMemoryForDevice("Unable to receive native memory for `x`.")))
            .id_c();
            let dest_desc = try!(result.get_cudnn_desc());
            let dest_data = try!(try!(result.get_mut(self.device()).ok_or(::co::plugin::Error::MissingMemoryForDevice("Unable to resolve memory for `result`")))
            .as_cuda().ok_or(::co::plugin::Error::MissingMemoryForDevice("Unable to receive native memory for `result`.")))
            .id_c();

            Ok(try!(match self.binary().cudnn().sigmoid_forward(
                &src_desc, unsafe { ::std::mem::transmute::<u64, *const ::libc::c_void>(src_data) },
                &dest_desc, unsafe { ::std::mem::transmute::<u64, *mut ::libc::c_void>(dest_data) },
                ::cudnn::ScalParams::<$t>::default()
            ) {
                Ok(_) => Ok(()),
                Err(err) => {
                    println!("{:?}", err);
                    Err(::co::plugin::Error::Operation("Unable to execute CUDA cuDNN Activation Sigmoid Forward."))
                }
            }))
        }

        fn sigmoid_diff(
            &self,
            x: &mut ::co::tensor::SharedTensor<$t>,
            result: &mut ::co::tensor::SharedTensor<$t>
        ) -> Result<(), ::co::error::Error> {
            match x.add_device(self.device()) { _ => try!(x.sync(self.device())) }
            match result.add_device(self.device()) { _ => () }
            let src_desc = try!(x.get_cudnn_desc());
            let src_data = try!(try!(x.get(self.device()).ok_or(::co::plugin::Error::MissingMemoryForDevice("Unable to resolve memory for `x`")))
            .as_cuda().ok_or(::co::plugin::Error::MissingMemoryForDevice("Unable to receive native memory for `x`.")))
            .id_c();
            let dest_desc = try!(result.get_cudnn_desc());
            let dest_data = try!(try!(result.get_mut(self.device()).ok_or(::co::plugin::Error::MissingMemoryForDevice("Unable to resolve memory for `result`")))
            .as_cuda().ok_or(::co::plugin::Error::MissingMemoryForDevice("Unable to receive native memory for `result`.")))
            .id_c();

            Ok(try!(match self.binary().cudnn().sigmoid_forward(
                &src_desc, unsafe { ::std::mem::transmute::<u64, *const ::libc::c_void>(src_data) },
                &dest_desc, unsafe { ::std::mem::transmute::<u64, *mut ::libc::c_void>(dest_data) },
                ::cudnn::ScalParams::<$t>::default()
            ) {
                Ok(_) => Ok(()),
                Err(err) => {
                    println!("{:?}", err);
                    Err(::co::plugin::Error::Operation("Unable to execute CUDA cuDNN Activation Sigmoid Forward."))
                }
            }))
        }

        fn sigmoid_diff_plain(
            &self,
            x: &::co::tensor::SharedTensor<$t>,
            result: &mut ::co::tensor::SharedTensor<$t>
        ) -> Result<(), ::co::error::Error> {
            let src_desc = try!(x.get_cudnn_desc());
            let src_data = try!(try!(x.get(self.device()).ok_or(::co::plugin::Error::MissingMemoryForDevice("Unable to resolve memory for `x`")))
            .as_cuda().ok_or(::co::plugin::Error::MissingMemoryForDevice("Unable to receive native memory for `x`.")))
            .id_c();
            let dest_desc = try!(result.get_cudnn_desc());
            let dest_data = try!(try!(result.get_mut(self.device()).ok_or(::co::plugin::Error::MissingMemoryForDevice("Unable to resolve memory for `result`")))
            .as_cuda().ok_or(::co::plugin::Error::MissingMemoryForDevice("Unable to receive native memory for `result`.")))
            .id_c();

            Ok(try!(match self.binary().cudnn().sigmoid_forward(
                &src_desc, unsafe { ::std::mem::transmute::<u64, *const ::libc::c_void>(src_data) },
                &dest_desc, unsafe { ::std::mem::transmute::<u64, *mut ::libc::c_void>(dest_data) },
                ::cudnn::ScalParams::<$t>::default()
            ) {
                Ok(_) => Ok(()),
                Err(err) => {
                    println!("{:?}", err);
                    Err(::co::plugin::Error::Operation("Unable to execute CUDA cuDNN Activation Sigmoid Forward."))
                }
            }))
        }
    )
}
<commit_msg>fix/scale_params: fix ScalParams default to work on stable<commit_after>//! Provides macros for convenient implementation of NN operations.

#[macro_export]
macro_rules! impl_ops_sigmoid_for {
    ($t:ident, $b:ty) => (
        fn sigmoid(
            &self,
            x: &mut ::co::tensor::SharedTensor<$t>,
            result: &mut ::co::tensor::SharedTensor<$t>
        ) -> Result<(), ::co::error::Error> {
            match x.add_device(self.device()) { _ => try!(x.sync(self.device())) }
            match result.add_device(self.device()) { _ => () }
            let src_desc = try!(x.get_cudnn_desc());
            let src_data = try!(try!(x.get(self.device()).ok_or(::co::plugin::Error::MissingMemoryForDevice("Unable to resolve memory for `x`")))
            .as_cuda().ok_or(::co::plugin::Error::MissingMemoryForDevice("Unable to receive native memory for `x`.")))
            .id_c();
            let dest_desc = try!(result.get_cudnn_desc());
            let dest_data = try!(try!(result.get_mut(self.device()).ok_or(::co::plugin::Error::MissingMemoryForDevice("Unable to resolve memory for `result`")))
            .as_cuda().ok_or(::co::plugin::Error::MissingMemoryForDevice("Unable to receive native memory for `result`.")))
            .id_c();
            let scal_params: ::cudnn::utils::ScalParams<$t> = ::cudnn::utils::ScalParams::default();

            Ok(try!(match self.binary().cudnn().sigmoid_forward(
                &src_desc, unsafe { ::std::mem::transmute::<u64, *const ::libc::c_void>(src_data) },
                &dest_desc, unsafe { ::std::mem::transmute::<u64, *mut ::libc::c_void>(dest_data) },
                scal_params
            ) {
                Ok(_) => Ok(()),
                Err(err) => {
                    println!("{:?}", err);
                    Err(::co::plugin::Error::Operation("Unable to execute CUDA cuDNN Activation Sigmoid Forward."))
                }
            }))
        }

        fn sigmoid_plain(
            &self,
            x: &::co::tensor::SharedTensor<$t>,
            result: &mut ::co::tensor::SharedTensor<$t>
        ) -> Result<(), ::co::error::Error> {
            let src_desc = try!(x.get_cudnn_desc());
            let src_data = try!(try!(x.get(self.device()).ok_or(::co::plugin::Error::MissingMemoryForDevice("Unable to resolve memory for `x`")))
            .as_cuda().ok_or(::co::plugin::Error::MissingMemoryForDevice("Unable to receive native memory for `x`.")))
            .id_c();
            let dest_desc = try!(result.get_cudnn_desc());
            let dest_data = try!(try!(result.get_mut(self.device()).ok_or(::co::plugin::Error::MissingMemoryForDevice("Unable to resolve memory for `result`")))
            .as_cuda().ok_or(::co::plugin::Error::MissingMemoryForDevice("Unable to receive native memory for `result`.")))
            .id_c();
            let scal_params: ::cudnn::utils::ScalParams<$t> = ::cudnn::utils::ScalParams::default();

            Ok(try!(match self.binary().cudnn().sigmoid_forward(
                &src_desc, unsafe { ::std::mem::transmute::<u64, *const ::libc::c_void>(src_data) },
                &dest_desc, unsafe { ::std::mem::transmute::<u64, *mut ::libc::c_void>(dest_data) },
                scal_params
            ) {
                Ok(_) => Ok(()),
                Err(err) => {
                    println!("{:?}", err);
                    Err(::co::plugin::Error::Operation("Unable to execute CUDA cuDNN Activation Sigmoid Forward."))
                }
            }))
        }

        fn sigmoid_diff(
            &self,
            x: &mut ::co::tensor::SharedTensor<$t>,
            result: &mut ::co::tensor::SharedTensor<$t>
        ) -> Result<(), ::co::error::Error> {
            match x.add_device(self.device()) { _ => try!(x.sync(self.device())) }
            match result.add_device(self.device()) { _ => () }
            let src_desc = try!(x.get_cudnn_desc());
            let src_data = try!(try!(x.get(self.device()).ok_or(::co::plugin::Error::MissingMemoryForDevice("Unable to resolve memory for `x`")))
            .as_cuda().ok_or(::co::plugin::Error::MissingMemoryForDevice("Unable to receive native memory for `x`.")))
            .id_c();
            let dest_desc = try!(result.get_cudnn_desc());
            let dest_data = try!(try!(result.get_mut(self.device()).ok_or(::co::plugin::Error::MissingMemoryForDevice("Unable to resolve memory for `result`")))
            .as_cuda().ok_or(::co::plugin::Error::MissingMemoryForDevice("Unable to receive native memory for `result`.")))
            .id_c();
            let scal_params: ::cudnn::utils::ScalParams<$t> = ::cudnn::utils::ScalParams::default();

            Ok(try!(match self.binary().cudnn().sigmoid_forward(
                &src_desc, unsafe { ::std::mem::transmute::<u64, *const ::libc::c_void>(src_data) },
                &dest_desc, unsafe { ::std::mem::transmute::<u64, *mut ::libc::c_void>(dest_data) },
                scal_params
            ) {
                Ok(_) => Ok(()),
                Err(err) => {
                    println!("{:?}", err);
                    Err(::co::plugin::Error::Operation("Unable to execute CUDA cuDNN Activation Sigmoid Forward."))
                }
            }))
        }

        fn sigmoid_diff_plain(
            &self,
            x: &::co::tensor::SharedTensor<$t>,
            result: &mut ::co::tensor::SharedTensor<$t>
        ) -> Result<(), ::co::error::Error> {
            let src_desc = try!(x.get_cudnn_desc());
            let src_data = try!(try!(x.get(self.device()).ok_or(::co::plugin::Error::MissingMemoryForDevice("Unable to resolve memory for `x`")))
            .as_cuda().ok_or(::co::plugin::Error::MissingMemoryForDevice("Unable to receive native memory for `x`.")))
            .id_c();
            let dest_desc = try!(result.get_cudnn_desc());
            let dest_data = try!(try!(result.get_mut(self.device()).ok_or(::co::plugin::Error::MissingMemoryForDevice("Unable to resolve memory for `result`")))
            .as_cuda().ok_or(::co::plugin::Error::MissingMemoryForDevice("Unable to receive native memory for `result`.")))
            .id_c();
            let scal_params: ::cudnn::utils::ScalParams<$t> = ::cudnn::utils::ScalParams::default();

            Ok(try!(match self.binary().cudnn().sigmoid_forward(
                &src_desc, unsafe { ::std::mem::transmute::<u64, *const ::libc::c_void>(src_data) },
                &dest_desc, unsafe { ::std::mem::transmute::<u64, *mut ::libc::c_void>(dest_data) },
                scal_params
            ) {
                Ok(_) => Ok(()),
                Err(err) => {
                    println!("{:?}", err);
                    Err(::co::plugin::Error::Operation("Unable to execute CUDA cuDNN Activation Sigmoid Forward."))
                }
            }))
        }
    )
}
<|endoftext|>


<commit_before>//! Generating Graphviz `dot` files from our IR.

use super::context::{BindgenContext, ItemId};
use super::traversal::Trace;
use std::fs::File;
use std::io::{self, Write};
use std::path::Path;

/// A trait for anything that can write attributes as `<table>` rows to a dot
/// file.
pub trait DotAttributes {
    /// Write this thing's attributes to the given output. Each attribute must
    /// be its own `<tr>...</tr>`.
    fn dot_attributes<W>(&self,
                         ctx: &BindgenContext,
                         out: &mut W)
                         -> io::Result<()>
        where W: io::Write;
}

/// Write a graphviz dot file containing our IR.
pub fn write_dot_file
(ctx: &BindgenContext, path: P) -> io::Result<()>
    where P: AsRef<Path>,
{
    let file = try!(File::create(path));
    let mut dot_file = io::BufWriter::new(file);
    try!(writeln!(&mut dot_file, "digraph {{"));

    let mut err: Option<io::Result<_>> = None;

    for (id, item) in ctx.items() {
        try!(writeln!(&mut dot_file,
                      r#"{} [fontname="courier", label=<
"#,
                      id.as_usize()));
        try!(item.dot_attributes(ctx, &mut dot_file));
        try!(writeln!(&mut dot_file, r#"
 >];"#));

        item.trace(ctx,
                   &mut |sub_id: ItemId, edge_kind| {
            if err.is_some() {
                return;
            }

            match writeln!(&mut dot_file,
                           "{} -> {} [label={:?}];",
                           id.as_usize(),
                           sub_id.as_usize(),
                           edge_kind) {
                Ok(_) => {}
                Err(e) => err = Some(Err(e)),
            }
        },
                   &());

        if let Some(err) = err {
            return err;
        }
    }

    try!(writeln!(&mut dot_file, "}}"));
    Ok(())
}
<commit_msg>Add dotted lines from modules to children in IR visualization<commit_after>//! Generating Graphviz `dot` files from our IR.

use super::context::{BindgenContext, ItemId};
use super::traversal::Trace;
use std::fs::File;
use std::io::{self, Write};
use std::path::Path;

/// A trait for anything that can write attributes as `<table>` rows to a dot
/// file.
pub trait DotAttributes {
    /// Write this thing's attributes to the given output. Each attribute must
    /// be its own `<tr>...</tr>`.
    fn dot_attributes<W>(&self,
                         ctx: &BindgenContext,
                         out: &mut W)
                         -> io::Result<()>
        where W: io::Write;
}

/// Write a graphviz dot file containing our IR.
pub fn write_dot_file
(ctx: &BindgenContext, path: P) -> io::Result<()>
    where P: AsRef<Path>,
{
    let file = try!(File::create(path));
    let mut dot_file = io::BufWriter::new(file);
    try!(writeln!(&mut dot_file, "digraph {{"));

    let mut err: Option<io::Result<_>> = None;

    for (id, item) in ctx.items() {
        try!(writeln!(&mut dot_file,
                      r#"{} [fontname="courier", label=<
"#,
                      id.as_usize()));
        try!(item.dot_attributes(ctx, &mut dot_file));
        try!(writeln!(&mut dot_file, r#"
 >];"#));

        item.trace(ctx,
                   &mut |sub_id: ItemId, edge_kind| {
            if err.is_some() {
                return;
            }

            match writeln!(&mut dot_file,
                           "{} -> {} [label={:?}];",
                           id.as_usize(),
                           sub_id.as_usize(),
                           edge_kind) {
                Ok(_) => {}
                Err(e) => err = Some(Err(e)),
            }
        },
                   &());

        if let Some(err) = err {
            return err;
        }

        if let Some(module) = item.as_module() {
            for child in module.children() {
                try!(writeln!(&mut dot_file,
                              "{} -> {} [style=dotted]",
                              item.id().as_usize(),
                              child.as_usize()));
            }
        }
    }

    try!(writeln!(&mut dot_file, "}}"));
    Ok(())
}
<|endoftext|>