mirror of
https://github.com/rust-windowing/winit.git
synced 2026-06-26 22:53:15 -04:00
Compare commits
22 Commits
v0.30.1
...
madsmtm/ve
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
89687656d6 | ||
|
|
9027d8f350 | ||
|
|
5d8091fc7f | ||
|
|
d7abe0316e | ||
|
|
5ea20fc905 | ||
|
|
e108fa2fbf | ||
|
|
fff6788c12 | ||
|
|
3e8fa41073 | ||
|
|
2b1c8cea1b | ||
|
|
ab33fb8eda | ||
|
|
b0b64a9a15 | ||
|
|
d5d202d60e | ||
|
|
cb39ab29f4 | ||
|
|
0a3cacd577 | ||
|
|
16fd2baba0 | ||
|
|
7f8771a362 | ||
|
|
337d50779c | ||
|
|
fd477986de | ||
|
|
94664ff687 | ||
|
|
0fc8c37721 | ||
|
|
ec29c81ad2 | ||
|
|
304a585493 |
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "winit"
|
||||
version = "0.30.1"
|
||||
version = "0.30.0"
|
||||
authors = [
|
||||
"The winit contributors",
|
||||
"Pierre Krieger <pierre.krieger1708@gmail.com>",
|
||||
@@ -112,12 +112,10 @@ objc2 = "0.5.2"
|
||||
|
||||
[target.'cfg(target_os = "macos")'.dependencies]
|
||||
core-graphics = "0.23.1"
|
||||
block2 = "0.5.1"
|
||||
|
||||
[target.'cfg(target_os = "macos")'.dependencies.objc2-foundation]
|
||||
version = "0.2.2"
|
||||
features = [
|
||||
"block2",
|
||||
"dispatch",
|
||||
"NSArray",
|
||||
"NSAttributedString",
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
winit = "0.30.1"
|
||||
winit = "0.30.0"
|
||||
```
|
||||
|
||||
## [Documentation](https://docs.rs/winit)
|
||||
|
||||
@@ -3,50 +3,45 @@
|
||||
fn main() -> Result<(), impl std::error::Error> {
|
||||
use std::collections::HashMap;
|
||||
|
||||
use winit::application::ApplicationHandler;
|
||||
use winit::dpi::{LogicalPosition, LogicalSize, Position};
|
||||
use winit::event::{ElementState, Event, KeyEvent, WindowEvent};
|
||||
use winit::event::{ElementState, KeyEvent, WindowEvent};
|
||||
use winit::event_loop::{ActiveEventLoop, EventLoop};
|
||||
use winit::raw_window_handle::HasRawWindowHandle;
|
||||
use winit::window::Window;
|
||||
use winit::window::{Window, WindowId};
|
||||
|
||||
#[path = "util/fill.rs"]
|
||||
mod fill;
|
||||
|
||||
fn spawn_child_window(parent: &Window, event_loop: &ActiveEventLoop) -> Window {
|
||||
let parent = parent.raw_window_handle().unwrap();
|
||||
let mut window_attributes = Window::default_attributes()
|
||||
.with_title("child window")
|
||||
.with_inner_size(LogicalSize::new(200.0f32, 200.0f32))
|
||||
.with_position(Position::Logical(LogicalPosition::new(0.0, 0.0)))
|
||||
.with_visible(true);
|
||||
// `with_parent_window` is unsafe. Parent window must be a valid window.
|
||||
window_attributes = unsafe { window_attributes.with_parent_window(Some(parent)) };
|
||||
|
||||
event_loop.create_window(window_attributes).unwrap()
|
||||
#[derive(Default)]
|
||||
struct Application {
|
||||
parent_window_id: Option<WindowId>,
|
||||
windows: HashMap<WindowId, Window>,
|
||||
}
|
||||
|
||||
let mut windows = HashMap::new();
|
||||
impl ApplicationHandler for Application {
|
||||
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
|
||||
let attributes = Window::default_attributes()
|
||||
.with_title("parent window")
|
||||
.with_position(Position::Logical(LogicalPosition::new(0.0, 0.0)))
|
||||
.with_inner_size(LogicalSize::new(640.0f32, 480.0f32));
|
||||
let window = event_loop.create_window(attributes).unwrap();
|
||||
|
||||
let event_loop: EventLoop<()> = EventLoop::new().unwrap();
|
||||
let mut parent_window_id = None;
|
||||
println!("Parent window id: {:?})", window.id());
|
||||
self.parent_window_id = Some(window.id());
|
||||
|
||||
event_loop.run(move |event: Event<()>, event_loop| {
|
||||
match event {
|
||||
Event::Resumed => {
|
||||
let attributes = Window::default_attributes()
|
||||
.with_title("parent window")
|
||||
.with_position(Position::Logical(LogicalPosition::new(0.0, 0.0)))
|
||||
.with_inner_size(LogicalSize::new(640.0f32, 480.0f32));
|
||||
let window = event_loop.create_window(attributes).unwrap();
|
||||
self.windows.insert(window.id(), window);
|
||||
}
|
||||
|
||||
parent_window_id = Some(window.id());
|
||||
|
||||
println!("Parent window id: {parent_window_id:?})");
|
||||
windows.insert(window.id(), window);
|
||||
},
|
||||
Event::WindowEvent { window_id, event } => match event {
|
||||
fn window_event(
|
||||
&mut self,
|
||||
event_loop: &ActiveEventLoop,
|
||||
window_id: winit::window::WindowId,
|
||||
event: WindowEvent,
|
||||
) {
|
||||
match event {
|
||||
WindowEvent::CloseRequested => {
|
||||
windows.clear();
|
||||
self.windows.clear();
|
||||
event_loop.exit();
|
||||
},
|
||||
WindowEvent::CursorEntered { device_id: _ } => {
|
||||
@@ -61,22 +56,38 @@ fn main() -> Result<(), impl std::error::Error> {
|
||||
event: KeyEvent { state: ElementState::Pressed, .. },
|
||||
..
|
||||
} => {
|
||||
let parent_window = windows.get(&parent_window_id.unwrap()).unwrap();
|
||||
let parent_window = self.windows.get(&self.parent_window_id.unwrap()).unwrap();
|
||||
let child_window = spawn_child_window(parent_window, event_loop);
|
||||
let child_id = child_window.id();
|
||||
println!("Child window created with id: {child_id:?}");
|
||||
windows.insert(child_id, child_window);
|
||||
self.windows.insert(child_id, child_window);
|
||||
},
|
||||
WindowEvent::RedrawRequested => {
|
||||
if let Some(window) = windows.get(&window_id) {
|
||||
if let Some(window) = self.windows.get(&window_id) {
|
||||
fill::fill_window(window);
|
||||
}
|
||||
},
|
||||
_ => (),
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn spawn_child_window(parent: &Window, event_loop: &ActiveEventLoop) -> Window {
|
||||
let parent = parent.raw_window_handle().unwrap();
|
||||
let mut window_attributes = Window::default_attributes()
|
||||
.with_title("child window")
|
||||
.with_inner_size(LogicalSize::new(200.0f32, 200.0f32))
|
||||
.with_position(Position::Logical(LogicalPosition::new(0.0, 0.0)))
|
||||
.with_visible(true);
|
||||
// `with_parent_window` is unsafe. Parent window must be a valid window.
|
||||
window_attributes = unsafe { window_attributes.with_parent_window(Some(parent)) };
|
||||
|
||||
event_loop.create_window(window_attributes).unwrap()
|
||||
}
|
||||
|
||||
let event_loop: EventLoop<()> = EventLoop::new().unwrap();
|
||||
let mut app = Application::default();
|
||||
event_loop.run_app(&mut app)
|
||||
}
|
||||
|
||||
#[cfg(all(feature = "rwh_06", not(any(x11_platform, macos_platform, windows_platform))))]
|
||||
|
||||
@@ -17,7 +17,9 @@ use softbuffer::{Context, Surface};
|
||||
|
||||
use winit::application::ApplicationHandler;
|
||||
use winit::dpi::{LogicalSize, PhysicalPosition, PhysicalSize};
|
||||
use winit::event::{DeviceEvent, DeviceId, Ime, MouseButton, MouseScrollDelta, WindowEvent};
|
||||
use winit::event::{
|
||||
DeviceEvent, DeviceId, Ime, MouseButton, MouseScrollDelta, StartCause, WindowEvent,
|
||||
};
|
||||
use winit::event_loop::{ActiveEventLoop, EventLoop};
|
||||
use winit::keyboard::{Key, ModifiersState};
|
||||
use winit::window::{
|
||||
@@ -303,6 +305,12 @@ impl Application {
|
||||
}
|
||||
|
||||
impl ApplicationHandler<UserEvent> for Application {
|
||||
fn new_events(&mut self, _event_loop: &ActiveEventLoop, start_cause: StartCause) {
|
||||
if let StartCause::Init = start_cause {
|
||||
info!("Started the event loop");
|
||||
}
|
||||
}
|
||||
|
||||
fn user_event(&mut self, _event_loop: &ActiveEventLoop, event: UserEvent) {
|
||||
info!("User event: {event:?}");
|
||||
}
|
||||
@@ -320,6 +328,7 @@ impl ApplicationHandler<UserEvent> for Application {
|
||||
|
||||
match event {
|
||||
WindowEvent::Resized(size) => {
|
||||
info!("Resized({size:?})");
|
||||
window.resize(size);
|
||||
},
|
||||
WindowEvent::Focused(focused) => {
|
||||
|
||||
@@ -12,6 +12,7 @@ on how to add them:
|
||||
### Added
|
||||
|
||||
- Add `Window::turbo()`, implemented on X11, Wayland, and Web.
|
||||
- Add traits `EventLoopExtWayland` and `EventLoopExtX11`, providing methods `is_wayland` and `is_x11` on `EventLoop`.
|
||||
- On X11, add `Window::some_rare_api`.
|
||||
- On X11, add `Window::even_more_rare_api`.
|
||||
- On Wayland, add `Window::common_api`.
|
||||
@@ -39,3 +40,19 @@ The migration guide could reference other migration examples in the current
|
||||
changelog entry.
|
||||
|
||||
## Unreleased
|
||||
|
||||
### Added
|
||||
|
||||
- Reexport `raw-window-handle` versions 0.4 and 0.5 as `raw_window_handle_04` and `raw_window_handle_05`.
|
||||
- Implement `ApplicationHandler` for `&mut` references and heap allocations to something that implements `ApplicationHandler`.
|
||||
|
||||
### Removed
|
||||
|
||||
- Remove `EventLoop::run`.
|
||||
- Remove `EventLoopExtRunOnDemand::run_on_demand`.
|
||||
- Remove `EventLoopExtPumpEvents::pump_events`.
|
||||
|
||||
### Fixed
|
||||
|
||||
- On macOS, fix panic on exit when dropping windows outside the event loop.
|
||||
- On macOS, fix window dragging glitches when dragging across a monitor boundary with different scale factor.
|
||||
|
||||
@@ -1,18 +1,3 @@
|
||||
## 0.30.1
|
||||
|
||||
### Added
|
||||
|
||||
- Reexport `raw-window-handle` versions 0.4 and 0.5 as `raw_window_handle_04` and `raw_window_handle_05`.
|
||||
- Implement `ApplicationHandler` for `&mut` references and heap allocations to something that implements `ApplicationHandler`.
|
||||
|
||||
### Fixed
|
||||
|
||||
- On macOS, fix panic on exit when dropping windows outside the event loop.
|
||||
- On macOS, fix window dragging glitches when dragging across a monitor boundary with different scale factor.
|
||||
- On macOS, fix the range in `Ime::Preedit`.
|
||||
- On macOS, use the system's internal mechanisms for queuing events.
|
||||
- On macOS, handle events directly instead of queuing when possible.
|
||||
|
||||
## 0.30.0
|
||||
|
||||
### Added
|
||||
@@ -80,7 +65,7 @@
|
||||
`Fn`. The semantics are mostly the same, given that the capture list of the
|
||||
closure is your new `State`. Consider the following code:
|
||||
|
||||
```rust,no_run
|
||||
```rust,no_run,ignore
|
||||
use winit::event::Event;
|
||||
use winit::event_loop::EventLoop;
|
||||
use winit::window::Window;
|
||||
|
||||
@@ -20,7 +20,6 @@ use web_time::{Duration, Instant};
|
||||
|
||||
use crate::application::ApplicationHandler;
|
||||
use crate::error::{EventLoopError, OsError};
|
||||
use crate::event::Event;
|
||||
use crate::monitor::MonitorHandle;
|
||||
use crate::platform_impl;
|
||||
use crate::window::{CustomCursor, CustomCursorSource, Window, WindowAttributes};
|
||||
@@ -152,6 +151,7 @@ impl fmt::Debug for ActiveEventLoop {
|
||||
/// Defaults to [`Wait`].
|
||||
///
|
||||
/// [`Wait`]: Self::Wait
|
||||
/// [`Event::AboutToWait`]: crate::event::Event::AboutToWait
|
||||
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
|
||||
pub enum ControlFlow {
|
||||
/// When the current loop iteration finishes, immediately begin a new iteration regardless of
|
||||
@@ -209,6 +209,110 @@ impl EventLoop<()> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
pub(crate) fn ensure_event_order<'a, T: 'static>(
|
||||
handler: impl ApplicationHandler<T> + 'a,
|
||||
) -> impl ApplicationHandler<T> + 'a {
|
||||
use crate::event::{DeviceEvent, DeviceId, StartCause, WindowEvent};
|
||||
use crate::window::WindowId;
|
||||
|
||||
#[derive(Default, Debug, PartialEq, Eq, Clone)]
|
||||
enum State {
|
||||
#[default]
|
||||
NotRunning,
|
||||
Suspended,
|
||||
Running,
|
||||
Waiting,
|
||||
}
|
||||
|
||||
impl State {
|
||||
#[track_caller]
|
||||
fn expect(&self, expected: State) {
|
||||
if *self != expected {
|
||||
tracing::error!("expected state to be {expected:?}, found {self:?}");
|
||||
}
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn transition(&mut self, from: State, to: State) {
|
||||
if *self != from {
|
||||
tracing::error!(
|
||||
"invalid state transition to {to:?}. Expected {from:?}, found {self:?}"
|
||||
);
|
||||
}
|
||||
*self = to;
|
||||
}
|
||||
}
|
||||
|
||||
struct EnsureEventOrder<A> {
|
||||
inner: A,
|
||||
state: State,
|
||||
}
|
||||
|
||||
impl<A: ApplicationHandler<T>, T: 'static> ApplicationHandler<T> for EnsureEventOrder<A> {
|
||||
fn new_events(&mut self, event_loop: &ActiveEventLoop, cause: StartCause) {
|
||||
match cause {
|
||||
StartCause::Init => self.state.transition(State::NotRunning, State::Suspended),
|
||||
_ => self.state.transition(State::Waiting, State::Running),
|
||||
}
|
||||
|
||||
self.inner.new_events(event_loop, cause);
|
||||
}
|
||||
|
||||
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
|
||||
self.state.transition(State::Suspended, State::Running);
|
||||
self.inner.resumed(event_loop);
|
||||
}
|
||||
|
||||
fn suspended(&mut self, event_loop: &ActiveEventLoop) {
|
||||
self.state.transition(State::Running, State::Suspended);
|
||||
self.inner.suspended(event_loop);
|
||||
}
|
||||
|
||||
fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) {
|
||||
self.state.transition(State::Running, State::Waiting);
|
||||
self.inner.about_to_wait(event_loop);
|
||||
}
|
||||
|
||||
fn exiting(&mut self, event_loop: &ActiveEventLoop) {
|
||||
self.state.transition(State::Suspended, State::NotRunning);
|
||||
self.inner.exiting(event_loop);
|
||||
}
|
||||
|
||||
fn user_event(&mut self, event_loop: &ActiveEventLoop, event: T) {
|
||||
self.state.expect(State::Running);
|
||||
self.inner.user_event(event_loop, event);
|
||||
}
|
||||
|
||||
fn window_event(
|
||||
&mut self,
|
||||
event_loop: &ActiveEventLoop,
|
||||
window_id: WindowId,
|
||||
event: WindowEvent,
|
||||
) {
|
||||
self.state.expect(State::Running);
|
||||
self.inner.window_event(event_loop, window_id, event);
|
||||
}
|
||||
|
||||
fn device_event(
|
||||
&mut self,
|
||||
event_loop: &ActiveEventLoop,
|
||||
device_id: DeviceId,
|
||||
event: DeviceEvent,
|
||||
) {
|
||||
self.state.expect(State::Running);
|
||||
self.inner.device_event(event_loop, device_id, event);
|
||||
}
|
||||
|
||||
fn memory_warning(&mut self, event_loop: &ActiveEventLoop) {
|
||||
// TODO: What states are allowed when receiving this?
|
||||
self.inner.memory_warning(event_loop);
|
||||
}
|
||||
}
|
||||
|
||||
EnsureEventOrder { inner: handler, state: State::NotRunning }
|
||||
}
|
||||
|
||||
impl<T> EventLoop<T> {
|
||||
/// Start building a new event loop, with the given type as the user event
|
||||
/// type.
|
||||
@@ -216,21 +320,6 @@ impl<T> EventLoop<T> {
|
||||
EventLoopBuilder { platform_specific: Default::default(), _p: PhantomData }
|
||||
}
|
||||
|
||||
/// See [`run_app`].
|
||||
///
|
||||
/// [`run_app`]: Self::run_app
|
||||
#[inline]
|
||||
#[deprecated = "use `EventLoop::run_app` instead"]
|
||||
#[cfg(not(all(web_platform, target_feature = "exception-handling")))]
|
||||
pub fn run<F>(self, event_handler: F) -> Result<(), EventLoopError>
|
||||
where
|
||||
F: FnMut(Event<T>, &ActiveEventLoop),
|
||||
{
|
||||
let _span = tracing::debug_span!("winit::EventLoop::run").entered();
|
||||
|
||||
self.event_loop.run(event_handler)
|
||||
}
|
||||
|
||||
/// Run the application with the event loop on the calling thread.
|
||||
///
|
||||
/// See the [`set_control_flow()`] docs on how to change the event loop's behavior.
|
||||
@@ -262,7 +351,9 @@ impl<T> EventLoop<T> {
|
||||
#[inline]
|
||||
#[cfg(not(all(web_platform, target_feature = "exception-handling")))]
|
||||
pub fn run_app<A: ApplicationHandler<T>>(self, app: &mut A) -> Result<(), EventLoopError> {
|
||||
self.event_loop.run(|event, event_loop| dispatch_event_for_app(app, event_loop, event))
|
||||
#[cfg(debug_assertions)]
|
||||
let app = &mut ensure_event_order(app);
|
||||
self.event_loop.run_app(app)
|
||||
}
|
||||
|
||||
/// Creates an [`EventLoopProxy`] that can be used to dispatch user events
|
||||
@@ -449,7 +540,7 @@ impl ActiveEventLoop {
|
||||
|
||||
/// This exits the event loop.
|
||||
///
|
||||
/// See [`LoopExiting`][Event::LoopExiting].
|
||||
/// See [`LoopExiting`][crate::event::Event::LoopExiting].
|
||||
pub fn exit(&self) {
|
||||
let _span = tracing::debug_span!("winit::ActiveEventLoop::exit",).entered();
|
||||
|
||||
@@ -618,23 +709,3 @@ impl AsyncRequestSerial {
|
||||
Self { serial }
|
||||
}
|
||||
}
|
||||
|
||||
/// Shim for various run APIs.
|
||||
#[inline(always)]
|
||||
pub(crate) fn dispatch_event_for_app<T: 'static, A: ApplicationHandler<T>>(
|
||||
app: &mut A,
|
||||
event_loop: &ActiveEventLoop,
|
||||
event: Event<T>,
|
||||
) {
|
||||
match event {
|
||||
Event::NewEvents(cause) => app.new_events(event_loop, cause),
|
||||
Event::WindowEvent { window_id, event } => app.window_event(event_loop, window_id, event),
|
||||
Event::DeviceEvent { device_id, event } => app.device_event(event_loop, device_id, event),
|
||||
Event::UserEvent(event) => app.user_event(event_loop, event),
|
||||
Event::Suspended => app.suspended(event_loop),
|
||||
Event::Resumed => app.resumed(event_loop),
|
||||
Event::AboutToWait => app.about_to_wait(event_loop),
|
||||
Event::LoopExiting => app.exiting(event_loop),
|
||||
Event::MemoryWarning => app.memory_warning(event_loop),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@
|
||||
//! If your application is currently based on `NativeActivity` via the `ndk-glue` crate and building
|
||||
//! with `cargo apk`, then the minimal changes would be:
|
||||
//! 1. Remove `ndk-glue` from your `Cargo.toml`
|
||||
//! 2. Enable the `"android-native-activity"` feature for Winit: `winit = { version = "0.30.1",
|
||||
//! 2. Enable the `"android-native-activity"` feature for Winit: `winit = { version = "0.30.0",
|
||||
//! features = [ "android-native-activity" ] }`
|
||||
//! 3. Add an `android_main` entrypoint (as above), instead of using the '`[ndk_glue::main]` proc
|
||||
//! macro from `ndk-macros` (optionally add a dependency on `android_logger` and initialize
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::application::ApplicationHandler;
|
||||
use crate::event::Event;
|
||||
use crate::event_loop::{self, ActiveEventLoop, EventLoop};
|
||||
use crate::event_loop::EventLoop;
|
||||
|
||||
/// Additional methods on [`EventLoop`] for pumping events within an external event loop
|
||||
pub trait EventLoopExtPumpEvents {
|
||||
/// A type provided by the user that can be passed through [`Event::UserEvent`].
|
||||
///
|
||||
/// [`Event::UserEvent`]: crate::event::Event::UserEvent
|
||||
type UserEvent: 'static;
|
||||
|
||||
/// Pump the `EventLoop` to check for and dispatch pending events.
|
||||
@@ -107,30 +108,18 @@ pub trait EventLoopExtPumpEvents {
|
||||
&mut self,
|
||||
timeout: Option<Duration>,
|
||||
app: &mut A,
|
||||
) -> PumpStatus {
|
||||
#[allow(deprecated)]
|
||||
self.pump_events(timeout, |event, event_loop| {
|
||||
event_loop::dispatch_event_for_app(app, event_loop, event)
|
||||
})
|
||||
}
|
||||
|
||||
/// See [`pump_app_events`].
|
||||
///
|
||||
/// [`pump_app_events`]: Self::pump_app_events
|
||||
#[deprecated = "use EventLoopExtPumpEvents::pump_app_events"]
|
||||
fn pump_events<F>(&mut self, timeout: Option<Duration>, event_handler: F) -> PumpStatus
|
||||
where
|
||||
F: FnMut(Event<Self::UserEvent>, &ActiveEventLoop);
|
||||
) -> PumpStatus;
|
||||
}
|
||||
|
||||
impl<T> EventLoopExtPumpEvents for EventLoop<T> {
|
||||
type UserEvent = T;
|
||||
|
||||
fn pump_events<F>(&mut self, timeout: Option<Duration>, event_handler: F) -> PumpStatus
|
||||
where
|
||||
F: FnMut(Event<Self::UserEvent>, &ActiveEventLoop),
|
||||
{
|
||||
self.event_loop.pump_events(timeout, event_handler)
|
||||
fn pump_app_events<A: ApplicationHandler<Self::UserEvent>>(
|
||||
&mut self,
|
||||
timeout: Option<Duration>,
|
||||
app: &mut A,
|
||||
) -> PumpStatus {
|
||||
self.event_loop.pump_app_events(timeout, app)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
use crate::application::ApplicationHandler;
|
||||
use crate::error::EventLoopError;
|
||||
use crate::event::Event;
|
||||
use crate::event_loop::{self, ActiveEventLoop, EventLoop};
|
||||
use crate::event_loop::{ActiveEventLoop, EventLoop};
|
||||
|
||||
#[cfg(doc)]
|
||||
use crate::{platform::pump_events::EventLoopExtPumpEvents, window::Window};
|
||||
@@ -9,15 +8,9 @@ use crate::{platform::pump_events::EventLoopExtPumpEvents, window::Window};
|
||||
/// Additional methods on [`EventLoop`] to return control flow to the caller.
|
||||
pub trait EventLoopExtRunOnDemand {
|
||||
/// A type provided by the user that can be passed through [`Event::UserEvent`].
|
||||
type UserEvent: 'static;
|
||||
|
||||
/// See [`run_app_on_demand`].
|
||||
///
|
||||
/// [`run_app_on_demand`]: Self::run_app_on_demand
|
||||
#[deprecated = "use EventLoopExtRunOnDemand::run_app_on_demand"]
|
||||
fn run_on_demand<F>(&mut self, event_handler: F) -> Result<(), EventLoopError>
|
||||
where
|
||||
F: FnMut(Event<Self::UserEvent>, &ActiveEventLoop);
|
||||
/// [`Event::UserEvent`]: crate::event::Event::UserEvent
|
||||
type UserEvent: 'static;
|
||||
|
||||
/// Run the application with the event loop on the calling thread.
|
||||
///
|
||||
@@ -68,23 +61,18 @@ pub trait EventLoopExtRunOnDemand {
|
||||
fn run_app_on_demand<A: ApplicationHandler<Self::UserEvent>>(
|
||||
&mut self,
|
||||
app: &mut A,
|
||||
) -> Result<(), EventLoopError> {
|
||||
#[allow(deprecated)]
|
||||
self.run_on_demand(|event, event_loop| {
|
||||
event_loop::dispatch_event_for_app(app, event_loop, event)
|
||||
})
|
||||
}
|
||||
) -> Result<(), EventLoopError>;
|
||||
}
|
||||
|
||||
impl<T> EventLoopExtRunOnDemand for EventLoop<T> {
|
||||
type UserEvent = T;
|
||||
|
||||
fn run_on_demand<F>(&mut self, event_handler: F) -> Result<(), EventLoopError>
|
||||
where
|
||||
F: FnMut(Event<Self::UserEvent>, &ActiveEventLoop),
|
||||
{
|
||||
fn run_app_on_demand<A: ApplicationHandler<Self::UserEvent>>(
|
||||
&mut self,
|
||||
app: &mut A,
|
||||
) -> Result<(), EventLoopError> {
|
||||
self.event_loop.window_target().clear_exit();
|
||||
self.event_loop.run_on_demand(event_handler)
|
||||
self.event_loop.run_app_on_demand(app)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -55,8 +55,7 @@ use web_sys::HtmlCanvasElement;
|
||||
|
||||
use crate::application::ApplicationHandler;
|
||||
use crate::cursor::CustomCursorSource;
|
||||
use crate::event::Event;
|
||||
use crate::event_loop::{self, ActiveEventLoop, EventLoop};
|
||||
use crate::event_loop::{ActiveEventLoop, EventLoop};
|
||||
#[cfg(web_platform)]
|
||||
use crate::platform_impl::CustomCursorFuture as PlatformCustomCursorFuture;
|
||||
use crate::platform_impl::PlatformCustomCursorSource;
|
||||
@@ -156,7 +155,9 @@ impl WindowAttributesExtWebSys for WindowAttributes {
|
||||
|
||||
/// Additional methods on `EventLoop` that are specific to the web.
|
||||
pub trait EventLoopExtWebSys {
|
||||
/// A type provided by the user that can be passed through `Event::UserEvent`.
|
||||
/// A type provided by the user that can be passed through [`Event::UserEvent`].
|
||||
///
|
||||
/// [`Event::UserEvent`]: crate::event::Event::UserEvent
|
||||
type UserEvent: 'static;
|
||||
|
||||
/// Initializes the winit event loop.
|
||||
@@ -182,30 +183,13 @@ pub trait EventLoopExtWebSys {
|
||||
)]
|
||||
/// [^1]: `run_app()` is _not_ available on WASM when the target supports `exception-handling`.
|
||||
fn spawn_app<A: ApplicationHandler<Self::UserEvent> + 'static>(self, app: A);
|
||||
|
||||
/// See [`spawn_app`].
|
||||
///
|
||||
/// [`spawn_app`]: Self::spawn_app
|
||||
#[deprecated = "use EventLoopExtWebSys::spawn_app"]
|
||||
fn spawn<F>(self, event_handler: F)
|
||||
where
|
||||
F: 'static + FnMut(Event<Self::UserEvent>, &ActiveEventLoop);
|
||||
}
|
||||
|
||||
impl<T> EventLoopExtWebSys for EventLoop<T> {
|
||||
type UserEvent = T;
|
||||
|
||||
fn spawn_app<A: ApplicationHandler<Self::UserEvent> + 'static>(self, mut app: A) {
|
||||
self.event_loop.spawn(move |event, event_loop| {
|
||||
event_loop::dispatch_event_for_app(&mut app, event_loop, event)
|
||||
});
|
||||
}
|
||||
|
||||
fn spawn<F>(self, event_handler: F)
|
||||
where
|
||||
F: 'static + FnMut(Event<Self::UserEvent>, &ActiveEventLoop),
|
||||
{
|
||||
self.event_loop.spawn(event_handler)
|
||||
fn spawn_app<A: ApplicationHandler<Self::UserEvent> + 'static>(self, app: A) {
|
||||
self.event_loop.spawn_app(app);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,12 +14,13 @@ use android_activity::{
|
||||
};
|
||||
use tracing::{debug, trace, warn};
|
||||
|
||||
use crate::application::ApplicationHandler;
|
||||
use crate::cursor::Cursor;
|
||||
use crate::dpi::{PhysicalPosition, PhysicalSize, Position, Size};
|
||||
use crate::error;
|
||||
use crate::error::EventLoopError;
|
||||
use crate::event::{self, Force, InnerSizeWriter, StartCause};
|
||||
use crate::event_loop::{self, ActiveEventLoop as RootAEL, ControlFlow, DeviceEvents};
|
||||
use crate::event_loop::{self, ControlFlow, DeviceEvents};
|
||||
use crate::platform::pump_events::PumpStatus;
|
||||
use crate::platform_impl::Fullscreen;
|
||||
use crate::window::{
|
||||
@@ -197,27 +198,28 @@ impl<T: 'static> EventLoop<T> {
|
||||
})
|
||||
}
|
||||
|
||||
fn single_iteration<F>(&mut self, main_event: Option<MainEvent<'_>>, callback: &mut F)
|
||||
where
|
||||
F: FnMut(event::Event<T>, &RootAEL),
|
||||
{
|
||||
fn single_iteration<A: ApplicationHandler<T>>(
|
||||
&mut self,
|
||||
main_event: Option<MainEvent<'_>>,
|
||||
app: &mut A,
|
||||
) {
|
||||
trace!("Mainloop iteration");
|
||||
|
||||
let cause = self.cause;
|
||||
let mut pending_redraw = self.pending_redraw;
|
||||
let mut resized = false;
|
||||
|
||||
callback(event::Event::NewEvents(cause), self.window_target());
|
||||
app.new_events(self.window_target(), cause);
|
||||
|
||||
if let Some(event) = main_event {
|
||||
trace!("Handling main event {:?}", event);
|
||||
|
||||
match event {
|
||||
MainEvent::InitWindow { .. } => {
|
||||
callback(event::Event::Resumed, self.window_target());
|
||||
app.resumed(self.window_target());
|
||||
},
|
||||
MainEvent::TerminateWindow { .. } => {
|
||||
callback(event::Event::Suspended, self.window_target());
|
||||
app.suspended(self.window_target());
|
||||
},
|
||||
MainEvent::WindowResized { .. } => resized = true,
|
||||
MainEvent::RedrawNeeded { .. } => pending_redraw = true,
|
||||
@@ -226,23 +228,15 @@ impl<T: 'static> EventLoop<T> {
|
||||
},
|
||||
MainEvent::GainedFocus => {
|
||||
HAS_FOCUS.store(true, Ordering::Relaxed);
|
||||
callback(
|
||||
event::Event::WindowEvent {
|
||||
window_id: window::WindowId(WindowId),
|
||||
event: event::WindowEvent::Focused(true),
|
||||
},
|
||||
self.window_target(),
|
||||
);
|
||||
let window_id = window::WindowId(WindowId);
|
||||
let event = event::WindowEvent::Focused(true);
|
||||
app.window_event(self.window_target(), window_id, event);
|
||||
},
|
||||
MainEvent::LostFocus => {
|
||||
HAS_FOCUS.store(false, Ordering::Relaxed);
|
||||
callback(
|
||||
event::Event::WindowEvent {
|
||||
window_id: window::WindowId(WindowId),
|
||||
event: event::WindowEvent::Focused(false),
|
||||
},
|
||||
self.window_target(),
|
||||
);
|
||||
let window_id = window::WindowId(WindowId);
|
||||
let event = event::WindowEvent::Focused(false);
|
||||
app.window_event(self.window_target(), window_id, event);
|
||||
},
|
||||
MainEvent::ConfigChanged { .. } => {
|
||||
let monitor = MonitorHandle::new(self.android_app.clone());
|
||||
@@ -252,20 +246,19 @@ impl<T: 'static> EventLoop<T> {
|
||||
let new_inner_size = Arc::new(Mutex::new(
|
||||
MonitorHandle::new(self.android_app.clone()).size(),
|
||||
));
|
||||
let event = event::Event::WindowEvent {
|
||||
window_id: window::WindowId(WindowId),
|
||||
event: event::WindowEvent::ScaleFactorChanged {
|
||||
inner_size_writer: InnerSizeWriter::new(Arc::downgrade(
|
||||
&new_inner_size,
|
||||
)),
|
||||
scale_factor,
|
||||
},
|
||||
let window_id = window::WindowId(WindowId);
|
||||
let event = event::WindowEvent::ScaleFactorChanged {
|
||||
inner_size_writer: InnerSizeWriter::new(Arc::downgrade(
|
||||
&new_inner_size,
|
||||
)),
|
||||
scale_factor,
|
||||
};
|
||||
callback(event, self.window_target());
|
||||
|
||||
app.window_event(self.window_target(), window_id, event);
|
||||
}
|
||||
},
|
||||
MainEvent::LowMemory => {
|
||||
callback(event::Event::MemoryWarning, self.window_target());
|
||||
app.memory_warning(self.window_target());
|
||||
},
|
||||
MainEvent::Start => {
|
||||
// XXX: how to forward this state to applications?
|
||||
@@ -313,7 +306,7 @@ impl<T: 'static> EventLoop<T> {
|
||||
match android_app.input_events_iter() {
|
||||
Ok(mut input_iter) => loop {
|
||||
let read_event =
|
||||
input_iter.next(|event| self.handle_input_event(&android_app, event, callback));
|
||||
input_iter.next(|event| self.handle_input_event(&android_app, event, app));
|
||||
|
||||
if !read_event {
|
||||
break;
|
||||
@@ -327,7 +320,7 @@ impl<T: 'static> EventLoop<T> {
|
||||
// Empty the user event buffer
|
||||
{
|
||||
while let Ok(event) = self.user_events_receiver.try_recv() {
|
||||
callback(crate::event::Event::UserEvent(event), self.window_target());
|
||||
app.user_event(self.window_target(), event);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -340,39 +333,32 @@ impl<T: 'static> EventLoop<T> {
|
||||
} else {
|
||||
PhysicalSize::new(0, 0)
|
||||
};
|
||||
let event = event::Event::WindowEvent {
|
||||
window_id: window::WindowId(WindowId),
|
||||
event: event::WindowEvent::Resized(size),
|
||||
};
|
||||
callback(event, self.window_target());
|
||||
let window_id = window::WindowId(WindowId);
|
||||
let event = event::WindowEvent::Resized(size);
|
||||
app.window_event(self.window_target(), window_id, event);
|
||||
}
|
||||
|
||||
pending_redraw |= self.redraw_flag.get_and_reset();
|
||||
if pending_redraw {
|
||||
pending_redraw = false;
|
||||
let event = event::Event::WindowEvent {
|
||||
window_id: window::WindowId(WindowId),
|
||||
event: event::WindowEvent::RedrawRequested,
|
||||
};
|
||||
callback(event, self.window_target());
|
||||
let window_id = window::WindowId(WindowId);
|
||||
let event = event::WindowEvent::RedrawRequested;
|
||||
app.window_event(self.window_target(), window_id, event);
|
||||
}
|
||||
}
|
||||
|
||||
// This is always the last event we dispatch before poll again
|
||||
callback(event::Event::AboutToWait, self.window_target());
|
||||
app.about_to_wait(self.window_target());
|
||||
|
||||
self.pending_redraw = pending_redraw;
|
||||
}
|
||||
|
||||
fn handle_input_event<F>(
|
||||
fn handle_input_event<A: ApplicationHandler<T>>(
|
||||
&mut self,
|
||||
android_app: &AndroidApp,
|
||||
event: &InputEvent<'_>,
|
||||
callback: &mut F,
|
||||
) -> InputStatus
|
||||
where
|
||||
F: FnMut(event::Event<T>, &RootAEL),
|
||||
{
|
||||
app: &mut A,
|
||||
) -> InputStatus {
|
||||
let mut input_status = InputStatus::Handled;
|
||||
match event {
|
||||
InputEvent::MotionEvent(motion_event) => {
|
||||
@@ -410,17 +396,16 @@ impl<T: 'static> EventLoop<T> {
|
||||
"Input event {device_id:?}, {phase:?}, loc={location:?}, \
|
||||
pointer={pointer:?}"
|
||||
);
|
||||
let event = event::Event::WindowEvent {
|
||||
window_id,
|
||||
event: event::WindowEvent::Touch(event::Touch {
|
||||
device_id,
|
||||
phase,
|
||||
location,
|
||||
id: pointer.pointer_id() as u64,
|
||||
force: Some(Force::Normalized(pointer.pressure() as f64)),
|
||||
}),
|
||||
};
|
||||
callback(event, self.window_target());
|
||||
|
||||
let event = event::WindowEvent::Touch(event::Touch {
|
||||
device_id,
|
||||
phase,
|
||||
location,
|
||||
id: pointer.pointer_id() as u64,
|
||||
force: Some(Force::Normalized(pointer.pressure() as f64)),
|
||||
});
|
||||
|
||||
app.window_event(self.window_target(), window_id, event);
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -448,23 +433,22 @@ impl<T: 'static> EventLoop<T> {
|
||||
&mut self.combining_accent,
|
||||
);
|
||||
|
||||
let event = event::Event::WindowEvent {
|
||||
window_id: window::WindowId(WindowId),
|
||||
event: event::WindowEvent::KeyboardInput {
|
||||
device_id: event::DeviceId(DeviceId(key.device_id())),
|
||||
event: event::KeyEvent {
|
||||
state,
|
||||
physical_key: keycodes::to_physical_key(keycode),
|
||||
logical_key: keycodes::to_logical(key_char, keycode),
|
||||
location: keycodes::to_location(keycode),
|
||||
repeat: key.repeat_count() > 0,
|
||||
text: None,
|
||||
platform_specific: KeyEventExtra {},
|
||||
},
|
||||
is_synthetic: false,
|
||||
let window_id = window::WindowId(WindowId);
|
||||
let event = event::WindowEvent::KeyboardInput {
|
||||
device_id: event::DeviceId(DeviceId(key.device_id())),
|
||||
event: event::KeyEvent {
|
||||
state,
|
||||
physical_key: keycodes::to_physical_key(keycode),
|
||||
logical_key: keycodes::to_logical(key_char, keycode),
|
||||
location: keycodes::to_location(keycode),
|
||||
repeat: key.repeat_count() > 0,
|
||||
text: None,
|
||||
platform_specific: KeyEventExtra {},
|
||||
},
|
||||
is_synthetic: false,
|
||||
};
|
||||
callback(event, self.window_target());
|
||||
|
||||
app.window_event(self.window_target(), window_id, event);
|
||||
},
|
||||
}
|
||||
},
|
||||
@@ -476,19 +460,16 @@ impl<T: 'static> EventLoop<T> {
|
||||
input_status
|
||||
}
|
||||
|
||||
pub fn run<F>(mut self, event_handler: F) -> Result<(), EventLoopError>
|
||||
where
|
||||
F: FnMut(event::Event<T>, &event_loop::ActiveEventLoop),
|
||||
{
|
||||
self.run_on_demand(event_handler)
|
||||
pub fn run_app<A: ApplicationHandler<T>>(mut self, app: &mut A) -> Result<(), EventLoopError> {
|
||||
self.run_app_on_demand(app)
|
||||
}
|
||||
|
||||
pub fn run_on_demand<F>(&mut self, mut event_handler: F) -> Result<(), EventLoopError>
|
||||
where
|
||||
F: FnMut(event::Event<T>, &event_loop::ActiveEventLoop),
|
||||
{
|
||||
pub fn run_app_on_demand<A: ApplicationHandler<T>>(
|
||||
&mut self,
|
||||
app: &mut A,
|
||||
) -> Result<(), EventLoopError> {
|
||||
loop {
|
||||
match self.pump_events(None, &mut event_handler) {
|
||||
match self.pump_app_events(None, app) {
|
||||
PumpStatus::Exit(0) => {
|
||||
break Ok(());
|
||||
},
|
||||
@@ -502,10 +483,11 @@ impl<T: 'static> EventLoop<T> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pump_events<F>(&mut self, timeout: Option<Duration>, mut callback: F) -> PumpStatus
|
||||
where
|
||||
F: FnMut(event::Event<T>, &RootAEL),
|
||||
{
|
||||
pub fn pump_app_events<A: ApplicationHandler<T>>(
|
||||
&mut self,
|
||||
timeout: Option<Duration>,
|
||||
app: &mut A,
|
||||
) -> PumpStatus {
|
||||
if !self.loop_running {
|
||||
self.loop_running = true;
|
||||
|
||||
@@ -516,18 +498,18 @@ impl<T: 'static> EventLoop<T> {
|
||||
self.cause = StartCause::Init;
|
||||
|
||||
// run the initial loop iteration
|
||||
self.single_iteration(None, &mut callback);
|
||||
self.single_iteration(None, app);
|
||||
}
|
||||
|
||||
// Consider the possibility that the `StartCause::Init` iteration could
|
||||
// request to Exit
|
||||
if !self.exiting() {
|
||||
self.poll_events_with_timeout(timeout, &mut callback);
|
||||
self.poll_events_with_timeout(timeout, app);
|
||||
}
|
||||
if self.exiting() {
|
||||
self.loop_running = false;
|
||||
|
||||
callback(event::Event::LoopExiting, self.window_target());
|
||||
app.exiting(self.window_target());
|
||||
|
||||
PumpStatus::Exit(0)
|
||||
} else {
|
||||
@@ -535,10 +517,11 @@ impl<T: 'static> EventLoop<T> {
|
||||
}
|
||||
}
|
||||
|
||||
fn poll_events_with_timeout<F>(&mut self, mut timeout: Option<Duration>, mut callback: F)
|
||||
where
|
||||
F: FnMut(event::Event<T>, &RootAEL),
|
||||
{
|
||||
fn poll_events_with_timeout<A: ApplicationHandler<T>>(
|
||||
&mut self,
|
||||
mut timeout: Option<Duration>,
|
||||
app: &mut A,
|
||||
) {
|
||||
let start = Instant::now();
|
||||
|
||||
self.pending_redraw |= self.redraw_flag.get_and_reset();
|
||||
@@ -559,8 +542,8 @@ impl<T: 'static> EventLoop<T> {
|
||||
min_timeout(control_flow_timeout, timeout)
|
||||
};
|
||||
|
||||
let app = self.android_app.clone(); // Don't borrow self as part of poll expression
|
||||
app.poll_events(timeout, |poll_event| {
|
||||
let android_app = self.android_app.clone(); // Don't borrow self as part of poll expression
|
||||
android_app.poll_events(timeout, |poll_event| {
|
||||
let mut main_event = None;
|
||||
|
||||
match poll_event {
|
||||
@@ -601,7 +584,7 @@ impl<T: 'static> EventLoop<T> {
|
||||
},
|
||||
};
|
||||
|
||||
self.single_iteration(main_event, &mut callback);
|
||||
self.single_iteration(main_event, app);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
use objc2::{declare_class, mutability, ClassType, DeclaredClass};
|
||||
use objc2_foundation::{MainThreadMarker, NSObject};
|
||||
use objc2_ui_kit::UIApplication;
|
||||
use objc2_foundation::{MainThreadMarker, NSObject, NSObjectProtocol};
|
||||
use objc2_ui_kit::{UIApplication, UIWindow};
|
||||
|
||||
use super::app_state::{self, send_occluded_event_for_all_windows, EventWrapper};
|
||||
use crate::event::Event;
|
||||
use super::app_state::{self, EventWrapper};
|
||||
use super::window::WinitUIWindow;
|
||||
use crate::event::{Event, WindowEvent};
|
||||
use crate::window::WindowId as RootWindowId;
|
||||
|
||||
declare_class!(
|
||||
pub struct AppDelegate;
|
||||
@@ -38,17 +40,35 @@ declare_class!(
|
||||
|
||||
#[method(applicationWillEnterForeground:)]
|
||||
fn will_enter_foreground(&self, application: &UIApplication) {
|
||||
send_occluded_event_for_all_windows(application, false);
|
||||
self.send_occluded_event_for_all_windows(application, false);
|
||||
}
|
||||
|
||||
#[method(applicationDidEnterBackground:)]
|
||||
fn did_enter_background(&self, application: &UIApplication) {
|
||||
send_occluded_event_for_all_windows(application, true);
|
||||
self.send_occluded_event_for_all_windows(application, true);
|
||||
}
|
||||
|
||||
#[method(applicationWillTerminate:)]
|
||||
fn will_terminate(&self, application: &UIApplication) {
|
||||
app_state::terminated(application);
|
||||
let mut events = Vec::new();
|
||||
#[allow(deprecated)]
|
||||
for window in application.windows().iter() {
|
||||
if window.is_kind_of::<WinitUIWindow>() {
|
||||
// SAFETY: We just checked that the window is a `winit` window
|
||||
let window = unsafe {
|
||||
let ptr: *const UIWindow = window;
|
||||
let ptr: *const WinitUIWindow = ptr.cast();
|
||||
&*ptr
|
||||
};
|
||||
events.push(EventWrapper::StaticEvent(Event::WindowEvent {
|
||||
window_id: RootWindowId(window.id()),
|
||||
event: WindowEvent::Destroyed,
|
||||
}));
|
||||
}
|
||||
}
|
||||
let mtm = MainThreadMarker::new().unwrap();
|
||||
app_state::handle_nonuser_events(mtm, events);
|
||||
app_state::terminated(mtm);
|
||||
}
|
||||
|
||||
#[method(applicationDidReceiveMemoryWarning:)]
|
||||
@@ -58,3 +78,26 @@ declare_class!(
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
impl AppDelegate {
|
||||
fn send_occluded_event_for_all_windows(&self, application: &UIApplication, occluded: bool) {
|
||||
let mut events = Vec::new();
|
||||
#[allow(deprecated)]
|
||||
for window in application.windows().iter() {
|
||||
if window.is_kind_of::<WinitUIWindow>() {
|
||||
// SAFETY: We just checked that the window is a `winit` window
|
||||
let window = unsafe {
|
||||
let ptr: *const UIWindow = window;
|
||||
let ptr: *const WinitUIWindow = ptr.cast();
|
||||
&*ptr
|
||||
};
|
||||
events.push(EventWrapper::StaticEvent(Event::WindowEvent {
|
||||
window_id: RootWindowId(window.id()),
|
||||
event: WindowEvent::Occluded(occluded),
|
||||
}));
|
||||
}
|
||||
}
|
||||
let mtm = MainThreadMarker::new().unwrap();
|
||||
app_state::handle_nonuser_events(mtm, events);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ use objc2_foundation::{
|
||||
CGRect, CGSize, MainThreadMarker, NSInteger, NSObjectProtocol, NSOperatingSystemVersion,
|
||||
NSProcessInfo,
|
||||
};
|
||||
use objc2_ui_kit::{UIApplication, UICoordinateSpace, UIView, UIWindow};
|
||||
use objc2_ui_kit::{UICoordinateSpace, UIView};
|
||||
|
||||
use super::window::WinitUIWindow;
|
||||
use crate::dpi::PhysicalSize;
|
||||
@@ -662,28 +662,6 @@ fn handle_user_events(mtm: MainThreadMarker) {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn send_occluded_event_for_all_windows(application: &UIApplication, occluded: bool) {
|
||||
let mtm = MainThreadMarker::from(application);
|
||||
|
||||
let mut events = Vec::new();
|
||||
#[allow(deprecated)]
|
||||
for window in application.windows().iter() {
|
||||
if window.is_kind_of::<WinitUIWindow>() {
|
||||
// SAFETY: We just checked that the window is a `winit` window
|
||||
let window = unsafe {
|
||||
let ptr: *const UIWindow = window;
|
||||
let ptr: *const WinitUIWindow = ptr.cast();
|
||||
&*ptr
|
||||
};
|
||||
events.push(EventWrapper::StaticEvent(Event::WindowEvent {
|
||||
window_id: RootWindowId(window.id()),
|
||||
event: WindowEvent::Occluded(occluded),
|
||||
}));
|
||||
}
|
||||
}
|
||||
handle_nonuser_events(mtm, events);
|
||||
}
|
||||
|
||||
pub fn handle_main_events_cleared(mtm: MainThreadMarker) {
|
||||
let mut this = AppState::get_mut(mtm);
|
||||
if !this.has_launched() || this.has_terminated() {
|
||||
@@ -718,27 +696,7 @@ pub fn handle_events_cleared(mtm: MainThreadMarker) {
|
||||
AppState::get_mut(mtm).events_cleared_transition();
|
||||
}
|
||||
|
||||
pub(crate) fn terminated(application: &UIApplication) {
|
||||
let mtm = MainThreadMarker::from(application);
|
||||
|
||||
let mut events = Vec::new();
|
||||
#[allow(deprecated)]
|
||||
for window in application.windows().iter() {
|
||||
if window.is_kind_of::<WinitUIWindow>() {
|
||||
// SAFETY: We just checked that the window is a `winit` window
|
||||
let window = unsafe {
|
||||
let ptr: *const UIWindow = window;
|
||||
let ptr: *const WinitUIWindow = ptr.cast();
|
||||
&*ptr
|
||||
};
|
||||
events.push(EventWrapper::StaticEvent(Event::WindowEvent {
|
||||
window_id: RootWindowId(window.id()),
|
||||
event: WindowEvent::Destroyed,
|
||||
}));
|
||||
}
|
||||
}
|
||||
handle_nonuser_events(mtm, events);
|
||||
|
||||
pub fn terminated(mtm: MainThreadMarker) {
|
||||
let mut this = AppState::get_mut(mtm);
|
||||
let mut handler = this.terminated_transition();
|
||||
drop(this);
|
||||
|
||||
@@ -16,6 +16,7 @@ use objc2::{msg_send_id, ClassType};
|
||||
use objc2_foundation::{MainThreadMarker, NSString};
|
||||
use objc2_ui_kit::{UIApplication, UIApplicationMain, UIDevice, UIScreen, UIUserInterfaceIdiom};
|
||||
|
||||
use crate::application::ApplicationHandler;
|
||||
use crate::error::EventLoopError;
|
||||
use crate::event::Event;
|
||||
use crate::event_loop::{
|
||||
@@ -108,17 +109,28 @@ impl OwnedDisplayHandle {
|
||||
}
|
||||
}
|
||||
|
||||
fn map_user_event<T: 'static>(
|
||||
mut handler: impl FnMut(Event<T>, &RootActiveEventLoop),
|
||||
fn map_user_event<T: 'static, A: ApplicationHandler<T>>(
|
||||
app: &mut A,
|
||||
receiver: mpsc::Receiver<T>,
|
||||
) -> impl FnMut(Event<HandlePendingUserEvents>, &RootActiveEventLoop) {
|
||||
move |event, window_target| match event.map_nonuser_event() {
|
||||
Ok(event) => (handler)(event, window_target),
|
||||
Err(_) => {
|
||||
) -> impl FnMut(Event<HandlePendingUserEvents>, &RootActiveEventLoop) + '_ {
|
||||
move |event, window_target| match event {
|
||||
Event::NewEvents(cause) => app.new_events(window_target, cause),
|
||||
Event::WindowEvent { window_id, event } => {
|
||||
app.window_event(window_target, window_id, event)
|
||||
},
|
||||
Event::DeviceEvent { device_id, event } => {
|
||||
app.device_event(window_target, device_id, event)
|
||||
},
|
||||
Event::UserEvent(_) => {
|
||||
for event in receiver.try_iter() {
|
||||
(handler)(Event::UserEvent(event), window_target);
|
||||
app.user_event(window_target, event);
|
||||
}
|
||||
},
|
||||
Event::Suspended => app.suspended(window_target),
|
||||
Event::Resumed => app.resumed(window_target),
|
||||
Event::AboutToWait => app.about_to_wait(window_target),
|
||||
Event::LoopExiting => app.exiting(window_target),
|
||||
Event::MemoryWarning => app.memory_warning(window_target),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -161,10 +173,7 @@ impl<T: 'static> EventLoop<T> {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn run<F>(self, handler: F) -> !
|
||||
where
|
||||
F: FnMut(Event<T>, &RootActiveEventLoop),
|
||||
{
|
||||
pub fn run_app<A: ApplicationHandler<T>>(self, app: &mut A) -> ! {
|
||||
let application: Option<Retained<UIApplication>> =
|
||||
unsafe { msg_send_id![UIApplication::class(), sharedApplication] };
|
||||
assert!(
|
||||
@@ -174,7 +183,7 @@ impl<T: 'static> EventLoop<T> {
|
||||
`EventLoop::run_app` calls `UIApplicationMain` on iOS",
|
||||
);
|
||||
|
||||
let handler = map_user_event(handler, self.receiver);
|
||||
let handler = map_user_event(app, self.receiver);
|
||||
|
||||
let handler = unsafe {
|
||||
std::mem::transmute::<
|
||||
|
||||
@@ -11,6 +11,8 @@ use std::{env, fmt};
|
||||
#[cfg(x11_platform)]
|
||||
use std::{ffi::CStr, mem::MaybeUninit, os::raw::*, sync::Mutex};
|
||||
|
||||
use crate::application::ApplicationHandler;
|
||||
use crate::platform::pump_events::PumpStatus;
|
||||
#[cfg(x11_platform)]
|
||||
use crate::utils::Lazy;
|
||||
use smol_str::SmolStr;
|
||||
@@ -19,12 +21,9 @@ use smol_str::SmolStr;
|
||||
use self::x11::{X11Error, XConnection, XError, XNotSupported};
|
||||
use crate::dpi::{PhysicalPosition, PhysicalSize, Position, Size};
|
||||
use crate::error::{EventLoopError, ExternalError, NotSupportedError, OsError as RootOsError};
|
||||
use crate::event_loop::{
|
||||
ActiveEventLoop as RootELW, AsyncRequestSerial, ControlFlow, DeviceEvents, EventLoopClosed,
|
||||
};
|
||||
use crate::event_loop::{AsyncRequestSerial, ControlFlow, DeviceEvents, EventLoopClosed};
|
||||
use crate::icon::Icon;
|
||||
use crate::keyboard::Key;
|
||||
use crate::platform::pump_events::PumpStatus;
|
||||
#[cfg(x11_platform)]
|
||||
use crate::platform::x11::{WindowType as XWindowType, XlibErrorHook};
|
||||
use crate::window::{
|
||||
@@ -799,25 +798,23 @@ impl<T: 'static> EventLoop<T> {
|
||||
x11_or_wayland!(match self; EventLoop(evlp) => evlp.create_proxy(); as EventLoopProxy)
|
||||
}
|
||||
|
||||
pub fn run<F>(mut self, callback: F) -> Result<(), EventLoopError>
|
||||
where
|
||||
F: FnMut(crate::event::Event<T>, &RootELW),
|
||||
{
|
||||
self.run_on_demand(callback)
|
||||
pub fn run_app<A: ApplicationHandler<T>>(self, app: &mut A) -> Result<(), EventLoopError> {
|
||||
x11_or_wayland!(match self; EventLoop(evlp) => evlp.run_app(app))
|
||||
}
|
||||
|
||||
pub fn run_on_demand<F>(&mut self, callback: F) -> Result<(), EventLoopError>
|
||||
where
|
||||
F: FnMut(crate::event::Event<T>, &RootELW),
|
||||
{
|
||||
x11_or_wayland!(match self; EventLoop(evlp) => evlp.run_on_demand(callback))
|
||||
pub fn run_app_on_demand<A: ApplicationHandler<T>>(
|
||||
&mut self,
|
||||
app: &mut A,
|
||||
) -> Result<(), EventLoopError> {
|
||||
x11_or_wayland!(match self; EventLoop(evlp) => evlp.run_app_on_demand(app))
|
||||
}
|
||||
|
||||
pub fn pump_events<F>(&mut self, timeout: Option<Duration>, callback: F) -> PumpStatus
|
||||
where
|
||||
F: FnMut(crate::event::Event<T>, &RootELW),
|
||||
{
|
||||
x11_or_wayland!(match self; EventLoop(evlp) => evlp.pump_events(timeout, callback))
|
||||
pub fn pump_app_events<A: ApplicationHandler<T>>(
|
||||
&mut self,
|
||||
timeout: Option<Duration>,
|
||||
app: &mut A,
|
||||
) -> PumpStatus {
|
||||
x11_or_wayland!(match self; EventLoop(evlp) => evlp.pump_app_events(timeout, app))
|
||||
}
|
||||
|
||||
pub fn window_target(&self) -> &crate::event_loop::ActiveEventLoop {
|
||||
|
||||
@@ -14,6 +14,7 @@ use sctk::reexports::calloop::Error as CalloopError;
|
||||
use sctk::reexports::calloop_wayland_source::WaylandSource;
|
||||
use sctk::reexports::client::{globals, Connection, QueueHandle};
|
||||
|
||||
use crate::application::ApplicationHandler;
|
||||
use crate::cursor::OnlyCursorImage;
|
||||
use crate::dpi::LogicalSize;
|
||||
use crate::error::{EventLoopError, OsError as RootOsError};
|
||||
@@ -173,12 +174,16 @@ impl<T: 'static> EventLoop<T> {
|
||||
Ok(event_loop)
|
||||
}
|
||||
|
||||
pub fn run_on_demand<F>(&mut self, mut event_handler: F) -> Result<(), EventLoopError>
|
||||
where
|
||||
F: FnMut(Event<T>, &RootActiveEventLoop),
|
||||
{
|
||||
pub fn run_app<A: ApplicationHandler<T>>(mut self, app: &mut A) -> Result<(), EventLoopError> {
|
||||
self.run_app_on_demand(app)
|
||||
}
|
||||
|
||||
pub fn run_app_on_demand<A: ApplicationHandler<T>>(
|
||||
&mut self,
|
||||
app: &mut A,
|
||||
) -> Result<(), EventLoopError> {
|
||||
let exit = loop {
|
||||
match self.pump_events(None, &mut event_handler) {
|
||||
match self.pump_app_events(None, app) {
|
||||
PumpStatus::Exit(0) => {
|
||||
break Ok(());
|
||||
},
|
||||
@@ -200,26 +205,27 @@ impl<T: 'static> EventLoop<T> {
|
||||
exit
|
||||
}
|
||||
|
||||
pub fn pump_events<F>(&mut self, timeout: Option<Duration>, mut callback: F) -> PumpStatus
|
||||
where
|
||||
F: FnMut(Event<T>, &RootActiveEventLoop),
|
||||
{
|
||||
pub fn pump_app_events<A: ApplicationHandler<T>>(
|
||||
&mut self,
|
||||
timeout: Option<Duration>,
|
||||
app: &mut A,
|
||||
) -> PumpStatus {
|
||||
if !self.loop_running {
|
||||
self.loop_running = true;
|
||||
|
||||
// Run the initial loop iteration.
|
||||
self.single_iteration(&mut callback, StartCause::Init);
|
||||
self.single_iteration(app, StartCause::Init);
|
||||
}
|
||||
|
||||
// Consider the possibility that the `StartCause::Init` iteration could
|
||||
// request to Exit.
|
||||
if !self.exiting() {
|
||||
self.poll_events_with_timeout(timeout, &mut callback);
|
||||
self.poll_events_with_timeout(timeout, app);
|
||||
}
|
||||
if let Some(code) = self.exit_code() {
|
||||
self.loop_running = false;
|
||||
|
||||
callback(Event::LoopExiting, self.window_target());
|
||||
app.exiting(&self.window_target);
|
||||
|
||||
PumpStatus::Exit(code)
|
||||
} else {
|
||||
@@ -227,10 +233,11 @@ impl<T: 'static> EventLoop<T> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn poll_events_with_timeout<F>(&mut self, mut timeout: Option<Duration>, mut callback: F)
|
||||
where
|
||||
F: FnMut(Event<T>, &RootActiveEventLoop),
|
||||
{
|
||||
pub fn poll_events_with_timeout<A: ApplicationHandler<T>>(
|
||||
&mut self,
|
||||
mut timeout: Option<Duration>,
|
||||
app: &mut A,
|
||||
) {
|
||||
let cause = loop {
|
||||
let start = Instant::now();
|
||||
|
||||
@@ -292,13 +299,10 @@ impl<T: 'static> EventLoop<T> {
|
||||
break cause;
|
||||
};
|
||||
|
||||
self.single_iteration(&mut callback, cause);
|
||||
self.single_iteration(app, cause);
|
||||
}
|
||||
|
||||
fn single_iteration<F>(&mut self, callback: &mut F, cause: StartCause)
|
||||
where
|
||||
F: FnMut(Event<T>, &RootActiveEventLoop),
|
||||
{
|
||||
fn single_iteration<A: ApplicationHandler<T>>(&mut self, app: &mut A, cause: StartCause) {
|
||||
// NOTE currently just indented to simplify the diff
|
||||
|
||||
// We retain these grow-only scratch buffers as part of the EventLoop
|
||||
@@ -309,18 +313,18 @@ impl<T: 'static> EventLoop<T> {
|
||||
let mut buffer_sink = std::mem::take(&mut self.buffer_sink);
|
||||
let mut window_ids = std::mem::take(&mut self.window_ids);
|
||||
|
||||
callback(Event::NewEvents(cause), &self.window_target);
|
||||
app.new_events(&self.window_target, cause);
|
||||
|
||||
// NB: For consistency all platforms must emit a 'resumed' event even though Wayland
|
||||
// applications don't themselves have a formal suspend/resume lifecycle.
|
||||
if cause == StartCause::Init {
|
||||
callback(Event::Resumed, &self.window_target);
|
||||
app.resumed(&self.window_target);
|
||||
}
|
||||
|
||||
// Handle pending user events. We don't need back buffer, since we can't dispatch
|
||||
// user events indirectly via callback to the user.
|
||||
for user_event in self.pending_user_events.borrow_mut().drain(..) {
|
||||
callback(Event::UserEvent(user_event), &self.window_target);
|
||||
app.user_event(&self.window_target, user_event);
|
||||
}
|
||||
|
||||
// Drain the pending compositor updates.
|
||||
@@ -341,18 +345,13 @@ impl<T: 'static> EventLoop<T> {
|
||||
let old_physical_size = physical_size;
|
||||
|
||||
let new_inner_size = Arc::new(Mutex::new(physical_size));
|
||||
callback(
|
||||
Event::WindowEvent {
|
||||
window_id: crate::window::WindowId(window_id),
|
||||
event: WindowEvent::ScaleFactorChanged {
|
||||
scale_factor,
|
||||
inner_size_writer: InnerSizeWriter::new(Arc::downgrade(
|
||||
&new_inner_size,
|
||||
)),
|
||||
},
|
||||
},
|
||||
&self.window_target,
|
||||
);
|
||||
let root_window_id = crate::window::WindowId(window_id);
|
||||
let event = WindowEvent::ScaleFactorChanged {
|
||||
scale_factor,
|
||||
inner_size_writer: InnerSizeWriter::new(Arc::downgrade(&new_inner_size)),
|
||||
};
|
||||
|
||||
app.window_event(&self.window_target, root_window_id, event);
|
||||
|
||||
let physical_size = *new_inner_size.lock().unwrap();
|
||||
drop(new_inner_size);
|
||||
@@ -395,23 +394,14 @@ impl<T: 'static> EventLoop<T> {
|
||||
size
|
||||
});
|
||||
|
||||
callback(
|
||||
Event::WindowEvent {
|
||||
window_id: crate::window::WindowId(window_id),
|
||||
event: WindowEvent::Resized(physical_size),
|
||||
},
|
||||
&self.window_target,
|
||||
);
|
||||
let window_id = crate::window::WindowId(window_id);
|
||||
let event = WindowEvent::Resized(physical_size);
|
||||
app.window_event(&self.window_target, window_id, event);
|
||||
}
|
||||
|
||||
if compositor_update.close_window {
|
||||
callback(
|
||||
Event::WindowEvent {
|
||||
window_id: crate::window::WindowId(window_id),
|
||||
event: WindowEvent::CloseRequested,
|
||||
},
|
||||
&self.window_target,
|
||||
);
|
||||
let window_id = crate::window::WindowId(window_id);
|
||||
app.window_event(&self.window_target, window_id, WindowEvent::CloseRequested);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -420,8 +410,15 @@ impl<T: 'static> EventLoop<T> {
|
||||
buffer_sink.append(&mut state.window_events_sink.lock().unwrap());
|
||||
});
|
||||
for event in buffer_sink.drain() {
|
||||
let event = event.map_nonuser_event().unwrap();
|
||||
callback(event, &self.window_target);
|
||||
match event {
|
||||
Event::WindowEvent { window_id, event } => {
|
||||
app.window_event(&self.window_target, window_id, event)
|
||||
},
|
||||
Event::DeviceEvent { device_id, event } => {
|
||||
app.device_event(&self.window_target, device_id, event)
|
||||
},
|
||||
_ => unreachable!("event which is neither device nor window event."),
|
||||
}
|
||||
}
|
||||
|
||||
// Handle non-synthetic events.
|
||||
@@ -429,8 +426,15 @@ impl<T: 'static> EventLoop<T> {
|
||||
buffer_sink.append(&mut state.events_sink);
|
||||
});
|
||||
for event in buffer_sink.drain() {
|
||||
let event = event.map_nonuser_event().unwrap();
|
||||
callback(event, &self.window_target);
|
||||
match event {
|
||||
Event::WindowEvent { window_id, event } => {
|
||||
app.window_event(&self.window_target, window_id, event)
|
||||
},
|
||||
Event::DeviceEvent { device_id, event } => {
|
||||
app.device_event(&self.window_target, device_id, event)
|
||||
},
|
||||
_ => unreachable!("event which is neither device nor window event."),
|
||||
}
|
||||
}
|
||||
|
||||
// Collect the window ids
|
||||
@@ -466,10 +470,8 @@ impl<T: 'static> EventLoop<T> {
|
||||
});
|
||||
|
||||
if let Some(event) = event {
|
||||
callback(
|
||||
Event::WindowEvent { window_id: crate::window::WindowId(*window_id), event },
|
||||
&self.window_target,
|
||||
);
|
||||
let window_id = crate::window::WindowId(*window_id);
|
||||
app.window_event(&self.window_target, window_id, event);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -479,7 +481,7 @@ impl<T: 'static> EventLoop<T> {
|
||||
});
|
||||
|
||||
// This is always the last event we dispatch before poll again
|
||||
callback(Event::AboutToWait, &self.window_target);
|
||||
app.about_to_wait(&self.window_target);
|
||||
|
||||
// Update the window frames and schedule redraws.
|
||||
let mut wake_up = false;
|
||||
|
||||
@@ -27,6 +27,7 @@ use x11rb::protocol::xproto::{self, ConnectionExt as _};
|
||||
use x11rb::x11_utils::X11Error as LogicalError;
|
||||
use x11rb::xcb_ffi::ReplyOrIdError;
|
||||
|
||||
use crate::application::ApplicationHandler;
|
||||
use crate::error::{EventLoopError, OsError as RootOsError};
|
||||
use crate::event::{Event, StartCause, WindowEvent};
|
||||
use crate::event_loop::{ActiveEventLoop as RootAEL, ControlFlow, DeviceEvents, EventLoopClosed};
|
||||
@@ -379,12 +380,16 @@ impl<T: 'static> EventLoop<T> {
|
||||
&self.event_processor.target
|
||||
}
|
||||
|
||||
pub fn run_on_demand<F>(&mut self, mut event_handler: F) -> Result<(), EventLoopError>
|
||||
where
|
||||
F: FnMut(Event<T>, &RootAEL),
|
||||
{
|
||||
pub fn run_app<A: ApplicationHandler<T>>(mut self, app: &mut A) -> Result<(), EventLoopError> {
|
||||
self.run_app_on_demand(app)
|
||||
}
|
||||
|
||||
pub fn run_app_on_demand<A: ApplicationHandler<T>>(
|
||||
&mut self,
|
||||
app: &mut A,
|
||||
) -> Result<(), EventLoopError> {
|
||||
let exit = loop {
|
||||
match self.pump_events(None, &mut event_handler) {
|
||||
match self.pump_app_events(None, app) {
|
||||
PumpStatus::Exit(0) => {
|
||||
break Ok(());
|
||||
},
|
||||
@@ -409,26 +414,27 @@ impl<T: 'static> EventLoop<T> {
|
||||
exit
|
||||
}
|
||||
|
||||
pub fn pump_events<F>(&mut self, timeout: Option<Duration>, mut callback: F) -> PumpStatus
|
||||
where
|
||||
F: FnMut(Event<T>, &RootAEL),
|
||||
{
|
||||
pub fn pump_app_events<A: ApplicationHandler<T>>(
|
||||
&mut self,
|
||||
timeout: Option<Duration>,
|
||||
app: &mut A,
|
||||
) -> PumpStatus {
|
||||
if !self.loop_running {
|
||||
self.loop_running = true;
|
||||
|
||||
// run the initial loop iteration
|
||||
self.single_iteration(&mut callback, StartCause::Init);
|
||||
self.single_iteration(app, StartCause::Init);
|
||||
}
|
||||
|
||||
// Consider the possibility that the `StartCause::Init` iteration could
|
||||
// request to Exit.
|
||||
if !self.exiting() {
|
||||
self.poll_events_with_timeout(timeout, &mut callback);
|
||||
self.poll_events_with_timeout(timeout, app);
|
||||
}
|
||||
if let Some(code) = self.exit_code() {
|
||||
self.loop_running = false;
|
||||
|
||||
callback(Event::LoopExiting, self.window_target());
|
||||
app.exiting(self.window_target());
|
||||
|
||||
PumpStatus::Exit(code)
|
||||
} else {
|
||||
@@ -442,10 +448,11 @@ impl<T: 'static> EventLoop<T> {
|
||||
|| self.redraw_receiver.has_incoming()
|
||||
}
|
||||
|
||||
pub fn poll_events_with_timeout<F>(&mut self, mut timeout: Option<Duration>, mut callback: F)
|
||||
where
|
||||
F: FnMut(Event<T>, &RootAEL),
|
||||
{
|
||||
pub fn poll_events_with_timeout<A: ApplicationHandler<T>>(
|
||||
&mut self,
|
||||
mut timeout: Option<Duration>,
|
||||
app: &mut A,
|
||||
) {
|
||||
let start = Instant::now();
|
||||
|
||||
let has_pending = self.has_pending();
|
||||
@@ -503,23 +510,20 @@ impl<T: 'static> EventLoop<T> {
|
||||
return;
|
||||
}
|
||||
|
||||
self.single_iteration(&mut callback, cause);
|
||||
self.single_iteration(app, cause);
|
||||
}
|
||||
|
||||
fn single_iteration<F>(&mut self, callback: &mut F, cause: StartCause)
|
||||
where
|
||||
F: FnMut(Event<T>, &RootAEL),
|
||||
{
|
||||
callback(Event::NewEvents(cause), &self.event_processor.target);
|
||||
fn single_iteration<A: ApplicationHandler<T>>(&mut self, app: &mut A, cause: StartCause) {
|
||||
app.new_events(&self.event_processor.target, cause);
|
||||
|
||||
// NB: For consistency all platforms must emit a 'resumed' event even though X11
|
||||
// applications don't themselves have a formal suspend/resume lifecycle.
|
||||
if cause == StartCause::Init {
|
||||
callback(Event::Resumed, &self.event_processor.target);
|
||||
app.resumed(&self.event_processor.target)
|
||||
}
|
||||
|
||||
// Process all pending events
|
||||
self.drain_events(callback);
|
||||
self.drain_events(app);
|
||||
|
||||
// Empty activation tokens.
|
||||
while let Ok((window_id, serial)) = self.activation_receiver.try_recv() {
|
||||
@@ -529,14 +533,12 @@ impl<T: 'static> EventLoop<T> {
|
||||
|
||||
match token {
|
||||
Some(Ok(token)) => {
|
||||
let event = Event::WindowEvent {
|
||||
window_id: crate::window::WindowId(window_id),
|
||||
event: WindowEvent::ActivationTokenDone {
|
||||
serial,
|
||||
token: crate::window::ActivationToken::_new(token),
|
||||
},
|
||||
let window_id = crate::window::WindowId(window_id);
|
||||
let event = WindowEvent::ActivationTokenDone {
|
||||
serial,
|
||||
token: crate::window::ActivationToken::_new(token),
|
||||
};
|
||||
callback(event, &self.event_processor.target)
|
||||
app.window_event(&self.event_processor.target, window_id, event);
|
||||
},
|
||||
Some(Err(e)) => {
|
||||
tracing::error!("Failed to get activation token: {}", e);
|
||||
@@ -548,7 +550,7 @@ impl<T: 'static> EventLoop<T> {
|
||||
// Empty the user event buffer
|
||||
{
|
||||
while let Ok(event) = self.user_receiver.try_recv() {
|
||||
callback(Event::UserEvent(event), &self.event_processor.target);
|
||||
app.user_event(&self.event_processor.target, event);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -562,28 +564,24 @@ impl<T: 'static> EventLoop<T> {
|
||||
|
||||
for window_id in windows {
|
||||
let window_id = crate::window::WindowId(window_id);
|
||||
callback(
|
||||
Event::WindowEvent { window_id, event: WindowEvent::RedrawRequested },
|
||||
app.window_event(
|
||||
&self.event_processor.target,
|
||||
window_id,
|
||||
WindowEvent::RedrawRequested,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// This is always the last event we dispatch before poll again
|
||||
{
|
||||
callback(Event::AboutToWait, &self.event_processor.target);
|
||||
}
|
||||
app.about_to_wait(&self.event_processor.target);
|
||||
}
|
||||
|
||||
fn drain_events<F>(&mut self, callback: &mut F)
|
||||
where
|
||||
F: FnMut(Event<T>, &RootAEL),
|
||||
{
|
||||
fn drain_events<A: ApplicationHandler<T>>(&mut self, app: &mut A) {
|
||||
let mut xev = MaybeUninit::uninit();
|
||||
|
||||
while unsafe { self.event_processor.poll_one_event(xev.as_mut_ptr()) } {
|
||||
let mut xev = unsafe { xev.assume_init() };
|
||||
self.event_processor.process_event(&mut xev, |window_target, event| {
|
||||
self.event_processor.process_event(&mut xev, |window_target, event: Event<T>| {
|
||||
if let Event::WindowEvent {
|
||||
window_id: crate::window::WindowId(wid),
|
||||
event: WindowEvent::RedrawRequested,
|
||||
@@ -592,7 +590,15 @@ impl<T: 'static> EventLoop<T> {
|
||||
let window_target = EventProcessor::window_target(window_target);
|
||||
window_target.redraw_sender.send(wid).unwrap();
|
||||
} else {
|
||||
callback(event, window_target);
|
||||
match event {
|
||||
Event::WindowEvent { window_id, event } => {
|
||||
app.window_event(window_target, window_id, event)
|
||||
},
|
||||
Event::DeviceEvent { device_id, event } => {
|
||||
app.device_event(window_target, device_id, event)
|
||||
},
|
||||
_ => unreachable!("event which is neither device nor window event."),
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ use objc2::{declare_class, msg_send, mutability, ClassType, DeclaredClass};
|
||||
use objc2_app_kit::{NSApplication, NSEvent, NSEventModifierFlags, NSEventType, NSResponder};
|
||||
use objc2_foundation::{MainThreadMarker, NSObject};
|
||||
|
||||
use super::app_state::ApplicationDelegate;
|
||||
use super::app_delegate::ApplicationDelegate;
|
||||
use crate::event::{DeviceEvent, ElementState};
|
||||
|
||||
declare_class!(
|
||||
@@ -57,27 +57,25 @@ fn maybe_dispatch_device_event(delegate: &ApplicationDelegate, event: &NSEvent)
|
||||
let delta_y = unsafe { event.deltaY() } as f64;
|
||||
|
||||
if delta_x != 0.0 {
|
||||
delegate.maybe_queue_device_event(DeviceEvent::Motion { axis: 0, value: delta_x });
|
||||
delegate.queue_device_event(DeviceEvent::Motion { axis: 0, value: delta_x });
|
||||
}
|
||||
|
||||
if delta_y != 0.0 {
|
||||
delegate.maybe_queue_device_event(DeviceEvent::Motion { axis: 1, value: delta_y })
|
||||
delegate.queue_device_event(DeviceEvent::Motion { axis: 1, value: delta_y })
|
||||
}
|
||||
|
||||
if delta_x != 0.0 || delta_y != 0.0 {
|
||||
delegate.maybe_queue_device_event(DeviceEvent::MouseMotion {
|
||||
delta: (delta_x, delta_y),
|
||||
});
|
||||
delegate.queue_device_event(DeviceEvent::MouseMotion { delta: (delta_x, delta_y) });
|
||||
}
|
||||
},
|
||||
NSEventType::LeftMouseDown | NSEventType::RightMouseDown | NSEventType::OtherMouseDown => {
|
||||
delegate.maybe_queue_device_event(DeviceEvent::Button {
|
||||
delegate.queue_device_event(DeviceEvent::Button {
|
||||
button: unsafe { event.buttonNumber() } as u32,
|
||||
state: ElementState::Pressed,
|
||||
});
|
||||
},
|
||||
NSEventType::LeftMouseUp | NSEventType::RightMouseUp | NSEventType::OtherMouseUp => {
|
||||
delegate.maybe_queue_device_event(DeviceEvent::Button {
|
||||
delegate.queue_device_event(DeviceEvent::Button {
|
||||
button: unsafe { event.buttonNumber() } as u32,
|
||||
state: ElementState::Released,
|
||||
});
|
||||
|
||||
@@ -1,27 +1,40 @@
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::collections::VecDeque;
|
||||
use std::mem;
|
||||
use std::rc::Weak;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::time::Instant;
|
||||
|
||||
use objc2::rc::Retained;
|
||||
use objc2::runtime::AnyObject;
|
||||
use objc2::{declare_class, msg_send_id, mutability, ClassType, DeclaredClass};
|
||||
use objc2_app_kit::{NSApplication, NSApplicationActivationPolicy, NSApplicationDelegate};
|
||||
use objc2_foundation::{MainThreadMarker, NSNotification, NSObject, NSObjectProtocol};
|
||||
use objc2_foundation::{MainThreadMarker, NSObject, NSObjectProtocol, NSSize};
|
||||
|
||||
use super::event_handler::EventHandler;
|
||||
use super::event_loop::{stop_app_immediately, ActiveEventLoop, PanicInfo};
|
||||
use super::observer::{EventLoopWaker, RunLoop};
|
||||
use super::window::WinitWindow;
|
||||
use super::{menu, WindowId, DEVICE_ID};
|
||||
use crate::event::{DeviceEvent, Event, StartCause, WindowEvent};
|
||||
use crate::dpi::PhysicalSize;
|
||||
use crate::event::{DeviceEvent, Event, InnerSizeWriter, StartCause, WindowEvent};
|
||||
use crate::event_loop::{ActiveEventLoop as RootActiveEventLoop, ControlFlow};
|
||||
use crate::window::WindowId as RootWindowId;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(super) struct AppState {
|
||||
activation_policy: NSApplicationActivationPolicy,
|
||||
struct Policy(NSApplicationActivationPolicy);
|
||||
|
||||
impl Default for Policy {
|
||||
fn default() -> Self {
|
||||
Self(NSApplicationActivationPolicy::Regular)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub(super) struct State {
|
||||
activation_policy: Policy,
|
||||
default_menu: bool,
|
||||
activate_ignoring_other_apps: bool,
|
||||
run_loop: RunLoop,
|
||||
event_handler: EventHandler,
|
||||
stop_on_launch: Cell<bool>,
|
||||
stop_before_wait: Cell<bool>,
|
||||
@@ -37,6 +50,7 @@ pub(super) struct AppState {
|
||||
waker: RefCell<EventLoopWaker>,
|
||||
start_time: Cell<Option<Instant>>,
|
||||
wait_timeout: Cell<Option<Instant>>,
|
||||
pending_events: RefCell<VecDeque<QueuedEvent>>,
|
||||
pending_redraw: RefCell<Vec<WindowId>>,
|
||||
// NOTE: This is strongly referenced by our `NSWindowDelegate` and our `NSView` subclass, and
|
||||
// as such should be careful to not add fields that, in turn, strongly reference those.
|
||||
@@ -53,20 +67,62 @@ declare_class!(
|
||||
}
|
||||
|
||||
impl DeclaredClass for ApplicationDelegate {
|
||||
type Ivars = AppState;
|
||||
type Ivars = State;
|
||||
}
|
||||
|
||||
unsafe impl NSObjectProtocol for ApplicationDelegate {}
|
||||
|
||||
unsafe impl NSApplicationDelegate for ApplicationDelegate {
|
||||
// NOTE: This will, globally, only be run once, no matter how many
|
||||
// `EventLoop`s the user creates.
|
||||
#[method(applicationDidFinishLaunching:)]
|
||||
fn app_did_finish_launching(&self, notification: &NSNotification) {
|
||||
self.did_finish_launching(notification)
|
||||
fn did_finish_launching(&self, _sender: Option<&AnyObject>) {
|
||||
trace_scope!("applicationDidFinishLaunching:");
|
||||
self.ivars().is_launched.set(true);
|
||||
|
||||
let mtm = MainThreadMarker::from(self);
|
||||
let app = NSApplication::sharedApplication(mtm);
|
||||
// We need to delay setting the activation policy and activating the app
|
||||
// until `applicationDidFinishLaunching` has been called. Otherwise the
|
||||
// menu bar is initially unresponsive on macOS 10.15.
|
||||
app.setActivationPolicy(self.ivars().activation_policy.0);
|
||||
|
||||
window_activation_hack(&app);
|
||||
#[allow(deprecated)]
|
||||
app.activateIgnoringOtherApps(self.ivars().activate_ignoring_other_apps);
|
||||
|
||||
if self.ivars().default_menu {
|
||||
// The menubar initialization should be before the `NewEvents` event, to allow
|
||||
// overriding of the default menu even if it's created
|
||||
menu::initialize(&app);
|
||||
}
|
||||
|
||||
self.ivars().waker.borrow_mut().start();
|
||||
|
||||
self.set_is_running(true);
|
||||
self.dispatch_init_events();
|
||||
|
||||
// If the application is being launched via `EventLoop::pump_app_events()` then we'll
|
||||
// want to stop the app once it is launched (and return to the external loop)
|
||||
//
|
||||
// In this case we still want to consider Winit's `EventLoop` to be "running",
|
||||
// so we call `start_running()` above.
|
||||
if self.ivars().stop_on_launch.get() {
|
||||
// NOTE: the original idea had been to only stop the underlying `RunLoop`
|
||||
// for the app but that didn't work as expected (`-[NSApplication run]`
|
||||
// effectively ignored the attempt to stop the RunLoop and re-started it).
|
||||
//
|
||||
// So we return from `pump_events` by stopping the application.
|
||||
let app = NSApplication::sharedApplication(mtm);
|
||||
stop_app_immediately(&app);
|
||||
}
|
||||
}
|
||||
|
||||
#[method(applicationWillTerminate:)]
|
||||
fn app_will_terminate(&self, notification: &NSNotification) {
|
||||
self.will_terminate(notification)
|
||||
fn will_terminate(&self, _sender: Option<&AnyObject>) {
|
||||
trace_scope!("applicationWillTerminate:");
|
||||
// TODO: Notify every window that it will be destroyed, like done in iOS?
|
||||
self.internal_exit();
|
||||
}
|
||||
}
|
||||
);
|
||||
@@ -78,78 +134,15 @@ impl ApplicationDelegate {
|
||||
default_menu: bool,
|
||||
activate_ignoring_other_apps: bool,
|
||||
) -> Retained<Self> {
|
||||
let this = mtm.alloc().set_ivars(AppState {
|
||||
activation_policy,
|
||||
let this = mtm.alloc().set_ivars(State {
|
||||
activation_policy: Policy(activation_policy),
|
||||
default_menu,
|
||||
activate_ignoring_other_apps,
|
||||
run_loop: RunLoop::main(mtm),
|
||||
event_handler: EventHandler::new(),
|
||||
stop_on_launch: Cell::new(false),
|
||||
stop_before_wait: Cell::new(false),
|
||||
stop_after_wait: Cell::new(false),
|
||||
stop_on_redraw: Cell::new(false),
|
||||
is_launched: Cell::new(false),
|
||||
is_running: Cell::new(false),
|
||||
exit: Cell::new(false),
|
||||
control_flow: Cell::new(ControlFlow::default()),
|
||||
waker: RefCell::new(EventLoopWaker::new()),
|
||||
start_time: Cell::new(None),
|
||||
wait_timeout: Cell::new(None),
|
||||
pending_redraw: RefCell::new(vec![]),
|
||||
..Default::default()
|
||||
});
|
||||
unsafe { msg_send_id![super(this), init] }
|
||||
}
|
||||
|
||||
// NOTE: This will, globally, only be run once, no matter how many
|
||||
// `EventLoop`s the user creates.
|
||||
fn did_finish_launching(&self, _notification: &NSNotification) {
|
||||
trace_scope!("applicationDidFinishLaunching:");
|
||||
self.ivars().is_launched.set(true);
|
||||
|
||||
let mtm = MainThreadMarker::from(self);
|
||||
let app = NSApplication::sharedApplication(mtm);
|
||||
// We need to delay setting the activation policy and activating the app
|
||||
// until `applicationDidFinishLaunching` has been called. Otherwise the
|
||||
// menu bar is initially unresponsive on macOS 10.15.
|
||||
app.setActivationPolicy(self.ivars().activation_policy);
|
||||
|
||||
window_activation_hack(&app);
|
||||
#[allow(deprecated)]
|
||||
app.activateIgnoringOtherApps(self.ivars().activate_ignoring_other_apps);
|
||||
|
||||
if self.ivars().default_menu {
|
||||
// The menubar initialization should be before the `NewEvents` event, to allow
|
||||
// overriding of the default menu even if it's created
|
||||
menu::initialize(&app);
|
||||
}
|
||||
|
||||
self.ivars().waker.borrow_mut().start();
|
||||
|
||||
self.set_is_running(true);
|
||||
self.dispatch_init_events();
|
||||
|
||||
// If the application is being launched via `EventLoop::pump_app_events()` then we'll
|
||||
// want to stop the app once it is launched (and return to the external loop)
|
||||
//
|
||||
// In this case we still want to consider Winit's `EventLoop` to be "running",
|
||||
// so we call `start_running()` above.
|
||||
if self.ivars().stop_on_launch.get() {
|
||||
// NOTE: the original idea had been to only stop the underlying `RunLoop`
|
||||
// for the app but that didn't work as expected (`-[NSApplication run]`
|
||||
// effectively ignored the attempt to stop the RunLoop and re-started it).
|
||||
//
|
||||
// So we return from `pump_events` by stopping the application.
|
||||
let app = NSApplication::sharedApplication(mtm);
|
||||
stop_app_immediately(&app);
|
||||
}
|
||||
}
|
||||
|
||||
fn will_terminate(&self, _notification: &NSNotification) {
|
||||
trace_scope!("applicationWillTerminate:");
|
||||
// TODO: Notify every window that it will be destroyed, like done in iOS?
|
||||
self.internal_exit();
|
||||
}
|
||||
|
||||
pub fn get(mtm: MainThreadMarker) -> Retained<Self> {
|
||||
let app = NSApplication::sharedApplication(mtm);
|
||||
let delegate =
|
||||
@@ -200,6 +193,7 @@ impl ApplicationDelegate {
|
||||
/// NOTE: that if the `NSApplication` has been launched then that state is preserved,
|
||||
/// and we won't need to re-launch the app if subsequent EventLoops are run.
|
||||
pub fn internal_exit(&self) {
|
||||
self.handle_event(Event::Suspended);
|
||||
self.handle_event(Event::LoopExiting);
|
||||
|
||||
self.set_is_running(false);
|
||||
@@ -241,16 +235,28 @@ impl ApplicationDelegate {
|
||||
self.ivars().control_flow.get()
|
||||
}
|
||||
|
||||
pub fn maybe_queue_window_event(&self, window_id: WindowId, event: WindowEvent) {
|
||||
self.maybe_queue_event(Event::WindowEvent { window_id: RootWindowId(window_id), event });
|
||||
pub fn queue_window_event(&self, window_id: WindowId, event: WindowEvent) {
|
||||
self.ivars()
|
||||
.pending_events
|
||||
.borrow_mut()
|
||||
.push_back(QueuedEvent::WindowEvent(window_id, event));
|
||||
}
|
||||
|
||||
pub fn handle_window_event(&self, window_id: WindowId, event: WindowEvent) {
|
||||
self.handle_event(Event::WindowEvent { window_id: RootWindowId(window_id), event });
|
||||
pub fn queue_device_event(&self, event: DeviceEvent) {
|
||||
self.ivars().pending_events.borrow_mut().push_back(QueuedEvent::DeviceEvent(event));
|
||||
}
|
||||
|
||||
pub fn maybe_queue_device_event(&self, event: DeviceEvent) {
|
||||
self.maybe_queue_event(Event::DeviceEvent { device_id: DEVICE_ID, event });
|
||||
pub fn queue_static_scale_factor_changed_event(
|
||||
&self,
|
||||
window: Retained<WinitWindow>,
|
||||
suggested_size: PhysicalSize<u32>,
|
||||
scale_factor: f64,
|
||||
) {
|
||||
self.ivars().pending_events.borrow_mut().push_back(QueuedEvent::ScaleFactorChanged {
|
||||
window,
|
||||
suggested_size,
|
||||
scale_factor,
|
||||
});
|
||||
}
|
||||
|
||||
pub fn handle_redraw(&self, window_id: WindowId) {
|
||||
@@ -278,27 +284,9 @@ impl ApplicationDelegate {
|
||||
if !pending_redraw.contains(&window_id) {
|
||||
pending_redraw.push(window_id);
|
||||
}
|
||||
self.ivars().run_loop.wakeup();
|
||||
unsafe { RunLoop::get() }.wakeup();
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn maybe_queue_event(&self, event: Event<HandlePendingUserEvents>) {
|
||||
// Most programmer actions in AppKit (e.g. change window fullscreen, set focused, etc.)
|
||||
// result in an event being queued, and applied at a later point.
|
||||
//
|
||||
// However, it is not documented which actions do this, and which ones are done immediately,
|
||||
// so to make sure that we don't encounter re-entrancy issues, we first check if we're
|
||||
// currently handling another event, and if we are, we queue the event instead.
|
||||
if !self.ivars().event_handler.in_use() {
|
||||
self.handle_event(event);
|
||||
} else {
|
||||
tracing::debug!(?event, "had to queue event since another is currently being handled");
|
||||
let this = self.retain();
|
||||
self.ivars().run_loop.queue_closure(move || this.handle_event(event));
|
||||
}
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn handle_event(&self, event: Event<HandlePendingUserEvents>) {
|
||||
self.ivars().event_handler.handle_event(event, &ActiveEventLoop::new_root(self.retain()))
|
||||
}
|
||||
@@ -360,6 +348,49 @@ impl ApplicationDelegate {
|
||||
|
||||
self.handle_event(Event::UserEvent(HandlePendingUserEvents));
|
||||
|
||||
let events = mem::take(&mut *self.ivars().pending_events.borrow_mut());
|
||||
for event in events {
|
||||
match event {
|
||||
QueuedEvent::WindowEvent(window_id, event) => {
|
||||
self.handle_event(Event::WindowEvent {
|
||||
window_id: RootWindowId(window_id),
|
||||
event,
|
||||
});
|
||||
},
|
||||
QueuedEvent::DeviceEvent(event) => {
|
||||
self.handle_event(Event::DeviceEvent { device_id: DEVICE_ID, event });
|
||||
},
|
||||
QueuedEvent::ScaleFactorChanged { window, suggested_size, scale_factor } => {
|
||||
let new_inner_size = Arc::new(Mutex::new(suggested_size));
|
||||
let scale_factor_changed_event = Event::WindowEvent {
|
||||
window_id: RootWindowId(window.id()),
|
||||
event: WindowEvent::ScaleFactorChanged {
|
||||
scale_factor,
|
||||
inner_size_writer: InnerSizeWriter::new(Arc::downgrade(
|
||||
&new_inner_size,
|
||||
)),
|
||||
},
|
||||
};
|
||||
|
||||
self.handle_event(scale_factor_changed_event);
|
||||
|
||||
let physical_size = *new_inner_size.lock().unwrap();
|
||||
drop(new_inner_size);
|
||||
if physical_size != suggested_size {
|
||||
let logical_size = physical_size.to_logical(scale_factor);
|
||||
let size = NSSize::new(logical_size.width, logical_size.height);
|
||||
window.setContentSize(size);
|
||||
}
|
||||
|
||||
let resized_event = Event::WindowEvent {
|
||||
window_id: RootWindowId(window.id()),
|
||||
event: WindowEvent::Resized(physical_size),
|
||||
};
|
||||
self.handle_event(resized_event);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
let redraw = mem::take(&mut *self.ivars().pending_redraw.borrow_mut());
|
||||
for window_id in redraw {
|
||||
self.handle_event(Event::WindowEvent {
|
||||
@@ -390,6 +421,17 @@ impl ApplicationDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum QueuedEvent {
|
||||
WindowEvent(WindowId, WindowEvent),
|
||||
DeviceEvent(DeviceEvent),
|
||||
ScaleFactorChanged {
|
||||
window: Retained<WinitWindow>,
|
||||
suggested_size: PhysicalSize<u32>,
|
||||
scale_factor: f64,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct HandlePendingUserEvents;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::cell::RefCell;
|
||||
use std::{fmt, mem};
|
||||
|
||||
use super::app_state::HandlePendingUserEvents;
|
||||
use super::app_delegate::HandlePendingUserEvents;
|
||||
use crate::event::Event;
|
||||
use crate::event_loop::ActiveEventLoop as RootActiveEventLoop;
|
||||
|
||||
@@ -16,7 +16,7 @@ impl fmt::Debug for EventHandlerData {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Default)]
|
||||
pub(crate) struct EventHandler {
|
||||
/// This can be in the following states:
|
||||
/// - Not registered by the event loop (None).
|
||||
@@ -26,10 +26,6 @@ pub(crate) struct EventHandler {
|
||||
}
|
||||
|
||||
impl EventHandler {
|
||||
pub(crate) const fn new() -> Self {
|
||||
Self { inner: RefCell::new(None) }
|
||||
}
|
||||
|
||||
/// Set the event loop handler for the duration of the given closure.
|
||||
///
|
||||
/// This is similar to using the `scoped-tls` or `scoped-tls-hkt` crates
|
||||
|
||||
@@ -21,10 +21,11 @@ use objc2_app_kit::{NSApplication, NSApplicationActivationPolicy, NSWindow};
|
||||
use objc2_foundation::{MainThreadMarker, NSObjectProtocol};
|
||||
|
||||
use super::app::WinitApplication;
|
||||
use super::app_state::{ApplicationDelegate, HandlePendingUserEvents};
|
||||
use super::app_delegate::{ApplicationDelegate, HandlePendingUserEvents};
|
||||
use super::event::dummy_event;
|
||||
use super::monitor::{self, MonitorHandle};
|
||||
use super::observer::setup_control_flow_observers;
|
||||
use crate::application::ApplicationHandler;
|
||||
use crate::error::EventLoopError;
|
||||
use crate::event::Event;
|
||||
use crate::event_loop::{
|
||||
@@ -155,17 +156,28 @@ impl ActiveEventLoop {
|
||||
}
|
||||
}
|
||||
|
||||
fn map_user_event<T: 'static>(
|
||||
mut handler: impl FnMut(Event<T>, &RootWindowTarget),
|
||||
fn map_user_event<T: 'static, A: ApplicationHandler<T>>(
|
||||
app: &mut A,
|
||||
receiver: Rc<mpsc::Receiver<T>>,
|
||||
) -> impl FnMut(Event<HandlePendingUserEvents>, &RootWindowTarget) {
|
||||
move |event, window_target| match event.map_nonuser_event() {
|
||||
Ok(event) => (handler)(event, window_target),
|
||||
Err(_) => {
|
||||
) -> impl FnMut(Event<HandlePendingUserEvents>, &RootWindowTarget) + '_ {
|
||||
move |event, window_target| match event {
|
||||
Event::NewEvents(cause) => app.new_events(window_target, cause),
|
||||
Event::WindowEvent { window_id, event } => {
|
||||
app.window_event(window_target, window_id, event)
|
||||
},
|
||||
Event::DeviceEvent { device_id, event } => {
|
||||
app.device_event(window_target, device_id, event)
|
||||
},
|
||||
Event::UserEvent(_) => {
|
||||
for event in receiver.try_iter() {
|
||||
(handler)(Event::UserEvent(event), window_target);
|
||||
app.user_event(window_target, event);
|
||||
}
|
||||
},
|
||||
Event::Suspended => app.suspended(window_target),
|
||||
Event::Resumed => app.resumed(window_target),
|
||||
Event::AboutToWait => app.about_to_wait(window_target),
|
||||
Event::LoopExiting => app.exiting(window_target),
|
||||
Event::MemoryWarning => app.memory_warning(window_target),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -240,7 +252,7 @@ impl<T> EventLoop<T> {
|
||||
});
|
||||
|
||||
let panic_info: Rc<PanicInfo> = Default::default();
|
||||
setup_control_flow_observers(mtm, Rc::downgrade(&panic_info));
|
||||
setup_control_flow_observers(Rc::downgrade(&panic_info));
|
||||
|
||||
let (sender, receiver) = mpsc::channel();
|
||||
Ok(EventLoop {
|
||||
@@ -260,22 +272,19 @@ impl<T> EventLoop<T> {
|
||||
&self.window_target
|
||||
}
|
||||
|
||||
pub fn run<F>(mut self, handler: F) -> Result<(), EventLoopError>
|
||||
where
|
||||
F: FnMut(Event<T>, &RootWindowTarget),
|
||||
{
|
||||
self.run_on_demand(handler)
|
||||
pub fn run_app<A: ApplicationHandler<T>>(mut self, app: &mut A) -> Result<(), EventLoopError> {
|
||||
self.run_app_on_demand(app)
|
||||
}
|
||||
|
||||
// NB: we don't base this on `pump_events` because for `MacOs` we can't support
|
||||
// `pump_events` elegantly (we just ask to run the loop for a "short" amount of
|
||||
// time and so a layered implementation would end up using a lot of CPU due to
|
||||
// redundant wake ups.
|
||||
pub fn run_on_demand<F>(&mut self, handler: F) -> Result<(), EventLoopError>
|
||||
where
|
||||
F: FnMut(Event<T>, &RootWindowTarget),
|
||||
{
|
||||
let handler = map_user_event(handler, self.receiver.clone());
|
||||
pub fn run_app_on_demand<A: ApplicationHandler<T>>(
|
||||
&mut self,
|
||||
app: &mut A,
|
||||
) -> Result<(), EventLoopError> {
|
||||
let handler = map_user_event(app, self.receiver.clone());
|
||||
|
||||
self.delegate.set_event_handler(handler, || {
|
||||
autoreleasepool(|_| {
|
||||
@@ -310,11 +319,12 @@ impl<T> EventLoop<T> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn pump_events<F>(&mut self, timeout: Option<Duration>, handler: F) -> PumpStatus
|
||||
where
|
||||
F: FnMut(Event<T>, &RootWindowTarget),
|
||||
{
|
||||
let handler = map_user_event(handler, self.receiver.clone());
|
||||
pub fn pump_app_events<A: ApplicationHandler<T>>(
|
||||
&mut self,
|
||||
timeout: Option<Duration>,
|
||||
app: &mut A,
|
||||
) -> PumpStatus {
|
||||
let handler = map_user_event(app, self.receiver.clone());
|
||||
|
||||
self.delegate.set_event_handler(handler, || {
|
||||
autoreleasepool(|_| {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
mod util;
|
||||
|
||||
mod app;
|
||||
mod app_state;
|
||||
mod app_delegate;
|
||||
mod cursor;
|
||||
mod event;
|
||||
mod event_handler;
|
||||
|
||||
@@ -1,28 +1,21 @@
|
||||
//! Utilities for working with `CFRunLoop`.
|
||||
//!
|
||||
//! See Apple's documentation on Run Loops for details:
|
||||
//! <https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Multithreading/RunLoopManagement/RunLoopManagement.html>
|
||||
use std::cell::Cell;
|
||||
use std::ffi::c_void;
|
||||
use std::panic::{AssertUnwindSafe, UnwindSafe};
|
||||
use std::ptr;
|
||||
use std::rc::Weak;
|
||||
use std::time::Instant;
|
||||
|
||||
use block2::Block;
|
||||
use core_foundation::base::{CFIndex, CFOptionFlags, CFRelease, CFTypeRef};
|
||||
use core_foundation::base::{CFIndex, CFOptionFlags, CFRelease};
|
||||
use core_foundation::date::CFAbsoluteTimeGetCurrent;
|
||||
use core_foundation::runloop::{
|
||||
kCFRunLoopAfterWaiting, kCFRunLoopBeforeWaiting, kCFRunLoopCommonModes, kCFRunLoopDefaultMode,
|
||||
kCFRunLoopExit, CFRunLoopActivity, CFRunLoopAddObserver, CFRunLoopAddTimer, CFRunLoopGetMain,
|
||||
kCFRunLoopAfterWaiting, kCFRunLoopBeforeWaiting, kCFRunLoopCommonModes, kCFRunLoopExit,
|
||||
CFRunLoopActivity, CFRunLoopAddObserver, CFRunLoopAddTimer, CFRunLoopGetMain,
|
||||
CFRunLoopObserverCallBack, CFRunLoopObserverContext, CFRunLoopObserverCreate,
|
||||
CFRunLoopObserverRef, CFRunLoopRef, CFRunLoopTimerCreate, CFRunLoopTimerInvalidate,
|
||||
CFRunLoopTimerRef, CFRunLoopTimerSetNextFireDate, CFRunLoopWakeUp,
|
||||
};
|
||||
use objc2_foundation::MainThreadMarker;
|
||||
use tracing::error;
|
||||
|
||||
use super::app_state::ApplicationDelegate;
|
||||
use super::app_delegate::ApplicationDelegate;
|
||||
use super::event_loop::{stop_app_on_panic, PanicInfo};
|
||||
use super::ffi;
|
||||
|
||||
@@ -91,20 +84,10 @@ extern "C" fn control_flow_end_handler(
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct RunLoop(CFRunLoopRef);
|
||||
|
||||
impl Default for RunLoop {
|
||||
fn default() -> Self {
|
||||
Self(ptr::null_mut())
|
||||
}
|
||||
}
|
||||
|
||||
impl RunLoop {
|
||||
pub fn main(mtm: MainThreadMarker) -> Self {
|
||||
// SAFETY: We have a MainThreadMarker here, which means we know we're on the main thread, so
|
||||
// scheduling (and scheduling a non-`Send` block) to that thread is allowed.
|
||||
let _ = mtm;
|
||||
pub unsafe fn get() -> Self {
|
||||
RunLoop(unsafe { CFRunLoopGetMain() })
|
||||
}
|
||||
|
||||
@@ -131,79 +114,9 @@ impl RunLoop {
|
||||
};
|
||||
unsafe { CFRunLoopAddObserver(self.0, observer, kCFRunLoopCommonModes) };
|
||||
}
|
||||
|
||||
/// Submit a closure to run on the main thread as the next step in the run loop, before other
|
||||
/// event sources are processed.
|
||||
///
|
||||
/// This is used for running event handlers, as those are not allowed to run re-entrantly.
|
||||
///
|
||||
/// # Implementation
|
||||
///
|
||||
/// This queuing could be implemented in the following several ways with subtle differences in
|
||||
/// timing. This list is sorted in rough order in which they are run:
|
||||
///
|
||||
/// 1. Using `CFRunLoopPerformBlock` or `-[NSRunLoop performBlock:]`.
|
||||
///
|
||||
/// 2. Using `-[NSObject performSelectorOnMainThread:withObject:waitUntilDone:]` or wrapping the
|
||||
/// event in `NSEvent` and posting that to `-[NSApplication postEvent:atStart:]` (both
|
||||
/// creates a custom `CFRunLoopSource`, and signals that to wake up the main event loop).
|
||||
///
|
||||
/// a. `atStart = true`.
|
||||
///
|
||||
/// b. `atStart = false`.
|
||||
///
|
||||
/// 3. `dispatch_async` or `dispatch_async_f`. Note that this may appear before 2b, it does not
|
||||
/// respect the ordering that runloop events have.
|
||||
///
|
||||
/// We choose the first one, both for ease-of-implementation, but mostly for consistency, as we
|
||||
/// want the event to be queued in a way that preserves the order the events originally arrived
|
||||
/// in.
|
||||
///
|
||||
/// As an example, let's assume that we receive two events from the user, a mouse click which we
|
||||
/// handled by queuing it, and a window resize which we handled immediately. If we allowed
|
||||
/// AppKit to choose the ordering when queuing the mouse event, it might get put in the back of
|
||||
/// the queue, and the events would appear out of order to the user of Winit. So we must instead
|
||||
/// put the event at the very front of the queue, to be handled as soon as possible after
|
||||
/// handling whatever event it's currently handling.
|
||||
pub fn queue_closure(&self, closure: impl FnOnce() + 'static) {
|
||||
extern "C" {
|
||||
fn CFRunLoopPerformBlock(rl: CFRunLoopRef, mode: CFTypeRef, block: &Block<dyn Fn()>);
|
||||
}
|
||||
|
||||
// Convert `FnOnce()` to `Block<dyn Fn()>`.
|
||||
let closure = Cell::new(Some(closure));
|
||||
let block = block2::RcBlock::new(move || {
|
||||
if let Some(closure) = closure.take() {
|
||||
closure()
|
||||
} else {
|
||||
error!("tried to execute queued closure on main thread twice");
|
||||
}
|
||||
});
|
||||
|
||||
// There are a few common modes (`kCFRunLoopCommonModes`) defined by Cocoa:
|
||||
// - `NSDefaultRunLoopMode`, alias of `kCFRunLoopDefaultMode`.
|
||||
// - `NSEventTrackingRunLoopMode`, used when mouse-dragging and live-resizing a window.
|
||||
// - `NSModalPanelRunLoopMode`, used when running a modal inside the Winit event loop.
|
||||
// - `NSConnectionReplyMode`: TODO.
|
||||
//
|
||||
// We only want to run event handlers in the default mode, as we support running a blocking
|
||||
// modal inside a Winit event handler (see [#1779]) which outrules the modal panel mode, and
|
||||
// resizing such panel window enters the event tracking run loop mode, so we can't directly
|
||||
// trigger events inside that mode either.
|
||||
//
|
||||
// Any events that are queued while running a modal or when live-resizing will instead wait,
|
||||
// and be delivered to the application afterwards.
|
||||
//
|
||||
// [#1779]: https://github.com/rust-windowing/winit/issues/1779
|
||||
let mode = unsafe { kCFRunLoopDefaultMode as CFTypeRef };
|
||||
|
||||
// SAFETY: The runloop is valid, the mode is a `CFStringRef`, and the block is `'static`.
|
||||
unsafe { CFRunLoopPerformBlock(self.0, mode, &block) }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn setup_control_flow_observers(mtm: MainThreadMarker, panic_info: Weak<PanicInfo>) {
|
||||
let run_loop = RunLoop::main(mtm);
|
||||
pub fn setup_control_flow_observers(panic_info: Weak<PanicInfo>) {
|
||||
unsafe {
|
||||
let mut context = CFRunLoopObserverContext {
|
||||
info: Weak::into_raw(panic_info) as *mut _,
|
||||
@@ -212,6 +125,7 @@ pub fn setup_control_flow_observers(mtm: MainThreadMarker, panic_info: Weak<Pani
|
||||
release: None,
|
||||
copyDescription: None,
|
||||
};
|
||||
let run_loop = RunLoop::get();
|
||||
run_loop.add_observer(
|
||||
kCFRunLoopAfterWaiting,
|
||||
CFIndex::min_value(),
|
||||
@@ -251,8 +165,8 @@ impl Drop for EventLoopWaker {
|
||||
}
|
||||
}
|
||||
|
||||
impl EventLoopWaker {
|
||||
pub(crate) fn new() -> Self {
|
||||
impl Default for EventLoopWaker {
|
||||
fn default() -> EventLoopWaker {
|
||||
extern "C" fn wakeup_main_loop(_timer: CFRunLoopTimerRef, _info: *mut c_void) {}
|
||||
unsafe {
|
||||
// Create a timer with a 0.1µs interval (1ns does not work) to mimic polling.
|
||||
@@ -268,10 +182,12 @@ impl EventLoopWaker {
|
||||
ptr::null_mut(),
|
||||
);
|
||||
CFRunLoopAddTimer(CFRunLoopGetMain(), timer, kCFRunLoopCommonModes);
|
||||
Self { timer, start_instant: Instant::now(), next_fire_date: None }
|
||||
EventLoopWaker { timer, start_instant: Instant::now(), next_fire_date: None }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl EventLoopWaker {
|
||||
pub fn stop(&mut self) {
|
||||
if self.next_fire_date.is_some() {
|
||||
self.next_fire_date = None;
|
||||
|
||||
@@ -16,7 +16,7 @@ use objc2_foundation::{
|
||||
NSPoint, NSRange, NSRect, NSSize, NSString, NSUInteger,
|
||||
};
|
||||
|
||||
use super::app_state::ApplicationDelegate;
|
||||
use super::app_delegate::ApplicationDelegate;
|
||||
use super::cursor::{default_cursor, invisible_cursor};
|
||||
use super::event::{
|
||||
code_to_key, code_to_location, create_key_event, event_mods, lalt_pressed, ralt_pressed,
|
||||
@@ -270,20 +270,19 @@ declare_class!(
|
||||
fn set_marked_text(
|
||||
&self,
|
||||
string: &NSObject,
|
||||
selected_range: NSRange,
|
||||
_selected_range: NSRange,
|
||||
_replacement_range: NSRange,
|
||||
) {
|
||||
// TODO: Use _replacement_range, requires changing the event to report surrounding text.
|
||||
trace_scope!("setMarkedText:selectedRange:replacementRange:");
|
||||
|
||||
// SAFETY: This method is guaranteed to get either a `NSString` or a `NSAttributedString`.
|
||||
let (marked_text, string) = if string.is_kind_of::<NSAttributedString>() {
|
||||
let (marked_text, preedit_string) = if string.is_kind_of::<NSAttributedString>() {
|
||||
let string: *const NSObject = string;
|
||||
let string: *const NSAttributedString = string.cast();
|
||||
let string = unsafe { &*string };
|
||||
(
|
||||
NSMutableAttributedString::from_attributed_nsstring(string),
|
||||
string.string(),
|
||||
string.string().to_string(),
|
||||
)
|
||||
} else {
|
||||
let string: *const NSObject = string;
|
||||
@@ -291,7 +290,7 @@ declare_class!(
|
||||
let string = unsafe { &*string };
|
||||
(
|
||||
NSMutableAttributedString::from_nsstring(string),
|
||||
string.copy(),
|
||||
string.to_string(),
|
||||
)
|
||||
};
|
||||
|
||||
@@ -311,21 +310,16 @@ declare_class!(
|
||||
self.ivars().ime_state.set(ImeState::Ground);
|
||||
}
|
||||
|
||||
let cursor_range = if string.is_empty() {
|
||||
// An empty string basically means that there's no preedit, so indicate that by
|
||||
// sending a `None` cursor range.
|
||||
// Empty string basically means that there's no preedit, so indicate that by sending
|
||||
// `None` cursor range.
|
||||
let cursor_range = if preedit_string.is_empty() {
|
||||
None
|
||||
} else {
|
||||
// Convert the selected range from UTF-16 indices to UTF-8 indices.
|
||||
let sub_string_a = unsafe { string.substringToIndex(selected_range.location) };
|
||||
let sub_string_b = unsafe { string.substringToIndex(selected_range.end()) };
|
||||
let lowerbound_utf8 = sub_string_a.len();
|
||||
let upperbound_utf8 = sub_string_b.len();
|
||||
Some((lowerbound_utf8, upperbound_utf8))
|
||||
Some((preedit_string.len(), preedit_string.len()))
|
||||
};
|
||||
|
||||
// Send WindowEvent for updating marked text
|
||||
self.queue_event(WindowEvent::Ime(Ime::Preedit(string.to_string(), cursor_range)));
|
||||
self.queue_event(WindowEvent::Ime(Ime::Preedit(preedit_string, cursor_range)));
|
||||
}
|
||||
|
||||
#[method(unmarkText)]
|
||||
@@ -385,7 +379,6 @@ declare_class!(
|
||||
|
||||
#[method(insertText:replacementRange:)]
|
||||
fn insert_text(&self, string: &NSObject, _replacement_range: NSRange) {
|
||||
// TODO: Use _replacement_range, requires changing the event to report surrounding text.
|
||||
trace_scope!("insertText:replacementRange:");
|
||||
|
||||
// SAFETY: This method is guaranteed to get either a `NSString` or a `NSAttributedString`.
|
||||
@@ -686,7 +679,7 @@ declare_class!(
|
||||
|
||||
self.update_modifiers(event, false);
|
||||
|
||||
self.ivars().app_delegate.maybe_queue_device_event(DeviceEvent::MouseWheel { delta });
|
||||
self.queue_device_event(DeviceEvent::MouseWheel { delta });
|
||||
self.queue_event(WindowEvent::MouseWheel {
|
||||
device_id: DEVICE_ID,
|
||||
delta,
|
||||
@@ -830,7 +823,11 @@ impl WinitView {
|
||||
}
|
||||
|
||||
fn queue_event(&self, event: WindowEvent) {
|
||||
self.ivars().app_delegate.maybe_queue_window_event(self.window().id(), event);
|
||||
self.ivars().app_delegate.queue_window_event(self.window().id(), event);
|
||||
}
|
||||
|
||||
fn queue_device_event(&self, event: DeviceEvent) {
|
||||
self.ivars().app_delegate.queue_device_event(event);
|
||||
}
|
||||
|
||||
fn scale_factor(&self) -> f64 {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
#![allow(clippy::unnecessary_cast)]
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::collections::VecDeque;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use core_graphics::display::{CGDisplay, CGPoint};
|
||||
use monitor::VideoModeHandle;
|
||||
@@ -22,16 +21,15 @@ use objc2_foundation::{
|
||||
NSPoint, NSRect, NSSize, NSString,
|
||||
};
|
||||
|
||||
use super::app_state::ApplicationDelegate;
|
||||
use super::app_delegate::ApplicationDelegate;
|
||||
use super::cursor::cursor_from_icon;
|
||||
use super::monitor::{self, flip_window_screen_coordinates, get_display_id};
|
||||
use super::observer::RunLoop;
|
||||
use super::view::WinitView;
|
||||
use super::window::WinitWindow;
|
||||
use super::{ffi, Fullscreen, MonitorHandle, OsError, WindowId};
|
||||
use crate::dpi::{LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize, Position, Size};
|
||||
use crate::error::{ExternalError, NotSupportedError, OsError as RootOsError};
|
||||
use crate::event::{InnerSizeWriter, WindowEvent};
|
||||
use crate::event::WindowEvent;
|
||||
use crate::platform::macos::{OptionAsAlt, WindowExtMacOS};
|
||||
use crate::window::{
|
||||
Cursor, CursorGrabMode, Icon, ImePurpose, ResizeDirection, Theme, UserAttentionType,
|
||||
@@ -186,17 +184,7 @@ declare_class!(
|
||||
#[method(windowDidChangeBackingProperties:)]
|
||||
fn window_did_change_backing_properties(&self, _: Option<&AnyObject>) {
|
||||
trace_scope!("windowDidChangeBackingProperties:");
|
||||
let scale_factor = self.scale_factor();
|
||||
if scale_factor == self.ivars().previous_scale_factor.get() {
|
||||
return;
|
||||
};
|
||||
self.ivars().previous_scale_factor.set(scale_factor);
|
||||
|
||||
let mtm = MainThreadMarker::from(self);
|
||||
let this = self.retain();
|
||||
RunLoop::main(mtm).queue_closure(move || {
|
||||
this.handle_scale_factor_changed(scale_factor);
|
||||
});
|
||||
self.queue_static_scale_factor_changed_event();
|
||||
}
|
||||
|
||||
#[method(windowDidBecomeKey:)]
|
||||
@@ -693,10 +681,7 @@ impl WindowDelegate {
|
||||
let delegate: Retained<WindowDelegate> = unsafe { msg_send_id![super(delegate), init] };
|
||||
|
||||
if scale_factor != 1.0 {
|
||||
let delegate = delegate.clone();
|
||||
RunLoop::main(mtm).queue_closure(move || {
|
||||
delegate.handle_scale_factor_changed(scale_factor);
|
||||
});
|
||||
delegate.queue_static_scale_factor_changed_event();
|
||||
}
|
||||
window.setDelegate(Some(ProtocolObject::from_ref(&*delegate)));
|
||||
|
||||
@@ -769,31 +754,24 @@ impl WindowDelegate {
|
||||
}
|
||||
|
||||
pub(crate) fn queue_event(&self, event: WindowEvent) {
|
||||
self.ivars().app_delegate.maybe_queue_window_event(self.window().id(), event);
|
||||
self.ivars().app_delegate.queue_window_event(self.window().id(), event);
|
||||
}
|
||||
|
||||
fn handle_scale_factor_changed(&self, scale_factor: CGFloat) {
|
||||
let app_delegate = &self.ivars().app_delegate;
|
||||
let window = self.window();
|
||||
fn queue_static_scale_factor_changed_event(&self) {
|
||||
let scale_factor = self.scale_factor();
|
||||
if scale_factor == self.ivars().previous_scale_factor.get() {
|
||||
return;
|
||||
};
|
||||
|
||||
let content_size = window.contentRectForFrameRect(window.frame()).size;
|
||||
self.ivars().previous_scale_factor.set(scale_factor);
|
||||
let content_size = self.window().contentRectForFrameRect(self.window().frame()).size;
|
||||
let content_size = LogicalSize::new(content_size.width, content_size.height);
|
||||
|
||||
let suggested_size = content_size.to_physical(scale_factor);
|
||||
let new_inner_size = Arc::new(Mutex::new(suggested_size));
|
||||
app_delegate.handle_window_event(window.id(), WindowEvent::ScaleFactorChanged {
|
||||
self.ivars().app_delegate.queue_static_scale_factor_changed_event(
|
||||
self.window().retain(),
|
||||
content_size.to_physical(scale_factor),
|
||||
scale_factor,
|
||||
inner_size_writer: InnerSizeWriter::new(Arc::downgrade(&new_inner_size)),
|
||||
});
|
||||
let physical_size = *new_inner_size.lock().unwrap();
|
||||
drop(new_inner_size);
|
||||
|
||||
if physical_size != suggested_size {
|
||||
let logical_size = physical_size.to_logical(scale_factor);
|
||||
let size = NSSize::new(logical_size.width, logical_size.height);
|
||||
window.setContentSize(size);
|
||||
}
|
||||
app_delegate.handle_window_event(window.id(), WindowEvent::Resized(physical_size));
|
||||
);
|
||||
}
|
||||
|
||||
fn emit_move_event(&self) {
|
||||
|
||||
@@ -12,6 +12,7 @@ use orbclient::{
|
||||
};
|
||||
use smol_str::SmolStr;
|
||||
|
||||
use crate::application::ApplicationHandler;
|
||||
use crate::error::EventLoopError;
|
||||
use crate::event::{self, Ime, Modifiers, StartCause};
|
||||
use crate::event_loop::{self, ControlFlow, DeviceEvents};
|
||||
@@ -322,14 +323,13 @@ impl<T: 'static> EventLoop<T> {
|
||||
})
|
||||
}
|
||||
|
||||
fn process_event<F>(
|
||||
fn process_event<A: ApplicationHandler<T>>(
|
||||
window_id: WindowId,
|
||||
event_option: EventOption,
|
||||
event_state: &mut EventState,
|
||||
mut event_handler: F,
|
||||
) where
|
||||
F: FnMut(event::Event<T>),
|
||||
{
|
||||
window_target: &event_loop::ActiveEventLoop,
|
||||
app: &mut A,
|
||||
) {
|
||||
match event_option {
|
||||
EventOption::Key(KeyEvent { character, scancode, pressed }) => {
|
||||
// Convert scancode
|
||||
@@ -371,125 +371,128 @@ impl<T: 'static> EventLoop<T> {
|
||||
key_without_modifiers = logical_key.clone();
|
||||
}
|
||||
|
||||
event_handler(event::Event::WindowEvent {
|
||||
window_id: RootWindowId(window_id),
|
||||
event: event::WindowEvent::KeyboardInput {
|
||||
device_id: event::DeviceId(DeviceId),
|
||||
event: event::KeyEvent {
|
||||
logical_key,
|
||||
physical_key,
|
||||
location: KeyLocation::Standard,
|
||||
state: element_state(pressed),
|
||||
repeat: false,
|
||||
text,
|
||||
platform_specific: KeyEventExtra {
|
||||
key_without_modifiers,
|
||||
text_with_all_modifiers,
|
||||
},
|
||||
let window_id = RootWindowId(window_id);
|
||||
let event = event::WindowEvent::KeyboardInput {
|
||||
device_id: event::DeviceId(DeviceId),
|
||||
event: event::KeyEvent {
|
||||
logical_key,
|
||||
physical_key,
|
||||
location: KeyLocation::Standard,
|
||||
state: element_state(pressed),
|
||||
repeat: false,
|
||||
text,
|
||||
platform_specific: KeyEventExtra {
|
||||
key_without_modifiers,
|
||||
text_with_all_modifiers,
|
||||
},
|
||||
is_synthetic: false,
|
||||
},
|
||||
});
|
||||
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 {
|
||||
event_handler(event::Event::WindowEvent {
|
||||
window_id: RootWindowId(window_id),
|
||||
event: event::WindowEvent::ModifiersChanged(event_state.modifiers()),
|
||||
})
|
||||
app.window_event(
|
||||
window_target,
|
||||
window_id,
|
||||
event::WindowEvent::ModifiersChanged(event_state.modifiers()),
|
||||
);
|
||||
}
|
||||
},
|
||||
EventOption::TextInput(TextInputEvent { character }) => {
|
||||
event_handler(event::Event::WindowEvent {
|
||||
window_id: RootWindowId(window_id),
|
||||
event: event::WindowEvent::Ime(Ime::Preedit("".into(), None)),
|
||||
});
|
||||
event_handler(event::Event::WindowEvent {
|
||||
window_id: RootWindowId(window_id),
|
||||
event: event::WindowEvent::Ime(Ime::Commit(character.into())),
|
||||
});
|
||||
app.window_event(
|
||||
window_target,
|
||||
RootWindowId(window_id),
|
||||
event::WindowEvent::Ime(Ime::Preedit("".into(), None)),
|
||||
);
|
||||
app.window_event(
|
||||
window_target,
|
||||
RootWindowId(window_id),
|
||||
event::WindowEvent::Ime(Ime::Commit(character.into())),
|
||||
);
|
||||
},
|
||||
EventOption::Mouse(MouseEvent { x, y }) => {
|
||||
event_handler(event::Event::WindowEvent {
|
||||
window_id: RootWindowId(window_id),
|
||||
event: event::WindowEvent::CursorMoved {
|
||||
app.window_event(
|
||||
window_target,
|
||||
RootWindowId(window_id),
|
||||
event::WindowEvent::CursorMoved {
|
||||
device_id: event::DeviceId(DeviceId),
|
||||
position: (x, y).into(),
|
||||
},
|
||||
});
|
||||
);
|
||||
},
|
||||
EventOption::MouseRelative(MouseRelativeEvent { dx, dy }) => {
|
||||
event_handler(event::Event::DeviceEvent {
|
||||
device_id: event::DeviceId(DeviceId),
|
||||
event: event::DeviceEvent::MouseMotion { delta: (dx as f64, dy as f64) },
|
||||
});
|
||||
app.device_event(
|
||||
window_target,
|
||||
event::DeviceId(DeviceId),
|
||||
event::DeviceEvent::MouseMotion { delta: (dx as f64, dy as f64) },
|
||||
);
|
||||
},
|
||||
EventOption::Button(ButtonEvent { left, middle, right }) => {
|
||||
while let Some((button, state)) = event_state.mouse(left, middle, right) {
|
||||
event_handler(event::Event::WindowEvent {
|
||||
window_id: RootWindowId(window_id),
|
||||
event: event::WindowEvent::MouseInput {
|
||||
app.window_event(
|
||||
window_target,
|
||||
RootWindowId(window_id),
|
||||
event::WindowEvent::MouseInput {
|
||||
device_id: event::DeviceId(DeviceId),
|
||||
state,
|
||||
button,
|
||||
},
|
||||
});
|
||||
);
|
||||
}
|
||||
},
|
||||
EventOption::Scroll(ScrollEvent { x, y }) => {
|
||||
event_handler(event::Event::WindowEvent {
|
||||
window_id: RootWindowId(window_id),
|
||||
event: event::WindowEvent::MouseWheel {
|
||||
app.window_event(
|
||||
window_target,
|
||||
RootWindowId(window_id),
|
||||
event::WindowEvent::MouseWheel {
|
||||
device_id: event::DeviceId(DeviceId),
|
||||
delta: event::MouseScrollDelta::LineDelta(x as f32, y as f32),
|
||||
phase: event::TouchPhase::Moved,
|
||||
},
|
||||
});
|
||||
);
|
||||
},
|
||||
EventOption::Quit(QuitEvent {}) => {
|
||||
event_handler(event::Event::WindowEvent {
|
||||
window_id: RootWindowId(window_id),
|
||||
event: event::WindowEvent::CloseRequested,
|
||||
});
|
||||
app.window_event(
|
||||
window_target,
|
||||
RootWindowId(window_id),
|
||||
event::WindowEvent::CloseRequested,
|
||||
);
|
||||
},
|
||||
EventOption::Focus(FocusEvent { focused }) => {
|
||||
event_handler(event::Event::WindowEvent {
|
||||
window_id: RootWindowId(window_id),
|
||||
event: event::WindowEvent::Focused(focused),
|
||||
});
|
||||
app.window_event(
|
||||
window_target,
|
||||
RootWindowId(window_id),
|
||||
event::WindowEvent::Focused(focused),
|
||||
);
|
||||
},
|
||||
EventOption::Move(MoveEvent { x, y }) => {
|
||||
event_handler(event::Event::WindowEvent {
|
||||
window_id: RootWindowId(window_id),
|
||||
event: event::WindowEvent::Moved((x, y).into()),
|
||||
});
|
||||
app.window_event(
|
||||
window_target,
|
||||
RootWindowId(window_id),
|
||||
event::WindowEvent::Moved((x, y).into()),
|
||||
);
|
||||
},
|
||||
EventOption::Resize(ResizeEvent { width, height }) => {
|
||||
event_handler(event::Event::WindowEvent {
|
||||
window_id: RootWindowId(window_id),
|
||||
event: event::WindowEvent::Resized((width, height).into()),
|
||||
});
|
||||
app.window_event(
|
||||
window_target,
|
||||
RootWindowId(window_id),
|
||||
event::WindowEvent::Resized((width, height).into()),
|
||||
);
|
||||
|
||||
// Acknowledge resize after event loop.
|
||||
event_state.resize_opt = Some((width, height));
|
||||
},
|
||||
// TODO: Screen, Clipboard, Drop
|
||||
EventOption::Hover(HoverEvent { entered }) => {
|
||||
if entered {
|
||||
event_handler(event::Event::WindowEvent {
|
||||
window_id: RootWindowId(window_id),
|
||||
event: event::WindowEvent::CursorEntered {
|
||||
device_id: event::DeviceId(DeviceId),
|
||||
},
|
||||
});
|
||||
let event = if entered {
|
||||
event::WindowEvent::CursorEntered { device_id: event::DeviceId(DeviceId) }
|
||||
} else {
|
||||
event_handler(event::Event::WindowEvent {
|
||||
window_id: RootWindowId(window_id),
|
||||
event: event::WindowEvent::CursorLeft {
|
||||
device_id: event::DeviceId(DeviceId),
|
||||
},
|
||||
});
|
||||
}
|
||||
event::WindowEvent::CursorLeft { device_id: event::DeviceId(DeviceId) }
|
||||
};
|
||||
|
||||
app.window_event(window_target, RootWindowId(window_id), event);
|
||||
},
|
||||
other => {
|
||||
tracing::warn!("unhandled event: {:?}", other);
|
||||
@@ -497,22 +500,13 @@ impl<T: 'static> EventLoop<T> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run<F>(mut self, mut event_handler_inner: F) -> Result<(), EventLoopError>
|
||||
where
|
||||
F: FnMut(event::Event<T>, &event_loop::ActiveEventLoop),
|
||||
{
|
||||
let mut event_handler =
|
||||
move |event: event::Event<T>, window_target: &event_loop::ActiveEventLoop| {
|
||||
event_handler_inner(event, window_target);
|
||||
};
|
||||
|
||||
pub fn run_app<A: ApplicationHandler<T>>(mut self, app: &mut A) -> Result<(), EventLoopError> {
|
||||
let mut start_cause = StartCause::Init;
|
||||
|
||||
loop {
|
||||
event_handler(event::Event::NewEvents(start_cause), &self.window_target);
|
||||
app.new_events(&self.window_target, start_cause);
|
||||
|
||||
if start_cause == StartCause::Init {
|
||||
event_handler(event::Event::Resumed, &self.window_target);
|
||||
app.resumed(&self.window_target);
|
||||
}
|
||||
|
||||
// Handle window creates.
|
||||
@@ -528,23 +522,15 @@ impl<T: 'static> EventLoop<T> {
|
||||
|
||||
self.windows.push((window, EventState::default()));
|
||||
|
||||
// Send resize event on create to indicate first size.
|
||||
event_handler(
|
||||
event::Event::WindowEvent {
|
||||
window_id: RootWindowId(window_id),
|
||||
event: event::WindowEvent::Resized((properties.w, properties.h).into()),
|
||||
},
|
||||
&self.window_target,
|
||||
);
|
||||
let window_id = RootWindowId(window_id);
|
||||
|
||||
// Send resize event on create to indicate first position.
|
||||
event_handler(
|
||||
event::Event::WindowEvent {
|
||||
window_id: RootWindowId(window_id),
|
||||
event: event::WindowEvent::Moved((properties.x, properties.y).into()),
|
||||
},
|
||||
&self.window_target,
|
||||
);
|
||||
// Send resize event on create to indicate first size.
|
||||
let event = event::WindowEvent::Resized((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.
|
||||
@@ -552,14 +538,8 @@ impl<T: 'static> EventLoop<T> {
|
||||
let mut destroys = self.window_target.p.destroys.lock().unwrap();
|
||||
destroys.pop_front()
|
||||
} {
|
||||
event_handler(
|
||||
event::Event::WindowEvent {
|
||||
window_id: RootWindowId(destroy_id),
|
||||
event: event::WindowEvent::Destroyed,
|
||||
},
|
||||
&self.window_target,
|
||||
);
|
||||
|
||||
let window_id = RootWindowId(destroy_id);
|
||||
app.window_event(&self.window_target, window_id, event::WindowEvent::Destroyed);
|
||||
self.windows.retain(|(window, _event_state)| window.fd as u64 != destroy_id.fd);
|
||||
}
|
||||
|
||||
@@ -586,7 +566,8 @@ impl<T: 'static> EventLoop<T> {
|
||||
window_id,
|
||||
orbital_event.to_option(),
|
||||
event_state,
|
||||
|event| event_handler(event, &self.window_target),
|
||||
&self.window_target,
|
||||
app,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -614,7 +595,7 @@ impl<T: 'static> EventLoop<T> {
|
||||
}
|
||||
|
||||
while let Ok(event) = self.user_events_receiver.try_recv() {
|
||||
event_handler(event::Event::UserEvent(event), &self.window_target);
|
||||
app.user_event(&self.window_target, event);
|
||||
}
|
||||
|
||||
// To avoid deadlocks the redraws lock is not held during event processing.
|
||||
@@ -622,16 +603,14 @@ impl<T: 'static> EventLoop<T> {
|
||||
let mut redraws = self.window_target.p.redraws.lock().unwrap();
|
||||
redraws.pop_front()
|
||||
} {
|
||||
event_handler(
|
||||
event::Event::WindowEvent {
|
||||
window_id: RootWindowId(window_id),
|
||||
event: event::WindowEvent::RedrawRequested,
|
||||
},
|
||||
app.window_event(
|
||||
&self.window_target,
|
||||
RootWindowId(window_id),
|
||||
event::WindowEvent::RedrawRequested,
|
||||
);
|
||||
}
|
||||
|
||||
event_handler(event::Event::AboutToWait, &self.window_target);
|
||||
app.about_to_wait(&self.window_target);
|
||||
|
||||
if self.window_target.p.exiting() {
|
||||
break;
|
||||
@@ -695,7 +674,7 @@ impl<T: 'static> EventLoop<T> {
|
||||
}
|
||||
}
|
||||
|
||||
event_handler(event::Event::LoopExiting, &self.window_target);
|
||||
app.exiting(&self.window_target);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use std::marker::PhantomData;
|
||||
use std::sync::mpsc::{self, Receiver, Sender};
|
||||
|
||||
use crate::application::ApplicationHandler;
|
||||
use crate::error::EventLoopError;
|
||||
use crate::event::Event;
|
||||
use crate::event_loop::ActiveEventLoop as RootActiveEventLoop;
|
||||
@@ -31,25 +32,13 @@ impl<T> EventLoop<T> {
|
||||
Ok(EventLoop { elw, user_event_sender, user_event_receiver })
|
||||
}
|
||||
|
||||
pub fn run<F>(self, mut event_handler: F) -> !
|
||||
where
|
||||
F: FnMut(Event<T>, &RootActiveEventLoop),
|
||||
{
|
||||
pub fn run_app<A: ApplicationHandler<T>>(self, app: &mut A) -> ! {
|
||||
let target = RootActiveEventLoop { p: self.elw.p.clone(), _marker: PhantomData };
|
||||
|
||||
// SAFETY: Don't use `move` to make sure we leak the `event_handler` and `target`.
|
||||
let handler: Box<dyn FnMut(Event<()>)> = Box::new(|event| {
|
||||
let event = match event.map_nonuser_event() {
|
||||
Ok(event) => event,
|
||||
Err(Event::UserEvent(())) => Event::UserEvent(
|
||||
self.user_event_receiver
|
||||
.try_recv()
|
||||
.expect("handler woken up without user event"),
|
||||
),
|
||||
Err(_) => unreachable!(),
|
||||
};
|
||||
event_handler(event, &target)
|
||||
});
|
||||
let handler: Box<dyn FnMut(Event<()>)> =
|
||||
Box::new(|event| handle_event(app, &target, &self.user_event_receiver, event));
|
||||
|
||||
// SAFETY: The `transmute` is necessary because `run()` requires `'static`. This is safe
|
||||
// because this function will never return and all resources not cleaned up by the point we
|
||||
// `throw` will leak, making this actually `'static`.
|
||||
@@ -65,24 +54,12 @@ impl<T> EventLoop<T> {
|
||||
unreachable!();
|
||||
}
|
||||
|
||||
pub fn spawn<F>(self, mut event_handler: F)
|
||||
where
|
||||
F: 'static + FnMut(Event<T>, &RootActiveEventLoop),
|
||||
{
|
||||
pub fn spawn_app<A: ApplicationHandler<T> + 'static>(self, mut app: A) {
|
||||
let target = RootActiveEventLoop { p: self.elw.p.clone(), _marker: PhantomData };
|
||||
|
||||
self.elw.p.run(
|
||||
Box::new(move |event| {
|
||||
let event = match event.map_nonuser_event() {
|
||||
Ok(event) => event,
|
||||
Err(Event::UserEvent(())) => Event::UserEvent(
|
||||
self.user_event_receiver
|
||||
.try_recv()
|
||||
.expect("handler woken up without user event"),
|
||||
),
|
||||
Err(_) => unreachable!(),
|
||||
};
|
||||
event_handler(event, &target)
|
||||
handle_event(&mut app, &target, &self.user_event_receiver, event)
|
||||
}),
|
||||
true,
|
||||
);
|
||||
@@ -96,3 +73,26 @@ impl<T> EventLoop<T> {
|
||||
&self.elw
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_event<T: 'static, A: ApplicationHandler<T>>(
|
||||
app: &mut A,
|
||||
target: &RootActiveEventLoop,
|
||||
user_event_receiver: &Receiver<T>,
|
||||
event: Event<()>,
|
||||
) {
|
||||
match event {
|
||||
Event::NewEvents(cause) => app.new_events(target, cause),
|
||||
Event::WindowEvent { window_id, event } => app.window_event(target, window_id, event),
|
||||
Event::DeviceEvent { device_id, event } => app.device_event(target, device_id, event),
|
||||
Event::UserEvent(_) => {
|
||||
let event =
|
||||
user_event_receiver.try_recv().expect("user event signaled but not received");
|
||||
app.user_event(target, event);
|
||||
},
|
||||
Event::Suspended => app.suspended(target),
|
||||
Event::Resumed => app.resumed(target),
|
||||
Event::AboutToWait => app.about_to_wait(target),
|
||||
Event::LoopExiting => app.exiting(target),
|
||||
Event::MemoryWarning => app.memory_warning(target),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,6 +57,7 @@ use windows_sys::Win32::UI::WindowsAndMessaging::{
|
||||
WS_EX_NOACTIVATE, WS_EX_TOOLWINDOW, WS_EX_TRANSPARENT, WS_OVERLAPPED, WS_POPUP, WS_VISIBLE,
|
||||
};
|
||||
|
||||
use crate::application::ApplicationHandler;
|
||||
use crate::dpi::{PhysicalPosition, PhysicalSize};
|
||||
use crate::error::EventLoopError;
|
||||
use crate::event::{
|
||||
@@ -215,17 +216,14 @@ impl<T: 'static> EventLoop<T> {
|
||||
&self.window_target
|
||||
}
|
||||
|
||||
pub fn run<F>(mut self, event_handler: F) -> Result<(), EventLoopError>
|
||||
where
|
||||
F: FnMut(Event<T>, &RootAEL),
|
||||
{
|
||||
self.run_on_demand(event_handler)
|
||||
pub fn run_app<A: ApplicationHandler<T>>(mut self, app: &mut A) -> Result<(), EventLoopError> {
|
||||
self.run_app_on_demand(app)
|
||||
}
|
||||
|
||||
pub fn run_on_demand<F>(&mut self, mut event_handler: F) -> Result<(), EventLoopError>
|
||||
where
|
||||
F: FnMut(Event<T>, &RootAEL),
|
||||
{
|
||||
pub fn run_app_on_demand<A: ApplicationHandler<T>>(
|
||||
&mut self,
|
||||
app: &mut A,
|
||||
) -> Result<(), EventLoopError> {
|
||||
{
|
||||
let runner = &self.window_target.p.runner_shared;
|
||||
|
||||
@@ -236,21 +234,32 @@ impl<T: 'static> EventLoop<T> {
|
||||
// returning
|
||||
unsafe {
|
||||
runner.set_event_handler(move |event| {
|
||||
// the shared `EventLoopRunner` is not parameterized
|
||||
// `EventLoopProxy::send_event()` calls `PostMessage`
|
||||
// to wakeup and dispatch a placeholder `UserEvent`,
|
||||
// when we received the placeholder event here, the
|
||||
// real UserEvent(T) should already be put in the
|
||||
// mpsc channel and ready to be pulled
|
||||
let event = match event.map_nonuser_event() {
|
||||
Ok(non_user_event) => non_user_event,
|
||||
Err(_user_event_placeholder) => Event::UserEvent(
|
||||
user_event_receiver
|
||||
match event {
|
||||
Event::NewEvents(cause) => app.new_events(event_loop_windows_ref, cause),
|
||||
Event::WindowEvent { window_id, event } => {
|
||||
app.window_event(event_loop_windows_ref, window_id, event)
|
||||
},
|
||||
Event::DeviceEvent { device_id, event } => {
|
||||
app.device_event(event_loop_windows_ref, device_id, event)
|
||||
},
|
||||
// The shared `EventLoopRunner` is not parameterized
|
||||
// `EventLoopProxy::send_event()` calls `PostMessage`
|
||||
// to wakeup and dispatch a placeholder `UserEvent`,
|
||||
// when we received the placeholder event here, the
|
||||
// real UserEvent(T) should already be put in the
|
||||
// mpsc channel and ready to be pulled
|
||||
Event::UserEvent(_) => {
|
||||
let event = user_event_receiver
|
||||
.try_recv()
|
||||
.expect("user event signaled but not received"),
|
||||
),
|
||||
};
|
||||
event_handler(event, event_loop_windows_ref)
|
||||
.expect("user event signaled but not received");
|
||||
app.user_event(event_loop_windows_ref, event);
|
||||
},
|
||||
Event::Suspended => app.suspended(event_loop_windows_ref),
|
||||
Event::Resumed => app.resumed(event_loop_windows_ref),
|
||||
Event::AboutToWait => app.about_to_wait(event_loop_windows_ref),
|
||||
Event::LoopExiting => app.exiting(event_loop_windows_ref),
|
||||
Event::MemoryWarning => app.memory_warning(event_loop_windows_ref),
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -284,10 +293,11 @@ impl<T: 'static> EventLoop<T> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pump_events<F>(&mut self, timeout: Option<Duration>, mut event_handler: F) -> PumpStatus
|
||||
where
|
||||
F: FnMut(Event<T>, &RootAEL),
|
||||
{
|
||||
pub fn pump_app_events<A: ApplicationHandler<T>>(
|
||||
&mut self,
|
||||
timeout: Option<Duration>,
|
||||
app: &mut A,
|
||||
) -> PumpStatus {
|
||||
{
|
||||
let runner = &self.window_target.p.runner_shared;
|
||||
let event_loop_windows_ref = &self.window_target;
|
||||
@@ -302,16 +312,34 @@ impl<T: 'static> EventLoop<T> {
|
||||
// event handler.
|
||||
unsafe {
|
||||
runner.set_event_handler(move |event| {
|
||||
let event = match event.map_nonuser_event() {
|
||||
Ok(non_user_event) => non_user_event,
|
||||
Err(_user_event_placeholder) => Event::UserEvent(
|
||||
user_event_receiver
|
||||
.recv()
|
||||
.expect("user event signaled but not received"),
|
||||
),
|
||||
};
|
||||
event_handler(event, event_loop_windows_ref)
|
||||
match event {
|
||||
Event::NewEvents(cause) => app.new_events(event_loop_windows_ref, cause),
|
||||
Event::WindowEvent { window_id, event } => {
|
||||
app.window_event(event_loop_windows_ref, window_id, event)
|
||||
},
|
||||
Event::DeviceEvent { device_id, event } => {
|
||||
app.device_event(event_loop_windows_ref, device_id, event)
|
||||
},
|
||||
// The shared `EventLoopRunner` is not parameterized
|
||||
// `EventLoopProxy::send_event()` calls `PostMessage`
|
||||
// to wakeup and dispatch a placeholder `UserEvent`,
|
||||
// when we received the placeholder event here, the
|
||||
// real UserEvent(T) should already be put in the
|
||||
// mpsc channel and ready to be pulled
|
||||
Event::UserEvent(_) => {
|
||||
let event = user_event_receiver
|
||||
.try_recv()
|
||||
.expect("user event signaled but not received");
|
||||
app.user_event(event_loop_windows_ref, event);
|
||||
},
|
||||
Event::Suspended => app.suspended(event_loop_windows_ref),
|
||||
Event::Resumed => app.resumed(event_loop_windows_ref),
|
||||
Event::AboutToWait => app.about_to_wait(event_loop_windows_ref),
|
||||
Event::LoopExiting => app.exiting(event_loop_windows_ref),
|
||||
Event::MemoryWarning => app.memory_warning(event_loop_windows_ref),
|
||||
}
|
||||
});
|
||||
|
||||
runner.wakeup();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -632,8 +632,7 @@ impl Window {
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **Android / iOS / X11 / Web / Windows / macOS / Orbital:** Unsupported.
|
||||
/// - **Wayland:** Schedules a frame callback to throttle [`WindowEvent::RedrawRequested`].
|
||||
/// **Wayland:** - schedules a frame callback to throttle [`WindowEvent::RedrawRequested`].
|
||||
///
|
||||
/// [`WindowEvent::RedrawRequested`]: crate::event::WindowEvent::RedrawRequested
|
||||
#[inline]
|
||||
|
||||
Reference in New Issue
Block a user