Compare commits

..

6 Commits

Author SHA1 Message Date
Kirill Chibisov
4b3c0655bf Winit version 0.30.0 2024-04-27 19:00:38 +04:00
Joshua Pedrick
0812adc983 Add UIGestureRecognizerDelegate and PanGestureRecogniser (#3597)
- Allow all gestures simultaneously recognized.
- Add PanGestureRecogniser with min/max number of touches.
- Fix sending delta values relative to Update instead to match macOS.
- Fix rotation gesture units from iOS to be in degrees instead of radians.

Co-authored-by: Mads Marquart <mads@marquart.dk>
2024-04-27 19:00:38 +04:00
Mads Marquart
cd6ec19300 Don't set the background color when initializing with transparency (#3657)
Setting the background color changes how the window title bar appears,
which is something that the application should customize itself if it
wants this behaviour (and also, it wasn't set when calling
`set_transparent`, so the behaviour wasn't consistent).
2024-04-27 19:00:38 +04:00
growfrow
61bd8172bd chore: fix some typos in comments (#3635)
Signed-off-by: growfrow <growfrow@outlook.com>
2024-04-27 19:00:38 +04:00
Kirill Chibisov
c04c113e7e chore: ensure that .cargo config is not published
Just in case, so the correct changelog will be rendered when pulling
the crate from the crates.io as archive and trying to build it.
2024-04-27 19:00:38 +04:00
Marijn Suijten
ce32a3024e android: bump to ndk 0.9.0 and android-activity 0.6.0 2024-04-27 19:00:38 +04:00
61 changed files with 1786 additions and 1310 deletions

View File

@@ -71,7 +71,7 @@ rwh_05 = ["dep:rwh_05", "ndk/rwh_05"]
rwh_06 = ["dep:rwh_06", "ndk/rwh_06"]
[build-dependencies]
cfg_aliases = "0.2.1"
cfg_aliases = "0.2.0"
[dependencies]
bitflags = "2"
@@ -108,33 +108,30 @@ ndk = { version = "0.9.0", default-features = false }
[target.'cfg(any(target_os = "ios", target_os = "macos"))'.dependencies]
core-foundation = "0.9.3"
objc2 = "0.5.2"
objc2 = "0.5.1"
[target.'cfg(target_os = "macos")'.dependencies]
core-graphics = "0.23.1"
[target.'cfg(target_os = "macos")'.dependencies.objc2-foundation]
version = "0.2.2"
version = "0.2.0"
features = [
"dispatch",
"NSArray",
"NSAttributedString",
"NSData",
"NSDictionary",
"NSDistributedNotificationCenter",
"NSEnumerator",
"NSNotification",
"NSObjCRuntime",
"NSString",
"NSPathUtilities",
"NSProcessInfo",
"NSRunLoop",
"NSString",
"NSThread",
"NSValue",
]
[target.'cfg(target_os = "macos")'.dependencies.objc2-app-kit]
version = "0.2.2"
version = "0.2.0"
features = [
"NSAppearance",
"NSApplication",
@@ -164,7 +161,7 @@ features = [
]
[target.'cfg(target_os = "ios")'.dependencies.objc2-foundation]
version = "0.2.2"
version = "0.2.0"
features = [
"dispatch",
"NSArray",
@@ -177,29 +174,6 @@ features = [
"NSSet",
]
[target.'cfg(target_os = "ios")'.dependencies.objc2-ui-kit]
version = "0.2.2"
features = [
"UIApplication",
"UIDevice",
"UIEvent",
"UIGeometry",
"UIGestureRecognizer",
"UIOrientation",
"UIPanGestureRecognizer",
"UIPinchGestureRecognizer",
"UIResponder",
"UIRotationGestureRecognizer",
"UIScreen",
"UIScreenMode",
"UITapGestureRecognizer",
"UITouch",
"UITraitCollection",
"UIView",
"UIViewController",
"UIWindow",
]
[target.'cfg(target_os = "windows")'.dependencies]
unicode-segmentation = "1.7.1"

View File

@@ -1,10 +1,10 @@
use cfg_aliases::cfg_aliases;
fn main() {
// The script doesn't depend on our code.
// The script doesn't depend on our code
println!("cargo:rerun-if-changed=build.rs");
// Setup cfg aliases.
// Setup cfg aliases
cfg_aliases! {
// Systems.
android_platform: { target_os = "android" },
@@ -21,7 +21,4 @@ fn main() {
wayland_platform: { all(feature = "wayland", free_unix, not(redox)) },
orbital_platform: { redox },
}
// Winit defined cfgs.
println!("cargo:rustc-check-cfg=cfg(unreleased_changelogs)");
}

View File

@@ -3,75 +3,15 @@
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, KeyEvent, WindowEvent};
use winit::event::{ElementState, Event, KeyEvent, WindowEvent};
use winit::event_loop::{ActiveEventLoop, EventLoop};
use winit::raw_window_handle::HasRawWindowHandle;
use winit::window::{Window, WindowId};
use winit::window::Window;
#[path = "util/fill.rs"]
mod fill;
#[derive(Default)]
struct Application {
parent_window_id: Option<WindowId>,
windows: HashMap<WindowId, Window>,
}
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();
println!("Parent window id: {:?})", window.id());
self.parent_window_id = Some(window.id());
self.windows.insert(window.id(), window);
}
fn window_event(
&mut self,
event_loop: &ActiveEventLoop,
window_id: winit::window::WindowId,
event: WindowEvent,
) {
match event {
WindowEvent::CloseRequested => {
self.windows.clear();
event_loop.exit();
},
WindowEvent::CursorEntered { device_id: _ } => {
// On x11, println when the cursor entered in a window even if the child window
// is created by some key inputs.
// the child windows are always placed at (0, 0) with size (200, 200) in the
// parent window, so we also can see this log when we move
// the cursor around (200, 200) in parent window.
println!("cursor entered in the window {window_id:?}");
},
WindowEvent::KeyboardInput {
event: KeyEvent { state: ElementState::Pressed, .. },
..
} => {
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:?}");
self.windows.insert(child_id, child_window);
},
WindowEvent::RedrawRequested => {
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()
@@ -85,9 +25,58 @@ fn main() -> Result<(), impl std::error::Error> {
event_loop.create_window(window_attributes).unwrap()
}
let mut windows = HashMap::new();
let event_loop: EventLoop<()> = EventLoop::new().unwrap();
let mut app = Application::default();
event_loop.run_app(&mut app)
let mut parent_window_id = None;
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();
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 {
WindowEvent::CloseRequested => {
windows.clear();
event_loop.exit();
},
WindowEvent::CursorEntered { device_id: _ } => {
// On x11, println when the cursor entered in a window even if the child window
// is created by some key inputs.
// the child windows are always placed at (0, 0) with size (200, 200) in the
// parent window, so we also can see this log when we move
// the cursor around (200, 200) in parent window.
println!("cursor entered in the window {window_id:?}");
},
WindowEvent::KeyboardInput {
event: KeyEvent { state: ElementState::Pressed, .. },
..
} => {
let parent_window = windows.get(&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);
},
WindowEvent::RedrawRequested => {
if let Some(window) = windows.get(&window_id) {
fill::fill_window(window);
}
},
_ => (),
},
_ => (),
}
})
}
#[cfg(all(feature = "rwh_06", not(any(x11_platform, macos_platform, windows_platform))))]

View File

@@ -17,9 +17,7 @@ use softbuffer::{Context, Surface};
use winit::application::ApplicationHandler;
use winit::dpi::{LogicalSize, PhysicalPosition, PhysicalSize};
use winit::event::{
DeviceEvent, DeviceId, Ime, MouseButton, MouseScrollDelta, StartCause, WindowEvent,
};
use winit::event::{DeviceEvent, DeviceId, Ime, MouseButton, MouseScrollDelta, WindowEvent};
use winit::event_loop::{ActiveEventLoop, EventLoop};
use winit::keyboard::{Key, ModifiersState};
use winit::window::{
@@ -305,12 +303,6 @@ 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:?}");
}
@@ -328,7 +320,6 @@ impl ApplicationHandler<UserEvent> for Application {
match event {
WindowEvent::Resized(size) => {
info!("Resized({size:?})");
window.resize(size);
},
WindowEvent::Focused(focused) => {

View File

@@ -190,7 +190,7 @@ pub trait ApplicationHandler<T: 'static = ()> {
/// Emitted when the event loop is being shut down.
///
/// This is irreversible - if this method is called, it is guaranteed that the event loop
/// will exit right after.
/// will exist right after.
fn exiting(&mut self, event_loop: &ActiveEventLoop) {
let _ = event_loop;
}
@@ -223,117 +223,3 @@ pub trait ApplicationHandler<T: 'static = ()> {
let _ = event_loop;
}
}
impl<A: ?Sized + ApplicationHandler<T>, T: 'static> ApplicationHandler<T> for &mut A {
#[inline]
fn new_events(&mut self, event_loop: &ActiveEventLoop, cause: StartCause) {
(**self).new_events(event_loop, cause);
}
#[inline]
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
(**self).resumed(event_loop);
}
#[inline]
fn user_event(&mut self, event_loop: &ActiveEventLoop, event: T) {
(**self).user_event(event_loop, event);
}
#[inline]
fn window_event(
&mut self,
event_loop: &ActiveEventLoop,
window_id: WindowId,
event: WindowEvent,
) {
(**self).window_event(event_loop, window_id, event);
}
#[inline]
fn device_event(
&mut self,
event_loop: &ActiveEventLoop,
device_id: DeviceId,
event: DeviceEvent,
) {
(**self).device_event(event_loop, device_id, event);
}
#[inline]
fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) {
(**self).about_to_wait(event_loop);
}
#[inline]
fn suspended(&mut self, event_loop: &ActiveEventLoop) {
(**self).suspended(event_loop);
}
#[inline]
fn exiting(&mut self, event_loop: &ActiveEventLoop) {
(**self).exiting(event_loop);
}
#[inline]
fn memory_warning(&mut self, event_loop: &ActiveEventLoop) {
(**self).memory_warning(event_loop);
}
}
impl<A: ?Sized + ApplicationHandler<T>, T: 'static> ApplicationHandler<T> for Box<A> {
#[inline]
fn new_events(&mut self, event_loop: &ActiveEventLoop, cause: StartCause) {
(**self).new_events(event_loop, cause);
}
#[inline]
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
(**self).resumed(event_loop);
}
#[inline]
fn user_event(&mut self, event_loop: &ActiveEventLoop, event: T) {
(**self).user_event(event_loop, event);
}
#[inline]
fn window_event(
&mut self,
event_loop: &ActiveEventLoop,
window_id: WindowId,
event: WindowEvent,
) {
(**self).window_event(event_loop, window_id, event);
}
#[inline]
fn device_event(
&mut self,
event_loop: &ActiveEventLoop,
device_id: DeviceId,
event: DeviceEvent,
) {
(**self).device_event(event_loop, device_id, event);
}
#[inline]
fn about_to_wait(&mut self, event_loop: &ActiveEventLoop) {
(**self).about_to_wait(event_loop);
}
#[inline]
fn suspended(&mut self, event_loop: &ActiveEventLoop) {
(**self).suspended(event_loop);
}
#[inline]
fn exiting(&mut self, event_loop: &ActiveEventLoop) {
(**self).exiting(event_loop);
}
#[inline]
fn memory_warning(&mut self, event_loop: &ActiveEventLoop) {
(**self).memory_warning(event_loop);
}
}

View File

@@ -12,7 +12,6 @@ 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`.
@@ -40,19 +39,3 @@ 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.

View File

@@ -65,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,ignore
```rust,no_run
use winit::event::Event;
use winit::event_loop::EventLoop;
use winit::window::Window;

View File

@@ -20,6 +20,7 @@ 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};
@@ -102,14 +103,11 @@ impl<T> EventLoopBuilder<T> {
///
/// [`platform`]: crate::platform
#[cfg_attr(
android_platform,
android,
doc = "[`.with_android_app(app)`]: \
crate::platform::android::EventLoopBuilderExtAndroid::with_android_app"
)]
#[cfg_attr(
not(android_platform),
doc = "[`.with_android_app(app)`]: #only-available-on-android"
)]
#[cfg_attr(not(android), doc = "[`.with_android_app(app)`]: #only-available-on-android")]
#[inline]
pub fn build(&mut self) -> Result<EventLoop<T>, EventLoopError> {
let _span = tracing::debug_span!("winit::EventLoopBuilder::build").entered();
@@ -151,7 +149,6 @@ 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,110 +206,6 @@ 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.
@@ -320,6 +213,21 @@ 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.
@@ -351,9 +259,7 @@ 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> {
#[cfg(debug_assertions)]
let app = &mut ensure_event_order(app);
self.event_loop.run_app(app)
self.event_loop.run(|event, event_loop| dispatch_event_for_app(app, event_loop, event))
}
/// Creates an [`EventLoopProxy`] that can be used to dispatch user events
@@ -540,7 +446,7 @@ impl ActiveEventLoop {
/// This exits the event loop.
///
/// See [`LoopExiting`][crate::event::Event::LoopExiting].
/// See [`LoopExiting`][Event::LoopExiting].
pub fn exit(&self) {
let _span = tracing::debug_span!("winit::ActiveEventLoop::exit",).entered();
@@ -709,3 +615,23 @@ 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),
}
}

View File

@@ -178,10 +178,6 @@
#![cfg_attr(docsrs, feature(doc_auto_cfg, doc_cfg_hide), doc(cfg_hide(doc, docsrs)))]
#![allow(clippy::missing_safety_doc)]
#[cfg(feature = "rwh_04")]
pub use rwh_04 as raw_window_handle_04;
#[cfg(feature = "rwh_05")]
pub use rwh_05 as raw_window_handle_05;
#[cfg(feature = "rwh_06")]
pub use rwh_06 as raw_window_handle;

View File

@@ -357,7 +357,7 @@ impl MonitorHandleExtIOS for MonitorHandle {
fn ui_screen(&self) -> *mut c_void {
// SAFETY: The marker is only used to get the pointer of the screen
let mtm = unsafe { objc2_foundation::MainThreadMarker::new_unchecked() };
objc2::rc::Retained::as_ptr(self.inner.ui_screen(mtm)) as *mut c_void
objc2::rc::Id::as_ptr(self.inner.ui_screen(mtm)) as *mut c_void
}
#[inline]

View File

@@ -375,7 +375,7 @@ impl MonitorHandleExtMacOS for MonitorHandle {
fn ns_screen(&self) -> Option<*mut c_void> {
// SAFETY: We only use the marker to get a pointer
let mtm = unsafe { objc2_foundation::MainThreadMarker::new_unchecked() };
self.inner.ns_screen(mtm).map(|s| objc2::rc::Retained::as_ptr(&s) as _)
self.inner.ns_screen(mtm).map(|s| objc2::rc::Id::as_ptr(&s) as _)
}
}

View File

@@ -1,13 +1,12 @@
use std::time::Duration;
use crate::application::ApplicationHandler;
use crate::event_loop::EventLoop;
use crate::event::Event;
use crate::event_loop::{self, ActiveEventLoop, 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.
@@ -108,18 +107,30 @@ pub trait EventLoopExtPumpEvents {
&mut self,
timeout: Option<Duration>,
app: &mut A,
) -> PumpStatus;
) -> 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);
}
impl<T> EventLoopExtPumpEvents for EventLoop<T> {
type UserEvent = T;
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)
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)
}
}

View File

@@ -1,6 +1,7 @@
use crate::application::ApplicationHandler;
use crate::error::EventLoopError;
use crate::event_loop::{ActiveEventLoop, EventLoop};
use crate::event::Event;
use crate::event_loop::{self, ActiveEventLoop, EventLoop};
#[cfg(doc)]
use crate::{platform::pump_events::EventLoopExtPumpEvents, window::Window};
@@ -8,10 +9,16 @@ 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`].
///
/// [`Event::UserEvent`]: crate::event::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);
/// Run the application with the event loop on the calling thread.
///
/// Unlike [`EventLoop::run_app`], this function accepts non-`'static` (i.e. non-`move`)
@@ -61,18 +68,23 @@ pub trait EventLoopExtRunOnDemand {
fn run_app_on_demand<A: ApplicationHandler<Self::UserEvent>>(
&mut self,
app: &mut A,
) -> Result<(), EventLoopError>;
) -> Result<(), EventLoopError> {
#[allow(deprecated)]
self.run_on_demand(|event, event_loop| {
event_loop::dispatch_event_for_app(app, event_loop, event)
})
}
}
impl<T> EventLoopExtRunOnDemand for EventLoop<T> {
type UserEvent = T;
fn run_app_on_demand<A: ApplicationHandler<Self::UserEvent>>(
&mut self,
app: &mut A,
) -> Result<(), EventLoopError> {
fn run_on_demand<F>(&mut self, event_handler: F) -> Result<(), EventLoopError>
where
F: FnMut(Event<Self::UserEvent>, &ActiveEventLoop),
{
self.event_loop.window_target().clear_exit();
self.event_loop.run_app_on_demand(app)
self.event_loop.run_on_demand(event_handler)
}
}

View File

@@ -13,7 +13,7 @@
//! * `wayland-csd-adwaita` (default).
//! * `wayland-csd-adwaita-crossfont`.
//! * `wayland-csd-adwaita-notitle`.
use crate::event_loop::{ActiveEventLoop, EventLoop, EventLoopBuilder};
use crate::event_loop::{ActiveEventLoop, EventLoopBuilder};
use crate::monitor::MonitorHandle;
use crate::window::{Window, WindowAttributes};
@@ -32,19 +32,6 @@ impl ActiveEventLoopExtWayland for ActiveEventLoop {
}
}
/// Additional methods on [`EventLoop`] that are specific to Wayland.
pub trait EventLoopExtWayland {
/// True if the [`EventLoop`] uses Wayland.
fn is_wayland(&self) -> bool;
}
impl<T: 'static> EventLoopExtWayland for EventLoop<T> {
#[inline]
fn is_wayland(&self) -> bool {
self.event_loop.is_wayland()
}
}
/// Additional methods on [`EventLoopBuilder`] that are specific to Wayland.
pub trait EventLoopBuilderExtWayland {
/// Force using Wayland.

View File

@@ -55,7 +55,8 @@ use web_sys::HtmlCanvasElement;
use crate::application::ApplicationHandler;
use crate::cursor::CustomCursorSource;
use crate::event_loop::{ActiveEventLoop, EventLoop};
use crate::event::Event;
use crate::event_loop::{self, ActiveEventLoop, EventLoop};
#[cfg(web_platform)]
use crate::platform_impl::CustomCursorFuture as PlatformCustomCursorFuture;
use crate::platform_impl::PlatformCustomCursorSource;
@@ -155,9 +156,7 @@ 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`].
///
/// [`Event::UserEvent`]: crate::event::Event::UserEvent
/// A type provided by the user that can be passed through `Event::UserEvent`.
type UserEvent: 'static;
/// Initializes the winit event loop.
@@ -183,13 +182,30 @@ 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, app: A) {
self.event_loop.spawn_app(app);
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)
}
}

View File

@@ -476,10 +476,10 @@ pub trait WindowAttributesExtWindows {
/// the menus look. If you use this, it is recommended that you combine it with
/// `with_theme(Some(Theme::Light))` to avoid a jarring effect.
#[cfg_attr(
windows_platform,
platform_windows,
doc = "[`CreateMenu`]: windows_sys::Win32::UI::WindowsAndMessaging::CreateMenu"
)]
#[cfg_attr(not(windows_platform), doc = "[`CreateMenu`]: #only-available-on-windows")]
#[cfg_attr(not(platform_windows), doc = "[`CreateMenu`]: #only-available-on-windows")]
fn with_menu(self, menu: HMENU) -> Self;
/// This sets `ICON_BIG`. A good ceiling here is 256x256.

View File

@@ -2,7 +2,7 @@
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use crate::event_loop::{ActiveEventLoop, EventLoop, EventLoopBuilder};
use crate::event_loop::{ActiveEventLoop, EventLoopBuilder};
use crate::monitor::MonitorHandle;
use crate::window::{Window, WindowAttributes};
@@ -99,19 +99,6 @@ impl ActiveEventLoopExtX11 for ActiveEventLoop {
}
}
/// Additional methods on [`EventLoop`] that are specific to X11.
pub trait EventLoopExtX11 {
/// True if the [`EventLoop`] uses X11.
fn is_x11(&self) -> bool;
}
impl<T: 'static> EventLoopExtX11 for EventLoop<T> {
#[inline]
fn is_x11(&self) -> bool {
!self.event_loop.is_wayland()
}
}
/// Additional methods on [`EventLoopBuilder`] that are specific to X11.
pub trait EventLoopBuilderExtX11 {
/// Force using X11.

View File

@@ -14,13 +14,12 @@ 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, ControlFlow, DeviceEvents};
use crate::event_loop::{self, ActiveEventLoop as RootAEL, ControlFlow, DeviceEvents};
use crate::platform::pump_events::PumpStatus;
use crate::platform_impl::Fullscreen;
use crate::window::{
@@ -198,28 +197,27 @@ impl<T: 'static> EventLoop<T> {
})
}
fn single_iteration<A: ApplicationHandler<T>>(
&mut self,
main_event: Option<MainEvent<'_>>,
app: &mut A,
) {
fn single_iteration<F>(&mut self, main_event: Option<MainEvent<'_>>, callback: &mut F)
where
F: FnMut(event::Event<T>, &RootAEL),
{
trace!("Mainloop iteration");
let cause = self.cause;
let mut pending_redraw = self.pending_redraw;
let mut resized = false;
app.new_events(self.window_target(), cause);
callback(event::Event::NewEvents(cause), self.window_target());
if let Some(event) = main_event {
trace!("Handling main event {:?}", event);
match event {
MainEvent::InitWindow { .. } => {
app.resumed(self.window_target());
callback(event::Event::Resumed, self.window_target());
},
MainEvent::TerminateWindow { .. } => {
app.suspended(self.window_target());
callback(event::Event::Suspended, self.window_target());
},
MainEvent::WindowResized { .. } => resized = true,
MainEvent::RedrawNeeded { .. } => pending_redraw = true,
@@ -228,15 +226,23 @@ impl<T: 'static> EventLoop<T> {
},
MainEvent::GainedFocus => {
HAS_FOCUS.store(true, Ordering::Relaxed);
let window_id = window::WindowId(WindowId);
let event = event::WindowEvent::Focused(true);
app.window_event(self.window_target(), window_id, event);
callback(
event::Event::WindowEvent {
window_id: window::WindowId(WindowId),
event: event::WindowEvent::Focused(true),
},
self.window_target(),
);
},
MainEvent::LostFocus => {
HAS_FOCUS.store(false, Ordering::Relaxed);
let window_id = window::WindowId(WindowId);
let event = event::WindowEvent::Focused(false);
app.window_event(self.window_target(), window_id, event);
callback(
event::Event::WindowEvent {
window_id: window::WindowId(WindowId),
event: event::WindowEvent::Focused(false),
},
self.window_target(),
);
},
MainEvent::ConfigChanged { .. } => {
let monitor = MonitorHandle::new(self.android_app.clone());
@@ -246,19 +252,20 @@ impl<T: 'static> EventLoop<T> {
let new_inner_size = Arc::new(Mutex::new(
MonitorHandle::new(self.android_app.clone()).size(),
));
let window_id = window::WindowId(WindowId);
let event = event::WindowEvent::ScaleFactorChanged {
inner_size_writer: InnerSizeWriter::new(Arc::downgrade(
&new_inner_size,
)),
scale_factor,
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,
},
};
app.window_event(self.window_target(), window_id, event);
callback(event, self.window_target());
}
},
MainEvent::LowMemory => {
app.memory_warning(self.window_target());
callback(event::Event::MemoryWarning, self.window_target());
},
MainEvent::Start => {
// XXX: how to forward this state to applications?
@@ -306,7 +313,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, app));
input_iter.next(|event| self.handle_input_event(&android_app, event, callback));
if !read_event {
break;
@@ -320,7 +327,7 @@ impl<T: 'static> EventLoop<T> {
// Empty the user event buffer
{
while let Ok(event) = self.user_events_receiver.try_recv() {
app.user_event(self.window_target(), event);
callback(crate::event::Event::UserEvent(event), self.window_target());
}
}
@@ -333,32 +340,39 @@ impl<T: 'static> EventLoop<T> {
} else {
PhysicalSize::new(0, 0)
};
let window_id = window::WindowId(WindowId);
let event = event::WindowEvent::Resized(size);
app.window_event(self.window_target(), window_id, event);
let event = event::Event::WindowEvent {
window_id: window::WindowId(WindowId),
event: event::WindowEvent::Resized(size),
};
callback(event, self.window_target());
}
pending_redraw |= self.redraw_flag.get_and_reset();
if pending_redraw {
pending_redraw = false;
let window_id = window::WindowId(WindowId);
let event = event::WindowEvent::RedrawRequested;
app.window_event(self.window_target(), window_id, event);
let event = event::Event::WindowEvent {
window_id: window::WindowId(WindowId),
event: event::WindowEvent::RedrawRequested,
};
callback(event, self.window_target());
}
}
// This is always the last event we dispatch before poll again
app.about_to_wait(self.window_target());
callback(event::Event::AboutToWait, self.window_target());
self.pending_redraw = pending_redraw;
}
fn handle_input_event<A: ApplicationHandler<T>>(
fn handle_input_event<F>(
&mut self,
android_app: &AndroidApp,
event: &InputEvent<'_>,
app: &mut A,
) -> InputStatus {
callback: &mut F,
) -> InputStatus
where
F: FnMut(event::Event<T>, &RootAEL),
{
let mut input_status = InputStatus::Handled;
match event {
InputEvent::MotionEvent(motion_event) => {
@@ -396,16 +410,17 @@ impl<T: 'static> EventLoop<T> {
"Input event {device_id:?}, {phase:?}, loc={location:?}, \
pointer={pointer:?}"
);
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);
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());
}
}
},
@@ -433,22 +448,23 @@ impl<T: 'static> EventLoop<T> {
&mut self.combining_accent,
);
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 {},
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,
},
is_synthetic: false,
};
app.window_event(self.window_target(), window_id, event);
callback(event, self.window_target());
},
}
},
@@ -460,16 +476,19 @@ impl<T: 'static> EventLoop<T> {
input_status
}
pub fn run_app<A: ApplicationHandler<T>>(mut self, app: &mut A) -> Result<(), EventLoopError> {
self.run_app_on_demand(app)
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_on_demand<A: ApplicationHandler<T>>(
&mut self,
app: &mut A,
) -> Result<(), EventLoopError> {
pub fn run_on_demand<F>(&mut self, mut event_handler: F) -> Result<(), EventLoopError>
where
F: FnMut(event::Event<T>, &event_loop::ActiveEventLoop),
{
loop {
match self.pump_app_events(None, app) {
match self.pump_events(None, &mut event_handler) {
PumpStatus::Exit(0) => {
break Ok(());
},
@@ -483,11 +502,10 @@ impl<T: 'static> EventLoop<T> {
}
}
pub fn pump_app_events<A: ApplicationHandler<T>>(
&mut self,
timeout: Option<Duration>,
app: &mut A,
) -> PumpStatus {
pub fn pump_events<F>(&mut self, timeout: Option<Duration>, mut callback: F) -> PumpStatus
where
F: FnMut(event::Event<T>, &RootAEL),
{
if !self.loop_running {
self.loop_running = true;
@@ -498,18 +516,18 @@ impl<T: 'static> EventLoop<T> {
self.cause = StartCause::Init;
// run the initial loop iteration
self.single_iteration(None, app);
self.single_iteration(None, &mut callback);
}
// Consider the possibility that the `StartCause::Init` iteration could
// request to Exit
if !self.exiting() {
self.poll_events_with_timeout(timeout, app);
self.poll_events_with_timeout(timeout, &mut callback);
}
if self.exiting() {
self.loop_running = false;
app.exiting(self.window_target());
callback(event::Event::LoopExiting, self.window_target());
PumpStatus::Exit(0)
} else {
@@ -517,11 +535,10 @@ impl<T: 'static> EventLoop<T> {
}
}
fn poll_events_with_timeout<A: ApplicationHandler<T>>(
&mut self,
mut timeout: Option<Duration>,
app: &mut A,
) {
fn poll_events_with_timeout<F>(&mut self, mut timeout: Option<Duration>, mut callback: F)
where
F: FnMut(event::Event<T>, &RootAEL),
{
let start = Instant::now();
self.pending_redraw |= self.redraw_flag.get_and_reset();
@@ -542,8 +559,8 @@ impl<T: 'static> EventLoop<T> {
min_timeout(control_flow_timeout, timeout)
};
let android_app = self.android_app.clone(); // Don't borrow self as part of poll expression
android_app.poll_events(timeout, |poll_event| {
let app = self.android_app.clone(); // Don't borrow self as part of poll expression
app.poll_events(timeout, |poll_event| {
let mut main_event = None;
match poll_event {
@@ -584,7 +601,7 @@ impl<T: 'static> EventLoop<T> {
},
};
self.single_iteration(main_event, app);
self.single_iteration(main_event, &mut callback);
});
}

View File

@@ -1,8 +1,8 @@
use objc2::{declare_class, mutability, ClassType, DeclaredClass};
use objc2_foundation::{MainThreadMarker, NSObject, NSObjectProtocol};
use objc2_ui_kit::{UIApplication, UIWindow};
use super::app_state::{self, EventWrapper};
use super::uikit::{UIApplication, UIWindow};
use super::window::WinitUIWindow;
use crate::event::{Event, WindowEvent};
use crate::window::WindowId as RootWindowId;
@@ -51,7 +51,6 @@ declare_class!(
#[method(applicationWillTerminate:)]
fn will_terminate(&self, application: &UIApplication) {
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
@@ -82,7 +81,6 @@ 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

View File

@@ -13,15 +13,14 @@ use core_foundation::runloop::{
kCFRunLoopCommonModes, CFRunLoopAddTimer, CFRunLoopGetMain, CFRunLoopRef, CFRunLoopTimerCreate,
CFRunLoopTimerInvalidate, CFRunLoopTimerRef, CFRunLoopTimerSetNextFireDate,
};
use objc2::rc::Retained;
use objc2::rc::Id;
use objc2::runtime::AnyObject;
use objc2::{msg_send, sel};
use objc2_foundation::{
CGRect, CGSize, MainThreadMarker, NSInteger, NSObjectProtocol, NSOperatingSystemVersion,
NSProcessInfo,
CGRect, CGSize, MainThreadMarker, NSInteger, NSOperatingSystemVersion, NSProcessInfo,
};
use objc2_ui_kit::{UICoordinateSpace, UIView};
use super::uikit::UIView;
use super::window::WinitUIWindow;
use crate::dpi::PhysicalSize;
use crate::event::{Event, InnerSizeWriter, StartCause, WindowEvent};
@@ -72,7 +71,7 @@ pub(crate) enum EventWrapper {
#[derive(Debug)]
pub struct ScaleFactorChanged {
pub(super) window: Retained<WinitUIWindow>,
pub(super) window: Id<WinitUIWindow>,
pub(super) suggested_size: PhysicalSize<u32>,
pub(super) scale_factor: f64,
}
@@ -99,25 +98,25 @@ impl Event<HandlePendingUserEvents> {
#[must_use = "dropping `AppStateImpl` without inspecting it is probably a bug"]
enum AppStateImpl {
NotLaunched {
queued_windows: Vec<Retained<WinitUIWindow>>,
queued_windows: Vec<Id<WinitUIWindow>>,
queued_events: Vec<EventWrapper>,
queued_gpu_redraws: HashSet<Retained<WinitUIWindow>>,
queued_gpu_redraws: HashSet<Id<WinitUIWindow>>,
},
Launching {
queued_windows: Vec<Retained<WinitUIWindow>>,
queued_windows: Vec<Id<WinitUIWindow>>,
queued_events: Vec<EventWrapper>,
queued_handler: EventLoopHandler,
queued_gpu_redraws: HashSet<Retained<WinitUIWindow>>,
queued_gpu_redraws: HashSet<Id<WinitUIWindow>>,
},
ProcessingEvents {
handler: EventLoopHandler,
queued_gpu_redraws: HashSet<Retained<WinitUIWindow>>,
queued_gpu_redraws: HashSet<Id<WinitUIWindow>>,
active_control_flow: ControlFlow,
},
// special state to deal with reentrancy and prevent mutable aliasing.
InUserCallback {
queued_events: Vec<EventWrapper>,
queued_gpu_redraws: HashSet<Retained<WinitUIWindow>>,
queued_gpu_redraws: HashSet<Id<WinitUIWindow>>,
},
ProcessingRedraws {
handler: EventLoopHandler,
@@ -228,9 +227,7 @@ impl AppState {
});
}
fn did_finish_launching_transition(
&mut self,
) -> (Vec<Retained<WinitUIWindow>>, Vec<EventWrapper>) {
fn did_finish_launching_transition(&mut self) -> (Vec<Id<WinitUIWindow>>, Vec<EventWrapper>) {
let (windows, events, handler, queued_gpu_redraws) = match self.take_state() {
AppStateImpl::Launching {
queued_windows,
@@ -346,7 +343,7 @@ impl AppState {
UserCallbackTransitionResult::Success { handler, active_control_flow, processing_redraws }
}
fn main_events_cleared_transition(&mut self) -> HashSet<Retained<WinitUIWindow>> {
fn main_events_cleared_transition(&mut self) -> HashSet<Id<WinitUIWindow>> {
let (handler, queued_gpu_redraws, active_control_flow) = match self.take_state() {
AppStateImpl::ProcessingEvents { handler, queued_gpu_redraws, active_control_flow } => {
(handler, queued_gpu_redraws, active_control_flow)
@@ -414,7 +411,7 @@ impl AppState {
}
}
pub(crate) fn set_key_window(mtm: MainThreadMarker, window: &Retained<WinitUIWindow>) {
pub(crate) fn set_key_window(mtm: MainThreadMarker, window: &Id<WinitUIWindow>) {
let mut this = AppState::get_mut(mtm);
match this.state_mut() {
&mut AppStateImpl::NotLaunched { ref mut queued_windows, .. } => {
@@ -434,7 +431,7 @@ pub(crate) fn set_key_window(mtm: MainThreadMarker, window: &Retained<WinitUIWin
window.makeKeyAndVisible();
}
pub(crate) fn queue_gl_or_metal_redraw(mtm: MainThreadMarker, window: Retained<WinitUIWindow>) {
pub(crate) fn queue_gl_or_metal_redraw(mtm: MainThreadMarker, window: Id<WinitUIWindow>) {
let mut this = AppState::get_mut(mtm);
match this.state_mut() {
&mut AppStateImpl::NotLaunched { ref mut queued_gpu_redraws, .. }
@@ -724,7 +721,7 @@ fn handle_hidpi_proxy(handler: &mut EventLoopHandler, event: ScaleFactorChanged)
view.setFrame(new_frame);
}
fn get_view_and_screen_frame(window: &WinitUIWindow) -> (Retained<UIView>, CGRect) {
fn get_view_and_screen_frame(window: &WinitUIWindow) -> (Id<UIView>, CGRect) {
let view_controller = window.rootViewController().unwrap();
let view = view_controller.view().unwrap();
let bounds = window.bounds();
@@ -860,17 +857,23 @@ fn meets_requirements(
}
fn get_version() -> NSOperatingSystemVersion {
let process_info = NSProcessInfo::processInfo();
let atleast_ios_8 = process_info.respondsToSelector(sel!(operatingSystemVersion));
// Winit requires atleast iOS 8 because no one has put the time into supporting earlier os
// versions. Older iOS versions are increasingly difficult to test. For example, Xcode 11 does
// not support debugging on devices with an iOS version of less than 8. Another example, in
// order to use an iOS simulator older than iOS 8, you must download an older version of Xcode
// (<9), and at least Xcode 7 has been tested to not even run on macOS 10.15 - Xcode 8 might?
//
// The minimum required iOS version is likely to grow in the future.
assert!(atleast_ios_8, "`winit` requires iOS version 8 or greater");
process_info.operatingSystemVersion()
unsafe {
let process_info = NSProcessInfo::processInfo();
let atleast_ios_8: bool = msg_send![
&process_info,
respondsToSelector: sel!(operatingSystemVersion)
];
// winit requires atleast iOS 8 because no one has put the time into supporting earlier os
// versions. Older iOS versions are increasingly difficult to test. For example,
// Xcode 11 does not support debugging on devices with an iOS version of less than
// 8. Another example, in order to use an iOS simulator older than iOS 8, you must
// download an older version of Xcode (<9), and at least Xcode 7 has been tested to
// not even run on macOS 10.15 - Xcode 8 might?
//
// The minimum required iOS version is likely to grow in the future.
assert!(atleast_ios_8, "`winit` requires iOS version 8 or greater");
process_info.operatingSystemVersion()
}
}
pub fn os_capabilities() -> OSCapabilities {

View File

@@ -1,7 +1,7 @@
use std::collections::VecDeque;
use std::ffi::{c_char, c_int, c_void};
use std::ffi::c_void;
use std::marker::PhantomData;
use std::ptr::{self, NonNull};
use std::ptr;
use std::sync::mpsc::{self, Receiver, Sender};
use core_foundation::base::{CFIndex, CFRelease};
@@ -11,12 +11,9 @@ use core_foundation::runloop::{
CFRunLoopObserverCreate, CFRunLoopObserverRef, CFRunLoopSourceContext, CFRunLoopSourceCreate,
CFRunLoopSourceInvalidate, CFRunLoopSourceRef, CFRunLoopSourceSignal, CFRunLoopWakeUp,
};
use objc2::rc::Retained;
use objc2::{msg_send_id, ClassType};
use objc2::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::{
@@ -28,6 +25,7 @@ use crate::window::{CustomCursor, CustomCursorSource};
use super::app_delegate::AppDelegate;
use super::app_state::AppState;
use super::uikit::{UIApplication, UIApplicationMain, UIDevice, UIScreen, UIUserInterfaceIdiom};
use super::{app_state, monitor, MonitorHandle};
#[derive(Debug)]
@@ -46,8 +44,7 @@ impl ActiveEventLoop {
}
pub fn primary_monitor(&self) -> Option<MonitorHandle> {
#[allow(deprecated)]
Some(MonitorHandle::new(UIScreen::mainScreen(self.mtm)))
Some(MonitorHandle::new(UIScreen::main(self.mtm)))
}
#[inline]
@@ -109,28 +106,17 @@ impl OwnedDisplayHandle {
}
}
fn map_user_event<T: 'static, A: ApplicationHandler<T>>(
app: &mut A,
fn map_user_event<T: 'static>(
mut handler: impl FnMut(Event<T>, &RootActiveEventLoop),
receiver: mpsc::Receiver<T>,
) -> 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(_) => {
) -> impl FnMut(Event<HandlePendingUserEvents>, &RootActiveEventLoop) {
move |event, window_target| match event.map_nonuser_event() {
Ok(event) => (handler)(event, window_target),
Err(_) => {
for event in receiver.try_iter() {
app.user_event(window_target, event);
(handler)(Event::UserEvent(event), window_target);
}
},
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),
}
}
@@ -173,9 +159,11 @@ impl<T: 'static> EventLoop<T> {
})
}
pub fn run_app<A: ApplicationHandler<T>>(self, app: &mut A) -> ! {
let application: Option<Retained<UIApplication>> =
unsafe { msg_send_id![UIApplication::class(), sharedApplication] };
pub fn run<F>(self, handler: F) -> !
where
F: FnMut(Event<T>, &RootActiveEventLoop),
{
let application = UIApplication::shared(self.mtm);
assert!(
application.is_none(),
"\
@@ -183,7 +171,7 @@ impl<T: 'static> EventLoop<T> {
`EventLoop::run_app` calls `UIApplicationMain` on iOS",
);
let handler = map_user_event(app, self.receiver);
let handler = map_user_event(handler, self.receiver);
let handler = unsafe {
std::mem::transmute::<
@@ -199,19 +187,8 @@ impl<T: 'static> EventLoop<T> {
// Ensure application delegate is initialized
let _ = AppDelegate::class();
extern "C" {
// These functions are in crt_externs.h.
fn _NSGetArgc() -> *mut c_int;
fn _NSGetArgv() -> *mut *mut *mut c_char;
}
unsafe {
UIApplicationMain(
*_NSGetArgc(),
NonNull::new(*_NSGetArgv()).unwrap(),
None,
Some(&NSString::from_str(AppDelegate::NAME)),
)
UIApplicationMain(0, ptr::null(), None, Some(&NSString::from_str(AppDelegate::NAME)))
};
unreachable!()
}
@@ -228,7 +205,7 @@ impl<T: 'static> EventLoop<T> {
// EventLoopExtIOS
impl<T: 'static> EventLoop<T> {
pub fn idiom(&self) -> Idiom {
match UIDevice::currentDevice(self.mtm).userInterfaceIdiom() {
match UIDevice::current(self.mtm).userInterfaceIdiom() {
UIUserInterfaceIdiom::Unspecified => Idiom::Unspecified,
UIUserInterfaceIdiom::Phone => Idiom::Phone,
UIUserInterfaceIdiom::Pad => Idiom::Pad,

View File

@@ -5,6 +5,7 @@ mod app_delegate;
mod app_state;
mod event_loop;
mod monitor;
mod uikit;
mod view;
mod view_controller;
mod window;

View File

@@ -4,22 +4,22 @@ use std::collections::{BTreeSet, VecDeque};
use std::{fmt, hash, ptr};
use objc2::mutability::IsRetainable;
use objc2::rc::Retained;
use objc2::rc::Id;
use objc2::Message;
use objc2_foundation::{run_on_main, MainThreadBound, MainThreadMarker, NSInteger};
use objc2_ui_kit::{UIScreen, UIScreenMode};
use super::uikit::{UIScreen, UIScreenMode};
use crate::dpi::{PhysicalPosition, PhysicalSize};
use crate::monitor::VideoModeHandle as RootVideoModeHandle;
use crate::platform_impl::platform::app_state;
// Workaround for `MainThreadBound` implementing almost no traits
#[derive(Debug)]
struct MainThreadBoundDelegateImpls<T>(MainThreadBound<Retained<T>>);
struct MainThreadBoundDelegateImpls<T>(MainThreadBound<Id<T>>);
impl<T: IsRetainable + Message> Clone for MainThreadBoundDelegateImpls<T> {
fn clone(&self) -> Self {
Self(run_on_main(|mtm| MainThreadBound::new(Retained::clone(self.0.get(mtm)), mtm)))
Self(run_on_main(|mtm| MainThreadBound::new(Id::clone(self.0.get(mtm)), mtm)))
}
}
@@ -27,7 +27,7 @@ impl<T: IsRetainable + Message> hash::Hash for MainThreadBoundDelegateImpls<T> {
fn hash<H: hash::Hasher>(&self, state: &mut H) {
// SAFETY: Marker only used to get the pointer
let mtm = unsafe { MainThreadMarker::new_unchecked() };
Retained::as_ptr(self.0.get(mtm)).hash(state);
Id::as_ptr(self.0.get(mtm)).hash(state);
}
}
@@ -35,7 +35,7 @@ impl<T: IsRetainable + Message> PartialEq for MainThreadBoundDelegateImpls<T> {
fn eq(&self, other: &Self) -> bool {
// SAFETY: Marker only used to get the pointer
let mtm = unsafe { MainThreadMarker::new_unchecked() };
Retained::as_ptr(self.0.get(mtm)) == Retained::as_ptr(other.0.get(mtm))
Id::as_ptr(self.0.get(mtm)) == Id::as_ptr(other.0.get(mtm))
}
}
@@ -52,8 +52,8 @@ pub struct VideoModeHandle {
impl VideoModeHandle {
fn new(
uiscreen: Retained<UIScreen>,
screen_mode: Retained<UIScreenMode>,
uiscreen: Id<UIScreen>,
screen_mode: Id<UIScreenMode>,
mtm: MainThreadMarker,
) -> VideoModeHandle {
let refresh_rate_millihertz = refresh_rate_millihertz(&uiscreen);
@@ -83,13 +83,13 @@ impl VideoModeHandle {
self.monitor.clone()
}
pub(super) fn screen_mode(&self, mtm: MainThreadMarker) -> &Retained<UIScreenMode> {
pub(super) fn screen_mode(&self, mtm: MainThreadMarker) -> &Id<UIScreenMode> {
self.screen_mode.0.get(mtm)
}
}
pub struct MonitorHandle {
ui_screen: MainThreadBound<Retained<UIScreen>>,
ui_screen: MainThreadBound<Id<UIScreen>>,
}
impl Clone for MonitorHandle {
@@ -140,22 +140,20 @@ impl fmt::Debug for MonitorHandle {
}
impl MonitorHandle {
pub(crate) fn new(ui_screen: Retained<UIScreen>) -> Self {
// Holding `Retained<UIScreen>` implies we're on the main thread.
pub(crate) fn new(ui_screen: Id<UIScreen>) -> Self {
// Holding `Id<UIScreen>` implies we're on the main thread.
let mtm = MainThreadMarker::new().unwrap();
Self { ui_screen: MainThreadBound::new(ui_screen, mtm) }
}
pub fn name(&self) -> Option<String> {
run_on_main(|mtm| {
#[allow(deprecated)]
let main = UIScreen::mainScreen(mtm);
let main = UIScreen::main(mtm);
if *self.ui_screen(mtm) == main {
Some("Primary".to_string())
} else if Some(self.ui_screen(mtm)) == main.mirroredScreen().as_ref() {
} else if *self.ui_screen(mtm) == main.mirroredScreen() {
Some("Mirrored".to_string())
} else {
#[allow(deprecated)]
UIScreen::screens(mtm)
.iter()
.position(|rhs| rhs == &**self.ui_screen(mtm))
@@ -199,7 +197,7 @@ impl MonitorHandle {
})
}
pub(crate) fn ui_screen(&self, mtm: MainThreadMarker) -> &Retained<UIScreen> {
pub(crate) fn ui_screen(&self, mtm: MainThreadMarker) -> &Id<UIScreen> {
self.ui_screen.get(mtm)
}
@@ -239,6 +237,5 @@ fn refresh_rate_millihertz(uiscreen: &UIScreen) -> u32 {
}
pub fn uiscreens(mtm: MainThreadMarker) -> VecDeque<MonitorHandle> {
#[allow(deprecated)]
UIScreen::screens(mtm).into_iter().map(MonitorHandle::new).collect()
}

View File

@@ -0,0 +1,31 @@
use objc2::rc::Id;
use objc2::{extern_class, extern_methods, msg_send_id, mutability, ClassType};
use objc2_foundation::{CGRect, MainThreadMarker, NSArray, NSObject};
use super::{UIResponder, UIWindow};
extern_class!(
#[derive(Debug, PartialEq, Eq, Hash)]
pub(crate) struct UIApplication;
unsafe impl ClassType for UIApplication {
#[inherits(NSObject)]
type Super = UIResponder;
type Mutability = mutability::InteriorMutable;
}
);
extern_methods!(
unsafe impl UIApplication {
pub fn shared(_mtm: MainThreadMarker) -> Option<Id<Self>> {
unsafe { msg_send_id![Self::class(), sharedApplication] }
}
pub fn windows(&self) -> Id<NSArray<UIWindow>> {
unsafe { msg_send_id![self, windows] }
}
#[method(statusBarFrame)]
pub fn statusBarFrame(&self) -> CGRect;
}
);

View File

@@ -0,0 +1,12 @@
use objc2::{extern_class, mutability, ClassType};
use objc2_foundation::NSObject;
extern_class!(
#[derive(Debug, PartialEq, Eq, Hash)]
pub(crate) struct UICoordinateSpace;
unsafe impl ClassType for UICoordinateSpace {
type Super = NSObject;
type Mutability = mutability::InteriorMutable;
}
);

View File

@@ -0,0 +1,41 @@
use objc2::encode::{Encode, Encoding};
use objc2::rc::Id;
use objc2::{extern_class, extern_methods, msg_send_id, mutability, ClassType};
use objc2_foundation::{MainThreadMarker, NSInteger, NSObject};
extern_class!(
#[derive(Debug, PartialEq, Eq, Hash)]
pub(crate) struct UIDevice;
unsafe impl ClassType for UIDevice {
type Super = NSObject;
type Mutability = mutability::InteriorMutable;
}
);
extern_methods!(
unsafe impl UIDevice {
pub fn current(_mtm: MainThreadMarker) -> Id<Self> {
unsafe { msg_send_id![Self::class(), currentDevice] }
}
#[method(userInterfaceIdiom)]
pub fn userInterfaceIdiom(&self) -> UIUserInterfaceIdiom;
}
);
#[repr(transparent)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct UIUserInterfaceIdiom(NSInteger);
unsafe impl Encode for UIUserInterfaceIdiom {
const ENCODING: Encoding = NSInteger::ENCODING;
}
impl UIUserInterfaceIdiom {
pub const Unspecified: UIUserInterfaceIdiom = UIUserInterfaceIdiom(-1);
pub const Phone: UIUserInterfaceIdiom = UIUserInterfaceIdiom(0);
pub const Pad: UIUserInterfaceIdiom = UIUserInterfaceIdiom(1);
pub const TV: UIUserInterfaceIdiom = UIUserInterfaceIdiom(2);
pub const CarPlay: UIUserInterfaceIdiom = UIUserInterfaceIdiom(3);
}

View File

@@ -0,0 +1,12 @@
use objc2::{extern_class, mutability, ClassType};
use objc2_foundation::NSObject;
extern_class!(
#[derive(Debug, PartialEq, Eq, Hash)]
pub(crate) struct UIEvent;
unsafe impl ClassType for UIEvent {
type Super = NSObject;
type Mutability = mutability::InteriorMutable;
}
);

View File

@@ -0,0 +1,14 @@
use objc2::encode::{Encode, Encoding};
use objc2_foundation::NSUInteger;
#[repr(transparent)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct UIRectEdge(pub NSUInteger);
impl UIRectEdge {
pub const NONE: Self = Self(0);
}
unsafe impl Encode for UIRectEdge {
const ENCODING: Encoding = NSUInteger::ENCODING;
}

View File

@@ -0,0 +1,176 @@
use objc2::encode::{Encode, Encoding};
use objc2::rc::Id;
use objc2::runtime::ProtocolObject;
use objc2::{extern_class, extern_methods, extern_protocol, mutability, ClassType, ProtocolType};
use objc2_foundation::{CGFloat, CGPoint, NSInteger, NSObject, NSObjectProtocol, NSUInteger};
use super::UIView;
extern_class!(
/// [`UIGestureRecognizer`](https://developer.apple.com/documentation/uikit/uigesturerecognizer)
#[derive(Debug, PartialEq, Eq, Hash)]
pub(crate) struct UIGestureRecognizer;
unsafe impl ClassType for UIGestureRecognizer {
type Super = NSObject;
type Mutability = mutability::InteriorMutable;
}
);
extern_methods!(
unsafe impl UIGestureRecognizer {
#[method(state)]
pub fn state(&self) -> UIGestureRecognizerState;
/// [`delegate`](https://developer.apple.com/documentation/uikit/uigesturerecognizer/1624207-delegate?language=objc)
/// @property(nullable, nonatomic, weak) id<UIGestureRecognizerDelegate> delegate;
#[method(setDelegate:)]
pub fn setDelegate(&self, delegate: &ProtocolObject<dyn UIGestureRecognizerDelegate>);
#[method_id(delegate)]
pub fn delegate(&self) -> Id<ProtocolObject<dyn UIGestureRecognizerDelegate>>;
}
);
unsafe impl Encode for UIGestureRecognizer {
const ENCODING: Encoding = Encoding::Object;
}
// [`UIGestureRecognizerState`](https://developer.apple.com/documentation/uikit/uigesturerecognizer/state)
#[repr(transparent)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct UIGestureRecognizerState(NSInteger);
unsafe impl Encode for UIGestureRecognizerState {
const ENCODING: Encoding = NSInteger::ENCODING;
}
#[allow(dead_code)]
impl UIGestureRecognizerState {
pub const Possible: Self = Self(0);
pub const Began: Self = Self(1);
pub const Changed: Self = Self(2);
pub const Ended: Self = Self(3);
pub const Cancelled: Self = Self(4);
pub const Failed: Self = Self(5);
}
// [`UIPinchGestureRecognizer`](https://developer.apple.com/documentation/uikit/uipinchgesturerecognizer)
extern_class!(
#[derive(Debug, PartialEq, Eq, Hash)]
pub(crate) struct UIPinchGestureRecognizer;
unsafe impl ClassType for UIPinchGestureRecognizer {
type Super = UIGestureRecognizer;
type Mutability = mutability::InteriorMutable;
}
);
extern_methods!(
unsafe impl UIPinchGestureRecognizer {
#[method(scale)]
pub fn scale(&self) -> CGFloat;
#[method(velocity)]
pub fn velocity(&self) -> CGFloat;
}
);
unsafe impl Encode for UIPinchGestureRecognizer {
const ENCODING: Encoding = Encoding::Object;
}
extern_class!(
/// [`UIRotationGestureRecognizer`](https://developer.apple.com/documentation/uikit/uirotationgesturerecognizer)
#[derive(Debug, PartialEq, Eq, Hash)]
pub(crate) struct UIRotationGestureRecognizer;
unsafe impl ClassType for UIRotationGestureRecognizer {
type Super = UIGestureRecognizer;
type Mutability = mutability::InteriorMutable;
}
);
extern_methods!(
unsafe impl UIRotationGestureRecognizer {
#[method(rotation)]
pub fn rotation(&self) -> CGFloat;
#[method(velocity)]
pub fn velocity(&self) -> CGFloat;
}
);
unsafe impl Encode for UIRotationGestureRecognizer {
const ENCODING: Encoding = Encoding::Object;
}
extern_class!(
/// [`UITapGestureRecognizer`](https://developer.apple.com/documentation/uikit/uitapgesturerecognizer)
#[derive(Debug, PartialEq, Eq, Hash)]
pub(crate) struct UITapGestureRecognizer;
unsafe impl ClassType for UITapGestureRecognizer {
type Super = UIGestureRecognizer;
type Mutability = mutability::InteriorMutable;
}
);
extern_methods!(
unsafe impl UITapGestureRecognizer {
#[method(setNumberOfTapsRequired:)]
pub fn setNumberOfTapsRequired(&self, number_of_taps_required: NSUInteger);
#[method(setNumberOfTouchesRequired:)]
pub fn setNumberOfTouchesRequired(&self, number_of_touches_required: NSUInteger);
}
);
unsafe impl Encode for UITapGestureRecognizer {
const ENCODING: Encoding = Encoding::Object;
}
extern_class!(
/// [`UIPanGestureRecognizer`](https://developer.apple.com/documentation/uikit/uipangesturerecognizer)
#[derive(Debug, PartialEq, Eq, Hash)]
pub(crate) struct UIPanGestureRecognizer;
unsafe impl ClassType for UIPanGestureRecognizer {
type Super = UIGestureRecognizer;
type Mutability = mutability::InteriorMutable;
}
);
extern_methods!(
unsafe impl UIPanGestureRecognizer {
#[method(translationInView:)]
pub fn translationInView(&self, view: &UIView) -> CGPoint;
#[method(setTranslation:inView:)]
pub fn setTranslationInView(&self, translation: CGPoint, view: &UIView);
#[method(velocityInView:)]
pub fn velocityInView(&self, view: &UIView) -> CGPoint;
#[method(setMinimumNumberOfTouches:)]
pub fn setMinimumNumberOfTouches(&self, minimum_number_of_touches: NSUInteger);
#[method(minimumNumberOfTouches)]
pub fn minimumNumberOfTouches(&self) -> NSUInteger;
#[method(setMaximumNumberOfTouches:)]
pub fn setMaximumNumberOfTouches(&self, maximum_number_of_touches: NSUInteger);
#[method(maximumNumberOfTouches)]
pub fn maximumNumberOfTouches(&self) -> NSUInteger;
}
);
extern_protocol!(
/// (@protocol UIGestureRecognizerDelegate)[https://developer.apple.com/documentation/uikit/uigesturerecognizerdelegate?language=objc]
pub(crate) unsafe trait UIGestureRecognizerDelegate: NSObjectProtocol {}
unsafe impl ProtocolType for dyn UIGestureRecognizerDelegate {
const NAME: &'static str = "UIGestureRecognizerDelegate";
}
);

View File

@@ -0,0 +1,53 @@
#![allow(non_snake_case)]
#![allow(non_upper_case_globals)]
use std::os::raw::{c_char, c_int};
use objc2_foundation::NSString;
mod application;
mod coordinate_space;
mod device;
mod event;
mod geometry;
mod gesture_recognizer;
mod responder;
mod screen;
mod screen_mode;
mod status_bar_style;
mod touch;
mod trait_collection;
mod view;
mod view_controller;
mod window;
pub(crate) use self::application::UIApplication;
pub(crate) use self::coordinate_space::UICoordinateSpace;
pub(crate) use self::device::{UIDevice, UIUserInterfaceIdiom};
pub(crate) use self::event::UIEvent;
pub(crate) use self::geometry::UIRectEdge;
pub(crate) use self::gesture_recognizer::{
UIGestureRecognizer, UIGestureRecognizerDelegate, UIGestureRecognizerState,
UIPanGestureRecognizer, UIPinchGestureRecognizer, UIRotationGestureRecognizer,
UITapGestureRecognizer,
};
pub(crate) use self::responder::UIResponder;
pub(crate) use self::screen::{UIScreen, UIScreenOverscanCompensation};
pub(crate) use self::screen_mode::UIScreenMode;
pub(crate) use self::status_bar_style::UIStatusBarStyle;
pub(crate) use self::touch::{UITouch, UITouchPhase, UITouchType};
pub(crate) use self::trait_collection::{UIForceTouchCapability, UITraitCollection};
#[allow(unused_imports)]
pub(crate) use self::view::{UIEdgeInsets, UIView};
pub(crate) use self::view_controller::{UIInterfaceOrientationMask, UIViewController};
pub(crate) use self::window::UIWindow;
#[link(name = "UIKit", kind = "framework")]
extern "C" {
pub fn UIApplicationMain(
argc: c_int,
argv: *const c_char,
principalClassName: Option<&NSString>,
delegateClassName: Option<&NSString>,
) -> c_int;
}

View File

@@ -0,0 +1,12 @@
use objc2::{extern_class, mutability, ClassType};
use objc2_foundation::NSObject;
extern_class!(
#[derive(Debug, PartialEq, Eq, Hash)]
pub(crate) struct UIResponder;
unsafe impl ClassType for UIResponder {
type Super = NSObject;
type Mutability = mutability::InteriorMutable;
}
);

View File

@@ -0,0 +1,80 @@
use objc2::encode::{Encode, Encoding};
use objc2::rc::Id;
use objc2::{extern_class, extern_methods, msg_send_id, mutability, ClassType};
use objc2_foundation::{CGFloat, CGRect, MainThreadMarker, NSArray, NSInteger, NSObject};
use super::{UICoordinateSpace, UIScreenMode};
extern_class!(
#[derive(Debug, PartialEq, Eq, Hash)]
pub(crate) struct UIScreen;
unsafe impl ClassType for UIScreen {
type Super = NSObject;
type Mutability = mutability::InteriorMutable;
}
);
extern_methods!(
unsafe impl UIScreen {
pub fn main(_mtm: MainThreadMarker) -> Id<Self> {
unsafe { msg_send_id![Self::class(), mainScreen] }
}
pub fn screens(_mtm: MainThreadMarker) -> Id<NSArray<Self>> {
unsafe { msg_send_id![Self::class(), screens] }
}
#[method(bounds)]
pub fn bounds(&self) -> CGRect;
#[method(scale)]
pub fn scale(&self) -> CGFloat;
#[method(nativeBounds)]
pub fn nativeBounds(&self) -> CGRect;
#[method(nativeScale)]
pub fn nativeScale(&self) -> CGFloat;
#[method(maximumFramesPerSecond)]
pub fn maximumFramesPerSecond(&self) -> NSInteger;
pub fn mirroredScreen(&self) -> Id<Self> {
unsafe { msg_send_id![Self::class(), mirroredScreen] }
}
pub fn preferredMode(&self) -> Option<Id<UIScreenMode>> {
unsafe { msg_send_id![self, preferredMode] }
}
#[method(setCurrentMode:)]
pub fn setCurrentMode(&self, mode: Option<&UIScreenMode>);
pub fn availableModes(&self) -> Id<NSArray<UIScreenMode>> {
unsafe { msg_send_id![self, availableModes] }
}
#[method(setOverscanCompensation:)]
pub fn setOverscanCompensation(&self, overscanCompensation: UIScreenOverscanCompensation);
pub fn coordinateSpace(&self) -> Id<UICoordinateSpace> {
unsafe { msg_send_id![self, coordinateSpace] }
}
}
);
#[repr(transparent)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct UIScreenOverscanCompensation(NSInteger);
unsafe impl Encode for UIScreenOverscanCompensation {
const ENCODING: Encoding = NSInteger::ENCODING;
}
#[allow(dead_code)]
impl UIScreenOverscanCompensation {
pub const Scale: Self = Self(0);
pub const InsetBounds: Self = Self(1);
pub const None: Self = Self(2);
}

View File

@@ -0,0 +1,19 @@
use objc2::{extern_class, extern_methods, mutability, ClassType};
use objc2_foundation::{CGSize, NSObject};
extern_class!(
#[derive(Debug, PartialEq, Eq, Hash)]
pub(crate) struct UIScreenMode;
unsafe impl ClassType for UIScreenMode {
type Super = NSObject;
type Mutability = mutability::InteriorMutable;
}
);
extern_methods!(
unsafe impl UIScreenMode {
#[method(size)]
pub fn size(&self) -> CGSize;
}
);

View File

@@ -0,0 +1,16 @@
use objc2::encode::{Encode, Encoding};
use objc2_foundation::NSInteger;
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
#[allow(dead_code)]
#[repr(isize)]
pub enum UIStatusBarStyle {
#[default]
Default = 0,
LightContent = 1,
DarkContent = 3,
}
unsafe impl Encode for UIStatusBarStyle {
const ENCODING: Encoding = NSInteger::ENCODING;
}

View File

@@ -0,0 +1,65 @@
use objc2::encode::{Encode, Encoding};
use objc2::{extern_class, extern_methods, mutability, ClassType};
use objc2_foundation::{CGFloat, CGPoint, NSInteger, NSObject};
use super::UIView;
extern_class!(
#[derive(Debug, PartialEq, Eq, Hash)]
pub(crate) struct UITouch;
unsafe impl ClassType for UITouch {
type Super = NSObject;
type Mutability = mutability::InteriorMutable;
}
);
extern_methods!(
unsafe impl UITouch {
#[method(locationInView:)]
pub fn locationInView(&self, view: Option<&UIView>) -> CGPoint;
#[method(type)]
pub fn type_(&self) -> UITouchType;
#[method(force)]
pub fn force(&self) -> CGFloat;
#[method(maximumPossibleForce)]
pub fn maximumPossibleForce(&self) -> CGFloat;
#[method(altitudeAngle)]
pub fn altitudeAngle(&self) -> CGFloat;
#[method(phase)]
pub fn phase(&self) -> UITouchPhase;
}
);
#[derive(Debug, PartialEq, Eq)]
#[allow(dead_code)]
#[repr(isize)]
pub enum UITouchType {
Direct = 0,
Indirect,
Pencil,
}
unsafe impl Encode for UITouchType {
const ENCODING: Encoding = NSInteger::ENCODING;
}
#[derive(Debug)]
#[allow(dead_code)]
#[repr(isize)]
pub enum UITouchPhase {
Began = 0,
Moved,
Stationary,
Ended,
Cancelled,
}
unsafe impl Encode for UITouchPhase {
const ENCODING: Encoding = NSInteger::ENCODING;
}

View File

@@ -0,0 +1,33 @@
use objc2::encode::{Encode, Encoding};
use objc2::{extern_class, extern_methods, mutability, ClassType};
use objc2_foundation::{NSInteger, NSObject};
extern_class!(
#[derive(Debug, PartialEq, Eq, Hash)]
pub(crate) struct UITraitCollection;
unsafe impl ClassType for UITraitCollection {
type Super = NSObject;
type Mutability = mutability::InteriorMutable;
}
);
extern_methods!(
unsafe impl UITraitCollection {
#[method(forceTouchCapability)]
pub fn forceTouchCapability(&self) -> UIForceTouchCapability;
}
);
#[derive(Debug, PartialEq, Eq)]
#[allow(dead_code)]
#[repr(isize)]
pub enum UIForceTouchCapability {
Unknown = 0,
Unavailable,
Available,
}
unsafe impl Encode for UIForceTouchCapability {
const ENCODING: Encoding = NSInteger::ENCODING;
}

View File

@@ -0,0 +1,93 @@
use objc2::encode::{Encode, Encoding};
use objc2::rc::Id;
use objc2::{extern_class, extern_methods, msg_send_id, mutability, ClassType};
use objc2_foundation::{CGFloat, CGRect, NSObject};
use super::{UICoordinateSpace, UIGestureRecognizer, UIResponder, UIViewController};
extern_class!(
#[derive(Debug, PartialEq, Eq, Hash)]
pub(crate) struct UIView;
unsafe impl ClassType for UIView {
#[inherits(NSObject)]
type Super = UIResponder;
type Mutability = mutability::InteriorMutable;
}
);
extern_methods!(
unsafe impl UIView {
#[method(bounds)]
pub fn bounds(&self) -> CGRect;
#[method(setBounds:)]
pub fn setBounds(&self, value: CGRect);
#[method(frame)]
pub fn frame(&self) -> CGRect;
#[method(setFrame:)]
pub fn setFrame(&self, value: CGRect);
#[method(contentScaleFactor)]
pub fn contentScaleFactor(&self) -> CGFloat;
#[method(setContentScaleFactor:)]
pub fn setContentScaleFactor(&self, val: CGFloat);
#[method(setMultipleTouchEnabled:)]
pub fn setMultipleTouchEnabled(&self, val: bool);
pub fn rootViewController(&self) -> Option<Id<UIViewController>> {
unsafe { msg_send_id![self, rootViewController] }
}
#[method(setRootViewController:)]
pub fn setRootViewController(&self, rootViewController: Option<&UIViewController>);
#[method(convertRect:toCoordinateSpace:)]
pub fn convertRect_toCoordinateSpace(
&self,
rect: CGRect,
coordinateSpace: &UICoordinateSpace,
) -> CGRect;
#[method(convertRect:fromCoordinateSpace:)]
pub fn convertRect_fromCoordinateSpace(
&self,
rect: CGRect,
coordinateSpace: &UICoordinateSpace,
) -> CGRect;
#[method(safeAreaInsets)]
pub fn safeAreaInsets(&self) -> UIEdgeInsets;
#[method(setNeedsDisplay)]
pub fn setNeedsDisplay(&self);
#[method(addGestureRecognizer:)]
pub fn addGestureRecognizer(&self, gestureRecognizer: &UIGestureRecognizer);
#[method(removeGestureRecognizer:)]
pub fn removeGestureRecognizer(&self, gestureRecognizer: &UIGestureRecognizer);
}
);
#[repr(C)]
#[derive(Debug, Clone)]
pub struct UIEdgeInsets {
pub top: CGFloat,
pub left: CGFloat,
pub bottom: CGFloat,
pub right: CGFloat,
}
unsafe impl Encode for UIEdgeInsets {
const ENCODING: Encoding = Encoding::Struct("UIEdgeInsets", &[
CGFloat::ENCODING,
CGFloat::ENCODING,
CGFloat::ENCODING,
CGFloat::ENCODING,
]);
}

View File

@@ -0,0 +1,57 @@
use objc2::encode::{Encode, Encoding};
use objc2::rc::Id;
use objc2::{extern_class, extern_methods, msg_send_id, mutability, ClassType};
use objc2_foundation::{NSObject, NSUInteger};
use super::{UIResponder, UIView};
extern_class!(
#[derive(Debug, PartialEq, Eq, Hash)]
pub(crate) struct UIViewController;
unsafe impl ClassType for UIViewController {
#[inherits(NSObject)]
type Super = UIResponder;
type Mutability = mutability::InteriorMutable;
}
);
extern_methods!(
unsafe impl UIViewController {
#[method(attemptRotationToDeviceOrientation)]
pub fn attemptRotationToDeviceOrientation();
#[method(setNeedsStatusBarAppearanceUpdate)]
pub fn setNeedsStatusBarAppearanceUpdate(&self);
#[method(setNeedsUpdateOfHomeIndicatorAutoHidden)]
pub fn setNeedsUpdateOfHomeIndicatorAutoHidden(&self);
#[method(setNeedsUpdateOfScreenEdgesDeferringSystemGestures)]
pub fn setNeedsUpdateOfScreenEdgesDeferringSystemGestures(&self);
pub fn view(&self) -> Option<Id<UIView>> {
unsafe { msg_send_id![self, view] }
}
#[method(setView:)]
pub fn setView(&self, view: Option<&UIView>);
}
);
bitflags::bitflags! {
#[derive(Clone, Copy)]
pub struct UIInterfaceOrientationMask: NSUInteger {
const Portrait = 1 << 1;
const PortraitUpsideDown = 1 << 2;
const LandscapeRight = 1 << 3;
const LandscapeLeft = 1 << 4;
const Landscape = Self::LandscapeLeft.bits() | Self::LandscapeRight.bits();
const AllButUpsideDown = Self::Landscape.bits() | Self::Portrait.bits();
const All = Self::AllButUpsideDown.bits() | Self::PortraitUpsideDown.bits();
}
}
unsafe impl Encode for UIInterfaceOrientationMask {
const ENCODING: Encoding = NSUInteger::ENCODING;
}

View File

@@ -0,0 +1,36 @@
use objc2::rc::Id;
use objc2::{extern_class, extern_methods, msg_send_id, mutability, ClassType};
use objc2_foundation::NSObject;
use super::{UIResponder, UIScreen, UIView};
extern_class!(
#[derive(Debug, PartialEq, Eq, Hash)]
pub(crate) struct UIWindow;
unsafe impl ClassType for UIWindow {
#[inherits(UIResponder, NSObject)]
type Super = UIView;
type Mutability = mutability::InteriorMutable;
}
);
extern_methods!(
unsafe impl UIWindow {
pub fn screen(&self) -> Id<UIScreen> {
unsafe { msg_send_id![self, screen] }
}
#[method(setScreen:)]
pub fn setScreen(&self, screen: &UIScreen);
#[method(setHidden:)]
pub fn setHidden(&self, flag: bool);
#[method(makeKeyAndVisible)]
pub fn makeKeyAndVisible(&self);
#[method(isKeyWindow)]
pub fn isKeyWindow(&self) -> bool;
}
);

View File

@@ -1,18 +1,20 @@
#![allow(clippy::unnecessary_cast)]
use std::cell::{Cell, RefCell};
use objc2::rc::Retained;
use objc2::runtime::{NSObjectProtocol, ProtocolObject};
use objc2::{declare_class, msg_send, msg_send_id, mutability, sel, ClassType, DeclaredClass};
use objc2_foundation::{CGFloat, CGPoint, CGRect, MainThreadMarker, NSObject, NSSet};
use objc2_ui_kit::{
UICoordinateSpace, UIEvent, UIForceTouchCapability, UIGestureRecognizer,
UIGestureRecognizerDelegate, UIGestureRecognizerState, UIPanGestureRecognizer,
UIPinchGestureRecognizer, UIResponder, UIRotationGestureRecognizer, UITapGestureRecognizer,
UITouch, UITouchPhase, UITouchType, UITraitEnvironment, UIView,
use objc2::rc::Id;
use objc2::runtime::{AnyClass, NSObjectProtocol, ProtocolObject};
use objc2::{
declare_class, extern_methods, msg_send, msg_send_id, mutability, sel, ClassType, DeclaredClass,
};
use objc2_foundation::{CGFloat, CGPoint, CGRect, MainThreadMarker, NSObject, NSSet};
use super::app_state::{self, EventWrapper};
use super::uikit::{
UIEvent, UIForceTouchCapability, UIGestureRecognizer, UIGestureRecognizerDelegate,
UIGestureRecognizerState, UIPanGestureRecognizer, UIPinchGestureRecognizer, UIResponder,
UIRotationGestureRecognizer, UITapGestureRecognizer, UITouch, UITouchPhase, UITouchType,
UITraitCollection, UIView,
};
use super::window::WinitUIWindow;
use crate::dpi::PhysicalPosition;
use crate::event::{Event, Force, Touch, TouchPhase, WindowEvent};
@@ -20,10 +22,10 @@ use crate::platform_impl::platform::DEVICE_ID;
use crate::window::{WindowAttributes, WindowId as RootWindowId};
pub struct WinitViewState {
pinch_gesture_recognizer: RefCell<Option<Retained<UIPinchGestureRecognizer>>>,
doubletap_gesture_recognizer: RefCell<Option<Retained<UITapGestureRecognizer>>>,
rotation_gesture_recognizer: RefCell<Option<Retained<UIRotationGestureRecognizer>>>,
pan_gesture_recognizer: RefCell<Option<Retained<UIPanGestureRecognizer>>>,
pinch_gesture_recognizer: RefCell<Option<Id<UIPinchGestureRecognizer>>>,
doubletap_gesture_recognizer: RefCell<Option<Id<UITapGestureRecognizer>>>,
rotation_gesture_recognizer: RefCell<Option<Id<UIRotationGestureRecognizer>>>,
pan_gesture_recognizer: RefCell<Option<Id<UIPanGestureRecognizer>>>,
// for iOS delta references the start of the Gesture
rotation_last_delta: Cell<CGFloat>,
@@ -37,7 +39,7 @@ declare_class!(
unsafe impl ClassType for WinitView {
#[inherits(UIResponder, NSObject)]
type Super = UIView;
type Mutability = mutability::MainThreadOnly;
type Mutability = mutability::InteriorMutable;
const NAME: &'static str = "WinitUIView";
}
@@ -268,7 +270,7 @@ declare_class!(
fn pan_gesture(&self, recognizer: &UIPanGestureRecognizer) {
let window = self.window().unwrap();
let translation = recognizer.translationInView(Some(self));
let translation = recognizer.translationInView(self);
let (phase, dx, dy) = match recognizer.state() {
UIGestureRecognizerState::Began => {
@@ -326,13 +328,30 @@ declare_class!(
}
);
extern_methods!(
#[allow(non_snake_case)]
unsafe impl WinitView {
fn window(&self) -> Option<Id<WinitUIWindow>> {
unsafe { msg_send_id![self, window] }
}
unsafe fn traitCollection(&self) -> Id<UITraitCollection> {
msg_send_id![self, traitCollection]
}
// TODO: Allow the user to customize this
#[method(layerClass)]
pub(crate) fn layerClass() -> &'static AnyClass;
}
);
impl WinitView {
pub(crate) fn new(
mtm: MainThreadMarker,
_mtm: MainThreadMarker,
window_attributes: &WindowAttributes,
frame: CGRect,
) -> Retained<Self> {
let this = mtm.alloc().set_ivars(WinitViewState {
) -> Id<Self> {
let this = Self::alloc().set_ivars(WinitViewState {
pinch_gesture_recognizer: RefCell::new(None),
doubletap_gesture_recognizer: RefCell::new(None),
rotation_gesture_recognizer: RefCell::new(None),
@@ -342,7 +361,7 @@ impl WinitView {
pinch_last_delta: Cell::new(0.0),
pan_last_delta: Cell::new(CGPoint { x: 0.0, y: 0.0 }),
});
let this: Retained<Self> = unsafe { msg_send_id![super(this), initWithFrame: frame] };
let this: Id<Self> = unsafe { msg_send_id![super(this), initWithFrame: frame] };
this.setMultipleTouchEnabled(true);
@@ -353,23 +372,13 @@ impl WinitView {
this
}
fn window(&self) -> Option<Retained<WinitUIWindow>> {
// SAFETY: `WinitView`s are always installed in a `WinitUIWindow`
(**self).window().map(|window| unsafe { Retained::cast(window) })
}
pub(crate) fn recognize_pinch_gesture(&self, should_recognize: bool) {
let mtm = MainThreadMarker::from(self);
if should_recognize {
if self.ivars().pinch_gesture_recognizer.borrow().is_none() {
let pinch = unsafe {
UIPinchGestureRecognizer::initWithTarget_action(
mtm.alloc(),
Some(self),
Some(sel!(pinchGesture:)),
)
let pinch: Id<UIPinchGestureRecognizer> = unsafe {
msg_send_id![UIPinchGestureRecognizer::alloc(), initWithTarget: self, action: sel!(pinchGesture:)]
};
pinch.setDelegate(Some(ProtocolObject::from_ref(self)));
pinch.setDelegate(ProtocolObject::from_ref(self));
self.addGestureRecognizer(&pinch);
self.ivars().pinch_gesture_recognizer.replace(Some(pinch));
}
@@ -384,17 +393,12 @@ impl WinitView {
minimum_number_of_touches: u8,
maximum_number_of_touches: u8,
) {
let mtm = MainThreadMarker::from(self);
if should_recognize {
if self.ivars().pan_gesture_recognizer.borrow().is_none() {
let pan = unsafe {
UIPanGestureRecognizer::initWithTarget_action(
mtm.alloc(),
Some(self),
Some(sel!(panGesture:)),
)
let pan: Id<UIPanGestureRecognizer> = unsafe {
msg_send_id![UIPanGestureRecognizer::alloc(), initWithTarget: self, action: sel!(panGesture:)]
};
pan.setDelegate(Some(ProtocolObject::from_ref(self)));
pan.setDelegate(ProtocolObject::from_ref(self));
pan.setMinimumNumberOfTouches(minimum_number_of_touches as _);
pan.setMaximumNumberOfTouches(maximum_number_of_touches as _);
self.addGestureRecognizer(&pan);
@@ -406,17 +410,12 @@ impl WinitView {
}
pub(crate) fn recognize_doubletap_gesture(&self, should_recognize: bool) {
let mtm = MainThreadMarker::from(self);
if should_recognize {
if self.ivars().doubletap_gesture_recognizer.borrow().is_none() {
let tap = unsafe {
UITapGestureRecognizer::initWithTarget_action(
mtm.alloc(),
Some(self),
Some(sel!(doubleTapGesture:)),
)
let tap: Id<UITapGestureRecognizer> = unsafe {
msg_send_id![UITapGestureRecognizer::alloc(), initWithTarget: self, action: sel!(doubleTapGesture:)]
};
tap.setDelegate(Some(ProtocolObject::from_ref(self)));
tap.setDelegate(ProtocolObject::from_ref(self));
tap.setNumberOfTapsRequired(2);
tap.setNumberOfTouchesRequired(1);
self.addGestureRecognizer(&tap);
@@ -428,17 +427,12 @@ impl WinitView {
}
pub(crate) fn recognize_rotation_gesture(&self, should_recognize: bool) {
let mtm = MainThreadMarker::from(self);
if should_recognize {
if self.ivars().rotation_gesture_recognizer.borrow().is_none() {
let rotation = unsafe {
UIRotationGestureRecognizer::initWithTarget_action(
mtm.alloc(),
Some(self),
Some(sel!(rotationGesture:)),
)
let rotation: Id<UIRotationGestureRecognizer> = unsafe {
msg_send_id![UIRotationGestureRecognizer::alloc(), initWithTarget: self, action: sel!(rotationGesture:)]
};
rotation.setDelegate(Some(ProtocolObject::from_ref(self)));
rotation.setDelegate(ProtocolObject::from_ref(self));
self.addGestureRecognizer(&rotation);
self.ivars().rotation_gesture_recognizer.replace(Some(rotation));
}
@@ -453,9 +447,9 @@ impl WinitView {
let os_supports_force = app_state::os_capabilities().force_touch;
for touch in touches {
let logical_location = touch.locationInView(None);
let touch_type = touch.r#type();
let touch_type = touch.type_();
let force = if os_supports_force {
let trait_collection = self.traitCollection();
let trait_collection = unsafe { self.traitCollection() };
let touch_capability = trait_collection.forceTouchCapability();
// Both the OS _and_ the device need to be checked for force touch support.
if touch_capability == UIForceTouchCapability::Available
@@ -488,7 +482,7 @@ impl WinitView {
// 2 is UITouchPhase::Stationary and is not expected here
UITouchPhase::Ended => TouchPhase::Ended,
UITouchPhase::Cancelled => TouchPhase::Cancelled,
_ => panic!("unexpected touch phase: {phase:?}"),
_ => panic!("unexpected touch phase: {:?}", phase as i32),
};
let physical_location = {

View File

@@ -1,14 +1,14 @@
use std::cell::Cell;
use objc2::rc::Retained;
use objc2::rc::Id;
use objc2::{declare_class, msg_send_id, mutability, ClassType, DeclaredClass};
use objc2_foundation::{MainThreadMarker, NSObject};
use objc2_ui_kit::{
use super::app_state::{self};
use super::uikit::{
UIDevice, UIInterfaceOrientationMask, UIRectEdge, UIResponder, UIStatusBarStyle,
UIUserInterfaceIdiom, UIView, UIViewController,
};
use super::app_state::{self};
use crate::platform::ios::{ScreenEdge, StatusBarStyle, ValidOrientations};
use crate::window::WindowAttributes;
@@ -26,7 +26,7 @@ declare_class!(
unsafe impl ClassType for WinitViewController {
#[inherits(UIResponder, NSObject)]
type Super = UIViewController;
type Mutability = mutability::MainThreadOnly;
type Mutability = mutability::InteriorMutable;
const NAME: &'static str = "WinitUIViewController";
}
@@ -114,7 +114,7 @@ impl WinitViewController {
mtm: MainThreadMarker,
valid_orientations: ValidOrientations,
) {
let mask = match (valid_orientations, UIDevice::currentDevice(mtm).userInterfaceIdiom()) {
let mask = match (valid_orientations, UIDevice::current(mtm).userInterfaceIdiom()) {
(ValidOrientations::LandscapeAndPortrait, UIUserInterfaceIdiom::Phone) => {
UIInterfaceOrientationMask::AllButUpsideDown
},
@@ -129,24 +129,23 @@ impl WinitViewController {
},
};
self.ivars().supported_orientations.set(mask);
#[allow(deprecated)]
UIViewController::attemptRotationToDeviceOrientation(mtm);
UIViewController::attemptRotationToDeviceOrientation();
}
pub(crate) fn new(
mtm: MainThreadMarker,
window_attributes: &WindowAttributes,
view: &UIView,
) -> Retained<Self> {
) -> Id<Self> {
// These are set properly below, we just to set them to something in the meantime.
let this = mtm.alloc().set_ivars(ViewControllerState {
let this = Self::alloc().set_ivars(ViewControllerState {
prefers_status_bar_hidden: Cell::new(false),
preferred_status_bar_style: Cell::new(UIStatusBarStyle::Default),
prefers_home_indicator_auto_hidden: Cell::new(false),
supported_orientations: Cell::new(UIInterfaceOrientationMask::All),
preferred_screen_edges_deferring_system_gestures: Cell::new(UIRectEdge::empty()),
preferred_screen_edges_deferring_system_gestures: Cell::new(UIRectEdge::NONE),
});
let this: Retained<Self> = unsafe { msg_send_id![super(this), init] };
let this: Id<Self> = unsafe { msg_send_id![super(this), init] };
this.set_prefers_status_bar_hidden(
window_attributes.platform_specific.prefers_status_bar_hidden,

View File

@@ -2,19 +2,16 @@
use std::collections::VecDeque;
use objc2::rc::Retained;
use objc2::rc::Id;
use objc2::runtime::{AnyObject, NSObject};
use objc2::{class, declare_class, msg_send, msg_send_id, mutability, ClassType, DeclaredClass};
use objc2_foundation::{
CGFloat, CGPoint, CGRect, CGSize, MainThreadBound, MainThreadMarker, NSObjectProtocol,
};
use objc2_ui_kit::{
UIApplication, UICoordinateSpace, UIResponder, UIScreen, UIScreenOverscanCompensation,
UIViewController, UIWindow,
};
use objc2_foundation::{CGFloat, CGPoint, CGRect, CGSize, MainThreadBound, MainThreadMarker};
use tracing::{debug, warn};
use super::app_state::EventWrapper;
use super::uikit::{
UIApplication, UIResponder, UIScreen, UIScreenOverscanCompensation, UIViewController, UIWindow,
};
use super::view::WinitView;
use super::view_controller::WinitViewController;
use crate::cursor::Cursor;
@@ -38,7 +35,7 @@ declare_class!(
unsafe impl ClassType for WinitUIWindow {
#[inherits(UIResponder, NSObject)]
type Super = UIWindow;
type Mutability = mutability::MainThreadOnly;
type Mutability = mutability::InteriorMutable;
const NAME: &'static str = "WinitUIWindow";
}
@@ -79,8 +76,8 @@ impl WinitUIWindow {
window_attributes: &WindowAttributes,
frame: CGRect,
view_controller: &UIViewController,
) -> Retained<Self> {
let this: Retained<Self> = unsafe { msg_send_id![mtm.alloc(), initWithFrame: frame] };
) -> Id<Self> {
let this: Id<Self> = unsafe { msg_send_id![Self::alloc(), initWithFrame: frame] };
this.setRootViewController(Some(view_controller));
@@ -107,9 +104,9 @@ impl WinitUIWindow {
}
pub struct Inner {
window: Retained<WinitUIWindow>,
view_controller: Retained<WinitViewController>,
view: Retained<WinitView>,
window: Id<WinitUIWindow>,
view_controller: Id<WinitViewController>,
view: Id<WinitView>,
gl_or_metal_backed: bool,
}
@@ -397,8 +394,7 @@ impl Inner {
}
pub fn primary_monitor(&self) -> Option<MonitorHandle> {
#[allow(deprecated)]
Some(MonitorHandle::new(UIScreen::mainScreen(MainThreadMarker::new().unwrap())))
Some(MonitorHandle::new(UIScreen::main(MainThreadMarker::new().unwrap())))
}
pub fn id(&self) -> WindowId {
@@ -408,18 +404,18 @@ impl Inner {
#[cfg(feature = "rwh_04")]
pub fn raw_window_handle_rwh_04(&self) -> rwh_04::RawWindowHandle {
let mut window_handle = rwh_04::UiKitHandle::empty();
window_handle.ui_window = Retained::as_ptr(&self.window) as _;
window_handle.ui_view = Retained::as_ptr(&self.view) as _;
window_handle.ui_view_controller = Retained::as_ptr(&self.view_controller) as _;
window_handle.ui_window = Id::as_ptr(&self.window) as _;
window_handle.ui_view = Id::as_ptr(&self.view) as _;
window_handle.ui_view_controller = Id::as_ptr(&self.view_controller) as _;
rwh_04::RawWindowHandle::UiKit(window_handle)
}
#[cfg(feature = "rwh_05")]
pub fn raw_window_handle_rwh_05(&self) -> rwh_05::RawWindowHandle {
let mut window_handle = rwh_05::UiKitWindowHandle::empty();
window_handle.ui_window = Retained::as_ptr(&self.window) as _;
window_handle.ui_view = Retained::as_ptr(&self.view) as _;
window_handle.ui_view_controller = Retained::as_ptr(&self.view_controller) as _;
window_handle.ui_window = Id::as_ptr(&self.window) as _;
window_handle.ui_view = Id::as_ptr(&self.view) as _;
window_handle.ui_view_controller = Id::as_ptr(&self.view_controller) as _;
rwh_05::RawWindowHandle::UiKit(window_handle)
}
@@ -431,11 +427,11 @@ impl Inner {
#[cfg(feature = "rwh_06")]
pub fn raw_window_handle_rwh_06(&self) -> rwh_06::RawWindowHandle {
let mut window_handle = rwh_06::UiKitWindowHandle::new({
let ui_view = Retained::as_ptr(&self.view) as _;
std::ptr::NonNull::new(ui_view).expect("Retained<T> should never be null")
let ui_view = Id::as_ptr(&self.view) as _;
std::ptr::NonNull::new(ui_view).expect("Id<T> should never be null")
});
window_handle.ui_view_controller =
std::ptr::NonNull::new(Retained::as_ptr(&self.view_controller) as _);
std::ptr::NonNull::new(Id::as_ptr(&self.view_controller) as _);
rwh_06::RawWindowHandle::UiKit(window_handle)
}
@@ -485,8 +481,7 @@ impl Window {
// TODO: transparency, visible
#[allow(deprecated)]
let main_screen = UIScreen::mainScreen(mtm);
let main_screen = UIScreen::main(mtm);
let fullscreen = window_attributes.fullscreen.clone().map(Into::into);
let screen = match fullscreen {
Some(Fullscreen::Exclusive(ref video_mode)) => video_mode.monitor.ui_screen(mtm),
@@ -510,8 +505,12 @@ impl Window {
let view = WinitView::new(mtm, &window_attributes, frame);
let gl_or_metal_backed =
view.isKindOfClass(class!(CAMetalLayer)) || view.isKindOfClass(class!(CAEAGLLayer));
let gl_or_metal_backed = unsafe {
let layer_class = WinitView::layerClass();
let is_metal = msg_send![layer_class, isSubclassOfClass: class!(CAMetalLayer)];
let is_gl = msg_send![layer_class, isSubclassOfClass: class!(CAEAGLLayer)];
is_metal || is_gl
};
let view_controller = WinitViewController::new(mtm, &window_attributes, &view);
let window = WinitUIWindow::new(mtm, &window_attributes, frame, &view_controller);
@@ -675,8 +674,10 @@ impl Inner {
} else {
let screen_frame = self.rect_to_screen_space(bounds);
let status_bar_frame = {
let app = UIApplication::sharedApplication(MainThreadMarker::new().unwrap());
#[allow(deprecated)]
let app = UIApplication::shared(MainThreadMarker::new().unwrap()).expect(
"`Window::get_inner_position` cannot be called before `EventLoop::run_app` on \
iOS",
);
app.statusBarFrame()
};
let (y, height) = if screen_frame.origin.y > status_bar_frame.size.height {

View File

@@ -11,8 +11,6 @@ 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;
@@ -21,9 +19,12 @@ 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::{AsyncRequestSerial, ControlFlow, DeviceEvents, EventLoopClosed};
use crate::event_loop::{
ActiveEventLoop as RootELW, 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::{
@@ -784,37 +785,29 @@ impl<T: 'static> EventLoop<T> {
Ok(EventLoop::X(x11::EventLoop::new(xconn)))
}
#[inline]
pub fn is_wayland(&self) -> bool {
match *self {
#[cfg(wayland_platform)]
EventLoop::Wayland(_) => true,
#[cfg(x11_platform)]
_ => false,
}
}
pub fn create_proxy(&self) -> EventLoopProxy<T> {
x11_or_wayland!(match self; EventLoop(evlp) => evlp.create_proxy(); as EventLoopProxy)
}
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<F>(mut self, callback: F) -> Result<(), EventLoopError>
where
F: FnMut(crate::event::Event<T>, &RootELW),
{
self.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 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 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 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 window_target(&self) -> &crate::event_loop::ActiveEventLoop {

View File

@@ -14,7 +14,6 @@ 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};
@@ -174,16 +173,12 @@ impl<T: 'static> EventLoop<T> {
Ok(event_loop)
}
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> {
pub fn run_on_demand<F>(&mut self, mut event_handler: F) -> Result<(), EventLoopError>
where
F: FnMut(Event<T>, &RootActiveEventLoop),
{
let exit = loop {
match self.pump_app_events(None, app) {
match self.pump_events(None, &mut event_handler) {
PumpStatus::Exit(0) => {
break Ok(());
},
@@ -205,27 +200,26 @@ impl<T: 'static> EventLoop<T> {
exit
}
pub fn pump_app_events<A: ApplicationHandler<T>>(
&mut self,
timeout: Option<Duration>,
app: &mut A,
) -> PumpStatus {
pub fn pump_events<F>(&mut self, timeout: Option<Duration>, mut callback: F) -> PumpStatus
where
F: FnMut(Event<T>, &RootActiveEventLoop),
{
if !self.loop_running {
self.loop_running = true;
// Run the initial loop iteration.
self.single_iteration(app, StartCause::Init);
self.single_iteration(&mut callback, StartCause::Init);
}
// Consider the possibility that the `StartCause::Init` iteration could
// request to Exit.
if !self.exiting() {
self.poll_events_with_timeout(timeout, app);
self.poll_events_with_timeout(timeout, &mut callback);
}
if let Some(code) = self.exit_code() {
self.loop_running = false;
app.exiting(&self.window_target);
callback(Event::LoopExiting, self.window_target());
PumpStatus::Exit(code)
} else {
@@ -233,11 +227,10 @@ impl<T: 'static> EventLoop<T> {
}
}
pub fn poll_events_with_timeout<A: ApplicationHandler<T>>(
&mut self,
mut timeout: Option<Duration>,
app: &mut A,
) {
pub fn poll_events_with_timeout<F>(&mut self, mut timeout: Option<Duration>, mut callback: F)
where
F: FnMut(Event<T>, &RootActiveEventLoop),
{
let cause = loop {
let start = Instant::now();
@@ -299,10 +292,13 @@ impl<T: 'static> EventLoop<T> {
break cause;
};
self.single_iteration(app, cause);
self.single_iteration(&mut callback, cause);
}
fn single_iteration<A: ApplicationHandler<T>>(&mut self, app: &mut A, cause: StartCause) {
fn single_iteration<F>(&mut self, callback: &mut F, cause: StartCause)
where
F: FnMut(Event<T>, &RootActiveEventLoop),
{
// NOTE currently just indented to simplify the diff
// We retain these grow-only scratch buffers as part of the EventLoop
@@ -313,18 +309,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);
app.new_events(&self.window_target, cause);
callback(Event::NewEvents(cause), &self.window_target);
// 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 {
app.resumed(&self.window_target);
callback(Event::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(..) {
app.user_event(&self.window_target, user_event);
callback(Event::UserEvent(user_event), &self.window_target);
}
// Drain the pending compositor updates.
@@ -345,13 +341,18 @@ impl<T: 'static> EventLoop<T> {
let old_physical_size = physical_size;
let new_inner_size = Arc::new(Mutex::new(physical_size));
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);
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 physical_size = *new_inner_size.lock().unwrap();
drop(new_inner_size);
@@ -394,14 +395,23 @@ impl<T: 'static> EventLoop<T> {
size
});
let window_id = crate::window::WindowId(window_id);
let event = WindowEvent::Resized(physical_size);
app.window_event(&self.window_target, window_id, event);
callback(
Event::WindowEvent {
window_id: crate::window::WindowId(window_id),
event: WindowEvent::Resized(physical_size),
},
&self.window_target,
);
}
if compositor_update.close_window {
let window_id = crate::window::WindowId(window_id);
app.window_event(&self.window_target, window_id, WindowEvent::CloseRequested);
callback(
Event::WindowEvent {
window_id: crate::window::WindowId(window_id),
event: WindowEvent::CloseRequested,
},
&self.window_target,
);
}
}
@@ -410,15 +420,8 @@ impl<T: 'static> EventLoop<T> {
buffer_sink.append(&mut state.window_events_sink.lock().unwrap());
});
for event in buffer_sink.drain() {
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."),
}
let event = event.map_nonuser_event().unwrap();
callback(event, &self.window_target);
}
// Handle non-synthetic events.
@@ -426,15 +429,8 @@ impl<T: 'static> EventLoop<T> {
buffer_sink.append(&mut state.events_sink);
});
for event in buffer_sink.drain() {
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."),
}
let event = event.map_nonuser_event().unwrap();
callback(event, &self.window_target);
}
// Collect the window ids
@@ -470,8 +466,10 @@ impl<T: 'static> EventLoop<T> {
});
if let Some(event) = event {
let window_id = crate::window::WindowId(*window_id);
app.window_event(&self.window_target, window_id, event);
callback(
Event::WindowEvent { window_id: crate::window::WindowId(*window_id), event },
&self.window_target,
);
}
}
@@ -481,7 +479,7 @@ impl<T: 'static> EventLoop<T> {
});
// This is always the last event we dispatch before poll again
app.about_to_wait(&self.window_target);
callback(Event::AboutToWait, &self.window_target);
// Update the window frames and schedule redraws.
let mut wake_up = false;

View File

@@ -1424,7 +1424,7 @@ impl EventProcessor {
if !xinput2::XIMaskIsSet(mask, i) {
continue;
}
let x = unsafe { value.read_unaligned() };
let x = unsafe { *value };
// We assume that every XInput2 device with analog axes is a pointing device emitting
// relative coordinates.

View File

@@ -27,7 +27,6 @@ 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};
@@ -380,16 +379,12 @@ impl<T: 'static> EventLoop<T> {
&self.event_processor.target
}
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> {
pub fn run_on_demand<F>(&mut self, mut event_handler: F) -> Result<(), EventLoopError>
where
F: FnMut(Event<T>, &RootAEL),
{
let exit = loop {
match self.pump_app_events(None, app) {
match self.pump_events(None, &mut event_handler) {
PumpStatus::Exit(0) => {
break Ok(());
},
@@ -414,27 +409,26 @@ impl<T: 'static> EventLoop<T> {
exit
}
pub fn pump_app_events<A: ApplicationHandler<T>>(
&mut self,
timeout: Option<Duration>,
app: &mut A,
) -> PumpStatus {
pub fn pump_events<F>(&mut self, timeout: Option<Duration>, mut callback: F) -> PumpStatus
where
F: FnMut(Event<T>, &RootAEL),
{
if !self.loop_running {
self.loop_running = true;
// run the initial loop iteration
self.single_iteration(app, StartCause::Init);
self.single_iteration(&mut callback, StartCause::Init);
}
// Consider the possibility that the `StartCause::Init` iteration could
// request to Exit.
if !self.exiting() {
self.poll_events_with_timeout(timeout, app);
self.poll_events_with_timeout(timeout, &mut callback);
}
if let Some(code) = self.exit_code() {
self.loop_running = false;
app.exiting(self.window_target());
callback(Event::LoopExiting, self.window_target());
PumpStatus::Exit(code)
} else {
@@ -448,11 +442,10 @@ impl<T: 'static> EventLoop<T> {
|| self.redraw_receiver.has_incoming()
}
pub fn poll_events_with_timeout<A: ApplicationHandler<T>>(
&mut self,
mut timeout: Option<Duration>,
app: &mut A,
) {
pub fn poll_events_with_timeout<F>(&mut self, mut timeout: Option<Duration>, mut callback: F)
where
F: FnMut(Event<T>, &RootAEL),
{
let start = Instant::now();
let has_pending = self.has_pending();
@@ -510,20 +503,23 @@ impl<T: 'static> EventLoop<T> {
return;
}
self.single_iteration(app, cause);
self.single_iteration(&mut callback, cause);
}
fn single_iteration<A: ApplicationHandler<T>>(&mut self, app: &mut A, cause: StartCause) {
app.new_events(&self.event_processor.target, 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);
// 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 {
app.resumed(&self.event_processor.target)
callback(Event::Resumed, &self.event_processor.target);
}
// Process all pending events
self.drain_events(app);
self.drain_events(callback);
// Empty activation tokens.
while let Ok((window_id, serial)) = self.activation_receiver.try_recv() {
@@ -533,12 +529,14 @@ impl<T: 'static> EventLoop<T> {
match token {
Some(Ok(token)) => {
let window_id = crate::window::WindowId(window_id);
let event = WindowEvent::ActivationTokenDone {
serial,
token: crate::window::ActivationToken::_new(token),
let event = Event::WindowEvent {
window_id: crate::window::WindowId(window_id),
event: WindowEvent::ActivationTokenDone {
serial,
token: crate::window::ActivationToken::_new(token),
},
};
app.window_event(&self.event_processor.target, window_id, event);
callback(event, &self.event_processor.target)
},
Some(Err(e)) => {
tracing::error!("Failed to get activation token: {}", e);
@@ -550,7 +548,7 @@ impl<T: 'static> EventLoop<T> {
// Empty the user event buffer
{
while let Ok(event) = self.user_receiver.try_recv() {
app.user_event(&self.event_processor.target, event);
callback(Event::UserEvent(event), &self.event_processor.target);
}
}
@@ -564,24 +562,28 @@ impl<T: 'static> EventLoop<T> {
for window_id in windows {
let window_id = crate::window::WindowId(window_id);
app.window_event(
callback(
Event::WindowEvent { window_id, event: WindowEvent::RedrawRequested },
&self.event_processor.target,
window_id,
WindowEvent::RedrawRequested,
);
}
}
// This is always the last event we dispatch before poll again
app.about_to_wait(&self.event_processor.target);
{
callback(Event::AboutToWait, &self.event_processor.target);
}
}
fn drain_events<A: ApplicationHandler<T>>(&mut self, app: &mut A) {
fn drain_events<F>(&mut self, callback: &mut F)
where
F: FnMut(Event<T>, &RootAEL),
{
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: Event<T>| {
self.event_processor.process_event(&mut xev, |window_target, event| {
if let Event::WindowEvent {
window_id: crate::window::WindowId(wid),
event: WindowEvent::RedrawRequested,
@@ -590,15 +592,7 @@ impl<T: 'static> EventLoop<T> {
let window_target = EventProcessor::window_target(window_target);
window_target.redraw_sender.send(wid).unwrap();
} else {
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."),
}
callback(event, window_target);
}
});
}

View File

@@ -5,6 +5,7 @@ use objc2_app_kit::{NSApplication, NSEvent, NSEventModifierFlags, NSEventType, N
use objc2_foundation::{MainThreadMarker, NSObject};
use super::app_delegate::ApplicationDelegate;
use super::event::flags_contains;
use crate::event::{DeviceEvent, ElementState};
declare_class!(
@@ -31,7 +32,7 @@ declare_class!(
let event_type = unsafe { event.r#type() };
let modifier_flags = unsafe { event.modifierFlags() };
if event_type == NSEventType::KeyUp
&& modifier_flags.contains(NSEventModifierFlags::NSEventModifierFlagCommand)
&& flags_contains(modifier_flags, NSEventModifierFlags::NSEventModifierFlagCommand)
{
if let Some(key_window) = self.keyWindow() {
key_window.sendEvent(event);

View File

@@ -5,7 +5,7 @@ use std::rc::Weak;
use std::sync::{Arc, Mutex};
use std::time::Instant;
use objc2::rc::Retained;
use objc2::rc::Id;
use objc2::runtime::AnyObject;
use objc2::{declare_class, msg_send_id, mutability, ClassType, DeclaredClass};
use objc2_app_kit::{NSApplication, NSApplicationActivationPolicy, NSApplicationDelegate};
@@ -52,8 +52,6 @@ pub(super) struct State {
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.
}
declare_class!(
@@ -73,7 +71,7 @@ declare_class!(
unsafe impl NSObjectProtocol for ApplicationDelegate {}
unsafe impl NSApplicationDelegate for ApplicationDelegate {
// NOTE: This will, globally, only be run once, no matter how many
// Note: This will, globally, only be run once, no matter how many
// `EventLoop`s the user creates.
#[method(applicationDidFinishLaunching:)]
fn did_finish_launching(&self, _sender: Option<&AnyObject>) {
@@ -108,7 +106,7 @@ declare_class!(
// 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`
// 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).
//
@@ -133,7 +131,7 @@ impl ApplicationDelegate {
activation_policy: NSApplicationActivationPolicy,
default_menu: bool,
activate_ignoring_other_apps: bool,
) -> Retained<Self> {
) -> Id<Self> {
let this = mtm.alloc().set_ivars(State {
activation_policy: Policy(activation_policy),
default_menu,
@@ -143,13 +141,13 @@ impl ApplicationDelegate {
unsafe { msg_send_id![super(this), init] }
}
pub fn get(mtm: MainThreadMarker) -> Retained<Self> {
pub fn get(mtm: MainThreadMarker) -> Id<Self> {
let app = NSApplication::sharedApplication(mtm);
let delegate =
unsafe { app.delegate() }.expect("a delegate was not configured on the application");
if delegate.is_kind_of::<Self>() {
// SAFETY: Just checked that the delegate is an instance of `ApplicationDelegate`
unsafe { Retained::cast(delegate) }
unsafe { Id::cast(delegate) }
} else {
panic!("tried to get a delegate that was not the one Winit has registered")
}
@@ -190,10 +188,9 @@ impl ApplicationDelegate {
/// Clears the `running` state and resets the `control_flow` state when an `EventLoop` exits.
///
/// NOTE: that if the `NSApplication` has been launched then that state is preserved,
/// 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);
@@ -248,7 +245,7 @@ impl ApplicationDelegate {
pub fn queue_static_scale_factor_changed_event(
&self,
window: Retained<WinitWindow>,
window: Id<WinitWindow>,
suggested_size: PhysicalSize<u32>,
scale_factor: f64,
) {
@@ -376,11 +373,9 @@ impl ApplicationDelegate {
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 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()),
@@ -426,7 +421,7 @@ pub(crate) enum QueuedEvent {
WindowEvent(WindowId, WindowEvent),
DeviceEvent(DeviceEvent),
ScaleFactorChanged {
window: Retained<WinitWindow>,
window: Id<WinitWindow>,
suggested_size: PhysicalSize<u32>,
scale_factor: f64,
},

View File

@@ -2,7 +2,7 @@ use std::ffi::c_uchar;
use std::slice;
use std::sync::OnceLock;
use objc2::rc::Retained;
use objc2::rc::Id;
use objc2::runtime::Sel;
use objc2::{msg_send_id, sel, ClassType};
use objc2_app_kit::{NSBitmapImageRep, NSCursor, NSDeviceRGBColorSpace, NSImage};
@@ -15,7 +15,7 @@ use crate::cursor::{CursorImage, OnlyCursorImageSource};
use crate::window::CursorIcon;
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct CustomCursor(pub(crate) Retained<NSCursor>);
pub struct CustomCursor(pub(crate) Id<NSCursor>);
// SAFETY: NSCursor is immutable and thread-safe
// TODO(madsmtm): Put this logic in objc2-app-kit itself
@@ -28,7 +28,7 @@ impl CustomCursor {
}
}
pub(crate) fn cursor_from_image(cursor: &CursorImage) -> Retained<NSCursor> {
pub(crate) fn cursor_from_image(cursor: &CursorImage) -> Id<NSCursor> {
let width = cursor.width;
let height = cursor.height;
@@ -60,14 +60,14 @@ pub(crate) fn cursor_from_image(cursor: &CursorImage) -> Retained<NSCursor> {
NSCursor::initWithImage_hotSpot(NSCursor::alloc(), &image, hotspot)
}
pub(crate) fn default_cursor() -> Retained<NSCursor> {
pub(crate) fn default_cursor() -> Id<NSCursor> {
NSCursor::arrowCursor()
}
unsafe fn try_cursor_from_selector(sel: Sel) -> Option<Retained<NSCursor>> {
unsafe fn try_cursor_from_selector(sel: Sel) -> Option<Id<NSCursor>> {
let cls = NSCursor::class();
if cls.responds_to(sel) {
let cursor: Retained<NSCursor> = unsafe { msg_send_id![cls, performSelector: sel] };
let cursor: Id<NSCursor> = unsafe { msg_send_id![cls, performSelector: sel] };
Some(cursor)
} else {
tracing::warn!("cursor `{sel}` appears to be invalid");
@@ -82,7 +82,7 @@ macro_rules! def_undocumented_cursor {
)*} => {$(
$(#[$($m)*])*
#[allow(non_snake_case)]
fn $name() -> Retained<NSCursor> {
fn $name() -> Id<NSCursor> {
unsafe { try_cursor_from_selector(sel!($name)).unwrap_or_else(|| default_cursor()) }
}
)*};
@@ -112,7 +112,7 @@ def_undocumented_cursor!(
// Note that loading `busybutclickable` with this code won't animate
// the frames; instead you'll just get them all in a column.
unsafe fn load_webkit_cursor(name: &NSString) -> Retained<NSCursor> {
unsafe fn load_webkit_cursor(name: &NSString) -> Id<NSCursor> {
// Snatch a cursor from WebKit; They fit the style of the native
// cursors, and will seem completely standard to macOS users.
//
@@ -128,7 +128,7 @@ unsafe fn load_webkit_cursor(name: &NSString) -> Retained<NSCursor> {
// TODO: Handle PLists better
let info_path = cursor_path.stringByAppendingPathComponent(ns_string!("info.plist"));
let info: Retained<NSDictionary<NSObject, NSObject>> = unsafe {
let info: Id<NSDictionary<NSObject, NSObject>> = unsafe {
msg_send_id![
<NSDictionary<NSObject, NSObject>>::class(),
dictionaryWithContentsOfFile: &*info_path,
@@ -155,15 +155,15 @@ unsafe fn load_webkit_cursor(name: &NSString) -> Retained<NSCursor> {
NSCursor::initWithImage_hotSpot(NSCursor::alloc(), &image, hotspot)
}
fn webkit_move() -> Retained<NSCursor> {
fn webkit_move() -> Id<NSCursor> {
unsafe { load_webkit_cursor(ns_string!("move")) }
}
fn webkit_cell() -> Retained<NSCursor> {
fn webkit_cell() -> Id<NSCursor> {
unsafe { load_webkit_cursor(ns_string!("cell")) }
}
pub(crate) fn invisible_cursor() -> Retained<NSCursor> {
pub(crate) fn invisible_cursor() -> Id<NSCursor> {
// 16x16 GIF data for invisible cursor
// You can reproduce this via ImageMagick.
// $ convert -size 16x16 xc:none cursor.gif
@@ -174,7 +174,7 @@ pub(crate) fn invisible_cursor() -> Retained<NSCursor> {
0xa3, 0x9c, 0xb4, 0xda, 0x8b, 0xb3, 0x3e, 0x05, 0x00, 0x3b,
];
fn new_invisible() -> Retained<NSCursor> {
fn new_invisible() -> Id<NSCursor> {
// TODO: Consider using `dataWithBytesNoCopy:`
let data = NSData::with_bytes(CURSOR_BYTES);
let image = NSImage::initWithData(NSImage::alloc(), &data).unwrap();
@@ -187,7 +187,7 @@ pub(crate) fn invisible_cursor() -> Retained<NSCursor> {
CURSOR.get_or_init(|| CustomCursor(new_invisible())).0.clone()
}
pub(crate) fn cursor_from_icon(icon: CursorIcon) -> Retained<NSCursor> {
pub(crate) fn cursor_from_icon(icon: CursorIcon) -> Id<NSCursor> {
match icon {
CursorIcon::Default => default_cursor(),
CursorIcon::Pointer => NSCursor::pointingHandCursor(),

View File

@@ -2,7 +2,7 @@ use std::ffi::c_void;
use core_foundation::base::CFRelease;
use core_foundation::data::{CFDataGetBytePtr, CFDataRef};
use objc2::rc::Retained;
use objc2::rc::Id;
use objc2_app_kit::{NSEvent, NSEventModifierFlags, NSEventSubtype, NSEventType};
use objc2_foundation::{run_on_main, NSPoint};
use smol_str::SmolStr;
@@ -133,8 +133,8 @@ pub(crate) fn create_key_event(
let key_without_modifiers = get_modifierless_char(scancode);
let modifiers = unsafe { ns_event.modifierFlags() };
let has_ctrl = modifiers.contains(NSEventModifierFlags::NSEventModifierFlagControl);
let has_cmd = modifiers.contains(NSEventModifierFlags::NSEventModifierFlagCommand);
let has_ctrl = flags_contains(modifiers, NSEventModifierFlags::NSEventModifierFlagControl);
let has_cmd = flags_contains(modifiers, NSEventModifierFlags::NSEventModifierFlagCommand);
let logical_key = match text_with_all_modifiers.as_ref() {
// Only checking for ctrl and cmd here, not checking for alt because we DO want to
@@ -305,12 +305,16 @@ const NX_DEVICELALTKEYMASK: NSEventModifierFlags = NSEventModifierFlags(0x000000
const NX_DEVICERALTKEYMASK: NSEventModifierFlags = NSEventModifierFlags(0x00000040);
const NX_DEVICERCTLKEYMASK: NSEventModifierFlags = NSEventModifierFlags(0x00002000);
pub(super) fn flags_contains(flags: NSEventModifierFlags, value: NSEventModifierFlags) -> bool {
flags.0 & value.0 == value.0
}
pub(super) fn lalt_pressed(event: &NSEvent) -> bool {
unsafe { event.modifierFlags() }.contains(NX_DEVICELALTKEYMASK)
flags_contains(unsafe { event.modifierFlags() }, NX_DEVICELALTKEYMASK)
}
pub(super) fn ralt_pressed(event: &NSEvent) -> bool {
unsafe { event.modifierFlags() }.contains(NX_DEVICERALTKEYMASK)
flags_contains(unsafe { event.modifierFlags() }, NX_DEVICERALTKEYMASK)
}
pub(super) fn event_mods(event: &NSEvent) -> Modifiers {
@@ -318,33 +322,38 @@ pub(super) fn event_mods(event: &NSEvent) -> Modifiers {
let mut state = ModifiersState::empty();
let mut pressed_mods = ModifiersKeys::empty();
state
.set(ModifiersState::SHIFT, flags.contains(NSEventModifierFlags::NSEventModifierFlagShift));
pressed_mods.set(ModifiersKeys::LSHIFT, flags.contains(NX_DEVICELSHIFTKEYMASK));
pressed_mods.set(ModifiersKeys::RSHIFT, flags.contains(NX_DEVICERSHIFTKEYMASK));
state.set(
ModifiersState::SHIFT,
flags_contains(flags, NSEventModifierFlags::NSEventModifierFlagShift),
);
pressed_mods.set(ModifiersKeys::LSHIFT, flags_contains(flags, NX_DEVICELSHIFTKEYMASK));
pressed_mods.set(ModifiersKeys::RSHIFT, flags_contains(flags, NX_DEVICERSHIFTKEYMASK));
state.set(
ModifiersState::CONTROL,
flags.contains(NSEventModifierFlags::NSEventModifierFlagControl),
flags_contains(flags, NSEventModifierFlags::NSEventModifierFlagControl),
);
pressed_mods.set(ModifiersKeys::LCONTROL, flags.contains(NX_DEVICELCTLKEYMASK));
pressed_mods.set(ModifiersKeys::RCONTROL, flags.contains(NX_DEVICERCTLKEYMASK));
pressed_mods.set(ModifiersKeys::LCONTROL, flags_contains(flags, NX_DEVICELCTLKEYMASK));
pressed_mods.set(ModifiersKeys::RCONTROL, flags_contains(flags, NX_DEVICERCTLKEYMASK));
state.set(ModifiersState::ALT, flags.contains(NSEventModifierFlags::NSEventModifierFlagOption));
pressed_mods.set(ModifiersKeys::LALT, flags.contains(NX_DEVICELALTKEYMASK));
pressed_mods.set(ModifiersKeys::RALT, flags.contains(NX_DEVICERALTKEYMASK));
state.set(
ModifiersState::ALT,
flags_contains(flags, NSEventModifierFlags::NSEventModifierFlagOption),
);
pressed_mods.set(ModifiersKeys::LALT, flags_contains(flags, NX_DEVICELALTKEYMASK));
pressed_mods.set(ModifiersKeys::RALT, flags_contains(flags, NX_DEVICERALTKEYMASK));
state.set(
ModifiersState::SUPER,
flags.contains(NSEventModifierFlags::NSEventModifierFlagCommand),
flags_contains(flags, NSEventModifierFlags::NSEventModifierFlagCommand),
);
pressed_mods.set(ModifiersKeys::LSUPER, flags.contains(NX_DEVICELCMDKEYMASK));
pressed_mods.set(ModifiersKeys::RSUPER, flags.contains(NX_DEVICERCMDKEYMASK));
pressed_mods.set(ModifiersKeys::LSUPER, flags_contains(flags, NX_DEVICELCMDKEYMASK));
pressed_mods.set(ModifiersKeys::RSUPER, flags_contains(flags, NX_DEVICERCMDKEYMASK));
Modifiers { state, pressed_mods }
}
pub(super) fn dummy_event() -> Option<Retained<NSEvent>> {
pub(super) fn dummy_event() -> Option<Id<NSEvent>> {
unsafe {
NSEvent::otherEventWithType_location_modifierFlags_timestamp_windowNumber_context_subtype_data1_data2(
NSEventType::ApplicationDefined,

View File

@@ -14,7 +14,7 @@ use core_foundation::runloop::{
kCFRunLoopCommonModes, CFRunLoopAddSource, CFRunLoopGetMain, CFRunLoopSourceContext,
CFRunLoopSourceCreate, CFRunLoopSourceRef, CFRunLoopSourceSignal, CFRunLoopWakeUp,
};
use objc2::rc::{autoreleasepool, Retained};
use objc2::rc::{autoreleasepool, Id};
use objc2::runtime::ProtocolObject;
use objc2::{msg_send_id, ClassType};
use objc2_app_kit::{NSApplication, NSApplicationActivationPolicy, NSWindow};
@@ -25,7 +25,6 @@ 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::{
@@ -68,21 +67,17 @@ impl PanicInfo {
#[derive(Debug)]
pub struct ActiveEventLoop {
delegate: Retained<ApplicationDelegate>,
delegate: Id<ApplicationDelegate>,
pub(super) mtm: MainThreadMarker,
}
impl ActiveEventLoop {
pub(super) fn new_root(delegate: Retained<ApplicationDelegate>) -> RootWindowTarget {
pub(super) fn new_root(delegate: Id<ApplicationDelegate>) -> RootWindowTarget {
let mtm = MainThreadMarker::from(&*delegate);
let p = Self { delegate, mtm };
RootWindowTarget { p, _marker: PhantomData }
}
pub(super) fn app_delegate(&self) -> &ApplicationDelegate {
&self.delegate
}
pub fn create_custom_cursor(&self, source: CustomCursorSource) -> RootCustomCursor {
RootCustomCursor { inner: CustomCursor::new(source.inner) }
}
@@ -138,7 +133,9 @@ impl ActiveEventLoop {
pub(crate) fn owned_display_handle(&self) -> OwnedDisplayHandle {
OwnedDisplayHandle
}
}
impl ActiveEventLoop {
pub(crate) fn hide_application(&self) {
NSApplication::sharedApplication(self.mtm).hide(None)
}
@@ -156,28 +153,17 @@ impl ActiveEventLoop {
}
}
fn map_user_event<T: 'static, A: ApplicationHandler<T>>(
app: &mut A,
fn map_user_event<T: 'static>(
mut handler: impl FnMut(Event<T>, &RootWindowTarget),
receiver: Rc<mpsc::Receiver<T>>,
) -> 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(_) => {
) -> impl FnMut(Event<HandlePendingUserEvents>, &RootWindowTarget) {
move |event, window_target| match event.map_nonuser_event() {
Ok(event) => (handler)(event, window_target),
Err(_) => {
for event in receiver.try_iter() {
app.user_event(window_target, event);
(handler)(Event::UserEvent(event), window_target);
}
},
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),
}
}
@@ -186,12 +172,12 @@ pub struct EventLoop<T: 'static> {
///
/// We intentionally don't store `WinitApplication` since we want to have
/// the possibility of swapping that out at some point.
app: Retained<NSApplication>,
app: Id<NSApplication>,
/// The application delegate that we've registered.
///
/// The delegate is only weakly referenced by NSApplication, so we must
/// keep it around here as well.
delegate: Retained<ApplicationDelegate>,
delegate: Id<ApplicationDelegate>,
// Event sender and receiver, used for EventLoopProxy.
sender: mpsc::Sender<T>,
@@ -225,7 +211,7 @@ impl<T> EventLoop<T> {
let mtm = MainThreadMarker::new()
.expect("on macOS, `EventLoop` must be created on the main thread!");
let app: Retained<NSApplication> =
let app: Id<NSApplication> =
unsafe { msg_send_id![WinitApplication::class(), sharedApplication] };
if !app.is_kind_of::<WinitApplication>() {
@@ -272,19 +258,22 @@ impl<T> EventLoop<T> {
&self.window_target
}
pub fn run_app<A: ApplicationHandler<T>>(mut self, app: &mut A) -> Result<(), EventLoopError> {
self.run_app_on_demand(app)
pub fn run<F>(mut self, handler: F) -> Result<(), EventLoopError>
where
F: FnMut(Event<T>, &RootWindowTarget),
{
self.run_on_demand(handler)
}
// 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_app_on_demand<A: ApplicationHandler<T>>(
&mut self,
app: &mut A,
) -> Result<(), EventLoopError> {
let handler = map_user_event(app, self.receiver.clone());
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());
self.delegate.set_event_handler(handler, || {
autoreleasepool(|_| {
@@ -319,12 +308,11 @@ impl<T> EventLoop<T> {
Ok(())
}
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());
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());
self.delegate.set_event_handler(handler, || {
autoreleasepool(|_| {

View File

@@ -1,4 +1,4 @@
use objc2::rc::Retained;
use objc2::rc::Id;
use objc2::runtime::Sel;
use objc2::sel;
use objc2_app_kit::{NSApplication, NSEventModifierFlags, NSMenu, NSMenuItem};
@@ -48,10 +48,10 @@ pub fn initialize(app: &NSApplication) {
Some(sel!(hideOtherApplications:)),
Some(KeyEquivalent {
key: ns_string!("h"),
masks: Some(
NSEventModifierFlags::NSEventModifierFlagOption
| NSEventModifierFlags::NSEventModifierFlagCommand,
),
masks: Some(NSEventModifierFlags(
NSEventModifierFlags::NSEventModifierFlagOption.0
| NSEventModifierFlags::NSEventModifierFlagCommand.0,
)),
}),
);
@@ -91,7 +91,7 @@ fn menu_item(
title: &NSString,
selector: Option<Sel>,
key_equivalent: Option<KeyEquivalent<'_>>,
) -> Retained<NSMenuItem> {
) -> Id<NSMenuItem> {
let (key, masks) = match key_equivalent {
Some(ke) => (ke.key, ke.masks),
None => (ns_string!(""), None),

View File

@@ -9,7 +9,7 @@ use core_foundation::string::CFString;
use core_graphics::display::{
CGDirectDisplayID, CGDisplay, CGDisplayBounds, CGDisplayCopyDisplayMode,
};
use objc2::rc::Retained;
use objc2::rc::Id;
use objc2::runtime::AnyObject;
use objc2_app_kit::NSScreen;
use objc2_foundation::{ns_string, run_on_main, MainThreadMarker, NSNumber, NSPoint, NSRect};
@@ -295,7 +295,7 @@ impl MonitorHandle {
}
}
pub(crate) fn ns_screen(&self, mtm: MainThreadMarker) -> Option<Retained<NSScreen>> {
pub(crate) fn ns_screen(&self, mtm: MainThreadMarker) -> Option<Id<NSScreen>> {
let uuid = unsafe { ffi::CGDisplayCreateUUIDFromDisplayID(self.0) };
NSScreen::screens(mtm).into_iter().find(|screen| {
let other_native_id = get_display_id(screen);

View File

@@ -1,5 +1,8 @@
use objc2_foundation::{NSNotFound, NSRange, NSUInteger};
use tracing::trace;
pub static EMPTY_RANGE: NSRange = NSRange { location: NSNotFound as NSUInteger, length: 0 };
macro_rules! trace_scope {
($s:literal) => {
let _crate = $crate::platform_impl::platform::util::TraceGuard::new(module_path!(), $s);

View File

@@ -3,17 +3,19 @@ use std::cell::{Cell, RefCell};
use std::collections::{HashMap, VecDeque};
use std::ptr;
use objc2::rc::{Retained, WeakId};
use objc2::rc::{Id, WeakId};
use objc2::runtime::{AnyObject, Sel};
use objc2::{declare_class, msg_send_id, mutability, sel, ClassType, DeclaredClass};
use objc2::{
class, declare_class, msg_send, msg_send_id, mutability, sel, ClassType, DeclaredClass,
};
use objc2_app_kit::{
NSApplication, NSCursor, NSEvent, NSEventPhase, NSResponder, NSTextInputClient,
NSTrackingRectTag, NSView, NSViewFrameDidChangeNotification,
NSTrackingRectTag, NSView,
};
use objc2_foundation::{
MainThreadMarker, NSArray, NSAttributedString, NSAttributedStringKey, NSCopying,
NSMutableAttributedString, NSNotFound, NSNotificationCenter, NSObject, NSObjectProtocol,
NSPoint, NSRange, NSRect, NSSize, NSString, NSUInteger,
NSMutableAttributedString, NSObject, NSObjectProtocol, NSPoint, NSRange, NSRect, NSSize,
NSString, NSUInteger,
};
use super::app_delegate::ApplicationDelegate;
@@ -23,7 +25,7 @@ use super::event::{
scancode_to_physicalkey,
};
use super::window::WinitWindow;
use super::DEVICE_ID;
use super::{util, DEVICE_ID};
use crate::dpi::{LogicalPosition, LogicalSize};
use crate::event::{
DeviceEvent, ElementState, Ime, Modifiers, MouseButton, MouseScrollDelta, TouchPhase,
@@ -35,7 +37,7 @@ use crate::platform::macos::OptionAsAlt;
#[derive(Debug)]
struct CursorState {
visible: bool,
cursor: Retained<NSCursor>,
cursor: Id<NSCursor>,
}
impl Default for CursorState {
@@ -108,11 +110,8 @@ fn get_left_modifier_code(key: &Key) -> KeyCode {
}
}
#[derive(Debug)]
#[derive(Debug, Default)]
pub struct ViewState {
/// Strong reference to the global application state.
app_delegate: Retained<ApplicationDelegate>,
cursor_state: RefCell<CursorState>,
ime_position: Cell<NSPoint>,
ime_size: Cell<NSSize>,
@@ -131,7 +130,7 @@ pub struct ViewState {
/// to the application, even during IME
forward_key_to_app: Cell<bool>,
marked_text: RefCell<Retained<NSMutableAttributedString>>,
marked_text: RefCell<Id<NSMutableAttributedString>>,
accepts_first_mouse: bool,
// Weak reference because the window keeps a strong reference to the view
@@ -200,15 +199,19 @@ declare_class!(
}
#[method(drawRect:)]
fn draw_rect(&self, _rect: NSRect) {
fn draw_rect(&self, rect: NSRect) {
trace_scope!("drawRect:");
// It's a workaround for https://github.com/rust-windowing/winit/issues/2640, don't replace with `self.window_id()`.
if let Some(window) = self.ivars()._ns_window.load() {
self.ivars().app_delegate.handle_redraw(window.id());
let app_delegate = ApplicationDelegate::get(MainThreadMarker::from(self));
app_delegate.handle_redraw(window.id());
}
// This is a direct subclass of NSView, no need to call superclass' drawRect:
#[allow(clippy::let_unit_value)]
unsafe {
let _: () = msg_send![super(self), drawRect: rect];
}
}
#[method(acceptsFirstResponder)]
@@ -221,7 +224,7 @@ declare_class!(
// IMKInputSession [0x7fc573576ff0 presentFunctionRowItemTextInputViewWithEndpoint:completionHandler:] : [self textInputContext]=0x7fc573558e10 *NO* NSRemoteViewController to client, NSError=Error Domain=NSCocoaErrorDomain Code=4099 "The connection from pid 0 was invalidated from this process." UserInfo={NSDebugDescription=The connection from pid 0 was invalidated from this process.}, com.apple.inputmethod.EmojiFunctionRowItem
// TODO: Add an API extension for using `NSTouchBar`
#[method_id(touchBar)]
fn touch_bar(&self) -> Option<Retained<NSObject>> {
fn touch_bar(&self) -> Option<Id<NSObject>> {
trace_scope!("touchBar");
None
}
@@ -254,16 +257,14 @@ declare_class!(
if length > 0 {
NSRange::new(0, length)
} else {
// Documented to return `{NSNotFound, 0}` if there is no marked range.
NSRange::new(NSNotFound as NSUInteger, 0)
util::EMPTY_RANGE
}
}
#[method(selectedRange)]
fn selected_range(&self) -> NSRange {
trace_scope!("selectedRange");
// Documented to return `{NSNotFound, 0}` if there is no selection.
NSRange::new(NSNotFound as NSUInteger, 0)
util::EMPTY_RANGE
}
#[method(setMarkedText:selectedRange:replacementRange:)]
@@ -340,7 +341,7 @@ declare_class!(
}
#[method_id(validAttributesForMarkedText)]
fn valid_attributes_for_marked_text(&self) -> Retained<NSArray<NSAttributedStringKey>> {
fn valid_attributes_for_marked_text(&self) -> Id<NSArray<NSAttributedStringKey>> {
trace_scope!("validAttributesForMarkedText");
NSArray::new()
}
@@ -350,7 +351,7 @@ declare_class!(
&self,
_range: NSRange,
_actual_range: *mut NSRange,
) -> Option<Retained<NSAttributedString>> {
) -> Option<Id<NSAttributedString>> {
trace_scope!("attributedSubstringForProposedRange:actualRange:");
None
}
@@ -772,40 +773,34 @@ declare_class!(
impl WinitView {
pub(super) fn new(
app_delegate: &ApplicationDelegate,
window: &WinitWindow,
accepts_first_mouse: bool,
option_as_alt: OptionAsAlt,
) -> Retained<Self> {
) -> Id<Self> {
let mtm = MainThreadMarker::from(window);
let this = mtm.alloc().set_ivars(ViewState {
app_delegate: app_delegate.retain(),
cursor_state: Default::default(),
ime_position: Default::default(),
ime_size: Default::default(),
modifiers: Default::default(),
phys_modifiers: Default::default(),
tracking_rect: Default::default(),
ime_state: Default::default(),
input_source: Default::default(),
ime_allowed: Default::default(),
forward_key_to_app: Default::default(),
marked_text: Default::default(),
accepts_first_mouse,
_ns_window: WeakId::new(&window.retain()),
option_as_alt: Cell::new(option_as_alt),
..Default::default()
});
let this: Retained<Self> = unsafe { msg_send_id![super(this), init] };
let this: Id<Self> = unsafe { msg_send_id![super(this), init] };
this.setPostsFrameChangedNotifications(true);
let notification_center = unsafe { NSNotificationCenter::defaultCenter() };
let notification_center: &AnyObject =
unsafe { msg_send![class!(NSNotificationCenter), defaultCenter] };
// About frame change
let frame_did_change_notification_name =
NSString::from_str("NSViewFrameDidChangeNotification");
#[allow(clippy::let_unit_value)]
unsafe {
notification_center.addObserver_selector_name_object(
&this,
sel!(frameDidChange:),
Some(NSViewFrameDidChangeNotification),
Some(&this),
)
let _: () = msg_send![
notification_center,
addObserver: &*this,
selector: sel!(frameDidChange:),
name: &*frame_did_change_notification_name,
object: &*this,
];
}
*this.ivars().input_source.borrow_mut() = this.current_input_source();
@@ -813,7 +808,7 @@ impl WinitView {
this
}
fn window(&self) -> Retained<WinitWindow> {
fn window(&self) -> Id<WinitWindow> {
// TODO: Simply use `window` property on `NSView`.
// That only returns a window _after_ the view has been attached though!
// (which is incompatible with `frameDidChange:`)
@@ -823,11 +818,13 @@ impl WinitView {
}
fn queue_event(&self, event: WindowEvent) {
self.ivars().app_delegate.queue_window_event(self.window().id(), event);
let app_delegate = ApplicationDelegate::get(MainThreadMarker::from(self));
app_delegate.queue_window_event(self.window().id(), event);
}
fn queue_device_event(&self, event: DeviceEvent) {
self.ivars().app_delegate.queue_device_event(event);
let app_delegate = ApplicationDelegate::get(MainThreadMarker::from(self));
app_delegate.queue_device_event(event);
}
fn scale_factor(&self) -> f64 {
@@ -846,11 +843,11 @@ impl WinitView {
.unwrap_or_default()
}
pub(super) fn cursor_icon(&self) -> Retained<NSCursor> {
pub(super) fn cursor_icon(&self) -> Id<NSCursor> {
self.ivars().cursor_state.borrow().cursor.clone()
}
pub(super) fn set_cursor_icon(&self, icon: Retained<NSCursor>) {
pub(super) fn set_cursor_icon(&self, icon: Id<NSCursor>) {
let mut cursor_state = self.ivars().cursor_state.borrow_mut();
cursor_state.cursor = icon;
}
@@ -1083,7 +1080,7 @@ fn mouse_button(event: &NSEvent) -> MouseButton {
// NOTE: to get option as alt working we need to rewrite events
// we're getting from the operating system, which makes it
// impossible to provide such events as extra in `KeyEvent`.
fn replace_event(event: &NSEvent, option_as_alt: OptionAsAlt) -> Retained<NSEvent> {
fn replace_event(event: &NSEvent, option_as_alt: OptionAsAlt) -> Id<NSEvent> {
let ev_mods = event_mods(event).state;
let ignore_alt_characters = match option_as_alt {
OptionAsAlt::OnlyLeft if lalt_pressed(event) => true,

View File

@@ -1,6 +1,6 @@
#![allow(clippy::unnecessary_cast)]
use objc2::rc::{autoreleasepool, Retained};
use objc2::rc::{autoreleasepool, Id};
use objc2::{declare_class, mutability, ClassType, DeclaredClass};
use objc2_app_kit::{NSResponder, NSWindow};
use objc2_foundation::{MainThreadBound, MainThreadMarker, NSObject};
@@ -11,9 +11,9 @@ use crate::error::OsError as RootOsError;
use crate::window::WindowAttributes;
pub(crate) struct Window {
window: MainThreadBound<Retained<WinitWindow>>,
window: MainThreadBound<Id<WinitWindow>>,
/// The window only keeps a weak reference to this, so we must keep it around here.
delegate: MainThreadBound<Retained<WindowDelegate>>,
delegate: MainThreadBound<Id<WindowDelegate>>,
}
impl Drop for Window {
@@ -28,9 +28,7 @@ impl Window {
attributes: WindowAttributes,
) -> Result<Self, RootOsError> {
let mtm = window_target.mtm;
let delegate = autoreleasepool(|_| {
WindowDelegate::new(window_target.app_delegate(), attributes, mtm)
})?;
let delegate = autoreleasepool(|_| WindowDelegate::new(attributes, mtm))?;
Ok(Window {
window: MainThreadBound::new(delegate.window().retain(), mtm),
delegate: MainThreadBound::new(delegate, mtm),

View File

@@ -1,12 +1,15 @@
#![allow(clippy::unnecessary_cast)]
use std::cell::{Cell, RefCell};
use std::collections::VecDeque;
use std::ptr;
use core_graphics::display::{CGDisplay, CGPoint};
use monitor::VideoModeHandle;
use objc2::rc::{autoreleasepool, Retained};
use objc2::rc::{autoreleasepool, Id};
use objc2::runtime::{AnyObject, ProtocolObject};
use objc2::{declare_class, msg_send_id, mutability, sel, ClassType, DeclaredClass};
use objc2::{
class, declare_class, msg_send, msg_send_id, mutability, sel, ClassType, DeclaredClass,
};
use objc2_app_kit::{
NSAppKitVersionNumber, NSAppKitVersionNumber10_12, NSAppearance, NSApplication,
NSApplicationPresentationOptions, NSBackingStoreType, NSDraggingDestination,
@@ -16,9 +19,8 @@ use objc2_app_kit::{
NSWindowTabbingMode, NSWindowTitleVisibility,
};
use objc2_foundation::{
ns_string, CGFloat, MainThreadMarker, NSArray, NSCopying, NSDistributedNotificationCenter,
NSObject, NSObjectNSDelayedPerforming, NSObjectNSThreadPerformAdditions, NSObjectProtocol,
NSPoint, NSRect, NSSize, NSString,
CGFloat, MainThreadMarker, NSArray, NSCopying, NSObject, NSObjectProtocol, NSPoint, NSRect,
NSSize, NSString,
};
use super::app_delegate::ApplicationDelegate;
@@ -72,10 +74,7 @@ impl Default for PlatformSpecificWindowAttributes {
#[derive(Debug)]
pub(crate) struct State {
/// Strong reference to the global application state.
app_delegate: Retained<ApplicationDelegate>,
window: Retained<WinitWindow>,
window: Id<WinitWindow>,
current_theme: Cell<Option<Theme>>,
@@ -231,7 +230,7 @@ declare_class!(
None => {
let current_monitor = self.current_monitor_inner();
*fullscreen = Some(Fullscreen::Borderless(current_monitor));
},
}
}
self.ivars().in_fullscreen_transition.set(true);
}
@@ -262,9 +261,11 @@ declare_class!(
let mut options = proposed_options;
let fullscreen = self.ivars().fullscreen.borrow();
if let Some(Fullscreen::Exclusive(_)) = &*fullscreen {
options = NSApplicationPresentationOptions::NSApplicationPresentationFullScreen
| NSApplicationPresentationOptions::NSApplicationPresentationHideDock
| NSApplicationPresentationOptions::NSApplicationPresentationHideMenuBar;
options = NSApplicationPresentationOptions(
NSApplicationPresentationOptions::NSApplicationPresentationFullScreen.0
| NSApplicationPresentationOptions::NSApplicationPresentationHideDock.0
| NSApplicationPresentationOptions::NSApplicationPresentationHideMenuBar.0,
);
}
options
@@ -315,12 +316,14 @@ declare_class!(
self.ivars().in_fullscreen_transition.set(false);
self.ivars().target_fullscreen.replace(None);
if self.ivars().initial_fullscreen.get() {
#[allow(clippy::let_unit_value)]
unsafe {
self.window().performSelector_withObject_afterDelay(
sel!(toggleFullScreen:),
None,
0.5,
)
let _: () = msg_send![
self.window(),
performSelector: sel!(toggleFullScreen:),
withObject: ptr::null::<AnyObject>(),
afterDelay: 0.5,
];
};
} else {
self.restore_state_from_fullscreen();
@@ -331,7 +334,8 @@ declare_class!(
#[method(windowDidChangeOcclusionState:)]
fn window_did_change_occlusion_state(&self, _: Option<&AnyObject>) {
trace_scope!("windowDidChangeOcclusionState:");
let visible = self.window().occlusionState().contains(NSWindowOcclusionState::Visible);
let visible = self.window().occlusionState().0 & NSWindowOcclusionState::Visible.0
== NSWindowOcclusionState::Visible.0;
self.queue_event(WindowEvent::Occluded(!visible));
}
@@ -355,9 +359,11 @@ declare_class!(
use std::path::PathBuf;
let pb: Retained<NSPasteboard> = unsafe { msg_send_id![sender, draggingPasteboard] };
let filenames = pb.propertyListForType(unsafe { NSFilenamesPboardType }).unwrap();
let filenames: Retained<NSArray<NSString>> = unsafe { Retained::cast(filenames) };
let pb: Id<NSPasteboard> = unsafe { msg_send_id![sender, draggingPasteboard] };
let filenames = pb
.propertyListForType(unsafe { NSFilenamesPboardType })
.unwrap();
let filenames: Id<NSArray<NSString>> = unsafe { Id::cast(filenames) };
filenames.into_iter().for_each(|file| {
let path = PathBuf::from(file.to_string());
@@ -381,9 +387,11 @@ declare_class!(
use std::path::PathBuf;
let pb: Retained<NSPasteboard> = unsafe { msg_send_id![sender, draggingPasteboard] };
let filenames = pb.propertyListForType(unsafe { NSFilenamesPboardType }).unwrap();
let filenames: Retained<NSArray<NSString>> = unsafe { Retained::cast(filenames) };
let pb: Id<NSPasteboard> = unsafe { msg_send_id![sender, draggingPasteboard] };
let filenames = pb
.propertyListForType(unsafe { NSFilenamesPboardType })
.unwrap();
let filenames: Id<NSArray<NSString>> = unsafe { Id::cast(filenames) };
filenames.into_iter().for_each(|file| {
let path = PathBuf::from(file.to_string());
@@ -410,15 +418,16 @@ declare_class!(
unsafe impl WindowDelegate {
// Observe theme change
#[method(effectiveAppearanceDidChange:)]
fn effective_appearance_did_change(&self, sender: Option<&AnyObject>) {
fn effective_appearance_did_change(this: *mut Self, sender: Option<&AnyObject>) {
trace_scope!("effectiveAppearanceDidChange:");
unsafe {
self.performSelectorOnMainThread_withObject_waitUntilDone(
sel!(effectiveAppearanceDidChangedOnMainThread:),
sender,
false,
)
};
msg_send![
this,
performSelectorOnMainThread: sel!(effectiveAppearanceDidChangedOnMainThread:),
withObject: sender,
waitUntilDone: false,
]
}
}
#[method(effectiveAppearanceDidChangedOnMainThread:)]
@@ -433,11 +442,7 @@ declare_class!(
}
);
fn new_window(
app_delegate: &ApplicationDelegate,
attrs: &WindowAttributes,
mtm: MainThreadMarker,
) -> Option<Retained<WinitWindow>> {
fn new_window(attrs: &WindowAttributes, mtm: MainThreadMarker) -> Option<Id<WinitWindow>> {
autoreleasepool(|_| {
let screen = match attrs.fullscreen.clone().map(Into::into) {
Some(Fullscreen::Borderless(Some(monitor)))
@@ -482,38 +487,38 @@ fn new_window(
// if decorations is set to false, ignore pl_attrs
//
// if the titlebar is hidden, ignore other pl_attrs
NSWindowStyleMask::Borderless
| NSWindowStyleMask::Resizable
| NSWindowStyleMask::Miniaturizable
NSWindowStyleMask::Borderless.0
| NSWindowStyleMask::Resizable.0
| NSWindowStyleMask::Miniaturizable.0
} else {
// default case, resizable window with titlebar and titlebar buttons
NSWindowStyleMask::Closable
| NSWindowStyleMask::Miniaturizable
| NSWindowStyleMask::Resizable
| NSWindowStyleMask::Titled
NSWindowStyleMask::Closable.0
| NSWindowStyleMask::Miniaturizable.0
| NSWindowStyleMask::Resizable.0
| NSWindowStyleMask::Titled.0
};
if !attrs.resizable {
masks &= !NSWindowStyleMask::Resizable;
masks &= !NSWindowStyleMask::Resizable.0;
}
if !attrs.enabled_buttons.contains(WindowButtons::MINIMIZE) {
masks &= !NSWindowStyleMask::Miniaturizable;
masks &= !NSWindowStyleMask::Miniaturizable.0;
}
if !attrs.enabled_buttons.contains(WindowButtons::CLOSE) {
masks &= !NSWindowStyleMask::Closable;
masks &= !NSWindowStyleMask::Closable.0;
}
if attrs.platform_specific.fullsize_content_view {
masks |= NSWindowStyleMask::FullSizeContentView;
masks |= NSWindowStyleMask::FullSizeContentView.0;
}
let window: Option<Retained<WinitWindow>> = unsafe {
let window: Option<Id<WinitWindow>> = unsafe {
msg_send_id![
super(mtm.alloc().set_ivars(())),
initWithContentRect: frame,
styleMask: masks,
styleMask: NSWindowStyleMask(masks),
backing: NSBackingStoreType::NSBackingStoreBuffered,
defer: false,
]
@@ -574,7 +579,6 @@ fn new_window(
}
let view = WinitView::new(
app_delegate,
&window,
attrs.platform_specific.accepts_first_mouse,
attrs.platform_specific.option_as_alt,
@@ -614,12 +618,8 @@ fn new_window(
}
impl WindowDelegate {
pub(super) fn new(
app_delegate: &ApplicationDelegate,
attrs: WindowAttributes,
mtm: MainThreadMarker,
) -> Result<Retained<Self>, RootOsError> {
let window = new_window(app_delegate, &attrs, mtm)
pub fn new(attrs: WindowAttributes, mtm: MainThreadMarker) -> Result<Id<Self>, RootOsError> {
let window = new_window(&attrs, mtm)
.ok_or_else(|| os_error!(OsError::CreationError("couldn't create `NSWindow`")))?;
#[cfg(feature = "rwh_06")]
@@ -627,8 +627,8 @@ impl WindowDelegate {
Some(rwh_06::RawWindowHandle::AppKit(handle)) => {
// SAFETY: Caller ensures the pointer is valid or NULL
// Unwrap is fine, since the pointer comes from `NonNull`.
let parent_view: Retained<NSView> =
unsafe { Retained::retain(handle.ns_view.as_ptr().cast()) }.unwrap();
let parent_view: Id<NSView> =
unsafe { Id::retain(handle.ns_view.as_ptr().cast()) }.unwrap();
let parent = parent_view.window().ok_or_else(|| {
os_error!(OsError::CreationError("parent view should be installed in a window"))
})?;
@@ -660,7 +660,6 @@ impl WindowDelegate {
};
let delegate = mtm.alloc().set_ivars(State {
app_delegate: app_delegate.retain(),
window: window.retain(),
current_theme: Cell::new(current_theme),
previous_position: Cell::new(None),
@@ -678,7 +677,7 @@ impl WindowDelegate {
is_simple_fullscreen: Cell::new(false),
saved_style: Cell::new(None),
});
let delegate: Retained<WindowDelegate> = unsafe { msg_send_id![super(delegate), init] };
let delegate: Id<WindowDelegate> = unsafe { msg_send_id![super(delegate), init] };
if scale_factor != 1.0 {
delegate.queue_static_scale_factor_changed_event();
@@ -686,14 +685,17 @@ impl WindowDelegate {
window.setDelegate(Some(ProtocolObject::from_ref(&*delegate)));
// Enable theme change event
let notification_center = unsafe { NSDistributedNotificationCenter::defaultCenter() };
unsafe {
notification_center.addObserver_selector_name_object(
&delegate,
sel!(effectiveAppearanceDidChange:),
Some(ns_string!("AppleInterfaceThemeChangedNotification")),
None,
)
let notification_center: Id<AnyObject> =
unsafe { msg_send_id![class!(NSDistributedNotificationCenter), defaultCenter] };
let notification_name = NSString::from_str("AppleInterfaceThemeChangedNotification");
let _: () = unsafe {
msg_send![
&notification_center,
addObserver: &*delegate,
selector: sel!(effectiveAppearanceDidChange:),
name: &*notification_name,
object: ptr::null::<AnyObject>(),
]
};
if attrs.blur {
@@ -738,9 +740,9 @@ impl WindowDelegate {
}
#[track_caller]
pub(super) fn view(&self) -> Retained<WinitView> {
pub(super) fn view(&self) -> Id<WinitView> {
// SAFETY: The view inside WinitWindow is always `WinitView`
unsafe { Retained::cast(self.window().contentView().unwrap()) }
unsafe { Id::cast(self.window().contentView().unwrap()) }
}
#[track_caller]
@@ -754,7 +756,8 @@ impl WindowDelegate {
}
pub(crate) fn queue_event(&self, event: WindowEvent) {
self.ivars().app_delegate.queue_window_event(self.window().id(), event);
let app_delegate = ApplicationDelegate::get(MainThreadMarker::from(self));
app_delegate.queue_window_event(self.window().id(), event);
}
fn queue_static_scale_factor_changed_event(&self) {
@@ -767,7 +770,8 @@ impl WindowDelegate {
let content_size = self.window().contentRectForFrameRect(self.window().frame()).size;
let content_size = LogicalSize::new(content_size.width, content_size.height);
self.ivars().app_delegate.queue_static_scale_factor_changed_event(
let app_delegate = ApplicationDelegate::get(MainThreadMarker::from(self));
app_delegate.queue_static_scale_factor_changed_event(
self.window().retain(),
content_size.to_physical(scale_factor),
scale_factor,
@@ -829,7 +833,8 @@ impl WindowDelegate {
}
pub fn request_redraw(&self) {
self.ivars().app_delegate.queue_redraw(self.window().id());
let app_delegate = ApplicationDelegate::get(MainThreadMarker::from(self));
app_delegate.queue_redraw(self.window().id());
}
#[inline]
@@ -953,13 +958,13 @@ impl WindowDelegate {
self.ivars().resizable.set(resizable);
let fullscreen = self.ivars().fullscreen.borrow().is_some();
if !fullscreen {
let mut mask = self.window().styleMask();
let mut mask = self.window().styleMask().0;
if resizable {
mask |= NSWindowStyleMask::Resizable;
mask |= NSWindowStyleMask::Resizable.0;
} else {
mask &= !NSWindowStyleMask::Resizable;
mask &= !NSWindowStyleMask::Resizable.0;
}
self.set_style_mask(mask);
self.set_style_mask(NSWindowStyleMask(mask));
}
// Otherwise, we don't change the mask until we exit fullscreen.
}
@@ -971,23 +976,23 @@ impl WindowDelegate {
#[inline]
pub fn set_enabled_buttons(&self, buttons: WindowButtons) {
let mut mask = self.window().styleMask();
let mut mask = self.window().styleMask().0;
if buttons.contains(WindowButtons::CLOSE) {
mask |= NSWindowStyleMask::Closable;
mask |= NSWindowStyleMask::Closable.0;
} else {
mask &= !NSWindowStyleMask::Closable;
mask &= !NSWindowStyleMask::Closable.0;
}
if buttons.contains(WindowButtons::MINIMIZE) {
mask |= NSWindowStyleMask::Miniaturizable;
mask |= NSWindowStyleMask::Miniaturizable.0;
} else {
mask &= !NSWindowStyleMask::Miniaturizable;
mask &= !NSWindowStyleMask::Miniaturizable.0;
}
// This must happen before the button's "enabled" status has been set,
// hence we do it synchronously.
self.set_style_mask(mask);
self.set_style_mask(NSWindowStyleMask(mask));
// We edit the button directly instead of using `NSResizableWindowMask`,
// since that mask also affect the resizability of the window (which is
@@ -1108,8 +1113,9 @@ impl WindowDelegate {
// we make it resizable temporarily.
let curr_mask = self.window().styleMask();
let required = NSWindowStyleMask::Titled | NSWindowStyleMask::Resizable;
let needs_temp_mask = !curr_mask.contains(required);
let required =
NSWindowStyleMask(NSWindowStyleMask::Titled.0 | NSWindowStyleMask::Resizable.0);
let needs_temp_mask = !mask_contains(curr_mask, required);
if needs_temp_mask {
self.set_style_mask(required);
}
@@ -1126,12 +1132,12 @@ impl WindowDelegate {
fn saved_style(&self) -> NSWindowStyleMask {
let base_mask =
self.ivars().saved_style.take().unwrap_or_else(|| self.window().styleMask());
if self.ivars().resizable.get() {
base_mask | NSWindowStyleMask::Resizable
self.ivars().saved_style.take().unwrap_or_else(|| self.window().styleMask()).0;
NSWindowStyleMask(if self.ivars().resizable.get() {
base_mask | NSWindowStyleMask::Resizable.0
} else {
base_mask & !NSWindowStyleMask::Resizable
}
base_mask & !NSWindowStyleMask::Resizable.0
})
}
/// This is called when the window is exiting fullscreen, whether by the
@@ -1186,7 +1192,7 @@ impl WindowDelegate {
return;
}
if self.window().styleMask().contains(NSWindowStyleMask::Resizable) {
if mask_contains(self.window().styleMask(), NSWindowStyleMask::Resizable) {
// Just use the native zoom if resizable
self.window().zoom(None);
} else {
@@ -1338,8 +1344,9 @@ impl WindowDelegate {
// set a normal style temporarily. The previous state will be
// restored in `WindowDelegate::window_did_exit_fullscreen`.
let curr_mask = self.window().styleMask();
let required = NSWindowStyleMask::Titled | NSWindowStyleMask::Resizable;
if !curr_mask.contains(required) {
let required =
NSWindowStyleMask(NSWindowStyleMask::Titled.0 | NSWindowStyleMask::Resizable.0);
if !mask_contains(curr_mask, required) {
self.set_style_mask(required);
self.ivars().saved_style.set(Some(curr_mask));
}
@@ -1370,10 +1377,11 @@ impl WindowDelegate {
// delegate in `window:willUseFullScreenPresentationOptions:`.
self.ivars().save_presentation_opts.set(Some(app.presentationOptions()));
let presentation_options =
NSApplicationPresentationOptions::NSApplicationPresentationFullScreen
| NSApplicationPresentationOptions::NSApplicationPresentationHideDock
| NSApplicationPresentationOptions::NSApplicationPresentationHideMenuBar;
let presentation_options = NSApplicationPresentationOptions(
NSApplicationPresentationOptions::NSApplicationPresentationFullScreen.0
| NSApplicationPresentationOptions::NSApplicationPresentationHideDock.0
| NSApplicationPresentationOptions::NSApplicationPresentationHideMenuBar.0,
);
app.setPresentationOptions(presentation_options);
let window_level = unsafe { ffi::CGShieldingWindowLevel() } as NSWindowLevel + 1;
@@ -1381,9 +1389,9 @@ impl WindowDelegate {
},
(Some(Fullscreen::Exclusive(ref video_mode)), Some(Fullscreen::Borderless(_))) => {
let presentation_options = self.ivars().save_presentation_opts.get().unwrap_or(
NSApplicationPresentationOptions::NSApplicationPresentationFullScreen
| NSApplicationPresentationOptions::NSApplicationPresentationAutoHideDock
| NSApplicationPresentationOptions::NSApplicationPresentationAutoHideMenuBar
NSApplicationPresentationOptions(NSApplicationPresentationOptions::NSApplicationPresentationFullScreen.0
| NSApplicationPresentationOptions::NSApplicationPresentationAutoHideDock.0
| NSApplicationPresentationOptions::NSApplicationPresentationAutoHideMenuBar.0),
);
app.setPresentationOptions(presentation_options);
@@ -1422,19 +1430,19 @@ impl WindowDelegate {
let new_mask = {
let mut new_mask = if decorations {
NSWindowStyleMask::Closable
| NSWindowStyleMask::Miniaturizable
| NSWindowStyleMask::Resizable
| NSWindowStyleMask::Titled
NSWindowStyleMask::Closable.0
| NSWindowStyleMask::Miniaturizable.0
| NSWindowStyleMask::Resizable.0
| NSWindowStyleMask::Titled.0
} else {
NSWindowStyleMask::Borderless | NSWindowStyleMask::Resizable
NSWindowStyleMask::Borderless.0 | NSWindowStyleMask::Resizable.0
};
if !resizable {
new_mask &= !NSWindowStyleMask::Resizable;
new_mask &= !NSWindowStyleMask::Resizable.0;
}
new_mask
};
self.set_style_mask(new_mask);
self.set_style_mask(NSWindowStyleMask(new_mask));
}
#[inline]
@@ -1537,7 +1545,7 @@ impl WindowDelegate {
pub fn raw_window_handle_rwh_04(&self) -> rwh_04::RawWindowHandle {
let mut window_handle = rwh_04::AppKitHandle::empty();
window_handle.ns_window = self.window() as *const WinitWindow as *mut _;
window_handle.ns_view = Retained::as_ptr(&self.contentView().unwrap()) as *mut _;
window_handle.ns_view = Id::as_ptr(&self.contentView().unwrap()) as *mut _;
rwh_04::RawWindowHandle::AppKit(window_handle)
}
@@ -1546,7 +1554,7 @@ impl WindowDelegate {
pub fn raw_window_handle_rwh_05(&self) -> rwh_05::RawWindowHandle {
let mut window_handle = rwh_05::AppKitWindowHandle::empty();
window_handle.ns_window = self.window() as *const WinitWindow as *mut _;
window_handle.ns_view = Retained::as_ptr(&self.view()) as *mut _;
window_handle.ns_view = Id::as_ptr(&self.view()) as *mut _;
rwh_05::RawWindowHandle::AppKit(window_handle)
}
@@ -1560,8 +1568,8 @@ impl WindowDelegate {
#[inline]
pub fn raw_window_handle_rwh_06(&self) -> rwh_06::RawWindowHandle {
let window_handle = rwh_06::AppKitWindowHandle::new({
let ptr = Retained::as_ptr(&self.view()) as *mut _;
std::ptr::NonNull::new(ptr).expect("Retained<T> should never be null")
let ptr = Id::as_ptr(&self.view()) as *mut _;
std::ptr::NonNull::new(ptr).expect("Id<T> should never be null")
});
rwh_06::RawWindowHandle::AppKit(window_handle)
}
@@ -1569,9 +1577,9 @@ impl WindowDelegate {
fn toggle_style_mask(&self, mask: NSWindowStyleMask, on: bool) {
let current_style_mask = self.window().styleMask();
if on {
self.set_style_mask(current_style_mask | mask);
self.set_style_mask(NSWindowStyleMask(current_style_mask.0 | mask.0));
} else {
self.set_style_mask(current_style_mask & !mask);
self.set_style_mask(NSWindowStyleMask(current_style_mask.0 & (!mask.0)));
}
}
@@ -1644,9 +1652,10 @@ impl WindowExtMacOS for WindowDelegate {
self.ivars().is_simple_fullscreen.set(true);
// Simulate pre-Lion fullscreen by hiding the dock and menu bar
let presentation_options =
NSApplicationPresentationOptions::NSApplicationPresentationAutoHideDock
| NSApplicationPresentationOptions::NSApplicationPresentationAutoHideMenuBar;
let presentation_options = NSApplicationPresentationOptions(
NSApplicationPresentationOptions::NSApplicationPresentationAutoHideDock.0
| NSApplicationPresentationOptions::NSApplicationPresentationAutoHideMenuBar.0,
);
app.setPresentationOptions(presentation_options);
// Hide the titlebar
@@ -1744,12 +1753,17 @@ impl WindowExtMacOS for WindowDelegate {
}
}
fn mask_contains(mask: NSWindowStyleMask, value: NSWindowStyleMask) -> bool {
mask.0 & value.0 == value.0
}
const DEFAULT_STANDARD_FRAME: NSRect =
NSRect::new(NSPoint::new(50.0, 50.0), NSSize::new(800.0, 600.0));
pub(super) fn get_ns_theme(mtm: MainThreadMarker) -> Theme {
let app = NSApplication::sharedApplication(mtm);
if !app.respondsToSelector(sel!(effectiveAppearance)) {
let has_theme: bool = unsafe { msg_send![&app, respondsToSelector: sel!(effectiveAppearance)] };
if !has_theme {
return Theme::Light;
}
let appearance = app.effectiveAppearance();
@@ -1767,7 +1781,8 @@ pub(super) fn get_ns_theme(mtm: MainThreadMarker) -> Theme {
fn set_ns_theme(theme: Option<Theme>, mtm: MainThreadMarker) {
let app = NSApplication::sharedApplication(mtm);
if app.respondsToSelector(sel!(effectiveAppearance)) {
let has_theme: bool = unsafe { msg_send![&app, respondsToSelector: sel!(effectiveAppearance)] };
if has_theme {
let appearance = theme.map(|t| {
let name = match t {
Theme::Dark => NSString::from_str("NSAppearanceNameDarkAqua"),

View File

@@ -12,7 +12,6 @@ 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};
@@ -323,13 +322,14 @@ impl<T: 'static> EventLoop<T> {
})
}
fn process_event<A: ApplicationHandler<T>>(
fn process_event<F>(
window_id: WindowId,
event_option: EventOption,
event_state: &mut EventState,
window_target: &event_loop::ActiveEventLoop,
app: &mut A,
) {
mut event_handler: F,
) where
F: FnMut(event::Event<T>),
{
match event_option {
EventOption::Key(KeyEvent { character, scancode, pressed }) => {
// Convert scancode
@@ -371,128 +371,125 @@ impl<T: 'static> EventLoop<T> {
key_without_modifiers = logical_key.clone();
}
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,
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,
},
},
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 {
app.window_event(
window_target,
window_id,
event::WindowEvent::ModifiersChanged(event_state.modifiers()),
);
event_handler(event::Event::WindowEvent {
window_id: RootWindowId(window_id),
event: event::WindowEvent::ModifiersChanged(event_state.modifiers()),
})
}
},
EventOption::TextInput(TextInputEvent { character }) => {
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())),
);
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())),
});
},
EventOption::Mouse(MouseEvent { x, y }) => {
app.window_event(
window_target,
RootWindowId(window_id),
event::WindowEvent::CursorMoved {
event_handler(event::Event::WindowEvent {
window_id: RootWindowId(window_id),
event: event::WindowEvent::CursorMoved {
device_id: event::DeviceId(DeviceId),
position: (x, y).into(),
},
);
});
},
EventOption::MouseRelative(MouseRelativeEvent { dx, dy }) => {
app.device_event(
window_target,
event::DeviceId(DeviceId),
event::DeviceEvent::MouseMotion { delta: (dx as f64, dy as f64) },
);
event_handler(event::Event::DeviceEvent {
device_id: event::DeviceId(DeviceId),
event: 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) {
app.window_event(
window_target,
RootWindowId(window_id),
event::WindowEvent::MouseInput {
event_handler(event::Event::WindowEvent {
window_id: RootWindowId(window_id),
event: event::WindowEvent::MouseInput {
device_id: event::DeviceId(DeviceId),
state,
button,
},
);
});
}
},
EventOption::Scroll(ScrollEvent { x, y }) => {
app.window_event(
window_target,
RootWindowId(window_id),
event::WindowEvent::MouseWheel {
event_handler(event::Event::WindowEvent {
window_id: RootWindowId(window_id),
event: 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 {}) => {
app.window_event(
window_target,
RootWindowId(window_id),
event::WindowEvent::CloseRequested,
);
event_handler(event::Event::WindowEvent {
window_id: RootWindowId(window_id),
event: event::WindowEvent::CloseRequested,
});
},
EventOption::Focus(FocusEvent { focused }) => {
app.window_event(
window_target,
RootWindowId(window_id),
event::WindowEvent::Focused(focused),
);
event_handler(event::Event::WindowEvent {
window_id: RootWindowId(window_id),
event: event::WindowEvent::Focused(focused),
});
},
EventOption::Move(MoveEvent { x, y }) => {
app.window_event(
window_target,
RootWindowId(window_id),
event::WindowEvent::Moved((x, y).into()),
);
event_handler(event::Event::WindowEvent {
window_id: RootWindowId(window_id),
event: event::WindowEvent::Moved((x, y).into()),
});
},
EventOption::Resize(ResizeEvent { width, height }) => {
app.window_event(
window_target,
RootWindowId(window_id),
event::WindowEvent::Resized((width, height).into()),
);
event_handler(event::Event::WindowEvent {
window_id: RootWindowId(window_id),
event: 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 }) => {
let event = if entered {
event::WindowEvent::CursorEntered { device_id: event::DeviceId(DeviceId) }
if entered {
event_handler(event::Event::WindowEvent {
window_id: RootWindowId(window_id),
event: event::WindowEvent::CursorEntered {
device_id: event::DeviceId(DeviceId),
},
});
} else {
event::WindowEvent::CursorLeft { device_id: event::DeviceId(DeviceId) }
};
app.window_event(window_target, RootWindowId(window_id), event);
event_handler(event::Event::WindowEvent {
window_id: RootWindowId(window_id),
event: event::WindowEvent::CursorLeft {
device_id: event::DeviceId(DeviceId),
},
});
}
},
other => {
tracing::warn!("unhandled event: {:?}", other);
@@ -500,13 +497,22 @@ impl<T: 'static> EventLoop<T> {
}
}
pub fn run_app<A: ApplicationHandler<T>>(mut self, app: &mut A) -> Result<(), EventLoopError> {
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);
};
let mut start_cause = StartCause::Init;
loop {
app.new_events(&self.window_target, start_cause);
event_handler(event::Event::NewEvents(start_cause), &self.window_target);
if start_cause == StartCause::Init {
app.resumed(&self.window_target);
event_handler(event::Event::Resumed, &self.window_target);
}
// Handle window creates.
@@ -522,15 +528,23 @@ impl<T: 'static> EventLoop<T> {
self.windows.push((window, EventState::default()));
let window_id = RootWindowId(window_id);
// 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);
event_handler(
event::Event::WindowEvent {
window_id: RootWindowId(window_id),
event: event::WindowEvent::Resized((properties.w, properties.h).into()),
},
&self.window_target,
);
// 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);
// 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,
);
}
// Handle window destroys.
@@ -538,8 +552,14 @@ impl<T: 'static> EventLoop<T> {
let mut destroys = self.window_target.p.destroys.lock().unwrap();
destroys.pop_front()
} {
let window_id = RootWindowId(destroy_id);
app.window_event(&self.window_target, window_id, event::WindowEvent::Destroyed);
event_handler(
event::Event::WindowEvent {
window_id: RootWindowId(destroy_id),
event: event::WindowEvent::Destroyed,
},
&self.window_target,
);
self.windows.retain(|(window, _event_state)| window.fd as u64 != destroy_id.fd);
}
@@ -566,8 +586,7 @@ impl<T: 'static> EventLoop<T> {
window_id,
orbital_event.to_option(),
event_state,
&self.window_target,
app,
|event| event_handler(event, &self.window_target),
);
}
@@ -595,7 +614,7 @@ impl<T: 'static> EventLoop<T> {
}
while let Ok(event) = self.user_events_receiver.try_recv() {
app.user_event(&self.window_target, event);
event_handler(event::Event::UserEvent(event), &self.window_target);
}
// To avoid deadlocks the redraws lock is not held during event processing.
@@ -603,14 +622,16 @@ impl<T: 'static> EventLoop<T> {
let mut redraws = self.window_target.p.redraws.lock().unwrap();
redraws.pop_front()
} {
app.window_event(
event_handler(
event::Event::WindowEvent {
window_id: RootWindowId(window_id),
event: event::WindowEvent::RedrawRequested,
},
&self.window_target,
RootWindowId(window_id),
event::WindowEvent::RedrawRequested,
);
}
app.about_to_wait(&self.window_target);
event_handler(event::Event::AboutToWait, &self.window_target);
if self.window_target.p.exiting() {
break;
@@ -674,7 +695,7 @@ impl<T: 'static> EventLoop<T> {
}
}
app.exiting(&self.window_target);
event_handler(event::Event::LoopExiting, &self.window_target);
Ok(())
}

View File

@@ -1,7 +1,6 @@
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;
@@ -32,13 +31,25 @@ impl<T> EventLoop<T> {
Ok(EventLoop { elw, user_event_sender, user_event_receiver })
}
pub fn run_app<A: ApplicationHandler<T>>(self, app: &mut A) -> ! {
pub fn run<F>(self, mut event_handler: F) -> !
where
F: FnMut(Event<T>, &RootActiveEventLoop),
{
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| handle_event(app, &target, &self.user_event_receiver, event));
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)
});
// 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`.
@@ -54,12 +65,24 @@ impl<T> EventLoop<T> {
unreachable!();
}
pub fn spawn_app<A: ApplicationHandler<T> + 'static>(self, mut app: A) {
pub fn spawn<F>(self, mut event_handler: F)
where
F: 'static + FnMut(Event<T>, &RootActiveEventLoop),
{
let target = RootActiveEventLoop { p: self.elw.p.clone(), _marker: PhantomData };
self.elw.p.run(
Box::new(move |event| {
handle_event(&mut app, &target, &self.user_event_receiver, 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)
}),
true,
);
@@ -73,26 +96,3 @@ 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),
}
}

View File

@@ -17,8 +17,8 @@
// incoming events (from the registered handlers) and ensuring they are passed to the user in a
// compliant way.
// TODO: FP, remove when <https://github.com/rust-lang/rust-clippy/issues/12377> is fixed.
#![allow(clippy::empty_docs)]
// TODO: FP, remove when <https://github.com/rust-lang/rust/issues/121621> is fixed.
#![allow(unknown_lints, non_local_definitions)]
mod r#async;
mod cursor;

View File

@@ -57,7 +57,6 @@ 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::{
@@ -216,14 +215,17 @@ impl<T: 'static> EventLoop<T> {
&self.window_target
}
pub fn run_app<A: ApplicationHandler<T>>(mut self, app: &mut A) -> Result<(), EventLoopError> {
self.run_app_on_demand(app)
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_on_demand<A: ApplicationHandler<T>>(
&mut self,
app: &mut A,
) -> Result<(), EventLoopError> {
pub fn run_on_demand<F>(&mut self, mut event_handler: F) -> Result<(), EventLoopError>
where
F: FnMut(Event<T>, &RootAEL),
{
{
let runner = &self.window_target.p.runner_shared;
@@ -234,32 +236,21 @@ impl<T: 'static> EventLoop<T> {
// returning
unsafe {
runner.set_event_handler(move |event| {
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
// 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
.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),
}
.expect("user event signaled but not received"),
),
};
event_handler(event, event_loop_windows_ref)
});
}
}
@@ -293,11 +284,10 @@ impl<T: 'static> EventLoop<T> {
}
}
pub fn pump_app_events<A: ApplicationHandler<T>>(
&mut self,
timeout: Option<Duration>,
app: &mut A,
) -> PumpStatus {
pub fn pump_events<F>(&mut self, timeout: Option<Duration>, mut event_handler: F) -> PumpStatus
where
F: FnMut(Event<T>, &RootAEL),
{
{
let runner = &self.window_target.p.runner_shared;
let event_loop_windows_ref = &self.window_target;
@@ -312,34 +302,16 @@ impl<T: 'static> EventLoop<T> {
// event handler.
unsafe {
runner.set_event_handler(move |event| {
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),
}
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)
});
runner.wakeup();
}
}