// Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "content/browser/renderer_host/input/synthetic_touchscreen_pinch_gesture.h" #include #include #include "base/logging.h" #include "ui/latency/latency_info.h" namespace content { SyntheticTouchscreenPinchGesture::SyntheticTouchscreenPinchGesture( const SyntheticPinchGestureParams& params) : params_(params), start_y_0_(0.0f), start_y_1_(0.0f), max_pointer_delta_0_(0.0f), gesture_source_type_(SyntheticGestureParams::DEFAULT_INPUT), state_(SETUP) { DCHECK_GT(params_.scale_factor, 0.0f); } SyntheticTouchscreenPinchGesture::~SyntheticTouchscreenPinchGesture() {} SyntheticGesture::Result SyntheticTouchscreenPinchGesture::ForwardInputEvents( const base::TimeTicks& timestamp, SyntheticGestureTarget* target) { if (state_ == SETUP) { gesture_source_type_ = params_.gesture_source_type; if (gesture_source_type_ == SyntheticGestureParams::DEFAULT_INPUT) gesture_source_type_ = target->GetDefaultSyntheticGestureSourceType(); state_ = STARTED; start_time_ = timestamp; } DCHECK_NE(gesture_source_type_, SyntheticGestureParams::DEFAULT_INPUT); if (!synthetic_pointer_driver_) synthetic_pointer_driver_ = SyntheticPointerDriver::Create(gesture_source_type_); if (gesture_source_type_ == SyntheticGestureParams::TOUCH_INPUT) { ForwardTouchInputEvents(timestamp, target); } else { return SyntheticGesture::GESTURE_SOURCE_TYPE_NOT_IMPLEMENTED; } return (state_ == DONE) ? SyntheticGesture::GESTURE_FINISHED : SyntheticGesture::GESTURE_RUNNING; } void SyntheticTouchscreenPinchGesture::ForwardTouchInputEvents( const base::TimeTicks& timestamp, SyntheticGestureTarget* target) { switch (state_) { case STARTED: // Check for an early finish. if (params_.scale_factor == 1.0f) { state_ = DONE; break; } SetupCoordinatesAndStopTime(target); PressTouchPoints(target, timestamp); state_ = MOVING; break; case MOVING: { base::TimeTicks event_timestamp = ClampTimestamp(timestamp); float delta = GetDeltaForPointer0AtTime(event_timestamp); MoveTouchPoints(target, delta, event_timestamp); if (HasReachedTarget(event_timestamp)) { ReleaseTouchPoints(target, event_timestamp); state_ = DONE; } } break; case SETUP: NOTREACHED() << "State SETUP invalid for synthetic pinch."; break; case DONE: NOTREACHED() << "State DONE invalid for synthetic pinch."; break; } } void SyntheticTouchscreenPinchGesture::PressTouchPoints( SyntheticGestureTarget* target, const base::TimeTicks& timestamp) { synthetic_pointer_driver_->Press(params_.anchor.x(), start_y_0_, 0); synthetic_pointer_driver_->Press(params_.anchor.x(), start_y_1_, 1); synthetic_pointer_driver_->DispatchEvent(target, timestamp); } void SyntheticTouchscreenPinchGesture::MoveTouchPoints( SyntheticGestureTarget* target, float delta, const base::TimeTicks& timestamp) { // The two pointers move in opposite directions. float current_y_0 = start_y_0_ + delta; float current_y_1 = start_y_1_ - delta; synthetic_pointer_driver_->Move(params_.anchor.x(), current_y_0, 0); synthetic_pointer_driver_->Move(params_.anchor.x(), current_y_1, 1); synthetic_pointer_driver_->DispatchEvent(target, timestamp); } void SyntheticTouchscreenPinchGesture::ReleaseTouchPoints( SyntheticGestureTarget* target, const base::TimeTicks& timestamp) { synthetic_pointer_driver_->Release(0); synthetic_pointer_driver_->Release(1); synthetic_pointer_driver_->DispatchEvent(target, timestamp); } void SyntheticTouchscreenPinchGesture::SetupCoordinatesAndStopTime( SyntheticGestureTarget* target) { // To achieve the specified scaling factor, the ratio of the final to the // initial span (distance between the pointers) has to be equal to the scaling // factor. Since we're moving both pointers at the same speed, each pointer's // distance to the anchor is half the span. float initial_distance_to_anchor, final_distance_to_anchor; const float single_point_slop = target->GetSpanSlopInDips() / 2.0f; if (params_.scale_factor > 1.0f) { // zooming in initial_distance_to_anchor = target->GetMinScalingSpanInDips() / 2.0f; final_distance_to_anchor = (initial_distance_to_anchor + single_point_slop) * params_.scale_factor; } else { // zooming out final_distance_to_anchor = target->GetMinScalingSpanInDips() / 2.0f; initial_distance_to_anchor = (final_distance_to_anchor / params_.scale_factor) + single_point_slop; } start_y_0_ = params_.anchor.y() - initial_distance_to_anchor; start_y_1_ = params_.anchor.y() + initial_distance_to_anchor; max_pointer_delta_0_ = initial_distance_to_anchor - final_distance_to_anchor; int64_t total_duration_in_us = static_cast( 1e6 * (static_cast(std::abs(2 * max_pointer_delta_0_)) / params_.relative_pointer_speed_in_pixels_s)); DCHECK_GT(total_duration_in_us, 0); stop_time_ = start_time_ + base::TimeDelta::FromMicroseconds(total_duration_in_us); } float SyntheticTouchscreenPinchGesture::GetDeltaForPointer0AtTime( const base::TimeTicks& timestamp) const { // Make sure the final delta is correct. Using the computation below can lead // to issues with floating point precision. if (HasReachedTarget(timestamp)) return max_pointer_delta_0_; float total_abs_delta = params_.relative_pointer_speed_in_pixels_s * (timestamp - start_time_).InSecondsF(); float abs_delta_pointer_0 = total_abs_delta / 2.0f; return (params_.scale_factor > 1.0f) ? -abs_delta_pointer_0 : abs_delta_pointer_0; } base::TimeTicks SyntheticTouchscreenPinchGesture::ClampTimestamp( const base::TimeTicks& timestamp) const { return std::min(timestamp, stop_time_); } bool SyntheticTouchscreenPinchGesture::HasReachedTarget( const base::TimeTicks& timestamp) const { return timestamp >= stop_time_; } } // namespace content