winit_appkit/
event_loop.rs1use std::rc::Rc;
2use std::sync::Arc;
3use std::time::{Duration, Instant};
4
5use objc2::rc::{Retained, autoreleasepool};
6use objc2::runtime::ProtocolObject;
7use objc2::{MainThreadMarker, available};
8use objc2_app_kit::{
9 NSApplication, NSApplicationActivationPolicy, NSApplicationDidFinishLaunchingNotification,
10 NSApplicationWillTerminateNotification, NSWindow,
11};
12use objc2_foundation::{NSNotificationCenter, NSObjectProtocol};
13use rwh_06::HasDisplayHandle;
14use winit_core::application::ApplicationHandler;
15use winit_core::cursor::{CustomCursor as CoreCustomCursor, CustomCursorSource};
16use winit_core::error::{EventLoopError, RequestError};
17use winit_core::event_loop::pump_events::PumpStatus;
18use winit_core::event_loop::{
19 ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents,
20 EventLoopProxy as CoreEventLoopProxy, OwnedDisplayHandle as CoreOwnedDisplayHandle,
21};
22use winit_core::monitor::MonitorHandle as CoreMonitorHandle;
23use winit_core::window::Theme;
24
25use super::app::override_send_event;
26use super::app_state::AppState;
27use super::cursor::CustomCursor;
28use super::event::dummy_event;
29use super::monitor;
30use super::notification_center::create_observer;
31use super::observer::setup_control_flow_observers;
32use crate::ActivationPolicy;
33use crate::window::Window;
34
35#[derive(Debug)]
36pub struct ActiveEventLoop {
37 pub(super) app_state: Rc<AppState>,
38 pub(super) mtm: MainThreadMarker,
39}
40
41impl ActiveEventLoop {
42 pub(crate) fn hide_application(&self) {
43 NSApplication::sharedApplication(self.mtm).hide(None)
44 }
45
46 pub(crate) fn hide_other_applications(&self) {
47 NSApplication::sharedApplication(self.mtm).hideOtherApplications(None)
48 }
49
50 pub(crate) fn set_allows_automatic_window_tabbing(&self, enabled: bool) {
51 NSWindow::setAllowsAutomaticWindowTabbing(enabled, self.mtm)
52 }
53
54 pub(crate) fn allows_automatic_window_tabbing(&self) -> bool {
55 NSWindow::allowsAutomaticWindowTabbing(self.mtm)
56 }
57}
58
59impl RootActiveEventLoop for ActiveEventLoop {
60 fn create_proxy(&self) -> CoreEventLoopProxy {
61 CoreEventLoopProxy::new(self.app_state.event_loop_proxy().clone())
62 }
63
64 fn create_window(
65 &self,
66 window_attributes: winit_core::window::WindowAttributes,
67 ) -> Result<Box<dyn winit_core::window::Window>, RequestError> {
68 Ok(Box::new(Window::new(self, window_attributes)?))
69 }
70
71 fn create_custom_cursor(
72 &self,
73 source: CustomCursorSource,
74 ) -> Result<CoreCustomCursor, RequestError> {
75 Ok(CoreCustomCursor(Arc::new(CustomCursor::new(source)?)))
76 }
77
78 fn available_monitors(&self) -> Box<dyn Iterator<Item = CoreMonitorHandle>> {
79 Box::new(
80 monitor::available_monitors()
81 .into_iter()
82 .map(|monitor| CoreMonitorHandle(Arc::new(monitor))),
83 )
84 }
85
86 fn primary_monitor(&self) -> Option<winit_core::monitor::MonitorHandle> {
87 let monitor = monitor::primary_monitor();
88 Some(CoreMonitorHandle(Arc::new(monitor)))
89 }
90
91 fn listen_device_events(&self, _allowed: DeviceEvents) {}
92
93 fn system_theme(&self) -> Option<Theme> {
94 let app = NSApplication::sharedApplication(self.mtm);
95
96 if available!(macos = 10.14) {
98 Some(super::window_delegate::appearance_to_theme(&app.effectiveAppearance()))
99 } else {
100 Some(Theme::Light)
101 }
102 }
103
104 fn set_control_flow(&self, control_flow: ControlFlow) {
105 self.app_state.set_control_flow(control_flow)
106 }
107
108 fn control_flow(&self) -> ControlFlow {
109 self.app_state.control_flow()
110 }
111
112 fn exit(&self) {
113 self.app_state.exit()
114 }
115
116 fn exiting(&self) -> bool {
117 self.app_state.exiting()
118 }
119
120 fn owned_display_handle(&self) -> CoreOwnedDisplayHandle {
121 CoreOwnedDisplayHandle::new(Arc::new(OwnedDisplayHandle))
122 }
123
124 fn rwh_06_handle(&self) -> &dyn rwh_06::HasDisplayHandle {
125 self
126 }
127}
128
129impl rwh_06::HasDisplayHandle for ActiveEventLoop {
130 fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
131 let raw = rwh_06::RawDisplayHandle::AppKit(rwh_06::AppKitDisplayHandle::new());
132 unsafe { Ok(rwh_06::DisplayHandle::borrow_raw(raw)) }
133 }
134}
135
136#[derive(Debug)]
137pub struct EventLoop {
138 app: Retained<NSApplication>,
143 app_state: Rc<AppState>,
144
145 window_target: ActiveEventLoop,
146
147 _did_finish_launching_observer: Retained<ProtocolObject<dyn NSObjectProtocol>>,
152 _will_terminate_observer: Retained<ProtocolObject<dyn NSObjectProtocol>>,
153}
154
155#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
156pub struct PlatformSpecificEventLoopAttributes {
157 pub activation_policy: Option<ActivationPolicy>,
158 pub default_menu: bool,
159 pub activate_ignoring_other_apps: bool,
160}
161
162impl Default for PlatformSpecificEventLoopAttributes {
163 fn default() -> Self {
164 Self { activation_policy: None, default_menu: true, activate_ignoring_other_apps: true }
165 }
166}
167
168impl EventLoop {
169 pub fn new(attributes: &PlatformSpecificEventLoopAttributes) -> Result<Self, EventLoopError> {
170 let mtm = MainThreadMarker::new()
171 .expect("on macOS, `EventLoop` must be created on the main thread!");
172
173 let activation_policy = match attributes.activation_policy {
174 None => None,
175 Some(ActivationPolicy::Regular) => Some(NSApplicationActivationPolicy::Regular),
176 Some(ActivationPolicy::Accessory) => Some(NSApplicationActivationPolicy::Accessory),
177 Some(ActivationPolicy::Prohibited) => Some(NSApplicationActivationPolicy::Prohibited),
178 };
179
180 let app_state = AppState::setup_global(
181 mtm,
182 activation_policy,
183 attributes.default_menu,
184 attributes.activate_ignoring_other_apps,
185 )
186 .ok_or_else(|| EventLoopError::RecreationAttempt)?;
187
188 let app = NSApplication::sharedApplication(mtm);
190
191 override_send_event(&app);
193
194 let center = NSNotificationCenter::defaultCenter();
195
196 let weak_app_state = Rc::downgrade(&app_state);
197 let _did_finish_launching_observer = create_observer(
198 ¢er,
199 unsafe { NSApplicationDidFinishLaunchingNotification },
201 move |notification| {
202 if let Some(app_state) = weak_app_state.upgrade() {
203 app_state.did_finish_launching(notification);
204 }
205 },
206 );
207
208 let weak_app_state = Rc::downgrade(&app_state);
209 let _will_terminate_observer = create_observer(
210 ¢er,
211 unsafe { NSApplicationWillTerminateNotification },
213 move |notification| {
214 if let Some(app_state) = weak_app_state.upgrade() {
215 app_state.will_terminate(notification);
216 }
217 },
218 );
219
220 setup_control_flow_observers(mtm);
221
222 Ok(EventLoop {
223 app,
224 app_state: app_state.clone(),
225 window_target: ActiveEventLoop { app_state, mtm },
226 _did_finish_launching_observer,
227 _will_terminate_observer,
228 })
229 }
230
231 pub fn window_target(&self) -> &dyn RootActiveEventLoop {
232 &self.window_target
233 }
234
235 pub fn run_app_on_demand<A: ApplicationHandler>(
240 &mut self,
241 app: A,
242 ) -> Result<(), EventLoopError> {
243 self.app_state.clear_exit();
244 self.app_state.set_event_handler(app, || {
245 autoreleasepool(|_| {
246 self.app_state.set_wait_timeout(None);
248 self.app_state.set_stop_before_wait(false);
249 self.app_state.set_stop_after_wait(false);
250 self.app_state.set_stop_on_redraw(false);
251
252 if self.app_state.is_launched() {
253 debug_assert!(!self.app_state.is_running());
254 self.app_state.set_is_running(true);
255 self.app_state.dispatch_init_events();
256 }
257
258 self.app.run();
260
261 self.app_state.internal_exit()
262 })
263 });
264
265 Ok(())
266 }
267
268 pub fn pump_app_events<A: ApplicationHandler>(
269 &mut self,
270 timeout: Option<Duration>,
271 app: A,
272 ) -> PumpStatus {
273 self.app_state.set_event_handler(app, || {
274 autoreleasepool(|_| {
275 if !self.app_state.is_launched() {
278 debug_assert!(!self.app_state.is_running());
279
280 self.app_state.set_stop_on_launch();
281 self.app.run();
282
283 } else if !self.app_state.is_running() {
286 self.app_state.set_is_running(true);
291 self.app_state.dispatch_init_events();
292 } else {
293 match timeout {
296 Some(Duration::ZERO) => {
297 self.app_state.set_wait_timeout(None);
298 self.app_state.set_stop_before_wait(true);
299 },
300 Some(duration) => {
301 self.app_state.set_stop_before_wait(false);
302 let timeout = Instant::now() + duration;
303 self.app_state.set_wait_timeout(Some(timeout));
304 self.app_state.set_stop_after_wait(true);
305 },
306 None => {
307 self.app_state.set_wait_timeout(None);
308 self.app_state.set_stop_before_wait(false);
309 self.app_state.set_stop_after_wait(true);
310 },
311 }
312 self.app_state.set_stop_on_redraw(true);
313 self.app.run();
314 }
315
316 if self.app_state.exiting() {
317 self.app_state.internal_exit();
318 PumpStatus::Exit(0)
319 } else {
320 PumpStatus::Continue
321 }
322 })
323 })
324 }
325}
326
327pub(crate) struct OwnedDisplayHandle;
328
329impl HasDisplayHandle for OwnedDisplayHandle {
330 fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
331 let raw = rwh_06::RawDisplayHandle::AppKit(rwh_06::AppKitDisplayHandle::new());
332 unsafe { Ok(rwh_06::DisplayHandle::borrow_raw(raw)) }
333 }
334}
335
336pub(super) fn stop_app_immediately(app: &NSApplication) {
337 autoreleasepool(|_| {
338 app.stop(None);
339 app.postEvent_atStart(&dummy_event().unwrap(), true);
342 });
343}
344
345pub(super) fn notify_windows_of_exit(app: &NSApplication) {
356 for window in app.windows() {
357 window.close();
358 }
359}