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