<commit_before>#![cfg(not(windows))]

extern crate libc;

use screen::Key;
use screen::Key::*;
use std::io::{Read, Write};
use std::fs::{File, OpenOptions};
use std::os::unix::io::AsRawFd;
use std::path::*;
use std::process::Command;
use std::process::Stdio;
use std::str;
use std::iter::repeat;
use std::cmp::min;
use ansi;
use ::NEWLINE;

use std::{mem, ptr};
use std::thread;
use std::sync::mpsc::Sender;
use std::sync::mpsc::Receiver;
use std::sync::{mpsc, Arc, Mutex};

use self::libc::{sigaction, SIGWINCH, SA_SIGINFO, sighandler_t, c_int, c_ushort, c_ulong};

pub struct Screen {
    tty: Terminal,
    original_stty_state: Vec<u8>,
    start_line: u16,
    desired_rows: u16,
}

impl Screen {
    pub fn open_screen(desired_rows: u16) -> Screen {
        let mut tty = Terminal::open_terminal();
        let current_stty_state = tty.stty(&["-g"]);
        tty.initialize();
        let (_, rows) = tty.winsize().unwrap();
        let visible_choices = min(desired_rows, rows - 1);
        let start_line = rows - visible_choices - 1;
        for _ in 0..visible_choices {
            tty.write(NEWLINE.as_bytes());
        }
        tty.write(&ansi::save_cursor());

        Screen {
            tty: tty,
            original_stty_state: current_stty_state,
            start_line: start_line,
            desired_rows: desired_rows,
        }
    }

    pub fn visible_choices(&self) -> u16 {
        let (_, rows) = self.tty.winsize().unwrap();
        min(self.desired_rows, rows - 1)
    }

    pub fn width(&self) -> u16 {
        let (cols, _) = self.tty.winsize().unwrap();
        cols
    }

    fn restore_tty(&mut self) {
        self.tty.stty(&[&String::from_utf8(self.original_stty_state.clone()).unwrap()]);
    }

    fn reset_cursor(&mut self) {
        self.tty.write(&ansi::restore_cursor());

        // Writing this carriage return works around a rendering bug in
        // Neovim's terminal emulation. Without it, the cursor flies
        // around all over the place while typing, and the input prompt
        // is not rendered correctly. I have no idea why this workaround
        // is effective, or why others are not, or what the root cause
        // of the issue is (likely something involving save/restore
        // support).
        self.tty.write(b"\r");

        let num_lines = self.visible_choices();
        self.tty.write(&ansi::cursor_up(num_lines));
    }

    pub fn move_cursor_to_prompt_line(&mut self, col: u16) {
        self.reset_cursor();
        self.tty.write(&ansi::cursor_right(col));
    }

    pub fn blank_screen(&mut self) {
        self.reset_cursor();
        let blank_line = repeat(' ').take(self.width() as usize).collect::<String>();
        for _ in 0..self.visible_choices() + 1 {
            self.tty.write(blank_line.as_bytes());
        }
        self.reset_cursor();
    }

    pub fn blank_entire_screen(&mut self){
        self.tty.write(&ansi::blank_screen());
    }

    pub fn show_cursor(&mut self) {
        self.tty.write(&ansi::show_cursor());
        self.tty.flush();
    }

    pub fn hide_cursor(&mut self) {
        self.tty.write(&ansi::hide_cursor());
    }

    pub fn write(&mut self, s: &str) {
        self.tty.write(s.as_bytes());
    }

    pub fn write_red_inverted(&mut self, s: &str) {
        self.tty.write(&ansi::red());
        self.tty.write(&ansi::inverse());
        self.tty.write(s.as_bytes());
        self.tty.write(&ansi::reset());
    }

    pub fn write_red(&mut self, s: &str) {
        self.tty.write(&ansi::red());
        self.tty.write(s.as_bytes());
        self.tty.write(&ansi::reset());
    }

    pub fn write_inverted(&mut self, s: &str) {
        self.tty.write(&ansi::inverse());
        self.tty.write(s.as_bytes());
        self.tty.write(&ansi::reset());
    }

    // Return all buffered keystrokes, or the next key if buffer is empty.
    pub fn get_buffered_keys(&mut self) -> Vec<Key> {
        let mut ret = Vec::new();
        while let Ok(bytes) = self.tty.input.try_recv() {
            ret.extend(bytes);
        }
        while ret.is_empty() {
            let bytes = self.tty.input.recv().unwrap();
            if bytes == vec![0] { // SIGWINCH
                self.blank_entire_screen();
                return vec![Nothing];
            } else {
                ret.extend(bytes);
            }
        }
        Terminal::translate_bytes(ret.clone())
    }

    fn more_bytes_needed(bytes: &[u8]) -> bool {
        if let Err(_) = str::from_utf8(bytes) { true } else { false }
    }
}

impl Drop for Screen {
    fn drop(&mut self) {
        self.tty.flush();
        self.restore_tty();
    }
}

struct Terminal {
    input: Receiver<Vec<u8>>,
    input_fd: i32,
    output: File,
    output_buf: Vec<u8>,
}

static mut GLOBAL_TX: *const Arc<Mutex<Sender<Vec<u8>>>> = 0 as *const Arc<Mutex<Sender<Vec<u8>>>>;

fn set_global_tx(tx: Sender<Vec<u8>>) {
    unsafe {
        let singleton = Arc::new(Mutex::new(tx));
        GLOBAL_TX = mem::transmute(Box::new(singleton));
    }
}

fn get_global_tx() -> Sender<Vec<u8>> {
    let singleton = unsafe { (*GLOBAL_TX).clone() };
    let tx = singleton.lock().unwrap();
    tx.clone()
}

unsafe extern fn sigwinch_handler(signum: c_int) {
    if signum == SIGWINCH {
        get_global_tx().send([0].to_vec()).unwrap();
    }
}

fn register_sigwinch_handler() {
    unsafe {
        let mut action: sigaction = mem::zeroed();
        action.sa_flags = SA_SIGINFO;
        action.sa_sigaction = sigwinch_handler as sighandler_t;
        sigaction(SIGWINCH, &action, ptr::null_mut());
    }
}

impl Terminal {
    fn open_terminal() -> Terminal {
        let term_path = Path::new("/dev/tty");
        let mut input_file = File::open(&term_path).unwrap();
        let output_file = OpenOptions::new().write(true).open(&term_path).unwrap();
        let input_fd = input_file.as_raw_fd();
        let (tx, rx) = mpsc::channel();
        set_global_tx(tx);

        register_sigwinch_handler();

        thread::spawn(move || {
            loop {
                let mut buf = [0; 255];
                let tx = get_global_tx();
                if let Ok(length) = input_file.read(&mut buf) {
                    tx.send(buf[0..length].to_vec()).unwrap();
                } else {
                    tx.send([0].to_vec()).unwrap();
                }
            }
        });
        Terminal {
            input: rx,
            input_fd: input_fd,
            output: output_file,
            output_buf: Vec::new(),
        }
    }

    fn initialize(&mut self) {
        self.stty(&["raw", "-echo", "cbreak", "opost", "onlcr"]);
    }

    fn stty(&mut self, args: &[&str]) -> Vec<u8> {
        extern { fn dup2(src: c_int, dst: c_int) -> c_int;  }
        unsafe {
            // This is a hack until a replacement for InheritFd from old_io is available.
            dup2(self.input_fd, 0);
        }
        let mut process = match Command::new("stty")
            .args(args)
            .stdin(Stdio::inherit())
            .stdout(Stdio::piped())
            .spawn()
        {
            Ok(p) => p,
            Err(e) => panic!("Spawn failed: {}", e),
        };

        let exit = process.wait();

        let mut buf = Vec::new();
        if exit.unwrap().success() {
            process.stdout.as_mut().unwrap().read_to_end(&mut buf).unwrap();
            let mut str = String::from_utf8(buf).unwrap();

            // The output from `stty -g` may include a newline, which we have to strip off. Otherwise,
            // when we go to restore the tty, stty (on some platforms) will fail with an "invalid
            // argument" error.
            ::trim(&mut str);

            str.into_bytes()
        } else {
            process.stderr.as_mut().unwrap().read_to_end(&mut buf).unwrap();
            panic!(String::from_utf8(buf).unwrap());
        }
    }

    fn translate_bytes(bytes: Vec<u8>) -> Vec<Key> {
        const BEGIN_PASTE: &'static [u8] = b"\x1B[200~";
        const END_PASTE: &'static [u8] = b"\x1B[201~";

        if bytes == b"\x1B[A" || bytes == b"\x1BOA" { return vec![Up] };
        if bytes == b"\x1B[B" || bytes == b"\x1BOB" { return vec![Down] };
        if bytes == b"\x1B[Z" { return vec![ShiftTab] };

        let bs = if bytes.starts_with(BEGIN_PASTE) && bytes.ends_with(END_PASTE) {
            let start = BEGIN_PASTE.len();
            let end = bytes.len() - END_PASTE.len();
            bytes[start..end].to_vec()
        } else {
            bytes
        };

        String::from_utf8(bs)
            .unwrap()
            .chars()
            .map(Terminal::translate_char)
            .collect()
    }

    fn translate_char(c: char) -> Key {
        let numeric_char = c as u32;
        if c == '\r' {
            Enter
        } else if numeric_char == 9 {
            Tab
        } else if numeric_char == 127 {
            Backspace
        } else if numeric_char & 96 == 0 && numeric_char <= 128u32 {
            let c = c as u8;
            Control((c + 96u8) as char)
        } else {
            Char(c)
        }
    }

    fn write(&mut self, s: &[u8]) {
        self.output_buf.extend_from_slice(s);
    }

    fn writeln(&mut self, s: &str) {
        self.output_buf.extend_from_slice(s.as_bytes());
        self.output_buf.push(b'\n');
    }

    fn flush(&mut self) {
        self.output.write(&self.output_buf).unwrap();
        self.output_buf.clear();
    }

    fn winsize(&self) -> Option<(u16, u16)> {
        extern { fn ioctl(fd: c_int, request: c_ulong, ...) -> c_int; }
        #[cfg(any(target_os = "macos", target_os = "freebsd", target_os = "netbsd"))]
        const TIOCGWINSZ: c_ulong = 0x40087468;

        #[cfg(any(target_os = "linux", target_os = "android"))]
        const TIOCGWINSZ: c_ulong = 0x00005413;

        #[repr(C)]
        struct TermSize {
            rows: c_ushort,
            cols: c_ushort,
            x: c_ushort,
            y: c_ushort,
        }

        let size = TermSize { rows: 0, cols: 0, x: 0, y: 0 };
        if unsafe { ioctl(self.output.as_raw_fd(), TIOCGWINSZ, &size) } == 0 {
            Some((size.cols as u16, size.rows as u16))
        } else {
            None
        }
    }
}

#[cfg(test)]
mod tests {
    use super::Terminal;

    #[test]
    fn winsize_test() {
        // Travis-CI builds run without a tty, making this test impossible.
        if option_env!("TRAVIS").is_some() {
            // TODO: It should be made obvious from the output that this test was skipped
            return;
        }
        let term = Terminal::open_terminal();
        let (cols, rows) = term.winsize().expect("Failed to get window size!");
        // We don't know the window size a priori, but we can at least
        // assert that it is within some kind of sensible range.
        assert!(cols > 40);
        assert!(rows > 40);
        assert!(cols < 1000);
        assert!(rows < 1000);
    }
}
<commit_msg>Add OpenBSD support<commit_after>#![cfg(not(windows))]

extern crate libc;

use screen::Key;
use screen::Key::*;
use std::io::{Read, Write};
use std::fs::{File, OpenOptions};
use std::os::unix::io::AsRawFd;
use std::path::*;
use std::process::Command;
use std::process::Stdio;
use std::str;
use std::iter::repeat;
use std::cmp::min;
use ansi;
use ::NEWLINE;

use std::{mem, ptr};
use std::thread;
use std::sync::mpsc::Sender;
use std::sync::mpsc::Receiver;
use std::sync::{mpsc, Arc, Mutex};

use self::libc::{sigaction, SIGWINCH, SA_SIGINFO, sighandler_t, c_int, c_ushort, c_ulong};

pub struct Screen {
    tty: Terminal,
    original_stty_state: Vec<u8>,
    start_line: u16,
    desired_rows: u16,
}

impl Screen {
    pub fn open_screen(desired_rows: u16) -> Screen {
        let mut tty = Terminal::open_terminal();
        let current_stty_state = tty.stty(&["-g"]);
        tty.initialize();
        let (_, rows) = tty.winsize().unwrap();
        let visible_choices = min(desired_rows, rows - 1);
        let start_line = rows - visible_choices - 1;
        for _ in 0..visible_choices {
            tty.write(NEWLINE.as_bytes());
        }
        tty.write(&ansi::save_cursor());

        Screen {
            tty: tty,
            original_stty_state: current_stty_state,
            start_line: start_line,
            desired_rows: desired_rows,
        }
    }

    pub fn visible_choices(&self) -> u16 {
        let (_, rows) = self.tty.winsize().unwrap();
        min(self.desired_rows, rows - 1)
    }

    pub fn width(&self) -> u16 {
        let (cols, _) = self.tty.winsize().unwrap();
        cols
    }

    fn restore_tty(&mut self) {
        self.tty.stty(&[&String::from_utf8(self.original_stty_state.clone()).unwrap()]);
    }

    fn reset_cursor(&mut self) {
        self.tty.write(&ansi::restore_cursor());

        // Writing this carriage return works around a rendering bug in
        // Neovim's terminal emulation. Without it, the cursor flies
        // around all over the place while typing, and the input prompt
        // is not rendered correctly. I have no idea why this workaround
        // is effective, or why others are not, or what the root cause
        // of the issue is (likely something involving save/restore
        // support).
        self.tty.write(b"\r");

        let num_lines = self.visible_choices();
        self.tty.write(&ansi::cursor_up(num_lines));
    }

    pub fn move_cursor_to_prompt_line(&mut self, col: u16) {
        self.reset_cursor();
        self.tty.write(&ansi::cursor_right(col));
    }

    pub fn blank_screen(&mut self) {
        self.reset_cursor();
        let blank_line = repeat(' ').take(self.width() as usize).collect::<String>();
        for _ in 0..self.visible_choices() + 1 {
            self.tty.write(blank_line.as_bytes());
        }
        self.reset_cursor();
    }

    pub fn blank_entire_screen(&mut self){
        self.tty.write(&ansi::blank_screen());
    }

    pub fn show_cursor(&mut self) {
        self.tty.write(&ansi::show_cursor());
        self.tty.flush();
    }

    pub fn hide_cursor(&mut self) {
        self.tty.write(&ansi::hide_cursor());
    }

    pub fn write(&mut self, s: &str) {
        self.tty.write(s.as_bytes());
    }

    pub fn write_red_inverted(&mut self, s: &str) {
        self.tty.write(&ansi::red());
        self.tty.write(&ansi::inverse());
        self.tty.write(s.as_bytes());
        self.tty.write(&ansi::reset());
    }

    pub fn write_red(&mut self, s: &str) {
        self.tty.write(&ansi::red());
        self.tty.write(s.as_bytes());
        self.tty.write(&ansi::reset());
    }

    pub fn write_inverted(&mut self, s: &str) {
        self.tty.write(&ansi::inverse());
        self.tty.write(s.as_bytes());
        self.tty.write(&ansi::reset());
    }

    // Return all buffered keystrokes, or the next key if buffer is empty.
    pub fn get_buffered_keys(&mut self) -> Vec<Key> {
        let mut ret = Vec::new();
        while let Ok(bytes) = self.tty.input.try_recv() {
            ret.extend(bytes);
        }
        while ret.is_empty() {
            let bytes = self.tty.input.recv().unwrap();
            if bytes == vec![0] { // SIGWINCH
                self.blank_entire_screen();
                return vec![Nothing];
            } else {
                ret.extend(bytes);
            }
        }
        Terminal::translate_bytes(ret.clone())
    }

    fn more_bytes_needed(bytes: &[u8]) -> bool {
        if let Err(_) = str::from_utf8(bytes) { true } else { false }
    }
}

impl Drop for Screen {
    fn drop(&mut self) {
        self.tty.flush();
        self.restore_tty();
    }
}

struct Terminal {
    input: Receiver<Vec<u8>>,
    input_fd: i32,
    output: File,
    output_buf: Vec<u8>,
}

static mut GLOBAL_TX: *const Arc<Mutex<Sender<Vec<u8>>>> = 0 as *const Arc<Mutex<Sender<Vec<u8>>>>;

fn set_global_tx(tx: Sender<Vec<u8>>) {
    unsafe {
        let singleton = Arc::new(Mutex::new(tx));
        GLOBAL_TX = mem::transmute(Box::new(singleton));
    }
}

fn get_global_tx() -> Sender<Vec<u8>> {
    let singleton = unsafe { (*GLOBAL_TX).clone() };
    let tx = singleton.lock().unwrap();
    tx.clone()
}

unsafe extern fn sigwinch_handler(signum: c_int) {
    if signum == SIGWINCH {
        get_global_tx().send([0].to_vec()).unwrap();
    }
}

fn register_sigwinch_handler() {
    unsafe {
        let mut action: sigaction = mem::zeroed();
        action.sa_flags = SA_SIGINFO;
        action.sa_sigaction = sigwinch_handler as sighandler_t;
        sigaction(SIGWINCH, &action, ptr::null_mut());
    }
}

impl Terminal {
    fn open_terminal() -> Terminal {
        let term_path = Path::new("/dev/tty");
        let mut input_file = File::open(&term_path).unwrap();
        let output_file = OpenOptions::new().write(true).open(&term_path).unwrap();
        let input_fd = input_file.as_raw_fd();
        let (tx, rx) = mpsc::channel();
        set_global_tx(tx);

        register_sigwinch_handler();

        thread::spawn(move || {
            loop {
                let mut buf = [0; 255];
                let tx = get_global_tx();
                if let Ok(length) = input_file.read(&mut buf) {
                    tx.send(buf[0..length].to_vec()).unwrap();
                } else {
                    tx.send([0].to_vec()).unwrap();
                }
            }
        });
        Terminal {
            input: rx,
            input_fd: input_fd,
            output: output_file,
            output_buf: Vec::new(),
        }
    }

    fn initialize(&mut self) {
        self.stty(&["raw", "-echo", "cbreak", "opost", "onlcr"]);
    }

    fn stty(&mut self, args: &[&str]) -> Vec<u8> {
        extern { fn dup2(src: c_int, dst: c_int) -> c_int;  }
        unsafe {
            // This is a hack until a replacement for InheritFd from old_io is available.
            dup2(self.input_fd, 0);
        }
        let mut process = match Command::new("stty")
            .args(args)
            .stdin(Stdio::inherit())
            .stdout(Stdio::piped())
            .spawn()
        {
            Ok(p) => p,
            Err(e) => panic!("Spawn failed: {}", e),
        };

        let exit = process.wait();

        let mut buf = Vec::new();
        if exit.unwrap().success() {
            process.stdout.as_mut().unwrap().read_to_end(&mut buf).unwrap();
            let mut str = String::from_utf8(buf).unwrap();

            // The output from `stty -g` may include a newline, which we have to strip off. Otherwise,
            // when we go to restore the tty, stty (on some platforms) will fail with an "invalid
            // argument" error.
            ::trim(&mut str);

            str.into_bytes()
        } else {
            process.stderr.as_mut().unwrap().read_to_end(&mut buf).unwrap();
            panic!(String::from_utf8(buf).unwrap());
        }
    }

    fn translate_bytes(bytes: Vec<u8>) -> Vec<Key> {
        const BEGIN_PASTE: &'static [u8] = b"\x1B[200~";
        const END_PASTE: &'static [u8] = b"\x1B[201~";

        if bytes == b"\x1B[A" || bytes == b"\x1BOA" { return vec![Up] };
        if bytes == b"\x1B[B" || bytes == b"\x1BOB" { return vec![Down] };
        if bytes == b"\x1B[Z" { return vec![ShiftTab] };

        let bs = if bytes.starts_with(BEGIN_PASTE) && bytes.ends_with(END_PASTE) {
            let start = BEGIN_PASTE.len();
            let end = bytes.len() - END_PASTE.len();
            bytes[start..end].to_vec()
        } else {
            bytes
        };

        String::from_utf8(bs)
            .unwrap()
            .chars()
            .map(Terminal::translate_char)
            .collect()
    }

    fn translate_char(c: char) -> Key {
        let numeric_char = c as u32;
        if c == '\r' {
            Enter
        } else if numeric_char == 9 {
            Tab
        } else if numeric_char == 127 {
            Backspace
        } else if numeric_char & 96 == 0 && numeric_char <= 128u32 {
            let c = c as u8;
            Control((c + 96u8) as char)
        } else {
            Char(c)
        }
    }

    fn write(&mut self, s: &[u8]) {
        self.output_buf.extend_from_slice(s);
    }

    fn writeln(&mut self, s: &str) {
        self.output_buf.extend_from_slice(s.as_bytes());
        self.output_buf.push(b'\n');
    }

    fn flush(&mut self) {
        self.output.write(&self.output_buf).unwrap();
        self.output_buf.clear();
    }

    fn winsize(&self) -> Option<(u16, u16)> {
        extern { fn ioctl(fd: c_int, request: c_ulong, ...) -> c_int; }
        #[cfg(any(target_os = "macos", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))]
        const TIOCGWINSZ: c_ulong = 0x40087468;

        #[cfg(any(target_os = "linux", target_os = "android"))]
        const TIOCGWINSZ: c_ulong = 0x00005413;

        #[repr(C)]
        struct TermSize {
            rows: c_ushort,
            cols: c_ushort,
            x: c_ushort,
            y: c_ushort,
        }

        let size = TermSize { rows: 0, cols: 0, x: 0, y: 0 };
        if unsafe { ioctl(self.output.as_raw_fd(), TIOCGWINSZ, &size) } == 0 {
            Some((size.cols as u16, size.rows as u16))
        } else {
            None
        }
    }
}

#[cfg(test)]
mod tests {
    use super::Terminal;

    #[test]
    fn winsize_test() {
        // Travis-CI builds run without a tty, making this test impossible.
        if option_env!("TRAVIS").is_some() {
            // TODO: It should be made obvious from the output that this test was skipped
            return;
        }
        let term = Terminal::open_terminal();
        let (cols, rows) = term.winsize().expect("Failed to get window size!");
        // We don't know the window size a priori, but we can at least
        // assert that it is within some kind of sensible range.
        assert!(cols > 40);
        assert!(rows > 40);
        assert!(cols < 1000);
        assert!(rows < 1000);
    }
}
<|endoftext|>


<commit_before>//! Create window.

// External crates.
use gfx;
use collections::Deque;
use collections::ringbuf::RingBuf;
use glfw;
use glfw::Context;
use gl;
use piston::input;
use piston::input::{
    keyboard,
    mouse,
};
use piston::{
    Window,
    WindowSettings,
};
use piston::shader_version::opengl::OpenGL;

/// Contains stuff for game window.
pub struct WindowGLFW {
    /// The window.
    pub window: glfw::Window,
    /// Receives events from window.
    events: Receiver<(f64, glfw::WindowEvent)>,
    /// GLFW context.
    pub glfw: glfw::Glfw,
    /// Game window settings.
    settings: WindowSettings,
    event_queue: RingBuf<input::InputEvent>,
    // Used to compute relative mouse movement.
    last_mouse_pos: Option<(f64, f64)>,
}

impl WindowGLFW {
    /// Create a new game window from an existing GLFW window.
    pub fn from_pieces(win: glfw::Window, glfw: glfw::Glfw,
                       events: Receiver<(f64, glfw::WindowEvent)>,
                       exit_on_esc: bool) -> WindowGLFW {
        win.set_key_polling(true);
        win.set_mouse_button_polling(true);
        win.set_cursor_pos_polling(true);
        win.set_scroll_polling(true);
        win.make_current();

        let (w, h) = win.get_framebuffer_size();
        let fullscreen = win.with_window_mode(|m| match m { glfw::Windowed => true, _ => false });

        WindowGLFW {
            window: win,
            events: events,
            glfw: glfw,
            settings: WindowSettings {
                title: "<unknown window title, created with from_pieces>".to_string(),
                size: [w as u32, h as u32],
                samples: 0, //unknown
                fullscreen: fullscreen,
                exit_on_esc: exit_on_esc,
            },
            event_queue: RingBuf::new(),
            last_mouse_pos: None,
        }
    }

    /// Creates a new game window for GLFW.
    pub fn new(opengl: OpenGL, settings: WindowSettings) -> WindowGLFW {
        use glfw::Context;

        // Initialize GLFW.
        let glfw = glfw::init(glfw::FAIL_ON_ERRORS).unwrap();

        let (major, minor) = opengl.get_major_minor();

        // Make sure we have the right GL version.
        glfw.window_hint(glfw::ContextVersion(major as u32, minor as u32));
        glfw.window_hint(glfw::OpenglForwardCompat(true));
        glfw.window_hint(glfw::OpenglProfile(glfw::OpenGlCoreProfile));
        glfw.window_hint(glfw::Samples(settings.samples as u32));

        // Create GLFW window.
        let (window, events) = glfw.create_window(
            settings.size[0],
            settings.size[1],
            settings.title.as_slice(), glfw::Windowed
        ).expect("Failed to create GLFW window.");
        window.set_key_polling(true);
        window.set_mouse_button_polling(true);
        window.set_cursor_pos_polling(true);
        window.set_scroll_polling(true);
        window.set_char_polling(true);
        // or polling all event
        //window.set_all_polling(true);
        window.make_current();

        // Load the OpenGL function pointers
        gl::load_with(|s| glfw.get_proc_address(s));

        WindowGLFW {
            window: window,
            events: events,
            glfw: glfw,
            settings: settings,
            event_queue: RingBuf::new(),
            last_mouse_pos: None,
        }
    }

    fn flush_messages(&mut self) {
        if self.event_queue.len() != 0 {
            return;
        }

        self.glfw.poll_events();
        for (_, event) in glfw::flush_messages(&self.events) {
            match event {
                glfw::KeyEvent(glfw::KeyEscape, _, glfw::Press, _)
                if self.settings.exit_on_esc => {
                    self.window.set_should_close(true);
                }
                glfw::CharEvent(ch) => {
                    self.event_queue.push(input::Text(ch.to_string()));
                }
                glfw::KeyEvent(key, _, glfw::Press, _) => {
                    self.event_queue.push(
                        input::Press(input::Keyboard(glfw_map_key(key)))
                    );
                }
                glfw::KeyEvent(key, _, glfw::Release, _) => {
                    self.event_queue.push(
                        input::Release(input::Keyboard(glfw_map_key(key)))
                    );
                }
                glfw::MouseButtonEvent(button, glfw::Press, _) => {
                    self.event_queue.push(
                        input::Press(input::Mouse(glfw_map_mouse(button)))
                    );
                }
                glfw::MouseButtonEvent(button, glfw::Release, _) => {
                    self.event_queue.push(
                        input::Release(input::Mouse(glfw_map_mouse(button)))
                    );
                }
                glfw::CursorPosEvent(x, y) => {
                    self.event_queue.push(input::Move(input::MouseCursor(x, y)));
                    match self.last_mouse_pos {
                        Some((lx, ly)) => {
                            self.event_queue.push(
                                input::Move(input::MouseRelative(x - lx, y - ly))
                            )
                        }
                        None => {}
                    };
                    self.last_mouse_pos = Some((x, y));
                }
                glfw::ScrollEvent(x, y) => {
                    self.event_queue.push(input::Move(input::MouseScroll(x, y)));
                }
                _ => {}
            }
        }
    }

    /// Creates a gfx device and frame.
    pub fn gfx(&self) -> (gfx::GlDevice, gfx::Frame) {
        let device = gfx::GlDevice::new(|s|
            self.glfw.get_proc_address(s)
        );
        let (w, h) = self.get_size();
        let frame = gfx::Frame::new(w as u16, h as u16);
        (device, frame)
    }
}

impl Window for WindowGLFW {
    fn get_settings<'a>(&'a self) -> &'a WindowSettings {
        &self.settings
    }

    fn get_draw_size(&self) -> (u32, u32) {
        let (w, h) = self.window.get_framebuffer_size();
        (w as u32, h as u32)
    }

    fn should_close(&self) -> bool {
        self.window.should_close()
    }

    fn close(&mut self) {
        self.window.set_should_close(true);
    }

    fn swap_buffers(&self) {
        use glfw::Context;

        self.window.swap_buffers();
    }

    fn capture_cursor(&mut self, enabled: bool) {
        if enabled {
            self.window.set_cursor_mode(glfw::CursorDisabled)
        } else {
            self.window.set_cursor_mode(glfw::CursorNormal);
            self.last_mouse_pos = None;
        }
    }

    fn poll_event(&mut self) -> Option<input::InputEvent> {
        self.flush_messages();

        if self.event_queue.len() != 0 {
            self.event_queue.pop_front()
        } else {
            None
        }
    }
}

fn glfw_map_key(keycode: glfw::Key) -> keyboard::Key {
    match keycode {
        glfw::Key0 => keyboard::D0,
        glfw::Key1 => keyboard::D1,
        glfw::Key2 => keyboard::D2,
        glfw::Key3 => keyboard::D3,
        glfw::Key4 => keyboard::D4,
        glfw::Key5 => keyboard::D5,
        glfw::Key6 => keyboard::D6,
        glfw::Key7 => keyboard::D7,
        glfw::Key8 => keyboard::D8,
        glfw::Key9 => keyboard::D9,
        glfw::KeyA => keyboard::A,
        glfw::KeyB => keyboard::B,
        glfw::KeyC => keyboard::C,
        glfw::KeyD => keyboard::D,
        glfw::KeyE => keyboard::E,
        glfw::KeyF => keyboard::F,
        glfw::KeyG => keyboard::G,
        glfw::KeyH => keyboard::H,
        glfw::KeyI => keyboard::I,
        glfw::KeyJ => keyboard::J,
        glfw::KeyK => keyboard::K,
        glfw::KeyL => keyboard::L,
        glfw::KeyM => keyboard::M,
        glfw::KeyN => keyboard::N,
        glfw::KeyO => keyboard::O,
        glfw::KeyP => keyboard::P,
        glfw::KeyQ => keyboard::Q,
        glfw::KeyR => keyboard::R,
        glfw::KeyS => keyboard::S,
        glfw::KeyT => keyboard::T,
        glfw::KeyU => keyboard::U,
        glfw::KeyV => keyboard::V,
        glfw::KeyW => keyboard::W,
        glfw::KeyX => keyboard::X,
        glfw::KeyY => keyboard::Y,
        glfw::KeyZ => keyboard::Z,
        glfw::KeyApostrophe => keyboard::Unknown,
        glfw::KeyBackslash => keyboard::Backslash,
        glfw::KeyBackspace => keyboard::Backspace,
        glfw::KeyCapsLock => keyboard::CapsLock,
        glfw::KeyDelete => keyboard::Delete,
        glfw::KeyComma => keyboard::Comma,
        glfw::KeyDown => keyboard::Down,
        glfw::KeyEnd => keyboard::End,
        glfw::KeyEnter => keyboard::Return,
        glfw::KeyEqual => keyboard::Equals,
        glfw::KeyEscape => keyboard::Escape,
        glfw::KeyF1 => keyboard::F1,
        glfw::KeyF2 => keyboard::F2,
        glfw::KeyF3 => keyboard::F3,
        glfw::KeyF4 => keyboard::F4,
        glfw::KeyF5 => keyboard::F5,
        glfw::KeyF6 => keyboard::F6,
        glfw::KeyF7 => keyboard::F7,
        glfw::KeyF8 => keyboard::F8,
        glfw::KeyF9 => keyboard::F9,
        glfw::KeyF10 => keyboard::F10,
        glfw::KeyF11 => keyboard::F11,
        glfw::KeyF12 => keyboard::F12,
        glfw::KeyF13 => keyboard::F13,
        glfw::KeyF14 => keyboard::F14,
        glfw::KeyF15 => keyboard::F15,
        glfw::KeyF16 => keyboard::F16,
        glfw::KeyF17 => keyboard::F17,
        glfw::KeyF18 => keyboard::F18,
        glfw::KeyF19 => keyboard::F19,
        glfw::KeyF20 => keyboard::F20,
        glfw::KeyF21 => keyboard::F21,
        glfw::KeyF22 => keyboard::F22,
        glfw::KeyF23 => keyboard::F23,
        glfw::KeyF24 => keyboard::F24,
        // Possibly next code.
        glfw::KeyF25 => keyboard::Unknown,
        glfw::KeyKp0 => keyboard::NumPad0,
        glfw::KeyKp1 => keyboard::NumPad1,
        glfw::KeyKp2 => keyboard::NumPad2,
        glfw::KeyKp3 => keyboard::NumPad3,
        glfw::KeyKp4 => keyboard::NumPad4,
        glfw::KeyKp5 => keyboard::NumPad5,
        glfw::KeyKp6 => keyboard::NumPad6,
        glfw::KeyKp7 => keyboard::NumPad7,
        glfw::KeyKp8 => keyboard::NumPad8,
        glfw::KeyKp9 => keyboard::NumPad9,
        glfw::KeyKpDecimal => keyboard::NumPadDecimal,
        glfw::KeyKpDivide => keyboard::NumPadDivide,
        glfw::KeyKpMultiply => keyboard::NumPadMultiply,
        glfw::KeyKpSubtract => keyboard::NumPadMinus,
        glfw::KeyKpAdd => keyboard::NumPadPlus,
        glfw::KeyKpEnter => keyboard::NumPadEnter,
        glfw::KeyKpEqual => keyboard::NumPadEquals,
        glfw::KeyLeftShift => keyboard::LShift,
        glfw::KeyLeftControl => keyboard::LCtrl,
        glfw::KeyLeftAlt => keyboard::LAlt,
        glfw::KeyLeftSuper => keyboard::LGui,
        glfw::KeyRightShift => keyboard::RShift,
        glfw::KeyRightControl => keyboard::RCtrl,
        glfw::KeyRightAlt => keyboard::RAlt,
        glfw::KeyRightSuper => keyboard::RGui,
        // Map to backslash?
        glfw::KeyGraveAccent => keyboard::Unknown,
        glfw::KeyHome => keyboard::Home,
        glfw::KeyInsert => keyboard::Insert,
        glfw::KeyLeft => keyboard::Left,
        glfw::KeyLeftBracket => keyboard::LeftBracket,
        glfw::KeyMenu => keyboard::Menu,
        glfw::KeyMinus => keyboard::Minus,
        glfw::KeyNumLock => keyboard::NumLockClear,
        glfw::KeyPageDown => keyboard::PageDown,
        glfw::KeyPageUp => keyboard::PageUp,
        glfw::KeyPause => keyboard::Pause,
        glfw::KeyPeriod => keyboard::Period,
        glfw::KeyPrintScreen => keyboard::PrintScreen,
        glfw::KeyRight => keyboard::Right,
        glfw::KeyRightBracket => keyboard::RightBracket,
        glfw::KeyScrollLock => keyboard::ScrollLock,
        glfw::KeySemicolon => keyboard::Semicolon,
        glfw::KeySlash => keyboard::Slash,
        glfw::KeySpace => keyboard::Space,
        glfw::KeyTab => keyboard::Tab,
        glfw::KeyUp => keyboard::Up,
        glfw::KeyWorld1 => keyboard::Unknown,
        glfw::KeyWorld2 => keyboard::Unknown,
        // _ => keyboard::Unknown,
    }
}

fn glfw_map_mouse(mouse_button: glfw::MouseButton) -> mouse::Button {
    match mouse_button {
        glfw::MouseButton1 => mouse::Left,
        glfw::MouseButton2 => mouse::Right,
        glfw::MouseButton3 => mouse::Middle,
        glfw::MouseButton4 => mouse::X1,
        glfw::MouseButton5 => mouse::X2,
        glfw::MouseButton6 => mouse::Button6,
        glfw::MouseButton7 => mouse::Button7,
        glfw::MouseButton8 => mouse::Button8,
    }
}
<commit_msg>Handle resize event<commit_after>//! Create window.

// External crates.
use gfx;
use collections::Deque;
use collections::ringbuf::RingBuf;
use glfw;
use glfw::Context;
use gl;
use piston::input;
use piston::input::{
    keyboard,
    mouse,
};
use piston::{
    Window,
    WindowSettings,
};
use piston::shader_version::opengl::OpenGL;

/// Contains stuff for game window.
pub struct WindowGLFW {
    /// The window.
    pub window: glfw::Window,
    /// Receives events from window.
    events: Receiver<(f64, glfw::WindowEvent)>,
    /// GLFW context.
    pub glfw: glfw::Glfw,
    /// Game window settings.
    settings: WindowSettings,
    event_queue: RingBuf<input::InputEvent>,
    // Used to compute relative mouse movement.
    last_mouse_pos: Option<(f64, f64)>,
}

impl WindowGLFW {
    /// Create a new game window from an existing GLFW window.
    pub fn from_pieces(win: glfw::Window, glfw: glfw::Glfw,
                       events: Receiver<(f64, glfw::WindowEvent)>,
                       exit_on_esc: bool) -> WindowGLFW {
        win.set_all_polling(true);
        win.make_current();

        let (w, h) = win.get_framebuffer_size();
        let fullscreen = win.with_window_mode(|m| match m { glfw::Windowed => true, _ => false });

        WindowGLFW {
            window: win,
            events: events,
            glfw: glfw,
            settings: WindowSettings {
                title: "<unknown window title, created with from_pieces>".to_string(),
                size: [w as u32, h as u32],
                samples: 0, //unknown
                fullscreen: fullscreen,
                exit_on_esc: exit_on_esc,
            },
            event_queue: RingBuf::new(),
            last_mouse_pos: None,
        }
    }

    /// Creates a new game window for GLFW.
    pub fn new(opengl: OpenGL, settings: WindowSettings) -> WindowGLFW {
        use glfw::Context;

        // Initialize GLFW.
        let glfw = glfw::init(glfw::FAIL_ON_ERRORS).unwrap();

        let (major, minor) = opengl.get_major_minor();

        // Make sure we have the right GL version.
        glfw.window_hint(glfw::ContextVersion(major as u32, minor as u32));
        glfw.window_hint(glfw::OpenglForwardCompat(true));
        glfw.window_hint(glfw::OpenglProfile(glfw::OpenGlCoreProfile));
        glfw.window_hint(glfw::Samples(settings.samples as u32));

        // Create GLFW window.
        let (window, events) = glfw.create_window(
            settings.size[0],
            settings.size[1],
            settings.title.as_slice(), glfw::Windowed
        ).expect("Failed to create GLFW window.");
        window.set_all_polling(true);
        window.make_current();

        // Load the OpenGL function pointers
        gl::load_with(|s| glfw.get_proc_address(s));

        WindowGLFW {
            window: window,
            events: events,
            glfw: glfw,
            settings: settings,
            event_queue: RingBuf::new(),
            last_mouse_pos: None,
        }
    }

    fn flush_messages(&mut self) {
        if self.event_queue.len() != 0 {
            return;
        }

        self.glfw.poll_events();
        for (_, event) in glfw::flush_messages(&self.events) {
            match event {
                glfw::KeyEvent(glfw::KeyEscape, _, glfw::Press, _)
                if self.settings.exit_on_esc => {
                    self.window.set_should_close(true);
                }
                glfw::CharEvent(ch) => {
                    self.event_queue.push(input::Text(ch.to_string()));
                }
                glfw::KeyEvent(key, _, glfw::Press, _) => {
                    self.event_queue.push(
                        input::Press(input::Keyboard(glfw_map_key(key)))
                    );
                }
                glfw::KeyEvent(key, _, glfw::Release, _) => {
                    self.event_queue.push(
                        input::Release(input::Keyboard(glfw_map_key(key)))
                    );
                }
                glfw::MouseButtonEvent(button, glfw::Press, _) => {
                    self.event_queue.push(
                        input::Press(input::Mouse(glfw_map_mouse(button)))
                    );
                }
                glfw::MouseButtonEvent(button, glfw::Release, _) => {
                    self.event_queue.push(
                        input::Release(input::Mouse(glfw_map_mouse(button)))
                    );
                }
                glfw::CursorPosEvent(x, y) => {
                    self.event_queue.push(input::Move(input::MouseCursor(x, y)));
                    match self.last_mouse_pos {
                        Some((lx, ly)) => {
                            self.event_queue.push(
                                input::Move(input::MouseRelative(x - lx, y - ly))
                            )
                        }
                        None => {}
                    };
                    self.last_mouse_pos = Some((x, y));
                }
                glfw::ScrollEvent(x, y) => {
                    self.event_queue.push(input::Move(input::MouseScroll(x, y)));
                }
                glfw::SizeEvent(w, h) => {
                    self.event_queue.push(input::Resize(w as u32, h as u32));
                }
                _ => {}
            }
        }
    }

    /// Creates a gfx device and frame.
    pub fn gfx(&self) -> (gfx::GlDevice, gfx::Frame) {
        let device = gfx::GlDevice::new(|s|
            self.glfw.get_proc_address(s)
        );
        let (w, h) = self.get_size();
        let frame = gfx::Frame::new(w as u16, h as u16);
        (device, frame)
    }
}

impl Window for WindowGLFW {
    fn get_settings<'a>(&'a self) -> &'a WindowSettings {
        &self.settings
    }

    fn get_draw_size(&self) -> (u32, u32) {
        let (w, h) = self.window.get_framebuffer_size();
        (w as u32, h as u32)
    }

    fn should_close(&self) -> bool {
        self.window.should_close()
    }

    fn close(&mut self) {
        self.window.set_should_close(true);
    }

    fn swap_buffers(&self) {
        use glfw::Context;

        self.window.swap_buffers();
    }

    fn capture_cursor(&mut self, enabled: bool) {
        if enabled {
            self.window.set_cursor_mode(glfw::CursorDisabled)
        } else {
            self.window.set_cursor_mode(glfw::CursorNormal);
            self.last_mouse_pos = None;
        }
    }

    fn poll_event(&mut self) -> Option<input::InputEvent> {
        self.flush_messages();

        if self.event_queue.len() != 0 {
            self.event_queue.pop_front()
        } else {
            None
        }
    }
}

fn glfw_map_key(keycode: glfw::Key) -> keyboard::Key {
    match keycode {
        glfw::Key0 => keyboard::D0,
        glfw::Key1 => keyboard::D1,
        glfw::Key2 => keyboard::D2,
        glfw::Key3 => keyboard::D3,
        glfw::Key4 => keyboard::D4,
        glfw::Key5 => keyboard::D5,
        glfw::Key6 => keyboard::D6,
        glfw::Key7 => keyboard::D7,
        glfw::Key8 => keyboard::D8,
        glfw::Key9 => keyboard::D9,
        glfw::KeyA => keyboard::A,
        glfw::KeyB => keyboard::B,
        glfw::KeyC => keyboard::C,
        glfw::KeyD => keyboard::D,
        glfw::KeyE => keyboard::E,
        glfw::KeyF => keyboard::F,
        glfw::KeyG => keyboard::G,
        glfw::KeyH => keyboard::H,
        glfw::KeyI => keyboard::I,
        glfw::KeyJ => keyboard::J,
        glfw::KeyK => keyboard::K,
        glfw::KeyL => keyboard::L,
        glfw::KeyM => keyboard::M,
        glfw::KeyN => keyboard::N,
        glfw::KeyO => keyboard::O,
        glfw::KeyP => keyboard::P,
        glfw::KeyQ => keyboard::Q,
        glfw::KeyR => keyboard::R,
        glfw::KeyS => keyboard::S,
        glfw::KeyT => keyboard::T,
        glfw::KeyU => keyboard::U,
        glfw::KeyV => keyboard::V,
        glfw::KeyW => keyboard::W,
        glfw::KeyX => keyboard::X,
        glfw::KeyY => keyboard::Y,
        glfw::KeyZ => keyboard::Z,
        glfw::KeyApostrophe => keyboard::Unknown,
        glfw::KeyBackslash => keyboard::Backslash,
        glfw::KeyBackspace => keyboard::Backspace,
        glfw::KeyCapsLock => keyboard::CapsLock,
        glfw::KeyDelete => keyboard::Delete,
        glfw::KeyComma => keyboard::Comma,
        glfw::KeyDown => keyboard::Down,
        glfw::KeyEnd => keyboard::End,
        glfw::KeyEnter => keyboard::Return,
        glfw::KeyEqual => keyboard::Equals,
        glfw::KeyEscape => keyboard::Escape,
        glfw::KeyF1 => keyboard::F1,
        glfw::KeyF2 => keyboard::F2,
        glfw::KeyF3 => keyboard::F3,
        glfw::KeyF4 => keyboard::F4,
        glfw::KeyF5 => keyboard::F5,
        glfw::KeyF6 => keyboard::F6,
        glfw::KeyF7 => keyboard::F7,
        glfw::KeyF8 => keyboard::F8,
        glfw::KeyF9 => keyboard::F9,
        glfw::KeyF10 => keyboard::F10,
        glfw::KeyF11 => keyboard::F11,
        glfw::KeyF12 => keyboard::F12,
        glfw::KeyF13 => keyboard::F13,
        glfw::KeyF14 => keyboard::F14,
        glfw::KeyF15 => keyboard::F15,
        glfw::KeyF16 => keyboard::F16,
        glfw::KeyF17 => keyboard::F17,
        glfw::KeyF18 => keyboard::F18,
        glfw::KeyF19 => keyboard::F19,
        glfw::KeyF20 => keyboard::F20,
        glfw::KeyF21 => keyboard::F21,
        glfw::KeyF22 => keyboard::F22,
        glfw::KeyF23 => keyboard::F23,
        glfw::KeyF24 => keyboard::F24,
        // Possibly next code.
        glfw::KeyF25 => keyboard::Unknown,
        glfw::KeyKp0 => keyboard::NumPad0,
        glfw::KeyKp1 => keyboard::NumPad1,
        glfw::KeyKp2 => keyboard::NumPad2,
        glfw::KeyKp3 => keyboard::NumPad3,
        glfw::KeyKp4 => keyboard::NumPad4,
        glfw::KeyKp5 => keyboard::NumPad5,
        glfw::KeyKp6 => keyboard::NumPad6,
        glfw::KeyKp7 => keyboard::NumPad7,
        glfw::KeyKp8 => keyboard::NumPad8,
        glfw::KeyKp9 => keyboard::NumPad9,
        glfw::KeyKpDecimal => keyboard::NumPadDecimal,
        glfw::KeyKpDivide => keyboard::NumPadDivide,
        glfw::KeyKpMultiply => keyboard::NumPadMultiply,
        glfw::KeyKpSubtract => keyboard::NumPadMinus,
        glfw::KeyKpAdd => keyboard::NumPadPlus,
        glfw::KeyKpEnter => keyboard::NumPadEnter,
        glfw::KeyKpEqual => keyboard::NumPadEquals,
        glfw::KeyLeftShift => keyboard::LShift,
        glfw::KeyLeftControl => keyboard::LCtrl,
        glfw::KeyLeftAlt => keyboard::LAlt,
        glfw::KeyLeftSuper => keyboard::LGui,
        glfw::KeyRightShift => keyboard::RShift,
        glfw::KeyRightControl => keyboard::RCtrl,
        glfw::KeyRightAlt => keyboard::RAlt,
        glfw::KeyRightSuper => keyboard::RGui,
        // Map to backslash?
        glfw::KeyGraveAccent => keyboard::Unknown,
        glfw::KeyHome => keyboard::Home,
        glfw::KeyInsert => keyboard::Insert,
        glfw::KeyLeft => keyboard::Left,
        glfw::KeyLeftBracket => keyboard::LeftBracket,
        glfw::KeyMenu => keyboard::Menu,
        glfw::KeyMinus => keyboard::Minus,
        glfw::KeyNumLock => keyboard::NumLockClear,
        glfw::KeyPageDown => keyboard::PageDown,
        glfw::KeyPageUp => keyboard::PageUp,
        glfw::KeyPause => keyboard::Pause,
        glfw::KeyPeriod => keyboard::Period,
        glfw::KeyPrintScreen => keyboard::PrintScreen,
        glfw::KeyRight => keyboard::Right,
        glfw::KeyRightBracket => keyboard::RightBracket,
        glfw::KeyScrollLock => keyboard::ScrollLock,
        glfw::KeySemicolon => keyboard::Semicolon,
        glfw::KeySlash => keyboard::Slash,
        glfw::KeySpace => keyboard::Space,
        glfw::KeyTab => keyboard::Tab,
        glfw::KeyUp => keyboard::Up,
        glfw::KeyWorld1 => keyboard::Unknown,
        glfw::KeyWorld2 => keyboard::Unknown,
        // _ => keyboard::Unknown,
    }
}

fn glfw_map_mouse(mouse_button: glfw::MouseButton) -> mouse::Button {
    match mouse_button {
        glfw::MouseButton1 => mouse::Left,
        glfw::MouseButton2 => mouse::Right,
        glfw::MouseButton3 => mouse::Middle,
        glfw::MouseButton4 => mouse::X1,
        glfw::MouseButton5 => mouse::X2,
        glfw::MouseButton6 => mouse::Button6,
        glfw::MouseButton7 => mouse::Button7,
        glfw::MouseButton8 => mouse::Button8,
    }
}
<|endoftext|>


<commit_before>/*
Copyright (C) 2018-2019 de4dot@gmail.com

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

use super::instruction::Instruction;
use wasm_bindgen::prelude::*;

/// x86 formatter that uses less code (smaller wasm files)
#[wasm_bindgen]
pub struct FastFormatter(iced_x86_rust::FastFormatter);

#[wasm_bindgen]
impl FastFormatter {
	/// Creates an x86 formatter
	///
	/// # Examples
	///
	/// ```js
	/// const assert = require("assert").strict;
	/// const { Decoder, DecoderOptions, FastFormatter } = require("iced-x86");
	///
	/// const bytes = new Uint8Array([0x62, 0xF2, 0x4F, 0xDD, 0x72, 0x50, 0x01]);
	/// const decoder = new Decoder(64, bytes, DecoderOptions.None);
	/// const instr = decoder.decode();
	///
	/// const formatter = new FastFormatter();
	/// formatter.spaceAfterOperandSeparator = true;
	/// const disasm = formatter.format(instr);
	/// assert.equal(disasm, "vcvtne2ps2bf16 zmm2{k5}{z}, zmm6, dword bcst [rax+4h]");
	///
	/// // Free wasm memory
	/// decoder.free();
	/// instr.free();
	/// formatter.free();
	/// ```
	///
	/// [`FormatterSyntax`]: enum.FormatterSyntax.html
	#[allow(clippy::new_without_default)]
	#[wasm_bindgen(constructor)]
	pub fn new() -> Self {
		Self(iced_x86_rust::FastFormatter::new())
	}

	/// Formats the whole instruction: prefixes, mnemonic, operands
	///
	/// # Arguments
	///
	/// - `instruction`: Instruction
	pub fn format(&mut self, instruction: &Instruction) -> String {
		let mut output = String::new();
		self.0.format(&instruction.0, &mut output);
		output
	}

	/// Add a space after the operand separator
	///
	/// Default | Value | Example
	/// --------|-------|--------
	/// - | `true` | `mov rax, rcx`
	/// ✔ | `false` | `mov rax,rcx`
	#[wasm_bindgen(getter)]
	#[wasm_bindgen(js_name = "spaceAfterOperandSeparator")]
	pub fn space_after_operand_separator(&self) -> bool {
		self.0.options().space_after_operand_separator()
	}

	/// Add a space after the operand separator
	///
	/// Default | Value | Example
	/// --------|-------|--------
	/// - | `true` | `mov rax, rcx`
	/// ✔ | `false` | `mov rax,rcx`
	///
	/// # Arguments
	///
	/// * `value`: New value
	#[wasm_bindgen(setter)]
	#[wasm_bindgen(js_name = "spaceAfterOperandSeparator")]
	pub fn set_space_after_operand_separator(&mut self, value: bool) {
		self.0.options_mut().set_space_after_operand_separator(value);
	}

	/// Show `RIP+displ` or the virtual address
	///
	/// Default | Value | Example
	/// --------|-------|--------
	/// - | `true` | `mov eax,[rip+12345678h]`
	/// ✔ | `false` | `mov eax,[1029384756AFBECDh]`
	#[wasm_bindgen(getter)]
	#[wasm_bindgen(js_name = "ripRelativeAddresses")]
	pub fn rip_relative_addresses(&self) -> bool {
		self.0.options().rip_relative_addresses()
	}

	/// Show `RIP+displ` or the virtual address
	///
	/// Default | Value | Example
	/// --------|-------|--------
	/// - | `true` | `mov eax,[rip+12345678h]`
	/// ✔ | `false` | `mov eax,[1029384756AFBECDh]`
	///
	/// # Arguments
	///
	/// * `value`: New value
	#[wasm_bindgen(setter)]
	#[wasm_bindgen(js_name = "ripRelativeAddresses")]
	pub fn set_rip_relative_addresses(&mut self, value: bool) {
		self.0.options_mut().set_rip_relative_addresses(value);
	}

	/// Use pseudo instructions
	///
	/// Default | Value | Example
	/// --------|-------|--------
	/// ✔ | `true` | `vcmpnltsd xmm2,xmm6,xmm3`
	/// - | `false` | `vcmpsd xmm2,xmm6,xmm3,5`
	#[wasm_bindgen(getter)]
	#[wasm_bindgen(js_name = "usePseudoOps")]
	pub fn use_pseudo_ops(&self) -> bool {
		self.0.options().use_pseudo_ops()
	}

	/// Use pseudo instructions
	///
	/// Default | Value | Example
	/// --------|-------|--------
	/// ✔ | `true` | `vcmpnltsd xmm2,xmm6,xmm3`
	/// - | `false` | `vcmpsd xmm2,xmm6,xmm3,5`
	///
	/// # Arguments
	///
	/// * `value`: New value
	#[wasm_bindgen(setter)]
	#[wasm_bindgen(js_name = "usePseudoOps")]
	pub fn set_use_pseudo_ops(&mut self, value: bool) {
		self.0.options_mut().set_use_pseudo_ops(value);
	}

	/// Show the original value after the symbol name
	///
	/// Default | Value | Example
	/// --------|-------|--------
	/// - | `true` | `mov eax,[myfield (12345678)]`
	/// ✔ | `false` | `mov eax,[myfield]`
	#[wasm_bindgen(getter)]
	#[wasm_bindgen(js_name = "showSymbolAddress")]
	pub fn show_symbol_address(&self) -> bool {
		self.0.options().show_symbol_address()
	}

	/// Show the original value after the symbol name
	///
	/// Default | Value | Example
	/// --------|-------|--------
	/// - | `true` | `mov eax,[myfield (12345678)]`
	/// ✔ | `false` | `mov eax,[myfield]`
	///
	/// # Arguments
	///
	/// * `value`: New value
	#[wasm_bindgen(setter)]
	#[wasm_bindgen(js_name = "showSymbolAddress")]
	pub fn set_show_symbol_address(&mut self, value: bool) {
		self.0.options_mut().set_show_symbol_address(value);
	}

	/// Always show the effective segment register. If the option is `false`, only show the segment register if
	/// there's a segment override prefix.
	///
	/// Default | Value | Example
	/// --------|-------|--------
	/// - | `true` | `mov eax,ds:[ecx]`
	/// ✔ | `false` | `mov eax,[ecx]`
	#[wasm_bindgen(getter)]
	#[wasm_bindgen(js_name = "alwaysShowSegmentRegister")]
	pub fn always_show_segment_register(&self) -> bool {
		self.0.options().always_show_segment_register()
	}

	/// Always show the effective segment register. If the option is `false`, only show the segment register if
	/// there's a segment override prefix.
	///
	/// Default | Value | Example
	/// --------|-------|--------
	/// - | `true` | `mov eax,ds:[ecx]`
	/// ✔ | `false` | `mov eax,[ecx]`
	///
	/// # Arguments
	///
	/// * `value`: New value
	#[wasm_bindgen(setter)]
	#[wasm_bindgen(js_name = "alwaysShowSegmentRegister")]
	pub fn set_always_show_segment_register(&mut self, value: bool) {
		self.0.options_mut().set_always_show_segment_register(value);
	}

	/// Always show memory operands' size
	///
	/// Default | Value | Example
	/// --------|-------|--------
	/// - | `true` | `mov eax,dword ptr [ebx]` / `add byte ptr [eax],0x12`
	/// ✔ | `false` | `mov eax,[ebx]` / `add byte ptr [eax],0x12`
	#[wasm_bindgen(getter)]
	#[wasm_bindgen(js_name = "alwaysShowMemorySize")]
	pub fn always_show_memory_size(&self) -> bool {
		self.0.options().always_show_memory_size()
	}

	/// Always show memory operands' size
	///
	/// Default | Value | Example
	/// --------|-------|--------
	/// - | `true` | `mov eax,dword ptr [ebx]` / `add byte ptr [eax],0x12`
	/// ✔ | `false` | `mov eax,[ebx]` / `add byte ptr [eax],0x12`
	///
	/// # Arguments
	///
	/// * `value`: New value
	#[wasm_bindgen(setter)]
	#[wasm_bindgen(js_name = "alwaysShowMemorySize")]
	pub fn set_always_show_memory_size(&mut self, value: bool) {
		self.0.options_mut().set_always_show_memory_size(value)
	}

	/// Use upper case hex digits
	///
	/// Default | Value | Example
	/// --------|-------|--------
	/// ✔ | `true` | `0xFF`
	/// - | `false` | `0xff`
	#[wasm_bindgen(getter)]
	#[wasm_bindgen(js_name = "uppercaseHex")]
	pub fn uppercase_hex(&self) -> bool {
		self.0.options().uppercase_hex()
	}

	/// Use upper case hex digits
	///
	/// Default | Value | Example
	/// --------|-------|--------
	/// ✔ | `true` | `0xFF`
	/// - | `false` | `0xff`
	///
	/// # Arguments
	///
	/// * `value`: New value
	#[wasm_bindgen(setter)]
	#[wasm_bindgen(js_name = "uppercaseHex")]
	pub fn set_uppercase_hex(&mut self, value: bool) {
		self.0.options_mut().set_uppercase_hex(value);
	}

	/// Use a hex prefix (`0x`) or a hex suffix (`h`)
	///
	/// Default | Value | Example
	/// --------|-------|--------
	/// - | `true` | `0x5A`
	/// ✔ | `false` | `5Ah`
	#[wasm_bindgen(getter)]
	#[wasm_bindgen(js_name = "useHexPrefix")]
	pub fn use_hex_prefix(&self) -> bool {
		self.0.options().use_hex_prefix()
	}

	/// Always show memory operands' size
	///
	/// Default | Value | Example
	/// --------|-------|--------
	/// - | `true` | `mov eax,dword ptr [ebx]` / `add byte ptr [eax],0x12`
	/// ✔ | `false` | `mov eax,[ebx]` / `add byte ptr [eax],0x12`
	///
	/// # Arguments
	///
	/// * `value`: New value
	#[wasm_bindgen(setter)]
	#[wasm_bindgen(js_name = "useHexPrefix")]
	pub fn set_use_hex_prefix(&mut self, value: bool) {
		self.0.options_mut().set_use_hex_prefix(value)
	}
}
<commit_msg>Add missing #[wasm_bindgen]<commit_after>/*
Copyright (C) 2018-2019 de4dot@gmail.com

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

use super::instruction::Instruction;
use wasm_bindgen::prelude::*;

/// x86 formatter that uses less code (smaller wasm files)
#[wasm_bindgen]
pub struct FastFormatter(iced_x86_rust::FastFormatter);

#[wasm_bindgen]
impl FastFormatter {
	/// Creates an x86 formatter
	///
	/// # Examples
	///
	/// ```js
	/// const assert = require("assert").strict;
	/// const { Decoder, DecoderOptions, FastFormatter } = require("iced-x86");
	///
	/// const bytes = new Uint8Array([0x62, 0xF2, 0x4F, 0xDD, 0x72, 0x50, 0x01]);
	/// const decoder = new Decoder(64, bytes, DecoderOptions.None);
	/// const instr = decoder.decode();
	///
	/// const formatter = new FastFormatter();
	/// formatter.spaceAfterOperandSeparator = true;
	/// const disasm = formatter.format(instr);
	/// assert.equal(disasm, "vcvtne2ps2bf16 zmm2{k5}{z}, zmm6, dword bcst [rax+4h]");
	///
	/// // Free wasm memory
	/// decoder.free();
	/// instr.free();
	/// formatter.free();
	/// ```
	///
	/// [`FormatterSyntax`]: enum.FormatterSyntax.html
	#[allow(clippy::new_without_default)]
	#[wasm_bindgen(constructor)]
	pub fn new() -> Self {
		Self(iced_x86_rust::FastFormatter::new())
	}

	/// Formats the whole instruction: prefixes, mnemonic, operands
	///
	/// # Arguments
	///
	/// - `instruction`: Instruction
	#[wasm_bindgen]
	pub fn format(&mut self, instruction: &Instruction) -> String {
		let mut output = String::new();
		self.0.format(&instruction.0, &mut output);
		output
	}

	/// Add a space after the operand separator
	///
	/// Default | Value | Example
	/// --------|-------|--------
	/// - | `true` | `mov rax, rcx`
	/// ✔ | `false` | `mov rax,rcx`
	#[wasm_bindgen(getter)]
	#[wasm_bindgen(js_name = "spaceAfterOperandSeparator")]
	pub fn space_after_operand_separator(&self) -> bool {
		self.0.options().space_after_operand_separator()
	}

	/// Add a space after the operand separator
	///
	/// Default | Value | Example
	/// --------|-------|--------
	/// - | `true` | `mov rax, rcx`
	/// ✔ | `false` | `mov rax,rcx`
	///
	/// # Arguments
	///
	/// * `value`: New value
	#[wasm_bindgen(setter)]
	#[wasm_bindgen(js_name = "spaceAfterOperandSeparator")]
	pub fn set_space_after_operand_separator(&mut self, value: bool) {
		self.0.options_mut().set_space_after_operand_separator(value);
	}

	/// Show `RIP+displ` or the virtual address
	///
	/// Default | Value | Example
	/// --------|-------|--------
	/// - | `true` | `mov eax,[rip+12345678h]`
	/// ✔ | `false` | `mov eax,[1029384756AFBECDh]`
	#[wasm_bindgen(getter)]
	#[wasm_bindgen(js_name = "ripRelativeAddresses")]
	pub fn rip_relative_addresses(&self) -> bool {
		self.0.options().rip_relative_addresses()
	}

	/// Show `RIP+displ` or the virtual address
	///
	/// Default | Value | Example
	/// --------|-------|--------
	/// - | `true` | `mov eax,[rip+12345678h]`
	/// ✔ | `false` | `mov eax,[1029384756AFBECDh]`
	///
	/// # Arguments
	///
	/// * `value`: New value
	#[wasm_bindgen(setter)]
	#[wasm_bindgen(js_name = "ripRelativeAddresses")]
	pub fn set_rip_relative_addresses(&mut self, value: bool) {
		self.0.options_mut().set_rip_relative_addresses(value);
	}

	/// Use pseudo instructions
	///
	/// Default | Value | Example
	/// --------|-------|--------
	/// ✔ | `true` | `vcmpnltsd xmm2,xmm6,xmm3`
	/// - | `false` | `vcmpsd xmm2,xmm6,xmm3,5`
	#[wasm_bindgen(getter)]
	#[wasm_bindgen(js_name = "usePseudoOps")]
	pub fn use_pseudo_ops(&self) -> bool {
		self.0.options().use_pseudo_ops()
	}

	/// Use pseudo instructions
	///
	/// Default | Value | Example
	/// --------|-------|--------
	/// ✔ | `true` | `vcmpnltsd xmm2,xmm6,xmm3`
	/// - | `false` | `vcmpsd xmm2,xmm6,xmm3,5`
	///
	/// # Arguments
	///
	/// * `value`: New value
	#[wasm_bindgen(setter)]
	#[wasm_bindgen(js_name = "usePseudoOps")]
	pub fn set_use_pseudo_ops(&mut self, value: bool) {
		self.0.options_mut().set_use_pseudo_ops(value);
	}

	/// Show the original value after the symbol name
	///
	/// Default | Value | Example
	/// --------|-------|--------
	/// - | `true` | `mov eax,[myfield (12345678)]`
	/// ✔ | `false` | `mov eax,[myfield]`
	#[wasm_bindgen(getter)]
	#[wasm_bindgen(js_name = "showSymbolAddress")]
	pub fn show_symbol_address(&self) -> bool {
		self.0.options().show_symbol_address()
	}

	/// Show the original value after the symbol name
	///
	/// Default | Value | Example
	/// --------|-------|--------
	/// - | `true` | `mov eax,[myfield (12345678)]`
	/// ✔ | `false` | `mov eax,[myfield]`
	///
	/// # Arguments
	///
	/// * `value`: New value
	#[wasm_bindgen(setter)]
	#[wasm_bindgen(js_name = "showSymbolAddress")]
	pub fn set_show_symbol_address(&mut self, value: bool) {
		self.0.options_mut().set_show_symbol_address(value);
	}

	/// Always show the effective segment register. If the option is `false`, only show the segment register if
	/// there's a segment override prefix.
	///
	/// Default | Value | Example
	/// --------|-------|--------
	/// - | `true` | `mov eax,ds:[ecx]`
	/// ✔ | `false` | `mov eax,[ecx]`
	#[wasm_bindgen(getter)]
	#[wasm_bindgen(js_name = "alwaysShowSegmentRegister")]
	pub fn always_show_segment_register(&self) -> bool {
		self.0.options().always_show_segment_register()
	}

	/// Always show the effective segment register. If the option is `false`, only show the segment register if
	/// there's a segment override prefix.
	///
	/// Default | Value | Example
	/// --------|-------|--------
	/// - | `true` | `mov eax,ds:[ecx]`
	/// ✔ | `false` | `mov eax,[ecx]`
	///
	/// # Arguments
	///
	/// * `value`: New value
	#[wasm_bindgen(setter)]
	#[wasm_bindgen(js_name = "alwaysShowSegmentRegister")]
	pub fn set_always_show_segment_register(&mut self, value: bool) {
		self.0.options_mut().set_always_show_segment_register(value);
	}

	/// Always show memory operands' size
	///
	/// Default | Value | Example
	/// --------|-------|--------
	/// - | `true` | `mov eax,dword ptr [ebx]` / `add byte ptr [eax],0x12`
	/// ✔ | `false` | `mov eax,[ebx]` / `add byte ptr [eax],0x12`
	#[wasm_bindgen(getter)]
	#[wasm_bindgen(js_name = "alwaysShowMemorySize")]
	pub fn always_show_memory_size(&self) -> bool {
		self.0.options().always_show_memory_size()
	}

	/// Always show memory operands' size
	///
	/// Default | Value | Example
	/// --------|-------|--------
	/// - | `true` | `mov eax,dword ptr [ebx]` / `add byte ptr [eax],0x12`
	/// ✔ | `false` | `mov eax,[ebx]` / `add byte ptr [eax],0x12`
	///
	/// # Arguments
	///
	/// * `value`: New value
	#[wasm_bindgen(setter)]
	#[wasm_bindgen(js_name = "alwaysShowMemorySize")]
	pub fn set_always_show_memory_size(&mut self, value: bool) {
		self.0.options_mut().set_always_show_memory_size(value)
	}

	/// Use upper case hex digits
	///
	/// Default | Value | Example
	/// --------|-------|--------
	/// ✔ | `true` | `0xFF`
	/// - | `false` | `0xff`
	#[wasm_bindgen(getter)]
	#[wasm_bindgen(js_name = "uppercaseHex")]
	pub fn uppercase_hex(&self) -> bool {
		self.0.options().uppercase_hex()
	}

	/// Use upper case hex digits
	///
	/// Default | Value | Example
	/// --------|-------|--------
	/// ✔ | `true` | `0xFF`
	/// - | `false` | `0xff`
	///
	/// # Arguments
	///
	/// * `value`: New value
	#[wasm_bindgen(setter)]
	#[wasm_bindgen(js_name = "uppercaseHex")]
	pub fn set_uppercase_hex(&mut self, value: bool) {
		self.0.options_mut().set_uppercase_hex(value);
	}

	/// Use a hex prefix (`0x`) or a hex suffix (`h`)
	///
	/// Default | Value | Example
	/// --------|-------|--------
	/// - | `true` | `0x5A`
	/// ✔ | `false` | `5Ah`
	#[wasm_bindgen(getter)]
	#[wasm_bindgen(js_name = "useHexPrefix")]
	pub fn use_hex_prefix(&self) -> bool {
		self.0.options().use_hex_prefix()
	}

	/// Always show memory operands' size
	///
	/// Default | Value | Example
	/// --------|-------|--------
	/// - | `true` | `mov eax,dword ptr [ebx]` / `add byte ptr [eax],0x12`
	/// ✔ | `false` | `mov eax,[ebx]` / `add byte ptr [eax],0x12`
	///
	/// # Arguments
	///
	/// * `value`: New value
	#[wasm_bindgen(setter)]
	#[wasm_bindgen(js_name = "useHexPrefix")]
	pub fn set_use_hex_prefix(&mut self, value: bool) {
		self.0.options_mut().set_use_hex_prefix(value)
	}
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use dom::bindings::codegen::BindingDeclarations::WindowBinding;
use dom::bindings::js::{JS, JSRef, Temporary, OptionalSettable};
use dom::bindings::trace::{Traceable, Untraceable};
use dom::bindings::utils::{Reflectable, Reflector};
use dom::browsercontext::BrowserContext;
use dom::document::Document;
use dom::element::Element;
use dom::eventtarget::{EventTarget, WindowTypeId};
use dom::console::Console;
use dom::location::Location;
use dom::navigator::Navigator;
use dom::performance::Performance;

use layout_interface::{ReflowForDisplay, DocumentDamageLevel};
use script_task::{ExitWindowMsg, FireTimerMsg, Page, ScriptChan};
use servo_msg::compositor_msg::ScriptListener;
use servo_net::image_cache_task::ImageCacheTask;
use servo_util::str::DOMString;
use servo_util::task::{spawn_named};

use js::jsapi::JSContext;
use js::jsapi::{JS_GC, JS_GetRuntime};
use js::jsval::{NullValue, JSVal};

use collections::hashmap::HashMap;
use std::cmp;
use std::comm::{channel, Sender};
use std::comm::Select;
use std::hash::{Hash, sip};
use std::io::timer::Timer;
use std::rc::Rc;

use time;

use serialize::{Encoder, Encodable};
use url::Url;

#[deriving(Eq, Encodable, TotalEq)]
pub struct TimerId(i32);

#[deriving(Encodable)]
pub struct TimerHandle {
    pub handle: TimerId,
    pub data: TimerData,
    pub cancel_chan: Untraceable<Option<Sender<()>>>,
}

impl Hash for TimerId {
    fn hash(&self, state: &mut sip::SipState) {
        let TimerId(id) = *self;
        id.hash(state);
    }
}

impl TimerHandle {
    fn cancel(&mut self) {
        self.cancel_chan.as_ref().map(|chan| chan.send(()));
    }
}

#[deriving(Encodable)]
pub struct Window {
    pub eventtarget: EventTarget,
    pub script_chan: ScriptChan,
    pub console: Option<JS<Console>>,
    pub location: Option<JS<Location>>,
    pub navigator: Option<JS<Navigator>>,
    pub image_cache_task: ImageCacheTask,
    pub active_timers: ~HashMap<TimerId, TimerHandle>,
    pub next_timer_handle: i32,
    pub compositor: Untraceable<~ScriptListener>,
    pub browser_context: Option<BrowserContext>,
    pub page: Rc<Page>,
    pub performance: Option<JS<Performance>>,
    pub navigationStart: u64,
    pub navigationStartPrecise: f64,
}

impl Window {
    pub fn get_cx(&self) -> *JSContext {
        let js_info = self.page().js_info();
        (**js_info.get_ref().js_context).ptr
    }

    pub fn page<'a>(&'a self) -> &'a Page {
        &*self.page
    }
    pub fn get_url(&self) -> Url {
        self.page().get_url()
    }
}

#[unsafe_destructor]
impl Drop for Window {
    fn drop(&mut self) {
        for (_, timer_handle) in self.active_timers.mut_iter() {
            timer_handle.cancel();
        }
    }
}

// Holder for the various JS values associated with setTimeout
// (ie. function value to invoke and all arguments to pass
//      to the function when calling it)
#[deriving(Encodable)]
pub struct TimerData {
    pub is_interval: bool,
    pub funval: Traceable<JSVal>,
}

pub trait WindowMethods {
    fn Alert(&self, s: DOMString);
    fn Close(&self);
    fn Document(&self) -> Temporary<Document>;
    fn Name(&self) -> DOMString;
    fn SetName(&self, _name: DOMString);
    fn Status(&self) -> DOMString;
    fn SetStatus(&self, _status: DOMString);
    fn Closed(&self) -> bool;
    fn Stop(&self);
    fn Focus(&self);
    fn Blur(&self);
    fn GetFrameElement(&self) -> Option<Temporary<Element>>;
    fn Location(&mut self) -> Temporary<Location>;
    fn Console(&mut self) -> Temporary<Console>;
    fn Navigator(&mut self) -> Temporary<Navigator>;
    fn Confirm(&self, _message: DOMString) -> bool;
    fn Prompt(&self, _message: DOMString, _default: DOMString) -> Option<DOMString>;
    fn Print(&self);
    fn ShowModalDialog(&self, _cx: *JSContext, _url: DOMString, _argument: Option<JSVal>) -> JSVal;
    fn SetTimeout(&mut self, _cx: *JSContext, callback: JSVal, timeout: i32) -> i32;
    fn ClearTimeout(&mut self, handle: i32);
    fn SetInterval(&mut self, _cx: *JSContext, callback: JSVal, timeout: i32) -> i32;
    fn ClearInterval(&mut self, handle: i32);
    fn Window(&self) -> Temporary<Window>;
    fn Self(&self) -> Temporary<Window>;
    fn Performance(&mut self) -> Temporary<Performance>;
    fn Debug(&self, message: DOMString);
    fn Gc(&self);
}

impl<'a> WindowMethods for JSRef<'a, Window> {
    fn Alert(&self, s: DOMString) {
        // Right now, just print to the console
        println!("ALERT: {:s}", s);
    }

    fn Close(&self) {
        let ScriptChan(ref chan) = self.script_chan;
        chan.send(ExitWindowMsg(self.page.id.clone()));
    }

    fn Document(&self) -> Temporary<Document> {
        let frame = self.page().frame();
        Temporary::new(frame.get_ref().document.clone())
    }

    fn Name(&self) -> DOMString {
        "".to_owned()
    }

    fn SetName(&self, _name: DOMString) {
    }

    fn Status(&self) -> DOMString {
        "".to_owned()
    }

    fn SetStatus(&self, _status: DOMString) {
    }

    fn Closed(&self) -> bool {
        false
    }

    fn Stop(&self) {
    }

    fn Focus(&self) {
    }

    fn Blur(&self) {
    }

    fn GetFrameElement(&self) -> Option<Temporary<Element>> {
        None
    }

    fn Location(&mut self) -> Temporary<Location> {
        if self.location.is_none() {
            let page = self.deref().page.clone();
            let location = Location::new(self, page);
            self.location.assign(Some(location));
        }
        Temporary::new(self.location.get_ref().clone())
    }

    fn Console(&mut self) -> Temporary<Console> {
        if self.console.is_none() {
            let console = Console::new(self);
            self.console.assign(Some(console));
        }
        Temporary::new(self.console.get_ref().clone())
    }

    fn Navigator(&mut self) -> Temporary<Navigator> {
        if self.navigator.is_none() {
            let navigator = Navigator::new(self);
            self.navigator.assign(Some(navigator));
        }
        Temporary::new(self.navigator.get_ref().clone())
    }

    fn Confirm(&self, _message: DOMString) -> bool {
        false
    }

    fn Prompt(&self, _message: DOMString, _default: DOMString) -> Option<DOMString> {
        None
    }

    fn Print(&self) {
    }

    fn ShowModalDialog(&self, _cx: *JSContext, _url: DOMString, _argument: Option<JSVal>) -> JSVal {
        NullValue()
    }

    fn SetTimeout(&mut self, _cx: *JSContext, callback: JSVal, timeout: i32) -> i32 {
        self.set_timeout_or_interval(callback, timeout, false)
    }

    fn ClearTimeout(&mut self, handle: i32) {
        let mut timer_handle = self.active_timers.pop(&TimerId(handle));
        match timer_handle {
            Some(ref mut handle) => handle.cancel(),
            None => { }
        }
        self.active_timers.remove(&TimerId(handle));
    }

    fn SetInterval(&mut self, _cx: *JSContext, callback: JSVal, timeout: i32) -> i32 {
        self.set_timeout_or_interval(callback, timeout, true)
    }

    fn ClearInterval(&mut self, handle: i32) {
        self.ClearTimeout(handle);
    }

    fn Window(&self) -> Temporary<Window> {
        Temporary::from_rooted(self)
    }

    fn Self(&self) -> Temporary<Window> {
        self.Window()
    }

    fn Performance(&mut self) -> Temporary<Performance> {
        if self.performance.is_none() {
            let performance = Performance::new(self);
            self.performance.assign(Some(performance));
        }
        Temporary::new(self.performance.get_ref().clone())
    }

    fn Debug(&self, message: DOMString) {
        debug!("{:s}", message);
    }

    fn Gc(&self) {
        unsafe {
            JS_GC(JS_GetRuntime(self.get_cx()));
        }
    }
}

impl Reflectable for Window {
    fn reflector<'a>(&'a self) -> &'a Reflector {
        self.eventtarget.reflector()
    }

    fn mut_reflector<'a>(&'a mut self) -> &'a mut Reflector {
        self.eventtarget.mut_reflector()
    }
}

impl Window {
    fn set_timeout_or_interval(&mut self, callback: JSVal, timeout: i32, is_interval: bool) -> i32 {
        let timeout = cmp::max(0, timeout) as u64;
        let handle = self.next_timer_handle;
        self.next_timer_handle += 1;

        // Post a delayed message to the per-window timer task; it will dispatch it
        // to the relevant script handler that will deal with it.
        let tm = Timer::new().unwrap();
        let (cancel_chan, cancel_port) = channel();
        let chan = self.script_chan.clone();
        let page_id = self.page.id.clone();
        let spawn_name = if is_interval {
            "Window:SetInterval"
        } else {
            "Window:SetTimeout"
        };
        spawn_named(spawn_name, proc() {
            let mut tm = tm;
            let timeout_port = if is_interval {
                tm.periodic(timeout)
            } else {
                tm.oneshot(timeout)
            };
            let cancel_port = cancel_port;

            let select = Select::new();
            let mut timeout_handle = select.handle(&timeout_port);
            unsafe { timeout_handle.add() };
            let mut cancel_handle = select.handle(&cancel_port);
            unsafe { cancel_handle.add() };

            loop {
                let id = select.wait();
                if id == timeout_handle.id() {
                    timeout_port.recv();
                    let ScriptChan(ref chan) = chan;
                    chan.send(FireTimerMsg(page_id, TimerId(handle)));
                    if !is_interval {
                        break;
                    }
                } else if id == cancel_handle.id() {
                    break;
                }
            }
        });
        let timer_id = TimerId(handle);
        let timer = TimerHandle {
            handle: timer_id,
            cancel_chan: Untraceable::new(Some(cancel_chan)),
            data: TimerData {
                is_interval: is_interval,
                funval: Traceable::new(callback),
            }
        };
        self.active_timers.insert(timer_id, timer);
        handle
    }

    pub fn damage_and_reflow(&self, damage: DocumentDamageLevel) {
        // FIXME This should probably be ReflowForQuery, not Display. All queries currently
        // currently rely on the display list, which means we can't destroy it by
        // doing a query reflow.
        self.page().damage(damage);
        self.page().reflow(ReflowForDisplay, self.script_chan.clone(), *self.compositor);
    }

    pub fn wait_until_safe_to_modify_dom(&self) {
        // FIXME: This disables concurrent layout while we are modifying the DOM, since
        //        our current architecture is entirely unsafe in the presence of races.
        self.page().join_layout();
    }

    pub fn init_browser_context(&mut self, doc: &JSRef<Document>) {
        self.browser_context = Some(BrowserContext::new(doc));
    }

    pub fn new(cx: *JSContext,
               page: Rc<Page>,
               script_chan: ScriptChan,
               compositor: ~ScriptListener,
               image_cache_task: ImageCacheTask)
               -> JS<Window> {
        let win = ~Window {
            eventtarget: EventTarget::new_inherited(WindowTypeId),
            script_chan: script_chan,
            console: None,
            compositor: Untraceable::new(compositor),
            page: page.clone(),
            location: None,
            navigator: None,
            image_cache_task: image_cache_task,
            active_timers: ~HashMap::new(),
            next_timer_handle: 0,
            browser_context: None,
            performance: None,
            navigationStart: time::get_time().sec as u64,
            navigationStartPrecise: time::precise_time_s(),
        };

        WindowBinding::Wrap(cx, win)
    }
}
<commit_msg>Drop the pointless indirection in Window::active_timers.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use dom::bindings::codegen::BindingDeclarations::WindowBinding;
use dom::bindings::js::{JS, JSRef, Temporary, OptionalSettable};
use dom::bindings::trace::{Traceable, Untraceable};
use dom::bindings::utils::{Reflectable, Reflector};
use dom::browsercontext::BrowserContext;
use dom::document::Document;
use dom::element::Element;
use dom::eventtarget::{EventTarget, WindowTypeId};
use dom::console::Console;
use dom::location::Location;
use dom::navigator::Navigator;
use dom::performance::Performance;

use layout_interface::{ReflowForDisplay, DocumentDamageLevel};
use script_task::{ExitWindowMsg, FireTimerMsg, Page, ScriptChan};
use servo_msg::compositor_msg::ScriptListener;
use servo_net::image_cache_task::ImageCacheTask;
use servo_util::str::DOMString;
use servo_util::task::{spawn_named};

use js::jsapi::JSContext;
use js::jsapi::{JS_GC, JS_GetRuntime};
use js::jsval::{NullValue, JSVal};

use collections::hashmap::HashMap;
use std::cmp;
use std::comm::{channel, Sender};
use std::comm::Select;
use std::hash::{Hash, sip};
use std::io::timer::Timer;
use std::rc::Rc;

use time;

use serialize::{Encoder, Encodable};
use url::Url;

#[deriving(Eq, Encodable, TotalEq)]
pub struct TimerId(i32);

#[deriving(Encodable)]
pub struct TimerHandle {
    pub handle: TimerId,
    pub data: TimerData,
    pub cancel_chan: Untraceable<Option<Sender<()>>>,
}

impl Hash for TimerId {
    fn hash(&self, state: &mut sip::SipState) {
        let TimerId(id) = *self;
        id.hash(state);
    }
}

impl TimerHandle {
    fn cancel(&mut self) {
        self.cancel_chan.as_ref().map(|chan| chan.send(()));
    }
}

#[deriving(Encodable)]
pub struct Window {
    pub eventtarget: EventTarget,
    pub script_chan: ScriptChan,
    pub console: Option<JS<Console>>,
    pub location: Option<JS<Location>>,
    pub navigator: Option<JS<Navigator>>,
    pub image_cache_task: ImageCacheTask,
    pub active_timers: HashMap<TimerId, TimerHandle>,
    pub next_timer_handle: i32,
    pub compositor: Untraceable<~ScriptListener>,
    pub browser_context: Option<BrowserContext>,
    pub page: Rc<Page>,
    pub performance: Option<JS<Performance>>,
    pub navigationStart: u64,
    pub navigationStartPrecise: f64,
}

impl Window {
    pub fn get_cx(&self) -> *JSContext {
        let js_info = self.page().js_info();
        (**js_info.get_ref().js_context).ptr
    }

    pub fn page<'a>(&'a self) -> &'a Page {
        &*self.page
    }
    pub fn get_url(&self) -> Url {
        self.page().get_url()
    }
}

#[unsafe_destructor]
impl Drop for Window {
    fn drop(&mut self) {
        for (_, timer_handle) in self.active_timers.mut_iter() {
            timer_handle.cancel();
        }
    }
}

// Holder for the various JS values associated with setTimeout
// (ie. function value to invoke and all arguments to pass
//      to the function when calling it)
#[deriving(Encodable)]
pub struct TimerData {
    pub is_interval: bool,
    pub funval: Traceable<JSVal>,
}

pub trait WindowMethods {
    fn Alert(&self, s: DOMString);
    fn Close(&self);
    fn Document(&self) -> Temporary<Document>;
    fn Name(&self) -> DOMString;
    fn SetName(&self, _name: DOMString);
    fn Status(&self) -> DOMString;
    fn SetStatus(&self, _status: DOMString);
    fn Closed(&self) -> bool;
    fn Stop(&self);
    fn Focus(&self);
    fn Blur(&self);
    fn GetFrameElement(&self) -> Option<Temporary<Element>>;
    fn Location(&mut self) -> Temporary<Location>;
    fn Console(&mut self) -> Temporary<Console>;
    fn Navigator(&mut self) -> Temporary<Navigator>;
    fn Confirm(&self, _message: DOMString) -> bool;
    fn Prompt(&self, _message: DOMString, _default: DOMString) -> Option<DOMString>;
    fn Print(&self);
    fn ShowModalDialog(&self, _cx: *JSContext, _url: DOMString, _argument: Option<JSVal>) -> JSVal;
    fn SetTimeout(&mut self, _cx: *JSContext, callback: JSVal, timeout: i32) -> i32;
    fn ClearTimeout(&mut self, handle: i32);
    fn SetInterval(&mut self, _cx: *JSContext, callback: JSVal, timeout: i32) -> i32;
    fn ClearInterval(&mut self, handle: i32);
    fn Window(&self) -> Temporary<Window>;
    fn Self(&self) -> Temporary<Window>;
    fn Performance(&mut self) -> Temporary<Performance>;
    fn Debug(&self, message: DOMString);
    fn Gc(&self);
}

impl<'a> WindowMethods for JSRef<'a, Window> {
    fn Alert(&self, s: DOMString) {
        // Right now, just print to the console
        println!("ALERT: {:s}", s);
    }

    fn Close(&self) {
        let ScriptChan(ref chan) = self.script_chan;
        chan.send(ExitWindowMsg(self.page.id.clone()));
    }

    fn Document(&self) -> Temporary<Document> {
        let frame = self.page().frame();
        Temporary::new(frame.get_ref().document.clone())
    }

    fn Name(&self) -> DOMString {
        "".to_owned()
    }

    fn SetName(&self, _name: DOMString) {
    }

    fn Status(&self) -> DOMString {
        "".to_owned()
    }

    fn SetStatus(&self, _status: DOMString) {
    }

    fn Closed(&self) -> bool {
        false
    }

    fn Stop(&self) {
    }

    fn Focus(&self) {
    }

    fn Blur(&self) {
    }

    fn GetFrameElement(&self) -> Option<Temporary<Element>> {
        None
    }

    fn Location(&mut self) -> Temporary<Location> {
        if self.location.is_none() {
            let page = self.deref().page.clone();
            let location = Location::new(self, page);
            self.location.assign(Some(location));
        }
        Temporary::new(self.location.get_ref().clone())
    }

    fn Console(&mut self) -> Temporary<Console> {
        if self.console.is_none() {
            let console = Console::new(self);
            self.console.assign(Some(console));
        }
        Temporary::new(self.console.get_ref().clone())
    }

    fn Navigator(&mut self) -> Temporary<Navigator> {
        if self.navigator.is_none() {
            let navigator = Navigator::new(self);
            self.navigator.assign(Some(navigator));
        }
        Temporary::new(self.navigator.get_ref().clone())
    }

    fn Confirm(&self, _message: DOMString) -> bool {
        false
    }

    fn Prompt(&self, _message: DOMString, _default: DOMString) -> Option<DOMString> {
        None
    }

    fn Print(&self) {
    }

    fn ShowModalDialog(&self, _cx: *JSContext, _url: DOMString, _argument: Option<JSVal>) -> JSVal {
        NullValue()
    }

    fn SetTimeout(&mut self, _cx: *JSContext, callback: JSVal, timeout: i32) -> i32 {
        self.set_timeout_or_interval(callback, timeout, false)
    }

    fn ClearTimeout(&mut self, handle: i32) {
        let mut timer_handle = self.active_timers.pop(&TimerId(handle));
        match timer_handle {
            Some(ref mut handle) => handle.cancel(),
            None => { }
        }
        self.active_timers.remove(&TimerId(handle));
    }

    fn SetInterval(&mut self, _cx: *JSContext, callback: JSVal, timeout: i32) -> i32 {
        self.set_timeout_or_interval(callback, timeout, true)
    }

    fn ClearInterval(&mut self, handle: i32) {
        self.ClearTimeout(handle);
    }

    fn Window(&self) -> Temporary<Window> {
        Temporary::from_rooted(self)
    }

    fn Self(&self) -> Temporary<Window> {
        self.Window()
    }

    fn Performance(&mut self) -> Temporary<Performance> {
        if self.performance.is_none() {
            let performance = Performance::new(self);
            self.performance.assign(Some(performance));
        }
        Temporary::new(self.performance.get_ref().clone())
    }

    fn Debug(&self, message: DOMString) {
        debug!("{:s}", message);
    }

    fn Gc(&self) {
        unsafe {
            JS_GC(JS_GetRuntime(self.get_cx()));
        }
    }
}

impl Reflectable for Window {
    fn reflector<'a>(&'a self) -> &'a Reflector {
        self.eventtarget.reflector()
    }

    fn mut_reflector<'a>(&'a mut self) -> &'a mut Reflector {
        self.eventtarget.mut_reflector()
    }
}

impl Window {
    fn set_timeout_or_interval(&mut self, callback: JSVal, timeout: i32, is_interval: bool) -> i32 {
        let timeout = cmp::max(0, timeout) as u64;
        let handle = self.next_timer_handle;
        self.next_timer_handle += 1;

        // Post a delayed message to the per-window timer task; it will dispatch it
        // to the relevant script handler that will deal with it.
        let tm = Timer::new().unwrap();
        let (cancel_chan, cancel_port) = channel();
        let chan = self.script_chan.clone();
        let page_id = self.page.id.clone();
        let spawn_name = if is_interval {
            "Window:SetInterval"
        } else {
            "Window:SetTimeout"
        };
        spawn_named(spawn_name, proc() {
            let mut tm = tm;
            let timeout_port = if is_interval {
                tm.periodic(timeout)
            } else {
                tm.oneshot(timeout)
            };
            let cancel_port = cancel_port;

            let select = Select::new();
            let mut timeout_handle = select.handle(&timeout_port);
            unsafe { timeout_handle.add() };
            let mut cancel_handle = select.handle(&cancel_port);
            unsafe { cancel_handle.add() };

            loop {
                let id = select.wait();
                if id == timeout_handle.id() {
                    timeout_port.recv();
                    let ScriptChan(ref chan) = chan;
                    chan.send(FireTimerMsg(page_id, TimerId(handle)));
                    if !is_interval {
                        break;
                    }
                } else if id == cancel_handle.id() {
                    break;
                }
            }
        });
        let timer_id = TimerId(handle);
        let timer = TimerHandle {
            handle: timer_id,
            cancel_chan: Untraceable::new(Some(cancel_chan)),
            data: TimerData {
                is_interval: is_interval,
                funval: Traceable::new(callback),
            }
        };
        self.active_timers.insert(timer_id, timer);
        handle
    }

    pub fn damage_and_reflow(&self, damage: DocumentDamageLevel) {
        // FIXME This should probably be ReflowForQuery, not Display. All queries currently
        // currently rely on the display list, which means we can't destroy it by
        // doing a query reflow.
        self.page().damage(damage);
        self.page().reflow(ReflowForDisplay, self.script_chan.clone(), *self.compositor);
    }

    pub fn wait_until_safe_to_modify_dom(&self) {
        // FIXME: This disables concurrent layout while we are modifying the DOM, since
        //        our current architecture is entirely unsafe in the presence of races.
        self.page().join_layout();
    }

    pub fn init_browser_context(&mut self, doc: &JSRef<Document>) {
        self.browser_context = Some(BrowserContext::new(doc));
    }

    pub fn new(cx: *JSContext,
               page: Rc<Page>,
               script_chan: ScriptChan,
               compositor: ~ScriptListener,
               image_cache_task: ImageCacheTask)
               -> JS<Window> {
        let win = ~Window {
            eventtarget: EventTarget::new_inherited(WindowTypeId),
            script_chan: script_chan,
            console: None,
            compositor: Untraceable::new(compositor),
            page: page.clone(),
            location: None,
            navigator: None,
            image_cache_task: image_cache_task,
            active_timers: HashMap::new(),
            next_timer_handle: 0,
            browser_context: None,
            performance: None,
            navigationStart: time::get_time().sec as u64,
            navigationStartPrecise: time::precise_time_s(),
        };

        WindowBinding::Wrap(cx, win)
    }
}
<|endoftext|>


<commit_before>extern crate libc;

use std::num::Int;
use std::ptr;
use std::mem;

use constants::LINE_SIZE;

#[repr(C)]
pub struct GCHeader {
    object_size: libc::size_t,
    variables: libc::size_t,
    reference_count: libc::size_t,
    spans_lines: bool,
    forwarded: bool,
    logged: bool,
    marked: bool,
    new: bool,
}

#[repr(C)]
pub struct GCObject {
    header: GCHeader,
    vmt_pointer: *mut u8,
}

impl GCObject {
    pub fn new(size: uint, variables: uint, mark: bool) -> GCObject {
        return GCObject {
            header: GCHeader {
                object_size: size as libc::size_t,
                variables: variables as libc::size_t,
                reference_count: 0,
                spans_lines: size > LINE_SIZE,
                forwarded: false,
                logged: false,
                marked: mark,
                new: true,
            },
            vmt_pointer: ptr::null_mut(),
        }
    }

    pub fn object_size(&self) -> uint {
        return self.header.object_size as uint;
    }

    pub fn children(&mut self) -> Vec<*mut GCObject> {
        let base: *const *mut GCObject = unsafe{ mem::transmute(&self.vmt_pointer) };
        return range(1, self.header.variables + 1)
               .map(|i| unsafe{ *base.offset(i as int) })
               .collect();
    }

    pub fn decrement(&mut self) -> bool {
        self.header.reference_count = Int::saturating_sub(self.header.reference_count, 1);
        debug!("Decrement object {:p} to {}", self, self.header.reference_count);
        return self.header.reference_count == 0;
    }

    pub fn increment(&mut self) -> bool {
        self.header.reference_count += 1;
        debug!("Increment object {:p} to {} (new={})", self,
            self.header.reference_count, self.header.new);
        if self.header.new {
            self.header.new = false;
            return true;
        }
        return false;
    }

    pub fn set_logged(&mut self, new: bool) -> bool {
        debug!("Set object {:p} logged={}", self, new);
        let logged = self.header.logged;
        self.header.logged = new;
        return logged;
    }

    pub fn set_marked(&mut self, current: bool) -> bool {
        debug!("Set object {:p} marked={}", self, current);
        let marked = self.header.marked;
        self.header.marked = current;
        return marked != current;
    }

    pub fn is_marked(&self, current: bool) -> bool {
        return self.header.marked != current;
    }

    pub fn spans_lines(&self) -> bool {
        return self.header.spans_lines;
    }
}

<commit_msg>Fix: GCObject.{set,is}_marked()<commit_after>extern crate libc;

use std::num::Int;
use std::ptr;
use std::mem;

use constants::LINE_SIZE;

#[repr(C)]
pub struct GCHeader {
    object_size: libc::size_t,
    variables: libc::size_t,
    reference_count: libc::size_t,
    spans_lines: bool,
    forwarded: bool,
    logged: bool,
    marked: bool,
    new: bool,
}

#[repr(C)]
pub struct GCObject {
    header: GCHeader,
    vmt_pointer: *mut u8,
}

impl GCObject {
    pub fn new(size: uint, variables: uint, mark: bool) -> GCObject {
        return GCObject {
            header: GCHeader {
                object_size: size as libc::size_t,
                variables: variables as libc::size_t,
                reference_count: 0,
                spans_lines: size > LINE_SIZE,
                forwarded: false,
                logged: false,
                marked: mark,
                new: true,
            },
            vmt_pointer: ptr::null_mut(),
        }
    }

    pub fn object_size(&self) -> uint {
        return self.header.object_size as uint;
    }

    pub fn children(&mut self) -> Vec<*mut GCObject> {
        let base: *const *mut GCObject = unsafe{ mem::transmute(&self.vmt_pointer) };
        return range(1, self.header.variables + 1)
               .map(|i| unsafe{ *base.offset(i as int) })
               .collect();
    }

    pub fn decrement(&mut self) -> bool {
        self.header.reference_count = Int::saturating_sub(self.header.reference_count, 1);
        debug!("Decrement object {:p} to {}", self, self.header.reference_count);
        return self.header.reference_count == 0;
    }

    pub fn increment(&mut self) -> bool {
        self.header.reference_count += 1;
        debug!("Increment object {:p} to {} (new={})", self,
            self.header.reference_count, self.header.new);
        if self.header.new {
            self.header.new = false;
            return true;
        }
        return false;
    }

    pub fn set_logged(&mut self, new: bool) -> bool {
        debug!("Set object {:p} logged={}", self, new);
        let logged = self.header.logged;
        self.header.logged = new;
        return logged;
    }

    pub fn set_marked(&mut self, current: bool) -> bool {
        debug!("Set object {:p} marked={}", self, current);
        let marked = self.header.marked;
        self.header.marked = !current;
        return marked == current;
    }

    pub fn is_marked(&self, current: bool) -> bool {
        return self.header.marked == current;
    }

    pub fn spans_lines(&self) -> bool {
        return self.header.spans_lines;
    }
}

<|endoftext|>