File indexing completed on 2024-04-28 16:48:42

0001 /*
0002     KWin - the KDE window manager
0003     This file is part of the KDE project.
0004 
0005     SPDX-FileCopyrightText: 2012 Martin Gräßlin <mgraesslin@kde.org>
0006 
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 
0010 // own
0011 #include "dbusinterface.h"
0012 #include "compositingadaptor.h"
0013 #include "pluginsadaptor.h"
0014 #include "virtualdesktopmanageradaptor.h"
0015 
0016 // kwin
0017 #include "atoms.h"
0018 #include "composite.h"
0019 #include "core/output.h"
0020 #include "core/renderbackend.h"
0021 #include "debug_console.h"
0022 #include "kwinadaptor.h"
0023 #include "main.h"
0024 #include "placement.h"
0025 #include "pluginmanager.h"
0026 #include "unmanaged.h"
0027 #include "virtualdesktops.h"
0028 #include "window.h"
0029 #include "workspace.h"
0030 #if KWIN_BUILD_ACTIVITIES
0031 #include "activities.h"
0032 #endif
0033 
0034 // Qt
0035 #include <QOpenGLContext>
0036 
0037 namespace KWin
0038 {
0039 
0040 DBusInterface::DBusInterface(QObject *parent)
0041     : QObject(parent)
0042     , m_serviceName(QStringLiteral("org.kde.KWin"))
0043 {
0044     (void)new KWinAdaptor(this);
0045 
0046     QDBusConnection dbus = QDBusConnection::sessionBus();
0047     dbus.registerObject(QStringLiteral("/KWin"), this);
0048     dbus.registerService(m_serviceName);
0049     dbus.connect(QString(), QStringLiteral("/KWin"), QStringLiteral("org.kde.KWin"), QStringLiteral("reloadConfig"),
0050                  Workspace::self(), SLOT(slotReloadConfig()));
0051 
0052     connect(Workspace::self(), &Workspace::showingDesktopChanged, this, &DBusInterface::onShowingDesktopChanged);
0053 }
0054 
0055 DBusInterface::~DBusInterface()
0056 {
0057     QDBusConnection::sessionBus().unregisterService(m_serviceName);
0058 }
0059 
0060 bool DBusInterface::showingDesktop() const
0061 {
0062     return workspace()->showingDesktop();
0063 }
0064 
0065 void DBusInterface::reconfigure()
0066 {
0067     Workspace::self()->reconfigure();
0068 }
0069 
0070 void DBusInterface::killWindow()
0071 {
0072     Workspace::self()->slotKillWindow();
0073 }
0074 
0075 void DBusInterface::cascadeDesktop()
0076 {
0077     workspace()->placement()->cascadeDesktop();
0078 }
0079 
0080 void DBusInterface::unclutterDesktop()
0081 {
0082     workspace()->placement()->unclutterDesktop();
0083 }
0084 
0085 QString DBusInterface::supportInformation()
0086 {
0087     return Workspace::self()->supportInformation();
0088 }
0089 
0090 QString DBusInterface::activeOutputName()
0091 {
0092     return Workspace::self()->activeOutput()->name();
0093 }
0094 
0095 bool DBusInterface::startActivity(const QString &in0)
0096 {
0097 #if KWIN_BUILD_ACTIVITIES
0098     if (!Workspace::self()->activities()) {
0099         return false;
0100     }
0101     return Workspace::self()->activities()->start(in0);
0102 #else
0103     return false;
0104 #endif
0105 }
0106 
0107 bool DBusInterface::stopActivity(const QString &in0)
0108 {
0109 #if KWIN_BUILD_ACTIVITIES
0110     if (!Workspace::self()->activities()) {
0111         return false;
0112     }
0113     return Workspace::self()->activities()->stop(in0);
0114 #else
0115     return false;
0116 #endif
0117 }
0118 
0119 int DBusInterface::currentDesktop()
0120 {
0121     return VirtualDesktopManager::self()->current();
0122 }
0123 
0124 bool DBusInterface::setCurrentDesktop(int desktop)
0125 {
0126     return VirtualDesktopManager::self()->setCurrent(desktop);
0127 }
0128 
0129 void DBusInterface::nextDesktop()
0130 {
0131     VirtualDesktopManager::self()->moveTo(VirtualDesktopManager::Direction::Next);
0132 }
0133 
0134 void DBusInterface::previousDesktop()
0135 {
0136     VirtualDesktopManager::self()->moveTo(VirtualDesktopManager::Direction::Previous);
0137 }
0138 
0139 void DBusInterface::showDebugConsole()
0140 {
0141     DebugConsole *console = new DebugConsole;
0142     console->show();
0143 }
0144 
0145 void DBusInterface::replace()
0146 {
0147     QCoreApplication::exit(133);
0148 }
0149 
0150 namespace
0151 {
0152 QVariantMap clientToVariantMap(const Window *c)
0153 {
0154     return
0155     {
0156         {QStringLiteral("resourceClass"), c->resourceClass()},
0157             {QStringLiteral("resourceName"), c->resourceName()},
0158             {QStringLiteral("desktopFile"), c->desktopFileName()},
0159             {QStringLiteral("role"), c->windowRole()},
0160             {QStringLiteral("caption"), c->captionNormal()},
0161             {QStringLiteral("clientMachine"), c->wmClientMachine(true)},
0162             {QStringLiteral("localhost"), c->isLocalhost()},
0163             {QStringLiteral("type"), c->windowType()},
0164             {QStringLiteral("x"), c->x()},
0165             {QStringLiteral("y"), c->y()},
0166             {QStringLiteral("width"), c->width()},
0167             {QStringLiteral("height"), c->height()},
0168             {QStringLiteral("desktops"), c->desktopIds()},
0169             {QStringLiteral("minimized"), c->isMinimized()},
0170             {QStringLiteral("shaded"), c->isShade()},
0171             {QStringLiteral("fullscreen"), c->isFullScreen()},
0172             {QStringLiteral("keepAbove"), c->keepAbove()},
0173             {QStringLiteral("keepBelow"), c->keepBelow()},
0174             {QStringLiteral("noBorder"), c->noBorder()},
0175             {QStringLiteral("skipTaskbar"), c->skipTaskbar()},
0176             {QStringLiteral("skipPager"), c->skipPager()},
0177             {QStringLiteral("skipSwitcher"), c->skipSwitcher()},
0178             {QStringLiteral("maximizeHorizontal"), c->maximizeMode() & MaximizeHorizontal},
0179             {QStringLiteral("maximizeVertical"), c->maximizeMode() & MaximizeVertical},
0180             {QStringLiteral("uuid"), c->internalId().toString()},
0181 #if KWIN_BUILD_ACTIVITIES
0182             {QStringLiteral("activities"), c->activities()},
0183 #endif
0184     };
0185 }
0186 }
0187 
0188 QVariantMap DBusInterface::queryWindowInfo()
0189 {
0190     m_replyQueryWindowInfo = message();
0191     setDelayedReply(true);
0192     kwinApp()->startInteractiveWindowSelection(
0193         [this](Window *t) {
0194             if (!t) {
0195                 QDBusConnection::sessionBus().send(m_replyQueryWindowInfo.createErrorReply(
0196                     QStringLiteral("org.kde.KWin.Error.UserCancel"),
0197                     QStringLiteral("User cancelled the query")));
0198                 return;
0199             }
0200             if (t->isClient()) {
0201                 QDBusConnection::sessionBus().send(m_replyQueryWindowInfo.createReply(clientToVariantMap(t)));
0202             } else {
0203                 QDBusConnection::sessionBus().send(m_replyQueryWindowInfo.createErrorReply(
0204                     QStringLiteral("org.kde.KWin.Error.InvalidWindow"),
0205                     QStringLiteral("Tried to query information about an unmanaged window")));
0206             }
0207         });
0208     return QVariantMap{};
0209 }
0210 
0211 QVariantMap DBusInterface::getWindowInfo(const QString &uuid)
0212 {
0213     const auto id = QUuid::fromString(uuid);
0214     const auto client = workspace()->findAbstractClient([&id](const Window *c) {
0215         return c->internalId() == id;
0216     });
0217     if (client) {
0218         return clientToVariantMap(client);
0219     } else {
0220         return {};
0221     }
0222 }
0223 
0224 void DBusInterface::showDesktop(bool show)
0225 {
0226     workspace()->setShowingDesktop(show, true);
0227 
0228     auto m = message();
0229     if (m.service().isEmpty()) {
0230         return;
0231     }
0232 
0233     // Keep track of whatever D-Bus client asked to show the desktop. If
0234     // they disappear from the bus, cancel the show desktop state so we do
0235     // not end up in a state where we are stuck showing the desktop.
0236     static QPointer<QDBusServiceWatcher> watcher;
0237 
0238     if (show) {
0239         if (watcher) {
0240             // If we get a second call to `showDesktop(true)`, drop the previous
0241             // watcher and watch the new client. That way, we simply always
0242             // track the last state.
0243             watcher->deleteLater();
0244         }
0245 
0246         watcher = new QDBusServiceWatcher(m.service(), QDBusConnection::sessionBus(), QDBusServiceWatcher::WatchForUnregistration, this);
0247         connect(watcher, &QDBusServiceWatcher::serviceUnregistered, []() {
0248             workspace()->setShowingDesktop(false, true);
0249             watcher->deleteLater();
0250         });
0251     } else if (watcher) {
0252         // Someone cancelled showing the desktop, so there's no more need to
0253         // watch to cancel the show desktop state.
0254         watcher->deleteLater();
0255     }
0256 }
0257 
0258 void DBusInterface::onShowingDesktopChanged(bool show, bool /*animated*/)
0259 {
0260     Q_EMIT showingDesktopChanged(show);
0261 }
0262 
0263 CompositorDBusInterface::CompositorDBusInterface(Compositor *parent)
0264     : QObject(parent)
0265     , m_compositor(parent)
0266 {
0267     connect(m_compositor, &Compositor::compositingToggled, this, &CompositorDBusInterface::compositingToggled);
0268     new CompositingAdaptor(this);
0269     QDBusConnection dbus = QDBusConnection::sessionBus();
0270     dbus.registerObject(QStringLiteral("/Compositor"), this);
0271     dbus.connect(QString(), QStringLiteral("/Compositor"), QStringLiteral("org.kde.kwin.Compositing"),
0272                  QStringLiteral("reinit"), this, SLOT(reinitialize()));
0273 }
0274 
0275 QString CompositorDBusInterface::compositingType() const
0276 {
0277     if (!m_compositor->compositing()) {
0278         return QStringLiteral("none");
0279     }
0280     switch (m_compositor->backend()->compositingType()) {
0281     case OpenGLCompositing:
0282         if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGLES) {
0283             return QStringLiteral("gles");
0284         } else {
0285             return QStringLiteral("gl2");
0286         }
0287     case QPainterCompositing:
0288         return QStringLiteral("qpainter");
0289     case NoCompositing:
0290     default:
0291         return QStringLiteral("none");
0292     }
0293 }
0294 
0295 bool CompositorDBusInterface::isActive() const
0296 {
0297     return m_compositor->isActive();
0298 }
0299 
0300 bool CompositorDBusInterface::isCompositingPossible() const
0301 {
0302     return m_compositor->compositingPossible();
0303 }
0304 
0305 QString CompositorDBusInterface::compositingNotPossibleReason() const
0306 {
0307     return m_compositor->compositingNotPossibleReason();
0308 }
0309 
0310 bool CompositorDBusInterface::isOpenGLBroken() const
0311 {
0312     return m_compositor->openGLCompositingIsBroken();
0313 }
0314 
0315 bool CompositorDBusInterface::platformRequiresCompositing() const
0316 {
0317     return kwinApp()->operationMode() != Application::OperationModeX11; // TODO: Remove this property?
0318 }
0319 
0320 void CompositorDBusInterface::resume()
0321 {
0322     if (kwinApp()->operationMode() == Application::OperationModeX11) {
0323         static_cast<X11Compositor *>(m_compositor)->resume(X11Compositor::ScriptSuspend);
0324     }
0325 }
0326 
0327 void CompositorDBusInterface::suspend()
0328 {
0329     if (kwinApp()->operationMode() == Application::OperationModeX11) {
0330         static_cast<X11Compositor *>(m_compositor)->suspend(X11Compositor::ScriptSuspend);
0331     }
0332 }
0333 
0334 void CompositorDBusInterface::reinitialize()
0335 {
0336     m_compositor->reinitialize();
0337 }
0338 
0339 QStringList CompositorDBusInterface::supportedOpenGLPlatformInterfaces() const
0340 {
0341     QStringList interfaces;
0342     bool supportsGlx = false;
0343 #if HAVE_EPOXY_GLX
0344     supportsGlx = (kwinApp()->operationMode() == Application::OperationModeX11);
0345 #endif
0346     if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGLES) {
0347         supportsGlx = false;
0348     }
0349     if (supportsGlx) {
0350         interfaces << QStringLiteral("glx");
0351     }
0352     interfaces << QStringLiteral("egl");
0353     return interfaces;
0354 }
0355 
0356 VirtualDesktopManagerDBusInterface::VirtualDesktopManagerDBusInterface(VirtualDesktopManager *parent)
0357     : QObject(parent)
0358     , m_manager(parent)
0359 {
0360     qDBusRegisterMetaType<KWin::DBusDesktopDataStruct>();
0361     qDBusRegisterMetaType<KWin::DBusDesktopDataVector>();
0362 
0363     new VirtualDesktopManagerAdaptor(this);
0364     QDBusConnection::sessionBus().registerObject(QStringLiteral("/VirtualDesktopManager"),
0365                                                  QStringLiteral("org.kde.KWin.VirtualDesktopManager"),
0366                                                  this);
0367 
0368     connect(m_manager, &VirtualDesktopManager::currentChanged, this, [this](uint previousDesktop, uint newDesktop) {
0369         Q_EMIT currentChanged(m_manager->currentDesktop()->id());
0370     });
0371 
0372     connect(m_manager, &VirtualDesktopManager::countChanged, this, [this](uint previousCount, uint newCount) {
0373         Q_EMIT countChanged(newCount);
0374         Q_EMIT desktopsChanged(desktops());
0375     });
0376 
0377     connect(m_manager, &VirtualDesktopManager::navigationWrappingAroundChanged, this, [this]() {
0378         Q_EMIT navigationWrappingAroundChanged(isNavigationWrappingAround());
0379     });
0380 
0381     connect(m_manager, &VirtualDesktopManager::rowsChanged, this, &VirtualDesktopManagerDBusInterface::rowsChanged);
0382 
0383     const QVector<VirtualDesktop *> allDesks = m_manager->desktops();
0384     for (auto *vd : allDesks) {
0385         connect(vd, &VirtualDesktop::x11DesktopNumberChanged, this, [this, vd]() {
0386             DBusDesktopDataStruct data{.position = vd->x11DesktopNumber() - 1, .id = vd->id(), .name = vd->name()};
0387             Q_EMIT desktopDataChanged(vd->id(), data);
0388             Q_EMIT desktopsChanged(desktops());
0389         });
0390         connect(vd, &VirtualDesktop::nameChanged, this, [this, vd]() {
0391             DBusDesktopDataStruct data{.position = vd->x11DesktopNumber() - 1, .id = vd->id(), .name = vd->name()};
0392             Q_EMIT desktopDataChanged(vd->id(), data);
0393             Q_EMIT desktopsChanged(desktops());
0394         });
0395     }
0396     connect(m_manager, &VirtualDesktopManager::desktopCreated, this, [this](VirtualDesktop *vd) {
0397         connect(vd, &VirtualDesktop::x11DesktopNumberChanged, this, [this, vd]() {
0398             DBusDesktopDataStruct data{.position = vd->x11DesktopNumber() - 1, .id = vd->id(), .name = vd->name()};
0399             Q_EMIT desktopDataChanged(vd->id(), data);
0400             Q_EMIT desktopsChanged(desktops());
0401         });
0402         connect(vd, &VirtualDesktop::nameChanged, this, [this, vd]() {
0403             DBusDesktopDataStruct data{.position = vd->x11DesktopNumber() - 1, .id = vd->id(), .name = vd->name()};
0404             Q_EMIT desktopDataChanged(vd->id(), data);
0405             Q_EMIT desktopsChanged(desktops());
0406         });
0407         DBusDesktopDataStruct data{.position = vd->x11DesktopNumber() - 1, .id = vd->id(), .name = vd->name()};
0408         Q_EMIT desktopCreated(vd->id(), data);
0409         Q_EMIT desktopsChanged(desktops());
0410     });
0411     connect(m_manager, &VirtualDesktopManager::desktopRemoved, this, [this](VirtualDesktop *vd) {
0412         Q_EMIT desktopRemoved(vd->id());
0413         Q_EMIT desktopsChanged(desktops());
0414     });
0415 }
0416 
0417 uint VirtualDesktopManagerDBusInterface::count() const
0418 {
0419     return m_manager->count();
0420 }
0421 
0422 void VirtualDesktopManagerDBusInterface::setRows(uint rows)
0423 {
0424     if (static_cast<uint>(m_manager->grid().height()) == rows) {
0425         return;
0426     }
0427 
0428     m_manager->setRows(rows);
0429     m_manager->save();
0430 }
0431 
0432 uint VirtualDesktopManagerDBusInterface::rows() const
0433 {
0434     return m_manager->rows();
0435 }
0436 
0437 void VirtualDesktopManagerDBusInterface::setCurrent(const QString &id)
0438 {
0439     if (m_manager->currentDesktop()->id() == id) {
0440         return;
0441     }
0442 
0443     auto *vd = m_manager->desktopForId(id);
0444     if (vd) {
0445         m_manager->setCurrent(vd);
0446     }
0447 }
0448 
0449 QString VirtualDesktopManagerDBusInterface::current() const
0450 {
0451     return m_manager->currentDesktop()->id();
0452 }
0453 
0454 void VirtualDesktopManagerDBusInterface::setNavigationWrappingAround(bool wraps)
0455 {
0456     if (m_manager->isNavigationWrappingAround() == wraps) {
0457         return;
0458     }
0459 
0460     m_manager->setNavigationWrappingAround(wraps);
0461 }
0462 
0463 bool VirtualDesktopManagerDBusInterface::isNavigationWrappingAround() const
0464 {
0465     return m_manager->isNavigationWrappingAround();
0466 }
0467 
0468 DBusDesktopDataVector VirtualDesktopManagerDBusInterface::desktops() const
0469 {
0470     const auto desks = m_manager->desktops();
0471     DBusDesktopDataVector desktopVect;
0472     desktopVect.reserve(m_manager->count());
0473 
0474     std::transform(desks.constBegin(), desks.constEnd(),
0475                    std::back_inserter(desktopVect),
0476                    [](const VirtualDesktop *vd) {
0477                        return DBusDesktopDataStruct{.position = vd->x11DesktopNumber() - 1, .id = vd->id(), .name = vd->name()};
0478                    });
0479 
0480     return desktopVect;
0481 }
0482 
0483 void VirtualDesktopManagerDBusInterface::createDesktop(uint position, const QString &name)
0484 {
0485     m_manager->createVirtualDesktop(position, name);
0486 }
0487 
0488 void VirtualDesktopManagerDBusInterface::setDesktopName(const QString &id, const QString &name)
0489 {
0490     VirtualDesktop *vd = m_manager->desktopForId(id);
0491     if (!vd) {
0492         return;
0493     }
0494     if (vd->name() == name) {
0495         return;
0496     }
0497 
0498     vd->setName(name);
0499     m_manager->save();
0500 }
0501 
0502 void VirtualDesktopManagerDBusInterface::removeDesktop(const QString &id)
0503 {
0504     m_manager->removeVirtualDesktop(id);
0505 }
0506 
0507 PluginManagerDBusInterface::PluginManagerDBusInterface(PluginManager *manager)
0508     : QObject(manager)
0509     , m_manager(manager)
0510 {
0511     new PluginsAdaptor(this);
0512 
0513     QDBusConnection::sessionBus().registerObject(QStringLiteral("/Plugins"),
0514                                                  QStringLiteral("org.kde.KWin.Plugins"),
0515                                                  this);
0516 }
0517 
0518 QStringList PluginManagerDBusInterface::loadedPlugins() const
0519 {
0520     return m_manager->loadedPlugins();
0521 }
0522 
0523 QStringList PluginManagerDBusInterface::availablePlugins() const
0524 {
0525     return m_manager->availablePlugins();
0526 }
0527 
0528 bool PluginManagerDBusInterface::LoadPlugin(const QString &name)
0529 {
0530     return m_manager->loadPlugin(name);
0531 }
0532 
0533 void PluginManagerDBusInterface::UnloadPlugin(const QString &name)
0534 {
0535     m_manager->unloadPlugin(name);
0536 }
0537 
0538 } // namespace