ButtonRebindsFilter: Set cursor position when rebinding tablet events

Since the tablet cursor and the mouse cursor is tracked separately,
rebinding a tablet button to a mouse click is sort of wonky. For
example, if you assign it to Right Click and attempt to open a context
menu it will appear to open in the wrong place.

So before we send the mouse button event, set the mouse position to
the tablet cursor position. A test is added to ensure this functionality
works as intended and doesn't regress.
This commit is contained in:
Joshua Goins 2024-07-19 15:34:27 -04:00 committed by Joshua Goins
parent fd670453e5
commit 59699402ad
3 changed files with 64 additions and 0 deletions

View file

@ -9,6 +9,7 @@
#include "kwin_wayland_test.h"
#include "pointer_input.h"
#include "tablet_input.h"
#include "wayland_server.h"
#include "workspace.h"
@ -42,6 +43,8 @@ private Q_SLOTS:
void testBindingTabletPad();
void testBindingTabletTool();
void testMouseTabletCursorSync();
private:
quint32 timestamp = 1;
};
@ -238,5 +241,44 @@ void TestButtonRebind::testBindingTabletTool()
Test::tabletToolButtonReleased(BTN_STYLUS, timestamp++);
}
void TestButtonRebind::testMouseTabletCursorSync()
{
KConfigGroup buttonGroup = KSharedConfig::openConfig(QStringLiteral("kcminputrc"))->group(QStringLiteral("ButtonRebinds")).group(QStringLiteral("TabletTool")).group(QStringLiteral("Virtual Tablet Tool 1"));
buttonGroup.writeEntry(QString::number(BTN_STYLUS), QStringList{"MouseButton", QString::number(BTN_LEFT)}, KConfig::Notify);
buttonGroup.sync();
std::unique_ptr<KWayland::Client::Surface> surface = Test::createSurface();
std::unique_ptr<Test::XdgToplevel> shellSurface = Test::createXdgToplevelSurface(surface.get());
auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
std::unique_ptr<KWayland::Client::Pointer> pointer(Test::waylandSeat()->createPointer());
QSignalSpy enteredSpy(pointer.get(), &KWayland::Client::Pointer::entered);
QSignalSpy buttonChangedSpy(pointer.get(), &KWayland::Client::Pointer::buttonStateChanged);
const QRectF startGeometry = window->frameGeometry();
// Move the mouse cursor to (25, 25)
input()->pointer()->warp(startGeometry.topLeft() + QPointF{25.f, 25.5f});
QVERIFY(enteredSpy.wait());
// Move the tablet cursor to (10,10)
Test::tabletToolEvent(InputRedirection::Proximity, startGeometry.topLeft() + QPointF{10.f, 10.f}, 1.0, 0, 0, 0, false, false, timestamp++);
// Verify they are not starting in the same place
QVERIFY(input()->pointer()->pos() != input()->tablet()->position());
// Send the tablet button event so it can be processed by the filter
Test::tabletToolButtonPressed(BTN_STYLUS, timestamp++);
QVERIFY(buttonChangedSpy.wait());
QCOMPARE(buttonChangedSpy.count(), 1);
QCOMPARE(buttonChangedSpy.at(0).at(2).value<qint32>(), BTN_LEFT);
Test::tabletToolButtonReleased(BTN_STYLUS, timestamp++);
// Verify that by using the mouse button binding, the mouse cursor was moved to the tablet cursor position
QVERIFY(input()->pointer()->pos() == input()->tablet()->position());
}
WAYLANDTEST_MAIN(TestButtonRebind)
#include "buttonrebind_test.moc"

View file

@ -205,6 +205,15 @@ bool ButtonRebindsFilter::pointerEvent(KWin::MouseEvent *event, quint32 nativeBu
return send(Pointer, {{}, event->button()}, event->type() == QEvent::MouseButtonPress, event->timestamp());
}
bool ButtonRebindsFilter::tabletToolEvent(KWin::TabletEvent *event)
{
if (RebindScope::isRebinding()) {
return false;
}
m_tabletCursorPos = event->position();
return false;
}
bool ButtonRebindsFilter::tabletPadButtonEvent(uint button, bool pressed, const KWin::TabletPadId &tabletPadId, std::chrono::microseconds time)
{
if (RebindScope::isRebinding()) {
@ -282,6 +291,9 @@ bool ButtonRebindsFilter::send(TriggerType type, const Trigger &trigger, bool pr
return sendKeySequence(*seq, pressed, timestamp);
}
if (const auto mb = std::get_if<MouseButton>(&action)) {
if (pressed && type != Pointer) {
sendMousePosition(m_tabletCursorPos, timestamp);
}
return sendMouseButton(mb->button, pressed, timestamp);
}
if (const auto tb = std::get_if<TabletToolButton>(&action)) {
@ -379,6 +391,13 @@ bool ButtonRebindsFilter::sendMouseButton(quint32 button, bool pressed, std::chr
return true;
}
bool ButtonRebindsFilter::sendMousePosition(QPointF position, std::chrono::microseconds time)
{
RebindScope scope;
Q_EMIT m_inputDevice.pointerMotionAbsolute(position, time, &m_inputDevice);
return true;
}
bool ButtonRebindsFilter::sendTabletToolButton(quint32 button, bool pressed, std::chrono::microseconds time)
{
if (!m_tabletTool) {

View file

@ -74,6 +74,7 @@ public:
explicit ButtonRebindsFilter();
bool pointerEvent(KWin::MouseEvent *event, quint32 nativeButton) override;
bool tabletToolEvent(KWin::TabletEvent *event) override;
bool tabletPadButtonEvent(uint button, bool pressed, const KWin::TabletPadId &tabletPadId, std::chrono::microseconds time) override;
bool tabletToolButtonEvent(uint button, bool pressed, const KWin::TabletToolId &tabletToolId, std::chrono::microseconds time) override;
@ -83,10 +84,12 @@ private:
bool send(TriggerType type, const Trigger &trigger, bool pressed, std::chrono::microseconds timestamp);
bool sendKeySequence(const QKeySequence &sequence, bool pressed, std::chrono::microseconds time);
bool sendMouseButton(quint32 button, bool pressed, std::chrono::microseconds time);
bool sendMousePosition(QPointF position, std::chrono::microseconds time);
bool sendTabletToolButton(quint32 button, bool pressed, std::chrono::microseconds time);
InputDevice m_inputDevice;
std::array<QHash<Trigger, std::variant<QKeySequence, MouseButton, TabletToolButton, DisabledButton>>, LastType> m_actions;
KConfigWatcher::Ptr m_configWatcher;
std::optional<KWin::TabletToolId> m_tabletTool;
QPointF m_cursorPos, m_tabletCursorPos;
};