<commit_before>extern crate graphics;
extern crate input;

use graphics::{ Context, Graphics };
use input::{ GenericEvent };

mod drawutils;

pub struct Timeline {
    pub frames: u32,
    pub current_frame: u32,
    pub hover_frame: Option<u32>,
    pub start_frame: u32,
    pub bounds: [u32; 4],
    pub settings: TimelineSettings,
}

pub struct TimelineSettings {
    pub left_to_frame: f64,
    pub right_to_frame: f64,
    pub top_to_frame: f64,
    pub frame_width: f64,
    pub frame_height: f64,
    pub frame_offset_x: f64,
    pub left_to_goto_beginning: f64,
    pub right_to_goto_end: f64,
}

impl TimelineSettings {
    pub fn new() -> TimelineSettings {
        TimelineSettings {
            left_to_frame: 20.0,
            right_to_frame: 20.0,
            top_to_frame: 15.0,
            frame_width: 10.0,
            frame_height: 10.0,
            frame_offset_x: 5.0,
            left_to_goto_beginning: 15.0,
            right_to_goto_end: 15.0,
        }
    }
}

pub struct ComputedTimelineSettings {
    pub width_for_frames: f64,
    pub max_visible_frames: u32,
    pub end_frame: u32,
}

impl ComputedTimelineSettings {
    pub fn new(timeline: &Timeline, settings: &TimelineSettings)
        -> ComputedTimelineSettings
    {
        use std::cmp::min;

        let width_for_frames = timeline.bounds[2] as f64
            - settings.left_to_frame - settings.right_to_frame;
        let max_visible_frames: u32 =
            if width_for_frames < settings.frame_width {
                0
            } else {
                1
                + (
                    (width_for_frames - settings.frame_width)
                    / (settings.frame_width + settings.frame_offset_x)
                ) as u32
            };
        let end_frame = min(
            timeline.frames,
            timeline.start_frame + max_visible_frames
        );

        ComputedTimelineSettings {
            width_for_frames: width_for_frames,
            max_visible_frames: max_visible_frames,
            end_frame: end_frame
        }
    }
}

impl Timeline {
    pub fn new_frames_bounds(frames: u32, bounds: [u32; 4]) -> Timeline {
        Timeline {
            frames: frames,
            current_frame: 0,
            hover_frame: None,
            start_frame: 0,
            bounds: bounds,
            settings: TimelineSettings::new(),
        }
    }

    pub fn event<E: GenericEvent>(&mut self, e: &E) {
        use input::MouseCursorEvent;

        if let Some(pos) = e.mouse_cursor_args() {
            let computed_settings = ComputedTimelineSettings::new(self,
                &self.settings);

            let left_to_frame: f64 = self.settings.left_to_frame;
            let frame_width: f64 = self.settings.frame_width;
            let frame_offset_x: f64 = self.settings.frame_offset_x;

            let end_frame: u32 = computed_settings.end_frame;

            let (x, y) = (pos[0], pos[1]);

            let left = self.bounds[0] as f64 + left_to_frame;
            let right = left + end_frame as f64 * (frame_width + frame_offset_x);
            let top = self.bounds[1] as f64;
            let bottom = top + self.bounds[3] as f64;

            if x < left || x > right
            || y < top || y > bottom {
                self.hover_frame = None;
            } else {
                let i: u32 = ((x - left) / (frame_width + frame_offset_x)) as u32;
                self.hover_frame = Some(i);
            }
        }
    }

    pub fn draw_timeline<G: Graphics>(&self, c: &Context, g: &mut G) {
        use std::cmp::min;

        let left_to_frame: f64 = self.settings.left_to_frame;
        let right_to_frame: f64 = self.settings.right_to_frame;
        let top_to_frame: f64 = self.settings.top_to_frame;
        let frame_width: f64 = self.settings.frame_width;
        let frame_height: f64 = self.settings.frame_height;
        let frame_offset_x: f64 = self.settings.frame_offset_x;
        let left_to_goto_beginning: f64 = self.settings.left_to_goto_beginning;
        let right_to_goto_end: f64 = self.settings.right_to_goto_end;

        let computed_settings = ComputedTimelineSettings::new(self, &self.settings);

        let slide_offset: f64 = 0.0;

        let width_for_frames = computed_settings.width_for_frames;
        let max_visible_frames: u32 = computed_settings.max_visible_frames;
        let end_frame = computed_settings.end_frame;

        // Draw bounds to see where the control is.
        {
            use graphics::Rectangle;

            let bounds = [
                    self.bounds[0] as f64,
                    self.bounds[1] as f64,
                    self.bounds[2] as f64,
                    self.bounds[3] as f64
                ];
            Rectangle::new_border([0.0, 0.0, 1.0, 1.0], 0.5)
                .draw(bounds, &c.draw_state, c.transform, g);
        }

        // Draw a sequence of squares for each frame.
        {
            use graphics::Rectangle;

            let rect = Rectangle::new_border([0.0, 0.0, 1.0, 1.0], 0.5);
            for i in 0..end_frame - self.start_frame {
                // Don't draw the hover frame.
                if Some(i) == self.hover_frame { continue; }

                let bounds = [
                    self.bounds[0] as f64 + left_to_frame
                        + i as f64 * (frame_width + frame_offset_x)
                        - slide_offset,
                    self.bounds[1] as f64 + top_to_frame,
                    frame_width,
                    frame_height
                ];
                rect.draw(bounds, &c.draw_state, c.transform, g);
            }
        }

        // Draw navigate to beginning button.
        {
            use graphics::Line;
            use graphics::math::*;

            let line = Line::new([0.0, 0.0, 1.0, 1.0], 0.5);

            let at_beginning: bool = false;
            drawutils::draw_goto_end(
                at_beginning,
                [
                    self.bounds[0] as f64 + left_to_goto_beginning,
                    self.bounds[1] as f64 + top_to_frame
                ],
                [
                    -frame_width * 0.5,
                    frame_height
                ],
                &line,
                c, g
            );

            let at_end: bool = true;
            drawutils::draw_goto_end(
                at_end,
                [
                    self.bounds[0] as f64 + self.bounds[2] as f64
                        - right_to_goto_end,
                    self.bounds[1] as f64 + top_to_frame
                ],
                [
                    frame_width * 0.5,
                    frame_height
                ],
                &line,
                c, g
            );
        }
    }

    pub fn draw_drag<G: Graphics>(&self, c: &Context, g: &mut G) {

    }
}
<commit_msg>Added hover frame<commit_after>extern crate graphics;
extern crate input;

use graphics::{ Context, Graphics };
use input::{ GenericEvent };

mod drawutils;

pub struct Timeline {
    pub frames: u32,
    pub current_frame: u32,
    pub hover_frame: Option<u32>,
    pub start_frame: u32,
    pub bounds: [u32; 4],
    pub settings: TimelineSettings,
}

pub struct TimelineSettings {
    pub left_to_frame: f64,
    pub right_to_frame: f64,
    pub top_to_frame: f64,
    pub frame_width: f64,
    pub frame_height: f64,
    pub frame_offset_x: f64,
    pub left_to_goto_beginning: f64,
    pub right_to_goto_end: f64,
    pub lift_hover_frame: f64,
}

impl TimelineSettings {
    pub fn new() -> TimelineSettings {
        TimelineSettings {
            left_to_frame: 20.0,
            right_to_frame: 20.0,
            top_to_frame: 15.0,
            frame_width: 10.0,
            frame_height: 10.0,
            frame_offset_x: 5.0,
            left_to_goto_beginning: 15.0,
            right_to_goto_end: 15.0,
            lift_hover_frame: 10.0,
        }
    }
}

pub struct ComputedTimelineSettings {
    pub width_for_frames: f64,
    pub max_visible_frames: u32,
    pub end_frame: u32,
}

impl ComputedTimelineSettings {
    pub fn new(timeline: &Timeline, settings: &TimelineSettings)
        -> ComputedTimelineSettings
    {
        use std::cmp::min;

        let width_for_frames = timeline.bounds[2] as f64
            - settings.left_to_frame - settings.right_to_frame;
        let max_visible_frames: u32 =
            if width_for_frames < settings.frame_width {
                0
            } else {
                1
                + (
                    (width_for_frames - settings.frame_width)
                    / (settings.frame_width + settings.frame_offset_x)
                ) as u32
            };
        let end_frame = min(
            timeline.frames,
            timeline.start_frame + max_visible_frames
        );

        ComputedTimelineSettings {
            width_for_frames: width_for_frames,
            max_visible_frames: max_visible_frames,
            end_frame: end_frame
        }
    }
}

impl Timeline {
    pub fn new_frames_bounds(frames: u32, bounds: [u32; 4]) -> Timeline {
        Timeline {
            frames: frames,
            current_frame: 0,
            hover_frame: None,
            start_frame: 0,
            bounds: bounds,
            settings: TimelineSettings::new(),
        }
    }

    pub fn event<E: GenericEvent>(&mut self, e: &E) {
        use input::MouseCursorEvent;

        if let Some(pos) = e.mouse_cursor_args() {
            let computed_settings = ComputedTimelineSettings::new(self,
                &self.settings);

            let left_to_frame: f64 = self.settings.left_to_frame;
            let frame_width: f64 = self.settings.frame_width;
            let frame_offset_x: f64 = self.settings.frame_offset_x;

            let end_frame: u32 = computed_settings.end_frame;

            let (x, y) = (pos[0], pos[1]);

            let left = self.bounds[0] as f64 + left_to_frame;
            let right = left + end_frame as f64 * (frame_width + frame_offset_x);
            let top = self.bounds[1] as f64;
            let bottom = top + self.bounds[3] as f64;

            if x < left || x > right
            || y < top || y > bottom {
                self.hover_frame = None;
            } else {
                let i: u32 = ((x - left) / (frame_width + frame_offset_x)) as u32;
                self.hover_frame = Some(i);
            }
        }
    }

    pub fn draw_timeline<G: Graphics>(&self, c: &Context, g: &mut G) {
        use std::cmp::min;

        let left_to_frame: f64 = self.settings.left_to_frame;
        let right_to_frame: f64 = self.settings.right_to_frame;
        let top_to_frame: f64 = self.settings.top_to_frame;
        let frame_width: f64 = self.settings.frame_width;
        let frame_height: f64 = self.settings.frame_height;
        let frame_offset_x: f64 = self.settings.frame_offset_x;
        let left_to_goto_beginning: f64 = self.settings.left_to_goto_beginning;
        let right_to_goto_end: f64 = self.settings.right_to_goto_end;
        let lift_hover_frame: f64 = self.settings.lift_hover_frame;

        let computed_settings = ComputedTimelineSettings::new(self, &self.settings);

        let slide_offset: f64 = 0.0;

        let width_for_frames = computed_settings.width_for_frames;
        let max_visible_frames: u32 = computed_settings.max_visible_frames;
        let end_frame = computed_settings.end_frame;

        // Draw bounds to see where the control is.
        {
            use graphics::Rectangle;

            let bounds = [
                    self.bounds[0] as f64,
                    self.bounds[1] as f64,
                    self.bounds[2] as f64,
                    self.bounds[3] as f64
                ];
            Rectangle::new_border([0.0, 0.0, 1.0, 1.0], 0.5)
                .draw(bounds, &c.draw_state, c.transform, g);
        }

        // Draw a sequence of squares for each frame.
        {
            use graphics::Rectangle;

            let frame_bounds = |i: u32, lift: f64| [
                self.bounds[0] as f64 + left_to_frame
                    + i as f64 * (frame_width + frame_offset_x)
                    - slide_offset,
                self.bounds[1] as f64 + top_to_frame - lift,
                frame_width,
                frame_height
            ];

            let rect = Rectangle::new_border([0.0, 0.0, 1.0, 1.0], 0.5);
            for i in 0..end_frame - self.start_frame {
                // Don't draw the hover frame.
                if Some(i) == self.hover_frame { continue; }

                let bounds = frame_bounds(i, 0.0);
                rect.draw(bounds, &c.draw_state, c.transform, g);
            }

            if let Some(i) = self.hover_frame {
                let bounds = frame_bounds(i, lift_hover_frame);
                rect.draw(bounds, &c.draw_state, c.transform, g);
            }
        }

        // Draw navigate to beginning button.
        {
            use graphics::Line;
            use graphics::math::*;

            let line = Line::new([0.0, 0.0, 1.0, 1.0], 0.5);

            let at_beginning: bool = false;
            drawutils::draw_goto_end(
                at_beginning,
                [
                    self.bounds[0] as f64 + left_to_goto_beginning,
                    self.bounds[1] as f64 + top_to_frame
                ],
                [
                    -frame_width * 0.5,
                    frame_height
                ],
                &line,
                c, g
            );

            let at_end: bool = true;
            drawutils::draw_goto_end(
                at_end,
                [
                    self.bounds[0] as f64 + self.bounds[2] as f64
                        - right_to_goto_end,
                    self.bounds[1] as f64 + top_to_frame
                ],
                [
                    frame_width * 0.5,
                    frame_height
                ],
                &line,
                c, g
            );
        }
    }

    pub fn draw_drag<G: Graphics>(&self, c: &Context, g: &mut G) {

    }
}
<|endoftext|>


<commit_before>#![crate_type="lib"]
#![feature(str_char)]

pub use button::Button;
pub use cell::{CopyCell, CloneCell};
pub use color::Color;
pub use event::Event;
pub use label::Label;
pub use place::Place;
pub use point::Point;
pub use progress_bar::ProgressBar;
pub use rect::Rect;
pub use renderer::Renderer;
pub use text_box::TextBox;
pub use widget::Widget;
pub use window::Window;

pub mod button;
pub mod cell;
pub mod color;
pub mod event;
pub mod label;
pub mod place;
pub mod point;
pub mod progress_bar;
pub mod rect;
pub mod renderer;
pub mod text_box;
pub mod widget;
pub mod window;

use callback::{Click, Enter};
pub mod callback;

pub fn example() {
    let mut window = Window::new(Rect::new(100, 100, 420, 420), "OrbTK");

    let x = 10;
    let mut y = 10;

    let label = Label::new()
        .position(x, y)
        .size(400, 16)
        .text("Test Label")
        .place(&mut window);

    y += label.rect.get().height as i32 + 10;

    let text_box = TextBox::new()
        .position(x, y)
        .size(342, 16)
        .on_enter(move |text_box: &TextBox| {
            label.text.set(text_box.text.get());
        })
        .place(&mut window);

    let button = Button::new()
        .position(x + text_box.rect.get().width as i32 + 10, y)
        .size(48, text_box.rect.get().height)
        .text("Update")
        .on_click(move |_button: &Button, _point: Point| {
            text_box.emit_enter();
        })
        .place(&mut window);

    y += button.rect.get().height as i32 + 10;

    let progress_label = Label::new()
        .text("Progress: 0%")
        .position(x, y)
        .size(400, 16)
        .place(&mut window);

    y += progress_label.rect.get().height as i32 + 10;

    let progress_bar = ProgressBar::new()
        .position(x, y)
        .size(400, 16)
        .on_click(move |progress_bar: &ProgressBar, point: Point| {
            let progress = point.x * 100 / progress_bar.rect.get().width as i32;
            progress_label.text.set(format!("Progress: {}%", progress));
            progress_bar.value.set(progress);
        })
        .place(&mut window);

    y += progress_bar.rect.get().height as i32 + 10;

    let multi_line_label = Label::new()
        .text("Multi-Line Text")
        .position(x, y)
        .size(400, 16)
        .place(&mut window);

    y += multi_line_label.rect.get().height as i32 + 10;

    let multi_line_text_box = TextBox::new()
        .position(x, y)
        .size(400, 128)
        .place(&mut window);

    y += multi_line_text_box.rect.get().height as i32 + 10;

    Label::new()
        .position(x, y)
        .size(400, 256)
        .text("Test Offset")
        .text_offset(50, 50)
        .place(&mut window);

    window.exec();
}
<commit_msg>Fix compile with rustc<commit_after>#![crate_name="orbtk"]
#![crate_type="lib"]
#![feature(str_char)]

pub use button::Button;
pub use cell::{CopyCell, CloneCell};
pub use color::Color;
pub use event::Event;
pub use label::Label;
pub use place::Place;
pub use point::Point;
pub use progress_bar::ProgressBar;
pub use rect::Rect;
pub use renderer::Renderer;
pub use text_box::TextBox;
pub use widget::Widget;
pub use window::Window;

pub mod button;
pub mod cell;
pub mod color;
pub mod event;
pub mod label;
pub mod place;
pub mod point;
pub mod progress_bar;
pub mod rect;
pub mod renderer;
pub mod text_box;
pub mod widget;
pub mod window;

use callback::{Click, Enter};
pub mod callback;

pub fn example() {
    let mut window = Window::new(Rect::new(100, 100, 420, 420), "OrbTK");

    let x = 10;
    let mut y = 10;

    let label = Label::new()
        .position(x, y)
        .size(400, 16)
        .text("Test Label")
        .place(&mut window);

    y += label.rect.get().height as i32 + 10;

    let text_box = TextBox::new()
        .position(x, y)
        .size(342, 16)
        .on_enter(move |text_box: &TextBox| {
            label.text.set(text_box.text.get());
        })
        .place(&mut window);

    let button = Button::new()
        .position(x + text_box.rect.get().width as i32 + 10, y)
        .size(48, text_box.rect.get().height)
        .text("Update")
        .on_click(move |_button: &Button, _point: Point| {
            text_box.emit_enter();
        })
        .place(&mut window);

    y += button.rect.get().height as i32 + 10;

    let progress_label = Label::new()
        .text("Progress: 0%")
        .position(x, y)
        .size(400, 16)
        .place(&mut window);

    y += progress_label.rect.get().height as i32 + 10;

    let progress_bar = ProgressBar::new()
        .position(x, y)
        .size(400, 16)
        .on_click(move |progress_bar: &ProgressBar, point: Point| {
            let progress = point.x * 100 / progress_bar.rect.get().width as i32;
            progress_label.text.set(format!("Progress: {}%", progress));
            progress_bar.value.set(progress);
        })
        .place(&mut window);

    y += progress_bar.rect.get().height as i32 + 10;

    let multi_line_label = Label::new()
        .text("Multi-Line Text")
        .position(x, y)
        .size(400, 16)
        .place(&mut window);

    y += multi_line_label.rect.get().height as i32 + 10;

    let multi_line_text_box = TextBox::new()
        .position(x, y)
        .size(400, 128)
        .place(&mut window);

    y += multi_line_text_box.rect.get().height as i32 + 10;

    Label::new()
        .position(x, y)
        .size(400, 256)
        .text("Test Offset")
        .text_offset(50, 50)
        .place(&mut window);

    window.exec();
}
<|endoftext|>


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

#![cfg_attr(not(test), no_std)]

//! A collection of strongly typed math tools for computer graphics with an inclination
//! towards 2d graphics and layout.
//!
//! All types are generic over the scalar type of their component (`f32`, `i32`, etc.),
//! and tagged with a generic Unit parameter which is useful to prevent mixing
//! values from different spaces. For example it should not be legal to translate
//! a screen-space position by a world-space vector and this can be expressed using
//! the generic Unit parameter.
//!
//! This unit system is not mandatory and all Typed* structures have an alias
//! with the default unit: `UnknownUnit`.
//! for example ```Point2D<T>``` is equivalent to ```TypedPoint2D<T, UnknownUnit>```.
//! Client code typically creates a set of aliases for each type and doesn't need
//! to deal with the specifics of typed units further. For example:
//!
//! ```rust
//! use euclid::*;
//! pub struct ScreenSpace;
//! pub type ScreenPoint = TypedPoint2D<f32, ScreenSpace>;
//! pub type ScreenSize = TypedSize2D<f32, ScreenSpace>;
//! pub struct WorldSpace;
//! pub type WorldPoint = TypedPoint3D<f32, WorldSpace>;
//! pub type ProjectionMatrix = TypedTransform3D<f32, WorldSpace, ScreenSpace>;
//! // etc...
//! ```
//!
//! All euclid types are marked `#[repr(C)]` in order to facilitate exposing them to
//! foreign function interfaces (provided the underlying scalar type is also `repr(C)`).
//!
//! Components are accessed in their scalar form by default for convenience, and most
//! types additionally implement strongly typed accessors which return typed ```Length``` wrappers.
//! For example:
//!
//! ```rust
//! # use euclid::*;
//! # pub struct WorldSpace;
//! # pub type WorldPoint = TypedPoint3D<f32, WorldSpace>;
//! let p = WorldPoint::new(0.0, 1.0, 1.0);
//! // p.x is an f32.
//! println!("p.x = {:?} ", p.x);
//! // p.x is a Length<f32, WorldSpace>.
//! println!("p.x_typed() = {:?} ", p.x_typed());
//! // Length::get returns the scalar value (f32).
//! assert_eq!(p.x, p.x_typed().get());
//! ```

#[cfg(feature = "serde")]
#[macro_use]
extern crate serde;

#[cfg(feature = "mint")]
pub extern crate mint;
extern crate num_traits;
#[cfg(test)]
extern crate rand;
#[cfg(test)]
use std as core;

pub use box2d::{TypedBox2D, Box2D};
pub use length::Length;
pub use scale::TypedScale;
pub use transform2d::{Transform2D, TypedTransform2D};
pub use transform3d::{Transform3D, TypedTransform3D};
pub use point::{Point2D, Point3D, TypedPoint2D, TypedPoint3D, point2, point3};
pub use vector::{TypedVector2D, TypedVector3D, Vector2D, Vector3D, vec2, vec3};
pub use vector::{BoolVector2D, BoolVector3D, bvec2, bvec3};
pub use homogen::HomogeneousVector;

pub use rect::{rect, Rect, TypedRect};
pub use rigid::{RigidTransform3D, TypedRigidTransform3D};
pub use box3d::{box3d, Box3D, TypedBox3D};
pub use translation::{TypedTranslation2D, TypedTranslation3D};
pub use rotation::{Angle, Rotation2D, Rotation3D, TypedRotation2D, TypedRotation3D};
pub use side_offsets::{SideOffsets2D, TypedSideOffsets2D};
pub use size::{Size2D, TypedSize2D, TypedSize3D, size2, size3};
pub use trig::Trig;

#[macro_use]
mod macros;

pub mod approxeq;
pub mod approxord;
mod box2d;
mod homogen;
pub mod num;
mod length;
mod point;
mod rect;
mod rigid;
mod rotation;
mod scale;
mod side_offsets;
mod size;
mod transform2d;
mod transform3d;
mod translation;
mod trig;
mod vector;
mod box3d;

/// The default unit.
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct UnknownUnit;

/// Temporary alias to facilitate the transition to the new naming scheme
#[deprecated]
pub type Matrix2D<T> = Transform2D<T>;

/// Temporary alias to facilitate the transition to the new naming scheme
#[deprecated]
pub type TypedMatrix2D<T, Src, Dst> = TypedTransform2D<T, Src, Dst>;

/// Temporary alias to facilitate the transition to the new naming scheme
#[deprecated]
pub type Matrix4D<T> = Transform3D<T>;

/// Temporary alias to facilitate the transition to the new naming scheme
#[deprecated]
pub type TypedMatrix4D<T, Src, Dst> = TypedTransform3D<T, Src, Dst>;

/// Temporary alias to facilitate the transition to the new naming scheme
#[deprecated]
pub type ScaleFactor<T, Src, Dst> = TypedScale<T, Src, Dst>;

/// Temporary alias to facilitate the transition to the new naming scheme
#[deprecated]
pub use Angle as Radians;

pub mod default {
    use super::*;

    pub type Point2D<T> = TypedPoint2D<T, UnknownUnit>;
    pub type Point3D<T> = TypedPoint3D<T, UnknownUnit>;
    pub type Vector2D<T> = TypedVector2D<T, UnknownUnit>;
    pub type Vector3D<T> = TypedVector3D<T, UnknownUnit>;
    pub type Vector4D<T> = HomogeneousVector<T, UnknownUnit>;
    pub type Size2D<T> = TypedSize2D<T, UnknownUnit>;
    pub type Size3D<T> = TypedSize3D<T, UnknownUnit>;
    pub type Rect<T> = TypedRect<T, UnknownUnit>;
    pub type Box2D<T> = TypedBox2D<T, UnknownUnit>;
    pub type Box3D<T> = TypedBox3D<T, UnknownUnit>;
    pub type SideOffsets2D<T> = TypedSideOffsets2D<T, UnknownUnit>;
    pub type Tranform2D<T> = TypedTransform2D<T, UnknownUnit, UnknownUnit>;
    pub type Tranform3D<T> = TypedTransform3D<T, UnknownUnit, UnknownUnit>;
    pub type Rotation2D<T> = TypedRotation2D<T, UnknownUnit, UnknownUnit>;
    pub type Rotation3D<T> = TypedRotation3D<T, UnknownUnit, UnknownUnit>;
    pub type Translation3D<T> = TypedTranslation3D<T, UnknownUnit, UnknownUnit>;
    pub type Scale<T> = TypedScale<T, UnknownUnit, UnknownUnit>;
    pub type RigidTransform3D<T> = TypedRigidTransform3D<T, UnknownUnit, UnknownUnit>;
}
<commit_msg>Remove some deprecated aliases.<commit_after>// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![cfg_attr(not(test), no_std)]

//! A collection of strongly typed math tools for computer graphics with an inclination
//! towards 2d graphics and layout.
//!
//! All types are generic over the scalar type of their component (`f32`, `i32`, etc.),
//! and tagged with a generic Unit parameter which is useful to prevent mixing
//! values from different spaces. For example it should not be legal to translate
//! a screen-space position by a world-space vector and this can be expressed using
//! the generic Unit parameter.
//!
//! This unit system is not mandatory and all Typed* structures have an alias
//! with the default unit: `UnknownUnit`.
//! for example ```Point2D<T>``` is equivalent to ```TypedPoint2D<T, UnknownUnit>```.
//! Client code typically creates a set of aliases for each type and doesn't need
//! to deal with the specifics of typed units further. For example:
//!
//! ```rust
//! use euclid::*;
//! pub struct ScreenSpace;
//! pub type ScreenPoint = TypedPoint2D<f32, ScreenSpace>;
//! pub type ScreenSize = TypedSize2D<f32, ScreenSpace>;
//! pub struct WorldSpace;
//! pub type WorldPoint = TypedPoint3D<f32, WorldSpace>;
//! pub type ProjectionMatrix = TypedTransform3D<f32, WorldSpace, ScreenSpace>;
//! // etc...
//! ```
//!
//! All euclid types are marked `#[repr(C)]` in order to facilitate exposing them to
//! foreign function interfaces (provided the underlying scalar type is also `repr(C)`).
//!
//! Components are accessed in their scalar form by default for convenience, and most
//! types additionally implement strongly typed accessors which return typed ```Length``` wrappers.
//! For example:
//!
//! ```rust
//! # use euclid::*;
//! # pub struct WorldSpace;
//! # pub type WorldPoint = TypedPoint3D<f32, WorldSpace>;
//! let p = WorldPoint::new(0.0, 1.0, 1.0);
//! // p.x is an f32.
//! println!("p.x = {:?} ", p.x);
//! // p.x is a Length<f32, WorldSpace>.
//! println!("p.x_typed() = {:?} ", p.x_typed());
//! // Length::get returns the scalar value (f32).
//! assert_eq!(p.x, p.x_typed().get());
//! ```

#[cfg(feature = "serde")]
#[macro_use]
extern crate serde;

#[cfg(feature = "mint")]
pub extern crate mint;
extern crate num_traits;
#[cfg(test)]
extern crate rand;
#[cfg(test)]
use std as core;

pub use box2d::{TypedBox2D, Box2D};
pub use length::Length;
pub use scale::TypedScale;
pub use transform2d::{Transform2D, TypedTransform2D};
pub use transform3d::{Transform3D, TypedTransform3D};
pub use point::{Point2D, Point3D, TypedPoint2D, TypedPoint3D, point2, point3};
pub use vector::{TypedVector2D, TypedVector3D, Vector2D, Vector3D, vec2, vec3};
pub use vector::{BoolVector2D, BoolVector3D, bvec2, bvec3};
pub use homogen::HomogeneousVector;

pub use rect::{rect, Rect, TypedRect};
pub use rigid::{RigidTransform3D, TypedRigidTransform3D};
pub use box3d::{box3d, Box3D, TypedBox3D};
pub use translation::{TypedTranslation2D, TypedTranslation3D};
pub use rotation::{Angle, Rotation2D, Rotation3D, TypedRotation2D, TypedRotation3D};
pub use side_offsets::{SideOffsets2D, TypedSideOffsets2D};
pub use size::{Size2D, TypedSize2D, TypedSize3D, size2, size3};
pub use trig::Trig;

#[macro_use]
mod macros;

pub mod approxeq;
pub mod approxord;
mod box2d;
mod homogen;
pub mod num;
mod length;
mod point;
mod rect;
mod rigid;
mod rotation;
mod scale;
mod side_offsets;
mod size;
mod transform2d;
mod transform3d;
mod translation;
mod trig;
mod vector;
mod box3d;

/// The default unit.
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct UnknownUnit;

pub mod default {
    use super::*;

    pub type Point2D<T> = TypedPoint2D<T, UnknownUnit>;
    pub type Point3D<T> = TypedPoint3D<T, UnknownUnit>;
    pub type Vector2D<T> = TypedVector2D<T, UnknownUnit>;
    pub type Vector3D<T> = TypedVector3D<T, UnknownUnit>;
    pub type Vector4D<T> = HomogeneousVector<T, UnknownUnit>;
    pub type Size2D<T> = TypedSize2D<T, UnknownUnit>;
    pub type Size3D<T> = TypedSize3D<T, UnknownUnit>;
    pub type Rect<T> = TypedRect<T, UnknownUnit>;
    pub type Box2D<T> = TypedBox2D<T, UnknownUnit>;
    pub type Box3D<T> = TypedBox3D<T, UnknownUnit>;
    pub type SideOffsets2D<T> = TypedSideOffsets2D<T, UnknownUnit>;
    pub type Tranform2D<T> = TypedTransform2D<T, UnknownUnit, UnknownUnit>;
    pub type Tranform3D<T> = TypedTransform3D<T, UnknownUnit, UnknownUnit>;
    pub type Rotation2D<T> = TypedRotation2D<T, UnknownUnit, UnknownUnit>;
    pub type Rotation3D<T> = TypedRotation3D<T, UnknownUnit, UnknownUnit>;
    pub type Translation3D<T> = TypedTranslation3D<T, UnknownUnit, UnknownUnit>;
    pub type Scale<T> = TypedScale<T, UnknownUnit, UnknownUnit>;
    pub type RigidTransform3D<T> = TypedRigidTransform3D<T, UnknownUnit, UnknownUnit>;
}
<|endoftext|>


<commit_before>//! This crate parses and processes `hyper::server::Request` data
//! that contains `multipart/form-data` content.
//!
//! The main entry point is `parse_multipart`

extern crate hyper;
#[macro_use] extern crate mime;
extern crate httparse;
extern crate libc;
extern crate tempdir;
extern crate textnonce;

#[cfg(test)]
mod mock;
pub mod buf;
mod content_disposition;
pub mod error;

pub use error::Error;
pub use buf::BufReadPlus;

use std::path::PathBuf;
use std::fs::File;
use std::io::{BufReader,BufRead};

use hyper::server::Request;
use hyper::header::{Headers,ContentType};
use mime::{Mime,TopLevel,SubLevel,Attr,Value};
use tempdir::TempDir;
use textnonce::TextNonce;

pub use content_disposition::ContentDispositionFormData;

#[cfg(test)]
use std::net::SocketAddr;

/// This structure represents uploaded files which were received as
/// part of the `multipart/form-data` parsing.  They are streamed to
/// disk because they may not fit in memory.
pub struct UploadedFile {
    /// This is the temporary file where the data was saved.
    pub path: PathBuf,
    /// This is the filename that was specified in the data, unfiltered.  It may or may not
    /// be legal on the local filesystem.
    pub filename: Option<String>,
    /// This is the content-type that was specified in the data, unvalidated.
    pub content_type: Mime,
    /// This is the actual size of the file received
    pub size: usize,
}

/// This function parses and processes `hyper::server::Request` data
/// that contains `multipart/form-data` content.  It does this in a streaming
/// fashion, saving embedded uploaded files to disk as they are encountered by
/// the parser.
///
/// It returns two sets of data.  The first, `Vec<(String,String)>`, are the
/// variable-value pairs from the POST-ed form.  The second
/// `Vec<(String,UploadedFile)>` are the variable-file pairs from the POST-ed
/// form, where `UploadedFile` structures describe the uploaded file.
pub fn parse_multipart<'a,'b>(
    request: &mut Request<'a,'b>)
    -> Result< (Vec<(String,String)>, Vec<(String,UploadedFile)>), Error >
{
    let mut parameters: Vec<(String,String)> = Vec::new();
    let mut files: Vec<(String,UploadedFile)> = Vec::new();

    let string_boundary = try!( get_boundary(request) );
    println!("Boundary is {}", string_boundary);
    let boundary: Vec<u8> = string_boundary.into_bytes();
    let mut crlf_boundary: Vec<u8> = Vec::with_capacity(2 + boundary.len());
    crlf_boundary.extend(b"\r\n".iter());
    crlf_boundary.extend(boundary.clone());

    // Request implements Read.  Internally it's a BufReader, but it's not
    // exposed that way.  We need to wrap it into a BufReader to get that
    // functionality ourselves.
    let mut r = BufReader::with_capacity(4096,request);

    enum State {
        Discarding, // Discard until after initial boundary and CRLF
        CapturingValue, // Capture value until boundary, then discard past CRLF
        CapturingFile, // Capture file until boundary, then discard past CRLF
        ReadingHeaders, // Capture headers to blank line
    }

    let mut state = State::Discarding;
    let mut headers = Headers::new();

    loop {
        let mut buf: Vec<u8> = Vec::new();

        match state {
            State::Discarding => {
                // Read up to and including the boundary
                let read = try!( r.stream_until_token( &*boundary, &mut buf ) );
                if read==0 { return Err(Error::Eof); }

                state = State::ReadingHeaders;
            },
            State::ReadingHeaders => {
                {
                    // IF the next two characters are '--' (look ahead), then we are finished
                    let peeker = try!( r.fill_buf() );
                    if peeker.len() >= 2 && &peeker[..2]==b"--" {
                        return Ok((parameters, files))
                    }
                } // drop peeker

                // Read up to and including the CRLF after the boundary
                let read = try!( r.stream_until_token( b"\r\n", &mut buf ) );
                if read==0 { return Err(Error::Eof); }

                buf.truncate(0);
                let read = try!( r.stream_until_token( b"\r\n\r\n", &mut buf ) );
                if read==0 { return Err(Error::Eof); }
                buf.extend(b"\r\n\r\n".iter()); // parse_headers() needs this token at the end

                // Parse the headers
                let mut header_memory = [httparse::EMPTY_HEADER; 4];
                match httparse::parse_headers( &*buf, &mut header_memory) {
                    Ok(httparse::Status::Complete((_,raw_headers))) => {
                        // Turn raw headers into hyper headers
                        headers = try!( Headers::from_raw(raw_headers) );

                        let cd: &ContentDispositionFormData = match headers.get() {
                            Some(x) => x,
                            None => return Err(Error::MissingDisposition),
                        };
                        let ct: Option<&ContentType> = headers.get();

                        state = if ct.is_some() || cd.filename.is_some() {
                            State::CapturingFile
                        } else {
                            State::CapturingValue
                        };
                    },
                    Ok(httparse::Status::Partial) => {
                        println!("Header Parsing was Partial");
                        return Err(Error::ParseError);
                    },
                    Err(e) => return Err( From::from(e) ),
                }
            },
            State::CapturingValue => {
                buf.truncate(0);
                let _ = try!( r.stream_until_token( &*crlf_boundary, &mut buf ) );

                let cd: &ContentDispositionFormData = headers.get().unwrap();

                let key = match cd.name {
                    None => return Err(Error::NoName),
                    Some(ref name) => name.clone()
                };
                let value = try!( String::from_utf8(buf) );

                parameters.push( (key,value) );

                state = State::ReadingHeaders;
            },
            State::CapturingFile => {
                // Setup a file to capture the contents
                let mut temp = try!( TempDir::new("formdata") ).into_path();
                temp.push( TextNonce::sized_urlsafe(32).unwrap().into_string() );
                let mut tmpfile = try!( File::create(temp.clone()) );

                // Stream out the file
                let read = try!( r.stream_until_token( &*crlf_boundary, &mut tmpfile ) );

                let cd: &ContentDispositionFormData = headers.get().unwrap();
                let key = match cd.name {
                    None => return Err(Error::NoName),
                    Some(ref name) => name.clone()
                };
                // FIXME: handle content-type header, and default to text/plain if
                //        no content-type header.
                // FIXME: handle content-type: multipart/mixed as multiple files
                // FIXME: handle content-transfer-encoding

                let ufile = UploadedFile {
                    path: temp,
                    filename: cd.filename.clone(),
                    content_type: mime!(Text/Plain; Charset=Utf8), // FIXME
                    size: read - crlf_boundary.len()
                };
                files.push( (key,ufile) );

                state = State::ReadingHeaders;
            },
        }
    }
}

fn get_boundary<'a,'b>(request: &Request<'a,'b>) -> Result<String,Error>
{
    // Verify that the request is 'content-type: multipart/form-data'
    let content_type: &ContentType = match request.headers.get() {
        Some(h) => h,
        None => return Err(Error::NoRequestContentType),
    };
    let ContentType(ref mime) = *content_type;
    let Mime(ref top_level, ref sub_level, ref params) = *mime;
    if *top_level != TopLevel::Multipart {
        return Err(Error::NotMultipart);
    }
    if *sub_level != SubLevel::FormData {
        return Err(Error::NotFormData);
    }

    // Get the boundary token
    for &(ref attr,ref value) in params.iter() {
        match (attr,value) {
            (&Attr::Ext(ref k), &Value::Ext(ref v)) => {
                if *k=="boundary" {
                    return Ok( format!("--{}",v.clone()) )
                }
            }
            _ => {}
        }
    }
    Err(Error::BoundaryNotSpecified)

}

#[test]
fn test1() {
    use mock::MockStream;
    use hyper::net::NetworkStream;
    use hyper::buffer::BufReader;

    let input=b"POST / HTTP/1.1\r\n\
                Host: example.domain\r\n\
                Content-Type: multipart/form-data; boundary=\"abcdefg\"\r\n\
                Content-Length: 217\r\n\
                \r\n\
                --abcdefg\r\n\
                Content-Disposition: form-data; name=\"field1\"\r\n\
                \r\n\
                data1\r\n\
                --abcdefg\r\n\
                Content-Disposition: form-data; name=\"field2\"; filename=\"file.txt\"\r\n\
                Content-Type: text/plain\r\n\
                \r\n\
                This is a file\r\n\
                with two lines\r\n\
                --abcdefg--";
    let mut mock = MockStream::with_input(input);

    let mock: &mut NetworkStream = &mut mock;
    let mut stream = BufReader::new(mock);
    let sock: SocketAddr = "127.0.0.1:80".parse().unwrap();

    let mut req = Request::new(&mut stream, sock).unwrap();

    match parse_multipart(&mut req) {
        Ok((fields,files)) => {
            assert_eq!(fields.len(),1);
            for (k,v) in fields {
                if &k=="field1" { assert_eq!( &v, "data1" ) }
            }
            assert_eq!(files.len(),1);
            for (k,file) in files {
                if &k=="field2" {
                    assert_eq!(file.size, 30);
                    assert!(file.filename.is_some());
                    assert_eq!(&file.filename.unwrap(), "file.txt");
                    // FIXME add content_type check after impl
                }
            }

        }
        Err(e) => {
            println!("FAILED ON: {:?}",e);
            assert!(false);
        }
    }
}
<commit_msg>Fix extend() calls<commit_after>//! This crate parses and processes `hyper::server::Request` data
//! that contains `multipart/form-data` content.
//!
//! The main entry point is `parse_multipart`

extern crate hyper;
#[macro_use] extern crate mime;
extern crate httparse;
extern crate libc;
extern crate tempdir;
extern crate textnonce;

#[cfg(test)]
mod mock;
pub mod buf;
mod content_disposition;
pub mod error;

pub use error::Error;
pub use buf::BufReadPlus;

use std::path::PathBuf;
use std::fs::File;
use std::io::{BufReader,BufRead};

use hyper::server::Request;
use hyper::header::{Headers,ContentType};
use mime::{Mime,TopLevel,SubLevel,Attr,Value};
use tempdir::TempDir;
use textnonce::TextNonce;

pub use content_disposition::ContentDispositionFormData;

#[cfg(test)]
use std::net::SocketAddr;

/// This structure represents uploaded files which were received as
/// part of the `multipart/form-data` parsing.  They are streamed to
/// disk because they may not fit in memory.
pub struct UploadedFile {
    /// This is the temporary file where the data was saved.
    pub path: PathBuf,
    /// This is the filename that was specified in the data, unfiltered.  It may or may not
    /// be legal on the local filesystem.
    pub filename: Option<String>,
    /// This is the content-type that was specified in the data, unvalidated.
    pub content_type: Mime,
    /// This is the actual size of the file received
    pub size: usize,
}

/// This function parses and processes `hyper::server::Request` data
/// that contains `multipart/form-data` content.  It does this in a streaming
/// fashion, saving embedded uploaded files to disk as they are encountered by
/// the parser.
///
/// It returns two sets of data.  The first, `Vec<(String,String)>`, are the
/// variable-value pairs from the POST-ed form.  The second
/// `Vec<(String,UploadedFile)>` are the variable-file pairs from the POST-ed
/// form, where `UploadedFile` structures describe the uploaded file.
pub fn parse_multipart<'a,'b>(
    request: &mut Request<'a,'b>)
    -> Result< (Vec<(String,String)>, Vec<(String,UploadedFile)>), Error >
{
    let mut parameters: Vec<(String,String)> = Vec::new();
    let mut files: Vec<(String,UploadedFile)> = Vec::new();

    let string_boundary = try!( get_boundary(request) );
    println!("Boundary is {}", string_boundary);
    let boundary: Vec<u8> = string_boundary.into_bytes();
    let mut crlf_boundary: Vec<u8> = Vec::with_capacity(2 + boundary.len());
    crlf_boundary.extend(b"\r\n".iter().map(|&i| i));
    crlf_boundary.extend(boundary.clone());

    // Request implements Read.  Internally it's a BufReader, but it's not
    // exposed that way.  We need to wrap it into a BufReader to get that
    // functionality ourselves.
    let mut r = BufReader::with_capacity(4096,request);

    enum State {
        Discarding, // Discard until after initial boundary and CRLF
        CapturingValue, // Capture value until boundary, then discard past CRLF
        CapturingFile, // Capture file until boundary, then discard past CRLF
        ReadingHeaders, // Capture headers to blank line
    }

    let mut state = State::Discarding;
    let mut headers = Headers::new();

    loop {
        let mut buf: Vec<u8> = Vec::new();

        match state {
            State::Discarding => {
                // Read up to and including the boundary
                let read = try!( r.stream_until_token( &*boundary, &mut buf ) );
                if read==0 { return Err(Error::Eof); }

                state = State::ReadingHeaders;
            },
            State::ReadingHeaders => {
                {
                    // IF the next two characters are '--' (look ahead), then we are finished
                    let peeker = try!( r.fill_buf() );
                    if peeker.len() >= 2 && &peeker[..2]==b"--" {
                        return Ok((parameters, files))
                    }
                } // drop peeker

                // Read up to and including the CRLF after the boundary
                let read = try!( r.stream_until_token( b"\r\n", &mut buf ) );
                if read==0 { return Err(Error::Eof); }

                buf.truncate(0);
                let read = try!( r.stream_until_token( b"\r\n\r\n", &mut buf ) );
                if read==0 { return Err(Error::Eof); }
                buf.extend(b"\r\n\r\n".iter().map(|&i| i)); // parse_headers() needs this token at the end

                // Parse the headers
                let mut header_memory = [httparse::EMPTY_HEADER; 4];
                match httparse::parse_headers( &*buf, &mut header_memory) {
                    Ok(httparse::Status::Complete((_,raw_headers))) => {
                        // Turn raw headers into hyper headers
                        headers = try!( Headers::from_raw(raw_headers) );

                        let cd: &ContentDispositionFormData = match headers.get() {
                            Some(x) => x,
                            None => return Err(Error::MissingDisposition),
                        };
                        let ct: Option<&ContentType> = headers.get();

                        state = if ct.is_some() || cd.filename.is_some() {
                            State::CapturingFile
                        } else {
                            State::CapturingValue
                        };
                    },
                    Ok(httparse::Status::Partial) => {
                        println!("Header Parsing was Partial");
                        return Err(Error::ParseError);
                    },
                    Err(e) => return Err( From::from(e) ),
                }
            },
            State::CapturingValue => {
                buf.truncate(0);
                let _ = try!( r.stream_until_token( &*crlf_boundary, &mut buf ) );

                let cd: &ContentDispositionFormData = headers.get().unwrap();

                let key = match cd.name {
                    None => return Err(Error::NoName),
                    Some(ref name) => name.clone()
                };
                let value = try!( String::from_utf8(buf) );

                parameters.push( (key,value) );

                state = State::ReadingHeaders;
            },
            State::CapturingFile => {
                // Setup a file to capture the contents
                let mut temp = try!( TempDir::new("formdata") ).into_path();
                temp.push( TextNonce::sized_urlsafe(32).unwrap().into_string() );
                let mut tmpfile = try!( File::create(temp.clone()) );

                // Stream out the file
                let read = try!( r.stream_until_token( &*crlf_boundary, &mut tmpfile ) );

                let cd: &ContentDispositionFormData = headers.get().unwrap();
                let key = match cd.name {
                    None => return Err(Error::NoName),
                    Some(ref name) => name.clone()
                };
                // FIXME: handle content-type header, and default to text/plain if
                //        no content-type header.
                // FIXME: handle content-type: multipart/mixed as multiple files
                // FIXME: handle content-transfer-encoding

                let ufile = UploadedFile {
                    path: temp,
                    filename: cd.filename.clone(),
                    content_type: mime!(Text/Plain; Charset=Utf8), // FIXME
                    size: read - crlf_boundary.len()
                };
                files.push( (key,ufile) );

                state = State::ReadingHeaders;
            },
        }
    }
}

fn get_boundary<'a,'b>(request: &Request<'a,'b>) -> Result<String,Error>
{
    // Verify that the request is 'content-type: multipart/form-data'
    let content_type: &ContentType = match request.headers.get() {
        Some(h) => h,
        None => return Err(Error::NoRequestContentType),
    };
    let ContentType(ref mime) = *content_type;
    let Mime(ref top_level, ref sub_level, ref params) = *mime;
    if *top_level != TopLevel::Multipart {
        return Err(Error::NotMultipart);
    }
    if *sub_level != SubLevel::FormData {
        return Err(Error::NotFormData);
    }

    // Get the boundary token
    for &(ref attr,ref value) in params.iter() {
        match (attr,value) {
            (&Attr::Ext(ref k), &Value::Ext(ref v)) => {
                if *k=="boundary" {
                    return Ok( format!("--{}",v.clone()) )
                }
            }
            _ => {}
        }
    }
    Err(Error::BoundaryNotSpecified)

}

#[test]
fn test1() {
    use mock::MockStream;
    use hyper::net::NetworkStream;
    use hyper::buffer::BufReader;

    let input=b"POST / HTTP/1.1\r\n\
                Host: example.domain\r\n\
                Content-Type: multipart/form-data; boundary=\"abcdefg\"\r\n\
                Content-Length: 217\r\n\
                \r\n\
                --abcdefg\r\n\
                Content-Disposition: form-data; name=\"field1\"\r\n\
                \r\n\
                data1\r\n\
                --abcdefg\r\n\
                Content-Disposition: form-data; name=\"field2\"; filename=\"file.txt\"\r\n\
                Content-Type: text/plain\r\n\
                \r\n\
                This is a file\r\n\
                with two lines\r\n\
                --abcdefg--";
    let mut mock = MockStream::with_input(input);

    let mock: &mut NetworkStream = &mut mock;
    let mut stream = BufReader::new(mock);
    let sock: SocketAddr = "127.0.0.1:80".parse().unwrap();

    let mut req = Request::new(&mut stream, sock).unwrap();

    match parse_multipart(&mut req) {
        Ok((fields,files)) => {
            assert_eq!(fields.len(),1);
            for (k,v) in fields {
                if &k=="field1" { assert_eq!( &v, "data1" ) }
            }
            assert_eq!(files.len(),1);
            for (k,file) in files {
                if &k=="field2" {
                    assert_eq!(file.size, 30);
                    assert!(file.filename.is_some());
                    assert_eq!(&file.filename.unwrap(), "file.txt");
                    // FIXME add content_type check after impl
                }
            }

        }
        Err(e) => {
            println!("FAILED ON: {:?}",e);
            assert!(false);
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2016 The android_logger Developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.

//! A logger which writes to android output.
//!
//! ## Example
//!
//! ```ignore
//! #[macro_use] extern crate log;
//! extern crate android_logger;
//!
//! use log::LogLevel;
//!
//! fn native_activity_create() {
//!     android_logger::init_once(LogLevel::Trace);
//!
//!     debug!("this is a debug {}", "message");
//!     error!("this is printed by default");
//!
//!     if log_enabled!(LogLevel::Info) {
//!         let x = 3 * 4; // expensive computation
//!         info!("the answer was: {}", x);
//!     }
//! }
//! ```

extern crate android_log_sys as log_ffi;
#[macro_use] extern crate log;

use log_ffi::LogPriority;
use log::{Log,LogLevel,LogMetadata,LogRecord};
use std::ffi::CStr;
use std::mem;
use std::fmt;
use std::ptr;

/// Output log to android system.
fn android_log(prio: log_ffi::LogPriority, tag: &CStr, msg: &CStr) {
    unsafe { log_ffi::__android_log_write(prio as log_ffi::c_int, tag.as_ptr() as *const log_ffi::c_char, msg.as_ptr() as *const log_ffi::c_char) };
}

/// Underlying android logger, for cases where `init_once` abstraction is not enough.
pub struct AndroidLogger;

const LOGGING_TAG_MAX_LEN: usize = 23;
const LOGGING_MSG_MAX_LEN: usize = 4000;

impl AndroidLogger {
    pub fn new() -> AndroidLogger {
        AndroidLogger
    }
}

impl Log for AndroidLogger {
    fn enabled(&self, _: &LogMetadata) -> bool {
        true
    }

    fn log(&self, record: &LogRecord) {
        // tag must not exceed LOGGING_TAG_MAX_LEN
        let mut tag_bytes: [u8; LOGGING_TAG_MAX_LEN + 1] = unsafe { mem::uninitialized() };
        // truncate the tag here to fit into LOGGING_TAG_MAX_LEN
        self.fill_tag_bytes(&mut tag_bytes, record);
        // use stack array as C string
        let tag: &CStr = unsafe { CStr::from_ptr(mem::transmute(tag_bytes.as_ptr())) };

        // message must not exceed LOGGING_MSG_MAX_LEN
        // therefore split log message into multiple log calls
        let mut writer = PlatformLogWriter::new(
            record.level(),
            tag
        );

        // use PlatformLogWriter to output chunks if they exceed max size
        let _ = fmt::write(&mut writer, *record.args());

        // output the remaining message (this would usually be the most common case)
        writer.flush();
    }
}

impl AndroidLogger {
    fn fill_tag_bytes(&self, array: &mut [u8], record: &LogRecord) {
        let tag_bytes_iter = record.location().module_path().bytes();
        if tag_bytes_iter.len() > LOGGING_TAG_MAX_LEN {
            for (input, output) in tag_bytes_iter
                .take(LOGGING_TAG_MAX_LEN - 2)
                .chain(b"..\0".iter().cloned())
                .zip(array.iter_mut())
            {
                *output = input;
            }
        } else {
            for (input, output) in tag_bytes_iter
                .chain(b"\0".iter().cloned())
                .zip(array.iter_mut())
            {
                *output = input;
            }
        }
    }
}

struct PlatformLogWriter<'a> {
    priority: LogPriority,
    len: usize,
    last_newline_index: usize,
    tag: &'a CStr,
    buffer: [u8; LOGGING_MSG_MAX_LEN + 1],
}

impl<'a> PlatformLogWriter<'a> {
    pub fn new<'r>(level: LogLevel, tag: &'r CStr) -> PlatformLogWriter<'r> {
        PlatformLogWriter {
            priority: match level {
                LogLevel::Warn => LogPriority::WARN,
                LogLevel::Info => LogPriority::INFO,
                LogLevel::Debug => LogPriority::DEBUG,
                LogLevel::Error => LogPriority::ERROR,
                LogLevel::Trace => LogPriority::VERBOSE,
            },
            len: 0,
            last_newline_index: 0,
            tag: tag,
            buffer: unsafe { mem::uninitialized() },
        }
    }

    /// Flush some bytes to android logger.
    ///
    /// If there is a newline, flush up to it.
    /// If ther was no newline, flush all.
    ///
    /// Not guaranteed to flush everything.
    fn temporal_flush(&mut self) {
        let total_len = self.len;

        if total_len == 0 {
            return;
        }

        if self.last_newline_index > 0 {
            let copy_from_index = self.last_newline_index;
            let remaining_chunk_len = total_len - copy_from_index;

            self.output_specified_len(copy_from_index);
            self.copy_bytes_to_start(copy_from_index, remaining_chunk_len);
            self.len = remaining_chunk_len;
        } else {
            self.output_specified_len(total_len);
            self.len = 0;
        }
        self.last_newline_index = 0;
    }

    /// Flush everything remaining to android logger.
    fn flush(&mut self) {
        let total_len = self.len;

        if total_len == 0 {
            return;
        }

        self.output_specified_len(total_len);
        self.len = 0;
        self.last_newline_index = 0;
    }

    /// Output buffer up until the \0 which will be placed at `len` position.
    fn output_specified_len(&mut self, len: usize) {
        let mut last_byte: u8 = b'\0';
        mem::swap(&mut last_byte, unsafe { self.buffer.get_unchecked_mut(len) });

        let msg: &CStr = unsafe { CStr::from_ptr(mem::transmute(self.buffer.as_ptr())) };
        android_log(self.priority, self.tag, msg);

        *unsafe { self.buffer.get_unchecked_mut(len) } = last_byte;
    }

    /// Copy `len` bytes from `index` position to starting position.
    fn copy_bytes_to_start(&mut self, index: usize, len: usize) {
        let src = unsafe { self.buffer.as_ptr().offset(index as isize) };
        let dst = self.buffer.as_mut_ptr();
        unsafe { ptr::copy(src, dst, len) };
    }
}

impl<'a> fmt::Write for PlatformLogWriter<'a> {
    fn write_str(&mut self, s: &str) -> fmt::Result {
        let mut incomming_bytes = s.as_bytes();

        while incomming_bytes.len() > 0 {
            let len = self.len;

            // write everything possible to buffer and mark last \n
            let new_len = len + incomming_bytes.len();
            let last_newline = self.buffer[len..LOGGING_MSG_MAX_LEN].iter_mut()
                .zip(incomming_bytes)
                .enumerate()
                .fold(None, |acc, (i, (output, input))| {
                    *output = *input;
                    if *input == b'\n' { Some(i) } else { acc }
                });

            // update last \n index
            if let Some(newline) = last_newline {
                self.last_newline_index = len + newline;
            }

            // calculate how many bytes were written
            let written_len = if new_len <= LOGGING_MSG_MAX_LEN {
                // if the len was not exceeded
                self.len = new_len;
                new_len - len // written len
            } else {
                // if new length was exceeded
                self.len = LOGGING_MSG_MAX_LEN;
                self.temporal_flush();

                LOGGING_MSG_MAX_LEN - len // written len
            };

            incomming_bytes = &incomming_bytes[written_len..];
        }

        Ok(())
    }
}

/// Initializes the global logger with an android logger.
///
/// This can be called many times, but will only initialize logging once,
/// and will not replace any other previously initialized logger.
///
/// It is ok to call this at the activity creation, and it will be
/// repeatedly called on every lifecycle restart (i.e. screen rotation).
pub fn init_once(log_level: LogLevel) {
    match log::set_logger(|max_log_level| {
        max_log_level.set(log_level.to_log_level_filter());
        return Box::new(AndroidLogger::new());
    }) {
        Err(e) => debug!("{}", e),
        _ => (),
    }
}
<commit_msg>Implement Default instead of `new`.<commit_after>// Copyright 2016 The android_logger Developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.

//! A logger which writes to android output.
//!
//! ## Example
//!
//! ```ignore
//! #[macro_use] extern crate log;
//! extern crate android_logger;
//!
//! use log::LogLevel;
//!
//! fn native_activity_create() {
//!     android_logger::init_once(LogLevel::Trace);
//!
//!     debug!("this is a debug {}", "message");
//!     error!("this is printed by default");
//!
//!     if log_enabled!(LogLevel::Info) {
//!         let x = 3 * 4; // expensive computation
//!         info!("the answer was: {}", x);
//!     }
//! }
//! ```

extern crate android_log_sys as log_ffi;
#[macro_use] extern crate log;

use log_ffi::LogPriority;
use log::{Log,LogLevel,LogMetadata,LogRecord};
use std::ffi::CStr;
use std::mem;
use std::fmt;
use std::ptr;

/// Output log to android system.
fn android_log(prio: log_ffi::LogPriority, tag: &CStr, msg: &CStr) {
    unsafe { log_ffi::__android_log_write(prio as log_ffi::c_int, tag.as_ptr() as *const log_ffi::c_char, msg.as_ptr() as *const log_ffi::c_char) };
}

/// Underlying android logger, for cases where `init_once` abstraction is not enough.
pub struct AndroidLogger;

const LOGGING_TAG_MAX_LEN: usize = 23;
const LOGGING_MSG_MAX_LEN: usize = 4000;

impl Default for AndroidLogger {
    fn default() -> AndroidLogger {
        AndroidLogger
    }
}

impl Log for AndroidLogger {
    fn enabled(&self, _: &LogMetadata) -> bool {
        true
    }

    fn log(&self, record: &LogRecord) {
        // tag must not exceed LOGGING_TAG_MAX_LEN
        let mut tag_bytes: [u8; LOGGING_TAG_MAX_LEN + 1] = unsafe { mem::uninitialized() };
        // truncate the tag here to fit into LOGGING_TAG_MAX_LEN
        self.fill_tag_bytes(&mut tag_bytes, record);
        // use stack array as C string
        let tag: &CStr = unsafe { CStr::from_ptr(mem::transmute(tag_bytes.as_ptr())) };

        // message must not exceed LOGGING_MSG_MAX_LEN
        // therefore split log message into multiple log calls
        let mut writer = PlatformLogWriter::new(
            record.level(),
            tag
        );

        // use PlatformLogWriter to output chunks if they exceed max size
        let _ = fmt::write(&mut writer, *record.args());

        // output the remaining message (this would usually be the most common case)
        writer.flush();
    }
}

impl AndroidLogger {
    fn fill_tag_bytes(&self, array: &mut [u8], record: &LogRecord) {
        let tag_bytes_iter = record.location().module_path().bytes();
        if tag_bytes_iter.len() > LOGGING_TAG_MAX_LEN {
            for (input, output) in tag_bytes_iter
                .take(LOGGING_TAG_MAX_LEN - 2)
                .chain(b"..\0".iter().cloned())
                .zip(array.iter_mut())
            {
                *output = input;
            }
        } else {
            for (input, output) in tag_bytes_iter
                .chain(b"\0".iter().cloned())
                .zip(array.iter_mut())
            {
                *output = input;
            }
        }
    }
}

struct PlatformLogWriter<'a> {
    priority: LogPriority,
    len: usize,
    last_newline_index: usize,
    tag: &'a CStr,
    buffer: [u8; LOGGING_MSG_MAX_LEN + 1],
}

impl<'a> PlatformLogWriter<'a> {
    pub fn new<'r>(level: LogLevel, tag: &'r CStr) -> PlatformLogWriter<'r> {
        PlatformLogWriter {
            priority: match level {
                LogLevel::Warn => LogPriority::WARN,
                LogLevel::Info => LogPriority::INFO,
                LogLevel::Debug => LogPriority::DEBUG,
                LogLevel::Error => LogPriority::ERROR,
                LogLevel::Trace => LogPriority::VERBOSE,
            },
            len: 0,
            last_newline_index: 0,
            tag: tag,
            buffer: unsafe { mem::uninitialized() },
        }
    }

    /// Flush some bytes to android logger.
    ///
    /// If there is a newline, flush up to it.
    /// If ther was no newline, flush all.
    ///
    /// Not guaranteed to flush everything.
    fn temporal_flush(&mut self) {
        let total_len = self.len;

        if total_len == 0 {
            return;
        }

        if self.last_newline_index > 0 {
            let copy_from_index = self.last_newline_index;
            let remaining_chunk_len = total_len - copy_from_index;

            self.output_specified_len(copy_from_index);
            self.copy_bytes_to_start(copy_from_index, remaining_chunk_len);
            self.len = remaining_chunk_len;
        } else {
            self.output_specified_len(total_len);
            self.len = 0;
        }
        self.last_newline_index = 0;
    }

    /// Flush everything remaining to android logger.
    fn flush(&mut self) {
        let total_len = self.len;

        if total_len == 0 {
            return;
        }

        self.output_specified_len(total_len);
        self.len = 0;
        self.last_newline_index = 0;
    }

    /// Output buffer up until the \0 which will be placed at `len` position.
    fn output_specified_len(&mut self, len: usize) {
        let mut last_byte: u8 = b'\0';
        mem::swap(&mut last_byte, unsafe { self.buffer.get_unchecked_mut(len) });

        let msg: &CStr = unsafe { CStr::from_ptr(mem::transmute(self.buffer.as_ptr())) };
        android_log(self.priority, self.tag, msg);

        *unsafe { self.buffer.get_unchecked_mut(len) } = last_byte;
    }

    /// Copy `len` bytes from `index` position to starting position.
    fn copy_bytes_to_start(&mut self, index: usize, len: usize) {
        let src = unsafe { self.buffer.as_ptr().offset(index as isize) };
        let dst = self.buffer.as_mut_ptr();
        unsafe { ptr::copy(src, dst, len) };
    }
}

impl<'a> fmt::Write for PlatformLogWriter<'a> {
    fn write_str(&mut self, s: &str) -> fmt::Result {
        let mut incomming_bytes = s.as_bytes();

        while incomming_bytes.len() > 0 {
            let len = self.len;

            // write everything possible to buffer and mark last \n
            let new_len = len + incomming_bytes.len();
            let last_newline = self.buffer[len..LOGGING_MSG_MAX_LEN].iter_mut()
                .zip(incomming_bytes)
                .enumerate()
                .fold(None, |acc, (i, (output, input))| {
                    *output = *input;
                    if *input == b'\n' { Some(i) } else { acc }
                });

            // update last \n index
            if let Some(newline) = last_newline {
                self.last_newline_index = len + newline;
            }

            // calculate how many bytes were written
            let written_len = if new_len <= LOGGING_MSG_MAX_LEN {
                // if the len was not exceeded
                self.len = new_len;
                new_len - len // written len
            } else {
                // if new length was exceeded
                self.len = LOGGING_MSG_MAX_LEN;
                self.temporal_flush();

                LOGGING_MSG_MAX_LEN - len // written len
            };

            incomming_bytes = &incomming_bytes[written_len..];
        }

        Ok(())
    }
}

/// Initializes the global logger with an android logger.
///
/// This can be called many times, but will only initialize logging once,
/// and will not replace any other previously initialized logger.
///
/// It is ok to call this at the activity creation, and it will be
/// repeatedly called on every lifecycle restart (i.e. screen rotation).
pub fn init_once(log_level: LogLevel) {
    match log::set_logger(|max_log_level| {
        max_log_level.set(log_level.to_log_level_filter());
        return Box::new(AndroidLogger::default());
    }) {
        Err(e) => debug!("{}", e),
        _ => (),
    }
}
<|endoftext|>


<commit_before>#![deny(missing_docs)]
#![allow(unstable)]

//! A SDL2 window back-end for the Piston game engine.

extern crate sdl2;
extern crate window;
extern crate shader_version;
extern crate input;
extern crate gl;
extern crate quack;

// External crates.
use std::mem::transmute;
use window::{
    WindowSettings,
    ShouldClose, Size, PollEvent, SwapBuffers,
    CaptureCursor, DrawSize, Title, ExitOnEsc
};
use input::{ keyboard, Button, MouseButton, Input, Motion };
use shader_version::opengl::OpenGL;
use quack::{ ActOn, Action, GetFrom, SetAt, Set };

/// A widow implemented by SDL2 back-end.
pub struct Sdl2Window {
    /// SDL window handle
    pub window: sdl2::video::Window,
    /// Allow dead code because this keeps track of the OpenGL context.
    /// Will be released on drop.
    #[allow(dead_code)]
    pub context: sdl2::video::GLContext,
    should_close: bool,
    mouse_relative: Option<(f64, f64)>,
    exit_on_esc: bool,
}

impl Sdl2Window {
    /// Creates a new game window for SDL2.
    pub fn new(opengl: OpenGL, settings: WindowSettings) -> Sdl2Window {
        sdl2::init(sdl2::INIT_EVERYTHING);
        let (major, minor) = opengl.get_major_minor();
        sdl2::video::gl_set_attribute(
            sdl2::video::GLAttr::GLContextMajorVersion,
            major
        );
        sdl2::video::gl_set_attribute(
            sdl2::video::GLAttr::GLContextMinorVersion,
            minor
        );
        sdl2::video::gl_set_attribute(
            sdl2::video::GLAttr::GLContextProfileMask,
            sdl2::video::GLProfile::GLCoreProfile as isize
        );
        if settings.samples != 0 {
            sdl2::video::gl_set_attribute(
                sdl2::video::GLAttr::GLMultiSampleBuffers,
                1
            );
            sdl2::video::gl_set_attribute(
                sdl2::video::GLAttr::GLMultiSampleSamples,
                settings.samples as isize
            );
        }

        let window = sdl2::video::Window::new(
            settings.title.as_slice(),
            sdl2::video::WindowPos::PosCentered,
            sdl2::video::WindowPos::PosCentered,
            settings.size[0] as isize,
            settings.size[1] as isize,
            sdl2::video::OPENGL| sdl2::video::RESIZABLE
        ).unwrap();
        if settings.fullscreen {
            window.set_fullscreen(sdl2::video::FullscreenType::FTTrue);
        }

        // Send text input events.
        sdl2::keyboard::start_text_input();

        let context = window.gl_create_context().unwrap();

        // Load the OpenGL function pointers
        gl::load_with(|s| unsafe {
            transmute(sdl2::video::gl_get_proc_address(s))
        });

        Sdl2Window {
            exit_on_esc: settings.exit_on_esc,
            should_close: false,
            window: window,
            context: context,
            mouse_relative: None,
        }
    }
}

impl Drop for Sdl2Window {
    fn drop(&mut self) {
        self.set_mut(CaptureCursor(false));
    }
}

impl GetFrom for (ShouldClose, Sdl2Window) {
    type Property = ShouldClose;
    type Object = Sdl2Window;

    fn get_from(obj: &Sdl2Window) -> ShouldClose {
        ShouldClose(obj.should_close)
    }
}

impl GetFrom for (Size, Sdl2Window) {
    type Property = Size;
    type Object = Sdl2Window;

    fn get_from(obj: &Sdl2Window) -> Size {
        let (w, h) = obj.window.get_size();
        Size([w as u32, h as u32])
    }
}

impl ActOn<()> for (SwapBuffers, Sdl2Window) {
    type Action = SwapBuffers;
    type Object = Sdl2Window;

    fn act_on(_: SwapBuffers, window: &mut Sdl2Window) {
        window.window.gl_swap_window();
    }
}

impl ActOn<Option<Input>> for (PollEvent, Sdl2Window) {
    type Action = PollEvent;
    type Object = Sdl2Window;

    fn act_on(
        _: PollEvent,
        window: &mut Sdl2Window
    ) -> Option<Input> {
        match window.mouse_relative {
            Some((x, y)) => {
                window.mouse_relative = None;
                return Some(Input::Move(Motion::MouseRelative(x, y)));
            }
            None => {}
        }
        match sdl2::event::poll_event() {
            sdl2::event::Event::Quit(_) => {
                window.should_close = true;
            }
            sdl2::event::Event::TextInput(_, _, text) => {
                return Some(Input::Text(text));
            }
            sdl2::event::Event::KeyDown(_, _, key, _, _, repeat) => {
                // SDL2 repeats the key down event.
                // If the event is the same as last one, ignore it.
                if repeat {
                    return window.action(PollEvent)
                }

                if window.exit_on_esc
                && key == sdl2::keycode::KeyCode::Escape {
                    window.should_close = true;
                } else {
                    return Some(Input::Press(Button::Keyboard(sdl2_map_key(key))));
                }
            }
            sdl2::event::Event::KeyUp(_, _, key, _, _, repeat) => {
                if repeat {
                    return window.action(PollEvent)
                }
                return Some(Input::Release(Button::Keyboard(sdl2_map_key(key))));
            }
            sdl2::event::Event::MouseButtonDown(_, _, _, button, _, _) => {
                return Some(Input::Press(Button::Mouse(sdl2_map_mouse(button))));
            }
            sdl2::event::Event::MouseButtonUp(_, _, _, button, _, _) => {
                return Some(Input::Release(Button::Mouse(sdl2_map_mouse(button))));
            }
            sdl2::event::Event::MouseMotion(_, _, _, _, x, y, dx, dy) => {
                // Send relative move movement next time.
                window.mouse_relative = Some((dx as f64, dy as f64));
                return Some(Input::Move(Motion::MouseCursor(x as f64, y as f64)));
            },
            sdl2::event::Event::MouseWheel(_, _, _, x, y) => {
                return Some(Input::Move(Motion::MouseScroll(x as f64, y as f64)));
            }
            sdl2::event::Event::Window(_, _, sdl2::event::WindowEventId::Resized, w, h) => {
                return Some(Input::Resize(w as u32, h as u32));
            }
            sdl2::event::Event::Window(_, _, sdl2::event::WindowEventId::FocusGained, _, _) => {
                return Some(Input::Focus(true));
            }
            sdl2::event::Event::Window(_, _, sdl2::event::WindowEventId::FocusLost, _, _) => {
                return Some(Input::Focus(false));
            }
            _ => {}
        }
        None
    }
}

impl SetAt for (CaptureCursor, Sdl2Window) {
    type Property = CaptureCursor;
    type Object = Sdl2Window;

    fn set_at(
        CaptureCursor(enabled):
        CaptureCursor, _window: &mut Sdl2Window
    ) {
        sdl2::mouse::set_relative_mouse_mode(enabled)
    }
}

impl SetAt for (ShouldClose, Sdl2Window) {
    type Property = ShouldClose;
    type Object = Sdl2Window;

    fn set_at(
        ShouldClose(val): ShouldClose,
        window: &mut Sdl2Window
    ) {
        window.should_close = val;
    }
}

impl GetFrom for (DrawSize, Sdl2Window) {
    type Property = DrawSize;
    type Object = Sdl2Window;

    fn get_from(obj: &Sdl2Window) -> DrawSize {
        let (w, h) = obj.window.get_drawable_size();
        DrawSize([w as u32, h as u32])
    }
}

impl GetFrom for (Title, Sdl2Window) {
    type Property = Title;
    type Object = Sdl2Window;

    fn get_from(obj: &Sdl2Window) -> Title {
        Title(obj.window.get_title())
    }
}

impl SetAt for (Title, Sdl2Window) {
    type Property = Title;
    type Object = Sdl2Window;

    fn set_at(Title(val): Title, window: &mut Sdl2Window) {
        window.window.set_title(val.as_slice());
    }
}

impl GetFrom for (ExitOnEsc, Sdl2Window) {
    type Property = ExitOnEsc;
    type Object = Sdl2Window;

    fn get_from(obj: &Sdl2Window) -> ExitOnEsc {
        ExitOnEsc(obj.exit_on_esc)
    }
}

impl SetAt for (ExitOnEsc, Sdl2Window) {
    type Property = ExitOnEsc;
    type Object = Sdl2Window;

    fn set_at(
        ExitOnEsc(val): ExitOnEsc,
        window: &mut Sdl2Window
    ) {
        window.exit_on_esc = val;
    }
}

/// Maps a SDL2 key to piston-input key.
pub fn sdl2_map_key(keycode: sdl2::keycode::KeyCode) -> keyboard::Key {
    use std::num::FromPrimitive;
    FromPrimitive::from_u64(keycode as u64).unwrap()
}

/// Maps a SDL2 mouse button to piston-input button.
pub fn sdl2_map_mouse(button: sdl2::mouse::Mouse) -> MouseButton {
    match button {
        sdl2::mouse::Mouse::Left => MouseButton::Left,
        sdl2::mouse::Mouse::Right => MouseButton::Right,
        sdl2::mouse::Mouse::Middle => MouseButton::Middle,
        sdl2::mouse::Mouse::X1 => MouseButton::X1,
        sdl2::mouse::Mouse::X2 => MouseButton::X2,
        sdl2::mouse::Mouse::Unknown(_) => MouseButton::Unknown,
    }
}
<commit_msg>Updated to match changes in SDL2 api<commit_after>#![deny(missing_docs)]
#![allow(unstable)]

//! A SDL2 window back-end for the Piston game engine.

extern crate sdl2;
extern crate window;
extern crate shader_version;
extern crate input;
extern crate gl;
extern crate quack;

// External crates.
use std::mem::transmute;
use window::{
    WindowSettings,
    ShouldClose, Size, PollEvent, SwapBuffers,
    CaptureCursor, DrawSize, Title, ExitOnEsc
};
use input::{ keyboard, Button, MouseButton, Input, Motion };
use shader_version::opengl::OpenGL;
use quack::{ ActOn, Action, GetFrom, SetAt, Set };

/// A widow implemented by SDL2 back-end.
pub struct Sdl2Window {
    /// SDL window handle
    pub window: sdl2::video::Window,
    /// Allow dead code because this keeps track of the OpenGL context.
    /// Will be released on drop.
    #[allow(dead_code)]
    pub context: sdl2::video::GLContext,
    should_close: bool,
    mouse_relative: Option<(f64, f64)>,
    exit_on_esc: bool,
}

impl Sdl2Window {
    /// Creates a new game window for SDL2.
    pub fn new(opengl: OpenGL, settings: WindowSettings) -> Sdl2Window {
        sdl2::init(sdl2::INIT_EVERYTHING);
        let (major, minor) = opengl.get_major_minor();
        sdl2::video::gl_set_attribute(
            sdl2::video::GLAttr::GLContextMajorVersion,
            major as i32
        );
        sdl2::video::gl_set_attribute(
            sdl2::video::GLAttr::GLContextMinorVersion,
            minor as i32
        );
        sdl2::video::gl_set_attribute(
            sdl2::video::GLAttr::GLContextProfileMask,
            sdl2::video::GLProfile::GLCoreProfile as i32
        );
        if settings.samples != 0 {
            sdl2::video::gl_set_attribute(
                sdl2::video::GLAttr::GLMultiSampleBuffers,
                1
            );
            sdl2::video::gl_set_attribute(
                sdl2::video::GLAttr::GLMultiSampleSamples,
                settings.samples as i32
            );
        }

        let window = sdl2::video::Window::new(
            settings.title.as_slice(),
            sdl2::video::WindowPos::PosCentered,
            sdl2::video::WindowPos::PosCentered,
            settings.size[0] as i32,
            settings.size[1] as i32,
            sdl2::video::OPENGL| sdl2::video::RESIZABLE
        ).unwrap();
        if settings.fullscreen {
            window.set_fullscreen(sdl2::video::FullscreenType::FTTrue);
        }

        // Send text input events.
        sdl2::keyboard::start_text_input();

        let context = window.gl_create_context().unwrap();

        // Load the OpenGL function pointers
        gl::load_with(|s| unsafe {
            transmute(sdl2::video::gl_get_proc_address(s))
        });

        Sdl2Window {
            exit_on_esc: settings.exit_on_esc,
            should_close: false,
            window: window,
            context: context,
            mouse_relative: None,
        }
    }
}

impl Drop for Sdl2Window {
    fn drop(&mut self) {
        self.set_mut(CaptureCursor(false));
    }
}

impl GetFrom for (ShouldClose, Sdl2Window) {
    type Property = ShouldClose;
    type Object = Sdl2Window;

    fn get_from(obj: &Sdl2Window) -> ShouldClose {
        ShouldClose(obj.should_close)
    }
}

impl GetFrom for (Size, Sdl2Window) {
    type Property = Size;
    type Object = Sdl2Window;

    fn get_from(obj: &Sdl2Window) -> Size {
        let (w, h) = obj.window.get_size();
        Size([w as u32, h as u32])
    }
}

impl ActOn<()> for (SwapBuffers, Sdl2Window) {
    type Action = SwapBuffers;
    type Object = Sdl2Window;

    fn act_on(_: SwapBuffers, window: &mut Sdl2Window) {
        window.window.gl_swap_window();
    }
}

impl ActOn<Option<Input>> for (PollEvent, Sdl2Window) {
    type Action = PollEvent;
    type Object = Sdl2Window;

    fn act_on(
        _: PollEvent,
        window: &mut Sdl2Window
    ) -> Option<Input> {
        match window.mouse_relative {
            Some((x, y)) => {
                window.mouse_relative = None;
                return Some(Input::Move(Motion::MouseRelative(x, y)));
            }
            None => {}
        }
        match sdl2::event::poll_event() {
            sdl2::event::Event::Quit(_) => {
                window.should_close = true;
            }
            sdl2::event::Event::TextInput(_, _, text) => {
                return Some(Input::Text(text));
            }
            sdl2::event::Event::KeyDown(_, _, key, _, _, repeat) => {
                // SDL2 repeats the key down event.
                // If the event is the same as last one, ignore it.
                if repeat {
                    return window.action(PollEvent)
                }

                if window.exit_on_esc
                && key == sdl2::keycode::KeyCode::Escape {
                    window.should_close = true;
                } else {
                    return Some(Input::Press(Button::Keyboard(sdl2_map_key(key))));
                }
            }
            sdl2::event::Event::KeyUp(_, _, key, _, _, repeat) => {
                if repeat {
                    return window.action(PollEvent)
                }
                return Some(Input::Release(Button::Keyboard(sdl2_map_key(key))));
            }
            sdl2::event::Event::MouseButtonDown(_, _, _, button, _, _) => {
                return Some(Input::Press(Button::Mouse(sdl2_map_mouse(button))));
            }
            sdl2::event::Event::MouseButtonUp(_, _, _, button, _, _) => {
                return Some(Input::Release(Button::Mouse(sdl2_map_mouse(button))));
            }
            sdl2::event::Event::MouseMotion(_, _, _, _, x, y, dx, dy) => {
                // Send relative move movement next time.
                window.mouse_relative = Some((dx as f64, dy as f64));
                return Some(Input::Move(Motion::MouseCursor(x as f64, y as f64)));
            },
            sdl2::event::Event::MouseWheel(_, _, _, x, y) => {
                return Some(Input::Move(Motion::MouseScroll(x as f64, y as f64)));
            }
            sdl2::event::Event::Window(_, _, sdl2::event::WindowEventId::Resized, w, h) => {
                return Some(Input::Resize(w as u32, h as u32));
            }
            sdl2::event::Event::Window(_, _, sdl2::event::WindowEventId::FocusGained, _, _) => {
                return Some(Input::Focus(true));
            }
            sdl2::event::Event::Window(_, _, sdl2::event::WindowEventId::FocusLost, _, _) => {
                return Some(Input::Focus(false));
            }
            _ => {}
        }
        None
    }
}

impl SetAt for (CaptureCursor, Sdl2Window) {
    type Property = CaptureCursor;
    type Object = Sdl2Window;

    fn set_at(
        CaptureCursor(enabled):
        CaptureCursor, _window: &mut Sdl2Window
    ) {
        sdl2::mouse::set_relative_mouse_mode(enabled)
    }
}

impl SetAt for (ShouldClose, Sdl2Window) {
    type Property = ShouldClose;
    type Object = Sdl2Window;

    fn set_at(
        ShouldClose(val): ShouldClose,
        window: &mut Sdl2Window
    ) {
        window.should_close = val;
    }
}

impl GetFrom for (DrawSize, Sdl2Window) {
    type Property = DrawSize;
    type Object = Sdl2Window;

    fn get_from(obj: &Sdl2Window) -> DrawSize {
        let (w, h) = obj.window.get_drawable_size();
        DrawSize([w as u32, h as u32])
    }
}

impl GetFrom for (Title, Sdl2Window) {
    type Property = Title;
    type Object = Sdl2Window;

    fn get_from(obj: &Sdl2Window) -> Title {
        Title(obj.window.get_title())
    }
}

impl SetAt for (Title, Sdl2Window) {
    type Property = Title;
    type Object = Sdl2Window;

    fn set_at(Title(val): Title, window: &mut Sdl2Window) {
        window.window.set_title(val.as_slice());
    }
}

impl GetFrom for (ExitOnEsc, Sdl2Window) {
    type Property = ExitOnEsc;
    type Object = Sdl2Window;

    fn get_from(obj: &Sdl2Window) -> ExitOnEsc {
        ExitOnEsc(obj.exit_on_esc)
    }
}

impl SetAt for (ExitOnEsc, Sdl2Window) {
    type Property = ExitOnEsc;
    type Object = Sdl2Window;

    fn set_at(
        ExitOnEsc(val): ExitOnEsc,
        window: &mut Sdl2Window
    ) {
        window.exit_on_esc = val;
    }
}

/// Maps a SDL2 key to piston-input key.
pub fn sdl2_map_key(keycode: sdl2::keycode::KeyCode) -> keyboard::Key {
    use std::num::FromPrimitive;
    FromPrimitive::from_u64(keycode as u64).unwrap()
}

/// Maps a SDL2 mouse button to piston-input button.
pub fn sdl2_map_mouse(button: sdl2::mouse::Mouse) -> MouseButton {
    match button {
        sdl2::mouse::Mouse::Left => MouseButton::Left,
        sdl2::mouse::Mouse::Right => MouseButton::Right,
        sdl2::mouse::Mouse::Middle => MouseButton::Middle,
        sdl2::mouse::Mouse::X1 => MouseButton::X1,
        sdl2::mouse::Mouse::X2 => MouseButton::X2,
        sdl2::mouse::Mouse::Unknown(_) => MouseButton::Unknown,
    }
}
<|endoftext|>


<commit_before>#[macro_use] extern crate bitflags;
extern crate gl as gl_lib;

mod context;
mod types;

pub mod gl {
    pub use super::gl_lib::*;
}

pub use context::Context;
<commit_msg>Export symbols in the `types` module<commit_after>#[macro_use] extern crate bitflags;
extern crate gl as gl_lib;

mod context;
mod types;

pub mod gl {
    pub use super::gl_lib::*;
}

pub use context::Context;
pub use types::{Color, BufferBits,
                COLOR_BUFFER_BIT, DEPTH_BUFFER_BIT, STENCIL_BUFFER_BIT};
<|endoftext|>