File indexing completed on 2024-05-05 05:38:37
0001 /* 0002 SPDX-FileCopyrightText: 2016 Eike Hein <hein@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL 0005 */ 0006 0007 #include "virtualdesktopinfo.h" 0008 #include "libtaskmanager_debug.h" 0009 0010 #include <KLocalizedString> 0011 #include <KWindowSystem> 0012 #include <KX11Extras> 0013 0014 #include <qwayland-org-kde-plasma-virtual-desktop.h> 0015 0016 #include <QDBusConnection> 0017 #include <QDBusMessage> 0018 #include <QDBusPendingCallWatcher> 0019 #include <QDBusPendingReply> 0020 #include <QGuiApplication> 0021 #include <QWaylandClientExtension> 0022 0023 #include <config-X11.h> 0024 0025 #if HAVE_X11 0026 #include <netwm.h> 0027 #endif // HAVE_X11 0028 0029 namespace X11Info 0030 { 0031 [[nodiscard]] inline auto connection() 0032 { 0033 return qGuiApp->nativeInterface<QNativeInterface::QX11Application>()->connection(); 0034 } 0035 } 0036 0037 namespace TaskManager 0038 { 0039 class Q_DECL_HIDDEN VirtualDesktopInfo::Private : public QObject 0040 { 0041 Q_OBJECT 0042 0043 public: 0044 Private(); 0045 virtual ~Private() 0046 { 0047 } 0048 0049 uint refCount = 1; 0050 // Fall back to true if we get an invalid DBus response when asking for the 0051 // user's preference since that's what it was for years and years while 0052 // 425787 was broken. 0053 bool navigationWrappingAround = true; 0054 0055 virtual void init() = 0; 0056 virtual QVariant currentDesktop() const = 0; 0057 virtual int numberOfDesktops() const = 0; 0058 virtual QVariantList desktopIds() const = 0; 0059 virtual QStringList desktopNames() const = 0; 0060 virtual quint32 position(const QVariant &desktop) const = 0; 0061 virtual int desktopLayoutRows() const = 0; 0062 virtual void requestActivate(const QVariant &desktop) = 0; 0063 virtual void requestCreateDesktop(quint32 position) = 0; 0064 virtual void requestRemoveDesktop(quint32 position) = 0; 0065 0066 Q_SIGNALS: 0067 void currentDesktopChanged() const; 0068 void numberOfDesktopsChanged() const; 0069 void desktopIdsChanged() const; 0070 void desktopNamesChanged() const; 0071 void desktopLayoutRowsChanged() const; 0072 void navigationWrappingAroundChanged() const; 0073 0074 protected Q_SLOTS: 0075 void navigationWrappingAroundChanged(bool newVal); 0076 }; 0077 0078 VirtualDesktopInfo::Private::Private() 0079 { 0080 // Connect to navigationWrappingAroundChanged signal 0081 const bool connection = QDBusConnection::sessionBus().connect(QStringLiteral("org.kde.KWin"), 0082 QStringLiteral("/VirtualDesktopManager"), 0083 QStringLiteral("org.kde.KWin.VirtualDesktopManager"), 0084 QStringLiteral("navigationWrappingAroundChanged"), 0085 this, 0086 SLOT(navigationWrappingAroundChanged(bool))); 0087 if (!connection) { 0088 qCWarning(TASKMANAGER_DEBUG) << "Could not connect to org.kde.KWin.VirtualDesktopManager.navigationWrappingAroundChanged signal"; 0089 } 0090 0091 // ...Then get the property's current value 0092 QDBusMessage msg = QDBusMessage::createMethodCall(QStringLiteral("org.kde.KWin"), 0093 QStringLiteral("/VirtualDesktopManager"), 0094 QStringLiteral("org.freedesktop.DBus.Properties"), 0095 QStringLiteral("Get")); 0096 msg.setArguments({QStringLiteral("org.kde.KWin.VirtualDesktopManager"), QStringLiteral("navigationWrappingAround")}); 0097 auto *watcher = new QDBusPendingCallWatcher(QDBusConnection::sessionBus().asyncCall(msg), this); 0098 QObject::connect(watcher, &QDBusPendingCallWatcher::finished, this, [this](QDBusPendingCallWatcher *watcher) { 0099 QDBusPendingReply<QVariant> reply = *watcher; 0100 watcher->deleteLater(); 0101 0102 if (reply.isError()) { 0103 qCWarning(TASKMANAGER_DEBUG) << "Failed to determine whether virtual desktop navigation wrapping is enabled: " << reply.error().message(); 0104 return; 0105 } 0106 0107 navigationWrappingAroundChanged(reply.value().toBool()); 0108 }); 0109 } 0110 0111 void VirtualDesktopInfo::Private::navigationWrappingAroundChanged(bool newVal) 0112 { 0113 if (navigationWrappingAround == newVal) { 0114 return; 0115 } 0116 navigationWrappingAround = newVal; 0117 Q_EMIT navigationWrappingAroundChanged(); 0118 } 0119 0120 #if HAVE_X11 0121 class Q_DECL_HIDDEN VirtualDesktopInfo::XWindowPrivate : public VirtualDesktopInfo::Private 0122 { 0123 Q_OBJECT 0124 public: 0125 XWindowPrivate(); 0126 0127 void init() override; 0128 QVariant currentDesktop() const override; 0129 int numberOfDesktops() const override; 0130 QVariantList desktopIds() const override; 0131 QStringList desktopNames() const override; 0132 quint32 position(const QVariant &desktop) const override; 0133 int desktopLayoutRows() const override; 0134 void requestActivate(const QVariant &desktop) override; 0135 void requestCreateDesktop(quint32 position) override; 0136 void requestRemoveDesktop(quint32 position) override; 0137 }; 0138 0139 VirtualDesktopInfo::XWindowPrivate::XWindowPrivate() 0140 : VirtualDesktopInfo::Private() 0141 { 0142 init(); 0143 } 0144 0145 void VirtualDesktopInfo::XWindowPrivate::init() 0146 { 0147 connect(KX11Extras::self(), &KX11Extras::currentDesktopChanged, this, &VirtualDesktopInfo::XWindowPrivate::currentDesktopChanged); 0148 0149 connect(KX11Extras::self(), &KX11Extras::numberOfDesktopsChanged, this, &VirtualDesktopInfo::XWindowPrivate::numberOfDesktopsChanged); 0150 0151 connect(KX11Extras::self(), &KX11Extras::desktopNamesChanged, this, &VirtualDesktopInfo::XWindowPrivate::desktopNamesChanged); 0152 0153 QDBusConnection dbus = QDBusConnection::sessionBus(); 0154 dbus.connect(QString(), 0155 QStringLiteral("/VirtualDesktopManager"), 0156 QStringLiteral("org.kde.KWin.VirtualDesktopManager"), 0157 QStringLiteral("rowsChanged"), 0158 this, 0159 SIGNAL(desktopLayoutRowsChanged())); 0160 } 0161 0162 QVariant VirtualDesktopInfo::XWindowPrivate::currentDesktop() const 0163 { 0164 return KX11Extras::currentDesktop(); 0165 } 0166 0167 int VirtualDesktopInfo::XWindowPrivate::numberOfDesktops() const 0168 { 0169 return KX11Extras::numberOfDesktops(); 0170 } 0171 0172 QVariantList VirtualDesktopInfo::XWindowPrivate::desktopIds() const 0173 { 0174 QVariantList ids; 0175 0176 for (int i = 1; i <= KX11Extras::numberOfDesktops(); ++i) { 0177 ids << i; 0178 } 0179 0180 return ids; 0181 } 0182 0183 QStringList VirtualDesktopInfo::XWindowPrivate::desktopNames() const 0184 { 0185 QStringList names; 0186 0187 // Virtual desktop numbers start at 1. 0188 for (int i = 1; i <= KX11Extras::numberOfDesktops(); ++i) { 0189 names << KX11Extras::desktopName(i); 0190 } 0191 0192 return names; 0193 } 0194 0195 quint32 VirtualDesktopInfo::XWindowPrivate::position(const QVariant &desktop) const 0196 { 0197 bool ok = false; 0198 0199 const quint32 desktopNumber = desktop.toUInt(&ok); 0200 0201 if (!ok) { 0202 return -1; 0203 } 0204 0205 return desktopNumber; 0206 } 0207 0208 int VirtualDesktopInfo::XWindowPrivate::desktopLayoutRows() const 0209 { 0210 const NETRootInfo info(X11Info::connection(), NET::NumberOfDesktops | NET::DesktopNames, NET::WM2DesktopLayout); 0211 return info.desktopLayoutColumnsRows().height(); 0212 } 0213 0214 void VirtualDesktopInfo::XWindowPrivate::requestActivate(const QVariant &desktop) 0215 { 0216 bool ok = false; 0217 const int desktopNumber = desktop.toInt(&ok); 0218 0219 // Virtual desktop numbers start at 1. 0220 if (ok && desktopNumber > 0 && desktopNumber <= KX11Extras::numberOfDesktops()) { 0221 KX11Extras::setCurrentDesktop(desktopNumber); 0222 } 0223 } 0224 0225 void VirtualDesktopInfo::XWindowPrivate::requestCreateDesktop(quint32 position) 0226 { 0227 Q_UNUSED(position) 0228 0229 NETRootInfo info(X11Info::connection(), NET::NumberOfDesktops); 0230 info.setNumberOfDesktops(info.numberOfDesktops() + 1); 0231 } 0232 0233 void VirtualDesktopInfo::XWindowPrivate::requestRemoveDesktop(quint32 position) 0234 { 0235 Q_UNUSED(position) 0236 0237 NETRootInfo info(X11Info::connection(), NET::NumberOfDesktops); 0238 0239 if (info.numberOfDesktops() > 1) { 0240 info.setNumberOfDesktops(info.numberOfDesktops() - 1); 0241 } 0242 } 0243 #endif // HAVE_X11 0244 0245 class PlasmaVirtualDesktop : public QObject, public QtWayland::org_kde_plasma_virtual_desktop 0246 { 0247 Q_OBJECT 0248 public: 0249 PlasmaVirtualDesktop(::org_kde_plasma_virtual_desktop *object, const QString &id) 0250 : org_kde_plasma_virtual_desktop(object) 0251 , id(id) 0252 { 0253 } 0254 ~PlasmaVirtualDesktop() 0255 { 0256 wl_proxy_destroy(reinterpret_cast<wl_proxy *>(object())); 0257 } 0258 const QString id; 0259 QString name; 0260 Q_SIGNALS: 0261 void done(); 0262 void activated(); 0263 0264 protected: 0265 void org_kde_plasma_virtual_desktop_name(const QString &name) override 0266 { 0267 this->name = name; 0268 } 0269 void org_kde_plasma_virtual_desktop_done() override 0270 { 0271 Q_EMIT done(); 0272 } 0273 void org_kde_plasma_virtual_desktop_activated() override 0274 { 0275 Q_EMIT activated(); 0276 } 0277 }; 0278 0279 class PlasmaVirtualDesktopManagement : public QWaylandClientExtensionTemplate<PlasmaVirtualDesktopManagement>, 0280 public QtWayland::org_kde_plasma_virtual_desktop_management 0281 { 0282 Q_OBJECT 0283 public: 0284 PlasmaVirtualDesktopManagement() 0285 : QWaylandClientExtensionTemplate(2) 0286 { 0287 connect(this, &QWaylandClientExtension::activeChanged, this, [this] { 0288 if (!isActive()) { 0289 wl_proxy_destroy(reinterpret_cast<wl_proxy *>(object())); 0290 } 0291 }); 0292 } 0293 ~PlasmaVirtualDesktopManagement() 0294 { 0295 if (isActive()) { 0296 wl_proxy_destroy(reinterpret_cast<wl_proxy *>(object())); 0297 } 0298 } 0299 Q_SIGNALS: 0300 void desktopCreated(const QString &id, quint32 position); 0301 void desktopRemoved(const QString &id); 0302 void rowsChanged(const quint32 rows); 0303 0304 protected: 0305 void org_kde_plasma_virtual_desktop_management_desktop_created(const QString &desktop_id, uint32_t position) override 0306 { 0307 Q_EMIT desktopCreated(desktop_id, position); 0308 } 0309 void org_kde_plasma_virtual_desktop_management_desktop_removed(const QString &desktop_id) override 0310 { 0311 Q_EMIT desktopRemoved(desktop_id); 0312 } 0313 void org_kde_plasma_virtual_desktop_management_rows(uint32_t rows) override 0314 { 0315 Q_EMIT rowsChanged(rows); 0316 } 0317 }; 0318 0319 class Q_DECL_HIDDEN VirtualDesktopInfo::WaylandPrivate : public VirtualDesktopInfo::Private 0320 { 0321 Q_OBJECT 0322 public: 0323 WaylandPrivate(); 0324 0325 QVariant currentVirtualDesktop; 0326 std::vector<std::unique_ptr<PlasmaVirtualDesktop>> virtualDesktops; 0327 std::unique_ptr<PlasmaVirtualDesktopManagement> virtualDesktopManagement; 0328 quint32 rows; 0329 0330 auto findDesktop(const QString &id) const; 0331 0332 void init() override; 0333 void addDesktop(const QString &id, quint32 position); 0334 QVariant currentDesktop() const override; 0335 int numberOfDesktops() const override; 0336 QVariantList desktopIds() const override; 0337 QStringList desktopNames() const override; 0338 quint32 position(const QVariant &desktop) const override; 0339 int desktopLayoutRows() const override; 0340 void requestActivate(const QVariant &desktop) override; 0341 void requestCreateDesktop(quint32 position) override; 0342 void requestRemoveDesktop(quint32 position) override; 0343 }; 0344 0345 VirtualDesktopInfo::WaylandPrivate::WaylandPrivate() 0346 : VirtualDesktopInfo::Private() 0347 { 0348 init(); 0349 } 0350 0351 auto VirtualDesktopInfo::WaylandPrivate::findDesktop(const QString &id) const 0352 { 0353 return std::find_if(virtualDesktops.begin(), virtualDesktops.end(), [&id](const std::unique_ptr<PlasmaVirtualDesktop> &desktop) { 0354 return desktop->id == id; 0355 }); 0356 } 0357 0358 void VirtualDesktopInfo::WaylandPrivate::init() 0359 { 0360 if (!KWindowSystem::isPlatformWayland()) { 0361 return; 0362 } 0363 0364 virtualDesktopManagement = std::make_unique<PlasmaVirtualDesktopManagement>(); 0365 0366 connect(virtualDesktopManagement.get(), &PlasmaVirtualDesktopManagement::activeChanged, this, [this] { 0367 if (!virtualDesktopManagement->isActive()) { 0368 rows = 0; 0369 virtualDesktops.clear(); 0370 currentVirtualDesktop.clear(); 0371 Q_EMIT currentDesktopChanged(); 0372 Q_EMIT numberOfDesktopsChanged(); 0373 Q_EMIT navigationWrappingAroundChanged(); 0374 Q_EMIT desktopIdsChanged(); 0375 Q_EMIT desktopNamesChanged(); 0376 Q_EMIT desktopLayoutRowsChanged(); 0377 } 0378 }); 0379 0380 connect(virtualDesktopManagement.get(), &PlasmaVirtualDesktopManagement::desktopCreated, this, &WaylandPrivate::addDesktop); 0381 0382 connect(virtualDesktopManagement.get(), &PlasmaVirtualDesktopManagement::desktopRemoved, this, [this](const QString &id) { 0383 std::erase_if(virtualDesktops, [id](const std::unique_ptr<PlasmaVirtualDesktop> &desktop) { 0384 return desktop->id == id; 0385 }); 0386 0387 Q_EMIT numberOfDesktopsChanged(); 0388 Q_EMIT desktopIdsChanged(); 0389 Q_EMIT desktopNamesChanged(); 0390 0391 if (currentVirtualDesktop == id) { 0392 currentVirtualDesktop.clear(); 0393 Q_EMIT currentDesktopChanged(); 0394 } 0395 }); 0396 0397 connect(virtualDesktopManagement.get(), &PlasmaVirtualDesktopManagement::rowsChanged, this, [this](quint32 rows) { 0398 this->rows = rows; 0399 Q_EMIT desktopLayoutRowsChanged(); 0400 }); 0401 } 0402 0403 void VirtualDesktopInfo::WaylandPrivate::addDesktop(const QString &id, quint32 position) 0404 { 0405 if (findDesktop(id) != virtualDesktops.end()) { 0406 return; 0407 } 0408 0409 auto desktop = std::make_unique<PlasmaVirtualDesktop>(virtualDesktopManagement->get_virtual_desktop(id), id); 0410 0411 connect(desktop.get(), &PlasmaVirtualDesktop::activated, this, [id, this]() { 0412 currentVirtualDesktop = id; 0413 Q_EMIT currentDesktopChanged(); 0414 }); 0415 0416 connect(desktop.get(), &PlasmaVirtualDesktop::done, this, [this]() { 0417 Q_EMIT desktopNamesChanged(); 0418 }); 0419 0420 virtualDesktops.insert(std::next(virtualDesktops.begin(), position), std::move(desktop)); 0421 0422 Q_EMIT numberOfDesktopsChanged(); 0423 Q_EMIT desktopIdsChanged(); 0424 Q_EMIT desktopNamesChanged(); 0425 } 0426 0427 QVariant VirtualDesktopInfo::WaylandPrivate::currentDesktop() const 0428 { 0429 return currentVirtualDesktop; 0430 } 0431 0432 int VirtualDesktopInfo::WaylandPrivate::numberOfDesktops() const 0433 { 0434 return virtualDesktops.size(); 0435 } 0436 0437 quint32 VirtualDesktopInfo::WaylandPrivate::position(const QVariant &desktop) const 0438 { 0439 return std::distance(virtualDesktops.begin(), findDesktop(desktop.toString())); 0440 } 0441 0442 QVariantList VirtualDesktopInfo::WaylandPrivate::desktopIds() const 0443 { 0444 QVariantList ids; 0445 ids.reserve(virtualDesktops.size()); 0446 0447 std::transform(virtualDesktops.cbegin(), virtualDesktops.cend(), std::back_inserter(ids), [](const std::unique_ptr<PlasmaVirtualDesktop> &desktop) { 0448 return desktop->id; 0449 }); 0450 return ids; 0451 } 0452 0453 QStringList VirtualDesktopInfo::WaylandPrivate::desktopNames() const 0454 { 0455 if (!virtualDesktopManagement->isActive()) { 0456 return QStringList(); 0457 } 0458 QStringList names; 0459 names.reserve(virtualDesktops.size()); 0460 0461 std::transform(virtualDesktops.cbegin(), virtualDesktops.cend(), std::back_inserter(names), [](const std::unique_ptr<PlasmaVirtualDesktop> &desktop) { 0462 return desktop->name; 0463 }); 0464 return names; 0465 } 0466 0467 int VirtualDesktopInfo::WaylandPrivate::desktopLayoutRows() const 0468 { 0469 if (!virtualDesktopManagement->isActive()) { 0470 return 0; 0471 } 0472 0473 return rows; 0474 } 0475 0476 void VirtualDesktopInfo::WaylandPrivate::requestActivate(const QVariant &desktop) 0477 { 0478 if (!virtualDesktopManagement->isActive()) { 0479 return; 0480 } 0481 0482 if (auto it = findDesktop(desktop.toString()); it != virtualDesktops.end()) { 0483 (*it)->request_activate(); 0484 } 0485 } 0486 0487 void VirtualDesktopInfo::WaylandPrivate::requestCreateDesktop(quint32 position) 0488 { 0489 if (!virtualDesktopManagement->isActive()) { 0490 return; 0491 } 0492 virtualDesktopManagement->request_create_virtual_desktop(i18n("New Desktop"), position); 0493 } 0494 0495 void VirtualDesktopInfo::WaylandPrivate::requestRemoveDesktop(quint32 position) 0496 { 0497 if (!virtualDesktopManagement->isActive()) { 0498 return; 0499 } 0500 if (virtualDesktops.size() == 1) { 0501 return; 0502 } 0503 0504 if (position > (virtualDesktops.size() - 1)) { 0505 return; 0506 } 0507 0508 virtualDesktopManagement->request_remove_virtual_desktop(virtualDesktops.at(position)->id); 0509 } 0510 0511 VirtualDesktopInfo::Private *VirtualDesktopInfo::d = nullptr; 0512 0513 VirtualDesktopInfo::VirtualDesktopInfo(QObject *parent) 0514 : QObject(parent) 0515 { 0516 if (!d) { 0517 #if HAVE_X11 0518 if (KWindowSystem::isPlatformX11()) { 0519 d = new VirtualDesktopInfo::XWindowPrivate; 0520 } else 0521 #endif // HAVE_X11 0522 { 0523 d = new VirtualDesktopInfo::WaylandPrivate; 0524 } 0525 } else { 0526 ++d->refCount; 0527 } 0528 0529 connect(d, &VirtualDesktopInfo::Private::currentDesktopChanged, this, &VirtualDesktopInfo::currentDesktopChanged); 0530 connect(d, &VirtualDesktopInfo::Private::numberOfDesktopsChanged, this, &VirtualDesktopInfo::numberOfDesktopsChanged); 0531 connect(d, &VirtualDesktopInfo::Private::desktopIdsChanged, this, &VirtualDesktopInfo::desktopIdsChanged); 0532 connect(d, &VirtualDesktopInfo::Private::desktopNamesChanged, this, &VirtualDesktopInfo::desktopNamesChanged); 0533 connect(d, &VirtualDesktopInfo::Private::desktopLayoutRowsChanged, this, &VirtualDesktopInfo::desktopLayoutRowsChanged); 0534 } 0535 0536 VirtualDesktopInfo::~VirtualDesktopInfo() 0537 { 0538 --d->refCount; 0539 0540 if (!d->refCount) { 0541 delete d; 0542 d = nullptr; 0543 } 0544 } 0545 0546 QVariant VirtualDesktopInfo::currentDesktop() const 0547 { 0548 return d->currentDesktop(); 0549 } 0550 0551 int VirtualDesktopInfo::numberOfDesktops() const 0552 { 0553 return d->numberOfDesktops(); 0554 } 0555 0556 QVariantList VirtualDesktopInfo::desktopIds() const 0557 { 0558 return d->desktopIds(); 0559 } 0560 0561 QStringList VirtualDesktopInfo::desktopNames() const 0562 { 0563 return d->desktopNames(); 0564 } 0565 0566 quint32 VirtualDesktopInfo::position(const QVariant &desktop) const 0567 { 0568 return d->position(desktop); 0569 } 0570 0571 int VirtualDesktopInfo::desktopLayoutRows() const 0572 { 0573 return d->desktopLayoutRows(); 0574 } 0575 0576 void VirtualDesktopInfo::requestActivate(const QVariant &desktop) 0577 { 0578 d->requestActivate(desktop); 0579 } 0580 0581 void VirtualDesktopInfo::requestCreateDesktop(quint32 position) 0582 { 0583 return d->requestCreateDesktop(position); 0584 } 0585 0586 void VirtualDesktopInfo::requestRemoveDesktop(quint32 position) 0587 { 0588 return d->requestRemoveDesktop(position); 0589 } 0590 0591 bool VirtualDesktopInfo::navigationWrappingAround() const 0592 { 0593 return d->navigationWrappingAround; 0594 } 0595 0596 } 0597 0598 #include "virtualdesktopinfo.moc"