mirror of
https://github.com/servo/servo.git
synced 2025-08-05 21:50:18 +01:00
Handle multi-touch events from glutin
This commit is contained in:
parent
5c11c88e92
commit
ef93650db9
7 changed files with 227 additions and 102 deletions
|
@ -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::Nothing
|
||||
}
|
||||
TouchState::Panning => {}
|
||||
TouchState::DefaultPrevented => {}
|
||||
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 = TouchState::Nothing;
|
||||
|
||||
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"),
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue