Bug 593372 - Part 2: Ensure that Elantech driver helper window doesn't prevent zoom gestures from working r=jmathies a=blocking-betaN
authorCameron McCormack <cam@mcc.id.au>, Ehsan Akhgari <ehsan@mozilla.com>
Thu, 27 Jan 2011 19:54:30 +1300
changeset 62478 ace573ee54218966898d9b9aae0cc8e1e5951869
parent 62477 720d17e280230b308d75901f40bbff1e939cbcd5
child 62479 fa7c98cbc33959fd77d3939a81941945d53b062b
push id18737
push usereakhgari@mozilla.com
push dateSat, 12 Feb 2011 19:37:20 +0000
treeherdermozilla-central@e9899b77c6ea [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjmathies, blocking-betaN
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 r=jmathies a=blocking-betaN
widget/src/windows/nsWindow.cpp
widget/src/windows/nsWindow.h
--- a/widget/src/windows/nsWindow.cpp
+++ b/widget/src/windows/nsWindow.cpp
@@ -7585,16 +7585,134 @@ 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, LPWSTR, 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 = ::LoadLibraryW(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] = {L'\0'};
+    if (pGetProcessImageFileName(hProcess, path, NS_ARRAY_LENGTH(path))) {
+      int pathLength = lstrlenW(path);
+      if (pathLength >= filenameSuffixLength) {
+        if (lstrcmpiW(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)
@@ -7639,16 +7757,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
@@ -8973,18 +9099,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);