@ -46,7 +46,8 @@ class XWaylandInputTest : public QObject
private Q_SLOTS :
void initTestCase ( ) ;
void init ( ) ;
void testPointerEnterLeave ( ) ;
void testPointerEnterLeaveSsd ( ) ;
void testPointerEventLeaveCsd ( ) ;
} ;
void XWaylandInputTest : : initTestCase ( )
@ -73,10 +74,11 @@ void XWaylandInputTest::init()
{
screens ( ) - > setCurrent ( 0 ) ;
Cursor : : setPos ( QPoint ( 640 , 512 ) ) ;
xcb_warp_pointer ( connection ( ) , XCB_WINDOW_NONE , kwinApp ( ) - > x11RootWindow ( ) , 0 , 0 , 0 , 0 , 640 , 512 ) ;
xcb_flush ( connection ( ) ) ;
QVERIFY ( waylandServer ( ) - > clients ( ) . isEmpty ( ) ) ;
}
struct XcbConnectionDeleter
{
static inline void cleanup ( xcb_connection_t * pointer )
@ -92,8 +94,8 @@ public:
X11EventReaderHelper ( xcb_connection_t * c ) ;
Q_SIGNALS :
void entered ( ) ;
void left ( ) ;
void entered ( const QPoint & localPoint ) ;
void left ( const QPoint & localPoint ) ;
private :
void processXcbEvents ( ) ;
@ -116,21 +118,23 @@ void X11EventReaderHelper::processXcbEvents()
while ( auto event = xcb_poll_for_event ( m_connection ) ) {
const uint8_t eventType = event - > response_type & ~ 0x80 ;
switch ( eventType ) {
case XCB_ENTER_NOTIFY :
emit entered ( ) ;
break ;
case XCB_LEAVE_NOTIFY :
emit left ( ) ;
break ;
case XCB_ENTER_NOTIFY : {
auto enterEvent = reinterpret_cast < xcb_enter_notify_event_t * > ( event ) ;
emit entered ( QPoint ( enterEvent - > event_x , enterEvent - > event_y ) ) ;
break ; }
case XCB_LEAVE_NOTIFY : {
auto leaveEvent = reinterpret_cast < xcb_leave_notify_event_t * > ( event ) ;
emit left ( QPoint ( leaveEvent - > event_x , leaveEvent - > event_y ) ) ;
break ; }
}
free ( event ) ;
}
xcb_flush ( m_connection ) ;
}
void XWaylandInputTest : : testPointerEnterLeave ( )
void XWaylandInputTest : : testPointerEnterLeaveSsd ( )
{
// this test simulates a pointer enter and pointer leave on an X11 window
// this test simulates a pointer enter and pointer leave on a server-side decorated X11 window
// create the test window
QScopedPointer < xcb_connection_t , XcbConnectionDeleter > c ( xcb_connect ( nullptr , nullptr ) ) ;
@ -192,10 +196,12 @@ void XWaylandInputTest::testPointerEnterLeave()
QCOMPARE ( waylandServer ( ) - > seat ( ) - > focusedPointerSurface ( ) , client - > surface ( ) ) ;
QVERIFY ( waylandServer ( ) - > seat ( ) - > focusedPointer ( ) ) ;
QVERIFY ( enteredSpy . wait ( ) ) ;
QCOMPARE ( enteredSpy . last ( ) . first ( ) , QPoint ( 49 , 81 ) ) ;
// move out of window
Cursor : : setPos ( client - > frameGeometry ( ) . bottomRight ( ) + QPoint ( 10 , 10 ) ) ;
QVERIFY ( leftSpy . wait ( ) ) ;
QCOMPARE ( leftSpy . last ( ) . first ( ) , QPoint ( 49 , 81 ) ) ;
// destroy window again
QSignalSpy windowClosedSpy ( client , & X11Client : : windowClosed ) ;
@ -206,6 +212,103 @@ void XWaylandInputTest::testPointerEnterLeave()
QVERIFY ( windowClosedSpy . wait ( ) ) ;
}
void XWaylandInputTest : : testPointerEventLeaveCsd ( )
{
// this test simulates a pointer enter and pointer leave on a client-side decorated X11 window
QScopedPointer < xcb_connection_t , XcbConnectionDeleter > c ( xcb_connect ( nullptr , nullptr ) ) ;
QVERIFY ( ! xcb_connection_has_error ( c . data ( ) ) ) ;
if ( xcb_get_setup ( c . data ( ) ) - > release_number < 11800000 ) {
QSKIP ( " XWayland 1.18 required " ) ;
}
if ( ! Xcb : : Extensions : : self ( ) - > isShapeAvailable ( ) ) {
QSKIP ( " SHAPE extension is required " ) ;
}
X11EventReaderHelper eventReader ( c . data ( ) ) ;
QSignalSpy enteredSpy ( & eventReader , & X11EventReaderHelper : : entered ) ;
QVERIFY ( enteredSpy . isValid ( ) ) ;
QSignalSpy leftSpy ( & eventReader , & X11EventReaderHelper : : left ) ;
QVERIFY ( leftSpy . isValid ( ) ) ;
// Extents of the client-side drop-shadow.
NETStrut clientFrameExtent ;
clientFrameExtent . left = 10 ;
clientFrameExtent . right = 10 ;
clientFrameExtent . top = 5 ;
clientFrameExtent . bottom = 20 ;
// Need to set the bounding shape in order to create a window without decoration.
xcb_rectangle_t boundingRect ;
boundingRect . x = 0 ;
boundingRect . y = 0 ;
boundingRect . width = 100 + clientFrameExtent . left + clientFrameExtent . right ;
boundingRect . height = 200 + clientFrameExtent . top + clientFrameExtent . bottom ;
xcb_window_t window = xcb_generate_id ( c . data ( ) ) ;
const uint32_t values [ ] = {
XCB_EVENT_MASK_ENTER_WINDOW |
XCB_EVENT_MASK_LEAVE_WINDOW
} ;
xcb_create_window ( c . data ( ) , XCB_COPY_FROM_PARENT , window , rootWindow ( ) ,
boundingRect . x , boundingRect . y , boundingRect . width , boundingRect . height ,
0 , XCB_WINDOW_CLASS_INPUT_OUTPUT , XCB_COPY_FROM_PARENT , XCB_CW_EVENT_MASK , values ) ;
xcb_size_hints_t hints ;
memset ( & hints , 0 , sizeof ( hints ) ) ;
xcb_icccm_size_hints_set_position ( & hints , 1 , boundingRect . x , boundingRect . y ) ;
xcb_icccm_size_hints_set_size ( & hints , 1 , boundingRect . width , boundingRect . height ) ;
xcb_icccm_set_wm_normal_hints ( c . data ( ) , window , & hints ) ;
xcb_shape_rectangles ( c . data ( ) , XCB_SHAPE_SO_SET , XCB_SHAPE_SK_BOUNDING ,
XCB_CLIP_ORDERING_UNSORTED , window , 0 , 0 , 1 , & boundingRect ) ;
NETWinInfo info ( c . data ( ) , window , rootWindow ( ) , NET : : WMAllProperties , NET : : WM2AllProperties ) ;
info . setWindowType ( NET : : Normal ) ;
info . setGtkFrameExtents ( clientFrameExtent ) ;
xcb_map_window ( c . data ( ) , window ) ;
xcb_flush ( c . data ( ) ) ;
QSignalSpy windowCreatedSpy ( workspace ( ) , & Workspace : : clientAdded ) ;
QVERIFY ( windowCreatedSpy . isValid ( ) ) ;
QVERIFY ( windowCreatedSpy . wait ( ) ) ;
X11Client * client = windowCreatedSpy . last ( ) . first ( ) . value < X11Client * > ( ) ;
QVERIFY ( client ) ;
QVERIFY ( ! client - > isDecorated ( ) ) ;
QVERIFY ( client - > isClientSideDecorated ( ) ) ;
QCOMPARE ( client - > bufferGeometry ( ) , QRect ( 0 , 0 , 120 , 225 ) ) ;
QCOMPARE ( client - > frameGeometry ( ) , QRect ( 10 , 5 , 100 , 200 ) ) ;
QMetaObject : : invokeMethod ( client , " setReadyForPainting " ) ;
QVERIFY ( client - > readyForPainting ( ) ) ;
QVERIFY ( ! client - > surface ( ) ) ;
QSignalSpy surfaceChangedSpy ( client , & Toplevel : : surfaceChanged ) ;
QVERIFY ( surfaceChangedSpy . isValid ( ) ) ;
QVERIFY ( surfaceChangedSpy . wait ( ) ) ;
QVERIFY ( client - > surface ( ) ) ;
// Move pointer into the window, should trigger an enter.
QVERIFY ( ! client - > frameGeometry ( ) . contains ( Cursor : : pos ( ) ) ) ;
QVERIFY ( enteredSpy . isEmpty ( ) ) ;
Cursor : : setPos ( client - > frameGeometry ( ) . center ( ) ) ;
QCOMPARE ( waylandServer ( ) - > seat ( ) - > focusedPointerSurface ( ) , client - > surface ( ) ) ;
QVERIFY ( waylandServer ( ) - > seat ( ) - > focusedPointer ( ) ) ;
QVERIFY ( enteredSpy . wait ( ) ) ;
QCOMPARE ( enteredSpy . last ( ) . first ( ) , QPoint ( 59 , 104 ) ) ;
// Move out of the window, should trigger a leave.
QVERIFY ( leftSpy . isEmpty ( ) ) ;
Cursor : : setPos ( client - > frameGeometry ( ) . bottomRight ( ) + QPoint ( 100 , 100 ) ) ;
QVERIFY ( leftSpy . wait ( ) ) ;
QCOMPARE ( leftSpy . last ( ) . first ( ) , QPoint ( 59 , 104 ) ) ;
// Destroy the window.
QSignalSpy windowClosedSpy ( client , & X11Client : : windowClosed ) ;
QVERIFY ( windowClosedSpy . isValid ( ) ) ;
xcb_unmap_window ( c . data ( ) , window ) ;
xcb_destroy_window ( c . data ( ) , window ) ;
xcb_flush ( c . data ( ) ) ;
QVERIFY ( windowClosedSpy . wait ( ) ) ;
}
}
WAYLANDTEST_MAIN ( KWin : : XWaylandInputTest )