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

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: Add Deinitialize() and change test 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
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..ebd370f38e3b7d5cc92ba9f92170e0c1b3ee316a
--- /dev/null
+++ b/remoting/host/touch_injector_win.cc
@@ -0,0 +1,284 @@
+// 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/logging.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 kMaxSimlutaneousTouchCount = 10;
Wez 2015/03/17 03:06:23 typo: Simultaneous
Rintaro Kuroiwa 2015/03/18 01:33:30 Done.
+
+// Change the POINTER_TOUCH_INFO structures in the map to MOVE and copy those
+// into |output_vector|.
Wez 2015/03/17 03:06:23 Why are you changing them in the map? Isn't the p
Rintaro Kuroiwa 2015/03/18 01:33:31 Done. As discussed offline, the map will always co
+// This is used to reinject all points that have not changed as "move"d point,
+// even if it has not moved.
+// This is required for multi-touch to work, e.g. pinching and zooming gestures
+// (handled by apps) won't work without reinjection the points. If not
+// reinjected, the injection API assumes that they were dropped.
+void ChangeToMovePointsAndAppendToVector(
+ std::map<uint32_t, POINTER_TOUCH_INFO>* touches_in_contact,
+ std::vector<POINTER_TOUCH_INFO>* output_vector) {
+ DCHECK(touches_in_contact);
+ DCHECK(output_vector);
Wez 2015/03/17 03:06:23 You are about to dereference these anyway, below,
Rintaro Kuroiwa 2015/03/18 01:33:31 Done.
+ for (auto& id_and_pointer_touch_info : *touches_in_contact) {
+ POINTER_TOUCH_INFO& pointer_touch_info = id_and_pointer_touch_info.second;
+ pointer_touch_info.pointerInfo.pointerFlags =
+ POINTER_FLAG_INRANGE | POINTER_FLAG_INCONTACT | POINTER_FLAG_UPDATE;
+ output_vector->push_back(pointer_touch_info);
+ }
+}
+
+// The caller should set pointer_touch_info->pointerInfo.pointerFlags.
+void ConvertToPointerTouchInfo(
+ const TouchEventPoint& touch_point,
+ POINTER_TOUCH_INFO* pointer_touch_info) {
+ DCHECK(pointer_touch_info);
Wez 2015/03/17 03:06:23 Same here
Rintaro Kuroiwa 2015/03/18 01:33:31 Done.
+
+ 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 will set the area correctly.
Wez 2015/03/17 03:06:23 Why will the default value set it correctly? That
Rintaro Kuroiwa 2015/03/18 01:33:31 Yes it does rely on get on unset values return 0.
+ 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 int kMaxTouchPressure = 1024; // Defined in MSDN.
+ const int pressure = touch_point.pressure() * kMaxTouchPressure;
Wez 2015/03/17 03:06:23 Is there a danger of this wrapping? Should we inst
Rintaro Kuroiwa 2015/03/18 01:33:31 Done.
+ if (pressure < 0) {
+ pointer_touch_info->pressure = 0;
+ } else if (pressure > kMaxTouchPressure) {
+ pointer_touch_info->pressure = kMaxTouchPressure;
+ } else {
+ pointer_touch_info->pressure = pressure;
+ }
+ } else {
+ // For a weird case where the pressure was there for a start event but
+ // not set in later events, set pressure to 0 because usually
+ // POINTER_TOUCH_INFO is memset() to 0.
Wez 2015/03/17 03:06:23 Is there a reason you don't both memset()ting it?
Rintaro Kuroiwa 2015/03/18 01:33:31 I was thinking if I memset(0) that's an extra writ
+ pointer_touch_info->pressure = 0;
+ }
+
+ 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
+
+TouchInjectFunctionsWin::TouchInjectFunctionsWin()
+ : initialize_touch_injection_func_(nullptr),
+ inject_touch_input_func_(nullptr) {}
+
+TouchInjectFunctionsWin::~TouchInjectFunctionsWin() {}
+
+bool TouchInjectFunctionsWin::Init() {
Wez 2015/03/17 03:06:23 You can use base::NativeLibrary, which wraps all t
Rintaro Kuroiwa 2015/03/18 01:33:31 Done.
+ if (initialize_touch_injection_func_ && inject_touch_input_func_)
+ return true;
+
+ initialize_touch_injection_func_ = nullptr;
+ inject_touch_input_func_ = nullptr;
Wez 2015/03/17 03:06:23 Why are you NULLing these? You just checked that t
Rintaro Kuroiwa 2015/03/18 01:33:30 N/A in the new patch.
+ HMODULE user32_dll_handle = ::GetModuleHandleA("User32.dll");
+ if (!user32_dll_handle) {
+ PLOG(INFO) << "Failed to get User32.dll handle.";
+ return false;
+ }
+
+ InitializeTouchInjectionFunction init_func =
+ reinterpret_cast<InitializeTouchInjectionFunction>(
+ ::GetProcAddress(user32_dll_handle, "InitializeTouchInjection"));
+ if (!init_func) {
+ PLOG(INFO) << "Failed to get InitializeTouchInjection function handle.";
+ return false;
+ }
+
+ InjectTouchInputFunction inject_touch_func =
+ reinterpret_cast<InjectTouchInputFunction>(
+ ::GetProcAddress(user32_dll_handle, "InjectTouchInput"));
+ if (!inject_touch_func) {
+ PLOG(INFO) << "Failed to get InjectTouchInput.";
+ return false;
+ }
+
+ initialize_touch_injection_func_ = init_func;
+ inject_touch_input_func_ = inject_touch_func;
+ return true;
+}
+
+bool TouchInjectFunctionsWin::Initialized() {
+ DCHECK((initialize_touch_injection_func_ && inject_touch_input_func_) ||
+ (!initialize_touch_injection_func_ && !inject_touch_input_func_));
+ return initialize_touch_injection_func_ != nullptr;
+}
+
+BOOL TouchInjectFunctionsWin::InitializeTouchInjection(UINT32 max_count,
+ DWORD dw_mode) {
Wez 2015/03/17 03:06:23 Indentation
Rintaro Kuroiwa 2015/03/18 01:33:31 Done.
+ return initialize_touch_injection_func_(max_count, dw_mode);
+}
+
+DWORD TouchInjectFunctionsWin::InjectTouchInput(
+ UINT32 count,
+ const POINTER_TOUCH_INFO* contacts) {
+ return inject_touch_input_func_(count, contacts);
+}
+
+TouchInjectorWin::TouchInjectorWin()
+ : delegate_(new TouchInjectFunctionsWin()) {}
+
+TouchInjectorWin::~TouchInjectorWin() {}
+
+bool TouchInjectorWin::Init() {
+ if (!delegate_->Init())
+ return false;
+
+ if (!delegate_->InitializeTouchInjection(kMaxSimlutaneousTouchCount,
+ TOUCH_FEEDBACK_DEFAULT)) {
+ PLOG(INFO) << "Failed to initialize touch injection.";
Wez 2015/03/17 03:06:23 If you null the delegate_ here then you can check
Rintaro Kuroiwa 2015/03/18 01:33:30 Done.
+ return false;
+ }
+
+ return true;
+}
+
+void TouchInjectorWin::Deinitialize() {
+ touches_in_contact_.clear();
+}
+
+void TouchInjectorWin::InjectTouchEvent(const TouchEvent& event) {
+ if (!delegate_->Initialized()) {
+ 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::SetInjectFunctionsForTest(
+ scoped_ptr<TouchInjectFunctionsWin> 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.
+ ChangeToMovePointsAndAppendToVector(&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_in_contact_[touch_point.id()] = pointer_touch_info;
+ touches.push_back(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()];
+ 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.
+ ChangeToMovePointsAndAppendToVector(&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);
+ }
+
+ ChangeToMovePointsAndAppendToVector(&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);
+ }
+
+ ChangeToMovePointsAndAppendToVector(&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
+

Powered by Google App Engine
This is Rietveld 408576698