Redox support (#35)

This commit is contained in:
xTibor 2017-04-24 09:09:05 +02:00 committed by Daniel Collin
parent 4d0aa028c2
commit f4bd8b7704
4 changed files with 444 additions and 3 deletions

View file

@ -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"

View file

@ -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<UnixMenu>> {
self.0.get_unix_menus()
}

View file

@ -8,3 +8,5 @@ pub mod windows;
target_os="netbsd",
target_os="openbsd"))]
pub mod unix;
#[cfg(target_os = "redox")]
pub mod redox;

434
src/os/redox/mod.rs Normal file
View file

@ -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<UnixMenu>,
}
impl Window {
pub fn new(name: &str, width: usize, height: usize, opts: WindowOptions) -> Result<Window> {
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<Vec<Key>> {
self.key_handler.get_keys()
}
pub fn get_keys_pressed(&self, repeat: KeyRepeat) -> Option<Vec<Key>> {
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<InputCallback>) {
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<Key> {
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<UnixMenu>> {
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<usize> {
None
}
}
pub struct Menu {
pub internal: UnixMenu,
}
impl Menu {
pub fn new(name: &str) -> Result<Menu> {
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);
}
}