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"