1use super::{geometry::RectFExt, renderer::Renderer};
2use crate::{
3 executor,
4 geometry::{
5 rect::RectF,
6 vector::{vec2f, Vector2F},
7 },
8 keymap::Keystroke,
9 platform::{self, Event, WindowBounds, WindowContext},
10 KeyDownEvent, ModifiersChangedEvent, MouseButton, MouseEvent, MouseMovedEvent, Scene,
11};
12use block::ConcreteBlock;
13use cocoa::{
14 appkit::{
15 CGPoint, NSApplication, NSBackingStoreBuffered, NSModalResponse, NSScreen, NSView,
16 NSViewHeightSizable, NSViewWidthSizable, NSWindow, NSWindowButton, NSWindowStyleMask,
17 },
18 base::{id, nil},
19 foundation::{NSAutoreleasePool, NSInteger, NSPoint, NSRect, NSSize, NSString, NSUInteger},
20 quartzcore::AutoresizingMask,
21};
22use core_graphics::display::CGRect;
23use ctor::ctor;
24use foreign_types::ForeignType as _;
25use objc::{
26 class,
27 declare::ClassDecl,
28 msg_send,
29 runtime::{Class, Object, Protocol, Sel, BOOL, NO, YES},
30 sel, sel_impl,
31};
32use postage::oneshot;
33use smol::Timer;
34use std::{
35 any::Any,
36 cell::{Cell, RefCell},
37 convert::TryInto,
38 ffi::c_void,
39 mem, ptr,
40 rc::{Rc, Weak},
41 sync::Arc,
42 time::Duration,
43};
44
45const WINDOW_STATE_IVAR: &'static str = "windowState";
46
47static mut WINDOW_CLASS: *const Class = ptr::null();
48static mut VIEW_CLASS: *const Class = ptr::null();
49
50#[repr(C)]
51#[derive(Copy, Clone)]
52struct NSRange {
53 pub location: NSUInteger,
54 pub length: NSUInteger,
55}
56
57unsafe impl objc::Encode for NSRange {
58 fn encode() -> objc::Encoding {
59 let encoding = format!(
60 "{{NSRange={}{}}}",
61 NSUInteger::encode().as_str(),
62 NSUInteger::encode().as_str()
63 );
64 unsafe { objc::Encoding::from_str(&encoding) }
65 }
66}
67
68#[allow(non_upper_case_globals)]
69const NSViewLayerContentsRedrawDuringViewResize: NSInteger = 2;
70
71#[ctor]
72unsafe fn build_classes() {
73 WINDOW_CLASS = {
74 let mut decl = ClassDecl::new("GPUIWindow", class!(NSWindow)).unwrap();
75 decl.add_ivar::<*mut c_void>(WINDOW_STATE_IVAR);
76 decl.add_method(sel!(dealloc), dealloc_window as extern "C" fn(&Object, Sel));
77 decl.add_method(
78 sel!(canBecomeMainWindow),
79 yes as extern "C" fn(&Object, Sel) -> BOOL,
80 );
81 decl.add_method(
82 sel!(canBecomeKeyWindow),
83 yes as extern "C" fn(&Object, Sel) -> BOOL,
84 );
85 decl.add_method(
86 sel!(sendEvent:),
87 send_event as extern "C" fn(&Object, Sel, id),
88 );
89 decl.add_method(
90 sel!(windowDidResize:),
91 window_did_resize as extern "C" fn(&Object, Sel, id),
92 );
93 decl.add_method(
94 sel!(windowDidBecomeKey:),
95 window_did_change_key_status as extern "C" fn(&Object, Sel, id),
96 );
97 decl.add_method(
98 sel!(windowDidResignKey:),
99 window_did_change_key_status as extern "C" fn(&Object, Sel, id),
100 );
101 decl.add_method(
102 sel!(windowShouldClose:),
103 window_should_close as extern "C" fn(&Object, Sel, id) -> BOOL,
104 );
105 decl.add_method(sel!(close), close_window as extern "C" fn(&Object, Sel));
106 decl.register()
107 };
108
109 VIEW_CLASS = {
110 let mut decl = ClassDecl::new("GPUIView", class!(NSView)).unwrap();
111 decl.add_ivar::<*mut c_void>(WINDOW_STATE_IVAR);
112
113 decl.add_method(sel!(dealloc), dealloc_view as extern "C" fn(&Object, Sel));
114
115 decl.add_method(
116 sel!(performKeyEquivalent:),
117 handle_key_equivalent as extern "C" fn(&Object, Sel, id) -> BOOL,
118 );
119 decl.add_method(
120 sel!(mouseDown:),
121 handle_view_event as extern "C" fn(&Object, Sel, id),
122 );
123 decl.add_method(
124 sel!(mouseUp:),
125 handle_view_event as extern "C" fn(&Object, Sel, id),
126 );
127 decl.add_method(
128 sel!(rightMouseDown:),
129 handle_view_event as extern "C" fn(&Object, Sel, id),
130 );
131 decl.add_method(
132 sel!(rightMouseUp:),
133 handle_view_event as extern "C" fn(&Object, Sel, id),
134 );
135 decl.add_method(
136 sel!(otherMouseDown:),
137 handle_view_event as extern "C" fn(&Object, Sel, id),
138 );
139 decl.add_method(
140 sel!(otherMouseUp:),
141 handle_view_event as extern "C" fn(&Object, Sel, id),
142 );
143 decl.add_method(
144 sel!(mouseMoved:),
145 handle_view_event as extern "C" fn(&Object, Sel, id),
146 );
147 decl.add_method(
148 sel!(mouseDragged:),
149 handle_view_event as extern "C" fn(&Object, Sel, id),
150 );
151 decl.add_method(
152 sel!(scrollWheel:),
153 handle_view_event as extern "C" fn(&Object, Sel, id),
154 );
155 decl.add_method(
156 sel!(flagsChanged:),
157 handle_view_event as extern "C" fn(&Object, Sel, id),
158 );
159 decl.add_method(
160 sel!(cancelOperation:),
161 cancel_operation as extern "C" fn(&Object, Sel, id),
162 );
163
164 decl.add_method(
165 sel!(makeBackingLayer),
166 make_backing_layer as extern "C" fn(&Object, Sel) -> id,
167 );
168
169 decl.add_protocol(Protocol::get("CALayerDelegate").unwrap());
170 decl.add_method(
171 sel!(viewDidChangeBackingProperties),
172 view_did_change_backing_properties as extern "C" fn(&Object, Sel),
173 );
174 decl.add_method(
175 sel!(setFrameSize:),
176 set_frame_size as extern "C" fn(&Object, Sel, NSSize),
177 );
178 decl.add_method(
179 sel!(displayLayer:),
180 display_layer as extern "C" fn(&Object, Sel, id),
181 );
182
183 decl.add_protocol(Protocol::get("NSTextInputClient").unwrap());
184 decl.add_method(
185 sel!(validAttributesForMarkedText),
186 valid_attributes_for_marked_text as extern "C" fn(&Object, Sel) -> id,
187 );
188 decl.add_method(
189 sel!(hasMarkedText),
190 has_marked_text as extern "C" fn(&Object, Sel) -> BOOL,
191 );
192 decl.add_method(
193 sel!(selectedRange),
194 selected_range as extern "C" fn(&Object, Sel) -> NSRange,
195 );
196 decl.add_method(
197 sel!(firstRectForCharacterRange:actualRange:),
198 first_rect_for_character_range as extern "C" fn(&Object, Sel, NSRange, id) -> NSRect,
199 );
200 decl.add_method(
201 sel!(insertText:replacementRange:),
202 insert_text as extern "C" fn(&Object, Sel, id, NSRange),
203 );
204 decl.add_method(
205 sel!(setMarkedText:selectedRange:replacementRange:),
206 set_marked_text as extern "C" fn(&Object, Sel, id, NSRange, NSRange),
207 );
208 decl.add_method(
209 sel!(attributedSubstringForProposedRange:actualRange:),
210 attributed_substring_for_proposed_range
211 as extern "C" fn(&Object, Sel, NSRange, id) -> id,
212 );
213
214 decl.register()
215 };
216}
217
218pub struct Window(Rc<RefCell<WindowState>>);
219
220struct WindowState {
221 id: usize,
222 native_window: id,
223 event_callback: Option<Box<dyn FnMut(Event) -> bool>>,
224 activate_callback: Option<Box<dyn FnMut(bool)>>,
225 resize_callback: Option<Box<dyn FnMut()>>,
226 should_close_callback: Option<Box<dyn FnMut() -> bool>>,
227 close_callback: Option<Box<dyn FnOnce()>>,
228 synthetic_drag_counter: usize,
229 executor: Rc<executor::Foreground>,
230 scene_to_render: Option<Scene>,
231 renderer: Renderer,
232 command_queue: metal::CommandQueue,
233 last_fresh_keydown: Option<(Keystroke, Option<String>)>,
234 layer: id,
235 traffic_light_position: Option<Vector2F>,
236 previous_modifiers_changed_event: Option<Event>,
237}
238
239impl Window {
240 pub fn open(
241 id: usize,
242 options: platform::WindowOptions,
243 executor: Rc<executor::Foreground>,
244 fonts: Arc<dyn platform::FontSystem>,
245 ) -> Self {
246 const PIXEL_FORMAT: metal::MTLPixelFormat = metal::MTLPixelFormat::BGRA8Unorm;
247
248 unsafe {
249 let pool = NSAutoreleasePool::new(nil);
250
251 let frame = match options.bounds {
252 WindowBounds::Maximized => RectF::new(Default::default(), vec2f(1024., 768.)),
253 WindowBounds::Fixed(rect) => rect,
254 }
255 .to_ns_rect();
256 let mut style_mask = NSWindowStyleMask::NSClosableWindowMask
257 | NSWindowStyleMask::NSMiniaturizableWindowMask
258 | NSWindowStyleMask::NSResizableWindowMask
259 | NSWindowStyleMask::NSTitledWindowMask;
260
261 if options.titlebar_appears_transparent {
262 style_mask |= NSWindowStyleMask::NSFullSizeContentViewWindowMask;
263 }
264
265 let native_window: id = msg_send![WINDOW_CLASS, alloc];
266 let native_window = native_window.initWithContentRect_styleMask_backing_defer_(
267 frame,
268 style_mask,
269 NSBackingStoreBuffered,
270 NO,
271 );
272 assert!(!native_window.is_null());
273
274 if matches!(options.bounds, WindowBounds::Maximized) {
275 let screen = native_window.screen();
276 native_window.setFrame_display_(screen.visibleFrame(), YES);
277 }
278
279 let device = if let Some(device) = metal::Device::system_default() {
280 device
281 } else {
282 let alert: id = msg_send![class!(NSAlert), alloc];
283 let _: () = msg_send![alert, init];
284 let _: () = msg_send![alert, setAlertStyle: 2];
285 let _: () = msg_send![alert, setMessageText: ns_string("Unable to access a compatible graphics device")];
286 let _: NSModalResponse = msg_send![alert, runModal];
287 std::process::exit(1);
288 };
289
290 let layer: id = msg_send![class!(CAMetalLayer), layer];
291 let _: () = msg_send![layer, setDevice: device.as_ptr()];
292 let _: () = msg_send![layer, setPixelFormat: PIXEL_FORMAT];
293 let _: () = msg_send![layer, setAllowsNextDrawableTimeout: NO];
294 let _: () = msg_send![layer, setNeedsDisplayOnBoundsChange: YES];
295 let _: () = msg_send![layer, setPresentsWithTransaction: YES];
296 let _: () = msg_send![
297 layer,
298 setAutoresizingMask: AutoresizingMask::WIDTH_SIZABLE
299 | AutoresizingMask::HEIGHT_SIZABLE
300 ];
301
302 let native_view: id = msg_send![VIEW_CLASS, alloc];
303 let native_view = NSView::init(native_view);
304 assert!(!native_view.is_null());
305
306 let window = Self(Rc::new(RefCell::new(WindowState {
307 id,
308 native_window,
309 event_callback: None,
310 resize_callback: None,
311 should_close_callback: None,
312 close_callback: None,
313 activate_callback: None,
314 synthetic_drag_counter: 0,
315 executor,
316 scene_to_render: Default::default(),
317 renderer: Renderer::new(
318 device.clone(),
319 PIXEL_FORMAT,
320 get_scale_factor(native_window),
321 fonts,
322 ),
323 command_queue: device.new_command_queue(),
324 last_fresh_keydown: None,
325 layer,
326 traffic_light_position: options.traffic_light_position,
327 previous_modifiers_changed_event: None,
328 })));
329
330 (*native_window).set_ivar(
331 WINDOW_STATE_IVAR,
332 Rc::into_raw(window.0.clone()) as *const c_void,
333 );
334 native_window.setDelegate_(native_window);
335 (*native_view).set_ivar(
336 WINDOW_STATE_IVAR,
337 Rc::into_raw(window.0.clone()) as *const c_void,
338 );
339
340 if let Some(title) = options.title.as_ref() {
341 native_window.setTitle_(NSString::alloc(nil).init_str(title));
342 }
343 if options.titlebar_appears_transparent {
344 native_window.setTitlebarAppearsTransparent_(YES);
345 }
346 native_window.setAcceptsMouseMovedEvents_(YES);
347
348 native_view.setAutoresizingMask_(NSViewWidthSizable | NSViewHeightSizable);
349 native_view.setWantsBestResolutionOpenGLSurface_(YES);
350
351 // From winit crate: On Mojave, views automatically become layer-backed shortly after
352 // being added to a native_window. Changing the layer-backedness of a view breaks the
353 // association between the view and its associated OpenGL context. To work around this,
354 // on we explicitly make the view layer-backed up front so that AppKit doesn't do it
355 // itself and break the association with its context.
356 native_view.setWantsLayer(YES);
357 let _: () = msg_send![
358 native_view,
359 setLayerContentsRedrawPolicy: NSViewLayerContentsRedrawDuringViewResize
360 ];
361
362 native_window.setContentView_(native_view.autorelease());
363 native_window.makeFirstResponder_(native_view);
364
365 native_window.center();
366 native_window.makeKeyAndOrderFront_(nil);
367
368 window.0.borrow().move_traffic_light();
369 pool.drain();
370
371 window
372 }
373 }
374
375 pub fn key_window_id() -> Option<usize> {
376 unsafe {
377 let app = NSApplication::sharedApplication(nil);
378 let key_window: id = msg_send![app, keyWindow];
379 if msg_send![key_window, isKindOfClass: WINDOW_CLASS] {
380 let id = get_window_state(&*key_window).borrow().id;
381 Some(id)
382 } else {
383 None
384 }
385 }
386 }
387}
388
389impl Drop for Window {
390 fn drop(&mut self) {
391 unsafe {
392 self.0.as_ref().borrow().native_window.close();
393 }
394 }
395}
396
397impl platform::Window for Window {
398 fn as_any_mut(&mut self) -> &mut dyn Any {
399 self
400 }
401
402 fn on_event(&mut self, callback: Box<dyn FnMut(Event) -> bool>) {
403 self.0.as_ref().borrow_mut().event_callback = Some(callback);
404 }
405
406 fn on_resize(&mut self, callback: Box<dyn FnMut()>) {
407 self.0.as_ref().borrow_mut().resize_callback = Some(callback);
408 }
409
410 fn on_should_close(&mut self, callback: Box<dyn FnMut() -> bool>) {
411 self.0.as_ref().borrow_mut().should_close_callback = Some(callback);
412 }
413
414 fn on_close(&mut self, callback: Box<dyn FnOnce()>) {
415 self.0.as_ref().borrow_mut().close_callback = Some(callback);
416 }
417
418 fn on_active_status_change(&mut self, callback: Box<dyn FnMut(bool)>) {
419 self.0.as_ref().borrow_mut().activate_callback = Some(callback);
420 }
421
422 fn prompt(
423 &self,
424 level: platform::PromptLevel,
425 msg: &str,
426 answers: &[&str],
427 ) -> oneshot::Receiver<usize> {
428 unsafe {
429 let alert: id = msg_send![class!(NSAlert), alloc];
430 let alert: id = msg_send![alert, init];
431 let alert_style = match level {
432 platform::PromptLevel::Info => 1,
433 platform::PromptLevel::Warning => 0,
434 platform::PromptLevel::Critical => 2,
435 };
436 let _: () = msg_send![alert, setAlertStyle: alert_style];
437 let _: () = msg_send![alert, setMessageText: ns_string(msg)];
438 for (ix, answer) in answers.into_iter().enumerate() {
439 let button: id = msg_send![alert, addButtonWithTitle: ns_string(answer)];
440 let _: () = msg_send![button, setTag: ix as NSInteger];
441 }
442 let (done_tx, done_rx) = oneshot::channel();
443 let done_tx = Cell::new(Some(done_tx));
444 let block = ConcreteBlock::new(move |answer: NSInteger| {
445 if let Some(mut done_tx) = done_tx.take() {
446 let _ = postage::sink::Sink::try_send(&mut done_tx, answer.try_into().unwrap());
447 }
448 });
449 let block = block.copy();
450 let native_window = self.0.borrow().native_window;
451 self.0
452 .borrow()
453 .executor
454 .spawn(async move {
455 let _: () = msg_send![
456 alert,
457 beginSheetModalForWindow: native_window
458 completionHandler: block
459 ];
460 })
461 .detach();
462
463 done_rx
464 }
465 }
466
467 fn activate(&self) {
468 let window = self.0.borrow().native_window;
469 self.0
470 .borrow()
471 .executor
472 .spawn(async move {
473 unsafe {
474 let _: () = msg_send![window, makeKeyAndOrderFront: nil];
475 }
476 })
477 .detach();
478 }
479
480 fn set_title(&mut self, title: &str) {
481 unsafe {
482 let app = NSApplication::sharedApplication(nil);
483 let window = self.0.borrow().native_window;
484 let title = ns_string(title);
485 msg_send![app, changeWindowsItem:window title:title filename:false]
486 }
487 }
488
489 fn set_edited(&mut self, edited: bool) {
490 unsafe {
491 let window = self.0.borrow().native_window;
492 msg_send![window, setDocumentEdited: edited as BOOL]
493 }
494
495 // Changing the document edited state resets the traffic light position,
496 // so we have to move it again.
497 self.0.borrow().move_traffic_light();
498 }
499
500 fn show_character_palette(&self) {
501 unsafe {
502 let app = NSApplication::sharedApplication(nil);
503 let window = self.0.borrow().native_window;
504 let _: () = msg_send![app, orderFrontCharacterPalette: window];
505 }
506 }
507}
508
509impl platform::WindowContext for Window {
510 fn size(&self) -> Vector2F {
511 self.0.as_ref().borrow().size()
512 }
513
514 fn scale_factor(&self) -> f32 {
515 self.0.as_ref().borrow().scale_factor()
516 }
517
518 fn present_scene(&mut self, scene: Scene) {
519 self.0.as_ref().borrow_mut().present_scene(scene);
520 }
521
522 fn titlebar_height(&self) -> f32 {
523 self.0.as_ref().borrow().titlebar_height()
524 }
525}
526
527impl WindowState {
528 fn move_traffic_light(&self) {
529 if let Some(traffic_light_position) = self.traffic_light_position {
530 let titlebar_height = self.titlebar_height();
531
532 unsafe {
533 let close_button: id = msg_send![
534 self.native_window,
535 standardWindowButton: NSWindowButton::NSWindowCloseButton
536 ];
537 let min_button: id = msg_send![
538 self.native_window,
539 standardWindowButton: NSWindowButton::NSWindowMiniaturizeButton
540 ];
541 let zoom_button: id = msg_send![
542 self.native_window,
543 standardWindowButton: NSWindowButton::NSWindowZoomButton
544 ];
545
546 let mut close_button_frame: CGRect = msg_send![close_button, frame];
547 let mut min_button_frame: CGRect = msg_send![min_button, frame];
548 let mut zoom_button_frame: CGRect = msg_send![zoom_button, frame];
549 let mut origin = vec2f(
550 traffic_light_position.x(),
551 titlebar_height
552 - traffic_light_position.y()
553 - close_button_frame.size.height as f32,
554 );
555 let button_spacing =
556 (min_button_frame.origin.x - close_button_frame.origin.x) as f32;
557
558 close_button_frame.origin = CGPoint::new(origin.x() as f64, origin.y() as f64);
559 let _: () = msg_send![close_button, setFrame: close_button_frame];
560 origin.set_x(origin.x() + button_spacing);
561
562 min_button_frame.origin = CGPoint::new(origin.x() as f64, origin.y() as f64);
563 let _: () = msg_send![min_button, setFrame: min_button_frame];
564 origin.set_x(origin.x() + button_spacing);
565
566 zoom_button_frame.origin = CGPoint::new(origin.x() as f64, origin.y() as f64);
567 let _: () = msg_send![zoom_button, setFrame: zoom_button_frame];
568 }
569 }
570 }
571}
572
573impl platform::WindowContext for WindowState {
574 fn size(&self) -> Vector2F {
575 let NSSize { width, height, .. } =
576 unsafe { NSView::frame(self.native_window.contentView()) }.size;
577 vec2f(width as f32, height as f32)
578 }
579
580 fn scale_factor(&self) -> f32 {
581 get_scale_factor(self.native_window)
582 }
583
584 fn titlebar_height(&self) -> f32 {
585 unsafe {
586 let frame = NSWindow::frame(self.native_window);
587 let content_layout_rect: CGRect = msg_send![self.native_window, contentLayoutRect];
588 (frame.size.height - content_layout_rect.size.height) as f32
589 }
590 }
591
592 fn present_scene(&mut self, scene: Scene) {
593 self.scene_to_render = Some(scene);
594 unsafe {
595 let _: () = msg_send![self.native_window.contentView(), setNeedsDisplay: YES];
596 }
597 }
598}
599
600fn get_scale_factor(native_window: id) -> f32 {
601 unsafe {
602 let screen: id = msg_send![native_window, screen];
603 NSScreen::backingScaleFactor(screen) as f32
604 }
605}
606
607unsafe fn get_window_state(object: &Object) -> Rc<RefCell<WindowState>> {
608 let raw: *mut c_void = *object.get_ivar(WINDOW_STATE_IVAR);
609 let rc1 = Rc::from_raw(raw as *mut RefCell<WindowState>);
610 let rc2 = rc1.clone();
611 mem::forget(rc1);
612 rc2
613}
614
615unsafe fn drop_window_state(object: &Object) {
616 let raw: *mut c_void = *object.get_ivar(WINDOW_STATE_IVAR);
617 Rc::from_raw(raw as *mut RefCell<WindowState>);
618}
619
620extern "C" fn yes(_: &Object, _: Sel) -> BOOL {
621 YES
622}
623
624extern "C" fn dealloc_window(this: &Object, _: Sel) {
625 unsafe {
626 drop_window_state(this);
627 let () = msg_send![super(this, class!(NSWindow)), dealloc];
628 }
629}
630
631extern "C" fn dealloc_view(this: &Object, _: Sel) {
632 unsafe {
633 drop_window_state(this);
634 let () = msg_send![super(this, class!(NSView)), dealloc];
635 }
636}
637
638extern "C" fn handle_key_equivalent(this: &Object, _: Sel, native_event: id) -> BOOL {
639 let window_state = unsafe { get_window_state(this) };
640 let mut window_state_borrow = window_state.as_ref().borrow_mut();
641
642 let event = unsafe { Event::from_native(native_event, Some(window_state_borrow.size().y())) };
643 if let Some(event) = event {
644 match &event {
645 Event::KeyDown(KeyDownEvent {
646 keystroke,
647 input,
648 is_held,
649 }) => {
650 let keydown = (keystroke.clone(), input.clone());
651 // Ignore events from held-down keys after some of the initially-pressed keys
652 // were released.
653 if *is_held {
654 if window_state_borrow.last_fresh_keydown.as_ref() != Some(&keydown) {
655 return YES;
656 }
657 } else {
658 window_state_borrow.last_fresh_keydown = Some(keydown);
659 }
660 }
661 _ => return NO,
662 }
663
664 if let Some(mut callback) = window_state_borrow.event_callback.take() {
665 drop(window_state_borrow);
666 let handled = callback(event);
667 window_state.borrow_mut().event_callback = Some(callback);
668 handled as BOOL
669 } else {
670 NO
671 }
672 } else {
673 NO
674 }
675}
676
677extern "C" fn handle_view_event(this: &Object, _: Sel, native_event: id) {
678 let window_state = unsafe { get_window_state(this) };
679 let weak_window_state = Rc::downgrade(&window_state);
680 let mut window_state_borrow = window_state.as_ref().borrow_mut();
681
682 let event = unsafe { Event::from_native(native_event, Some(window_state_borrow.size().y())) };
683
684 if let Some(event) = event {
685 match &event {
686 Event::MouseMoved(
687 event @ MouseMovedEvent {
688 pressed_button: Some(_),
689 ..
690 },
691 ) => {
692 window_state_borrow.synthetic_drag_counter += 1;
693 window_state_borrow
694 .executor
695 .spawn(synthetic_drag(
696 weak_window_state,
697 window_state_borrow.synthetic_drag_counter,
698 *event,
699 ))
700 .detach();
701 }
702 Event::MouseUp(MouseEvent {
703 button: MouseButton::Left,
704 ..
705 }) => {
706 window_state_borrow.synthetic_drag_counter += 1;
707 }
708 Event::ModifiersChanged(ModifiersChangedEvent {
709 ctrl,
710 alt,
711 shift,
712 cmd,
713 }) => {
714 // Only raise modifiers changed event when they have actually changed
715 if let Some(Event::ModifiersChanged(ModifiersChangedEvent {
716 ctrl: prev_ctrl,
717 alt: prev_alt,
718 shift: prev_shift,
719 cmd: prev_cmd,
720 })) = &window_state_borrow.previous_modifiers_changed_event
721 {
722 if prev_ctrl == ctrl
723 && prev_alt == alt
724 && prev_shift == shift
725 && prev_cmd == cmd
726 {
727 return;
728 }
729 }
730
731 window_state_borrow.previous_modifiers_changed_event = Some(event.clone());
732 }
733 _ => {}
734 }
735
736 if let Some(mut callback) = window_state_borrow.event_callback.take() {
737 drop(window_state_borrow);
738 callback(event);
739 window_state.borrow_mut().event_callback = Some(callback);
740 }
741 }
742}
743
744// Allows us to receive `cmd-.` (the shortcut for closing a dialog)
745// https://bugs.eclipse.org/bugs/show_bug.cgi?id=300620#c6
746extern "C" fn cancel_operation(this: &Object, _sel: Sel, _sender: id) {
747 let window_state = unsafe { get_window_state(this) };
748 let mut window_state_borrow = window_state.as_ref().borrow_mut();
749
750 let chars = ".".to_string();
751 let keystroke = Keystroke {
752 cmd: true,
753 ctrl: false,
754 alt: false,
755 shift: false,
756 key: chars.clone(),
757 };
758 let event = Event::KeyDown(KeyDownEvent {
759 keystroke: keystroke.clone(),
760 input: Some(chars.clone()),
761 is_held: false,
762 });
763
764 window_state_borrow.last_fresh_keydown = Some((keystroke, Some(chars)));
765 if let Some(mut callback) = window_state_borrow.event_callback.take() {
766 drop(window_state_borrow);
767 callback(event);
768 window_state.borrow_mut().event_callback = Some(callback);
769 }
770}
771
772extern "C" fn send_event(this: &Object, _: Sel, native_event: id) {
773 unsafe {
774 let () = msg_send![super(this, class!(NSWindow)), sendEvent: native_event];
775 }
776}
777
778extern "C" fn window_did_resize(this: &Object, _: Sel, _: id) {
779 let window_state = unsafe { get_window_state(this) };
780 window_state.as_ref().borrow().move_traffic_light();
781}
782
783extern "C" fn window_did_change_key_status(this: &Object, selector: Sel, _: id) {
784 let is_active = if selector == sel!(windowDidBecomeKey:) {
785 true
786 } else if selector == sel!(windowDidResignKey:) {
787 false
788 } else {
789 unreachable!();
790 };
791
792 let window_state = unsafe { get_window_state(this) };
793 let executor = window_state.as_ref().borrow().executor.clone();
794 executor
795 .spawn(async move {
796 let mut window_state_borrow = window_state.as_ref().borrow_mut();
797 if let Some(mut callback) = window_state_borrow.activate_callback.take() {
798 drop(window_state_borrow);
799 callback(is_active);
800 window_state.borrow_mut().activate_callback = Some(callback);
801 };
802 })
803 .detach();
804}
805
806extern "C" fn window_should_close(this: &Object, _: Sel, _: id) -> BOOL {
807 let window_state = unsafe { get_window_state(this) };
808 let mut window_state_borrow = window_state.as_ref().borrow_mut();
809 if let Some(mut callback) = window_state_borrow.should_close_callback.take() {
810 drop(window_state_borrow);
811 let should_close = callback();
812 window_state.borrow_mut().should_close_callback = Some(callback);
813 should_close as BOOL
814 } else {
815 YES
816 }
817}
818
819extern "C" fn close_window(this: &Object, _: Sel) {
820 unsafe {
821 let close_callback = {
822 let window_state = get_window_state(this);
823 window_state
824 .as_ref()
825 .try_borrow_mut()
826 .ok()
827 .and_then(|mut window_state| window_state.close_callback.take())
828 };
829
830 if let Some(callback) = close_callback {
831 callback();
832 }
833
834 let () = msg_send![super(this, class!(NSWindow)), close];
835 }
836}
837
838extern "C" fn make_backing_layer(this: &Object, _: Sel) -> id {
839 let window_state = unsafe { get_window_state(this) };
840 let window_state = window_state.as_ref().borrow();
841 window_state.layer
842}
843
844extern "C" fn view_did_change_backing_properties(this: &Object, _: Sel) {
845 let window_state = unsafe { get_window_state(this) };
846 let mut window_state_borrow = window_state.as_ref().borrow_mut();
847
848 unsafe {
849 let scale_factor = window_state_borrow.scale_factor() as f64;
850 let size = window_state_borrow.size();
851 let drawable_size: NSSize = NSSize {
852 width: size.x() as f64 * scale_factor,
853 height: size.y() as f64 * scale_factor,
854 };
855
856 let _: () = msg_send![window_state_borrow.layer, setContentsScale: scale_factor];
857 let _: () = msg_send![window_state_borrow.layer, setDrawableSize: drawable_size];
858 }
859
860 if let Some(mut callback) = window_state_borrow.resize_callback.take() {
861 drop(window_state_borrow);
862 callback();
863 window_state.as_ref().borrow_mut().resize_callback = Some(callback);
864 };
865}
866
867extern "C" fn set_frame_size(this: &Object, _: Sel, size: NSSize) {
868 let window_state = unsafe { get_window_state(this) };
869 let window_state_borrow = window_state.as_ref().borrow();
870
871 if window_state_borrow.size() == vec2f(size.width as f32, size.height as f32) {
872 return;
873 }
874
875 unsafe {
876 let _: () = msg_send![super(this, class!(NSView)), setFrameSize: size];
877 }
878
879 let scale_factor = window_state_borrow.scale_factor() as f64;
880 let drawable_size: NSSize = NSSize {
881 width: size.width * scale_factor,
882 height: size.height * scale_factor,
883 };
884
885 unsafe {
886 let _: () = msg_send![window_state_borrow.layer, setDrawableSize: drawable_size];
887 }
888
889 drop(window_state_borrow);
890 let mut window_state_borrow = window_state.borrow_mut();
891 if let Some(mut callback) = window_state_borrow.resize_callback.take() {
892 drop(window_state_borrow);
893 callback();
894 window_state.borrow_mut().resize_callback = Some(callback);
895 };
896}
897
898extern "C" fn display_layer(this: &Object, _: Sel, _: id) {
899 unsafe {
900 let window_state = get_window_state(this);
901 let mut window_state = window_state.as_ref().borrow_mut();
902
903 if let Some(scene) = window_state.scene_to_render.take() {
904 let drawable: &metal::MetalDrawableRef = msg_send![window_state.layer, nextDrawable];
905 let command_queue = window_state.command_queue.clone();
906 let command_buffer = command_queue.new_command_buffer();
907
908 let size = window_state.size();
909 let scale_factor = window_state.scale_factor();
910
911 window_state.renderer.render(
912 &scene,
913 size * scale_factor,
914 command_buffer,
915 drawable.texture(),
916 );
917
918 command_buffer.commit();
919 command_buffer.wait_until_completed();
920 drawable.present();
921 };
922 }
923}
924
925extern "C" fn valid_attributes_for_marked_text(_: &Object, _: Sel) -> id {
926 unsafe { msg_send![class!(NSArray), array] }
927}
928
929extern "C" fn has_marked_text(_: &Object, _: Sel) -> BOOL {
930 false as BOOL
931}
932
933extern "C" fn selected_range(_: &Object, _: Sel) -> NSRange {
934 NSRange {
935 location: 0,
936 length: 0,
937 }
938}
939
940extern "C" fn first_rect_for_character_range(_: &Object, _: Sel, _: NSRange, _: id) -> NSRect {
941 NSRect::new(NSPoint::new(0., 0.), NSSize::new(20., 20.))
942}
943
944extern "C" fn insert_text(_: &Object, _: Sel, _: id, _: NSRange) {}
945
946extern "C" fn set_marked_text(_: &Object, _: Sel, _: id, _: NSRange, _: NSRange) {}
947
948extern "C" fn attributed_substring_for_proposed_range(_: &Object, _: Sel, _: NSRange, _: id) -> id {
949 unsafe { msg_send![class!(NSAttributedString), alloc] }
950}
951
952async fn synthetic_drag(
953 window_state: Weak<RefCell<WindowState>>,
954 drag_id: usize,
955 event: MouseMovedEvent,
956) {
957 loop {
958 Timer::after(Duration::from_millis(16)).await;
959 if let Some(window_state) = window_state.upgrade() {
960 let mut window_state_borrow = window_state.borrow_mut();
961 if window_state_borrow.synthetic_drag_counter == drag_id {
962 if let Some(mut callback) = window_state_borrow.event_callback.take() {
963 drop(window_state_borrow);
964 callback(Event::MouseMoved(event));
965 window_state.borrow_mut().event_callback = Some(callback);
966 }
967 } else {
968 break;
969 }
970 }
971 }
972}
973
974unsafe fn ns_string(string: &str) -> id {
975 NSString::alloc(nil).init_str(string).autorelease()
976}