18#include <QAbstractEventDispatcher>
19#include <QSocketNotifier>
22#include <xcb/xcb_icccm.h>
27static const QString s_socketName = QStringLiteral(
"wayland_test_kwin_xwayland_input-0");
35 void testPointerEnterLeaveSsd();
36 void testPointerEventLeaveCsd();
39void XWaylandInputTest::initTestCase()
41 qRegisterMetaType<KWin::Window *>();
45 QRect(0, 0, 1280, 1024),
46 QRect(1280, 0, 1280, 1024),
50 QVERIFY(applicationStartedSpy.wait());
52 QCOMPARE(outputs.count(), 2);
53 QCOMPARE(outputs[0]->geometry(), QRect(0, 0, 1280, 1024));
54 QCOMPARE(outputs[1]->geometry(), QRect(1280, 0, 1280, 1024));
55 setenv(
"QT_QPA_PLATFORM",
"wayland",
true);
58void XWaylandInputTest::init()
74 void left(
const QPoint &localPoint);
77 void processXcbEvents();
78 xcb_connection_t *m_connection;
79 QSocketNotifier *m_notifier;
85 , m_notifier(new QSocketNotifier(xcb_get_file_descriptor(m_connection), QSocketNotifier::Read, this))
87 connect(m_notifier, &QSocketNotifier::activated,
this, &X11EventReaderHelper::processXcbEvents);
88 connect(QCoreApplication::eventDispatcher(), &QAbstractEventDispatcher::aboutToBlock,
this, &X11EventReaderHelper::processXcbEvents);
89 connect(QCoreApplication::eventDispatcher(), &QAbstractEventDispatcher::awake,
this, &X11EventReaderHelper::processXcbEvents);
92void X11EventReaderHelper::processXcbEvents()
94 while (
auto event = xcb_poll_for_event(m_connection)) {
95 const uint8_t eventType =
event->response_type & ~0x80;
97 case XCB_ENTER_NOTIFY: {
98 auto enterEvent =
reinterpret_cast<xcb_enter_notify_event_t *
>(event);
99 Q_EMIT
entered(QPoint(enterEvent->event_x, enterEvent->event_y));
102 case XCB_LEAVE_NOTIFY: {
103 auto leaveEvent =
reinterpret_cast<xcb_leave_notify_event_t *
>(event);
104 Q_EMIT
left(QPoint(leaveEvent->event_x, leaveEvent->event_y));
110 xcb_flush(m_connection);
113void XWaylandInputTest::testPointerEnterLeaveSsd()
119 QVERIFY(!xcb_connection_has_error(c.get()));
120 if (xcb_get_setup(c.get())->release_number < 11800000) {
121 QSKIP(
"XWayland 1.18 required");
124 xcb_warp_pointer(
connection(), XCB_WINDOW_NONE, kwinApp()->x11RootWindow(), 0, 0, 0, 0, 640, 512);
127 X11EventReaderHelper eventReader(c.get());
131 xcb_window_t windowId = xcb_generate_id(c.get());
132 const QRect windowGeometry = QRect(0, 0, 100, 200);
133 const uint32_t values[] = {
134 XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_LEAVE_WINDOW};
135 xcb_create_window(c.get(), XCB_COPY_FROM_PARENT, windowId,
rootWindow(),
138 windowGeometry.width(),
139 windowGeometry.height(),
140 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, XCB_CW_EVENT_MASK, values);
141 xcb_size_hints_t hints;
142 memset(&hints, 0,
sizeof(hints));
143 xcb_icccm_size_hints_set_position(&hints, 1, windowGeometry.x(), windowGeometry.y());
144 xcb_icccm_size_hints_set_size(&hints, 1, windowGeometry.width(), windowGeometry.height());
145 xcb_icccm_set_wm_normal_hints(c.get(), windowId, &hints);
146 NETWinInfo info(c.get(), windowId,
rootWindow(), NET::WMAllProperties, NET::WM2AllProperties);
147 info.setWindowType(NET::Normal);
148 xcb_map_window(c.get(), windowId);
152 QVERIFY(windowCreatedSpy.wait());
153 X11Window *window = windowCreatedSpy.last().first().value<X11Window *>();
155 QVERIFY(window->isDecorated());
156 QVERIFY(!window->hasStrut());
157 QVERIFY(!window->readyForPainting());
159 QMetaObject::invokeMethod(window,
"setReadyForPainting");
160 QVERIFY(window->readyForPainting());
165 QVERIFY(enteredSpy.isEmpty());
168 QVERIFY(enteredSpy.wait());
169 QCOMPARE(enteredSpy.last().first().toPoint(), (window->frameGeometry().center() - QPointF(window->frameMargins().left(), window->frameMargins().top())).toPoint());
172 input()->
pointer()->
warp(window->frameGeometry().bottomRight() + QPointF(10, 10));
173 QVERIFY(leftSpy.wait());
174 QCOMPARE(leftSpy.last().first().toPoint(), (window->frameGeometry().center() - QPointF(window->frameMargins().left(), window->frameMargins().top())).toPoint());
178 xcb_unmap_window(c.get(), windowId);
179 xcb_destroy_window(c.get(), windowId);
181 QVERIFY(windowClosedSpy.wait());
184void XWaylandInputTest::testPointerEventLeaveCsd()
189 QVERIFY(!xcb_connection_has_error(c.get()));
191 xcb_warp_pointer(
connection(), XCB_WINDOW_NONE, kwinApp()->x11RootWindow(), 0, 0, 0, 0, 640, 512);
194 if (xcb_get_setup(c.get())->release_number < 11800000) {
195 QSKIP(
"XWayland 1.18 required");
198 QSKIP(
"SHAPE extension is required");
201 X11EventReaderHelper eventReader(c.get());
206 NETStrut clientFrameExtent;
207 clientFrameExtent.left = 10;
208 clientFrameExtent.right = 10;
209 clientFrameExtent.top = 5;
210 clientFrameExtent.bottom = 20;
213 xcb_rectangle_t boundingRect;
216 boundingRect.width = 100 + clientFrameExtent.left + clientFrameExtent.right;
217 boundingRect.height = 200 + clientFrameExtent.top + clientFrameExtent.bottom;
219 xcb_window_t windowId = xcb_generate_id(c.get());
220 const uint32_t values[] = {
221 XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_LEAVE_WINDOW};
222 xcb_create_window(c.get(), XCB_COPY_FROM_PARENT, windowId,
rootWindow(),
223 boundingRect.x, boundingRect.y, boundingRect.width, boundingRect.height,
224 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, XCB_CW_EVENT_MASK, values);
225 xcb_size_hints_t hints;
226 memset(&hints, 0,
sizeof(hints));
227 xcb_icccm_size_hints_set_position(&hints, 1, boundingRect.x, boundingRect.y);
228 xcb_icccm_size_hints_set_size(&hints, 1, boundingRect.width, boundingRect.height);
229 xcb_icccm_set_wm_normal_hints(c.get(), windowId, &hints);
230 xcb_shape_rectangles(c.get(), XCB_SHAPE_SO_SET, XCB_SHAPE_SK_BOUNDING,
231 XCB_CLIP_ORDERING_UNSORTED, windowId, 0, 0, 1, &boundingRect);
232 NETWinInfo info(c.get(), windowId,
rootWindow(), NET::WMAllProperties, NET::WM2AllProperties);
233 info.setWindowType(NET::Normal);
234 info.setGtkFrameExtents(clientFrameExtent);
235 xcb_map_window(c.get(), windowId);
239 QVERIFY(windowCreatedSpy.wait());
240 X11Window *window = windowCreatedSpy.last().first().value<X11Window *>();
242 QVERIFY(!window->isDecorated());
243 QVERIFY(window->isClientSideDecorated());
244 QCOMPARE(window->bufferGeometry(), QRectF(0, 0, 120, 225));
245 QCOMPARE(window->frameGeometry(), QRectF(10, 5, 100, 200));
247 QMetaObject::invokeMethod(window,
"setReadyForPainting");
248 QVERIFY(window->readyForPainting());
253 QVERIFY(enteredSpy.isEmpty());
256 QVERIFY(enteredSpy.wait());
257 QCOMPARE(enteredSpy.last().first().toPoint(), QPoint(60, 105));
260 QVERIFY(leftSpy.isEmpty());
261 input()->
pointer()->
warp(window->frameGeometry().bottomRight() + QPoint(100, 100));
262 QVERIFY(leftSpy.wait());
263 QCOMPARE(leftSpy.last().first().toPoint(), QPoint(60, 105));
267 xcb_unmap_window(c.get(), windowId);
268 xcb_destroy_window(c.get(), windowId);
270 QVERIFY(windowClosedSpy.wait());
276#include "xwayland_input_test.moc"
void windowAdded(KWin::Window *)
QList< Output * > outputs() const
void setActiveOutput(Output *output)
void entered(const QPoint &localPoint)
X11EventReaderHelper(xcb_connection_t *c)
void left(const QPoint &localPoint)
static Extensions * self()
#define WAYLANDTEST_MAIN(TestObject)
void setOutputConfig(const QList< QRect > &geometries)
KWayland::Client::Seat * seat
bool waitForWaylandSurface(Window *window)
XcbConnectionPtr createX11Connection()
std::unique_ptr< xcb_connection_t, XcbConnectionDeleter > XcbConnectionPtr
KWIN_EXPORT xcb_window_t rootWindow()
WaylandServer * waylandServer()
KWIN_EXPORT xcb_connection_t * connection()
InputRedirection * input()