mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 13:21:05 +00:00
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:
parent
d99441a995
commit
9fccbff1fd
@ -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();
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user