diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 39566e1671..bd6ba3ecff 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -8,3 +8,24 @@ endif() set(screenedgeshowtest_SRCS screenedgeshowtest.cpp) add_executable(screenedgeshowtest ${screenedgeshowtest_SRCS}) target_link_libraries(screenedgeshowtest Qt5::Widgets Qt5::X11Extras KF5::WindowSystem ${XCB_XCB_LIBRARY}) + +if (Wayland_Client_FOUND AND XKB_FOUND) + add_definitions(-DSOURCE_DIR="${KWIN_SOURCE_DIR}") + set(waylandclienttest_SRCS + waylandclienttest.cpp + ../wayland_client/buffer.cpp + ../wayland_client/compositor.cpp + ../wayland_client/connection_thread.cpp + ../wayland_client/keyboard.cpp + ../wayland_client/output.cpp + ../wayland_client/pointer.cpp + ../wayland_client/registry.cpp + ../wayland_client/seat.cpp + ../wayland_client/shell.cpp + ../wayland_client/shm_pool.cpp + ../wayland_client/surface.cpp + ${CMAKE_BINARY_DIR}/wayland_protocols/wayland-client-fullscreen-shell.c + ) + add_executable(waylandclienttest ${waylandclienttest_SRCS}) + target_link_libraries(waylandclienttest Qt5::Core Qt5::Gui Wayland::Client) +endif() diff --git a/tests/waylandclienttest.cpp b/tests/waylandclienttest.cpp new file mode 100644 index 0000000000..d2d11d01d9 --- /dev/null +++ b/tests/waylandclienttest.cpp @@ -0,0 +1,238 @@ +/* + * Copyright 2014 Martin Gräßlin + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License or (at your option) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . +*/ +#include "waylandclienttest.h" +// KWin::Wayland +#include "../wayland_client/buffer.h" +#include "../wayland_client/compositor.h" +#include "../wayland_client/connection_thread.h" +#include "../wayland_client/keyboard.h" +#include "../wayland_client/output.h" +#include "../wayland_client/pointer.h" +#include "../wayland_client/registry.h" +#include "../wayland_client/seat.h" +#include "../wayland_client/shell.h" +#include "../wayland_client/shm_pool.h" +#include "../wayland_client/surface.h" +// Qt +#include +#include +#include +#include +#include +#include +// Wayland +#include + +#include + +using namespace KWin::Wayland; + +static Qt::GlobalColor s_colors[] = { + Qt::white, + Qt::red, + Qt::green, + Qt::blue, + Qt::black +}; +static int s_colorIndex = 0; + +WaylandClientTest::WaylandClientTest(QObject *parent) + : QObject(parent) + , m_connectionThread(new QThread(this)) + , m_connectionThreadObject(new ConnectionThread(nullptr)) + , m_eventQueue(nullptr) + , m_compositor(new Compositor(this)) + , m_output(new Output(this)) + , m_surface(nullptr) + , m_shm(new ShmPool(this)) + , m_timer(new QTimer(this)) +{ + init(); +} + +WaylandClientTest::~WaylandClientTest() +{ + m_connectionThread->quit(); + m_connectionThread->wait(); + m_connectionThreadObject->deleteLater(); +} + +void WaylandClientTest::init() +{ + connect(m_connectionThreadObject, &ConnectionThread::connected, this, + [this]() { + // create the event queue for the main gui thread + wl_display *display = m_connectionThreadObject->display(); + m_eventQueue = wl_display_create_queue(display); + // setup registry + Registry *registry = new Registry(this); + setupRegistry(registry); + + QAbstractEventDispatcher *dispatcher = QCoreApplication::instance()->eventDispatcher(); + connect(dispatcher, &QAbstractEventDispatcher::aboutToBlock, this, + [this]() { + wl_display_flush(m_connectionThreadObject->display()); + } + ); + }, + Qt::QueuedConnection); + connect(m_connectionThreadObject, &ConnectionThread::eventsRead, this, + [this]() { + if (!m_eventQueue) { + return; + } + wl_display_dispatch_queue_pending(m_connectionThreadObject->display(), m_eventQueue); + wl_display_flush(m_connectionThreadObject->display()); + }, + Qt::QueuedConnection); + + m_connectionThreadObject->moveToThread(m_connectionThread); + m_connectionThread->start(); + + m_connectionThreadObject->initConnection(); + + connect(m_timer, &QTimer::timeout, this, + [this]() { + s_colorIndex = (s_colorIndex + 1) % 5; + render(); + } + ); + m_timer->setInterval(1000); + m_timer->start(); +} + +void WaylandClientTest::setupRegistry(Registry *registry) +{ + connect(registry, &Registry::compositorAnnounced, this, + [this, registry](quint32 name) { + m_compositor->setup(registry->bindCompositor(name, 1)); + m_surface = m_compositor->createSurface(this); + } + ); + connect(registry, &Registry::shellAnnounced, this, + [this, registry](quint32 name) { + Shell *shell = new Shell(this); + shell->setup(registry->bindShell(name, 1)); + ShellSurface *shellSurface = shell->createSurface(m_surface, m_surface); + shellSurface->setFullscreen(m_output); + connect(shellSurface, &ShellSurface::sizeChanged, this, static_cast(&WaylandClientTest::render)); + } + ); + connect(registry, &Registry::outputAnnounced, this, + [this, registry](quint32 name) { + if (m_output->isValid()) { + return; + } + m_output->setup(registry->bindOutput(name, 2)); + } + ); + connect(registry, &Registry::shmAnnounced, this, + [this, registry](quint32 name) { + m_shm->setup(registry->bindShm(name, 1)); + } + ); + connect(registry, &Registry::seatAnnounced, this, + [this, registry](quint32 name) { + Seat *s = new Seat(this); + connect(s, &Seat::hasKeyboardChanged, this, + [this, s](bool has) { + if (!has) { + return; + } + Keyboard *k = s->createKeyboard(this); + connect(k, &Keyboard::keyChanged, this, + [this](quint32 key, Keyboard::KeyState state) { + if (key == KEY_Q && state == Keyboard::KeyState::Released) { + QCoreApplication::instance()->quit(); + } + } + ); + } + ); + connect(s, &Seat::hasPointerChanged, this, + [this, s](bool has) { + if (!has) { + return; + } + Pointer *p = s->createPointer(this); + connect(p, &Pointer::buttonStateChanged, this, + [this](quint32 serial, quint32 time, quint32 button, Pointer::ButtonState state) { + Q_UNUSED(serial) + Q_UNUSED(time) + if (state == Pointer::ButtonState::Released) { + if (button == BTN_LEFT) { + if (m_timer->isActive()) { + m_timer->stop(); + } else { + m_timer->start(); + } + } + if (button == BTN_RIGHT) { + QCoreApplication::instance()->quit(); + } + } + } + ); + } + ); + s->setup(registry->bindSeat(name, 2)); + } + ); + registry->create(m_connectionThreadObject->display()); + wl_proxy_set_queue((wl_proxy*)registry->registry(), m_eventQueue); + registry->setup(); +} + +void WaylandClientTest::render(const QSize &size) +{ + m_currentSize = size; + render(); +} + +void WaylandClientTest::render() +{ + if (!m_shm || !m_surface || !m_surface->isValid() || !m_currentSize.isValid()) { + return; + } + Buffer *buffer = m_shm->getBuffer(m_currentSize, m_currentSize.width() * 4); + buffer->setUsed(true); + QImage image(buffer->address(), m_currentSize.width(), m_currentSize.height(), QImage::Format_ARGB32_Premultiplied); + image.fill(s_colors[s_colorIndex]); + + QPainter p; + p.begin(&image); + QImage icon(QStringLiteral(SOURCE_DIR) + QStringLiteral("/48-apps-kwin.png")); + p.drawImage(QPoint(0, 0), icon); + p.end(); + + m_surface->attachBuffer(*buffer); + m_surface->damage(QRect(QPoint(0, 0), m_currentSize)); + m_surface->commit(Surface::CommitFlag::None); + buffer->setUsed(false); +} + +int main(int argc, char **argv) +{ + QCoreApplication app(argc, argv); + + new WaylandClientTest(&app); + + return app.exec(); +} diff --git a/tests/waylandclienttest.h b/tests/waylandclienttest.h new file mode 100644 index 0000000000..07f5cccc18 --- /dev/null +++ b/tests/waylandclienttest.h @@ -0,0 +1,67 @@ +/* + * Copyright 2014 Martin Gräßlin + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License or (at your option) version 3 or any later version + * accepted by the membership of KDE e.V. (or its successor approved + * by the membership of KDE e.V.), which shall act as a proxy + * defined in Section 14 of version 3 of the license. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . +*/ +#ifndef WAYLANDCLIENTTEST_H +#define WAYLANDCLIENTTEST_H + +#include +#include + +namespace KWin +{ +namespace Wayland +{ +class Compositor; +class ConnectionThread; +class Output; +class Registry; +class ShmPool; +class Surface; +} +} + +class QThread; +class QTimer; +struct wl_event_queue; + +class WaylandClientTest : public QObject +{ + Q_OBJECT +public: + explicit WaylandClientTest(QObject *parent = nullptr); + virtual ~WaylandClientTest(); + +private: + void init(); + void render(const QSize &size); + void render(); + void setupRegistry(KWin::Wayland::Registry *registry); + void toggleTimer(); + QThread *m_connectionThread; + KWin::Wayland::ConnectionThread *m_connectionThreadObject; + wl_event_queue *m_eventQueue; + KWin::Wayland::Compositor *m_compositor; + KWin::Wayland::Output *m_output; + KWin::Wayland::Surface *m_surface; + KWin::Wayland::ShmPool *m_shm; + QSize m_currentSize; + QTimer *m_timer; +}; + +#endif