michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim: set ts=2 et sw=2 tw=80: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this file, michael@0: * You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "mozilla/DebugOnly.h" michael@0: michael@0: #ifdef MOZ_LOGGING michael@0: #define FORCE_PR_LOG /* Allow logging in the release build */ michael@0: #endif // MOZ_LOGGING michael@0: #include "prlog.h" michael@0: michael@0: #include "WinMouseScrollHandler.h" michael@0: #include "nsWindow.h" michael@0: #include "nsWindowDefs.h" michael@0: #include "KeyboardLayout.h" michael@0: #include "WinUtils.h" michael@0: #include "nsGkAtoms.h" michael@0: #include "nsIDOMWindowUtils.h" michael@0: #include "nsIDOMWheelEvent.h" michael@0: michael@0: #include "mozilla/MiscEvents.h" michael@0: #include "mozilla/MouseEvents.h" michael@0: #include "mozilla/Preferences.h" michael@0: #include "mozilla/WindowsVersion.h" michael@0: michael@0: #include michael@0: michael@0: namespace mozilla { michael@0: namespace widget { michael@0: michael@0: #ifdef PR_LOGGING michael@0: PRLogModuleInfo* gMouseScrollLog = nullptr; michael@0: michael@0: static const char* GetBoolName(bool aBool) michael@0: { michael@0: return aBool ? "TRUE" : "FALSE"; michael@0: } michael@0: michael@0: static void LogKeyStateImpl() michael@0: { michael@0: if (!PR_LOG_TEST(gMouseScrollLog, PR_LOG_DEBUG)) { michael@0: return; michael@0: } michael@0: BYTE keyboardState[256]; michael@0: if (::GetKeyboardState(keyboardState)) { michael@0: for (size_t i = 0; i < ArrayLength(keyboardState); i++) { michael@0: if (keyboardState[i]) { michael@0: PR_LOG(gMouseScrollLog, PR_LOG_DEBUG, michael@0: (" Current key state: keyboardState[0x%02X]=0x%02X (%s)", michael@0: i, keyboardState[i], michael@0: ((keyboardState[i] & 0x81) == 0x81) ? "Pressed and Toggled" : michael@0: (keyboardState[i] & 0x80) ? "Pressed" : michael@0: (keyboardState[i] & 0x01) ? "Toggled" : "Unknown")); michael@0: } michael@0: } michael@0: } else { michael@0: PR_LOG(gMouseScrollLog, PR_LOG_DEBUG, michael@0: ("MouseScroll::Device::Elantech::HandleKeyMessage(): Failed to print " michael@0: "current keyboard state")); michael@0: } michael@0: } michael@0: michael@0: #define LOG_KEYSTATE() LogKeyStateImpl() michael@0: #else // PR_LOGGING michael@0: #define LOG_KEYSTATE() michael@0: #endif michael@0: michael@0: MouseScrollHandler* MouseScrollHandler::sInstance = nullptr; michael@0: michael@0: bool MouseScrollHandler::Device::sFakeScrollableWindowNeeded = false; michael@0: michael@0: bool MouseScrollHandler::Device::Elantech::sUseSwipeHack = false; michael@0: bool MouseScrollHandler::Device::Elantech::sUsePinchHack = false; michael@0: DWORD MouseScrollHandler::Device::Elantech::sZoomUntil = 0; michael@0: michael@0: bool MouseScrollHandler::Device::SetPoint::sMightBeUsing = false; michael@0: michael@0: // The duration until timeout of events transaction. The value is 1.5 sec, michael@0: // it's just a magic number, it was suggested by Logitech's engineer, see michael@0: // bug 605648 comment 90. michael@0: #define DEFAULT_TIMEOUT_DURATION 1500 michael@0: michael@0: /****************************************************************************** michael@0: * michael@0: * MouseScrollHandler michael@0: * michael@0: ******************************************************************************/ michael@0: michael@0: /* static */ michael@0: POINTS michael@0: MouseScrollHandler::GetCurrentMessagePos() michael@0: { michael@0: if (SynthesizingEvent::IsSynthesizing()) { michael@0: return sInstance->mSynthesizingEvent->GetCursorPoint(); michael@0: } michael@0: DWORD pos = ::GetMessagePos(); michael@0: return MAKEPOINTS(pos); michael@0: } michael@0: michael@0: // Get rid of the GetMessagePos() API. michael@0: #define GetMessagePos() michael@0: michael@0: /* static */ michael@0: void michael@0: MouseScrollHandler::Initialize() michael@0: { michael@0: #ifdef PR_LOGGING michael@0: if (!gMouseScrollLog) { michael@0: gMouseScrollLog = PR_NewLogModule("MouseScrollHandlerWidgets"); michael@0: } michael@0: #endif michael@0: Device::Init(); michael@0: } michael@0: michael@0: /* static */ michael@0: void michael@0: MouseScrollHandler::Shutdown() michael@0: { michael@0: delete sInstance; michael@0: sInstance = nullptr; michael@0: } michael@0: michael@0: /* static */ michael@0: MouseScrollHandler* michael@0: MouseScrollHandler::GetInstance() michael@0: { michael@0: if (!sInstance) { michael@0: sInstance = new MouseScrollHandler(); michael@0: } michael@0: return sInstance; michael@0: } michael@0: michael@0: MouseScrollHandler::MouseScrollHandler() : michael@0: mIsWaitingInternalMessage(false), michael@0: mSynthesizingEvent(nullptr) michael@0: { michael@0: PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, michael@0: ("MouseScroll: Creating an instance, this=%p, sInstance=%p", michael@0: this, sInstance)); michael@0: } michael@0: michael@0: MouseScrollHandler::~MouseScrollHandler() michael@0: { michael@0: PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, michael@0: ("MouseScroll: Destroying an instance, this=%p, sInstance=%p", michael@0: this, sInstance)); michael@0: michael@0: delete mSynthesizingEvent; michael@0: } michael@0: michael@0: /* static */ michael@0: bool michael@0: MouseScrollHandler::NeedsMessage(UINT aMsg) michael@0: { michael@0: switch (aMsg) { michael@0: case WM_SETTINGCHANGE: michael@0: case WM_MOUSEWHEEL: michael@0: case WM_MOUSEHWHEEL: michael@0: case WM_HSCROLL: michael@0: case WM_VSCROLL: michael@0: case MOZ_WM_MOUSEVWHEEL: michael@0: case MOZ_WM_MOUSEHWHEEL: michael@0: case MOZ_WM_HSCROLL: michael@0: case MOZ_WM_VSCROLL: michael@0: case WM_KEYDOWN: michael@0: case WM_KEYUP: michael@0: return true; michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: /* static */ michael@0: bool michael@0: MouseScrollHandler::ProcessMessage(nsWindowBase* aWidget, UINT msg, michael@0: WPARAM wParam, LPARAM lParam, michael@0: MSGResult& aResult) michael@0: { michael@0: Device::Elantech::UpdateZoomUntil(); michael@0: michael@0: switch (msg) { michael@0: case WM_SETTINGCHANGE: michael@0: if (!sInstance) { michael@0: return false; michael@0: } michael@0: if (wParam == SPI_SETWHEELSCROLLLINES || michael@0: wParam == SPI_SETWHEELSCROLLCHARS) { michael@0: sInstance->mSystemSettings.MarkDirty(); michael@0: } michael@0: return false; michael@0: michael@0: case WM_MOUSEWHEEL: michael@0: case WM_MOUSEHWHEEL: michael@0: GetInstance()-> michael@0: ProcessNativeMouseWheelMessage(aWidget, msg, wParam, lParam); michael@0: sInstance->mSynthesizingEvent->NotifyNativeMessageHandlingFinished(); michael@0: // We don't need to call next wndproc for WM_MOUSEWHEEL and michael@0: // WM_MOUSEHWHEEL. We should consume them always. If the messages michael@0: // would be handled by our window again, it caused making infinite michael@0: // message loop. michael@0: aResult.mConsumed = true; michael@0: aResult.mResult = (msg != WM_MOUSEHWHEEL); michael@0: return true; michael@0: michael@0: case WM_HSCROLL: michael@0: case WM_VSCROLL: michael@0: aResult.mConsumed = michael@0: GetInstance()->ProcessNativeScrollMessage(aWidget, msg, wParam, lParam); michael@0: sInstance->mSynthesizingEvent->NotifyNativeMessageHandlingFinished(); michael@0: aResult.mResult = 0; michael@0: return true; michael@0: michael@0: case MOZ_WM_MOUSEVWHEEL: michael@0: case MOZ_WM_MOUSEHWHEEL: michael@0: GetInstance()->HandleMouseWheelMessage(aWidget, msg, wParam, lParam); michael@0: sInstance->mSynthesizingEvent->NotifyInternalMessageHandlingFinished(); michael@0: // Doesn't need to call next wndproc for internal wheel message. michael@0: aResult.mConsumed = true; michael@0: return true; michael@0: michael@0: case MOZ_WM_HSCROLL: michael@0: case MOZ_WM_VSCROLL: michael@0: GetInstance()-> michael@0: HandleScrollMessageAsMouseWheelMessage(aWidget, msg, wParam, lParam); michael@0: sInstance->mSynthesizingEvent->NotifyInternalMessageHandlingFinished(); michael@0: // Doesn't need to call next wndproc for internal scroll message. michael@0: aResult.mConsumed = true; michael@0: return true; michael@0: michael@0: case WM_KEYDOWN: michael@0: case WM_KEYUP: michael@0: PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, michael@0: ("MouseScroll::ProcessMessage(): aWidget=%p, " michael@0: "msg=%s(0x%04X), wParam=0x%02X, ::GetMessageTime()=%d", michael@0: aWidget, msg == WM_KEYDOWN ? "WM_KEYDOWN" : michael@0: msg == WM_KEYUP ? "WM_KEYUP" : "Unknown", msg, wParam, michael@0: ::GetMessageTime())); michael@0: LOG_KEYSTATE(); michael@0: if (Device::Elantech::HandleKeyMessage(aWidget, msg, wParam)) { michael@0: aResult.mResult = 0; michael@0: aResult.mConsumed = true; michael@0: return true; michael@0: } michael@0: return false; michael@0: michael@0: default: michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: /* static */ michael@0: nsresult michael@0: MouseScrollHandler::SynthesizeNativeMouseScrollEvent(nsWindowBase* aWidget, michael@0: const nsIntPoint& aPoint, michael@0: uint32_t aNativeMessage, michael@0: int32_t aDelta, michael@0: uint32_t aModifierFlags, michael@0: uint32_t aAdditionalFlags) michael@0: { michael@0: bool useFocusedWindow = michael@0: !(aAdditionalFlags & nsIDOMWindowUtils::MOUSESCROLL_PREFER_WIDGET_AT_POINT); michael@0: michael@0: POINT pt; michael@0: pt.x = aPoint.x; michael@0: pt.y = aPoint.y; michael@0: michael@0: HWND target = useFocusedWindow ? ::WindowFromPoint(pt) : ::GetFocus(); michael@0: NS_ENSURE_TRUE(target, NS_ERROR_FAILURE); michael@0: michael@0: WPARAM wParam = 0; michael@0: LPARAM lParam = 0; michael@0: switch (aNativeMessage) { michael@0: case WM_MOUSEWHEEL: michael@0: case WM_MOUSEHWHEEL: { michael@0: lParam = MAKELPARAM(pt.x, pt.y); michael@0: WORD mod = 0; michael@0: if (aModifierFlags & (nsIWidget::CTRL_L | nsIWidget::CTRL_R)) { michael@0: mod |= MK_CONTROL; michael@0: } michael@0: if (aModifierFlags & (nsIWidget::SHIFT_L | nsIWidget::SHIFT_R)) { michael@0: mod |= MK_SHIFT; michael@0: } michael@0: wParam = MAKEWPARAM(mod, aDelta); michael@0: break; michael@0: } michael@0: case WM_VSCROLL: michael@0: case WM_HSCROLL: michael@0: lParam = (aAdditionalFlags & michael@0: nsIDOMWindowUtils::MOUSESCROLL_WIN_SCROLL_LPARAM_NOT_NULL) ? michael@0: reinterpret_cast(target) : 0; michael@0: wParam = aDelta; michael@0: break; michael@0: default: michael@0: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: michael@0: // Ensure to make the instance. michael@0: GetInstance(); michael@0: michael@0: BYTE kbdState[256]; michael@0: memset(kbdState, 0, sizeof(kbdState)); michael@0: michael@0: nsAutoTArray keySequence; michael@0: WinUtils::SetupKeyModifiersSequence(&keySequence, aModifierFlags); michael@0: michael@0: for (uint32_t i = 0; i < keySequence.Length(); ++i) { michael@0: uint8_t key = keySequence[i].mGeneral; michael@0: uint8_t keySpecific = keySequence[i].mSpecific; michael@0: kbdState[key] = 0x81; // key is down and toggled on if appropriate michael@0: if (keySpecific) { michael@0: kbdState[keySpecific] = 0x81; michael@0: } michael@0: } michael@0: michael@0: if (!sInstance->mSynthesizingEvent) { michael@0: sInstance->mSynthesizingEvent = new SynthesizingEvent(); michael@0: } michael@0: michael@0: POINTS pts; michael@0: pts.x = static_cast(pt.x); michael@0: pts.y = static_cast(pt.y); michael@0: return sInstance->mSynthesizingEvent-> michael@0: Synthesize(pts, target, aNativeMessage, wParam, lParam, kbdState); michael@0: } michael@0: michael@0: /* static */ michael@0: bool michael@0: MouseScrollHandler::DispatchEvent(nsWindowBase* aWidget, michael@0: WidgetGUIEvent& aEvent) michael@0: { michael@0: // note, in metrofx, this will always return false for now michael@0: return aWidget->DispatchScrollEvent(&aEvent); michael@0: } michael@0: michael@0: /* static */ michael@0: void michael@0: MouseScrollHandler::InitEvent(nsWindowBase* aWidget, michael@0: WidgetGUIEvent& aEvent, michael@0: nsIntPoint* aPoint) michael@0: { michael@0: NS_ENSURE_TRUE_VOID(aWidget); michael@0: nsIntPoint point; michael@0: if (aPoint) { michael@0: point = *aPoint; michael@0: } else { michael@0: POINTS pts = GetCurrentMessagePos(); michael@0: POINT pt; michael@0: pt.x = pts.x; michael@0: pt.y = pts.y; michael@0: ::ScreenToClient(aWidget->GetWindowHandle(), &pt); michael@0: point.x = pt.x; michael@0: point.y = pt.y; michael@0: } michael@0: aWidget->InitEvent(aEvent, &point); michael@0: } michael@0: michael@0: /* static */ michael@0: ModifierKeyState michael@0: MouseScrollHandler::GetModifierKeyState(UINT aMessage) michael@0: { michael@0: ModifierKeyState result; michael@0: // Assume the Control key is down if the Elantech touchpad has sent the michael@0: // mis-ordered WM_KEYDOWN/WM_MOUSEWHEEL messages. (See the comment in michael@0: // MouseScrollHandler::Device::Elantech::HandleKeyMessage().) michael@0: if ((aMessage == MOZ_WM_MOUSEVWHEEL || aMessage == WM_MOUSEWHEEL) && michael@0: !result.IsControl() && Device::Elantech::IsZooming()) { michael@0: result.Set(MODIFIER_CONTROL); michael@0: } michael@0: return result; michael@0: } michael@0: michael@0: POINT michael@0: MouseScrollHandler::ComputeMessagePos(UINT aMessage, michael@0: WPARAM aWParam, michael@0: LPARAM aLParam) michael@0: { michael@0: POINT point; michael@0: if (Device::SetPoint::IsGetMessagePosResponseValid(aMessage, michael@0: aWParam, aLParam)) { michael@0: PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, michael@0: ("MouseScroll::ComputeMessagePos: Using ::GetCursorPos()")); michael@0: ::GetCursorPos(&point); michael@0: } else { michael@0: POINTS pts = GetCurrentMessagePos(); michael@0: point.x = pts.x; michael@0: point.y = pts.y; michael@0: } michael@0: return point; michael@0: } michael@0: michael@0: void michael@0: MouseScrollHandler::ProcessNativeMouseWheelMessage(nsWindowBase* aWidget, michael@0: UINT aMessage, michael@0: WPARAM aWParam, michael@0: LPARAM aLParam) michael@0: { michael@0: if (SynthesizingEvent::IsSynthesizing()) { michael@0: mSynthesizingEvent->NativeMessageReceived(aWidget, aMessage, michael@0: aWParam, aLParam); michael@0: } michael@0: michael@0: POINT point = ComputeMessagePos(aMessage, aWParam, aLParam); michael@0: michael@0: PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, michael@0: ("MouseScroll::ProcessNativeMouseWheelMessage: aWidget=%p, " michael@0: "aMessage=%s, wParam=0x%08X, lParam=0x%08X, point: { x=%d, y=%d }", michael@0: aWidget, aMessage == WM_MOUSEWHEEL ? "WM_MOUSEWHEEL" : michael@0: aMessage == WM_MOUSEHWHEEL ? "WM_MOUSEHWHEEL" : michael@0: aMessage == WM_VSCROLL ? "WM_VSCROLL" : "WM_HSCROLL", michael@0: aWParam, aLParam, point.x, point.y)); michael@0: LOG_KEYSTATE(); michael@0: michael@0: HWND underCursorWnd = ::WindowFromPoint(point); michael@0: if (!underCursorWnd) { michael@0: PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, michael@0: ("MouseScroll::ProcessNativeMouseWheelMessage: " michael@0: "No window is not found under the cursor")); michael@0: return; michael@0: } michael@0: michael@0: if (Device::Elantech::IsPinchHackNeeded() && michael@0: Device::Elantech::IsHelperWindow(underCursorWnd)) { michael@0: // The Elantech driver places a window right underneath the cursor michael@0: // when sending a WM_MOUSEWHEEL event to us as part of a pinch-to-zoom michael@0: // gesture. We detect that here, and search for our window that would michael@0: // be beneath the cursor if that window wasn't there. michael@0: underCursorWnd = WinUtils::FindOurWindowAtPoint(point); michael@0: if (!underCursorWnd) { michael@0: PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, michael@0: ("MouseScroll::ProcessNativeMouseWheelMessage: " michael@0: "Our window is not found under the Elantech helper window")); michael@0: return; michael@0: } michael@0: } michael@0: michael@0: // Handle most cases first. If the window under mouse cursor is our window michael@0: // except plugin window (MozillaWindowClass), we should handle the message michael@0: // on the window. michael@0: if (WinUtils::IsOurProcessWindow(underCursorWnd)) { michael@0: nsWindowBase* destWindow = WinUtils::GetNSWindowBasePtr(underCursorWnd); michael@0: if (!destWindow) { michael@0: PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, michael@0: ("MouseScroll::ProcessNativeMouseWheelMessage: " michael@0: "Found window under the cursor isn't managed by nsWindow...")); michael@0: HWND wnd = ::GetParent(underCursorWnd); michael@0: for (; wnd; wnd = ::GetParent(wnd)) { michael@0: destWindow = WinUtils::GetNSWindowBasePtr(wnd); michael@0: if (destWindow) { michael@0: break; michael@0: } michael@0: } michael@0: if (!wnd) { michael@0: PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, michael@0: ("MouseScroll::ProcessNativeMouseWheelMessage: Our window which is " michael@0: "managed by nsWindow is not found under the cursor")); michael@0: return; michael@0: } michael@0: } michael@0: michael@0: MOZ_ASSERT(destWindow, "destWindow must not be NULL"); michael@0: michael@0: // If the found window is our plugin window, it means that the message michael@0: // has been handled by the plugin but not consumed. We should handle the michael@0: // message on its parent window. However, note that the DOM event may michael@0: // cause accessing the plugin. Therefore, we should unlock the plugin michael@0: // process by using PostMessage(). michael@0: if (destWindow->WindowType() == eWindowType_plugin) { michael@0: destWindow = destWindow->GetParentWindowBase(false); michael@0: if (!destWindow) { michael@0: PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, michael@0: ("MouseScroll::ProcessNativeMouseWheelMessage: " michael@0: "Our window which is a parent of a plugin window is not found")); michael@0: return; michael@0: } michael@0: } michael@0: PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, michael@0: ("MouseScroll::ProcessNativeMouseWheelMessage: Succeeded, " michael@0: "Posting internal message to an nsWindow (%p)...", michael@0: destWindow)); michael@0: mIsWaitingInternalMessage = true; michael@0: UINT internalMessage = WinUtils::GetInternalMessage(aMessage); michael@0: ::PostMessage(destWindow->GetWindowHandle(), internalMessage, michael@0: aWParam, aLParam); michael@0: return; michael@0: } michael@0: michael@0: // If the window under cursor is not in our process, it means: michael@0: // 1. The window may be a plugin window (GeckoPluginWindow or its descendant). michael@0: // 2. The window may be another application's window. michael@0: HWND pluginWnd = WinUtils::FindOurProcessWindow(underCursorWnd); michael@0: if (!pluginWnd) { michael@0: // If there is no plugin window in ancestors of the window under cursor, michael@0: // the window is for another applications (case 2). michael@0: // We don't need to handle this message. michael@0: PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, michael@0: ("MouseScroll::ProcessNativeMouseWheelMessage: " michael@0: "Our window is not found under the cursor")); michael@0: return; michael@0: } michael@0: michael@0: // If we're a plugin window (MozillaWindowClass) and cursor in this window, michael@0: // the message shouldn't go to plugin's wndproc again. So, we should handle michael@0: // it on parent window. However, note that the DOM event may cause accessing michael@0: // the plugin. Therefore, we should unlock the plugin process by using michael@0: // PostMessage(). michael@0: if (aWidget->WindowType() == eWindowType_plugin && michael@0: aWidget->GetWindowHandle() == pluginWnd) { michael@0: nsWindowBase* destWindow = aWidget->GetParentWindowBase(false); michael@0: if (!destWindow) { michael@0: PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, michael@0: ("MouseScroll::ProcessNativeMouseWheelMessage: Our normal window which " michael@0: "is a parent of this plugin window is not found")); michael@0: return; michael@0: } michael@0: PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, michael@0: ("MouseScroll::ProcessNativeMouseWheelMessage: Succeeded, " michael@0: "Posting internal message to an nsWindow (%p) which is parent of this " michael@0: "plugin window...", michael@0: destWindow)); michael@0: mIsWaitingInternalMessage = true; michael@0: UINT internalMessage = WinUtils::GetInternalMessage(aMessage); michael@0: ::PostMessage(destWindow->GetWindowHandle(), internalMessage, michael@0: aWParam, aLParam); michael@0: return; michael@0: } michael@0: michael@0: // If the window is a part of plugin, we should post the message to it. michael@0: PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, michael@0: ("MouseScroll::ProcessNativeMouseWheelMessage: Succeeded, " michael@0: "Redirecting the message to a window which is a plugin child window")); michael@0: ::PostMessage(underCursorWnd, aMessage, aWParam, aLParam); michael@0: } michael@0: michael@0: bool michael@0: MouseScrollHandler::ProcessNativeScrollMessage(nsWindowBase* aWidget, michael@0: UINT aMessage, michael@0: WPARAM aWParam, michael@0: LPARAM aLParam) michael@0: { michael@0: if (aLParam || mUserPrefs.IsScrollMessageHandledAsWheelMessage()) { michael@0: // Scroll message generated by Thinkpad Trackpoint Driver or similar michael@0: // Treat as a mousewheel message and scroll appropriately michael@0: ProcessNativeMouseWheelMessage(aWidget, aMessage, aWParam, aLParam); michael@0: // Always consume the scroll message if we try to emulate mouse wheel michael@0: // action. michael@0: return true; michael@0: } michael@0: michael@0: if (SynthesizingEvent::IsSynthesizing()) { michael@0: mSynthesizingEvent->NativeMessageReceived(aWidget, aMessage, michael@0: aWParam, aLParam); michael@0: } michael@0: michael@0: PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, michael@0: ("MouseScroll::ProcessNativeScrollMessage: aWidget=%p, " michael@0: "aMessage=%s, wParam=0x%08X, lParam=0x%08X", michael@0: aWidget, aMessage == WM_VSCROLL ? "WM_VSCROLL" : "WM_HSCROLL", michael@0: aWParam, aLParam)); michael@0: michael@0: // Scroll message generated by external application michael@0: WidgetContentCommandEvent commandEvent(true, NS_CONTENT_COMMAND_SCROLL, michael@0: aWidget); michael@0: michael@0: commandEvent.mScroll.mIsHorizontal = (aMessage == WM_HSCROLL); michael@0: michael@0: switch (LOWORD(aWParam)) { michael@0: case SB_LINEUP: // SB_LINELEFT michael@0: commandEvent.mScroll.mUnit = michael@0: WidgetContentCommandEvent::eCmdScrollUnit_Line; michael@0: commandEvent.mScroll.mAmount = -1; michael@0: break; michael@0: case SB_LINEDOWN: // SB_LINERIGHT michael@0: commandEvent.mScroll.mUnit = michael@0: WidgetContentCommandEvent::eCmdScrollUnit_Line; michael@0: commandEvent.mScroll.mAmount = 1; michael@0: break; michael@0: case SB_PAGEUP: // SB_PAGELEFT michael@0: commandEvent.mScroll.mUnit = michael@0: WidgetContentCommandEvent::eCmdScrollUnit_Page; michael@0: commandEvent.mScroll.mAmount = -1; michael@0: break; michael@0: case SB_PAGEDOWN: // SB_PAGERIGHT michael@0: commandEvent.mScroll.mUnit = michael@0: WidgetContentCommandEvent::eCmdScrollUnit_Page; michael@0: commandEvent.mScroll.mAmount = 1; michael@0: break; michael@0: case SB_TOP: // SB_LEFT michael@0: commandEvent.mScroll.mUnit = michael@0: WidgetContentCommandEvent::eCmdScrollUnit_Whole; michael@0: commandEvent.mScroll.mAmount = -1; michael@0: break; michael@0: case SB_BOTTOM: // SB_RIGHT michael@0: commandEvent.mScroll.mUnit = michael@0: WidgetContentCommandEvent::eCmdScrollUnit_Whole; michael@0: commandEvent.mScroll.mAmount = 1; michael@0: break; michael@0: default: michael@0: return false; michael@0: } michael@0: // XXX If this is a plugin window, we should dispatch the event from michael@0: // parent window. michael@0: DispatchEvent(aWidget, commandEvent); michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: MouseScrollHandler::HandleMouseWheelMessage(nsWindowBase* aWidget, michael@0: UINT aMessage, michael@0: WPARAM aWParam, michael@0: LPARAM aLParam) michael@0: { michael@0: NS_ABORT_IF_FALSE( michael@0: (aMessage == MOZ_WM_MOUSEVWHEEL || aMessage == MOZ_WM_MOUSEHWHEEL), michael@0: "HandleMouseWheelMessage must be called with " michael@0: "MOZ_WM_MOUSEVWHEEL or MOZ_WM_MOUSEHWHEEL"); michael@0: michael@0: PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, michael@0: ("MouseScroll::HandleMouseWheelMessage: aWidget=%p, " michael@0: "aMessage=MOZ_WM_MOUSE%sWHEEL, aWParam=0x%08X, aLParam=0x%08X", michael@0: aWidget, aMessage == MOZ_WM_MOUSEVWHEEL ? "V" : "H", michael@0: aWParam, aLParam)); michael@0: michael@0: mIsWaitingInternalMessage = false; michael@0: michael@0: EventInfo eventInfo(aWidget, WinUtils::GetNativeMessage(aMessage), michael@0: aWParam, aLParam); michael@0: if (!eventInfo.CanDispatchWheelEvent()) { michael@0: PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, michael@0: ("MouseScroll::HandleMouseWheelMessage: Cannot dispatch the events")); michael@0: mLastEventInfo.ResetTransaction(); michael@0: return; michael@0: } michael@0: michael@0: // Discard the remaining delta if current wheel message and last one are michael@0: // received by different window or to scroll different direction or michael@0: // different unit scroll. Furthermore, if the last event was too old. michael@0: if (!mLastEventInfo.CanContinueTransaction(eventInfo)) { michael@0: mLastEventInfo.ResetTransaction(); michael@0: } michael@0: michael@0: mLastEventInfo.RecordEvent(eventInfo); michael@0: michael@0: ModifierKeyState modKeyState = GetModifierKeyState(aMessage); michael@0: michael@0: // Grab the widget, it might be destroyed by a DOM event handler. michael@0: nsRefPtr kungFuDethGrip(aWidget); michael@0: michael@0: WidgetWheelEvent wheelEvent(true, NS_WHEEL_WHEEL, aWidget); michael@0: if (mLastEventInfo.InitWheelEvent(aWidget, wheelEvent, modKeyState)) { michael@0: PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, michael@0: ("MouseScroll::HandleMouseWheelMessage: dispatching " michael@0: "NS_WHEEL_WHEEL event")); michael@0: DispatchEvent(aWidget, wheelEvent); michael@0: if (aWidget->Destroyed()) { michael@0: PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, michael@0: ("MouseScroll::HandleMouseWheelMessage: The window was destroyed " michael@0: "by NS_WHEEL_WHEEL event")); michael@0: mLastEventInfo.ResetTransaction(); michael@0: return; michael@0: } michael@0: } michael@0: #ifdef PR_LOGGING michael@0: else { michael@0: PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, michael@0: ("MouseScroll::HandleMouseWheelMessage: NS_WHEEL_WHEEL event is not " michael@0: "dispatched")); michael@0: } michael@0: #endif michael@0: } michael@0: michael@0: void michael@0: MouseScrollHandler::HandleScrollMessageAsMouseWheelMessage(nsWindowBase* aWidget, michael@0: UINT aMessage, michael@0: WPARAM aWParam, michael@0: LPARAM aLParam) michael@0: { michael@0: NS_ABORT_IF_FALSE( michael@0: (aMessage == MOZ_WM_VSCROLL || aMessage == MOZ_WM_HSCROLL), michael@0: "HandleScrollMessageAsMouseWheelMessage must be called with " michael@0: "MOZ_WM_VSCROLL or MOZ_WM_HSCROLL"); michael@0: michael@0: mIsWaitingInternalMessage = false; michael@0: michael@0: ModifierKeyState modKeyState = GetModifierKeyState(aMessage); michael@0: michael@0: WidgetWheelEvent wheelEvent(true, NS_WHEEL_WHEEL, aWidget); michael@0: double& delta = michael@0: (aMessage == MOZ_WM_VSCROLL) ? wheelEvent.deltaY : wheelEvent.deltaX; michael@0: int32_t& lineOrPageDelta = michael@0: (aMessage == MOZ_WM_VSCROLL) ? wheelEvent.lineOrPageDeltaY : michael@0: wheelEvent.lineOrPageDeltaX; michael@0: michael@0: delta = 1.0; michael@0: lineOrPageDelta = 1; michael@0: michael@0: switch (LOWORD(aWParam)) { michael@0: case SB_PAGEUP: michael@0: delta = -1.0; michael@0: lineOrPageDelta = -1; michael@0: case SB_PAGEDOWN: michael@0: wheelEvent.deltaMode = nsIDOMWheelEvent::DOM_DELTA_PAGE; michael@0: break; michael@0: michael@0: case SB_LINEUP: michael@0: delta = -1.0; michael@0: lineOrPageDelta = -1; michael@0: case SB_LINEDOWN: michael@0: wheelEvent.deltaMode = nsIDOMWheelEvent::DOM_DELTA_LINE; michael@0: break; michael@0: michael@0: default: michael@0: return; michael@0: } michael@0: modKeyState.InitInputEvent(wheelEvent); michael@0: // XXX Current mouse position may not be same as when the original message michael@0: // is received. We need to know the actual mouse cursor position when michael@0: // the original message was received. michael@0: InitEvent(aWidget, wheelEvent); michael@0: michael@0: PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, michael@0: ("MouseScroll::HandleScrollMessageAsMouseWheelMessage: aWidget=%p, " michael@0: "aMessage=MOZ_WM_%sSCROLL, aWParam=0x%08X, aLParam=0x%08X, " michael@0: "wheelEvent { refPoint: { x: %d, y: %d }, deltaX: %f, deltaY: %f, " michael@0: "lineOrPageDeltaX: %d, lineOrPageDeltaY: %d, " michael@0: "isShift: %s, isControl: %s, isAlt: %s, isMeta: %s }", michael@0: aWidget, (aMessage == MOZ_WM_VSCROLL) ? "V" : "H", aWParam, aLParam, michael@0: wheelEvent.refPoint.x, wheelEvent.refPoint.y, michael@0: wheelEvent.deltaX, wheelEvent.deltaY, michael@0: wheelEvent.lineOrPageDeltaX, wheelEvent.lineOrPageDeltaY, michael@0: GetBoolName(wheelEvent.IsShift()), michael@0: GetBoolName(wheelEvent.IsControl()), michael@0: GetBoolName(wheelEvent.IsAlt()), michael@0: GetBoolName(wheelEvent.IsMeta()))); michael@0: michael@0: DispatchEvent(aWidget, wheelEvent); michael@0: } michael@0: michael@0: /****************************************************************************** michael@0: * michael@0: * EventInfo michael@0: * michael@0: ******************************************************************************/ michael@0: michael@0: MouseScrollHandler::EventInfo::EventInfo(nsWindowBase* aWidget, michael@0: UINT aMessage, michael@0: WPARAM aWParam, LPARAM aLParam) michael@0: { michael@0: NS_ABORT_IF_FALSE(aMessage == WM_MOUSEWHEEL || aMessage == WM_MOUSEHWHEEL, michael@0: "EventInfo must be initialized with WM_MOUSEWHEEL or WM_MOUSEHWHEEL"); michael@0: michael@0: MouseScrollHandler::GetInstance()->mSystemSettings.Init(); michael@0: michael@0: mIsVertical = (aMessage == WM_MOUSEWHEEL); michael@0: mIsPage = MouseScrollHandler::sInstance-> michael@0: mSystemSettings.IsPageScroll(mIsVertical); michael@0: mDelta = (short)HIWORD(aWParam); michael@0: mWnd = aWidget->GetWindowHandle(); michael@0: mTimeStamp = TimeStamp::Now(); michael@0: } michael@0: michael@0: bool michael@0: MouseScrollHandler::EventInfo::CanDispatchWheelEvent() const michael@0: { michael@0: if (!GetScrollAmount()) { michael@0: // XXX I think that we should dispatch mouse wheel events even if the michael@0: // operation will not scroll because the wheel operation really happened michael@0: // and web application may want to handle the event for non-scroll action. michael@0: return false; michael@0: } michael@0: michael@0: return (mDelta != 0); michael@0: } michael@0: michael@0: int32_t michael@0: MouseScrollHandler::EventInfo::GetScrollAmount() const michael@0: { michael@0: if (mIsPage) { michael@0: return 1; michael@0: } michael@0: return MouseScrollHandler::sInstance-> michael@0: mSystemSettings.GetScrollAmount(mIsVertical); michael@0: } michael@0: michael@0: /****************************************************************************** michael@0: * michael@0: * LastEventInfo michael@0: * michael@0: ******************************************************************************/ michael@0: michael@0: bool michael@0: MouseScrollHandler::LastEventInfo::CanContinueTransaction( michael@0: const EventInfo& aNewEvent) michael@0: { michael@0: int32_t timeout = MouseScrollHandler::sInstance-> michael@0: mUserPrefs.GetMouseScrollTransactionTimeout(); michael@0: return !mWnd || michael@0: (mWnd == aNewEvent.GetWindowHandle() && michael@0: IsPositive() == aNewEvent.IsPositive() && michael@0: mIsVertical == aNewEvent.IsVertical() && michael@0: mIsPage == aNewEvent.IsPage() && michael@0: (timeout < 0 || michael@0: TimeStamp::Now() - mTimeStamp <= michael@0: TimeDuration::FromMilliseconds(timeout))); michael@0: } michael@0: michael@0: void michael@0: MouseScrollHandler::LastEventInfo::ResetTransaction() michael@0: { michael@0: if (!mWnd) { michael@0: return; michael@0: } michael@0: michael@0: PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, michael@0: ("MouseScroll::LastEventInfo::ResetTransaction()")); michael@0: michael@0: mWnd = nullptr; michael@0: mAccumulatedDelta = 0; michael@0: } michael@0: michael@0: void michael@0: MouseScrollHandler::LastEventInfo::RecordEvent(const EventInfo& aEvent) michael@0: { michael@0: mWnd = aEvent.GetWindowHandle(); michael@0: mDelta = aEvent.GetNativeDelta(); michael@0: mIsVertical = aEvent.IsVertical(); michael@0: mIsPage = aEvent.IsPage(); michael@0: mTimeStamp = TimeStamp::Now(); michael@0: } michael@0: michael@0: /* static */ michael@0: int32_t michael@0: MouseScrollHandler::LastEventInfo::RoundDelta(double aDelta) michael@0: { michael@0: return (aDelta >= 0) ? (int32_t)floor(aDelta) : (int32_t)ceil(aDelta); michael@0: } michael@0: michael@0: bool michael@0: MouseScrollHandler::LastEventInfo::InitWheelEvent( michael@0: nsWindowBase* aWidget, michael@0: WidgetWheelEvent& aWheelEvent, michael@0: const ModifierKeyState& aModKeyState) michael@0: { michael@0: MOZ_ASSERT(aWheelEvent.message == NS_WHEEL_WHEEL); michael@0: michael@0: // XXX Why don't we use lParam value? We should use lParam value because michael@0: // our internal message is always posted by original message handler. michael@0: // So, GetMessagePos() may return different cursor position. michael@0: InitEvent(aWidget, aWheelEvent); michael@0: michael@0: aModKeyState.InitInputEvent(aWheelEvent); michael@0: michael@0: // Our positive delta value means to bottom or right. michael@0: // But positive native delta value means to top or right. michael@0: // Use orienter for computing our delta value with native delta value. michael@0: int32_t orienter = mIsVertical ? -1 : 1; michael@0: michael@0: aWheelEvent.deltaMode = mIsPage ? nsIDOMWheelEvent::DOM_DELTA_PAGE : michael@0: nsIDOMWheelEvent::DOM_DELTA_LINE; michael@0: michael@0: double& delta = mIsVertical ? aWheelEvent.deltaY : aWheelEvent.deltaX; michael@0: int32_t& lineOrPageDelta = mIsVertical ? aWheelEvent.lineOrPageDeltaY : michael@0: aWheelEvent.lineOrPageDeltaX; michael@0: michael@0: double nativeDeltaPerUnit = michael@0: mIsPage ? static_cast(WHEEL_DELTA) : michael@0: static_cast(WHEEL_DELTA) / GetScrollAmount(); michael@0: michael@0: delta = static_cast(mDelta) * orienter / nativeDeltaPerUnit; michael@0: mAccumulatedDelta += mDelta; michael@0: lineOrPageDelta = michael@0: mAccumulatedDelta * orienter / RoundDelta(nativeDeltaPerUnit); michael@0: mAccumulatedDelta -= michael@0: lineOrPageDelta * orienter * RoundDelta(nativeDeltaPerUnit); michael@0: michael@0: PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, michael@0: ("MouseScroll::LastEventInfo::InitWheelEvent: aWidget=%p, " michael@0: "aWheelEvent { refPoint: { x: %d, y: %d }, deltaX: %f, deltaY: %f, " michael@0: "lineOrPageDeltaX: %d, lineOrPageDeltaY: %d, " michael@0: "isShift: %s, isControl: %s, isAlt: %s, isMeta: %s }, " michael@0: "mAccumulatedDelta: %d", michael@0: aWidget, aWheelEvent.refPoint.x, aWheelEvent.refPoint.y, michael@0: aWheelEvent.deltaX, aWheelEvent.deltaY, michael@0: aWheelEvent.lineOrPageDeltaX, aWheelEvent.lineOrPageDeltaY, michael@0: GetBoolName(aWheelEvent.IsShift()), michael@0: GetBoolName(aWheelEvent.IsControl()), michael@0: GetBoolName(aWheelEvent.IsAlt()), michael@0: GetBoolName(aWheelEvent.IsMeta()), mAccumulatedDelta)); michael@0: michael@0: return (delta != 0); michael@0: } michael@0: michael@0: /****************************************************************************** michael@0: * michael@0: * SystemSettings michael@0: * michael@0: ******************************************************************************/ michael@0: michael@0: void michael@0: MouseScrollHandler::SystemSettings::Init() michael@0: { michael@0: if (mInitialized) { michael@0: return; michael@0: } michael@0: michael@0: mInitialized = true; michael@0: michael@0: MouseScrollHandler::UserPrefs& userPrefs = michael@0: MouseScrollHandler::sInstance->mUserPrefs; michael@0: michael@0: mScrollLines = userPrefs.GetOverriddenVerticalScrollAmout(); michael@0: if (mScrollLines >= 0) { michael@0: // overridden by the pref. michael@0: PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, michael@0: ("MouseScroll::SystemSettings::Init(): mScrollLines is overridden by " michael@0: "the pref: %d", michael@0: mScrollLines)); michael@0: } else if (!::SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, michael@0: &mScrollLines, 0)) { michael@0: PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, michael@0: ("MouseScroll::SystemSettings::Init(): ::SystemParametersInfo(" michael@0: "SPI_GETWHEELSCROLLLINES) failed")); michael@0: mScrollLines = 3; michael@0: } michael@0: michael@0: if (mScrollLines > WHEEL_DELTA) { michael@0: PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, michael@0: ("MouseScroll::SystemSettings::Init(): the result of " michael@0: "::SystemParametersInfo(SPI_GETWHEELSCROLLLINES) is too large: %d", michael@0: mScrollLines)); michael@0: // sScrollLines usually equals 3 or 0 (for no scrolling) michael@0: // However, if sScrollLines > WHEEL_DELTA, we assume that michael@0: // the mouse driver wants a page scroll. The docs state that michael@0: // sScrollLines should explicitly equal WHEEL_PAGESCROLL, but michael@0: // since some mouse drivers use an arbitrary large number instead, michael@0: // we have to handle that as well. michael@0: mScrollLines = WHEEL_PAGESCROLL; michael@0: } michael@0: michael@0: mScrollChars = userPrefs.GetOverriddenHorizontalScrollAmout(); michael@0: if (mScrollChars >= 0) { michael@0: // overridden by the pref. michael@0: PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, michael@0: ("MouseScroll::SystemSettings::Init(): mScrollChars is overridden by " michael@0: "the pref: %d", michael@0: mScrollChars)); michael@0: } else if (!::SystemParametersInfo(SPI_GETWHEELSCROLLCHARS, 0, michael@0: &mScrollChars, 0)) { michael@0: PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, michael@0: ("MouseScroll::SystemSettings::Init(): ::SystemParametersInfo(" michael@0: "SPI_GETWHEELSCROLLCHARS) failed, %s", michael@0: IsVistaOrLater() ? michael@0: "this is unexpected on Vista or later" : michael@0: "but on XP or earlier, this is not a problem")); michael@0: mScrollChars = 1; michael@0: } michael@0: michael@0: if (mScrollChars > WHEEL_DELTA) { michael@0: PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, michael@0: ("MouseScroll::SystemSettings::Init(): the result of " michael@0: "::SystemParametersInfo(SPI_GETWHEELSCROLLCHARS) is too large: %d", michael@0: mScrollChars)); michael@0: // See the comments for the case mScrollLines > WHEEL_DELTA. michael@0: mScrollChars = WHEEL_PAGESCROLL; michael@0: } michael@0: michael@0: PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, michael@0: ("MouseScroll::SystemSettings::Init(): initialized, " michael@0: "mScrollLines=%d, mScrollChars=%d", michael@0: mScrollLines, mScrollChars)); michael@0: } michael@0: michael@0: void michael@0: MouseScrollHandler::SystemSettings::MarkDirty() michael@0: { michael@0: PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, michael@0: ("MouseScrollHandler::SystemSettings::MarkDirty(): " michael@0: "Marking SystemSettings dirty")); michael@0: mInitialized = false; michael@0: // When system settings are changed, we should reset current transaction. michael@0: MOZ_ASSERT(sInstance, michael@0: "Must not be called at initializing MouseScrollHandler"); michael@0: MouseScrollHandler::sInstance->mLastEventInfo.ResetTransaction(); michael@0: } michael@0: michael@0: /****************************************************************************** michael@0: * michael@0: * UserPrefs michael@0: * michael@0: ******************************************************************************/ michael@0: michael@0: MouseScrollHandler::UserPrefs::UserPrefs() : michael@0: mInitialized(false) michael@0: { michael@0: // We need to reset mouse wheel transaction when all of mousewheel related michael@0: // prefs are changed. michael@0: DebugOnly rv = michael@0: Preferences::RegisterCallback(OnChange, "mousewheel.", this); michael@0: MOZ_ASSERT(NS_SUCCEEDED(rv), michael@0: "Failed to register callback for mousewheel."); michael@0: } michael@0: michael@0: MouseScrollHandler::UserPrefs::~UserPrefs() michael@0: { michael@0: DebugOnly rv = michael@0: Preferences::UnregisterCallback(OnChange, "mousewheel.", this); michael@0: MOZ_ASSERT(NS_SUCCEEDED(rv), michael@0: "Failed to unregister callback for mousewheel."); michael@0: } michael@0: michael@0: void michael@0: MouseScrollHandler::UserPrefs::Init() michael@0: { michael@0: if (mInitialized) { michael@0: return; michael@0: } michael@0: michael@0: mInitialized = true; michael@0: michael@0: mScrollMessageHandledAsWheelMessage = michael@0: Preferences::GetBool("mousewheel.emulate_at_wm_scroll", false); michael@0: mOverriddenVerticalScrollAmount = michael@0: Preferences::GetInt("mousewheel.windows.vertical_amount_override", -1); michael@0: mOverriddenHorizontalScrollAmount = michael@0: Preferences::GetInt("mousewheel.windows.horizontal_amount_override", -1); michael@0: mMouseScrollTransactionTimeout = michael@0: Preferences::GetInt("mousewheel.windows.transaction.timeout", michael@0: DEFAULT_TIMEOUT_DURATION); michael@0: michael@0: PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, michael@0: ("MouseScroll::UserPrefs::Init(): initialized, " michael@0: "mScrollMessageHandledAsWheelMessage=%s, " michael@0: "mOverriddenVerticalScrollAmount=%d, " michael@0: "mOverriddenHorizontalScrollAmount=%d, " michael@0: "mMouseScrollTransactionTimeout=%d", michael@0: GetBoolName(mScrollMessageHandledAsWheelMessage), michael@0: mOverriddenVerticalScrollAmount, mOverriddenHorizontalScrollAmount, michael@0: mMouseScrollTransactionTimeout)); michael@0: } michael@0: michael@0: void michael@0: MouseScrollHandler::UserPrefs::MarkDirty() michael@0: { michael@0: PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, michael@0: ("MouseScrollHandler::UserPrefs::MarkDirty(): Marking UserPrefs dirty")); michael@0: mInitialized = false; michael@0: // Some prefs might override system settings, so, we should mark them dirty. michael@0: MouseScrollHandler::sInstance->mSystemSettings.MarkDirty(); michael@0: // When user prefs for mousewheel are changed, we should reset current michael@0: // transaction. michael@0: MOZ_ASSERT(sInstance, michael@0: "Must not be called at initializing MouseScrollHandler"); michael@0: MouseScrollHandler::sInstance->mLastEventInfo.ResetTransaction(); michael@0: } michael@0: michael@0: /****************************************************************************** michael@0: * michael@0: * Device michael@0: * michael@0: ******************************************************************************/ michael@0: michael@0: /* static */ michael@0: bool michael@0: MouseScrollHandler::Device::GetWorkaroundPref(const char* aPrefName, michael@0: bool aValueIfAutomatic) michael@0: { michael@0: if (!aPrefName) { michael@0: PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, michael@0: ("MouseScroll::Device::GetWorkaroundPref(): Failed, aPrefName is NULL")); michael@0: return aValueIfAutomatic; michael@0: } michael@0: michael@0: int32_t lHackValue = 0; michael@0: if (NS_FAILED(Preferences::GetInt(aPrefName, &lHackValue))) { michael@0: PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, michael@0: ("MouseScroll::Device::GetWorkaroundPref(): Preferences::GetInt() failed," michael@0: " aPrefName=\"%s\", aValueIfAutomatic=%s", michael@0: aPrefName, GetBoolName(aValueIfAutomatic))); michael@0: return aValueIfAutomatic; michael@0: } michael@0: michael@0: PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, michael@0: ("MouseScroll::Device::GetWorkaroundPref(): Succeeded, " michael@0: "aPrefName=\"%s\", aValueIfAutomatic=%s, lHackValue=%d", michael@0: aPrefName, GetBoolName(aValueIfAutomatic), lHackValue)); michael@0: michael@0: switch (lHackValue) { michael@0: case 0: // disabled michael@0: return false; michael@0: case 1: // enabled michael@0: return true; michael@0: default: // -1: autodetect michael@0: return aValueIfAutomatic; michael@0: } michael@0: } michael@0: michael@0: /* static */ michael@0: void michael@0: MouseScrollHandler::Device::Init() michael@0: { michael@0: // Not supported in metro mode. michael@0: if (XRE_GetWindowsEnvironment() == WindowsEnvironmentType_Metro) { michael@0: return; michael@0: } michael@0: michael@0: sFakeScrollableWindowNeeded = michael@0: GetWorkaroundPref("ui.trackpoint_hack.enabled", michael@0: (TrackPoint::IsDriverInstalled() || michael@0: UltraNav::IsObsoleteDriverInstalled())); michael@0: michael@0: PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, michael@0: ("MouseScroll::Device::Init(): sFakeScrollableWindowNeeded=%s", michael@0: GetBoolName(sFakeScrollableWindowNeeded))); michael@0: michael@0: Elantech::Init(); michael@0: } michael@0: michael@0: /****************************************************************************** michael@0: * michael@0: * Device::Elantech michael@0: * michael@0: ******************************************************************************/ michael@0: michael@0: /* static */ michael@0: void michael@0: MouseScrollHandler::Device::Elantech::Init() michael@0: { michael@0: int32_t version = GetDriverMajorVersion(); michael@0: bool needsHack = michael@0: Device::GetWorkaroundPref("ui.elantech_gesture_hacks.enabled", michael@0: version != 0); michael@0: sUseSwipeHack = needsHack && version <= 7; michael@0: sUsePinchHack = needsHack && version <= 8; michael@0: michael@0: PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, michael@0: ("MouseScroll::Device::Elantech::Init(): version=%d, sUseSwipeHack=%s, " michael@0: "sUsePinchHack=%s", michael@0: version, GetBoolName(sUseSwipeHack), GetBoolName(sUsePinchHack))); michael@0: } michael@0: michael@0: /* static */ michael@0: int32_t michael@0: MouseScrollHandler::Device::Elantech::GetDriverMajorVersion() michael@0: { michael@0: wchar_t buf[40]; michael@0: // The driver version is found in one of these two registry keys. michael@0: bool foundKey = michael@0: WinUtils::GetRegistryKey(HKEY_CURRENT_USER, michael@0: L"Software\\Elantech\\MainOption", michael@0: L"DriverVersion", michael@0: buf, sizeof buf); michael@0: if (!foundKey) { michael@0: foundKey = michael@0: WinUtils::GetRegistryKey(HKEY_CURRENT_USER, michael@0: L"Software\\Elantech", michael@0: L"DriverVersion", michael@0: buf, sizeof buf); michael@0: } michael@0: michael@0: if (!foundKey) { michael@0: return 0; michael@0: } michael@0: michael@0: // Assume that the major version number can be found just after a space michael@0: // or at the start of the string. michael@0: for (wchar_t* p = buf; *p; p++) { michael@0: if (*p >= L'0' && *p <= L'9' && (p == buf || *(p - 1) == L' ')) { michael@0: return wcstol(p, nullptr, 10); michael@0: } michael@0: } michael@0: michael@0: return 0; michael@0: } michael@0: michael@0: /* static */ michael@0: bool michael@0: MouseScrollHandler::Device::Elantech::IsHelperWindow(HWND aWnd) michael@0: { michael@0: // The helper window cannot be distinguished based on its window class, so we michael@0: // need to check if it is owned by the helper process, ETDCtrl.exe. michael@0: michael@0: const wchar_t* filenameSuffix = L"\\etdctrl.exe"; michael@0: const int filenameSuffixLength = 12; michael@0: michael@0: DWORD pid; michael@0: ::GetWindowThreadProcessId(aWnd, &pid); michael@0: michael@0: HANDLE hProcess = ::OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid); michael@0: if (!hProcess) { michael@0: return false; michael@0: } michael@0: michael@0: bool result = false; michael@0: wchar_t path[256] = {L'\0'}; michael@0: if (::GetProcessImageFileNameW(hProcess, path, ArrayLength(path))) { michael@0: int pathLength = lstrlenW(path); michael@0: if (pathLength >= filenameSuffixLength) { michael@0: if (lstrcmpiW(path + pathLength - filenameSuffixLength, michael@0: filenameSuffix) == 0) { michael@0: result = true; michael@0: } michael@0: } michael@0: } michael@0: ::CloseHandle(hProcess); michael@0: michael@0: return result; michael@0: } michael@0: michael@0: /* static */ michael@0: bool michael@0: MouseScrollHandler::Device::Elantech::HandleKeyMessage(nsWindowBase* aWidget, michael@0: UINT aMsg, michael@0: WPARAM aWParam) michael@0: { michael@0: // The Elantech touchpad driver understands three-finger swipe left and michael@0: // right gestures, and translates them into Page Up and Page Down key michael@0: // events for most applications. For Firefox 3.6, it instead sends michael@0: // Alt+Left and Alt+Right to trigger browser back/forward actions. As michael@0: // with the Thinkpad Driver hack in nsWindow::Create, the change in michael@0: // HWND structure makes Firefox not trigger the driver's heuristics michael@0: // any longer. michael@0: // michael@0: // The Elantech driver actually sends these messages for a three-finger michael@0: // swipe right: michael@0: // michael@0: // WM_KEYDOWN virtual_key = 0xCC or 0xFF (depending on driver version) michael@0: // WM_KEYDOWN virtual_key = VK_NEXT michael@0: // WM_KEYUP virtual_key = VK_NEXT michael@0: // WM_KEYUP virtual_key = 0xCC or 0xFF michael@0: // michael@0: // so we use the 0xCC or 0xFF key modifier to detect whether the Page Down michael@0: // is due to the gesture rather than a regular Page Down keypress. We then michael@0: // pretend that we should dispatch "Go Forward" command. Similarly michael@0: // for VK_PRIOR and "Go Back" command. michael@0: if (sUseSwipeHack && michael@0: (aWParam == VK_NEXT || aWParam == VK_PRIOR) && michael@0: (IS_VK_DOWN(0xFF) || IS_VK_DOWN(0xCC))) { michael@0: if (aMsg == WM_KEYDOWN) { michael@0: PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, michael@0: ("MouseScroll::Device::Elantech::HandleKeyMessage(): Dispatching " michael@0: "%s command event", michael@0: aWParam == VK_NEXT ? "Forward" : "Back")); michael@0: michael@0: WidgetCommandEvent commandEvent(true, nsGkAtoms::onAppCommand, michael@0: (aWParam == VK_NEXT) ? nsGkAtoms::Forward : nsGkAtoms::Back, aWidget); michael@0: InitEvent(aWidget, commandEvent); michael@0: MouseScrollHandler::DispatchEvent(aWidget, commandEvent); michael@0: } michael@0: #ifdef PR_LOGGING michael@0: else { michael@0: PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, michael@0: ("MouseScroll::Device::Elantech::HandleKeyMessage(): Consumed")); michael@0: } michael@0: #endif michael@0: return true; // consume the message (doesn't need to dispatch key events) michael@0: } michael@0: michael@0: // Version 8 of the Elantech touchpad driver sends these messages for michael@0: // zoom gestures: michael@0: // michael@0: // WM_KEYDOWN virtual_key = 0xCC time = 10 michael@0: // WM_KEYDOWN virtual_key = VK_CONTROL time = 10 michael@0: // WM_MOUSEWHEEL time = ::GetTickCount() michael@0: // WM_KEYUP virtual_key = VK_CONTROL time = 10 michael@0: // WM_KEYUP virtual_key = 0xCC time = 10 michael@0: // michael@0: // The result of this is that we process all of the WM_KEYDOWN/WM_KEYUP michael@0: // messages first because their timestamps make them appear to have michael@0: // been sent before the WM_MOUSEWHEEL message. To work around this, michael@0: // we store the current time when we process the WM_KEYUP message and michael@0: // assume that any WM_MOUSEWHEEL message with a timestamp before that michael@0: // time is one that should be processed as if the Control key was down. michael@0: if (sUsePinchHack && aMsg == WM_KEYUP && michael@0: aWParam == VK_CONTROL && ::GetMessageTime() == 10) { michael@0: // We look only at the bottom 31 bits of the system tick count since michael@0: // GetMessageTime returns a LONG, which is signed, so we want values michael@0: // that are more easily comparable. michael@0: sZoomUntil = ::GetTickCount() & 0x7FFFFFFF; michael@0: michael@0: PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, michael@0: ("MouseScroll::Device::Elantech::HandleKeyMessage(): sZoomUntil=%d", michael@0: sZoomUntil)); michael@0: } michael@0: michael@0: return false; michael@0: } michael@0: michael@0: /* static */ michael@0: void michael@0: MouseScrollHandler::Device::Elantech::UpdateZoomUntil() michael@0: { michael@0: if (!sZoomUntil) { michael@0: return; michael@0: } michael@0: michael@0: // For the Elantech Touchpad Zoom Gesture Hack, we should check that the michael@0: // system time (32-bit milliseconds) hasn't wrapped around. Otherwise we michael@0: // might get into the situation where wheel events for the next 50 days of michael@0: // system uptime are assumed to be Ctrl+Wheel events. (It is unlikely that michael@0: // we would get into that state, because the system would already need to be michael@0: // up for 50 days and the Control key message would need to be processed just michael@0: // before the system time overflow and the wheel message just after.) michael@0: // michael@0: // We also take the chance to reset sZoomUntil if we simply have passed that michael@0: // time. michael@0: LONG msgTime = ::GetMessageTime(); michael@0: if ((sZoomUntil >= 0x3fffffffu && DWORD(msgTime) < 0x40000000u) || michael@0: (sZoomUntil < DWORD(msgTime))) { michael@0: sZoomUntil = 0; michael@0: michael@0: PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, michael@0: ("MouseScroll::Device::Elantech::UpdateZoomUntil(): " michael@0: "sZoomUntil was reset")); michael@0: } michael@0: } michael@0: michael@0: /* static */ michael@0: bool michael@0: MouseScrollHandler::Device::Elantech::IsZooming() michael@0: { michael@0: // Assume the Control key is down if the Elantech touchpad has sent the michael@0: // mis-ordered WM_KEYDOWN/WM_MOUSEWHEEL messages. (See the comment in michael@0: // OnKeyUp.) michael@0: return (sZoomUntil && static_cast(::GetMessageTime()) < sZoomUntil); michael@0: } michael@0: michael@0: /****************************************************************************** michael@0: * michael@0: * Device::TrackPoint michael@0: * michael@0: ******************************************************************************/ michael@0: michael@0: /* static */ michael@0: bool michael@0: MouseScrollHandler::Device::TrackPoint::IsDriverInstalled() michael@0: { michael@0: if (WinUtils::HasRegistryKey(HKEY_CURRENT_USER, michael@0: L"Software\\Lenovo\\TrackPoint")) { michael@0: PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, michael@0: ("MouseScroll::Device::TrackPoint::IsDriverInstalled(): " michael@0: "Lenovo's TrackPoint driver is found")); michael@0: return true; michael@0: } michael@0: michael@0: if (WinUtils::HasRegistryKey(HKEY_CURRENT_USER, michael@0: L"Software\\Alps\\Apoint\\TrackPoint")) { michael@0: PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, michael@0: ("MouseScroll::Device::TrackPoint::IsDriverInstalled(): " michael@0: "Alps's TrackPoint driver is found")); michael@0: } michael@0: michael@0: return false; michael@0: } michael@0: michael@0: /****************************************************************************** michael@0: * michael@0: * Device::UltraNav michael@0: * michael@0: ******************************************************************************/ michael@0: michael@0: /* static */ michael@0: bool michael@0: MouseScrollHandler::Device::UltraNav::IsObsoleteDriverInstalled() michael@0: { michael@0: if (WinUtils::HasRegistryKey(HKEY_CURRENT_USER, michael@0: L"Software\\Lenovo\\UltraNav")) { michael@0: PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, michael@0: ("MouseScroll::Device::UltraNav::IsObsoleteDriverInstalled(): " michael@0: "Lenovo's UltraNav driver is found")); michael@0: return true; michael@0: } michael@0: michael@0: bool installed = false; michael@0: if (WinUtils::HasRegistryKey(HKEY_CURRENT_USER, michael@0: L"Software\\Synaptics\\SynTPEnh\\UltraNavUSB")) { michael@0: PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, michael@0: ("MouseScroll::Device::UltraNav::IsObsoleteDriverInstalled(): " michael@0: "Synaptics's UltraNav (USB) driver is found")); michael@0: installed = true; michael@0: } else if (WinUtils::HasRegistryKey(HKEY_CURRENT_USER, michael@0: L"Software\\Synaptics\\SynTPEnh\\UltraNavPS2")) { michael@0: PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, michael@0: ("MouseScroll::Device::UltraNav::IsObsoleteDriverInstalled(): " michael@0: "Synaptics's UltraNav (PS/2) driver is found")); michael@0: installed = true; michael@0: } michael@0: michael@0: if (!installed) { michael@0: return false; michael@0: } michael@0: michael@0: wchar_t buf[40]; michael@0: bool foundKey = michael@0: WinUtils::GetRegistryKey(HKEY_LOCAL_MACHINE, michael@0: L"Software\\Synaptics\\SynTP\\Install", michael@0: L"DriverVersion", michael@0: buf, sizeof buf); michael@0: if (!foundKey) { michael@0: PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, michael@0: ("MouseScroll::Device::UltraNav::IsObsoleteDriverInstalled(): " michael@0: "Failed to get UltraNav driver version")); michael@0: return false; michael@0: } michael@0: michael@0: int majorVersion = wcstol(buf, nullptr, 10); michael@0: int minorVersion = 0; michael@0: wchar_t* p = wcschr(buf, L'.'); michael@0: if (p) { michael@0: minorVersion = wcstol(p + 1, nullptr, 10); michael@0: } michael@0: PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, michael@0: ("MouseScroll::Device::UltraNav::IsObsoleteDriverInstalled(): " michael@0: "found driver version = %d.%d", michael@0: majorVersion, minorVersion)); michael@0: return majorVersion < 15 || (majorVersion == 15 && minorVersion == 0); michael@0: } michael@0: michael@0: /****************************************************************************** michael@0: * michael@0: * Device::SetPoint michael@0: * michael@0: ******************************************************************************/ michael@0: michael@0: /* static */ michael@0: bool michael@0: MouseScrollHandler::Device::SetPoint::IsGetMessagePosResponseValid( michael@0: UINT aMessage, michael@0: WPARAM aWParam, michael@0: LPARAM aLParam) michael@0: { michael@0: if (aMessage != WM_MOUSEHWHEEL) { michael@0: return false; michael@0: } michael@0: michael@0: POINTS pts = MouseScrollHandler::GetCurrentMessagePos(); michael@0: LPARAM messagePos = MAKELPARAM(pts.x, pts.y); michael@0: michael@0: // XXX We should check whether SetPoint is installed or not by registry. michael@0: michael@0: // SetPoint, Logitech (Logicool) mouse driver, (confirmed with 4.82.11 and michael@0: // MX-1100) always sets 0 to the lParam of WM_MOUSEHWHEEL. The driver SENDs michael@0: // one message at first time, this time, ::GetMessagePos() works fine. michael@0: // Then, we will return 0 (0 means we process it) to the message. Then, the michael@0: // driver will POST the same messages continuously during the wheel tilted. michael@0: // But ::GetMessagePos() API always returns (0, 0) for them, even if the michael@0: // actual mouse cursor isn't 0,0. Therefore, we cannot trust the result of michael@0: // ::GetMessagePos API if the sender is SetPoint. michael@0: if (!sMightBeUsing && !aLParam && aLParam != messagePos && michael@0: ::InSendMessage()) { michael@0: sMightBeUsing = true; michael@0: PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, michael@0: ("MouseScroll::Device::SetPoint::IsGetMessagePosResponseValid(): " michael@0: "Might using SetPoint")); michael@0: } else if (sMightBeUsing && aLParam != 0 && ::InSendMessage()) { michael@0: // The user has changed the mouse from Logitech's to another one (e.g., michael@0: // the user has changed to the touchpad of the notebook. michael@0: sMightBeUsing = false; michael@0: PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, michael@0: ("MouseScroll::Device::SetPoint::IsGetMessagePosResponseValid(): " michael@0: "Might stop using SetPoint")); michael@0: } michael@0: return (sMightBeUsing && !aLParam && !messagePos); michael@0: } michael@0: michael@0: /****************************************************************************** michael@0: * michael@0: * SynthesizingEvent michael@0: * michael@0: ******************************************************************************/ michael@0: michael@0: /* static */ michael@0: bool michael@0: MouseScrollHandler::SynthesizingEvent::IsSynthesizing() michael@0: { michael@0: return MouseScrollHandler::sInstance && michael@0: MouseScrollHandler::sInstance->mSynthesizingEvent && michael@0: MouseScrollHandler::sInstance->mSynthesizingEvent->mStatus != michael@0: NOT_SYNTHESIZING; michael@0: } michael@0: michael@0: nsresult michael@0: MouseScrollHandler::SynthesizingEvent::Synthesize(const POINTS& aCursorPoint, michael@0: HWND aWnd, michael@0: UINT aMessage, michael@0: WPARAM aWParam, michael@0: LPARAM aLParam, michael@0: const BYTE (&aKeyStates)[256]) michael@0: { michael@0: PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, michael@0: ("MouseScrollHandler::SynthesizingEvent::Synthesize(): aCursorPoint: { " michael@0: "x: %d, y: %d }, aWnd=0x%X, aMessage=0x%04X, aWParam=0x%08X, " michael@0: "aLParam=0x%08X, IsSynthesized()=%s, mStatus=%s", michael@0: aCursorPoint.x, aCursorPoint.y, aWnd, aMessage, aWParam, aLParam, michael@0: GetBoolName(IsSynthesizing()), GetStatusName())); michael@0: michael@0: if (IsSynthesizing()) { michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: michael@0: ::GetKeyboardState(mOriginalKeyState); michael@0: michael@0: // Note that we cannot use ::SetCursorPos() because it works asynchronously. michael@0: // We should SEND the message for reducing the possibility of receiving michael@0: // unexpected message which were not sent from here. michael@0: mCursorPoint = aCursorPoint; michael@0: michael@0: mWnd = aWnd; michael@0: mMessage = aMessage; michael@0: mWParam = aWParam; michael@0: mLParam = aLParam; michael@0: michael@0: memcpy(mKeyState, aKeyStates, sizeof(mKeyState)); michael@0: ::SetKeyboardState(mKeyState); michael@0: michael@0: mStatus = SENDING_MESSAGE; michael@0: michael@0: // Don't assume that aWnd is always managed by nsWindow. It might be michael@0: // a plugin window. michael@0: ::SendMessage(aWnd, aMessage, aWParam, aLParam); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: MouseScrollHandler::SynthesizingEvent::NativeMessageReceived(nsWindowBase* aWidget, michael@0: UINT aMessage, michael@0: WPARAM aWParam, michael@0: LPARAM aLParam) michael@0: { michael@0: if (mStatus == SENDING_MESSAGE && mMessage == aMessage && michael@0: mWParam == aWParam && mLParam == aLParam) { michael@0: mStatus = NATIVE_MESSAGE_RECEIVED; michael@0: if (aWidget && aWidget->GetWindowHandle() == mWnd) { michael@0: return; michael@0: } michael@0: // If the target window is not ours and received window is our plugin michael@0: // window, it comes from child window of the plugin. michael@0: if (aWidget && aWidget->WindowType() == eWindowType_plugin && michael@0: !WinUtils::GetNSWindowBasePtr(mWnd)) { michael@0: return; michael@0: } michael@0: // Otherwise, the message may not be sent by us. michael@0: } michael@0: michael@0: PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, michael@0: ("MouseScrollHandler::SynthesizingEvent::NativeMessageReceived(): " michael@0: "aWidget=%p, aWidget->GetWindowHandle()=0x%X, mWnd=0x%X, " michael@0: "aMessage=0x%04X, aWParam=0x%08X, aLParam=0x%08X, mStatus=%s", michael@0: aWidget, aWidget ? aWidget->GetWindowHandle() : 0, mWnd, michael@0: aMessage, aWParam, aLParam, GetStatusName())); michael@0: michael@0: // We failed to receive our sent message, we failed to do the job. michael@0: Finish(); michael@0: michael@0: return; michael@0: } michael@0: michael@0: void michael@0: MouseScrollHandler::SynthesizingEvent::NotifyNativeMessageHandlingFinished() michael@0: { michael@0: if (!IsSynthesizing()) { michael@0: return; michael@0: } michael@0: michael@0: PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, michael@0: ("MouseScrollHandler::SynthesizingEvent::" michael@0: "NotifyNativeMessageHandlingFinished(): IsWaitingInternalMessage=%s", michael@0: GetBoolName(MouseScrollHandler::IsWaitingInternalMessage()))); michael@0: michael@0: if (MouseScrollHandler::IsWaitingInternalMessage()) { michael@0: mStatus = INTERNAL_MESSAGE_POSTED; michael@0: return; michael@0: } michael@0: michael@0: // If the native message handler didn't post our internal message, michael@0: // we our job is finished. michael@0: // TODO: When we post the message to plugin window, there is remaning job. michael@0: Finish(); michael@0: } michael@0: michael@0: void michael@0: MouseScrollHandler::SynthesizingEvent::NotifyInternalMessageHandlingFinished() michael@0: { michael@0: if (!IsSynthesizing()) { michael@0: return; michael@0: } michael@0: michael@0: PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, michael@0: ("MouseScrollHandler::SynthesizingEvent::" michael@0: "NotifyInternalMessageHandlingFinished()")); michael@0: michael@0: Finish(); michael@0: } michael@0: michael@0: void michael@0: MouseScrollHandler::SynthesizingEvent::Finish() michael@0: { michael@0: if (!IsSynthesizing()) { michael@0: return; michael@0: } michael@0: michael@0: PR_LOG(gMouseScrollLog, PR_LOG_ALWAYS, michael@0: ("MouseScrollHandler::SynthesizingEvent::Finish()")); michael@0: michael@0: // Restore the original key state. michael@0: ::SetKeyboardState(mOriginalKeyState); michael@0: michael@0: mStatus = NOT_SYNTHESIZING; michael@0: } michael@0: michael@0: } // namespace widget michael@0: } // namespace mozilla