File indexing completed on 2024-05-05 17:39:36

0001 /*
0002  *  SPDX-FileCopyrightText: 2014-2015 Sebastian Kügler <sebas@kde.org>
0003  *  SPDX-FileCopyrightText: 2021 Méven Car <meven.car@enioka.com>
0004  *
0005  *  SPDX-License-Identifier: LGPL-2.1-or-later
0006  */
0007 #include "waylandoutputdevice.h"
0008 #include "waylandoutputmanagement.h"
0009 
0010 #include "kscreen_kwayland_logging.h"
0011 
0012 #include "../utils.h"
0013 
0014 #include <mode.h>
0015 #include <output.h>
0016 
0017 #include <wayland-server-protocol.h>
0018 
0019 #include <utility>
0020 
0021 using namespace KScreen;
0022 
0023 WaylandOutputDevice::WaylandOutputDevice(int id)
0024     : QObject()
0025     , kde_output_device_v2()
0026     , m_id(id)
0027 {
0028 }
0029 
0030 WaylandOutputDevice::~WaylandOutputDevice()
0031 {
0032     qDeleteAll(m_modes);
0033 
0034     kde_output_device_v2_destroy(object());
0035 }
0036 
0037 void WaylandOutputDevice::kde_output_device_v2_geometry(int32_t x,
0038                                                         int32_t y,
0039                                                         int32_t physical_width,
0040                                                         int32_t physical_height,
0041                                                         int32_t subpixel,
0042                                                         const QString &make,
0043                                                         const QString &model,
0044                                                         int32_t transform)
0045 {
0046     m_pos = QPoint(x, y);
0047     m_physicalSize = QSize(physical_width, physical_height);
0048     m_subpixel = subpixel;
0049     m_manufacturer = make;
0050     m_model = model;
0051     m_transform = transform;
0052 }
0053 
0054 void WaylandOutputDevice::kde_output_device_v2_current_mode(struct ::kde_output_device_mode_v2 *mode)
0055 {
0056     auto m = WaylandOutputDeviceMode::get(mode);
0057 
0058     if (*m == *m_mode) {
0059         // unchanged
0060         return;
0061     }
0062     m_mode = m;
0063 }
0064 
0065 void WaylandOutputDevice::kde_output_device_v2_mode(struct ::kde_output_device_mode_v2 *mode)
0066 {
0067     WaylandOutputDeviceMode *m = new WaylandOutputDeviceMode(mode);
0068     // last mode sent is the current one
0069     m_mode = m;
0070     m_modes.append(m);
0071 
0072     connect(m, &WaylandOutputDeviceMode::removed, this, [this, m]() {
0073         m_modes.removeOne(m);
0074         if (m_mode == m) {
0075             if (!m_modes.isEmpty()) {
0076                 m_mode = m_modes.first();
0077             } else {
0078                 // was last mode
0079                 qFatal("KWaylandBackend: no output modes available anymore, this seems like a compositor bug");
0080             }
0081         }
0082 
0083         delete m;
0084     });
0085 }
0086 
0087 OutputPtr WaylandOutputDevice::toKScreenOutput()
0088 {
0089     OutputPtr output(new Output());
0090     output->setId(m_id);
0091     updateKScreenOutput(output);
0092     return output;
0093 }
0094 
0095 Output::Rotation toKScreenRotation(int32_t transform)
0096 {
0097     switch (transform) {
0098     case WL_OUTPUT_TRANSFORM_NORMAL:
0099         return Output::None;
0100     case WL_OUTPUT_TRANSFORM_90:
0101         return Output::Left;
0102     case WL_OUTPUT_TRANSFORM_180:
0103         return Output::Inverted;
0104     case WL_OUTPUT_TRANSFORM_270:
0105         return Output::Right;
0106     case WL_OUTPUT_TRANSFORM_FLIPPED:
0107         qCWarning(KSCREEN_WAYLAND) << "flipped transform is unsupported by kscreen";
0108         return Output::None;
0109     case WL_OUTPUT_TRANSFORM_FLIPPED_90:
0110         qCWarning(KSCREEN_WAYLAND) << "flipped-90 transform is unsupported by kscreen";
0111         return Output::Left;
0112     case WL_OUTPUT_TRANSFORM_FLIPPED_180:
0113         qCWarning(KSCREEN_WAYLAND) << "flipped-180 transform is unsupported by kscreen";
0114         return Output::Inverted;
0115     case WL_OUTPUT_TRANSFORM_FLIPPED_270:
0116         qCWarning(KSCREEN_WAYLAND) << "flipped-270 transform is unsupported by kscreen";
0117         return Output::Right;
0118     default:
0119         Q_UNREACHABLE();
0120     }
0121 }
0122 
0123 wl_output_transform toKWaylandTransform(const Output::Rotation rotation)
0124 {
0125     switch (rotation) {
0126     case Output::None:
0127         return WL_OUTPUT_TRANSFORM_NORMAL;
0128     case Output::Left:
0129         return WL_OUTPUT_TRANSFORM_90;
0130     case Output::Inverted:
0131         return WL_OUTPUT_TRANSFORM_180;
0132     case Output::Right:
0133         return WL_OUTPUT_TRANSFORM_270;
0134     default:
0135         Q_UNREACHABLE();
0136     }
0137 }
0138 
0139 void KScreen::WaylandOutputDevice::updateKScreenModes(OutputPtr &output)
0140 {
0141     ModeList modeList;
0142     QStringList preferredModeIds;
0143     QString currentModeId = QStringLiteral("-1");
0144     int modeId = 0;
0145 
0146     for (const WaylandOutputDeviceMode *wlMode : std::as_const(m_modes)) {
0147         ModePtr mode(new Mode());
0148 
0149         const QString modeIdStr = QString::number(modeId);
0150         // KWayland gives the refresh rate as int in mHz
0151         mode->setId(modeIdStr);
0152         mode->setRefreshRate(wlMode->refreshRate() / 1000.0);
0153         mode->setSize(wlMode->size());
0154         mode->setName(modeName(wlMode));
0155 
0156         if (m_mode == wlMode) {
0157             currentModeId = modeIdStr;
0158         }
0159 
0160         if (wlMode->preferred()) {
0161             preferredModeIds << modeIdStr;
0162         }
0163 
0164         // Add to the modelist which gets set on the output
0165         modeList[modeIdStr] = mode;
0166         modeId++;
0167     }
0168     output->setCurrentModeId(currentModeId);
0169     output->setPreferredModes(preferredModeIds);
0170     output->setModes(modeList);
0171 }
0172 
0173 void WaylandOutputDevice::updateKScreenOutput(OutputPtr &output)
0174 {
0175     // Initialize primary output
0176     output->setId(m_id);
0177     output->setEnabled(enabled());
0178     output->setConnected(true);
0179     output->setName(name());
0180     output->setSizeMm(m_physicalSize);
0181     output->setPos(m_pos);
0182     output->setRotation(toKScreenRotation(m_transform));
0183     if (!output->edid()) {
0184         output->setEdid(m_edid);
0185     }
0186 
0187     QSize currentSize = m_mode->size();
0188     output->setSize(output->isHorizontal() ? currentSize : currentSize.transposed());
0189     output->setScale(m_factor);
0190     output->setType(Utils::guessOutputType(m_outputName, m_outputName));
0191     output->setCapabilities(static_cast<Output::Capabilities>(static_cast<uint32_t>(m_flags)));
0192     output->setOverscan(m_overscan);
0193     output->setVrrPolicy(static_cast<Output::VrrPolicy>(m_vrr_policy));
0194     output->setRgbRange(static_cast<Output::RgbRange>(m_rgbRange));
0195 
0196     updateKScreenModes(output);
0197 }
0198 
0199 QString WaylandOutputDevice::modeId() const
0200 {
0201     return QString::number(m_modes.indexOf(m_mode));
0202 }
0203 
0204 WaylandOutputDeviceMode *WaylandOutputDevice::deviceModeFromId(const int modeId) const
0205 {
0206     return m_modes.at(modeId);
0207 }
0208 
0209 bool WaylandOutputDevice::setWlConfig(WaylandOutputConfiguration *wlConfig, const KScreen::OutputPtr &output)
0210 {
0211     bool changed = false;
0212 
0213     // enabled?
0214     if (enabled() != output->isEnabled()) {
0215         changed = true;
0216         wlConfig->enable(object(), output->isEnabled());
0217     }
0218 
0219     // position
0220     if (globalPosition() != output->pos()) {
0221         changed = true;
0222         wlConfig->position(object(), output->pos().x(), output->pos().y());
0223     }
0224 
0225     // scale
0226     if (!qFuzzyCompare(scale(), output->scale())) {
0227         changed = true;
0228         wlConfig->scale(object(), wl_fixed_from_double(output->scale()));
0229     }
0230 
0231     // rotation
0232     if (toKScreenRotation(m_transform) != output->rotation()) {
0233         changed = true;
0234         wlConfig->transform(object(), toKWaylandTransform(output->rotation()));
0235     }
0236 
0237     // mode
0238     const ModePtr mode = output->currentMode();
0239     if (mode->size() != pixelSize() || mode->refreshRate() != refreshRate()) {
0240         bool toIntOk;
0241         int modeId = mode->id().toInt(&toIntOk);
0242         Q_ASSERT(toIntOk);
0243 
0244         changed = true;
0245         wlConfig->mode(object(), deviceModeFromId(modeId)->object());
0246     }
0247 
0248     // overscan
0249     if ((output->capabilities() & Output::Capability::Overscan) && overscan() != output->overscan()) {
0250         wlConfig->overscan(object(), output->overscan());
0251         changed = true;
0252     }
0253 
0254     // vrr
0255     if ((output->capabilities() & Output::Capability::Vrr) && vrrPolicy() != static_cast<uint32_t>(output->vrrPolicy())) {
0256         wlConfig->set_vrr_policy(object(), static_cast<uint32_t>(output->vrrPolicy()));
0257         changed = true;
0258     }
0259 
0260     if ((output->capabilities() & Output::Capability::RgbRange) && rgbRange() != static_cast<uint32_t>(output->rgbRange())) {
0261         wlConfig->set_rgb_range(object(), static_cast<uint32_t>(output->rgbRange()));
0262         changed = true;
0263     }
0264     if (output->priority() != m_index) {
0265         changed = true;
0266     }
0267     // always send all outputs
0268     if (kde_output_configuration_v2_get_version(wlConfig->object()) >= KDE_OUTPUT_CONFIGURATION_V2_SET_PRIORITY_SINCE_VERSION) {
0269         wlConfig->set_priority(object(), output->priority());
0270     }
0271 
0272     return changed;
0273 }
0274 
0275 QString WaylandOutputDevice::modeName(const WaylandOutputDeviceMode *m) const
0276 {
0277     return QString::number(m->size().width()) + QLatin1Char('x') + QString::number(m->size().height()) + QLatin1Char('@')
0278         + QString::number(qRound(m->refreshRate() / 1000.0));
0279 }
0280 
0281 QString WaylandOutputDevice::name() const
0282 {
0283     return m_outputName;
0284 }
0285 
0286 QDebug operator<<(QDebug dbg, const WaylandOutputDevice *output)
0287 {
0288     dbg << "WaylandOutput(Id:" << output->id() << ", Name:" << QString(output->manufacturer() + QLatin1Char(' ') + output->model()) << ")";
0289     return dbg;
0290 }
0291 
0292 void WaylandOutputDevice::setIndex(uint32_t index)
0293 {
0294     m_index = index;
0295 }
0296 
0297 uint32_t WaylandOutputDevice::index() const
0298 {
0299     return m_index;
0300 }
0301 
0302 void WaylandOutputDevice::kde_output_device_v2_done()
0303 {
0304     Q_EMIT done();
0305 }
0306 
0307 void WaylandOutputDevice::kde_output_device_v2_scale(wl_fixed_t factor)
0308 {
0309     const double factorAsDouble = wl_fixed_to_double(factor);
0310 
0311     // the fractional scaling protocol only speaks in unit of 120ths
0312     // using the same scale throughout makes that simpler
0313     // this also eliminates most loss from wl_fixed
0314     m_factor = std::round(factorAsDouble * 120) / 120;
0315 }
0316 
0317 void WaylandOutputDevice::kde_output_device_v2_edid(const QString &edid)
0318 {
0319     m_edid = QByteArray::fromBase64(edid.toUtf8());
0320 }
0321 
0322 void WaylandOutputDevice::kde_output_device_v2_enabled(int32_t enabled)
0323 {
0324     m_enabled = enabled;
0325 }
0326 
0327 void WaylandOutputDevice::kde_output_device_v2_uuid(const QString &uuid)
0328 {
0329     m_uuid = uuid;
0330 }
0331 
0332 void WaylandOutputDevice::kde_output_device_v2_serial_number(const QString &serialNumber)
0333 {
0334     m_serialNumber = serialNumber;
0335 }
0336 
0337 void WaylandOutputDevice::kde_output_device_v2_eisa_id(const QString &eisaId)
0338 {
0339     m_eisaId = eisaId;
0340 }
0341 
0342 void WaylandOutputDevice::kde_output_device_v2_capabilities(uint32_t flags)
0343 {
0344     m_flags = flags;
0345 }
0346 
0347 void WaylandOutputDevice::kde_output_device_v2_overscan(uint32_t overscan)
0348 {
0349     m_overscan = overscan;
0350 }
0351 
0352 void WaylandOutputDevice::kde_output_device_v2_vrr_policy(uint32_t vrr_policy)
0353 {
0354     m_vrr_policy = vrr_policy;
0355 }
0356 
0357 void WaylandOutputDevice::kde_output_device_v2_rgb_range(uint32_t rgb_range)
0358 {
0359     m_rgbRange = rgb_range;
0360 }
0361 
0362 void WaylandOutputDevice::kde_output_device_v2_name(const QString &outputName)
0363 {
0364     m_outputName = outputName;
0365 }
0366 
0367 QByteArray WaylandOutputDevice::edid() const
0368 {
0369     return m_edid;
0370 }
0371 
0372 bool WaylandOutputDevice::enabled() const
0373 {
0374     return m_enabled;
0375 }
0376 
0377 int WaylandOutputDevice::id() const
0378 {
0379     return m_id;
0380 }
0381 
0382 qreal WaylandOutputDevice::scale() const
0383 {
0384     return m_factor;
0385 }
0386 
0387 QString WaylandOutputDevice::manufacturer() const
0388 {
0389     return m_manufacturer;
0390 }
0391 
0392 QString WaylandOutputDevice::model() const
0393 {
0394     return m_model;
0395 }
0396 
0397 QPoint WaylandOutputDevice::globalPosition() const
0398 {
0399     return m_pos;
0400 }
0401 
0402 QSize WaylandOutputDevice::pixelSize() const
0403 {
0404     return m_mode->size();
0405 }
0406 
0407 int WaylandOutputDevice::refreshRate() const
0408 {
0409     return m_mode->refreshRate();
0410 }
0411 
0412 uint32_t WaylandOutputDevice::vrrPolicy() const
0413 {
0414     return m_vrr_policy;
0415 }
0416 
0417 uint32_t WaylandOutputDevice::overscan() const
0418 {
0419     return m_overscan;
0420 }
0421 
0422 uint32_t WaylandOutputDevice::capabilities() const
0423 {
0424     return m_flags;
0425 }
0426 
0427 uint32_t WaylandOutputDevice::rgbRange() const
0428 {
0429     return m_rgbRange;
0430 }