diff --git a/backends/kwayland/waylandoutput.cpp b/backends/kwayland/waylandoutput.cpp index faac87c..c205e94 100644 --- a/backends/kwayland/waylandoutput.cpp +++ b/backends/kwayland/waylandoutput.cpp @@ -1,196 +1,198 @@ /************************************************************************************* * Copyright 2014-2015 Sebastian Kügler * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Lesser General Public * * License as published by the Free Software Foundation; either * * version 2.1 of the License, or (at your option) any later version. * * * * This library is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *************************************************************************************/ #include "waylandoutput.h" #include "waylandbackend.h" #include "waylandconfig.h" #include "../utils.h" #include #include #include #include using namespace KScreen; WaylandOutput::WaylandOutput(quint32 id, WaylandConfig *parent) : QObject(parent) , m_id(id) , m_output(nullptr) { m_rotationMap = { {KWayland::Client::OutputDevice::Transform::Normal, KScreen::Output::None}, {KWayland::Client::OutputDevice::Transform::Rotated90, KScreen::Output::Right}, {KWayland::Client::OutputDevice::Transform::Rotated180, KScreen::Output::Inverted}, {KWayland::Client::OutputDevice::Transform::Rotated270, KScreen::Output::Left}, {KWayland::Client::OutputDevice::Transform::Flipped, KScreen::Output::None}, {KWayland::Client::OutputDevice::Transform::Flipped90, KScreen::Output::Right}, {KWayland::Client::OutputDevice::Transform::Flipped180, KScreen::Output::Inverted}, {KWayland::Client::OutputDevice::Transform::Flipped270, KScreen::Output::Left} }; } KScreen::Output::Rotation WaylandOutput::toKScreenRotation(const KWayland::Client::OutputDevice::Transform transform) const { auto it = m_rotationMap.constFind(transform); return it.value(); } KWayland::Client::OutputDevice::Transform WaylandOutput::toKWaylandTransform(const KScreen::Output::Rotation rotation) const { return m_rotationMap.key(rotation); } QString WaylandOutput::toKScreenModeId(int kwaylandmodeid) const { - if (!m_modeIdMap.values().contains(kwaylandmodeid)) { + auto it = std::find(m_modeIdMap.constBegin(), m_modeIdMap.constEnd(), kwaylandmodeid); + if (it == m_modeIdMap.constEnd()) { qCWarning(KSCREEN_WAYLAND) << "Invalid kwayland mode id:" << kwaylandmodeid << m_modeIdMap; + return QStringLiteral("invalid_mode_id"); } - return m_modeIdMap.key(kwaylandmodeid, QStringLiteral("invalid_mode_id")); + return it.key(); } int WaylandOutput::toKWaylandModeId(const QString &kscreenmodeid) const { if (!m_modeIdMap.contains(kscreenmodeid)) { qCWarning(KSCREEN_WAYLAND) << "Invalid kscreen mode id:" << kscreenmodeid << m_modeIdMap; } return m_modeIdMap.value(kscreenmodeid, -1); } WaylandOutput::~WaylandOutput() { } quint32 WaylandOutput::id() const { Q_ASSERT(m_output); return m_id; } bool WaylandOutput::enabled() const { return m_output != nullptr; } KWayland::Client::OutputDevice* WaylandOutput::outputDevice() const { return m_output; } void WaylandOutput::bindOutputDevice(KWayland::Client::Registry* registry, KWayland::Client::OutputDevice* op, quint32 name, quint32 version) { if (m_output == op) { return; } m_output = op; connect(m_output, &KWayland::Client::OutputDevice::done, this, [this]() { Q_EMIT complete(); connect(m_output, &KWayland::Client::OutputDevice::changed, this, &WaylandOutput::changed); }); m_output->setup(registry->bindOutputDevice(name, version)); } KScreen::OutputPtr WaylandOutput::toKScreenOutput() { KScreen::OutputPtr output(new KScreen::Output()); output->setId(m_id); updateKScreenOutput(output); return output; } void WaylandOutput::updateKScreenOutput(KScreen::OutputPtr &output) { // Initialize primary output output->setId(m_id); output->setEnabled(m_output->enabled() == KWayland::Client::OutputDevice::Enablement::Enabled); output->setConnected(true); output->setPrimary(true); // FIXME: wayland doesn't have the concept of a primary display output->setName(name()); // Physical size output->setSizeMm(m_output->physicalSize()); output->setPos(m_output->globalPosition()); output->setRotation(m_rotationMap[m_output->transform()]); KScreen::ModeList modeList; QStringList preferredModeIds; m_modeIdMap.clear(); QString currentModeId = QStringLiteral("-1"); Q_FOREACH (const KWayland::Client::OutputDevice::Mode &m, m_output->modes()) { KScreen::ModePtr mode(new KScreen::Mode()); const QString modename = modeName(m); QString modeid = QString::number(m.id); if (modeid.isEmpty()) { qCDebug(KSCREEN_WAYLAND) << "Could not create mode id from" << m.id << ", using" << modename << "instead."; modeid = modename; } - if (m_modeIdMap.keys().contains(modeid)) { + if (m_modeIdMap.contains(modeid)) { qCWarning(KSCREEN_WAYLAND) << "Mode id already in use:" << modeid; } mode->setId(modeid); // KWayland gives the refresh rate as int in mHz mode->setRefreshRate(m.refreshRate / 1000.0); mode->setSize(m.size); mode->setName(modename); if (m.flags.testFlag(KWayland::Client::OutputDevice::Mode::Flag::Current)) { currentModeId = modeid; } if (m.flags.testFlag(KWayland::Client::OutputDevice::Mode::Flag::Preferred)) { preferredModeIds << modeid; } // Update the kscreen => kwayland mode id translation map m_modeIdMap.insert(modeid, m.id); // Add to the modelist which gets set on the output modeList[modeid] = mode; } if (currentModeId == QLatin1String("-1")) { qCWarning(KSCREEN_WAYLAND) << "Could not find the current mode id" << modeList; } output->setCurrentModeId(currentModeId); output->setPreferredModes(preferredModeIds); output->setModes(modeList); output->setScale(m_output->scale()); output->setType(Utils::guessOutputType(m_output->model(), m_output->model())); } QString WaylandOutput::modeName(const KWayland::Client::OutputDevice::Mode &m) const { return QString::number(m.size.width()) + QLatin1Char('x') + QString::number(m.size.height()) + QLatin1Char('@') + QString::number(qRound(m.refreshRate/1000.0)); } QString WaylandOutput::name() const { Q_ASSERT(m_output); return QStringLiteral("%1 %2").arg(m_output->manufacturer(), m_output->model()); } QDebug operator<<(QDebug dbg, const WaylandOutput *output) { dbg << "WaylandOutput(Id:" << output->id() <<", Name:" << \ QString(output->outputDevice()->manufacturer() + QLatin1Char(' ') + \ output->outputDevice()->model()) << ")"; return dbg; } diff --git a/backends/qscreen/qscreenconfig.cpp b/backends/qscreen/qscreenconfig.cpp index 035501b..b689cf2 100644 --- a/backends/qscreen/qscreenconfig.cpp +++ b/backends/qscreen/qscreenconfig.cpp @@ -1,133 +1,131 @@ /************************************************************************************* * Copyright 2014 Sebastian Kügler * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Lesser General Public * * License as published by the Free Software Foundation; either * * version 2.1 of the License, or (at your option) any later version. * * * * This library is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *************************************************************************************/ #include "qscreenconfig.h" #include "qscreenoutput.h" #include "qscreenscreen.h" #include "qscreenbackend.h" #include #include #include #include using namespace KScreen; QScreenConfig::QScreenConfig(QObject *parent) : QObject(parent) , m_screen(new QScreenScreen(this)) , m_blockSignals(true) { foreach(const QScreen * qscreen, QGuiApplication::screens()) { screenAdded(qscreen); } m_blockSignals = false; connect(qApp, &QGuiApplication::screenAdded, this, &QScreenConfig::screenAdded); connect(qApp, &QGuiApplication::screenRemoved, this, &QScreenConfig::screenRemoved); } QScreenConfig::~QScreenConfig() { - foreach(auto output, m_outputMap.values()) { - delete output; - } + qDeleteAll(m_outputMap); } ConfigPtr QScreenConfig::toKScreenConfig() const { ConfigPtr config(new Config); config->setScreen(m_screen->toKScreenScreen()); updateKScreenConfig(config); return config; } int QScreenConfig::outputId(const QScreen *qscreen) { QList ids; - foreach(auto output, m_outputMap.values()) { + foreach(auto output, m_outputMap) { if (qscreen == output->qscreen()) { return output->id(); } } m_lastOutputId++; return m_lastOutputId; } void QScreenConfig::screenAdded(const QScreen *qscreen) { qCDebug(KSCREEN_QSCREEN) << "Screen added" << qscreen << qscreen->name(); QScreenOutput *qscreenoutput = new QScreenOutput(qscreen, this); qscreenoutput->setId(outputId(qscreen)); m_outputMap.insert(qscreenoutput->id(), qscreenoutput); if (!m_blockSignals) { Q_EMIT configChanged(toKScreenConfig()); } } void QScreenConfig::screenRemoved(QScreen *qscreen) { qCDebug(KSCREEN_QSCREEN) << "Screen removed" << qscreen << QGuiApplication::screens().count(); // Find output matching the QScreen object and remove it int removedOutputId = -1; - foreach(auto output, m_outputMap.values()) { + foreach(auto output, m_outputMap) { if (output->qscreen() == qscreen) { removedOutputId = output->id(); m_outputMap.remove(removedOutputId); delete output; } } Q_EMIT configChanged(toKScreenConfig()); } void QScreenConfig::updateKScreenConfig(ConfigPtr &config) const { KScreen::ScreenPtr screen = config->screen(); m_screen->updateKScreenScreen(screen); config->setScreen(screen); //Removing removed outputs KScreen::OutputList outputs = config->outputs(); Q_FOREACH(const KScreen::OutputPtr &output, outputs) { - if (!m_outputMap.keys().contains(output->id())) { + if (!m_outputMap.contains(output->id())) { config->removeOutput(output->id()); } } // Add KScreen::Outputs that aren't in the list yet, handle primaryOutput KScreen::OutputList kscreenOutputs = config->outputs(); - foreach(QScreenOutput *output, m_outputMap.values()) { + foreach(QScreenOutput *output, m_outputMap) { KScreen::OutputPtr kscreenOutput = kscreenOutputs[output->id()]; if (!kscreenOutput) { kscreenOutput = output->toKScreenOutput(); kscreenOutputs.insert(kscreenOutput->id(), kscreenOutput); } output->updateKScreenOutput(kscreenOutput); if (QGuiApplication::primaryScreen() == output->qscreen()) { config->setPrimaryOutput(kscreenOutput); } } config->setOutputs(kscreenOutputs); } QMap< int, QScreenOutput * > QScreenConfig::outputMap() const { return m_outputMap; } diff --git a/src/output.cpp b/src/output.cpp index 9a038a7..c36c3ef 100644 --- a/src/output.cpp +++ b/src/output.cpp @@ -1,611 +1,616 @@ /************************************************************************************* * Copyright (C) 2012 by Alejandro Fiestas Olivares * * Copyright (C) 2014 by Daniel Vrátil * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Lesser General Public * * License as published by the Free Software Foundation; either * * version 2.1 of the License, or (at your option) any later version. * * * * This library is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *************************************************************************************/ #include "output.h" #include "mode.h" #include "edid.h" #include "abstractbackend.h" #include "backendmanager_p.h" #include "debug_p.h" #include #include #include using namespace KScreen; class Q_DECL_HIDDEN Output::Private { public: Private(): id(0), type(Unknown), rotation(None), scale(1.0), connected(false), enabled(false), primary(false), edid(nullptr) {} Private(const Private &other): id(other.id), name(other.name), type(other.type), icon(other.icon), clones(other.clones), currentMode(other.currentMode), preferredMode(other.preferredMode), preferredModes(other.preferredModes), sizeMm(other.sizeMm), pos(other.pos), size(other.size), rotation(other.rotation), scale(other.scale), connected(other.connected), enabled(other.enabled), primary(other.primary), followPreferredMode(other.followPreferredMode) { Q_FOREACH (const ModePtr &otherMode, other.modeList) { modeList.insert(otherMode->id(), otherMode->clone()); } if (other.edid) { edid.reset(other.edid->clone()); } } QString biggestMode(const ModeList& modes) const; bool compareModeList(const ModeList& before, const ModeList& after); int id; QString name; Type type; QString icon; ModeList modeList; QList clones; QString currentMode; QString preferredMode; QStringList preferredModes; QSize sizeMm; QPoint pos; QSize size; Rotation rotation; qreal scale; bool connected; bool enabled; bool primary; bool followPreferredMode = false; mutable QScopedPointer edid; }; bool Output::Private::compareModeList(const ModeList& before, const ModeList &after) { - if (before.keys() != after.keys()) { + if (before.count() != after.count()) { return false; } - for (const QString &key : before.keys()) { - const auto mb = before.value(key); - const auto ma = after.value(key); + + for (auto itb = before.constBegin(); itb != before.constEnd(); ++itb) { + auto ita = after.constFind(itb.key()); + if (ita == after.constEnd()) { + return false; + } + const auto &mb = itb.value(); + const auto &ma = ita.value(); if (mb->id() != ma->id()) { return false; } if (mb->size() != ma->size()) { return false; } if (!qFuzzyCompare(mb->refreshRate(), ma->refreshRate())) { return false; } if (mb->name() != ma->name()) { return false; } } // They're the same return true; } QString Output::Private::biggestMode(const ModeList& modes) const { int area, total = 0; KScreen::ModePtr biggest; Q_FOREACH(const KScreen::ModePtr &mode, modes) { area = mode->size().width() * mode->size().height(); if (area < total) { continue; } if (area == total && mode->refreshRate() < biggest->refreshRate()) { continue; } if (area == total && mode->refreshRate() > biggest->refreshRate()) { biggest = mode; continue; } total = area; biggest = mode; } if (!biggest) { return QString(); } return biggest->id(); } Output::Output() : QObject(nullptr) , d(new Private()) { } Output::Output(Output::Private *dd) : QObject() , d(dd) { } Output::~Output() { delete d; } OutputPtr Output::clone() const { return OutputPtr(new Output(new Private(*d))); } int Output::id() const { return d->id; } void Output::setId(int id) { if (d->id == id) { return; } d->id = id; Q_EMIT outputChanged(); } QString Output::name() const { return d->name; } void Output::setName(const QString& name) { if (d->name == name) { return; } d->name = name; Q_EMIT outputChanged(); } Output::Type Output::type() const { return d->type; } void Output::setType(Type type) { if (d->type == type) { return; } d->type = type; Q_EMIT outputChanged(); } QString Output::icon() const { return d->icon; } void Output::setIcon(const QString& icon) { if (d->icon == icon) { return; } d->icon = icon; Q_EMIT outputChanged(); } ModePtr Output::mode(const QString& id) const { if (!d->modeList.contains(id)) { return ModePtr(); } return d->modeList[id]; } ModeList Output::modes() const { return d->modeList; } void Output::setModes(const ModeList &modes) { bool changed = !d->compareModeList(d->modeList, modes); d->modeList = modes; if (changed) { emit modesChanged(); emit outputChanged(); } } QString Output::currentModeId() const { return d->currentMode; } void Output::setCurrentModeId(const QString& mode) { if (d->currentMode == mode) { return; } d->currentMode = mode; Q_EMIT currentModeIdChanged(); } ModePtr Output::currentMode() const { return d->modeList.value(d->currentMode); } void Output::setPreferredModes(const QStringList &modes) { d->preferredMode = QString(); d->preferredModes = modes; } QStringList Output::preferredModes() const { return d->preferredModes; } QString Output::preferredModeId() const { if (!d->preferredMode.isEmpty()) { return d->preferredMode; } if (d->preferredModes.isEmpty()) { return d->biggestMode(modes()); } int area, total = 0; KScreen::ModePtr biggest; KScreen::ModePtr candidateMode; Q_FOREACH(const QString &modeId, d->preferredModes) { candidateMode = mode(modeId); area = candidateMode->size().width() * candidateMode->size().height(); if (area < total) { continue; } if (area == total && biggest && candidateMode->refreshRate() < biggest->refreshRate()) { continue; } if (area == total && biggest && candidateMode->refreshRate() > biggest->refreshRate()) { biggest = candidateMode; continue; } total = area; biggest = candidateMode; } Q_ASSERT_X(biggest, "preferredModeId", "biggest mode must exist"); d->preferredMode = biggest->id(); return d->preferredMode; } ModePtr Output::preferredMode() const { return d->modeList.value(preferredModeId()); } QPoint Output::pos() const { return d->pos; } void Output::setPos(const QPoint& pos) { if (d->pos == pos) { return; } d->pos = pos; Q_EMIT posChanged(); } QSize Output::size() const { return d->size; } void Output::setSize(const QSize& size) { if (d->size == size) { return; } d->size = size; Q_EMIT sizeChanged(); } Output::Rotation Output::rotation() const { return d->rotation; } void Output::setRotation(Output::Rotation rotation) { if (d->rotation == rotation) { return; } d->rotation = rotation; Q_EMIT rotationChanged(); } qreal Output::scale() const { return d->scale; } void Output::setScale(qreal factor) { if (qFuzzyCompare(d->scale, factor)) { return; } d->scale = factor; emit scaleChanged(); } bool Output::isConnected() const { return d->connected; } void Output::setConnected(bool connected) { if (d->connected == connected) { return; } d->connected = connected; Q_EMIT isConnectedChanged(); } bool Output::isEnabled() const { return d->enabled; } void Output::setEnabled(bool enabled) { if (d->enabled == enabled) { return; } d->enabled = enabled; Q_EMIT isEnabledChanged(); } bool Output::isPrimary() const { return d->primary; } void Output::setPrimary(bool primary) { if (d->primary == primary) { return; } d->primary = primary; Q_EMIT isPrimaryChanged(); } QList Output::clones() const { return d->clones; } void Output::setClones(QList outputlist) { if (d->clones == outputlist) { return; } d->clones = outputlist; Q_EMIT clonesChanged(); } void Output::setEdid(const QByteArray& rawData) { Q_ASSERT(d->edid.isNull()); d->edid.reset(new Edid(rawData)); } Edid *Output::edid() const { return d->edid.data(); } QSize Output::sizeMm() const { return d->sizeMm; } void Output::setSizeMm(const QSize &size) { d->sizeMm = size; } bool KScreen::Output::followPreferredMode() const { return d->followPreferredMode; } void KScreen::Output::setFollowPreferredMode(bool follow) { if (follow != d->followPreferredMode) { d->followPreferredMode = follow; Q_EMIT followPreferredModeChanged(follow); } } QRect Output::geometry() const { if (!currentMode()) { return QRect(); } // We can't use QRect(d->pos, d->size), because d->size does not reflect the // actual rotation() set by caller, it's only updated when we get update from // KScreen, but not when user changes mode or rotation manually QSize size = currentMode()->size() / d->scale; if (!isHorizontal()) { size = size.transposed(); } return QRect(d->pos, size); } void Output::apply(const OutputPtr& other) { typedef void (KScreen::Output::*ChangeSignal)(); QList changes; // We block all signals, and emit them only after we have set up everything // This is necessary in order to prevent clients from accessing inconsistent // outputs from intermediate change signals const bool keepBlocked = signalsBlocked(); blockSignals(true); if (d->name != other->d->name) { changes << &Output::outputChanged; setName(other->d->name); } if (d->type != other->d->type) { changes << &Output::outputChanged; setType(other->d->type); } if (d->icon != other->d->icon) { changes << &Output::outputChanged; setIcon(other->d->icon); } if (d->pos != other->d->pos) { changes << &Output::posChanged; setPos(other->pos()); } if (d->rotation != other->d->rotation) { changes << &Output::rotationChanged; setRotation(other->d->rotation); } if (!qFuzzyCompare(d->scale, other->d->scale)) { changes << &Output::scaleChanged; setScale(other->d->scale); } if (d->currentMode != other->d->currentMode) { changes << &Output::currentModeIdChanged; setCurrentModeId(other->d->currentMode); } if (d->connected != other->d->connected) { changes << &Output::isConnectedChanged; setConnected(other->d->connected); } if (d->enabled != other->d->enabled) { changes << &Output::isEnabledChanged; setEnabled(other->d->enabled); } if (d->primary != other->d->primary) { changes << &Output::isPrimaryChanged; setPrimary(other->d->primary); } if (d->clones != other->d->clones) { changes << &Output::clonesChanged; setClones(other->d->clones);; } if (!d->compareModeList(d->modeList, other->d->modeList)) { changes << &Output::outputChanged; } setPreferredModes(other->d->preferredModes); ModeList modes; Q_FOREACH (const ModePtr &otherMode, other->modes()) { modes.insert(otherMode->id(), otherMode->clone()); } setModes(modes); // Non-notifyable changes if (other->d->edid) { d->edid.reset(other->d->edid->clone()); } blockSignals(keepBlocked); while (!changes.isEmpty()) { const ChangeSignal &sig = changes.first(); Q_EMIT (this->*sig)(); changes.removeAll(sig); } } QDebug operator<<(QDebug dbg, const KScreen::OutputPtr &output) { if(output) { dbg << "KScreen::Output(" << output->id() << " " << output->name() << (output->isConnected() ? "connected" : "disconnected") << (output->isEnabled() ? "enabled" : "disabled") << (output->isPrimary() ? "primary" : "") << "pos:" << output->pos() << "res:" << output->size() << "modeId:" << output->currentModeId() << "scale:" << output->scale() << "followPreferredMode:" << output->followPreferredMode() << ")"; } else { dbg << "KScreen::Output(NULL)"; } return dbg; } diff --git a/tests/kwayland/waylandconfigreader.cpp b/tests/kwayland/waylandconfigreader.cpp index 3837c61..4a8fdb8 100644 --- a/tests/kwayland/waylandconfigreader.cpp +++ b/tests/kwayland/waylandconfigreader.cpp @@ -1,253 +1,255 @@ /************************************************************************************* * Copyright 2014-2015 Sebastian Kügler * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Lesser General Public * * License as published by the Free Software Foundation; either * * version 2.1 of the License, or (at your option) any later version. * * * * This library is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *************************************************************************************/ #include "waylandconfigreader.h" #include #include #include #include #include #include #include "edid.h" using namespace KScreen; static QList s_outputIds; void WaylandConfigReader::outputsFromConfig(const QString& configfile, KWayland::Server::Display* display, QList< KWayland::Server::OutputDeviceInterface* >& outputs) { qDebug() << "Loading server from" << configfile; QFile file(configfile); file.open(QIODevice::ReadOnly); QJsonDocument jsonDoc = QJsonDocument::fromJson(file.readAll()); QJsonObject json = jsonDoc.object(); QJsonArray omap = json[QStringLiteral("outputs")].toArray(); Q_FOREACH(const QJsonValue &value, omap) { const QVariantMap &output = value.toObject().toVariantMap(); if (output[QStringLiteral("connected")].toBool()) { outputs << createOutputDevice(output, display); //qDebug() << "new Output created: " << output["name"].toString(); } else { //qDebug() << "disconnected Output" << output["name"].toString(); } } auto outpus = WaylandConfigReader::createOutputs(display, outputs); s_outputIds.clear(); } OutputDeviceInterface* WaylandConfigReader::createOutputDevice(const QVariantMap& outputConfig, KWayland::Server::Display *display) { KWayland::Server::OutputDeviceInterface *outputdevice = display->createOutputDevice(display); QByteArray data = QByteArray::fromBase64(outputConfig[QStringLiteral("edid")].toByteArray()); outputdevice->setEdid(data); Edid edid(data, display); // qDebug() << "EDID Info: "; if (edid.isValid()) { // qDebug() << "\tDevice ID: " << edid.deviceId(); // qDebug() << "\tName: " << edid.name(); // qDebug() << "\tVendor: " << edid.vendor(); // qDebug() << "\tSerial: " << edid.serial(); // qDebug() << "\tEISA ID: " << edid.eisaId(); // qDebug() << "\tHash: " << edid.hash(); // qDebug() << "\tWidth (mm): " << edid.width(); // qDebug() << "\tHeight (mm): " << edid.height(); // qDebug() << "\tGamma: " << edid.gamma(); // qDebug() << "\tRed: " << edid.red(); // qDebug() << "\tGreen: " << edid.green(); // qDebug() << "\tBlue: " << edid.blue(); // qDebug() << "\tWhite: " << edid.white(); outputdevice->setPhysicalSize(QSize(edid.width() * 10, edid.height() * 10)); outputdevice->setManufacturer(edid.vendor()); outputdevice->setModel(edid.name()); } else { outputdevice->setPhysicalSize(sizeFromJson(outputConfig[QStringLiteral("sizeMM")])); outputdevice->setManufacturer(outputConfig[QStringLiteral("manufacturer")].toString()); outputdevice->setModel(outputConfig[QStringLiteral("model")].toString()); } auto uuid = QUuid::createUuid().toByteArray(); auto _id = outputConfig[QStringLiteral("id")].toInt(); if (_id) { uuid = QString::number(_id).toLocal8Bit(); } outputdevice->setUuid(uuid); const QMap transformMap = { {0, KWayland::Server::OutputDeviceInterface::Transform::Normal}, {1, KWayland::Server::OutputDeviceInterface::Transform::Normal}, {2, KWayland::Server::OutputDeviceInterface::Transform::Rotated270}, {3, KWayland::Server::OutputDeviceInterface::Transform::Rotated180}, {4, KWayland::Server::OutputDeviceInterface::Transform::Rotated90} }; outputdevice->setTransform(transformMap[outputConfig[QStringLiteral("rotation")].toInt()]); int currentModeId = outputConfig[QStringLiteral("currentModeId")].toInt(); QVariantList preferredModes = outputConfig[QStringLiteral("preferredModes")].toList(); int mode_id = 0; Q_FOREACH (const QVariant &_mode, outputConfig[QStringLiteral("modes")].toList()) { mode_id++; const QVariantMap &mode = _mode.toMap(); OutputDeviceInterface::Mode m0; const QSize _size = sizeFromJson(mode[QStringLiteral("size")]); - if (mode.keys().contains(QStringLiteral("refreshRate"))) { - m0.refreshRate = qRound(mode[QStringLiteral("refreshRate")].toReal() * 1000); // config has it in Hz + auto refreshRateIt = mode.constFind(QStringLiteral("refreshRate")); + if (refreshRateIt != mode.constEnd()) { + m0.refreshRate = qRound(refreshRateIt->toReal() * 1000); // config has it in Hz } bool isCurrent = currentModeId == mode[QStringLiteral("id")].toInt(); bool isPreferred = preferredModes.contains(mode[QStringLiteral("id")]); OutputDeviceInterface::ModeFlags flags; if (isCurrent && isPreferred) { flags = OutputDeviceInterface::ModeFlags(OutputDeviceInterface::ModeFlag::Current | OutputDeviceInterface::ModeFlag::Preferred); } else if (isCurrent) { flags = OutputDeviceInterface::ModeFlags(OutputDeviceInterface::ModeFlag::Current); } else if (isPreferred) { flags = OutputDeviceInterface::ModeFlags(OutputDeviceInterface::ModeFlag::Preferred); } - if (mode.keys().contains(QLatin1String("id"))) { - m0.id = mode[QStringLiteral("id")].toInt(); + auto idIt = mode.constFind(QStringLiteral("id")); + if (idIt != mode.constEnd()) { + m0.id = idIt->toInt(); } else { m0.id = mode_id; } m0.size = _size; m0.flags = flags; outputdevice->addMode(m0); if (isCurrent) { outputdevice->setCurrentMode(m0.id); } } outputdevice->setGlobalPosition(pointFromJson(outputConfig[QStringLiteral("pos")])); outputdevice->setEnabled(outputConfig[QStringLiteral("enabled")].toBool() ? OutputDeviceInterface::Enablement::Enabled : OutputDeviceInterface::Enablement::Disabled); outputdevice->create(); return outputdevice; } QList KScreen::WaylandConfigReader::createOutputs(KWayland::Server::Display* display, QList& outputdevices) { const QMap transformMap = { {KWayland::Server::OutputDeviceInterface::Transform::Normal, KWayland::Server::OutputInterface::Transform::Normal}, {KWayland::Server::OutputDeviceInterface::Transform::Rotated270, KWayland::Server::OutputInterface::Transform::Rotated270}, {KWayland::Server::OutputDeviceInterface::Transform::Rotated180, KWayland::Server::OutputInterface::Transform::Rotated180}, {KWayland::Server::OutputDeviceInterface::Transform::Rotated90, KWayland::Server::OutputInterface::Transform::Rotated90}, }; QList outputs; Q_FOREACH (const auto outputdevice, outputdevices) { qDebug() << "New Output!"; KWayland::Server::OutputInterface *output = display->createOutput(display); // Sync properties from outputdevice to the newly created output interface output->setManufacturer(outputdevice->manufacturer()); output->setModel(outputdevice->model()); //output->setUuid(outputdevice->uuid()); Q_FOREACH (const auto mode, outputdevice->modes()) { bool isCurrent = mode.flags.testFlag(OutputDeviceInterface::ModeFlag::Current); bool isPreferred = mode.flags.testFlag(OutputDeviceInterface::ModeFlag::Current); OutputInterface::ModeFlags flags; if (isPreferred && isCurrent) { flags = OutputInterface::ModeFlags(OutputInterface::ModeFlag::Current | OutputInterface::ModeFlag::Preferred); } else if (isCurrent) { flags = OutputInterface::ModeFlags(OutputInterface::ModeFlag::Current); } else if (isPreferred) { flags = OutputInterface::ModeFlags(OutputInterface::ModeFlag::Preferred); } OutputInterface::Mode m0; m0.size = mode.size; output->addMode(m0.size, m0.flags, m0.refreshRate); if (isCurrent) { output->setCurrentMode(m0.size, m0.refreshRate); } //qDebug() << "mode added:" << m0.size << m0.refreshRate << isCurrent; } output->setGlobalPosition(outputdevice->globalPosition()); output->setPhysicalSize(outputdevice->physicalSize()); output->setTransform(transformMap.value(outputdevice->transform())); output->setDpmsSupported(true); output->setDpmsMode(OutputInterface::DpmsMode::On); QObject::connect(output, &OutputInterface::dpmsModeRequested, [] (KWayland::Server::OutputInterface::DpmsMode requestedMode) { Q_UNUSED(requestedMode); // FIXME: make sure this happens in the scope of an object! qDebug() << "DPMS Mode change requested"; }); output->create(); outputs << output; } return outputs; } QSize WaylandConfigReader::sizeFromJson(const QVariant& data) { QVariantMap map = data.toMap(); QSize size; size.setWidth(map[QStringLiteral("width")].toInt()); size.setHeight(map[QStringLiteral("height")].toInt()); return size; } QPoint WaylandConfigReader::pointFromJson(const QVariant& data) { QVariantMap map = data.toMap(); QPoint point; point.setX(map[QStringLiteral("x")].toInt()); point.setY(map[QStringLiteral("y")].toInt()); return point; } QRect WaylandConfigReader::rectFromJson(const QVariant& data) { QRect rect; rect.setSize(WaylandConfigReader::sizeFromJson(data)); rect.setBottomLeft(WaylandConfigReader::pointFromJson(data)); return rect; } diff --git a/tests/kwayland/waylandtestserver.cpp b/tests/kwayland/waylandtestserver.cpp index 6f50147..019250e 100644 --- a/tests/kwayland/waylandtestserver.cpp +++ b/tests/kwayland/waylandtestserver.cpp @@ -1,200 +1,201 @@ /************************************************************************************* * Copyright 2014-2015 Sebastian Kügler * * * * This library is free software; you can redistribute it and/or * * modify it under the terms of the GNU Lesser General Public * * License as published by the Free Software Foundation; either * * version 2.1 of the License, or (at your option) any later version. * * * * This library is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this library; if not, write to the Free Software * * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *************************************************************************************/ #include "waylandtestserver.h" #include "waylandconfigreader.h" #include #include #include #include #include #include #include "../src/edid.h" Q_LOGGING_CATEGORY(KSCREEN_WAYLAND_TESTSERVER, "kscreen.kwayland.testserver") using namespace KScreen; using namespace KWayland::Server; WaylandTestServer::WaylandTestServer(QObject *parent) : QObject(parent) , m_configFile(QLatin1String(TEST_DATA) + QLatin1String("default.json")) , m_display(nullptr) , m_outputManagement(nullptr) , m_dpmsManager(nullptr) , m_suspendChanges(false) , m_waiting(nullptr) { } WaylandTestServer::~WaylandTestServer() { stop(); qCDebug(KSCREEN_WAYLAND_TESTSERVER) << "Wayland server shut down."; } void WaylandTestServer::start() { using namespace KWayland::Server; delete m_display; m_display = new KWayland::Server::Display(this); if (qgetenv("WAYLAND_DISPLAY").isEmpty()) { m_display->setSocketName(s_socketName); } else { m_display->setSocketName(QString::fromLatin1(qgetenv("WAYLAND_DISPLAY"))); } m_display->start(); auto manager = m_display->createDpmsManager(); manager->create(); m_outputManagement = m_display->createOutputManagement(); m_outputManagement->create(); connect(m_outputManagement, &OutputManagementInterface::configurationChangeRequested, this, &WaylandTestServer::configurationChangeRequested); KScreen::WaylandConfigReader::outputsFromConfig(m_configFile, m_display, m_outputs); qCDebug(KSCREEN_WAYLAND_TESTSERVER) << QStringLiteral("export WAYLAND_DISPLAY=") + m_display->socketName(); qCDebug(KSCREEN_WAYLAND_TESTSERVER) << QStringLiteral("You can specify the WAYLAND_DISPLAY for this server by exporting it in the environment"); //showOutputs(); } void WaylandTestServer::stop() { Q_FOREACH (const auto &o, m_outputs) { delete o; } m_outputs.clear(); // actually stop the Wayland server delete m_display; m_display = nullptr; } KWayland::Server::Display* WaylandTestServer::display() { return m_display; } void WaylandTestServer::setConfig(const QString& configfile) { qCDebug(KSCREEN_WAYLAND_TESTSERVER) << "Creating Wayland server from " << configfile; m_configFile = configfile; } int WaylandTestServer::outputCount() const { return m_outputs.count(); } QList WaylandTestServer::outputs() const { return m_outputs; } void WaylandTestServer::suspendChanges(bool suspend) { if (m_suspendChanges == suspend) { return; } m_suspendChanges = suspend; if (!suspend && m_waiting) { m_waiting->setApplied(); m_waiting = nullptr; Q_EMIT configChanged(); } } void WaylandTestServer::configurationChangeRequested(KWayland::Server::OutputConfigurationInterface* configurationInterface) { qCDebug(KSCREEN_WAYLAND_TESTSERVER) << "Server received change request, changes:" << configurationInterface->changes().count(); Q_EMIT configReceived(); auto changes = configurationInterface->changes(); - Q_FOREACH (const auto &outputdevice, changes.keys()) { - auto c = changes[outputdevice]; + for (auto it = changes.constBegin(); it != changes.constEnd(); ++it) { + auto outputdevice = it.key(); + auto c = it.value(); if (c->enabledChanged()) { qCDebug(KSCREEN_WAYLAND_TESTSERVER) << "Setting enabled:"; outputdevice->setEnabled(c->enabled()); } if (c->modeChanged()) { qCDebug(KSCREEN_WAYLAND_TESTSERVER) << "Setting new mode:" << c->mode() << modeString(outputdevice, c->mode()); outputdevice->setCurrentMode(c->mode()); } if (c->transformChanged()) { qCDebug(KSCREEN_WAYLAND_TESTSERVER) << "Server setting transform: " << (int)(c->transform()); outputdevice->setTransform(c->transform()); } if (c->positionChanged()) { qCDebug(KSCREEN_WAYLAND_TESTSERVER) << "Server setting position: " << c->position(); outputdevice->setGlobalPosition(c->position()); } if (c->scaleChanged()) { qCDebug(KSCREEN_WAYLAND_TESTSERVER) << "Setting scale:" << c->scale(); outputdevice->setScale(c->scale()); } } if (m_suspendChanges) { Q_ASSERT(!m_waiting); m_waiting = configurationInterface; return; } configurationInterface->setApplied(); //showOutputs(); Q_EMIT configChanged(); } void WaylandTestServer::showOutputs() { qCDebug(KSCREEN_WAYLAND_TESTSERVER) << "******** Wayland server running: " << m_outputs.count() << " outputs. ********"; foreach (auto o, m_outputs) { bool enabled = (o->enabled() == KWayland::Server::OutputDeviceInterface::Enablement::Enabled); qCDebug(KSCREEN_WAYLAND_TESTSERVER) << " * Output id: " << o->uuid(); qCDebug(KSCREEN_WAYLAND_TESTSERVER) << " Enabled: " << (enabled ? "enabled" : "disabled"); qCDebug(KSCREEN_WAYLAND_TESTSERVER) << " Name: " << QStringLiteral("%2-%3").arg(o->manufacturer(), o->model()); qCDebug(KSCREEN_WAYLAND_TESTSERVER) << " Mode: " << modeString(o, o->currentModeId()); qCDebug(KSCREEN_WAYLAND_TESTSERVER) << " Pos: " << o->globalPosition(); qCDebug(KSCREEN_WAYLAND_TESTSERVER) << " Edid: " << o->edid(); // << o->currentMode().size(); } qCDebug(KSCREEN_WAYLAND_TESTSERVER) << "******************************************************"; } QString WaylandTestServer::modeString(KWayland::Server::OutputDeviceInterface* outputdevice, int mid) { QString s; QString ids; int _i = 0; Q_FOREACH (const auto &_m, outputdevice->modes()) { _i++; if (_i < 6) { ids.append(QString::number(_m.id) + QLatin1String(", ")); } else { ids.append(QLatin1Char('.')); } if (_m.id == mid) { s = QStringLiteral("%1x%2 @%3").arg(QString::number(_m.size.width()), \ QString::number(_m.size.height()), QString::number(_m.refreshRate)); } } return QStringLiteral("[%1] %2 (%4 modes: %3)").arg(QString::number(mid), s, ids, QString::number(outputdevice->modes().count())); }