<commit_before>// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use texturegl::Texture;

use geom::matrix::{Matrix4, identity};
use geom::size::Size2D;
use geom::rect::Rect;
use std::cell::RefCell;
use std::rc::Rc;

pub enum Format {
    ARGB32Format,
    RGB24Format
}

#[deriving(Clone)]
pub enum Layer {
    ContainerLayerKind(Rc<ContainerLayer>),
    TextureLayerKind(Rc<TextureLayer>),
}

impl Layer {
    pub fn with_common<T>(&self, f: |&mut CommonLayer| -> T) -> T {
        match *self {
            ContainerLayerKind(ref container_layer) => {
                f(&mut *container_layer.common.borrow_mut())
            },
            TextureLayerKind(ref texture_layer) => {
                f(&mut *texture_layer.common.borrow_mut())
            },
        }
    }
}

pub struct CommonLayer {
    parent: Option<Layer>,
    prev_sibling: Option<Layer>,
    next_sibling: Option<Layer>,

    pub transform: Matrix4<f32>,
}

impl CommonLayer {
    // FIXME: Workaround for cross-crate bug regarding mutability of class fields
    pub fn set_transform(&mut self, new_transform: Matrix4<f32>) {
        self.transform = new_transform;
    }
}

pub fn CommonLayer() -> CommonLayer {
    CommonLayer {
        parent: None,
        prev_sibling: None,
        next_sibling: None,
        transform: identity(),
    }
}

pub struct ContainerLayer {
    pub common: RefCell<CommonLayer>,
    first_child: RefCell<Option<Layer>>,
    last_child: RefCell<Option<Layer>>,
    pub scissor: RefCell<Option<Rect<f32>>>,
}

pub fn ContainerLayer() -> ContainerLayer {
    ContainerLayer {
        common: RefCell::new(CommonLayer()),
        first_child: RefCell::new(None),
        last_child: RefCell::new(None),
        scissor: RefCell::new(None),
    }
}

struct ChildIterator {
    current: Option<Layer>,
}

impl Iterator<Layer> for ChildIterator {
    fn next(&mut self) -> Option<Layer> {
        let (new_current, result) =
            match self.current {
                None => (None, None),
                Some(ref child) => {
                    (child.with_common(|x| x.next_sibling.clone()),
                     Some(child.clone()))
                }
            };
        self.current = new_current;
        result
    }
}

impl ContainerLayer {
    pub fn children(&self) -> ChildIterator {
        ChildIterator {
            current: self.first_child.get().clone(),
        }
    }

    /// Adds a child to the beginning of the list.
    /// Only works when the child is disconnected from the layer tree.
    pub fn add_child_start(pseudo_self: Rc<ContainerLayer>, new_child: Layer) {
        new_child.with_common(|new_child_common| {
            assert!(new_child_common.parent.is_none());
            assert!(new_child_common.prev_sibling.is_none());
            assert!(new_child_common.next_sibling.is_none());

            new_child_common.parent = Some(ContainerLayerKind(pseudo_self.clone()));

            match pseudo_self.first_child.get() {
                None => {}
                Some(ref first_child) => {
                    first_child.with_common(|first_child_common| {
                        assert!(first_child_common.prev_sibling.is_none());
                        first_child_common.prev_sibling = Some(new_child.clone());
                        new_child_common.next_sibling = Some(first_child.clone());
                    });
                }
            }

            pseudo_self.first_child.set(Some(new_child.clone()));

            let should_set = pseudo_self.last_child.get().is_none();
            if should_set {
                pseudo_self.last_child.set(Some(new_child.clone()));
            }
        });
    }

    /// Adds a child to the end of the list.
    /// Only works when the child is disconnected from the layer tree.
    pub fn add_child_end(pseudo_self: Rc<ContainerLayer>, new_child: Layer) {
        new_child.with_common(|new_child_common| {
            assert!(new_child_common.parent.is_none());
            assert!(new_child_common.prev_sibling.is_none());
            assert!(new_child_common.next_sibling.is_none());

            new_child_common.parent = Some(ContainerLayerKind(pseudo_self.clone()));

            match pseudo_self.last_child.get() {
                None => {}
                Some(ref last_child) => {
                    last_child.with_common(|last_child_common| {
                        assert!(last_child_common.next_sibling.is_none());
                        last_child_common.next_sibling = Some(new_child.clone());
                        new_child_common.prev_sibling = Some(last_child.clone());
                    });
                }
            }

            pseudo_self.last_child.set(Some(new_child.clone()));

            let mut child = pseudo_self.first_child.borrow_mut();
            match *child {
                Some(_) => {},
                None => *child = Some(new_child.clone()),
            }
        });
    }

    pub fn remove_child(pseudo_self: Rc<ContainerLayer>, child: Layer) {
        child.with_common(|child_common| {
            assert!(child_common.parent.is_some());
            match child_common.parent {
                Some(ContainerLayerKind(ref container)) => {
                    assert!(container.deref() as *ContainerLayer ==
                            pseudo_self.deref() as *ContainerLayer);
                },
                _ => fail!(~"Invalid parent of child in layer tree"),
            }

            match child_common.next_sibling {
                None => { // this is the last child
                    pseudo_self.last_child.set(child_common.prev_sibling.clone());
                },
                Some(ref sibling) => {
                    sibling.with_common(|sibling_common| {
                        sibling_common.prev_sibling = child_common.prev_sibling.clone();
                    });
                }
            }
            match child_common.prev_sibling {
                None => { // this is the first child
                    pseudo_self.first_child.set(child_common.next_sibling.clone());
                },
                Some(ref sibling) => {
                    sibling.with_common(|sibling_common| {
                        sibling_common.next_sibling = child_common.next_sibling.clone();
                    });
                }
            }
        });
    }
}

/// Whether a texture should be flipped.
#[deriving(Eq)]
pub enum Flip {
    /// The texture should not be flipped.
    NoFlip,
    /// The texture should be flipped vertically.
    VerticalFlip,
}

pub struct TextureLayer {
    pub common: RefCell<CommonLayer>,
    /// A handle to the GPU texture.
    pub texture: Texture,
    /// The size of the texture in pixels.
    size: Size2D<uint>,
    /// Whether this texture is flipped vertically.
    pub flip: Flip,
}

impl TextureLayer {
    pub fn new(texture: Texture, size: Size2D<uint>, flip: Flip) -> TextureLayer {
        TextureLayer {
            common: RefCell::new(CommonLayer()),
            texture: texture,
            size: size,
            flip: flip,
        }
    }
}

<commit_msg>Update to current rust: RefCell::{get, set} are gone.<commit_after>// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use texturegl::Texture;

use geom::matrix::{Matrix4, identity};
use geom::size::Size2D;
use geom::rect::Rect;
use std::cell::RefCell;
use std::rc::Rc;

pub enum Format {
    ARGB32Format,
    RGB24Format
}

#[deriving(Clone)]
pub enum Layer {
    ContainerLayerKind(Rc<ContainerLayer>),
    TextureLayerKind(Rc<TextureLayer>),
}

impl Layer {
    pub fn with_common<T>(&self, f: |&mut CommonLayer| -> T) -> T {
        match *self {
            ContainerLayerKind(ref container_layer) => {
                f(&mut *container_layer.common.borrow_mut())
            },
            TextureLayerKind(ref texture_layer) => {
                f(&mut *texture_layer.common.borrow_mut())
            },
        }
    }
}

pub struct CommonLayer {
    parent: Option<Layer>,
    prev_sibling: Option<Layer>,
    next_sibling: Option<Layer>,

    pub transform: Matrix4<f32>,
}

impl CommonLayer {
    // FIXME: Workaround for cross-crate bug regarding mutability of class fields
    pub fn set_transform(&mut self, new_transform: Matrix4<f32>) {
        self.transform = new_transform;
    }
}

pub fn CommonLayer() -> CommonLayer {
    CommonLayer {
        parent: None,
        prev_sibling: None,
        next_sibling: None,
        transform: identity(),
    }
}

pub struct ContainerLayer {
    pub common: RefCell<CommonLayer>,
    first_child: RefCell<Option<Layer>>,
    last_child: RefCell<Option<Layer>>,
    pub scissor: RefCell<Option<Rect<f32>>>,
}

pub fn ContainerLayer() -> ContainerLayer {
    ContainerLayer {
        common: RefCell::new(CommonLayer()),
        first_child: RefCell::new(None),
        last_child: RefCell::new(None),
        scissor: RefCell::new(None),
    }
}

struct ChildIterator {
    current: Option<Layer>,
}

impl Iterator<Layer> for ChildIterator {
    fn next(&mut self) -> Option<Layer> {
        let (new_current, result) =
            match self.current {
                None => (None, None),
                Some(ref child) => {
                    (child.with_common(|x| x.next_sibling.clone()),
                     Some(child.clone()))
                }
            };
        self.current = new_current;
        result
    }
}

impl ContainerLayer {
    pub fn children(&self) -> ChildIterator {
        ChildIterator {
            current: self.first_child.borrow().clone(),
        }
    }

    /// Adds a child to the beginning of the list.
    /// Only works when the child is disconnected from the layer tree.
    pub fn add_child_start(pseudo_self: Rc<ContainerLayer>, new_child: Layer) {
        new_child.with_common(|new_child_common| {
            assert!(new_child_common.parent.is_none());
            assert!(new_child_common.prev_sibling.is_none());
            assert!(new_child_common.next_sibling.is_none());

            new_child_common.parent = Some(ContainerLayerKind(pseudo_self.clone()));

            match *pseudo_self.first_child.borrow() {
                None => {}
                Some(ref first_child) => {
                    first_child.with_common(|first_child_common| {
                        assert!(first_child_common.prev_sibling.is_none());
                        first_child_common.prev_sibling = Some(new_child.clone());
                        new_child_common.next_sibling = Some(first_child.clone());
                    });
                }
            }

            *pseudo_self.first_child.borrow_mut() = Some(new_child.clone());

            let should_set = pseudo_self.last_child.borrow().is_none();
            if should_set {
                *pseudo_self.last_child.borrow_mut() = Some(new_child.clone());
            }
        });
    }

    /// Adds a child to the end of the list.
    /// Only works when the child is disconnected from the layer tree.
    pub fn add_child_end(pseudo_self: Rc<ContainerLayer>, new_child: Layer) {
        new_child.with_common(|new_child_common| {
            assert!(new_child_common.parent.is_none());
            assert!(new_child_common.prev_sibling.is_none());
            assert!(new_child_common.next_sibling.is_none());

            new_child_common.parent = Some(ContainerLayerKind(pseudo_self.clone()));

            match *pseudo_self.last_child.borrow() {
                None => {}
                Some(ref last_child) => {
                    last_child.with_common(|last_child_common| {
                        assert!(last_child_common.next_sibling.is_none());
                        last_child_common.next_sibling = Some(new_child.clone());
                        new_child_common.prev_sibling = Some(last_child.clone());
                    });
                }
            }

            *pseudo_self.last_child.borrow_mut() = Some(new_child.clone());

            let mut child = pseudo_self.first_child.borrow_mut();
            match *child {
                Some(_) => {},
                None => *child = Some(new_child.clone()),
            }
        });
    }

    pub fn remove_child(pseudo_self: Rc<ContainerLayer>, child: Layer) {
        child.with_common(|child_common| {
            assert!(child_common.parent.is_some());
            match child_common.parent {
                Some(ContainerLayerKind(ref container)) => {
                    assert!(container.deref() as *ContainerLayer ==
                            pseudo_self.deref() as *ContainerLayer);
                },
                _ => fail!(~"Invalid parent of child in layer tree"),
            }

            match child_common.next_sibling {
                None => { // this is the last child
                    *pseudo_self.last_child.borrow_mut() = child_common.prev_sibling.clone();
                },
                Some(ref sibling) => {
                    sibling.with_common(|sibling_common| {
                        sibling_common.prev_sibling = child_common.prev_sibling.clone();
                    });
                }
            }
            match child_common.prev_sibling {
                None => { // this is the first child
                    *pseudo_self.first_child.borrow_mut() = child_common.next_sibling.clone();
                },
                Some(ref sibling) => {
                    sibling.with_common(|sibling_common| {
                        sibling_common.next_sibling = child_common.next_sibling.clone();
                    });
                }
            }
        });
    }
}

/// Whether a texture should be flipped.
#[deriving(Eq)]
pub enum Flip {
    /// The texture should not be flipped.
    NoFlip,
    /// The texture should be flipped vertically.
    VerticalFlip,
}

pub struct TextureLayer {
    pub common: RefCell<CommonLayer>,
    /// A handle to the GPU texture.
    pub texture: Texture,
    /// The size of the texture in pixels.
    size: Size2D<uint>,
    /// Whether this texture is flipped vertically.
    pub flip: Flip,
}

impl TextureLayer {
    pub fn new(texture: Texture, size: Size2D<uint>, flip: Flip) -> TextureLayer {
        TextureLayer {
            common: RefCell::new(CommonLayer()),
            texture: texture,
            size: size,
            flip: flip,
        }
    }
}

<|endoftext|>


<commit_before>#[macro_use]
extern crate nom;
extern crate syn;

pub trait Template {
    fn render_to(&self, writer: &mut std::fmt::Write);
    fn render(&self) -> String {
        let mut buf = String::new();
        self.render_to(&mut buf);
        buf
    }
}

mod generator;
mod parser;
mod path;

pub mod filters;
pub use path::rerun_if_templates_changed;

struct TemplateMeta {
    path: String,
    print: bool,
}

fn get_template_meta(ast: &syn::DeriveInput) -> TemplateMeta {
    let mut path = None;
    let mut print = false;
    let attr = ast.attrs.iter().find(|a| a.name() == "template").unwrap();
    if let syn::MetaItem::List(_, ref inner) = attr.value {
        for nm_item in inner {
            if let &syn::NestedMetaItem::MetaItem(ref item) = nm_item {
                if let &syn::MetaItem::NameValue(ref key, ref val) = item {
                    match key.as_ref() {
                        "path" => if let &syn::Lit::Str(ref s, _) = val {
                            path = Some(s.clone());
                        } else {
                            panic!("template path must be string literal");
                        },
                        "print" => if let &syn::Lit::Str(ref s, _) = val {
                            print = if s == "true" { true } else { false };
                        } else {
                            panic!("print value must be string literal");
                        },
                        _ => { panic!("unsupported annotation key found") }
                    }
                }
            }
        }
    }
    if path.is_none() {
        panic!("template path not found in struct attributes");
    }
    TemplateMeta { path: path.unwrap(), print: print }
}

pub fn build_template(ast: &syn::DeriveInput) -> String {
    let meta = get_template_meta(ast);
    let src = path::get_template_source(&meta.path);
    let nodes = parser::parse(&src);
    let code = generator::generate(ast, &meta.path, nodes);
    if meta.print {
        println!("{}", code);
    }
    code
}
<commit_msg>Cleanups as suggested by clippy<commit_after>#[macro_use]
extern crate nom;
extern crate syn;

pub trait Template {
    fn render_to(&self, writer: &mut std::fmt::Write);
    fn render(&self) -> String {
        let mut buf = String::new();
        self.render_to(&mut buf);
        buf
    }
}

mod generator;
mod parser;
mod path;

pub mod filters;
pub use path::rerun_if_templates_changed;

struct TemplateMeta {
    path: String,
    print: bool,
}

fn get_template_meta(ast: &syn::DeriveInput) -> TemplateMeta {
    let mut path = None;
    let mut print = false;
    let attr = ast.attrs.iter().find(|a| a.name() == "template").unwrap();
    if let syn::MetaItem::List(_, ref inner) = attr.value {
        for nm_item in inner {
            if let syn::NestedMetaItem::MetaItem(ref item) = *nm_item {
                if let syn::MetaItem::NameValue(ref key, ref val) = *item {
                    match key.as_ref() {
                        "path" => if let syn::Lit::Str(ref s, _) = *val {
                            path = Some(s.clone());
                        } else {
                            panic!("template path must be string literal");
                        },
                        "print" => if let syn::Lit::Str(ref s, _) = *val {
                            print = s == "true";
                        } else {
                            panic!("print value must be string literal");
                        },
                        _ => { panic!("unsupported annotation key found") }
                    }
                }
            }
        }
    }
    if path.is_none() {
        panic!("template path not found in struct attributes");
    }
    TemplateMeta { path: path.unwrap(), print: print }
}

pub fn build_template(ast: &syn::DeriveInput) -> String {
    let meta = get_template_meta(ast);
    let src = path::get_template_source(&meta.path);
    let nodes = parser::parse(&src);
    let code = generator::generate(ast, &meta.path, nodes);
    if meta.print {
        println!("{}", code);
    }
    code
}
<|endoftext|>


<commit_before>
extern crate glob;
extern crate winapi;
use self::winapi::shared::minwindef::{DWORD, HKEY};
use self::winapi::um::winnt::{KEY_READ};
use self::winapi::um::winreg;

extern crate intercom;
use intercom::*;

use std::path::PathBuf;
use std::ffi::OsString;
use std::os::windows::ffi::{OsStrExt, OsStringExt};
use ::host;

#[allow(dead_code)]
#[allow(non_upper_case_globals)]
const CLSID_SetupConfiguration : GUID = GUID {
    data1: 0x177f_0c4a,
    data2: 0x1cd3,
    data3: 0x4de7,
    data4: [ 0xa3, 0x2c, 0x71, 0xdb, 0xbb, 0x9f, 0xa3, 0x6d ]
};

#[repr(C)]
#[derive(Default)]
pub struct FILETIME {
    low_part : u32,
    high_part : u32,
}

#[com_interface( "B41463C3-8866-43B5-BC33-2B0676F7F42E" )]
pub trait ISetupInstance
{
    fn get_instance_id( &self ) -> ComResult< String >;

    fn get_install_date( &self ) -> ComResult< FILETIME >;

    fn get_installation_name( &self ) -> ComResult< String >;

    fn get_installation_path( &self ) -> ComResult< String >;

    fn get_installation_version( &self ) -> ComResult< String >;

    fn get_display_name( &self, lcid: u32 ) -> ComResult< String >;

    fn get_description( &self, lcid: u32 ) -> ComResult< String >;

    fn resolve_path( &self, path : String ) -> ComResult< String >;
}

#[com_interface( "6380BCFF-41D3-4B2E-8B2E-BF8A6810C848" )]
pub trait IEnumSetupInstances
{
    fn next(
        &self,
        celt : u32
    ) -> ComResult< ( ComItf< ISetupInstance >, u32 ) >;

    fn skip( &self, celt : u32 ) -> ComResult<()>;

    fn reset( &self ) -> ComResult<()>;

    fn clone( &self ) -> ComResult< ComItf< IEnumSetupInstances > >;
}

#[com_interface( "26AAB78C-4A60-49D6-AF3B-3C35BC93365D" )]
pub trait ISetupConfiguration2
{
    fn enum_instances( &self )
        -> ComResult< ComItf< IEnumSetupInstances > >;

    fn get_instance_for_current_process( &self )
        -> ComResult< ComItf< ISetupInstance > >;

    fn get_instance_for_path( &self, path : String )
        -> ComResult< ComItf< ISetupInstance > >;

    fn enum_all_instances( &self )
        -> ComResult< ComItf< IEnumSetupInstances > >;
}

pub struct ToolPaths {
    pub mt : PathBuf,
    pub rc : PathBuf,
    pub midl : PathBuf,

    pub vs_bin : PathBuf,

    pub libs : Vec<PathBuf>,
    pub incs : Vec<PathBuf>,
}

/// Finds a path for the target within the roots.
///
/// * `roots` - Roots to search the target under.
/// * `target` - Path to send. May contain directories, such as `x86/foo.lib`.
///
/// Returns the full path to the target or None if the target was not found.
fn find_path( roots : &[&PathBuf], path : &str ) -> Option< PathBuf > {

    // Glob options. We'll want case insensitive searching thanks to Windows.
    let options = glob::MatchOptions {
        case_sensitive: false,
        require_literal_separator: true,
        require_literal_leading_dot: true,
    };

    // Go through all the roots one by one.
    for root in roots {

        // Create the glob pattern. Concatenate the root with the target and
        // throw ** in between.
        let root_str = root.to_string_lossy();
        let pattern = format!( "{}/**/{}", root_str, path );

        // Go through all entries.
        for entry in glob::glob_with( &pattern, &options ).unwrap() {
            if let Ok( entry_path ) = entry {

                // The first entry we find, we'll return.
                return Some( entry_path );
            }
        }
    }

    // No paths found. Return none as the default.
    None
}

/// Get compiler paths from absolute file paths.
///
/// The compiler takes paths to the directories containing the files we want
/// as LIB and INCLUDE paths. This function strips the file name from the path
/// and deduplicates the resulting directories.
fn get_compiler_paths( paths : &[ PathBuf ] ) -> Vec<PathBuf>
{
    // Get the directory paths from the file paths.
    let mut dir_paths = paths.into_iter()
        .filter_map(
            |p| p.parent().and_then(
                |p| Some( p.to_owned() ) ) )
        .collect::<Vec<_>>();

    // De-duplicate the directories.
    dir_paths.sort();
    dir_paths.dedup();

    dir_paths
}

/// Gets the Windows kit path and version.
fn get_kit_path() -> Result<( String, String ), String> {

    unsafe {

        // Turn the various string constants into WCHAR* strings for winapi.
        let key = OsString::from( "SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots\0" )
                    .encode_wide()
                    .collect::<Vec<_>>();
        let value = OsString::from( "KitsRoot10\0" )
                    .encode_wide()
                    .collect::<Vec<_>>();

        // Open the Installed Roots registry key.
        let mut hkey_roots : HKEY = ::std::ptr::null_mut();
        let mut disp : DWORD = 0;
        winreg::RegCreateKeyExW(
            winreg::HKEY_LOCAL_MACHINE,
            key.as_slice().as_ptr(),
            0, ::std::ptr::null_mut(), 0,
            KEY_READ,
            ::std::ptr::null_mut(),
            &mut hkey_roots,
            &mut disp );
        if hkey_roots.is_null() {
            return Err( "Could not open Windows Kits registry key".to_owned() );
        }

        // Get the 'KitsRoot10' value from the above key.
        let mut buf = Vec::with_capacity( 250 );
        let mut buf_len : DWORD = 250;
        let hr = winreg::RegQueryValueExW(
                hkey_roots, value.as_slice().as_ptr(), ::std::ptr::null_mut(),
                ::std::ptr::null_mut(),
                buf.as_mut_ptr() as *mut _,
                &mut buf_len );
        if hr != 0 {
            return Err( "Could not read KitsRoot10 registry value".to_owned() );
        }

        // Turn the value into a rust string.
        buf.set_len( ( buf_len / 2 ) as usize - 1 );
        let kitsroot_osstring = OsString::from_wide( &buf );
        let kitsroot = kitsroot_osstring.to_string_lossy().to_owned();

        // Get the first sub-key under the Installed Roots. We don't really
        // care which kit version this is as long as it's one that is present
        // on the system.
        let mut buf = Vec::with_capacity( 250 );
        let mut buf_len : DWORD = 250;
        let hr = winreg::RegEnumKeyExW(
            hkey_roots, 0,
            buf.as_mut_ptr(), &mut buf_len,
            ::std::ptr::null_mut(),
            ::std::ptr::null_mut(),
            ::std::ptr::null_mut(),
            ::std::ptr::null_mut() );
        if hr != 0 {
            return Err( "Could not read Windows Kits versions".to_owned() );
        }

        // Turn the installed version to a rust string.
        buf.set_len( buf_len as usize );
        let kitsversion_osstring = OsString::from_wide( &buf );
        let kitsversion = kitsversion_osstring.to_string_lossy().to_owned();

        Ok( ( kitsroot.into_owned(), kitsversion.into_owned() ) )
    }
}

/// Gets the VS 2017+ installation path.
fn get_vs_path() -> Result<String, String> {

    intercom::runtime::initialize()
            .map_err( |hr| format!( "Failed to initialize COM: {:?}", hr ) )?;

    let installation_path = {

        // Get the COM API entry point for the new VS configuration API.
        let setup_conf = ComRc::<ISetupConfiguration2>
                ::create( CLSID_SetupConfiguration ).unwrap();

        // Get the first instance.
        let instances = setup_conf.enum_instances().unwrap();
        let ( next, _ ) = instances.next( 1 ).unwrap();

        // Read the installation path. We don't care about anything else for now.
        next.get_installation_path().unwrap()
    };

    intercom::runtime::uninitialize();

    Ok( installation_path )
}

pub fn get_tool_paths() -> Result<ToolPaths, String> {

    // Get the Windows kit base path and version.
    let ( kitroot, kitversion ) = get_kit_path()?;
    let kitroot = PathBuf::from( &kitroot );

    // Form the kit lib and include paths.
    // let kit_lib_root = kitroot.join( "Lib" ).join( &kitversion );
    let kit_include_root = kitroot.join( "Include" ).join( &kitversion );

    // Get the Visual C++ tools path.
    let vsroot = PathBuf::from( &get_vs_path()? ).join( r"VC\Tools\MSVC" );

    // Resolve the required libs.
    let libs = get_compiler_paths( &[

            // These paths cover most of the lib dirs. However no idea if we
            // need them so they are commented out until something complains.

            // find_path( &[ &kit_lib_root ], r"x64\ucrt.lib" ).unwrap(),
            // find_path( &[ &kit_lib_root ], r"x64\ole32.lib" ).unwrap(),
            // find_path( &[ &vsroot ], r"lib\x64\vcruntime.lib" ).unwrap(),
        ] );

    // Resolve the required include directories.
    let incs = get_compiler_paths( &[
            find_path( &[ &kit_include_root ], r"oaidl.idl" ).unwrap(),
            find_path( &[ &kit_include_root ], r"wtypes.idl" ).unwrap(),
        ] );

    // We'll need the C compiler for the preprocessing of the IDL.
    let mut vs_bin = find_path( &[ &vsroot ], r"Hostx64\x64\cl.exe" ).unwrap();
    vs_bin.pop();

    // Resource compiler depends on the toolchain.
    let host = host::get_host();
    let rc = match host.compiler {
        host::Compiler::Msvc =>
            kitroot.join( format!( r"bin\{}\x64\rc.exe", kitversion ) ),
        host::Compiler::Gnu =>
            PathBuf::from( "windres.exe" ),  // Needs to be on PATH.
    };

    Ok( ToolPaths {
        mt: kitroot.join( format!( r"bin\{}\x64\mt.exe", kitversion ) ),
        midl: kitroot.join( format!( r"bin\{}\x64\midl.exe", kitversion ) ),
        rc: rc,

        vs_bin: vs_bin,

        libs: libs,
        incs: incs,
    } )
}

#[cfg(test)]
mod test
{
    use super::*;
    use ::std::process::Command;
    use ::std::path::PathBuf;

    #[test]
    fn get_vs_2017_details() {
        intercom::runtime::initialize().unwrap();
		{

			let setup_conf = ComRc::<ISetupConfiguration2>
					::create( CLSID_SetupConfiguration ).unwrap();
			let instances = setup_conf.enum_instances().unwrap();

			let ( next, _ ) = instances.next( 1 ).unwrap();
			let installation_name = next.get_installation_name().unwrap();
			let installation_path = next.get_installation_path().unwrap();
			let installation_version = next.get_installation_version().unwrap();

			let vswhere_path = get_intercom_root().join( "scripts/vswhere.exe" );
			let installation_name_output = Command::new( &vswhere_path )
					.arg( "/nologo" )
                    .arg( "-products").arg( "*" )  // Required to locate Visual Studio Build Tools
					.arg( "-property" ).arg( "installationName" )
					.output()
					.unwrap();
			let installation_path_output = Command::new( &vswhere_path )
					.arg( "/nologo" )
                    .arg( "-products").arg( "*" )  // Required to locate Visual Studio Build Tools
					.arg( "-property" ).arg( "installationPath" )
					.output()
					.unwrap();
			let installation_version_output = Command::new( &vswhere_path )
					.arg( "/nologo" )
                    .arg( "-products").arg( "*" )  // Required to locate Visual Studio Build Tools
					.arg( "-property" ).arg( "installationVersion" )
					.output()
					.unwrap();

			let installation_name_actual = String::from_utf8_lossy(
					&installation_name_output.stdout );
			let installation_path_actual = String::from_utf8_lossy(
					&installation_path_output.stdout );
			let installation_version_actual = String::from_utf8_lossy(
					&installation_version_output.stdout );

			assert_eq!( installation_name_actual.trim(), installation_name );
			assert_eq!( installation_path_actual.trim(), installation_path );
			assert_eq!( installation_version_actual.trim(), installation_version );

		}
        intercom::runtime::uninitialize();
    }

    fn get_intercom_root() -> PathBuf {
        let mut root_path = ::std::env::current_exe().unwrap();
        loop {
            if root_path.join( "intercom-build" ).exists() {
                break;
            }
            assert!( root_path.pop() );
        }

        root_path
    }

    #[test]
    fn get_tool_paths_returns_valid_paths() {
        intercom::runtime::initialize().unwrap();

        // The HOST env variable is set for build.rs, not for tests.
        // We need to fake one here.
        ::std::env::set_var( "HOST", "x86_64-pc-windows-msvc" );

        let paths = get_tool_paths().unwrap();
        assert!( paths.mt.exists() );
        assert!( paths.rc.exists() );
        assert!( paths.midl.exists() );

        intercom::runtime::uninitialize();
    }
}
<commit_msg>Try locating information about available VS compilers separately instead of using "*" with vswhere<commit_after>
extern crate glob;
extern crate winapi;
use self::winapi::shared::minwindef::{DWORD, HKEY};
use self::winapi::um::winnt::{KEY_READ};
use self::winapi::um::winreg;

extern crate intercom;
use intercom::*;

use std::path::PathBuf;
use std::ffi::OsString;
use std::os::windows::ffi::{OsStrExt, OsStringExt};
use ::host;

#[allow(dead_code)]
#[allow(non_upper_case_globals)]
const CLSID_SetupConfiguration : GUID = GUID {
    data1: 0x177f_0c4a,
    data2: 0x1cd3,
    data3: 0x4de7,
    data4: [ 0xa3, 0x2c, 0x71, 0xdb, 0xbb, 0x9f, 0xa3, 0x6d ]
};

#[repr(C)]
#[derive(Default)]
pub struct FILETIME {
    low_part : u32,
    high_part : u32,
}

#[com_interface( "B41463C3-8866-43B5-BC33-2B0676F7F42E" )]
pub trait ISetupInstance
{
    fn get_instance_id( &self ) -> ComResult< String >;

    fn get_install_date( &self ) -> ComResult< FILETIME >;

    fn get_installation_name( &self ) -> ComResult< String >;

    fn get_installation_path( &self ) -> ComResult< String >;

    fn get_installation_version( &self ) -> ComResult< String >;

    fn get_display_name( &self, lcid: u32 ) -> ComResult< String >;

    fn get_description( &self, lcid: u32 ) -> ComResult< String >;

    fn resolve_path( &self, path : String ) -> ComResult< String >;
}

#[com_interface( "6380BCFF-41D3-4B2E-8B2E-BF8A6810C848" )]
pub trait IEnumSetupInstances
{
    fn next(
        &self,
        celt : u32
    ) -> ComResult< ( ComItf< ISetupInstance >, u32 ) >;

    fn skip( &self, celt : u32 ) -> ComResult<()>;

    fn reset( &self ) -> ComResult<()>;

    fn clone( &self ) -> ComResult< ComItf< IEnumSetupInstances > >;
}

#[com_interface( "26AAB78C-4A60-49D6-AF3B-3C35BC93365D" )]
pub trait ISetupConfiguration2
{
    fn enum_instances( &self )
        -> ComResult< ComItf< IEnumSetupInstances > >;

    fn get_instance_for_current_process( &self )
        -> ComResult< ComItf< ISetupInstance > >;

    fn get_instance_for_path( &self, path : String )
        -> ComResult< ComItf< ISetupInstance > >;

    fn enum_all_instances( &self )
        -> ComResult< ComItf< IEnumSetupInstances > >;
}

pub struct ToolPaths {
    pub mt : PathBuf,
    pub rc : PathBuf,
    pub midl : PathBuf,

    pub vs_bin : PathBuf,

    pub libs : Vec<PathBuf>,
    pub incs : Vec<PathBuf>,
}

/// Finds a path for the target within the roots.
///
/// * `roots` - Roots to search the target under.
/// * `target` - Path to send. May contain directories, such as `x86/foo.lib`.
///
/// Returns the full path to the target or None if the target was not found.
fn find_path( roots : &[&PathBuf], path : &str ) -> Option< PathBuf > {

    // Glob options. We'll want case insensitive searching thanks to Windows.
    let options = glob::MatchOptions {
        case_sensitive: false,
        require_literal_separator: true,
        require_literal_leading_dot: true,
    };

    // Go through all the roots one by one.
    for root in roots {

        // Create the glob pattern. Concatenate the root with the target and
        // throw ** in between.
        let root_str = root.to_string_lossy();
        let pattern = format!( "{}/**/{}", root_str, path );

        // Go through all entries.
        for entry in glob::glob_with( &pattern, &options ).unwrap() {
            if let Ok( entry_path ) = entry {

                // The first entry we find, we'll return.
                return Some( entry_path );
            }
        }
    }

    // No paths found. Return none as the default.
    None
}

/// Get compiler paths from absolute file paths.
///
/// The compiler takes paths to the directories containing the files we want
/// as LIB and INCLUDE paths. This function strips the file name from the path
/// and deduplicates the resulting directories.
fn get_compiler_paths( paths : &[ PathBuf ] ) -> Vec<PathBuf>
{
    // Get the directory paths from the file paths.
    let mut dir_paths = paths.into_iter()
        .filter_map(
            |p| p.parent().and_then(
                |p| Some( p.to_owned() ) ) )
        .collect::<Vec<_>>();

    // De-duplicate the directories.
    dir_paths.sort();
    dir_paths.dedup();

    dir_paths
}

/// Gets the Windows kit path and version.
fn get_kit_path() -> Result<( String, String ), String> {

    unsafe {

        // Turn the various string constants into WCHAR* strings for winapi.
        let key = OsString::from( "SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots\0" )
                    .encode_wide()
                    .collect::<Vec<_>>();
        let value = OsString::from( "KitsRoot10\0" )
                    .encode_wide()
                    .collect::<Vec<_>>();

        // Open the Installed Roots registry key.
        let mut hkey_roots : HKEY = ::std::ptr::null_mut();
        let mut disp : DWORD = 0;
        winreg::RegCreateKeyExW(
            winreg::HKEY_LOCAL_MACHINE,
            key.as_slice().as_ptr(),
            0, ::std::ptr::null_mut(), 0,
            KEY_READ,
            ::std::ptr::null_mut(),
            &mut hkey_roots,
            &mut disp );
        if hkey_roots.is_null() {
            return Err( "Could not open Windows Kits registry key".to_owned() );
        }

        // Get the 'KitsRoot10' value from the above key.
        let mut buf = Vec::with_capacity( 250 );
        let mut buf_len : DWORD = 250;
        let hr = winreg::RegQueryValueExW(
                hkey_roots, value.as_slice().as_ptr(), ::std::ptr::null_mut(),
                ::std::ptr::null_mut(),
                buf.as_mut_ptr() as *mut _,
                &mut buf_len );
        if hr != 0 {
            return Err( "Could not read KitsRoot10 registry value".to_owned() );
        }

        // Turn the value into a rust string.
        buf.set_len( ( buf_len / 2 ) as usize - 1 );
        let kitsroot_osstring = OsString::from_wide( &buf );
        let kitsroot = kitsroot_osstring.to_string_lossy().to_owned();

        // Get the first sub-key under the Installed Roots. We don't really
        // care which kit version this is as long as it's one that is present
        // on the system.
        let mut buf = Vec::with_capacity( 250 );
        let mut buf_len : DWORD = 250;
        let hr = winreg::RegEnumKeyExW(
            hkey_roots, 0,
            buf.as_mut_ptr(), &mut buf_len,
            ::std::ptr::null_mut(),
            ::std::ptr::null_mut(),
            ::std::ptr::null_mut(),
            ::std::ptr::null_mut() );
        if hr != 0 {
            return Err( "Could not read Windows Kits versions".to_owned() );
        }

        // Turn the installed version to a rust string.
        buf.set_len( buf_len as usize );
        let kitsversion_osstring = OsString::from_wide( &buf );
        let kitsversion = kitsversion_osstring.to_string_lossy().to_owned();

        Ok( ( kitsroot.into_owned(), kitsversion.into_owned() ) )
    }
}

/// Gets the VS 2017+ installation path.
fn get_vs_path() -> Result<String, String> {

    intercom::runtime::initialize()
            .map_err( |hr| format!( "Failed to initialize COM: {:?}", hr ) )?;

    let installation_path = {

        // Get the COM API entry point for the new VS configuration API.
        let setup_conf = ComRc::<ISetupConfiguration2>
                ::create( CLSID_SetupConfiguration ).unwrap();

        // Get the first instance.
        let instances = setup_conf.enum_instances().unwrap();
        let ( next, _ ) = instances.next( 1 ).unwrap();

        // Read the installation path. We don't care about anything else for now.
        next.get_installation_path().unwrap()
    };

    intercom::runtime::uninitialize();

    Ok( installation_path )
}

pub fn get_tool_paths() -> Result<ToolPaths, String> {

    // Get the Windows kit base path and version.
    let ( kitroot, kitversion ) = get_kit_path()?;
    let kitroot = PathBuf::from( &kitroot );

    // Form the kit lib and include paths.
    // let kit_lib_root = kitroot.join( "Lib" ).join( &kitversion );
    let kit_include_root = kitroot.join( "Include" ).join( &kitversion );

    // Get the Visual C++ tools path.
    let vsroot = PathBuf::from( &get_vs_path()? ).join( r"VC\Tools\MSVC" );

    // Resolve the required libs.
    let libs = get_compiler_paths( &[

            // These paths cover most of the lib dirs. However no idea if we
            // need them so they are commented out until something complains.

            // find_path( &[ &kit_lib_root ], r"x64\ucrt.lib" ).unwrap(),
            // find_path( &[ &kit_lib_root ], r"x64\ole32.lib" ).unwrap(),
            // find_path( &[ &vsroot ], r"lib\x64\vcruntime.lib" ).unwrap(),
        ] );

    // Resolve the required include directories.
    let incs = get_compiler_paths( &[
            find_path( &[ &kit_include_root ], r"oaidl.idl" ).unwrap(),
            find_path( &[ &kit_include_root ], r"wtypes.idl" ).unwrap(),
        ] );

    // We'll need the C compiler for the preprocessing of the IDL.
    let mut vs_bin = find_path( &[ &vsroot ], r"Hostx64\x64\cl.exe" ).unwrap();
    vs_bin.pop();

    // Resource compiler depends on the toolchain.
    let host = host::get_host();
    let rc = match host.compiler {
        host::Compiler::Msvc =>
            kitroot.join( format!( r"bin\{}\x64\rc.exe", kitversion ) ),
        host::Compiler::Gnu =>
            PathBuf::from( "windres.exe" ),  // Needs to be on PATH.
    };

    Ok( ToolPaths {
        mt: kitroot.join( format!( r"bin\{}\x64\mt.exe", kitversion ) ),
        midl: kitroot.join( format!( r"bin\{}\x64\midl.exe", kitversion ) ),
        rc: rc,

        vs_bin: vs_bin,

        libs: libs,
        incs: incs,
    } )
}

#[cfg(test)]
mod test
{
    use super::*;
    use ::std::process::Command;
    use ::std::path::PathBuf;

    #[test]
    fn get_vs_2017_details() {
        intercom::runtime::initialize().unwrap();
		{

			let setup_conf = ComRc::<ISetupConfiguration2>
					::create( CLSID_SetupConfiguration ).unwrap();
			let instances = setup_conf.enum_instances().unwrap();

			let ( next, _ ) = instances.next( 1 ).unwrap();
			let installation_name = next.get_installation_name().unwrap();
			let installation_path = next.get_installation_path().unwrap();
			let installation_version = next.get_installation_version().unwrap();

			let installation_name_actual = get_vswhere_property( "installationName" );
			let installation_path_actual = get_vswhere_property( "installationPath" );
			let installation_version_actual = get_vswhere_property( "installationVersion" );

			assert_eq!( installation_name_actual.trim(), installation_name );
			assert_eq!( installation_path_actual.trim(), installation_path );
			assert_eq!( installation_version_actual.trim(), installation_version );

		}
        intercom::runtime::uninitialize();
    }

    fn get_intercom_root() -> PathBuf {
        let mut root_path = ::std::env::current_exe().unwrap();
        loop {
            if root_path.join( "intercom-build" ).exists() {
                break;
            }
            assert!( root_path.pop() );
        }

        root_path
    }

    /// Gets the specified vswhere property.
    fn get_vswhere_property(
        property: &str,
    ) -> String {

        // First attempt to get the property without the "BuildTools" product.
        // If we don't get a result then try "BuildTools" explicitly.
        // We do this in two steps to avoid issues when the developer has both the Visual Studio and the BuildTools installed.
        match get_vswhere_property_for_products( property, &[] )
        {
            Ok( value ) => value,
            Err( _ ) => get_vswhere_property_for_products(
                    property, &[ "Microsoft.VisualStudio.Product.BuildTools" ] ).unwrap(),
        }
    }

    /// Gets the specified vswhere property for the specified products.
    fn get_vswhere_property_for_products(
        property: &str,
        products: &[&str],
    ) -> Result<String, String>
    {
        let vswhere_path = get_intercom_root().join( "scripts/vswhere.exe" );
        let property_from_output =
                if products.len() == 0
                {
                    // No products where specified so we use the defaults which are
                    // Community, Professional and Enterprise.
                    Command::new( &vswhere_path )
                            .arg( "/nologo" )
                            .arg( "-property" ).arg( property )
                            .output()
                            .unwrap()
                            .stdout
                }
                else
                {
                    Command::new( &vswhere_path )
                            .arg( "/nologo" )
                            .arg( "-products").args( products )
                            .arg( "-property" ).arg( property )
                            .output()
                            .unwrap()
                            .stdout
                };

        // Ensure we got exactly one result.
        let property_from_output: String = String::from_utf8_lossy( &property_from_output ).to_owned().to_string();
        let values: Vec<&str> = property_from_output
                .split( "\r\n" )
                .filter_map( |s| { if s.is_empty() { None } else { Some( s ) } } )
                .collect();
        if values.len() != 1
        {
            Err( format!( "Found multiple products with the property: {}. Properties: {}",
                            property, values.join( ", " ) ) )
        }
        else
        {
            Ok( values[ 0 ].to_string() )
        }
    }

    #[test]
    fn get_tool_paths_returns_valid_paths() {
        intercom::runtime::initialize().unwrap();

        // The HOST env variable is set for build.rs, not for tests.
        // We need to fake one here.
        ::std::env::set_var( "HOST", "x86_64-pc-windows-msvc" );

        let paths = get_tool_paths().unwrap();
        assert!( paths.mt.exists() );
        assert!( paths.rc.exists() );
        assert!( paths.midl.exists() );

        intercom::runtime::uninitialize();
    }
}
<|endoftext|>


<commit_before>#![allow(dead_code)]
use std::ops::{Deref, DerefMut};

use uuid::Uuid;
use rustwlc::WlcView;

use ::layout::{Tree, Layout, TreeError};

type TestTreeResult = Result<UnitTestTree, TreeError>;

/// A tree that has some utility methods to aid in creating unit tests.
#[derive(Debug)]
pub struct UnitTestTree(Tree);

#[allow(dead_code)]
impl UnitTestTree {
    pub fn new() -> Self {
        UnitTestTree (
            Tree::new()
        )
    }

    /// Attempts to add an output to the tree.
    pub fn add_output(mut self) -> TestTreeResult {
        let fake_output = WlcView::root().as_output();
        self.0.add_output(fake_output)?;
        Ok(self)
    }

    /// Switches to a workspace, which is the default workspace
    /// for all the subsequent operations.
    ///
    /// NOTE If you don't add anything to this workspace, it will be
    /// removed if you switch to another one.
    pub fn switch_to_workspace(mut self, name: &str) -> TestTreeResult {
        self.0.switch_to_workspace(name)?;
        assert_eq!(self.0.current_workspace()?, name);
        Ok(self)
    }

    /// Sets the active container to the given layout.
    pub fn set_active_layout(mut self, layout: Layout) -> TestTreeResult {
        self.0.set_active_layout(layout)?;
        Ok(self)
    }

    /// Adds a view, and sets it as the active container
    pub fn add_view(mut self) -> TestTreeResult {
        let fake_view = WlcView::root();
        self.0.add_view(fake_view)?;
        Ok(self)
    }

    /// Focuses on the `Container` associated with the UUID.
    ///
    /// If the container is floating, it is not set to be the active container,
    /// per the spec.
    pub fn focus_on(mut self, id: Uuid) -> TestTreeResult {
        self.0.focus(id)?;
        Ok(self)
    }
}

impl Deref for UnitTestTree {
    type Target = Tree;

    fn deref(&self) -> &Tree {
        &self.0
    }
}

impl DerefMut for UnitTestTree {
    fn deref_mut(&mut self) -> &mut Tree {
        &mut self.0
    }
}

/// Makes a very basic tree. This is sufficient for basic tests.
/// There is only one output,
/// Two workspaces,
/// First workspace has a single view in the root container,
/// second workspace has a container with two views in it
/// (the container is a child of the root container).
///
/// The active container is the only view in the first workspace
pub fn basic_tree() -> UnitTestTree {
    UnitTestTree::new()
        .add_output().unwrap()
        .switch_to_workspace("1").unwrap()
        .add_view().unwrap()
        .switch_to_workspace("2").unwrap()
        .add_view().unwrap()
        .set_active_layout(Layout::Horizontal).unwrap()
        .add_view().unwrap()
        .switch_to_workspace("1").unwrap()
}

<commit_msg>distinguished UnitTestTree methods from Tree funcs<commit_after>#![allow(dead_code)]
use std::ops::{Deref, DerefMut};

use uuid::Uuid;
use rustwlc::WlcView;

use ::layout::{Tree, Layout, TreeError};

type TestTreeResult = Result<UnitTestTree, TreeError>;

/// A tree that has some utility methods to aid in creating unit tests.
#[derive(Debug)]
pub struct UnitTestTree(Tree);

#[allow(dead_code)]
impl UnitTestTree {
    pub fn new() -> Self {
        UnitTestTree (
            Tree::new()
        )
    }

    /// Attempts to add an output to the tree.
    pub fn add_new_output(mut self) -> TestTreeResult {
        let fake_output = WlcView::root().as_output();
        self.0.add_output(fake_output)?;
        Ok(self)
    }

    /// Switches to a workspace, which is the default workspace
    /// for all the subsequent operations.
    ///
    /// NOTE If you don't add anything to this workspace, it will be
    /// removed if you switch to another one.
    pub fn add_workspace(mut self, name: &str) -> TestTreeResult {
        self.0.switch_to_workspace(name)?;
        assert_eq!(self.0.current_workspace()?, name);
        Ok(self)
    }

    /// Sets the active container to the given layout.
    pub fn set_layout(mut self, layout: Layout) -> TestTreeResult {
        self.0.set_active_layout(layout)?;
        Ok(self)
    }

    /// Adds a view, and sets it as the active container
    pub fn add_new_view(mut self) -> TestTreeResult {
        let fake_view = WlcView::root();
        self.0.add_view(fake_view)?;
        Ok(self)
    }

    /// Focuses on the `Container` associated with the UUID.
    ///
    /// If the container is floating, it is not set to be the active container,
    /// per the spec.
    pub fn focus_on(mut self, id: Uuid) -> TestTreeResult {
        self.0.focus(id)?;
        Ok(self)
    }
}

impl Deref for UnitTestTree {
    type Target = Tree;

    fn deref(&self) -> &Tree {
        &self.0
    }
}

impl DerefMut for UnitTestTree {
    fn deref_mut(&mut self) -> &mut Tree {
        &mut self.0
    }
}

/// Makes a very basic tree. This is sufficient for basic tests.
/// There is only one output,
/// Two workspaces,
/// First workspace has a single view in the root container,
/// second workspace has a container with two views in it
/// (the container is a child of the root container).
///
/// The active container is the only view in the first workspace
pub fn basic_tree() -> UnitTestTree {
    UnitTestTree::new()
        .add_new_output().unwrap()
        .add_workspace("1").unwrap()
        .add_new_view().unwrap()
        .add_workspace("2").unwrap()
        .add_new_view().unwrap()
        .set_layout(Layout::Horizontal).unwrap()
        .add_new_view().unwrap()
        .add_workspace("1").unwrap()
}

<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// NOTE: https://www.chromium.org/directwrite-font-proxy has useful
// information for an approach that we'll likely need to take when the
// renderer moves to a sandboxed process.

use app_units::Au;
use dwrote;
use dwrote::{Font, FontFace, FontFile};
use dwrote::{FontWeight, FontStretch, FontStyle};
use font::{FontHandleMethods, FontMetrics, FontTableMethods};
use font::{FontTableTag, FractionalPixel};
use platform::font_template::FontTemplateData;
use platform::windows::font_context::FontContextHandle;
use platform::windows::font_list::font_from_atom;
use servo_atoms::Atom;
use std::sync::Arc;
use style::computed_values::font_stretch::T as StyleFontStretch;
use style::computed_values::font_weight::T as StyleFontWeight;
use style::values::computed::font::FontStyle as StyleFontStyle;
use style::values::generics::NonNegative;
use style::values::generics::font::FontStyle as GenericFontStyle;
use style::values::specified::font::FontStretchKeyword;
use text::glyph::GlyphId;
use truetype;

// 1em = 12pt = 16px, assuming 72 points per inch and 96 px per inch
fn pt_to_px(pt: f64) -> f64 {
    pt / 72. * 96.
}
fn em_to_px(em: f64) -> f64 {
    em * 16.
}
fn au_from_em(em: f64) -> Au {
    Au::from_f64_px(em_to_px(em))
}
fn au_from_pt(pt: f64) -> Au {
    Au::from_f64_px(pt_to_px(pt))
}

pub struct FontTable {
    data: Vec<u8>,
}

impl FontTable {
    pub fn wrap(data: &[u8]) -> FontTable {
        FontTable {
            data: data.to_vec(),
        }
    }
}

impl FontTableMethods for FontTable {
    fn buffer(&self) -> &[u8] {
        &self.data
    }
}

fn make_tag(tag_bytes: &[u8]) -> FontTableTag {
    assert_eq!(tag_bytes.len(), 4);
    unsafe { *(tag_bytes.as_ptr() as *const FontTableTag) }
}

macro_rules! try_lossy(($result:expr) => ($result.map_err(|_| (()))?));

// Given a set of records, figure out the string indices for the family and face
// names.  We want name_id 1 and 2, and we need to use platform_id == 1 and
// language_id == 0 to avoid limitations in the truetype crate.  We *could* just
// do our own parsing here, and use the offset/length data and pull the values out
// ourselves.
fn get_family_face_indices(records: &[truetype::naming_table::Record]) -> Option<(usize, usize)> {
    let mut family_name_index = None;
    let mut face_name_index = None;

    for i in 0..records.len() {
        // the truetype crate can only decode mac platform format names
        if records[i].platform_id != 1 {
            continue;
        }

        if records[i].language_id != 0 {
            continue;
        }

        if records[i].name_id == 1 {
            family_name_index = Some(i);
        } else if records[i].name_id == 2 {
            face_name_index = Some(i);
        }
    }

    if family_name_index.is_some() && face_name_index.is_some() {
        Some((family_name_index.unwrap(), face_name_index.unwrap()))
    } else {
        None
    }
}

// We need the font (DWriteFont) in order to be able to query things like
// the family name, face name, weight, etc.  On Windows 10, the
// DWriteFontFace3 interface provides this on the FontFace, but that's only
// available on Win10+.
//
// Instead, we do the parsing work using the truetype crate for raw fonts.
// We're just extracting basic info, so this is sufficient for now.

#[derive(Debug)]
struct FontInfo {
    family_name: String,
    face_name: String,
    weight: StyleFontWeight,
    stretch: StyleFontStretch,
    style: StyleFontStyle,
}

impl FontInfo {
    fn new_from_face(face: &FontFace) -> Result<FontInfo, ()> {
        use std::cmp::{min, max};
        use std::io::Cursor;
        use truetype::{NamingTable, Value, WindowsMetrics};

        let name_table_bytes = face.get_font_table(make_tag(b"name"));
        let os2_table_bytes = face.get_font_table(make_tag(b"OS/2"));
        if name_table_bytes.is_none() || os2_table_bytes.is_none() {
            return Err(());
        }

        let mut name_table_cursor = Cursor::new(name_table_bytes.as_ref().unwrap());
        let names = try_lossy!(NamingTable::read(&mut name_table_cursor));
        let (family, face) = match names {
            NamingTable::Format0(ref table) => {
                if let Some((family_index, face_index)) = get_family_face_indices(&table.records) {
                    let strings = table.strings().unwrap();
                    let family = strings[family_index].clone();
                    let face = strings[face_index].clone();
                    ((family, face))
                } else {
                    return Err(());
                }
            },
            NamingTable::Format1(ref table) => {
                if let Some((family_index, face_index)) = get_family_face_indices(&table.records) {
                    let strings = table.strings().unwrap();
                    let family = strings[family_index].clone();
                    let face = strings[face_index].clone();
                    ((family, face))
                } else {
                    return Err(());
                }
            },
        };

        let mut os2_table_cursor = Cursor::new(os2_table_bytes.as_ref().unwrap());
        let metrics = try_lossy!(WindowsMetrics::read(&mut os2_table_cursor));
        let (weight_val, width_val, italic_bool) = match metrics {
            WindowsMetrics::Version0(ref m) => {
                (m.weight_class, m.width_class, m.selection_flags.0 & 1 == 1)
            },
            WindowsMetrics::Version1(ref m) => {
                (m.weight_class, m.width_class, m.selection_flags.0 & 1 == 1)
            },
            WindowsMetrics::Version2(ref m) |
            WindowsMetrics::Version3(ref m) |
            WindowsMetrics::Version4(ref m) => {
                (m.weight_class, m.width_class, m.selection_flags.0 & 1 == 1)
            },
            WindowsMetrics::Version5(ref m) => {
                (m.weight_class, m.width_class, m.selection_flags.0 & 1 == 1)
            },
        };

        let weight = StyleFontWeight(weight_val as f32);

        let stretch = StyleFontStretch(NonNegative(
            match min(9, max(1, width_val)) {
                1 => FontStretchKeyword::UltraCondensed,
                2 => FontStretchKeyword::ExtraCondensed,
                3 => FontStretchKeyword::Condensed,
                4 => FontStretchKeyword::SemiCondensed,
                5 => FontStretchKeyword::Normal,
                6 => FontStretchKeyword::SemiExpanded,
                7 => FontStretchKeyword::Expanded,
                8 => FontStretchKeyword::ExtraExpanded,
                9 => FontStretchKeyword::UltraExpanded,
                _ => return Err(()),
            }.compute(),
        ));

        let style = if italic_bool {
            GenericFontStyle::Italic
        } else {
            GenericFontStyle::Normal
        };

        Ok(FontInfo {
            family_name: family,
            face_name: face,
            weight,
            stretch,
            style,
        })
    }

    fn new_from_font(font: &Font) -> Result<FontInfo, ()> {
        let style = match font.style() {
            FontStyle::Normal => GenericFontStyle::Normal,
            FontStyle::Oblique => GenericFontStyle::Oblique(StyleFontStyle::default_angle()),
            FontStyle::Italic => GenericFontStyle::Italic,
        };
        let weight = StyleFontWeight(font.weight().to_u32() as f32);
        let stretch = StyleFontStretch(NonNegative(
            match font.stretch() {
                FontStretch::Undefined => FontStretchKeyword::Normal,
                FontStretch::UltraCondensed => FontStretchKeyword::UltraCondensed,
                FontStretch::ExtraCondensed => FontStretchKeyword::ExtraCondensed,
                FontStretch::Condensed => FontStretchKeyword::Condensed,
                FontStretch::SemiCondensed => FontStretchKeyword::SemiCondensed,
                FontStretch::Normal => FontStretchKeyword::Normal,
                FontStretch::SemiExpanded => FontStretchKeyword::SemiExpanded,
                FontStretch::Expanded => FontStretchKeyword::Expanded,
                FontStretch::ExtraExpanded => FontStretchKeyword::ExtraExpanded,
                FontStretch::UltraExpanded => FontStretchKeyword::UltraExpanded,
            }.compute(),
        ));

        Ok(FontInfo {
            family_name: font.family_name(),
            face_name: font.face_name(),
            style,
            weight,
            stretch,
        })
    }
}

#[derive(Debug)]
pub struct FontHandle {
    font_data: Arc<FontTemplateData>,
    face: FontFace,
    info: FontInfo,
    em_size: f32,
    du_per_em: f32,
    du_to_px: f32,
    scaled_du_to_px: f32,
}

impl FontHandle {}

impl FontHandleMethods for FontHandle {
    fn new_from_template(
        _: &FontContextHandle,
        template: Arc<FontTemplateData>,
        pt_size: Option<Au>,
    ) -> Result<Self, ()> {
        let (info, face) = if let Some(ref raw_font) = template.bytes {
            let font_file = FontFile::new_from_data(&raw_font);
            if font_file.is_none() {
                // failed to load raw font
                return Err(());
            }

            let face = font_file
                .unwrap()
                .create_face(0, dwrote::DWRITE_FONT_SIMULATIONS_NONE);
            let info = FontInfo::new_from_face(&face)?;
            (info, face)
        } else {
            let font = font_from_atom(&template.identifier);
            let face = font.create_font_face();
            let info = FontInfo::new_from_font(&font)?;
            (info, face)
        };

        let pt_size = pt_size.unwrap_or(au_from_pt(12.));
        let du_per_em = face.metrics().designUnitsPerEm as f32;

        let em_size = pt_size.to_f32_px() / 16.;
        let design_units_per_pixel = du_per_em / 16.;

        let design_units_to_pixels = 1. / design_units_per_pixel;
        let scaled_design_units_to_pixels = em_size / design_units_per_pixel;

        Ok(FontHandle {
            font_data: template.clone(),
            face: face,
            info: info,
            em_size: em_size,
            du_per_em: du_per_em,
            du_to_px: design_units_to_pixels,
            scaled_du_to_px: scaled_design_units_to_pixels,
        })
    }

    fn template(&self) -> Arc<FontTemplateData> {
        self.font_data.clone()
    }

    fn family_name(&self) -> Option<String> {
        Some(self.info.family_name.clone())
    }

    fn face_name(&self) -> Option<String> {
        Some(self.info.face_name.clone())
    }

    fn style(&self) -> StyleFontStyle {
        self.info.style
    }

    fn boldness(&self) -> StyleFontWeight {
        self.info.weight
    }

    fn stretchiness(&self) -> StyleFontStretch {
        self.info.stretch
    }

    fn glyph_index(&self, codepoint: char) -> Option<GlyphId> {
        let glyph = self.face.get_glyph_indices(&[codepoint as u32])[0];
        if glyph == 0 {
            return None;
        }
        Some(glyph as GlyphId)
    }

    fn glyph_h_advance(&self, glyph: GlyphId) -> Option<FractionalPixel> {
        if glyph == 0 {
            return None;
        }

        let gm = self.face.get_design_glyph_metrics(&[glyph as u16], false)[0];
        let f = (gm.advanceWidth as f32 * self.scaled_du_to_px) as FractionalPixel;

        Some(f)
    }

    /// Can this font do basic horizontal LTR shaping without Harfbuzz?
    fn can_do_fast_shaping(&self) -> bool {
        // TODO copy CachedKernTable from the MacOS X implementation to
        // somehwere global and use it here.  We could also implement the
        // IDirectWriteFontFace1 interface and use the glyph kerning pair
        // methods there.
        false
    }

    fn glyph_h_kerning(&self, _: GlyphId, _: GlyphId) -> FractionalPixel {
        0.0
    }

    fn metrics(&self) -> FontMetrics {
        let dm = self.face.metrics();

        let au_from_du = |du| -> Au { Au::from_f32_px(du as f32 * self.du_to_px) };
        let au_from_du_s = |du| -> Au { Au::from_f32_px(du as f32 * self.scaled_du_to_px) };

        // anything that we calculate and don't just pull out of self.face.metrics
        // is pulled out here for clarity
        let leading = dm.ascent - dm.capHeight;

        let metrics = FontMetrics {
            underline_size: au_from_du(dm.underlineThickness as i32),
            underline_offset: au_from_du_s(dm.underlinePosition as i32),
            strikeout_size: au_from_du(dm.strikethroughThickness as i32),
            strikeout_offset: au_from_du_s(dm.strikethroughPosition as i32),
            leading: au_from_du_s(leading as i32),
            x_height: au_from_du_s(dm.xHeight as i32),
            em_size: au_from_em(self.em_size as f64),
            ascent: au_from_du_s(dm.ascent as i32),
            descent: au_from_du_s(dm.descent as i32),
            max_advance: au_from_pt(0.0),     // FIXME
            average_advance: au_from_pt(0.0), // FIXME
            line_gap: au_from_du_s((dm.ascent + dm.descent + dm.lineGap as u16) as i32),
        };
        debug!("Font metrics (@{} pt): {:?}", self.em_size * 12., metrics);
        metrics
    }

    fn table_for_tag(&self, tag: FontTableTag) -> Option<FontTable> {
        self.face
            .get_font_table(tag)
            .map(|bytes| FontTable { data: bytes })
    }

    fn identifier(&self) -> Atom {
        self.font_data.identifier.clone()
    }
}
<commit_msg>Fix windows build errors.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

// NOTE: https://www.chromium.org/directwrite-font-proxy has useful
// information for an approach that we'll likely need to take when the
// renderer moves to a sandboxed process.

use app_units::Au;
use dwrote;
use dwrote::{Font, FontFace, FontFile};
use dwrote::{FontStretch, FontStyle};
use font::{FontHandleMethods, FontMetrics, FontTableMethods};
use font::{FontTableTag, FractionalPixel};
use platform::font_template::FontTemplateData;
use platform::windows::font_context::FontContextHandle;
use platform::windows::font_list::font_from_atom;
use servo_atoms::Atom;
use std::fmt;
use std::ops::Deref;
use std::sync::Arc;
use style::computed_values::font_stretch::T as StyleFontStretch;
use style::computed_values::font_weight::T as StyleFontWeight;
use style::values::computed::font::FontStyle as StyleFontStyle;
use style::values::generics::NonNegative;
use style::values::generics::font::FontStyle as GenericFontStyle;
use style::values::specified::font::FontStretchKeyword;
use text::glyph::GlyphId;
use truetype;

// 1em = 12pt = 16px, assuming 72 points per inch and 96 px per inch
fn pt_to_px(pt: f64) -> f64 {
    pt / 72. * 96.
}
fn em_to_px(em: f64) -> f64 {
    em * 16.
}
fn au_from_em(em: f64) -> Au {
    Au::from_f64_px(em_to_px(em))
}
fn au_from_pt(pt: f64) -> Au {
    Au::from_f64_px(pt_to_px(pt))
}

pub struct FontTable {
    data: Vec<u8>,
}

impl FontTable {
    pub fn wrap(data: &[u8]) -> FontTable {
        FontTable {
            data: data.to_vec(),
        }
    }
}

impl FontTableMethods for FontTable {
    fn buffer(&self) -> &[u8] {
        &self.data
    }
}

fn make_tag(tag_bytes: &[u8]) -> FontTableTag {
    assert_eq!(tag_bytes.len(), 4);
    unsafe { *(tag_bytes.as_ptr() as *const FontTableTag) }
}

macro_rules! try_lossy(($result:expr) => ($result.map_err(|_| (()))?));

// Given a set of records, figure out the string indices for the family and face
// names.  We want name_id 1 and 2, and we need to use platform_id == 1 and
// language_id == 0 to avoid limitations in the truetype crate.  We *could* just
// do our own parsing here, and use the offset/length data and pull the values out
// ourselves.
fn get_family_face_indices(records: &[truetype::naming_table::Record]) -> Option<(usize, usize)> {
    let mut family_name_index = None;
    let mut face_name_index = None;

    for i in 0..records.len() {
        // the truetype crate can only decode mac platform format names
        if records[i].platform_id != 1 {
            continue;
        }

        if records[i].language_id != 0 {
            continue;
        }

        if records[i].name_id == 1 {
            family_name_index = Some(i);
        } else if records[i].name_id == 2 {
            face_name_index = Some(i);
        }
    }

    if family_name_index.is_some() && face_name_index.is_some() {
        Some((family_name_index.unwrap(), face_name_index.unwrap()))
    } else {
        None
    }
}

// We need the font (DWriteFont) in order to be able to query things like
// the family name, face name, weight, etc.  On Windows 10, the
// DWriteFontFace3 interface provides this on the FontFace, but that's only
// available on Win10+.
//
// Instead, we do the parsing work using the truetype crate for raw fonts.
// We're just extracting basic info, so this is sufficient for now.

#[derive(Debug)]
struct FontInfo {
    family_name: String,
    face_name: String,
    weight: StyleFontWeight,
    stretch: StyleFontStretch,
    style: StyleFontStyle,
}

impl FontInfo {
    fn new_from_face(face: &FontFace) -> Result<FontInfo, ()> {
        use std::cmp::{min, max};
        use std::io::Cursor;
        use truetype::{NamingTable, Value, WindowsMetrics};

        let name_table_bytes = face.get_font_table(make_tag(b"name"));
        let os2_table_bytes = face.get_font_table(make_tag(b"OS/2"));
        if name_table_bytes.is_none() || os2_table_bytes.is_none() {
            return Err(());
        }

        let mut name_table_cursor = Cursor::new(name_table_bytes.as_ref().unwrap());
        let names = try_lossy!(NamingTable::read(&mut name_table_cursor));
        let (family, face) = match names {
            NamingTable::Format0(ref table) => {
                if let Some((family_index, face_index)) = get_family_face_indices(&table.records) {
                    let strings = table.strings().unwrap();
                    let family = strings[family_index].clone();
                    let face = strings[face_index].clone();
                    ((family, face))
                } else {
                    return Err(());
                }
            },
            NamingTable::Format1(ref table) => {
                if let Some((family_index, face_index)) = get_family_face_indices(&table.records) {
                    let strings = table.strings().unwrap();
                    let family = strings[family_index].clone();
                    let face = strings[face_index].clone();
                    ((family, face))
                } else {
                    return Err(());
                }
            },
        };

        let mut os2_table_cursor = Cursor::new(os2_table_bytes.as_ref().unwrap());
        let metrics = try_lossy!(WindowsMetrics::read(&mut os2_table_cursor));
        let (weight_val, width_val, italic_bool) = match metrics {
            WindowsMetrics::Version0(ref m) => {
                (m.weight_class, m.width_class, m.selection_flags.0 & 1 == 1)
            },
            WindowsMetrics::Version1(ref m) => {
                (m.weight_class, m.width_class, m.selection_flags.0 & 1 == 1)
            },
            WindowsMetrics::Version2(ref m) |
            WindowsMetrics::Version3(ref m) |
            WindowsMetrics::Version4(ref m) => {
                (m.weight_class, m.width_class, m.selection_flags.0 & 1 == 1)
            },
            WindowsMetrics::Version5(ref m) => {
                (m.weight_class, m.width_class, m.selection_flags.0 & 1 == 1)
            },
        };

        let weight = StyleFontWeight(weight_val as f32);

        let stretch = StyleFontStretch(NonNegative(
            match min(9, max(1, width_val)) {
                1 => FontStretchKeyword::UltraCondensed,
                2 => FontStretchKeyword::ExtraCondensed,
                3 => FontStretchKeyword::Condensed,
                4 => FontStretchKeyword::SemiCondensed,
                5 => FontStretchKeyword::Normal,
                6 => FontStretchKeyword::SemiExpanded,
                7 => FontStretchKeyword::Expanded,
                8 => FontStretchKeyword::ExtraExpanded,
                9 => FontStretchKeyword::UltraExpanded,
                _ => return Err(()),
            }.compute(),
        ));

        let style = if italic_bool {
            GenericFontStyle::Italic
        } else {
            GenericFontStyle::Normal
        };

        Ok(FontInfo {
            family_name: family,
            face_name: face,
            weight,
            stretch,
            style,
        })
    }

    fn new_from_font(font: &Font) -> Result<FontInfo, ()> {
        let style = match font.style() {
            FontStyle::Normal => GenericFontStyle::Normal,
            FontStyle::Oblique => GenericFontStyle::Oblique(StyleFontStyle::default_angle()),
            FontStyle::Italic => GenericFontStyle::Italic,
        };
        let weight = StyleFontWeight(font.weight().to_u32() as f32);
        let stretch = StyleFontStretch(NonNegative(
            match font.stretch() {
                FontStretch::Undefined => FontStretchKeyword::Normal,
                FontStretch::UltraCondensed => FontStretchKeyword::UltraCondensed,
                FontStretch::ExtraCondensed => FontStretchKeyword::ExtraCondensed,
                FontStretch::Condensed => FontStretchKeyword::Condensed,
                FontStretch::SemiCondensed => FontStretchKeyword::SemiCondensed,
                FontStretch::Normal => FontStretchKeyword::Normal,
                FontStretch::SemiExpanded => FontStretchKeyword::SemiExpanded,
                FontStretch::Expanded => FontStretchKeyword::Expanded,
                FontStretch::ExtraExpanded => FontStretchKeyword::ExtraExpanded,
                FontStretch::UltraExpanded => FontStretchKeyword::UltraExpanded,
            }.compute(),
        ));

        Ok(FontInfo {
            family_name: font.family_name(),
            face_name: font.face_name(),
            style,
            weight,
            stretch,
        })
    }
}

#[derive(Debug)]
pub struct FontHandle {
    font_data: Arc<FontTemplateData>,
    face: Nondebug<FontFace>,
    info: FontInfo,
    em_size: f32,
    du_per_em: f32,
    du_to_px: f32,
    scaled_du_to_px: f32,
}

struct Nondebug<T>(T);

impl<T> fmt::Debug for Nondebug<T> {
    fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result {
        Ok(())
    }
}

impl<T> Deref for Nondebug<T> {
    type Target = T;
    fn deref(&self) -> &T {
        &self.0
    }
}

impl FontHandle {}

impl FontHandleMethods for FontHandle {
    fn new_from_template(
        _: &FontContextHandle,
        template: Arc<FontTemplateData>,
        pt_size: Option<Au>,
    ) -> Result<Self, ()> {
        let (info, face) = if let Some(ref raw_font) = template.bytes {
            let font_file = FontFile::new_from_data(&raw_font);
            if font_file.is_none() {
                // failed to load raw font
                return Err(());
            }

            let face = font_file
                .unwrap()
                .create_face(0, dwrote::DWRITE_FONT_SIMULATIONS_NONE);
            let info = FontInfo::new_from_face(&face)?;
            (info, face)
        } else {
            let font = font_from_atom(&template.identifier);
            let face = font.create_font_face();
            let info = FontInfo::new_from_font(&font)?;
            (info, face)
        };

        let pt_size = pt_size.unwrap_or(au_from_pt(12.));
        let du_per_em = face.metrics().designUnitsPerEm as f32;

        let em_size = pt_size.to_f32_px() / 16.;
        let design_units_per_pixel = du_per_em / 16.;

        let design_units_to_pixels = 1. / design_units_per_pixel;
        let scaled_design_units_to_pixels = em_size / design_units_per_pixel;

        Ok(FontHandle {
            font_data: template.clone(),
            face: Nondebug(face),
            info: info,
            em_size: em_size,
            du_per_em: du_per_em,
            du_to_px: design_units_to_pixels,
            scaled_du_to_px: scaled_design_units_to_pixels,
        })
    }

    fn template(&self) -> Arc<FontTemplateData> {
        self.font_data.clone()
    }

    fn family_name(&self) -> Option<String> {
        Some(self.info.family_name.clone())
    }

    fn face_name(&self) -> Option<String> {
        Some(self.info.face_name.clone())
    }

    fn style(&self) -> StyleFontStyle {
        self.info.style
    }

    fn boldness(&self) -> StyleFontWeight {
        self.info.weight
    }

    fn stretchiness(&self) -> StyleFontStretch {
        self.info.stretch
    }

    fn glyph_index(&self, codepoint: char) -> Option<GlyphId> {
        let glyph = self.face.get_glyph_indices(&[codepoint as u32])[0];
        if glyph == 0 {
            return None;
        }
        Some(glyph as GlyphId)
    }

    fn glyph_h_advance(&self, glyph: GlyphId) -> Option<FractionalPixel> {
        if glyph == 0 {
            return None;
        }

        let gm = self.face.get_design_glyph_metrics(&[glyph as u16], false)[0];
        let f = (gm.advanceWidth as f32 * self.scaled_du_to_px) as FractionalPixel;

        Some(f)
    }

    /// Can this font do basic horizontal LTR shaping without Harfbuzz?
    fn can_do_fast_shaping(&self) -> bool {
        // TODO copy CachedKernTable from the MacOS X implementation to
        // somehwere global and use it here.  We could also implement the
        // IDirectWriteFontFace1 interface and use the glyph kerning pair
        // methods there.
        false
    }

    fn glyph_h_kerning(&self, _: GlyphId, _: GlyphId) -> FractionalPixel {
        0.0
    }

    fn metrics(&self) -> FontMetrics {
        let dm = self.face.metrics();

        let au_from_du = |du| -> Au { Au::from_f32_px(du as f32 * self.du_to_px) };
        let au_from_du_s = |du| -> Au { Au::from_f32_px(du as f32 * self.scaled_du_to_px) };

        // anything that we calculate and don't just pull out of self.face.metrics
        // is pulled out here for clarity
        let leading = dm.ascent - dm.capHeight;

        let metrics = FontMetrics {
            underline_size: au_from_du(dm.underlineThickness as i32),
            underline_offset: au_from_du_s(dm.underlinePosition as i32),
            strikeout_size: au_from_du(dm.strikethroughThickness as i32),
            strikeout_offset: au_from_du_s(dm.strikethroughPosition as i32),
            leading: au_from_du_s(leading as i32),
            x_height: au_from_du_s(dm.xHeight as i32),
            em_size: au_from_em(self.em_size as f64),
            ascent: au_from_du_s(dm.ascent as i32),
            descent: au_from_du_s(dm.descent as i32),
            max_advance: au_from_pt(0.0),     // FIXME
            average_advance: au_from_pt(0.0), // FIXME
            line_gap: au_from_du_s((dm.ascent + dm.descent + dm.lineGap as u16) as i32),
        };
        debug!("Font metrics (@{} pt): {:?}", self.em_size * 12., metrics);
        metrics
    }

    fn table_for_tag(&self, tag: FontTableTag) -> Option<FontTable> {
        self.face
            .get_font_table(tag)
            .map(|bytes| FontTable { data: bytes })
    }

    fn identifier(&self) -> Atom {
        self.font_data.identifier.clone()
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;

use library::*;
use parser::is_empty_c_type;

impl Namespace {
    fn unresolved(&self) -> Vec<&str> {
        self.index
            .iter()
            .filter_map(|(name, &id)| if self.types[id as usize].is_none() {
                Some(&name[..])
            } else {
                None
            })
            .collect()
    }
}

type DetectedCTypes = HashMap<TypeId, String>;

impl Library {
    pub fn postprocessing(&mut self) {
        self.fix_gtype();
        self.check_resolved();
        self.fill_empty_signals_c_types();
        self.resolve_class_structs();
        self.correlate_class_structs();
    }

    fn fix_gtype(&mut self) {
        if let Some(ns_id) = self.find_namespace("GObject") {
            // hide the `GType` type alias in `GObject`
            self.add_type(ns_id, "Type", Type::Fundamental(Fundamental::Unsupported));
        }
    }

    fn check_resolved(&self) {
        let list: Vec<_> = self.index
            .iter()
            .flat_map(|(name, &id)| {
                let name = name.clone();
                self.namespace(id)
                    .unresolved()
                    .into_iter()
                    .map(move |s| format!("{}.{}", name, s))
            })
            .collect();

        if !list.is_empty() {
            panic!("Incomplete library, unresolved: {:?}", list);
        }
    }

    fn fill_empty_signals_c_types(&mut self) {
        let mut tids = Vec::new();
        let mut c_types = DetectedCTypes::new();
        for (ns_id, ns) in self.namespaces.iter().enumerate() {
            for (id, type_) in ns.types.iter().enumerate() {
                let type_ = type_.as_ref().unwrap(); //Always contains something
                let tid = TypeId {
                    ns_id: ns_id as u16,
                    id: id as u32,
                };
                match *type_ {
                    Type::Class(ref klass) => {
                        if self.detect_empty_signals_c_types(&klass.signals, &mut c_types) {
                            tids.push(tid);
                        }
                    }
                    Type::Interface(ref iface) => {
                        if self.detect_empty_signals_c_types(&iface.signals, &mut c_types) {
                            tids.push(tid);
                        }
                    }
                    _ => (),
                }
            }
        }

        fn update_empty_signals_c_types(signals: &mut [Signal], c_types: &DetectedCTypes) {
            for signal in signals {
                update_empty_signal_c_types(signal, c_types);
            }
        }

        fn update_empty_signal_c_types(signal: &mut Signal, c_types: &DetectedCTypes) {
            for par in &mut signal.parameters {
                update_empty_c_type(&mut par.c_type, par.typ, c_types);
            }
            update_empty_c_type(&mut signal.ret.c_type, signal.ret.typ, c_types);
        }

        fn update_empty_c_type(c_type: &mut String, tid: TypeId, c_types: &DetectedCTypes) {
            if !is_empty_c_type(c_type) {
                return;
            }
            if let Some(ref mut s) = c_types.get(&tid) {
                *c_type = s.clone();
            }
        }

        for tid in tids {
            match *self.type_mut(tid) {
                Type::Class(ref mut klass) => {
                    update_empty_signals_c_types(&mut klass.signals, &c_types)
                }
                Type::Interface(ref mut iface) => {
                    update_empty_signals_c_types(&mut iface.signals, &c_types)
                }
                _ => (),
            }
        }
    }

    fn detect_empty_signals_c_types(
        &self,
        signals: &[Signal],
        c_types: &mut DetectedCTypes,
    ) -> bool {
        let mut detected = false;
        for signal in signals {
            if self.detect_empty_signal_c_types(signal, c_types) {
                detected = true;
            }
        }
        detected
    }

    fn detect_empty_signal_c_types(&self, signal: &Signal, c_types: &mut DetectedCTypes) -> bool {
        let mut detected = false;
        for par in &signal.parameters {
            if self.detect_empty_c_type(&par.c_type, par.typ, c_types) {
                detected = true;
            }
        }
        if self.detect_empty_c_type(&signal.ret.c_type, signal.ret.typ, c_types) {
            detected = true;
        }
        detected
    }

    fn detect_empty_c_type(&self, c_type: &str, tid: TypeId, c_types: &mut DetectedCTypes) -> bool {
        if !is_empty_c_type(c_type) {
            return false;
        }
        if !c_types.contains_key(&tid) {
            if let Some(detected_c_type) = self.c_type_by_type_id(tid) {
                c_types.insert(tid, detected_c_type);
            }
        }
        true
    }

    fn c_type_by_type_id(&self, tid: TypeId) -> Option<String> {
        let type_ = self.type_(tid);
        type_.get_glib_name().map(
            |glib_name| if self.is_referenced_type(type_) {
                format!("{}*", glib_name)
            } else {
                glib_name.to_string()
            },
        )
    }

    fn is_referenced_type(&self, type_: &Type) -> bool {
        use library::Type::*;
        match *type_ {
            Alias(ref alias) => self.is_referenced_type(self.type_(alias.typ)),
            Record(..) | Union(..) | Class(..) | Interface(..) => true,
            _ => false,
        }
    }

    fn resolve_class_structs(&mut self) {
        // stores pairs of (gtype-struct-c-name, type-name)
        let mut structs_and_types = Vec::new();

        for (ns_id, ns) in self.namespaces.iter().enumerate() {
            for type_ in ns.types.iter() {
                let type_ = type_.as_ref().unwrap(); //Always contains something

                if let Type::Record(ref record) = *type_ {
                    if let Some(ref struct_for) = record.gtype_struct_for {
                        if let Some(struct_for_tid) = self.find_type(ns_id as u16, struct_for) {
                            structs_and_types.push ((record.c_type.clone(), struct_for_tid));
                        }
                    }
                }
            }
        }

        for (gtype_struct_c_type, struct_for_tid) in structs_and_types {
            match *self.type_mut(struct_for_tid) {
                Type::Class(ref mut klass) => {
                    klass.c_class_type = Some(gtype_struct_c_type);
                },

                Type::Interface(ref mut iface) => {
                    iface.c_class_type = Some(gtype_struct_c_type);
                },

                ref x @ _ => unreachable!("Something other than a class or interface has a class struct: {:?}", x)
            }
        }
    }

    fn correlate_class_structs(&self) {
        for (ns_id, ns) in self.namespaces.iter().enumerate() {
            for type_ in ns.types.iter() {
                let type_ = type_.as_ref().unwrap(); //Always contains something
                let mut name;
                let mut type_struct;
                let mut c_class_type;

                match *type_ {
                    Type::Class(ref klass) => {
                        name         = &klass.name;
                        type_struct  = &klass.type_struct;
                        c_class_type = &klass.c_class_type;
                    },

                    Type::Interface(ref iface) => {
                        name         = &iface.name;
                        type_struct  = &iface.type_struct;
                        c_class_type = &iface.c_class_type;
                    },

                    _ => { continue; }
                }

                if let &Some(ref type_struct) = type_struct {
                    let type_struct_tid = self.find_type(ns_id as u16, type_struct);
                    if type_struct_tid.is_none() {
                        panic!("\"{}\" has glib:type-struct=\"{}\" but there is no such record",
                               name,
                               type_struct);
                    }

                    let type_struct_type = self.type_(type_struct_tid.unwrap());

                    if let Type::Record(ref r) = *type_struct_type {
                        if r.gtype_struct_for.as_ref() != Some(name) {
                            if let Some(ref gtype_struct_for) = r.gtype_struct_for {
                                panic!("\"{}\" has glib:type-struct=\"{}\" but the corresponding record \"{}\" has glib:is-gtype-struct-for={:?}",
                                       name,
                                       type_struct,
                                       r.name,
                                       gtype_struct_for);
                            } else {
                                panic!("\"{}\" has glib:type-struct=\"{}\" but the corresponding record \"{}\" has no glib:is-gtype-struct-for attribute",
                                       name,
                                       type_struct,
                                       r.name);
                            }
                        }
                    } else {
                        panic!("Element with name=\"{}\" should be a record but it isn't",
                               type_struct);
                    }
                } else if let Some(ref c) = *c_class_type {
                    panic!("\"{}\" has no glib:type-struct but there is an element with glib:is-gtype-struct-for=\"{}\"",
                           name,
                           c);
                }
                // else both type_struct and c_class_type are None,
                // and that's fine because they don't reference each
                // other.
            }
        }
    }
}
<commit_msg>Stylistic fix<commit_after>use std::collections::HashMap;

use library::*;
use parser::is_empty_c_type;

impl Namespace {
    fn unresolved(&self) -> Vec<&str> {
        self.index
            .iter()
            .filter_map(|(name, &id)| if self.types[id as usize].is_none() {
                Some(&name[..])
            } else {
                None
            })
            .collect()
    }
}

type DetectedCTypes = HashMap<TypeId, String>;

impl Library {
    pub fn postprocessing(&mut self) {
        self.fix_gtype();
        self.check_resolved();
        self.fill_empty_signals_c_types();
        self.resolve_class_structs();
        self.correlate_class_structs();
    }

    fn fix_gtype(&mut self) {
        if let Some(ns_id) = self.find_namespace("GObject") {
            // hide the `GType` type alias in `GObject`
            self.add_type(ns_id, "Type", Type::Fundamental(Fundamental::Unsupported));
        }
    }

    fn check_resolved(&self) {
        let list: Vec<_> = self.index
            .iter()
            .flat_map(|(name, &id)| {
                let name = name.clone();
                self.namespace(id)
                    .unresolved()
                    .into_iter()
                    .map(move |s| format!("{}.{}", name, s))
            })
            .collect();

        if !list.is_empty() {
            panic!("Incomplete library, unresolved: {:?}", list);
        }
    }

    fn fill_empty_signals_c_types(&mut self) {
        let mut tids = Vec::new();
        let mut c_types = DetectedCTypes::new();
        for (ns_id, ns) in self.namespaces.iter().enumerate() {
            for (id, type_) in ns.types.iter().enumerate() {
                let type_ = type_.as_ref().unwrap(); //Always contains something
                let tid = TypeId {
                    ns_id: ns_id as u16,
                    id: id as u32,
                };
                match *type_ {
                    Type::Class(ref klass) => {
                        if self.detect_empty_signals_c_types(&klass.signals, &mut c_types) {
                            tids.push(tid);
                        }
                    }
                    Type::Interface(ref iface) => {
                        if self.detect_empty_signals_c_types(&iface.signals, &mut c_types) {
                            tids.push(tid);
                        }
                    }
                    _ => (),
                }
            }
        }

        fn update_empty_signals_c_types(signals: &mut [Signal], c_types: &DetectedCTypes) {
            for signal in signals {
                update_empty_signal_c_types(signal, c_types);
            }
        }

        fn update_empty_signal_c_types(signal: &mut Signal, c_types: &DetectedCTypes) {
            for par in &mut signal.parameters {
                update_empty_c_type(&mut par.c_type, par.typ, c_types);
            }
            update_empty_c_type(&mut signal.ret.c_type, signal.ret.typ, c_types);
        }

        fn update_empty_c_type(c_type: &mut String, tid: TypeId, c_types: &DetectedCTypes) {
            if !is_empty_c_type(c_type) {
                return;
            }
            if let Some(ref mut s) = c_types.get(&tid) {
                *c_type = s.clone();
            }
        }

        for tid in tids {
            match *self.type_mut(tid) {
                Type::Class(ref mut klass) => {
                    update_empty_signals_c_types(&mut klass.signals, &c_types)
                }
                Type::Interface(ref mut iface) => {
                    update_empty_signals_c_types(&mut iface.signals, &c_types)
                }
                _ => (),
            }
        }
    }

    fn detect_empty_signals_c_types(
        &self,
        signals: &[Signal],
        c_types: &mut DetectedCTypes,
    ) -> bool {
        let mut detected = false;
        for signal in signals {
            if self.detect_empty_signal_c_types(signal, c_types) {
                detected = true;
            }
        }
        detected
    }

    fn detect_empty_signal_c_types(&self, signal: &Signal, c_types: &mut DetectedCTypes) -> bool {
        let mut detected = false;
        for par in &signal.parameters {
            if self.detect_empty_c_type(&par.c_type, par.typ, c_types) {
                detected = true;
            }
        }
        if self.detect_empty_c_type(&signal.ret.c_type, signal.ret.typ, c_types) {
            detected = true;
        }
        detected
    }

    fn detect_empty_c_type(&self, c_type: &str, tid: TypeId, c_types: &mut DetectedCTypes) -> bool {
        if !is_empty_c_type(c_type) {
            return false;
        }
        if !c_types.contains_key(&tid) {
            if let Some(detected_c_type) = self.c_type_by_type_id(tid) {
                c_types.insert(tid, detected_c_type);
            }
        }
        true
    }

    fn c_type_by_type_id(&self, tid: TypeId) -> Option<String> {
        let type_ = self.type_(tid);
        type_.get_glib_name().map(
            |glib_name| if self.is_referenced_type(type_) {
                format!("{}*", glib_name)
            } else {
                glib_name.to_string()
            },
        )
    }

    fn is_referenced_type(&self, type_: &Type) -> bool {
        use library::Type::*;
        match *type_ {
            Alias(ref alias) => self.is_referenced_type(self.type_(alias.typ)),
            Record(..) | Union(..) | Class(..) | Interface(..) => true,
            _ => false,
        }
    }

    fn resolve_class_structs(&mut self) {
        // stores pairs of (gtype-struct-c-name, type-name)
        let mut structs_and_types = Vec::new();

        for (ns_id, ns) in self.namespaces.iter().enumerate() {
            for type_ in ns.types.iter() {
                let type_ = type_.as_ref().unwrap(); //Always contains something

                if let Type::Record(ref record) = *type_ {
                    if let Some(ref struct_for) = record.gtype_struct_for {
                        if let Some(struct_for_tid) = self.find_type(ns_id as u16, struct_for) {
                            structs_and_types.push ((record.c_type.clone(), struct_for_tid));
                        }
                    }
                }
            }
        }

        for (gtype_struct_c_type, struct_for_tid) in structs_and_types {
            match *self.type_mut(struct_for_tid) {
                Type::Class(ref mut klass) => {
                    klass.c_class_type = Some(gtype_struct_c_type);
                },

                Type::Interface(ref mut iface) => {
                    iface.c_class_type = Some(gtype_struct_c_type);
                },

                ref x @ _ => unreachable!("Something other than a class or interface has a class struct: {:?}", x)
            }
        }
    }

    fn correlate_class_structs(&self) {
        for (ns_id, ns) in self.namespaces.iter().enumerate() {
            for type_ in ns.types.iter() {
                let type_ = type_.as_ref().unwrap(); //Always contains something
                let mut name;
                let mut type_struct;
                let mut c_class_type;

                match *type_ {
                    Type::Class(ref klass) => {
                        name         = &klass.name;
                        type_struct  = &klass.type_struct;
                        c_class_type = &klass.c_class_type;
                    },

                    Type::Interface(ref iface) => {
                        name         = &iface.name;
                        type_struct  = &iface.type_struct;
                        c_class_type = &iface.c_class_type;
                    },

                    _ => { continue; }
                }

                if let Some(ref type_struct) = *type_struct {
                    let type_struct_tid = self.find_type(ns_id as u16, type_struct);
                    if type_struct_tid.is_none() {
                        panic!("\"{}\" has glib:type-struct=\"{}\" but there is no such record",
                               name,
                               type_struct);
                    }

                    let type_struct_type = self.type_(type_struct_tid.unwrap());

                    if let Type::Record(ref r) = *type_struct_type {
                        if r.gtype_struct_for.as_ref() != Some(name) {
                            if let Some(ref gtype_struct_for) = r.gtype_struct_for {
                                panic!("\"{}\" has glib:type-struct=\"{}\" but the corresponding record \"{}\" has glib:is-gtype-struct-for={:?}",
                                       name,
                                       type_struct,
                                       r.name,
                                       gtype_struct_for);
                            } else {
                                panic!("\"{}\" has glib:type-struct=\"{}\" but the corresponding record \"{}\" has no glib:is-gtype-struct-for attribute",
                                       name,
                                       type_struct,
                                       r.name);
                            }
                        }
                    } else {
                        panic!("Element with name=\"{}\" should be a record but it isn't",
                               type_struct);
                    }
                } else if let Some(ref c) = *c_class_type {
                    panic!("\"{}\" has no glib:type-struct but there is an element with glib:is-gtype-struct-for=\"{}\"",
                           name,
                           c);
                }
                // else both type_struct and c_class_type are None,
                // and that's fine because they don't reference each
                // other.
            }
        }
    }
}
<|endoftext|>