2024-03-11 01:27:14 +00:00
/*
KWin - the KDE window manager
This file is part of the KDE project .
SPDX - FileCopyrightText : 2024 Aleix Pol Gonzalez < aleixpol @ kde . org >
SPDX - License - Identifier : GPL - 2.0 - or - later
*/
# include "kwin_wayland_test.h"
# include "pointer_input.h"
2024-07-19 19:34:27 +00:00
# include "tablet_input.h"
2024-03-11 01:27:14 +00:00
# include "wayland_server.h"
# include "workspace.h"
# include <KWayland/Client/keyboard.h>
2024-07-19 21:31:48 +00:00
# include <KWayland/Client/pointer.h>
2024-03-11 01:27:14 +00:00
# include <KWayland/Client/seat.h>
# include <linux/input-event-codes.h>
using namespace KWin ;
static const QString s_socketName = QStringLiteral ( " wayland_test_kwin_buttonrebind-0 " ) ;
class TestButtonRebind : public QObject
{
Q_OBJECT
private Q_SLOTS :
void init ( ) ;
void cleanup ( ) ;
void initTestCase ( ) ;
void testKey_data ( ) ;
void testKey ( ) ;
2024-07-19 21:31:48 +00:00
void testMouse_data ( ) ;
void testMouse ( ) ;
2024-07-19 21:34:01 +00:00
void testDisabled ( ) ;
2024-07-20 00:10:23 +00:00
// NOTE: Mouse buttons are not tested because those are used in the other tests
void testBindingTabletPad ( ) ;
void testBindingTabletTool ( ) ;
2024-07-19 19:34:27 +00:00
void testMouseTabletCursorSync ( ) ;
2024-03-11 01:27:14 +00:00
private :
quint32 timestamp = 1 ;
} ;
void TestButtonRebind : : init ( )
{
QVERIFY ( Test : : setupWaylandConnection ( Test : : AdditionalWaylandInterface : : Seat ) ) ;
2024-07-19 21:31:48 +00:00
QVERIFY ( Test : : waitForWaylandPointer ( ) ) ;
2024-03-11 01:27:14 +00:00
}
void TestButtonRebind : : cleanup ( )
{
Test : : destroyWaylandConnection ( ) ;
QVERIFY ( QFile : : remove ( QStandardPaths : : locate ( QStandardPaths : : ConfigLocation , QStringLiteral ( " kcminputrc " ) ) ) ) ;
}
void TestButtonRebind : : initTestCase ( )
{
qRegisterMetaType < KWin : : Window * > ( ) ;
QSignalSpy applicationStartedSpy ( kwinApp ( ) , & Application : : started ) ;
QVERIFY ( waylandServer ( ) - > init ( s_socketName ) ) ;
Test : : setOutputConfig ( {
QRect ( 0 , 0 , 1280 , 1024 ) ,
QRect ( 1280 , 0 , 1280 , 1024 ) ,
} ) ;
kwinApp ( ) - > start ( ) ;
QVERIFY ( applicationStartedSpy . wait ( ) ) ;
}
void TestButtonRebind : : testKey_data ( )
{
QTest : : addColumn < QKeySequence > ( " boundKeys " ) ;
QTest : : addColumn < QList < quint32 > > ( " expectedKeys " ) ;
QTest : : newRow ( " single key " ) < < QKeySequence ( Qt : : Key_A ) < < QList < quint32 > { KEY_A } ;
QTest : : newRow ( " single modifier " ) < < QKeySequence ( Qt : : Key_Control ) < < QList < quint32 > { KEY_LEFTCTRL } ;
QTest : : newRow ( " single modifier plus key " ) < < QKeySequence ( Qt : : ControlModifier | Qt : : Key_N ) < < QList < quint32 > { KEY_LEFTCTRL , KEY_N } ;
QTest : : newRow ( " multiple modifiers plus key " ) < < QKeySequence ( Qt : : ControlModifier | Qt : : MetaModifier | Qt : : Key_Y ) < < QList < quint32 > { KEY_LEFTCTRL , KEY_LEFTMETA , KEY_Y } ;
QTest : : newRow ( " delete " ) < < QKeySequence ( Qt : : Key_Delete ) < < QList < quint32 > { KEY_DELETE } ;
QTest : : newRow ( " keypad delete " ) < < QKeySequence ( Qt : : KeypadModifier | Qt : : Key_Delete ) < < QList < quint32 > { KEY_KPDOT } ;
QTest : : newRow ( " keypad enter " ) < < QKeySequence ( Qt : : KeypadModifier | Qt : : Key_Enter ) < < QList < quint32 > { KEY_KPENTER } ;
}
void TestButtonRebind : : testKey ( )
{
KConfigGroup buttonGroup = KSharedConfig : : openConfig ( QStringLiteral ( " kcminputrc " ) ) - > group ( QStringLiteral ( " ButtonRebinds " ) ) . group ( QStringLiteral ( " Mouse " ) ) ;
QFETCH ( QKeySequence , boundKeys ) ;
buttonGroup . writeEntry ( " ExtraButton7 " , QStringList { " Key " , boundKeys . toString ( QKeySequence : : PortableText ) } , KConfig : : Notify ) ;
buttonGroup . sync ( ) ;
std : : unique_ptr < KWayland : : Client : : Surface > surface = Test : : createSurface ( ) ;
std : : unique_ptr < Test : : XdgToplevel > shellSurface = Test : : createXdgToplevelSurface ( surface . get ( ) ) ;
Test : : renderAndWaitForShown ( surface . get ( ) , QSize ( 100 , 50 ) , Qt : : blue ) ;
std : : unique_ptr < KWayland : : Client : : Keyboard > keyboard ( Test : : waylandSeat ( ) - > createKeyboard ( ) ) ;
QSignalSpy enteredSpy ( keyboard . get ( ) , & KWayland : : Client : : Keyboard : : entered ) ;
QSignalSpy keyChangedSpy ( keyboard . get ( ) , & KWayland : : Client : : Keyboard : : keyChanged ) ;
QVERIFY ( enteredSpy . wait ( ) ) ;
// 0x119 is Qt::ExtraButton7
Test : : pointerButtonPressed ( 0x119 , timestamp + + ) ;
QVERIFY ( keyChangedSpy . wait ( ) ) ;
QFETCH ( QList < quint32 > , expectedKeys ) ;
QCOMPARE ( keyChangedSpy . count ( ) , expectedKeys . count ( ) ) ;
for ( int i = 0 ; i < keyChangedSpy . count ( ) ; i + + ) {
QCOMPARE ( keyChangedSpy . at ( i ) . at ( 0 ) . value < quint32 > ( ) , expectedKeys . at ( i ) ) ;
QCOMPARE ( keyChangedSpy . at ( i ) . at ( 1 ) . value < KWayland : : Client : : Keyboard : : KeyState > ( ) , KWayland : : Client : : Keyboard : : KeyState : : Pressed ) ;
}
Test : : pointerButtonReleased ( 0x119 , timestamp + + ) ;
}
2024-07-19 21:31:48 +00:00
void TestButtonRebind : : testMouse_data ( )
{
QTest : : addColumn < int > ( " mouseButton " ) ;
QTest : : newRow ( " left button " ) < < BTN_LEFT ;
QTest : : newRow ( " middle button " ) < < BTN_MIDDLE ;
QTest : : newRow ( " right button " ) < < BTN_RIGHT ;
}
void TestButtonRebind : : testMouse ( )
{
KConfigGroup buttonGroup = KSharedConfig : : openConfig ( QStringLiteral ( " kcminputrc " ) ) - > group ( QStringLiteral ( " ButtonRebinds " ) ) . group ( QStringLiteral ( " Mouse " ) ) ;
QFETCH ( int , mouseButton ) ;
buttonGroup . writeEntry ( " ExtraButton7 " , QStringList { " MouseButton " , QString : : number ( mouseButton ) } , 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 ( ) ;
input ( ) - > pointer ( ) - > warp ( startGeometry . center ( ) ) ;
QVERIFY ( enteredSpy . wait ( ) ) ;
// 0x119 is Qt::ExtraButton7
Test : : pointerButtonPressed ( 0x119 , timestamp + + ) ;
QVERIFY ( buttonChangedSpy . wait ( ) ) ;
QCOMPARE ( buttonChangedSpy . count ( ) , 1 ) ;
QCOMPARE ( buttonChangedSpy . at ( 0 ) . at ( 2 ) . value < qint32 > ( ) , mouseButton ) ;
Test : : pointerButtonReleased ( 0x119 , timestamp + + ) ;
}
2024-07-19 21:34:01 +00:00
void TestButtonRebind : : testDisabled ( )
{
KConfigGroup buttonGroup = KSharedConfig : : openConfig ( QStringLiteral ( " kcminputrc " ) ) - > group ( QStringLiteral ( " ButtonRebinds " ) ) . group ( QStringLiteral ( " Mouse " ) ) ;
buttonGroup . writeEntry ( " ExtraButton7 " , QStringList { " Disabled " } , 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 ( ) ;
input ( ) - > pointer ( ) - > warp ( startGeometry . center ( ) ) ;
QVERIFY ( enteredSpy . wait ( ) ) ;
// 0x119 is Qt::ExtraButton7
Test : : pointerButtonPressed ( 0x119 , timestamp + + ) ;
// Qt::ExtraButton7 should not have been emitted if this button is disabled
QVERIFY ( ! buttonChangedSpy . wait ( std : : chrono : : milliseconds ( 100 ) ) ) ;
QCOMPARE ( buttonChangedSpy . count ( ) , 0 ) ;
Test : : pointerButtonReleased ( 0x119 , timestamp + + ) ;
}
2024-07-20 00:10:23 +00:00
void TestButtonRebind : : testBindingTabletPad ( )
{
const QKeySequence sequence ( Qt : : Key_A ) ;
KConfigGroup buttonGroup = KSharedConfig : : openConfig ( QStringLiteral ( " kcminputrc " ) ) - > group ( QStringLiteral ( " ButtonRebinds " ) ) . group ( QStringLiteral ( " Tablet " ) ) . group ( QStringLiteral ( " Virtual Tablet Pad 1 " ) ) ;
buttonGroup . writeEntry ( " 1 " , QStringList { " Key " , sequence . toString ( QKeySequence : : PortableText ) } , KConfig : : Notify ) ;
buttonGroup . sync ( ) ;
std : : unique_ptr < KWayland : : Client : : Surface > surface = Test : : createSurface ( ) ;
std : : unique_ptr < Test : : XdgToplevel > shellSurface = Test : : createXdgToplevelSurface ( surface . get ( ) ) ;
Test : : renderAndWaitForShown ( surface . get ( ) , QSize ( 100 , 50 ) , Qt : : blue ) ;
std : : unique_ptr < KWayland : : Client : : Keyboard > keyboard ( Test : : waylandSeat ( ) - > createKeyboard ( ) ) ;
QSignalSpy enteredSpy ( keyboard . get ( ) , & KWayland : : Client : : Keyboard : : entered ) ;
QSignalSpy keyChangedSpy ( keyboard . get ( ) , & KWayland : : Client : : Keyboard : : keyChanged ) ;
QVERIFY ( enteredSpy . wait ( ) ) ;
Test : : tabletPadButtonPressed ( 1 , timestamp + + ) ;
QVERIFY ( keyChangedSpy . wait ( ) ) ;
QCOMPARE ( keyChangedSpy . count ( ) , 1 ) ;
QCOMPARE ( keyChangedSpy . at ( 0 ) . at ( 0 ) , KEY_A ) ;
Test : : tabletPadButtonReleased ( 1 , timestamp + + ) ;
}
void TestButtonRebind : : testBindingTabletTool ( )
{
const QKeySequence sequence ( Qt : : Key_A ) ;
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 { " Key " , sequence . toString ( QKeySequence : : PortableText ) } , 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 : : Keyboard > keyboard ( Test : : waylandSeat ( ) - > createKeyboard ( ) ) ;
QSignalSpy enteredSpy ( keyboard . get ( ) , & KWayland : : Client : : Keyboard : : entered ) ;
QSignalSpy keyChangedSpy ( keyboard . get ( ) , & KWayland : : Client : : Keyboard : : keyChanged ) ;
QVERIFY ( enteredSpy . wait ( ) ) ;
const QRectF startGeometry = window - > frameGeometry ( ) ;
2024-07-23 01:28:24 +00:00
Test : : tabletToolEvent ( InputRedirection : : Proximity , startGeometry . center ( ) , 1.0 , 0 , 0 , 0 , false , false , timestamp + + ) ;
2024-07-20 00:10:23 +00:00
Test : : tabletToolButtonPressed ( BTN_STYLUS , timestamp + + ) ;
QVERIFY ( keyChangedSpy . wait ( ) ) ;
QCOMPARE ( keyChangedSpy . count ( ) , 1 ) ;
QCOMPARE ( keyChangedSpy . at ( 0 ) . at ( 0 ) , KEY_A ) ;
Test : : tabletToolButtonReleased ( BTN_STYLUS , timestamp + + ) ;
}
2024-07-19 19:34:27 +00:00
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 ( ) ) ;
}
2024-03-11 01:27:14 +00:00
WAYLANDTEST_MAIN ( TestButtonRebind )
# include "buttonrebind_test.moc"