From f4bd8b7704da86054107d7a86be6d810ec254dbf Mon Sep 17 00:00:00 2001 From: xTibor Date: Mon, 24 Apr 2017 09:09:05 +0200 Subject: [PATCH] Redox support (#35) --- Cargo.toml | 3 + src/lib.rs | 6 +- src/os/mod.rs | 4 +- src/os/redox/mod.rs | 434 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 444 insertions(+), 3 deletions(-) create mode 100644 src/os/redox/mod.rs diff --git a/Cargo.toml b/Cargo.toml index 1ff66ef..bd1c7d2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -57,3 +57,6 @@ x11-dl = "~2.2" [target.x86_64-unknown-freebsd.dependencies] x11-dl = "~2.2" + +[target.x86_64-unknown-redox.dependencies] +orbclient = "0.3.4" diff --git a/src/lib.rs b/src/lib.rs index c97f0a3..9cb17bd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -121,7 +121,8 @@ use self::os::windows as imp; target_os="netbsd", target_os="openbsd"))] use self::os::unix as imp; - +#[cfg(target_os = "redox")] +use self::os::redox as imp; /// /// Window is used to open up a window. It's possible to optionally display a 32-bit buffer when /// the widow is set as non-resizable. @@ -542,7 +543,8 @@ impl Window { target_os="freebsd", target_os="dragonfly", target_os="netbsd", - target_os="openbsd"))] + target_os="openbsd", + target_os="redox"))] pub fn get_unix_menus(&self) -> Option<&Vec> { self.0.get_unix_menus() } diff --git a/src/os/mod.rs b/src/os/mod.rs index 748dc68..a49520a 100644 --- a/src/os/mod.rs +++ b/src/os/mod.rs @@ -7,4 +7,6 @@ pub mod windows; target_os="dragonfly", target_os="netbsd", target_os="openbsd"))] -pub mod unix; +pub mod unix; +#[cfg(target_os = "redox")] +pub mod redox; diff --git a/src/os/redox/mod.rs b/src/os/redox/mod.rs new file mode 100644 index 0000000..2c6a44f --- /dev/null +++ b/src/os/redox/mod.rs @@ -0,0 +1,434 @@ +#![cfg(target_os = "redox")] + +extern crate orbclient; +use os::redox::orbclient::Renderer; + +use error::Error; +use Result; +use mouse_handler; +use key_handler::KeyHandler; +use InputCallback; +use {CursorStyle, MouseButton, MouseMode}; +use {Key, KeyRepeat}; +use {Scale, WindowOptions}; +use {MenuItem, MenuItemHandle, MenuHandle, UnixMenu, UnixMenuItem}; + +use std::cmp; +use std::os::raw; + +pub struct Window { + is_open: bool, + is_active: bool, + mouse_pos: Option<(i32, i32)>, + mouse_scroll: Option<(i32, i32)>, + /// The state of the left, middle and right mouse buttons + mouse_state: (bool, bool, bool), + buffer_width: usize, + buffer_height: usize, + window: orbclient::Window, + window_scale: usize, + key_handler: KeyHandler, + menu_counter: MenuHandle, + menus: Vec, +} + +impl Window { + pub fn new(name: &str, width: usize, height: usize, opts: WindowOptions) -> Result { + let window_scale = match opts.scale { + Scale::X1 => 1, + Scale::X2 => 2, + Scale::X4 => 4, + Scale::X8 => 8, + Scale::X16 => 16, + Scale::X32 => 32, + Scale::FitScreen => { + let display_size = orbclient::get_display_size() + .map_err(|_| Error::WindowCreate("Unable to get display size".to_owned()))?; + let mut scale = 32; + while scale > 1 { + if width * scale < display_size.0 as usize && + height * scale < display_size.1 as usize { + break; + } + scale -= 1; + } + scale + }, + }; + + let window_width = width as u32 * window_scale as u32; + let window_height = height as u32 * window_scale as u32; + let window_name = if opts.title { name } else { "" }; + + let mut window_flags = vec![orbclient::WindowFlag::Async]; + if opts.resize { + window_flags.push(orbclient::WindowFlag::Resizable); + } + + let window_opt = orbclient::Window::new_flags(-1, + -1, + window_width, + window_height, + window_name, + &window_flags); + match window_opt { + Some(window) => { + Ok(Window { + mouse_pos: None, + mouse_scroll: None, + mouse_state: (false, false, false), + is_open: true, + is_active: true, + buffer_width: width, + buffer_height: height, + window: window, + window_scale: window_scale, + key_handler: KeyHandler::new(), + menu_counter: MenuHandle(0), + menus: Vec::new(), + }) + }, + None => Err(Error::WindowCreate("Unable to open Window".to_owned())), + } + } + + pub fn set_title(&mut self, title: &str) { + self.window.set_title(title) + } + + pub fn get_window_handle(&self) -> *mut raw::c_void { + 0 as *mut raw::c_void + } + + pub fn update_with_buffer(&mut self, buffer: &[u32]) { + self.process_events(); + self.key_handler.update(); + self.render_buffer(buffer); + self.window.sync(); + } + + pub fn update(&mut self) { + self.process_events(); + self.key_handler.update(); + self.window.sync(); + } + + pub fn set_position(&mut self, x: isize, y: isize) { + self.window.set_pos(x as i32, y as i32) + } + + pub fn get_size(&self) -> (usize, usize) { + (self.window.width() as usize, self.window.height() as usize) + } + + pub fn get_scroll_wheel(&self) -> Option<(f32, f32)> { + if let Some((scroll_x, scroll_y)) = self.mouse_scroll { + Some((scroll_x as f32, scroll_y as f32)) + } else { + None + } + } + + pub fn get_mouse_down(&self, button: MouseButton) -> bool { + match button { + MouseButton::Left => self.mouse_state.0, + MouseButton::Middle => self.mouse_state.1, + MouseButton::Right => self.mouse_state.2, + } + } + + pub fn get_mouse_pos(&self, mode: MouseMode) -> Option<(f32, f32)> { + if let Some((mouse_x, mouse_y)) = self.mouse_pos { + mouse_handler::get_pos(mode, + mouse_x as f32, + mouse_y as f32, + self.window_scale as f32, + self.buffer_width as f32 * self.window_scale as f32, + self.buffer_height as f32 * self.window_scale as f32) + } else { + None + } + } + + pub fn get_unscaled_mouse_pos(&self, mode: MouseMode) -> Option<(f32, f32)> { + if let Some((mouse_x, mouse_y)) = self.mouse_pos { + mouse_handler::get_pos(mode, + mouse_x as f32, + mouse_y as f32, + 1.0 as f32, + self.buffer_width as f32 * self.window_scale as f32, + self.buffer_height as f32 * self.window_scale as f32) + } else { + None + } + } + + pub fn set_cursor_style(&mut self, _cursor: CursorStyle) { + // Orbital doesn't support cursor styles yet + } + + pub fn get_keys(&self) -> Option> { + self.key_handler.get_keys() + } + + pub fn get_keys_pressed(&self, repeat: KeyRepeat) -> Option> { + self.key_handler.get_keys_pressed(repeat) + } + + pub fn is_key_down(&self, key: Key) -> bool { + self.key_handler.is_key_down(key) + } + + pub fn set_key_repeat_delay(&mut self, delay: f32) { + self.key_handler.set_key_repeat_delay(delay) + } + + pub fn set_key_repeat_rate(&mut self, rate: f32) { + self.key_handler.set_key_repeat_rate(rate) + } + + pub fn is_key_pressed(&self, key: Key, repeat: KeyRepeat) -> bool { + self.key_handler.is_key_pressed(key, repeat) + } + + pub fn set_input_callback(&mut self, callback: Box) { + self.key_handler.set_input_callback(callback) + } + + pub fn is_open(&self) -> bool { + self.is_open + } + + pub fn is_active(&mut self) -> bool { + self.is_active + } + + fn process_events(&mut self) { + self.mouse_scroll = None; + + for event in self.window.events() { + match event.to_option() { + orbclient::EventOption::Key(key_event) => { + let key_opt = self.map_key_to_minifb(key_event.scancode); + if let Some(key) = key_opt { + self.key_handler.set_key_state(key, key_event.pressed); + } + }, + orbclient::EventOption::Mouse(mouse_event) => { + self.mouse_pos = Some((mouse_event.x, mouse_event.y)); + }, + orbclient::EventOption::Button(button_event) => { + self.mouse_state = (button_event.left, button_event.middle, button_event.right); + }, + orbclient::EventOption::Quit(_) => { + self.is_open = false; + }, + orbclient::EventOption::Focus(focus_event) => { + self.is_active = focus_event.focused; + if ! self.is_active { + self.mouse_pos = None; + } + }, + orbclient::EventOption::Resize(_) => { + // The window must be redrawn with an opaque color (black) + // because Orbital uses the transparent color for the new areas + // appearing after resizing + self.window.set(orbclient::Color::rgb(0, 0, 0)); + }, + orbclient::EventOption::Scroll(scroll_event) => { + self.mouse_pos = Some((scroll_event.x, scroll_event.y)); + }, + _ => { }, + } + } + } + + /// Maps Orbital scancodes to MiniFB Key enums + fn map_key_to_minifb(&self, scancode: u8) -> Option { + match scancode { + orbclient::K_0 => Some(Key::Key0), + orbclient::K_1 => Some(Key::Key1), + orbclient::K_2 => Some(Key::Key2), + orbclient::K_3 => Some(Key::Key3), + orbclient::K_4 => Some(Key::Key4), + orbclient::K_5 => Some(Key::Key5), + orbclient::K_6 => Some(Key::Key6), + orbclient::K_7 => Some(Key::Key7), + orbclient::K_8 => Some(Key::Key8), + orbclient::K_9 => Some(Key::Key9), + orbclient::K_A => Some(Key::A), + orbclient::K_B => Some(Key::B), + orbclient::K_C => Some(Key::C), + orbclient::K_D => Some(Key::D), + orbclient::K_E => Some(Key::E), + orbclient::K_F => Some(Key::F), + orbclient::K_G => Some(Key::G), + orbclient::K_H => Some(Key::H), + orbclient::K_I => Some(Key::I), + orbclient::K_J => Some(Key::J), + orbclient::K_K => Some(Key::K), + orbclient::K_L => Some(Key::L), + orbclient::K_M => Some(Key::M), + orbclient::K_N => Some(Key::N), + orbclient::K_O => Some(Key::O), + orbclient::K_P => Some(Key::P), + orbclient::K_Q => Some(Key::Q), + orbclient::K_R => Some(Key::R), + orbclient::K_S => Some(Key::S), + orbclient::K_T => Some(Key::T), + orbclient::K_U => Some(Key::U), + orbclient::K_V => Some(Key::V), + orbclient::K_W => Some(Key::W), + orbclient::K_X => Some(Key::X), + orbclient::K_Y => Some(Key::Y), + orbclient::K_Z => Some(Key::Z), + orbclient::K_F1 => Some(Key::F1), + orbclient::K_F2 => Some(Key::F2), + orbclient::K_F3 => Some(Key::F3), + orbclient::K_F4 => Some(Key::F4), + orbclient::K_F5 => Some(Key::F5), + orbclient::K_F6 => Some(Key::F6), + orbclient::K_F7 => Some(Key::F7), + orbclient::K_F8 => Some(Key::F8), + orbclient::K_F9 => Some(Key::F9), + orbclient::K_F10 => Some(Key::F10), + orbclient::K_F11 => Some(Key::F11), + orbclient::K_F12 => Some(Key::F12), + orbclient::K_DOWN => Some(Key::Down), + orbclient::K_LEFT => Some(Key::Left), + orbclient::K_RIGHT => Some(Key::Right), + orbclient::K_UP => Some(Key::Up), + orbclient::K_TICK => Some(Key::Apostrophe), + orbclient::K_BACKSLASH => Some(Key::Backslash), + orbclient::K_COMMA => Some(Key::Comma), + orbclient::K_EQUALS => Some(Key::Equal), + orbclient::K_BRACE_OPEN => Some(Key::LeftBracket), + orbclient::K_MINUS => Some(Key::Minus), + orbclient::K_PERIOD => Some(Key::Period), + orbclient::K_BRACE_CLOSE => Some(Key::RightBracket), + orbclient::K_SEMICOLON => Some(Key::Semicolon), + orbclient::K_SLASH => Some(Key::Slash), + orbclient::K_BKSP => Some(Key::Backspace), + orbclient::K_DEL => Some(Key::Delete), + orbclient::K_END => Some(Key::End), + orbclient::K_ENTER => Some(Key::Enter), + orbclient::K_ESC => Some(Key::Escape), + orbclient::K_HOME => Some(Key::Home), + orbclient::K_PGDN => Some(Key::PageDown), + orbclient::K_PGUP => Some(Key::PageUp), + orbclient::K_SPACE => Some(Key::Space), + orbclient::K_TAB => Some(Key::Tab), + orbclient::K_CAPS => Some(Key::CapsLock), + orbclient::K_LEFT_SHIFT => Some(Key::LeftShift), + orbclient::K_RIGHT_SHIFT => Some(Key::RightShift), + orbclient::K_CTRL => Some(Key::LeftCtrl), + orbclient::K_ALT => Some(Key::LeftAlt), + _ => { + println!("Unknown Orbital scancode 0x{:2x}", scancode); + None + }, + } + } + + /// Renders the given pixel data into the Orbital window + fn render_buffer(&mut self, buffer: &[u32]) { + let render_width = cmp::min(self.buffer_width * self.window_scale, + self.window.width() as usize); + let render_height = cmp::min(self.buffer_height * self.window_scale, + self.window.height() as usize); + + for y in 0..render_height { + for x in 0..render_width { + let buffer_x = x / self.window_scale; + let buffer_y = y / self.window_scale; + + // The pixel format of MiniFB and Orbital are the same (0xAARRGGBB), + // however Orbital expects opaque colors where the alpha is 0xFF + let data = buffer[buffer_y * self.buffer_width + buffer_x]; + let color = orbclient::Color { data: data | 0xFF000000 }; + + self.window.pixel(x as i32, y as i32, color); + } + } + } + + fn next_menu_handle(&mut self) -> MenuHandle { + let handle = self.menu_counter; + self.menu_counter.0 += 1; + handle + } + + pub fn add_menu(&mut self, menu: &Menu) -> MenuHandle { + let handle = self.next_menu_handle(); + let mut menu = menu.internal.clone(); + menu.handle = handle; + self.menus.push(menu); + handle + } + + pub fn get_unix_menus(&self) -> Option<&Vec> { + Some(&self.menus) + } + + pub fn remove_menu(&mut self, handle: MenuHandle) { + self.menus.retain(|ref menu| menu.handle != handle); + } + + pub fn is_menu_pressed(&mut self) -> Option { + None + } +} + +pub struct Menu { + pub internal: UnixMenu, +} + +impl Menu { + pub fn new(name: &str) -> Result { + Ok(Menu { + internal: UnixMenu { + handle: MenuHandle(0), + item_counter: MenuItemHandle(0), + name: name.to_owned(), + items: Vec::new(), + } + }) + } + + pub fn add_sub_menu(&mut self, name: &str, sub_menu: &Menu) { + let handle = self.next_item_handle(); + self.internal.items.push(UnixMenuItem { + label: name.to_owned(), + handle: handle, + sub_menu: Some(Box::new(sub_menu.internal.clone())), + id: 0, + enabled: true, + key: Key::Unknown, + modifier: 0, + }); + } + + fn next_item_handle(&mut self) -> MenuItemHandle { + let handle = self.internal.item_counter; + self.internal.item_counter.0 += 1; + handle + } + + pub fn add_menu_item(&mut self, item: &MenuItem) -> MenuItemHandle { + let item_handle = self.next_item_handle(); + self.internal.items.push(UnixMenuItem { + sub_menu: None, + handle: self.internal.item_counter, + id: item.id, + label: item.label.clone(), + enabled: item.enabled, + key: item.key, + modifier: item.modifier, + }); + item_handle + } + + pub fn remove_item(&mut self, handle: &MenuItemHandle) { + self.internal.items.retain(|ref item| item.handle.0 != handle.0); + } +}