blob: 1fb5e432c364d72ade69111d52c16b574bdfde81 [file] [log] [blame]
// Copyright 2015 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 "components/exo/touch.h"
#include "components/exo/shell_surface_util.h"
#include "components/exo/surface.h"
#include "components/exo/touch_delegate.h"
#include "components/exo/touch_stylus_delegate.h"
#include "components/exo/wm_helper.h"
#include "ui/aura/window.h"
#include "ui/events/event.h"
#include "ui/wm/core/capture_controller.h"
#include "ui/wm/core/window_util.h"
namespace exo {
namespace {
// Helper function that returns an iterator to the first item in |vector|
// with |value|.
template <typename T, typename U>
typename T::iterator FindVectorItem(T& vector, U value) {
return std::find(vector.begin(), vector.end(), value);
}
// Helper function that returns true if |vector| contains an item with |value|.
template <typename T, typename U>
bool VectorContainsItem(T& vector, U value) {
return FindVectorItem(vector, value) != vector.end();
}
gfx::PointF EventLocationInWindow(ui::TouchEvent* event, aura::Window* window) {
ui::Layer* root = window->GetRootWindow()->layer();
ui::Layer* target = window->layer();
gfx::Transform transform;
target->GetTargetTransformRelativeTo(root, &transform);
auto point = gfx::Point3F(event->root_location_f());
transform.TransformPointReverse(&point);
return point.AsPointF();
}
} // namespace
////////////////////////////////////////////////////////////////////////////////
// Touch, public:
Touch::Touch(TouchDelegate* delegate) : delegate_(delegate) {
WMHelper::GetInstance()->AddPreTargetHandler(this);
}
Touch::~Touch() {
delegate_->OnTouchDestroying(this);
if (focus_)
focus_->RemoveSurfaceObserver(this);
WMHelper::GetInstance()->RemovePreTargetHandler(this);
}
void Touch::SetStylusDelegate(TouchStylusDelegate* delegate) {
stylus_delegate_ = delegate;
}
bool Touch::HasStylusDelegate() const {
return !!stylus_delegate_;
}
////////////////////////////////////////////////////////////////////////////////
// ui::EventHandler overrides:
void Touch::OnTouchEvent(ui::TouchEvent* event) {
bool send_details = false;
const int touch_pointer_id = event->pointer_details().id;
switch (event->type()) {
case ui::ET_TOUCH_PRESSED: {
// Early out if event doesn't contain a valid target for touch device.
Surface* target = GetEffectiveTargetForEvent(event);
if (!target)
return;
// If this is the first touch point then target becomes the focus surface
// until all touch points have been released.
if (touch_points_.empty()) {
DCHECK(!focus_);
focus_ = target;
focus_->AddSurfaceObserver(this);
}
DCHECK(!VectorContainsItem(touch_points_, touch_pointer_id));
touch_points_.push_back(touch_pointer_id);
// Convert location to focus surface coordinate space.
DCHECK(focus_);
gfx::PointF location = EventLocationInWindow(event, focus_->window());
// Generate a touch down event for the focus surface. Note that this can
// be different from the target surface.
delegate_->OnTouchDown(focus_, event->time_stamp(), touch_pointer_id,
location);
if (stylus_delegate_ &&
event->pointer_details().pointer_type !=
ui::EventPointerType::POINTER_TYPE_TOUCH) {
stylus_delegate_->OnTouchTool(touch_pointer_id,
event->pointer_details().pointer_type);
}
send_details = true;
} break;
case ui::ET_TOUCH_RELEASED: {
auto it = FindVectorItem(touch_points_, touch_pointer_id);
if (it == touch_points_.end())
return;
touch_points_.erase(it);
// Reset focus surface if this is the last touch point.
if (touch_points_.empty()) {
DCHECK(focus_);
focus_->RemoveSurfaceObserver(this);
focus_ = nullptr;
}
delegate_->OnTouchUp(event->time_stamp(), touch_pointer_id);
} break;
case ui::ET_TOUCH_MOVED: {
auto it = FindVectorItem(touch_points_, touch_pointer_id);
if (it == touch_points_.end())
return;
DCHECK(focus_);
// Convert location to focus surface coordinate space.
gfx::PointF location = EventLocationInWindow(event, focus_->window());
delegate_->OnTouchMotion(event->time_stamp(), touch_pointer_id, location);
send_details = true;
} break;
case ui::ET_TOUCH_CANCELLED: {
auto it = FindVectorItem(touch_points_, touch_pointer_id);
if (it == touch_points_.end())
return;
DCHECK(focus_);
focus_->RemoveSurfaceObserver(this);
focus_ = nullptr;
// Cancel the full set of touch sequences as soon as one is canceled.
touch_points_.clear();
delegate_->OnTouchCancel();
} break;
default:
NOTREACHED();
return;
}
if (send_details) {
// Some devices do not report radius_y/minor. We assume a circular shape
// in that case.
float major = event->pointer_details().radius_x * 2.0f;
float minor = event->pointer_details().radius_y * 2.0f;
if (!minor)
minor = major;
delegate_->OnTouchShape(touch_pointer_id, major, minor);
if (stylus_delegate_ &&
event->pointer_details().pointer_type !=
ui::EventPointerType::POINTER_TYPE_TOUCH) {
if (!std::isnan(event->pointer_details().force)) {
stylus_delegate_->OnTouchForce(event->time_stamp(), touch_pointer_id,
event->pointer_details().force);
}
stylus_delegate_->OnTouchTilt(
event->time_stamp(), touch_pointer_id,
gfx::Vector2dF(event->pointer_details().tilt_x,
event->pointer_details().tilt_y));
}
}
// TODO(denniskempin): Extend ui::TouchEvent to signal end of sequence of
// touch events to send TouchFrame once after all touches have been updated.
delegate_->OnTouchFrame();
}
////////////////////////////////////////////////////////////////////////////////
// SurfaceObserver overrides:
void Touch::OnSurfaceDestroying(Surface* surface) {
DCHECK(surface == focus_);
focus_ = nullptr;
surface->RemoveSurfaceObserver(this);
// Cancel touch sequences.
DCHECK_NE(touch_points_.size(), 0u);
touch_points_.clear();
delegate_->OnTouchCancel();
}
////////////////////////////////////////////////////////////////////////////////
// Touch, private:
Surface* Touch::GetEffectiveTargetForEvent(ui::LocatedEvent* event) const {
Surface* target = GetTargetSurfaceForLocatedEvent(event);
if (!target)
return nullptr;
return delegate_->CanAcceptTouchEventsForSurface(target) ? target : nullptr;
}
} // namespace exo