Compare commits

..

10 Commits

Author SHA1 Message Date
Mads Marquart
9efc50a262 AppKit: Launch app inside EventLoop::new
This moves the application launch from the beginning of
`EventLoop::run_app` to `EventLoop::new`, which:
- Feels more consistent, we now emit `new_events(StartCause::Init)` at
  the beginning of `run_app_on_demand` instead of conditionally
  depending on if the application has launched or not.
- Would allow us to bring back `event_loop.create_window` in some form
  on macOS, since the application would now be launched between
  `EventLoop::new` and `EventLoop::run_app`.

This differs semantically from iOS where we must still launch the
application in `EventLoop::run_app`, but iOS is the special snowflake
here, the other desktop platforms' are semantically launched after
`EventLoop::new`.

Doing it this way also matches what GLFW's init function does.
2026-03-19 01:47:39 +01:00
Mads Marquart
117ec364f9 examples: Always use tracing helper module 2026-03-18 22:55:47 +01:00
Mads Marquart
9f789e56ee examples: Use tracing macros instead of println!
This allows the examples to work a bit better in WASM and on iOS.
2026-03-18 22:55:47 +01:00
Mads Marquart
91558169d2 example: Fix tracing registration in pump_events
Tracing subscribers must be set up before `EventLoop::new()`.
2026-03-18 22:55:47 +01:00
Mads Marquart
4998cb990f AppKit: Trace sendEvent: calls
Most events in AppKit go through `sendEvent:`, and they contain a lot of
information, so it's nice to surface this when debugging.

We could override `sendEvent:` in UIKit and track this in there too, but
that's much less important, since there the relevant events are fairly
narrowly scoped, see the link below, other events go through CFRunLoop.
https://developer.apple.com/documentation/uikit/uievent/eventtype
2026-03-18 22:55:47 +01:00
Mads Marquart
98692641c4 UIKit: Add tracing spans 2026-03-18 22:55:47 +01:00
Mads Marquart
a630b5333c Apple: Track spans across queued closures 2026-03-18 22:55:47 +01:00
Mads Marquart
ca7735f10b Apple: Use tracing spans instead of custom trace_scope! macro
Spans are more powerful, and can even optionally be emitted as events by
using `.with_span_events(tracing_subscriber::fmt::format::FmtSpan::*)`.
2026-03-18 22:55:47 +01:00
Mads Marquart
7adb805011 Apple: Trace CFRunLoop activities
Add two run loop observers that:
- Create a TRACE-level span when the run loop enters a new state.
- Drops the span when the run loop exits that state.

These spans attach information to events, such that e.g. resizing a view
produces messages like:
```
TRACE inside runloop{mode=NSEventTrackingRunLoopMode}:timers:
  winit_appkit::util: Triggered `drawRect:` target="winit_appkit::view"
TRACE inside runloop{mode=NSEventTrackingRunLoopMode}:timers:
  winit_appkit::util: Completed `drawRect:` target="winit_appkit::view"
```
2026-03-18 22:55:47 +01:00
Mads Marquart
a8c7d809b9 Use new macOS 15 cursors for resize icons (#4422)
These look slightly different from the old ones. Verified that we now
use the same cursor icons as those used in Safari 26.
2026-03-18 22:33:53 +01:00
35 changed files with 676 additions and 430 deletions

View File

@@ -60,7 +60,6 @@ objc2-core-foundation = { version = "0.3.2", default-features = false }
objc2-core-graphics = { version = "0.3.2", default-features = false }
objc2-core-video = { version = "0.3.2", default-features = false }
objc2-foundation = { version = "0.3.2", default-features = false }
objc2-quartz-core = { version = "0.3.2", default-features = false }
objc2-ui-kit = { version = "0.3.2", default-features = false }
# Windows dependencies.

View File

@@ -1,4 +1,11 @@
# Using allow-invalid because this is platform-specific code
disallowed-macros = [
{ path = "std::print", reason = "works badly on web", replacement = "tracing::info" },
{ path = "std::println", reason = "works badly on web", replacement = "tracing::info" },
{ path = "std::eprint", reason = "works badly on web", replacement = "tracing::error" },
{ path = "std::eprintln", reason = "works badly on web", replacement = "tracing::error" },
{ path = "std::dbg", reason = "leftover debugging aid, remove it or use tracing" },
]
disallowed-methods = [
{ allow-invalid = true, path = "objc2_app_kit::NSView::visibleRect", reason = "We expose a render target to the user, and visibility is not really relevant to that (and can break if you don't use the rectangle position as well). Use `frame` instead." },
{ allow-invalid = true, path = "objc2_app_kit::NSWindow::setFrameTopLeftPoint", reason = "Not sufficient when working with Winit's coordinate system, use `flip_window_screen_coordinates` instead" },

View File

@@ -107,7 +107,6 @@ objc2-foundation = { workspace = true, features = [
"NSThread",
"NSValue",
] }
objc2-quartz-core = { workspace = true, features = ["std", "CABase"] }
winit-common = { workspace = true, features = ["core-foundation", "event-handler"] }
[dev-dependencies]

View File

@@ -9,6 +9,7 @@ use objc2::runtime::{Imp, Sel};
use objc2::sel;
use objc2_app_kit::{NSApplication, NSEvent, NSEventModifierFlags, NSEventType};
use objc2_foundation::MainThreadMarker;
use tracing::trace_span;
use winit_core::event::{DeviceEvent, ElementState};
use super::app_state::AppState;
@@ -21,6 +22,10 @@ static ORIGINAL: MainThreadBound<Cell<Option<SendEvent>>> = {
};
extern "C-unwind" fn send_event(app: &NSApplication, sel: Sel, event: &NSEvent) {
// This can be a bit noisy, since `event` is fairly large. Note that you can use
// `RUST_LOG='trace,winit_appkit::app=warn'` if you're debugging and want TRACE-level logs but
// not this.
let _entered = trace_span!("sendEvent:", ?event).entered();
let mtm = MainThreadMarker::from(app);
// Normally, holding Cmd + any key never sends us a `keyUp` event for that key.
@@ -98,7 +103,6 @@ pub(crate) fn override_send_event(global_app: &NSApplication) {
}
fn maybe_dispatch_device_event(app_state: &Rc<AppState>, event: &NSEvent) {
let time = app_state.event_time(event);
let event_type = event.r#type();
#[allow(non_upper_case_globals)]
match event_type {
@@ -111,7 +115,7 @@ fn maybe_dispatch_device_event(app_state: &Rc<AppState>, event: &NSEvent) {
if delta_x != 0.0 || delta_y != 0.0 {
app_state.maybe_queue_with_handler(move |app, event_loop| {
app.device_event(event_loop, None, time, DeviceEvent::PointerMotion {
app.device_event(event_loop, None, DeviceEvent::PointerMotion {
delta: (delta_x, delta_y),
});
});
@@ -120,7 +124,7 @@ fn maybe_dispatch_device_event(app_state: &Rc<AppState>, event: &NSEvent) {
NSEventType::LeftMouseDown | NSEventType::RightMouseDown | NSEventType::OtherMouseDown => {
let button = event.buttonNumber() as u32;
app_state.maybe_queue_with_handler(move |app, event_loop| {
app.device_event(event_loop, None, time, DeviceEvent::Button {
app.device_event(event_loop, None, DeviceEvent::Button {
button,
state: ElementState::Pressed,
});
@@ -129,7 +133,7 @@ fn maybe_dispatch_device_event(app_state: &Rc<AppState>, event: &NSEvent) {
NSEventType::LeftMouseUp | NSEventType::RightMouseUp | NSEventType::OtherMouseUp => {
let button = event.buttonNumber() as u32;
app_state.maybe_queue_with_handler(move |app, event_loop| {
app.device_event(event_loop, None, time, DeviceEvent::Button {
app.device_event(event_loop, None, DeviceEvent::Button {
button,
state: ElementState::Released,
});

View File

@@ -2,14 +2,11 @@ use std::cell::{Cell, OnceCell, RefCell};
use std::mem;
use std::rc::Rc;
use std::sync::Arc;
use std::time::{Duration, Instant};
use std::time::Instant;
use dispatch2::MainThreadBound;
use objc2::MainThreadMarker;
use objc2_app_kit::{NSApplication, NSApplicationActivationPolicy, NSEvent, NSRunningApplication};
use objc2_foundation::{NSNotification, NSTimeInterval};
use objc2_quartz_core::CACurrentMediaTime;
use tracing::warn;
use objc2_app_kit::NSApplication;
use winit_common::core_foundation::{EventLoopProxy, MainRunLoop};
use winit_common::event_handler::EventHandler;
use winit_core::application::ApplicationHandler;
@@ -18,24 +15,17 @@ use winit_core::event_loop::ControlFlow;
use winit_core::window::WindowId;
use super::event_loop::{ActiveEventLoop, notify_windows_of_exit, stop_app_immediately};
use super::menu;
use super::observer::EventLoopWaker;
#[derive(Debug)]
pub(super) struct AppState {
mtm: MainThreadMarker,
activation_policy: Option<NSApplicationActivationPolicy>,
default_menu: bool,
activate_ignoring_other_apps: bool,
run_loop: MainRunLoop,
event_loop_proxy: Arc<EventLoopProxy>,
event_handler: EventHandler,
stop_on_launch: Cell<bool>,
stop_before_wait: Cell<bool>,
stop_after_wait: Cell<bool>,
stop_on_redraw: Cell<bool>,
/// Whether `applicationDidFinishLaunching:` has been run or not.
is_launched: Cell<bool>,
/// Whether an `EventLoop` is currently running.
is_running: Cell<bool>,
/// Whether the user has requested the event loop to exit.
@@ -45,8 +35,6 @@ pub(super) struct AppState {
start_time: Cell<Option<Instant>>,
wait_timeout: Cell<Option<Instant>>,
pending_redraw: RefCell<Vec<WindowId>>,
startup_instant: Instant,
startup_timestamp: NSTimeInterval,
// 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.
}
@@ -57,40 +45,19 @@ static GLOBAL: MainThreadBound<OnceCell<Rc<AppState>>> =
MainThreadBound::new(OnceCell::new(), unsafe { MainThreadMarker::new_unchecked() });
impl AppState {
pub(super) fn setup_global(
mtm: MainThreadMarker,
activation_policy: Option<NSApplicationActivationPolicy>,
default_menu: bool,
activate_ignoring_other_apps: bool,
) -> Option<Rc<Self>> {
pub(super) fn setup_global(mtm: MainThreadMarker) -> Option<Rc<Self>> {
let event_loop_proxy = Arc::new(EventLoopProxy::new(mtm, move || {
Self::get(mtm).with_handler(|app, event_loop| app.proxy_wake_up(event_loop));
}));
// Prime dylib caches etc.
let _ = CACurrentMediaTime();
// Find the current Rust timestamp.
let startup_instant = Instant::now();
// Find the timestamp
//
// `NSProcessInfo::processInfo().systemUptime()` needs the required reason manifest,
// `CACurrentMediaTime` (currently) doesn't, so we use that instead.
let startup_timestamp = CACurrentMediaTime();
let this = Rc::new(Self {
mtm,
activation_policy,
default_menu,
activate_ignoring_other_apps,
run_loop: MainRunLoop::get(mtm),
event_loop_proxy,
event_handler: EventHandler::new(),
stop_on_launch: Cell::new(false),
stop_before_wait: Cell::new(false),
stop_after_wait: Cell::new(false),
stop_on_redraw: Cell::new(false),
is_launched: Cell::new(false),
is_running: Cell::new(false),
exit: Cell::new(false),
control_flow: Cell::new(ControlFlow::default()),
@@ -98,8 +65,6 @@ impl AppState {
start_time: Cell::new(None),
wait_timeout: Cell::new(None),
pending_redraw: RefCell::new(vec![]),
startup_instant,
startup_timestamp,
});
GLOBAL.get(mtm).set(this.clone()).ok().and(Some(this))
@@ -113,81 +78,6 @@ impl AppState {
.clone()
}
pub(crate) fn event_time(&self, event: &NSEvent) -> Instant {
if event.timestamp() == 0.0 {
warn!(?event, "got zero timestamp");
return Instant::now();
}
let duration_since_startup = event.timestamp() - self.startup_timestamp;
let duration_since_startup = Duration::from_secs_f64(duration_since_startup as f64);
self.startup_instant + duration_since_startup
}
// NOTE: This notification will, globally, only be emitted once,
// no matter how many `EventLoop`s the user creates.
pub fn did_finish_launching(self: &Rc<Self>, _notification: &NSNotification) {
trace_scope!("NSApplicationDidFinishLaunchingNotification");
self.is_launched.set(true);
let app = NSApplication::sharedApplication(self.mtm);
// We need to delay setting the activation policy and activating the app
// until `applicationDidFinishLaunching` has been called. Otherwise the
// menu bar is initially unresponsive on macOS 10.15.
if let Some(activation_policy) = self.activation_policy {
app.setActivationPolicy(activation_policy);
} else {
// If no activation policy is explicitly provided, and the application
// is bundled, do not set the activation policy at all, to allow the
// package manifest to define the behavior via LSUIElement.
//
// See:
// - https://github.com/rust-windowing/winit/issues/261
// - https://github.com/rust-windowing/winit/issues/3958
let is_bundled =
NSRunningApplication::currentApplication().bundleIdentifier().is_some();
if !is_bundled {
app.setActivationPolicy(NSApplicationActivationPolicy::Regular);
}
}
#[allow(deprecated)]
app.activateIgnoringOtherApps(self.activate_ignoring_other_apps);
if self.default_menu {
// The menubar initialization should be before the `NewEvents` event, to allow
// overriding of the default menu even if it's created
menu::initialize(&app);
}
self.waker.borrow_mut().start();
self.set_is_running(true);
self.dispatch_init_events();
// If the application is being launched via `EventLoop::pump_app_events()` then we'll
// want to stop the app once it is launched (and return to the external loop)
//
// In this case we still want to consider Winit's `EventLoop` to be "running",
// so we call `start_running()` above.
if self.stop_on_launch.get() {
// NOTE: the original idea had been to only stop the underlying `RunLoop`
// for the app but that didn't work as expected (`-[NSApplication run]`
// effectively ignored the attempt to stop the RunLoop and re-started it).
//
// So we return from `pump_events` by stopping the application.
let app = NSApplication::sharedApplication(self.mtm);
stop_app_immediately(&app);
}
}
pub fn will_terminate(self: &Rc<Self>, _notification: &NSNotification) {
trace_scope!("NSApplicationWillTerminateNotification");
let app = NSApplication::sharedApplication(self.mtm);
notify_windows_of_exit(&app);
self.event_handler.terminate();
self.internal_exit();
}
/// Place the event handler in the application state for the duration
/// of the given closure.
pub fn set_event_handler<R>(
@@ -198,15 +88,12 @@ impl AppState {
self.event_handler.set(Box::new(handler), closure)
}
pub fn event_loop_proxy(&self) -> &Arc<EventLoopProxy> {
&self.event_loop_proxy
pub fn terminate_event_handler(&self) {
self.event_handler.terminate();
}
/// If `pump_events` is called to progress the event loop then we
/// bootstrap the event loop via `-[NSApplication run]` but will use
/// `CFRunLoopRunInMode` for subsequent calls to `pump_events`.
pub fn set_stop_on_launch(&self) {
self.stop_on_launch.set(true);
pub fn event_loop_proxy(&self) -> &Arc<EventLoopProxy> {
&self.event_loop_proxy
}
pub fn set_stop_before_wait(&self, value: bool) {
@@ -237,10 +124,6 @@ impl AppState {
self.set_wait_timeout(None);
}
pub fn is_launched(&self) -> bool {
self.is_launched.get()
}
pub fn set_is_running(&self, value: bool) {
self.is_running.set(value)
}
@@ -274,12 +157,7 @@ impl AppState {
// -> Don't go back into the event handler when our callstack originates from there
if !self.event_handler.in_use() {
self.with_handler(|app, event_loop| {
app.window_event(
event_loop,
window_id,
Instant::now(),
WindowEvent::RedrawRequested,
);
app.window_event(event_loop, window_id, WindowEvent::RedrawRequested);
});
// `pump_events` will request to stop immediately _after_ dispatching RedrawRequested
@@ -379,12 +257,7 @@ impl AppState {
let redraw = mem::take(&mut *self.pending_redraw.borrow_mut());
for window_id in redraw {
self.with_handler(|app, event_loop| {
app.window_event(
event_loop,
window_id,
Instant::now(),
WindowEvent::RedrawRequested,
);
app.window_event(event_loop, window_id, WindowEvent::RedrawRequested);
});
}
self.with_handler(|app, event_loop| {

View File

@@ -5,7 +5,10 @@ use std::sync::OnceLock;
use objc2::rc::Retained;
use objc2::runtime::Sel;
use objc2::{AllocAnyThread, ClassType, available, msg_send, sel};
use objc2_app_kit::{NSBitmapImageRep, NSCursor, NSDeviceRGBColorSpace, NSImage};
use objc2_app_kit::{
NSBitmapImageRep, NSCursor, NSCursorFrameResizeDirections, NSCursorFrameResizePosition,
NSDeviceRGBColorSpace, NSImage,
};
use objc2_foundation::{
NSData, NSDictionary, NSNumber, NSObject, NSPoint, NSSize, NSString, ns_string,
};
@@ -204,23 +207,155 @@ pub(crate) fn cursor_from_icon(icon: CursorIcon) -> Retained<NSCursor> {
CursorIcon::NotAllowed | CursorIcon::NoDrop => NSCursor::operationNotAllowedCursor(),
CursorIcon::ContextMenu => NSCursor::contextualMenuCursor(),
CursorIcon::Crosshair => NSCursor::crosshairCursor(),
CursorIcon::EResize => NSCursor::resizeRightCursor(),
CursorIcon::NResize => NSCursor::resizeUpCursor(),
CursorIcon::WResize => NSCursor::resizeLeftCursor(),
CursorIcon::SResize => NSCursor::resizeDownCursor(),
CursorIcon::EwResize | CursorIcon::ColResize => NSCursor::resizeLeftRightCursor(),
CursorIcon::NsResize | CursorIcon::RowResize => NSCursor::resizeUpDownCursor(),
CursorIcon::EResize => {
if available!(macos = 15.0) {
NSCursor::frameResizeCursorFromPosition_inDirections(
NSCursorFrameResizePosition::Right,
NSCursorFrameResizeDirections::Outward,
)
} else {
NSCursor::resizeRightCursor()
}
},
CursorIcon::NResize => {
if available!(macos = 15.0) {
NSCursor::frameResizeCursorFromPosition_inDirections(
NSCursorFrameResizePosition::Top,
NSCursorFrameResizeDirections::Outward,
)
} else {
NSCursor::resizeUpCursor()
}
},
CursorIcon::WResize => {
if available!(macos = 15.0) {
NSCursor::frameResizeCursorFromPosition_inDirections(
NSCursorFrameResizePosition::Left,
NSCursorFrameResizeDirections::Outward,
)
} else {
NSCursor::resizeLeftCursor()
}
},
CursorIcon::SResize => {
if available!(macos = 15.0) {
NSCursor::frameResizeCursorFromPosition_inDirections(
NSCursorFrameResizePosition::Bottom,
NSCursorFrameResizeDirections::Outward,
)
} else {
NSCursor::resizeDownCursor()
}
},
CursorIcon::EwResize => {
if available!(macos = 15.0) {
NSCursor::frameResizeCursorFromPosition_inDirections(
NSCursorFrameResizePosition::Right,
NSCursorFrameResizeDirections::All,
)
} else {
NSCursor::resizeLeftRightCursor()
}
},
CursorIcon::NsResize => {
if available!(macos = 15.0) {
NSCursor::frameResizeCursorFromPosition_inDirections(
NSCursorFrameResizePosition::Top,
NSCursorFrameResizeDirections::All,
)
} else {
NSCursor::resizeUpDownCursor()
}
},
CursorIcon::NeResize => {
if available!(macos = 15.0) {
NSCursor::frameResizeCursorFromPosition_inDirections(
NSCursorFrameResizePosition::TopRight,
NSCursorFrameResizeDirections::Outward,
)
} else {
_windowResizeNorthEastCursor()
}
},
CursorIcon::NwResize => {
if available!(macos = 15.0) {
NSCursor::frameResizeCursorFromPosition_inDirections(
NSCursorFrameResizePosition::TopLeft,
NSCursorFrameResizeDirections::Outward,
)
} else {
_windowResizeNorthWestCursor()
}
},
CursorIcon::SeResize => {
if available!(macos = 15.0) {
NSCursor::frameResizeCursorFromPosition_inDirections(
NSCursorFrameResizePosition::BottomRight,
NSCursorFrameResizeDirections::Outward,
)
} else {
_windowResizeSouthEastCursor()
}
},
CursorIcon::SwResize => {
if available!(macos = 15.0) {
NSCursor::frameResizeCursorFromPosition_inDirections(
NSCursorFrameResizePosition::BottomLeft,
NSCursorFrameResizeDirections::Outward,
)
} else {
_windowResizeSouthWestCursor()
}
},
CursorIcon::NeswResize => {
if available!(macos = 15.0) {
NSCursor::frameResizeCursorFromPosition_inDirections(
NSCursorFrameResizePosition::TopRight,
NSCursorFrameResizeDirections::All,
)
} else {
_windowResizeNorthEastSouthWestCursor()
}
},
CursorIcon::NwseResize => {
if available!(macos = 15.0) {
NSCursor::frameResizeCursorFromPosition_inDirections(
NSCursorFrameResizePosition::TopLeft,
NSCursorFrameResizeDirections::All,
)
} else {
_windowResizeNorthWestSouthEastCursor()
}
},
CursorIcon::ColResize => {
if available!(macos = 15.0) {
NSCursor::columnResizeCursor()
} else {
NSCursor::resizeLeftRightCursor()
}
},
CursorIcon::RowResize => {
if available!(macos = 15.0) {
NSCursor::rowResizeCursor()
} else {
NSCursor::resizeUpDownCursor()
}
},
CursorIcon::ZoomIn => {
if available!(macos = 15.0) {
NSCursor::zoomInCursor()
} else {
_zoomInCursor()
}
},
CursorIcon::ZoomOut => {
if available!(macos = 15.0) {
NSCursor::zoomOutCursor()
} else {
_zoomOutCursor()
}
},
CursorIcon::Help => _helpCursor(),
CursorIcon::ZoomIn if available!(macos = 15.0) => NSCursor::zoomInCursor(),
CursorIcon::ZoomIn => _zoomInCursor(),
CursorIcon::ZoomOut if available!(macos = 15.0) => NSCursor::zoomOutCursor(),
CursorIcon::ZoomOut => _zoomOutCursor(),
CursorIcon::NeResize => _windowResizeNorthEastCursor(),
CursorIcon::NwResize => _windowResizeNorthWestCursor(),
CursorIcon::SeResize => _windowResizeSouthEastCursor(),
CursorIcon::SwResize => _windowResizeSouthWestCursor(),
CursorIcon::NeswResize => _windowResizeNorthEastSouthWestCursor(),
CursorIcon::NwseResize => _windowResizeNorthWestSouthEastCursor(),
// This is the wrong semantics for `Wait`, but it's the same as
// what's used in Safari and Chrome.
CursorIcon::Wait | CursorIcon::Progress => busyButClickableCursor(),

View File

@@ -7,12 +7,13 @@ use objc2::runtime::ProtocolObject;
use objc2::{MainThreadMarker, available};
use objc2_app_kit::{
NSApplication, NSApplicationActivationPolicy, NSApplicationDidFinishLaunchingNotification,
NSApplicationWillTerminateNotification, NSWindow,
NSApplicationWillTerminateNotification, NSRunningApplication, NSWindow,
};
use objc2_core_foundation::{CFIndex, CFRunLoopActivity, kCFRunLoopCommonModes};
use objc2_foundation::{NSNotificationCenter, NSObjectProtocol};
use rwh_06::HasDisplayHandle;
use winit_common::core_foundation::{MainRunLoop, MainRunLoopObserver};
use tracing::debug_span;
use winit_common::core_foundation::{MainRunLoop, MainRunLoopObserver, tracing_observers};
use winit_core::application::ApplicationHandler;
use winit_core::cursor::{CustomCursor as CoreCustomCursor, CustomCursorSource};
use winit_core::error::{EventLoopError, RequestError};
@@ -30,8 +31,8 @@ use super::cursor::CustomCursor;
use super::event::dummy_event;
use super::monitor;
use super::notification_center::create_observer;
use crate::ActivationPolicy;
use crate::window::Window;
use crate::{ActivationPolicy, menu};
#[derive(Debug)]
pub struct ActiveEventLoop {
@@ -149,9 +150,9 @@ pub struct EventLoop {
// the system instead cleans it up next time it would have posted a notification to it.
//
// Though we do still need to keep the observers around to prevent them from being deallocated.
_did_finish_launching_observer: Retained<ProtocolObject<dyn NSObjectProtocol>>,
_will_terminate_observer: Retained<ProtocolObject<dyn NSObjectProtocol>>,
_tracing_observers: Option<(MainRunLoopObserver, MainRunLoopObserver)>,
_before_waiting_observer: MainRunLoopObserver,
_after_waiting_observer: MainRunLoopObserver,
}
@@ -174,20 +175,8 @@ impl EventLoop {
let mtm = MainThreadMarker::new()
.expect("on macOS, `EventLoop` must be created on the main thread!");
let activation_policy = match attributes.activation_policy {
None => None,
Some(ActivationPolicy::Regular) => Some(NSApplicationActivationPolicy::Regular),
Some(ActivationPolicy::Accessory) => Some(NSApplicationActivationPolicy::Accessory),
Some(ActivationPolicy::Prohibited) => Some(NSApplicationActivationPolicy::Prohibited),
};
let app_state = AppState::setup_global(
mtm,
activation_policy,
attributes.default_menu,
attributes.activate_ignoring_other_apps,
)
.ok_or_else(|| EventLoopError::RecreationAttempt)?;
let app_state =
AppState::setup_global(mtm).ok_or_else(|| EventLoopError::RecreationAttempt)?;
// Initialize the application (if it has not already been).
let app = NSApplication::sharedApplication(mtm);
@@ -197,41 +186,50 @@ impl EventLoop {
let center = NSNotificationCenter::defaultCenter();
let weak_app_state = Rc::downgrade(&app_state);
let _did_finish_launching_observer = create_observer(
&center,
// `applicationDidFinishLaunching:`
unsafe { NSApplicationDidFinishLaunchingNotification },
move |notification| {
if let Some(app_state) = weak_app_state.upgrade() {
app_state.did_finish_launching(notification);
}
},
);
// Handle `terminate:`. This may happen if:
// - The user uses the context menu in the Dock icon.
// - Or the `Quit` menu item we install with the default menu (including via. the keyboard
// shortcut).
// - Maybe other cases?
//
// In these cases, AppKit is going to call `std::process::exit`, so we won't get the chance
// to return to the user from `EventLoop::run_app`. So we have to clean up and drop their
// windows and application here too.
let weak_app_state = Rc::downgrade(&app_state);
let _will_terminate_observer = create_observer(
&center,
// `applicationWillTerminate:`
unsafe { NSApplicationWillTerminateNotification },
move |notification| {
let _entered = debug_span!("applicationWillTerminate").entered();
let app = notification.object().unwrap().downcast::<NSApplication>().unwrap();
notify_windows_of_exit(&app);
if let Some(app_state) = weak_app_state.upgrade() {
app_state.will_terminate(notification);
app_state.terminate_event_handler();
app_state.internal_exit();
}
},
);
// Set up run loop observers for calling `new_events` and `about_to_wait`.
let main_loop = MainRunLoop::get(mtm);
let mode = unsafe { kCFRunLoopCommonModes }.unwrap();
// Tracing observers have the lowest and highest orderings.
let _tracing_observers = tracing_observers(mtm).inspect(|(start, end)| {
main_loop.add_observer(start, mode);
main_loop.add_observer(end, mode);
});
let app_state_clone = Rc::clone(&app_state);
let _before_waiting_observer = MainRunLoopObserver::new(
mtm,
CFRunLoopActivity::BeforeWaiting,
true,
// Queued with the lowest priority to ensure it is processed after other observers.
// Without that, we'd get a `LoopExiting` after `AboutToWait`.
CFIndex::MAX,
// Queued with the second-lowest priority (tracing observers use the lowest) to ensure
// it is processed after other observers.
CFIndex::MAX - 1,
move |_| app_state_clone.cleared(),
);
main_loop.add_observer(&_before_waiting_observer, mode);
@@ -241,18 +239,109 @@ impl EventLoop {
mtm,
CFRunLoopActivity::AfterWaiting,
true,
// Queued with the highest priority to ensure it is processed before other observers.
CFIndex::MIN,
// Queued with the second-highest priority (tracing observers use the highest) to
// ensure it is processed before other observers.
CFIndex::MIN + 1,
move |_| app_state_clone.wakeup(),
);
main_loop.add_observer(&_after_waiting_observer, mode);
// Run `finishLaunching` just in case it works.
app.finishLaunching();
// Now _ideally_, calling `finishLaunching` should be enough for the application to, you
// know, launch (create the a dock icon etc.), but unfortunately, this doesn't happen for
// various godforsaken reasons... The only way to make the application properly launch is by
// calling `NSApplication::run`.
//
// So we check if the application hasn't finished launching, and if it hasn't, we run it
// once to finish it.
//
// This is _very_ important, there's a _lot_ of weird and subtle state that requires that
// the application is launched properly, including window creation, the menu bar,
// activation, see:
// - https://github.com/rust-windowing/winit/pull/1903
// - https://github.com/rust-windowing/winit/pull/1922
// - https://github.com/rust-windowing/winit/issues/2238
// - https://github.com/rust-windowing/winit/issues/2051
// - https://github.com/rust-windowing/winit/issues/2087
// - https://developer.apple.com/forums/thread/772169
//
// This approach is similar to what other cross-platform windowing libraries do (except that
// we do it without a delegate to allow users to override that):
// - GLFW delegate: https://github.com/glfw/glfw/blob/3.4/src/cocoa_init.m#L439-L443
// - GLFW launch: https://github.com/glfw/glfw/blob/3.4/src/cocoa_init.m#L634-L635
// - FLTK delegate: https://github.com/fltk/fltk/blob/release-1.4.4/src/Fl_cocoa.mm#L1604-L1607
// - FLTK launch: https://github.com/fltk/fltk/blob/release-1.4.4/src/Fl_cocoa.mm#L1903-L1919
// - Stackoverflow issue: https://stackoverflow.com/questions/48020222/how-to-make-nsapp-run-not-block/67626393#67626393
if !NSRunningApplication::currentApplication().isFinishedLaunching() {
// Register an observer to stop the application immediately after launching.
//
// NOTE: This notification will, globally, only be emitted once, no matter how many
// `EventLoop`s the user creates. We detect it with `isFinishedLaunching` above.
let did_finish_launching_observer = create_observer(
&center,
unsafe { NSApplicationDidFinishLaunchingNotification },
move |notification| {
let _entered = debug_span!("applicationDidFinishLaunching").entered();
let app = notification.object().unwrap().downcast::<NSApplication>().unwrap();
// Stop the application, to make the `app.run()` call below return.
stop_app_immediately(&app);
},
);
// We call `stop_app_immediately` above, so this should return after launching.
app.run();
// The observer should've been called at this point.
drop(did_finish_launching_observer);
// We _could_ keep trying if we failed to initialize, but that would potentially lead
// to an infinite loop, it's probably better to just continue.
debug_assert!(NSRunningApplication::currentApplication().isFinishedLaunching());
}
// We need to delay setting the activation policy and activating the app until
// `applicationDidFinishLaunching:` has been called, otherwise the menu bar is initially
// unresponsive on macOS 10.15.
if let Some(activation_policy) = attributes.activation_policy {
app.setActivationPolicy(match activation_policy {
ActivationPolicy::Regular => NSApplicationActivationPolicy::Regular,
ActivationPolicy::Accessory => NSApplicationActivationPolicy::Accessory,
ActivationPolicy::Prohibited => NSApplicationActivationPolicy::Prohibited,
});
} else {
// If no activation policy is explicitly provided, and the application
// is bundled, do not set the activation policy at all, to allow the
// package manifest to define the behavior via LSUIElement.
//
// See:
// - https://github.com/rust-windowing/winit/issues/261
// - https://github.com/rust-windowing/winit/issues/3958
let is_bundled =
NSRunningApplication::currentApplication().bundleIdentifier().is_some();
if !is_bundled {
app.setActivationPolicy(NSApplicationActivationPolicy::Regular);
}
}
// TODO: Use `app.activate()` instead on newer OS versions?
#[expect(deprecated)]
app.activateIgnoringOtherApps(attributes.activate_ignoring_other_apps);
if attributes.default_menu {
// The default menubar initialization should be before everything else, to allow
// overriding it even if it's created.
menu::initialize(&app);
}
Ok(EventLoop {
app,
app_state: app_state.clone(),
window_target: ActiveEventLoop { app_state, mtm },
_did_finish_launching_observer,
_will_terminate_observer,
_tracing_observers,
_before_waiting_observer,
_after_waiting_observer,
})
@@ -270,6 +359,7 @@ impl EventLoop {
&mut self,
app: A,
) -> Result<(), EventLoopError> {
let _entered = debug_span!("run_app_on_demand").entered();
self.app_state.clear_exit();
self.app_state.set_event_handler(app, || {
autoreleasepool(|_| {
@@ -279,11 +369,9 @@ impl EventLoop {
self.app_state.set_stop_after_wait(false);
self.app_state.set_stop_on_redraw(false);
if self.app_state.is_launched() {
debug_assert!(!self.app_state.is_running());
self.app_state.set_is_running(true);
self.app_state.dispatch_init_events();
}
debug_assert!(!self.app_state.is_running());
self.app_state.set_is_running(true);
self.app_state.dispatch_init_events();
// NOTE: Make sure to not run the application re-entrantly, as that'd be confusing.
self.app.run();
@@ -300,19 +388,10 @@ impl EventLoop {
timeout: Option<Duration>,
app: A,
) -> PumpStatus {
let _entered = debug_span!("pump_app_events").entered();
self.app_state.set_event_handler(app, || {
autoreleasepool(|_| {
// As a special case, if the application hasn't been launched yet then we at least
// run the loop until it has fully launched.
if !self.app_state.is_launched() {
debug_assert!(!self.app_state.is_running());
self.app_state.set_stop_on_launch();
self.app.run();
// Note: we dispatch `NewEvents(Init)` + `Resumed` events after the application
// has launched
} else if !self.app_state.is_running() {
if !self.app_state.is_running() {
// Even though the application may have been launched, it's possible we aren't
// running if the `EventLoop` was run before and has since
// exited. This indicates that we just starting to re-run

View File

@@ -50,15 +50,13 @@
//! }
//!
//! fn main() -> Result<(), Box<dyn std::error::Error>> {
//! let event_loop = EventLoop::new()?;
//!
//! // Register the delegate before Winit gets a chance to touch things.
//! let mtm = MainThreadMarker::new().unwrap();
//! let delegate = AppDelegate::new(mtm);
//! // Important: Call `sharedApplication` after `EventLoop::new`,
//! // doing it before is not yet supported.
//! let app = NSApplication::sharedApplication(mtm);
//! app.setDelegate(Some(ProtocolObject::from_ref(&*delegate)));
//!
//! let event_loop = EventLoop::new()?;
//! // event_loop.run_app(&mut my_app);
//! Ok(())
//! }

View File

@@ -1,37 +1,10 @@
use objc2_core_graphics::CGError;
use tracing::trace;
use winit_core::error::OsError;
macro_rules! os_error {
($error:expr) => {{ winit_core::error::OsError::new(line!(), file!(), $error) }};
}
macro_rules! trace_scope {
($s:literal) => {
let _crate = $crate::util::TraceGuard::new(module_path!(), $s);
};
}
pub(crate) struct TraceGuard {
module_path: &'static str,
called_from_fn: &'static str,
}
impl TraceGuard {
#[inline]
pub(crate) fn new(module_path: &'static str, called_from_fn: &'static str) -> Self {
trace!(target = module_path, "Triggered `{}`", called_from_fn);
Self { module_path, called_from_fn }
}
}
impl Drop for TraceGuard {
#[inline]
fn drop(&mut self) {
trace!(target = self.module_path, "Completed `{}`", self.called_from_fn);
}
}
#[track_caller]
pub(crate) fn cgerr(err: CGError) -> Result<(), OsError> {
if err == CGError::Success { Ok(()) } else { Err(os_error!(format!("CGError {err:?}"))) }

View File

@@ -2,12 +2,11 @@
use std::cell::{Cell, RefCell};
use std::collections::{HashMap, VecDeque};
use std::rc::Rc;
use std::time::Instant;
use dpi::{LogicalPosition, LogicalSize};
use objc2::rc::Retained;
use objc2::runtime::{AnyObject, Sel};
use objc2::{AnyThread, DefinedClass, MainThreadMarker, MainThreadOnly, define_class, msg_send};
use objc2::{AnyThread, DefinedClass, MainThreadMarker, define_class, msg_send};
use objc2_app_kit::{
NSApplication, NSCursor, NSEvent, NSEventPhase, NSResponder, NSTextInputClient, NSTrackingArea,
NSTrackingAreaOptions, NSView, NSWindow,
@@ -17,7 +16,7 @@ use objc2_foundation::{
NSArray, NSAttributedString, NSAttributedStringKey, NSCopying, NSMutableAttributedString,
NSNotFound, NSObject, NSPoint, NSRange, NSRect, NSSize, NSString, NSUInteger,
};
use tracing::warn;
use tracing::{debug_span, trace_span};
use winit_core::event::{
DeviceEvent, ElementState, Ime, KeyEvent, Modifiers, MouseButton, MouseScrollDelta,
PointerKind, PointerSource, TouchPhase, WindowEvent,
@@ -155,7 +154,7 @@ define_class!(
// Not a normal method on `NSView`, it's triggered by `NSViewFrameDidChangeNotification`.
#[unsafe(method(viewFrameDidChangeNotification:))]
fn frame_did_change(&self, _notification: Option<&AnyObject>) {
trace_scope!("NSViewFrameDidChangeNotification");
let _entered = debug_span!("NSViewFrameDidChangeNotification").entered();
// Emit resize event here rather than from windowDidResize because:
// 1. When a new window is created as a tab, the frame size may change without a window
@@ -170,7 +169,7 @@ define_class!(
#[unsafe(method(drawRect:))]
fn draw_rect(&self, _rect: NSRect) {
trace_scope!("drawRect:");
let _entered = debug_span!("drawRect:").entered();
self.ivars().app_state.handle_redraw(window_id(&self.window()));
@@ -179,7 +178,7 @@ define_class!(
#[unsafe(method(acceptsFirstResponder))]
fn accepts_first_responder(&self) -> bool {
trace_scope!("acceptsFirstResponder");
let _entered = trace_span!("acceptsFirstResponder").entered();
true
}
@@ -193,13 +192,13 @@ define_class!(
// extension for using `NSTouchBar`
#[unsafe(method_id(touchBar))]
fn touch_bar(&self) -> Option<Retained<NSObject>> {
trace_scope!("touchBar");
let _entered = debug_span!("touchBar").entered();
None
}
#[unsafe(method(resetCursorRects))]
fn reset_cursor_rects(&self) {
trace_scope!("resetCursorRects");
let _entered = debug_span!("resetCursorRects").entered();
let bounds = self.bounds();
let cursor_state = self.ivars().cursor_state.borrow();
// We correctly invoke `addCursorRect` only from inside `resetCursorRects`
@@ -214,13 +213,13 @@ define_class!(
unsafe impl NSTextInputClient for WinitView {
#[unsafe(method(hasMarkedText))]
fn has_marked_text(&self) -> bool {
trace_scope!("hasMarkedText");
let _entered = debug_span!("hasMarkedText").entered();
self.ivars().marked_text.borrow().length() > 0
}
#[unsafe(method(markedRange))]
fn marked_range(&self) -> NSRange {
trace_scope!("markedRange");
let _entered = debug_span!("markedRange").entered();
let length = self.ivars().marked_text.borrow().length();
if length > 0 {
NSRange::new(0, length)
@@ -232,7 +231,7 @@ define_class!(
#[unsafe(method(selectedRange))]
fn selected_range(&self) -> NSRange {
trace_scope!("selectedRange");
let _entered = debug_span!("selectedRange").entered();
// Documented to return `{NSNotFound, 0}` if there is no selection.
NSRange::new(NSNotFound as NSUInteger, 0)
}
@@ -245,7 +244,7 @@ define_class!(
_replacement_range: NSRange,
) {
// TODO: Use _replacement_range, requires changing the event to report surrounding text.
trace_scope!("setMarkedText:selectedRange:replacementRange:");
let _entered = debug_span!("setMarkedText:selectedRange:replacementRange:").entered();
let (marked_text, string) = if let Some(string) =
string.downcast_ref::<NSAttributedString>()
@@ -299,7 +298,7 @@ define_class!(
#[unsafe(method(unmarkText))]
fn unmark_text(&self) {
trace_scope!("unmarkText");
let _entered = debug_span!("unmarkText").entered();
*self.ivars().marked_text.borrow_mut() = NSMutableAttributedString::new();
let input_context = self.inputContext().expect("input context");
@@ -316,7 +315,7 @@ define_class!(
#[unsafe(method_id(validAttributesForMarkedText))]
fn valid_attributes_for_marked_text(&self) -> Retained<NSArray<NSAttributedStringKey>> {
trace_scope!("validAttributesForMarkedText");
let _entered = trace_span!("validAttributesForMarkedText").entered();
NSArray::new()
}
@@ -326,13 +325,14 @@ define_class!(
_range: NSRange,
_actual_range: *mut NSRange,
) -> Option<Retained<NSAttributedString>> {
trace_scope!("attributedSubstringForProposedRange:actualRange:");
let _entered =
trace_span!("attributedSubstringForProposedRange:actualRange:").entered();
None
}
#[unsafe(method(characterIndexForPoint:))]
fn character_index_for_point(&self, _point: NSPoint) -> NSUInteger {
trace_scope!("characterIndexForPoint:");
let _entered = debug_span!("characterIndexForPoint:").entered();
0
}
@@ -342,7 +342,7 @@ define_class!(
_range: NSRange,
_actual_range: *mut NSRange,
) -> NSRect {
trace_scope!("firstRectForCharacterRange:actualRange:");
let _entered = debug_span!("firstRectForCharacterRange:actualRange:").entered();
// Guard when the view is no longer in a window during teardown.
let Some(window) = (**self).window() else {
@@ -358,7 +358,7 @@ define_class!(
#[unsafe(method(insertText:replacementRange:))]
fn insert_text(&self, string: &NSObject, _replacement_range: NSRange) {
// TODO: Use _replacement_range, requires changing the event to report surrounding text.
trace_scope!("insertText:replacementRange:");
let _entered = debug_span!("insertText:replacementRange:").entered();
let string = if let Some(string) = string.downcast_ref::<NSAttributedString>() {
string.string().to_string()
@@ -383,7 +383,7 @@ define_class!(
// "human readable" character happens, i.e. newlines, tabs, and Ctrl+C.
#[unsafe(method(doCommandBySelector:))]
fn do_command_by_selector(&self, command: Sel) {
trace_scope!("doCommandBySelector:");
let _entered = debug_span!("doCommandBySelector:").entered();
// We shouldn't forward any character from just committed text, since we'll end up
// sending it twice with some IMEs like Korean one. We'll also always send
@@ -422,7 +422,7 @@ define_class!(
impl WinitView {
#[unsafe(method(keyDown:))]
fn key_down(&self, event: &NSEvent) {
trace_scope!("keyDown:");
let _entered = debug_span!("keyDown:").entered();
{
let mut prev_input_source = self.ivars().input_source.borrow_mut();
let current_input_source = self.current_input_source();
@@ -481,7 +481,7 @@ define_class!(
#[unsafe(method(keyUp:))]
fn key_up(&self, event: &NSEvent) {
trace_scope!("keyUp:");
let _entered = debug_span!("keyUp:").entered();
let event = replace_event(event, self.option_as_alt());
self.update_modifiers(&event, false);
@@ -498,14 +498,14 @@ define_class!(
#[unsafe(method(flagsChanged:))]
fn flags_changed(&self, event: &NSEvent) {
trace_scope!("flagsChanged:");
let _entered = debug_span!("flagsChanged:").entered();
self.update_modifiers(event, true);
}
#[unsafe(method(insertTab:))]
fn insert_tab(&self, _sender: Option<&AnyObject>) {
trace_scope!("insertTab:");
let _entered = debug_span!("insertTab:").entered();
let window = self.window();
if let Some(first_responder) = window.firstResponder() {
if *first_responder == ***self {
@@ -516,7 +516,7 @@ define_class!(
#[unsafe(method(insertBackTab:))]
fn insert_back_tab(&self, _sender: Option<&AnyObject>) {
trace_scope!("insertBackTab:");
let _entered = debug_span!("insertBackTab:").entered();
let window = self.window();
if let Some(first_responder) = window.firstResponder() {
if *first_responder == ***self {
@@ -530,7 +530,7 @@ define_class!(
#[unsafe(method(cancelOperation:))]
fn cancel_operation(&self, _sender: Option<&AnyObject>) {
let mtm = MainThreadMarker::from(self);
trace_scope!("cancelOperation:");
let _entered = debug_span!("cancelOperation:").entered();
let event = NSApplication::sharedApplication(mtm)
.currentEvent()
@@ -559,71 +559,73 @@ define_class!(
#[unsafe(method(mouseDown:))]
fn mouse_down(&self, event: &NSEvent) {
trace_scope!("mouseDown:");
let _entered = debug_span!("mouseDown:").entered();
self.mouse_motion(event);
self.mouse_click(event, ElementState::Pressed);
}
#[unsafe(method(mouseUp:))]
fn mouse_up(&self, event: &NSEvent) {
trace_scope!("mouseUp:");
let _entered = debug_span!("mouseUp:").entered();
self.mouse_motion(event);
self.mouse_click(event, ElementState::Released);
}
#[unsafe(method(rightMouseDown:))]
fn right_mouse_down(&self, event: &NSEvent) {
trace_scope!("rightMouseDown:");
let _entered = debug_span!("rightMouseDown:").entered();
self.mouse_motion(event);
self.mouse_click(event, ElementState::Pressed);
}
#[unsafe(method(rightMouseUp:))]
fn right_mouse_up(&self, event: &NSEvent) {
trace_scope!("rightMouseUp:");
let _entered = debug_span!("rightMouseUp:").entered();
self.mouse_motion(event);
self.mouse_click(event, ElementState::Released);
}
#[unsafe(method(otherMouseDown:))]
fn other_mouse_down(&self, event: &NSEvent) {
trace_scope!("otherMouseDown:");
let _entered = debug_span!("otherMouseDown:").entered();
self.mouse_motion(event);
self.mouse_click(event, ElementState::Pressed);
}
#[unsafe(method(otherMouseUp:))]
fn other_mouse_up(&self, event: &NSEvent) {
trace_scope!("otherMouseUp:");
let _entered = debug_span!("otherMouseUp:").entered();
self.mouse_motion(event);
self.mouse_click(event, ElementState::Released);
}
// No tracing on these because that would be overly verbose
#[unsafe(method(mouseMoved:))]
fn mouse_moved(&self, event: &NSEvent) {
let _entered = debug_span!("mouseMoved:").entered();
self.mouse_motion(event);
}
#[unsafe(method(mouseDragged:))]
fn mouse_dragged(&self, event: &NSEvent) {
let _entered = debug_span!("mouseDragged:").entered();
self.mouse_motion(event);
}
#[unsafe(method(rightMouseDragged:))]
fn right_mouse_dragged(&self, event: &NSEvent) {
let _entered = debug_span!("rightMouseDragged:").entered();
self.mouse_motion(event);
}
#[unsafe(method(otherMouseDragged:))]
fn other_mouse_dragged(&self, event: &NSEvent) {
let _entered = debug_span!("otherMouseDragged:").entered();
self.mouse_motion(event);
}
#[unsafe(method(mouseEntered:))]
fn mouse_entered(&self, event: &NSEvent) {
trace_scope!("mouseEntered:");
let _entered = debug_span!("mouseEntered:").entered();
let position = self.mouse_view_point(event).to_physical(self.scale_factor());
@@ -637,7 +639,7 @@ define_class!(
#[unsafe(method(mouseExited:))]
fn mouse_exited(&self, event: &NSEvent) {
trace_scope!("mouseExited:");
let _entered = debug_span!("mouseExited:").entered();
let position = self.mouse_view_point(event).to_physical(self.scale_factor());
@@ -651,7 +653,7 @@ define_class!(
#[unsafe(method(scrollWheel:))]
fn scroll_wheel(&self, event: &NSEvent) {
trace_scope!("scrollWheel:");
let _entered = debug_span!("scrollWheel:").entered();
self.mouse_motion(event);
@@ -682,16 +684,15 @@ define_class!(
self.update_modifiers(event, false);
let time = self.ivars().app_state.event_time(event);
self.ivars().app_state.maybe_queue_with_handler(move |app, event_loop| {
app.device_event(event_loop, None, time, DeviceEvent::MouseWheel { delta })
app.device_event(event_loop, None, DeviceEvent::MouseWheel { delta })
});
self.queue_event(WindowEvent::MouseWheel { device_id: None, delta, phase });
}
#[unsafe(method(magnifyWithEvent:))]
fn magnify_with_event(&self, event: &NSEvent) {
trace_scope!("magnifyWithEvent:");
let _entered = debug_span!("magnifyWithEvent:").entered();
self.mouse_motion(event);
@@ -713,7 +714,7 @@ define_class!(
#[unsafe(method(smartMagnifyWithEvent:))]
fn smart_magnify_with_event(&self, event: &NSEvent) {
trace_scope!("smartMagnifyWithEvent:");
let _entered = debug_span!("smartMagnifyWithEvent:").entered();
self.mouse_motion(event);
@@ -722,7 +723,7 @@ define_class!(
#[unsafe(method(rotateWithEvent:))]
fn rotate_with_event(&self, event: &NSEvent) {
trace_scope!("rotateWithEvent:");
let _entered = debug_span!("rotateWithEvent:").entered();
self.mouse_motion(event);
@@ -744,7 +745,7 @@ define_class!(
#[unsafe(method(pressureChangeWithEvent:))]
fn pressure_change_with_event(&self, event: &NSEvent) {
trace_scope!("pressureChangeWithEvent:");
let _entered = debug_span!("pressureChangeWithEvent:").entered();
self.queue_event(WindowEvent::TouchpadPressure {
device_id: None,
@@ -758,13 +759,13 @@ define_class!(
// https://github.com/chromium/chromium/blob/a86a8a6bcfa438fa3ac2eba6f02b3ad1f8e0756f/ui/views/cocoa/bridged_content_view.mm#L816
#[unsafe(method(_wantsKeyDownForEvent:))]
fn wants_key_down_for_event(&self, _event: &NSEvent) -> bool {
trace_scope!("_wantsKeyDownForEvent:");
let _entered = debug_span!("_wantsKeyDownForEvent:").entered();
true
}
#[unsafe(method(acceptsFirstMouse:))]
fn accepts_first_mouse(&self, _event: &NSEvent) -> bool {
trace_scope!("acceptsFirstMouse:");
let _entered = debug_span!("acceptsFirstMouse:").entered();
self.ivars().accepts_first_mouse
}
}
@@ -847,16 +848,9 @@ impl WinitView {
}
fn queue_event(&self, event: WindowEvent) {
let app = NSApplication::sharedApplication(self.mtm());
let window_id = window_id(&self.window());
let time = if let Some(nsevent) = app.currentEvent() {
self.ivars().app_state.event_time(&nsevent)
} else {
warn!("queued event with wrong timestamp, no active NSEvent found");
Instant::now()
};
self.ivars().app_state.maybe_queue_with_handler(move |app, event_loop| {
app.window_event(event_loop, window_id, time, event);
app.window_event(event_loop, window_id, event);
});
}

View File

@@ -8,6 +8,7 @@ use objc2::rc::{Retained, autoreleasepool};
use objc2::{MainThreadMarker, Message, define_class};
use objc2_app_kit::{NSPanel, NSResponder, NSWindow};
use objc2_foundation::NSObject;
use tracing::trace_span;
use winit_core::cursor::Cursor;
use winit_core::error::RequestError;
use winit_core::icon::Icon;
@@ -350,13 +351,13 @@ define_class!(
impl WinitWindow {
#[unsafe(method(canBecomeMainWindow))]
fn can_become_main_window(&self) -> bool {
trace_scope!("canBecomeMainWindow");
let _entered = trace_span!("canBecomeMainWindow").entered();
true
}
#[unsafe(method(canBecomeKeyWindow))]
fn can_become_key_window(&self) -> bool {
trace_scope!("canBecomeKeyWindow");
let _entered = trace_span!("canBecomeKeyWindow").entered();
true
}
}
@@ -374,7 +375,7 @@ define_class!(
// it doesn't if window doesn't have NSWindowStyleMask::Titled
#[unsafe(method(canBecomeKeyWindow))]
fn can_become_key_window(&self) -> bool {
trace_scope!("canBecomeKeyWindow");
let _entered = trace_span!("canBecomeKeyWindow").entered();
true
}
}

View File

@@ -5,7 +5,6 @@ use std::ffi::c_void;
use std::ptr;
use std::rc::Rc;
use std::sync::{Arc, Mutex};
use std::time::Instant;
use dpi::{
LogicalInsets, LogicalPosition, LogicalSize, PhysicalInsets, PhysicalPosition, PhysicalSize,
@@ -42,7 +41,7 @@ use objc2_foundation::{
NSObjectNSDelayedPerforming, NSObjectNSKeyValueObserverRegistration, NSObjectProtocol, NSPoint,
NSRect, NSSize, NSString, ns_string,
};
use tracing::{trace, warn};
use tracing::{debug_span, trace, warn};
use winit_common::core_foundation::MainRunLoop;
use winit_core::cursor::Cursor;
use winit_core::error::{NotSupportedError, RequestError};
@@ -122,14 +121,14 @@ define_class!(
unsafe impl NSWindowDelegate for WindowDelegate {
#[unsafe(method(windowShouldClose:))]
fn window_should_close(&self, _: Option<&AnyObject>) -> bool {
trace_scope!("windowShouldClose:");
let _entered = debug_span!("windowShouldClose:").entered();
self.queue_event(WindowEvent::CloseRequested);
false
}
#[unsafe(method(windowWillClose:))]
fn window_will_close(&self, _: Option<&AnyObject>) {
trace_scope!("windowWillClose:");
let _entered = debug_span!("windowWillClose:").entered();
// `setDelegate:` retains the previous value and then autoreleases it
autoreleasepool(|_| {
// Since El Capitan, we need to be careful that delegate methods can't
@@ -141,14 +140,14 @@ define_class!(
#[unsafe(method(windowDidResize:))]
fn window_did_resize(&self, _: Option<&AnyObject>) {
trace_scope!("windowDidResize:");
let _entered = debug_span!("windowDidResize:").entered();
// NOTE: WindowEvent::SurfaceResized is reported using NSViewFrameDidChangeNotification.
self.emit_move_event();
}
#[unsafe(method(windowWillStartLiveResize:))]
fn window_will_start_live_resize(&self, _: Option<&AnyObject>) {
trace_scope!("windowWillStartLiveResize:");
let _entered = debug_span!("windowWillStartLiveResize:").entered();
let increments = self.ivars().surface_resize_increments.get();
self.set_resize_increments_inner(increments);
@@ -156,20 +155,20 @@ define_class!(
#[unsafe(method(windowDidEndLiveResize:))]
fn window_did_end_live_resize(&self, _: Option<&AnyObject>) {
trace_scope!("windowDidEndLiveResize:");
let _entered = debug_span!("windowDidEndLiveResize:").entered();
self.set_resize_increments_inner(NSSize::new(1., 1.));
}
// This won't be triggered if the move was part of a resize.
#[unsafe(method(windowDidMove:))]
fn window_did_move(&self, _: Option<&AnyObject>) {
trace_scope!("windowDidMove:");
let _entered = debug_span!("windowDidMove:").entered();
self.emit_move_event();
}
#[unsafe(method(windowDidChangeBackingProperties:))]
fn window_did_change_backing_properties(&self, _: Option<&AnyObject>) {
trace_scope!("windowDidChangeBackingProperties:");
let _entered = debug_span!("windowDidChangeBackingProperties:").entered();
let scale_factor = self.scale_factor();
if scale_factor == self.ivars().previous_scale_factor.get() {
return;
@@ -185,7 +184,7 @@ define_class!(
#[unsafe(method(windowDidBecomeKey:))]
fn window_did_become_key(&self, _: Option<&AnyObject>) {
trace_scope!("windowDidBecomeKey:");
let _entered = debug_span!("windowDidBecomeKey:").entered();
// TODO: center the cursor if the window had mouse grab when it
// lost focus
self.queue_event(WindowEvent::Focused(true));
@@ -193,7 +192,7 @@ define_class!(
#[unsafe(method(windowDidResignKey:))]
fn window_did_resign_key(&self, _: Option<&AnyObject>) {
trace_scope!("windowDidResignKey:");
let _entered = debug_span!("windowDidResignKey:").entered();
// It happens rather often, e.g. when the user is Cmd+Tabbing, that the
// NSWindowDelegate will receive a didResignKey event despite no event
// being received when the modifiers are released. This is because
@@ -209,7 +208,7 @@ define_class!(
/// Invoked when before enter fullscreen
#[unsafe(method(windowWillEnterFullScreen:))]
fn window_will_enter_fullscreen(&self, _: Option<&AnyObject>) {
trace_scope!("windowWillEnterFullScreen:");
let _entered = debug_span!("windowWillEnterFullScreen:").entered();
self.ivars().maximized.set(self.is_zoomed());
let mut fullscreen = self.ivars().fullscreen.borrow_mut();
@@ -237,7 +236,7 @@ define_class!(
/// Invoked when before exit fullscreen
#[unsafe(method(windowWillExitFullScreen:))]
fn window_will_exit_fullscreen(&self, _: Option<&AnyObject>) {
trace_scope!("windowWillExitFullScreen:");
let _entered = debug_span!("windowWillExitFullScreen:").entered();
self.ivars().in_fullscreen_transition.set(true);
}
@@ -248,7 +247,7 @@ define_class!(
_: Option<&AnyObject>,
proposed_options: NSApplicationPresentationOptions,
) -> NSApplicationPresentationOptions {
trace_scope!("window:willUseFullScreenPresentationOptions:");
let _entered = debug_span!("window:willUseFullScreenPresentationOptions:").entered();
// Generally, games will want to disable the menu bar and the dock. Ideally,
// this would be configurable by the user. Unfortunately because of our
// `CGShieldingWindowLevel() + 1` hack (see `set_fullscreen`), our window is
@@ -271,7 +270,7 @@ define_class!(
/// Invoked when entered fullscreen
#[unsafe(method(windowDidEnterFullScreen:))]
fn window_did_enter_fullscreen(&self, _: Option<&AnyObject>) {
trace_scope!("windowDidEnterFullScreen:");
let _entered = debug_span!("windowDidEnterFullScreen:").entered();
self.ivars().initial_fullscreen.set(false);
self.ivars().in_fullscreen_transition.set(false);
if let Some(target_fullscreen) = self.ivars().target_fullscreen.take() {
@@ -282,7 +281,7 @@ define_class!(
/// Invoked when exited fullscreen
#[unsafe(method(windowDidExitFullScreen:))]
fn window_did_exit_fullscreen(&self, _: Option<&AnyObject>) {
trace_scope!("windowDidExitFullScreen:");
let _entered = debug_span!("windowDidExitFullScreen:").entered();
self.restore_state_from_fullscreen();
self.ivars().in_fullscreen_transition.set(false);
@@ -309,7 +308,7 @@ define_class!(
/// work you may have done to prepare to enter full-screen mode.
#[unsafe(method(windowDidFailToEnterFullScreen:))]
fn window_did_fail_to_enter_fullscreen(&self, _: Option<&AnyObject>) {
trace_scope!("windowDidFailToEnterFullScreen:");
let _entered = debug_span!("windowDidFailToEnterFullScreen:").entered();
self.ivars().in_fullscreen_transition.set(false);
self.ivars().target_fullscreen.replace(None);
if self.ivars().initial_fullscreen.get() {
@@ -328,7 +327,7 @@ define_class!(
// Invoked when the occlusion state of the window changes
#[unsafe(method(windowDidChangeOcclusionState:))]
fn window_did_change_occlusion_state(&self, _: Option<&AnyObject>) {
trace_scope!("windowDidChangeOcclusionState:");
let _entered = debug_span!("windowDidChangeOcclusionState:").entered();
let visible = self.window().occlusionState().contains(NSWindowOcclusionState::Visible);
self.queue_event(WindowEvent::Occluded(!visible));
@@ -349,7 +348,7 @@ define_class!(
#[unsafe(method(windowDidChangeScreen:))]
fn window_did_change_screen(&self, _: Option<&AnyObject>) {
trace_scope!("windowDidChangeScreen:");
let _entered = debug_span!("windowDidChangeScreen:").entered();
let is_simple_fullscreen = self.ivars().is_simple_fullscreen.get();
if is_simple_fullscreen {
if let Some(screen) = self.window().screen() {
@@ -363,7 +362,7 @@ define_class!(
/// Invoked when the dragged image enters destination bounds or frame
#[unsafe(method(draggingEntered:))]
fn dragging_entered(&self, sender: &ProtocolObject<dyn NSDraggingInfo>) -> bool {
trace_scope!("draggingEntered:");
let _entered = debug_span!("draggingEntered:").entered();
use std::path::PathBuf;
@@ -394,7 +393,7 @@ define_class!(
#[unsafe(method(wantsPeriodicDraggingUpdates))]
fn wants_periodic_dragging_updates(&self) -> bool {
trace_scope!("wantsPeriodicDraggingUpdates:");
let _entered = debug_span!("wantsPeriodicDraggingUpdates:").entered();
true
}
@@ -402,7 +401,7 @@ define_class!(
/// modification of the dragging operation or mouse-pointer position.
#[unsafe(method(draggingUpdated:))]
fn dragging_updated(&self, sender: &ProtocolObject<dyn NSDraggingInfo>) -> bool {
trace_scope!("draggingUpdated:");
let _entered = debug_span!("draggingUpdated:").entered();
let dl = sender.draggingLocation();
let dl = self.view().convertPoint_fromView(dl, None);
@@ -417,14 +416,14 @@ define_class!(
/// Invoked when the image is released
#[unsafe(method(prepareForDragOperation:))]
fn prepare_for_drag_operation(&self, _sender: &NSObject) -> bool {
trace_scope!("prepareForDragOperation:");
let _entered = debug_span!("prepareForDragOperation:").entered();
true
}
/// Invoked after the released image has been removed from the screen
#[unsafe(method(performDragOperation:))]
fn perform_drag_operation(&self, sender: &ProtocolObject<dyn NSDraggingInfo>) -> bool {
trace_scope!("performDragOperation:");
let _entered = debug_span!("performDragOperation:").entered();
use std::path::PathBuf;
@@ -456,13 +455,13 @@ define_class!(
/// Invoked when the dragging operation is complete
#[unsafe(method(concludeDragOperation:))]
fn conclude_drag_operation(&self, _sender: Option<&NSObject>) {
trace_scope!("concludeDragOperation:");
let _entered = debug_span!("concludeDragOperation:").entered();
}
/// Invoked when the dragging operation is cancelled
#[unsafe(method(draggingExited:))]
fn dragging_exited(&self, sender: Option<&ProtocolObject<dyn NSDraggingInfo>>) {
trace_scope!("draggingExited:");
let _entered = debug_span!("draggingExited:").entered();
let position = sender.map(|sender| {
let dl = sender.draggingLocation();
@@ -484,7 +483,7 @@ define_class!(
change: Option<&NSDictionary<NSKeyValueChangeKey, AnyObject>>,
_context: *mut c_void,
) {
trace_scope!("observeValueForKeyPath:ofObject:change:context:");
let _entered = debug_span!("observeValueForKeyPath:ofObject:change:context:").entered();
// NOTE: We don't _really_ need to check the key path, as there should only be one, but
// in the future we might want to observe other key paths.
if key_path == Some(ns_string!("effectiveAppearance")) {
@@ -904,16 +903,9 @@ impl WindowDelegate {
}
pub(crate) fn queue_event(&self, event: WindowEvent) {
let app = NSApplication::sharedApplication(self.mtm());
let window_id = window_id(self.window());
let time = if let Some(nsevent) = app.currentEvent() {
self.ivars().app_state.event_time(&nsevent)
} else {
warn!("queued event with wrong timestamp, no active NSEvent found");
Instant::now()
};
self.ivars().app_state.maybe_queue_with_handler(move |app, event_loop| {
app.window_event(event_loop, window_id, time, event);
app.window_event(event_loop, window_id, event);
});
}

View File

@@ -9,7 +9,7 @@ use std::cell::Cell;
use objc2::MainThreadMarker;
use objc2_core_foundation::{CFRetained, CFRunLoop, CFRunLoopMode, kCFRunLoopDefaultMode};
use tracing::error;
use tracing::{Span, error};
use super::MainRunLoopObserver;
@@ -49,7 +49,8 @@ impl MainRunLoop {
/// This queuing could be implemented in the following several ways with subtle differences in
/// timing. This list is sorted in rough order in which they are run:
///
/// 1. Using `CFRunLoopPerformBlock` or `-[NSRunLoop performBlock:]`.
/// 1. Using `CFRunLoopPerformBlock` or `-[NSRunLoop performBlock:]` to queue a closure to run
/// the next time runloop sources are processed.
///
/// 2. Using `-[NSObject performSelectorOnMainThread:withObject:waitUntilDone:]` or wrapping the
/// event in `NSEvent` and posting that to `-[NSApplication postEvent:atStart:]` (both
@@ -73,9 +74,17 @@ impl MainRunLoop {
/// put the event at the very front of the queue, to be handled as soon as possible after
/// handling whatever event it's currently handling.
pub fn queue_closure(&self, closure: impl FnOnce() + 'static) {
// We use this to run a closure asynchronously at a later point, so it also makes sense to
// re-enter the current span when running the queued closure.
let span = Span::current();
// Convert `FnOnce()` to `Block<dyn Fn()>`.
let closure = Cell::new(Some(closure));
let block = block2::RcBlock::new(move || {
// Running this block happens inside a `CFRunLoopSource`, but the spans that we emit for
// that are (intentionally) overwritten by entering the span from before.
let _enter = span.enter();
debug_assert!(MainThreadMarker::new().is_some());
if let Some(closure) = closure.take() {
closure()

View File

@@ -6,7 +6,9 @@
mod event_loop_proxy;
mod main_run_loop;
mod main_run_loop_observer;
mod tracing_observers;
pub use self::event_loop_proxy::*;
pub use self::main_run_loop::*;
pub use self::main_run_loop_observer::*;
pub use self::tracing_observers::*;

View File

@@ -0,0 +1,146 @@
use std::cell::RefCell;
use std::rc::Rc;
use objc2::MainThreadMarker;
use objc2_core_foundation::{CFIndex, CFRunLoop, CFRunLoopActivity, kCFRunLoopDefaultMode};
use tracing::span::EnteredSpan;
use tracing::{Level, error, field, span_enabled, trace_span};
use crate::core_foundation::MainRunLoopObserver;
/// Create two run loop observers that add TRACE-level [spans][tracing::span].
///
/// This is useful when debugging run loops, it makes it easier to see in which run loop activity an
/// event is triggered inside (if any).
///
/// When debugging these interactions, it can also be useful to configure your tracing subscriber
/// with `.with_span_events(tracing_subscriber::fmt::format::FmtSpan::ACTIVE)` to emit events upon
/// entering and exiting these stages.
pub fn tracing_observers(
mtm: MainThreadMarker,
) -> Option<(MainRunLoopObserver, MainRunLoopObserver)> {
// Observers are a bit costly, so don't create them if the tracing-level for this module is
// configured to disable them.
if !span_enabled!(Level::TRACE) {
return None;
}
/// The state that we think the runloop is currently in.
///
/// The order of activities we observe if waiting twice looks something like:
/// - CFRunLoopActivity::Entry
/// - CFRunLoopActivity::BeforeTimers
/// - CFRunLoopActivity::BeforeSources
/// - CFRunLoopActivity::BeforeWaiting
/// - CFRunLoopActivity::AfterWaiting
/// - CFRunLoopActivity::BeforeTimers
/// - CFRunLoopActivity::BeforeSources
/// - CFRunLoopActivity::BeforeWaiting
/// - CFRunLoopActivity::AfterWaiting
/// - CFRunLoopActivity::Exit
///
/// And if not waiting, it looks something like:
/// - CFRunLoopActivity::Entry
/// - CFRunLoopActivity::BeforeTimers
/// - CFRunLoopActivity::BeforeSources
/// - CFRunLoopActivity::Exit
#[derive(Default)]
#[allow(unused)] // EnteredSpans are kept around
enum RunLoopState {
/// Currently processing `Entry`/`Exit` observers.
#[default]
Entered,
/// Currently processing timers or `BeforeTimers` observers.
Timers(EnteredSpan),
/// Currently processing sources or `BeforeSources` observers.
Sources(EnteredSpan),
/// Currently waiting or processing `BeforeWaiting`/`AfterWaiting` observers.
Waiting(EnteredSpan),
}
// A list of currently entered (outer) spans and their state.
//
// This is a list because runloops can be run recursively.
let spans: Rc<RefCell<Vec<(EnteredSpan, RunLoopState)>>> = Rc::new(RefCell::new(Vec::new()));
let spans_clone = Rc::clone(&spans);
// An observer at the start of run loop activities.
let activities = CFRunLoopActivity::Entry
| CFRunLoopActivity::BeforeTimers
| CFRunLoopActivity::BeforeSources
| CFRunLoopActivity::BeforeWaiting;
let start = MainRunLoopObserver::new(mtm, activities, true, CFIndex::MIN, move |activity| {
match activity {
// Add an outer span for each runloop iteration.
CFRunLoopActivity::Entry => {
let span = trace_span!("inside runloop", mode = field::Empty);
// Get the mode dynamically, the observer may added to multiple different modes.
let mode = CFRunLoop::current().unwrap().current_mode().unwrap();
// Mode isn't interesting if it's the default mode.
if &*mode != unsafe { kCFRunLoopDefaultMode }.unwrap() {
span.record("mode", field::display(mode));
}
let entered = span.entered();
spans.borrow_mut().push((entered, RunLoopState::Entered));
},
// Add inner spans that help inspecting the state the runloop is in.
CFRunLoopActivity::BeforeTimers => {
if let Some((_, state)) = spans.borrow_mut().last_mut() {
*state = RunLoopState::Entered; // Drop any previous spans.
*state = RunLoopState::Timers(trace_span!("processing timers").entered());
} else {
error!("unbalanced observer invocations");
}
},
CFRunLoopActivity::BeforeSources => {
if let Some((_, state)) = spans.borrow_mut().last_mut() {
*state = RunLoopState::Entered; // Drop any previous spans.
*state = RunLoopState::Sources(trace_span!("processing sources").entered());
} else {
error!("unbalanced observer invocations");
}
},
CFRunLoopActivity::BeforeWaiting => {
if let Some((_, state)) = spans.borrow_mut().last_mut() {
*state = RunLoopState::Entered; // Drop any previous spans.
*state = RunLoopState::Waiting(trace_span!("waiting").entered());
} else {
error!("unbalanced observer invocations");
}
},
activity => unreachable!("unexpected activity: {activity:?}"),
}
});
// An observer at the end of run loop activities.
let activities = CFRunLoopActivity::AfterWaiting | CFRunLoopActivity::Exit;
let end = MainRunLoopObserver::new(mtm, activities, true, CFIndex::MAX, move |activity| {
match activity {
CFRunLoopActivity::AfterWaiting => {
if let Some((_, state)) = spans_clone.borrow_mut().last_mut() {
// Transition from the waiting state to the initial state.
*state = RunLoopState::Entered;
} else {
error!("unbalanced observer invocations");
}
},
CFRunLoopActivity::Exit => {
if let Some((span, state)) = spans_clone.borrow_mut().pop() {
drop(state); // Explicitly exit and drop inner span.
drop(span); // Explicitly exit and drop outer span.
} else {
error!("unbalanced observer invocations");
}
},
activity => unreachable!("unexpected activity: {activity:?}"),
}
});
Some((start, end))
}

View File

@@ -79,6 +79,10 @@ impl EventHandler {
// Allowed, happens if the handler was cleared manually
// elsewhere (such as in `applicationWillTerminate:`).
},
// We use `eprintln!` here over `tracing::error!`, since we're going to abort
// immediately after this, and it'd be annoying for the user if they didn't get
// any feedback on that if they don't have a tracing subscriber.
#[allow(clippy::disallowed_macros)]
Err(_) => {
// Note: This is not expected to ever happen, this
// module generally controls the `RefCell`, and

View File

@@ -1,9 +1,4 @@
//! End user application handling.
#[cfg(not(target_family = "wasm"))]
use std::time::Instant;
#[cfg(target_family = "wasm")]
use web_time::Instant;
use crate::event::{DeviceEvent, DeviceId, StartCause, WindowEvent};
use crate::event_loop::ActiveEventLoop;
@@ -197,14 +192,10 @@ pub trait ApplicationHandler {
}
/// Emitted when the OS sends an event to a winit window.
///
/// Contains the ID of the window, the event, and the time the event was received. Note that
/// since events are queued, the time will differ from [`Instant::now()`].
fn window_event(
&mut self,
event_loop: &dyn ActiveEventLoop,
window_id: WindowId,
timestamp: Instant,
event: WindowEvent,
);
@@ -215,10 +206,9 @@ pub trait ApplicationHandler {
&mut self,
event_loop: &dyn ActiveEventLoop,
device_id: Option<DeviceId>,
timestamp: Instant,
event: DeviceEvent,
) {
let _ = (event_loop, device_id, timestamp, event);
let _ = (event_loop, device_id, event);
}
/// Emitted when the event loop is about to block and wait for new events.
@@ -382,10 +372,9 @@ impl<A: ?Sized + ApplicationHandler> ApplicationHandler for &mut A {
&mut self,
event_loop: &dyn ActiveEventLoop,
window_id: WindowId,
timestamp: Instant,
event: WindowEvent,
) {
(**self).window_event(event_loop, window_id, timestamp, event);
(**self).window_event(event_loop, window_id, event);
}
#[inline]
@@ -393,10 +382,9 @@ impl<A: ?Sized + ApplicationHandler> ApplicationHandler for &mut A {
&mut self,
event_loop: &dyn ActiveEventLoop,
device_id: Option<DeviceId>,
timestamp: Instant,
event: DeviceEvent,
) {
(**self).device_event(event_loop, device_id, timestamp, event);
(**self).device_event(event_loop, device_id, event);
}
#[inline]
@@ -452,10 +440,9 @@ impl<A: ?Sized + ApplicationHandler> ApplicationHandler for Box<A> {
&mut self,
event_loop: &dyn ActiveEventLoop,
window_id: WindowId,
timestamp: Instant,
event: WindowEvent,
) {
(**self).window_event(event_loop, window_id, timestamp, event);
(**self).window_event(event_loop, window_id, event);
}
#[inline]
@@ -463,10 +450,9 @@ impl<A: ?Sized + ApplicationHandler> ApplicationHandler for Box<A> {
&mut self,
event_loop: &dyn ActiveEventLoop,
device_id: Option<DeviceId>,
timestamp: Instant,
event: DeviceEvent,
) {
(**self).device_event(event_loop, device_id, timestamp, event);
(**self).device_event(event_loop, device_id, event);
}
#[inline]

View File

@@ -12,7 +12,8 @@ use objc2_ui_kit::{
UIApplicationWillResignActiveNotification, UIApplicationWillTerminateNotification, UIScreen,
};
use rwh_06::HasDisplayHandle;
use winit_common::core_foundation::{MainRunLoop, MainRunLoopObserver};
use tracing::debug_span;
use winit_common::core_foundation::{MainRunLoop, MainRunLoopObserver, tracing_observers};
use winit_core::application::ApplicationHandler;
use winit_core::cursor::{CustomCursor, CustomCursorSource};
use winit_core::error::{EventLoopError, NotSupportedError, RequestError};
@@ -134,6 +135,7 @@ pub struct EventLoop {
_will_terminate_observer: Retained<ProtocolObject<dyn NSObjectProtocol>>,
_did_receive_memory_warning_observer: Retained<ProtocolObject<dyn NSObjectProtocol>>,
_tracing_observers: Option<(MainRunLoopObserver, MainRunLoopObserver)>,
_wakeup_observer: MainRunLoopObserver,
_main_events_cleared_observer: MainRunLoopObserver,
_events_cleared_observer: MainRunLoopObserver,
@@ -159,6 +161,7 @@ impl EventLoop {
// `application:didFinishLaunchingWithOptions:`
unsafe { UIApplicationDidFinishLaunchingNotification },
move |_| {
let _entered = debug_span!("UIApplicationDidFinishLaunchingNotification").entered();
app_state::did_finish_launching(mtm);
},
);
@@ -166,19 +169,27 @@ impl EventLoop {
&center,
// `applicationDidBecomeActive:`
unsafe { UIApplicationDidBecomeActiveNotification },
move |_| app_state::handle_resumed(mtm),
move |_| {
let _entered = debug_span!("UIApplicationDidBecomeActiveNotification").entered();
app_state::handle_resumed(mtm)
},
);
let _will_resign_active_observer = create_observer(
&center,
// `applicationWillResignActive:`
unsafe { UIApplicationWillResignActiveNotification },
move |_| app_state::handle_suspended(mtm),
move |_| {
let _entered = debug_span!("UIApplicationWillResignActiveNotification").entered();
app_state::handle_suspended(mtm)
},
);
let _will_enter_foreground_observer = create_observer(
&center,
// `applicationWillEnterForeground:`
unsafe { UIApplicationWillEnterForegroundNotification },
move |notification| {
let _entered =
debug_span!("UIApplicationWillEnterForegroundNotification").entered();
let app = notification.object().expect(
"UIApplicationWillEnterForegroundNotification to have application object",
);
@@ -193,6 +204,7 @@ impl EventLoop {
// `applicationDidEnterBackground:`
unsafe { UIApplicationDidEnterBackgroundNotification },
move |notification| {
let _entered = debug_span!("UIApplicationDidEnterBackgroundNotification").entered();
let app = notification.object().expect(
"UIApplicationDidEnterBackgroundNotification to have application object",
);
@@ -207,6 +219,7 @@ impl EventLoop {
// `applicationWillTerminate:`
unsafe { UIApplicationWillTerminateNotification },
move |notification| {
let _entered = debug_span!("UIApplicationWillTerminateNotification").entered();
let app = notification
.object()
.expect("UIApplicationWillTerminateNotification to have application object");
@@ -220,18 +233,29 @@ impl EventLoop {
&center,
// `applicationDidReceiveMemoryWarning:`
unsafe { UIApplicationDidReceiveMemoryWarningNotification },
move |_| app_state::handle_memory_warning(mtm),
move |_| {
let _entered =
debug_span!("UIApplicationDidReceiveMemoryWarningNotification").entered();
app_state::handle_memory_warning(mtm)
},
);
let main_loop = MainRunLoop::get(mtm);
let mode = unsafe { kCFRunLoopDefaultMode }.unwrap();
// Tracing observers have the lowest and highest orderings.
let _tracing_observers = tracing_observers(mtm).inspect(|(start, end)| {
main_loop.add_observer(start, mode);
main_loop.add_observer(end, mode);
});
let _wakeup_observer = MainRunLoopObserver::new(
mtm,
CFRunLoopActivity::AfterWaiting,
true,
// Queued with the highest priority to ensure it is processed before other observers.
CFIndex::MIN,
// Queued with the second-highest priority (tracing observers use the highest) to
// ensure it is processed before other observers.
CFIndex::MIN + 1,
move |_| app_state::handle_wakeup_transition(mtm),
);
main_loop.add_observer(&_wakeup_observer, mode);
@@ -241,17 +265,17 @@ impl EventLoop {
CFRunLoopActivity::BeforeWaiting,
true,
// Core Animation registers its `CFRunLoopObserver` that performs drawing operations in
// `CA::Transaction::ensure_implicit` with a priority of `0x1e8480`. We set the
// `CA::Transaction::ensure_implicit` with a priority of `2000000`. We set the
// main_end priority to be 0, in order to send `AboutToWait` before `RedrawRequested`.
// This value was chosen conservatively to guard against apple using different
// priorities for their redraw observers in different OS's or on different devices. If
// it so happens that it's too conservative, the main symptom would be non-redraw
// events coming in after `AboutToWait`.
//
// The value of `0x1e8480` was determined by inspecting stack traces and the associated
// The value of `2000000` was determined by inspecting stack traces and the associated
// registers for every `CFRunLoopAddObserver` call on an iPad Air 2 running iOS 11.4.
//
// Also tested to be `0x1e8480` on iPhone 8, iOS 13 beta 4.
// Also tested to be `2000000` on iPhone 8, iOS 13 beta 4.
0,
move |_| app_state::handle_main_events_cleared(mtm),
);
@@ -261,8 +285,9 @@ impl EventLoop {
mtm,
CFRunLoopActivity::BeforeWaiting,
true,
// Queued with the lowest priority to ensure it is processed after other observers.
CFIndex::MAX,
// Queued with the second-lowest priority (tracing observers use the lowest) to ensure
// it is processed after other observers.
CFIndex::MAX - 1,
move |_| app_state::handle_events_cleared(mtm),
);
main_loop.add_observer(&_events_cleared_observer, mode);
@@ -277,6 +302,7 @@ impl EventLoop {
_did_enter_background_observer,
_will_terminate_observer,
_did_receive_memory_warning_observer,
_tracing_observers,
_wakeup_observer,
_main_events_cleared_observer,
_events_cleared_observer,

View File

@@ -13,7 +13,7 @@ use objc2_ui_kit::{
UIResponder, UIRotationGestureRecognizer, UITapGestureRecognizer, UITextInputTraits, UITouch,
UITouchPhase, UITouchType, UITraitEnvironment, UIView,
};
use tracing::debug;
use tracing::{debug, debug_span, trace_span};
use winit_core::event::{
ButtonSource, ElementState, FingerId, Force, KeyEvent, PointerKind, PointerSource,
TabletToolAngle, TabletToolButton, TabletToolData, TabletToolKind, TouchPhase, WindowEvent,
@@ -48,6 +48,7 @@ define_class!(
impl WinitView {
#[unsafe(method(drawRect:))]
fn draw_rect(&self, rect: CGRect) {
let _entered = debug_span!("drawRect:").entered();
let mtm = MainThreadMarker::new().unwrap();
let window = self.window().unwrap();
app_state::handle_nonuser_event(mtm, EventWrapper::Window {
@@ -59,6 +60,7 @@ define_class!(
#[unsafe(method(layoutSubviews))]
fn layout_subviews(&self) {
let _entered = debug_span!("layoutSubviews").entered();
let mtm = MainThreadMarker::new().unwrap();
let _: () = unsafe { msg_send![super(self), layoutSubviews] };
@@ -79,6 +81,7 @@ define_class!(
#[unsafe(method(setContentScaleFactor:))]
fn set_content_scale_factor(&self, untrusted_scale_factor: CGFloat) {
let _entered = debug_span!("setContentScaleFactor:").entered();
let mtm = MainThreadMarker::new().unwrap();
let _: () =
unsafe { msg_send![super(self), setContentScaleFactor: untrusted_scale_factor] };
@@ -124,6 +127,7 @@ define_class!(
#[unsafe(method(safeAreaInsetsDidChange))]
fn safe_area_changed(&self) {
let _entered = debug_span!("safeAreaInsetsDidChange").entered();
debug!("safeAreaInsetsDidChange was called, requesting redraw");
// When the safe area changes we want to make sure to emit a redraw event
self.setNeedsDisplay();
@@ -131,26 +135,31 @@ define_class!(
#[unsafe(method(touchesBegan:withEvent:))]
fn touches_began(&self, touches: &NSSet<UITouch>, _event: Option<&UIEvent>) {
let _entered = debug_span!("touchesBegan:withEvent:").entered();
self.handle_touches(touches)
}
#[unsafe(method(touchesMoved:withEvent:))]
fn touches_moved(&self, touches: &NSSet<UITouch>, _event: Option<&UIEvent>) {
let _entered = debug_span!("touchesMoved:withEvent:").entered();
self.handle_touches(touches)
}
#[unsafe(method(touchesEnded:withEvent:))]
fn touches_ended(&self, touches: &NSSet<UITouch>, _event: Option<&UIEvent>) {
let _entered = debug_span!("touchesEnded:withEvent:").entered();
self.handle_touches(touches)
}
#[unsafe(method(touchesCancelled:withEvent:))]
fn touches_cancelled(&self, touches: &NSSet<UITouch>, _event: Option<&UIEvent>) {
let _entered = debug_span!("touchesCancelled:withEvent:").entered();
self.handle_touches(touches)
}
#[unsafe(method(pinchGesture:))]
fn pinch_gesture(&self, recognizer: &UIPinchGestureRecognizer) {
let _entered = debug_span!("pinchGesture:").entered();
let window = self.window().unwrap();
let (phase, delta) = match recognizer.state() {
@@ -185,6 +194,7 @@ define_class!(
#[unsafe(method(doubleTapGesture:))]
fn double_tap_gesture(&self, recognizer: &UITapGestureRecognizer) {
let _entered = debug_span!("doubleTapGesture:").entered();
let window = self.window().unwrap();
if recognizer.state() == UIGestureRecognizerState::Ended {
@@ -200,6 +210,7 @@ define_class!(
#[unsafe(method(rotationGesture:))]
fn rotation_gesture(&self, recognizer: &UIRotationGestureRecognizer) {
let _entered = debug_span!("rotationGesture:").entered();
let window = self.window().unwrap();
let (phase, delta) = match recognizer.state() {
@@ -244,6 +255,7 @@ define_class!(
#[unsafe(method(panGesture:))]
fn pan_gesture(&self, recognizer: &UIPanGestureRecognizer) {
let _entered = debug_span!("panGesture:").entered();
let window = self.window().unwrap();
let translation = recognizer.translationInView(Some(self));
@@ -296,6 +308,7 @@ define_class!(
#[unsafe(method(canBecomeFirstResponder))]
fn can_become_first_responder(&self) -> bool {
let _entered = trace_span!("canBecomeFirstResponder").entered();
true
}
}
@@ -309,6 +322,10 @@ define_class!(
_gesture_recognizer: &UIGestureRecognizer,
_other_gesture_recognizer: &UIGestureRecognizer,
) -> bool {
let _entered = trace_span!(
"gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:"
)
.entered();
true
}
}
@@ -318,16 +335,19 @@ define_class!(
unsafe impl UIKeyInput for WinitView {
#[unsafe(method(hasText))]
fn has_text(&self) -> bool {
let _entered = debug_span!("hasText").entered();
true
}
#[unsafe(method(insertText:))]
fn insert_text(&self, text: &NSString) {
let _entered = debug_span!("insertText:").entered();
self.handle_insert_text(text)
}
#[unsafe(method(deleteBackward))]
fn delete_backward(&self) {
let _entered = debug_span!("deleteBackward").entered();
self.handle_delete_backward()
}
}

View File

@@ -7,6 +7,7 @@ use objc2_ui_kit::{
UIDevice, UIInterfaceOrientationMask, UIRectEdge, UIResponder, UIStatusBarStyle,
UIUserInterfaceIdiom, UIView, UIViewController,
};
use tracing::trace_span;
use crate::{ScreenEdge, StatusBarStyle, ValidOrientations, WindowAttributesIos};
@@ -28,31 +29,37 @@ define_class!(
impl WinitViewController {
#[unsafe(method(shouldAutorotate))]
fn should_autorotate(&self) -> bool {
let _entered = trace_span!("shouldAutorotate").entered();
true
}
#[unsafe(method(prefersStatusBarHidden))]
fn prefers_status_bar_hidden(&self) -> bool {
let _entered = trace_span!("prefersStatusBarHidden").entered();
self.ivars().prefers_status_bar_hidden.get()
}
#[unsafe(method(preferredStatusBarStyle))]
fn preferred_status_bar_style(&self) -> UIStatusBarStyle {
let _entered = trace_span!("preferredStatusBarStyle").entered();
self.ivars().preferred_status_bar_style.get()
}
#[unsafe(method(prefersHomeIndicatorAutoHidden))]
fn prefers_home_indicator_auto_hidden(&self) -> bool {
let _entered = trace_span!("prefersHomeIndicatorAutoHidden").entered();
self.ivars().prefers_home_indicator_auto_hidden.get()
}
#[unsafe(method(supportedInterfaceOrientations))]
fn supported_orientations(&self) -> UIInterfaceOrientationMask {
let _entered = trace_span!("supportedInterfaceOrientations").entered();
self.ivars().supported_orientations.get()
}
#[unsafe(method(preferredScreenEdgesDeferringSystemGestures))]
fn preferred_screen_edges_deferring_system_gestures(&self) -> UIRectEdge {
let _entered = trace_span!("preferredScreenEdgesDeferringSystemGestures").entered();
self.ivars().preferred_screen_edges_deferring_system_gestures.get()
}
}

View File

@@ -16,7 +16,7 @@ use objc2_ui_kit::{
UIApplication, UICoordinateSpace, UIEdgeInsets, UIResponder, UIScreen,
UIScreenOverscanCompensation, UIViewController, UIWindow,
};
use tracing::{debug, warn};
use tracing::{debug, debug_span, warn};
use winit_core::cursor::Cursor;
use winit_core::error::{NotSupportedError, RequestError};
use winit_core::event::WindowEvent;
@@ -46,6 +46,7 @@ define_class!(
impl WinitUIWindow {
#[unsafe(method(becomeKeyWindow))]
fn become_key_window(&self) {
let _entered = debug_span!("becomeKeyWindow").entered();
let mtm = MainThreadMarker::new().unwrap();
app_state::handle_nonuser_event(mtm, EventWrapper::Window {
window_id: self.id(),
@@ -56,6 +57,7 @@ define_class!(
#[unsafe(method(resignKeyWindow))]
fn resign_key_window(&self) {
let _entered = debug_span!("resignKeyWindow").entered();
let mtm = MainThreadMarker::new().unwrap();
app_state::handle_nonuser_event(mtm, EventWrapper::Window {
window_id: self.id(),

View File

@@ -387,11 +387,7 @@ impl LayoutCache {
let unicode = Self::to_unicode_string(&key_state, vk, scancode, locale_id);
let key = match unicode {
ToUnicodeResult::Str(str) => Key::Character(SmolStr::new(str)),
ToUnicodeResult::Dead(dead_char) => {
// println!("{:?} - {:?} produced dead {:?}", key_code, mod_state,
// dead_char);
Key::Dead(dead_char)
},
ToUnicodeResult::Dead(dead_char) => Key::Dead(dead_char),
ToUnicodeResult::None => {
let has_alt = mod_state.contains(WindowsModifiers::ALT);
let has_ctrl = mod_state.contains(WindowsModifiers::CONTROL);

View File

@@ -66,7 +66,6 @@ impl XConnection {
// All util functions that abstract an async function will return a `Flusher`.
pub fn flush_requests(&self) -> Result<(), XError> {
unsafe { (self.xlib.XFlush)(self.display) };
// println!("XFlush");
// This isn't necessarily a useful time to check for errors (since our request hasn't
// necessarily been processed yet)
self.check_errors()
@@ -74,7 +73,6 @@ impl XConnection {
pub fn sync_with_server(&self) -> Result<(), XError> {
unsafe { (self.xlib.XSync)(self.display, ffi::False) };
// println!("XSync");
self.check_errors()
}
}

View File

@@ -1,7 +1,9 @@
use cfg_aliases::cfg_aliases;
// Only relevant for examples and Winit, our usage of println! is fine here.
#[allow(clippy::disallowed_macros)]
fn main() {
// The script doesn't depend on our code.
// Dummy invocation to enable change-tracking in build scripts.
println!("cargo:rerun-if-changed=build.rs");
// Setup cfg aliases.

View File

@@ -3,6 +3,7 @@
fn main() -> Result<(), impl std::error::Error> {
use std::collections::HashMap;
use tracing::info;
use winit::application::ApplicationHandler;
use winit::dpi::{LogicalPosition, LogicalSize, Position};
use winit::event::{ElementState, KeyEvent, WindowEvent};
@@ -38,7 +39,7 @@ fn main() -> Result<(), impl std::error::Error> {
.with_position(Position::Logical(LogicalPosition::new(0.0, 0.0)))
.with_surface_size(LogicalSize::new(640.0f32, 480.0f32));
let window = event_loop.create_window(attributes).unwrap();
println!("Parent window id: {:?})", window.id());
info!("Parent window id: {:?})", window.id());
self.parent_window_id = Some(window.id());
self.windows.insert(window.id(), WindowData::new(window, 0xffbbbbbb));
@@ -56,12 +57,12 @@ fn main() -> Result<(), impl std::error::Error> {
event_loop.exit();
},
WindowEvent::PointerEntered { device_id: _, .. } => {
// On x11, println when the cursor entered in a window even if the child window
// On x11, log 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:?}");
info!("cursor entered in the window {window_id:?}");
},
WindowEvent::KeyboardInput {
event: KeyEvent { state: ElementState::Pressed, .. },
@@ -75,7 +76,7 @@ fn main() -> Result<(), impl std::error::Error> {
let child_window =
spawn_child_window(parent_window.window.as_ref(), event_loop, child_index);
let child_id = child_window.id();
println!("Child window created with id: {child_id:?}");
info!("Child window created with id: {child_id:?}");
self.windows.insert(child_id, WindowData::new(child_window, child_color));
},
WindowEvent::RedrawRequested => {

View File

@@ -4,7 +4,7 @@ use std::thread;
#[cfg(not(web_platform))]
use std::time;
use ::tracing::{info, warn};
use tracing::{info, warn};
#[cfg(web_platform)]
use web_time as time;
use winit::application::ApplicationHandler;

View File

@@ -1,5 +1,6 @@
use std::error::Error;
use tracing::info;
use winit::application::ApplicationHandler;
use winit::event::WindowEvent;
use winit::event_loop::{ActiveEventLoop, EventLoop};
@@ -49,7 +50,7 @@ impl ApplicationHandler for Application {
| WindowEvent::DragEntered { .. }
| WindowEvent::DragMoved { .. }
| WindowEvent::DragDropped { .. } => {
println!("{event:?}");
info!("{event:?}");
},
WindowEvent::RedrawRequested => {
let window = self.window.as_ref().unwrap();

View File

@@ -76,7 +76,7 @@ impl ApplicationHandler for App {
self.window = match event_loop.create_window(window_attributes) {
Ok(window) => Some(window),
Err(err) => {
eprintln!("error creating window: {err}");
error!("error creating window: {err}");
event_loop.exit();
return;
},
@@ -346,7 +346,7 @@ fn main() -> Result<(), Box<dyn Error>> {
let event_loop = EventLoop::new()?;
println!(
info!(
r#"This showcases the use of an input method engine (IME) by emulating a text edit field.
Use CTRL+i to toggle IME support.
Use CTRL+p to cycle content purpose values.

View File

@@ -7,6 +7,7 @@ fn main() -> std::process::ExitCode {
use std::thread::sleep;
use std::time::Duration;
use tracing::info;
use winit::application::ApplicationHandler;
use winit::event::WindowEvent;
use winit::event_loop::pump_events::{EventLoopExtPumpEvents, PumpStatus};
@@ -15,6 +16,8 @@ fn main() -> std::process::ExitCode {
#[path = "util/fill.rs"]
mod fill;
#[path = "util/tracing.rs"]
mod tracing;
#[derive(Default, Debug)]
struct PumpDemo {
@@ -33,7 +36,7 @@ fn main() -> std::process::ExitCode {
_window_id: WindowId,
event: WindowEvent,
) {
println!("{event:?}");
info!("{event:?}");
let window = match self.window.as_ref() {
Some(window) => window,
@@ -51,9 +54,9 @@ fn main() -> std::process::ExitCode {
}
}
let mut event_loop = EventLoop::new().unwrap();
tracing::init();
tracing_subscriber::fmt::init();
let mut event_loop = EventLoop::new().unwrap();
let mut app = PumpDemo::default();
@@ -69,12 +72,12 @@ fn main() -> std::process::ExitCode {
//
// Since `pump_events` doesn't block it will be important to
// throttle the loop in the app somehow.
println!("Update()");
info!("Update()");
sleep(Duration::from_millis(16));
}
}
#[cfg(any(ios_platform, web_platform, orbital_platform))]
fn main() {
println!("This platform doesn't support pump_events.");
panic!("This platform doesn't support pump_events.")
}

View File

@@ -5,6 +5,7 @@
fn main() -> Result<(), Box<dyn std::error::Error>> {
use std::time::Duration;
use tracing::info;
use winit::application::ApplicationHandler;
use winit::event::WindowEvent;
use winit::event_loop::run_on_demand::EventLoopExtRunOnDemand;
@@ -13,6 +14,8 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
#[path = "util/fill.rs"]
mod fill;
#[path = "util/tracing.rs"]
mod tracing;
#[derive(Default, Debug)]
struct App {
@@ -44,10 +47,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
event: WindowEvent,
) {
if event == WindowEvent::Destroyed && self.window_id == Some(window_id) {
println!(
"--------------------------------------------------------- Window {} Destroyed",
self.idx
);
info!("Window {} Destroyed", self.idx);
self.window_id = None;
event_loop.exit();
return;
@@ -60,11 +60,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
match event {
WindowEvent::CloseRequested => {
println!(
"--------------------------------------------------------- Window {} \
CloseRequested",
self.idx
);
info!("Window {} CloseRequested", self.idx);
fill::cleanup_window(window.as_ref());
self.window = None;
},
@@ -76,20 +72,20 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
}
}
tracing_subscriber::fmt::init();
tracing::init();
let mut event_loop = EventLoop::new().unwrap();
let mut app = App { idx: 1, ..Default::default() };
event_loop.run_app_on_demand(&mut app)?;
println!("--------------------------------------------------------- Finished first loop");
println!("--------------------------------------------------------- Waiting 5 seconds");
info!("Finished first loop");
info!("Waiting 5 seconds");
std::thread::sleep(Duration::from_secs(5));
app.idx += 1;
event_loop.run_app_on_demand(&mut app)?;
println!("--------------------------------------------------------- Finished second loop");
info!("Finished second loop");
Ok(())
}
@@ -101,5 +97,5 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
orbital_platform
)))]
fn main() {
println!("This example is not supported on this platform");
panic!("This example is not supported on this platform")
}

View File

@@ -23,3 +23,6 @@ pub fn init() {
)
.init();
}
#[allow(unused_imports)]
pub use ::tracing::*;

View File

@@ -1,8 +1,8 @@
//! Simple winit window example.
use std::error::Error;
use std::time::Instant;
use tracing::{error, info};
use winit::application::ApplicationHandler;
use winit::event::WindowEvent;
use winit::event_loop::{ActiveEventLoop, EventLoop};
@@ -30,24 +30,18 @@ impl ApplicationHandler for App {
self.window = match event_loop.create_window(window_attributes) {
Ok(window) => Some(window),
Err(err) => {
eprintln!("error creating window: {err}");
error!("error creating window: {err}");
event_loop.exit();
return;
},
}
}
fn window_event(
&mut self,
event_loop: &dyn ActiveEventLoop,
_: WindowId,
timestamp: Instant,
event: WindowEvent,
) {
::tracing::info!("{:?}: {event:?}", timestamp.elapsed());
fn window_event(&mut self, event_loop: &dyn ActiveEventLoop, _: WindowId, event: WindowEvent) {
info!("{event:?}");
match event {
WindowEvent::CloseRequested => {
println!("Close was requested; stopping");
info!("Close was requested; stopping");
event_loop.exit();
},
WindowEvent::SurfaceResized(_) => {
@@ -69,21 +63,11 @@ impl ApplicationHandler for App {
fill::fill_window(window.as_ref());
// For contiguous redraw loop you can request a redraw from here.
window.request_redraw();
// window.request_redraw();
},
_ => (),
}
}
fn device_event(
&mut self,
_event_loop: &dyn ActiveEventLoop,
_device_id: Option<winit::event::DeviceId>,
timestamp: Instant,
event: winit::event::DeviceEvent,
) {
::tracing::info!("{:?}: {event:?}", timestamp.elapsed());
}
}
fn main() -> Result<(), Box<dyn Error>> {

View File

@@ -11,6 +11,8 @@ fn main() -> Result<(), Box<dyn Error>> {
#[path = "util/fill.rs"]
mod fill;
#[path = "util/tracing.rs"]
mod tracing;
#[derive(Debug)]
pub struct XEmbedDemo {
@@ -58,7 +60,7 @@ fn main() -> Result<(), Box<dyn Error>> {
.ok_or("Expected a 32-bit X11 window ID as the first argument.")?
.parse::<u32>()?;
tracing_subscriber::fmt::init();
tracing::init();
let event_loop = EventLoop::new()?;
Ok(event_loop.run_app(XEmbedDemo { parent_window_id, window: None })?)
@@ -66,6 +68,5 @@ fn main() -> Result<(), Box<dyn Error>> {
#[cfg(not(x11_platform))]
fn main() -> Result<(), Box<dyn Error>> {
println!("This example is only supported on X11 platforms.");
Ok(())
panic!("This example is only supported on X11 platforms.")
}

View File

@@ -46,11 +46,13 @@ changelog entry.
- On iOS, add Apple Pencil support with force, altitude, and azimuth data.
- On Redox, add support for missing keyboard scancodes.
- Implement `Send` and `Sync` for `OwnedDisplayHandle`.
- Use new macOS 15 cursors for resize icons.
### Changed
- Updated `windows-sys` to `v0.61`.
- On older macOS versions (tested up to 12.7.6), applications now receive mouse movement events for unfocused windows, matching the behavior on other platforms.
- On macOS, the application is now launched in `EventLoop::new` instead of `EventLoop::run_app`. If you're registering a custom delegate, you should now register it before `EventLoop::new`.
### Fixed

View File

@@ -75,6 +75,9 @@ impl EventLoopBuilder {
/// `DISPLAY` respectively when building the event loop.
/// - **Android:** must be configured with an `AndroidApp` from `android_main()` by calling
/// [`.with_android_app(app)`] before calling `.build()`, otherwise it'll panic.
/// - **macOS:** this will launch the application, so if you want to register a custom delegate,
/// or otherwise do stuff before `applicationDidFinishLaunching:`, you should do it before
/// this function is called.
///
/// [`platform`]: crate::platform
#[cfg_attr(
@@ -88,7 +91,7 @@ impl EventLoopBuilder {
)]
#[inline]
pub fn build(&mut self) -> Result<EventLoop, EventLoopError> {
let _span = tracing::debug_span!("winit::EventLoopBuilder::build").entered();
let _entered = tracing::debug_span!("winit::EventLoopBuilder::build").entered();
// Certain platforms accept a mutable reference in their API.
#[allow(clippy::unnecessary_mut_passed)]
@@ -262,7 +265,7 @@ impl EventLoop {
///
/// [`DeviceEvent`]: crate::event::DeviceEvent
pub fn listen_device_events(&self, allowed: DeviceEvents) {
let _span = tracing::debug_span!(
let _entered = tracing::debug_span!(
"winit::EventLoop::listen_device_events",
allowed = ?allowed
)