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