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:
parent
246824aa61
commit
630ba5fab4
6 changed files with 39 additions and 17 deletions
|
@ -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));
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue