File indexing completed on 2024-04-28 05:30:13

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_x11.h"
0011 #include "core/outputbackend.h"
0012 #include "core/overlaywindow.h"
0013 #include "core/renderbackend.h"
0014 #include "core/renderlayer.h"
0015 #include "effect/effecthandler.h"
0016 #include "opengl/glplatform.h"
0017 #include "options.h"
0018 #include "platformsupport/scenes/opengl/openglbackend.h"
0019 #include "scene/surfaceitem_x11.h"
0020 #include "scene/workspacescene_opengl.h"
0021 #include "utils/common.h"
0022 #include "utils/xcbutils.h"
0023 #include "window.h"
0024 #include "workspace.h"
0025 #include "x11syncmanager.h"
0026 
0027 #include <KCrash>
0028 #include <KGlobalAccel>
0029 #include <KLocalizedString>
0030 #include <KSelectionOwner>
0031 
0032 #include <QAction>
0033 #include <QOpenGLContext>
0034 #include <QThread>
0035 
0036 Q_DECLARE_METATYPE(KWin::X11Compositor::SuspendReason)
0037 
0038 namespace KWin
0039 {
0040 
0041 class X11CompositorSelectionOwner : public KSelectionOwner
0042 {
0043     Q_OBJECT
0044 
0045 public:
0046     X11CompositorSelectionOwner(const char *selection)
0047         : KSelectionOwner(selection, kwinApp()->x11Connection(), kwinApp()->x11RootWindow())
0048         , m_owning(false)
0049     {
0050         connect(this, &X11CompositorSelectionOwner::lostOwnership, this, [this]() {
0051             m_owning = false;
0052         });
0053     }
0054     bool owning() const
0055     {
0056         return m_owning;
0057     }
0058     void setOwning(bool own)
0059     {
0060         m_owning = own;
0061     }
0062 
0063 private:
0064     bool m_owning;
0065 };
0066 
0067 X11Compositor *X11Compositor::create(QObject *parent)
0068 {
0069     Q_ASSERT(!s_compositor);
0070     auto *compositor = new X11Compositor(parent);
0071     s_compositor = compositor;
0072     return compositor;
0073 }
0074 
0075 X11Compositor::X11Compositor(QObject *parent)
0076     : Compositor(parent)
0077     , m_suspended(options->isUseCompositing() ? NoReasonSuspend : UserSuspend)
0078 {
0079     if (qEnvironmentVariableIsSet("KWIN_MAX_FRAMES_TESTED")) {
0080         m_framesToTestForSafety = qEnvironmentVariableIntValue("KWIN_MAX_FRAMES_TESTED");
0081     }
0082 
0083     connect(options, &Options::configChanged, this, [this]() {
0084         if (m_suspended) {
0085             stop();
0086         } else {
0087             reinitialize();
0088         }
0089     });
0090 
0091     m_releaseSelectionTimer.setSingleShot(true);
0092     m_releaseSelectionTimer.setInterval(2000);
0093     connect(&m_releaseSelectionTimer, &QTimer::timeout, this, &X11Compositor::releaseCompositorSelection);
0094 
0095     QAction *toggleAction = new QAction(this);
0096     toggleAction->setProperty("componentName", QStringLiteral("kwin"));
0097     toggleAction->setObjectName("Suspend Compositing");
0098     toggleAction->setText(i18n("Suspend Compositing"));
0099     KGlobalAccel::self()->setDefaultShortcut(toggleAction, QList<QKeySequence>() << (Qt::SHIFT | Qt::ALT | Qt::Key_F12));
0100     KGlobalAccel::self()->setShortcut(toggleAction, QList<QKeySequence>() << (Qt::SHIFT | Qt::ALT | Qt::Key_F12));
0101     connect(toggleAction, &QAction::triggered, this, &X11Compositor::toggle);
0102 }
0103 
0104 X11Compositor::~X11Compositor()
0105 {
0106     Q_EMIT aboutToDestroy();
0107     if (m_openGLFreezeProtectionThread) {
0108         m_openGLFreezeProtectionThread->quit();
0109         m_openGLFreezeProtectionThread->wait();
0110     }
0111     stop(); // this can't be called in the destructor of Compositor
0112     destroyCompositorSelection();
0113 }
0114 
0115 X11SyncManager *X11Compositor::syncManager() const
0116 {
0117     return m_syncManager.get();
0118 }
0119 
0120 void X11Compositor::toggle()
0121 {
0122     if (m_suspended) {
0123         // Direct user call; clear all bits.
0124         resume(AllReasonSuspend);
0125     } else {
0126         // But only set the user one (sufficient to suspend).
0127         suspend(UserSuspend);
0128     }
0129 }
0130 
0131 void X11Compositor::reinitialize()
0132 {
0133     // Resume compositing if suspended.
0134     m_suspended = NoReasonSuspend;
0135     m_inhibitors.clear();
0136     Compositor::reinitialize();
0137 }
0138 
0139 void X11Compositor::suspend(X11Compositor::SuspendReason reason)
0140 {
0141     Q_ASSERT(reason != NoReasonSuspend);
0142     m_suspended |= reason;
0143     stop();
0144 }
0145 
0146 void X11Compositor::resume(X11Compositor::SuspendReason reason)
0147 {
0148     Q_ASSERT(reason != NoReasonSuspend);
0149     m_suspended &= ~reason;
0150     if (reason & BlockRuleSuspend) {
0151         m_inhibitors.clear();
0152     }
0153     start();
0154 }
0155 
0156 void X11Compositor::destroyCompositorSelection()
0157 {
0158     m_selectionOwner.reset();
0159 }
0160 
0161 void X11Compositor::releaseCompositorSelection()
0162 {
0163     switch (m_state) {
0164     case State::On:
0165         // We are compositing at the moment. Don't release.
0166         break;
0167     case State::Off:
0168         if (m_selectionOwner) {
0169             qCDebug(KWIN_CORE) << "Releasing compositor selection";
0170             m_selectionOwner->setOwning(false);
0171             m_selectionOwner->release();
0172         }
0173         break;
0174     case State::Starting:
0175     case State::Stopping:
0176         // Still starting or shutting down the compositor. Starting might fail
0177         // or after stopping a restart might follow. So test again later on.
0178         m_releaseSelectionTimer.start();
0179         break;
0180     }
0181 }
0182 
0183 bool X11Compositor::attemptOpenGLCompositing()
0184 {
0185     // Some broken drivers crash on glXQuery() so to prevent constant KWin crashes:
0186     if (openGLCompositingIsBroken()) {
0187         qCWarning(KWIN_CORE) << "KWin has detected that your OpenGL library is unsafe to use";
0188         return false;
0189     }
0190 
0191     createOpenGLSafePoint(OpenGLSafePoint::PreInit);
0192     auto safePointScope = qScopeGuard([this]() {
0193         createOpenGLSafePoint(OpenGLSafePoint::PostInit);
0194     });
0195 
0196     std::unique_ptr<OpenGLBackend> backend = kwinApp()->outputBackend()->createOpenGLBackend();
0197     if (!backend) {
0198         return false;
0199     }
0200     if (!backend->isFailed()) {
0201         backend->init();
0202     }
0203     if (backend->isFailed()) {
0204         return false;
0205     }
0206 
0207     const QByteArray forceEnv = qgetenv("KWIN_COMPOSE");
0208     if (!forceEnv.isEmpty()) {
0209         if (qstrcmp(forceEnv, "O2") == 0 || qstrcmp(forceEnv, "O2ES") == 0) {
0210             qCDebug(KWIN_CORE) << "OpenGL 2 compositing enforced by environment variable";
0211         } else {
0212             // OpenGL 2 disabled by environment variable
0213             return false;
0214         }
0215     } else {
0216         if (GLPlatform::instance()->recommendedCompositor() < OpenGLCompositing) {
0217             qCDebug(KWIN_CORE) << "Driver does not recommend OpenGL compositing";
0218             return false;
0219         }
0220     }
0221 
0222     // We only support the OpenGL 2+ shader API, not GL_ARB_shader_objects
0223     if (!hasGLVersion(2, 0)) {
0224         qCDebug(KWIN_CORE) << "OpenGL 2.0 is not supported";
0225         return false;
0226     }
0227 
0228     m_scene = std::make_unique<WorkspaceSceneOpenGL>(backend.get());
0229     m_backend = std::move(backend);
0230 
0231     // set strict binding
0232     if (options->isGlStrictBindingFollowsDriver()) {
0233         options->setGlStrictBinding(!GLPlatform::instance()->supports(GLFeature::LooseBinding));
0234     }
0235 
0236     qCDebug(KWIN_CORE) << "OpenGL compositing has been successfully initialized";
0237     return true;
0238 }
0239 
0240 void X11Compositor::start()
0241 {
0242     if (m_suspended) {
0243         QStringList reasons;
0244         if (m_suspended & UserSuspend) {
0245             reasons << QStringLiteral("Disabled by User");
0246         }
0247         if (m_suspended & BlockRuleSuspend) {
0248             reasons << QStringLiteral("Disabled by Window");
0249         }
0250         qCInfo(KWIN_CORE) << "Compositing is suspended, reason:" << reasons;
0251         return;
0252     } else if (!compositingPossible()) {
0253         qCWarning(KWIN_CORE) << "Compositing is not possible";
0254         return;
0255     }
0256 
0257     if (kwinApp()->isTerminating()) {
0258         return;
0259     }
0260     if (m_state != State::Off) {
0261         return;
0262     }
0263 
0264     Q_EMIT aboutToToggleCompositing();
0265     m_state = State::Starting;
0266 
0267     // Claim special _NET_WM_CM_S0 selection and redirect child windows of the root window.
0268     if (!m_selectionOwner) {
0269         m_selectionOwner = std::make_unique<X11CompositorSelectionOwner>("_NET_WM_CM_S0");
0270         connect(m_selectionOwner.get(), &X11CompositorSelectionOwner::lostOwnership, this, &X11Compositor::stop);
0271     }
0272     if (!m_selectionOwner->owning()) {
0273         // Force claim ownership.
0274         m_selectionOwner->claim(true);
0275         m_selectionOwner->setOwning(true);
0276     }
0277 
0278     xcb_composite_redirect_subwindows(kwinApp()->x11Connection(),
0279                                       kwinApp()->x11RootWindow(),
0280                                       XCB_COMPOSITE_REDIRECT_MANUAL);
0281 
0282     // Decide what compositing types can be used.
0283     QList<CompositingType> candidateCompositors = kwinApp()->outputBackend()->supportedCompositors();
0284     const auto userConfigIt = std::find(candidateCompositors.begin(), candidateCompositors.end(), options->compositingMode());
0285     if (userConfigIt != candidateCompositors.end()) {
0286         candidateCompositors.erase(userConfigIt);
0287         candidateCompositors.prepend(options->compositingMode());
0288     } else {
0289         qCWarning(KWIN_CORE) << "Configured compositor not supported by Platform. Falling back to defaults";
0290     }
0291 
0292     for (auto type : std::as_const(candidateCompositors)) {
0293         bool stop = false;
0294         switch (type) {
0295         case OpenGLCompositing:
0296             qCDebug(KWIN_CORE) << "Attempting to load the OpenGL scene";
0297             stop = attemptOpenGLCompositing();
0298             break;
0299         case QPainterCompositing:
0300             qCDebug(KWIN_CORE) << "QPainter compositing is unsupported on X11";
0301             break;
0302         case NoCompositing:
0303             qCDebug(KWIN_CORE) << "Starting without compositing...";
0304             stop = true;
0305             break;
0306         }
0307 
0308         if (stop) {
0309             break;
0310         } else if (qEnvironmentVariableIsSet("KWIN_COMPOSE")) {
0311             qCCritical(KWIN_CORE) << "Could not fulfill the requested compositing mode in KWIN_COMPOSE:" << type << ". Exiting.";
0312             qApp->quit();
0313         }
0314     }
0315 
0316     if (!m_backend) {
0317         m_state = State::Off;
0318 
0319         xcb_composite_unredirect_subwindows(kwinApp()->x11Connection(),
0320                                             kwinApp()->x11RootWindow(),
0321                                             XCB_COMPOSITE_REDIRECT_MANUAL);
0322         if (m_selectionOwner) {
0323             m_selectionOwner->setOwning(false);
0324             m_selectionOwner->release();
0325         }
0326         return;
0327     }
0328 
0329     Q_EMIT sceneCreated();
0330 
0331     kwinApp()->setX11CompositeWindow(backend()->overlayWindow()->window());
0332 
0333     auto workspaceLayer = new RenderLayer(workspace()->outputs()[0]->renderLoop());
0334     workspaceLayer->setDelegate(std::make_unique<SceneDelegate>(m_scene.get(), nullptr));
0335     workspaceLayer->setGeometry(workspace()->geometry());
0336     connect(workspace(), &Workspace::geometryChanged, workspaceLayer, [workspaceLayer]() {
0337         workspaceLayer->setGeometry(workspace()->geometry());
0338     });
0339     addSuperLayer(workspaceLayer);
0340 
0341     m_state = State::On;
0342 
0343     const auto windows = workspace()->windows();
0344     for (Window *window : windows) {
0345         window->setupCompositing();
0346     }
0347 
0348     // Sets also the 'effects' pointer.
0349     kwinApp()->createEffectsHandler(this, m_scene.get());
0350 
0351     m_syncManager.reset(X11SyncManager::create());
0352     if (m_releaseSelectionTimer.isActive()) {
0353         m_releaseSelectionTimer.stop();
0354     }
0355 
0356     Q_EMIT compositingToggled(true);
0357 }
0358 
0359 void X11Compositor::stop()
0360 {
0361     if (m_state == State::Off || m_state == State::Stopping) {
0362         return;
0363     }
0364     m_state = State::Stopping;
0365     Q_EMIT aboutToToggleCompositing();
0366 
0367     m_releaseSelectionTimer.start();
0368 
0369     // Some effects might need access to effect windows when they are about to
0370     // be destroyed, for example to unreference deleted windows, so we have to
0371     // make sure that effect windows outlive effects.
0372     delete effects;
0373     effects = nullptr;
0374 
0375     if (Workspace::self()) {
0376         const auto windows = workspace()->windows();
0377         for (Window *window : windows) {
0378             window->finishCompositing();
0379         }
0380         xcb_composite_unredirect_subwindows(kwinApp()->x11Connection(),
0381                                             kwinApp()->x11RootWindow(),
0382                                             XCB_COMPOSITE_REDIRECT_MANUAL);
0383     }
0384 
0385     if (m_backend->compositingType() == OpenGLCompositing) {
0386         // some layers need a context current for destruction
0387         static_cast<OpenGLBackend *>(m_backend.get())->makeCurrent();
0388     }
0389 
0390     const auto superlayers = m_superlayers;
0391     for (auto it = superlayers.begin(); it != superlayers.end(); ++it) {
0392         removeSuperLayer(*it);
0393     }
0394 
0395     m_syncManager.reset();
0396     m_scene.reset();
0397     m_backend.reset();
0398 
0399     kwinApp()->setX11CompositeWindow(XCB_WINDOW_NONE);
0400 
0401     m_state = State::Off;
0402     Q_EMIT compositingToggled(false);
0403 }
0404 
0405 void X11Compositor::composite(RenderLoop *renderLoop)
0406 {
0407     if (backend()->overlayWindow() && !backend()->overlayWindow()->isVisible()) {
0408         // Return since nothing is visible.
0409         return;
0410     }
0411 
0412     QList<Window *> windows = workspace()->stackingOrder();
0413     QList<SurfaceItemX11 *> dirtyItems;
0414 
0415     // Reset the damage state of each window and fetch the damage region
0416     // without waiting for a reply
0417     for (Window *window : std::as_const(windows)) {
0418         SurfaceItemX11 *surfaceItem = static_cast<SurfaceItemX11 *>(window->surfaceItem());
0419         if (surfaceItem->fetchDamage()) {
0420             dirtyItems.append(surfaceItem);
0421         }
0422     }
0423 
0424     if (dirtyItems.count() > 0) {
0425         if (m_syncManager) {
0426             m_syncManager->triggerFence();
0427         }
0428         xcb_flush(kwinApp()->x11Connection());
0429     }
0430 
0431     // Get the replies
0432     for (SurfaceItemX11 *item : std::as_const(dirtyItems)) {
0433         item->waitForDamage();
0434     }
0435 
0436     if (m_framesToTestForSafety > 0 && (backend()->compositingType() & OpenGLCompositing)) {
0437         createOpenGLSafePoint(OpenGLSafePoint::PreFrame);
0438     }
0439 
0440     Compositor::composite(renderLoop);
0441 
0442     if (m_syncManager) {
0443         if (!m_syncManager->endFrame()) {
0444             qCDebug(KWIN_CORE) << "Aborting explicit synchronization with the X command stream.";
0445             qCDebug(KWIN_CORE) << "Future frames will be rendered unsynchronized.";
0446             m_syncManager.reset();
0447         }
0448     }
0449 
0450     if (m_framesToTestForSafety > 0) {
0451         if (backend()->compositingType() & OpenGLCompositing) {
0452             createOpenGLSafePoint(OpenGLSafePoint::PostFrame);
0453         }
0454         m_framesToTestForSafety--;
0455         if (m_framesToTestForSafety == 0 && (backend()->compositingType() & OpenGLCompositing)) {
0456             createOpenGLSafePoint(OpenGLSafePoint::PostLastGuardedFrame);
0457         }
0458     }
0459 }
0460 
0461 void X11Compositor::inhibit(Window *window)
0462 {
0463     m_inhibitors.insert(window);
0464     // Do NOT attempt to call suspend(true) from within the eventchain!
0465     if (!(m_suspended & BlockRuleSuspend)) {
0466         QMetaObject::invokeMethod(
0467             this, [this]() {
0468                 suspend(BlockRuleSuspend);
0469             },
0470             Qt::QueuedConnection);
0471     }
0472 }
0473 
0474 void X11Compositor::uninhibit(Window *window)
0475 {
0476     if (!m_inhibitors.remove(window)) {
0477         return;
0478     }
0479     if (m_suspended & BlockRuleSuspend) {
0480         if (m_inhibitors.isEmpty()) {
0481             // Do NOT attempt to call suspend(false) from within the eventchain!
0482             QMetaObject::invokeMethod(
0483                 this, [this]() {
0484                     resume(BlockRuleSuspend);
0485                 },
0486                 Qt::QueuedConnection);
0487         }
0488     }
0489 }
0490 
0491 X11Compositor *X11Compositor::self()
0492 {
0493     return qobject_cast<X11Compositor *>(Compositor::self());
0494 }
0495 
0496 bool X11Compositor::openGLCompositingIsBroken() const
0497 {
0498     auto timestamp = KConfigGroup(kwinApp()->config(), QStringLiteral("Compositing")).readEntry(QLatin1String("LastFailureTimestamp"), 0);
0499     if (timestamp > 0) {
0500         if (QDateTime::currentSecsSinceEpoch() - timestamp < 60) {
0501             return true;
0502         }
0503     }
0504 
0505     return false;
0506 }
0507 
0508 QString X11Compositor::compositingNotPossibleReason() const
0509 {
0510     // first off, check whether we figured that we'll crash on detection because of a buggy driver
0511     KConfigGroup gl_workaround_group(kwinApp()->config(), QStringLiteral("Compositing"));
0512     if (gl_workaround_group.readEntry("Backend", "OpenGL") == QLatin1String("OpenGL") && openGLCompositingIsBroken()) {
0513         return i18n("<b>OpenGL compositing (the default) has crashed KWin in the past.</b><br>"
0514                     "This was most likely due to a driver bug."
0515                     "<p>If you think that you have meanwhile upgraded to a stable driver,<br>"
0516                     "you can reset this protection but <b>be aware that this might result in an immediate crash!</b></p>");
0517     }
0518 
0519     if (!Xcb::Extensions::self()->isCompositeAvailable() || !Xcb::Extensions::self()->isDamageAvailable()) {
0520         return i18n("Required X extensions (XComposite and XDamage) are not available.");
0521     }
0522     if (!Xcb::Extensions::self()->hasGlx()) {
0523         return i18n("GLX/OpenGL is not available.");
0524     }
0525     return QString();
0526 }
0527 
0528 bool X11Compositor::compositingPossible() const
0529 {
0530     // first off, check whether we figured that we'll crash on detection because of a buggy driver
0531     KConfigGroup gl_workaround_group(kwinApp()->config(), QStringLiteral("Compositing"));
0532     if (gl_workaround_group.readEntry("Backend", "OpenGL") == QLatin1String("OpenGL") && openGLCompositingIsBroken()) {
0533         qCWarning(KWIN_CORE) << "Compositing disabled: video driver seems unstable. If you think it's a false positive, please try again in a few minutes.";
0534         return false;
0535     }
0536 
0537     if (!Xcb::Extensions::self()->isCompositeAvailable()) {
0538         qCWarning(KWIN_CORE) << "Compositing disabled: no composite extension available";
0539         return false;
0540     }
0541     if (!Xcb::Extensions::self()->isDamageAvailable()) {
0542         qCWarning(KWIN_CORE) << "Compositing disabled: no damage extension available";
0543         return false;
0544     }
0545     if (Xcb::Extensions::self()->hasGlx()) {
0546         return true;
0547     }
0548     if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGLES) {
0549         return true;
0550     } else if (qstrcmp(qgetenv("KWIN_COMPOSE"), "O2ES") == 0) {
0551         return true;
0552     }
0553     qCWarning(KWIN_CORE) << "Compositing disabled: no OpenGL support";
0554     return false;
0555 }
0556 
0557 void X11Compositor::createOpenGLSafePoint(OpenGLSafePoint safePoint)
0558 {
0559     auto group = KConfigGroup(kwinApp()->config(), QStringLiteral("Compositing"));
0560     switch (safePoint) {
0561     case OpenGLSafePoint::PreInit:
0562         // Explicitly write the failure timestamp so that if we crash during
0563         // OpenGL init, we know we should not try again.
0564         group.writeEntry(QLatin1String("LastFailureTimestamp"), QDateTime::currentSecsSinceEpoch());
0565         group.sync();
0566         // Deliberately continue with PreFrame
0567         Q_FALLTHROUGH();
0568     case OpenGLSafePoint::PreFrame:
0569         if (m_openGLFreezeProtectionThread == nullptr) {
0570             Q_ASSERT(m_openGLFreezeProtection == nullptr);
0571             m_openGLFreezeProtectionThread = std::make_unique<QThread>();
0572             m_openGLFreezeProtectionThread->setObjectName("FreezeDetector");
0573             m_openGLFreezeProtectionThread->start();
0574             m_openGLFreezeProtection = std::make_unique<QTimer>();
0575             m_openGLFreezeProtection->setInterval(15000);
0576             m_openGLFreezeProtection->setSingleShot(true);
0577             m_openGLFreezeProtection->start();
0578             const QString configName = kwinApp()->config()->name();
0579             m_openGLFreezeProtection->moveToThread(m_openGLFreezeProtectionThread.get());
0580             connect(
0581                 m_openGLFreezeProtection.get(), &QTimer::timeout, m_openGLFreezeProtection.get(),
0582                 [configName] {
0583                     auto group = KConfigGroup(KSharedConfig::openConfig(configName), QStringLiteral("Compositing"));
0584                     group.writeEntry(QLatin1String("LastFailureTimestamp"), QDateTime::currentSecsSinceEpoch());
0585                     group.sync();
0586                     KCrash::setDrKonqiEnabled(false);
0587                     qFatal("Freeze in OpenGL initialization detected");
0588                 },
0589                 Qt::DirectConnection);
0590         } else {
0591             Q_ASSERT(m_openGLFreezeProtection);
0592             QMetaObject::invokeMethod(m_openGLFreezeProtection.get(), QOverload<>::of(&QTimer::start), Qt::QueuedConnection);
0593         }
0594         break;
0595     case OpenGLSafePoint::PostInit:
0596         group.deleteEntry(QLatin1String("LastFailureTimestamp"));
0597         group.sync();
0598         // Deliberately continue with PostFrame
0599         Q_FALLTHROUGH();
0600     case OpenGLSafePoint::PostFrame:
0601         QMetaObject::invokeMethod(m_openGLFreezeProtection.get(), &QTimer::stop, Qt::QueuedConnection);
0602         break;
0603     case OpenGLSafePoint::PostLastGuardedFrame:
0604         m_openGLFreezeProtectionThread->quit();
0605         m_openGLFreezeProtectionThread->wait();
0606         m_openGLFreezeProtectionThread.reset();
0607         m_openGLFreezeProtection.reset();
0608         break;
0609     }
0610 }
0611 
0612 } // namespace KWin
0613 
0614 #include "compositor_x11.moc"
0615 #include "moc_compositor_x11.cpp"