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