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

0001 /*
0002     SPDX-FileCopyrightText: 2018 David Edmundson <kde@davidedmundson.co.uk>
0003     SPDX-FileCopyrightText: 2020 David Edmundson <kde@davidedmundson.co.uk>
0004 
0005     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0006 */
0007 #include "xdgoutput_v1_interface.h"
0008 #include "display.h"
0009 #include "output_interface.h"
0010 
0011 #include "qwayland-server-xdg-output-unstable-v1.h"
0012 
0013 #include "core/output.h"
0014 
0015 #include <QDebug>
0016 #include <QHash>
0017 #include <QPointer>
0018 #include <cmath>
0019 
0020 using namespace KWin;
0021 
0022 namespace KWaylandServer
0023 {
0024 static const quint32 s_version = 3;
0025 
0026 class XdgOutputV1Interface : public QObject, public QtWaylandServer::zxdg_output_v1
0027 {
0028 public:
0029     explicit XdgOutputV1Interface(OutputInterface *wlOutput);
0030 
0031     void resend();
0032     void update();
0033 
0034     QPointF pos;
0035     QSizeF size;
0036     QString name;
0037     QString description;
0038     QPointer<OutputInterface> output;
0039 
0040     void sendLogicalPosition(Resource *resource);
0041     void sendLogicalSize(Resource *resource);
0042     void sendDone(Resource *resource);
0043 
0044 protected:
0045     void zxdg_output_v1_bind_resource(Resource *resource) override;
0046     void zxdg_output_v1_destroy(Resource *resource) override;
0047 };
0048 
0049 class XdgOutputManagerV1InterfacePrivate : public QtWaylandServer::zxdg_output_manager_v1
0050 {
0051 public:
0052     explicit XdgOutputManagerV1InterfacePrivate(Display *display);
0053 
0054     QHash<OutputInterface *, XdgOutputV1Interface *> outputs;
0055 
0056 protected:
0057     void zxdg_output_manager_v1_destroy(Resource *resource) override;
0058     void zxdg_output_manager_v1_get_xdg_output(Resource *resource, uint32_t id, wl_resource *output) override;
0059 };
0060 
0061 XdgOutputManagerV1Interface::XdgOutputManagerV1Interface(Display *display, QObject *parent)
0062     : QObject(parent)
0063     , d(new XdgOutputManagerV1InterfacePrivate(display))
0064 {
0065 }
0066 
0067 XdgOutputManagerV1Interface::~XdgOutputManagerV1Interface()
0068 {
0069 }
0070 
0071 void XdgOutputManagerV1Interface::offer(OutputInterface *output)
0072 {
0073     Q_ASSERT_X(!d->outputs.contains(output), "offer", "An XdgOuputInterface already exists for this output");
0074 
0075     auto xdgOutput = new XdgOutputV1Interface(output);
0076     d->outputs[output] = xdgOutput;
0077     connect(output, &QObject::destroyed, this, [this, output]() {
0078         delete d->outputs.take(output);
0079     });
0080 }
0081 
0082 XdgOutputManagerV1InterfacePrivate::XdgOutputManagerV1InterfacePrivate(Display *d)
0083     : QtWaylandServer::zxdg_output_manager_v1(*d, s_version)
0084 {
0085 }
0086 
0087 void XdgOutputManagerV1InterfacePrivate::zxdg_output_manager_v1_get_xdg_output(Resource *resource, uint32_t id, wl_resource *outputResource)
0088 {
0089     auto output = OutputInterface::get(outputResource);
0090     if (!output) { // output client is requesting XdgOutput for an Output that doesn't exist
0091         return;
0092     }
0093     auto xdgOutput = outputs.value(output);
0094     if (!xdgOutput) {
0095         return; // client is requesting XdgOutput for an Output that doesn't exist
0096     }
0097     xdgOutput->add(resource->client(), id, resource->version());
0098 }
0099 
0100 void XdgOutputManagerV1InterfacePrivate::zxdg_output_manager_v1_destroy(Resource *resource)
0101 {
0102     wl_resource_destroy(resource->handle);
0103 }
0104 
0105 XdgOutputV1Interface::XdgOutputV1Interface(OutputInterface *output)
0106     : output(output)
0107 {
0108     const Output *handle = output->handle();
0109 
0110     name = handle->name();
0111     description = handle->description();
0112     pos = handle->geometry().topLeft();
0113     size = handle->geometry().size();
0114 
0115     connect(handle, &Output::geometryChanged, this, &XdgOutputV1Interface::update);
0116 }
0117 
0118 void XdgOutputV1Interface::update()
0119 {
0120     if (!output || output->isRemoved()) {
0121         return;
0122     }
0123 
0124     const QRectF geometry = output->handle()->fractionalGeometry();
0125     const auto resources = resourceMap();
0126 
0127     if (pos != geometry.topLeft()) {
0128         pos = geometry.topLeft();
0129         for (auto resource : resources) {
0130             sendLogicalPosition(resource);
0131         }
0132     }
0133 
0134     if (size != geometry.size()) {
0135         size = geometry.size();
0136         for (auto resource : resources) {
0137             sendLogicalSize(resource);
0138         }
0139     }
0140 
0141     for (auto resource : resources) {
0142         if (wl_resource_get_version(resource->handle) < 3) {
0143             send_done(resource->handle);
0144         }
0145     }
0146 
0147     output->scheduleDone();
0148 }
0149 
0150 void XdgOutputV1Interface::zxdg_output_v1_destroy(Resource *resource)
0151 {
0152     wl_resource_destroy(resource->handle);
0153 }
0154 
0155 void XdgOutputV1Interface::zxdg_output_v1_bind_resource(Resource *resource)
0156 {
0157     if (!output || output->isRemoved()) {
0158         return;
0159     }
0160 
0161     sendLogicalPosition(resource);
0162     sendLogicalSize(resource);
0163     if (resource->version() >= ZXDG_OUTPUT_V1_NAME_SINCE_VERSION) {
0164         send_name(resource->handle, name);
0165     }
0166     if (resource->version() >= ZXDG_OUTPUT_V1_DESCRIPTION_SINCE_VERSION) {
0167         send_description(resource->handle, description);
0168     }
0169 
0170     sendDone(resource);
0171 
0172     ClientConnection *connection = output->display()->getConnection(resource->client());
0173     connect(connection, &ClientConnection::scaleOverrideChanged, this, &XdgOutputV1Interface::resend, Qt::UniqueConnection);
0174 }
0175 
0176 void XdgOutputV1Interface::sendLogicalSize(Resource *resource)
0177 {
0178     ClientConnection *connection = output->display()->getConnection(resource->client());
0179     qreal scaleOverride = connection->scaleOverride();
0180 
0181     send_logical_size(resource->handle, std::round(size.width() * scaleOverride), std::round(size.height() * scaleOverride));
0182 }
0183 
0184 void XdgOutputV1Interface::sendLogicalPosition(Resource *resource)
0185 {
0186     ClientConnection *connection = output->display()->getConnection(resource->client());
0187     qreal scaleOverride = connection->scaleOverride();
0188 
0189     send_logical_position(resource->handle, pos.x() * scaleOverride, pos.y() * scaleOverride);
0190 }
0191 
0192 void XdgOutputV1Interface::sendDone(Resource *resource)
0193 {
0194     if (wl_resource_get_version(resource->handle) >= 3) {
0195         output->done(resource->client());
0196     } else {
0197         send_done(resource->handle);
0198     }
0199 }
0200 
0201 void XdgOutputV1Interface::resend()
0202 {
0203     if (!output || output->isRemoved()) {
0204         return;
0205     }
0206 
0207     auto changedConnection = qobject_cast<ClientConnection *>(sender());
0208     const auto outputResources = resourceMap();
0209     for (auto resource : outputResources) {
0210         ClientConnection *connection = output->display()->getConnection(resource->client());
0211         if (connection == changedConnection) {
0212             sendLogicalPosition(resource);
0213             sendLogicalSize(resource);
0214             sendDone(resource);
0215         }
0216     }
0217 }
0218 }