Warning, file /plasma/libkscreen/backends/kwayland/waylandconfig.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* 0002 * SPDX-FileCopyrightText: 2014-2015 Sebastian Kügler <sebas@kde.org> 0003 * SPDX-FileCopyrightText: 2013 Martin Gräßlin <mgraesslin@kde.org> 0004 * SPDX-FileCopyrightText: 2021 Méven Car <meven.car@enioka.com> 0005 * 0006 * SPDX-License-Identifier: LGPL-2.1-or-later 0007 */ 0008 #include "waylandconfig.h" 0009 0010 #include "kscreen_kwayland_logging.h" 0011 0012 #include "waylandbackend.h" 0013 #include "waylandoutputdevice.h" 0014 #include "waylandoutputmanagement.h" 0015 #include "waylandscreen.h" 0016 0017 #include "tabletmodemanager_interface.h" 0018 0019 #include <QThread> 0020 #include <QTimer> 0021 #include <configmonitor.h> 0022 #include <mode.h> 0023 #include <output.h> 0024 0025 #include <KWayland/Client/connection_thread.h> 0026 #include <KWayland/Client/event_queue.h> 0027 #include <KWayland/Client/registry.h> 0028 0029 #include <utility> 0030 0031 using namespace KScreen; 0032 0033 WaylandConfig::WaylandConfig(QObject *parent) 0034 : QObject(parent) 0035 , m_outputManagement(nullptr) 0036 , m_registryInitialized(false) 0037 , m_blockSignals(true) 0038 , m_kscreenConfig(new Config) 0039 , m_kscreenPendingConfig(nullptr) 0040 , m_screen(new WaylandScreen(this)) 0041 , m_tabletModeAvailable(false) 0042 , m_tabletModeEngaged(false) 0043 { 0044 initKWinTabletMode(); 0045 0046 connect(this, &WaylandConfig::initialized, &m_syncLoop, &QEventLoop::quit); 0047 QTimer::singleShot(3000, this, [this] { 0048 if (m_syncLoop.isRunning()) { 0049 qCWarning(KSCREEN_WAYLAND) << "Connection to Wayland server timed out."; 0050 m_syncLoop.quit(); 0051 } 0052 }); 0053 0054 initConnection(); 0055 m_syncLoop.exec(); 0056 } 0057 0058 WaylandConfig::~WaylandConfig() 0059 { 0060 m_syncLoop.quit(); 0061 } 0062 0063 void WaylandConfig::initKWinTabletMode() 0064 { 0065 auto *interface = 0066 new OrgKdeKWinTabletModeManagerInterface(QStringLiteral("org.kde.KWin"), QStringLiteral("/org/kde/KWin"), QDBusConnection::sessionBus(), this); 0067 if (!interface->isValid()) { 0068 m_tabletModeAvailable = false; 0069 m_tabletModeEngaged = false; 0070 return; 0071 } 0072 0073 m_tabletModeAvailable = interface->tabletModeAvailable(); 0074 m_tabletModeEngaged = interface->tabletMode(); 0075 0076 connect(interface, &OrgKdeKWinTabletModeManagerInterface::tabletModeChanged, this, [this](bool tabletMode) { 0077 if (m_tabletModeEngaged == tabletMode) { 0078 return; 0079 } 0080 m_tabletModeEngaged = tabletMode; 0081 if (!m_blockSignals && m_initializingOutputs.empty()) { 0082 Q_EMIT configChanged(); 0083 } 0084 }); 0085 connect(interface, &OrgKdeKWinTabletModeManagerInterface::tabletModeAvailableChanged, this, [this](bool available) { 0086 if (m_tabletModeAvailable == available) { 0087 return; 0088 } 0089 m_tabletModeAvailable = available; 0090 if (!m_blockSignals && m_initializingOutputs.empty()) { 0091 Q_EMIT configChanged(); 0092 } 0093 }); 0094 } 0095 0096 void WaylandConfig::initConnection() 0097 { 0098 m_connection = KWayland::Client::ConnectionThread::fromApplication(this); 0099 setupRegistry(); 0100 } 0101 0102 void WaylandConfig::blockSignals() 0103 { 0104 Q_ASSERT(m_blockSignals == false); 0105 m_blockSignals = true; 0106 } 0107 0108 void WaylandConfig::unblockSignals() 0109 { 0110 Q_ASSERT(m_blockSignals == true); 0111 m_blockSignals = false; 0112 } 0113 0114 void WaylandConfig::setupRegistry() 0115 { 0116 if (!m_connection) { 0117 return; 0118 } 0119 0120 m_registry = new KWayland::Client::Registry(this); 0121 0122 connect(m_registry, &KWayland::Client::Registry::interfaceAnnounced, this, [this](const QByteArray &interface, quint32 name, quint32 version) { 0123 if (interface == WaylandOutputDevice::interface()->name) { 0124 addOutput(name, std::min(2u, version)); 0125 } 0126 if (interface == WaylandOutputManagement::interface()->name) { 0127 m_outputManagement = new WaylandOutputManagement(m_registry->registry(), name, std::min(3u, version)); 0128 } 0129 if (interface == WaylandOutputOrder::interface()->name) { 0130 m_outputOrder = std::make_unique<WaylandOutputOrder>(m_registry->registry(), name, std::min(1u, version)); 0131 connect(m_outputOrder.get(), &WaylandOutputOrder::outputOrderChanged, this, [this](const QVector<QString> &names) { 0132 bool change = false; 0133 for (const auto &output : std::as_const(m_outputMap)) { 0134 const uint32_t newIndex = names.indexOf(output->name()) + 1; 0135 change = change || output->index() != newIndex; 0136 output->setIndex(newIndex); 0137 } 0138 if (change && !m_blockSignals) { 0139 Q_EMIT configChanged(); 0140 } 0141 }); 0142 } 0143 }); 0144 0145 connect(m_registry, &KWayland::Client::Registry::interfacesAnnounced, this, [this] { 0146 m_registryInitialized = true; 0147 unblockSignals(); 0148 checkInitialized(); 0149 }); 0150 0151 m_registry->create(m_connection); 0152 m_registry->setup(); 0153 } 0154 0155 int s_outputId = 0; 0156 0157 void WaylandConfig::addOutput(quint32 name, quint32 version) 0158 { 0159 qCDebug(KSCREEN_WAYLAND) << "adding output" << name; 0160 0161 auto device = new WaylandOutputDevice(++s_outputId); 0162 m_initializingOutputs << device; 0163 0164 connect(m_registry, &KWayland::Client::Registry::interfaceRemoved, this, [name, device, this](const quint32 &interfaceName) { 0165 if (name == interfaceName) { 0166 removeOutput(device); 0167 } 0168 }); 0169 0170 QMetaObject::Connection *const connection = new QMetaObject::Connection; 0171 *connection = connect(device, &WaylandOutputDevice::done, this, [this, connection, device]() { 0172 QObject::disconnect(*connection); 0173 delete connection; 0174 0175 m_initializingOutputs.removeOne(device); 0176 m_outputMap.insert(device->id(), device); 0177 if (m_outputOrder) { 0178 device->setIndex(m_outputOrder->order().indexOf(device->name()) + 1); 0179 } 0180 checkInitialized(); 0181 0182 if (m_initializingOutputs.isEmpty()) { 0183 m_screen->setOutputs(m_outputMap.values()); 0184 } 0185 if (!m_blockSignals && m_initializingOutputs.isEmpty()) { 0186 Q_EMIT configChanged(); 0187 } 0188 0189 connect(device, &WaylandOutputDevice::done, this, [this]() { 0190 // output got update must update current config 0191 if (!m_blockSignals) { 0192 Q_EMIT configChanged(); 0193 } 0194 }); 0195 }); 0196 0197 device->init(*m_registry, name, version); 0198 } 0199 0200 void WaylandConfig::removeOutput(WaylandOutputDevice *output) 0201 { 0202 qCDebug(KSCREEN_WAYLAND) << "removing output" << output->name(); 0203 0204 if (m_initializingOutputs.removeOne(output)) { 0205 // output was not yet fully initialized, just remove here and return 0206 delete output; 0207 return; 0208 } 0209 0210 // remove the output from output mapping 0211 const auto removedOutput = m_outputMap.take(output->id()); 0212 Q_ASSERT(removedOutput == output); 0213 Q_UNUSED(removedOutput); 0214 m_screen->setOutputs(m_outputMap.values()); 0215 delete output; 0216 0217 if (!m_blockSignals) { 0218 Q_EMIT configChanged(); 0219 } 0220 } 0221 0222 bool WaylandConfig::isReady() const 0223 { 0224 // clang-format off 0225 return !m_blockSignals 0226 && m_registryInitialized 0227 && m_initializingOutputs.isEmpty() 0228 && m_outputMap.count() > 0 0229 && m_outputManagement != nullptr; 0230 // clang-format on 0231 } 0232 0233 void WaylandConfig::checkInitialized() 0234 { 0235 if (!m_initialized && isReady()) { 0236 m_initialized = true; 0237 m_screen->setOutputs(m_outputMap.values()); 0238 Q_EMIT initialized(); 0239 } 0240 } 0241 0242 KScreen::ConfigPtr WaylandConfig::currentConfig() 0243 { 0244 m_kscreenConfig->setScreen(m_screen->toKScreenScreen(m_kscreenConfig)); 0245 0246 const auto features = Config::Feature::Writable | Config::Feature::PerOutputScaling | Config::Feature::AutoRotation | Config::Feature::TabletMode 0247 | Config::Feature::PrimaryDisplay | Config::Feature::XwaylandScales | Config::Feature::SynchronousOutputChanges; 0248 m_kscreenConfig->setSupportedFeatures(features); 0249 m_kscreenConfig->setValid(m_connection->display()); 0250 0251 KScreen::ScreenPtr screen = m_kscreenConfig->screen(); 0252 m_screen->updateKScreenScreen(screen); 0253 0254 // Removing removed outputs 0255 const KScreen::OutputList outputs = m_kscreenConfig->outputs(); 0256 for (const auto &output : outputs) { 0257 if (!m_outputMap.contains(output->id())) { 0258 m_kscreenConfig->removeOutput(output->id()); 0259 } 0260 } 0261 0262 // Add KScreen::Outputs that aren't in the list yet 0263 KScreen::OutputList kscreenOutputs = m_kscreenConfig->outputs(); 0264 QMap<OutputPtr, uint32_t> priorities; 0265 for (const auto &output : m_outputMap) { 0266 KScreen::OutputPtr kscreenOutput; 0267 if (m_kscreenConfig->outputs().contains(output->id())) { 0268 kscreenOutput = m_kscreenConfig->outputs()[output->id()]; 0269 output->updateKScreenOutput(kscreenOutput); 0270 } else { 0271 kscreenOutput = output->toKScreenOutput(); 0272 m_kscreenConfig->addOutput(kscreenOutput); 0273 } 0274 priorities[kscreenOutput] = output->index(); 0275 } 0276 m_kscreenConfig->setOutputPriorities(priorities); 0277 0278 m_kscreenConfig->setTabletModeAvailable(m_tabletModeAvailable); 0279 m_kscreenConfig->setTabletModeEngaged(m_tabletModeEngaged); 0280 0281 return m_kscreenConfig; 0282 } 0283 0284 QMap<int, WaylandOutputDevice *> WaylandConfig::outputMap() const 0285 { 0286 return m_outputMap; 0287 } 0288 0289 void WaylandConfig::tryPendingConfig() 0290 { 0291 if (!m_kscreenPendingConfig) { 0292 return; 0293 } 0294 applyConfig(m_kscreenPendingConfig); 0295 m_kscreenPendingConfig = nullptr; 0296 } 0297 0298 WaylandOutputDevice *WaylandConfig::findOutputDevice(struct ::kde_output_device_v2 *outputdevice) const 0299 { 0300 for (WaylandOutputDevice *device : m_outputMap) { 0301 if (device->object() == outputdevice) { 0302 return device; 0303 } 0304 } 0305 return nullptr; 0306 } 0307 0308 void WaylandConfig::applyConfig(const KScreen::ConfigPtr &newConfig) 0309 { 0310 using namespace KWayland::Client; 0311 0312 newConfig->adjustPriorities(); // never trust input 0313 0314 // Create a new configuration object 0315 auto wlConfig = m_outputManagement->createConfiguration(); 0316 bool changed = false; 0317 0318 if (m_blockSignals) { 0319 // Last apply still pending, remember new changes and apply afterwards 0320 m_kscreenPendingConfig = newConfig; 0321 return; 0322 } 0323 0324 for (const auto &output : newConfig->outputs()) { 0325 changed |= m_outputMap[output->id()]->setWlConfig(wlConfig, output); 0326 } 0327 0328 if (!changed) { 0329 return; 0330 } 0331 0332 // We now block changes in order to compress events while the compositor is doing its thing 0333 // once it's done or failed, we'll trigger configChanged() only once, and not per individual 0334 // property change. 0335 connect(wlConfig, &WaylandOutputConfiguration::applied, this, [this, wlConfig] { 0336 wlConfig->deleteLater(); 0337 unblockSignals(); 0338 Q_EMIT configChanged(); 0339 tryPendingConfig(); 0340 }); 0341 connect(wlConfig, &WaylandOutputConfiguration::failed, this, [this, wlConfig] { 0342 wlConfig->deleteLater(); 0343 unblockSignals(); 0344 Q_EMIT configChanged(); 0345 tryPendingConfig(); 0346 }); 0347 0348 // Now block signals and ask the compositor to apply the changes. 0349 blockSignals(); 0350 wlConfig->apply(); 0351 }