<commit_before>// Copyright (c) 2013-2015 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

#[cfg(feature = "sync_reader")]
pub use sync::ReadLimiter;

#[cfg(feature = "sync_reader")]
mod sync {
    use crate::{Error, Result};
    use core::sync::atomic::{AtomicUsize, AtomicBool, Ordering};

    pub struct ReadLimiter {
        pub limit: AtomicUsize,
        pub limit_reached: AtomicBool,
    }

    impl ReadLimiter {
        pub fn new(limit: u64) -> ReadLimiter {
            if limit > core::usize::MAX as u64 {
                panic!("traversal_limit_in_words cannot be bigger than core::usize::MAX")
            }

            ReadLimiter {
                limit: AtomicUsize::new(limit as usize),
                limit_reached: AtomicBool::new(false),
            }
        }

        #[inline]
        pub fn can_read(&self, amount: usize) -> Result<()> {
            let limit_reached = self.limit_reached.load(Ordering::Relaxed);
            if limit_reached {
                return Err(Error::failed(format!("read limit exceeded")));
            }

            let prev_limit = self.limit.fetch_sub(amount, Ordering::Relaxed);
            if prev_limit == amount {
                self.limit_reached.store(true, Ordering::Relaxed);
            } else if prev_limit < amount {
                self.limit_reached.store(true, Ordering::Relaxed);
                return Err(Error::failed(format!("read limit exceeded")));
            }

            Ok(())
        }
    }
}

#[cfg(not(feature = "sync_reader"))]
pub use unsync::ReadLimiter;

#[cfg(not(feature = "sync_reader"))]
mod unsync {
    use crate::{Error, Result};
    use core::cell::Cell;

    pub struct ReadLimiter {
        pub limit: Cell<u64>,
    }

    impl ReadLimiter {
        pub fn new(limit: u64) -> ReadLimiter {
            ReadLimiter {
                limit: Cell::new(limit),
            }
        }

        #[inline]
        pub fn can_read(&self, amount: usize) -> Result<()> {
            let amount = amount as u64;
            let current = self.limit.get();
            if amount > current {
                Err(Error::failed(format!("read limit exceeded")))
            } else {
                self.limit.set(current - amount);
                Ok(())
            }
        }
    }
}
<commit_msg>Atomic read limiter: better logic<commit_after>// Copyright (c) 2013-2015 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

#[cfg(feature = "sync_reader")]
pub use sync::ReadLimiter;

#[cfg(feature = "sync_reader")]
mod sync {
    use crate::{Error, Result};
    use core::sync::atomic::{AtomicUsize, Ordering};

    pub struct ReadLimiter {
        pub limit: AtomicUsize,
    }

    impl ReadLimiter {
        pub fn new(limit: u64) -> ReadLimiter {
            if limit > core::usize::MAX as u64 {
                panic!("traversal_limit_in_words cannot be bigger than core::usize::MAX")
            }

            ReadLimiter {
                limit: AtomicUsize::new(limit as usize),
            }
        }

        #[inline]
        pub fn can_read(&self, amount: usize) -> Result<()> {
            let cur_limit = self.limit.load(Ordering::Relaxed);
            if cur_limit < amount {
                return Err(Error::failed(format!("read limit exceeded")));
            }

            let prev_limit = self.limit.fetch_sub(amount, Ordering::Relaxed);
            if prev_limit < amount {
                // if the previous limit was lower than the amount we read, the limit has underflowed
                // and wrapped around so we need to reset it to 0 for next reader to fail
                self.limit.store(0, Ordering::Relaxed);
                return Err(Error::failed(format!("read limit exceeded")));
            }

            Ok(())
        }
    }
}

#[cfg(not(feature = "sync_reader"))]
pub use unsync::ReadLimiter;

#[cfg(not(feature = "sync_reader"))]
mod unsync {
    use crate::{Error, Result};
    use core::cell::Cell;

    pub struct ReadLimiter {
        pub limit: Cell<u64>,
    }

    impl ReadLimiter {
        pub fn new(limit: u64) -> ReadLimiter {
            ReadLimiter {
                limit: Cell::new(limit),
            }
        }

        #[inline]
        pub fn can_read(&self, amount: usize) -> Result<()> {
            let amount = amount as u64;
            let current = self.limit.get();
            if amount > current {
                Err(Error::failed(format!("read limit exceeded")))
            } else {
                self.limit.set(current - amount);
                Ok(())
            }
        }
    }
}
<|endoftext|>


<commit_before>use super::*;
use std::collections::HashMap;
use termion::event as keyevent;
use nalgebra::{distance, Vector2};
use tui::{layout::{Direction, Group, Rect, Size}, style::{Color, Style},
          widgets::{Block, Borders, Paragraph, Row, SelectableList, Table, Widget, canvas::Canvas}};

use utils::Point;
use astronomicals::system::System;
use entities::Faction;
use player::Player;

lazy_static! {
    /// Color mapping for each faction.
    static ref FACTION_COLORS: HashMap<Faction, Color> = {
        let mut m = HashMap::new();
        m.insert(Faction::Empire, Color::Red);
        m.insert(Faction::Federation, Color::Yellow);
        m.insert(Faction::Cartel, Color::Magenta);
        m.insert(Faction::Independent, Color::LightGreen);
        m
    };

    /// Styling for unselected item.
    static ref DEFAULT_STYLE: Style = Style::default();
}

/// The minimum distance within which the gui will snap to the closest system.
const MIN_SNAP_DIST: f64 = 0.9;

/// Displays the map tab.
pub struct MapTab {
    state: Arc<Game>,
    sender: Sender<Event>,
    search_mode: bool,
    search_str: String,
    selected: Option<Point>,
    route: Option<(u32, Vec<Point>)>,
    cursor: Point,
    map_scale: f64,
}

impl MapTab {
    /// Attempts to find a route to the selected system.
    fn find_route(&mut self) {
        let galaxy = &self.state.galaxy.lock().unwrap();
        let player = &mut self.state.player.lock().unwrap();
        let range = match player.ship() {
            &Some(ref ship) => ship.range(),
            &None => 0.,
        };
        let max_jumps = match player.ship() {
            &Some(ref ship) => ship.fuel(),
            &None => 0,
        };
        // Plan route if possible.
        self.route = galaxy.route(
            &player.location(),
            &self.selected.unwrap(),
            range,
            max_jumps,
        );
    }

    /// Moves the player's location to the selected system.
    fn travel_to_selected(&mut self) {
        let player = &mut self.state.player.lock().unwrap();

        // Only travel if the selected system is the same as the cursor and
        // and the final destination for the route.
        if let Some((ref jumps, ref route)) = self.route {
            if self.selected.is_some() && self.selected.unwrap() == self.cursor
                && self.selected.unwrap() == *route.last().unwrap()
            {
                player.set_route(route.clone());
                self.sender.send(Event::Travel);
            }
        }

        // Reset route.
        self.route = None;
    }

    /// Draws the event box in the given terminal and area.
    pub fn draw_search(&self, term: &mut Terminal<MouseBackend>, area: &Rect) {
        let draw_str = match self.search_mode {
            true => self.search_str.as_str(),
            false => "Press '/' to search for a system",
        };
        Paragraph::default()
            .block(Block::default().borders(Borders::ALL))
            .style(Style::default().fg(Color::Yellow))
            .text(draw_str)
            .render(term, &area);
    }

    /// Draw system ship information for the selected system, if any.
    fn draw_system_info(
        &self,
        player_loc: &Point,
        selected_system: Option<&System>,
        term: &mut Terminal<MouseBackend>,
        area: &Rect,
    ) {
        // Do not draw anything if no system is selected.
        if selected_system.is_none() {
            return;
        }
        let system = selected_system.unwrap();

        let system_data = vec![
            format!("Faction:       {}", system.faction.to_string()),
            format!("State:         {}", system.state.to_string()),
            format!("Security:      {}", system.security.to_string()),
            format!("Reputation:    {}", system.reputation.to_string()),
            format!(
                "Distance:      {:.1} ly",
                distance(player_loc, &system.location)
            ),
            format!("Star mass:     {:.1} M", system.star.mass),
            format!("Star type:     {}", system.star.startype.to_string()),
            format!("Bodies:        {}", system.satelites.len()),
        ];

        Group::default()
            .direction(Direction::Vertical)
            .sizes(&[Size::Fixed(9), Size::Min(1)])
            .render(term, area, |term, chunks| {
                SelectableList::default()
                    .items(&system_data)
                    .block(Block::default().title(&format!("{} System", system.name)))
                    .style(Style::default().fg(Color::Yellow))
                    .render(term, &chunks[0]);
                Table::new(
                    // Prepending empty character to get alignment with list above.
                    [" Planet", "Mass", "Population", "Temperature", "Type"].into_iter(),
                    system.satelites.iter().map(|ref planet| {
                        let style: &Style = &DEFAULT_STYLE;
                        Row::StyledData(
                            vec![
                                format!(" {}", planet.name.clone()),
                                format!("{:.1}", planet.mass),
                                format!("{:.1} M", planet.population),
                                format!("{:.1}", planet.surface_temperature),
                                planet.planet_type.to_string(),
                            ].into_iter(),
                            &style,
                        )
                    }),
                ).block(Block::default().title("Planets"))
                    .header_style(Style::default().fg(Color::Yellow))
                    .widths(&[15, 5, 15, 15, 10])
                    .render(term, &chunks[1]);
            });
    }

    /// Draw the galaxy map.
    fn draw_galaxy_map(
        &self,
        player: &Player,
        systems: &Vec<&System>,
        term: &mut Terminal<MouseBackend>,
        area: &Rect,
    ) {
        let player_loc = player.location();
        // Scale map to not overlap systems.
        let map_scaling = 20. * self.map_scale;
        let (max_x, max_y) = systems.iter().fold((0., 0.), |(x_max, y_max), s| {
            (
                (s.location.coords.x / map_scaling).abs().max(x_max),
                (s.location.coords.y / map_scaling).abs().max(y_max),
            )
        });
        Canvas::default()
            .block(Block::default().title("Systems").borders(Borders::ALL))
            .paint(|ctx| {
                for system in systems.iter() {
                    let color = FACTION_COLORS.get(&system.faction).unwrap().clone();
                    ctx.print(
                        system.location.coords.x,
                        system.location.coords.y,
                        ".",
                        color,
                    );
                }
                // Draw player location.
                ctx.print(player_loc.coords.x, player_loc.coords.y, "X", Color::White);

                // Draw the cursor.
                ctx.print(
                    self.cursor.coords.x,
                    self.cursor.coords.y,
                    "*",
                    Color::Yellow,
                );

                // Draw route if available.
                if let Some((_, ref route)) = self.route {
                    for system in route {
                        ctx.print(system.coords.x, system.coords.y, "X", Color::Yellow);
                    }
                    ctx.print(player_loc.coords.x, player_loc.coords.y, "S", Color::Yellow);
                    ctx.print(
                        route.last().unwrap().coords.x,
                        route.last().unwrap().coords.y,
                        "G",
                        Color::Yellow,
                    );
                }
                // Draw currently travelling route if available.
                if let Some(ref route) = player.route() {
                    for system in route {
                        ctx.print(system.coords.x, system.coords.y, "X", Color::White);
                    }
                    ctx.print(player_loc.coords.x, player_loc.coords.y, "S", Color::White);
                    ctx.print(
                        route.last().unwrap().coords.x,
                        route.last().unwrap().coords.y,
                        "G",
                        Color::White,
                    );
                }
            })
            .x_bounds([self.cursor.coords.x - max_x, self.cursor.coords.x + max_x])
            .y_bounds([self.cursor.coords.y - max_y, self.cursor.coords.y + max_y])
            .render(term, &area);
    }
}

impl Tab for MapTab {
    /// Creates a map tab.
    fn new(state: Arc<Game>, send_handle: Sender<Event>) -> Box<Self> {
        let cursor = state.player.lock().unwrap().location().clone();
        Box::new(MapTab {
            state: state,
            sender: send_handle,
            selected: Some(cursor.clone()),
            search_mode: false,
            search_str: String::new(),
            route: None,
            cursor: cursor,
            map_scale: 1.,
        })
    }

    /// Returns the title string describing the tab.
    fn title(&self) -> String {
        String::from("Galaxy Map")
    }

    /// Handles the user provided event.
    fn handle_event(&mut self, event: Event) {
        match event {
            Event::Input(input) => {
                match input {
                    keyevent::Key::Char('\n') if self.search_mode => {
                        let galaxy = self.state.galaxy.lock().unwrap();

                        // Set cursor to the closest matching system if
                        // possible.
                        match galaxy.search_name(&self.search_str) {
                            Some(system) => self.cursor = system.location.clone(),
                            None => {}
                        };

                        // Clear input.
                        self.search_str.clear();
                        self.search_mode = false;
                    }
                    keyevent::Key::Char(e) if self.search_mode => {
                        self.search_str.push(e);
                        // Early exit.
                        return;
                    }
                    keyevent::Key::Backspace if self.search_mode => {
                        self.search_str.pop();
                    }
                    keyevent::Key::Char('\n') if self.selected.is_some() => {
                        match self.route {
                            Some(_) => self.travel_to_selected(),
                            None => self.find_route(),
                        };
                    }
                    // Start search mode.
                    keyevent::Key::Char('/') => {
                        self.search_mode = true;
                        return;
                    }
                    // Quit search mode.
                    keyevent::Key::Esc => {
                        self.search_str.clear();
                        self.search_mode = false;
                        return;
                    }
                    _ => {}
                };

                self.map_scale *= match input {
                    // Zoom out.
                    keyevent::Key::Char('u') => 0.5,
                    // Zoom in.
                    keyevent::Key::Char('i') => 2.,
                    // No zooming.
                    _ => 1.,
                };

                // Prevent zooming too far in.
                self.map_scale = self.map_scale.min(4.);

                let translation = match input {
                    // Move up.
                    keyevent::Key::Char('k') => Vector2::new(0., 1. / self.map_scale),
                    // Move down.
                    keyevent::Key::Char('j') => Vector2::new(0., -1. / self.map_scale),
                    // Move right.
                    keyevent::Key::Char('l') => Vector2::new(1. / self.map_scale, 0.),
                    // Move left.
                    keyevent::Key::Char('h') => Vector2::new(-1. / self.map_scale, 0.),
                    _ => Vector2::new(0., 0.),
                };

                // Move out of snapping system if currently snapped.
                self.cursor += match self.selected {
                    Some(_) => Vector2::new(
                        translation.x * 1.1 * self.map_scale * MIN_SNAP_DIST,
                        translation.y * 1.1 * self.map_scale * MIN_SNAP_DIST,
                    ),
                    None => translation,
                };
                self.selected = None;

                // Check if cursor should snap to closest system.
                if let Some(neighbor) = self.state.galaxy.lock().unwrap().nearest(&self.cursor) {
                    if distance(&self.cursor, &neighbor) < MIN_SNAP_DIST {
                        self.cursor = neighbor.clone();
                        self.selected = Some(neighbor.clone());
                    }
                }
            }
            _ => {}
        };
    }

    /// Draws the tab in the given terminal and area.
    fn draw(&self, term: &mut Terminal<MouseBackend>, area: &Rect) {
        let galaxy = self.state.galaxy.lock().unwrap();
        Group::default()
            .direction(Direction::Horizontal)
            .sizes(&[Size::Fixed(75), Size::Min(1)])
            .render(term, area, |term, chunks| {
                // TODO: Draw system detailed information.
                let systems = &galaxy.systems();
                let player = &self.state.player.lock().unwrap();
                //let player_loc = &self.state.player.lock().unwrap().location().clone();
                // Draw sidebar.
                Group::default()
                    .direction(Direction::Vertical)
                    .sizes(&[Size::Min(1), Size::Fixed(3)])
                    .render(term, &chunks[0], |term, sidebar_chunk| {
                        self.draw_system_info(
                            &player.location(),
                            self.selected.map(|point| galaxy.system(&point).unwrap()),
                            term,
                            &sidebar_chunk[0],
                        );
                        self.draw_search(term, &sidebar_chunk[1]);
                    });
                self.draw_galaxy_map(player, &systems, term, &chunks[1]);
            });
    }
}
<commit_msg>Improve user ergonomics in the map tab (#110)<commit_after>use super::*;
use std::collections::HashMap;
use termion::event as keyevent;
use nalgebra::{distance, Vector2};
use tui::{layout::{Direction, Group, Rect, Size}, style::{Color, Style},
          widgets::{Block, Borders, Paragraph, Row, SelectableList, Table, Widget, canvas::Canvas}};

use utils::Point;
use astronomicals::system::System;
use entities::Faction;
use player::Player;

lazy_static! {
    /// Color mapping for each faction.
    static ref FACTION_COLORS: HashMap<Faction, Color> = {
        let mut m = HashMap::new();
        m.insert(Faction::Empire, Color::Red);
        m.insert(Faction::Federation, Color::Yellow);
        m.insert(Faction::Cartel, Color::Magenta);
        m.insert(Faction::Independent, Color::LightGreen);
        m
    };

    /// Styling for unselected item.
    static ref DEFAULT_STYLE: Style = Style::default();
}

/// The minimum distance within which the gui will snap to the closest system.
const MIN_SNAP_DIST: f64 = 0.9;

/// Displays the map tab.
pub struct MapTab {
    state: Arc<Game>,
    sender: Sender<Event>,
    search_mode: bool,
    search_str: String,
    selected: Option<Point>,
    route: Option<(u32, Vec<Point>)>,
    cursor: Point,
    map_scale: f64,
}

impl MapTab {
    /// Attempts to find a route to the selected system.
    fn find_route(&mut self) {
        let galaxy = &self.state.galaxy.lock().unwrap();
        let player = &mut self.state.player.lock().unwrap();
        let range = match player.ship() {
            &Some(ref ship) => ship.range(),
            &None => 0.,
        };
        let max_jumps = match player.ship() {
            &Some(ref ship) => ship.fuel(),
            &None => 0,
        };
        // Plan route if possible.
        self.route = galaxy.route(
            &player.location(),
            &self.selected.unwrap(),
            range,
            max_jumps,
        );
    }

    /// Moves the player's location to the selected system.
    fn travel_to_selected(&mut self) {
        let player = &mut self.state.player.lock().unwrap();

        // Only travel if the selected system is the same as the cursor and
        // and the final destination for the route.
        if let Some((ref jumps, ref route)) = self.route {
            if self.selected.is_some() && self.selected.unwrap() == self.cursor
                && self.selected.unwrap() == *route.last().unwrap()
            {
                player.set_route(route.clone());
                self.sender.send(Event::Travel);
            }
        }

        // Reset route.
        self.route = None;
    }

    /// Draws the event box in the given terminal and area.
    pub fn draw_search(&self, term: &mut Terminal<MouseBackend>, area: &Rect) {
        let draw_str = match self.search_mode {
            true => format!("{}{}", self.search_str, "{mod=bold |}"),
            false => String::from("Press '/' to search for a system"),
        };
        Paragraph::default()
            .block(Block::default().borders(Borders::ALL))
            .style(Style::default().fg(Color::Yellow))
            .text(draw_str.as_str())
            .render(term, &area);
    }

    /// Draw system ship information for the selected system, if any.
    fn draw_system_info(
        &self,
        player_loc: &Point,
        selected_system: Option<&System>,
        term: &mut Terminal<MouseBackend>,
        area: &Rect,
    ) {
        // Do not draw anything if no system is selected.
        if selected_system.is_none() {
            return;
        }
        let system = selected_system.unwrap();

        let system_data = vec![
            format!("Faction:       {}", system.faction.to_string()),
            format!("State:         {}", system.state.to_string()),
            format!("Security:      {}", system.security.to_string()),
            format!("Reputation:    {}", system.reputation.to_string()),
            format!(
                "Distance:      {:.1} ly",
                distance(player_loc, &system.location)
            ),
            format!("Star mass:     {:.1} M", system.star.mass),
            format!("Star type:     {}", system.star.startype.to_string()),
            format!("Bodies:        {}", system.satelites.len()),
        ];

        Group::default()
            .direction(Direction::Vertical)
            .sizes(&[Size::Fixed(9), Size::Min(1)])
            .render(term, area, |term, chunks| {
                SelectableList::default()
                    .items(&system_data)
                    .block(Block::default().title(&format!("{} System", system.name)))
                    .style(Style::default().fg(Color::Yellow))
                    .render(term, &chunks[0]);
                Table::new(
                    // Prepending empty character to get alignment with list above.
                    [" Planet", "Mass", "Population", "Temperature", "Type"].into_iter(),
                    system.satelites.iter().map(|ref planet| {
                        let style: &Style = &DEFAULT_STYLE;
                        Row::StyledData(
                            vec![
                                format!(" {}", planet.name.clone()),
                                format!("{:.1}", planet.mass),
                                format!("{:.1} M", planet.population),
                                format!("{:.1}", planet.surface_temperature),
                                planet.planet_type.to_string(),
                            ].into_iter(),
                            &style,
                        )
                    }),
                ).block(Block::default().title("Planets"))
                    .header_style(Style::default().fg(Color::Yellow))
                    .widths(&[15, 5, 15, 15, 10])
                    .render(term, &chunks[1]);
            });
    }

    /// Draw the galaxy map.
    fn draw_galaxy_map(
        &self,
        player: &Player,
        systems: &Vec<&System>,
        term: &mut Terminal<MouseBackend>,
        area: &Rect,
    ) {
        let player_loc = player.location();
        // Scale map to not overlap systems.
        let map_scaling = 20. * self.map_scale;
        let (max_x, max_y) = systems.iter().fold((0., 0.), |(x_max, y_max), s| {
            (
                (s.location.coords.x / map_scaling).abs().max(x_max),
                (s.location.coords.y / map_scaling).abs().max(y_max),
            )
        });
        Canvas::default()
            .block(Block::default().title("Systems").borders(Borders::ALL))
            .paint(|ctx| {
                for system in systems.iter() {
                    let color = FACTION_COLORS.get(&system.faction).unwrap().clone();
                    ctx.print(
                        system.location.coords.x,
                        system.location.coords.y,
                        ".",
                        color,
                    );
                }
                // Draw player location.
                ctx.print(player_loc.coords.x, player_loc.coords.y, "X", Color::White);

                // Draw the cursor.
                ctx.print(
                    self.cursor.coords.x,
                    self.cursor.coords.y,
                    "*",
                    Color::Yellow,
                );

                // Draw route if available.
                if let Some((_, ref route)) = self.route {
                    for system in route {
                        ctx.print(system.coords.x, system.coords.y, "X", Color::Yellow);
                    }
                    ctx.print(player_loc.coords.x, player_loc.coords.y, "S", Color::Yellow);
                    ctx.print(
                        route.last().unwrap().coords.x,
                        route.last().unwrap().coords.y,
                        "G",
                        Color::Yellow,
                    );
                }
                // Draw currently travelling route if available.
                if let Some(ref route) = player.route() {
                    for system in route {
                        ctx.print(system.coords.x, system.coords.y, "X", Color::White);
                    }
                    ctx.print(player_loc.coords.x, player_loc.coords.y, "S", Color::White);
                    ctx.print(
                        route.last().unwrap().coords.x,
                        route.last().unwrap().coords.y,
                        "G",
                        Color::White,
                    );
                }
            })
            .x_bounds([self.cursor.coords.x - max_x, self.cursor.coords.x + max_x])
            .y_bounds([self.cursor.coords.y - max_y, self.cursor.coords.y + max_y])
            .render(term, &area);
    }
}

impl Tab for MapTab {
    /// Creates a map tab.
    fn new(state: Arc<Game>, send_handle: Sender<Event>) -> Box<Self> {
        let cursor = state.player.lock().unwrap().location().clone();
        Box::new(MapTab {
            state: state,
            sender: send_handle,
            selected: Some(cursor.clone()),
            search_mode: false,
            search_str: String::new(),
            route: None,
            cursor: cursor,
            map_scale: 1.,
        })
    }

    /// Returns the title string describing the tab.
    fn title(&self) -> String {
        String::from("Galaxy Map")
    }

    /// Handles the user provided event.
    fn handle_event(&mut self, event: Event) {
        match event {
            Event::Input(input) => {
                match input {
                    keyevent::Key::Char('\n') if self.search_mode => {
                        let galaxy = self.state.galaxy.lock().unwrap();

                        // Set cursor to the closest matching system if
                        // possible.
                        match galaxy.search_name(&self.search_str) {
                            Some(system) => self.cursor = system.location.clone(),
                            None => {}
                        };

                        // Clear input.
                        self.search_str.clear();
                        self.search_mode = false;
                    }
                    keyevent::Key::Char(e) if self.search_mode => {
                        self.search_str.push(e);
                        // Early exit.
                        return;
                    }
                    keyevent::Key::Backspace if self.search_mode => {
                        self.search_str.pop();
                    }
                    keyevent::Key::Char('\n') if self.selected.is_some() => {
                        match self.route {
                            Some(_) => self.travel_to_selected(),
                            None => self.find_route(),
                        };
                    }
                    // Center map around player
                    keyevent::Key::Char(' ') => {
                        if let Ok(player) = self.state.player.lock() {
                            self.cursor = player.location().clone();
                        }
                    }
                    // Start search mode.
                    keyevent::Key::Char('/') => {
                        self.search_mode = true;
                        return;
                    }
                    // Quit search mode.
                    keyevent::Key::Esc => {
                        self.search_str.clear();
                        self.search_mode = false;
                        return;
                    }
                    _ => {}
                };

                self.map_scale *= match input {
                    // Zoom out.
                    keyevent::Key::Char('u') => 0.5,
                    // Zoom in.
                    keyevent::Key::Char('i') => 2.,
                    // No zooming.
                    _ => 1.,
                };

                // Prevent zooming too far in.
                self.map_scale = self.map_scale.min(4.);

                let translation = match input {
                    // Move up.
                    keyevent::Key::Char('k') => Vector2::new(0., 1. / self.map_scale),
                    // Move down.
                    keyevent::Key::Char('j') => Vector2::new(0., -1. / self.map_scale),
                    // Move right.
                    keyevent::Key::Char('l') => Vector2::new(1. / self.map_scale, 0.),
                    // Move left.
                    keyevent::Key::Char('h') => Vector2::new(-1. / self.map_scale, 0.),
                    _ => Vector2::new(0., 0.),
                };

                // Move out of snapping system if currently snapped.
                self.cursor += match self.selected {
                    Some(_) => Vector2::new(
                        translation.x * 1.1 * self.map_scale * MIN_SNAP_DIST,
                        translation.y * 1.1 * self.map_scale * MIN_SNAP_DIST,
                    ),
                    None => translation,
                };
                self.selected = None;

                // Check if cursor should snap to closest system.
                if let Some(neighbor) = self.state.galaxy.lock().unwrap().nearest(&self.cursor) {
                    if distance(&self.cursor, &neighbor) < MIN_SNAP_DIST {
                        self.cursor = neighbor.clone();
                        self.selected = Some(neighbor.clone());
                    }
                }
            }
            _ => {}
        };
    }

    /// Draws the tab in the given terminal and area.
    fn draw(&self, term: &mut Terminal<MouseBackend>, area: &Rect) {
        let galaxy = self.state.galaxy.lock().unwrap();
        Group::default()
            .direction(Direction::Horizontal)
            .sizes(&[Size::Fixed(75), Size::Min(1)])
            .render(term, area, |term, chunks| {
                // TODO: Draw system detailed information.
                let systems = &galaxy.systems();
                let player = &self.state.player.lock().unwrap();
                //let player_loc = &self.state.player.lock().unwrap().location().clone();
                // Draw sidebar.
                Group::default()
                    .direction(Direction::Vertical)
                    .sizes(&[Size::Min(1), Size::Fixed(3)])
                    .render(term, &chunks[0], |term, sidebar_chunk| {
                        self.draw_system_info(
                            &player.location(),
                            self.selected.map(|point| galaxy.system(&point).unwrap()),
                            term,
                            &sidebar_chunk[0],
                        );
                        self.draw_search(term, &sidebar_chunk[1]);
                    });
                self.draw_galaxy_map(player, &systems, term, &chunks[1]);
            });
    }
}
<|endoftext|>


<commit_before>use crate::state::Fireplace;
use smithay::{
    backend::input::{Device, DeviceCapability, InputBackend, InputEvent, KeyState},
    reexports::wayland_server::Display,
    wayland::{
        data_device::set_data_device_focus,
        seat::{CursorImageStatus, Seat, XkbConfig},
        SERIAL_COUNTER as SCOUNTER,
    },
};
use std::{cell::RefCell, collections::HashMap};

pub mod keyboard;

pub struct ActiveOutput(pub RefCell<String>);

struct Devices(RefCell<HashMap<String, Vec<DeviceCapability>>>);

impl Devices {
    fn new() -> Devices {
        Devices(RefCell::new(HashMap::new()))
    }

    fn add_device<D: Device>(&self, device: &D) -> Vec<DeviceCapability> {
        let id = device.id();
        let mut map = self.0.borrow_mut();
        let caps = [DeviceCapability::Keyboard, DeviceCapability::Pointer]
            .iter()
            .cloned()
            .filter(|c| device.has_capability(*c))
            .collect::<Vec<_>>();
        let new_caps = caps
            .iter()
            .cloned()
            .filter(|c| map.values().flatten().all(|has| *c != *has))
            .collect::<Vec<_>>();
        map.insert(id, caps);
        new_caps
    }

    fn has_device<D: Device>(&self, device: &D) -> bool {
        self.0.borrow().contains_key(&device.id())
    }

    fn remove_device<D: Device>(&self, device: &D) -> Vec<DeviceCapability> {
        let id = device.id();
        let mut map = self.0.borrow_mut();
        map.remove(&id)
            .unwrap_or(Vec::new())
            .into_iter()
            .filter(|c| map.values().flatten().all(|has| *c != *has))
            .collect()
    }
}

pub fn add_seat(display: &mut Display, name: String) -> Seat {
    let (seat, _) = Seat::new(display, name, None);
    let userdata = seat.user_data();
    userdata.insert_if_missing(|| Devices::new());
    userdata.insert_if_missing(|| RefCell::new(CursorImageStatus::Hidden));
    seat
}

impl Fireplace {
    pub fn process_input_event<B: InputBackend>(&mut self, event: InputEvent<B>) {
        use smithay::backend::input::Event;

        match event {
            InputEvent::DeviceAdded { device } => {
                let seat = &mut self.last_active_seat;
                let userdata = seat.user_data();
                let devices = userdata.get::<Devices>().unwrap();
                for cap in devices.add_device(&device) {
                    match cap {
                        DeviceCapability::Keyboard => {
                            let _ =
                                seat.add_keyboard(XkbConfig::default(), 200, 25, |seat, focus| {
                                    set_data_device_focus(seat, focus.and_then(|s| s.as_ref().client()))
                                });
                        }
                        DeviceCapability::Pointer => {
                            let output = String::from(
                                self.workspaces
                                    .borrow_mut()
                                    .output(|_| true)
                                    .map(|x| x.name())
                                    .unwrap_or("headless"),
                            );
                            seat.user_data()
                                .insert_if_missing(|| ActiveOutput(RefCell::new(output)));
                            let owned_seat = seat.clone();
                            seat.add_pointer(move |status| {
                                *owned_seat.user_data().get::<RefCell<CursorImageStatus>>().unwrap().borrow_mut() = status;
                            });
                        }
                        _ => {}
                    }
                }
            }
            InputEvent::DeviceRemoved { device } => {
                for seat in &mut self.seats {
                    let userdata = seat.user_data();
                    let devices = userdata.get::<Devices>().unwrap();
                    if devices.has_device(&device) {
                        for cap in devices.remove_device(&device) {
                            match cap {
                                DeviceCapability::Keyboard => {
                                    seat.remove_keyboard();
                                }
                                DeviceCapability::Pointer => {
                                    seat.remove_pointer();
                                }
                                _ => {}
                            }
                        }
                        break;
                    }
                }
            }
            InputEvent::Keyboard { event, .. } => {
                use smithay::backend::input::KeyboardKeyEvent;

                let device = event.device();
                for seat in self.seats.clone().iter() {
                    let userdata = seat.user_data();
                    let devices = userdata.get::<Devices>().unwrap();
                    if devices.has_device(&device) {
                        let keycode = event.key_code();
                        let state = event.state();
                        slog_scope::debug!("key"; "keycode" => keycode, "state" => format!("{:?}", state));
                        let serial = SCOUNTER.next_serial();
                        let time = Event::time(&event);
                        seat.get_keyboard().unwrap().input(
                            keycode,
                            state,
                            serial,
                            time,
                            |modifiers, keysym| {
                                slog_scope::debug!("keysym";
                                    "state" => format!("{:?}", state),
                                    "mods" => format!("{:?}", modifiers),
                                    "keysym" => ::xkbcommon::xkb::keysym_get_name(keysym)
                                );

                                // If the key is pressed and triggered a action
                                // we will not forward the key to the client.
                                // Additionally add the key to the suppressed keys
                                // so that we can decide on a release if the key
                                // should be forwarded to the client or not.
                                if let KeyState::Pressed = state {
                                    if let Some(command) = self
                                        .config
                                        .keys
                                        .iter()
                                        .find(|(_, p)| p.modifiers == *modifiers && p.key == keysym)
                                        .map(|(c, _)| c)
                                        .cloned()
                                    {
                                        self.process_global_command(&command);
                                        self.suppressed_keys.push(keysym);
                                        return false;
                                    }
                                    if let Some(command) = self
                                        .config
                                        .workspace
                                        .keys
                                        .iter()
                                        .find(|(_, p)| p.modifiers == *modifiers && p.key == keysym)
                                        .map(|(c, _)| c)
                                        .cloned()
                                    {
                                        self.process_workspace_command(&command, seat);
                                        self.suppressed_keys.push(keysym);
                                        return false;
                                    }
                                    if let Some(command) = self
                                        .config
                                        .view
                                        .keys
                                        .iter()
                                        .find(|(_, p)| p.modifiers == *modifiers && p.key == keysym)
                                        .map(|(c, _)| c)
                                        .cloned()
                                    {
                                        self.process_view_command(&command, seat);
                                        self.suppressed_keys.push(keysym);
                                        return false;
                                    }
                                    if let Some(command) = self
                                        .config
                                        .exec
                                        .keys
                                        .iter()
                                        .find(|(_, p)| p.modifiers == *modifiers && p.key == keysym)
                                        .map(|(c, _)| c)
                                        .cloned()
                                    {
                                        if let Err(err) = self.process_exec_command(&command) {
                                            slog_scope::warn!("Failed to spawn process: {}", err);
                                        }
                                        self.suppressed_keys.push(keysym);
                                        return false;
                                    }
                                    true
                                } else {
                                    let suppressed = self.suppressed_keys.contains(&keysym);
                                    if suppressed {
                                        self.suppressed_keys.retain(|k| *k != keysym);
                                    }
                                    !suppressed
                                }
                            },
                        );

                        break;
                    }
                }
            }
            InputEvent::PointerMotion { event, .. } => {
                use smithay::backend::input::PointerMotionEvent;

                let device = event.device();
                for seat in self.seats.clone().iter() {
                    let userdata = seat.user_data();
                    let devices = userdata.get::<Devices>().unwrap();
                    if devices.has_device(&device) {
                        let mut current_output_name =
                            userdata.get::<ActiveOutput>().unwrap().0.borrow_mut();
                        let mut workspaces = self.workspaces.borrow_mut();

                        let serial = SCOUNTER.next_serial();

                        // clamp coordinates
                        let mut location = seat.get_pointer().unwrap().current_location();
                        let output_name = {
                            location += event.delta();
                            location.x =
                                f64::min(f64::max(0.0, location.x), workspaces.width() as f64);
                            let output = workspaces
                                .output(|o| {
                                    let geo = o.geometry();
                                    geo.loc.x as f64 <= location.x
                                        && (geo.loc.x + geo.size.w) as f64 > location.x
                                })
                                .unwrap();
                            location.y =
                                f64::min(f64::max(0.0, location.y), output.size().h as f64);
                            String::from(output.name())
                        };

                        let space = workspaces.space_by_output_name(&output_name).unwrap();
                        let under = space.surface_under(location);
                        seat.get_pointer()
                            .unwrap()
                            .motion(location, under, serial, event.time());

                        *current_output_name = output_name;
                        break;
                    }
                }
            }
            InputEvent::PointerMotionAbsolute { event, .. } => {
                use smithay::backend::input::PointerMotionAbsoluteEvent;

                let device = event.device();
                for seat in self.seats.clone().iter() {
                    let userdata = seat.user_data();
                    let devices = userdata.get::<Devices>().unwrap();
                    if devices.has_device(&device) {
                        let output_name = userdata.get::<ActiveOutput>().unwrap().0.borrow();
                        let mut workspaces = self.workspaces.borrow_mut();
                        let output = workspaces.output_by_name(&*output_name).unwrap();
                        let output_size = output.size();
                        let pos =
                            output.location().to_f64() + event.position_transformed(output_size);
                        let serial = SCOUNTER.next_serial();
                        let space = workspaces.space_by_output_name(&*output_name).unwrap();
                        let under = space.surface_under(pos);
                        seat.get_pointer()
                            .unwrap()
                            .motion(pos, under, serial, event.time());
                        break;
                    }
                }
            }
            InputEvent::PointerButton { event, .. } => {
                use smithay::{
                    backend::input::{ButtonState, MouseButton, PointerButtonEvent},
                    reexports::wayland_server::protocol::wl_pointer,
                };

                let device = event.device();
                for seat in self.seats.clone().iter() {
                    let userdata = seat.user_data();
                    let devices = userdata.get::<Devices>().unwrap();
                    if devices.has_device(&device) {
                        let serial = SCOUNTER.next_serial();
                        let button = match event.button() {
                            MouseButton::Left => 0x110,
                            MouseButton::Right => 0x111,
                            MouseButton::Middle => 0x112,
                            MouseButton::Other(b) => b as u32,
                        };
                        let state = match event.state() {
                            ButtonState::Pressed => {
                                // change the keyboard focus unless the pointer is grabbed
                                if !seat.get_pointer().unwrap().is_grabbed() {
                                    let mut workspaces = self.workspaces.borrow_mut();
                                    let space = workspaces.space_by_seat(&seat).unwrap();
                                    let pos = seat.get_pointer().unwrap().current_location();
                                    let under = space.surface_under(pos);
                                    if let Some(&(ref under, _)) = under.as_ref() {
                                        space.on_focus(under);
                                    }
                                    if let Some(keyboard) = seat.get_keyboard() {
                                        keyboard
                                            .set_focus(under.as_ref().map(|&(ref s, _)| s), serial);
                                    }
                                }
                                wl_pointer::ButtonState::Pressed
                            }
                            ButtonState::Released => wl_pointer::ButtonState::Released,
                        };
                        seat.get_pointer()
                            .unwrap()
                            .button(button, state, serial, event.time());
                        break;
                    }
                }
            }
            InputEvent::PointerAxis { event, .. } => {
                use smithay::{
                    backend::input::{Axis, AxisSource, PointerAxisEvent},
                    reexports::wayland_server::protocol::wl_pointer,
                    wayland::seat::AxisFrame,
                };

                let device = event.device();
                for seat in self.seats.clone().iter() {
                    let userdata = seat.user_data();
                    let devices = userdata.get::<Devices>().unwrap();
                    if devices.has_device(&device) {
                        let source = match event.source() {
                            AxisSource::Continuous => wl_pointer::AxisSource::Continuous,
                            AxisSource::Finger => wl_pointer::AxisSource::Finger,
                            AxisSource::Wheel | AxisSource::WheelTilt => {
                                wl_pointer::AxisSource::Wheel
                            }
                        };
                        let horizontal_amount =
                            event.amount(Axis::Horizontal).unwrap_or_else(|| {
                                event.amount_discrete(Axis::Horizontal).unwrap() * 3.0
                            });
                        let vertical_amount = event.amount(Axis::Vertical).unwrap_or_else(|| {
                            event.amount_discrete(Axis::Vertical).unwrap() * 3.0
                        });
                        let horizontal_amount_discrete = event.amount_discrete(Axis::Horizontal);
                        let vertical_amount_discrete = event.amount_discrete(Axis::Vertical);

                        {
                            let mut frame = AxisFrame::new(event.time()).source(source);
                            if horizontal_amount != 0.0 {
                                frame = frame
                                    .value(wl_pointer::Axis::HorizontalScroll, horizontal_amount);
                                if let Some(discrete) = horizontal_amount_discrete {
                                    frame = frame.discrete(
                                        wl_pointer::Axis::HorizontalScroll,
                                        discrete as i32,
                                    );
                                }
                            } else if source == wl_pointer::AxisSource::Finger {
                                frame = frame.stop(wl_pointer::Axis::HorizontalScroll);
                            }
                            if vertical_amount != 0.0 {
                                frame =
                                    frame.value(wl_pointer::Axis::VerticalScroll, vertical_amount);
                                if let Some(discrete) = vertical_amount_discrete {
                                    frame = frame.discrete(
                                        wl_pointer::Axis::VerticalScroll,
                                        discrete as i32,
                                    );
                                }
                            } else if source == wl_pointer::AxisSource::Finger {
                                frame = frame.stop(wl_pointer::Axis::VerticalScroll);
                            }
                            seat.get_pointer().unwrap().axis(frame);
                        }
                        break;
                    }
                }
            }
            _ => {}
        }
    }

    pub fn process_global_command(&mut self, command: &str) {
        match command {
            "terminate" => {
                self.should_stop = true;
            }
            _ => {
                slog_scope::debug!("Unknown global command: {}", command);
            }
        }
    }

    pub fn process_workspace_command(&mut self, command: &str, seat: &Seat) {
        let mut workspaces = self.workspaces.borrow_mut();
        match command {
            x if x.starts_with("workspace") => {
                if let Ok(idx) = x.strip_prefix("workspace").unwrap().parse::<u8>() {
                    workspaces.switch_workspace(seat, idx);
                }
            }
            x if x.starts_with("moveto_workspace") => {
                if let Ok(idx) = x.strip_prefix("moveto_workspace").unwrap().parse::<u8>() {
                    slog_scope::debug!("Moveto: {}", idx);
                    let output_name = &seat.user_data().get::<ActiveOutput>().unwrap().0;
                    let current_space_idx = workspaces
                        .idx_by_output_name(&*output_name.borrow())
                        .unwrap();
                    if current_space_idx != idx {
                        let window = {
                            let current_space = workspaces.space_by_idx(current_space_idx);
                            if let Some(window) = current_space.focused_window() {
                                current_space.remove_toplevel(window.clone());
                                window
                            } else {
                                return;
                            }
                        };
                        let new_space = workspaces.space_by_idx(idx);
                        new_space.new_toplevel(window);
                    }
                }
            }
            _ => {
                slog_scope::debug!("Unknown workspace command: {}", command);
            }
        }
    }

    pub fn process_view_command(&mut self, command: &str, seat: &Seat) {
        match command {
            "close" => {
                let mut workspaces = self.workspaces.borrow_mut();
                let space = workspaces.space_by_seat(&seat).unwrap();
                if let Some(window) = space.focused_window() {
                    window.send_close();
                }
            }
            _ => {
                slog_scope::debug!("Unknown view command: {}", command);
            }
        }
    }

    pub fn process_exec_command(&mut self, command: &str) -> std::io::Result<()> {
        std::process::Command::new("/bin/sh")
            .arg("-c")
            .arg(command)
            .spawn()
            .map(|_| ())
    }

    pub fn last_active_seat(&self) -> &Seat {
        &self.last_active_seat
    }
}
<commit_msg>exec: Set *DISPLAY appropriately for new clients<commit_after>use crate::state::Fireplace;
use smithay::{
    backend::input::{Device, DeviceCapability, InputBackend, InputEvent, KeyState},
    reexports::wayland_server::Display,
    wayland::{
        data_device::set_data_device_focus,
        seat::{CursorImageStatus, Seat, XkbConfig},
        SERIAL_COUNTER as SCOUNTER,
    },
};
use std::{cell::RefCell, collections::HashMap};

pub mod keyboard;

pub struct ActiveOutput(pub RefCell<String>);

struct Devices(RefCell<HashMap<String, Vec<DeviceCapability>>>);

impl Devices {
    fn new() -> Devices {
        Devices(RefCell::new(HashMap::new()))
    }

    fn add_device<D: Device>(&self, device: &D) -> Vec<DeviceCapability> {
        let id = device.id();
        let mut map = self.0.borrow_mut();
        let caps = [DeviceCapability::Keyboard, DeviceCapability::Pointer]
            .iter()
            .cloned()
            .filter(|c| device.has_capability(*c))
            .collect::<Vec<_>>();
        let new_caps = caps
            .iter()
            .cloned()
            .filter(|c| map.values().flatten().all(|has| *c != *has))
            .collect::<Vec<_>>();
        map.insert(id, caps);
        new_caps
    }

    fn has_device<D: Device>(&self, device: &D) -> bool {
        self.0.borrow().contains_key(&device.id())
    }

    fn remove_device<D: Device>(&self, device: &D) -> Vec<DeviceCapability> {
        let id = device.id();
        let mut map = self.0.borrow_mut();
        map.remove(&id)
            .unwrap_or(Vec::new())
            .into_iter()
            .filter(|c| map.values().flatten().all(|has| *c != *has))
            .collect()
    }
}

pub fn add_seat(display: &mut Display, name: String) -> Seat {
    let (seat, _) = Seat::new(display, name, None);
    let userdata = seat.user_data();
    userdata.insert_if_missing(|| Devices::new());
    userdata.insert_if_missing(|| RefCell::new(CursorImageStatus::Hidden));
    seat
}

impl Fireplace {
    pub fn process_input_event<B: InputBackend>(&mut self, event: InputEvent<B>) {
        use smithay::backend::input::Event;

        match event {
            InputEvent::DeviceAdded { device } => {
                let seat = &mut self.last_active_seat;
                let userdata = seat.user_data();
                let devices = userdata.get::<Devices>().unwrap();
                for cap in devices.add_device(&device) {
                    match cap {
                        DeviceCapability::Keyboard => {
                            let _ =
                                seat.add_keyboard(XkbConfig::default(), 200, 25, |seat, focus| {
                                    set_data_device_focus(seat, focus.and_then(|s| s.as_ref().client()))
                                });
                        }
                        DeviceCapability::Pointer => {
                            let output = String::from(
                                self.workspaces
                                    .borrow_mut()
                                    .output(|_| true)
                                    .map(|x| x.name())
                                    .unwrap_or("headless"),
                            );
                            seat.user_data()
                                .insert_if_missing(|| ActiveOutput(RefCell::new(output)));
                            let owned_seat = seat.clone();
                            seat.add_pointer(move |status| {
                                *owned_seat.user_data().get::<RefCell<CursorImageStatus>>().unwrap().borrow_mut() = status;
                            });
                        }
                        _ => {}
                    }
                }
            }
            InputEvent::DeviceRemoved { device } => {
                for seat in &mut self.seats {
                    let userdata = seat.user_data();
                    let devices = userdata.get::<Devices>().unwrap();
                    if devices.has_device(&device) {
                        for cap in devices.remove_device(&device) {
                            match cap {
                                DeviceCapability::Keyboard => {
                                    seat.remove_keyboard();
                                }
                                DeviceCapability::Pointer => {
                                    seat.remove_pointer();
                                }
                                _ => {}
                            }
                        }
                        break;
                    }
                }
            }
            InputEvent::Keyboard { event, .. } => {
                use smithay::backend::input::KeyboardKeyEvent;

                let device = event.device();
                for seat in self.seats.clone().iter() {
                    let userdata = seat.user_data();
                    let devices = userdata.get::<Devices>().unwrap();
                    if devices.has_device(&device) {
                        let keycode = event.key_code();
                        let state = event.state();
                        slog_scope::debug!("key"; "keycode" => keycode, "state" => format!("{:?}", state));
                        let serial = SCOUNTER.next_serial();
                        let time = Event::time(&event);
                        seat.get_keyboard().unwrap().input(
                            keycode,
                            state,
                            serial,
                            time,
                            |modifiers, keysym| {
                                slog_scope::debug!("keysym";
                                    "state" => format!("{:?}", state),
                                    "mods" => format!("{:?}", modifiers),
                                    "keysym" => ::xkbcommon::xkb::keysym_get_name(keysym)
                                );

                                // If the key is pressed and triggered a action
                                // we will not forward the key to the client.
                                // Additionally add the key to the suppressed keys
                                // so that we can decide on a release if the key
                                // should be forwarded to the client or not.
                                if let KeyState::Pressed = state {
                                    if let Some(command) = self
                                        .config
                                        .keys
                                        .iter()
                                        .find(|(_, p)| p.modifiers == *modifiers && p.key == keysym)
                                        .map(|(c, _)| c)
                                        .cloned()
                                    {
                                        self.process_global_command(&command);
                                        self.suppressed_keys.push(keysym);
                                        return false;
                                    }
                                    if let Some(command) = self
                                        .config
                                        .workspace
                                        .keys
                                        .iter()
                                        .find(|(_, p)| p.modifiers == *modifiers && p.key == keysym)
                                        .map(|(c, _)| c)
                                        .cloned()
                                    {
                                        self.process_workspace_command(&command, seat);
                                        self.suppressed_keys.push(keysym);
                                        return false;
                                    }
                                    if let Some(command) = self
                                        .config
                                        .view
                                        .keys
                                        .iter()
                                        .find(|(_, p)| p.modifiers == *modifiers && p.key == keysym)
                                        .map(|(c, _)| c)
                                        .cloned()
                                    {
                                        self.process_view_command(&command, seat);
                                        self.suppressed_keys.push(keysym);
                                        return false;
                                    }
                                    if let Some(command) = self
                                        .config
                                        .exec
                                        .keys
                                        .iter()
                                        .find(|(_, p)| p.modifiers == *modifiers && p.key == keysym)
                                        .map(|(c, _)| c)
                                        .cloned()
                                    {
                                        if let Err(err) = self.process_exec_command(&command) {
                                            slog_scope::warn!("Failed to spawn process: {}", err);
                                        }
                                        self.suppressed_keys.push(keysym);
                                        return false;
                                    }
                                    true
                                } else {
                                    let suppressed = self.suppressed_keys.contains(&keysym);
                                    if suppressed {
                                        self.suppressed_keys.retain(|k| *k != keysym);
                                    }
                                    !suppressed
                                }
                            },
                        );

                        break;
                    }
                }
            }
            InputEvent::PointerMotion { event, .. } => {
                use smithay::backend::input::PointerMotionEvent;

                let device = event.device();
                for seat in self.seats.clone().iter() {
                    let userdata = seat.user_data();
                    let devices = userdata.get::<Devices>().unwrap();
                    if devices.has_device(&device) {
                        let mut current_output_name =
                            userdata.get::<ActiveOutput>().unwrap().0.borrow_mut();
                        let mut workspaces = self.workspaces.borrow_mut();

                        let serial = SCOUNTER.next_serial();

                        // clamp coordinates
                        let mut location = seat.get_pointer().unwrap().current_location();
                        let output_name = {
                            location += event.delta();
                            location.x =
                                f64::min(f64::max(0.0, location.x), workspaces.width() as f64);
                            let output = workspaces
                                .output(|o| {
                                    let geo = o.geometry();
                                    geo.loc.x as f64 <= location.x
                                        && (geo.loc.x + geo.size.w) as f64 > location.x
                                })
                                .unwrap();
                            location.y =
                                f64::min(f64::max(0.0, location.y), output.size().h as f64);
                            String::from(output.name())
                        };

                        let space = workspaces.space_by_output_name(&output_name).unwrap();
                        let under = space.surface_under(location);
                        seat.get_pointer()
                            .unwrap()
                            .motion(location, under, serial, event.time());

                        *current_output_name = output_name;
                        break;
                    }
                }
            }
            InputEvent::PointerMotionAbsolute { event, .. } => {
                use smithay::backend::input::PointerMotionAbsoluteEvent;

                let device = event.device();
                for seat in self.seats.clone().iter() {
                    let userdata = seat.user_data();
                    let devices = userdata.get::<Devices>().unwrap();
                    if devices.has_device(&device) {
                        let output_name = userdata.get::<ActiveOutput>().unwrap().0.borrow();
                        let mut workspaces = self.workspaces.borrow_mut();
                        let output = workspaces.output_by_name(&*output_name).unwrap();
                        let output_size = output.size();
                        let pos =
                            output.location().to_f64() + event.position_transformed(output_size);
                        let serial = SCOUNTER.next_serial();
                        let space = workspaces.space_by_output_name(&*output_name).unwrap();
                        let under = space.surface_under(pos);
                        seat.get_pointer()
                            .unwrap()
                            .motion(pos, under, serial, event.time());
                        break;
                    }
                }
            }
            InputEvent::PointerButton { event, .. } => {
                use smithay::{
                    backend::input::{ButtonState, MouseButton, PointerButtonEvent},
                    reexports::wayland_server::protocol::wl_pointer,
                };

                let device = event.device();
                for seat in self.seats.clone().iter() {
                    let userdata = seat.user_data();
                    let devices = userdata.get::<Devices>().unwrap();
                    if devices.has_device(&device) {
                        let serial = SCOUNTER.next_serial();
                        let button = match event.button() {
                            MouseButton::Left => 0x110,
                            MouseButton::Right => 0x111,
                            MouseButton::Middle => 0x112,
                            MouseButton::Other(b) => b as u32,
                        };
                        let state = match event.state() {
                            ButtonState::Pressed => {
                                // change the keyboard focus unless the pointer is grabbed
                                if !seat.get_pointer().unwrap().is_grabbed() {
                                    let mut workspaces = self.workspaces.borrow_mut();
                                    let space = workspaces.space_by_seat(&seat).unwrap();
                                    let pos = seat.get_pointer().unwrap().current_location();
                                    let under = space.surface_under(pos);
                                    if let Some(&(ref under, _)) = under.as_ref() {
                                        space.on_focus(under);
                                    }
                                    if let Some(keyboard) = seat.get_keyboard() {
                                        keyboard
                                            .set_focus(under.as_ref().map(|&(ref s, _)| s), serial);
                                    }
                                }
                                wl_pointer::ButtonState::Pressed
                            }
                            ButtonState::Released => wl_pointer::ButtonState::Released,
                        };
                        seat.get_pointer()
                            .unwrap()
                            .button(button, state, serial, event.time());
                        break;
                    }
                }
            }
            InputEvent::PointerAxis { event, .. } => {
                use smithay::{
                    backend::input::{Axis, AxisSource, PointerAxisEvent},
                    reexports::wayland_server::protocol::wl_pointer,
                    wayland::seat::AxisFrame,
                };

                let device = event.device();
                for seat in self.seats.clone().iter() {
                    let userdata = seat.user_data();
                    let devices = userdata.get::<Devices>().unwrap();
                    if devices.has_device(&device) {
                        let source = match event.source() {
                            AxisSource::Continuous => wl_pointer::AxisSource::Continuous,
                            AxisSource::Finger => wl_pointer::AxisSource::Finger,
                            AxisSource::Wheel | AxisSource::WheelTilt => {
                                wl_pointer::AxisSource::Wheel
                            }
                        };
                        let horizontal_amount =
                            event.amount(Axis::Horizontal).unwrap_or_else(|| {
                                event.amount_discrete(Axis::Horizontal).unwrap() * 3.0
                            });
                        let vertical_amount = event.amount(Axis::Vertical).unwrap_or_else(|| {
                            event.amount_discrete(Axis::Vertical).unwrap() * 3.0
                        });
                        let horizontal_amount_discrete = event.amount_discrete(Axis::Horizontal);
                        let vertical_amount_discrete = event.amount_discrete(Axis::Vertical);

                        {
                            let mut frame = AxisFrame::new(event.time()).source(source);
                            if horizontal_amount != 0.0 {
                                frame = frame
                                    .value(wl_pointer::Axis::HorizontalScroll, horizontal_amount);
                                if let Some(discrete) = horizontal_amount_discrete {
                                    frame = frame.discrete(
                                        wl_pointer::Axis::HorizontalScroll,
                                        discrete as i32,
                                    );
                                }
                            } else if source == wl_pointer::AxisSource::Finger {
                                frame = frame.stop(wl_pointer::Axis::HorizontalScroll);
                            }
                            if vertical_amount != 0.0 {
                                frame =
                                    frame.value(wl_pointer::Axis::VerticalScroll, vertical_amount);
                                if let Some(discrete) = vertical_amount_discrete {
                                    frame = frame.discrete(
                                        wl_pointer::Axis::VerticalScroll,
                                        discrete as i32,
                                    );
                                }
                            } else if source == wl_pointer::AxisSource::Finger {
                                frame = frame.stop(wl_pointer::Axis::VerticalScroll);
                            }
                            seat.get_pointer().unwrap().axis(frame);
                        }
                        break;
                    }
                }
            }
            _ => {}
        }
    }

    pub fn process_global_command(&mut self, command: &str) {
        match command {
            "terminate" => {
                self.should_stop = true;
            }
            _ => {
                slog_scope::debug!("Unknown global command: {}", command);
            }
        }
    }

    pub fn process_workspace_command(&mut self, command: &str, seat: &Seat) {
        let mut workspaces = self.workspaces.borrow_mut();
        match command {
            x if x.starts_with("workspace") => {
                if let Ok(idx) = x.strip_prefix("workspace").unwrap().parse::<u8>() {
                    workspaces.switch_workspace(seat, idx);
                }
            }
            x if x.starts_with("moveto_workspace") => {
                if let Ok(idx) = x.strip_prefix("moveto_workspace").unwrap().parse::<u8>() {
                    slog_scope::debug!("Moveto: {}", idx);
                    let output_name = &seat.user_data().get::<ActiveOutput>().unwrap().0;
                    let current_space_idx = workspaces
                        .idx_by_output_name(&*output_name.borrow())
                        .unwrap();
                    if current_space_idx != idx {
                        let window = {
                            let current_space = workspaces.space_by_idx(current_space_idx);
                            if let Some(window) = current_space.focused_window() {
                                current_space.remove_toplevel(window.clone());
                                window
                            } else {
                                return;
                            }
                        };
                        let new_space = workspaces.space_by_idx(idx);
                        new_space.new_toplevel(window);
                    }
                }
            }
            _ => {
                slog_scope::debug!("Unknown workspace command: {}", command);
            }
        }
    }

    pub fn process_view_command(&mut self, command: &str, seat: &Seat) {
        match command {
            "close" => {
                let mut workspaces = self.workspaces.borrow_mut();
                let space = workspaces.space_by_seat(&seat).unwrap();
                if let Some(window) = space.focused_window() {
                    window.send_close();
                }
            }
            _ => {
                slog_scope::debug!("Unknown view command: {}", command);
            }
        }
    }

    pub fn process_exec_command(&mut self, command: &str) -> std::io::Result<()> {
        std::process::Command::new("/bin/sh")
            .arg("-c")
            .arg(command)
            .env_remove("DISPLAY")
            .env("WAYLAND_DISPLAY", &self.socket_name)
            .spawn()
            .map(|_| ())
    }

    pub fn last_active_seat(&self) -> &Seat {
        &self.last_active_seat
    }
}
<|endoftext|>


<commit_before>use std::io;

const ALPHABET: &'static[u8] =   b"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";

#[inline]
pub fn encode_wr<W:io::Write>(wr: &mut W, id: u64) -> io::Result<()> {
    let mut n = id;
    let alphasize = ALPHABET.len() as u64;
    loop {
        let rem = n % alphasize;
        n = n / alphasize;
        try!(wr.write_all(&ALPHABET[rem as usize .. rem as usize + 1]));
        if n == 0 { break; }
    }
    Ok(())
}

pub fn encode(id: u64) -> String {
    let mut v = Vec::new();
    encode_wr(&mut v, id).unwrap();
    String::from_utf8(v).unwrap()
}

pub fn decode(bytes: &[u8]) -> Option<u64> {
    let mut inv: Vec<i8> = (0..256).map(|_| -1).collect();
    for (i, &b) in ALPHABET.iter().enumerate() {
        inv[b as usize] = i as i8;
    }

    let mut failure = false;
    let n = {
        bytes.into_iter().rev().filter_map(|&b| {
            match inv.get(b as usize) {
                Some(&-1) | None => {failure = true; None},
                Some(&val) => Some(val as u8)
            }
        }).fold(0u64, |acc, item| acc * ALPHABET.len() as u64 + item as u64)
    };

    if failure { None }
    else { Some(n) }
}

#[test]
fn test_encode() {
    assert_eq!("c4NP", encode(12343344));
}

#[test]
fn test_decode() {
    assert_eq!(Some(12343344), decode(b"c4NP"));
}

#[test]
fn test_cyclic() {
    use super::rand::random;
    assert_eq!(Some(0), decode(encode(0).as_bytes()));
    for _ in 0 .. 1_000 {
        let i = random::<u64>();
        let enc = encode(i);
        let dec = decode(enc.as_bytes());
        assert_eq!(Some(i), dec);
    }
}
<commit_msg>Use buf<commit_after>use std::io;
use std::u64;

const ALPHABET: &'static[u8] =   b"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";

#[inline(always)]
pub fn encode_wr<W:io::Write>(wr: &mut W, id: u64) -> io::Result<()> {
    let mut n = id;
    let alphasize = ALPHABET.len() as u64;
    let mut buf: [u8; 11] = [0u8; 11];
    let mut i = 0;

    loop {
        let rem = n % alphasize;
        n = n / alphasize;
        buf[i] = ALPHABET[rem as usize];
        i += 1;
        if n == 0 { break; }
    }

    wr.write_all(&buf[..i])
}

pub fn encode(id: u64) -> String {
    let mut v = Vec::new();
    encode_wr(&mut v, id).unwrap();
    String::from_utf8(v).unwrap()
}

pub fn decode(bytes: &[u8]) -> Option<u64> {
    let mut inv: Vec<i8> = (0..256).map(|_| -1).collect();
    for (i, &b) in ALPHABET.iter().enumerate() {
        inv[b as usize] = i as i8;
    }

    let mut failure = false;
    let n = {
        bytes.into_iter().rev().filter_map(|&b| {
            match inv.get(b as usize) {
                Some(&-1) | None => {failure = true; None},
                Some(&val) => Some(val as u8)
            }
        }).fold(0u64, |acc, item| acc * ALPHABET.len() as u64 + item as u64)
    };

    if failure { None }
    else { Some(n) }
}

#[test]
fn test_encode() {
    assert_eq!("c4NP", encode(12343344));
    assert_eq!("fyha61AhGYl", encode(u64::MAX));
    assert_eq!("0", encode(0));
}

#[test]
fn test_decode() {
    assert_eq!(Some(12343344), decode(b"c4NP"));
    assert_eq!(Some(0), decode(b"0"));
    assert_eq!(Some(u64::MAX), decode(b"fyha61AhGYl"));
}

#[test]
fn test_cyclic() {
    use super::rand::random;
    assert_eq!(Some(0), decode(encode(0).as_bytes()));
    for _ in 0 .. 1_000 {
        let i = random::<u64>();
        let enc = encode(i);
        let dec = decode(enc.as_bytes());
        assert_eq!(Some(i), dec);
    }
}
<|endoftext|>


<commit_before>//! Module containing HTTP client implementations
use std::net::{SocketAddr, ToSocketAddrs, TcpStream};
use std::io::prelude::*;
use std::collections::HashMap;
use std::collections::hash_map::{Iter, Keys};
use std::io::{Error, BufWriter, ErrorKind};
use std::marker::PhantomData;

use openssl::ssl::SslStream;

use super::methods::Method;
use super::messages::HttpReply;
use super::constants::properties;
use super::streams::*;

/// A simple and low-level HTTP client implementation
struct BaseClient<T: Read+Write, S: Open+Stream<T>> {
	addr: SocketAddr,
	header: HashMap<String, String>,
	stream: Option<S>,
	inner_stream_type: PhantomData<T>
}

/// Client for secured HTTP
pub type HttpsClient = BaseClient<SslStream<TcpStream>, HttpsStream>;
/// Client for unsecured HTTP
pub type HttpClient = BaseClient<TcpStream, HttpStream>;

impl <T: Read+Write, S: Open+Stream<T>> BaseClient<T, S> {

	/// Create a new HTTP client that will send requests to `addr`.
	/// # Example
	/// ```no_run
	/// use http::client::BaseClient;
	/// let mut client = HttpClient::new("www.google.com:80");
	/// // Send some requests
	/// ```
	pub fn new<A: ToSocketAddrs>(addr: A) -> Result<BaseClient<T, S>, Error> {
		let address = option!(try!(addr.to_socket_addrs()).next(), "Cannot resolve address");
		let client = BaseClient{
			addr: address,
			header: HashMap::new(),
			stream: None,
			inner_stream_type: PhantomData
		};
		return Ok(client);
	}

	/// Get a property from client permanent header
	pub fn get_property(&self, key: &String) -> Option<&String> {
		return self.header.get(key);
	}

	/// Set a property in permanent header
	pub fn set_property(&mut self, key: String, value: String) {
		self.header.insert(key, value);
	}

	/// Remove a property from permanent header
	pub fn unset_property(&mut self, key: &String) {
		self.header.remove(key);
	}

	/// Return an iterator over properties names from permanent header
	pub fn get_properties_name(&self) -> Keys<String, String> {
		return self.header.keys();
	}

	/// Return an iterator over properties from permanent header
	pub fn iter(&self) -> Iter<String, String> {
		return self.header.iter();
	}

	/// Open an `HttpStream` to remote host
	fn connect(&mut self) -> Result<&mut S, Error> {
		self.stream = Some(try!(S::open(self.addr)));
		return Ok(self.stream.as_mut().unwrap());
	}

	fn update_properties(&self, header: Option<&HashMap<String, String>>) -> HashMap<String, String> {
		let mut hdr = match header {
			Some(h) => h.clone(),
			None => HashMap::new()
		};
		for (k, v) in &self.header {
			if ! hdr.contains_key(k) {
				hdr.insert(k.clone(), v.clone());
			}
		}
		return hdr;
	}

	pub fn send_stream(&mut self, method: Method, path: &str, header: Option<&HashMap<String, String>>) -> Result<BufWriter<T>, Error> {
		let hdr = self.update_properties(header);
		let mut stream = try!(self.connect());
		let mut w = stream.new_writer();
		{
			let mut writer = &mut w;
			try!(writer.write(method.as_bytes()));
			try!(writer.write(b" "));
			try!(writer.write(path.as_bytes()));
			try!(writer.write(b"\r\n"));

			//Write header
			for (k, v) in hdr {
				try!(writer.write(k.as_bytes()));
				try!(writer.write(b": "));
				try!(writer.write(v.as_bytes()));
				try!(writer.write(b"\r\n"));
			}
			try!(writer.write(b"\r\n"));
		}
		return Ok(w);
	}

	pub fn get_reply(&mut self) -> Result<HttpReply<T>, Error> {
		let stream = match self.stream.as_mut() {
			Some(s) => s,
			None => return Err(Error::new(ErrorKind::NotConnected, "Cannot get reply since no stream is opened"))
		};
		return HttpReply::parse(stream.new_reader());
	}

	pub fn send(&mut self, method: Method, path: &str, header: Option<&HashMap<String, String>>, data: Option<&[u8]>) -> Result<HttpReply<T>, Error> {
		{
			let mut hdr = match header {
				Some(h) => h.clone(),
				None => HashMap::new()
			};
			if let Some(d) = data {
				hdr.insert(properties::CONTENT_LENGTH.to_string(), d.len().to_string());
			}
			let mut writer = try!(self.send_stream(method, path, Some(&hdr)));
			if let Some(d) = data {
				try!(writer.write(d));
			}
			try!(writer.flush());
		}
		return self.get_reply();
	}
}<commit_msg>Re-fixed failing doc test<commit_after>//! Module containing HTTP client implementations
use std::net::{SocketAddr, ToSocketAddrs, TcpStream};
use std::io::prelude::*;
use std::collections::HashMap;
use std::collections::hash_map::{Iter, Keys};
use std::io::{Error, BufWriter, ErrorKind};
use std::marker::PhantomData;

use openssl::ssl::SslStream;

use super::methods::Method;
use super::messages::HttpReply;
use super::constants::properties;
use super::streams::*;

/// A simple and low-level HTTP client implementation
struct BaseClient<T: Read+Write, S: Open+Stream<T>> {
	addr: SocketAddr,
	header: HashMap<String, String>,
	stream: Option<S>,
	inner_stream_type: PhantomData<T>
}

/// Client for secured HTTP
pub type HttpsClient = BaseClient<SslStream<TcpStream>, HttpsStream>;
/// Client for unsecured HTTP
pub type HttpClient = BaseClient<TcpStream, HttpStream>;

impl <T: Read+Write, S: Open+Stream<T>> BaseClient<T, S> {

	/// Create a new HTTP client that will send requests to `addr`.
	/// # Example
	/// ```no_run
	/// use http::client::HttpClient;
	/// let mut client = HttpClient::new("www.google.com:80");
	/// // Send some requests
	/// ```
	pub fn new<A: ToSocketAddrs>(addr: A) -> Result<BaseClient<T, S>, Error> {
		let address = option!(try!(addr.to_socket_addrs()).next(), "Cannot resolve address");
		let client = BaseClient{
			addr: address,
			header: HashMap::new(),
			stream: None,
			inner_stream_type: PhantomData
		};
		return Ok(client);
	}

	/// Get a property from client permanent header
	pub fn get_property(&self, key: &String) -> Option<&String> {
		return self.header.get(key);
	}

	/// Set a property in permanent header
	pub fn set_property(&mut self, key: String, value: String) {
		self.header.insert(key, value);
	}

	/// Remove a property from permanent header
	pub fn unset_property(&mut self, key: &String) {
		self.header.remove(key);
	}

	/// Return an iterator over properties names from permanent header
	pub fn get_properties_name(&self) -> Keys<String, String> {
		return self.header.keys();
	}

	/// Return an iterator over properties from permanent header
	pub fn iter(&self) -> Iter<String, String> {
		return self.header.iter();
	}

	/// Open an `HttpStream` to remote host
	fn connect(&mut self) -> Result<&mut S, Error> {
		self.stream = Some(try!(S::open(self.addr)));
		return Ok(self.stream.as_mut().unwrap());
	}

	fn update_properties(&self, header: Option<&HashMap<String, String>>) -> HashMap<String, String> {
		let mut hdr = match header {
			Some(h) => h.clone(),
			None => HashMap::new()
		};
		for (k, v) in &self.header {
			if ! hdr.contains_key(k) {
				hdr.insert(k.clone(), v.clone());
			}
		}
		return hdr;
	}

	pub fn send_stream(&mut self, method: Method, path: &str, header: Option<&HashMap<String, String>>) -> Result<BufWriter<T>, Error> {
		let hdr = self.update_properties(header);
		let mut stream = try!(self.connect());
		let mut w = stream.new_writer();
		{
			let mut writer = &mut w;
			try!(writer.write(method.as_bytes()));
			try!(writer.write(b" "));
			try!(writer.write(path.as_bytes()));
			try!(writer.write(b"\r\n"));

			//Write header
			for (k, v) in hdr {
				try!(writer.write(k.as_bytes()));
				try!(writer.write(b": "));
				try!(writer.write(v.as_bytes()));
				try!(writer.write(b"\r\n"));
			}
			try!(writer.write(b"\r\n"));
		}
		return Ok(w);
	}

	pub fn get_reply(&mut self) -> Result<HttpReply<T>, Error> {
		let stream = match self.stream.as_mut() {
			Some(s) => s,
			None => return Err(Error::new(ErrorKind::NotConnected, "Cannot get reply since no stream is opened"))
		};
		return HttpReply::parse(stream.new_reader());
	}

	pub fn send(&mut self, method: Method, path: &str, header: Option<&HashMap<String, String>>, data: Option<&[u8]>) -> Result<HttpReply<T>, Error> {
		{
			let mut hdr = match header {
				Some(h) => h.clone(),
				None => HashMap::new()
			};
			if let Some(d) = data {
				hdr.insert(properties::CONTENT_LENGTH.to_string(), d.len().to_string());
			}
			let mut writer = try!(self.send_stream(method, path, Some(&hdr)));
			if let Some(d) = data {
				try!(writer.write(d));
			}
			try!(writer.flush());
		}
		return self.get_reply();
	}
}<|endoftext|>