Compare commits

..

1 Commits

Author SHA1 Message Date
Mads Marquart
99f7bb6b44 Add timestamps to events 2026-03-18 18:18:25 +01:00
35 changed files with 432 additions and 678 deletions

View File

@@ -60,6 +60,7 @@ 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,11 +1,4 @@
# 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,6 +107,7 @@ 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,7 +9,6 @@ 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;
@@ -22,10 +21,6 @@ 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.
@@ -103,6 +98,7 @@ 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 {
@@ -115,7 +111,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, DeviceEvent::PointerMotion {
app.device_event(event_loop, None, time, DeviceEvent::PointerMotion {
delta: (delta_x, delta_y),
});
});
@@ -124,7 +120,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, DeviceEvent::Button {
app.device_event(event_loop, None, time, DeviceEvent::Button {
button,
state: ElementState::Pressed,
});
@@ -133,7 +129,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, DeviceEvent::Button {
app.device_event(event_loop, None, time, DeviceEvent::Button {
button,
state: ElementState::Released,
});

View File

@@ -2,11 +2,14 @@ use std::cell::{Cell, OnceCell, RefCell};
use std::mem;
use std::rc::Rc;
use std::sync::Arc;
use std::time::Instant;
use std::time::{Duration, Instant};
use dispatch2::MainThreadBound;
use objc2::MainThreadMarker;
use objc2_app_kit::NSApplication;
use objc2_app_kit::{NSApplication, NSApplicationActivationPolicy, NSEvent, NSRunningApplication};
use objc2_foundation::{NSNotification, NSTimeInterval};
use objc2_quartz_core::CACurrentMediaTime;
use tracing::warn;
use winit_common::core_foundation::{EventLoopProxy, MainRunLoop};
use winit_common::event_handler::EventHandler;
use winit_core::application::ApplicationHandler;
@@ -15,17 +18,24 @@ 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.
@@ -35,6 +45,8 @@ 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.
}
@@ -45,19 +57,40 @@ static GLOBAL: MainThreadBound<OnceCell<Rc<AppState>>> =
MainThreadBound::new(OnceCell::new(), unsafe { MainThreadMarker::new_unchecked() });
impl AppState {
pub(super) fn setup_global(mtm: MainThreadMarker) -> Option<Rc<Self>> {
pub(super) fn setup_global(
mtm: MainThreadMarker,
activation_policy: Option<NSApplicationActivationPolicy>,
default_menu: bool,
activate_ignoring_other_apps: bool,
) -> 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()),
@@ -65,6 +98,8 @@ 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))
@@ -78,6 +113,81 @@ 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>(
@@ -88,14 +198,17 @@ impl AppState {
self.event_handler.set(Box::new(handler), closure)
}
pub fn terminate_event_handler(&self) {
self.event_handler.terminate();
}
pub fn event_loop_proxy(&self) -> &Arc<EventLoopProxy> {
&self.event_loop_proxy
}
/// 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 set_stop_before_wait(&self, value: bool) {
self.stop_before_wait.set(value)
}
@@ -124,6 +237,10 @@ 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)
}
@@ -157,7 +274,12 @@ 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, WindowEvent::RedrawRequested);
app.window_event(
event_loop,
window_id,
Instant::now(),
WindowEvent::RedrawRequested,
);
});
// `pump_events` will request to stop immediately _after_ dispatching RedrawRequested
@@ -257,7 +379,12 @@ 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, WindowEvent::RedrawRequested);
app.window_event(
event_loop,
window_id,
Instant::now(),
WindowEvent::RedrawRequested,
);
});
}
self.with_handler(|app, event_loop| {

View File

@@ -5,10 +5,7 @@ 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, NSCursorFrameResizeDirections, NSCursorFrameResizePosition,
NSDeviceRGBColorSpace, NSImage,
};
use objc2_app_kit::{NSBitmapImageRep, NSCursor, NSDeviceRGBColorSpace, NSImage};
use objc2_foundation::{
NSData, NSDictionary, NSNumber, NSObject, NSPoint, NSSize, NSString, ns_string,
};
@@ -207,155 +204,23 @@ 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 => {
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::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::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,13 +7,12 @@ use objc2::runtime::ProtocolObject;
use objc2::{MainThreadMarker, available};
use objc2_app_kit::{
NSApplication, NSApplicationActivationPolicy, NSApplicationDidFinishLaunchingNotification,
NSApplicationWillTerminateNotification, NSRunningApplication, NSWindow,
NSApplicationWillTerminateNotification, NSWindow,
};
use objc2_core_foundation::{CFIndex, CFRunLoopActivity, kCFRunLoopCommonModes};
use objc2_foundation::{NSNotificationCenter, NSObjectProtocol};
use rwh_06::HasDisplayHandle;
use tracing::debug_span;
use winit_common::core_foundation::{MainRunLoop, MainRunLoopObserver, tracing_observers};
use winit_common::core_foundation::{MainRunLoop, MainRunLoopObserver};
use winit_core::application::ApplicationHandler;
use winit_core::cursor::{CustomCursor as CoreCustomCursor, CustomCursorSource};
use winit_core::error::{EventLoopError, RequestError};
@@ -31,8 +30,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 {
@@ -150,9 +149,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,
}
@@ -175,8 +174,20 @@ impl EventLoop {
let mtm = MainThreadMarker::new()
.expect("on macOS, `EventLoop` must be created on the main thread!");
let app_state =
AppState::setup_global(mtm).ok_or_else(|| EventLoopError::RecreationAttempt)?;
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)?;
// Initialize the application (if it has not already been).
let app = NSApplication::sharedApplication(mtm);
@@ -186,50 +197,41 @@ impl EventLoop {
let center = NSNotificationCenter::defaultCenter();
// 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(
let _did_finish_launching_observer = create_observer(
&center,
unsafe { NSApplicationWillTerminateNotification },
// `applicationDidFinishLaunching:`
unsafe { NSApplicationDidFinishLaunchingNotification },
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.terminate_event_handler();
app_state.internal_exit();
app_state.did_finish_launching(notification);
}
},
);
let weak_app_state = Rc::downgrade(&app_state);
let _will_terminate_observer = create_observer(
&center,
// `applicationWillTerminate:`
unsafe { NSApplicationWillTerminateNotification },
move |notification| {
if let Some(app_state) = weak_app_state.upgrade() {
app_state.will_terminate(notification);
}
},
);
// 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 second-lowest priority (tracing observers use the lowest) to ensure
// it is processed after other observers.
CFIndex::MAX - 1,
// Queued with the lowest priority to ensure it is processed after other observers.
// Without that, we'd get a `LoopExiting` after `AboutToWait`.
CFIndex::MAX,
move |_| app_state_clone.cleared(),
);
main_loop.add_observer(&_before_waiting_observer, mode);
@@ -239,109 +241,18 @@ impl EventLoop {
mtm,
CFRunLoopActivity::AfterWaiting,
true,
// Queued with the second-highest priority (tracing observers use the highest) to
// ensure it is processed before other observers.
CFIndex::MIN + 1,
// Queued with the highest priority to ensure it is processed before other observers.
CFIndex::MIN,
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,
})
@@ -359,7 +270,6 @@ 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(|_| {
@@ -369,9 +279,11 @@ impl EventLoop {
self.app_state.set_stop_after_wait(false);
self.app_state.set_stop_on_redraw(false);
debug_assert!(!self.app_state.is_running());
self.app_state.set_is_running(true);
self.app_state.dispatch_init_events();
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();
}
// NOTE: Make sure to not run the application re-entrantly, as that'd be confusing.
self.app.run();
@@ -388,10 +300,19 @@ impl EventLoop {
timeout: Option<Duration>,
app: A,
) -> PumpStatus {
let _entered = debug_span!("pump_app_events").entered();
self.app_state.set_event_handler(app, || {
autoreleasepool(|_| {
if !self.app_state.is_running() {
// 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() {
// 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,13 +50,15 @@
//! }
//!
//! fn main() -> Result<(), Box<dyn std::error::Error>> {
//! // Register the delegate before Winit gets a chance to touch things.
//! let event_loop = EventLoop::new()?;
//!
//! 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,10 +1,37 @@
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,11 +2,12 @@
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, define_class, msg_send};
use objc2::{AnyThread, DefinedClass, MainThreadMarker, MainThreadOnly, define_class, msg_send};
use objc2_app_kit::{
NSApplication, NSCursor, NSEvent, NSEventPhase, NSResponder, NSTextInputClient, NSTrackingArea,
NSTrackingAreaOptions, NSView, NSWindow,
@@ -16,7 +17,7 @@ use objc2_foundation::{
NSArray, NSAttributedString, NSAttributedStringKey, NSCopying, NSMutableAttributedString,
NSNotFound, NSObject, NSPoint, NSRange, NSRect, NSSize, NSString, NSUInteger,
};
use tracing::{debug_span, trace_span};
use tracing::warn;
use winit_core::event::{
DeviceEvent, ElementState, Ime, KeyEvent, Modifiers, MouseButton, MouseScrollDelta,
PointerKind, PointerSource, TouchPhase, WindowEvent,
@@ -154,7 +155,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>) {
let _entered = debug_span!("NSViewFrameDidChangeNotification").entered();
trace_scope!("NSViewFrameDidChangeNotification");
// 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
@@ -169,7 +170,7 @@ define_class!(
#[unsafe(method(drawRect:))]
fn draw_rect(&self, _rect: NSRect) {
let _entered = debug_span!("drawRect:").entered();
trace_scope!("drawRect:");
self.ivars().app_state.handle_redraw(window_id(&self.window()));
@@ -178,7 +179,7 @@ define_class!(
#[unsafe(method(acceptsFirstResponder))]
fn accepts_first_responder(&self) -> bool {
let _entered = trace_span!("acceptsFirstResponder").entered();
trace_scope!("acceptsFirstResponder");
true
}
@@ -192,13 +193,13 @@ define_class!(
// extension for using `NSTouchBar`
#[unsafe(method_id(touchBar))]
fn touch_bar(&self) -> Option<Retained<NSObject>> {
let _entered = debug_span!("touchBar").entered();
trace_scope!("touchBar");
None
}
#[unsafe(method(resetCursorRects))]
fn reset_cursor_rects(&self) {
let _entered = debug_span!("resetCursorRects").entered();
trace_scope!("resetCursorRects");
let bounds = self.bounds();
let cursor_state = self.ivars().cursor_state.borrow();
// We correctly invoke `addCursorRect` only from inside `resetCursorRects`
@@ -213,13 +214,13 @@ define_class!(
unsafe impl NSTextInputClient for WinitView {
#[unsafe(method(hasMarkedText))]
fn has_marked_text(&self) -> bool {
let _entered = debug_span!("hasMarkedText").entered();
trace_scope!("hasMarkedText");
self.ivars().marked_text.borrow().length() > 0
}
#[unsafe(method(markedRange))]
fn marked_range(&self) -> NSRange {
let _entered = debug_span!("markedRange").entered();
trace_scope!("markedRange");
let length = self.ivars().marked_text.borrow().length();
if length > 0 {
NSRange::new(0, length)
@@ -231,7 +232,7 @@ define_class!(
#[unsafe(method(selectedRange))]
fn selected_range(&self) -> NSRange {
let _entered = debug_span!("selectedRange").entered();
trace_scope!("selectedRange");
// Documented to return `{NSNotFound, 0}` if there is no selection.
NSRange::new(NSNotFound as NSUInteger, 0)
}
@@ -244,7 +245,7 @@ define_class!(
_replacement_range: NSRange,
) {
// TODO: Use _replacement_range, requires changing the event to report surrounding text.
let _entered = debug_span!("setMarkedText:selectedRange:replacementRange:").entered();
trace_scope!("setMarkedText:selectedRange:replacementRange:");
let (marked_text, string) = if let Some(string) =
string.downcast_ref::<NSAttributedString>()
@@ -298,7 +299,7 @@ define_class!(
#[unsafe(method(unmarkText))]
fn unmark_text(&self) {
let _entered = debug_span!("unmarkText").entered();
trace_scope!("unmarkText");
*self.ivars().marked_text.borrow_mut() = NSMutableAttributedString::new();
let input_context = self.inputContext().expect("input context");
@@ -315,7 +316,7 @@ define_class!(
#[unsafe(method_id(validAttributesForMarkedText))]
fn valid_attributes_for_marked_text(&self) -> Retained<NSArray<NSAttributedStringKey>> {
let _entered = trace_span!("validAttributesForMarkedText").entered();
trace_scope!("validAttributesForMarkedText");
NSArray::new()
}
@@ -325,14 +326,13 @@ define_class!(
_range: NSRange,
_actual_range: *mut NSRange,
) -> Option<Retained<NSAttributedString>> {
let _entered =
trace_span!("attributedSubstringForProposedRange:actualRange:").entered();
trace_scope!("attributedSubstringForProposedRange:actualRange:");
None
}
#[unsafe(method(characterIndexForPoint:))]
fn character_index_for_point(&self, _point: NSPoint) -> NSUInteger {
let _entered = debug_span!("characterIndexForPoint:").entered();
trace_scope!("characterIndexForPoint:");
0
}
@@ -342,7 +342,7 @@ define_class!(
_range: NSRange,
_actual_range: *mut NSRange,
) -> NSRect {
let _entered = debug_span!("firstRectForCharacterRange:actualRange:").entered();
trace_scope!("firstRectForCharacterRange:actualRange:");
// 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.
let _entered = debug_span!("insertText:replacementRange:").entered();
trace_scope!("insertText:replacementRange:");
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) {
let _entered = debug_span!("doCommandBySelector:").entered();
trace_scope!("doCommandBySelector:");
// 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) {
let _entered = debug_span!("keyDown:").entered();
trace_scope!("keyDown:");
{
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) {
let _entered = debug_span!("keyUp:").entered();
trace_scope!("keyUp:");
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) {
let _entered = debug_span!("flagsChanged:").entered();
trace_scope!("flagsChanged:");
self.update_modifiers(event, true);
}
#[unsafe(method(insertTab:))]
fn insert_tab(&self, _sender: Option<&AnyObject>) {
let _entered = debug_span!("insertTab:").entered();
trace_scope!("insertTab:");
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>) {
let _entered = debug_span!("insertBackTab:").entered();
trace_scope!("insertBackTab:");
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);
let _entered = debug_span!("cancelOperation:").entered();
trace_scope!("cancelOperation:");
let event = NSApplication::sharedApplication(mtm)
.currentEvent()
@@ -559,73 +559,71 @@ define_class!(
#[unsafe(method(mouseDown:))]
fn mouse_down(&self, event: &NSEvent) {
let _entered = debug_span!("mouseDown:").entered();
trace_scope!("mouseDown:");
self.mouse_motion(event);
self.mouse_click(event, ElementState::Pressed);
}
#[unsafe(method(mouseUp:))]
fn mouse_up(&self, event: &NSEvent) {
let _entered = debug_span!("mouseUp:").entered();
trace_scope!("mouseUp:");
self.mouse_motion(event);
self.mouse_click(event, ElementState::Released);
}
#[unsafe(method(rightMouseDown:))]
fn right_mouse_down(&self, event: &NSEvent) {
let _entered = debug_span!("rightMouseDown:").entered();
trace_scope!("rightMouseDown:");
self.mouse_motion(event);
self.mouse_click(event, ElementState::Pressed);
}
#[unsafe(method(rightMouseUp:))]
fn right_mouse_up(&self, event: &NSEvent) {
let _entered = debug_span!("rightMouseUp:").entered();
trace_scope!("rightMouseUp:");
self.mouse_motion(event);
self.mouse_click(event, ElementState::Released);
}
#[unsafe(method(otherMouseDown:))]
fn other_mouse_down(&self, event: &NSEvent) {
let _entered = debug_span!("otherMouseDown:").entered();
trace_scope!("otherMouseDown:");
self.mouse_motion(event);
self.mouse_click(event, ElementState::Pressed);
}
#[unsafe(method(otherMouseUp:))]
fn other_mouse_up(&self, event: &NSEvent) {
let _entered = debug_span!("otherMouseUp:").entered();
trace_scope!("otherMouseUp:");
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) {
let _entered = debug_span!("mouseEntered:").entered();
trace_scope!("mouseEntered:");
let position = self.mouse_view_point(event).to_physical(self.scale_factor());
@@ -639,7 +637,7 @@ define_class!(
#[unsafe(method(mouseExited:))]
fn mouse_exited(&self, event: &NSEvent) {
let _entered = debug_span!("mouseExited:").entered();
trace_scope!("mouseExited:");
let position = self.mouse_view_point(event).to_physical(self.scale_factor());
@@ -653,7 +651,7 @@ define_class!(
#[unsafe(method(scrollWheel:))]
fn scroll_wheel(&self, event: &NSEvent) {
let _entered = debug_span!("scrollWheel:").entered();
trace_scope!("scrollWheel:");
self.mouse_motion(event);
@@ -684,15 +682,16 @@ 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, DeviceEvent::MouseWheel { delta })
app.device_event(event_loop, None, time, DeviceEvent::MouseWheel { delta })
});
self.queue_event(WindowEvent::MouseWheel { device_id: None, delta, phase });
}
#[unsafe(method(magnifyWithEvent:))]
fn magnify_with_event(&self, event: &NSEvent) {
let _entered = debug_span!("magnifyWithEvent:").entered();
trace_scope!("magnifyWithEvent:");
self.mouse_motion(event);
@@ -714,7 +713,7 @@ define_class!(
#[unsafe(method(smartMagnifyWithEvent:))]
fn smart_magnify_with_event(&self, event: &NSEvent) {
let _entered = debug_span!("smartMagnifyWithEvent:").entered();
trace_scope!("smartMagnifyWithEvent:");
self.mouse_motion(event);
@@ -723,7 +722,7 @@ define_class!(
#[unsafe(method(rotateWithEvent:))]
fn rotate_with_event(&self, event: &NSEvent) {
let _entered = debug_span!("rotateWithEvent:").entered();
trace_scope!("rotateWithEvent:");
self.mouse_motion(event);
@@ -745,7 +744,7 @@ define_class!(
#[unsafe(method(pressureChangeWithEvent:))]
fn pressure_change_with_event(&self, event: &NSEvent) {
let _entered = debug_span!("pressureChangeWithEvent:").entered();
trace_scope!("pressureChangeWithEvent:");
self.queue_event(WindowEvent::TouchpadPressure {
device_id: None,
@@ -759,13 +758,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 {
let _entered = debug_span!("_wantsKeyDownForEvent:").entered();
trace_scope!("_wantsKeyDownForEvent:");
true
}
#[unsafe(method(acceptsFirstMouse:))]
fn accepts_first_mouse(&self, _event: &NSEvent) -> bool {
let _entered = debug_span!("acceptsFirstMouse:").entered();
trace_scope!("acceptsFirstMouse:");
self.ivars().accepts_first_mouse
}
}
@@ -848,9 +847,16 @@ 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, event);
app.window_event(event_loop, window_id, time, event);
});
}

View File

@@ -8,7 +8,6 @@ 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;
@@ -351,13 +350,13 @@ define_class!(
impl WinitWindow {
#[unsafe(method(canBecomeMainWindow))]
fn can_become_main_window(&self) -> bool {
let _entered = trace_span!("canBecomeMainWindow").entered();
trace_scope!("canBecomeMainWindow");
true
}
#[unsafe(method(canBecomeKeyWindow))]
fn can_become_key_window(&self) -> bool {
let _entered = trace_span!("canBecomeKeyWindow").entered();
trace_scope!("canBecomeKeyWindow");
true
}
}
@@ -375,7 +374,7 @@ define_class!(
// it doesn't if window doesn't have NSWindowStyleMask::Titled
#[unsafe(method(canBecomeKeyWindow))]
fn can_become_key_window(&self) -> bool {
let _entered = trace_span!("canBecomeKeyWindow").entered();
trace_scope!("canBecomeKeyWindow");
true
}
}

View File

@@ -5,6 +5,7 @@ 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,
@@ -41,7 +42,7 @@ use objc2_foundation::{
NSObjectNSDelayedPerforming, NSObjectNSKeyValueObserverRegistration, NSObjectProtocol, NSPoint,
NSRect, NSSize, NSString, ns_string,
};
use tracing::{debug_span, trace, warn};
use tracing::{trace, warn};
use winit_common::core_foundation::MainRunLoop;
use winit_core::cursor::Cursor;
use winit_core::error::{NotSupportedError, RequestError};
@@ -121,14 +122,14 @@ define_class!(
unsafe impl NSWindowDelegate for WindowDelegate {
#[unsafe(method(windowShouldClose:))]
fn window_should_close(&self, _: Option<&AnyObject>) -> bool {
let _entered = debug_span!("windowShouldClose:").entered();
trace_scope!("windowShouldClose:");
self.queue_event(WindowEvent::CloseRequested);
false
}
#[unsafe(method(windowWillClose:))]
fn window_will_close(&self, _: Option<&AnyObject>) {
let _entered = debug_span!("windowWillClose:").entered();
trace_scope!("windowWillClose:");
// `setDelegate:` retains the previous value and then autoreleases it
autoreleasepool(|_| {
// Since El Capitan, we need to be careful that delegate methods can't
@@ -140,14 +141,14 @@ define_class!(
#[unsafe(method(windowDidResize:))]
fn window_did_resize(&self, _: Option<&AnyObject>) {
let _entered = debug_span!("windowDidResize:").entered();
trace_scope!("windowDidResize:");
// NOTE: WindowEvent::SurfaceResized is reported using NSViewFrameDidChangeNotification.
self.emit_move_event();
}
#[unsafe(method(windowWillStartLiveResize:))]
fn window_will_start_live_resize(&self, _: Option<&AnyObject>) {
let _entered = debug_span!("windowWillStartLiveResize:").entered();
trace_scope!("windowWillStartLiveResize:");
let increments = self.ivars().surface_resize_increments.get();
self.set_resize_increments_inner(increments);
@@ -155,20 +156,20 @@ define_class!(
#[unsafe(method(windowDidEndLiveResize:))]
fn window_did_end_live_resize(&self, _: Option<&AnyObject>) {
let _entered = debug_span!("windowDidEndLiveResize:").entered();
trace_scope!("windowDidEndLiveResize:");
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>) {
let _entered = debug_span!("windowDidMove:").entered();
trace_scope!("windowDidMove:");
self.emit_move_event();
}
#[unsafe(method(windowDidChangeBackingProperties:))]
fn window_did_change_backing_properties(&self, _: Option<&AnyObject>) {
let _entered = debug_span!("windowDidChangeBackingProperties:").entered();
trace_scope!("windowDidChangeBackingProperties:");
let scale_factor = self.scale_factor();
if scale_factor == self.ivars().previous_scale_factor.get() {
return;
@@ -184,7 +185,7 @@ define_class!(
#[unsafe(method(windowDidBecomeKey:))]
fn window_did_become_key(&self, _: Option<&AnyObject>) {
let _entered = debug_span!("windowDidBecomeKey:").entered();
trace_scope!("windowDidBecomeKey:");
// TODO: center the cursor if the window had mouse grab when it
// lost focus
self.queue_event(WindowEvent::Focused(true));
@@ -192,7 +193,7 @@ define_class!(
#[unsafe(method(windowDidResignKey:))]
fn window_did_resign_key(&self, _: Option<&AnyObject>) {
let _entered = debug_span!("windowDidResignKey:").entered();
trace_scope!("windowDidResignKey:");
// 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
@@ -208,7 +209,7 @@ define_class!(
/// Invoked when before enter fullscreen
#[unsafe(method(windowWillEnterFullScreen:))]
fn window_will_enter_fullscreen(&self, _: Option<&AnyObject>) {
let _entered = debug_span!("windowWillEnterFullScreen:").entered();
trace_scope!("windowWillEnterFullScreen:");
self.ivars().maximized.set(self.is_zoomed());
let mut fullscreen = self.ivars().fullscreen.borrow_mut();
@@ -236,7 +237,7 @@ define_class!(
/// Invoked when before exit fullscreen
#[unsafe(method(windowWillExitFullScreen:))]
fn window_will_exit_fullscreen(&self, _: Option<&AnyObject>) {
let _entered = debug_span!("windowWillExitFullScreen:").entered();
trace_scope!("windowWillExitFullScreen:");
self.ivars().in_fullscreen_transition.set(true);
}
@@ -247,7 +248,7 @@ define_class!(
_: Option<&AnyObject>,
proposed_options: NSApplicationPresentationOptions,
) -> NSApplicationPresentationOptions {
let _entered = debug_span!("window:willUseFullScreenPresentationOptions:").entered();
trace_scope!("window:willUseFullScreenPresentationOptions:");
// 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
@@ -270,7 +271,7 @@ define_class!(
/// Invoked when entered fullscreen
#[unsafe(method(windowDidEnterFullScreen:))]
fn window_did_enter_fullscreen(&self, _: Option<&AnyObject>) {
let _entered = debug_span!("windowDidEnterFullScreen:").entered();
trace_scope!("windowDidEnterFullScreen:");
self.ivars().initial_fullscreen.set(false);
self.ivars().in_fullscreen_transition.set(false);
if let Some(target_fullscreen) = self.ivars().target_fullscreen.take() {
@@ -281,7 +282,7 @@ define_class!(
/// Invoked when exited fullscreen
#[unsafe(method(windowDidExitFullScreen:))]
fn window_did_exit_fullscreen(&self, _: Option<&AnyObject>) {
let _entered = debug_span!("windowDidExitFullScreen:").entered();
trace_scope!("windowDidExitFullScreen:");
self.restore_state_from_fullscreen();
self.ivars().in_fullscreen_transition.set(false);
@@ -308,7 +309,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>) {
let _entered = debug_span!("windowDidFailToEnterFullScreen:").entered();
trace_scope!("windowDidFailToEnterFullScreen:");
self.ivars().in_fullscreen_transition.set(false);
self.ivars().target_fullscreen.replace(None);
if self.ivars().initial_fullscreen.get() {
@@ -327,7 +328,7 @@ define_class!(
// Invoked when the occlusion state of the window changes
#[unsafe(method(windowDidChangeOcclusionState:))]
fn window_did_change_occlusion_state(&self, _: Option<&AnyObject>) {
let _entered = debug_span!("windowDidChangeOcclusionState:").entered();
trace_scope!("windowDidChangeOcclusionState:");
let visible = self.window().occlusionState().contains(NSWindowOcclusionState::Visible);
self.queue_event(WindowEvent::Occluded(!visible));
@@ -348,7 +349,7 @@ define_class!(
#[unsafe(method(windowDidChangeScreen:))]
fn window_did_change_screen(&self, _: Option<&AnyObject>) {
let _entered = debug_span!("windowDidChangeScreen:").entered();
trace_scope!("windowDidChangeScreen:");
let is_simple_fullscreen = self.ivars().is_simple_fullscreen.get();
if is_simple_fullscreen {
if let Some(screen) = self.window().screen() {
@@ -362,7 +363,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 {
let _entered = debug_span!("draggingEntered:").entered();
trace_scope!("draggingEntered:");
use std::path::PathBuf;
@@ -393,7 +394,7 @@ define_class!(
#[unsafe(method(wantsPeriodicDraggingUpdates))]
fn wants_periodic_dragging_updates(&self) -> bool {
let _entered = debug_span!("wantsPeriodicDraggingUpdates:").entered();
trace_scope!("wantsPeriodicDraggingUpdates:");
true
}
@@ -401,7 +402,7 @@ define_class!(
/// modification of the dragging operation or mouse-pointer position.
#[unsafe(method(draggingUpdated:))]
fn dragging_updated(&self, sender: &ProtocolObject<dyn NSDraggingInfo>) -> bool {
let _entered = debug_span!("draggingUpdated:").entered();
trace_scope!("draggingUpdated:");
let dl = sender.draggingLocation();
let dl = self.view().convertPoint_fromView(dl, None);
@@ -416,14 +417,14 @@ define_class!(
/// Invoked when the image is released
#[unsafe(method(prepareForDragOperation:))]
fn prepare_for_drag_operation(&self, _sender: &NSObject) -> bool {
let _entered = debug_span!("prepareForDragOperation:").entered();
trace_scope!("prepareForDragOperation:");
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 {
let _entered = debug_span!("performDragOperation:").entered();
trace_scope!("performDragOperation:");
use std::path::PathBuf;
@@ -455,13 +456,13 @@ define_class!(
/// Invoked when the dragging operation is complete
#[unsafe(method(concludeDragOperation:))]
fn conclude_drag_operation(&self, _sender: Option<&NSObject>) {
let _entered = debug_span!("concludeDragOperation:").entered();
trace_scope!("concludeDragOperation:");
}
/// Invoked when the dragging operation is cancelled
#[unsafe(method(draggingExited:))]
fn dragging_exited(&self, sender: Option<&ProtocolObject<dyn NSDraggingInfo>>) {
let _entered = debug_span!("draggingExited:").entered();
trace_scope!("draggingExited:");
let position = sender.map(|sender| {
let dl = sender.draggingLocation();
@@ -483,7 +484,7 @@ define_class!(
change: Option<&NSDictionary<NSKeyValueChangeKey, AnyObject>>,
_context: *mut c_void,
) {
let _entered = debug_span!("observeValueForKeyPath:ofObject:change:context:").entered();
trace_scope!("observeValueForKeyPath:ofObject:change:context:");
// 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")) {
@@ -903,9 +904,16 @@ 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, event);
app.window_event(event_loop, window_id, time, event);
});
}

View File

@@ -9,7 +9,7 @@ use std::cell::Cell;
use objc2::MainThreadMarker;
use objc2_core_foundation::{CFRetained, CFRunLoop, CFRunLoopMode, kCFRunLoopDefaultMode};
use tracing::{Span, error};
use tracing::error;
use super::MainRunLoopObserver;
@@ -49,8 +49,7 @@ 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:]` to queue a closure to run
/// the next time runloop sources are processed.
/// 1. Using `CFRunLoopPerformBlock` or `-[NSRunLoop performBlock:]`.
///
/// 2. Using `-[NSObject performSelectorOnMainThread:withObject:waitUntilDone:]` or wrapping the
/// event in `NSEvent` and posting that to `-[NSApplication postEvent:atStart:]` (both
@@ -74,17 +73,9 @@ 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,9 +6,7 @@
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

@@ -1,146 +0,0 @@
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,10 +79,6 @@ 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,4 +1,9 @@
//! 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;
@@ -192,10 +197,14 @@ 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,
);
@@ -206,9 +215,10 @@ pub trait ApplicationHandler {
&mut self,
event_loop: &dyn ActiveEventLoop,
device_id: Option<DeviceId>,
timestamp: Instant,
event: DeviceEvent,
) {
let _ = (event_loop, device_id, event);
let _ = (event_loop, device_id, timestamp, event);
}
/// Emitted when the event loop is about to block and wait for new events.
@@ -372,9 +382,10 @@ 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, event);
(**self).window_event(event_loop, window_id, timestamp, event);
}
#[inline]
@@ -382,9 +393,10 @@ 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, event);
(**self).device_event(event_loop, device_id, timestamp, event);
}
#[inline]
@@ -440,9 +452,10 @@ 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, event);
(**self).window_event(event_loop, window_id, timestamp, event);
}
#[inline]
@@ -450,9 +463,10 @@ 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, event);
(**self).device_event(event_loop, device_id, timestamp, event);
}
#[inline]

View File

@@ -12,8 +12,7 @@ use objc2_ui_kit::{
UIApplicationWillResignActiveNotification, UIApplicationWillTerminateNotification, UIScreen,
};
use rwh_06::HasDisplayHandle;
use tracing::debug_span;
use winit_common::core_foundation::{MainRunLoop, MainRunLoopObserver, tracing_observers};
use winit_common::core_foundation::{MainRunLoop, MainRunLoopObserver};
use winit_core::application::ApplicationHandler;
use winit_core::cursor::{CustomCursor, CustomCursorSource};
use winit_core::error::{EventLoopError, NotSupportedError, RequestError};
@@ -135,7 +134,6 @@ 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,
@@ -161,7 +159,6 @@ impl EventLoop {
// `application:didFinishLaunchingWithOptions:`
unsafe { UIApplicationDidFinishLaunchingNotification },
move |_| {
let _entered = debug_span!("UIApplicationDidFinishLaunchingNotification").entered();
app_state::did_finish_launching(mtm);
},
);
@@ -169,27 +166,19 @@ impl EventLoop {
&center,
// `applicationDidBecomeActive:`
unsafe { UIApplicationDidBecomeActiveNotification },
move |_| {
let _entered = debug_span!("UIApplicationDidBecomeActiveNotification").entered();
app_state::handle_resumed(mtm)
},
move |_| app_state::handle_resumed(mtm),
);
let _will_resign_active_observer = create_observer(
&center,
// `applicationWillResignActive:`
unsafe { UIApplicationWillResignActiveNotification },
move |_| {
let _entered = debug_span!("UIApplicationWillResignActiveNotification").entered();
app_state::handle_suspended(mtm)
},
move |_| 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",
);
@@ -204,7 +193,6 @@ impl EventLoop {
// `applicationDidEnterBackground:`
unsafe { UIApplicationDidEnterBackgroundNotification },
move |notification| {
let _entered = debug_span!("UIApplicationDidEnterBackgroundNotification").entered();
let app = notification.object().expect(
"UIApplicationDidEnterBackgroundNotification to have application object",
);
@@ -219,7 +207,6 @@ impl EventLoop {
// `applicationWillTerminate:`
unsafe { UIApplicationWillTerminateNotification },
move |notification| {
let _entered = debug_span!("UIApplicationWillTerminateNotification").entered();
let app = notification
.object()
.expect("UIApplicationWillTerminateNotification to have application object");
@@ -233,29 +220,18 @@ impl EventLoop {
&center,
// `applicationDidReceiveMemoryWarning:`
unsafe { UIApplicationDidReceiveMemoryWarningNotification },
move |_| {
let _entered =
debug_span!("UIApplicationDidReceiveMemoryWarningNotification").entered();
app_state::handle_memory_warning(mtm)
},
move |_| 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 second-highest priority (tracing observers use the highest) to
// ensure it is processed before other observers.
CFIndex::MIN + 1,
// Queued with the highest priority to ensure it is processed before other observers.
CFIndex::MIN,
move |_| app_state::handle_wakeup_transition(mtm),
);
main_loop.add_observer(&_wakeup_observer, mode);
@@ -265,17 +241,17 @@ impl EventLoop {
CFRunLoopActivity::BeforeWaiting,
true,
// Core Animation registers its `CFRunLoopObserver` that performs drawing operations in
// `CA::Transaction::ensure_implicit` with a priority of `2000000`. We set the
// `CA::Transaction::ensure_implicit` with a priority of `0x1e8480`. 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 `2000000` was determined by inspecting stack traces and the associated
// The value of `0x1e8480` 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 `2000000` on iPhone 8, iOS 13 beta 4.
// Also tested to be `0x1e8480` on iPhone 8, iOS 13 beta 4.
0,
move |_| app_state::handle_main_events_cleared(mtm),
);
@@ -285,9 +261,8 @@ impl EventLoop {
mtm,
CFRunLoopActivity::BeforeWaiting,
true,
// Queued with the second-lowest priority (tracing observers use the lowest) to ensure
// it is processed after other observers.
CFIndex::MAX - 1,
// Queued with the lowest priority to ensure it is processed after other observers.
CFIndex::MAX,
move |_| app_state::handle_events_cleared(mtm),
);
main_loop.add_observer(&_events_cleared_observer, mode);
@@ -302,7 +277,6 @@ 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, debug_span, trace_span};
use tracing::debug;
use winit_core::event::{
ButtonSource, ElementState, FingerId, Force, KeyEvent, PointerKind, PointerSource,
TabletToolAngle, TabletToolButton, TabletToolData, TabletToolKind, TouchPhase, WindowEvent,
@@ -48,7 +48,6 @@ 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 {
@@ -60,7 +59,6 @@ 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] };
@@ -81,7 +79,6 @@ 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] };
@@ -127,7 +124,6 @@ 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();
@@ -135,31 +131,26 @@ 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() {
@@ -194,7 +185,6 @@ 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 {
@@ -210,7 +200,6 @@ 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() {
@@ -255,7 +244,6 @@ 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));
@@ -308,7 +296,6 @@ define_class!(
#[unsafe(method(canBecomeFirstResponder))]
fn can_become_first_responder(&self) -> bool {
let _entered = trace_span!("canBecomeFirstResponder").entered();
true
}
}
@@ -322,10 +309,6 @@ define_class!(
_gesture_recognizer: &UIGestureRecognizer,
_other_gesture_recognizer: &UIGestureRecognizer,
) -> bool {
let _entered = trace_span!(
"gestureRecognizer:shouldRecognizeSimultaneouslyWithGestureRecognizer:"
)
.entered();
true
}
}
@@ -335,19 +318,16 @@ 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,7 +7,6 @@ use objc2_ui_kit::{
UIDevice, UIInterfaceOrientationMask, UIRectEdge, UIResponder, UIStatusBarStyle,
UIUserInterfaceIdiom, UIView, UIViewController,
};
use tracing::trace_span;
use crate::{ScreenEdge, StatusBarStyle, ValidOrientations, WindowAttributesIos};
@@ -29,37 +28,31 @@ 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, debug_span, warn};
use tracing::{debug, warn};
use winit_core::cursor::Cursor;
use winit_core::error::{NotSupportedError, RequestError};
use winit_core::event::WindowEvent;
@@ -46,7 +46,6 @@ 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(),
@@ -57,7 +56,6 @@ 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,7 +387,11 @@ 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) => Key::Dead(dead_char),
ToUnicodeResult::Dead(dead_char) => {
// println!("{:?} - {:?} produced dead {:?}", key_code, mod_state,
// 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,6 +66,7 @@ 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()
@@ -73,6 +74,7 @@ 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,9 +1,7 @@
use cfg_aliases::cfg_aliases;
// Only relevant for examples and Winit, our usage of println! is fine here.
#[allow(clippy::disallowed_macros)]
fn main() {
// Dummy invocation to enable change-tracking in build scripts.
// The script doesn't depend on our code.
println!("cargo:rerun-if-changed=build.rs");
// Setup cfg aliases.

View File

@@ -3,7 +3,6 @@
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};
@@ -39,7 +38,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();
info!("Parent window id: {:?})", window.id());
println!("Parent window id: {:?})", window.id());
self.parent_window_id = Some(window.id());
self.windows.insert(window.id(), WindowData::new(window, 0xffbbbbbb));
@@ -57,12 +56,12 @@ fn main() -> Result<(), impl std::error::Error> {
event_loop.exit();
},
WindowEvent::PointerEntered { device_id: _, .. } => {
// On x11, log when the cursor entered in a window even if the child window
// On x11, println when the cursor entered in a window even if the child window
// is created by some key inputs.
// the child windows are always placed at (0, 0) with size (200, 200) in the
// parent window, so we also can see this log when we move
// the cursor around (200, 200) in parent window.
info!("cursor entered in the window {window_id:?}");
println!("cursor entered in the window {window_id:?}");
},
WindowEvent::KeyboardInput {
event: KeyEvent { state: ElementState::Pressed, .. },
@@ -76,7 +75,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();
info!("Child window created with id: {child_id:?}");
println!("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,6 +1,5 @@
use std::error::Error;
use tracing::info;
use winit::application::ApplicationHandler;
use winit::event::WindowEvent;
use winit::event_loop::{ActiveEventLoop, EventLoop};
@@ -50,7 +49,7 @@ impl ApplicationHandler for Application {
| WindowEvent::DragEntered { .. }
| WindowEvent::DragMoved { .. }
| WindowEvent::DragDropped { .. } => {
info!("{event:?}");
println!("{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) => {
error!("error creating window: {err}");
eprintln!("error creating window: {err}");
event_loop.exit();
return;
},
@@ -346,7 +346,7 @@ fn main() -> Result<(), Box<dyn Error>> {
let event_loop = EventLoop::new()?;
info!(
println!(
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,7 +7,6 @@ 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};
@@ -16,8 +15,6 @@ fn main() -> std::process::ExitCode {
#[path = "util/fill.rs"]
mod fill;
#[path = "util/tracing.rs"]
mod tracing;
#[derive(Default, Debug)]
struct PumpDemo {
@@ -36,7 +33,7 @@ fn main() -> std::process::ExitCode {
_window_id: WindowId,
event: WindowEvent,
) {
info!("{event:?}");
println!("{event:?}");
let window = match self.window.as_ref() {
Some(window) => window,
@@ -54,10 +51,10 @@ fn main() -> std::process::ExitCode {
}
}
tracing::init();
let mut event_loop = EventLoop::new().unwrap();
tracing_subscriber::fmt::init();
let mut app = PumpDemo::default();
loop {
@@ -72,12 +69,12 @@ fn main() -> std::process::ExitCode {
//
// Since `pump_events` doesn't block it will be important to
// throttle the loop in the app somehow.
info!("Update()");
println!("Update()");
sleep(Duration::from_millis(16));
}
}
#[cfg(any(ios_platform, web_platform, orbital_platform))]
fn main() {
panic!("This platform doesn't support pump_events.")
println!("This platform doesn't support pump_events.");
}

View File

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

View File

@@ -23,6 +23,3 @@ 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,18 +30,24 @@ impl ApplicationHandler for App {
self.window = match event_loop.create_window(window_attributes) {
Ok(window) => Some(window),
Err(err) => {
error!("error creating window: {err}");
eprintln!("error creating window: {err}");
event_loop.exit();
return;
},
}
}
fn window_event(&mut self, event_loop: &dyn ActiveEventLoop, _: WindowId, event: WindowEvent) {
info!("{event:?}");
fn window_event(
&mut self,
event_loop: &dyn ActiveEventLoop,
_: WindowId,
timestamp: Instant,
event: WindowEvent,
) {
::tracing::info!("{:?}: {event:?}", timestamp.elapsed());
match event {
WindowEvent::CloseRequested => {
info!("Close was requested; stopping");
println!("Close was requested; stopping");
event_loop.exit();
},
WindowEvent::SurfaceResized(_) => {
@@ -63,11 +69,21 @@ 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,8 +11,6 @@ fn main() -> Result<(), Box<dyn Error>> {
#[path = "util/fill.rs"]
mod fill;
#[path = "util/tracing.rs"]
mod tracing;
#[derive(Debug)]
pub struct XEmbedDemo {
@@ -60,7 +58,7 @@ fn main() -> Result<(), Box<dyn Error>> {
.ok_or("Expected a 32-bit X11 window ID as the first argument.")?
.parse::<u32>()?;
tracing::init();
tracing_subscriber::fmt::init();
let event_loop = EventLoop::new()?;
Ok(event_loop.run_app(XEmbedDemo { parent_window_id, window: None })?)
@@ -68,5 +66,6 @@ fn main() -> Result<(), Box<dyn Error>> {
#[cfg(not(x11_platform))]
fn main() -> Result<(), Box<dyn Error>> {
panic!("This example is only supported on X11 platforms.")
println!("This example is only supported on X11 platforms.");
Ok(())
}

View File

@@ -46,13 +46,11 @@ 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,9 +75,6 @@ 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(
@@ -91,7 +88,7 @@ impl EventLoopBuilder {
)]
#[inline]
pub fn build(&mut self) -> Result<EventLoop, EventLoopError> {
let _entered = tracing::debug_span!("winit::EventLoopBuilder::build").entered();
let _span = tracing::debug_span!("winit::EventLoopBuilder::build").entered();
// Certain platforms accept a mutable reference in their API.
#[allow(clippy::unnecessary_mut_passed)]
@@ -265,7 +262,7 @@ impl EventLoop {
///
/// [`DeviceEvent`]: crate::event::DeviceEvent
pub fn listen_device_events(&self, allowed: DeviceEvents) {
let _entered = tracing::debug_span!(
let _span = tracing::debug_span!(
"winit::EventLoop::listen_device_events",
allowed = ?allowed
)