Warning, file /plasma/kwin/src/gestures.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
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 }