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.
|
# Orbital dependencies.
|
||||||
libredox = "0.1.12"
|
libredox = "0.1.12"
|
||||||
orbclient = { version = "0.3.47", default-features = false }
|
orbclient = { version = "0.4.3", default-features = false }
|
||||||
redox_event = { package = "redox_event", version = "0.4.5" }
|
redox_event = { package = "redox_event", version = "0.4.8" }
|
||||||
|
|
||||||
# Web dependencies.
|
# Web dependencies.
|
||||||
atomic-waker = "1"
|
atomic-waker = "1"
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use std::os::raw::c_long;
|
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::sync::{Arc, Mutex, mpsc};
|
use std::sync::{Arc, Mutex, mpsc};
|
||||||
use std::time::Instant;
|
use std::time::{Duration, Instant};
|
||||||
use std::{iter, mem, slice};
|
use std::{iter, mem, slice};
|
||||||
|
|
||||||
use bitflags::bitflags;
|
use bitflags::bitflags;
|
||||||
@@ -11,12 +10,13 @@ use orbclient::{
|
|||||||
ButtonEvent, EventOption, FocusEvent, HoverEvent, KeyEvent, MouseEvent, MouseRelativeEvent,
|
ButtonEvent, EventOption, FocusEvent, HoverEvent, KeyEvent, MouseEvent, MouseRelativeEvent,
|
||||||
MoveEvent, QuitEvent, ResizeEvent, ScrollEvent, TextInputEvent,
|
MoveEvent, QuitEvent, ResizeEvent, ScrollEvent, TextInputEvent,
|
||||||
};
|
};
|
||||||
use redox_event::{EventFlags, EventQueue};
|
use redox_event::{EventFlags, EventQueue, UserData};
|
||||||
use smol_str::SmolStr;
|
use smol_str::SmolStr;
|
||||||
use winit_core::application::ApplicationHandler;
|
use winit_core::application::ApplicationHandler;
|
||||||
use winit_core::cursor::{CustomCursor, CustomCursorSource};
|
use winit_core::cursor::{CustomCursor, CustomCursorSource};
|
||||||
use winit_core::error::{EventLoopError, NotSupportedError, RequestError};
|
use winit_core::error::{EventLoopError, NotSupportedError, RequestError};
|
||||||
use winit_core::event::{self, Ime, Modifiers, StartCause};
|
use winit_core::event::{self, Ime, Modifiers, StartCause};
|
||||||
|
use winit_core::event_loop::pump_events::PumpStatus;
|
||||||
use winit_core::event_loop::{
|
use winit_core::event_loop::{
|
||||||
ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents,
|
ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents,
|
||||||
EventLoopProxy as CoreEventLoopProxy, EventLoopProxyProvider,
|
EventLoopProxy as CoreEventLoopProxy, EventLoopProxyProvider,
|
||||||
@@ -31,6 +31,9 @@ use winit_core::window::{Theme, Window as CoreWindow, WindowId};
|
|||||||
use crate::window::Window;
|
use crate::window::Window;
|
||||||
use crate::{RedoxSocket, TimeSocket, WindowProperties};
|
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>) {
|
fn convert_scancode(scancode: u8) -> (PhysicalKey, Option<NamedKey>) {
|
||||||
// Key constants from https://docs.rs/orbclient/latest/orbclient/event/index.html
|
// Key constants from https://docs.rs/orbclient/latest/orbclient/event/index.html
|
||||||
let (key_code, named_key_opt) = match scancode {
|
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)
|
(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 {
|
fn element_state(pressed: bool) -> event::ElementState {
|
||||||
if pressed { event::ElementState::Pressed } else { event::ElementState::Released }
|
if pressed { event::ElementState::Pressed } else { event::ElementState::Released }
|
||||||
}
|
}
|
||||||
@@ -296,6 +308,8 @@ impl EventState {
|
|||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct EventLoop {
|
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)>,
|
windows: Vec<(Arc<RedoxSocket>, EventState)>,
|
||||||
window_target: ActiveEventLoop,
|
window_target: ActiveEventLoop,
|
||||||
user_events_receiver: mpsc::Receiver<()>,
|
user_events_receiver: mpsc::Receiver<()>,
|
||||||
@@ -323,6 +337,7 @@ impl EventLoop {
|
|||||||
.map_err(|error| os_error!(format!("{error}")))?;
|
.map_err(|error| os_error!(format!("{error}")))?;
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
|
loop_running: false,
|
||||||
windows: Vec::new(),
|
windows: Vec::new(),
|
||||||
window_target: ActiveEventLoop {
|
window_target: ActiveEventLoop {
|
||||||
control_flow: Cell::new(ControlFlow::default()),
|
control_flow: Cell::new(ControlFlow::default()),
|
||||||
@@ -508,11 +523,28 @@ impl EventLoop {
|
|||||||
&mut self,
|
&mut self,
|
||||||
mut app: A,
|
mut app: A,
|
||||||
) -> Result<(), EventLoopError> {
|
) -> Result<(), EventLoopError> {
|
||||||
let mut start_cause = StartCause::Init;
|
|
||||||
loop {
|
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);
|
app.can_create_surfaces(&self.window_target);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -572,7 +604,7 @@ impl EventLoop {
|
|||||||
orbital_event.to_option(),
|
orbital_event.to_option(),
|
||||||
event_state,
|
event_state,
|
||||||
&self.window_target,
|
&self.window_target,
|
||||||
&mut app,
|
app,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -616,75 +648,82 @@ impl EventLoop {
|
|||||||
}
|
}
|
||||||
|
|
||||||
app.about_to_wait(&self.window_target);
|
app.about_to_wait(&self.window_target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if self.window_target.exiting() {
|
pub fn pump_app_events<A: ApplicationHandler>(
|
||||||
break;
|
&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() {
|
// Run the initial loop iteration.
|
||||||
ControlFlow::Poll => {
|
self.single_iteration(&mut app, StartCause::Init);
|
||||||
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 };
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
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)]
|
#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||||
pub struct PlatformSpecificEventLoopAttributes {}
|
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.
|
// Read current time.
|
||||||
|
#[allow(unused)]
|
||||||
fn current_time(&self) -> Result<TimeSpec> {
|
fn current_time(&self) -> Result<TimeSpec> {
|
||||||
let mut timespec: libredox::data::TimeSpec = unsafe { mem::zeroed() };
|
let mut timespec: libredox::data::TimeSpec = unsafe { mem::zeroed() };
|
||||||
let timespec_bytes = unsafe {
|
let timespec_bytes = unsafe {
|
||||||
|
|||||||
@@ -1,7 +1,14 @@
|
|||||||
#![allow(clippy::single_match)]
|
#![allow(clippy::single_match)]
|
||||||
|
|
||||||
// Limit this example to only compatible platforms.
|
// 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 {
|
fn main() -> std::process::ExitCode {
|
||||||
use std::process::ExitCode;
|
use std::process::ExitCode;
|
||||||
use std::thread::sleep;
|
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() {
|
fn main() {
|
||||||
panic!("This platform doesn't support pump_events.")
|
panic!("This platform doesn't support pump_events.")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ changelog entry.
|
|||||||
- Add `keyboard` support for OpenHarmony.
|
- Add `keyboard` support for OpenHarmony.
|
||||||
- On iOS, add Apple Pencil support with force, altitude, and azimuth data.
|
- 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 missing keyboard scancodes.
|
||||||
|
- On Redox, add support for `EventLoopExtPumpEvents::pump_app_events`.
|
||||||
- Implement `Send` and `Sync` for `OwnedDisplayHandle`.
|
- Implement `Send` and `Sync` for `OwnedDisplayHandle`.
|
||||||
- Use new macOS 15 cursors for resize icons.
|
- Use new macOS 15 cursors for resize icons.
|
||||||
- On Android, added scancode conversions for more obscure key codes.
|
- On Android, added scancode conversions for more obscure key codes.
|
||||||
|
|||||||
@@ -326,6 +326,7 @@ impl AsRawFd for EventLoop {
|
|||||||
windows_platform,
|
windows_platform,
|
||||||
macos_platform,
|
macos_platform,
|
||||||
android_platform,
|
android_platform,
|
||||||
|
orbital_platform,
|
||||||
x11_platform,
|
x11_platform,
|
||||||
wayland_platform,
|
wayland_platform,
|
||||||
docsrs,
|
docsrs,
|
||||||
|
|||||||
@@ -269,7 +269,7 @@
|
|||||||
//! [`raw_window_handle`]: ./window/struct.Window.html#method.raw_window_handle
|
//! [`raw_window_handle`]: ./window/struct.Window.html#method.raw_window_handle
|
||||||
//! [`raw_display_handle`]: ./window/struct.Window.html#method.raw_display_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()
|
//! [`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(rust_2018_idioms)]
|
||||||
#![deny(rustdoc::broken_intra_doc_links)]
|
#![deny(rustdoc::broken_intra_doc_links)]
|
||||||
|
|||||||
Reference in New Issue
Block a user