Bug 1648267 - Part 4: Add OS native pen input injection r=edgar,aklotz

Differential Revision: https://phabricator.services.mozilla.com/D106050
This commit is contained in:
Kagami Sascha Rosylight 2021-03-02 18:29:45 +00:00
parent d99441a995
commit 9fccbff1fd
11 changed files with 244 additions and 15 deletions

View File

@ -1152,6 +1152,34 @@ nsDOMWindowUtils::SendNativeTouchTap(int32_t aScreenX, int32_t aScreenY,
return NS_OK;
}
NS_IMETHODIMP
nsDOMWindowUtils::SendNativePenInput(uint32_t aPointerId,
uint32_t aPointerState, int32_t aScreenX,
int32_t aScreenY, double aPressure,
uint32_t aRotation, int32_t aTiltX,
int32_t aTiltY, nsIObserver* aObserver) {
nsCOMPtr<nsIWidget> widget = GetWidget();
if (!widget) {
return NS_ERROR_FAILURE;
}
if (aPressure < 0 || aPressure > 1 || aRotation > 359 || aTiltX < -90 ||
aTiltX > 90 || aTiltY < -90 || aTiltY > 90) {
return NS_ERROR_INVALID_ARG;
}
NS_DispatchToMainThread(NativeInputRunnable::Create(
NewRunnableMethod<uint32_t, nsIWidget::TouchPointerState,
LayoutDeviceIntPoint, double, uint32_t, int32_t,
int32_t, nsIObserver*>(
"nsIWidget::SynthesizeNativePenInput", widget,
&nsIWidget::SynthesizeNativePenInput, aPointerId,
(nsIWidget::TouchPointerState)aPointerState,
LayoutDeviceIntPoint(aScreenX, aScreenY), aPressure, aRotation,
aTiltX, aTiltY, aObserver)));
return NS_OK;
}
NS_IMETHODIMP
nsDOMWindowUtils::SuppressAnimation(bool aSuppress) {
nsIWidget* widget = GetWidget();

View File

@ -623,7 +623,7 @@ interface nsIDOMWindowUtils : nsISupports {
// input sequence. This may not cancel moz platform related events
// that might get tirggered by input already delivered.
const long TOUCH_CANCEL = 0x08;
/**
* Phase states for sendNativeTouchPadPinch.
*/
@ -662,15 +662,15 @@ interface nsIDOMWindowUtils : nsISupports {
[optional] in nsIObserver aObserver);
/**
* These values indicate touchpad pinch phase states :
* PHASE_BEGIN
* PHASE_BEGIN
* PHASE_UPDATE
* PHASE_END
* Widget support: Linux GTK 3.18+.
* @param aEventPhase The touchpad pinch phase using states listed above.
* @param aScale Events with PHASE_UPDATE will change the zoom level by
* @param aScale Events with PHASE_UPDATE will change the zoom level by
* the ratio between the scale of the current event and the scale of the last event.
* @param aScreenX, aScreenY screen coords of the focus point of this event.
* @param aModifierFlags is expected to contain native modifier values.
* @param aModifierFlags is expected to contain native modifier values.
*/
void sendNativeTouchpadPinch(in unsigned long aEventPhase,
in float aScale,
@ -706,6 +706,37 @@ interface nsIDOMWindowUtils : nsISupports {
in boolean aLongTap,
[optional] in nsIObserver aObserver);
/**
* Create a new or update an existing pen input on the digitizer.
*
* Widget support: Windows 10 1809+. Other widgets will throw.
*
* NOTE: The synthesized native event will be fired asynchronously, and upon
* completion the observer, if provided, will be notified with a "peninput"
* topic.
*
* @param aPointerId The touch point id to create or update.
* @param aPointerState one or more of the TOUCH_* listed above
* @param aScreenX x screen coord of this event
* @param aScreenY y screen coord of this event
* @param aPressure 0.0 -> 1.0 float val indicating pressure
* @param aRotation 0 -> 359 degree value indicating the rotation of the
* pointer. Use 0 for normal taps.
* @param aTiltX -90 -> 90 degree value indicating the tilt along the x-axis
* of the pointer. Use 0 for normal taps.
* @param aTiltY -90 -> 90 degree value indicating the tilt along the y-axis
* of the pointer. Use 0 for normal taps.
*/
void sendNativePenInput(in unsigned long aPointerId,
in unsigned long aPointerState,
in long aScreenX,
in long aScreenY,
in double aPressure,
in unsigned long aRotation,
in long aTiltX,
in long aTiltY,
[optional] in nsIObserver aObserver);
/**
* Cancel any existing touch points or long tap delays. Calling this is safe
* even if you're sure there aren't any pointers recorded. You should call

View File

@ -1878,6 +1878,21 @@ mozilla::ipc::IPCResult BrowserParent::RecvClearNativeTouchSequence(
return IPC_OK();
}
mozilla::ipc::IPCResult BrowserParent::RecvSynthesizeNativePenInput(
const uint32_t& aPointerId, const TouchPointerState& aPointerState,
const LayoutDeviceIntPoint& aPoint, const double& aPressure,
const uint32_t& aRotation, const int32_t& aTiltX, const int32_t& aTiltY,
const uint64_t& aObserverId) {
AutoSynthesizedEventResponder responder(this, aObserverId, "peninput");
nsCOMPtr<nsIWidget> widget = GetWidget();
if (widget) {
widget->SynthesizeNativePenInput(aPointerId, aPointerState, aPoint,
aPressure, aRotation, aTiltX, aTiltY,
responder.GetObserver());
}
return IPC_OK();
}
void BrowserParent::SendRealKeyEvent(WidgetKeyboardEvent& aEvent) {
if (mIsDestroyed || !mIsReadyToHandleInputEvents) {
return;

View File

@ -544,6 +544,12 @@ class BrowserParent final : public PBrowserParent,
mozilla::ipc::IPCResult RecvClearNativeTouchSequence(
const uint64_t& aObserverId);
mozilla::ipc::IPCResult RecvSynthesizeNativePenInput(
const uint32_t& aPointerId, const TouchPointerState& aPointerState,
const LayoutDeviceIntPoint& aPoint, const double& aPressure,
const uint32_t& aRotation, const int32_t& aTiltX, const int32_t& aTiltY,
const uint64_t& aObserverId);
void SendMouseEvent(const nsAString& aType, float aX, float aY,
int32_t aButton, int32_t aClickCount, int32_t aModifiers);
@ -934,8 +940,8 @@ class BrowserParent final : public PBrowserParent,
// CreateWindow response. Then BrowserChild loads them immediately.
nsTArray<FrameScriptInfo> mDelayedFrameScripts;
// Cached cursor setting from BrowserChild. When the cursor is over the tab,
// it should take this appearance.
// Cached cursor setting from BrowserChild. When the cursor is over the
// tab, it should take this appearance.
nsCursor mCursor;
nsCOMPtr<imgIContainer> mCustomCursor;
uint32_t mCustomCursorHotspotX, mCustomCursorHotspotY;
@ -951,11 +957,12 @@ class BrowserParent final : public PBrowserParent,
// When true, we've initiated normal shutdown and notified our managing
// PContent.
bool mMarkedDestroying : 1;
// When true, the BrowserParent is invalid and we should not send IPC messages
// anymore.
// When true, the BrowserParent is invalid and we should not send IPC
// messages anymore.
bool mIsDestroyed : 1;
// True if the cursor changes from the BrowserChild should change the widget
// cursor. This happens whenever the cursor is in the remote target's region.
// cursor. This happens whenever the cursor is in the remote target's
// region.
bool mRemoteTargetSetsCursor : 1;
// If this flag is set, then the tab's layers will be preserved even when
@ -981,9 +988,9 @@ class BrowserParent final : public PBrowserParent,
// True when the remote browser is created and ready to handle input events.
bool mIsReadyToHandleInputEvents : 1;
// True if we suppress the eMouseEnterIntoWidget event due to the BrowserChild
// was not ready to handle it. We will resend it when the next time we fire a
// mouse event and the BrowserChild is ready.
// True if we suppress the eMouseEnterIntoWidget event due to the
// BrowserChild was not ready to handle it. We will resend it when the next
// time we fire a mouse event and the BrowserChild is ready.
bool mIsMouseEnterIntoWidgetEventSuppressed : 1;
// Set to true if we're currently suspending nsIWebProgress events.

View File

@ -525,7 +525,7 @@ parent:
LayoutDeviceIntPoint aPoint,
double aPointerPressure,
uint32_t aPointerOrientation,
uint64_t aObserverId);
uint64_t aObserverId);
async SynthesizeNativeTouchPadPinch(TouchpadPinchPhase aEventPhase,
float aScale,
LayoutDeviceIntPoint aPoint,
@ -534,6 +534,14 @@ parent:
bool aLongTap,
uint64_t aObserverId);
async ClearNativeTouchSequence(uint64_t aObserverId);
async SynthesizeNativePenInput(uint32_t aPointerId,
TouchPointerState aPointerState,
LayoutDeviceIntPoint aPoint,
double aPressure,
uint32_t aRotation,
int32_t aTiltX,
int32_t aTiltY,
uint64_t aObserverId);
async AccessKeyNotHandled(WidgetKeyboardEvent event);

View File

@ -511,6 +511,20 @@ nsresult PuppetWidget::ClearNativeTouchSequence(nsIObserver* aObserver) {
return NS_OK;
}
nsresult PuppetWidget::SynthesizeNativePenInput(
uint32_t aPointerId, TouchPointerState aPointerState,
LayoutDeviceIntPoint aPoint, double aPressure, uint32_t aRotation,
int32_t aTiltX, int32_t aTiltY, nsIObserver* aObserver) {
AutoObserverNotifier notifier(aObserver, "peninput");
if (!mBrowserChild) {
return NS_ERROR_FAILURE;
}
mBrowserChild->SendSynthesizeNativePenInput(aPointerId, aPointerState, aPoint,
aPressure, aRotation, aTiltX,
aTiltY, notifier.SaveObserver());
return NS_OK;
}
void PuppetWidget::SetConfirmedTargetAPZC(
uint64_t aInputBlockId,
const nsTArray<ScrollableLayerGuid>& aTargets) const {

View File

@ -275,6 +275,10 @@ class PuppetWidget : public nsBaseWidget,
nsIObserver* aObserver) override;
virtual nsresult ClearNativeTouchSequence(nsIObserver* aObserver) override;
virtual uint32_t GetMaxTouchPoints() const override;
virtual nsresult SynthesizeNativePenInput(
uint32_t aPointerId, TouchPointerState aPointerState,
LayoutDeviceIntPoint aPoint, double aPressure, uint32_t aRotation,
int32_t aTiltX, int32_t aTiltY, nsIObserver* aObserver) override;
virtual void StartAsyncScrollbarDrag(
const AsyncDragMetrics& aDragMetrics) override;

View File

@ -529,6 +529,15 @@ class nsBaseWidget : public nsIWidget, public nsSupportsWeakReference {
return NS_ERROR_UNEXPECTED;
}
virtual nsresult SynthesizeNativePenInput(
uint32_t aPointerId, TouchPointerState aPointerState,
LayoutDeviceIntPoint aPoint, double aPressure, uint32_t aRotation,
int32_t aTiltX, int32_t aTiltY, nsIObserver* aObserver) override {
MOZ_RELEASE_ASSERT(
false, "This method is not implemented on the current platform");
return NS_ERROR_UNEXPECTED;
}
/**
* GetPseudoIMEContext() returns pseudo IME context when TextEventDispatcher
* has non-native input transaction. Otherwise, returns nullptr.

View File

@ -1700,6 +1700,11 @@ class nsIWidget : public nsISupports {
bool aLongTap,
nsIObserver* aObserver);
virtual nsresult SynthesizeNativePenInput(
uint32_t aPointerId, TouchPointerState aPointerState,
LayoutDeviceIntPoint aPoint, double aPressure, uint32_t aRotation,
int32_t aTiltX, int32_t aTiltY, nsIObserver* aObserver) = 0;
/*
* Cancels all active simulated touch input points and pending long taps.
* Native widgets should track existing points such that they can clear the

View File

@ -61,8 +61,7 @@ bool nsWindowBase::InjectTouchPoint(uint32_t aId, LayoutDeviceIntPoint& aPoint,
return false;
}
POINTER_TOUCH_INFO info;
memset(&info, 0, sizeof(POINTER_TOUCH_INFO));
POINTER_TOUCH_INFO info{};
info.touchFlags = TOUCH_FLAG_NONE;
info.touchMask =
@ -232,6 +231,109 @@ nsresult nsWindowBase::ClearNativeTouchSequence(nsIObserver* aObserver) {
return NS_OK;
}
#if !defined(NTDDI_WIN10_RS5) || (NTDDI_VERSION < NTDDI_WIN10_RS5)
static CreateSyntheticPointerDevicePtr CreateSyntheticPointerDevice;
static DestroySyntheticPointerDevicePtr DestroySyntheticPointerDevice;
static InjectSyntheticPointerInputPtr InjectSyntheticPointerInput;
#endif
static HSYNTHETICPOINTERDEVICE sSyntheticPenDevice;
static bool InitPenInjection() {
if (sSyntheticPenDevice) {
return true;
}
#if !defined(NTDDI_WIN10_RS5) || (NTDDI_VERSION < NTDDI_WIN10_RS5)
HMODULE hMod = LoadLibraryW(kUser32LibName);
if (!hMod) {
return false;
}
CreateSyntheticPointerDevice =
(CreateSyntheticPointerDevicePtr)GetProcAddress(
hMod, "CreateSyntheticPointerDevice");
if (!CreateSyntheticPointerDevice) {
WinUtils::Log("CreateSyntheticPointerDevice not available.");
return false;
}
DestroySyntheticPointerDevice =
(DestroySyntheticPointerDevicePtr)GetProcAddress(
hMod, "DestroySyntheticPointerDevice");
if (!DestroySyntheticPointerDevice) {
WinUtils::Log("DestroySyntheticPointerDevice not available.");
return false;
}
InjectSyntheticPointerInput = (InjectSyntheticPointerInputPtr)GetProcAddress(
hMod, "InjectSyntheticPointerInput");
if (!InjectSyntheticPointerInput) {
WinUtils::Log("InjectSyntheticPointerInput not available.");
return false;
}
#endif
sSyntheticPenDevice =
CreateSyntheticPointerDevice(PT_PEN, 1, POINTER_FEEDBACK_DEFAULT);
return !!sSyntheticPenDevice;
}
nsresult nsWindowBase::SynthesizeNativePenInput(
uint32_t aPointerId, nsIWidget::TouchPointerState aPointerState,
LayoutDeviceIntPoint aPoint, double aPressure, uint32_t aRotation,
int32_t aTiltX, int32_t aTiltY, nsIObserver* aObserver) {
AutoObserverNotifier notifier(aObserver, "peninput");
if (!InitPenInjection()) {
return NS_ERROR_UNEXPECTED;
}
// 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(aPressure * 1024);
// If we already know about this pointer id get it's record
return mActivePointers.WithEntryHandle(aPointerId, [&](auto&& entry) {
POINTER_FLAGS flags;
// Can't use MOZ_TRY_VAR because it confuses WithEntryHandle
auto result = PointerStateToFlag(aPointerState, !!entry);
if (result.isOk()) {
flags = result.unwrap();
} else {
return result.unwrapErr();
}
if (!entry) {
entry.Insert(MakeUnique<PointerInfo>(aPointerId, aPoint,
PointerInfo::PointerType::PEN));
} else {
if (entry.Data()->mType != PointerInfo::PointerType::PEN) {
return NS_ERROR_UNEXPECTED;
}
if (aPointerState & TOUCH_REMOVE) {
// Remove the pointer from our tracking list. This is UniquePtr wrapped,
// so shouldn't leak.
entry.Remove();
}
}
POINTER_TYPE_INFO info{};
info.type = PT_PEN;
info.penInfo.pointerInfo.pointerType = PT_PEN;
info.penInfo.pointerInfo.pointerFlags = flags;
info.penInfo.pointerInfo.pointerId = aPointerId;
info.penInfo.pointerInfo.ptPixelLocation.x = aPoint.x;
info.penInfo.pointerInfo.ptPixelLocation.y = aPoint.y;
info.penInfo.penFlags = PEN_FLAG_NONE;
info.penInfo.penMask = PEN_MASK_PRESSURE | PEN_MASK_ROTATION |
PEN_MASK_TILT_X | PEN_MASK_TILT_Y;
info.penInfo.pressure = pressure;
info.penInfo.rotation = aRotation;
info.penInfo.tiltX = aTiltX;
info.penInfo.tiltY = aTiltY;
return InjectSyntheticPointerInput(sSyntheticPenDevice, &info, 1)
? NS_OK
: NS_ERROR_UNEXPECTED;
});
};
bool nsWindowBase::HandleAppCommandMsg(const MSG& aAppCommandMsg,
LRESULT* aRetValue) {
ModifierKeyState modKeyState;

View File

@ -91,6 +91,11 @@ class nsWindowBase : public nsBaseWidget {
nsIObserver* aObserver) override;
virtual nsresult ClearNativeTouchSequence(nsIObserver* aObserver) override;
virtual nsresult SynthesizeNativePenInput(
uint32_t aPointerId, TouchPointerState aPointerState,
LayoutDeviceIntPoint aPoint, double aPressure, uint32_t aRotation,
int32_t aTiltX, int32_t aTiltY, nsIObserver* aObserver) override;
/*
* WM_APPCOMMAND common handler.
* Sends events via NativeKey::HandleAppCommandMessage().
@ -113,6 +118,7 @@ class nsWindowBase : public nsBaseWidget {
public:
enum class PointerType : uint8_t {
TOUCH,
PEN,
};
PointerInfo(int32_t aPointerId, LayoutDeviceIntPoint& aPoint,