File indexing completed on 2024-11-10 04:57:45
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"