Bug 593372 - Part 2: Ensure that Elantech driver helper window doesn't prevent zoom gestures from working (v2.1) r=jmathies a=blocking-final
authorCameron McCormack <cam@mcc.id.au>
Thu, 27 Jan 2011 19:54:30 +1300
changeset 62184 c0ee311faca1713c4e30d1dd1bb9916210e6c41e
parent 62183 87fb25283d8d2ff4b2c6232d46278e93c7d8e159
child 62185 0da0c99b690d1f222e5676d04706f8eeab93a332
push id18633
push usereakhgari@mozilla.com
push dateTue, 08 Feb 2011 23:07:49 +0000
treeherdermozilla-central@d2db6c6be747 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjmathies, blocking-final
bugs593372
milestone2.0b12pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 593372 - Part 2: Ensure that Elantech driver helper window doesn't prevent zoom gestures from working (v2.1) r=jmathies a=blocking-final
widget/src/windows/nsWindow.cpp
widget/src/windows/nsWindow.h
--- a/widget/src/windows/nsWindow.cpp
+++ b/widget/src/windows/nsWindow.cpp
@@ -7598,16 +7598,135 @@ HWND nsWindow::FindOurProcessWindow(HWND
   for (HWND wnd = ::GetParent(aHWND); wnd; wnd = ::GetParent(wnd)) {
     if (IsOurProcessWindow(wnd)) {
       return wnd;
     }
   }
   return nsnull;
 }
 
+static PRBool PointInWindow(HWND aHWND, const POINT& aPoint)
+{
+  RECT bounds;
+  if (!::GetWindowRect(aHWND, &bounds)) {
+    return PR_FALSE;
+  }
+
+  if (aPoint.x < bounds.left
+      || aPoint.x >= bounds.right
+      || aPoint.y < bounds.top
+      || aPoint.y >= bounds.bottom) {
+    return PR_FALSE;
+  }
+
+  return PR_TRUE;
+}
+
+static HWND FindTopmostWindowAtPoint(HWND aHWND, const POINT& aPoint)
+{
+  if (!::IsWindowVisible(aHWND) || !PointInWindow(aHWND, aPoint)) {
+    return 0;
+  }
+
+  HWND childWnd = ::GetTopWindow(aHWND);
+  while (childWnd) {
+    HWND topmostWnd = FindTopmostWindowAtPoint(childWnd, aPoint);
+    if (topmostWnd) {
+      return topmostWnd;
+    }
+    childWnd = ::GetNextWindow(childWnd, GW_HWNDNEXT);
+  }
+
+  return aHWND;
+}
+
+struct FindOurWindowAtPointInfo
+{
+  POINT mInPoint;
+  HWND mOutHWND;
+};
+
+/* static */
+BOOL CALLBACK nsWindow::FindOurWindowAtPointCallback(HWND aHWND, LPARAM aLPARAM)
+{
+  if (!nsWindow::IsOurProcessWindow(aHWND)) {
+    // This isn't one of our top-level windows; continue enumerating.
+    return TRUE;
+  }
+
+  // Get the top-most child window under the point.  If there's no child
+  // window, and the point is within the top-level window, then the top-level
+  // window will be returned.  (This is the usual case.  A child window
+  // would be returned for plugins.)
+  FindOurWindowAtPointInfo* info = reinterpret_cast<FindOurWindowAtPointInfo*>(aLPARAM);
+  HWND childWnd = FindTopmostWindowAtPoint(aHWND, info->mInPoint);
+  if (!childWnd) {
+    // This window doesn't contain the point; continue enumerating.
+    return TRUE;
+  }
+
+  // Return the HWND and stop enumerating.
+  info->mOutHWND = childWnd;
+  return FALSE;
+}
+
+/* static */
+HWND nsWindow::FindOurWindowAtPoint(const POINT& aPoint)
+{
+  FindOurWindowAtPointInfo info;
+  info.mInPoint = aPoint;
+  info.mOutHWND = 0;
+
+  // This will enumerate all top-level windows in order from top to bottom.
+  EnumWindows(FindOurWindowAtPointCallback, reinterpret_cast<LPARAM>(&info));
+  return info.mOutHWND;
+}
+
+typedef DWORD (*GetProcessImageFileNameProc)(HANDLE, LPTSTR, DWORD);
+
+// Determine whether the given HWND is the handle for the Elantech helper
+// window.  The helper window cannot be distinguished based on its
+// window class, so we need to check if it is owned by the helper process,
+// ETDCtrl.exe.
+static PRBool IsElantechHelperWindow(HWND aHWND)
+{
+  static HMODULE hPSAPI = ::LoadLibrary(L"psapi.dll");
+  static GetProcessImageFileNameProc pGetProcessImageFileName =
+    reinterpret_cast<GetProcessImageFileNameProc>(::GetProcAddress(hPSAPI, "GetProcessImageFileNameW"));
+
+  if (!pGetProcessImageFileName) {
+    return PR_FALSE;
+  }
+
+  const PRUnichar* filenameSuffix = L"\\etdctrl.exe";
+  const int filenameSuffixLength = 12;
+
+  DWORD pid;
+  ::GetWindowThreadProcessId(aHWND, &pid);
+
+  PRBool result = PR_FALSE;
+
+  HANDLE hProcess = ::OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid);
+  if (hProcess) {
+    PRUnichar path[256];
+    if (pGetProcessImageFileName(hProcess, path, sizeof path)) {
+      path[255] = 0;
+      int pathLength = lstrlen(path);
+      if (pathLength >= filenameSuffixLength) {
+        if (lstrcmpi(path + pathLength - filenameSuffixLength, filenameSuffix) == 0) {
+          result = PR_TRUE;
+        }
+      }
+    }
+    ::CloseHandle(hProcess);
+  }
+
+  return result;
+}
+
 // Scrolling helper function for handling plugins.  
 // Return value indicates whether the calling function should handle this
 // aHandled indicates whether this was handled at all
 // aQuitProcessing tells whether or not to continue processing the message
 PRBool nsWindow::HandleScrollingPlugins(UINT aMsg, WPARAM aWParam,
                                         LPARAM aLParam, PRBool& aHandled,
                                         LRESULT* aRetValue,
                                         PRBool& aQuitProcessing)
@@ -7652,16 +7771,24 @@ PRBool nsWindow::HandleScrollingPlugins(
   }
 
   HWND destWnd = ::WindowFromPoint(point);
   // Since we receive scroll events for as long as
   // we are focused, it's entirely possible that there
   // is another app's window or no window under the
   // pointer.
 
+  if (sUseElantechGestureHacks && IsElantechHelperWindow(destWnd)) {
+    // The Elantech driver places a window right underneath the cursor
+    // when sending a WM_MOUSEWHEEL event to us as part of a pinch-to-zoom
+    // gesture.  We detect that here, and search for our window that would
+    // be beneath the cursor if that window wasn't there.
+    destWnd = FindOurWindowAtPoint(point);
+  }
+
   if (!destWnd) {
     // No window is under the pointer
     return PR_FALSE; // break, but continue processing
   }
 
   nsWindow* destWindow;
 
   // We don't handle the message if the found window belongs to another
@@ -8986,18 +9113,18 @@ IsObsoleteElantechDriver()
   if (!foundKey)
     return PR_FALSE;
 
   // Assume that the major version number can be found just after a space
   // or at the start of the string.
   for (PRUnichar* p = buf; *p; p++) {
     if (*p >= L'0' && *p <= L'9' && (p == buf || *(p - 1) == L' ')) {
       int majorVersion = wcstol(p, NULL, 10);
-      // Version 7 needs the hack.
-      if (majorVersion == 7)
+      // Versions 7 and 8 need the hack.
+      if (majorVersion == 7 || majorVersion == 8)
         return PR_TRUE;
     }
   }
 
   return PR_FALSE;
 }
 
 void nsWindow::InitInputWorkaroundPrefDefaults()
--- a/widget/src/windows/nsWindow.h
+++ b/widget/src/windows/nsWindow.h
@@ -312,16 +312,17 @@ protected:
   static LRESULT CALLBACK MozSpecialWndProc(int code, WPARAM wParam, LPARAM lParam);
   static LRESULT CALLBACK MozSpecialMouseProc(int code, WPARAM wParam, LPARAM lParam);
   static VOID    CALLBACK HookTimerForPopups( HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime );
   static BOOL    CALLBACK ClearResourcesCallback(HWND aChild, LPARAM aParam);
   static BOOL    CALLBACK EnumAllChildWindProc(HWND aWnd, LPARAM aParam);
   static BOOL    CALLBACK EnumAllThreadWindowProc(HWND aWnd, LPARAM aParam);
   static void             AllowD3D9Callback(nsWindow *aWindow);
   static void             AllowD3D9WithReinitializeCallback(nsWindow *aWindow);
+  static BOOL CALLBACK    FindOurWindowAtPointCallback(HWND aHWND, LPARAM aLPARAM);
 
   /**
    * Window utilities
    */
   static BOOL             SetNSWindowPtr(HWND aWnd, nsWindow * ptr);
   LPARAM                  lParamToScreen(LPARAM lParam);
   LPARAM                  lParamToClient(LPARAM lParam);
   virtual void            SubclassWindow(BOOL bState);
@@ -337,18 +338,19 @@ protected:
   static PRBool           GetInputWorkaroundPref(const char* aPrefName, PRBool aValueIfAutomatic);
   static PRBool           UseTrackPointHack();
   static void             PerformElantechSwipeGestureHack(UINT& aVirtualKeyCode, nsModifierKeyState& aModKeyState);
   static void             GetMainWindowClass(nsAString& aClass);
   PRBool                  HasGlass() const {
     return mTransparencyMode == eTransparencyGlass ||
            mTransparencyMode == eTransparencyBorderlessGlass;
   }
-  PRBool                  IsOurProcessWindow(HWND aHWND);
-  HWND                    FindOurProcessWindow(HWND aHWND);
+  static PRBool           IsOurProcessWindow(HWND aHWND);
+  static HWND             FindOurProcessWindow(HWND aHWND);
+  static HWND             FindOurWindowAtPoint(const POINT& aPoint);
 
   /**
    * Event processing helpers
    */
   PRBool                  DispatchPluginEvent(const MSG &aMsg);
   PRBool                  DispatchFocusToTopLevelWindow(PRUint32 aEventType);
   PRBool                  DispatchFocus(PRUint32 aEventType);
   PRBool                  DispatchStandardEvent(PRUint32 aMsg);