diff options
Diffstat (limited to 'tests')
-rw-r--r-- | tests/auto/client/client/tst_client.cpp | 20 | ||||
-rw-r--r-- | tests/auto/client/shared/mockcompositor.cpp | 5 | ||||
-rw-r--r-- | tests/auto/client/shared/mockcompositor.h | 15 | ||||
-rw-r--r-- | tests/auto/client/shared/mocksurface.cpp | 5 | ||||
-rw-r--r-- | tests/auto/client/shared/mockxdgshellv6.cpp | 43 | ||||
-rw-r--r-- | tests/auto/client/shared/mockxdgshellv6.h | 6 | ||||
-rw-r--r-- | tests/auto/client/xdgshellv6/tst_xdgshellv6.cpp | 236 | ||||
-rw-r--r-- | tests/auto/compositor/compositor/compositor.pro | 4 | ||||
-rw-r--r-- | tests/auto/compositor/compositor/mockclient.h | 2 | ||||
-rw-r--r-- | tests/auto/compositor/compositor/mockkeyboard.cpp | 101 | ||||
-rw-r--r-- | tests/auto/compositor/compositor/mockkeyboard.h | 50 | ||||
-rw-r--r-- | tests/auto/compositor/compositor/mockseat.cpp | 5 | ||||
-rw-r--r-- | tests/auto/compositor/compositor/mockseat.h | 4 | ||||
-rw-r--r-- | tests/auto/compositor/compositor/tst_compositor.cpp | 141 |
14 files changed, 618 insertions, 19 deletions
diff --git a/tests/auto/client/client/tst_client.cpp b/tests/auto/client/client/tst_client.cpp index 874de82c..470db25a 100644 --- a/tests/auto/client/client/tst_client.cpp +++ b/tests/auto/client/client/tst_client.cpp @@ -173,6 +173,7 @@ private slots: void windowScreens(); void removePrimaryScreen(); void createDestroyWindow(); + void activeWindowFollowsKeyboardFocus(); void events(); void backingStore(); void touchDrag(); @@ -309,7 +310,7 @@ void tst_WaylandClient::createDestroyWindow() QTRY_VERIFY(!compositor->surface()); } -void tst_WaylandClient::events() +void tst_WaylandClient::activeWindowFollowsKeyboardFocus() { TestWindow window; window.show(); @@ -320,6 +321,9 @@ void tst_WaylandClient::events() QTRY_VERIFY(window.isExposed()); + if (compositor->xdgToplevelV6()) + QSKIP("On xdg-shell v6 focus is handled by configure events"); + QCOMPARE(window.focusInEventCount, 0); compositor->setKeyboardFocus(surface); QTRY_COMPARE(window.focusInEventCount, 1); @@ -329,9 +333,21 @@ void tst_WaylandClient::events() compositor->setKeyboardFocus(QSharedPointer<MockSurface>(nullptr)); QTRY_COMPARE(window.focusOutEventCount, 1); QTRY_COMPARE(QGuiApplication::focusWindow(), static_cast<QWindow *>(nullptr)); +} + +void tst_WaylandClient::events() +{ + TestWindow window; + window.show(); + + QSharedPointer<MockSurface> surface; + QTRY_VERIFY(surface = compositor->surface()); + compositor->sendShellSurfaceConfigure(surface); + + QTRY_VERIFY(window.isExposed()); compositor->setKeyboardFocus(surface); - QTRY_COMPARE(window.focusInEventCount, 2); + QTRY_COMPARE(window.focusInEventCount, 1); QTRY_COMPARE(QGuiApplication::focusWindow(), &window); uint keyCode = 80; // arbitrarily chosen diff --git a/tests/auto/client/shared/mockcompositor.cpp b/tests/auto/client/shared/mockcompositor.cpp index 9ef08ad9..077b8ee0 100644 --- a/tests/auto/client/shared/mockcompositor.cpp +++ b/tests/auto/client/shared/mockcompositor.cpp @@ -236,11 +236,14 @@ void MockCompositor::sendIviSurfaceConfigure(const QSharedPointer<MockIviSurface processCommand(command); } -void MockCompositor::sendXdgToplevelV6Configure(const QSharedPointer<MockXdgToplevelV6> toplevel, const QSize &size) +void MockCompositor::sendXdgToplevelV6Configure(const QSharedPointer<MockXdgToplevelV6> toplevel, const QSize &size, const QVector<uint> &states) { Command command = makeCommand(Impl::Compositor::sendXdgToplevelV6Configure, m_compositor); command.parameters << QVariant::fromValue(toplevel); command.parameters << QVariant::fromValue(size); + QByteArray statesBytes(reinterpret_cast<const char *>(states.data()), + states.size() * static_cast<int>(sizeof(uint))); + command.parameters << statesBytes; processCommand(command); } diff --git a/tests/auto/client/shared/mockcompositor.h b/tests/auto/client/shared/mockcompositor.h index 34c20943..51b6f4bf 100644 --- a/tests/auto/client/shared/mockcompositor.h +++ b/tests/auto/client/shared/mockcompositor.h @@ -175,12 +175,22 @@ private: Q_DECLARE_METATYPE(QSharedPointer<MockIviSurface>) -class MockXdgToplevelV6 +class MockXdgToplevelV6 : public QObject { + Q_OBJECT public: Impl::XdgToplevelV6 *handle() const { return m_toplevel; } void sendConfigure(const QSharedPointer<MockXdgToplevelV6> toplevel); + +signals: + uint setMinimizedRequested(); + uint setMaximizedRequested(); + uint unsetMaximizedRequested(); + uint setFullscreenRequested(); + uint unsetFullscreenRequested(); + void windowGeometryRequested(QRect geometry); // NOTE: This is really an xdg surface event + private: MockXdgToplevelV6(Impl::XdgToplevelV6 *toplevel) : m_toplevel(toplevel) {} friend class Impl::Compositor; @@ -234,7 +244,8 @@ public: void sendSurfaceLeave(const QSharedPointer<MockSurface> &surface, QSharedPointer<MockOutput> &output); void sendShellSurfaceConfigure(const QSharedPointer<MockSurface> surface, const QSize &size = QSize(0, 0)); void sendIviSurfaceConfigure(const QSharedPointer<MockIviSurface> iviSurface, const QSize &size); - void sendXdgToplevelV6Configure(const QSharedPointer<MockXdgToplevelV6> toplevel, const QSize &size = QSize(0, 0)); + void sendXdgToplevelV6Configure(const QSharedPointer<MockXdgToplevelV6> toplevel, const QSize &size = QSize(0, 0), + const QVector<uint> &states = { ZXDG_TOPLEVEL_V6_STATE_ACTIVATED }); void waitForStartDrag(); QSharedPointer<MockSurface> surface(); diff --git a/tests/auto/client/shared/mocksurface.cpp b/tests/auto/client/shared/mocksurface.cpp index 81a865f1..82ce37ac 100644 --- a/tests/auto/client/shared/mocksurface.cpp +++ b/tests/auto/client/shared/mocksurface.cpp @@ -70,7 +70,10 @@ void Compositor::sendShellSurfaceConfigure(void *data, const QList<QVariant> &pa QSize size = parameters.at(1).toSize(); Q_ASSERT(size.isValid()); if (auto toplevel = surface->xdgToplevelV6()) { - toplevel->send_configure(size.width(), size.height(), QByteArray()); + QVector<uint> states = { ZXDG_TOPLEVEL_V6_STATE_ACTIVATED }; + auto statesBytes = QByteArray::fromRawData(reinterpret_cast<const char *>(states.data()), + states.size() * static_cast<int>(sizeof(uint))); + toplevel->send_configure(size.width(), size.height(), statesBytes); toplevel->xdgSurface()->send_configure(compositor->nextSerial()); } else if (auto wlShellSurface = surface->wlShellSurface()) { const uint edges = 0; diff --git a/tests/auto/client/shared/mockxdgshellv6.cpp b/tests/auto/client/shared/mockxdgshellv6.cpp index 39e03296..014a24be 100644 --- a/tests/auto/client/shared/mockxdgshellv6.cpp +++ b/tests/auto/client/shared/mockxdgshellv6.cpp @@ -39,8 +39,8 @@ void Compositor::sendXdgToplevelV6Configure(void *data, const QList<QVariant> &p Q_ASSERT(toplevel && toplevel->resource()); QSize size = parameters.at(1).toSize(); Q_ASSERT(size.isValid()); - QByteArray states; - toplevel->send_configure(size.width(), size.height(), states); + auto statesBytes = parameters.at(2).toByteArray(); + toplevel->send_configure(size.width(), size.height(), statesBytes); toplevel->xdgSurface()->send_configure(compositor->nextSerial()); } @@ -57,6 +57,14 @@ void XdgSurfaceV6::zxdg_surface_v6_get_toplevel(QtWaylandServer::zxdg_surface_v6 m_toplevel = new XdgToplevelV6(this, resource->client(), id, version); } +void XdgSurfaceV6::zxdg_surface_v6_set_window_geometry(QtWaylandServer::zxdg_surface_v6::Resource *resource, int32_t x, int32_t y, int32_t width, int32_t height) +{ + if (m_toplevel) { + QRect geometry(x, y, width, height); + emit m_toplevel->mockToplevel()->windowGeometryRequested(geometry); + } +} + void XdgSurfaceV6::zxdg_surface_v6_destroy(QtWaylandServer::zxdg_surface_v6::Resource *resource) { Q_ASSERT(!m_toplevel); @@ -87,6 +95,37 @@ void XdgToplevelV6::zxdg_toplevel_v6_destroy(QtWaylandServer::zxdg_toplevel_v6:: wl_resource_destroy(resource->handle); } +void XdgToplevelV6::zxdg_toplevel_v6_set_minimized(QtWaylandServer::zxdg_toplevel_v6::Resource *resource) +{ + Q_UNUSED(resource); + emit m_mockToplevel->setMinimizedRequested(); +} + +void XdgToplevelV6::zxdg_toplevel_v6_set_maximized(QtWaylandServer::zxdg_toplevel_v6::Resource *resource) +{ + Q_UNUSED(resource); + emit m_mockToplevel->setMaximizedRequested(); +} + +void XdgToplevelV6::zxdg_toplevel_v6_unset_maximized(QtWaylandServer::zxdg_toplevel_v6::Resource *resource) +{ + Q_UNUSED(resource); + emit m_mockToplevel->unsetMaximizedRequested(); +} + +void XdgToplevelV6::zxdg_toplevel_v6_set_fullscreen(QtWaylandServer::zxdg_toplevel_v6::Resource *resource, wl_resource *output) +{ + Q_UNUSED(resource); + Q_UNUSED(output); + emit m_mockToplevel->setFullscreenRequested(); +} + +void XdgToplevelV6::zxdg_toplevel_v6_unset_fullscreen(QtWaylandServer::zxdg_toplevel_v6::Resource *resource) +{ + Q_UNUSED(resource); + emit m_mockToplevel->unsetFullscreenRequested(); +} + void Impl::XdgShellV6::zxdg_shell_v6_get_xdg_surface(QtWaylandServer::zxdg_shell_v6::Resource *resource, uint32_t id, wl_resource *surface) { new XdgSurfaceV6(this, Surface::fromResource(surface), resource->client(), id); diff --git a/tests/auto/client/shared/mockxdgshellv6.h b/tests/auto/client/shared/mockxdgshellv6.h index 92b808ba..bd5e1306 100644 --- a/tests/auto/client/shared/mockxdgshellv6.h +++ b/tests/auto/client/shared/mockxdgshellv6.h @@ -52,6 +52,7 @@ public: protected: void zxdg_surface_v6_destroy_resource(Resource *) override { delete this; } void zxdg_surface_v6_get_toplevel(Resource *resource, uint32_t id) override; + void zxdg_surface_v6_set_window_geometry(Resource *resource, int32_t x, int32_t y, int32_t width, int32_t height) override; void zxdg_surface_v6_destroy(Resource *resource) override; private: @@ -74,6 +75,11 @@ public: protected: void zxdg_toplevel_v6_destroy_resource(Resource *) override { delete this; } void zxdg_toplevel_v6_destroy(Resource *resource) override; + void zxdg_toplevel_v6_set_minimized(Resource *resource) override; + void zxdg_toplevel_v6_set_maximized(Resource *resource) override; + void zxdg_toplevel_v6_unset_maximized(Resource *resource) override; + void zxdg_toplevel_v6_set_fullscreen(Resource *resource, struct ::wl_resource *output) override; + void zxdg_toplevel_v6_unset_fullscreen(Resource *resource) override; private: XdgSurfaceV6 *m_xdgSurface = nullptr; diff --git a/tests/auto/client/xdgshellv6/tst_xdgshellv6.cpp b/tests/auto/client/xdgshellv6/tst_xdgshellv6.cpp index 364cd109..91679cb5 100644 --- a/tests/auto/client/xdgshellv6/tst_xdgshellv6.cpp +++ b/tests/auto/client/xdgshellv6/tst_xdgshellv6.cpp @@ -42,6 +42,7 @@ static const QSize screenSize(1600, 1200); class TestWindow : public QWindow { + Q_OBJECT public: TestWindow() { @@ -49,6 +50,16 @@ public: setGeometry(0, 0, 32, 32); create(); } + + bool event(QEvent *event) override + { + if (event->type() == QEvent::WindowStateChange) + emit windowStateChangeEventReceived(static_cast<QWindowStateChangeEvent *>(event)->oldState()); + return QWindow::event(event); + } + +signals: + void windowStateChangeEventReceived(uint oldState); }; class tst_WaylandClientXdgShellV6 : public QObject @@ -58,6 +69,7 @@ public: tst_WaylandClientXdgShellV6(MockCompositor *c) : m_compositor(c) { + qRegisterMetaType<Qt::WindowState>(); QSocketNotifier *notifier = new QSocketNotifier(m_compositor->waylandFileDescriptor(), QSocketNotifier::Read, this); connect(notifier, &QSocketNotifier::activated, this, &tst_WaylandClientXdgShellV6::processWaylandEvents); // connect to the event dispatcher to make sure to flush out the outgoing message queue @@ -82,6 +94,13 @@ public slots: private slots: void createDestroyWindow(); void configure(); + void showMinimized(); + void setMinimized(); + void unsetMaximized(); + void focusWindowFollowsConfigure(); + void windowStateChangedEvents(); + void windowGeometrySimple(); + void windowGeometryFixed(); private: MockCompositor *m_compositor = nullptr; @@ -118,10 +137,227 @@ void tst_WaylandClientXdgShellV6::configure() QSharedPointer<MockXdgToplevelV6> toplevel; QTRY_VERIFY(toplevel = m_compositor->xdgToplevelV6()); + const QSize newSize(123, 456); m_compositor->sendXdgToplevelV6Configure(toplevel, newSize); QTRY_VERIFY(window.isExposed()); + QTRY_COMPARE(window.visibility(), QWindow::Windowed); + QTRY_COMPARE(window.windowStates(), Qt::WindowNoState); QTRY_COMPARE(window.frameGeometry(), QRect(QPoint(), newSize)); + + m_compositor->sendXdgToplevelV6Configure(toplevel, screenSize, { ZXDG_TOPLEVEL_V6_STATE_ACTIVATED, ZXDG_TOPLEVEL_V6_STATE_MAXIMIZED }); + QTRY_COMPARE(window.visibility(), QWindow::Maximized); + QTRY_COMPARE(window.windowStates(), Qt::WindowMaximized); + QTRY_COMPARE(window.frameGeometry(), QRect(QPoint(), screenSize)); + + m_compositor->sendXdgToplevelV6Configure(toplevel, screenSize, { ZXDG_TOPLEVEL_V6_STATE_ACTIVATED, ZXDG_TOPLEVEL_V6_STATE_FULLSCREEN }); + QTRY_COMPARE(window.visibility(), QWindow::FullScreen); + QTRY_COMPARE(window.windowStates(), Qt::WindowFullScreen); + QTRY_COMPARE(window.frameGeometry(), QRect(QPoint(), screenSize)); + + //The window should remember it's original size + m_compositor->sendXdgToplevelV6Configure(toplevel, QSize(0, 0), { ZXDG_TOPLEVEL_V6_STATE_ACTIVATED }); + QTRY_COMPARE(window.visibility(), QWindow::Windowed); + QTRY_COMPARE(window.windowStates(), Qt::WindowNoState); + QTRY_COMPARE(window.frameGeometry(), QRect(QPoint(), newSize)); +} + +void tst_WaylandClientXdgShellV6::showMinimized() +{ + // On xdg-shell v6 there's really no way for the compositor to tell the window if it's minimized + // There are wl_surface.enter events and so on, but there's really no way to differentiate + // between a window preview and an unminimized window. + TestWindow window; + window.showMinimized(); + QCOMPARE(window.windowStates(), Qt::WindowMinimized); // should return minimized until + QTRY_COMPARE(window.windowStates(), Qt::WindowNoState); // rejected by handleWindowStateChanged + + // Make sure the window on the compositor side is/was created here, and not after the test + // finishes, as that may mess up for later tests. + QTRY_VERIFY(m_compositor->xdgToplevelV6()); +} + +void tst_WaylandClientXdgShellV6::setMinimized() +{ + TestWindow window; + window.show(); + + QSharedPointer<MockXdgToplevelV6> toplevel; + QTRY_VERIFY(toplevel = m_compositor->xdgToplevelV6()); + + m_compositor->sendXdgToplevelV6Configure(toplevel); + QTRY_COMPARE(window.visibility(), QWindow::Windowed); + QTRY_COMPARE(window.windowStates(), Qt::WindowNoState); + + QSignalSpy setMinimizedSpy(toplevel.data(), SIGNAL(setMinimizedRequested())); + QSignalSpy windowStateChangeSpy(&window, SIGNAL(windowStateChangeEventReceived(uint))); + + window.setVisibility(QWindow::Minimized); + QCOMPARE(window.visibility(), QWindow::Minimized); + QCOMPARE(window.windowStates(), Qt::WindowMinimized); + QTRY_COMPARE(setMinimizedSpy.count(), 1); + { + QTRY_VERIFY(windowStateChangeSpy.count() > 0); + Qt::WindowStates oldStates(windowStateChangeSpy.takeFirst().at(0).toUInt()); + QCOMPARE(oldStates, Qt::WindowNoState); + } + + // In the meantime the compositor may minimize, do nothing or reshow the window without + // telling us. + + QTRY_COMPARE(window.visibility(), QWindow::Windowed); // verify that we don't know anything + QTRY_COMPARE(window.windowStates(), Qt::WindowNoState); + { + QTRY_COMPARE(windowStateChangeSpy.count(), 1); + Qt::WindowStates oldStates(windowStateChangeSpy.takeFirst().at(0).toUInt()); + QCOMPARE(oldStates, Qt::WindowNoState); // because the window never was minimized + } + + // Setting visibility again should send another set_minimized request + window.setVisibility(QWindow::Minimized); + QTRY_COMPARE(setMinimizedSpy.count(), 2); +} + +void tst_WaylandClientXdgShellV6::unsetMaximized() +{ + TestWindow window; + window.show(); + + QSharedPointer<MockXdgToplevelV6> toplevel; + QTRY_VERIFY(toplevel = m_compositor->xdgToplevelV6()); + + QSignalSpy unsetMaximizedSpy(toplevel.data(), SIGNAL(unsetMaximizedRequested())); + + QSignalSpy windowStateChangedSpy(&window, SIGNAL(windowStateChanged(Qt::WindowState))); + + m_compositor->sendXdgToplevelV6Configure(toplevel, screenSize, { ZXDG_TOPLEVEL_V6_STATE_MAXIMIZED }); + + QTRY_COMPARE(windowStateChangedSpy.count(), 1); + QCOMPARE(windowStateChangedSpy.takeFirst().at(0).toUInt(), Qt::WindowMaximized); + + window.setWindowStates(Qt::WindowNoState); + + QTRY_COMPARE(unsetMaximizedSpy.count(), 1); + QTRY_COMPARE(windowStateChangedSpy.count(), 1); + QCOMPARE(windowStateChangedSpy.takeFirst().at(0).toUInt(), Qt::WindowNoState); + + m_compositor->sendXdgToplevelV6Configure(toplevel, QSize(0, 0), {}); + + QTRY_COMPARE(windowStateChangedSpy.count(), 1); + QCOMPARE(windowStateChangedSpy.takeFirst().at(0).toUInt(), Qt::WindowNoState); +} + +void tst_WaylandClientXdgShellV6::focusWindowFollowsConfigure() +{ + TestWindow window; + window.show(); + + QSharedPointer<MockXdgToplevelV6> toplevel; + QTRY_VERIFY(toplevel = m_compositor->xdgToplevelV6()); + QTRY_VERIFY(!window.isActive()); + + QSignalSpy windowStateChangeSpy(&window, SIGNAL(windowStateChangeEventReceived(uint))); + + m_compositor->sendXdgToplevelV6Configure(toplevel, QSize(0, 0), { ZXDG_TOPLEVEL_V6_STATE_ACTIVATED }); + QTRY_VERIFY(window.isActive()); + + m_compositor->sendXdgToplevelV6Configure(toplevel, QSize(0, 0), {}); + QTRY_VERIFY(!window.isActive()); +} + +void tst_WaylandClientXdgShellV6::windowStateChangedEvents() +{ + TestWindow window; + window.show(); + + QSharedPointer<MockXdgToplevelV6> toplevel; + QTRY_VERIFY(toplevel = m_compositor->xdgToplevelV6()); + + QSignalSpy eventSpy(&window, SIGNAL(windowStateChangeEventReceived(uint))); + QSignalSpy signalSpy(&window, SIGNAL(windowStateChanged(Qt::WindowState))); + + m_compositor->sendXdgToplevelV6Configure(toplevel, screenSize, { ZXDG_TOPLEVEL_V6_STATE_MAXIMIZED }); + + QTRY_COMPARE(window.windowStates(), Qt::WindowMaximized); + QTRY_COMPARE(window.windowState(), Qt::WindowMaximized); + { + QTRY_COMPARE(eventSpy.count(), 1); + Qt::WindowStates oldStates(eventSpy.takeFirst().at(0).toUInt()); + QCOMPARE(oldStates, Qt::WindowNoState); + + QTRY_COMPARE(signalSpy.count(), 1); + uint newState = signalSpy.takeFirst().at(0).toUInt(); + QCOMPARE(newState, Qt::WindowMaximized); + } + + m_compositor->sendXdgToplevelV6Configure(toplevel, screenSize, { ZXDG_TOPLEVEL_V6_STATE_FULLSCREEN }); + + QTRY_COMPARE(window.windowStates(), Qt::WindowFullScreen); + QTRY_COMPARE(window.windowState(), Qt::WindowFullScreen); + { + QTRY_COMPARE(eventSpy.count(), 1); + Qt::WindowStates oldStates(eventSpy.takeFirst().at(0).toUInt()); + QCOMPARE(oldStates, Qt::WindowMaximized); + + QTRY_COMPARE(signalSpy.count(), 1); + uint newState = signalSpy.takeFirst().at(0).toUInt(); + QCOMPARE(newState, Qt::WindowFullScreen); + } + + m_compositor->sendXdgToplevelV6Configure(toplevel, QSize(0, 0), {}); + + QTRY_COMPARE(window.windowStates(), Qt::WindowNoState); + QTRY_COMPARE(window.windowState(), Qt::WindowNoState); + { + QTRY_COMPARE(eventSpy.count(), 1); + Qt::WindowStates oldStates(eventSpy.takeFirst().at(0).toUInt()); + QCOMPARE(oldStates, Qt::WindowFullScreen); + + QTRY_COMPARE(signalSpy.count(), 1); + uint newState = signalSpy.takeFirst().at(0).toUInt(); + QCOMPARE(newState, Qt::WindowNoState); + } +} + +void tst_WaylandClientXdgShellV6::windowGeometrySimple() +{ + QWindow window; + window.show(); + + QSharedPointer<MockXdgToplevelV6> toplevel; + QTRY_VERIFY(toplevel = m_compositor->xdgToplevelV6()); + QSignalSpy geometrySpy(toplevel.data(), SIGNAL(windowGeometryRequested(QRect))); + + m_compositor->sendXdgToplevelV6Configure(toplevel); + QTRY_COMPARE(geometrySpy.count(), 1); + QCOMPARE(geometrySpy.takeFirst().at(0).toRect().size(), window.frameGeometry().size()); + + m_compositor->sendXdgToplevelV6Configure(toplevel, QSize(123, 456)); + QTRY_COMPARE(geometrySpy.count(), 1); + QCOMPARE(geometrySpy.takeFirst().at(0).toRect().size(), QSize(123, 456)); +} + +void tst_WaylandClientXdgShellV6::windowGeometryFixed() +{ + QWindow window; + window.resize(QSize(1337, 137)); + window.setMaximumSize(window.size()); + window.setMinimumSize(window.size()); + window.show(); + + QSharedPointer<MockXdgToplevelV6> toplevel; + QTRY_VERIFY(toplevel = m_compositor->xdgToplevelV6()); + QSignalSpy geometrySpy(toplevel.data(), SIGNAL(windowGeometryRequested(QRect))); + + m_compositor->sendXdgToplevelV6Configure(toplevel); + QTRY_COMPARE(geometrySpy.count(), 1); + QRect initialWindowGeometry = geometrySpy.takeFirst().at(0).toRect(); + QCOMPARE(initialWindowGeometry.size(), window.frameGeometry().size()); + + m_compositor->sendXdgToplevelV6Configure(toplevel, QSize(123, 456)); + QTRY_COMPARE(geometrySpy.count(), 1); + // Configuring the window should not change the window geometry + QCOMPARE(geometrySpy.takeFirst().at(0).toRect().size(), initialWindowGeometry.size()); } int main(int argc, char **argv) diff --git a/tests/auto/compositor/compositor/compositor.pro b/tests/auto/compositor/compositor/compositor.pro index 2919fa4b..d69db4ca 100644 --- a/tests/auto/compositor/compositor/compositor.pro +++ b/tests/auto/compositor/compositor/compositor.pro @@ -11,7 +11,7 @@ qtConfig(xkbcommon-evdev): \ QMAKE_USE += xkbcommon_evdev WAYLANDCLIENTSOURCES += \ - ../../../../src/3rdparty/protocol/xdg-shell.xml \ + ../../../../src/3rdparty/protocol/xdg-shell-unstable-v5.xml \ ../../../../src/3rdparty/protocol/ivi-application.xml \ SOURCES += \ @@ -21,6 +21,7 @@ SOURCES += \ mockclient.cpp \ mockseat.cpp \ testseat.cpp \ + mockkeyboard.cpp \ mockpointer.cpp HEADERS += \ @@ -29,4 +30,5 @@ HEADERS += \ mockclient.h \ mockseat.h \ testseat.h \ + mockkeyboard.h \ mockpointer.h diff --git a/tests/auto/compositor/compositor/mockclient.h b/tests/auto/compositor/compositor/mockclient.h index 6d5bf31d..6bfb652e 100644 --- a/tests/auto/compositor/compositor/mockclient.h +++ b/tests/auto/compositor/compositor/mockclient.h @@ -27,7 +27,7 @@ ****************************************************************************/ #include <wayland-client.h> -#include <wayland-xdg-shell-client-protocol.h> +#include <qwayland-xdg-shell-unstable-v5.h> #include <wayland-ivi-application-client-protocol.h> #include <QObject> diff --git a/tests/auto/compositor/compositor/mockkeyboard.cpp b/tests/auto/compositor/compositor/mockkeyboard.cpp new file mode 100644 index 00000000..e5f5f8d3 --- /dev/null +++ b/tests/auto/compositor/compositor/mockkeyboard.cpp @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "mockkeyboard.h" + +void keyboardKeymap(void *keyboard, struct wl_keyboard *wl_keyboard, uint32_t format, int32_t fd, uint32_t size) +{ + Q_UNUSED(keyboard); + Q_UNUSED(wl_keyboard); + Q_UNUSED(format); + Q_UNUSED(fd); + Q_UNUSED(size); +} + +void keyboardEnter(void *keyboard, struct wl_keyboard *wl_keyboard, uint32_t serial, struct wl_surface *surface, struct wl_array *keys) +{ + Q_UNUSED(wl_keyboard); + Q_UNUSED(serial); + Q_UNUSED(surface); + Q_UNUSED(keys); + + static_cast<MockKeyboard *>(keyboard)->m_enteredSurface = surface; +} + +void keyboardLeave(void *keyboard, struct wl_keyboard *wl_keyboard, uint32_t serial, struct wl_surface *surface) +{ + Q_UNUSED(wl_keyboard); + Q_UNUSED(serial); + Q_UNUSED(surface); + + static_cast<MockKeyboard *>(keyboard)->m_enteredSurface = nullptr; +} + +void keyboardKey(void *keyboard, struct wl_keyboard *wl_keyboard, uint32_t serial, uint32_t time, uint32_t key, uint32_t state) +{ + Q_UNUSED(keyboard); + Q_UNUSED(wl_keyboard); + Q_UNUSED(serial); + Q_UNUSED(time); + Q_UNUSED(key); + Q_UNUSED(state); + auto kb = static_cast<MockKeyboard *>(keyboard); + kb->m_lastKeyCode = key; + kb->m_lastKeyState = state; +} + +void keyboardModifiers(void *keyboard, struct wl_keyboard *wl_keyboard, uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked, uint32_t group) +{ + Q_UNUSED(keyboard); + Q_UNUSED(wl_keyboard); + Q_UNUSED(serial); + Q_UNUSED(mods_depressed); + Q_UNUSED(mods_latched); + Q_UNUSED(mods_locked); + auto kb = static_cast<MockKeyboard *>(keyboard); + kb->m_group = group; +} + +static const struct wl_keyboard_listener keyboardListener = { + keyboardKeymap, + keyboardEnter, + keyboardLeave, + keyboardKey, + keyboardModifiers +}; + +MockKeyboard::MockKeyboard(wl_seat *seat) + : m_keyboard(wl_seat_get_keyboard(seat)) +{ + wl_keyboard_add_listener(m_keyboard, &keyboardListener, this); +} + +MockKeyboard::~MockKeyboard() +{ + wl_keyboard_destroy(m_keyboard); +} diff --git a/tests/auto/compositor/compositor/mockkeyboard.h b/tests/auto/compositor/compositor/mockkeyboard.h new file mode 100644 index 00000000..1090db59 --- /dev/null +++ b/tests/auto/compositor/compositor/mockkeyboard.h @@ -0,0 +1,50 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef MOCKKEYBOARD_H +#define MOCKKEYBOARD_H + +#include <QObject> +#include <wayland-client.h> + +class MockKeyboard : public QObject +{ + Q_OBJECT + +public: + explicit MockKeyboard(wl_seat *seat); + ~MockKeyboard() override; + + wl_keyboard *m_keyboard = nullptr; + wl_surface *m_enteredSurface = nullptr; + uint m_lastKeyCode = 0; + uint m_lastKeyState = 0; + uint m_group = 0; +}; + +#endif // MOCKKEYBOARD_H diff --git a/tests/auto/compositor/compositor/mockseat.cpp b/tests/auto/compositor/compositor/mockseat.cpp index 052c2f90..ce873c12 100644 --- a/tests/auto/compositor/compositor/mockseat.cpp +++ b/tests/auto/compositor/compositor/mockseat.cpp @@ -31,14 +31,11 @@ MockSeat::MockSeat(wl_seat *seat) : m_seat(seat) , m_pointer(new MockPointer(seat)) + , m_keyboard(new MockKeyboard(seat)) { - // Bind to the keyboard interface so that the compositor has - // the right resource associations - m_keyboard = wl_seat_get_keyboard(seat); } MockSeat::~MockSeat() { - wl_keyboard_destroy(m_keyboard); wl_seat_destroy(m_seat); } diff --git a/tests/auto/compositor/compositor/mockseat.h b/tests/auto/compositor/compositor/mockseat.h index 73d28928..f8c103ed 100644 --- a/tests/auto/compositor/compositor/mockseat.h +++ b/tests/auto/compositor/compositor/mockseat.h @@ -29,6 +29,7 @@ #define MOCKSEAT #include "mockpointer.h" +#include "mockkeyboard.h" #include <QObject> #include <wayland-client.h> @@ -41,12 +42,13 @@ public: MockSeat(wl_seat *seat); ~MockSeat() override; MockPointer *pointer() const { return m_pointer.data(); } + MockKeyboard *keyboard() const { return m_keyboard.data(); } wl_seat *m_seat = nullptr; - wl_keyboard *m_keyboard = nullptr; private: QScopedPointer<MockPointer> m_pointer; + QScopedPointer<MockKeyboard> m_keyboard; }; #endif diff --git a/tests/auto/compositor/compositor/tst_compositor.cpp b/tests/auto/compositor/compositor/tst_compositor.cpp index ae5ea927..e3c4dacc 100644 --- a/tests/auto/compositor/compositor/tst_compositor.cpp +++ b/tests/auto/compositor/compositor/tst_compositor.cpp @@ -40,11 +40,13 @@ #include <QtGui/QScreen> #include <QtWaylandCompositor/QWaylandXdgShellV5> #include <QtWaylandCompositor/private/qwaylandxdgshellv6_p.h> +#include <QtWaylandCompositor/private/qwaylandkeyboard_p.h> #include <QtWaylandCompositor/QWaylandIviApplication> #include <QtWaylandCompositor/QWaylandIviSurface> #include <QtWaylandCompositor/QWaylandSurface> #include <QtWaylandCompositor/QWaylandResource> -#include <qwayland-xdg-shell.h> +#include <QtWaylandCompositor/QWaylandKeymap> +#include <qwayland-xdg-shell-unstable-v5.h> #include <qwayland-ivi-application.h> #include <QtTest/QtTest> @@ -56,6 +58,11 @@ class tst_WaylandCompositor : public QObject private slots: void init(); void seatCapabilities(); +#if QT_CONFIG(xkbcommon_evdev) + void simpleKeyboard(); + void keyboardKeymaps(); + void keyboardLayoutSwitching(); +#endif void keyboardGrab(); void seatCreation(); void seatKeyboardFocus(); @@ -160,6 +167,121 @@ void tst_WaylandCompositor::multipleClients() QTRY_COMPARE(compositor.surfaces.size(), 0); } +#if QT_CONFIG(xkbcommon_evdev) + +void tst_WaylandCompositor::simpleKeyboard() +{ + TestCompositor compositor; + compositor.create(); + + QWaylandSeat* seat = compositor.defaultSeat(); + seat->keymap()->setLayout("us"); + + MockClient client; + + QTRY_COMPARE(client.m_seats.size(), 1); + MockKeyboard *mockKeyboard = client.m_seats.at(0)->keyboard(); + + wl_surface *mockSurface = client.createSurface(); + QTRY_COMPARE(compositor.surfaces.size(), 1); + seat->setKeyboardFocus(compositor.surfaces.at(0)); + + compositor.flushClients(); + QTRY_COMPARE(mockKeyboard->m_enteredSurface, mockSurface); + + seat->sendKeyEvent(Qt::Key_A, true); + compositor.flushClients(); + QTRY_COMPARE(mockKeyboard->m_lastKeyState, 1u); + QTRY_COMPARE(mockKeyboard->m_lastKeyCode, 30u); // 30 is the scan code for A on us keyboard layouts + + seat->sendKeyEvent(Qt::Key_A, false); + compositor.flushClients(); + QTRY_COMPARE(mockKeyboard->m_lastKeyState, 0u); + QTRY_COMPARE(mockKeyboard->m_lastKeyCode, 30u); + + seat->sendKeyEvent(Qt::Key_Super_L, true); + seat->sendKeyEvent(Qt::Key_Super_L, false); + compositor.flushClients(); + QTRY_COMPARE(mockKeyboard->m_lastKeyCode, 125u); +} + +void tst_WaylandCompositor::keyboardKeymaps() +{ + TestCompositor compositor; + compositor.create(); + QWaylandSeat* seat = compositor.defaultSeat(); + MockClient client; + QTRY_COMPARE(client.m_seats.size(), 1); + MockKeyboard *mockKeyboard = client.m_seats.at(0)->keyboard(); + client.createSurface(); + QTRY_COMPARE(compositor.surfaces.size(), 1); + seat->setKeyboardFocus(compositor.surfaces.at(0)); + + seat->keymap()->setLayout("us"); + + seat->sendKeyEvent(Qt::Key_Y, true); + seat->sendKeyEvent(Qt::Key_Y, false); + compositor.flushClients(); + QTRY_COMPARE(mockKeyboard->m_lastKeyCode, 21u); + + seat->sendKeyEvent(Qt::Key_Z, true); + seat->sendKeyEvent(Qt::Key_Z, false); + compositor.flushClients(); + QTRY_COMPARE(mockKeyboard->m_lastKeyCode, 44u); + + seat->keymap()->setLayout("de"); // In the German layout y and z have changed places + + seat->sendKeyEvent(Qt::Key_Y, true); + seat->sendKeyEvent(Qt::Key_Y, false); + compositor.flushClients(); + QTRY_COMPARE(mockKeyboard->m_lastKeyCode, 44u); + + seat->sendKeyEvent(Qt::Key_Z, true); + seat->sendKeyEvent(Qt::Key_Z, false); + compositor.flushClients(); + QTRY_COMPARE(mockKeyboard->m_lastKeyCode, 21u); +} + +void tst_WaylandCompositor::keyboardLayoutSwitching() +{ + TestCompositor compositor; + compositor.create(); + QWaylandSeat* seat = compositor.defaultSeat(); + MockClient client; + QTRY_COMPARE(client.m_seats.size(), 1); + MockKeyboard *mockKeyboard = client.m_seats.at(0)->keyboard(); + client.createSurface(); + QTRY_COMPARE(compositor.surfaces.size(), 1); + seat->setKeyboardFocus(compositor.surfaces.at(0)); + + seat->keymap()->setLayout("us,de"); + seat->keymap()->setOptions("grp:lalt_toggle"); //toggle keyboard layout with left alt + + compositor.flushClients(); + QTRY_COMPARE(mockKeyboard->m_group, 0u); + + seat->sendKeyEvent(Qt::Key_Y, true); + seat->sendKeyEvent(Qt::Key_Y, false); + compositor.flushClients(); + QTRY_COMPARE(mockKeyboard->m_lastKeyCode, 21u); + + // It's not currently possible to switch layouts programmatically with the public APIs + // We will just fake it with the private APIs here. + auto keyboardPrivate = QWaylandKeyboardPrivate::get(seat->keyboard()); + const uint leftAltCode = 64; + keyboardPrivate->updateModifierState(leftAltCode, WL_KEYBOARD_KEY_STATE_PRESSED); + keyboardPrivate->updateModifierState(leftAltCode, WL_KEYBOARD_KEY_STATE_RELEASED); + compositor.flushClients(); + QTRY_COMPARE(mockKeyboard->m_group, 1u); + + seat->sendKeyEvent(Qt::Key_Y, true); + seat->sendKeyEvent(Qt::Key_Y, false); + compositor.flushClients(); + QTRY_COMPARE(mockKeyboard->m_lastKeyCode, 44u); +} + +#endif // QT_CONFIG(xkbcommon_evdev) + void tst_WaylandCompositor::keyboardGrab() { TestCompositor compositor; @@ -450,13 +572,24 @@ void tst_WaylandCompositor::seatKeyboardFocus() // Create client after all the input devices have been set up as the mock client // does not dynamically listen to new seats MockClient client; + + QTRY_COMPARE(client.m_seats.size(), 1); + MockKeyboard *mockKeyboard = client.m_seats.first()->keyboard(); + QVERIFY(mockKeyboard); + QCOMPARE(mockKeyboard->m_enteredSurface, nullptr); + wl_surface *surface = client.createSurface(); QTRY_COMPARE(compositor.surfaces.size(), 1); QWaylandSurface *waylandSurface = compositor.surfaces.at(0); - QWaylandSeat* dev = compositor.defaultSeat(); - dev->setKeyboardFocus(waylandSurface); - QTRY_COMPARE(compositor.defaultSeat()->keyboardFocus(), waylandSurface); + QWaylandSeat* seat = compositor.defaultSeat(); + QVERIFY(seat->setKeyboardFocus(waylandSurface)); + QCOMPARE(compositor.defaultSeat()->keyboardFocus(), waylandSurface); + + compositor.flushClients(); + + qDebug() << mockKeyboard->m_enteredSurface; + QTRY_COMPARE(mockKeyboard->m_enteredSurface, surface); wl_surface_destroy(surface); QTRY_VERIFY(compositor.surfaces.size() == 0); |