<commit_before>#![allow(unused)]
extern crate rz80;
extern crate time;
extern crate minifb;

use std::cell::RefCell;
use minifb::{Window, Key, Scale, WindowOptions};
use rz80::*;

const FB_WIDTH: usize=256;          // Z1013 framebuffer width
const FB_HEIGHT: usize=256;         // Z1013 framebuffer height
const Z1013_MAX_KEYS: usize=128;
const Z1013_FREQ_KHZ: i64=2000;     // frequency (2MHz = 2000 kHz)

const DEV_PIO: usize=0;
const DEV_PIO_A: usize=0;
const DEV_PIO_B: usize=1;
const NUM_DEVS: usize=2;

static Z1013_MON_A2: &'static [u8] = include_bytes!("z1013_mon_a2.bin");
static Z1013_FONT:   &'static [u8] = include_bytes!("z1013_font.bin");

struct Z1013 {
    kbd_column_nr_requested: usize,
    kbd_8x8_requested: bool,
    next_kbd_column_bits: u64,
    kbd_column_bits: u64,
    key_map: [u64; Z1013_MAX_KEYS],
}

impl Z1013 {
    pub fn new() -> Z1013 {
        let mut z1013 = Z1013 {
            kbd_column_nr_requested: 0,
            kbd_8x8_requested: false,
            next_kbd_column_bits: 0,
            kbd_column_bits: 0,
            key_map: [0; Z1013_MAX_KEYS],
        };
        z1013.init_keymap_8x8();
        z1013
    }

    fn kbd_bit(col: usize, line: usize, num_lines: usize) -> u64 {
        (1u64<<line)<<(col*num_lines)
    }

    fn init_key(&mut self, ascii: u8, col: usize, line: usize, shift:usize, num_lines: usize) {
        let mask =
            Z1013::kbd_bit(col, line, num_lines) |
            if shift != 0 {Z1013::kbd_bit(7, 6, num_lines)} else {0};
        self.key_map[ascii as usize] = mask;
    }

    fn init_keymap_8x8(&mut self) {

        // 8x8 keyboard matrix with 2 shift-layers
        let layers_8x8 =
            b"13579-  \
              QETUO@  \
              ADGJL*  \
              YCBM.^  \
              24680[  \
              WRZIP]  \
              SFHK+\\  \
              XVN,/_  \
              !#%')=  \
              qetuo`  \
              adgjl:  \
              ycbm>~  \
              \"$&( {  \
              wrzip}  \
              sfhk;|  \
              xvn<?   ";
        for shift in 0..2 {
            for line in 0..8 {
                for col in 0..8 {
                    let c = layers_8x8[shift*64 + line*8 + col];
                    if 0x20 != c {
                        self.init_key(c, col, line, shift, 8);
                    }
                }
            }
        }

        // special keys
        self.init_key(0x20, 6, 4, 0, 8);    // space
        self.init_key(0x08, 6, 2, 0, 8);    // cursor left
        self.init_key(0x09, 6, 3, 0, 8);    // cursor right
        self.init_key(0x0A, 6, 7, 0, 8);    // cursor down
        self.init_key(0x0B, 6, 6, 0, 8);    // cursor up
        self.init_key(0x0D, 6, 1, 0, 8);    // enter

        // Ctrl+C (== STOP/BREAK)
        self.key_map[0x03] = Z1013::kbd_bit(6, 5, 8) | Z1013::kbd_bit(1, 3, 4);
    }

    pub fn poweron(&mut self, cpu: &mut CPU) {

        // reset the hardware
        self.init_keymap_8x8();
        self.kbd_column_nr_requested = 0;
        self.kbd_8x8_requested = false;
        self.next_kbd_column_bits = 0;
        self.kbd_column_bits = 0;

        // map memory (64 KByte RAM incl. vid-mem, and 2 KByte ROM)
        cpu.mem.unmap_all();
        cpu.mem.map(1, 0x00000, 0x0000, true, 1<<16);
        cpu.mem.map_bytes(0, 0x10000, 0xF000, false, &Z1013_MON_A2);

        // start execution at address 0xF000
        cpu.reg.set_pc(0xF000);
    }

    pub fn reset(&mut self, cpu: &mut CPU) {
        self.kbd_column_nr_requested = 0;
        self.next_kbd_column_bits = 0;
        self.kbd_column_bits = 0;
        cpu.reg.set_pc(0xF000);
    }

    pub fn put_key(&mut self, ascii: u8) {
        let key_index = (ascii as usize) & (Z1013_MAX_KEYS-1);
        match ascii {
            0 => {
                self.next_kbd_column_bits = 0;
            },
            _ => {
                self.next_kbd_column_bits = self.key_map[key_index];
            }
        }
    }
}

struct System {
    pub cpu: RefCell<CPU>,
    pub pio: RefCell<PIO>,
    pub daisy: RefCell<Daisychain>,
    pub z1013: RefCell<Z1013>,
}

impl Bus for System {

    fn cpu_outp(&self, port: RegT, val: RegT) {
        match port & 0xFF {
            // PIO-A data
            0x00 => {
                // println!("PIO-A write data {:x}", val);
                self.pio.borrow_mut().write_data(self, PIO_A, val);
            },
            // PIO-A, control
            0x01 => {
                // println!("PIO-A write control {:x}", val);
                self.pio.borrow_mut().write_control(PIO_A, val);
            },
            // PIO-B, data
            0x02 => {
                // println!("PIO-B write data {:x}", val);
                self.pio.borrow_mut().write_data(self, PIO_B, val);
            },
            // PIO-B, control
            0x03 => {
                // println!("PIO-B write control {:x}", val);
                self.pio.borrow_mut().write_control(PIO_B, val);
            },
            // keyboard column
            0x08 => {
                // println!("keyboard column {:x}", val);
                let mut z1013 = self.z1013.borrow_mut();
                if val == 0 {
                    z1013.kbd_column_bits = z1013.next_kbd_column_bits;
                }
                z1013.kbd_column_nr_requested = val as usize;
            },
            _ => {
                println!("CPU OUT: {:x}", val);
            }
        }
    }

    fn cpu_inp(&self, port: RegT) -> RegT {
        match port & 0xFF {
            // PIO-A data
            0x00 => {
                // println!("PIO-A read data");
                self.pio.borrow_mut().read_data(self, PIO_A)
            },
            // PIO-A control
            0x01 => {
                // println!("PIO-A read control");
                self.pio.borrow_mut().read_control()
            },
            // PIO-B read data
            0x02 => {
                // println!("PIO-B read data");
                self.pio.borrow_mut().read_data(self, PIO_B)
            },
            // PIO-B read control
            0x03 => {
                // println!("PIO-B read control");
                self.pio.borrow_mut().read_control()
            },
            _ => {
                println!("CPU IN: {:x}", port);
                0xFF
            }
        }
    }

    fn pio_outp(&self, pio: usize, chn: usize, data: RegT) {
        if chn == PIO_B {
            // println!("PIO-B out {:x}", data);
            let mut z1013 = self.z1013.borrow_mut();
            z1013.kbd_8x8_requested = 0 != (data & (1<<4));
        }
        else {
            println!("PIO-A output (unused)");
        }
    }

    fn pio_inp(&self, pio: usize, chn: usize) -> RegT {
        if chn == PIO_B {
            // println!("PIO-B in");
            let z1013 = self.z1013.borrow();
            let col = z1013.kbd_column_nr_requested & 7;
            let mut val = z1013.kbd_column_bits >> (col*8);
            if z1013.kbd_8x8_requested {
                val >>= 4;
            }
            val = 0xF & !(val & 0xF);
            val as RegT
        }
        else {
            println!("PIO-A input (unused)");
            0xFF
        }
    }

    fn irq(&self, ctrl_id: usize, vec: u8) {
        println!("IRQ");
    }
    fn irq_cpu(&self) {
        println!("IRQ CPU");
    }

    fn irq_ack(&self) -> RegT {
        println!("IRQ ACK");
        0
    }
    fn irq_reti(&self) {
        println!("IRQ RETI");
    }

}

impl System {
    pub fn new() -> System {
        System {
            cpu: RefCell::new(CPU::new()),
            pio: RefCell::new(PIO::new(DEV_PIO)),
            daisy: RefCell::new(Daisychain::new(NUM_DEVS)),
            z1013: RefCell::new(Z1013::new()),
        }
    }

    pub fn poweron(&self) {
        let mut z1013 = self.z1013.borrow_mut();
        let mut cpu = self.cpu.borrow_mut();
        z1013.poweron(&mut cpu);
    }

    pub fn reset(&self) {
        let mut cpu = self.cpu.borrow_mut();
        let mut pio = self.pio.borrow_mut();
        let mut daisy = self.daisy.borrow_mut();
        let mut z1013 = self.z1013.borrow_mut();
        cpu.reset();
        pio.reset();
        daisy.reset();
        z1013.reset(&mut cpu);
    }

    pub fn step(&self, micro_seconds: i64) {
        let num_cycles = (Z1013_FREQ_KHZ * micro_seconds) / 1000;
        let mut cur_cycles = 0;
        let mut cpu = self.cpu.borrow_mut();
        while cur_cycles < num_cycles {
            cur_cycles += cpu.step(self);
        }
    }

    pub fn decode_video(&self, fb: &mut [u32]) {
        let mut i: usize = 0;
        let cpu = self.cpu.borrow();
        let vid_mem = &cpu.mem.heap[0xEC00..0xF000];
        for y in 0..32 {
            for py in 0..8 {
                for x in 0..32 {
                    let chr = vid_mem[(y<<5)+x] as usize;
                    let bits = Z1013_FONT[(chr<<3)|py];
                    for px in 0..8 {
                        fb[i] = if (bits & (1<<(7-px))) != 0 {0xFFFFFFFF} else {0xFF000000};
                        i += 1;
                    }
                }
            }
        }
    }

    pub fn put_key(&mut self, ascii: u8) {
        let mut z1013 = self.z1013.borrow_mut();
        z1013.put_key(ascii);
    }
}

fn create_window() -> Window {
    match Window::new("rz80 Z1013 Example",
               FB_WIDTH, FB_HEIGHT,
               WindowOptions {
                   resize: false,
                   scale: Scale::X2,
                   ..WindowOptions::default()
               }) {
        Ok(win) => win,
        Err(err) => panic!("Unable to create minifb window: {}", err)
    }
}

fn main() {
    let mut system = System::new();
    let mut window = create_window();
    let mut frame_buffer = [0u32; FB_WIDTH*FB_HEIGHT];
    let micro_seconds_per_frame: i64 = 1000000 / 60;
    system.poweron();
    while window.is_open() && !window.is_key_down(Key::Escape) {
        if window.is_key_down(Key::A) {
            println!("KEY!");
            system.put_key(b'A');
        }
        else {
            system.put_key(0);
        }
        system.step(micro_seconds_per_frame);
        system.decode_video(&mut frame_buffer);
        window.update_with_buffer(&frame_buffer);
    }
}

<commit_msg>Z1013 example: rough keyboard input working<commit_after>extern crate rz80;
extern crate time;
extern crate minifb;

use rz80::{CPU,PIO,Bus,RegT,PIO_A,PIO_B};
use minifb::{Key, Window, Scale, WindowOptions};
use std::cell::RefCell;

const FB_WIDTH: usize=256;          // Z1013 framebuffer width
const FB_HEIGHT: usize=256;         // Z1013 framebuffer height
const Z1013_MAX_KEYS: usize=128;
const Z1013_FREQ_KHZ: i64=2000;     // frequency (2MHz = 2000 kHz)

// binary dump of the Z1013 operatin system ROM and font data
static Z1013_MON_A2: &'static [u8] = include_bytes!("z1013_mon_a2.bin");
static Z1013_FONT:   &'static [u8] = include_bytes!("z1013_font.bin");

// a mapping of all accepted minifb keys to their ASCII values (with and
// without shift key pressed), this only works for english keyboard layouts
static KEYS: &'static [(Key,u8,u8)] = &[
    (Key::Key0,b'0',b')'), (Key::Key1,b'1',b'!'), (Key::Key2,b'2',b'@'), (Key::Key3,b'3',b'#'),
    (Key::Key4,b'4',b'$'), (Key::Key5,b'5',b'%'), (Key::Key6,b'6',b'^'), (Key::Key7,b'7',b'&'),
    (Key::Key8,b'8',b'*'), (Key::Key9,b'9',b'('), (Key::Minus,b'-',b'_'), (Key::Equal,b'=',b'+'),
    (Key::A,b'A',b'a'), (Key::B,b'B',b'b'), (Key::C,b'C',b'c'), (Key::D,b'D',b'd'),
    (Key::E,b'E',b'e'), (Key::F,b'F',b'f'), (Key::G,b'G',b'g'), (Key::H,b'H',b'h'),
    (Key::I,b'I',b'i'), (Key::J,b'J',b'j'), (Key::K,b'K',b'k'), (Key::L,b'L',b'l'),
    (Key::M,b'M',b'm'), (Key::N,b'N',b'n'), (Key::O,b'O',b'o'), (Key::P,b'P',b'p'),
    (Key::Q,b'Q',b'q'), (Key::R,b'R',b'r'), (Key::S,b'S',b's'), (Key::T,b'T',b't'),
    (Key::U,b'U',b'u'), (Key::V,b'V',b'v'), (Key::W,b'W',b'w'), (Key::X,b'X',b'x'),
    (Key::Y,b'Y',b'y'), (Key::Z,b'Z',b'z'),
    (Key::Comma,b',',b'<'), (Key::Period,b'.',b'>'), (Key::Slash,b'/',b'?'),
    (Key::LeftBracket,b'[',b'{'), (Key::RightBracket,b']',b'}'),
    (Key::Semicolon,b';',b':'), (Key::Apostrophe,b'\'',b'"'), (Key::Backslash,b'\\',b'|'),
    (Key::Space,0x20,0x20), (Key::Left,0x08,0x08), (Key::Right,0x09,0x09), (Key::Down,0x0A,0x0A),
    (Key::Up, 0x0B, 0x0B), (Key::Enter,0x0D,0x0D)
];

// the Z1013 8x8 keyboard matrix, in 2 layers (no-shift and shift)
static KEY_MATRIX: &'static [u8] =
    b"13579-  QETUO@  ADGJL*  YCBM.^  24680[  WRZIP]  SFHK+\\  XVN,/_  \
      !#%')=  qetuo`  adgjl:  ycbm>~  \"$&( {  wrzip}  sfhk;|  xvn<?   ";

struct Z1013 {
    kbd_column_nr_requested: usize,
    kbd_8x8_requested: bool,
    next_kbd_column_bits: u64,
    kbd_column_bits: u64,
    key_map: [u64; Z1013_MAX_KEYS],
}

impl Z1013 {
    pub fn new() -> Z1013 {
        let mut z1013 = Z1013 {
            kbd_column_nr_requested: 0,
            kbd_8x8_requested: false,
            next_kbd_column_bits: 0,
            kbd_column_bits: 0,
            key_map: [0; Z1013_MAX_KEYS],
        };
        z1013.init_keymap_8x8();
        z1013
    }

    fn kbd_bit(col: usize, line: usize, num_lines: usize) -> u64 {
        (1u64<<line)<<(col*num_lines)
    }

    fn init_key(&mut self, ascii: u8, col: usize, line: usize, shift:usize, num_lines: usize) {
        let mask =
            Z1013::kbd_bit(col, line, num_lines) |
            if shift != 0 {Z1013::kbd_bit(7, 6, num_lines)} else {0};
        self.key_map[ascii as usize] = mask;
    }

    fn init_keymap_8x8(&mut self) {
        for shift in 0..2 {
            for line in 0..8 {
                for col in 0..8 {
                    let c = KEY_MATRIX[shift*64 + line*8 + col];
                    if 0x20 != c {
                        self.init_key(c, col, line, shift, 8);
                    }
                }
            }
        }

        // special keys
        self.init_key(0x20, 6, 4, 0, 8);    // space
        self.init_key(0x08, 6, 2, 0, 8);    // cursor left
        self.init_key(0x09, 6, 3, 0, 8);    // cursor right
        self.init_key(0x0A, 6, 7, 0, 8);    // cursor down
        self.init_key(0x0B, 6, 6, 0, 8);    // cursor up
        self.init_key(0x0D, 6, 1, 0, 8);    // enter

        // Ctrl+C (== STOP/BREAK)
        self.key_map[0x03] = Z1013::kbd_bit(6, 5, 8) | Z1013::kbd_bit(1, 3, 4);
    }

    pub fn poweron(&mut self, cpu: &mut CPU) {

        // reset the hardware
        self.init_keymap_8x8();
        self.kbd_column_nr_requested = 0;
        self.kbd_8x8_requested = false;
        self.next_kbd_column_bits = 0;
        self.kbd_column_bits = 0;

        // map memory (64 KByte RAM incl. vid-mem, and 2 KByte ROM)
        cpu.mem.unmap_all();
        cpu.mem.map(1, 0x00000, 0x0000, true, 1<<16);
        cpu.mem.map_bytes(0, 0x10000, 0xF000, false, &Z1013_MON_A2);

        // start execution at address 0xF000
        cpu.reg.set_pc(0xF000);
    }

    pub fn put_key(&mut self, ascii: u8) {
        let key_index = (ascii as usize) & (Z1013_MAX_KEYS-1);
        match ascii {
            0 => {
                self.next_kbd_column_bits = 0;
            },
            _ => {
                self.next_kbd_column_bits = self.key_map[key_index];
            }
        }
    }
}

struct System {
    pub cpu: RefCell<CPU>,
    pub pio: RefCell<PIO>,
    pub z1013: RefCell<Z1013>,
}

impl Bus for System {

    fn cpu_outp(&self, port: RegT, val: RegT) {
        match port & 0xFF {
            // PIO-A data
            0x00 => {
                self.pio.borrow_mut().write_data(self, PIO_A, val);
            },
            // PIO-A, control
            0x01 => {
                self.pio.borrow_mut().write_control(PIO_A, val);
            },
            // PIO-B, data
            0x02 => {
                self.pio.borrow_mut().write_data(self, PIO_B, val);
            },
            // PIO-B, control
            0x03 => {
                self.pio.borrow_mut().write_control(PIO_B, val);
            },
            // keyboard column
            0x08 => {
                let mut z1013 = self.z1013.borrow_mut();
                if val == 0 {
                    z1013.kbd_column_bits = z1013.next_kbd_column_bits;
                }
                z1013.kbd_column_nr_requested = val as usize;
            },
            _ => ()
        }
    }

    fn cpu_inp(&self, port: RegT) -> RegT {
        match port & 0xFF {
            // PIO-A data
            0x00 => {
                self.pio.borrow_mut().read_data(self, PIO_A)
            },
            // PIO-A control
            0x01 => {
                self.pio.borrow_mut().read_control()
            },
            // PIO-B read data
            0x02 => {
                self.pio.borrow_mut().read_data(self, PIO_B)
            },
            // PIO-B read control
            0x03 => {
                self.pio.borrow_mut().read_control()
            },
            _ => 0xFF
        }
    }

    fn pio_outp(&self, _: usize, chn: usize, data: RegT) {
        if chn == PIO_B {
            let mut z1013 = self.z1013.borrow_mut();
            z1013.kbd_8x8_requested = 0 != (data & (1<<4));
        }
    }

    fn pio_inp(&self, _: usize, chn: usize) -> RegT {
        if chn == PIO_B {
            // println!("PIO-B in");
            let z1013 = self.z1013.borrow();
            let col = z1013.kbd_column_nr_requested & 7;
            let mut val = z1013.kbd_column_bits >> (col*8);
            if z1013.kbd_8x8_requested {
                val >>= 4;
            }
            val = 0xF & !(val & 0xF);
            val as RegT
        }
        else {
            0xFF
        }
    }
}

impl System {
    pub fn new() -> System {
        System {
            cpu: RefCell::new(CPU::new()),
            pio: RefCell::new(PIO::new(0)),
            z1013: RefCell::new(Z1013::new()),
        }
    }

    pub fn poweron(&self) {
        let mut z1013 = self.z1013.borrow_mut();
        let mut cpu = self.cpu.borrow_mut();
        z1013.poweron(&mut cpu);
    }

    pub fn step(&self, micro_seconds: i64) {
        let num_cycles = (Z1013_FREQ_KHZ * micro_seconds) / 1000;
        let mut cur_cycles = 0;
        let mut cpu = self.cpu.borrow_mut();
        while cur_cycles < num_cycles {
            cur_cycles += cpu.step(self);
        }
    }

    pub fn decode_video(&self, fb: &mut [u32]) {
        let mut i: usize = 0;
        let cpu = self.cpu.borrow();
        let vid_mem = &cpu.mem.heap[0xEC00..0xF000];
        for y in 0..32 {
            for py in 0..8 {
                for x in 0..32 {
                    let chr = vid_mem[(y<<5)+x] as usize;
                    let bits = Z1013_FONT[(chr<<3)|py];
                    for px in 0..8 {
                        fb[i] = if (bits & (1<<(7-px))) != 0 {0xFFFFFFFF} else {0xFF000000};
                        i += 1;
                    }
                }
            }
        }
    }

    pub fn put_key(&mut self, ascii: u8) {
        let mut z1013 = self.z1013.borrow_mut();
        z1013.put_key(ascii);
    }
}

fn main() {
    let mut system = System::new();
    let mut window = match Window::new("rz80 Z1013 Example",
           FB_WIDTH, FB_HEIGHT,
           WindowOptions {
               resize: false,
               scale: Scale::X2,
               ..WindowOptions::default()
           }) {
        Ok(win) => win,
        Err(err) => panic!("Unable to create minifb window: {}", err)
    };
    let mut frame_buffer = [0u32; FB_WIDTH*FB_HEIGHT];
    let micro_seconds_per_frame: i64 = 1000000 / 60;
    system.poweron();
    while window.is_open() && !window.is_key_down(Key::Escape) {
        let mut ascii: u8 = 0;
        let shift = window.is_key_down(Key::LeftShift)|window.is_key_down(Key::RightShift);
        for key in KEYS {
            if window.is_key_down(key.0) {
                ascii = if shift {key.2} else {key.1}
            }
        }
        system.put_key(ascii);
        system.step(micro_seconds_per_frame);
        system.decode_video(&mut frame_buffer);
        window.update_with_buffer(&frame_buffer);
    }
}

<|endoftext|>


<commit_before>use std::cell::RefCell;
use std::collections::hash_map::{Entry, HashMap};
use std::collections::hash_set::HashSet;
use std::rc::{Rc, Weak};
use uuid::Uuid;

use alloc::{Alloc, AllocBox};
use js_types::js_type::{JsVar, JsType, JsPtrEnum};

pub struct Scope {
    pub parent: Option<Rc<Scope>>,
    alloc_box: Rc<RefCell<AllocBox>>,
    pub get_roots: Box<Fn() -> HashSet<Uuid>>,
}

impl Scope {
    pub fn new<F>(alloc_box: &Rc<RefCell<AllocBox>>, get_roots: F) -> Scope
        where F: Fn() -> HashSet<Uuid> + 'static {
        Scope {
            parent: None,
            alloc_box: alloc_box.clone(),
            get_roots: Box::new(get_roots),
        }
    }

    pub fn as_child<F>(parent: &Rc<Scope>, alloc_box: &Rc<RefCell<AllocBox>>, get_roots: F) -> Scope
        where F: Fn() -> HashSet<Uuid> + 'static {
        Scope {
            parent: Some(parent.clone()),
            alloc_box: alloc_box.clone(),
            get_roots: Box::new(get_roots),
        }
    }

    pub fn set_parent(&mut self, parent: &Rc<Scope>) {
        self.parent = Some(parent.clone());
    }

    pub fn alloc(&mut self, uuid: Uuid, ptr: JsPtrEnum) -> Uuid {
        self.alloc_box.borrow_mut().alloc(uuid, ptr)
    }

    pub fn dealloc(&mut self, uuid: &Uuid) -> bool {
        self.alloc_box.borrow_mut().dealloc(uuid)
    }

    pub fn get_ptr_copy(&self, uuid: &Uuid) -> Option<JsPtrEnum> {
        self.alloc_box.borrow().find_id(uuid).map(|var| var.borrow().clone())
    }

    pub fn update_ptr(&mut self, uuid: &Uuid, ptr: JsPtrEnum) -> bool {
        self.alloc_box.borrow_mut().update_var(uuid, ptr)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::cell::RefCell;
    use std::collections::hash_set::HashSet;
    use std::rc::{Rc, Weak};
    use uuid::Uuid;

    use alloc::{Alloc, AllocBox};
    use js_types::js_type::{JsVar, JsType, JsPtrEnum, JsKey, JsKeyEnum};
    use js_types::js_obj::JsObjStruct;
    use js_types::js_str::JsStrStruct;

    fn dummy_get_roots() -> HashSet<Uuid> {
        HashSet::new()
    }

    fn make_alloc_box() -> Rc<RefCell<AllocBox>> {
        Rc::new(RefCell::new(AllocBox::new()))
    }

    fn make_num(i: f64) -> JsVar {
        JsVar::new(JsType::JsNum(i))
    }

    fn make_obj(alloc_box: Rc<RefCell<AllocBox>>, kvs: Vec<(JsKey, JsVar)>) -> JsVar {
        let var = JsVar::new(JsType::JsPtr);
        alloc_box.borrow_mut()
            .alloc(var.uuid, JsPtrEnum::JsObj(JsObjStruct::new(None, "test", kvs)));
        var
    }

    #[test]
    fn test_new_scope() {
        let alloc_box = make_alloc_box();
        let mut test_scope = Scope::new(&alloc_box, dummy_get_roots);
        assert!(test_scope.parent.is_none());
    }

    #[test]
    fn test_as_child_scope() {
        let alloc_box = make_alloc_box();
        let parent_scope = Rc::new(Scope::new(&alloc_box, dummy_get_roots));
        let mut test_scope = Scope::as_child(&parent_scope, &alloc_box, dummy_get_roots);
        assert!(test_scope.parent.is_some());
    }

    #[test]
    fn test_set_parent() {
        let alloc_box = make_alloc_box();
        let parent_scope = Rc::new(Scope::new(&alloc_box, dummy_get_roots));
        let mut test_scope = Scope::new(&alloc_box, dummy_get_roots);
        assert!(test_scope.parent.is_none());
        test_scope.set_parent(&parent_scope);
        assert!(test_scope.parent.is_some());
    }

    #[test]
    fn test_alloc() {
        let alloc_box = make_alloc_box();
        let mut test_scope = Scope::new(&alloc_box, dummy_get_roots);
        let test_var = JsVar::new(JsType::JsPtr);
        let test_id = test_scope.alloc(test_var.uuid, JsPtrEnum::JsNull);
        assert_eq!(test_id, test_var.uuid);
    }

    #[test]
    fn test_dealloc() {
        let alloc_box = make_alloc_box();
        let mut test_scope = Scope::new(&alloc_box, dummy_get_roots);
        let test_var = JsVar::new(JsType::JsPtr);
        let test_id = test_scope.alloc(test_var.uuid, JsPtrEnum::JsNull);
        let bad_uuid = Uuid::new_v4();
        assert!(test_scope.dealloc(&test_id));
        assert!(!test_scope.dealloc(&bad_uuid));
    }

    #[test]
    fn test_get_ptr_copy() {
        let alloc_box = make_alloc_box();
        let mut test_scope = Scope::new(&alloc_box, dummy_get_roots);
        let test_var = JsVar::new(JsType::JsPtr);
        let test_id = test_scope.alloc(test_var.uuid, JsPtrEnum::JsNull);
        let bad_uuid = Uuid::new_v4();

        let ptr_copy = test_scope.get_ptr_copy(&test_id);
        assert!(ptr_copy.is_some());

        // FIXME it's kind of problematic that UUIDs are separated
        // from ptr types
        //let ptr = ptr_copy.unwrap();
        //assert_eq!(ptr.uuid, test_id);

        let bad_copy = test_scope.get_ptr_copy(&bad_uuid);
        assert!(bad_copy.is_none());
    }

    #[test]
    fn test_update_ptr() {
        let alloc_box = make_alloc_box();
        let mut test_scope = Scope::new(&alloc_box, dummy_get_roots);
        let test_var = JsVar::new(JsType::JsPtr);
        let test_id = test_scope.alloc(test_var.uuid, JsPtrEnum::JsNull);
        let mut update = test_scope.get_ptr_copy(&test_id).unwrap();
        update = JsPtrEnum::JsStr(JsStrStruct::new("test"));
        assert!(test_scope.update_ptr(&test_id, update));

        let update = test_scope.get_ptr_copy(&test_id).unwrap();
        match update {
            JsPtrEnum::JsStr(JsStrStruct{text: ref s}) => assert_eq!(s, "test"),
            _ => ()
        }
        test_scope.dealloc(&test_id);
        assert!(!test_scope.update_ptr(&test_id, update));
    }
}
<commit_msg>Fix broken scope tests<commit_after>use std::cell::RefCell;
use std::collections::hash_map::{Entry, HashMap};
use std::collections::hash_set::HashSet;
use std::rc::{Rc, Weak};
use uuid::Uuid;

use alloc::{Alloc, AllocBox};
use js_types::js_type::{JsVar, JsType, JsPtrEnum};

pub struct Scope {
    pub parent: Option<Rc<Scope>>,
    alloc_box: Rc<RefCell<AllocBox>>,
    pub get_roots: Box<Fn() -> HashSet<Uuid>>,
}

impl Scope {
    pub fn new<F>(alloc_box: &Rc<RefCell<AllocBox>>, get_roots: F) -> Scope
        where F: Fn() -> HashSet<Uuid> + 'static {
        Scope {
            parent: None,
            alloc_box: alloc_box.clone(),
            get_roots: Box::new(get_roots),
        }
    }

    pub fn as_child<F>(parent: &Rc<Scope>, alloc_box: &Rc<RefCell<AllocBox>>, get_roots: F) -> Scope
        where F: Fn() -> HashSet<Uuid> + 'static {
        Scope {
            parent: Some(parent.clone()),
            alloc_box: alloc_box.clone(),
            get_roots: Box::new(get_roots),
        }
    }

    pub fn set_parent(&mut self, parent: &Rc<Scope>) {
        self.parent = Some(parent.clone());
    }

    pub fn alloc(&mut self, uuid: Uuid, ptr: JsPtrEnum) -> Uuid {
        self.alloc_box.borrow_mut().alloc(uuid, ptr)
    }

    pub fn dealloc(&mut self, uuid: &Uuid) -> bool {
        self.alloc_box.borrow_mut().dealloc(uuid)
    }

    pub fn get_ptr_copy(&self, uuid: &Uuid) -> Option<JsPtrEnum> {
        self.alloc_box.borrow().find_id(uuid).map(|var| var.borrow().clone())
    }

    pub fn update_ptr(&mut self, uuid: &Uuid, ptr: JsPtrEnum) -> bool {
        self.alloc_box.borrow_mut().update_var(uuid, ptr)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::cell::RefCell;
    use std::collections::hash_set::HashSet;
    use std::rc::{Rc, Weak};
    use uuid::Uuid;

    use alloc::{Alloc, AllocBox};
    use js_types::js_type::{JsVar, JsType, JsPtrEnum, JsKey, JsKeyEnum};
    use js_types::js_obj::JsObjStruct;
    use js_types::js_str::JsStrStruct;

    fn dummy_get_roots() -> HashSet<Uuid> {
        HashSet::new()
    }

    fn make_alloc_box() -> Rc<RefCell<AllocBox>> {
        Rc::new(RefCell::new(AllocBox::new()))
    }

    fn make_num(i: f64) -> JsVar {
        JsVar::new(JsType::JsNum(i))
    }

    fn make_obj(alloc_box: Rc<RefCell<AllocBox>>, kvs: Vec<(JsKey, JsVar)>) -> JsVar {
        let var = JsVar::new(JsType::JsPtr);
        alloc_box.borrow_mut()
            .alloc(var.uuid, JsPtrEnum::JsObj(JsObjStruct::new(None, "test", kvs)));
        var
    }

    #[test]
    fn test_new_scope() {
        let alloc_box = make_alloc_box();
        let mut test_scope = Scope::new(&alloc_box, dummy_get_roots);
        assert!(test_scope.parent.is_none());
    }

    #[test]
    fn test_as_child_scope() {
        let alloc_box = make_alloc_box();
        let parent_scope = Rc::new(Scope::new(&alloc_box, dummy_get_roots));
        let mut test_scope = Scope::as_child(&parent_scope, &alloc_box, dummy_get_roots);
        assert!(test_scope.parent.is_some());
    }

    #[test]
    fn test_set_parent() {
        let alloc_box = make_alloc_box();
        let parent_scope = Rc::new(Scope::new(&alloc_box, dummy_get_roots));
        let mut test_scope = Scope::new(&alloc_box, dummy_get_roots);
        assert!(test_scope.parent.is_none());
        test_scope.set_parent(&parent_scope);
        assert!(test_scope.parent.is_some());
    }

    #[test]
    fn test_alloc() {
        let alloc_box = make_alloc_box();
        let mut test_scope = Scope::new(&alloc_box, dummy_get_roots);
        let test_var = JsVar::new(JsType::JsPtr);
        let test_id = test_scope.alloc(test_var.uuid, JsPtrEnum::JsSym(String::from("test")));
        assert_eq!(test_id, test_var.uuid);
    }

    #[test]
    fn test_dealloc() {
        let alloc_box = make_alloc_box();
        let mut test_scope = Scope::new(&alloc_box, dummy_get_roots);
        let test_var = JsVar::new(JsType::JsPtr);
        let test_id = test_scope.alloc(test_var.uuid, JsPtrEnum::JsSym(String::from("test")));
        let bad_uuid = Uuid::new_v4();
        assert!(test_scope.dealloc(&test_id));
        assert!(!test_scope.dealloc(&bad_uuid));
    }

    #[test]
    fn test_get_ptr_copy() {
        let alloc_box = make_alloc_box();
        let mut test_scope = Scope::new(&alloc_box, dummy_get_roots);
        let test_var = JsVar::new(JsType::JsPtr);
        let test_id = test_scope.alloc(test_var.uuid, JsPtrEnum::JsSym(String::from("test")));
        let bad_uuid = Uuid::new_v4();

        let ptr_copy = test_scope.get_ptr_copy(&test_id);
        assert!(ptr_copy.is_some());

        let bad_copy = test_scope.get_ptr_copy(&bad_uuid);
        assert!(bad_copy.is_none());
    }

    #[test]
    fn test_update_ptr() {
        let alloc_box = make_alloc_box();
        let mut test_scope = Scope::new(&alloc_box, dummy_get_roots);
        let test_var = JsVar::new(JsType::JsPtr);
        let test_id = test_scope.alloc(test_var.uuid, JsPtrEnum::JsSym(String::from("test")));
        let mut update = test_scope.get_ptr_copy(&test_id).unwrap();
        update = JsPtrEnum::JsStr(JsStrStruct::new("test"));
        assert!(test_scope.update_ptr(&test_id, update));

        let update = test_scope.get_ptr_copy(&test_id).unwrap();
        match update {
            JsPtrEnum::JsStr(JsStrStruct{text: ref s}) => assert_eq!(s, "test"),
            _ => ()
        }
        test_scope.dealloc(&test_id);
        assert!(!test_scope.update_ptr(&test_id, update));
    }
}
<|endoftext|>


<commit_before>extern crate android_glue;

use libc;
use std::ffi::{CString};
use std::sync::mpsc::{Receiver, channel};
use {CreationError, Event, WindowBuilder};
use CreationError::OsError;
use events::ElementState::{Pressed, Released};
use events::Event::{MouseInput, MouseMoved};
use events::MouseButton::LeftMouseButton;

use std::collections::RingBuf;

use BuilderAttribs;

pub struct Window {
    display: ffi::egl::types::EGLDisplay,
    context: ffi::egl::types::EGLContext,
    surface: ffi::egl::types::EGLSurface,
    event_rx: Receiver<android_glue::Event>,
}

pub struct MonitorID;

mod ffi;

pub fn get_available_monitors() -> RingBuf <MonitorID> {
    let mut rb = RingBuf::new();
    rb.push_back(MonitorID);
    rb
}

pub fn get_primary_monitor() -> MonitorID {
    MonitorID
}

impl MonitorID {
    pub fn get_name(&self) -> Option<String> {
        Some("Primary".to_string())
    }

    pub fn get_dimensions(&self) -> (uint, uint) {
        unimplemented!()
    }
}

#[cfg(feature = "headless")]
pub struct HeadlessContext(int);

#[cfg(feature = "headless")]
impl HeadlessContext {
    /// See the docs in the crate root file.
    pub fn new(_builder: BuilderAttribs) -> Result<HeadlessContext, CreationError> {
        unimplemented!()
    }

    /// See the docs in the crate root file.
    pub unsafe fn make_current(&self) {
        unimplemented!()
    }

    /// See the docs in the crate root file.
    pub fn get_proc_address(&self, _addr: &str) -> *const () {
        unimplemented!()
    }
}

#[cfg(feature = "headless")]
unsafe impl Send for HeadlessContext {}
#[cfg(feature = "headless")]
unsafe impl Sync for HeadlessContext {}

impl Window {
    pub fn new(builder: BuilderAttribs) -> Result<Window, CreationError> {
        use std::{mem, ptr};

        if builder.sharing.is_some() {
            unimplemented!()
        }

        let native_window = unsafe { android_glue::get_native_window() };
        if native_window.is_null() {
            return Err(OsError(format!("Android's native window is null")));
        }

        let display = unsafe {
            let display = ffi::egl::GetDisplay(mem::transmute(ffi::egl::DEFAULT_DISPLAY));
            if display.is_null() {
                return Err(OsError("No EGL display connection available".to_string()));
            }
            display
        };

        android_glue::write_log("eglGetDisplay succeeded");

        let (_major, _minor) = unsafe {
            let mut major: ffi::egl::types::EGLint = mem::uninitialized();
            let mut minor: ffi::egl::types::EGLint = mem::uninitialized();

            if ffi::egl::Initialize(display, &mut major, &mut minor) == 0 {
                return Err(OsError(format!("eglInitialize failed")))
            }

            (major, minor)
        };

        android_glue::write_log("eglInitialize succeeded");

        let use_gles2 = match builder.gl_version {
            Some((2, 0)) => true,
            _ => false,
        };

        let config = unsafe {
            let mut attribute_list = vec!();
            if use_gles2 {
                attribute_list.push_all(&[ffi::egl::RENDERABLE_TYPE as i32,
                                         ffi::egl::OPENGL_ES2_BIT as i32]);
            }
            attribute_list.push_all(&[ffi::egl::RED_SIZE as i32, 1]);
            attribute_list.push_all(&[ffi::egl::GREEN_SIZE as i32, 1]);
            attribute_list.push_all(&[ffi::egl::BLUE_SIZE as i32, 1]);
            attribute_list.push(ffi::egl::NONE as i32);

            let mut num_config: ffi::egl::types::EGLint = mem::uninitialized();
            let mut config: ffi::egl::types::EGLConfig = mem::uninitialized();
            if ffi::egl::ChooseConfig(display, attribute_list.as_ptr(), &mut config, 1,
                &mut num_config) == 0
            {
                return Err(OsError(format!("eglChooseConfig failed")))
            }

            if num_config <= 0 {
                return Err(OsError(format!("eglChooseConfig returned no available config")))
            }

            config
        };

        android_glue::write_log("eglChooseConfig succeeded");

        let context = unsafe {
            let mut context_attributes = vec!();
            if use_gles2 {
                context_attributes.push_all(&[ffi::egl::CONTEXT_CLIENT_VERSION as i32, 2]);
            }
            context_attributes.push(ffi::egl::NONE as i32);

            let context = ffi::egl::CreateContext(display, config, ptr::null(),
                                                  context_attributes.as_ptr());
            if context.is_null() {
                return Err(OsError(format!("eglCreateContext failed")))
            }
            context
        };

        android_glue::write_log("eglCreateContext succeeded");

        let surface = unsafe {
            let surface = ffi::egl::CreateWindowSurface(display, config, native_window, ptr::null());
            if surface.is_null() {
                return Err(OsError(format!("eglCreateWindowSurface failed")))
            }
            surface
        };

        android_glue::write_log("eglCreateWindowSurface succeeded");

        let (tx, rx) = channel();
        android_glue::add_sender(tx);

        Ok(Window {
            display: display,
            context: context,
            surface: surface,
            event_rx: rx,
        })
    }

    pub fn is_closed(&self) -> bool {
        false
    }

    pub fn set_title(&self, _: &str) {
    }

    pub fn show(&self) {
    }

    pub fn hide(&self) {
    }

    pub fn get_position(&self) -> Option<(int, int)> {
        None
    }

    pub fn set_position(&self, _x: int, _y: int) {
    }

    pub fn get_inner_size(&self) -> Option<(uint, uint)> {
        let native_window = unsafe { android_glue::get_native_window() };

        if native_window.is_null() {
            None
        } else {
            Some((
                unsafe { ffi::ANativeWindow_getWidth(native_window) } as uint,
                unsafe { ffi::ANativeWindow_getHeight(native_window) } as uint
            ))
        }
    }

    pub fn get_outer_size(&self) -> Option<(uint, uint)> {
        self.get_inner_size()
    }

    pub fn set_inner_size(&self, _x: uint, _y: uint) {
    }

    pub fn create_window_proxy(&self) -> WindowProxy {
        WindowProxy
    }

    pub fn poll_events(&self) -> RingBuf<Event> {
        let mut events = RingBuf::new();
        loop {
            match self.event_rx.try_recv() {
                Ok(event) => match event {
                    android_glue::Event::EventDown => {
                        events.push_back(MouseInput(Pressed, LeftMouseButton));
                    },
                    android_glue::Event::EventUp => {
                        events.push_back(MouseInput(Released, LeftMouseButton));
                    },
                    android_glue::Event::EventMove(x, y) => {
                        events.push_back(MouseMoved((x as int, y as int)));
                    },
                },
                Err(_) => {
                    break;
                },
            }
        }
        events
    }

    pub fn wait_events(&self) -> RingBuf<Event> {
        use std::time::Duration;
        use std::io::timer;
        timer::sleep(Duration::milliseconds(16));
        self.poll_events()
    }

    pub fn make_current(&self) {
        unsafe {
            ffi::egl::MakeCurrent(self.display, self.surface, self.surface, self.context);
        }
    }

    pub fn get_proc_address(&self, addr: &str) -> *const () {
        let addr = CString::from_slice(addr.as_bytes()).as_slice_with_nul().as_ptr();
        unsafe {
            ffi::egl::GetProcAddress(addr) as *const ()
        }
    }

    pub fn swap_buffers(&self) {
        unsafe {
            ffi::egl::SwapBuffers(self.display, self.surface);
        }
    }

    pub fn platform_display(&self) -> *mut libc::c_void {
        self.display as *mut libc::c_void
    }

    pub fn get_api(&self) -> ::Api {
        ::Api::OpenGlEs
    }

    pub fn set_window_resize_callback(&mut self, _: Option<fn(uint, uint)>) {
    }

    pub fn set_cursor(&self, cursor: MouseCursor) {
    }
}

unsafe impl Send for Window {}
unsafe impl Sync for Window {}

#[cfg(feature = "window")]
#[derive(Clone)]
pub struct WindowProxy;

impl WindowProxy {
    pub fn wakeup_event_loop(&self) {
        unimplemented!()
    }
}

#[unsafe_destructor]
impl Drop for Window {
    fn drop(&mut self) {
        use std::ptr;

        unsafe {
            android_glue::write_log("Destroying gl-init window");
            ffi::egl::MakeCurrent(self.display, ptr::null(), ptr::null(), ptr::null());
            ffi::egl::DestroySurface(self.display, self.surface);
            ffi::egl::DestroyContext(self.display, self.context);
            ffi::egl::Terminate(self.display);
        }
    }
}
<commit_msg>Fixed android: Added 'MouseCursor' import<commit_after>extern crate android_glue;

use libc;
use std::ffi::{CString};
use std::sync::mpsc::{Receiver, channel};
use {CreationError, Event, WindowBuilder, MouseCursor};
use CreationError::OsError;
use events::ElementState::{Pressed, Released};
use events::Event::{MouseInput, MouseMoved};
use events::MouseButton::LeftMouseButton;

use std::collections::RingBuf;

use BuilderAttribs;

pub struct Window {
    display: ffi::egl::types::EGLDisplay,
    context: ffi::egl::types::EGLContext,
    surface: ffi::egl::types::EGLSurface,
    event_rx: Receiver<android_glue::Event>,
}

pub struct MonitorID;

mod ffi;

pub fn get_available_monitors() -> RingBuf <MonitorID> {
    let mut rb = RingBuf::new();
    rb.push_back(MonitorID);
    rb
}

pub fn get_primary_monitor() -> MonitorID {
    MonitorID
}

impl MonitorID {
    pub fn get_name(&self) -> Option<String> {
        Some("Primary".to_string())
    }

    pub fn get_dimensions(&self) -> (uint, uint) {
        unimplemented!()
    }
}

#[cfg(feature = "headless")]
pub struct HeadlessContext(int);

#[cfg(feature = "headless")]
impl HeadlessContext {
    /// See the docs in the crate root file.
    pub fn new(_builder: BuilderAttribs) -> Result<HeadlessContext, CreationError> {
        unimplemented!()
    }

    /// See the docs in the crate root file.
    pub unsafe fn make_current(&self) {
        unimplemented!()
    }

    /// See the docs in the crate root file.
    pub fn get_proc_address(&self, _addr: &str) -> *const () {
        unimplemented!()
    }
}

#[cfg(feature = "headless")]
unsafe impl Send for HeadlessContext {}
#[cfg(feature = "headless")]
unsafe impl Sync for HeadlessContext {}

impl Window {
    pub fn new(builder: BuilderAttribs) -> Result<Window, CreationError> {
        use std::{mem, ptr};

        if builder.sharing.is_some() {
            unimplemented!()
        }

        let native_window = unsafe { android_glue::get_native_window() };
        if native_window.is_null() {
            return Err(OsError(format!("Android's native window is null")));
        }

        let display = unsafe {
            let display = ffi::egl::GetDisplay(mem::transmute(ffi::egl::DEFAULT_DISPLAY));
            if display.is_null() {
                return Err(OsError("No EGL display connection available".to_string()));
            }
            display
        };

        android_glue::write_log("eglGetDisplay succeeded");

        let (_major, _minor) = unsafe {
            let mut major: ffi::egl::types::EGLint = mem::uninitialized();
            let mut minor: ffi::egl::types::EGLint = mem::uninitialized();

            if ffi::egl::Initialize(display, &mut major, &mut minor) == 0 {
                return Err(OsError(format!("eglInitialize failed")))
            }

            (major, minor)
        };

        android_glue::write_log("eglInitialize succeeded");

        let use_gles2 = match builder.gl_version {
            Some((2, 0)) => true,
            _ => false,
        };

        let config = unsafe {
            let mut attribute_list = vec!();
            if use_gles2 {
                attribute_list.push_all(&[ffi::egl::RENDERABLE_TYPE as i32,
                                         ffi::egl::OPENGL_ES2_BIT as i32]);
            }
            attribute_list.push_all(&[ffi::egl::RED_SIZE as i32, 1]);
            attribute_list.push_all(&[ffi::egl::GREEN_SIZE as i32, 1]);
            attribute_list.push_all(&[ffi::egl::BLUE_SIZE as i32, 1]);
            attribute_list.push(ffi::egl::NONE as i32);

            let mut num_config: ffi::egl::types::EGLint = mem::uninitialized();
            let mut config: ffi::egl::types::EGLConfig = mem::uninitialized();
            if ffi::egl::ChooseConfig(display, attribute_list.as_ptr(), &mut config, 1,
                &mut num_config) == 0
            {
                return Err(OsError(format!("eglChooseConfig failed")))
            }

            if num_config <= 0 {
                return Err(OsError(format!("eglChooseConfig returned no available config")))
            }

            config
        };

        android_glue::write_log("eglChooseConfig succeeded");

        let context = unsafe {
            let mut context_attributes = vec!();
            if use_gles2 {
                context_attributes.push_all(&[ffi::egl::CONTEXT_CLIENT_VERSION as i32, 2]);
            }
            context_attributes.push(ffi::egl::NONE as i32);

            let context = ffi::egl::CreateContext(display, config, ptr::null(),
                                                  context_attributes.as_ptr());
            if context.is_null() {
                return Err(OsError(format!("eglCreateContext failed")))
            }
            context
        };

        android_glue::write_log("eglCreateContext succeeded");

        let surface = unsafe {
            let surface = ffi::egl::CreateWindowSurface(display, config, native_window, ptr::null());
            if surface.is_null() {
                return Err(OsError(format!("eglCreateWindowSurface failed")))
            }
            surface
        };

        android_glue::write_log("eglCreateWindowSurface succeeded");

        let (tx, rx) = channel();
        android_glue::add_sender(tx);

        Ok(Window {
            display: display,
            context: context,
            surface: surface,
            event_rx: rx,
        })
    }

    pub fn is_closed(&self) -> bool {
        false
    }

    pub fn set_title(&self, _: &str) {
    }

    pub fn show(&self) {
    }

    pub fn hide(&self) {
    }

    pub fn get_position(&self) -> Option<(int, int)> {
        None
    }

    pub fn set_position(&self, _x: int, _y: int) {
    }

    pub fn get_inner_size(&self) -> Option<(uint, uint)> {
        let native_window = unsafe { android_glue::get_native_window() };

        if native_window.is_null() {
            None
        } else {
            Some((
                unsafe { ffi::ANativeWindow_getWidth(native_window) } as uint,
                unsafe { ffi::ANativeWindow_getHeight(native_window) } as uint
            ))
        }
    }

    pub fn get_outer_size(&self) -> Option<(uint, uint)> {
        self.get_inner_size()
    }

    pub fn set_inner_size(&self, _x: uint, _y: uint) {
    }

    pub fn create_window_proxy(&self) -> WindowProxy {
        WindowProxy
    }

    pub fn poll_events(&self) -> RingBuf<Event> {
        let mut events = RingBuf::new();
        loop {
            match self.event_rx.try_recv() {
                Ok(event) => match event {
                    android_glue::Event::EventDown => {
                        events.push_back(MouseInput(Pressed, LeftMouseButton));
                    },
                    android_glue::Event::EventUp => {
                        events.push_back(MouseInput(Released, LeftMouseButton));
                    },
                    android_glue::Event::EventMove(x, y) => {
                        events.push_back(MouseMoved((x as int, y as int)));
                    },
                },
                Err(_) => {
                    break;
                },
            }
        }
        events
    }

    pub fn wait_events(&self) -> RingBuf<Event> {
        use std::time::Duration;
        use std::io::timer;
        timer::sleep(Duration::milliseconds(16));
        self.poll_events()
    }

    pub fn make_current(&self) {
        unsafe {
            ffi::egl::MakeCurrent(self.display, self.surface, self.surface, self.context);
        }
    }

    pub fn get_proc_address(&self, addr: &str) -> *const () {
        let addr = CString::from_slice(addr.as_bytes()).as_slice_with_nul().as_ptr();
        unsafe {
            ffi::egl::GetProcAddress(addr) as *const ()
        }
    }

    pub fn swap_buffers(&self) {
        unsafe {
            ffi::egl::SwapBuffers(self.display, self.surface);
        }
    }

    pub fn platform_display(&self) -> *mut libc::c_void {
        self.display as *mut libc::c_void
    }

    pub fn get_api(&self) -> ::Api {
        ::Api::OpenGlEs
    }

    pub fn set_window_resize_callback(&mut self, _: Option<fn(uint, uint)>) {
    }

    pub fn set_cursor(&self, cursor: MouseCursor) {
    }
}

unsafe impl Send for Window {}
unsafe impl Sync for Window {}

#[cfg(feature = "window")]
#[derive(Clone)]
pub struct WindowProxy;

impl WindowProxy {
    pub fn wakeup_event_loop(&self) {
        unimplemented!()
    }
}

#[unsafe_destructor]
impl Drop for Window {
    fn drop(&mut self) {
        use std::ptr;

        unsafe {
            android_glue::write_log("Destroying gl-init window");
            ffi::egl::MakeCurrent(self.display, ptr::null(), ptr::null(), ptr::null());
            ffi::egl::DestroySurface(self.display, self.surface);
            ffi::egl::DestroyContext(self.display, self.context);
            ffi::egl::Terminate(self.display);
        }
    }
}
<|endoftext|>


<commit_before>use std::fmt;
use super::{
    ArrayBase,
    Data,
    Dimension,
};

fn format_array<A, S, D, F>(view: &ArrayBase<S, D>, f: &mut fmt::Formatter,
                            mut format: F)
    -> fmt::Result
    where F: FnMut(&mut fmt::Formatter, &A) -> fmt::Result,
          D: Dimension,
          S: Data<Elem=A>,
{
    let ndim = view.dim.slice().len();
    /* private nowadays
    if ndim > 0 && f.width.is_none() {
        f.width = Some(4)
    }
    */
    // None will be an empty iter.
    let mut last_index = match view.dim.first_index() {
        None => view.dim.clone(),
        Some(ix) => ix,
    };
    for _ in 0..ndim {
        try!(write!(f, "["));
    }
    let mut first = true;
    // Simply use the indexed iterator, and take the index wraparounds
    // as cues for when to add []'s and how many to add.
    for (index, elt) in view.indexed_iter() {
        let take_n = if ndim == 0 { 1 } else { ndim - 1 };
        let mut update_index = false;
        for (i, (a, b)) in index.slice()
                                .iter()
                                .take(take_n)
                                .zip(last_index.slice().iter())
                                .enumerate() {
            if a != b {
                // New row.
                // # of ['s needed
                let n = ndim - i - 1;
                for _ in 0..n {
                    try!(write!(f, "]"));
                }
                try!(write!(f, ","));
                if !f.alternate() {
                    try!(write!(f, "\n"));
                }
                for _ in 0..ndim - n {
                    try!(write!(f, " "));
                }
                for _ in 0..n {
                    try!(write!(f, "["));
                }
                first = true;
                update_index = true;
                break;
            }
        }
        if !first {
            try!(write!(f, ", "));
        }
        first = false;
        try!(format(f, elt));

        if update_index {
            last_index = index;
        }
    }
    for _ in 0..ndim {
        try!(write!(f, "]"));
    }
    Ok(())
}

// NOTE: We can impl other fmt traits here
/// Format the array using `Display` and apply the formatting parameters used
/// to each element.
///
/// The array is shown in multiline style, unless the alternate form
/// is used, `{:#}`.
impl<'a, A: fmt::Display, S, D: Dimension> fmt::Display for ArrayBase<S, D>
    where S: Data<Elem=A>,
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        format_array(self, f, |f, elt| elt.fmt(f))
    }
}

/// Format the array using `Debug` and apply the formatting parameters used
/// to each element.
///
/// The array is shown in multiline style, unless the alternate form
/// is used, `{:#?}`.
impl<'a, A: fmt::Debug, S, D: Dimension> fmt::Debug for ArrayBase<S, D>
    where S: Data<Elem=A>,
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        format_array(self, f, |f, elt| elt.fmt(f))
    }
}

/// Format the array using `LowerExp` and apply the formatting parameters used
/// to each element.
///
/// The array is shown in multiline style, unless the alternate form
/// is used, `{:#e}`.
impl<'a, A: fmt::LowerExp, S, D: Dimension> fmt::LowerExp for ArrayBase<S, D>
    where S: Data<Elem=A>,
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        format_array(self, f, |f, elt| elt.fmt(f))
    }
}

/// Format the array using `UpperExp` and apply the formatting parameters used
/// to each element.
///
/// The array is shown in multiline style, unless the alternate form
/// is used, `{:#E}`.
impl<'a, A: fmt::UpperExp, S, D: Dimension> fmt::UpperExp for ArrayBase<S, D>
    where S: Data<Elem=A>,
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        format_array(self, f, |f, elt| elt.fmt(f))
    }
}
/// Format the array using `LowerHex` and apply the formatting parameters used
/// to each element.
///
/// The array is shown in multiline style, unless the alternate form
/// is used, `{:#x}`.
impl<'a, A: fmt::LowerHex, S, D: Dimension> fmt::LowerHex for ArrayBase<S, D>
    where S: Data<Elem=A>,
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        format_array(self, f, |f, elt| elt.fmt(f))
    }
}

<commit_msg>arrayformat: simplify closure passing<commit_after>use std::fmt;
use super::{
    ArrayBase,
    Data,
    Dimension,
};

fn format_array<A, S, D, F>(view: &ArrayBase<S, D>, f: &mut fmt::Formatter,
                            mut format: F)
    -> fmt::Result
    where F: FnMut(&A, &mut fmt::Formatter) -> fmt::Result,
          D: Dimension,
          S: Data<Elem=A>,
{
    let ndim = view.dim.slice().len();
    /* private nowadays
    if ndim > 0 && f.width.is_none() {
        f.width = Some(4)
    }
    */
    // None will be an empty iter.
    let mut last_index = match view.dim.first_index() {
        None => view.dim.clone(),
        Some(ix) => ix,
    };
    for _ in 0..ndim {
        try!(write!(f, "["));
    }
    let mut first = true;
    // Simply use the indexed iterator, and take the index wraparounds
    // as cues for when to add []'s and how many to add.
    for (index, elt) in view.indexed_iter() {
        let take_n = if ndim == 0 { 1 } else { ndim - 1 };
        let mut update_index = false;
        for (i, (a, b)) in index.slice()
                                .iter()
                                .take(take_n)
                                .zip(last_index.slice().iter())
                                .enumerate() {
            if a != b {
                // New row.
                // # of ['s needed
                let n = ndim - i - 1;
                for _ in 0..n {
                    try!(write!(f, "]"));
                }
                try!(write!(f, ","));
                if !f.alternate() {
                    try!(write!(f, "\n"));
                }
                for _ in 0..ndim - n {
                    try!(write!(f, " "));
                }
                for _ in 0..n {
                    try!(write!(f, "["));
                }
                first = true;
                update_index = true;
                break;
            }
        }
        if !first {
            try!(write!(f, ", "));
        }
        first = false;
        try!(format(elt, f));

        if update_index {
            last_index = index;
        }
    }
    for _ in 0..ndim {
        try!(write!(f, "]"));
    }
    Ok(())
}

// NOTE: We can impl other fmt traits here
/// Format the array using `Display` and apply the formatting parameters used
/// to each element.
///
/// The array is shown in multiline style, unless the alternate form
/// is used, `{:#}`.
impl<'a, A: fmt::Display, S, D: Dimension> fmt::Display for ArrayBase<S, D>
    where S: Data<Elem=A>,
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        format_array(self, f, <_>::fmt)
    }
}

/// Format the array using `Debug` and apply the formatting parameters used
/// to each element.
///
/// The array is shown in multiline style, unless the alternate form
/// is used, `{:#?}`.
impl<'a, A: fmt::Debug, S, D: Dimension> fmt::Debug for ArrayBase<S, D>
    where S: Data<Elem=A>,
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        format_array(self, f, <_>::fmt)
    }
}

/// Format the array using `LowerExp` and apply the formatting parameters used
/// to each element.
///
/// The array is shown in multiline style, unless the alternate form
/// is used, `{:#e}`.
impl<'a, A: fmt::LowerExp, S, D: Dimension> fmt::LowerExp for ArrayBase<S, D>
    where S: Data<Elem=A>,
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        format_array(self, f, <_>::fmt)
    }
}

/// Format the array using `UpperExp` and apply the formatting parameters used
/// to each element.
///
/// The array is shown in multiline style, unless the alternate form
/// is used, `{:#E}`.
impl<'a, A: fmt::UpperExp, S, D: Dimension> fmt::UpperExp for ArrayBase<S, D>
    where S: Data<Elem=A>,
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        format_array(self, f, <_>::fmt)
    }
}
/// Format the array using `LowerHex` and apply the formatting parameters used
/// to each element.
///
/// The array is shown in multiline style, unless the alternate form
/// is used, `{:#x}`.
impl<'a, A: fmt::LowerHex, S, D: Dimension> fmt::LowerHex for ArrayBase<S, D>
    where S: Data<Elem=A>,
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        format_array(self, f, <_>::fmt)
    }
}

<|endoftext|>


<commit_before>/*
 * Copyright (C) 2017 Genymobile
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

extern crate chrono;
extern crate ctrlc;
#[macro_use]
extern crate log;
extern crate relaylib;

mod cli_args;
mod logger;

use std::env;
use cli_args::CommandLineArguments;
use logger::SimpleLogger;
use std::error;
use std::io;
use std::fmt;
#[cfg(unix)]
use std::os::unix::process::ExitStatusExt;
use std::process::{self, ExitStatus};
use std::sync::{Arc, Mutex, Condvar};
use std::thread;
use std::time::Duration;

const TAG: &'static str = "Main";

const COMMANDS: &[&'static Command] = &[
    &InstallCommand,
    &UninstallCommand,
    &ReinstallCommand,
    &RunCommand,
    &StartCommand,
    &StopCommand,
    &RestartCommand,
    &RelayCommand,
];

trait Command {
    fn command(&self) -> &'static str;
    fn accepted_parameters(&self) -> u8;
    fn description(&self) -> &'static str;
    fn execute(&self, args: &CommandLineArguments) -> Result<(), CommandExecutionError>;
}

struct InstallCommand;
struct UninstallCommand;
struct ReinstallCommand;
struct RunCommand;
struct StartCommand;
struct StopCommand;
struct RestartCommand;
struct RelayCommand;

impl Command for InstallCommand {
    fn command(&self) -> &'static str {
        "install"
    }

    fn accepted_parameters(&self) -> u8 {
        cli_args::PARAM_SERIAL
    }

    fn description(&self) -> &'static str {
        "Install the client on the Android device and exit.\n\
        If several devices are connected via adb, then serial must be\n\
        specified."
    }

    fn execute(&self, args: &CommandLineArguments) -> Result<(), CommandExecutionError> {
        info!("Installing gnirehtet...");
        exec_adb(args.serial(), vec!["install", "-r", "gnirehtet.apk"])
    }
}

impl Command for UninstallCommand {
    fn command(&self) -> &'static str {
        "uninstall"
    }

    fn accepted_parameters(&self) -> u8 {
        cli_args::PARAM_SERIAL
    }

    fn description(&self) -> &'static str {
        "Uninstall the client from the Android device and exit.\n\
        If several devices are connected via adb, then serial must be\n\
        specified."
    }

    fn execute(&self, args: &CommandLineArguments) -> Result<(), CommandExecutionError> {
        info!("Uninstalling gnirehtet...");
        exec_adb(args.serial(), vec!["uninstall", "com.genymobile.gnirehtet"])
    }
}

impl Command for ReinstallCommand {
    fn command(&self) -> &'static str {
        "reinstall"
    }

    fn accepted_parameters(&self) -> u8 {
        cli_args::PARAM_SERIAL
    }

    fn description(&self) -> &'static str {
        "Uninstall then install."
    }

    fn execute(&self, args: &CommandLineArguments) -> Result<(), CommandExecutionError> {
        UninstallCommand.execute(args)?;
        InstallCommand.execute(args)?;
        Ok(())
    }
}

impl Command for RunCommand {
    fn command(&self) -> &'static str {
        "run"
    }

    fn accepted_parameters(&self) -> u8 {
        cli_args::PARAM_SERIAL | cli_args::PARAM_DNS_SERVERS
    }

    fn description(&self) -> &'static str {
        "Enable reverse tethering for exactly one device:\n  \
          - install the client if necessary;\n  \
          - start the client;\n  \
          - start the relay server;\n  \
          - on Ctrl+C, stop both the relay server and the client."
    }

    fn execute(&self, args: &CommandLineArguments) -> Result<(), CommandExecutionError> {
        if !is_gnirehtet_installed(args.serial())? {
            InstallCommand.execute(args)?;
            // wait a bit after the app is installed so that intent actions are correctly
            // registered
            thread::sleep(Duration::from_millis(500));
        }

        {
            // start in parallel so that the relay server is ready when the client connects
            let serial = args.serial().cloned();
            let dns_servers = args.dns_servers().cloned();
            thread::spawn(move || if let Err(err) = start_gnirehtet(
                serial.as_ref(),
                dns_servers.as_ref(),
            )
            {
                error!("Cannot start gnirehtet: {}", err);
            });
        }

        let pair = Arc::new((Mutex::new(false), Condvar::new()));

        let serial = args.serial().cloned();
        let handler_pair = pair.clone();
        ctrlc::set_handler(move || {
            relaylib::interrupt::set_interrupted_by_user();
            if let Err(err) = stop_gnirehtet(serial.as_ref()) {
                error!("Cannot stop gnirehtet: {}", err);
            }

            let (ref lock, ref cvar) = *handler_pair;
            *lock.lock().unwrap() = true;
            cvar.notify_one();
        }).expect("Error setting Ctrl-C handler");

        match relay() {
            Err(CommandExecutionError::Io(ref err)) if err.kind() == io::ErrorKind::Interrupted => {
                warn!(target: TAG, "Relay server interrupted");
                // wait the ctrlc handler to complete
                let (ref lock, ref cvar) = *pair;
                let mut complete = lock.lock().unwrap();
                while !*complete {
                    complete = cvar.wait(complete).unwrap();
                }
                Ok(())
            }
            Err(ref err) => {
                panic!("Cannot relay: {}", err);
            }
            ok => ok,
        }
    }
}

impl Command for StartCommand {
    fn command(&self) -> &'static str {
        "start"
    }

    fn accepted_parameters(&self) -> u8 {
        cli_args::PARAM_SERIAL | cli_args::PARAM_DNS_SERVERS
    }

    fn description(&self) -> &'static str {
        "Start a client on the Android device and exit.\n\
        If several devices are connected via adb, then serial must be\n\
        specified.\n\
        If -d is given, then make the Android device use the specified\n\
        DNS server(s). Otherwise, use 8.8.8.8 (Google public DNS).\n\
        If the client is already started, then do nothing, and ignore\n\
        DNS servers parameter.\n\
        To use the host 'localhost' as DNS, use 10.0.2.2."
    }

    fn execute(&self, args: &CommandLineArguments) -> Result<(), CommandExecutionError> {
        start_gnirehtet(args.serial(), args.dns_servers())
    }
}

impl Command for StopCommand {
    fn command(&self) -> &'static str {
        "stop"
    }

    fn accepted_parameters(&self) -> u8 {
        cli_args::PARAM_SERIAL
    }

    fn description(&self) -> &'static str {
        "Stop the client on the Android device and exit.\n\
        If several devices are connected via adb, then serial must be\n\
        specified."
    }

    fn execute(&self, args: &CommandLineArguments) -> Result<(), CommandExecutionError> {
        stop_gnirehtet(args.serial())
    }
}

impl Command for RestartCommand {
    fn command(&self) -> &'static str {
        "restart"
    }

    fn accepted_parameters(&self) -> u8 {
        cli_args::PARAM_SERIAL | cli_args::PARAM_DNS_SERVERS
    }

    fn description(&self) -> &'static str {
        "Stop then start."
    }

    fn execute(&self, args: &CommandLineArguments) -> Result<(), CommandExecutionError> {
        StopCommand.execute(args)?;
        StartCommand.execute(args)?;
        Ok(())
    }
}

impl Command for RelayCommand {
    fn command(&self) -> &'static str {
        "relay"
    }

    fn accepted_parameters(&self) -> u8 {
        cli_args::PARAM_NONE
    }

    fn description(&self) -> &'static str {
        "Start the relay server in the current terminal."
    }

    fn execute(&self, _: &CommandLineArguments) -> Result<(), CommandExecutionError> {
        relay()
    }
}

#[derive(Debug)]
enum Termination {
    Value(i32),
    #[cfg(unix)]
    Signal(i32),
}

impl Termination {
    fn from(status: ExitStatus) -> Self {
        match status.code() {
            Some(code) => Termination::Value(code),
            #[cfg(unix)]
            None => Termination::Signal(status.signal().unwrap()),
            #[cfg(not(unix))]
            None => panic!("Unexpected signal termination on non-unix system"),
        }
    }
}

#[derive(Debug)]
struct CommandStatusError {
    command: Vec<String>,
    termination: Termination,
}

impl CommandStatusError {
    fn new(command: Vec<String>, status: ExitStatus) -> Self {
        Self {
            command: command,
            termination: Termination::from(status),
        }
    }
}

impl fmt::Display for CommandStatusError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self.termination {
            Termination::Value(code) => {
                write!(f, "Command {:?} returned with value {}", self.command, code)
            }
            #[cfg(unix)]
            Termination::Signal(sig) => {
                write!(f, "Command {:?} terminated by signal {}", self.command, sig)
            }
        }
    }
}

impl error::Error for CommandStatusError {
    fn description(&self) -> &str {
        "Execution terminated with failure"
    }

    fn cause(&self) -> Option<&error::Error> {
        None
    }
}

#[derive(Debug)]
enum CommandExecutionError {
    Io(io::Error),
    Status(CommandStatusError),
}

impl fmt::Display for CommandExecutionError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            CommandExecutionError::Io(ref err) => write!(f, "IO error: {}", err),
            CommandExecutionError::Status(ref err) => write!(f, "Status in error: {}", err),
        }
    }
}

impl error::Error for CommandExecutionError {
    fn description(&self) -> &str {
        match *self {
            CommandExecutionError::Io(ref err) => err.description(),
            CommandExecutionError::Status(ref err) => err.description(),
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        match *self {
            CommandExecutionError::Io(ref err) => Some(err),
            CommandExecutionError::Status(ref err) => Some(err),
        }
    }
}

impl From<io::Error> for CommandExecutionError {
    fn from(error: io::Error) -> Self {
        CommandExecutionError::Io(error)
    }
}

impl From<CommandStatusError> for CommandExecutionError {
    fn from(error: CommandStatusError) -> Self {
        CommandExecutionError::Status(error)
    }
}

fn create_adb_args<S: Into<String>>(serial: Option<&String>, args: Vec<S>) -> Vec<String> {
    let mut command = Vec::<String>::new();
    if let Some(serial) = serial {
        command.push("-s".into());
        command.push(serial.clone());
    }
    for arg in args {
        command.push(arg.into());
    }
    command
}

fn exec_adb<S: Into<String>>(
    serial: Option<&String>,
    args: Vec<S>,
) -> Result<(), CommandExecutionError> {
    let mut adb_args = create_adb_args(serial, args);
    info!("Execute: adb {:?}", adb_args);
    let exit_status = process::Command::new("adb").args(&adb_args[..]).status()?;
    if exit_status.success() {
        Ok(())
    } else {
        let mut cmd = vec!["adb".to_string()];
        cmd.append(&mut adb_args);
        Err(CommandStatusError::new(cmd, exit_status).into())
    }
}

fn is_gnirehtet_installed(serial: Option<&String>) -> Result<bool, CommandExecutionError> {
    let args = create_adb_args(
        serial,
        vec![
            "shell",
            "pm",
            "list",
            "packages",
            "com.genymobile.gnirehtet",
        ],
    );
    let output = process::Command::new("adb").args(&args[..]).output()?;
    // empty output when not found
    Ok(!output.stdout.is_empty())
}

fn start_gnirehtet(
    serial: Option<&String>,
    dns_servers: Option<&String>,
) -> Result<(), CommandExecutionError> {
    info!("Starting gnirehtet...");
    exec_adb(serial, vec!["reverse", "tcp:31416", "tcp:31416"])?;

    let mut adb_args = vec![
        "shell",
        "am",
        "startservice",
        "-a",
        "com.genymobile.gnirehtet.START",
    ];
    if let Some(dns_servers) = dns_servers {
        adb_args.append(&mut vec!["--esa", "dnsServers", dns_servers]);
    }
    exec_adb(serial, adb_args)
}

fn stop_gnirehtet(serial: Option<&String>) -> Result<(), CommandExecutionError> {
    info!("Stopping gnirehtet...");
    exec_adb(
        serial,
        vec![
            "shell",
            "am",
            "startservice",
            "-a",
            "com.genymobile.gnirehtet.STOP",
        ],
    )
}

fn relay() -> Result<(), CommandExecutionError> {
    relaylib::relay()?;
    Ok(())
}

fn print_usage() {
    let mut msg = "Syntax: gnirehtet (".to_string();
    msg.push_str(COMMANDS[0].command());
    for command in &COMMANDS[1..] {
        msg.push('|');
        msg.push_str(command.command());
    }
    msg.push_str(") ...\n");
    for &command in COMMANDS {
        msg.push('\n');
        append_command_usage(&mut msg, command);
    }
    eprint!("{}", msg);
}

fn append_command_usage(msg: &mut String, command: &Command) {
    msg.push_str("  gnirehtet ");
    msg.push_str(command.command());
    let accepted_parameters = command.accepted_parameters();
    if (accepted_parameters & cli_args::PARAM_SERIAL) != 0 {
        msg.push_str(" [serial]");
    }
    if (accepted_parameters & cli_args::PARAM_DNS_SERVERS) != 0 {
        msg.push_str(" [-d DNS[,DNS2,...]]");
    }
    msg.push('\n');
    for desc_line in command.description().split('\n') {
        msg.push_str("      ");
        msg.push_str(desc_line);
        msg.push('\n');
    }
}

fn print_command_usage(command: &Command) {
    let mut msg = String::new();
    append_command_usage(&mut msg, command);
    eprint!("{}", msg);
}

fn main() {
    SimpleLogger::init().unwrap();
    let mut args = env::args();
    // args.nth(1) will consume the two first arguments (the binary name and the command name)
    if let Some(command_name) = args.nth(1) {
        let command = COMMANDS.iter().find(
            |&&command| command.command() == command_name,
        );
        match command {
            Some(&command) => {
                // args now contains only the command parameters
                let arguments =
                    CommandLineArguments::parse(command.accepted_parameters(), args.collect());
                match arguments {
                    Ok(arguments) => {
                        if let Err(err) = command.execute(&arguments) {
                            error!("Execution error: {}", err);
                        }
                    }
                    Err(err) => {
                        error!("{}", err);
                        print_command_usage(command);
                    }
                }
            }
            None => {
                if command_name == "rt" {
                    error!(
                        "The 'rt' command has been renamed to 'run'. Try 'gnirehtet run' instead."
                    );
                    print_command_usage(&RunCommand);
                } else {
                    error!("Unknown command: {}", command_name);
                    print_usage();
                }
            }
        }
    } else {
        print_usage();
    }
}
<commit_msg>Exit with non-zero status on error<commit_after>/*
 * Copyright (C) 2017 Genymobile
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

extern crate chrono;
extern crate ctrlc;
#[macro_use]
extern crate log;
extern crate relaylib;

mod cli_args;
mod logger;

use std::env;
use cli_args::CommandLineArguments;
use logger::SimpleLogger;
use std::error;
use std::io;
use std::fmt;
#[cfg(unix)]
use std::os::unix::process::ExitStatusExt;
use std::process::{self, ExitStatus, exit};
use std::sync::{Arc, Mutex, Condvar};
use std::thread;
use std::time::Duration;

const TAG: &'static str = "Main";

const COMMANDS: &[&'static Command] = &[
    &InstallCommand,
    &UninstallCommand,
    &ReinstallCommand,
    &RunCommand,
    &StartCommand,
    &StopCommand,
    &RestartCommand,
    &RelayCommand,
];

trait Command {
    fn command(&self) -> &'static str;
    fn accepted_parameters(&self) -> u8;
    fn description(&self) -> &'static str;
    fn execute(&self, args: &CommandLineArguments) -> Result<(), CommandExecutionError>;
}

struct InstallCommand;
struct UninstallCommand;
struct ReinstallCommand;
struct RunCommand;
struct StartCommand;
struct StopCommand;
struct RestartCommand;
struct RelayCommand;

impl Command for InstallCommand {
    fn command(&self) -> &'static str {
        "install"
    }

    fn accepted_parameters(&self) -> u8 {
        cli_args::PARAM_SERIAL
    }

    fn description(&self) -> &'static str {
        "Install the client on the Android device and exit.\n\
        If several devices are connected via adb, then serial must be\n\
        specified."
    }

    fn execute(&self, args: &CommandLineArguments) -> Result<(), CommandExecutionError> {
        info!("Installing gnirehtet...");
        exec_adb(args.serial(), vec!["install", "-r", "gnirehtet.apk"])
    }
}

impl Command for UninstallCommand {
    fn command(&self) -> &'static str {
        "uninstall"
    }

    fn accepted_parameters(&self) -> u8 {
        cli_args::PARAM_SERIAL
    }

    fn description(&self) -> &'static str {
        "Uninstall the client from the Android device and exit.\n\
        If several devices are connected via adb, then serial must be\n\
        specified."
    }

    fn execute(&self, args: &CommandLineArguments) -> Result<(), CommandExecutionError> {
        info!("Uninstalling gnirehtet...");
        exec_adb(args.serial(), vec!["uninstall", "com.genymobile.gnirehtet"])
    }
}

impl Command for ReinstallCommand {
    fn command(&self) -> &'static str {
        "reinstall"
    }

    fn accepted_parameters(&self) -> u8 {
        cli_args::PARAM_SERIAL
    }

    fn description(&self) -> &'static str {
        "Uninstall then install."
    }

    fn execute(&self, args: &CommandLineArguments) -> Result<(), CommandExecutionError> {
        UninstallCommand.execute(args)?;
        InstallCommand.execute(args)?;
        Ok(())
    }
}

impl Command for RunCommand {
    fn command(&self) -> &'static str {
        "run"
    }

    fn accepted_parameters(&self) -> u8 {
        cli_args::PARAM_SERIAL | cli_args::PARAM_DNS_SERVERS
    }

    fn description(&self) -> &'static str {
        "Enable reverse tethering for exactly one device:\n  \
          - install the client if necessary;\n  \
          - start the client;\n  \
          - start the relay server;\n  \
          - on Ctrl+C, stop both the relay server and the client."
    }

    fn execute(&self, args: &CommandLineArguments) -> Result<(), CommandExecutionError> {
        if !is_gnirehtet_installed(args.serial())? {
            InstallCommand.execute(args)?;
            // wait a bit after the app is installed so that intent actions are correctly
            // registered
            thread::sleep(Duration::from_millis(500));
        }

        {
            // start in parallel so that the relay server is ready when the client connects
            let serial = args.serial().cloned();
            let dns_servers = args.dns_servers().cloned();
            thread::spawn(move || if let Err(err) = start_gnirehtet(
                serial.as_ref(),
                dns_servers.as_ref(),
            )
            {
                error!("Cannot start gnirehtet: {}", err);
            });
        }

        let pair = Arc::new((Mutex::new(false), Condvar::new()));

        let serial = args.serial().cloned();
        let handler_pair = pair.clone();
        ctrlc::set_handler(move || {
            relaylib::interrupt::set_interrupted_by_user();
            if let Err(err) = stop_gnirehtet(serial.as_ref()) {
                error!("Cannot stop gnirehtet: {}", err);
            }

            let (ref lock, ref cvar) = *handler_pair;
            *lock.lock().unwrap() = true;
            cvar.notify_one();
        }).expect("Error setting Ctrl-C handler");

        match relay() {
            Err(CommandExecutionError::Io(ref err)) if err.kind() == io::ErrorKind::Interrupted => {
                warn!(target: TAG, "Relay server interrupted");
                // wait the ctrlc handler to complete
                let (ref lock, ref cvar) = *pair;
                let mut complete = lock.lock().unwrap();
                while !*complete {
                    complete = cvar.wait(complete).unwrap();
                }
                Ok(())
            }
            Err(ref err) => {
                panic!("Cannot relay: {}", err);
            }
            ok => ok,
        }
    }
}

impl Command for StartCommand {
    fn command(&self) -> &'static str {
        "start"
    }

    fn accepted_parameters(&self) -> u8 {
        cli_args::PARAM_SERIAL | cli_args::PARAM_DNS_SERVERS
    }

    fn description(&self) -> &'static str {
        "Start a client on the Android device and exit.\n\
        If several devices are connected via adb, then serial must be\n\
        specified.\n\
        If -d is given, then make the Android device use the specified\n\
        DNS server(s). Otherwise, use 8.8.8.8 (Google public DNS).\n\
        If the client is already started, then do nothing, and ignore\n\
        DNS servers parameter.\n\
        To use the host 'localhost' as DNS, use 10.0.2.2."
    }

    fn execute(&self, args: &CommandLineArguments) -> Result<(), CommandExecutionError> {
        start_gnirehtet(args.serial(), args.dns_servers())
    }
}

impl Command for StopCommand {
    fn command(&self) -> &'static str {
        "stop"
    }

    fn accepted_parameters(&self) -> u8 {
        cli_args::PARAM_SERIAL
    }

    fn description(&self) -> &'static str {
        "Stop the client on the Android device and exit.\n\
        If several devices are connected via adb, then serial must be\n\
        specified."
    }

    fn execute(&self, args: &CommandLineArguments) -> Result<(), CommandExecutionError> {
        stop_gnirehtet(args.serial())
    }
}

impl Command for RestartCommand {
    fn command(&self) -> &'static str {
        "restart"
    }

    fn accepted_parameters(&self) -> u8 {
        cli_args::PARAM_SERIAL | cli_args::PARAM_DNS_SERVERS
    }

    fn description(&self) -> &'static str {
        "Stop then start."
    }

    fn execute(&self, args: &CommandLineArguments) -> Result<(), CommandExecutionError> {
        StopCommand.execute(args)?;
        StartCommand.execute(args)?;
        Ok(())
    }
}

impl Command for RelayCommand {
    fn command(&self) -> &'static str {
        "relay"
    }

    fn accepted_parameters(&self) -> u8 {
        cli_args::PARAM_NONE
    }

    fn description(&self) -> &'static str {
        "Start the relay server in the current terminal."
    }

    fn execute(&self, _: &CommandLineArguments) -> Result<(), CommandExecutionError> {
        relay()
    }
}

#[derive(Debug)]
enum Termination {
    Value(i32),
    #[cfg(unix)]
    Signal(i32),
}

impl Termination {
    fn from(status: ExitStatus) -> Self {
        match status.code() {
            Some(code) => Termination::Value(code),
            #[cfg(unix)]
            None => Termination::Signal(status.signal().unwrap()),
            #[cfg(not(unix))]
            None => panic!("Unexpected signal termination on non-unix system"),
        }
    }
}

#[derive(Debug)]
struct CommandStatusError {
    command: Vec<String>,
    termination: Termination,
}

impl CommandStatusError {
    fn new(command: Vec<String>, status: ExitStatus) -> Self {
        Self {
            command: command,
            termination: Termination::from(status),
        }
    }
}

impl fmt::Display for CommandStatusError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self.termination {
            Termination::Value(code) => {
                write!(f, "Command {:?} returned with value {}", self.command, code)
            }
            #[cfg(unix)]
            Termination::Signal(sig) => {
                write!(f, "Command {:?} terminated by signal {}", self.command, sig)
            }
        }
    }
}

impl error::Error for CommandStatusError {
    fn description(&self) -> &str {
        "Execution terminated with failure"
    }

    fn cause(&self) -> Option<&error::Error> {
        None
    }
}

#[derive(Debug)]
enum CommandExecutionError {
    Io(io::Error),
    Status(CommandStatusError),
}

impl fmt::Display for CommandExecutionError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            CommandExecutionError::Io(ref err) => write!(f, "IO error: {}", err),
            CommandExecutionError::Status(ref err) => write!(f, "Status in error: {}", err),
        }
    }
}

impl error::Error for CommandExecutionError {
    fn description(&self) -> &str {
        match *self {
            CommandExecutionError::Io(ref err) => err.description(),
            CommandExecutionError::Status(ref err) => err.description(),
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        match *self {
            CommandExecutionError::Io(ref err) => Some(err),
            CommandExecutionError::Status(ref err) => Some(err),
        }
    }
}

impl From<io::Error> for CommandExecutionError {
    fn from(error: io::Error) -> Self {
        CommandExecutionError::Io(error)
    }
}

impl From<CommandStatusError> for CommandExecutionError {
    fn from(error: CommandStatusError) -> Self {
        CommandExecutionError::Status(error)
    }
}

fn create_adb_args<S: Into<String>>(serial: Option<&String>, args: Vec<S>) -> Vec<String> {
    let mut command = Vec::<String>::new();
    if let Some(serial) = serial {
        command.push("-s".into());
        command.push(serial.clone());
    }
    for arg in args {
        command.push(arg.into());
    }
    command
}

fn exec_adb<S: Into<String>>(
    serial: Option<&String>,
    args: Vec<S>,
) -> Result<(), CommandExecutionError> {
    let mut adb_args = create_adb_args(serial, args);
    info!("Execute: adb {:?}", adb_args);
    let exit_status = process::Command::new("adb").args(&adb_args[..]).status()?;
    if exit_status.success() {
        Ok(())
    } else {
        let mut cmd = vec!["adb".to_string()];
        cmd.append(&mut adb_args);
        Err(CommandStatusError::new(cmd, exit_status).into())
    }
}

fn is_gnirehtet_installed(serial: Option<&String>) -> Result<bool, CommandExecutionError> {
    let args = create_adb_args(
        serial,
        vec![
            "shell",
            "pm",
            "list",
            "packages",
            "com.genymobile.gnirehtet",
        ],
    );
    let output = process::Command::new("adb").args(&args[..]).output()?;
    // empty output when not found
    Ok(!output.stdout.is_empty())
}

fn start_gnirehtet(
    serial: Option<&String>,
    dns_servers: Option<&String>,
) -> Result<(), CommandExecutionError> {
    info!("Starting gnirehtet...");
    exec_adb(serial, vec!["reverse", "tcp:31416", "tcp:31416"])?;

    let mut adb_args = vec![
        "shell",
        "am",
        "startservice",
        "-a",
        "com.genymobile.gnirehtet.START",
    ];
    if let Some(dns_servers) = dns_servers {
        adb_args.append(&mut vec!["--esa", "dnsServers", dns_servers]);
    }
    exec_adb(serial, adb_args)
}

fn stop_gnirehtet(serial: Option<&String>) -> Result<(), CommandExecutionError> {
    info!("Stopping gnirehtet...");
    exec_adb(
        serial,
        vec![
            "shell",
            "am",
            "startservice",
            "-a",
            "com.genymobile.gnirehtet.STOP",
        ],
    )
}

fn relay() -> Result<(), CommandExecutionError> {
    relaylib::relay()?;
    Ok(())
}

fn print_usage() {
    let mut msg = "Syntax: gnirehtet (".to_string();
    msg.push_str(COMMANDS[0].command());
    for command in &COMMANDS[1..] {
        msg.push('|');
        msg.push_str(command.command());
    }
    msg.push_str(") ...\n");
    for &command in COMMANDS {
        msg.push('\n');
        append_command_usage(&mut msg, command);
    }
    eprint!("{}", msg);
}

fn append_command_usage(msg: &mut String, command: &Command) {
    msg.push_str("  gnirehtet ");
    msg.push_str(command.command());
    let accepted_parameters = command.accepted_parameters();
    if (accepted_parameters & cli_args::PARAM_SERIAL) != 0 {
        msg.push_str(" [serial]");
    }
    if (accepted_parameters & cli_args::PARAM_DNS_SERVERS) != 0 {
        msg.push_str(" [-d DNS[,DNS2,...]]");
    }
    msg.push('\n');
    for desc_line in command.description().split('\n') {
        msg.push_str("      ");
        msg.push_str(desc_line);
        msg.push('\n');
    }
}

fn print_command_usage(command: &Command) {
    let mut msg = String::new();
    append_command_usage(&mut msg, command);
    eprint!("{}", msg);
}

fn main() {
    SimpleLogger::init().unwrap();
    let mut args = env::args();
    // args.nth(1) will consume the two first arguments (the binary name and the command name)
    if let Some(command_name) = args.nth(1) {
        let command = COMMANDS.iter().find(
            |&&command| command.command() == command_name,
        );
        match command {
            Some(&command) => {
                // args now contains only the command parameters
                let arguments =
                    CommandLineArguments::parse(command.accepted_parameters(), args.collect());
                match arguments {
                    Ok(arguments) => {
                        if let Err(err) = command.execute(&arguments) {
                            error!("Execution error: {}", err);
                            exit(3);
                        }
                    }
                    Err(err) => {
                        error!("{}", err);
                        print_command_usage(command);
                        exit(2);
                    }
                }
            }
            None => {
                if command_name == "rt" {
                    error!(
                        "The 'rt' command has been renamed to 'run'. Try 'gnirehtet run' instead."
                    );
                    print_command_usage(&RunCommand);
                } else {
                    error!("Unknown command: {}", command_name);
                    print_usage();
                }
                exit(1);
            }
        }
    } else {
        print_usage();
    }
}
<|endoftext|>


<commit_before>//! Awesome compatibilty modules
use rlua::{self, Lua};
pub mod keygrabber;
pub mod mousegrabber;
pub mod awful;
pub mod button;
mod signal;
mod object;
mod class;
mod property;

pub use self::object::Object;
pub use self::keygrabber::keygrabber_handle;
pub use self::mousegrabber::mousegrabber_handle;

pub fn init(lua: &Lua) -> rlua::Result<()> {
    keygrabber::init(lua)?;
    mousegrabber::init(lua)?;
    awful::init(lua)?;
    button::init(lua)?;
    Ok(())
}
<commit_msg>Made button not public module<commit_after>//! Awesome compatibilty modules
use rlua::{self, Lua};
pub mod keygrabber;
pub mod mousegrabber;
pub mod awful;
mod button;
mod signal;
mod object;
mod class;
mod property;

pub use self::object::Object;
pub use self::keygrabber::keygrabber_handle;
pub use self::mousegrabber::mousegrabber_handle;

pub fn init(lua: &Lua) -> rlua::Result<()> {
    keygrabber::init(lua)?;
    mousegrabber::init(lua)?;
    awful::init(lua)?;
    button::init(lua)?;
    Ok(())
}
<|endoftext|>