Skip to content

Commit d84ec64

Browse files
willnodejackpot51
authored andcommitted
winit-orbital: Implement pump_app_events
1 parent 47dada2 commit d84ec64

7 files changed

Lines changed: 123 additions & 67 deletions

File tree

Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,8 @@ xkbcommon-dl = "0.4.2"
8181

8282
# Orbital dependencies.
8383
libredox = "0.1.12"
84-
orbclient = { version = "0.3.47", default-features = false }
85-
redox_event = { package = "redox_event", version = "0.4.5" }
84+
orbclient = { version = "0.4.3", default-features = false }
85+
redox_event = { package = "redox_event", version = "0.4.8" }
8686

8787
# Web dependencies.
8888
atomic-waker = "1"

winit-orbital/src/event_loop.rs

Lines changed: 108 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,22 @@
11
use std::cell::Cell;
22
use std::collections::VecDeque;
3-
use std::os::raw::c_long;
43
use std::sync::atomic::{AtomicBool, Ordering};
54
use std::sync::{Arc, Mutex, mpsc};
6-
use std::time::Instant;
5+
use std::time::{Duration, Instant};
76
use std::{iter, mem, slice};
87

98
use bitflags::bitflags;
109
use orbclient::{
1110
ButtonEvent, EventOption, FocusEvent, HoverEvent, KeyEvent, MouseEvent, MouseRelativeEvent,
1211
MoveEvent, QuitEvent, ResizeEvent, ScrollEvent, TextInputEvent,
1312
};
14-
use redox_event::{EventFlags, EventQueue};
13+
use redox_event::{EventFlags, EventQueue, UserData};
1514
use smol_str::SmolStr;
1615
use winit_core::application::ApplicationHandler;
1716
use winit_core::cursor::{CustomCursor, CustomCursorSource};
1817
use winit_core::error::{EventLoopError, NotSupportedError, RequestError};
1918
use winit_core::event::{self, Ime, Modifiers, StartCause};
19+
use winit_core::event_loop::pump_events::PumpStatus;
2020
use winit_core::event_loop::{
2121
ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents,
2222
EventLoopProxy as CoreEventLoopProxy, EventLoopProxyProvider,
@@ -31,6 +31,9 @@ use winit_core::window::{Theme, Window as CoreWindow, WindowId};
3131
use crate::window::Window;
3232
use crate::{RedoxSocket, TimeSocket, WindowProperties};
3333

34+
/// timeout from redox_syscall
35+
const EVENT_TIMEOUT_ID: usize = usize::MAX - 2;
36+
3437
fn convert_scancode(scancode: u8) -> (PhysicalKey, Option<NamedKey>) {
3538
// Key constants from https://docs.rs/orbclient/latest/orbclient/event/index.html
3639
let (key_code, named_key_opt) = match scancode {
@@ -157,6 +160,15 @@ fn convert_scancode(scancode: u8) -> (PhysicalKey, Option<NamedKey>) {
157160
(PhysicalKey::Code(key_code), named_key_opt)
158161
}
159162

163+
pub fn scancode_to_physicalkey(scancode: u32) -> PhysicalKey {
164+
convert_scancode(scancode.try_into().unwrap_or_default()).0
165+
}
166+
167+
pub fn physicalkey_to_scancode(_physical_key: PhysicalKey) -> Option<u32> {
168+
// TODO
169+
None
170+
}
171+
160172
fn element_state(pressed: bool) -> event::ElementState {
161173
if pressed { event::ElementState::Pressed } else { event::ElementState::Released }
162174
}
@@ -296,6 +308,8 @@ impl EventState {
296308

297309
#[derive(Debug)]
298310
pub struct EventLoop {
311+
/// Has `run` or `run_on_demand` been called or a call to `pump_events` that starts the loop
312+
loop_running: bool,
299313
windows: Vec<(Arc<RedoxSocket>, EventState)>,
300314
window_target: ActiveEventLoop,
301315
user_events_receiver: mpsc::Receiver<()>,
@@ -323,6 +337,7 @@ impl EventLoop {
323337
.map_err(|error| os_error!(format!("{error}")))?;
324338

325339
Ok(Self {
340+
loop_running: false,
326341
windows: Vec::new(),
327342
window_target: ActiveEventLoop {
328343
control_flow: Cell::new(ControlFlow::default()),
@@ -508,11 +523,28 @@ impl EventLoop {
508523
&mut self,
509524
mut app: A,
510525
) -> Result<(), EventLoopError> {
511-
let mut start_cause = StartCause::Init;
512526
loop {
513-
app.new_events(&self.window_target, start_cause);
527+
match self.pump_app_events(None, &mut app) {
528+
PumpStatus::Exit(0) => {
529+
break Ok(());
530+
},
531+
PumpStatus::Exit(code) => {
532+
break Err(EventLoopError::ExitFailure(code));
533+
},
534+
_ => {
535+
continue;
536+
},
537+
}
538+
}
539+
}
540+
541+
fn single_iteration<A: ApplicationHandler>(&mut self, app: &mut A, cause: StartCause) {
542+
// TODO: Unindent
543+
{
544+
app.new_events(&self.window_target, cause);
514545

515-
if start_cause == StartCause::Init {
546+
if cause == StartCause::Init {
547+
// NB: For consistency all platforms must call `can_create_surfaces`
516548
app.can_create_surfaces(&self.window_target);
517549
}
518550

@@ -572,7 +604,7 @@ impl EventLoop {
572604
orbital_event.to_option(),
573605
event_state,
574606
&self.window_target,
575-
&mut app,
607+
app,
576608
);
577609
}
578610

@@ -616,75 +648,82 @@ impl EventLoop {
616648
}
617649

618650
app.about_to_wait(&self.window_target);
651+
}
652+
}
619653

620-
if self.window_target.exiting() {
621-
break;
622-
}
654+
pub fn pump_app_events<A: ApplicationHandler>(
655+
&mut self,
656+
timeout: Option<Duration>,
657+
mut app: A,
658+
) -> PumpStatus {
659+
if !self.loop_running {
660+
self.loop_running = true;
661+
662+
// Run the initial loop iteration.
663+
self.single_iteration(&mut app, StartCause::Init);
664+
}
665+
666+
if self.window_target.exit.get() {
667+
self.loop_running = false;
668+
// TODO: other exit codes
669+
return PumpStatus::Exit(0);
670+
}
623671

672+
let start = Instant::now();
673+
let timeout = {
624674
let requested_resume = match self.window_target.control_flow() {
625-
ControlFlow::Poll => {
626-
start_cause = StartCause::Poll;
627-
continue;
628-
},
675+
ControlFlow::Poll => Some(Duration::ZERO),
629676
ControlFlow::Wait => None,
630-
ControlFlow::WaitUntil(instant) => Some(instant),
677+
ControlFlow::WaitUntil(instant) => Some(instant.saturating_duration_since(start)),
631678
};
679+
min_timeout(timeout, requested_resume)
680+
};
632681

633-
// Re-using wake socket caused extra wake events before because there were leftover
634-
// timeouts, and then new timeouts were added each time a spurious timeout expired.
635-
let timeout_socket = TimeSocket::open().unwrap();
636-
682+
if let Some(timeout) = timeout {
637683
self.window_target
638684
.event_socket
639-
.subscribe(timeout_socket.0.fd(), EventSource::Time, EventFlags::READ)
640-
.unwrap();
641-
642-
let start = Instant::now();
643-
if let Some(instant) = requested_resume {
644-
let mut time = timeout_socket.current_time().unwrap();
645-
646-
if let Some(duration) = instant.checked_duration_since(start) {
647-
time.tv_sec += duration.as_secs() as i64;
648-
time.tv_nsec += duration.subsec_nanos() as c_long;
649-
// Normalize timespec so tv_nsec is not greater than one second.
650-
while time.tv_nsec >= 1_000_000_000 {
651-
time.tv_sec += 1;
652-
time.tv_nsec -= 1_000_000_000;
653-
}
654-
}
685+
.subscribe(
686+
EVENT_TIMEOUT_ID,
687+
UserData::from_user_data(timeout.as_millis() as usize),
688+
EventFlags::READ,
689+
)
690+
.expect("failed to register EVENT_TIMEOUT_ID")
691+
}
655692

656-
timeout_socket.timeout(&time).unwrap();
693+
// Wait for event if needed.
694+
let event = loop {
695+
match self.window_target.event_socket.next_event() {
696+
Ok(event) => break event,
697+
Err(err) if err.is_interrupt() => continue,
698+
Err(err) => {
699+
panic!("failed to read event: {err}");
700+
},
657701
}
702+
};
658703

659-
// Wait for event if needed.
660-
let event = loop {
661-
match self.window_target.event_socket.next_event() {
662-
Ok(event) => break event,
663-
Err(err) if err.is_interrupt() => continue,
664-
Err(err) => {
665-
return Err(os_error!(format!("failed to read event: {err}")).into());
666-
},
704+
if timeout.is_some() && event.fd == EVENT_TIMEOUT_ID {
705+
// NB: EVENT_TIMEOUT_ID is not a regular event ID.
706+
// Here nothing is happened yet.
707+
return PumpStatus::Continue;
708+
}
709+
710+
// Normal window event or spurious timeout.
711+
let cause = match self.window_target.control_flow() {
712+
ControlFlow::Poll => StartCause::Poll,
713+
ControlFlow::Wait => StartCause::WaitCancelled { start, requested_resume: None },
714+
ControlFlow::WaitUntil(deadline) => {
715+
if Instant::now() < deadline {
716+
StartCause::WaitCancelled { start, requested_resume: Some(deadline) }
717+
} else {
718+
StartCause::ResumeTimeReached { start, requested_resume: deadline }
667719
}
668-
};
720+
},
721+
};
669722

670-
// TODO: handle spurious wakeups (redraw caused wakeup but redraw already handled)
671-
match requested_resume {
672-
Some(requested_resume)
673-
if event.fd == timeout_socket.0.fd()
674-
&& matches!(event.user_data, EventSource::Time) =>
675-
{
676-
// If the event is from the special timeout socket, report that resume
677-
// time was reached.
678-
start_cause = StartCause::ResumeTimeReached { start, requested_resume };
679-
},
680-
_ => {
681-
// Normal window event or spurious timeout.
682-
start_cause = StartCause::WaitCancelled { start, requested_resume };
683-
},
684-
}
685-
}
723+
// Do actual event processing
724+
self.single_iteration(&mut app, cause);
686725

687-
Ok(())
726+
PumpStatus::Continue
688727
}
689728

690729
pub fn window_target(&self) -> &dyn RootActiveEventLoop {
@@ -802,3 +841,10 @@ impl rwh_06::HasDisplayHandle for OwnedDisplayHandle {
802841

803842
#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Hash)]
804843
pub struct PlatformSpecificEventLoopAttributes {}
844+
845+
/// Returns the minimum `Option<Duration>`, taking into account that `None`
846+
/// equates to an infinite timeout, not a zero timeout (so can't just use
847+
/// `Option::min`)
848+
fn min_timeout(a: Option<Duration>, b: Option<Duration>) -> Option<Duration> {
849+
a.map_or(b, |a_timeout| b.map_or(Some(a_timeout), |b_timeout| Some(a_timeout.min(b_timeout))))
850+
}

winit-orbital/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ impl TimeSocket {
6666
}
6767

6868
// Read current time.
69+
#[allow(unused)]
6970
fn current_time(&self) -> Result<TimeSpec> {
7071
let mut timespec: libredox::data::TimeSpec = unsafe { mem::zeroed() };
7172
let timespec_bytes = unsafe {

winit/examples/pump_events.rs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
11
#![allow(clippy::single_match)]
22

33
// Limit this example to only compatible platforms.
4-
#[cfg(any(windows_platform, macos_platform, x11_platform, wayland_platform, android_platform,))]
4+
#[cfg(any(
5+
windows_platform,
6+
macos_platform,
7+
x11_platform,
8+
wayland_platform,
9+
android_platform,
10+
orbital_platform,
11+
))]
512
fn main() -> std::process::ExitCode {
613
use std::process::ExitCode;
714
use std::thread::sleep;
@@ -81,7 +88,7 @@ fn main() -> std::process::ExitCode {
8188
}
8289
}
8390

84-
#[cfg(any(ios_platform, web_platform, orbital_platform))]
91+
#[cfg(any(ios_platform, web_platform))]
8592
fn main() {
8693
panic!("This platform doesn't support pump_events.")
8794
}

winit/src/changelog/unreleased.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ changelog entry.
4545
- Add `keyboard` support for OpenHarmony.
4646
- On iOS, add Apple Pencil support with force, altitude, and azimuth data.
4747
- On Redox, add support for missing keyboard scancodes.
48+
- On Redox, add support for `EventLoopExtPumpEvents::pump_app_events`.
4849
- Implement `Send` and `Sync` for `OwnedDisplayHandle`.
4950
- Use new macOS 15 cursors for resize icons.
5051
- On Android, added scancode conversions for more obscure key codes.

winit/src/event_loop.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,7 @@ impl AsRawFd for EventLoop {
326326
windows_platform,
327327
macos_platform,
328328
android_platform,
329+
orbital_platform,
329330
x11_platform,
330331
wayland_platform,
331332
docsrs,

winit/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,7 @@
269269
//! [`raw_window_handle`]: ./window/struct.Window.html#method.raw_window_handle
270270
//! [`raw_display_handle`]: ./window/struct.Window.html#method.raw_display_handle
271271
//! [`EventLoopExtPumpEvents::pump_app_events()`]: crate::event_loop::pump_events::EventLoopExtPumpEvents::pump_app_events()
272-
//! [^1]: `EventLoopExtPumpEvents::pump_app_events()` is only available on Windows, macOS, Android, X11 and Wayland.
272+
//! [^1]: `EventLoopExtPumpEvents::pump_app_events()` is only available on Windows, macOS, Android, Redox, X11 and Wayland.
273273
274274
#![deny(rust_2018_idioms)]
275275
#![deny(rustdoc::broken_intra_doc_links)]

0 commit comments

Comments
 (0)