File indexing completed on 2024-05-19 16:33:12

0001 /*
0002     SPDX-FileCopyrightText: 2015 Martin Gräßlin <mgraesslin@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0005 */
0006 #include "../src/client/compositor.h"
0007 #include "../src/client/connection_thread.h"
0008 #include "../src/client/event_queue.h"
0009 #include "../src/client/pointer.h"
0010 #include "../src/client/registry.h"
0011 #include "../src/client/seat.h"
0012 #include "../src/client/shadow.h"
0013 #include "../src/client/shell.h"
0014 #include "../src/client/shm_pool.h"
0015 #include "../src/client/surface.h"
0016 #include "../src/client/xdgshell.h"
0017 // Qt
0018 #include <QGuiApplication>
0019 #include <QImage>
0020 #include <QPainter>
0021 #include <QThread>
0022 
0023 using namespace KWayland::Client;
0024 
0025 class XdgTest : public QObject
0026 {
0027     Q_OBJECT
0028 public:
0029     explicit XdgTest(QObject *parent = nullptr);
0030     ~XdgTest() override;
0031 
0032     void init();
0033 
0034 private:
0035     void setupRegistry(Registry *registry);
0036     void createPopup();
0037     void render();
0038     void renderPopup();
0039     QThread *m_connectionThread;
0040     ConnectionThread *m_connectionThreadObject;
0041     EventQueue *m_eventQueue = nullptr;
0042     Compositor *m_compositor = nullptr;
0043     ShmPool *m_shm = nullptr;
0044     Surface *m_surface = nullptr;
0045     XdgShell *m_xdgShell = nullptr;
0046     XdgShellSurface *m_xdgShellSurface = nullptr;
0047     Surface *m_popupSurface = nullptr;
0048     XdgShellPopup *m_xdgShellPopup = nullptr;
0049 };
0050 
0051 XdgTest::XdgTest(QObject *parent)
0052     : QObject(parent)
0053     , m_connectionThread(new QThread(this))
0054     , m_connectionThreadObject(new ConnectionThread())
0055 {
0056 }
0057 
0058 XdgTest::~XdgTest()
0059 {
0060     m_connectionThread->quit();
0061     m_connectionThread->wait();
0062     m_connectionThreadObject->deleteLater();
0063 }
0064 
0065 void XdgTest::init()
0066 {
0067     connect(
0068         m_connectionThreadObject,
0069         &ConnectionThread::connected,
0070         this,
0071         [this] {
0072             m_eventQueue = new EventQueue(this);
0073             m_eventQueue->setup(m_connectionThreadObject);
0074 
0075             Registry *registry = new Registry(this);
0076             setupRegistry(registry);
0077         },
0078         Qt::QueuedConnection);
0079     m_connectionThreadObject->moveToThread(m_connectionThread);
0080     m_connectionThread->start();
0081 
0082     m_connectionThreadObject->initConnection();
0083 }
0084 
0085 void XdgTest::setupRegistry(Registry *registry)
0086 {
0087     connect(registry, &Registry::compositorAnnounced, this, [this, registry](quint32 name, quint32 version) {
0088         m_compositor = registry->createCompositor(name, version, this);
0089     });
0090     connect(registry, &Registry::shmAnnounced, this, [this, registry](quint32 name, quint32 version) {
0091         m_shm = registry->createShmPool(name, version, this);
0092     });
0093     connect(registry, &Registry::xdgShellStableAnnounced, this, [this, registry](quint32 name, quint32 version) {
0094         m_xdgShell = registry->createXdgShell(name, version, this);
0095         m_xdgShell->setEventQueue(m_eventQueue);
0096     });
0097     connect(registry, &Registry::interfacesAnnounced, this, [this] {
0098         Q_ASSERT(m_compositor);
0099         Q_ASSERT(m_xdgShell);
0100         Q_ASSERT(m_shm);
0101         m_surface = m_compositor->createSurface(this);
0102         Q_ASSERT(m_surface);
0103         m_xdgShellSurface = m_xdgShell->createSurface(m_surface, this);
0104         Q_ASSERT(m_xdgShellSurface);
0105         connect(m_xdgShellSurface,
0106                 &XdgShellSurface::configureRequested,
0107                 this,
0108                 [this](const QSize &size, KWayland::Client::XdgShellSurface::States states, int serial) {
0109                     Q_UNUSED(size);
0110                     Q_UNUSED(states);
0111                     m_xdgShellSurface->ackConfigure(serial);
0112                     render();
0113                 });
0114 
0115         m_xdgShellSurface->setTitle(QStringLiteral("Test Window"));
0116 
0117         m_surface->commit();
0118     });
0119     connect(registry, &Registry::seatAnnounced, this, [this, registry](quint32 name) {
0120         Seat *s = registry->createSeat(name, 2, this);
0121         connect(s, &Seat::hasPointerChanged, this, [this, s](bool has) {
0122             if (!has) {
0123                 return;
0124             }
0125             Pointer *p = s->createPointer(this);
0126             connect(p, &Pointer::buttonStateChanged, this, [this](quint32 serial, quint32 time, quint32 button, Pointer::ButtonState state) {
0127                 Q_UNUSED(button)
0128                 Q_UNUSED(serial)
0129                 Q_UNUSED(time)
0130                 if (state == Pointer::ButtonState::Released) {
0131                     if (m_popupSurface) {
0132                         m_popupSurface->deleteLater();
0133                         m_popupSurface = nullptr;
0134                     } else {
0135                         createPopup();
0136                     }
0137                 }
0138             });
0139         });
0140     });
0141 
0142     registry->setEventQueue(m_eventQueue);
0143     registry->create(m_connectionThreadObject);
0144     registry->setup();
0145 }
0146 
0147 void XdgTest::createPopup()
0148 {
0149     if (m_popupSurface) {
0150         m_popupSurface->deleteLater();
0151     }
0152 
0153     m_popupSurface = m_compositor->createSurface(this);
0154 
0155     XdgPositioner positioner(QSize(200, 200), QRect(50, 50, 400, 400));
0156     positioner.setAnchorEdge(Qt::BottomEdge | Qt::RightEdge);
0157     positioner.setGravity(Qt::BottomEdge);
0158     positioner.setConstraints(XdgPositioner::Constraint::FlipX | XdgPositioner::Constraint::SlideY);
0159     m_xdgShellPopup = m_xdgShell->createPopup(m_popupSurface, m_xdgShellSurface, positioner, m_popupSurface);
0160     renderPopup();
0161 }
0162 
0163 void XdgTest::render()
0164 {
0165     const QSize &size = m_xdgShellSurface->size().isValid() ? m_xdgShellSurface->size() : QSize(500, 500);
0166     auto buffer = m_shm->getBuffer(size, size.width() * 4).toStrongRef();
0167     buffer->setUsed(true);
0168     QImage image(buffer->address(), size.width(), size.height(), QImage::Format_ARGB32_Premultiplied);
0169     image.fill(QColor(255, 255, 255, 255));
0170     // draw a red rectangle indicating the anchor of the top level
0171     QPainter painter(&image);
0172     painter.setBrush(Qt::red);
0173     painter.setPen(Qt::black);
0174     painter.drawRect(50, 50, 400, 400);
0175 
0176     m_surface->attachBuffer(*buffer);
0177     m_surface->damage(QRect(QPoint(0, 0), size));
0178     m_surface->commit(Surface::CommitFlag::None);
0179     buffer->setUsed(false);
0180 }
0181 
0182 void XdgTest::renderPopup()
0183 {
0184     QSize size(200, 200);
0185     auto buffer = m_shm->getBuffer(size, size.width() * 4).toStrongRef();
0186     buffer->setUsed(true);
0187     QImage image(buffer->address(), size.width(), size.height(), QImage::Format_ARGB32_Premultiplied);
0188     image.fill(QColor(0, 0, 255, 255));
0189 
0190     m_popupSurface->attachBuffer(*buffer);
0191     m_popupSurface->damage(QRect(QPoint(0, 0), size));
0192     m_popupSurface->commit(Surface::CommitFlag::None);
0193     buffer->setUsed(false);
0194 }
0195 
0196 int main(int argc, char **argv)
0197 {
0198     QCoreApplication app(argc, argv);
0199     XdgTest client;
0200     client.init();
0201 
0202     return app.exec();
0203 }
0204 
0205 #include "xdgtest.moc"