kwin/src/wayland/output_interface.cpp
Weng Xuetian 823a34a10d Add explicit done to OutputInterface and deprecate zxdg_output_v1.done if version 3 is used.
In zxdg_output_v1 version 3, done is deprecated and should be replaced
with wl_output.done. This makes automatically send done upon change not
suitable for the usage. OutputInterface user should now use done()
explicitly to send the update.

CCBUG: 433224
2021-03-19 10:39:07 -07:00

553 lines
15 KiB
C++

/*
SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@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_p.h"
#include "global_p.h"
#include "display.h"
#include <QVector>
#include <wayland-server.h>
namespace KWaylandServer
{
class OutputInterface::Private : public Global::Private
{
public:
struct ResourceData {
wl_resource *resource;
uint32_t version;
};
Private(OutputInterface *q, Display *d);
~Private();
void sendMode(wl_resource *resource, const Mode &mode);
void sendDone(const ResourceData &data);
void updateGeometry();
void updateScale();
QSize physicalSize;
QPoint globalPosition;
QString manufacturer = QStringLiteral("org.kde.kwin");
QString model = QStringLiteral("none");
int scale = 1;
SubPixel subPixel = SubPixel::Unknown;
Transform transform = Transform::Normal;
QList<Mode> modes;
QList<ResourceData> resources;
struct {
DpmsMode mode = DpmsMode::Off;
bool supported = false;
} dpms;
static OutputInterface *get(wl_resource *native);
private:
static Private *cast(wl_resource *native);
static void releaseCallback(wl_client *client, wl_resource *resource);
static void unbind(wl_resource *resource);
void bind(wl_client *client, uint32_t version, uint32_t id) override;
int32_t toTransform() const;
int32_t toSubPixel() const;
void sendGeometry(wl_resource *resource);
void sendScale(const ResourceData &data);
OutputInterface *q;
static QVector<Private*> s_privates;
static const struct wl_output_interface s_interface;
static const quint32 s_version;
};
QVector<OutputInterface::Private*> OutputInterface::Private::s_privates;
const quint32 OutputInterface::Private::s_version = 3;
OutputInterface::Private::Private(OutputInterface *q, Display *d)
: Global::Private(d, &wl_output_interface, s_version)
, q(q)
{
DisplayPrivate *displayPrivate = DisplayPrivate::get(display);
displayPrivate->outputs.append(q);
s_privates << this;
}
OutputInterface::Private::~Private()
{
if (display) {
DisplayPrivate *displayPrivate = DisplayPrivate::get(display);
displayPrivate->outputs.removeOne(q);
}
s_privates.removeAll(this);
}
#ifndef K_DOXYGEN
const struct wl_output_interface OutputInterface::Private::s_interface = {
releaseCallback
};
#endif
void OutputInterface::Private::releaseCallback(wl_client *client, wl_resource *resource)
{
Q_UNUSED(client);
unbind(resource);
}
OutputInterface *OutputInterface::Private::get(wl_resource *native)
{
if (Private *p = cast(native)) {
return p->q;
}
return nullptr;
}
OutputInterface::Private *OutputInterface::Private::cast(wl_resource *native)
{
for (auto it = s_privates.constBegin(); it != s_privates.constEnd(); ++it) {
const auto &resources = (*it)->resources;
auto rit = std::find_if(resources.constBegin(), resources.constEnd(), [native] (const ResourceData &data) { return data.resource == native; });
if (rit != resources.constEnd()) {
return (*it);
}
}
return nullptr;
}
OutputInterface::OutputInterface(Display *display, QObject *parent)
: Global(new Private(this, display), parent)
{
Q_D();
connect(this, &OutputInterface::currentModeChanged, this,
[this] {
Q_D();
auto currentModeIt = std::find_if(d->modes.constBegin(), d->modes.constEnd(), [](const Mode &mode) { return mode.flags.testFlag(ModeFlag::Current); });
if (currentModeIt == d->modes.constEnd()) {
return;
}
for (auto it = d->resources.constBegin(); it != d->resources.constEnd(); ++it) {
d->sendMode((*it).resource, *currentModeIt);
}
}
);
connect(this, &OutputInterface::subPixelChanged, this, [d] { d->updateGeometry(); });
connect(this, &OutputInterface::transformChanged, this, [d] { d->updateGeometry(); });
connect(this, &OutputInterface::globalPositionChanged, this, [d] { d->updateGeometry(); });
connect(this, &OutputInterface::modelChanged, this, [d] { d->updateGeometry(); });
connect(this, &OutputInterface::manufacturerChanged, this, [d] { d->updateGeometry(); });
connect(this, &OutputInterface::scaleChanged, this, [d] { d->updateScale(); });
}
OutputInterface::~OutputInterface() = default;
QSize OutputInterface::pixelSize() const
{
Q_D();
auto it = std::find_if(d->modes.constBegin(), d->modes.constEnd(),
[](const Mode &mode) {
return mode.flags.testFlag(ModeFlag::Current);
}
);
if (it == d->modes.constEnd()) {
return QSize();
}
return (*it).size;
}
int OutputInterface::refreshRate() const
{
Q_D();
auto it = std::find_if(d->modes.constBegin(), d->modes.constEnd(),
[](const Mode &mode) {
return mode.flags.testFlag(ModeFlag::Current);
}
);
if (it == d->modes.constEnd()) {
return 60000;
}
return (*it).refreshRate;
}
void OutputInterface::addMode(const QSize &size, OutputInterface::ModeFlags flags, int refreshRate)
{
Q_ASSERT(!isValid());
Q_D();
auto currentModeIt = std::find_if(d->modes.begin(), d->modes.end(),
[](const Mode &mode) {
return mode.flags.testFlag(ModeFlag::Current);
}
);
if (currentModeIt == d->modes.end() && !flags.testFlag(ModeFlag::Current)) {
// no mode with current flag - enforce
flags |= ModeFlag::Current;
}
if (currentModeIt != d->modes.end() && flags.testFlag(ModeFlag::Current)) {
// another mode has the current flag - remove
(*currentModeIt).flags &= ~uint(ModeFlag::Current);
}
if (flags.testFlag(ModeFlag::Preferred)) {
// remove from existing Preferred mode
auto preferredIt = std::find_if(d->modes.begin(), d->modes.end(),
[](const Mode &mode) {
return mode.flags.testFlag(ModeFlag::Preferred);
}
);
if (preferredIt != d->modes.end()) {
(*preferredIt).flags &= ~uint(ModeFlag::Preferred);
}
}
auto existingModeIt = std::find_if(d->modes.begin(), d->modes.end(),
[size,refreshRate](const Mode &mode) {
return mode.size == size && mode.refreshRate == refreshRate;
}
);
auto emitChanges = [this,flags,size,refreshRate] {
emit modesChanged();
if (flags.testFlag(ModeFlag::Current)) {
emit refreshRateChanged(refreshRate);
emit pixelSizeChanged(size);
emit currentModeChanged();
}
};
if (existingModeIt != d->modes.end()) {
if ((*existingModeIt).flags == flags) {
// nothing to do
return;
}
(*existingModeIt).flags = flags;
emitChanges();
return;
}
Mode mode;
mode.size = size;
mode.refreshRate = refreshRate;
mode.flags = flags;
d->modes << mode;
emitChanges();
}
void OutputInterface::setCurrentMode(const QSize &size, int refreshRate)
{
Q_D();
auto currentModeIt = std::find_if(d->modes.begin(), d->modes.end(),
[](const Mode &mode) {
return mode.flags.testFlag(ModeFlag::Current);
}
);
if (currentModeIt != d->modes.end()) {
// another mode has the current flag - remove
(*currentModeIt).flags &= ~uint(ModeFlag::Current);
}
auto existingModeIt = std::find_if(d->modes.begin(), d->modes.end(),
[size,refreshRate](const Mode &mode) {
return mode.size == size && mode.refreshRate == refreshRate;
}
);
Q_ASSERT(existingModeIt != d->modes.end());
(*existingModeIt).flags |= ModeFlag::Current;
emit modesChanged();
emit refreshRateChanged((*existingModeIt).refreshRate);
emit pixelSizeChanged((*existingModeIt).size);
emit currentModeChanged();
}
int32_t OutputInterface::Private::toTransform() const
{
switch (transform) {
case Transform::Normal:
return WL_OUTPUT_TRANSFORM_NORMAL;
case Transform::Rotated90:
return WL_OUTPUT_TRANSFORM_90;
case Transform::Rotated180:
return WL_OUTPUT_TRANSFORM_180;
case Transform::Rotated270:
return WL_OUTPUT_TRANSFORM_270;
case Transform::Flipped:
return WL_OUTPUT_TRANSFORM_FLIPPED;
case Transform::Flipped90:
return WL_OUTPUT_TRANSFORM_FLIPPED_90;
case Transform::Flipped180:
return WL_OUTPUT_TRANSFORM_FLIPPED_180;
case Transform::Flipped270:
return WL_OUTPUT_TRANSFORM_FLIPPED_270;
}
abort();
}
int32_t OutputInterface::Private::toSubPixel() const
{
switch (subPixel) {
case SubPixel::Unknown:
return WL_OUTPUT_SUBPIXEL_UNKNOWN;
case SubPixel::None:
return WL_OUTPUT_SUBPIXEL_NONE;
case SubPixel::HorizontalRGB:
return WL_OUTPUT_SUBPIXEL_HORIZONTAL_RGB;
case SubPixel::HorizontalBGR:
return WL_OUTPUT_SUBPIXEL_HORIZONTAL_BGR;
case SubPixel::VerticalRGB:
return WL_OUTPUT_SUBPIXEL_VERTICAL_RGB;
case SubPixel::VerticalBGR:
return WL_OUTPUT_SUBPIXEL_VERTICAL_BGR;
}
abort();
}
void OutputInterface::Private::bind(wl_client *client, uint32_t version, uint32_t id)
{
auto c = display->getConnection(client);
wl_resource *resource = c->createResource(&wl_output_interface, qMin(version, s_version), id);
if (!resource) {
wl_client_post_no_memory(client);
return;
}
wl_resource_set_user_data(resource, this);
wl_resource_set_implementation(resource, &s_interface, this, unbind);
ResourceData r;
r.resource = resource;
r.version = version;
resources << r;
sendGeometry(resource);
sendScale(r);
auto currentModeIt = modes.constEnd();
for (auto it = modes.constBegin(); it != modes.constEnd(); ++it) {
const Mode &mode = *it;
if (mode.flags.testFlag(ModeFlag::Current)) {
// needs to be sent as last mode
currentModeIt = it;
continue;
}
sendMode(resource, mode);
}
if (currentModeIt != modes.constEnd()) {
sendMode(resource, *currentModeIt);
}
sendDone(r);
c->flush();
emit q->bound(display->getConnection(client), r.resource);
}
void OutputInterface::Private::unbind(wl_resource *resource)
{
Private *o = cast(resource);
if (!o) {
return;
}
auto it = std::find_if(o->resources.begin(), o->resources.end(), [resource](const ResourceData &r) { return r.resource == resource; });
if (it != o->resources.end()) {
o->resources.erase(it);
}
}
void OutputInterface::Private::sendMode(wl_resource *resource, const Mode &mode)
{
int32_t flags = 0;
if (mode.flags.testFlag(ModeFlag::Current)) {
flags |= WL_OUTPUT_MODE_CURRENT;
}
if (mode.flags.testFlag(ModeFlag::Preferred)) {
flags |= WL_OUTPUT_MODE_PREFERRED;
}
wl_output_send_mode(resource,
flags,
mode.size.width(),
mode.size.height(),
mode.refreshRate);
}
void OutputInterface::Private::sendGeometry(wl_resource *resource)
{
wl_output_send_geometry(resource,
globalPosition.x(),
globalPosition.y(),
physicalSize.width(),
physicalSize.height(),
toSubPixel(),
qPrintable(manufacturer),
qPrintable(model),
toTransform());
}
void OutputInterface::Private::sendScale(const ResourceData &data)
{
if (data.version < 2) {
return;
}
wl_output_send_scale(data.resource, scale);
}
void OutputInterface::Private::sendDone(const ResourceData &data)
{
if (data.version < 2) {
return;
}
wl_output_send_done(data.resource);
}
void OutputInterface::Private::updateGeometry()
{
for (auto it = resources.constBegin(); it != resources.constEnd(); ++it) {
sendGeometry((*it).resource);
}
}
void OutputInterface::Private::updateScale()
{
for (auto it = resources.constBegin(); it != resources.constEnd(); ++it) {
sendScale(*it);
}
}
#define SETTER(setterName, type, argumentName) \
void OutputInterface::setterName(type arg) \
{ \
Q_D(); \
if (d->argumentName == arg) { \
return; \
} \
d->argumentName = arg; \
emit argumentName##Changed(d->argumentName); \
}
SETTER(setPhysicalSize, const QSize&, physicalSize)
SETTER(setGlobalPosition, const QPoint&, globalPosition)
SETTER(setManufacturer, const QString&, manufacturer)
SETTER(setModel, const QString&, model)
SETTER(setScale, int, scale)
SETTER(setSubPixel, SubPixel, subPixel)
SETTER(setTransform, Transform, transform)
#undef SETTER
QSize OutputInterface::physicalSize() const
{
Q_D();
return d->physicalSize;
}
QPoint OutputInterface::globalPosition() const
{
Q_D();
return d->globalPosition;
}
QString OutputInterface::manufacturer() const
{
Q_D();
return d->manufacturer;
}
QString OutputInterface::model() const
{
Q_D();
return d->model;
}
int OutputInterface::scale() const
{
Q_D();
return d->scale;
}
OutputInterface::SubPixel OutputInterface::subPixel() const
{
Q_D();
return d->subPixel;
}
OutputInterface::Transform OutputInterface::transform() const
{
Q_D();
return d->transform;
}
QList< OutputInterface::Mode > OutputInterface::modes() const
{
Q_D();
return d->modes;
}
void OutputInterface::setDpmsMode(OutputInterface::DpmsMode mode)
{
Q_D();
if (d->dpms.mode == mode) {
return;
}
d->dpms.mode = mode;
emit dpmsModeChanged();
}
void OutputInterface::setDpmsSupported(bool supported)
{
Q_D();
if (d->dpms.supported == supported) {
return;
}
d->dpms.supported = supported;
emit dpmsSupportedChanged();
}
OutputInterface::DpmsMode OutputInterface::dpmsMode() const
{
Q_D();
return d->dpms.mode;
}
bool OutputInterface::isDpmsSupported() const
{
Q_D();
return d->dpms.supported;
}
QVector<wl_resource *> OutputInterface::clientResources(ClientConnection *client) const
{
Q_D();
QVector<wl_resource *> ret;
for (auto it = d->resources.constBegin(), end = d->resources.constEnd(); it != end; ++it) {
if (wl_resource_get_client((*it).resource) == client->client()) {
ret << (*it).resource;
}
}
return ret;
}
bool OutputInterface::isEnabled() const
{
Q_D();
if (!d->dpms.supported) {
return true;
}
return d->dpms.mode == DpmsMode::On;
}
void OutputInterface::done() {
Q_D();
for (auto it = d->resources.constBegin(); it != d->resources.constEnd(); ++it) {
d->sendDone(*it);
}
}
OutputInterface *OutputInterface::get(wl_resource* native)
{
return Private::get(native);
}
OutputInterface::Private *OutputInterface::d_func() const
{
return reinterpret_cast<Private*>(d.data());
}
}