Bug 593372 - Part 4: Work around the Elantech v8 driver's erroneous message timestamps for zoom gestures r=jmathies a=blocking-betaN
authorCameron McCormack <cam@mcc.id.au>, Ehsan Akhgari <ehsan@mozilla.com>
Fri, 11 Feb 2011 20:56:13 -0500
changeset 62480 e9899b77c6ea78d8c178f9d8c64ece1766f1e778
parent 62479 fa7c98cbc33959fd77d3939a81941945d53b062b
child 62481 7ed4cb02ffbfcc335d38d54b0fb77d2fe45fd6e8
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 4: Work around the Elantech v8 driver's erroneous message timestamps for zoom gestures 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
@@ -428,16 +428,17 @@ nsWindow::nsWindow() : nsBaseWidget()
   mLastPoint.x          = 0;
   mLastPoint.y          = 0;
   mLastSize.width       = 0;
   mLastSize.height      = 0;
   mOldStyle             = 0;
   mOldExStyle           = 0;
   mPainting             = 0;
   mLastKeyboardLayout   = 0;
+  mAssumeWheelIsZoomUntil = 0;
   mBlurSuppressLevel    = 0;
   mIMEContext.mStatus   = nsIWidget::IME_STATUS_ENABLED;
 #ifdef MOZ_XUL
   mTransparentSurface   = nsnull;
   mMemoryDC             = nsnull;
   mTransparencyMode     = eTransparencyOpaque;
 #if MOZ_WINSDK_TARGETVER >= MOZ_NTDDI_LONGHORN
   memset(&mGlassMargins, 0, sizeof mGlassMargins);
@@ -4635,16 +4636,34 @@ nsWindow::ProcessMessageForPlugin(const 
   DispatchPendingEvents();
   return PR_TRUE;
 }
 
 // The main windows message processing method.
 PRBool nsWindow::ProcessMessage(UINT msg, WPARAM &wParam, LPARAM &lParam,
                                 LRESULT *aRetValue)
 {
+  // For the Elantech Touchpad Zoom Gesture Hack, we should check that the
+  // system time (32-bit milliseconds) hasn't wrapped around.  Otherwise we
+  // might get into the situation where wheel events for the next 50 days of
+  // system uptime are assumed to be Ctrl+Wheel events.  (It is unlikely that
+  // we would get into that state, because the system would already need to be
+  // up for 50 days and the Control key message would need to be processed just
+  // before the system time overflow and the wheel message just after.)
+  //
+  // We also take the chance to reset mAssumeWheelIsZoomUntil if we simply have
+  // passed that time.
+  if (mAssumeWheelIsZoomUntil) {
+    LONG msgTime = ::GetMessageTime();
+    if ((mAssumeWheelIsZoomUntil >= 0x3fffffffu && DWORD(msgTime) < 0x40000000u) ||
+        (mAssumeWheelIsZoomUntil < DWORD(msgTime))) {
+      mAssumeWheelIsZoomUntil = 0;
+    }
+  }
+
   // (Large blocks of code should be broken out into OnEvent handlers.)
   if (mWindowHook.Notify(mWnd, msg, wParam, lParam, aRetValue))
     return PR_TRUE;
 
 #if defined(EVENT_DEBUG_OUTPUT)
   // First param shows all events, second param indicates whether
   // to show mouse move events. See nsWindowDbg for details.
   PrintEvent(msg, SHOW_REPEAT_EVENTS, SHOW_MOUSEMOVE_EVENTS);
@@ -5009,16 +5028,17 @@ PRBool nsWindow::ProcessMessage(UINT msg
       DispatchPendingEvents();
     }
     break;
 
     case WM_SYSKEYUP:
     case WM_KEYUP:
     {
       MSG nativeMsg = InitMSG(msg, wParam, lParam);
+      nativeMsg.time = ::GetMessageTime();
       result = ProcessKeyUpMessage(nativeMsg, nsnull);
       DispatchPendingEvents();
     }
     break;
 
     case WM_SYSKEYDOWN:
     case WM_KEYDOWN:
     {
@@ -6688,18 +6708,29 @@ PRBool nsWindow::OnMouseWheel(UINT msg, 
   }
 
 #ifdef MOZ_IPC
   // The event may go to a plug-in which already dispatched this message.
   // Then, the event can cause deadlock.  We should unlock the sender here.
   ::ReplyMessage(isVertical ? 0 : TRUE);
 #endif
 
+  // Assume the Control key is down if the Elantech touchpad has sent the
+  // mis-ordered WM_KEYDOWN/WM_MOUSEWHEEL messages.  (See the comment in
+  // OnKeyUp.)
+  PRBool isControl;
+  if (mAssumeWheelIsZoomUntil &&
+      static_cast<DWORD>(::GetMessageTime()) < mAssumeWheelIsZoomUntil) {
+    isControl = PR_TRUE;
+  } else {
+    isControl = IS_VK_DOWN(NS_VK_CONTROL);
+  }
+
   scrollEvent.isShift   = IS_VK_DOWN(NS_VK_SHIFT);
-  scrollEvent.isControl = IS_VK_DOWN(NS_VK_CONTROL);
+  scrollEvent.isControl = isControl;
   scrollEvent.isMeta    = PR_FALSE;
   scrollEvent.isAlt     = IS_VK_DOWN(NS_VK_ALT);
   InitEvent(scrollEvent);
   if (nsnull != mEventCallback) {
     result = DispatchWindowEvent(&scrollEvent);
   }
   // Note that we should return zero if we process WM_MOUSEWHEEL.
   // But if we process WM_MOUSEHWHEEL, we should return non-zero.
@@ -7144,16 +7175,38 @@ LRESULT nsWindow::OnKeyDown(const MSG &a
 LRESULT nsWindow::OnKeyUp(const MSG &aMsg,
                           nsModifierKeyState &aModKeyState,
                           PRBool *aEventDispatched)
 {
   UINT virtualKeyCode = aMsg.wParam;
 
   if (sUseElantechGestureHacks) {
     PerformElantechSwipeGestureHack(virtualKeyCode, aModKeyState);
+
+    // Version 8 of the Elantech touchpad driver sends these messages for
+    // zoom gestures:
+    //
+    //   WM_KEYDOWN    virtual_key = 0xCC        time = 10
+    //   WM_KEYDOWN    virtual_key = VK_CONTROL  time = 10
+    //   WM_MOUSEWHEEL                           time = ::GetTickCount()
+    //   WM_KEYUP      virtual_key = VK_CONTROL  time = 10
+    //   WM_KEYUP      virtual_key = 0xCC        time = 10
+    //
+    // The result of this is that we process all of the WM_KEYDOWN/WM_KEYUP
+    // messages first because their timestamps make them appear to have
+    // been sent before the WM_MOUSEWHEEL message.  To work around this,
+    // we store the current time when we process the WM_KEYUP message and
+    // assume that any WM_MOUSEWHEEL message with a timestamp before that
+    // time is one that should be processed as if the Control key was down.
+    if (virtualKeyCode == VK_CONTROL && aMsg.time == 10) {
+      // We look only at the bottom 31 bits of the system tick count since
+      // GetMessageTime returns a LONG, which is signed, so we want values
+      // that are more easily comparable.
+      mAssumeWheelIsZoomUntil = ::GetTickCount() & 0x7FFFFFFF;
+    }
   }
 
   PR_LOG(gWindowsLog, PR_LOG_ALWAYS,
          ("nsWindow::OnKeyUp VK=%d\n", virtualKeyCode));
 
   if (!nsIMM32Handler::IsComposingOn(this)) {
     virtualKeyCode = MapFromNativeToDOM(virtualKeyCode);
   }
--- a/widget/src/windows/nsWindow.h
+++ b/widget/src/windows/nsWindow.h
@@ -526,16 +526,17 @@ protected:
   DWORD_PTR             mOldExStyle;
   HIMC                  mOldIMC;
   IMEContext            mIMEContext;
   nsNativeDragTarget*   mNativeDragTarget;
   HKL                   mLastKeyboardLayout;
   nsPopupType           mPopupType;
   nsSizeMode            mOldSizeMode;
   WindowHook            mWindowHook;
+  DWORD                 mAssumeWheelIsZoomUntil;
   static PRUint32       sInstanceCount;
   static TriStateBool   sCanQuit;
   static nsWindow*      sCurrentWindow;
   static BOOL           sIsOleInitialized;
   static HCURSOR        sHCursor;
   static imgIContainer* sCursorImgContainer;
   static PRBool         sSwitchKeyboardLayout;
   static PRBool         sJustGotDeactivate;