File indexing completed on 2025-04-20 08:02:55
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 std::for_each(m_desktops.constBegin(), m_desktops.constEnd(), createPlasmaVirtualDesktop); 0091 0092 // Now we are sure all ids are there 0093 save(); 0094 0095 connect(this, &VirtualDesktopManager::currentChanged, m_virtualDesktopManagement, [this]() { 0096 const QList<PlasmaVirtualDesktopInterface *> deskIfaces = m_virtualDesktopManagement->desktops(); 0097 for (auto *deskInt : deskIfaces) { 0098 if (deskInt->id() == currentDesktop()->id()) { 0099 deskInt->setActive(true); 0100 } else { 0101 deskInt->setActive(false); 0102 } 0103 } 0104 }); 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 int columns = count() / m_rows; 0217 if (count() % m_rows > 0) { 0218 columns++; 0219 } 0220 m_rootInfo->setDesktopLayout(NET::OrientationHorizontal, columns, m_rows, NET::DesktopLayoutCornerTopLeft); 0221 updateRootInfo(); 0222 m_rootInfo->setCurrentDesktop(currentDesktop()->x11DesktopNumber()); 0223 for (auto *vd : std::as_const(m_desktops)) { 0224 m_rootInfo->setDesktopName(vd->x11DesktopNumber(), vd->name().toUtf8().data()); 0225 } 0226 } 0227 } 0228 0229 VirtualDesktop *VirtualDesktopManager::inDirection(VirtualDesktop *desktop, Direction direction, bool wrap) 0230 { 0231 switch (direction) { 0232 case Direction::Up: 0233 return above(desktop, wrap); 0234 case Direction::Down: 0235 return below(desktop, wrap); 0236 case Direction::Right: 0237 return toRight(desktop, wrap); 0238 case Direction::Left: 0239 return toLeft(desktop, wrap); 0240 case Direction::Next: 0241 return next(desktop, wrap); 0242 case Direction::Previous: 0243 return previous(desktop, wrap); 0244 } 0245 Q_UNREACHABLE(); 0246 } 0247 0248 uint VirtualDesktopManager::inDirection(uint desktop, Direction direction, bool wrap) 0249 { 0250 return inDirection(desktopForX11Id(desktop), direction, wrap)->x11DesktopNumber(); 0251 } 0252 0253 void VirtualDesktopManager::moveTo(Direction direction, bool wrap) 0254 { 0255 setCurrent(inDirection(nullptr, direction, wrap)); 0256 } 0257 0258 VirtualDesktop *VirtualDesktopManager::above(VirtualDesktop *desktop, bool wrap) const 0259 { 0260 Q_ASSERT(m_current); 0261 if (!desktop) { 0262 desktop = m_current; 0263 } 0264 QPoint coords = m_grid.gridCoords(desktop); 0265 Q_ASSERT(coords.x() >= 0); 0266 while (true) { 0267 coords.ry()--; 0268 if (coords.y() < 0) { 0269 if (wrap) { 0270 coords.setY(m_grid.height() - 1); 0271 } else { 0272 return desktop; // Already at the top-most desktop 0273 } 0274 } 0275 if (VirtualDesktop *vd = m_grid.at(coords)) { 0276 return vd; 0277 } 0278 } 0279 return nullptr; 0280 } 0281 0282 VirtualDesktop *VirtualDesktopManager::toRight(VirtualDesktop *desktop, bool wrap) const 0283 { 0284 Q_ASSERT(m_current); 0285 if (!desktop) { 0286 desktop = m_current; 0287 } 0288 QPoint coords = m_grid.gridCoords(desktop); 0289 Q_ASSERT(coords.x() >= 0); 0290 while (true) { 0291 coords.rx()++; 0292 if (coords.x() >= m_grid.width()) { 0293 if (wrap) { 0294 coords.setX(0); 0295 } else { 0296 return desktop; // Already at the right-most desktop 0297 } 0298 } 0299 if (VirtualDesktop *vd = m_grid.at(coords)) { 0300 return vd; 0301 } 0302 } 0303 return nullptr; 0304 } 0305 0306 VirtualDesktop *VirtualDesktopManager::below(VirtualDesktop *desktop, bool wrap) const 0307 { 0308 Q_ASSERT(m_current); 0309 if (!desktop) { 0310 desktop = m_current; 0311 } 0312 QPoint coords = m_grid.gridCoords(desktop); 0313 Q_ASSERT(coords.x() >= 0); 0314 while (true) { 0315 coords.ry()++; 0316 if (coords.y() >= m_grid.height()) { 0317 if (wrap) { 0318 coords.setY(0); 0319 } else { 0320 // Already at the bottom-most desktop 0321 return desktop; 0322 } 0323 } 0324 if (VirtualDesktop *vd = m_grid.at(coords)) { 0325 return vd; 0326 } 0327 } 0328 return nullptr; 0329 } 0330 0331 VirtualDesktop *VirtualDesktopManager::toLeft(VirtualDesktop *desktop, bool wrap) const 0332 { 0333 Q_ASSERT(m_current); 0334 if (!desktop) { 0335 desktop = m_current; 0336 } 0337 QPoint coords = m_grid.gridCoords(desktop); 0338 Q_ASSERT(coords.x() >= 0); 0339 while (true) { 0340 coords.rx()--; 0341 if (coords.x() < 0) { 0342 if (wrap) { 0343 coords.setX(m_grid.width() - 1); 0344 } else { 0345 return desktop; // Already at the left-most desktop 0346 } 0347 } 0348 if (VirtualDesktop *vd = m_grid.at(coords)) { 0349 return vd; 0350 } 0351 } 0352 return nullptr; 0353 } 0354 0355 VirtualDesktop *VirtualDesktopManager::next(VirtualDesktop *desktop, bool wrap) const 0356 { 0357 Q_ASSERT(m_current); 0358 if (!desktop) { 0359 desktop = m_current; 0360 } 0361 auto it = std::find(m_desktops.begin(), m_desktops.end(), desktop); 0362 Q_ASSERT(it != m_desktops.end()); 0363 it++; 0364 if (it == m_desktops.end()) { 0365 if (wrap) { 0366 return m_desktops.first(); 0367 } else { 0368 return desktop; 0369 } 0370 } 0371 return *it; 0372 } 0373 0374 VirtualDesktop *VirtualDesktopManager::previous(VirtualDesktop *desktop, bool wrap) const 0375 { 0376 Q_ASSERT(m_current); 0377 if (!desktop) { 0378 desktop = m_current; 0379 } 0380 auto it = std::find(m_desktops.begin(), m_desktops.end(), desktop); 0381 Q_ASSERT(it != m_desktops.end()); 0382 if (it == m_desktops.begin()) { 0383 if (wrap) { 0384 return m_desktops.last(); 0385 } else { 0386 return desktop; 0387 } 0388 } 0389 it--; 0390 return *it; 0391 } 0392 0393 VirtualDesktop *VirtualDesktopManager::desktopForX11Id(uint id) const 0394 { 0395 if (id == 0 || id > count()) { 0396 return nullptr; 0397 } 0398 return m_desktops.at(id - 1); 0399 } 0400 0401 VirtualDesktop *VirtualDesktopManager::desktopForId(const QString &id) const 0402 { 0403 auto desk = std::find_if( 0404 m_desktops.constBegin(), 0405 m_desktops.constEnd(), 0406 [id](const VirtualDesktop *desk) { 0407 return desk->id() == id; 0408 }); 0409 0410 if (desk != m_desktops.constEnd()) { 0411 return *desk; 0412 } 0413 0414 return nullptr; 0415 } 0416 0417 VirtualDesktop *VirtualDesktopManager::createVirtualDesktop(uint position, const QString &name) 0418 { 0419 // too many, can't insert new ones 0420 if ((uint)m_desktops.count() == VirtualDesktopManager::maximum()) { 0421 return nullptr; 0422 } 0423 0424 position = std::clamp(position, 0u, static_cast<uint>(m_desktops.count())); 0425 0426 QString desktopName = name; 0427 if (desktopName.isEmpty()) { 0428 desktopName = defaultName(position + 1); 0429 } 0430 0431 auto *vd = new VirtualDesktop(this); 0432 vd->setX11DesktopNumber(position + 1); 0433 vd->setId(generateDesktopId()); 0434 vd->setName(desktopName); 0435 0436 connect(vd, &VirtualDesktop::nameChanged, this, [this, vd]() { 0437 if (m_rootInfo) { 0438 m_rootInfo->setDesktopName(vd->x11DesktopNumber(), vd->name().toUtf8().data()); 0439 } 0440 }); 0441 0442 if (m_rootInfo) { 0443 m_rootInfo->setDesktopName(vd->x11DesktopNumber(), vd->name().toUtf8().data()); 0444 } 0445 0446 m_desktops.insert(position, vd); 0447 0448 // update the id of displaced desktops 0449 for (uint i = position + 1; i < (uint)m_desktops.count(); ++i) { 0450 m_desktops[i]->setX11DesktopNumber(i + 1); 0451 if (m_rootInfo) { 0452 m_rootInfo->setDesktopName(i + 1, m_desktops[i]->name().toUtf8().data()); 0453 } 0454 } 0455 0456 save(); 0457 0458 updateRootInfo(); 0459 Q_EMIT desktopAdded(vd); 0460 Q_EMIT countChanged(m_desktops.count() - 1, m_desktops.count()); 0461 return vd; 0462 } 0463 0464 void VirtualDesktopManager::removeVirtualDesktop(const QString &id) 0465 { 0466 auto desktop = desktopForId(id); 0467 if (desktop) { 0468 removeVirtualDesktop(desktop); 0469 } 0470 } 0471 0472 void VirtualDesktopManager::removeVirtualDesktop(VirtualDesktop *desktop) 0473 { 0474 // don't end up without any desktop 0475 if (m_desktops.count() == 1) { 0476 return; 0477 } 0478 0479 const int i = m_desktops.indexOf(desktop); 0480 m_desktops.remove(i); 0481 0482 for (int j = i; j < m_desktops.count(); ++j) { 0483 m_desktops[j]->setX11DesktopNumber(j + 1); 0484 if (m_rootInfo) { 0485 m_rootInfo->setDesktopName(j + 1, m_desktops[j]->name().toUtf8().data()); 0486 } 0487 } 0488 0489 if (m_current == desktop) { 0490 m_current = (i < m_desktops.count()) ? m_desktops.at(i) : m_desktops.constLast(); 0491 Q_EMIT currentChanged(desktop, m_current); 0492 } 0493 0494 updateRootInfo(); 0495 save(); 0496 0497 Q_EMIT desktopRemoved(desktop); 0498 Q_EMIT countChanged(m_desktops.count() + 1, m_desktops.count()); 0499 0500 desktop->deleteLater(); 0501 } 0502 0503 uint VirtualDesktopManager::current() const 0504 { 0505 return m_current ? m_current->x11DesktopNumber() : 0; 0506 } 0507 0508 VirtualDesktop *VirtualDesktopManager::currentDesktop() const 0509 { 0510 return m_current; 0511 } 0512 0513 bool VirtualDesktopManager::setCurrent(uint newDesktop) 0514 { 0515 if (newDesktop < 1 || newDesktop > count()) { 0516 return false; 0517 } 0518 auto d = desktopForX11Id(newDesktop); 0519 Q_ASSERT(d); 0520 return setCurrent(d); 0521 } 0522 0523 bool VirtualDesktopManager::setCurrent(VirtualDesktop *newDesktop) 0524 { 0525 Q_ASSERT(newDesktop); 0526 if (m_current == newDesktop) { 0527 return false; 0528 } 0529 VirtualDesktop *oldDesktop = currentDesktop(); 0530 m_current = newDesktop; 0531 Q_EMIT currentChanged(oldDesktop, newDesktop); 0532 return true; 0533 } 0534 0535 void VirtualDesktopManager::setCount(uint count) 0536 { 0537 count = std::clamp<uint>(count, 1, VirtualDesktopManager::maximum()); 0538 if (count == uint(m_desktops.count())) { 0539 // nothing to change 0540 return; 0541 } 0542 QList<VirtualDesktop *> newDesktops; 0543 const uint oldCount = m_desktops.count(); 0544 // this explicit check makes it more readable 0545 if ((uint)m_desktops.count() > count) { 0546 const auto desktopsToRemove = m_desktops.mid(count); 0547 m_desktops.resize(count); 0548 if (m_current && desktopsToRemove.contains(m_current)) { 0549 VirtualDesktop *oldCurrent = m_current; 0550 m_current = m_desktops.last(); 0551 Q_EMIT currentChanged(oldCurrent, m_current); 0552 } 0553 for (auto desktop : desktopsToRemove) { 0554 Q_EMIT desktopRemoved(desktop); 0555 desktop->deleteLater(); 0556 } 0557 } else { 0558 while (uint(m_desktops.count()) < count) { 0559 auto vd = new VirtualDesktop(this); 0560 const int x11Number = m_desktops.count() + 1; 0561 vd->setX11DesktopNumber(x11Number); 0562 vd->setName(defaultName(x11Number)); 0563 if (!s_loadingDesktopSettings) { 0564 vd->setId(generateDesktopId()); 0565 } 0566 m_desktops << vd; 0567 newDesktops << vd; 0568 connect(vd, &VirtualDesktop::nameChanged, this, [this, vd]() { 0569 if (m_rootInfo) { 0570 m_rootInfo->setDesktopName(vd->x11DesktopNumber(), vd->name().toUtf8().data()); 0571 } 0572 }); 0573 if (m_rootInfo) { 0574 m_rootInfo->setDesktopName(vd->x11DesktopNumber(), vd->name().toUtf8().data()); 0575 } 0576 } 0577 } 0578 0579 if (!m_current) { 0580 m_current = m_desktops.at(0); 0581 } 0582 0583 updateRootInfo(); 0584 0585 if (!s_loadingDesktopSettings) { 0586 save(); 0587 } 0588 for (auto vd : std::as_const(newDesktops)) { 0589 Q_EMIT desktopAdded(vd); 0590 } 0591 Q_EMIT countChanged(oldCount, m_desktops.count()); 0592 } 0593 0594 uint VirtualDesktopManager::rows() const 0595 { 0596 return m_rows; 0597 } 0598 0599 void VirtualDesktopManager::setRows(uint rows) 0600 { 0601 if (rows == 0 || rows > count() || rows == m_rows) { 0602 return; 0603 } 0604 0605 m_rows = rows; 0606 0607 int columns = count() / m_rows; 0608 if (count() % m_rows > 0) { 0609 columns++; 0610 } 0611 if (m_rootInfo) { 0612 m_rootInfo->setDesktopLayout(NET::OrientationHorizontal, columns, m_rows, NET::DesktopLayoutCornerTopLeft); 0613 m_rootInfo->activate(); 0614 } 0615 0616 updateLayout(); 0617 0618 // rowsChanged will be emitted by setNETDesktopLayout called by updateLayout 0619 } 0620 0621 void VirtualDesktopManager::updateRootInfo() 0622 { 0623 if (!m_rootInfo) { 0624 // Make sure the layout is still valid 0625 updateLayout(); 0626 return; 0627 } 0628 const int n = count(); 0629 m_rootInfo->setNumberOfDesktops(n); 0630 NETPoint *viewports = new NETPoint[n]; 0631 m_rootInfo->setDesktopViewport(n, *viewports); 0632 delete[] viewports; 0633 // Make sure the layout is still valid 0634 updateLayout(); 0635 } 0636 0637 void VirtualDesktopManager::updateLayout() 0638 { 0639 m_rows = std::min(m_rows, count()); 0640 0641 int columns = count() / m_rows; 0642 if (count() % m_rows > 0) { 0643 columns++; 0644 } 0645 0646 m_grid.update(QSize(columns, m_rows), m_desktops); 0647 // TODO: why is there no call to m_rootInfo->setDesktopLayout? 0648 0649 Q_EMIT layoutChanged(columns, m_rows); 0650 Q_EMIT rowsChanged(m_rows); 0651 } 0652 0653 void VirtualDesktopManager::load() 0654 { 0655 s_loadingDesktopSettings = true; 0656 if (!m_config) { 0657 return; 0658 } 0659 0660 KConfigGroup group(m_config, QStringLiteral("Desktops")); 0661 const int n = group.readEntry("Number", 1); 0662 setCount(n); 0663 0664 for (int i = 1; i <= n; i++) { 0665 QString s = group.readEntry(QStringLiteral("Name_%1").arg(i), i18n("Desktop %1", i)); 0666 if (m_rootInfo) { 0667 m_rootInfo->setDesktopName(i, s.toUtf8().data()); 0668 } 0669 m_desktops[i - 1]->setName(s); 0670 0671 const QString sId = group.readEntry(QStringLiteral("Id_%1").arg(i), QString()); 0672 0673 if (m_desktops[i - 1]->id().isEmpty()) { 0674 m_desktops[i - 1]->setId(sId.isEmpty() ? generateDesktopId() : sId); 0675 } else { 0676 Q_ASSERT(sId.isEmpty() || m_desktops[i - 1]->id() == sId); 0677 } 0678 0679 // TODO: update desktop focus chain, why? 0680 // m_desktopFocusChain.value()[i-1] = i; 0681 } 0682 0683 int rows = group.readEntry<int>("Rows", 2); 0684 m_rows = std::clamp(rows, 1, n); 0685 0686 s_loadingDesktopSettings = false; 0687 } 0688 0689 void VirtualDesktopManager::save() 0690 { 0691 if (s_loadingDesktopSettings) { 0692 return; 0693 } 0694 if (!m_config) { 0695 return; 0696 } 0697 KConfigGroup group(m_config, QStringLiteral("Desktops")); 0698 0699 for (int i = count() + 1; group.hasKey(QStringLiteral("Id_%1").arg(i)); i++) { 0700 group.deleteEntry(QStringLiteral("Id_%1").arg(i)); 0701 group.deleteEntry(QStringLiteral("Name_%1").arg(i)); 0702 } 0703 0704 group.writeEntry("Number", count()); 0705 for (VirtualDesktop *desktop : std::as_const(m_desktops)) { 0706 const uint position = desktop->x11DesktopNumber(); 0707 0708 QString s = desktop->name(); 0709 const QString defaultvalue = defaultName(position); 0710 if (s.isEmpty()) { 0711 s = defaultvalue; 0712 if (m_rootInfo) { 0713 m_rootInfo->setDesktopName(position, s.toUtf8().data()); 0714 } 0715 } 0716 0717 if (s != defaultvalue) { 0718 group.writeEntry(QStringLiteral("Name_%1").arg(position), s); 0719 } else { 0720 QString currentvalue = group.readEntry(QStringLiteral("Name_%1").arg(position), QString()); 0721 if (currentvalue != defaultvalue) { 0722 group.deleteEntry(QStringLiteral("Name_%1").arg(position)); 0723 } 0724 } 0725 group.writeEntry(QStringLiteral("Id_%1").arg(position), desktop->id()); 0726 } 0727 0728 group.writeEntry("Rows", m_rows); 0729 0730 // Save to disk 0731 group.sync(); 0732 } 0733 0734 QString VirtualDesktopManager::defaultName(int desktop) const 0735 { 0736 return i18n("Desktop %1", desktop); 0737 } 0738 0739 void VirtualDesktopManager::initShortcuts() 0740 { 0741 initSwitchToShortcuts(); 0742 0743 addAction(QStringLiteral("Switch to Next Desktop"), i18n("Switch to Next Desktop"), QKeySequence(), &VirtualDesktopManager::slotNext); 0744 addAction(QStringLiteral("Switch to Previous Desktop"), i18n("Switch to Previous Desktop"), QKeySequence(), &VirtualDesktopManager::slotPrevious); 0745 0746 // shortcuts 0747 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); 0748 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); 0749 addAction(QStringLiteral("Switch One Desktop Up"), i18n("Switch One Desktop Up"), QKeySequence(Qt::CTRL | Qt::META | Qt::Key_Up), &VirtualDesktopManager::slotUp); 0750 addAction(QStringLiteral("Switch One Desktop Down"), i18n("Switch One Desktop Down"), QKeySequence(Qt::CTRL | Qt::META | Qt::Key_Down), &VirtualDesktopManager::slotDown); 0751 0752 // Gestures 0753 // These connections decide which desktop to end on after gesture ends 0754 connect(m_swipeGestureReleasedX.get(), &QAction::triggered, this, &VirtualDesktopManager::gestureReleasedX); 0755 connect(m_swipeGestureReleasedY.get(), &QAction::triggered, this, &VirtualDesktopManager::gestureReleasedY); 0756 0757 const auto left = [this](qreal cb) { 0758 if (grid().width() > 1) { 0759 m_currentDesktopOffset.setX(cb); 0760 Q_EMIT currentChanging(currentDesktop(), m_currentDesktopOffset); 0761 } 0762 }; 0763 const auto right = [this](qreal cb) { 0764 if (grid().width() > 1) { 0765 m_currentDesktopOffset.setX(-cb); 0766 Q_EMIT currentChanging(currentDesktop(), m_currentDesktopOffset); 0767 } 0768 }; 0769 input()->registerTouchpadSwipeShortcut(SwipeDirection::Left, 3, m_swipeGestureReleasedX.get(), left); 0770 input()->registerTouchpadSwipeShortcut(SwipeDirection::Right, 3, m_swipeGestureReleasedX.get(), right); 0771 input()->registerTouchpadSwipeShortcut(SwipeDirection::Left, 4, m_swipeGestureReleasedX.get(), left); 0772 input()->registerTouchpadSwipeShortcut(SwipeDirection::Right, 4, m_swipeGestureReleasedX.get(), right); 0773 input()->registerTouchpadSwipeShortcut(SwipeDirection::Down, 3, m_swipeGestureReleasedY.get(), [this](qreal cb) { 0774 if (grid().height() > 1) { 0775 m_currentDesktopOffset.setY(-cb); 0776 Q_EMIT currentChanging(currentDesktop(), m_currentDesktopOffset); 0777 } 0778 }); 0779 input()->registerTouchpadSwipeShortcut(SwipeDirection::Up, 3, m_swipeGestureReleasedY.get(), [this](qreal cb) { 0780 if (grid().height() > 1) { 0781 m_currentDesktopOffset.setY(cb); 0782 Q_EMIT currentChanging(currentDesktop(), m_currentDesktopOffset); 0783 } 0784 }); 0785 input()->registerTouchscreenSwipeShortcut(SwipeDirection::Left, 3, m_swipeGestureReleasedX.get(), left); 0786 input()->registerTouchscreenSwipeShortcut(SwipeDirection::Right, 3, m_swipeGestureReleasedX.get(), right); 0787 0788 // axis events 0789 input()->registerAxisShortcut(Qt::MetaModifier | Qt::AltModifier, PointerAxisDown, 0790 findChild<QAction *>(QStringLiteral("Switch to Next Desktop"))); 0791 input()->registerAxisShortcut(Qt::MetaModifier | Qt::AltModifier, PointerAxisUp, 0792 findChild<QAction *>(QStringLiteral("Switch to Previous Desktop"))); 0793 } 0794 0795 void VirtualDesktopManager::gestureReleasedY() 0796 { 0797 // Note that if desktop wrapping is disabled and there's no desktop above or below, 0798 // above() and below() will return the current desktop. 0799 VirtualDesktop *target = m_current; 0800 if (m_currentDesktopOffset.y() <= -GESTURE_SWITCH_THRESHOLD) { 0801 target = above(m_current, isNavigationWrappingAround()); 0802 } else if (m_currentDesktopOffset.y() >= GESTURE_SWITCH_THRESHOLD) { 0803 target = below(m_current, isNavigationWrappingAround()); 0804 } 0805 0806 // If the current desktop has not changed, consider that the gesture has been canceled. 0807 if (m_current != target) { 0808 setCurrent(target); 0809 } else { 0810 Q_EMIT currentChangingCancelled(); 0811 } 0812 m_currentDesktopOffset = QPointF(0, 0); 0813 } 0814 0815 void VirtualDesktopManager::gestureReleasedX() 0816 { 0817 // Note that if desktop wrapping is disabled and there's no desktop to left or right, 0818 // toLeft() and toRight() will return the current desktop. 0819 VirtualDesktop *target = m_current; 0820 if (m_currentDesktopOffset.x() <= -GESTURE_SWITCH_THRESHOLD) { 0821 target = toLeft(m_current, isNavigationWrappingAround()); 0822 } else if (m_currentDesktopOffset.x() >= GESTURE_SWITCH_THRESHOLD) { 0823 target = toRight(m_current, isNavigationWrappingAround()); 0824 } 0825 0826 // If the current desktop has not changed, consider that the gesture has been canceled. 0827 if (m_current != target) { 0828 setCurrent(target); 0829 } else { 0830 Q_EMIT currentChangingCancelled(); 0831 } 0832 m_currentDesktopOffset = QPointF(0, 0); 0833 } 0834 0835 void VirtualDesktopManager::initSwitchToShortcuts() 0836 { 0837 const QString toDesktop = QStringLiteral("Switch to Desktop %1"); 0838 const KLocalizedString toDesktopLabel = ki18n("Switch to Desktop %1"); 0839 addAction(toDesktop, toDesktopLabel, 1, QKeySequence(Qt::CTRL | Qt::Key_F1), &VirtualDesktopManager::slotSwitchTo); 0840 addAction(toDesktop, toDesktopLabel, 2, QKeySequence(Qt::CTRL | Qt::Key_F2), &VirtualDesktopManager::slotSwitchTo); 0841 addAction(toDesktop, toDesktopLabel, 3, QKeySequence(Qt::CTRL | Qt::Key_F3), &VirtualDesktopManager::slotSwitchTo); 0842 addAction(toDesktop, toDesktopLabel, 4, QKeySequence(Qt::CTRL | Qt::Key_F4), &VirtualDesktopManager::slotSwitchTo); 0843 0844 for (uint i = 5; i <= maximum(); ++i) { 0845 addAction(toDesktop, toDesktopLabel, i, QKeySequence(), &VirtualDesktopManager::slotSwitchTo); 0846 } 0847 } 0848 0849 QAction *VirtualDesktopManager::addAction(const QString &name, const KLocalizedString &label, uint value, const QKeySequence &key, void (VirtualDesktopManager::*slot)()) 0850 { 0851 QAction *a = new QAction(this); 0852 a->setProperty("componentName", QStringLiteral("kwin")); 0853 a->setObjectName(name.arg(value)); 0854 a->setText(label.subs(value).toString()); 0855 a->setData(value); 0856 KGlobalAccel::setGlobalShortcut(a, key); 0857 connect(a, &QAction::triggered, this, slot); 0858 return a; 0859 } 0860 0861 QAction *VirtualDesktopManager::addAction(const QString &name, const QString &label, const QKeySequence &key, void (VirtualDesktopManager::*slot)()) 0862 { 0863 QAction *a = new QAction(this); 0864 a->setProperty("componentName", QStringLiteral("kwin")); 0865 a->setObjectName(name); 0866 a->setText(label); 0867 KGlobalAccel::setGlobalShortcut(a, key); 0868 connect(a, &QAction::triggered, this, slot); 0869 return a; 0870 } 0871 0872 void VirtualDesktopManager::slotSwitchTo() 0873 { 0874 QAction *act = qobject_cast<QAction *>(sender()); 0875 if (!act) { 0876 return; 0877 } 0878 bool ok = false; 0879 const uint i = act->data().toUInt(&ok); 0880 if (!ok) { 0881 return; 0882 } 0883 setCurrent(i); 0884 } 0885 0886 void VirtualDesktopManager::setNavigationWrappingAround(bool enabled) 0887 { 0888 if (enabled == m_navigationWrapsAround) { 0889 return; 0890 } 0891 m_navigationWrapsAround = enabled; 0892 Q_EMIT navigationWrappingAroundChanged(); 0893 } 0894 0895 void VirtualDesktopManager::slotDown() 0896 { 0897 moveTo(Direction::Down, isNavigationWrappingAround()); 0898 } 0899 0900 void VirtualDesktopManager::slotLeft() 0901 { 0902 moveTo(Direction::Left, isNavigationWrappingAround()); 0903 } 0904 0905 void VirtualDesktopManager::slotPrevious() 0906 { 0907 moveTo(Direction::Previous, isNavigationWrappingAround()); 0908 } 0909 0910 void VirtualDesktopManager::slotNext() 0911 { 0912 moveTo(Direction::Next, isNavigationWrappingAround()); 0913 } 0914 0915 void VirtualDesktopManager::slotRight() 0916 { 0917 moveTo(Direction::Right, isNavigationWrappingAround()); 0918 } 0919 0920 void VirtualDesktopManager::slotUp() 0921 { 0922 moveTo(Direction::Up, isNavigationWrappingAround()); 0923 } 0924 0925 } // KWin 0926 0927 #include "moc_virtualdesktops.cpp"