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

0001 /*
0002     KWin - the KDE window manager
0003     This file is part of the KDE project.
0004 
0005     SPDX-FileCopyrightText: 2009 Lucas Murray <lmurray@undefinedfire.com>
0006     SPDX-FileCopyrightText: 2012 Martin Gräßlin <mgraesslin@kde.org>
0007 
0008     SPDX-License-Identifier: GPL-2.0-or-later
0009 */
0010 #include "virtualdesktops.h"
0011 #include "input.h"
0012 #include "wayland/plasmavirtualdesktop.h"
0013 // KDE
0014 #include <KConfigGroup>
0015 #include <KGlobalAccel>
0016 #include <KLocalizedString>
0017 #include <NETWM>
0018 
0019 // Qt
0020 #include <QAction>
0021 #include <QDebug>
0022 #include <QUuid>
0023 
0024 #include <algorithm>
0025 
0026 namespace KWin
0027 {
0028 
0029 static bool s_loadingDesktopSettings = false;
0030 static const double GESTURE_SWITCH_THRESHOLD = .25;
0031 
0032 static QString generateDesktopId()
0033 {
0034     return QUuid::createUuid().toString(QUuid::WithoutBraces);
0035 }
0036 
0037 VirtualDesktop::VirtualDesktop(QObject *parent)
0038     : QObject(parent)
0039 {
0040 }
0041 
0042 VirtualDesktop::~VirtualDesktop()
0043 {
0044     Q_EMIT aboutToBeDestroyed();
0045 }
0046 
0047 void VirtualDesktopManager::setVirtualDesktopManagement(PlasmaVirtualDesktopManagementInterface *management)
0048 {
0049     Q_ASSERT(!m_virtualDesktopManagement);
0050     m_virtualDesktopManagement = management;
0051 
0052     auto createPlasmaVirtualDesktop = [this](VirtualDesktop *desktop) {
0053         PlasmaVirtualDesktopInterface *pvd = m_virtualDesktopManagement->createDesktop(desktop->id(), desktop->x11DesktopNumber() - 1);
0054         pvd->setName(desktop->name());
0055         pvd->sendDone();
0056 
0057         connect(desktop, &VirtualDesktop::nameChanged, pvd, [desktop, pvd]() {
0058             pvd->setName(desktop->name());
0059             pvd->sendDone();
0060         });
0061         connect(pvd, &PlasmaVirtualDesktopInterface::activateRequested, this, [this, desktop]() {
0062             setCurrent(desktop);
0063         });
0064     };
0065 
0066     connect(this, &VirtualDesktopManager::desktopAdded, m_virtualDesktopManagement, createPlasmaVirtualDesktop);
0067 
0068     connect(this, &VirtualDesktopManager::rowsChanged, m_virtualDesktopManagement, [this](uint rows) {
0069         m_virtualDesktopManagement->setRows(rows);
0070         m_virtualDesktopManagement->sendDone();
0071     });
0072 
0073     // handle removed: from VirtualDesktopManager to the wayland interface
0074     connect(this, &VirtualDesktopManager::desktopRemoved, m_virtualDesktopManagement, [this](VirtualDesktop *desktop) {
0075         m_virtualDesktopManagement->removeDesktop(desktop->id());
0076     });
0077 
0078     // create a new desktop when the client asks to
0079     connect(m_virtualDesktopManagement, &PlasmaVirtualDesktopManagementInterface::desktopCreateRequested, this, [this](const QString &name, quint32 position) {
0080         createVirtualDesktop(position, name);
0081     });
0082 
0083     // remove when the client asks to
0084     connect(m_virtualDesktopManagement, &PlasmaVirtualDesktopManagementInterface::desktopRemoveRequested, this, [this](const QString &id) {
0085         // here there can be some nice kauthorized check?
0086         // remove only from VirtualDesktopManager, the other connections will remove it from m_virtualDesktopManagement as well
0087         removeVirtualDesktop(id);
0088     });
0089 
0090     connect(this, &VirtualDesktopManager::currentChanged, m_virtualDesktopManagement, [this]() {
0091         const QList<PlasmaVirtualDesktopInterface *> deskIfaces = m_virtualDesktopManagement->desktops();
0092         for (auto *deskInt : deskIfaces) {
0093             if (deskInt->id() == currentDesktop()->id()) {
0094                 deskInt->setActive(true);
0095             } else {
0096                 deskInt->setActive(false);
0097             }
0098         }
0099     });
0100 
0101     std::for_each(m_desktops.constBegin(), m_desktops.constEnd(), createPlasmaVirtualDesktop);
0102 
0103     m_virtualDesktopManagement->setRows(rows());
0104     m_virtualDesktopManagement->sendDone();
0105 }
0106 
0107 void VirtualDesktop::setId(const QString &id)
0108 {
0109     Q_ASSERT(m_id.isEmpty());
0110     m_id = id;
0111 }
0112 
0113 void VirtualDesktop::setX11DesktopNumber(uint number)
0114 {
0115     // x11DesktopNumber can be changed now
0116     if (static_cast<uint>(m_x11DesktopNumber) == number) {
0117         return;
0118     }
0119 
0120     m_x11DesktopNumber = number;
0121 
0122     if (m_x11DesktopNumber != 0) {
0123         Q_EMIT x11DesktopNumberChanged();
0124     }
0125 }
0126 
0127 void VirtualDesktop::setName(const QString &name)
0128 {
0129     if (m_name == name) {
0130         return;
0131     }
0132     m_name = name;
0133     Q_EMIT nameChanged();
0134 }
0135 
0136 VirtualDesktopGrid::VirtualDesktopGrid()
0137     : m_size(1, 2) // Default to tow rows
0138     , m_grid(QList<QList<VirtualDesktop *>>{QList<VirtualDesktop *>{}, QList<VirtualDesktop *>{}})
0139 {
0140 }
0141 
0142 VirtualDesktopGrid::~VirtualDesktopGrid() = default;
0143 
0144 void VirtualDesktopGrid::update(const QSize &size, const QList<VirtualDesktop *> &desktops)
0145 {
0146     // Set private variables
0147     m_size = size;
0148     const uint width = size.width();
0149     const uint height = size.height();
0150 
0151     m_grid.clear();
0152     auto it = desktops.begin();
0153     auto end = desktops.end();
0154     for (uint y = 0; y < height; ++y) {
0155         QList<VirtualDesktop *> row;
0156         for (uint x = 0; x < width && it != end; ++x) {
0157             row << *it;
0158             it++;
0159         }
0160         m_grid << row;
0161     }
0162 }
0163 
0164 QPoint VirtualDesktopGrid::gridCoords(uint id) const
0165 {
0166     return gridCoords(VirtualDesktopManager::self()->desktopForX11Id(id));
0167 }
0168 
0169 QPoint VirtualDesktopGrid::gridCoords(VirtualDesktop *vd) const
0170 {
0171     for (int y = 0; y < m_grid.count(); ++y) {
0172         const auto &row = m_grid.at(y);
0173         for (int x = 0; x < row.count(); ++x) {
0174             if (row.at(x) == vd) {
0175                 return QPoint(x, y);
0176             }
0177         }
0178     }
0179     return QPoint(-1, -1);
0180 }
0181 
0182 VirtualDesktop *VirtualDesktopGrid::at(const QPoint &coords) const
0183 {
0184     if (coords.y() >= m_grid.count()) {
0185         return nullptr;
0186     }
0187     const auto &row = m_grid.at(coords.y());
0188     if (coords.x() >= row.count()) {
0189         return nullptr;
0190     }
0191     return row.at(coords.x());
0192 }
0193 
0194 KWIN_SINGLETON_FACTORY_VARIABLE(VirtualDesktopManager, s_manager)
0195 
0196 VirtualDesktopManager::VirtualDesktopManager(QObject *parent)
0197     : QObject(parent)
0198     , m_navigationWrapsAround(false)
0199     , m_rootInfo(nullptr)
0200     , m_swipeGestureReleasedY(new QAction(this))
0201     , m_swipeGestureReleasedX(new QAction(this))
0202 {
0203 }
0204 
0205 VirtualDesktopManager::~VirtualDesktopManager()
0206 {
0207     s_manager = nullptr;
0208 }
0209 
0210 void VirtualDesktopManager::setRootInfo(NETRootInfo *info)
0211 {
0212     m_rootInfo = info;
0213 
0214     // Nothing will be connected to rootInfo
0215     if (m_rootInfo) {
0216         updateRootInfo();
0217         m_rootInfo->setCurrentDesktop(currentDesktop()->x11DesktopNumber());
0218         for (auto *vd : std::as_const(m_desktops)) {
0219             m_rootInfo->setDesktopName(vd->x11DesktopNumber(), vd->name().toUtf8().data());
0220         }
0221     }
0222 }
0223 
0224 VirtualDesktop *VirtualDesktopManager::inDirection(VirtualDesktop *desktop, Direction direction, bool wrap)
0225 {
0226     switch (direction) {
0227     case Direction::Up:
0228         return above(desktop, wrap);
0229     case Direction::Down:
0230         return below(desktop, wrap);
0231     case Direction::Right:
0232         return toRight(desktop, wrap);
0233     case Direction::Left:
0234         return toLeft(desktop, wrap);
0235     case Direction::Next:
0236         return next(desktop, wrap);
0237     case Direction::Previous:
0238         return previous(desktop, wrap);
0239     }
0240     Q_UNREACHABLE();
0241 }
0242 
0243 uint VirtualDesktopManager::inDirection(uint desktop, Direction direction, bool wrap)
0244 {
0245     return inDirection(desktopForX11Id(desktop), direction, wrap)->x11DesktopNumber();
0246 }
0247 
0248 void VirtualDesktopManager::moveTo(Direction direction, bool wrap)
0249 {
0250     setCurrent(inDirection(nullptr, direction, wrap));
0251 }
0252 
0253 VirtualDesktop *VirtualDesktopManager::above(VirtualDesktop *desktop, bool wrap) const
0254 {
0255     Q_ASSERT(m_current);
0256     if (!desktop) {
0257         desktop = m_current;
0258     }
0259     QPoint coords = m_grid.gridCoords(desktop);
0260     Q_ASSERT(coords.x() >= 0);
0261     while (true) {
0262         coords.ry()--;
0263         if (coords.y() < 0) {
0264             if (wrap) {
0265                 coords.setY(m_grid.height() - 1);
0266             } else {
0267                 return desktop; // Already at the top-most desktop
0268             }
0269         }
0270         if (VirtualDesktop *vd = m_grid.at(coords)) {
0271             return vd;
0272         }
0273     }
0274     return nullptr;
0275 }
0276 
0277 VirtualDesktop *VirtualDesktopManager::toRight(VirtualDesktop *desktop, bool wrap) const
0278 {
0279     Q_ASSERT(m_current);
0280     if (!desktop) {
0281         desktop = m_current;
0282     }
0283     QPoint coords = m_grid.gridCoords(desktop);
0284     Q_ASSERT(coords.x() >= 0);
0285     while (true) {
0286         coords.rx()++;
0287         if (coords.x() >= m_grid.width()) {
0288             if (wrap) {
0289                 coords.setX(0);
0290             } else {
0291                 return desktop; // Already at the right-most desktop
0292             }
0293         }
0294         if (VirtualDesktop *vd = m_grid.at(coords)) {
0295             return vd;
0296         }
0297     }
0298     return nullptr;
0299 }
0300 
0301 VirtualDesktop *VirtualDesktopManager::below(VirtualDesktop *desktop, bool wrap) const
0302 {
0303     Q_ASSERT(m_current);
0304     if (!desktop) {
0305         desktop = m_current;
0306     }
0307     QPoint coords = m_grid.gridCoords(desktop);
0308     Q_ASSERT(coords.x() >= 0);
0309     while (true) {
0310         coords.ry()++;
0311         if (coords.y() >= m_grid.height()) {
0312             if (wrap) {
0313                 coords.setY(0);
0314             } else {
0315                 // Already at the bottom-most desktop
0316                 return desktop;
0317             }
0318         }
0319         if (VirtualDesktop *vd = m_grid.at(coords)) {
0320             return vd;
0321         }
0322     }
0323     return nullptr;
0324 }
0325 
0326 VirtualDesktop *VirtualDesktopManager::toLeft(VirtualDesktop *desktop, bool wrap) const
0327 {
0328     Q_ASSERT(m_current);
0329     if (!desktop) {
0330         desktop = m_current;
0331     }
0332     QPoint coords = m_grid.gridCoords(desktop);
0333     Q_ASSERT(coords.x() >= 0);
0334     while (true) {
0335         coords.rx()--;
0336         if (coords.x() < 0) {
0337             if (wrap) {
0338                 coords.setX(m_grid.width() - 1);
0339             } else {
0340                 return desktop; // Already at the left-most desktop
0341             }
0342         }
0343         if (VirtualDesktop *vd = m_grid.at(coords)) {
0344             return vd;
0345         }
0346     }
0347     return nullptr;
0348 }
0349 
0350 VirtualDesktop *VirtualDesktopManager::next(VirtualDesktop *desktop, bool wrap) const
0351 {
0352     Q_ASSERT(m_current);
0353     if (!desktop) {
0354         desktop = m_current;
0355     }
0356     auto it = std::find(m_desktops.begin(), m_desktops.end(), desktop);
0357     Q_ASSERT(it != m_desktops.end());
0358     it++;
0359     if (it == m_desktops.end()) {
0360         if (wrap) {
0361             return m_desktops.first();
0362         } else {
0363             return desktop;
0364         }
0365     }
0366     return *it;
0367 }
0368 
0369 VirtualDesktop *VirtualDesktopManager::previous(VirtualDesktop *desktop, bool wrap) const
0370 {
0371     Q_ASSERT(m_current);
0372     if (!desktop) {
0373         desktop = m_current;
0374     }
0375     auto it = std::find(m_desktops.begin(), m_desktops.end(), desktop);
0376     Q_ASSERT(it != m_desktops.end());
0377     if (it == m_desktops.begin()) {
0378         if (wrap) {
0379             return m_desktops.last();
0380         } else {
0381             return desktop;
0382         }
0383     }
0384     it--;
0385     return *it;
0386 }
0387 
0388 VirtualDesktop *VirtualDesktopManager::desktopForX11Id(uint id) const
0389 {
0390     if (id == 0 || id > count()) {
0391         return nullptr;
0392     }
0393     return m_desktops.at(id - 1);
0394 }
0395 
0396 VirtualDesktop *VirtualDesktopManager::desktopForId(const QString &id) const
0397 {
0398     auto desk = std::find_if(
0399         m_desktops.constBegin(),
0400         m_desktops.constEnd(),
0401         [id](const VirtualDesktop *desk) {
0402             return desk->id() == id;
0403         });
0404 
0405     if (desk != m_desktops.constEnd()) {
0406         return *desk;
0407     }
0408 
0409     return nullptr;
0410 }
0411 
0412 VirtualDesktop *VirtualDesktopManager::createVirtualDesktop(uint position, const QString &name)
0413 {
0414     // too many, can't insert new ones
0415     if ((uint)m_desktops.count() == VirtualDesktopManager::maximum()) {
0416         return nullptr;
0417     }
0418 
0419     position = std::clamp(position, 0u, static_cast<uint>(m_desktops.count()));
0420 
0421     QString desktopName = name;
0422     if (desktopName.isEmpty()) {
0423         desktopName = defaultName(position + 1);
0424     }
0425 
0426     auto *vd = new VirtualDesktop(this);
0427     vd->setX11DesktopNumber(position + 1);
0428     vd->setId(generateDesktopId());
0429     vd->setName(desktopName);
0430 
0431     connect(vd, &VirtualDesktop::nameChanged, this, [this, vd]() {
0432         if (m_rootInfo) {
0433             m_rootInfo->setDesktopName(vd->x11DesktopNumber(), vd->name().toUtf8().data());
0434         }
0435     });
0436 
0437     if (m_rootInfo) {
0438         m_rootInfo->setDesktopName(vd->x11DesktopNumber(), vd->name().toUtf8().data());
0439     }
0440 
0441     m_desktops.insert(position, vd);
0442 
0443     // update the id of displaced desktops
0444     for (uint i = position + 1; i < (uint)m_desktops.count(); ++i) {
0445         m_desktops[i]->setX11DesktopNumber(i + 1);
0446         if (m_rootInfo) {
0447             m_rootInfo->setDesktopName(i + 1, m_desktops[i]->name().toUtf8().data());
0448         }
0449     }
0450 
0451     save();
0452 
0453     updateLayout();
0454     updateRootInfo();
0455     Q_EMIT desktopAdded(vd);
0456     Q_EMIT countChanged(m_desktops.count() - 1, m_desktops.count());
0457     return vd;
0458 }
0459 
0460 void VirtualDesktopManager::removeVirtualDesktop(const QString &id)
0461 {
0462     auto desktop = desktopForId(id);
0463     if (desktop) {
0464         removeVirtualDesktop(desktop);
0465     }
0466 }
0467 
0468 void VirtualDesktopManager::removeVirtualDesktop(VirtualDesktop *desktop)
0469 {
0470     // don't end up without any desktop
0471     if (m_desktops.count() == 1) {
0472         return;
0473     }
0474 
0475     const int i = m_desktops.indexOf(desktop);
0476     m_desktops.remove(i);
0477 
0478     for (int j = i; j < m_desktops.count(); ++j) {
0479         m_desktops[j]->setX11DesktopNumber(j + 1);
0480         if (m_rootInfo) {
0481             m_rootInfo->setDesktopName(j + 1, m_desktops[j]->name().toUtf8().data());
0482         }
0483     }
0484 
0485     if (m_current == desktop) {
0486         m_current = (i < m_desktops.count()) ? m_desktops.at(i) : m_desktops.constLast();
0487         Q_EMIT currentChanged(desktop, m_current);
0488     }
0489 
0490     updateLayout();
0491     updateRootInfo();
0492     save();
0493 
0494     Q_EMIT desktopRemoved(desktop);
0495     Q_EMIT countChanged(m_desktops.count() + 1, m_desktops.count());
0496 
0497     desktop->deleteLater();
0498 }
0499 
0500 uint VirtualDesktopManager::current() const
0501 {
0502     return m_current ? m_current->x11DesktopNumber() : 0;
0503 }
0504 
0505 VirtualDesktop *VirtualDesktopManager::currentDesktop() const
0506 {
0507     return m_current;
0508 }
0509 
0510 bool VirtualDesktopManager::setCurrent(uint newDesktop)
0511 {
0512     if (newDesktop < 1 || newDesktop > count()) {
0513         return false;
0514     }
0515     auto d = desktopForX11Id(newDesktop);
0516     Q_ASSERT(d);
0517     return setCurrent(d);
0518 }
0519 
0520 bool VirtualDesktopManager::setCurrent(VirtualDesktop *newDesktop)
0521 {
0522     Q_ASSERT(newDesktop);
0523     if (m_current == newDesktop) {
0524         return false;
0525     }
0526     VirtualDesktop *oldDesktop = currentDesktop();
0527     m_current = newDesktop;
0528     Q_EMIT currentChanged(oldDesktop, newDesktop);
0529     return true;
0530 }
0531 
0532 void VirtualDesktopManager::setCount(uint count)
0533 {
0534     count = std::clamp<uint>(count, 1, VirtualDesktopManager::maximum());
0535     if (count == uint(m_desktops.count())) {
0536         // nothing to change
0537         return;
0538     }
0539     QList<VirtualDesktop *> newDesktops;
0540     const uint oldCount = m_desktops.count();
0541     // this explicit check makes it more readable
0542     if ((uint)m_desktops.count() > count) {
0543         const auto desktopsToRemove = m_desktops.mid(count);
0544         m_desktops.resize(count);
0545         if (m_current && desktopsToRemove.contains(m_current)) {
0546             VirtualDesktop *oldCurrent = m_current;
0547             m_current = m_desktops.last();
0548             Q_EMIT currentChanged(oldCurrent, m_current);
0549         }
0550         for (auto desktop : desktopsToRemove) {
0551             Q_EMIT desktopRemoved(desktop);
0552             desktop->deleteLater();
0553         }
0554     } else {
0555         while (uint(m_desktops.count()) < count) {
0556             auto vd = new VirtualDesktop(this);
0557             const int x11Number = m_desktops.count() + 1;
0558             vd->setX11DesktopNumber(x11Number);
0559             vd->setName(defaultName(x11Number));
0560             if (!s_loadingDesktopSettings) {
0561                 vd->setId(generateDesktopId());
0562             }
0563             m_desktops << vd;
0564             newDesktops << vd;
0565             connect(vd, &VirtualDesktop::nameChanged, this, [this, vd]() {
0566                 if (m_rootInfo) {
0567                     m_rootInfo->setDesktopName(vd->x11DesktopNumber(), vd->name().toUtf8().data());
0568                 }
0569             });
0570             if (m_rootInfo) {
0571                 m_rootInfo->setDesktopName(vd->x11DesktopNumber(), vd->name().toUtf8().data());
0572             }
0573         }
0574     }
0575 
0576     if (!m_current) {
0577         m_current = m_desktops.at(0);
0578     }
0579 
0580     updateLayout();
0581     updateRootInfo();
0582 
0583     if (!s_loadingDesktopSettings) {
0584         save();
0585     }
0586     for (auto vd : std::as_const(newDesktops)) {
0587         Q_EMIT desktopAdded(vd);
0588     }
0589     Q_EMIT countChanged(oldCount, m_desktops.count());
0590 }
0591 
0592 uint VirtualDesktopManager::rows() const
0593 {
0594     return m_rows;
0595 }
0596 
0597 void VirtualDesktopManager::setRows(uint rows)
0598 {
0599     if (rows == 0 || rows > count() || rows == m_rows) {
0600         return;
0601     }
0602 
0603     m_rows = rows;
0604 
0605     updateLayout();
0606     updateRootInfo();
0607 }
0608 
0609 void VirtualDesktopManager::updateRootInfo()
0610 {
0611     if (m_rootInfo) {
0612         const int n = count();
0613         m_rootInfo->setNumberOfDesktops(n);
0614         NETPoint *viewports = new NETPoint[n];
0615         m_rootInfo->setDesktopViewport(n, *viewports);
0616         delete[] viewports;
0617         m_rootInfo->setDesktopLayout(NET::OrientationHorizontal, m_grid.width(), m_grid.height(), NET::DesktopLayoutCornerTopLeft);
0618     }
0619 }
0620 
0621 void VirtualDesktopManager::updateLayout()
0622 {
0623     m_rows = std::min(m_rows, count());
0624 
0625     int columns = count() / m_rows;
0626     if (count() % m_rows > 0) {
0627         columns++;
0628     }
0629 
0630     m_grid.update(QSize(columns, m_rows), m_desktops);
0631 
0632     Q_EMIT layoutChanged(columns, m_rows);
0633     Q_EMIT rowsChanged(m_rows);
0634 }
0635 
0636 void VirtualDesktopManager::load()
0637 {
0638     s_loadingDesktopSettings = true;
0639     if (!m_config) {
0640         return;
0641     }
0642 
0643     KConfigGroup group(m_config, QStringLiteral("Desktops"));
0644     const int n = group.readEntry("Number", 1);
0645     setCount(n);
0646 
0647     for (int i = 1; i <= n; i++) {
0648         QString s = group.readEntry(QStringLiteral("Name_%1").arg(i), i18n("Desktop %1", i));
0649         if (m_rootInfo) {
0650             m_rootInfo->setDesktopName(i, s.toUtf8().data());
0651         }
0652         m_desktops[i - 1]->setName(s);
0653 
0654         const QString sId = group.readEntry(QStringLiteral("Id_%1").arg(i), QString());
0655 
0656         if (m_desktops[i - 1]->id().isEmpty()) {
0657             m_desktops[i - 1]->setId(sId.isEmpty() ? generateDesktopId() : sId);
0658         } else {
0659             Q_ASSERT(sId.isEmpty() || m_desktops[i - 1]->id() == sId);
0660         }
0661 
0662         // TODO: update desktop focus chain, why?
0663         //         m_desktopFocusChain.value()[i-1] = i;
0664     }
0665 
0666     int rows = group.readEntry<int>("Rows", 2);
0667     m_rows = std::clamp(rows, 1, n);
0668 
0669     s_loadingDesktopSettings = false;
0670 }
0671 
0672 void VirtualDesktopManager::save()
0673 {
0674     if (s_loadingDesktopSettings) {
0675         return;
0676     }
0677     if (!m_config) {
0678         return;
0679     }
0680     KConfigGroup group(m_config, QStringLiteral("Desktops"));
0681 
0682     for (int i = count() + 1; group.hasKey(QStringLiteral("Id_%1").arg(i)); i++) {
0683         group.deleteEntry(QStringLiteral("Id_%1").arg(i));
0684         group.deleteEntry(QStringLiteral("Name_%1").arg(i));
0685     }
0686 
0687     group.writeEntry("Number", count());
0688     for (VirtualDesktop *desktop : std::as_const(m_desktops)) {
0689         const uint position = desktop->x11DesktopNumber();
0690 
0691         QString s = desktop->name();
0692         const QString defaultvalue = defaultName(position);
0693         if (s.isEmpty()) {
0694             s = defaultvalue;
0695             if (m_rootInfo) {
0696                 m_rootInfo->setDesktopName(position, s.toUtf8().data());
0697             }
0698         }
0699 
0700         if (s != defaultvalue) {
0701             group.writeEntry(QStringLiteral("Name_%1").arg(position), s);
0702         } else {
0703             QString currentvalue = group.readEntry(QStringLiteral("Name_%1").arg(position), QString());
0704             if (currentvalue != defaultvalue) {
0705                 group.deleteEntry(QStringLiteral("Name_%1").arg(position));
0706             }
0707         }
0708         group.writeEntry(QStringLiteral("Id_%1").arg(position), desktop->id());
0709     }
0710 
0711     group.writeEntry("Rows", m_rows);
0712 
0713     // Save to disk
0714     group.sync();
0715 }
0716 
0717 QString VirtualDesktopManager::defaultName(int desktop) const
0718 {
0719     return i18n("Desktop %1", desktop);
0720 }
0721 
0722 void VirtualDesktopManager::initShortcuts()
0723 {
0724     initSwitchToShortcuts();
0725 
0726     addAction(QStringLiteral("Switch to Next Desktop"), i18n("Switch to Next Desktop"), QKeySequence(), &VirtualDesktopManager::slotNext);
0727     addAction(QStringLiteral("Switch to Previous Desktop"), i18n("Switch to Previous Desktop"), QKeySequence(), &VirtualDesktopManager::slotPrevious);
0728 
0729     // shortcuts
0730     addAction(QStringLiteral("Switch One Desktop to the Right"), i18n("Switch One Desktop to the Right"), QKeySequence(Qt::CTRL | Qt::META | Qt::Key_Right), &VirtualDesktopManager::slotRight);
0731     addAction(QStringLiteral("Switch One Desktop to the Left"), i18n("Switch One Desktop to the Left"), QKeySequence(Qt::CTRL | Qt::META | Qt::Key_Left), &VirtualDesktopManager::slotLeft);
0732     addAction(QStringLiteral("Switch One Desktop Up"), i18n("Switch One Desktop Up"), QKeySequence(Qt::CTRL | Qt::META | Qt::Key_Up), &VirtualDesktopManager::slotUp);
0733     addAction(QStringLiteral("Switch One Desktop Down"), i18n("Switch One Desktop Down"), QKeySequence(Qt::CTRL | Qt::META | Qt::Key_Down), &VirtualDesktopManager::slotDown);
0734 
0735     // Gestures
0736     // These connections decide which desktop to end on after gesture ends
0737     connect(m_swipeGestureReleasedX.get(), &QAction::triggered, this, &VirtualDesktopManager::gestureReleasedX);
0738     connect(m_swipeGestureReleasedY.get(), &QAction::triggered, this, &VirtualDesktopManager::gestureReleasedY);
0739 
0740     const auto left = [this](qreal cb) {
0741         if (grid().width() > 1) {
0742             m_currentDesktopOffset.setX(cb);
0743             Q_EMIT currentChanging(currentDesktop(), m_currentDesktopOffset);
0744         }
0745     };
0746     const auto right = [this](qreal cb) {
0747         if (grid().width() > 1) {
0748             m_currentDesktopOffset.setX(-cb);
0749             Q_EMIT currentChanging(currentDesktop(), m_currentDesktopOffset);
0750         }
0751     };
0752     input()->registerTouchpadSwipeShortcut(SwipeDirection::Left, 3, m_swipeGestureReleasedX.get(), left);
0753     input()->registerTouchpadSwipeShortcut(SwipeDirection::Right, 3, m_swipeGestureReleasedX.get(), right);
0754     input()->registerTouchpadSwipeShortcut(SwipeDirection::Left, 4, m_swipeGestureReleasedX.get(), left);
0755     input()->registerTouchpadSwipeShortcut(SwipeDirection::Right, 4, m_swipeGestureReleasedX.get(), right);
0756     input()->registerTouchpadSwipeShortcut(SwipeDirection::Down, 3, m_swipeGestureReleasedY.get(), [this](qreal cb) {
0757         if (grid().height() > 1) {
0758             m_currentDesktopOffset.setY(-cb);
0759             Q_EMIT currentChanging(currentDesktop(), m_currentDesktopOffset);
0760         }
0761     });
0762     input()->registerTouchpadSwipeShortcut(SwipeDirection::Up, 3, m_swipeGestureReleasedY.get(), [this](qreal cb) {
0763         if (grid().height() > 1) {
0764             m_currentDesktopOffset.setY(cb);
0765             Q_EMIT currentChanging(currentDesktop(), m_currentDesktopOffset);
0766         }
0767     });
0768     input()->registerTouchscreenSwipeShortcut(SwipeDirection::Left, 3, m_swipeGestureReleasedX.get(), left);
0769     input()->registerTouchscreenSwipeShortcut(SwipeDirection::Right, 3, m_swipeGestureReleasedX.get(), right);
0770 
0771     // axis events
0772     input()->registerAxisShortcut(Qt::MetaModifier | Qt::AltModifier, PointerAxisDown,
0773                                   findChild<QAction *>(QStringLiteral("Switch to Next Desktop")));
0774     input()->registerAxisShortcut(Qt::MetaModifier | Qt::AltModifier, PointerAxisUp,
0775                                   findChild<QAction *>(QStringLiteral("Switch to Previous Desktop")));
0776 }
0777 
0778 void VirtualDesktopManager::gestureReleasedY()
0779 {
0780     // Note that if desktop wrapping is disabled and there's no desktop above or below,
0781     // above() and below() will return the current desktop.
0782     VirtualDesktop *target = m_current;
0783     if (m_currentDesktopOffset.y() <= -GESTURE_SWITCH_THRESHOLD) {
0784         target = above(m_current, isNavigationWrappingAround());
0785     } else if (m_currentDesktopOffset.y() >= GESTURE_SWITCH_THRESHOLD) {
0786         target = below(m_current, isNavigationWrappingAround());
0787     }
0788 
0789     // If the current desktop has not changed, consider that the gesture has been canceled.
0790     if (m_current != target) {
0791         setCurrent(target);
0792     } else {
0793         Q_EMIT currentChangingCancelled();
0794     }
0795     m_currentDesktopOffset = QPointF(0, 0);
0796 }
0797 
0798 void VirtualDesktopManager::gestureReleasedX()
0799 {
0800     // Note that if desktop wrapping is disabled and there's no desktop to left or right,
0801     // toLeft() and toRight() will return the current desktop.
0802     VirtualDesktop *target = m_current;
0803     if (m_currentDesktopOffset.x() <= -GESTURE_SWITCH_THRESHOLD) {
0804         target = toLeft(m_current, isNavigationWrappingAround());
0805     } else if (m_currentDesktopOffset.x() >= GESTURE_SWITCH_THRESHOLD) {
0806         target = toRight(m_current, isNavigationWrappingAround());
0807     }
0808 
0809     // If the current desktop has not changed, consider that the gesture has been canceled.
0810     if (m_current != target) {
0811         setCurrent(target);
0812     } else {
0813         Q_EMIT currentChangingCancelled();
0814     }
0815     m_currentDesktopOffset = QPointF(0, 0);
0816 }
0817 
0818 void VirtualDesktopManager::initSwitchToShortcuts()
0819 {
0820     const QString toDesktop = QStringLiteral("Switch to Desktop %1");
0821     const KLocalizedString toDesktopLabel = ki18n("Switch to Desktop %1");
0822     addAction(toDesktop, toDesktopLabel, 1, QKeySequence(Qt::CTRL | Qt::Key_F1), &VirtualDesktopManager::slotSwitchTo);
0823     addAction(toDesktop, toDesktopLabel, 2, QKeySequence(Qt::CTRL | Qt::Key_F2), &VirtualDesktopManager::slotSwitchTo);
0824     addAction(toDesktop, toDesktopLabel, 3, QKeySequence(Qt::CTRL | Qt::Key_F3), &VirtualDesktopManager::slotSwitchTo);
0825     addAction(toDesktop, toDesktopLabel, 4, QKeySequence(Qt::CTRL | Qt::Key_F4), &VirtualDesktopManager::slotSwitchTo);
0826 
0827     for (uint i = 5; i <= maximum(); ++i) {
0828         addAction(toDesktop, toDesktopLabel, i, QKeySequence(), &VirtualDesktopManager::slotSwitchTo);
0829     }
0830 }
0831 
0832 QAction *VirtualDesktopManager::addAction(const QString &name, const KLocalizedString &label, uint value, const QKeySequence &key, void (VirtualDesktopManager::*slot)())
0833 {
0834     QAction *a = new QAction(this);
0835     a->setProperty("componentName", QStringLiteral("kwin"));
0836     a->setObjectName(name.arg(value));
0837     a->setText(label.subs(value).toString());
0838     a->setData(value);
0839     KGlobalAccel::setGlobalShortcut(a, key);
0840     connect(a, &QAction::triggered, this, slot);
0841     return a;
0842 }
0843 
0844 QAction *VirtualDesktopManager::addAction(const QString &name, const QString &label, const QKeySequence &key, void (VirtualDesktopManager::*slot)())
0845 {
0846     QAction *a = new QAction(this);
0847     a->setProperty("componentName", QStringLiteral("kwin"));
0848     a->setObjectName(name);
0849     a->setText(label);
0850     KGlobalAccel::setGlobalShortcut(a, key);
0851     connect(a, &QAction::triggered, this, slot);
0852     return a;
0853 }
0854 
0855 void VirtualDesktopManager::slotSwitchTo()
0856 {
0857     QAction *act = qobject_cast<QAction *>(sender());
0858     if (!act) {
0859         return;
0860     }
0861     bool ok = false;
0862     const uint i = act->data().toUInt(&ok);
0863     if (!ok) {
0864         return;
0865     }
0866     setCurrent(i);
0867 }
0868 
0869 void VirtualDesktopManager::setNavigationWrappingAround(bool enabled)
0870 {
0871     if (enabled == m_navigationWrapsAround) {
0872         return;
0873     }
0874     m_navigationWrapsAround = enabled;
0875     Q_EMIT navigationWrappingAroundChanged();
0876 }
0877 
0878 void VirtualDesktopManager::slotDown()
0879 {
0880     moveTo(Direction::Down, isNavigationWrappingAround());
0881 }
0882 
0883 void VirtualDesktopManager::slotLeft()
0884 {
0885     moveTo(Direction::Left, isNavigationWrappingAround());
0886 }
0887 
0888 void VirtualDesktopManager::slotPrevious()
0889 {
0890     moveTo(Direction::Previous, isNavigationWrappingAround());
0891 }
0892 
0893 void VirtualDesktopManager::slotNext()
0894 {
0895     moveTo(Direction::Next, isNavigationWrappingAround());
0896 }
0897 
0898 void VirtualDesktopManager::slotRight()
0899 {
0900     moveTo(Direction::Right, isNavigationWrappingAround());
0901 }
0902 
0903 void VirtualDesktopManager::slotUp()
0904 {
0905     moveTo(Direction::Up, isNavigationWrappingAround());
0906 }
0907 
0908 } // KWin
0909 
0910 #include "moc_virtualdesktops.cpp"