File indexing completed on 2024-11-10 04:57:41
0001 /* 0002 KWin - the KDE window manager 0003 This file is part of the KDE project. 0004 0005 SPDX-FileCopyrightText: 2006 Lubos Lunak <l.lunak@kde.org> 0006 0007 SPDX-License-Identifier: GPL-2.0-or-later 0008 */ 0009 0010 #include "compositor_wayland.h" 0011 #include "core/output.h" 0012 #include "core/outputbackend.h" 0013 #include "core/renderbackend.h" 0014 #include "core/renderlayer.h" 0015 #include "effect/effecthandler.h" 0016 #include "main.h" 0017 #include "opengl/glplatform.h" 0018 #include "platformsupport/scenes/opengl/openglbackend.h" 0019 #include "platformsupport/scenes/qpainter/qpainterbackend.h" 0020 #include "scene/cursordelegate_opengl.h" 0021 #include "scene/cursordelegate_qpainter.h" 0022 #include "scene/cursorscene.h" 0023 #include "scene/itemrenderer_opengl.h" 0024 #include "scene/itemrenderer_qpainter.h" 0025 #include "scene/workspacescene_opengl.h" 0026 #include "scene/workspacescene_qpainter.h" 0027 #include "window.h" 0028 #include "workspace.h" 0029 0030 #include <QQuickWindow> 0031 0032 namespace KWin 0033 { 0034 0035 WaylandCompositor *WaylandCompositor::create(QObject *parent) 0036 { 0037 Q_ASSERT(!s_compositor); 0038 auto *compositor = new WaylandCompositor(parent); 0039 s_compositor = compositor; 0040 return compositor; 0041 } 0042 0043 WaylandCompositor::WaylandCompositor(QObject *parent) 0044 : Compositor(parent) 0045 { 0046 } 0047 0048 WaylandCompositor::~WaylandCompositor() 0049 { 0050 Q_EMIT aboutToDestroy(); 0051 stop(); // this can't be called in the destructor of Compositor 0052 } 0053 0054 bool WaylandCompositor::attemptOpenGLCompositing() 0055 { 0056 std::unique_ptr<OpenGLBackend> backend = kwinApp()->outputBackend()->createOpenGLBackend(); 0057 if (!backend) { 0058 return false; 0059 } 0060 if (!backend->isFailed()) { 0061 backend->init(); 0062 } 0063 if (backend->isFailed()) { 0064 return false; 0065 } 0066 0067 const QByteArray forceEnv = qgetenv("KWIN_COMPOSE"); 0068 if (!forceEnv.isEmpty()) { 0069 if (qstrcmp(forceEnv, "O2") == 0 || qstrcmp(forceEnv, "O2ES") == 0) { 0070 qCDebug(KWIN_CORE) << "OpenGL 2 compositing enforced by environment variable"; 0071 } else { 0072 // OpenGL 2 disabled by environment variable 0073 return false; 0074 } 0075 } else { 0076 if (GLPlatform::instance()->recommendedCompositor() < OpenGLCompositing) { 0077 qCDebug(KWIN_CORE) << "Driver does not recommend OpenGL compositing"; 0078 return false; 0079 } 0080 } 0081 0082 // We only support the OpenGL 2+ shader API, not GL_ARB_shader_objects 0083 if (!hasGLVersion(2, 0)) { 0084 qCDebug(KWIN_CORE) << "OpenGL 2.0 is not supported"; 0085 return false; 0086 } 0087 0088 m_scene = std::make_unique<WorkspaceSceneOpenGL>(backend.get()); 0089 m_cursorScene = std::make_unique<CursorScene>(std::make_unique<ItemRendererOpenGL>()); 0090 m_backend = std::move(backend); 0091 0092 qCDebug(KWIN_CORE) << "OpenGL compositing has been successfully initialized"; 0093 return true; 0094 } 0095 0096 bool WaylandCompositor::attemptQPainterCompositing() 0097 { 0098 std::unique_ptr<QPainterBackend> backend(kwinApp()->outputBackend()->createQPainterBackend()); 0099 if (!backend || backend->isFailed()) { 0100 return false; 0101 } 0102 0103 m_scene = std::make_unique<WorkspaceSceneQPainter>(backend.get()); 0104 m_cursorScene = std::make_unique<CursorScene>(std::make_unique<ItemRendererQPainter>()); 0105 m_backend = std::move(backend); 0106 0107 qCDebug(KWIN_CORE) << "QPainter compositing has been successfully initialized"; 0108 return true; 0109 } 0110 0111 void WaylandCompositor::start() 0112 { 0113 if (kwinApp()->isTerminating()) { 0114 return; 0115 } 0116 if (m_state != State::Off) { 0117 return; 0118 } 0119 0120 Q_EMIT aboutToToggleCompositing(); 0121 m_state = State::Starting; 0122 0123 // If compositing has been restarted, try to use the last used compositing type. 0124 const QList<CompositingType> availableCompositors = kwinApp()->outputBackend()->supportedCompositors(); 0125 QList<CompositingType> candidateCompositors; 0126 0127 if (m_selectedCompositor != NoCompositing) { 0128 candidateCompositors.append(m_selectedCompositor); 0129 } else { 0130 candidateCompositors = availableCompositors; 0131 0132 const auto userConfigIt = std::find(candidateCompositors.begin(), candidateCompositors.end(), options->compositingMode()); 0133 if (userConfigIt != candidateCompositors.end()) { 0134 candidateCompositors.erase(userConfigIt); 0135 candidateCompositors.prepend(options->compositingMode()); 0136 } else { 0137 qCWarning(KWIN_CORE) << "Configured compositor not supported by Platform. Falling back to defaults"; 0138 } 0139 } 0140 0141 for (auto type : std::as_const(candidateCompositors)) { 0142 bool stop = false; 0143 switch (type) { 0144 case OpenGLCompositing: 0145 qCDebug(KWIN_CORE) << "Attempting to load the OpenGL scene"; 0146 stop = attemptOpenGLCompositing(); 0147 break; 0148 case QPainterCompositing: 0149 qCDebug(KWIN_CORE) << "Attempting to load the QPainter scene"; 0150 stop = attemptQPainterCompositing(); 0151 break; 0152 case NoCompositing: 0153 qCDebug(KWIN_CORE) << "Starting without compositing..."; 0154 stop = true; 0155 break; 0156 } 0157 0158 if (stop) { 0159 break; 0160 } else if (qEnvironmentVariableIsSet("KWIN_COMPOSE")) { 0161 qCCritical(KWIN_CORE) << "Could not fulfill the requested compositing mode in KWIN_COMPOSE:" << type << ". Exiting."; 0162 qApp->quit(); 0163 } 0164 } 0165 0166 if (!m_backend) { 0167 m_state = State::Off; 0168 0169 qCCritical(KWIN_CORE) << "The used windowing system requires compositing"; 0170 qCCritical(KWIN_CORE) << "We are going to quit KWin now as it is broken"; 0171 qApp->quit(); 0172 return; 0173 } 0174 0175 if (m_selectedCompositor == NoCompositing) { 0176 m_selectedCompositor = m_backend->compositingType(); 0177 0178 switch (m_selectedCompositor) { 0179 case NoCompositing: 0180 break; 0181 case OpenGLCompositing: 0182 QQuickWindow::setGraphicsApi(QSGRendererInterface::OpenGL); 0183 break; 0184 case QPainterCompositing: 0185 QQuickWindow::setGraphicsApi(QSGRendererInterface::Software); 0186 break; 0187 } 0188 } 0189 0190 Q_EMIT sceneCreated(); 0191 0192 const QList<Output *> outputs = workspace()->outputs(); 0193 for (Output *output : outputs) { 0194 addOutput(output); 0195 } 0196 connect(workspace(), &Workspace::outputAdded, this, &WaylandCompositor::addOutput); 0197 connect(workspace(), &Workspace::outputRemoved, this, &WaylandCompositor::removeOutput); 0198 0199 m_state = State::On; 0200 0201 const auto windows = workspace()->windows(); 0202 for (Window *window : windows) { 0203 window->setupCompositing(); 0204 } 0205 0206 // Sets also the 'effects' pointer. 0207 kwinApp()->createEffectsHandler(this, m_scene.get()); 0208 0209 Q_EMIT compositingToggled(true); 0210 } 0211 0212 void WaylandCompositor::stop() 0213 { 0214 if (m_state == State::Off || m_state == State::Stopping) { 0215 return; 0216 } 0217 m_state = State::Stopping; 0218 Q_EMIT aboutToToggleCompositing(); 0219 0220 // Some effects might need access to effect windows when they are about to 0221 // be destroyed, for example to unreference deleted windows, so we have to 0222 // make sure that effect windows outlive effects. 0223 delete effects; 0224 effects = nullptr; 0225 0226 if (Workspace::self()) { 0227 const auto windows = workspace()->windows(); 0228 for (Window *window : windows) { 0229 window->finishCompositing(); 0230 } 0231 disconnect(workspace(), &Workspace::outputAdded, this, &WaylandCompositor::addOutput); 0232 disconnect(workspace(), &Workspace::outputRemoved, this, &WaylandCompositor::removeOutput); 0233 } 0234 0235 if (m_backend->compositingType() == OpenGLCompositing) { 0236 // some layers need a context current for destruction 0237 static_cast<OpenGLBackend *>(m_backend.get())->makeCurrent(); 0238 } 0239 0240 const auto superlayers = m_superlayers; 0241 for (auto it = superlayers.begin(); it != superlayers.end(); ++it) { 0242 removeSuperLayer(*it); 0243 } 0244 0245 m_scene.reset(); 0246 m_cursorScene.reset(); 0247 m_backend.reset(); 0248 0249 m_state = State::Off; 0250 Q_EMIT compositingToggled(false); 0251 } 0252 0253 void WaylandCompositor::addOutput(Output *output) 0254 { 0255 if (output->isPlaceholder()) { 0256 return; 0257 } 0258 auto workspaceLayer = new RenderLayer(output->renderLoop()); 0259 workspaceLayer->setDelegate(std::make_unique<SceneDelegate>(m_scene.get(), output)); 0260 workspaceLayer->setGeometry(output->rectF()); 0261 connect(output, &Output::geometryChanged, workspaceLayer, [output, workspaceLayer]() { 0262 workspaceLayer->setGeometry(output->rectF()); 0263 }); 0264 0265 auto cursorLayer = new RenderLayer(output->renderLoop()); 0266 cursorLayer->setVisible(false); 0267 if (m_backend->compositingType() == OpenGLCompositing) { 0268 cursorLayer->setDelegate(std::make_unique<CursorDelegateOpenGL>(output)); 0269 } else { 0270 cursorLayer->setDelegate(std::make_unique<CursorDelegateQPainter>(output)); 0271 } 0272 cursorLayer->setParent(workspaceLayer); 0273 cursorLayer->setSuperlayer(workspaceLayer); 0274 0275 // Software cursor is forced for intel devices because there are screen stuttering issues with hardware cursor, 0276 // possibly a kernel driver bug. Remove the workaround when https://gitlab.freedesktop.org/drm/intel/-/issues/9571 is fixed. 0277 static bool forceSoftwareCursorIsSet; 0278 static const bool forceSoftwareCursor = qEnvironmentVariableIntValue("KWIN_FORCE_SW_CURSOR", &forceSoftwareCursorIsSet) == 1 0279 || (!forceSoftwareCursorIsSet && GLPlatform::instance() && GLPlatform::instance()->isIntel()); 0280 0281 auto updateCursorLayer = [this, output, cursorLayer]() { 0282 const Cursor *cursor = Cursors::self()->currentCursor(); 0283 const QRectF outputLocalRect = output->mapFromGlobal(cursor->geometry()); 0284 const auto outputLayer = m_backend->cursorLayer(output); 0285 if (!cursor->isOnOutput(output)) { 0286 if (outputLayer && outputLayer->isEnabled()) { 0287 outputLayer->setEnabled(false); 0288 output->updateCursorLayer(); 0289 } 0290 cursorLayer->setVisible(false); 0291 return true; 0292 } 0293 const auto renderHardwareCursor = [&]() { 0294 if (!outputLayer || forceSoftwareCursor) { 0295 return false; 0296 } 0297 QRectF nativeCursorRect = output->transform().map(scaledRect(outputLocalRect, output->scale()), output->pixelSize()); 0298 QSize bufferSize(std::ceil(nativeCursorRect.width()), std::ceil(nativeCursorRect.height())); 0299 if (const auto fixedSize = outputLayer->fixedSize()) { 0300 if (fixedSize->width() < bufferSize.width() || fixedSize->height() < bufferSize.height()) { 0301 return false; 0302 } 0303 bufferSize = *fixedSize; 0304 nativeCursorRect = output->transform().map(QRectF(outputLocalRect.topLeft() * output->scale(), bufferSize), output->pixelSize()); 0305 } 0306 outputLayer->setPosition(nativeCursorRect.topLeft()); 0307 outputLayer->setHotspot(output->transform().map(cursor->hotspot() * output->scale(), bufferSize)); 0308 outputLayer->setSize(bufferSize); 0309 if (auto beginInfo = outputLayer->beginFrame()) { 0310 const RenderTarget &renderTarget = beginInfo->renderTarget; 0311 0312 RenderLayer renderLayer(output->renderLoop()); 0313 renderLayer.setDelegate(std::make_unique<SceneDelegate>(m_cursorScene.get(), output)); 0314 renderLayer.setOutputLayer(outputLayer); 0315 0316 renderLayer.delegate()->prePaint(); 0317 renderLayer.delegate()->paint(renderTarget, infiniteRegion()); 0318 renderLayer.delegate()->postPaint(); 0319 0320 if (!outputLayer->endFrame(infiniteRegion(), infiniteRegion())) { 0321 return false; 0322 } 0323 } else { 0324 return false; 0325 } 0326 outputLayer->setEnabled(true); 0327 return output->updateCursorLayer(); 0328 }; 0329 if (renderHardwareCursor()) { 0330 cursorLayer->setVisible(false); 0331 return true; 0332 } else { 0333 if (outputLayer && outputLayer->isEnabled()) { 0334 outputLayer->setEnabled(false); 0335 output->updateCursorLayer(); 0336 } 0337 cursorLayer->setVisible(cursor->isOnOutput(output)); 0338 cursorLayer->setGeometry(outputLocalRect); 0339 cursorLayer->addRepaintFull(); 0340 return false; 0341 } 0342 }; 0343 auto moveCursorLayer = [this, output, cursorLayer, updateCursorLayer]() { 0344 const Cursor *cursor = Cursors::self()->currentCursor(); 0345 const QRectF outputLocalRect = output->mapFromGlobal(cursor->geometry()); 0346 const auto outputLayer = m_backend->cursorLayer(output); 0347 bool hardwareCursor = false; 0348 if (outputLayer) { 0349 if (outputLayer->isEnabled()) { 0350 const QRectF nativeCursorRect = output->transform() 0351 .map(QRectF(outputLocalRect.topLeft() * output->scale(), outputLayer->size()), output->pixelSize()); 0352 outputLayer->setPosition(nativeCursorRect.topLeft()); 0353 hardwareCursor = output->updateCursorLayer(); 0354 } else if (!cursorLayer->isVisible() && !forceSoftwareCursor) { 0355 // this is for the case that the cursor wasn't visible because it was on a different output before 0356 hardwareCursor = updateCursorLayer(); 0357 } 0358 } 0359 cursorLayer->setVisible(cursor->isOnOutput(output) && !hardwareCursor); 0360 cursorLayer->setGeometry(outputLocalRect); 0361 cursorLayer->addRepaintFull(); 0362 }; 0363 updateCursorLayer(); 0364 connect(output, &Output::geometryChanged, cursorLayer, updateCursorLayer); 0365 connect(Cursors::self(), &Cursors::currentCursorChanged, cursorLayer, updateCursorLayer); 0366 connect(Cursors::self(), &Cursors::hiddenChanged, cursorLayer, updateCursorLayer); 0367 connect(Cursors::self(), &Cursors::positionChanged, cursorLayer, moveCursorLayer); 0368 0369 addSuperLayer(workspaceLayer); 0370 } 0371 0372 void WaylandCompositor::removeOutput(Output *output) 0373 { 0374 if (output->isPlaceholder()) { 0375 return; 0376 } 0377 removeSuperLayer(m_superlayers[output->renderLoop()]); 0378 } 0379 0380 } // namespace KWin 0381 0382 #include "moc_compositor_wayland.cpp"