Split Orbital backend out into winit-orbital (#4243)
This commit is contained in:
parent
e2b883d215
commit
47b938dbe7
11 changed files with 61 additions and 33 deletions
|
|
@ -9,7 +9,7 @@ pub mod ios;
|
|||
#[cfg(macos_platform)]
|
||||
pub mod macos;
|
||||
#[cfg(orbital_platform)]
|
||||
pub mod orbital;
|
||||
pub use winit_orbital as orbital;
|
||||
#[cfg(any(x11_platform, wayland_platform))]
|
||||
pub mod startup_notify;
|
||||
#[cfg(wayland_platform)]
|
||||
|
|
|
|||
|
|
@ -1,6 +0,0 @@
|
|||
//! # Orbital / Redox OS
|
||||
//!
|
||||
//! Redox OS has some functionality not yet present that will be implemented
|
||||
//! when its orbital display server provides it.
|
||||
|
||||
// There are no Orbital specific traits yet.
|
||||
|
|
@ -5,7 +5,7 @@ mod apple;
|
|||
#[cfg(any(x11_platform, wayland_platform))]
|
||||
mod linux;
|
||||
#[cfg(orbital_platform)]
|
||||
mod orbital;
|
||||
pub(crate) use winit_orbital as platform;
|
||||
#[cfg(web_platform)]
|
||||
mod web;
|
||||
#[cfg(windows_platform)]
|
||||
|
|
@ -17,8 +17,6 @@ use self::android as platform;
|
|||
use self::apple as platform;
|
||||
#[cfg(any(x11_platform, wayland_platform))]
|
||||
use self::linux as platform;
|
||||
#[cfg(orbital_platform)]
|
||||
use self::orbital as platform;
|
||||
#[allow(unused_imports)]
|
||||
pub use self::platform::*;
|
||||
#[cfg(web_platform)]
|
||||
|
|
|
|||
|
|
@ -1,759 +0,0 @@
|
|||
use std::cell::Cell;
|
||||
use std::collections::VecDeque;
|
||||
use std::sync::{mpsc, Arc, Mutex};
|
||||
use std::time::Instant;
|
||||
use std::{iter, mem, slice};
|
||||
|
||||
use bitflags::bitflags;
|
||||
use orbclient::{
|
||||
ButtonEvent, EventOption, FocusEvent, HoverEvent, KeyEvent, MouseEvent, MouseRelativeEvent,
|
||||
MoveEvent, QuitEvent, ResizeEvent, ScrollEvent, TextInputEvent,
|
||||
};
|
||||
use smol_str::SmolStr;
|
||||
use winit_core::application::ApplicationHandler;
|
||||
use winit_core::cursor::{CustomCursor, CustomCursorSource};
|
||||
use winit_core::error::{EventLoopError, NotSupportedError, RequestError};
|
||||
use winit_core::event::{self, Ime, Modifiers, StartCause};
|
||||
use winit_core::event_loop::{
|
||||
ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents,
|
||||
EventLoopProxy as CoreEventLoopProxy, EventLoopProxyProvider,
|
||||
OwnedDisplayHandle as CoreOwnedDisplayHandle,
|
||||
};
|
||||
use winit_core::keyboard::{
|
||||
Key, KeyCode, KeyLocation, ModifiersKeys, ModifiersState, NamedKey, NativeKey, NativeKeyCode,
|
||||
PhysicalKey,
|
||||
};
|
||||
use winit_core::window::{Theme, Window as CoreWindow, WindowId};
|
||||
|
||||
use super::{PlatformSpecificEventLoopAttributes, RedoxSocket, TimeSocket, WindowProperties};
|
||||
use crate::platform_impl::Window;
|
||||
|
||||
fn convert_scancode(scancode: u8) -> (PhysicalKey, Option<NamedKey>) {
|
||||
// Key constants from https://docs.rs/orbclient/latest/orbclient/event/index.html
|
||||
let (key_code, named_key_opt) = match scancode {
|
||||
orbclient::K_A => (KeyCode::KeyA, None),
|
||||
orbclient::K_B => (KeyCode::KeyB, None),
|
||||
orbclient::K_C => (KeyCode::KeyC, None),
|
||||
orbclient::K_D => (KeyCode::KeyD, None),
|
||||
orbclient::K_E => (KeyCode::KeyE, None),
|
||||
orbclient::K_F => (KeyCode::KeyF, None),
|
||||
orbclient::K_G => (KeyCode::KeyG, None),
|
||||
orbclient::K_H => (KeyCode::KeyH, None),
|
||||
orbclient::K_I => (KeyCode::KeyI, None),
|
||||
orbclient::K_J => (KeyCode::KeyJ, None),
|
||||
orbclient::K_K => (KeyCode::KeyK, None),
|
||||
orbclient::K_L => (KeyCode::KeyL, None),
|
||||
orbclient::K_M => (KeyCode::KeyM, None),
|
||||
orbclient::K_N => (KeyCode::KeyN, None),
|
||||
orbclient::K_O => (KeyCode::KeyO, None),
|
||||
orbclient::K_P => (KeyCode::KeyP, None),
|
||||
orbclient::K_Q => (KeyCode::KeyQ, None),
|
||||
orbclient::K_R => (KeyCode::KeyR, None),
|
||||
orbclient::K_S => (KeyCode::KeyS, None),
|
||||
orbclient::K_T => (KeyCode::KeyT, None),
|
||||
orbclient::K_U => (KeyCode::KeyU, None),
|
||||
orbclient::K_V => (KeyCode::KeyV, None),
|
||||
orbclient::K_W => (KeyCode::KeyW, None),
|
||||
orbclient::K_X => (KeyCode::KeyX, None),
|
||||
orbclient::K_Y => (KeyCode::KeyY, None),
|
||||
orbclient::K_Z => (KeyCode::KeyZ, None),
|
||||
orbclient::K_0 => (KeyCode::Digit0, None),
|
||||
orbclient::K_1 => (KeyCode::Digit1, None),
|
||||
orbclient::K_2 => (KeyCode::Digit2, None),
|
||||
orbclient::K_3 => (KeyCode::Digit3, None),
|
||||
orbclient::K_4 => (KeyCode::Digit4, None),
|
||||
orbclient::K_5 => (KeyCode::Digit5, None),
|
||||
orbclient::K_6 => (KeyCode::Digit6, None),
|
||||
orbclient::K_7 => (KeyCode::Digit7, None),
|
||||
orbclient::K_8 => (KeyCode::Digit8, None),
|
||||
orbclient::K_9 => (KeyCode::Digit9, None),
|
||||
|
||||
orbclient::K_ALT => (KeyCode::AltLeft, Some(NamedKey::Alt)),
|
||||
orbclient::K_ALT_GR => (KeyCode::AltRight, Some(NamedKey::AltGraph)),
|
||||
orbclient::K_BACKSLASH => (KeyCode::Backslash, None),
|
||||
orbclient::K_BKSP => (KeyCode::Backspace, Some(NamedKey::Backspace)),
|
||||
orbclient::K_BRACE_CLOSE => (KeyCode::BracketRight, None),
|
||||
orbclient::K_BRACE_OPEN => (KeyCode::BracketLeft, None),
|
||||
orbclient::K_CAPS => (KeyCode::CapsLock, Some(NamedKey::CapsLock)),
|
||||
orbclient::K_COMMA => (KeyCode::Comma, None),
|
||||
orbclient::K_CTRL => (KeyCode::ControlLeft, Some(NamedKey::Control)),
|
||||
orbclient::K_DEL => (KeyCode::Delete, Some(NamedKey::Delete)),
|
||||
orbclient::K_DOWN => (KeyCode::ArrowDown, Some(NamedKey::ArrowDown)),
|
||||
orbclient::K_END => (KeyCode::End, Some(NamedKey::End)),
|
||||
orbclient::K_ENTER => (KeyCode::Enter, Some(NamedKey::Enter)),
|
||||
orbclient::K_EQUALS => (KeyCode::Equal, None),
|
||||
orbclient::K_ESC => (KeyCode::Escape, Some(NamedKey::Escape)),
|
||||
orbclient::K_F1 => (KeyCode::F1, Some(NamedKey::F1)),
|
||||
orbclient::K_F2 => (KeyCode::F2, Some(NamedKey::F2)),
|
||||
orbclient::K_F3 => (KeyCode::F3, Some(NamedKey::F3)),
|
||||
orbclient::K_F4 => (KeyCode::F4, Some(NamedKey::F4)),
|
||||
orbclient::K_F5 => (KeyCode::F5, Some(NamedKey::F5)),
|
||||
orbclient::K_F6 => (KeyCode::F6, Some(NamedKey::F6)),
|
||||
orbclient::K_F7 => (KeyCode::F7, Some(NamedKey::F7)),
|
||||
orbclient::K_F8 => (KeyCode::F8, Some(NamedKey::F8)),
|
||||
orbclient::K_F9 => (KeyCode::F9, Some(NamedKey::F9)),
|
||||
orbclient::K_F10 => (KeyCode::F10, Some(NamedKey::F10)),
|
||||
orbclient::K_F11 => (KeyCode::F11, Some(NamedKey::F11)),
|
||||
orbclient::K_F12 => (KeyCode::F12, Some(NamedKey::F12)),
|
||||
orbclient::K_HOME => (KeyCode::Home, Some(NamedKey::Home)),
|
||||
orbclient::K_LEFT => (KeyCode::ArrowLeft, Some(NamedKey::ArrowLeft)),
|
||||
orbclient::K_LEFT_SHIFT => (KeyCode::ShiftLeft, Some(NamedKey::Shift)),
|
||||
orbclient::K_MINUS => (KeyCode::Minus, None),
|
||||
orbclient::K_NUM_0 => (KeyCode::Numpad0, None),
|
||||
orbclient::K_NUM_1 => (KeyCode::Numpad1, None),
|
||||
orbclient::K_NUM_2 => (KeyCode::Numpad2, None),
|
||||
orbclient::K_NUM_3 => (KeyCode::Numpad3, None),
|
||||
orbclient::K_NUM_4 => (KeyCode::Numpad4, None),
|
||||
orbclient::K_NUM_5 => (KeyCode::Numpad5, None),
|
||||
orbclient::K_NUM_6 => (KeyCode::Numpad6, None),
|
||||
orbclient::K_NUM_7 => (KeyCode::Numpad7, None),
|
||||
orbclient::K_NUM_8 => (KeyCode::Numpad8, None),
|
||||
orbclient::K_NUM_9 => (KeyCode::Numpad9, None),
|
||||
orbclient::K_PERIOD => (KeyCode::Period, None),
|
||||
orbclient::K_PGDN => (KeyCode::PageDown, Some(NamedKey::PageDown)),
|
||||
orbclient::K_PGUP => (KeyCode::PageUp, Some(NamedKey::PageUp)),
|
||||
orbclient::K_QUOTE => (KeyCode::Quote, None),
|
||||
orbclient::K_RIGHT => (KeyCode::ArrowRight, Some(NamedKey::ArrowRight)),
|
||||
orbclient::K_RIGHT_SHIFT => (KeyCode::ShiftRight, Some(NamedKey::Shift)),
|
||||
orbclient::K_SEMICOLON => (KeyCode::Semicolon, None),
|
||||
orbclient::K_SLASH => (KeyCode::Slash, None),
|
||||
orbclient::K_SPACE => (KeyCode::Space, None),
|
||||
orbclient::K_SUPER => (KeyCode::MetaLeft, Some(NamedKey::Meta)),
|
||||
orbclient::K_TAB => (KeyCode::Tab, Some(NamedKey::Tab)),
|
||||
orbclient::K_TICK => (KeyCode::Backquote, None),
|
||||
orbclient::K_UP => (KeyCode::ArrowUp, Some(NamedKey::ArrowUp)),
|
||||
orbclient::K_VOLUME_DOWN => (KeyCode::AudioVolumeDown, Some(NamedKey::AudioVolumeDown)),
|
||||
orbclient::K_VOLUME_TOGGLE => (KeyCode::AudioVolumeMute, Some(NamedKey::AudioVolumeMute)),
|
||||
orbclient::K_VOLUME_UP => (KeyCode::AudioVolumeUp, Some(NamedKey::AudioVolumeUp)),
|
||||
|
||||
_ => return (PhysicalKey::Unidentified(NativeKeyCode::Unidentified), None),
|
||||
};
|
||||
(PhysicalKey::Code(key_code), named_key_opt)
|
||||
}
|
||||
|
||||
fn element_state(pressed: bool) -> event::ElementState {
|
||||
if pressed {
|
||||
event::ElementState::Pressed
|
||||
} else {
|
||||
event::ElementState::Released
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
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 LMETA = 1 << 6;
|
||||
const RMETA = 1 << 7;
|
||||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
struct MouseButtonState: u8 {
|
||||
const LEFT = 1 << 0;
|
||||
const MIDDLE = 1 << 1;
|
||||
const RIGHT = 1 << 2;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
struct EventState {
|
||||
keyboard: KeyboardModifierState,
|
||||
mouse: MouseButtonState,
|
||||
resize_opt: Option<(u32, u32)>,
|
||||
}
|
||||
|
||||
impl EventState {
|
||||
fn character_all_modifiers(&self, character: char) -> char {
|
||||
// Modify character if Ctrl is pressed
|
||||
#[allow(clippy::collapsible_if)]
|
||||
if self.keyboard.contains(KeyboardModifierState::LCTRL)
|
||||
|| self.keyboard.contains(KeyboardModifierState::RCTRL)
|
||||
{
|
||||
if character.is_ascii_lowercase() {
|
||||
return ((character as u8 - b'a') + 1) as char;
|
||||
}
|
||||
// TODO: more control key variants?
|
||||
}
|
||||
|
||||
// Return character as-is if no special handling required
|
||||
character
|
||||
}
|
||||
|
||||
fn key(&mut self, key: PhysicalKey, pressed: bool) {
|
||||
let code = match key {
|
||||
PhysicalKey::Code(code) => code,
|
||||
_ => return,
|
||||
};
|
||||
|
||||
match code {
|
||||
KeyCode::ShiftLeft => self.keyboard.set(KeyboardModifierState::LSHIFT, pressed),
|
||||
KeyCode::ShiftRight => self.keyboard.set(KeyboardModifierState::RSHIFT, pressed),
|
||||
KeyCode::ControlLeft => self.keyboard.set(KeyboardModifierState::LCTRL, pressed),
|
||||
KeyCode::ControlRight => self.keyboard.set(KeyboardModifierState::RCTRL, pressed),
|
||||
KeyCode::AltLeft => self.keyboard.set(KeyboardModifierState::LALT, pressed),
|
||||
KeyCode::AltRight => self.keyboard.set(KeyboardModifierState::RALT, pressed),
|
||||
KeyCode::MetaLeft => self.keyboard.set(KeyboardModifierState::LMETA, pressed),
|
||||
KeyCode::MetaRight => self.keyboard.set(KeyboardModifierState::RMETA, 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) -> Modifiers {
|
||||
let mut state = ModifiersState::empty();
|
||||
let mut pressed_mods = ModifiersKeys::empty();
|
||||
|
||||
if self.keyboard.intersects(KeyboardModifierState::LSHIFT | KeyboardModifierState::RSHIFT) {
|
||||
state |= ModifiersState::SHIFT;
|
||||
}
|
||||
|
||||
pressed_mods
|
||||
.set(ModifiersKeys::LSHIFT, self.keyboard.contains(KeyboardModifierState::LSHIFT));
|
||||
pressed_mods
|
||||
.set(ModifiersKeys::RSHIFT, self.keyboard.contains(KeyboardModifierState::RSHIFT));
|
||||
|
||||
if self.keyboard.intersects(KeyboardModifierState::LCTRL | KeyboardModifierState::RCTRL) {
|
||||
state |= ModifiersState::CONTROL;
|
||||
}
|
||||
|
||||
pressed_mods
|
||||
.set(ModifiersKeys::LCONTROL, self.keyboard.contains(KeyboardModifierState::LCTRL));
|
||||
pressed_mods
|
||||
.set(ModifiersKeys::RCONTROL, self.keyboard.contains(KeyboardModifierState::RCTRL));
|
||||
|
||||
if self.keyboard.intersects(KeyboardModifierState::LALT | KeyboardModifierState::RALT) {
|
||||
state |= ModifiersState::ALT;
|
||||
}
|
||||
|
||||
pressed_mods.set(ModifiersKeys::LALT, self.keyboard.contains(KeyboardModifierState::LALT));
|
||||
pressed_mods.set(ModifiersKeys::RALT, self.keyboard.contains(KeyboardModifierState::RALT));
|
||||
|
||||
if self.keyboard.intersects(KeyboardModifierState::LMETA | KeyboardModifierState::RMETA) {
|
||||
state |= ModifiersState::META
|
||||
}
|
||||
|
||||
pressed_mods
|
||||
.set(ModifiersKeys::LMETA, self.keyboard.contains(KeyboardModifierState::LMETA));
|
||||
pressed_mods
|
||||
.set(ModifiersKeys::RMETA, self.keyboard.contains(KeyboardModifierState::RMETA));
|
||||
|
||||
Modifiers::new(state, pressed_mods)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct EventLoop {
|
||||
windows: Vec<(Arc<RedoxSocket>, EventState)>,
|
||||
window_target: ActiveEventLoop,
|
||||
user_events_receiver: mpsc::Receiver<()>,
|
||||
}
|
||||
|
||||
impl EventLoop {
|
||||
pub(crate) fn new(_: &PlatformSpecificEventLoopAttributes) -> Result<Self, EventLoopError> {
|
||||
// NOTE: Create a channel which can hold only one event to automatically _squash_ user
|
||||
// events.
|
||||
let (user_events_sender, user_events_receiver) = mpsc::sync_channel(1);
|
||||
|
||||
let event_socket =
|
||||
Arc::new(RedoxSocket::event().map_err(|error| os_error!(format!("{error}")))?);
|
||||
|
||||
let wake_socket = TimeSocket::open().map_err(|error| os_error!(format!("{error}")))?;
|
||||
|
||||
event_socket
|
||||
.write(&syscall::Event {
|
||||
id: wake_socket.0.fd,
|
||||
flags: syscall::EventFlags::EVENT_READ,
|
||||
data: wake_socket.0.fd,
|
||||
})
|
||||
.map_err(|error| os_error!(format!("{error}")))?;
|
||||
|
||||
Ok(Self {
|
||||
windows: Vec::new(),
|
||||
window_target: ActiveEventLoop {
|
||||
control_flow: Cell::new(ControlFlow::default()),
|
||||
exit: Cell::new(false),
|
||||
creates: Mutex::new(VecDeque::new()),
|
||||
redraws: Arc::new(Mutex::new(VecDeque::new())),
|
||||
destroys: Arc::new(Mutex::new(VecDeque::new())),
|
||||
event_socket,
|
||||
event_loop_proxy: Arc::new(EventLoopProxy { wake_socket, user_events_sender }),
|
||||
},
|
||||
user_events_receiver,
|
||||
})
|
||||
}
|
||||
|
||||
fn process_event<A: ApplicationHandler>(
|
||||
window_id: WindowId,
|
||||
event_option: EventOption,
|
||||
event_state: &mut EventState,
|
||||
window_target: &ActiveEventLoop,
|
||||
app: &mut A,
|
||||
) {
|
||||
match event_option {
|
||||
EventOption::Key(KeyEvent { character, scancode, pressed }) => {
|
||||
// Convert scancode
|
||||
let (physical_key, named_key_opt) = convert_scancode(scancode);
|
||||
|
||||
// Get previous modifiers and update modifiers based on physical key
|
||||
let modifiers_before = event_state.keyboard;
|
||||
event_state.key(physical_key, pressed);
|
||||
|
||||
// Default to unidentified key with no text
|
||||
let mut logical_key = Key::Unidentified(NativeKey::Unidentified);
|
||||
let mut key_without_modifiers = logical_key.clone();
|
||||
let mut text = None;
|
||||
let mut text_with_all_modifiers = None;
|
||||
|
||||
// Set key and text based on character
|
||||
if character != '\0' {
|
||||
let mut tmp = [0u8; 4];
|
||||
let character_str = character.encode_utf8(&mut tmp);
|
||||
// The key with Shift and Caps Lock applied (but not Ctrl)
|
||||
logical_key = Key::Character(character_str.into());
|
||||
// The key without Shift or Caps Lock applied
|
||||
key_without_modifiers =
|
||||
Key::Character(SmolStr::from_iter(character.to_lowercase()));
|
||||
if pressed {
|
||||
// The key with Shift and Caps Lock applied (but not Ctrl)
|
||||
text = Some(character_str.into());
|
||||
// The key with Shift, Caps Lock, and Ctrl applied
|
||||
let character_all_modifiers =
|
||||
event_state.character_all_modifiers(character);
|
||||
text_with_all_modifiers =
|
||||
Some(character_all_modifiers.encode_utf8(&mut tmp).into())
|
||||
}
|
||||
};
|
||||
|
||||
// Override key if a named key was found (this is to allow Enter to replace '\n')
|
||||
if let Some(named_key) = named_key_opt {
|
||||
logical_key = Key::Named(named_key);
|
||||
key_without_modifiers = logical_key.clone();
|
||||
}
|
||||
|
||||
let event = event::WindowEvent::KeyboardInput {
|
||||
device_id: None,
|
||||
event: event::KeyEvent {
|
||||
logical_key,
|
||||
physical_key,
|
||||
location: KeyLocation::Standard,
|
||||
state: element_state(pressed),
|
||||
repeat: false,
|
||||
text,
|
||||
key_without_modifiers,
|
||||
text_with_all_modifiers,
|
||||
},
|
||||
is_synthetic: false,
|
||||
};
|
||||
|
||||
app.window_event(window_target, window_id, event);
|
||||
|
||||
// If the state of the modifiers has changed, send the event.
|
||||
if modifiers_before != event_state.keyboard {
|
||||
app.window_event(
|
||||
window_target,
|
||||
window_id,
|
||||
event::WindowEvent::ModifiersChanged(event_state.modifiers()),
|
||||
);
|
||||
}
|
||||
},
|
||||
EventOption::TextInput(TextInputEvent { character }) => {
|
||||
app.window_event(
|
||||
window_target,
|
||||
window_id,
|
||||
event::WindowEvent::Ime(Ime::Preedit("".into(), None)),
|
||||
);
|
||||
app.window_event(
|
||||
window_target,
|
||||
window_id,
|
||||
event::WindowEvent::Ime(Ime::Commit(character.into())),
|
||||
);
|
||||
},
|
||||
EventOption::Mouse(MouseEvent { x, y }) => {
|
||||
app.window_event(window_target, window_id, event::WindowEvent::PointerMoved {
|
||||
device_id: None,
|
||||
primary: true,
|
||||
position: (x, y).into(),
|
||||
source: event::PointerSource::Mouse,
|
||||
});
|
||||
},
|
||||
EventOption::MouseRelative(MouseRelativeEvent { dx, dy }) => {
|
||||
app.device_event(window_target, None, event::DeviceEvent::PointerMotion {
|
||||
delta: (dx as f64, dy as f64),
|
||||
});
|
||||
},
|
||||
EventOption::Button(ButtonEvent { left, middle, right }) => {
|
||||
while let Some((button, state)) = event_state.mouse(left, middle, right) {
|
||||
app.window_event(window_target, window_id, event::WindowEvent::PointerButton {
|
||||
device_id: None,
|
||||
primary: true,
|
||||
state,
|
||||
position: dpi::PhysicalPosition::default(),
|
||||
button: button.into(),
|
||||
});
|
||||
}
|
||||
},
|
||||
EventOption::Scroll(ScrollEvent { x, y }) => {
|
||||
app.window_event(window_target, window_id, event::WindowEvent::MouseWheel {
|
||||
device_id: None,
|
||||
delta: event::MouseScrollDelta::LineDelta(x as f32, y as f32),
|
||||
phase: event::TouchPhase::Moved,
|
||||
});
|
||||
},
|
||||
EventOption::Quit(QuitEvent {}) => {
|
||||
app.window_event(window_target, window_id, event::WindowEvent::CloseRequested);
|
||||
},
|
||||
EventOption::Focus(FocusEvent { focused }) => {
|
||||
app.window_event(window_target, window_id, event::WindowEvent::Focused(focused));
|
||||
},
|
||||
EventOption::Move(MoveEvent { x, y }) => {
|
||||
app.window_event(
|
||||
window_target,
|
||||
window_id,
|
||||
event::WindowEvent::Moved((x, y).into()),
|
||||
);
|
||||
},
|
||||
EventOption::Resize(ResizeEvent { width, height }) => {
|
||||
app.window_event(
|
||||
window_target,
|
||||
window_id,
|
||||
event::WindowEvent::SurfaceResized((width, height).into()),
|
||||
);
|
||||
|
||||
// Acknowledge resize after event loop.
|
||||
event_state.resize_opt = Some((width, height));
|
||||
},
|
||||
// TODO: Screen, Clipboard, Drop
|
||||
EventOption::Hover(HoverEvent { entered }) => {
|
||||
let event = if entered {
|
||||
event::WindowEvent::PointerEntered {
|
||||
device_id: None,
|
||||
primary: true,
|
||||
position: dpi::PhysicalPosition::default(),
|
||||
kind: event::PointerKind::Mouse,
|
||||
}
|
||||
} else {
|
||||
event::WindowEvent::PointerLeft {
|
||||
device_id: None,
|
||||
primary: true,
|
||||
position: None,
|
||||
kind: event::PointerKind::Mouse,
|
||||
}
|
||||
};
|
||||
|
||||
app.window_event(window_target, window_id, event);
|
||||
},
|
||||
other => {
|
||||
tracing::warn!("unhandled event: {:?}", other);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run_app<A: ApplicationHandler>(mut self, mut app: A) -> Result<(), EventLoopError> {
|
||||
let mut start_cause = StartCause::Init;
|
||||
loop {
|
||||
app.new_events(&self.window_target, start_cause);
|
||||
|
||||
if start_cause == StartCause::Init {
|
||||
app.can_create_surfaces(&self.window_target);
|
||||
}
|
||||
|
||||
// Handle window creates.
|
||||
while let Some(window) = {
|
||||
let mut creates = self.window_target.creates.lock().unwrap();
|
||||
creates.pop_front()
|
||||
} {
|
||||
let window_id = WindowId::from_raw(window.fd);
|
||||
|
||||
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.
|
||||
let event = event::WindowEvent::SurfaceResized((properties.w, properties.h).into());
|
||||
app.window_event(&self.window_target, window_id, event);
|
||||
|
||||
// Send moved event on create to indicate first position.
|
||||
let event = event::WindowEvent::Moved((properties.x, properties.y).into());
|
||||
app.window_event(&self.window_target, window_id, event);
|
||||
}
|
||||
|
||||
// Handle window destroys.
|
||||
while let Some(destroy_id) = {
|
||||
let mut destroys = self.window_target.destroys.lock().unwrap();
|
||||
destroys.pop_front()
|
||||
} {
|
||||
app.window_event(&self.window_target, destroy_id, event::WindowEvent::Destroyed);
|
||||
self.windows
|
||||
.retain(|(window, _event_state)| WindowId::from_raw(window.fd) != destroy_id);
|
||||
}
|
||||
|
||||
// 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::from_raw(window.fd);
|
||||
|
||||
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,
|
||||
&self.window_target,
|
||||
&mut app,
|
||||
);
|
||||
}
|
||||
|
||||
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.redraws.lock().unwrap();
|
||||
if !redraws.contains(&window_id) {
|
||||
redraws.push_back(window_id);
|
||||
}
|
||||
}
|
||||
|
||||
// Move to next window.
|
||||
i += 1;
|
||||
}
|
||||
|
||||
while self.user_events_receiver.try_recv().is_ok() {
|
||||
app.proxy_wake_up(&self.window_target);
|
||||
}
|
||||
|
||||
// To avoid deadlocks the redraws lock is not held during event processing.
|
||||
while let Some(window_id) = {
|
||||
let mut redraws = self.window_target.redraws.lock().unwrap();
|
||||
redraws.pop_front()
|
||||
} {
|
||||
app.window_event(
|
||||
&self.window_target,
|
||||
window_id,
|
||||
event::WindowEvent::RedrawRequested,
|
||||
);
|
||||
}
|
||||
|
||||
app.about_to_wait(&self.window_target);
|
||||
|
||||
if self.window_target.exiting() {
|
||||
break;
|
||||
}
|
||||
|
||||
let requested_resume = match self.window_target.control_flow() {
|
||||
ControlFlow::Poll => {
|
||||
start_cause = StartCause::Poll;
|
||||
continue;
|
||||
},
|
||||
ControlFlow::Wait => None,
|
||||
ControlFlow::WaitUntil(instant) => Some(instant),
|
||||
};
|
||||
|
||||
// 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
|
||||
.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.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 };
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn window_target(&self) -> &dyn RootActiveEventLoop {
|
||||
&self.window_target
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct EventLoopProxy {
|
||||
user_events_sender: mpsc::SyncSender<()>,
|
||||
pub(super) wake_socket: TimeSocket,
|
||||
}
|
||||
|
||||
impl EventLoopProxyProvider for EventLoopProxy {
|
||||
fn wake_up(&self) {
|
||||
// When we fail to send the event it means that we haven't woken up to read the previous
|
||||
// event.
|
||||
if self.user_events_sender.try_send(()).is_ok() {
|
||||
self.wake_socket.wake().unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Unpin for EventLoopProxy {}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ActiveEventLoop {
|
||||
control_flow: Cell<ControlFlow>,
|
||||
exit: Cell<bool>,
|
||||
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) event_loop_proxy: Arc<EventLoopProxy>,
|
||||
}
|
||||
|
||||
impl RootActiveEventLoop for ActiveEventLoop {
|
||||
fn create_proxy(&self) -> CoreEventLoopProxy {
|
||||
CoreEventLoopProxy::new(self.event_loop_proxy.clone())
|
||||
}
|
||||
|
||||
fn create_window(
|
||||
&self,
|
||||
window_attributes: winit_core::window::WindowAttributes,
|
||||
) -> Result<Box<dyn CoreWindow>, RequestError> {
|
||||
Ok(Box::new(Window::new(self, window_attributes)?))
|
||||
}
|
||||
|
||||
fn create_custom_cursor(&self, _: CustomCursorSource) -> Result<CustomCursor, RequestError> {
|
||||
Err(NotSupportedError::new("create_custom_cursor is not supported").into())
|
||||
}
|
||||
|
||||
fn available_monitors(&self) -> Box<dyn Iterator<Item = winit_core::monitor::MonitorHandle>> {
|
||||
Box::new(iter::empty())
|
||||
}
|
||||
|
||||
fn system_theme(&self) -> Option<Theme> {
|
||||
None
|
||||
}
|
||||
|
||||
fn primary_monitor(&self) -> Option<winit_core::monitor::MonitorHandle> {
|
||||
None
|
||||
}
|
||||
|
||||
fn listen_device_events(&self, _allowed: DeviceEvents) {}
|
||||
|
||||
fn set_control_flow(&self, control_flow: ControlFlow) {
|
||||
self.control_flow.set(control_flow)
|
||||
}
|
||||
|
||||
fn control_flow(&self) -> ControlFlow {
|
||||
self.control_flow.get()
|
||||
}
|
||||
|
||||
fn exit(&self) {
|
||||
self.exit.set(true);
|
||||
}
|
||||
|
||||
fn exiting(&self) -> bool {
|
||||
self.exit.get()
|
||||
}
|
||||
|
||||
fn owned_display_handle(&self) -> CoreOwnedDisplayHandle {
|
||||
CoreOwnedDisplayHandle::new(Arc::new(OwnedDisplayHandle))
|
||||
}
|
||||
|
||||
fn rwh_06_handle(&self) -> &dyn rwh_06::HasDisplayHandle {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl rwh_06::HasDisplayHandle for ActiveEventLoop {
|
||||
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
|
||||
let raw = rwh_06::RawDisplayHandle::Orbital(rwh_06::OrbitalDisplayHandle::new());
|
||||
unsafe { Ok(rwh_06::DisplayHandle::borrow_raw(raw)) }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct OwnedDisplayHandle;
|
||||
|
||||
impl rwh_06::HasDisplayHandle for OwnedDisplayHandle {
|
||||
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
|
||||
let raw = rwh_06::RawDisplayHandle::Orbital(rwh_06::OrbitalDisplayHandle::new());
|
||||
unsafe { Ok(rwh_06::DisplayHandle::borrow_raw(raw)) }
|
||||
}
|
||||
}
|
||||
|
|
@ -1,128 +0,0 @@
|
|||
#![cfg(target_os = "redox")]
|
||||
|
||||
use std::{fmt, str};
|
||||
|
||||
pub(crate) use self::event_loop::{ActiveEventLoop, EventLoop};
|
||||
pub use self::window::Window;
|
||||
|
||||
mod event_loop;
|
||||
mod window;
|
||||
|
||||
#[derive(Debug)]
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
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, Default, Eq, PartialEq)]
|
||||
pub struct PlatformSpecificWindowAttributes;
|
||||
|
||||
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 fmt::Display for WindowProperties<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"orbital:{}/{}/{}/{}/{}/{}",
|
||||
self.flags, self.x, self.y, self.w, self.h, self.title
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,488 +0,0 @@
|
|||
use std::collections::VecDeque;
|
||||
use std::iter;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use dpi::{PhysicalInsets, PhysicalPosition, PhysicalSize, Position, Size};
|
||||
use winit_core::cursor::Cursor;
|
||||
use winit_core::error::{NotSupportedError, RequestError};
|
||||
use winit_core::monitor::{Fullscreen, MonitorHandle as CoreMonitorHandle};
|
||||
use winit_core::window::{self, ImePurpose, Window as CoreWindow, WindowId};
|
||||
|
||||
use super::event_loop::EventLoopProxy;
|
||||
use super::{ActiveEventLoop, RedoxSocket, 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_HIDDEN: char = 'h';
|
||||
const ORBITAL_FLAG_BORDERLESS: char = 'l';
|
||||
const ORBITAL_FLAG_MAXIMIZED: char = 'm';
|
||||
const ORBITAL_FLAG_RESIZABLE: char = 'r';
|
||||
const ORBITAL_FLAG_TRANSPARENT: char = 't';
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Window {
|
||||
window_socket: Arc<RedoxSocket>,
|
||||
redraws: Arc<Mutex<VecDeque<WindowId>>>,
|
||||
destroys: Arc<Mutex<VecDeque<WindowId>>>,
|
||||
event_loop_proxy: Arc<EventLoopProxy>,
|
||||
}
|
||||
|
||||
impl Window {
|
||||
pub(crate) fn new(
|
||||
el: &ActiveEventLoop,
|
||||
attrs: window::WindowAttributes,
|
||||
) -> Result<Self, RequestError> {
|
||||
let scale = 1.;
|
||||
|
||||
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.surface_size {
|
||||
size.to_physical::<u32>(scale).into()
|
||||
} else {
|
||||
(1024, 768)
|
||||
};
|
||||
|
||||
// TODO: min/max surface_size
|
||||
|
||||
// Async by default.
|
||||
let mut flag_str = ORBITAL_FLAG_ASYNC.to_string();
|
||||
|
||||
if attrs.maximized {
|
||||
flag_str.push(ORBITAL_FLAG_MAXIMIZED);
|
||||
}
|
||||
|
||||
if attrs.resizable {
|
||||
flag_str.push(ORBITAL_FLAG_RESIZABLE);
|
||||
}
|
||||
|
||||
// TODO: fullscreen
|
||||
|
||||
if attrs.transparent {
|
||||
flag_str.push(ORBITAL_FLAG_TRANSPARENT);
|
||||
}
|
||||
|
||||
if !attrs.decorations {
|
||||
flag_str.push(ORBITAL_FLAG_BORDERLESS);
|
||||
}
|
||||
|
||||
if !attrs.visible {
|
||||
flag_str.push(ORBITAL_FLAG_HIDDEN);
|
||||
}
|
||||
|
||||
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.event_loop_proxy.wake_socket.wake().unwrap();
|
||||
|
||||
Ok(Self {
|
||||
window_socket,
|
||||
redraws: el.redraws.clone(),
|
||||
destroys: el.destroys.clone(),
|
||||
event_loop_proxy: el.event_loop_proxy.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
fn get_flag(&self, flag: char) -> Result<bool, RequestError> {
|
||||
let mut buf: [u8; 4096] = [0; 4096];
|
||||
let path = self.window_socket.fpath(&mut buf).map_err(|err| os_error!(format!("{err}")))?;
|
||||
let properties = WindowProperties::new(path);
|
||||
Ok(properties.flags.contains(flag))
|
||||
}
|
||||
|
||||
fn set_flag(&self, flag: char, value: bool) -> Result<(), RequestError> {
|
||||
self.window_socket
|
||||
.write(format!("F,{flag},{}", if value { 1 } else { 0 }).as_bytes())
|
||||
.map_err(|err| os_error!(format!("{err}")))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn raw_window_handle_rwh_06(&self) -> Result<rwh_06::RawWindowHandle, rwh_06::HandleError> {
|
||||
let handle = rwh_06::OrbitalWindowHandle::new({
|
||||
let window = self.window_socket.fd as *mut _;
|
||||
std::ptr::NonNull::new(window).expect("orbital fd should never be null")
|
||||
});
|
||||
Ok(rwh_06::RawWindowHandle::Orbital(handle))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn raw_display_handle_rwh_06(&self) -> Result<rwh_06::RawDisplayHandle, rwh_06::HandleError> {
|
||||
Ok(rwh_06::RawDisplayHandle::Orbital(rwh_06::OrbitalDisplayHandle::new()))
|
||||
}
|
||||
}
|
||||
|
||||
impl CoreWindow for Window {
|
||||
fn id(&self) -> WindowId {
|
||||
WindowId::from_raw(self.window_socket.fd)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn primary_monitor(&self) -> Option<CoreMonitorHandle> {
|
||||
None
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn available_monitors(&self) -> Box<dyn Iterator<Item = CoreMonitorHandle>> {
|
||||
Box::new(iter::empty())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn current_monitor(&self) -> Option<CoreMonitorHandle> {
|
||||
None
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn scale_factor(&self) -> f64 {
|
||||
1.
|
||||
}
|
||||
|
||||
#[inline]
|
||||
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.event_loop_proxy.wake_socket.wake().unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn pre_present_notify(&self) {}
|
||||
|
||||
#[inline]
|
||||
fn reset_dead_keys(&self) {
|
||||
// TODO?
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn surface_position(&self) -> PhysicalPosition<i32> {
|
||||
// TODO: adjust for window decorations
|
||||
(0, 0).into()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn outer_position(&self) -> Result<PhysicalPosition<i32>, RequestError> {
|
||||
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]
|
||||
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]
|
||||
fn surface_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]
|
||||
fn request_surface_size(&self, size: Size) -> Option<PhysicalSize<u32>> {
|
||||
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");
|
||||
None
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn outer_size(&self) -> PhysicalSize<u32> {
|
||||
// TODO: adjust for window decorations
|
||||
self.surface_size()
|
||||
}
|
||||
|
||||
fn safe_area(&self) -> PhysicalInsets<u32> {
|
||||
PhysicalInsets::new(0, 0, 0, 0)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_min_surface_size(&self, _: Option<Size>) {}
|
||||
|
||||
#[inline]
|
||||
fn set_max_surface_size(&self, _: Option<Size>) {}
|
||||
|
||||
#[inline]
|
||||
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]
|
||||
fn set_title(&self, title: &str) {
|
||||
self.window_socket.write(format!("T,{title}").as_bytes()).expect("failed to set title");
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_transparent(&self, transparent: bool) {
|
||||
let _ = self.set_flag(ORBITAL_FLAG_TRANSPARENT, transparent);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_blur(&self, _blur: bool) {}
|
||||
|
||||
#[inline]
|
||||
fn set_visible(&self, visible: bool) {
|
||||
let _ = self.set_flag(ORBITAL_FLAG_HIDDEN, !visible);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_visible(&self) -> Option<bool> {
|
||||
Some(!self.get_flag(ORBITAL_FLAG_HIDDEN).unwrap_or(false))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn surface_resize_increments(&self) -> Option<PhysicalSize<u32>> {
|
||||
None
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_surface_resize_increments(&self, _increments: Option<Size>) {}
|
||||
|
||||
#[inline]
|
||||
fn set_resizable(&self, resizeable: bool) {
|
||||
let _ = self.set_flag(ORBITAL_FLAG_RESIZABLE, resizeable);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_resizable(&self) -> bool {
|
||||
self.get_flag(ORBITAL_FLAG_RESIZABLE).unwrap_or(false)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_minimized(&self, _minimized: bool) {}
|
||||
|
||||
#[inline]
|
||||
fn is_minimized(&self) -> Option<bool> {
|
||||
None
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_maximized(&self, maximized: bool) {
|
||||
let _ = self.set_flag(ORBITAL_FLAG_MAXIMIZED, maximized);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_maximized(&self) -> bool {
|
||||
self.get_flag(ORBITAL_FLAG_MAXIMIZED).unwrap_or(false)
|
||||
}
|
||||
|
||||
fn set_fullscreen(&self, _monitor: Option<Fullscreen>) {}
|
||||
|
||||
fn fullscreen(&self) -> Option<Fullscreen> {
|
||||
None
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_decorations(&self, decorations: bool) {
|
||||
let _ = self.set_flag(ORBITAL_FLAG_BORDERLESS, !decorations);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_decorated(&self) -> bool {
|
||||
!self.get_flag(ORBITAL_FLAG_BORDERLESS).unwrap_or(false)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_window_level(&self, level: window::WindowLevel) {
|
||||
match level {
|
||||
window::WindowLevel::AlwaysOnBottom => {
|
||||
let _ = self.set_flag(ORBITAL_FLAG_BACK, true);
|
||||
},
|
||||
window::WindowLevel::Normal => {
|
||||
let _ = self.set_flag(ORBITAL_FLAG_BACK, false);
|
||||
let _ = self.set_flag(ORBITAL_FLAG_FRONT, false);
|
||||
},
|
||||
window::WindowLevel::AlwaysOnTop => {
|
||||
let _ = self.set_flag(ORBITAL_FLAG_FRONT, true);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_window_icon(&self, _window_icon: Option<winit_core::icon::Icon>) {}
|
||||
|
||||
#[inline]
|
||||
fn set_ime_cursor_area(&self, _position: Position, _size: Size) {}
|
||||
|
||||
#[inline]
|
||||
fn set_ime_allowed(&self, _allowed: bool) {}
|
||||
|
||||
#[inline]
|
||||
fn set_ime_purpose(&self, _purpose: ImePurpose) {}
|
||||
|
||||
#[inline]
|
||||
fn focus_window(&self) {}
|
||||
|
||||
#[inline]
|
||||
fn request_user_attention(&self, _request_type: Option<window::UserAttentionType>) {}
|
||||
|
||||
#[inline]
|
||||
fn set_cursor(&self, _: Cursor) {}
|
||||
|
||||
#[inline]
|
||||
fn set_cursor_position(&self, _: Position) -> Result<(), RequestError> {
|
||||
Err(NotSupportedError::new("set_cursor_position is not supported").into())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_cursor_grab(&self, mode: window::CursorGrabMode) -> Result<(), RequestError> {
|
||||
let (grab, relative) = match mode {
|
||||
window::CursorGrabMode::None => (false, false),
|
||||
window::CursorGrabMode::Confined => (true, false),
|
||||
window::CursorGrabMode::Locked => (true, true),
|
||||
};
|
||||
self.window_socket
|
||||
.write(format!("M,G,{}", if grab { 1 } else { 0 }).as_bytes())
|
||||
.map_err(|err| os_error!(format!("{err}")))?;
|
||||
self.window_socket
|
||||
.write(format!("M,R,{}", if relative { 1 } else { 0 }).as_bytes())
|
||||
.map_err(|err| os_error!(format!("{err}")))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_cursor_visible(&self, visible: bool) {
|
||||
let _ = self.window_socket.write(format!("M,C,{}", if visible { 1 } else { 0 }).as_bytes());
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn drag_window(&self) -> Result<(), RequestError> {
|
||||
self.window_socket.write(b"D").map_err(|err| os_error!(format!("{err}")))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn drag_resize_window(&self, direction: window::ResizeDirection) -> Result<(), RequestError> {
|
||||
let arg = match direction {
|
||||
window::ResizeDirection::East => "R",
|
||||
window::ResizeDirection::North => "T",
|
||||
window::ResizeDirection::NorthEast => "T,R",
|
||||
window::ResizeDirection::NorthWest => "T,L",
|
||||
window::ResizeDirection::South => "B",
|
||||
window::ResizeDirection::SouthEast => "B,R",
|
||||
window::ResizeDirection::SouthWest => "B,L",
|
||||
window::ResizeDirection::West => "L",
|
||||
};
|
||||
self.window_socket
|
||||
.write(format!("D,{arg}").as_bytes())
|
||||
.map_err(|err| os_error!(format!("{err}")))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn show_window_menu(&self, _position: Position) {}
|
||||
|
||||
#[inline]
|
||||
fn set_cursor_hittest(&self, _hittest: bool) -> Result<(), RequestError> {
|
||||
Err(NotSupportedError::new("set_cursor_hittest is not supported").into())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_enabled_buttons(&self, _buttons: window::WindowButtons) {}
|
||||
|
||||
#[inline]
|
||||
fn enabled_buttons(&self) -> window::WindowButtons {
|
||||
window::WindowButtons::all()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn theme(&self) -> Option<window::Theme> {
|
||||
None
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn has_focus(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_theme(&self, _theme: Option<window::Theme>) {}
|
||||
|
||||
fn set_content_protected(&self, _protected: bool) {}
|
||||
|
||||
fn rwh_06_window_handle(&self) -> &dyn rwh_06::HasWindowHandle {
|
||||
self
|
||||
}
|
||||
|
||||
fn rwh_06_display_handle(&self) -> &dyn rwh_06::HasDisplayHandle {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl rwh_06::HasWindowHandle for Window {
|
||||
fn window_handle(&self) -> Result<rwh_06::WindowHandle<'_>, rwh_06::HandleError> {
|
||||
let raw = self.raw_window_handle_rwh_06()?;
|
||||
unsafe { Ok(rwh_06::WindowHandle::borrow_raw(raw)) }
|
||||
}
|
||||
}
|
||||
|
||||
impl rwh_06::HasDisplayHandle for Window {
|
||||
fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
|
||||
let raw = self.raw_display_handle_rwh_06()?;
|
||||
unsafe { Ok(rwh_06::DisplayHandle::borrow_raw(raw)) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Window {
|
||||
fn drop(&mut self) {
|
||||
{
|
||||
let mut destroys = self.destroys.lock().unwrap();
|
||||
destroys.push_back(self.id());
|
||||
}
|
||||
|
||||
self.event_loop_proxy.wake_socket.wake().unwrap();
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue