<commit_before>//#![allow(unused)]

use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::{Condvar, Mutex, Arc};
use std::vec::Vec;
use std::collections::HashMap;
use std::cell::UnsafeCell;
use std::hash::{Hash, Hasher};
use std::mem::{align_of, size_of_val, transmute};
use std::slice::bytes::copy_memory;

use ::Result;

pub struct GlobalState { version : AtomicUsize }

/// Baseline implementation of GlobalState, currently do not hqve the aid of the compiler, so this
/// may have major issues.
/// TODO: make a proper implementation of the GlobalState trait
impl GlobalState {
    fn newLocal<'l, 'g>(&'g self) -> LocalState<'l, 'g> {
        LocalState {
            reads: Vec::new(),
            writes: HashMap::new(),
            global: self,
            snapshot: self.version.load(Ordering::SeqCst),
        }
    }

    pub fn retry<A, F>(&self, l: &mut LocalState, mut func: F) -> A
    where F: FnMut(&mut LocalState) -> Result<A> {
        let bx = Arc::new((Mutex::new(false), Condvar::new()));
        for &(addr, size) in &l.reads {
            let mut waiters = addr.addr.waiters.lock().unwrap();
            // this is actually unsafe...
            // maybe protect waiters with a lock
            waiters.push(bx.clone());
            //addr.waiters().push((&mtx, &cv));
        }
        let (ref mtx, ref cv) = *bx;
        let mut done = mtx.lock().unwrap();
        while !*done {
            done = cv.wait(done).unwrap();
        }
        self.run(func)
    }

    pub fn run<A, F>(&self, mut func : F) -> A
        where F: FnMut(&mut LocalState) -> Result<A> {
            let mut local = self.newLocal();
            match func(&mut local).spec_move() {
                Ok(r) => r,
                _ => self.run(func)
            }
    }

    pub fn or_else<A, F, G>(&self, mut func1 : F, func2 : G) -> A
        where F: FnMut(&mut LocalState) -> Result<A>,
              G: FnMut(&mut LocalState) -> Result<A> {
            let mut local1 = self.newLocal();
            match func1(&mut local1).spec_move() {
                Ok(r) => r,
                //TODO: this may not work, compiler may not know that
                //local1, which borrows self, has relinquised ownership
                _ => self.run(func2)
            }
    }
}

pub struct LocalState<'v, 'g : 'v> {
    /// the norec paper has the reads stored as a list of <address, value> pairs. For us it
    /// would probably be better to have versioned TVars. That way we always have a fast
    /// comparison operation and avoid doing extra dynamic dispatch when validating reads.
    reads : Vec<(TVarAddr<'v, 'g>, usize)>,

    /// for writes we will probably have to cast unsafely to get this to work. TVars just hold
    /// arbitrary types and we only have one table.  XXX: gross
    writes : HashMap<TVarAddr<'v, 'g>, Box<[u8]>>,

    /// STM environment we are using
    /// TODO: how are we getting away with this not being a mutable reference?
    global : &'g GlobalState,

    /// last version at which our view of the global state was valid
    snapshot : usize
}

impl<'v, 'g : 'v> LocalState<'v, 'g> {

    /// Validate the current local state against the state of the world,
    /// Aborts if a TVar in our readset has been written, otherwise
    /// returns the version number to with we are consistent
    pub fn validate(&self) -> Result<usize> {
        loop {
            let time = self.global.version.load(Ordering::SeqCst);
            if (time & 1) == 0 {
                continue;
            }
            for &(addr, version) in &self.reads {
                if addr.version().load(Ordering::SeqCst) != version {
                    return Result::abort();
                }
            }
            return Result::ret(time);
        }
    }

    /// Acquire reference to value in TVar within the transaction
    pub fn read<'l, A>(&'l mut self, tvar : &'v TVar<'g, A>) -> Result<&'l A>
        where 'v : 'l
    {
        let addr: TVarAddr<'v, 'g> = unsafe { tvar.to_addr() } ;

        // Should be &&'l ?
        if let Some(v) = self.writes.get(&addr) {
            let r : && [u8] = &&**v;
            assert_eq!(r.len(), ::std::mem::size_of::<A>());
            let r2 : && A = unsafe { transmute(r) };
            return Result::ret(r2)
        }

        if self.snapshot == self.global.version.load(Ordering::SeqCst) {
            self.reads.push((addr, tvar.version.load(Ordering::SeqCst)));
            Result::ret(unsafe {tvar.get_val()})
        } else {
            self.validate().bind( | v | {
                assert!(self.snapshot <= v);
                self.snapshot = v;
                self.reads.push((addr, tvar.version.load(Ordering::SeqCst)));
                Result::ret(unsafe {tvar.get_val()})
            })
        }
    }

    /// Add the TVar and value to our writeset, these are thread-local until the
    /// transaction commits
    pub fn write<A : Copy>(&mut self, tvar : &'v mut TVar<'g, A>, val : A) -> Result<()> {
        let b : Box<[A]> = Box::new([val]);
        unsafe {
            self.writes.insert(tvar.to_addr(), transmute(b));
        }
        Result::ret(())
    }

    /// if (read−only transaction)
    ///     return
    /// while (!CAS(&global lock, snapshot, snapshot + 1))
    ///     snapshot = Validate()
    /// for each (addr, val) in writes
    ///     ∗addr = val
    /// global lock = snapshot + 2 // one more than CAS above
    pub fn commit(&mut self) -> Result<()> {
        fn commit_loop(l : &mut LocalState) -> Result<()> {
            if l.global
                .version
                .compare_and_swap(l.snapshot, l.snapshot + 1, Ordering::SeqCst) == l.snapshot {
                Result::ret(())
            } else {
                l.validate().bind(|v| {
                    l.snapshot = v;
                    commit_loop(l)
                })
            }
        }
        if self.writes.is_empty() {
            Result::ret(())
        } else {
            commit_loop(self).bind(| _ | {
                for (mut addr, value) in &self.writes {
                    let dst = unsafe { addr.aligned_mem() };
                    debug_assert_eq!(value.len(), dst.len());
                    copy_memory(value, dst);
                    addr.addr.version.fetch_add(1, Ordering::SeqCst);
                    //TODO: DCLP if this is too slow in the common case that waiters is empty
                    let mut waiters = addr.addr.waiters.lock().unwrap();
                    for bx in &*waiters {
                        let (ref mtx, ref cv) = **bx;
                        let mut done = mtx.lock().unwrap();
                        *done = true;
                        cv.notify_all();
                    }
                    waiters.clear();
                }
                self.global
                    .version
                    .compare_and_swap(self.snapshot + 1, self.snapshot + 2, Ordering::SeqCst);
                Result::ret(())
            })
        }
    }
}

/// Haskell-style container of mutable state within a transaction
/// TODO: impl TVar trait
pub struct TVar<'a, A: ?Sized> {
    //TODO add in mutex/cv pair for waiting for explicit aborts
    //This includes an explicit abort() command (as opposed to failing validation,
    //which should cause an immediate retry).
    version : AtomicUsize,
    //TODO this can be of lifetime 'b : 'a
    waiters : Mutex<Vec<Arc<(Mutex<bool>, Condvar)>>>,
    global : &'a GlobalState,
    val : UnsafeCell<A>,
}

impl<'a, A: ?Sized> TVar<'a, A> {
    unsafe fn get_val(&self) -> &A {
        &*self.val.get()
    }

    unsafe fn get_val_mut(&self) -> &mut A {
        &mut *self.val.get()
    }
}

impl<'a, A> TVar<'a, A> {
    unsafe fn to_addr<'b>(&'b self) -> TVarAddr<'b, 'a> {
        use std::raw::Slice;
        let fat_ptr = Slice::<u8> {
            data: transmute(self),
            len: size_of_val(self),
        };
        //let ptr : &*mut TVar<'a, [u8]> = transmute(self));
        TVarAddr {
            align: align_of::<A>(),
            addr: transmute(fat_ptr),
        }
    }

    ///TODO: change trait API to use this signature
    fn read<'l, 'b> (&'b mut self, l: &'l mut LocalState<'b, 'a>) -> Result<&'l A> {
        l.read(self)
    }

}
impl<'a, A : Copy> TVar<'a, A> {
    ///TODO: change trait API to use this signature
    fn write<'l, 'b>(&'b mut self, l: &'l mut LocalState<'b, 'a>, arg : A) -> Result<()> {
        l.write(self, arg)
    }
}

#[derive(Copy, Clone)]
/// Lies... Lies
struct TVarAddr<'a, 'b : 'a> {
    align: usize,
    addr: &'a TVar<'b, [u8]>, //*mut
    //_phan: PhantomData<&'a TVar<'b, ()>>,
}

impl<'a, 'b : 'a> Hash for TVarAddr<'a, 'b> {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.raw_addr().hash(state);
    }
}

impl<'a, 'b : 'a> Eq for TVarAddr<'a, 'b> {}

impl<'a, 'b : 'a> PartialEq for TVarAddr<'a, 'b> {
    fn eq(&self, other: &Self) -> bool {
        let c = self.raw_addr() == other.raw_addr();
        if c {
            debug_assert_eq!(self.raw_len(), other.raw_len());
            debug_assert_eq!(self.align, other.align);
        }
        c
    }
}

impl<'a, 'b : 'a> TVarAddr<'a, 'b> {
    fn raw_addr(self) -> usize {
        self.addr as *const _ as *const () as usize
    }

    fn raw_len(self) -> usize {
        let y = self.addr.val.get();
        unsafe { transmute::<_,&[u8]>(y).len() }
    }

    unsafe fn aligned_mem(self) -> &'a mut [u8] {
        let buf = self.addr.val.get();
        let p = buf as *mut () as usize;
        // Assumes alignment is power of 2
        let o = ((p + (self.align - 1)) & self.align) - p;
        &mut (*buf)[o..]
    }

    fn version(self) -> &'a AtomicUsize {
        &(*self.addr).version
    }

    fn waiters(self) -> &'a mut Vec<(&'b Mutex<bool>, &'b Condvar)> {
        panic!("nyi")
    }
}

pub fn align_down_mut<T>(sp: *mut T, n: usize) -> *mut T {
  let sp = (sp as usize) & !(n - 1);
  sp as *mut T
}
<commit_msg>removed irrelevant comment<commit_after>//#![allow(unused)]

use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::{Condvar, Mutex, Arc};
use std::vec::Vec;
use std::collections::HashMap;
use std::cell::UnsafeCell;
use std::hash::{Hash, Hasher};
use std::mem::{align_of, size_of_val, transmute};
use std::slice::bytes::copy_memory;

use ::Result;

pub struct GlobalState { version : AtomicUsize }

/// Baseline implementation of GlobalState, currently do not hqve the aid of the compiler, so this
/// may have major issues.
/// TODO: make a proper implementation of the GlobalState trait
impl GlobalState {
    fn newLocal<'l, 'g>(&'g self) -> LocalState<'l, 'g> {
        LocalState {
            reads: Vec::new(),
            writes: HashMap::new(),
            global: self,
            snapshot: self.version.load(Ordering::SeqCst),
        }
    }

    pub fn retry<A, F>(&self, l: &mut LocalState, mut func: F) -> A
    where F: FnMut(&mut LocalState) -> Result<A> {
        let bx = Arc::new((Mutex::new(false), Condvar::new()));
        for &(addr, size) in &l.reads {
            let mut waiters = addr.addr.waiters.lock().unwrap();
            // this is actually unsafe...
            // maybe protect waiters with a lock
            waiters.push(bx.clone());
            //addr.waiters().push((&mtx, &cv));
        }
        let (ref mtx, ref cv) = *bx;
        let mut done = mtx.lock().unwrap();
        while !*done {
            done = cv.wait(done).unwrap();
        }
        self.run(func)
    }

    pub fn run<A, F>(&self, mut func : F) -> A
        where F: FnMut(&mut LocalState) -> Result<A> {
            let mut local = self.newLocal();
            match func(&mut local).spec_move() {
                Ok(r) => r,
                _ => self.run(func)
            }
    }

    pub fn or_else<A, F, G>(&self, mut func1 : F, func2 : G) -> A
        where F: FnMut(&mut LocalState) -> Result<A>,
              G: FnMut(&mut LocalState) -> Result<A> {
            let mut local1 = self.newLocal();
            match func1(&mut local1).spec_move() {
                Ok(r) => r,
                //TODO: this may not work, compiler may not know that
                //local1, which borrows self, has relinquised ownership
                _ => self.run(func2)
            }
    }
}

pub struct LocalState<'v, 'g : 'v> {
    /// the norec paper has the reads stored as a list of <address, value> pairs. For us it
    /// would probably be better to have versioned TVars. That way we always have a fast
    /// comparison operation and avoid doing extra dynamic dispatch when validating reads.
    reads : Vec<(TVarAddr<'v, 'g>, usize)>,

    /// for writes we will probably have to cast unsafely to get this to work. TVars just hold
    /// arbitrary types and we only have one table.  XXX: gross
    writes : HashMap<TVarAddr<'v, 'g>, Box<[u8]>>,

    /// STM environment we are using
    /// TODO: how are we getting away with this not being a mutable reference?
    global : &'g GlobalState,

    /// last version at which our view of the global state was valid
    snapshot : usize
}

impl<'v, 'g : 'v> LocalState<'v, 'g> {

    /// Validate the current local state against the state of the world,
    /// Aborts if a TVar in our readset has been written, otherwise
    /// returns the version number to with we are consistent
    pub fn validate(&self) -> Result<usize> {
        loop {
            let time = self.global.version.load(Ordering::SeqCst);
            if (time & 1) == 0 {
                continue;
            }
            for &(addr, version) in &self.reads {
                if addr.version().load(Ordering::SeqCst) != version {
                    return Result::abort();
                }
            }
            return Result::ret(time);
        }
    }

    /// Acquire reference to value in TVar within the transaction
    pub fn read<'l, A>(&'l mut self, tvar : &'v TVar<'g, A>) -> Result<&'l A>
        where 'v : 'l
    {
        let addr: TVarAddr<'v, 'g> = unsafe { tvar.to_addr() } ;

        // Should be &&'l ?
        if let Some(v) = self.writes.get(&addr) {
            let r : && [u8] = &&**v;
            assert_eq!(r.len(), ::std::mem::size_of::<A>());
            let r2 : && A = unsafe { transmute(r) };
            return Result::ret(r2)
        }

        if self.snapshot == self.global.version.load(Ordering::SeqCst) {
            self.reads.push((addr, tvar.version.load(Ordering::SeqCst)));
            Result::ret(unsafe {tvar.get_val()})
        } else {
            self.validate().bind( | v | {
                assert!(self.snapshot <= v);
                self.snapshot = v;
                self.reads.push((addr, tvar.version.load(Ordering::SeqCst)));
                Result::ret(unsafe {tvar.get_val()})
            })
        }
    }

    /// Add the TVar and value to our writeset, these are thread-local until the
    /// transaction commits
    pub fn write<A : Copy>(&mut self, tvar : &'v mut TVar<'g, A>, val : A) -> Result<()> {
        let b : Box<[A]> = Box::new([val]);
        unsafe {
            self.writes.insert(tvar.to_addr(), transmute(b));
        }
        Result::ret(())
    }

    /// if (read−only transaction)
    ///     return
    /// while (!CAS(&global lock, snapshot, snapshot + 1))
    ///     snapshot = Validate()
    /// for each (addr, val) in writes
    ///     ∗addr = val
    /// global lock = snapshot + 2 // one more than CAS above
    pub fn commit(&mut self) -> Result<()> {
        fn commit_loop(l : &mut LocalState) -> Result<()> {
            if l.global
                .version
                .compare_and_swap(l.snapshot, l.snapshot + 1, Ordering::SeqCst) == l.snapshot {
                Result::ret(())
            } else {
                l.validate().bind(|v| {
                    l.snapshot = v;
                    commit_loop(l)
                })
            }
        }
        if self.writes.is_empty() {
            Result::ret(())
        } else {
            commit_loop(self).bind(| _ | {
                for (mut addr, value) in &self.writes {
                    let dst = unsafe { addr.aligned_mem() };
                    debug_assert_eq!(value.len(), dst.len());
                    copy_memory(value, dst);
                    addr.addr.version.fetch_add(1, Ordering::SeqCst);
                    //TODO: DCLP if this is too slow in the common case that waiters is empty
                    let mut waiters = addr.addr.waiters.lock().unwrap();
                    for bx in &*waiters {
                        let (ref mtx, ref cv) = **bx;
                        let mut done = mtx.lock().unwrap();
                        *done = true;
                        cv.notify_all();
                    }
                    waiters.clear();
                }
                self.global
                    .version
                    .compare_and_swap(self.snapshot + 1, self.snapshot + 2, Ordering::SeqCst);
                Result::ret(())
            })
        }
    }
}

/// Haskell-style container of mutable state within a transaction
/// TODO: impl TVar trait
pub struct TVar<'a, A: ?Sized> {
    version : AtomicUsize,
    waiters : Mutex<Vec<Arc<(Mutex<bool>, Condvar)>>>,
    global : &'a GlobalState,
    val : UnsafeCell<A>,
}

impl<'a, A: ?Sized> TVar<'a, A> {
    unsafe fn get_val(&self) -> &A {
        &*self.val.get()
    }

    unsafe fn get_val_mut(&self) -> &mut A {
        &mut *self.val.get()
    }
}

impl<'a, A> TVar<'a, A> {
    unsafe fn to_addr<'b>(&'b self) -> TVarAddr<'b, 'a> {
        use std::raw::Slice;
        let fat_ptr = Slice::<u8> {
            data: transmute(self),
            len: size_of_val(self),
        };
        //let ptr : &*mut TVar<'a, [u8]> = transmute(self));
        TVarAddr {
            align: align_of::<A>(),
            addr: transmute(fat_ptr),
        }
    }

    ///TODO: change trait API to use this signature
    fn read<'l, 'b> (&'b mut self, l: &'l mut LocalState<'b, 'a>) -> Result<&'l A> {
        l.read(self)
    }

}
impl<'a, A : Copy> TVar<'a, A> {
    ///TODO: change trait API to use this signature
    fn write<'l, 'b>(&'b mut self, l: &'l mut LocalState<'b, 'a>, arg : A) -> Result<()> {
        l.write(self, arg)
    }
}

#[derive(Copy, Clone)]
/// Lies... Lies
struct TVarAddr<'a, 'b : 'a> {
    align: usize,
    addr: &'a TVar<'b, [u8]>, //*mut
    //_phan: PhantomData<&'a TVar<'b, ()>>,
}

impl<'a, 'b : 'a> Hash for TVarAddr<'a, 'b> {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.raw_addr().hash(state);
    }
}

impl<'a, 'b : 'a> Eq for TVarAddr<'a, 'b> {}

impl<'a, 'b : 'a> PartialEq for TVarAddr<'a, 'b> {
    fn eq(&self, other: &Self) -> bool {
        let c = self.raw_addr() == other.raw_addr();
        if c {
            debug_assert_eq!(self.raw_len(), other.raw_len());
            debug_assert_eq!(self.align, other.align);
        }
        c
    }
}

impl<'a, 'b : 'a> TVarAddr<'a, 'b> {
    fn raw_addr(self) -> usize {
        self.addr as *const _ as *const () as usize
    }

    fn raw_len(self) -> usize {
        let y = self.addr.val.get();
        unsafe { transmute::<_,&[u8]>(y).len() }
    }

    unsafe fn aligned_mem(self) -> &'a mut [u8] {
        let buf = self.addr.val.get();
        let p = buf as *mut () as usize;
        // Assumes alignment is power of 2
        let o = ((p + (self.align - 1)) & self.align) - p;
        &mut (*buf)[o..]
    }

    fn version(self) -> &'a AtomicUsize {
        &(*self.addr).version
    }

    fn waiters(self) -> &'a mut Vec<(&'b Mutex<bool>, &'b Condvar)> {
        panic!("nyi")
    }
}

pub fn align_down_mut<T>(sp: *mut T, n: usize) -> *mut T {
  let sp = (sp as usize) & !(n - 1);
  sp as *mut T
}
<|endoftext|>


<commit_before>// Allow single character names so clippy doesn't lint on x, y, r, g, b, which
// are reasonable variable names in this domain.
#![allow(clippy::many_single_char_names)]

use std::process::exit;
use std::{cmp::min, io::Write, os::unix::io::AsRawFd};

use wayland_client::{
    event_enum,
    protocol::{wl_compositor, wl_keyboard, wl_pointer, wl_seat, wl_shm},
    Display, Filter, GlobalManager,
};
use wayland_protocols::xdg_shell::client::{xdg_surface, xdg_toplevel, xdg_wm_base};

// declare an event enum containing the events we want to receive in the iterator
event_enum!(
    Events |
    Pointer => wl_pointer::WlPointer,
    Keyboard => wl_keyboard::WlKeyboard
);

fn main() {
    let display = Display::connect_to_env().unwrap();

    let mut event_queue = display.create_event_queue();

    let attached_display = (*display).clone().attach(event_queue.token());

    let globals = GlobalManager::new(&attached_display);

    // Make a synchronized roundtrip to the wayland server.
    //
    // When this returns it must be true that the server has already
    // sent us all available globals.
    event_queue.sync_roundtrip(&mut (), |_, _, _| unreachable!()).unwrap();

    /*
     * Create a buffer with window contents
     */

    // buffer (and window) width and height
    let buf_x: u32 = 320;
    let buf_y: u32 = 240;

    // create a tempfile to write the contents of the window on
    let mut tmp = tempfile::tempfile().expect("Unable to create a tempfile.");
    // write the contents to it, lets put a nice color gradient
    for i in 0..(buf_x * buf_y) {
        let x = i % buf_x;
        let y = i / buf_x;
        let a = 0xFF;
        let r = min(((buf_x - x) * 0xFF) / buf_x, ((buf_y - y) * 0xFF) / buf_y);
        let g = min((x * 0xFF) / buf_x, ((buf_y - y) * 0xFF) / buf_y);
        let b = min(((buf_x - x) * 0xFF) / buf_x, (y * 0xFF) / buf_y);
        tmp.write_all(&((a << 24) + (r << 16) + (g << 8) + b).to_ne_bytes()).unwrap();
    }
    let _ = tmp.flush();

    /*
     * Init wayland objects
     */

    // The compositor allows us to creates surfaces
    let compositor = globals.instantiate_exact::<wl_compositor::WlCompositor>(1).unwrap();
    let surface = compositor.create_surface();

    // The SHM allows us to share memory with the server, and create buffers
    // on this shared memory to paint our surfaces
    let shm = globals.instantiate_exact::<wl_shm::WlShm>(1).unwrap();
    let pool = shm.create_pool(
        tmp.as_raw_fd(),            // RawFd to the tempfile serving as shared memory
        (buf_x * buf_y * 4) as i32, // size in bytes of the shared memory (4 bytes per pixel)
    );
    let buffer = pool.create_buffer(
        0,                        // Start of the buffer in the pool
        buf_x as i32,             // width of the buffer in pixels
        buf_y as i32,             // height of the buffer in pixels
        (buf_x * 4) as i32,       // number of bytes between the beginning of two consecutive lines
        wl_shm::Format::Argb8888, // chosen encoding for the data
    );

    let xdg_wm_base = globals
        .instantiate_exact::<xdg_wm_base::XdgWmBase>(2)
        .expect("Compositor does not support xdg_shell");

    xdg_wm_base.quick_assign(|xdg_wm_base, event, _| {
        if let xdg_wm_base::Event::Ping { serial } = event {
            xdg_wm_base.pong(serial);
        };
    });

    let xdg_surface = xdg_wm_base.get_xdg_surface(&surface);
    xdg_surface.quick_assign(move |xdg_surface, event, _| match event {
        xdg_surface::Event::Configure { serial } => {
            println!("xdg_surface (Configure)");
            xdg_surface.ack_configure(serial);
        }
        _ => unreachable!(),
    });

    let xdg_toplevel = xdg_surface.get_toplevel();
    xdg_toplevel.quick_assign(move |_, event, _| match event {
        xdg_toplevel::Event::Close => {
            exit(0);
        }
        xdg_toplevel::Event::Configure { width, height, states } => {
            println!(
                "xdg_toplevel (Configure) width: {}, height: {}, states: {:?}",
                width, height, states
            );
        }
        _ => unreachable!(),
    });
    xdg_toplevel.set_title("Simple Window".to_string());

    // initialize a seat to retrieve pointer & keyboard events
    //
    // example of using a common filter to handle both pointer & keyboard events
    let common_filter = Filter::new(move |event, _, _| match event {
        Events::Pointer { event, .. } => match event {
            wl_pointer::Event::Enter { surface_x, surface_y, .. } => {
                println!("Pointer entered at ({}, {}).", surface_x, surface_y);
            }
            wl_pointer::Event::Leave { .. } => {
                println!("Pointer left.");
            }
            wl_pointer::Event::Motion { surface_x, surface_y, .. } => {
                println!("Pointer moved to ({}, {}).", surface_x, surface_y);
            }
            wl_pointer::Event::Button { button, state, .. } => {
                println!("Button {} was {:?}.", button, state);
            }
            _ => {}
        },
        Events::Keyboard { event, .. } => match event {
            wl_keyboard::Event::Enter { .. } => {
                println!("Gained keyboard focus.");
            }
            wl_keyboard::Event::Leave { .. } => {
                println!("Lost keyboard focus.");
            }
            wl_keyboard::Event::Key { key, state, .. } => {
                println!("Key with id {} was {:?}.", key, state);
            }
            _ => (),
        },
    });
    // to be handled properly this should be more dynamic, as more
    // than one seat can exist (and they can be created and destroyed
    // dynamically), however most "traditional" setups have a single
    // seat, so we'll keep it simple here
    let mut pointer_created = false;
    let mut keyboard_created = false;
    globals.instantiate_exact::<wl_seat::WlSeat>(1).unwrap().quick_assign(move |seat, event, _| {
        // The capabilities of a seat are known at runtime and we retrieve
        // them via an events. 3 capabilities exists: pointer, keyboard, and touch
        // we are only interested in pointer & keyboard here
        use wayland_client::protocol::wl_seat::{Capability, Event as SeatEvent};

        if let SeatEvent::Capabilities { capabilities } = event {
            if !pointer_created && capabilities.contains(Capability::Pointer) {
                // create the pointer only once
                pointer_created = true;
                seat.get_pointer().assign(common_filter.clone());
            }
            if !keyboard_created && capabilities.contains(Capability::Keyboard) {
                // create the keyboard only once
                keyboard_created = true;
                seat.get_keyboard().assign(common_filter.clone());
            }
        }
    });

    surface.commit();

    event_queue.sync_roundtrip(&mut (), |_, _, _| { /* we ignore unfiltered messages */ }).unwrap();

    surface.attach(Some(&buffer), 0, 0);
    surface.commit();

    loop {
        event_queue.dispatch(&mut (), |_, _, _| { /* we ignore unfiltered messages */ }).unwrap();
    }
}
<commit_msg>Use a BufWriter in simple_window example<commit_after>// Allow single character names so clippy doesn't lint on x, y, r, g, b, which
// are reasonable variable names in this domain.
#![allow(clippy::many_single_char_names)]

use std::{
    cmp::min,
    io::{BufWriter, Write},
    os::unix::io::AsRawFd,
    process::exit,
    time::Instant,
};

use wayland_client::{
    event_enum,
    protocol::{wl_compositor, wl_keyboard, wl_pointer, wl_seat, wl_shm},
    Display, Filter, GlobalManager,
};
use wayland_protocols::xdg_shell::client::{xdg_surface, xdg_toplevel, xdg_wm_base};

// declare an event enum containing the events we want to receive in the iterator
event_enum!(
    Events |
    Pointer => wl_pointer::WlPointer,
    Keyboard => wl_keyboard::WlKeyboard
);

fn main() {
    let now = Instant::now();

    let display = Display::connect_to_env().unwrap();

    let mut event_queue = display.create_event_queue();

    let attached_display = (*display).clone().attach(event_queue.token());

    let globals = GlobalManager::new(&attached_display);

    // Make a synchronized roundtrip to the wayland server.
    //
    // When this returns it must be true that the server has already
    // sent us all available globals.
    event_queue.sync_roundtrip(&mut (), |_, _, _| unreachable!()).unwrap();

    /*
     * Create a buffer with window contents
     */

    // buffer (and window) width and height
    let buf_x: u32 = 320;
    let buf_y: u32 = 240;

    // create a tempfile to write the contents of the window on
    let mut tmp = tempfile::tempfile().expect("Unable to create a tempfile.");

    // write the contents to it, lets put a nice color gradient
    {
        let now = Instant::now();

        let mut buf = BufWriter::new(&mut tmp);
        for y in 0..buf_y {
            for x in 0..buf_x {
                let a = 0xFF;
                let r = min(((buf_x - x) * 0xFF) / buf_x, ((buf_y - y) * 0xFF) / buf_y);
                let g = min((x * 0xFF) / buf_x, ((buf_y - y) * 0xFF) / buf_y);
                let b = min(((buf_x - x) * 0xFF) / buf_x, (y * 0xFF) / buf_y);

                let color = (a << 24) + (r << 16) + (g << 8) + b;
                buf.write_all(&color.to_ne_bytes()).unwrap();
            }
        }
        buf.flush().unwrap();

        println!(
            "Time used to create the nice color gradient: {:?}",
            Instant::now().duration_since(now)
        );
    }

    /*
     * Init wayland objects
     */

    // The compositor allows us to creates surfaces
    let compositor = globals.instantiate_exact::<wl_compositor::WlCompositor>(1).unwrap();
    let surface = compositor.create_surface();

    // The SHM allows us to share memory with the server, and create buffers
    // on this shared memory to paint our surfaces
    let shm = globals.instantiate_exact::<wl_shm::WlShm>(1).unwrap();
    let pool = shm.create_pool(
        tmp.as_raw_fd(),            // RawFd to the tempfile serving as shared memory
        (buf_x * buf_y * 4) as i32, // size in bytes of the shared memory (4 bytes per pixel)
    );
    let buffer = pool.create_buffer(
        0,                        // Start of the buffer in the pool
        buf_x as i32,             // width of the buffer in pixels
        buf_y as i32,             // height of the buffer in pixels
        (buf_x * 4) as i32,       // number of bytes between the beginning of two consecutive lines
        wl_shm::Format::Argb8888, // chosen encoding for the data
    );

    let xdg_wm_base = globals
        .instantiate_exact::<xdg_wm_base::XdgWmBase>(2)
        .expect("Compositor does not support xdg_shell");

    xdg_wm_base.quick_assign(|xdg_wm_base, event, _| {
        if let xdg_wm_base::Event::Ping { serial } = event {
            xdg_wm_base.pong(serial);
        };
    });

    let xdg_surface = xdg_wm_base.get_xdg_surface(&surface);
    xdg_surface.quick_assign(move |xdg_surface, event, _| match event {
        xdg_surface::Event::Configure { serial } => {
            println!("xdg_surface (Configure)");
            xdg_surface.ack_configure(serial);
        }
        _ => unreachable!(),
    });

    let xdg_toplevel = xdg_surface.get_toplevel();
    xdg_toplevel.quick_assign(move |_, event, _| match event {
        xdg_toplevel::Event::Close => {
            exit(0);
        }
        xdg_toplevel::Event::Configure { width, height, states } => {
            println!(
                "xdg_toplevel (Configure) width: {}, height: {}, states: {:?}",
                width, height, states
            );
        }
        _ => unreachable!(),
    });
    xdg_toplevel.set_title("Simple Window".to_string());

    // initialize a seat to retrieve pointer & keyboard events
    //
    // example of using a common filter to handle both pointer & keyboard events
    let common_filter = Filter::new(move |event, _, _| match event {
        Events::Pointer { event, .. } => match event {
            wl_pointer::Event::Enter { surface_x, surface_y, .. } => {
                println!("Pointer entered at ({}, {}).", surface_x, surface_y);
            }
            wl_pointer::Event::Leave { .. } => {
                println!("Pointer left.");
            }
            wl_pointer::Event::Motion { surface_x, surface_y, .. } => {
                println!("Pointer moved to ({}, {}).", surface_x, surface_y);
            }
            wl_pointer::Event::Button { button, state, .. } => {
                println!("Button {} was {:?}.", button, state);
            }
            _ => {}
        },
        Events::Keyboard { event, .. } => match event {
            wl_keyboard::Event::Enter { .. } => {
                println!("Gained keyboard focus.");
            }
            wl_keyboard::Event::Leave { .. } => {
                println!("Lost keyboard focus.");
            }
            wl_keyboard::Event::Key { key, state, .. } => {
                println!("Key with id {} was {:?}.", key, state);
            }
            _ => (),
        },
    });
    // to be handled properly this should be more dynamic, as more
    // than one seat can exist (and they can be created and destroyed
    // dynamically), however most "traditional" setups have a single
    // seat, so we'll keep it simple here
    let mut pointer_created = false;
    let mut keyboard_created = false;
    globals.instantiate_exact::<wl_seat::WlSeat>(1).unwrap().quick_assign(move |seat, event, _| {
        // The capabilities of a seat are known at runtime and we retrieve
        // them via an events. 3 capabilities exists: pointer, keyboard, and touch
        // we are only interested in pointer & keyboard here
        use wayland_client::protocol::wl_seat::{Capability, Event as SeatEvent};

        if let SeatEvent::Capabilities { capabilities } = event {
            if !pointer_created && capabilities.contains(Capability::Pointer) {
                // create the pointer only once
                pointer_created = true;
                seat.get_pointer().assign(common_filter.clone());
            }
            if !keyboard_created && capabilities.contains(Capability::Keyboard) {
                // create the keyboard only once
                keyboard_created = true;
                seat.get_keyboard().assign(common_filter.clone());
            }
        }
    });

    surface.commit();

    event_queue.sync_roundtrip(&mut (), |_, _, _| { /* we ignore unfiltered messages */ }).unwrap();

    surface.attach(Some(&buffer), 0, 0);
    surface.commit();

    println!("Time used before enter in the main loop: {:?}", Instant::now().duration_since(now));

    loop {
        event_queue.dispatch(&mut (), |_, _, _| { /* we ignore unfiltered messages */ }).unwrap();
    }
}
<|endoftext|>


<commit_before>/// List of material font icons. Use this to generate a font Icon.
// emacs: use C-x 8 Ret => #xf207 ("BUS"-Glyph)
pub const FLOPPY_FONT_ICON: &str = "";
pub const ADD_FONT_ICON: &str = "";
pub const LOAD_FONT_ICON: &str = "";
pub const SAVE_FONT_ICON: &str = "";
pub const KEYBOARD_ARROW_LEFT_FONT_ICON: &str = "";
pub const KEYBOARD_ARROW_RIGHT_FONT_ICON: &str = "";
pub const REMOVE_ICON: &str = "";
pub const ARROW_DOWN_ICON: &str = "";

pub const MENU_FONT_ICON: &str = "";
pub const SETTINGS_FONT_ICON: &str = "";
pub const HELP_OUTLINE_FONT_ICON: &str = "";

// category: Social
pub const ANDROID_FONT_ICON: &str = "";
pub const APPLE_FONT_ICON: &str = "";
pub const GITHUB_FONT_ICON: &str = "";
pub const GOOGLE_FONT_ICON: &str = "";
pub const GOOGLE_DRIVE_FONT_ICON: &str = "";
pub const GOOGLE_MAPS_FONT_ICON: &str = "";
pub const LANGUAGE_JAVASCRIPT_FONT_ICON: &str = "";
pub const LANGUAGE_HTML5_FONT_ICON: &str = "";
pub const LANGUAGE_PYTHON_ICON: &str = "";
pub const PAYPAL_FONT_ICON: &str = "";
pub const POCKET_FONT_ICON: &str = "";
pub const RSS_FONT_ICON: &str = "";
pub const SHARE_FONT_ICON: &str = "";
pub const STEAM_FONT_ICON: &str = "";
pub const TWITTER_BOX_FONT_ICON: &str = "";
pub const TWITTER_FONT_ICON: &str = "";
pub const WIKIPEDIA_FONT_ICON: &str = "";
pub const LINKEDIN_FONT_ICON: &str = "";
pub const REDDIT_FONT_ICON: &str = "";
pub const VIMEO_FONT_ICON: &str = "";
pub const WINDOWS_FONT_ICON: &str = "";

// category: Person
pub const ACCOUNT_FONT_ICON: &str = "";
pub const ACCOUNT_ADD_FONT_ICON: &str = "";
pub const ACCOUNT_BOX_FONT_ICON: &str = "";
pub const ACCOUNT_CIRCLE_FONT_ICON: &str = "";
pub const ACCOUNTS_ADD_FONT_ICON: &str = "";
pub const ACCOUNTS_FONT_ICON: &str = "";
pub const MALE_FONT_ICON: &str = "";
pub const FEMALE_FONT_ICON: &str = "";
pub const MALE_FEMALE_FONT_ICON: &str = "";
pub const RUN_FONT_ICON: &str = "";
pub const WALK_FONT_ICON: &str = "";

// category: File
pub const CLOUD_FONT_ICON: &str = "";
pub const DOWNLOAD_FONT_ICON: &str = "";
pub const UPLOAD_FONT_ICON: &str = "";
pub const FILE_FONT_ICON: &str = "";
pub const FOLDER_FONT_ICON: &str = "";
pub const FOLDER_OUTLINE_FONT_ICON: &str = "";
pub const CLOUD_DONE_FONT_ICON: &str = "";
pub const CLOUD_OFF_FONT_ICON: &str = "";
pub const CLOUD_DOWNLOAD_FONT_ICON: &str = "";
pub const CLOUD_OUTLINE_FONT_ICON: &str = "";
pub const CLOUD_UPLOAD_FONT_ICON: &str = "";

// category: Comment
pub const COMMENT_FONT_ICON: &str = "";
pub const COMMENT_LIST_FONT_ICON: &str = "";
pub const COMMENT_IMAGE_FONT_ICON: &str = "";
pub const COMMENT_EDIT_FONT_ICON: &str = "";
pub const COMMENT_ALERT_FONT_ICON: &str = "";
pub const COMMENT_MORE_FONT_ICON: &str = "";
pub const COMMENT_OUTLINE_FONT_ICON: &str = "";
pub const COMMENTS_FONT_ICON: &str = "";
pub const COMMENT_VIDEO_TEXT_FONT_ICON: &str = "";
pub const COMMENT_TEXT_FONT_ICON: &str = "";

// category: Form
pub const CHECK_ALL_FONT_ICON: &str = "";
pub const CHECK_CIRCLE_U_FONT_ICON: &str = "";
pub const CHECK_CIRCLE_FONT_ICON: &str = "";
pub const CHECK_SQUARE_FONT_ICON: &str = "";
pub const CHECK_FONT_ICON: &str = "";
//pub const CHECK_FONT_ICON: &str = "";
pub const CIRCLE_O_FONT_ICON: &str = "";
pub const CIRCLE_FONT_ICON: &str = "";
pub const DOT_CIRCLE_ALT_FONT_ICON: &str = "";
pub const DOT_CIRCLE_FONT_ICON: &str = "";
pub const MINUS_CIRCLE_OUTLINE_FONT_ICON: &str = "";
pub const MINUS_CIRCLE_FONT_ICON: &str = "";
pub const MINUS_SQUARE_FONT_ICON: &str = "";
pub const MINUS_FONT_ICON: &str = "";
//pub const MINUS_FONT_ICON: &str = "";
pub const PLUS_CIRCLE_O_DUPLICATE_FONT_ICON: &str = "";
pub const PLUS_CIRCLE_O_FONT_ICON: &str = "";
pub const PLUS_CIRCLE_FONT_ICON: &str = "";
pub const PLUS_SQUARE_FONT_ICON: &str = "";
pub const PLUS_FONT_ICON: &str = "";
pub const SQUARE_O_FONT_ICON: &str = "";
pub const STAR_CIRCLE_FONT_ICON: &str = "";
pub const STAR_HALF_FONT_ICON: &str = "";
pub const STAR_OUTLINE_FONT_ICON: &str = "";
pub const STAR_STAR_FONT_ICON: &str = "";
<commit_msg>material_font_icons: remove old unused code name<commit_after><|endoftext|>


<commit_before>#![crate_name = "rboy"]

extern crate clap;
extern crate sdl2;
extern crate rboy;

use rboy::device::Device;
use std::sync::{Arc,RwLock};
use std::sync::mpsc::{Sender,Receiver};
use std::sync::mpsc::TryRecvError::{Disconnected,Empty};

const SCALE : u32 = 2;
const EXITCODE_SUCCESS : i32 = 0;
const EXITCODE_CPULOADFAILS : i32 = 2;

fn main() {
    let exit_status = real_main();
    if exit_status != EXITCODE_SUCCESS {
        std::process::exit(exit_status);
    }
}

fn real_main() -> i32 {
    let matches = clap::App::new("rboy")
        .version("0.1")
        .author("Mathijs van de Nes")
        .about("A Gameboy Colour emulator written in Rust")
        .arg(clap::Arg::with_name("filename")
             .help("Sets the ROM file to load")
             .required(true))
        .arg(clap::Arg::with_name("serial")
             .help("Prints the data from the serial port to stdout")
             .short("s")
             .long("serial"))
        .arg(clap::Arg::with_name("classic")
             .help("Forces the emulator to run in classic Gameboy mode")
             .short("c")
             .long("classic"))
        .get_matches();

    let opt_serial = matches.is_present("serial");
    let opt_classic = matches.is_present("classic");
    let filename = matches.value_of("filename").unwrap();

    let cpu = construct_cpu(filename, opt_classic, opt_serial);
    if cpu.is_none() { return EXITCODE_CPULOADFAILS; }
    let cpu = cpu.unwrap();

    let sdl_context = sdl2::init().unwrap();
    let sdl_video = sdl_context.video().unwrap();
    let window = match sdl2::video::WindowBuilder::new(&sdl_video,
                                                "RBoy - A gameboy in Rust",
                                                160*SCALE,
                                                144*SCALE).build() {
        Ok(window) => window,
        Err(err) => panic!("failed to open window: {}", err),
    };
    let mut renderer = match sdl2::render::RendererBuilder::new(window).accelerated().build() {
        Ok(screen) => screen,
        Err(err) => panic!("failed to open screen: {}", err),
    };

    let (sdl_tx, cpu_rx) = std::sync::mpsc::channel();
    let (cpu_tx, sdl_rx) = std::sync::mpsc::channel();
    let rawscreen = ::std::iter::repeat(0u8).take(160*144*3).collect();
    let arc = Arc::new(RwLock::new(rawscreen));
    let arc2 = arc.clone();

    let cpuloop_thread = std::thread::spawn(move|| cpuloop(&cpu_tx, &cpu_rx, arc2, cpu));

    let mut event_queue = sdl_context.event_pump().unwrap();
    'main : loop {
        let mut refreshed = false;
        'rx : loop {
            match sdl_rx.try_recv() {
                Err(Disconnected) => break 'main,
                Ok(_) => { if !refreshed { recalculate_screen(&mut renderer, &arc); refreshed = true } },
                Err(Empty) => break 'rx,
            }
        }
        for event in event_queue.wait_timeout_iter(2) {
            match event {
                sdl2::event::Event::Quit { .. } => break 'main,
                sdl2::event::Event::KeyDown { keycode: Some(sdl2::keyboard::Keycode::Escape), .. }
                    => break 'main,
                sdl2::event::Event::KeyDown { keycode: Some(sdl2::keyboard::Keycode::LShift), .. }
                    => { let _ = sdl_tx.send(GBEvent::SpeedUp); },
                sdl2::event::Event::KeyUp { keycode: Some(sdl2::keyboard::Keycode::LShift), .. }
                    => { let _ = sdl_tx.send(GBEvent::SlowDown); },
                sdl2::event::Event::KeyDown { keycode: Some(sdlkey), .. } => {
                    match sdl_to_keypad(sdlkey) {
                        Some(key) =>  { let _ = sdl_tx.send(GBEvent::KeyDown(key)); },
                        None => {},
                    }
                },
                sdl2::event::Event::KeyUp { keycode: Some(sdlkey), .. } => {
                    match sdl_to_keypad(sdlkey) {
                        Some(key) => { let _ = sdl_tx.send(GBEvent::KeyUp(key)); },
                        None => {},
                    }
                },
                _ => {}
            }
        }
    }

    drop(sdl_tx); // Disconnect such that the cpuloop will exit
    let _ = cpuloop_thread.join();

    EXITCODE_SUCCESS
}

fn sdl_to_keypad(key: sdl2::keyboard::Keycode) -> Option<rboy::KeypadKey> {
    match key {
        sdl2::keyboard::Keycode::Z => Some(rboy::KeypadKey::A),
        sdl2::keyboard::Keycode::X => Some(rboy::KeypadKey::B),
        sdl2::keyboard::Keycode::Up => Some(rboy::KeypadKey::Up),
        sdl2::keyboard::Keycode::Down => Some(rboy::KeypadKey::Down),
        sdl2::keyboard::Keycode::Left => Some(rboy::KeypadKey::Left),
        sdl2::keyboard::Keycode::Right => Some(rboy::KeypadKey::Right),
        sdl2::keyboard::Keycode::Space => Some(rboy::KeypadKey::Select),
        sdl2::keyboard::Keycode::Return => Some(rboy::KeypadKey::Start),
        _ => None,
    }
}

fn recalculate_screen(screen: &mut sdl2::render::Renderer, arc: &Arc<RwLock<Vec<u8>>>) {
    screen.set_draw_color(sdl2::pixels::Color::RGB(0xFF, 0xFF, 0xFF));
    screen.clear();

    let data =  arc.read().unwrap().clone();
    for y in 0..144 {
        for x in 0..160 {
            screen.set_draw_color(sdl2::pixels::Color::RGB(data[y*160*3 + x*3 + 0],
                                                            data[y*160*3 + x*3 + 1],
                                                            data[y*160*3 + x*3 + 2]));
            screen.fill_rect(sdl2::rect::Rect::new(x as i32 * SCALE as i32, y as i32 * SCALE as i32, SCALE, SCALE).unwrap().unwrap());
        }
    }
    screen.present();
}

enum GBEvent {
    KeyUp(rboy::KeypadKey),
    KeyDown(rboy::KeypadKey),
    SpeedUp,
    SlowDown,
}

fn warn(message: &'static str) {
    use std::io::Write;
    let _ = write!(&mut std::io::stderr(), "{}\n", message);
}

fn construct_cpu(filename: &str, classic_mode: bool, output_serial: bool) -> Option<Device> {
    let opt_c = match classic_mode {
        true => Device::new(filename),
        false => Device::new_cgb(filename),
    };
    let mut c = match opt_c
    {
        Ok(cpu) => { cpu },
        Err(message) => { warn(message); return None; },
    };
    c.set_stdout(output_serial);
    Some(c)
}

fn cpuloop(cpu_tx: &Sender<()>, cpu_rx: &Receiver<GBEvent>, arc: Arc<RwLock<Vec<u8>>>, cpu: Device) {
    let mut c = cpu;
    let periodic = timer_periodic(8);
    let mut limit_speed = true;

    let waitticks = (4194304f64 / 1000.0 * 8.0) as u32;

    let mut ticks = 0;
    'cpuloop: loop {
        while ticks < waitticks {
            ticks += c.do_cycle();
            if c.check_and_reset_gpu_updated() {
                let mut data = arc.write().unwrap();
                let gpu_data = c.get_gpu_data();
                for i in 0..data.len() { data[i] = gpu_data[i]; }
                if cpu_tx.send(()).is_err() { break 'cpuloop };
            }
        }
        ticks -= waitticks;
        if limit_speed { let _ = periodic.recv(); }

        'rx : loop {
            match cpu_rx.try_recv() {
                Ok(event) => match event {
                    GBEvent::KeyUp(key) => c.keyup(key),
                    GBEvent::KeyDown(key) => c.keydown(key),
                    GBEvent::SpeedUp => limit_speed = false,
                    GBEvent::SlowDown => limit_speed = true,
                },
                Err(Empty) => break 'rx,
                Err(Disconnected) => break 'cpuloop,
            }
        }
    }
}

fn timer_periodic(ms: u32) -> Receiver<()> {
    let (tx, rx) = std::sync::mpsc::channel();
    std::thread::spawn(move || {
        loop {
            std::thread::sleep_ms(ms);
            if tx.send(()).is_err() {
                break;
            }
        }
    });
    rx
}
<commit_msg>Add option for scale on the commandline<commit_after>#![crate_name = "rboy"]

extern crate clap;
extern crate sdl2;
extern crate rboy;

use rboy::device::Device;
use std::sync::{Arc,RwLock};
use std::sync::mpsc::{Sender,Receiver};
use std::sync::mpsc::TryRecvError::{Disconnected,Empty};
use std::error::Error;

const EXITCODE_SUCCESS : i32 = 0;
const EXITCODE_CPULOADFAILS : i32 = 2;

fn main() {
    let exit_status = real_main();
    if exit_status != EXITCODE_SUCCESS {
        std::process::exit(exit_status);
    }
}

fn real_main() -> i32 {
    let matches = clap::App::new("rboy")
        .version("0.1")
        .author("Mathijs van de Nes")
        .about("A Gameboy Colour emulator written in Rust")
        .arg(clap::Arg::with_name("filename")
             .help("Sets the ROM file to load")
             .required(true))
        .arg(clap::Arg::with_name("serial")
             .help("Prints the data from the serial port to stdout")
             .short("s")
             .long("serial"))
        .arg(clap::Arg::with_name("classic")
             .help("Forces the emulator to run in classic Gameboy mode")
             .short("c")
             .long("classic"))
        .arg(clap::Arg::with_name("scale")
             .help("Sets the scale of the interface. Default: 2")
             .short("x")
             .long("scale")
             .validator(|s|
                 match s.parse::<u32>() {
                     Err(e) => Err(format!("Could not parse scale: {}", e.description())),
                     Ok(s) if s < 1 => Err("Scale must be at least 1".to_owned()),
                     Ok(s) if s > 8 => Err("Scale may be at most 8".to_owned()),
                     Ok(..) => Ok(()),
                 })
             .takes_value(true))
        .get_matches();

    let opt_serial = matches.is_present("serial");
    let opt_classic = matches.is_present("classic");
    let filename = matches.value_of("filename").unwrap();
    let scale = matches.value_of("scale").unwrap_or("2").parse::<u32>().unwrap();

    let cpu = construct_cpu(filename, opt_classic, opt_serial);
    if cpu.is_none() { return EXITCODE_CPULOADFAILS; }
    let cpu = cpu.unwrap();

    let sdl_context = sdl2::init().unwrap();
    let sdl_video = sdl_context.video().unwrap();
    let window = match sdl2::video::WindowBuilder::new(&sdl_video,
                                                "RBoy - A gameboy in Rust",
                                                160*scale,
                                                144*scale).build() {
        Ok(window) => window,
        Err(err) => panic!("failed to open window: {}", err),
    };
    let mut renderer = match sdl2::render::RendererBuilder::new(window).accelerated().build() {
        Ok(screen) => screen,
        Err(err) => panic!("failed to open screen: {}", err),
    };

    let (sdl_tx, cpu_rx) = std::sync::mpsc::channel();
    let (cpu_tx, sdl_rx) = std::sync::mpsc::channel();
    let rawscreen = ::std::iter::repeat(0u8).take(160*144*3).collect();
    let arc = Arc::new(RwLock::new(rawscreen));
    let arc2 = arc.clone();

    let cpuloop_thread = std::thread::spawn(move|| cpuloop(&cpu_tx, &cpu_rx, arc2, cpu));

    let mut event_queue = sdl_context.event_pump().unwrap();
    'main : loop {
        let mut refreshed = false;
        'rx : loop {
            match sdl_rx.try_recv() {
                Err(Disconnected) => break 'main,
                Ok(_) => { if !refreshed { recalculate_screen(&mut renderer, &arc, scale); refreshed = true } },
                Err(Empty) => break 'rx,
            }
        }
        for event in event_queue.wait_timeout_iter(2) {
            match event {
                sdl2::event::Event::Quit { .. } => break 'main,
                sdl2::event::Event::KeyDown { keycode: Some(sdl2::keyboard::Keycode::Escape), .. }
                    => break 'main,
                sdl2::event::Event::KeyDown { keycode: Some(sdl2::keyboard::Keycode::LShift), .. }
                    => { let _ = sdl_tx.send(GBEvent::SpeedUp); },
                sdl2::event::Event::KeyUp { keycode: Some(sdl2::keyboard::Keycode::LShift), .. }
                    => { let _ = sdl_tx.send(GBEvent::SlowDown); },
                sdl2::event::Event::KeyDown { keycode: Some(sdlkey), .. } => {
                    match sdl_to_keypad(sdlkey) {
                        Some(key) =>  { let _ = sdl_tx.send(GBEvent::KeyDown(key)); },
                        None => {},
                    }
                },
                sdl2::event::Event::KeyUp { keycode: Some(sdlkey), .. } => {
                    match sdl_to_keypad(sdlkey) {
                        Some(key) => { let _ = sdl_tx.send(GBEvent::KeyUp(key)); },
                        None => {},
                    }
                },
                _ => {}
            }
        }
    }

    drop(sdl_tx); // Disconnect such that the cpuloop will exit
    let _ = cpuloop_thread.join();

    EXITCODE_SUCCESS
}

fn sdl_to_keypad(key: sdl2::keyboard::Keycode) -> Option<rboy::KeypadKey> {
    match key {
        sdl2::keyboard::Keycode::Z => Some(rboy::KeypadKey::A),
        sdl2::keyboard::Keycode::X => Some(rboy::KeypadKey::B),
        sdl2::keyboard::Keycode::Up => Some(rboy::KeypadKey::Up),
        sdl2::keyboard::Keycode::Down => Some(rboy::KeypadKey::Down),
        sdl2::keyboard::Keycode::Left => Some(rboy::KeypadKey::Left),
        sdl2::keyboard::Keycode::Right => Some(rboy::KeypadKey::Right),
        sdl2::keyboard::Keycode::Space => Some(rboy::KeypadKey::Select),
        sdl2::keyboard::Keycode::Return => Some(rboy::KeypadKey::Start),
        _ => None,
    }
}

fn recalculate_screen(screen: &mut sdl2::render::Renderer, arc: &Arc<RwLock<Vec<u8>>>, scale: u32) {
    screen.set_draw_color(sdl2::pixels::Color::RGB(0xFF, 0xFF, 0xFF));
    screen.clear();

    let data =  arc.read().unwrap().clone();
    for y in 0..144 {
        for x in 0..160 {
            screen.set_draw_color(sdl2::pixels::Color::RGB(data[y*160*3 + x*3 + 0],
                                                            data[y*160*3 + x*3 + 1],
                                                            data[y*160*3 + x*3 + 2]));
            screen.fill_rect(sdl2::rect::Rect::new(x as i32 * scale as i32, y as i32 * scale as i32, scale, scale).unwrap().unwrap());
        }
    }
    screen.present();
}

enum GBEvent {
    KeyUp(rboy::KeypadKey),
    KeyDown(rboy::KeypadKey),
    SpeedUp,
    SlowDown,
}

fn warn(message: &'static str) {
    use std::io::Write;
    let _ = write!(&mut std::io::stderr(), "{}\n", message);
}

fn construct_cpu(filename: &str, classic_mode: bool, output_serial: bool) -> Option<Device> {
    let opt_c = match classic_mode {
        true => Device::new(filename),
        false => Device::new_cgb(filename),
    };
    let mut c = match opt_c
    {
        Ok(cpu) => { cpu },
        Err(message) => { warn(message); return None; },
    };
    c.set_stdout(output_serial);
    Some(c)
}

fn cpuloop(cpu_tx: &Sender<()>, cpu_rx: &Receiver<GBEvent>, arc: Arc<RwLock<Vec<u8>>>, cpu: Device) {
    let mut c = cpu;
    let periodic = timer_periodic(8);
    let mut limit_speed = true;

    let waitticks = (4194304f64 / 1000.0 * 8.0) as u32;

    let mut ticks = 0;
    'cpuloop: loop {
        while ticks < waitticks {
            ticks += c.do_cycle();
            if c.check_and_reset_gpu_updated() {
                let mut data = arc.write().unwrap();
                let gpu_data = c.get_gpu_data();
                for i in 0..data.len() { data[i] = gpu_data[i]; }
                if cpu_tx.send(()).is_err() { break 'cpuloop };
            }
        }
        ticks -= waitticks;
        if limit_speed { let _ = periodic.recv(); }

        'rx : loop {
            match cpu_rx.try_recv() {
                Ok(event) => match event {
                    GBEvent::KeyUp(key) => c.keyup(key),
                    GBEvent::KeyDown(key) => c.keydown(key),
                    GBEvent::SpeedUp => limit_speed = false,
                    GBEvent::SlowDown => limit_speed = true,
                },
                Err(Empty) => break 'rx,
                Err(Disconnected) => break 'cpuloop,
            }
        }
    }
}

fn timer_periodic(ms: u32) -> Receiver<()> {
    let (tx, rx) = std::sync::mpsc::channel();
    std::thread::spawn(move || {
        loop {
            std::thread::sleep_ms(ms);
            if tx.send(()).is_err() {
                break;
            }
        }
    });
    rx
}
<|endoftext|>


<commit_before>#![deny(warnings)]

#[macro_use]
extern crate error_chain;
extern crate fs2;
#[cfg(any(all(target_os = "linux", not(target_env = "musl")),
          target_os = "macos"))]
extern crate libc;
extern crate rustc_version;
extern crate serde_json;
extern crate tempdir;
extern crate toml;
extern crate walkdir;

use std::hash::{Hash, Hasher};
use std::io::Write;
use std::path::{Path, PathBuf};
use std::process::ExitStatus;
use std::{env, io, process};

use rustc_version::Channel;

use errors::*;
use rustc::Target;

mod cargo;
mod cli;
mod errors;
mod extensions;
mod flock;
mod rustc;
mod sysroot;
mod util;
mod xargo;

// We use a different sysroot for Native compilation to avoid file locking
//
// Cross compilation requires `lib/rustlib/$HOST` to match `rustc`'s sysroot,
// whereas Native compilation wants to use a custom `lib/rustlib/$HOST`. If each
// mode has its own sysroot then we avoid sharing that directory and thus file
// locking it.
pub enum CompilationMode {
    Cross(Target),
    Native(String),
}

impl CompilationMode {
    fn hash<H>(&self, hasher: &mut H) -> Result<()>
    where
        H: Hasher,
    {
        match *self {
            CompilationMode::Cross(ref target) => target.hash(hasher)?,
            CompilationMode::Native(ref triple) => triple.hash(hasher),
        }

        Ok(())
    }

    fn triple(&self) -> &str {
        match *self {
            CompilationMode::Cross(ref target) => target.triple(),
            CompilationMode::Native(ref triple) => triple,
        }
    }

    fn is_native(&self) -> bool {
        match *self {
            CompilationMode::Native(_) => true,
            _ => false,
        }
    }
}

pub fn main() {
    fn show_backtrace() -> bool {
        env::var("RUST_BACKTRACE").as_ref().map(|s| &s[..]) == Ok("1")
    }

    match run() {
        Err(e) => {
            let stderr = io::stderr();
            let mut stderr = stderr.lock();

            writeln!(stderr, "error: {}", e).ok();

            for e in e.iter().skip(1) {
                writeln!(stderr, "caused by: {}", e).ok();
            }

            if show_backtrace() {
                if let Some(backtrace) = e.backtrace() {
                    writeln!(stderr, "{:?}", backtrace).ok();
                }
            } else {
                writeln!(
                    stderr,
                    "note: run with `RUST_BACKTRACE=1` for a backtrace"
                )
                    .ok();
            }

            process::exit(1)
        }
        Ok(status) => {
            if !status.success() {
                process::exit(status.code().unwrap_or(1))
            }
        }
    }
}

fn run() -> Result<ExitStatus> {
    let args = cli::args();
    let verbose = args.verbose();

    let meta = rustc::version();

    if let Some(sc) = args.subcommand() {
        if !sc.needs_sysroot() {
            return cargo::run(&args, verbose);
        }
    } else if args.version() {
        writeln!(
            io::stderr(),
            concat!("xargo ", env!("CARGO_PKG_VERSION"), "{}"),
            include_str!(concat!(env!("OUT_DIR"), "/commit-info.txt"))
        )
            .ok();

        return cargo::run(&args, verbose);
    }

    let cd = CurrentDirectory::get()?;

    let config = cargo::config()?;
    if let Some(root) = cargo::root()? {
        // We can't build sysroot with stable or beta due to unstable features
        let sysroot = rustc::sysroot(verbose)?;
        let src = match meta.channel {
            Channel::Dev => rustc::Src::from_env().ok_or(
                "The XARGO_RUST_SRC env variable must be set and point to the \
                 Rust source directory when working with the 'dev' channel")?,
            Channel::Nightly => {
                if let Some(src) = rustc::Src::from_env() {
                    src
                } else {
                    sysroot.src()?
                }
            }
            Channel::Stable | Channel::Beta => {
                return cargo::run(&args, verbose)
            }
        };

        let cmode = if let Some(triple) = args.target() {
            if Path::new(triple).is_file() {
                bail!(
                    "Xargo doesn't support files as an argument to --target. \
                     Use `--target foo` instead of `--target foo.json`."
                )
            } else if triple == meta.host {
                Some(CompilationMode::Native(meta.host.clone()))
            } else {
                Target::new(triple, &cd, verbose)?.map(
                    CompilationMode::Cross,
                )
            }
        } else {
            if let Some(ref config) = config {
                if let Some(triple) = config.target()? {
                    Target::new(triple, &cd, verbose)?.map(
                        CompilationMode::Cross,
                    )
                } else {
                    Some(CompilationMode::Native(meta.host.clone()))
                }
            } else {
                Some(CompilationMode::Native(meta.host.clone()))
            }
        };

        if let Some(cmode) = cmode {
            let home = xargo::home(&cmode)?;
            let rustflags = cargo::rustflags(config.as_ref(), cmode.triple())?;

            sysroot::update(
                &cmode,
                &home,
                &root,
                &rustflags,
                &meta,
                &src,
                &sysroot,
                verbose,
            )?;
            return xargo::run(
                &args,
                &cmode,
                rustflags,
                &home,
                &meta,
                config.as_ref(),
                verbose,
            );
        }
    }

    cargo::run(&args, verbose)
}

pub struct CurrentDirectory {
    path: PathBuf,
}

impl CurrentDirectory {
    fn get() -> Result<CurrentDirectory> {
        env::current_dir()
            .chain_err(|| "couldn't get the current directory")
            .map(|cd| CurrentDirectory { path: cd })
    }

    fn path(&self) -> &Path {
        &self.path
    }
}
<commit_msg>print a warning when stable or beta is used<commit_after>#![deny(warnings)]

#[macro_use]
extern crate error_chain;
extern crate fs2;
#[cfg(any(all(target_os = "linux", not(target_env = "musl")),
          target_os = "macos"))]
extern crate libc;
extern crate rustc_version;
extern crate serde_json;
extern crate tempdir;
extern crate toml;
extern crate walkdir;

use std::hash::{Hash, Hasher};
use std::io::Write;
use std::path::{Path, PathBuf};
use std::process::ExitStatus;
use std::{env, io, process};

use rustc_version::Channel;

use errors::*;
use rustc::Target;

mod cargo;
mod cli;
mod errors;
mod extensions;
mod flock;
mod rustc;
mod sysroot;
mod util;
mod xargo;

// We use a different sysroot for Native compilation to avoid file locking
//
// Cross compilation requires `lib/rustlib/$HOST` to match `rustc`'s sysroot,
// whereas Native compilation wants to use a custom `lib/rustlib/$HOST`. If each
// mode has its own sysroot then we avoid sharing that directory and thus file
// locking it.
pub enum CompilationMode {
    Cross(Target),
    Native(String),
}

impl CompilationMode {
    fn hash<H>(&self, hasher: &mut H) -> Result<()>
    where
        H: Hasher,
    {
        match *self {
            CompilationMode::Cross(ref target) => target.hash(hasher)?,
            CompilationMode::Native(ref triple) => triple.hash(hasher),
        }

        Ok(())
    }

    fn triple(&self) -> &str {
        match *self {
            CompilationMode::Cross(ref target) => target.triple(),
            CompilationMode::Native(ref triple) => triple,
        }
    }

    fn is_native(&self) -> bool {
        match *self {
            CompilationMode::Native(_) => true,
            _ => false,
        }
    }
}

pub fn main() {
    fn show_backtrace() -> bool {
        env::var("RUST_BACKTRACE").as_ref().map(|s| &s[..]) == Ok("1")
    }

    match run() {
        Err(e) => {
            let stderr = io::stderr();
            let mut stderr = stderr.lock();

            writeln!(stderr, "error: {}", e).ok();

            for e in e.iter().skip(1) {
                writeln!(stderr, "caused by: {}", e).ok();
            }

            if show_backtrace() {
                if let Some(backtrace) = e.backtrace() {
                    writeln!(stderr, "{:?}", backtrace).ok();
                }
            } else {
                writeln!(
                    stderr,
                    "note: run with `RUST_BACKTRACE=1` for a backtrace"
                )
                    .ok();
            }

            process::exit(1)
        }
        Ok(status) => {
            if !status.success() {
                process::exit(status.code().unwrap_or(1))
            }
        }
    }
}

fn run() -> Result<ExitStatus> {
    let args = cli::args();
    let verbose = args.verbose();

    let meta = rustc::version();

    if let Some(sc) = args.subcommand() {
        if !sc.needs_sysroot() {
            return cargo::run(&args, verbose);
        }
    } else if args.version() {
        writeln!(
            io::stderr(),
            concat!("xargo ", env!("CARGO_PKG_VERSION"), "{}"),
            include_str!(concat!(env!("OUT_DIR"), "/commit-info.txt"))
        )
            .ok();

        return cargo::run(&args, verbose);
    }

    let cd = CurrentDirectory::get()?;

    let config = cargo::config()?;
    if let Some(root) = cargo::root()? {
        // We can't build sysroot with stable or beta due to unstable features
        let sysroot = rustc::sysroot(verbose)?;
        let src = match meta.channel {
            Channel::Dev => rustc::Src::from_env().ok_or(
                "The XARGO_RUST_SRC env variable must be set and point to the \
                 Rust source directory when working with the 'dev' channel")?,
            Channel::Nightly => {
                if let Some(src) = rustc::Src::from_env() {
                    src
                } else {
                    sysroot.src()?
                }
            }
            Channel::Stable | Channel::Beta => {
                writeln!(
                    io::stderr(),
                    "WARNING: the sysroot can't be built for the {:?} channel. \
                     Switch to nightly.",
                    meta.channel).ok();
                return cargo::run(&args, verbose)
            }
        };

        let cmode = if let Some(triple) = args.target() {
            if Path::new(triple).is_file() {
                bail!(
                    "Xargo doesn't support files as an argument to --target. \
                     Use `--target foo` instead of `--target foo.json`."
                )
            } else if triple == meta.host {
                Some(CompilationMode::Native(meta.host.clone()))
            } else {
                Target::new(triple, &cd, verbose)?.map(
                    CompilationMode::Cross,
                )
            }
        } else {
            if let Some(ref config) = config {
                if let Some(triple) = config.target()? {
                    Target::new(triple, &cd, verbose)?.map(
                        CompilationMode::Cross,
                    )
                } else {
                    Some(CompilationMode::Native(meta.host.clone()))
                }
            } else {
                Some(CompilationMode::Native(meta.host.clone()))
            }
        };

        if let Some(cmode) = cmode {
            let home = xargo::home(&cmode)?;
            let rustflags = cargo::rustflags(config.as_ref(), cmode.triple())?;

            sysroot::update(
                &cmode,
                &home,
                &root,
                &rustflags,
                &meta,
                &src,
                &sysroot,
                verbose,
            )?;
            return xargo::run(
                &args,
                &cmode,
                rustflags,
                &home,
                &meta,
                config.as_ref(),
                verbose,
            );
        }
    }

    cargo::run(&args, verbose)
}

pub struct CurrentDirectory {
    path: PathBuf,
}

impl CurrentDirectory {
    fn get() -> Result<CurrentDirectory> {
        env::current_dir()
            .chain_err(|| "couldn't get the current directory")
            .map(|cd| CurrentDirectory { path: cd })
    }

    fn path(&self) -> &Path {
        &self.path
    }
}
<|endoftext|>


<commit_before>extern crate regex;

#[derive(Debug, PartialEq)]
enum Atom {
    Integer(i64),
    Float(f64),
    Symbol(String),
    Nil
}

use regex::Regex;

fn tokenize(line: &str) -> Node {
    let r = Regex::new(r"\s+").unwrap();
    let l = line.replace("(", " ( ").replace(")", " ) ").trim().to_string();
    let iter = r.split(&l);
    read_tokens(&mut iter.peekable())
}

fn atom(token: &str) -> Atom {
    let i = token.parse::<i64>();
     match i {
         Ok(v) => Atom::Integer(v),
         _ => {
             let f = token.parse::<f64>();
             match f {
                 Ok(x) => Atom::Float(x),
                 _ => Atom::Symbol(token.to_string())
             }
         },
     }
}

#[test]
fn atom_test() {
    assert_eq!(Atom::Integer(32), atom("32"));
    assert_eq!(Atom::Symbol("symbol".to_string()), atom("symbol"));
}
#[derive(Debug,PartialEq)]
struct Node {
    atom: Atom,
    children: Vec<Node>
}

fn make_atom_node(s: &str) -> Node {
    return Node{ atom: atom(s), children: vec![]};
}

fn read_tokens(iter: &mut std::iter::Peekable<regex::RegexSplits>) -> Node {
    let mut node = Node{ atom: Atom::Nil, children: vec![] };

    match iter.next() {
        Some("(") => {
            loop {
                match iter.peek() {
                    Some(&")") => break,
                    Some(_) => {},
                    None => panic!("unexpected EOF"),
                }
                node.children.push(read_tokens(iter));
            }
        },
        Some(")") => panic!("mismatched )"),
        Some(x) => {
            node.atom = atom(x);
        },
        None => panic!("unexpected EOF")
    }
    return node;
}

#[test]
fn simple_read_tokens() {
    let x = parse("(+ 1 2)");

    assert_eq!(Atom::Nil, x.atom);
    assert_eq!(3, x.children.len());

    let xx : Vec<Node> = vec![make_atom_node("+"), make_atom_node("1"), make_atom_node("2")];

    for pair in xx.iter().zip(x.children.iter()) {
        assert_eq!(pair.0, pair.1);
    }
}

#[test]
fn nested_read_tokens() {
    let x = parse("(+ 1 (* 2 2))");

    assert_eq!( atom("+"), x.children[0].atom);
    assert_eq!( atom("1"), x.children[1].atom);
    assert_eq!( Atom::Nil, x.children[2].atom);

    assert_eq!( 3, x.children[2].children.len());
    assert_eq!( atom("*"), x.children[2].children[0].atom);
    assert_eq!( atom("2"), x.children[2].children[1].atom);
    assert_eq!( atom("2"), x.children[2].children[2].atom);
}

#[test]
#[should_panic]
fn unmatched_bracket() {
    let x = parse("(+ 1");
}

#[test]
#[should_panic]
fn eof() {
    let x = parse("");
}

fn eval(node: &Node) -> Atom {
    return Atom::Nil;
}

fn parse(line: &str) -> Node {
    tokenize(line)
}

pub fn main() {
    let x = parse("(+ 1 (* 3 2))");

    println!("x has {} many children", x.children.len());
}
<commit_msg>a repl<commit_after>extern crate regex;

use regex::Regex;
use std::collections::HashMap;
use std::io::prelude::*;
use std::io::stdin;
use std::iter::Iterator;
use std::string::String;

#[derive(Debug, PartialEq)]
enum Atom {
    Integer(i64),
    Float(f64),
    Symbol(String),
    Nil
}

fn default_env() -> HashMap<String,String> {
    let mut env = HashMap::new();

    env.insert("FOO".to_string(), "BAR".to_string());

    return env;
}

fn tokenize(line: &str) -> Node {
    let r = Regex::new(r"\s+").unwrap();
    let l = line.replace("(", " ( ").replace(")", " ) ").trim().to_string();
    let iter = r.split(&l);
    read_tokens(&mut iter.peekable())
}

fn atom(token: &str) -> Atom {
    let i = token.parse::<i64>();
     match i {
         Ok(v) => Atom::Integer(v),
         _ => {
             let f = token.parse::<f64>();
             match f {
                 Ok(x) => Atom::Float(x),
                 _ => Atom::Symbol(token.to_string())
             }
         },
     }
}

#[test]
fn atom_test() {
    assert_eq!(Atom::Integer(32), atom("32"));
    assert_eq!(Atom::Symbol("symbol".to_string()), atom("symbol"));
}

#[derive(Debug,PartialEq)]
struct Node {
    atom: Atom,
    children: Vec<Node>
}

fn make_atom_node(s: &str) -> Node {
    return Node{ atom: atom(s), children: vec![]};
}

fn read_tokens(iter: &mut std::iter::Peekable<regex::RegexSplits>) -> Node {
    let mut node = Node{ atom: Atom::Nil, children: vec![] };

    match iter.next() {
        Some("(") => {
            loop {
                match iter.peek() {
                    Some(&")") => break,
                    Some(_) => {},
                    None => panic!("unexpected EOF"),
                }
                node.children.push(read_tokens(iter));
            }
        },
        Some(")") => panic!("mismatched )"),
        Some(x) => {
            node.atom = atom(x);
        },
        None => panic!("unexpected EOF")
    }
    return node;
}

#[test]
fn simple_read_tokens() {
    let x = parse("(+ 1 2)");

    assert_eq!(Atom::Nil, x.atom);
    assert_eq!(3, x.children.len());

    let xx : Vec<Node> = vec![make_atom_node("+"), make_atom_node("1"), make_atom_node("2")];

    for pair in xx.iter().zip(x.children.iter()) {
        assert_eq!(pair.0, pair.1);
    }
}

#[test]
fn nested_read_tokens() {
    let x = parse("(+ 1 (* 2 2))");

    assert_eq!( atom("+"), x.children[0].atom);
    assert_eq!( atom("1"), x.children[1].atom);
    assert_eq!( Atom::Nil, x.children[2].atom);

    assert_eq!( 3, x.children[2].children.len());
    assert_eq!( atom("*"), x.children[2].children[0].atom);
    assert_eq!( atom("2"), x.children[2].children[1].atom);
    assert_eq!( atom("2"), x.children[2].children[2].atom);
}

#[test]
#[should_panic]
fn unmatched_bracket() {
    let x = parse("(+ 1");
}

#[test]
#[should_panic]
fn eof() {
    let x = parse("");
}

fn add(v: Vec<Atom>) -> Atom {
    let mut result = 0;

    for i in v {
        match i {
            Atom::Integer(d) => result += d,
            _ => panic!("DfD"),
        }
    }

    return Atom::Integer(result);
}

fn eval(node: &Node, env: &mut HashMap<String,String>) -> Atom {
    match node.atom {
        Atom::Nil => {
            let args = node.children.iter()
                .skip(1)
                .map(|node| eval(node, env)).collect();
            return add(args);
        },
        Atom::Symbol(ref v) => {
            // string
        },
        Atom::Integer(v) => {
            return Atom::Integer(v);
        },
        _ => unreachable!(),
    }
    return Atom::Nil;
}

fn parse(line: &str) -> Node {
    tokenize(line)
}

fn flush() {
    let out = std::io::stdout();
    let mut o = out.lock();
    o.flush().unwrap();
}

fn repl() {
    println!("Rust Lisp!");
    let mut input = stdin();
    let mut line = String::new();
    let mut env = default_env();
    loop {
        print!(">");
        flush();

        match input.read_line(&mut line) {
            Ok(size) => {
                if size == 0 {
                    break;
                } else {
                    let r = eval(&parse(&line), &mut env);
                    println!("{:?}", r);
                }
            },
            Err(e) => {
                println!("Error: {:?}", e);
                break;
            }
        }
    }
}

pub fn main() {
    repl();
}
<|endoftext|>


<commit_before>#![cfg_attr(feature = "dev", allow(unstable_features))]
#![cfg_attr(feature = "dev", feature(plugin))]
#![cfg_attr(feature = "dev", plugin(clippy))]

mod logger;
mod toml_file;
mod git;
mod changelog;
mod commit_analyzer;
mod cargo;
mod error;
mod github;
mod config;
mod utils;

extern crate rustc_serialize;
extern crate toml;
extern crate regex;
extern crate semver;
extern crate docopt;
extern crate git2;
extern crate clog;
extern crate hyper;
extern crate hubcaps;
extern crate url;
extern crate travis_after_all;
extern crate env_logger;

use docopt::Docopt;
use commit_analyzer::CommitType;
use config::ConfigBuilder;
use std::process;
use semver::Version;
use std::{env,fs};
use std::path::Path;
use std::error::Error;
use std::thread;
use std::time::Duration;
use travis_after_all::Build;
use utils::user_repo_from_url;

const VERSION: &'static str = env!("CARGO_PKG_VERSION");
const USERAGENT: &'static str = concat!("semantic-rs/", env!("CARGO_PKG_VERSION"));
const USAGE: &'static str = "
semantic.rs 🚀

Usage:
  semantic-rs [options]
  semantic-rs --version

Options:
  -h --help              Show this screen.
  --version              Show version.
  -p PATH, --path=PATH   Specifies the repository path. [default: .]
  -w <w>, --write=<w>    Write changes to files (default: yes if CI is set, otherwise no).
  -r <r>, --release=<r>  Create release on GitHub and publish on crates.io (only in write mode) [default: yes].
  -b <b>, --branch=<b>   The branch on which releases should happen. [default: master].
";

macro_rules! print_exit {
    ($fmt:expr) => {{
        logger::stderr($fmt);
        process::exit(1);
    }};
    ($fmt:expr, $($arg:tt)*) => {{
        logger::stderr(format!($fmt, $($arg)*));
        process::exit(1);
    }};
}

#[derive(Debug, RustcDecodable)]
struct Args {
    flag_path: String,
    flag_write: Option<String>,
    flag_version: bool,
    flag_release: String,
    flag_branch: String,
}

fn string_to_bool(answer: &str) -> bool {
    match &answer.to_lowercase()[..] {
        "yes" | "true" | "1" => true,
        _ => false
    }
}

fn version_bump(version: &Version, bump: CommitType) -> Option<Version> {
    let mut version = version.clone();
    match bump {
        CommitType::Unknown => return None,
        CommitType::Patch => version.increment_patch(),
        CommitType::Minor => version.increment_minor(),
        CommitType::Major => version.increment_major(),
    }

    Some(version)
}

fn ci_env_set() -> bool {
    env::var("CI").is_ok()
}

fn current_branch(repo: &git2::Repository) -> Option<String> {
    if let Ok(branch) = env::var("TRAVIS_BRANCH") {
        return Some(branch)
    }

    let head = repo.head().expect("No HEAD found for repository");

    if head.is_branch() {
        let short = head.shorthand().expect("No branch name found");
        return Some(short.into());
    }

    None
}

fn is_release_branch(current: &str, release: &str) -> bool {
    if let Ok(pr) = env::var("TRAVIS_PULL_REQUEST") {
        if pr != "false" {
            return false;
        }
    }

    current == release
}

fn main() {
    env_logger::init().expect("Can't instantiate env logger");

    let args: Args = Docopt::new(USAGE)
        .and_then(|d| d.decode())
        .unwrap_or_else(|e| e.exit());

    let mut config_builder = ConfigBuilder::new();

    if args.flag_version {
        println!("semantic.rs 🚀 -- v{}", VERSION);
        process::exit(0);
    }

    // If write mode is requested OR denied,
    // adhere to the user's wish,
    // otherwise we decide based on whether we are running in CI.
    let write_mode = match args.flag_write {
        None => ci_env_set(),
        Some(ref flag) => string_to_bool(flag)
    };

    // We can only release, if we are allowed to write
    let release_mode = write_mode && string_to_bool(&args.flag_release);

    config_builder.write(write_mode);
    config_builder.release(release_mode);
    config_builder.branch(args.flag_branch);

    println!("semantic.rs 🚀");

    logger::stdout("Analyzing your repository");
    let path = Path::new(&args.flag_path);
    let path = fs::canonicalize(path)
        .unwrap_or_else(|_| print_exit!("Path does not exist or a component is not a directory"));
    let repository_path = path.to_str()
        .unwrap_or_else(|| print_exit!("Path is not valid unicode"));

    let repo = match git2::Repository::open(repository_path) {
        Ok(repo) => repo,
        Err(e) => {
            logger::stderr(format!("Could not open the git repository: {:?}", e));
            process::exit(1);
        }
    };

    config_builder.repository_path(repository_path.to_owned());

    // extra scope scope to make sure borrow of `repo` is dropped
    {
        let signature = match git::get_signature(&repo) {
            Ok(sig) => sig,
            Err(e) => {
                logger::stderr(format!("Failed to get the committer's name and email address: {}", e.description()));
                logger::stderr(r"
A release commit needs a committer name and email address.
We tried fetching it from different locations, but couldn't find one.

Committer information is taken from the following environment variables, if set:

GIT_COMMITTER_NAME
GIT_COMMITTER_EMAIL

If none is set the normal git config is tried in the following order:

Local repository config
User config
Global config");
                process::exit(1);
            }
        };

        config_builder.signature(signature.to_owned());
    }

    // In case we are in write-mode AND release mode,
    // we will make sure we got all configuration settings
    if write_mode && release_mode {
        let remote_or_none = repo.find_remote("origin");
        match remote_or_none {
            Ok(remote) => {
                let url = remote.url().expect("Remote URL is not valid UTF-8").to_owned();
                let (user, repo_name) = user_repo_from_url(&url)
                    .unwrap_or_else(|e| print_exit!("Could not extract user and repository name from URL: {:?}", e));
                config_builder.user(user);
                config_builder.repository_name(repo_name);

                let gh_token = env::var("GH_TOKEN")
                    .unwrap_or_else(|err| print_exit!("GH_TOKEN not set: {:?}", err));

                let cargo_token = env::var("CARGO_TOKEN")
                    .unwrap_or_else(|err| print_exit!("CARGO_TOKEN not set: {:?}", err));

                config_builder.gh_token(gh_token);
                config_builder.cargo_token(cargo_token);
            },
            Err(err) => {
                logger::warn(format!("Could not determine the origin remote url: {:?}", err));
                logger::warn("semantic-rs can't push changes or create a release on GitHub");
            }
        }
    }

    config_builder.repository(repo);
    let config = config_builder.build();

    let branch = current_branch(&config.repository)
        .unwrap_or_else(|| print_exit!("Could not determine current branch."));

    if !is_release_branch(&branch, &config.branch) {
        println!("Current branch is '{}', releases are only done from branch '{}'", branch, config.branch);
        println!("No release done from a pull request either.");
        process::exit(0);
    }

    if config.release_mode && ci_env_set() {
        let build_run = Build::from_env()
            .unwrap_or_else(|e| print_exit!("CI mode, but can't check other builds. Error: {:?}", e));

        if !build_run.is_leader() {
            println!("Not the build leader. Nothing to do. Bye.");
            process::exit(0);
        }

        println!("I am the build leader. Waiting for other jobs to finish.");
        match build_run.wait_for_others() {
            Ok(()) => println!("Other jobs finished and succeeded. Doing my work now."),
            Err(travis_after_all::Error::FailedBuilds) => {
                print_exit!("Some builds failed. Stopping here.");
            },
            Err(e) => print_exit!("Waiting for other builds failed Reason: {:?}", e),
        }
    }

    let version = toml_file::read_from_file(&config.repository_path)
        .unwrap_or_else(|err| print_exit!("Reading `Cargo.toml` failed: {:?}", err));

    let version = Version::parse(&version).expect("Not a valid version");
    logger::stdout(format!("Current version: {}", version.to_string()));

    logger::stdout("Analyzing commits");

    let bump = git::version_bump_since_latest(&config.repository);
    if write_mode {
        logger::stdout(format!("Commits analyzed. Bump will be {:?}", bump));
    } else {
        logger::stdout(format!("Commits analyzed. Bump would be {:?}", bump));
    }
    let new_version = match version_bump(&version, bump) {
        Some(new_version) => new_version.to_string(),
        None => {
            logger::stdout("No version bump. Nothing to do.");
            process::exit(0);
        }
    };

    if !config.write_mode {
        logger::stdout(format!("New version would be: {}", new_version));
        logger::stdout("Would write the following Changelog:");
        let changelog = match changelog::generate(repository_path, &version.to_string(), &new_version) {
            Ok(log) => log,
            Err(err) => {
                logger::stderr(format!("Generating Changelog failed: {:?}", err));
                process::exit(1);
            }
        };
        logger::stdout("====================================");
        logger::stdout(changelog);
        logger::stdout("====================================");
        logger::stdout("Would create annotated git tag");
    } else {
        logger::stdout(format!("New version: {}", new_version));

        toml_file::write_new_version(repository_path, &new_version)
            .unwrap_or_else(|err| print_exit!("Writing `Cargo.toml` failed: {:?}", err));

        logger::stdout("Writing Changelog");
        changelog::write(repository_path, &version.to_string(), &new_version)
            .unwrap_or_else(|err| print_exit!("Writing Changelog failed: {:?}", err));

        if config.release_mode {
            logger::stdout("Updating lockfile");
            if !cargo::update_lockfile(repository_path) {
                print_exit!("`cargo fetch` failed. See above for the cargo error message.");
            }
        }

        logger::stdout("Package crate");
        if !cargo::package(repository_path) {
            print_exit!("`cargo package` failed. See above for the cargo error message.");
        }

        git::commit_files(&config, &new_version)
            .unwrap_or_else(|err| print_exit!("Committing files failed: {:?}", err));

        logger::stdout("Creating annotated git tag");
        let tag_message = changelog::generate(repository_path, &version.to_string(), &new_version)
            .unwrap_or_else(|err| print_exit!("Can't generate changelog: {:?}", err));

        let tag_name = format!("v{}", new_version);
        git::tag(&config, &tag_name, &tag_message)
            .unwrap_or_else(|err| print_exit!("Failed to create git tag: {:?}", err));

        if config.release_mode && config.can_push() {
            logger::stdout("Pushing new commit and tag");
            git::push(&config, &tag_name)
                .unwrap_or_else(|err| print_exit!("Failed to push git: {:?}", err));

            logger::stdout("Waiting a tiny bit, so GitHub can store the git tag");
            thread::sleep(Duration::from_secs(1));

            logger::stdout("Creating GitHub release");
            github::release(&config, &tag_name, &tag_message)
                .unwrap_or_else(|err| print_exit!("Failed to create GitHub release: {:?}", err));

            logger::stdout("Publishing crate on crates.io");
            if !cargo::publish(&config.repository_path, &config.cargo_token.as_ref().unwrap()) {
                print_exit!("Failed to publish on crates.io");
            }

            println!("{} v{} is released. 🚀🚀🚀", config.repository_name.unwrap(), new_version);
        }
    }
}
<commit_msg>fix: Make help output consistent<commit_after>#![cfg_attr(feature = "dev", allow(unstable_features))]
#![cfg_attr(feature = "dev", feature(plugin))]
#![cfg_attr(feature = "dev", plugin(clippy))]

mod logger;
mod toml_file;
mod git;
mod changelog;
mod commit_analyzer;
mod cargo;
mod error;
mod github;
mod config;
mod utils;

extern crate rustc_serialize;
extern crate toml;
extern crate regex;
extern crate semver;
extern crate docopt;
extern crate git2;
extern crate clog;
extern crate hyper;
extern crate hubcaps;
extern crate url;
extern crate travis_after_all;
extern crate env_logger;

use docopt::Docopt;
use commit_analyzer::CommitType;
use config::ConfigBuilder;
use std::process;
use semver::Version;
use std::{env,fs};
use std::path::Path;
use std::error::Error;
use std::thread;
use std::time::Duration;
use travis_after_all::Build;
use utils::user_repo_from_url;

const VERSION: &'static str = env!("CARGO_PKG_VERSION");
const USERAGENT: &'static str = concat!("semantic-rs/", env!("CARGO_PKG_VERSION"));
const USAGE: &'static str = "
semantic.rs 🚀

Usage:
  semantic-rs [options]
  semantic-rs --version

Options:
  -h --help              Show this screen.
  --version              Show version.
  -p PATH, --path=PATH   Specifies the repository path. [default: .]
  -w W, --write=W        Write changes to files (default: yes if CI is set, otherwise no).
  -r R, --release=R      Create release on GitHub and publish on crates.io (only in write mode) [default: yes].
  -b B, --branch=B       The branch on which releases should happen. [default: master].
";

macro_rules! print_exit {
    ($fmt:expr) => {{
        logger::stderr($fmt);
        process::exit(1);
    }};
    ($fmt:expr, $($arg:tt)*) => {{
        logger::stderr(format!($fmt, $($arg)*));
        process::exit(1);
    }};
}

#[derive(Debug, RustcDecodable)]
struct Args {
    flag_path: String,
    flag_write: Option<String>,
    flag_version: bool,
    flag_release: String,
    flag_branch: String,
}

fn string_to_bool(answer: &str) -> bool {
    match &answer.to_lowercase()[..] {
        "yes" | "true" | "1" => true,
        _ => false
    }
}

fn version_bump(version: &Version, bump: CommitType) -> Option<Version> {
    let mut version = version.clone();
    match bump {
        CommitType::Unknown => return None,
        CommitType::Patch => version.increment_patch(),
        CommitType::Minor => version.increment_minor(),
        CommitType::Major => version.increment_major(),
    }

    Some(version)
}

fn ci_env_set() -> bool {
    env::var("CI").is_ok()
}

fn current_branch(repo: &git2::Repository) -> Option<String> {
    if let Ok(branch) = env::var("TRAVIS_BRANCH") {
        return Some(branch)
    }

    let head = repo.head().expect("No HEAD found for repository");

    if head.is_branch() {
        let short = head.shorthand().expect("No branch name found");
        return Some(short.into());
    }

    None
}

fn is_release_branch(current: &str, release: &str) -> bool {
    if let Ok(pr) = env::var("TRAVIS_PULL_REQUEST") {
        if pr != "false" {
            return false;
        }
    }

    current == release
}

fn main() {
    env_logger::init().expect("Can't instantiate env logger");

    let args: Args = Docopt::new(USAGE)
        .and_then(|d| d.decode())
        .unwrap_or_else(|e| e.exit());

    let mut config_builder = ConfigBuilder::new();

    if args.flag_version {
        println!("semantic.rs 🚀 -- v{}", VERSION);
        process::exit(0);
    }

    // If write mode is requested OR denied,
    // adhere to the user's wish,
    // otherwise we decide based on whether we are running in CI.
    let write_mode = match args.flag_write {
        None => ci_env_set(),
        Some(ref flag) => string_to_bool(flag)
    };

    // We can only release, if we are allowed to write
    let release_mode = write_mode && string_to_bool(&args.flag_release);

    config_builder.write(write_mode);
    config_builder.release(release_mode);
    config_builder.branch(args.flag_branch);

    println!("semantic.rs 🚀");

    logger::stdout("Analyzing your repository");
    let path = Path::new(&args.flag_path);
    let path = fs::canonicalize(path)
        .unwrap_or_else(|_| print_exit!("Path does not exist or a component is not a directory"));
    let repository_path = path.to_str()
        .unwrap_or_else(|| print_exit!("Path is not valid unicode"));

    let repo = match git2::Repository::open(repository_path) {
        Ok(repo) => repo,
        Err(e) => {
            logger::stderr(format!("Could not open the git repository: {:?}", e));
            process::exit(1);
        }
    };

    config_builder.repository_path(repository_path.to_owned());

    // extra scope scope to make sure borrow of `repo` is dropped
    {
        let signature = match git::get_signature(&repo) {
            Ok(sig) => sig,
            Err(e) => {
                logger::stderr(format!("Failed to get the committer's name and email address: {}", e.description()));
                logger::stderr(r"
A release commit needs a committer name and email address.
We tried fetching it from different locations, but couldn't find one.

Committer information is taken from the following environment variables, if set:

GIT_COMMITTER_NAME
GIT_COMMITTER_EMAIL

If none is set the normal git config is tried in the following order:

Local repository config
User config
Global config");
                process::exit(1);
            }
        };

        config_builder.signature(signature.to_owned());
    }

    // In case we are in write-mode AND release mode,
    // we will make sure we got all configuration settings
    if write_mode && release_mode {
        let remote_or_none = repo.find_remote("origin");
        match remote_or_none {
            Ok(remote) => {
                let url = remote.url().expect("Remote URL is not valid UTF-8").to_owned();
                let (user, repo_name) = user_repo_from_url(&url)
                    .unwrap_or_else(|e| print_exit!("Could not extract user and repository name from URL: {:?}", e));
                config_builder.user(user);
                config_builder.repository_name(repo_name);

                let gh_token = env::var("GH_TOKEN")
                    .unwrap_or_else(|err| print_exit!("GH_TOKEN not set: {:?}", err));

                let cargo_token = env::var("CARGO_TOKEN")
                    .unwrap_or_else(|err| print_exit!("CARGO_TOKEN not set: {:?}", err));

                config_builder.gh_token(gh_token);
                config_builder.cargo_token(cargo_token);
            },
            Err(err) => {
                logger::warn(format!("Could not determine the origin remote url: {:?}", err));
                logger::warn("semantic-rs can't push changes or create a release on GitHub");
            }
        }
    }

    config_builder.repository(repo);
    let config = config_builder.build();

    let branch = current_branch(&config.repository)
        .unwrap_or_else(|| print_exit!("Could not determine current branch."));

    if !is_release_branch(&branch, &config.branch) {
        println!("Current branch is '{}', releases are only done from branch '{}'", branch, config.branch);
        println!("No release done from a pull request either.");
        process::exit(0);
    }

    if config.release_mode && ci_env_set() {
        let build_run = Build::from_env()
            .unwrap_or_else(|e| print_exit!("CI mode, but can't check other builds. Error: {:?}", e));

        if !build_run.is_leader() {
            println!("Not the build leader. Nothing to do. Bye.");
            process::exit(0);
        }

        println!("I am the build leader. Waiting for other jobs to finish.");
        match build_run.wait_for_others() {
            Ok(()) => println!("Other jobs finished and succeeded. Doing my work now."),
            Err(travis_after_all::Error::FailedBuilds) => {
                print_exit!("Some builds failed. Stopping here.");
            },
            Err(e) => print_exit!("Waiting for other builds failed Reason: {:?}", e),
        }
    }

    let version = toml_file::read_from_file(&config.repository_path)
        .unwrap_or_else(|err| print_exit!("Reading `Cargo.toml` failed: {:?}", err));

    let version = Version::parse(&version).expect("Not a valid version");
    logger::stdout(format!("Current version: {}", version.to_string()));

    logger::stdout("Analyzing commits");

    let bump = git::version_bump_since_latest(&config.repository);
    if write_mode {
        logger::stdout(format!("Commits analyzed. Bump will be {:?}", bump));
    } else {
        logger::stdout(format!("Commits analyzed. Bump would be {:?}", bump));
    }
    let new_version = match version_bump(&version, bump) {
        Some(new_version) => new_version.to_string(),
        None => {
            logger::stdout("No version bump. Nothing to do.");
            process::exit(0);
        }
    };

    if !config.write_mode {
        logger::stdout(format!("New version would be: {}", new_version));
        logger::stdout("Would write the following Changelog:");
        let changelog = match changelog::generate(repository_path, &version.to_string(), &new_version) {
            Ok(log) => log,
            Err(err) => {
                logger::stderr(format!("Generating Changelog failed: {:?}", err));
                process::exit(1);
            }
        };
        logger::stdout("====================================");
        logger::stdout(changelog);
        logger::stdout("====================================");
        logger::stdout("Would create annotated git tag");
    } else {
        logger::stdout(format!("New version: {}", new_version));

        toml_file::write_new_version(repository_path, &new_version)
            .unwrap_or_else(|err| print_exit!("Writing `Cargo.toml` failed: {:?}", err));

        logger::stdout("Writing Changelog");
        changelog::write(repository_path, &version.to_string(), &new_version)
            .unwrap_or_else(|err| print_exit!("Writing Changelog failed: {:?}", err));

        if config.release_mode {
            logger::stdout("Updating lockfile");
            if !cargo::update_lockfile(repository_path) {
                print_exit!("`cargo fetch` failed. See above for the cargo error message.");
            }
        }

        logger::stdout("Package crate");
        if !cargo::package(repository_path) {
            print_exit!("`cargo package` failed. See above for the cargo error message.");
        }

        git::commit_files(&config, &new_version)
            .unwrap_or_else(|err| print_exit!("Committing files failed: {:?}", err));

        logger::stdout("Creating annotated git tag");
        let tag_message = changelog::generate(repository_path, &version.to_string(), &new_version)
            .unwrap_or_else(|err| print_exit!("Can't generate changelog: {:?}", err));

        let tag_name = format!("v{}", new_version);
        git::tag(&config, &tag_name, &tag_message)
            .unwrap_or_else(|err| print_exit!("Failed to create git tag: {:?}", err));

        if config.release_mode && config.can_push() {
            logger::stdout("Pushing new commit and tag");
            git::push(&config, &tag_name)
                .unwrap_or_else(|err| print_exit!("Failed to push git: {:?}", err));

            logger::stdout("Waiting a tiny bit, so GitHub can store the git tag");
            thread::sleep(Duration::from_secs(1));

            logger::stdout("Creating GitHub release");
            github::release(&config, &tag_name, &tag_message)
                .unwrap_or_else(|err| print_exit!("Failed to create GitHub release: {:?}", err));

            logger::stdout("Publishing crate on crates.io");
            if !cargo::publish(&config.repository_path, &config.cargo_token.as_ref().unwrap()) {
                print_exit!("Failed to publish on crates.io");
            }

            println!("{} v{} is released. 🚀🚀🚀", config.repository_name.unwrap(), new_version);
        }
    }
}
<|endoftext|>