File indexing completed on 2024-05-12 09:36:12

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         return Output::Flipped;
0108     case WL_OUTPUT_TRANSFORM_FLIPPED_90:
0109         return Output::Flipped90;
0110     case WL_OUTPUT_TRANSFORM_FLIPPED_180:
0111         return Output::Flipped180;
0112     case WL_OUTPUT_TRANSFORM_FLIPPED_270:
0113         return Output::Flipped270;
0114     default:
0115         Q_UNREACHABLE();
0116     }
0117 }
0118 
0119 wl_output_transform toKWaylandTransform(const Output::Rotation rotation)
0120 {
0121     switch (rotation) {
0122     case Output::None:
0123         return WL_OUTPUT_TRANSFORM_NORMAL;
0124     case Output::Left:
0125         return WL_OUTPUT_TRANSFORM_90;
0126     case Output::Inverted:
0127         return WL_OUTPUT_TRANSFORM_180;
0128     case Output::Right:
0129         return WL_OUTPUT_TRANSFORM_270;
0130     case Output::Flipped:
0131         return WL_OUTPUT_TRANSFORM_FLIPPED;
0132     case Output::Flipped90:
0133         return WL_OUTPUT_TRANSFORM_FLIPPED_90;
0134     case Output::Flipped180:
0135         return WL_OUTPUT_TRANSFORM_FLIPPED_180;
0136     case Output::Flipped270:
0137         return WL_OUTPUT_TRANSFORM_FLIPPED_270;
0138     default:
0139         Q_UNREACHABLE();
0140     }
0141 }
0142 
0143 void KScreen::WaylandOutputDevice::updateKScreenModes(OutputPtr &output)
0144 {
0145     ModeList modeList;
0146     QStringList preferredModeIds;
0147     QString currentModeId = QStringLiteral("-1");
0148     int modeId = 0;
0149 
0150     for (const WaylandOutputDeviceMode *wlMode : std::as_const(m_modes)) {
0151         ModePtr mode(new Mode());
0152 
0153         const QString modeIdStr = QString::number(modeId);
0154         // KWayland gives the refresh rate as int in mHz
0155         mode->setId(modeIdStr);
0156         mode->setRefreshRate(wlMode->refreshRate() / 1000.0);
0157         mode->setSize(wlMode->size());
0158         mode->setName(modeName(wlMode));
0159 
0160         if (m_mode == wlMode) {
0161             currentModeId = modeIdStr;
0162         }
0163 
0164         if (wlMode->preferred()) {
0165             preferredModeIds << modeIdStr;
0166         }
0167 
0168         // Add to the modelist which gets set on the output
0169         modeList[modeIdStr] = mode;
0170         modeId++;
0171     }
0172     output->setCurrentModeId(currentModeId);
0173     output->setPreferredModes(preferredModeIds);
0174     output->setModes(modeList);
0175 }
0176 
0177 void WaylandOutputDevice::updateKScreenOutput(OutputPtr &output)
0178 {
0179     // Initialize primary output
0180     output->setId(m_id);
0181     output->setEnabled(enabled());
0182     output->setConnected(true);
0183     output->setName(name());
0184     output->setSizeMm(m_physicalSize);
0185     output->setPos(m_pos);
0186     output->setRotation(toKScreenRotation(m_transform));
0187     if (!output->edid()) {
0188         output->setEdid(m_edid);
0189     }
0190 
0191     QSize currentSize = m_mode->size();
0192     output->setSize(output->isHorizontal() ? currentSize : currentSize.transposed());
0193     output->setScale(m_factor);
0194     output->setType(Utils::guessOutputType(m_outputName, m_outputName));
0195     output->setCapabilities(static_cast<Output::Capabilities>(static_cast<uint32_t>(m_flags)));
0196     output->setOverscan(m_overscan);
0197     output->setVrrPolicy(static_cast<Output::VrrPolicy>(m_vrr_policy));
0198     output->setRgbRange(static_cast<Output::RgbRange>(m_rgbRange));
0199     output->setHdrEnabled(m_hdrEnabled);
0200     output->setSdrBrightness(m_sdrBrightness);
0201     output->setWcgEnabled(m_wideColorGamutEnabled);
0202     output->setAutoRotatePolicy(static_cast<Output::AutoRotatePolicy>(m_autoRotatePolicy));
0203     output->setIccProfilePath(m_iccProfilePath);
0204     output->setSdrGamutWideness(m_sdrGamutWideness);
0205     output->setMaxPeakBrightness(m_maxPeakBrightness);
0206     output->setMaxAverageBrightness(m_maxAverageBrightness);
0207     output->setMinBrightness(m_minBrightness);
0208     output->setMaxPeakBrightnessOverride(m_maxPeakBrightnessOverride);
0209     output->setMaxAverageBrightnessOverride(m_maxAverageBrightnessOverride);
0210     output->setMinBrightnessOverride(m_minBrightnessOverride);
0211 
0212     updateKScreenModes(output);
0213 }
0214 
0215 QString WaylandOutputDevice::modeId() const
0216 {
0217     return QString::number(m_modes.indexOf(m_mode));
0218 }
0219 
0220 WaylandOutputDeviceMode *WaylandOutputDevice::deviceModeFromId(const int modeId) const
0221 {
0222     return m_modes.at(modeId);
0223 }
0224 
0225 bool WaylandOutputDevice::setWlConfig(WaylandOutputConfiguration *wlConfig, const KScreen::OutputPtr &output)
0226 {
0227     bool changed = false;
0228 
0229     // enabled?
0230     if (enabled() != output->isEnabled()) {
0231         changed = true;
0232         wlConfig->enable(object(), output->isEnabled());
0233     }
0234 
0235     // position
0236     if (globalPosition() != output->pos()) {
0237         changed = true;
0238         wlConfig->position(object(), output->pos().x(), output->pos().y());
0239     }
0240 
0241     // scale
0242     if (!qFuzzyCompare(scale(), output->scale())) {
0243         changed = true;
0244         wlConfig->scale(object(), wl_fixed_from_double(output->scale()));
0245     }
0246 
0247     // rotation
0248     if (toKScreenRotation(m_transform) != output->rotation()) {
0249         changed = true;
0250         wlConfig->transform(object(), toKWaylandTransform(output->rotation()));
0251     }
0252 
0253     // mode
0254     const ModePtr mode = output->currentMode();
0255     if (mode->size() != pixelSize() || mode->refreshRate() != refreshRate()) {
0256         bool toIntOk;
0257         int modeId = mode->id().toInt(&toIntOk);
0258         Q_ASSERT(toIntOk);
0259 
0260         changed = true;
0261         wlConfig->mode(object(), deviceModeFromId(modeId)->object());
0262     }
0263 
0264     // overscan
0265     if ((output->capabilities() & Output::Capability::Overscan) && overscan() != output->overscan()) {
0266         wlConfig->overscan(object(), output->overscan());
0267         changed = true;
0268     }
0269 
0270     // vrr
0271     if ((output->capabilities() & Output::Capability::Vrr) && vrrPolicy() != static_cast<uint32_t>(output->vrrPolicy())) {
0272         wlConfig->set_vrr_policy(object(), static_cast<uint32_t>(output->vrrPolicy()));
0273         changed = true;
0274     }
0275 
0276     if ((output->capabilities() & Output::Capability::RgbRange) && rgbRange() != static_cast<uint32_t>(output->rgbRange())) {
0277         wlConfig->set_rgb_range(object(), static_cast<uint32_t>(output->rgbRange()));
0278         changed = true;
0279     }
0280     if (output->priority() != m_index) {
0281         changed = true;
0282     }
0283     // always send all outputs
0284     if (kde_output_configuration_v2_get_version(wlConfig->object()) >= KDE_OUTPUT_CONFIGURATION_V2_SET_PRIORITY_SINCE_VERSION) {
0285         wlConfig->set_priority(object(), output->priority());
0286     }
0287     if ((output->capabilities() & Output::Capability::HighDynamicRange) && (m_hdrEnabled == 1) != output->isHdrEnabled()) {
0288         wlConfig->set_high_dynamic_range(object(), output->isHdrEnabled());
0289         changed = true;
0290     }
0291     if ((output->capabilities() & Output::Capability::HighDynamicRange) && m_sdrBrightness != output->sdrBrightness()) {
0292         wlConfig->set_sdr_brightness(object(), output->sdrBrightness());
0293         changed = true;
0294     }
0295     if ((output->capabilities() & Output::Capability::WideColorGamut) && (m_wideColorGamutEnabled == 1) != output->isWcgEnabled()) {
0296         wlConfig->set_wide_color_gamut(object(), output->isWcgEnabled());
0297         changed = true;
0298     }
0299     if ((output->capabilities() & Output::Capability::AutoRotation) && m_autoRotatePolicy != static_cast<uint32_t>(output->autoRotatePolicy())) {
0300         wlConfig->set_auto_rotate_policy(object(), static_cast<uint32_t>(output->autoRotatePolicy()));
0301         changed = true;
0302     }
0303     if ((output->capabilities() & Output::Capability::IccProfile) && m_iccProfilePath != output->iccProfilePath()) {
0304         wlConfig->set_icc_profile_path(object(), output->iccProfilePath());
0305         changed = true;
0306     }
0307     if (kde_output_configuration_v2_get_version(wlConfig->object()) >= KDE_OUTPUT_CONFIGURATION_V2_SET_SDR_GAMUT_WIDENESS_SINCE_VERSION
0308         && m_sdrGamutWideness != output->sdrGamutWideness()) {
0309         wlConfig->set_sdr_gamut_wideness(object(), std::clamp<uint32_t>(std::round(output->sdrGamutWideness() * 10'000), 0, 10'000));
0310         changed = true;
0311     }
0312     if (kde_output_configuration_v2_get_version(wlConfig->object()) >= KDE_OUTPUT_CONFIGURATION_V2_SET_BRIGHTNESS_OVERRIDES_SINCE_VERSION
0313         && (m_maxPeakBrightnessOverride != output->maxPeakBrightnessOverride() || m_maxAverageBrightnessOverride != output->maxAverageBrightnessOverride()
0314             || m_minBrightnessOverride != output->minBrightnessOverride())) {
0315         wlConfig->set_brightness_overrides(object(),
0316                                            output->maxPeakBrightnessOverride().value_or(-1),
0317                                            output->maxAverageBrightnessOverride().value_or(-1),
0318                                            std::round(output->minBrightnessOverride().value_or(-0.000'1) * 10'000.0));
0319         changed = true;
0320     }
0321 
0322     return changed;
0323 }
0324 
0325 QString WaylandOutputDevice::modeName(const WaylandOutputDeviceMode *m) const
0326 {
0327     return QString::number(m->size().width()) + QLatin1Char('x') + QString::number(m->size().height()) + QLatin1Char('@')
0328         + QString::number(qRound(m->refreshRate() / 1000.0));
0329 }
0330 
0331 QString WaylandOutputDevice::name() const
0332 {
0333     return m_outputName;
0334 }
0335 
0336 QDebug operator<<(QDebug dbg, const WaylandOutputDevice *output)
0337 {
0338     dbg << "WaylandOutput(Id:" << output->id() << ", Name:" << QString(output->manufacturer() + QLatin1Char(' ') + output->model()) << ")";
0339     return dbg;
0340 }
0341 
0342 void WaylandOutputDevice::setIndex(uint32_t index)
0343 {
0344     m_index = index;
0345 }
0346 
0347 uint32_t WaylandOutputDevice::index() const
0348 {
0349     return m_index;
0350 }
0351 
0352 void WaylandOutputDevice::kde_output_device_v2_done()
0353 {
0354     Q_EMIT done();
0355 }
0356 
0357 void WaylandOutputDevice::kde_output_device_v2_scale(wl_fixed_t factor)
0358 {
0359     const double factorAsDouble = wl_fixed_to_double(factor);
0360 
0361     // the fractional scaling protocol only speaks in unit of 120ths
0362     // using the same scale throughout makes that simpler
0363     // this also eliminates most loss from wl_fixed
0364     m_factor = std::round(factorAsDouble * 120) / 120;
0365 }
0366 
0367 void WaylandOutputDevice::kde_output_device_v2_edid(const QString &edid)
0368 {
0369     m_edid = QByteArray::fromBase64(edid.toUtf8());
0370 }
0371 
0372 void WaylandOutputDevice::kde_output_device_v2_enabled(int32_t enabled)
0373 {
0374     m_enabled = enabled;
0375 }
0376 
0377 void WaylandOutputDevice::kde_output_device_v2_uuid(const QString &uuid)
0378 {
0379     m_uuid = uuid;
0380 }
0381 
0382 void WaylandOutputDevice::kde_output_device_v2_serial_number(const QString &serialNumber)
0383 {
0384     m_serialNumber = serialNumber;
0385 }
0386 
0387 void WaylandOutputDevice::kde_output_device_v2_eisa_id(const QString &eisaId)
0388 {
0389     m_eisaId = eisaId;
0390 }
0391 
0392 void WaylandOutputDevice::kde_output_device_v2_capabilities(uint32_t flags)
0393 {
0394     m_flags = flags;
0395 }
0396 
0397 void WaylandOutputDevice::kde_output_device_v2_overscan(uint32_t overscan)
0398 {
0399     m_overscan = overscan;
0400 }
0401 
0402 void WaylandOutputDevice::kde_output_device_v2_vrr_policy(uint32_t vrr_policy)
0403 {
0404     m_vrr_policy = vrr_policy;
0405 }
0406 
0407 void WaylandOutputDevice::kde_output_device_v2_rgb_range(uint32_t rgb_range)
0408 {
0409     m_rgbRange = rgb_range;
0410 }
0411 
0412 void WaylandOutputDevice::kde_output_device_v2_name(const QString &outputName)
0413 {
0414     m_outputName = outputName;
0415 }
0416 
0417 void WaylandOutputDevice::kde_output_device_v2_high_dynamic_range(uint32_t hdr_enabled)
0418 {
0419     m_hdrEnabled = hdr_enabled == 1;
0420 }
0421 
0422 void WaylandOutputDevice::kde_output_device_v2_sdr_brightness(uint32_t sdr_brightness)
0423 {
0424     m_sdrBrightness = sdr_brightness;
0425 }
0426 
0427 void WaylandOutputDevice::kde_output_device_v2_wide_color_gamut(uint32_t wcg_enabled)
0428 {
0429     m_wideColorGamutEnabled = wcg_enabled == 1;
0430 }
0431 
0432 void WaylandOutputDevice::kde_output_device_v2_auto_rotate_policy(uint32_t policy)
0433 {
0434     m_autoRotatePolicy = policy;
0435 }
0436 
0437 void WaylandOutputDevice::kde_output_device_v2_icc_profile_path(const QString &profile)
0438 {
0439     m_iccProfilePath = profile;
0440 }
0441 
0442 void WaylandOutputDevice::kde_output_device_v2_brightness_metadata(uint32_t max_peak_brightness, uint32_t max_frame_average_brightness, uint32_t min_brightness)
0443 {
0444     m_maxPeakBrightness = max_peak_brightness;
0445     m_maxAverageBrightness = max_frame_average_brightness;
0446     m_minBrightness = min_brightness / 10'000.0;
0447 }
0448 
0449 void WaylandOutputDevice::kde_output_device_v2_brightness_overrides(int32_t max_peak_brightness, int32_t max_average_brightness, int32_t min_brightness)
0450 {
0451     m_maxPeakBrightnessOverride = max_peak_brightness == -1 ? std::nullopt : std::optional(max_peak_brightness);
0452     m_maxAverageBrightnessOverride = max_average_brightness == -1 ? std::nullopt : std::optional(max_average_brightness);
0453     m_minBrightnessOverride = min_brightness == -1 ? std::nullopt : std::optional(min_brightness / 10'000.0);
0454 }
0455 
0456 void WaylandOutputDevice::kde_output_device_v2_sdr_gamut_wideness(uint32_t value)
0457 {
0458     m_sdrGamutWideness = value / 10'000.0;
0459 }
0460 
0461 QByteArray WaylandOutputDevice::edid() const
0462 {
0463     return m_edid;
0464 }
0465 
0466 bool WaylandOutputDevice::enabled() const
0467 {
0468     return m_enabled;
0469 }
0470 
0471 int WaylandOutputDevice::id() const
0472 {
0473     return m_id;
0474 }
0475 
0476 qreal WaylandOutputDevice::scale() const
0477 {
0478     return m_factor;
0479 }
0480 
0481 QString WaylandOutputDevice::manufacturer() const
0482 {
0483     return m_manufacturer;
0484 }
0485 
0486 QString WaylandOutputDevice::model() const
0487 {
0488     return m_model;
0489 }
0490 
0491 QPoint WaylandOutputDevice::globalPosition() const
0492 {
0493     return m_pos;
0494 }
0495 
0496 QSize WaylandOutputDevice::pixelSize() const
0497 {
0498     return m_mode->size();
0499 }
0500 
0501 int WaylandOutputDevice::refreshRate() const
0502 {
0503     return m_mode->refreshRate();
0504 }
0505 
0506 uint32_t WaylandOutputDevice::vrrPolicy() const
0507 {
0508     return m_vrr_policy;
0509 }
0510 
0511 uint32_t WaylandOutputDevice::overscan() const
0512 {
0513     return m_overscan;
0514 }
0515 
0516 uint32_t WaylandOutputDevice::capabilities() const
0517 {
0518     return m_flags;
0519 }
0520 
0521 uint32_t WaylandOutputDevice::rgbRange() const
0522 {
0523     return m_rgbRange;
0524 }
0525 
0526 #include "moc_waylandoutputdevice.cpp"