From 558a75b0b56f07660c09bc22e37a259c80d11577 Mon Sep 17 00:00:00 2001 From: Cameron McCormack Date: Thu, 27 Jan 2011 19:54:30 +1300 Subject: [PATCH] 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 | 131 +++++++++++++++++++++++++++++++- widget/src/windows/nsWindow.h | 6 +- 2 files changed, 133 insertions(+), 4 deletions(-) diff --git a/widget/src/windows/nsWindow.cpp b/widget/src/windows/nsWindow.cpp index 8c1f3400cb6f..3125bf58fd0d 100644 --- a/widget/src/windows/nsWindow.cpp +++ b/widget/src/windows/nsWindow.cpp @@ -7603,6 +7603,125 @@ HWND nsWindow::FindOurProcessWindow(HWND aHWND) 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(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(&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(::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 @@ -7657,6 +7776,14 @@ PRBool nsWindow::HandleScrollingPlugins(UINT aMsg, WPARAM aWParam, // 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 @@ -8991,8 +9118,8 @@ IsObsoleteElantechDriver() 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; } } diff --git a/widget/src/windows/nsWindow.h b/widget/src/windows/nsWindow.h index f0f465dd2274..5e51c04c859e 100644 --- a/widget/src/windows/nsWindow.h +++ b/widget/src/windows/nsWindow.h @@ -317,6 +317,7 @@ protected: 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 @@ -342,8 +343,9 @@ protected: 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