pointer input: handle warp events differently from absolute motion events

As Wayland doesn't have a warp event yet, before this commit, warps were
dealt with like normal absolute motion events. This trips up games though,
which don't deal with actual absolute motion events well. As a solution
to that, until an actual warp event is a thing, we send a motion event with
a position + a relative motion event with no motion

BUG: 458233
CCBUG: 482476
This commit is contained in:
Xaver Hugl 2024-03-12 23:01:41 +01:00
parent 246824aa61
commit 630ba5fab4
6 changed files with 39 additions and 17 deletions

View file

@ -53,7 +53,7 @@ void InputEventsTest::testInitMouseEvent()
QFETCH(QEvent::Type, type);
// now create our own event
MouseEvent event(type, QPointF(100, 200), Qt::LeftButton, Qt::LeftButton | Qt::RightButton,
Qt::ShiftModifier | Qt::ControlModifier, 300ms, QPointF(1, 2), QPointF(3, 4), &d);
Qt::ShiftModifier | Qt::ControlModifier, 300ms, QPointF(1, 2), QPointF(3, 4), &d, false);
// and verify the contract of QMouseEvent
QCOMPARE(event.type(), type);
QCOMPARE(event.globalPos(), QPoint(100, 200));

View file

@ -1788,7 +1788,11 @@ public:
case QEvent::MouseMove: {
seat->notifyPointerMotion(event->globalPosition());
MouseEvent *e = static_cast<MouseEvent *>(event);
if (!e->delta().isNull()) {
// absolute motion events confuse games and Wayland doesn't have a warp event yet
// -> send a relative motion event with a zero delta to signal the warp instead
if (e->isWarp()) {
seat->relativePointerMotion(QPointF(0, 0), QPointF(0, 0), e->timestamp());
} else if (!e->delta().isNull()) {
seat->relativePointerMotion(e->delta(), e->deltaUnaccelerated(), e->timestamp());
}
break;

View file

@ -14,12 +14,13 @@ namespace KWin
MouseEvent::MouseEvent(QEvent::Type type, const QPointF &pos, Qt::MouseButton button,
Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers, std::chrono::microseconds timestamp,
const QPointF &delta, const QPointF &deltaNonAccelerated, InputDevice *device)
const QPointF &delta, const QPointF &deltaNonAccelerated, InputDevice *device, bool warp)
: QMouseEvent(type, pos, pos, button, buttons, modifiers)
, m_delta(delta)
, m_deltaUnccelerated(deltaNonAccelerated)
, m_timestamp(timestamp)
, m_device(device)
, m_warp(warp)
{
setTimestamp(std::chrono::duration_cast<std::chrono::milliseconds>(timestamp).count());
}

View file

@ -23,7 +23,7 @@ class MouseEvent : public QMouseEvent
public:
explicit MouseEvent(QEvent::Type type, const QPointF &pos, Qt::MouseButton button, Qt::MouseButtons buttons,
Qt::KeyboardModifiers modifiers, std::chrono::microseconds timestamp,
const QPointF &delta, const QPointF &deltaNonAccelerated, InputDevice *device);
const QPointF &delta, const QPointF &deltaNonAccelerated, InputDevice *device, bool warp);
QPointF delta() const
{
@ -65,6 +65,11 @@ public:
m_nativeButton = button;
}
bool isWarp() const
{
return m_warp;
}
private:
QPointF m_delta;
QPointF m_deltaUnccelerated;
@ -72,6 +77,7 @@ private:
InputDevice *m_device;
Qt::KeyboardModifiers m_modifiersRelevantForShortcuts = Qt::KeyboardModifiers();
quint32 m_nativeButton = 0;
bool m_warp = false;
};
// TODO: Don't derive from QWheelEvent, this event is quite domain specific.

View file

@ -187,7 +187,7 @@ public:
if (s_counter == 0) {
if (!s_scheduledPositions.isEmpty()) {
const auto pos = s_scheduledPositions.takeFirst();
m_pointer->processMotionInternal(pos.pos, pos.delta, pos.deltaNonAccelerated, pos.time, nullptr);
m_pointer->processMotionInternal(pos.pos, pos.delta, pos.deltaNonAccelerated, pos.time, nullptr, pos.type);
}
}
}
@ -197,9 +197,9 @@ public:
return s_counter > 0;
}
static void schedulePosition(const QPointF &pos, const QPointF &delta, const QPointF &deltaNonAccelerated, std::chrono::microseconds time)
static void schedulePosition(const QPointF &pos, const QPointF &delta, const QPointF &deltaNonAccelerated, std::chrono::microseconds time, PointerInputRedirection::MotionType type)
{
s_scheduledPositions.append({pos, delta, deltaNonAccelerated, time});
s_scheduledPositions.append({pos, delta, deltaNonAccelerated, time, type});
}
private:
@ -210,6 +210,7 @@ private:
QPointF delta;
QPointF deltaNonAccelerated;
std::chrono::microseconds time;
PointerInputRedirection::MotionType type;
};
static QList<ScheduledPosition> s_scheduledPositions;
@ -221,22 +222,27 @@ QList<PositionUpdateBlocker::ScheduledPosition> PositionUpdateBlocker::s_schedul
void PointerInputRedirection::processMotionAbsolute(const QPointF &pos, std::chrono::microseconds time, InputDevice *device)
{
processMotionInternal(pos, QPointF(), QPointF(), time, device);
processMotionInternal(pos, QPointF(), QPointF(), time, device, MotionType::Motion);
}
void PointerInputRedirection::processWarp(const QPointF &pos, std::chrono::microseconds time, InputDevice *device)
{
processMotionInternal(pos, QPointF(), QPointF(), time, device, MotionType::Warp);
}
void PointerInputRedirection::processMotion(const QPointF &delta, const QPointF &deltaNonAccelerated, std::chrono::microseconds time, InputDevice *device)
{
processMotionInternal(m_pos + delta, delta, deltaNonAccelerated, time, device);
processMotionInternal(m_pos + delta, delta, deltaNonAccelerated, time, device, MotionType::Motion);
}
void PointerInputRedirection::processMotionInternal(const QPointF &pos, const QPointF &delta, const QPointF &deltaNonAccelerated, std::chrono::microseconds time, InputDevice *device)
void PointerInputRedirection::processMotionInternal(const QPointF &pos, const QPointF &delta, const QPointF &deltaNonAccelerated, std::chrono::microseconds time, InputDevice *device, MotionType type)
{
input()->setLastInputHandler(this);
if (!inited()) {
return;
}
if (PositionUpdateBlocker::isPositionBlocked()) {
PositionUpdateBlocker::schedulePosition(pos, delta, deltaNonAccelerated, time);
PositionUpdateBlocker::schedulePosition(pos, delta, deltaNonAccelerated, time, type);
return;
}
@ -244,7 +250,7 @@ void PointerInputRedirection::processMotionInternal(const QPointF &pos, const QP
updatePosition(pos, time);
MouseEvent event(QEvent::MouseMove, m_pos, Qt::NoButton, m_qtButtons,
input()->keyboardModifiers(), time,
delta, deltaNonAccelerated, device);
delta, deltaNonAccelerated, device, type == MotionType::Warp);
event.setModifiersRelevantForGlobalShortcuts(input()->modifiersRelevantForGlobalShortcuts());
update();
@ -272,7 +278,7 @@ void PointerInputRedirection::processButton(uint32_t button, InputRedirection::P
updateButton(button, state);
MouseEvent event(type, m_pos, buttonToQtMouseButton(button), m_qtButtons,
input()->keyboardModifiers(), time, QPointF(), QPointF(), device);
input()->keyboardModifiers(), time, QPointF(), QPointF(), device, false);
event.setModifiersRelevantForGlobalShortcuts(input()->modifiersRelevantForGlobalShortcuts());
event.setNativeButton(button);
@ -668,7 +674,7 @@ void PointerInputRedirection::updatePointerConstraints()
m_locked = false;
disconnectLockedPointerAboutToBeUnboundConnection();
if (!(hint.x() < 0 || hint.y() < 0) && focus()) {
processMotionAbsolute(focus()->mapFromLocal(hint), waylandServer()->seat()->timestamp());
processWarp(focus()->mapFromLocal(hint), waylandServer()->seat()->timestamp());
}
}
return;
@ -688,7 +694,7 @@ void PointerInputRedirection::updatePointerConstraints()
// When the resource finally goes away, reposition the cursor according to the hint
connect(lock, &LockedPointerV1Interface::destroyed, this, [this, globalHint]() {
processMotionAbsolute(globalHint, waylandServer()->seat()->timestamp());
processWarp(globalHint, waylandServer()->seat()->timestamp());
});
});
// TODO: connect to region change - is it needed at all? If the pointer is locked it's always in the region
@ -865,7 +871,7 @@ void PointerInputRedirection::updateButton(uint32_t button, InputRedirection::Po
void PointerInputRedirection::warp(const QPointF &pos)
{
if (supportsWarping()) {
processMotionAbsolute(pos, waylandServer()->seat()->timestamp());
processWarp(pos, waylandServer()->seat()->timestamp());
}
}

View file

@ -152,7 +152,12 @@ private:
EdgeElementBarrier,
CornerBarrier,
};
void processMotionInternal(const QPointF &pos, const QPointF &delta, const QPointF &deltaNonAccelerated, std::chrono::microseconds time, InputDevice *device);
void processWarp(const QPointF &pos, std::chrono::microseconds time, InputDevice *device = nullptr);
enum class MotionType {
Motion,
Warp
};
void processMotionInternal(const QPointF &pos, const QPointF &delta, const QPointF &deltaNonAccelerated, std::chrono::microseconds time, InputDevice *device, MotionType type);
void cleanupDecoration(Decoration::DecoratedClientImpl *old, Decoration::DecoratedClientImpl *now) override;
void focusUpdate(Window *focusOld, Window *focusNow) override;