From 2b4b64f4997ad2a1ec8683e0437dcbf61e313758 Mon Sep 17 00:00:00 2001 From: Francesca Frangipane Date: Mon, 11 Jun 2018 11:16:39 -0400 Subject: [PATCH] macOS: Only detect clicks+motion within client area (#561) * macOS: Only detect clicks within client area * macOS: Only track mouse motion within client area * Add CHANGELOG entry about #463 fix --- CHANGELOG.md | 2 + src/platform/macos/events_loop.rs | 30 +------- src/platform/macos/view.rs | 120 +++++++++++++++++++++++++++++- 3 files changed, 122 insertions(+), 30 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a00b88d3d..6ba6357030 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,8 @@ - On macOS, `VirtualKeyCode::RWin` and `VirtualKeyCode::LWin` are no longer switched. - On macOS, windows without decorations can once again be resized. - Fixed race conditions when creating an `EventsLoop` on X11, most commonly manifesting as "[xcb] Unknown sequence number while processing queue". +- On macOS, `CursorMoved` and `MouseInput` events are only generated if they occurs within the window's client area. +- On macOS, resizing the window no longer generates a spurious `MouseInput` event. # Version 0.15.0 (2018-05-22) diff --git a/src/platform/macos/events_loop.rs b/src/platform/macos/events_loop.rs index 6b6d1c67a6..f084e46aff 100644 --- a/src/platform/macos/events_loop.rs +++ b/src/platform/macos/events_loop.rs @@ -1,7 +1,7 @@ use {ControlFlow, EventsLoopClosed}; use cocoa::{self, appkit, foundation}; use cocoa::appkit::{NSApplication, NSEvent, NSEventMask, NSEventModifierFlags, NSEventPhase, NSView, NSWindow}; -use events::{self, ElementState, Event, MouseButton, TouchPhase, WindowEvent, DeviceEvent, ModifiersState, KeyboardInput}; +use events::{self, ElementState, Event, TouchPhase, WindowEvent, DeviceEvent, ModifiersState, KeyboardInput}; use std::collections::VecDeque; use std::sync::{Arc, Mutex, Weak}; use super::window::Window2; @@ -9,7 +9,6 @@ use std; use std::os::raw::*; use super::DeviceId; - pub struct EventsLoop { modifiers: Modifiers, pub shared: Arc, @@ -363,13 +362,6 @@ impl EventsLoop { event }, - appkit::NSLeftMouseDown => { Some(into_event(WindowEvent::MouseInput { device_id: DEVICE_ID, state: ElementState::Pressed, button: MouseButton::Left, modifiers: event_mods(ns_event) })) }, - appkit::NSLeftMouseUp => { Some(into_event(WindowEvent::MouseInput { device_id: DEVICE_ID, state: ElementState::Released, button: MouseButton::Left, modifiers: event_mods(ns_event) })) }, - appkit::NSRightMouseDown => { Some(into_event(WindowEvent::MouseInput { device_id: DEVICE_ID, state: ElementState::Pressed, button: MouseButton::Right, modifiers: event_mods(ns_event) })) }, - appkit::NSRightMouseUp => { Some(into_event(WindowEvent::MouseInput { device_id: DEVICE_ID, state: ElementState::Released, button: MouseButton::Right, modifiers: event_mods(ns_event) })) }, - appkit::NSOtherMouseDown => { Some(into_event(WindowEvent::MouseInput { device_id: DEVICE_ID, state: ElementState::Pressed, button: MouseButton::Middle, modifiers: event_mods(ns_event) })) }, - appkit::NSOtherMouseUp => { Some(into_event(WindowEvent::MouseInput { device_id: DEVICE_ID, state: ElementState::Released, button: MouseButton::Middle, modifiers: event_mods(ns_event) })) }, - appkit::NSMouseEntered => { let window = match maybe_window.or_else(maybe_key_window) { Some(window) => window, @@ -410,27 +402,9 @@ impl EventsLoop { None => return None, }; - let window_point = ns_event.locationInWindow(); - let view_point = if ns_window == cocoa::base::nil { - let ns_size = foundation::NSSize::new(0.0, 0.0); - let ns_rect = foundation::NSRect::new(window_point, ns_size); - let window_rect = window.window.convertRectFromScreen_(ns_rect); - window.view.convertPoint_fromView_(window_rect.origin, cocoa::base::nil) - } else { - window.view.convertPoint_fromView_(window_point, cocoa::base::nil) - }; - let view_rect = NSView::frame(*window.view); let scale_factor = window.hidpi_factor(); - let mut events = std::collections::VecDeque::new(); - - { - let x = (scale_factor * view_point.x as f32) as f64; - let y = (scale_factor * (view_rect.size.height - view_point.y) as f32) as f64; - let window_event = WindowEvent::CursorMoved { device_id: DEVICE_ID, position: (x, y), modifiers: event_mods(ns_event) }; - let event = Event::WindowEvent { window_id: ::WindowId(window.id()), event: window_event }; - events.push_back(event); - } + let mut events = std::collections::VecDeque::with_capacity(3); let delta_x = (scale_factor * ns_event.deltaX() as f32) as f64; if delta_x != 0.0 { diff --git a/src/platform/macos/view.rs b/src/platform/macos/view.rs index 8d20778498..7ce3de0dae 100644 --- a/src/platform/macos/view.rs +++ b/src/platform/macos/view.rs @@ -8,12 +8,12 @@ use std::os::raw::*; use std::sync::Weak; use cocoa::base::{class, id, nil}; -use cocoa::appkit::NSWindow; +use cocoa::appkit::{NSEvent, NSView, NSWindow}; use cocoa::foundation::{NSPoint, NSRect, NSSize, NSString, NSUInteger}; use objc::declare::ClassDecl; use objc::runtime::{Class, Object, Protocol, Sel, BOOL}; -use {ElementState, Event, KeyboardInput, WindowEvent, WindowId}; +use {ElementState, Event, KeyboardInput, MouseButton, WindowEvent, WindowId}; use platform::platform::events_loop::{DEVICE_ID, event_mods, Shared, to_virtual_key_code}; use platform::platform::util; use platform::platform::ffi::*; @@ -112,6 +112,16 @@ lazy_static! { decl.add_method(sel!(keyUp:), key_up as extern fn(&Object, Sel, id)); decl.add_method(sel!(insertTab:), insert_tab as extern fn(&Object, Sel, id)); decl.add_method(sel!(insertBackTab:), insert_back_tab as extern fn(&Object, Sel, id)); + decl.add_method(sel!(mouseDown:), mouse_down as extern fn(&Object, Sel, id)); + decl.add_method(sel!(mouseUp:), mouse_up as extern fn(&Object, Sel, id)); + decl.add_method(sel!(rightMouseDown:), right_mouse_down as extern fn(&Object, Sel, id)); + decl.add_method(sel!(rightMouseUp:), right_mouse_up as extern fn(&Object, Sel, id)); + decl.add_method(sel!(otherMouseDown:), other_mouse_down as extern fn(&Object, Sel, id)); + decl.add_method(sel!(otherMouseUp:), other_mouse_up as extern fn(&Object, Sel, id)); + decl.add_method(sel!(mouseMoved:), mouse_moved as extern fn(&Object, Sel, id)); + decl.add_method(sel!(mouseDragged:), mouse_dragged as extern fn(&Object, Sel, id)); + decl.add_method(sel!(rightMouseDragged:), right_mouse_dragged as extern fn(&Object, Sel, id)); + decl.add_method(sel!(otherMouseDragged:), other_mouse_dragged as extern fn(&Object, Sel, id)); decl.add_ivar::<*mut c_void>("winitState"); decl.add_ivar::("markedText"); let protocol = Protocol::get("NSTextInputClient").unwrap(); @@ -448,3 +458,109 @@ extern fn insert_back_tab(this: &Object, _sel: Sel, _sender: id) { } } } + +fn mouse_click(this: &Object, event: id, button: MouseButton, button_state: ElementState) { + unsafe { + let state_ptr: *mut c_void = *this.get_ivar("winitState"); + let state = &mut *(state_ptr as *mut ViewState); + + let window_event = Event::WindowEvent { + window_id: WindowId(get_window_id(state.window)), + event: WindowEvent::MouseInput { + device_id: DEVICE_ID, + state: button_state, + button, + modifiers: event_mods(event), + }, + }; + + if let Some(shared) = state.shared.upgrade() { + shared.pending_events + .lock() + .unwrap() + .push_back(window_event); + } + } +} + +extern fn mouse_down(this: &Object, _sel: Sel, event: id) { + mouse_click(this, event, MouseButton::Left, ElementState::Pressed); +} + +extern fn mouse_up(this: &Object, _sel: Sel, event: id) { + mouse_click(this, event, MouseButton::Left, ElementState::Released); +} + +extern fn right_mouse_down(this: &Object, _sel: Sel, event: id) { + mouse_click(this, event, MouseButton::Right, ElementState::Pressed); +} + +extern fn right_mouse_up(this: &Object, _sel: Sel, event: id) { + mouse_click(this, event, MouseButton::Right, ElementState::Released); +} + +extern fn other_mouse_down(this: &Object, _sel: Sel, event: id) { + mouse_click(this, event, MouseButton::Middle, ElementState::Pressed); +} + +extern fn other_mouse_up(this: &Object, _sel: Sel, event: id) { + mouse_click(this, event, MouseButton::Middle, ElementState::Released); +} + +fn mouse_motion(this: &Object, event: id) { + unsafe { + let state_ptr: *mut c_void = *this.get_ivar("winitState"); + let state = &mut *(state_ptr as *mut ViewState); + + // We have to do this to have access to the `NSView` trait... + let view: id = this as *const _ as *mut _; + + let window_point = event.locationInWindow(); + let view_point = view.convertPoint_fromView_(window_point, nil); + let view_rect = NSView::frame(view); + + if view_point.x.is_sign_negative() + || view_point.y.is_sign_negative() + || view_point.x > view_rect.size.width + || view_point.y > view_rect.size.height { + // Point is outside of the client area (view) + return; + } + + let scale_factor = NSWindow::backingScaleFactor(state.window) as f64; + let x = scale_factor * view_point.x as f64; + let y = scale_factor * (view_rect.size.height as f64 - view_point.y as f64); + + let window_event = Event::WindowEvent { + window_id: WindowId(get_window_id(state.window)), + event: WindowEvent::CursorMoved { + device_id: DEVICE_ID, + position: (x, y), + modifiers: event_mods(event), + }, + }; + + if let Some(shared) = state.shared.upgrade() { + shared.pending_events + .lock() + .unwrap() + .push_back(window_event); + } + } +} + +extern fn mouse_moved(this: &Object, _sel: Sel, event: id) { + mouse_motion(this, event); +} + +extern fn mouse_dragged(this: &Object, _sel: Sel, event: id) { + mouse_motion(this, event); +} + +extern fn right_mouse_dragged(this: &Object, _sel: Sel, event: id) { + mouse_motion(this, event); +} + +extern fn other_mouse_dragged(this: &Object, _sel: Sel, event: id) { + mouse_motion(this, event); +}