File indexing completed on 2024-11-10 04:57:28

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