File indexing completed on 2024-04-28 05:30:17

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 SwipeDirection::Up:
0052     case SwipeDirection::Down:
0053         return std::min(std::abs(delta.y()) / std::abs(m_minimumDelta.y()), 1.0);
0054     case SwipeDirection::Left:
0055     case SwipeDirection::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 SwipeDirection::Up:
0177         case SwipeDirection::Down:
0178             if (m_currentSwipeAxis == Axis::Horizontal) {
0179                 continue;
0180             }
0181             break;
0182         case SwipeDirection::Left:
0183         case SwipeDirection::Right:
0184             if (m_currentSwipeAxis == Axis::Vertical) {
0185                 continue;
0186             }
0187             break;
0188         case SwipeDirection::Invalid:
0189             Q_UNREACHABLE();
0190         }
0191 
0192         m_activeSwipeGestures << gesture;
0193         count++;
0194         Q_EMIT gesture->started();
0195     }
0196     return count;
0197 }
0198 
0199 void GestureRecognizer::updateSwipeGesture(const QPointF &delta)
0200 {
0201     m_currentDelta += delta;
0202 
0203     SwipeDirection direction; // Overall direction
0204     Axis swipeAxis;
0205 
0206     // Pick an axis for gestures so horizontal ones don't change to vertical ones without lifting fingers
0207     if (m_currentSwipeAxis == Axis::None) {
0208         if (std::abs(m_currentDelta.x()) >= std::abs(m_currentDelta.y())) {
0209             swipeAxis = Axis::Horizontal;
0210             direction = m_currentDelta.x() < 0 ? SwipeDirection::Left : SwipeDirection::Right;
0211         } else {
0212             swipeAxis = Axis::Vertical;
0213             direction = m_currentDelta.y() < 0 ? SwipeDirection::Up : SwipeDirection::Down;
0214         }
0215         if (std::abs(m_currentDelta.x()) >= 5 || std::abs(m_currentDelta.y()) >= 5) {
0216             // only lock in a direction if the delta is big enough
0217             // to prevent accidentally choosing the wrong direction
0218             m_currentSwipeAxis = swipeAxis;
0219         }
0220     } else {
0221         swipeAxis = m_currentSwipeAxis;
0222     }
0223 
0224     // Find the current swipe direction
0225     switch (swipeAxis) {
0226     case Axis::Vertical:
0227         direction = m_currentDelta.y() < 0 ? SwipeDirection::Up : SwipeDirection::Down;
0228         break;
0229     case Axis::Horizontal:
0230         direction = m_currentDelta.x() < 0 ? SwipeDirection::Left : SwipeDirection::Right;
0231         break;
0232     default:
0233         Q_UNREACHABLE();
0234     }
0235 
0236     // Eliminate wrong gestures (takes two iterations)
0237     for (int i = 0; i < 2; i++) {
0238 
0239         if (m_activeSwipeGestures.isEmpty()) {
0240             startSwipeGesture(m_currentFingerCount);
0241         }
0242 
0243         for (auto it = m_activeSwipeGestures.begin(); it != m_activeSwipeGestures.end();) {
0244             auto g = static_cast<SwipeGesture *>(*it);
0245 
0246             if (g->direction() != direction) {
0247                 // If a gesture was started from a touchscreen border never cancel it
0248                 if (!g->minimumXIsRelevant() || !g->maximumXIsRelevant() || !g->minimumYIsRelevant() || !g->maximumYIsRelevant()) {
0249                     Q_EMIT g->cancelled();
0250                     it = m_activeSwipeGestures.erase(it);
0251                     continue;
0252                 }
0253             }
0254 
0255             it++;
0256         }
0257     }
0258 
0259     // Send progress update
0260     for (SwipeGesture *g : std::as_const(m_activeSwipeGestures)) {
0261         Q_EMIT g->progress(g->deltaToProgress(m_currentDelta));
0262         Q_EMIT g->deltaProgress(m_currentDelta);
0263     }
0264 }
0265 
0266 void GestureRecognizer::cancelActiveGestures()
0267 {
0268     for (auto g : std::as_const(m_activeSwipeGestures)) {
0269         Q_EMIT g->cancelled();
0270     }
0271     for (auto g : std::as_const(m_activePinchGestures)) {
0272         Q_EMIT g->cancelled();
0273     }
0274     m_activeSwipeGestures.clear();
0275     m_activePinchGestures.clear();
0276     m_currentScale = 0;
0277     m_currentDelta = QPointF(0, 0);
0278     m_currentSwipeAxis = Axis::None;
0279 }
0280 
0281 void GestureRecognizer::cancelSwipeGesture()
0282 {
0283     cancelActiveGestures();
0284     m_currentFingerCount = 0;
0285     m_currentDelta = QPointF(0, 0);
0286     m_currentSwipeAxis = Axis::None;
0287 }
0288 
0289 void GestureRecognizer::endSwipeGesture()
0290 {
0291     const QPointF delta = m_currentDelta;
0292     for (auto g : std::as_const(m_activeSwipeGestures)) {
0293         if (static_cast<SwipeGesture *>(g)->minimumDeltaReached(delta)) {
0294             Q_EMIT g->triggered();
0295         } else {
0296             Q_EMIT g->cancelled();
0297         }
0298     }
0299     m_activeSwipeGestures.clear();
0300     m_currentFingerCount = 0;
0301     m_currentDelta = QPointF(0, 0);
0302     m_currentSwipeAxis = Axis::None;
0303 }
0304 
0305 int GestureRecognizer::startPinchGesture(uint fingerCount)
0306 {
0307     m_currentFingerCount = fingerCount;
0308     int count = 0;
0309     if (!m_activeSwipeGestures.isEmpty() || !m_activePinchGestures.isEmpty()) {
0310         return 0;
0311     }
0312     for (PinchGesture *gesture : std::as_const(m_pinchGestures)) {
0313         if (gesture->minimumFingerCountIsRelevant()) {
0314             if (gesture->minimumFingerCount() > fingerCount) {
0315                 continue;
0316             }
0317         }
0318         if (gesture->maximumFingerCountIsRelevant()) {
0319             if (gesture->maximumFingerCount() < fingerCount) {
0320                 continue;
0321             }
0322         }
0323 
0324         // direction doesn't matter yet
0325         m_activePinchGestures << gesture;
0326         count++;
0327         Q_EMIT gesture->started();
0328     }
0329     return count;
0330 }
0331 
0332 void GestureRecognizer::updatePinchGesture(qreal scale, qreal angleDelta, const QPointF &posDelta)
0333 {
0334     m_currentScale = scale;
0335 
0336     // Determine the direction of the swipe
0337     PinchDirection direction;
0338     if (scale < 1) {
0339         direction = PinchDirection::Contracting;
0340     } else {
0341         direction = PinchDirection::Expanding;
0342     }
0343 
0344     // Eliminate wrong gestures (takes two iterations)
0345     for (int i = 0; i < 2; i++) {
0346         if (m_activePinchGestures.isEmpty()) {
0347             startPinchGesture(m_currentFingerCount);
0348         }
0349 
0350         for (auto it = m_activePinchGestures.begin(); it != m_activePinchGestures.end();) {
0351             auto g = static_cast<PinchGesture *>(*it);
0352 
0353             if (g->direction() != direction) {
0354                 Q_EMIT g->cancelled();
0355                 it = m_activePinchGestures.erase(it);
0356                 continue;
0357             }
0358             it++;
0359         }
0360     }
0361 
0362     for (PinchGesture *g : std::as_const(m_activePinchGestures)) {
0363         Q_EMIT g->progress(g->scaleDeltaToProgress(scale));
0364     }
0365 }
0366 
0367 void GestureRecognizer::cancelPinchGesture()
0368 {
0369     cancelActiveGestures();
0370     m_currentScale = 1;
0371     m_currentFingerCount = 0;
0372     m_currentSwipeAxis = Axis::None;
0373 }
0374 
0375 void GestureRecognizer::endPinchGesture() // because fingers up
0376 {
0377     for (auto g : std::as_const(m_activePinchGestures)) {
0378         if (g->minimumScaleDeltaReached(m_currentScale)) {
0379             Q_EMIT g->triggered();
0380         } else {
0381             Q_EMIT g->cancelled();
0382         }
0383     }
0384     m_activeSwipeGestures.clear();
0385     m_activePinchGestures.clear();
0386     m_currentScale = 1;
0387     m_currentFingerCount = 0;
0388     m_currentSwipeAxis = Axis::None;
0389 }
0390 
0391 bool SwipeGesture::maximumFingerCountIsRelevant() const
0392 {
0393     return m_maximumFingerCountRelevant;
0394 }
0395 
0396 uint SwipeGesture::minimumFingerCount() const
0397 {
0398     return m_minimumFingerCount;
0399 }
0400 
0401 void SwipeGesture::setMinimumFingerCount(uint count)
0402 {
0403     m_minimumFingerCount = count;
0404     m_minimumFingerCountRelevant = true;
0405 }
0406 
0407 bool SwipeGesture::minimumFingerCountIsRelevant() const
0408 {
0409     return m_minimumFingerCountRelevant;
0410 }
0411 
0412 void SwipeGesture::setMaximumFingerCount(uint count)
0413 {
0414     m_maximumFingerCount = count;
0415     m_maximumFingerCountRelevant = true;
0416 }
0417 
0418 uint SwipeGesture::maximumFingerCount() const
0419 {
0420     return m_maximumFingerCount;
0421 }
0422 
0423 SwipeDirection SwipeGesture::direction() const
0424 {
0425     return m_direction;
0426 }
0427 
0428 void SwipeGesture::setDirection(SwipeDirection direction)
0429 {
0430     m_direction = direction;
0431 }
0432 
0433 void SwipeGesture::setMinimumX(int x)
0434 {
0435     m_minimumX = x;
0436     m_minimumXRelevant = true;
0437 }
0438 
0439 int SwipeGesture::minimumX() const
0440 {
0441     return m_minimumX;
0442 }
0443 
0444 bool SwipeGesture::minimumXIsRelevant() const
0445 {
0446     return m_minimumXRelevant;
0447 }
0448 
0449 void SwipeGesture::setMinimumY(int y)
0450 {
0451     m_minimumY = y;
0452     m_minimumYRelevant = true;
0453 }
0454 
0455 int SwipeGesture::minimumY() const
0456 {
0457     return m_minimumY;
0458 }
0459 
0460 bool SwipeGesture::minimumYIsRelevant() const
0461 {
0462     return m_minimumYRelevant;
0463 }
0464 
0465 void SwipeGesture::setMaximumX(int x)
0466 {
0467     m_maximumX = x;
0468     m_maximumXRelevant = true;
0469 }
0470 
0471 int SwipeGesture::maximumX() const
0472 {
0473     return m_maximumX;
0474 }
0475 
0476 bool SwipeGesture::maximumXIsRelevant() const
0477 {
0478     return m_maximumXRelevant;
0479 }
0480 
0481 void SwipeGesture::setMaximumY(int y)
0482 {
0483     m_maximumY = y;
0484     m_maximumYRelevant = true;
0485 }
0486 
0487 int SwipeGesture::maximumY() const
0488 {
0489     return m_maximumY;
0490 }
0491 
0492 bool SwipeGesture::maximumYIsRelevant() const
0493 {
0494     return m_maximumYRelevant;
0495 }
0496 
0497 QPointF SwipeGesture::minimumDelta() const
0498 {
0499     return m_minimumDelta;
0500 }
0501 
0502 void SwipeGesture::setMinimumDelta(const QPointF &delta)
0503 {
0504     m_minimumDelta = delta;
0505     m_minimumDeltaRelevant = true;
0506 }
0507 
0508 bool SwipeGesture::isMinimumDeltaRelevant() const
0509 {
0510     return m_minimumDeltaRelevant;
0511 }
0512 
0513 bool PinchGesture::minimumFingerCountIsRelevant() const
0514 {
0515     return m_minimumFingerCountRelevant;
0516 }
0517 
0518 void PinchGesture::setMinimumFingerCount(uint count)
0519 {
0520     m_minimumFingerCount = count;
0521     m_minimumFingerCountRelevant = true;
0522 }
0523 
0524 uint PinchGesture::minimumFingerCount() const
0525 {
0526     return m_minimumFingerCount;
0527 }
0528 
0529 bool PinchGesture::maximumFingerCountIsRelevant() const
0530 {
0531     return m_maximumFingerCountRelevant;
0532 }
0533 
0534 void PinchGesture::setMaximumFingerCount(uint count)
0535 {
0536     m_maximumFingerCount = count;
0537     m_maximumFingerCountRelevant = true;
0538 }
0539 
0540 uint PinchGesture::maximumFingerCount() const
0541 {
0542     return m_maximumFingerCount;
0543 }
0544 
0545 PinchDirection PinchGesture::direction() const
0546 {
0547     return m_direction;
0548 }
0549 
0550 void PinchGesture::setDirection(PinchDirection direction)
0551 {
0552     m_direction = direction;
0553 }
0554 
0555 qreal PinchGesture::minimumScaleDelta() const
0556 {
0557     return m_minimumScaleDelta;
0558 }
0559 
0560 void PinchGesture::setMinimumScaleDelta(const qreal &scaleDelta)
0561 {
0562     m_minimumScaleDelta = scaleDelta;
0563     m_minimumScaleDeltaRelevant = true;
0564 }
0565 
0566 bool PinchGesture::isMinimumScaleDeltaRelevant() const
0567 {
0568     return m_minimumScaleDeltaRelevant;
0569 }
0570 
0571 int GestureRecognizer::startSwipeGesture(uint fingerCount)
0572 {
0573     return startSwipeGesture(fingerCount, QPointF(), StartPositionBehavior::Irrelevant);
0574 }
0575 
0576 int GestureRecognizer::startSwipeGesture(const QPointF &startPos)
0577 {
0578     return startSwipeGesture(1, startPos, StartPositionBehavior::Relevant);
0579 }
0580 
0581 }
0582 
0583 #include "moc_gestures.cpp"