Add callback functionality for touch screen swipe gestures

Summary:
This is implemented through QActions following the general approach
inside KWin and not the older approach used by ScreenEdges for pointer
callback activation.

Test Plan: Extended auto test

Reviewers: #kwin, #plasma

Subscribers: plasma-devel

Tags: #plasma

Differential Revision: https://phabricator.kde.org/D5263
This commit is contained in:
Martin Gräßlin 2017-03-31 07:41:21 +02:00
parent 64ce6259a9
commit e6aabf5b9f
7 changed files with 216 additions and 0 deletions

View file

@ -181,6 +181,8 @@ public:
void reloadEffect(KWin::Effect *) override {}
void removeSupportProperty(const QByteArray &, KWin::Effect *) override {}
void reserveElectricBorder(KWin::ElectricBorder, KWin::Effect *) override {}
void registerTouchBorder(KWin::ElectricBorder, QAction *) override {}
void unregisterTouchBorder(KWin::ElectricBorder, QAction *) override {}
QPainter *scenePainter() override {
return nullptr;
}

View file

@ -130,6 +130,8 @@ private Q_SLOTS:
void testFullScreenBlocking();
void testClientEdge();
void testTouchEdge();
void testTouchCallback_data();
void testTouchCallback();
};
void TestScreenEdges::initTestCase()
@ -940,6 +942,98 @@ void TestScreenEdges::testTouchEdge()
}
void TestScreenEdges::testTouchCallback_data()
{
QTest::addColumn<KWin::ElectricBorder>("border");
QTest::addColumn<QPoint>("startPos");
QTest::addColumn<QSizeF>("delta");
QTest::newRow("left") << KWin::ElectricLeft << QPoint(0, 50) << QSizeF(250, 20);
QTest::newRow("top") << KWin::ElectricTop << QPoint(50, 0) << QSizeF(20, 250);
QTest::newRow("right") << KWin::ElectricRight << QPoint(99, 50) << QSizeF(-200, 0);
QTest::newRow("bottom") << KWin::ElectricBottom << QPoint(50, 99) << QSizeF(0, -200);
}
void TestScreenEdges::testTouchCallback()
{
qRegisterMetaType<KWin::ElectricBorder>("ElectricBorder");
using namespace KWin;
auto config = KSharedConfig::openConfig(QString(), KConfig::SimpleConfig);
auto group = config->group("TouchEdges");
group.writeEntry("Top", "none");
group.writeEntry("Left", "none");
group.writeEntry("Bottom", "none");
group.writeEntry("Right", "none");
config->sync();
auto s = ScreenEdges::self();
s->setConfig(config);
s->init();
// none of our actions should be reserved
const QList<Edge*> edges = s->findChildren<Edge*>(QString(), Qt::FindDirectChildrenOnly);
QCOMPARE(edges.size(), 8);
for (auto e : edges) {
QCOMPARE(e->isReserved(), false);
QCOMPARE(e->activatesForPointer(), false);
QCOMPARE(e->activatesForTouchGesture(), false);
}
// let's reserve an action
QAction action;
QSignalSpy actionTriggeredSpy(&action, &QAction::triggered);
QVERIFY(actionTriggeredSpy.isValid());
QSignalSpy approachingSpy(s, &ScreenEdges::approaching);
QVERIFY(approachingSpy.isValid());
// reserve on edge
QFETCH(KWin::ElectricBorder, border);
s->reserveTouch(border, &action);
for (auto e : edges) {
QCOMPARE(e->isReserved(), e->border() == border);
QCOMPARE(e->activatesForPointer(), false);
QCOMPARE(e->activatesForTouchGesture(), e->border() == border);
}
QVERIFY(approachingSpy.isEmpty());
QFETCH(QPoint, startPos);
QCOMPARE(s->gestureRecognizer()->startSwipeGesture(startPos), 1);
QVERIFY(actionTriggeredSpy.isEmpty());
QCOMPARE(approachingSpy.count(), 1);
QFETCH(QSizeF, delta);
s->gestureRecognizer()->updateSwipeGesture(delta);
QCOMPARE(approachingSpy.count(), 2);
QVERIFY(actionTriggeredSpy.isEmpty());
s->gestureRecognizer()->endSwipeGesture();
QVERIFY(actionTriggeredSpy.wait());
QCOMPARE(actionTriggeredSpy.count(), 1);
QCOMPARE(approachingSpy.count(), 3);
// unreserve again
s->unreserveTouch(border, &action);
for (auto e : edges) {
QCOMPARE(e->isReserved(), false);
QCOMPARE(e->activatesForPointer(), false);
QCOMPARE(e->activatesForTouchGesture(), false);
}
// reserve another action
QScopedPointer<QAction> action2(new QAction);
s->reserveTouch(border, action2.data());
for (auto e : edges) {
QCOMPARE(e->isReserved(), e->border() == border);
QCOMPARE(e->activatesForPointer(), false);
QCOMPARE(e->activatesForTouchGesture(), e->border() == border);
}
// and unreserve by destroying
action2.reset();
for (auto e : edges) {
QCOMPARE(e->isReserved(), false);
QCOMPARE(e->activatesForPointer(), false);
QCOMPARE(e->activatesForTouchGesture(), false);
}
}
Q_CONSTRUCTOR_FUNCTION(forceXcb)
QTEST_MAIN(TestScreenEdges)
#include "test_screen_edges.moc"

View file

@ -1306,6 +1306,16 @@ void EffectsHandlerImpl::unreserveElectricBorder(ElectricBorder border, Effect *
ScreenEdges::self()->unreserve(border, effect);
}
void EffectsHandlerImpl::registerTouchBorder(ElectricBorder border, QAction *action)
{
ScreenEdges::self()->reserveTouch(border, action);
}
void EffectsHandlerImpl::unregisterTouchBorder(ElectricBorder border, QAction *action)
{
ScreenEdges::self()->unreserveTouch(border, action);
}
unsigned long EffectsHandlerImpl::xrenderBufferPicture()
{
#ifdef KWIN_HAVE_XRENDER_COMPOSITING

View file

@ -172,6 +172,9 @@ public:
void reserveElectricBorder(ElectricBorder border, Effect *effect) override;
void unreserveElectricBorder(ElectricBorder border, Effect *effect) override;
void registerTouchBorder(ElectricBorder border, QAction *action) override;
void unregisterTouchBorder(ElectricBorder border, QAction *action) override;
unsigned long xrenderBufferPicture() override;
QPainter* scenePainter() override;
void reconfigure() override;

View file

@ -897,6 +897,28 @@ public:
virtual void reserveElectricBorder(ElectricBorder border, Effect *effect) = 0;
virtual void unreserveElectricBorder(ElectricBorder border, Effect *effect) = 0;
/**
* Registers the given @p action for the given @p border to be activated through
* a touch swipe gesture.
*
* If the @p border gets triggered through a touch swipe gesture the @link{QAction::triggered}
* signal gets invoked.
*
* To unregister the touch screen action either delete the @p action or
* invoke @link{unregisterTouchBorder}.
*
* @see unregisterTouchBorder
* @since 5.10
**/
virtual void registerTouchBorder(ElectricBorder border, QAction *action) = 0;
/**
* Unregisters the given @p action for the given touch @p border.
*
* @see registerTouchBorder
* @since 5.10
**/
virtual void unregisterTouchBorder(ElectricBorder border, QAction *action) = 0;
// functions that allow controlling windows/desktop
virtual void activateWindow(KWin::EffectWindow* c) = 0;
virtual KWin::EffectWindow* activeWindow() const = 0 ;

View file

@ -48,6 +48,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
// frameworks
#include <KConfigGroup>
// Qt
#include <QAction>
#include <QMouseEvent>
#include <QSharedPointer>
#include <QTimer>
@ -86,6 +87,7 @@ Edge::Edge(ScreenEdges *parent)
return;
}
handleTouchAction();
handleTouchCallback();
}, Qt::QueuedConnection
);
connect(m_gesture, &SwipeGesture::started, this, &Edge::startApproaching);
@ -132,6 +134,29 @@ void Edge::reserve(QObject *object, const char *slot)
reserve();
}
void Edge::reserveTouchCallBack(QAction *action)
{
if (m_touchActions.contains(action)) {
return;
}
connect(action, &QAction::destroyed, this,
[this, action] {
unreserveTouchCallBack(action);
}
);
m_touchActions << action;
reserve();
}
void Edge::unreserveTouchCallBack(QAction *action)
{
auto it = std::find_if(m_touchActions.begin(), m_touchActions.end(), [action] (QAction *a) { return a == action; });
if (it != m_touchActions.end()) {
m_touchActions.erase(it);
unreserve();
}
}
void Edge::unreserve()
{
m_reserved--;
@ -178,6 +203,9 @@ bool Edge::activatesForTouchGesture() const
if (m_touchAction != ElectricActionNone) {
return true;
}
if (!m_touchActions.isEmpty()) {
return true;
}
return false;
}
@ -365,6 +393,14 @@ bool Edge::handleByCallback()
return false;
}
void Edge::handleTouchCallback()
{
if (m_touchActions.isEmpty()) {
return;
}
m_touchActions.first()->trigger();
}
void Edge::switchDesktop(const QPoint &cursorPos)
{
QPoint pos(cursorPos);
@ -1002,6 +1038,10 @@ void ScreenEdges::recreateEdges()
++callback) {
edge->reserve(callback.key(), callback.value().constData());
}
const auto touchCallBacks = oldEdge->touchCallBacks();
for (auto a : touchCallBacks) {
edge->reserveTouchCallBack(a);
}
}
}
qDeleteAll(oldEdges);
@ -1188,6 +1228,24 @@ void ScreenEdges::reserve(AbstractClient *client, ElectricBorder border)
}
}
void ScreenEdges::reserveTouch(ElectricBorder border, QAction *action)
{
for (auto it = m_edges.begin(); it != m_edges.end(); ++it) {
if ((*it)->border() == border) {
(*it)->reserveTouchCallBack(action);
}
}
}
void ScreenEdges::unreserveTouch(ElectricBorder border, QAction *action)
{
for (auto it = m_edges.begin(); it != m_edges.end(); ++it) {
if ((*it)->border() == border) {
(*it)->unreserveTouchCallBack(action);
}
}
}
void ScreenEdges::createEdgeForClient(AbstractClient *client, ElectricBorder border)
{
int y = 0;

View file

@ -39,6 +39,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <QDateTime>
#include <QRect>
class QAction;
class QMouseEvent;
namespace KWin {
@ -69,6 +70,11 @@ public:
ElectricBorder border() const;
void reserve(QObject *object, const char *slot);
const QHash<QObject *, QByteArray> &callBacks() const;
void reserveTouchCallBack(QAction *action);
void unreserveTouchCallBack(QAction *action);
QVector<QAction *> touchCallBacks() const {
return m_touchActions;
}
void startApproaching();
void stopApproaching();
bool isApproaching() const;
@ -128,6 +134,7 @@ private:
return handleAction(m_touchAction);
}
bool handleByCallback();
void handleTouchCallback();
void switchDesktop(const QPoint &cursorPos);
void pushCursorBack(const QPoint &cursorPos);
ScreenEdges *m_edges;
@ -147,6 +154,7 @@ private:
bool m_pushBackBlocked;
AbstractClient *m_client;
SwipeGesture *m_gesture;
QVector<QAction *> m_touchActions;
};
/**
@ -275,6 +283,25 @@ public:
* @param border The border which the client wants to use, only proper borders are supported (no corners)
**/
void reserve(KWin::AbstractClient *client, ElectricBorder border);
/**
* Mark the specified screen edge as reserved for touch gestures. This method is provided for
* external activation like effects and scripts.
* When the effect/script does no longer need the edge it is supposed
* to call @link unreserveTouch.
* @param border the screen edge to mark as reserved
* @param action The action which gets triggered
* @see unreserveTouch
* @since 5.10
**/
void reserveTouch(ElectricBorder border, QAction *action);
/**
* Unreserves the specified @p border from activating the @p action for touch gestures.
* @see reserveTouch
* @since 5.10
**/
void unreserveTouch(ElectricBorder border, QAction *action);
/**
* Reserve desktop switching for screen edges, if @p isToReserve is @c true. Unreserve otherwise.
* @param reserve indicated weather desktop switching should be reserved or unreseved