From e50416066cab4be7df8382bd224d9e4ddd7a903a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20R=C3=B8dal?= Date: Thu, 12 Jan 2012 08:53:13 +0100 Subject: Added application flags to translate between touch and mouse events. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The current way we do it of having the platform or touch plugin send both mouse and touch events is not ideal. There's no good way to write an application that works sanely both on a touch-only device and on a desktop except by restricting yourself to only handling mouse events. If you try to handle touch events you don't get any events at all on desktop, and if you try to handle both, you end up getting duplicate events on touch devices. Instead, we should get rid of the code in the plugins that automatically sends mouse events translated from touch events. This change enables that by making the behaviour fully configurable in QtGui. Two new application attributes are added to explicitly say whether unhandled touch events should be sent as synthesized mouse events and vice versa, and no duplicates are automatically sent as the current situation. Synthesized mouse events are enabled by default. We also get rid of the QTouchEvent::TouchPoint::Primary flag, which was only used to signal that the windowing system automatically generated mouse events for that touch point. Now we only generate mouse events from the first touch point in the list. Change-Id: I8e20f3480407ca8c31b42de0a4d2b319e1346b65 Reviewed-by: Laszlo Agocs Reviewed-by: Jocelyn Turcotte Reviewed-by: Tor Arne Vestbø Reviewed-by: Friedemann Kleint Reviewed-by: Lars Knoll Reviewed-by: Denis Dzyubenko --- src/corelib/global/qnamespace.h | 2 + src/corelib/global/qnamespace.qdoc | 9 +++ src/corelib/kernel/qcoreapplication.cpp | 2 +- src/gui/kernel/qevent.cpp | 26 ++----- src/gui/kernel/qevent.h | 4 +- src/gui/kernel/qguiapplication.cpp | 46 +++++++++++- src/gui/kernel/qguiapplication_p.h | 1 + src/gui/kernel/qwindow.cpp | 82 +++++++++++++--------- src/gui/kernel/qwindowsysteminterface_qpa.cpp | 40 +++++++---- src/gui/kernel/qwindowsysteminterface_qpa_p.h | 5 +- src/plugins/platforms/cocoa/qmultitouch_mac.mm | 2 - .../platforms/windows/qwindowsmousehandler.cpp | 2 - src/plugins/platforms/xcb/qxcbconnection_maemo.cpp | 2 - src/widgets/kernel/qwidget.cpp | 35 +-------- 14 files changed, 143 insertions(+), 115 deletions(-) (limited to 'src') diff --git a/src/corelib/global/qnamespace.h b/src/corelib/global/qnamespace.h index d3b048579f..ee4000498f 100644 --- a/src/corelib/global/qnamespace.h +++ b/src/corelib/global/qnamespace.h @@ -481,6 +481,8 @@ public: AA_MacDontSwapCtrlAndMeta = 7, AA_Use96Dpi = 8, AA_X11InitThreads = 10, + AA_SynthesizeTouchForUnhandledMouseEvents = 11, + AA_SynthesizeMouseForUnhandledTouchEvents = 12, // Add new attributes before this line AA_AttributeCount diff --git a/src/corelib/global/qnamespace.qdoc b/src/corelib/global/qnamespace.qdoc index 3c2697e1f8..3add1a48c7 100644 --- a/src/corelib/global/qnamespace.qdoc +++ b/src/corelib/global/qnamespace.qdoc @@ -167,6 +167,15 @@ construction in order to make Xlib calls thread-safe. This attribute must be set before QApplication is constructed. + \value AA_SynthesizeTouchForUnhandledMouseEvents All mouse events + that are not accepted by the application will be translated + to touch events instead. + + \value AA_SynthesizeMouseForUnhandledTouchEvents All touch events + that are not accepted by the application will be translated + to left button mouse events instead. This attribute is enabled + by default. + \omitvalue AA_AttributeCount */ diff --git a/src/corelib/kernel/qcoreapplication.cpp b/src/corelib/kernel/qcoreapplication.cpp index 7a4618be34..05f26cc87f 100644 --- a/src/corelib/kernel/qcoreapplication.cpp +++ b/src/corelib/kernel/qcoreapplication.cpp @@ -300,7 +300,7 @@ Q_CORE_EXPORT uint qGlobalPostedEventsCount() QCoreApplication *QCoreApplication::self = 0; QAbstractEventDispatcher *QCoreApplicationPrivate::eventDispatcher = 0; -uint QCoreApplicationPrivate::attribs; +uint QCoreApplicationPrivate::attribs = (1 << Qt::AA_SynthesizeMouseForUnhandledTouchEvents); #ifdef Q_OS_UNIX Qt::HANDLE qt_application_thread_id = 0; diff --git a/src/gui/kernel/qevent.cpp b/src/gui/kernel/qevent.cpp index e0ce334321..61ccaa5cfd 100644 --- a/src/gui/kernel/qevent.cpp +++ b/src/gui/kernel/qevent.cpp @@ -3323,7 +3323,7 @@ QWindowStateChangeEvent::~QWindowStateChangeEvent() \section1 Event Delivery and Propagation - By default, QWidget::event() translates the first non-primary touch point in a QTouchEvent into + By default, QGuiApplication translates the first touch point in a QTouchEvent into a QMouseEvent. This makes it possible to enable touch events on existing widgets that do not normally handle QTouchEvent. See below for information on some special considerations needed when doing this. @@ -3361,17 +3361,12 @@ QWindowStateChangeEvent::~QWindowStateChangeEvent() This makes it possible for sibling widgets to handle touch events independently while making sure that the sequence of QTouchEvents is always correct. - \section1 Mouse Events and the Primary Touch Point + \section1 Mouse Events and Touch Event synthesizing - QTouchEvent delivery is independent from that of QMouseEvent. On some windowing systems, mouse - events are also sent for the \l{QTouchEvent::TouchPoint::isPrimary()}{primary touch point}. - This means it is possible for your widget to receive both QTouchEvent and QMouseEvent for the - same user interaction point. You can use the QTouchEvent::TouchPoint::isPrimary() function to - identify the primary touch point. - - Note that on some systems, it is possible to receive touch events without a primary touch - point. All this means is that there will be no mouse event generated for the touch points in - the QTouchEvent. + QTouchEvent delivery is independent from that of QMouseEvent. The application flags + Qt::AA_SynthesizeTouchForUnhandledMouseEvents and Qt::AA_SynthesizeMouseForUnhandledTouchEvents + can be used to enable or disable automatic synthesizing of touch events to mouse events and + mouse events to touch events. \section1 Caveats @@ -3592,15 +3587,6 @@ Qt::TouchPointState QTouchEvent::TouchPoint::state() const return Qt::TouchPointState(int(d->state)); } -/*! - Returns true if this touch point is the primary touch point. The primary touch point is the - point for which the windowing system generates mouse events. -*/ -bool QTouchEvent::TouchPoint::isPrimary() const -{ - return (d->flags & Primary) != 0; -} - /*! Returns the position of this touch point, relative to the widget or QGraphicsItem that received the event. diff --git a/src/gui/kernel/qevent.h b/src/gui/kernel/qevent.h index 5d54d39378..a59b178cec 100644 --- a/src/gui/kernel/qevent.h +++ b/src/gui/kernel/qevent.h @@ -690,8 +690,7 @@ public: { public: enum InfoFlag { - Primary = 0x0001, - Pen = 0x0002 + Pen = 0x0001 }; Q_DECLARE_FLAGS(InfoFlags, InfoFlag) @@ -702,7 +701,6 @@ public: int id() const; Qt::TouchPointState state() const; - bool isPrimary() const; QPointF pos() const; QPointF startPos() const; diff --git a/src/gui/kernel/qguiapplication.cpp b/src/gui/kernel/qguiapplication.cpp index 9f1eaeacfd..f92e66b38e 100644 --- a/src/gui/kernel/qguiapplication.cpp +++ b/src/gui/kernel/qguiapplication.cpp @@ -113,6 +113,7 @@ static Qt::LayoutDirection layout_direction = Qt::LeftToRight; static bool force_reverse = false; QGuiApplicationPrivate *QGuiApplicationPrivate::self = 0; +QTouchDevice *QGuiApplicationPrivate::m_fakeTouchDevice = 0; #ifndef QT_NO_CLIPBOARD QClipboard *QGuiApplicationPrivate::qt_clipboard = 0; @@ -689,7 +690,38 @@ void QGuiApplicationPrivate::processMouseEvent(QWindowSystemInterfacePrivate::Mo cursors.at(i).data()->pointerEvent(ev); #endif QGuiApplication::sendSpontaneousEvent(window, &ev); - return; + if (!e->synthetic && !ev.isAccepted() && qApp->testAttribute(Qt::AA_SynthesizeTouchForUnhandledMouseEvents)) { + if (!m_fakeTouchDevice) { + m_fakeTouchDevice = new QTouchDevice; + QWindowSystemInterface::registerTouchDevice(m_fakeTouchDevice); + } + QList points; + QWindowSystemInterface::TouchPoint point; + point.id = 1; + point.area = QRectF(globalPoint.x() - 2, globalPoint.y() - 2, 4, 4); + + // only translate left button related events to + // avoid strange touch event sequences when several + // buttons are pressed + if (type == QEvent::MouseButtonPress && button == Qt::LeftButton) { + point.state = Qt::TouchPointPressed; + } else if (type == QEvent::MouseButtonRelease && button == Qt::LeftButton) { + point.state = Qt::TouchPointReleased; + } else if (type == QEvent::MouseMove && (buttons & Qt::LeftButton)) { + point.state = Qt::TouchPointMoved; + } else { + return; + } + + points << point; + + QEvent::Type type; + QList touchPoints = QWindowSystemInterfacePrivate::convertTouchPoints(points, &type); + + QWindowSystemInterfacePrivate::TouchEvent fake(window, e->timestamp, type, m_fakeTouchDevice, touchPoints, e->modifiers); + fake.synthetic = true; + processTouchEvent(&fake); + } } } @@ -999,6 +1031,18 @@ void QGuiApplicationPrivate::processTouchEvent(QWindowSystemInterfacePrivate::To } QGuiApplication::sendSpontaneousEvent(w, &touchEvent); + if (!e->synthetic && !touchEvent.isAccepted() && qApp->testAttribute(Qt::AA_SynthesizeMouseForUnhandledTouchEvents)) { + // exclude touchpads as those generate their own mouse events + if (touchEvent.device()->type() != QTouchDevice::TouchPad) { + Qt::MouseButtons b = eventType == QEvent::TouchEnd ? Qt::NoButton : Qt::LeftButton; + + const QTouchEvent::TouchPoint &touchPoint = touchEvent.touchPoints().first(); + + QWindowSystemInterfacePrivate::MouseEvent fake(w, e->timestamp, touchPoint.pos(), touchPoint.screenPos(), b, e->modifiers); + fake.synthetic = true; + processMouseEvent(&fake); + } + } } // Remove released points from the hash table only after the event is diff --git a/src/gui/kernel/qguiapplication_p.h b/src/gui/kernel/qguiapplication_p.h index b1269178d0..9c965cd109 100644 --- a/src/gui/kernel/qguiapplication_p.h +++ b/src/gui/kernel/qguiapplication_p.h @@ -205,6 +205,7 @@ private: void init(); static QGuiApplicationPrivate *self; + static QTouchDevice *m_fakeTouchDevice; }; Q_GUI_EXPORT uint qHash(const QGuiApplicationPrivate::ActiveTouchPointsKey &k); diff --git a/src/gui/kernel/qwindow.cpp b/src/gui/kernel/qwindow.cpp index 0d08316945..97c6b0cbc9 100644 --- a/src/gui/kernel/qwindow.cpp +++ b/src/gui/kernel/qwindow.cpp @@ -788,78 +788,83 @@ bool QWindow::close() return true; } -void QWindow::exposeEvent(QExposeEvent *) +void QWindow::exposeEvent(QExposeEvent *ev) { + ev->ignore(); } -void QWindow::moveEvent(QMoveEvent *) +void QWindow::moveEvent(QMoveEvent *ev) { + ev->ignore(); } -void QWindow::resizeEvent(QResizeEvent *) +void QWindow::resizeEvent(QResizeEvent *ev) { + ev->ignore(); } -void QWindow::showEvent(QShowEvent *) +void QWindow::showEvent(QShowEvent *ev) { + ev->ignore(); } -void QWindow::hideEvent(QHideEvent *) +void QWindow::hideEvent(QHideEvent *ev) { + ev->ignore(); } -bool QWindow::event(QEvent *event) +bool QWindow::event(QEvent *ev) { - switch (event->type()) { + switch (ev->type()) { case QEvent::MouseMove: - mouseMoveEvent(static_cast(event)); + mouseMoveEvent(static_cast(ev)); break; case QEvent::MouseButtonPress: - mousePressEvent(static_cast(event)); + mousePressEvent(static_cast(ev)); break; case QEvent::MouseButtonRelease: - mouseReleaseEvent(static_cast(event)); + mouseReleaseEvent(static_cast(ev)); break; case QEvent::MouseButtonDblClick: - mouseDoubleClickEvent(static_cast(event)); + mouseDoubleClickEvent(static_cast(ev)); break; case QEvent::TouchBegin: case QEvent::TouchUpdate: case QEvent::TouchEnd: - touchEvent(static_cast(event)); + touchEvent(static_cast(ev)); break; case QEvent::Move: - moveEvent(static_cast(event)); + moveEvent(static_cast(ev)); break; case QEvent::Resize: - resizeEvent(static_cast(event)); + resizeEvent(static_cast(ev)); break; case QEvent::KeyPress: - keyPressEvent(static_cast(event)); + keyPressEvent(static_cast(ev)); break; case QEvent::KeyRelease: - keyReleaseEvent(static_cast(event)); + keyReleaseEvent(static_cast(ev)); break; case QEvent::FocusIn: - focusInEvent(static_cast(event)); + focusInEvent(static_cast(ev)); break; case QEvent::FocusOut: - focusOutEvent(static_cast(event)); + focusOutEvent(static_cast(ev)); break; #ifndef QT_NO_WHEELEVENT case QEvent::Wheel: - wheelEvent(static_cast(event)); + wheelEvent(static_cast(ev)); break; #endif @@ -872,66 +877,75 @@ bool QWindow::event(QEvent *event) break; } case QEvent::Expose: - exposeEvent(static_cast(event)); + exposeEvent(static_cast(ev)); break; case QEvent::Show: - showEvent(static_cast(event)); + showEvent(static_cast(ev)); break; case QEvent::Hide: - hideEvent(static_cast(event)); + hideEvent(static_cast(ev)); break; default: - return QObject::event(event); + return QObject::event(ev); } return true; } -void QWindow::keyPressEvent(QKeyEvent *) +void QWindow::keyPressEvent(QKeyEvent *ev) { + ev->ignore(); } -void QWindow::keyReleaseEvent(QKeyEvent *) +void QWindow::keyReleaseEvent(QKeyEvent *ev) { + ev->ignore(); } -void QWindow::focusInEvent(QFocusEvent *) +void QWindow::focusInEvent(QFocusEvent *ev) { + ev->ignore(); } -void QWindow::focusOutEvent(QFocusEvent *) +void QWindow::focusOutEvent(QFocusEvent *ev) { + ev->ignore(); } -void QWindow::mousePressEvent(QMouseEvent *) +void QWindow::mousePressEvent(QMouseEvent *ev) { + ev->ignore(); } -void QWindow::mouseReleaseEvent(QMouseEvent *) +void QWindow::mouseReleaseEvent(QMouseEvent *ev) { + ev->ignore(); } -void QWindow::mouseDoubleClickEvent(QMouseEvent *) +void QWindow::mouseDoubleClickEvent(QMouseEvent *ev) { + ev->ignore(); } -void QWindow::mouseMoveEvent(QMouseEvent *) +void QWindow::mouseMoveEvent(QMouseEvent *ev) { + ev->ignore(); } #ifndef QT_NO_WHEELEVENT -void QWindow::wheelEvent(QWheelEvent *) +void QWindow::wheelEvent(QWheelEvent *ev) { + ev->ignore(); } #endif //QT_NO_WHEELEVENT -void QWindow::touchEvent(QTouchEvent *) +void QWindow::touchEvent(QTouchEvent *ev) { + ev->ignore(); } - /*! \fn QPoint QWindow::mapToGlobal(const QPoint &pos) const diff --git a/src/gui/kernel/qwindowsysteminterface_qpa.cpp b/src/gui/kernel/qwindowsysteminterface_qpa.cpp index 29d134c51c..4a7ebd1c0c 100644 --- a/src/gui/kernel/qwindowsysteminterface_qpa.cpp +++ b/src/gui/kernel/qwindowsysteminterface_qpa.cpp @@ -226,21 +226,14 @@ void QWindowSystemInterface::handleTouchEvent(QWindow *w, QTouchDevice *device, handleTouchEvent(w, time, device, points, mods); } -void QWindowSystemInterface::handleTouchEvent(QWindow *tlw, ulong timestamp, QTouchDevice *device, - const QList &points, Qt::KeyboardModifiers mods) +QList QWindowSystemInterfacePrivate::convertTouchPoints(const QList &points, QEvent::Type *type) { - if (!points.size()) // Touch events must have at least one point - return; - - if (!QTouchDevicePrivate::isRegistered(device)) // Disallow passing bogus, non-registered devices. - return; - QList touchPoints; Qt::TouchPointStates states; QTouchEvent::TouchPoint p; - QList::const_iterator point = points.constBegin(); - QList::const_iterator end = points.constEnd(); + QList::const_iterator point = points.constBegin(); + QList::const_iterator end = points.constEnd(); while (point != end) { p.setId(point->id); p.setPressure(point->pressure); @@ -264,11 +257,28 @@ void QWindowSystemInterface::handleTouchEvent(QWindow *tlw, ulong timestamp, QTo } // Determine the event type based on the combined point states. - QEvent::Type type = QEvent::TouchUpdate; - if (states == Qt::TouchPointPressed) - type = QEvent::TouchBegin; - else if (states == Qt::TouchPointReleased) - type = QEvent::TouchEnd; + if (type) { + *type = QEvent::TouchUpdate; + if (states == Qt::TouchPointPressed) + *type = QEvent::TouchBegin; + else if (states == Qt::TouchPointReleased) + *type = QEvent::TouchEnd; + } + + return touchPoints; +} + +void QWindowSystemInterface::handleTouchEvent(QWindow *tlw, ulong timestamp, QTouchDevice *device, + const QList &points, Qt::KeyboardModifiers mods) +{ + if (!points.size()) // Touch events must have at least one point + return; + + if (!QTouchDevicePrivate::isRegistered(device)) // Disallow passing bogus, non-registered devices. + return; + + QEvent::Type type; + QList touchPoints = QWindowSystemInterfacePrivate::convertTouchPoints(points, &type); QWindowSystemInterfacePrivate::TouchEvent *e = new QWindowSystemInterfacePrivate::TouchEvent(tlw, timestamp, type, device, touchPoints, mods); diff --git a/src/gui/kernel/qwindowsysteminterface_qpa_p.h b/src/gui/kernel/qwindowsysteminterface_qpa_p.h index 886c1d0762..b5614eb38e 100644 --- a/src/gui/kernel/qwindowsysteminterface_qpa_p.h +++ b/src/gui/kernel/qwindowsysteminterface_qpa_p.h @@ -72,8 +72,9 @@ public: class WindowSystemEvent { public: explicit WindowSystemEvent(EventType t) - : type(t) { } + : type(t), synthetic(false) { } EventType type; + bool synthetic; }; class CloseEvent : public WindowSystemEvent { @@ -261,6 +262,8 @@ public: static void queueWindowSystemEvent(WindowSystemEvent *ev); static QTime eventTime; + + static QList convertTouchPoints(const QList &points, QEvent::Type *type); }; QT_END_HEADER diff --git a/src/plugins/platforms/cocoa/qmultitouch_mac.mm b/src/plugins/platforms/cocoa/qmultitouch_mac.mm index cc85c47c58..43767b09b2 100644 --- a/src/plugins/platforms/cocoa/qmultitouch_mac.mm +++ b/src/plugins/platforms/cocoa/qmultitouch_mac.mm @@ -73,8 +73,6 @@ QCocoaTouch::~QCocoaTouch() void QCocoaTouch::updateTouchData(NSTouch *nstouch, NSTouchPhase phase) { _touchPoint.state = toTouchPointState(phase); - if (_touchCount == 1) - _touchPoint.flags |= QTouchEvent::TouchPoint::Primary; // From the normalized position on the trackpad, calculate // where on screen the touchpoint should be according to the diff --git a/src/plugins/platforms/windows/qwindowsmousehandler.cpp b/src/plugins/platforms/windows/qwindowsmousehandler.cpp index 54a16d5013..e491029ea1 100644 --- a/src/plugins/platforms/windows/qwindowsmousehandler.cpp +++ b/src/plugins/platforms/windows/qwindowsmousehandler.cpp @@ -240,8 +240,6 @@ bool QWindowsMouseHandler::translateTouchEvent(QWindow *window, HWND, const TOUCHINPUT &winTouchInput = winTouchInputs[i]; QTouchPoint touchPoint; touchPoint.pressure = 1.0; - if ((winTouchInput.dwFlags & TOUCHEVENTF_PRIMARY) != 0) - touchPoint.flags |= QTouchEvent::TouchPoint::Primary; touchPoint.id = m_touchInputIDToTouchPointID.value(winTouchInput.dwID, -1); if (touchPoint.id == -1) { touchPoint.id = m_touchInputIDToTouchPointID.size(); diff --git a/src/plugins/platforms/xcb/qxcbconnection_maemo.cpp b/src/plugins/platforms/xcb/qxcbconnection_maemo.cpp index 1ac8e771fc..12b3d67b9f 100644 --- a/src/plugins/platforms/xcb/qxcbconnection_maemo.cpp +++ b/src/plugins/platforms/xcb/qxcbconnection_maemo.cpp @@ -222,8 +222,6 @@ void QXcbConnection::handleGenericEvent(xcb_ge_event_t *event) for (int i = 0; i < m_xinputData->xiMaxContacts; ++i) { QWindowSystemInterface::TouchPoint tp; tp.id = i; - if (i == 0) - tp.flags |= QTouchEvent::TouchPoint::Primary; tp.state = Qt::TouchPointReleased; touchPoints << tp; } diff --git a/src/widgets/kernel/qwidget.cpp b/src/widgets/kernel/qwidget.cpp index a66ccf8572..531a217b1d 100644 --- a/src/widgets/kernel/qwidget.cpp +++ b/src/widgets/kernel/qwidget.cpp @@ -8227,40 +8227,7 @@ bool QWidget::event(QEvent *event) case QEvent::TouchUpdate: case QEvent::TouchEnd: { -#ifndef Q_WS_MAC - QTouchEvent *touchEvent = static_cast(event); - const QTouchEvent::TouchPoint &touchPoint = touchEvent->touchPoints().first(); - if (touchPoint.isPrimary() || touchEvent->device()->type() == QTouchDevice::TouchPad) - break; - - // fake a mouse event! - QEvent::Type eventType = QEvent::None; - switch (touchEvent->type()) { - case QEvent::TouchBegin: - eventType = QEvent::MouseButtonPress; - break; - case QEvent::TouchUpdate: - eventType = QEvent::MouseMove; - break; - case QEvent::TouchEnd: - eventType = QEvent::MouseButtonRelease; - break; - default: - Q_ASSERT(!true); - break; - } - if (eventType == QEvent::None) - break; - - QMouseEvent mouseEvent(eventType, - touchPoint.pos(), - touchPoint.scenePos(), - touchPoint.screenPos(), - Qt::LeftButton, - Qt::LeftButton, - touchEvent->modifiers()); - (void) QApplication::sendEvent(this, &mouseEvent); -#endif // Q_WS_MAC + event->ignore(); break; } #ifndef QT_NO_GESTURES -- cgit v1.2.3