Commit 883da7ed authored by rkuroiwa's avatar rkuroiwa Committed by Commit bot

Windows Host Touch Injection

- Update SessionInputInjectorWin and InputInjectorWin to
  handle touch events.
- Add TouchInjectorWin which converts TouchEvents to
  structs for Windows touch injection API.

BUG=314515

Review URL: https://codereview.chromium.org/991643002

Cr-Commit-Position: refs/heads/master@{#322116}
parent b4838b40
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"
#include "remoting/base/util.h" #include "remoting/base/util.h"
#include "remoting/host/clipboard.h" #include "remoting/host/clipboard.h"
#include "remoting/host/touch_injector_win.h"
#include "remoting/proto/event.pb.h" #include "remoting/proto/event.pb.h"
#include "ui/events/keycodes/dom4/keycode_converter.h" #include "ui/events/keycodes/dom4/keycode_converter.h"
...@@ -87,6 +88,7 @@ class InputInjectorWin : public InputInjector { ...@@ -87,6 +88,7 @@ class InputInjectorWin : public InputInjector {
void InjectKeyEvent(const KeyEvent& event); void InjectKeyEvent(const KeyEvent& event);
void InjectTextEvent(const TextEvent& event); void InjectTextEvent(const TextEvent& event);
void InjectMouseEvent(const MouseEvent& event); void InjectMouseEvent(const MouseEvent& event);
void InjectTouchEvent(const TouchEvent& event);
// Mirrors the InputInjector interface. // Mirrors the InputInjector interface.
void Start(scoped_ptr<protocol::ClipboardStub> client_clipboard); void Start(scoped_ptr<protocol::ClipboardStub> client_clipboard);
...@@ -100,10 +102,12 @@ class InputInjectorWin : public InputInjector { ...@@ -100,10 +102,12 @@ class InputInjectorWin : public InputInjector {
void HandleKey(const KeyEvent& event); void HandleKey(const KeyEvent& event);
void HandleText(const TextEvent& event); void HandleText(const TextEvent& event);
void HandleMouse(const MouseEvent& event); void HandleMouse(const MouseEvent& event);
void HandleTouch(const TouchEvent& event);
scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_; scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_;
scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_; scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner_;
scoped_ptr<Clipboard> clipboard_; scoped_ptr<Clipboard> clipboard_;
TouchInjectorWin touch_injector_;
DISALLOW_COPY_AND_ASSIGN(Core); DISALLOW_COPY_AND_ASSIGN(Core);
}; };
...@@ -140,7 +144,7 @@ void InputInjectorWin::InjectMouseEvent(const MouseEvent& event) { ...@@ -140,7 +144,7 @@ void InputInjectorWin::InjectMouseEvent(const MouseEvent& event) {
} }
void InputInjectorWin::InjectTouchEvent(const TouchEvent& event) { void InputInjectorWin::InjectTouchEvent(const TouchEvent& event) {
NOTIMPLEMENTED() << "Raw touch event injection not implemented for Windows."; core_->InjectTouchEvent(event);
} }
void InputInjectorWin::Start( void InputInjectorWin::Start(
...@@ -197,6 +201,16 @@ void InputInjectorWin::Core::InjectMouseEvent(const MouseEvent& event) { ...@@ -197,6 +201,16 @@ void InputInjectorWin::Core::InjectMouseEvent(const MouseEvent& event) {
HandleMouse(event); HandleMouse(event);
} }
void InputInjectorWin::Core::InjectTouchEvent(const TouchEvent& event) {
if (!main_task_runner_->BelongsToCurrentThread()) {
main_task_runner_->PostTask(
FROM_HERE, base::Bind(&Core::InjectTouchEvent, this, event));
return;
}
HandleTouch(event);
}
void InputInjectorWin::Core::Start( void InputInjectorWin::Core::Start(
scoped_ptr<protocol::ClipboardStub> client_clipboard) { scoped_ptr<protocol::ClipboardStub> client_clipboard) {
if (!ui_task_runner_->BelongsToCurrentThread()) { if (!ui_task_runner_->BelongsToCurrentThread()) {
...@@ -207,6 +221,7 @@ void InputInjectorWin::Core::Start( ...@@ -207,6 +221,7 @@ void InputInjectorWin::Core::Start(
} }
clipboard_->Start(client_clipboard.Pass()); clipboard_->Start(client_clipboard.Pass());
touch_injector_.Init();
} }
void InputInjectorWin::Core::Stop() { void InputInjectorWin::Core::Stop() {
...@@ -216,6 +231,7 @@ void InputInjectorWin::Core::Stop() { ...@@ -216,6 +231,7 @@ void InputInjectorWin::Core::Stop() {
} }
clipboard_.reset(); clipboard_.reset();
touch_injector_.Deinitialize();
} }
InputInjectorWin::Core::~Core() {} InputInjectorWin::Core::~Core() {}
...@@ -326,6 +342,10 @@ void InputInjectorWin::Core::HandleMouse(const MouseEvent& event) { ...@@ -326,6 +342,10 @@ void InputInjectorWin::Core::HandleMouse(const MouseEvent& event) {
} }
} }
void InputInjectorWin::Core::HandleTouch(const TouchEvent& event) {
touch_injector_.InjectTouchEvent(event);
}
} // namespace } // namespace
scoped_ptr<InputInjector> InputInjector::Create( scoped_ptr<InputInjector> InputInjector::Create(
......
// 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
// 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.
#ifndef REMOTING_HOST_TOUCH_INJECTOR_WIN_H_
#define REMOTING_HOST_TOUCH_INJECTOR_WIN_H_
#include <windows.h>
#include <map>
#include <vector>
#include "base/memory/scoped_ptr.h"
#include "base/scoped_native_library.h"
namespace remoting {
namespace protocol {
class TouchEvent;
} // namespace protocol
// This class calls InitializeTouchInjection() and InjectTouchInput() functions.
// The methods are virtual for mocking.
class TouchInjectorWinDelegate {
public:
virtual ~TouchInjectorWinDelegate();
// Determines whether Windows touch injection functions can be used.
// Returns a non-null TouchInjectorWinDelegate on success.
static scoped_ptr<TouchInjectorWinDelegate> Create();
// These match the functions in MSDN.
virtual BOOL InitializeTouchInjection(UINT32 max_count, DWORD dw_mode);
virtual DWORD InjectTouchInput(UINT32 count,
const POINTER_TOUCH_INFO* contacts);
protected:
// Ctor in protected scope for mocking.
// This object takes ownership of the |library|.
TouchInjectorWinDelegate(
base::NativeLibrary library,
BOOL(NTAPI* initialize_touch_injection_func)(UINT32, DWORD),
BOOL(NTAPI* inject_touch_input_func)(UINT32, const POINTER_TOUCH_INFO*));
private:
base::ScopedNativeLibrary library_module_;
// Pointers to Windows touch injection functions.
BOOL(NTAPI* initialize_touch_injection_func_)(UINT32, DWORD);
BOOL(NTAPI* inject_touch_input_func_)(UINT32, const POINTER_TOUCH_INFO*);
DISALLOW_COPY_AND_ASSIGN(TouchInjectorWinDelegate);
};
// This class converts TouchEvent objects to POINTER_TOUCH_INFO so that it can
// be injected using the Windows touch injection API, and calls the injection
// functions.
// This class expects good inputs and does not sanity check the inputs.
// This class just converts the object and hands it off to the Windows API.
class TouchInjectorWin {
public:
TouchInjectorWin();
~TouchInjectorWin();
// Returns false if initialization of touch injection APIs fails.
bool Init();
// Deinitializes the object so that it can be reinitialized.
void Deinitialize();
// Inject touch events.
void InjectTouchEvent(const protocol::TouchEvent& event);
void SetInjectorDelegateForTest(
scoped_ptr<TouchInjectorWinDelegate> functions);
private:
// Helper methods called from InjectTouchEvent().
// These helpers adapt Chromoting touch events, which convey changes to touch
// points, to Windows touch descriptions, which must include descriptions for
// all currently-active touch points, not just the changed ones.
void AddNewTouchPoints(const protocol::TouchEvent& event);
void MoveTouchPoints(const protocol::TouchEvent& event);
void EndTouchPoints(const protocol::TouchEvent& event);
void CancelTouchPoints(const protocol::TouchEvent& event);
// Set to null if touch injection is not available from the OS.
scoped_ptr<TouchInjectorWinDelegate> delegate_;
// TODO(rkuroiwa): crbug.com/470203
// This is a naive implementation. Check if we can achieve
// better performance by reducing the number of copies.
// To reduce the number of copies, we can have a vector of
// POINTER_TOUCH_INFO and a map from touch ID to index in the vector.
// When removing points from the vector, just swap it with the last element
// and resize the vector.
// All the POINTER_TOUCH_INFOs are stored as "move" points.
std::map<uint32_t, POINTER_TOUCH_INFO> touches_in_contact_;
DISALLOW_COPY_AND_ASSIGN(TouchInjectorWin);
};
} // namespace remoting
#endif // REMOTING_HOST_TOUCH_INJECTOR_WIN_H_
// 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 <map>
#include "base/stl_util.h"
#include "remoting/proto/event.pb.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using ::testing::_;
using ::testing::AtLeast;
using ::testing::InSequence;
using ::testing::ExpectationSet;
using ::testing::Return;
namespace remoting {
using protocol::TouchEvent;
using protocol::TouchEventPoint;
namespace {
// Maps touch pointer ID to expected flags [start, move, end, cancel] listed
// below.
typedef std::map<uint32_t, uint32_t> IdFlagMap;
const uint32_t kStartFlag =
POINTER_FLAG_INRANGE | POINTER_FLAG_INCONTACT | POINTER_FLAG_DOWN;
const uint32_t kMoveFlag =
POINTER_FLAG_INRANGE | POINTER_FLAG_INCONTACT | POINTER_FLAG_UPDATE;
const uint32_t kEndFlag = POINTER_FLAG_UP;
const uint32_t kCancelFlag = POINTER_FLAG_UP | POINTER_FLAG_CANCELED;
MATCHER_P(EqualsSinglePointerTouchInfo, expected, "") {
return arg->touchMask == expected.touchMask &&
arg->rcContact.left == expected.rcContact.left &&
arg->rcContact.top == expected.rcContact.top &&
arg->rcContact.right == expected.rcContact.right &&
arg->rcContact.bottom == expected.rcContact.bottom &&
arg->orientation == expected.orientation &&
arg->pressure == expected.pressure &&
arg->pointerInfo.pointerType == expected.pointerInfo.pointerType &&
arg->pointerInfo.pointerId == expected.pointerInfo.pointerId &&
arg->pointerInfo.ptPixelLocation.x ==
expected.pointerInfo.ptPixelLocation.x &&
arg->pointerInfo.ptPixelLocation.y ==
expected.pointerInfo.ptPixelLocation.y;
}
// Make sure that every touch point has the right flag (pointerFlags).
MATCHER_P(EqualsPointerTouchInfoFlag, id_to_flag_map, "") {
for (size_t i = 0; i < id_to_flag_map.size(); ++i) {
const POINTER_TOUCH_INFO* touch_info = arg + i;
const uint32_t id = touch_info->pointerInfo.pointerId;
if (!ContainsKey(id_to_flag_map, id))
return false;
if (id_to_flag_map.find(id)->second != touch_info->pointerInfo.pointerFlags)
return false;
}
return true;
}
class TouchInjectorWinDelegateMock : public TouchInjectorWinDelegate {
public:
TouchInjectorWinDelegateMock()
: TouchInjectorWinDelegate(nullptr, nullptr, nullptr) {}
~TouchInjectorWinDelegateMock() override {};
MOCK_METHOD2(InitializeTouchInjection, BOOL(UINT32 max_count, DWORD dw_mode));
MOCK_METHOD2(InjectTouchInput,
DWORD(UINT32 count, const POINTER_TOUCH_INFO* contacts));
};
} // namespace
// A test to make sure that the touch event is converted correctly to
// POINTER_TOUCH_INFO.
TEST(TouchInjectorWinTest, CheckConversionWithPressure) {
scoped_ptr<TouchInjectorWinDelegateMock> delegate_mock(
new ::testing::StrictMock<TouchInjectorWinDelegateMock>());
TouchEvent event;
event.set_event_type(TouchEvent::TOUCH_POINT_START);
TouchEventPoint* point = event.add_touch_points();
point->set_id(1234u);
point->set_x(321.0f);
point->set_y(123.0f);
point->set_radius_x(10.0f);
point->set_radius_y(20.0f);
point->set_pressure(0.5f);
point->set_angle(45.0f);
POINTER_TOUCH_INFO expected_touch_info;
expected_touch_info.touchMask =
TOUCH_MASK_CONTACTAREA | TOUCH_MASK_ORIENTATION | TOUCH_MASK_PRESSURE;
expected_touch_info.rcContact.left = 311;
expected_touch_info.rcContact.top = 103;
expected_touch_info.rcContact.right = 331;
expected_touch_info.rcContact.bottom = 143;
expected_touch_info.orientation = 0;
expected_touch_info.pressure = 512;
expected_touch_info.orientation = 45;
expected_touch_info.pointerInfo.pointerType = PT_TOUCH;
expected_touch_info.pointerInfo.pointerId = 1234u;
expected_touch_info.pointerInfo.ptPixelLocation.x = 321;
expected_touch_info.pointerInfo.ptPixelLocation.y = 123;
InSequence s;
EXPECT_CALL(*delegate_mock, InitializeTouchInjection(_, _))
.WillOnce(Return(1));
EXPECT_CALL(
*delegate_mock,
InjectTouchInput(1, EqualsSinglePointerTouchInfo(expected_touch_info)))
.WillOnce(Return(1));
// Check pressure clamping as well.
expected_touch_info.pressure = 1024; // Max
EXPECT_CALL(
*delegate_mock,
InjectTouchInput(1, EqualsSinglePointerTouchInfo(expected_touch_info)))
.WillOnce(Return(1));
expected_touch_info.pressure = 0; // Min
EXPECT_CALL(
*delegate_mock,
InjectTouchInput(1, EqualsSinglePointerTouchInfo(expected_touch_info)))
.WillOnce(Return(1));
TouchInjectorWin injector;
injector.SetInjectorDelegateForTest(delegate_mock.Pass());
EXPECT_TRUE(injector.Init());
injector.InjectTouchEvent(event);
// Change to MOVE so that there still only one point.
event.set_event_type(TouchEvent::TOUCH_POINT_MOVE);
point->set_pressure(2.0f);
injector.InjectTouchEvent(event);
point->set_pressure(-3.0f);
injector.InjectTouchEvent(event);
}
// Some devices don't detect pressure. This test is a conversion check for
// such devices.
TEST(TouchInjectorWinTest, CheckConversionNoPressure) {
scoped_ptr<TouchInjectorWinDelegateMock> delegate_mock(
new ::testing::StrictMock<TouchInjectorWinDelegateMock>());
TouchEvent event;
event.set_event_type(TouchEvent::TOUCH_POINT_START);
TouchEventPoint* point = event.add_touch_points();
point->set_id(1234u);
point->set_x(321.0f);
point->set_y(123.0f);
point->set_radius_x(10.0f);
point->set_radius_y(20.0f);
point->set_angle(45.0f);
POINTER_TOUCH_INFO expected_touch_info;
expected_touch_info.touchMask =
TOUCH_MASK_CONTACTAREA | TOUCH_MASK_ORIENTATION;
expected_touch_info.rcContact.left = 311;
expected_touch_info.rcContact.top = 103;
expected_touch_info.rcContact.right = 331;
expected_touch_info.rcContact.bottom = 143;
expected_touch_info.orientation = 0;
expected_touch_info.pressure = 0;
expected_touch_info.orientation = 45;
expected_touch_info.pointerInfo.pointerType = PT_TOUCH;
expected_touch_info.pointerInfo.pointerId = 1234u;
expected_touch_info.pointerInfo.ptPixelLocation.x = 321;
expected_touch_info.pointerInfo.ptPixelLocation.y = 123;
InSequence s;
EXPECT_CALL(*delegate_mock, InitializeTouchInjection(_, _))
.WillOnce(Return(1));
EXPECT_CALL(
*delegate_mock,
InjectTouchInput(1, EqualsSinglePointerTouchInfo(expected_touch_info)))
.WillOnce(Return(1));
TouchInjectorWin injector;
injector.SetInjectorDelegateForTest(delegate_mock.Pass());
EXPECT_TRUE(injector.Init());
injector.InjectTouchEvent(event);
}
// If initialization fails, it should not call any touch injection functions.
TEST(TouchInjectorWinTest, InitFailed) {
scoped_ptr<TouchInjectorWinDelegateMock> delegate_mock(
new ::testing::StrictMock<TouchInjectorWinDelegateMock>());
TouchEvent event;
event.set_event_type(TouchEvent::TOUCH_POINT_START);
InSequence s;
EXPECT_CALL(*delegate_mock, InitializeTouchInjection(_, _))
.WillOnce(Return(0));
EXPECT_CALL(*delegate_mock, InjectTouchInput(_, _)).Times(0);
TouchInjectorWin injector;
injector.SetInjectorDelegateForTest(delegate_mock.Pass());
EXPECT_FALSE(injector.Init());
injector.InjectTouchEvent(event);
}
// Deinitialize and initialize should clean the state.
TEST(TouchInjectorWinTest, Reinitialize) {
scoped_ptr<TouchInjectorWinDelegateMock> delegate_mock_before_deinitialize(
new ::testing::StrictMock<TouchInjectorWinDelegateMock>());
scoped_ptr<TouchInjectorWinDelegateMock> delegate_mock_after_deinitialize(
new ::testing::StrictMock<TouchInjectorWinDelegateMock>());
TouchEvent first_event;
first_event.set_event_type(TouchEvent::TOUCH_POINT_START);
TouchEventPoint* point0 = first_event.add_touch_points();
point0->set_id(0u);
TouchEvent second_event;
second_event.set_event_type(TouchEvent::TOUCH_POINT_START);
TouchEventPoint* point1 = second_event.add_touch_points();
point1->set_id(1u);
InSequence s;
EXPECT_CALL(*delegate_mock_before_deinitialize,
InitializeTouchInjection(_, _)).WillOnce(Return(1));
IdFlagMap id_to_flags;
id_to_flags[0u] = kStartFlag;
EXPECT_CALL(
*delegate_mock_before_deinitialize,
InjectTouchInput(1, EqualsPointerTouchInfoFlag(id_to_flags)))
.WillOnce(Return(1));
EXPECT_CALL(*delegate_mock_after_deinitialize,
InitializeTouchInjection(_, _)).WillOnce(Return(1));
// After deinitializing and then initializing, previous touch points should be
// gone.
id_to_flags.clear();
id_to_flags[1u] = kStartFlag;
EXPECT_CALL(
*delegate_mock_after_deinitialize,
InjectTouchInput(1, EqualsPointerTouchInfoFlag(id_to_flags)))
.WillOnce(Return(1));
TouchInjectorWin injector;
injector.SetInjectorDelegateForTest(delegate_mock_before_deinitialize.Pass());
EXPECT_TRUE(injector.Init());
injector.InjectTouchEvent(first_event);
injector.Deinitialize();
injector.SetInjectorDelegateForTest(delegate_mock_after_deinitialize.Pass());
EXPECT_TRUE(injector.Init());
injector.InjectTouchEvent(second_event);
}
// Make sure that the flag is set to kStartFlag.
TEST(TouchInjectorWinTest, StartTouchPoint) {
scoped_ptr<TouchInjectorWinDelegateMock> delegate_mock(
new ::testing::StrictMock<TouchInjectorWinDelegateMock>());
TouchEvent event;
event.set_event_type(TouchEvent::TOUCH_POINT_START);
TouchEventPoint* point = event.add_touch_points();
point->set_id(0u);
InSequence s;
EXPECT_CALL(*delegate_mock, InitializeTouchInjection(_, _))
.WillOnce(Return(1));
IdFlagMap id_to_flags;
id_to_flags[0u] = kStartFlag;
EXPECT_CALL(
*delegate_mock,
InjectTouchInput(1, EqualsPointerTouchInfoFlag(id_to_flags)))
.WillOnce(Return(1));
TouchInjectorWin injector;
injector.SetInjectorDelegateForTest(delegate_mock.Pass());
EXPECT_TRUE(injector.Init());
injector.InjectTouchEvent(event);
}
// Start a point and then move, make sure the flag is set to kMoveFlag.
TEST(TouchInjectorWinTest, MoveTouchPoint) {
scoped_ptr<TouchInjectorWinDelegateMock> delegate_mock(
new ::testing::StrictMock<TouchInjectorWinDelegateMock>());
TouchEvent event;
event.set_event_type(TouchEvent::TOUCH_POINT_START);
TouchEventPoint* point = event.add_touch_points();
point->set_id(0u);
InSequence s;
EXPECT_CALL(*delegate_mock, InitializeTouchInjection(_, _))
.WillOnce(Return(1));
IdFlagMap id_to_flags;
id_to_flags[0u] = kStartFlag;
EXPECT_CALL(
*delegate_mock,
InjectTouchInput(1, EqualsPointerTouchInfoFlag(id_to_flags)))
.WillOnce(Return(1));
id_to_flags[0u] = kMoveFlag;
EXPECT_CALL(
*delegate_mock,
InjectTouchInput(1, EqualsPointerTouchInfoFlag(id_to_flags)))
.WillOnce(Return(1));
TouchInjectorWin injector;
injector.SetInjectorDelegateForTest(delegate_mock.Pass());
EXPECT_TRUE(injector.Init());
injector.InjectTouchEvent(event);
event.set_event_type(TouchEvent::TOUCH_POINT_MOVE);
injector.InjectTouchEvent(event);
}
// Start a point and then move, make sure the flag is set to kEndFlag.
TEST(TouchInjectorWinTest, EndTouchPoint) {
scoped_ptr<TouchInjectorWinDelegateMock> delegate_mock(
new ::testing::StrictMock<TouchInjectorWinDelegateMock>());
TouchEvent event;
event.set_event_type(TouchEvent::TOUCH_POINT_START);
TouchEventPoint* point = event.add_touch_points();
point->set_id(0u);
InSequence s;
EXPECT_CALL(*delegate_mock, InitializeTouchInjection(_, _))
.WillOnce(Return(1));
IdFlagMap id_to_flags;
id_to_flags[0u] = kStartFlag;
EXPECT_CALL(
*delegate_mock,
InjectTouchInput(1, EqualsPointerTouchInfoFlag(id_to_flags)))
.WillOnce(Return(1));
id_to_flags[0u] = kEndFlag;
EXPECT_CALL(
*delegate_mock,
InjectTouchInput(1, EqualsPointerTouchInfoFlag(id_to_flags)))
.WillOnce(Return(1));
TouchInjectorWin injector;
injector.SetInjectorDelegateForTest(delegate_mock.Pass());
EXPECT_TRUE(injector.Init());
injector.InjectTouchEvent(event);
event.set_event_type(TouchEvent::TOUCH_POINT_END);
injector.InjectTouchEvent(event);
}
// Start a point and then move, make sure the flag is set to kCancelFlag.
TEST(TouchInjectorWinTest, CancelTouchPoint) {
scoped_ptr<TouchInjectorWinDelegateMock> delegate_mock(
new ::testing::StrictMock<TouchInjectorWinDelegateMock>());
TouchEvent event;
event.set_event_type(TouchEvent::TOUCH_POINT_START);
TouchEventPoint* point = event.add_touch_points();
point->set_id(0u);
InSequence s;
EXPECT_CALL(*delegate_mock, InitializeTouchInjection(_, _))
.WillOnce(Return(1));
IdFlagMap id_to_flags;
id_to_flags[0u] = kStartFlag;
EXPECT_CALL(
*delegate_mock,
InjectTouchInput(1, EqualsPointerTouchInfoFlag(id_to_flags)))
.WillOnce(Return(1));
id_to_flags[0u] = kCancelFlag;
EXPECT_CALL(
*delegate_mock,
InjectTouchInput(1, EqualsPointerTouchInfoFlag(id_to_flags)))
.WillOnce(Return(1));
TouchInjectorWin injector;
injector.SetInjectorDelegateForTest(delegate_mock.Pass());
EXPECT_TRUE(injector.Init());
injector.InjectTouchEvent(event);
event.set_event_type(TouchEvent::TOUCH_POINT_CANCEL);
injector.InjectTouchEvent(event);
}
// Note that points that haven't changed should be injected as MOVE.
// This tests:
// 1. Start first touch point.
// 2. Start second touch point.
// 3. Move both touch points.
// 4. Start third touch point.
// 5. End second touch point.
// 6. Cancel remaining (first and third) touch points.
TEST(TouchInjectorWinTest, MultiTouch) {
scoped_ptr<TouchInjectorWinDelegateMock> delegate_mock(
new ::testing::StrictMock<TouchInjectorWinDelegateMock>());
InSequence s;
EXPECT_CALL(*delegate_mock, InitializeTouchInjection(_, _))
.WillOnce(Return(1));
IdFlagMap id_to_flags;
id_to_flags[0u] = kStartFlag;
EXPECT_CALL(
*delegate_mock,
InjectTouchInput(1, EqualsPointerTouchInfoFlag(id_to_flags)))
.WillOnce(Return(1));
id_to_flags[0u] = kMoveFlag;
id_to_flags[1u] = kStartFlag;
EXPECT_CALL(
*delegate_mock,
InjectTouchInput(2, EqualsPointerTouchInfoFlag(id_to_flags)))
.WillOnce(Return(1));
id_to_flags[0u] = kMoveFlag;
id_to_flags[1u] = kMoveFlag;
EXPECT_CALL(
*delegate_mock,
InjectTouchInput(2, EqualsPointerTouchInfoFlag(id_to_flags)))
.WillOnce(Return(1));
id_to_flags[0u] = kMoveFlag;
id_to_flags[1u] = kMoveFlag;
id_to_flags[2u] = kStartFlag;
EXPECT_CALL(
*delegate_mock,
InjectTouchInput(3, EqualsPointerTouchInfoFlag(id_to_flags)))
.WillOnce(Return(1));
id_to_flags[0u] = kMoveFlag;
id_to_flags[1u] = kEndFlag;
id_to_flags[2u] = kMoveFlag;
EXPECT_CALL(
*delegate_mock,
InjectTouchInput(3, EqualsPointerTouchInfoFlag(id_to_flags)))
.WillOnce(Return(1));
id_to_flags.erase(1u);
id_to_flags[0u] = kCancelFlag;
id_to_flags[2u] = kCancelFlag;
EXPECT_CALL(
*delegate_mock,
InjectTouchInput(2, EqualsPointerTouchInfoFlag(id_to_flags)))
.WillOnce(Return(1));
TouchInjectorWin injector;
injector.SetInjectorDelegateForTest(delegate_mock.Pass());
EXPECT_TRUE(injector.Init());
// Start first touch point.
TouchEvent first_touch_start;
first_touch_start.set_event_type(TouchEvent::TOUCH_POINT_START);
TouchEventPoint* point0 = first_touch_start.add_touch_points();
point0->set_id(0u);
injector.InjectTouchEvent(first_touch_start);
// Add second touch point.
TouchEvent second_touch_start;
second_touch_start.set_event_type(TouchEvent::TOUCH_POINT_START);
TouchEventPoint* point1 = second_touch_start.add_touch_points();
point1->set_id(1u);
injector.InjectTouchEvent(second_touch_start);
// Move both touch points.
TouchEvent move_both;
move_both.set_event_type(TouchEvent::TOUCH_POINT_MOVE);
point0 = second_touch_start.add_touch_points();
point1 = second_touch_start.add_touch_points();
point0->set_id(0u);
point1->set_id(1u);
injector.InjectTouchEvent(move_both);
// Add another.
TouchEvent third_touch_start;
third_touch_start.set_event_type(TouchEvent::TOUCH_POINT_START);
TouchEventPoint* point2 = third_touch_start.add_touch_points();
point2->set_id(2u);
injector.InjectTouchEvent(third_touch_start);
// Release second touch point.
TouchEvent release_second;
release_second.set_event_type(TouchEvent::TOUCH_POINT_END);
point1 = release_second.add_touch_points();
point1->set_id(1u);
injector.InjectTouchEvent(release_second);
// Cancel the remaining two points.
TouchEvent cancel_rest;
cancel_rest.set_event_type(TouchEvent::TOUCH_POINT_CANCEL);
point0 = cancel_rest.add_touch_points();
point0->set_id(0u);
point2 = cancel_rest.add_touch_points();
point2->set_id(2u);
injector.InjectTouchEvent(cancel_rest);
}
} // namespace remoting
...@@ -190,7 +190,14 @@ void SessionInputInjectorWin::Core::InjectMouseEvent(const MouseEvent& event) { ...@@ -190,7 +190,14 @@ void SessionInputInjectorWin::Core::InjectMouseEvent(const MouseEvent& event) {
} }
void SessionInputInjectorWin::Core::InjectTouchEvent(const TouchEvent& event) { void SessionInputInjectorWin::Core::InjectTouchEvent(const TouchEvent& event) {
NOTIMPLEMENTED(); if (!input_task_runner_->BelongsToCurrentThread()) {
input_task_runner_->PostTask(
FROM_HERE, base::Bind(&Core::InjectTouchEvent, this, event));
return;
}
SwitchToInputDesktop();
nested_executor_->InjectTouchEvent(event);
} }
SessionInputInjectorWin::Core::~Core() { SessionInputInjectorWin::Core::~Core() {
......
...@@ -224,6 +224,8 @@ ...@@ -224,6 +224,8 @@
'host/token_validator_base.h', 'host/token_validator_base.h',
'host/token_validator_factory_impl.cc', 'host/token_validator_factory_impl.cc',
'host/token_validator_factory_impl.h', 'host/token_validator_factory_impl.h',
'host/touch_injector_win.h',
'host/touch_injector_win.cc',
'host/usage_stats_consent.h', 'host/usage_stats_consent.h',
'host/usage_stats_consent_mac.cc', 'host/usage_stats_consent_mac.cc',
'host/usage_stats_consent_win.cc', 'host/usage_stats_consent_win.cc',
......
...@@ -214,6 +214,7 @@ ...@@ -214,6 +214,7 @@
'host/shaped_desktop_capturer_unittest.cc', 'host/shaped_desktop_capturer_unittest.cc',
'host/third_party_auth_config_unittest.cc', 'host/third_party_auth_config_unittest.cc',
'host/token_validator_factory_impl_unittest.cc', 'host/token_validator_factory_impl_unittest.cc',
'host/touch_injector_win_unittest.cc',
'host/video_frame_pump_unittest.cc', 'host/video_frame_pump_unittest.cc',
'host/video_frame_recorder_unittest.cc', 'host/video_frame_recorder_unittest.cc',
'host/win/rdp_client_unittest.cc', 'host/win/rdp_client_unittest.cc',
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment