1 use std::{
2     boxed::Box,
3     collections::VecDeque,
4     os::raw::*,
5     slice, str,
6     sync::{Arc, Mutex, Weak},
7 };
8 
9 use cocoa::{
10     appkit::{NSApp, NSEvent, NSEventModifierFlags, NSEventPhase, NSView, NSWindow},
11     base::{id, nil},
12     foundation::{NSInteger, NSPoint, NSRect, NSSize, NSString, NSUInteger},
13 };
14 use objc::{
15     declare::ClassDecl,
16     runtime::{Class, Object, Protocol, Sel, BOOL, NO, YES},
17 };
18 
19 use crate::{
20     dpi::LogicalPosition,
21     event::{
22         DeviceEvent, ElementState, Event, KeyboardInput, ModifiersState, MouseButton,
23         MouseScrollDelta, TouchPhase, VirtualKeyCode, WindowEvent,
24     },
25     platform_impl::platform::{
26         app_state::AppState,
27         event::{
28             char_to_keycode, check_function_keys, event_mods, get_scancode, modifier_event,
29             scancode_to_keycode, EventWrapper,
30         },
31         ffi::*,
32         util::{self, IdRef},
33         window::get_window_id,
34         DEVICE_ID,
35     },
36     window::WindowId,
37 };
38 
39 pub struct CursorState {
40     pub visible: bool,
41     pub cursor: util::Cursor,
42 }
43 
44 impl Default for CursorState {
default() -> Self45     fn default() -> Self {
46         Self {
47             visible: true,
48             cursor: Default::default(),
49         }
50     }
51 }
52 
53 pub(super) struct ViewState {
54     ns_window: id,
55     pub cursor_state: Arc<Mutex<CursorState>>,
56     ime_spot: Option<(f64, f64)>,
57     raw_characters: Option<String>,
58     is_key_down: bool,
59     pub(super) modifiers: ModifiersState,
60     tracking_rect: Option<NSInteger>,
61 }
62 
63 impl ViewState {
get_scale_factor(&self) -> f6464     fn get_scale_factor(&self) -> f64 {
65         (unsafe { NSWindow::backingScaleFactor(self.ns_window) }) as f64
66     }
67 }
68 
new_view(ns_window: id) -> (IdRef, Weak<Mutex<CursorState>>)69 pub fn new_view(ns_window: id) -> (IdRef, Weak<Mutex<CursorState>>) {
70     let cursor_state = Default::default();
71     let cursor_access = Arc::downgrade(&cursor_state);
72     let state = ViewState {
73         ns_window,
74         cursor_state,
75         ime_spot: None,
76         raw_characters: None,
77         is_key_down: false,
78         modifiers: Default::default(),
79         tracking_rect: None,
80     };
81     unsafe {
82         // This is free'd in `dealloc`
83         let state_ptr = Box::into_raw(Box::new(state)) as *mut c_void;
84         let ns_view: id = msg_send![VIEW_CLASS.0, alloc];
85         (
86             IdRef::new(msg_send![ns_view, initWithWinit: state_ptr]),
87             cursor_access,
88         )
89     }
90 }
91 
set_ime_position(ns_view: id, input_context: id, x: f64, y: f64)92 pub unsafe fn set_ime_position(ns_view: id, input_context: id, x: f64, y: f64) {
93     let state_ptr: *mut c_void = *(*ns_view).get_mut_ivar("winitState");
94     let state = &mut *(state_ptr as *mut ViewState);
95     let content_rect =
96         NSWindow::contentRectForFrameRect_(state.ns_window, NSWindow::frame(state.ns_window));
97     let base_x = content_rect.origin.x as f64;
98     let base_y = (content_rect.origin.y + content_rect.size.height) as f64;
99     state.ime_spot = Some((base_x + x, base_y - y));
100     let _: () = msg_send![input_context, invalidateCharacterCoordinates];
101 }
102 
103 struct ViewClass(*const Class);
104 unsafe impl Send for ViewClass {}
105 unsafe impl Sync for ViewClass {}
106 
107 lazy_static! {
108     static ref VIEW_CLASS: ViewClass = unsafe {
109         let superclass = class!(NSView);
110         let mut decl = ClassDecl::new("WinitView", superclass).unwrap();
111         decl.add_method(sel!(dealloc), dealloc as extern "C" fn(&Object, Sel));
112         decl.add_method(
113             sel!(initWithWinit:),
114             init_with_winit as extern "C" fn(&Object, Sel, *mut c_void) -> id,
115         );
116         decl.add_method(
117             sel!(viewDidMoveToWindow),
118             view_did_move_to_window as extern "C" fn(&Object, Sel),
119         );
120         decl.add_method(
121             sel!(drawRect:),
122             draw_rect as extern "C" fn(&Object, Sel, NSRect),
123         );
124         decl.add_method(
125             sel!(acceptsFirstResponder),
126             accepts_first_responder as extern "C" fn(&Object, Sel) -> BOOL,
127         );
128         decl.add_method(
129             sel!(touchBar),
130             touch_bar as extern "C" fn(&Object, Sel) -> BOOL,
131         );
132         decl.add_method(
133             sel!(resetCursorRects),
134             reset_cursor_rects as extern "C" fn(&Object, Sel),
135         );
136         decl.add_method(
137             sel!(hasMarkedText),
138             has_marked_text as extern "C" fn(&Object, Sel) -> BOOL,
139         );
140         decl.add_method(
141             sel!(markedRange),
142             marked_range as extern "C" fn(&Object, Sel) -> NSRange,
143         );
144         decl.add_method(
145             sel!(selectedRange),
146             selected_range as extern "C" fn(&Object, Sel) -> NSRange,
147         );
148         decl.add_method(
149             sel!(setMarkedText:selectedRange:replacementRange:),
150             set_marked_text as extern "C" fn(&mut Object, Sel, id, NSRange, NSRange),
151         );
152         decl.add_method(sel!(unmarkText), unmark_text as extern "C" fn(&Object, Sel));
153         decl.add_method(
154             sel!(validAttributesForMarkedText),
155             valid_attributes_for_marked_text as extern "C" fn(&Object, Sel) -> id,
156         );
157         decl.add_method(
158             sel!(attributedSubstringForProposedRange:actualRange:),
159             attributed_substring_for_proposed_range
160                 as extern "C" fn(&Object, Sel, NSRange, *mut c_void) -> id,
161         );
162         decl.add_method(
163             sel!(insertText:replacementRange:),
164             insert_text as extern "C" fn(&Object, Sel, id, NSRange),
165         );
166         decl.add_method(
167             sel!(characterIndexForPoint:),
168             character_index_for_point as extern "C" fn(&Object, Sel, NSPoint) -> NSUInteger,
169         );
170         decl.add_method(
171             sel!(firstRectForCharacterRange:actualRange:),
172             first_rect_for_character_range
173                 as extern "C" fn(&Object, Sel, NSRange, *mut c_void) -> NSRect,
174         );
175         decl.add_method(
176             sel!(doCommandBySelector:),
177             do_command_by_selector as extern "C" fn(&Object, Sel, Sel),
178         );
179         decl.add_method(sel!(keyDown:), key_down as extern "C" fn(&Object, Sel, id));
180         decl.add_method(sel!(keyUp:), key_up as extern "C" fn(&Object, Sel, id));
181         decl.add_method(
182             sel!(flagsChanged:),
183             flags_changed as extern "C" fn(&Object, Sel, id),
184         );
185         decl.add_method(
186             sel!(insertTab:),
187             insert_tab as extern "C" fn(&Object, Sel, id),
188         );
189         decl.add_method(
190             sel!(insertBackTab:),
191             insert_back_tab as extern "C" fn(&Object, Sel, id),
192         );
193         decl.add_method(
194             sel!(mouseDown:),
195             mouse_down as extern "C" fn(&Object, Sel, id),
196         );
197         decl.add_method(sel!(mouseUp:), mouse_up as extern "C" fn(&Object, Sel, id));
198         decl.add_method(
199             sel!(rightMouseDown:),
200             right_mouse_down as extern "C" fn(&Object, Sel, id),
201         );
202         decl.add_method(
203             sel!(rightMouseUp:),
204             right_mouse_up as extern "C" fn(&Object, Sel, id),
205         );
206         decl.add_method(
207             sel!(otherMouseDown:),
208             other_mouse_down as extern "C" fn(&Object, Sel, id),
209         );
210         decl.add_method(
211             sel!(otherMouseUp:),
212             other_mouse_up as extern "C" fn(&Object, Sel, id),
213         );
214         decl.add_method(
215             sel!(mouseMoved:),
216             mouse_moved as extern "C" fn(&Object, Sel, id),
217         );
218         decl.add_method(
219             sel!(mouseDragged:),
220             mouse_dragged as extern "C" fn(&Object, Sel, id),
221         );
222         decl.add_method(
223             sel!(rightMouseDragged:),
224             right_mouse_dragged as extern "C" fn(&Object, Sel, id),
225         );
226         decl.add_method(
227             sel!(otherMouseDragged:),
228             other_mouse_dragged as extern "C" fn(&Object, Sel, id),
229         );
230         decl.add_method(
231             sel!(mouseEntered:),
232             mouse_entered as extern "C" fn(&Object, Sel, id),
233         );
234         decl.add_method(
235             sel!(mouseExited:),
236             mouse_exited as extern "C" fn(&Object, Sel, id),
237         );
238         decl.add_method(
239             sel!(scrollWheel:),
240             scroll_wheel as extern "C" fn(&Object, Sel, id),
241         );
242         decl.add_method(
243             sel!(pressureChangeWithEvent:),
244             pressure_change_with_event as extern "C" fn(&Object, Sel, id),
245         );
246         decl.add_method(
247             sel!(_wantsKeyDownForEvent:),
248             wants_key_down_for_event as extern "C" fn(&Object, Sel, id) -> BOOL,
249         );
250         decl.add_method(
251             sel!(cancelOperation:),
252             cancel_operation as extern "C" fn(&Object, Sel, id),
253         );
254         decl.add_method(
255             sel!(frameDidChange:),
256             frame_did_change as extern "C" fn(&Object, Sel, id),
257         );
258         decl.add_ivar::<*mut c_void>("winitState");
259         decl.add_ivar::<id>("markedText");
260         let protocol = Protocol::get("NSTextInputClient").unwrap();
261         decl.add_protocol(&protocol);
262         ViewClass(decl.register())
263     };
264 }
265 
dealloc(this: &Object, _sel: Sel)266 extern "C" fn dealloc(this: &Object, _sel: Sel) {
267     unsafe {
268         let state: *mut c_void = *this.get_ivar("winitState");
269         let marked_text: id = *this.get_ivar("markedText");
270         let _: () = msg_send![marked_text, release];
271         Box::from_raw(state as *mut ViewState);
272     }
273 }
274 
init_with_winit(this: &Object, _sel: Sel, state: *mut c_void) -> id275 extern "C" fn init_with_winit(this: &Object, _sel: Sel, state: *mut c_void) -> id {
276     unsafe {
277         let this: id = msg_send![this, init];
278         if this != nil {
279             (*this).set_ivar("winitState", state);
280             let marked_text =
281                 <id as NSMutableAttributedString>::init(NSMutableAttributedString::alloc(nil));
282             (*this).set_ivar("markedText", marked_text);
283             let _: () = msg_send![this, setPostsFrameChangedNotifications: YES];
284 
285             let notification_center: &Object =
286                 msg_send![class!(NSNotificationCenter), defaultCenter];
287             let notification_name =
288                 NSString::alloc(nil).init_str("NSViewFrameDidChangeNotification");
289             let _: () = msg_send![
290                 notification_center,
291                 addObserver: this
292                 selector: sel!(frameDidChange:)
293                 name: notification_name
294                 object: this
295             ];
296         }
297         this
298     }
299 }
300 
view_did_move_to_window(this: &Object, _sel: Sel)301 extern "C" fn view_did_move_to_window(this: &Object, _sel: Sel) {
302     trace!("Triggered `viewDidMoveToWindow`");
303     unsafe {
304         let state_ptr: *mut c_void = *this.get_ivar("winitState");
305         let state = &mut *(state_ptr as *mut ViewState);
306 
307         if let Some(tracking_rect) = state.tracking_rect.take() {
308             let _: () = msg_send![this, removeTrackingRect: tracking_rect];
309         }
310 
311         let rect: NSRect = msg_send![this, visibleRect];
312         let tracking_rect: NSInteger = msg_send![this,
313             addTrackingRect:rect
314             owner:this
315             userData:nil
316             assumeInside:NO
317         ];
318         state.tracking_rect = Some(tracking_rect);
319     }
320     trace!("Completed `viewDidMoveToWindow`");
321 }
322 
frame_did_change(this: &Object, _sel: Sel, _event: id)323 extern "C" fn frame_did_change(this: &Object, _sel: Sel, _event: id) {
324     unsafe {
325         let state_ptr: *mut c_void = *this.get_ivar("winitState");
326         let state = &mut *(state_ptr as *mut ViewState);
327 
328         if let Some(tracking_rect) = state.tracking_rect.take() {
329             let _: () = msg_send![this, removeTrackingRect: tracking_rect];
330         }
331 
332         let rect: NSRect = msg_send![this, visibleRect];
333         let tracking_rect: NSInteger = msg_send![this,
334             addTrackingRect:rect
335             owner:this
336             userData:nil
337             assumeInside:NO
338         ];
339 
340         state.tracking_rect = Some(tracking_rect);
341     }
342 }
343 
draw_rect(this: &Object, _sel: Sel, rect: NSRect)344 extern "C" fn draw_rect(this: &Object, _sel: Sel, rect: NSRect) {
345     unsafe {
346         let state_ptr: *mut c_void = *this.get_ivar("winitState");
347         let state = &mut *(state_ptr as *mut ViewState);
348 
349         AppState::queue_redraw(WindowId(get_window_id(state.ns_window)));
350 
351         let superclass = util::superclass(this);
352         let () = msg_send![super(this, superclass), drawRect: rect];
353     }
354 }
355 
accepts_first_responder(_this: &Object, _sel: Sel) -> BOOL356 extern "C" fn accepts_first_responder(_this: &Object, _sel: Sel) -> BOOL {
357     YES
358 }
359 
360 // This is necessary to prevent a beefy terminal error on MacBook Pros:
361 // IMKInputSession [0x7fc573576ff0 presentFunctionRowItemTextInputViewWithEndpoint:completionHandler:] : [self textInputContext]=0x7fc573558e10 *NO* NSRemoteViewController to client, NSError=Error Domain=NSCocoaErrorDomain Code=4099 "The connection from pid 0 was invalidated from this process." UserInfo={NSDebugDescription=The connection from pid 0 was invalidated from this process.}, com.apple.inputmethod.EmojiFunctionRowItem
362 // TODO: Add an API extension for using `NSTouchBar`
touch_bar(_this: &Object, _sel: Sel) -> BOOL363 extern "C" fn touch_bar(_this: &Object, _sel: Sel) -> BOOL {
364     NO
365 }
366 
reset_cursor_rects(this: &Object, _sel: Sel)367 extern "C" fn reset_cursor_rects(this: &Object, _sel: Sel) {
368     unsafe {
369         let state_ptr: *mut c_void = *this.get_ivar("winitState");
370         let state = &mut *(state_ptr as *mut ViewState);
371 
372         let bounds: NSRect = msg_send![this, bounds];
373         let cursor_state = state.cursor_state.lock().unwrap();
374         let cursor = if cursor_state.visible {
375             cursor_state.cursor.load()
376         } else {
377             util::invisible_cursor()
378         };
379         let _: () = msg_send![this,
380             addCursorRect:bounds
381             cursor:cursor
382         ];
383     }
384 }
385 
has_marked_text(this: &Object, _sel: Sel) -> BOOL386 extern "C" fn has_marked_text(this: &Object, _sel: Sel) -> BOOL {
387     unsafe {
388         trace!("Triggered `hasMarkedText`");
389         let marked_text: id = *this.get_ivar("markedText");
390         trace!("Completed `hasMarkedText`");
391         (marked_text.length() > 0) as i8
392     }
393 }
394 
marked_range(this: &Object, _sel: Sel) -> NSRange395 extern "C" fn marked_range(this: &Object, _sel: Sel) -> NSRange {
396     unsafe {
397         trace!("Triggered `markedRange`");
398         let marked_text: id = *this.get_ivar("markedText");
399         let length = marked_text.length();
400         trace!("Completed `markedRange`");
401         if length > 0 {
402             NSRange::new(0, length - 1)
403         } else {
404             util::EMPTY_RANGE
405         }
406     }
407 }
408 
selected_range(_this: &Object, _sel: Sel) -> NSRange409 extern "C" fn selected_range(_this: &Object, _sel: Sel) -> NSRange {
410     trace!("Triggered `selectedRange`");
411     trace!("Completed `selectedRange`");
412     util::EMPTY_RANGE
413 }
414 
set_marked_text( this: &mut Object, _sel: Sel, string: id, _selected_range: NSRange, _replacement_range: NSRange, )415 extern "C" fn set_marked_text(
416     this: &mut Object,
417     _sel: Sel,
418     string: id,
419     _selected_range: NSRange,
420     _replacement_range: NSRange,
421 ) {
422     trace!("Triggered `setMarkedText`");
423     unsafe {
424         let marked_text_ref: &mut id = this.get_mut_ivar("markedText");
425         let _: () = msg_send![(*marked_text_ref), release];
426         let marked_text = NSMutableAttributedString::alloc(nil);
427         let has_attr = msg_send![string, isKindOfClass: class!(NSAttributedString)];
428         if has_attr {
429             marked_text.initWithAttributedString(string);
430         } else {
431             marked_text.initWithString(string);
432         };
433         *marked_text_ref = marked_text;
434     }
435     trace!("Completed `setMarkedText`");
436 }
437 
unmark_text(this: &Object, _sel: Sel)438 extern "C" fn unmark_text(this: &Object, _sel: Sel) {
439     trace!("Triggered `unmarkText`");
440     unsafe {
441         let marked_text: id = *this.get_ivar("markedText");
442         let mutable_string = marked_text.mutableString();
443         let _: () = msg_send![mutable_string, setString:""];
444         let input_context: id = msg_send![this, inputContext];
445         let _: () = msg_send![input_context, discardMarkedText];
446     }
447     trace!("Completed `unmarkText`");
448 }
449 
valid_attributes_for_marked_text(_this: &Object, _sel: Sel) -> id450 extern "C" fn valid_attributes_for_marked_text(_this: &Object, _sel: Sel) -> id {
451     trace!("Triggered `validAttributesForMarkedText`");
452     trace!("Completed `validAttributesForMarkedText`");
453     unsafe { msg_send![class!(NSArray), array] }
454 }
455 
attributed_substring_for_proposed_range( _this: &Object, _sel: Sel, _range: NSRange, _actual_range: *mut c_void, ) -> id456 extern "C" fn attributed_substring_for_proposed_range(
457     _this: &Object,
458     _sel: Sel,
459     _range: NSRange,
460     _actual_range: *mut c_void, // *mut NSRange
461 ) -> id {
462     trace!("Triggered `attributedSubstringForProposedRange`");
463     trace!("Completed `attributedSubstringForProposedRange`");
464     nil
465 }
466 
character_index_for_point(_this: &Object, _sel: Sel, _point: NSPoint) -> NSUInteger467 extern "C" fn character_index_for_point(_this: &Object, _sel: Sel, _point: NSPoint) -> NSUInteger {
468     trace!("Triggered `characterIndexForPoint`");
469     trace!("Completed `characterIndexForPoint`");
470     0
471 }
472 
first_rect_for_character_range( this: &Object, _sel: Sel, _range: NSRange, _actual_range: *mut c_void, ) -> NSRect473 extern "C" fn first_rect_for_character_range(
474     this: &Object,
475     _sel: Sel,
476     _range: NSRange,
477     _actual_range: *mut c_void, // *mut NSRange
478 ) -> NSRect {
479     unsafe {
480         trace!("Triggered `firstRectForCharacterRange`");
481         let state_ptr: *mut c_void = *this.get_ivar("winitState");
482         let state = &mut *(state_ptr as *mut ViewState);
483         let (x, y) = state.ime_spot.unwrap_or_else(|| {
484             let content_rect = NSWindow::contentRectForFrameRect_(
485                 state.ns_window,
486                 NSWindow::frame(state.ns_window),
487             );
488             let x = content_rect.origin.x;
489             let y = util::bottom_left_to_top_left(content_rect);
490             (x, y)
491         });
492         trace!("Completed `firstRectForCharacterRange`");
493         NSRect::new(NSPoint::new(x as _, y as _), NSSize::new(0.0, 0.0))
494     }
495 }
496 
insert_text(this: &Object, _sel: Sel, string: id, _replacement_range: NSRange)497 extern "C" fn insert_text(this: &Object, _sel: Sel, string: id, _replacement_range: NSRange) {
498     trace!("Triggered `insertText`");
499     unsafe {
500         let state_ptr: *mut c_void = *this.get_ivar("winitState");
501         let state = &mut *(state_ptr as *mut ViewState);
502 
503         let has_attr = msg_send![string, isKindOfClass: class!(NSAttributedString)];
504         let characters = if has_attr {
505             // This is a *mut NSAttributedString
506             msg_send![string, string]
507         } else {
508             // This is already a *mut NSString
509             string
510         };
511 
512         let slice =
513             slice::from_raw_parts(characters.UTF8String() as *const c_uchar, characters.len());
514         let string = str::from_utf8_unchecked(slice);
515         state.is_key_down = true;
516 
517         // We don't need this now, but it's here if that changes.
518         //let event: id = msg_send![NSApp(), currentEvent];
519 
520         let mut events = VecDeque::with_capacity(characters.len());
521         for character in string.chars().filter(|c| !is_corporate_character(*c)) {
522             events.push_back(EventWrapper::StaticEvent(Event::WindowEvent {
523                 window_id: WindowId(get_window_id(state.ns_window)),
524                 event: WindowEvent::ReceivedCharacter(character),
525             }));
526         }
527 
528         AppState::queue_events(events);
529     }
530     trace!("Completed `insertText`");
531 }
532 
do_command_by_selector(this: &Object, _sel: Sel, command: Sel)533 extern "C" fn do_command_by_selector(this: &Object, _sel: Sel, command: Sel) {
534     trace!("Triggered `doCommandBySelector`");
535     // Basically, we're sent this message whenever a keyboard event that doesn't generate a "human readable" character
536     // happens, i.e. newlines, tabs, and Ctrl+C.
537     unsafe {
538         let state_ptr: *mut c_void = *this.get_ivar("winitState");
539         let state = &mut *(state_ptr as *mut ViewState);
540 
541         let mut events = VecDeque::with_capacity(1);
542         if command == sel!(insertNewline:) {
543             // The `else` condition would emit the same character, but I'm keeping this here both...
544             // 1) as a reminder for how `doCommandBySelector` works
545             // 2) to make our use of carriage return explicit
546             events.push_back(EventWrapper::StaticEvent(Event::WindowEvent {
547                 window_id: WindowId(get_window_id(state.ns_window)),
548                 event: WindowEvent::ReceivedCharacter('\r'),
549             }));
550         } else {
551             let raw_characters = state.raw_characters.take();
552             if let Some(raw_characters) = raw_characters {
553                 for character in raw_characters
554                     .chars()
555                     .filter(|c| !is_corporate_character(*c))
556                 {
557                     events.push_back(EventWrapper::StaticEvent(Event::WindowEvent {
558                         window_id: WindowId(get_window_id(state.ns_window)),
559                         event: WindowEvent::ReceivedCharacter(character),
560                     }));
561                 }
562             }
563         };
564 
565         AppState::queue_events(events);
566     }
567     trace!("Completed `doCommandBySelector`");
568 }
569 
get_characters(event: id, ignore_modifiers: bool) -> String570 fn get_characters(event: id, ignore_modifiers: bool) -> String {
571     unsafe {
572         let characters: id = if ignore_modifiers {
573             msg_send![event, charactersIgnoringModifiers]
574         } else {
575             msg_send![event, characters]
576         };
577 
578         assert_ne!(characters, nil);
579         let slice =
580             slice::from_raw_parts(characters.UTF8String() as *const c_uchar, characters.len());
581 
582         let string = str::from_utf8_unchecked(slice);
583         string.to_owned()
584     }
585 }
586 
587 // As defined in: https://www.unicode.org/Public/MAPPINGS/VENDORS/APPLE/CORPCHAR.TXT
is_corporate_character(c: char) -> bool588 fn is_corporate_character(c: char) -> bool {
589     match c {
590         '\u{F700}'..='\u{F747}'
591         | '\u{F802}'..='\u{F84F}'
592         | '\u{F850}'
593         | '\u{F85C}'
594         | '\u{F85D}'
595         | '\u{F85F}'
596         | '\u{F860}'..='\u{F86B}'
597         | '\u{F870}'..='\u{F8FF}' => true,
598         _ => false,
599     }
600 }
601 
602 // Retrieves a layout-independent keycode given an event.
retrieve_keycode(event: id) -> Option<VirtualKeyCode>603 fn retrieve_keycode(event: id) -> Option<VirtualKeyCode> {
604     #[inline]
605     fn get_code(ev: id, raw: bool) -> Option<VirtualKeyCode> {
606         let characters = get_characters(ev, raw);
607         characters.chars().next().and_then(|c| char_to_keycode(c))
608     }
609 
610     // Cmd switches Roman letters for Dvorak-QWERTY layout, so we try modified characters first.
611     // If we don't get a match, then we fall back to unmodified characters.
612     let code = get_code(event, false).or_else(|| get_code(event, true));
613 
614     // We've checked all layout related keys, so fall through to scancode.
615     // Reaching this code means that the key is layout-independent (e.g. Backspace, Return).
616     //
617     // We're additionally checking here for F21-F24 keys, since their keycode
618     // can vary, but we know that they are encoded
619     // in characters property.
620     code.or_else(|| {
621         let scancode = get_scancode(event);
622         scancode_to_keycode(scancode).or_else(|| check_function_keys(&get_characters(event, true)))
623     })
624 }
625 
626 // Update `state.modifiers` if `event` has something different
update_potentially_stale_modifiers(state: &mut ViewState, event: id)627 fn update_potentially_stale_modifiers(state: &mut ViewState, event: id) {
628     let event_modifiers = event_mods(event);
629     if state.modifiers != event_modifiers {
630         state.modifiers = event_modifiers;
631 
632         AppState::queue_event(EventWrapper::StaticEvent(Event::WindowEvent {
633             window_id: WindowId(get_window_id(state.ns_window)),
634             event: WindowEvent::ModifiersChanged(state.modifiers),
635         }));
636     }
637 }
638 
key_down(this: &Object, _sel: Sel, event: id)639 extern "C" fn key_down(this: &Object, _sel: Sel, event: id) {
640     trace!("Triggered `keyDown`");
641     unsafe {
642         let state_ptr: *mut c_void = *this.get_ivar("winitState");
643         let state = &mut *(state_ptr as *mut ViewState);
644         let window_id = WindowId(get_window_id(state.ns_window));
645         let characters = get_characters(event, false);
646 
647         state.raw_characters = Some(characters.clone());
648 
649         let scancode = get_scancode(event) as u32;
650         let virtual_keycode = retrieve_keycode(event);
651 
652         let is_repeat = msg_send![event, isARepeat];
653 
654         update_potentially_stale_modifiers(state, event);
655 
656         #[allow(deprecated)]
657         let window_event = Event::WindowEvent {
658             window_id,
659             event: WindowEvent::KeyboardInput {
660                 device_id: DEVICE_ID,
661                 input: KeyboardInput {
662                     state: ElementState::Pressed,
663                     scancode,
664                     virtual_keycode,
665                     modifiers: event_mods(event),
666                 },
667                 is_synthetic: false,
668             },
669         };
670 
671         let pass_along = {
672             AppState::queue_event(EventWrapper::StaticEvent(window_event));
673             // Emit `ReceivedCharacter` for key repeats
674             if is_repeat && state.is_key_down {
675                 for character in characters.chars().filter(|c| !is_corporate_character(*c)) {
676                     AppState::queue_event(EventWrapper::StaticEvent(Event::WindowEvent {
677                         window_id,
678                         event: WindowEvent::ReceivedCharacter(character),
679                     }));
680                 }
681                 false
682             } else {
683                 true
684             }
685         };
686 
687         if pass_along {
688             // Some keys (and only *some*, with no known reason) don't trigger `insertText`, while others do...
689             // So, we don't give repeats the opportunity to trigger that, since otherwise our hack will cause some
690             // keys to generate twice as many characters.
691             let array: id = msg_send![class!(NSArray), arrayWithObject: event];
692             let _: () = msg_send![this, interpretKeyEvents: array];
693         }
694     }
695     trace!("Completed `keyDown`");
696 }
697 
key_up(this: &Object, _sel: Sel, event: id)698 extern "C" fn key_up(this: &Object, _sel: Sel, event: id) {
699     trace!("Triggered `keyUp`");
700     unsafe {
701         let state_ptr: *mut c_void = *this.get_ivar("winitState");
702         let state = &mut *(state_ptr as *mut ViewState);
703 
704         state.is_key_down = false;
705 
706         let scancode = get_scancode(event) as u32;
707         let virtual_keycode = retrieve_keycode(event);
708 
709         update_potentially_stale_modifiers(state, event);
710 
711         #[allow(deprecated)]
712         let window_event = Event::WindowEvent {
713             window_id: WindowId(get_window_id(state.ns_window)),
714             event: WindowEvent::KeyboardInput {
715                 device_id: DEVICE_ID,
716                 input: KeyboardInput {
717                     state: ElementState::Released,
718                     scancode,
719                     virtual_keycode,
720                     modifiers: event_mods(event),
721                 },
722                 is_synthetic: false,
723             },
724         };
725 
726         AppState::queue_event(EventWrapper::StaticEvent(window_event));
727     }
728     trace!("Completed `keyUp`");
729 }
730 
flags_changed(this: &Object, _sel: Sel, event: id)731 extern "C" fn flags_changed(this: &Object, _sel: Sel, event: id) {
732     trace!("Triggered `flagsChanged`");
733     unsafe {
734         let state_ptr: *mut c_void = *this.get_ivar("winitState");
735         let state = &mut *(state_ptr as *mut ViewState);
736 
737         let mut events = VecDeque::with_capacity(4);
738 
739         if let Some(window_event) = modifier_event(
740             event,
741             NSEventModifierFlags::NSShiftKeyMask,
742             state.modifiers.shift(),
743         ) {
744             state.modifiers.toggle(ModifiersState::SHIFT);
745             events.push_back(window_event);
746         }
747 
748         if let Some(window_event) = modifier_event(
749             event,
750             NSEventModifierFlags::NSControlKeyMask,
751             state.modifiers.ctrl(),
752         ) {
753             state.modifiers.toggle(ModifiersState::CTRL);
754             events.push_back(window_event);
755         }
756 
757         if let Some(window_event) = modifier_event(
758             event,
759             NSEventModifierFlags::NSCommandKeyMask,
760             state.modifiers.logo(),
761         ) {
762             state.modifiers.toggle(ModifiersState::LOGO);
763             events.push_back(window_event);
764         }
765 
766         if let Some(window_event) = modifier_event(
767             event,
768             NSEventModifierFlags::NSAlternateKeyMask,
769             state.modifiers.alt(),
770         ) {
771             state.modifiers.toggle(ModifiersState::ALT);
772             events.push_back(window_event);
773         }
774 
775         let window_id = WindowId(get_window_id(state.ns_window));
776 
777         for event in events {
778             AppState::queue_event(EventWrapper::StaticEvent(Event::WindowEvent {
779                 window_id,
780                 event,
781             }));
782         }
783 
784         AppState::queue_event(EventWrapper::StaticEvent(Event::WindowEvent {
785             window_id,
786             event: WindowEvent::ModifiersChanged(state.modifiers),
787         }));
788     }
789     trace!("Completed `flagsChanged`");
790 }
791 
insert_tab(this: &Object, _sel: Sel, _sender: id)792 extern "C" fn insert_tab(this: &Object, _sel: Sel, _sender: id) {
793     unsafe {
794         let window: id = msg_send![this, window];
795         let first_responder: id = msg_send![window, firstResponder];
796         let this_ptr = this as *const _ as *mut _;
797         if first_responder == this_ptr {
798             let (): _ = msg_send![window, selectNextKeyView: this];
799         }
800     }
801 }
802 
insert_back_tab(this: &Object, _sel: Sel, _sender: id)803 extern "C" fn insert_back_tab(this: &Object, _sel: Sel, _sender: id) {
804     unsafe {
805         let window: id = msg_send![this, window];
806         let first_responder: id = msg_send![window, firstResponder];
807         let this_ptr = this as *const _ as *mut _;
808         if first_responder == this_ptr {
809             let (): _ = msg_send![window, selectPreviousKeyView: this];
810         }
811     }
812 }
813 
814 // Allows us to receive Cmd-. (the shortcut for closing a dialog)
815 // https://bugs.eclipse.org/bugs/show_bug.cgi?id=300620#c6
cancel_operation(this: &Object, _sel: Sel, _sender: id)816 extern "C" fn cancel_operation(this: &Object, _sel: Sel, _sender: id) {
817     trace!("Triggered `cancelOperation`");
818     unsafe {
819         let state_ptr: *mut c_void = *this.get_ivar("winitState");
820         let state = &mut *(state_ptr as *mut ViewState);
821 
822         let scancode = 0x2f;
823         let virtual_keycode = scancode_to_keycode(scancode);
824         debug_assert_eq!(virtual_keycode, Some(VirtualKeyCode::Period));
825 
826         let event: id = msg_send![NSApp(), currentEvent];
827 
828         update_potentially_stale_modifiers(state, event);
829 
830         #[allow(deprecated)]
831         let window_event = Event::WindowEvent {
832             window_id: WindowId(get_window_id(state.ns_window)),
833             event: WindowEvent::KeyboardInput {
834                 device_id: DEVICE_ID,
835                 input: KeyboardInput {
836                     state: ElementState::Pressed,
837                     scancode: scancode as _,
838                     virtual_keycode,
839                     modifiers: event_mods(event),
840                 },
841                 is_synthetic: false,
842             },
843         };
844 
845         AppState::queue_event(EventWrapper::StaticEvent(window_event));
846     }
847     trace!("Completed `cancelOperation`");
848 }
849 
mouse_click(this: &Object, event: id, button: MouseButton, button_state: ElementState)850 fn mouse_click(this: &Object, event: id, button: MouseButton, button_state: ElementState) {
851     unsafe {
852         let state_ptr: *mut c_void = *this.get_ivar("winitState");
853         let state = &mut *(state_ptr as *mut ViewState);
854 
855         update_potentially_stale_modifiers(state, event);
856 
857         let window_event = Event::WindowEvent {
858             window_id: WindowId(get_window_id(state.ns_window)),
859             event: WindowEvent::MouseInput {
860                 device_id: DEVICE_ID,
861                 state: button_state,
862                 button,
863                 modifiers: event_mods(event),
864             },
865         };
866 
867         AppState::queue_event(EventWrapper::StaticEvent(window_event));
868     }
869 }
870 
mouse_down(this: &Object, _sel: Sel, event: id)871 extern "C" fn mouse_down(this: &Object, _sel: Sel, event: id) {
872     mouse_motion(this, event);
873     mouse_click(this, event, MouseButton::Left, ElementState::Pressed);
874 }
875 
mouse_up(this: &Object, _sel: Sel, event: id)876 extern "C" fn mouse_up(this: &Object, _sel: Sel, event: id) {
877     mouse_motion(this, event);
878     mouse_click(this, event, MouseButton::Left, ElementState::Released);
879 }
880 
right_mouse_down(this: &Object, _sel: Sel, event: id)881 extern "C" fn right_mouse_down(this: &Object, _sel: Sel, event: id) {
882     mouse_motion(this, event);
883     mouse_click(this, event, MouseButton::Right, ElementState::Pressed);
884 }
885 
right_mouse_up(this: &Object, _sel: Sel, event: id)886 extern "C" fn right_mouse_up(this: &Object, _sel: Sel, event: id) {
887     mouse_motion(this, event);
888     mouse_click(this, event, MouseButton::Right, ElementState::Released);
889 }
890 
other_mouse_down(this: &Object, _sel: Sel, event: id)891 extern "C" fn other_mouse_down(this: &Object, _sel: Sel, event: id) {
892     mouse_motion(this, event);
893     mouse_click(this, event, MouseButton::Middle, ElementState::Pressed);
894 }
895 
other_mouse_up(this: &Object, _sel: Sel, event: id)896 extern "C" fn other_mouse_up(this: &Object, _sel: Sel, event: id) {
897     mouse_motion(this, event);
898     mouse_click(this, event, MouseButton::Middle, ElementState::Released);
899 }
900 
mouse_motion(this: &Object, event: id)901 fn mouse_motion(this: &Object, event: id) {
902     unsafe {
903         let state_ptr: *mut c_void = *this.get_ivar("winitState");
904         let state = &mut *(state_ptr as *mut ViewState);
905 
906         // We have to do this to have access to the `NSView` trait...
907         let view: id = this as *const _ as *mut _;
908 
909         let window_point = event.locationInWindow();
910         let view_point = view.convertPoint_fromView_(window_point, nil);
911         let view_rect = NSView::frame(view);
912 
913         if view_point.x.is_sign_negative()
914             || view_point.y.is_sign_negative()
915             || view_point.x > view_rect.size.width
916             || view_point.y > view_rect.size.height
917         {
918             let mouse_buttons_down: NSInteger = msg_send![class!(NSEvent), pressedMouseButtons];
919             if mouse_buttons_down == 0 {
920                 // Point is outside of the client area (view) and no buttons are pressed
921                 return;
922             }
923         }
924 
925         let x = view_point.x as f64;
926         let y = view_rect.size.height as f64 - view_point.y as f64;
927         let logical_position = LogicalPosition::new(x, y);
928 
929         update_potentially_stale_modifiers(state, event);
930 
931         let window_event = Event::WindowEvent {
932             window_id: WindowId(get_window_id(state.ns_window)),
933             event: WindowEvent::CursorMoved {
934                 device_id: DEVICE_ID,
935                 position: logical_position.to_physical(state.get_scale_factor()),
936                 modifiers: event_mods(event),
937             },
938         };
939 
940         AppState::queue_event(EventWrapper::StaticEvent(window_event));
941     }
942 }
943 
mouse_moved(this: &Object, _sel: Sel, event: id)944 extern "C" fn mouse_moved(this: &Object, _sel: Sel, event: id) {
945     mouse_motion(this, event);
946 }
947 
mouse_dragged(this: &Object, _sel: Sel, event: id)948 extern "C" fn mouse_dragged(this: &Object, _sel: Sel, event: id) {
949     mouse_motion(this, event);
950 }
951 
right_mouse_dragged(this: &Object, _sel: Sel, event: id)952 extern "C" fn right_mouse_dragged(this: &Object, _sel: Sel, event: id) {
953     mouse_motion(this, event);
954 }
955 
other_mouse_dragged(this: &Object, _sel: Sel, event: id)956 extern "C" fn other_mouse_dragged(this: &Object, _sel: Sel, event: id) {
957     mouse_motion(this, event);
958 }
959 
mouse_entered(this: &Object, _sel: Sel, _event: id)960 extern "C" fn mouse_entered(this: &Object, _sel: Sel, _event: id) {
961     trace!("Triggered `mouseEntered`");
962     unsafe {
963         let state_ptr: *mut c_void = *this.get_ivar("winitState");
964         let state = &mut *(state_ptr as *mut ViewState);
965 
966         let enter_event = Event::WindowEvent {
967             window_id: WindowId(get_window_id(state.ns_window)),
968             event: WindowEvent::CursorEntered {
969                 device_id: DEVICE_ID,
970             },
971         };
972 
973         AppState::queue_event(EventWrapper::StaticEvent(enter_event));
974     }
975     trace!("Completed `mouseEntered`");
976 }
977 
mouse_exited(this: &Object, _sel: Sel, _event: id)978 extern "C" fn mouse_exited(this: &Object, _sel: Sel, _event: id) {
979     trace!("Triggered `mouseExited`");
980     unsafe {
981         let state_ptr: *mut c_void = *this.get_ivar("winitState");
982         let state = &mut *(state_ptr as *mut ViewState);
983 
984         let window_event = Event::WindowEvent {
985             window_id: WindowId(get_window_id(state.ns_window)),
986             event: WindowEvent::CursorLeft {
987                 device_id: DEVICE_ID,
988             },
989         };
990 
991         AppState::queue_event(EventWrapper::StaticEvent(window_event));
992     }
993     trace!("Completed `mouseExited`");
994 }
995 
scroll_wheel(this: &Object, _sel: Sel, event: id)996 extern "C" fn scroll_wheel(this: &Object, _sel: Sel, event: id) {
997     trace!("Triggered `scrollWheel`");
998 
999     mouse_motion(this, event);
1000 
1001     unsafe {
1002         let state_ptr: *mut c_void = *this.get_ivar("winitState");
1003         let state = &mut *(state_ptr as *mut ViewState);
1004 
1005         let delta = {
1006             let (x, y) = (event.scrollingDeltaX(), event.scrollingDeltaY());
1007             if event.hasPreciseScrollingDeltas() == YES {
1008                 let delta = LogicalPosition::new(x, y).to_physical(state.get_scale_factor());
1009                 MouseScrollDelta::PixelDelta(delta)
1010             } else {
1011                 MouseScrollDelta::LineDelta(x as f32, y as f32)
1012             }
1013         };
1014         let phase = match event.phase() {
1015             NSEventPhase::NSEventPhaseMayBegin | NSEventPhase::NSEventPhaseBegan => {
1016                 TouchPhase::Started
1017             }
1018             NSEventPhase::NSEventPhaseEnded => TouchPhase::Ended,
1019             _ => TouchPhase::Moved,
1020         };
1021 
1022         let device_event = Event::DeviceEvent {
1023             device_id: DEVICE_ID,
1024             event: DeviceEvent::MouseWheel { delta },
1025         };
1026 
1027         let state_ptr: *mut c_void = *this.get_ivar("winitState");
1028         let state = &mut *(state_ptr as *mut ViewState);
1029 
1030         update_potentially_stale_modifiers(state, event);
1031 
1032         let window_event = Event::WindowEvent {
1033             window_id: WindowId(get_window_id(state.ns_window)),
1034             event: WindowEvent::MouseWheel {
1035                 device_id: DEVICE_ID,
1036                 delta,
1037                 phase,
1038                 modifiers: event_mods(event),
1039             },
1040         };
1041 
1042         AppState::queue_event(EventWrapper::StaticEvent(device_event));
1043         AppState::queue_event(EventWrapper::StaticEvent(window_event));
1044     }
1045     trace!("Completed `scrollWheel`");
1046 }
1047 
pressure_change_with_event(this: &Object, _sel: Sel, event: id)1048 extern "C" fn pressure_change_with_event(this: &Object, _sel: Sel, event: id) {
1049     trace!("Triggered `pressureChangeWithEvent`");
1050 
1051     mouse_motion(this, event);
1052 
1053     unsafe {
1054         let state_ptr: *mut c_void = *this.get_ivar("winitState");
1055         let state = &mut *(state_ptr as *mut ViewState);
1056 
1057         let pressure = event.pressure();
1058         let stage = event.stage();
1059 
1060         let window_event = Event::WindowEvent {
1061             window_id: WindowId(get_window_id(state.ns_window)),
1062             event: WindowEvent::TouchpadPressure {
1063                 device_id: DEVICE_ID,
1064                 pressure,
1065                 stage,
1066             },
1067         };
1068 
1069         AppState::queue_event(EventWrapper::StaticEvent(window_event));
1070     }
1071     trace!("Completed `pressureChangeWithEvent`");
1072 }
1073 
1074 // Allows us to receive Ctrl-Tab and Ctrl-Esc.
1075 // Note that this *doesn't* help with any missing Cmd inputs.
1076 // https://github.com/chromium/chromium/blob/a86a8a6bcfa438fa3ac2eba6f02b3ad1f8e0756f/ui/views/cocoa/bridged_content_view.mm#L816
wants_key_down_for_event(_this: &Object, _sel: Sel, _event: id) -> BOOL1077 extern "C" fn wants_key_down_for_event(_this: &Object, _sel: Sel, _event: id) -> BOOL {
1078     YES
1079 }
1080