From 6af0cc6ebe67c3ddf179879395ebc4c8a21b94db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Gr=C3=A4=C3=9Flin?= Date: Mon, 15 Aug 2016 15:44:10 +0200 Subject: [PATCH] Add support for touch events in the Effect system Summary: The Effect class is extended by three new virtual methods: * touchDown * touchMotion * touchUp The methods return a boolean value so that the events can be filtered out. E.g. an effect which has also a mouse grab installed wants to filter out all events, other effects don't need the events exclusively. This is a difference to how e.g. keyboard and pointer events are handled. But is more close to how KWin's internal input event passing works and makes it easier to get touch event: one does not explicitly has to grab the events. It's also closer to Wayland where all input events are available. As a first example the Present Windows effect is adjusted and allows to activate windows through the touch screen. As much code as possible is shared with pointer input. Reviewers: #kwin, #plasma_on_wayland Subscribers: plasma-devel, kwin Tags: #plasma_on_wayland, #kwin Differential Revision: https://phabricator.kde.org/D2450 --- effects.cpp | 34 ++++++++++++ effects.h | 4 ++ effects/presentwindows/presentwindows.cpp | 63 ++++++++++++++++++++--- effects/presentwindows/presentwindows.h | 9 ++++ input.cpp | 18 +++++++ libkwineffects/kwineffects.cpp | 23 +++++++++ libkwineffects/kwineffects.h | 61 ++++++++++++++++++++++ 7 files changed, 206 insertions(+), 6 deletions(-) diff --git a/effects.cpp b/effects.cpp index 77d52da586..5cebfa60f0 100644 --- a/effects.cpp +++ b/effects.cpp @@ -672,6 +672,40 @@ bool EffectsHandlerImpl::isMouseInterception() const return m_grabbedMouseEffects.count() > 0; } + +bool EffectsHandlerImpl::touchDown(quint32 id, const QPointF &pos, quint32 time) +{ + // TODO: reverse call order? + for (auto it = loaded_effects.constBegin(); it != loaded_effects.constEnd(); ++it) { + if (it->second->touchDown(id, pos, time)) { + return true; + } + } + return false; +} + +bool EffectsHandlerImpl::touchMotion(quint32 id, const QPointF &pos, quint32 time) +{ + // TODO: reverse call order? + for (auto it = loaded_effects.constBegin(); it != loaded_effects.constEnd(); ++it) { + if (it->second->touchMotion(id, pos, time)) { + return true; + } + } + return false; +} + +bool EffectsHandlerImpl::touchUp(quint32 id, quint32 time) +{ + // TODO: reverse call order? + for (auto it = loaded_effects.constBegin(); it != loaded_effects.constEnd(); ++it) { + if (it->second->touchUp(id, time)) { + return true; + } + } + return false; +} + void EffectsHandlerImpl::registerGlobalShortcut(const QKeySequence &shortcut, QAction *action) { input()->registerShortcut(shortcut, action); diff --git a/effects.h b/effects.h index e6f26621a1..dbdacfd8a0 100644 --- a/effects.h +++ b/effects.h @@ -232,6 +232,10 @@ public: return m_scene; } + bool touchDown(quint32 id, const QPointF &pos, quint32 time); + bool touchMotion(quint32 id, const QPointF &pos, quint32 time); + bool touchUp(quint32 id, quint32 time); + public Q_SLOTS: void slotCurrentTabAboutToChange(EffectWindow* from, EffectWindow* to); void slotTabAdded(EffectWindow* from, EffectWindow* to); diff --git a/effects/presentwindows/presentwindows.cpp b/effects/presentwindows/presentwindows.cpp index 8f192c83a4..135fbfb654 100755 --- a/effects/presentwindows/presentwindows.cpp +++ b/effects/presentwindows/presentwindows.cpp @@ -553,6 +553,11 @@ void PresentWindowsEffect::windowInputMouseEvent(QEvent *e) } } } + inputEventUpdate(me->pos(), me->type(), me->button()); +} + +void PresentWindowsEffect::inputEventUpdate(const QPoint &pos, QEvent::Type type, Qt::MouseButton button) +{ // Which window are we hovering over? Always trigger as we don't always get move events before clicking // We cannot use m_motionManager.windowAtPoint() as the window might not be visible EffectWindowList windows = m_motionManager.managedWindows(); @@ -562,7 +567,7 @@ void PresentWindowsEffect::windowInputMouseEvent(QEvent *e) DataHash::const_iterator winData = m_windowData.constFind(windows.at(i)); if (winData == m_windowData.constEnd()) continue; - if (m_motionManager.transformedGeometry(windows.at(i)).contains(cursorPos()) && + if (m_motionManager.transformedGeometry(windows.at(i)).contains(pos) && winData->visible && !winData->deleted) { hovering = true; if (windows.at(i) && m_highlightedWindow != windows.at(i)) @@ -572,15 +577,15 @@ void PresentWindowsEffect::windowInputMouseEvent(QEvent *e) } if (!hovering) setHighlightedWindow(NULL); - if (m_highlightedWindow && m_motionManager.transformedGeometry(m_highlightedWindow).contains(me->pos())) + if (m_highlightedWindow && m_motionManager.transformedGeometry(m_highlightedWindow).contains(pos)) updateCloseWindow(); else if (m_closeView) m_closeView->hide(); - if (e->type() == QEvent::MouseButtonRelease) { + if (type == QEvent::MouseButtonRelease) { if (highlightCandidate) setHighlightedWindow(highlightCandidate); - if (me->button() == Qt::LeftButton) { + if (button == Qt::LeftButton) { if (hovering) { // mouse is hovering above a window - use MouseActionsWindow mouseActionWindow(m_leftButtonWindow); @@ -589,7 +594,7 @@ void PresentWindowsEffect::windowInputMouseEvent(QEvent *e) mouseActionDesktop(m_leftButtonDesktop); } } - if (me->button() == Qt::MidButton) { + if (button == Qt::MidButton) { if (hovering) { // mouse is hovering above a window - use MouseActionsWindow mouseActionWindow(m_middleButtonWindow); @@ -598,7 +603,7 @@ void PresentWindowsEffect::windowInputMouseEvent(QEvent *e) mouseActionDesktop(m_middleButtonDesktop); } } - if (me->button() == Qt::RightButton) { + if (button == Qt::RightButton) { if (hovering) { // mouse is hovering above a window - use MouseActionsWindow mouseActionWindow(m_rightButtonWindow); @@ -611,6 +616,52 @@ void PresentWindowsEffect::windowInputMouseEvent(QEvent *e) setHighlightedWindow(highlightCandidate); } +bool PresentWindowsEffect::touchDown(quint32 id, const QPointF &pos, quint32 time) +{ + Q_UNUSED(time) + if (!m_activated) { + return false; + } + // only if we don't track a touch id yet + if (!m_touch.active) { + m_touch.active = true; + m_touch.id = id; + inputEventUpdate(pos.toPoint()); + } + return true; +} + +bool PresentWindowsEffect::touchMotion(quint32 id, const QPointF &pos, quint32 time) +{ + Q_UNUSED(id) + Q_UNUSED(time) + if (!m_activated) { + return false; + } + if (m_touch.active && m_touch.id == id) { + // only update for the touch id we track + inputEventUpdate(pos.toPoint()); + } + return true; +} + +bool PresentWindowsEffect::touchUp(quint32 id, quint32 time) +{ + Q_UNUSED(id) + Q_UNUSED(time) + if (!m_activated) { + return false; + } + if (m_touch.active && m_touch.id == id) { + m_touch.active = false; + m_touch.id = 0; + if (m_highlightedWindow) { + mouseActionWindow(m_leftButtonWindow); + } + } + return true; +} + void PresentWindowsEffect::mouseActionWindow(WindowMouseAction& action) { switch(action) { diff --git a/effects/presentwindows/presentwindows.h b/effects/presentwindows/presentwindows.h index 81154b51c4..531d2e5c51 100644 --- a/effects/presentwindows/presentwindows.h +++ b/effects/presentwindows/presentwindows.h @@ -126,6 +126,10 @@ public: virtual void grabbedKeyboardEvent(QKeyEvent *e); virtual bool isActive() const; + bool touchDown(quint32 id, const QPointF &pos, quint32 time) override; + bool touchMotion(quint32 id, const QPointF &pos, quint32 time) override; + bool touchUp(quint32 id, quint32 time) override; + int requestedEffectChainPosition() const override { return 70; } @@ -265,6 +269,7 @@ protected: // Helper functions for mouse actions void mouseActionWindow(WindowMouseAction& action); void mouseActionDesktop(DesktopMouseAction& action); + void inputEventUpdate(const QPoint &pos, QEvent::Type type = QEvent::None, Qt::MouseButton button = Qt::NoButton); private: PresentWindowsEffectProxy m_proxy; @@ -330,6 +335,10 @@ private: CloseWindowView* m_closeView; EffectWindow* m_closeWindow; Qt::Corner m_closeButtonCorner; + struct { + quint32 id = 0; + bool active = false; + } m_touch; }; } // namespace diff --git a/input.cpp b/input.cpp index 1bc5a71222..819ac05f3e 100644 --- a/input.cpp +++ b/input.cpp @@ -344,6 +344,24 @@ public: static_cast< EffectsHandlerImpl* >(effects)->grabbedKeyboardEvent(event); return true; } + bool touchDown(quint32 id, const QPointF &pos, quint32 time) override { + if (!effects) { + return false; + } + return static_cast< EffectsHandlerImpl* >(effects)->touchDown(id, pos, time); + } + bool touchMotion(quint32 id, const QPointF &pos, quint32 time) override { + if (!effects) { + return false; + } + return static_cast< EffectsHandlerImpl* >(effects)->touchMotion(id, pos, time); + } + bool touchUp(quint32 id, quint32 time) override { + if (!effects) { + return false; + } + return static_cast< EffectsHandlerImpl* >(effects)->touchUp(id, time); + } }; class MoveResizeFilter : public InputEventFilter { diff --git a/libkwineffects/kwineffects.cpp b/libkwineffects/kwineffects.cpp index b1d8f5dc88..60e3eb9dfd 100644 --- a/libkwineffects/kwineffects.cpp +++ b/libkwineffects/kwineffects.cpp @@ -672,6 +672,29 @@ xcb_window_t Effect::x11RootWindow() const return effects->x11RootWindow(); } +bool Effect::touchDown(quint32 id, const QPointF &pos, quint32 time) +{ + Q_UNUSED(id) + Q_UNUSED(pos) + Q_UNUSED(time) + return false; +} + +bool Effect::touchMotion(quint32 id, const QPointF &pos, quint32 time) +{ + Q_UNUSED(id) + Q_UNUSED(pos) + Q_UNUSED(time) + return false; +} + +bool Effect::touchUp(quint32 id, quint32 time) +{ + Q_UNUSED(id) + Q_UNUSED(time) + return false; +} + //**************************************** // EffectFactory //**************************************** diff --git a/libkwineffects/kwineffects.h b/libkwineffects/kwineffects.h index 721084cf44..3a93722982 100644 --- a/libkwineffects/kwineffects.h +++ b/libkwineffects/kwineffects.h @@ -545,6 +545,67 @@ public: **/ virtual int requestedEffectChainPosition() const; + + /** + * A touch point was pressed. + * + * If the effect wants to exclusively use the touch event it should return @c true. + * If @c false is returned the touch event is passed to further effects. + * + * In general an Effect should only return @c true if it is the exclusive effect getting + * input events. E.g. has grabbed mouse events. + * + * Default implementation returns @c false. + * + * @param id The unique id of the touch point + * @param pos The position of the touch point in global coordinates + * @param time Timestamp + * + * @see touchMotion + * @see touchUp + * @since 5.8 + **/ + virtual bool touchDown(quint32 id, const QPointF &pos, quint32 time); + /** + * A touch point moved. + * + * If the effect wants to exclusively use the touch event it should return @c true. + * If @c false is returned the touch event is passed to further effects. + * + * In general an Effect should only return @c true if it is the exclusive effect getting + * input events. E.g. has grabbed mouse events. + * + * Default implementation returns @c false. + * + * @param id The unique id of the touch point + * @param pos The position of the touch point in global coordinates + * @param time Timestamp + * + * @see touchDown + * @see touchUp + * @since 5.8 + **/ + virtual bool touchMotion(quint32 id, const QPointF &pos, quint32 time); + /** + * A touch point was released. + * + * If the effect wants to exclusively use the touch event it should return @c true. + * If @c false is returned the touch event is passed to further effects. + * + * In general an Effect should only return @c true if it is the exclusive effect getting + * input events. E.g. has grabbed mouse events. + * + * Default implementation returns @c false. + * + * @param id The unique id of the touch point + * @param time Timestamp + * + * @see touchDown + * @see touchMotion + * @since 5.8 + **/ + virtual bool touchUp(quint32 id, quint32 time); + static QPoint cursorPos(); /**