Skip to content

Commit

Permalink
macOS: Only detect clicks+motion within client area (#561)
Browse files Browse the repository at this point in the history
* macOS: Only detect clicks within client area

* macOS: Only track mouse motion within client area

* Add CHANGELOG entry about #463 fix
  • Loading branch information
francesca64 authored Jun 11, 2018
1 parent 262490d commit 2b4b64f
Show file tree
Hide file tree
Showing 3 changed files with 122 additions and 30 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
30 changes: 2 additions & 28 deletions src/platform/macos/events_loop.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
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;
use std;
use std::os::raw::*;
use super::DeviceId;


pub struct EventsLoop {
modifiers: Modifiers,
pub shared: Arc<Shared>,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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 {
Expand Down
120 changes: 118 additions & 2 deletions src/platform/macos/view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::*;
Expand Down Expand Up @@ -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::<id>("markedText");
let protocol = Protocol::get("NSTextInputClient").unwrap();
Expand Down Expand Up @@ -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);
}

0 comments on commit 2b4b64f

Please sign in to comment.