<commit_before>//! Iterators that are sources (produce elements from parameters,
//! not from another iterator).

/// An iterator source that produces elements indefinitely by calling
/// a given closure.
///
/// Iterator elment type is the return type of the closure.
///
/// ## Example
///
/// ```
/// use itertools::RepeatCall;
///
/// assert!(itertools::equal(
///     RepeatCall::new(|| "A".to_string()).take(5),
///     vec!["A", "A", "A", "A", "A"]
/// ));
///
/// let mut x = 1;
/// assert!(itertools::equal(
///     RepeatCall::new(|| { x = -x; x }).take(5),
///     vec![-1, 1, -1, 1, -1]
/// ));
/// ```
pub struct RepeatCall<F> {
    f: F,
}

impl<F> RepeatCall<F>
{
    /// Create a new **RepeatCall** from a closure.
    pub fn new(func: F) -> Self where
        F: FnMut() -> A,
    {
        RepeatCall { f: func }
    }
}

impl Iterator for RepeatCall<F> where
    F: FnMut() -> A,
{
    type Item = A;

    #[inline]
    fn next(&mut self) -> Option
    {
        Some((self.f)())
    }

    fn size_hint(&self) -> (usize, Option<usize>)
    {
        (usize::max_value(), None)
    }
}

impl DoubleEndedIterator for RepeatCall<F> where
    F: FnMut() -> A,
{
    #[inline]
    fn next_back(&mut self) -> Option { self.next() }
}

<commit_msg>Fix typo in docs<commit_after>//! Iterators that are sources (produce elements from parameters,
//! not from another iterator).

/// An iterator source that produces elements indefinitely by calling
/// a given closure.
///
/// Iterator element type is the return type of the closure.
///
/// ## Example
///
/// ```
/// use itertools::RepeatCall;
///
/// assert!(itertools::equal(
///     RepeatCall::new(|| "A".to_string()).take(5),
///     vec!["A", "A", "A", "A", "A"]
/// ));
///
/// let mut x = 1;
/// assert!(itertools::equal(
///     RepeatCall::new(|| { x = -x; x }).take(5),
///     vec![-1, 1, -1, 1, -1]
/// ));
/// ```
pub struct RepeatCall<F> {
    f: F,
}

impl<F> RepeatCall<F>
{
    /// Create a new **RepeatCall** from a closure.
    pub fn new(func: F) -> Self where
        F: FnMut() -> A,
    {
        RepeatCall { f: func }
    }
}

impl Iterator for RepeatCall<F> where
    F: FnMut() -> A,
{
    type Item = A;

    #[inline]
    fn next(&mut self) -> Option
    {
        Some((self.f)())
    }

    fn size_hint(&self) -> (usize, Option<usize>)
    {
        (usize::max_value(), None)
    }
}

impl DoubleEndedIterator for RepeatCall<F> where
    F: FnMut() -> A,
{
    #[inline]
    fn next_back(&mut self) -> Option { self.next() }
}

<|endoftext|>


<commit_before>//! This LintPass catches both string addition and string addition + assignment
//!
//! Note that since we have two lints where one subsumes the other, we try to
//! disable the subsumed lint unless it has a higher level

use rustc::lint::*;
use rustc_front::hir::*;
use syntax::codemap::Spanned;

use utils::{is_exp_equal, match_type, span_lint, walk_ptrs_ty, get_parent_expr};
use utils::STRING_PATH;

/// **What it does:** This lint matches code of the form `x = x + y` (without `let`!). It is `Allow` by default.
///
/// **Why is this bad?** Because this expression needs another copy as opposed to `x.push_str(y)` (in practice LLVM will usually elide it, though). Despite [llogiq](https://github.com/llogiq)'s reservations, this lint also is `allow` by default, as some people opine that it's more readable.
///
/// **Known problems:** None. Well apart from the lint being `allow` by default. :smile:
///
/// **Example:**
///
/// ```
/// let mut x = "Hello".to_owned();
/// x = x + ", World";
/// ```
declare_lint! {
    pub STRING_ADD_ASSIGN,
    Allow,
    "using `x = x + ..` where x is a `String`; suggests using `push_str()` instead"
}

/// **What it does:** The `string_add` lint matches all instances of `x + _` where `x` is of type `String`, but only if [`string_add_assign`](#string_add_assign) does *not* match.  It is `Allow` by default.
///
/// **Why is this bad?** It's not bad in and of itself. However, this particular `Add` implementation is asymmetric (the other operand need not be `String`, but `x` does), while addition as mathematically defined is symmetric, also the `String::push_str(_)` function is a perfectly good replacement. Therefore some dislike it and wish not to have it in their code.
///
/// That said, other people think that String addition, having a long tradition in other languages is actually fine, which is why we decided to make this particular lint `allow` by default.
///
/// **Known problems:** None
///
/// **Example:**
///
/// ```
/// let x = "Hello".to_owned();
/// x + ", World"
/// ```
declare_lint! {
    pub STRING_ADD,
    Allow,
    "using `x + ..` where x is a `String`; suggests using `push_str()` instead"
}

/// **What it does:** This lint matches the `as_bytes` method called on string
/// literals that contain only ascii characters. It is `Warn` by default.
///
/// **Why is this bad?** Byte string literals (e.g. `b"foo"`) can be used instead. They are shorter but less discoverable than `as_bytes()`.
///
/// **Example:**
///
/// ```
/// let bs = "a byte string".as_bytes();
/// ```
declare_lint! {
    pub STRING_LIT_AS_BYTES,
    Warn,
    "calling `as_bytes` on a string literal; suggests using a byte string literal instead"
}

#[derive(Copy, Clone)]
pub struct StringAdd;

impl LintPass for StringAdd {
    fn get_lints(&self) -> LintArray {
        lint_array!(STRING_ADD, STRING_ADD_ASSIGN)
    }
}

impl LateLintPass for StringAdd {
    fn check_expr(&mut self, cx: &LateContext, e: &Expr) {
        if let ExprBinary(Spanned{ node: BiAdd, .. }, ref left, _) = e.node {
            if is_string(cx, left) {
                if let Allow = cx.current_level(STRING_ADD_ASSIGN) {
                    // the string_add_assign is allow, so no duplicates
                } else {
                    let parent = get_parent_expr(cx, e);
                    if let Some(ref p) = parent {
                        if let ExprAssign(ref target, _) = p.node {
                            // avoid duplicate matches
                            if is_exp_equal(cx, target, left) {
                                return;
                            }
                        }
                    }
                }
                span_lint(cx,
                          STRING_ADD,
                          e.span,
                          "you added something to a string. Consider using `String::push_str()` instead");
            }
        } else if let ExprAssign(ref target, ref src) = e.node {
            if is_string(cx, target) && is_add(cx, src, target) {
                span_lint(cx,
                          STRING_ADD_ASSIGN,
                          e.span,
                          "you assigned the result of adding something to this string. Consider using \
                           `String::push_str()` instead");
            }
        }
    }
}

fn is_string(cx: &LateContext, e: &Expr) -> bool {
    match_type(cx, walk_ptrs_ty(cx.tcx.expr_ty(e)), &STRING_PATH)
}

fn is_add(cx: &LateContext, src: &Expr, target: &Expr) -> bool {
    match src.node {
        ExprBinary(Spanned{ node: BiAdd, .. }, ref left, _) => is_exp_equal(cx, target, left),
        ExprBlock(ref block) => {
            block.stmts.is_empty() && block.expr.as_ref().map_or(false, |expr| is_add(cx, expr, target))
        }
        _ => false,
    }
}

#[derive(Copy, Clone)]
pub struct StringLitAsBytes;

impl LintPass for StringLitAsBytes {
    fn get_lints(&self) -> LintArray {
        lint_array!(STRING_LIT_AS_BYTES)
    }
}

impl LateLintPass for StringLitAsBytes {
    fn check_expr(&mut self, cx: &LateContext, e: &Expr) {
        use std::ascii::AsciiExt;
        use syntax::ast::Lit_::LitStr;
        use utils::snippet;

        if let ExprMethodCall(ref name, _, ref args) = e.node {
            if name.node.as_str() == "as_bytes" {
                if let ExprLit(ref lit) = args[0].node {
                    if let LitStr(ref lit_content, _) = lit.node {
                        if lit_content.chars().all(|c| c.is_ascii()) {
                            let msg = format!("calling `as_bytes()` on a string literal. \
                                               Consider using a byte string literal instead: \
                                               `b{}`",
                                               snippet(cx, args[0].span, r#""foo""#));
                            span_lint(cx, STRING_LIT_AS_BYTES, e.span, &msg);
                        }
                    }
                }
            }
        }
    }
}
<commit_msg>Add macro check<commit_after>//! This LintPass catches both string addition and string addition + assignment
//!
//! Note that since we have two lints where one subsumes the other, we try to
//! disable the subsumed lint unless it has a higher level

use rustc::lint::*;
use rustc_front::hir::*;
use syntax::codemap::Spanned;

use utils::{is_exp_equal, match_type, span_lint, walk_ptrs_ty, get_parent_expr};
use utils::STRING_PATH;

/// **What it does:** This lint matches code of the form `x = x + y` (without `let`!). It is `Allow` by default.
///
/// **Why is this bad?** Because this expression needs another copy as opposed to `x.push_str(y)` (in practice LLVM will usually elide it, though). Despite [llogiq](https://github.com/llogiq)'s reservations, this lint also is `allow` by default, as some people opine that it's more readable.
///
/// **Known problems:** None. Well apart from the lint being `allow` by default. :smile:
///
/// **Example:**
///
/// ```
/// let mut x = "Hello".to_owned();
/// x = x + ", World";
/// ```
declare_lint! {
    pub STRING_ADD_ASSIGN,
    Allow,
    "using `x = x + ..` where x is a `String`; suggests using `push_str()` instead"
}

/// **What it does:** The `string_add` lint matches all instances of `x + _` where `x` is of type `String`, but only if [`string_add_assign`](#string_add_assign) does *not* match.  It is `Allow` by default.
///
/// **Why is this bad?** It's not bad in and of itself. However, this particular `Add` implementation is asymmetric (the other operand need not be `String`, but `x` does), while addition as mathematically defined is symmetric, also the `String::push_str(_)` function is a perfectly good replacement. Therefore some dislike it and wish not to have it in their code.
///
/// That said, other people think that String addition, having a long tradition in other languages is actually fine, which is why we decided to make this particular lint `allow` by default.
///
/// **Known problems:** None
///
/// **Example:**
///
/// ```
/// let x = "Hello".to_owned();
/// x + ", World"
/// ```
declare_lint! {
    pub STRING_ADD,
    Allow,
    "using `x + ..` where x is a `String`; suggests using `push_str()` instead"
}

/// **What it does:** This lint matches the `as_bytes` method called on string
/// literals that contain only ascii characters. It is `Warn` by default.
///
/// **Why is this bad?** Byte string literals (e.g. `b"foo"`) can be used instead. They are shorter but less discoverable than `as_bytes()`.
///
/// **Example:**
///
/// ```
/// let bs = "a byte string".as_bytes();
/// ```
declare_lint! {
    pub STRING_LIT_AS_BYTES,
    Warn,
    "calling `as_bytes` on a string literal; suggests using a byte string literal instead"
}

#[derive(Copy, Clone)]
pub struct StringAdd;

impl LintPass for StringAdd {
    fn get_lints(&self) -> LintArray {
        lint_array!(STRING_ADD, STRING_ADD_ASSIGN)
    }
}

impl LateLintPass for StringAdd {
    fn check_expr(&mut self, cx: &LateContext, e: &Expr) {
        if let ExprBinary(Spanned{ node: BiAdd, .. }, ref left, _) = e.node {
            if is_string(cx, left) {
                if let Allow = cx.current_level(STRING_ADD_ASSIGN) {
                    // the string_add_assign is allow, so no duplicates
                } else {
                    let parent = get_parent_expr(cx, e);
                    if let Some(ref p) = parent {
                        if let ExprAssign(ref target, _) = p.node {
                            // avoid duplicate matches
                            if is_exp_equal(cx, target, left) {
                                return;
                            }
                        }
                    }
                }
                span_lint(cx,
                          STRING_ADD,
                          e.span,
                          "you added something to a string. Consider using `String::push_str()` instead");
            }
        } else if let ExprAssign(ref target, ref src) = e.node {
            if is_string(cx, target) && is_add(cx, src, target) {
                span_lint(cx,
                          STRING_ADD_ASSIGN,
                          e.span,
                          "you assigned the result of adding something to this string. Consider using \
                           `String::push_str()` instead");
            }
        }
    }
}

fn is_string(cx: &LateContext, e: &Expr) -> bool {
    match_type(cx, walk_ptrs_ty(cx.tcx.expr_ty(e)), &STRING_PATH)
}

fn is_add(cx: &LateContext, src: &Expr, target: &Expr) -> bool {
    match src.node {
        ExprBinary(Spanned{ node: BiAdd, .. }, ref left, _) => is_exp_equal(cx, target, left),
        ExprBlock(ref block) => {
            block.stmts.is_empty() && block.expr.as_ref().map_or(false, |expr| is_add(cx, expr, target))
        }
        _ => false,
    }
}

#[derive(Copy, Clone)]
pub struct StringLitAsBytes;

impl LintPass for StringLitAsBytes {
    fn get_lints(&self) -> LintArray {
        lint_array!(STRING_LIT_AS_BYTES)
    }
}

impl LateLintPass for StringLitAsBytes {
    fn check_expr(&mut self, cx: &LateContext, e: &Expr) {
        use std::ascii::AsciiExt;
        use syntax::ast::Lit_::LitStr;
        use utils::{snippet, in_macro};

        if let ExprMethodCall(ref name, _, ref args) = e.node {
            if name.node.as_str() == "as_bytes" {
                if let ExprLit(ref lit) = args[0].node {
                    if let LitStr(ref lit_content, _) = lit.node {
                        if lit_content.chars().all(|c| c.is_ascii()) && !in_macro(cx, e.span) {
                            let msg = format!("calling `as_bytes()` on a string literal. \
                                               Consider using a byte string literal instead: \
                                               `b{}`",
                                               snippet(cx, args[0].span, r#""foo""#));
                            span_lint(cx, STRING_LIT_AS_BYTES, e.span, &msg);
                        }
                    }
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2015-2016, The Gtk-rs Project Developers.
// See the COPYRIGHT file at the top-level directory of this distribution.
// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>

use std::mem;
use std::ptr;
use std::slice;
use libc::{c_ulong, c_void};
use std::ffi::CString;

#[cfg(feature = "use_glib")]
use glib::translate::*;
use ffi;
use ::enums::{
    Content,
    Status,
    SurfaceType,
};

#[derive(Debug)]
pub struct Surface(*mut ffi::cairo_surface_t, bool);

impl Surface {
    pub unsafe fn from_raw_none(ptr: *mut ffi::cairo_surface_t) -> Surface {
        assert!(!ptr.is_null());
        ffi::cairo_surface_reference(ptr);
        Surface(ptr, false)
    }

    pub unsafe fn from_raw_borrow(ptr: *mut ffi::cairo_surface_t) -> Surface {
        assert!(!ptr.is_null());
        Surface(ptr, true)
    }

    pub unsafe fn from_raw_full(ptr: *mut ffi::cairo_surface_t) -> Surface {
        assert!(!ptr.is_null());
        Surface(ptr, false)
    }

    pub fn to_raw_none(&self) -> *mut ffi::cairo_surface_t {
        self.0
    }

    pub fn create_similar(&self, content: Content, width: i32, height: i32) -> Surface {
        unsafe {
            Self::from_raw_full(ffi::cairo_surface_create_similar(self.0,
                                                                  content.into(),
                                                                  width,
                                                                  height))
        }
    }

    pub fn get_mime_data(&self, mime_type: &str) -> Option<Vec<u8>> {
        let data_ptr: *mut u8 = ptr::null_mut();
        let mut length: c_ulong = 0;
        unsafe {
            let mime_type = CString::new(mime_type).unwrap();
            ffi::cairo_surface_get_mime_data(
                self.to_raw_none(),
                mime_type.as_ptr(),
                &data_ptr,
                &mut length,
            );
            if !data_ptr.is_null() && length != 0 {
                Some(slice::from_raw_parts(
                    data_ptr as *const u8,
                    length as usize,
                    ).to_vec()
                )
            } else {
                None
            }
        }
    }

    pub unsafe fn get_mime_data_raw(&self, mime_type: &str) -> Option<&[u8]> {
        let data_ptr: *mut u8 = ptr::null_mut();
        let mut length: c_ulong = 0;
        let mime_type = CString::new(mime_type).unwrap();
        ffi::cairo_surface_get_mime_data(
            self.to_raw_none(),
            mime_type.as_ptr(),
            &data_ptr,
            &mut length,
        );
        if !data_ptr.is_null() && length != 0 {
            Some(slice::from_raw_parts(
                    data_ptr as *const u8,
                    length as usize,
                )
            )
        } else {
            None
        }
    }

    pub fn set_mime_data<T: AsRef<[u8]> + Send + 'static>(
        &self,
        mime_type: &str,
        slice: T) -> Result<(), Status> {
        let b = Box::new(slice);
        let (size, data) = {
            let slice = (*b).as_ref();
            (slice.len(), slice.as_ptr())
        };

        let user_data = Box::into_raw(b);

        let status = unsafe {
            let mime_type = CString::new(mime_type).unwrap();
            ffi::cairo_surface_set_mime_data(self.to_raw_none(),
                mime_type.as_ptr(),
                data,
                size as c_ulong,
                Some(unbox::<T>),
                user_data as *mut _,
            )
        };

        match Status::from(status) {
            Status::Success => Ok(()),
            x => Err(x),
        }
    }

    pub fn supports_mime_type(&self, mime_type: &str) -> bool {
        unsafe {
            let mime_type = CString::new(mime_type).unwrap();
            ffi::cairo_surface_supports_mime_type(self.0, mime_type.as_ptr()).as_bool()
        }
    }

    pub fn set_device_offset(&mut self, x_offset: f64, y_offset: f64) {
        unsafe { ffi::cairo_surface_set_device_offset(self.to_raw_none(), x_offset, y_offset) }
    }

    pub fn get_device_offset(&self) -> (f64, f64) {
        let mut x_offset = 0.0f64;
        let mut y_offset = 0.0f64;
        unsafe { ffi::cairo_surface_get_device_offset(self.to_raw_none(), &mut x_offset, &mut y_offset); }
        (x_offset, y_offset)
    }

    pub fn set_device_scale(&mut self, x_scale: f64, y_scale: f64) {
        unsafe { ffi::cairo_surface_set_device_scale(self.to_raw_none(), x_scale, y_scale) }
    }

    pub fn get_device_scale(&self) -> (f64, f64) {
        let mut x_scale = 0.0f64;
        let mut y_scale = 0.0f64;
        unsafe { ffi::cairo_surface_get_device_scale(self.to_raw_none(), &mut x_scale, &mut y_scale); }
        (x_scale, y_scale)
    }

    pub fn set_fallback_resolution(&mut self, x_pixels_per_inch: f64, y_pixels_per_inch: f64) {
        unsafe { ffi::cairo_surface_set_fallback_resolution(self.to_raw_none(), x_pixels_per_inch, y_pixels_per_inch) }
    }

    pub fn get_fallback_resolution(&self) -> (f64, f64) {
        let mut x_pixels_per_inch = 0.0f64;
        let mut y_pixels_per_inch = 0.0f64;
        unsafe { ffi::cairo_surface_get_fallback_resolution(self.to_raw_none(), &mut x_pixels_per_inch, &mut y_pixels_per_inch); }
        (x_pixels_per_inch, y_pixels_per_inch)
    }
}

#[cfg(feature = "use_glib")]
impl<'a> ToGlibPtr<'a, *mut ffi::cairo_surface_t> for Surface {
    type Storage = &'a Surface;

    #[inline]
    fn to_glib_none(&'a self) -> Stash<'a, *mut ffi::cairo_surface_t, Self> {
        Stash(self.to_raw_none(), self)
    }

    #[inline]
    fn to_glib_full(&self) -> *mut ffi::cairo_surface_t {
        unsafe {
            ffi::cairo_surface_reference(self.to_raw_none())
        }
    }
}

#[cfg(feature = "use_glib")]
impl FromGlibPtrNone<*mut ffi::cairo_surface_t> for Surface {
    #[inline]
    unsafe fn from_glib_none(ptr: *mut ffi::cairo_surface_t) -> Surface {
        Self::from_raw_none(ptr)
    }
}

#[cfg(feature = "use_glib")]
impl FromGlibPtrBorrow<*mut ffi::cairo_surface_t> for Surface {
    #[inline]
    unsafe fn from_glib_borrow(ptr: *mut ffi::cairo_surface_t) -> Surface {
        Self::from_raw_borrow(ptr)
    }
}

#[cfg(feature = "use_glib")]
impl FromGlibPtrFull<*mut ffi::cairo_surface_t> for Surface {
    #[inline]
    unsafe fn from_glib_full(ptr: *mut ffi::cairo_surface_t) -> Surface {
        Self::from_raw_full(ptr)
    }
}

#[cfg(feature = "use_glib")]
gvalue_impl!(Surface, ffi::cairo_surface_t, ffi::gobject::cairo_gobject_surface_get_type);

impl AsRef<Surface> for Surface {
    fn as_ref(&self) -> &Surface {
        self
    }
}

impl Clone for Surface {
    fn clone(&self) -> Surface {
        unsafe { Self::from_raw_none(self.0) }
    }
}

impl Drop for Surface {
    fn drop(&mut self) {
        if !self.1 {
            unsafe { ffi::cairo_surface_destroy(self.0); }
        }
    }
}

pub trait SurfaceExt {
    fn flush(&self);
    fn finish(&self);
    fn get_type(&self) -> SurfaceType;
    fn status(&self) -> Status;
}

impl<O: AsRef<Surface>> SurfaceExt for O {
    fn flush(&self) {
        unsafe { ffi::cairo_surface_flush(self.as_ref().0); }
    }

    fn finish(&self) {
        unsafe { ffi::cairo_surface_finish(self.as_ref().0); }
    }

    fn get_type(&self) -> SurfaceType {
        unsafe { SurfaceType::from(ffi::cairo_surface_get_type(self.as_ref().0)) }
    }

    fn status(&self) -> Status {
        unsafe { Status::from(ffi::cairo_surface_status(self.as_ref().0)) }
    }
}

pub(crate) trait SurfacePriv {
    unsafe fn set_user_data<K, T>(&self, key: &K, data: Box<T>) -> Result<(), Status>;
}

impl<O: AsRef<Surface>> SurfacePriv for O {
    unsafe fn set_user_data<K, T>(&self, key: &K, data: Box<T>) -> Result<(), Status> {
        let ptr: *mut T = Box::into_raw(data);

        assert_eq!(mem::size_of::<*mut c_void>(), mem::size_of_val(&ptr));

        let status = ffi::cairo_surface_set_user_data(self.as_ref().0, key as *const _ as *mut _,
            ptr as *mut c_void, Some(unbox::<T>));
        match Status::from(status) {
            Status::Success => Ok(()),
            x => Err(x),
        }
    }
}

unsafe extern "C" fn unbox<T>(data: *mut c_void) {
    let data: Box<T> = Box::from_raw(data as *mut T);
    drop(data);
}

#[cfg(test)]
mod tests {
    use Format;
    use ImageSurface;
    use constants::MIME_TYPE_PNG;

    #[test]
    fn mime_data() {
        let surface = ImageSurface::create(Format::ARgb32, 500, 500).unwrap();
        let data = surface.get_mime_data(MIME_TYPE_PNG);
        /* Initially the data for any mime type has to be none */
        assert!(data.is_none());

        assert!(surface.set_mime_data(MIME_TYPE_PNG, &[1u8, 10u8]).is_ok());
        let data = surface.get_mime_data(MIME_TYPE_PNG).unwrap();
        assert_eq!(data, &[1u8, 10u8]);
    }
}
<commit_msg>Make Surface methods &self from &mut self<commit_after>// Copyright 2015-2016, The Gtk-rs Project Developers.
// See the COPYRIGHT file at the top-level directory of this distribution.
// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>

use std::mem;
use std::ptr;
use std::slice;
use libc::{c_ulong, c_void};
use std::ffi::CString;

#[cfg(feature = "use_glib")]
use glib::translate::*;
use ffi;
use ::enums::{
    Content,
    Status,
    SurfaceType,
};

#[derive(Debug)]
pub struct Surface(*mut ffi::cairo_surface_t, bool);

impl Surface {
    pub unsafe fn from_raw_none(ptr: *mut ffi::cairo_surface_t) -> Surface {
        assert!(!ptr.is_null());
        ffi::cairo_surface_reference(ptr);
        Surface(ptr, false)
    }

    pub unsafe fn from_raw_borrow(ptr: *mut ffi::cairo_surface_t) -> Surface {
        assert!(!ptr.is_null());
        Surface(ptr, true)
    }

    pub unsafe fn from_raw_full(ptr: *mut ffi::cairo_surface_t) -> Surface {
        assert!(!ptr.is_null());
        Surface(ptr, false)
    }

    pub fn to_raw_none(&self) -> *mut ffi::cairo_surface_t {
        self.0
    }

    pub fn create_similar(&self, content: Content, width: i32, height: i32) -> Surface {
        unsafe {
            Self::from_raw_full(ffi::cairo_surface_create_similar(self.0,
                                                                  content.into(),
                                                                  width,
                                                                  height))
        }
    }

    pub fn get_mime_data(&self, mime_type: &str) -> Option<Vec<u8>> {
        let data_ptr: *mut u8 = ptr::null_mut();
        let mut length: c_ulong = 0;
        unsafe {
            let mime_type = CString::new(mime_type).unwrap();
            ffi::cairo_surface_get_mime_data(
                self.to_raw_none(),
                mime_type.as_ptr(),
                &data_ptr,
                &mut length,
            );
            if !data_ptr.is_null() && length != 0 {
                Some(slice::from_raw_parts(
                    data_ptr as *const u8,
                    length as usize,
                    ).to_vec()
                )
            } else {
                None
            }
        }
    }

    pub unsafe fn get_mime_data_raw(&self, mime_type: &str) -> Option<&[u8]> {
        let data_ptr: *mut u8 = ptr::null_mut();
        let mut length: c_ulong = 0;
        let mime_type = CString::new(mime_type).unwrap();
        ffi::cairo_surface_get_mime_data(
            self.to_raw_none(),
            mime_type.as_ptr(),
            &data_ptr,
            &mut length,
        );
        if !data_ptr.is_null() && length != 0 {
            Some(slice::from_raw_parts(
                    data_ptr as *const u8,
                    length as usize,
                )
            )
        } else {
            None
        }
    }

    pub fn set_mime_data<T: AsRef<[u8]> + Send + 'static>(
        &self,
        mime_type: &str,
        slice: T) -> Result<(), Status> {
        let b = Box::new(slice);
        let (size, data) = {
            let slice = (*b).as_ref();
            (slice.len(), slice.as_ptr())
        };

        let user_data = Box::into_raw(b);

        let status = unsafe {
            let mime_type = CString::new(mime_type).unwrap();
            ffi::cairo_surface_set_mime_data(self.to_raw_none(),
                mime_type.as_ptr(),
                data,
                size as c_ulong,
                Some(unbox::<T>),
                user_data as *mut _,
            )
        };

        match Status::from(status) {
            Status::Success => Ok(()),
            x => Err(x),
        }
    }

    pub fn supports_mime_type(&self, mime_type: &str) -> bool {
        unsafe {
            let mime_type = CString::new(mime_type).unwrap();
            ffi::cairo_surface_supports_mime_type(self.0, mime_type.as_ptr()).as_bool()
        }
    }

    pub fn set_device_offset(&self, x_offset: f64, y_offset: f64) {
        unsafe { ffi::cairo_surface_set_device_offset(self.to_raw_none(), x_offset, y_offset) }
    }

    pub fn get_device_offset(&self) -> (f64, f64) {
        let mut x_offset = 0.0f64;
        let mut y_offset = 0.0f64;
        unsafe { ffi::cairo_surface_get_device_offset(self.to_raw_none(), &mut x_offset, &mut y_offset); }
        (x_offset, y_offset)
    }

    pub fn set_device_scale(&self, x_scale: f64, y_scale: f64) {
        unsafe { ffi::cairo_surface_set_device_scale(self.to_raw_none(), x_scale, y_scale) }
    }

    pub fn get_device_scale(&self) -> (f64, f64) {
        let mut x_scale = 0.0f64;
        let mut y_scale = 0.0f64;
        unsafe { ffi::cairo_surface_get_device_scale(self.to_raw_none(), &mut x_scale, &mut y_scale); }
        (x_scale, y_scale)
    }

    pub fn set_fallback_resolution(&self, x_pixels_per_inch: f64, y_pixels_per_inch: f64) {
        unsafe { ffi::cairo_surface_set_fallback_resolution(self.to_raw_none(), x_pixels_per_inch, y_pixels_per_inch) }
    }

    pub fn get_fallback_resolution(&self) -> (f64, f64) {
        let mut x_pixels_per_inch = 0.0f64;
        let mut y_pixels_per_inch = 0.0f64;
        unsafe { ffi::cairo_surface_get_fallback_resolution(self.to_raw_none(), &mut x_pixels_per_inch, &mut y_pixels_per_inch); }
        (x_pixels_per_inch, y_pixels_per_inch)
    }
}

#[cfg(feature = "use_glib")]
impl<'a> ToGlibPtr<'a, *mut ffi::cairo_surface_t> for Surface {
    type Storage = &'a Surface;

    #[inline]
    fn to_glib_none(&'a self) -> Stash<'a, *mut ffi::cairo_surface_t, Self> {
        Stash(self.to_raw_none(), self)
    }

    #[inline]
    fn to_glib_full(&self) -> *mut ffi::cairo_surface_t {
        unsafe {
            ffi::cairo_surface_reference(self.to_raw_none())
        }
    }
}

#[cfg(feature = "use_glib")]
impl FromGlibPtrNone<*mut ffi::cairo_surface_t> for Surface {
    #[inline]
    unsafe fn from_glib_none(ptr: *mut ffi::cairo_surface_t) -> Surface {
        Self::from_raw_none(ptr)
    }
}

#[cfg(feature = "use_glib")]
impl FromGlibPtrBorrow<*mut ffi::cairo_surface_t> for Surface {
    #[inline]
    unsafe fn from_glib_borrow(ptr: *mut ffi::cairo_surface_t) -> Surface {
        Self::from_raw_borrow(ptr)
    }
}

#[cfg(feature = "use_glib")]
impl FromGlibPtrFull<*mut ffi::cairo_surface_t> for Surface {
    #[inline]
    unsafe fn from_glib_full(ptr: *mut ffi::cairo_surface_t) -> Surface {
        Self::from_raw_full(ptr)
    }
}

#[cfg(feature = "use_glib")]
gvalue_impl!(Surface, ffi::cairo_surface_t, ffi::gobject::cairo_gobject_surface_get_type);

impl AsRef<Surface> for Surface {
    fn as_ref(&self) -> &Surface {
        self
    }
}

impl Clone for Surface {
    fn clone(&self) -> Surface {
        unsafe { Self::from_raw_none(self.0) }
    }
}

impl Drop for Surface {
    fn drop(&mut self) {
        if !self.1 {
            unsafe { ffi::cairo_surface_destroy(self.0); }
        }
    }
}

pub trait SurfaceExt {
    fn flush(&self);
    fn finish(&self);
    fn get_type(&self) -> SurfaceType;
    fn status(&self) -> Status;
}

impl<O: AsRef<Surface>> SurfaceExt for O {
    fn flush(&self) {
        unsafe { ffi::cairo_surface_flush(self.as_ref().0); }
    }

    fn finish(&self) {
        unsafe { ffi::cairo_surface_finish(self.as_ref().0); }
    }

    fn get_type(&self) -> SurfaceType {
        unsafe { SurfaceType::from(ffi::cairo_surface_get_type(self.as_ref().0)) }
    }

    fn status(&self) -> Status {
        unsafe { Status::from(ffi::cairo_surface_status(self.as_ref().0)) }
    }
}

pub(crate) trait SurfacePriv {
    unsafe fn set_user_data<K, T>(&self, key: &K, data: Box<T>) -> Result<(), Status>;
}

impl<O: AsRef<Surface>> SurfacePriv for O {
    unsafe fn set_user_data<K, T>(&self, key: &K, data: Box<T>) -> Result<(), Status> {
        let ptr: *mut T = Box::into_raw(data);

        assert_eq!(mem::size_of::<*mut c_void>(), mem::size_of_val(&ptr));

        let status = ffi::cairo_surface_set_user_data(self.as_ref().0, key as *const _ as *mut _,
            ptr as *mut c_void, Some(unbox::<T>));
        match Status::from(status) {
            Status::Success => Ok(()),
            x => Err(x),
        }
    }
}

unsafe extern "C" fn unbox<T>(data: *mut c_void) {
    let data: Box<T> = Box::from_raw(data as *mut T);
    drop(data);
}

#[cfg(test)]
mod tests {
    use Format;
    use ImageSurface;
    use constants::MIME_TYPE_PNG;

    #[test]
    fn mime_data() {
        let surface = ImageSurface::create(Format::ARgb32, 500, 500).unwrap();
        let data = surface.get_mime_data(MIME_TYPE_PNG);
        /* Initially the data for any mime type has to be none */
        assert!(data.is_none());

        assert!(surface.set_mime_data(MIME_TYPE_PNG, &[1u8, 10u8]).is_ok());
        let data = surface.get_mime_data(MIME_TYPE_PNG).unwrap();
        assert_eq!(data, &[1u8, 10u8]);
    }
}
<|endoftext|>


<commit_before>use dailyschedule::{Handler, Schedule};
use std::cell::{Cell, RefCell};
use std::collections::BTreeMap;
use std::path;
use std::rc::Rc;
use super::config;
use super::serial;
use time::{Duration, Timespec, at_utc, at, now_utc};
use zoneinfo::ZoneInfo;

#[derive(Eq, PartialEq, Debug, Copy, Clone)]
enum Context {
    Off,
    On
}

struct Switch {
    serial: serial::SerialClient,
    alias: String,
    last_on: Cell<Timespec>,
    state: Cell<Context>,
    valid_events: RefCell<BTreeMap<Timespec, Context>>,
}

impl Switch {
    fn new(alias: String, serial: serial::SerialClient) -> Switch {
        Switch {
            alias: alias,
            serial: serial,
            last_on: Cell::new(Timespec::new(0, 0)),
            state: Cell::new(Context::Off),
            valid_events: RefCell::new(BTreeMap::new()),
        }
    }
}

impl Handler<Context> for Switch {
    /// Hint the event-handler for future events; this function will
    /// add only valid events (where on event lies before the off event) to
    /// the valid events list.
    fn hint(&self, ts: &Timespec, context: &Context) {
        match context {
            &Context::On => self.last_on.set(*ts),
            &Context::Off => {
                if *ts > self.last_on.get() {
                    let mut events = self.valid_events.borrow_mut();
                    events.insert(self.last_on.get(), Context::On);
                    events.insert(*ts, Context::Off);
                }
            }
        }
    }

    /// Perform action only when the timestamp is considered valid;
    /// Remove the current or prior timestamps from the expected timestamps.
    fn kick(&self, ts: &Timespec, context: &Context) {
        if self.valid_events.borrow().contains_key(ts) {
            println!("XXX: {} {:?} {}", at(*ts).asctime(), context, self.alias); // XXX
            self.state.set(*context);
            match context {
                &Context::Off => self.serial.switch_off(&self.alias[..]),
                &Context::On => self.serial.switch_on(&self.alias[..]),
            }

            let mut events = self.valid_events.borrow_mut();

            // don't like the clone here, but keeps events mutable inside the loop
            for (e, _) in events.clone().iter().take_while(|&(&e, _)| e <= *ts) {
                events.remove(e);
            }
        }
    }
}

struct TrackerInner {
    serial: serial::SerialClient,
    config: config::Config,
    schedule: Schedule<Context, Switch>,
    schedule_ref: Timespec,
    initial: bool,
    switches: BTreeMap<String, Rc<Switch>>,
}

impl TrackerInner {
    fn load_schedule(&mut self) {
        self.switches.clear();
        for circle in self.config.circles.iter() {
            let switch = Rc::new(Switch::new(circle.alias.clone(), self.serial.clone()));
            match circle.default {
                config::CircleSetting::On => self.serial.switch_on(&circle.alias),
                config::CircleSetting::Off => self.serial.switch_off(&circle.alias),
                config::CircleSetting::Schedule => {
                    for toggle in circle.toggles.iter() {
                        let start = toggle.start.into_dailyevent(&self.config.device);
                        let end = toggle.end.into_dailyevent(&self.config.device);

                        self.schedule.add_event(start, switch.clone(), Context::On);
                        self.schedule.add_event(end, switch.clone(), Context::Off);
                    }
                }
            }
            self.switches.insert(circle.alias.clone(), switch);
        }
    }

    fn new(configfile: &path::PathBuf, zoneinfo: &ZoneInfo) -> TrackerInner {
        let config = config::Config::new(configfile).unwrap(); // XXX
        let schedule = Schedule::new(zoneinfo.clone());
        let serial = serial::Serial::spawn();

        // XXX: register circles / connect etc...

        let mut tracker = TrackerInner {
            config: config,
            schedule: schedule,
            serial: serial,
            schedule_ref: Timespec::new(0,0),
            initial: true,
            switches: BTreeMap::new(),
        };

        tracker.load_schedule();

        tracker
    }

    fn update_schedule(&mut self) {
        self.schedule.update_schedule(self.schedule_ref);
        self.schedule_ref = self.schedule_ref + Duration::days(1);
    }

    fn process_tick(&mut self, timestamp: Timespec) {
        if self.initial {
            self.initial = false;
            let mut tm = at_utc(timestamp);
            tm.tm_hour = 0;
            tm.tm_min = 0;
            tm.tm_sec = 0;
            tm.tm_nsec = 0;
            self.schedule_ref = tm.to_timespec();

            // fill schedule for 48 hours:
            self.update_schedule();
            self.update_schedule();
        }

        if (self.schedule_ref - timestamp) <= Duration::days(1) {
            // make sure that at least 1 day of future updates is known
            self.update_schedule();
        }

        self.schedule.kick_event(timestamp);
    }

    // XXX remove in time
    fn fast_forward(&mut self) {
        let mut now = now_utc().to_timespec();

        loop {
            match self.schedule.kick_event(now) {
                Some(next) => {
                    now = next;
                }
                None => break
            }
        }
    }

}

pub struct Tracker {
    zoneinfo: ZoneInfo,
    path: path::PathBuf,
    inner: TrackerInner,
}

impl Tracker {
    pub fn new(configfile: path::PathBuf) -> Tracker {
        let zoneinfo = ZoneInfo::get_local_zoneinfo().ok().expect("BUG: not able to load local zoneinfo");

        let tracker = Tracker {
            inner: TrackerInner::new(&configfile, &zoneinfo),
            path: configfile,
            zoneinfo: zoneinfo,
        };

        tracker
    }

    pub fn process_tick(&mut self, timestamp: Timespec) {
        self.inner.process_tick(timestamp);
    }

    // XXX remove in time
    pub fn update_schedule(&mut self) {
        self.inner.update_schedule();
    }

    // XXX remove in time
    pub fn fast_forward(&mut self) {
        self.inner.fast_forward();
    }
}

<commit_msg>Tracker: add 'hot' state for switch<commit_after>use dailyschedule::{Handler, Schedule};
use std::cell::{Cell, RefCell};
use std::collections::BTreeMap;
use std::path;
use std::rc::Rc;
use super::config;
use super::serial;
use time::{Duration, Timespec, at_utc, at, now_utc};
use zoneinfo::ZoneInfo;

#[derive(Eq, PartialEq, Debug, Copy, Clone)]
enum Context {
    Off,
    On
}

struct Switch {
    serial: serial::SerialClient,
    alias: String,
    last_on: Cell<Timespec>,
    state: Cell<Context>,
    valid_events: RefCell<BTreeMap<Timespec, Context>>,
    /// when "hot" perform actual relay operations
    hot: Cell<bool>,
}

impl Switch {
    fn new(alias: String, serial: serial::SerialClient) -> Switch {
        Switch {
            alias: alias,
            serial: serial,
            last_on: Cell::new(Timespec::new(0, 0)),
            state: Cell::new(Context::Off),
            valid_events: RefCell::new(BTreeMap::new()),
            hot: Cell::new(false),
        }
    }
}

impl Switch {
    fn set_switch_state(&self, state: Context) {
        self.state.set(state);
        self.dispatch_context();
    }

    fn dispatch_context(&self) {
        if self.hot.get() {
            match self.state.get() {
                Context::Off => self.serial.switch_off(&self.alias[..]),
                Context::On => self.serial.switch_on(&self.alias[..]),
            }
        }
    }

    fn make_hot(&self) {
        self.hot.set(true);
        self.dispatch_context();
    }
}

impl Handler<Context> for Switch {
    /// Hint the event-handler for future events; this function will
    /// add only valid events (where on event lies before the off event) to
    /// the valid events list.
    fn hint(&self, ts: &Timespec, context: &Context) {
        match context {
            &Context::On => self.last_on.set(*ts),
            &Context::Off => {
                if *ts > self.last_on.get() {
                    let mut events = self.valid_events.borrow_mut();
                    events.insert(self.last_on.get(), Context::On);
                    events.insert(*ts, Context::Off);
                }
            }
        }
    }

    /// Perform action only when the timestamp is considered valid;
    /// Remove the current or prior timestamps from the expected timestamps.
    fn kick(&self, ts: &Timespec, context: &Context) {
        if self.valid_events.borrow().contains_key(ts) {
            println!("XXX: {} {:?} {}", at(*ts).asctime(), context, self.alias); // XXX
            self.set_switch_state(*context);

            let mut events = self.valid_events.borrow_mut();

            // don't like the clone here, but keeps events mutable inside the loop
            for (e, _) in events.clone().iter().take_while(|&(&e, _)| e <= *ts) {
                events.remove(e);
            }
        }
    }
}

struct TrackerInner {
    serial: serial::SerialClient,
    config: config::Config,
    schedule: Schedule<Context, Switch>,
    schedule_ref: Timespec,
    initial: bool,
    switches: BTreeMap<String, Rc<Switch>>,
}

impl TrackerInner {
    fn load_schedule(&mut self) {
        self.switches.clear();
        for circle in self.config.circles.iter() {
            let switch = Rc::new(Switch::new(circle.alias.clone(), self.serial.clone()));
            match circle.default {
                config::CircleSetting::On => switch.set_switch_state(Context::On),
                config::CircleSetting::Off => switch.set_switch_state(Context::Off),
                config::CircleSetting::Schedule => {
                    for toggle in circle.toggles.iter() {
                        let start = toggle.start.into_dailyevent(&self.config.device);
                        let end = toggle.end.into_dailyevent(&self.config.device);

                        self.schedule.add_event(start, switch.clone(), Context::On);
                        self.schedule.add_event(end, switch.clone(), Context::Off);
                    }
                }
            }
            self.switches.insert(circle.alias.clone(), switch);
        }
    }

    fn new(configfile: &path::PathBuf, zoneinfo: &ZoneInfo) -> TrackerInner {
        let config = config::Config::new(configfile).unwrap(); // XXX
        let schedule = Schedule::new(zoneinfo.clone());
        let serial = serial::Serial::spawn();

        // XXX: register circles / connect etc...

        let mut tracker = TrackerInner {
            config: config,
            schedule: schedule,
            serial: serial,
            schedule_ref: Timespec::new(0,0),
            initial: true,
            switches: BTreeMap::new(),
        };

        tracker.load_schedule();

        tracker
    }

    fn update_schedule(&mut self) {
        self.schedule.update_schedule(self.schedule_ref);
        self.schedule_ref = self.schedule_ref + Duration::days(1);
    }

    fn process_tick(&mut self, timestamp: Timespec) {
        if self.initial {
            let mut tm = at_utc(timestamp);
            tm.tm_hour = 0;
            tm.tm_min = 0;
            tm.tm_sec = 0;
            tm.tm_nsec = 0;
            self.schedule_ref = tm.to_timespec();

            // fill schedule for 48 hours:
            self.update_schedule();
            self.update_schedule();
        }

        if (self.schedule_ref - timestamp) <= Duration::days(1) {
            // make sure that at least 1 day of future updates is known
            self.update_schedule();
        }

        self.schedule.kick_event(timestamp);

        if self.initial {
            self.initial = false;
            // configure the switch to actually set the relay (otherwise the initial kicks will
            // quickly toggle switches unintendedly
            for (_, ref mut switch) in self.switches.iter_mut() {
                switch.make_hot();
            }
        }
    }

    // XXX remove in time
    fn fast_forward(&mut self) {
        let mut now = now_utc().to_timespec();

        loop {
            match self.schedule.kick_event(now) {
                Some(next) => {
                    now = next;
                }
                None => break
            }
        }
    }

}

pub struct Tracker {
    zoneinfo: ZoneInfo,
    path: path::PathBuf,
    inner: TrackerInner,
}

impl Tracker {
    pub fn new(configfile: path::PathBuf) -> Tracker {
        let zoneinfo = ZoneInfo::get_local_zoneinfo().ok().expect("BUG: not able to load local zoneinfo");

        let tracker = Tracker {
            inner: TrackerInner::new(&configfile, &zoneinfo),
            path: configfile,
            zoneinfo: zoneinfo,
        };

        tracker
    }

    pub fn process_tick(&mut self, timestamp: Timespec) {
        self.inner.process_tick(timestamp);
    }

    // XXX remove in time
    pub fn update_schedule(&mut self) {
        self.inner.update_schedule();
    }

    // XXX remove in time
    pub fn fast_forward(&mut self) {
        self.inner.fast_forward();
    }
}

<|endoftext|>


<commit_before>use conrod::{Button,Position,Positionable, Text, Sizeable,Widget,WidgetId, UserInput,Canvas,Colorable,color,Labelable};
use piston_window::Glyphs;
use ::{Backend,Graph,GraphNode,GraphEdge,NodeBase,EdgeGuard,Nid,Eid};

pub type Ui = ::conrod::Ui<Glyphs>;

pub trait UiNode: GraphNode {
    fn get_ui(&self) -> &UiBase;
    fn get_ui_mut(&mut self) -> &mut UiBase;
    fn build_ui(&mut self, ui: &mut Ui) -> bool {

        if self.get_ui().destroy { return false }

        let mut canvas_height = self.get_ui().height;
        let mut cl = "-"; //canvas label
        if self.get_ui().collapse {
            cl = "+";
            canvas_height = 20.0;
        }

        let ui_id_start: WidgetId = self.get_ui().get_id();

        let position = self.get_position().clone();

        let mut color = color::WHITE;
        if self.get_ui().select { color=color::RED; }

        //floating canvas where our node data resides
        Canvas::new()
            .floating(true)
            .title_bar(self.get_name())
            .xy([position[0], position[1]])
            .h(canvas_height)
            .w(self.get_ui().width)
            .color(color::BLUE)
            .label_color(color)
            .set(ui_id_start, ui);

        //build buttons in reverse order, to stay within canvas
        //start at top right, and head left from
        //close button
        Button::new()
            .top_right_of(ui_id_start) //todo: shift up ~5.0
            .label("x")
            .w_h(20.0,20.0)
            .react(|| self.get_ui_mut().destroy = true)
            .set(ui_id_start + 1, ui);

        //collapse
        Button::new()
            .left(5.0)
            .label(cl)
            .w_h(20.0,20.0)
            .react(|| self.get_ui_mut().toggle_collapse())
            .set(ui_id_start + 2, ui);

        //connector
        Button::new()
            .top_left_of(ui_id_start) //todo: shift up ~5.0
            .label("o")
            .w_h(20.0,20.0)
            .react(|| self.get_ui_mut().toggle_select())
            .set(ui_id_start + 3, ui);

        if !self.get_ui().collapse {
            Text::new("Node Data")
                .middle_of(ui_id_start)
                .set(ui_id_start + 4, ui);
        }

        self.get_ui().select
    }
}

#[derive(Debug,Clone,PartialEq)]
pub struct UiBase {
    ui_id: WidgetId,
    select: bool,
    collapse: bool,
    destroy: bool,
    width: f64,
    height: f64,
}

impl UiBase {
    pub fn default() -> UiBase {
        UiBase { ui_id: WidgetId(10000),
                 select: false,
                 collapse: false,
                 destroy: false,
                 width: 150.0,
                 height: 100.0,
        }
    }
    pub fn toggle_destroy(&mut self) { self.destroy = !self.destroy; }
    pub fn toggle_collapse(&mut self) { self.collapse = !self.collapse; }
    pub fn toggle_select(&mut self) { self.select = !self.select; }

    pub fn get_id(&self) -> WidgetId { self.ui_id }
    pub fn set_id(&mut self, id: WidgetId) { self.ui_id = id; }
}

pub trait UiGraph {
    fn render(&mut self, ui: &mut Ui);
}

impl<E:GraphEdge,N:UiNode> UiGraph for Graph<E,N> {
    fn render(&mut self, ui: &mut Ui) {
        let mut select: (Option<Nid>,Option<Nid>) = (None,None);
        let mut edges: Vec<(Nid,Vec<Nid>)> = vec!();

        self.with_nodes_mut(|n| {
            let is_select: bool = n.build_ui(ui);
            if is_select { //selected?
                if select.0.is_some() {
                    if !select.1.is_some() {
                        select.1 = Some(n.get_base().get_id());
                    }
                }
                else { select.0 = Some(n.get_base().get_id()); }
            }

            edges.push((n.get_base().get_id(),n.get_base().get_edges()));
        });

        // build edges
        for (nid,ev) in edges {
            let n = self.get_node(&nid).unwrap();
            for en in ev.iter() {
                let p = n.get_position();
                let id = n.get_ui().get_id();
                if let Some(n2) = self.get_node(&en) {
                    let p2 = n2.get_position();
                    // FIXME: uiedge widget is being removed, needs replacement here
                    //UiEdge::new("edge",
                    //            Position::Absolute(p[0],p[1]),
                    //            Position::Absolute(p2[0],p2[1]))
                     //   .set(id +100, ui);
                }
            }
        }

        // build new edge
        match select {
            (Some(first),Some(second)) => {
                //clear node selection
                self.get_node_mut(&first).unwrap().get_ui_mut().select = false;
                self.get_node_mut(&second).unwrap().get_ui_mut().select = false;

                //todo: prompt for edge data!
                self.direct(&first,&second,E::default());
            },
            _ => (),
        }
    }
}
<commit_msg>draw edges, and dynamic render<commit_after>use conrod::{Button,Position,Positionable, Text, Sizeable,Widget,WidgetId, UserInput,Canvas,Colorable,color,Labelable,Line};
use piston_window::Glyphs;
use ::{Backend,Graph,GraphNode,GraphEdge,NodeBase,EdgeGuard,Nid,Eid};

pub type Ui = ::conrod::Ui<Glyphs>;

pub trait UiNode: GraphNode {
    fn get_ui(&self) -> &UiBase;
    fn get_ui_mut(&mut self) -> &mut UiBase;
    fn build_ui(&mut self, ui: &mut Ui) -> bool {

        if self.get_ui().destroy { return false }

        let mut canvas_height = self.get_ui().height;
        let mut cl = "-"; //canvas label
        if self.get_ui().collapse {
            cl = "+";
            canvas_height = 20.0;
        }

        let ui_id_start: WidgetId = self.get_ui().get_id();

        let position = self.get_position().clone();

        let mut color = color::WHITE;
        if self.get_ui().select { color=color::RED; }

        //floating canvas where our node data resides
        Canvas::new()
            .floating(true)
            .title_bar(self.get_name())
            .xy([position[0], position[1]])
            .h(canvas_height)
            .w(self.get_ui().width)
            .color(color::BLUE)
            .label_color(color)
            .set(ui_id_start, ui);

        //build buttons in reverse order, to stay within canvas
        //start at top right, and head left from
        //close button
        Button::new()
            .top_right_of(ui_id_start) //todo: shift up ~5.0
            .label("x")
            .w_h(20.0,20.0)
            .react(|| self.get_ui_mut().destroy = true)
            .set(ui_id_start + 1, ui);

        //collapse
        Button::new()
            .left(5.0)
            .label(cl)
            .w_h(20.0,20.0)
            .react(|| self.get_ui_mut().toggle_collapse())
            .set(ui_id_start + 2, ui);

        //connector
        Button::new()
            .top_left_of(ui_id_start) //todo: shift up ~5.0
            .label("o")
            .w_h(20.0,20.0)
            .react(|| self.get_ui_mut().toggle_select())
            .set(ui_id_start + 3, ui);

        if !self.get_ui().collapse {
            Text::new("Node Data") // TODO: derive actual node data, and represent somehow
                .middle_of(ui_id_start)
                .set(ui_id_start + 4, ui);
        }

        self.get_ui().select
    }
}

#[derive(Debug,Clone,PartialEq)]
pub struct UiBase {
    ui_id: WidgetId,
    select: bool,
    collapse: bool,
    destroy: bool,
    width: f64,
    height: f64,
}

impl UiBase {
    pub fn default() -> UiBase {
        UiBase { ui_id: WidgetId(10000),
                 select: false,
                 collapse: false,
                 destroy: false,
                 width: 150.0,
                 height: 100.0,
        }
    }
    pub fn toggle_destroy(&mut self) { self.destroy = !self.destroy; }
    pub fn toggle_collapse(&mut self) { self.collapse = !self.collapse; }
    pub fn toggle_select(&mut self) { self.select = !self.select; }

    pub fn get_id(&self) -> WidgetId { self.ui_id }
    pub fn set_id(&mut self, id: WidgetId) { self.ui_id = id; }
}

pub trait UiGraph {
    fn render(&mut self, ui: &mut Ui);
}

impl<E:GraphEdge,N:UiNode> UiGraph for Graph<E,N> {
    fn render(&mut self, ui: &mut Ui) {
        let mut select: (Option<Nid>,Option<Nid>) = (None,None);
        let mut edges: Vec<(Nid,Vec<Nid>)> = vec!();

        self.with_nodes_mut(|n| {
            let is_select: bool = n.build_ui(ui);

            {
                let base = n.get_base();
                if is_select { //selected?
                    if select.0.is_some() {
                        if !select.1.is_some() {
                            select.1 = Some(base.get_id());
                        }
                    }
                    else { select.0 = Some(base.get_id()); }
                }

                edges.push((base.get_id(),base.get_edges()));
            }

            if let Some(coord) = ui.xy_of(n.get_ui().get_id()) {
                n.set_position(coord);
            }
        });

        // build edges
        for (nid,ev) in edges {
            let n = self.get_node(&nid).unwrap();
            for en in ev.iter() {
                let id = n.get_ui().get_id();
                if let Some(n2) = self.get_node(&en) {
                    Line::abs(*n.get_position(), *n2.get_position())
                        .set(id+1000, ui);
                }
            }
        }

        // build new edge
        match select {
            (Some(first),Some(second)) => {
                //clear node selection
                self.get_node_mut(&first).unwrap().get_ui_mut().select = false;
                self.get_node_mut(&second).unwrap().get_ui_mut().select = false;

                //todo: prompt for edge data!
                self.direct(&first,&second,E::default());
            },
            _ => (),
        }
    }
}
<|endoftext|>


<commit_before>import platform::osmain;
import geometry::*;
import comm::*;
import image::base::Image;
import dl = layout::display_list;
import azure::*;
import azure::bindgen::*;
import libc::size_t;
import text::font::Font;
import text::text_run::TextRun;
import dom::event::{Event, ResizeEvent};
import geom::size::Size2D;
import geom::rect::Rect;
import geom::point::Point2D;
import azure_hl::{AsAzureRect, B8G8R8A8, Color, ColorPattern, DrawOptions, DrawSurfaceOptions};
import azure_hl::{DrawTarget, Linear};
import ptr::addr_of;
import std::arc::arc;
import azure::cairo::{cairo_font_face_t, cairo_scaled_font_t};

import pipes::{port, chan};

type Renderer = comm::Chan<Msg>;

enum Msg {
    RenderMsg(dl::display_list),
    ExitMsg(pipes::chan<()>)
}

#[doc = "
The interface used to by the renderer to aquire draw targets for
each rendered frame and submit them to be drawn to the display

FIXME: Change this name to Compositor.
"]
trait Sink {
    fn begin_drawing(+next_dt: pipes::chan<AzDrawTargetRef>);
    fn draw(+next_dt: pipes::chan<AzDrawTargetRef>, draw_me: AzDrawTargetRef);
    fn add_event_listener(listener: comm::Chan<Event>);
}

fn Renderer<S: Sink send copy>(sink: S) -> comm::Chan<Msg> {
    do task::spawn_listener |po: comm::Port<Msg>| {
        let (draw_target_ch, draw_target_po) = pipes::stream();
        let mut draw_target_ch = draw_target_ch;
        let mut draw_target_po = draw_target_po;

        debug!("renderer: beginning rendering loop");

        sink.begin_drawing(draw_target_ch);

        loop {
            match po.recv() {
              RenderMsg(display_list) => {
                #debug("renderer: got render request");
                let draw_target = draw_target_po.recv();
                let (ch, po) = pipes::stream();
                let mut draw_target_ch_ = some(ch);
                draw_target_po = po;
                #debug("renderer: rendering");
                do util::time::time(~"rendering") {
                    let mut draw_target_ch = none;
                    draw_target_ch_ <-> draw_target_ch;
                    let draw_target_ch = option::unwrap(draw_target_ch);

                    clear(draw_target);
                    draw_display_list(draw_target, display_list);

                    #debug("renderer: returning surface");
                    sink.draw(draw_target_ch, draw_target);
                }
              }
              ExitMsg(response_ch) => {
                response_ch.send(());
                break;
              }
            }
        }
    }
}

trait to_float {
    fn to_float() -> float;
}

impl u8 : to_float {
    fn to_float() -> float {
        (self as float) / 255f
    }
}

fn draw_display_list(azure_draw_target: AzDrawTargetRef, display_list: dl::display_list) {
    let draw_target = azure_hl::new_draw_target_from_azure_draw_target(azure_draw_target);
    for display_list.each |item| {
        #debug["drawing %?", item];

        match item.item_type {
          dl::display_item_solid_color(r, g, b) => draw_solid_color(&draw_target, item, r, g, b),
          dl::display_item_image(image) => draw_image(&draw_target, item, *image),
          dl::display_item_text(text_run) => draw_text(&draw_target, item, text_run),
          dl::padding(*) => fail ~"should never see padding"
        }
    }
}

trait ToAzureRect {
    fn to_azure_rect() -> Rect<AzFloat>;
}

impl Rect<au> : ToAzureRect {
    fn to_azure_rect() -> Rect<AzFloat> {
        Rect(Point2D(au_to_px(self.origin.x) as AzFloat, au_to_px(self.origin.y) as AzFloat),
             Size2D(au_to_px(self.size.width) as AzFloat, au_to_px(self.size.height) as AzFloat))
    }
}

fn draw_solid_color(draw_target: &DrawTarget, item: dl::display_item, r: u8, g: u8, b: u8) {
    let color = Color(r.to_float() as AzFloat,
                      g.to_float() as AzFloat,
                      b.to_float() as AzFloat,
                      1f as AzFloat);

    draw_target.fill_rect(item.bounds.to_azure_rect(), ColorPattern(color));
}

fn draw_image(draw_target: &DrawTarget, item: dl::display_item, image: arc<~Image>) unsafe {
    let image = std::arc::get(&image);
    let size = Size2D(image.width as i32, image.height as i32);
    let stride = image.width * 4;

    // Do color space conversion :(
    let data = do vec::from_fn(image.width * image.height * 4) |i| {
        let color = i % 4;
        let pixel = i / 4;
        match color {
            0 => image.data[pixel * 3 + 2],
            1 => image.data[pixel * 3 + 1],
            2 => image.data[pixel * 3 + 0],
            3 => 0xffu8,
            _ => fail
        }
    };

    let azure_surface = draw_target.create_source_surface_from_data(data, size, stride as i32,
                                                                    B8G8R8A8);
    let source_rect = Rect(Point2D(0 as AzFloat, 0 as AzFloat),
                           Size2D(image.width as AzFloat, image.height as AzFloat));
    let dest_rect = item.bounds.to_azure_rect();
    let draw_surface_options = DrawSurfaceOptions(Linear, true);
    let draw_options = DrawOptions(1.0f as AzFloat, 0);
    draw_target.draw_surface(azure_surface, dest_rect, source_rect, draw_surface_options,
                             draw_options);
}

fn draw_text(draw_target: &DrawTarget, item: dl::display_item, text_run: TextRun) {
    import ptr::{addr_of, null};
    import vec::unsafe::to_ptr;
    import libc::types::common::c99::{uint16_t, uint32_t};
    import geom::point::Point2D;
    import text::font_library::FontLibrary;
    import text::font::Font;
    import azure::{AzNativeFont, AzFloat, AZ_NATIVE_FONT_CAIRO_FONT_FACE};
    import azure::bindgen::{AzCreateScaledFontWithCairo,
                            AzReleaseScaledFont,
                            AzCreateColorPattern,
                            AzReleaseColorPattern};
    import azure::cairo::bindgen::cairo_scaled_font_destroy;

    let draw_target = draw_target.azure_draw_target;

    let bounds = copy (*item).bounds;
    // FIXME: The font library should not be created here
    let flib = FontLibrary();
    let font = flib.get_test_font();

    let nfont: AzNativeFont = {
        mType: AZ_NATIVE_FONT_CAIRO_FONT_FACE,
        mFont: null()
    };

    let cfont = get_cairo_font(font);
    let azfont = AzCreateScaledFontWithCairo(addr_of(nfont), 1f as AzFloat, cfont);
    assert azfont.is_not_null();
    cairo_scaled_font_destroy(cfont);

    let color = {
        r: 0f as AzFloat,
        g: 0f as AzFloat,
        b: 0f as AzFloat,
        a: 1f as AzFloat
    };
    let pattern = AzCreateColorPattern(addr_of(color));
    assert pattern.is_not_null();

    let options: AzDrawOptions = {
        mAlpha: 1f as AzFloat,
        fields: 0 as uint16_t
    };

    let mut origin = Point2D(bounds.origin.x, bounds.origin.y.add(bounds.size.height));
    let azglyphs = text_run.glyphs.map(|glyph| {
        let azglyph: AzGlyph = {
            mIndex: glyph.index as uint32_t,
            mPosition: {
                x: au_to_px(origin.x.add(glyph.pos.offset.x)) as AzFloat,
                y: au_to_px(origin.y.add(glyph.pos.offset.y)) as AzFloat
            }
        };
        origin = Point2D(origin.x.add(glyph.pos.advance.x),
                         origin.y.add(glyph.pos.advance.y));
        azglyph
    });

    let glyphbuf: AzGlyphBuffer = unsafe {{
        mGlyphs: to_ptr(azglyphs),
        mNumGlyphs: azglyphs.len() as uint32_t
    }};

    AzDrawTargetFillGlyphs(draw_target, azfont, addr_of(glyphbuf),
                           pattern, addr_of(options), null());

    AzReleaseColorPattern(pattern);
    AzReleaseScaledFont(azfont);
}

#[cfg(target_os = "linux")]
fn get_cairo_face(font: &Font) -> *cairo_font_face_t {

    import libc::c_int;
    import azure::cairo_ft::bindgen::{cairo_ft_font_face_create_for_ft_face};

    let ftface = font.native_font.face;
    let cface = cairo_ft_font_face_create_for_ft_face(ftface, 0 as c_int);
    // FIXME: error handling
    return cface;
}

#[cfg(target_os = "macos")]
fn get_cairo_face(font: &Font) -> *cairo_font_face_t {
    import azure::cairo_quartz::bindgen::cairo_quartz_font_face_create_for_cgfont;

    let cgfont = font.native_font.cgfont;
    let face = cairo_quartz_font_face_create_for_cgfont(cgfont);
    // FIXME: error handling
    return face;
}

fn get_cairo_font(font: &Font) -> *cairo_scaled_font_t {

    import libc::c_double;
    import azure::cairo;
    import cairo::cairo_matrix_t;
    import cairo::bindgen::{cairo_matrix_init_identity,
                            cairo_matrix_scale,
                            cairo_font_options_create,
                            cairo_scaled_font_create,
                            cairo_font_options_destroy,
                            cairo_font_face_destroy};

    // FIXME: error handling

    let face = get_cairo_face(font);

    let idmatrix: cairo_matrix_t = {
        xx: 0 as c_double,
        yx: 0 as c_double,
        xy: 0 as c_double,
        yy: 0 as c_double,
        x0: 0 as c_double,
        y0: 0 as c_double
    };
    cairo_matrix_init_identity(addr_of(idmatrix));

    let fontmatrix = idmatrix;
    cairo_matrix_scale(addr_of(fontmatrix),
                       20f as c_double, 20f as c_double);
    let options = cairo_font_options_create();
    let cfont = cairo_scaled_font_create(face, addr_of(fontmatrix),
                                         addr_of(idmatrix), options);
    cairo_font_options_destroy(options);
    cairo_font_face_destroy(face);

    return cfont;
}

fn clear(draw_target: AzDrawTargetRef) {

    let black_color = {
        r: 0f as AzFloat,
        g: 0f as AzFloat,
        b: 0f as AzFloat,
        a: 1f as AzFloat
    };
    let black_pattern = AzCreateColorPattern(ptr::addr_of(black_color));

    let black_rect = {
        x: 0 as AzFloat,
        y: 0 as AzFloat,
        width: 800 as AzFloat,
        height: 600 as AzFloat,
    };

    AzDrawTargetFillRect(
        draw_target,
        ptr::addr_of(black_rect),
        unsafe { unsafe::reinterpret_cast(black_pattern) }
    );

    AzReleaseColorPattern(black_pattern);
}
<commit_msg>Use the high-level Azure API in the clear function<commit_after>import platform::osmain;
import geometry::*;
import comm::*;
import image::base::Image;
import dl = layout::display_list;
import azure::*;
import azure::bindgen::*;
import libc::size_t;
import text::font::Font;
import text::text_run::TextRun;
import dom::event::{Event, ResizeEvent};
import geom::size::Size2D;
import geom::rect::Rect;
import geom::point::Point2D;
import azure_hl::{AsAzureRect, B8G8R8A8, Color, ColorPattern, DrawOptions, DrawSurfaceOptions};
import azure_hl::{DrawTarget, Linear};
import ptr::addr_of;
import std::arc::arc;
import azure::cairo::{cairo_font_face_t, cairo_scaled_font_t};

import pipes::{port, chan};

type Renderer = comm::Chan<Msg>;

enum Msg {
    RenderMsg(dl::display_list),
    ExitMsg(pipes::chan<()>)
}

#[doc = "
The interface used to by the renderer to aquire draw targets for
each rendered frame and submit them to be drawn to the display

FIXME: Change this name to Compositor.
"]
trait Sink {
    fn begin_drawing(+next_dt: pipes::chan<AzDrawTargetRef>);
    fn draw(+next_dt: pipes::chan<AzDrawTargetRef>, draw_me: AzDrawTargetRef);
    fn add_event_listener(listener: comm::Chan<Event>);
}

fn Renderer<S: Sink send copy>(sink: S) -> comm::Chan<Msg> {
    do task::spawn_listener |po: comm::Port<Msg>| {
        let (draw_target_ch, draw_target_po) = pipes::stream();
        let mut draw_target_ch = draw_target_ch;
        let mut draw_target_po = draw_target_po;

        debug!("renderer: beginning rendering loop");

        sink.begin_drawing(draw_target_ch);

        loop {
            match po.recv() {
              RenderMsg(display_list) => {
                #debug("renderer: got render request");
                let azure_draw_target = draw_target_po.recv();
                let (ch, po) = pipes::stream();
                let mut draw_target_ch_ = some(ch);
                draw_target_po = po;
                #debug("renderer: rendering");
                do util::time::time(~"rendering") {
                    let mut draw_target_ch = none;
                    draw_target_ch_ <-> draw_target_ch;
                    let draw_target_ch = option::unwrap(draw_target_ch);

                    let draw_target =
                        azure_hl::new_draw_target_from_azure_draw_target(azure_draw_target);
                    clear(&draw_target);
                    draw_display_list(&draw_target, display_list);

                    #debug("renderer: returning surface");
                    sink.draw(draw_target_ch, azure_draw_target);
                }
              }
              ExitMsg(response_ch) => {
                response_ch.send(());
                break;
              }
            }
        }
    }
}

trait to_float {
    fn to_float() -> float;
}

impl u8 : to_float {
    fn to_float() -> float {
        (self as float) / 255f
    }
}

fn draw_display_list(draw_target: &DrawTarget, display_list: dl::display_list) {
    for display_list.each |item| {
        #debug["drawing %?", item];

        match item.item_type {
          dl::display_item_solid_color(r, g, b) => draw_solid_color(draw_target, item, r, g, b),
          dl::display_item_image(image) => draw_image(draw_target, item, *image),
          dl::display_item_text(text_run) => draw_text(draw_target, item, text_run),
          dl::padding(*) => fail ~"should never see padding"
        }
    }
}

trait ToAzureRect {
    fn to_azure_rect() -> Rect<AzFloat>;
}

impl Rect<au> : ToAzureRect {
    fn to_azure_rect() -> Rect<AzFloat> {
        Rect(Point2D(au_to_px(self.origin.x) as AzFloat, au_to_px(self.origin.y) as AzFloat),
             Size2D(au_to_px(self.size.width) as AzFloat, au_to_px(self.size.height) as AzFloat))
    }
}

fn draw_solid_color(draw_target: &DrawTarget, item: dl::display_item, r: u8, g: u8, b: u8) {
    let color = Color(r.to_float() as AzFloat,
                      g.to_float() as AzFloat,
                      b.to_float() as AzFloat,
                      1f as AzFloat);

    draw_target.fill_rect(item.bounds.to_azure_rect(), ColorPattern(color));
}

fn draw_image(draw_target: &DrawTarget, item: dl::display_item, image: arc<~Image>) unsafe {
    let image = std::arc::get(&image);
    let size = Size2D(image.width as i32, image.height as i32);
    let stride = image.width * 4;

    // Do color space conversion :(
    let data = do vec::from_fn(image.width * image.height * 4) |i| {
        let color = i % 4;
        let pixel = i / 4;
        match color {
            0 => image.data[pixel * 3 + 2],
            1 => image.data[pixel * 3 + 1],
            2 => image.data[pixel * 3 + 0],
            3 => 0xffu8,
            _ => fail
        }
    };

    let azure_surface = draw_target.create_source_surface_from_data(data, size, stride as i32,
                                                                    B8G8R8A8);
    let source_rect = Rect(Point2D(0 as AzFloat, 0 as AzFloat),
                           Size2D(image.width as AzFloat, image.height as AzFloat));
    let dest_rect = item.bounds.to_azure_rect();
    let draw_surface_options = DrawSurfaceOptions(Linear, true);
    let draw_options = DrawOptions(1.0f as AzFloat, 0);
    draw_target.draw_surface(azure_surface, dest_rect, source_rect, draw_surface_options,
                             draw_options);
}

fn draw_text(draw_target: &DrawTarget, item: dl::display_item, text_run: TextRun) {
    import ptr::{addr_of, null};
    import vec::unsafe::to_ptr;
    import libc::types::common::c99::{uint16_t, uint32_t};
    import geom::point::Point2D;
    import text::font_library::FontLibrary;
    import text::font::Font;
    import azure::{AzNativeFont, AzFloat, AZ_NATIVE_FONT_CAIRO_FONT_FACE};
    import azure::bindgen::{AzCreateScaledFontWithCairo,
                            AzReleaseScaledFont,
                            AzCreateColorPattern,
                            AzReleaseColorPattern};
    import azure::cairo::bindgen::cairo_scaled_font_destroy;

    let draw_target = draw_target.azure_draw_target;

    let bounds = copy (*item).bounds;
    // FIXME: The font library should not be created here
    let flib = FontLibrary();
    let font = flib.get_test_font();

    let nfont: AzNativeFont = {
        mType: AZ_NATIVE_FONT_CAIRO_FONT_FACE,
        mFont: null()
    };

    let cfont = get_cairo_font(font);
    let azfont = AzCreateScaledFontWithCairo(addr_of(nfont), 1f as AzFloat, cfont);
    assert azfont.is_not_null();
    cairo_scaled_font_destroy(cfont);

    let color = {
        r: 0f as AzFloat,
        g: 0f as AzFloat,
        b: 0f as AzFloat,
        a: 1f as AzFloat
    };
    let pattern = AzCreateColorPattern(addr_of(color));
    assert pattern.is_not_null();

    let options: AzDrawOptions = {
        mAlpha: 1f as AzFloat,
        fields: 0 as uint16_t
    };

    let mut origin = Point2D(bounds.origin.x, bounds.origin.y.add(bounds.size.height));
    let azglyphs = text_run.glyphs.map(|glyph| {
        let azglyph: AzGlyph = {
            mIndex: glyph.index as uint32_t,
            mPosition: {
                x: au_to_px(origin.x.add(glyph.pos.offset.x)) as AzFloat,
                y: au_to_px(origin.y.add(glyph.pos.offset.y)) as AzFloat
            }
        };
        origin = Point2D(origin.x.add(glyph.pos.advance.x),
                         origin.y.add(glyph.pos.advance.y));
        azglyph
    });

    let glyphbuf: AzGlyphBuffer = unsafe {{
        mGlyphs: to_ptr(azglyphs),
        mNumGlyphs: azglyphs.len() as uint32_t
    }};

    AzDrawTargetFillGlyphs(draw_target, azfont, addr_of(glyphbuf),
                           pattern, addr_of(options), null());

    AzReleaseColorPattern(pattern);
    AzReleaseScaledFont(azfont);
}

#[cfg(target_os = "linux")]
fn get_cairo_face(font: &Font) -> *cairo_font_face_t {

    import libc::c_int;
    import azure::cairo_ft::bindgen::{cairo_ft_font_face_create_for_ft_face};

    let ftface = font.native_font.face;
    let cface = cairo_ft_font_face_create_for_ft_face(ftface, 0 as c_int);
    // FIXME: error handling
    return cface;
}

#[cfg(target_os = "macos")]
fn get_cairo_face(font: &Font) -> *cairo_font_face_t {
    import azure::cairo_quartz::bindgen::cairo_quartz_font_face_create_for_cgfont;

    let cgfont = font.native_font.cgfont;
    let face = cairo_quartz_font_face_create_for_cgfont(cgfont);
    // FIXME: error handling
    return face;
}

fn get_cairo_font(font: &Font) -> *cairo_scaled_font_t {

    import libc::c_double;
    import azure::cairo;
    import cairo::cairo_matrix_t;
    import cairo::bindgen::{cairo_matrix_init_identity,
                            cairo_matrix_scale,
                            cairo_font_options_create,
                            cairo_scaled_font_create,
                            cairo_font_options_destroy,
                            cairo_font_face_destroy};

    // FIXME: error handling

    let face = get_cairo_face(font);

    let idmatrix: cairo_matrix_t = {
        xx: 0 as c_double,
        yx: 0 as c_double,
        xy: 0 as c_double,
        yy: 0 as c_double,
        x0: 0 as c_double,
        y0: 0 as c_double
    };
    cairo_matrix_init_identity(addr_of(idmatrix));

    let fontmatrix = idmatrix;
    cairo_matrix_scale(addr_of(fontmatrix),
                       20f as c_double, 20f as c_double);
    let options = cairo_font_options_create();
    let cfont = cairo_scaled_font_create(face, addr_of(fontmatrix),
                                         addr_of(idmatrix), options);
    cairo_font_options_destroy(options);
    cairo_font_face_destroy(face);

    return cfont;
}

fn clear(draw_target: &DrawTarget) {
    let pattern = ColorPattern(Color(1f as AzFloat, 1f as AzFloat, 1f as AzFloat, 1f as AzFloat));
    let rect = Rect(Point2D(0 as AzFloat, 0 as AzFloat), Size2D(800 as AzFloat, 600 as AzFloat));
    draw_target.fill_rect(rect, pattern);
}
<|endoftext|>


<commit_before>/*
 * Cymbalum, Molecular Simulation in Rust
 * Copyright (C) 2015 Guillaume Fraux
 *
 * 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/
*/

//! Saving properties of the simulation to some stream (file or stdout mainly)

extern crate chemharp;

use std::io::prelude::*;
use std::io;
use std::fs::File;
use std::path::Path;

use ::units;
use ::universe::Universe;

use ::universe::chemharp::universe_to_frame;

/// The `Output` trait define the interface for all the quantities outputed by
/// the simulation during the run. An Output can be a text or a binary data
/// file, an image, a text log, …
pub trait Output {
    /// Function called once at the beggining of the simulation, which allow
    /// for some setup of the output if needed.
    fn setup(&mut self, _: &Universe) {}

    /// Write the output from the universe.
    fn write(&mut self, universe: &Universe);

    /// Function called once at the end of the simulation.
    fn finish(&mut self, _: &Universe) {}
}

/******************************************************************************/
/// The `TrajectoryOutput` allow to write the trajectory of the system to a
/// file, using any format supported by the
/// [Chemharp](http://chemharp.readthedocs.org/en/latest/formats.html) library.
pub struct TrajectoryOutput {
    file: chemharp::Trajectory,
}

impl TrajectoryOutput {
    /// Create a new `TrajectoryOutput` writing to `filename`. The file is
    /// replaced if it already exists.
    pub fn new<'a, S>(filename: S) -> Result<TrajectoryOutput, chemharp::Error> where S: Into<&'a str> {
        Ok(TrajectoryOutput{
            file: try!(chemharp::Trajectory::create(filename.into()))
        })
    }
}

impl Output for TrajectoryOutput {
    fn write(&mut self, universe: &Universe) {
        let frame = match universe_to_frame(universe) {
            Ok(val) => val,
            Err(err) => {
                error!("Error in Chemharp runtime while converting data: {}", err.message());
                panic!();
            }
        };

        match self.file.write(&frame) {
            Ok(()) => {},
            Err(err) => {
                error!("Error in Chemharp runtime while writing file: {}", err.message());
                panic!();
            }
        }
    }
}

/******************************************************************************/
/// The `CellOutput` write all the components of a cell to a file . The columns
/// in the file contains the following values: `A B C α β γ`.
pub struct CellOutput {
    file: File,
    path: String
}

impl CellOutput {
    /// Create a new `CellOutput` writing to `filename`. The file is replaced if
    /// it already exists.
    pub fn new<P: AsRef<Path>>(filename: P) -> Result<CellOutput, io::Error> {
        let path = String::from(filename.as_ref().to_str().unwrap());
        Ok(CellOutput{
            file: try!(File::create(filename)),
            path: path,
        })
    }
}

impl Output for CellOutput {
    fn setup(&mut self, _: &Universe) {
        if let Err(e) = writeln!(&mut self.file, "# Unit cell of the simulation") {
            // Do panic in early time
            panic!("Could not write to file '{}': {:?}", self.path, e);
        }
        if let Err(e) = writeln!(&mut self.file, "# Step A/Å B/Å C/Å α/deg β/deg γ/deg") {
            panic!("Could not write to file '{}': {:?}", self.path, e);
        }
    }

    fn write(&mut self, universe: &Universe) {
        let cell = universe.cell();
        if let Err(e) = writeln!(
            &mut self.file, "{} {} {} {} {} {} {}",
            universe.step(), cell.a(), cell.b(), cell.c(), cell.alpha(), cell.beta(), cell.gamma()
        ) {
            // Do not panic during the simulation
            error!("Could not write to file '{}': {:?}", self.path, e);
        }
    }
}

/******************************************************************************/
/// The `EnergyOutput` write the energy of the system to a text file, organized
/// as: `PotentialEnergy     KineticEnergy     TotalEnergy`.
pub struct EnergyOutput {
    file: File,
}

impl EnergyOutput {
    /// Create a new `EnergyOutput` writing to `filename`. The file is replaced
    /// if it already exists.
    pub fn new<P: AsRef<Path>>(filename: P) -> Result<EnergyOutput, io::Error> {
        Ok(EnergyOutput{
            file: try!(File::create(filename))
        })
    }
}

impl Output for EnergyOutput {
    fn setup(&mut self, _: &Universe) {
        writeln!(&mut self.file, "# Energy of the simulation (kJ/mol)").unwrap();
        writeln!(&mut self.file, "# Step     Potential     Kinetic     Total").unwrap();
    }

    fn write(&mut self, universe: &Universe) {
        let potential = units::to(universe.potential_energy(), "kJ/mol").unwrap();
        let kinetic = units::to(universe.kinetic_energy(), "kJ/mol").unwrap();
        let total = units::to(universe.total_energy(), "kJ/mol").unwrap();
        writeln!(&mut self.file, "{}   {}   {}   {}", universe.step(), potential, kinetic, total).unwrap();
    }
}

#[cfg(test)]
mod tests {
    use std::io::prelude::*;
    use std::fs;

    use super::*;
    use ::universe::*;
    use ::types::*;
    use ::potentials::*;
    use ::units;

    fn testing_universe() -> Universe {
        let mut universe = Universe::from_cell(UnitCell::cubic(10.0));;

        universe.add_particle(Particle::new("F"));
        universe[0].set_position(Vector3D::new(0.0, 0.0, 0.0));

        universe.add_particle(Particle::new("F"));
        universe[1].set_position(Vector3D::new(1.3, 0.0, 0.0));

        universe.add_pair_interaction("F", "F",
            Harmonic{k: units::from(300.0, "kJ/mol/A^2").unwrap(), x0: units::from(1.2, "A").unwrap()});
        return universe;
    }

    fn check_file_content(filename: &str, content: &str) {
        let mut file = fs::File::open(filename).unwrap();
        let mut buffer = String::new();
        file.read_to_string(&mut buffer).unwrap();

        assert_eq!(buffer, content);
    }

    #[test]
    fn trajectory() {
        let filename = "testing-trajectory-output.xyz";
        let universe = testing_universe();
        {
            let mut out = TrajectoryOutput::new(filename).unwrap();
            out.setup(&universe);
            out.write(&universe);
            out.finish(&universe);
        }

        check_file_content(filename, "2\nWritten by Chemharp\nF 0 0 0\nF 1.3 0 0\n");
        fs::remove_file(filename).unwrap();
    }

    #[test]
    fn energy() {
        let filename = "testing-energy-output.dat";
        let universe = testing_universe();
        {
            let mut out = EnergyOutput::new(filename).unwrap();
            out.setup(&universe);
            out.write(&universe);
            out.finish(&universe);
        }

        check_file_content(filename, "# Energy of the simulation (kJ/mol)\n# Step     Potential     Kinetic     Total\n0   1.5000000000000027   0   1.5000000000000027\n");
        fs::remove_file(filename).unwrap();
    }

    #[test]
    fn cell() {
        let filename = "testing-cell-output.dat";
        let universe = testing_universe();
        {
            let mut out = CellOutput::new(filename).unwrap();
            out.setup(&universe);
            out.write(&universe);
            out.finish(&universe);
        }

        check_file_content(filename, "# Unit cell of the simulation\n# Step A/Å B/Å C/Å α/deg β/deg γ/deg\n0 10 10 10 90 90 90\n");
        fs::remove_file(filename).unwrap();
    }
}
<commit_msg>Remove extraneous spaces in energy output<commit_after>/*
 * Cymbalum, Molecular Simulation in Rust
 * Copyright (C) 2015 Guillaume Fraux
 *
 * 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/
*/

//! Saving properties of the simulation to some stream (file or stdout mainly)

extern crate chemharp;

use std::io::prelude::*;
use std::io;
use std::fs::File;
use std::path::Path;

use ::units;
use ::universe::Universe;

use ::universe::chemharp::universe_to_frame;

/// The `Output` trait define the interface for all the quantities outputed by
/// the simulation during the run. An Output can be a text or a binary data
/// file, an image, a text log, …
pub trait Output {
    /// Function called once at the beggining of the simulation, which allow
    /// for some setup of the output if needed.
    fn setup(&mut self, _: &Universe) {}

    /// Write the output from the universe.
    fn write(&mut self, universe: &Universe);

    /// Function called once at the end of the simulation.
    fn finish(&mut self, _: &Universe) {}
}

/******************************************************************************/
/// The `TrajectoryOutput` allow to write the trajectory of the system to a
/// file, using any format supported by the
/// [Chemharp](http://chemharp.readthedocs.org/en/latest/formats.html) library.
pub struct TrajectoryOutput {
    file: chemharp::Trajectory,
}

impl TrajectoryOutput {
    /// Create a new `TrajectoryOutput` writing to `filename`. The file is
    /// replaced if it already exists.
    pub fn new<'a, S>(filename: S) -> Result<TrajectoryOutput, chemharp::Error> where S: Into<&'a str> {
        Ok(TrajectoryOutput{
            file: try!(chemharp::Trajectory::create(filename.into()))
        })
    }
}

impl Output for TrajectoryOutput {
    fn write(&mut self, universe: &Universe) {
        let frame = match universe_to_frame(universe) {
            Ok(val) => val,
            Err(err) => {
                error!("Error in Chemharp runtime while converting data: {}", err.message());
                panic!();
            }
        };

        match self.file.write(&frame) {
            Ok(()) => {},
            Err(err) => {
                error!("Error in Chemharp runtime while writing file: {}", err.message());
                panic!();
            }
        }
    }
}

/******************************************************************************/
/// The `CellOutput` write all the components of a cell to a file . The columns
/// in the file contains the following values: `A B C α β γ`.
pub struct CellOutput {
    file: File,
    path: String
}

impl CellOutput {
    /// Create a new `CellOutput` writing to `filename`. The file is replaced if
    /// it already exists.
    pub fn new<P: AsRef<Path>>(filename: P) -> Result<CellOutput, io::Error> {
        let path = String::from(filename.as_ref().to_str().unwrap());
        Ok(CellOutput{
            file: try!(File::create(filename)),
            path: path,
        })
    }
}

impl Output for CellOutput {
    fn setup(&mut self, _: &Universe) {
        if let Err(e) = writeln!(&mut self.file, "# Unit cell of the simulation") {
            // Do panic in early time
            panic!("Could not write to file '{}': {:?}", self.path, e);
        }
        if let Err(e) = writeln!(&mut self.file, "# Step A/Å B/Å C/Å α/deg β/deg γ/deg") {
            panic!("Could not write to file '{}': {:?}", self.path, e);
        }
    }

    fn write(&mut self, universe: &Universe) {
        let cell = universe.cell();
        if let Err(e) = writeln!(
            &mut self.file, "{} {} {} {} {} {} {}",
            universe.step(), cell.a(), cell.b(), cell.c(), cell.alpha(), cell.beta(), cell.gamma()
        ) {
            // Do not panic during the simulation
            error!("Could not write to file '{}': {:?}", self.path, e);
        }
    }
}

/******************************************************************************/
/// The `EnergyOutput` write the energy of the system to a text file, organized
/// as: `PotentialEnergy     KineticEnergy     TotalEnergy`.
pub struct EnergyOutput {
    file: File,
}

impl EnergyOutput {
    /// Create a new `EnergyOutput` writing to `filename`. The file is replaced
    /// if it already exists.
    pub fn new<P: AsRef<Path>>(filename: P) -> Result<EnergyOutput, io::Error> {
        Ok(EnergyOutput{
            file: try!(File::create(filename))
        })
    }
}

impl Output for EnergyOutput {
    fn setup(&mut self, _: &Universe) {
        writeln!(&mut self.file, "# Energy of the simulation (kJ/mol)").unwrap();
        writeln!(&mut self.file, "# Step Potential Kinetic Total").unwrap();
    }

    fn write(&mut self, universe: &Universe) {
        let potential = units::to(universe.potential_energy(), "kJ/mol").unwrap();
        let kinetic = units::to(universe.kinetic_energy(), "kJ/mol").unwrap();
        let total = units::to(universe.total_energy(), "kJ/mol").unwrap();
        writeln!(&mut self.file, "{} {} {} {}", universe.step(), potential, kinetic, total).unwrap();
    }
}

#[cfg(test)]
mod tests {
    use std::io::prelude::*;
    use std::fs;

    use super::*;
    use ::universe::*;
    use ::types::*;
    use ::potentials::*;
    use ::units;

    fn testing_universe() -> Universe {
        let mut universe = Universe::from_cell(UnitCell::cubic(10.0));;

        universe.add_particle(Particle::new("F"));
        universe[0].set_position(Vector3D::new(0.0, 0.0, 0.0));

        universe.add_particle(Particle::new("F"));
        universe[1].set_position(Vector3D::new(1.3, 0.0, 0.0));

        universe.add_pair_interaction("F", "F",
            Harmonic{k: units::from(300.0, "kJ/mol/A^2").unwrap(), x0: units::from(1.2, "A").unwrap()});
        return universe;
    }

    fn check_file_content(filename: &str, content: &str) {
        let mut file = fs::File::open(filename).unwrap();
        let mut buffer = String::new();
        file.read_to_string(&mut buffer).unwrap();

        assert_eq!(buffer, content);
    }

    #[test]
    fn trajectory() {
        let filename = "testing-trajectory-output.xyz";
        let universe = testing_universe();
        {
            let mut out = TrajectoryOutput::new(filename).unwrap();
            out.setup(&universe);
            out.write(&universe);
            out.finish(&universe);
        }

        check_file_content(filename, "2\nWritten by Chemharp\nF 0 0 0\nF 1.3 0 0\n");
        fs::remove_file(filename).unwrap();
    }

    #[test]
    fn energy() {
        let filename = "testing-energy-output.dat";
        let universe = testing_universe();
        {
            let mut out = EnergyOutput::new(filename).unwrap();
            out.setup(&universe);
            out.write(&universe);
            out.finish(&universe);
        }

        check_file_content(filename, "# Energy of the simulation (kJ/mol)\n# Step Potential Kinetic Total\n0 1.5000000000000027 0 1.5000000000000027\n");
        fs::remove_file(filename).unwrap();
    }

    #[test]
    fn cell() {
        let filename = "testing-cell-output.dat";
        let universe = testing_universe();
        {
            let mut out = CellOutput::new(filename).unwrap();
            out.setup(&universe);
            out.write(&universe);
            out.finish(&universe);
        }

        check_file_content(filename, "# Unit cell of the simulation\n# Step A/Å B/Å C/Å α/deg β/deg γ/deg\n0 10 10 10 90 90 90\n");
        fs::remove_file(filename).unwrap();
    }
}
<|endoftext|>