/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
// The duration until timeout of events transaction. The value is 1.5 sec, // it's just a magic number, it was suggested by Logitech's engineer, see // bug 605648 comment 90. #define DEFAULT_TIMEOUT_DURATION 1500
/* static */ bool MouseScrollHandler::NeedsMessage(UINT aMsg) { switch (aMsg) { case WM_SETTINGCHANGE: case WM_MOUSEWHEEL: case WM_MOUSEHWHEEL: case WM_HSCROLL: case WM_VSCROLL: case MOZ_WM_MOUSEVWHEEL: case MOZ_WM_MOUSEHWHEEL: case MOZ_WM_HSCROLL: case MOZ_WM_VSCROLL: case WM_KEYDOWN: case WM_KEYUP: returntrue;
} returnfalse;
}
bool MouseScrollHandler::ProcessMessageDirectly(UINT msg, WPARAM wParam,
LPARAM lParam,
MSGResult& aResult) { // This should never be entered recursively. Assert if that somehow // happens. (In release, bail out rather than crashing due to a stack // overflow, to give the user time to diagnose and report.) staticbool isRecursing = false;
MOZ_ASSERT(!isRecursing, "recursive event handler detected"); if (isRecursing) { returnfalse;
}
AutoRestore<bool> _restore{isRecursing};
isRecursing = true;
// Select the appropriate message handler. using HandlerT = bool (MouseScrollHandler::*)(nsWindow*, UINT, WPARAM, LPARAM);
HandlerT const handler = [&]() -> HandlerT { switch (msg) { case WM_MOUSEWHEEL: case WM_MOUSEHWHEEL: return &MouseScrollHandler::HandleMouseWheelMessage; case WM_VSCROLL: case WM_HSCROLL: if (lParam || mUserPrefs.IsScrollMessageHandledAsWheelMessage()) { return &MouseScrollHandler::HandleScrollMessageAsMouseWheelMessage;
} return &MouseScrollHandler::HandleScrollMessageAsItself; default:
MOZ_ASSERT(false, "wrong message type in ProcessMessageDirectly"); return nullptr;
}
}(); if (!handler) { returnfalse;
}
// Find the appropriate nsWindow to handle this message. (This is not // necessarily the window to which the message was sent!)
nsWindow* const destWindow = FindTargetWindow(msg, wParam, lParam); if (!destWindow) { // Not over our window; return without consuming. (This will not recurse.)
aResult.mConsumed = false; returntrue;
}
switch (msg) { case WM_SETTINGCHANGE: if (!sInstance) { returnfalse;
} if (wParam == SPI_SETWHEELSCROLLLINES ||
wParam == SPI_SETWHEELSCROLLCHARS) {
sInstance->mSystemSettings.MarkDirty();
} returnfalse;
case WM_MOUSEWHEEL: case WM_MOUSEHWHEEL: if (SkipScrollWheelHack()) { return GetInstance()->ProcessMessageDirectly(msg, wParam, lParam,
aResult);
}
GetInstance()->ProcessNativeMouseWheelMessage(aWidget, msg, wParam,
lParam); if (auto* synth = GetActiveSynthEvent()) {
synth->NotifyNativeMessageHandlingFinished();
} // We don't need to call next wndproc for WM_MOUSEWHEEL and // WM_MOUSEHWHEEL. We should consume them always. If the messages // would be handled by our window again, it caused making infinite // message loop.
aResult.mConsumed = true;
aResult.mResult = 0; returntrue;
case WM_HSCROLL: case WM_VSCROLL: if (SkipScrollWheelHack()) { return GetInstance()->ProcessMessageDirectly(msg, wParam, lParam,
aResult);
}
case MOZ_WM_MOUSEVWHEEL: case MOZ_WM_MOUSEHWHEEL:
GetInstance()->HandleMouseWheelMessage(aWidget, msg, wParam, lParam); if (auto* synth = GetActiveSynthEvent()) {
synth->NotifyInternalMessageHandlingFinished();
} // Doesn't need to call next wndproc for internal wheel message.
aResult.mConsumed = true; returntrue;
case MOZ_WM_HSCROLL: case MOZ_WM_VSCROLL:
GetInstance()->HandleScrollMessageAsMouseWheelMessage(aWidget, msg,
wParam, lParam); if (auto* synth = GetActiveSynthEvent()) {
synth->NotifyInternalMessageHandlingFinished();
} // Doesn't need to call next wndproc for internal scroll message.
aResult.mConsumed = true; returntrue;
for (uint32_t i = 0; i < keySequence.Length(); ++i) {
uint8_t key = keySequence[i].mGeneral;
uint8_t keySpecific = keySequence[i].mSpecific;
kbdState[key] = 0x81; // key is down and toggled on if appropriate if (keySpecific) {
kbdState[keySpecific] = 0x81;
}
}
if (!sInstance->mSynthesizingEvent) {
sInstance->mSynthesizingEvent = MakeUnique<SynthesizingEvent>();
}
// If a point is provided, use it; otherwise, get current message point or // synthetic point
POINTS pointOnScreen; if (aPoint != nullptr) {
pointOnScreen = MAKEPOINTS(*aPoint);
} else {
pointOnScreen = GetCurrentMessagePos();
}
// InitEvent expects the point to be in window coordinates, so translate the // point from screen coordinates.
POINT pointOnWindow;
POINTSTOPOINT(pointOnWindow, pointOnScreen);
::ScreenToClient(aWidget->GetWindowHandle(), &pointOnWindow);
/* static */
ModifierKeyState MouseScrollHandler::GetModifierKeyState(UINT aMessage) {
ModifierKeyState result; // Assume the Control key is down if the Elantech touchpad has sent the // mis-ordered WM_KEYDOWN/WM_MOUSEWHEEL messages. (See the comment in // MouseScrollHandler::Device::Elantech::HandleKeyMessage().) if ((aMessage == MOZ_WM_MOUSEVWHEEL || aMessage == WM_MOUSEWHEEL) &&
!result.IsControl() && Device::Elantech::IsZooming()) { // XXX Do we need to unset MODIFIER_SHIFT, MODIFIER_ALT, MODIFIER_META too? // If one of them are true, the default action becomes not zooming.
result.Unset(MODIFIER_ALTGRAPH);
result.Set(MODIFIER_CONTROL);
} return result;
}
POINT
MouseScrollHandler::ComputeMessagePos(UINT aMessage, WPARAM aWParam,
LPARAM aLParam) {
POINT point; if (Device::SetPoint::IsGetMessagePosResponseValid(aMessage, aWParam,
aLParam)) {
MOZ_LOG(gMouseScrollLog, LogLevel::Info,
("MouseScroll::ComputeMessagePos: Using ::GetCursorPos()"));
::GetCursorPos(&point);
} else {
POINTS pts = GetCurrentMessagePos();
point.x = pts.x;
point.y = pts.y;
} return point;
}
nsWindow* MouseScrollHandler::FindTargetWindow(UINT aMessage, WPARAM aWParam,
LPARAM aLParam) {
POINT point = ComputeMessagePos(aMessage, aWParam, aLParam);
HWND underCursorWnd = ::WindowFromPoint(point); if (!underCursorWnd) { // This is unsurprising: Windows ordinarily sends wheel messages to the // focused window, regardless of cursor position. (Nowadays, this is // configurable in Windows' settings, but we've always deliberately // overridden this behavior in Gecko; see bug 168354.)
MOZ_LOG(gMouseScrollLog, LogLevel::Info,
("MouseScroll::FindTargetWindow: " "No window was found under the cursor")); return nullptr;
}
if (Device::Elantech::IsPinchHackNeeded() &&
Device::Elantech::IsHelperWindow(underCursorWnd)) { // 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.
underCursorWnd = WinUtils::FindOurWindowAtPoint(point); if (!underCursorWnd) {
MOZ_LOG(gMouseScrollLog, LogLevel::Info,
("MouseScroll::FindTargetWindow: " "Our window is not found under the Elantech helper window")); return nullptr;
}
}
// If the window under the mouse cursor is not in our process, we assume it's // another application's window, and discard the message. if (!WinUtils::IsOurProcessWindow(underCursorWnd)) { return nullptr;
}
// Otherwise, (try to) handle this message on the nsWindow it's associated // with. if (nsWindow* destWindow = WinUtils::GetNSWindowPtr(underCursorWnd)) { return destWindow;
}
MOZ_LOG(gMouseScrollLog, LogLevel::Info,
("MouseScroll::FindTargetWindow: " "Window found under the cursor isn't an nsWindow..."));
HWND wnd = ::GetParent(underCursorWnd); for (; wnd; wnd = ::GetParent(wnd)) { if (nsWindow* destWindow = WinUtils::GetNSWindowPtr(wnd)) { return destWindow;
}
}
MOZ_LOG(gMouseScrollLog, LogLevel::Info,
("MouseScroll::FindTargetWindow: " " ...and doesn't have any nsWindow ancestors")); return nullptr;
}
// Some odd touchpad utils sets focus to window under the mouse cursor. // This emulates the odd behavior for debug. if (mUserPrefs.ShouldEmulateToMakeWindowUnderCursorForeground() &&
(aMessage == WM_MOUSEWHEEL || aMessage == WM_MOUSEHWHEEL) &&
::GetForegroundWindow() != destWindow->GetWindowHandle()) {
::SetForegroundWindow(destWindow->GetWindowHandle());
}
bool MouseScrollHandler::ProcessNativeScrollMessage(nsWindow* aWidget,
UINT aMessage,
WPARAM aWParam,
LPARAM aLParam) { if (aLParam || mUserPrefs.IsScrollMessageHandledAsWheelMessage()) { // Scroll message generated by Thinkpad Trackpoint Driver or similar // Treat as a mousewheel message and scroll appropriately
ProcessNativeMouseWheelMessage(aWidget, aMessage, aWParam, aLParam); // Always consume the scroll message if we try to emulate mouse wheel // action. returntrue;
}
bool MouseScrollHandler::HandleMouseWheelMessage(nsWindow* aWidget,
UINT aMessage, WPARAM aWParam,
LPARAM aLParam) { // for logging only constchar* const msgName [[maybe_unused]] = [&]() { switch (aMessage) { case MOZ_WM_MOUSEVWHEEL: return"MOZ_WM_MOUSEVWHEEL"; case MOZ_WM_MOUSEHWHEEL: return"MOZ_WM_MOUSEHWHEEL"; case WM_MOUSEWHEEL: return"WM_MOUSEWHEEL"; case WM_MOUSEHWHEEL: return"WM_MOUSEHWHEEL"; default: return"err";
}
}();
// direct call is OK if this pref is set; to simplify internal logic, adjust // message ID as though we had gone through the hack if (SkipScrollWheelHack()) { if (aMessage == WM_MOUSEWHEEL) {
aMessage = MOZ_WM_MOUSEVWHEEL;
} elseif (aMessage == WM_MOUSEHWHEEL) {
aMessage = MOZ_WM_MOUSEHWHEEL;
}
}
// N.B.: text here is not entirely accurate if SkipScrollWheelHack()
MOZ_ASSERT((aMessage == MOZ_WM_MOUSEVWHEEL || aMessage == MOZ_WM_MOUSEHWHEEL), "HandleMouseWheelMessage must be called with " "MOZ_WM_MOUSEVWHEEL or MOZ_WM_MOUSEHWHEEL");
// If it's not allowed to cache system settings, we need to reset the cache // before handling the mouse wheel message.
mSystemSettings.TrustedScrollSettingsDriver();
EventInfo eventInfo(aWidget, WinUtils::GetNativeMessage(aMessage), aWParam,
aLParam); if (!eventInfo.CanDispatchWheelEvent()) {
MOZ_LOG(
gMouseScrollLog, LogLevel::Info,
("MouseScroll::HandleMouseWheelMessage: Cannot dispatch the events"));
mLastEventInfo.ResetTransaction(); returnfalse;
}
// Discard the remaining delta if current wheel message and last one are // received by different window or to scroll different direction or // different unit scroll. Furthermore, if the last event was too old. if (!mLastEventInfo.CanContinueTransaction(eventInfo)) {
mLastEventInfo.ResetTransaction();
}
WidgetWheelEvent wheelEvent(true, eWheel, aWidget); if (mLastEventInfo.InitWheelEvent(aWidget, wheelEvent, modKeyState,
aLParam)) {
MOZ_LOG(gMouseScrollLog, LogLevel::Info,
("MouseScroll::HandleMouseWheelMessage: dispatching " "eWheel event"));
aWidget->DispatchWheelEvent(&wheelEvent); if (aWidget->Destroyed()) {
MOZ_LOG(gMouseScrollLog, LogLevel::Info,
("MouseScroll::HandleMouseWheelMessage: The window was destroyed " "by eWheel event"));
mLastEventInfo.ResetTransaction();
} returntrue;
}
MOZ_LOG(gMouseScrollLog, LogLevel::Info,
("MouseScroll::HandleMouseWheelMessage: eWheel event was not " "dispatched")); returnfalse;
}
bool MouseScrollHandler::HandleScrollMessageAsMouseWheelMessage(
nsWindow* aWidget, UINT aMessage, WPARAM aWParam, LPARAM aLParam) { // for logging only constchar* const msgName [[maybe_unused]] = [&]() { switch (aMessage) { case MOZ_WM_VSCROLL: return"MOZ_WM_VSCROLL"; case MOZ_WM_HSCROLL: return"MOZ_WM_HSCROLL"; case WM_VSCROLL: return"WM_VSCROLL"; case WM_HSCROLL: return"WM_HSCROLL"; default: return"err";
}
}();
// direct call is OK if this pref is set; to simplify internal logic, adjust // message ID as though we had gone through the hack if (SkipScrollWheelHack()) { if (aMessage == WM_VSCROLL) {
aMessage = MOZ_WM_VSCROLL;
} elseif (aMessage == WM_HSCROLL) {
aMessage = MOZ_WM_HSCROLL;
}
}
// N.B.: text here is not entirely accurate if SkipScrollWheelHack()
MOZ_ASSERT((aMessage == MOZ_WM_VSCROLL || aMessage == MOZ_WM_HSCROLL), "HandleScrollMessageAsMouseWheelMessage must be called with " "MOZ_WM_VSCROLL or MOZ_WM_HSCROLL");
// Current mouse position may not be same as when the original message // is received. However, this data is not available with the original // message, which is why nullptr is passed in. We need to know the actual // mouse cursor position when the original message was received.
InitEvent(aWidget, wheelEvent, nullptr);
bool MouseScrollHandler::EventInfo::CanDispatchWheelEvent() const { if (!GetScrollAmount()) { // XXX I think that we should dispatch mouse wheel events even if the // operation will not scroll because the wheel operation really happened // and web application may want to handle the event for non-scroll action. returnfalse;
}
// Our positive delta value means to bottom or right. // But positive native delta value means to top or right. // Use orienter for computing our delta value with native delta value.
int32_t orienter = mIsVertical ? -1 : 1;
if (aWheelEvent.mDeltaMode != dom::WheelEvent_Binding::DOM_DELTA_LINE) { // If the scroll delta mode isn't per line scroll, we shouldn't allow to // override the system scroll speed setting.
aWheelEvent.mAllowToOverrideSystemScrollSpeed = false;
}
if (mScrollLines > WHEEL_DELTA) {
MOZ_LOG(gMouseScrollLog, LogLevel::Info,
("MouseScroll::SystemSettings::InitScrollLines(): the result of " "::SystemParametersInfo(SPI_GETWHEELSCROLLLINES) is too large: %d",
mScrollLines)); // sScrollLines usually equals 3 or 0 (for no scrolling) // However, if sScrollLines > WHEEL_DELTA, we assume that // the mouse driver wants a page scroll. The docs state that // sScrollLines should explicitly equal WHEEL_PAGESCROLL, but // since some mouse drivers use an arbitrary large number instead, // we have to handle that as well.
mScrollLines = WHEEL_PAGESCROLL;
}
return oldValue != mScrollLines;
}
bool MouseScrollHandler::SystemSettings::InitScrollChars() {
int32_t oldValue = mInitialized ? mScrollChars : 0;
mIsReliableScrollChars = false;
mScrollChars = MouseScrollHandler::sInstance->mUserPrefs
.GetOverriddenHorizontalScrollAmout(); if (mScrollChars >= 0) { // overridden by the pref.
mIsReliableScrollChars = true;
MOZ_LOG(gMouseScrollLog, LogLevel::Info,
("MouseScroll::SystemSettings::InitScrollChars(): mScrollChars is " "overridden by the pref: %d",
mScrollChars));
} elseif (!::SystemParametersInfo(SPI_GETWHEELSCROLLCHARS, 0, &mScrollChars,
0)) {
MOZ_LOG(gMouseScrollLog, LogLevel::Info,
("MouseScroll::SystemSettings::InitScrollChars(): " "::SystemParametersInfo(" "SPI_GETWHEELSCROLLCHARS) failed, this is unexpected on Vista or " "later")); // XXX Should we use DefaultScrollChars()?
mScrollChars = 1;
}
if (mScrollChars > WHEEL_DELTA) {
MOZ_LOG(gMouseScrollLog, LogLevel::Info,
("MouseScroll::SystemSettings::InitScrollChars(): the result of " "::SystemParametersInfo(SPI_GETWHEELSCROLLCHARS) is too large: %d",
mScrollChars)); // See the comments for the case mScrollLines > WHEEL_DELTA.
mScrollChars = WHEEL_PAGESCROLL;
}
return oldValue != mScrollChars;
}
void MouseScrollHandler::SystemSettings::MarkDirty() {
MOZ_LOG(gMouseScrollLog, LogLevel::Info,
("MouseScrollHandler::SystemSettings::MarkDirty(): " "Marking SystemSettings dirty"));
mInitialized = false; // When system settings are changed, we should reset current transaction.
MOZ_ASSERT(sInstance, "Must not be called at initializing MouseScrollHandler");
MouseScrollHandler::sInstance->mLastEventInfo.ResetTransaction();
}
void MouseScrollHandler::SystemSettings::RefreshCache() { bool isChanged = InitScrollLines();
isChanged = InitScrollChars() || isChanged; if (!isChanged) { return;
} // If the scroll amount is changed, we should reset current transaction.
MOZ_ASSERT(sInstance, "Must not be called at initializing MouseScrollHandler");
MouseScrollHandler::sInstance->mLastEventInfo.ResetTransaction();
}
void MouseScrollHandler::SystemSettings::TrustedScrollSettingsDriver() { if (!mInitialized) { return;
}
// if the cache is initialized with prefs, we don't need to refresh it. if (mIsReliableScrollLines && mIsReliableScrollChars) { return;
}
// If system settings cache is disabled, we should always refresh them. if (!userPrefs.IsSystemSettingCacheEnabled()) {
RefreshCache(); return;
}
// If pref is set to as "always trust the cache", we shouldn't refresh them // in any environments. if (userPrefs.IsSystemSettingCacheForciblyEnabled()) { return;
}
// If SynTP of Synaptics or Apoint of Alps is installed, it may hook // ::SystemParametersInfo() and returns different value from system settings. if (Device::SynTP::IsDriverInstalled() ||
Device::Apoint::IsDriverInstalled()) {
RefreshCache(); return;
}
// XXX We're not sure about other touchpad drivers...
}
MouseScrollHandler::UserPrefs::UserPrefs() : mInitialized(false) { // We need to reset mouse wheel transaction when all of mousewheel related // prefs are changed.
DebugOnly<nsresult> rv =
Preferences::RegisterPrefixCallback(OnChange, "mousewheel.", this);
MOZ_ASSERT(NS_SUCCEEDED(rv), "Failed to register callback for mousewheel.");
}
MouseScrollHandler::UserPrefs::~UserPrefs() {
DebugOnly<nsresult> rv =
Preferences::UnregisterPrefixCallback(OnChange, "mousewheel.", this);
MOZ_ASSERT(NS_SUCCEEDED(rv), "Failed to unregister callback for mousewheel.");
}
void MouseScrollHandler::UserPrefs::Init() { if (mInitialized) { return;
}
void MouseScrollHandler::UserPrefs::MarkDirty() {
MOZ_LOG(
gMouseScrollLog, LogLevel::Info,
("MouseScrollHandler::UserPrefs::MarkDirty(): Marking UserPrefs dirty"));
mInitialized = false; // Some prefs might override system settings, so, we should mark them dirty.
MouseScrollHandler::sInstance->mSystemSettings.MarkDirty(); // When user prefs for mousewheel are changed, we should reset current // transaction.
MOZ_ASSERT(sInstance, "Must not be called at initializing MouseScrollHandler");
MouseScrollHandler::sInstance->mLastEventInfo.ResetTransaction();
}
/* static */ void MouseScrollHandler::Device::Init() { // FYI: Thinkpad's TrackPoint is Apoint of Alps and UltraNav is SynTP of // Synaptics. So, those drivers' information should be initialized // before calling methods of TrackPoint and UltraNav.
SynTP::Init();
Elantech::Init();
Apoint::Init();
/* static */
int32_t MouseScrollHandler::Device::Elantech::GetDriverMajorVersion() { wchar_t buf[40]; // The driver version is found in one of these two registry keys. if (!WinRegistry::GetString(
HKEY_CURRENT_USER, u"Software\\Elantech\\MainOption"_ns,
u"DriverVersion"_ns, buf, WinRegistry::kLegacyWinUtilsStringFlags) &&
!WinRegistry::GetString(HKEY_CURRENT_USER, u"Software\\Elantech"_ns,
u"DriverVersion"_ns, buf,
WinRegistry::kLegacyWinUtilsStringFlags)) { return 0;
}
// Assume that the major version number can be found just after a space // or at the start of the string. for (wchar_t* p = buf; *p; p++) { if (*p >= L'0' && *p <= L'9' && (p == buf || *(p - 1) == L' ')) { return wcstol(p, nullptr, 10);
}
}
return 0;
}
/* static */ bool MouseScrollHandler::Device::Elantech::IsHelperWindow(HWND aWnd) { // 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.
bool result = false; wchar_t path[256] = {L'\0'}; if (::GetProcessImageFileNameW(hProcess, path, std::size(path))) { int pathLength = lstrlenW(path); if (pathLength >= filenameSuffixLength) { if (lstrcmpiW(path + pathLength - filenameSuffixLength, filenameSuffix) ==
0) {
result = true;
}
}
}
::CloseHandle(hProcess);
return result;
}
/* static */ bool MouseScrollHandler::Device::Elantech::HandleKeyMessage(nsWindow* aWidget,
UINT aMsg,
WPARAM aWParam,
LPARAM aLParam) { // The Elantech touchpad driver understands three-finger swipe left and // right gestures, and translates them into Page Up and Page Down key // events for most applications. For Firefox 3.6, it instead sends // Alt+Left and Alt+Right to trigger browser back/forward actions. As // with the Thinkpad Driver hack in nsWindow::Create, the change in // HWND structure makes Firefox not trigger the driver's heuristics // any longer. // // The Elantech driver actually sends these messages for a three-finger // swipe right: // // WM_KEYDOWN virtual_key = 0xCC or 0xFF ScanCode = 00 // WM_KEYDOWN virtual_key = VK_NEXT ScanCode = 00 // WM_KEYUP virtual_key = VK_NEXT ScanCode = 00 // WM_KEYUP virtual_key = 0xCC or 0xFF ScanCode = 00 // // Whether 0xCC or 0xFF is sent is suspected to depend on the driver // version. 7.0.4.12_14Jul09_WHQL, 7.0.5.10, and 7.0.6.0 generate 0xCC. // 7.0.4.3 from Asus on EeePC generates 0xFF. // // On some hardware, IS_VK_DOWN(0xFF) returns true even when Elantech // messages are not involved, meaning that alone is not enough to // distinguish the gesture from a regular Page Up or Page Down key press. // The ScanCode is therefore also tested to detect the gesture. // We then pretend that we should dispatch "Go Forward" command. Similarly // for VK_PRIOR and "Go Back" command. if (sUseSwipeHack && (aWParam == VK_NEXT || aWParam == VK_PRIOR) &&
WinUtils::GetScanCode(aLParam) == 0 &&
(IS_VK_DOWN(0xFF) || IS_VK_DOWN(0xCC))) { if (aMsg == WM_KEYDOWN) {
MOZ_LOG(gMouseScrollLog, LogLevel::Info,
("MouseScroll::Device::Elantech::HandleKeyMessage(): Dispatching " "%s command event",
aWParam == VK_NEXT ? "Forward" : "Back"));
// In this scenario, the coordinate of the event isn't supplied, so pass // nullptr as an argument to indicate using the coordinate from the last // available window message.
InitEvent(aWidget, appCommandEvent, nullptr);
aWidget->DispatchWindowEvent(appCommandEvent);
} else {
MOZ_LOG(gMouseScrollLog, LogLevel::Info,
("MouseScroll::Device::Elantech::HandleKeyMessage(): Consumed"));
} returntrue; // consume the message (doesn't need to dispatch key events)
}
// 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 (sUsePinchHack && aMsg == WM_KEYUP && aWParam == VK_CONTROL &&
::GetMessageTime() == 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.
sZoomUntil = ::GetTickCount() & 0x7FFFFFFF;
// 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 sZoomUntil if we simply have passed that // time. LONG msgTime = ::GetMessageTime(); if ((sZoomUntil >= 0x3fffffffu && DWORD(msgTime) < 0x40000000u) ||
(sZoomUntil < DWORD(msgTime))) {
sZoomUntil = 0;
MOZ_LOG(gMouseScrollLog, LogLevel::Info,
("MouseScroll::Device::Elantech::UpdateZoomUntil(): " "sZoomUntil was reset"));
}
}
/* static */ bool MouseScrollHandler::Device::Elantech::IsZooming() { // 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.) return (sZoomUntil && static_cast<DWORD>(::GetMessageTime()) < sZoomUntil);
}
// XXX We should check whether SetPoint is installed or not by registry.
// SetPoint, Logitech (Logicool) mouse driver, (confirmed with 4.82.11 and // MX-1100) always sets 0 to the lParam of WM_MOUSEHWHEEL. The driver SENDs // one message at first time, this time, ::GetMessagePos() works fine. // Then, we will return 0 (0 means we process it) to the message. Then, the // driver will POST the same messages continuously during the wheel tilted. // But ::GetMessagePos() API always returns (0, 0) for them, even if the // actual mouse cursor isn't 0,0. Therefore, we cannot trust the result of // ::GetMessagePos API if the sender is SetPoint. if (!sMightBeUsing && !aLParam && aLParam != messagePos &&
::InSendMessage()) {
sMightBeUsing = true;
MOZ_LOG(gMouseScrollLog, LogLevel::Info,
("MouseScroll::Device::SetPoint::IsGetMessagePosResponseValid(): " "Might using SetPoint"));
} elseif (sMightBeUsing && aLParam != 0 && ::InSendMessage()) { // The user has changed the mouse from Logitech's to another one (e.g., // the user has changed to the touchpad of the notebook.
sMightBeUsing = false;
MOZ_LOG(gMouseScrollLog, LogLevel::Info,
("MouseScroll::Device::SetPoint::IsGetMessagePosResponseValid(): " "Might stop using SetPoint"));
} return (sMightBeUsing && !aLParam && !messagePos);
}
if (mStatus != NOT_SYNTHESIZING) { return NS_ERROR_NOT_AVAILABLE;
}
::GetKeyboardState(mOriginalKeyState);
// Note that we cannot use ::SetCursorPos() because it works asynchronously. // We should SEND the message for reducing the possibility of receiving // unexpected message which were not sent from here.
mCursorPoint = aCursorPoint;
if (MouseScrollHandler::IsWaitingInternalMessage()) {
mStatus = INTERNAL_MESSAGE_POSTED; return;
}
// If the native message handler didn't post our internal message, // we our job is finished. // TODO: When we post the message to plugin window, there is remaning job.
Finish();
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.