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