kwin/src/wayland/output_interface.cpp
2023-02-12 12:12:46 +01:00

328 lines
9.5 KiB
C++

/*
SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
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 "core/output.h"
#include "qwayland-server-wayland.h"
#include <QPointer>
#include <QTimer>
#include <QVector>
#include <cmath>
namespace KWaylandServer
{
static const int s_version = 4;
class OutputInterfacePrivate : public QtWaylandServer::wl_output
{
public:
explicit OutputInterfacePrivate(Display *display, OutputInterface *q, KWin::Output *handle);
void sendScale(Resource *resource);
void sendGeometry(Resource *resource);
void sendMode(Resource *resource);
void sendDone(Resource *resource);
OutputInterface *q;
QPointer<Display> display;
QPointer<KWin::Output> handle;
QSize physicalSize;
QPoint globalPosition;
QString manufacturer;
QString model;
int scale = 1;
KWin::Output::SubPixel subPixel = KWin::Output::SubPixel::Unknown;
KWin::Output::Transform transform = KWin::Output::Transform::Normal;
QSize modeSize;
int refreshRate = 0;
QString name;
QString description;
QTimer doneTimer;
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, KWin::Output *handle)
: QtWaylandServer::wl_output(*display, s_version)
, q(q)
, display(display)
, handle(handle)
{
}
void OutputInterfacePrivate::sendMode(Resource *resource)
{
send_mode(resource->handle, mode_current, modeSize.width(), modeSize.height(), 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::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.
}
if (resource->version() >= WL_OUTPUT_NAME_SINCE_VERSION) {
send_name(resource->handle, name);
}
if (resource->version() >= WL_OUTPUT_DESCRIPTION_SINCE_VERSION) {
send_description(resource->handle, description);
}
sendMode(resource);
sendScale(resource);
sendGeometry(resource);
sendDone(resource);
Q_EMIT q->bound(display->getConnection(resource->client()), resource->handle);
}
OutputInterface::OutputInterface(Display *display, KWin::Output *handle, QObject *parent)
: QObject(parent)
, d(new OutputInterfacePrivate(display, this, handle))
{
DisplayPrivate *displayPrivate = DisplayPrivate::get(display);
displayPrivate->outputs.append(this);
// Delay the done event to batch property updates.
d->doneTimer.setSingleShot(true);
d->doneTimer.setInterval(0);
connect(&d->doneTimer, &QTimer::timeout, this, [this]() {
const auto resources = d->resourceMap();
for (const auto &resource : resources) {
d->sendDone(resource);
}
});
d->name = handle->name();
d->description = handle->description();
d->transform = handle->transform();
d->manufacturer = handle->manufacturer();
d->model = handle->model();
d->physicalSize = handle->physicalSize();
d->globalPosition = handle->geometry().topLeft();
d->scale = std::ceil(handle->scale());
d->modeSize = handle->modeSize();
d->refreshRate = handle->refreshRate();
d->subPixel = handle->subPixel();
connect(handle, &KWin::Output::geometryChanged, this, [this]() {
const QPoint position = d->handle->geometry().topLeft();
if (d->globalPosition != position) {
d->globalPosition = position;
const auto resources = d->resourceMap();
for (const auto &resource : resources) {
d->sendGeometry(resource);
}
scheduleDone();
}
});
connect(handle, &KWin::Output::scaleChanged, this, [this]() {
const int scale = std::ceil(d->handle->scale());
if (d->scale != scale) {
d->scale = scale;
const auto resources = d->resourceMap();
for (const auto &resource : resources) {
d->sendScale(resource);
}
scheduleDone();
}
});
connect(handle, &KWin::Output::transformChanged, this, [this]() {
const KWin::Output::Transform transform = d->handle->transform();
if (d->transform != transform) {
d->transform = transform;
const auto resources = d->resourceMap();
for (const auto &resource : resources) {
d->sendGeometry(resource);
}
scheduleDone();
}
});
connect(handle, &KWin::Output::currentModeChanged, this, [this]() {
const QSize size = d->handle->modeSize();
const int refreshRate = d->handle->refreshRate();
if (d->modeSize != size || d->refreshRate != refreshRate) {
d->modeSize = size;
d->refreshRate = refreshRate;
const auto resources = d->resourceMap();
for (const auto &resource : resources) {
d->sendMode(resource);
}
scheduleDone();
}
});
}
OutputInterface::~OutputInterface()
{
remove();
}
Display *OutputInterface::display() const
{
return d->display;
}
KWin::Output *OutputInterface::handle() const
{
return d->handle;
}
bool OutputInterface::isRemoved() const
{
return d->isGlobalRemoved();
}
void OutputInterface::remove()
{
if (d->isGlobalRemoved()) {
return;
}
d->doneTimer.stop();
if (d->handle) {
disconnect(d->handle, nullptr, this, nullptr);
}
if (d->display) {
DisplayPrivate *displayPrivate = DisplayPrivate::get(d->display);
displayPrivate->outputs.removeOne(this);
}
Q_EMIT removed();
d->globalRemove();
}
QVector<wl_resource *> OutputInterface::clientResources(ClientConnection *client) const
{
const auto outputResources = d->resourceMap().values(client->client());
QVector<wl_resource *> ret;
ret.reserve(outputResources.count());
for (OutputInterfacePrivate::Resource *resource : outputResources) {
ret.append(resource->handle);
}
return ret;
}
void OutputInterface::scheduleDone()
{
if (!d->isGlobalRemoved()) {
d->doneTimer.start();
}
}
void OutputInterface::done(wl_client *client)
{
if (!d->isGlobalRemoved()) {
d->sendDone(d->resourceMap().value(client));
}
}
OutputInterface *OutputInterface::get(wl_resource *native)
{
if (auto outputPrivate = resource_cast<OutputInterfacePrivate *>(native)) {
return outputPrivate->q;
}
return nullptr;
}
} // namespace KWaylandServer