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

0001 /*
0002     SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
0003     SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
0004 
0005     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0006 */
0007 #include "output_interface.h"
0008 #include "display.h"
0009 #include "display_p.h"
0010 #include "utils.h"
0011 
0012 #include "core/output.h"
0013 
0014 #include "qwayland-server-wayland.h"
0015 
0016 #include <QPointer>
0017 #include <QTimer>
0018 #include <QVector>
0019 
0020 namespace KWaylandServer
0021 {
0022 static const int s_version = 4;
0023 
0024 class OutputInterfacePrivate : public QtWaylandServer::wl_output
0025 {
0026 public:
0027     explicit OutputInterfacePrivate(Display *display, OutputInterface *q, KWin::Output *handle);
0028 
0029     void sendScale(Resource *resource);
0030     void sendGeometry(Resource *resource);
0031     void sendMode(Resource *resource);
0032     void sendDone(Resource *resource);
0033 
0034     OutputInterface *q;
0035     QPointer<Display> display;
0036     QPointer<KWin::Output> handle;
0037     QSize physicalSize;
0038     QPoint globalPosition;
0039     QString manufacturer;
0040     QString model;
0041     int scale = 1;
0042     KWin::Output::SubPixel subPixel = KWin::Output::SubPixel::Unknown;
0043     KWin::Output::Transform transform = KWin::Output::Transform::Normal;
0044     QSize modeSize;
0045     int refreshRate = 0;
0046     QString name;
0047     QString description;
0048     QTimer doneTimer;
0049 
0050 private:
0051     void output_destroy_global() override;
0052     void output_bind_resource(Resource *resource) override;
0053     void output_release(Resource *resource) override;
0054 };
0055 
0056 OutputInterfacePrivate::OutputInterfacePrivate(Display *display, OutputInterface *q, KWin::Output *handle)
0057     : QtWaylandServer::wl_output(*display, s_version)
0058     , q(q)
0059     , display(display)
0060     , handle(handle)
0061 {
0062 }
0063 
0064 void OutputInterfacePrivate::sendMode(Resource *resource)
0065 {
0066     send_mode(resource->handle, mode_current, modeSize.width(), modeSize.height(), refreshRate);
0067 }
0068 
0069 void OutputInterfacePrivate::sendScale(Resource *resource)
0070 {
0071     if (resource->version() >= WL_OUTPUT_SCALE_SINCE_VERSION) {
0072         send_scale(resource->handle, scale);
0073     }
0074 }
0075 
0076 static quint32 kwaylandServerTransformToWaylandTransform(KWin::Output::Transform transform)
0077 {
0078     switch (transform) {
0079     case KWin::Output::Transform::Normal:
0080         return OutputInterfacePrivate::transform_normal;
0081     case KWin::Output::Transform::Rotated90:
0082         return OutputInterfacePrivate::transform_90;
0083     case KWin::Output::Transform::Rotated180:
0084         return OutputInterfacePrivate::transform_180;
0085     case KWin::Output::Transform::Rotated270:
0086         return OutputInterfacePrivate::transform_270;
0087     case KWin::Output::Transform::Flipped:
0088         return OutputInterfacePrivate::transform_flipped;
0089     case KWin::Output::Transform::Flipped90:
0090         return OutputInterfacePrivate::transform_flipped_90;
0091     case KWin::Output::Transform::Flipped180:
0092         return OutputInterfacePrivate::transform_flipped_180;
0093     case KWin::Output::Transform::Flipped270:
0094         return OutputInterfacePrivate::transform_flipped_270;
0095     default:
0096         Q_UNREACHABLE();
0097     }
0098 }
0099 
0100 static quint32 kwaylandServerSubPixelToWaylandSubPixel(KWin::Output::SubPixel subPixel)
0101 {
0102     switch (subPixel) {
0103     case KWin::Output::SubPixel::Unknown:
0104         return OutputInterfacePrivate::subpixel_unknown;
0105     case KWin::Output::SubPixel::None:
0106         return OutputInterfacePrivate::subpixel_none;
0107     case KWin::Output::SubPixel::Horizontal_RGB:
0108         return OutputInterfacePrivate::subpixel_horizontal_rgb;
0109     case KWin::Output::SubPixel::Horizontal_BGR:
0110         return OutputInterfacePrivate::subpixel_horizontal_bgr;
0111     case KWin::Output::SubPixel::Vertical_RGB:
0112         return OutputInterfacePrivate::subpixel_vertical_rgb;
0113     case KWin::Output::SubPixel::Vertical_BGR:
0114         return OutputInterfacePrivate::subpixel_vertical_bgr;
0115     default:
0116         Q_UNREACHABLE();
0117     }
0118 }
0119 
0120 void OutputInterfacePrivate::sendGeometry(Resource *resource)
0121 {
0122     send_geometry(resource->handle,
0123                   globalPosition.x(),
0124                   globalPosition.y(),
0125                   physicalSize.width(),
0126                   physicalSize.height(),
0127                   kwaylandServerSubPixelToWaylandSubPixel(subPixel),
0128                   manufacturer,
0129                   model,
0130                   kwaylandServerTransformToWaylandTransform(transform));
0131 }
0132 
0133 void OutputInterfacePrivate::sendDone(Resource *resource)
0134 {
0135     if (resource->version() >= WL_OUTPUT_DONE_SINCE_VERSION) {
0136         send_done(resource->handle);
0137     }
0138 }
0139 
0140 void OutputInterfacePrivate::output_destroy_global()
0141 {
0142     delete q;
0143 }
0144 
0145 void OutputInterfacePrivate::output_release(Resource *resource)
0146 {
0147     wl_resource_destroy(resource->handle);
0148 }
0149 
0150 void OutputInterfacePrivate::output_bind_resource(Resource *resource)
0151 {
0152     if (isGlobalRemoved()) {
0153         return; // We are waiting for the wl_output global to be destroyed.
0154     }
0155 
0156     if (resource->version() >= WL_OUTPUT_NAME_SINCE_VERSION) {
0157         send_name(resource->handle, name);
0158     }
0159     if (resource->version() >= WL_OUTPUT_DESCRIPTION_SINCE_VERSION) {
0160         send_description(resource->handle, description);
0161     }
0162 
0163     sendMode(resource);
0164     sendScale(resource);
0165     sendGeometry(resource);
0166     sendDone(resource);
0167 
0168     Q_EMIT q->bound(display->getConnection(resource->client()), resource->handle);
0169 }
0170 
0171 OutputInterface::OutputInterface(Display *display, KWin::Output *handle, QObject *parent)
0172     : QObject(parent)
0173     , d(new OutputInterfacePrivate(display, this, handle))
0174 {
0175     DisplayPrivate *displayPrivate = DisplayPrivate::get(display);
0176     displayPrivate->outputs.append(this);
0177 
0178     // Delay the done event to batch property updates.
0179     d->doneTimer.setSingleShot(true);
0180     d->doneTimer.setInterval(0);
0181     connect(&d->doneTimer, &QTimer::timeout, this, [this]() {
0182         const auto resources = d->resourceMap();
0183         for (const auto &resource : resources) {
0184             d->sendDone(resource);
0185         }
0186     });
0187 
0188     d->name = handle->name();
0189     d->description = handle->description();
0190     d->transform = handle->transform();
0191     d->manufacturer = handle->manufacturer();
0192     d->model = handle->model();
0193     d->physicalSize = handle->physicalSize();
0194     d->globalPosition = handle->geometry().topLeft();
0195     d->scale = std::ceil(handle->scale());
0196     d->modeSize = handle->modeSize();
0197     d->refreshRate = handle->refreshRate();
0198     d->subPixel = handle->subPixel();
0199 
0200     connect(handle, &KWin::Output::geometryChanged, this, [this]() {
0201         const QPoint position = d->handle->geometry().topLeft();
0202         if (d->globalPosition != position) {
0203             d->globalPosition = position;
0204             const auto resources = d->resourceMap();
0205             for (const auto &resource : resources) {
0206                 d->sendGeometry(resource);
0207             }
0208             scheduleDone();
0209         }
0210     });
0211 
0212     connect(handle, &KWin::Output::scaleChanged, this, [this]() {
0213         const int scale = std::ceil(d->handle->scale());
0214         if (d->scale != scale) {
0215             d->scale = scale;
0216             const auto resources = d->resourceMap();
0217             for (const auto &resource : resources) {
0218                 d->sendScale(resource);
0219             }
0220             scheduleDone();
0221         }
0222     });
0223 
0224     connect(handle, &KWin::Output::transformChanged, this, [this]() {
0225         const KWin::Output::Transform transform = d->handle->transform();
0226         if (d->transform != transform) {
0227             d->transform = transform;
0228             const auto resources = d->resourceMap();
0229             for (const auto &resource : resources) {
0230                 d->sendGeometry(resource);
0231             }
0232             scheduleDone();
0233         }
0234     });
0235 
0236     connect(handle, &KWin::Output::currentModeChanged, this, [this]() {
0237         const QSize size = d->handle->modeSize();
0238         const int refreshRate = d->handle->refreshRate();
0239         if (d->modeSize != size || d->refreshRate != refreshRate) {
0240             d->modeSize = size;
0241             d->refreshRate = refreshRate;
0242             const auto resources = d->resourceMap();
0243             for (const auto &resource : resources) {
0244                 d->sendMode(resource);
0245             }
0246             scheduleDone();
0247         }
0248     });
0249 }
0250 
0251 OutputInterface::~OutputInterface()
0252 {
0253     remove();
0254 }
0255 
0256 Display *OutputInterface::display() const
0257 {
0258     return d->display;
0259 }
0260 
0261 KWin::Output *OutputInterface::handle() const
0262 {
0263     return d->handle;
0264 }
0265 
0266 bool OutputInterface::isRemoved() const
0267 {
0268     return d->isGlobalRemoved();
0269 }
0270 
0271 void OutputInterface::remove()
0272 {
0273     if (d->isGlobalRemoved()) {
0274         return;
0275     }
0276 
0277     d->doneTimer.stop();
0278     if (d->handle) {
0279         disconnect(d->handle, nullptr, this, nullptr);
0280     }
0281 
0282     if (d->display) {
0283         DisplayPrivate *displayPrivate = DisplayPrivate::get(d->display);
0284         displayPrivate->outputs.removeOne(this);
0285     }
0286 
0287     Q_EMIT removed();
0288     d->globalRemove();
0289 }
0290 
0291 QVector<wl_resource *> OutputInterface::clientResources(ClientConnection *client) const
0292 {
0293     const auto outputResources = d->resourceMap().values(client->client());
0294     QVector<wl_resource *> ret;
0295     ret.reserve(outputResources.count());
0296 
0297     for (OutputInterfacePrivate::Resource *resource : outputResources) {
0298         ret.append(resource->handle);
0299     }
0300 
0301     return ret;
0302 }
0303 
0304 void OutputInterface::scheduleDone()
0305 {
0306     if (!d->isGlobalRemoved()) {
0307         d->doneTimer.start();
0308     }
0309 }
0310 
0311 void OutputInterface::done(wl_client *client)
0312 {
0313     if (!d->isGlobalRemoved()) {
0314         d->sendDone(d->resourceMap().value(client));
0315     }
0316 }
0317 
0318 OutputInterface *OutputInterface::get(wl_resource *native)
0319 {
0320     if (auto outputPrivate = resource_cast<OutputInterfacePrivate *>(native)) {
0321         return outputPrivate->q;
0322     }
0323     return nullptr;
0324 }
0325 
0326 } // namespace KWaylandServer