<commit_before>#![feature(question_mark)]
#![feature(associated_consts)]
#![feature(const_fn)]
#![feature(conservative_impl_trait)]

extern crate mio;
extern crate slab;
extern crate uuid;
extern crate byteorder;
extern crate graphsearch;
extern crate clap;
#[macro_use]
extern crate protocol as proto;

pub use self::proto_connection::{ProtoConnection, ProtoState};
pub use self::proto_node::ProtoNode;
pub use self::connection::*;
pub use self::network::{Network, Node, Edge};
pub use self::error::Error;
pub use self::protocol::Packet;
pub use self::path::Path;

pub mod proto_connection;
pub mod proto_node;
pub mod connection;
pub mod network;
pub mod error;
pub mod protocol;
pub mod path;

use mio::tcp::*;
use slab::Slab;

use uuid::Uuid;

const SERVER_TOKEN: mio::Token = mio::Token(usize::max_value() - 10);
const NEW_CONNECTION_TOKEN: mio::Token = mio::Token(usize::max_value() - 11);

const SERVER_PORT: u16 = 53371;
const SERVER_ADDRESS: (&'static str, u16) = ("127.0.0.1", SERVER_PORT);

const CLIENT_NAME: &'static str = "vanilla";
const CLIENT_VERSION: &'static str = env!("CARGO_PKG_VERSION");

const PROTOCOL_MAJOR: u16 = 0;
const PROTOCOL_REVISION: u16 = 0;

const DESCRIPTION: &'static str = "
    If you pass an address, it will connect to an existing node on
    some network, otherwise a new network will be created.
";

fn user_agent() -> protocol::UserAgent {
    protocol::UserAgent {
        client: format!("{} v{}", CLIENT_NAME, CLIENT_VERSION),
        protocol_major: PROTOCOL_MAJOR,
        protocol_revision: PROTOCOL_REVISION,
    }
}

pub struct ConnectedNode
{
    pub uuid: Uuid,
    pub listener: TcpListener,

    /// The network we are apart of.
    pub network: Network,
}

// Flow:
//
// Client sends 'JoinRequest' to some node
// Server responds with 'JoinResponse'
// Client is now ready to act as server.

pub enum State
{
    Unconnected,
    Pending(ProtoConnection),
    /// We are now a connected node in the network.
    Connected {
        node: ConnectedNode,

        pending_connections: Slab<ProtoNode, mio::Token>,
    },
}

pub struct Parapet
{
    pub state: State,
    pub poll: mio::Poll,
}

impl Parapet
{
    /// Create a new network.
    pub fn new<A>(addr: A) -> Result<Self, Error>
        where A: std::net::ToSocketAddrs {
        let mut poll = mio::Poll::new()?;

        let listener = Parapet::bind(&mut poll, addr)?;

        Ok(Parapet {
            state: State::Connected {
                node: ConnectedNode {
                    uuid: Uuid::new_v4(),
                    listener: listener,
                    network: Network::new(),
                },
                pending_connections: Slab::with_capacity(1024),
            },
            poll: poll,
        })
    }

    /// Create a new tcp listener locally.
    fn bind<A>(poll: &mio::Poll, addr: A) -> Result<TcpListener, Error>
        where A: std::net::ToSocketAddrs {
        let mut addresses = addr.to_socket_addrs()?;
        let address = addresses.next().expect("could not resolve address");

        let listener = TcpListener::bind(&address)?;

        poll.register(&listener, SERVER_TOKEN, mio::Ready::readable(),
            mio::PollOpt::edge())?;

        Ok(listener)
    }

    /// Connect to an existing network.
    /// * `addr` - Any node on the network.
    pub fn connect<A>(addr: A) -> Result<Self, std::io::Error>
        where A: std::net::ToSocketAddrs {
        let mut addresses = addr.to_socket_addrs()?;
        let address = addresses.next().expect("could not resolve address");

        let stream = TcpStream::connect(&address)?;

        let poll = mio::Poll::new()?;
        poll.register(&stream, NEW_CONNECTION_TOKEN, mio::Ready::writable(),
            mio::PollOpt::edge())?;

        Ok(Parapet {
            state: State::Pending(ProtoConnection::new(Connection {
                token: NEW_CONNECTION_TOKEN,
                protocol: proto::wire::stream::Connection::new(stream, proto::wire::middleware::pipeline::default()),
            })),
            poll: poll,
        })
    }

    /// Attempts to advance the current state if possible.
    pub fn advance_new_connection_state(&mut self) -> Result<(), Error> {
        self.mutate_state(|parapet, state|
            if let State::Pending(mut proto_connection) = state {
                match proto_connection.state.clone() {
                    ProtoState::PendingPing => {
                        let ping = protocol::Ping {
                            user_agent: user_agent(),
                            // TODO: randomise this data
                            data: vec![6, 2, 6, 1, 8, 8],
                        };

                        proto_connection.connection.send_packet(&Packet::Ping(ping.clone()))?;
                        proto_connection.state = ProtoState::PendingPong { original_ping: ping };

                        Ok(State::Pending(proto_connection))
                    },
                    ProtoState::PendingJoinRequest => {
                        proto_connection.connection.send_packet(&Packet::JoinRequest(protocol::JoinRequest))?;

                        proto_connection.state = ProtoState::PendingJoinResponse;
                        Ok(State::Pending(proto_connection))
                    },
                    ProtoState::Complete { join_response } => {
                        let mut network: Network = join_response.network.into();
                        let listener = Parapet::bind(&mut parapet.poll, SERVER_ADDRESS)?;

                        network.set_connection(&join_response.my_uuid, proto_connection.connection);

                        Ok(State::Connected {
                            node: ConnectedNode {
                                uuid: join_response.your_uuid,
                                listener: listener,
                                network: network,
                            },
                            pending_connections: Slab::with_capacity(1024),
                        })
                    },
                    _ => Ok(State::Pending(proto_connection)), // we don't have to do anything.
                }
            } else {
                Ok(state)
            }
        )
    }

    pub fn run(&mut self) -> Result<(), Error> {
        // Create storage for events
        let mut events = mio::Events::with_capacity(1024);

        loop {
            self.poll.poll(&mut events, None).unwrap();

            for event in events.iter() {
                match event.token() {
                    // A pending connection.
                    SERVER_TOKEN => {
                        if let State::Connected { ref mut node, ref mut pending_connections } = self.state {
                            let (socket, addr) = node.listener.accept()?;

                            println!("accepted connection from {:?}", addr);

                            let entry = pending_connections.vacant_entry().expect("ran out of connections");
                            let token = entry.index();

                            self.poll.register(&socket, token, mio::Ready::readable(),
                                mio::PollOpt::edge())?;

                            entry.insert(ProtoNode(ProtoConnection::new(Connection {
                                token: token,
                                protocol: proto::wire::stream::Connection::new(socket, proto::wire::middleware::pipeline::default()),
                            })));
                        } else {
                            // We only start listening after we are successfully connected to the
                            // network.
                            unreachable!();
                        }
                    },
                    token => {
                        println!("event kind: {:?}", event.kind());

                        if event.kind().is_readable() {
                            self.advance_new_connection_state()?;
                        }

                        // TODO: check for `HUP` event.

                        match self.state {
                            State::Pending(ref mut proto_connection) => {
                                assert_eq!(token, NEW_CONNECTION_TOKEN);

                                match proto_connection.state.clone() {
                                    ProtoState::PendingPing => (),
                                    ProtoState::PendingPong { original_ping } => {
                                        if let Some(packet) = proto_connection.connection.receive_packet()? {
                                            if let Packet::Pong(pong) = packet {
                                                // Check if the echoed data is correct.
                                                if pong.data != original_ping.data {
                                                    return Err(Error::InvalidPong{
                                                        expected: original_ping.data.clone(),
                                                        received: pong.data,
                                                    });
                                                }

                                                // Ensure the protocol versions are compatible.
                                                if !pong.user_agent.is_compatible(&original_ping.user_agent) {
                                                    // proto_connection.connection.terminate("protocol versions are not compatible")?;

                                                    // FIXME: Remove the connection.
                                                    unimplemented!();
                                                }

                                                proto_connection.state = ProtoState::PendingJoinRequest;
                                            } else {
                                                return Err(Error::UnexpectedPacket { expected: "pong", received: packet })
                                            }
                                        } else {
                                            // we haven't received a full packet yet.
                                        }
                                    },
                                    ProtoState::PendingJoinRequest  => (),
                                    ProtoState::PendingJoinResponse => {
                                        if let Some(packet) = proto_connection.connection.receive_packet()? {
                                            if let Packet::JoinResponse(join_response) = packet {
                                                proto_connection.state = ProtoState::Complete { join_response: join_response };
                                            } else {
                                                return Err(Error::UnexpectedPacket { expected: "join response", received: packet })
                                            }
                                        }
                                    },
                                    ProtoState::Complete { .. } => {
                                        // nothing to do
                                    },
                                }
                            },
                            State::Connected { ref mut node, ref mut pending_connections } => {
                                let mut pending_connection = pending_connections.entry(token).unwrap();
                                pending_connection.get_mut().process_incoming_data(node)?;
                            },
                            State::Unconnected => unreachable!(),
                        }
                    },
                }
            }
        }
    }

    fn mutate_state<F>(&mut self, mut f: F) -> Result<(), Error>
        where F: FnMut(&mut Self, State) -> Result<State, Error> {
        let mut state = State::Unconnected;
        std::mem::swap(&mut state, &mut self.state);

        self.state = f(self, state)?;

        Ok(())
    }
}

fn main() {
    use clap::{App, Arg};

    let matches = App::new("parapet")
                      // .version("1.0")
                      .author("Dylan <dylanmckay34@gmail.com>")
                      .about("Peer-to-peer build system")
                      .after_help(DESCRIPTION)
                      .arg(Arg::with_name("address")
                           .help("The address of an existing node on a network to connect to")
                           .index(1))
                      .get_matches();

    let mut parapet = if let Some(address) = matches.value_of("address") {
        println!("connecting to existing network on {}", address);

        Parapet::connect(address).unwrap()
    } else {
        println!("running new network on {}:{}", SERVER_ADDRESS.0, SERVER_ADDRESS.1);

        // Create a new network.
        Parapet::new(SERVER_ADDRESS).unwrap()
    };

    parapet.run().unwrap();
}

<commit_msg>Add debugging message for client<commit_after>#![feature(question_mark)]
#![feature(associated_consts)]
#![feature(const_fn)]
#![feature(conservative_impl_trait)]

extern crate mio;
extern crate slab;
extern crate uuid;
extern crate byteorder;
extern crate graphsearch;
extern crate clap;
#[macro_use]
extern crate protocol as proto;

pub use self::proto_connection::{ProtoConnection, ProtoState};
pub use self::proto_node::ProtoNode;
pub use self::connection::*;
pub use self::network::{Network, Node, Edge};
pub use self::error::Error;
pub use self::protocol::Packet;
pub use self::path::Path;

pub mod proto_connection;
pub mod proto_node;
pub mod connection;
pub mod network;
pub mod error;
pub mod protocol;
pub mod path;

use mio::tcp::*;
use slab::Slab;

use uuid::Uuid;

const SERVER_TOKEN: mio::Token = mio::Token(usize::max_value() - 10);
const NEW_CONNECTION_TOKEN: mio::Token = mio::Token(usize::max_value() - 11);

const SERVER_PORT: u16 = 53371;
const SERVER_ADDRESS: (&'static str, u16) = ("127.0.0.1", SERVER_PORT);

const CLIENT_NAME: &'static str = "vanilla";
const CLIENT_VERSION: &'static str = env!("CARGO_PKG_VERSION");

const PROTOCOL_MAJOR: u16 = 0;
const PROTOCOL_REVISION: u16 = 0;

const DESCRIPTION: &'static str = "
    If you pass an address, it will connect to an existing node on
    some network, otherwise a new network will be created.
";

fn user_agent() -> protocol::UserAgent {
    protocol::UserAgent {
        client: format!("{} v{}", CLIENT_NAME, CLIENT_VERSION),
        protocol_major: PROTOCOL_MAJOR,
        protocol_revision: PROTOCOL_REVISION,
    }
}

pub struct ConnectedNode
{
    pub uuid: Uuid,
    pub listener: TcpListener,

    /// The network we are apart of.
    pub network: Network,
}

// Flow:
//
// Client sends 'JoinRequest' to some node
// Server responds with 'JoinResponse'
// Client is now ready to act as server.

pub enum State
{
    Unconnected,
    Pending(ProtoConnection),
    /// We are now a connected node in the network.
    Connected {
        node: ConnectedNode,

        pending_connections: Slab<ProtoNode, mio::Token>,
    },
}

pub struct Parapet
{
    pub state: State,
    pub poll: mio::Poll,
}

impl Parapet
{
    /// Create a new network.
    pub fn new<A>(addr: A) -> Result<Self, Error>
        where A: std::net::ToSocketAddrs {
        let mut poll = mio::Poll::new()?;

        let listener = Parapet::bind(&mut poll, addr)?;

        Ok(Parapet {
            state: State::Connected {
                node: ConnectedNode {
                    uuid: Uuid::new_v4(),
                    listener: listener,
                    network: Network::new(),
                },
                pending_connections: Slab::with_capacity(1024),
            },
            poll: poll,
        })
    }

    /// Create a new tcp listener locally.
    fn bind<A>(poll: &mio::Poll, addr: A) -> Result<TcpListener, Error>
        where A: std::net::ToSocketAddrs {
        let mut addresses = addr.to_socket_addrs()?;
        let address = addresses.next().expect("could not resolve address");

        let listener = TcpListener::bind(&address)?;

        poll.register(&listener, SERVER_TOKEN, mio::Ready::readable(),
            mio::PollOpt::edge())?;

        Ok(listener)
    }

    /// Connect to an existing network.
    /// * `addr` - Any node on the network.
    pub fn connect<A>(addr: A) -> Result<Self, std::io::Error>
        where A: std::net::ToSocketAddrs {
        let mut addresses = addr.to_socket_addrs()?;
        let address = addresses.next().expect("could not resolve address");

        let stream = TcpStream::connect(&address)?;

        let poll = mio::Poll::new()?;
        poll.register(&stream, NEW_CONNECTION_TOKEN, mio::Ready::writable(),
            mio::PollOpt::edge())?;

        Ok(Parapet {
            state: State::Pending(ProtoConnection::new(Connection {
                token: NEW_CONNECTION_TOKEN,
                protocol: proto::wire::stream::Connection::new(stream, proto::wire::middleware::pipeline::default()),
            })),
            poll: poll,
        })
    }

    /// Attempts to advance the current state if possible.
    pub fn advance_new_connection_state(&mut self) -> Result<(), Error> {
        self.mutate_state(|parapet, state|
            if let State::Pending(mut proto_connection) = state {
                match proto_connection.state.clone() {
                    ProtoState::PendingPing => {
                        let ping = protocol::Ping {
                            user_agent: user_agent(),
                            // TODO: randomise this data
                            data: vec![6, 2, 6, 1, 8, 8],
                        };

                        proto_connection.connection.send_packet(&Packet::Ping(ping.clone()))?;
                        proto_connection.state = ProtoState::PendingPong { original_ping: ping };

                        Ok(State::Pending(proto_connection))
                    },
                    ProtoState::PendingJoinRequest => {
                        proto_connection.connection.send_packet(&Packet::JoinRequest(protocol::JoinRequest))?;

                        proto_connection.state = ProtoState::PendingJoinResponse;
                        Ok(State::Pending(proto_connection))
                    },
                    ProtoState::Complete { join_response } => {
                        let mut network: Network = join_response.network.into();
                        let listener = Parapet::bind(&mut parapet.poll, SERVER_ADDRESS)?;

                        network.set_connection(&join_response.my_uuid, proto_connection.connection);

                        println!("connected to network (with node uuid {})", &join_response.my_uuid);

                        Ok(State::Connected {
                            node: ConnectedNode {
                                uuid: join_response.your_uuid,
                                listener: listener,
                                network: network,
                            },
                            pending_connections: Slab::with_capacity(1024),
                        })
                    },
                    _ => Ok(State::Pending(proto_connection)), // we don't have to do anything.
                }
            } else {
                Ok(state)
            }
        )
    }

    pub fn run(&mut self) -> Result<(), Error> {
        // Create storage for events
        let mut events = mio::Events::with_capacity(1024);

        loop {
            self.poll.poll(&mut events, None).unwrap();

            for event in events.iter() {
                match event.token() {
                    // A pending connection.
                    SERVER_TOKEN => {
                        if let State::Connected { ref mut node, ref mut pending_connections } = self.state {
                            let (socket, addr) = node.listener.accept()?;

                            println!("accepted connection from {:?}", addr);

                            let entry = pending_connections.vacant_entry().expect("ran out of connections");
                            let token = entry.index();

                            self.poll.register(&socket, token, mio::Ready::readable(),
                                mio::PollOpt::edge())?;

                            entry.insert(ProtoNode(ProtoConnection::new(Connection {
                                token: token,
                                protocol: proto::wire::stream::Connection::new(socket, proto::wire::middleware::pipeline::default()),
                            })));
                        } else {
                            // We only start listening after we are successfully connected to the
                            // network.
                            unreachable!();
                        }
                    },
                    token => {
                        println!("event kind: {:?}", event.kind());

                        if event.kind().is_readable() {
                            self.advance_new_connection_state()?;
                        }

                        // TODO: check for `HUP` event.

                        match self.state {
                            State::Pending(ref mut proto_connection) => {
                                assert_eq!(token, NEW_CONNECTION_TOKEN);

                                match proto_connection.state.clone() {
                                    ProtoState::PendingPing => (),
                                    ProtoState::PendingPong { original_ping } => {
                                        if let Some(packet) = proto_connection.connection.receive_packet()? {
                                            if let Packet::Pong(pong) = packet {
                                                // Check if the echoed data is correct.
                                                if pong.data != original_ping.data {
                                                    return Err(Error::InvalidPong{
                                                        expected: original_ping.data.clone(),
                                                        received: pong.data,
                                                    });
                                                }

                                                // Ensure the protocol versions are compatible.
                                                if !pong.user_agent.is_compatible(&original_ping.user_agent) {
                                                    // proto_connection.connection.terminate("protocol versions are not compatible")?;

                                                    // FIXME: Remove the connection.
                                                    unimplemented!();
                                                }

                                                proto_connection.state = ProtoState::PendingJoinRequest;
                                            } else {
                                                return Err(Error::UnexpectedPacket { expected: "pong", received: packet })
                                            }
                                        } else {
                                            // we haven't received a full packet yet.
                                        }
                                    },
                                    ProtoState::PendingJoinRequest  => (),
                                    ProtoState::PendingJoinResponse => {
                                        if let Some(packet) = proto_connection.connection.receive_packet()? {
                                            if let Packet::JoinResponse(join_response) = packet {
                                                proto_connection.state = ProtoState::Complete { join_response: join_response };
                                            } else {
                                                return Err(Error::UnexpectedPacket { expected: "join response", received: packet })
                                            }
                                        }
                                    },
                                    ProtoState::Complete { .. } => {
                                        // nothing to do
                                    },
                                }
                            },
                            State::Connected { ref mut node, ref mut pending_connections } => {
                                let mut pending_connection = pending_connections.entry(token).unwrap();
                                pending_connection.get_mut().process_incoming_data(node)?;
                            },
                            State::Unconnected => unreachable!(),
                        }
                    },
                }
            }
        }
    }

    fn mutate_state<F>(&mut self, mut f: F) -> Result<(), Error>
        where F: FnMut(&mut Self, State) -> Result<State, Error> {
        let mut state = State::Unconnected;
        std::mem::swap(&mut state, &mut self.state);

        self.state = f(self, state)?;

        Ok(())
    }
}

fn main() {
    use clap::{App, Arg};

    let matches = App::new("parapet")
                      // .version("1.0")
                      .author("Dylan <dylanmckay34@gmail.com>")
                      .about("Peer-to-peer build system")
                      .after_help(DESCRIPTION)
                      .arg(Arg::with_name("address")
                           .help("The address of an existing node on a network to connect to")
                           .index(1))
                      .get_matches();

    let mut parapet = if let Some(address) = matches.value_of("address") {
        println!("connecting to existing network on {}", address);

        Parapet::connect(address).unwrap()
    } else {
        println!("running new network on {}:{}", SERVER_ADDRESS.0, SERVER_ADDRESS.1);

        // Create a new network.
        Parapet::new(SERVER_ADDRESS).unwrap()
    };

    parapet.run().unwrap();
}

<|endoftext|>


<commit_before>// Copyright 2016 Joe Wilm, The Alacritty Project Contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//! Alacritty - The GPU Enhanced Terminal
#![feature(question_mark)]
#![feature(range_contains)]
#![feature(inclusive_range_syntax)]
#![feature(drop_types_in_const)]
#![feature(unicode)]
#![feature(step_trait)]
#![feature(custom_derive, plugin)]
#![plugin(serde_macros)]

extern crate cgmath;
extern crate errno;
extern crate font;
extern crate glutin;
extern crate libc;
extern crate notify;
extern crate parking_lot;
extern crate serde;
extern crate serde_yaml;

#[macro_use]
extern crate bitflags;

#[macro_use]
mod macros;

mod renderer;
pub mod grid;
mod meter;
pub mod config;
mod input;
mod index;
mod tty;
pub mod ansi;
mod term;
mod util;
mod io;
mod sync;

use std::io::{Write, BufWriter, Read};
use std::sync::{mpsc, Arc};

use sync::PriorityMutex;

use config::Config;
use font::FontDesc;
use meter::Meter;
use renderer::{QuadRenderer, GlyphCache};
use term::Term;
use tty::process_should_exit;
use util::thread;

use io::{Utf8Chars, Utf8CharsError};

/// Channel used by resize handling on mac
static mut resize_sender: Option<mpsc::Sender<(u32, u32)>> = None;

/// Resize handling for Mac
fn window_resize_handler(width: u32, height: u32) {
    unsafe {
        if let Some(ref tx) = resize_sender {
            let _ = tx.send((width, height));
        }
    }
}

#[derive(Debug, Eq, PartialEq, Copy, Clone, Default)]
pub struct Rgb {
    r: u8,
    g: u8,
    b: u8,
}

mod gl {
    include!(concat!(env!("OUT_DIR"), "/gl_bindings.rs"));
}

fn main() {
    // Load configuration
    let config = match Config::load() {
        Err(err) => match err {
            // Use default config when not found
            config::Error::NotFound => Config::default(),
            // Exit when there's a problem with it
            _ => die!("{}", err),
        },
        Ok(config) => config,
    };

    let font = config.font();
    let dpi = config.dpi();
    let render_timer = config.render_timer();

    let mut window = glutin::WindowBuilder::new()
                                           .with_vsync()
                                           .with_title("Alacritty")
                                           .build().unwrap();

    window.set_window_resize_callback(Some(window_resize_handler as fn(u32, u32)));

    gl::load_with(|symbol| window.get_proc_address(symbol) as *const _);
    let (width, height) = window.get_inner_size_pixels().unwrap();
    let dpr = window.hidpi_factor();

    println!("device_pixel_ratio: {}", dpr);

    let _ = unsafe { window.make_current() };
    unsafe {
        gl::Viewport(0, 0, width as i32, height as i32);
        gl::Enable(gl::BLEND);
        gl::BlendFunc(gl::SRC1_COLOR, gl::ONE_MINUS_SRC1_COLOR);
        gl::Enable(gl::MULTISAMPLE);
    }

    let desc = FontDesc::new(font.family(), font.style());
    let mut rasterizer = font::Rasterizer::new(dpi.x(), dpi.y(), dpr);

    let metrics = rasterizer.metrics(&desc, font.size());
    let cell_width = (metrics.average_advance + font.offset().x() as f64) as u32;
    let cell_height = (metrics.line_height + font.offset().y() as f64) as u32;

    println!("Cell Size: ({} x {})", cell_width, cell_height);

    let terminal = Term::new(width as f32, height as f32, cell_width as f32, cell_height as f32);

    let mut reader = terminal.tty().reader();
    let writer = terminal.tty().writer();

    let mut glyph_cache = GlyphCache::new(rasterizer, desc, font.size());

    let (tx, rx) = mpsc::channel();
    unsafe {
        resize_sender = Some(tx.clone());
    }

    let terminal = Arc::new(PriorityMutex::new(terminal));
    let term_ref = terminal.clone();
    let term_updater = terminal.clone();

    // Rendering thread
    //
    // Holds lock only when updating terminal
    let reader_thread = thread::spawn_named("TTY Reader", move || {
        let mut buf = [0u8; 4096];
        let mut start = 0;
        let mut pty_parser = ansi::Parser::new();

        'reader: loop {
            let got = reader.read(&mut buf[start..]).expect("pty fd active");
            let mut remain = 0;

            // if `start` is nonzero, then actual bytes in buffer is > `got` by `start` bytes.
            let end = start + got;
            let mut terminal = term_updater.lock_low();
            for c in Utf8Chars::new(&buf[..end]) {
                match c {
                    Ok(c) => pty_parser.advance(&mut *terminal, c),
                    Err(err) => match err {
                        Utf8CharsError::IncompleteUtf8(unused) => {
                            remain = unused;
                            break;
                        },
                        _ => panic!("{}", err),
                    }
                }
            }

            // Move any leftover bytes to front of buffer
            for i in 0..remain {
                buf[i] = buf[end - (remain - i)];
            }
            start = remain;
        }
    });

    let mut meter = Meter::new();

    let window = Arc::new(window);
    let window_ref = window.clone();

    // Create renderer
    let mut renderer = QuadRenderer::new(width, height);

    // Initialize glyph cache
    {
        let init_start = ::std::time::Instant::now();
        println!("Initializing glyph cache");
        let terminal = term_ref.lock_high();
        renderer.with_api(terminal.size_info(), |mut api| {
            glyph_cache.init(&mut api);
        });

        let stop = init_start.elapsed();
        let stop_f = stop.as_secs() as f64 + stop.subsec_nanos() as f64 / 1_000_000_000f64;
        println!("Finished initializing glyph cache in {}", stop_f);
    }

    let mut input_processor = input::Processor::new();

    'main_loop: loop {

        // Scope ensures terminal lock isn't held when calling swap_buffers
        {
            // Acquire term lock
            let mut terminal = term_ref.lock_high();

            // Resize events new_size and are handled outside the poll_events
            // iterator. This has the effect of coalescing multiple resize
            // events into one.
            let mut new_size = None;

            // Process input events
            {
                let mut writer = BufWriter::new(&writer);
                for event in window_ref.poll_events() {
                    match event {
                        glutin::Event::Closed => break 'main_loop,
                        glutin::Event::ReceivedCharacter(c) => {
                            match c {
                                // Ignore BACKSPACE and DEL. These are handled specially.
                                '\u{8}' | '\u{7f}' => (),
                                _ => {
                                    let encoded = c.encode_utf8();
                                    writer.write(encoded.as_slice()).unwrap();
                                }
                            }
                        },
                        glutin::Event::Resized(w, h) => {
                            new_size = Some((w, h));
                        },
                        glutin::Event::KeyboardInput(state, _code, key, mods) => {
                            input_processor.process(state,
                                                    key,
                                                    mods,
                                                    &mut input::WriteNotifier(&mut writer),
                                                    *terminal.mode())
                        },
                        _ => (),
                    }
                }
            }

            unsafe {
                gl::ClearColor(0.0, 0.0, 0.00, 1.0);
                gl::Clear(gl::COLOR_BUFFER_BIT);
            }

            // Check for any out-of-band resize events (mac only)
            while let Ok(sz) = rx.try_recv() {
                new_size = Some(sz);
            }

            // Receive any resize events; only call gl::Viewport on last
            // available
            if let Some((w, h)) = new_size.take() {
                terminal.resize(w as f32, h as f32);
                renderer.resize(w as i32, h as i32);
            }

            {
                // Draw grid
                {
                    let _sampler = meter.sampler();

                    let size_info = terminal.size_info().clone();
                    renderer.with_api(&size_info, |mut api| {
                        // Draw the grid
                        api.render_grid(&terminal.render_grid(), &mut glyph_cache);
                    })
                }

                // Draw render timer
                if render_timer {
                    let timing = format!("{:.3} usec", meter.average());
                    let color = Rgb { r: 0xd5, g: 0x4e, b: 0x53 };
                    renderer.with_api(terminal.size_info(), |mut api| {
                        api.render_string(&timing[..], &mut glyph_cache, &color);
                    });
                }
            }
        }

        window.swap_buffers().unwrap();

        if process_should_exit() {
            break;
        }
    }

    reader_thread.join().ok();
    println!("Goodbye");
}

<commit_msg>Fix arrow keys on macOS<commit_after>// Copyright 2016 Joe Wilm, The Alacritty Project Contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//! Alacritty - The GPU Enhanced Terminal
#![feature(question_mark)]
#![feature(range_contains)]
#![feature(inclusive_range_syntax)]
#![feature(drop_types_in_const)]
#![feature(unicode)]
#![feature(step_trait)]
#![feature(custom_derive, plugin)]
#![plugin(serde_macros)]

extern crate cgmath;
extern crate errno;
extern crate font;
extern crate glutin;
extern crate libc;
extern crate notify;
extern crate parking_lot;
extern crate serde;
extern crate serde_yaml;

#[macro_use]
extern crate bitflags;

#[macro_use]
mod macros;

mod renderer;
pub mod grid;
mod meter;
pub mod config;
mod input;
mod index;
mod tty;
pub mod ansi;
mod term;
mod util;
mod io;
mod sync;

use std::io::{Write, BufWriter, Read};
use std::sync::{mpsc, Arc};

use sync::PriorityMutex;

use config::Config;
use font::FontDesc;
use meter::Meter;
use renderer::{QuadRenderer, GlyphCache};
use term::Term;
use tty::process_should_exit;
use util::thread;

use io::{Utf8Chars, Utf8CharsError};

/// Channel used by resize handling on mac
static mut resize_sender: Option<mpsc::Sender<(u32, u32)>> = None;

/// Resize handling for Mac
fn window_resize_handler(width: u32, height: u32) {
    unsafe {
        if let Some(ref tx) = resize_sender {
            let _ = tx.send((width, height));
        }
    }
}

#[derive(Debug, Eq, PartialEq, Copy, Clone, Default)]
pub struct Rgb {
    r: u8,
    g: u8,
    b: u8,
}

mod gl {
    include!(concat!(env!("OUT_DIR"), "/gl_bindings.rs"));
}

fn main() {
    // Load configuration
    let config = match Config::load() {
        Err(err) => match err {
            // Use default config when not found
            config::Error::NotFound => Config::default(),
            // Exit when there's a problem with it
            _ => die!("{}", err),
        },
        Ok(config) => config,
    };

    let font = config.font();
    let dpi = config.dpi();
    let render_timer = config.render_timer();

    let mut window = glutin::WindowBuilder::new()
                                           .with_vsync()
                                           .with_title("Alacritty")
                                           .build().unwrap();

    window.set_window_resize_callback(Some(window_resize_handler as fn(u32, u32)));

    gl::load_with(|symbol| window.get_proc_address(symbol) as *const _);
    let (width, height) = window.get_inner_size_pixels().unwrap();
    let dpr = window.hidpi_factor();

    println!("device_pixel_ratio: {}", dpr);

    let _ = unsafe { window.make_current() };
    unsafe {
        gl::Viewport(0, 0, width as i32, height as i32);
        gl::Enable(gl::BLEND);
        gl::BlendFunc(gl::SRC1_COLOR, gl::ONE_MINUS_SRC1_COLOR);
        gl::Enable(gl::MULTISAMPLE);
    }

    let desc = FontDesc::new(font.family(), font.style());
    let mut rasterizer = font::Rasterizer::new(dpi.x(), dpi.y(), dpr);

    let metrics = rasterizer.metrics(&desc, font.size());
    let cell_width = (metrics.average_advance + font.offset().x() as f64) as u32;
    let cell_height = (metrics.line_height + font.offset().y() as f64) as u32;

    println!("Cell Size: ({} x {})", cell_width, cell_height);

    let terminal = Term::new(width as f32, height as f32, cell_width as f32, cell_height as f32);

    let mut reader = terminal.tty().reader();
    let writer = terminal.tty().writer();

    let mut glyph_cache = GlyphCache::new(rasterizer, desc, font.size());

    let (tx, rx) = mpsc::channel();
    unsafe {
        resize_sender = Some(tx.clone());
    }

    let terminal = Arc::new(PriorityMutex::new(terminal));
    let term_ref = terminal.clone();
    let term_updater = terminal.clone();

    // Rendering thread
    //
    // Holds lock only when updating terminal
    let reader_thread = thread::spawn_named("TTY Reader", move || {
        let mut buf = [0u8; 4096];
        let mut start = 0;
        let mut pty_parser = ansi::Parser::new();

        'reader: loop {
            let got = reader.read(&mut buf[start..]).expect("pty fd active");
            let mut remain = 0;

            // if `start` is nonzero, then actual bytes in buffer is > `got` by `start` bytes.
            let end = start + got;
            let mut terminal = term_updater.lock_low();
            for c in Utf8Chars::new(&buf[..end]) {
                match c {
                    Ok(c) => pty_parser.advance(&mut *terminal, c),
                    Err(err) => match err {
                        Utf8CharsError::IncompleteUtf8(unused) => {
                            remain = unused;
                            break;
                        },
                        _ => panic!("{}", err),
                    }
                }
            }

            // Move any leftover bytes to front of buffer
            for i in 0..remain {
                buf[i] = buf[end - (remain - i)];
            }
            start = remain;
        }
    });

    let mut meter = Meter::new();

    let window = Arc::new(window);
    let window_ref = window.clone();

    // Create renderer
    let mut renderer = QuadRenderer::new(width, height);

    // Initialize glyph cache
    {
        let init_start = ::std::time::Instant::now();
        println!("Initializing glyph cache");
        let terminal = term_ref.lock_high();
        renderer.with_api(terminal.size_info(), |mut api| {
            glyph_cache.init(&mut api);
        });

        let stop = init_start.elapsed();
        let stop_f = stop.as_secs() as f64 + stop.subsec_nanos() as f64 / 1_000_000_000f64;
        println!("Finished initializing glyph cache in {}", stop_f);
    }

    let mut input_processor = input::Processor::new();

    'main_loop: loop {

        // Scope ensures terminal lock isn't held when calling swap_buffers
        {
            // Acquire term lock
            let mut terminal = term_ref.lock_high();

            // Resize events new_size and are handled outside the poll_events
            // iterator. This has the effect of coalescing multiple resize
            // events into one.
            let mut new_size = None;

            // Process input events
            {
                let mut writer = BufWriter::new(&writer);
                for event in window_ref.poll_events() {
                    match event {
                        glutin::Event::Closed => break 'main_loop,
                        glutin::Event::ReceivedCharacter(c) => {
                            match c {
                                // Ignore BACKSPACE and DEL. These are handled specially.
                                '\u{8}' | '\u{7f}' => (),
                                // OSX arrow keys send invalid characters; ignore.
                                '\u{f700}' | '\u{f701}' | '\u{f702}' | '\u{f703}' => (),
                                _ => {
                                    let encoded = c.encode_utf8();
                                    writer.write(encoded.as_slice()).unwrap();
                                }
                            }
                        },
                        glutin::Event::Resized(w, h) => {
                            new_size = Some((w, h));
                        },
                        glutin::Event::KeyboardInput(state, _code, key, mods) => {
                            input_processor.process(state,
                                                    key,
                                                    mods,
                                                    &mut input::WriteNotifier(&mut writer),
                                                    *terminal.mode())
                        },
                        _ => (),
                    }
                }
            }

            unsafe {
                gl::ClearColor(0.0, 0.0, 0.00, 1.0);
                gl::Clear(gl::COLOR_BUFFER_BIT);
            }

            // Check for any out-of-band resize events (mac only)
            while let Ok(sz) = rx.try_recv() {
                new_size = Some(sz);
            }

            // Receive any resize events; only call gl::Viewport on last
            // available
            if let Some((w, h)) = new_size.take() {
                terminal.resize(w as f32, h as f32);
                renderer.resize(w as i32, h as i32);
            }

            {
                // Draw grid
                {
                    let _sampler = meter.sampler();

                    let size_info = terminal.size_info().clone();
                    renderer.with_api(&size_info, |mut api| {
                        // Draw the grid
                        api.render_grid(&terminal.render_grid(), &mut glyph_cache);
                    })
                }

                // Draw render timer
                if render_timer {
                    let timing = format!("{:.3} usec", meter.average());
                    let color = Rgb { r: 0xd5, g: 0x4e, b: 0x53 };
                    renderer.with_api(terminal.size_info(), |mut api| {
                        api.render_string(&timing[..], &mut glyph_cache, &color);
                    });
                }
            }
        }

        window.swap_buffers().unwrap();

        if process_should_exit() {
            break;
        }
    }

    reader_thread.join().ok();
    println!("Goodbye");
}

<|endoftext|>


<commit_before>use camera::Camera;
use vector::Vector;
use std::fs::File;
use std::io::prelude::*;
mod sphere;
mod ray;
mod colour;
mod vector;
mod camera;

fn main() {
	let width = 640;
	let height = 480;
	let campos =  Vector::new(1.0,1.0,1.0);
	let camlook = Vector::empty();
	let camup = Vector::new(0.0, 1.0,0.0);
	let camera =  Camera::new(campos, camlook,camup, 45, width, height);

	let mut file = File::create("img.pbm");

	match  file {
		Ok(mut file) => {
			file.write("P3\n".as_bytes());
			file.write(format!("{} {} 255\n", camera.width, camera.height).as_bytes());
			for x in 0..width {
				for y in 0..height {
					let colour = camera.trace_ray(x, y);
					file.write(format!("{} {} {}\n", colour.red, colour.green, colour.blue).as_bytes());
				}
			}

		},

		Err(str) => {
			println!("{:?}", str);
		}
	}

}<commit_msg>Remove warnings from using unstable fs and io<commit_after>#![feature(fs,io)]
use camera::Camera;
use vector::Vector;
use std::fs::File;
use std::io::prelude::*;
mod sphere;
mod ray;
mod colour;
mod vector;
mod camera;

fn main() {
	let width = 640;
	let height = 480;
	let campos =  Vector::new(1.0,1.0,1.0);
	let camlook = Vector::empty();
	let camup = Vector::new(0.0, 1.0,0.0);
	let camera =  Camera::new(campos, camlook,camup, 45, width, height);

	let mut file = File::create("img.pbm");

	match  file {
		Ok(mut file) => {
			file.write("P3\n".as_bytes());
			file.write(format!("{} {} 255\n", camera.width, camera.height).as_bytes());
			for x in 0..width {
				for y in 0..height {
					let colour = camera.trace_ray(x, y);
					file.write(format!("{} {} {}\n", colour.red, colour.green, colour.blue).as_bytes());
				}
			}

		},

		Err(str) => {
			println!("{:?}", str);
		}
	}

}<|endoftext|>


<commit_before>#![allow(dead_code)]
#![allow(unused_variables)]
#![allow(unused_mut)]
#![allow(unused_imports)]

#![feature(phase)]

extern crate collections;

use std::os::{set_exit_status, getenv};
use std::path::Path;
use std::io;
use std::io::IoResult;
use std::io::IoError;
use std::io::IoErrorKind::{OtherIoError};
use std::io::process::Command;

use std::io::fs::{
   PathExtensions,
   mkdir_recursive,
   unlink,
   copy
};

use file_conflict::{
   find,
   ConflictType,
   Wuala,
   Dropbox,
   Conflict,
   ConflictingFile
};

use user_reply::{
   UserReply,
   TakeFile,
   MoveToTrash,
   ShowDiff,
   ShowDiffWith,
   ShowDiffBetween,
   Skip,
   Quit,
   Help
};

use args::{
   ResolveWuala,
   ResolveDropbox,
   PrintHelp,
   InvalidUsage
};

use app_result::{
   AppResult,
   AppError
};

mod app_result;
mod file_system;
mod user_reply;
mod wuala_conflict;
mod dropbox_conflict;
mod file_conflict;
mod file_conflict_types;
mod parser;
mod appdirs;
mod args;
mod stream;

fn main()
{
//   let cmd = ResolveWuala(Path::new("/home/dan/test/confsolve"));
   let cmd = args::get_command();
   match cmd {
      ResolveWuala(path) => {
         resolve_conflicts(Wuala, &path)
            .unwrap_or_else(|err| { exit_with_error(format!("{}", err)); });
      }

      ResolveDropbox(path) => {
         resolve_conflicts(Dropbox, &path)
            .unwrap_or_else(|err| { exit_with_error(format!("{}", err)); });
      }

      PrintHelp => args::print_help(),

      InvalidUsage => {
         exit_with_error("Invalid usage!".to_string());
         args::print_help();
      }
   }
}

fn exit_with_error(error: String)
{
   let mut stderr = io::stderr();
   let _ = writeln!(stderr, "confsolve: {}", error);
   set_exit_status(1);
}

/// Finds file conflicts of type `conf_type` starting at the directory `start_dir`,
/// recursively visiting every file, asking the user how each conflict should
/// be handled and then executing the user command.
fn resolve_conflicts(conf_type: ConflictType, start_dir: &Path) -> AppResult<()>
{
   let mut stdin = io::stdin();
   let confs = try!(file_conflict::find(conf_type, start_dir));
   for conf in confs.iter() {
      if ! conf.original_path.is_file() {
         println!("\nFound conflicts for the file '{}', but the file itself is missing! Skipping it.",
                  conf.original_path.display());
         continue;
      }

      let num_conf_files = conf.conflicting_files.len();
      println!("\n{}", conf);
      loop {
         print!("{}", "(T)ake File (NUM) | (M)ove to Trash | Show (D)iff (NUM [NUM]) | (S)kip | (Q)uit | (H)elp: ");
         let mut line = try!(stdin.read_line());
         match user_reply::parse(&line, num_conf_files) {
            Some(reply) => {
               match reply {
                  TakeFile(num) => {
                     let ref take_file = conf.conflicting_files[num - 1].path;
                     for conf_file in conf.conflicting_files.iter() {
                        if conf_file.path != *take_file {
                           try!(move_to_trash(&conf_file.path));
                        }
                     }

                     try!(move_to_trash(&conf.original_path));
                     try!(copy(take_file, &conf.original_path));
                  }

                  MoveToTrash => {
                     for conf_file in conf.conflicting_files.iter() {
                        try!(move_to_trash(&conf_file.path));
                     }
                  }

                  ShowDiff => {
                     try!(show_diff(&conf.original_path, &conf.conflicting_files[0].path));
                  }

                  ShowDiffWith(num) => {
                     try!(show_diff(&conf.original_path, &conf.conflicting_files[num - 1].path));
                  }

                  ShowDiffBetween(num1, num2) => {
                     try!(show_diff(&conf.conflicting_files[num1 - 1].path,
                                    &conf.conflicting_files[num2 - 1].path));
                  }

                  Skip => { break; }
                  Quit => { return Ok(()); }
                  Help => try!(print_runtime_help())
               }
            }

            None => {
               // remove newline at end of line
               line.pop();
               println!("\nInvalid user input: '{}' !\n", line);
            }
         }
      }
   }

   Ok(())
}

/// Calls the diff command specified by the environment variable `CONFSOLVE_DIFF`
/// or - if not defined - `gvimdiff -f` with the files `file1` and `file2`.
fn show_diff(file1: &Path, file2: &Path) -> AppResult<()>
{
   let diff_cmd_and_args = getenv("CONFSOLVE_DIFF").unwrap_or("gvimdiff -f".to_string());
   let diff_cmd_and_args = diff_cmd_and_args.as_slice().split(' ').collect::<Vec<&str>>();

   let diff_cmd = diff_cmd_and_args[0];

//   let mut args = diff_cmd_and_args.iter().collect::<Vec<&str>>();
//   let mut args = diff_cmd_and_args.iter().skip(1).collect::<Vec<&str>>();
//   let mut args = diff_cmd_and_args;
//   args.push(file1.as_str().unwrap());
//   args.push(file2.as_str().unwrap());

//   let mut cmd = Command::new(diff_cmd);
//   for arg in args.iter() {
//      cmd.arg(arg);
//   }
//
//   try!(cmd.output());
   Ok(())
}

/// Moves `file` into the trash directory of confsolve.
fn move_to_trash(file: &Path) -> AppResult<()>
{
   let filename = try!(file.filename_str()
      .ok_or(AppError::from_string(format!("Couldn't get filename from path '{}'!", file.display()))));

   let mut trash_file = try!(trash_dir());
   trash_file.set_filename(filename);

   try!(copy(file, &trash_file));
   try!(unlink(file));

   Ok(())
}

/// Returns the trash directory of confsolve, where all deleted/moved files are put into.
fn trash_dir() -> AppResult<Path>
{
   let mut dir = try!(appdirs::cache("confsolve")
      .ok_or(AppError::from_string(format!("Couldn't get cache directory!"))));

   dir.push("trash");
   if ! dir.is_dir() {
      try!(mkdir_recursive(&dir, io::USER_RWX));
   }

   Ok(dir)
}

fn print_runtime_help() -> AppResult<()>
{
   let trash_dir = try!(trash_dir());
   let dir_str   = trash_dir.display();

   println!("
Runtime Options:
   (T)ake File (NUM) => By pressing 't' and a number (e.g 't1'), the conflicting file with the
                        number NUM is used as the new version. A copy of the
                        current file and the other conflicting files is put
                        into the trash directory '{}'.

   (M)ove to Trash   => By pressing 'm', all conflicting files are
                        moved into the trash directory '{}'.

   Show (D)iff (NUM) => By pressing 'd' and a number (e.g 'd1'), the difference between the
                        current file and the conflicting file NUM is shown.
                        If there's only one conflicting file, then only pressing
                        'd' is sufficient.
                        By pressing 'd' and two numbers (e.g 'd1 2'), the difference between
                        the two conflicting files is shown.
                        The diff tool can be specified by the user by setting the environment
                        variable 'CONFSOLVE_DIFF'. The default diff tool is 'gvimdiff -f'.

   (S)kip            => By pressing 's', the current conflict is skipped
                        and the next one is shown.

   (Q)uit            => By pressing 'q', the application is quit.

   (H)elp            => By pressing 'h', this help is printed.
", dir_str, dir_str);

   Ok(())
}
<commit_msg>Fix runtime help text<commit_after>#![allow(dead_code)]
#![allow(unused_variables)]
#![allow(unused_mut)]
#![allow(unused_imports)]

#![feature(phase)]

extern crate collections;

use std::os::{set_exit_status, getenv};
use std::path::Path;
use std::io;
use std::io::IoResult;
use std::io::IoError;
use std::io::IoErrorKind::{OtherIoError};
use std::io::process::Command;

use std::io::fs::{
   PathExtensions,
   mkdir_recursive,
   unlink,
   copy
};

use file_conflict::{
   find,
   ConflictType,
   Wuala,
   Dropbox,
   Conflict,
   ConflictingFile
};

use user_reply::{
   UserReply,
   TakeFile,
   MoveToTrash,
   ShowDiff,
   ShowDiffWith,
   ShowDiffBetween,
   Skip,
   Quit,
   Help
};

use args::{
   ResolveWuala,
   ResolveDropbox,
   PrintHelp,
   InvalidUsage
};

use app_result::{
   AppResult,
   AppError
};

mod app_result;
mod file_system;
mod user_reply;
mod wuala_conflict;
mod dropbox_conflict;
mod file_conflict;
mod file_conflict_types;
mod parser;
mod appdirs;
mod args;
mod stream;

fn main()
{
//   let cmd = ResolveWuala(Path::new("/home/dan/test/confsolve"));
   let cmd = args::get_command();
   match cmd {
      ResolveWuala(path) => {
         resolve_conflicts(Wuala, &path)
            .unwrap_or_else(|err| { exit_with_error(format!("{}", err)); });
      }

      ResolveDropbox(path) => {
         resolve_conflicts(Dropbox, &path)
            .unwrap_or_else(|err| { exit_with_error(format!("{}", err)); });
      }

      PrintHelp => args::print_help(),

      InvalidUsage => {
         exit_with_error("Invalid usage!".to_string());
         args::print_help();
      }
   }
}

fn exit_with_error(error: String)
{
   let mut stderr = io::stderr();
   let _ = writeln!(stderr, "confsolve: {}", error);
   set_exit_status(1);
}

/// Finds file conflicts of type `conf_type` starting at the directory `start_dir`,
/// recursively visiting every file, asking the user how each conflict should
/// be handled and then executing the user command.
fn resolve_conflicts(conf_type: ConflictType, start_dir: &Path) -> AppResult<()>
{
   let mut stdin = io::stdin();
   let confs = try!(file_conflict::find(conf_type, start_dir));
   for conf in confs.iter() {
      if ! conf.original_path.is_file() {
         println!("\nFound conflicts for the file '{}', but the file itself is missing! Skipping it.",
                  conf.original_path.display());
         continue;
      }

      let num_conf_files = conf.conflicting_files.len();
      println!("\n{}", conf);
      loop {
         print!("{}", "(T)ake File (NUM) | (M)ove to Trash | Show (D)iff (NUM [NUM]) | (S)kip | (Q)uit | (H)elp: ");
         let mut line = try!(stdin.read_line());
         match user_reply::parse(&line, num_conf_files) {
            Some(reply) => {
               match reply {
                  TakeFile(num) => {
                     let ref take_file = conf.conflicting_files[num - 1].path;
                     for conf_file in conf.conflicting_files.iter() {
                        if conf_file.path != *take_file {
                           try!(move_to_trash(&conf_file.path));
                        }
                     }

                     try!(move_to_trash(&conf.original_path));
                     try!(copy(take_file, &conf.original_path));
                  }

                  MoveToTrash => {
                     for conf_file in conf.conflicting_files.iter() {
                        try!(move_to_trash(&conf_file.path));
                     }
                  }

                  ShowDiff => {
                     try!(show_diff(&conf.original_path, &conf.conflicting_files[0].path));
                  }

                  ShowDiffWith(num) => {
                     try!(show_diff(&conf.original_path, &conf.conflicting_files[num - 1].path));
                  }

                  ShowDiffBetween(num1, num2) => {
                     try!(show_diff(&conf.conflicting_files[num1 - 1].path,
                                    &conf.conflicting_files[num2 - 1].path));
                  }

                  Skip => { break; }
                  Quit => { return Ok(()); }
                  Help => try!(print_runtime_help())
               }
            }

            None => {
               // remove newline at end of line
               line.pop();
               println!("\nInvalid user input: '{}' !\n", line);
            }
         }
      }
   }

   Ok(())
}

/// Calls the diff command specified by the environment variable `CONFSOLVE_DIFF`
/// or - if not defined - `gvimdiff -f` with the files `file1` and `file2`.
fn show_diff(file1: &Path, file2: &Path) -> AppResult<()>
{
   let diff_cmd_and_args = getenv("CONFSOLVE_DIFF").unwrap_or("gvimdiff -f".to_string());
   let diff_cmd_and_args = diff_cmd_and_args.as_slice().split(' ').collect::<Vec<&str>>();

   let diff_cmd = diff_cmd_and_args[0];

//   let mut args = diff_cmd_and_args.iter().collect::<Vec<&str>>();
//   let mut args = diff_cmd_and_args.iter().skip(1).collect::<Vec<&str>>();
//   let mut args = diff_cmd_and_args;
//   args.push(file1.as_str().unwrap());
//   args.push(file2.as_str().unwrap());

//   let mut cmd = Command::new(diff_cmd);
//   for arg in args.iter() {
//      cmd.arg(arg);
//   }
//
//   try!(cmd.output());
   Ok(())
}

/// Moves `file` into the trash directory of confsolve.
fn move_to_trash(file: &Path) -> AppResult<()>
{
   let filename = try!(file.filename_str()
      .ok_or(AppError::from_string(format!("Couldn't get filename from path '{}'!", file.display()))));

   let mut trash_file = try!(trash_dir());
   trash_file.set_filename(filename);

   try!(copy(file, &trash_file));
   try!(unlink(file));

   Ok(())
}

/// Returns the trash directory of confsolve, where all deleted/moved files are put into.
fn trash_dir() -> AppResult<Path>
{
   let mut dir = try!(appdirs::cache("confsolve")
      .ok_or(AppError::from_string(format!("Couldn't get cache directory!"))));

   dir.push("trash");
   if ! dir.is_dir() {
      try!(mkdir_recursive(&dir, io::USER_RWX));
   }

   Ok(dir)
}

fn print_runtime_help() -> AppResult<()>
{
   let trash_dir = try!(trash_dir());
   let dir_str   = trash_dir.display();

   println!("
Runtime Options:
   (T)ake File (NUM) => By pressing 't' and a number (e.g 't1'), the conflicting file with the
                        number NUM is used as the new version. A copy of the
                        current file and the other conflicting files is put
                        into the trash directory '{}'.

   (M)ove to Trash   => By pressing 'm', all conflicting files are
                        moved into the trash directory '{}'.

   Show (D)iff (NUM) => By pressing 'd' and a number (e.g 'd1'), the difference between the
                        current file and the conflicting file NUM is shown.
                        If there's only one conflicting file, then only pressing
                        'd' is sufficient.
                        By pressing 'd' and two numbers (e.g 'd1 2'), the difference between
                        the two conflicting files is shown.
                        The diff tool can be specified by the user by setting the environment
                        variable 'CONFSOLVE_DIFF'. The default diff tool is 'gvimdiff -f'.

   (S)kip            => By pressing 's', the current conflict is skipped
                        and the next one is shown.

   (Q)uit            => By pressing 'q', the application is quit.

   (H)elp            => By pressing 'h', this help is printed.\n", dir_str, dir_str);

   Ok(())
}
<|endoftext|>


<commit_before>// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// Copyright 2014 Mark McDermott.
//
// Licensed under the the MIT license
// <LICENSE or http://opensource.org/licenses/MIT>.
// This file may not be copied, modified, or distributed
// except according to those terms.
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------

extern crate sdl2;
extern crate gl;
extern crate native;

//use sdl2::video::{Window, PosCentered, OPENGL};
//use sdl2::event::{Quit, None, poll_event};

// --------------------------------------------------------------------------
// Native entry point
// --------------------------------------------------------------------------
#[start]
fn start(argc: int, argv: *const *const u8) -> int {
    native::start(argc, argv, main)
}

// --------------------------------------------------------------------------
// The "Real" entry point
// --------------------------------------------------------------------------
fn main() {
    println!("Welcome to the the Cosmolark editor!")

    sdl2::init(sdl2::INIT_EVERYTHING);

    gl::load_with(|s| unsafe {
        std::mem::transmute(sdl2::video::gl_get_proc_address(s))
    });

    // open a window
    let window = match sdl2::video::Window::new("Cosmolark Asset Editor",sdl2::video::PosCentered, sdl2::video::PosCentered, 300,300,sdl2::video::OPENGL) {
        Ok(window) => window,
        Err(err)   => panic!("failed to create window: {}", err)
    };
    window.show();

    // create a rendering context
    let renderer = match sdl2::render::Renderer::from_window(window, sdl2::render::RenderDriverIndex::Auto, sdl2::render::ACCELERATED) {
        Ok(renderer) => renderer,
        Err(err)     => panic!("failed to create renderer: {}", err)
    };

    let _ = renderer.set_draw_color(sdl2::pixels::RGB(101,208,246)); // light blue
    let _ = renderer.clear();

    let _ = renderer.set_draw_color(sdl2::pixels::RGB(0,153,204)); // dark blue
    let border_rect = sdl2::rect::Rect::new(320-64, 240-64, 128, 128);
    let _ = match renderer.draw_rect(&border_rect) {
        Ok(_) => {},
        Err(err) => panic!("failed to draw rect: {}", err)
    };

    // swap buffer
    let _ = renderer.present();

    'event : loop {
        match sdl2::event::poll_event() {
            sdl2::event::Quit(_) => break 'event,
            sdl2::event::None    => continue,
            event                => println!("event: {}", event),
        }
    }

    sdl2::quit();
} // end function main()

<commit_msg>modified how default return values are handled<commit_after>// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
// Copyright 2014 Mark McDermott.
//
// Licensed under the the MIT license
// <LICENSE or http://opensource.org/licenses/MIT>.
// This file may not be copied, modified, or distributed
// except according to those terms.
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------

extern crate sdl2;
extern crate gl;
extern crate native;

//use sdl2::video::{Window, PosCentered, OPENGL};
//use sdl2::event::{Quit, None, poll_event};

// --------------------------------------------------------------------------
// Native entry point
// --------------------------------------------------------------------------
#[start]
fn start(argc: int, argv: *const *const u8) -> int {
    native::start(argc, argv, main)
}

// --------------------------------------------------------------------------
// The "Real" entry point
// --------------------------------------------------------------------------
fn main() {
    println!("Welcome to the the Cosmolark editor!")

    sdl2::init(sdl2::INIT_EVERYTHING);

    gl::load_with(|s| unsafe {
        std::mem::transmute(sdl2::video::gl_get_proc_address(s))
    });

    // open a window
    let window = match sdl2::video::Window::new("Cosmolark Asset Editor",sdl2::video::PosCentered, sdl2::video::PosCentered, 300,300,sdl2::video::OPENGL) {
        Ok(window) => window,
        Err(err)   => panic!("failed to create window: {}", err)
    };
    window.show();

    // create a rendering context
    let renderer = match sdl2::render::Renderer::from_window(window, sdl2::render::RenderDriverIndex::Auto, sdl2::render::ACCELERATED) {
        Ok(renderer) => renderer,
        Err(err)     => panic!("failed to create renderer: {}", err)
    };

    renderer.set_draw_color(sdl2::pixels::RGB(101,208,246)).unwrap(); // light blue
    renderer.clear().unwrap();

    renderer.set_draw_color(sdl2::pixels::RGB(0,153,204)).unwrap(); // dark blue
    let border_rect = sdl2::rect::Rect::new(320-64, 240-64, 128, 128);
    match renderer.draw_rect(&border_rect) {
        Ok(_) => {},
        Err(err) => panic!("failed to draw rect: {}", err)
    };

    // swap buffer
    renderer.present();

    'event : loop {
        match sdl2::event::poll_event() {
            sdl2::event::Quit(_) => break 'event,
            sdl2::event::None    => continue,
            event                => println!("event: {}", event),
        }
    }

    sdl2::quit();
} // end function main()

<|endoftext|>


<commit_before>#[macro_use] extern crate clap;
extern crate migrant_lib;

use std::env;
use std::path::PathBuf;
use clap::{Arg, App, SubCommand};
use migrant_lib::{Error, Config, Direction, Migrator};

fn main() {
    let matches = App::new("Migrant")
        .version(crate_version!())
        .author("James K. <james.kominick@gmail.com>")
        .about("Postgres/SQLite migration manager")
        .subcommand(SubCommand::with_name("init")
            .about("Initialize project config")
            .arg(Arg::with_name("type")
                 .long("type")
                 .short("t")
                 .takes_value(true)
                 .help("Specify the database type (sqlite|postgres)"))
            .arg(Arg::with_name("location")
                 .long("location")
                 .short("l")
                 .takes_value(true)
                 .help("Directory to initialize in"))
            .arg(Arg::with_name("no-confirm")
                 .long("no-confirm")
                 .takes_value(false)
                 .help("Disable interactive prompts")))
        .subcommand(SubCommand::with_name("setup")
            .about("Setup migration table"))
        .subcommand(SubCommand::with_name("connect-string")
            .about("Print out the connection string for postgres, or file-path for sqlite"))
        .subcommand(SubCommand::with_name("list")
            .about("List status of applied and available migrations"))
        .subcommand(SubCommand::with_name("apply")
            .about("Moves up or down (applies up/down.sql) one migration. Default direction is up unless specified with `-d/--down`.")
            .arg(Arg::with_name("down")
                .long("down")
                .short("d")
                .help("Applies `down.sql` migrations"))
            .arg(Arg::with_name("all")
                .long("all")
                .short("a")
                .help("Applies all available migrations"))
            .arg(Arg::with_name("force")
                .long("force")
                .help("Applies the migration and treats it as if it were successful"))
            .arg(Arg::with_name("fake")
                .long("fake")
                .help("Updates the `.migrant.toml` file as if the specified migration was applied")))
        .subcommand(SubCommand::with_name("new")
            .about("Create new migration up/down files")
            .arg(Arg::with_name("TAG")
                 .required(true)
                 .help("tag to use for new migration")))
        .subcommand(SubCommand::with_name("shell")
            .about("Open a repl connection"))
        .subcommand(SubCommand::with_name("which-config")
            .about("Display the path to the configuration file being used"))
        .get_matches();

    let dir = env::current_dir().expect("Unable to retrieve current directory");

    if let Err(ref e) = run(&dir, &matches) {
        match *e {
            Error::MigrationComplete(ref s) => println!("{}", s),
            _ => {
                println!("[ERROR] {}", e);
                ::std::process::exit(1);
            }
        }
    }
}

fn run(dir: &PathBuf, matches: &clap::ArgMatches) -> Result<(), Error> {
    let config_path = migrant_lib::search_for_config(dir);

    if matches.is_present("init") || config_path.is_none() {
        let init_matches = matches.subcommand_matches("init").unwrap();
        let dir = init_matches.value_of("location").map(PathBuf::from).unwrap_or_else(|| dir.to_owned());
        let interactive = !init_matches.is_present("no-confirm");
        Config::init_in(&dir)
            .interactive(interactive)
            .for_database(init_matches.value_of("type"))?;
        return Ok(())
    }

    let config_path = config_path.unwrap();    // absolute path of `.migrant` file
    let config = Config::load_file_only(&config_path)?;

    if matches.is_present("setup") {
        config.setup()?;
        return Ok(())
    }

    let config = config.reload()?;

    match matches.subcommand() {
        ("connect-string", _) => {
            if config.database_type()? == "sqlite" {
                println!("{}", config.database_path()?.to_str().unwrap());
            } else {
                println!("{}", config.connect_string()?);
            }
        }
        ("list", _) => {
            migrant_lib::list(&config)?;
        }
        ("new", Some(matches)) => {
            let tag = matches.value_of("TAG").unwrap();
            migrant_lib::new(&config, tag)?;
            migrant_lib::list(&config)?;
        }
        ("apply", Some(matches)) => {
            let force = matches.is_present("force");
            let fake = matches.is_present("fake");
            let all = matches.is_present("all");
            let direction = if matches.is_present("down") { Direction::Down } else { Direction::Up };

            Migrator::with_config(&config)
                .direction(direction)
                .force(force)
                .fake(fake)
                .all(all)
                .apply()?;

            let config = config.reload()?;
            migrant_lib::list(&config)?;
        }
        ("shell", _) => {
            migrant_lib::shell(&config)?;
        }
        ("which-config", _) => {
            println!("{}", config_path.to_str().unwrap());
        }
        _ => {
            println!("migrant: see `--help`");
        }
    };
    Ok(())
}
<commit_msg>initialization<commit_after>#[macro_use] extern crate clap;
extern crate migrant_lib;

use std::env;
use std::path::PathBuf;
use clap::{Arg, App, SubCommand};
use migrant_lib::{Error, Config, Direction, Migrator};

fn main() {
    let matches = App::new("Migrant")
        .version(crate_version!())
        .author("James K. <james.kominick@gmail.com>")
        .about("Postgres/SQLite migration manager")
        .subcommand(SubCommand::with_name("init")
            .about("Initialize project config")
            .arg(Arg::with_name("type")
                 .long("type")
                 .short("t")
                 .takes_value(true)
                 .help("Specify the database type (sqlite|postgres)"))
            .arg(Arg::with_name("location")
                 .long("location")
                 .short("l")
                 .takes_value(true)
                 .help("Directory to initialize in"))
            .arg(Arg::with_name("no-confirm")
                 .long("no-confirm")
                 .takes_value(false)
                 .help("Disable interactive prompts")))
        .subcommand(SubCommand::with_name("setup")
            .about("Setup migration table"))
        .subcommand(SubCommand::with_name("connect-string")
            .about("Print out the connection string for postgres, or file-path for sqlite"))
        .subcommand(SubCommand::with_name("list")
            .about("List status of applied and available migrations"))
        .subcommand(SubCommand::with_name("apply")
            .about("Moves up or down (applies up/down.sql) one migration. Default direction is up unless specified with `-d/--down`.")
            .arg(Arg::with_name("down")
                .long("down")
                .short("d")
                .help("Applies `down.sql` migrations"))
            .arg(Arg::with_name("all")
                .long("all")
                .short("a")
                .help("Applies all available migrations"))
            .arg(Arg::with_name("force")
                .long("force")
                .help("Applies the migration and treats it as if it were successful"))
            .arg(Arg::with_name("fake")
                .long("fake")
                .help("Updates the `.migrant.toml` file as if the specified migration was applied")))
        .subcommand(SubCommand::with_name("new")
            .about("Create new migration up/down files")
            .arg(Arg::with_name("TAG")
                 .required(true)
                 .help("tag to use for new migration")))
        .subcommand(SubCommand::with_name("shell")
            .about("Open a repl connection"))
        .subcommand(SubCommand::with_name("which-config")
            .about("Display the path to the configuration file being used"))
        .get_matches();

    let dir = env::current_dir().expect("Unable to retrieve current directory");

    if let Err(ref e) = run(&dir, &matches) {
        match *e {
            Error::MigrationComplete(ref s) => println!("{}", s),
            _ => {
                println!("[ERROR] {}", e);
                ::std::process::exit(1);
            }
        }
    }
}

fn run(dir: &PathBuf, matches: &clap::ArgMatches) -> Result<(), Error> {
    let config_path = migrant_lib::search_for_config(dir);

    if matches.is_present("init") || config_path.is_none() {
        let init_matches = matches.subcommand_matches("init").unwrap();
        let dir = init_matches.value_of("location").map(PathBuf::from).unwrap_or_else(|| dir.to_owned());
        let interactive = !init_matches.is_present("no-confirm");
        Config::init_in(&dir)
            .interactive(interactive)
            .for_database(init_matches.value_of("type"))?
            .initialize()?;
        return Ok(())
    }

    let config_path = config_path.unwrap();    // absolute path of `.migrant` file
    let config = Config::load_file_only(&config_path)?;

    if matches.is_present("setup") {
        config.setup()?;
        return Ok(())
    }

    if matches.is_present("connect-string") {
        if config.database_type()? == "sqlite" {
            println!("{}", config.database_path()?.to_str().unwrap());
        } else {
            println!("{}", config.connect_string()?);
        }
        return Ok(())
    }

    // load applied migrations from the database
    let config = config.reload()?;

    match matches.subcommand() {
        ("list", _) => {
            migrant_lib::list(&config)?;
        }
        ("new", Some(matches)) => {
            let tag = matches.value_of("TAG").unwrap();
            migrant_lib::new(&config, tag)?;
            migrant_lib::list(&config)?;
        }
        ("apply", Some(matches)) => {
            let force = matches.is_present("force");
            let fake = matches.is_present("fake");
            let all = matches.is_present("all");
            let direction = if matches.is_present("down") { Direction::Down } else { Direction::Up };

            Migrator::with_config(&config)
                .direction(direction)
                .force(force)
                .fake(fake)
                .all(all)
                .apply()?;

            let config = config.reload()?;
            migrant_lib::list(&config)?;
        }
        ("shell", _) => {
            migrant_lib::shell(&config)?;
        }
        ("which-config", _) => {
            println!("{}", config_path.to_str().unwrap());
        }
        _ => {
            println!("migrant: see `--help`");
        }
    };
    Ok(())
}
<|endoftext|>


<commit_before>extern crate libc;
extern crate neovim;
extern crate rgtk;
extern crate "rustc-serialize" as rustc_serialize;

use rgtk::*;
use std::collections::HashSet;
use std::old_io::fs;
use std::old_io::fs::PathExtensions;

mod projects;
mod ui;
mod utils;

mod ffi {
    pub use libc::{c_int, c_uchar, c_void};
    pub use libc::funcs::posix88::unistd::{close, pipe, read, write};
    pub use libc::types::os::arch::c95::size_t;

    extern "C" {
        pub fn fork () -> c_int;
        pub fn kill (pid: c_int, sig: c_int);
    }
}

fn gui_main(
    pty: &mut gtk::VtePty,
    read_fd: ffi::c_int,
    write_fd: ffi::c_int,
    pid: ffi::c_int)
{
    gtk::init();

    // constants

    let width = 1242;
    let height = 768;
    let editor_height = ((height as f32) * 0.75) as i32;

    // create the window

    let title = format!("SolidOak {}.{}.{}",
                        option_env!("CARGO_PKG_VERSION_MAJOR").unwrap(),
                        option_env!("CARGO_PKG_VERSION_MINOR").unwrap(),
                        option_env!("CARGO_PKG_VERSION_PATCH").unwrap());
    let mut window = gtk::Window::new(gtk::WindowType::TopLevel).unwrap();
    window.set_title(title.as_slice());
    window.set_window_position(gtk::WindowPosition::Center);
    window.set_default_size(width, height);

    window.connect(gtk::signals::DeleteEvent::new(&mut |&: _| {
        unsafe {
            ffi::close(read_fd);
            ffi::close(write_fd);
            ffi::kill(pid, 15);
        }
        gtk::main_quit();
        true
    }));

    // create the panes

    let new_button = gtk::Button::new_with_label("New Project").unwrap();
    let import_button = gtk::Button::new_with_label("Import").unwrap();
    let rename_button = gtk::Button::new_with_label("Rename").unwrap();
    let remove_button = gtk::Button::new_with_label("Remove").unwrap();

    let mut project_buttons = gtk::Box::new(gtk::Orientation::Horizontal, 0).unwrap();
    project_buttons.set_size_request(-1, -1);
    project_buttons.add(&new_button);
    project_buttons.add(&import_button);
    project_buttons.add(&rename_button);
    project_buttons.add(&remove_button);

    let mut project_tree = gtk::TreeView::new().unwrap();
    let selection = project_tree.get_selection().unwrap();
    let column_types = [glib::ffi::g_type_string, glib::ffi::g_type_string];
    let store = gtk::TreeStore::new(&column_types).unwrap();
    let model = store.get_model().unwrap();
    project_tree.set_model(&model);
    project_tree.set_headers_visible(false);

    let mut scroll_pane = gtk::ScrolledWindow::new(None, None).unwrap();
    scroll_pane.add(&project_tree);

    let column = gtk::TreeViewColumn::new().unwrap();
    let cell = gtk::CellRendererText::new().unwrap();
    column.pack_start(&cell, true);
    column.add_attribute(&cell, "text", 0);
    project_tree.append_column(&column);

    let mut project_pane = gtk::Box::new(gtk::Orientation::Vertical, 0).unwrap();
    project_pane.set_size_request(-1, -1);
    project_pane.pack_start(&project_buttons, false, true, 0);
    project_pane.pack_start(&scroll_pane, true, true, 0);

    let mut editor_pane = gtk::VteTerminal::new().unwrap();
    editor_pane.set_size_request(-1, editor_height);
    editor_pane.set_pty(pty);
    editor_pane.watch_child(pid);

    let run_button = gtk::Button::new_with_label("Run").unwrap();
    let build_button = gtk::Button::new_with_label("Build").unwrap();
    let test_button = gtk::Button::new_with_label("Test").unwrap();
    let clean_button = gtk::Button::new_with_label("Clean").unwrap();
    let stop_button = gtk::Button::new_with_label("Stop").unwrap();

    let mut build_buttons = gtk::Box::new(gtk::Orientation::Horizontal, 0).unwrap();
    build_buttons.set_size_request(-1, -1);
    build_buttons.add(&run_button);
    build_buttons.add(&build_button);
    build_buttons.add(&test_button);
    build_buttons.add(&clean_button);
    build_buttons.add(&stop_button);

    let build_term = gtk::VteTerminal::new().unwrap();

    let mut build_pane = gtk::Box::new(gtk::Orientation::Vertical, 0).unwrap();
    build_pane.pack_start(&build_buttons, false, true, 0);
    build_pane.pack_start(&build_term, true, true, 0);

    let mut content = gtk::Box::new(gtk::Orientation::Vertical, 0).unwrap();
    content.pack_start(&editor_pane, false, true, 0);
    content.pack_start(&build_pane, true, true, 0);

    let mut hbox = gtk::Box::new(gtk::Orientation::Horizontal, 0).unwrap();
    hbox.pack_start(&project_pane, false, true, 0);
    hbox.pack_start(&content, true, true, 0);
    window.add(&hbox);

    // populate the project tree

    let mut state = ::utils::State{
        projects: HashSet::new(),
        expansions: HashSet::new(),
        selection: None,
        tree_model: &model,
        tree_store: &store,
        tree_selection: &selection,
        rename_button: &rename_button,
        remove_button: &remove_button,
    };

    ::utils::read_prefs(&mut state);
    ::ui::update_project_tree(&mut state, &mut project_tree);

    // connect to the signals

    new_button.connect(gtk::signals::Clicked::new(&mut || {
        ::projects::new_project(&mut state, &mut project_tree);
    }));
    import_button.connect(gtk::signals::Clicked::new(&mut || {
        ::projects::import_project(&mut state, &mut project_tree);
    }));
    rename_button.connect(gtk::signals::Clicked::new(&mut || {
        ::projects::rename_file(&mut state);
    }));
    remove_button.connect(gtk::signals::Clicked::new(&mut || {
        ::projects::remove_item(&mut state);
    }));
    selection.connect(gtk::signals::Changed::new(&mut || {
        ::projects::save_selection(&mut state);
    }));
    project_tree.connect(gtk::signals::RowCollapsed::new(&mut |iter_raw, _| {
        let iter = gtk::TreeIter::wrap_pointer(iter_raw);
        ::projects::remove_expansion(&mut state, &iter);
    }));
    project_tree.connect(gtk::signals::RowExpanded::new(&mut |iter_raw, _| {
        let iter = gtk::TreeIter::wrap_pointer(iter_raw);
        ::projects::add_expansion(&mut state, &iter);
    }));

    // show the window

    window.show_all();
    gtk::main();
}

fn nvim_attach(fd: ffi::c_int) {
    let mut arr = neovim::Array::new();
    arr.add_integer(80);
    arr.add_integer(24);
    arr.add_boolean(true);
    let msg = neovim::serialize_message(1, "ui_attach", &arr);
    let msg_ptr = msg.as_slice().as_ptr() as *const ffi::c_void;
    unsafe { ffi::write(fd, msg_ptr, msg.len() as ffi::size_t) };
}

fn nvim_execute(fd: ffi::c_int, command: &str) {
    let mut arr = neovim::Array::new();
    arr.add_string(command);
    let msg = neovim::serialize_message(1, "vim_command", &arr);
    let msg_ptr = msg.as_slice().as_ptr() as *const ffi::c_void;
    unsafe { ffi::write(fd, msg_ptr, msg.len() as ffi::size_t) };
}

fn receive_message(fd: ffi::c_int) -> Option<neovim::Array> {
    let mut buf : [ffi::c_uchar; 1024] = [0; 1024];
    let n = unsafe { ffi::read(fd, buf.as_mut_ptr() as *mut ffi::c_void, 1024) };
    if n < 0 {
        return None;
    }
    unsafe {
        let v = Vec::from_raw_buf(buf.as_ptr(), n as usize);
        let s = String::from_utf8_unchecked(v);
        Some(neovim::deserialize_message(&s))
    }
}

fn main() {
    // create data dir
    let home_dir = ::utils::get_home_dir();
    let data_dir = home_dir.join(::utils::DATA_DIR);
    if !data_dir.exists() {
        match fs::mkdir(&data_dir, ::std::old_io::USER_DIR) {
            Ok(_) => {
                for res in ::utils::DATA_CONTENT.iter() {
                    let res_path = data_dir.join_many(res.path);
                    ::std::old_io::fs::mkdir_recursive(&res_path.dir_path(), ::std::old_io::USER_DIR).ok();
                    ::std::old_io::File::create(&res_path).write_all(res.data.as_bytes()).ok();
                }
                println!("Created data dir at {}", data_dir.as_str().unwrap());
            },
            Err(e) => { println!("Error creating data dir: {}", e) }
        }
    }

    // set $VIM to the data dir if it isn't already set
    if ::std::env::var("VIM").is_none() {
        ::std::env::set_var("VIM", data_dir.as_str().unwrap());
    }

    // create config file
    let config_file = home_dir.join(::utils::CONFIG_FILE);
    if !config_file.exists() {
        match ::std::old_io::File::create(&config_file).write_all(::utils::CONFIG_CONTENT.as_bytes()) {
            Ok(_) => { println!("Created config file at {}", config_file.as_str().unwrap()) },
            Err(e) => { println!("Error creating config file: {}", e) }
        }
    }

    // takes care of piping stdin/stdout between the gui and nvim
    let mut pty = gtk::VtePty::new().unwrap();

    // two pairs of anonymous pipes for msgpack-rpc between the gui and nvim
    let mut nvim_gui : [ffi::c_int; 2] = [0; 2]; // to nvim from gui
    let mut gui_nvim : [ffi::c_int; 2] = [0; 2]; // to gui from nvim
    unsafe {
        ffi::pipe(nvim_gui.as_mut_ptr());
        ffi::pipe(gui_nvim.as_mut_ptr());
    };

    // split into two processes
    let pid = unsafe { ffi::fork() };

    if pid > 0 { // the gui process
        ::std::thread::Thread::spawn(move || {
            // start communicating with nvim
            nvim_attach(nvim_gui[1]);

            // listen for bufread events
            nvim_execute(nvim_gui[1], "au BufRead * call rpcnotify(1, \"bufread\", bufname(\"\"))");

            // receive messages
            while let Some(recv_arr) = receive_message(gui_nvim[0]) {
                if recv_arr.len() > 0 {
                    println!("Received: {:?}", recv_arr);
                }
            }
        });

        // start the gui
        gui_main(&mut pty, gui_nvim[0], gui_nvim[1], pid);
    } else { // the nvim process
        // prepare this process to be piped into the gui
        pty.child_setup();

        // start nvim
        let mut args = Vec::new();
        for arg in ::std::env::args() {
            args.push(arg.into_string().unwrap());
        }
        args.push_all(&["-u".to_string(), config_file.as_str().unwrap().to_string()]);
        neovim::main_setup(&args);
        neovim::channel_from_fds(nvim_gui[0], gui_nvim[1]);
        neovim::main_loop();
    }
}
<commit_msg>Attach to nvim before starting thread<commit_after>extern crate libc;
extern crate neovim;
extern crate rgtk;
extern crate "rustc-serialize" as rustc_serialize;

use rgtk::*;
use std::collections::HashSet;
use std::old_io::fs;
use std::old_io::fs::PathExtensions;

mod projects;
mod ui;
mod utils;

mod ffi {
    pub use libc::{c_int, c_uchar, c_void};
    pub use libc::funcs::posix88::unistd::{close, pipe, read, write};
    pub use libc::types::os::arch::c95::size_t;

    extern "C" {
        pub fn fork () -> c_int;
        pub fn kill (pid: c_int, sig: c_int);
    }
}

fn gui_main(
    pty: &mut gtk::VtePty,
    read_fd: ffi::c_int,
    write_fd: ffi::c_int,
    pid: ffi::c_int)
{
    gtk::init();

    // constants

    let width = 1242;
    let height = 768;
    let editor_height = ((height as f32) * 0.75) as i32;

    // create the window

    let title = format!("SolidOak {}.{}.{}",
                        option_env!("CARGO_PKG_VERSION_MAJOR").unwrap(),
                        option_env!("CARGO_PKG_VERSION_MINOR").unwrap(),
                        option_env!("CARGO_PKG_VERSION_PATCH").unwrap());
    let mut window = gtk::Window::new(gtk::WindowType::TopLevel).unwrap();
    window.set_title(title.as_slice());
    window.set_window_position(gtk::WindowPosition::Center);
    window.set_default_size(width, height);

    window.connect(gtk::signals::DeleteEvent::new(&mut |&: _| {
        unsafe {
            ffi::close(read_fd);
            ffi::close(write_fd);
            ffi::kill(pid, 15);
        }
        gtk::main_quit();
        true
    }));

    // create the panes

    let new_button = gtk::Button::new_with_label("New Project").unwrap();
    let import_button = gtk::Button::new_with_label("Import").unwrap();
    let rename_button = gtk::Button::new_with_label("Rename").unwrap();
    let remove_button = gtk::Button::new_with_label("Remove").unwrap();

    let mut project_buttons = gtk::Box::new(gtk::Orientation::Horizontal, 0).unwrap();
    project_buttons.set_size_request(-1, -1);
    project_buttons.add(&new_button);
    project_buttons.add(&import_button);
    project_buttons.add(&rename_button);
    project_buttons.add(&remove_button);

    let mut project_tree = gtk::TreeView::new().unwrap();
    let selection = project_tree.get_selection().unwrap();
    let column_types = [glib::ffi::g_type_string, glib::ffi::g_type_string];
    let store = gtk::TreeStore::new(&column_types).unwrap();
    let model = store.get_model().unwrap();
    project_tree.set_model(&model);
    project_tree.set_headers_visible(false);

    let mut scroll_pane = gtk::ScrolledWindow::new(None, None).unwrap();
    scroll_pane.add(&project_tree);

    let column = gtk::TreeViewColumn::new().unwrap();
    let cell = gtk::CellRendererText::new().unwrap();
    column.pack_start(&cell, true);
    column.add_attribute(&cell, "text", 0);
    project_tree.append_column(&column);

    let mut project_pane = gtk::Box::new(gtk::Orientation::Vertical, 0).unwrap();
    project_pane.set_size_request(-1, -1);
    project_pane.pack_start(&project_buttons, false, true, 0);
    project_pane.pack_start(&scroll_pane, true, true, 0);

    let mut editor_pane = gtk::VteTerminal::new().unwrap();
    editor_pane.set_size_request(-1, editor_height);
    editor_pane.set_pty(pty);
    editor_pane.watch_child(pid);

    let run_button = gtk::Button::new_with_label("Run").unwrap();
    let build_button = gtk::Button::new_with_label("Build").unwrap();
    let test_button = gtk::Button::new_with_label("Test").unwrap();
    let clean_button = gtk::Button::new_with_label("Clean").unwrap();
    let stop_button = gtk::Button::new_with_label("Stop").unwrap();

    let mut build_buttons = gtk::Box::new(gtk::Orientation::Horizontal, 0).unwrap();
    build_buttons.set_size_request(-1, -1);
    build_buttons.add(&run_button);
    build_buttons.add(&build_button);
    build_buttons.add(&test_button);
    build_buttons.add(&clean_button);
    build_buttons.add(&stop_button);

    let build_term = gtk::VteTerminal::new().unwrap();

    let mut build_pane = gtk::Box::new(gtk::Orientation::Vertical, 0).unwrap();
    build_pane.pack_start(&build_buttons, false, true, 0);
    build_pane.pack_start(&build_term, true, true, 0);

    let mut content = gtk::Box::new(gtk::Orientation::Vertical, 0).unwrap();
    content.pack_start(&editor_pane, false, true, 0);
    content.pack_start(&build_pane, true, true, 0);

    let mut hbox = gtk::Box::new(gtk::Orientation::Horizontal, 0).unwrap();
    hbox.pack_start(&project_pane, false, true, 0);
    hbox.pack_start(&content, true, true, 0);
    window.add(&hbox);

    // populate the project tree

    let mut state = ::utils::State{
        projects: HashSet::new(),
        expansions: HashSet::new(),
        selection: None,
        tree_model: &model,
        tree_store: &store,
        tree_selection: &selection,
        rename_button: &rename_button,
        remove_button: &remove_button,
    };

    ::utils::read_prefs(&mut state);
    ::ui::update_project_tree(&mut state, &mut project_tree);

    // connect to the signals

    new_button.connect(gtk::signals::Clicked::new(&mut || {
        ::projects::new_project(&mut state, &mut project_tree);
    }));
    import_button.connect(gtk::signals::Clicked::new(&mut || {
        ::projects::import_project(&mut state, &mut project_tree);
    }));
    rename_button.connect(gtk::signals::Clicked::new(&mut || {
        ::projects::rename_file(&mut state);
    }));
    remove_button.connect(gtk::signals::Clicked::new(&mut || {
        ::projects::remove_item(&mut state);
    }));
    selection.connect(gtk::signals::Changed::new(&mut || {
        ::projects::save_selection(&mut state);
    }));
    project_tree.connect(gtk::signals::RowCollapsed::new(&mut |iter_raw, _| {
        let iter = gtk::TreeIter::wrap_pointer(iter_raw);
        ::projects::remove_expansion(&mut state, &iter);
    }));
    project_tree.connect(gtk::signals::RowExpanded::new(&mut |iter_raw, _| {
        let iter = gtk::TreeIter::wrap_pointer(iter_raw);
        ::projects::add_expansion(&mut state, &iter);
    }));

    // show the window

    window.show_all();
    gtk::main();
}

fn nvim_attach(fd: ffi::c_int) {
    let mut arr = neovim::Array::new();
    arr.add_integer(80);
    arr.add_integer(24);
    arr.add_boolean(true);
    let msg = neovim::serialize_message(1, "ui_attach", &arr);
    let msg_ptr = msg.as_slice().as_ptr() as *const ffi::c_void;
    unsafe { ffi::write(fd, msg_ptr, msg.len() as ffi::size_t) };
}

fn nvim_execute(fd: ffi::c_int, command: &str) {
    let mut arr = neovim::Array::new();
    arr.add_string(command);
    let msg = neovim::serialize_message(1, "vim_command", &arr);
    let msg_ptr = msg.as_slice().as_ptr() as *const ffi::c_void;
    unsafe { ffi::write(fd, msg_ptr, msg.len() as ffi::size_t) };
}

fn receive_message(fd: ffi::c_int) -> Option<neovim::Array> {
    let mut buf : [ffi::c_uchar; 1024] = [0; 1024];
    let n = unsafe { ffi::read(fd, buf.as_mut_ptr() as *mut ffi::c_void, 1024) };
    if n < 0 {
        return None;
    }
    unsafe {
        let v = Vec::from_raw_buf(buf.as_ptr(), n as usize);
        let s = String::from_utf8_unchecked(v);
        Some(neovim::deserialize_message(&s))
    }
}

fn main() {
    // create data dir
    let home_dir = ::utils::get_home_dir();
    let data_dir = home_dir.join(::utils::DATA_DIR);
    if !data_dir.exists() {
        match fs::mkdir(&data_dir, ::std::old_io::USER_DIR) {
            Ok(_) => {
                for res in ::utils::DATA_CONTENT.iter() {
                    let res_path = data_dir.join_many(res.path);
                    ::std::old_io::fs::mkdir_recursive(&res_path.dir_path(), ::std::old_io::USER_DIR).ok();
                    ::std::old_io::File::create(&res_path).write_all(res.data.as_bytes()).ok();
                }
                println!("Created data dir at {}", data_dir.as_str().unwrap());
            },
            Err(e) => { println!("Error creating data dir: {}", e) }
        }
    }

    // set $VIM to the data dir if it isn't already set
    if ::std::env::var("VIM").is_none() {
        ::std::env::set_var("VIM", data_dir.as_str().unwrap());
    }

    // create config file
    let config_file = home_dir.join(::utils::CONFIG_FILE);
    if !config_file.exists() {
        match ::std::old_io::File::create(&config_file).write_all(::utils::CONFIG_CONTENT.as_bytes()) {
            Ok(_) => { println!("Created config file at {}", config_file.as_str().unwrap()) },
            Err(e) => { println!("Error creating config file: {}", e) }
        }
    }

    // takes care of piping stdin/stdout between the gui and nvim
    let mut pty = gtk::VtePty::new().unwrap();

    // two pairs of anonymous pipes for msgpack-rpc between the gui and nvim
    let mut nvim_gui : [ffi::c_int; 2] = [0; 2]; // to nvim from gui
    let mut gui_nvim : [ffi::c_int; 2] = [0; 2]; // to gui from nvim
    unsafe {
        ffi::pipe(nvim_gui.as_mut_ptr());
        ffi::pipe(gui_nvim.as_mut_ptr());
    };

    // split into two processes
    let pid = unsafe { ffi::fork() };

    if pid > 0 { // the gui process
        // start communicating with nvim
        nvim_attach(nvim_gui[1]);

        ::std::thread::Thread::spawn(move || {
            // listen for bufread events
            nvim_execute(nvim_gui[1], "au BufRead * call rpcnotify(1, \"bufread\", bufname(\"\"))");

            // receive messages
            while let Some(recv_arr) = receive_message(gui_nvim[0]) {
                if recv_arr.len() > 0 {
                    println!("Received: {:?}", recv_arr);
                }
            }
        });

        // start the gui
        gui_main(&mut pty, gui_nvim[0], gui_nvim[1], pid);
    } else { // the nvim process
        // prepare this process to be piped into the gui
        pty.child_setup();

        // start nvim
        let mut args = Vec::new();
        for arg in ::std::env::args() {
            args.push(arg.into_string().unwrap());
        }
        args.push_all(&["-u".to_string(), config_file.as_str().unwrap().to_string()]);
        neovim::main_setup(&args);
        neovim::channel_from_fds(nvim_gui[0], gui_nvim[1]);
        neovim::main_loop();
    }
}
<|endoftext|>