Bug 941774 - Win32/winrt shared implementation. r=bbondy a=lsblakk
authorJim Mathies <jmathies@mozilla.com>
Sat, 14 Dec 2013 14:40:56 -0600
changeset 175380 f4bc4144c7e4f0517966ee5018d9f68f6a2e1325
parent 175379 ff02fb4d847479e27014847a46330be035226ee6
child 175381 cb28a42ecacfa500ca92a6c54fff60c0de618f0f
push id445
push userffxbld
push dateMon, 10 Mar 2014 22:05:19 +0000
treeherdermozilla-release@dc38b741b04e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbbondy, lsblakk
bugs941774
milestone28.0a2
Bug 941774 - Win32/winrt shared implementation. r=bbondy a=lsblakk
widget/windows/nsWindowBase.cpp
widget/windows/nsWindowBase.h
widget/windows/touchinjection_sdk80.h
--- a/widget/windows/nsWindowBase.cpp
+++ b/widget/windows/nsWindowBase.cpp
@@ -1,19 +1,26 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsWindowBase.h"
 
 #include "mozilla/MiscEvents.h"
+
+#include "WinUtils.h"
 #include "npapi.h"
 
 using namespace mozilla;
+using namespace mozilla::widget;
+
+static const wchar_t kUser32LibName[] =  L"user32.dll";
+bool nsWindowBase::sTouchInjectInitialized = false;
+InjectTouchInputPtr nsWindowBase::sInjectTouchFuncPtr;
 
 bool
 nsWindowBase::DispatchPluginEvent(const MSG& aMsg)
 {
   if (!PluginHasFocus()) {
     return false;
   }
   WidgetPluginEvent pluginEvent(true, NS_PLUGIN_INPUT_EVENT, this);
@@ -22,8 +29,169 @@ nsWindowBase::DispatchPluginEvent(const 
   NPEvent npEvent;
   npEvent.event = aMsg.message;
   npEvent.wParam = aMsg.wParam;
   npEvent.lParam = aMsg.lParam;
   pluginEvent.pluginEvent = &npEvent;
   pluginEvent.retargetToFocusedDocument = true;
   return DispatchWindowEvent(&pluginEvent);
 }
+
+// static
+bool
+nsWindowBase::InitTouchInjection()
+{
+  if (!sTouchInjectInitialized) {
+    // Initialize touch injection on the first call
+    HMODULE hMod = LoadLibraryW(kUser32LibName);
+    if (!hMod) {
+      return false;
+    }
+
+    InitializeTouchInjectionPtr func =
+      (InitializeTouchInjectionPtr)GetProcAddress(hMod, "InitializeTouchInjection");
+    if (!func) {
+      WinUtils::Log("InitializeTouchInjection not available.");
+      return false;
+    }
+
+    if (!func(TOUCH_INJECT_MAX_POINTS, TOUCH_FEEDBACK_DEFAULT)) {
+      WinUtils::Log("InitializeTouchInjection failure. GetLastError=%d", GetLastError());
+      return false;
+    }
+
+    sInjectTouchFuncPtr =
+      (InjectTouchInputPtr)GetProcAddress(hMod, "InjectTouchInput");
+    if (!sInjectTouchFuncPtr) {
+      WinUtils::Log("InjectTouchInput not available.");
+      return false;
+    }
+    sTouchInjectInitialized = true;
+  }
+  return true;
+}
+
+bool
+nsWindowBase::InjectTouchPoint(uint32_t aId, nsIntPoint& aPointerScreenPoint,
+                               POINTER_FLAGS aFlags, uint32_t aPressure,
+                               uint32_t aOrientation)
+{
+  if (aId > TOUCH_INJECT_MAX_POINTS) {
+    WinUtils::Log("Pointer ID exceeds maximum. See TOUCH_INJECT_MAX_POINTS.");
+    return false;
+  }
+
+  POINTER_TOUCH_INFO info;
+  memset(&info, 0, sizeof(POINTER_TOUCH_INFO));
+
+  info.touchFlags = TOUCH_FLAG_NONE;
+  info.touchMask = TOUCH_MASK_CONTACTAREA|TOUCH_MASK_ORIENTATION|TOUCH_MASK_PRESSURE;
+  info.pressure = aPressure;
+  info.orientation = aOrientation;
+  
+  info.pointerInfo.pointerFlags = aFlags;
+  info.pointerInfo.pointerType =  PT_TOUCH;
+  info.pointerInfo.pointerId = aId;
+  info.pointerInfo.ptPixelLocation.x = WinUtils::LogToPhys(aPointerScreenPoint.x);
+  info.pointerInfo.ptPixelLocation.y = WinUtils::LogToPhys(aPointerScreenPoint.y);
+
+  info.rcContact.top = info.pointerInfo.ptPixelLocation.y - 2;
+  info.rcContact.bottom = info.pointerInfo.ptPixelLocation.y + 2;
+  info.rcContact.left = info.pointerInfo.ptPixelLocation.x - 2;
+  info.rcContact.right = info.pointerInfo.ptPixelLocation.x + 2;
+  
+  if (!sInjectTouchFuncPtr(1, &info)) {
+    WinUtils::Log("InjectTouchInput failure. GetLastError=%d", GetLastError());
+    return false;
+  }
+  return true;
+}
+
+nsresult
+nsWindowBase::SynthesizeNativeTouchPoint(uint32_t aPointerId,
+                                         nsIWidget::TouchPointerState aPointerState,
+                                         nsIntPoint aPointerScreenPoint,
+                                         double aPointerPressure,
+                                         uint32_t aPointerOrientation)
+{
+  if (!InitTouchInjection()) {
+    return NS_ERROR_NOT_IMPLEMENTED;
+  }
+
+  bool hover = aPointerState & TOUCH_HOVER;
+  bool contact = aPointerState & TOUCH_CONTACT;
+  bool remove = aPointerState & TOUCH_REMOVE;
+  bool cancel = aPointerState & TOUCH_CANCEL;
+
+  // win api expects a value from 0 to 1024. aPointerPressure is a value
+  // from 0.0 to 1.0.
+  uint32_t pressure = (uint32_t)ceil(aPointerPressure * 1024);
+
+  // If we already know about this pointer id get it's record
+  PointerInfo* info = mActivePointers.Get(aPointerId);
+
+  // We know about this pointer, send an update
+  if (info) {
+    POINTER_FLAGS flags = POINTER_FLAG_UPDATE;
+    if (hover) {
+      flags |= POINTER_FLAG_INRANGE;
+    } else if (contact) {
+      flags |= POINTER_FLAG_INCONTACT|POINTER_FLAG_INRANGE;
+    } else if (remove) {
+      flags = POINTER_FLAG_UP;
+      // Remove the pointer from our tracking list. This is nsAutPtr wrapped,
+      // so shouldn't leak.
+      mActivePointers.Remove(aPointerId);
+    }
+
+    if (cancel) {
+      flags |= POINTER_FLAG_CANCELED;
+    }
+
+    return !InjectTouchPoint(aPointerId, aPointerScreenPoint, flags,
+                             pressure, aPointerOrientation) ?
+      NS_ERROR_UNEXPECTED : NS_OK;
+  }
+
+  // Missing init state, error out
+  if (remove || cancel) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  // Create a new pointer
+  info = new PointerInfo(aPointerId, aPointerScreenPoint);
+
+  POINTER_FLAGS flags = POINTER_FLAG_INRANGE;
+  if (contact) {
+    flags |= POINTER_FLAG_INCONTACT|POINTER_FLAG_DOWN;
+  }
+
+  mActivePointers.Put(aPointerId, info);
+  return !InjectTouchPoint(aPointerId, aPointerScreenPoint, flags,
+                           pressure, aPointerOrientation) ?
+    NS_ERROR_UNEXPECTED : NS_OK;
+}
+
+// static
+PLDHashOperator
+nsWindowBase::CancelTouchPoints(const unsigned int& aPointerId, nsAutoPtr<PointerInfo>& aInfo, void* aUserArg)
+{
+  nsWindowBase* self = static_cast<nsWindowBase*>(aUserArg);
+  self->InjectTouchPoint(aInfo.get()->mPointerId, aInfo.get()->mPosition, POINTER_FLAG_CANCELED);
+  return (PLDHashOperator)(PL_DHASH_NEXT|PL_DHASH_REMOVE);
+}
+
+nsresult
+nsWindowBase::ClearNativeTouchSequence()
+{
+  if (!sTouchInjectInitialized) {
+    return NS_OK;
+  }
+
+  // cancel all input points
+  mActivePointers.Enumerate(CancelTouchPoints, (void*)this);
+
+  nsBaseWidget::ClearNativeTouchSequence();
+
+  return NS_OK;
+}
+
+
--- a/widget/windows/nsWindowBase.h
+++ b/widget/windows/nsWindowBase.h
@@ -3,17 +3,20 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsWindowBase_h_
 #define nsWindowBase_h_
 
 #include "mozilla/EventForwards.h"
 #include "nsBaseWidget.h"
+#include "nsClassHashtable.h"
+
 #include <windows.h>
+#include "touchinjection_sdk80.h"
 
 /*
  * nsWindowBase - Base class of common methods other classes need to access
  * in both win32 and winrt window classes.
  */
 class nsWindowBase : public nsBaseWidget
 {
 public:
@@ -70,13 +73,49 @@ public:
   /*
    * Returns true if a plugin has focus on this widget.  Otherwise, false.
    */
   virtual bool PluginHasFocus() const MOZ_FINAL
   {
     return (mInputContext.mIMEState.mEnabled == IMEState::PLUGIN);
   }
 
+public:
+  /*
+   * Touch input injection apis
+   */
+  virtual nsresult SynthesizeNativeTouchPoint(uint32_t aPointerId,
+                                              TouchPointerState aPointerState,
+                                              nsIntPoint aPointerScreenPoint,
+                                              double aPointerPressure,
+                                              uint32_t aPointerOrientation);
+  virtual nsresult ClearNativeTouchSequence();
+
+protected:
+  static bool InitTouchInjection();
+  bool InjectTouchPoint(uint32_t aId, nsIntPoint& aPointerScreenPoint,
+                        POINTER_FLAGS aFlags, uint32_t aPressure = 1024,
+                        uint32_t aOrientation = 90);
+
+  class PointerInfo
+  {
+  public:
+    PointerInfo(int32_t aPointerId, nsIntPoint& aPoint) :
+      mPointerId(aPointerId),
+      mPosition(aPoint)
+    {
+    }
+
+    int32_t mPointerId;
+    nsIntPoint mPosition;
+  };
+
+  static PLDHashOperator CancelTouchPoints(const unsigned int& aPointerId, nsAutoPtr<PointerInfo>& aInfo, void* aUserArg);
+
+  nsClassHashtable<nsUint32HashKey, PointerInfo> mActivePointers;
+  static bool sTouchInjectInitialized;
+  static InjectTouchInputPtr sInjectTouchFuncPtr;
+
 protected:
   InputContext mInputContext;
 };
 
 #endif // nsWindowBase_h_
new file mode 100644
--- /dev/null
+++ b/widget/windows/touchinjection_sdk80.h
@@ -0,0 +1,107 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef touchinjection_sdk80_h
+#define touchinjection_sdk80_h
+
+// Note, this isn't inclusive of all touch injection header info.
+// You may need to add more to expand on current apis. 
+
+#ifndef TOUCH_FEEDBACK_DEFAULT
+
+#define TOUCH_FEEDBACK_DEFAULT 0x1
+#define TOUCH_FEEDBACK_INDIRECT 0x2
+#define TOUCH_FEEDBACK_NONE 0x3
+
+enum {
+  PT_POINTER  = 0x00000001,   // Generic pointer
+  PT_TOUCH    = 0x00000002,   // Touch
+  PT_PEN      = 0x00000003,   // Pen
+  PT_MOUSE    = 0x00000004,   // Mouse
+};
+
+typedef DWORD POINTER_INPUT_TYPE;
+typedef UINT32 POINTER_FLAGS;
+
+typedef enum {
+  POINTER_CHANGE_NONE,
+  POINTER_CHANGE_FIRSTBUTTON_DOWN,
+  POINTER_CHANGE_FIRSTBUTTON_UP,
+  POINTER_CHANGE_SECONDBUTTON_DOWN,
+  POINTER_CHANGE_SECONDBUTTON_UP,
+  POINTER_CHANGE_THIRDBUTTON_DOWN,
+  POINTER_CHANGE_THIRDBUTTON_UP,
+  POINTER_CHANGE_FOURTHBUTTON_DOWN,
+  POINTER_CHANGE_FOURTHBUTTON_UP,
+  POINTER_CHANGE_FIFTHBUTTON_DOWN,
+  POINTER_CHANGE_FIFTHBUTTON_UP,
+} POINTER_BUTTON_CHANGE_TYPE;
+
+typedef struct {
+  POINTER_INPUT_TYPE    pointerType;
+  UINT32          pointerId;
+  UINT32          frameId;
+  POINTER_FLAGS   pointerFlags;
+  HANDLE          sourceDevice;
+  HWND            hwndTarget;
+  POINT           ptPixelLocation;
+  POINT           ptHimetricLocation;
+  POINT           ptPixelLocationRaw;
+  POINT           ptHimetricLocationRaw;
+  DWORD           dwTime;
+  UINT32          historyCount;
+  INT32           InputData;
+  DWORD           dwKeyStates;
+  UINT64          PerformanceCount;
+  POINTER_BUTTON_CHANGE_TYPE ButtonChangeType;
+} POINTER_INFO;
+
+typedef UINT32 TOUCH_FLAGS;
+typedef UINT32 TOUCH_MASK;
+
+typedef struct {
+  POINTER_INFO    pointerInfo;
+  TOUCH_FLAGS     touchFlags;
+  TOUCH_MASK      touchMask;
+  RECT            rcContact;
+  RECT            rcContactRaw;
+  UINT32          orientation;
+  UINT32          pressure;
+} POINTER_TOUCH_INFO;
+
+#define TOUCH_FLAG_NONE                 0x00000000 // Default
+
+#define TOUCH_MASK_NONE                 0x00000000 // Default - none of the optional fields are valid
+#define TOUCH_MASK_CONTACTAREA          0x00000001 // The rcContact field is valid
+#define TOUCH_MASK_ORIENTATION          0x00000002 // The orientation field is valid
+#define TOUCH_MASK_PRESSURE             0x00000004 // The pressure field is valid
+
+#define POINTER_FLAG_NONE               0x00000000 // Default
+#define POINTER_FLAG_NEW                0x00000001 // New pointer
+#define POINTER_FLAG_INRANGE            0x00000002 // Pointer has not departed
+#define POINTER_FLAG_INCONTACT          0x00000004 // Pointer is in contact
+#define POINTER_FLAG_FIRSTBUTTON        0x00000010 // Primary action
+#define POINTER_FLAG_SECONDBUTTON       0x00000020 // Secondary action
+#define POINTER_FLAG_THIRDBUTTON        0x00000040 // Third button
+#define POINTER_FLAG_FOURTHBUTTON       0x00000080 // Fourth button
+#define POINTER_FLAG_FIFTHBUTTON        0x00000100 // Fifth button
+#define POINTER_FLAG_PRIMARY            0x00002000 // Pointer is primary
+#define POINTER_FLAG_CONFIDENCE         0x00004000 // Pointer is considered unlikely to be accidental
+#define POINTER_FLAG_CANCELED           0x00008000 // Pointer is departing in an abnormal manner
+#define POINTER_FLAG_DOWN               0x00010000 // Pointer transitioned to down state (made contact)
+#define POINTER_FLAG_UPDATE             0x00020000 // Pointer update
+#define POINTER_FLAG_UP                 0x00040000 // Pointer transitioned from down state (broke contact)
+#define POINTER_FLAG_WHEEL              0x00080000 // Vertical wheel
+#define POINTER_FLAG_HWHEEL             0x00100000 // Horizontal wheel
+#define POINTER_FLAG_CAPTURECHANGED     0x00200000 // Lost capture
+
+#endif // TOUCH_FEEDBACK_DEFAULT
+
+#define TOUCH_FLAGS_CONTACTUPDATE (POINTER_FLAG_UPDATE|POINTER_FLAG_INRANGE|POINTER_FLAG_INCONTACT)
+#define TOUCH_FLAGS_CONTACTDOWN (POINTER_FLAG_DOWN|POINTER_FLAG_INRANGE|POINTER_FLAG_INCONTACT)
+
+typedef BOOL (WINAPI* InitializeTouchInjectionPtr)(UINT32 maxCount, DWORD dwMode);
+typedef BOOL (WINAPI* InjectTouchInputPtr)(UINT32 count, CONST POINTER_TOUCH_INFO *info);
+
+#endif // touchinjection_sdk80_h
\ No newline at end of file