touch_injector_win.cc 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  1. // Copyright 2015 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "remoting/host/touch_injector_win.h"
  5. #include <string>
  6. #include <utility>
  7. #include "base/files/file_path.h"
  8. #include "base/logging.h"
  9. #include "base/native_library.h"
  10. #include "base/notreached.h"
  11. #include "remoting/proto/event.pb.h"
  12. #include "third_party/webrtc/modules/desktop_capture/desktop_capture_types.h"
  13. #include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h"
  14. #include "third_party/webrtc/modules/desktop_capture/win/screen_capture_utils.h"
  15. namespace remoting {
  16. using protocol::TouchEvent;
  17. using protocol::TouchEventPoint;
  18. namespace {
  19. typedef BOOL(NTAPI* InitializeTouchInjectionFunction)(UINT32, DWORD);
  20. typedef BOOL(NTAPI* InjectTouchInputFunction)(UINT32,
  21. const POINTER_TOUCH_INFO*);
  22. const uint32_t kMaxSimultaneousTouchCount = 10;
  23. // This is used to reinject all points that have not changed as "move"ed points,
  24. // even if they have not actually moved.
  25. // This is required for multi-touch to work, e.g. pinching and zooming gestures
  26. // (handled by apps) won't work without reinjecting the points, even though the
  27. // user moved only one finger and held the other finger in place.
  28. void AppendMapValuesToVector(
  29. std::map<uint32_t, POINTER_TOUCH_INFO>* touches_in_contact,
  30. std::vector<POINTER_TOUCH_INFO>* output_vector) {
  31. for (auto& id_and_pointer_touch_info : *touches_in_contact) {
  32. POINTER_TOUCH_INFO& pointer_touch_info = id_and_pointer_touch_info.second;
  33. output_vector->push_back(pointer_touch_info);
  34. }
  35. }
  36. void ConvertToPointerTouchInfoImpl(
  37. const TouchEventPoint& touch_point,
  38. POINTER_TOUCH_INFO* pointer_touch_info) {
  39. pointer_touch_info->touchMask =
  40. TOUCH_MASK_CONTACTAREA | TOUCH_MASK_ORIENTATION;
  41. pointer_touch_info->touchFlags = TOUCH_FLAG_NONE;
  42. // Although radius_{x,y} can be undefined (i.e. has_radius_{x,y} == false),
  43. // the default value (0.0) will set the area correctly.
  44. // MSDN mentions that if the digitizer does not detect the size of the touch
  45. // point, rcContact should be set to 0 by 0 rectangle centered at the
  46. // coordinate.
  47. pointer_touch_info->rcContact.left =
  48. touch_point.x() - touch_point.radius_x();
  49. pointer_touch_info->rcContact.top = touch_point.y() - touch_point.radius_y();
  50. pointer_touch_info->rcContact.right =
  51. touch_point.x() + touch_point.radius_x();
  52. pointer_touch_info->rcContact.bottom =
  53. touch_point.y() + touch_point.radius_y();
  54. pointer_touch_info->orientation = touch_point.angle();
  55. if (touch_point.has_pressure()) {
  56. pointer_touch_info->touchMask |= TOUCH_MASK_PRESSURE;
  57. const float kMinimumPressure = 0.0;
  58. const float kMaximumPressure = 1.0;
  59. const float clamped_touch_point_pressure =
  60. std::max(kMinimumPressure,
  61. std::min(kMaximumPressure, touch_point.pressure()));
  62. const int kWindowsMaxTouchPressure = 1024; // Defined in MSDN.
  63. const int pressure =
  64. clamped_touch_point_pressure * kWindowsMaxTouchPressure;
  65. pointer_touch_info->pressure = pressure;
  66. }
  67. pointer_touch_info->pointerInfo.pointerType = PT_TOUCH;
  68. pointer_touch_info->pointerInfo.pointerId = touch_point.id();
  69. pointer_touch_info->pointerInfo.ptPixelLocation.x = touch_point.x();
  70. pointer_touch_info->pointerInfo.ptPixelLocation.y = touch_point.y();
  71. }
  72. // The caller should set memset(0) the struct and set
  73. // pointer_touch_info->pointerInfo.pointerFlags.
  74. void ConvertToPointerTouchInfo(
  75. const TouchEventPoint& touch_point,
  76. POINTER_TOUCH_INFO* pointer_touch_info) {
  77. // TODO(zijiehe): Use GetFullscreenTopLeft() once
  78. // https://chromium-review.googlesource.com/c/581951/ is submitted.
  79. webrtc::DesktopVector top_left = webrtc::GetScreenRect(
  80. webrtc::kFullDesktopScreenId, std::wstring()).top_left();
  81. if (top_left.is_zero()) {
  82. ConvertToPointerTouchInfoImpl(touch_point, pointer_touch_info);
  83. return;
  84. }
  85. TouchEventPoint point(touch_point);
  86. point.set_x(point.x() + top_left.x());
  87. point.set_y(point.y() + top_left.y());
  88. ConvertToPointerTouchInfoImpl(point, pointer_touch_info);
  89. }
  90. } // namespace
  91. TouchInjectorWinDelegate::~TouchInjectorWinDelegate() {}
  92. // static.
  93. std::unique_ptr<TouchInjectorWinDelegate> TouchInjectorWinDelegate::Create() {
  94. base::ScopedNativeLibrary library(base::FilePath(L"User32.dll"));
  95. if (!library.is_valid()) {
  96. PLOG(INFO) << "Failed to get library module for touch injection functions.";
  97. return nullptr;
  98. }
  99. InitializeTouchInjectionFunction init_func =
  100. reinterpret_cast<InitializeTouchInjectionFunction>(
  101. library.GetFunctionPointer("InitializeTouchInjection"));
  102. if (!init_func) {
  103. PLOG(INFO) << "Failed to get InitializeTouchInjection function handle.";
  104. return nullptr;
  105. }
  106. InjectTouchInputFunction inject_touch_func =
  107. reinterpret_cast<InjectTouchInputFunction>(
  108. library.GetFunctionPointer("InjectTouchInput"));
  109. if (!inject_touch_func) {
  110. PLOG(INFO) << "Failed to get InjectTouchInput.";
  111. return nullptr;
  112. }
  113. return std::unique_ptr<TouchInjectorWinDelegate>(new TouchInjectorWinDelegate(
  114. library.release(), init_func, inject_touch_func));
  115. }
  116. TouchInjectorWinDelegate::TouchInjectorWinDelegate(
  117. base::NativeLibrary library,
  118. InitializeTouchInjectionFunction initialize_touch_injection_func,
  119. InjectTouchInputFunction inject_touch_input_func)
  120. : library_module_(library),
  121. initialize_touch_injection_func_(initialize_touch_injection_func),
  122. inject_touch_input_func_(inject_touch_input_func) {}
  123. BOOL TouchInjectorWinDelegate::InitializeTouchInjection(UINT32 max_count,
  124. DWORD dw_mode) {
  125. return initialize_touch_injection_func_(max_count, dw_mode);
  126. }
  127. DWORD TouchInjectorWinDelegate::InjectTouchInput(
  128. UINT32 count,
  129. const POINTER_TOUCH_INFO* contacts) {
  130. return inject_touch_input_func_(count, contacts);
  131. }
  132. TouchInjectorWin::TouchInjectorWin() = default;
  133. TouchInjectorWin::~TouchInjectorWin() = default;
  134. // Note that TouchInjectorWinDelegate::Create() is not called in this method
  135. // so that a mock delegate can be injected in tests and set expectations on the
  136. // mock and return value of this method.
  137. bool TouchInjectorWin::Init() {
  138. if (!delegate_)
  139. delegate_ = TouchInjectorWinDelegate::Create();
  140. // If initializing the delegate failed above, then the platform likely doesn't
  141. // support touch (or the libraries failed to load for some reason).
  142. if (!delegate_)
  143. return false;
  144. if (!delegate_->InitializeTouchInjection(
  145. kMaxSimultaneousTouchCount, TOUCH_FEEDBACK_DEFAULT)) {
  146. // delagate_ is reset here so that the function that need the delegate
  147. // can check if it is null.
  148. delegate_.reset();
  149. PLOG(INFO) << "Failed to initialize touch injection.";
  150. return false;
  151. }
  152. return true;
  153. }
  154. void TouchInjectorWin::Deinitialize() {
  155. touches_in_contact_.clear();
  156. // Same reason as TouchInjectorWin::Init(). For injecting mock delegates for
  157. // tests, a new delegate is created here.
  158. delegate_ = TouchInjectorWinDelegate::Create();
  159. }
  160. void TouchInjectorWin::InjectTouchEvent(const TouchEvent& event) {
  161. if (!delegate_) {
  162. VLOG(3) << "Touch injection functions are not initialized.";
  163. return;
  164. }
  165. switch (event.event_type()) {
  166. case TouchEvent::TOUCH_POINT_START:
  167. AddNewTouchPoints(event);
  168. break;
  169. case TouchEvent::TOUCH_POINT_MOVE:
  170. MoveTouchPoints(event);
  171. break;
  172. case TouchEvent::TOUCH_POINT_END:
  173. EndTouchPoints(event);
  174. break;
  175. case TouchEvent::TOUCH_POINT_CANCEL:
  176. CancelTouchPoints(event);
  177. break;
  178. default:
  179. NOTREACHED();
  180. return;
  181. }
  182. }
  183. void TouchInjectorWin::SetInjectorDelegateForTest(
  184. std::unique_ptr<TouchInjectorWinDelegate> functions) {
  185. delegate_ = std::move(functions);
  186. }
  187. void TouchInjectorWin::AddNewTouchPoints(const TouchEvent& event) {
  188. DCHECK_EQ(event.event_type(), TouchEvent::TOUCH_POINT_START);
  189. std::vector<POINTER_TOUCH_INFO> touches;
  190. // Must inject already touching points as move events.
  191. AppendMapValuesToVector(&touches_in_contact_, &touches);
  192. for (const TouchEventPoint& touch_point : event.touch_points()) {
  193. POINTER_TOUCH_INFO pointer_touch_info;
  194. memset(&pointer_touch_info, 0, sizeof(pointer_touch_info));
  195. pointer_touch_info.pointerInfo.pointerFlags =
  196. POINTER_FLAG_INRANGE | POINTER_FLAG_INCONTACT | POINTER_FLAG_DOWN;
  197. ConvertToPointerTouchInfo(touch_point, &pointer_touch_info);
  198. touches.push_back(pointer_touch_info);
  199. // All points in the map should be a move point.
  200. pointer_touch_info.pointerInfo.pointerFlags =
  201. POINTER_FLAG_INRANGE | POINTER_FLAG_INCONTACT | POINTER_FLAG_UPDATE;
  202. touches_in_contact_[touch_point.id()] = pointer_touch_info;
  203. }
  204. if (delegate_->InjectTouchInput(touches.size(), touches.data()) == 0) {
  205. PLOG(ERROR) << "Failed to inject a touch start event.";
  206. }
  207. }
  208. void TouchInjectorWin::MoveTouchPoints(const TouchEvent& event) {
  209. DCHECK_EQ(event.event_type(), TouchEvent::TOUCH_POINT_MOVE);
  210. for (const TouchEventPoint& touch_point : event.touch_points()) {
  211. POINTER_TOUCH_INFO* pointer_touch_info =
  212. &touches_in_contact_[touch_point.id()];
  213. memset(pointer_touch_info, 0, sizeof(*pointer_touch_info));
  214. pointer_touch_info->pointerInfo.pointerFlags =
  215. POINTER_FLAG_INRANGE | POINTER_FLAG_INCONTACT | POINTER_FLAG_UPDATE;
  216. ConvertToPointerTouchInfo(touch_point, pointer_touch_info);
  217. }
  218. std::vector<POINTER_TOUCH_INFO> touches;
  219. // Must inject already touching points as move events.
  220. AppendMapValuesToVector(&touches_in_contact_, &touches);
  221. if (delegate_->InjectTouchInput(touches.size(), touches.data()) == 0) {
  222. PLOG(ERROR) << "Failed to inject a touch move event.";
  223. }
  224. }
  225. void TouchInjectorWin::EndTouchPoints(const TouchEvent& event) {
  226. DCHECK_EQ(event.event_type(), TouchEvent::TOUCH_POINT_END);
  227. std::vector<POINTER_TOUCH_INFO> touches;
  228. for (const TouchEventPoint& touch_point : event.touch_points()) {
  229. POINTER_TOUCH_INFO pointer_touch_info =
  230. touches_in_contact_[touch_point.id()];
  231. pointer_touch_info.pointerInfo.pointerFlags = POINTER_FLAG_UP;
  232. touches_in_contact_.erase(touch_point.id());
  233. touches.push_back(pointer_touch_info);
  234. }
  235. AppendMapValuesToVector(&touches_in_contact_, &touches);
  236. if (delegate_->InjectTouchInput(touches.size(), touches.data()) == 0) {
  237. PLOG(ERROR) << "Failed to inject a touch end event.";
  238. }
  239. }
  240. void TouchInjectorWin::CancelTouchPoints(const TouchEvent& event) {
  241. DCHECK_EQ(event.event_type(), TouchEvent::TOUCH_POINT_CANCEL);
  242. std::vector<POINTER_TOUCH_INFO> touches;
  243. for (const TouchEventPoint& touch_point : event.touch_points()) {
  244. POINTER_TOUCH_INFO pointer_touch_info =
  245. touches_in_contact_[touch_point.id()];
  246. pointer_touch_info.pointerInfo.pointerFlags =
  247. POINTER_FLAG_UP | POINTER_FLAG_CANCELED;
  248. touches_in_contact_.erase(touch_point.id());
  249. touches.push_back(pointer_touch_info);
  250. }
  251. AppendMapValuesToVector(&touches_in_contact_, &touches);
  252. if (delegate_->InjectTouchInput(touches.size(), touches.data()) == 0) {
  253. PLOG(ERROR) << "Failed to inject a touch cancel event.";
  254. }
  255. }
  256. } // namespace remoting