Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Correct event dispatching for multiple simultaneous touch points #8232

Merged
merged 1 commit into from Nov 3, 2015
Merged
Changes from all commits
Commits
File filter...
Filter file types
Jump to…
Jump to file
Failed to load files.

Always

Just for now

Handle multi-touch events from glutin

  • Loading branch information
mbrubeck committed Nov 3, 2015
commit ef93650db944c0865da9f0c0b198dbc89fc46bd4
@@ -35,8 +35,9 @@ use msg::constellation_msg::{NavigationDirection, PipelineId, WindowSizeData};
use pipeline::CompositionPipeline;
use profile_traits::mem::{self, ReportKind, Reporter, ReporterRequest};
use profile_traits::time::{self, ProfilerCategory, profile};
use script_traits::CompositorEvent::{TouchDownEvent, TouchMoveEvent, TouchUpEvent};
use script_traits::CompositorEvent::TouchEvent;
use script_traits::{ConstellationControlMsg, LayoutControlMsg, MouseButton};
use script_traits::{TouchEventType, TouchId};
use scrolling::ScrollingTimerProxy;
use std::collections::hash_map::Entry::{Occupied, Vacant};
use std::collections::{HashMap, HashSet};
@@ -72,23 +73,6 @@ enum ReadyState {
ReadyToSaveImage,
}

/// The states of the touch input state machine.
///
/// TODO: Currently Add support for "flinging" (scrolling inertia), pinch zooming, better
/// support for multiple touch points.
enum TouchState {
/// Not tracking any touch point
Nothing,
/// A touchstart event was dispatched to the page, but the response wasn't received yet.
WaitingForScript,
/// Script is consuming the current touch point; don't perform default actions.
DefaultPrevented,
/// A single touch point is active and may perform click or pan default actions.
Touching,
/// A single touch point is active and has started panning.
Panning,
}

/// NB: Never block on the constellation, because sometimes the constellation blocks on us.
pub struct IOCompositor<Window: WindowMethods> {
/// The application window.
@@ -201,6 +185,27 @@ pub struct IOCompositor<Window: WindowMethods> {
pending_subpages: HashSet<PipelineId>,
}

/// The states of the touch input state machine.
///
/// TODO: Currently Add support for "flinging" (scrolling inertia), pinch zooming.
enum TouchState {
/// Not tracking any touch point
Nothing,
/// A touchstart event was dispatched to the page, but the response wasn't received yet.
/// Contains the number of active touch points.
WaitingForScript(u32),
/// Script is consuming the current touch sequence; don't perform default actions.
/// Contains the number of active touch points.
DefaultPrevented(u32),
/// A single touch point is active and may perform click or pan default actions.
Touching,
/// A single touch point is active and has started panning.
Panning,
/// A multi-touch gesture is in progress. Contains the number of active touch points.
MultiTouch(u32),
}


pub struct ScrollEvent {
delta: TypedPoint2D<DevicePixel, f32>,
cursor: TypedPoint2D<DevicePixel, i32>,
@@ -537,10 +542,14 @@ impl<Window: WindowMethods> IOCompositor<Window> {

(Msg::TouchEventProcessed(result), ShutdownState::NotShuttingDown) => {
match self.touch_gesture_state {
TouchState::WaitingForScript => {
TouchState::WaitingForScript(n) => {
self.touch_gesture_state = match result {
EventResult::DefaultAllowed => TouchState::Touching,
EventResult::DefaultPrevented => TouchState::DefaultPrevented,
EventResult::DefaultPrevented => TouchState::DefaultPrevented(n),
EventResult::DefaultAllowed => if n > 1 {
TouchState::MultiTouch(n)
} else {
TouchState::Touching
}
};
}
_ => {}
@@ -1056,6 +1065,15 @@ impl<Window: WindowMethods> IOCompositor<Window> {
self.on_mouse_window_move_event_class(cursor);
}

WindowEvent::Touch(event_type, identifier, location) => {
match event_type {
TouchEventType::Down => self.on_touch_down(identifier, location),
TouchEventType::Move => self.on_touch_move(identifier, location),
TouchEventType::Up => self.on_touch_up(identifier, location),
TouchEventType::Cancel => self.on_touch_cancel(identifier, location),
}
}

WindowEvent::Scroll(delta, cursor) => {
self.on_scroll_window_event(delta, cursor);
}
@@ -1127,8 +1145,8 @@ impl<Window: WindowMethods> IOCompositor<Window> {
if opts::get().convert_mouse_to_touch {
match mouse_window_event {
MouseWindowEvent::Click(_, _) => {}
MouseWindowEvent::MouseDown(_, p) => self.on_touch_down(0, p),
MouseWindowEvent::MouseUp(_, p) => self.on_touch_up(0, p),
MouseWindowEvent::MouseDown(_, p) => self.on_touch_down(TouchId(0), p),
MouseWindowEvent::MouseUp(_, p) => self.on_touch_up(TouchId(0), p),
}
return
}
@@ -1146,7 +1164,7 @@ impl<Window: WindowMethods> IOCompositor<Window> {

fn on_mouse_window_move_event_class(&mut self, cursor: TypedPoint2D<DevicePixel, f32>) {
if opts::get().convert_mouse_to_touch {
self.on_touch_move(0, cursor);
self.on_touch_move(TouchId(0), cursor);
return
}

@@ -1156,31 +1174,40 @@ impl<Window: WindowMethods> IOCompositor<Window> {
}
}

fn on_touch_down(&mut self, identifier: i32, point: TypedPoint2D<DevicePixel, f32>) {
match self.touch_gesture_state {
fn on_touch_down(&mut self, identifier: TouchId, point: TypedPoint2D<DevicePixel, f32>) {
self.touch_gesture_state = match self.touch_gesture_state {
TouchState::Nothing => {
// TODO: Don't wait for script if we know the page has no touch event listeners.
self.first_touch_point = Some(point);
self.last_touch_point = Some(point);
self.touch_gesture_state = TouchState::WaitingForScript;
TouchState::WaitingForScript(1)
}
TouchState::WaitingForScript => {
// TODO: Queue events while waiting for script?
TouchState::Touching |
TouchState::Panning => {
// Was a single-touch sequence; now a multi-touch sequence:
TouchState::MultiTouch(2)
}
TouchState::DefaultPrevented => {}
TouchState::Touching => {}
TouchState::Panning => {}
}
TouchState::WaitingForScript(n) => {
TouchState::WaitingForScript(n + 1)
}
TouchState::DefaultPrevented(n) => {
TouchState::DefaultPrevented(n + 1)
}
TouchState::MultiTouch(n) => {
TouchState::MultiTouch(n + 1)
}
};
if let Some(result) = self.find_topmost_layer_at_point(point / self.scene.scale) {
result.layer.send_event(self, TouchDownEvent(identifier, result.point.to_untyped()));
result.layer.send_event(self, TouchEvent(TouchEventType::Down, identifier,
result.point.to_untyped()));
}
}

fn on_touch_move(&mut self, identifier: i32, point: TypedPoint2D<DevicePixel, f32>) {
fn on_touch_move(&mut self, identifier: TouchId, point: TypedPoint2D<DevicePixel, f32>) {
match self.touch_gesture_state {
TouchState::Nothing => warn!("Got unexpected touch move event"),

TouchState::WaitingForScript => {
TouchState::WaitingForScript(_) => {
// TODO: Queue events while waiting for script?
}
TouchState::Touching => {
@@ -1209,40 +1236,67 @@ impl<Window: WindowMethods> IOCompositor<Window> {
None => warn!("last_touch_point not set")
}
}
TouchState::DefaultPrevented => {
TouchState::DefaultPrevented(_) => {
// Send the event to script.
if let Some(result) = self.find_topmost_layer_at_point(point / self.scene.scale) {
result.layer.send_event(self,
TouchMoveEvent(identifier, result.point.to_untyped()));
result.layer.send_event(self, TouchEvent(TouchEventType::Move, identifier,
result.point.to_untyped()));
}
}
TouchState::MultiTouch(_) => {
// TODO: Pinch zooming.
}
}
self.last_touch_point = Some(point);
}

fn on_touch_up(&mut self, identifier: i32, point: TypedPoint2D<DevicePixel, f32>) {
// TODO: Track the number of active touch points, and don't reset stuff until it is zero.
self.first_touch_point = None;
self.last_touch_point = None;

fn on_touch_up(&mut self, identifier: TouchId, point: TypedPoint2D<DevicePixel, f32>) {
// Send the event to script.
if let Some(result) = self.find_topmost_layer_at_point(point / self.scene.scale) {
result.layer.send_event(self, TouchUpEvent(identifier, result.point.to_untyped()));
result.layer.send_event(self, TouchEvent(TouchEventType::Up, identifier,
result.point.to_untyped()));
}

match self.touch_gesture_state {
TouchState::Nothing => warn!("Got unexpected touch up event"),

TouchState::WaitingForScript => {}
self.touch_gesture_state = match self.touch_gesture_state {
TouchState::Touching => {
// TODO: If the duration exceeds some threshold, send a contextmenu event instead.
// TODO: Don't send a click if preventDefault is called on the touchend event.
self.simulate_mouse_click(point);
}
TouchState::Panning => {}
TouchState::DefaultPrevented => {}
}
self.touch_gesture_state = TouchState::Nothing;
TouchState::Nothing
}
TouchState::Nothing |
TouchState::Panning |
TouchState::WaitingForScript(1) |
TouchState::DefaultPrevented(1) |
TouchState::MultiTouch(1) => {
TouchState::Nothing
}
TouchState::WaitingForScript(n) => TouchState::WaitingForScript(n - 1),
TouchState::DefaultPrevented(n) => TouchState::DefaultPrevented(n - 1),
TouchState::MultiTouch(n) => TouchState::MultiTouch(n - 1),
};
}

fn on_touch_cancel(&mut self, identifier: TouchId, point: TypedPoint2D<DevicePixel, f32>) {
// Send the event to script.
if let Some(result) = self.find_topmost_layer_at_point(point / self.scene.scale) {
result.layer.send_event(self, TouchEvent(TouchEventType::Cancel, identifier,
result.point.to_untyped()));
}

self.touch_gesture_state = match self.touch_gesture_state {
TouchState::Nothing |
TouchState::Touching |
TouchState::Panning |
TouchState::WaitingForScript(1) |
TouchState::DefaultPrevented(1) |
TouchState::MultiTouch(1) => {
TouchState::Nothing
}
TouchState::WaitingForScript(n) => TouchState::WaitingForScript(n - 1),
TouchState::DefaultPrevented(n) => TouchState::DefaultPrevented(n - 1),
TouchState::MultiTouch(n) => TouchState::MultiTouch(n - 1),
};
}

/// http://w3c.github.io/touch-events/#mouse-events
@@ -13,7 +13,7 @@ use layers::geometry::DevicePixel;
use layers::platform::surface::NativeDisplay;
use msg::constellation_msg::{Key, KeyModifiers, KeyState};
use net_traits::net_error_list::NetError;
use script_traits::MouseButton;
use script_traits::{MouseButton, TouchEventType, TouchId};
use std::fmt::{Debug, Error, Formatter};
use std::rc::Rc;
use url::Url;
@@ -27,6 +27,7 @@ pub enum MouseWindowEvent {
MouseUp(MouseButton, TypedPoint2D<DevicePixel, f32>),
}


#[derive(Clone)]
pub enum WindowNavigateMsg {
Forward,
@@ -59,6 +60,8 @@ pub enum WindowEvent {
MouseWindowEventClass(MouseWindowEvent),
/// Sent when a mouse move.
MouseWindowMoveEventClass(TypedPoint2D<DevicePixel, f32>),
/// Touch event: type, identifier, point
Touch(TouchEventType, TouchId, TypedPoint2D<DevicePixel, f32>),
/// Sent when the user scrolls. The first point is the delta and the second point is the
/// origin.
Scroll(TypedPoint2D<DevicePixel, f32>, TypedPoint2D<DevicePixel, i32>),
@@ -88,6 +91,7 @@ impl Debug for WindowEvent {
WindowEvent::LoadUrl(..) => write!(f, "LoadUrl"),
WindowEvent::MouseWindowEventClass(..) => write!(f, "Mouse"),
WindowEvent::MouseWindowMoveEventClass(..) => write!(f, "MouseMove"),
WindowEvent::Touch(..) => write!(f, "Touch"),
WindowEvent::Scroll(..) => write!(f, "Scroll"),
WindowEvent::Zoom(..) => write!(f, "Zoom"),
WindowEvent::PinchZoom(..) => write!(f, "PinchZoom"),
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.