| Index: remoting/host/touch_injector_win.cc
|
| diff --git a/remoting/host/touch_injector_win.cc b/remoting/host/touch_injector_win.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..3eaadf476ef1cefdb88dbcfe27acb7cd12552aec
|
| --- /dev/null
|
| +++ b/remoting/host/touch_injector_win.cc
|
| @@ -0,0 +1,285 @@
|
| +// 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 "remoting/host/touch_injector_win.h"
|
| +
|
| +#include "base/files/file_path.h"
|
| +#include "base/logging.h"
|
| +#include "base/native_library.h"
|
| +#include "base/stl_util.h"
|
| +#include "remoting/proto/event.pb.h"
|
| +
|
| +namespace remoting {
|
| +
|
| +using protocol::TouchEvent;
|
| +using protocol::TouchEventPoint;
|
| +
|
| +namespace {
|
| +
|
| +typedef BOOL(NTAPI* InitializeTouchInjectionFunction)(UINT32, DWORD);
|
| +typedef BOOL(NTAPI* InjectTouchInputFunction)(UINT32,
|
| + const POINTER_TOUCH_INFO*);
|
| +const uint32_t kMaxSimultaneousTouchCount = 10;
|
| +
|
| +// This is used to reinject all points that have not changed as "move"ed points,
|
| +// even if they have not actually moved.
|
| +// This is required for multi-touch to work, e.g. pinching and zooming gestures
|
| +// (handled by apps) won't work without reinjecting the points, even though the
|
| +// user moved only one finger and held the other finger in place.
|
| +void AppendMapValuesToVector(
|
| + std::map<uint32_t, POINTER_TOUCH_INFO>* touches_in_contact,
|
| + std::vector<POINTER_TOUCH_INFO>* output_vector) {
|
| + for (auto& id_and_pointer_touch_info : *touches_in_contact) {
|
| + POINTER_TOUCH_INFO& pointer_touch_info = id_and_pointer_touch_info.second;
|
| + output_vector->push_back(pointer_touch_info);
|
| + }
|
| +}
|
| +
|
| +// The caller should set memset(0) the struct and set
|
| +// pointer_touch_info->pointerInfo.pointerFlags.
|
| +void ConvertToPointerTouchInfo(
|
| + const TouchEventPoint& touch_point,
|
| + POINTER_TOUCH_INFO* pointer_touch_info) {
|
| + pointer_touch_info->touchMask =
|
| + TOUCH_MASK_CONTACTAREA | TOUCH_MASK_ORIENTATION;
|
| + pointer_touch_info->touchFlags = TOUCH_FLAG_NONE;
|
| +
|
| + // Although radius_{x,y} can be undefined (i.e. has_radius_{x,y} == false),
|
| + // the default value (0.0) will set the area correctly.
|
| + // MSDN mentions that if the digitizer does not detect the size of the touch
|
| + // point, rcContact should be set to 0 by 0 rectangle centered at the
|
| + // coordinate.
|
| + pointer_touch_info->rcContact.left =
|
| + touch_point.x() - touch_point.radius_x();
|
| + pointer_touch_info->rcContact.top = touch_point.y() - touch_point.radius_y();
|
| + pointer_touch_info->rcContact.right =
|
| + touch_point.x() + touch_point.radius_x();
|
| + pointer_touch_info->rcContact.bottom =
|
| + touch_point.y() + touch_point.radius_y();
|
| +
|
| + pointer_touch_info->orientation = touch_point.angle();
|
| +
|
| + if (touch_point.has_pressure()) {
|
| + pointer_touch_info->touchMask |= TOUCH_MASK_PRESSURE;
|
| + const float kMinimumPressure = 0.0;
|
| + const float kMaximumPressure = 1.0;
|
| + const float clamped_touch_point_pressure =
|
| + std::max(kMinimumPressure,
|
| + std::min(kMaximumPressure, touch_point.pressure()));
|
| +
|
| + const int kWindowsMaxTouchPressure = 1024; // Defined in MSDN.
|
| + const int pressure =
|
| + clamped_touch_point_pressure * kWindowsMaxTouchPressure;
|
| + pointer_touch_info->pressure = pressure;
|
| + }
|
| +
|
| + pointer_touch_info->pointerInfo.pointerType = PT_TOUCH;
|
| + pointer_touch_info->pointerInfo.pointerId = touch_point.id();
|
| + pointer_touch_info->pointerInfo.ptPixelLocation.x = touch_point.x();
|
| + pointer_touch_info->pointerInfo.ptPixelLocation.y = touch_point.y();
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +TouchInjectorWinDelegate::~TouchInjectorWinDelegate() {}
|
| +
|
| +// static.
|
| +scoped_ptr<TouchInjectorWinDelegate> TouchInjectorWinDelegate::Create() {
|
| + base::ScopedNativeLibrary library(base::FilePath(L"User32.dll"));
|
| + if (!library.is_valid()) {
|
| + PLOG(INFO) << "Failed to get library module for touch injection functions.";
|
| + return scoped_ptr<TouchInjectorWinDelegate>();
|
| + }
|
| +
|
| + InitializeTouchInjectionFunction init_func =
|
| + reinterpret_cast<InitializeTouchInjectionFunction>(
|
| + library.GetFunctionPointer("InitializeTouchInjection"));
|
| + if (!init_func) {
|
| + PLOG(INFO) << "Failed to get InitializeTouchInjection function handle.";
|
| + return scoped_ptr<TouchInjectorWinDelegate>();
|
| + }
|
| +
|
| + InjectTouchInputFunction inject_touch_func =
|
| + reinterpret_cast<InjectTouchInputFunction>(
|
| + library.GetFunctionPointer("InjectTouchInput"));
|
| + if (!inject_touch_func) {
|
| + PLOG(INFO) << "Failed to get InjectTouchInput.";
|
| + return scoped_ptr<TouchInjectorWinDelegate>();
|
| + }
|
| +
|
| + return scoped_ptr<TouchInjectorWinDelegate>(
|
| + new TouchInjectorWinDelegate(
|
| + library.Release(), init_func, inject_touch_func));
|
| +}
|
| +
|
| +TouchInjectorWinDelegate::TouchInjectorWinDelegate(
|
| + base::NativeLibrary library,
|
| + InitializeTouchInjectionFunction initialize_touch_injection_func,
|
| + InjectTouchInputFunction inject_touch_input_func)
|
| + : library_module_(library),
|
| + initialize_touch_injection_func_(initialize_touch_injection_func),
|
| + inject_touch_input_func_(inject_touch_input_func) {}
|
| +
|
| +BOOL TouchInjectorWinDelegate::InitializeTouchInjection(UINT32 max_count,
|
| + DWORD dw_mode) {
|
| + return initialize_touch_injection_func_(max_count, dw_mode);
|
| +}
|
| +
|
| +DWORD TouchInjectorWinDelegate::InjectTouchInput(
|
| + UINT32 count,
|
| + const POINTER_TOUCH_INFO* contacts) {
|
| + return inject_touch_input_func_(count, contacts);
|
| +}
|
| +
|
| +TouchInjectorWin::TouchInjectorWin()
|
| + : delegate_(TouchInjectorWinDelegate::Create()) {}
|
| +
|
| +TouchInjectorWin::~TouchInjectorWin() {}
|
| +
|
| +// Note that TouchInjectorWinDelegate::Create() is not called in this method
|
| +// so that a mock delegate can be injected in tests and set expectations on the
|
| +// mock and return value of this method.
|
| +bool TouchInjectorWin::Init() {
|
| + if (!delegate_)
|
| + return false;
|
| +
|
| + if (!delegate_->InitializeTouchInjection(
|
| + kMaxSimultaneousTouchCount, TOUCH_FEEDBACK_DEFAULT)) {
|
| + // delagate_ is reset here so that the function that need the delegate
|
| + // can check if it is null.
|
| + delegate_.reset();
|
| + PLOG(INFO) << "Failed to initialize touch injection.";
|
| + return false;
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| +void TouchInjectorWin::Deinitialize() {
|
| + touches_in_contact_.clear();
|
| + // Same reason as TouchInjectorWin::Init(). For injecting mock delegates for
|
| + // tests, a new delegate is created here.
|
| + delegate_ = TouchInjectorWinDelegate::Create();
|
| +}
|
| +
|
| +void TouchInjectorWin::InjectTouchEvent(const TouchEvent& event) {
|
| + if (!delegate_) {
|
| + VLOG(3) << "Touch injection functions are not initialized.";
|
| + return;
|
| + }
|
| +
|
| + switch (event.event_type()) {
|
| + case TouchEvent::TOUCH_POINT_START:
|
| + AddNewTouchPoints(event);
|
| + break;
|
| + case TouchEvent::TOUCH_POINT_MOVE:
|
| + MoveTouchPoints(event);
|
| + break;
|
| + case TouchEvent::TOUCH_POINT_END:
|
| + EndTouchPoints(event);
|
| + break;
|
| + case TouchEvent::TOUCH_POINT_CANCEL:
|
| + CancelTouchPoints(event);
|
| + break;
|
| + default:
|
| + NOTREACHED();
|
| + return;
|
| + }
|
| +}
|
| +
|
| +void TouchInjectorWin::SetInjectorDelegateForTest(
|
| + scoped_ptr<TouchInjectorWinDelegate> functions) {
|
| + delegate_ = functions.Pass();
|
| +}
|
| +
|
| +void TouchInjectorWin::AddNewTouchPoints(const TouchEvent& event) {
|
| + DCHECK_EQ(event.event_type(), TouchEvent::TOUCH_POINT_START);
|
| +
|
| + std::vector<POINTER_TOUCH_INFO> touches;
|
| + // Must inject already touching points as move events.
|
| + AppendMapValuesToVector(&touches_in_contact_, &touches);
|
| +
|
| + for (const TouchEventPoint& touch_point : event.touch_points()) {
|
| + POINTER_TOUCH_INFO pointer_touch_info;
|
| + memset(&pointer_touch_info, 0, sizeof(pointer_touch_info));
|
| + pointer_touch_info.pointerInfo.pointerFlags =
|
| + POINTER_FLAG_INRANGE | POINTER_FLAG_INCONTACT | POINTER_FLAG_DOWN;
|
| + ConvertToPointerTouchInfo(touch_point, &pointer_touch_info);
|
| + touches.push_back(pointer_touch_info);
|
| +
|
| + // All points in the map should be a move point.
|
| + pointer_touch_info.pointerInfo.pointerFlags =
|
| + POINTER_FLAG_INRANGE | POINTER_FLAG_INCONTACT | POINTER_FLAG_UPDATE;
|
| + touches_in_contact_[touch_point.id()] = pointer_touch_info;
|
| + }
|
| +
|
| + if (delegate_->InjectTouchInput(touches.size(),
|
| + vector_as_array(&touches)) == 0) {
|
| + PLOG(ERROR) << "Failed to inject a touch start event.";
|
| + }
|
| +}
|
| +
|
| +void TouchInjectorWin::MoveTouchPoints(const TouchEvent& event) {
|
| + DCHECK_EQ(event.event_type(), TouchEvent::TOUCH_POINT_MOVE);
|
| +
|
| + for (const TouchEventPoint& touch_point : event.touch_points()) {
|
| + POINTER_TOUCH_INFO* pointer_touch_info =
|
| + &touches_in_contact_[touch_point.id()];
|
| + memset(pointer_touch_info, 0, sizeof(*pointer_touch_info));
|
| + pointer_touch_info->pointerInfo.pointerFlags =
|
| + POINTER_FLAG_INRANGE | POINTER_FLAG_INCONTACT | POINTER_FLAG_UPDATE;
|
| + ConvertToPointerTouchInfo(touch_point, pointer_touch_info);
|
| + }
|
| +
|
| + std::vector<POINTER_TOUCH_INFO> touches;
|
| + // Must inject already touching points as move events.
|
| + AppendMapValuesToVector(&touches_in_contact_, &touches);
|
| + if (delegate_->InjectTouchInput(touches.size(),
|
| + vector_as_array(&touches)) == 0) {
|
| + PLOG(ERROR) << "Failed to inject a touch move event.";
|
| + }
|
| +}
|
| +
|
| +void TouchInjectorWin::EndTouchPoints(const TouchEvent& event) {
|
| + DCHECK_EQ(event.event_type(), TouchEvent::TOUCH_POINT_END);
|
| +
|
| + std::vector<POINTER_TOUCH_INFO> touches;
|
| + for (const TouchEventPoint& touch_point : event.touch_points()) {
|
| + POINTER_TOUCH_INFO pointer_touch_info =
|
| + touches_in_contact_[touch_point.id()];
|
| + pointer_touch_info.pointerInfo.pointerFlags = POINTER_FLAG_UP;
|
| +
|
| + touches_in_contact_.erase(touch_point.id());
|
| + touches.push_back(pointer_touch_info);
|
| + }
|
| +
|
| + AppendMapValuesToVector(&touches_in_contact_, &touches);
|
| + if (delegate_->InjectTouchInput(touches.size(),
|
| + vector_as_array(&touches)) == 0) {
|
| + PLOG(ERROR) << "Failed to inject a touch end event.";
|
| + }
|
| +}
|
| +
|
| +void TouchInjectorWin::CancelTouchPoints(const TouchEvent& event) {
|
| + DCHECK_EQ(event.event_type(), TouchEvent::TOUCH_POINT_CANCEL);
|
| +
|
| + std::vector<POINTER_TOUCH_INFO> touches;
|
| + for (const TouchEventPoint& touch_point : event.touch_points()) {
|
| + POINTER_TOUCH_INFO pointer_touch_info =
|
| + touches_in_contact_[touch_point.id()];
|
| + pointer_touch_info.pointerInfo.pointerFlags =
|
| + POINTER_FLAG_UP | POINTER_FLAG_CANCELED;
|
| +
|
| + touches_in_contact_.erase(touch_point.id());
|
| + touches.push_back(pointer_touch_info);
|
| + }
|
| +
|
| + AppendMapValuesToVector(&touches_in_contact_, &touches);
|
| + if (delegate_->InjectTouchInput(touches.size(),
|
| + vector_as_array(&touches)) == 0) {
|
| + PLOG(ERROR) << "Failed to inject a touch cancel event.";
|
| + }
|
| +}
|
| +
|
| +} // namespace remoting
|
|
|