mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-31 22:25:30 +00:00
cb026d2536
When using the InjectTouchInput API on Windows, the API requires that the caller keep providing input frames (by calling the API function) at least every 100ms. If the caller fails to do so, Windows can return an ERROR_TIMEOUT and throw away the touch sequence. In some tests, it is hard for us to make this guarantee, because we need to wait for other events between the touchdown and touchup. For these tests, we can use the widget-level touch injection code that we have as a fallback for the OS-level touch injection code. The widget-level touch injection is less representative of real-world usage but allows us to bypass the timeout problem. MozReview-Commit-ID: EoVUSZmERUw --HG-- extra : rebase_source : 4f7ca1a1e6da4479d51526e8f132f5cb23fceb49
244 lines
7.4 KiB
C++
244 lines
7.4 KiB
C++
/* -*- 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 "KeyboardLayout.h"
|
|
#include "WinUtils.h"
|
|
#include "npapi.h"
|
|
#include "nsAutoPtr.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)
|
|
{
|
|
if (!ShouldDispatchPluginEvent()) {
|
|
return false;
|
|
}
|
|
WidgetPluginEvent pluginEvent(true, ePluginInputEvent, this);
|
|
LayoutDeviceIntPoint point(0, 0);
|
|
InitEvent(pluginEvent, &point);
|
|
NPEvent npEvent;
|
|
npEvent.event = aMsg.message;
|
|
npEvent.wParam = aMsg.wParam;
|
|
npEvent.lParam = aMsg.lParam;
|
|
pluginEvent.mPluginEvent.Copy(npEvent);
|
|
pluginEvent.mRetargetToFocusedDocument = true;
|
|
return DispatchWindowEvent(&pluginEvent);
|
|
}
|
|
|
|
bool
|
|
nsWindowBase::ShouldDispatchPluginEvent()
|
|
{
|
|
return PluginHasFocus();
|
|
}
|
|
|
|
// 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;
|
|
|
|
if (!sInjectTouchFuncPtr(1, &info)) {
|
|
WinUtils::Log("InjectTouchInput failure. GetLastError=%d", GetLastError());
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void nsWindowBase::ChangedDPI()
|
|
{
|
|
if (mWidgetListener) {
|
|
nsIPresShell* presShell = mWidgetListener->GetPresShell();
|
|
if (presShell) {
|
|
presShell->BackingScaleFactorChanged();
|
|
}
|
|
mWidgetListener->UIResolutionChanged();
|
|
}
|
|
}
|
|
|
|
nsresult
|
|
nsWindowBase::SynthesizeNativeTouchPoint(uint32_t aPointerId,
|
|
nsIWidget::TouchPointerState aPointerState,
|
|
LayoutDeviceIntPoint aPoint,
|
|
double aPointerPressure,
|
|
uint32_t aPointerOrientation,
|
|
nsIObserver* aObserver)
|
|
{
|
|
AutoObserverNotifier notifier(aObserver, "touchpoint");
|
|
|
|
if (gfxPrefs::APZTestFailsWithNativeInjection() || !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()) {
|
|
nsAutoPtr<PointerInfo>& info = iter.Data();
|
|
InjectTouchPoint(info.get()->mPointerId, info.get()->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;
|
|
}
|