Redox OS support (#2588)

* Add Redox OS support

* Simplify control flow usage

* Apply more recommendations

* Update naming to indicate that Orbital is a platform

* Adjust import order
This commit is contained in:
Jeremy Soller 2023-01-05 06:58:08 -07:00 committed by GitHub
parent 2f52c23fa9
commit 66ca445caa
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 1476 additions and 85 deletions

View file

@ -19,6 +19,9 @@ mod platform;
#[cfg(wasm_platform)]
#[path = "web/mod.rs"]
mod platform;
#[cfg(orbital_platform)]
#[path = "orbital/mod.rs"]
mod platform;
pub use self::platform::*;
@ -59,5 +62,6 @@ impl From<Fullscreen> for RootFullscreen {
not(x11_platform),
not(wayland_platform),
not(wasm_platform),
not(orbital_platform),
))]
compile_error!("The platform you're compiling for is not supported by winit");

View file

@ -0,0 +1,709 @@
use std::{
collections::VecDeque,
mem, slice,
sync::{mpsc, Arc, Mutex},
time::Instant,
};
use orbclient::{
ButtonEvent, EventOption, FocusEvent, HoverEvent, KeyEvent, MouseEvent, MoveEvent, QuitEvent,
ResizeEvent, ScrollEvent, TextInputEvent,
};
use raw_window_handle::{OrbitalDisplayHandle, RawDisplayHandle};
use crate::{
event::{self, StartCause, VirtualKeyCode},
event_loop::{self, ControlFlow},
window::WindowId as RootWindowId,
};
use super::{
DeviceId, MonitorHandle, PlatformSpecificEventLoopAttributes, RedoxSocket, TimeSocket,
WindowId, WindowProperties,
};
fn convert_scancode(scancode: u8) -> Option<VirtualKeyCode> {
match scancode {
orbclient::K_A => Some(VirtualKeyCode::A),
orbclient::K_B => Some(VirtualKeyCode::B),
orbclient::K_C => Some(VirtualKeyCode::C),
orbclient::K_D => Some(VirtualKeyCode::D),
orbclient::K_E => Some(VirtualKeyCode::E),
orbclient::K_F => Some(VirtualKeyCode::F),
orbclient::K_G => Some(VirtualKeyCode::G),
orbclient::K_H => Some(VirtualKeyCode::H),
orbclient::K_I => Some(VirtualKeyCode::I),
orbclient::K_J => Some(VirtualKeyCode::J),
orbclient::K_K => Some(VirtualKeyCode::K),
orbclient::K_L => Some(VirtualKeyCode::L),
orbclient::K_M => Some(VirtualKeyCode::M),
orbclient::K_N => Some(VirtualKeyCode::N),
orbclient::K_O => Some(VirtualKeyCode::O),
orbclient::K_P => Some(VirtualKeyCode::P),
orbclient::K_Q => Some(VirtualKeyCode::Q),
orbclient::K_R => Some(VirtualKeyCode::R),
orbclient::K_S => Some(VirtualKeyCode::S),
orbclient::K_T => Some(VirtualKeyCode::T),
orbclient::K_U => Some(VirtualKeyCode::U),
orbclient::K_V => Some(VirtualKeyCode::V),
orbclient::K_W => Some(VirtualKeyCode::W),
orbclient::K_X => Some(VirtualKeyCode::X),
orbclient::K_Y => Some(VirtualKeyCode::Y),
orbclient::K_Z => Some(VirtualKeyCode::Z),
orbclient::K_0 => Some(VirtualKeyCode::Key0),
orbclient::K_1 => Some(VirtualKeyCode::Key1),
orbclient::K_2 => Some(VirtualKeyCode::Key2),
orbclient::K_3 => Some(VirtualKeyCode::Key3),
orbclient::K_4 => Some(VirtualKeyCode::Key4),
orbclient::K_5 => Some(VirtualKeyCode::Key5),
orbclient::K_6 => Some(VirtualKeyCode::Key6),
orbclient::K_7 => Some(VirtualKeyCode::Key7),
orbclient::K_8 => Some(VirtualKeyCode::Key8),
orbclient::K_9 => Some(VirtualKeyCode::Key9),
orbclient::K_TICK => Some(VirtualKeyCode::Grave),
orbclient::K_MINUS => Some(VirtualKeyCode::Minus),
orbclient::K_EQUALS => Some(VirtualKeyCode::Equals),
orbclient::K_BACKSLASH => Some(VirtualKeyCode::Backslash),
orbclient::K_BRACE_OPEN => Some(VirtualKeyCode::LBracket),
orbclient::K_BRACE_CLOSE => Some(VirtualKeyCode::RBracket),
orbclient::K_SEMICOLON => Some(VirtualKeyCode::Semicolon),
orbclient::K_QUOTE => Some(VirtualKeyCode::Apostrophe),
orbclient::K_COMMA => Some(VirtualKeyCode::Comma),
orbclient::K_PERIOD => Some(VirtualKeyCode::Period),
orbclient::K_SLASH => Some(VirtualKeyCode::Slash),
orbclient::K_BKSP => Some(VirtualKeyCode::Back),
orbclient::K_SPACE => Some(VirtualKeyCode::Space),
orbclient::K_TAB => Some(VirtualKeyCode::Tab),
//orbclient::K_CAPS => Some(VirtualKeyCode::CAPS),
orbclient::K_LEFT_SHIFT => Some(VirtualKeyCode::LShift),
orbclient::K_RIGHT_SHIFT => Some(VirtualKeyCode::RShift),
orbclient::K_CTRL => Some(VirtualKeyCode::LControl),
orbclient::K_ALT => Some(VirtualKeyCode::LAlt),
orbclient::K_ENTER => Some(VirtualKeyCode::Return),
orbclient::K_ESC => Some(VirtualKeyCode::Escape),
orbclient::K_F1 => Some(VirtualKeyCode::F1),
orbclient::K_F2 => Some(VirtualKeyCode::F2),
orbclient::K_F3 => Some(VirtualKeyCode::F3),
orbclient::K_F4 => Some(VirtualKeyCode::F4),
orbclient::K_F5 => Some(VirtualKeyCode::F5),
orbclient::K_F6 => Some(VirtualKeyCode::F6),
orbclient::K_F7 => Some(VirtualKeyCode::F7),
orbclient::K_F8 => Some(VirtualKeyCode::F8),
orbclient::K_F9 => Some(VirtualKeyCode::F9),
orbclient::K_F10 => Some(VirtualKeyCode::F10),
orbclient::K_HOME => Some(VirtualKeyCode::Home),
orbclient::K_UP => Some(VirtualKeyCode::Up),
orbclient::K_PGUP => Some(VirtualKeyCode::PageUp),
orbclient::K_LEFT => Some(VirtualKeyCode::Left),
orbclient::K_RIGHT => Some(VirtualKeyCode::Right),
orbclient::K_END => Some(VirtualKeyCode::End),
orbclient::K_DOWN => Some(VirtualKeyCode::Down),
orbclient::K_PGDN => Some(VirtualKeyCode::PageDown),
orbclient::K_DEL => Some(VirtualKeyCode::Delete),
orbclient::K_F11 => Some(VirtualKeyCode::F11),
orbclient::K_F12 => Some(VirtualKeyCode::F12),
_ => None,
}
}
fn element_state(pressed: bool) -> event::ElementState {
if pressed {
event::ElementState::Pressed
} else {
event::ElementState::Released
}
}
bitflags! {
#[derive(Default)]
struct KeyboardModifierState: u8 {
const LSHIFT = 1 << 0;
const RSHIFT = 1 << 1;
const LCTRL = 1 << 2;
const RCTRL = 1 << 3;
const LALT = 1 << 4;
const RALT = 1 << 5;
const LSUPER = 1 << 6;
const RSUPER = 1 << 7;
}
}
bitflags! {
#[derive(Default)]
struct MouseButtonState: u8 {
const LEFT = 1 << 0;
const MIDDLE = 1 << 1;
const RIGHT = 1 << 2;
}
}
#[derive(Default)]
struct EventState {
keyboard: KeyboardModifierState,
mouse: MouseButtonState,
resize_opt: Option<(u32, u32)>,
}
impl EventState {
fn key(&mut self, vk: VirtualKeyCode, pressed: bool) {
match vk {
VirtualKeyCode::LShift => self.keyboard.set(KeyboardModifierState::LSHIFT, pressed),
VirtualKeyCode::RShift => self.keyboard.set(KeyboardModifierState::RSHIFT, pressed),
VirtualKeyCode::LControl => self.keyboard.set(KeyboardModifierState::LCTRL, pressed),
VirtualKeyCode::RControl => self.keyboard.set(KeyboardModifierState::RCTRL, pressed),
VirtualKeyCode::LAlt => self.keyboard.set(KeyboardModifierState::LALT, pressed),
VirtualKeyCode::RAlt => self.keyboard.set(KeyboardModifierState::RALT, pressed),
VirtualKeyCode::LWin => self.keyboard.set(KeyboardModifierState::LSUPER, pressed),
VirtualKeyCode::RWin => self.keyboard.set(KeyboardModifierState::RSUPER, pressed),
_ => (),
}
}
fn mouse(
&mut self,
left: bool,
middle: bool,
right: bool,
) -> Option<(event::MouseButton, event::ElementState)> {
if self.mouse.contains(MouseButtonState::LEFT) != left {
self.mouse.set(MouseButtonState::LEFT, left);
return Some((event::MouseButton::Left, element_state(left)));
}
if self.mouse.contains(MouseButtonState::MIDDLE) != middle {
self.mouse.set(MouseButtonState::MIDDLE, middle);
return Some((event::MouseButton::Middle, element_state(middle)));
}
if self.mouse.contains(MouseButtonState::RIGHT) != right {
self.mouse.set(MouseButtonState::RIGHT, right);
return Some((event::MouseButton::Right, element_state(right)));
}
None
}
fn modifiers(&self) -> event::ModifiersState {
let mut modifiers = event::ModifiersState::empty();
if self
.keyboard
.intersects(KeyboardModifierState::LSHIFT | KeyboardModifierState::RSHIFT)
{
modifiers |= event::ModifiersState::SHIFT;
}
if self
.keyboard
.intersects(KeyboardModifierState::LCTRL | KeyboardModifierState::RCTRL)
{
modifiers |= event::ModifiersState::CTRL;
}
if self
.keyboard
.intersects(KeyboardModifierState::LALT | KeyboardModifierState::RALT)
{
modifiers |= event::ModifiersState::ALT;
}
if self
.keyboard
.intersects(KeyboardModifierState::LSUPER | KeyboardModifierState::RSUPER)
{
modifiers |= event::ModifiersState::LOGO
}
modifiers
}
}
pub struct EventLoop<T: 'static> {
windows: Vec<(Arc<RedoxSocket>, EventState)>,
window_target: event_loop::EventLoopWindowTarget<T>,
}
impl<T: 'static> EventLoop<T> {
pub(crate) fn new(_: &PlatformSpecificEventLoopAttributes) -> Self {
let (user_events_sender, user_events_receiver) = mpsc::channel();
let event_socket = Arc::new(RedoxSocket::event().unwrap());
let wake_socket = Arc::new(TimeSocket::open().unwrap());
event_socket
.write(&syscall::Event {
id: wake_socket.0.fd,
flags: syscall::EventFlags::EVENT_READ,
data: wake_socket.0.fd,
})
.unwrap();
Self {
windows: Vec::new(),
window_target: event_loop::EventLoopWindowTarget {
p: EventLoopWindowTarget {
user_events_sender,
user_events_receiver,
creates: Mutex::new(VecDeque::new()),
redraws: Arc::new(Mutex::new(VecDeque::new())),
destroys: Arc::new(Mutex::new(VecDeque::new())),
event_socket,
wake_socket,
},
_marker: std::marker::PhantomData,
},
}
}
pub fn run<F>(mut self, event_handler: F) -> !
where
F: 'static
+ FnMut(event::Event<'_, T>, &event_loop::EventLoopWindowTarget<T>, &mut ControlFlow),
{
let exit_code = self.run_return(event_handler);
::std::process::exit(exit_code);
}
fn process_event<F>(
window_id: WindowId,
event_option: EventOption,
event_state: &mut EventState,
mut event_handler: F,
) where
F: FnMut(event::Event<'_, T>),
{
match event_option {
EventOption::Key(KeyEvent {
character: _,
scancode,
pressed,
}) => {
if scancode != 0 {
let vk_opt = convert_scancode(scancode);
if let Some(vk) = vk_opt {
event_state.key(vk, pressed);
}
event_handler(
#[allow(deprecated)]
event::Event::WindowEvent {
window_id: RootWindowId(window_id),
event: event::WindowEvent::KeyboardInput {
device_id: event::DeviceId(DeviceId),
input: event::KeyboardInput {
scancode: scancode as u32,
state: element_state(pressed),
virtual_keycode: vk_opt,
modifiers: event_state.modifiers(),
},
is_synthetic: false,
},
},
);
}
}
EventOption::TextInput(TextInputEvent { character }) => {
event_handler(event::Event::WindowEvent {
window_id: RootWindowId(window_id),
event: event::WindowEvent::ReceivedCharacter(character),
});
}
EventOption::Mouse(MouseEvent { x, y }) => {
event_handler(event::Event::WindowEvent {
window_id: RootWindowId(window_id),
event: event::WindowEvent::CursorMoved {
device_id: event::DeviceId(DeviceId),
position: (x, y).into(),
modifiers: event_state.modifiers(),
},
});
}
EventOption::Button(ButtonEvent {
left,
middle,
right,
}) => {
while let Some((button, state)) = event_state.mouse(left, middle, right) {
event_handler(event::Event::WindowEvent {
window_id: RootWindowId(window_id),
event: event::WindowEvent::MouseInput {
device_id: event::DeviceId(DeviceId),
state,
button,
modifiers: event_state.modifiers(),
},
});
}
}
EventOption::Scroll(ScrollEvent { x, y }) => {
event_handler(event::Event::WindowEvent {
window_id: RootWindowId(window_id),
event: event::WindowEvent::MouseWheel {
device_id: event::DeviceId(DeviceId),
delta: event::MouseScrollDelta::LineDelta(x as f32, y as f32),
phase: event::TouchPhase::Moved,
modifiers: event_state.modifiers(),
},
});
}
EventOption::Quit(QuitEvent {}) => {
event_handler(event::Event::WindowEvent {
window_id: RootWindowId(window_id),
event: event::WindowEvent::CloseRequested,
});
}
EventOption::Focus(FocusEvent { focused }) => {
event_handler(event::Event::WindowEvent {
window_id: RootWindowId(window_id),
event: event::WindowEvent::Focused(focused),
});
}
EventOption::Move(MoveEvent { x, y }) => {
event_handler(event::Event::WindowEvent {
window_id: RootWindowId(window_id),
event: event::WindowEvent::Moved((x, y).into()),
});
}
EventOption::Resize(ResizeEvent { width, height }) => {
event_handler(event::Event::WindowEvent {
window_id: RootWindowId(window_id),
event: event::WindowEvent::Resized((width, height).into()),
});
// Acknowledge resize after event loop.
event_state.resize_opt = Some((width, height));
}
//TODO: Clipboard
EventOption::Hover(HoverEvent { entered }) => {
if entered {
event_handler(event::Event::WindowEvent {
window_id: RootWindowId(window_id),
event: event::WindowEvent::CursorEntered {
device_id: event::DeviceId(DeviceId),
},
});
} else {
event_handler(event::Event::WindowEvent {
window_id: RootWindowId(window_id),
event: event::WindowEvent::CursorLeft {
device_id: event::DeviceId(DeviceId),
},
});
}
}
other => {
warn!("unhandled event: {:?}", other);
}
}
}
pub fn run_return<F>(&mut self, mut event_handler_inner: F) -> i32
where
F: FnMut(event::Event<'_, T>, &event_loop::EventLoopWindowTarget<T>, &mut ControlFlow),
{
// Wrapper for event handler function that prevents ExitWithCode from being unset.
let mut event_handler =
move |event: event::Event<'_, T>,
window_target: &event_loop::EventLoopWindowTarget<T>,
control_flow: &mut ControlFlow| {
if let ControlFlow::ExitWithCode(code) = control_flow {
event_handler_inner(
event,
window_target,
&mut ControlFlow::ExitWithCode(*code),
);
} else {
event_handler_inner(event, window_target, control_flow);
}
};
let mut control_flow = ControlFlow::default();
let mut start_cause = StartCause::Init;
let code = loop {
event_handler(
event::Event::NewEvents(start_cause),
&self.window_target,
&mut control_flow,
);
if start_cause == StartCause::Init {
event_handler(
event::Event::Resumed,
&self.window_target,
&mut control_flow,
);
}
// Handle window creates.
while let Some(window) = {
let mut creates = self.window_target.p.creates.lock().unwrap();
creates.pop_front()
} {
let window_id = WindowId {
fd: window.fd as u64,
};
let mut buf: [u8; 4096] = [0; 4096];
let path = window.fpath(&mut buf).expect("failed to read properties");
let properties = WindowProperties::new(path);
self.windows.push((window, EventState::default()));
// Send resize event on create to indicate first size.
event_handler(
event::Event::WindowEvent {
window_id: RootWindowId(window_id),
event: event::WindowEvent::Resized((properties.w, properties.h).into()),
},
&self.window_target,
&mut control_flow,
);
// Send resize event on create to indicate first position.
event_handler(
event::Event::WindowEvent {
window_id: RootWindowId(window_id),
event: event::WindowEvent::Moved((properties.x, properties.y).into()),
},
&self.window_target,
&mut control_flow,
);
}
// Handle window destroys.
while let Some(destroy_id) = {
let mut destroys = self.window_target.p.destroys.lock().unwrap();
destroys.pop_front()
} {
event_handler(
event::Event::WindowEvent {
window_id: RootWindowId(destroy_id),
event: event::WindowEvent::Destroyed,
},
&self.window_target,
&mut control_flow,
);
self.windows
.retain(|(window, _event_state)| window.fd as u64 != destroy_id.fd);
}
// Handle window events.
let mut i = 0;
// While loop is used here because the same window may be processed more than once.
while let Some((window, event_state)) = self.windows.get_mut(i) {
let window_id = WindowId {
fd: window.fd as u64,
};
let mut event_buf = [0u8; 16 * mem::size_of::<orbclient::Event>()];
let count =
syscall::read(window.fd, &mut event_buf).expect("failed to read window events");
// Safety: orbclient::Event is a packed struct designed to be transferred over a socket.
let events = unsafe {
slice::from_raw_parts(
event_buf.as_ptr() as *const orbclient::Event,
count / mem::size_of::<orbclient::Event>(),
)
};
for orbital_event in events {
Self::process_event(
window_id,
orbital_event.to_option(),
event_state,
|event| event_handler(event, &self.window_target, &mut control_flow),
);
}
if count == event_buf.len() {
// If event buf was full, process same window again to ensure all events are drained.
continue;
}
// Acknowledge the latest resize event.
if let Some((w, h)) = event_state.resize_opt.take() {
window
.write(format!("S,{},{}", w, h).as_bytes())
.expect("failed to acknowledge resize");
// Require redraw after resize.
let mut redraws = self.window_target.p.redraws.lock().unwrap();
if !redraws.contains(&window_id) {
redraws.push_back(window_id);
}
}
// Move to next window.
i += 1;
}
while let Ok(event) = self.window_target.p.user_events_receiver.try_recv() {
event_handler(
event::Event::UserEvent(event),
&self.window_target,
&mut control_flow,
);
}
event_handler(
event::Event::MainEventsCleared,
&self.window_target,
&mut control_flow,
);
// To avoid deadlocks the redraws lock is not held during event processing.
while let Some(window_id) = {
let mut redraws = self.window_target.p.redraws.lock().unwrap();
redraws.pop_front()
} {
event_handler(
event::Event::RedrawRequested(RootWindowId(window_id)),
&self.window_target,
&mut control_flow,
);
}
event_handler(
event::Event::RedrawEventsCleared,
&self.window_target,
&mut control_flow,
);
let requested_resume = match control_flow {
ControlFlow::Poll => {
start_cause = StartCause::Poll;
continue;
}
ControlFlow::Wait => None,
ControlFlow::WaitUntil(instant) => Some(instant),
ControlFlow::ExitWithCode(code) => break code,
};
// Re-using wake socket caused extra wake events before because there were leftover
// timeouts, and then new timeouts were added each time a spurious timeout expired.
let timeout_socket = TimeSocket::open().unwrap();
self.window_target
.p
.event_socket
.write(&syscall::Event {
id: timeout_socket.0.fd,
flags: syscall::EventFlags::EVENT_READ,
data: 0,
})
.unwrap();
let start = Instant::now();
if let Some(instant) = requested_resume {
let mut time = timeout_socket.current_time().unwrap();
if let Some(duration) = instant.checked_duration_since(start) {
time.tv_sec += duration.as_secs() as i64;
time.tv_nsec += duration.subsec_nanos() as i32;
// Normalize timespec so tv_nsec is not greater than one second.
while time.tv_nsec >= 1_000_000_000 {
time.tv_sec += 1;
time.tv_nsec -= 1_000_000_000;
}
}
timeout_socket.timeout(&time).unwrap();
}
// Wait for event if needed.
let mut event = syscall::Event::default();
self.window_target.p.event_socket.read(&mut event).unwrap();
// TODO: handle spurious wakeups (redraw caused wakeup but redraw already handled)
match requested_resume {
Some(requested_resume) if event.id == timeout_socket.0.fd => {
// If the event is from the special timeout socket, report that resume
// time was reached.
start_cause = StartCause::ResumeTimeReached {
start,
requested_resume,
};
}
_ => {
// Normal window event or spurious timeout.
start_cause = StartCause::WaitCancelled {
start,
requested_resume,
};
}
}
};
event_handler(
event::Event::LoopDestroyed,
&self.window_target,
&mut control_flow,
);
code
}
pub fn window_target(&self) -> &event_loop::EventLoopWindowTarget<T> {
&self.window_target
}
pub fn create_proxy(&self) -> EventLoopProxy<T> {
EventLoopProxy {
user_events_sender: self.window_target.p.user_events_sender.clone(),
wake_socket: self.window_target.p.wake_socket.clone(),
}
}
}
pub struct EventLoopProxy<T: 'static> {
user_events_sender: mpsc::Sender<T>,
wake_socket: Arc<TimeSocket>,
}
impl<T> EventLoopProxy<T> {
pub fn send_event(&self, event: T) -> Result<(), event_loop::EventLoopClosed<T>> {
self.user_events_sender
.send(event)
.map_err(|mpsc::SendError(x)| event_loop::EventLoopClosed(x))?;
self.wake_socket.wake().unwrap();
Ok(())
}
}
impl<T> Clone for EventLoopProxy<T> {
fn clone(&self) -> Self {
Self {
user_events_sender: self.user_events_sender.clone(),
wake_socket: self.wake_socket.clone(),
}
}
}
impl<T> Unpin for EventLoopProxy<T> {}
pub struct EventLoopWindowTarget<T: 'static> {
pub(super) user_events_sender: mpsc::Sender<T>,
pub(super) user_events_receiver: mpsc::Receiver<T>,
pub(super) creates: Mutex<VecDeque<Arc<RedoxSocket>>>,
pub(super) redraws: Arc<Mutex<VecDeque<WindowId>>>,
pub(super) destroys: Arc<Mutex<VecDeque<WindowId>>>,
pub(super) event_socket: Arc<RedoxSocket>,
pub(super) wake_socket: Arc<TimeSocket>,
}
impl<T: 'static> EventLoopWindowTarget<T> {
pub fn primary_monitor(&self) -> Option<MonitorHandle> {
Some(MonitorHandle)
}
pub fn available_monitors(&self) -> VecDeque<MonitorHandle> {
let mut v = VecDeque::with_capacity(1);
v.push_back(MonitorHandle);
v
}
pub fn raw_display_handle(&self) -> RawDisplayHandle {
RawDisplayHandle::Orbital(OrbitalDisplayHandle::empty())
}
}

View file

@ -0,0 +1,253 @@
#![cfg(target_os = "redox")]
use std::str;
use crate::dpi::{PhysicalPosition, PhysicalSize};
pub use self::event_loop::{EventLoop, EventLoopProxy, EventLoopWindowTarget};
mod event_loop;
pub use self::window::Window;
mod window;
struct RedoxSocket {
fd: usize,
}
impl RedoxSocket {
fn event() -> syscall::Result<Self> {
Self::open_raw("event:")
}
fn orbital(properties: &WindowProperties<'_>) -> syscall::Result<Self> {
Self::open_raw(&format!("{}", properties))
}
// Paths should be checked to ensure they are actually sockets and not normal files. If a
// non-socket path is used, it could cause read and write to not function as expected. For
// example, the seek would change in a potentially unpredictable way if either read or write
// were called at the same time by multiple threads.
fn open_raw(path: &str) -> syscall::Result<Self> {
let fd = syscall::open(path, syscall::O_RDWR | syscall::O_CLOEXEC)?;
Ok(Self { fd })
}
fn read(&self, buf: &mut [u8]) -> syscall::Result<()> {
let count = syscall::read(self.fd, buf)?;
if count == buf.len() {
Ok(())
} else {
Err(syscall::Error::new(syscall::EINVAL))
}
}
fn write(&self, buf: &[u8]) -> syscall::Result<()> {
let count = syscall::write(self.fd, buf)?;
if count == buf.len() {
Ok(())
} else {
Err(syscall::Error::new(syscall::EINVAL))
}
}
fn fpath<'a>(&self, buf: &'a mut [u8]) -> syscall::Result<&'a str> {
let count = syscall::fpath(self.fd, buf)?;
str::from_utf8(&buf[..count]).map_err(|_err| syscall::Error::new(syscall::EINVAL))
}
}
impl Drop for RedoxSocket {
fn drop(&mut self) {
let _ = syscall::close(self.fd);
}
}
pub struct TimeSocket(RedoxSocket);
impl TimeSocket {
fn open() -> syscall::Result<Self> {
RedoxSocket::open_raw("time:4").map(Self)
}
// Read current time.
fn current_time(&self) -> syscall::Result<syscall::TimeSpec> {
let mut timespec = syscall::TimeSpec::default();
self.0.read(&mut timespec)?;
Ok(timespec)
}
// Write a timeout.
fn timeout(&self, timespec: &syscall::TimeSpec) -> syscall::Result<()> {
self.0.write(timespec)
}
// Wake immediately.
fn wake(&self) -> syscall::Result<()> {
// Writing a default TimeSpec will always trigger a time event.
self.timeout(&syscall::TimeSpec::default())
}
}
#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub(crate) struct PlatformSpecificEventLoopAttributes {}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct WindowId {
fd: u64,
}
impl WindowId {
pub const fn dummy() -> Self {
WindowId {
fd: u64::max_value(),
}
}
}
impl From<WindowId> for u64 {
fn from(id: WindowId) -> Self {
id.fd
}
}
impl From<u64> for WindowId {
fn from(fd: u64) -> Self {
Self { fd }
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct DeviceId;
impl DeviceId {
pub const fn dummy() -> Self {
DeviceId
}
}
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
pub struct PlatformSpecificWindowBuilderAttributes;
struct WindowProperties<'a> {
flags: &'a str,
x: i32,
y: i32,
w: u32,
h: u32,
title: &'a str,
}
impl<'a> WindowProperties<'a> {
fn new(path: &'a str) -> Self {
// orbital:flags/x/y/w/h/t
let mut parts = path.splitn(6, '/');
let flags = parts.next().unwrap_or("");
let x = parts
.next()
.map_or(0, |part| part.parse::<i32>().unwrap_or(0));
let y = parts
.next()
.map_or(0, |part| part.parse::<i32>().unwrap_or(0));
let w = parts
.next()
.map_or(0, |part| part.parse::<u32>().unwrap_or(0));
let h = parts
.next()
.map_or(0, |part| part.parse::<u32>().unwrap_or(0));
let title = parts.next().unwrap_or("");
Self {
flags,
x,
y,
w,
h,
title,
}
}
}
impl<'a> fmt::Display for WindowProperties<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"orbital:{}/{}/{}/{}/{}/{}",
self.flags, self.x, self.y, self.w, self.h, self.title
)
}
}
#[derive(Default, Clone, Debug)]
pub struct OsError;
use std::fmt::{self, Display, Formatter};
impl Display for OsError {
fn fmt(&self, fmt: &mut Formatter<'_>) -> Result<(), fmt::Error> {
write!(fmt, "Redox OS Error")
}
}
pub(crate) use crate::icon::NoIcon as PlatformIcon;
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct MonitorHandle;
impl MonitorHandle {
pub fn name(&self) -> Option<String> {
Some("Redox Device".to_owned())
}
pub fn size(&self) -> PhysicalSize<u32> {
PhysicalSize::new(0, 0) // TODO
}
pub fn position(&self) -> PhysicalPosition<i32> {
(0, 0).into()
}
pub fn scale_factor(&self) -> f64 {
1.0 // TODO
}
pub fn refresh_rate_millihertz(&self) -> Option<u32> {
// FIXME no way to get real refresh rate for now.
None
}
pub fn video_modes(&self) -> impl Iterator<Item = VideoMode> {
let size = self.size().into();
// FIXME this is not the real refresh rate
// (it is guaranteed to support 32 bit color though)
std::iter::once(VideoMode {
size,
bit_depth: 32,
refresh_rate_millihertz: 60000,
monitor: self.clone(),
})
}
}
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct VideoMode {
size: (u32, u32),
bit_depth: u16,
refresh_rate_millihertz: u32,
monitor: MonitorHandle,
}
impl VideoMode {
pub fn size(&self) -> PhysicalSize<u32> {
self.size.into()
}
pub fn bit_depth(&self) -> u16 {
self.bit_depth
}
pub fn refresh_rate_millihertz(&self) -> u32 {
self.refresh_rate_millihertz
}
pub fn monitor(&self) -> MonitorHandle {
self.monitor.clone()
}
}

View file

@ -0,0 +1,396 @@
use std::{
collections::VecDeque,
sync::{Arc, Mutex},
};
use raw_window_handle::{
OrbitalDisplayHandle, OrbitalWindowHandle, RawDisplayHandle, RawWindowHandle,
};
use crate::{
dpi::{PhysicalPosition, PhysicalSize, Position, Size},
error,
platform_impl::Fullscreen,
window,
};
use super::{
EventLoopWindowTarget, MonitorHandle, PlatformSpecificWindowBuilderAttributes, RedoxSocket,
TimeSocket, WindowId, WindowProperties,
};
// These values match the values uses in the `window_new` function in orbital:
// https://gitlab.redox-os.org/redox-os/orbital/-/blob/master/src/scheme.rs
const ORBITAL_FLAG_ASYNC: char = 'a';
const ORBITAL_FLAG_BACK: char = 'b';
const ORBITAL_FLAG_FRONT: char = 'f';
const ORBITAL_FLAG_BORDERLESS: char = 'l';
const ORBITAL_FLAG_RESIZABLE: char = 'r';
const ORBITAL_FLAG_TRANSPARENT: char = 't';
pub struct Window {
window_socket: Arc<RedoxSocket>,
redraws: Arc<Mutex<VecDeque<WindowId>>>,
destroys: Arc<Mutex<VecDeque<WindowId>>>,
wake_socket: Arc<TimeSocket>,
}
impl Window {
pub(crate) fn new<T: 'static>(
el: &EventLoopWindowTarget<T>,
attrs: window::WindowAttributes,
_: PlatformSpecificWindowBuilderAttributes,
) -> Result<Self, error::OsError> {
let scale = MonitorHandle.scale_factor();
let (x, y) = if let Some(pos) = attrs.position {
pos.to_physical::<i32>(scale).into()
} else {
// These coordinates are a special value to center the window.
(-1, -1)
};
let (w, h): (u32, u32) = if let Some(size) = attrs.inner_size {
size.to_physical::<u32>(scale).into()
} else {
(1024, 768)
};
//TODO: min/max inner_size
// Async by default.
let mut flag_str = ORBITAL_FLAG_ASYNC.to_string();
if attrs.resizable {
flag_str.push(ORBITAL_FLAG_RESIZABLE);
}
//TODO: maximized, fullscreen, visible
if attrs.transparent {
flag_str.push(ORBITAL_FLAG_TRANSPARENT);
}
if !attrs.decorations {
flag_str.push(ORBITAL_FLAG_BORDERLESS);
}
match attrs.window_level {
window::WindowLevel::AlwaysOnBottom => {
flag_str.push(ORBITAL_FLAG_BACK);
}
window::WindowLevel::Normal => {}
window::WindowLevel::AlwaysOnTop => {
flag_str.push(ORBITAL_FLAG_FRONT);
}
}
//TODO: window_icon
// Open window.
let window = RedoxSocket::orbital(&WindowProperties {
flags: &flag_str,
x,
y,
w,
h,
title: &attrs.title,
})
.expect("failed to open window");
// Add to event socket.
el.event_socket
.write(&syscall::Event {
id: window.fd,
flags: syscall::EventFlags::EVENT_READ,
data: window.fd,
})
.unwrap();
let window_socket = Arc::new(window);
// Notify event thread that this window was created, it will send some default events.
{
let mut creates = el.creates.lock().unwrap();
creates.push_back(window_socket.clone());
}
el.wake_socket.wake().unwrap();
Ok(Self {
window_socket,
redraws: el.redraws.clone(),
destroys: el.destroys.clone(),
wake_socket: el.wake_socket.clone(),
})
}
#[inline]
pub fn id(&self) -> WindowId {
WindowId {
fd: self.window_socket.fd as u64,
}
}
#[inline]
pub fn primary_monitor(&self) -> Option<MonitorHandle> {
Some(MonitorHandle)
}
#[inline]
pub fn available_monitors(&self) -> VecDeque<MonitorHandle> {
let mut v = VecDeque::with_capacity(1);
v.push_back(MonitorHandle);
v
}
#[inline]
pub fn current_monitor(&self) -> Option<MonitorHandle> {
Some(MonitorHandle)
}
#[inline]
pub fn scale_factor(&self) -> f64 {
MonitorHandle.scale_factor()
}
#[inline]
pub fn request_redraw(&self) {
let window_id = self.id();
let mut redraws = self.redraws.lock().unwrap();
if !redraws.contains(&window_id) {
redraws.push_back(window_id);
self.wake_socket.wake().unwrap();
}
}
#[inline]
pub fn inner_position(&self) -> Result<PhysicalPosition<i32>, error::NotSupportedError> {
let mut buf: [u8; 4096] = [0; 4096];
let path = self
.window_socket
.fpath(&mut buf)
.expect("failed to read properties");
let properties = WindowProperties::new(path);
Ok((properties.x, properties.y).into())
}
#[inline]
pub fn outer_position(&self) -> Result<PhysicalPosition<i32>, error::NotSupportedError> {
//TODO: adjust for window decorations
self.inner_position()
}
#[inline]
pub fn set_outer_position(&self, position: Position) {
//TODO: adjust for window decorations
let (x, y): (i32, i32) = position.to_physical::<i32>(self.scale_factor()).into();
self.window_socket
.write(format!("P,{},{}", x, y).as_bytes())
.expect("failed to set position");
}
#[inline]
pub fn inner_size(&self) -> PhysicalSize<u32> {
let mut buf: [u8; 4096] = [0; 4096];
let path = self
.window_socket
.fpath(&mut buf)
.expect("failed to read properties");
let properties = WindowProperties::new(path);
(properties.w, properties.h).into()
}
#[inline]
pub fn set_inner_size(&self, size: Size) {
let (w, h): (u32, u32) = size.to_physical::<u32>(self.scale_factor()).into();
self.window_socket
.write(format!("S,{},{}", w, h).as_bytes())
.expect("failed to set size");
}
#[inline]
pub fn outer_size(&self) -> PhysicalSize<u32> {
//TODO: adjust for window decorations
self.inner_size()
}
#[inline]
pub fn set_min_inner_size(&self, _: Option<Size>) {}
#[inline]
pub fn set_max_inner_size(&self, _: Option<Size>) {}
#[inline]
pub fn title(&self) -> String {
let mut buf: [u8; 4096] = [0; 4096];
let path = self
.window_socket
.fpath(&mut buf)
.expect("failed to read properties");
let properties = WindowProperties::new(path);
properties.title.to_string()
}
#[inline]
pub fn set_title(&self, title: &str) {
self.window_socket
.write(format!("T,{}", title).as_bytes())
.expect("failed to set title");
}
#[inline]
pub fn set_visible(&self, _visibility: bool) {}
#[inline]
pub fn is_visible(&self) -> Option<bool> {
None
}
#[inline]
pub fn resize_increments(&self) -> Option<PhysicalSize<u32>> {
None
}
#[inline]
pub fn set_resize_increments(&self, _increments: Option<Size>) {}
#[inline]
pub fn set_resizable(&self, _resizeable: bool) {}
#[inline]
pub fn is_resizable(&self) -> bool {
let mut buf: [u8; 4096] = [0; 4096];
let path = self
.window_socket
.fpath(&mut buf)
.expect("failed to read properties");
let properties = WindowProperties::new(path);
properties.flags.contains(ORBITAL_FLAG_RESIZABLE)
}
#[inline]
pub fn set_minimized(&self, _minimized: bool) {}
#[inline]
pub fn set_maximized(&self, _maximized: bool) {}
#[inline]
pub fn is_maximized(&self) -> bool {
false
}
#[inline]
pub(crate) fn set_fullscreen(&self, _monitor: Option<Fullscreen>) {}
#[inline]
pub(crate) fn fullscreen(&self) -> Option<Fullscreen> {
None
}
#[inline]
pub fn set_decorations(&self, _decorations: bool) {}
#[inline]
pub fn is_decorated(&self) -> bool {
let mut buf: [u8; 4096] = [0; 4096];
let path = self
.window_socket
.fpath(&mut buf)
.expect("failed to read properties");
let properties = WindowProperties::new(path);
!properties.flags.contains(ORBITAL_FLAG_BORDERLESS)
}
#[inline]
pub fn set_window_level(&self, _level: window::WindowLevel) {}
#[inline]
pub fn set_window_icon(&self, _window_icon: Option<crate::icon::Icon>) {}
#[inline]
pub fn set_ime_position(&self, _position: Position) {}
#[inline]
pub fn set_ime_allowed(&self, _allowed: bool) {}
#[inline]
pub fn focus_window(&self) {}
#[inline]
pub fn request_user_attention(&self, _request_type: Option<window::UserAttentionType>) {}
#[inline]
pub fn set_cursor_icon(&self, _: window::CursorIcon) {}
#[inline]
pub fn set_cursor_position(&self, _: Position) -> Result<(), error::ExternalError> {
Err(error::ExternalError::NotSupported(
error::NotSupportedError::new(),
))
}
#[inline]
pub fn set_cursor_grab(&self, _: window::CursorGrabMode) -> Result<(), error::ExternalError> {
Err(error::ExternalError::NotSupported(
error::NotSupportedError::new(),
))
}
#[inline]
pub fn set_cursor_visible(&self, _: bool) {}
#[inline]
pub fn drag_window(&self) -> Result<(), error::ExternalError> {
Err(error::ExternalError::NotSupported(
error::NotSupportedError::new(),
))
}
#[inline]
pub fn set_cursor_hittest(&self, _hittest: bool) -> Result<(), error::ExternalError> {
Err(error::ExternalError::NotSupported(
error::NotSupportedError::new(),
))
}
#[inline]
pub fn raw_window_handle(&self) -> RawWindowHandle {
let mut handle = OrbitalWindowHandle::empty();
handle.window = self.window_socket.fd as *mut _;
RawWindowHandle::Orbital(handle)
}
#[inline]
pub fn raw_display_handle(&self) -> RawDisplayHandle {
RawDisplayHandle::Orbital(OrbitalDisplayHandle::empty())
}
#[inline]
pub fn set_enabled_buttons(&self, _buttons: window::WindowButtons) {}
#[inline]
pub fn enabled_buttons(&self) -> window::WindowButtons {
window::WindowButtons::all()
}
#[inline]
pub fn theme(&self) -> Option<window::Theme> {
None
}
#[inline]
pub fn set_theme(&self, _theme: Option<window::Theme>) {}
}
impl Drop for Window {
fn drop(&mut self) {
{
let mut destroys = self.destroys.lock().unwrap();
destroys.push_back(self.id());
}
self.wake_socket.wake().unwrap();
}
}