mirror of
https://github.com/rust-windowing/winit.git
synced 2026-08-29 04:40:04 -04:00
winit-orbital: Implement pump_app_events
This commit is contained in:
@@ -81,8 +81,8 @@ xkbcommon-dl = "0.4.2"
|
||||
|
||||
# Orbital dependencies.
|
||||
libredox = "0.1.12"
|
||||
orbclient = { version = "0.3.47", default-features = false }
|
||||
redox_event = { package = "redox_event", version = "0.4.5" }
|
||||
orbclient = { version = "0.4.3", default-features = false }
|
||||
redox_event = { package = "redox_event", version = "0.4.8" }
|
||||
|
||||
# Web dependencies.
|
||||
atomic-waker = "1"
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
use std::cell::Cell;
|
||||
use std::collections::VecDeque;
|
||||
use std::os::raw::c_long;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::{Arc, Mutex, mpsc};
|
||||
use std::time::Instant;
|
||||
use std::time::{Duration, Instant};
|
||||
use std::{iter, mem, slice};
|
||||
|
||||
use bitflags::bitflags;
|
||||
@@ -11,12 +10,13 @@ use orbclient::{
|
||||
ButtonEvent, EventOption, FocusEvent, HoverEvent, KeyEvent, MouseEvent, MouseRelativeEvent,
|
||||
MoveEvent, QuitEvent, ResizeEvent, ScrollEvent, TextInputEvent,
|
||||
};
|
||||
use redox_event::{EventFlags, EventQueue};
|
||||
use redox_event::{EventFlags, EventQueue, UserData};
|
||||
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::pump_events::PumpStatus;
|
||||
use winit_core::event_loop::{
|
||||
ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents,
|
||||
EventLoopProxy as CoreEventLoopProxy, EventLoopProxyProvider,
|
||||
@@ -31,6 +31,9 @@ use winit_core::window::{Theme, Window as CoreWindow, WindowId};
|
||||
use crate::window::Window;
|
||||
use crate::{RedoxSocket, TimeSocket, WindowProperties};
|
||||
|
||||
/// timeout from redox_syscall
|
||||
const EVENT_TIMEOUT_ID: usize = usize::MAX - 2;
|
||||
|
||||
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 {
|
||||
@@ -157,6 +160,15 @@ fn convert_scancode(scancode: u8) -> (PhysicalKey, Option<NamedKey>) {
|
||||
(PhysicalKey::Code(key_code), named_key_opt)
|
||||
}
|
||||
|
||||
pub fn scancode_to_physicalkey(scancode: u32) -> PhysicalKey {
|
||||
convert_scancode(scancode.try_into().unwrap_or_default()).0
|
||||
}
|
||||
|
||||
pub fn physicalkey_to_scancode(_physical_key: PhysicalKey) -> Option<u32> {
|
||||
// TODO
|
||||
None
|
||||
}
|
||||
|
||||
fn element_state(pressed: bool) -> event::ElementState {
|
||||
if pressed { event::ElementState::Pressed } else { event::ElementState::Released }
|
||||
}
|
||||
@@ -296,6 +308,8 @@ impl EventState {
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct EventLoop {
|
||||
/// Has `run` or `run_on_demand` been called or a call to `pump_events` that starts the loop
|
||||
loop_running: bool,
|
||||
windows: Vec<(Arc<RedoxSocket>, EventState)>,
|
||||
window_target: ActiveEventLoop,
|
||||
user_events_receiver: mpsc::Receiver<()>,
|
||||
@@ -323,6 +337,7 @@ impl EventLoop {
|
||||
.map_err(|error| os_error!(format!("{error}")))?;
|
||||
|
||||
Ok(Self {
|
||||
loop_running: false,
|
||||
windows: Vec::new(),
|
||||
window_target: ActiveEventLoop {
|
||||
control_flow: Cell::new(ControlFlow::default()),
|
||||
@@ -508,11 +523,28 @@ impl EventLoop {
|
||||
&mut self,
|
||||
mut app: A,
|
||||
) -> Result<(), EventLoopError> {
|
||||
let mut start_cause = StartCause::Init;
|
||||
loop {
|
||||
app.new_events(&self.window_target, start_cause);
|
||||
match self.pump_app_events(None, &mut app) {
|
||||
PumpStatus::Exit(0) => {
|
||||
break Ok(());
|
||||
},
|
||||
PumpStatus::Exit(code) => {
|
||||
break Err(EventLoopError::ExitFailure(code));
|
||||
},
|
||||
_ => {
|
||||
continue;
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if start_cause == StartCause::Init {
|
||||
fn single_iteration<A: ApplicationHandler>(&mut self, app: &mut A, cause: StartCause) {
|
||||
// TODO: Unindent
|
||||
{
|
||||
app.new_events(&self.window_target, cause);
|
||||
|
||||
if cause == StartCause::Init {
|
||||
// NB: For consistency all platforms must call `can_create_surfaces`
|
||||
app.can_create_surfaces(&self.window_target);
|
||||
}
|
||||
|
||||
@@ -572,7 +604,7 @@ impl EventLoop {
|
||||
orbital_event.to_option(),
|
||||
event_state,
|
||||
&self.window_target,
|
||||
&mut app,
|
||||
app,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -616,75 +648,82 @@ impl EventLoop {
|
||||
}
|
||||
|
||||
app.about_to_wait(&self.window_target);
|
||||
}
|
||||
}
|
||||
|
||||
if self.window_target.exiting() {
|
||||
break;
|
||||
}
|
||||
pub fn pump_app_events<A: ApplicationHandler>(
|
||||
&mut self,
|
||||
timeout: Option<Duration>,
|
||||
mut app: A,
|
||||
) -> PumpStatus {
|
||||
if !self.loop_running {
|
||||
self.loop_running = true;
|
||||
|
||||
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
|
||||
.subscribe(timeout_socket.0.fd(), EventSource::Time, EventFlags::READ)
|
||||
.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 c_long;
|
||||
// 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 event = loop {
|
||||
match self.window_target.event_socket.next_event() {
|
||||
Ok(event) => break event,
|
||||
Err(err) if err.is_interrupt() => continue,
|
||||
Err(err) => {
|
||||
return Err(os_error!(format!("failed to read event: {err}")).into());
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
// TODO: handle spurious wakeups (redraw caused wakeup but redraw already handled)
|
||||
match requested_resume {
|
||||
Some(requested_resume)
|
||||
if event.fd == timeout_socket.0.fd()
|
||||
&& matches!(event.user_data, EventSource::Time) =>
|
||||
{
|
||||
// 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 };
|
||||
},
|
||||
}
|
||||
// Run the initial loop iteration.
|
||||
self.single_iteration(&mut app, StartCause::Init);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
if self.window_target.exit.get() {
|
||||
self.loop_running = false;
|
||||
// TODO: other exit codes
|
||||
return PumpStatus::Exit(0);
|
||||
}
|
||||
|
||||
let start = Instant::now();
|
||||
let timeout = {
|
||||
let requested_resume = match self.window_target.control_flow() {
|
||||
ControlFlow::Poll => Some(Duration::ZERO),
|
||||
ControlFlow::Wait => None,
|
||||
ControlFlow::WaitUntil(instant) => Some(instant.saturating_duration_since(start)),
|
||||
};
|
||||
min_timeout(timeout, requested_resume)
|
||||
};
|
||||
|
||||
if let Some(timeout) = timeout {
|
||||
self.window_target
|
||||
.event_socket
|
||||
.subscribe(
|
||||
EVENT_TIMEOUT_ID,
|
||||
UserData::from_user_data(timeout.as_millis() as usize),
|
||||
EventFlags::READ,
|
||||
)
|
||||
.expect("failed to register EVENT_TIMEOUT_ID")
|
||||
}
|
||||
|
||||
// Wait for event if needed.
|
||||
let event = loop {
|
||||
match self.window_target.event_socket.next_event() {
|
||||
Ok(event) => break event,
|
||||
Err(err) if err.is_interrupt() => continue,
|
||||
Err(err) => {
|
||||
panic!("failed to read event: {err}");
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
if timeout.is_some() && event.fd == EVENT_TIMEOUT_ID {
|
||||
// NB: EVENT_TIMEOUT_ID is not a regular event ID.
|
||||
// Here nothing is happened yet.
|
||||
return PumpStatus::Continue;
|
||||
}
|
||||
|
||||
// Normal window event or spurious timeout.
|
||||
let cause = match self.window_target.control_flow() {
|
||||
ControlFlow::Poll => StartCause::Poll,
|
||||
ControlFlow::Wait => StartCause::WaitCancelled { start, requested_resume: None },
|
||||
ControlFlow::WaitUntil(deadline) => {
|
||||
if Instant::now() < deadline {
|
||||
StartCause::WaitCancelled { start, requested_resume: Some(deadline) }
|
||||
} else {
|
||||
StartCause::ResumeTimeReached { start, requested_resume: deadline }
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
// Do actual event processing
|
||||
self.single_iteration(&mut app, cause);
|
||||
|
||||
PumpStatus::Continue
|
||||
}
|
||||
|
||||
pub fn window_target(&self) -> &dyn RootActiveEventLoop {
|
||||
@@ -802,3 +841,10 @@ impl rwh_06::HasDisplayHandle for OwnedDisplayHandle {
|
||||
|
||||
#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct PlatformSpecificEventLoopAttributes {}
|
||||
|
||||
/// Returns the minimum `Option<Duration>`, taking into account that `None`
|
||||
/// equates to an infinite timeout, not a zero timeout (so can't just use
|
||||
/// `Option::min`)
|
||||
fn min_timeout(a: Option<Duration>, b: Option<Duration>) -> Option<Duration> {
|
||||
a.map_or(b, |a_timeout| b.map_or(Some(a_timeout), |b_timeout| Some(a_timeout.min(b_timeout))))
|
||||
}
|
||||
|
||||
@@ -66,6 +66,7 @@ impl TimeSocket {
|
||||
}
|
||||
|
||||
// Read current time.
|
||||
#[allow(unused)]
|
||||
fn current_time(&self) -> Result<TimeSpec> {
|
||||
let mut timespec: libredox::data::TimeSpec = unsafe { mem::zeroed() };
|
||||
let timespec_bytes = unsafe {
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
#![allow(clippy::single_match)]
|
||||
|
||||
// Limit this example to only compatible platforms.
|
||||
#[cfg(any(windows_platform, macos_platform, x11_platform, wayland_platform, android_platform,))]
|
||||
#[cfg(any(
|
||||
windows_platform,
|
||||
macos_platform,
|
||||
x11_platform,
|
||||
wayland_platform,
|
||||
android_platform,
|
||||
orbital_platform,
|
||||
))]
|
||||
fn main() -> std::process::ExitCode {
|
||||
use std::process::ExitCode;
|
||||
use std::thread::sleep;
|
||||
@@ -81,7 +88,7 @@ fn main() -> std::process::ExitCode {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(ios_platform, web_platform, orbital_platform))]
|
||||
#[cfg(any(ios_platform, web_platform))]
|
||||
fn main() {
|
||||
panic!("This platform doesn't support pump_events.")
|
||||
}
|
||||
|
||||
@@ -45,6 +45,7 @@ changelog entry.
|
||||
- Add `keyboard` support for OpenHarmony.
|
||||
- On iOS, add Apple Pencil support with force, altitude, and azimuth data.
|
||||
- On Redox, add support for missing keyboard scancodes.
|
||||
- On Redox, add support for `EventLoopExtPumpEvents::pump_app_events`.
|
||||
- Implement `Send` and `Sync` for `OwnedDisplayHandle`.
|
||||
- Use new macOS 15 cursors for resize icons.
|
||||
- On Android, added scancode conversions for more obscure key codes.
|
||||
|
||||
@@ -326,6 +326,7 @@ impl AsRawFd for EventLoop {
|
||||
windows_platform,
|
||||
macos_platform,
|
||||
android_platform,
|
||||
orbital_platform,
|
||||
x11_platform,
|
||||
wayland_platform,
|
||||
docsrs,
|
||||
|
||||
@@ -269,7 +269,7 @@
|
||||
//! [`raw_window_handle`]: ./window/struct.Window.html#method.raw_window_handle
|
||||
//! [`raw_display_handle`]: ./window/struct.Window.html#method.raw_display_handle
|
||||
//! [`EventLoopExtPumpEvents::pump_app_events()`]: crate::event_loop::pump_events::EventLoopExtPumpEvents::pump_app_events()
|
||||
//! [^1]: `EventLoopExtPumpEvents::pump_app_events()` is only available on Windows, macOS, Android, X11 and Wayland.
|
||||
//! [^1]: `EventLoopExtPumpEvents::pump_app_events()` is only available on Windows, macOS, Android, Redox, X11 and Wayland.
|
||||
|
||||
#![deny(rust_2018_idioms)]
|
||||
#![deny(rustdoc::broken_intra_doc_links)]
|
||||
|
||||
Reference in New Issue
Block a user