зеркало из https://github.com/mozilla/gecko-dev.git
241 строка
7.8 KiB
C++
241 строка
7.8 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 "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) {
|
|
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;
|
|
|
|
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;
|
|
}
|