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 }