michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 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 michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* michael@0: * nsWinGesture - Touch input handling for tablet displays. michael@0: */ michael@0: michael@0: #include "nscore.h" michael@0: #include "nsWinGesture.h" michael@0: #include "nsUXThemeData.h" michael@0: #include "nsIDOMSimpleGestureEvent.h" michael@0: #include "nsIDOMWheelEvent.h" michael@0: #include "mozilla/Constants.h" michael@0: #include "mozilla/MouseEvents.h" michael@0: #include "mozilla/Preferences.h" michael@0: #include "mozilla/TouchEvents.h" michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::widget; michael@0: michael@0: #ifdef PR_LOGGING michael@0: extern PRLogModuleInfo* gWindowsLog; michael@0: #endif michael@0: michael@0: const wchar_t nsWinGesture::kGestureLibraryName[] = L"user32.dll"; michael@0: HMODULE nsWinGesture::sLibraryHandle = nullptr; michael@0: nsWinGesture::GetGestureInfoPtr nsWinGesture::getGestureInfo = nullptr; michael@0: nsWinGesture::CloseGestureInfoHandlePtr nsWinGesture::closeGestureInfoHandle = nullptr; michael@0: nsWinGesture::GetGestureExtraArgsPtr nsWinGesture::getGestureExtraArgs = nullptr; michael@0: nsWinGesture::SetGestureConfigPtr nsWinGesture::setGestureConfig = nullptr; michael@0: nsWinGesture::GetGestureConfigPtr nsWinGesture::getGestureConfig = nullptr; michael@0: nsWinGesture::BeginPanningFeedbackPtr nsWinGesture::beginPanningFeedback = nullptr; michael@0: nsWinGesture::EndPanningFeedbackPtr nsWinGesture::endPanningFeedback = nullptr; michael@0: nsWinGesture::UpdatePanningFeedbackPtr nsWinGesture::updatePanningFeedback = nullptr; michael@0: michael@0: nsWinGesture::RegisterTouchWindowPtr nsWinGesture::registerTouchWindow = nullptr; michael@0: nsWinGesture::UnregisterTouchWindowPtr nsWinGesture::unregisterTouchWindow = nullptr; michael@0: nsWinGesture::GetTouchInputInfoPtr nsWinGesture::getTouchInputInfo = nullptr; michael@0: nsWinGesture::CloseTouchInputHandlePtr nsWinGesture::closeTouchInputHandle = nullptr; michael@0: michael@0: static bool gEnableSingleFingerPanEvents = false; michael@0: michael@0: nsWinGesture::nsWinGesture() : michael@0: mPanActive(false), michael@0: mFeedbackActive(false), michael@0: mXAxisFeedback(false), michael@0: mYAxisFeedback(false), michael@0: mPanInertiaActive(false) michael@0: { michael@0: (void)InitLibrary(); michael@0: mPixelScrollOverflow = 0; michael@0: } michael@0: michael@0: /* Load and shutdown */ michael@0: michael@0: bool nsWinGesture::InitLibrary() michael@0: { michael@0: if (getGestureInfo) { michael@0: return true; michael@0: } else if (sLibraryHandle) { michael@0: return false; michael@0: } michael@0: michael@0: sLibraryHandle = ::LoadLibraryW(kGestureLibraryName); michael@0: HMODULE hTheme = nsUXThemeData::GetThemeDLL(); michael@0: michael@0: // gesture interfaces michael@0: if (sLibraryHandle) { michael@0: getGestureInfo = (GetGestureInfoPtr)GetProcAddress(sLibraryHandle, "GetGestureInfo"); michael@0: closeGestureInfoHandle = (CloseGestureInfoHandlePtr)GetProcAddress(sLibraryHandle, "CloseGestureInfoHandle"); michael@0: getGestureExtraArgs = (GetGestureExtraArgsPtr)GetProcAddress(sLibraryHandle, "GetGestureExtraArgs"); michael@0: setGestureConfig = (SetGestureConfigPtr)GetProcAddress(sLibraryHandle, "SetGestureConfig"); michael@0: getGestureConfig = (GetGestureConfigPtr)GetProcAddress(sLibraryHandle, "GetGestureConfig"); michael@0: registerTouchWindow = (RegisterTouchWindowPtr)GetProcAddress(sLibraryHandle, "RegisterTouchWindow"); michael@0: unregisterTouchWindow = (UnregisterTouchWindowPtr)GetProcAddress(sLibraryHandle, "UnregisterTouchWindow"); michael@0: getTouchInputInfo = (GetTouchInputInfoPtr)GetProcAddress(sLibraryHandle, "GetTouchInputInfo"); michael@0: closeTouchInputHandle = (CloseTouchInputHandlePtr)GetProcAddress(sLibraryHandle, "CloseTouchInputHandle"); michael@0: } michael@0: michael@0: if (!getGestureInfo || !closeGestureInfoHandle || !getGestureExtraArgs || michael@0: !setGestureConfig || !getGestureConfig) { michael@0: getGestureInfo = nullptr; michael@0: closeGestureInfoHandle = nullptr; michael@0: getGestureExtraArgs = nullptr; michael@0: setGestureConfig = nullptr; michael@0: getGestureConfig = nullptr; michael@0: return false; michael@0: } michael@0: michael@0: if (!registerTouchWindow || !unregisterTouchWindow || !getTouchInputInfo || !closeTouchInputHandle) { michael@0: registerTouchWindow = nullptr; michael@0: unregisterTouchWindow = nullptr; michael@0: getTouchInputInfo = nullptr; michael@0: closeTouchInputHandle = nullptr; michael@0: } michael@0: michael@0: // panning feedback interfaces michael@0: if (hTheme) { michael@0: beginPanningFeedback = (BeginPanningFeedbackPtr)GetProcAddress(hTheme, "BeginPanningFeedback"); michael@0: endPanningFeedback = (EndPanningFeedbackPtr)GetProcAddress(hTheme, "EndPanningFeedback"); michael@0: updatePanningFeedback = (UpdatePanningFeedbackPtr)GetProcAddress(hTheme, "UpdatePanningFeedback"); michael@0: } michael@0: michael@0: if (!beginPanningFeedback || !endPanningFeedback || !updatePanningFeedback) { michael@0: beginPanningFeedback = nullptr; michael@0: endPanningFeedback = nullptr; michael@0: updatePanningFeedback = nullptr; michael@0: } michael@0: michael@0: // Check to see if we want single finger gesture input. Only do this once michael@0: // for the app so we don't have to look it up on every window create. michael@0: gEnableSingleFingerPanEvents = michael@0: Preferences::GetBool("gestures.enable_single_finger_input", false); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: #define GCOUNT 5 michael@0: michael@0: bool nsWinGesture::SetWinGestureSupport(HWND hWnd, michael@0: WidgetGestureNotifyEvent::ePanDirection aDirection) michael@0: { michael@0: if (!getGestureInfo) michael@0: return false; michael@0: michael@0: GESTURECONFIG config[GCOUNT]; michael@0: michael@0: memset(&config, 0, sizeof(config)); michael@0: michael@0: config[0].dwID = GID_ZOOM; michael@0: config[0].dwWant = GC_ZOOM; michael@0: config[0].dwBlock = 0; michael@0: michael@0: config[1].dwID = GID_ROTATE; michael@0: config[1].dwWant = GC_ROTATE; michael@0: config[1].dwBlock = 0; michael@0: michael@0: config[2].dwID = GID_PAN; michael@0: config[2].dwWant = GC_PAN|GC_PAN_WITH_INERTIA| michael@0: GC_PAN_WITH_GUTTER; michael@0: config[2].dwBlock = GC_PAN_WITH_SINGLE_FINGER_VERTICALLY| michael@0: GC_PAN_WITH_SINGLE_FINGER_HORIZONTALLY; michael@0: michael@0: if (gEnableSingleFingerPanEvents) { michael@0: michael@0: if (aDirection == WidgetGestureNotifyEvent::ePanVertical || michael@0: aDirection == WidgetGestureNotifyEvent::ePanBoth) michael@0: { michael@0: config[2].dwWant |= GC_PAN_WITH_SINGLE_FINGER_VERTICALLY; michael@0: config[2].dwBlock -= GC_PAN_WITH_SINGLE_FINGER_VERTICALLY; michael@0: } michael@0: michael@0: if (aDirection == WidgetGestureNotifyEvent::ePanHorizontal || michael@0: aDirection == WidgetGestureNotifyEvent::ePanBoth) michael@0: { michael@0: config[2].dwWant |= GC_PAN_WITH_SINGLE_FINGER_HORIZONTALLY; michael@0: config[2].dwBlock -= GC_PAN_WITH_SINGLE_FINGER_HORIZONTALLY; michael@0: } michael@0: michael@0: } michael@0: michael@0: config[3].dwWant = GC_TWOFINGERTAP; michael@0: config[3].dwID = GID_TWOFINGERTAP; michael@0: config[3].dwBlock = 0; michael@0: michael@0: config[4].dwWant = GC_PRESSANDTAP; michael@0: config[4].dwID = GID_PRESSANDTAP; michael@0: config[4].dwBlock = 0; michael@0: michael@0: return SetGestureConfig(hWnd, GCOUNT, (PGESTURECONFIG)&config); michael@0: } michael@0: michael@0: /* Helpers */ michael@0: michael@0: bool nsWinGesture::IsAvailable() michael@0: { michael@0: return getGestureInfo != nullptr; michael@0: } michael@0: michael@0: bool nsWinGesture::RegisterTouchWindow(HWND hWnd) michael@0: { michael@0: if (!registerTouchWindow) michael@0: return false; michael@0: michael@0: return registerTouchWindow(hWnd, TWF_WANTPALM); michael@0: } michael@0: michael@0: bool nsWinGesture::UnregisterTouchWindow(HWND hWnd) michael@0: { michael@0: if (!unregisterTouchWindow) michael@0: return false; michael@0: michael@0: return unregisterTouchWindow(hWnd); michael@0: } michael@0: michael@0: bool nsWinGesture::GetTouchInputInfo(HTOUCHINPUT hTouchInput, uint32_t cInputs, PTOUCHINPUT pInputs) michael@0: { michael@0: if (!getTouchInputInfo) michael@0: return false; michael@0: michael@0: return getTouchInputInfo(hTouchInput, cInputs, pInputs, sizeof(TOUCHINPUT)); michael@0: } michael@0: michael@0: bool nsWinGesture::CloseTouchInputHandle(HTOUCHINPUT hTouchInput) michael@0: { michael@0: if (!closeTouchInputHandle) michael@0: return false; michael@0: michael@0: return closeTouchInputHandle(hTouchInput); michael@0: } michael@0: michael@0: bool nsWinGesture::GetGestureInfo(HGESTUREINFO hGestureInfo, PGESTUREINFO pGestureInfo) michael@0: { michael@0: if (!getGestureInfo || !hGestureInfo || !pGestureInfo) michael@0: return false; michael@0: michael@0: ZeroMemory(pGestureInfo, sizeof(GESTUREINFO)); michael@0: pGestureInfo->cbSize = sizeof(GESTUREINFO); michael@0: michael@0: return getGestureInfo(hGestureInfo, pGestureInfo); michael@0: } michael@0: michael@0: bool nsWinGesture::CloseGestureInfoHandle(HGESTUREINFO hGestureInfo) michael@0: { michael@0: if (!getGestureInfo || !hGestureInfo) michael@0: return false; michael@0: michael@0: return closeGestureInfoHandle(hGestureInfo); michael@0: } michael@0: michael@0: bool nsWinGesture::GetGestureExtraArgs(HGESTUREINFO hGestureInfo, UINT cbExtraArgs, PBYTE pExtraArgs) michael@0: { michael@0: if (!getGestureInfo || !hGestureInfo || !pExtraArgs) michael@0: return false; michael@0: michael@0: return getGestureExtraArgs(hGestureInfo, cbExtraArgs, pExtraArgs); michael@0: } michael@0: michael@0: bool nsWinGesture::SetGestureConfig(HWND hWnd, UINT cIDs, PGESTURECONFIG pGestureConfig) michael@0: { michael@0: if (!getGestureInfo || !pGestureConfig) michael@0: return false; michael@0: michael@0: return setGestureConfig(hWnd, 0, cIDs, pGestureConfig, sizeof(GESTURECONFIG)); michael@0: } michael@0: michael@0: bool nsWinGesture::GetGestureConfig(HWND hWnd, DWORD dwFlags, PUINT pcIDs, PGESTURECONFIG pGestureConfig) michael@0: { michael@0: if (!getGestureInfo || !pGestureConfig) michael@0: return false; michael@0: michael@0: return getGestureConfig(hWnd, 0, dwFlags, pcIDs, pGestureConfig, sizeof(GESTURECONFIG)); michael@0: } michael@0: michael@0: bool nsWinGesture::BeginPanningFeedback(HWND hWnd) michael@0: { michael@0: if (!beginPanningFeedback) michael@0: return false; michael@0: michael@0: return beginPanningFeedback(hWnd); michael@0: } michael@0: michael@0: bool nsWinGesture::EndPanningFeedback(HWND hWnd) michael@0: { michael@0: if (!beginPanningFeedback) michael@0: return false; michael@0: michael@0: return endPanningFeedback(hWnd, TRUE); michael@0: } michael@0: michael@0: bool nsWinGesture::UpdatePanningFeedback(HWND hWnd, LONG offsetX, LONG offsetY, BOOL fInInertia) michael@0: { michael@0: if (!beginPanningFeedback) michael@0: return false; michael@0: michael@0: return updatePanningFeedback(hWnd, offsetX, offsetY, fInInertia); michael@0: } michael@0: michael@0: bool nsWinGesture::IsPanEvent(LPARAM lParam) michael@0: { michael@0: GESTUREINFO gi; michael@0: michael@0: ZeroMemory(&gi,sizeof(GESTUREINFO)); michael@0: gi.cbSize = sizeof(GESTUREINFO); michael@0: michael@0: BOOL result = GetGestureInfo((HGESTUREINFO)lParam, &gi); michael@0: if (!result) michael@0: return false; michael@0: michael@0: if (gi.dwID == GID_PAN) michael@0: return true; michael@0: michael@0: return false; michael@0: } michael@0: michael@0: /* Gesture event processing */ michael@0: michael@0: bool michael@0: nsWinGesture::ProcessGestureMessage(HWND hWnd, WPARAM wParam, LPARAM lParam, michael@0: WidgetSimpleGestureEvent& evt) michael@0: { michael@0: GESTUREINFO gi; michael@0: michael@0: ZeroMemory(&gi,sizeof(GESTUREINFO)); michael@0: gi.cbSize = sizeof(GESTUREINFO); michael@0: michael@0: BOOL result = GetGestureInfo((HGESTUREINFO)lParam, &gi); michael@0: if (!result) michael@0: return false; michael@0: michael@0: // The coordinates of this event michael@0: nsPointWin coord; michael@0: coord = gi.ptsLocation; michael@0: coord.ScreenToClient(hWnd); michael@0: michael@0: evt.refPoint.x = coord.x; michael@0: evt.refPoint.y = coord.y; michael@0: michael@0: // Multiple gesture can occur at the same time so gesture state michael@0: // info can't be shared. michael@0: switch(gi.dwID) michael@0: { michael@0: case GID_BEGIN: michael@0: case GID_END: michael@0: // These should always fall through to DefWndProc michael@0: return false; michael@0: break; michael@0: michael@0: case GID_ZOOM: michael@0: { michael@0: if (gi.dwFlags & GF_BEGIN) { michael@0: // Send a zoom start event michael@0: michael@0: // The low 32 bits are the distance in pixels. michael@0: mZoomIntermediate = (float)gi.ullArguments; michael@0: michael@0: evt.message = NS_SIMPLE_GESTURE_MAGNIFY_START; michael@0: evt.delta = 0.0; michael@0: } michael@0: else if (gi.dwFlags & GF_END) { michael@0: // Send a zoom end event, the delta is the change michael@0: // in touch points. michael@0: evt.message = NS_SIMPLE_GESTURE_MAGNIFY; michael@0: // (positive for a "zoom in") michael@0: evt.delta = -1.0 * (mZoomIntermediate - (float)gi.ullArguments); michael@0: mZoomIntermediate = (float)gi.ullArguments; michael@0: } michael@0: else { michael@0: // Send a zoom intermediate event, the delta is the change michael@0: // in touch points. michael@0: evt.message = NS_SIMPLE_GESTURE_MAGNIFY_UPDATE; michael@0: // (positive for a "zoom in") michael@0: evt.delta = -1.0 * (mZoomIntermediate - (float)gi.ullArguments); michael@0: mZoomIntermediate = (float)gi.ullArguments; michael@0: } michael@0: } michael@0: break; michael@0: michael@0: case GID_ROTATE: michael@0: { michael@0: // Send a rotate start event michael@0: double radians = 0.0; michael@0: michael@0: // On GF_BEGIN, ullArguments contains the absolute rotation at the michael@0: // start of the gesture. In later events it contains the offset from michael@0: // the start angle. michael@0: if (gi.ullArguments != 0) michael@0: radians = GID_ROTATE_ANGLE_FROM_ARGUMENT(gi.ullArguments); michael@0: michael@0: double degrees = -1 * radians * (180/M_PI); michael@0: michael@0: if (gi.dwFlags & GF_BEGIN) { michael@0: // At some point we should pass the initial angle in michael@0: // along with delta. It's useful. michael@0: degrees = mRotateIntermediate = 0.0; michael@0: } michael@0: michael@0: evt.direction = 0; michael@0: evt.delta = degrees - mRotateIntermediate; michael@0: mRotateIntermediate = degrees; michael@0: michael@0: if (evt.delta > 0) michael@0: evt.direction = nsIDOMSimpleGestureEvent::ROTATION_COUNTERCLOCKWISE; michael@0: else if (evt.delta < 0) michael@0: evt.direction = nsIDOMSimpleGestureEvent::ROTATION_CLOCKWISE; michael@0: michael@0: if (gi.dwFlags & GF_BEGIN) michael@0: evt.message = NS_SIMPLE_GESTURE_ROTATE_START; michael@0: else if (gi.dwFlags & GF_END) michael@0: evt.message = NS_SIMPLE_GESTURE_ROTATE; michael@0: else michael@0: evt.message = NS_SIMPLE_GESTURE_ROTATE_UPDATE; michael@0: } michael@0: break; michael@0: michael@0: case GID_TWOFINGERTAP: michael@0: { michael@0: // Normally maps to "restore" from whatever you may have recently changed. A simple michael@0: // double click. michael@0: evt.message = NS_SIMPLE_GESTURE_TAP; michael@0: evt.clickCount = 1; michael@0: } michael@0: break; michael@0: michael@0: case GID_PRESSANDTAP: michael@0: { michael@0: // Two finger right click. Defaults to right click if it falls through. michael@0: evt.message = NS_SIMPLE_GESTURE_PRESSTAP; michael@0: evt.clickCount = 1; michael@0: } michael@0: break; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: nsWinGesture::ProcessPanMessage(HWND hWnd, WPARAM wParam, LPARAM lParam) michael@0: { michael@0: GESTUREINFO gi; michael@0: michael@0: ZeroMemory(&gi,sizeof(GESTUREINFO)); michael@0: gi.cbSize = sizeof(GESTUREINFO); michael@0: michael@0: BOOL result = GetGestureInfo((HGESTUREINFO)lParam, &gi); michael@0: if (!result) michael@0: return false; michael@0: michael@0: // The coordinates of this event michael@0: nsPointWin coord; michael@0: coord = mPanRefPoint = gi.ptsLocation; michael@0: // We want screen coordinates in our local offsets as client coordinates will change michael@0: // when feedback is taking place. Gui events though require client coordinates. michael@0: mPanRefPoint.ScreenToClient(hWnd); michael@0: michael@0: switch(gi.dwID) michael@0: { michael@0: case GID_BEGIN: michael@0: case GID_END: michael@0: // These should always fall through to DefWndProc michael@0: return false; michael@0: break; michael@0: michael@0: // Setup pixel scroll events for both axis michael@0: case GID_PAN: michael@0: { michael@0: if (gi.dwFlags & GF_BEGIN) { michael@0: mPanIntermediate = coord; michael@0: mPixelScrollDelta = 0; michael@0: mPanActive = true; michael@0: mPanInertiaActive = false; michael@0: } michael@0: else { michael@0: michael@0: #ifdef DBG_jimm michael@0: int32_t deltaX = mPanIntermediate.x - coord.x; michael@0: int32_t deltaY = mPanIntermediate.y - coord.y; michael@0: PR_LOG(gWindowsLog, PR_LOG_ALWAYS, michael@0: ("coordX=%d coordY=%d deltaX=%d deltaY=%d x:%d y:%d\n", coord.x, michael@0: coord.y, deltaX, deltaY, mXAxisFeedback, mYAxisFeedback)); michael@0: #endif michael@0: michael@0: mPixelScrollDelta.x = mPanIntermediate.x - coord.x; michael@0: mPixelScrollDelta.y = mPanIntermediate.y - coord.y; michael@0: mPanIntermediate = coord; michael@0: michael@0: if (gi.dwFlags & GF_INERTIA) michael@0: mPanInertiaActive = true; michael@0: michael@0: if (gi.dwFlags & GF_END) { michael@0: mPanActive = false; michael@0: mPanInertiaActive = false; michael@0: PanFeedbackFinalize(hWnd, true); michael@0: } michael@0: } michael@0: } michael@0: break; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: inline bool TestTransition(int32_t a, int32_t b) michael@0: { michael@0: // If a is zero, overflow is zero, implying the cursor has moved back to the start position. michael@0: // If b is zero, cached overscroll is zero, implying feedback just begun. michael@0: if (a == 0 || b == 0) return true; michael@0: // Test for different signs. michael@0: return (a < 0) == (b < 0); michael@0: } michael@0: michael@0: void michael@0: nsWinGesture::UpdatePanFeedbackX(HWND hWnd, int32_t scrollOverflow, bool& endFeedback) michael@0: { michael@0: // If scroll overflow was returned indicating we panned past the bounds of michael@0: // the scrollable view port, start feeback. michael@0: if (scrollOverflow != 0) { michael@0: if (!mFeedbackActive) { michael@0: BeginPanningFeedback(hWnd); michael@0: mFeedbackActive = true; michael@0: } michael@0: endFeedback = false; michael@0: mXAxisFeedback = true; michael@0: return; michael@0: } michael@0: michael@0: if (mXAxisFeedback) { michael@0: int32_t newOverflow = mPixelScrollOverflow.x - mPixelScrollDelta.x; michael@0: michael@0: // Detect a reverse transition past the starting drag point. This tells us the user michael@0: // has panned all the way back so we can stop providing feedback for this axis. michael@0: if (!TestTransition(newOverflow, mPixelScrollOverflow.x) || newOverflow == 0) michael@0: return; michael@0: michael@0: // Cache the total over scroll in pixels. michael@0: mPixelScrollOverflow.x = newOverflow; michael@0: endFeedback = false; michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsWinGesture::UpdatePanFeedbackY(HWND hWnd, int32_t scrollOverflow, bool& endFeedback) michael@0: { michael@0: // If scroll overflow was returned indicating we panned past the bounds of michael@0: // the scrollable view port, start feeback. michael@0: if (scrollOverflow != 0) { michael@0: if (!mFeedbackActive) { michael@0: BeginPanningFeedback(hWnd); michael@0: mFeedbackActive = true; michael@0: } michael@0: endFeedback = false; michael@0: mYAxisFeedback = true; michael@0: return; michael@0: } michael@0: michael@0: if (mYAxisFeedback) { michael@0: int32_t newOverflow = mPixelScrollOverflow.y - mPixelScrollDelta.y; michael@0: michael@0: // Detect a reverse transition past the starting drag point. This tells us the user michael@0: // has panned all the way back so we can stop providing feedback for this axis. michael@0: if (!TestTransition(newOverflow, mPixelScrollOverflow.y) || newOverflow == 0) michael@0: return; michael@0: michael@0: // Cache the total over scroll in pixels. michael@0: mPixelScrollOverflow.y = newOverflow; michael@0: endFeedback = false; michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsWinGesture::PanFeedbackFinalize(HWND hWnd, bool endFeedback) michael@0: { michael@0: if (!mFeedbackActive) michael@0: return; michael@0: michael@0: if (endFeedback) { michael@0: mFeedbackActive = false; michael@0: mXAxisFeedback = false; michael@0: mYAxisFeedback = false; michael@0: mPixelScrollOverflow = 0; michael@0: EndPanningFeedback(hWnd); michael@0: return; michael@0: } michael@0: michael@0: UpdatePanningFeedback(hWnd, mPixelScrollOverflow.x, mPixelScrollOverflow.y, mPanInertiaActive); michael@0: } michael@0: michael@0: bool michael@0: nsWinGesture::PanDeltaToPixelScroll(WidgetWheelEvent& aWheelEvent) michael@0: { michael@0: aWheelEvent.deltaX = aWheelEvent.deltaY = aWheelEvent.deltaZ = 0.0; michael@0: aWheelEvent.lineOrPageDeltaX = aWheelEvent.lineOrPageDeltaY = 0; michael@0: michael@0: aWheelEvent.refPoint.x = mPanRefPoint.x; michael@0: aWheelEvent.refPoint.y = mPanRefPoint.y; michael@0: aWheelEvent.deltaMode = nsIDOMWheelEvent::DOM_DELTA_PIXEL; michael@0: aWheelEvent.scrollType = WidgetWheelEvent::SCROLL_SYNCHRONOUSLY; michael@0: aWheelEvent.isPixelOnlyDevice = true; michael@0: michael@0: aWheelEvent.overflowDeltaX = 0.0; michael@0: aWheelEvent.overflowDeltaY = 0.0; michael@0: michael@0: // Don't scroll the view if we are currently at a bounds, or, if we are michael@0: // panning back from a max feedback position. This keeps the original drag point michael@0: // constant. michael@0: if (!mXAxisFeedback) { michael@0: aWheelEvent.deltaX = mPixelScrollDelta.x; michael@0: } michael@0: if (!mYAxisFeedback) { michael@0: aWheelEvent.deltaY = mPixelScrollDelta.y; michael@0: } michael@0: michael@0: return (aWheelEvent.deltaX != 0 || aWheelEvent.deltaY != 0); michael@0: }