Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(903)

Unified Diff: remoting/host/touch_injector_win.cc

Issue 991643002: Windows Host Touch Injection (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: rebased Created 5 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « remoting/host/touch_injector_win.h ('k') | remoting/host/touch_injector_win_unittest.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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
« no previous file with comments | « remoting/host/touch_injector_win.h ('k') | remoting/host/touch_injector_win_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698