<commit_before>use Urgency;
use ffi;
use gdk_pixbuf;
use glib::translate::*;
use glib;
use glib_ffi;
use std;

glib_wrapper! {
    /// `Notification` represents a passive pop-up notification. It can contain
    /// summary text, body text, and an icon, as well as hints specifying how
    /// the notification should be presented. The notification is rendered by
    /// a notification daemon, and may present the notification in any number
    /// of ways. As such, there is a clear separation of content and
    /// presentation, and this API enforces that.
    pub struct Notification(Object<ffi::NotifyNotification>);

    match fn {
        get_type => || ffi::notify_notification_get_type(),
    }
}

impl Notification {
    /// Creates a new `Notification`. The summary text is required, but
    /// all other parameters are optional.
    /// ## `summary`
    /// The required summary text.
    /// ## `body`
    /// The optional body text.
    /// ## `icon`
    /// The optional icon theme icon name or filename.
    ///
    /// # Returns
    ///
    /// The new `Notification`.
    pub fn new<'a, 'b, P: Into<Option<&'a str>>, Q: Into<Option<&'b str>>>(
        summary: &str,
        body: P,
        icon: Q,
    ) -> Notification {
        assert_initialized_libnotify!();
        let body = body.into();
        let body = body.to_glib_none();
        let icon = icon.into();
        let icon = icon.to_glib_none();
        unsafe {
            from_glib_full(ffi::notify_notification_new(
                summary.to_glib_none().0,
                body.0,
                icon.0,
            ))
        }
    }

    /// Synchronously tells the notification server to hide the notification on the screen.
    ///
    /// # Returns
    ///
    /// `Ok(())` on success, or `Err(err)` on error
    pub fn close(&self) -> Result<(), glib::error::Error> {
        assert_initialized_libnotify!();
        unsafe {
            let mut err: *mut glib_ffi::GError = std::ptr::null_mut();
            ffi::notify_notification_close(self.to_glib_none().0, &mut err);

            if !err.is_null() {
                return Err(glib::error::Error::wrap(err));
            } else {
                return Ok(());
            }
        }
    }

    /// Tells the notification server to display the notification on the screen.
    ///
    /// # Returns
    ///
    /// `Ok(())` on success, or `Err(err)` on error
    // TODO: test if Error leaks memory
    pub fn show(&self) -> Result<(), glib::error::Error> {
        assert_initialized_libnotify!();
        unsafe {
            let mut err: *mut glib_ffi::GError = std::ptr::null_mut();
            ffi::notify_notification_show(self.to_glib_none().0, &mut err);

            if !err.is_null() {
                return Err(glib::error::Error::wrap(err));
            } else {
                return Ok(());
            }
        }
    }

    /// Sets a hint for `key` with value `value`. If `value` is `None`,
    /// a previously set hint for `key` is unset.
    ///
    /// If `value` is floating, it is consumed.
    /// ## `key`
    /// the hint key
    /// ## `value`
    pub fn set_hint(&self, key: &str, value: Option<glib::variant::Variant>) {
        assert_initialized_libnotify!();

        let gvalue: *mut glib_ffi::GVariant = {
            match value {
                Some(ref value) => value.to_glib_none().0,
                None => std::ptr::null_mut(),
            }
        };

        unsafe {
            ffi::notify_notification_set_hint(
                self.to_glib_none().0,
                key.to_glib_none().0,
                gvalue,
            )
        }
    }

    /// Sets the image in the notification from a `gdk_pixbuf::Pixbuf`.
    /// ## `pixbuf`
    /// The image.
    pub fn set_image_from_pixbuf(&self, pixbuf: &gdk_pixbuf::Pixbuf) {
        assert_initialized_libnotify!();

        unsafe {
            ffi::notify_notification_set_image_from_pixbuf(
                self.to_glib_none().0,
                pixbuf.to_glib_none().0,
            );
        }
    }

    /// Clears all hints from the notification.
    pub fn clear_hints(&self) {
        unsafe {
            ffi::notify_notification_clear_hints(self.to_glib_none().0);
        }
    }

    /// Sets the application name for the notification. If this function is
    /// not called or if `app_name` is `None`, the application name will be
    /// set from the value used in `notify_init` or overridden with
    /// `notify_set_app_name`.
    /// ## `app_name`
    /// the localised application name
    pub fn set_app_name<'a, P: Into<Option<&'a str>>>(&self, app_name: P) {
        let app_name = app_name.into();
        let app_name = app_name.to_glib_none();
        unsafe {
            ffi::notify_notification_set_app_name(
                self.to_glib_none().0,
                app_name.0,
            );
        }
    }

    /// Sets the category of this notification. This can be used by the
    /// notification server to filter or display the data in a certain way.
    /// ## `category`
    /// The category.
    pub fn set_category(&self, category: &str) {
        unsafe {
            ffi::notify_notification_set_category(
                self.to_glib_none().0,
                category.to_glib_none().0,
            );
        }
    }

    /// Sets the timeout of the notification. To set the default time, pass
    /// `NOTIFY_EXPIRES_DEFAULT` as `timeout`. To set the notification to never
    /// expire, pass `NOTIFY_EXPIRES_NEVER`.
    ///
    /// Note that the timeout may be ignored by the server.
    /// ## `timeout`
    /// The timeout in milliseconds.
    pub fn set_timeout(&self, timeout: i32) {
        unsafe {
            ffi::notify_notification_set_timeout(
                self.to_glib_none().0,
                timeout,
            );
        }
    }

    /// Sets the urgency level of this notification.
    ///
    /// See: `Urgency`
    /// ## `urgency`
    /// The urgency level.
    pub fn set_urgency(&self, urgency: Urgency) {
        unsafe {
            ffi::notify_notification_set_urgency(
                self.to_glib_none().0,
                urgency.to_glib(),
            );
        }
    }

    /// Updates the notification text and icon. This won't send the update out
    /// and display it on the screen. For that, you will need to call
    /// `Notification::show`.
    /// ## `summary`
    /// The new required summary text.
    /// ## `body`
    /// The optional body text.
    /// ## `icon`
    /// The optional icon theme icon name or filename.
    ///
    /// # Returns
    ///
    /// `true`, unless an invalid parameter was passed.
    /// `Ok(())` on success, or `Err(str)` if an invalid parameter was passed
    // TODO: switch back to BoolError when it hits stable glib
    pub fn update<
        'a,
        'b,
        P: Into<Option<&'a str>>,
        Q: Into<Option<&'b str>>,
    >(
        &self,
        summary: &str,
        body: P,
        icon: Q,
    ) -> Result<(), String> {
        let body = body.into();
        let body = body.to_glib_none();
        let icon = icon.into();
        let icon = icon.to_glib_none();
        unsafe {
            let b = ffi::notify_notification_update(
                self.to_glib_none().0,
                summary.to_glib_none().0,
                body.0,
                icon.0,
            );
            match b {
                glib_ffi::GFALSE => Err(
                    String::from("Invalid parameter passed"),
                ),
                _ => Ok(()),
            }
        }
    }
}
<commit_msg>Fix documentation<commit_after>use Urgency;
use ffi;
use gdk_pixbuf;
use glib::translate::*;
use glib;
use glib_ffi;
use std;

glib_wrapper! {
    /// `Notification` represents a passive pop-up notification. It can contain
    /// summary text, body text, and an icon, as well as hints specifying how
    /// the notification should be presented. The notification is rendered by
    /// a notification daemon, and may present the notification in any number
    /// of ways. As such, there is a clear separation of content and
    /// presentation, and this API enforces that.
    pub struct Notification(Object<ffi::NotifyNotification>);

    match fn {
        get_type => || ffi::notify_notification_get_type(),
    }
}

impl Notification {
    /// Creates a new `Notification`. The summary text is required, but
    /// all other parameters are optional.
    /// ## `summary`
    /// The required summary text.
    /// ## `body`
    /// The optional body text.
    /// ## `icon`
    /// The optional icon theme icon name or filename.
    ///
    /// # Returns
    ///
    /// The new `Notification`.
    pub fn new<'a, 'b, P: Into<Option<&'a str>>, Q: Into<Option<&'b str>>>(
        summary: &str,
        body: P,
        icon: Q,
    ) -> Notification {
        assert_initialized_libnotify!();
        let body = body.into();
        let body = body.to_glib_none();
        let icon = icon.into();
        let icon = icon.to_glib_none();
        unsafe {
            from_glib_full(ffi::notify_notification_new(
                summary.to_glib_none().0,
                body.0,
                icon.0,
            ))
        }
    }

    /// Synchronously tells the notification server to hide the notification on the screen.
    ///
    /// # Returns
    ///
    /// `Ok(())` on success, or `Err(err)` on error
    pub fn close(&self) -> Result<(), glib::error::Error> {
        assert_initialized_libnotify!();
        unsafe {
            let mut err: *mut glib_ffi::GError = std::ptr::null_mut();
            ffi::notify_notification_close(self.to_glib_none().0, &mut err);

            if !err.is_null() {
                return Err(glib::error::Error::wrap(err));
            } else {
                return Ok(());
            }
        }
    }

    /// Tells the notification server to display the notification on the screen.
    ///
    /// # Returns
    ///
    /// `Ok(())` on success, or `Err(err)` on error
    // TODO: test if Error leaks memory
    pub fn show(&self) -> Result<(), glib::error::Error> {
        assert_initialized_libnotify!();
        unsafe {
            let mut err: *mut glib_ffi::GError = std::ptr::null_mut();
            ffi::notify_notification_show(self.to_glib_none().0, &mut err);

            if !err.is_null() {
                return Err(glib::error::Error::wrap(err));
            } else {
                return Ok(());
            }
        }
    }

    /// Sets a hint for `key` with value `value`. If `value` is `None`,
    /// a previously set hint for `key` is unset.
    ///
    /// If `value` is floating, it is consumed.
    /// ## `key`
    /// the hint key
    /// ## `value`
    pub fn set_hint(&self, key: &str, value: Option<glib::variant::Variant>) {
        assert_initialized_libnotify!();

        let gvalue: *mut glib_ffi::GVariant = {
            match value {
                Some(ref value) => value.to_glib_none().0,
                None => std::ptr::null_mut(),
            }
        };

        unsafe {
            ffi::notify_notification_set_hint(
                self.to_glib_none().0,
                key.to_glib_none().0,
                gvalue,
            )
        }
    }

    /// Sets the image in the notification from a `gdk_pixbuf::Pixbuf`.
    /// ## `pixbuf`
    /// The image.
    pub fn set_image_from_pixbuf(&self, pixbuf: &gdk_pixbuf::Pixbuf) {
        assert_initialized_libnotify!();

        unsafe {
            ffi::notify_notification_set_image_from_pixbuf(
                self.to_glib_none().0,
                pixbuf.to_glib_none().0,
            );
        }
    }

    /// Clears all hints from the notification.
    pub fn clear_hints(&self) {
        unsafe {
            ffi::notify_notification_clear_hints(self.to_glib_none().0);
        }
    }

    /// Sets the application name for the notification. If this function is
    /// not called or if `app_name` is `None`, the application name will be
    /// set from the value used in `notify_init` or overridden with
    /// `notify_set_app_name`.
    /// ## `app_name`
    /// the localised application name
    pub fn set_app_name<'a, P: Into<Option<&'a str>>>(&self, app_name: P) {
        let app_name = app_name.into();
        let app_name = app_name.to_glib_none();
        unsafe {
            ffi::notify_notification_set_app_name(
                self.to_glib_none().0,
                app_name.0,
            );
        }
    }

    /// Sets the category of this notification. This can be used by the
    /// notification server to filter or display the data in a certain way.
    /// ## `category`
    /// The category.
    pub fn set_category(&self, category: &str) {
        unsafe {
            ffi::notify_notification_set_category(
                self.to_glib_none().0,
                category.to_glib_none().0,
            );
        }
    }

    /// Sets the timeout of the notification. To set the default time, pass
    /// `NOTIFY_EXPIRES_DEFAULT` as `timeout`. To set the notification to never
    /// expire, pass `NOTIFY_EXPIRES_NEVER`.
    ///
    /// Note that the timeout may be ignored by the server.
    /// ## `timeout`
    /// The timeout in milliseconds.
    pub fn set_timeout(&self, timeout: i32) {
        unsafe {
            ffi::notify_notification_set_timeout(
                self.to_glib_none().0,
                timeout,
            );
        }
    }

    /// Sets the urgency level of this notification.
    ///
    /// See: `Urgency`
    /// ## `urgency`
    /// The urgency level.
    pub fn set_urgency(&self, urgency: Urgency) {
        unsafe {
            ffi::notify_notification_set_urgency(
                self.to_glib_none().0,
                urgency.to_glib(),
            );
        }
    }

    /// Updates the notification text and icon. This won't send the update out
    /// and display it on the screen. For that, you will need to call
    /// `Notification::show`.
    /// ## `summary`
    /// The new required summary text.
    /// ## `body`
    /// The optional body text.
    /// ## `icon`
    /// The optional icon theme icon name or filename.
    ///
    /// # Returns
    ///
    /// `Ok(())` on success, or `Err(str)` if an invalid parameter was passed
    // TODO: switch back to BoolError when it hits stable glib
    pub fn update<
        'a,
        'b,
        P: Into<Option<&'a str>>,
        Q: Into<Option<&'b str>>,
    >(
        &self,
        summary: &str,
        body: P,
        icon: Q,
    ) -> Result<(), String> {
        let body = body.into();
        let body = body.to_glib_none();
        let icon = icon.into();
        let icon = icon.to_glib_none();
        unsafe {
            let b = ffi::notify_notification_update(
                self.to_glib_none().0,
                summary.to_glib_none().0,
                body.0,
                icon.0,
            );
            match b {
                glib_ffi::GFALSE => Err(
                    String::from("Invalid parameter passed"),
                ),
                _ => Ok(()),
            }
        }
    }
}
<|endoftext|>


<commit_before>use std::cmp::max;
use std::fmt;
use std::ops::Deref;
use std::sync::{Mutex, MutexGuard};

use datetime::TimeZone;
use zoneinfo_compiled::{CompiledData, Result as TZResult};

use locale;

use log::debug;

use users::UsersCache;

use crate::style::Colours;
use crate::output::cell::TextCell;
use crate::output::render::TimeRender;
use crate::output::time::TimeFormat;
use crate::fs::{File, fields as f};
use crate::fs::feature::git::GitCache;

/// Options for displaying a table.
pub struct Options {
    pub env: Environment,
    pub size_format: SizeFormat,
    pub time_format: TimeFormat,
    pub columns: Columns,
}

// I had to make other types derive Debug,
// and Mutex<UsersCache> is not that!
impl fmt::Debug for Options {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(f, "Table({:#?})", self.columns)
    }
}

/// Extra columns to display in the table.
#[derive(PartialEq, Debug)]
pub struct Columns {

    /// At least one of these timestamps will be shown.
    pub time_types: TimeTypes,

    // The rest are just on/off
    pub inode: bool,
    pub links: bool,
    pub blocks: bool,
    pub group: bool,
    pub git: bool,

    // Defaults to true:
    pub permissions: bool,
    pub filesize: bool,
    pub user: bool,
}

impl Columns {
    pub fn collect(&self, actually_enable_git: bool) -> Vec<Column> {
        let mut columns = Vec::with_capacity(4);

        if self.inode {
            columns.push(Column::Inode);
        }

        if self.permissions {
            columns.push(Column::Permissions);
        }

        if self.links {
            columns.push(Column::HardLinks);
        }

        if self.filesize {
            columns.push(Column::FileSize);
        }

        if self.blocks {
            columns.push(Column::Blocks);
        }

        if self.user {
            columns.push(Column::User);
        }

        if self.group {
            columns.push(Column::Group);
        }

        if self.time_types.modified {
            columns.push(Column::Timestamp(TimeType::Modified));
        }

        if self.time_types.changed {
            columns.push(Column::Timestamp(TimeType::Changed));
        }

        if self.time_types.created {
            columns.push(Column::Timestamp(TimeType::Created));
        }

        if self.time_types.accessed {
            columns.push(Column::Timestamp(TimeType::Accessed));
        }

        if cfg!(feature="git") && self.git && actually_enable_git {
            columns.push(Column::GitStatus);
        }

        columns
    }
}

/// A table contains these.
#[derive(Debug)]
pub enum Column {
    Permissions,
    FileSize,
    Timestamp(TimeType),
    Blocks,
    User,
    Group,
    HardLinks,
    Inode,
    GitStatus,
}

/// Each column can pick its own **Alignment**. Usually, numbers are
/// right-aligned, and text is left-aligned.
#[derive(Copy, Clone)]
pub enum Alignment {
    Left, Right,
}

impl Column {

    /// Get the alignment this column should use.
    pub fn alignment(&self) -> Alignment {
        match *self {
            Column::FileSize
            | Column::HardLinks
            | Column::Inode
            | Column::Blocks
            | Column::GitStatus => Alignment::Right,
            _                   => Alignment::Left,
        }
    }

    /// Get the text that should be printed at the top, when the user elects
    /// to have a header row printed.
    pub fn header(&self) -> &'static str {
        match *self {
            Column::Permissions   => "Permissions",
            Column::FileSize      => "Size",
            Column::Timestamp(t)  => t.header(),
            Column::Blocks        => "Blocks",
            Column::User          => "User",
            Column::Group         => "Group",
            Column::HardLinks     => "Links",
            Column::Inode         => "inode",
            Column::GitStatus     => "Git",
        }
    }
}

/// Formatting options for file sizes.
#[derive(PartialEq, Debug, Copy, Clone)]
pub enum SizeFormat {

    /// Format the file size using **decimal** prefixes, such as “kilo”,
    /// “mega”, or “giga”.
    DecimalBytes,

    /// Format the file size using **binary** prefixes, such as “kibi”,
    /// “mebi”, or “gibi”.
    BinaryBytes,

    /// Do no formatting and just display the size as a number of bytes.
    JustBytes,
}

impl Default for SizeFormat {
    fn default() -> SizeFormat {
        SizeFormat::DecimalBytes
    }
}

/// The types of a file’s time fields. These three fields are standard
/// across most (all?) operating systems.
#[derive(PartialEq, Debug, Copy, Clone)]
pub enum TimeType {
    /// The file’s modified time (`st_mtime`).
    Modified,

    /// The file’s changed time (`st_ctime`)
    Changed,

    /// The file’s accessed time (`st_atime`).
    Accessed,

    /// The file’s creation time (`btime` or `birthtime`).
    Created,
}

impl TimeType {

    /// Returns the text to use for a column’s heading in the columns output.
    pub fn header(self) -> &'static str {
        match self {
            TimeType::Modified  => "Date Modified",
            TimeType::Changed   => "Date Changed",
            TimeType::Accessed  => "Date Accessed",
            TimeType::Created   => "Date Created",
        }
    }
}

/// Fields for which of a file’s time fields should be displayed in the
/// columns output.
///
/// There should always be at least one of these--there's no way to disable
/// the time columns entirely (yet).
#[derive(PartialEq, Debug, Copy, Clone)]
pub struct TimeTypes {
    pub modified: bool,
    pub changed:  bool,
    pub accessed: bool,
    pub created:  bool,
}

impl Default for TimeTypes {

    /// By default, display just the ‘modified’ time. This is the most
    /// common option, which is why it has this shorthand.
    fn default() -> TimeTypes {
        TimeTypes { modified: true, changed: false, accessed: false, created: false }
    }
}

/// The **environment** struct contains any data that could change between
/// running instances of exa, depending on the user's computer's configuration.
///
/// Any environment field should be able to be mocked up for test runs.
pub struct Environment {

    /// Localisation rules for formatting numbers.
    numeric: locale::Numeric,

    /// The computer's current time zone. This gets used to determine how to
    /// offset files' timestamps.
    tz: Option<TimeZone>,

    /// Mapping cache of user IDs to usernames.
    users: Mutex<UsersCache>,
}

impl Environment {
    pub fn lock_users(&self) -> MutexGuard<UsersCache> {
        self.users.lock().unwrap()
    }

    pub fn load_all() -> Self {
        let tz = match determine_time_zone() {
            Ok(t) => Some(t),
            Err(ref e) => {
                println!("Unable to determine time zone: {}", e);
                None
            }
        };

        let numeric = locale::Numeric::load_user_locale()
                          .unwrap_or_else(|_| locale::Numeric::english());

        let users = Mutex::new(UsersCache::new());

        Environment { tz, numeric, users }
    }
}

fn determine_time_zone() -> TZResult<TimeZone> {
    TimeZone::from_file("/etc/localtime")
}

pub struct Table<'a> {
    columns: Vec<Column>,
    colours: &'a Colours,
    env: &'a Environment,
    widths: TableWidths,
    time_format: &'a TimeFormat,
    size_format: SizeFormat,
    git: Option<&'a GitCache>,
}

#[derive(Clone)]
pub struct Row {
    cells: Vec<TextCell>,
}

impl<'a, 'f> Table<'a> {
    pub fn new(options: &'a Options, git: Option<&'a GitCache>, colours: &'a Colours) -> Table<'a> {
        let columns = options.columns.collect(git.is_some());
        let widths = TableWidths::zero(columns.len());

        Table {
            colours, widths, columns, git,
            env:         &options.env,
            time_format: &options.time_format,
            size_format:  options.size_format,
        }
    }

    pub fn widths(&self) -> &TableWidths {
        &self.widths
    }

    pub fn header_row(&self) -> Row {
        let cells = self.columns.iter()
                        .map(|c| TextCell::paint_str(self.colours.header, c.header()))
                        .collect();

        Row { cells }
    }

    pub fn row_for_file(&self, file: &File, xattrs: bool) -> Row {
        let cells = self.columns.iter()
                        .map(|c| self.display(file, c, xattrs))
                        .collect();

        Row { cells }
    }

    pub fn add_widths(&mut self, row: &Row) {
        self.widths.add_widths(row)
    }

    fn permissions_plus(&self, file: &File, xattrs: bool) -> f::PermissionsPlus {
        f::PermissionsPlus {
            file_type: file.type_char(),
            permissions: file.permissions(),
            xattrs,
        }
    }

    fn display(&self, file: &File, column: &Column, xattrs: bool) -> TextCell {
        use crate::output::table::TimeType::*;

        match *column {
            Column::Permissions    => self.permissions_plus(file, xattrs).render(self.colours),
            Column::FileSize       => file.size().render(self.colours, self.size_format, &self.env.numeric),
            Column::HardLinks      => file.links().render(self.colours, &self.env.numeric),
            Column::Inode          => file.inode().render(self.colours.inode),
            Column::Blocks         => file.blocks().render(self.colours),
            Column::User           => file.user().render(self.colours, &*self.env.lock_users()),
            Column::Group          => file.group().render(self.colours, &*self.env.lock_users()),
            Column::GitStatus      => self.git_status(file).render(self.colours),

            Column::Timestamp(Modified)  => file.modified_time().render(self.colours.date, &self.env.tz, &self.time_format),
            Column::Timestamp(Changed)   => file.changed_time() .render(self.colours.date, &self.env.tz, &self.time_format),
            Column::Timestamp(Created)   => file.created_time() .render(self.colours.date, &self.env.tz, &self.time_format),
            Column::Timestamp(Accessed)  => file.accessed_time().render(self.colours.date, &self.env.tz, &self.time_format),
        }
    }

    fn git_status(&self, file: &File) -> f::Git {
        debug!("Getting Git status for file {:?}", file.path);
        self.git
            .map(|g| g.get(&file.path, file.is_directory()))
            .unwrap_or_default()
    }

    pub fn render(&self, row: Row) -> TextCell {
        let mut cell = TextCell::default();

        for (n, (this_cell, width)) in row.cells.into_iter().zip(self.widths.iter()).enumerate() {
            let padding = width - *this_cell.width;

            match self.columns[n].alignment() {
                Alignment::Left  => { cell.append(this_cell); cell.add_spaces(padding); }
                Alignment::Right => { cell.add_spaces(padding); cell.append(this_cell); }
            }

            cell.add_spaces(1);
        }

        cell
    }
}

pub struct TableWidths(Vec<usize>);

impl Deref for TableWidths {
    type Target = [usize];

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl TableWidths {
    pub fn zero(count: usize) -> TableWidths {
        TableWidths(vec![ 0; count ])
    }

    pub fn add_widths(&mut self, row: &Row) {
        for (old_width, cell) in self.0.iter_mut().zip(row.cells.iter()) {
            *old_width = max(*old_width, *cell.width);
        }
    }

    pub fn total(&self) -> usize {
        self.0.len() + self.0.iter().sum::<usize>()
    }
}
<commit_msg>Update determine_time_zone function to check TZ<commit_after>use std::cmp::max;
use std::env;
use std::fmt;
use std::ops::Deref;
use std::sync::{Mutex, MutexGuard};

use datetime::TimeZone;
use zoneinfo_compiled::{CompiledData, Result as TZResult};

use locale;

use log::debug;

use users::UsersCache;

use crate::style::Colours;
use crate::output::cell::TextCell;
use crate::output::render::TimeRender;
use crate::output::time::TimeFormat;
use crate::fs::{File, fields as f};
use crate::fs::feature::git::GitCache;

/// Options for displaying a table.
pub struct Options {
    pub env: Environment,
    pub size_format: SizeFormat,
    pub time_format: TimeFormat,
    pub columns: Columns,
}

// I had to make other types derive Debug,
// and Mutex<UsersCache> is not that!
impl fmt::Debug for Options {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(f, "Table({:#?})", self.columns)
    }
}

/// Extra columns to display in the table.
#[derive(PartialEq, Debug)]
pub struct Columns {

    /// At least one of these timestamps will be shown.
    pub time_types: TimeTypes,

    // The rest are just on/off
    pub inode: bool,
    pub links: bool,
    pub blocks: bool,
    pub group: bool,
    pub git: bool,

    // Defaults to true:
    pub permissions: bool,
    pub filesize: bool,
    pub user: bool,
}

impl Columns {
    pub fn collect(&self, actually_enable_git: bool) -> Vec<Column> {
        let mut columns = Vec::with_capacity(4);

        if self.inode {
            columns.push(Column::Inode);
        }

        if self.permissions {
            columns.push(Column::Permissions);
        }

        if self.links {
            columns.push(Column::HardLinks);
        }

        if self.filesize {
            columns.push(Column::FileSize);
        }

        if self.blocks {
            columns.push(Column::Blocks);
        }

        if self.user {
            columns.push(Column::User);
        }

        if self.group {
            columns.push(Column::Group);
        }

        if self.time_types.modified {
            columns.push(Column::Timestamp(TimeType::Modified));
        }

        if self.time_types.changed {
            columns.push(Column::Timestamp(TimeType::Changed));
        }

        if self.time_types.created {
            columns.push(Column::Timestamp(TimeType::Created));
        }

        if self.time_types.accessed {
            columns.push(Column::Timestamp(TimeType::Accessed));
        }

        if cfg!(feature="git") && self.git && actually_enable_git {
            columns.push(Column::GitStatus);
        }

        columns
    }
}

/// A table contains these.
#[derive(Debug)]
pub enum Column {
    Permissions,
    FileSize,
    Timestamp(TimeType),
    Blocks,
    User,
    Group,
    HardLinks,
    Inode,
    GitStatus,
}

/// Each column can pick its own **Alignment**. Usually, numbers are
/// right-aligned, and text is left-aligned.
#[derive(Copy, Clone)]
pub enum Alignment {
    Left, Right,
}

impl Column {

    /// Get the alignment this column should use.
    pub fn alignment(&self) -> Alignment {
        match *self {
            Column::FileSize
            | Column::HardLinks
            | Column::Inode
            | Column::Blocks
            | Column::GitStatus => Alignment::Right,
            _                   => Alignment::Left,
        }
    }

    /// Get the text that should be printed at the top, when the user elects
    /// to have a header row printed.
    pub fn header(&self) -> &'static str {
        match *self {
            Column::Permissions   => "Permissions",
            Column::FileSize      => "Size",
            Column::Timestamp(t)  => t.header(),
            Column::Blocks        => "Blocks",
            Column::User          => "User",
            Column::Group         => "Group",
            Column::HardLinks     => "Links",
            Column::Inode         => "inode",
            Column::GitStatus     => "Git",
        }
    }
}

/// Formatting options for file sizes.
#[derive(PartialEq, Debug, Copy, Clone)]
pub enum SizeFormat {

    /// Format the file size using **decimal** prefixes, such as “kilo”,
    /// “mega”, or “giga”.
    DecimalBytes,

    /// Format the file size using **binary** prefixes, such as “kibi”,
    /// “mebi”, or “gibi”.
    BinaryBytes,

    /// Do no formatting and just display the size as a number of bytes.
    JustBytes,
}

impl Default for SizeFormat {
    fn default() -> SizeFormat {
        SizeFormat::DecimalBytes
    }
}

/// The types of a file’s time fields. These three fields are standard
/// across most (all?) operating systems.
#[derive(PartialEq, Debug, Copy, Clone)]
pub enum TimeType {
    /// The file’s modified time (`st_mtime`).
    Modified,

    /// The file’s changed time (`st_ctime`)
    Changed,

    /// The file’s accessed time (`st_atime`).
    Accessed,

    /// The file’s creation time (`btime` or `birthtime`).
    Created,
}

impl TimeType {

    /// Returns the text to use for a column’s heading in the columns output.
    pub fn header(self) -> &'static str {
        match self {
            TimeType::Modified  => "Date Modified",
            TimeType::Changed   => "Date Changed",
            TimeType::Accessed  => "Date Accessed",
            TimeType::Created   => "Date Created",
        }
    }
}

/// Fields for which of a file’s time fields should be displayed in the
/// columns output.
///
/// There should always be at least one of these--there's no way to disable
/// the time columns entirely (yet).
#[derive(PartialEq, Debug, Copy, Clone)]
pub struct TimeTypes {
    pub modified: bool,
    pub changed:  bool,
    pub accessed: bool,
    pub created:  bool,
}

impl Default for TimeTypes {

    /// By default, display just the ‘modified’ time. This is the most
    /// common option, which is why it has this shorthand.
    fn default() -> TimeTypes {
        TimeTypes { modified: true, changed: false, accessed: false, created: false }
    }
}

/// The **environment** struct contains any data that could change between
/// running instances of exa, depending on the user's computer's configuration.
///
/// Any environment field should be able to be mocked up for test runs.
pub struct Environment {

    /// Localisation rules for formatting numbers.
    numeric: locale::Numeric,

    /// The computer's current time zone. This gets used to determine how to
    /// offset files' timestamps.
    tz: Option<TimeZone>,

    /// Mapping cache of user IDs to usernames.
    users: Mutex<UsersCache>,
}

impl Environment {
    pub fn lock_users(&self) -> MutexGuard<UsersCache> {
        self.users.lock().unwrap()
    }

    pub fn load_all() -> Self {
        let tz = match determine_time_zone() {
            Ok(t) => Some(t),
            Err(ref e) => {
                println!("Unable to determine time zone: {}", e);
                None
            }
        };

        let numeric = locale::Numeric::load_user_locale()
                          .unwrap_or_else(|_| locale::Numeric::english());

        let users = Mutex::new(UsersCache::new());

        Environment { tz, numeric, users }
    }
}

fn determine_time_zone() -> TZResult<TimeZone> {
    let tz = env::var("TZ");
    if tz.is_err() {
        return TimeZone::from_file("/etc/localtime");
    } else {
        return TimeZone::from_file(format!("/usr/share/zoneinfo/{}", tz.unwrap()));
    }
}

pub struct Table<'a> {
    columns: Vec<Column>,
    colours: &'a Colours,
    env: &'a Environment,
    widths: TableWidths,
    time_format: &'a TimeFormat,
    size_format: SizeFormat,
    git: Option<&'a GitCache>,
}

#[derive(Clone)]
pub struct Row {
    cells: Vec<TextCell>,
}

impl<'a, 'f> Table<'a> {
    pub fn new(options: &'a Options, git: Option<&'a GitCache>, colours: &'a Colours) -> Table<'a> {
        let columns = options.columns.collect(git.is_some());
        let widths = TableWidths::zero(columns.len());

        Table {
            colours, widths, columns, git,
            env:         &options.env,
            time_format: &options.time_format,
            size_format:  options.size_format,
        }
    }

    pub fn widths(&self) -> &TableWidths {
        &self.widths
    }

    pub fn header_row(&self) -> Row {
        let cells = self.columns.iter()
                        .map(|c| TextCell::paint_str(self.colours.header, c.header()))
                        .collect();

        Row { cells }
    }

    pub fn row_for_file(&self, file: &File, xattrs: bool) -> Row {
        let cells = self.columns.iter()
                        .map(|c| self.display(file, c, xattrs))
                        .collect();

        Row { cells }
    }

    pub fn add_widths(&mut self, row: &Row) {
        self.widths.add_widths(row)
    }

    fn permissions_plus(&self, file: &File, xattrs: bool) -> f::PermissionsPlus {
        f::PermissionsPlus {
            file_type: file.type_char(),
            permissions: file.permissions(),
            xattrs,
        }
    }

    fn display(&self, file: &File, column: &Column, xattrs: bool) -> TextCell {
        use crate::output::table::TimeType::*;

        match *column {
            Column::Permissions    => self.permissions_plus(file, xattrs).render(self.colours),
            Column::FileSize       => file.size().render(self.colours, self.size_format, &self.env.numeric),
            Column::HardLinks      => file.links().render(self.colours, &self.env.numeric),
            Column::Inode          => file.inode().render(self.colours.inode),
            Column::Blocks         => file.blocks().render(self.colours),
            Column::User           => file.user().render(self.colours, &*self.env.lock_users()),
            Column::Group          => file.group().render(self.colours, &*self.env.lock_users()),
            Column::GitStatus      => self.git_status(file).render(self.colours),

            Column::Timestamp(Modified)  => file.modified_time().render(self.colours.date, &self.env.tz, &self.time_format),
            Column::Timestamp(Changed)   => file.changed_time() .render(self.colours.date, &self.env.tz, &self.time_format),
            Column::Timestamp(Created)   => file.created_time() .render(self.colours.date, &self.env.tz, &self.time_format),
            Column::Timestamp(Accessed)  => file.accessed_time().render(self.colours.date, &self.env.tz, &self.time_format),
        }
    }

    fn git_status(&self, file: &File) -> f::Git {
        debug!("Getting Git status for file {:?}", file.path);
        self.git
            .map(|g| g.get(&file.path, file.is_directory()))
            .unwrap_or_default()
    }

    pub fn render(&self, row: Row) -> TextCell {
        let mut cell = TextCell::default();

        for (n, (this_cell, width)) in row.cells.into_iter().zip(self.widths.iter()).enumerate() {
            let padding = width - *this_cell.width;

            match self.columns[n].alignment() {
                Alignment::Left  => { cell.append(this_cell); cell.add_spaces(padding); }
                Alignment::Right => { cell.add_spaces(padding); cell.append(this_cell); }
            }

            cell.add_spaces(1);
        }

        cell
    }
}

pub struct TableWidths(Vec<usize>);

impl Deref for TableWidths {
    type Target = [usize];

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl TableWidths {
    pub fn zero(count: usize) -> TableWidths {
        TableWidths(vec![ 0; count ])
    }

    pub fn add_widths(&mut self, row: &Row) {
        for (old_width, cell) in self.0.iter_mut().zip(row.cells.iter()) {
            *old_width = max(*old_width, *cell.width);
        }
    }

    pub fn total(&self) -> usize {
        self.0.len() + self.0.iter().sum::<usize>()
    }
}
<|endoftext|>


<commit_before>#![allow(dead_code)]

//! The `ParallelIterator` module makes it easy to write parallel
//! programs using an iterator-style interface. To get access to all
//! the methods you want, the easiest is to write `use
//! rayon::par_iter::*;` at the top of your module, which will import
//! the various traits and methods you need.
//!
//! The submodules of this module mostly just contain implementaton
//! details of little interest to an end-user.

use std::ops::Fn;
use self::collect::collect_into;
use self::enumerate::Enumerate;
use self::map::Map;
use self::reduce::{reduce, ReduceOp, SumOp, MulOp, MinOp, MaxOp, ReduceWithOp,
                   SUM, MUL, MIN, MAX};
use self::state::ParallelIteratorState;
use self::weight::Weight;

pub mod collect;
pub mod enumerate;
pub mod len;
pub mod for_each;
pub mod reduce;
pub mod slice;
pub mod slice_mut;
pub mod state;
pub mod map;
pub mod weight;

#[cfg(test)]
mod test;

pub trait IntoParallelIterator {
    type Iter: ParallelIterator<Item=Self::Item>;
    type Item: Send;

    fn into_par_iter(self) -> Self::Iter;
}

pub trait IntoParallelRefIterator<'r> {
    type Iter: ParallelIterator<Item=&'r Self::Item>;
    type Item: Sync + 'r;

    fn par_iter(&'r self) -> Self::Iter;
}

pub trait IntoParallelRefMutIterator<'r> {
    type Iter: ParallelIterator<Item=&'r mut Self::Item>;
    type Item: Send + 'r;

    fn par_iter_mut(&'r mut self) -> Self::Iter;
}

/// The `ParallelIterator` interface.
pub trait ParallelIterator {
    type Item: Send;
    type Shared: Sync;
    type State: ParallelIteratorState<Shared=Self::Shared, Item=Self::Item> + Send;

    fn state(self) -> (Self::Shared, Self::State);

    /// Indicates the relative "weight" of producing each item in this
    /// parallel iterator. A higher weight will cause finer-grained
    /// parallel subtasks. 1.0 indicates something very cheap and
    /// uniform, like copying a value out of an array, or computing `x
    /// + 1`. If your tasks are either very expensive, or very
    /// unpredictable, you are better off with higher values. Using
    /// `f64::INFINITY` will cause the finest grained parallel
    /// subtasks possible. Tuning this value should not affect
    /// correctness but can improve (or hurt) performance.
    fn weight(self, scale: f64) -> Weight<Self>
        where Self: Sized
    {
        Weight::new(self, scale)
    }

    /// Yields an index along with each item.
    fn enumerate(self) -> Enumerate<Self>
        where Self: Sized
    {
        Enumerate::new(self)
    }

    /// Executes `OP` on each item produced by the iterator, in parallel.
    fn for_each<OP>(self, op: OP)
        where OP: Fn(Self::Item) + Sync, Self: Sized
    {
        for_each::for_each(self, &op)
    }

    /// Applies `map_op` to each item of his iterator, producing a new
    /// iterator with the results.
    fn map<MAP_OP,R>(self, map_op: MAP_OP) -> Map<Self, MAP_OP>
        where MAP_OP: Fn(Self::Item) -> R, Self: Sized
    {
        Map::new(self, map_op)
    }

    /// Collects the results of the iterator into the specified
    /// vector. The vector is always truncated before execution
    /// begins. If possible, reusing the vector across calls can lead
    /// to better performance since it reuses the same backing buffer.
    fn collect_into(self, target: &mut Vec<Self::Item>)
        where Self: Sized
    {
        collect_into(self, target);
    }

    /// Reduces the items in the iterator into one item using `op`.
    /// See also `sum`, `mul`, `min`, etc, which are slightly more
    /// efficient. Returns `None` if the iterator is empty.
    ///
    /// Note: unlike in a sequential iterator, the order in which `op`
    /// will be applied to reduce the result is not specified. So `op`
    /// should be commutative and associative or else the results will
    /// be non-deterministic.
    fn reduce_with<OP>(self, op: OP) -> Option<Self::Item>
        where Self: Sized, OP: Fn(Self::Item, Self::Item) -> Self::Item + Sync,
    {
        reduce(self.map(Some), &ReduceWithOp::new(op))
    }

    /// Sums up the items in the iterator.
    ///
    /// Note that the order in items will be reduced is not specified,
    /// so if the `+` operator is not truly commutative and
    /// associative (as is the case for floating point numbers), then
    /// the results are not fully deterministic.
    fn sum(self) -> Self::Item
        where Self: Sized, SumOp: ReduceOp<Self::Item>
    {
        reduce(self, SUM)
    }

    /// Multiplies all the items in the iterator.
    ///
    /// Note that the order in items will be reduced is not specified,
    /// so if the `*` operator is not truly commutative and
    /// associative (as is the case for floating point numbers), then
    /// the results are not fully deterministic.
    fn mul(self) -> Self::Item
        where Self: Sized, MulOp: ReduceOp<Self::Item>
    {
        reduce(self, MUL)
    }

    /// Computes the minimum of all the items in the iterator.
    ///
    /// Note that the order in items will be reduced is not specified,
    /// so if the `Ord` impl is not truly commutative and associative
    /// (as is the case for floating point numbers), then the results
    /// are not deterministic.
    fn min(self) -> Self::Item
        where Self: Sized, MinOp: ReduceOp<Self::Item>
    {
        reduce(self, MIN)
    }

    /// Computes the maximum of all the items in the iterator.
    ///
    /// Note that the order in items will be reduced is not specified,
    /// so if the `Ord` impl is not truly commutative and associative
    /// (as is the case for floating point numbers), then the results
    /// are not deterministic.
    fn max(self) -> Self::Item
        where Self: Sized, MaxOp: ReduceOp<Self::Item>
    {
        reduce(self, MAX)
    }

    /// Reduces the items using the given "reduce operator". You may
    /// prefer `reduce_with` for a simpler interface.
    ///
    /// Note that the order in items will be reduced is not specified,
    /// so if the `reduce_op` impl is not truly commutative and
    /// associative, then the results are not deterministic.
    fn reduce<REDUCE_OP>(self, reduce_op: &REDUCE_OP) -> Self::Item
        where Self: Sized, REDUCE_OP: ReduceOp<Self::Item>
    {
        reduce(self, reduce_op)
    }
}

<commit_msg>Add a `weight_max` method as a nice shorthand. Fixes #13.<commit_after>#![allow(dead_code)]

//! The `ParallelIterator` module makes it easy to write parallel
//! programs using an iterator-style interface. To get access to all
//! the methods you want, the easiest is to write `use
//! rayon::par_iter::*;` at the top of your module, which will import
//! the various traits and methods you need.
//!
//! The submodules of this module mostly just contain implementaton
//! details of little interest to an end-user.

use std::f64;
use std::ops::Fn;
use self::collect::collect_into;
use self::enumerate::Enumerate;
use self::map::Map;
use self::reduce::{reduce, ReduceOp, SumOp, MulOp, MinOp, MaxOp, ReduceWithOp,
                   SUM, MUL, MIN, MAX};
use self::state::ParallelIteratorState;
use self::weight::Weight;

pub mod collect;
pub mod enumerate;
pub mod len;
pub mod for_each;
pub mod reduce;
pub mod slice;
pub mod slice_mut;
pub mod state;
pub mod map;
pub mod weight;

#[cfg(test)]
mod test;

pub trait IntoParallelIterator {
    type Iter: ParallelIterator<Item=Self::Item>;
    type Item: Send;

    fn into_par_iter(self) -> Self::Iter;
}

pub trait IntoParallelRefIterator<'r> {
    type Iter: ParallelIterator<Item=&'r Self::Item>;
    type Item: Sync + 'r;

    fn par_iter(&'r self) -> Self::Iter;
}

pub trait IntoParallelRefMutIterator<'r> {
    type Iter: ParallelIterator<Item=&'r mut Self::Item>;
    type Item: Send + 'r;

    fn par_iter_mut(&'r mut self) -> Self::Iter;
}

/// The `ParallelIterator` interface.
pub trait ParallelIterator {
    type Item: Send;
    type Shared: Sync;
    type State: ParallelIteratorState<Shared=Self::Shared, Item=Self::Item> + Send;

    fn state(self) -> (Self::Shared, Self::State);

    /// Indicates the relative "weight" of producing each item in this
    /// parallel iterator. A higher weight will cause finer-grained
    /// parallel subtasks. 1.0 indicates something very cheap and
    /// uniform, like copying a value out of an array, or computing `x
    /// + 1`. If your tasks are either very expensive, or very
    /// unpredictable, you are better off with higher values. See also
    /// `weight_max`, which is a convenient shorthand to force the
    /// finest grained parallel execution posible. Tuning this value
    /// should not affect correctness but can improve (or hurt)
    /// performance.
    fn weight(self, scale: f64) -> Weight<Self>
        where Self: Sized
    {
        Weight::new(self, scale)
    }

    /// Shorthand for `self.weight(f64::INFINITY)`. This forces the
    /// smallest granularity of parallel execution, which makes sense
    /// when your parallel tasks are (potentially) very expensive to
    /// execute.
    fn weight_max(self) -> Weight<Self>
        where Self: Sized
    {
        self.weight(f64::INFINITY)
    }

    /// Yields an index along with each item.
    fn enumerate(self) -> Enumerate<Self>
        where Self: Sized
    {
        Enumerate::new(self)
    }

    /// Executes `OP` on each item produced by the iterator, in parallel.
    fn for_each<OP>(self, op: OP)
        where OP: Fn(Self::Item) + Sync, Self: Sized
    {
        for_each::for_each(self, &op)
    }

    /// Applies `map_op` to each item of his iterator, producing a new
    /// iterator with the results.
    fn map<MAP_OP,R>(self, map_op: MAP_OP) -> Map<Self, MAP_OP>
        where MAP_OP: Fn(Self::Item) -> R, Self: Sized
    {
        Map::new(self, map_op)
    }

    /// Collects the results of the iterator into the specified
    /// vector. The vector is always truncated before execution
    /// begins. If possible, reusing the vector across calls can lead
    /// to better performance since it reuses the same backing buffer.
    fn collect_into(self, target: &mut Vec<Self::Item>)
        where Self: Sized
    {
        collect_into(self, target);
    }

    /// Reduces the items in the iterator into one item using `op`.
    /// See also `sum`, `mul`, `min`, etc, which are slightly more
    /// efficient. Returns `None` if the iterator is empty.
    ///
    /// Note: unlike in a sequential iterator, the order in which `op`
    /// will be applied to reduce the result is not specified. So `op`
    /// should be commutative and associative or else the results will
    /// be non-deterministic.
    fn reduce_with<OP>(self, op: OP) -> Option<Self::Item>
        where Self: Sized, OP: Fn(Self::Item, Self::Item) -> Self::Item + Sync,
    {
        reduce(self.map(Some), &ReduceWithOp::new(op))
    }

    /// Sums up the items in the iterator.
    ///
    /// Note that the order in items will be reduced is not specified,
    /// so if the `+` operator is not truly commutative and
    /// associative (as is the case for floating point numbers), then
    /// the results are not fully deterministic.
    fn sum(self) -> Self::Item
        where Self: Sized, SumOp: ReduceOp<Self::Item>
    {
        reduce(self, SUM)
    }

    /// Multiplies all the items in the iterator.
    ///
    /// Note that the order in items will be reduced is not specified,
    /// so if the `*` operator is not truly commutative and
    /// associative (as is the case for floating point numbers), then
    /// the results are not fully deterministic.
    fn mul(self) -> Self::Item
        where Self: Sized, MulOp: ReduceOp<Self::Item>
    {
        reduce(self, MUL)
    }

    /// Computes the minimum of all the items in the iterator.
    ///
    /// Note that the order in items will be reduced is not specified,
    /// so if the `Ord` impl is not truly commutative and associative
    /// (as is the case for floating point numbers), then the results
    /// are not deterministic.
    fn min(self) -> Self::Item
        where Self: Sized, MinOp: ReduceOp<Self::Item>
    {
        reduce(self, MIN)
    }

    /// Computes the maximum of all the items in the iterator.
    ///
    /// Note that the order in items will be reduced is not specified,
    /// so if the `Ord` impl is not truly commutative and associative
    /// (as is the case for floating point numbers), then the results
    /// are not deterministic.
    fn max(self) -> Self::Item
        where Self: Sized, MaxOp: ReduceOp<Self::Item>
    {
        reduce(self, MAX)
    }

    /// Reduces the items using the given "reduce operator". You may
    /// prefer `reduce_with` for a simpler interface.
    ///
    /// Note that the order in items will be reduced is not specified,
    /// so if the `reduce_op` impl is not truly commutative and
    /// associative, then the results are not deterministic.
    fn reduce<REDUCE_OP>(self, reduce_op: &REDUCE_OP) -> Self::Item
        where Self: Sized, REDUCE_OP: ReduceOp<Self::Item>
    {
        reduce(self, reduce_op)
    }
}

<|endoftext|>


<commit_before>use std::rc::Rc;
use std::cell::RefCell;
use std::num::abs;

use game::entity;
use game::entity::Entity;
use game::entity::PhysicalProperties;
use game::map::Map;
use game::sprite::{Sprite, Animation, AnimationPlayer};

use gmath::vectors::Vec2;
use gmath::shapes::Rect;

use keyboard::KeyboardState;

use sdl2::keycode;
use sdl2::render::Renderer;
use sdl2::render::Texture;

pub struct Player {
    accel      : Vec2<f32>,
    vel        : Vec2<f32>,
    pos        : Vec2<f32>,
    base_bounds: Rect,
    base_hitbox: Rect,
    keyboard   : Rc<RefCell<KeyboardState>>,
    on_ground  : bool,
    properties : PhysicalProperties,
    animations : EntityAnimations,
    animation_player: AnimationPlayer
}

pub struct EntityAnimations {
    idle: Animation,
    walk: Animation,
    jump: Animation,
    fall: Animation,
}

impl Entity for Player {
    fn acceleration(&self) -> Vec2<f32> { self.accel }
    fn set_acceleration(&mut self, new_accel: Vec2<f32>) { self.accel = new_accel; }
    fn velocity(&self) -> Vec2<f32> { self.vel }
    fn set_velocity(&mut self, new_vel: Vec2<f32>) { self.vel = new_vel; }
    fn position(&self) -> Vec2<f32> { self.pos }
    fn set_position(&mut self, new_pos: Vec2<f32>) { self.pos = new_pos }
    fn physical_properties<'a>(&'a self) -> &'a PhysicalProperties { &self.properties }
    fn bounds(&self) -> Rect { self.base_bounds.move_vec(self.pos) }
    fn is_on_ground(&self) -> bool { self.on_ground }
    fn hit_y(&mut self, value: bool) { self.on_ground = value }
}

impl Player {
    pub fn rounded_position(&self) -> Vec2<i32> {
        Vec2::new(self.pos.x as i32, self.pos.y as i32)
    }

    pub fn new(position: Vec2<f32>, spritesheet: Rc<~Texture>,
            keyboard: Rc<RefCell<KeyboardState>>) -> Player {
        let stand_sprite = Sprite {
            spritesheet: spritesheet.clone(),
            offset: Vec2::new(0, 0),
            frame_width: 64,
            frame_height: 128,
            num_frames_x: 1,
            num_frames_y: 1,
        };
        let stand_animation = Animation { sprite: stand_sprite, repeat: true, frame_time: 0.0 };

        let walk_sprite = Sprite {
            spritesheet: spritesheet.clone(),
            offset: Vec2::new(1*64, 0*128),
            frame_width: 64,
            frame_height: 128,
            num_frames_x: 6,
            num_frames_y: 1,
        };
        let walk_animation = Animation { sprite: walk_sprite, repeat: true, frame_time: 0.7 };

        let jump_sprite = Sprite {
            spritesheet: spritesheet.clone(),
            offset: Vec2::new(7*64, 1*128),
            frame_width: 64,
            frame_height: 128,
            num_frames_x: 1,
            num_frames_y: 1,
        };
        let jump_animation = Animation { sprite: jump_sprite, repeat: true, frame_time: 0.6 };

        let falling_sprite = Sprite {
            spritesheet: spritesheet.clone(),
            offset: Vec2::new(8*64, 1*128),
            frame_width: 64,
            frame_height: 128,
            num_frames_x: 2,
            num_frames_y: 1,
        };
        let falling_animation = Animation { sprite: falling_sprite, repeat: true, frame_time: 0.5 };

        Player {
            accel: Vec2::new(0.0, 9.8),
            vel: Vec2::zero(),
            pos: position,
            base_bounds: Rect::new(14.0, 36.0, 32.0, 92.0),
            base_hitbox: Rect::new(0.0, 0.0, 32.0, 32.0),
            keyboard: keyboard,
            on_ground: false,
            properties: PhysicalProperties {
                c_drag        : 0.470,
                mass          : 70.00, // (kg)
                acting_area   : 0.760, // (m^2)
                movement_accel: 3.000,
                max_velocity  : 6.000, // (m/s)
                jump_accel    : 8.000, // (m/s)
                jump_time     : 0.000, // (secs)
                stopping_bonus: 3.000,
            },
            animations: EntityAnimations {
                idle: stand_animation.clone(),
                walk: walk_animation,
                jump: jump_animation,
                fall: falling_animation
            },
            animation_player: AnimationPlayer::new(stand_animation.clone()),
        }
    }

    pub fn update(&mut self, map: &Map, secs: f32) {
        self.handle_input();
        entity::physics(self, map, secs);

        if abs(self.acceleration().x) != 0.0 {
            let flip = self.acceleration().x < 0.0;
            self.animation_player.flip_horizontal(flip);
        }

        // If the entity is on the ground, then it must be standing or walking
        if self.on_ground {
            if self.acceleration().x == 0.0 {
                self.animation_player.play(self.animations.idle.clone());
            }
            else if self.velocity().x != 0.0 {
                self.animation_player.play(self.animations.walk.clone());
                self.animation_player.speed_up = 1.0 / abs(self.velocity().x);
            }
        }
        // The entity is in the air, so it must be jumping or falling
        else {
            if self.velocity().y > 0.0 {
                self.animation_player.play(self.animations.fall.clone());
            }
        }

        self.animation_player.update(secs);
    }

    fn handle_input(&mut self) {
        let keyboard = self.keyboard.borrow().borrow();

        self.accel.x =
            if keyboard.get().is_keydown(keycode::LeftKey) {
                -self.properties.movement_accel * if self.on_ground { 1.0 } else { 0.6 }
            }
            else if keyboard.get().is_keydown(keycode::RightKey) {
                self.properties.movement_accel * if self.on_ground { 1.0 } else { 0.6 }
            }
            else {
                0.0
            };

        if keyboard.get().is_new_keypress(keycode::UpKey) {
            self.vel = self.vel + Vec2::new(0.0, -self.properties.jump_accel);
            self.animation_player.play(self.animations.jump.clone());
            self.animation_player.reset();
        }
    }

    pub fn draw(&self, camera: Vec2<i32>, renderer: &Renderer) {
        let pos = Vec2::new(self.pos.x as i32, self.pos.y as i32) - camera;
        self.animation_player.draw(pos, renderer);
    }
}
<commit_msg>Fix some player issues<commit_after>use std::rc::Rc;
use std::cell::RefCell;
use std::num::abs;

use game::entity;
use game::entity::Entity;
use game::entity::PhysicalProperties;
use game::map::Map;
use game::sprite::{Sprite, Animation, AnimationPlayer};

use gmath::vectors::Vec2;
use gmath::shapes::Rect;

use keyboard::KeyboardState;

use sdl2::keycode;
use sdl2::render::Renderer;
use sdl2::render::Texture;

pub struct Player {
    accel      : Vec2<f32>,
    vel        : Vec2<f32>,
    pos        : Vec2<f32>,
    base_bounds: Rect,
    base_hitbox: Rect,
    keyboard   : Rc<RefCell<KeyboardState>>,
    on_ground  : bool,
    properties : PhysicalProperties,
    animations : EntityAnimations,
    animation_player: AnimationPlayer
}

pub struct EntityAnimations {
    idle: Animation,
    walk: Animation,
    jump: Animation,
    fall: Animation,
}

impl Entity for Player {
    fn acceleration(&self) -> Vec2<f32> { self.accel }
    fn set_acceleration(&mut self, new_accel: Vec2<f32>) { self.accel = new_accel; }
    fn velocity(&self) -> Vec2<f32> { self.vel }
    fn set_velocity(&mut self, new_vel: Vec2<f32>) { self.vel = new_vel; }
    fn position(&self) -> Vec2<f32> { self.pos }
    fn set_position(&mut self, new_pos: Vec2<f32>) { self.pos = new_pos }
    fn physical_properties<'a>(&'a self) -> &'a PhysicalProperties { &self.properties }
    fn bounds(&self) -> Rect { self.base_bounds.move_vec(self.pos) }
    fn is_on_ground(&self) -> bool { self.on_ground }
    fn hit_y(&mut self, value: bool) { self.on_ground = value }
}

impl Player {
    pub fn rounded_position(&self) -> Vec2<i32> {
        Vec2::new(self.pos.x as i32, self.pos.y as i32)
    }

    pub fn center(&self) -> Vec2<i32> {
        let actual_center = self.bounds().center();
        Vec2::new(actual_center.x as i32, actual_center.y as i32)
    }

    pub fn new(position: Vec2<f32>, spritesheet: Rc<~Texture>,
            keyboard: Rc<RefCell<KeyboardState>>) -> Player {
        let stand_sprite = Sprite {
            spritesheet: spritesheet.clone(),
            offset: Vec2::new(0, 0),
            frame_width: 64,
            frame_height: 128,
            num_frames_x: 1,
            num_frames_y: 1,
        };
        let stand_animation = Animation { sprite: stand_sprite, repeat: true, frame_time: 0.0 };

        let walk_sprite = Sprite {
            spritesheet: spritesheet.clone(),
            offset: Vec2::new(1*64, 0*128),
            frame_width: 64,
            frame_height: 128,
            num_frames_x: 6,
            num_frames_y: 1,
        };
        let walk_animation = Animation { sprite: walk_sprite, repeat: true, frame_time: 0.7 };

        let jump_sprite = Sprite {
            spritesheet: spritesheet.clone(),
            offset: Vec2::new(7*64, 1*128),
            frame_width: 64,
            frame_height: 128,
            num_frames_x: 1,
            num_frames_y: 1,
        };
        let jump_animation = Animation { sprite: jump_sprite, repeat: true, frame_time: 0.6 };

        let falling_sprite = Sprite {
            spritesheet: spritesheet.clone(),
            offset: Vec2::new(8*64, 1*128),
            frame_width: 64,
            frame_height: 128,
            num_frames_x: 2,
            num_frames_y: 1,
        };
        let falling_animation = Animation { sprite: falling_sprite, repeat: true, frame_time: 0.5 };

        Player {
            accel: Vec2::new(0.0, 9.8),
            vel: Vec2::zero(),
            pos: position,
            base_bounds: Rect::new(14.0, 36.0, 32.0, 92.0),
            base_hitbox: Rect::new(0.0, 0.0, 32.0, 32.0),
            keyboard: keyboard,
            on_ground: false,
            properties: PhysicalProperties {
                c_drag        : 0.470,
                mass          : 70.00, // (kg)
                acting_area   : 0.760, // (m^2)
                movement_accel: 6.000,
                max_velocity  : 7.000, // (m/s)
                jump_accel    : 5.000, // (m/s)
                jump_time     : 0.000, // (secs)
                stopping_bonus: 6.000,
            },
            animations: EntityAnimations {
                idle: stand_animation.clone(),
                walk: walk_animation,
                jump: jump_animation,
                fall: falling_animation
            },
            animation_player: AnimationPlayer::new(stand_animation.clone()),
        }
    }

    pub fn update(&mut self, map: &Map, secs: f32) {
        self.handle_input();
        entity::physics(self, map, secs);

        if abs(self.acceleration().x) != 0.0 {
            let flip = self.acceleration().x < 0.0;
            self.animation_player.flip_horizontal(flip);
        }

        // If the entity is on the ground, then it must be standing or walking
        if self.on_ground {
            if self.acceleration().x == 0.0 && self.velocity().x == 0.0 {
                self.animation_player.play(self.animations.idle.clone());
            }
            else {
                self.animation_player.play(self.animations.walk.clone());
                if self.velocity().x != 0.0 {
                    self.animation_player.speed_up = 1.0 / abs(self.velocity().x);
                }
                else {
                    self.animation_player.speed_up = 1.0;
                }

            }
        }
        // The entity is in the air, so it must be jumping or falling
        else {
            if self.velocity().y > 0.0 {
                self.animation_player.play(self.animations.fall.clone());
            }
        }

        self.animation_player.update(secs);
    }

    fn handle_input(&mut self) {
        let keyboard = self.keyboard.borrow().borrow();

        self.accel.x =
            if keyboard.get().is_keydown(keycode::LeftKey) {
                -self.properties.movement_accel * if self.on_ground { 1.0 } else { 0.6 }
            }
            else if keyboard.get().is_keydown(keycode::RightKey) {
                self.properties.movement_accel * if self.on_ground { 1.0 } else { 0.6 }
            }
            else {
                0.0
            };

        if self.on_ground && keyboard.get().is_new_keypress(keycode::UpKey) {
            self.vel = self.vel + Vec2::new(0.0, -self.properties.jump_accel);
            self.animation_player.play(self.animations.jump.clone());
            self.animation_player.reset();
        }
    }

    pub fn draw(&self, camera: Vec2<i32>, renderer: &Renderer) {
        let pos = Vec2::new(self.pos.x as i32, self.pos.y as i32) - camera;
        self.animation_player.draw(pos, renderer);
    }

    #[cfg(debug)]
    pub fn draw_bounding_rect(&self, camera: Vec2<i32>, renderer: &Renderer)  {
        renderer.draw_rect(sdl::rect::Rect::new(
                self.bounds().x as i32 - camera.x, self.bounds().y as i32 - camera.y,
                self.bounds().width as i32, self.bounds().height() as i32));
    }
}
<|endoftext|>


<commit_before>use frame::ChannelAssignment;

fn decode_left_side(buffer: &mut [i32]) {
  let block_size = buffer.len() / 2;

  for i in 0..block_size {
    let left = buffer[i];
    let side = buffer[i + block_size];

    // right channel
    buffer[i + block_size] = left - side;
  }
}

fn decode_right_side(buffer: &mut [i32]) {
  let block_size = buffer.len() / 2;

  for i in 0..block_size {
    let side  = buffer[i];
    let right = buffer[i + block_size];

    // left channel
    buffer[i] = side + right;
  }
}

fn decode_middle_side(buffer: &mut [i32]) {
  let block_size = buffer.len() / 2;

  for i in 0..block_size {
    let mut middle = buffer[i];
    let side       = buffer[i + block_size];

    middle = (middle << 1) | (side & 1);

    // left and right channel
    buffer[i]              = (middle + side) >> 1;
    buffer[i + block_size] = (middle - side) >> 1;
  }
}

pub fn decode(channel_assignment: ChannelAssignment, buffer: &mut [i32]) {
  match channel_assignment {
    ChannelAssignment::Independent => return,
    ChannelAssignment::LeftSide    => decode_left_side(buffer),
    ChannelAssignment::RightSide   => decode_right_side(buffer),
    ChannelAssignment::MiddleSide  => unimplemented!(),
  }
}
<commit_msg>Add decode_middle_side to `frame::decode<commit_after>use frame::ChannelAssignment;

fn decode_left_side(buffer: &mut [i32]) {
  let block_size = buffer.len() / 2;

  for i in 0..block_size {
    let left = buffer[i];
    let side = buffer[i + block_size];

    // right channel
    buffer[i + block_size] = left - side;
  }
}

fn decode_right_side(buffer: &mut [i32]) {
  let block_size = buffer.len() / 2;

  for i in 0..block_size {
    let side  = buffer[i];
    let right = buffer[i + block_size];

    // left channel
    buffer[i] = side + right;
  }
}

fn decode_middle_side(buffer: &mut [i32]) {
  let block_size = buffer.len() / 2;

  for i in 0..block_size {
    let mut middle = buffer[i];
    let side       = buffer[i + block_size];

    middle = (middle << 1) | (side & 1);

    // left and right channel
    buffer[i]              = (middle + side) >> 1;
    buffer[i + block_size] = (middle - side) >> 1;
  }
}

pub fn decode(channel_assignment: ChannelAssignment, buffer: &mut [i32]) {
  match channel_assignment {
    ChannelAssignment::Independent => return,
    ChannelAssignment::LeftSide    => decode_left_side(buffer),
    ChannelAssignment::RightSide   => decode_right_side(buffer),
    ChannelAssignment::MiddleSide  => decode_middle_side(buffer),
  }
}
<|endoftext|>


<commit_before>use time;
use std::io::timer::sleep;

use GameWindow;
use keyboard;
use mouse;
use event;

use std::cmp;

/// Render argument.
#[deriving(Clone)]
pub struct RenderArgs {
    /// Extrapolated time in seconds, used to do smooth animation.
    pub ext_dt: f64,
    /// The width of rendered area.
    pub width: u32,
    /// The height of rendered area.
    pub height: u32,
}

/// Update argument.
#[deriving(Clone)]
pub struct UpdateArgs {
    /// Delta time in seconds.
    pub dt: f64,
}

/// Key press arguments.
#[deriving(Clone)]
pub struct KeyPressArgs {
    /// Keyboard key.
    pub key: keyboard::Key,
}

/// Key release arguments.
#[deriving(Clone)]
pub struct KeyReleaseArgs {
    /// Keyboard key.
    pub key: keyboard::Key,
}

/// Mouse press arguments.
#[deriving(Clone)]
pub struct MousePressArgs {
    /// Mouse button.
    pub button: mouse::Button,
}

/// Mouse release arguments.
#[deriving(Clone)]
pub struct MouseReleaseArgs {
    /// Mouse button.
    pub button: mouse::Button,
}

/// Mouse move arguments.
#[deriving(Clone)]
pub struct MouseMoveArgs {
    /// x.
    pub x: f64,
    /// y.
    pub y: f64,
}

/// Mouse relative move arguments.
#[deriving(Clone)]
pub struct MouseRelativeMoveArgs {
    /// Delta x.
    pub dx: f64,
    /// Delta y.
    pub dy: f64,
}

/// Mouse scroll arguments.
#[deriving(Clone)]
pub struct MouseScrollArgs {
    /// x.
    pub x: f64,
    /// y.
    pub y: f64,
}

/// Contains the different game events.
#[deriving(Clone)]
pub enum GameEvent {
    /// Render graphics.
    Render(RenderArgs),
    /// Update physical state of the game.
    Update(UpdateArgs),
    /// Pressed a keyboard key.
    KeyPress(KeyPressArgs),
    /// Released a keyboard key.
    KeyRelease(KeyReleaseArgs),
    /// Pressed a mouse button.
    MousePress(MousePressArgs),
    /// Released a mouse button.
    MouseRelease(MouseReleaseArgs),
    /// Moved mouse cursor.
    MouseMove(MouseMoveArgs),
    /// Moved mouse relative, not bounded by cursor.
    MouseRelativeMove(MouseRelativeMoveArgs),
    /// Scrolled mouse.
    MouseScroll(MouseScrollArgs)
}

#[deriving(Show)]
enum GameIteratorState {
    RenderState,
    SwapBuffersState,
    UpdateLoopState,
    HandleEventsState,
    MouseRelativeMoveState(f64, f64),
    UpdateState,
}

/// Settings for the game loop behavior.
#[deriving(Clone)]
pub struct GameIteratorSettings {
    /// The number of updates per second (UPS).
    pub updates_per_second: u64,
    /// The maximum number of frames per second (FPS target).
    pub max_frames_per_second: u64,
}

/// A game loop iterator.
///
/// Example:
///
/// ```Rust
/// let game_iter_settings = GameIteratorSettings {
///     updates_per_second: 120,
///     max_frames_per_second: 60,
/// };
/// let mut game_iter = GameIterator::new(&mut window, &game_iter_settings);
/// loop {
///     match game_iter.next() {
///         None => break,
///         Some(mut e) => match e {
///             Render(ref mut args) => {
///                 // Create graphics context with absolute coordinates.
///                 let c = Context::abs(args.width as f64, args.height as f64);
///                 // Do rendering here.
///             },
///             _ => {},
///         },
///     }
/// }
/// ```
pub struct GameIterator<'a, W> {
    /// The game window used by iterator.
    pub game_window: &'a mut W,
    state: GameIteratorState,
    last_update: u64,
    last_frame: u64,
    dt_update_in_ns: u64,
    dt_frame_in_ns: u64,
    dt: f64,
}

static billion: u64 = 1_000_000_000;

impl<'a, W: GameWindow> GameIterator<'a, W> {
    /// Creates a new game iterator.
    pub fn new(
        game_window: &'a mut W,
        settings: &GameIteratorSettings
    ) -> GameIterator<'a, W> {
        let updates_per_second: u64 = settings.updates_per_second;
        let max_frames_per_second: u64 = settings.max_frames_per_second;

        let start = time::precise_time_ns();
        GameIterator {
            game_window: game_window,
            state: RenderState,
            last_update: start,
            last_frame: start,
            dt_update_in_ns: billion / updates_per_second,
            dt_frame_in_ns: billion / max_frames_per_second,
            dt: 1.0 / updates_per_second as f64,
        }
    }
}

impl<'a, W: GameWindow>
Iterator<GameEvent>
for GameIterator<'a, W> {
    /// Returns the next game event.
    fn next(&mut self) -> Option<GameEvent> {
        loop {
            match self.state {
                RenderState => {
                    if self.game_window.should_close() { return None; }

                    self.last_frame += self.dt_frame_in_ns;

                    let start_render = time::precise_time_ns();
                    // Rendering code
                    let (w, h) = self.game_window.get_size();
                    if w != 0 && h != 0 {
                        // Swap buffers next time.
                        self.state = SwapBuffersState;
                        return Some(Render(RenderArgs {
                                // Extrapolate time forward to allow smooth motion.
                                ext_dt: (start_render - self.last_frame) as f64 / billion as f64,
                                width: w,
                                height: h,
                            }
                        ));
                    }

                    self.state = UpdateLoopState;
                },
                SwapBuffersState => {
                    self.game_window.swap_buffers();
                    self.state = UpdateLoopState;
                },
                UpdateLoopState => {
                    let current_time = time::precise_time_ns();
                    let next_frame = self.last_frame + self.dt_frame_in_ns;
                    let next_update = self.last_update + self.dt_update_in_ns;
                    let next_event = cmp::min(next_frame, next_update);
                    if next_event > current_time {
                        sleep( (next_event - current_time) / 1_000_000 );
                    } else if next_event == next_frame {
                        self.state = RenderState;
                    } else {
                        self.state = HandleEventsState;
                    }
                },
                HandleEventsState => {
                    // Handle all events before updating.
                    return match self.game_window.poll_event() {
                        event::KeyPressed(key) => {
                            Some(KeyPress(KeyPressArgs {
                                key: key,
                            }))
                        },
                        event::KeyReleased(key) => {
                            Some(KeyRelease(KeyReleaseArgs {
                                key: key,
                            }))
                        },
                        event::MouseButtonPressed(mouse_button) => {
                            Some(MousePress(MousePressArgs {
                                button: mouse_button,
                            }))
                        },
                        event::MouseButtonReleased(mouse_button) => {
                            Some(MouseRelease(MouseReleaseArgs {
                                button: mouse_button,
                            }))
                        },
                        event::MouseMoved(x, y, relative_move) => {
                            match relative_move {
                                Some((dx, dy)) =>
                                    self.state = MouseRelativeMoveState(dx, dy),
                                None => {},
                            };
                            Some(MouseMove(MouseMoveArgs {
                                x: x,
                                y: y,
                            }))
                        },
                        event::MouseScrolled(x, y) => {
                            Some(MouseScroll(MouseScrollArgs {
                                x: x,
                                y: y
                            }))
                        },
                        event::NoEvent => {
                            self.state = UpdateState;
                            continue;
                        },
                    }
                },
                MouseRelativeMoveState(dx, dy) => {
                    self.state = HandleEventsState;
                    return Some(MouseRelativeMove(MouseRelativeMoveArgs {
                        dx: dx,
                        dy: dy,
                    }));
                },
                UpdateState => {
                    self.state = UpdateLoopState;
                    self.last_update += self.dt_update_in_ns;
                    return Some(Update(UpdateArgs{
                        dt: self.dt,
                    }));
                },
            };
        }
    }
}
<commit_msg>Fixed edge cases in rendering<commit_after>use time;
use std::io::timer::sleep;

use GameWindow;
use keyboard;
use mouse;
use event;

use std::cmp;

/// Render argument.
#[deriving(Clone)]
pub struct RenderArgs {
    /// Extrapolated time in seconds, used to do smooth animation.
    pub ext_dt: f64,
    /// The width of rendered area.
    pub width: u32,
    /// The height of rendered area.
    pub height: u32,
}

/// Update argument.
#[deriving(Clone)]
pub struct UpdateArgs {
    /// Delta time in seconds.
    pub dt: f64,
}

/// Key press arguments.
#[deriving(Clone)]
pub struct KeyPressArgs {
    /// Keyboard key.
    pub key: keyboard::Key,
}

/// Key release arguments.
#[deriving(Clone)]
pub struct KeyReleaseArgs {
    /// Keyboard key.
    pub key: keyboard::Key,
}

/// Mouse press arguments.
#[deriving(Clone)]
pub struct MousePressArgs {
    /// Mouse button.
    pub button: mouse::Button,
}

/// Mouse release arguments.
#[deriving(Clone)]
pub struct MouseReleaseArgs {
    /// Mouse button.
    pub button: mouse::Button,
}

/// Mouse move arguments.
#[deriving(Clone)]
pub struct MouseMoveArgs {
    /// x.
    pub x: f64,
    /// y.
    pub y: f64,
}

/// Mouse relative move arguments.
#[deriving(Clone)]
pub struct MouseRelativeMoveArgs {
    /// Delta x.
    pub dx: f64,
    /// Delta y.
    pub dy: f64,
}

/// Mouse scroll arguments.
#[deriving(Clone)]
pub struct MouseScrollArgs {
    /// x.
    pub x: f64,
    /// y.
    pub y: f64,
}

/// Contains the different game events.
#[deriving(Clone)]
pub enum GameEvent {
    /// Render graphics.
    Render(RenderArgs),
    /// Update physical state of the game.
    Update(UpdateArgs),
    /// Pressed a keyboard key.
    KeyPress(KeyPressArgs),
    /// Released a keyboard key.
    KeyRelease(KeyReleaseArgs),
    /// Pressed a mouse button.
    MousePress(MousePressArgs),
    /// Released a mouse button.
    MouseRelease(MouseReleaseArgs),
    /// Moved mouse cursor.
    MouseMove(MouseMoveArgs),
    /// Moved mouse relative, not bounded by cursor.
    MouseRelativeMove(MouseRelativeMoveArgs),
    /// Scrolled mouse.
    MouseScroll(MouseScrollArgs)
}

#[deriving(Show)]
enum GameIteratorState {
    RenderState,
    SwapBuffersState,
    UpdateLoopState,
    HandleEventsState,
    MouseRelativeMoveState(f64, f64),
    UpdateState,
}

/// Settings for the game loop behavior.
#[deriving(Clone)]
pub struct GameIteratorSettings {
    /// The number of updates per second (UPS).
    pub updates_per_second: u64,
    /// The maximum number of frames per second (FPS target).
    pub max_frames_per_second: u64,
}

/// A game loop iterator.
///
/// Example:
///
/// ```Rust
/// let game_iter_settings = GameIteratorSettings {
///     updates_per_second: 120,
///     max_frames_per_second: 60,
/// };
/// let mut game_iter = GameIterator::new(&mut window, &game_iter_settings);
/// loop {
///     match game_iter.next() {
///         None => break,
///         Some(mut e) => match e {
///             Render(ref mut args) => {
///                 // Create graphics context with absolute coordinates.
///                 let c = Context::abs(args.width as f64, args.height as f64);
///                 // Do rendering here.
///             },
///             _ => {},
///         },
///     }
/// }
/// ```
pub struct GameIterator<'a, W> {
    /// The game window used by iterator.
    pub game_window: &'a mut W,
    state: GameIteratorState,
    last_update: u64,
    last_frame: u64,
    dt_update_in_ns: u64,
    dt_frame_in_ns: u64,
    dt: f64,
}

static billion: u64 = 1_000_000_000;

impl<'a, W: GameWindow> GameIterator<'a, W> {
    /// Creates a new game iterator.
    pub fn new(
        game_window: &'a mut W,
        settings: &GameIteratorSettings
    ) -> GameIterator<'a, W> {
        let updates_per_second: u64 = settings.updates_per_second;
        let max_frames_per_second: u64 = settings.max_frames_per_second;

        let start = time::precise_time_ns();
        GameIterator {
            game_window: game_window,
            state: RenderState,
            last_update: start,
            last_frame: start,
            dt_update_in_ns: billion / updates_per_second,
            dt_frame_in_ns: billion / max_frames_per_second,
            dt: 1.0 / updates_per_second as f64,
        }
    }
}

impl<'a, W: GameWindow>
Iterator<GameEvent>
for GameIterator<'a, W> {
    /// Returns the next game event.
    fn next(&mut self) -> Option<GameEvent> {
        loop {
            match self.state {
                RenderState => {
                    if self.game_window.should_close() { return None; }

                    let start_render = time::precise_time_ns();
                    let jump_frames = cmp::max(1, (start_render - self.last_frame) / self.dt_frame_in_ns);
                    self.last_frame += self.dt_frame_in_ns * jump_frames;

                    let (w, h) = self.game_window.get_size();
                    if w != 0 && h != 0 {
                        // Swap buffers next time.
                        self.state = SwapBuffersState;
                        return Some(Render(RenderArgs {
                                // Extrapolate time forward to allow smooth motion.
                                ext_dt: (start_render - self.last_update) as f64 / billion as f64,
                                width: w,
                                height: h,
                            }
                        ));
                    }

                    self.state = UpdateLoopState;
                },
                SwapBuffersState => {
                    self.game_window.swap_buffers();
                    self.state = UpdateLoopState;
                },
                UpdateLoopState => {
                    let current_time = time::precise_time_ns();
                    let next_frame = self.last_frame + self.dt_frame_in_ns;
                    let next_update = self.last_update + self.dt_update_in_ns;
                    let next_event = cmp::min(next_frame, next_update);
                    if next_event > current_time {
                        sleep( (next_event - current_time) / 1_000_000 );
                    } else if next_event == next_frame {
                        self.state = RenderState;
                    } else {
                        self.state = HandleEventsState;
                    }
                },
                HandleEventsState => {
                    // Handle all events before updating.
                    return match self.game_window.poll_event() {
                        event::KeyPressed(key) => {
                            Some(KeyPress(KeyPressArgs {
                                key: key,
                            }))
                        },
                        event::KeyReleased(key) => {
                            Some(KeyRelease(KeyReleaseArgs {
                                key: key,
                            }))
                        },
                        event::MouseButtonPressed(mouse_button) => {
                            Some(MousePress(MousePressArgs {
                                button: mouse_button,
                            }))
                        },
                        event::MouseButtonReleased(mouse_button) => {
                            Some(MouseRelease(MouseReleaseArgs {
                                button: mouse_button,
                            }))
                        },
                        event::MouseMoved(x, y, relative_move) => {
                            match relative_move {
                                Some((dx, dy)) =>
                                    self.state = MouseRelativeMoveState(dx, dy),
                                None => {},
                            };
                            Some(MouseMove(MouseMoveArgs {
                                x: x,
                                y: y,
                            }))
                        },
                        event::MouseScrolled(x, y) => {
                            Some(MouseScroll(MouseScrollArgs {
                                x: x,
                                y: y
                            }))
                        },
                        event::NoEvent => {
                            self.state = UpdateState;
                            continue;
                        },
                    }
                },
                MouseRelativeMoveState(dx, dy) => {
                    self.state = HandleEventsState;
                    return Some(MouseRelativeMove(MouseRelativeMoveArgs {
                        dx: dx,
                        dy: dy,
                    }));
                },
                UpdateState => {
                    self.state = UpdateLoopState;
                    self.last_update += self.dt_update_in_ns;
                    return Some(Update(UpdateArgs{
                        dt: self.dt,
                    }));
                },
            };
        }
    }
}
<|endoftext|>