Decide whether to synthesize mouse events on a per device basis

Currently Qt uses the QPlatformIntegration::StyleHint
SynthesizeMouseFromTouchEvents to check whether to synthesize mouse
events from touch events. But not only platform plugins can produce
touch events, they can be created by e.g. QTest::touchEvent() and in
this case we almost definitely need synthesizing regardless of the
platform.

This commit introduces a QTouchDevice::MouseEmulation capability which
replaces use of the QPlatformIntegration::SynthesizeMouseFromTouchEvents.
So it's possible to pass QTouchDevice without this capability to
QTest::touchEvent() and be sure that mouse events will be synthesized.
Notice that touch pads always emulate mouse events.
As a result we can activate some tests which were disabled for specific
platform configurations by commits 6c1670d8c2
and e9760f1559.

Change-Id: Idc82fa4007a095fc1cb5934979361b0023d2b793
Reviewed-by: Laszlo Agocs <laszlo.agocs@theqtcompany.com>
This commit is contained in:
Alexander Volkov 2014-12-09 18:52:24 +03:00
parent ef22739f47
commit 76922a706f
20 changed files with 85 additions and 66 deletions

View File

@ -788,12 +788,6 @@ bool QGuiApplicationPrivate::isWindowBlocked(QWindow *window, QWindow **blocking
return false;
}
bool QGuiApplicationPrivate::synthesizeMouseFromTouchEventsEnabled()
{
return QCoreApplication::testAttribute(Qt::AA_SynthesizeMouseForUnhandledTouchEvents)
&& QGuiApplicationPrivate::platformIntegration()->styleHint(QPlatformIntegration::SynthesizeMouseFromTouchEvents).toBool();
}
/*!
Returns the QWindow that receives events tied to focus,
such as key events.
@ -2423,9 +2417,9 @@ void QGuiApplicationPrivate::processTouchEvent(QWindowSystemInterfacePrivate::To
}
QGuiApplication::sendSpontaneousEvent(w, &touchEvent);
if (!e->synthetic() && !touchEvent.isAccepted() && synthesizeMouseFromTouchEventsEnabled()) {
// exclude touchpads as those generate their own mouse events
if (touchEvent.device()->type() != QTouchDevice::TouchPad) {
if (!e->synthetic() && !touchEvent.isAccepted() && qApp->testAttribute(Qt::AA_SynthesizeMouseForUnhandledTouchEvents)) {
// exclude devices which generate their own mouse events
if (!(touchEvent.device()->capabilities() & QTouchDevice::MouseEmulation)) {
Qt::MouseButtons b = eventType == QEvent::TouchEnd ? Qt::NoButton : Qt::LeftButton;
if (b == Qt::NoButton)
self->synthesizedMousePoints.clear();

View File

@ -190,8 +190,6 @@ public:
static void updateBlockedStatus(QWindow *window);
virtual bool isWindowBlocked(QWindow *window, QWindow **blockingWindow = 0) const;
static bool synthesizeMouseFromTouchEventsEnabled();
static Qt::MouseButtons buttons;
static ulong mousePressTime;
static Qt::MouseButton mousePressButton;

View File

@ -386,8 +386,6 @@ QVariant QPlatformIntegration::styleHint(StyleHint hint) const
return QPlatformTheme::defaultThemeHint(QPlatformTheme::StartDragVelocity);
case UseRtlExtensions:
return QVariant(false);
case SynthesizeMouseFromTouchEvents:
return true;
case SetFocusOnTouchRelease:
return QVariant(false);
case MousePressAndHoldInterval:

View File

@ -141,7 +141,6 @@ public:
FontSmoothingGamma,
StartDragVelocity,
UseRtlExtensions,
SynthesizeMouseFromTouchEvents,
PasswordMaskCharacter,
SetFocusOnTouchRelease,
ShowIsMaximized,

View File

@ -96,6 +96,9 @@ QT_BEGIN_NAMESPACE
\value NormalizedPosition Indicates that the normalized position is available, meaning that normalizedPos()
returns a valid value.
\value MouseEmulation Indicates that the device synthesizes mouse events.
This enum value has been introduced in Qt 5.5.
*/
/*!

View File

@ -55,7 +55,8 @@ public:
Pressure = 0x0004,
Velocity = 0x0008,
RawPositions = 0x0010,
NormalizedPosition = 0x0020
NormalizedPosition = 0x0020,
MouseEmulation = 0x0040
};
Q_DECLARE_FLAGS(Capabilities, CapabilityFlag)

View File

@ -505,8 +505,6 @@ QVariant QCocoaIntegration::styleHint(StyleHint hint) const
{
if (hint == QPlatformIntegration::FontSmoothingGamma)
return 2.0;
if (hint == QPlatformIntegration::SynthesizeMouseFromTouchEvents)
return false;
return QPlatformIntegration::styleHint(hint);
}

View File

@ -157,7 +157,7 @@ static NSString *_q_NSWindowDidChangeOcclusionStateNotification = nil;
if (!touchDevice) {
touchDevice = new QTouchDevice;
touchDevice->setType(QTouchDevice::TouchPad);
touchDevice->setCapabilities(QTouchDevice::Position | QTouchDevice::NormalizedPosition);
touchDevice->setCapabilities(QTouchDevice::Position | QTouchDevice::NormalizedPosition | QTouchDevice::MouseEmulation);
QWindowSystemInterface::registerTouchDevice(touchDevice);
}
}

View File

@ -1235,6 +1235,11 @@ void QWindowsContext::setAsyncExpose(bool value)
d->m_asyncExpose = value;
}
QTouchDevice *QWindowsContext::touchDevice() const
{
return d->m_mouseHandler.touchDevice();
}
/*!
\brief Windows functions for actual windows.

View File

@ -67,6 +67,7 @@ struct QWindowCreationContext;
struct QWindowsContextPrivate;
class QPoint;
class QKeyEvent;
class QTouchDevice;
#ifndef Q_OS_WINCE
struct QWindowsUser32DLL
@ -219,6 +220,8 @@ public:
bool asyncExpose() const;
void setAsyncExpose(bool value);
QTouchDevice *touchDevice() const;
private:
void handleFocusEvent(QtWindows::WindowsEventType et, QWindowsWindow *w);
#ifndef QT_NO_CONTEXTMENU

View File

@ -227,6 +227,18 @@ QWindowsIntegrationPrivate::QWindowsIntegrationPrivate(const QStringList &paramL
qCDebug(lcQpaWindows)
<< __FUNCTION__ << "DpiAwareness=" << dpiAwareness <<",Scaling="
<< QWindowsScaling::factor();
QTouchDevice *touchDevice = m_context.touchDevice();
if (touchDevice) {
#ifdef Q_OS_WINCE
touchDevice->setCapabilities(touchDevice->capabilities() | QTouchDevice::MouseEmulation);
#else
if (!(m_options & QWindowsIntegration::DontPassOsMouseEventsSynthesizedFromTouch)) {
touchDevice->setCapabilities(touchDevice->capabilities() | QTouchDevice::MouseEmulation);
}
#endif
QWindowSystemInterface::registerTouchDevice(touchDevice);
}
}
QWindowsIntegrationPrivate::~QWindowsIntegrationPrivate()
@ -496,13 +508,6 @@ QVariant QWindowsIntegration::styleHint(QPlatformIntegration::StyleHint hint) co
break;
case QPlatformIntegration::UseRtlExtensions:
return QVariant(d->m_context.useRTLExtensions());
case QPlatformIntegration::SynthesizeMouseFromTouchEvents:
#ifdef Q_OS_WINCE
// We do not want Qt to synthesize mouse events as Windows also does that.
return false;
#else // Q_OS_WINCE
return QVariant(bool(d->m_options & DontPassOsMouseEventsSynthesizedFromTouch));
#endif // !Q_OS_WINCE
default:
break;
}

View File

@ -128,7 +128,10 @@ static inline QTouchDevice *createTouchDevice()
QTouchDevice *result = new QTouchDevice;
result->setType(digitizers & QT_NID_INTEGRATED_TOUCH
? QTouchDevice::TouchScreen : QTouchDevice::TouchPad);
result->setCapabilities(QTouchDevice::Position | QTouchDevice::Area | QTouchDevice::NormalizedPosition);
QTouchDevice::Capabilities capabilities = QTouchDevice::Position | QTouchDevice::Area | QTouchDevice::NormalizedPosition;
if (result->type() == QTouchDevice::TouchPad)
capabilities |= QTouchDevice::MouseEmulation;
result->setCapabilities(capabilities);
result->setMaximumTouchPoints(maxTouchPoints);
return result;
}
@ -150,8 +153,6 @@ QWindowsMouseHandler::QWindowsMouseHandler() :
m_leftButtonDown(false),
m_previousCaptureWindow(0)
{
if (m_touchDevice)
QWindowSystemInterface::registerTouchDevice(m_touchDevice);
}
Qt::MouseButtons QWindowsMouseHandler::queryMouseButtons()

View File

@ -224,8 +224,6 @@ QVariant QWinRTTheme::styleHint(QPlatformIntegration::StyleHint hint)
return defaultThemeHint(StartDragVelocity);
case QPlatformIntegration::UseRtlExtensions:
return false;
case QPlatformIntegration::SynthesizeMouseFromTouchEvents:
return true;
case QPlatformIntegration::PasswordMaskCharacter:
return defaultThemeHint(PasswordMaskCharacter);
case QPlatformIntegration::SetFocusOnTouchRelease:

View File

@ -299,7 +299,6 @@ QXcbConnection::QXcbConnection(QXcbNativeInterface *nativeInterface, bool canGra
, has_shape_extension(false)
, has_randr_extension(false)
, has_input_shape(false)
, has_touch_without_mouse_emulation(false)
, has_xkb(false)
, m_buttons(0)
, m_focusWindow(0)

View File

@ -442,7 +442,6 @@ public:
bool hasXShape() const { return has_shape_extension; }
bool hasXRandr() const { return has_randr_extension; }
bool hasInputShape() const { return has_input_shape; }
bool hasTouchWithoutMouseEmulation() const { return has_touch_without_mouse_emulation; }
bool hasXKB() const { return has_xkb; }
bool supportsThreadedRendering() const { return m_reader->isRunning(); }
@ -609,7 +608,6 @@ private:
bool has_shape_extension;
bool has_randr_extension;
bool has_input_shape;
bool has_touch_without_mouse_emulation;
bool has_xkb;
Qt::MouseButtons m_buttons;

View File

@ -282,14 +282,14 @@ void QXcbConnection::xi2Select(xcb_window_t window)
mask.mask_len = sizeof(bitMask);
mask.mask = xiBitMask;
if (!m_touchDevices.isEmpty()) {
mask.deviceid = XIAllMasterDevices;
Status result = XISelectEvents(xDisplay, window, &mask, 1);
// If we select for touch events on the master pointer, XInput2
// will not synthesize mouse events. This means Qt must do it,
// which is also preferable, since Qt can control better when
// to do so.
if (result == Success)
has_touch_without_mouse_emulation = true;
mask.deviceid = XIAllMasterDevices;
Status result = XISelectEvents(xDisplay, window, &mask, 1);
if (result != Success)
qCDebug(lcQpaXInput, "XInput 2.2: failed to select touch events, window %x, result %d", window, result);
}
}
#endif // XCB_USE_XINPUT22
@ -424,6 +424,9 @@ XInput2TouchDeviceData *QXcbConnection::touchDeviceForId(int id)
dev->size.width() > 10000 || dev->size.height() > 10000)
dev->size = QSizeF(130, 110);
}
if (!isUsingXInput22() || type == QTouchDevice::TouchPad)
caps |= QTouchDevice::MouseEmulation;
if (type >= QTouchDevice::TouchScreen && type <= QTouchDevice::TouchPad) {
dev->qtTouchDevice = new QTouchDevice;
dev->qtTouchDevice->setName(QString::fromUtf8(dev->xiDeviceInfo->name));

View File

@ -374,9 +374,6 @@ QVariant QXcbIntegration::styleHint(QPlatformIntegration::StyleHint hint) const
// X11 always has support for windows, but the
// window manager could prevent it (e.g. matchbox)
return false;
case QPlatformIntegration::SynthesizeMouseFromTouchEvents:
// We do not want Qt to synthesize mouse events if X11 already does it.
return m_connections.at(0)->hasTouchWithoutMouseEmulation();
default:
break;
}

View File

@ -68,6 +68,7 @@ private slots:
void isActive();
void testInputEvents();
void touchToMouseTranslation();
void touchToMouseTranslationForDevices();
void mouseToTouchTranslation();
void mouseToTouchLoop();
void touchCancel();
@ -705,8 +706,6 @@ void tst_QWindow::testInputEvents()
void tst_QWindow::touchToMouseTranslation()
{
if (!QGuiApplicationPrivate::platformIntegration()->styleHint(QPlatformIntegration::SynthesizeMouseFromTouchEvents).toBool())
QSKIP("Mouse events are synthesized by the system on this platform.");
InputTestWindow window;
window.ignoreTouch = true;
window.setGeometry(QRect(m_availableTopLeft + QPoint(80, 80), m_testWindowSize));
@ -779,6 +778,35 @@ void tst_QWindow::touchToMouseTranslation()
QTRY_COMPARE(window.mouseReleaseButton, 0);
}
void tst_QWindow::touchToMouseTranslationForDevices()
{
InputTestWindow window;
window.ignoreTouch = true;
window.setGeometry(QRect(m_availableTopLeft + QPoint(80, 80), m_testWindowSize));
window.show();
QVERIFY(QTest::qWaitForWindowExposed(&window));
QPoint touchPoint(10, 10);
QTest::touchEvent(&window, touchDevice).press(0, touchPoint, &window);
QTest::touchEvent(&window, touchDevice).release(0, touchPoint, &window);
QCoreApplication::processEvents();
QCOMPARE(window.mousePressedCount, 1);
QCOMPARE(window.mouseReleasedCount, 1);
window.resetCounters();
touchDevice->setCapabilities(touchDevice->capabilities() | QTouchDevice::MouseEmulation);
QTest::touchEvent(&window, touchDevice).press(0, touchPoint, &window);
QTest::touchEvent(&window, touchDevice).release(0, touchPoint, &window);
QCoreApplication::processEvents();
touchDevice->setCapabilities(touchDevice->capabilities() & ~QTouchDevice::MouseEmulation);
QCOMPARE(window.mousePressedCount, 0);
QCOMPARE(window.mouseReleasedCount, 0);
}
void tst_QWindow::mouseToTouchTranslation()
{
qApp->setAttribute(Qt::AA_SynthesizeTouchForUnhandledMouseEvents, true);
@ -907,8 +935,6 @@ void tst_QWindow::touchCancel()
void tst_QWindow::touchCancelWithTouchToMouse()
{
if (!QGuiApplicationPrivate::platformIntegration()->styleHint(QPlatformIntegration::SynthesizeMouseFromTouchEvents).toBool())
QSKIP("Mouse events are synthesized by the system on this platform.");
InputTestWindow window;
window.ignoreTouch = true;
window.setGeometry(QRect(m_availableTopLeft + QPoint(80, 80), m_testWindowSize));

View File

@ -2006,9 +2006,6 @@ void tst_QApplication::touchEventPropagation()
int argc = 1;
QApplication app(argc, &argv0);
const bool mouseEventSynthesizing = QGuiApplicationPrivate::platformIntegration()
->styleHint(QPlatformIntegration::SynthesizeMouseFromTouchEvents).toBool();
QList<QTouchEvent::TouchPoint> pressedTouchPoints;
QTouchEvent::TouchPoint press(0);
press.setState(Qt::TouchPointPressed);
@ -2047,7 +2044,7 @@ void tst_QApplication::touchEventPropagation()
touchPointList(releasedTouchPoints));
QCoreApplication::processEvents();
QVERIFY(!window.seenTouchEvent);
QCOMPARE(window.seenMouseEvent, mouseEventSynthesizing); // QApplication may transform ignored touch events in mouse events
QVERIFY(window.seenMouseEvent); // QApplication may transform ignored touch events in mouse events
window.reset();
window.setAttribute(Qt::WA_AcceptTouchEvents);
@ -2061,7 +2058,7 @@ void tst_QApplication::touchEventPropagation()
touchPointList(releasedTouchPoints));
QCoreApplication::processEvents();
QVERIFY(window.seenTouchEvent);
QCOMPARE(window.seenMouseEvent, mouseEventSynthesizing);
QVERIFY(window.seenMouseEvent);
window.reset();
window.acceptTouchEvent = true;
@ -2100,9 +2097,9 @@ void tst_QApplication::touchEventPropagation()
touchPointList(releasedTouchPoints));
QCoreApplication::processEvents();
QVERIFY(!widget.seenTouchEvent);
QCOMPARE(widget.seenMouseEvent, mouseEventSynthesizing);
QVERIFY(widget.seenMouseEvent);
QVERIFY(!window.seenTouchEvent);
QCOMPARE(window.seenMouseEvent, mouseEventSynthesizing);
QVERIFY(window.seenMouseEvent);
window.reset();
widget.reset();
@ -2117,9 +2114,9 @@ void tst_QApplication::touchEventPropagation()
touchPointList(releasedTouchPoints));
QCoreApplication::processEvents();
QVERIFY(widget.seenTouchEvent);
QCOMPARE(widget.seenMouseEvent, mouseEventSynthesizing);
QVERIFY(widget.seenMouseEvent);
QVERIFY(!window.seenTouchEvent);
QCOMPARE(window.seenMouseEvent, mouseEventSynthesizing);
QVERIFY(window.seenMouseEvent);
window.reset();
widget.reset();
@ -2134,7 +2131,7 @@ void tst_QApplication::touchEventPropagation()
touchPointList(releasedTouchPoints));
QCoreApplication::processEvents();
QVERIFY(widget.seenTouchEvent);
QCOMPARE(widget.seenMouseEvent, mouseEventSynthesizing);
QVERIFY(widget.seenMouseEvent);
QVERIFY(!window.seenTouchEvent);
QVERIFY(!window.seenMouseEvent);
@ -2169,9 +2166,9 @@ void tst_QApplication::touchEventPropagation()
touchPointList(releasedTouchPoints));
QCoreApplication::processEvents();
QVERIFY(!widget.seenTouchEvent);
QCOMPARE(widget.seenMouseEvent, mouseEventSynthesizing);
QVERIFY(widget.seenMouseEvent);
QVERIFY(window.seenTouchEvent);
QCOMPARE(window.seenMouseEvent, mouseEventSynthesizing);
QVERIFY(window.seenMouseEvent);
window.reset();
widget.reset();
@ -2186,13 +2183,13 @@ void tst_QApplication::touchEventPropagation()
touchPointList(releasedTouchPoints));
QCoreApplication::processEvents();
QVERIFY(!widget.seenTouchEvent);
QCOMPARE(widget.seenMouseEvent, mouseEventSynthesizing);
QVERIFY(!widget.seenMouseEvent);
QVERIFY(window.seenTouchEvent);
QVERIFY(!window.seenMouseEvent);
window.reset();
widget.reset();
widget.acceptMouseEvent = true; // it matters, touch events are propagated in parallel to synthesized mouse events
widget.acceptMouseEvent = true; // doesn't matter, touch events are propagated first
window.acceptTouchEvent = true;
QWindowSystemInterface::handleTouchEvent(window.windowHandle(),
0,
@ -2204,8 +2201,8 @@ void tst_QApplication::touchEventPropagation()
touchPointList(releasedTouchPoints));
QCoreApplication::processEvents();
QVERIFY(!widget.seenTouchEvent);
QCOMPARE(widget.seenMouseEvent, mouseEventSynthesizing);
QCOMPARE(!window.seenTouchEvent, mouseEventSynthesizing);
QVERIFY(!widget.seenMouseEvent);
QVERIFY(window.seenTouchEvent);
QVERIFY(!window.seenMouseEvent);
}
}

View File

@ -9824,9 +9824,6 @@ public:
void tst_QWidget::touchEventSynthesizedMouseEvent()
{
// Pass if the platform does not want mouse event synhesizing
if (!QGuiApplicationPrivate::platformIntegration()->styleHint(QPlatformIntegration::SynthesizeMouseFromTouchEvents).toBool())
return;
if (m_platform == QStringLiteral("wayland"))
QSKIP("Wayland: This fails. Figure out why.");
@ -9852,7 +9849,7 @@ void tst_QWidget::touchEventSynthesizedMouseEvent()
QCOMPARE(widget.m_lastMouseEventPos, QPointF(15, 15));
QTest::touchEvent(&widget, device).release(0, QPoint(20, 20), &widget);
QCOMPARE(widget.m_touchEventCount, 0);
QCOMPARE(widget.m_mouseEventCount, 3);
QCOMPARE(widget.m_mouseEventCount, 4); // we receive extra mouse move event
QCOMPARE(widget.m_lastMouseEventPos, QPointF(20, 20));
}
@ -9903,8 +9900,7 @@ void tst_QWidget::touchEventSynthesizedMouseEvent()
QCOMPARE(parent.m_touchEventCount, 1);
QCOMPARE(parent.m_mouseEventCount, 0);
QCOMPARE(child.m_touchEventCount, 0);
QCOMPARE(child.m_mouseEventCount, 1); // Attempt at mouse event before propagation
QCOMPARE(child.m_lastMouseEventPos, QPointF(10, 10));
QCOMPARE(child.m_mouseEventCount, 0);
}
{