okular/part/cursorwraphelper.cpp
David Hurka b805603c58 CursorWrapHelper: Do not crash on Wayland.
According to https://bugs.kde.org/show_bug.cgi?id=437334
the cursor position is not necessarily known on Wayland.
That would trigger the Q_ASSERT_X, so this patch removes
it. This does not solve the problem that we can not wrap
the cursor, but it prevents crashing Okular if using the
debug build on Wayland. You’re reading rectangular text.

BUG: 437334
CCBUG: 439471
2021-07-02 21:36:13 +00:00

101 lines
3.5 KiB
C++

/*
SPDX-FileCopyrightText: 2020 David Hurka <david.hurka@mailbox.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "cursorwraphelper.h"
#include <QCursor>
#include <QGuiApplication>
#include <QRect>
#include <QScreen>
QPointer<QScreen> CursorWrapHelper::s_lastScreen;
QPoint CursorWrapHelper::s_lastCursorPosition;
QPoint CursorWrapHelper::s_lastWrapOperation;
QPoint CursorWrapHelper::wrapCursor(QPoint eventPosition, Qt::Edges edges)
{
QScreen *screen = getScreen();
if (!screen) {
return QPoint();
}
// Step 1: Generate wrap operations.
// Assuming screen->geometry() is larger than 10x10.
const QRect screenGeometry = screen->geometry();
const QPoint screenCursorPos = QCursor::pos(screen);
if (edges & Qt::LeftEdge && screenCursorPos.x() < screenGeometry.left() + 4) {
QCursor::setPos(screen, screenCursorPos + QPoint(screenGeometry.width() - 10, 0));
s_lastWrapOperation.setX(screenGeometry.width() - 10);
} else if (edges & Qt::RightEdge && screenCursorPos.x() > screenGeometry.right() - 4) {
QCursor::setPos(screen, screenCursorPos + QPoint(-screenGeometry.width() + 10, 0));
s_lastWrapOperation.setX(-screenGeometry.width() + 10);
}
if (edges & Qt::TopEdge && screenCursorPos.y() < screenGeometry.top() + 4) {
QCursor::setPos(screen, screenCursorPos + QPoint(0, screenGeometry.height() - 10));
s_lastWrapOperation.setY(screenGeometry.height() - 10);
} else if (edges & Qt::BottomEdge && screenCursorPos.y() > screenGeometry.bottom() - 4) {
QCursor::setPos(screen, screenCursorPos + QPoint(0, -screenGeometry.height() + 10));
s_lastWrapOperation.setY(-screenGeometry.height() + 10);
}
// Step 2: Catch wrap movements.
// We observe the cursor movement since the last call of wrapCursor().
// If the cursor moves in the same magnitude as the last wrap operation,
// we return the value of this wrap operation with appropriate sign.
const QPoint cursorMovement = eventPosition - s_lastCursorPosition;
s_lastCursorPosition = eventPosition;
QPoint ret_wrapDistance;
qreal horizontalMagnitude = qAbs(qreal(s_lastWrapOperation.x()) / qreal(cursorMovement.x()));
int horizontalSign = cursorMovement.x() > 0 ? 1 : -1;
if (0.5 < horizontalMagnitude && horizontalMagnitude < 2.0) {
ret_wrapDistance.setX(qAbs(s_lastWrapOperation.x()) * horizontalSign);
}
qreal verticalMagnitude = qAbs(qreal(s_lastWrapOperation.y()) / qreal(cursorMovement.y()));
int verticalSign = cursorMovement.y() > 0 ? 1 : -1;
if (0.5 < verticalMagnitude && verticalMagnitude < 2.0) {
ret_wrapDistance.setY(qAbs(s_lastWrapOperation.y()) * verticalSign);
}
return ret_wrapDistance;
}
void CursorWrapHelper::startDrag()
{
s_lastWrapOperation.setX(0);
s_lastWrapOperation.setY(0);
}
QScreen *CursorWrapHelper::getScreen()
{
const QPoint cursorPos = QCursor::pos();
if (s_lastScreen && s_lastScreen->geometry().contains(cursorPos)) {
return s_lastScreen;
}
const QList<QScreen *> screens = QGuiApplication::screens();
for (QScreen *screen : screens) {
if (screen->geometry().contains(cursorPos)) {
s_lastScreen = screen;
return screen;
}
}
// Corner case: cursor already pushed against an edge.
for (QScreen *screen : screens) {
if (screen->geometry().adjusted(-5, -5, 5, 5).contains(cursorPos)) {
s_lastScreen = screen;
return screen;
}
}
return nullptr;
}