File indexing completed on 2024-12-01 05:02:04

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 "KWayland/Client/compositor.h"
0007 #include "KWayland/Client/connection_thread.h"
0008 #include "KWayland/Client/event_queue.h"
0009 #include "KWayland/Client/pointer.h"
0010 #include "KWayland/Client/registry.h"
0011 #include "KWayland/Client/seat.h"
0012 #include "KWayland/Client/shadow.h"
0013 #include "KWayland/Client/shell.h"
0014 #include "KWayland/Client/shm_pool.h"
0015 #include "KWayland/Client/surface.h"
0016 #include "KWayland/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     virtual ~XdgTest();
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::xdgShellUnstableV6Announced, 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                     m_xdgShellSurface->ackConfigure(serial);
0110                     render();
0111                 });
0112 
0113         m_xdgShellSurface->setTitle(QStringLiteral("Test Window"));
0114 
0115         m_surface->commit();
0116     });
0117     connect(registry, &Registry::seatAnnounced, this, [this, registry](quint32 name) {
0118         Seat *s = registry->createSeat(name, 2, this);
0119         connect(s, &Seat::hasPointerChanged, this, [this, s](bool has) {
0120             if (!has) {
0121                 return;
0122             }
0123             Pointer *p = s->createPointer(this);
0124             connect(p, &Pointer::buttonStateChanged, this, [this](quint32 serial, quint32 time, quint32 button, Pointer::ButtonState state) {
0125                 if (state == Pointer::ButtonState::Released) {
0126                     if (m_popupSurface) {
0127                         m_popupSurface->deleteLater();
0128                         m_popupSurface = nullptr;
0129                     } else {
0130                         createPopup();
0131                     }
0132                 }
0133             });
0134         });
0135     });
0136 
0137     registry->setEventQueue(m_eventQueue);
0138     registry->create(m_connectionThreadObject);
0139     registry->setup();
0140 }
0141 
0142 void XdgTest::createPopup()
0143 {
0144     if (m_popupSurface) {
0145         m_popupSurface->deleteLater();
0146     }
0147 
0148     m_popupSurface = m_compositor->createSurface(this);
0149 
0150     XdgPositioner positioner(QSize(200, 200), QRect(50, 50, 400, 400));
0151     positioner.setAnchorEdge(Qt::BottomEdge | Qt::RightEdge);
0152     positioner.setGravity(Qt::BottomEdge);
0153     positioner.setConstraints(XdgPositioner::Constraint::FlipX | XdgPositioner::Constraint::SlideY);
0154     m_xdgShellPopup = m_xdgShell->createPopup(m_popupSurface, m_xdgShellSurface, positioner, m_popupSurface);
0155     renderPopup();
0156 }
0157 
0158 void XdgTest::render()
0159 {
0160     const QSize &size = m_xdgShellSurface->size().isValid() ? m_xdgShellSurface->size() : QSize(500, 500);
0161     auto buffer = m_shm->getBuffer(size, size.width() * 4).toStrongRef();
0162     buffer->setUsed(true);
0163     QImage image(buffer->address(), size.width(), size.height(), QImage::Format_ARGB32_Premultiplied);
0164     image.fill(QColor(255, 255, 255, 255));
0165     // draw a red rectangle indicating the anchor of the top level
0166     QPainter painter(&image);
0167     painter.setBrush(Qt::red);
0168     painter.setPen(Qt::black);
0169     painter.drawRect(50, 50, 400, 400);
0170 
0171     m_surface->attachBuffer(*buffer);
0172     m_surface->damage(QRect(QPoint(0, 0), size));
0173     m_surface->commit(Surface::CommitFlag::None);
0174     buffer->setUsed(false);
0175 }
0176 
0177 void XdgTest::renderPopup()
0178 {
0179     QSize size(200, 200);
0180     auto buffer = m_shm->getBuffer(size, size.width() * 4).toStrongRef();
0181     buffer->setUsed(true);
0182     QImage image(buffer->address(), size.width(), size.height(), QImage::Format_ARGB32_Premultiplied);
0183     image.fill(QColor(0, 0, 255, 255));
0184 
0185     m_popupSurface->attachBuffer(*buffer);
0186     m_popupSurface->damage(QRect(QPoint(0, 0), size));
0187     m_popupSurface->commit(Surface::CommitFlag::None);
0188     buffer->setUsed(false);
0189 }
0190 
0191 int main(int argc, char **argv)
0192 {
0193     QCoreApplication app(argc, argv);
0194     XdgTest client;
0195     client.init();
0196 
0197     return app.exec();
0198 }
0199 
0200 #include "xdgtest.moc"