1 use {ControlFlow, EventsLoopClosed};
2 use cocoa::{self, appkit, foundation};
3 use cocoa::appkit::{NSApplication, NSEvent, NSEventMask, NSEventModifierFlags, NSEventPhase, NSView, NSWindow};
4 use events::{self, ElementState, Event, TouchPhase, WindowEvent, DeviceEvent, ModifiersState, KeyboardInput};
5 use std::collections::VecDeque;
6 use std::sync::{Arc, Mutex, Weak};
7 use super::window::Window2;
8 use std;
9 use std::os::raw::*;
10 use super::DeviceId;
11
12 pub struct EventsLoop {
13 modifiers: Modifiers,
14 pub shared: Arc<Shared>,
15 }
16
17 // State shared between the `EventsLoop` and its registered windows.
18 pub struct Shared {
19 pub windows: Mutex<Vec<Weak<Window2>>>,
20 pub pending_events: Mutex<VecDeque<Event>>,
21 // The user event callback given via either of the `poll_events` or `run_forever` methods.
22 //
23 // We store the user's callback here so that it may be accessed by each of the window delegate
24 // callbacks (e.g. resize, close, etc) for the duration of a call to either of the
25 // `poll_events` or `run_forever` methods.
26 //
27 // This is *only* `Some` for the duration of a call to either of these methods and will be
28 // `None` otherwise.
29 user_callback: UserCallback,
30 }
31
32 #[derive(Clone)]
33 pub struct Proxy {}
34
35 struct Modifiers {
36 shift_pressed: bool,
37 ctrl_pressed: bool,
38 win_pressed: bool,
39 alt_pressed: bool,
40 }
41
42 // Wrapping the user callback in a type allows us to:
43 //
44 // - ensure the callback pointer is never accidentally cloned
45 // - ensure that only the `EventsLoop` can `store` and `drop` the callback pointer
46 // - Share access to the user callback with the NSWindow callbacks.
47 pub struct UserCallback {
48 mutex: Mutex<Option<*mut FnMut(Event)>>,
49 }
50
51
52 impl Shared {
53
new() -> Self54 pub fn new() -> Self {
55 Shared {
56 windows: Mutex::new(Vec::new()),
57 pending_events: Mutex::new(VecDeque::new()),
58 user_callback: UserCallback { mutex: Mutex::new(None) },
59 }
60 }
61
call_user_callback_with_pending_events(&self)62 fn call_user_callback_with_pending_events(&self) {
63 loop {
64 let event = match self.pending_events.lock().unwrap().pop_front() {
65 Some(event) => event,
66 None => return,
67 };
68 unsafe {
69 self.user_callback.call_with_event(event);
70 }
71 }
72 }
73
74 // Calls the user callback if one exists.
75 //
76 // Otherwise, stores the event in the `pending_events` queue.
77 //
78 // This is necessary for the case when `WindowDelegate` callbacks are triggered during a call
79 // to the user's callback.
call_user_callback_with_event_or_store_in_pending(&self, event: Event)80 pub fn call_user_callback_with_event_or_store_in_pending(&self, event: Event) {
81 if self.user_callback.mutex.lock().unwrap().is_some() {
82 unsafe {
83 self.user_callback.call_with_event(event);
84 }
85 } else {
86 self.pending_events.lock().unwrap().push_back(event);
87 }
88 }
89
90 // Removes the window with the given `Id` from the `windows` list.
91 //
92 // This is called in response to `windowWillClose`.
find_and_remove_window(&self, id: super::window::Id)93 pub fn find_and_remove_window(&self, id: super::window::Id) {
94 if let Ok(mut windows) = self.windows.lock() {
95 windows.retain(|w| match w.upgrade() {
96 Some(w) => w.id() != id,
97 None => false,
98 });
99 }
100 }
101
102 }
103
104
105 impl Modifiers {
new() -> Self106 pub fn new() -> Self {
107 Modifiers {
108 shift_pressed: false,
109 ctrl_pressed: false,
110 win_pressed: false,
111 alt_pressed: false,
112 }
113 }
114 }
115
116
117 impl UserCallback {
118
119 // Here we store user's `callback` behind the mutex so that they may be safely shared between
120 // each of the window delegates.
121 //
122 // In order to make sure that the pointer is always valid, we must manually guarantee that it
123 // is dropped before the callback itself is dropped. Thus, this should *only* be called at the
124 // beginning of a call to `poll_events` and `run_forever`, both of which *must* drop the
125 // callback at the end of their scope using the `drop` method.
store<F>(&self, callback: &mut F) where F: FnMut(Event)126 fn store<F>(&self, callback: &mut F)
127 where F: FnMut(Event)
128 {
129 let trait_object = callback as &mut FnMut(Event);
130 let trait_object_ptr = trait_object as *const FnMut(Event) as *mut FnMut(Event);
131 *self.mutex.lock().unwrap() = Some(trait_object_ptr);
132 }
133
134 // Emits the given event via the user-given callback.
135 //
136 // This is unsafe as it requires dereferencing the pointer to the user-given callback. We
137 // guarantee this is safe by ensuring the `UserCallback` never lives longer than the user-given
138 // callback.
139 //
140 // Note that the callback may not always be `Some`. This is because some `NSWindowDelegate`
141 // callbacks can be triggered by means other than `NSApp().sendEvent`. For example, if a window
142 // is destroyed or created during a call to the user's callback, the `WindowDelegate` methods
143 // may be called with `windowShouldClose` or `windowDidResignKey`.
call_with_event(&self, event: Event)144 unsafe fn call_with_event(&self, event: Event) {
145 let callback = match self.mutex.lock().unwrap().take() {
146 Some(callback) => callback,
147 None => return,
148 };
149 (*callback)(event);
150 *self.mutex.lock().unwrap() = Some(callback);
151 }
152
153 // Used to drop the user callback pointer at the end of the `poll_events` and `run_forever`
154 // methods. This is done to enforce our guarantee that the top callback will never live longer
155 // than the call to either `poll_events` or `run_forever` to which it was given.
drop(&self)156 fn drop(&self) {
157 self.mutex.lock().unwrap().take();
158 }
159
160 }
161
162
163 impl EventsLoop {
164
new() -> Self165 pub fn new() -> Self {
166 // Mark this thread as the main thread of the Cocoa event system.
167 //
168 // This must be done before any worker threads get a chance to call it
169 // (e.g., via `EventsLoopProxy::wakeup()`), causing a wrong thread to be
170 // marked as the main thread.
171 unsafe { appkit::NSApp(); }
172
173 EventsLoop {
174 shared: Arc::new(Shared::new()),
175 modifiers: Modifiers::new(),
176 }
177 }
178
poll_events<F>(&mut self, mut callback: F) where F: FnMut(Event),179 pub fn poll_events<F>(&mut self, mut callback: F)
180 where F: FnMut(Event),
181 {
182 unsafe {
183 if !msg_send![class!(NSThread), isMainThread] {
184 panic!("Events can only be polled from the main thread on macOS");
185 }
186 }
187
188 self.shared.user_callback.store(&mut callback);
189
190 // Loop as long as we have pending events to return.
191 loop {
192 unsafe {
193 // First, yield all pending events.
194 self.shared.call_user_callback_with_pending_events();
195
196 let pool = foundation::NSAutoreleasePool::new(cocoa::base::nil);
197
198 // Poll for the next event, returning `nil` if there are none.
199 let ns_event = appkit::NSApp().nextEventMatchingMask_untilDate_inMode_dequeue_(
200 NSEventMask::NSAnyEventMask.bits() | NSEventMask::NSEventMaskPressure.bits(),
201 foundation::NSDate::distantPast(cocoa::base::nil),
202 foundation::NSDefaultRunLoopMode,
203 cocoa::base::YES);
204
205 let event = self.ns_event_to_event(ns_event);
206
207 let _: () = msg_send![pool, release];
208
209 match event {
210 // Call the user's callback.
211 Some(event) => self.shared.user_callback.call_with_event(event),
212 None => break,
213 }
214 }
215 }
216
217 self.shared.user_callback.drop();
218 }
219
run_forever<F>(&mut self, mut callback: F) where F: FnMut(Event) -> ControlFlow220 pub fn run_forever<F>(&mut self, mut callback: F)
221 where F: FnMut(Event) -> ControlFlow
222 {
223 unsafe {
224 if !msg_send![class!(NSThread), isMainThread] {
225 panic!("Events can only be polled from the main thread on macOS");
226 }
227 }
228
229 // Track whether or not control flow has changed.
230 let control_flow = std::cell::Cell::new(ControlFlow::Continue);
231
232 let mut callback = |event| {
233 if let ControlFlow::Break = callback(event) {
234 control_flow.set(ControlFlow::Break);
235 }
236 };
237
238 self.shared.user_callback.store(&mut callback);
239
240 loop {
241 unsafe {
242 // First, yield all pending events.
243 self.shared.call_user_callback_with_pending_events();
244 if let ControlFlow::Break = control_flow.get() {
245 break;
246 }
247
248 let pool = foundation::NSAutoreleasePool::new(cocoa::base::nil);
249
250 // Wait for the next event. Note that this function blocks during resize.
251 let ns_event = appkit::NSApp().nextEventMatchingMask_untilDate_inMode_dequeue_(
252 NSEventMask::NSAnyEventMask.bits() | NSEventMask::NSEventMaskPressure.bits(),
253 foundation::NSDate::distantFuture(cocoa::base::nil),
254 foundation::NSDefaultRunLoopMode,
255 cocoa::base::YES);
256
257 let maybe_event = self.ns_event_to_event(ns_event);
258
259 // Release the pool before calling the top callback in case the user calls either
260 // `run_forever` or `poll_events` within the callback.
261 let _: () = msg_send![pool, release];
262
263 if let Some(event) = maybe_event {
264 self.shared.user_callback.call_with_event(event);
265 if let ControlFlow::Break = control_flow.get() {
266 break;
267 }
268 }
269 }
270 }
271
272 self.shared.user_callback.drop();
273 }
274
275 // Convert some given `NSEvent` into a winit `Event`.
ns_event_to_event(&mut self, ns_event: cocoa::base::id) -> Option<Event>276 unsafe fn ns_event_to_event(&mut self, ns_event: cocoa::base::id) -> Option<Event> {
277 if ns_event == cocoa::base::nil {
278 return None;
279 }
280
281 // FIXME: Despite not being documented anywhere, an `NSEvent` is produced when a user opens
282 // Spotlight while the NSApplication is in focus. This `NSEvent` produces a `NSEventType`
283 // with value `21`. This causes a SEGFAULT as soon as we try to match on the `NSEventType`
284 // enum as there is no variant associated with the value. Thus, we return early if this
285 // sneaky event occurs. If someone does find some documentation on this, please fix this by
286 // adding an appropriate variant to the `NSEventType` enum in the cocoa-rs crate.
287 if ns_event.eventType() as u64 == 21 {
288 return None;
289 }
290
291 let event_type = ns_event.eventType();
292 let ns_window = ns_event.window();
293 let window_id = super::window::get_window_id(ns_window);
294
295 // FIXME: Document this. Why do we do this? Seems like it passes on events to window/app.
296 // If we don't do this, window does not become main for some reason.
297 appkit::NSApp().sendEvent_(ns_event);
298
299 let windows = self.shared.windows.lock().unwrap();
300 let maybe_window = windows.iter()
301 .filter_map(Weak::upgrade)
302 .find(|window| window_id == window.id());
303
304 let into_event = |window_event| Event::WindowEvent {
305 window_id: ::WindowId(window_id),
306 event: window_event,
307 };
308
309 // Returns `Some` window if one of our windows is the key window.
310 let maybe_key_window = || windows.iter()
311 .filter_map(Weak::upgrade)
312 .find(|window| {
313 let is_key_window: cocoa::base::BOOL = msg_send![*window.window, isKeyWindow];
314 is_key_window == cocoa::base::YES
315 });
316
317 match event_type {
318 // https://github.com/glfw/glfw/blob/50eccd298a2bbc272b4977bd162d3e4b55f15394/src/cocoa_window.m#L881
319 appkit::NSKeyUp => {
320 if let Some(key_window) = maybe_key_window() {
321 if event_mods(ns_event).logo {
322 let _: () = msg_send![*key_window.window, sendEvent:ns_event];
323 }
324 }
325 None
326 },
327 // similar to above, but for `<Cmd-.>`, the keyDown is suppressed instead of the
328 // KeyUp, and the above trick does not appear to work.
329 appkit::NSKeyDown => {
330 let modifiers = event_mods(ns_event);
331 let keycode = NSEvent::keyCode(ns_event);
332 if modifiers.logo && keycode == 47 {
333 modifier_event(ns_event, NSEventModifierFlags::NSCommandKeyMask, false)
334 .map(into_event)
335 } else {
336 None
337 }
338 },
339 appkit::NSFlagsChanged => {
340 let mut events = std::collections::VecDeque::new();
341
342 if let Some(window_event) = modifier_event(
343 ns_event,
344 NSEventModifierFlags::NSShiftKeyMask,
345 self.modifiers.shift_pressed,
346 ) {
347 self.modifiers.shift_pressed = !self.modifiers.shift_pressed;
348 events.push_back(into_event(window_event));
349 }
350
351 if let Some(window_event) = modifier_event(
352 ns_event,
353 NSEventModifierFlags::NSControlKeyMask,
354 self.modifiers.ctrl_pressed,
355 ) {
356 self.modifiers.ctrl_pressed = !self.modifiers.ctrl_pressed;
357 events.push_back(into_event(window_event));
358 }
359
360 if let Some(window_event) = modifier_event(
361 ns_event,
362 NSEventModifierFlags::NSCommandKeyMask,
363 self.modifiers.win_pressed,
364 ) {
365 self.modifiers.win_pressed = !self.modifiers.win_pressed;
366 events.push_back(into_event(window_event));
367 }
368
369 if let Some(window_event) = modifier_event(
370 ns_event,
371 NSEventModifierFlags::NSAlternateKeyMask,
372 self.modifiers.alt_pressed,
373 ) {
374 self.modifiers.alt_pressed = !self.modifiers.alt_pressed;
375 events.push_back(into_event(window_event));
376 }
377
378 let event = events.pop_front();
379 self.shared.pending_events
380 .lock()
381 .unwrap()
382 .extend(events.into_iter());
383 event
384 },
385
386 appkit::NSMouseEntered => {
387 let window = match maybe_window.or_else(maybe_key_window) {
388 Some(window) => window,
389 None => return None,
390 };
391
392 let window_point = ns_event.locationInWindow();
393 let view_point = if ns_window == cocoa::base::nil {
394 let ns_size = foundation::NSSize::new(0.0, 0.0);
395 let ns_rect = foundation::NSRect::new(window_point, ns_size);
396 let window_rect = window.window.convertRectFromScreen_(ns_rect);
397 window.view.convertPoint_fromView_(window_rect.origin, cocoa::base::nil)
398 } else {
399 window.view.convertPoint_fromView_(window_point, cocoa::base::nil)
400 };
401
402 let view_rect = NSView::frame(*window.view);
403 let x = view_point.x as f64;
404 let y = (view_rect.size.height - view_point.y) as f64;
405 let window_event = WindowEvent::CursorMoved {
406 device_id: DEVICE_ID,
407 position: (x, y).into(),
408 modifiers: event_mods(ns_event),
409 };
410 let event = Event::WindowEvent { window_id: ::WindowId(window.id()), event: window_event };
411 self.shared.pending_events.lock().unwrap().push_back(event);
412 Some(into_event(WindowEvent::CursorEntered { device_id: DEVICE_ID }))
413 },
414 appkit::NSMouseExited => { Some(into_event(WindowEvent::CursorLeft { device_id: DEVICE_ID })) },
415
416 appkit::NSMouseMoved |
417 appkit::NSLeftMouseDragged |
418 appkit::NSOtherMouseDragged |
419 appkit::NSRightMouseDragged => {
420 // If the mouse movement was on one of our windows, use it.
421 // Otherwise, if one of our windows is the key window (receiving input), use it.
422 // Otherwise, return `None`.
423 match maybe_window.or_else(maybe_key_window) {
424 Some(_window) => (),
425 None => return None,
426 }
427
428 let mut events = std::collections::VecDeque::with_capacity(3);
429
430 let delta_x = ns_event.deltaX() as f64;
431 if delta_x != 0.0 {
432 let motion_event = DeviceEvent::Motion { axis: 0, value: delta_x };
433 let event = Event::DeviceEvent { device_id: DEVICE_ID, event: motion_event };
434 events.push_back(event);
435 }
436
437 let delta_y = ns_event.deltaY() as f64;
438 if delta_y != 0.0 {
439 let motion_event = DeviceEvent::Motion { axis: 1, value: delta_y };
440 let event = Event::DeviceEvent { device_id: DEVICE_ID, event: motion_event };
441 events.push_back(event);
442 }
443
444 if delta_x != 0.0 || delta_y != 0.0 {
445 let motion_event = DeviceEvent::MouseMotion { delta: (delta_x, delta_y) };
446 let event = Event::DeviceEvent { device_id: DEVICE_ID, event: motion_event };
447 events.push_back(event);
448 }
449
450 let event = events.pop_front();
451 self.shared.pending_events.lock().unwrap().extend(events.into_iter());
452 event
453 },
454
455 appkit::NSScrollWheel => {
456 // If none of the windows received the scroll, return `None`.
457 if maybe_window.is_none() {
458 return None;
459 }
460
461 use events::MouseScrollDelta::{LineDelta, PixelDelta};
462 let delta = if ns_event.hasPreciseScrollingDeltas() == cocoa::base::YES {
463 PixelDelta((
464 ns_event.scrollingDeltaX() as f64,
465 ns_event.scrollingDeltaY() as f64,
466 ).into())
467 } else {
468 // TODO: This is probably wrong
469 LineDelta(
470 ns_event.scrollingDeltaX() as f32,
471 ns_event.scrollingDeltaY() as f32,
472 )
473 };
474 let phase = match ns_event.phase() {
475 NSEventPhase::NSEventPhaseMayBegin | NSEventPhase::NSEventPhaseBegan => TouchPhase::Started,
476 NSEventPhase::NSEventPhaseEnded => TouchPhase::Ended,
477 _ => TouchPhase::Moved,
478 };
479 self.shared.pending_events.lock().unwrap().push_back(Event::DeviceEvent {
480 device_id: DEVICE_ID,
481 event: DeviceEvent::MouseWheel {
482 delta: if ns_event.hasPreciseScrollingDeltas() == cocoa::base::YES {
483 PixelDelta((
484 ns_event.scrollingDeltaX() as f64,
485 ns_event.scrollingDeltaY() as f64,
486 ).into())
487 } else {
488 LineDelta(
489 ns_event.scrollingDeltaX() as f32,
490 ns_event.scrollingDeltaY() as f32,
491 )
492 },
493 }
494 });
495 let window_event = WindowEvent::MouseWheel { device_id: DEVICE_ID, delta: delta, phase: phase, modifiers: event_mods(ns_event) };
496 Some(into_event(window_event))
497 },
498
499 appkit::NSEventTypePressure => {
500 let pressure = ns_event.pressure();
501 let stage = ns_event.stage();
502 let window_event = WindowEvent::TouchpadPressure { device_id: DEVICE_ID, pressure: pressure, stage: stage };
503 Some(into_event(window_event))
504 },
505
506 appkit::NSApplicationDefined => match ns_event.subtype() {
507 appkit::NSEventSubtype::NSApplicationActivatedEventType => {
508 Some(Event::Awakened)
509 },
510 _ => None,
511 },
512
513 _ => None,
514 }
515 }
516
create_proxy(&self) -> Proxy517 pub fn create_proxy(&self) -> Proxy {
518 Proxy {}
519 }
520
521 }
522
523 impl Proxy {
wakeup(&self) -> Result<(), EventsLoopClosed>524 pub fn wakeup(&self) -> Result<(), EventsLoopClosed> {
525 // Awaken the event loop by triggering `NSApplicationActivatedEventType`.
526 unsafe {
527 let pool = foundation::NSAutoreleasePool::new(cocoa::base::nil);
528 let event =
529 NSEvent::otherEventWithType_location_modifierFlags_timestamp_windowNumber_context_subtype_data1_data2_(
530 cocoa::base::nil,
531 appkit::NSApplicationDefined,
532 foundation::NSPoint::new(0.0, 0.0),
533 appkit::NSEventModifierFlags::empty(),
534 0.0,
535 0,
536 cocoa::base::nil,
537 appkit::NSEventSubtype::NSApplicationActivatedEventType,
538 0,
539 0);
540 appkit::NSApp().postEvent_atStart_(event, cocoa::base::NO);
541 foundation::NSAutoreleasePool::drain(pool);
542 }
543 Ok(())
544 }
545 }
546
char_to_keycode(c: char) -> Option<events::VirtualKeyCode>547 pub fn char_to_keycode(c: char) -> Option<events::VirtualKeyCode> {
548 // We only translate keys that are affected by keyboard layout.
549 //
550 // Note that since keys are translated in a somewhat "dumb" way (reading character)
551 // there is a concern that some combination, i.e. Cmd+char, causes the wrong
552 // letter to be received, and so we receive the wrong key.
553 //
554 // Implementation reference: https://github.com/WebKit/webkit/blob/82bae82cf0f329dbe21059ef0986c4e92fea4ba6/Source/WebCore/platform/cocoa/KeyEventCocoa.mm#L626
555 Some(match c {
556 'a' | 'A' => events::VirtualKeyCode::A,
557 'b' | 'B' => events::VirtualKeyCode::B,
558 'c' | 'C' => events::VirtualKeyCode::C,
559 'd' | 'D' => events::VirtualKeyCode::D,
560 'e' | 'E' => events::VirtualKeyCode::E,
561 'f' | 'F' => events::VirtualKeyCode::F,
562 'g' | 'G' => events::VirtualKeyCode::G,
563 'h' | 'H' => events::VirtualKeyCode::H,
564 'i' | 'I' => events::VirtualKeyCode::I,
565 'j' | 'J' => events::VirtualKeyCode::J,
566 'k' | 'K' => events::VirtualKeyCode::K,
567 'l' | 'L' => events::VirtualKeyCode::L,
568 'm' | 'M' => events::VirtualKeyCode::M,
569 'n' | 'N' => events::VirtualKeyCode::N,
570 'o' | 'O' => events::VirtualKeyCode::O,
571 'p' | 'P' => events::VirtualKeyCode::P,
572 'q' | 'Q' => events::VirtualKeyCode::Q,
573 'r' | 'R' => events::VirtualKeyCode::R,
574 's' | 'S' => events::VirtualKeyCode::S,
575 't' | 'T' => events::VirtualKeyCode::T,
576 'u' | 'U' => events::VirtualKeyCode::U,
577 'v' | 'V' => events::VirtualKeyCode::V,
578 'w' | 'W' => events::VirtualKeyCode::W,
579 'x' | 'X' => events::VirtualKeyCode::X,
580 'y' | 'Y' => events::VirtualKeyCode::Y,
581 'z' | 'Z' => events::VirtualKeyCode::Z,
582 '1' | '!' => events::VirtualKeyCode::Key1,
583 '2' | '@' => events::VirtualKeyCode::Key2,
584 '3' | '#' => events::VirtualKeyCode::Key3,
585 '4' | '$' => events::VirtualKeyCode::Key4,
586 '5' | '%' => events::VirtualKeyCode::Key5,
587 '6' | '^' => events::VirtualKeyCode::Key6,
588 '7' | '&' => events::VirtualKeyCode::Key7,
589 '8' | '*' => events::VirtualKeyCode::Key8,
590 '9' | '(' => events::VirtualKeyCode::Key9,
591 '0' | ')' => events::VirtualKeyCode::Key0,
592 '=' | '+' => events::VirtualKeyCode::Equals,
593 '-' | '_' => events::VirtualKeyCode::Minus,
594 ']' | '}' => events::VirtualKeyCode::RBracket,
595 '[' | '{' => events::VirtualKeyCode::LBracket,
596 '\''| '"' => events::VirtualKeyCode::Apostrophe,
597 ';' | ':' => events::VirtualKeyCode::Semicolon,
598 '\\'| '|' => events::VirtualKeyCode::Backslash,
599 ',' | '<' => events::VirtualKeyCode::Comma,
600 '/' | '?' => events::VirtualKeyCode::Slash,
601 '.' | '>' => events::VirtualKeyCode::Period,
602 '`' | '~' => events::VirtualKeyCode::Grave,
603 _ => return None,
604 })
605 }
606
scancode_to_keycode(code: c_ushort) -> Option<events::VirtualKeyCode>607 pub fn scancode_to_keycode(code: c_ushort) -> Option<events::VirtualKeyCode> {
608 Some(match code {
609 0x00 => events::VirtualKeyCode::A,
610 0x01 => events::VirtualKeyCode::S,
611 0x02 => events::VirtualKeyCode::D,
612 0x03 => events::VirtualKeyCode::F,
613 0x04 => events::VirtualKeyCode::H,
614 0x05 => events::VirtualKeyCode::G,
615 0x06 => events::VirtualKeyCode::Z,
616 0x07 => events::VirtualKeyCode::X,
617 0x08 => events::VirtualKeyCode::C,
618 0x09 => events::VirtualKeyCode::V,
619 //0x0a => World 1,
620 0x0b => events::VirtualKeyCode::B,
621 0x0c => events::VirtualKeyCode::Q,
622 0x0d => events::VirtualKeyCode::W,
623 0x0e => events::VirtualKeyCode::E,
624 0x0f => events::VirtualKeyCode::R,
625 0x10 => events::VirtualKeyCode::Y,
626 0x11 => events::VirtualKeyCode::T,
627 0x12 => events::VirtualKeyCode::Key1,
628 0x13 => events::VirtualKeyCode::Key2,
629 0x14 => events::VirtualKeyCode::Key3,
630 0x15 => events::VirtualKeyCode::Key4,
631 0x16 => events::VirtualKeyCode::Key6,
632 0x17 => events::VirtualKeyCode::Key5,
633 0x18 => events::VirtualKeyCode::Equals,
634 0x19 => events::VirtualKeyCode::Key9,
635 0x1a => events::VirtualKeyCode::Key7,
636 0x1b => events::VirtualKeyCode::Minus,
637 0x1c => events::VirtualKeyCode::Key8,
638 0x1d => events::VirtualKeyCode::Key0,
639 0x1e => events::VirtualKeyCode::RBracket,
640 0x1f => events::VirtualKeyCode::O,
641 0x20 => events::VirtualKeyCode::U,
642 0x21 => events::VirtualKeyCode::LBracket,
643 0x22 => events::VirtualKeyCode::I,
644 0x23 => events::VirtualKeyCode::P,
645 0x24 => events::VirtualKeyCode::Return,
646 0x25 => events::VirtualKeyCode::L,
647 0x26 => events::VirtualKeyCode::J,
648 0x27 => events::VirtualKeyCode::Apostrophe,
649 0x28 => events::VirtualKeyCode::K,
650 0x29 => events::VirtualKeyCode::Semicolon,
651 0x2a => events::VirtualKeyCode::Backslash,
652 0x2b => events::VirtualKeyCode::Comma,
653 0x2c => events::VirtualKeyCode::Slash,
654 0x2d => events::VirtualKeyCode::N,
655 0x2e => events::VirtualKeyCode::M,
656 0x2f => events::VirtualKeyCode::Period,
657 0x30 => events::VirtualKeyCode::Tab,
658 0x31 => events::VirtualKeyCode::Space,
659 0x32 => events::VirtualKeyCode::Grave,
660 0x33 => events::VirtualKeyCode::Back,
661 //0x34 => unkown,
662 0x35 => events::VirtualKeyCode::Escape,
663 0x36 => events::VirtualKeyCode::RWin,
664 0x37 => events::VirtualKeyCode::LWin,
665 0x38 => events::VirtualKeyCode::LShift,
666 //0x39 => Caps lock,
667 0x3a => events::VirtualKeyCode::LAlt,
668 0x3b => events::VirtualKeyCode::LControl,
669 0x3c => events::VirtualKeyCode::RShift,
670 0x3d => events::VirtualKeyCode::RAlt,
671 0x3e => events::VirtualKeyCode::RControl,
672 //0x3f => Fn key,
673 0x40 => events::VirtualKeyCode::F17,
674 0x41 => events::VirtualKeyCode::Decimal,
675 //0x42 -> unkown,
676 0x43 => events::VirtualKeyCode::Multiply,
677 //0x44 => unkown,
678 0x45 => events::VirtualKeyCode::Add,
679 //0x46 => unkown,
680 0x47 => events::VirtualKeyCode::Numlock,
681 //0x48 => KeypadClear,
682 0x49 => events::VirtualKeyCode::VolumeUp,
683 0x4a => events::VirtualKeyCode::VolumeDown,
684 0x4b => events::VirtualKeyCode::Divide,
685 0x4c => events::VirtualKeyCode::NumpadEnter,
686 0x4e => events::VirtualKeyCode::Subtract,
687 //0x4d => unkown,
688 0x4e => events::VirtualKeyCode::Subtract,
689 0x4f => events::VirtualKeyCode::F18,
690 0x50 => events::VirtualKeyCode::F19,
691 0x51 => events::VirtualKeyCode::NumpadEquals,
692 0x52 => events::VirtualKeyCode::Numpad0,
693 0x53 => events::VirtualKeyCode::Numpad1,
694 0x54 => events::VirtualKeyCode::Numpad2,
695 0x55 => events::VirtualKeyCode::Numpad3,
696 0x56 => events::VirtualKeyCode::Numpad4,
697 0x57 => events::VirtualKeyCode::Numpad5,
698 0x58 => events::VirtualKeyCode::Numpad6,
699 0x59 => events::VirtualKeyCode::Numpad7,
700 0x5a => events::VirtualKeyCode::F20,
701 0x5b => events::VirtualKeyCode::Numpad8,
702 0x5c => events::VirtualKeyCode::Numpad9,
703 0x5d => events::VirtualKeyCode::Yen,
704 //0x5e => JIS Ro,
705 //0x5f => unkown,
706 0x60 => events::VirtualKeyCode::F5,
707 0x61 => events::VirtualKeyCode::F6,
708 0x62 => events::VirtualKeyCode::F7,
709 0x63 => events::VirtualKeyCode::F3,
710 0x64 => events::VirtualKeyCode::F8,
711 0x65 => events::VirtualKeyCode::F9,
712 //0x66 => JIS Eisuu (macOS),
713 0x67 => events::VirtualKeyCode::F11,
714 //0x68 => JIS Kana (macOS),
715 0x69 => events::VirtualKeyCode::F13,
716 0x6a => events::VirtualKeyCode::F16,
717 0x6b => events::VirtualKeyCode::F14,
718 //0x6c => unkown,
719 0x6d => events::VirtualKeyCode::F10,
720 //0x6e => unkown,
721 0x6f => events::VirtualKeyCode::F12,
722 //0x70 => unkown,
723 0x71 => events::VirtualKeyCode::F15,
724 0x72 => events::VirtualKeyCode::Insert,
725 0x73 => events::VirtualKeyCode::Home,
726 0x74 => events::VirtualKeyCode::PageUp,
727 0x75 => events::VirtualKeyCode::Delete,
728 0x76 => events::VirtualKeyCode::F4,
729 0x77 => events::VirtualKeyCode::End,
730 0x78 => events::VirtualKeyCode::F2,
731 0x79 => events::VirtualKeyCode::PageDown,
732 0x7a => events::VirtualKeyCode::F1,
733 0x7b => events::VirtualKeyCode::Left,
734 0x7c => events::VirtualKeyCode::Right,
735 0x7d => events::VirtualKeyCode::Down,
736 0x7e => events::VirtualKeyCode::Up,
737 //0x7f => unkown,
738
739 0xa => events::VirtualKeyCode::Caret,
740 _ => return None,
741 })
742 }
743
check_function_keys( s: &String ) -> Option<events::VirtualKeyCode>744 pub fn check_function_keys(
745 s: &String
746 ) -> Option<events::VirtualKeyCode> {
747 if let Some(ch) = s.encode_utf16().next() {
748 return Some(match ch {
749 0xf718 => events::VirtualKeyCode::F21,
750 0xf719 => events::VirtualKeyCode::F22,
751 0xf71a => events::VirtualKeyCode::F23,
752 0xf71b => events::VirtualKeyCode::F24,
753 _ => return None,
754 })
755 }
756
757 None
758 }
759
event_mods(event: cocoa::base::id) -> ModifiersState760 pub fn event_mods(event: cocoa::base::id) -> ModifiersState {
761 let flags = unsafe {
762 NSEvent::modifierFlags(event)
763 };
764 ModifiersState {
765 shift: flags.contains(NSEventModifierFlags::NSShiftKeyMask),
766 ctrl: flags.contains(NSEventModifierFlags::NSControlKeyMask),
767 alt: flags.contains(NSEventModifierFlags::NSAlternateKeyMask),
768 logo: flags.contains(NSEventModifierFlags::NSCommandKeyMask),
769 }
770 }
771
get_scancode(event: cocoa::base::id) -> c_ushort772 pub fn get_scancode(event: cocoa::base::id) -> c_ushort {
773 // In AppKit, `keyCode` refers to the position (scancode) of a key rather than its character,
774 // and there is no easy way to navtively retrieve the layout-dependent character.
775 // In winit, we use keycode to refer to the key's character, and so this function aligns
776 // AppKit's terminology with ours.
777 unsafe {
778 msg_send![event, keyCode]
779 }
780 }
781
modifier_event( ns_event: cocoa::base::id, keymask: NSEventModifierFlags, was_key_pressed: bool, ) -> Option<WindowEvent>782 unsafe fn modifier_event(
783 ns_event: cocoa::base::id,
784 keymask: NSEventModifierFlags,
785 was_key_pressed: bool,
786 ) -> Option<WindowEvent> {
787 if !was_key_pressed && NSEvent::modifierFlags(ns_event).contains(keymask)
788 || was_key_pressed && !NSEvent::modifierFlags(ns_event).contains(keymask) {
789 let state = if was_key_pressed {
790 ElementState::Released
791 } else {
792 ElementState::Pressed
793 };
794
795 let scancode = get_scancode(ns_event);
796 let virtual_keycode = scancode_to_keycode(scancode);
797 Some(WindowEvent::KeyboardInput {
798 device_id: DEVICE_ID,
799 input: KeyboardInput {
800 state,
801 scancode: scancode as u32,
802 virtual_keycode,
803 modifiers: event_mods(ns_event),
804 },
805 })
806 } else {
807 None
808 }
809 }
810
811 // Constant device ID, to be removed when this backend is updated to report real device IDs.
812 pub const DEVICE_ID: ::DeviceId = ::DeviceId(DeviceId);
813