<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/. */

use cssparser::Parser;
use euclid::size::Size2D;
use euclid::scale_factor::ScaleFactor;
use style::media_queries::{Device, MediaType};
use style::parser::ParserContext;
use style::stylesheets::{Origin, Stylesheet, CSSRuleIteratorExt};
use style::values::specified::{Length, LengthOrPercentageOrAuto};
use style::viewport::*;
use url::Url;

macro_rules! stylesheet {
    ($css:expr, $origin:ident) => {
        Stylesheet::from_str($css,
                             Url::parse("http://localhost").unwrap(),
                             Origin::$origin);
    }
}

fn test_viewport_rule<F>(css: &str,
                         device: &Device,
                         callback: F)
    where F: Fn(&Vec<ViewportDescriptorDeclaration>, &str)
{
    ::util::opts::set_experimental_enabled(true);

    let stylesheet = stylesheet!(css, Author);
    let mut rule_count = 0;
    for rule in stylesheet.effective_rules(&device).viewport() {
        rule_count += 1;
        callback(&rule.declarations, css);
    }
    assert!(rule_count > 0);
}

macro_rules! assert_decl_len {
    ($declarations:ident == 1) => {
        assert!($declarations.len() == 1,
                "expected 1 declaration; have {}: {:?})",
                $declarations.len(), $declarations)
    };
    ($declarations:ident == $len:expr) => {
        assert!($declarations.len() == $len,
                "expected {} declarations; have {}: {:?})",
                $len, $declarations.len(), $declarations)
    }
}

#[test]
fn empty_viewport_rule() {
    let device = Device::new(MediaType::Screen, Size2D::typed(800., 600.));

    test_viewport_rule("@viewport {}", &device, |declarations, css| {
        println!("{}", css);
        assert_decl_len!(declarations == 0);
    });
}

macro_rules! assert_decl_eq {
    ($d:expr, $origin:ident, $expected:ident: $value:expr) => {{
        assert_eq!($d.origin, Origin::$origin);
        assert_eq!($d.descriptor, ViewportDescriptor::$expected($value));
        assert!($d.important == false, "descriptor should not be !important");
    }};
    ($d:expr, $origin:ident, $expected:ident: $value:expr, !important) => {{
        assert_eq!($d.origin, Origin::$origin);
        assert_eq!($d.descriptor, ViewportDescriptor::$expected($value));
        assert!($d.important == true, "descriptor should be !important");
    }};
}

#[test]
fn simple_viewport_rules() {
    let device = Device::new(MediaType::Screen, Size2D::typed(800., 600.));

    test_viewport_rule("@viewport { width: auto; height: auto;\
                                    zoom: auto; min-zoom: 0; max-zoom: 200%;\
                                    user-zoom: zoom; orientation: auto; }",
                       &device, |declarations, css| {
        println!("{}", css);
        assert_decl_len!(declarations == 9);
        assert_decl_eq!(&declarations[0], Author, MinWidth: LengthOrPercentageOrAuto::Auto);
        assert_decl_eq!(&declarations[1], Author, MaxWidth: LengthOrPercentageOrAuto::Auto);
        assert_decl_eq!(&declarations[2], Author, MinHeight: LengthOrPercentageOrAuto::Auto);
        assert_decl_eq!(&declarations[3], Author, MaxHeight: LengthOrPercentageOrAuto::Auto);
        assert_decl_eq!(&declarations[4], Author, Zoom: Zoom::Auto);
        assert_decl_eq!(&declarations[5], Author, MinZoom: Zoom::Number(0.));
        assert_decl_eq!(&declarations[6], Author, MaxZoom: Zoom::Percentage(2.));
        assert_decl_eq!(&declarations[7], Author, UserZoom: UserZoom::Zoom);
        assert_decl_eq!(&declarations[8], Author, Orientation: Orientation::Auto);
    });

    test_viewport_rule("@viewport { min-width: 200px; max-width: auto;\
                                    min-height: 200px; max-height: auto; }",
                       &device, |declarations, css| {
        println!("{}", css);
        assert_decl_len!(declarations == 4);
        assert_decl_eq!(&declarations[0], Author, MinWidth: LengthOrPercentageOrAuto::Length(Length::from_px(200.)));
        assert_decl_eq!(&declarations[1], Author, MaxWidth: LengthOrPercentageOrAuto::Auto);
        assert_decl_eq!(&declarations[2], Author, MinHeight: LengthOrPercentageOrAuto::Length(Length::from_px(200.)));
        assert_decl_eq!(&declarations[3], Author, MaxHeight: LengthOrPercentageOrAuto::Auto);
    });
}

#[test]
fn cascading_within_viewport_rule() {
    let device = Device::new(MediaType::Screen, Size2D::typed(800., 600.));

    // normal order of appearance
    test_viewport_rule("@viewport { min-width: 200px; min-width: auto; }",
                       &device, |declarations, css| {
        println!("{}", css);
        assert_decl_len!(declarations == 1);
        assert_decl_eq!(&declarations[0], Author, MinWidth: LengthOrPercentageOrAuto::Auto);
    });

    // !important order of appearance
    test_viewport_rule("@viewport { min-width: 200px !important; min-width: auto !important; }",
                       &device, |declarations, css| {
        println!("{}", css);
        assert_decl_len!(declarations == 1);
        assert_decl_eq!(&declarations[0], Author, MinWidth: LengthOrPercentageOrAuto::Auto, !important);
    });

    // !important vs normal
    test_viewport_rule("@viewport { min-width: auto !important; min-width: 200px; }",
                       &device, |declarations, css| {
        println!("{}", css);
        assert_decl_len!(declarations == 1);
        assert_decl_eq!(&declarations[0], Author, MinWidth: LengthOrPercentageOrAuto::Auto, !important);
    });

    // normal longhands vs normal shorthand
    test_viewport_rule("@viewport { min-width: 200px; max-width: 200px; width: auto; }",
                       &device, |declarations, css| {
        println!("{}", css);
        assert_decl_len!(declarations == 2);
        assert_decl_eq!(&declarations[0], Author, MinWidth: LengthOrPercentageOrAuto::Auto);
        assert_decl_eq!(&declarations[1], Author, MaxWidth: LengthOrPercentageOrAuto::Auto);
    });

    // normal shorthand vs normal longhands
    test_viewport_rule("@viewport { width: 200px; min-width: auto; max-width: auto; }",
                       &device, |declarations, css| {
        println!("{}", css);
        assert_decl_len!(declarations == 2);
        assert_decl_eq!(&declarations[0], Author, MinWidth: LengthOrPercentageOrAuto::Auto);
        assert_decl_eq!(&declarations[1], Author, MaxWidth: LengthOrPercentageOrAuto::Auto);
    });

    // one !important longhand vs normal shorthand
    test_viewport_rule("@viewport { min-width: auto !important; width: 200px; }",
                       &device, |declarations, css| {
        println!("{}", css);
        assert_decl_len!(declarations == 2);
        assert_decl_eq!(&declarations[0], Author, MinWidth: LengthOrPercentageOrAuto::Auto, !important);
        assert_decl_eq!(&declarations[1], Author, MaxWidth: LengthOrPercentageOrAuto::Length(Length::from_px(200.)));
    });

    // both !important longhands vs normal shorthand
    test_viewport_rule("@viewport { min-width: auto !important; max-width: auto !important; width: 200px; }",
                       &device, |declarations, css| {
        println!("{}", css);
        assert_decl_len!(declarations == 2);
        assert_decl_eq!(&declarations[0], Author, MinWidth: LengthOrPercentageOrAuto::Auto, !important);
        assert_decl_eq!(&declarations[1], Author, MaxWidth: LengthOrPercentageOrAuto::Auto, !important);
    });
}

#[test]
fn multiple_stylesheets_cascading() {
    let device = Device::new(MediaType::Screen, Size2D::typed(800., 600.));

    let stylesheets = vec![
        stylesheet!("@viewport { min-width: 100px; min-height: 100px; zoom: 1; }", UserAgent),
        stylesheet!("@viewport { min-width: 200px; min-height: 200px; }", User),
        stylesheet!("@viewport { min-width: 300px; }", Author)];

    let declarations = stylesheets.iter()
        .flat_map(|s| s.effective_rules(&device).viewport())
        .cascade()
        .declarations;
    assert_decl_len!(declarations == 3);
    assert_decl_eq!(&declarations[0], UserAgent, Zoom: Zoom::Number(1.));
    assert_decl_eq!(&declarations[1], User, MinHeight: LengthOrPercentageOrAuto::Length(Length::from_px(200.)));
    assert_decl_eq!(&declarations[2], Author, MinWidth: LengthOrPercentageOrAuto::Length(Length::from_px(300.)));

    let stylesheets = vec![
        stylesheet!("@viewport { min-width: 100px !important; }", UserAgent),
        stylesheet!("@viewport { min-width: 200px !important; min-height: 200px !important; }", User),
        stylesheet!(
            "@viewport { min-width: 300px !important; min-height: 300px !important; zoom: 3 !important; }", Author)];

    let declarations = stylesheets.iter()
        .flat_map(|s| s.effective_rules(&device).viewport())
        .cascade()
        .declarations;
    assert_decl_len!(declarations == 3);
    assert_decl_eq!(
        &declarations[0], UserAgent, MinWidth: LengthOrPercentageOrAuto::Length(Length::from_px(100.)), !important);
    assert_decl_eq!(
        &declarations[1], User, MinHeight: LengthOrPercentageOrAuto::Length(Length::from_px(200.)), !important);
    assert_decl_eq!(&declarations[2], Author, Zoom: Zoom::Number(3.), !important);
}

#[test]
fn constrain_viewport() {
    let url = Url::parse("http://localhost").unwrap();
    let context = ParserContext::new(Origin::Author, &url);

    macro_rules! from_css {
        ($css:expr) => {
            &ViewportRule::parse(&mut Parser::new($css), &context).unwrap()
        }
    }

    let initial_viewport = Size2D::typed(800., 600.);
    assert_eq!(ViewportConstraints::maybe_new(initial_viewport, from_css!("")),
               None);

    let initial_viewport = Size2D::typed(800., 600.);
    assert_eq!(ViewportConstraints::maybe_new(initial_viewport, from_css!("width: 320px auto")),
               Some(ViewportConstraints {
                   size: initial_viewport,

                   initial_zoom: ScaleFactor::new(1.),
                   min_zoom: None,
                   max_zoom: None,

                   user_zoom: UserZoom::Zoom,
                   orientation: Orientation::Auto
               }));

    let initial_viewport = Size2D::typed(200., 150.);
    assert_eq!(ViewportConstraints::maybe_new(initial_viewport, from_css!("width: 320px auto")),
               Some(ViewportConstraints {
                   size: Size2D::typed(320., 240.),

                   initial_zoom: ScaleFactor::new(1.),
                   min_zoom: None,
                   max_zoom: None,

                   user_zoom: UserZoom::Zoom,
                   orientation: Orientation::Auto
               }));

    let initial_viewport = Size2D::typed(800., 600.);
    assert_eq!(ViewportConstraints::maybe_new(initial_viewport, from_css!("width: 320px auto")),
               Some(ViewportConstraints {
                   size: initial_viewport,

                   initial_zoom: ScaleFactor::new(1.),
                   min_zoom: None,
                   max_zoom: None,

                   user_zoom: UserZoom::Zoom,
                   orientation: Orientation::Auto
               }));

    let initial_viewport = Size2D::typed(800., 600.);
    assert_eq!(ViewportConstraints::maybe_new(initial_viewport, from_css!("width: 800px; height: 600px;\
                                                                     zoom: 1;\
                                                                     user-zoom: zoom;\
                                                                     orientation: auto;")),
               Some(ViewportConstraints {
                   size: initial_viewport,

                   initial_zoom: ScaleFactor::new(1.),
                   min_zoom: None,
                   max_zoom: None,

                   user_zoom: UserZoom::Zoom,
                   orientation: Orientation::Auto
               }));
}
<commit_msg>Auto merge of #6470 - Ms2ger:6466-viewport, r=metajack<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/. */

use cssparser::Parser;
use euclid::size::Size2D;
use euclid::scale_factor::ScaleFactor;
use style::media_queries::{Device, MediaType};
use style::parser::ParserContext;
use style::stylesheets::{Origin, Stylesheet, CSSRuleIteratorExt};
use style::values::specified::{Length, LengthOrPercentageOrAuto};
use style::viewport::*;
use url::Url;

macro_rules! stylesheet {
    ($css:expr, $origin:ident) => {
        Stylesheet::from_str($css,
                             Url::parse("http://localhost").unwrap(),
                             Origin::$origin);
    }
}

fn test_viewport_rule<F>(css: &str,
                         device: &Device,
                         callback: F)
    where F: Fn(&Vec<ViewportDescriptorDeclaration>, &str)
{
    ::util::opts::set_experimental_enabled(true);

    let stylesheet = stylesheet!(css, Author);
    let mut rule_count = 0;
    for rule in stylesheet.effective_rules(&device).viewport() {
        rule_count += 1;
        callback(&rule.declarations, css);
    }
    assert!(rule_count > 0);
}

macro_rules! assert_decl_len {
    ($declarations:ident == 1) => {
        assert!($declarations.len() == 1,
                "expected 1 declaration; have {}: {:?})",
                $declarations.len(), $declarations)
    };
    ($declarations:ident == $len:expr) => {
        assert!($declarations.len() == $len,
                "expected {} declarations; have {}: {:?})",
                $len, $declarations.len(), $declarations)
    }
}

#[test]
fn empty_viewport_rule() {
    let device = Device::new(MediaType::Screen, Size2D::typed(800., 600.));

    test_viewport_rule("@viewport {}", &device, |declarations, css| {
        println!("{}", css);
        assert_decl_len!(declarations == 0);
    });
}

macro_rules! assert_decl_eq {
    ($d:expr, $origin:ident, $expected:ident: $value:expr) => {{
        assert_eq!($d.origin, Origin::$origin);
        assert_eq!($d.descriptor, ViewportDescriptor::$expected($value));
        assert!($d.important == false, "descriptor should not be !important");
    }};
    ($d:expr, $origin:ident, $expected:ident: $value:expr, !important) => {{
        assert_eq!($d.origin, Origin::$origin);
        assert_eq!($d.descriptor, ViewportDescriptor::$expected($value));
        assert!($d.important == true, "descriptor should be !important");
    }};
}

#[test]
fn simple_viewport_rules() {
    let device = Device::new(MediaType::Screen, Size2D::typed(800., 600.));

    test_viewport_rule("@viewport { width: auto; height: auto;\
                                    zoom: auto; min-zoom: 0; max-zoom: 200%;\
                                    user-zoom: zoom; orientation: auto; }",
                       &device, |declarations, css| {
        println!("{}", css);
        assert_decl_len!(declarations == 9);
        assert_decl_eq!(&declarations[0], Author, MinWidth: LengthOrPercentageOrAuto::Auto);
        assert_decl_eq!(&declarations[1], Author, MaxWidth: LengthOrPercentageOrAuto::Auto);
        assert_decl_eq!(&declarations[2], Author, MinHeight: LengthOrPercentageOrAuto::Auto);
        assert_decl_eq!(&declarations[3], Author, MaxHeight: LengthOrPercentageOrAuto::Auto);
        assert_decl_eq!(&declarations[4], Author, Zoom: Zoom::Auto);
        assert_decl_eq!(&declarations[5], Author, MinZoom: Zoom::Number(0.));
        assert_decl_eq!(&declarations[6], Author, MaxZoom: Zoom::Percentage(2.));
        assert_decl_eq!(&declarations[7], Author, UserZoom: UserZoom::Zoom);
        assert_decl_eq!(&declarations[8], Author, Orientation: Orientation::Auto);
    });

    test_viewport_rule("@viewport { min-width: 200px; max-width: auto;\
                                    min-height: 200px; max-height: auto; }",
                       &device, |declarations, css| {
        println!("{}", css);
        assert_decl_len!(declarations == 4);
        assert_decl_eq!(&declarations[0], Author, MinWidth: LengthOrPercentageOrAuto::Length(Length::from_px(200.)));
        assert_decl_eq!(&declarations[1], Author, MaxWidth: LengthOrPercentageOrAuto::Auto);
        assert_decl_eq!(&declarations[2], Author, MinHeight: LengthOrPercentageOrAuto::Length(Length::from_px(200.)));
        assert_decl_eq!(&declarations[3], Author, MaxHeight: LengthOrPercentageOrAuto::Auto);
    });
}

#[test]
fn cascading_within_viewport_rule() {
    let device = Device::new(MediaType::Screen, Size2D::typed(800., 600.));

    // normal order of appearance
    test_viewport_rule("@viewport { min-width: 200px; min-width: auto; }",
                       &device, |declarations, css| {
        println!("{}", css);
        assert_decl_len!(declarations == 1);
        assert_decl_eq!(&declarations[0], Author, MinWidth: LengthOrPercentageOrAuto::Auto);
    });

    // !important order of appearance
    test_viewport_rule("@viewport { min-width: 200px !important; min-width: auto !important; }",
                       &device, |declarations, css| {
        println!("{}", css);
        assert_decl_len!(declarations == 1);
        assert_decl_eq!(&declarations[0], Author, MinWidth: LengthOrPercentageOrAuto::Auto, !important);
    });

    // !important vs normal
    test_viewport_rule("@viewport { min-width: auto !important; min-width: 200px; }",
                       &device, |declarations, css| {
        println!("{}", css);
        assert_decl_len!(declarations == 1);
        assert_decl_eq!(&declarations[0], Author, MinWidth: LengthOrPercentageOrAuto::Auto, !important);
    });

    // normal longhands vs normal shorthand
    test_viewport_rule("@viewport { min-width: 200px; max-width: 200px; width: auto; }",
                       &device, |declarations, css| {
        println!("{}", css);
        assert_decl_len!(declarations == 2);
        assert_decl_eq!(&declarations[0], Author, MinWidth: LengthOrPercentageOrAuto::Auto);
        assert_decl_eq!(&declarations[1], Author, MaxWidth: LengthOrPercentageOrAuto::Auto);
    });

    // normal shorthand vs normal longhands
    test_viewport_rule("@viewport { width: 200px; min-width: auto; max-width: auto; }",
                       &device, |declarations, css| {
        println!("{}", css);
        assert_decl_len!(declarations == 2);
        assert_decl_eq!(&declarations[0], Author, MinWidth: LengthOrPercentageOrAuto::Auto);
        assert_decl_eq!(&declarations[1], Author, MaxWidth: LengthOrPercentageOrAuto::Auto);
    });

    // one !important longhand vs normal shorthand
    test_viewport_rule("@viewport { min-width: auto !important; width: 200px; }",
                       &device, |declarations, css| {
        println!("{}", css);
        assert_decl_len!(declarations == 2);
        assert_decl_eq!(&declarations[0], Author, MinWidth: LengthOrPercentageOrAuto::Auto, !important);
        assert_decl_eq!(&declarations[1], Author, MaxWidth: LengthOrPercentageOrAuto::Length(Length::from_px(200.)));
    });

    // both !important longhands vs normal shorthand
    test_viewport_rule("@viewport { min-width: auto !important; max-width: auto !important; width: 200px; }",
                       &device, |declarations, css| {
        println!("{}", css);
        assert_decl_len!(declarations == 2);
        assert_decl_eq!(&declarations[0], Author, MinWidth: LengthOrPercentageOrAuto::Auto, !important);
        assert_decl_eq!(&declarations[1], Author, MaxWidth: LengthOrPercentageOrAuto::Auto, !important);
    });
}

#[test]
fn multiple_stylesheets_cascading() {
    ::util::opts::set_experimental_enabled(true);
    let device = Device::new(MediaType::Screen, Size2D::typed(800., 600.));

    let stylesheets = vec![
        stylesheet!("@viewport { min-width: 100px; min-height: 100px; zoom: 1; }", UserAgent),
        stylesheet!("@viewport { min-width: 200px; min-height: 200px; }", User),
        stylesheet!("@viewport { min-width: 300px; }", Author)];

    let declarations = stylesheets.iter()
        .flat_map(|s| s.effective_rules(&device).viewport())
        .cascade()
        .declarations;
    assert_decl_len!(declarations == 3);
    assert_decl_eq!(&declarations[0], UserAgent, Zoom: Zoom::Number(1.));
    assert_decl_eq!(&declarations[1], User, MinHeight: LengthOrPercentageOrAuto::Length(Length::from_px(200.)));
    assert_decl_eq!(&declarations[2], Author, MinWidth: LengthOrPercentageOrAuto::Length(Length::from_px(300.)));

    let stylesheets = vec![
        stylesheet!("@viewport { min-width: 100px !important; }", UserAgent),
        stylesheet!("@viewport { min-width: 200px !important; min-height: 200px !important; }", User),
        stylesheet!(
            "@viewport { min-width: 300px !important; min-height: 300px !important; zoom: 3 !important; }", Author)];

    let declarations = stylesheets.iter()
        .flat_map(|s| s.effective_rules(&device).viewport())
        .cascade()
        .declarations;
    assert_decl_len!(declarations == 3);
    assert_decl_eq!(
        &declarations[0], UserAgent, MinWidth: LengthOrPercentageOrAuto::Length(Length::from_px(100.)), !important);
    assert_decl_eq!(
        &declarations[1], User, MinHeight: LengthOrPercentageOrAuto::Length(Length::from_px(200.)), !important);
    assert_decl_eq!(&declarations[2], Author, Zoom: Zoom::Number(3.), !important);
}

#[test]
fn constrain_viewport() {
    let url = Url::parse("http://localhost").unwrap();
    let context = ParserContext::new(Origin::Author, &url);

    macro_rules! from_css {
        ($css:expr) => {
            &ViewportRule::parse(&mut Parser::new($css), &context).unwrap()
        }
    }

    let initial_viewport = Size2D::typed(800., 600.);
    assert_eq!(ViewportConstraints::maybe_new(initial_viewport, from_css!("")),
               None);

    let initial_viewport = Size2D::typed(800., 600.);
    assert_eq!(ViewportConstraints::maybe_new(initial_viewport, from_css!("width: 320px auto")),
               Some(ViewportConstraints {
                   size: initial_viewport,

                   initial_zoom: ScaleFactor::new(1.),
                   min_zoom: None,
                   max_zoom: None,

                   user_zoom: UserZoom::Zoom,
                   orientation: Orientation::Auto
               }));

    let initial_viewport = Size2D::typed(200., 150.);
    assert_eq!(ViewportConstraints::maybe_new(initial_viewport, from_css!("width: 320px auto")),
               Some(ViewportConstraints {
                   size: Size2D::typed(320., 240.),

                   initial_zoom: ScaleFactor::new(1.),
                   min_zoom: None,
                   max_zoom: None,

                   user_zoom: UserZoom::Zoom,
                   orientation: Orientation::Auto
               }));

    let initial_viewport = Size2D::typed(800., 600.);
    assert_eq!(ViewportConstraints::maybe_new(initial_viewport, from_css!("width: 320px auto")),
               Some(ViewportConstraints {
                   size: initial_viewport,

                   initial_zoom: ScaleFactor::new(1.),
                   min_zoom: None,
                   max_zoom: None,

                   user_zoom: UserZoom::Zoom,
                   orientation: Orientation::Auto
               }));

    let initial_viewport = Size2D::typed(800., 600.);
    assert_eq!(ViewportConstraints::maybe_new(initial_viewport, from_css!("width: 800px; height: 600px;\
                                                                     zoom: 1;\
                                                                     user-zoom: zoom;\
                                                                     orientation: auto;")),
               Some(ViewportConstraints {
                   size: initial_viewport,

                   initial_zoom: ScaleFactor::new(1.),
                   min_zoom: None,
                   max_zoom: None,

                   user_zoom: UserZoom::Zoom,
                   orientation: Orientation::Auto
               }));
}
<|endoftext|>


<commit_before>use std::marker::PhantomData;
use std::thread::sleep;
use std::time::Duration;

// TODO replace by configurable value
use super::FIRST_LINE_ADDRESS;
use super::{DisplayControlBuilder, EntryModeBuilder};

// TODO make configurable
// TODO add optional implementation using the busy flag
static E_DELAY: u32 = 5;
const LCD_WIDTH: usize = 16;

bitflags! {
    struct Instructions: u8 {
        const CLEAR_DISPLAY     = 0b00000001;
        const RETURN_HOME       = 0b00000010;
        const SHIFT             = 0b00010000;
        const SET_CGRAM         = 0b01000000;
        const SET_DDRAM         = 0b10000000;
    }
}

bitflags! {
    struct ShiftTarget: u8 {
        const CURSOR  = 0b00000000;
        const DISPLAY = 0b00001000;
    }
}

bitflags! {
    struct ShiftDirection: u8 {
        const RIGHT = 0b00000100;
        const LEFT  = 0b00000000;
    }
}

enum WriteMode {
    Command,
    Data,
}

enum ReadMode {
    Data,
    // TODO: use busy flag
    BusyFlag,
}

/// Enumeration of possible methods to shift a cursor or display.
pub enum ShiftTo {
    /// Shifts to the right by the given offset.
    Right(u8),
    /// Shifts to the left by the given offset.
    Left(u8),
}

impl ShiftTo {
    fn as_offset_and_raw_direction(&self) -> (u8, ShiftDirection) {
        match *self {
            ShiftTo::Right(offset) => (offset, RIGHT),
            ShiftTo::Left(offset) => (offset, LEFT),
        }
    }
}

/// Enumeration of possible methods to seek within a `Display` object.
pub enum SeekFrom<T: Into<u8>> {
    /// Sets the cursor position to `Home` plus the provided number of bytes.
    Home(u8),
    /// Sets the cursor to the current position plus the specified number of bytes.
    Current(u8),
    /// Sets the cursor position to the provides line plus the specified number of bytes.
    Line { line: T, bytes: u8 },
}

/// Enumeration of possible data directions of a pin.
pub enum Direction {
    In,
    Out,
}

/// The `DisplayHardwareLayer` trait is intended to be implemented by the library user as a thin
/// wrapper around the hardware specific system calls.
pub trait DisplayHardwareLayer {
    /// Initializes an I/O pin.
    fn init(&self) {}
    /// Cleanup an I/O pin.
    fn cleanup(&self) {}

    fn set_direction(&self, Direction);
    /// Sets a value on an I/O pin.
    // TODO need a way to let the user set up how levels are interpreted by the hardware
    fn set_value(&self, u8) -> Result<(), ()>;

    fn get_value(&self) -> u8;
}

pub struct DisplayPins {
    pub register_select: u64,
    pub read: u64,
    pub enable: u64,
    pub data4: u64,
    pub data5: u64,
    pub data6: u64,
    pub data7: u64,
}

/// A HD44780 compliant display.
///
/// It provides a high-level and hardware agnostic interface to controll a HD44780 compliant
/// liquid crystal display (LCD).
pub struct Display<T, U>
where
    T: From<u64> + DisplayHardwareLayer,
    U: Into<u8>,
{
    register_select: T,
    read: T,
    enable: T,
    data4: T,
    data5: T,
    data6: T,
    data7: T,
    cursor_address: u8,
    _marker: PhantomData<U>,
}

impl<T, U> Display<T, U>
where
    T: From<u64> + DisplayHardwareLayer,
    U: Into<u8>,
{
    /// Makes a new `Display` from a numeric pins configuration, given via `DisplayPins`.
    pub fn from_pins(pins: DisplayPins) -> Display<T, U> {
        let lcd = Display {
            register_select: T::from(pins.register_select),
            enable: T::from(pins.enable),
            read: T::from(pins.read),
            data4: T::from(pins.data4),
            data5: T::from(pins.data5),
            data6: T::from(pins.data6),
            data7: T::from(pins.data7),
            cursor_address: 0,
            _marker: PhantomData,
        };

        lcd.register_select.init();
        lcd.read.init();
        lcd.enable.init();
        lcd.data4.init();
        lcd.data5.init();
        lcd.data6.init();
        lcd.data7.init();

        lcd.read.set_value(0).unwrap();

        // Initializing by Instruction
        lcd.write_byte(0x33, WriteMode::Command);
        lcd.write_byte(0x32, WriteMode::Command);
        // FuctionSet: Data length 4bit + 2 lines
        lcd.write_byte(0x28, WriteMode::Command);
        // DisplayControl: Display on, Cursor off + cursor blinking off
        lcd.write_byte(0x0C, WriteMode::Command);
        // EntryModeSet: Cursor move direction inc + no display shift
        lcd.write_byte(0x06, WriteMode::Command);
        lcd.clear(); // ClearDisplay

        lcd
    }

    /// Sets the entry mode of the display using the builder given in the closure.
    pub fn set_entry_mode<F>(&self, f: F)
    where
        F: Fn(&mut EntryModeBuilder),
    {
        let mut builder = EntryModeBuilder::default();
        f(&mut builder);
        self.write_byte(builder.build_command(), WriteMode::Command);
    }

    /// Sets the display control settings using the builder given in the closure.
    pub fn set_display_control<F>(&self, f: F)
    where
        F: Fn(&mut DisplayControlBuilder),
    {
        let mut builder = DisplayControlBuilder::default();
        f(&mut builder);
        self.write_byte(builder.build_command(), WriteMode::Command);
    }

    /// Shifts the cursor to the left or the right by the given offset.
    ///
    /// **Note:** Consider to use [seek()](struct.Display.html#method.seek) for longer distances.
    pub fn shift_cursor(&mut self, direction: ShiftTo) {
        let (offset, raw_direction) = direction.as_offset_and_raw_direction();

        match direction {
            ShiftTo::Right(offset) => self.cursor_address += offset,
            ShiftTo::Left(offset) => self.cursor_address -= offset,
        }

        self.raw_shift(CURSOR, offset, raw_direction);
    }

    /// Shifts the display to the right or the left by the given offset.
    ///
    /// Note that the first and second line will shift at the same time.
    ///
    /// When the displayed data is shifted repeatedly each line moves only horizontally.
    /// The second line display does not shift into the first line position.
    pub fn shift(&self, direction: ShiftTo) {
        let (offset, raw_direction) = direction.as_offset_and_raw_direction();

        self.raw_shift(DISPLAY, offset, raw_direction);
    }

    fn raw_shift(&self, shift_type: ShiftTarget, offset: u8, raw_direction: ShiftDirection) {
        let mut cmd = SHIFT.bits();

        cmd |= shift_type.bits();
        cmd |= raw_direction.bits();

        for _ in 0..offset {
            self.write_byte(cmd, WriteMode::Command);
        }
    }

    /// Clears the entire display, sets the cursor to the home position and undo all display
    /// shifts.
    ///
    /// It also sets the cursor's move direction to `Increment`.
    pub fn clear(&self) {
        self.write_byte(CLEAR_DISPLAY.bits(), WriteMode::Command);
    }

    /// Seeks to an offset in display data RAM.
    pub fn seek(&mut self, pos: SeekFrom<U>) {
        let mut cmd = SET_DDRAM.bits();

        let (start, bytes) = match pos {
            SeekFrom::Home(bytes) => (FIRST_LINE_ADDRESS, bytes),
            SeekFrom::Current(bytes) => (self.cursor_address, bytes),
            SeekFrom::Line { line, bytes } => (line.into(), bytes),
        };

        self.cursor_address = start + bytes;

        cmd |= self.cursor_address;

        self.write_byte(cmd, WriteMode::Command);
    }

    /// Seeks to an offset in display character generator RAM.
    pub fn seek_cgram(&mut self, pos: SeekFrom<U>) {
        let mut cmd = SET_CGRAM.bits();

        let (start, bytes) = match pos {
            SeekFrom::Home(bytes) => (FIRST_LINE_ADDRESS, bytes),
            SeekFrom::Current(bytes) => (self.cursor_address, bytes),
            SeekFrom::Line { line, bytes } => (line.into(), bytes),
        };
        self.cursor_address = start + bytes;

        cmd |= self.cursor_address;

        self.write_byte(cmd, WriteMode::Command);
    }

    fn write_byte(&self, value: u8, mode: WriteMode) {
        let wait_time = Duration::new(0, E_DELAY);

        self.read.set_value(0).unwrap();
        self.data4.set_direction(Direction::Out);
        self.data5.set_direction(Direction::Out);
        self.data6.set_direction(Direction::Out);
        self.data7.set_direction(Direction::Out);

        match mode {
            WriteMode::Data => self.register_select.set_value(1),
            WriteMode::Command => self.register_select.set_value(0),
        }.unwrap();

        self.data4.set_value(0).unwrap();
        self.data5.set_value(0).unwrap();
        self.data6.set_value(0).unwrap();
        self.data7.set_value(0).unwrap();

        if value & 0x10 == 0x10 {
            self.data4.set_value(1).unwrap();
        }
        if value & 0x20 == 0x20 {
            self.data5.set_value(1).unwrap();
        }
        if value & 0x40 == 0x40 {
            self.data6.set_value(1).unwrap();
        }
        if value & 0x80 == 0x80 {
            self.data7.set_value(1).unwrap();
        }

        sleep(wait_time);
        self.enable.set_value(1).unwrap();
        sleep(wait_time);
        self.enable.set_value(0).unwrap();
        sleep(wait_time);

        self.data4.set_value(0).unwrap();
        self.data5.set_value(0).unwrap();
        self.data6.set_value(0).unwrap();
        self.data7.set_value(0).unwrap();

        if value & 0x01 == 0x01 {
            self.data4.set_value(1).unwrap();
        }
        if value & 0x02 == 0x02 {
            self.data5.set_value(1).unwrap();
        }
        if value & 0x04 == 0x04 {
            self.data6.set_value(1).unwrap();
        }
        if value & 0x08 == 0x08 {
            self.data7.set_value(1).unwrap();
        }

        sleep(wait_time);
        self.enable.set_value(1).unwrap();
        sleep(wait_time);
        self.enable.set_value(0).unwrap();
        sleep(wait_time);
    }

    fn read_raw_byte(&self, mode: ReadMode) -> u8 {
        let mut result = 0u8;
        let wait_time = Duration::new(0, 10);

        self.data4.set_direction(Direction::In);
        self.data5.set_direction(Direction::In);
        self.data6.set_direction(Direction::In);
        self.data7.set_direction(Direction::In);

        match mode {
            ReadMode::Data => self.register_select.set_value(1),
            ReadMode::BusyFlag => self.register_select.set_value(0),
        }.unwrap();

        self.read.set_value(1).unwrap();
        sleep(Duration::new(0, 45));

        self.enable.set_value(1).unwrap();
        sleep(Duration::new(0, 165));

        result |= self.data7.get_value() << 7;
        result |= self.data6.get_value() << 6;
        result |= self.data5.get_value() << 5;
        result |= self.data4.get_value() << 4;

        self.enable.set_value(0).unwrap();
        sleep(wait_time);
        self.enable.set_value(1).unwrap();
        sleep(Duration::new(0, 165));

        result |= self.data7.get_value() << 3;
        result |= self.data6.get_value() << 2;
        result |= self.data5.get_value() << 1;
        result |= self.data4.get_value();

        self.enable.set_value(0).unwrap();
        sleep(wait_time);

        result
    }

    /// Reads a single byte from data RAM.
    pub fn read_byte(&mut self) -> u8 {
        self.cursor_address += 1;
        self.read_raw_byte(ReadMode::Data)
    }

    /// Reads busy flag and the cursor's current address.
    pub fn read_busy_flag(&self) -> (bool, u8) {
        let byte = self.read_raw_byte(ReadMode::BusyFlag);

        let busy_flag = (byte & 0b10000000) != 0;

        let address = byte & 0b01111111;

        (busy_flag, address)
    }

    /// Writes the given message to data or character generator RAM, depending on the previous
    /// seek operation.
    pub fn write_message(&mut self, msg: &str) {
        for c in msg.as_bytes().iter().take(LCD_WIDTH) {
            self.cursor_address += 1;
            self.write_byte(*c, WriteMode::Data);
        }
    }
}

impl<T, U> Drop for Display<T, U>
where
    T: From<u64> + DisplayHardwareLayer,
    U: Into<u8>,
{
    fn drop(&mut self) {
        self.register_select.cleanup();
        self.enable.cleanup();
        self.data4.cleanup();
        self.data5.cleanup();
        self.data6.cleanup();
        self.data7.cleanup();
    }
}
<commit_msg>Refactor duplicate code<commit_after>use std::marker::PhantomData;
use std::thread::sleep;
use std::time::Duration;

// TODO replace by configurable value
use super::FIRST_LINE_ADDRESS;
use super::{DisplayControlBuilder, EntryModeBuilder};

// TODO make configurable
// TODO add optional implementation using the busy flag
static E_DELAY: u32 = 5;
const LCD_WIDTH: usize = 16;

bitflags! {
    struct Instructions: u8 {
        const CLEAR_DISPLAY     = 0b00000001;
        const RETURN_HOME       = 0b00000010;
        const SHIFT             = 0b00010000;
    }
}

bitflags! {
    struct ShiftTarget: u8 {
        const CURSOR  = 0b00000000;
        const DISPLAY = 0b00001000;
    }
}

bitflags! {
    struct ShiftDirection: u8 {
        const RIGHT = 0b00000100;
        const LEFT  = 0b00000000;
    }
}

enum WriteMode {
    Command,
    Data,
}

enum ReadMode {
    Data,
    // TODO: use busy flag
    BusyFlag,
}

enum RamType {
    DisplayData,
    CharacterGenerator,
}

impl From<RamType> for u8 {
    fn from(ram_type: RamType) -> Self {
        match ram_type {
            RamType::DisplayData => 0b10000000,
            RamType::CharacterGenerator => 0b01000000,
        }
    }
}

/// Enumeration of possible methods to shift a cursor or display.
pub enum ShiftTo {
    /// Shifts to the right by the given offset.
    Right(u8),
    /// Shifts to the left by the given offset.
    Left(u8),
}

impl ShiftTo {
    fn as_offset_and_raw_direction(&self) -> (u8, ShiftDirection) {
        match *self {
            ShiftTo::Right(offset) => (offset, RIGHT),
            ShiftTo::Left(offset) => (offset, LEFT),
        }
    }
}

/// Enumeration of possible methods to seek within a `Display` object.
pub enum SeekFrom<T: Into<u8>> {
    /// Sets the cursor position to `Home` plus the provided number of bytes.
    Home(u8),
    /// Sets the cursor to the current position plus the specified number of bytes.
    Current(u8),
    /// Sets the cursor position to the provides line plus the specified number of bytes.
    Line { line: T, bytes: u8 },
}

/// Enumeration of possible data directions of a pin.
pub enum Direction {
    In,
    Out,
}

/// The `DisplayHardwareLayer` trait is intended to be implemented by the library user as a thin
/// wrapper around the hardware specific system calls.
pub trait DisplayHardwareLayer {
    /// Initializes an I/O pin.
    fn init(&self) {}
    /// Cleanup an I/O pin.
    fn cleanup(&self) {}

    fn set_direction(&self, Direction);
    /// Sets a value on an I/O pin.
    // TODO need a way to let the user set up how levels are interpreted by the hardware
    fn set_value(&self, u8) -> Result<(), ()>;

    fn get_value(&self) -> u8;
}

pub struct DisplayPins {
    pub register_select: u64,
    pub read: u64,
    pub enable: u64,
    pub data4: u64,
    pub data5: u64,
    pub data6: u64,
    pub data7: u64,
}

/// A HD44780 compliant display.
///
/// It provides a high-level and hardware agnostic interface to controll a HD44780 compliant
/// liquid crystal display (LCD).
pub struct Display<T, U>
where
    T: From<u64> + DisplayHardwareLayer,
    U: Into<u8>,
{
    register_select: T,
    read: T,
    enable: T,
    data4: T,
    data5: T,
    data6: T,
    data7: T,
    cursor_address: u8,
    _marker: PhantomData<U>,
}

impl<T, U> Display<T, U>
where
    T: From<u64> + DisplayHardwareLayer,
    U: Into<u8>,
{
    /// Makes a new `Display` from a numeric pins configuration, given via `DisplayPins`.
    pub fn from_pins(pins: DisplayPins) -> Display<T, U> {
        let lcd = Display {
            register_select: T::from(pins.register_select),
            enable: T::from(pins.enable),
            read: T::from(pins.read),
            data4: T::from(pins.data4),
            data5: T::from(pins.data5),
            data6: T::from(pins.data6),
            data7: T::from(pins.data7),
            cursor_address: 0,
            _marker: PhantomData,
        };

        lcd.register_select.init();
        lcd.read.init();
        lcd.enable.init();
        lcd.data4.init();
        lcd.data5.init();
        lcd.data6.init();
        lcd.data7.init();

        lcd.read.set_value(0).unwrap();

        // Initializing by Instruction
        lcd.write_byte(0x33, WriteMode::Command);
        lcd.write_byte(0x32, WriteMode::Command);
        // FuctionSet: Data length 4bit + 2 lines
        lcd.write_byte(0x28, WriteMode::Command);
        // DisplayControl: Display on, Cursor off + cursor blinking off
        lcd.write_byte(0x0C, WriteMode::Command);
        // EntryModeSet: Cursor move direction inc + no display shift
        lcd.write_byte(0x06, WriteMode::Command);
        lcd.clear(); // ClearDisplay

        lcd
    }

    /// Sets the entry mode of the display using the builder given in the closure.
    pub fn set_entry_mode<F>(&self, f: F)
    where
        F: Fn(&mut EntryModeBuilder),
    {
        let mut builder = EntryModeBuilder::default();
        f(&mut builder);
        self.write_byte(builder.build_command(), WriteMode::Command);
    }

    /// Sets the display control settings using the builder given in the closure.
    pub fn set_display_control<F>(&self, f: F)
    where
        F: Fn(&mut DisplayControlBuilder),
    {
        let mut builder = DisplayControlBuilder::default();
        f(&mut builder);
        self.write_byte(builder.build_command(), WriteMode::Command);
    }

    /// Shifts the cursor to the left or the right by the given offset.
    ///
    /// **Note:** Consider to use [seek()](struct.Display.html#method.seek) for longer distances.
    pub fn shift_cursor(&mut self, direction: ShiftTo) {
        let (offset, raw_direction) = direction.as_offset_and_raw_direction();

        match direction {
            ShiftTo::Right(offset) => self.cursor_address += offset,
            ShiftTo::Left(offset) => self.cursor_address -= offset,
        }

        self.raw_shift(CURSOR, offset, raw_direction);
    }

    /// Shifts the display to the right or the left by the given offset.
    ///
    /// Note that the first and second line will shift at the same time.
    ///
    /// When the displayed data is shifted repeatedly each line moves only horizontally.
    /// The second line display does not shift into the first line position.
    pub fn shift(&self, direction: ShiftTo) {
        let (offset, raw_direction) = direction.as_offset_and_raw_direction();

        self.raw_shift(DISPLAY, offset, raw_direction);
    }

    fn raw_shift(&self, shift_type: ShiftTarget, offset: u8, raw_direction: ShiftDirection) {
        let mut cmd = SHIFT.bits();

        cmd |= shift_type.bits();
        cmd |= raw_direction.bits();

        for _ in 0..offset {
            self.write_byte(cmd, WriteMode::Command);
        }
    }

    /// Clears the entire display, sets the cursor to the home position and undo all display
    /// shifts.
    ///
    /// It also sets the cursor's move direction to `Increment`.
    pub fn clear(&self) {
        self.write_byte(CLEAR_DISPLAY.bits(), WriteMode::Command);
    }

    fn generic_seek(&mut self, ram_type: RamType, pos: SeekFrom<U>) {
        let mut cmd = ram_type.into();

        let (start, bytes) = match pos {
            SeekFrom::Home(bytes) => (FIRST_LINE_ADDRESS, bytes),
            SeekFrom::Current(bytes) => (self.cursor_address, bytes),
            SeekFrom::Line { line, bytes } => (line.into(), bytes),
        };

        self.cursor_address = start + bytes;

        cmd |= self.cursor_address;

        self.write_byte(cmd, WriteMode::Command);
    }

    /// Seeks to an offset in display data RAM.
    pub fn seek(&mut self, pos: SeekFrom<U>) {
        self.generic_seek(RamType::DisplayData, pos);
    }

    /// Seeks to an offset in display character generator RAM.
    pub fn seek_cgram(&mut self, pos: SeekFrom<U>) {
        self.generic_seek(RamType::CharacterGenerator, pos);
    }

    fn write_byte(&self, value: u8, mode: WriteMode) {
        let wait_time = Duration::new(0, E_DELAY);

        self.read.set_value(0).unwrap();
        self.data4.set_direction(Direction::Out);
        self.data5.set_direction(Direction::Out);
        self.data6.set_direction(Direction::Out);
        self.data7.set_direction(Direction::Out);

        match mode {
            WriteMode::Data => self.register_select.set_value(1),
            WriteMode::Command => self.register_select.set_value(0),
        }.unwrap();

        self.data4.set_value(0).unwrap();
        self.data5.set_value(0).unwrap();
        self.data6.set_value(0).unwrap();
        self.data7.set_value(0).unwrap();

        if value & 0x10 == 0x10 {
            self.data4.set_value(1).unwrap();
        }
        if value & 0x20 == 0x20 {
            self.data5.set_value(1).unwrap();
        }
        if value & 0x40 == 0x40 {
            self.data6.set_value(1).unwrap();
        }
        if value & 0x80 == 0x80 {
            self.data7.set_value(1).unwrap();
        }

        sleep(wait_time);
        self.enable.set_value(1).unwrap();
        sleep(wait_time);
        self.enable.set_value(0).unwrap();
        sleep(wait_time);

        self.data4.set_value(0).unwrap();
        self.data5.set_value(0).unwrap();
        self.data6.set_value(0).unwrap();
        self.data7.set_value(0).unwrap();

        if value & 0x01 == 0x01 {
            self.data4.set_value(1).unwrap();
        }
        if value & 0x02 == 0x02 {
            self.data5.set_value(1).unwrap();
        }
        if value & 0x04 == 0x04 {
            self.data6.set_value(1).unwrap();
        }
        if value & 0x08 == 0x08 {
            self.data7.set_value(1).unwrap();
        }

        sleep(wait_time);
        self.enable.set_value(1).unwrap();
        sleep(wait_time);
        self.enable.set_value(0).unwrap();
        sleep(wait_time);
    }

    fn read_raw_byte(&self, mode: ReadMode) -> u8 {
        let mut result = 0u8;
        let wait_time = Duration::new(0, 10);

        self.data4.set_direction(Direction::In);
        self.data5.set_direction(Direction::In);
        self.data6.set_direction(Direction::In);
        self.data7.set_direction(Direction::In);

        match mode {
            ReadMode::Data => self.register_select.set_value(1),
            ReadMode::BusyFlag => self.register_select.set_value(0),
        }.unwrap();

        self.read.set_value(1).unwrap();
        sleep(Duration::new(0, 45));

        self.enable.set_value(1).unwrap();
        sleep(Duration::new(0, 165));

        result |= self.data7.get_value() << 7;
        result |= self.data6.get_value() << 6;
        result |= self.data5.get_value() << 5;
        result |= self.data4.get_value() << 4;

        self.enable.set_value(0).unwrap();
        sleep(wait_time);
        self.enable.set_value(1).unwrap();
        sleep(Duration::new(0, 165));

        result |= self.data7.get_value() << 3;
        result |= self.data6.get_value() << 2;
        result |= self.data5.get_value() << 1;
        result |= self.data4.get_value();

        self.enable.set_value(0).unwrap();
        sleep(wait_time);

        result
    }

    /// Reads a single byte from data RAM.
    pub fn read_byte(&mut self) -> u8 {
        self.cursor_address += 1;
        self.read_raw_byte(ReadMode::Data)
    }

    /// Reads busy flag and the cursor's current address.
    pub fn read_busy_flag(&self) -> (bool, u8) {
        let byte = self.read_raw_byte(ReadMode::BusyFlag);

        let busy_flag = (byte & 0b10000000) != 0;

        let address = byte & 0b01111111;

        (busy_flag, address)
    }

    /// Writes the given message to data or character generator RAM, depending on the previous
    /// seek operation.
    pub fn write_message(&mut self, msg: &str) {
        for c in msg.as_bytes().iter().take(LCD_WIDTH) {
            self.cursor_address += 1;
            self.write_byte(*c, WriteMode::Data);
        }
    }
}

impl<T, U> Drop for Display<T, U>
where
    T: From<u64> + DisplayHardwareLayer,
    U: Into<u8>,
{
    fn drop(&mut self) {
        self.register_select.cleanup();
        self.enable.cleanup();
        self.data4.cleanup();
        self.data5.cleanup();
        self.data6.cleanup();
        self.data7.cleanup();
    }
}
<|endoftext|>


<commit_before>
/*
# Minimal gif encoder
```
use std::fs::File;

// Get pixel data from some source
let mut pixels = unimplemented!();
// Create frame from data
let frame = Frame::from_rgb(100, 100, &mut *pixels);
// Create encoder
let encoder = gif::Encoder::new(File::create("some.gif").unwrap(), frame.width, frame.height);
// Write header to file
let mut encoder = encoder.write_global_palette(&[]).unwrap();
// Write frame to file
encoder.write_frame(&frame).unwrap();
```
*/

use std::cmp::min;
use std::io;
use std::io::prelude::*;
use std::slice::bytes;

use lzw;

use traits::WriteBytesExt;
use common::{Block, Frame, Extension, DisposalMethod};

pub enum ExtensionData {
	Control { flags: u8, delay: u16, trns: u8 }
}

impl ExtensionData {
	pub fn new_control_ext(delay: u16, dispose: DisposalMethod,
						   needs_user_input: bool, trns: Option<u8>) -> ExtensionData {
		let mut flags = 0;
		let trns = match trns {
			Some(trns) => {
				flags |= 1;
				trns as u8
			},
			None => 0
		};
		flags |= (needs_user_input as u8) << 1;
		flags |= (dispose as u8) << 2;
		ExtensionData::Control {
			flags: flags,
			delay: delay,
			trns: trns
		}
	}
}

struct BlockWriter<'a, W: Write + 'a> {
	w: &'a mut W,
	bytes: usize,
	buf: [u8; 0xFF]
}

impl<'a, W: Write + 'a> BlockWriter<'a, W> {
	fn new(w: &'a mut W) -> BlockWriter<'a, W> {
		BlockWriter {
			w: w,
			bytes: 0,
			buf: [0; 0xFF]
		}
	}
}

impl<'a, W: Write + 'a> Write for BlockWriter<'a, W> {

	fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
		let to_copy = min(buf.len(), 0xFF - self.bytes);
		bytes::copy_memory(&buf[..to_copy], &mut self.buf[self.bytes..]);
		self.bytes += to_copy;
		if self.bytes == 0xFF {
			self.bytes = 0;
			try!(self.w.write_le(0xFFu8));
			try!(self.w.write_all(&self.buf));
		}
		Ok(to_copy)
	}
	fn flush(&mut self) -> io::Result<()> {
		return Err(io::Error::new(
			io::ErrorKind::Other,
			"Cannot flush a BlockWriter, use `drop` instead."
		))
	}
}

impl<'a, W: Write + 'a> Drop for BlockWriter<'a, W> {

    #[cfg(feature = "raii_no_panic")]
	fn drop(&mut self) {
		if self.bytes > 0 {
			let _ = self.w.write_le(self.bytes as u8);
			let _ = self.w.write_all(&self.buf[..self.bytes]);
		}
	}

    #[cfg(not(feature = "raii_no_panic"))]
	fn drop(&mut self) {
		if self.bytes > 0 {
			self.w.write_le(self.bytes as u8).unwrap();
			self.w.write_all(&self.buf[..self.bytes]).unwrap();
		}
	}
}

pub struct Encoder<'a, W: Write + 'a> {
	w: &'a mut W,
	global_palette: bool,
	width: u16,
	height: u16
}

pub struct HeaderWritten<'a, W: Write + 'a> {
	enc: Encoder<'a, W>
}

impl<'a, W: Write + 'a> HeaderWritten<'a, W> {
	/// Writes a complete frame to the image
	///
	/// Note: This function also writes a control extention if necessary.
	pub fn write_frame(&mut self, frame: &Frame) -> io::Result<()> {
		self.enc.write_frame(frame)
	}

	/// Writes an extension to the image
	pub fn write_extension(&mut self, extension: ExtensionData) -> io::Result<()> {
		self.enc.write_extension(extension)
	}

	/// Writes a raw extension to the image
	pub fn write_raw_extension(&mut self, func: u8, data: &[u8]) -> io::Result<()> {
		self.enc.write_raw_extension(func, data)
	}
}

impl<'a, W: Write + 'a> Drop for HeaderWritten<'a, W> {

    #[cfg(feature = "raii_no_panic")]
	fn drop(&mut self) {
		let _ = self.enc.w.write_le(Block::Trailer as u8);
	}

    #[cfg(not(feature = "raii_no_panic"))]
	fn drop(&mut self) {
		self.enc.w.write_le(Block::Trailer as u8).unwrap()
	}
}

/*

pub struct Frame {
    pub delay: u16,
    pub dispose: DisposalMethod,
    pub transparent: Option<usize>,
    pub needs_user_input: bool,
    pub top: u16,
    pub left: u16,
    pub width: u16,
    pub height: u16,
    pub interlaced: bool,
    pub palette: Option<Vec<u8>>,
    pub buffer: Vec<u8>
}

*/
impl<'a, W: Write + 'a> Encoder<'a, W> {
	pub fn new(w: &'a mut W, width: u16, height: u16) -> Self {
		Encoder {
			w: w,
			global_palette: false,
			width: width,
			height: height
		}
	}

	/// Writes the global color palette
	pub fn write_global_palette(mut self, palette: &[u8]) -> io::Result<HeaderWritten<'a, W>> {
		self.global_palette = true;
		let mut flags = 0;
		flags |= 0b1000_0000;
		let num_colors = palette.len() / 3;
		flags |= flag_size(num_colors);
		flags |= flag_size(num_colors) << 4; // wtf flag
		try!(self.write_screen_desc(flags));
		try!(self.write_color_table(palette));
		Ok(HeaderWritten {
			enc: self
		})
	}

	/// Writes a complete frame to the image
	///
	/// Note: This function also writes a control extension if necessary.
	fn write_frame(&mut self, frame: &Frame) -> io::Result<()> {
		// TODO commented off to pass test in lib.rs
		//if frame.delay > 0 || frame.transparent.is_some() {
			try!(self.write_extension(ExtensionData::new_control_ext(
				frame.delay,
				frame.dispose,
				frame.needs_user_input,
				frame.transparent

			)));
		//}
		try!(self.w.write_le(Block::Image as u8));
		try!(self.w.write_le(frame.left));
		try!(self.w.write_le(frame.top));
		try!(self.w.write_le(frame.width));
		try!(self.w.write_le(frame.height));
		let mut flags = 0;
		try!(match frame.palette {
			Some(ref palette) => {
				flags |= 0b1000_0000;
				let num_colors = palette.len() / 3;
				flags |= flag_size(num_colors);
				try!(self.w.write_le(flags));
				self.write_color_table(palette)
			},
			None => if !self.global_palette {
				return Err(io::Error::new(
					io::ErrorKind::InvalidInput,
					"The GIF format requires a color palette but none was given."
				))
			} else {
				self.w.write_le(flags)
			}
		});
		self.write_image_block(&frame.buffer)
	}

	fn write_image_block(&mut self, data: &[u8]) -> io::Result<()> {
		{
			let min_code_size: u8 = flag_size((*data.iter().max().unwrap_or(&0) as usize + 1)) + 1;
			try!(self.w.write_le(min_code_size));
			let mut bw = BlockWriter::new(&mut self.w);
			let mut enc = try!(lzw::Encoder::new(lzw::LsbWriter::new(&mut bw), min_code_size));
			try!(enc.encode_bytes(data));
		}
		self.w.write_le(0u8)
	}

	fn write_color_table(&mut self, table: &[u8]) -> io::Result<()> {
		let num_colors = table.len() / 3;
        let size = flag_size(num_colors);
		try!(self.w.write_all(&table[..num_colors * 3]));
        // Waste some space as of gif spec
        for _ in 0..((2 << size) - num_colors) {
            try!(self.w.write_all(&[0, 0, 0]))
        }
        Ok(())
	}

	/// Writes an extension to the image
	fn write_extension(&mut self, extension: ExtensionData) -> io::Result<()> {
		use self::ExtensionData::*;
		try!(self.w.write_le(Block::Extension as u8));
		match extension {
			Control { flags, delay, trns } => {
				try!(self.w.write_le(Extension::Control as u8));
				try!(self.w.write_le(4u8));
				try!(self.w.write_le(flags));
				try!(self.w.write_le(delay));
				try!(self.w.write_le(trns));
			}
		}
		self.w.write_le(0u8)
	}

	/// Writes a raw extension to the image
	fn write_raw_extension(&mut self, func: u8, data: &[u8]) -> io::Result<()> {
		try!(self.w.write_le(Block::Extension as u8));
		try!(self.w.write_le(func as u8));
		for chunk in data.chunks(0xFF) {
			try!(self.w.write_le(chunk.len() as u8));
			try!(self.w.write_all(chunk));
		}
		self.w.write_le(0u8)
	}

	/// Writes the logical screen desriptor
	fn write_screen_desc(&mut self, flags: u8) -> io::Result<()> {
		try!(self.w.write_all(b"GIF89a"));
		try!(self.w.write_le(self.width));
		try!(self.w.write_le(self.height));
		try!(self.w.write_le(flags)); // packed field
		try!(self.w.write_le(0u8)); // bg index
		self.w.write_le(0u8) // aspect ratio
	}
}

// Color table size converted to flag bits
fn flag_size(size: usize) -> u8 {
    match size {
        0  ...2   => 0,
        3  ...4   => 1,
        5  ...8   => 2,
        7  ...16  => 3,
        17 ...32  => 4,
        33 ...64  => 5,
        65 ...128 => 6,
        129...256 => 7,
        _ => 7
    }
}<commit_msg>take writer by value<commit_after>
/*
# Minimal gif encoder
```
use std::fs::File;

// Get pixel data from some source
let mut pixels = unimplemented!();
// Create frame from data
let frame = Frame::from_rgb(100, 100, &mut *pixels);
// Create encoder
let encoder = gif::Encoder::new(File::create("some.gif").unwrap(), frame.width, frame.height);
// Write header to file
let mut encoder = encoder.write_global_palette(&[]).unwrap();
// Write frame to file
encoder.write_frame(&frame).unwrap();
```
*/

use std::cmp::min;
use std::io;
use std::io::prelude::*;
use std::slice::bytes;

use lzw;

use traits::WriteBytesExt;
use common::{Block, Frame, Extension, DisposalMethod};

pub enum ExtensionData {
	Control { flags: u8, delay: u16, trns: u8 }
}

impl ExtensionData {
	pub fn new_control_ext(delay: u16, dispose: DisposalMethod,
						   needs_user_input: bool, trns: Option<u8>) -> ExtensionData {
		let mut flags = 0;
		let trns = match trns {
			Some(trns) => {
				flags |= 1;
				trns as u8
			},
			None => 0
		};
		flags |= (needs_user_input as u8) << 1;
		flags |= (dispose as u8) << 2;
		ExtensionData::Control {
			flags: flags,
			delay: delay,
			trns: trns
		}
	}
}

struct BlockWriter<'a, W: Write + 'a> {
	w: &'a mut W,
	bytes: usize,
	buf: [u8; 0xFF]
}

impl<'a, W: Write + 'a> BlockWriter<'a, W> {
	fn new(w: &'a mut W) -> BlockWriter<'a, W> {
		BlockWriter {
			w: w,
			bytes: 0,
			buf: [0; 0xFF]
		}
	}
}

impl<'a, W: Write + 'a> Write for BlockWriter<'a, W> {

	fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
		let to_copy = min(buf.len(), 0xFF - self.bytes);
		bytes::copy_memory(&buf[..to_copy], &mut self.buf[self.bytes..]);
		self.bytes += to_copy;
		if self.bytes == 0xFF {
			self.bytes = 0;
			try!(self.w.write_le(0xFFu8));
			try!(self.w.write_all(&self.buf));
		}
		Ok(to_copy)
	}
	fn flush(&mut self) -> io::Result<()> {
		return Err(io::Error::new(
			io::ErrorKind::Other,
			"Cannot flush a BlockWriter, use `drop` instead."
		))
	}
}

impl<'a, W: Write + 'a> Drop for BlockWriter<'a, W> {

    #[cfg(feature = "raii_no_panic")]
	fn drop(&mut self) {
		if self.bytes > 0 {
			let _ = self.w.write_le(self.bytes as u8);
			let _ = self.w.write_all(&self.buf[..self.bytes]);
		}
	}

    #[cfg(not(feature = "raii_no_panic"))]
	fn drop(&mut self) {
		if self.bytes > 0 {
			self.w.write_le(self.bytes as u8).unwrap();
			self.w.write_all(&self.buf[..self.bytes]).unwrap();
		}
	}
}

/// Wrapper for `Encoder` that indicates that the file headers have been written.
pub struct HeaderWritten<W: Write> {
	enc: Encoder<W>
}

impl<W: Write> HeaderWritten<W> {
	/// Writes a complete frame to the image
	///
	/// Note: This function also writes a control extention if necessary.
	pub fn write_frame(&mut self, frame: &Frame) -> io::Result<()> {
		self.enc.write_frame(frame)
	}

	/// Writes an extension to the image
	pub fn write_extension(&mut self, extension: ExtensionData) -> io::Result<()> {
		self.enc.write_extension(extension)
	}

	/// Writes a raw extension to the image
	pub fn write_raw_extension(&mut self, func: u8, data: &[u8]) -> io::Result<()> {
		self.enc.write_raw_extension(func, data)
	}
}

impl<W: Write> Drop for HeaderWritten<W> {

    #[cfg(feature = "raii_no_panic")]
	fn drop(&mut self) {
		let _ = self.enc.w.write_le(Block::Trailer as u8);
	}

    #[cfg(not(feature = "raii_no_panic"))]
	fn drop(&mut self) {
		self.enc.w.write_le(Block::Trailer as u8).unwrap()
	}
}

/*

pub struct Frame {
    pub delay: u16,
    pub dispose: DisposalMethod,
    pub transparent: Option<usize>,
    pub needs_user_input: bool,
    pub top: u16,
    pub left: u16,
    pub width: u16,
    pub height: u16,
    pub interlaced: bool,
    pub palette: Option<Vec<u8>>,
    pub buffer: Vec<u8>
}

*/

/// Minimal GIF encoder.
pub struct Encoder<W: Write> {
    w: W,
    global_palette: bool,
    width: u16,
    height: u16
}

impl<W: Write> Encoder<W> {
    /// Creates a new encoder.
	pub fn new(w: W, width: u16, height: u16) -> Self {
		Encoder {
			w: w,
			global_palette: false,
			width: width,
			height: height
		}
	}

	/// Writes the global color palette
	pub fn write_global_palette(mut self, palette: &[u8]) -> io::Result<HeaderWritten<W>> {
		self.global_palette = true;
		let mut flags = 0;
		flags |= 0b1000_0000;
		let num_colors = palette.len() / 3;
		flags |= flag_size(num_colors);
		flags |= flag_size(num_colors) << 4; // wtf flag
		try!(self.write_screen_desc(flags));
		try!(self.write_color_table(palette));
		Ok(HeaderWritten {
			enc: self
		})
	}

	/// Writes a complete frame to the image
	///
	/// Note: This function also writes a control extension if necessary.
	fn write_frame(&mut self, frame: &Frame) -> io::Result<()> {
		// TODO commented off to pass test in lib.rs
		//if frame.delay > 0 || frame.transparent.is_some() {
			try!(self.write_extension(ExtensionData::new_control_ext(
				frame.delay,
				frame.dispose,
				frame.needs_user_input,
				frame.transparent

			)));
		//}
		try!(self.w.write_le(Block::Image as u8));
		try!(self.w.write_le(frame.left));
		try!(self.w.write_le(frame.top));
		try!(self.w.write_le(frame.width));
		try!(self.w.write_le(frame.height));
		let mut flags = 0;
		try!(match frame.palette {
			Some(ref palette) => {
				flags |= 0b1000_0000;
				let num_colors = palette.len() / 3;
				flags |= flag_size(num_colors);
				try!(self.w.write_le(flags));
				self.write_color_table(palette)
			},
			None => if !self.global_palette {
				return Err(io::Error::new(
					io::ErrorKind::InvalidInput,
					"The GIF format requires a color palette but none was given."
				))
			} else {
				self.w.write_le(flags)
			}
		});
		self.write_image_block(&frame.buffer)
	}

	fn write_image_block(&mut self, data: &[u8]) -> io::Result<()> {
		{
			let min_code_size: u8 = flag_size((*data.iter().max().unwrap_or(&0) as usize + 1)) + 1;
			try!(self.w.write_le(min_code_size));
			let mut bw = BlockWriter::new(&mut self.w);
			let mut enc = try!(lzw::Encoder::new(lzw::LsbWriter::new(&mut bw), min_code_size));
			try!(enc.encode_bytes(data));
		}
		self.w.write_le(0u8)
	}

	fn write_color_table(&mut self, table: &[u8]) -> io::Result<()> {
		let num_colors = table.len() / 3;
        let size = flag_size(num_colors);
		try!(self.w.write_all(&table[..num_colors * 3]));
        // Waste some space as of gif spec
        for _ in 0..((2 << size) - num_colors) {
            try!(self.w.write_all(&[0, 0, 0]))
        }
        Ok(())
	}

	/// Writes an extension to the image
	fn write_extension(&mut self, extension: ExtensionData) -> io::Result<()> {
		use self::ExtensionData::*;
		try!(self.w.write_le(Block::Extension as u8));
		match extension {
			Control { flags, delay, trns } => {
				try!(self.w.write_le(Extension::Control as u8));
				try!(self.w.write_le(4u8));
				try!(self.w.write_le(flags));
				try!(self.w.write_le(delay));
				try!(self.w.write_le(trns));
			}
		}
		self.w.write_le(0u8)
	}

	/// Writes a raw extension to the image
	fn write_raw_extension(&mut self, func: u8, data: &[u8]) -> io::Result<()> {
		try!(self.w.write_le(Block::Extension as u8));
		try!(self.w.write_le(func as u8));
		for chunk in data.chunks(0xFF) {
			try!(self.w.write_le(chunk.len() as u8));
			try!(self.w.write_all(chunk));
		}
		self.w.write_le(0u8)
	}

	/// Writes the logical screen desriptor
	fn write_screen_desc(&mut self, flags: u8) -> io::Result<()> {
		try!(self.w.write_all(b"GIF89a"));
		try!(self.w.write_le(self.width));
		try!(self.w.write_le(self.height));
		try!(self.w.write_le(flags)); // packed field
		try!(self.w.write_le(0u8)); // bg index
		self.w.write_le(0u8) // aspect ratio
	}
}

// Color table size converted to flag bits
fn flag_size(size: usize) -> u8 {
    match size {
        0  ...2   => 0,
        3  ...4   => 1,
        5  ...8   => 2,
        7  ...16  => 3,
        17 ...32  => 4,
        33 ...64  => 5,
        65 ...128 => 6,
        129...256 => 7,
        _ => 7
    }
}<|endoftext|>


<commit_before>// Copyright 2015 The Gfx-rs Developers.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use libc;
use log::LogLevel;
use std::rc::Rc;
use std::slice;

use {gl, tex};
use gfx::device as d;
use gfx::device::handle;
use gfx::device::handle::Producer;
use gfx::device::mapping::Builder;

use Buffer;
use Resources as R;

pub fn update_sub_buffer(gl: &gl::Gl, buffer: Buffer, address: *const u8,
                         size: usize, offset: usize, role: d::BufferRole) {
    let target = match role {
        d::BufferRole::Vertex => gl::ARRAY_BUFFER,
        d::BufferRole::Index  => gl::ELEMENT_ARRAY_BUFFER,
    };
    unsafe { gl.BindBuffer(target, buffer) };
    unsafe {
        gl.BufferSubData(target,
            offset as gl::types::GLintptr,
            size as gl::types::GLsizeiptr,
            address as *const gl::types::GLvoid
        );
    }
}

/// GL resource factory.
pub struct Factory {
    caps: d::Capabilities,
    gl: Rc<gl::Gl>,
    main_fbo: handle::FrameBuffer<R>,
    handles: handle::Manager<R>,
    frame_handles: handle::Manager<R>,
}

/// Create a new `Factory`.
pub fn create(caps: d::Capabilities, gl: Rc<gl::Gl>) -> Factory {
    let mut handles = handle::Manager::new();

    Factory {
        caps: caps,
        gl: gl,
        main_fbo: handles.make_frame_buffer(0),
        handles: handles,
        frame_handles: handle::Manager::new(),
    }
}

impl Factory {
    fn create_buffer_internal(&mut self) -> Buffer {
        let mut name = 0 as Buffer;
        unsafe {
            self.gl.GenBuffers(1, &mut name);
        }
        info!("\tCreated buffer {}", name);
        name
    }

    fn init_buffer(&mut self, buffer: Buffer, info: &d::BufferInfo) {
        let target = match info.role {
            d::BufferRole::Vertex => gl::ARRAY_BUFFER,
            d::BufferRole::Index  => gl::ELEMENT_ARRAY_BUFFER,
        };
        unsafe { self.gl.BindBuffer(target, buffer) };
        let usage = match info.usage {
            d::BufferUsage::Static  => gl::STATIC_DRAW,
            d::BufferUsage::Dynamic => gl::DYNAMIC_DRAW,
            d::BufferUsage::Stream  => gl::STREAM_DRAW,
        };
        unsafe {
            self.gl.BufferData(target,
                info.size as gl::types::GLsizeiptr,
                0 as *const gl::types::GLvoid,
                usage
            );
        }
    }
}

#[allow(raw_pointer_derive)]
#[derive(Copy, Clone)]
pub struct RawMapping {
    pub pointer: *mut libc::c_void,
    target: gl::types::GLenum,
}

impl d::mapping::Raw for RawMapping {
    unsafe fn set<T>(&self, index: usize, val: T) {
        *(self.pointer as *mut T).offset(index as isize) = val;
    }

    unsafe fn to_slice<T>(&self, len: usize) -> &[T] {
        slice::from_raw_parts(self.pointer as *const T, len)
    }

    unsafe fn to_mut_slice<T>(&self, len: usize) -> &mut [T] {
        slice::from_raw_parts_mut(self.pointer as *mut T, len)
    }
}

impl d::Factory<R> for Factory {
    type Mapper = RawMapping;

    fn create_buffer_raw(&mut self, size: usize, usage: d::BufferUsage)
                         -> handle::RawBuffer<R> {
        let name = self.create_buffer_internal();
        let info = d::BufferInfo {
            role: d::BufferRole::Vertex,
            usage: usage,
            size: size,
        };
        self.init_buffer(name, &info);
        self.handles.make_buffer(name, info)
    }

    fn create_buffer_static_raw(&mut self, data: &[u8], role: d::BufferRole)
                                -> handle::RawBuffer<R> {
        let name = self.create_buffer_internal();

        let info = d::BufferInfo {
            role: role,
            usage: d::BufferUsage::Static,
            size: data.len(),
        };
        self.init_buffer(name, &info);
        update_sub_buffer(&self.gl, name, data.as_ptr(), data.len(), 0, role);
        self.handles.make_buffer(name, info)
    }

    fn create_array_buffer(&mut self) -> Result<handle::ArrayBuffer<R>, ()> {
        if self.caps.array_buffer_supported {
            let mut name = 0 as ::ArrayBuffer;
            unsafe {
                self.gl.GenVertexArrays(1, &mut name);
            }
            info!("\tCreated array buffer {}", name);
            Ok(self.handles.make_array_buffer(name))
        } else {
            error!("\tarray buffer creation unsupported, ignored");
            Err(())
        }
    }

    fn create_shader(&mut self, stage: d::shade::Stage, code: &[u8])
                     -> Result<handle::Shader<R>, d::shade::CreateShaderError> {
        let (name, info) = ::shade::create_shader(&self.gl, stage, code);
        info.map(|info| {
            let level = if name.is_err() { LogLevel::Error } else { LogLevel::Warn };
            log!(level, "\tShader compile log: {}", info);
        });
        name.map(|sh| self.handles.make_shader(sh, stage))
    }

    fn create_program(&mut self, shaders: &[handle::Shader<R>],
                      targets: Option<&[&str]>)
                      -> Result<handle::Program<R>, ()> {
        let objects: Vec<::Shader> = shaders.iter()
            .map(|h| self.frame_handles.ref_shader(h))
            .collect();
        let (prog, log) = ::shade::create_program(&self.gl, &self.caps,
                                                  objects.into_iter(), targets);
        log.map(|log| {
            let level = if prog.is_err() { LogLevel::Error } else { LogLevel::Warn };
            log!(level, "\tProgram link log: {}", log);
        });
        prog.map(|(name, info)| self.handles.make_program(name, info))
    }

    fn create_frame_buffer(&mut self) -> handle::FrameBuffer<R> {
        if !self.caps.render_targets_supported {
            panic!("No framebuffer objects, can't make a new one!");
        }

        let mut name = 0 as ::FrameBuffer;
        unsafe {
            self.gl.GenFramebuffers(1, &mut name);
        }
        info!("\tCreated frame buffer {}", name);
        self.handles.make_frame_buffer(name)
    }

    fn create_surface(&mut self, info: d::tex::SurfaceInfo) ->
                      Result<handle::Surface<R>, d::tex::SurfaceError> {
        if info.format.does_convert_gamma() && !self.caps.srgb_color_supported {
            return Err(d::tex::SurfaceError::UnsupportedGamma)
        }
        tex::make_surface(&self.gl, &info)
            .map(|suf| self.handles.make_surface(suf, info))
    }

    fn create_texture(&mut self, info: d::tex::TextureInfo) ->
                      Result<handle::Texture<R>, d::tex::TextureError> {
        if info.width == 0 || info.height == 0 || info.levels == 0 {
            return Err(d::tex::TextureError::InvalidInfo(info))
        }
        if info.format.does_convert_gamma() && !self.caps.srgb_color_supported {
            return Err(d::tex::TextureError::UnsupportedGamma)
        }

        let name = if self.caps.immutable_storage_supported {
            tex::make_with_storage(&self.gl, &info)
        } else {
            tex::make_without_storage(&self.gl, &info)
        };
        name.map(|tex| self.handles.make_texture(tex, info))
    }

    fn create_sampler(&mut self, info: d::tex::SamplerInfo)
                      -> handle::Sampler<R> {
        let sam = if self.caps.sampler_objects_supported {
            tex::make_sampler(&self.gl, &info)
        } else {
            0
        };
        self.handles.make_sampler(sam, info)
    }

    fn get_main_frame_buffer(&self) -> handle::FrameBuffer<R> {
        self.main_fbo.clone()
    }

    fn update_buffer_raw(&mut self, buffer: &handle::RawBuffer<R>,
                         data: &[u8], offset_bytes: usize) {
        debug_assert!(offset_bytes + data.len() <= buffer.get_info().size);
        let raw_handle = self.frame_handles.ref_buffer(buffer);
        update_sub_buffer(&self.gl, raw_handle, data.as_ptr(), data.len(),
                          offset_bytes, buffer.get_info().role)
    }

    fn update_texture_raw(&mut self, texture: &handle::Texture<R>,
                          img: &d::tex::ImageInfo, data: &[u8],
                          optkind: Option<d::tex::TextureKind>)
                          -> Result<(), d::tex::TextureError> {

        // use the specified texture kind if set for this update, otherwise
        // fall back on the kind that was set when the texture was created.
        let kind = optkind.unwrap_or(texture.get_info().kind);

        tex::update_texture(&self.gl, kind,
                            self.frame_handles.ref_texture(texture),
                            img, data.as_ptr(), data.len())
    }

    fn generate_mipmap(&mut self, texture: &handle::Texture<R>) {
        tex::generate_mipmap(&self.gl, texture.get_info().kind,
                             self.frame_handles.ref_texture(texture));
    }

    fn map_buffer_raw(&mut self, buf: &handle::RawBuffer<R>,
                      access: d::MapAccess) -> RawMapping {
        let raw_handle = self.frame_handles.ref_buffer(buf);
        unsafe { self.gl.BindBuffer(gl::ARRAY_BUFFER, raw_handle) };
        let ptr = unsafe { self.gl.MapBuffer(gl::ARRAY_BUFFER, match access {
            d::MapAccess::Readable => gl::READ_ONLY,
            d::MapAccess::Writable => gl::WRITE_ONLY,
            d::MapAccess::RW => gl::READ_WRITE
        }) } as *mut libc::c_void;
        RawMapping {
            pointer: ptr,
            target: gl::ARRAY_BUFFER
        }
    }

    fn unmap_buffer_raw(&mut self, map: RawMapping) {
        unsafe { self.gl.UnmapBuffer(map.target) };
    }

    fn map_buffer_readable<T: Copy>(&mut self, buf: &handle::Buffer<R, T>)
                           -> d::mapping::Readable<T, R, Factory> {
        let map = self.map_buffer_raw(buf.raw(), d::MapAccess::Readable);
        self.map_readable(map, buf.len())
    }

    fn map_buffer_writable<T: Copy>(&mut self, buf: &handle::Buffer<R, T>)
                                    -> d::mapping::Writable<T, R, Factory> {
        let map = self.map_buffer_raw(buf.raw(), d::MapAccess::Writable);
        self.map_writable(map, buf.len())
    }

    fn map_buffer_rw<T: Copy>(&mut self, buf: &handle::Buffer<R, T>)
                              -> d::mapping::RW<T, R, Factory> {
        let map = self.map_buffer_raw(buf.raw(), d::MapAccess::RW);
        self.map_read_write(map, buf.len())
    }

    fn cleanup(&mut self) {
        self.handles.clean_with(&mut self.gl,
            |gl, v| unsafe { gl.DeleteBuffers(1, v) },
            |gl, v| unsafe { gl.DeleteVertexArrays(1, v) },
            |gl, v| unsafe { gl.DeleteShader(*v) },
            |gl, v| unsafe { gl.DeleteProgram(*v) },
            |gl, v| unsafe { gl.DeleteFramebuffers(1, v) },
            |gl, v| unsafe { gl.DeleteRenderbuffers(1, v) },
            |gl, v| unsafe { gl.DeleteTextures(1, v) },
            |gl, v| unsafe { gl.DeleteSamplers(1, v) });
        self.frame_handles.clear();
    }
}
<commit_msg>Moved get_main_frame_buffer into the Factory methods<commit_after>// Copyright 2015 The Gfx-rs Developers.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use libc;
use log::LogLevel;
use std::rc::Rc;
use std::slice;

use {gl, tex};
use gfx::device as d;
use gfx::device::handle;
use gfx::device::handle::Producer;
use gfx::device::mapping::Builder;

use Buffer;
use Resources as R;

pub fn update_sub_buffer(gl: &gl::Gl, buffer: Buffer, address: *const u8,
                         size: usize, offset: usize, role: d::BufferRole) {
    let target = match role {
        d::BufferRole::Vertex => gl::ARRAY_BUFFER,
        d::BufferRole::Index  => gl::ELEMENT_ARRAY_BUFFER,
    };
    unsafe { gl.BindBuffer(target, buffer) };
    unsafe {
        gl.BufferSubData(target,
            offset as gl::types::GLintptr,
            size as gl::types::GLsizeiptr,
            address as *const gl::types::GLvoid
        );
    }
}

/// GL resource factory.
pub struct Factory {
    caps: d::Capabilities,
    gl: Rc<gl::Gl>,
    main_fbo: handle::FrameBuffer<R>,
    handles: handle::Manager<R>,
    frame_handles: handle::Manager<R>,
}

/// Create a new `Factory`.
pub fn create(caps: d::Capabilities, gl: Rc<gl::Gl>) -> Factory {
    let mut handles = handle::Manager::new();

    Factory {
        caps: caps,
        gl: gl,
        main_fbo: handles.make_frame_buffer(0),
        handles: handles,
        frame_handles: handle::Manager::new(),
    }
}

impl Factory {
    fn create_buffer_internal(&mut self) -> Buffer {
        let mut name = 0 as Buffer;
        unsafe {
            self.gl.GenBuffers(1, &mut name);
        }
        info!("\tCreated buffer {}", name);
        name
    }

    fn init_buffer(&mut self, buffer: Buffer, info: &d::BufferInfo) {
        let target = match info.role {
            d::BufferRole::Vertex => gl::ARRAY_BUFFER,
            d::BufferRole::Index  => gl::ELEMENT_ARRAY_BUFFER,
        };
        unsafe { self.gl.BindBuffer(target, buffer) };
        let usage = match info.usage {
            d::BufferUsage::Static  => gl::STATIC_DRAW,
            d::BufferUsage::Dynamic => gl::DYNAMIC_DRAW,
            d::BufferUsage::Stream  => gl::STREAM_DRAW,
        };
        unsafe {
            self.gl.BufferData(target,
                info.size as gl::types::GLsizeiptr,
                0 as *const gl::types::GLvoid,
                usage
            );
        }
    }

    pub fn get_main_frame_buffer(&self) -> handle::FrameBuffer<R> {
        self.main_fbo.clone()
    }
}

#[allow(raw_pointer_derive)]
#[derive(Copy, Clone)]
pub struct RawMapping {
    pub pointer: *mut libc::c_void,
    target: gl::types::GLenum,
}

impl d::mapping::Raw for RawMapping {
    unsafe fn set<T>(&self, index: usize, val: T) {
        *(self.pointer as *mut T).offset(index as isize) = val;
    }

    unsafe fn to_slice<T>(&self, len: usize) -> &[T] {
        slice::from_raw_parts(self.pointer as *const T, len)
    }

    unsafe fn to_mut_slice<T>(&self, len: usize) -> &mut [T] {
        slice::from_raw_parts_mut(self.pointer as *mut T, len)
    }
}

impl d::Factory<R> for Factory {
    type Mapper = RawMapping;

    fn create_buffer_raw(&mut self, size: usize, usage: d::BufferUsage)
                         -> handle::RawBuffer<R> {
        let name = self.create_buffer_internal();
        let info = d::BufferInfo {
            role: d::BufferRole::Vertex,
            usage: usage,
            size: size,
        };
        self.init_buffer(name, &info);
        self.handles.make_buffer(name, info)
    }

    fn create_buffer_static_raw(&mut self, data: &[u8], role: d::BufferRole)
                                -> handle::RawBuffer<R> {
        let name = self.create_buffer_internal();

        let info = d::BufferInfo {
            role: role,
            usage: d::BufferUsage::Static,
            size: data.len(),
        };
        self.init_buffer(name, &info);
        update_sub_buffer(&self.gl, name, data.as_ptr(), data.len(), 0, role);
        self.handles.make_buffer(name, info)
    }

    fn create_array_buffer(&mut self) -> Result<handle::ArrayBuffer<R>, ()> {
        if self.caps.array_buffer_supported {
            let mut name = 0 as ::ArrayBuffer;
            unsafe {
                self.gl.GenVertexArrays(1, &mut name);
            }
            info!("\tCreated array buffer {}", name);
            Ok(self.handles.make_array_buffer(name))
        } else {
            error!("\tarray buffer creation unsupported, ignored");
            Err(())
        }
    }

    fn create_shader(&mut self, stage: d::shade::Stage, code: &[u8])
                     -> Result<handle::Shader<R>, d::shade::CreateShaderError> {
        let (name, info) = ::shade::create_shader(&self.gl, stage, code);
        info.map(|info| {
            let level = if name.is_err() { LogLevel::Error } else { LogLevel::Warn };
            log!(level, "\tShader compile log: {}", info);
        });
        name.map(|sh| self.handles.make_shader(sh, stage))
    }

    fn create_program(&mut self, shaders: &[handle::Shader<R>],
                      targets: Option<&[&str]>)
                      -> Result<handle::Program<R>, ()> {
        let objects: Vec<::Shader> = shaders.iter()
            .map(|h| self.frame_handles.ref_shader(h))
            .collect();
        let (prog, log) = ::shade::create_program(&self.gl, &self.caps,
                                                  objects.into_iter(), targets);
        log.map(|log| {
            let level = if prog.is_err() { LogLevel::Error } else { LogLevel::Warn };
            log!(level, "\tProgram link log: {}", log);
        });
        prog.map(|(name, info)| self.handles.make_program(name, info))
    }

    fn create_frame_buffer(&mut self) -> handle::FrameBuffer<R> {
        if !self.caps.render_targets_supported {
            panic!("No framebuffer objects, can't make a new one!");
        }

        let mut name = 0 as ::FrameBuffer;
        unsafe {
            self.gl.GenFramebuffers(1, &mut name);
        }
        info!("\tCreated frame buffer {}", name);
        self.handles.make_frame_buffer(name)
    }

    fn create_surface(&mut self, info: d::tex::SurfaceInfo) ->
                      Result<handle::Surface<R>, d::tex::SurfaceError> {
        if info.format.does_convert_gamma() && !self.caps.srgb_color_supported {
            return Err(d::tex::SurfaceError::UnsupportedGamma)
        }
        tex::make_surface(&self.gl, &info)
            .map(|suf| self.handles.make_surface(suf, info))
    }

    fn create_texture(&mut self, info: d::tex::TextureInfo) ->
                      Result<handle::Texture<R>, d::tex::TextureError> {
        if info.width == 0 || info.height == 0 || info.levels == 0 {
            return Err(d::tex::TextureError::InvalidInfo(info))
        }
        if info.format.does_convert_gamma() && !self.caps.srgb_color_supported {
            return Err(d::tex::TextureError::UnsupportedGamma)
        }

        let name = if self.caps.immutable_storage_supported {
            tex::make_with_storage(&self.gl, &info)
        } else {
            tex::make_without_storage(&self.gl, &info)
        };
        name.map(|tex| self.handles.make_texture(tex, info))
    }

    fn create_sampler(&mut self, info: d::tex::SamplerInfo)
                      -> handle::Sampler<R> {
        let sam = if self.caps.sampler_objects_supported {
            tex::make_sampler(&self.gl, &info)
        } else {
            0
        };
        self.handles.make_sampler(sam, info)
    }

    fn update_buffer_raw(&mut self, buffer: &handle::RawBuffer<R>,
                         data: &[u8], offset_bytes: usize) {
        debug_assert!(offset_bytes + data.len() <= buffer.get_info().size);
        let raw_handle = self.frame_handles.ref_buffer(buffer);
        update_sub_buffer(&self.gl, raw_handle, data.as_ptr(), data.len(),
                          offset_bytes, buffer.get_info().role)
    }

    fn update_texture_raw(&mut self, texture: &handle::Texture<R>,
                          img: &d::tex::ImageInfo, data: &[u8],
                          optkind: Option<d::tex::TextureKind>)
                          -> Result<(), d::tex::TextureError> {

        // use the specified texture kind if set for this update, otherwise
        // fall back on the kind that was set when the texture was created.
        let kind = optkind.unwrap_or(texture.get_info().kind);

        tex::update_texture(&self.gl, kind,
                            self.frame_handles.ref_texture(texture),
                            img, data.as_ptr(), data.len())
    }

    fn generate_mipmap(&mut self, texture: &handle::Texture<R>) {
        tex::generate_mipmap(&self.gl, texture.get_info().kind,
                             self.frame_handles.ref_texture(texture));
    }

    fn map_buffer_raw(&mut self, buf: &handle::RawBuffer<R>,
                      access: d::MapAccess) -> RawMapping {
        let raw_handle = self.frame_handles.ref_buffer(buf);
        unsafe { self.gl.BindBuffer(gl::ARRAY_BUFFER, raw_handle) };
        let ptr = unsafe { self.gl.MapBuffer(gl::ARRAY_BUFFER, match access {
            d::MapAccess::Readable => gl::READ_ONLY,
            d::MapAccess::Writable => gl::WRITE_ONLY,
            d::MapAccess::RW => gl::READ_WRITE
        }) } as *mut libc::c_void;
        RawMapping {
            pointer: ptr,
            target: gl::ARRAY_BUFFER
        }
    }

    fn unmap_buffer_raw(&mut self, map: RawMapping) {
        unsafe { self.gl.UnmapBuffer(map.target) };
    }

    fn map_buffer_readable<T: Copy>(&mut self, buf: &handle::Buffer<R, T>)
                           -> d::mapping::Readable<T, R, Factory> {
        let map = self.map_buffer_raw(buf.raw(), d::MapAccess::Readable);
        self.map_readable(map, buf.len())
    }

    fn map_buffer_writable<T: Copy>(&mut self, buf: &handle::Buffer<R, T>)
                                    -> d::mapping::Writable<T, R, Factory> {
        let map = self.map_buffer_raw(buf.raw(), d::MapAccess::Writable);
        self.map_writable(map, buf.len())
    }

    fn map_buffer_rw<T: Copy>(&mut self, buf: &handle::Buffer<R, T>)
                              -> d::mapping::RW<T, R, Factory> {
        let map = self.map_buffer_raw(buf.raw(), d::MapAccess::RW);
        self.map_read_write(map, buf.len())
    }

    fn cleanup(&mut self) {
        self.handles.clean_with(&mut self.gl,
            |gl, v| unsafe { gl.DeleteBuffers(1, v) },
            |gl, v| unsafe { gl.DeleteVertexArrays(1, v) },
            |gl, v| unsafe { gl.DeleteShader(*v) },
            |gl, v| unsafe { gl.DeleteProgram(*v) },
            |gl, v| unsafe { gl.DeleteFramebuffers(1, v) },
            |gl, v| unsafe { gl.DeleteRenderbuffers(1, v) },
            |gl, v| unsafe { gl.DeleteTextures(1, v) },
            |gl, v| unsafe { gl.DeleteSamplers(1, v) });
        self.frame_handles.clear();
    }
}
<|endoftext|>


<commit_before>#![crate_name = "hiredis"]
#![crate_type = "lib"]
#![feature(globs)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
#![allow(dead_code)]

extern crate libc;

use std::mem::transmute;

pub mod api;

pub struct Reply {
    reply: *const api::Reply
}

impl Reply {
    unsafe fn new(reply: *const api::Reply) -> Reply {
        Reply {
            reply: reply
        }
    }
}

pub struct Redis {
    context: *const api::Context
}

impl Redis {
    pub fn new(ip: &str, port: i32) -> Redis {
        unsafe {
            Redis {
                context: api::redisConnect(
                    ip.to_c_str().as_ptr(),
                    port
                )
            }
        }
    }

    /* TODO: Move to IoResult using context.err */
    pub fn exec(&self, command: &str) -> Option<Reply> {
        command.with_c_str(|v| {
            unsafe {
                let result = api::redisCommand(self.context, v);

                /* Fail if the command errored for some reason. */
                if result == 0 as *const ::libc::c_void {
                    None
                }

                /* Otherwise transmute the void pointer memory into a pointer
                 * to a reply structure and return it. */
                Some(Reply::new(transmute(result)))
            }
        }
    }
}

impl Drop for Redis {
    fn drop(&mut self) {
        unsafe {
            api::redisFree(self.context)
        }
    }
}
<commit_msg>Fix Syntax<commit_after>#![crate_name = "hiredis"]
#![crate_type = "lib"]
#![feature(globs)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
#![allow(dead_code)]

extern crate libc;

use std::mem::transmute;

pub mod api;

pub struct Reply {
    reply: *const api::Reply
}

impl Reply {
    unsafe fn new(reply: *const api::Reply) -> Reply {
        Reply {
            reply: reply
        }
    }
}

pub struct Redis {
    context: *const api::Context
}

impl Redis {
    pub fn new(ip: &str, port: i32) -> Redis {
        unsafe {
            Redis {
                context: api::redisConnect(
                    ip.to_c_str().as_ptr(),
                    port
                )
            }
        }
    }

    /* TODO: Move to IoResult using context.err */
    pub fn exec(&self, command: &str) -> Option<Reply> {
        command.with_c_str(|v| {
            unsafe {
                let result = api::redisCommand(self.context, v);

                /* Fail if the command errored for some reason. */
                if result == 0 as *const ::libc::c_void {
                    return None;
                }

                /* Otherwise transmute the void pointer memory into a pointer
                 * to a reply structure and return it. */
                Some(Reply::new(transmute(result)))
            }
        })
    }
}

impl Drop for Redis {
    fn drop(&mut self) {
        unsafe {
            api::redisFree(self.context)
        }
    }
}
<|endoftext|>


<commit_before>use edit::buffer::{SplitBuffer, TextBuffer};
use state::editor::{Buffer, Editor};
use std::fs::File;
use std::io::{Read, Write};

/// The status of a file IO operation.
pub enum FileStatus {
    /// Oll fino.
    Ok,
    /// File not found.
    NotFound,
    /// Other error.
    Other,
}

impl Editor {
    /// Open a file.
    pub fn open(&mut self, path: &str) -> FileStatus {
        if let Some(mut file) = File::open(path).ok() {
            let mut con = String::new();
            let _ = file.read_to_string(&mut con);

            let mut new_buffer: Buffer = SplitBuffer::from_str(&con).into();
            new_buffer.title = Some(path.into());

            let new_buffer_index = self.buffers.new_buffer(new_buffer);
            self.buffers.switch_to(new_buffer_index);
            self.hint();
            FileStatus::Ok
        } else {
            FileStatus::NotFound
        }
    }

    /// Write the file.
    pub fn write(&mut self, path: &str) -> FileStatus {
        self.buffers.current_buffer_info_mut().title = Some(path.into());
        if let Some(mut file) = File::create(path).ok() {
            if file.write(self.buffers.current_buffer().to_string().as_bytes())
                .is_ok()
            {
                FileStatus::Ok
            } else {
                FileStatus::Other
            }
        } else {
            FileStatus::NotFound
        }
    }
}
<commit_msg>When opening an empty file, always have at least one line of text<commit_after>use edit::buffer::{SplitBuffer, TextBuffer};
use state::editor::{Buffer, Editor};
use std::fs::File;
use std::io::{Read, Write};

/// The status of a file IO operation.
pub enum FileStatus {
    /// Oll fino.
    Ok,
    /// File not found.
    NotFound,
    /// Other error.
    Other,
}

impl Editor {
    /// Open a file.
    pub fn open(&mut self, path: &str) -> FileStatus {
        if let Some(mut file) = File::open(path).ok() {
            let mut con = String::new();
            let _ = file.read_to_string(&mut con);

            if con.is_empty() {
                con.push('\n');
            }

            let mut new_buffer: Buffer = SplitBuffer::from_str(&con).into();
            new_buffer.title = Some(path.into());

            let new_buffer_index = self.buffers.new_buffer(new_buffer);
            self.buffers.switch_to(new_buffer_index);
            self.hint();
            FileStatus::Ok
        } else {
            FileStatus::NotFound
        }
    }

    /// Write the file.
    pub fn write(&mut self, path: &str) -> FileStatus {
        self.buffers.current_buffer_info_mut().title = Some(path.into());
        if let Some(mut file) = File::create(path).ok() {
            if file.write(self.buffers.current_buffer().to_string().as_bytes())
                .is_ok()
            {
                FileStatus::Ok
            } else {
                FileStatus::Other
            }
        } else {
            FileStatus::NotFound
        }
    }
}
<|endoftext|>