<commit_before>use std::fmt;

use xcb;
use xcb_util::{ewmh, icccm};
use xcb_util::keysyms::KeySymbols;

use debug;
use keys::{KeyCombo, KeyHandlers, ModKey};
use groups::Group;
use stack::Stack;

/// A handle to an X Window.
#[derive(Debug, PartialEq)]
pub struct WindowId(xcb::Window);

impl WindowId {
    fn to_x(&self) -> xcb::Window {
        self.0
    }
}

impl fmt::Display for WindowId {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.0)
    }
}

#[allow(non_snake_case)]
struct InternedAtoms {
    WM_DELETE_WINDOW: xcb::Atom,
    WM_PROTOCOLS: xcb::Atom,
    _NET_NUMBER_OF_DESKTOPS: xcb::Atom,
    _NET_CURRENT_DESKTOP: xcb::Atom,
    _NET_DESKTOP_NAMES: xcb::Atom,
}

pub struct Connection {
    conn: ewmh::Connection,
    root: WindowId,
    screen_idx: i32,
    atoms: InternedAtoms,
}

impl Connection {
    /// Opens a connection to the X server, returning a new Connection object.
    pub fn connect() -> Result<Connection, String> {
        let (conn, screen_idx) = xcb::Connection::connect(None).unwrap();
        let conn = ewmh::Connection::connect(conn).map_err(|_| ()).unwrap();
        let root = conn.get_setup()
            .roots()
            .nth(screen_idx as usize)
            .ok_or("Invalid screen")?
            .root();

        let atoms = InternedAtoms {
            WM_PROTOCOLS: Self::intern_atom(&conn, "WM_PROTOCOLS").unwrap(),
            WM_DELETE_WINDOW: Self::intern_atom(&conn, "WM_DELETE_WINDOW").unwrap(),
            _NET_NUMBER_OF_DESKTOPS: Self::intern_atom(&conn, "_NET_NUMBER_OF_DESKTOPS").unwrap(),
            _NET_CURRENT_DESKTOP: Self::intern_atom(&conn, "_NET_CURRENT_DESKTOP").unwrap(),
            _NET_DESKTOP_NAMES: Self::intern_atom(&conn, "_NET_DESKTOP_NAMES").unwrap(),
        };

        Ok(Connection {
            conn,
            root: WindowId(root),
            screen_idx,
            atoms,
        })
    }

    /// Returns the Atom identifier associated with the atom_name str.
    fn intern_atom(
        conn: &xcb::Connection,
        atom_name: &str,
    ) -> Result<xcb::Atom, xcb::GenericError> {
        Ok(xcb::intern_atom(conn, false, atom_name).get_reply()?.atom())
    }

    fn flush(&self) {
        self.conn.flush();
    }

    /// Installs the Connection as a window manager, by registers for
    /// SubstructureNotify and SubstructureRedirect events on the root window.
    /// If there is already a window manager on the display, then this will
    /// fail.
    pub fn install_as_wm(&self, key_handlers: &KeyHandlers) -> Result<(), String> {
        let values = [
            (
                xcb::CW_EVENT_MASK,
                xcb::EVENT_MASK_SUBSTRUCTURE_NOTIFY | xcb::EVENT_MASK_SUBSTRUCTURE_REDIRECT,
            ),
        ];
        xcb::change_window_attributes_checked(&self.conn, self.root.to_x(), &values)
            .request_check()
            .or(Err(
                "Could not register SUBSTRUCTURE_NOTIFY/REDIRECT".to_owned(),
            ))?;

        self.enable_window_key_events(&self.root, key_handlers);

        Ok(())
    }

    /// Returns the ID of the root window.
    pub fn root_window_id(&self) -> &WindowId {
        &self.root
    }

    pub fn update_ewmh_desktops(&self, groups: &Stack<Group>) {
        let group_names = groups.iter().map(|g| g.name());
        ewmh::set_desktop_names(&self.conn, self.screen_idx, group_names);
        ewmh::set_number_of_desktops(&self.conn, self.screen_idx, groups.len() as u32);

        // Matching the current group on name isn't perfect, but it's good enough for
        // EWMH.
        let focused_idx = groups.focused().and_then(|focused| {
            groups.iter().position(|g| g.name() == focused.name())
        });
        match focused_idx {
            Some(idx) => {
                ewmh::set_current_desktop(&self.conn, self.screen_idx, idx as u32);
            }
            None => {
                error!("Invariant: failed to get active group index");
            }
        };
    }

    pub fn top_level_windows(&self) -> Result<Vec<WindowId>, xcb::GenericError> {
        let windows = xcb::query_tree(&self.conn, self.root.to_x())
            .get_reply()?
            .children()
            .iter()
            .map(|w| WindowId(*w))
            .collect();
        Ok(windows)
    }

    /// Queries the WM_PROTOCOLS property of a window, returning a list of the
    /// protocols that it supports.
    // TODO: Have this return a list of atoms, rather than a list of strings.
    // (Perhaps we should
    // have a separate function to convert to a list of strings for debugging?)
    fn get_wm_protocols(&self, window_id: &WindowId) -> Result<Vec<xcb::Atom>, xcb::GenericError> {
        let reply = icccm::get_wm_protocols(&self.conn, window_id.to_x(), self.atoms.WM_PROTOCOLS)
            .get_reply()?;
        Ok(reply.atoms().to_vec())
    }

    /// Closes a window.
    ///
    /// The window will be closed gracefully using the ICCCM WM_DELETE_WINDOW
    /// protocol if it is supported.
    pub fn close_window(&self, window_id: &WindowId) {
        let protocols = self.get_wm_protocols(window_id).unwrap();
        let has_wm_delete_window = protocols.contains(&self.atoms.WM_DELETE_WINDOW);

        // TODO: Use XDestroyWindow to forcefully close windows that do not support
        // WM_DELETE_WINDOW.
        if !has_wm_delete_window {
            panic!("Not implemented: closing windows that don't expose WM_DELETE_WINDOW");
        }

        let data = xcb::ClientMessageData::from_data32(
            [self.atoms.WM_DELETE_WINDOW, xcb::CURRENT_TIME, 0, 0, 0],
        );
        let event =
            xcb::ClientMessageEvent::new(32, window_id.to_x(), self.atoms.WM_PROTOCOLS, data);
        xcb::send_event(
            &self.conn,
            false,
            window_id.to_x(),
            xcb::EVENT_MASK_NO_EVENT,
            &event,
        );
    }

    /// Sets the window's position and size.
    pub fn configure_window(&self, window_id: &WindowId, x: u32, y: u32, width: u32, height: u32) {
        let values = [
            (xcb::CONFIG_WINDOW_X as u16, x),
            (xcb::CONFIG_WINDOW_Y as u16, y),
            (xcb::CONFIG_WINDOW_WIDTH as u16, width),
            (xcb::CONFIG_WINDOW_HEIGHT as u16, height),
        ];
        xcb::configure_window(&self.conn, window_id.to_x(), &values);
    }

    /// Get's the window's width and height.
    pub fn get_window_geometry(&self, window_id: &WindowId) -> (u32, u32) {
        let reply = xcb::get_geometry(&self.conn, window_id.to_x())
            .get_reply()
            .unwrap();
        // Cast as everywhere else uses u32.
        (reply.width() as u32, reply.height() as u32)
    }

    /// Map a window.
    pub fn map_window(&self, window_id: &WindowId) {
        xcb::map_window(&self.conn, window_id.to_x());
    }

    /// Unmap a window.
    pub fn unmap_window(&self, window_id: &WindowId) {
        xcb::unmap_window(&self.conn, window_id.to_x());
    }

    /// Registers for key events.
    pub fn enable_window_key_events(&self, window_id: &WindowId, key_handlers: &KeyHandlers) {
        let key_symbols = KeySymbols::new(&self.conn).expect("Failed to create KeySymbols");
        for key in key_handlers.key_combos() {
            let keycode = key_symbols.get_keycode(key.keysym);
            xcb::grab_key(
                &self.conn,
                false,
                window_id.to_x(),
                key.mod_mask as u16,
                keycode,
                xcb::GRAB_MODE_ASYNC as u8,
                xcb::GRAB_MODE_ASYNC as u8,
            );
        }
    }

    pub fn enable_window_focus_tracking(&self, window_id: &WindowId) {
        let values = [(xcb::CW_EVENT_MASK, xcb::EVENT_MASK_ENTER_WINDOW)];
        xcb::change_window_attributes(&self.conn, window_id.to_x(), &values);
    }

    pub fn disable_window_focus_tracking(&self, window_id: &WindowId) {
        let values = [(xcb::CW_EVENT_MASK, xcb::EVENT_MASK_NO_EVENT)];
        xcb::change_window_attributes(&self.conn, window_id.to_x(), &values);
    }

    pub fn focus_window(&self, window_id: &WindowId) {
        xcb::set_input_focus(
            &self.conn,
            xcb::INPUT_FOCUS_POINTER_ROOT as u8,
            window_id.to_x(),
            xcb::CURRENT_TIME,
        );
    }

    pub fn get_event_loop(&self) -> EventLoop {
        EventLoop { connection: self }
    }
}

/// Events received from the `EventLoop`.
pub enum Event {
    MapRequest(WindowId),
    DestroyNotify(WindowId),
    KeyPress(KeyCombo),
    EnterNotify(WindowId),
}

/// An iterator that yields events from the X event loop.
///
/// Use `Connection::get_event_loop()` to get one.
pub struct EventLoop<'a> {
    connection: &'a Connection,
}

impl<'a> Iterator for EventLoop<'a> {
    type Item = Event;

    fn next(&mut self) -> Option<Self::Item> {
        loop {
            // Flush any pending operations that came out of the event we (might
            // have) just yielded.
            self.connection.flush();

            let event = self.connection.conn.wait_for_event().expect(
                "wait_for_event() returned None: IO error?",
            );

            let propagate = match event.response_type() {
                xcb::CONFIGURE_REQUEST => self.on_configure_request(xcb::cast_event(&event)),
                xcb::MAP_REQUEST => self.on_map_request(xcb::cast_event(&event)),
                xcb::DESTROY_NOTIFY => self.on_destroy_notify(xcb::cast_event(&event)),
                xcb::KEY_PRESS => self.on_key_press(xcb::cast_event(&event)),
                xcb::ENTER_NOTIFY => self.on_enter_notify(xcb::cast_event(&event)),
                _ => {
                    debug!("Unhandled event: {}", debug::xcb_event_to_str(&event));
                    None
                }
            };

            if let Some(propagate_event) = propagate {
                return Some(propagate_event);
            }
        }
    }
}

impl<'a> EventLoop<'a> {
    fn on_configure_request(&self, event: &xcb::ConfigureRequestEvent) -> Option<Event> {
        // This request is not interesting for us: grant it unchanged.
        // Build a request with all attributes set, then filter out to only include
        // those from the original request.
        let values = vec![
            (xcb::CONFIG_WINDOW_X as u16, event.x() as u32),
            (xcb::CONFIG_WINDOW_Y as u16, event.y() as u32),
            (xcb::CONFIG_WINDOW_WIDTH as u16, event.width() as u32),
            (xcb::CONFIG_WINDOW_HEIGHT as u16, event.height() as u32),
            (
                xcb::CONFIG_WINDOW_BORDER_WIDTH as u16,
                event.border_width() as u32
            ),
            (xcb::CONFIG_WINDOW_SIBLING as u16, event.sibling() as u32),
            (
                xcb::CONFIG_WINDOW_STACK_MODE as u16,
                event.stack_mode() as u32
            ),
        ];
        let filtered_values: Vec<_> = values
            .into_iter()
            .filter(|&(mask, _)| mask & event.value_mask() != 0)
            .collect();
        xcb::configure_window(&self.connection.conn, event.window(), &filtered_values);

        // There's no value in propogating this event.
        None
    }

    fn on_map_request(&self, event: &xcb::MapRequestEvent) -> Option<Event> {
        Some(Event::MapRequest(WindowId(event.window())))
    }

    fn on_destroy_notify(&self, event: &xcb::DestroyNotifyEvent) -> Option<Event> {
        Some(Event::DestroyNotify(WindowId(event.window())))
    }

    fn on_key_press(&self, event: &xcb::KeyPressEvent) -> Option<Event> {
        let key_symbols =
            KeySymbols::new(&self.connection.conn).expect("Failed to create KeySymbols");
        let keysym = key_symbols.key_press_lookup_keysym(event, 0);
        let mod_mask = event.state() as u32 & ModKey::mask_all();
        let key = KeyCombo { mod_mask, keysym };
        Some(Event::KeyPress(key))
    }

    fn on_enter_notify(&self, event: &xcb::EnterNotifyEvent) -> Option<Event> {
        Some(Event::EnterNotify(WindowId(event.event())))
    }
}
<commit_msg>Set EWMH _NET_WM_ACTIVE_WINDOW.<commit_after>use std::fmt;

use xcb;
use xcb_util::{ewmh, icccm};
use xcb_util::keysyms::KeySymbols;

use debug;
use keys::{KeyCombo, KeyHandlers, ModKey};
use groups::Group;
use stack::Stack;

/// A handle to an X Window.
#[derive(Debug, PartialEq)]
pub struct WindowId(xcb::Window);

impl WindowId {
    fn to_x(&self) -> xcb::Window {
        self.0
    }
}

impl fmt::Display for WindowId {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.0)
    }
}

#[allow(non_snake_case)]
struct InternedAtoms {
    WM_DELETE_WINDOW: xcb::Atom,
    WM_PROTOCOLS: xcb::Atom,
    _NET_NUMBER_OF_DESKTOPS: xcb::Atom,
    _NET_CURRENT_DESKTOP: xcb::Atom,
    _NET_DESKTOP_NAMES: xcb::Atom,
}

pub struct Connection {
    conn: ewmh::Connection,
    root: WindowId,
    screen_idx: i32,
    atoms: InternedAtoms,
}

impl Connection {
    /// Opens a connection to the X server, returning a new Connection object.
    pub fn connect() -> Result<Connection, String> {
        let (conn, screen_idx) = xcb::Connection::connect(None).unwrap();
        let conn = ewmh::Connection::connect(conn).map_err(|_| ()).unwrap();
        let root = conn.get_setup()
            .roots()
            .nth(screen_idx as usize)
            .ok_or("Invalid screen")?
            .root();

        let atoms = InternedAtoms {
            WM_PROTOCOLS: Self::intern_atom(&conn, "WM_PROTOCOLS").unwrap(),
            WM_DELETE_WINDOW: Self::intern_atom(&conn, "WM_DELETE_WINDOW").unwrap(),
            _NET_NUMBER_OF_DESKTOPS: Self::intern_atom(&conn, "_NET_NUMBER_OF_DESKTOPS").unwrap(),
            _NET_CURRENT_DESKTOP: Self::intern_atom(&conn, "_NET_CURRENT_DESKTOP").unwrap(),
            _NET_DESKTOP_NAMES: Self::intern_atom(&conn, "_NET_DESKTOP_NAMES").unwrap(),
        };

        Ok(Connection {
            conn,
            root: WindowId(root),
            screen_idx,
            atoms,
        })
    }

    /// Returns the Atom identifier associated with the atom_name str.
    fn intern_atom(
        conn: &xcb::Connection,
        atom_name: &str,
    ) -> Result<xcb::Atom, xcb::GenericError> {
        Ok(xcb::intern_atom(conn, false, atom_name).get_reply()?.atom())
    }

    fn flush(&self) {
        self.conn.flush();
    }

    /// Installs the Connection as a window manager, by registers for
    /// SubstructureNotify and SubstructureRedirect events on the root window.
    /// If there is already a window manager on the display, then this will
    /// fail.
    pub fn install_as_wm(&self, key_handlers: &KeyHandlers) -> Result<(), String> {
        let values = [
            (
                xcb::CW_EVENT_MASK,
                xcb::EVENT_MASK_SUBSTRUCTURE_NOTIFY | xcb::EVENT_MASK_SUBSTRUCTURE_REDIRECT,
            ),
        ];
        xcb::change_window_attributes_checked(&self.conn, self.root.to_x(), &values)
            .request_check()
            .or(Err(
                "Could not register SUBSTRUCTURE_NOTIFY/REDIRECT".to_owned(),
            ))?;

        self.enable_window_key_events(&self.root, key_handlers);

        Ok(())
    }

    /// Returns the ID of the root window.
    pub fn root_window_id(&self) -> &WindowId {
        &self.root
    }

    pub fn update_ewmh_desktops(&self, groups: &Stack<Group>) {
        let group_names = groups.iter().map(|g| g.name());
        ewmh::set_desktop_names(&self.conn, self.screen_idx, group_names);
        ewmh::set_number_of_desktops(&self.conn, self.screen_idx, groups.len() as u32);

        // Matching the current group on name isn't perfect, but it's good enough for
        // EWMH.
        let focused_idx = groups.focused().and_then(|focused| {
            groups.iter().position(|g| g.name() == focused.name())
        });
        match focused_idx {
            Some(idx) => {
                ewmh::set_current_desktop(&self.conn, self.screen_idx, idx as u32);
            }
            None => {
                error!("Invariant: failed to get active group index");
            }
        };
    }

    pub fn top_level_windows(&self) -> Result<Vec<WindowId>, xcb::GenericError> {
        let windows = xcb::query_tree(&self.conn, self.root.to_x())
            .get_reply()?
            .children()
            .iter()
            .map(|w| WindowId(*w))
            .collect();
        Ok(windows)
    }

    /// Queries the WM_PROTOCOLS property of a window, returning a list of the
    /// protocols that it supports.
    // TODO: Have this return a list of atoms, rather than a list of strings.
    // (Perhaps we should
    // have a separate function to convert to a list of strings for debugging?)
    fn get_wm_protocols(&self, window_id: &WindowId) -> Result<Vec<xcb::Atom>, xcb::GenericError> {
        let reply = icccm::get_wm_protocols(&self.conn, window_id.to_x(), self.atoms.WM_PROTOCOLS)
            .get_reply()?;
        Ok(reply.atoms().to_vec())
    }

    /// Closes a window.
    ///
    /// The window will be closed gracefully using the ICCCM WM_DELETE_WINDOW
    /// protocol if it is supported.
    pub fn close_window(&self, window_id: &WindowId) {
        let protocols = self.get_wm_protocols(window_id).unwrap();
        let has_wm_delete_window = protocols.contains(&self.atoms.WM_DELETE_WINDOW);

        // TODO: Use XDestroyWindow to forcefully close windows that do not support
        // WM_DELETE_WINDOW.
        if !has_wm_delete_window {
            panic!("Not implemented: closing windows that don't expose WM_DELETE_WINDOW");
        }

        let data = xcb::ClientMessageData::from_data32(
            [self.atoms.WM_DELETE_WINDOW, xcb::CURRENT_TIME, 0, 0, 0],
        );
        let event =
            xcb::ClientMessageEvent::new(32, window_id.to_x(), self.atoms.WM_PROTOCOLS, data);
        xcb::send_event(
            &self.conn,
            false,
            window_id.to_x(),
            xcb::EVENT_MASK_NO_EVENT,
            &event,
        );
    }

    /// Sets the window's position and size.
    pub fn configure_window(&self, window_id: &WindowId, x: u32, y: u32, width: u32, height: u32) {
        let values = [
            (xcb::CONFIG_WINDOW_X as u16, x),
            (xcb::CONFIG_WINDOW_Y as u16, y),
            (xcb::CONFIG_WINDOW_WIDTH as u16, width),
            (xcb::CONFIG_WINDOW_HEIGHT as u16, height),
        ];
        xcb::configure_window(&self.conn, window_id.to_x(), &values);
    }

    /// Get's the window's width and height.
    pub fn get_window_geometry(&self, window_id: &WindowId) -> (u32, u32) {
        let reply = xcb::get_geometry(&self.conn, window_id.to_x())
            .get_reply()
            .unwrap();
        // Cast as everywhere else uses u32.
        (reply.width() as u32, reply.height() as u32)
    }

    /// Map a window.
    pub fn map_window(&self, window_id: &WindowId) {
        xcb::map_window(&self.conn, window_id.to_x());
    }

    /// Unmap a window.
    pub fn unmap_window(&self, window_id: &WindowId) {
        xcb::unmap_window(&self.conn, window_id.to_x());
    }

    /// Registers for key events.
    pub fn enable_window_key_events(&self, window_id: &WindowId, key_handlers: &KeyHandlers) {
        let key_symbols = KeySymbols::new(&self.conn).expect("Failed to create KeySymbols");
        for key in key_handlers.key_combos() {
            let keycode = key_symbols.get_keycode(key.keysym);
            xcb::grab_key(
                &self.conn,
                false,
                window_id.to_x(),
                key.mod_mask as u16,
                keycode,
                xcb::GRAB_MODE_ASYNC as u8,
                xcb::GRAB_MODE_ASYNC as u8,
            );
        }
    }

    pub fn enable_window_focus_tracking(&self, window_id: &WindowId) {
        let values = [(xcb::CW_EVENT_MASK, xcb::EVENT_MASK_ENTER_WINDOW)];
        xcb::change_window_attributes(&self.conn, window_id.to_x(), &values);
    }

    pub fn disable_window_focus_tracking(&self, window_id: &WindowId) {
        let values = [(xcb::CW_EVENT_MASK, xcb::EVENT_MASK_NO_EVENT)];
        xcb::change_window_attributes(&self.conn, window_id.to_x(), &values);
    }

    pub fn focus_window(&self, window_id: &WindowId) {
        xcb::set_input_focus(
            &self.conn,
            xcb::INPUT_FOCUS_POINTER_ROOT as u8,
            window_id.to_x(),
            xcb::CURRENT_TIME,
        );
        ewmh::set_active_window(&self.conn, self.screen_idx, window_id.to_x());
    }

    pub fn get_event_loop(&self) -> EventLoop {
        EventLoop { connection: self }
    }
}

/// Events received from the `EventLoop`.
pub enum Event {
    MapRequest(WindowId),
    DestroyNotify(WindowId),
    KeyPress(KeyCombo),
    EnterNotify(WindowId),
}

/// An iterator that yields events from the X event loop.
///
/// Use `Connection::get_event_loop()` to get one.
pub struct EventLoop<'a> {
    connection: &'a Connection,
}

impl<'a> Iterator for EventLoop<'a> {
    type Item = Event;

    fn next(&mut self) -> Option<Self::Item> {
        loop {
            // Flush any pending operations that came out of the event we (might
            // have) just yielded.
            self.connection.flush();

            let event = self.connection.conn.wait_for_event().expect(
                "wait_for_event() returned None: IO error?",
            );

            let propagate = match event.response_type() {
                xcb::CONFIGURE_REQUEST => self.on_configure_request(xcb::cast_event(&event)),
                xcb::MAP_REQUEST => self.on_map_request(xcb::cast_event(&event)),
                xcb::DESTROY_NOTIFY => self.on_destroy_notify(xcb::cast_event(&event)),
                xcb::KEY_PRESS => self.on_key_press(xcb::cast_event(&event)),
                xcb::ENTER_NOTIFY => self.on_enter_notify(xcb::cast_event(&event)),
                _ => {
                    debug!("Unhandled event: {}", debug::xcb_event_to_str(&event));
                    None
                }
            };

            if let Some(propagate_event) = propagate {
                return Some(propagate_event);
            }
        }
    }
}

impl<'a> EventLoop<'a> {
    fn on_configure_request(&self, event: &xcb::ConfigureRequestEvent) -> Option<Event> {
        // This request is not interesting for us: grant it unchanged.
        // Build a request with all attributes set, then filter out to only include
        // those from the original request.
        let values = vec![
            (xcb::CONFIG_WINDOW_X as u16, event.x() as u32),
            (xcb::CONFIG_WINDOW_Y as u16, event.y() as u32),
            (xcb::CONFIG_WINDOW_WIDTH as u16, event.width() as u32),
            (xcb::CONFIG_WINDOW_HEIGHT as u16, event.height() as u32),
            (
                xcb::CONFIG_WINDOW_BORDER_WIDTH as u16,
                event.border_width() as u32
            ),
            (xcb::CONFIG_WINDOW_SIBLING as u16, event.sibling() as u32),
            (
                xcb::CONFIG_WINDOW_STACK_MODE as u16,
                event.stack_mode() as u32
            ),
        ];
        let filtered_values: Vec<_> = values
            .into_iter()
            .filter(|&(mask, _)| mask & event.value_mask() != 0)
            .collect();
        xcb::configure_window(&self.connection.conn, event.window(), &filtered_values);

        // There's no value in propogating this event.
        None
    }

    fn on_map_request(&self, event: &xcb::MapRequestEvent) -> Option<Event> {
        Some(Event::MapRequest(WindowId(event.window())))
    }

    fn on_destroy_notify(&self, event: &xcb::DestroyNotifyEvent) -> Option<Event> {
        Some(Event::DestroyNotify(WindowId(event.window())))
    }

    fn on_key_press(&self, event: &xcb::KeyPressEvent) -> Option<Event> {
        let key_symbols =
            KeySymbols::new(&self.connection.conn).expect("Failed to create KeySymbols");
        let keysym = key_symbols.key_press_lookup_keysym(event, 0);
        let mod_mask = event.state() as u32 & ModKey::mask_all();
        let key = KeyCombo { mod_mask, keysym };
        Some(Event::KeyPress(key))
    }

    fn on_enter_notify(&self, event: &xcb::EnterNotifyEvent) -> Option<Event> {
        Some(Event::EnterNotify(WindowId(event.event())))
    }
}
<|endoftext|>


<commit_before>/*
Copyright (c) 2016-2017, Robert Ou <rqou@robertou.com> and contributors
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice,
   this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
   this list of conditions and the following disclaimer in the documentation
   and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

// ZIA

pub struct XC2ZIARowPiece {
    what: Option<u32>,
}
<commit_msg>xc2bit: Transcribe 32/32A ZIA map<commit_after>/*
Copyright (c) 2016-2017, Robert Ou <rqou@robertou.com> and contributors
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice,
   this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
   this list of conditions and the following disclaimer in the documentation
   and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

// ZIA

pub struct XC2ZIARowPiece {
    selected: Option<XC2ZIAInput>,
}

pub enum XC2ZIAInput {
    Macrocell {
        fb: u32,
        ff: u32,
    },
    IBuf {
        ibuf: u32,
    },
    Zero,
    One,
}

static ZIA_BIT_TO_CHOICE_32: [[XC2ZIAInput; 6]; 40] = [
    // Row 0
    [XC2ZIAInput::IBuf{ibuf: 0},
     XC2ZIAInput::IBuf{ibuf: 10},
     XC2ZIAInput::IBuf{ibuf: 21},
     XC2ZIAInput::Macrocell{fb: 0, ff: 1},
     XC2ZIAInput::Macrocell{fb: 0, ff: 13},
     XC2ZIAInput::Macrocell{fb: 1, ff: 9}],

    [XC2ZIAInput::IBuf{ibuf: 1},
     XC2ZIAInput::IBuf{ibuf: 11},
     XC2ZIAInput::IBuf{ibuf: 22},
     XC2ZIAInput::Macrocell{fb: 0, ff: 8},
     XC2ZIAInput::Macrocell{fb: 0, ff: 15},
     XC2ZIAInput::Macrocell{fb: 1, ff: 12}],

    [XC2ZIAInput::IBuf{ibuf: 2},
     XC2ZIAInput::IBuf{ibuf: 12},
     XC2ZIAInput::IBuf{ibuf: 29},
     XC2ZIAInput::Macrocell{fb: 0, ff: 2},
     XC2ZIAInput::Macrocell{fb: 1, ff: 4},
     XC2ZIAInput::Macrocell{fb: 1, ff: 11}],

    [XC2ZIAInput::IBuf{ibuf: 3},
     XC2ZIAInput::IBuf{ibuf: 13},
     XC2ZIAInput::IBuf{ibuf: 25},
     XC2ZIAInput::Macrocell{fb: 0, ff: 9},
     XC2ZIAInput::Macrocell{fb: 0, ff: 14},
     XC2ZIAInput::Macrocell{fb: 1, ff: 6}],

    [XC2ZIAInput::IBuf{ibuf: 4},
     XC2ZIAInput::IBuf{ibuf: 14},
     XC2ZIAInput::IBuf{ibuf: 27},
     XC2ZIAInput::Macrocell{fb: 0, ff: 5},
     XC2ZIAInput::Macrocell{fb: 0, ff: 11},
     XC2ZIAInput::Macrocell{fb: 1, ff: 10}],
    // Row 5
    [XC2ZIAInput::IBuf{ibuf: 5},
     XC2ZIAInput::IBuf{ibuf: 15},
     XC2ZIAInput::IBuf{ibuf: 30},
     XC2ZIAInput::Macrocell{fb: 0, ff: 7},
     XC2ZIAInput::Macrocell{fb: 1, ff: 1},
     XC2ZIAInput::Macrocell{fb: 1, ff: 7}],

    [XC2ZIAInput::IBuf{ibuf: 6},
     XC2ZIAInput::IBuf{ibuf: 32},
     XC2ZIAInput::IBuf{ibuf: 20},
     XC2ZIAInput::Macrocell{fb: 0, ff: 0},
     XC2ZIAInput::Macrocell{fb: 1, ff: 3},
     XC2ZIAInput::Macrocell{fb: 1, ff: 13}],

    [XC2ZIAInput::IBuf{ibuf: 7},
     XC2ZIAInput::IBuf{ibuf: 16},
     XC2ZIAInput::IBuf{ibuf: 26},
     XC2ZIAInput::IBuf{ibuf: 31},
     XC2ZIAInput::Macrocell{fb: 0, ff: 12},
     XC2ZIAInput::Macrocell{fb: 1, ff: 15}],

    [XC2ZIAInput::IBuf{ibuf: 8},
     XC2ZIAInput::IBuf{ibuf: 17},
     XC2ZIAInput::IBuf{ibuf: 24},
     XC2ZIAInput::Macrocell{fb: 0, ff: 6},
     XC2ZIAInput::Macrocell{fb: 0, ff: 10},
     XC2ZIAInput::Macrocell{fb: 1, ff: 8}],

    [XC2ZIAInput::IBuf{ibuf: 9},
     XC2ZIAInput::IBuf{ibuf: 18},
     XC2ZIAInput::IBuf{ibuf: 23},
     XC2ZIAInput::Macrocell{fb: 0, ff: 4},
     XC2ZIAInput::Macrocell{fb: 1, ff: 2},
     XC2ZIAInput::Macrocell{fb: 1, ff: 5}],
    // Row 10
    [XC2ZIAInput::IBuf{ibuf: 7},
     XC2ZIAInput::IBuf{ibuf: 19},
     XC2ZIAInput::IBuf{ibuf: 28},
     XC2ZIAInput::Macrocell{fb: 0, ff: 3},
     XC2ZIAInput::Macrocell{fb: 1, ff: 0},
     XC2ZIAInput::Macrocell{fb: 1, ff: 14}],

    [XC2ZIAInput::IBuf{ibuf: 0},
     XC2ZIAInput::IBuf{ibuf: 11},
     XC2ZIAInput::IBuf{ibuf: 22},
     XC2ZIAInput::Macrocell{fb: 0, ff: 2},
     XC2ZIAInput::Macrocell{fb: 0, ff: 14},
     XC2ZIAInput::Macrocell{fb: 1, ff: 10}],

    [XC2ZIAInput::IBuf{ibuf: 1},
     XC2ZIAInput::IBuf{ibuf: 12},
     XC2ZIAInput::IBuf{ibuf: 28},
     XC2ZIAInput::Macrocell{fb: 0, ff: 4},
     XC2ZIAInput::Macrocell{fb: 1, ff: 1},
     XC2ZIAInput::Macrocell{fb: 1, ff: 15}],

    [XC2ZIAInput::IBuf{ibuf: 2},
     XC2ZIAInput::IBuf{ibuf: 18},
     XC2ZIAInput::IBuf{ibuf: 23},
     XC2ZIAInput::Macrocell{fb: 0, ff: 9},
     XC2ZIAInput::Macrocell{fb: 1, ff: 0},
     XC2ZIAInput::Macrocell{fb: 1, ff: 13}],

    [XC2ZIAInput::IBuf{ibuf: 3},
     XC2ZIAInput::IBuf{ibuf: 15},
     XC2ZIAInput::IBuf{ibuf: 30},
     XC2ZIAInput::Macrocell{fb: 0, ff: 3},
     XC2ZIAInput::Macrocell{fb: 0, ff: 11},
     XC2ZIAInput::Macrocell{fb: 1, ff: 12}],
    // Row 15
    [XC2ZIAInput::IBuf{ibuf: 4},
     XC2ZIAInput::IBuf{ibuf: 16},
     XC2ZIAInput::IBuf{ibuf: 21},
     XC2ZIAInput::Macrocell{fb: 0, ff: 0},
     XC2ZIAInput::Macrocell{fb: 0, ff: 15},
     XC2ZIAInput::Macrocell{fb: 1, ff: 7}],

    [XC2ZIAInput::IBuf{ibuf: 5},
     XC2ZIAInput::IBuf{ibuf: 19},
     XC2ZIAInput::IBuf{ibuf: 28},
     XC2ZIAInput::Macrocell{fb: 0, ff: 6},
     XC2ZIAInput::Macrocell{fb: 0, ff: 12},
     XC2ZIAInput::Macrocell{fb: 1, ff: 11}],

    [XC2ZIAInput::IBuf{ibuf: 6},
     XC2ZIAInput::IBuf{ibuf: 10},
     XC2ZIAInput::IBuf{ibuf: 21},
     XC2ZIAInput::Macrocell{fb: 0, ff: 8},
     XC2ZIAInput::Macrocell{fb: 1, ff: 2},
     XC2ZIAInput::Macrocell{fb: 1, ff: 8}],

    [XC2ZIAInput::IBuf{ibuf: 7},
     XC2ZIAInput::IBuf{ibuf: 32},
     XC2ZIAInput::IBuf{ibuf: 20},
     XC2ZIAInput::Macrocell{fb: 0, ff: 1},
     XC2ZIAInput::Macrocell{fb: 1, ff: 4},
     XC2ZIAInput::Macrocell{fb: 1, ff: 14}],

    [XC2ZIAInput::IBuf{ibuf: 8},
     XC2ZIAInput::IBuf{ibuf: 14},
     XC2ZIAInput::IBuf{ibuf: 27},
     XC2ZIAInput::IBuf{ibuf: 31},
     XC2ZIAInput::Macrocell{fb: 0, ff: 13},
     XC2ZIAInput::Macrocell{fb: 1, ff: 6}],
    // Row 20
    [XC2ZIAInput::IBuf{ibuf: 9},
     XC2ZIAInput::IBuf{ibuf: 13},
     XC2ZIAInput::IBuf{ibuf: 25},
     XC2ZIAInput::Macrocell{fb: 0, ff: 7},
     XC2ZIAInput::Macrocell{fb: 0, ff: 10},
     XC2ZIAInput::Macrocell{fb: 1, ff: 9}],

    [XC2ZIAInput::IBuf{ibuf: 8},
     XC2ZIAInput::IBuf{ibuf: 17},
     XC2ZIAInput::IBuf{ibuf: 24},
     XC2ZIAInput::Macrocell{fb: 0, ff: 5},
     XC2ZIAInput::Macrocell{fb: 1, ff: 3},
     XC2ZIAInput::Macrocell{fb: 1, ff: 5}],

    [XC2ZIAInput::IBuf{ibuf: 0},
     XC2ZIAInput::IBuf{ibuf: 12},
     XC2ZIAInput::IBuf{ibuf: 23},
     XC2ZIAInput::Macrocell{fb: 0, ff: 3},
     XC2ZIAInput::Macrocell{fb: 0, ff: 15},
     XC2ZIAInput::Macrocell{fb: 1, ff: 11}],

    [XC2ZIAInput::IBuf{ibuf: 1},
     XC2ZIAInput::IBuf{ibuf: 18},
     XC2ZIAInput::IBuf{ibuf: 25},
     XC2ZIAInput::Macrocell{fb: 0, ff: 6},
     XC2ZIAInput::Macrocell{fb: 1, ff: 4},
     XC2ZIAInput::Macrocell{fb: 1, ff: 5}],

    [XC2ZIAInput::IBuf{ibuf: 2},
     XC2ZIAInput::IBuf{ibuf: 13},
     XC2ZIAInput::IBuf{ibuf: 30},
     XC2ZIAInput::Macrocell{fb: 0, ff: 5},
     XC2ZIAInput::Macrocell{fb: 1, ff: 2},
     XC2ZIAInput::Macrocell{fb: 1, ff: 6}],
    // Row 25
    [XC2ZIAInput::IBuf{ibuf: 3},
     XC2ZIAInput::IBuf{ibuf: 19},
     XC2ZIAInput::IBuf{ibuf: 24},
     XC2ZIAInput::Macrocell{fb: 0, ff: 0},
     XC2ZIAInput::Macrocell{fb: 1, ff: 1},
     XC2ZIAInput::Macrocell{fb: 1, ff: 14}],

    [XC2ZIAInput::IBuf{ibuf: 4},
     XC2ZIAInput::IBuf{ibuf: 32},
     XC2ZIAInput::IBuf{ibuf: 21},
     XC2ZIAInput::Macrocell{fb: 0, ff: 4},
     XC2ZIAInput::Macrocell{fb: 0, ff: 12},
     XC2ZIAInput::Macrocell{fb: 1, ff: 13}],

    [XC2ZIAInput::IBuf{ibuf: 5},
     XC2ZIAInput::IBuf{ibuf: 17},
     XC2ZIAInput::IBuf{ibuf: 27},
     XC2ZIAInput::Macrocell{fb: 0, ff: 1},
     XC2ZIAInput::Macrocell{fb: 1, ff: 0},
     XC2ZIAInput::Macrocell{fb: 1, ff: 8}],

    [XC2ZIAInput::IBuf{ibuf: 6},
     XC2ZIAInput::IBuf{ibuf: 11},
     XC2ZIAInput::IBuf{ibuf: 29},
     XC2ZIAInput::Macrocell{fb: 0, ff: 7},
     XC2ZIAInput::Macrocell{fb: 0, ff: 13},
     XC2ZIAInput::Macrocell{fb: 1, ff: 12}],

    [XC2ZIAInput::IBuf{ibuf: 7},
     XC2ZIAInput::IBuf{ibuf: 10},
     XC2ZIAInput::IBuf{ibuf: 22},
     XC2ZIAInput::Macrocell{fb: 0, ff: 9},
     XC2ZIAInput::Macrocell{fb: 1, ff: 3},
     XC2ZIAInput::Macrocell{fb: 1, ff: 9}],
    // Row 30
    [XC2ZIAInput::IBuf{ibuf: 8},
     XC2ZIAInput::IBuf{ibuf: 16},
     XC2ZIAInput::IBuf{ibuf: 20},
     XC2ZIAInput::Macrocell{fb: 0, ff: 2},
     XC2ZIAInput::Macrocell{fb: 0, ff: 11},
     XC2ZIAInput::Macrocell{fb: 1, ff: 15}],

    [XC2ZIAInput::IBuf{ibuf: 9},
     XC2ZIAInput::IBuf{ibuf: 15},
     XC2ZIAInput::IBuf{ibuf: 28},
     XC2ZIAInput::IBuf{ibuf: 31},
     XC2ZIAInput::Macrocell{fb: 0, ff: 14},
     XC2ZIAInput::Macrocell{fb: 1, ff: 7}],

    [XC2ZIAInput::IBuf{ibuf: 9},
     XC2ZIAInput::IBuf{ibuf: 14},
     XC2ZIAInput::IBuf{ibuf: 21},
     XC2ZIAInput::Macrocell{fb: 0, ff: 8},
     XC2ZIAInput::Macrocell{fb: 0, ff: 10},
     XC2ZIAInput::Macrocell{fb: 1, ff: 10}],

    [XC2ZIAInput::IBuf{ibuf: 0},
     XC2ZIAInput::IBuf{ibuf: 13},
     XC2ZIAInput::IBuf{ibuf: 24},
     XC2ZIAInput::Macrocell{fb: 0, ff: 4},
     XC2ZIAInput::Macrocell{fb: 1, ff: 0},
     XC2ZIAInput::Macrocell{fb: 1, ff: 12}],

    [XC2ZIAInput::IBuf{ibuf: 1},
     XC2ZIAInput::IBuf{ibuf: 15},
     XC2ZIAInput::IBuf{ibuf: 27},
     XC2ZIAInput::Macrocell{fb: 0, ff: 9},
     XC2ZIAInput::Macrocell{fb: 0, ff: 10},
     XC2ZIAInput::Macrocell{fb: 1, ff: 11}],
    // Row 35
    [XC2ZIAInput::IBuf{ibuf: 2},
     XC2ZIAInput::IBuf{ibuf: 19},
     XC2ZIAInput::IBuf{ibuf: 21},
     XC2ZIAInput::Macrocell{fb: 0, ff: 7},
     XC2ZIAInput::Macrocell{fb: 0, ff: 11},
     XC2ZIAInput::Macrocell{fb: 1, ff: 5}],

    [XC2ZIAInput::IBuf{ibuf: 3},
     XC2ZIAInput::IBuf{ibuf: 14},
     XC2ZIAInput::IBuf{ibuf: 21},
     XC2ZIAInput::Macrocell{fb: 0, ff: 6},
     XC2ZIAInput::Macrocell{fb: 1, ff: 3},
     XC2ZIAInput::Macrocell{fb: 1, ff: 7}],

    [XC2ZIAInput::IBuf{ibuf: 4},
     XC2ZIAInput::IBuf{ibuf: 11},
     XC2ZIAInput::IBuf{ibuf: 25},
     XC2ZIAInput::Macrocell{fb: 0, ff: 1},
     XC2ZIAInput::Macrocell{fb: 1, ff: 2},
     XC2ZIAInput::Macrocell{fb: 1, ff: 15}],

    [XC2ZIAInput::IBuf{ibuf: 5},
     XC2ZIAInput::IBuf{ibuf: 16},
     XC2ZIAInput::IBuf{ibuf: 22},
     XC2ZIAInput::Macrocell{fb: 0, ff: 5},
     XC2ZIAInput::Macrocell{fb: 0, ff: 13},
     XC2ZIAInput::Macrocell{fb: 1, ff: 14}],

    [XC2ZIAInput::IBuf{ibuf: 6},
     XC2ZIAInput::IBuf{ibuf: 18},
     XC2ZIAInput::IBuf{ibuf: 28},
     XC2ZIAInput::Macrocell{fb: 0, ff: 2},
     XC2ZIAInput::Macrocell{fb: 1, ff: 1},
     XC2ZIAInput::Macrocell{fb: 1, ff: 9}],
];

// Read a piece of the ZIA corresponding to one FB and one row
pub fn read_32_zia_fb_row_logical(fuses: &[bool], block_idx: usize, row_idx: usize)
    -> Result<XC2ZIARowPiece, &'static str> {

    Err("err")
}
<|endoftext|>


<commit_before>extern crate gauc;

use gauc::client::*;
use gauc::couchbase::types::error_type::ErrorType;
use gauc::couchbase::types::operation::Operation;

const DEFAULT_CONNECTION_STRING: &'static str = "couchbase://localhost/default";

#[test]
fn it_connects() {
    let client = Client::new(DEFAULT_CONNECTION_STRING);
    assert!(client.opts.version() == 3);
}

#[test]
fn it_fails_when_getting_nonexisting_document() {
    let mut client = Client::new(DEFAULT_CONNECTION_STRING);

    // Get data
    client.get("non-existing-key", |res| {
        if let Err(response) = res {
            let (_response, error) = response;
            assert!(error.to_string() == "The key does not exist on the server");
        }
    });
}

#[test]
fn it_stores_document() {
    let mut client = Client::new(DEFAULT_CONNECTION_STRING);

    // Store some data
    client.store("foo", "{\"msg\": \"This is test!\"}", Operation::Upsert, |res| {
        if let Ok(response) = res {
            assert!(response.rc == ErrorType::Success);
            println!("Created new document, CAS: {}", response.cas)
        }
    });
}
<commit_msg>Fix tests<commit_after>extern crate gauc;

use gauc::client::*;
use gauc::couchbase::types::error_type::ErrorType;
use gauc::couchbase::types::operation::Operation;

const DEFAULT_CONNECTION_STRING: &'static str = "couchbase://localhost/default";

#[test]
fn it_connects() {
    let client = Client::new(DEFAULT_CONNECTION_STRING);
    assert!(client.opts.lock().unwrap().version() == 3);
}

#[test]
fn it_fails_when_getting_nonexisting_document() {
    let mut client = Client::new(DEFAULT_CONNECTION_STRING);

    // Get data
    client.get("non-existing-key", |res| {
        if let Err(response) = res {
            let (_response, error) = response;
            assert!(error.to_string() == "The key does not exist on the server");
        }
    });
}

#[test]
fn it_stores_document() {
    let mut client = Client::new(DEFAULT_CONNECTION_STRING);

    // Store some data
    client.store("foo", "{\"msg\": \"This is test!\"}", Operation::Upsert, |res| {
        if let Ok(response) = res {
            assert!(response.rc == ErrorType::Success);
            println!("Created new document, CAS: {}", response.cas)
        }
    });
}
<|endoftext|>


<commit_before>use bytes::{Buf, BufMut, IntoBuf};
use futures::{Async, Future, Poll};
use hyper::client::{HttpConnector, Service};
use hyper::Uri;
use hyper_tls::{HttpsConnector, MaybeHttpsStream};
use native_tls::TlsConnector;
use tokio_core::reactor::Handle;
use tokio_io::{AsyncRead, AsyncWrite};
use tokio_tls::{TlsConnectorExt, TlsStream};

use std::io::{self, Cursor, Read, Write};
use std::sync::Arc;

use {proxy, Proxy};

// pub(crate)

pub struct Connector {
    https: HttpsConnector<HttpConnector>,
    proxies: Arc<Vec<Proxy>>,
    tls: TlsConnector,
}

impl Connector {
    pub fn new(tls: TlsConnector, proxies: Arc<Vec<Proxy>>, handle: &Handle) -> Connector {
        let mut http = HttpConnector::new(4, handle);
        http.enforce_http(false);
        let https = HttpsConnector::from((http, tls.clone()));

        Connector {
            https: https,
            proxies: proxies,
            tls: tls,
        }
    }

    pub fn danger_disable_hostname_verification(&mut self) {
        self.https.danger_disable_hostname_verification(true);
    }
}

impl Service for Connector {
    type Request = Uri;
    type Response = Conn;
    type Error = io::Error;
    type Future = Connecting;

    fn call(&self, uri: Uri) -> Self::Future {
        for prox in self.proxies.iter() {
            if let Some(puri) = proxy::proxies(prox, &uri) {
                if uri.scheme() == Some("https") {
                    let host = uri.host().unwrap().to_owned();
                    let port = match uri.port(){
                        Some(port) => port.to_string(),
                        None => 443.to_string()
                    }.to_owned();
                    let tls = self.tls.clone();
                    return Box::new(self.https.call(puri).and_then(move |conn| {
                        tunnel(conn, host.clone(), port.clone())
                            .and_then(move |tunneled| {
                                tls.connect_async(&host, tunneled)
                                    .map_err(|e| io::Error::new(io::ErrorKind::Other, e))
                            })
                            .map(|io| Conn::Proxied(io))
                    }));
                }
                return Box::new(self.https.call(puri).map(|io| Conn::Normal(io)));
            }
        }
        Box::new(self.https.call(uri).map(|io| Conn::Normal(io)))
    }
}

type HttpStream = <HttpConnector as Service>::Response;
type HttpsStream = MaybeHttpsStream<HttpStream>;

pub type Connecting = Box<Future<Item=Conn, Error=io::Error>>;

pub enum Conn {
    Normal(HttpsStream),
    Proxied(TlsStream<MaybeHttpsStream<HttpStream>>),
}

impl Read for Conn {
    #[inline]
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        match *self {
            Conn::Normal(ref mut s) => s.read(buf),
            Conn::Proxied(ref mut s) => s.read(buf),
        }
    }
}

impl Write for Conn {
    #[inline]
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        match *self {
            Conn::Normal(ref mut s) => s.write(buf),
            Conn::Proxied(ref mut s) => s.write(buf),
        }
    }

    #[inline]
    fn flush(&mut self) -> io::Result<()> {
        match *self {
            Conn::Normal(ref mut s) => s.flush(),
            Conn::Proxied(ref mut s) => s.flush(),
        }
    }
}

impl AsyncRead for Conn {
    unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool {
        match *self {
            Conn::Normal(ref s) => s.prepare_uninitialized_buffer(buf),
            Conn::Proxied(ref s) => s.prepare_uninitialized_buffer(buf),
        }
    }

    fn read_buf<B: BufMut>(&mut self, buf: &mut B) -> Poll<usize, io::Error> {
        match *self {
            Conn::Normal(ref mut s) => s.read_buf(buf),
            Conn::Proxied(ref mut s) => s.read_buf(buf),
        }
    }
}

impl AsyncWrite for Conn {
    fn shutdown(&mut self) -> Poll<(), io::Error> {
        match *self {
            Conn::Normal(ref mut s) => s.shutdown(),
            Conn::Proxied(ref mut s) => s.shutdown(),
        }
    }

    fn write_buf<B: Buf>(&mut self, buf: &mut B) -> Poll<usize, io::Error> {
        match *self {
            Conn::Normal(ref mut s) => s.write_buf(buf),
            Conn::Proxied(ref mut s) => s.write_buf(buf),
        }
    }
}

fn tunnel<T>(conn: T, host: String, port: String) -> Tunnel<T> {
     let buf = format!("\
        CONNECT {0}:{1} HTTP/1.1\r\n\
        Host: {0}:{1}\r\n\
        \r\n\
    ", host, port).into_bytes();

     Tunnel {
        buf: buf.into_buf(),
        conn: Some(conn),
        state: TunnelState::Writing,
     }
}

struct Tunnel<T> {
    buf: Cursor<Vec<u8>>,
    conn: Option<T>,
    state: TunnelState,
}

enum TunnelState {
    Writing,
    Reading
}

impl<T> Future for Tunnel<T>
where T: AsyncRead + AsyncWrite {
    type Item = T;
    type Error = io::Error;

    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        loop {
            if let TunnelState::Writing = self.state {
                let n = try_ready!(self.conn.as_mut().unwrap().write_buf(&mut self.buf));
                if !self.buf.has_remaining_mut() {
                    self.state = TunnelState::Reading;
                    self.buf.get_mut().truncate(0);
                } else if n == 0 {
                    return Err(io::Error::new(io::ErrorKind::UnexpectedEof, "unexpected eof while tunneling"));
                }
            } else {
                let n = try_ready!(self.conn.as_mut().unwrap().read_buf(&mut self.buf.get_mut()));
                let read = &self.buf.get_ref()[..];
                if n == 0 {
                    return Err(io::Error::new(io::ErrorKind::UnexpectedEof, "unexpected eof while tunneling"));
                } else if read.len() > 12 {
                    if read.starts_with(b"HTTP/1.1 200") || read.starts_with(b"HTTP/1.0 200"){
                        if read.ends_with(b"\r\n\r\n") {
                            return Ok(Async::Ready(self.conn.take().unwrap()));
                        }
                        // else read more
                    } else {
                        return Err(io::Error::new(io::ErrorKind::Other, "unsuccessful tunnel"));
                    }
                }
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use std::io::{Read, Write};
    use std::net::TcpListener;
    use std::thread;
    use futures::Future;
    use tokio_core::reactor::Core;
    use tokio_core::net::TcpStream;
    use super::tunnel;

    macro_rules! mock_tunnel {
        () => ({
            mock_tunnel!(b"\
                HTTP/1.1 200 OK\r\n\
                \r\n\
            ")
        });
        ($write:expr) => ({
            let listener = TcpListener::bind("127.0.0.1:0").unwrap();
            let addr = listener.local_addr().unwrap();
            let connect_expected = format!("\
                CONNECT {0}:{1} HTTP/1.1\r\n\
                Host: {0}:{1}\r\n\
                \r\n\
            ", addr.ip(), addr.port()).into_bytes();

            thread::spawn(move || {
                let (mut sock, _) = listener.accept().unwrap();
                let mut buf = [0u8; 4096];
                let n = sock.read(&mut buf).unwrap();
                assert_eq!(&buf[..n], &connect_expected[..]);

                sock.write_all($write).unwrap();
            });
            addr
        })
    }

    #[test]
    fn test_tunnel() {
        let addr = mock_tunnel!();

        let mut core = Core::new().unwrap();
        let work = TcpStream::connect(&addr, &core.handle());
        let host = addr.ip().to_string();
        let port = addr.port().to_string();
        let work = work.and_then(|tcp| {
            tunnel(tcp, host, port)
        });

        core.run(work).unwrap();
    }

    #[test]
    fn test_tunnel_eof() {
        let addr = mock_tunnel!(b"HTTP/1.1 200 OK");

        let mut core = Core::new().unwrap();
        let work = TcpStream::connect(&addr, &core.handle());
        let host = addr.ip().to_string();
        let port = addr.port().to_string();
        let work = work.and_then(|tcp| {
            tunnel(tcp, host, port)
        });

        core.run(work).unwrap_err();
    }

    #[test]
    fn test_tunnel_bad_response() {
        let addr = mock_tunnel!(b"foo bar baz hallo");

        let mut core = Core::new().unwrap();
        let work = TcpStream::connect(&addr, &core.handle());
        let host = addr.ip().to_string();
        let port = addr.port().to_string();
        let work = work.and_then(|tcp| {
            tunnel(tcp, host, port)
        });

        core.run(work).unwrap_err();
    }
}
<commit_msg>use proxy port as a u16 instead of String<commit_after>use bytes::{Buf, BufMut, IntoBuf};
use futures::{Async, Future, Poll};
use hyper::client::{HttpConnector, Service};
use hyper::Uri;
use hyper_tls::{HttpsConnector, MaybeHttpsStream};
use native_tls::TlsConnector;
use tokio_core::reactor::Handle;
use tokio_io::{AsyncRead, AsyncWrite};
use tokio_tls::{TlsConnectorExt, TlsStream};

use std::io::{self, Cursor, Read, Write};
use std::sync::Arc;

use {proxy, Proxy};

// pub(crate)

pub struct Connector {
    https: HttpsConnector<HttpConnector>,
    proxies: Arc<Vec<Proxy>>,
    tls: TlsConnector,
}

impl Connector {
    pub fn new(tls: TlsConnector, proxies: Arc<Vec<Proxy>>, handle: &Handle) -> Connector {
        let mut http = HttpConnector::new(4, handle);
        http.enforce_http(false);
        let https = HttpsConnector::from((http, tls.clone()));

        Connector {
            https: https,
            proxies: proxies,
            tls: tls,
        }
    }

    pub fn danger_disable_hostname_verification(&mut self) {
        self.https.danger_disable_hostname_verification(true);
    }
}

impl Service for Connector {
    type Request = Uri;
    type Response = Conn;
    type Error = io::Error;
    type Future = Connecting;

    fn call(&self, uri: Uri) -> Self::Future {
        for prox in self.proxies.iter() {
            if let Some(puri) = proxy::proxies(prox, &uri) {
                if uri.scheme() == Some("https") {
                    let host = uri.host().unwrap().to_owned();
                    let port = uri.port().unwrap_or(443);
                    let tls = self.tls.clone();
                    return Box::new(self.https.call(puri).and_then(move |conn| {
                        tunnel(conn, host.clone(), port)
                            .and_then(move |tunneled| {
                                tls.connect_async(&host, tunneled)
                                    .map_err(|e| io::Error::new(io::ErrorKind::Other, e))
                            })
                            .map(|io| Conn::Proxied(io))
                    }));
                }
                return Box::new(self.https.call(puri).map(|io| Conn::Normal(io)));
            }
        }
        Box::new(self.https.call(uri).map(|io| Conn::Normal(io)))
    }
}

type HttpStream = <HttpConnector as Service>::Response;
type HttpsStream = MaybeHttpsStream<HttpStream>;

pub type Connecting = Box<Future<Item=Conn, Error=io::Error>>;

pub enum Conn {
    Normal(HttpsStream),
    Proxied(TlsStream<MaybeHttpsStream<HttpStream>>),
}

impl Read for Conn {
    #[inline]
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        match *self {
            Conn::Normal(ref mut s) => s.read(buf),
            Conn::Proxied(ref mut s) => s.read(buf),
        }
    }
}

impl Write for Conn {
    #[inline]
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        match *self {
            Conn::Normal(ref mut s) => s.write(buf),
            Conn::Proxied(ref mut s) => s.write(buf),
        }
    }

    #[inline]
    fn flush(&mut self) -> io::Result<()> {
        match *self {
            Conn::Normal(ref mut s) => s.flush(),
            Conn::Proxied(ref mut s) => s.flush(),
        }
    }
}

impl AsyncRead for Conn {
    unsafe fn prepare_uninitialized_buffer(&self, buf: &mut [u8]) -> bool {
        match *self {
            Conn::Normal(ref s) => s.prepare_uninitialized_buffer(buf),
            Conn::Proxied(ref s) => s.prepare_uninitialized_buffer(buf),
        }
    }

    fn read_buf<B: BufMut>(&mut self, buf: &mut B) -> Poll<usize, io::Error> {
        match *self {
            Conn::Normal(ref mut s) => s.read_buf(buf),
            Conn::Proxied(ref mut s) => s.read_buf(buf),
        }
    }
}

impl AsyncWrite for Conn {
    fn shutdown(&mut self) -> Poll<(), io::Error> {
        match *self {
            Conn::Normal(ref mut s) => s.shutdown(),
            Conn::Proxied(ref mut s) => s.shutdown(),
        }
    }

    fn write_buf<B: Buf>(&mut self, buf: &mut B) -> Poll<usize, io::Error> {
        match *self {
            Conn::Normal(ref mut s) => s.write_buf(buf),
            Conn::Proxied(ref mut s) => s.write_buf(buf),
        }
    }
}

fn tunnel<T>(conn: T, host: String, port: u16) -> Tunnel<T> {
     let buf = format!("\
        CONNECT {0}:{1} HTTP/1.1\r\n\
        Host: {0}:{1}\r\n\
        \r\n\
    ", host, port).into_bytes();

     Tunnel {
        buf: buf.into_buf(),
        conn: Some(conn),
        state: TunnelState::Writing,
     }
}

struct Tunnel<T> {
    buf: Cursor<Vec<u8>>,
    conn: Option<T>,
    state: TunnelState,
}

enum TunnelState {
    Writing,
    Reading
}

impl<T> Future for Tunnel<T>
where T: AsyncRead + AsyncWrite {
    type Item = T;
    type Error = io::Error;

    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        loop {
            if let TunnelState::Writing = self.state {
                let n = try_ready!(self.conn.as_mut().unwrap().write_buf(&mut self.buf));
                if !self.buf.has_remaining_mut() {
                    self.state = TunnelState::Reading;
                    self.buf.get_mut().truncate(0);
                } else if n == 0 {
                    return Err(tunnel_eof());
                }
            } else {
                let n = try_ready!(self.conn.as_mut().unwrap().read_buf(&mut self.buf.get_mut()));
                let read = &self.buf.get_ref()[..];
                if n == 0 {
                    return Err(tunnel_eof());
                } else if read.len() > 12 {
                    if read.starts_with(b"HTTP/1.1 200") || read.starts_with(b"HTTP/1.0 200") {
                        if read.ends_with(b"\r\n\r\n") {
                            return Ok(Async::Ready(self.conn.take().unwrap()));
                        }
                        // else read more
                    } else {
                        return Err(io::Error::new(io::ErrorKind::Other, "unsuccessful tunnel"));
                    }
                }
            }
        }
    }
}

#[inline]
fn tunnel_eof() -> io::Error {
    io::Error::new(
        io::ErrorKind::UnexpectedEof,
        "unexpected eof while tunneling"
    )
}

#[cfg(test)]
mod tests {
    use std::io::{Read, Write};
    use std::net::TcpListener;
    use std::thread;
    use futures::Future;
    use tokio_core::reactor::Core;
    use tokio_core::net::TcpStream;
    use super::tunnel;

    macro_rules! mock_tunnel {
        () => ({
            mock_tunnel!(b"\
                HTTP/1.1 200 OK\r\n\
                \r\n\
            ")
        });
        ($write:expr) => ({
            let listener = TcpListener::bind("127.0.0.1:0").unwrap();
            let addr = listener.local_addr().unwrap();
            let connect_expected = format!("\
                CONNECT {0}:{1} HTTP/1.1\r\n\
                Host: {0}:{1}\r\n\
                \r\n\
            ", addr.ip(), addr.port()).into_bytes();

            thread::spawn(move || {
                let (mut sock, _) = listener.accept().unwrap();
                let mut buf = [0u8; 4096];
                let n = sock.read(&mut buf).unwrap();
                assert_eq!(&buf[..n], &connect_expected[..]);

                sock.write_all($write).unwrap();
            });
            addr
        })
    }

    #[test]
    fn test_tunnel() {
        let addr = mock_tunnel!();

        let mut core = Core::new().unwrap();
        let work = TcpStream::connect(&addr, &core.handle());
        let host = addr.ip().to_string();
        let port = addr.port();
        let work = work.and_then(|tcp| {
            tunnel(tcp, host, port)
        });

        core.run(work).unwrap();
    }

    #[test]
    fn test_tunnel_eof() {
        let addr = mock_tunnel!(b"HTTP/1.1 200 OK");

        let mut core = Core::new().unwrap();
        let work = TcpStream::connect(&addr, &core.handle());
        let host = addr.ip().to_string();
        let port = addr.port();
        let work = work.and_then(|tcp| {
            tunnel(tcp, host, port)
        });

        core.run(work).unwrap_err();
    }

    #[test]
    fn test_tunnel_bad_response() {
        let addr = mock_tunnel!(b"foo bar baz hallo");

        let mut core = Core::new().unwrap();
        let work = TcpStream::connect(&addr, &core.handle());
        let host = addr.ip().to_string();
        let port = addr.port();
        let work = work.and_then(|tcp| {
            tunnel(tcp, host, port)
        });

        core.run(work).unwrap_err();
    }
}
<|endoftext|>


<commit_before>use winit;
use winit::dpi;

use std::fmt;

use audio;
use conf;
use event::winit_event;
use filesystem::Filesystem;
use graphics::{self, Point2};
use input::{gamepad, keyboard, mouse};
use timer;
use GameResult;

/// A `Context` is an object that holds on to global resources.
/// It basically tracks hardware state such as the screen, audio
/// system, timers, and so on.  Generally this type is **not** thread-
/// safe and only one `Context` can exist at a time.  Trying to create
/// another one will fail.
///
/// Most functions that interact with the hardware, for instance
/// drawing things, playing sounds, or loading resources (which then
/// need to be transformed into a format the hardware likes) will need
/// to access the `Context`.
pub struct Context {
    /// Filesystem state
    pub filesystem: Filesystem,
    /// Graphics state
    pub(crate) gfx_context: graphics::GraphicsContext,
    /// Timer state
    pub timer_context: timer::TimeContext,
    /// Audio context
    pub audio_context: audio::AudioContext,
    /// Keyboard context
    pub keyboard_context: keyboard::KeyboardContext,
    /// Mouse context
    pub mouse_context: mouse::MouseContext,
    /// Gamepad context
    pub gamepad_context: gamepad::GamepadContext,

    /// The Conf object the Context was created with
    pub conf: conf::Conf,
    /// Controls whether or not the event loop should be running.
    pub continuing: bool,

    /// Context-specific unique ID.
    /// Compiles to nothing in release mode, and so
    /// vanishes; meanwhile we get dead-code warnings.
    #[allow(dead_code)]
    debug_id: DebugId,
}

impl fmt::Debug for Context {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "<Context: {:p}>", self)
    }
}

impl Context {
    /// Tries to create a new Context using settings from the given config file.
    /// Usually called by `ContextBuilder::build()`.
    fn from_conf(conf: conf::Conf, fs: Filesystem) -> GameResult<(Context, winit::EventsLoop)> {
        let debug_id = DebugId::new();
        let audio_context = audio::AudioContext::new()?;
        let events_loop = winit::EventsLoop::new();
        let timer_context = timer::TimeContext::new();
        let backend_spec = graphics::GlBackendSpec::from(conf.backend);
        let graphics_context = graphics::GraphicsContext::new(
            &events_loop,
            &conf.window_setup,
            conf.window_mode,
            backend_spec,
            debug_id,
        )?;
        let mouse_context = mouse::MouseContext::new();
        let keyboard_context = keyboard::KeyboardContext::new();
        let gamepad_context = gamepad::GamepadContext::new()?;

        let ctx = Context {
            conf,
            filesystem: fs,
            gfx_context: graphics_context,
            continuing: true,
            timer_context,
            audio_context,
            keyboard_context,
            gamepad_context,
            mouse_context,

            debug_id,
        };

        Ok((ctx, events_loop))
    }

    /// Terminates `ggez::run()` loop by setting `Context::continuing` to `false`.
    pub fn quit(&mut self) {
        self.continuing = false;
    }

    /// Feeds an `Event` into the `Context` so it can update any internal
    /// state it needs to, such as detecting window resizes.  If you are
    /// rolling your own event loop, you should call this on the events
    /// you receive before processing them yourself.
    ///
    /// This also returns a new version of the `event` that has been modified
    /// for ggez's optional overriding of hidpi.  For full discussion see
    /// <https://github.com/tomaka/winit/issues/591#issuecomment-403096230>.
    pub fn process_event(&mut self, event: &winit::Event) -> winit::Event {
        let event = self.gfx_context.hack_event_hidpi(event);
        match event.clone() {
            winit_event::Event::WindowEvent { event, .. } => match event {
                winit_event::WindowEvent::Resized(_) => {
                    self.gfx_context.resize_viewport();
                }
                winit_event::WindowEvent::CursorMoved {
                    position: dpi::LogicalPosition { x, y },
                    ..
                } => {
                    self.mouse_context
                        .set_last_position(Point2::new(x as f32, y as f32));
                }
                winit_event::WindowEvent::MouseInput { button, state, .. } => {
                    let pressed = match state {
                        winit_event::ElementState::Pressed => true,
                        winit_event::ElementState::Released => false,
                    };
                    self.mouse_context.set_button(button, pressed);
                }
                _ => (),
            },
            winit_event::Event::DeviceEvent { event, .. } => match event {
                winit_event::DeviceEvent::MouseMotion { delta: (x, y) } => {
                    self.mouse_context
                        .set_last_delta(Point2::new(x as f32, y as f32));
                }
                winit_event::DeviceEvent::Key(winit_event::KeyboardInput {
                    state,
                    virtual_keycode: Some(keycode),
                    modifiers,
                    ..
                }) => {
                    let pressed = match state {
                        winit_event::ElementState::Pressed => true,
                        winit_event::ElementState::Released => false,
                    };
                    self.keyboard_context
                        .set_modifiers(keyboard::KeyMods::from(modifiers));
                    self.keyboard_context.set_key(keycode, pressed);
                }
                _ => (),
            },

            _ => (),
        };
        event
    }
}

use std::path;

/// A builder object for creating a `Context`.
///
/// Can do everything the `Context::load_from_conf()` method does, plus you can
/// also specify new paths to add to the resource path list at build time instead
/// of using `filesystem::mount()`.
///
/// TODO: Better docs.  Should `Context::load_from_conf` be outright deprecated?
#[derive(Debug)]
pub struct ContextBuilder {
    game_id: &'static str,
    author: &'static str,
    conf: conf::Conf,
    paths: Vec<path::PathBuf>,
    load_conf_file: bool,
}

impl ContextBuilder {
    /// Create a new ContextBuilder
    pub fn new(game_id: &'static str, author: &'static str) -> Self {
        Self {
            game_id,
            author,
            conf: conf::Conf::default(),
            paths: vec![],
            load_conf_file: true,
        }
    }

    /// Sets the window setup settings
    pub fn window_setup(mut self, setup: conf::WindowSetup) -> Self {
        self.conf.window_setup = setup;
        self
    }

    /// Sets the window mode settings
    pub fn window_mode(mut self, mode: conf::WindowMode) -> Self {
        self.conf.window_mode = mode;
        self
    }

    /// Sets the graphics backend
    pub fn backend(mut self, backend: conf::Backend) -> Self {
        self.conf.backend = backend;
        self
    }

    /// Add a new read-only filesystem path to the places to search
    /// for resources.
    pub fn add_resource_path<T>(mut self, path: T) -> Self
    where
        T: Into<path::PathBuf>,
    {
        self.paths.push(path.into());
        self
    }

    /// Specifies whether or not to load the `conf.toml` file if it
    /// exists and use its settings to override the provided values.
    /// Defaults to `true` which is usually what you want, but being
    /// able to fiddle with it is sometimes useful for debugging.
    pub fn with_conf_file(mut self, load_conf_file: bool) -> Self {
        self.load_conf_file = load_conf_file;
        self
    }

    /// Build the Context.
    pub fn build(self) -> GameResult<(Context, winit::EventsLoop)> {
        let mut fs = Filesystem::new(self.game_id, self.author)?;

        for path in &self.paths {
            fs.mount(path, true);
        }

        let config = if self.load_conf_file {
            fs.read_config().unwrap_or(self.conf)
        } else {
            self.conf
        };

        Context::from_conf(config, fs)
    }
}

#[cfg(debug_assertions)]
use std::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT};
#[cfg(debug_assertions)]
static DEBUG_ID_COUNTER: AtomicUsize = ATOMIC_USIZE_INIT;

/// This is a type that contains a unique ID for each Context and
/// is contained in each thing created from the Context which
/// becomes invalid when the Context goes away (for example, Image because
/// it contains texture handles).  When compiling without assertions
/// (in release mode) it is replaced with a zero-size type, compiles
/// down to nothing, disappears entirely with a puff of optimization logic.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg(debug_assertions)]
pub(crate) struct DebugId(u32);
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg(not(debug_assertions))]
pub(crate) struct DebugId;

#[cfg(debug_assertions)]
impl DebugId {
    pub fn new() -> Self {
        let id = DEBUG_ID_COUNTER.fetch_add(1, Ordering::SeqCst) as u32;
        // fetch_add() wraps on overflow so we check for overflow explicitly.
        // JUST IN CASE YOU TRY TO CREATE 2^32 CONTEXTS IN ONE PROGRAM!
        assert!(DEBUG_ID_COUNTER.load(Ordering::SeqCst) as u32 > id);
        DebugId(id)
    }

    pub fn get(ctx: &Context) -> Self {
        DebugId(ctx.debug_id.0)
    }

    pub fn assert(&self, ctx: &Context) {
        if *self != ctx.debug_id {
            panic!("Tried to use a resource with a Context that did not create it; this should never happen!");
        }
    }
}

#[cfg(not(debug_assertions))]
impl DebugId {
    pub fn new() -> Self {
        DebugId
    }

    pub fn get(_ctx: &Context) -> Self {
        DebugId
    }

    pub fn assert(&self, _ctx: &Context) {
        // Do nothing.
    }
}
<commit_msg>add set config methods for Context and ContextBuilder<commit_after>use winit;
use winit::dpi;

use std::fmt;

use audio;
use conf;
use event::winit_event;
use filesystem::Filesystem;
use graphics::{self, Point2};
use input::{gamepad, keyboard, mouse};
use timer;
use GameResult;

/// A `Context` is an object that holds on to global resources.
/// It basically tracks hardware state such as the screen, audio
/// system, timers, and so on.  Generally this type is **not** thread-
/// safe and only one `Context` can exist at a time.  Trying to create
/// another one will fail.
///
/// Most functions that interact with the hardware, for instance
/// drawing things, playing sounds, or loading resources (which then
/// need to be transformed into a format the hardware likes) will need
/// to access the `Context`.
pub struct Context {
    /// Filesystem state
    pub filesystem: Filesystem,
    /// Graphics state
    pub(crate) gfx_context: graphics::GraphicsContext,
    /// Timer state
    pub timer_context: timer::TimeContext,
    /// Audio context
    pub audio_context: audio::AudioContext,
    /// Keyboard context
    pub keyboard_context: keyboard::KeyboardContext,
    /// Mouse context
    pub mouse_context: mouse::MouseContext,
    /// Gamepad context
    pub gamepad_context: gamepad::GamepadContext,

    /// The Conf object the Context was created with
    pub conf: conf::Conf,
    /// Controls whether or not the event loop should be running.
    pub continuing: bool,

    /// Context-specific unique ID.
    /// Compiles to nothing in release mode, and so
    /// vanishes; meanwhile we get dead-code warnings.
    #[allow(dead_code)]
    debug_id: DebugId,
}

impl fmt::Debug for Context {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "<Context: {:p}>", self)
    }
}

impl Context {
    /// Tries to create a new Context using settings from the given config file.
    /// Usually called by `ContextBuilder::build()`.
    fn from_conf(conf: conf::Conf, fs: Filesystem) -> GameResult<(Context, winit::EventsLoop)> {
        let debug_id = DebugId::new();
        let audio_context = audio::AudioContext::new()?;
        let events_loop = winit::EventsLoop::new();
        let timer_context = timer::TimeContext::new();
        let backend_spec = graphics::GlBackendSpec::from(conf.backend);
        let graphics_context = graphics::GraphicsContext::new(
            &events_loop,
            &conf.window_setup,
            conf.window_mode,
            backend_spec,
            debug_id,
        )?;
        let mouse_context = mouse::MouseContext::new();
        let keyboard_context = keyboard::KeyboardContext::new();
        let gamepad_context = gamepad::GamepadContext::new()?;

        let ctx = Context {
            conf,
            filesystem: fs,
            gfx_context: graphics_context,
            continuing: true,
            timer_context,
            audio_context,
            keyboard_context,
            gamepad_context,
            mouse_context,

            debug_id,
        };

        Ok((ctx, events_loop))
    }

    /// Terminates `ggez::run()` loop by setting `Context::continuing` to `false`.
    pub fn quit(&mut self) {
        self.continuing = false;
    }

    /// Feeds an `Event` into the `Context` so it can update any internal
    /// state it needs to, such as detecting window resizes.  If you are
    /// rolling your own event loop, you should call this on the events
    /// you receive before processing them yourself.
    ///
    /// This also returns a new version of the `event` that has been modified
    /// for ggez's optional overriding of hidpi.  For full discussion see
    /// <https://github.com/tomaka/winit/issues/591#issuecomment-403096230>.
    pub fn process_event(&mut self, event: &winit::Event) -> winit::Event {
        let event = self.gfx_context.hack_event_hidpi(event);
        match event.clone() {
            winit_event::Event::WindowEvent { event, .. } => match event {
                winit_event::WindowEvent::Resized(_) => {
                    self.gfx_context.resize_viewport();
                }
                winit_event::WindowEvent::CursorMoved {
                    position: dpi::LogicalPosition { x, y },
                    ..
                } => {
                    self.mouse_context
                        .set_last_position(Point2::new(x as f32, y as f32));
                }
                winit_event::WindowEvent::MouseInput { button, state, .. } => {
                    let pressed = match state {
                        winit_event::ElementState::Pressed => true,
                        winit_event::ElementState::Released => false,
                    };
                    self.mouse_context.set_button(button, pressed);
                }
                _ => (),
            },
            winit_event::Event::DeviceEvent { event, .. } => match event {
                winit_event::DeviceEvent::MouseMotion { delta: (x, y) } => {
                    self.mouse_context
                        .set_last_delta(Point2::new(x as f32, y as f32));
                }
                winit_event::DeviceEvent::Key(winit_event::KeyboardInput {
                    state,
                    virtual_keycode: Some(keycode),
                    modifiers,
                    ..
                }) => {
                    let pressed = match state {
                        winit_event::ElementState::Pressed => true,
                        winit_event::ElementState::Released => false,
                    };
                    self.keyboard_context
                        .set_modifiers(keyboard::KeyMods::from(modifiers));
                    self.keyboard_context.set_key(keycode, pressed);
                }
                _ => (),
            },

            _ => (),
        };
        event
    }

    /// Sets the config
    pub fn set_config(&mut self, conf: conf::Conf) {
        self.conf = conf
    }
}

use std::path;

/// A builder object for creating a `Context`.
///
/// Can do everything the `Context::load_from_conf()` method does, plus you can
/// also specify new paths to add to the resource path list at build time instead
/// of using `filesystem::mount()`.
///
/// TODO: Better docs.  Should `Context::load_from_conf` be outright deprecated?
#[derive(Debug)]
pub struct ContextBuilder {
    game_id: &'static str,
    author: &'static str,
    conf: conf::Conf,
    paths: Vec<path::PathBuf>,
    load_conf_file: bool,
}

impl ContextBuilder {
    /// Create a new ContextBuilder
    pub fn new(game_id: &'static str, author: &'static str) -> Self {
        Self {
            game_id,
            author,
            conf: conf::Conf::default(),
            paths: vec![],
            load_conf_file: true,
        }
    }

    /// Sets the window setup settings
    pub fn window_setup(mut self, setup: conf::WindowSetup) -> Self {
        self.conf.window_setup = setup;
        self
    }

    /// Sets the window mode settings
    pub fn window_mode(mut self, mode: conf::WindowMode) -> Self {
        self.conf.window_mode = mode;
        self
    }

    /// Sets the graphics backend
    pub fn backend(mut self, backend: conf::Backend) -> Self {
        self.conf.backend = backend;
        self
    }

    /// Sets the entire config
    pub fn conf(mut self, conf: conf::Conf) -> Self {
        self.conf = conf;
        self
    }

    /// Add a new read-only filesystem path to the places to search
    /// for resources.
    pub fn add_resource_path<T>(mut self, path: T) -> Self
    where
        T: Into<path::PathBuf>,
    {
        self.paths.push(path.into());
        self
    }

    /// Specifies whether or not to load the `conf.toml` file if it
    /// exists and use its settings to override the provided values.
    /// Defaults to `true` which is usually what you want, but being
    /// able to fiddle with it is sometimes useful for debugging.
    pub fn with_conf_file(mut self, load_conf_file: bool) -> Self {
        self.load_conf_file = load_conf_file;
        self
    }

    /// Build the Context.
    pub fn build(self) -> GameResult<(Context, winit::EventsLoop)> {
        let mut fs = Filesystem::new(self.game_id, self.author)?;

        for path in &self.paths {
            fs.mount(path, true);
        }

        let config = if self.load_conf_file {
            fs.read_config().unwrap_or(self.conf)
        } else {
            self.conf
        };

        Context::from_conf(config, fs)
    }
}

#[cfg(debug_assertions)]
use std::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT};
#[cfg(debug_assertions)]
static DEBUG_ID_COUNTER: AtomicUsize = ATOMIC_USIZE_INIT;

/// This is a type that contains a unique ID for each Context and
/// is contained in each thing created from the Context which
/// becomes invalid when the Context goes away (for example, Image because
/// it contains texture handles).  When compiling without assertions
/// (in release mode) it is replaced with a zero-size type, compiles
/// down to nothing, disappears entirely with a puff of optimization logic.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg(debug_assertions)]
pub(crate) struct DebugId(u32);
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg(not(debug_assertions))]
pub(crate) struct DebugId;

#[cfg(debug_assertions)]
impl DebugId {
    pub fn new() -> Self {
        let id = DEBUG_ID_COUNTER.fetch_add(1, Ordering::SeqCst) as u32;
        // fetch_add() wraps on overflow so we check for overflow explicitly.
        // JUST IN CASE YOU TRY TO CREATE 2^32 CONTEXTS IN ONE PROGRAM!
        assert!(DEBUG_ID_COUNTER.load(Ordering::SeqCst) as u32 > id);
        DebugId(id)
    }

    pub fn get(ctx: &Context) -> Self {
        DebugId(ctx.debug_id.0)
    }

    pub fn assert(&self, ctx: &Context) {
        if *self != ctx.debug_id {
            panic!("Tried to use a resource with a Context that did not create it; this should never happen!");
        }
    }
}

#[cfg(not(debug_assertions))]
impl DebugId {
    pub fn new() -> Self {
        DebugId
    }

    pub fn get(_ctx: &Context) -> Self {
        DebugId
    }

    pub fn assert(&self, _ctx: &Context) {
        // Do nothing.
    }
}
<|endoftext|>


<commit_before>extern crate cc;
extern crate regex;

fn main() {
    build::main()
}

#[cfg(feature = "docs-only")]
mod build {
    pub fn main() {}
}

#[cfg(not(feature = "docs-only"))]
mod build {
    use std::process::Command;
    use std::env;
    use std::fs;
    use std::path::Path;
    use regex::Regex;

    pub fn main() {
        let out_dir = env::var("OUT_DIR").unwrap();
        let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap();

        let out_dir = Path::new(&out_dir);
        let native_from = Path::new(&crate_dir).join("native");
        let native_to = Path::new(&out_dir).join("native");

        // 1. Copy the native runtime library into the build directory.
        copy_native_library(&native_from, &native_to);

        // 2. Build the object file from source using node-gyp.
        build_object_file(&native_to);

        // 3. Link the library from the object file using gcc.
        link_library(&native_to);

        // 4. Copy native build artifacts
        copy_build_artifacts(&native_to, &out_dir);

        // Rebuild if any of these environment variables change
        println!("cargo:rerun-if-env-changed=npm_config_target");
        println!("cargo:rerun-if-env-changed=npm_config_arch");
        println!("cargo:rerun-if-env-changed=npm_config_target_arch");
    }

    fn copy_files(dir_from: impl AsRef<Path>, dir_to: impl AsRef<Path>) {
        for entry in fs::read_dir(dir_from.as_ref()).unwrap() {
            let entry = entry.unwrap();

            if entry.file_type().unwrap().is_dir() {
                continue;
            }

            let file_name = entry.file_name();

            let from = dir_from.as_ref().join(&file_name);
            let to = dir_to.as_ref().join(&file_name);

            fs::copy(from, to).unwrap();
        }
    }

    fn copy_native_library(native_from: impl AsRef<Path>, native_to: impl AsRef<Path>) {
        let native_from = native_from.as_ref();
        let native_to = native_to.as_ref();

        let src_from = native_from.join("src");
        let src_to = native_to.join("src");

        fs::create_dir_all(&src_to).unwrap();

        copy_files(&native_from, &native_to);
        copy_files(&src_from, &src_to);
    }

    #[cfg(unix)]
    fn npm(cwd: &Path) -> Command {
        let mut cmd = Command::new("npm");
        cmd.current_dir(cwd);
        cmd
    }

    #[cfg(windows)]
    fn npm(cwd: &Path) -> Command {
        let mut cmd = Command::new("cmd.exe");
        cmd.args(&["/C", "npm"]);
        cmd.current_dir(cwd);
        cmd
    }

    // The node-gyp build output includes platform information in a string
    // that looks like:
    //
    //     gyp verb architecture ia32
    fn parse_node_arch(node_gyp_output: &str) -> String {
        let version_regex = Regex::new(r"gyp verb architecture (?P<arch>ia32|x64)").unwrap();
        let captures = version_regex.captures(&node_gyp_output).unwrap();
        String::from(&captures["arch"])
    }

    // The node-gyp output includes the root directory of shared resources
    // for the Node installation in a string that looks like:
    //
    //     '-Dnode_root_dir=C:\\Users\\dherman\\.node-gyp\\8.3.0'
    fn parse_node_root_dir(node_gyp_output: &str) -> &str {
        let node_root_dir_flag_pattern = "'-Dnode_root_dir=";
        let node_root_dir_start_index = node_gyp_output
            .find(node_root_dir_flag_pattern)
            .map(|i| i + node_root_dir_flag_pattern.len())
            .expect("Couldn't find node_root_dir in node-gyp output.");
        let node_root_dir_end_index = node_gyp_output[node_root_dir_start_index..].find("'").unwrap() + node_root_dir_start_index;
        &node_gyp_output[node_root_dir_start_index..node_root_dir_end_index]
    }

    // The node-gyp output includes the name of the shared Node library file.
    // In NPM versions <= v5.0.3, this was just the filename by itself, e.g.:
    //
    //     '-Dnode_lib_file=node.lib'
    //
    // In NPM versions >= v5.3.0, this was a templated absolute path with a
    // reference to a gyp variable, e.g.:
    //
    //     '-Dnode_lib_file=C:\\Users\\dherman\\.node-gyp\\8.3.0\\<(target_arch)\\node.lib'
    //
    // Either way, we simply extract the value here. The `neon-build` crate
    // processes it further.
    fn parse_node_lib_file(node_gyp_output: &str) -> &str {
        let node_lib_file_flag_pattern = "'-Dnode_lib_file=";
        let node_lib_file_start_index = node_gyp_output
            .find(node_lib_file_flag_pattern)
            .map(|i| i + node_lib_file_flag_pattern.len())
            .expect("Couldn't find node_lib_file in node-gyp output.");
        let node_lib_file_end_index = node_gyp_output[node_lib_file_start_index..].find("'").unwrap() + node_lib_file_start_index;
        &node_gyp_output[node_lib_file_start_index..node_lib_file_end_index]
    }

    fn build_object_file(native_dir: &Path) {
        if cfg!(windows) {
            // Downcase all the npm environment variables to ensure they are read by node-gyp.
            for (key, value) in env::vars() {
                if key.starts_with("NPM_CONFIG") {
                    env::remove_var(&key);
                    env::set_var(key.to_lowercase(), value);
                }
            }
        }

        // Ensure that all package.json dependencies and dev dependencies are installed.
        npm(native_dir).args(&["install", "--silent"]).status().ok().expect("Failed to run \"npm install\" for neon-sys!");

        // Run `node-gyp configure` in verbose mode to read node_root_dir on Windows.
        let output = npm(native_dir)
            .args(&["run", if debug() { "configure-debug" } else { "configure-release" }])
            .output()
            .expect("Failed to run \"node-gyp configure\" for neon-sys!");

        if !output.status.success() {
            panic!(format!(
                "Failed to run \"node-gyp configure\" for neon-sys!\n Out: {}\n Err: {}",
                String::from_utf8_lossy(&output.stdout),
                String::from_utf8_lossy(&output.stderr)
            ));
        }

        if cfg!(windows) {
            println!("cargo:node_root_dir={}", parse_node_root_dir(&node_gyp_output));
            println!("cargo:node_lib_file={}", parse_node_lib_file(&node_gyp_output));
        }

        // Run `node-gyp build`.
        let build_output = npm(native_dir)
            .args(&["run", if debug() { "build-debug" } else { "build-release" }])
            .output()
            .ok()
            .expect("Failed to run \"node-gyp build\" for neon-sys!");

        let node_gyp_build_output = String::from_utf8_lossy(&build_output.stderr);
        println!("cargo:node_arch={}", parse_node_arch(&node_gyp_build_output));
    }

    // Link the built object file into a static library.
    fn link_library(native_dir: &Path) {
        let configuration = if debug() { "Debug" } else { "Release" };

        let object_path = if cfg!(unix) {
            native_dir
                .join("build")
                .join(configuration)
                .join("obj.target")
                .join("neon")
                .join("src")
                .join("neon.o")
        } else {
            native_dir
                .join("build")
                .join(configuration)
                .join("obj")
                .join("neon")
                .join("neon.obj")
        };

        cc::Build::new().cpp(true).object(object_path).compile("libneon.a");
    }

    #[cfg(unix)]
    fn copy_build_artifacts(_native_dir: &Path, _out_dir: &Path) {}

    #[cfg(windows)]
    fn copy_build_artifacts(native_dir: &Path, out_dir: &Path) {
        let configuration = if debug() { "Debug" } else { "Release" };
        let win_delay_file = "win_delay_load_hook.obj";
        let win_delay_dest = out_dir.join(win_delay_file);
        let win_delay_source = native_dir
            .join("build")
            .join(configuration)
            .join("obj")
            .join("neon")
            .join(win_delay_file);

        // Win delay hook is only needed for electron, warn instead of crash if not found
        if let Err(err) = fs::copy(win_delay_source, win_delay_dest) {
            if err.kind() == std::io::ErrorKind::NotFound {
                eprintln!("warning: {} could not be found", win_delay_file);
            } else {
                panic!("{:?}", err);
            }
        }
    }

    fn debug() -> bool {
        match env::var("DEBUG") {
            Ok(s) => s == "true",
            Err(_) => false
        }
    }
}
<commit_msg>Put node_gyp_output back<commit_after>extern crate cc;
extern crate regex;

fn main() {
    build::main()
}

#[cfg(feature = "docs-only")]
mod build {
    pub fn main() {}
}

#[cfg(not(feature = "docs-only"))]
mod build {
    use std::process::Command;
    use std::env;
    use std::fs;
    use std::path::Path;
    use regex::Regex;

    pub fn main() {
        let out_dir = env::var("OUT_DIR").unwrap();
        let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap();

        let out_dir = Path::new(&out_dir);
        let native_from = Path::new(&crate_dir).join("native");
        let native_to = Path::new(&out_dir).join("native");

        // 1. Copy the native runtime library into the build directory.
        copy_native_library(&native_from, &native_to);

        // 2. Build the object file from source using node-gyp.
        build_object_file(&native_to);

        // 3. Link the library from the object file using gcc.
        link_library(&native_to);

        // 4. Copy native build artifacts
        copy_build_artifacts(&native_to, &out_dir);

        // Rebuild if any of these environment variables change
        println!("cargo:rerun-if-env-changed=npm_config_target");
        println!("cargo:rerun-if-env-changed=npm_config_arch");
        println!("cargo:rerun-if-env-changed=npm_config_target_arch");
    }

    fn copy_files(dir_from: impl AsRef<Path>, dir_to: impl AsRef<Path>) {
        for entry in fs::read_dir(dir_from.as_ref()).unwrap() {
            let entry = entry.unwrap();

            if entry.file_type().unwrap().is_dir() {
                continue;
            }

            let file_name = entry.file_name();

            let from = dir_from.as_ref().join(&file_name);
            let to = dir_to.as_ref().join(&file_name);

            fs::copy(from, to).unwrap();
        }
    }

    fn copy_native_library(native_from: impl AsRef<Path>, native_to: impl AsRef<Path>) {
        let native_from = native_from.as_ref();
        let native_to = native_to.as_ref();

        let src_from = native_from.join("src");
        let src_to = native_to.join("src");

        fs::create_dir_all(&src_to).unwrap();

        copy_files(&native_from, &native_to);
        copy_files(&src_from, &src_to);
    }

    #[cfg(unix)]
    fn npm(cwd: &Path) -> Command {
        let mut cmd = Command::new("npm");
        cmd.current_dir(cwd);
        cmd
    }

    #[cfg(windows)]
    fn npm(cwd: &Path) -> Command {
        let mut cmd = Command::new("cmd.exe");
        cmd.args(&["/C", "npm"]);
        cmd.current_dir(cwd);
        cmd
    }

    // The node-gyp build output includes platform information in a string
    // that looks like:
    //
    //     gyp verb architecture ia32
    fn parse_node_arch(node_gyp_output: &str) -> String {
        let version_regex = Regex::new(r"gyp verb architecture (?P<arch>ia32|x64)").unwrap();
        let captures = version_regex.captures(&node_gyp_output).unwrap();
        String::from(&captures["arch"])
    }

    // The node-gyp output includes the root directory of shared resources
    // for the Node installation in a string that looks like:
    //
    //     '-Dnode_root_dir=C:\\Users\\dherman\\.node-gyp\\8.3.0'
    fn parse_node_root_dir(node_gyp_output: &str) -> &str {
        let node_root_dir_flag_pattern = "'-Dnode_root_dir=";
        let node_root_dir_start_index = node_gyp_output
            .find(node_root_dir_flag_pattern)
            .map(|i| i + node_root_dir_flag_pattern.len())
            .expect("Couldn't find node_root_dir in node-gyp output.");
        let node_root_dir_end_index = node_gyp_output[node_root_dir_start_index..].find("'").unwrap() + node_root_dir_start_index;
        &node_gyp_output[node_root_dir_start_index..node_root_dir_end_index]
    }

    // The node-gyp output includes the name of the shared Node library file.
    // In NPM versions <= v5.0.3, this was just the filename by itself, e.g.:
    //
    //     '-Dnode_lib_file=node.lib'
    //
    // In NPM versions >= v5.3.0, this was a templated absolute path with a
    // reference to a gyp variable, e.g.:
    //
    //     '-Dnode_lib_file=C:\\Users\\dherman\\.node-gyp\\8.3.0\\<(target_arch)\\node.lib'
    //
    // Either way, we simply extract the value here. The `neon-build` crate
    // processes it further.
    fn parse_node_lib_file(node_gyp_output: &str) -> &str {
        let node_lib_file_flag_pattern = "'-Dnode_lib_file=";
        let node_lib_file_start_index = node_gyp_output
            .find(node_lib_file_flag_pattern)
            .map(|i| i + node_lib_file_flag_pattern.len())
            .expect("Couldn't find node_lib_file in node-gyp output.");
        let node_lib_file_end_index = node_gyp_output[node_lib_file_start_index..].find("'").unwrap() + node_lib_file_start_index;
        &node_gyp_output[node_lib_file_start_index..node_lib_file_end_index]
    }

    fn build_object_file(native_dir: &Path) {
        if cfg!(windows) {
            // Downcase all the npm environment variables to ensure they are read by node-gyp.
            for (key, value) in env::vars() {
                if key.starts_with("NPM_CONFIG") {
                    env::remove_var(&key);
                    env::set_var(key.to_lowercase(), value);
                }
            }
        }

        // Ensure that all package.json dependencies and dev dependencies are installed.
        npm(native_dir).args(&["install", "--silent"]).status().ok().expect("Failed to run \"npm install\" for neon-sys!");

        // Run `node-gyp configure` in verbose mode to read node_root_dir on Windows.
        let output = npm(native_dir)
            .args(&["run", if debug() { "configure-debug" } else { "configure-release" }])
            .output()
            .expect("Failed to run \"node-gyp configure\" for neon-sys!");

        if !output.status.success() {
            panic!(format!(
                "Failed to run \"node-gyp configure\" for neon-sys!\n Out: {}\n Err: {}",
                String::from_utf8_lossy(&output.stdout),
                String::from_utf8_lossy(&output.stderr)
            ));
        }

        if cfg!(windows) {
            let node_gyp_output = String::from_utf8_lossy(&output.stderr);
            println!("cargo:node_root_dir={}", parse_node_root_dir(&node_gyp_output));
            println!("cargo:node_lib_file={}", parse_node_lib_file(&node_gyp_output));
        }

        // Run `node-gyp build`.
        let build_output = npm(native_dir)
            .args(&["run", if debug() { "build-debug" } else { "build-release" }])
            .output()
            .ok()
            .expect("Failed to run \"node-gyp build\" for neon-sys!");

        let node_gyp_build_output = String::from_utf8_lossy(&build_output.stderr);
        println!("cargo:node_arch={}", parse_node_arch(&node_gyp_build_output));
    }

    // Link the built object file into a static library.
    fn link_library(native_dir: &Path) {
        let configuration = if debug() { "Debug" } else { "Release" };

        let object_path = if cfg!(unix) {
            native_dir
                .join("build")
                .join(configuration)
                .join("obj.target")
                .join("neon")
                .join("src")
                .join("neon.o")
        } else {
            native_dir
                .join("build")
                .join(configuration)
                .join("obj")
                .join("neon")
                .join("neon.obj")
        };

        cc::Build::new().cpp(true).object(object_path).compile("libneon.a");
    }

    #[cfg(unix)]
    fn copy_build_artifacts(_native_dir: &Path, _out_dir: &Path) {}

    #[cfg(windows)]
    fn copy_build_artifacts(native_dir: &Path, out_dir: &Path) {
        let configuration = if debug() { "Debug" } else { "Release" };
        let win_delay_file = "win_delay_load_hook.obj";
        let win_delay_dest = out_dir.join(win_delay_file);
        let win_delay_source = native_dir
            .join("build")
            .join(configuration)
            .join("obj")
            .join("neon")
            .join(win_delay_file);

        // Win delay hook is only needed for electron, warn instead of crash if not found
        if let Err(err) = fs::copy(win_delay_source, win_delay_dest) {
            if err.kind() == std::io::ErrorKind::NotFound {
                eprintln!("warning: {} could not be found", win_delay_file);
            } else {
                panic!("{:?}", err);
            }
        }
    }

    fn debug() -> bool {
        match env::var("DEBUG") {
            Ok(s) => s == "true",
            Err(_) => false
        }
    }
}
<|endoftext|>