// 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 "base/trace_event/trace_event.h" #include "components/exo/input_trace.h" #include "components/exo/seat.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/compositor/layer.h" #include "ui/events/event.h" #include "ui/wm/core/capture_controller.h" #include "ui/wm/core/window_util.h" namespace exo { namespace { 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, Seat* seat) : delegate_(delegate), seat_(seat) { WMHelper::GetInstance()->AddPreTargetHandler(this); } Touch::~Touch() { WMHelper::GetInstance()->RemovePreTargetHandler(this); delegate_->OnTouchDestroying(this); if (HasStylusDelegate()) stylus_delegate_->OnTouchDestroying(this); CancelAllTouches(); } void Touch::SetStylusDelegate(TouchStylusDelegate* delegate) { stylus_delegate_ = delegate; } bool Touch::HasStylusDelegate() const { return !!stylus_delegate_; } //////////////////////////////////////////////////////////////////////////////// // ui::EventHandler overrides: void Touch::OnTouchEvent(ui::TouchEvent* event) { if (seat_->was_shutdown() || event->handled()) return; 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. // TODO(b/147848270): Verify GetEffectiveTargetForEvent gets the correct // surface when input is captured. Surface* target = GetEffectiveTargetForEvent(event); if (!target) return; TRACE_EXO_INPUT_EVENT(event); DCHECK(touch_points_surface_map_.find(touch_pointer_id) == touch_points_surface_map_.end()); touch_points_surface_map_.emplace(touch_pointer_id, target); // Update the count of pointers on the target surface. auto it = surface_touch_count_map_.find(target); if (it == surface_touch_count_map_.end()) { target->AddSurfaceObserver(this); surface_touch_count_map_.emplace(target, 1); } else { it->second++; } // Convert location to target surface coordinate space. const gfx::PointF location = EventLocationInWindow(event, target->window()); // Generate a touch down event for the target surface. delegate_->OnTouchDown(target, event->time_stamp(), touch_pointer_id, location); if (stylus_delegate_ && event->pointer_details().pointer_type != ui::EventPointerType::kTouch) { stylus_delegate_->OnTouchTool(touch_pointer_id, event->pointer_details().pointer_type); } send_details = true; } break; case ui::ET_TOUCH_RELEASED: { auto it = touch_points_surface_map_.find(touch_pointer_id); if (it == touch_points_surface_map_.end()) return; Surface* target = it->second; DCHECK(target); TRACE_EXO_INPUT_EVENT(event); touch_points_surface_map_.erase(it); // Update the count of pointers on the target surface. auto count_it = surface_touch_count_map_.find(target); if (count_it == surface_touch_count_map_.end()) return; if ((--count_it->second) <= 0) { surface_touch_count_map_.erase(target); target->RemoveSurfaceObserver(this); } delegate_->OnTouchUp(event->time_stamp(), touch_pointer_id); seat_->AbortPendingDragOperation(); } break; case ui::ET_TOUCH_MOVED: { auto it = touch_points_surface_map_.find(touch_pointer_id); if (it == touch_points_surface_map_.end()) return; Surface* target = it->second; DCHECK(target); TRACE_EXO_INPUT_EVENT(event); // Convert location to focus surface coordinate space. gfx::PointF location = EventLocationInWindow(event, target->window()); delegate_->OnTouchMotion(event->time_stamp(), touch_pointer_id, location); send_details = true; } break; case ui::ET_TOUCH_CANCELLED: { TRACE_EXO_INPUT_EVENT(event); // Cancel the full set of touch sequences as soon as one is canceled. CancelAllTouches(); delegate_->OnTouchCancel(); seat_->AbortPendingDragOperation(); } 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::kTouch) { 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) { // TODO(b/147848407): Do not cancel touches on surfaces of different clients // when this surface dies. CancelAllTouches(); 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; } void Touch::CancelAllTouches() { std::for_each(surface_touch_count_map_.begin(), surface_touch_count_map_.end(), [this](auto& it) { it.first->RemoveSurfaceObserver(this); }); touch_points_surface_map_.clear(); surface_touch_count_map_.clear(); } } // namespace exo