File indexing completed on 2024-05-05 17:44:54
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 <KWayland/Client/connection_thread.h> 0012 #include <KWayland/Client/plasmavirtualdesktop.h> 0013 #include <KWayland/Client/registry.h> 0014 #include <KWindowSystem> 0015 #include <KX11Extras> 0016 0017 #include <QDBusConnection> 0018 #include <QDBusMessage> 0019 #include <QDBusPendingCallWatcher> 0020 #include <QDBusPendingReply> 0021 #include <QDebug> 0022 0023 #include <config-X11.h> 0024 0025 #if HAVE_X11 0026 #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) 0027 #include <private/qtx11extras_p.h> 0028 #else 0029 #include <QX11Info> 0030 #endif 0031 #include <netwm.h> 0032 #endif // HAVE_X11 0033 0034 namespace TaskManager 0035 { 0036 class Q_DECL_HIDDEN VirtualDesktopInfo::Private : public QObject 0037 { 0038 Q_OBJECT 0039 0040 public: 0041 Private(); 0042 virtual ~Private() 0043 { 0044 } 0045 0046 uint refCount = 1; 0047 // Fall back to true if we get an invalid DBus response when asking for the 0048 // user's preference since that's what it was for years and years while 0049 // 425787 was broken. 0050 bool navigationWrappingAround = true; 0051 0052 virtual void init() = 0; 0053 virtual QVariant currentDesktop() const = 0; 0054 virtual int numberOfDesktops() const = 0; 0055 virtual QVariantList desktopIds() const = 0; 0056 virtual QStringList desktopNames() const = 0; 0057 virtual quint32 position(const QVariant &desktop) const = 0; 0058 virtual int desktopLayoutRows() const = 0; 0059 virtual void requestActivate(const QVariant &desktop) = 0; 0060 virtual void requestCreateDesktop(quint32 position) = 0; 0061 virtual void requestRemoveDesktop(quint32 position) = 0; 0062 0063 Q_SIGNALS: 0064 void currentDesktopChanged() const; 0065 void numberOfDesktopsChanged() const; 0066 void desktopIdsChanged() const; 0067 void desktopNamesChanged() const; 0068 void desktopLayoutRowsChanged() const; 0069 void navigationWrappingAroundChanged() const; 0070 0071 protected Q_SLOTS: 0072 void navigationWrappingAroundChanged(bool newVal); 0073 }; 0074 0075 VirtualDesktopInfo::Private::Private() 0076 { 0077 // Connect to navigationWrappingAroundChanged signal 0078 const bool connection = QDBusConnection::sessionBus().connect(QStringLiteral("org.kde.KWin"), 0079 QStringLiteral("/VirtualDesktopManager"), 0080 QStringLiteral("org.kde.KWin.VirtualDesktopManager"), 0081 QStringLiteral("navigationWrappingAroundChanged"), 0082 this, 0083 SLOT(navigationWrappingAroundChanged(bool))); 0084 if (!connection) { 0085 qCWarning(TASKMANAGER_DEBUG) << "Could not connect to org.kde.KWin.VirtualDesktopManager.navigationWrappingAroundChanged signal"; 0086 } 0087 0088 // ...Then get the property's current value 0089 QDBusMessage msg = QDBusMessage::createMethodCall(QStringLiteral("org.kde.KWin"), 0090 QStringLiteral("/VirtualDesktopManager"), 0091 QStringLiteral("org.freedesktop.DBus.Properties"), 0092 QStringLiteral("Get")); 0093 msg.setArguments({QStringLiteral("org.kde.KWin.VirtualDesktopManager"), QStringLiteral("navigationWrappingAround")}); 0094 auto *watcher = new QDBusPendingCallWatcher(QDBusConnection::sessionBus().asyncCall(msg), this); 0095 QObject::connect(watcher, &QDBusPendingCallWatcher::finished, this, [this](QDBusPendingCallWatcher *watcher) { 0096 QDBusPendingReply<QVariant> reply = *watcher; 0097 watcher->deleteLater(); 0098 0099 if (reply.isError()) { 0100 qCWarning(TASKMANAGER_DEBUG) << "Failed to determine whether virtual desktop navigation wrapping is enabled: " << reply.error().message(); 0101 return; 0102 } 0103 0104 navigationWrappingAroundChanged(reply.value().toBool()); 0105 }); 0106 } 0107 0108 void VirtualDesktopInfo::Private::navigationWrappingAroundChanged(bool newVal) 0109 { 0110 if (navigationWrappingAround == newVal) { 0111 return; 0112 } 0113 navigationWrappingAround = newVal; 0114 Q_EMIT navigationWrappingAroundChanged(); 0115 } 0116 0117 #if HAVE_X11 0118 class Q_DECL_HIDDEN VirtualDesktopInfo::XWindowPrivate : public VirtualDesktopInfo::Private 0119 { 0120 Q_OBJECT 0121 public: 0122 XWindowPrivate(); 0123 0124 void init() override; 0125 QVariant currentDesktop() const override; 0126 int numberOfDesktops() const override; 0127 QVariantList desktopIds() const override; 0128 QStringList desktopNames() const override; 0129 quint32 position(const QVariant &desktop) const override; 0130 int desktopLayoutRows() const override; 0131 void requestActivate(const QVariant &desktop) override; 0132 void requestCreateDesktop(quint32 position) override; 0133 void requestRemoveDesktop(quint32 position) override; 0134 }; 0135 0136 VirtualDesktopInfo::XWindowPrivate::XWindowPrivate() 0137 : VirtualDesktopInfo::Private() 0138 { 0139 init(); 0140 } 0141 0142 void VirtualDesktopInfo::XWindowPrivate::init() 0143 { 0144 connect(KX11Extras::self(), &KX11Extras::currentDesktopChanged, this, &VirtualDesktopInfo::XWindowPrivate::currentDesktopChanged); 0145 0146 connect(KX11Extras::self(), &KX11Extras::numberOfDesktopsChanged, this, &VirtualDesktopInfo::XWindowPrivate::numberOfDesktopsChanged); 0147 0148 connect(KX11Extras::self(), &KX11Extras::desktopNamesChanged, this, &VirtualDesktopInfo::XWindowPrivate::desktopNamesChanged); 0149 0150 QDBusConnection dbus = QDBusConnection::sessionBus(); 0151 dbus.connect(QString(), 0152 QStringLiteral("/VirtualDesktopManager"), 0153 QStringLiteral("org.kde.KWin.VirtualDesktopManager"), 0154 QStringLiteral("rowsChanged"), 0155 this, 0156 SIGNAL(desktopLayoutRowsChanged())); 0157 } 0158 0159 QVariant VirtualDesktopInfo::XWindowPrivate::currentDesktop() const 0160 { 0161 return KX11Extras::currentDesktop(); 0162 } 0163 0164 int VirtualDesktopInfo::XWindowPrivate::numberOfDesktops() const 0165 { 0166 return KX11Extras::numberOfDesktops(); 0167 } 0168 0169 QVariantList VirtualDesktopInfo::XWindowPrivate::desktopIds() const 0170 { 0171 QVariantList ids; 0172 0173 for (int i = 1; i <= KX11Extras::numberOfDesktops(); ++i) { 0174 ids << i; 0175 } 0176 0177 return ids; 0178 } 0179 0180 QStringList VirtualDesktopInfo::XWindowPrivate::desktopNames() const 0181 { 0182 QStringList names; 0183 0184 // Virtual desktop numbers start at 1. 0185 for (int i = 1; i <= KX11Extras::numberOfDesktops(); ++i) { 0186 names << KX11Extras::desktopName(i); 0187 } 0188 0189 return names; 0190 } 0191 0192 quint32 VirtualDesktopInfo::XWindowPrivate::position(const QVariant &desktop) const 0193 { 0194 bool ok = false; 0195 0196 const quint32 desktopNumber = desktop.toUInt(&ok); 0197 0198 if (!ok) { 0199 return -1; 0200 } 0201 0202 return desktopNumber; 0203 } 0204 0205 int VirtualDesktopInfo::XWindowPrivate::desktopLayoutRows() const 0206 { 0207 const NETRootInfo info(QX11Info::connection(), NET::NumberOfDesktops | NET::DesktopNames, NET::WM2DesktopLayout); 0208 return info.desktopLayoutColumnsRows().height(); 0209 } 0210 0211 void VirtualDesktopInfo::XWindowPrivate::requestActivate(const QVariant &desktop) 0212 { 0213 bool ok = false; 0214 const int desktopNumber = desktop.toInt(&ok); 0215 0216 // Virtual desktop numbers start at 1. 0217 if (ok && desktopNumber > 0 && desktopNumber <= KX11Extras::numberOfDesktops()) { 0218 KX11Extras::setCurrentDesktop(desktopNumber); 0219 } 0220 } 0221 0222 void VirtualDesktopInfo::XWindowPrivate::requestCreateDesktop(quint32 position) 0223 { 0224 Q_UNUSED(position) 0225 0226 NETRootInfo info(QX11Info::connection(), NET::NumberOfDesktops); 0227 info.setNumberOfDesktops(info.numberOfDesktops() + 1); 0228 } 0229 0230 void VirtualDesktopInfo::XWindowPrivate::requestRemoveDesktop(quint32 position) 0231 { 0232 Q_UNUSED(position) 0233 0234 NETRootInfo info(QX11Info::connection(), NET::NumberOfDesktops); 0235 0236 if (info.numberOfDesktops() > 1) { 0237 info.setNumberOfDesktops(info.numberOfDesktops() - 1); 0238 } 0239 } 0240 #endif // HAVE_X11 0241 0242 class Q_DECL_HIDDEN VirtualDesktopInfo::WaylandPrivate : public VirtualDesktopInfo::Private 0243 { 0244 Q_OBJECT 0245 public: 0246 WaylandPrivate(); 0247 0248 QVariant currentVirtualDesktop; 0249 QStringList virtualDesktops; 0250 KWayland::Client::PlasmaVirtualDesktopManagement *virtualDesktopManagement = nullptr; 0251 0252 void init() override; 0253 void addDesktop(const QString &id, quint32 position); 0254 QVariant currentDesktop() const override; 0255 int numberOfDesktops() const override; 0256 QVariantList desktopIds() const override; 0257 QStringList desktopNames() const override; 0258 quint32 position(const QVariant &desktop) const override; 0259 int desktopLayoutRows() const override; 0260 void requestActivate(const QVariant &desktop) override; 0261 void requestCreateDesktop(quint32 position) override; 0262 void requestRemoveDesktop(quint32 position) override; 0263 }; 0264 0265 VirtualDesktopInfo::WaylandPrivate::WaylandPrivate() 0266 : VirtualDesktopInfo::Private() 0267 { 0268 init(); 0269 } 0270 0271 void VirtualDesktopInfo::WaylandPrivate::init() 0272 { 0273 if (!KWindowSystem::isPlatformWayland()) { 0274 return; 0275 } 0276 0277 KWayland::Client::ConnectionThread *connection = KWayland::Client::ConnectionThread::fromApplication(this); 0278 0279 if (!connection) { 0280 return; 0281 } 0282 0283 KWayland::Client::Registry *registry = new KWayland::Client::Registry(this); 0284 registry->create(connection); 0285 0286 QObject::connect(registry, &KWayland::Client::Registry::plasmaVirtualDesktopManagementAnnounced, [this, registry](quint32 name, quint32 version) { 0287 virtualDesktopManagement = registry->createPlasmaVirtualDesktopManagement(name, version, this); 0288 0289 QObject::connect(virtualDesktopManagement, 0290 &KWayland::Client::PlasmaVirtualDesktopManagement::desktopCreated, 0291 this, 0292 [this](const QString &id, quint32 position) { 0293 addDesktop(id, position); 0294 }); 0295 0296 QObject::connect(virtualDesktopManagement, &KWayland::Client::PlasmaVirtualDesktopManagement::desktopRemoved, this, [this](const QString &id) { 0297 virtualDesktops.removeOne(id); 0298 0299 Q_EMIT numberOfDesktopsChanged(); 0300 Q_EMIT desktopIdsChanged(); 0301 Q_EMIT desktopNamesChanged(); 0302 0303 if (currentVirtualDesktop == id) { 0304 currentVirtualDesktop.clear(); 0305 Q_EMIT currentDesktopChanged(); 0306 } 0307 }); 0308 0309 QObject::connect(virtualDesktopManagement, 0310 &KWayland::Client::PlasmaVirtualDesktopManagement::rowsChanged, 0311 this, 0312 &VirtualDesktopInfo::WaylandPrivate::desktopLayoutRowsChanged); 0313 }); 0314 0315 registry->setup(); 0316 } 0317 0318 void VirtualDesktopInfo::WaylandPrivate::addDesktop(const QString &id, quint32 position) 0319 { 0320 if (virtualDesktops.indexOf(id) != -1) { 0321 return; 0322 } 0323 0324 virtualDesktops.insert(position, id); 0325 0326 Q_EMIT numberOfDesktopsChanged(); 0327 Q_EMIT desktopIdsChanged(); 0328 Q_EMIT desktopNamesChanged(); 0329 0330 const KWayland::Client::PlasmaVirtualDesktop *desktop = virtualDesktopManagement->getVirtualDesktop(id); 0331 0332 QObject::connect(desktop, &KWayland::Client::PlasmaVirtualDesktop::activated, this, [desktop, this]() { 0333 currentVirtualDesktop = desktop->id(); 0334 Q_EMIT currentDesktopChanged(); 0335 }); 0336 0337 QObject::connect(desktop, &KWayland::Client::PlasmaVirtualDesktop::done, this, [this]() { 0338 Q_EMIT desktopNamesChanged(); 0339 }); 0340 0341 if (desktop->isActive()) { 0342 currentVirtualDesktop = id; 0343 Q_EMIT currentDesktopChanged(); 0344 } 0345 } 0346 0347 QVariant VirtualDesktopInfo::WaylandPrivate::currentDesktop() const 0348 { 0349 return currentVirtualDesktop; 0350 } 0351 0352 int VirtualDesktopInfo::WaylandPrivate::numberOfDesktops() const 0353 { 0354 return virtualDesktops.count(); 0355 } 0356 0357 quint32 VirtualDesktopInfo::WaylandPrivate::position(const QVariant &desktop) const 0358 { 0359 return virtualDesktops.indexOf(desktop.toString()); 0360 } 0361 0362 QVariantList VirtualDesktopInfo::WaylandPrivate::desktopIds() const 0363 { 0364 QVariantList ids; 0365 0366 foreach (const QString &id, virtualDesktops) { 0367 ids << id; 0368 } 0369 0370 return ids; 0371 } 0372 0373 QStringList VirtualDesktopInfo::WaylandPrivate::desktopNames() const 0374 { 0375 if (!virtualDesktopManagement) { 0376 return QStringList(); 0377 } 0378 QStringList names; 0379 0380 foreach (const QString &id, virtualDesktops) { 0381 const KWayland::Client::PlasmaVirtualDesktop *desktop = virtualDesktopManagement->getVirtualDesktop(id); 0382 0383 if (desktop) { 0384 names << desktop->name(); 0385 } 0386 } 0387 0388 return names; 0389 } 0390 0391 int VirtualDesktopInfo::WaylandPrivate::desktopLayoutRows() const 0392 { 0393 if (!virtualDesktopManagement) { 0394 return 0; 0395 } 0396 0397 return virtualDesktopManagement->rows(); 0398 } 0399 0400 void VirtualDesktopInfo::WaylandPrivate::requestActivate(const QVariant &desktop) 0401 { 0402 if (!virtualDesktopManagement) { 0403 return; 0404 } 0405 KWayland::Client::PlasmaVirtualDesktop *desktopObj = virtualDesktopManagement->getVirtualDesktop(desktop.toString()); 0406 0407 if (desktopObj) { 0408 desktopObj->requestActivate(); 0409 } 0410 } 0411 0412 void VirtualDesktopInfo::WaylandPrivate::requestCreateDesktop(quint32 position) 0413 { 0414 if (!virtualDesktopManagement) { 0415 return; 0416 } 0417 virtualDesktopManagement->requestCreateVirtualDesktop(i18n("New Desktop"), position); 0418 } 0419 0420 void VirtualDesktopInfo::WaylandPrivate::requestRemoveDesktop(quint32 position) 0421 { 0422 if (!virtualDesktopManagement) { 0423 return; 0424 } 0425 if (virtualDesktops.count() == 1) { 0426 return; 0427 } 0428 0429 if (position > ((quint32)virtualDesktops.count() - 1)) { 0430 return; 0431 } 0432 0433 virtualDesktopManagement->requestRemoveVirtualDesktop(virtualDesktops.at(position)); 0434 } 0435 0436 VirtualDesktopInfo::Private *VirtualDesktopInfo::d = nullptr; 0437 0438 VirtualDesktopInfo::VirtualDesktopInfo(QObject *parent) 0439 : QObject(parent) 0440 { 0441 if (!d) { 0442 #if HAVE_X11 0443 if (KWindowSystem::isPlatformX11()) { 0444 d = new VirtualDesktopInfo::XWindowPrivate; 0445 } else 0446 #endif // HAVE_X11 0447 { 0448 d = new VirtualDesktopInfo::WaylandPrivate; 0449 } 0450 } else { 0451 ++d->refCount; 0452 } 0453 0454 connect(d, &VirtualDesktopInfo::Private::currentDesktopChanged, this, &VirtualDesktopInfo::currentDesktopChanged); 0455 connect(d, &VirtualDesktopInfo::Private::numberOfDesktopsChanged, this, &VirtualDesktopInfo::numberOfDesktopsChanged); 0456 connect(d, &VirtualDesktopInfo::Private::desktopIdsChanged, this, &VirtualDesktopInfo::desktopIdsChanged); 0457 connect(d, &VirtualDesktopInfo::Private::desktopNamesChanged, this, &VirtualDesktopInfo::desktopNamesChanged); 0458 connect(d, &VirtualDesktopInfo::Private::desktopLayoutRowsChanged, this, &VirtualDesktopInfo::desktopLayoutRowsChanged); 0459 } 0460 0461 VirtualDesktopInfo::~VirtualDesktopInfo() 0462 { 0463 --d->refCount; 0464 0465 if (!d->refCount) { 0466 delete d; 0467 d = nullptr; 0468 } 0469 } 0470 0471 QVariant VirtualDesktopInfo::currentDesktop() const 0472 { 0473 return d->currentDesktop(); 0474 } 0475 0476 int VirtualDesktopInfo::numberOfDesktops() const 0477 { 0478 return d->numberOfDesktops(); 0479 } 0480 0481 QVariantList VirtualDesktopInfo::desktopIds() const 0482 { 0483 return d->desktopIds(); 0484 } 0485 0486 QStringList VirtualDesktopInfo::desktopNames() const 0487 { 0488 return d->desktopNames(); 0489 } 0490 0491 quint32 VirtualDesktopInfo::position(const QVariant &desktop) const 0492 { 0493 return d->position(desktop); 0494 } 0495 0496 int VirtualDesktopInfo::desktopLayoutRows() const 0497 { 0498 return d->desktopLayoutRows(); 0499 } 0500 0501 void VirtualDesktopInfo::requestActivate(const QVariant &desktop) 0502 { 0503 d->requestActivate(desktop); 0504 } 0505 0506 void VirtualDesktopInfo::requestCreateDesktop(quint32 position) 0507 { 0508 return d->requestCreateDesktop(position); 0509 } 0510 0511 void VirtualDesktopInfo::requestRemoveDesktop(quint32 position) 0512 { 0513 return d->requestRemoveDesktop(position); 0514 } 0515 0516 bool VirtualDesktopInfo::navigationWrappingAround() const 0517 { 0518 return d->navigationWrappingAround; 0519 } 0520 0521 } 0522 0523 #include "virtualdesktopinfo.moc"