1use std::fmt;
2use std::rc::Rc;
3use std::sync::Arc;
4use std::time::{Duration, Instant};
5
6use objc2::rc::{Retained, autoreleasepool};
7use objc2::runtime::ProtocolObject;
8use objc2::{AnyThread, MainThreadMarker, available};
9use objc2_app_kit::{
10 NSApplication, NSApplicationActivationPolicy, NSApplicationDidFinishLaunchingNotification,
11 NSApplicationWillTerminateNotification, NSDraggingItem, NSWindow,
12};
13use objc2_core_foundation::{
14 CFIndex, CFRunLoopActivity, CGPoint, CGRect, CGSize, kCFRunLoopCommonModes,
15};
16use objc2_foundation::{NSArray, NSNotificationCenter, NSObjectProtocol, NSString};
17use rwh_06::HasDisplayHandle;
18use tracing::debug_span;
19use winit_common::core_foundation::{MainRunLoop, MainRunLoopObserver, tracing_observers};
20use winit_common::foundation::create_observer;
21use winit_core::application::ApplicationHandler;
22use winit_core::cursor::{CustomCursor as CoreCustomCursor, CustomCursorSource};
23use winit_core::data_transfer::{
24 DataTransfer, DataTransferId, DataTransferSend, SendData, TransferType, TypeHint,
25};
26use winit_core::error::{EventLoopError, RequestError};
27use winit_core::event::WindowEvent;
28use winit_core::event_loop::pump_events::PumpStatus;
29use winit_core::event_loop::{
30 ActiveEventLoop as RootActiveEventLoop, AsyncRequestSerial, ControlFlow, DeviceEvents,
31 DndAction, DragIcon, EventLoopProvider, EventLoopProxy as CoreEventLoopProxy,
32 OwnedDisplayHandle as CoreOwnedDisplayHandle,
33};
34use winit_core::monitor::MonitorHandle as CoreMonitorHandle;
35use winit_core::window::{Theme, WindowId};
36
37use super::app::override_send_event;
38use super::app_state::AppState;
39use super::cursor::CustomCursor;
40use super::event::dummy_event;
41use super::monitor;
42use crate::ActivationPolicy;
43use crate::cursor::image_from_icon;
44use crate::dnd::{PasteboardTypeSpec, PasteboardWriter, dnd_actions_to_ns_drag_operation};
45use crate::window::Window;
46
47#[derive(Debug)]
48pub struct ActiveEventLoop {
49 pub(super) app_state: Rc<AppState>,
50 pub(super) mtm: MainThreadMarker,
51}
52
53impl ActiveEventLoop {
54 pub(crate) fn hide_application(&self) {
55 NSApplication::sharedApplication(self.mtm).hide(None)
56 }
57
58 pub(crate) fn hide_other_applications(&self) {
59 NSApplication::sharedApplication(self.mtm).hideOtherApplications(None)
60 }
61
62 pub(crate) fn set_allows_automatic_window_tabbing(&self, enabled: bool) {
63 NSWindow::setAllowsAutomaticWindowTabbing(enabled, self.mtm)
64 }
65
66 pub(crate) fn allows_automatic_window_tabbing(&self) -> bool {
67 NSWindow::allowsAutomaticWindowTabbing(self.mtm)
68 }
69}
70
71impl RootActiveEventLoop for ActiveEventLoop {
72 fn create_proxy(&self) -> CoreEventLoopProxy {
73 CoreEventLoopProxy::new(self.app_state.event_loop_proxy().clone())
74 }
75
76 fn create_window(
77 &self,
78 window_attributes: winit_core::window::WindowAttributes,
79 ) -> Result<Box<dyn winit_core::window::Window>, RequestError> {
80 Ok(Box::new(Window::new(self, window_attributes)?))
81 }
82
83 fn create_custom_cursor(
84 &self,
85 source: CustomCursorSource,
86 ) -> Result<CoreCustomCursor, RequestError> {
87 Ok(CoreCustomCursor(Arc::new(CustomCursor::new(source)?)))
88 }
89
90 fn available_monitors(&self) -> Box<dyn Iterator<Item = CoreMonitorHandle>> {
91 Box::new(
92 monitor::available_monitors()
93 .into_iter()
94 .map(|monitor| CoreMonitorHandle(Arc::new(monitor))),
95 )
96 }
97
98 fn primary_monitor(&self) -> Option<winit_core::monitor::MonitorHandle> {
99 let monitor = monitor::primary_monitor();
100 Some(CoreMonitorHandle(Arc::new(monitor)))
101 }
102
103 fn listen_device_events(&self, _allowed: DeviceEvents) {}
104
105 fn system_theme(&self) -> Option<Theme> {
106 let app = NSApplication::sharedApplication(self.mtm);
107
108 if available!(macos = 10.14) {
110 Some(super::window_delegate::appearance_to_theme(&app.effectiveAppearance()))
111 } else {
112 Some(Theme::Light)
113 }
114 }
115
116 fn set_control_flow(&self, control_flow: ControlFlow) {
117 self.app_state.set_control_flow(control_flow)
118 }
119
120 fn control_flow(&self) -> ControlFlow {
121 self.app_state.control_flow()
122 }
123
124 fn exit(&self) {
125 self.app_state.exit()
126 }
127
128 fn exiting(&self) -> bool {
129 self.app_state.exiting()
130 }
131
132 fn owned_display_handle(&self) -> CoreOwnedDisplayHandle {
133 CoreOwnedDisplayHandle::new(Arc::new(OwnedDisplayHandle))
134 }
135
136 fn rwh_06_handle(&self) -> &dyn rwh_06::HasDisplayHandle {
137 self
138 }
139
140 fn fetch_data_transfer(
141 &self,
142 id: DataTransferId,
143 type_: &dyn TransferType,
144 ) -> Result<AsyncRequestSerial, RequestError> {
145 let Some(pb) = self.app_state.pasteboards().get(id) else {
146 return Err(RequestError::Ignored);
147 };
148 let Some(window_id) = self.app_state.pasteboards().window_id(id) else {
149 return Err(RequestError::Ignored);
150 };
151
152 let serial = AsyncRequestSerial::get();
153
154 let Some(type_) = PasteboardTypeSpec::from_dyn(type_) else {
155 return Err(os_error!(format!("Pasteboard does not contain type {type_:?}")).into());
156 };
157
158 let data = Arc::new(pb.with_type(type_));
159
160 self.app_state.maybe_queue_with_handler(move |app, event_loop| {
161 app.window_event(event_loop, window_id, WindowEvent::DataTransferReceived {
162 id,
163 serial,
164 value: data,
165 });
166 });
167
168 Ok(serial)
169 }
170
171 fn data_transfer(&self, id: DataTransferId) -> Result<Box<dyn DataTransfer>, RequestError> {
172 let Some(pb) = self.app_state.pasteboards().get(id) else {
173 return Err(RequestError::Ignored);
174 };
175
176 Ok(Box::new(pb))
177 }
178
179 fn set_valid_dnd_actions(
180 &self,
181 id: DataTransferId,
182 actions: &[DndAction],
183 ) -> Result<(), RequestError> {
184 let mut state = self.app_state.drag_state().borrow_mut();
185 let Some(drag_state) = &mut *state else {
186 return Err(os_error!(UnknownDataTransfer(id)).into());
187 };
188
189 if drag_state.id != id {
190 return Err(os_error!(UnknownDataTransfer(id)).into());
191 }
192
193 drag_state.valid_actions.clear();
194 drag_state.valid_actions.extend_from_slice(actions);
195
196 Ok(())
197 }
198
199 fn start_drag(
200 &self,
201 source: WindowId,
202 send_data: Box<dyn DataTransferSend>,
203 actions: &[DndAction],
204 icon: Option<DragIcon>,
205 ) -> Result<DataTransferId, RequestError> {
206 let drag_operation = dnd_actions_to_ns_drag_operation(actions);
207
208 self.app_state
209 .with_window_delegate_on_main(source, move |delegate| {
210 let (dragging_rect_offset_x, dragging_rect_offset_y) =
211 icon.as_ref().map(|icon| (icon.offset_x, icon.offset_y)).unwrap_or_default();
212 let drag_image = icon.and_then(|icon| image_from_icon(&icon.icon).ok());
213
214 let Some(event) = delegate.window().currentEvent() else {
215 return Err(RequestError::Ignored);
216 };
217
218 let dragging_rect_size = drag_image
219 .as_ref()
220 .map(|img| img.size())
221 .unwrap_or(CGSize::new(16., 16.));
224
225 let event_location = event.locationInWindow();
226 let dragging_rect_location = CGPoint::new(
227 event_location.x + dragging_rect_offset_x as f64,
228 event_location.y - dragging_rect_size.height - dragging_rect_offset_y as f64,
231 );
232 let dragging_rect = CGRect::new(dragging_rect_location, dragging_rect_size);
233
234 let mut uris = send_data
235 .data_for_type(&TypeHint::UriList)
236 .and_then(|file_uris| {
237 let ns_url_from_str = |str: String| NSString::from_str(&str);
239 match file_uris {
242 SendData::Uris(os_strings) => Some(
243 None.into_iter().chain(os_strings.into_iter().map(ns_url_from_str)),
244 ),
245 SendData::String(string) => Some(
246 Some(NSString::from_str(&string))
247 .into_iter()
248 .chain(Vec::new().into_iter().map(ns_url_from_str)),
249 ),
250 SendData::Bytes(_) => None,
251 _ => None,
252 }
253 })
254 .into_iter()
255 .flatten();
256
257 let first_uri = uris.next();
258
259 let mut pasteboard_items = uris
260 .map(|ns_url| {
261 let dragging_item = NSDraggingItem::initWithPasteboardWriter(
262 NSDraggingItem::alloc(),
263 ProtocolObject::from_ref(&*ns_url),
264 );
265
266 dragging_item
269 })
270 .collect::<Vec<_>>();
271
272 let first_dragging_item = NSDraggingItem::initWithPasteboardWriter(
273 NSDraggingItem::alloc(),
274 ProtocolObject::from_ref(&*PasteboardWriter::new(send_data, first_uri)),
275 );
276
277 unsafe {
278 first_dragging_item.setDraggingFrame_contents(
279 dragging_rect,
280 drag_image.as_ref().map(AsRef::as_ref),
281 )
282 };
283
284 pasteboard_items.insert(0, first_dragging_item);
285
286 let pasteboard_items = NSArray::from_retained_slice(&pasteboard_items);
287
288 let session = delegate.window().beginDraggingSessionWithItems_event_source(
289 &pasteboard_items,
290 &event,
291 ProtocolObject::from_ref(&*delegate),
292 );
293
294 let id = DataTransferId::from_raw(session.draggingSequenceNumber() as i64);
295
296 delegate.view().set_dragging_session(session, drag_operation);
297
298 Ok(id)
299 })
300 .ok_or(RequestError::Ignored)?
301 }
302}
303
304#[derive(Debug, Copy, Clone, PartialEq, Eq)]
306pub struct UnknownDataTransfer(pub DataTransferId);
307
308impl fmt::Display for UnknownDataTransfer {
309 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
310 let id = self.0.into_raw();
311 write!(f, "Unknown data transfer with ID {id}")
312 }
313}
314
315impl std::error::Error for UnknownDataTransfer {}
316
317impl rwh_06::HasDisplayHandle for ActiveEventLoop {
318 fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
319 let raw = rwh_06::RawDisplayHandle::AppKit(rwh_06::AppKitDisplayHandle::new());
320 unsafe { Ok(rwh_06::DisplayHandle::borrow_raw(raw)) }
321 }
322}
323
324#[derive(Debug)]
325pub struct EventLoop {
326 app: Retained<NSApplication>,
331 app_state: Rc<AppState>,
332
333 window_target: ActiveEventLoop,
334
335 _did_finish_launching_observer: Retained<ProtocolObject<dyn NSObjectProtocol>>,
340 _will_terminate_observer: Retained<ProtocolObject<dyn NSObjectProtocol>>,
341
342 _tracing_observers: Option<(MainRunLoopObserver, MainRunLoopObserver)>,
343 _before_waiting_observer: MainRunLoopObserver,
344 _after_waiting_observer: MainRunLoopObserver,
345}
346
347#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
348pub struct PlatformSpecificEventLoopAttributes {
349 pub activation_policy: Option<ActivationPolicy>,
350 pub default_menu: bool,
351 pub activate_ignoring_other_apps: bool,
352}
353
354impl Default for PlatformSpecificEventLoopAttributes {
355 fn default() -> Self {
356 Self { activation_policy: None, default_menu: true, activate_ignoring_other_apps: true }
357 }
358}
359
360impl EventLoop {
361 pub fn new(attributes: &PlatformSpecificEventLoopAttributes) -> Result<Self, EventLoopError> {
362 let mtm = MainThreadMarker::new()
363 .expect("on macOS, `EventLoop` must be created on the main thread!");
364
365 let activation_policy = match attributes.activation_policy {
366 None => None,
367 Some(ActivationPolicy::Regular) => Some(NSApplicationActivationPolicy::Regular),
368 Some(ActivationPolicy::Accessory) => Some(NSApplicationActivationPolicy::Accessory),
369 Some(ActivationPolicy::Prohibited) => Some(NSApplicationActivationPolicy::Prohibited),
370 };
371
372 let app_state = AppState::setup_global(
373 mtm,
374 activation_policy,
375 attributes.default_menu,
376 attributes.activate_ignoring_other_apps,
377 )
378 .ok_or_else(|| EventLoopError::RecreationAttempt)?;
379
380 let app = NSApplication::sharedApplication(mtm);
382
383 override_send_event(&app);
385
386 let center = NSNotificationCenter::defaultCenter();
387
388 let weak_app_state = Rc::downgrade(&app_state);
389 let _did_finish_launching_observer = create_observer(
390 ¢er,
391 unsafe { NSApplicationDidFinishLaunchingNotification },
393 move |notification| {
394 let _entered = debug_span!("NSApplicationDidFinishLaunchingNotification").entered();
395 if let Some(app_state) = weak_app_state.upgrade() {
396 app_state.did_finish_launching(notification);
397 }
398 },
399 );
400
401 let weak_app_state = Rc::downgrade(&app_state);
402 let _will_terminate_observer = create_observer(
403 ¢er,
404 unsafe { NSApplicationWillTerminateNotification },
406 move |notification| {
407 let _entered = debug_span!("NSApplicationWillTerminateNotification").entered();
408 if let Some(app_state) = weak_app_state.upgrade() {
409 app_state.will_terminate(notification);
410 }
411 },
412 );
413
414 let main_loop = MainRunLoop::get(mtm);
415 let mode = unsafe { kCFRunLoopCommonModes }.unwrap();
416
417 let _tracing_observers = tracing_observers(mtm).inspect(|(start, end)| {
419 main_loop.add_observer(start, mode);
420 main_loop.add_observer(end, mode);
421 });
422
423 let app_state_clone = Rc::clone(&app_state);
424 let _before_waiting_observer = MainRunLoopObserver::new(
425 mtm,
426 CFRunLoopActivity::BeforeWaiting,
427 true,
428 CFIndex::MAX - 1,
431 move |_| app_state_clone.cleared(),
432 );
433 main_loop.add_observer(&_before_waiting_observer, mode);
434
435 let app_state_clone = Rc::clone(&app_state);
436 let _after_waiting_observer = MainRunLoopObserver::new(
437 mtm,
438 CFRunLoopActivity::AfterWaiting,
439 true,
440 CFIndex::MIN + 1,
443 move |_| app_state_clone.wakeup(),
444 );
445 main_loop.add_observer(&_after_waiting_observer, mode);
446
447 Ok(EventLoop {
448 app,
449 app_state: app_state.clone(),
450 window_target: ActiveEventLoop { app_state, mtm },
451 _did_finish_launching_observer,
452 _will_terminate_observer,
453 _tracing_observers,
454 _before_waiting_observer,
455 _after_waiting_observer,
456 })
457 }
458
459 pub fn window_target(&self) -> &dyn RootActiveEventLoop {
460 &self.window_target
461 }
462
463 pub fn run_app_on_demand<A: ApplicationHandler>(
468 &mut self,
469 app: A,
470 ) -> Result<(), EventLoopError> {
471 self.app_state.clear_exit();
472 self.app_state.set_event_handler(app, || {
473 autoreleasepool(|_| {
474 self.app_state.set_wait_timeout(None);
476 self.app_state.set_stop_before_wait(false);
477 self.app_state.set_stop_after_wait(false);
478 self.app_state.set_stop_on_redraw(false);
479
480 if self.app_state.is_launched() {
481 debug_assert!(!self.app_state.is_running());
482 self.app_state.set_is_running(true);
483 self.app_state.dispatch_init_events();
484 }
485
486 self.app.run();
488
489 self.app_state.internal_exit()
490 })
491 });
492
493 Ok(())
494 }
495
496 pub fn pump_app_events<A: ApplicationHandler>(
497 &mut self,
498 timeout: Option<Duration>,
499 app: A,
500 ) -> PumpStatus {
501 self.app_state.set_event_handler(app, || {
502 autoreleasepool(|_| {
503 if !self.app_state.is_launched() {
506 debug_assert!(!self.app_state.is_running());
507
508 self.app_state.set_stop_on_launch();
509 self.app.run();
510
511 } else if !self.app_state.is_running() {
514 self.app_state.set_is_running(true);
519 self.app_state.dispatch_init_events();
520 } else {
521 match timeout {
524 Some(Duration::ZERO) => {
525 self.app_state.set_wait_timeout(None);
526 self.app_state.set_stop_before_wait(true);
527 },
528 Some(duration) => {
529 self.app_state.set_stop_before_wait(false);
530 let timeout = Instant::now() + duration;
531 self.app_state.set_wait_timeout(Some(timeout));
532 self.app_state.set_stop_after_wait(true);
533 },
534 None => {
535 self.app_state.set_wait_timeout(None);
536 self.app_state.set_stop_before_wait(false);
537 self.app_state.set_stop_after_wait(true);
538 },
539 }
540 self.app_state.set_stop_on_redraw(true);
541 self.app.run();
542 }
543
544 if self.app_state.exiting() {
545 self.app_state.internal_exit();
546 PumpStatus::Exit(0)
547 } else {
548 PumpStatus::Continue
549 }
550 })
551 })
552 }
553}
554
555impl EventLoopProvider for EventLoop {
556 fn run_app<A: ApplicationHandler + 'static>(
557 mut self,
558 mut app: A,
559 ) -> Result<(), EventLoopError> {
560 let result = self.run_app_on_demand(&mut app);
561 drop(app);
563 result
564 }
565
566 fn create_proxy(&self) -> CoreEventLoopProxy {
567 self.window_target().create_proxy()
568 }
569
570 fn owned_display_handle(&self) -> CoreOwnedDisplayHandle {
571 self.window_target().owned_display_handle()
572 }
573
574 fn listen_device_events(&self, allowed: DeviceEvents) {
575 self.window_target().listen_device_events(allowed);
576 }
577
578 fn set_control_flow(&self, control_flow: ControlFlow) {
579 self.window_target().set_control_flow(control_flow);
580 }
581
582 fn create_custom_cursor(
583 &self,
584 custom_cursor: CustomCursorSource,
585 ) -> Result<CoreCustomCursor, RequestError> {
586 self.window_target().create_custom_cursor(custom_cursor)
587 }
588}
589
590pub(crate) struct OwnedDisplayHandle;
591
592impl HasDisplayHandle for OwnedDisplayHandle {
593 fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
594 let raw = rwh_06::RawDisplayHandle::AppKit(rwh_06::AppKitDisplayHandle::new());
595 unsafe { Ok(rwh_06::DisplayHandle::borrow_raw(raw)) }
596 }
597}
598
599pub(super) fn stop_app_immediately(app: &NSApplication) {
600 autoreleasepool(|_| {
601 app.stop(None);
602 app.postEvent_atStart(&dummy_event().unwrap(), true);
605 });
606}
607
608pub(super) fn notify_windows_of_exit(app: &NSApplication) {
619 for window in app.windows() {
620 window.close();
621 }
622}