<commit_before>extern crate libc;

use {KeyboardControllable, Key, MouseControllable, MouseButton};
use linux::keysyms::*;

use std::ffi::CString;
use std::ptr;
use self::libc::{c_ulong, c_uint, c_int, c_char, c_void};

pub type Display = *const c_void;
pub type Window = c_int;

pub type KeySym = *const c_void;
pub type KeyCode = c_uint;

type Bool = c_int;

#[link(name = "X11")]
extern "C" {
    fn XOpenDisplay(string: *const c_char) -> Display;
    fn XRootWindow(display: Display, index: c_int) -> Window;
    fn XFree(data: *const c_void) -> c_int;
    fn XFlush(display: Display) -> c_int;

    fn XStringToKeysym(string: *const c_char) -> KeySym;
    fn XKeysymToKeycode(display: Display, keysym: KeySym, index: c_int) -> KeyCode;
    fn XChangeKeyboardMapping(
        display: Display,
        first_keycode: c_int,
        keycode_count: c_int,
        keysyms: *const KeySym,
        keysyms_per_keycode_return: c_int,
    ) -> KeySym;
    fn XGetKeyboardMapping(
        display: Display,
        first_keycode: KeyCode,
        keycode_count: c_int,
        keysyms_per_keycode_return: *mut c_int,
    ) -> *mut KeySym;
    fn XDisplayKeycodes(
        display: Display,
        min_keycodes_return: *mut c_int,
        max_keycodes_return: *mut c_int,
    ) -> c_int;

    fn XWarpPointer(
        display: Display,
        src_w: Window,
        dest_w: Window,
        src_x: c_int,
        src_y: c_int,
        src_width: c_int,
        src_height: c_int,
        dest_x: c_int,
        dest_y: c_int,
    );
}

#[link(name = "Xtst")]
extern "C" {
    fn XTestFakeKeyEvent(display: Display, keycode: KeyCode, state: Bool, delay: c_ulong);
    fn XTestFakeButtonEvent(display: Display, keycode: KeyCode, state: Bool, delay: c_ulong);
}

/// The main struct for handling the event emitting
pub struct Enigo {
    display: Display,
    window: Window,
}

impl Enigo {
    /// Constructs a new `Enigo` instance.
    ///
    /// # Example
    ///
    /// ```no_run
    /// use enigo::*;
    /// let mut enigo = Enigo::new();
    /// ```
    pub fn new() -> Self {
        let display;
        unsafe { display = XOpenDisplay(ptr::null()) };
        if display.is_null() {
            panic!("can't open display");
        }

        let window = unsafe { XRootWindow(display, 0) };

        Enigo {
            display: display,
            window: window,
        }
    }

    // TODO(dustin): implement drop
}

impl Default for Enigo {
    fn default() -> Self {
        Self::new()
    }
}

impl MouseControllable for Enigo {
    fn mouse_move_to(&mut self, x: i32, y: i32) {
        if self.display.is_null() {
            panic!("display is not available")
        }

        unsafe {
            XWarpPointer(self.display, 0, self.window, 0, 0, 0, 0, x, y);
            XFlush(self.display);
        }
    }

    fn mouse_move_relative(&mut self, x: i32, y: i32) {

        if self.display.is_null() {
            panic!("display is not available")
        }

        unsafe {
            XWarpPointer(self.display, 0, 0, 0, 0, 0, 0, x, y);
            XFlush(self.display);
        }
    }

    fn mouse_down(&mut self, button: MouseButton) {
        if self.display.is_null() {
            panic!("display is not available")
        }

        unsafe {
            XTestFakeButtonEvent(
                self.display,
                match button {
                    MouseButton::Left => 1,
                    MouseButton::Middle => 2,
                    MouseButton::Right => 3,
                    MouseButton::ScrollUp => 4,
                    MouseButton::ScrollDown => 5,
                    MouseButton::ScrollLeft => 6,
                    MouseButton::ScrollRight => 7,
                },
                1,
                0,
            );
            XFlush(self.display);
        }
    }

    fn mouse_up(&mut self, button: MouseButton) {
        if self.display.is_null() {
            panic!("display is not available")
        }

        unsafe {
            XTestFakeButtonEvent(
                self.display,
                match button {
                    MouseButton::Left => 1,
                    MouseButton::Middle => 2,
                    MouseButton::Right => 3,
                    MouseButton::ScrollUp => 4,
                    MouseButton::ScrollDown => 5,
                    MouseButton::ScrollLeft => 6,
                    MouseButton::ScrollRight => 7,
                },
                0,
                0,
            );
            XFlush(self.display);
        }
    }

    fn mouse_click(&mut self, button: MouseButton) {
        use std::{thread, time};

        self.mouse_down(button);
        thread::sleep(time::Duration::from_millis(100));
        self.mouse_up(button);
    }

    fn mouse_scroll_x(&mut self, length: i32) {
        let button;
        let mut length = length;

        if length < 0 {
            button = MouseButton::ScrollLeft;
        } else {
            button = MouseButton::ScrollRight;
        }

        if length < 0 {
            length *= -1;
        }

        for _ in 0..length {
            self.mouse_down(button);
            self.mouse_up(button);
        }
    }

    fn mouse_scroll_y(&mut self, length: i32) {
        let button;
        let mut length = length;

        if length < 0 {
            button = MouseButton::ScrollUp;
        } else {
            button = MouseButton::ScrollDown;
        }

        if length < 0 {
            length *= -1;
        }

        for _ in 0..length {
            self.mouse_down(button);
            self.mouse_up(button);
        }
    }
}

impl KeyboardControllable for Enigo {
    fn key_sequence(&mut self, sequence: &str) {
        for c in sequence.chars() {
            let rust_unicode: String = format!("U{:x}", c as u32);
            let keycode = self.unicode_string_to_keycode(&rust_unicode);

            self.keycode_click(keycode);
            self.reset_keycode(keycode);
        }
    }

    fn key_click(&mut self, key: Key) {
        self.keycode_click(self.key_to_keycode(key));
    }

    fn key_down(&mut self, key: Key) {
        self.keycode_down(self.key_to_keycode(key));
    }

    fn key_up(&mut self, key: Key) {
        self.keycode_up(self.key_to_keycode(key));
    }
}

impl Enigo {
    fn reset_keycode(&self, keycode: u32) {
        unsafe {
            let keysym_list = [0 as KeySym, 0 as KeySym].as_ptr();
            XChangeKeyboardMapping(self.display, keycode as i32, 2, keysym_list, 1);
        }
    }

    fn unicode_string_to_keycode(&self, unicode_string: &str) -> u32 {
        let mut keysyms_per_keycode = 0;
        // scratch space for temporary keycode bindings
        let mut scratch_keycode = 0;
        let mut keycode_low = 0;
        let mut keycode_high = 0;

        let keysyms = unsafe {
            // get the range of keycodes usually from 8 - 255
            XDisplayKeycodes(self.display, &mut keycode_low, &mut keycode_high);
            // get all the mapped keysysms available
            let keycode_count = keycode_high - keycode_low;
            XGetKeyboardMapping(
                self.display,
                keycode_low as u32,
                keycode_count,
                &mut keysyms_per_keycode,
            )
        };

        // find unused keycode for unmapped keysyms so we can
        // hook up our own keycode and map every keysym on it
        // so we just need to 'click' our once unmapped keycode
        for cidx in keycode_low..keycode_high + 1 {
            let mut key_is_empty = true;
            for sidx in 0..keysyms_per_keycode {
                let map_idx = (cidx - keycode_low) * keysyms_per_keycode + sidx;
                let sym_at_idx = unsafe { keysyms.offset(map_idx as isize) };
                if unsafe { *sym_at_idx } != 0 as *const c_void {
                    key_is_empty = false;
                } else {
                    break;
                }
            }
            if key_is_empty {
                scratch_keycode = cidx;
                break;
            }
        }

        unsafe {
            XFree(keysyms as *mut c_void);
            XFlush(self.display);
        }

        // TODO(dustin) make this an error!
        if scratch_keycode == 0 {
            panic!("cannot find free keycode");
        }

        // find the keysym for the given unicode char
        // map that keysym to our previous unmapped keycode
        // click that keycode/'button' with our keysym on it
        let unicode_as_c_string = CString::new(unicode_string).unwrap();
        let keysym = unsafe { XStringToKeysym(unicode_as_c_string.as_ptr() as *mut c_char) };
        let keysym_list = [keysym, keysym].as_ptr();
        unsafe {
            XChangeKeyboardMapping(self.display, scratch_keycode, 2, keysym_list, 1);
        }

        unsafe {
            XFlush(self.display);
        }

        scratch_keycode as u32
    }

    fn key_to_keycode(&self, key: Key) -> u32 {
        unsafe {
            match key {
                Key::Return => XKeysymToKeycode(self.display, XK_RETURN as *const c_void, 0),
                Key::Tab => XKeysymToKeycode(self.display, XK_TAB as *const c_void, 0),
                Key::Space => XKeysymToKeycode(self.display, XK_SPACE as *const c_void, 0),
                Key::Backspace => XKeysymToKeycode(self.display, XK_BACKSPACE as *const c_void, 0),
                Key::Escape => XKeysymToKeycode(self.display, XK_ESCAPE as *const c_void, 0),
                Key::Super => XKeysymToKeycode(self.display, XK_SUPER_L as *const c_void, 0),
                Key::Command => XKeysymToKeycode(self.display, XK_SUPER_L as *const c_void, 0),
                Key::Windows => XKeysymToKeycode(self.display, XK_SUPER_L as *const c_void, 0),
                Key::Shift => XKeysymToKeycode(self.display, XK_SHIFT_L as *const c_void, 0),
                Key::CapsLock => XKeysymToKeycode(self.display, XK_CAPS_LOCK as *const c_void, 0),
                Key::Alt => XKeysymToKeycode(self.display, XK_ALT_L as *const c_void, 0),
                Key::Option => XKeysymToKeycode(self.display, XK_ALT_L as *const c_void, 0),
                Key::Control => XKeysymToKeycode(self.display, XK_CONTROL_L as *const c_void, 0),
                Key::Home => XKeysymToKeycode(self.display, XK_HOME as *const c_void, 0),
                Key::PageUp => XKeysymToKeycode(self.display, XK_PAGE_UP as *const c_void, 0),
                Key::PageDown => XKeysymToKeycode(self.display, XK_PAGE_DOWN as *const c_void, 0),
                Key::LeftArrow => XKeysymToKeycode(self.display, XK_LEFT as *const c_void, 0),
                Key::RightArrow => XKeysymToKeycode(self.display, XK_RIGHT as *const c_void, 0),
                Key::DownArrow => XKeysymToKeycode(self.display, XK_DOWN as *const c_void, 0),
                Key::UpArrow => XKeysymToKeycode(self.display, XK_UP as *const c_void, 0),
                Key::F1 => XKeysymToKeycode(self.display, XK_F1 as *const c_void, 0),
                Key::F2 => XKeysymToKeycode(self.display, XK_F2 as *const c_void, 0),
                Key::F3 => XKeysymToKeycode(self.display, XK_F3 as *const c_void, 0),
                Key::F4 => XKeysymToKeycode(self.display, XK_F4 as *const c_void, 0),
                Key::F5 => XKeysymToKeycode(self.display, XK_F5 as *const c_void, 0),
                Key::F6 => XKeysymToKeycode(self.display, XK_F6 as *const c_void, 0),
                Key::F7 => XKeysymToKeycode(self.display, XK_F7 as *const c_void, 0),
                Key::F8 => XKeysymToKeycode(self.display, XK_F8 as *const c_void, 0),
                Key::F9 => XKeysymToKeycode(self.display, XK_F9 as *const c_void, 0),
                Key::F10 => XKeysymToKeycode(self.display, XK_F10 as *const c_void, 0),
                Key::F11 => XKeysymToKeycode(self.display, XK_F11 as *const c_void, 0),
                Key::F12 => XKeysymToKeycode(self.display, XK_F12 as *const c_void, 0),

                Key::Raw(raw_keycode) => raw_keycode as u32,
                Key::Layout(string) => self.get_layoutdependent_keycode(string),
            }
        }
    }

    fn get_layoutdependent_keycode(&self, string: String) -> u32 {
        let c_string = CString::new(string).unwrap();
        let keysym = unsafe { XStringToKeysym(c_string.as_ptr() as *mut c_char) };

        unsafe { XKeysymToKeycode(self.display, keysym, 0) }
    }

    fn keycode_click(&self, keycode: u32) {
        // use std::{thread, time};
        // thread::sleep(time::Duration::from_millis(20));
        self.keycode_down(keycode);
        self.keycode_up(keycode);
        // thread::sleep(time::Duration::from_millis(20));
    }

    fn keycode_down(&self, keycode: u32) {
        use std::{thread, time};
        thread::sleep(time::Duration::from_millis(20));
        unsafe {
            XTestFakeKeyEvent(self.display, keycode, 1, 0);
            XFlush(self.display);
        }
        thread::sleep(time::Duration::from_millis(20));
    }

    fn keycode_up(&self, keycode: u32) {
        use std::{thread, time};
        thread::sleep(time::Duration::from_millis(20));
        unsafe {
            XTestFakeKeyEvent(self.display, keycode, 0, 0);
            XFlush(self.display);
        }
        thread::sleep(time::Duration::from_millis(20));
    }
}
<commit_msg>Implemented fancy KeyGuard<commit_after>extern crate libc;

use {KeyboardControllable, Key, MouseControllable, MouseButton};
use linux::keysyms::*;

use self::libc::{c_ulong, c_uint, c_int, c_char, c_void};
use std::ffi::CString;
use std::ops::Deref;
use std::ptr;

pub type Display = *const c_void;
pub type Window = c_int;

pub type KeySym = *const c_void;
pub type KeyCode = c_uint;

type Bool = c_int;

#[link(name = "X11")]
extern "C" {
    fn XOpenDisplay(string: *const c_char) -> Display;
    fn XRootWindow(display: Display, index: c_int) -> Window;
    fn XFree(data: *const c_void) -> c_int;
    fn XFlush(display: Display) -> c_int;

    fn XStringToKeysym(string: *const c_char) -> KeySym;
    fn XKeysymToKeycode(display: Display, keysym: KeySym, index: c_int) -> KeyCode;
    fn XChangeKeyboardMapping(
        display: Display,
        first_keycode: c_int,
        keycode_count: c_int,
        keysyms: *const KeySym,
        keysyms_per_keycode_return: c_int,
    ) -> KeySym;
    fn XGetKeyboardMapping(
        display: Display,
        first_keycode: KeyCode,
        keycode_count: c_int,
        keysyms_per_keycode_return: *mut c_int,
    ) -> *mut KeySym;
    fn XDisplayKeycodes(
        display: Display,
        min_keycodes_return: *mut c_int,
        max_keycodes_return: *mut c_int,
    ) -> c_int;

    fn XWarpPointer(
        display: Display,
        src_w: Window,
        dest_w: Window,
        src_x: c_int,
        src_y: c_int,
        src_width: c_int,
        src_height: c_int,
        dest_x: c_int,
        dest_y: c_int,
    );
}

#[link(name = "Xtst")]
extern "C" {
    fn XTestFakeKeyEvent(display: Display, keycode: KeyCode, state: Bool, delay: c_ulong);
    fn XTestFakeButtonEvent(display: Display, keycode: KeyCode, state: Bool, delay: c_ulong);
}

/// The main struct for handling the event emitting
pub struct Enigo {
    display: Display,
    window: Window,
}

impl Enigo {
    /// Constructs a new `Enigo` instance.
    ///
    /// # Example
    ///
    /// ```no_run
    /// use enigo::*;
    /// let mut enigo = Enigo::new();
    /// ```
    pub fn new() -> Self {
        let display;
        unsafe { display = XOpenDisplay(ptr::null()) };
        if display.is_null() {
            panic!("can't open display");
        }

        let window = unsafe { XRootWindow(display, 0) };

        Enigo {
            display: display,
            window: window,
        }
    }

    // TODO(dustin): implement drop
}

impl Default for Enigo {
    fn default() -> Self {
        Self::new()
    }
}

impl MouseControllable for Enigo {
    fn mouse_move_to(&mut self, x: i32, y: i32) {
        if self.display.is_null() {
            panic!("display is not available")
        }

        unsafe {
            XWarpPointer(self.display, 0, self.window, 0, 0, 0, 0, x, y);
            XFlush(self.display);
        }
    }

    fn mouse_move_relative(&mut self, x: i32, y: i32) {

        if self.display.is_null() {
            panic!("display is not available")
        }

        unsafe {
            XWarpPointer(self.display, 0, 0, 0, 0, 0, 0, x, y);
            XFlush(self.display);
        }
    }

    fn mouse_down(&mut self, button: MouseButton) {
        if self.display.is_null() {
            panic!("display is not available")
        }

        unsafe {
            XTestFakeButtonEvent(
                self.display,
                match button {
                    MouseButton::Left => 1,
                    MouseButton::Middle => 2,
                    MouseButton::Right => 3,
                    MouseButton::ScrollUp => 4,
                    MouseButton::ScrollDown => 5,
                    MouseButton::ScrollLeft => 6,
                    MouseButton::ScrollRight => 7,
                },
                1,
                0,
            );
            XFlush(self.display);
        }
    }

    fn mouse_up(&mut self, button: MouseButton) {
        if self.display.is_null() {
            panic!("display is not available")
        }

        unsafe {
            XTestFakeButtonEvent(
                self.display,
                match button {
                    MouseButton::Left => 1,
                    MouseButton::Middle => 2,
                    MouseButton::Right => 3,
                    MouseButton::ScrollUp => 4,
                    MouseButton::ScrollDown => 5,
                    MouseButton::ScrollLeft => 6,
                    MouseButton::ScrollRight => 7,
                },
                0,
                0,
            );
            XFlush(self.display);
        }
    }

    fn mouse_click(&mut self, button: MouseButton) {
        use std::{thread, time};

        self.mouse_down(button);
        thread::sleep(time::Duration::from_millis(100));
        self.mouse_up(button);
    }

    fn mouse_scroll_x(&mut self, length: i32) {
        let button;
        let mut length = length;

        if length < 0 {
            button = MouseButton::ScrollLeft;
        } else {
            button = MouseButton::ScrollRight;
        }

        if length < 0 {
            length *= -1;
        }

        for _ in 0..length {
            self.mouse_down(button);
            self.mouse_up(button);
        }
    }

    fn mouse_scroll_y(&mut self, length: i32) {
        let button;
        let mut length = length;

        if length < 0 {
            button = MouseButton::ScrollUp;
        } else {
            button = MouseButton::ScrollDown;
        }

        if length < 0 {
            length *= -1;
        }

        for _ in 0..length {
            self.mouse_down(button);
            self.mouse_up(button);
        }
    }
}

impl KeyboardControllable for Enigo {
    fn key_sequence(&mut self, sequence: &str) {
        for c in sequence.chars() {
            let rust_unicode: String = format!("U{:x}", c as u32);
            let keycode = self.unicode_string_to_keycode(&rust_unicode);

            self.keycode_click(*keycode);
        }
    }

    fn key_click(&mut self, key: Key) {
        self.keycode_click(self.key_to_keycode(key));
    }

    fn key_down(&mut self, key: Key) {
        self.keycode_down(self.key_to_keycode(key));
    }

    fn key_up(&mut self, key: Key) {
        self.keycode_up(self.key_to_keycode(key));
    }
}

struct KeyGuard<'a>(&'a Enigo, u32);

impl<'a> Deref for KeyGuard<'a> {
	type Target = u32;

	fn deref(&self) -> &Self::Target {
		return &self.1;
	}
}
impl<'a> Drop for KeyGuard<'a> {
	fn drop(&mut self) {
		self.0.reset_keycode(self.1);
	}
}

impl Enigo {
    fn reset_keycode(&self, keycode: u32) {
        unsafe {
            let keysym_list = [0 as KeySym, 0 as KeySym].as_ptr();
            XChangeKeyboardMapping(self.display, keycode as i32, 2, keysym_list, 1);
        }
    }

    fn unicode_string_to_keycode(&self, unicode_string: &str) -> KeyGuard {
        let mut keysyms_per_keycode = 0;
        // scratch space for temporary keycode bindings
        let mut scratch_keycode = 0;
        let mut keycode_low = 0;
        let mut keycode_high = 0;

        let keysyms = unsafe {
            // get the range of keycodes usually from 8 - 255
            XDisplayKeycodes(self.display, &mut keycode_low, &mut keycode_high);
            // get all the mapped keysysms available
            let keycode_count = keycode_high - keycode_low;
            XGetKeyboardMapping(
                self.display,
                keycode_low as u32,
                keycode_count,
                &mut keysyms_per_keycode,
            )
        };

        // find unused keycode for unmapped keysyms so we can
        // hook up our own keycode and map every keysym on it
        // so we just need to 'click' our once unmapped keycode
        for cidx in keycode_low..keycode_high + 1 {
            let mut key_is_empty = true;
            for sidx in 0..keysyms_per_keycode {
                let map_idx = (cidx - keycode_low) * keysyms_per_keycode + sidx;
                let sym_at_idx = unsafe { keysyms.offset(map_idx as isize) };
                if unsafe { *sym_at_idx } != 0 as *const c_void {
                    key_is_empty = false;
                } else {
                    break;
                }
            }
            if key_is_empty {
                scratch_keycode = cidx;
                break;
            }
        }

        unsafe {
            XFree(keysyms as *mut c_void);
            XFlush(self.display);
        }

        // TODO(dustin) make this an error!
        if scratch_keycode == 0 {
            panic!("cannot find free keycode");
        }

        // find the keysym for the given unicode char
        // map that keysym to our previous unmapped keycode
        // click that keycode/'button' with our keysym on it
        let unicode_as_c_string = CString::new(unicode_string).unwrap();
        let keysym = unsafe { XStringToKeysym(unicode_as_c_string.as_ptr() as *mut c_char) };
        let keysym_list = [keysym, keysym].as_ptr();
        unsafe {
            XChangeKeyboardMapping(self.display, scratch_keycode, 2, keysym_list, 1);
        }

        unsafe {
            XFlush(self.display);
        }

        KeyGuard(self, scratch_keycode as u32)
    }

    fn key_to_keycode(&self, key: Key) -> u32 {
        unsafe {
            match key {
                Key::Return => XKeysymToKeycode(self.display, XK_RETURN as *const c_void, 0),
                Key::Tab => XKeysymToKeycode(self.display, XK_TAB as *const c_void, 0),
                Key::Space => XKeysymToKeycode(self.display, XK_SPACE as *const c_void, 0),
                Key::Backspace => XKeysymToKeycode(self.display, XK_BACKSPACE as *const c_void, 0),
                Key::Escape => XKeysymToKeycode(self.display, XK_ESCAPE as *const c_void, 0),
                Key::Super => XKeysymToKeycode(self.display, XK_SUPER_L as *const c_void, 0),
                Key::Command => XKeysymToKeycode(self.display, XK_SUPER_L as *const c_void, 0),
                Key::Windows => XKeysymToKeycode(self.display, XK_SUPER_L as *const c_void, 0),
                Key::Shift => XKeysymToKeycode(self.display, XK_SHIFT_L as *const c_void, 0),
                Key::CapsLock => XKeysymToKeycode(self.display, XK_CAPS_LOCK as *const c_void, 0),
                Key::Alt => XKeysymToKeycode(self.display, XK_ALT_L as *const c_void, 0),
                Key::Option => XKeysymToKeycode(self.display, XK_ALT_L as *const c_void, 0),
                Key::Control => XKeysymToKeycode(self.display, XK_CONTROL_L as *const c_void, 0),
                Key::Home => XKeysymToKeycode(self.display, XK_HOME as *const c_void, 0),
                Key::PageUp => XKeysymToKeycode(self.display, XK_PAGE_UP as *const c_void, 0),
                Key::PageDown => XKeysymToKeycode(self.display, XK_PAGE_DOWN as *const c_void, 0),
                Key::LeftArrow => XKeysymToKeycode(self.display, XK_LEFT as *const c_void, 0),
                Key::RightArrow => XKeysymToKeycode(self.display, XK_RIGHT as *const c_void, 0),
                Key::DownArrow => XKeysymToKeycode(self.display, XK_DOWN as *const c_void, 0),
                Key::UpArrow => XKeysymToKeycode(self.display, XK_UP as *const c_void, 0),
                Key::F1 => XKeysymToKeycode(self.display, XK_F1 as *const c_void, 0),
                Key::F2 => XKeysymToKeycode(self.display, XK_F2 as *const c_void, 0),
                Key::F3 => XKeysymToKeycode(self.display, XK_F3 as *const c_void, 0),
                Key::F4 => XKeysymToKeycode(self.display, XK_F4 as *const c_void, 0),
                Key::F5 => XKeysymToKeycode(self.display, XK_F5 as *const c_void, 0),
                Key::F6 => XKeysymToKeycode(self.display, XK_F6 as *const c_void, 0),
                Key::F7 => XKeysymToKeycode(self.display, XK_F7 as *const c_void, 0),
                Key::F8 => XKeysymToKeycode(self.display, XK_F8 as *const c_void, 0),
                Key::F9 => XKeysymToKeycode(self.display, XK_F9 as *const c_void, 0),
                Key::F10 => XKeysymToKeycode(self.display, XK_F10 as *const c_void, 0),
                Key::F11 => XKeysymToKeycode(self.display, XK_F11 as *const c_void, 0),
                Key::F12 => XKeysymToKeycode(self.display, XK_F12 as *const c_void, 0),

                Key::Raw(raw_keycode) => raw_keycode as u32,
                Key::Layout(string) => self.get_layoutdependent_keycode(string),
            }
        }
    }

    fn get_layoutdependent_keycode(&self, string: String) -> u32 {
        let c_string = CString::new(string).unwrap();
        let keysym = unsafe { XStringToKeysym(c_string.as_ptr() as *mut c_char) };

        unsafe { XKeysymToKeycode(self.display, keysym, 0) }
    }

    fn keycode_click(&self, keycode: u32) {
        // use std::{thread, time};
        // thread::sleep(time::Duration::from_millis(20));
        self.keycode_down(keycode);
        self.keycode_up(keycode);
        // thread::sleep(time::Duration::from_millis(20));
    }

    fn keycode_down(&self, keycode: u32) {
        use std::{thread, time};
        thread::sleep(time::Duration::from_millis(20));
        unsafe {
            XTestFakeKeyEvent(self.display, keycode, 1, 0);
            XFlush(self.display);
        }
        thread::sleep(time::Duration::from_millis(20));
    }

    fn keycode_up(&self, keycode: u32) {
        use std::{thread, time};
        thread::sleep(time::Duration::from_millis(20));
        unsafe {
            XTestFakeKeyEvent(self.display, keycode, 0, 0);
            XFlush(self.display);
        }
        thread::sleep(time::Duration::from_millis(20));
    }
}
<|endoftext|>


<commit_before>//*****************************
// Java Codecity data model

extern crate classreader;

use self::classreader::*;
use std::collections::HashMap;
use std::cmp;

#[derive(Debug)]
pub struct Rect {
    // Dimenstions
    width:  u16,
    depth:  u16,
    // Placement
    pos_w:  u16,
    pos_d:  u16
}

impl Rect {
    fn new(w: u16, d: u16) -> Rect {
        Rect {
            width: w,
            depth: d,
            pos_w: 0,
            pos_d: 0
        }
    }

    fn area(self: &Rect) -> u16 {self.width * self.depth}

    fn place(self: &mut Rect, state: &mut PackState) {
        // println!("Used: {}x{} free: {}x{} placing {}x{}", cur_w, cur_d, free_w, free_d, b.width, b.depth);
        if self.width > state.free_w || self.depth > state.free_d {
            // decide the direction for growth
            if state.width <= state.depth {
                // Grow by width
                state.dir = Direction::Width;
                self.pos_w = state.width;
                self.pos_d = 1;
                state.cur_w = state.width;
                state.cur_d = 2 + self.depth;
                state.width += self.width + 1;
                state.depth = cmp::max(state.depth, self.depth + 2);
                state.free_w = self.width;
                state.free_d = state.depth - self.depth - 2;
            } else {
                // Grow by depth
                state.dir = Direction::Depth;
                self.pos_d = state.depth;
                self.pos_w = 1;
                state.cur_d = state.depth;
                state.cur_w = 2 + self.width;
                state.depth += self.depth + 1;
                state.width = cmp::max(state.width, self.width + 2);
                state.free_d = self.depth;
                state.free_w = state.width - self.width - 2;
            }
        } else {
            // Enough room
            match state.dir {
                Direction::Width => {
                    self.pos_w = state.cur_w;
                    self.pos_d = state.cur_d;
                    state.free_d -= self.depth + 1;
                    state.cur_d += self.depth + 1;
                }
                Direction::Depth => {
                    self.pos_d = state.cur_d;
                    self.pos_w = state.cur_w;
                    state.free_w -= self.width + 1;
                    state.cur_w += self.width + 1;
                }
            }
        }
    }
}

#[derive(Debug)]
pub struct Building {
    name:   String,
    rect:   Rect,
    height: u16,
}

impl Building {
    fn new(class: &Class) -> Building {
        Building {
            name: get_class_name(class).to_string(),
            rect: Rect::new(class.methods.len() as u16 + 1, class.fields.len() as u16 + 1),
            height: get_total_code_size(&class) as u16 / 10 + 1,
        }
    }

    fn pos(self: &Building) -> (u16, u16) {
        (self.rect.pos_w, self.rect.pos_d)
    }
}

enum Direction {
    Width,
    Depth
}

#[derive(Debug)]
pub struct Package {
    name:  String,
    width: u16,
    depth: u16,
    packages:  HashMap<String, Package>,
    buildings: Vec<Building>
}

struct PackState {
    // Total aread
    width: u16,
    depth: u16,
    // Free in the corrent row
    free_w: u16,
    free_d: u16,
    // Position of the next placement
    cur_w: u16,
    cur_d: u16,
    // Direction we just grew the area
    dir: Direction
}

impl Package {
    /// Create a new Package.
    pub fn new(name: &str) -> Package {
        Package {
            name: name.to_string(),
            width: 1,
            depth: 1,
            packages: HashMap::new(),
            buildings: Vec::new()
        }
    }

    fn size(self: &Package) -> (u16, u16) {
        (self.width, self.depth)
    }

    /// Add a class or package to this package
    fn add(&mut self, names: &[&str], class: &Class) {
        if names.len() == 1 {
            self.buildings.push(Building::new(class));
        } else {
            match names.split_first() {
                Some((head, tail)) => {
                    self.packages.entry(head.to_string()).or_insert_with(|| Package::new(head)).add(&tail, class);
                },
                _ => {}
            }
        }
    }

    /// Pack own members for the nice rendering
    fn pack(&mut self) {
        for (_, ch) in &mut self.packages {
            ch.pack();
        }
        let mut state = PackState {
            width: self.width,
            depth: self.depth,
            free_w: 0,
            free_d: 0,
            cur_w: 1,
            cur_d: 1,
            dir: Direction::Width
        };

        // TODO Packages
        // Buildings
        self.buildings.sort_by_key(|b| b.rect.area());
        self.buildings.reverse();
        for b in &mut self.buildings {
            b.rect.place(&mut state);
        }
        self.width = state.width;
        self.depth = state.depth;
    }

}

pub fn build_packages(classes: &Vec<Class>) -> Package {
    let mut root_pkg = Package::new("_root_");
    for c in classes {
        let name = get_class_name(&c);
        let names: Vec<&str> = name.split('/').collect();
        root_pkg.add(&names, c);
    }
    return root_pkg;
}

pub fn process_class(class: &Class) {
    assert_eq!(0xCAFEBABE, class.magic);
    println!("class: {}", get_class_name(&class));
    println!("method count: {}", class.methods.len());
    println!("field count:  {}", class.fields.len());
    println!("code size:    {}", get_total_code_size(&class));
    for method in &class.methods {
        println!("\tmethod: {}{}", get_string(&class, method.name_index as usize), get_string(&class, method.descriptor_index as usize));
        println!("\t\tsize:       {}", get_method_size(&method));
        println!("\t\tlines:      {}", get_method_lines(&method));
        println!("\t\tcomplexity: {}", get_method_complexity(&method));
    }

    println!("======================================================");
}

/// Get constant from a pool, correcting for java's 1-based indexes.
fn get_const(class: &Class, i: usize) -> &ConstantPoolInfo {
    &class.constant_pool[i - 1]
}

/// Get string from constant pool
fn get_string(class: &Class, index: usize) -> String {
    match get_const(class, index) {
        &ConstantPoolInfo::Utf8(ref s) => s.clone(),
        _ => "?".to_string()
    }
}

fn get_class_name(class: &Class) -> String {
    match get_const(class, class.this_class as usize) {
        &ConstantPoolInfo::Class(index) => get_string(class, index as usize),
        _ => "?".to_string()
    }
}

fn get_total_code_size(class: &Class) -> usize {
    class.methods.iter().fold(0usize, |sum, method| sum + get_method_size(method))
}

/// Compute method size in byte code instructions
/// If multiple code segments are present in a method (not a javac output) sum it up
fn get_method_size(method: &Method) -> usize {
    method.attributes.iter().fold(0usize, |sum, a| sum + match a {
            &Attribute::Code{ref code, ..} => code.len(),
            _ => 0
        })
}

/// Compute method size in lines of code.
/// If multiple code segments or multiple line number attrbitues are present in a method (not a javac output) sum it up
fn get_method_lines(method: &Method) -> u16 {
    method.attributes.iter().fold(0, |sum, a| sum + match a {
            &Attribute::Code{ref attributes, ..} => attributes.iter().fold(0, |sum, a| sum + match a {
                &Attribute::LineNumberTable(ref line_table) => {
                    let end = get_opt_line_number(line_table.last());
                    let start = get_opt_line_number(line_table.first());
                    if start > end { // TODO: Investigate when it happens
                        0
                    } else {
                        end - start
                    }
                },
                _ => 0
            }),
            _ => 0
        })

}

fn get_opt_line_number(line: Option<&LineNumber>) -> u16 {
    line.map(|x| x.line_number).unwrap_or(0)
}

fn get_method_complexity(method: &Method) -> u16 {
    method.attributes.iter().fold(0, |sum, a| sum + match a {
            &Attribute::Code{ref code, ..} => get_code_complexity(code),
            _ => 0
        })
}

/// Using complexity definition from http://www.leepoint.net/principles_and_practices/complexity/complexity-java-method.html
/// Start with 1 for the method. Add one for each of the following flow-related elements that are found in the method.
/// Returns     Each return that isn't the last statement of a method.
/// Selection   if, else, case, default.
/// Loops   for, while, do-while, break, and continue.
/// Operators   &&, ||, ?, and :
/// Exceptions  catch, finally, throw, or throws clause.
/// Bytecode reference: http://www.angelibrary.com/computer/java_21days/ch32.htm
fn get_code_complexity(code: &Vec<(u32, Instruction)>) -> u16 {
    // println!("{:?}", code);
    // We start from zero to account for always-present final return - it will add the "starting" 1 for us.
    code.iter().fold(0, |sum, instruction| sum + match instruction.1 {
        Instruction::areturn => 1,
        Instruction::athrow => 1,
        Instruction::dreturn => 1,
        Instruction::freturn => 1,
        Instruction::if_acmpeq(..) => 1,
        Instruction::if_acmpne(..) => 1,
        Instruction::if_icmpeq(..) => 1,
        Instruction::if_icmpne(..) => 1,
        Instruction::if_icmplt(..) => 1,
        Instruction::if_icmpge(..) => 1,
        Instruction::if_icmpgt(..) => 1,
        Instruction::if_icmple(..) => 1,
        Instruction::ifeq(..) => 1,
        Instruction::ifne(..) => 1,
        Instruction::iflt(..) => 1,
        Instruction::ifge(..) => 1,
        Instruction::ifgt(..) => 1,
        Instruction::ifle(..) => 1,
        Instruction::ifnonnull(..) => 1,
        Instruction::ifnull(..) => 1,
        Instruction::ireturn => 1,
        Instruction::lookupswitch(_, ref jumps) => jumps.len() as u16, // tricky - lets use the size of the jump table
        Instruction::lreturn => 1,
        Instruction::return_ => 1,
        Instruction::tableswitch(_, _, ref jumps) => jumps.len() as u16, // tricky - lets use the size of the jump table
        // Subroutines in the method. Currently - do not count them towards the complexity. They are used to compile "finally", but I do not see them as branching really. Finally block is executed every time
        // Instruction::jsr(..) => 1,
        // Instruction::jsr_w(..) => 1,
        // Instruction::ret => 1,
        // Instruction::ret_w => 1,
        _ => 0
    })
}

//*****************************
// Unit tests

fn mock(name: &str, w: u16, d: u16) -> Building {
  Building {
    name: name.to_string(),
    rect: Rect::new(w, d),
    height: 0
  }
}

/// A function to print out details about placement
fn debug_test(pkg: &Package) {
    println!("{:?}", pkg);
    for b in &pkg.buildings {
        println!("{:?}", b);
    }
}

#[test]
fn pack_4_singles() {
    let mut pkg = Package::new("_root_");
    for _ in 0..4 {
        pkg.buildings.push(mock("a", 1, 1));
    }
    pkg.pack();
    debug_test(&pkg);
    assert!(pkg.size() == (5, 5));
    // Make sure the last building is in the corner
    assert!(pkg.buildings[3].pos() == (3, 3));
}

#[test]
fn pack_5_singles() {
    let mut pkg = Package::new("_root_");
    for _ in 0..5 {
        pkg.buildings.push(mock("a", 1, 1));
    }
    pkg.pack();
    debug_test(&pkg);
    assert!(pkg.size() == (7, 5));
    assert!(pkg.buildings[4].pos() == (5, 1));
}

#[test]
fn pack_16_singles() {
    let mut pkg = Package::new("_root_");
    for _ in 0..16 {
        pkg.buildings.push(mock("a", 1, 1));
    }
    pkg.pack();
    debug_test(&pkg);
    assert!(pkg.size() == (9, 9));
    // Make sure the last building is in the corner
    assert!(pkg.buildings[15].pos() == (7, 7));
}

#[test]
fn pack_smart_square_width() {
    let mut pkg = Package::new("_root_");
    pkg.buildings.push(mock("w", 3, 1));
    for _ in 0..2 {
        pkg.buildings.push(mock("a", 1, 1));
    }
    pkg.pack();
    debug_test(&pkg);
    assert!(pkg.size() == (5, 5));
    // Make sure the last building is in the corner
    assert!(pkg.buildings[2].pos() == (3, 3));
}

#[test]
fn pack_buildings() {
    let mut pkg = Package::new("_root_");
    pkg.buildings.push(mock("b", 8, 3));
    pkg.buildings.push(mock("c", 4, 2));
    pkg.buildings.push(mock("a", 10, 7));
    pkg.buildings.push(mock("d", 3, 2));
    pkg.buildings.push(mock("e1", 1, 1));
    pkg.buildings.push(mock("e2", 1, 1));
    pkg.buildings.push(mock("e3", 1, 1));
    println!("Starting the test");
    pkg.pack();
    debug_test(&pkg);
    assert!(pkg.size() == (17, 13));
    assert!(pkg.buildings[0].pos() == (1, 1));
    assert!(pkg.buildings[1].pos() == (1, 9));
    assert!(pkg.buildings[2].pos() == (12, 1));
    assert!(pkg.buildings[3].pos() == (12, 4));
    assert!(pkg.buildings[4].pos() == (12, 7));
    assert!(pkg.buildings[5].pos() == (12, 9));
    assert!(pkg.buildings[6].pos() == (12, 11));

}<commit_msg>Finished refactoring<commit_after>//*****************************
// Java Codecity data model

extern crate classreader;

use self::classreader::*;
use std::collections::HashMap;
use std::cmp;

#[derive(Debug)]
pub struct Rect {
    // Dimenstions
    width:  u16,
    depth:  u16,
    // Placement
    pos_w:  u16,
    pos_d:  u16
}

impl Rect {
    fn new(w: u16, d: u16) -> Rect {
        Rect {
            width: w,
            depth: d,
            pos_w: 0,
            pos_d: 0
        }
    }

    fn area(self: &Rect) -> u16 {self.width * self.depth}

    fn place(self: &mut Rect, state: &mut PackState) {
        // println!("Used: {}x{} free: {}x{} placing {}x{}", cur_w, cur_d, free_w, free_d, b.width, b.depth);
        if self.width > state.free_w || self.depth > state.free_d {
            // decide the direction for growth
            if state.width <= state.depth {
                // Grow by width
                state.dir = Direction::Width;
                self.pos_w = state.width;
                self.pos_d = 1;
                state.cur_w = state.width;
                state.cur_d = 2 + self.depth;
                state.width += self.width + 1;
                state.depth = cmp::max(state.depth, self.depth + 2);
                state.free_w = self.width;
                state.free_d = state.depth - self.depth - 2;
            } else {
                // Grow by depth
                state.dir = Direction::Depth;
                self.pos_d = state.depth;
                self.pos_w = 1;
                state.cur_d = state.depth;
                state.cur_w = 2 + self.width;
                state.depth += self.depth + 1;
                state.width = cmp::max(state.width, self.width + 2);
                state.free_d = self.depth;
                state.free_w = state.width - self.width - 2;
            }
        } else {
            // Enough room
            match state.dir {
                Direction::Width => {
                    self.pos_w = state.cur_w;
                    self.pos_d = state.cur_d;
                    state.free_d -= self.depth + 1;
                    state.cur_d += self.depth + 1;
                }
                Direction::Depth => {
                    self.pos_d = state.cur_d;
                    self.pos_w = state.cur_w;
                    state.free_w -= self.width + 1;
                    state.cur_w += self.width + 1;
                }
            }
        }
    }
}

#[derive(Debug)]
pub struct Building {
    name:   String,
    rect:   Rect,
    height: u16,
}

impl Building {
    fn new(class: &Class) -> Building {
        Building {
            name: get_class_name(class).to_string(),
            rect: Rect::new(class.methods.len() as u16 + 1, class.fields.len() as u16 + 1),
            height: get_total_code_size(&class) as u16 / 10 + 1,
        }
    }

    fn pos(self: &Building) -> (u16, u16) {
        (self.rect.pos_w, self.rect.pos_d)
    }
}

enum Direction {
    Width,
    Depth
}

struct PackState {
    // Total aread
    width: u16,
    depth: u16,
    // Free in the corrent row
    free_w: u16,
    free_d: u16,
    // Position of the next placement
    cur_w: u16,
    cur_d: u16,
    // Direction we just grew the area
    dir: Direction
}

#[derive(Debug)]
pub struct Package {
    name:  String,
    rect: Rect,
    packages:  HashMap<String, Package>,
    buildings: Vec<Building>
}

impl Package {
    /// Create a new Package.
    pub fn new(name: &str) -> Package {
        Package {
            name: name.to_string(),
            rect: Rect::new(1, 1),
            packages: HashMap::new(),
            buildings: Vec::new()
        }
    }

    fn size(self: &Package) -> (u16, u16) {
        (self.rect.width, self.rect.depth)
    }

    /// Add a class or package to this package
    fn add(&mut self, names: &[&str], class: &Class) {
        if names.len() == 1 {
            self.buildings.push(Building::new(class));
        } else {
            match names.split_first() {
                Some((head, tail)) => {
                    self.packages.entry(head.to_string()).or_insert_with(|| Package::new(head)).add(&tail, class);
                },
                _ => {}
            }
        }
    }

    /// Pack own members for the nice rendering
    fn pack(&mut self) {
        for (_, ch) in &mut self.packages {
            ch.pack();
        }
        let mut state = PackState {
            width: self.rect.width,
            depth: self.rect.depth,
            free_w: 0,
            free_d: 0,
            cur_w: 1,
            cur_d: 1,
            dir: Direction::Width
        };

        // Packages
        let mut packages: Vec<&mut Package> = self.packages.iter_mut().map(|(_, v)| v).collect();
        packages.sort_by_key(|p| p.rect.area());
        packages.reverse();
        for p in &mut packages {
            p.rect.place(&mut state);
        }
        // Buildings
        self.buildings.sort_by_key(|b| b.rect.area());
        self.buildings.reverse();
        for b in &mut self.buildings {
            b.rect.place(&mut state);
        }
        self.rect.width = state.width;
        self.rect.depth = state.depth;
    }

}

pub fn build_packages(classes: &Vec<Class>) -> Package {
    let mut root_pkg = Package::new("_root_");
    for c in classes {
        let name = get_class_name(&c);
        let names: Vec<&str> = name.split('/').collect();
        root_pkg.add(&names, c);
    }
    return root_pkg;
}

pub fn process_class(class: &Class) {
    assert_eq!(0xCAFEBABE, class.magic);
    println!("class: {}", get_class_name(&class));
    println!("method count: {}", class.methods.len());
    println!("field count:  {}", class.fields.len());
    println!("code size:    {}", get_total_code_size(&class));
    for method in &class.methods {
        println!("\tmethod: {}{}", get_string(&class, method.name_index as usize), get_string(&class, method.descriptor_index as usize));
        println!("\t\tsize:       {}", get_method_size(&method));
        println!("\t\tlines:      {}", get_method_lines(&method));
        println!("\t\tcomplexity: {}", get_method_complexity(&method));
    }

    println!("======================================================");
}

/// Get constant from a pool, correcting for java's 1-based indexes.
fn get_const(class: &Class, i: usize) -> &ConstantPoolInfo {
    &class.constant_pool[i - 1]
}

/// Get string from constant pool
fn get_string(class: &Class, index: usize) -> String {
    match get_const(class, index) {
        &ConstantPoolInfo::Utf8(ref s) => s.clone(),
        _ => "?".to_string()
    }
}

fn get_class_name(class: &Class) -> String {
    match get_const(class, class.this_class as usize) {
        &ConstantPoolInfo::Class(index) => get_string(class, index as usize),
        _ => "?".to_string()
    }
}

fn get_total_code_size(class: &Class) -> usize {
    class.methods.iter().fold(0usize, |sum, method| sum + get_method_size(method))
}

/// Compute method size in byte code instructions
/// If multiple code segments are present in a method (not a javac output) sum it up
fn get_method_size(method: &Method) -> usize {
    method.attributes.iter().fold(0usize, |sum, a| sum + match a {
            &Attribute::Code{ref code, ..} => code.len(),
            _ => 0
        })
}

/// Compute method size in lines of code.
/// If multiple code segments or multiple line number attrbitues are present in a method (not a javac output) sum it up
fn get_method_lines(method: &Method) -> u16 {
    method.attributes.iter().fold(0, |sum, a| sum + match a {
            &Attribute::Code{ref attributes, ..} => attributes.iter().fold(0, |sum, a| sum + match a {
                &Attribute::LineNumberTable(ref line_table) => {
                    let end = get_opt_line_number(line_table.last());
                    let start = get_opt_line_number(line_table.first());
                    if start > end { // TODO: Investigate when it happens
                        0
                    } else {
                        end - start
                    }
                },
                _ => 0
            }),
            _ => 0
        })

}

fn get_opt_line_number(line: Option<&LineNumber>) -> u16 {
    line.map(|x| x.line_number).unwrap_or(0)
}

fn get_method_complexity(method: &Method) -> u16 {
    method.attributes.iter().fold(0, |sum, a| sum + match a {
            &Attribute::Code{ref code, ..} => get_code_complexity(code),
            _ => 0
        })
}

/// Using complexity definition from http://www.leepoint.net/principles_and_practices/complexity/complexity-java-method.html
/// Start with 1 for the method. Add one for each of the following flow-related elements that are found in the method.
/// Returns     Each return that isn't the last statement of a method.
/// Selection   if, else, case, default.
/// Loops   for, while, do-while, break, and continue.
/// Operators   &&, ||, ?, and :
/// Exceptions  catch, finally, throw, or throws clause.
/// Bytecode reference: http://www.angelibrary.com/computer/java_21days/ch32.htm
fn get_code_complexity(code: &Vec<(u32, Instruction)>) -> u16 {
    // println!("{:?}", code);
    // We start from zero to account for always-present final return - it will add the "starting" 1 for us.
    code.iter().fold(0, |sum, instruction| sum + match instruction.1 {
        Instruction::areturn => 1,
        Instruction::athrow => 1,
        Instruction::dreturn => 1,
        Instruction::freturn => 1,
        Instruction::if_acmpeq(..) => 1,
        Instruction::if_acmpne(..) => 1,
        Instruction::if_icmpeq(..) => 1,
        Instruction::if_icmpne(..) => 1,
        Instruction::if_icmplt(..) => 1,
        Instruction::if_icmpge(..) => 1,
        Instruction::if_icmpgt(..) => 1,
        Instruction::if_icmple(..) => 1,
        Instruction::ifeq(..) => 1,
        Instruction::ifne(..) => 1,
        Instruction::iflt(..) => 1,
        Instruction::ifge(..) => 1,
        Instruction::ifgt(..) => 1,
        Instruction::ifle(..) => 1,
        Instruction::ifnonnull(..) => 1,
        Instruction::ifnull(..) => 1,
        Instruction::ireturn => 1,
        Instruction::lookupswitch(_, ref jumps) => jumps.len() as u16, // tricky - lets use the size of the jump table
        Instruction::lreturn => 1,
        Instruction::return_ => 1,
        Instruction::tableswitch(_, _, ref jumps) => jumps.len() as u16, // tricky - lets use the size of the jump table
        // Subroutines in the method. Currently - do not count them towards the complexity. They are used to compile "finally", but I do not see them as branching really. Finally block is executed every time
        // Instruction::jsr(..) => 1,
        // Instruction::jsr_w(..) => 1,
        // Instruction::ret => 1,
        // Instruction::ret_w => 1,
        _ => 0
    })
}

//*****************************
// Unit tests

fn mock(name: &str, w: u16, d: u16) -> Building {
  Building {
    name: name.to_string(),
    rect: Rect::new(w, d),
    height: 0
  }
}

/// A function to print out details about placement
fn debug_test(pkg: &Package) {
    println!("{:?}", pkg);
    for b in &pkg.buildings {
        println!("{:?}", b);
    }
}

#[test]
fn pack_4_singles() {
    let mut pkg = Package::new("_root_");
    for _ in 0..4 {
        pkg.buildings.push(mock("a", 1, 1));
    }
    pkg.pack();
    debug_test(&pkg);
    assert!(pkg.size() == (5, 5));
    // Make sure the last building is in the corner
    assert!(pkg.buildings[3].pos() == (3, 3));
}

#[test]
fn pack_5_singles() {
    let mut pkg = Package::new("_root_");
    for _ in 0..5 {
        pkg.buildings.push(mock("a", 1, 1));
    }
    pkg.pack();
    debug_test(&pkg);
    assert!(pkg.size() == (7, 5));
    assert!(pkg.buildings[4].pos() == (5, 1));
}

#[test]
fn pack_16_singles() {
    let mut pkg = Package::new("_root_");
    for _ in 0..16 {
        pkg.buildings.push(mock("a", 1, 1));
    }
    pkg.pack();
    debug_test(&pkg);
    assert!(pkg.size() == (9, 9));
    // Make sure the last building is in the corner
    assert!(pkg.buildings[15].pos() == (7, 7));
}

#[test]
fn pack_smart_square_width() {
    let mut pkg = Package::new("_root_");
    pkg.buildings.push(mock("w", 3, 1));
    for _ in 0..2 {
        pkg.buildings.push(mock("a", 1, 1));
    }
    pkg.pack();
    debug_test(&pkg);
    assert!(pkg.size() == (5, 5));
    // Make sure the last building is in the corner
    assert!(pkg.buildings[2].pos() == (3, 3));
}

#[test]
fn pack_buildings() {
    let mut pkg = Package::new("_root_");
    pkg.buildings.push(mock("b", 8, 3));
    pkg.buildings.push(mock("c", 4, 2));
    pkg.buildings.push(mock("a", 10, 7));
    pkg.buildings.push(mock("d", 3, 2));
    pkg.buildings.push(mock("e1", 1, 1));
    pkg.buildings.push(mock("e2", 1, 1));
    pkg.buildings.push(mock("e3", 1, 1));
    println!("Starting the test");
    pkg.pack();
    debug_test(&pkg);
    assert!(pkg.size() == (17, 13));
    assert!(pkg.buildings[0].pos() == (1, 1));
    assert!(pkg.buildings[1].pos() == (1, 9));
    assert!(pkg.buildings[2].pos() == (12, 1));
    assert!(pkg.buildings[3].pos() == (12, 4));
    assert!(pkg.buildings[4].pos() == (12, 7));
    assert!(pkg.buildings[5].pos() == (12, 9));
    assert!(pkg.buildings[6].pos() == (12, 11));

}<|endoftext|>


<commit_before>use asm_ops::*;

use std::error::Error;
use std::io::prelude::*;
use std::fs::File;
use std::path::Path;
use std::process::Command;

pub fn compile(ops: &Vec<AsmOp>) -> Vec<u8>{
    let mut str = "use64\n".to_string();
    for op in ops{
        match *op {
            AsmOp::Add(ref dest, ref operand) => match *operand{
                AsmOperand::RegisterOperand(ref source) => str = str + &format!("add {:?}, {:?}\n", dest, source),
                AsmOperand::Value(ref i) => str = str + &format!("add {:?}, {:?}\n", dest, i)
            },
            AsmOp::Mul(ref dest, ref operand) => match *operand{
                AsmOperand::RegisterOperand(ref source) => str = str + &format!("imul {:?}, {:?}\n", dest, source),
                AsmOperand::Value(ref i) => str = str + &format!("imul {:?}, {:?}\n", dest, i)
            },
            AsmOp::Pop(ref dest) => match *dest {
                AsmOperand::RegisterOperand(ref dest) => str = str + &format!("pop {:?}\n", dest),
                AsmOperand::Value(ref dest) => str = str + &format!("pop {:?}\n", dest)
            },
            AsmOp::Push(ref dest) => match *dest {
                AsmOperand::RegisterOperand(ref dest) => str = str + &format!("push {:?}\n", dest),
                AsmOperand::Value(ref dest) => str = str + &format!("push {:?}\n", dest)
            },
            AsmOp::Mov(ref dest, ref operand) => match *operand{
                AsmOperand::RegisterOperand(ref source) => str = str + &format!("mov {:?}, {:?}\n", dest, source),
                AsmOperand::Value(ref i) => str = str + &format!("mov {:?}, {:?}\n", dest, i)
            },
            // _ => {}
        }
    }
    str = str + "pop rax\n";
    println!("\nOutput assembly is:\n{}\n",str);

    write_asm(&str);
    assemble();
    read_bytes()
}

fn write_asm(str: &String) {
    let path = Path::new("temp.asm");
    let display = path.display();

    let mut file = match File::create(&path) {
        Err(why) => panic!("couldn't create {}: {}", display, Error::description(&why)),
        Ok(file) => file,
    };

    match file.write_all(str.as_bytes()) {
        Err(why) => {
            panic!("couldn't write to {}: {}", display, Error::description(&why))
        },
        Ok(_) => println!("Successfully wrote to {}", display),
    }
}

fn assemble() {
    let output = Command::new("fasm").arg("temp.asm").output().unwrap_or_else(|e| {
        panic!("failed to execute process: {}", e)
    });

    if !output.status.success() {
        println!("status: {}", output.status);
        println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
        println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
    } else {
        println!("Successfully assembled to temp.bin");
    }
}

fn read_bytes() -> Vec<u8>{
    let path = Path::new("temp.bin");
    let display = path.display();

    let mut file = match File::open(&path) {
       Err(why) => panic!("couldn't open {}: {}", display, Error::description(&why)),
       Ok(file) => file,
   };

    let mut contents: Vec<u8> = Vec::new();
    // Returns amount of bytes read and append the result to the buffer
    let result = file.read_to_end(&mut contents).unwrap();
    println!("Program consists of {} bytes", result);
    contents
}
<commit_msg>Moved temporary generated files into target/<commit_after>use asm_ops::*;

use std::error::Error;
use std::io::prelude::*;
use std::fs::File;
use std::path::Path;
use std::process::Command;

pub fn compile(ops: &Vec<AsmOp>) -> Vec<u8>{
    let mut str = "use64\n".to_string();
    for op in ops{
        match *op {
            AsmOp::Add(ref dest, ref operand) => match *operand{
                AsmOperand::RegisterOperand(ref source) => str = str + &format!("add {:?}, {:?}\n", dest, source),
                AsmOperand::Value(ref i) => str = str + &format!("add {:?}, {:?}\n", dest, i)
            },
            AsmOp::Mul(ref dest, ref operand) => match *operand{
                AsmOperand::RegisterOperand(ref source) => str = str + &format!("imul {:?}, {:?}\n", dest, source),
                AsmOperand::Value(ref i) => str = str + &format!("imul {:?}, {:?}\n", dest, i)
            },
            AsmOp::Pop(ref dest) => match *dest {
                AsmOperand::RegisterOperand(ref dest) => str = str + &format!("pop {:?}\n", dest),
                AsmOperand::Value(ref dest) => str = str + &format!("pop {:?}\n", dest)
            },
            AsmOp::Push(ref dest) => match *dest {
                AsmOperand::RegisterOperand(ref dest) => str = str + &format!("push {:?}\n", dest),
                AsmOperand::Value(ref dest) => str = str + &format!("push {:?}\n", dest)
            },
            AsmOp::Mov(ref dest, ref operand) => match *operand{
                AsmOperand::RegisterOperand(ref source) => str = str + &format!("mov {:?}, {:?}\n", dest, source),
                AsmOperand::Value(ref i) => str = str + &format!("mov {:?}, {:?}\n", dest, i)
            },
            // _ => {}
        }
    }
    str = str + "pop rax\n";
    println!("\nOutput assembly is:\n{}\n",str);

    write_asm(&str);
    assemble();
    read_bytes()
}

fn write_asm(str: &String) {
    let path = Path::new("target/temp.asm");
    let display = path.display();

    let mut file = match File::create(&path) {
        Err(why) => panic!("couldn't create {}: {}", display, Error::description(&why)),
        Ok(file) => file,
    };

    match file.write_all(str.as_bytes()) {
        Err(why) => {
            panic!("couldn't write to {}: {}", display, Error::description(&why))
        },
        Ok(_) => println!("Successfully wrote to {}", display),
    }
}

fn assemble() {
    let output = Command::new("fasm").arg("target/temp.asm").output().unwrap_or_else(|e| {
        panic!("failed to execute process: {}", e)
    });

    if !output.status.success() {
        println!("status: {}", output.status);
        println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
        println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
    } else {
        println!("Successfully assembled to temp.bin");
    }
}

fn read_bytes() -> Vec<u8>{
    let path = Path::new("target/temp.bin");
    let display = path.display();

    let mut file = match File::open(&path) {
       Err(why) => panic!("couldn't open {}: {}", display, Error::description(&why)),
       Ok(file) => file,
   };

    let mut contents: Vec<u8> = Vec::new();
    // Returns amount of bytes read and append the result to the buffer
    let result = file.read_to_end(&mut contents).unwrap();
    println!("Program consists of {} bytes", result);
    contents
}
<|endoftext|>


<commit_before>use std::{cell::RefCell, collections::BTreeMap};

use dces::prelude::*;

use crate::{
    prelude::*, proc_macros::IntoLayout, render::RenderContext2D, theming::*, tree::Tree,
    utils::prelude::*,
};

use super::{component, component_or_default, component_try_mut, try_component, Layout};

/// The `PopupLayout` handles measuring and arrangement of a `Popup` widget.
#[derive(Default, IntoLayout)]
pub struct PopupLayout {
    // TODO: Add padding to the widget.
    desired_size: RefCell<DirtySize>,
}

/// Associated functions, that react on any given state change inside the `Popup` widget.
impl PopupLayout {
    /// Instatiate a new PopupLayout object.
    pub fn new() -> Self {
	PopupLayout::default()
    }
}

impl Layout for PopupLayout {
    fn measure(
	&self,
	render_context_2_d: &mut RenderContext2D,
	entity: Entity,
	ecm: &mut EntityComponentManager<Tree>,
	layouts: &BTreeMap<Entity, Box<dyn Layout>>,
	theme: &Theme,
    ) -> DirtySize {
	// if visibility is collapsed, the entity is rendered but
	// shouldn't consume any size in the render buffer.
	if component::<Visibility>(ecm, entity, "visibility") == Visibility::Collapsed {
	    self.desired_size.borrow_mut().set_size(0.0, 0.0);
	    return *self.desired_size.borrow();
	}

	/*
	if let Some(target) = try_component::<u32>(ecm, entity, "target") {
	    let target_bounds = component::<Rectangle>(ecm, target.into(), "bounds");
	    component_try_mut::<Constraint>(ecm, entity, "constraint")
		.unwrap()
		.set_width(target_bounds.width());
	}

	let horizontal_alignment: Alignment = component(ecm, entity, "h_align");
	let vertical_alignment: Alignment = component(ecm, entity, "v_align");

	if horizontal_alignment != self.old_alignment.get().1
	    || vertical_alignment != self.old_alignment.get().0
	{
	    self.desired_size.borrow_mut().set_dirty(true);
	}

	if constraint.height() > 0.0 {
	    self.desired_size
		.borrow_mut()
		.set_height(constraint.height());
	}
	 */

	// TODO: implement usage of padding
	let _padding: Thickness = component(ecm, entity, "padding");

	if let Some(target) = try_component::<PopupTarget>(ecm, entity, "target") {
	    let current_bounds: Rectangle = component(ecm, entity, "bounds");
	    let current_constraint: Constraint = component(ecm, entity, "constraint");

	    let real_target_bounds = match target {
		PopupTarget::Entity(entity) => {
		    let target_position: Point = component(ecm, entity.into(), "position");

		    // WARNING: widget bounds (width an height values)
		    // will be measured as available space for the
		    // widget. Only in state `post_layout_update` it
		    // will reflect the effective size of the given
		    // entity.
		    let mut target_bounds: Rectangle = component(ecm, entity.into(), "bounds");
		    target_bounds.set_position(target_position);
		    target_bounds
		}
		PopupTarget::Point(mut point) => {
		    point.set_x(point.x() + current_bounds.width() / 2.0);
		    point.set_y(point.y() + current_bounds.height() / 2.0);
		    Rectangle::new(point, (0.0, 0.0))
		}
	    };

	    let placement: Placement =
		component_or_default(ecm, entity, "placement");

	    // TODO: enable `margin` handling
	    let new_popup_size = match placement {
		Placement::Left(_margin) => {
		    let current_v_align: Alignment = component(ecm, entity, "v_align");

		    let width = current_bounds.width();
		    let height = current_v_align.align_measure(
			real_target_bounds.height(),
			current_bounds.height(),
			0.0,
			0.0,
		    );

		    // respect given bound constraints
		    current_constraint.perform((width, height))
		}
		Placement::Right(_margin) => {
		    let current_v_align: Alignment = component(ecm, entity, "v_align");

		    let width = current_bounds.width();
		    let height = current_v_align.align_measure(
			real_target_bounds.height(),
			current_bounds.height(),
			0.0,
			0.0,
		    );

		    current_constraint.perform((width, height))
		}
		Placement::Top(_margin) => {
		    let current_h_align: Alignment = component(ecm, entity, "h_align");

		    let width = current_h_align.align_measure(
			real_target_bounds.width(),
			current_bounds.width(),
			0.0,
			0.0,
		    );
		    let height = current_bounds.height();

		    current_constraint.perform((width, height))
		}
		Placement::Bottom(_margin) => {
		   let current_h_align: Alignment = component(ecm, entity, "h_align");

		    let width = current_h_align.align_measure(
			real_target_bounds.width(),
			current_bounds.width(),
			0.0,
			0.0,
		    );
		    let height = current_bounds.height();

		    current_constraint.perform((width, height))
		}
	    };

	    {
		let mut desired_size = self.desired_size.borrow_mut();
		desired_size.set_width(new_popup_size.0);
		desired_size.set_height(new_popup_size.1);
	    }

	    let padding: Thickness = component(ecm, entity, "padding");
	    for index in 0..ecm.entity_store().children[&entity].len() {
		let child = ecm.entity_store().children[&entity][index];

		if let Some(child_layout) = layouts.get(&child) {
		    let child_desired_size =
			child_layout.measure(render_context_2_d, child, ecm, layouts, theme);
		    let mut desired_size = self.desired_size.borrow().size();

		    let dirty = child_desired_size.dirty() || self.desired_size.borrow().dirty();
		    self.desired_size.borrow_mut().set_dirty(dirty);

		    let child_margin = *ecm
			.component_store()
			.get::<Thickness>("margin", child)
			.unwrap();

		    desired_size.0 = desired_size.0.max(
			child_desired_size.width()
			    + padding.left()
			    + padding.right()
			    + child_margin.left()
			    + child_margin.right(),
		    );
		    desired_size.1 = desired_size.1.max(
			child_desired_size.height()
			    + padding.top()
			    + padding.bottom()
			    + child_margin.top()
			    + child_margin.left(),
		    );

		    self.desired_size
			.borrow_mut()
			.set_size(desired_size.0, desired_size.1);
		}
	    }
	} else {
	    println!("Target not found");
	}

	    *self.desired_size.borrow()
    }

    fn arrange(
	&self,
	render_context_2_d: &mut RenderContext2D,
	parent_size: (f64, f64),
	entity: Entity,
	ecm: &mut EntityComponentManager<Tree>,
	layouts: &BTreeMap<Entity, Box<dyn Layout>>,
	theme: &Theme,
    ) -> (f64, f64) {
	if component::<Visibility>(ecm, entity, "visibility") == Visibility::Collapsed {
	    self.desired_size.borrow_mut().set_size(0.0, 0.0);
	    return (0.0, 0.0);
	}

	if !self.desired_size.borrow().dirty() {
	    return self.desired_size.borrow().size();
	}

	let horizontal_alignment: Alignment = component(ecm, entity, "h_align");
	let vertical_alignment: Alignment = component(ecm, entity, "v_align");
	let margin = *ecm
	    .component_store()
	    .get::<Thickness>("margin", entity)
	    .unwrap();
	let padding: Thickness = component(ecm, entity, "padding");
	let constraint: Constraint = component(ecm, entity, "constraint");

	let size = constraint.perform((
	    horizontal_alignment.align_measure(
		parent_size.0,
		self.desired_size.borrow().width(),
		margin.left(),
		margin.right(),
	    ),
	    vertical_alignment.align_measure(
		parent_size.1,
		self.desired_size.borrow().height(),
		margin.top(),
		margin.bottom(),
	    ),
	));

	if let Some(bounds) = component_try_mut::<Rectangle>(ecm, entity, "bounds") {
	    bounds.set_width(size.0);
	    bounds.set_height(size.1);
	}

	mark_as_dirty("bounds", entity, ecm);

	let available_size = (
	    size.0 - padding.left() - padding.right(),
	    size.1 - padding.top() - padding.bottom(),
	);

	for index in 0..ecm.entity_store().children[&entity].len() {
	    let child = ecm.entity_store().children[&entity][index];

	    let child_margin: Thickness = component(ecm, entity, "margin");

	    if let Some(child_layout) = layouts.get(&child) {
		child_layout.arrange(
		    render_context_2_d,
		    available_size,
		    child,
		    ecm,
		    layouts,
		    theme,
		);
	    }

	    let child_horizontal_alignment: Alignment =
		*ecm.component_store().get("h_align", child).unwrap();
	    let child_vertical_alignment: Alignment =
		*ecm.component_store().get("v_align", child).unwrap();
	    if let Ok(child_bounds) = ecm
		.component_store_mut()
		.get_mut::<Rectangle>("bounds", child)
	    {
		child_bounds.set_x(
		    padding.left()
			+ child_horizontal_alignment.align_position(
			    available_size.0,
			    child_bounds.width(),
			    child_margin.left(),
			    child_margin.right(),
			),
		);
		child_bounds.set_y(
		    padding.top()
			+ child_vertical_alignment.align_position(
			    available_size.1,
			    child_bounds.height(),
			    child_margin.top(),
			    child_margin.bottom(),
			),
		);
	    }

	    mark_as_dirty("bounds", child, ecm);
	}

	self.desired_size.borrow_mut().set_dirty(false);
	size
    }
}
<commit_msg>orbtk_core: update popup_v2 layout<commit_after>use std::{cell::RefCell, collections::BTreeMap};

use dces::prelude::*;

use crate::{
    prelude::*, proc_macros::IntoLayout, render::RenderContext2D, theming::*, tree::Tree,
    utils::prelude::*,
};

use super::{component, component_or_default, component_try_mut, try_component, Layout};

/// The `PopupLayout` handles measuring and arrangement of a `Popup` widget.
#[derive(Default, IntoLayout)]
pub struct PopupLayout {
    // TODO: Add padding to the widget.
    desired_size: RefCell<DirtySize>,
}

/// Associated functions, that react on any given state change inside the `Popup` widget.
impl PopupLayout {
    /// Instatiate a new PopupLayout object.
    pub fn new() -> Self {
	PopupLayout::default()
    }
}

impl Layout for PopupLayout {
    fn measure(
	&self,
	render_context_2_d: &mut RenderContext2D,
	entity: Entity,
	ecm: &mut EntityComponentManager<Tree>,
	layouts: &BTreeMap<Entity, Box<dyn Layout>>,
	theme: &Theme,
    ) -> DirtySize {
	// if visibility is collapsed, the entity is rendered but
	// shouldn't consume any size in the render buffer.
	if component::<Visibility>(ecm, entity, "visibility") == Visibility::Collapsed {
	    self.desired_size.borrow_mut().set_size(0.0, 0.0);
	    return *self.desired_size.borrow();
	}

	/*
	if let Some(target) = try_component::<u32>(ecm, entity, "target") {
	    let target_bounds = component::<Rectangle>(ecm, target.into(), "bounds");
	    component_try_mut::<Constraint>(ecm, entity, "constraint")
		.unwrap()
		.set_width(target_bounds.width());
	}

	let horizontal_alignment: Alignment = component(ecm, entity, "h_align");
	let vertical_alignment: Alignment = component(ecm, entity, "v_align");

	if horizontal_alignment != self.old_alignment.get().1
	    || vertical_alignment != self.old_alignment.get().0
	{
	    self.desired_size.borrow_mut().set_dirty(true);
	}

	if constraint.height() > 0.0 {
	    self.desired_size
		.borrow_mut()
		.set_height(constraint.height());
	}
	 */

	// TODO: implement usage of padding
	let _padding: Thickness = component(ecm, entity, "padding");

	if let Some(target) = try_component::<PopupTarget>(ecm, entity, "target") {
	    let current_bounds: Rectangle = component(ecm, entity, "bounds");
	    let current_constraint: Constraint = component(ecm, entity, "constraint");

	    let real_target_bounds = match target {
		PopupTarget::Entity(entity) => {
		    let target_position: Point = component(ecm, entity.into(), "position");

		    // WARNING: widget bounds (width an height values)
		    // will be measured as available space for the
		    // widget. Only in state `post_layout_update` it
		    // will reflect the effective size of the given
		    // entity.
		    let mut target_bounds: Rectangle = component(ecm, entity.into(), "bounds");
		    target_bounds.set_position(target_position);
		    target_bounds
		}
		PopupTarget::Point(mut point) => {
		    point.set_x(point.x() + current_bounds.width() / 2.0);
		    point.set_y(point.y() + current_bounds.height() / 2.0);
		    Rectangle::new(point, (0.0, 0.0))
		}
	    };

	    let placement: Placement =
		component_or_default(ecm, entity, "placement");

	    let new_popup_size = match placement {
		Placement::Bottom => {
		    let current_h_align: Alignment = component(ecm, entity, "h_align");

		    let width = current_h_align.align_measure(
			real_target_bounds.width(),
			current_bounds.width(),
			0.0,
			0.0,
		    );
		    let height = current_bounds.height();

		    //let y = real_target_bounds.y() - current_bounds.height();

		    current_constraint.perform((width, height))
		}
		Placement::BottomLeft => {
		    let current_h_align: Alignment = component(ecm, entity, "h_align");

		    let width = current_h_align.align_measure(
			real_target_bounds.width(),
			current_bounds.width(),
			0.0,
			0.0,
		    );
		    let height = current_bounds.height();

		    current_constraint.perform((width, height))
		}
		Placement::BottomRight => {
		    let current_h_align: Alignment = component(ecm, entity, "h_align");

		    let width = current_h_align.align_measure(
			real_target_bounds.width(),
			current_bounds.width(),
			0.0,
			0.0,
		    );
		    let height = current_bounds.height();

		    current_constraint.perform((width, height))
		}
		Placement::Left => {
		    let current_v_align: Alignment = component(ecm, entity, "v_align");

		    let width = current_bounds.width();
		    let height = current_v_align.align_measure(
			real_target_bounds.height(),
			current_bounds.height(),
			0.0,
			0.0,
		    );

		    // respect given bound constraints
		    current_constraint.perform((width, height))
		}
		Placement::Right => {
		    let current_v_align: Alignment = component(ecm, entity, "v_align");

		    let width = current_bounds.width();
		    let height = current_v_align.align_measure(
			real_target_bounds.height(),
			current_bounds.height(),
			0.0,
			0.0,
		    );

		    current_constraint.perform((width, height))
		}
		Placement::Top => {
		    let current_h_align: Alignment = component(ecm, entity, "h_align");

		    let width = current_h_align.align_measure(
			real_target_bounds.width(),
			current_bounds.width(),
			0.0,
			0.0,
		    );
		    let height = current_bounds.height();

		    current_constraint.perform((width, height))
		}
		Placement::TopLeft => {
		    let current_h_align: Alignment = component(ecm, entity, "h_align");

		    let width = current_h_align.align_measure(
			real_target_bounds.width(),
			current_bounds.width(),
			0.0,
			0.0,
		    );
		    let height = current_bounds.height();

		    current_constraint.perform((width, height))
		}
		Placement::TopRight => {
		    let current_h_align: Alignment = component(ecm, entity, "h_align");

		    let width = current_h_align.align_measure(
			real_target_bounds.width(),
			current_bounds.width(),
			0.0,
			0.0,
		    );
		    let height = current_bounds.height();

		    current_constraint.perform((width, height))
		}
	    };

	    {
		let mut desired_size = self.desired_size.borrow_mut();
		desired_size.set_width(new_popup_size.0);
		desired_size.set_height(new_popup_size.1);
	    }

	    let padding: Thickness = component(ecm, entity, "padding");
	    for index in 0..ecm.entity_store().children[&entity].len() {
		let child = ecm.entity_store().children[&entity][index];

		if let Some(child_layout) = layouts.get(&child) {
		    let child_desired_size =
			child_layout.measure(render_context_2_d, child, ecm, layouts, theme);
		    let mut desired_size = self.desired_size.borrow().size();

		    let dirty = child_desired_size.dirty() || self.desired_size.borrow().dirty();
		    self.desired_size.borrow_mut().set_dirty(dirty);

		    let child_margin = *ecm
			.component_store()
			.get::<Thickness>("margin", child)
			.unwrap();

		    desired_size.0 = desired_size.0.max(
			child_desired_size.width()
			    + padding.left()
			    + padding.right()
			    + child_margin.left()
			    + child_margin.right(),
		    );
		    desired_size.1 = desired_size.1.max(
			child_desired_size.height()
			    + padding.top()
			    + padding.bottom()
			    + child_margin.top()
			    + child_margin.left(),
		    );

		    self.desired_size
			.borrow_mut()
			.set_size(desired_size.0, desired_size.1);
		}
	    }
	} else {
	    println!("Target not found");
	}

	    *self.desired_size.borrow()
    }

    fn arrange(
	&self,
	render_context_2_d: &mut RenderContext2D,
	parent_size: (f64, f64),
	entity: Entity,
	ecm: &mut EntityComponentManager<Tree>,
	layouts: &BTreeMap<Entity, Box<dyn Layout>>,
	theme: &Theme,
    ) -> (f64, f64) {
	if component::<Visibility>(ecm, entity, "visibility") == Visibility::Collapsed {
	    self.desired_size.borrow_mut().set_size(0.0, 0.0);
	    return (0.0, 0.0);
	}

	if !self.desired_size.borrow().dirty() {
	    return self.desired_size.borrow().size();
	}

	let horizontal_alignment: Alignment = component(ecm, entity, "h_align");
	let vertical_alignment: Alignment = component(ecm, entity, "v_align");
	let margin = *ecm
	    .component_store()
	    .get::<Thickness>("margin", entity)
	    .unwrap();
	let padding: Thickness = component(ecm, entity, "padding");
	let constraint: Constraint = component(ecm, entity, "constraint");

	let size = constraint.perform((
	    horizontal_alignment.align_measure(
		parent_size.0,
		self.desired_size.borrow().width(),
		margin.left(),
		margin.right(),
	    ),
	    vertical_alignment.align_measure(
		parent_size.1,
		self.desired_size.borrow().height(),
		margin.top(),
		margin.bottom(),
	    ),
	));

	if let Some(bounds) = component_try_mut::<Rectangle>(ecm, entity, "bounds") {
	    bounds.set_width(size.0);
	    bounds.set_height(size.1);
	}

	mark_as_dirty("bounds", entity, ecm);

	let available_size = (
	    size.0 - padding.left() - padding.right(),
	    size.1 - padding.top() - padding.bottom(),
	);

	for index in 0..ecm.entity_store().children[&entity].len() {
	    let child = ecm.entity_store().children[&entity][index];

	    let child_margin: Thickness = component(ecm, entity, "margin");

	    if let Some(child_layout) = layouts.get(&child) {
		child_layout.arrange(
		    render_context_2_d,
		    available_size,
		    child,
		    ecm,
		    layouts,
		    theme,
		);
	    }

	    let child_horizontal_alignment: Alignment =
		*ecm.component_store().get("h_align", child).unwrap();
	    let child_vertical_alignment: Alignment =
		*ecm.component_store().get("v_align", child).unwrap();
	    if let Ok(child_bounds) = ecm
		.component_store_mut()
		.get_mut::<Rectangle>("bounds", child)
	    {
		child_bounds.set_x(
		    padding.left()
			+ child_horizontal_alignment.align_position(
			    available_size.0,
			    child_bounds.width(),
			    child_margin.left(),
			    child_margin.right(),
			),
		);
		child_bounds.set_y(
		    padding.top()
			+ child_vertical_alignment.align_position(
			    available_size.1,
			    child_bounds.height(),
			    child_margin.top(),
			    child_margin.bottom(),
			),
		);
	    }

	    mark_as_dirty("bounds", child, ecm);
	}

	self.desired_size.borrow_mut().set_dirty(false);
	size
    }
}
<|endoftext|>


<commit_before><commit_msg>Fix replacing for loops over ranges with for_each.<commit_after><|endoftext|>


<commit_before>//! The oldest module, the data module stores all the data needed to collect
//! and also the search, dir algorithms
pub mod bktree;
pub mod collection;
pub mod ipc;

use super::ctrl::{CollectionPathAlive, ForwardNetMessage, NetMessages, Status, UiUpdateMsg};
use collection::{Collection, Container};

use std::{
    path::Path, // path, clear
    sync::{mpsc::Sender, Arc as SArc, Mutex as SMutex},
};

// todo: hide it
pub struct CollectionOutputData {
    pub nr_found_songs: u32,
}

pub fn search_in_single_path(
    has_ui: bool,
    collection_data: SArc<SMutex<Container>>,
    collection_protected: SArc<SMutex<Collection>>,
    mutex_to_ui_msg: SArc<SMutex<Sender<UiUpdateMsg>>>,
    index: usize,
    elem: &str,
) -> CollectionOutputData {
    if !has_ui {
        println!("[{:?}] looking into path {:?}", index, elem);
    } else {
        // send start animation for that path
        mutex_to_ui_msg
            .lock()
            .and_then(|locked_to_start_ui_message| {
                trace!("send startAnimation for path {:?}", index);
                locked_to_start_ui_message
                    .send(UiUpdateMsg::CollectionUpdate(
                        CollectionPathAlive::BusyPath(index),
                        Status::ON,
                    ))
                    .unwrap_or_else(|_| warn!("... lost start animation for index {:?}", index));
                trace!("start busy animation");
                Ok(())
            })
            .unwrap_or_else(|_| error!("... that should not happen here at start"));
    }
    // this collection lock is fine, it's not the main Container, only some data
    let locked_collection = &mut *collection_protected.lock().unwrap();

    // do it: main task here is to visit and dive deep
    //        into the subfolders of this folder
    match locked_collection.visit_path(
        collection_data,
        Path::new(elem),
        &collection::Collection::visit_files,
    ) {
        Ok(local_stats) => {
            if has_ui {
                // send stop animation for that path
                trace!("send stopAnimation for path {:?}", index);
                mutex_to_ui_msg
                    .lock()
                    .and_then(|locked_to_stop_ui_message| {
                        locked_to_stop_ui_message
                            .send(UiUpdateMsg::CollectionUpdate(
                                CollectionPathAlive::BusyPath(index),
                                Status::OFF,
                            ))
                            .unwrap_or_else(|_| warn!("... lost stop animation for {:?}", index));
                        trace!("stop busy animation");
                        Ok(())
                    })
                    .unwrap_or_else(|_| {
                        error!("... that should not happen here at stop");
                    });
            } else {
                let text = format!(
                    "\n\
                             analyzed: {an:>width$}, faulty: {fa:>width$}\n\
                             searched: {se:>width$}, other: {ot:>width$}",
                    an = local_stats.analyzed,
                    fa = local_stats.faulty,
                    se = local_stats.searched,
                    ot = local_stats.other,
                    width = 3
                );
                println!("[{:?}] done {}", index, text);
            }
            // return this here
            CollectionOutputData {
                nr_found_songs: local_stats.analyzed,
            }
        }
        Err(_e) => {
            let text = format!("An error has occurred in search path [{}]!!", index);
            if has_ui {
                mutex_to_ui_msg
                    .lock()
                    .and_then(|locked_update_message| {
                        locked_update_message
                            .send(UiUpdateMsg::NetUpdate(ForwardNetMessage::Stats(
                                NetMessages::Debug(text),
                            )))
                            .unwrap();
                        Ok(())
                    })
                    .unwrap();
            } else {
                println!("{:?}", text);
            }
            // return this here
            CollectionOutputData { nr_found_songs: 0 }
        }
    }
}
<commit_msg>[collection] added todo for path, subpaths<commit_after>//! The oldest module, the data module stores all the data needed to collect
//! and also the search, dir algorithms
pub mod bktree;
pub mod collection;
pub mod ipc;

use super::ctrl::{CollectionPathAlive, ForwardNetMessage, NetMessages, Status, UiUpdateMsg};
use collection::{Collection, Container};

use std::{
    path::Path, // path, clear
    sync::{mpsc::Sender, Arc as SArc, Mutex as SMutex},
};

// todo: hide it
pub struct CollectionOutputData {
    pub nr_found_songs: u32,
}

pub fn search_in_single_path(
    has_ui: bool,
    collection_data: SArc<SMutex<Container>>,
    collection_protected: SArc<SMutex<Collection>>,
    mutex_to_ui_msg: SArc<SMutex<Sender<UiUpdateMsg>>>,
    index: usize,
    elem: &str,
) -> CollectionOutputData {
    if !has_ui {
        println!("[{:?}] looking into path {:?}", index, elem);
    } else {
        // send start animation for that path
        mutex_to_ui_msg
            .lock()
            .and_then(|locked_to_start_ui_message| {
                trace!("send startAnimation for path {:?}", index);
                locked_to_start_ui_message
                    .send(UiUpdateMsg::CollectionUpdate(
                        CollectionPathAlive::BusyPath(index),
                        Status::ON,
                    ))
                    .unwrap_or_else(|_| warn!("... lost start animation for index {:?}", index));
                trace!("start busy animation");
                Ok(())
            })
            .unwrap_or_else(|_| error!("... that should not happen here at start"));
    }
    // this collection lock is fine, it's not the main Container, only some data
    let locked_collection = &mut *collection_protected.lock().unwrap();

    // do it: main task here is to visit and dive deep
    //        into the subfolders of this folder
    // todo: in some time, check if path a subpath of another also being searched
    //       super-path (which I use for testing for duplicates), and exclude the subpath!
    match locked_collection.visit_path(
        collection_data,
        Path::new(elem),
        &collection::Collection::visit_files,
    ) {
        Ok(local_stats) => {
            if has_ui {
                // send stop animation for that path
                trace!("send stopAnimation for path {:?}", index);
                mutex_to_ui_msg
                    .lock()
                    .and_then(|locked_to_stop_ui_message| {
                        locked_to_stop_ui_message
                            .send(UiUpdateMsg::CollectionUpdate(
                                CollectionPathAlive::BusyPath(index),
                                Status::OFF,
                            ))
                            .unwrap_or_else(|_| warn!("... lost stop animation for {:?}", index));
                        trace!("stop busy animation");
                        Ok(())
                    })
                    .unwrap_or_else(|_| {
                        error!("... that should not happen here at stop");
                    });
            } else {
                let text = format!(
                    "\n\
                             analyzed: {an:>width$}, faulty: {fa:>width$}\n\
                             searched: {se:>width$}, other: {ot:>width$}",
                    an = local_stats.analyzed,
                    fa = local_stats.faulty,
                    se = local_stats.searched,
                    ot = local_stats.other,
                    width = 3
                );
                println!("[{:?}] done {}", index, text);
            }
            // return this here
            CollectionOutputData {
                nr_found_songs: local_stats.analyzed,
            }
        }
        Err(_e) => {
            let text = format!("An error has occurred in search path [{}]!!", index);
            if has_ui {
                mutex_to_ui_msg
                    .lock()
                    .and_then(|locked_update_message| {
                        locked_update_message
                            .send(UiUpdateMsg::NetUpdate(ForwardNetMessage::Stats(
                                NetMessages::Debug(text),
                            )))
                            .unwrap();
                        Ok(())
                    })
                    .unwrap();
            } else {
                println!("{:?}", text);
            }
            // return this here
            CollectionOutputData { nr_found_songs: 0 }
        }
    }
}
<|endoftext|>


<commit_before>use std::collections::{
    HashMap,
    BinaryHeap,
};
use std::collections::hash_map::Entry::{
    Occupied,
    Vacant,
};

use std::default::Default;
use std::hash::Hash;
use std::ops::{
    Add,
};

use super::MinScored;
use super::visit::{
    Visitable,
    VisitMap,
};

/// Dijkstra's shortest path algorithm.
pub fn dijkstra<'a, G: Visitable, K, F, Edges>(graph: &'a G,
                                               start: G::NodeId,
                                               goal: Option<G::NodeId>,
                                               mut edges: F) -> HashMap<G::NodeId, K> where
    G::NodeId: Clone + Eq + Hash,
    K: Default + Add<Output=K> + Copy + PartialOrd,
    F: FnMut(&'a G, G::NodeId) -> Edges,
    Edges: Iterator<Item=(G::NodeId, K)>,
    <G as Visitable>::Map: VisitMap<G::NodeId>,
{
    let mut visited = graph.visit_map();
    let mut scores = HashMap::new();
    let mut predecessor = HashMap::new();
    let mut visit_next = BinaryHeap::new();
    let zero_score: K = Default::default();
    scores.insert(start.clone(), zero_score);
    visit_next.push(MinScored(zero_score, start));
    while let Some(MinScored(node_score, node)) = visit_next.pop() {
        if visited.is_visited(&node) {
            continue
        }
        if goal.as_ref() == Some(&node) {
            break
        }
        for (next, edge) in edges(graph, node.clone()) {
            if visited.is_visited(&next) {
                continue
            }
            let mut next_score = node_score + edge;
            match scores.entry(next.clone()) {
                Occupied(ent) => if next_score < *ent.get() {
                    *ent.into_mut() = next_score;
                    predecessor.insert(next.clone(), node.clone());
                } else {
                    next_score = *ent.get();
                },
                Vacant(ent) => {
                    ent.insert(next_score);
                    predecessor.insert(next.clone(), node.clone());
                }
            }
            visit_next.push(MinScored(next_score, next));
        }
        visited.visit(node);
    }
    scores
}
<commit_msg>dijkstra: Don't record predecessors (are not exposed)<commit_after>use std::collections::{
    HashMap,
    BinaryHeap,
};
use std::collections::hash_map::Entry::{
    Occupied,
    Vacant,
};

use std::default::Default;
use std::hash::Hash;
use std::ops::{
    Add,
};

use super::MinScored;
use super::visit::{
    Visitable,
    VisitMap,
};

/// Dijkstra's shortest path algorithm.
pub fn dijkstra<'a, G: Visitable, K, F, Edges>(graph: &'a G,
                                               start: G::NodeId,
                                               goal: Option<G::NodeId>,
                                               mut edges: F) -> HashMap<G::NodeId, K> where
    G::NodeId: Clone + Eq + Hash,
    K: Default + Add<Output=K> + Copy + PartialOrd,
    F: FnMut(&'a G, G::NodeId) -> Edges,
    Edges: Iterator<Item=(G::NodeId, K)>,
    <G as Visitable>::Map: VisitMap<G::NodeId>,
{
    let mut visited = graph.visit_map();
    let mut scores = HashMap::new();
    //let mut predecessor = HashMap::new();
    let mut visit_next = BinaryHeap::new();
    let zero_score: K = Default::default();
    scores.insert(start.clone(), zero_score);
    visit_next.push(MinScored(zero_score, start));
    while let Some(MinScored(node_score, node)) = visit_next.pop() {
        if visited.is_visited(&node) {
            continue
        }
        if goal.as_ref() == Some(&node) {
            break
        }
        for (next, edge) in edges(graph, node.clone()) {
            if visited.is_visited(&next) {
                continue
            }
            let mut next_score = node_score + edge;
            match scores.entry(next.clone()) {
                Occupied(ent) => if next_score < *ent.get() {
                    *ent.into_mut() = next_score;
                    //predecessor.insert(next.clone(), node.clone());
                } else {
                    next_score = *ent.get();
                },
                Vacant(ent) => {
                    ent.insert(next_score);
                    //predecessor.insert(next.clone(), node.clone());
                }
            }
            visit_next.push(MinScored(next_score, next));
        }
        visited.visit(node);
    }
    scores
}
<|endoftext|>


<commit_before>use objects::PhotoSize;

/// Represents a document
#[derive(Clone, Serialize, Deserialize, Debug)]
pub struct Document {
    pub file_id: String,
    pub thump: Option<PhotoSize>,
    pub file_name: Option<String>,
    pub mime_typ: Option<String>,
    pub file_size: Option<i64>,
}
<commit_msg>remove Serialize from my objects, forgot sticker and document<commit_after>use objects::PhotoSize;

/// Represents a document
#[derive(Clone, Deserialize, Debug)]
pub struct Document {
    pub file_id: String,
    pub thump: Option<PhotoSize>,
    pub file_name: Option<String>,
    pub mime_typ: Option<String>,
    pub file_size: Option<i64>,
}
<|endoftext|>


<commit_before>//! Asynchronous filesystem manipulation operations.
//!
//! This module contains basic methods and types for manipulating the contents
//! of the local filesystem from within the context of the Tokio runtime.
//!
//! Unlike *most* other Tokio APIs, the filesystem APIs **must** be used from
//! the context of the Tokio runtime as they require Tokio specific features to
//! function.

pub use tokio_fs::{
    file,
    File,
    OpenOptions,
};
<commit_msg>tokio: expose tokio_fs::metadata (#479)<commit_after>//! Asynchronous filesystem manipulation operations.
//!
//! This module contains basic methods and types for manipulating the contents
//! of the local filesystem from within the context of the Tokio runtime.
//!
//! Unlike *most* other Tokio APIs, the filesystem APIs **must** be used from
//! the context of the Tokio runtime as they require Tokio specific features to
//! function.

pub use tokio_fs::{file, metadata, File, OpenOptions};
<|endoftext|>