<commit_before>extern crate user32;
extern crate winapi;
extern crate kernel32;
extern crate libc;
extern crate gdi32;
use std::ffi::OsStr;
use std::iter::once;
use std::os::windows::ffi::OsStrExt;
use std::mem;

use self::winapi::winuser::*;
use self::winapi::windef::*;
use self::winapi::minwindef::*;
use self::winapi::wingdi::*;
use self::winapi::winnt::*;

struct Win32OffscreenBuffer {
    info: BITMAPINFO,
    bytes_per_pixel: i32,
    width: i32,
    height: i32,
    memory: LPVOID
}

struct WindowDimensions {
    width: i32,
    height: i32
}

static mut GLOBAL_RUNNING: bool = true;
static mut REFRESH_WINDOW_DIMENSIONS: bool = false;

static mut OFFSCREEN_BUFFER: Win32OffscreenBuffer = Win32OffscreenBuffer {
    info: BITMAPINFO {
        bmiHeader: BITMAPINFOHEADER {
            biSize: 0,
            biWidth: 0,
            biHeight: 0,
            biPlanes: 1,
            biBitCount: 32,
            biCompression: BI_RGB,
            biSizeImage: 0,
            biXPelsPerMeter: 0,
            biYPelsPerMeter: 0,
            biClrUsed: 0,
            biClrImportant: 0
        } ,
        bmiColors: []
    },
    bytes_per_pixel: 4,
    width: 0,
    height: 0,
    memory: 0 as LPVOID
};

unsafe fn win32_get_window_dimensions (window_handle: HWND) -> WindowDimensions {
    let mut window_rect: RECT = mem::uninitialized();
    user32::GetClientRect(window_handle, &mut window_rect);
    WindowDimensions {
        width: window_rect.right - window_rect.left,
        height: window_rect.bottom - window_rect.top
    }
}

unsafe fn win32_resize_dib_section(buffer: &mut Win32OffscreenBuffer, width: i32, height: i32) -> () {
    buffer.width = width;
    buffer.height = height;
    buffer.info.bmiHeader.biWidth = width;
    buffer.info.bmiHeader.biHeight = -height; //Top-down coord

    let bitmap_memory_size = (width*height)*buffer.bytes_per_pixel;

    if buffer.memory != 0 as LPVOID {
        kernel32::VirtualFree(buffer.memory, 0 as u64, MEM_RELEASE);
    }

    buffer.memory = kernel32::VirtualAlloc(0 as LPVOID, bitmap_memory_size as u64, MEM_COMMIT, PAGE_READWRITE);

}

unsafe fn draw_weird_gradient(buffer: &mut Win32OffscreenBuffer, x_offset: u32, y_offset: u32) {

    let width = buffer.width;
    let height = buffer.height;
    let bytes_per_pixel = buffer.bytes_per_pixel;

    let pitch = width*bytes_per_pixel;
    let mut row: *mut u8 = buffer.memory as *mut u8;
    for y in 0..height {
        let mut pixel: *mut u32 = row as *mut u32;
        for x in 0..width {
            *pixel = ((x as u32 + x_offset) << 0 ) + ((y as u32 + y_offset) << 8);
            pixel = pixel.offset(1);

        };
        row = row.offset(pitch as isize);
    };
}

unsafe fn win32_update_window(device_context: HDC, buffer: &mut Win32OffscreenBuffer, window_width: i32, window_height: i32) {
    gdi32::StretchDIBits(device_context,
        0, 0, buffer.width, buffer.height,
        0, 0, window_width, window_height,
        buffer.memory,
        &buffer.info,
        DIB_RGB_COLORS,
        SRCCOPY);
}

pub fn main() {
    unsafe {
        OFFSCREEN_BUFFER.info.bmiHeader.biSize = mem::size_of::<BITMAPINFOHEADER>() as u32;
        OFFSCREEN_BUFFER.info.bmiHeader.biWidth = 1080;
        OFFSCREEN_BUFFER.info.bmiHeader.biHeight = 640;
    }

    let window_class_name = to_wide_string("RustHeroWindowClass").as_ptr();
    let h_instance = unsafe {
        kernel32::GetModuleHandleW(0 as winapi::winnt::LPCWSTR)
    };

    let mut window = WNDCLASSW {
        style: CS_HREDRAW | CS_VREDRAW | CS_OWNDC,
        lpfnWndProc: Some(winproc),
        cbClsExtra: 0,
        cbWndExtra: 0,
        hInstance: h_instance,
        hIcon: unsafe { user32::LoadIconW(h_instance, winapi::winuser::IDI_APPLICATION) },
        hCursor: unsafe { user32::LoadCursorW(h_instance, winapi::winuser::IDI_APPLICATION) },
        hbrBackground: 0 as HBRUSH,
        lpszMenuName: to_wide_string("RustHeroMenu").as_ptr(),
        lpszClassName: window_class_name
    };

    let ret = unsafe { //TODO: Can we use Options to get rid of all these unsafes?
        user32::RegisterClassW(&mut window)
    };

    if ret == 0 {
        println!("Registering the window class failed.");
        return
    }

    unsafe {
         let window_handle = user32::CreateWindowExW(
            0,
            window_class_name,
            to_wide_string("Rust Hero").as_ptr(),
            WS_VISIBLE|WS_OVERLAPPEDWINDOW,
            CW_USEDEFAULT,
            CW_USEDEFAULT,
            CW_USEDEFAULT,
            CW_USEDEFAULT,
            0 as HWND,
            0 as HMENU,
            h_instance,
            0 as LPVOID
        );

        if window_handle.is_null() {
            println!("Failed to create window.");
            return;
        }

        let mut device_context = user32::GetDC(window_handle);
        // let mut window_rect: RECT = mem::uninitialized();
        // user32::GetClientRect(window_handle, &mut window_rect);
        let mut window_dimensions = win32_get_window_dimensions(window_handle);

        let mut x = 0;
        let mut y = 0;
        while GLOBAL_RUNNING {
            let mut msg: MSG = mem::uninitialized();
            while user32::PeekMessageW(&mut msg, window_handle,0,0,PM_REMOVE) != 0 {
                user32::TranslateMessage(&mut msg);
                user32::DispatchMessageW(&mut msg);
            }

            x += 1;
            y += 1;
            draw_weird_gradient(&mut OFFSCREEN_BUFFER, x,y);

            if REFRESH_WINDOW_DIMENSIONS {
                window_dimensions = win32_get_window_dimensions(window_handle);
                REFRESH_WINDOW_DIMENSIONS = false;
            }
            win32_update_window(device_context, &mut OFFSCREEN_BUFFER, window_dimensions.width, window_dimensions.height);

        }
    }
}

fn to_wide_string(str: &str) -> Vec<u16> {
    OsStr::new(str).encode_wide().chain(once(0)).collect()
}

 pub unsafe extern "system" fn winproc(hwnd :HWND, msg :UINT, w_param :WPARAM, l_param :LPARAM) -> LRESULT {
    match msg {
        WM_SIZE => {
            println!("WM_SIZE");
            let window_dimensions = win32_get_window_dimensions(hwnd);
            win32_resize_dib_section(&mut OFFSCREEN_BUFFER, window_dimensions.width, window_dimensions.height);
            REFRESH_WINDOW_DIMENSIONS = true;
        },
        WM_CLOSE => {  GLOBAL_RUNNING = false;  },
        WM_DESTROY => { GLOBAL_RUNNING = false; },
        WM_ACTIVATEAPP => { println!("WM_ACTIVATEAPP"); },
        WM_PAINT => {
            println!("WM_PAINT");
            let mut paint: PAINTSTRUCT = mem::uninitialized();
            let hdc = user32::BeginPaint(hwnd, &mut paint);
            let window_dimensions = win32_get_window_dimensions(hwnd);
            win32_update_window(hdc, &mut OFFSCREEN_BUFFER, window_dimensions.width, window_dimensions.height);
            user32::EndPaint(hwnd, &paint);
        },
        _ => { return user32::DefWindowProcW(hwnd, msg, w_param, l_param); }
    };
    0
}<commit_msg>Made DC immutable<commit_after>extern crate user32;
extern crate winapi;
extern crate kernel32;
extern crate libc;
extern crate gdi32;
use std::ffi::OsStr;
use std::iter::once;
use std::os::windows::ffi::OsStrExt;
use std::mem;

use self::winapi::winuser::*;
use self::winapi::windef::*;
use self::winapi::minwindef::*;
use self::winapi::wingdi::*;
use self::winapi::winnt::*;

struct Win32OffscreenBuffer {
    info: BITMAPINFO,
    bytes_per_pixel: i32,
    width: i32,
    height: i32,
    memory: LPVOID
}

struct WindowDimensions {
    width: i32,
    height: i32
}

static mut GLOBAL_RUNNING: bool = true;
static mut REFRESH_WINDOW_DIMENSIONS: bool = false;

static mut OFFSCREEN_BUFFER: Win32OffscreenBuffer = Win32OffscreenBuffer {
    info: BITMAPINFO {
        bmiHeader: BITMAPINFOHEADER {
            biSize: 0,
            biWidth: 0,
            biHeight: 0,
            biPlanes: 1,
            biBitCount: 32,
            biCompression: BI_RGB,
            biSizeImage: 0,
            biXPelsPerMeter: 0,
            biYPelsPerMeter: 0,
            biClrUsed: 0,
            biClrImportant: 0
        } ,
        bmiColors: []
    },
    bytes_per_pixel: 4,
    width: 0,
    height: 0,
    memory: 0 as LPVOID
};

unsafe fn win32_get_window_dimensions (window_handle: HWND) -> WindowDimensions {
    let mut window_rect: RECT = mem::uninitialized();
    user32::GetClientRect(window_handle, &mut window_rect);
    WindowDimensions {
        width: window_rect.right - window_rect.left,
        height: window_rect.bottom - window_rect.top
    }
}

unsafe fn win32_resize_dib_section(buffer: &mut Win32OffscreenBuffer, width: i32, height: i32) -> () {
    buffer.width = width;
    buffer.height = height;
    buffer.info.bmiHeader.biWidth = width;
    buffer.info.bmiHeader.biHeight = -height; //Top-down coord

    let bitmap_memory_size = (width*height)*buffer.bytes_per_pixel;

    if buffer.memory != 0 as LPVOID {
        kernel32::VirtualFree(buffer.memory, 0 as u64, MEM_RELEASE);
    }

    buffer.memory = kernel32::VirtualAlloc(0 as LPVOID, bitmap_memory_size as u64, MEM_COMMIT, PAGE_READWRITE);

}

unsafe fn draw_weird_gradient(buffer: &mut Win32OffscreenBuffer, x_offset: u32, y_offset: u32) {

    let width = buffer.width;
    let height = buffer.height;
    let bytes_per_pixel = buffer.bytes_per_pixel;

    let pitch = width*bytes_per_pixel;
    let mut row: *mut u8 = buffer.memory as *mut u8;
    for y in 0..height {
        let mut pixel: *mut u32 = row as *mut u32;
        for x in 0..width {
            *pixel = ((x as u32 + x_offset) << 0 ) + ((y as u32 + y_offset) << 8);
            pixel = pixel.offset(1);

        };
        row = row.offset(pitch as isize);
    };
}

unsafe fn win32_update_window(device_context: HDC, buffer: &mut Win32OffscreenBuffer, window_width: i32, window_height: i32) {
    gdi32::StretchDIBits(device_context,
        0, 0, buffer.width, buffer.height,
        0, 0, window_width, window_height,
        buffer.memory,
        &buffer.info,
        DIB_RGB_COLORS,
        SRCCOPY);
}

pub fn main() {
    unsafe {
        OFFSCREEN_BUFFER.info.bmiHeader.biSize = mem::size_of::<BITMAPINFOHEADER>() as u32;
        OFFSCREEN_BUFFER.info.bmiHeader.biWidth = 1080;
        OFFSCREEN_BUFFER.info.bmiHeader.biHeight = 640;
    }

    let window_class_name = to_wide_string("RustHeroWindowClass").as_ptr();
    let h_instance = unsafe {
        kernel32::GetModuleHandleW(0 as winapi::winnt::LPCWSTR)
    };

    let mut window = WNDCLASSW {
        style: CS_HREDRAW | CS_VREDRAW | CS_OWNDC,
        lpfnWndProc: Some(winproc),
        cbClsExtra: 0,
        cbWndExtra: 0,
        hInstance: h_instance,
        hIcon: unsafe { user32::LoadIconW(h_instance, winapi::winuser::IDI_APPLICATION) },
        hCursor: unsafe { user32::LoadCursorW(h_instance, winapi::winuser::IDI_APPLICATION) },
        hbrBackground: 0 as HBRUSH,
        lpszMenuName: to_wide_string("RustHeroMenu").as_ptr(),
        lpszClassName: window_class_name
    };

    let ret = unsafe { //TODO: Can we use Options to get rid of all these unsafes?
        user32::RegisterClassW(&mut window)
    };

    if ret == 0 {
        println!("Registering the window class failed.");
        return
    }

    unsafe {
         let window_handle = user32::CreateWindowExW(
            0,
            window_class_name,
            to_wide_string("Rust Hero").as_ptr(),
            WS_VISIBLE|WS_OVERLAPPEDWINDOW,
            CW_USEDEFAULT,
            CW_USEDEFAULT,
            CW_USEDEFAULT,
            CW_USEDEFAULT,
            0 as HWND,
            0 as HMENU,
            h_instance,
            0 as LPVOID
        );

        if window_handle.is_null() {
            println!("Failed to create window.");
            return;
        }

        let device_context = user32::GetDC(window_handle);
        let mut window_dimensions = win32_get_window_dimensions(window_handle);

        let mut x = 0;
        let mut y = 0;
        while GLOBAL_RUNNING {
            let mut msg: MSG = mem::uninitialized();
            while user32::PeekMessageW(&mut msg, window_handle,0,0,PM_REMOVE) != 0 {
                user32::TranslateMessage(&mut msg);
                user32::DispatchMessageW(&mut msg);
            }

            x += 1;
            y += 1;
            draw_weird_gradient(&mut OFFSCREEN_BUFFER, x,y);

            if REFRESH_WINDOW_DIMENSIONS {
                window_dimensions = win32_get_window_dimensions(window_handle);
                REFRESH_WINDOW_DIMENSIONS = false;
            }
            win32_update_window(device_context, &mut OFFSCREEN_BUFFER, window_dimensions.width, window_dimensions.height);
        }
    }
}

fn to_wide_string(str: &str) -> Vec<u16> {
    OsStr::new(str).encode_wide().chain(once(0)).collect()
}

 pub unsafe extern "system" fn winproc(hwnd :HWND, msg :UINT, w_param :WPARAM, l_param :LPARAM) -> LRESULT {
    match msg {
        WM_SIZE => {
            println!("WM_SIZE");
            let window_dimensions = win32_get_window_dimensions(hwnd);
            win32_resize_dib_section(&mut OFFSCREEN_BUFFER, window_dimensions.width, window_dimensions.height);
            REFRESH_WINDOW_DIMENSIONS = true;
        },
        WM_CLOSE => {  GLOBAL_RUNNING = false;  },
        WM_DESTROY => { GLOBAL_RUNNING = false; },
        WM_ACTIVATEAPP => { println!("WM_ACTIVATEAPP"); },
        WM_PAINT => {
            println!("WM_PAINT");
            let mut paint: PAINTSTRUCT = mem::uninitialized();
            let hdc = user32::BeginPaint(hwnd, &mut paint);
            let window_dimensions = win32_get_window_dimensions(hwnd);
            win32_update_window(hdc, &mut OFFSCREEN_BUFFER, window_dimensions.width, window_dimensions.height);
            user32::EndPaint(hwnd, &paint);
        },
        _ => { return user32::DefWindowProcW(hwnd, msg, w_param, l_param); }
    };
    0
}<|endoftext|>


<commit_before>use std::error::FromError;
use std::old_io::IoError;
use std::collections::HashMap;
use std::sync::mpsc::{sync_channel, SyncSender, Receiver};

use rustc_serialize::json::{self, DecoderError};
use time::{get_time, Timespec};
use url::Url;
use url::form_urlencoded::serialize_owned;
use hyper::client::request::Request;
use hyper::HttpError;
use hyper::method::Method::Get;

use irc::UserId;
use irc::parse::IrcMsg;
use irc::MessageEndpoint::{self, KnownUser};

use command_mapper::{
    RustBotPlugin,
    CommandMapperDispatch,
    IrcBotConfigurator,
    Format,
    Token,
};

const CMD_DEER_NAMED: Token = Token(0);
const CMD_DEER: Token = Token(1);
const CMD_REED: Token = Token(2);
const CMD_DEERMAN: Token = Token(3);
const CMD_NAMREED: Token = Token(4);
const CMD_DEER_STATS: Token = Token(5);

static DEER: &'static str = concat!(
    "\u{0003}01,01@@@@@@@@\u{0003}00,00@\u{0003}01,01@@\u{0003}00,00@\u{0003}01,01@\n",
    "\u{0003}01,01@@@@@@@@\u{0003}00,00@\u{0003}01,01@@\u{0003}00,00@\u{0003}01,01@\n",
    "\u{0003}01,01@@@@@@@@@\u{0003}00,00@@\u{0003}01,01@@\n",
    "\u{0003}01,01@@@@@@@@\u{0003}00,00@@@\u{0003}01,01@@\n",
    "\u{0003}01,01@@@@@@@@@\u{0003}00,00@@\u{0003}01,01@@\n",
    "\u{0003}01,01@@\u{0003}00,00@@@@@@@@@\u{0003}01,01@@\n",
    "\u{0003}01,01@\u{0003}00,00@@@@@@@@@@\u{0003}01,01@@\n",
    "\u{0003}01,01@\u{0003}00,00@@@@@@@@@@\u{0003}01,01@@\n",
    "\u{0003}01,01@\u{0003}00,00@\u{0003}01,01@\u{0003}00,00@",
    "\u{0003}01,01@@@@\u{0003}00,00@\u{0003}01,01@\u{0003}00,00@\u{0003}01,01@@\n",
    "\u{0003}01,01@\u{0003}00,00@\u{0003}01,01@\u{0003}00,00@",
    "\u{0003}01,01@@@@\u{0003}00,00@\u{0003}01,01@\u{0003}00,00@\u{0003}01,01@@\n",
    "\u{0003}01,01@\u{0003}00,00@\u{0003}01,01@\u{0003}00,00@",
    "\u{0003}01,01@@@@\u{0003}00,00@\u{0003}01,01@\u{0003}00,00@\u{0003}01,01@@");

static DEERMAN: &'static str = concat!(
    "\u{0003}00,00@@\u{0003}01,01@\u{0003}00,00@@\u{0003}01,01@\u{0003}00,00@@\n",
    "\u{0003}00,00@@\u{0003}01,01@\u{0003}00,00@@\u{0003}01,01@\u{0003}00,00@@\n",
    "\u{0003}00,00@@@\u{0003}01,01@@\u{0003}00,00@@@\n",
    "\u{0003}00,00@@\u{0003}01,01@@@\u{0003}00,00@@@\n",
    "\u{0003}00,00@@@\u{0003}01,01@@\u{0003}00,00@@@\n",
    "\u{0003}00,00@@\u{0003}01,01@@@@\u{0003}00,00@@\n",
    "\u{0003}01,01@@@@@@@@\n",
    "\u{0003}01,01@\u{0003}00,00@\u{0003}01,01@@@@\u{0003}00,00@\u{0003}01,01@\n",
    "\u{0003}01,01@\u{0003}00,00@\u{0003}01,01@@@@\u{0003}00,00@\u{0003}01,01@\n",
    "\u{0003}00,00@@\u{0003}01,01@@@@\u{0003}00,00@@\n",
    "\u{0003}00,00@@\u{0003}01,01@\u{0003}00,00@@\u{0003}01,01@\u{0003}00,00@@\n",
    "\u{0003}00,00@@\u{0003}01,01@\u{0003}00,00@@\u{0003}01,01@\u{0003}00,00@@\n",
    "\u{0003}00,00@@\u{0003}01,01@\u{0003}00,00@@\u{0003}01,01@\u{0003}00,00@@\n");

static NOT_IMPLEMENTED: &'static str = "Not yet implemented";

static BASE_URL: &'static str = "http://deer.satf.se/deerlist.php";

#[derive(RustcDecodable, RustcEncodable, Clone)]
struct DeerApiResponse {
    irccode: String
}

#[derive(Debug)]
enum DeerApiFailure {
    ResponseDecodeError,
    RequestError(HttpError),
    ResponseReadError(IoError),
    ResponseDeserializeError(DecoderError)
}

impl FromError<HttpError> for DeerApiFailure {
    fn from_error(err: HttpError) -> DeerApiFailure {
        DeerApiFailure::RequestError(err)
    }
}

impl FromError<IoError> for DeerApiFailure {
    fn from_error(err: IoError) -> DeerApiFailure {
        DeerApiFailure::ResponseReadError(err)
    }
}

impl FromError<DecoderError> for DeerApiFailure {
    fn from_error(err: DecoderError) -> DeerApiFailure {
        DeerApiFailure::ResponseDeserializeError(err)
    }
}

fn get_deer_nocache(deer_name: &str) -> Result<DeerApiResponse, DeerApiFailure> {
    let mut url = match Url::parse(BASE_URL) {
        Ok(url) => url,
        Err(_err) => unreachable!()
    };
    url.query = Some(serialize_owned(&[
        (String::from_str("deer"), String::from_str(deer_name)),
    ]));

    let mut resp = try!(try!(try!(Request::new(Get, url)).start()).send());
    let body = match String::from_utf8(try!(resp.read_to_end())) {
        Ok(body) => body,
        Err(_err) => return Err(DeerApiFailure::ResponseDecodeError)
    };

    Ok(try!(json::decode::<DeerApiResponse>(&body)))
}

fn get_deer(state: &mut DeerInternalState, deer_name: &str) -> Result<DeerApiResponse, DeerApiFailure> {
    let deer_name_alloc = String::from_str(deer_name);

    match state.cache.get(&deer_name_alloc) {
        Some(result) => return Ok(result.clone()),
        None => ()
    }

    match get_deer_nocache(deer_name) {
        Ok(response) => {
            state.cache.insert(
                String::from_str(deer_name),
                response.clone());
            Ok(response)
        },
        Err(err) => Err(err)
    }
}

pub struct DeerPlugin {
    sender: Option<SyncSender<(CommandMapperDispatch, IrcMsg)>>
}

impl DeerPlugin {
    pub fn new() -> DeerPlugin {
        DeerPlugin { sender: None }
    }

    pub fn get_plugin_name() -> &'static str {
        "deer"
    }
}

struct DeerInternalState {
    lines_sent: u64,
    cache: HashMap<String, DeerApiResponse>,
    throttle_map: HashMap<(UserId, MessageEndpoint), Timespec>,
}

impl DeerInternalState {
    fn new() -> DeerInternalState {
        DeerInternalState {
            lines_sent: 0,
            cache: HashMap::new(),
            throttle_map: HashMap::new(),
        }
    }

    fn throttle_ok(&mut self, uid: UserId, endpoint: MessageEndpoint) -> bool {
        match self.throttle_map.get(&(uid, endpoint)) {
            Some(entry) => 60 < (get_time() - *entry).num_seconds(),
            None => true
        }
    }

    fn throttle_bump(&mut self, uid: UserId, endpoint: MessageEndpoint) {
        self.throttle_map.insert((uid, endpoint), get_time());
    }

    fn handle_command<'a>(&mut self, m: &CommandMapperDispatch, cmd: &'a DeerCommandType) {
        let source = match m.source {
            KnownUser(source) => source,
            _ => return
        };

        if let DeerCommandType::Deer(_) = *cmd {
            if !self.throttle_ok(source, m.target.clone()) {
                m.reply(String::from_str("2deer4plus"));
                return;
            }
        }
        match *cmd {
            DeerCommandType::Deer(ref deer_name) => {
                match get_deer(self, &deer_name) {
                    Ok(deer_data) => {
                        for deer_line in deer_data.irccode.split('\n') {
                            m.reply(String::from_str(deer_line));
                            self.lines_sent += 1;
                        }
                        self.throttle_bump(source, m.target.clone());
                    },
                    Err(err) => {
                        m.reply(format!("error: {:?}", err));
                    }
                }
            },
            DeerCommandType::StaticDeer(data) => {
                for deer_line in data.split('\n') {
                    m.reply(String::from_str(deer_line));
                    self.lines_sent += 1;
                }
                self.throttle_bump(source, m.target.clone());
            },
            DeerCommandType::DeerStats => {
                m.reply(format!("lines sent: {}", self.lines_sent));
            }
        };
    }

    fn start(&mut self, rx: Receiver<(CommandMapperDispatch, IrcMsg)>) {
        for (m, _) in rx.iter() {
            match parse_command(&m) {
                Some(command) => {
                    println!("handle_command(..., {:?})", command);
                    self.handle_command(&m, &command);
                }
                None => ()
            }
        }
    }
}

#[derive(Debug)]
enum DeerCommandType {
    Deer(String),
    StaticDeer(&'static str),
    DeerStats
}

fn parse_command<'a>(m: &CommandMapperDispatch) -> Option<DeerCommandType> {
    let command_phrase = m.command();
    println!("deer::parse_command ** token = {:?}", command_phrase.token);
    match command_phrase.token {
        CMD_DEER_NAMED => Some(match command_phrase.get("deername") {
            Some(deername) => DeerCommandType::Deer(deername),
            None => DeerCommandType::StaticDeer(DEER)
        }),
        CMD_DEER => Some(DeerCommandType::StaticDeer(DEER)),
        CMD_REED => Some(DeerCommandType::StaticDeer(NOT_IMPLEMENTED)),
        CMD_DEERMAN => Some(DeerCommandType::StaticDeer(DEERMAN)),
        CMD_NAMREED => Some(DeerCommandType::StaticDeer(NOT_IMPLEMENTED)),
        CMD_DEER_STATS => Some(DeerCommandType::DeerStats),
        _ => None
    }
}

impl RustBotPlugin for DeerPlugin {
    fn configure(&mut self, conf: &mut IrcBotConfigurator) {
        conf.map_format(CMD_DEER_NAMED, Format::from_str("deer {*deername}").unwrap());
        conf.map_format(CMD_DEER, Format::from_str("deer").unwrap());
        conf.map_format(CMD_REED, Format::from_str("reed").unwrap());
        conf.map_format(CMD_DEERMAN, Format::from_str("deerman").unwrap());
        conf.map_format(CMD_NAMREED, Format::from_str("namreed").unwrap());
        conf.map_format(CMD_DEER_STATS, Format::from_str("deer-stats").unwrap());
    }

    fn start(&mut self) {
        let (tx, rx) = sync_channel(10);
        self.sender = Some(tx);

        let _ = ::std::thread::Builder::new().name("plugin-deer".to_string()).spawn(move || {
            let mut deer_internal_state = DeerInternalState::new();
            deer_internal_state.start(rx);
        });
    }

    fn dispatch_cmd(&mut self, m: &CommandMapperDispatch, message: &IrcMsg) {
        match self.sender {
            Some(ref sender) => {
                if let Err(err) = sender.send((m.clone(), message.clone())) {
                    m.reply(format!("Service ``deer'' unavailable: {:?}", err));
                }
            }
            None => ()
        };
    }
}

<commit_msg>deer: Add more Deers<commit_after>use std::error::FromError;
use std::old_io::IoError;
use std::collections::HashMap;
use std::sync::mpsc::{sync_channel, SyncSender, Receiver};
use std::io::Read;

use rustc_serialize::json::{self, DecoderError};
use time::{get_time, Timespec};
use url::Url;
use url::form_urlencoded::serialize_owned;
use hyper::client::request::Request;
use hyper::HttpError;
use hyper::method::Method::Get;

use irc::UserId;
use irc::parse::IrcMsg;
use irc::MessageEndpoint::{self, KnownUser};

use command_mapper::{
    RustBotPlugin,
    CommandMapperDispatch,
    IrcBotConfigurator,
    Format,
    Token,
};

const CMD_DEER_NAMED: Token = Token(0);
const CMD_DEER: Token = Token(1);
const CMD_REED: Token = Token(2);
const CMD_DEERMAN: Token = Token(3);
const CMD_NAMREED: Token = Token(4);
const CMD_DEER_STATS: Token = Token(5);

static DEER: &'static str = concat!(
    "1111111101101\n",
    "1111111101101\n",
    "1111111110011\n",
    "1111111100011\n",
    "1111111110011\n",
    "1100000000011\n",
    "1000000000011\n",
    "1000000000011\n",
    "1010111101011\n",
    "1010111101011\n",
    "1010111101011");

static REED: &'static str = concat!(
    "1011011111111\n",
    "1011011111111\n",
    "1100111111111\n",
    "1100011111111\n",
    "1100111111111\n",
    "1100000000011\n",
    "1100000000001\n",
    "1100000000001\n",
    "1101011110101\n",
    "1101011110101\n",
    "1101011110101");

static DEERMAN: &'static str = concat!(
    "00100100\n",
    "00100100\n",
    "00011000\n",
    "00111000\n",
    "00011000\n",
    "00111100\n",
    "11111111\n",
    "10111101\n",
    "10111101\n",
    "00111100\n",
    "00100100\n",
    "00100100\n",
    "00100100");

static NAMREED: &'static str = concat!(
    "00100100\n",
    "00100100\n",
    "00011000\n",
    "00011100\n",
    "00011000\n",
    "00111100\n",
    "11111111\n",
    "10111101\n",
    "10111101\n",
    "00111100\n",
    "00100100\n",
    "00100100\n",
    "00100100");

fn render_deer(format: &str) -> Vec<String> {
    let mut out = Vec::new();
    for format_line in format.split('\n') {
        let mut output_line = String::new();
        for ch in format_line.chars() {
            match ch {
                '0' => output_line.push_str("\u{0003}00,00@"),
                '1' => output_line.push_str("\u{0003}01,01@"),
                any => output_line.push(any),
            }
        }
        out.push(output_line);
    }
    out
}

static BASE_URL: &'static str = "http://deer.satf.se/deerlist.php";

#[derive(RustcDecodable, RustcEncodable, Clone)]
struct DeerApiResponse {
    irccode: String
}

#[derive(Debug)]
enum DeerApiFailure {
    ResponseDecodeError,
    RequestError(HttpError),
    ResponseReadError(IoError),
    ResponseDeserializeError(DecoderError)
}

impl FromError<HttpError> for DeerApiFailure {
    fn from_error(err: HttpError) -> DeerApiFailure {
        DeerApiFailure::RequestError(err)
    }
}

impl FromError<IoError> for DeerApiFailure {
    fn from_error(err: IoError) -> DeerApiFailure {
        DeerApiFailure::ResponseReadError(err)
    }
}

impl FromError<DecoderError> for DeerApiFailure {
    fn from_error(err: DecoderError) -> DeerApiFailure {
        DeerApiFailure::ResponseDeserializeError(err)
    }
}

fn get_deer_nocache(deer_name: &str) -> Result<DeerApiResponse, DeerApiFailure> {
    let mut url = match Url::parse(BASE_URL) {
        Ok(url) => url,
        Err(_err) => unreachable!()
    };
    url.query = Some(serialize_owned(&[
        (String::from_str("deer"), String::from_str(deer_name)),
    ]));

    let mut resp = try!(try!(try!(Request::new(Get, url)).start()).send());
    let body = match String::from_utf8(try!(resp.read_to_end())) {
        Ok(body) => body,
        Err(_err) => return Err(DeerApiFailure::ResponseDecodeError)
    };

    Ok(try!(json::decode::<DeerApiResponse>(&body)))
}

fn get_deer(state: &mut DeerInternalState, deer_name: &str) -> Result<DeerApiResponse, DeerApiFailure> {
    let deer_name_alloc = String::from_str(deer_name);

    match state.cache.get(&deer_name_alloc) {
        Some(result) => return Ok(result.clone()),
        None => ()
    }

    match get_deer_nocache(deer_name) {
        Ok(response) => {
            state.cache.insert(
                String::from_str(deer_name),
                response.clone());
            Ok(response)
        },
        Err(err) => Err(err)
    }
}

pub struct DeerPlugin {
    sender: Option<SyncSender<(CommandMapperDispatch, IrcMsg)>>
}

impl DeerPlugin {
    pub fn new() -> DeerPlugin {
        DeerPlugin { sender: None }
    }

    pub fn get_plugin_name() -> &'static str {
        "deer"
    }
}

struct DeerInternalState {
    lines_sent: u64,
    cache: HashMap<String, DeerApiResponse>,
    throttle_map: HashMap<(UserId, MessageEndpoint), Timespec>,
}

impl DeerInternalState {
    fn new() -> DeerInternalState {
        DeerInternalState {
            lines_sent: 0,
            cache: HashMap::new(),
            throttle_map: HashMap::new(),
        }
    }

    fn throttle_ok(&mut self, uid: UserId, endpoint: MessageEndpoint) -> bool {
        match self.throttle_map.get(&(uid, endpoint)) {
            Some(entry) => 60 < (get_time() - *entry).num_seconds(),
            None => true
        }
    }

    fn throttle_bump(&mut self, uid: UserId, endpoint: MessageEndpoint) {
        self.throttle_map.insert((uid, endpoint), get_time());
    }

    fn handle_command<'a>(&mut self, m: &CommandMapperDispatch, cmd: &'a DeerCommandType) {
        let source = match m.source {
            KnownUser(source) => source,
            _ => return
        };

        if let DeerCommandType::Deer(_) = *cmd {
            if !self.throttle_ok(source, m.target.clone()) {
                m.reply(String::from_str("2deer4plus"));
                return;
            }
        }
        match *cmd {
            DeerCommandType::Deer(ref deer_name) => {
                match get_deer(self, &deer_name) {
                    Ok(deer_data) => {
                        for deer_line in deer_data.irccode.split('\n') {
                            m.reply(String::from_str(deer_line));
                            self.lines_sent += 1;
                        }
                        self.throttle_bump(source, m.target.clone());
                    },
                    Err(err) => {
                        m.reply(format!("error: {:?}", err));
                    }
                }
            },
            DeerCommandType::StaticDeer(data) => {
                for deer_line in render_deer(data).into_iter() {
                    m.reply(deer_line);
                    self.lines_sent += 1;
                }
                self.throttle_bump(source, m.target.clone());
            },
            DeerCommandType::DeerStats => {
                m.reply(format!("lines sent: {}", self.lines_sent));
            }
        };
    }

    fn start(&mut self, rx: Receiver<(CommandMapperDispatch, IrcMsg)>) {
        for (m, _) in rx.iter() {
            match parse_command(&m) {
                Some(command) => {
                    println!("handle_command(..., {:?})", command);
                    self.handle_command(&m, &command);
                }
                None => ()
            }
        }
    }
}

#[derive(Debug)]
enum DeerCommandType {
    Deer(String),
    StaticDeer(&'static str),
    DeerStats
}

fn parse_command<'a>(m: &CommandMapperDispatch) -> Option<DeerCommandType> {
    let command_phrase = m.command();
    println!("deer::parse_command ** token = {:?}", command_phrase.token);
    match command_phrase.token {
        CMD_DEER_NAMED => Some(match command_phrase.get("deername") {
            Some(deername) => DeerCommandType::Deer(deername),
            None => DeerCommandType::StaticDeer(DEER)
        }),
        CMD_DEER => Some(DeerCommandType::StaticDeer(DEER)),
        CMD_REED => Some(DeerCommandType::StaticDeer(REED)),
        CMD_DEERMAN => Some(DeerCommandType::StaticDeer(DEERMAN)),
        CMD_NAMREED => Some(DeerCommandType::StaticDeer(NAMREED)),
        CMD_DEER_STATS => Some(DeerCommandType::DeerStats),
        _ => None
    }
}

impl RustBotPlugin for DeerPlugin {
    fn configure(&mut self, conf: &mut IrcBotConfigurator) {
        conf.map_format(CMD_DEER_NAMED, Format::from_str("deer {*deername}").unwrap());
        conf.map_format(CMD_DEER, Format::from_str("deer").unwrap());
        conf.map_format(CMD_REED, Format::from_str("reed").unwrap());
        conf.map_format(CMD_DEERMAN, Format::from_str("deerman").unwrap());
        conf.map_format(CMD_NAMREED, Format::from_str("namreed").unwrap());
        conf.map_format(CMD_DEER_STATS, Format::from_str("deer-stats").unwrap());
    }

    fn start(&mut self) {
        let (tx, rx) = sync_channel(10);
        self.sender = Some(tx);

        let _ = ::std::thread::Builder::new().name("plugin-deer".to_string()).spawn(move || {
            let mut deer_internal_state = DeerInternalState::new();
            deer_internal_state.start(rx);
        });
    }

    fn dispatch_cmd(&mut self, m: &CommandMapperDispatch, message: &IrcMsg) {
        match self.sender {
            Some(ref sender) => {
                if let Err(err) = sender.send((m.clone(), message.clone())) {
                    m.reply(format!("Service ``deer'' unavailable: {:?}", err));
                }
            }
            None => ()
        };
    }
}

<|endoftext|>


<commit_before>extern crate test;
extern crate rand;
extern crate "nalgebra" as na;

use rand::{IsaacRng, Rng};
use test::Bencher;
use na::{UnitQuat, Rot2, Rot3, Vec1, Vec3};

#[path="common/macros.rs"]
mod macros;

bench_construction!(_bench_quat_from_axisangle, UnitQuat::new, axisangle: Vec3<f32>);
bench_construction!(_bench_rot2_from_axisangle, Rot2::new, axisangle: Vec1<f32>);
bench_construction!(_bench_rot3_from_axisangle, Rot3::new, axisangle: Vec3<f32>);

bench_construction!(_bench_quat_from_euler_angles, UnitQuat::new_with_euler_angles, roll: f32, pitch: f32, yaw: f32);
bench_construction!(_bench_rot3_from_euler_angles, Rot3::new_with_euler_angles, roll: f32, pitch: f32, yaw: f32);
<commit_msg>Make the benchmarks compile again<commit_after>#![feature(test)]

extern crate test;
extern crate rand;
extern crate nalgebra as na;

use rand::{IsaacRng, Rng};
use test::Bencher;
use na::{UnitQuat, Rot2, Rot3, Vec1, Vec3};

#[path="common/macros.rs"]
mod macros;

bench_construction!(_bench_quat_from_axisangle, UnitQuat::new, axisangle: Vec3<f32>);
bench_construction!(_bench_rot2_from_axisangle, Rot2::new, axisangle: Vec1<f32>);
bench_construction!(_bench_rot3_from_axisangle, Rot3::new, axisangle: Vec3<f32>);

bench_construction!(_bench_quat_from_euler_angles, UnitQuat::new_with_euler_angles, roll: f32, pitch: f32, yaw: f32);
bench_construction!(_bench_rot3_from_euler_angles, Rot3::new_with_euler_angles, roll: f32, pitch: f32, yaw: f32);
<|endoftext|>


<commit_before>use std::cmp::Ordering;
use svg_element::SvgElement;
use svg::Node;
use svg::node::element::{
    Circle as SvgCircle,
    Line as SvgLine,
    Path as SvgPath,
    Text as SvgText,
};
use grid::svg_escape;
use point::collinear;
use settings::Settings;

use point::Point;
use loc::Loc;
use element::{
    Stroke::{Solid,Dashed},
    ArcFlag::{Minor,Major},
    Feature::{Arrow,ArrowStart,Circle,Square,OpenCircle,BigOpenCircle,Nothing},
};
use unicode_width::UnicodeWidthStr;
use point;

#[derive(Debug, Clone, PartialEq, PartialOrd )]
pub enum Element {
    Circle(Point, f32, String),
    //   start,  end,  stroke , start_feature, end feature
    Line(Point, Point, Stroke, Feature, Feature),
    //   start, end, radius,   sweep,   stroke, start_feat, end_feat
    Arc(Point, Point, f32, ArcFlag, bool, Stroke, Feature, Feature),
    Text(Loc, String),
}

impl Element{

    fn longer_line<'a>(&'a self, other: &'a Element) -> &'a Element {
        match self{
            Element::Line(s1, e1, _, _, _) => match other{
                Element::Line(s2, e2, _, _,_) => {
                    let d1 = point::distance(s1, e1);
                    let d2 = point::distance(s2, e2);
                    if d1 > d2 {
                        self
                    }
                    else{
                        other
                    }
                }
                _ => panic!("only for lines"),
            }
            _ => panic!("only for lines"),
        }
    }
}

#[derive(Debug, Clone, PartialEq, PartialOrd, Ord, Eq)]
pub enum Stroke {
    Solid,
    Dashed,
}

//TODO: rename to marker
#[derive(Debug, Clone, PartialEq, PartialOrd, Ord, Eq)]
pub enum Feature {
    ArrowStart, // start arrow
    Arrow,  //end
    Circle, //start
    Square, //start
    OpenCircle, //start
    BigOpenCircle, //start
    Nothing,
}

#[derive(Debug, Clone, PartialEq, PartialOrd)]
pub enum ArcFlag {
    Major,
    Minor,
}

impl Ord for Element{
    fn cmp(&self, other: &Self) -> Ordering{
        if let Some(order) = self.partial_cmp(&other){
            return order
        }
        Ordering::Less
    }
}
impl Eq for Element{
}

pub fn line(a: &Point, b: &Point) -> Element {
    Element::Line(a.clone(), b.clone(), Solid, Nothing, Nothing)
}

pub fn dashed_line(a: &Point, b: &Point) -> Element {
    Element::Line(a.clone(), b.clone(), Dashed, Nothing, Nothing)
}

pub fn circle_start_line(a: &Point, b: &Point) -> Element {
    Element::Line(a.clone(), b.clone(), Solid, Circle, Nothing)
}
pub fn square_start_line(a: &Point, b: &Point) -> Element {
    Element::Line(a.clone(), b.clone(), Solid, Square, Nothing)
}

pub fn circle_open_line(a: &Point, b: &Point) -> Element {
    Element::Line(a.clone(), b.clone(), Solid, OpenCircle, Nothing)
}

pub fn big_circle_open_line(a: &Point, b: &Point) -> Element {
    Element::Line(a.clone(), b.clone(), Solid, BigOpenCircle, Nothing)
}

pub fn arc(a: &Point, b: &Point, r: f32) -> Element {
    Element::Arc(a.clone(), b.clone(), r, Minor, false, Solid, Nothing, Nothing)
}

pub fn open_circle(c: &Point, r: f32) -> Element {
    Element::Circle(c.clone(), r.clone(), "open".to_string())
}

pub fn arrow_line(s: &Point, e: &Point) -> Element {
    Element::Line(s.clone(), e.clone(), Solid, Nothing, Arrow)
}

pub fn start_arrow_line(s: &Point, e: &Point) -> Element {
    Element::Line(s.clone(), e.clone(), Solid, ArrowStart, Nothing)
}

pub fn text(loc: &Loc, txt: &str) -> Element {
    Element::Text(loc.clone(), svg_escape(txt))
}

impl Element {

    pub fn shares_endpoints(&self, other: &Element) -> bool {
        match *self{
            Element::Line(ref s, ref e, _, _,_) => {
                match *other{
                    Element::Line(ref s2, ref e2, _, _,_) => {
                        s == s2 || e == e2 || s == e2 || s2 == e
                    }
                    Element::Arc(ref s2, ref e2, _,_,_,_,_,_) => {
                        s == s2 || e == e2 || s == e2 || s2 == e
                    }
                    _ => false
                }
            }
            Element::Arc(ref s, ref e, _,_,_,_,_,_) => {
                match *other{
                    Element::Line(ref s2, ref e2, _, _,_) => {
                        s == s2 || e == e2 || s == e2 || s2 == e
                    }
                    Element::Arc(ref s2, ref e2, _,_,_,_,_,_) => {
                        s == s2 || e == e2 || s == e2 || s2 == e
                    }
                    _ => false
                }
            }
            _ => false
        }
    }
    // if this element can reduce the other, return the new reduced element
    // for line it has to be collinear and in can connect start->end->start
    // for text, the other text should apear on the right side of this text
    pub fn reduce(&self, other: &Element) -> Option<Element> {
        // if same then return one
        if self == other{
            return Some(other.clone())
        }
        match *self {
            Element::Line(ref s, ref e, ref stroke, ref start_feature, ref end_feature) => {
                match *other {
                    Element::Line(ref s2, ref e2, ref stroke2, ref start_feature2, ref end_feature2) => {
                        // note: dual 3 point check for trully collinear lines
                        if collinear(s, e, s2)
                            && collinear(s, e, e2)
                            && stroke == stroke2{
                            // same length line
                            if s == s2 && e == e2 && start_feature == start_feature2 && end_feature == end_feature2 {
                                return Some(other.clone())
                            }

                            // extend 1 with 2
                            //    line1      line2
                            //   s-----e   s2-----e2
                            //   s----------------e2
                            else if e == s2 {
                                // -----
                                // o----
                                // ------
                                // ------>
                                if *end_feature == Nothing && *start_feature2 == Nothing{
                                    return Some(Element::Line(
                                                s.clone(),
                                                e2.clone(),
                                                stroke.clone(),
                                                start_feature.clone(),
                                                end_feature2.clone()
                                                ));
                                }
                            }
                            //  extend 1 with flip 2
                            //    line1     line2
                            //  s------e   e2-------s2
                            //  s-------------------s2
                            else if e == e2{
                                //  -------  --------
                                //  o------  ---------
                                if *end_feature == Nothing && *end_feature2 == Nothing{
                                    return Some(Element::Line(
                                            s.clone(),
                                            s2.clone(),
                                            stroke.clone(),
                                            start_feature.clone(),
                                            start_feature2.clone(),
                                            ));
                                }
                            }
                            // flip1 extend 2
                            //    line1    line2
                            //  e------s   s2------e2
                            //  s------------------e2
                            else if s == s2{
                                // -------   -------
                                // -------  ------->
                                // except for line 1 has arrow at the end
                                if *start_feature == Nothing && *start_feature2 == Nothing && *end_feature != Arrow {
                                    return Some(Element::Line(
                                            e.clone(),
                                            e2.clone(),
                                            stroke.clone(),
                                            end_feature.clone(),
                                            end_feature2.clone(),
                                            ));
                               }
                                // TODO: need fixing
                                // same starting point, take longer line
                                // longer line
                                // s-------e
                                // s2-------------->e2
                                else {
                                    //println!("longer line");
                                    return Some(self.longer_line(other).clone())
                                }
                            }
                            // extend 2 with 1
                            //      line1    line2
                            //  e------s    e2------s2
                            //  e---------------------s2
                            //
                            else if s == e2{
                                //   -----   -----
                                //   -----   ----o
                                //   <----   -----
                                //   <----   ----o
                                if *start_feature == Nothing && *end_feature2 == Nothing{
                                    return Some(Element::Line(
                                            s2.clone(),
                                            e.clone(),
                                            stroke.clone(),
                                            start_feature2.clone(),
                                            end_feature.clone(),
                                            ));
                                }
                            }

                        }
                        return None;
                    }
                    _ => None,
                }
            }
            Element::Text(ref loc, ref text) => {
                match *other {
                    Element::Text(ref loc2, ref text2) => {
                        // reduce if other is next to it
                        let uwidth = text.width() as i32;
                        if loc.y == loc2.y && loc.x + uwidth == loc2.x {
                            let merged_text = text.clone() + text2;
                            let reduced = Some(Element::Text(loc.clone(), merged_text));
                            reduced
                        } else {
                            None
                        }
                    }
                    _ => None,
                }
            }
            _ => None,
        }
    }

    /// convert drawing element to SVG element
    pub fn to_svg(&self, settings: &Settings) -> SvgElement {
        match *self {
            Element::Circle(ref c, r, ref class) => {
                let svg_circle = SvgCircle::new()
                    .set("class", class.clone())
                    .set("cx", c.x)
                    .set("cy", c.y)
                    .set("r", r);

                SvgElement::Circle(svg_circle)
            }
            Element::Line(ref s, ref e, ref stroke, ref start_feature, ref end_feature) => {
                let mut svg_line = SvgLine::new()
                    .set("x1", s.x)
                    .set("y1", s.y)
                    .set("x2", e.x)
                    .set("y2", e.y);
                match *start_feature {
                    Nothing => (),
                    Arrow => {
                        svg_line.assign("marker-start", "url(#triangle)");
                    }
                    ArrowStart => {
                        svg_line.assign("marker-start", "url(#triangle)");
                    }
                    Circle => {
                        svg_line.assign("marker-start", "url(#circle)");
                    }
                    Square => {
                        svg_line.assign("marker-start", "url(#square)");
                    }
                    OpenCircle => {
                        svg_line.assign("marker-start", "url(#open_circle)");
                    }
                    BigOpenCircle => {
                        svg_line.assign("marker-start", "url(#big_open_circle)");
                    }
                }
                match *end_feature {
                    Nothing => (),
                    Arrow => {
                        svg_line.assign("marker-end", "url(#triangle)");
                    }
                    ArrowStart => {
                        svg_line.assign("marker-end", "url(#triangle)");
                    }
                    Circle => {
                        svg_line.assign("marker-end", "url(#circle)");
                    }
                    Square => {
                        svg_line.assign("marker-end", "url(#square)");
                    }
                    OpenCircle => {
                        svg_line.assign("marker-end", "url(#open_circle)");
                    }
                    BigOpenCircle => {
                        svg_line.assign("marker-end", "url(#big_open_circle)");
                    }
                }
                match *stroke {
                    Solid => (),
                    Dashed => {
                        svg_line.assign("fill", "none");
                        svg_line.assign("class","dashed");
                    }
                };
                SvgElement::Line(svg_line)
            }
            Element::Arc(ref s, ref e, radius, ref arc_flag, sweep, _, ref start_feature, ref end_feature) => {
                let sweept = if sweep { "1" } else { "0" };
                let arc_flag = match *arc_flag {
                    Major => "1",
                    Minor => "0",
                };
                let d = format!(
                    "M {} {} A {} {} 0 {} {} {} {}",
                    s.x, s.y, radius, radius, arc_flag, sweept, e.x, e.y
                );
                let mut svg_arc = SvgPath::new().set("d", d).set("fill", "none");
                match *start_feature {
                    Nothing => {}
                    Arrow => {
                        svg_arc.assign("marker-start", "url(#triangle)");
                    }
                    ArrowStart => {
                        svg_arc.assign("marker-start", "url(#triangle)");
                    }
                    Circle => {
                        svg_arc.assign("marker-start", "url(#circle)");
                    }
                    Square => {
                        svg_arc.assign("marker-start", "url(#square)");
                    }
                    OpenCircle => {
                        svg_arc.assign("marker-start", "url(#open_circle)");
                    }
                    BigOpenCircle => {
                        svg_arc.assign("marker-start", "url(#big_open_circle)");
                    }
                }
                match *end_feature {
                    Nothing => {}
                    Arrow => {
                        svg_arc.assign("marker-end", "url(#triangle)");
                    }
                    ArrowStart => {
                        svg_arc.assign("marker-end", "url(#triangle)");
                    }
                    Circle => {
                        svg_arc.assign("marker-end", "url(#circle)");
                    }
                    Square => {
                        svg_arc.assign("marker-end", "url(#square)");
                    }
                    OpenCircle => {
                        svg_arc.assign("marker-end", "url(#open_circle)");
                    }
                    BigOpenCircle => {
                        svg_arc.assign("marker-end", "url(#big_open_circle)");
                    }
                }
                SvgElement::Path(svg_arc)
            }
            Element::Text(ref loc, ref string) => {
                let sx = loc.x as f32 * settings.text_width + settings.text_width / 8.0;
                let sy = loc.y as f32 * settings.text_height + settings.text_height * 3.0 / 4.0;
                let mut svg_text = SvgText::new().set("x", sx).set("y", sy);
                let text_node = svg::node::Text::new(string.to_string());
                svg_text.append(text_node);
                SvgElement::Text(svg_text)
            }
        }
    }
}
<commit_msg>Fix bug arrows disappearing when connecting to top, to left and top_left<commit_after>use std::cmp::Ordering;
use svg_element::SvgElement;
use svg::Node;
use svg::node::element::{
    Circle as SvgCircle,
    Line as SvgLine,
    Path as SvgPath,
    Text as SvgText,
};
use grid::svg_escape;
use point::collinear;
use settings::Settings;

use point::Point;
use loc::Loc;
use element::{
    Stroke::{Solid,Dashed},
    ArcFlag::{Minor,Major},
    Feature::{Arrow,ArrowStart,Circle,Square,OpenCircle,BigOpenCircle,Nothing},
};
use unicode_width::UnicodeWidthStr;
use point;

#[derive(Debug, Clone, PartialEq, PartialOrd )]
pub enum Element {
    Circle(Point, f32, String),
    //   start,  end,  stroke , start_feature, end feature
    Line(Point, Point, Stroke, Feature, Feature),
    //   start, end, radius,   sweep,   stroke, start_feat, end_feat
    Arc(Point, Point, f32, ArcFlag, bool, Stroke, Feature, Feature),
    Text(Loc, String),
}

impl Element{

    fn longer_line<'a>(&'a self, other: &'a Element) -> &'a Element {
        match self{
            Element::Line(s1, e1, _, _, _) => match other{
                Element::Line(s2, e2, _, _,_) => {
                    let d1 = point::distance(s1, e1);
                    let d2 = point::distance(s2, e2);
                    if d1 > d2 {
                        self
                    }
                    else{
                        other
                    }
                }
                _ => panic!("only for lines"),
            }
            _ => panic!("only for lines"),
        }
    }
}

#[derive(Debug, Clone, PartialEq, PartialOrd, Ord, Eq)]
pub enum Stroke {
    Solid,
    Dashed,
}

//TODO: rename to marker
#[derive(Debug, Clone, PartialEq, PartialOrd, Ord, Eq)]
pub enum Feature {
    ArrowStart, // start arrow
    Arrow,  //end
    Circle, //start
    Square, //start
    OpenCircle, //start
    BigOpenCircle, //start
    Nothing,
}

#[derive(Debug, Clone, PartialEq, PartialOrd)]
pub enum ArcFlag {
    Major,
    Minor,
}

impl Ord for Element{
    fn cmp(&self, other: &Self) -> Ordering{
        if let Some(order) = self.partial_cmp(&other){
            return order
        }
        Ordering::Less
    }
}
impl Eq for Element{
}

pub fn line(a: &Point, b: &Point) -> Element {
    Element::Line(a.clone(), b.clone(), Solid, Nothing, Nothing)
}

pub fn dashed_line(a: &Point, b: &Point) -> Element {
    Element::Line(a.clone(), b.clone(), Dashed, Nothing, Nothing)
}

pub fn circle_start_line(a: &Point, b: &Point) -> Element {
    Element::Line(a.clone(), b.clone(), Solid, Circle, Nothing)
}
pub fn square_start_line(a: &Point, b: &Point) -> Element {
    Element::Line(a.clone(), b.clone(), Solid, Square, Nothing)
}

pub fn circle_open_line(a: &Point, b: &Point) -> Element {
    Element::Line(a.clone(), b.clone(), Solid, OpenCircle, Nothing)
}

pub fn big_circle_open_line(a: &Point, b: &Point) -> Element {
    Element::Line(a.clone(), b.clone(), Solid, BigOpenCircle, Nothing)
}

pub fn arc(a: &Point, b: &Point, r: f32) -> Element {
    Element::Arc(a.clone(), b.clone(), r, Minor, false, Solid, Nothing, Nothing)
}

pub fn open_circle(c: &Point, r: f32) -> Element {
    Element::Circle(c.clone(), r.clone(), "open".to_string())
}

pub fn arrow_line(s: &Point, e: &Point) -> Element {
    Element::Line(s.clone(), e.clone(), Solid, Nothing, Arrow)
}

pub fn start_arrow_line(s: &Point, e: &Point) -> Element {
    Element::Line(s.clone(), e.clone(), Solid, ArrowStart, Nothing)
}

pub fn text(loc: &Loc, txt: &str) -> Element {
    Element::Text(loc.clone(), svg_escape(txt))
}

impl Element {

    pub fn shares_endpoints(&self, other: &Element) -> bool {
        match *self{
            Element::Line(ref s, ref e, _, _,_) => {
                match *other{
                    Element::Line(ref s2, ref e2, _, _,_) => {
                        s == s2 || e == e2 || s == e2 || s2 == e
                    }
                    Element::Arc(ref s2, ref e2, _,_,_,_,_,_) => {
                        s == s2 || e == e2 || s == e2 || s2 == e
                    }
                    _ => false
                }
            }
            Element::Arc(ref s, ref e, _,_,_,_,_,_) => {
                match *other{
                    Element::Line(ref s2, ref e2, _, _,_) => {
                        s == s2 || e == e2 || s == e2 || s2 == e
                    }
                    Element::Arc(ref s2, ref e2, _,_,_,_,_,_) => {
                        s == s2 || e == e2 || s == e2 || s2 == e
                    }
                    _ => false
                }
            }
            _ => false
        }
    }
    // if this element can reduce the other, return the new reduced element
    // for line it has to be collinear and in can connect start->end->start
    // for text, the other text should apear on the right side of this text
    pub fn reduce(&self, other: &Element) -> Option<Element> {
        // if same then return one
        if self == other{
            return Some(other.clone())
        }
        match *self {
            Element::Line(ref s, ref e, ref stroke, ref start_feature, ref end_feature) => {
                match *other {
                    Element::Line(ref s2, ref e2, ref stroke2, ref start_feature2, ref end_feature2) => {
                        // note: dual 3 point check for trully collinear lines
                        if collinear(s, e, s2)
                            && collinear(s, e, e2)
                            && stroke == stroke2{
                            // same length line
                            if s == s2 && e == e2 && start_feature == start_feature2 && end_feature == end_feature2 {
                                return Some(other.clone())
                            }

                            // extend 1 with 2
                            //    line1      line2
                            //   s-----e   s2-----e2
                            //   s----------------e2
                            else if e == s2 {
                                // -----
                                // o----
                                // ------
                                // ------>
                                if *end_feature == Nothing && *start_feature2 == Nothing{
                                    return Some(Element::Line(
                                                s.clone(),
                                                e2.clone(),
                                                stroke.clone(),
                                                start_feature.clone(),
                                                end_feature2.clone()
                                                ));
                                }
                            }
                            //  extend 1 with flip 2
                            //    line1     line2
                            //  s------e   e2-------s2
                            //  s-------------------s2
                            else if e == e2{
                                //  -------  --------
                                //  o------  ---------
                                if *end_feature == Nothing && *end_feature2 == Nothing{
                                    return Some(Element::Line(
                                            s.clone(),
                                            s2.clone(),
                                            stroke.clone(),
                                            start_feature.clone(),
                                            start_feature2.clone(),
                                            ));
                                }
                            }
                            // flip1 extend 2
                            //    line1    line2
                            //  e------s   s2------e2
                            //  s------------------e2
                            else if s == s2{
                                // -------   -------
                                // -------  ------->
                                // except for line 1 has arrow at the end
                                if *start_feature == Nothing && *start_feature2 == Nothing && *end_feature != Arrow {
                                    return Some(Element::Line(
                                            e.clone(),
                                            e2.clone(),
                                            stroke.clone(),
                                            end_feature.clone(),
                                            end_feature2.clone(),
                                            ));
                               }
                            }
                            // extend 2 with 1
                            //      line1    line2
                            //  e------s    e2------s2
                            //  e---------------------s2
                            //
                            else if s == e2{
                                //   -----   -----
                                //   -----   ----o
                                //   <----   -----
                                //   <----   ----o
                                if *start_feature == Nothing && *end_feature2 == Nothing{
                                    return Some(Element::Line(
                                            s2.clone(),
                                            e.clone(),
                                            stroke.clone(),
                                            start_feature2.clone(),
                                            end_feature.clone(),
                                            ));
                                }
                            }

                        }
                        return None;
                    }
                    _ => None,
                }
            }
            Element::Text(ref loc, ref text) => {
                match *other {
                    Element::Text(ref loc2, ref text2) => {
                        // reduce if other is next to it
                        let uwidth = text.width() as i32;
                        if loc.y == loc2.y && loc.x + uwidth == loc2.x {
                            let merged_text = text.clone() + text2;
                            let reduced = Some(Element::Text(loc.clone(), merged_text));
                            reduced
                        } else {
                            None
                        }
                    }
                    _ => None,
                }
            }
            _ => None,
        }
    }

    /// convert drawing element to SVG element
    pub fn to_svg(&self, settings: &Settings) -> SvgElement {
        match *self {
            Element::Circle(ref c, r, ref class) => {
                let svg_circle = SvgCircle::new()
                    .set("class", class.clone())
                    .set("cx", c.x)
                    .set("cy", c.y)
                    .set("r", r);

                SvgElement::Circle(svg_circle)
            }
            Element::Line(ref s, ref e, ref stroke, ref start_feature, ref end_feature) => {
                let mut svg_line = SvgLine::new()
                    .set("x1", s.x)
                    .set("y1", s.y)
                    .set("x2", e.x)
                    .set("y2", e.y);
                match *start_feature {
                    Nothing => (),
                    Arrow => {
                        svg_line.assign("marker-start", "url(#triangle)");
                    }
                    ArrowStart => {
                        svg_line.assign("marker-start", "url(#triangle)");
                    }
                    Circle => {
                        svg_line.assign("marker-start", "url(#circle)");
                    }
                    Square => {
                        svg_line.assign("marker-start", "url(#square)");
                    }
                    OpenCircle => {
                        svg_line.assign("marker-start", "url(#open_circle)");
                    }
                    BigOpenCircle => {
                        svg_line.assign("marker-start", "url(#big_open_circle)");
                    }
                }
                match *end_feature {
                    Nothing => (),
                    Arrow => {
                        svg_line.assign("marker-end", "url(#triangle)");
                    }
                    ArrowStart => {
                        svg_line.assign("marker-end", "url(#triangle)");
                    }
                    Circle => {
                        svg_line.assign("marker-end", "url(#circle)");
                    }
                    Square => {
                        svg_line.assign("marker-end", "url(#square)");
                    }
                    OpenCircle => {
                        svg_line.assign("marker-end", "url(#open_circle)");
                    }
                    BigOpenCircle => {
                        svg_line.assign("marker-end", "url(#big_open_circle)");
                    }
                }
                match *stroke {
                    Solid => (),
                    Dashed => {
                        svg_line.assign("fill", "none");
                        svg_line.assign("class","dashed");
                    }
                };
                SvgElement::Line(svg_line)
            }
            Element::Arc(ref s, ref e, radius, ref arc_flag, sweep, _, ref start_feature, ref end_feature) => {
                let sweept = if sweep { "1" } else { "0" };
                let arc_flag = match *arc_flag {
                    Major => "1",
                    Minor => "0",
                };
                let d = format!(
                    "M {} {} A {} {} 0 {} {} {} {}",
                    s.x, s.y, radius, radius, arc_flag, sweept, e.x, e.y
                );
                let mut svg_arc = SvgPath::new().set("d", d).set("fill", "none");
                match *start_feature {
                    Nothing => {}
                    Arrow => {
                        svg_arc.assign("marker-start", "url(#triangle)");
                    }
                    ArrowStart => {
                        svg_arc.assign("marker-start", "url(#triangle)");
                    }
                    Circle => {
                        svg_arc.assign("marker-start", "url(#circle)");
                    }
                    Square => {
                        svg_arc.assign("marker-start", "url(#square)");
                    }
                    OpenCircle => {
                        svg_arc.assign("marker-start", "url(#open_circle)");
                    }
                    BigOpenCircle => {
                        svg_arc.assign("marker-start", "url(#big_open_circle)");
                    }
                }
                match *end_feature {
                    Nothing => {}
                    Arrow => {
                        svg_arc.assign("marker-end", "url(#triangle)");
                    }
                    ArrowStart => {
                        svg_arc.assign("marker-end", "url(#triangle)");
                    }
                    Circle => {
                        svg_arc.assign("marker-end", "url(#circle)");
                    }
                    Square => {
                        svg_arc.assign("marker-end", "url(#square)");
                    }
                    OpenCircle => {
                        svg_arc.assign("marker-end", "url(#open_circle)");
                    }
                    BigOpenCircle => {
                        svg_arc.assign("marker-end", "url(#big_open_circle)");
                    }
                }
                SvgElement::Path(svg_arc)
            }
            Element::Text(ref loc, ref string) => {
                let sx = loc.x as f32 * settings.text_width + settings.text_width / 8.0;
                let sy = loc.y as f32 * settings.text_height + settings.text_height * 3.0 / 4.0;
                let mut svg_text = SvgText::new().set("x", sx).set("y", sy);
                let text_node = svg::node::Text::new(string.to_string());
                svg_text.append(text_node);
                SvgElement::Text(svg_text)
            }
        }
    }
}
<|endoftext|>


<commit_before>#![crate_type="staticlib"]
#![feature(alloc)]
#![feature(allocator)]
#![feature(arc_counts)]
#![feature(asm)]
#![feature(box_syntax)]
#![feature(collections)]
#![feature(const_fn)]
#![feature(core_intrinsics)]
#![feature(core_str_ext)]
#![feature(core_slice_ext)]
#![feature(fnbox)]
#![feature(fundamental)]
#![feature(lang_items)]
#![feature(no_std)]
#![feature(unboxed_closures)]
#![feature(unsafe_no_drop_flag)]
#![feature(unwind_attributes)]
#![feature(vec_push_all)]
#![no_std]

#[macro_use]
extern crate alloc;

#[macro_use]
extern crate collections;

use alloc::boxed::Box;

use collections::string::{String, ToString};
use collections::vec::Vec;

use core::cell::UnsafeCell;
use core::{mem, usize};
use core::slice::SliceExt;

use common::debug;
use common::event::{self, EVENT_KEY, EventOption};
use common::memory;
use common::paging::Page;
use common::time::Duration;

use drivers::pci::*;
use drivers::pio::*;
use drivers::ps2::*;
use drivers::rtc::*;
use drivers::serial::*;

use env::Environment;

pub use externs::*;

use graphics::display;

use programs::executor::execute;
use programs::scheme::*;

use scheduler::{Context, Regs, TSS};
use scheduler::context::{context_enabled, context_switch, context_i, context_pid};

use schemes::Url;
use schemes::arp::*;
use schemes::context::*;
use schemes::debug::*;
use schemes::ethernet::*;
use schemes::icmp::*;
use schemes::ip::*;
use schemes::memory::*;
// use schemes::display::*;

use syscall::handle::*;

/// Common std-like functionality
#[macro_use]
pub mod common;
/// Allocation
pub mod alloc_system;
/// Audio
pub mod audio;
/// Various drivers
/// TODO: Move out of kernel space (like other microkernels)
pub mod drivers;
/// Environment
pub mod env;
/// Externs
pub mod externs;
/// Various graphical methods
pub mod graphics;
/// Network
pub mod network;
/// Panic
pub mod panic;
/// Programs
pub mod programs;
/// Schemes
pub mod schemes;
/// Scheduling
pub mod scheduler;
/// Sync primatives
pub mod sync;
/// System calls
pub mod syscall;
/// USB input/output
pub mod usb;

pub static mut TSS_PTR: Option<&'static mut TSS> = None;

pub static mut ENV_PTR: Option<&'static mut Environment> = None;

pub fn env() -> &'static Environment {
    unsafe {
        match ENV_PTR {
            Some(&mut ref p) => p,
            None => unreachable!(),
        }
    }
}

/// Pit duration
static PIT_DURATION: Duration = Duration {
    secs: 0,
    nanos: 2250286,
};

/// Idle loop (active while idle)
unsafe fn idle_loop() -> ! {
    loop {
        asm!("cli" : : : : "intel", "volatile");

        let mut halt = true;

        {
            let contexts = ::env().contexts.lock();
            for i in 1..contexts.len() {
                match contexts.get(i) {
                    Some(context) => if context.interrupted {
                        halt = false;
                        break;
                    },
                    None => (),
                }
            }
        }

        if halt {
            asm!("sti" : : : : "intel", "volatile");
            asm!("hlt" : : : : "intel", "volatile");
        } else {
            asm!("sti" : : : : "intel", "volatile");
        }

        context_switch(false);
    }
}

/// Event poll loop
fn poll_loop() -> ! {
    loop {
        env().on_poll();

        unsafe { context_switch(false) };
    }
}

/// Event loop
fn event_loop() -> ! {
    let mut cmd = String::new();
    loop {
        loop {
            let mut console = env().console.lock();
            match env().events.lock().pop_front() {
                Some(event) => {
                    if console.draw {
                        match event.to_option() {
                            EventOption::Key(key_event) => {
                                if key_event.pressed {
                                    match key_event.scancode {
                                        event::K_F2 => {
                                            console.draw = false;
                                        }
                                        event::K_BKSP => if !cmd.is_empty() {
                                            console.write(&[8]);
                                            cmd.pop();
                                        },
                                        _ => match key_event.character {
                                            '\0' => (),
                                            '\n' => {
                                                console.command = Some(cmd.clone());

                                                cmd.clear();
                                                console.write(&[10]);
                                            }
                                            _ => {
                                                cmd.push(key_event.character);
                                                console.write(&[key_event.character as u8]);
                                            }
                                        },
                                    }
                                }
                            }
                            _ => (),
                        }
                    } else {
                        if event.code == EVENT_KEY && event.b as u8 == event::K_F1 && event.c > 0 {
                            console.draw = true;
                            console.redraw = true;
                        } else {
                            // TODO: Magical orbital hack
                            unsafe {
                                let reenable = scheduler::start_no_ints();
                                for scheme in env().schemes.iter() {
                                    if (*scheme.get()).scheme() == "orbital" {
                                        (*scheme.get()).event(&event);
                                        break;
                                    }
                                }
                                scheduler::end_no_ints(reenable);
                            }
                        }
                    }
                }
                None => break,
            }
        }

        {
            let mut console = env().console.lock();
            if console.draw {
                if console.redraw {
                    console.redraw = false;
                    console.display.flip();
                }
            }
        }

        unsafe { context_switch(false) };
    }
}

/// Initialize kernel
unsafe fn init(font_data: usize, tss_data: usize) {
    Page::init();
    memory::cluster_init();
    // Unmap first page to catch null pointer errors (after reading memory map)
    Page::new(0).unmap();

    sync::intex::intex_count = 0;

    display::fonts = font_data;
    TSS_PTR = Some(&mut *(tss_data as *mut TSS));

    ENV_PTR = Some(&mut *Box::into_raw(Environment::new()));

    context_pid = 1;
    context_i = 0;
    context_enabled = false;

    match ENV_PTR {
        Some(ref mut env) => {
            env.contexts.lock().push(Context::root());
            env.console.lock().draw = true;

            debug!("Redox ");
            debug::dd(mem::size_of::<usize>() * 8);
            debug!(" bits");
            debug::dl();

            env.clock_realtime = Rtc::new().time();

            env.schemes.push(UnsafeCell::new(Ps2::new()));
            env.schemes.push(UnsafeCell::new(Serial::new(0x3F8, 0x4)));

            pci_init(env);

            env.schemes.push(UnsafeCell::new(DebugScheme::new()));
            env.schemes.push(UnsafeCell::new(box ContextScheme));
            env.schemes.push(UnsafeCell::new(box MemoryScheme));
            // session.items.push(box RandomScheme);
            // session.items.push(box TimeScheme);

            env.schemes.push(UnsafeCell::new(box EthernetScheme));
            env.schemes.push(UnsafeCell::new(box ArpScheme));
            env.schemes.push(UnsafeCell::new(box IcmpScheme));
            env.schemes.push(UnsafeCell::new(box IpScheme { arp: Vec::new() }));
            // session.items.push(box DisplayScheme);

            Context::spawn("kpoll".to_string(),
            box move || {
                poll_loop();
            });

            Context::spawn("kevent".to_string(),
            box move || {
                event_loop();
            });

            Context::spawn("karp".to_string(),
            box move || {
                ArpScheme::reply_loop();
            });

            Context::spawn("kicmp".to_string(),
            box move || {
                IcmpScheme::reply_loop();
            });

            context_enabled = true;

            if let Some(mut resource) = Url::from_str("file:/schemes/").open() {
                let mut vec: Vec<u8> = Vec::new();
                resource.read_to_end(&mut vec);

                for folder in String::from_utf8_unchecked(vec).lines() {
                    if folder.ends_with('/') {
                        let scheme_item = SchemeItem::from_url(&Url::from_string("file:/schemes/"
                                                                                .to_string() +
                                                                                &folder));

                        let reenable = scheduler::start_no_ints();
                        env.schemes.push(UnsafeCell::new(scheme_item));
                        scheduler::end_no_ints(reenable);
                    }
                }
            }

            Context::spawn("kinit".to_string(),
            box move || {
                let wd_c = "file:/\0";
                do_sys_chdir(wd_c.as_ptr());

                let stdio_c = "debug:\0";
                do_sys_open(stdio_c.as_ptr(), 0);
                do_sys_open(stdio_c.as_ptr(), 0);
                do_sys_open(stdio_c.as_ptr(), 0);

                let path_string = "file:/apps/shell/main.bin";
                let path = Url::from_str(path_string);

                debug!("INIT: Executing {}\n", path_string);
                execute(path, Vec::new());
                debug!("INIT: Failed to execute\n");

                loop {
                    context_switch(false);
                }
            });
        },
        None => unreachable!(),
    }
}

#[cold]
#[inline(never)]
#[no_mangle]
/// Take regs for kernel calls and exceptions
pub extern "cdecl" fn kernel(interrupt: usize, mut regs: &mut Regs) {
    macro_rules! exception_inner {
        ($name:expr) => ({
            unsafe {
                if let Some(context) = Context::current() {
                    debugln!("PID {}: {}", context.pid, context.name);
                }
            }

            debugln!("  INT {:X}: {}", interrupt, $name);
            debugln!("    CS:  {:08X}    IP:  {:08X}    FLG: {:08X}", regs.cs, regs.ip, regs.flags);
            debugln!("    SS:  {:08X}    SP:  {:08X}    BP:  {:08X}", regs.ss, regs.sp, regs.bp);
            debugln!("    AX:  {:08X}    BX:  {:08X}    CX:  {:08X}    DX:  {:08X}", regs.ax, regs.bx, regs.cx, regs.dx);
            debugln!("    DI:  {:08X}    SI:  {:08X}", regs.di, regs.di);

            let cr0: usize;
            let cr2: usize;
            let cr3: usize;
            let cr4: usize;
            unsafe {
                asm!("mov $0, cr0" : "=r"(cr0) : : : "intel", "volatile");
                asm!("mov $0, cr2" : "=r"(cr2) : : : "intel", "volatile");
                asm!("mov $0, cr3" : "=r"(cr3) : : : "intel", "volatile");
                asm!("mov $0, cr4" : "=r"(cr4) : : : "intel", "volatile");
            }
            debugln!("    CR0: {:08X}    CR2: {:08X}    CR3: {:08X}    CR4: {:08X}", cr0, cr2, cr3, cr4);
        })
    };

    macro_rules! exception {
        ($name:expr) => ({
            exception_inner!($name);

            loop {
                unsafe { do_sys_exit(usize::MAX) };
            }
        })
    };

    macro_rules! exception_error {
        ($name:expr) => ({
            let error = regs.ip;
            regs.ip = regs.cs;
            regs.cs = regs.flags;
            regs.flags = regs.sp;
            regs.sp = regs.ss;
            regs.ss = 0;
            //regs.ss = regs.error;

            exception_inner!($name);
            debugln!("    ERR: {:08X}", error);

            loop {
                unsafe { do_sys_exit(usize::MAX) };
            }
        })
    };

    if interrupt >= 0x20 && interrupt < 0x30 {
        if interrupt >= 0x28 {
            unsafe { Pio8::new(0xA0).write(0x20) };
        }

        unsafe { Pio8::new(0x20).write(0x20) };
    }

    match interrupt {
        0x20 => {
            unsafe {
                let reenable = scheduler::start_no_ints();

                match ENV_PTR {
                    Some(ref mut env) => {
                        env.clock_realtime = env.clock_realtime + PIT_DURATION;
                        env.clock_monotonic = env.clock_monotonic + PIT_DURATION;

                        scheduler::end_no_ints(reenable);

                        let switch = if let Some(mut context) = Context::current_mut() {
                            context.slices -= 1;
                            context.slice_total += 1;
                            context.slices == 0
                        } else {
                            false
                        };

                        if switch {
                            context_switch(true);
                        }
                    },
                    None => unreachable!(),
                }
            }
        }
        0x21 => env().on_irq(0x1), // keyboard
        0x23 => env().on_irq(0x3), // serial 2 and 4
        0x24 => env().on_irq(0x4), // serial 1 and 3
        0x25 => env().on_irq(0x5), //parallel 2
        0x26 => env().on_irq(0x6), //floppy
        0x27 => env().on_irq(0x7), //parallel 1 or spurious
        0x28 => env().on_irq(0x8), //RTC
        0x29 => env().on_irq(0x9), //pci
        0x2A => env().on_irq(0xA), //pci
        0x2B => env().on_irq(0xB), //pci
        0x2C => env().on_irq(0xC), //mouse
        0x2D => env().on_irq(0xD), //coprocessor
        0x2E => env().on_irq(0xE), //disk
        0x2F => env().on_irq(0xF), //disk
        0x80 => if !unsafe { syscall_handle(regs) } {
            exception!("Unknown Syscall");
        },
        0xFF => {
            unsafe {
                init(regs.ax, regs.bx);
                idle_loop();
            }
        }
        0x0 => exception!("Divide by zero exception"),
        0x1 => exception!("Debug exception"),
        0x2 => exception!("Non-maskable interrupt"),
        0x3 => exception!("Breakpoint exception"),
        0x4 => exception!("Overflow exception"),
        0x5 => exception!("Bound range exceeded exception"),
        0x6 => exception!("Invalid opcode exception"),
        0x7 => exception!("Device not available exception"),
        0x8 => exception_error!("Double fault"),
        0xA => exception_error!("Invalid TSS exception"),
        0xB => exception_error!("Segment not present exception"),
        0xC => exception_error!("Stack-segment fault"),
        0xD => exception_error!("General protection fault"),
        0xE => exception_error!("Page fault"),
        0x10 => exception!("x87 floating-point exception"),
        0x11 => exception_error!("Alignment check exception"),
        0x12 => exception!("Machine check exception"),
        0x13 => exception!("SIMD floating-point exception"),
        0x14 => exception!("Virtualization exception"),
        0x1E => exception_error!("Security exception"),
        _ => exception!("Unknown Interrupt"),
    }
}
<commit_msg>Code cleanup<commit_after>#![crate_type="staticlib"]
#![feature(alloc)]
#![feature(allocator)]
#![feature(arc_counts)]
#![feature(asm)]
#![feature(box_syntax)]
#![feature(collections)]
#![feature(const_fn)]
#![feature(core_intrinsics)]
#![feature(core_str_ext)]
#![feature(core_slice_ext)]
#![feature(fnbox)]
#![feature(fundamental)]
#![feature(lang_items)]
#![feature(no_std)]
#![feature(unboxed_closures)]
#![feature(unsafe_no_drop_flag)]
#![feature(unwind_attributes)]
#![feature(vec_push_all)]
#![no_std]

#[macro_use]
extern crate alloc;

#[macro_use]
extern crate collections;

use alloc::boxed::Box;

use collections::string::{String, ToString};
use collections::vec::Vec;

use core::cell::UnsafeCell;
use core::{mem, usize};
use core::slice::SliceExt;

use common::debug;
use common::event::{self, EVENT_KEY, EventOption};
use common::memory;
use common::paging::Page;
use common::time::Duration;

use drivers::pci::*;
use drivers::pio::*;
use drivers::ps2::*;
use drivers::rtc::*;
use drivers::serial::*;

use env::Environment;

pub use externs::*;

use graphics::display;

use programs::executor::execute;
use programs::scheme::*;

use scheduler::{Context, Regs, TSS};
use scheduler::context::{context_enabled, context_switch, context_i, context_pid};

use schemes::Url;
use schemes::arp::*;
use schemes::context::*;
use schemes::debug::*;
use schemes::ethernet::*;
use schemes::icmp::*;
use schemes::ip::*;
use schemes::memory::*;
// use schemes::display::*;

use syscall::handle::*;

/// Common std-like functionality
#[macro_use]
pub mod common;
/// Allocation
pub mod alloc_system;
/// Audio
pub mod audio;
/// Various drivers
/// TODO: Move out of kernel space (like other microkernels)
pub mod drivers;
/// Environment
pub mod env;
/// Externs
pub mod externs;
/// Various graphical methods
pub mod graphics;
/// Network
pub mod network;
/// Panic
pub mod panic;
/// Programs
pub mod programs;
/// Schemes
pub mod schemes;
/// Scheduling
pub mod scheduler;
/// Sync primatives
pub mod sync;
/// System calls
pub mod syscall;
/// USB input/output
pub mod usb;

pub static mut TSS_PTR: Option<&'static mut TSS> = None;
pub static mut ENV_PTR: Option<&'static mut Environment> = None;

pub fn env() -> &'static Environment {
    unsafe {
        match ENV_PTR {
            Some(&mut ref p) => p,
            None => unreachable!(),
        }
    }
}

/// Pit duration
static PIT_DURATION: Duration = Duration {
    secs: 0,
    nanos: 2250286,
};

/// Idle loop (active while idle)
unsafe fn idle_loop() -> ! {
    loop {
        asm!("cli" : : : : "intel", "volatile");

        let mut halt = true;

        {
            let contexts = ::env().contexts.lock();
            for i in 1..contexts.len() {
                if let Some(context) = contexts.get(i) {
                    if context.interrupted {
                        halt = false;
                        break;
                    }
                }
            }
        }

        if halt {
            asm!("sti" : : : : "intel", "volatile");
            asm!("hlt" : : : : "intel", "volatile");
        } else {
            asm!("sti" : : : : "intel", "volatile");
        }

        context_switch(false);
    }
}

/// Event poll loop
fn poll_loop() -> ! {
    loop {
        env().on_poll();

        unsafe { context_switch(false) };
    }
}

/// Event loop
fn event_loop() -> ! {
    let mut cmd = String::new();
    loop {
        loop {
            let mut console = env().console.lock();
            match env().events.lock().pop_front() {
                Some(event) => {
                    if console.draw {
                        match event.to_option() {
                            EventOption::Key(key_event) => {
                                if key_event.pressed {
                                    match key_event.scancode {
                                        event::K_F2 => {
                                            console.draw = false;
                                        }
                                        event::K_BKSP => if !cmd.is_empty() {
                                            console.write(&[8]);
                                            cmd.pop();
                                        },
                                        _ => match key_event.character {
                                            '\0' => (),
                                            '\n' => {
                                                console.command = Some(cmd.clone());

                                                cmd.clear();
                                                console.write(&[10]);
                                            }
                                            _ => {
                                                cmd.push(key_event.character);
                                                console.write(&[key_event.character as u8]);
                                            }
                                        },
                                    }
                                }
                            }
                            _ => (),
                        }
                    } else {
                        if event.code == EVENT_KEY && event.b as u8 == event::K_F1 && event.c > 0 {
                            console.draw = true;
                            console.redraw = true;
                        } else {
                            // TODO: Magical orbital hack
                            unsafe {
                                let reenable = scheduler::start_no_ints();
                                for scheme in env().schemes.iter() {
                                    if (*scheme.get()).scheme() == "orbital" {
                                        (*scheme.get()).event(&event);
                                        break;
                                    }
                                }
                                scheduler::end_no_ints(reenable);
                            }
                        }
                    }
                }
                None => break,
            }
        }

        {
            let mut console = env().console.lock();
            if console.draw && console.redraw {
                console.redraw = false;
                console.display.flip();
            }
        }

        unsafe { context_switch(false) };
    }
}

/// Initialize kernel
unsafe fn init(font_data: usize, tss_data: usize) {
    Page::init();
    memory::cluster_init();
    // Unmap first page to catch null pointer errors (after reading memory map)
    Page::new(0).unmap();

    sync::intex::intex_count = 0;

    display::fonts = font_data;
    TSS_PTR = Some(&mut *(tss_data as *mut TSS));
    ENV_PTR = Some(&mut *Box::into_raw(Environment::new()));

    context_pid = 1;
    context_i = 0;
    context_enabled = false;

    match ENV_PTR {
        Some(ref mut env) => {
            env.contexts.lock().push(Context::root());
            env.console.lock().draw = true;

            debug!("Redox ");
            debug::dd(mem::size_of::<usize>() * 8);
            debug!(" bits");
            debug::dl();

            env.clock_realtime = Rtc::new().time();

            env.schemes.push(UnsafeCell::new(Ps2::new()));
            env.schemes.push(UnsafeCell::new(Serial::new(0x3F8, 0x4)));

            pci_init(env);

            env.schemes.push(UnsafeCell::new(DebugScheme::new()));
            env.schemes.push(UnsafeCell::new(box ContextScheme));
            env.schemes.push(UnsafeCell::new(box MemoryScheme));
            // session.items.push(box RandomScheme);
            // session.items.push(box TimeScheme);

            env.schemes.push(UnsafeCell::new(box EthernetScheme));
            env.schemes.push(UnsafeCell::new(box ArpScheme));
            env.schemes.push(UnsafeCell::new(box IcmpScheme));
            env.schemes.push(UnsafeCell::new(box IpScheme { arp: Vec::new() }));
            // session.items.push(box DisplayScheme);

            Context::spawn("kpoll".to_string(),
            box move || {
                poll_loop();
            });

            Context::spawn("kevent".to_string(),
            box move || {
                event_loop();
            });

            Context::spawn("karp".to_string(),
            box move || {
                ArpScheme::reply_loop();
            });

            Context::spawn("kicmp".to_string(),
            box move || {
                IcmpScheme::reply_loop();
            });

            context_enabled = true;

            if let Some(mut resource) = Url::from_str("file:/schemes/").open() {
                let mut vec: Vec<u8> = Vec::new();
                resource.read_to_end(&mut vec);

                for folder in String::from_utf8_unchecked(vec).lines() {
                    if folder.ends_with('/') {
                        let scheme_item = SchemeItem::from_url(&Url::from_string("file:/schemes/"
                                                                                .to_string() +
                                                                                &folder));

                        let reenable = scheduler::start_no_ints();
                        env.schemes.push(UnsafeCell::new(scheme_item));
                        scheduler::end_no_ints(reenable);
                    }
                }
            }

            Context::spawn("kinit".to_string(),
            box move || {
                let wd_c = "file:/\0";
                do_sys_chdir(wd_c.as_ptr());

                let stdio_c = "debug:\0";
                do_sys_open(stdio_c.as_ptr(), 0);
                do_sys_open(stdio_c.as_ptr(), 0);
                do_sys_open(stdio_c.as_ptr(), 0);

                let path_string = "file:/apps/shell/main.bin";
                let path = Url::from_str(path_string);

                debug!("INIT: Executing {}\n", path_string);
                execute(path, Vec::new());
                debug!("INIT: Failed to execute\n");

                loop {
                    context_switch(false);
                }
            });
        },
        None => unreachable!(),
    }
}

#[cold]
#[inline(never)]
#[no_mangle]
/// Take regs for kernel calls and exceptions
pub extern "cdecl" fn kernel(interrupt: usize, mut regs: &mut Regs) {
    macro_rules! exception_inner {
        ($name:expr) => ({
            unsafe {
                if let Some(context) = Context::current() {
                    debugln!("PID {}: {}", context.pid, context.name);
                }
            }

            debugln!("  INT {:X}: {}", interrupt, $name);
            debugln!("    CS:  {:08X}    IP:  {:08X}    FLG: {:08X}", regs.cs, regs.ip, regs.flags);
            debugln!("    SS:  {:08X}    SP:  {:08X}    BP:  {:08X}", regs.ss, regs.sp, regs.bp);
            debugln!("    AX:  {:08X}    BX:  {:08X}    CX:  {:08X}    DX:  {:08X}", regs.ax, regs.bx, regs.cx, regs.dx);
            debugln!("    DI:  {:08X}    SI:  {:08X}", regs.di, regs.di);

            let cr0: usize;
            let cr2: usize;
            let cr3: usize;
            let cr4: usize;
            unsafe {
                asm!("mov $0, cr0" : "=r"(cr0) : : : "intel", "volatile");
                asm!("mov $0, cr2" : "=r"(cr2) : : : "intel", "volatile");
                asm!("mov $0, cr3" : "=r"(cr3) : : : "intel", "volatile");
                asm!("mov $0, cr4" : "=r"(cr4) : : : "intel", "volatile");
            }
            debugln!("    CR0: {:08X}    CR2: {:08X}    CR3: {:08X}    CR4: {:08X}", cr0, cr2, cr3, cr4);
        })
    };

    macro_rules! exception {
        ($name:expr) => ({
            exception_inner!($name);

            loop {
                unsafe { do_sys_exit(usize::MAX) };
            }
        })
    };

    macro_rules! exception_error {
        ($name:expr) => ({
            let error = regs.ip;
            regs.ip = regs.cs;
            regs.cs = regs.flags;
            regs.flags = regs.sp;
            regs.sp = regs.ss;
            regs.ss = 0;
            //regs.ss = regs.error;

            exception_inner!($name);
            debugln!("    ERR: {:08X}", error);

            loop {
                unsafe { do_sys_exit(usize::MAX) };
            }
        })
    };

    if interrupt >= 0x20 && interrupt < 0x30 {
        if interrupt >= 0x28 {
            unsafe { Pio8::new(0xA0).write(0x20) };
        }

        unsafe { Pio8::new(0x20).write(0x20) };
    }

    match interrupt {
        0x20 => {
            unsafe {
                let reenable = scheduler::start_no_ints();

                match ENV_PTR {
                    Some(ref mut env) => {
                        env.clock_realtime = env.clock_realtime + PIT_DURATION;
                        env.clock_monotonic = env.clock_monotonic + PIT_DURATION;

                        scheduler::end_no_ints(reenable);

                        let switch = if let Some(mut context) = Context::current_mut() {
                            context.slices -= 1;
                            context.slice_total += 1;
                            context.slices == 0
                        } else {
                            false
                        };

                        if switch {
                            context_switch(true);
                        }
                    },
                    None => unreachable!(),
                }
            }
        }
        0x21 => env().on_irq(0x1), // keyboard
        0x23 => env().on_irq(0x3), // serial 2 and 4
        0x24 => env().on_irq(0x4), // serial 1 and 3
        0x25 => env().on_irq(0x5), //parallel 2
        0x26 => env().on_irq(0x6), //floppy
        0x27 => env().on_irq(0x7), //parallel 1 or spurious
        0x28 => env().on_irq(0x8), //RTC
        0x29 => env().on_irq(0x9), //pci
        0x2A => env().on_irq(0xA), //pci
        0x2B => env().on_irq(0xB), //pci
        0x2C => env().on_irq(0xC), //mouse
        0x2D => env().on_irq(0xD), //coprocessor
        0x2E => env().on_irq(0xE), //disk
        0x2F => env().on_irq(0xF), //disk
        0x80 => if !unsafe { syscall_handle(regs) } {
            exception!("Unknown Syscall");
        },
        0xFF => {
            unsafe {
                init(regs.ax, regs.bx);
                idle_loop();
            }
        },
        0x0 => exception!("Divide by zero exception"),
        0x1 => exception!("Debug exception"),
        0x2 => exception!("Non-maskable interrupt"),
        0x3 => exception!("Breakpoint exception"),
        0x4 => exception!("Overflow exception"),
        0x5 => exception!("Bound range exceeded exception"),
        0x6 => exception!("Invalid opcode exception"),
        0x7 => exception!("Device not available exception"),
        0x8 => exception_error!("Double fault"),
        0xA => exception_error!("Invalid TSS exception"),
        0xB => exception_error!("Segment not present exception"),
        0xC => exception_error!("Stack-segment fault"),
        0xD => exception_error!("General protection fault"),
        0xE => exception_error!("Page fault"),
        0x10 => exception!("x87 floating-point exception"),
        0x11 => exception_error!("Alignment check exception"),
        0x12 => exception!("Machine check exception"),
        0x13 => exception!("SIMD floating-point exception"),
        0x14 => exception!("Virtualization exception"),
        0x1E => exception_error!("Security exception"),
        _ => exception!("Unknown Interrupt"),
    }
}
<|endoftext|>