diff options
Diffstat (limited to '')
-rw-r--r-- | widget/windows/nsWindowBase.cpp | 224 |
1 files changed, 224 insertions, 0 deletions
diff --git a/widget/windows/nsWindowBase.cpp b/widget/windows/nsWindowBase.cpp new file mode 100644 index 0000000000..a9c9d2b53e --- /dev/null +++ b/widget/windows/nsWindowBase.cpp @@ -0,0 +1,224 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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/. */ + +#include "nsWindowBase.h" + +#include "mozilla/MiscEvents.h" +#include "mozilla/PresShell.h" +#include "mozilla/StaticPrefs_apz.h" +#include "KeyboardLayout.h" +#include "WinUtils.h" +#include "npapi.h" + +using namespace mozilla; +using namespace mozilla::widget; + +static const wchar_t kUser32LibName[] = L"user32.dll"; +bool nsWindowBase::sTouchInjectInitialized = false; +InjectTouchInputPtr nsWindowBase::sInjectTouchFuncPtr; + +bool nsWindowBase::DispatchPluginEvent(const MSG& aMsg) { return false; } + +// static +bool nsWindowBase::InitTouchInjection() { + if (!sTouchInjectInitialized) { + // Initialize touch injection on the first call + HMODULE hMod = LoadLibraryW(kUser32LibName); + if (!hMod) { + return false; + } + + InitializeTouchInjectionPtr func = + (InitializeTouchInjectionPtr)GetProcAddress(hMod, + "InitializeTouchInjection"); + if (!func) { + WinUtils::Log("InitializeTouchInjection not available."); + return false; + } + + if (!func(TOUCH_INJECT_MAX_POINTS, TOUCH_FEEDBACK_DEFAULT)) { + WinUtils::Log("InitializeTouchInjection failure. GetLastError=%d", + GetLastError()); + return false; + } + + sInjectTouchFuncPtr = + (InjectTouchInputPtr)GetProcAddress(hMod, "InjectTouchInput"); + if (!sInjectTouchFuncPtr) { + WinUtils::Log("InjectTouchInput not available."); + return false; + } + sTouchInjectInitialized = true; + } + return true; +} + +bool nsWindowBase::InjectTouchPoint(uint32_t aId, LayoutDeviceIntPoint& aPoint, + POINTER_FLAGS aFlags, uint32_t aPressure, + uint32_t aOrientation) { + if (aId > TOUCH_INJECT_MAX_POINTS) { + WinUtils::Log("Pointer ID exceeds maximum. See TOUCH_INJECT_MAX_POINTS."); + return false; + } + + POINTER_TOUCH_INFO info; + memset(&info, 0, sizeof(POINTER_TOUCH_INFO)); + + info.touchFlags = TOUCH_FLAG_NONE; + info.touchMask = + TOUCH_MASK_CONTACTAREA | TOUCH_MASK_ORIENTATION | TOUCH_MASK_PRESSURE; + info.pressure = aPressure; + info.orientation = aOrientation; + + info.pointerInfo.pointerFlags = aFlags; + info.pointerInfo.pointerType = PT_TOUCH; + info.pointerInfo.pointerId = aId; + info.pointerInfo.ptPixelLocation.x = aPoint.x; + info.pointerInfo.ptPixelLocation.y = aPoint.y; + + info.rcContact.top = info.pointerInfo.ptPixelLocation.y - 2; + info.rcContact.bottom = info.pointerInfo.ptPixelLocation.y + 2; + info.rcContact.left = info.pointerInfo.ptPixelLocation.x - 2; + info.rcContact.right = info.pointerInfo.ptPixelLocation.x + 2; + + for (int i = 0; i < 3; i++) { + if (sInjectTouchFuncPtr(1, &info)) { + break; + } + DWORD error = GetLastError(); + if (error == ERROR_NOT_READY && i < 2) { + // We sent it too quickly after the previous injection (see bug 1535140 + // comment 10). On the first loop iteration we just yield (via Sleep(0)) + // and try again. If it happens again on the second loop iteration we + // explicitly Sleep(1) and try again. If that doesn't work either we just + // error out. + ::Sleep(i); + continue; + } + WinUtils::Log("InjectTouchInput failure. GetLastError=%d", error); + return false; + } + return true; +} + +void nsWindowBase::ChangedDPI() { + if (mWidgetListener) { + if (PresShell* presShell = mWidgetListener->GetPresShell()) { + presShell->BackingScaleFactorChanged(); + } + } +} + +nsresult nsWindowBase::SynthesizeNativeTouchPoint( + uint32_t aPointerId, nsIWidget::TouchPointerState aPointerState, + LayoutDeviceIntPoint aPoint, double aPointerPressure, + uint32_t aPointerOrientation, nsIObserver* aObserver) { + AutoObserverNotifier notifier(aObserver, "touchpoint"); + + if (StaticPrefs::apz_test_fails_with_native_injection() || + !InitTouchInjection()) { + // If we don't have touch injection from the OS, or if we are running a test + // that cannot properly inject events to satisfy the OS requirements (see + // bug 1313170) we can just fake it and synthesize the events from here. + MOZ_ASSERT(NS_IsMainThread()); + if (aPointerState == TOUCH_HOVER) { + return NS_ERROR_UNEXPECTED; + } + + if (!mSynthesizedTouchInput) { + mSynthesizedTouchInput = MakeUnique<MultiTouchInput>(); + } + + WidgetEventTime time = CurrentMessageWidgetEventTime(); + LayoutDeviceIntPoint pointInWindow = aPoint - WidgetToScreenOffset(); + MultiTouchInput inputToDispatch = UpdateSynthesizedTouchState( + mSynthesizedTouchInput.get(), time.mTime, time.mTimeStamp, aPointerId, + aPointerState, pointInWindow, aPointerPressure, aPointerOrientation); + DispatchTouchInput(inputToDispatch); + return NS_OK; + } + + bool hover = aPointerState & TOUCH_HOVER; + bool contact = aPointerState & TOUCH_CONTACT; + bool remove = aPointerState & TOUCH_REMOVE; + bool cancel = aPointerState & TOUCH_CANCEL; + + // win api expects a value from 0 to 1024. aPointerPressure is a value + // from 0.0 to 1.0. + uint32_t pressure = (uint32_t)ceil(aPointerPressure * 1024); + + // If we already know about this pointer id get it's record + PointerInfo* info = mActivePointers.Get(aPointerId); + + // We know about this pointer, send an update + if (info) { + POINTER_FLAGS flags = POINTER_FLAG_UPDATE; + if (hover) { + flags |= POINTER_FLAG_INRANGE; + } else if (contact) { + flags |= POINTER_FLAG_INCONTACT | POINTER_FLAG_INRANGE; + } else if (remove) { + flags = POINTER_FLAG_UP; + // Remove the pointer from our tracking list. This is nsAutPtr wrapped, + // so shouldn't leak. + mActivePointers.Remove(aPointerId); + } + + if (cancel) { + flags |= POINTER_FLAG_CANCELED; + } + + return !InjectTouchPoint(aPointerId, aPoint, flags, pressure, + aPointerOrientation) + ? NS_ERROR_UNEXPECTED + : NS_OK; + } + + // Missing init state, error out + if (remove || cancel) { + return NS_ERROR_INVALID_ARG; + } + + // Create a new pointer + info = new PointerInfo(aPointerId, aPoint); + + POINTER_FLAGS flags = POINTER_FLAG_INRANGE; + if (contact) { + flags |= POINTER_FLAG_INCONTACT | POINTER_FLAG_DOWN; + } + + mActivePointers.Put(aPointerId, info); + return !InjectTouchPoint(aPointerId, aPoint, flags, pressure, + aPointerOrientation) + ? NS_ERROR_UNEXPECTED + : NS_OK; +} + +nsresult nsWindowBase::ClearNativeTouchSequence(nsIObserver* aObserver) { + AutoObserverNotifier notifier(aObserver, "cleartouch"); + if (!sTouchInjectInitialized) { + return NS_OK; + } + + // cancel all input points + for (auto iter = mActivePointers.Iter(); !iter.Done(); iter.Next()) { + auto info = iter.UserData(); + InjectTouchPoint(info->mPointerId, info->mPosition, POINTER_FLAG_CANCELED); + iter.Remove(); + } + + nsBaseWidget::ClearNativeTouchSequence(nullptr); + + return NS_OK; +} + +bool nsWindowBase::HandleAppCommandMsg(const MSG& aAppCommandMsg, + LRESULT* aRetValue) { + ModifierKeyState modKeyState; + NativeKey nativeKey(this, aAppCommandMsg, modKeyState); + bool consumed = nativeKey.HandleAppCommandMessage(); + *aRetValue = consumed ? 1 : 0; + return consumed; +} |