File indexing completed on 2024-04-28 16:48:47

0001 /*
0002     KWin - the KDE window manager
0003     This file is part of the KDE project.
0004 
0005     SPDX-FileCopyrightText: 2017 Martin Gräßlin <mgraesslin@kde.org>
0006 
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 #include "gestures.h"
0010 
0011 #include <QDebug>
0012 #include <QRect>
0013 #include <cmath>
0014 #include <functional>
0015 
0016 namespace KWin
0017 {
0018 
0019 Gesture::Gesture(QObject *parent)
0020     : QObject(parent)
0021 {
0022 }
0023 
0024 Gesture::~Gesture() = default;
0025 
0026 SwipeGesture::SwipeGesture(QObject *parent)
0027     : Gesture(parent)
0028 {
0029 }
0030 
0031 SwipeGesture::~SwipeGesture() = default;
0032 
0033 void SwipeGesture::setStartGeometry(const QRect &geometry)
0034 {
0035     setMinimumX(geometry.x());
0036     setMinimumY(geometry.y());
0037     setMaximumX(geometry.x() + geometry.width());
0038     setMaximumY(geometry.y() + geometry.height());
0039 
0040     Q_ASSERT(m_maximumX >= m_minimumX);
0041     Q_ASSERT(m_maximumY >= m_minimumY);
0042 }
0043 
0044 qreal SwipeGesture::deltaToProgress(const QPointF &delta) const
0045 {
0046     if (!m_minimumDeltaRelevant || m_minimumDelta.isNull()) {
0047         return 1.0;
0048     }
0049 
0050     switch (m_direction) {
0051     case Direction::Up:
0052     case Direction::Down:
0053         return std::min(std::abs(delta.y()) / std::abs(m_minimumDelta.y()), 1.0);
0054     case Direction::Left:
0055     case Direction::Right:
0056         return std::min(std::abs(delta.x()) / std::abs(m_minimumDelta.x()), 1.0);
0057     default:
0058         Q_UNREACHABLE();
0059     }
0060 }
0061 
0062 bool SwipeGesture::minimumDeltaReached(const QPointF &delta) const
0063 {
0064     return deltaToProgress(delta) >= 1.0;
0065 }
0066 
0067 PinchGesture::PinchGesture(QObject *parent)
0068     : Gesture(parent)
0069 {
0070 }
0071 
0072 PinchGesture::~PinchGesture() = default;
0073 
0074 qreal PinchGesture::scaleDeltaToProgress(const qreal &scaleDelta) const
0075 {
0076     return std::clamp(std::abs(scaleDelta - 1) / minimumScaleDelta(), 0.0, 1.0);
0077 }
0078 
0079 bool PinchGesture::minimumScaleDeltaReached(const qreal &scaleDelta) const
0080 {
0081     return scaleDeltaToProgress(scaleDelta) >= 1.0;
0082 }
0083 
0084 GestureRecognizer::GestureRecognizer(QObject *parent)
0085     : QObject(parent)
0086 {
0087 }
0088 
0089 GestureRecognizer::~GestureRecognizer() = default;
0090 
0091 void GestureRecognizer::registerSwipeGesture(KWin::SwipeGesture *gesture)
0092 {
0093     Q_ASSERT(!m_swipeGestures.contains(gesture));
0094     auto connection = connect(gesture, &QObject::destroyed, this, std::bind(&GestureRecognizer::unregisterSwipeGesture, this, gesture));
0095     m_destroyConnections.insert(gesture, connection);
0096     m_swipeGestures << gesture;
0097 }
0098 
0099 void GestureRecognizer::unregisterSwipeGesture(KWin::SwipeGesture *gesture)
0100 {
0101     auto it = m_destroyConnections.find(gesture);
0102     if (it != m_destroyConnections.end()) {
0103         disconnect(it.value());
0104         m_destroyConnections.erase(it);
0105     }
0106     m_swipeGestures.removeAll(gesture);
0107     if (m_activeSwipeGestures.removeOne(gesture)) {
0108         Q_EMIT gesture->cancelled();
0109     }
0110 }
0111 
0112 void GestureRecognizer::registerPinchGesture(KWin::PinchGesture *gesture)
0113 {
0114     Q_ASSERT(!m_pinchGestures.contains(gesture));
0115     auto connection = connect(gesture, &QObject::destroyed, this, std::bind(&GestureRecognizer::unregisterPinchGesture, this, gesture));
0116     m_destroyConnections.insert(gesture, connection);
0117     m_pinchGestures << gesture;
0118 }
0119 
0120 void GestureRecognizer::unregisterPinchGesture(KWin::PinchGesture *gesture)
0121 {
0122     auto it = m_destroyConnections.find(gesture);
0123     if (it != m_destroyConnections.end()) {
0124         disconnect(it.value());
0125         m_destroyConnections.erase(it);
0126     }
0127     m_pinchGestures.removeAll(gesture);
0128     if (m_activePinchGestures.removeOne(gesture)) {
0129         Q_EMIT gesture->cancelled();
0130     }
0131 }
0132 
0133 int GestureRecognizer::startSwipeGesture(uint fingerCount, const QPointF &startPos, StartPositionBehavior startPosBehavior)
0134 {
0135     m_currentFingerCount = fingerCount;
0136     if (!m_activeSwipeGestures.isEmpty() || !m_activePinchGestures.isEmpty()) {
0137         return 0;
0138     }
0139     int count = 0;
0140     for (SwipeGesture *gesture : std::as_const(m_swipeGestures)) {
0141         if (gesture->minimumFingerCountIsRelevant()) {
0142             if (gesture->minimumFingerCount() > fingerCount) {
0143                 continue;
0144             }
0145         }
0146         if (gesture->maximumFingerCountIsRelevant()) {
0147             if (gesture->maximumFingerCount() < fingerCount) {
0148                 continue;
0149             }
0150         }
0151         if (startPosBehavior == StartPositionBehavior::Relevant) {
0152             if (gesture->minimumXIsRelevant()) {
0153                 if (gesture->minimumX() > startPos.x()) {
0154                     continue;
0155                 }
0156             }
0157             if (gesture->maximumXIsRelevant()) {
0158                 if (gesture->maximumX() < startPos.x()) {
0159                     continue;
0160                 }
0161             }
0162             if (gesture->minimumYIsRelevant()) {
0163                 if (gesture->minimumY() > startPos.y()) {
0164                     continue;
0165                 }
0166             }
0167             if (gesture->maximumYIsRelevant()) {
0168                 if (gesture->maximumY() < startPos.y()) {
0169                     continue;
0170                 }
0171             }
0172         }
0173 
0174         // Only add gestures who's direction aligns with current swipe axis
0175         switch (gesture->direction()) {
0176         case SwipeGesture::Direction::Up:
0177         case SwipeGesture::Direction::Down:
0178             if (m_currentSwipeAxis == Axis::Horizontal) {
0179                 continue;
0180             }
0181             break;
0182         case SwipeGesture::Direction::Left:
0183         case SwipeGesture::Direction::Right:
0184             if (m_currentSwipeAxis == Axis::Vertical) {
0185                 continue;
0186             }
0187             break;
0188         }
0189 
0190         m_activeSwipeGestures << gesture;
0191         count++;
0192         Q_EMIT gesture->started();
0193     }
0194     return count;
0195 }
0196 
0197 void GestureRecognizer::updateSwipeGesture(const QPointF &delta)
0198 {
0199     m_currentDelta += delta;
0200 
0201     SwipeGesture::Direction direction; // Overall direction
0202     Axis swipeAxis;
0203 
0204     // Pick an axis for gestures so horizontal ones don't change to vertical ones without lifting fingers
0205     if (m_currentSwipeAxis == Axis::None) {
0206         if (std::abs(m_currentDelta.x()) >= std::abs(m_currentDelta.y())) {
0207             swipeAxis = Axis::Horizontal;
0208             direction = m_currentDelta.x() < 0 ? SwipeGesture::Direction::Left : SwipeGesture::Direction::Right;
0209         } else {
0210             swipeAxis = Axis::Vertical;
0211             direction = m_currentDelta.y() < 0 ? SwipeGesture::Direction::Up : SwipeGesture::Direction::Down;
0212         }
0213         if (std::abs(m_currentDelta.x()) >= 5 || std::abs(m_currentDelta.y()) >= 5) {
0214             // only lock in a direction if the delta is big enough
0215             // to prevent accidentally choosing the wrong direction
0216             m_currentSwipeAxis = swipeAxis;
0217         }
0218     } else {
0219         swipeAxis = m_currentSwipeAxis;
0220     }
0221 
0222     // Find the current swipe direction
0223     switch (swipeAxis) {
0224     case Axis::Vertical:
0225         direction = m_currentDelta.y() < 0 ? SwipeGesture::Direction::Up : SwipeGesture::Direction::Down;
0226         break;
0227     case Axis::Horizontal:
0228         direction = m_currentDelta.x() < 0 ? SwipeGesture::Direction::Left : SwipeGesture::Direction::Right;
0229         break;
0230     default:
0231         Q_UNREACHABLE();
0232     }
0233 
0234     // Eliminate wrong gestures (takes two iterations)
0235     for (int i = 0; i < 2; i++) {
0236 
0237         if (m_activeSwipeGestures.isEmpty()) {
0238             startSwipeGesture(m_currentFingerCount);
0239         }
0240 
0241         for (auto it = m_activeSwipeGestures.begin(); it != m_activeSwipeGestures.end();) {
0242             auto g = static_cast<SwipeGesture *>(*it);
0243 
0244             if (g->direction() != direction) {
0245                 // If a gesture was started from a touchscreen border never cancel it
0246                 if (!g->minimumXIsRelevant() || !g->maximumXIsRelevant() || !g->minimumYIsRelevant() || !g->maximumYIsRelevant()) {
0247                     Q_EMIT g->cancelled();
0248                     it = m_activeSwipeGestures.erase(it);
0249                     continue;
0250                 }
0251             }
0252 
0253             it++;
0254         }
0255     }
0256 
0257     // Send progress update
0258     for (SwipeGesture *g : std::as_const(m_activeSwipeGestures)) {
0259         Q_EMIT g->progress(g->deltaToProgress(m_currentDelta));
0260         Q_EMIT g->deltaProgress(m_currentDelta);
0261     }
0262 }
0263 
0264 void GestureRecognizer::cancelActiveGestures()
0265 {
0266     for (auto g : std::as_const(m_activeSwipeGestures)) {
0267         Q_EMIT g->cancelled();
0268     }
0269     for (auto g : std::as_const(m_activePinchGestures)) {
0270         Q_EMIT g->cancelled();
0271     }
0272     m_activeSwipeGestures.clear();
0273     m_activePinchGestures.clear();
0274     m_currentScale = 0;
0275     m_currentDelta = QPointF(0, 0);
0276     m_currentSwipeAxis = Axis::None;
0277 }
0278 
0279 void GestureRecognizer::cancelSwipeGesture()
0280 {
0281     cancelActiveGestures();
0282     m_currentFingerCount = 0;
0283     m_currentDelta = QPointF(0, 0);
0284     m_currentSwipeAxis = Axis::None;
0285 }
0286 
0287 void GestureRecognizer::endSwipeGesture()
0288 {
0289     const QPointF delta = m_currentDelta;
0290     for (auto g : std::as_const(m_activeSwipeGestures)) {
0291         if (static_cast<SwipeGesture *>(g)->minimumDeltaReached(delta)) {
0292             Q_EMIT g->triggered();
0293         } else {
0294             Q_EMIT g->cancelled();
0295         }
0296     }
0297     m_activeSwipeGestures.clear();
0298     m_currentFingerCount = 0;
0299     m_currentDelta = QPointF(0, 0);
0300     m_currentSwipeAxis = Axis::None;
0301 }
0302 
0303 int GestureRecognizer::startPinchGesture(uint fingerCount)
0304 {
0305     m_currentFingerCount = fingerCount;
0306     int count = 0;
0307     if (!m_activeSwipeGestures.isEmpty() || !m_activePinchGestures.isEmpty()) {
0308         return 0;
0309     }
0310     for (PinchGesture *gesture : std::as_const(m_pinchGestures)) {
0311         if (gesture->minimumFingerCountIsRelevant()) {
0312             if (gesture->minimumFingerCount() > fingerCount) {
0313                 continue;
0314             }
0315         }
0316         if (gesture->maximumFingerCountIsRelevant()) {
0317             if (gesture->maximumFingerCount() < fingerCount) {
0318                 continue;
0319             }
0320         }
0321 
0322         // direction doesn't matter yet
0323         m_activePinchGestures << gesture;
0324         count++;
0325         Q_EMIT gesture->started();
0326     }
0327     return count;
0328 }
0329 
0330 void GestureRecognizer::updatePinchGesture(qreal scale, qreal angleDelta, const QPointF &posDelta)
0331 {
0332     m_currentScale = scale;
0333 
0334     // Determine the direction of the swipe
0335     PinchGesture::Direction direction;
0336     if (scale < 1) {
0337         direction = PinchGesture::Direction::Contracting;
0338     } else {
0339         direction = PinchGesture::Direction::Expanding;
0340     }
0341 
0342     // Eliminate wrong gestures (takes two iterations)
0343     for (int i = 0; i < 2; i++) {
0344         if (m_activePinchGestures.isEmpty()) {
0345             startPinchGesture(m_currentFingerCount);
0346         }
0347 
0348         for (auto it = m_activePinchGestures.begin(); it != m_activePinchGestures.end();) {
0349             auto g = static_cast<PinchGesture *>(*it);
0350 
0351             if (g->direction() != direction) {
0352                 Q_EMIT g->cancelled();
0353                 it = m_activePinchGestures.erase(it);
0354                 continue;
0355             }
0356             it++;
0357         }
0358     }
0359 
0360     for (PinchGesture *g : std::as_const(m_activePinchGestures)) {
0361         Q_EMIT g->progress(g->scaleDeltaToProgress(scale));
0362     }
0363 }
0364 
0365 void GestureRecognizer::cancelPinchGesture()
0366 {
0367     cancelActiveGestures();
0368     m_currentScale = 1;
0369     m_currentFingerCount = 0;
0370     m_currentSwipeAxis = Axis::None;
0371 }
0372 
0373 void GestureRecognizer::endPinchGesture() // because fingers up
0374 {
0375     for (auto g : std::as_const(m_activePinchGestures)) {
0376         if (g->minimumScaleDeltaReached(m_currentScale)) {
0377             Q_EMIT g->triggered();
0378         } else {
0379             Q_EMIT g->cancelled();
0380         }
0381     }
0382     m_activeSwipeGestures.clear();
0383     m_activePinchGestures.clear();
0384     m_currentScale = 1;
0385     m_currentFingerCount = 0;
0386     m_currentSwipeAxis = Axis::None;
0387 }
0388 
0389 bool SwipeGesture::maximumFingerCountIsRelevant() const
0390 {
0391     return m_maximumFingerCountRelevant;
0392 }
0393 
0394 uint SwipeGesture::minimumFingerCount() const
0395 {
0396     return m_minimumFingerCount;
0397 }
0398 
0399 void SwipeGesture::setMinimumFingerCount(uint count)
0400 {
0401     m_minimumFingerCount = count;
0402     m_minimumFingerCountRelevant = true;
0403 }
0404 
0405 bool SwipeGesture::minimumFingerCountIsRelevant() const
0406 {
0407     return m_minimumFingerCountRelevant;
0408 }
0409 
0410 void SwipeGesture::setMaximumFingerCount(uint count)
0411 {
0412     m_maximumFingerCount = count;
0413     m_maximumFingerCountRelevant = true;
0414 }
0415 
0416 uint SwipeGesture::maximumFingerCount() const
0417 {
0418     return m_maximumFingerCount;
0419 }
0420 
0421 SwipeGesture::Direction SwipeGesture::direction() const
0422 {
0423     return m_direction;
0424 }
0425 
0426 void SwipeGesture::setDirection(Direction direction)
0427 {
0428     m_direction = direction;
0429 }
0430 
0431 void SwipeGesture::setMinimumX(int x)
0432 {
0433     m_minimumX = x;
0434     m_minimumXRelevant = true;
0435 }
0436 
0437 int SwipeGesture::minimumX() const
0438 {
0439     return m_minimumX;
0440 }
0441 
0442 bool SwipeGesture::minimumXIsRelevant() const
0443 {
0444     return m_minimumXRelevant;
0445 }
0446 
0447 void SwipeGesture::setMinimumY(int y)
0448 {
0449     m_minimumY = y;
0450     m_minimumYRelevant = true;
0451 }
0452 
0453 int SwipeGesture::minimumY() const
0454 {
0455     return m_minimumY;
0456 }
0457 
0458 bool SwipeGesture::minimumYIsRelevant() const
0459 {
0460     return m_minimumYRelevant;
0461 }
0462 
0463 void SwipeGesture::setMaximumX(int x)
0464 {
0465     m_maximumX = x;
0466     m_maximumXRelevant = true;
0467 }
0468 
0469 int SwipeGesture::maximumX() const
0470 {
0471     return m_maximumX;
0472 }
0473 
0474 bool SwipeGesture::maximumXIsRelevant() const
0475 {
0476     return m_maximumXRelevant;
0477 }
0478 
0479 void SwipeGesture::setMaximumY(int y)
0480 {
0481     m_maximumY = y;
0482     m_maximumYRelevant = true;
0483 }
0484 
0485 int SwipeGesture::maximumY() const
0486 {
0487     return m_maximumY;
0488 }
0489 
0490 bool SwipeGesture::maximumYIsRelevant() const
0491 {
0492     return m_maximumYRelevant;
0493 }
0494 
0495 QPointF SwipeGesture::minimumDelta() const
0496 {
0497     return m_minimumDelta;
0498 }
0499 
0500 void SwipeGesture::setMinimumDelta(const QPointF &delta)
0501 {
0502     m_minimumDelta = delta;
0503     m_minimumDeltaRelevant = true;
0504 }
0505 
0506 bool SwipeGesture::isMinimumDeltaRelevant() const
0507 {
0508     return m_minimumDeltaRelevant;
0509 }
0510 
0511 bool PinchGesture::minimumFingerCountIsRelevant() const
0512 {
0513     return m_minimumFingerCountRelevant;
0514 }
0515 
0516 void PinchGesture::setMinimumFingerCount(uint count)
0517 {
0518     m_minimumFingerCount = count;
0519     m_minimumFingerCountRelevant = true;
0520 }
0521 
0522 uint PinchGesture::minimumFingerCount() const
0523 {
0524     return m_minimumFingerCount;
0525 }
0526 
0527 bool PinchGesture::maximumFingerCountIsRelevant() const
0528 {
0529     return m_maximumFingerCountRelevant;
0530 }
0531 
0532 void PinchGesture::setMaximumFingerCount(uint count)
0533 {
0534     m_maximumFingerCount = count;
0535     m_maximumFingerCountRelevant = true;
0536 }
0537 
0538 uint PinchGesture::maximumFingerCount() const
0539 {
0540     return m_maximumFingerCount;
0541 }
0542 
0543 PinchGesture::Direction PinchGesture::direction() const
0544 {
0545     return m_direction;
0546 }
0547 
0548 void PinchGesture::setDirection(Direction direction)
0549 {
0550     m_direction = direction;
0551 }
0552 
0553 qreal PinchGesture::minimumScaleDelta() const
0554 {
0555     return m_minimumScaleDelta;
0556 }
0557 
0558 void PinchGesture::setMinimumScaleDelta(const qreal &scaleDelta)
0559 {
0560     m_minimumScaleDelta = scaleDelta;
0561     m_minimumScaleDeltaRelevant = true;
0562 }
0563 
0564 bool PinchGesture::isMinimumScaleDeltaRelevant() const
0565 {
0566     return m_minimumScaleDeltaRelevant;
0567 }
0568 
0569 int GestureRecognizer::startSwipeGesture(uint fingerCount)
0570 {
0571     return startSwipeGesture(fingerCount, QPointF(), StartPositionBehavior::Irrelevant);
0572 }
0573 
0574 int GestureRecognizer::startSwipeGesture(const QPointF &startPos)
0575 {
0576     return startSwipeGesture(1, startPos, StartPositionBehavior::Relevant);
0577 }
0578 
0579 }