/* SPDX-FileCopyrightText: 2014 Martin Gräßlin SPDX-FileCopyrightText: 2021 Vlad Zahorodnii SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL */ #include "output_interface.h" #include "display.h" #include "display_p.h" #include "utils.h" #include "qwayland-server-wayland.h" #include #include namespace KWaylandServer { static const int s_version = 3; class OutputInterfacePrivate : public QtWaylandServer::wl_output { public: explicit OutputInterfacePrivate(Display *display, OutputInterface *q); void sendScale(Resource *resource); void sendGeometry(Resource *resource); void sendMode(Resource *resource); void sendDone(Resource *resource); void broadcastGeometry(); OutputInterface *q; QPointer display; QSize physicalSize; QPoint globalPosition; QString manufacturer = QStringLiteral("org.kde.kwin"); QString model = QStringLiteral("none"); int scale = 1; KWin::Output::SubPixel subPixel = KWin::Output::SubPixel::Unknown; KWin::Output::Transform transform = KWin::Output::Transform::Normal; QSharedPointer mode; struct { KWin::Output::DpmsMode mode = KWin::Output::DpmsMode::Off; bool supported = false; } dpms; private: void output_destroy_global() override; void output_bind_resource(Resource *resource) override; void output_release(Resource *resource) override; }; OutputInterfacePrivate::OutputInterfacePrivate(Display *display, OutputInterface *q) : QtWaylandServer::wl_output(*display, s_version) , q(q) , display(display) { } void OutputInterfacePrivate::sendMode(Resource *resource) { send_mode(resource->handle, mode_current, mode->size().width(), mode->size().height(), mode->refreshRate()); } void OutputInterfacePrivate::sendScale(Resource *resource) { if (resource->version() >= WL_OUTPUT_SCALE_SINCE_VERSION) { send_scale(resource->handle, scale); } } static quint32 kwaylandServerTransformToWaylandTransform(KWin::Output::Transform transform) { switch (transform) { case KWin::Output::Transform::Normal: return OutputInterfacePrivate::transform_normal; case KWin::Output::Transform::Rotated90: return OutputInterfacePrivate::transform_90; case KWin::Output::Transform::Rotated180: return OutputInterfacePrivate::transform_180; case KWin::Output::Transform::Rotated270: return OutputInterfacePrivate::transform_270; case KWin::Output::Transform::Flipped: return OutputInterfacePrivate::transform_flipped; case KWin::Output::Transform::Flipped90: return OutputInterfacePrivate::transform_flipped_90; case KWin::Output::Transform::Flipped180: return OutputInterfacePrivate::transform_flipped_180; case KWin::Output::Transform::Flipped270: return OutputInterfacePrivate::transform_flipped_270; default: Q_UNREACHABLE(); } } static quint32 kwaylandServerSubPixelToWaylandSubPixel(KWin::Output::SubPixel subPixel) { switch (subPixel) { case KWin::Output::SubPixel::Unknown: return OutputInterfacePrivate::subpixel_unknown; case KWin::Output::SubPixel::None: return OutputInterfacePrivate::subpixel_none; case KWin::Output::SubPixel::Horizontal_RGB: return OutputInterfacePrivate::subpixel_horizontal_rgb; case KWin::Output::SubPixel::Horizontal_BGR: return OutputInterfacePrivate::subpixel_horizontal_bgr; case KWin::Output::SubPixel::Vertical_RGB: return OutputInterfacePrivate::subpixel_vertical_rgb; case KWin::Output::SubPixel::Vertical_BGR: return OutputInterfacePrivate::subpixel_vertical_bgr; default: Q_UNREACHABLE(); } } void OutputInterfacePrivate::sendGeometry(Resource *resource) { send_geometry(resource->handle, globalPosition.x(), globalPosition.y(), physicalSize.width(), physicalSize.height(), kwaylandServerSubPixelToWaylandSubPixel(subPixel), manufacturer, model, kwaylandServerTransformToWaylandTransform(transform)); } void OutputInterfacePrivate::sendDone(Resource *resource) { if (resource->version() >= WL_OUTPUT_DONE_SINCE_VERSION) { send_done(resource->handle); } } void OutputInterfacePrivate::broadcastGeometry() { const auto outputResources = resourceMap(); for (Resource *resource : outputResources) { sendGeometry(resource); } } void OutputInterfacePrivate::output_destroy_global() { delete q; } void OutputInterfacePrivate::output_release(Resource *resource) { wl_resource_destroy(resource->handle); } void OutputInterfacePrivate::output_bind_resource(Resource *resource) { if (isGlobalRemoved()) { return; // We are waiting for the wl_output global to be destroyed. } sendMode(resource); sendScale(resource); sendGeometry(resource); sendDone(resource); Q_EMIT q->bound(display->getConnection(resource->client()), resource->handle); } OutputInterface::OutputInterface(Display *display, QObject *parent) : QObject(parent) , d(new OutputInterfacePrivate(display, this)) { DisplayPrivate *displayPrivate = DisplayPrivate::get(display); displayPrivate->outputs.append(this); } OutputInterface::~OutputInterface() { remove(); } void OutputInterface::remove() { if (d->isGlobalRemoved()) { return; } if (d->display) { DisplayPrivate *displayPrivate = DisplayPrivate::get(d->display); displayPrivate->outputs.removeOne(this); } Q_EMIT removed(); d->globalRemove(); } QSize OutputInterface::pixelSize() const { return d->mode->size(); } int OutputInterface::refreshRate() const { return d->mode->refreshRate(); } QSharedPointer OutputInterface::mode() const { return d->mode; } void OutputInterface::setMode(const QSharedPointer &mode) { if (d->mode == mode) { return; } d->mode = mode; const auto outputResources = d->resourceMap(); for (OutputInterfacePrivate::Resource *resource : outputResources) { d->sendMode(resource); } Q_EMIT modeChanged(); Q_EMIT refreshRateChanged(mode->refreshRate()); Q_EMIT pixelSizeChanged(mode->size()); } QSize OutputInterface::physicalSize() const { return d->physicalSize; } void OutputInterface::setPhysicalSize(const QSize &physicalSize) { if (d->physicalSize == physicalSize) { return; } d->physicalSize = physicalSize; d->broadcastGeometry(); Q_EMIT physicalSizeChanged(d->physicalSize); } QPoint OutputInterface::globalPosition() const { return d->globalPosition; } void OutputInterface::setGlobalPosition(const QPoint &globalPos) { if (d->globalPosition == globalPos) { return; } d->globalPosition = globalPos; Q_EMIT globalPositionChanged(d->globalPosition); } QString OutputInterface::manufacturer() const { return d->manufacturer; } void OutputInterface::setManufacturer(const QString &manufacturer) { if (d->manufacturer == manufacturer) { return; } d->manufacturer = manufacturer; d->broadcastGeometry(); Q_EMIT manufacturerChanged(d->manufacturer); } QString OutputInterface::model() const { return d->model; } void OutputInterface::setModel(const QString &model) { if (d->model == model) { return; } d->model = model; d->broadcastGeometry(); Q_EMIT modelChanged(d->model); } int OutputInterface::scale() const { return d->scale; } void OutputInterface::setScale(int scale) { if (d->scale == scale) { return; } d->scale = scale; const auto outputResources = d->resourceMap(); for (OutputInterfacePrivate::Resource *resource : outputResources) { d->sendScale(resource); } Q_EMIT scaleChanged(d->scale); } KWin::Output::SubPixel OutputInterface::subPixel() const { return d->subPixel; } void OutputInterface::setSubPixel(KWin::Output::SubPixel subPixel) { if (d->subPixel == subPixel) { return; } d->subPixel = subPixel; d->broadcastGeometry(); Q_EMIT subPixelChanged(d->subPixel); } KWin::Output::Transform OutputInterface::transform() const { return d->transform; } void OutputInterface::setTransform(KWin::Output::Transform transform) { if (d->transform == transform) { return; } d->transform = transform; d->broadcastGeometry(); Q_EMIT transformChanged(d->transform); } void OutputInterface::setDpmsMode(KWin::Output::DpmsMode mode) { if (d->dpms.mode == mode) { return; } d->dpms.mode = mode; Q_EMIT dpmsModeChanged(); } void OutputInterface::setDpmsSupported(bool supported) { if (d->dpms.supported == supported) { return; } d->dpms.supported = supported; Q_EMIT dpmsSupportedChanged(); } KWin::Output::DpmsMode OutputInterface::dpmsMode() const { return d->dpms.mode; } bool OutputInterface::isDpmsSupported() const { return d->dpms.supported; } QVector OutputInterface::clientResources(ClientConnection *client) const { const auto outputResources = d->resourceMap().values(client->client()); QVector ret; ret.reserve(outputResources.count()); for (OutputInterfacePrivate::Resource *resource : outputResources) { ret.append(resource->handle); } return ret; } bool OutputInterface::isEnabled() const { if (!d->dpms.supported) { return true; } return d->dpms.mode == KWin::Output::DpmsMode::On; } void OutputInterface::done() { const auto outputResources = d->resourceMap(); for (OutputInterfacePrivate::Resource *resource : outputResources) { d->sendDone(resource); } } void OutputInterface::done(wl_client *client) { d->sendDone(d->resourceMap().value(client)); } OutputInterface *OutputInterface::get(wl_resource *native) { if (auto outputPrivate = resource_cast(native)) { return outputPrivate->q; } return nullptr; } } // namespace KWaylandServer