<commit_before>#[macro_use]
mod macros;

use proc_macro2::{Delimiter, Group, Ident, Span, TokenStream, TokenTree};
use quote::quote;
use std::iter::FromIterator;
use syn::{Item, ItemTrait};

#[test]
fn test_macro_variable_attr() {
    // mimics the token stream corresponding to `$attr fn f() {}`
    let tokens = TokenStream::from_iter(vec![
        TokenTree::Group(Group::new(Delimiter::None, quote! { #[test] })),
        TokenTree::Ident(Ident::new("fn", Span::call_site())),
        TokenTree::Ident(Ident::new("f", Span::call_site())),
        TokenTree::Group(Group::new(Delimiter::Parenthesis, TokenStream::new())),
        TokenTree::Group(Group::new(Delimiter::Brace, TokenStream::new())),
    ]);

    snapshot!(tokens as Item, @r###"
    Item::Fn {
        attrs: [
            Attribute {
                style: Outer,
                path: Path {
                    segments: [
                        PathSegment {
                            ident: "test",
                            arguments: None,
                        },
                    ],
                },
                tokens: TokenStream(``),
            },
        ],
        vis: Inherited,
        sig: Signature {
            ident: "f",
            generics: Generics,
            output: Default,
        },
        block: Block,
    }
    "###);
}

#[test]
fn test_negative_impl() {
    // Rustc parses all of the following.

    #[cfg(any())]
    impl ! {}
    let tokens = quote! {
        impl ! {}
    };
    snapshot!(tokens as Item, @r###"
    Item::Impl {
        generics: Generics,
        self_ty: Type::Never,
    }
    "###);

    #[cfg(any())]
    #[rustfmt::skip]
    impl !Trait {}
    let tokens = quote! {
        impl !Trait {}
    };
    snapshot!(tokens as Item, @r###"
    Item::Impl {
        generics: Generics,
        self_ty: Verbatim(`! Trait`),
    }
    "###);

    #[cfg(any())]
    impl !Trait for T {}
    let tokens = quote! {
        impl !Trait for T {}
    };
    snapshot!(tokens as Item, @r###"
    Item::Impl {
        generics: Generics,
        trait_: Some((
            Some,
            Path {
                segments: [
                    PathSegment {
                        ident: "Trait",
                        arguments: None,
                    },
                ],
            },
        )),
        self_ty: Type::Path {
            path: Path {
                segments: [
                    PathSegment {
                        ident: "T",
                        arguments: None,
                    },
                ],
            },
        },
    }
    "###);

    #[cfg(any())]
    #[rustfmt::skip]
    impl !! {}
    let tokens = quote! {
        impl !! {}
    };
    snapshot!(tokens as Item, @r###"
    Item::Impl {
        generics: Generics,
        self_ty: Verbatim(`! !`),
    }
    "###);
}

#[test]
fn test_macro_variable_impl() {
    // mimics the token stream corresponding to `impl $trait for $ty {}`
    let tokens = TokenStream::from_iter(vec![
        TokenTree::Ident(Ident::new("impl", Span::call_site())),
        TokenTree::Group(Group::new(Delimiter::None, quote!(Trait))),
        TokenTree::Ident(Ident::new("for", Span::call_site())),
        TokenTree::Group(Group::new(Delimiter::None, quote!(Type))),
        TokenTree::Group(Group::new(Delimiter::Brace, TokenStream::new())),
    ]);

    snapshot!(tokens as Item, @r###"
    Item::Impl {
        generics: Generics,
        trait_: Some((
            None,
            Path {
                segments: [
                    PathSegment {
                        ident: "Trait",
                        arguments: None,
                    },
                ],
            },
        )),
        self_ty: Type::Group {
            elem: Type::Path {
                path: Path {
                    segments: [
                        PathSegment {
                            ident: "Type",
                            arguments: None,
                        },
                    ],
                },
            },
        },
    }
    "###);
}

#[test]
fn test_supertraits() {
    // Rustc parses all of the following.

    #[rustfmt::skip]
    let tokens = quote!(trait Trait where {});
    snapshot!(tokens as ItemTrait, @r###"
    ItemTrait {
        vis: Inherited,
        ident: "Trait",
        generics: Generics {
            where_clause: Some(WhereClause),
        },
    }
    "###);

    #[rustfmt::skip]
    let tokens = quote!(trait Trait: where {});
    snapshot!(tokens as ItemTrait, @r###"
    ItemTrait {
        vis: Inherited,
        ident: "Trait",
        generics: Generics {
            where_clause: Some(WhereClause),
        },
        colon_token: Some,
    }
    "###);

    #[rustfmt::skip]
    let tokens = quote!(trait Trait: Sized where {});
    snapshot!(tokens as ItemTrait, @r###"
    ItemTrait {
        vis: Inherited,
        ident: "Trait",
        generics: Generics {
            where_clause: Some(WhereClause),
        },
        colon_token: Some,
        supertraits: [
            Trait(TraitBound {
                modifier: None,
                path: Path {
                    segments: [
                        PathSegment {
                            ident: "Sized",
                            arguments: None,
                        },
                    ],
                },
            }),
        ],
    }
    "###);

    #[rustfmt::skip]
    let tokens = quote!(trait Trait: Sized + where {});
    snapshot!(tokens as ItemTrait, @r###"
    ItemTrait {
        vis: Inherited,
        ident: "Trait",
        generics: Generics {
            where_clause: Some(WhereClause),
        },
        colon_token: Some,
        supertraits: [
            Trait(TraitBound {
                modifier: None,
                path: Path {
                    segments: [
                        PathSegment {
                            ident: "Sized",
                            arguments: None,
                        },
                    ],
                },
            }),
        ],
    }
    "###);
}
<commit_msg>Add regression test for issue 974<commit_after>#[macro_use]
mod macros;

use proc_macro2::{Delimiter, Group, Ident, Span, TokenStream, TokenTree};
use quote::quote;
use std::iter::FromIterator;
use syn::{Item, ItemTrait};

#[test]
fn test_macro_variable_attr() {
    // mimics the token stream corresponding to `$attr fn f() {}`
    let tokens = TokenStream::from_iter(vec![
        TokenTree::Group(Group::new(Delimiter::None, quote! { #[test] })),
        TokenTree::Ident(Ident::new("fn", Span::call_site())),
        TokenTree::Ident(Ident::new("f", Span::call_site())),
        TokenTree::Group(Group::new(Delimiter::Parenthesis, TokenStream::new())),
        TokenTree::Group(Group::new(Delimiter::Brace, TokenStream::new())),
    ]);

    snapshot!(tokens as Item, @r###"
    Item::Fn {
        attrs: [
            Attribute {
                style: Outer,
                path: Path {
                    segments: [
                        PathSegment {
                            ident: "test",
                            arguments: None,
                        },
                    ],
                },
                tokens: TokenStream(``),
            },
        ],
        vis: Inherited,
        sig: Signature {
            ident: "f",
            generics: Generics,
            output: Default,
        },
        block: Block,
    }
    "###);
}

#[test]
fn test_negative_impl() {
    // Rustc parses all of the following.

    #[cfg(any())]
    impl ! {}
    let tokens = quote! {
        impl ! {}
    };
    snapshot!(tokens as Item, @r###"
    Item::Impl {
        generics: Generics,
        self_ty: Type::Never,
    }
    "###);

    #[cfg(any())]
    #[rustfmt::skip]
    impl !Trait {}
    let tokens = quote! {
        impl !Trait {}
    };
    snapshot!(tokens as Item, @r###"
    Item::Impl {
        generics: Generics,
        self_ty: Verbatim(`! Trait`),
    }
    "###);

    #[cfg(any())]
    impl !Trait for T {}
    let tokens = quote! {
        impl !Trait for T {}
    };
    snapshot!(tokens as Item, @r###"
    Item::Impl {
        generics: Generics,
        trait_: Some((
            Some,
            Path {
                segments: [
                    PathSegment {
                        ident: "Trait",
                        arguments: None,
                    },
                ],
            },
        )),
        self_ty: Type::Path {
            path: Path {
                segments: [
                    PathSegment {
                        ident: "T",
                        arguments: None,
                    },
                ],
            },
        },
    }
    "###);

    #[cfg(any())]
    #[rustfmt::skip]
    impl !! {}
    let tokens = quote! {
        impl !! {}
    };
    snapshot!(tokens as Item, @r###"
    Item::Impl {
        generics: Generics,
        self_ty: Verbatim(`! !`),
    }
    "###);
}

#[test]
fn test_macro_variable_impl() {
    // mimics the token stream corresponding to `impl $trait for $ty {}`
    let tokens = TokenStream::from_iter(vec![
        TokenTree::Ident(Ident::new("impl", Span::call_site())),
        TokenTree::Group(Group::new(Delimiter::None, quote!(Trait))),
        TokenTree::Ident(Ident::new("for", Span::call_site())),
        TokenTree::Group(Group::new(Delimiter::None, quote!(Type))),
        TokenTree::Group(Group::new(Delimiter::Brace, TokenStream::new())),
    ]);

    snapshot!(tokens as Item, @r###"
    Item::Impl {
        generics: Generics,
        trait_: Some((
            None,
            Path {
                segments: [
                    PathSegment {
                        ident: "Trait",
                        arguments: None,
                    },
                ],
            },
        )),
        self_ty: Type::Group {
            elem: Type::Path {
                path: Path {
                    segments: [
                        PathSegment {
                            ident: "Type",
                            arguments: None,
                        },
                    ],
                },
            },
        },
    }
    "###);
}

#[test]
fn test_supertraits() {
    // Rustc parses all of the following.

    #[rustfmt::skip]
    let tokens = quote!(trait Trait where {});
    snapshot!(tokens as ItemTrait, @r###"
    ItemTrait {
        vis: Inherited,
        ident: "Trait",
        generics: Generics {
            where_clause: Some(WhereClause),
        },
    }
    "###);

    #[rustfmt::skip]
    let tokens = quote!(trait Trait: where {});
    snapshot!(tokens as ItemTrait, @r###"
    ItemTrait {
        vis: Inherited,
        ident: "Trait",
        generics: Generics {
            where_clause: Some(WhereClause),
        },
        colon_token: Some,
    }
    "###);

    #[rustfmt::skip]
    let tokens = quote!(trait Trait: Sized where {});
    snapshot!(tokens as ItemTrait, @r###"
    ItemTrait {
        vis: Inherited,
        ident: "Trait",
        generics: Generics {
            where_clause: Some(WhereClause),
        },
        colon_token: Some,
        supertraits: [
            Trait(TraitBound {
                modifier: None,
                path: Path {
                    segments: [
                        PathSegment {
                            ident: "Sized",
                            arguments: None,
                        },
                    ],
                },
            }),
        ],
    }
    "###);

    #[rustfmt::skip]
    let tokens = quote!(trait Trait: Sized + where {});
    snapshot!(tokens as ItemTrait, @r###"
    ItemTrait {
        vis: Inherited,
        ident: "Trait",
        generics: Generics {
            where_clause: Some(WhereClause),
        },
        colon_token: Some,
        supertraits: [
            Trait(TraitBound {
                modifier: None,
                path: Path {
                    segments: [
                        PathSegment {
                            ident: "Sized",
                            arguments: None,
                        },
                    ],
                },
            }),
        ],
    }
    "###);
}

#[test]
fn test_type_empty_bounds() {
    #[rustfmt::skip]
    let tokens = quote! {
        trait Foo {
            type Bar: ;
        }
    };

    snapshot!(tokens as ItemTrait, @r###"
    ItemTrait {
        vis: Inherited,
        ident: "Foo",
        generics: Generics,
        items: [
            TraitItem::Type {
                ident: "Bar",
                generics: Generics,
                colon_token: Some,
            },
        ],
    }
    "###);
}
<|endoftext|>


<commit_before>use regex::Regex;
use time::Timespec;

use timefmt::parse_time_str;

pub struct FileNameInfo<'a> {
    pub file_name: &'a str,
    pub info: Info,
}

#[derive(Eq, PartialEq, Debug)]
pub struct Info {
    pub tp: Type,
    pub compressed: bool,
    pub encrypted: bool,
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub enum Type {
    Full {
        time: Timespec,
        volume_number: usize,
    },
    FullManifest {
        time: Timespec,
        partial: bool,
    },
    Inc {
        start_time: Timespec,
        end_time: Timespec,
        volume_number: usize,
    },
    IncManifest {
        start_time: Timespec,
        end_time: Timespec,
        partial: bool,
    },
    FullSig {
        time: Timespec,
        partial: bool,
    },
    NewSig {
        start_time: Timespec,
        end_time: Timespec,
        partial: bool,
    },
}

pub struct FileNameParser {
    full_vol_re: Regex,
    full_manifest_re: Regex,
    inc_vol_re: Regex,
    inc_manifest_re: Regex,
    full_sig_re: Regex,
    new_sig_re: Regex,
}

impl<'a> FileNameInfo<'a> {
    pub fn new(name: &'a str, info: Info) -> Self {
        FileNameInfo {
            file_name: &name,
            info: info,
        }
    }

    pub fn start_time(&self) -> Timespec {
        self.info.tp.time_range().0
    }
}

impl Type {
    pub fn time_range(&self) -> (Timespec, Timespec) {
        match *self {
            Type::Full{ time, .. } |
            Type::FullSig{ time, .. } |
            Type::FullManifest{ time, .. } => (time, time),
            Type::Inc{ start_time, end_time, .. } |
            Type::IncManifest{ start_time, end_time, .. } |
            Type::NewSig{ start_time, end_time, .. } => (start_time, end_time),
        }
    }
}

impl FileNameParser {
    pub fn new() -> Self {
        FileNameParser {
            full_vol_re: Regex::new(r"^duplicity-full\.(?P<time>.*?)\.vol(?P<num>[0-9]+)\.difftar(?P<partial>(\.part))?($|\.)").unwrap(),
            full_manifest_re: Regex::new(r"^duplicity-full\.(?P<time>.*?)\.manifest(?P<partial>(\.part))?($|\.)").unwrap(),
            inc_vol_re: Regex::new(r"^duplicity-inc\.(?P<start_time>.*?)\.to\.(?P<end_time>.*?)\.vol(?P<num>[0-9]+)\.difftar($|\.)").unwrap(),
            inc_manifest_re: Regex::new(r"^duplicity-inc\.(?P<start_time>.*?)\.to\.(?P<end_time>.*?)\.manifest(?P<partial>(\.part))?(\.|$)").unwrap(),
            full_sig_re: Regex::new(r"^duplicity-full-signatures\.(?P<time>.*?)\.sigtar(?P<partial>(\.part))?(\.|$)").unwrap(),
            new_sig_re: Regex::new(r"^duplicity-new-signatures\.(?P<start_time>.*?)\.to\.(?P<end_time>.*?)\.sigtar(?P<partial>(\.part))?(\.|$)").unwrap(),
        }
    }

    pub fn parse(&self, filename: &str) -> Option<Info> {
        use std::ascii::AsciiExt;

        let lower_fname = filename.to_ascii_lowercase();
        let opt_type = self.check_full(&lower_fname)
                           .or(self.check_inc(&lower_fname))
                           .or(self.check_sig(&lower_fname));
        opt_type.map(|t| {
            Info {
                tp: t,
                compressed: is_compressed(lower_fname.as_ref()),
                encrypted: is_encrypted(lower_fname.as_ref()),
            }
        })
    }

    fn check_full(&self, filename: &str) -> Option<Type> {
        if let Some(captures) = self.full_vol_re.captures(filename) {
            let time = try_opt!(parse_time_str(captures.name("time").unwrap()));
            let vol_num = try_opt!(get_vol_num(captures.name("num").unwrap()));
            Some(Type::Full {
                time: time,
                volume_number: vol_num,
            })
        } else if let Some(captures) = self.full_manifest_re.captures(filename) {
            let time = try_opt!(parse_time_str(captures.name("time").unwrap()));
            Some(Type::FullManifest {
                time: time,
                partial: captures.name("partial").is_some(),
            })
        } else {
            None
        }
    }

    fn check_inc(&self, filename: &str) -> Option<Type> {
        if let Some(captures) = self.inc_vol_re.captures(filename) {
            let start_time = try_opt!(parse_time_str(captures.name("start_time").unwrap()));
            let end_time = try_opt!(parse_time_str(captures.name("end_time").unwrap()));
            let vol_num = try_opt!(get_vol_num(captures.name("num").unwrap()));
            Some(Type::Inc {
                start_time: start_time,
                end_time: end_time,
                volume_number: vol_num,
            })
        } else if let Some(captures) = self.inc_manifest_re.captures(filename) {
            let start_time = try_opt!(parse_time_str(captures.name("start_time").unwrap()));
            let end_time = try_opt!(parse_time_str(captures.name("end_time").unwrap()));
            Some(Type::IncManifest {
                start_time: start_time,
                end_time: end_time,
                partial: captures.name("partial").is_some(),
            })
        } else {
            None
        }
    }

    fn check_sig(&self, filename: &str) -> Option<Type> {
        if let Some(captures) = self.full_sig_re.captures(filename) {
            let time = try_opt!(parse_time_str(captures.name("time").unwrap()));
            Some(Type::FullSig {
                time: time,
                partial: captures.name("partial").is_some(),
            })
        } else if let Some(captures) = self.new_sig_re.captures(filename) {
            let start_time = try_opt!(parse_time_str(captures.name("start_time").unwrap()));
            let end_time = try_opt!(parse_time_str(captures.name("end_time").unwrap()));
            Some(Type::NewSig {
                start_time: start_time,
                end_time: end_time,
                partial: captures.name("partial").is_some(),
            })
        } else {
            None
        }
    }
}

fn get_vol_num(s: &str) -> Option<usize> {
    s.parse::<usize>().ok()
}

fn is_encrypted(s: &str) -> bool {
    s.ends_with(".gpg") || s.ends_with(".g")
}

fn is_compressed(s: &str) -> bool {
    s.ends_with(".gz") || s.ends_with(".z")
}

#[cfg(test)]
mod test {
    use super::*;
    use timefmt::parse_time_str;

    #[test]
    fn parser_test() {
        let parser = FileNameParser::new();
        // invalid
        assert_eq!(parser.parse("invalid"), None);
        // full
        assert_eq!(parser.parse("duplicity-full.20150617T182545Z.vol1.difftar.gz"),
                   Some(Info {
                       tp: Type::Full {
                           time: parse_time_str("20150617t182545z").unwrap(),
                           volume_number: 1,
                       },
                       compressed: true,
                       encrypted: false,
                   }));
        // full manifest
        assert_eq!(parser.parse("duplicity-full.20150617T182545Z.manifest"),
                   Some(Info {
                       tp: Type::FullManifest {
                           time: parse_time_str("20150617t182545z").unwrap(),
                           partial: false,
                       },
                       compressed: false,
                       encrypted: false,
                   }));
        // inc
        assert_eq!(parser.parse("duplicity-inc.20150617T182629Z.to.20150617T182650Z.vol1.difftar.gz"),
                   Some(Info{ tp: Type::Inc{ start_time: parse_time_str("20150617t182629z").unwrap(),
                                             end_time: parse_time_str("20150617t182650z").unwrap(),
                                             volume_number: 1 },
                              compressed: true,
                              encrypted: false }));
        // inc manifest
        assert_eq!(parser.parse("duplicity-inc.20150617T182545Z.to.20150617T182629Z.manifest"),
                   Some(Info {
                       tp: Type::IncManifest {
                           start_time: parse_time_str("20150617t182545z").unwrap(),
                           end_time: parse_time_str("20150617t182629z").unwrap(),
                           partial: false,
                       },
                       compressed: false,
                       encrypted: false,
                   }));
        // new sig
        assert_eq!(parser.parse("duplicity-new-signatures.20150617T182545Z.to.20150617T182629Z.sigtar.gz"),
                   Some(Info{ tp: Type::NewSig{ start_time: parse_time_str("20150617t182545z").unwrap(),
                                                end_time: parse_time_str("20150617t182629z").unwrap(),
                                                partial: false },
                              compressed: true,
                              encrypted: false }));
        // full sig
        assert_eq!(parser.parse("duplicity-full-signatures.20150617T182545Z.sigtar.gz"),
                   Some(Info {
                       tp: Type::FullSig {
                           time: parse_time_str("20150617t182545z").unwrap(),
                           partial: false,
                       },
                       compressed: true,
                       encrypted: false,
                   }));
    }
}
<commit_msg>Added missing copy impl<commit_after>use regex::Regex;
use time::Timespec;

use timefmt::parse_time_str;

pub struct FileNameInfo<'a> {
    pub file_name: &'a str,
    pub info: Info,
}

#[derive(Eq, PartialEq, Debug)]
pub struct Info {
    pub tp: Type,
    pub compressed: bool,
    pub encrypted: bool,
}

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Type {
    Full {
        time: Timespec,
        volume_number: usize,
    },
    FullManifest {
        time: Timespec,
        partial: bool,
    },
    Inc {
        start_time: Timespec,
        end_time: Timespec,
        volume_number: usize,
    },
    IncManifest {
        start_time: Timespec,
        end_time: Timespec,
        partial: bool,
    },
    FullSig {
        time: Timespec,
        partial: bool,
    },
    NewSig {
        start_time: Timespec,
        end_time: Timespec,
        partial: bool,
    },
}

pub struct FileNameParser {
    full_vol_re: Regex,
    full_manifest_re: Regex,
    inc_vol_re: Regex,
    inc_manifest_re: Regex,
    full_sig_re: Regex,
    new_sig_re: Regex,
}

impl<'a> FileNameInfo<'a> {
    pub fn new(name: &'a str, info: Info) -> Self {
        FileNameInfo {
            file_name: &name,
            info: info,
        }
    }

    pub fn start_time(&self) -> Timespec {
        self.info.tp.time_range().0
    }
}

impl Type {
    pub fn time_range(&self) -> (Timespec, Timespec) {
        match *self {
            Type::Full{ time, .. } |
            Type::FullSig{ time, .. } |
            Type::FullManifest{ time, .. } => (time, time),
            Type::Inc{ start_time, end_time, .. } |
            Type::IncManifest{ start_time, end_time, .. } |
            Type::NewSig{ start_time, end_time, .. } => (start_time, end_time),
        }
    }
}

impl FileNameParser {
    pub fn new() -> Self {
        FileNameParser {
            full_vol_re: Regex::new(r"^duplicity-full\.(?P<time>.*?)\.vol(?P<num>[0-9]+)\.difftar(?P<partial>(\.part))?($|\.)").unwrap(),
            full_manifest_re: Regex::new(r"^duplicity-full\.(?P<time>.*?)\.manifest(?P<partial>(\.part))?($|\.)").unwrap(),
            inc_vol_re: Regex::new(r"^duplicity-inc\.(?P<start_time>.*?)\.to\.(?P<end_time>.*?)\.vol(?P<num>[0-9]+)\.difftar($|\.)").unwrap(),
            inc_manifest_re: Regex::new(r"^duplicity-inc\.(?P<start_time>.*?)\.to\.(?P<end_time>.*?)\.manifest(?P<partial>(\.part))?(\.|$)").unwrap(),
            full_sig_re: Regex::new(r"^duplicity-full-signatures\.(?P<time>.*?)\.sigtar(?P<partial>(\.part))?(\.|$)").unwrap(),
            new_sig_re: Regex::new(r"^duplicity-new-signatures\.(?P<start_time>.*?)\.to\.(?P<end_time>.*?)\.sigtar(?P<partial>(\.part))?(\.|$)").unwrap(),
        }
    }

    pub fn parse(&self, filename: &str) -> Option<Info> {
        use std::ascii::AsciiExt;

        let lower_fname = filename.to_ascii_lowercase();
        let opt_type = self.check_full(&lower_fname)
                           .or(self.check_inc(&lower_fname))
                           .or(self.check_sig(&lower_fname));
        opt_type.map(|t| {
            Info {
                tp: t,
                compressed: is_compressed(lower_fname.as_ref()),
                encrypted: is_encrypted(lower_fname.as_ref()),
            }
        })
    }

    fn check_full(&self, filename: &str) -> Option<Type> {
        if let Some(captures) = self.full_vol_re.captures(filename) {
            let time = try_opt!(parse_time_str(captures.name("time").unwrap()));
            let vol_num = try_opt!(get_vol_num(captures.name("num").unwrap()));
            Some(Type::Full {
                time: time,
                volume_number: vol_num,
            })
        } else if let Some(captures) = self.full_manifest_re.captures(filename) {
            let time = try_opt!(parse_time_str(captures.name("time").unwrap()));
            Some(Type::FullManifest {
                time: time,
                partial: captures.name("partial").is_some(),
            })
        } else {
            None
        }
    }

    fn check_inc(&self, filename: &str) -> Option<Type> {
        if let Some(captures) = self.inc_vol_re.captures(filename) {
            let start_time = try_opt!(parse_time_str(captures.name("start_time").unwrap()));
            let end_time = try_opt!(parse_time_str(captures.name("end_time").unwrap()));
            let vol_num = try_opt!(get_vol_num(captures.name("num").unwrap()));
            Some(Type::Inc {
                start_time: start_time,
                end_time: end_time,
                volume_number: vol_num,
            })
        } else if let Some(captures) = self.inc_manifest_re.captures(filename) {
            let start_time = try_opt!(parse_time_str(captures.name("start_time").unwrap()));
            let end_time = try_opt!(parse_time_str(captures.name("end_time").unwrap()));
            Some(Type::IncManifest {
                start_time: start_time,
                end_time: end_time,
                partial: captures.name("partial").is_some(),
            })
        } else {
            None
        }
    }

    fn check_sig(&self, filename: &str) -> Option<Type> {
        if let Some(captures) = self.full_sig_re.captures(filename) {
            let time = try_opt!(parse_time_str(captures.name("time").unwrap()));
            Some(Type::FullSig {
                time: time,
                partial: captures.name("partial").is_some(),
            })
        } else if let Some(captures) = self.new_sig_re.captures(filename) {
            let start_time = try_opt!(parse_time_str(captures.name("start_time").unwrap()));
            let end_time = try_opt!(parse_time_str(captures.name("end_time").unwrap()));
            Some(Type::NewSig {
                start_time: start_time,
                end_time: end_time,
                partial: captures.name("partial").is_some(),
            })
        } else {
            None
        }
    }
}

fn get_vol_num(s: &str) -> Option<usize> {
    s.parse::<usize>().ok()
}

fn is_encrypted(s: &str) -> bool {
    s.ends_with(".gpg") || s.ends_with(".g")
}

fn is_compressed(s: &str) -> bool {
    s.ends_with(".gz") || s.ends_with(".z")
}

#[cfg(test)]
mod test {
    use super::*;
    use timefmt::parse_time_str;

    #[test]
    fn parser_test() {
        let parser = FileNameParser::new();
        // invalid
        assert_eq!(parser.parse("invalid"), None);
        // full
        assert_eq!(parser.parse("duplicity-full.20150617T182545Z.vol1.difftar.gz"),
                   Some(Info {
                       tp: Type::Full {
                           time: parse_time_str("20150617t182545z").unwrap(),
                           volume_number: 1,
                       },
                       compressed: true,
                       encrypted: false,
                   }));
        // full manifest
        assert_eq!(parser.parse("duplicity-full.20150617T182545Z.manifest"),
                   Some(Info {
                       tp: Type::FullManifest {
                           time: parse_time_str("20150617t182545z").unwrap(),
                           partial: false,
                       },
                       compressed: false,
                       encrypted: false,
                   }));
        // inc
        assert_eq!(parser.parse("duplicity-inc.20150617T182629Z.to.20150617T182650Z.vol1.difftar.gz"),
                   Some(Info{ tp: Type::Inc{ start_time: parse_time_str("20150617t182629z").unwrap(),
                                             end_time: parse_time_str("20150617t182650z").unwrap(),
                                             volume_number: 1 },
                              compressed: true,
                              encrypted: false }));
        // inc manifest
        assert_eq!(parser.parse("duplicity-inc.20150617T182545Z.to.20150617T182629Z.manifest"),
                   Some(Info {
                       tp: Type::IncManifest {
                           start_time: parse_time_str("20150617t182545z").unwrap(),
                           end_time: parse_time_str("20150617t182629z").unwrap(),
                           partial: false,
                       },
                       compressed: false,
                       encrypted: false,
                   }));
        // new sig
        assert_eq!(parser.parse("duplicity-new-signatures.20150617T182545Z.to.20150617T182629Z.sigtar.gz"),
                   Some(Info{ tp: Type::NewSig{ start_time: parse_time_str("20150617t182545z").unwrap(),
                                                end_time: parse_time_str("20150617t182629z").unwrap(),
                                                partial: false },
                              compressed: true,
                              encrypted: false }));
        // full sig
        assert_eq!(parser.parse("duplicity-full-signatures.20150617T182545Z.sigtar.gz"),
                   Some(Info {
                       tp: Type::FullSig {
                           time: parse_time_str("20150617t182545z").unwrap(),
                           partial: false,
                       },
                       compressed: true,
                       encrypted: false,
                   }));
    }
}
<|endoftext|>


<commit_before>use component::Component;
use tokio_core::reactor::Handle;
use error::{Error, Result, ErrorKind};
use xcb::{self, Connection};
use xcb_event_stream;
use futures::stream::Stream;

/// This struct is used for the window title component.
pub struct WindowTitle {
    active_window: xcb::AtomEnum,
    wm_name: xcb::AtomEnum,
}

impl Default for WindowTitle {
    /// Create the default `WindowTitle` component.
    /// The default by itself does not work, `init` is required.
    fn default() -> WindowTitle {
        WindowTitle {
            active_window: 0,
            wm_name: 0,
        }
    }
}

impl WindowTitle {
    // Get the title of the window that currently is focused
    fn get_window_title(&self) -> Result<String> {
        // Connect to Xorg
        let (conn, screen_num) = Connection::connect(None)
            .map_err(|e| ErrorKind::XcbConnection(e))?;

        // Get the screen for accessing the root window later
        let setup = conn.get_setup();
        let screen = setup
            .roots()
            .nth(screen_num as usize)
            .ok_or("Unable to acquire screen.")?;

        // Get the currently active window
        let property_reply = xcb::get_property(
            &conn,
            false,
            screen.root(),
            self.active_window,
            xcb::ATOM_WINDOW,
            0,
            32,
        ).get_reply()?;

        // Get the window from the reply
        // Returns with error if no window has been found
        let active_window_value: &[u32] = property_reply.value();
        if active_window_value.is_empty() {
            return Err("Could not get active window.".into());
        }
        let window = active_window_value[0];

        // Get the title of the active window
        let property_reply =
            xcb::get_property(&conn, false, window, self.wm_name, xcb::ATOM_ANY, 0, 32)
                .get_reply()?;

        // Convert active window reply to string and return it
        let name = String::from_utf8_lossy(property_reply.value());
        Ok(name.into())
    }
}

// Poll the window title on a timer
impl Component for WindowTitle {
    type Error = Error;
    type Stream = Box<Stream<Item = String, Error = Error>>;

    fn stream(self, _: Handle) -> Self::Stream {
        let (conn, screen_num) = Connection::connect(None).unwrap();

        // Setup the event for the root screen
        {
            let setup = conn.get_setup();
            let screen = setup.roots().nth(screen_num as usize).unwrap();

            xcb::change_window_attributes(
                &conn,
                screen.root(),
                &[(xcb::CW_EVENT_MASK, xcb::EVENT_MASK_PROPERTY_CHANGE)],
            );
        }

        // Start event receiver future
        // Executes `get_window_title` every time window title event is received
        conn.flush();
        let active_window = self.active_window;
        let stream = xcb_event_stream::XcbEventStream::new(conn)
            .filter(move |event| unsafe {
                let property_event: &xcb::PropertyNotifyEvent = xcb::cast_event(&event);
                let property_atom = property_event.atom();
                property_atom == active_window
            })
            .and_then(move |_| self.get_window_title());

        Box::new(stream)
    }

    // Set fields of `WindowTitle` struct.
    //
    // # Errors
    //
    // If there is a problem with X or the Atoms are not supported, this will fail.
    fn init(&mut self) -> Result<()> {
        let (conn, _) = Connection::connect(None)
            .map_err(|e| ErrorKind::XcbConnection(e))?;

        let active_window = xcb::intern_atom(&conn, true, "_NET_ACTIVE_WINDOW")
            .get_reply()?
            .atom();
        let wm_name = xcb::intern_atom(&conn, true, "_NET_WM_NAME")
            .get_reply()?
            .atom();

        // Fail if one of the atoms is not supported
        if wm_name == 0 || active_window == 0 {
            return Err("The required EWMH properties are not supported.".into());
        }

        self.active_window = active_window;
        self.wm_name = wm_name;

        Ok(())
    }
}
<commit_msg>Rebase to include #35 and fix issues caused<commit_after>use component::Component;
use tokio_core::reactor::Handle;
use error::{Error, Result, ErrorKind};
use xcb::{self, Connection};
use xcb_event_stream;
use futures::stream::Stream;

/// This struct is used for the window title component.
pub struct WindowTitle {
    active_window: xcb::AtomEnum,
    wm_name: xcb::AtomEnum,
}

impl Default for WindowTitle {
    /// Create the default `WindowTitle` component.
    /// The default by itself does not work, `init` is required.
    fn default() -> WindowTitle {
        WindowTitle {
            active_window: 0,
            wm_name: 0,
        }
    }
}

impl WindowTitle {
    // Get the title of the window that currently is focused
    fn get_window_title(&self) -> Result<String> {
        // Connect to Xorg
        let (conn, screen_num) = Connection::connect(None)
            .map_err(|e| ErrorKind::XcbConnection(e))?;

        // Get the screen for accessing the root window later
        let setup = conn.get_setup();
        let screen = setup
            .roots()
            .nth(screen_num as usize)
            .ok_or("Unable to acquire screen.")?;

        // Get the currently active window
        let property_reply = xcb::get_property(
            &conn,
            false,
            screen.root(),
            self.active_window,
            xcb::ATOM_WINDOW,
            0,
            32,
        ).get_reply()?;

        // Get the window from the reply
        // Returns with error if no window has been found
        let active_window_value: &[u32] = property_reply.value();
        if active_window_value.is_empty() {
            return Err("Could not get active window.".into());
        }
        let window = active_window_value[0];

        // Get the title of the active window
        let property_reply =
            xcb::get_property(&conn, false, window, self.wm_name, xcb::ATOM_ANY, 0, 32)
                .get_reply()?;

        // Convert active window reply to string and return it
        let name = String::from_utf8_lossy(property_reply.value());
        Ok(name.into())
    }
}

// Poll the window title on a timer
impl Component for WindowTitle {
    type Error = Error;
    type Stream = Box<Stream<Item = String, Error = Error>>;

    fn stream(self, handle: Handle) -> Self::Stream {
        let (conn, screen_num) = Connection::connect(None).unwrap();

        // Setup the event for the root screen
        {
            let setup = conn.get_setup();
            let screen = setup.roots().nth(screen_num as usize).unwrap();

            xcb::change_window_attributes(
                &conn,
                screen.root(),
                &[(xcb::CW_EVENT_MASK, xcb::EVENT_MASK_PROPERTY_CHANGE)],
            );
        }

        // Start event receiver future
        // Executes `get_window_title` every time window title event is received
        conn.flush();
        let active_window = self.active_window;
        let stream = xcb_event_stream::XcbEventStream::new(conn, &handle)
            .unwrap()
            .filter(move |event| unsafe {
                let property_event: &xcb::PropertyNotifyEvent = xcb::cast_event(&event);
                let property_atom = property_event.atom();
                property_atom == active_window
            })
            .and_then(move |_| self.get_window_title());

        Box::new(stream)
    }

    // Set fields of `WindowTitle` struct.
    //
    // # Errors
    //
    // If there is a problem with X or the Atoms are not supported, this will fail.
    fn init(&mut self) -> Result<()> {
        let (conn, _) = Connection::connect(None)
            .map_err(|e| ErrorKind::XcbConnection(e))?;

        let active_window = xcb::intern_atom(&conn, true, "_NET_ACTIVE_WINDOW")
            .get_reply()?
            .atom();
        let wm_name = xcb::intern_atom(&conn, true, "_NET_WM_NAME")
            .get_reply()?
            .atom();

        // Fail if one of the atoms is not supported
        if wm_name == 0 || active_window == 0 {
            return Err("The required EWMH properties are not supported.".into());
        }

        self.active_window = active_window;
        self.wm_name = wm_name;

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


<commit_before>use std::sync::{Arc};
use std::path::{Path};

use cgmath::{Matrix4};
use crossbeam::sync::{MsQueue};
use glium::{Surface};
use glium::backend::glutin_backend::{GlutinFacade};
use glium::framebuffer::{SimpleFrameBuffer};
use glium::glutin::{Event, VirtualKeyCode, ElementState};
use glium::texture::{DepthFormat, DepthTexture2d, MipmapsOption, Texture2d};

use debug::gnomon;
use input::{InputEvent};
use model::{Model};
use render::shaders::{FlatColorProgram, ForwardProgram, ImageProgram, ShadowProgram};
use render::render_context::{DEPTH_DIMENSION};
use render::render_frame::{RenderFrame};
use scene::{Scene};

pub struct RenderProcessor {
	pub facade: GlutinFacade,
	pub flat_color_program: FlatColorProgram,
	pub image_program: ImageProgram,

	q:               Arc<MsQueue<RenderFrame>>,
	player:          Model,
	scene:           Scene,
	forward_program: ForwardProgram,
	shadow_program:  ShadowProgram,
}

impl RenderProcessor {
	pub fn new(q: Arc<MsQueue<RenderFrame>>, facade: GlutinFacade) -> RenderProcessor {
		const PLAYER_PATH_STRING: &'static str = "./data/player.obj";
		let player = Model::new(&facade, &Path::new(PLAYER_PATH_STRING));

		let scene = Scene::new(&facade);

		let flat_color_program = FlatColorProgram::new(&facade);
		let forward_program = ForwardProgram::new(&facade);
		let image_program = ImageProgram::new(&facade);
		let shadow_program = ShadowProgram::new(&facade);

		RenderProcessor {
			q: q,
			facade: facade,
			player: player,
			scene: scene,
			forward_program: forward_program,
			shadow_program: shadow_program,
			flat_color_program: flat_color_program,
			image_program: image_program,
		}
	}

	// returns true to signal caller to exit program and event loop
	// TODO: should std::process::exit(i32) be used instead?
	//
	pub fn handle_system_events(&self) -> Option<Vec<InputEvent>> {
		let mut out = Vec::new();

		let (width, height) = self.facade.get_window().unwrap().get_inner_size().unwrap();
		let (width, height) = (width as i32, height as i32);

		for event in self.facade.poll_events() {
			match event {
				Event::Closed => {
					println!("Exiting due to quit event");
					out.push(InputEvent::Quit);
					return None;
				},
				Event::KeyboardInput(_, _, Some(VirtualKeyCode::Escape)) => {
					println!("Exiting due to escape key");
					out.push(InputEvent::Quit);
					return None;
				},
				Event::KeyboardInput(state, _, Some(key_code)) => {
					out.push(InputEvent::KeyboardInput {
						pressed: state == ElementState::Pressed,
						id: key_code,
					});
				},
				Event::MouseMoved(x, y) => {
					let (cx, cy) = (width / 2, height / 2);

					if x == cx && y == cy { continue }

					self.facade.get_window().unwrap().set_cursor_position(cx, cy).ok();
					out.push(InputEvent::MouseMoved {
						dx: ((x - cx) as f64) / (cx as f64),
						dy: ((y - cy) as f64) / (cy as f64),
					});
				},
				Event::Resized(_width, _height) => {
					// TODO: implement and event bus it or something
				},
				_ => ()
			}
		}
		Some(out)
	}

	pub fn handle_render_commands(&mut self) {
		while let Some(render_frame) = self.q.try_pop() {
			let shadow_map = DepthTexture2d::empty_with_format(
				&self.facade,
				DepthFormat::I24,
				MipmapsOption::NoMipmap,
				DEPTH_DIMENSION,
				DEPTH_DIMENSION
			).unwrap(); // TODO: handle error instead

			{
				let color = Texture2d::empty(
					&self.facade,
					DEPTH_DIMENSION, // TODO: can we use 0/1
					DEPTH_DIMENSION,
				).unwrap();

				let mut frame_buffer = SimpleFrameBuffer::with_depth_buffer(&self.facade, &color, &shadow_map).unwrap();

				frame_buffer.clear_depth(1.0);

				{
					let uniform_buffer = uniform! {
						// TODO: doesn't seem like the right place(thread) for these clones
						shadow: render_frame.scene_uniforms.shadow.clone(),
					};
					frame_buffer.draw(
						&self.scene.model.vertex_buffer,
						&self.scene.model.index_buffer,
						&self.shadow_program.program,
						&uniform_buffer,
						&self.shadow_program.parameters,
					).unwrap();
				}
				{
					let uniform_buffer = uniform! {
						shadow: render_frame.player_uniforms.shadow.clone(),
					};
					frame_buffer.draw(
						&self.player.vertex_buffer,
						&self.player.index_buffer,
						&self.shadow_program.program,
						&uniform_buffer,
						&self.shadow_program.parameters,
					).unwrap();
				}
			}

			let mut frame = self.facade.draw();
			frame.clear_color_and_depth((0.125f32, 0.25f32, 0.5f32, 1.0f32), 1.0);
			{
				let uniform_buffer = uniform! {
					shadow:                  render_frame.scene_uniforms.shadow.clone(),
					shadow_map:              shadow_map.sampled(),
					model:                   render_frame.scene_uniforms.model.clone(),
					model_view_projection:   render_frame.scene_uniforms.model_view_projection.clone(),
					reverse_light_direction: render_frame.scene_uniforms.reverse_light_direction.clone(),
				};
				frame.draw(
					&self.scene.model.vertex_buffer,
					&self.scene.model.index_buffer,
					&self.forward_program.program,
					&uniform_buffer,
					&self.forward_program.parameters
				).unwrap();
			}
			{
				let uniform_buffer = uniform! {
					shadow:                  render_frame.player_uniforms.shadow.clone(),
					shadow_map:              shadow_map.sampled(),
					model:                   render_frame.player_uniforms.model.clone(),
					model_view_projection:   render_frame.player_uniforms.model_view_projection.clone(),
					reverse_light_direction: render_frame.player_uniforms.reverse_light_direction.clone(),
				};
				frame.draw(
					&self.player.vertex_buffer,
					&self.player.index_buffer,
					&self.forward_program.program,
					&uniform_buffer,
					&self.forward_program.parameters
				).unwrap();
			}
			{
				let s = Matrix4::from_scale(3.0);
				// TODO: wrap the Matrix4 extraction in a function
				let matrix = render_frame.scene_uniforms.model_view_projection.clone();
				gnomon::draw(self, &mut frame, matrix.0 * s);
				let matrix = render_frame.player_uniforms.model_view_projection.clone();
				gnomon::draw(self, &mut frame, matrix.0 * s);
			}
			frame.set_finish().unwrap();
		}
	}
}
<commit_msg>Create shadow map texture only once<commit_after>use std::sync::{Arc};
use std::path::{Path};

use cgmath::{Matrix4};
use crossbeam::sync::{MsQueue};
use glium::{Surface};
use glium::backend::glutin_backend::{GlutinFacade};
use glium::framebuffer::{SimpleFrameBuffer};
use glium::glutin::{Event, VirtualKeyCode, ElementState};
use glium::texture::{DepthFormat, DepthTexture2d, MipmapsOption, Texture2d};

use debug::gnomon;
use input::{InputEvent};
use model::{Model};
use render::shaders::{FlatColorProgram, ForwardProgram, ImageProgram, ShadowProgram};
use render::render_context::{DEPTH_DIMENSION};
use render::render_frame::{RenderFrame};
use scene::{Scene};

pub struct RenderProcessor {
	pub facade: GlutinFacade,
	pub flat_color_program: FlatColorProgram,
	pub image_program: ImageProgram,

	q:               Arc<MsQueue<RenderFrame>>,
	player:          Model,
	scene:           Scene,
	forward_program: ForwardProgram,
	shadow_program:  ShadowProgram,
	shadow_texture:  DepthTexture2d,
}

impl RenderProcessor {
	pub fn new(q: Arc<MsQueue<RenderFrame>>, facade: GlutinFacade) -> RenderProcessor {
		const PLAYER_PATH_STRING: &'static str = "./data/player.obj";
		let player = Model::new(&facade, &Path::new(PLAYER_PATH_STRING));

		let scene = Scene::new(&facade);

		let flat_color_program = FlatColorProgram::new(&facade);
		let forward_program = ForwardProgram::new(&facade);
		let image_program = ImageProgram::new(&facade);
		let shadow_program = ShadowProgram::new(&facade);

		let shadow_texture = DepthTexture2d::empty_with_format(
			&facade,
			DepthFormat::I24,
			MipmapsOption::NoMipmap,
			DEPTH_DIMENSION,
			DEPTH_DIMENSION
		).unwrap(); // TODO: handle error instead

		RenderProcessor {
			q: q,
			facade: facade,
			player: player,
			scene: scene,
			forward_program: forward_program,
			shadow_program: shadow_program,
			flat_color_program: flat_color_program,
			image_program: image_program,
			shadow_texture: shadow_texture,
		}
	}

	// returns true to signal caller to exit program and event loop
	// TODO: should std::process::exit(i32) be used instead?
	//
	pub fn handle_system_events(&self) -> Option<Vec<InputEvent>> {
		let mut out = Vec::new();

		let (width, height) = self.facade.get_window().unwrap().get_inner_size().unwrap();
		let (width, height) = (width as i32, height as i32);

		for event in self.facade.poll_events() {
			match event {
				Event::Closed => {
					println!("Exiting due to quit event");
					out.push(InputEvent::Quit);
					return None;
				},
				Event::KeyboardInput(_, _, Some(VirtualKeyCode::Escape)) => {
					println!("Exiting due to escape key");
					out.push(InputEvent::Quit);
					return None;
				},
				Event::KeyboardInput(state, _, Some(key_code)) => {
					out.push(InputEvent::KeyboardInput {
						pressed: state == ElementState::Pressed,
						id: key_code,
					});
				},
				Event::MouseMoved(x, y) => {
					let (cx, cy) = (width / 2, height / 2);

					if x == cx && y == cy { continue }

					self.facade.get_window().unwrap().set_cursor_position(cx, cy).ok();
					out.push(InputEvent::MouseMoved {
						dx: ((x - cx) as f64) / (cx as f64),
						dy: ((y - cy) as f64) / (cy as f64),
					});
				},
				Event::Resized(_width, _height) => {
					// TODO: implement and event bus it or something
				},
				_ => ()
			}
		}
		Some(out)
	}

	pub fn handle_render_commands(&mut self) {
		while let Some(render_frame) = self.q.try_pop() {
			{
				let color = Texture2d::empty(
					&self.facade,
					DEPTH_DIMENSION, // TODO: can we use 0/1
					DEPTH_DIMENSION,
				).unwrap();

				let mut frame_buffer = SimpleFrameBuffer::with_depth_buffer(&self.facade, &color, &self.shadow_texture).unwrap();

				frame_buffer.clear_depth(1.0);

				{
					let uniform_buffer = uniform! {
						// TODO: doesn't seem like the right place(thread) for these clones
						shadow: render_frame.scene_uniforms.shadow.clone(),
					};
					frame_buffer.draw(
						&self.scene.model.vertex_buffer,
						&self.scene.model.index_buffer,
						&self.shadow_program.program,
						&uniform_buffer,
						&self.shadow_program.parameters,
					).unwrap();
				}
				{
					let uniform_buffer = uniform! {
						shadow: render_frame.player_uniforms.shadow.clone(),
					};
					frame_buffer.draw(
						&self.player.vertex_buffer,
						&self.player.index_buffer,
						&self.shadow_program.program,
						&uniform_buffer,
						&self.shadow_program.parameters,
					).unwrap();
				}
			}

			let mut frame = self.facade.draw();
			frame.clear_color_and_depth((0.125f32, 0.25f32, 0.5f32, 1.0f32), 1.0);
			{
				let uniform_buffer = uniform! {
					shadow:                  render_frame.scene_uniforms.shadow.clone(),
					shadow_map:              self.shadow_texture.sampled(),
					model:                   render_frame.scene_uniforms.model.clone(),
					model_view_projection:   render_frame.scene_uniforms.model_view_projection.clone(),
					reverse_light_direction: render_frame.scene_uniforms.reverse_light_direction.clone(),
				};
				frame.draw(
					&self.scene.model.vertex_buffer,
					&self.scene.model.index_buffer,
					&self.forward_program.program,
					&uniform_buffer,
					&self.forward_program.parameters
				).unwrap();
			}
			{
				let uniform_buffer = uniform! {
					shadow:                  render_frame.player_uniforms.shadow.clone(),
					shadow_map:              self.shadow_texture.sampled(),
					model:                   render_frame.player_uniforms.model.clone(),
					model_view_projection:   render_frame.player_uniforms.model_view_projection.clone(),
					reverse_light_direction: render_frame.player_uniforms.reverse_light_direction.clone(),
				};
				frame.draw(
					&self.player.vertex_buffer,
					&self.player.index_buffer,
					&self.forward_program.program,
					&uniform_buffer,
					&self.forward_program.parameters
				).unwrap();
			}
			{
				let s = Matrix4::from_scale(3.0);
				// TODO: wrap the Matrix4 extraction in a function
				let matrix = render_frame.scene_uniforms.model_view_projection.clone();
				gnomon::draw(self, &mut frame, matrix.0 * s);
				let matrix = render_frame.player_uniforms.model_view_projection.clone();
				gnomon::draw(self, &mut frame, matrix.0 * s);
			}
			frame.set_finish().unwrap();
		}
	}
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate wlroots;

use std::process::Command;
use std::thread;

use wlroots::{project_box, Area, Capability, CompositorBuilder, CompositorHandle, Cursor,
              CursorHandle, CursorHandler, InputManagerHandler, KeyboardHandle, KeyboardHandler,
              Origin, OutputBuilder, OutputBuilderResult, OutputHandle, OutputHandler,
              OutputLayout, OutputLayoutHandle, OutputLayoutHandler, OutputManagerHandler,
              PointerHandle, PointerHandler, Renderer, Seat, SeatHandle, SeatHandler, Size,
              XCursorManager, XdgV6ShellHandler, XdgV6ShellManagerHandler, XdgV6ShellState,
              XdgV6ShellSurfaceHandle, SurfaceHandler, SurfaceHandle};
use wlroots::key_events::KeyEvent;
use wlroots::pointer_events::{AbsoluteMotionEvent, ButtonEvent, MotionEvent};
use wlroots::utils::{init_logging, L_DEBUG, current_time};
use wlroots::wlroots_sys::wlr_key_state::WLR_KEY_PRESSED;
use wlroots::xkbcommon::xkb::keysyms::{KEY_Escape, KEY_F1};

struct State {
    xcursor_manager: XCursorManager,
    keyboard: Option<KeyboardHandle>,
    layout: OutputLayoutHandle,
    cursor: CursorHandle,
    shells: Vec<XdgV6ShellSurfaceHandle>,
    seat_handle: Option<SeatHandle>
}

impl State {
    fn new(xcursor_manager: XCursorManager,
           layout: OutputLayoutHandle,
           cursor: CursorHandle)
           -> Self {
        State { xcursor_manager,
                layout,
                cursor,
                keyboard: None,
                seat_handle: None,
                shells: vec![] }
    }
}

compositor_data!(State);

struct SeatHandlerEx;

struct CursorEx;

impl CursorHandler for CursorEx {}

impl SeatHandler for SeatHandlerEx {}

struct XdgV6ShellHandlerEx;
struct XdgV6ShellManager;
struct OutputLayoutEx;

impl OutputLayoutHandler for OutputLayoutEx {}

impl XdgV6ShellHandler for XdgV6ShellHandlerEx {
    fn destroyed(&mut self, compositor: CompositorHandle, shell: XdgV6ShellSurfaceHandle) {
        with_handles!([(compositor: {compositor})] => {
            let state: &mut State = compositor.into();
            let weak = shell;
            if let Some(index) = state.shells.iter().position(|s| *s == weak) {
                state.shells.remove(index);
            }
        }).unwrap();
    }
}

struct SurfaceEx;

impl SurfaceHandler for SurfaceEx {
    fn on_commit(&mut self, _: CompositorHandle, surface: SurfaceHandle) {
        wlr_log!(L_DEBUG, "Commiting for surface {:?}", surface);
    }
}

impl XdgV6ShellManagerHandler for XdgV6ShellManager {
    fn new_surface(&mut self,
                   compositor: CompositorHandle,
                   shell: XdgV6ShellSurfaceHandle)
                   -> (Option<Box<XdgV6ShellHandler>>, Option<Box<SurfaceHandler>>) {
        with_handles!([(compositor: {compositor}), (shell: {shell})] => {
            shell.ping();
            let state: &mut State = compositor.into();
            state.shells.push(shell.weak_reference());
            with_handles!([(layout: {&mut state.layout})] => {
                for (mut output, _) in layout.outputs() {
                    with_handles!([(output: {output})] =>{
                        output.schedule_frame()
                    }).ok();
                }
            }).expect("Layout was destroyed");
        }).unwrap();
        (Some(Box::new(XdgV6ShellHandlerEx)), Some(Box::new(SurfaceEx)))
    }
}

struct OutputManager;

struct ExOutput;

struct InputManager;

struct ExPointer;

struct ExKeyboardHandler;

impl OutputManagerHandler for OutputManager {
    fn output_added<'output>(&mut self,
                             compositor: CompositorHandle,
                             builder: OutputBuilder<'output>)
                             -> Option<OutputBuilderResult<'output>> {
        let mut result = builder.build_best_mode(ExOutput);
        dehandle!(
            @compositor = {compositor}?;
            @output = {&mut result.output}?;
            let state: &mut State = compositor.data.downcast_mut().unwrap();
            let xcursor_manager = &mut state.xcursor_manager;
            // TODO use output config if present instead of auto
            let layout = &mut state.layout;
            let cursor = &mut state.cursor;
            @layout = {layout}?;
            @cursor = {cursor}?;
            layout.add_auto(output);
            cursor.attach_output_layout(layout);
            xcursor_manager.load(output.scale());
            xcursor_manager.set_cursor_image("left_ptr".to_string(), cursor);
            let (x, y) = cursor.coords();
            // https://en.wikipedia.org/wiki/Mouse_warping
            cursor.warp(None, x, y)
        ).unwrap().unwrap().unwrap().unwrap();
        Some(result)
    }
}

impl KeyboardHandler for ExKeyboardHandler {
    fn on_key(&mut self,
              compositor: CompositorHandle,
              _: KeyboardHandle,
              key_event: &KeyEvent) {
        dehandle!(
            @compositor = {compositor}?;
            for key in key_event.pressed_keys() {
                if key == KEY_Escape {
                    wlroots::terminate();
                } else if key_event.key_state() == WLR_KEY_PRESSED {
                    if key == KEY_F1 {
                        thread::spawn(move || {
                            Command::new("weston-terminal").output().unwrap();
                        });
                        return
                    }
                }
            };
            let state: &mut State = compositor.into();
            let seat_handle = state.seat_handle.clone().unwrap();
            seat_handle.run(|seat| {
                seat.keyboard_notify_key(key_event.time_msec(),
                                         key_event.keycode(),
                                         key_event.key_state() as u32);
            }).unwrap()
        ).unwrap();
    }
}

impl PointerHandler for ExPointer {
    fn on_motion_absolute(&mut self,
                          compositor: CompositorHandle,
                          _: PointerHandle,
                          event: &AbsoluteMotionEvent) {
        dehandle!(
            @compositor = {compositor}?;
            let state: &mut State = compositor.into();
            let (x, y) = event.pos();
            @cursor = {&state.cursor}?;
            cursor.warp_absolute(event.device(), x, y)
        ).unwrap().unwrap();
    }

    fn on_motion(&mut self, compositor: CompositorHandle, _: PointerHandle, event: &MotionEvent) {
        dehandle!(
            @compositor = {compositor}?;
            let state: &mut State = compositor.into();
            let (delta_x, delta_y) = event.delta();
            @cursor = {&state.cursor}?;
            cursor.move_to(event.device(), delta_x, delta_y)
        ).unwrap().unwrap();
    }

    fn on_button(&mut self, compositor: CompositorHandle, _: PointerHandle, _: &ButtonEvent) {
        dehandle!(
            @compositor = {compositor}?;
            let state: &mut State = compositor.into();
            let seat = state.seat_handle.clone().unwrap();
            let keyboard = state.keyboard.clone().unwrap();
            @shell = {&state.shells[0]}?;
            match shell.state() {
                Some(&mut XdgV6ShellState::TopLevel(ref mut toplevel)) => {
                    toplevel.set_activated(true);
                }
                _ => {}
            };
            let surface = shell.surface();
            @seat = {seat}?;
            @keyboard = {keyboard}?;
            @surface = {surface}?;
            seat.set_keyboard(keyboard.input_device());
            seat.keyboard_notify_enter(surface,
                                       &mut keyboard.keycodes(),
                                       &mut keyboard.get_modifier_masks())
        ).unwrap().unwrap().unwrap().unwrap().unwrap();
    }
}

impl OutputHandler for ExOutput {
    fn on_frame(&mut self, compositor: CompositorHandle, output: OutputHandle) {
        dehandle!(
            @compositor = {compositor}?;
            @output = {output}?;
            let state: &mut State = compositor.data.downcast_mut().unwrap();
            let renderer = compositor.renderer
                .as_mut()
                .expect("Compositor was not loaded with a renderer");
            let mut render_context = renderer.render(output, None);
            render_context.clear([0.25, 0.25, 0.25, 1.0]);
            render_shells(state, &mut render_context)
        ).unwrap().unwrap();
    }
}

impl InputManagerHandler for InputManager {
    fn pointer_added(&mut self,
                     _: CompositorHandle,
                     _: PointerHandle)
                     -> Option<Box<PointerHandler>> {
        Some(Box::new(ExPointer))
    }

    fn keyboard_added(&mut self,
                      compositor: CompositorHandle,
                      keyboard: KeyboardHandle)
                      -> Option<Box<KeyboardHandler>> {
        dehandle!(
            @compositor = {compositor}?;
            @keyboard = {keyboard}?;
            let state: &mut State = compositor.into();
            state.keyboard = Some(keyboard.weak_reference());
            @seat = {state.seat_handle.as_ref().unwrap()}?;
            seat.set_keyboard(keyboard.input_device())
        ).unwrap().unwrap().unwrap();
        Some(Box::new(ExKeyboardHandler))
    }
}

fn main() {
    init_logging(L_DEBUG, None);
    let cursor = Cursor::create(Box::new(CursorEx));
    let mut xcursor_manager =
        XCursorManager::create("default".to_string(), 24).expect("Could not create xcursor \
                                                                  manager");
    xcursor_manager.load(1.0);
    cursor.run(|c| xcursor_manager.set_cursor_image("left_ptr".to_string(), c))
          .unwrap();
    let layout = OutputLayout::create(Box::new(OutputLayoutEx));

    let mut compositor =
        CompositorBuilder::new().gles2(true)
                                .input_manager(Box::new(InputManager))
                                .output_manager(Box::new(OutputManager))
                                .xdg_shell_v6_manager(Box::new(XdgV6ShellManager))
                                .build_auto(State::new(xcursor_manager, layout, cursor));

    {
        let seat_handle =
            Seat::create(&mut compositor, "seat0".into(), Box::new(SeatHandlerEx));
        seat_handle.run(|seat| {
                            seat.set_capabilities(Capability::all());
                        })
                   .unwrap();
        let state: &mut State = (&mut compositor).into();
        state.seat_handle = Some(seat_handle);
    }
    compositor.run();
}

/// Render the shells in the current compositor state on the given output.
fn render_shells(state: &mut State, renderer: &mut Renderer) {
    let shells = state.shells.clone();
    for mut shell in shells {
        dehandle!(
            @shell = {shell}?;
            @surface = {shell.surface()}?;
            @layout = {&state.layout}?;
            let (width, height) = surface.current_state().size();
            let (render_width, render_height) =
                (width * renderer.output.scale() as i32,
                 height * renderer.output.scale() as i32);
            let (lx, ly) = (0.0, 0.0);
            let render_box = Area::new(Origin::new(lx as i32, ly as i32),
                                       Size::new(render_width,
                                                 render_height));
            if layout.intersects(renderer.output, render_box) {
                let transform = renderer.output.get_transform().invert();
                let matrix = project_box(render_box,
                                         transform,
                                         0.0,
                                         renderer.output
                                         .transform_matrix());
                renderer.render_texture_with_matrix(&surface.texture(),
                                                    matrix);
                surface.send_frame_done(current_time());
            };
            ()
        ).unwrap().unwrap().unwrap();
    }
}
<commit_msg>Use dehandle up to its limitation<commit_after>#[macro_use]
extern crate wlroots;

use std::process::Command;
use std::thread;

use wlroots::{project_box, Area, Capability, CompositorBuilder, CompositorHandle, Cursor,
              CursorHandle, CursorHandler, InputManagerHandler, KeyboardHandle, KeyboardHandler,
              Origin, OutputBuilder, OutputBuilderResult, OutputHandle, OutputHandler,
              OutputLayout, OutputLayoutHandle, OutputLayoutHandler, OutputManagerHandler,
              PointerHandle, PointerHandler, Renderer, Seat, SeatHandle, SeatHandler, Size,
              XCursorManager, XdgV6ShellHandler, XdgV6ShellManagerHandler, XdgV6ShellState,
              XdgV6ShellSurfaceHandle, SurfaceHandler, SurfaceHandle};
use wlroots::key_events::KeyEvent;
use wlroots::pointer_events::{AbsoluteMotionEvent, ButtonEvent, MotionEvent};
use wlroots::utils::{init_logging, L_DEBUG, current_time};
use wlroots::wlroots_sys::wlr_key_state::WLR_KEY_PRESSED;
use wlroots::xkbcommon::xkb::keysyms::{KEY_Escape, KEY_F1};

struct State {
    xcursor_manager: XCursorManager,
    keyboard: Option<KeyboardHandle>,
    layout: OutputLayoutHandle,
    cursor: CursorHandle,
    shells: Vec<XdgV6ShellSurfaceHandle>,
    seat_handle: Option<SeatHandle>
}

impl State {
    fn new(xcursor_manager: XCursorManager,
           layout: OutputLayoutHandle,
           cursor: CursorHandle)
           -> Self {
        State { xcursor_manager,
                layout,
                cursor,
                keyboard: None,
                seat_handle: None,
                shells: vec![] }
    }
}

compositor_data!(State);

struct SeatHandlerEx;

struct CursorEx;

impl CursorHandler for CursorEx {}

impl SeatHandler for SeatHandlerEx {}

struct XdgV6ShellHandlerEx;
struct XdgV6ShellManager;
struct OutputLayoutEx;

impl OutputLayoutHandler for OutputLayoutEx {}

impl XdgV6ShellHandler for XdgV6ShellHandlerEx {
    fn destroyed(&mut self, compositor: CompositorHandle, shell: XdgV6ShellSurfaceHandle) {
        with_handles!([(compositor: {compositor})] => {
            let state: &mut State = compositor.into();
            let weak = shell;
            if let Some(index) = state.shells.iter().position(|s| *s == weak) {
                state.shells.remove(index);
            }
        }).unwrap();
    }
}

struct SurfaceEx;

impl SurfaceHandler for SurfaceEx {
    fn on_commit(&mut self, _: CompositorHandle, surface: SurfaceHandle) {
        wlr_log!(L_DEBUG, "Commiting for surface {:?}", surface);
    }
}

impl XdgV6ShellManagerHandler for XdgV6ShellManager {
    fn new_surface(&mut self,
                   compositor: CompositorHandle,
                   shell: XdgV6ShellSurfaceHandle)
                   -> (Option<Box<XdgV6ShellHandler>>, Option<Box<SurfaceHandler>>) {
        dehandle!(
            @compositor = {compositor}?;
            @shell = {shell}?;
            shell.ping();
            let state: &mut State = compositor.into();
            state.shells.push(shell.weak_reference());
            @layout = {&state.layout}?;
            for (mut output, _) in layout.outputs() {
                with_handles!([(output: {output})] =>{
                    output.schedule_frame()
                }).ok();
            }
        ).unwrap().unwrap().unwrap();
        (Some(Box::new(XdgV6ShellHandlerEx)), Some(Box::new(SurfaceEx)))
    }
}

struct OutputManager;

struct ExOutput;

struct InputManager;

struct ExPointer;

struct ExKeyboardHandler;

impl OutputManagerHandler for OutputManager {
    fn output_added<'output>(&mut self,
                             compositor: CompositorHandle,
                             builder: OutputBuilder<'output>)
                             -> Option<OutputBuilderResult<'output>> {
        let mut result = builder.build_best_mode(ExOutput);
        dehandle!(
            @compositor = {compositor}?;
            @output = {&mut result.output}?;
            let state: &mut State = compositor.data.downcast_mut().unwrap();
            let xcursor_manager = &mut state.xcursor_manager;
            // TODO use output config if present instead of auto
            let layout = &mut state.layout;
            let cursor = &mut state.cursor;
            @layout = {layout}?;
            @cursor = {cursor}?;
            layout.add_auto(output);
            cursor.attach_output_layout(layout);
            xcursor_manager.load(output.scale());
            xcursor_manager.set_cursor_image("left_ptr".to_string(), cursor);
            let (x, y) = cursor.coords();
            // https://en.wikipedia.org/wiki/Mouse_warping
            cursor.warp(None, x, y)
        ).unwrap().unwrap().unwrap().unwrap();
        Some(result)
    }
}

impl KeyboardHandler for ExKeyboardHandler {
    fn on_key(&mut self,
              compositor: CompositorHandle,
              _: KeyboardHandle,
              key_event: &KeyEvent) {
        dehandle!(
            @compositor = {compositor}?;
            for key in key_event.pressed_keys() {
                if key == KEY_Escape {
                    wlroots::terminate();
                } else if key_event.key_state() == WLR_KEY_PRESSED {
                    if key == KEY_F1 {
                        thread::spawn(move || {
                            Command::new("weston-terminal").output().unwrap();
                        });
                        return
                    }
                }
            };
            let state: &mut State = compositor.into();
            let seat_handle = state.seat_handle.clone().unwrap();
            seat_handle.run(|seat| {
                seat.keyboard_notify_key(key_event.time_msec(),
                                         key_event.keycode(),
                                         key_event.key_state() as u32);
            }).unwrap()
        ).unwrap();
    }
}

impl PointerHandler for ExPointer {
    fn on_motion_absolute(&mut self,
                          compositor: CompositorHandle,
                          _: PointerHandle,
                          event: &AbsoluteMotionEvent) {
        dehandle!(
            @compositor = {compositor}?;
            let state: &mut State = compositor.into();
            let (x, y) = event.pos();
            @cursor = {&state.cursor}?;
            cursor.warp_absolute(event.device(), x, y)
        ).unwrap().unwrap();
    }

    fn on_motion(&mut self, compositor: CompositorHandle, _: PointerHandle, event: &MotionEvent) {
        dehandle!(
            @compositor = {compositor}?;
            let state: &mut State = compositor.into();
            let (delta_x, delta_y) = event.delta();
            @cursor = {&state.cursor}?;
            cursor.move_to(event.device(), delta_x, delta_y)
        ).unwrap().unwrap();
    }

    fn on_button(&mut self, compositor: CompositorHandle, _: PointerHandle, _: &ButtonEvent) {
        dehandle!(
            @compositor = {compositor}?;
            let state: &mut State = compositor.into();
            let seat = state.seat_handle.clone().unwrap();
            let keyboard = state.keyboard.clone().unwrap();
            @shell = {&state.shells[0]}?;
            match shell.state() {
                Some(&mut XdgV6ShellState::TopLevel(ref mut toplevel)) => {
                    toplevel.set_activated(true);
                }
                _ => {}
            };
            let surface = shell.surface();
            @seat = {seat}?;
            @keyboard = {keyboard}?;
            @surface = {surface}?;
            seat.set_keyboard(keyboard.input_device());
            seat.keyboard_notify_enter(surface,
                                       &mut keyboard.keycodes(),
                                       &mut keyboard.get_modifier_masks())
        ).unwrap().unwrap().unwrap().unwrap().unwrap();
    }
}

impl OutputHandler for ExOutput {
    fn on_frame(&mut self, compositor: CompositorHandle, output: OutputHandle) {
        dehandle!(
            @compositor = {compositor}?;
            @output = {output}?;
            let state: &mut State = compositor.data.downcast_mut().unwrap();
            let renderer = compositor.renderer
                .as_mut()
                .expect("Compositor was not loaded with a renderer");
            let mut render_context = renderer.render(output, None);
            render_context.clear([0.25, 0.25, 0.25, 1.0]);
            render_shells(state, &mut render_context)
        ).unwrap().unwrap();
    }
}

impl InputManagerHandler for InputManager {
    fn pointer_added(&mut self,
                     _: CompositorHandle,
                     _: PointerHandle)
                     -> Option<Box<PointerHandler>> {
        Some(Box::new(ExPointer))
    }

    fn keyboard_added(&mut self,
                      compositor: CompositorHandle,
                      keyboard: KeyboardHandle)
                      -> Option<Box<KeyboardHandler>> {
        dehandle!(
            @compositor = {compositor}?;
            @keyboard = {keyboard}?;
            let state: &mut State = compositor.into();
            state.keyboard = Some(keyboard.weak_reference());
            @seat = {state.seat_handle.as_ref().unwrap()}?;
            seat.set_keyboard(keyboard.input_device())
        ).unwrap().unwrap().unwrap();
        Some(Box::new(ExKeyboardHandler))
    }
}

fn main() {
    init_logging(L_DEBUG, None);
    let cursor = Cursor::create(Box::new(CursorEx));
    let mut xcursor_manager =
        XCursorManager::create("default".to_string(), 24).expect("Could not create xcursor \
                                                                  manager");
    xcursor_manager.load(1.0);
    cursor.run(|c| xcursor_manager.set_cursor_image("left_ptr".to_string(), c))
          .unwrap();
    let layout = OutputLayout::create(Box::new(OutputLayoutEx));

    let mut compositor =
        CompositorBuilder::new().gles2(true)
                                .input_manager(Box::new(InputManager))
                                .output_manager(Box::new(OutputManager))
                                .xdg_shell_v6_manager(Box::new(XdgV6ShellManager))
                                .build_auto(State::new(xcursor_manager, layout, cursor));

    {
        let seat_handle =
            Seat::create(&mut compositor, "seat0".into(), Box::new(SeatHandlerEx));
        seat_handle.run(|seat| {
                            seat.set_capabilities(Capability::all());
                        })
                   .unwrap();
        let state: &mut State = (&mut compositor).into();
        state.seat_handle = Some(seat_handle);
    }
    compositor.run();
}

/// Render the shells in the current compositor state on the given output.
fn render_shells(state: &mut State, renderer: &mut Renderer) {
    let shells = state.shells.clone();
    for mut shell in shells {
        dehandle!(
            @shell = {shell}?;
            @surface = {shell.surface()}?;
            @layout = {&state.layout}?;
            let (width, height) = surface.current_state().size();
            let (render_width, render_height) =
                (width * renderer.output.scale() as i32,
                 height * renderer.output.scale() as i32);
            let (lx, ly) = (0.0, 0.0);
            let render_box = Area::new(Origin::new(lx as i32, ly as i32),
                                       Size::new(render_width,
                                                 render_height));
            if layout.intersects(renderer.output, render_box) {
                let transform = renderer.output.get_transform().invert();
                let matrix = project_box(render_box,
                                         transform,
                                         0.0,
                                         renderer.output
                                         .transform_matrix());
                renderer.render_texture_with_matrix(&surface.texture(),
                                                    matrix);
                surface.send_frame_done(current_time());
            };
            ()
        ).unwrap().unwrap().unwrap();
    }
}
<|endoftext|>