diff --git a/virtualdesktops.cpp b/virtualdesktops.cpp index 3332b2871..2df3a7b3b 100644 --- a/virtualdesktops.cpp +++ b/virtualdesktops.cpp @@ -1,899 +1,896 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2009 Lucas Murray Copyright (C) 2012 Martin Gräßlin This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #include "virtualdesktops.h" #include "input.h" // KDE #include #include #include #include #include // Qt #include #include #include #include namespace KWin { extern int screen_number; VirtualDesktop::VirtualDesktop(QObject *parent) : QObject(parent) { } VirtualDesktop::~VirtualDesktop() { emit aboutToBeDestroyed(); } void VirtualDesktopManager::setVirtualDesktopManagement(KWayland::Server::PlasmaVirtualDesktopManagementInterface *management) { using namespace KWayland::Server; Q_ASSERT(!m_virtualDesktopManagement); m_virtualDesktopManagement = management; connect(this, &VirtualDesktopManager::desktopCreated, this, [this](VirtualDesktop *desktop) { using namespace KWayland::Server; PlasmaVirtualDesktopInterface *pvd = m_virtualDesktopManagement->createDesktop(desktop->id(), desktop->x11DesktopNumber() - 1); pvd->setName(desktop->name()); pvd->sendDone(); connect(desktop, &VirtualDesktop::nameChanged, this, [this, desktop, pvd]() { pvd->setName(desktop->name()); } ); } ); //handle removed: from VirtualDesktopManager to the wayland interface connect(this, &VirtualDesktopManager::desktopRemoved, this, [this](VirtualDesktop *desktop) { m_virtualDesktopManagement->removeDesktop(desktop->id()); } ); //create a new desktop when the client asks to connect (m_virtualDesktopManagement, &PlasmaVirtualDesktopManagementInterface::desktopCreateRequested, this, [this](const QString &name, quint32 position) { VirtualDesktop *vd = createVirtualDesktop(position); if (vd) { vd->setName(name); } } ); //remove when the client asks to connect (m_virtualDesktopManagement, &PlasmaVirtualDesktopManagementInterface::desktopRemoveRequested, this, [this](const QString &id) { //here there can be some nice kauthorized check? //remove only from VirtualDesktopManager, the other connections will remove it from m_virtualDesktopManagement as well removeVirtualDesktop(id.toUtf8()); } ); for (quint32 i = 1; i <= count(); ++i) { VirtualDesktop *internalDesktop = desktopForX11Id(i); PlasmaVirtualDesktopInterface *desktop = m_virtualDesktopManagement->createDesktop(internalDesktop->id()); desktop->setName(desktop->name()); desktop->sendDone(); connect(desktop, &PlasmaVirtualDesktopInterface::activateRequested, this, [this, desktop] () { setCurrent(desktopForId(desktop->id().toUtf8())); } ); } //Now we are sure all ids are there save(); connect(this, &VirtualDesktopManager::currentChanged, this, [this]() { for (auto *deskInt : m_virtualDesktopManagement->desktops()) { if (deskInt->id() == currentDesktop()->id()) { deskInt->setActive(true); } else { deskInt->setActive(false); } } } ); } void VirtualDesktop::setId(const QByteArray &id) { Q_ASSERT(m_id.isEmpty()); m_id = id; } void VirtualDesktop::setX11DesktopNumber(uint number) { //x11DesktopNumber can be changed now if (static_cast(m_x11DesktopNumber) == number) { return; } m_x11DesktopNumber = number; if (m_x11DesktopNumber != 0) { emit x11DesktopNumberChanged(); } } void VirtualDesktop::setName(const QString &name) { if (m_name == name) { return; } m_name = name; emit nameChanged(); } VirtualDesktopGrid::VirtualDesktopGrid() : m_size(1, 2) // Default to tow rows , m_grid(QVector>{QVector{}, QVector{}}) { } VirtualDesktopGrid::~VirtualDesktopGrid() = default; void VirtualDesktopGrid::update(const QSize &size, Qt::Orientation orientation, const QVector &desktops) { // Set private variables m_size = size; const uint width = size.width(); const uint height = size.height(); m_grid.clear(); auto it = desktops.begin(); auto end = desktops.end(); if (orientation == Qt::Horizontal) { for (uint y = 0; y < height; ++y) { QVector row; for (uint x = 0; x < width && it != end; ++x) { row << *it; it++; } m_grid << row; } } else { for (uint y = 0; y < height; ++y) { m_grid << QVector(); } for (uint x = 0; x < width; ++x) { for (uint y = 0; y < height && it != end; ++y) { auto &row = m_grid[y]; row << *it; it++; } } } } QPoint VirtualDesktopGrid::gridCoords(uint id) const { return gridCoords(VirtualDesktopManager::self()->desktopForX11Id(id)); } QPoint VirtualDesktopGrid::gridCoords(VirtualDesktop *vd) const { for (int y = 0; y < m_grid.count(); ++y) { const auto &row = m_grid.at(y); for (int x = 0; x < row.count(); ++x) { if (row.at(x) == vd) { return QPoint(x, y); } } } return QPoint(-1, -1); } VirtualDesktop *VirtualDesktopGrid::at(const QPoint &coords) const { if (coords.y() >= m_grid.count()) { return nullptr; } const auto &row = m_grid.at(coords.y()); if (coords.x() >= row.count()) { return nullptr; } return row.at(coords.x()); } KWIN_SINGLETON_FACTORY_VARIABLE(VirtualDesktopManager, s_manager) VirtualDesktopManager::VirtualDesktopManager(QObject *parent) : QObject(parent) , m_navigationWrapsAround(false) , m_rootInfo(NULL) { } VirtualDesktopManager::~VirtualDesktopManager() { s_manager = NULL; } QString VirtualDesktopManager::name(uint desktop) const { if (!m_rootInfo) { return defaultName(desktop); } return QString::fromUtf8(m_rootInfo->desktopName(desktop)); } uint VirtualDesktopManager::above(uint id, bool wrap) const { auto vd = above(desktopForX11Id(id), wrap); return vd ? vd->x11DesktopNumber() : 0; } VirtualDesktop *VirtualDesktopManager::above(VirtualDesktop *desktop, bool wrap) const { Q_ASSERT(m_current); if (!desktop) { desktop = m_current; } QPoint coords = m_grid.gridCoords(desktop); Q_ASSERT(coords.x() >= 0); while (true) { coords.ry()--; if (coords.y() < 0) { if (wrap) { coords.setY(m_grid.height() - 1); } else { return desktop; // Already at the top-most desktop } } if (VirtualDesktop *vd = m_grid.at(coords)) { return vd; } } return nullptr; } uint VirtualDesktopManager::toRight(uint id, bool wrap) const { auto vd = toRight(desktopForX11Id(id), wrap); return vd ? vd->x11DesktopNumber() : 0; } VirtualDesktop *VirtualDesktopManager::toRight(VirtualDesktop *desktop, bool wrap) const { Q_ASSERT(m_current); if (!desktop) { desktop = m_current; } QPoint coords = m_grid.gridCoords(desktop); Q_ASSERT(coords.x() >= 0); while (true) { coords.rx()++; if (coords.x() >= m_grid.width()) { if (wrap) { coords.setX(0); } else { return desktop; // Already at the right-most desktop } } if (VirtualDesktop *vd = m_grid.at(coords)) { return vd; } } return nullptr; } uint VirtualDesktopManager::below(uint id, bool wrap) const { auto vd = below(desktopForX11Id(id), wrap); return vd ? vd->x11DesktopNumber() : 0; } VirtualDesktop *VirtualDesktopManager::below(VirtualDesktop *desktop, bool wrap) const { Q_ASSERT(m_current); if (!desktop) { desktop = m_current; } QPoint coords = m_grid.gridCoords(desktop); Q_ASSERT(coords.x() >= 0); while (true) { coords.ry()++; if (coords.y() >= m_grid.height()) { if (wrap) { coords.setY(0); } else { // Already at the bottom-most desktop return desktop; } } if (VirtualDesktop *vd = m_grid.at(coords)) { return vd; } } return nullptr; } uint VirtualDesktopManager::toLeft(uint id, bool wrap) const { auto vd = toLeft(desktopForX11Id(id), wrap); return vd ? vd->x11DesktopNumber() : 0; } VirtualDesktop *VirtualDesktopManager::toLeft(VirtualDesktop *desktop, bool wrap) const { Q_ASSERT(m_current); if (!desktop) { desktop = m_current; } QPoint coords = m_grid.gridCoords(desktop); Q_ASSERT(coords.x() >= 0); while (true) { coords.rx()--; if (coords.x() < 0) { if (wrap) { coords.setX(m_grid.width() - 1); } else { return desktop; // Already at the left-most desktop } } if (VirtualDesktop *vd = m_grid.at(coords)) { return vd; } } return nullptr; } VirtualDesktop *VirtualDesktopManager::next(VirtualDesktop *desktop, bool wrap) const { Q_ASSERT(m_current); if (!desktop) { desktop = m_current; } auto it = std::find(m_desktops.begin(), m_desktops.end(), desktop); Q_ASSERT(it != m_desktops.end()); it++; if (it == m_desktops.end()) { if (wrap) { return m_desktops.first(); } else { return desktop; } } return *it; } VirtualDesktop *VirtualDesktopManager::previous(VirtualDesktop *desktop, bool wrap) const { Q_ASSERT(m_current); if (!desktop) { desktop = m_current; } auto it = std::find(m_desktops.begin(), m_desktops.end(), desktop); Q_ASSERT(it != m_desktops.end()); if (it == m_desktops.begin()) { if (wrap) { return m_desktops.last(); } else { return desktop; } } it--; return *it; } VirtualDesktop *VirtualDesktopManager::desktopForX11Id(uint id) const { if (id == 0 || id > count()) { return nullptr; } return m_desktops.at(id - 1); } VirtualDesktop *VirtualDesktopManager::desktopForId(const QByteArray &id) const { auto desk = std::find_if( m_desktops.constBegin(), m_desktops.constEnd(), [id] (const VirtualDesktop *desk ) { return desk->id() == id; } ); if (desk != m_desktops.constEnd()) { return *desk; } return nullptr; } VirtualDesktop *VirtualDesktopManager::createVirtualDesktop(uint number, const QString &name) { //too many, can't insert new ones if ((uint)m_desktops.count() == VirtualDesktopManager::maximum()) { return nullptr; } const uint actualNumber = qBound(0, number, VirtualDesktopManager::maximum()); auto *vd = new VirtualDesktop(this); vd->setX11DesktopNumber(actualNumber); //TODO: depend on Qt 5.11, use toString(QUuid::WithoutBraces) vd->setId(QUuid::createUuid().toString().toUtf8()); vd->setName(name); if (m_rootInfo) { connect(vd, &VirtualDesktop::nameChanged, this, [this, vd]() { if (m_rootInfo) { m_rootInfo->setDesktopName(vd->x11DesktopNumber(), vd->name().toUtf8().data()); } } ); m_rootInfo->setDesktopName(vd->x11DesktopNumber(), vd->name().toUtf8().data()); } //update the id of displaced desktops for (uint i = actualNumber; i < (uint)m_desktops.count(); ++i) { m_desktops[i]->setX11DesktopNumber(i + 1); if (m_rootInfo) { m_rootInfo->setDesktopName(i + 1, m_desktops[i]->name().toUtf8().data()); } } m_desktops.insert(actualNumber - 1, vd); save(); updateRootInfo(); emit desktopCreated(vd); emit countChanged(m_desktops.count()-1, m_desktops.count()); return vd; } void VirtualDesktopManager::removeVirtualDesktop(const QByteArray &id) { //don't end up without any desktop if (m_desktops.count() == 1) { return; } auto desktop = desktopForId(id); if (!desktop) { return; } const uint oldCurrent = m_current->x11DesktopNumber(); const uint i = desktop->x11DesktopNumber() - 1; m_desktops.remove(i); for (uint j = i; j < (uint)m_desktops.count(); ++j) { m_desktops[j]->setX11DesktopNumber(j + 1); if (m_rootInfo) { m_rootInfo->setDesktopName(j + 1, m_desktops[j]->name().toUtf8().data()); } } const uint newCurrent = qMin(oldCurrent, (uint)m_desktops.count()); m_current = m_desktops.at(newCurrent - 1); if (oldCurrent != newCurrent) { emit currentChanged(oldCurrent, newCurrent); } updateRootInfo(); emit desktopRemoved(desktop); emit countChanged(m_desktops.count()+1, m_desktops.count()); desktop->deleteLater(); } uint VirtualDesktopManager::current() const { return m_current ? m_current->x11DesktopNumber() : 0; } VirtualDesktop *VirtualDesktopManager::currentDesktop() const { return m_current; } bool VirtualDesktopManager::setCurrent(uint newDesktop) { if (newDesktop < 1 || newDesktop > count() || newDesktop == current()) { return false; } auto d = desktopForX11Id(newDesktop); Q_ASSERT(d); return setCurrent(d); } bool VirtualDesktopManager::setCurrent(VirtualDesktop *newDesktop) { Q_ASSERT(newDesktop); if (m_current == newDesktop) { return false; } const uint oldDesktop = current(); m_current = newDesktop; emit currentChanged(oldDesktop, newDesktop->x11DesktopNumber()); return true; } +static bool s_loadingDesktopSettings = false; + void VirtualDesktopManager::setCount(uint count) { count = qBound(1, count, VirtualDesktopManager::maximum()); if (count == uint(m_desktops.count())) { // nothing to change return; } QList newDesktops; const uint oldCount = m_desktops.count(); //this explicit check makes it more readable if ((uint)m_desktops.count() > count) { const auto desktopsToRemove = m_desktops.mid(count); m_desktops.resize(count); if (m_current) { uint oldCurrent = current(); uint newCurrent = qMin(oldCurrent, count); m_current = m_desktops.at(newCurrent - 1); if (oldCurrent != newCurrent) { emit currentChanged(oldCurrent, newCurrent); } } for (auto desktop : desktopsToRemove) { emit desktopRemoved(desktop); desktop->deleteLater(); } } else { while (uint(m_desktops.count()) < count) { auto vd = new VirtualDesktop(this); vd->setX11DesktopNumber(m_desktops.count() + 1); - if (!m_isLoading) { + if (!s_loadingDesktopSettings) { vd->setId(QUuid::createUuid().toString().toUtf8()); } m_desktops << vd; newDesktops << vd; if (m_rootInfo) { connect(vd, &VirtualDesktop::nameChanged, this, [this, vd]() { if (m_rootInfo) { m_rootInfo->setDesktopName(vd->x11DesktopNumber(), vd->name().toUtf8().data()); } } ); m_rootInfo->setDesktopName(vd->x11DesktopNumber(), vd->name().toUtf8().data()); } } } updateRootInfo(); save(); for (auto vd : newDesktops) { emit desktopCreated(vd); } emit countChanged(oldCount, m_desktops.count()); } uint VirtualDesktopManager::rows() const { return grid().height(); } void VirtualDesktopManager::setRows(uint rows) { if (static_cast(grid().height()) == rows || rows == 0 || rows > count()) { return; } int columns = count() / rows; if (count() % rows > 0) { columns++; } if (m_rootInfo) { m_rootInfo->setDesktopLayout(NET::OrientationHorizontal, columns, rows, NET::DesktopLayoutCornerTopLeft); m_rootInfo->activate(); } updateLayout(); //rowsChanged will be emitted by setNETDesktopLayout called by updateLayout } void VirtualDesktopManager::updateRootInfo() { if (!m_rootInfo) { // Make sure the layout is still valid updateLayout(); return; } const int n = count(); m_rootInfo->setNumberOfDesktops(n); NETPoint *viewports = new NETPoint[n]; m_rootInfo->setDesktopViewport(n, *viewports); delete[] viewports; // Make sure the layout is still valid updateLayout(); } void VirtualDesktopManager::updateLayout() { int width = 0; int height = 0; Qt::Orientation orientation = Qt::Horizontal; if (m_rootInfo) { // TODO: Is there a sane way to avoid overriding the existing grid? width = m_rootInfo->desktopLayoutColumnsRows().width(); height = m_rootInfo->desktopLayoutColumnsRows().height(); orientation = m_rootInfo->desktopLayoutOrientation() == NET::OrientationHorizontal ? Qt::Horizontal : Qt::Vertical; } if (width == 0 && height == 0) { // Not given, set default layout height = count() == 1u ? 1 : 2; } setNETDesktopLayout(orientation, width, height, 0 //rootInfo->desktopLayoutCorner() // Not really worth implementing right now. ); } -static bool s_loadingDesktopSettings = false; - void VirtualDesktopManager::load() { s_loadingDesktopSettings = true; if (!m_config) { return; } - //FIXME: how to avoid this? - m_isLoading = true; QString groupname; if (screen_number == 0) { groupname = QStringLiteral("Desktops"); } else { groupname.sprintf("Desktops-screen-%d", screen_number); } KConfigGroup group(m_config, groupname); const int n = group.readEntry("Number", 1); setCount(n); for (int i = 1; i <= n; i++) { QString s = group.readEntry(QStringLiteral("Name_%1").arg(i), i18n("Desktop %1", i)); if (m_rootInfo) { m_rootInfo->setDesktopName(i, s.toUtf8().data()); } m_desktops[i-1]->setName(s.toUtf8().data()); QString sId = group.readEntry(QStringLiteral("Id_%1").arg(i), QString()); //load gets called 2 times, see workspace.cpp line 416 and BUG 385260 if (m_desktops[i-1]->id().isEmpty()) { if (sId.isEmpty()) { sId = QUuid::createUuid().toString(); } m_desktops[i-1]->setId(sId.toUtf8().data()); } else { Q_ASSERT(sId.isEmpty() || m_desktops[i-1]->id() == sId.toUtf8().data()); } // TODO: update desktop focus chain, why? // m_desktopFocusChain.value()[i-1] = i; } if (m_rootInfo) { int rows = group.readEntry("Rows", 2); rows = qBound(1, rows, n); // avoid weird cases like having 3 rows for 4 desktops, where the last row is unused int columns = n / rows; if (n % rows > 0) { columns++; } m_rootInfo->setDesktopLayout(NET::OrientationHorizontal, columns, rows, NET::DesktopLayoutCornerTopLeft); m_rootInfo->activate(); } s_loadingDesktopSettings = false; - m_isLoading = false; } void VirtualDesktopManager::save() { if (s_loadingDesktopSettings) { return; } if (!m_config) { return; } QString groupname; if (screen_number == 0) { groupname = QStringLiteral("Desktops"); } else { groupname.sprintf("Desktops-screen-%d", screen_number); } KConfigGroup group(m_config, groupname); group.writeEntry("Number", count()); for (uint i = 1; i <= count(); ++i) { QString s = name(i); const QString defaultvalue = defaultName(i); if (s.isEmpty()) { s = defaultvalue; if (m_rootInfo) { m_rootInfo->setDesktopName(i, s.toUtf8().data()); } } if (s != defaultvalue) { group.writeEntry(QStringLiteral("Name_%1").arg(i), s); } else { QString currentvalue = group.readEntry(QStringLiteral("Name_%1").arg(i), QString()); if (currentvalue != defaultvalue) { group.deleteEntry(QStringLiteral("Name_%1").arg(i)); } } group.writeEntry(QStringLiteral("Id_%1").arg(i), m_desktops[i-1]->id()); } // Save to disk group.sync(); } QString VirtualDesktopManager::defaultName(int desktop) const { return i18n("Desktop %1", desktop); } void VirtualDesktopManager::setNETDesktopLayout(Qt::Orientation orientation, uint width, uint height, int startingCorner) { Q_UNUSED(startingCorner); // Not really worth implementing right now. const uint count = m_desktops.count(); // Calculate valid grid size Q_ASSERT(width > 0 || height > 0); if ((width <= 0) && (height > 0)) { width = (count + height - 1) / height; } else if ((height <= 0) && (width > 0)) { height = (count + width - 1) / width; } while (width * height < count) { if (orientation == Qt::Horizontal) { ++width; } else { ++height; } } m_grid.update(QSize(width, height), orientation, m_desktops); // TODO: why is there no call to m_rootInfo->setDesktopLayout? emit layoutChanged(width, height); emit rowsChanged(height); } void VirtualDesktopManager::initShortcuts() { initSwitchToShortcuts(); QAction *nextAction = addAction(QStringLiteral("Switch to Next Desktop"), i18n("Switch to Next Desktop"), &VirtualDesktopManager::slotNext); input()->registerTouchpadSwipeShortcut(SwipeDirection::Right, nextAction); QAction *previousAction = addAction(QStringLiteral("Switch to Previous Desktop"), i18n("Switch to Previous Desktop"), &VirtualDesktopManager::slotPrevious); input()->registerTouchpadSwipeShortcut(SwipeDirection::Left, previousAction); addAction(QStringLiteral("Switch One Desktop to the Right"), i18n("Switch One Desktop to the Right"), &VirtualDesktopManager::slotRight); addAction(QStringLiteral("Switch One Desktop to the Left"), i18n("Switch One Desktop to the Left"), &VirtualDesktopManager::slotLeft); addAction(QStringLiteral("Switch One Desktop Up"), i18n("Switch One Desktop Up"), &VirtualDesktopManager::slotUp); addAction(QStringLiteral("Switch One Desktop Down"), i18n("Switch One Desktop Down"), &VirtualDesktopManager::slotDown); // axis events input()->registerAxisShortcut(Qt::ControlModifier | Qt::AltModifier, PointerAxisDown, findChild(QStringLiteral("Switch to Next Desktop"))); input()->registerAxisShortcut(Qt::ControlModifier | Qt::AltModifier, PointerAxisUp, findChild(QStringLiteral("Switch to Previous Desktop"))); } void VirtualDesktopManager::initSwitchToShortcuts() { const QString toDesktop = QStringLiteral("Switch to Desktop %1"); const KLocalizedString toDesktopLabel = ki18n("Switch to Desktop %1"); addAction(toDesktop, toDesktopLabel, 1, QKeySequence(Qt::CTRL + Qt::Key_F1), &VirtualDesktopManager::slotSwitchTo); addAction(toDesktop, toDesktopLabel, 2, QKeySequence(Qt::CTRL + Qt::Key_F2), &VirtualDesktopManager::slotSwitchTo); addAction(toDesktop, toDesktopLabel, 3, QKeySequence(Qt::CTRL + Qt::Key_F3), &VirtualDesktopManager::slotSwitchTo); addAction(toDesktop, toDesktopLabel, 4, QKeySequence(Qt::CTRL + Qt::Key_F4), &VirtualDesktopManager::slotSwitchTo); for (uint i = 5; i <= maximum(); ++i) { addAction(toDesktop, toDesktopLabel, i, QKeySequence(), &VirtualDesktopManager::slotSwitchTo); } } QAction *VirtualDesktopManager::addAction(const QString &name, const KLocalizedString &label, uint value, const QKeySequence &key, void (VirtualDesktopManager::*slot)()) { QAction *a = new QAction(this); a->setProperty("componentName", QStringLiteral(KWIN_NAME)); a->setObjectName(name.arg(value)); a->setText(label.subs(value).toString()); a->setData(value); KGlobalAccel::setGlobalShortcut(a, key); input()->registerShortcut(key, a, this, slot); return a; } QAction *VirtualDesktopManager::addAction(const QString &name, const QString &label, void (VirtualDesktopManager::*slot)()) { QAction *a = new QAction(this); a->setProperty("componentName", QStringLiteral(KWIN_NAME)); a->setObjectName(name); a->setText(label); KGlobalAccel::setGlobalShortcut(a, QKeySequence()); input()->registerShortcut(QKeySequence(), a, this, slot); return a; } void VirtualDesktopManager::slotSwitchTo() { QAction *act = qobject_cast(sender()); if (!act) { return; } bool ok = false; const uint i = act->data().toUInt(&ok); if (!ok) { return; } setCurrent(i); } void VirtualDesktopManager::setNavigationWrappingAround(bool enabled) { if (enabled == m_navigationWrapsAround) { return; } m_navigationWrapsAround = enabled; emit navigationWrappingAroundChanged(); } void VirtualDesktopManager::slotDown() { moveTo(isNavigationWrappingAround()); } void VirtualDesktopManager::slotLeft() { moveTo(isNavigationWrappingAround()); } void VirtualDesktopManager::slotPrevious() { moveTo(isNavigationWrappingAround()); } void VirtualDesktopManager::slotNext() { moveTo(isNavigationWrappingAround()); } void VirtualDesktopManager::slotRight() { moveTo(isNavigationWrappingAround()); } void VirtualDesktopManager::slotUp() { moveTo(isNavigationWrappingAround()); } } // KWin diff --git a/virtualdesktops.h b/virtualdesktops.h index e9fe5a3c9..f74e8d6e1 100644 --- a/virtualdesktops.h +++ b/virtualdesktops.h @@ -1,738 +1,737 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2012 Martin Gräßlin This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #ifndef KWIN_VIRTUAL_DESKTOPS_H #define KWIN_VIRTUAL_DESKTOPS_H // KWin #include #include // Qt includes #include #include #include #include // KDE includes #include #include class KLocalizedString; class NETRootInfo; class QAction; namespace KWayland { namespace Server { class PlasmaVirtualDesktopManagementInterface; } } namespace KWin { class KWIN_EXPORT VirtualDesktop : public QObject { Q_OBJECT Q_PROPERTY(QByteArray id READ id CONSTANT) Q_PROPERTY(uint x11DesktopNumber READ x11DesktopNumber NOTIFY x11DesktopNumberChanged) Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) public: explicit VirtualDesktop(QObject *parent = nullptr); virtual ~VirtualDesktop(); void setId(const QByteArray &id); QByteArray id() const { return m_id; } void setName(const QString &name); QString name() const { return m_name; } void setX11DesktopNumber(uint number); uint x11DesktopNumber() const { return m_x11DesktopNumber; } Q_SIGNALS: void nameChanged(); void x11DesktopNumberChanged(); /** * Emitted just before the desktop gets destroyed. **/ void aboutToBeDestroyed(); private: QByteArray m_id; QString m_name; int m_x11DesktopNumber = 0; }; /** * @brief Two dimensional grid containing the ID of the virtual desktop at a specific position * in the grid. * * The VirtualDesktopGrid represents a visual layout of the Virtual Desktops as they are in e.g. * a Pager. This grid is used for getting a desktop next to a given desktop in any direction by * making use of the layout information. This allows navigation like move to desktop on left. **/ class VirtualDesktopGrid { public: VirtualDesktopGrid(); ~VirtualDesktopGrid(); void update(const QSize &size, Qt::Orientation orientation, const QVector &desktops); /** * @returns The coords of desktop @a id in grid units. */ QPoint gridCoords(uint id) const; /** * @returns The coords of desktop @a vd in grid units. */ QPoint gridCoords(VirtualDesktop *vd) const; /** * @returns The desktop at the point @a coords or 0 if no desktop exists at that * point. @a coords is to be in grid units. */ VirtualDesktop *at(const QPoint &coords) const; int width() const; int height() const; const QSize &size() const; private: QSize m_size; QVector> m_grid; }; /** * @brief Manages the number of available virtual desktops, the layout of those and which virtual * desktop is the current one. * * This manager is responsible for Virtual Desktop handling inside KWin. It has a property for the * count of available virtual desktops and a property for the currently active virtual desktop. All * changes to the number of virtual desktops and the current virtual desktop need to go through this * manager. * * On all changes a signal is emitted and interested parties should connect to the signal. The manager * itself does not interact with other parts of the system. E.g. it does not hide/show windows of * desktop changes. This is outside the scope of this manager. * * Internally the manager organizes the virtual desktops in a grid allowing to navigate over the * virtual desktops. For this a set of convenient methods are available which allow to get the id * of an adjacent desktop or to switch to an adjacent desktop. Interested parties should make use of * these methods and not replicate the logic to switch to the next desktop. **/ class KWIN_EXPORT VirtualDesktopManager : public QObject { Q_OBJECT /** * The number of virtual desktops currently available. * The ids of the virtual desktops are in the range [1, VirtualDesktopManager::maximum()]. **/ Q_PROPERTY(uint count READ count WRITE setCount NOTIFY countChanged) /** * The id of the virtual desktop which is currently in use. **/ Q_PROPERTY(uint current READ current WRITE setCurrent NOTIFY currentChanged) /** * Whether navigation in the desktop layout wraps around at the borders. **/ Q_PROPERTY(bool navigationWrappingAround READ isNavigationWrappingAround WRITE setNavigationWrappingAround NOTIFY navigationWrappingAroundChanged) public: virtual ~VirtualDesktopManager(); /** * @internal, for X11 case **/ void setRootInfo(NETRootInfo *info); /** * @internal, for Wayland case **/ void setVirtualDesktopManagement(KWayland::Server::PlasmaVirtualDesktopManagementInterface *management); /** * @internal **/ void setConfig(KSharedConfig::Ptr config); /** * @returns Total number of desktops currently in existence. * @see setCount * @see countChanged */ uint count() const; /** * @returns the number of rows the layout has. * @see setRows * @see rowsChanged */ uint rows() const; /** * @returns The ID of the current desktop. * @see setCurrent * @see currentChanged */ uint current() const; /** * @returns The current desktop * @see setCurrent * @see currentChanged **/ VirtualDesktop *currentDesktop() const; /** * Moves to the desktop through the algorithm described by Direction. * @param wrap If @c true wraps around to the other side of the layout * @see setCurrent **/ template void moveTo(bool wrap = false); /** * @returns The name of the @p desktop **/ QString name(uint desktop) const; /** * @returns @c true if navigation at borders of layout wraps around, @c false otherwise * @see setNavigationWrappingAround * @see navigationWrappingAroundChanged **/ bool isNavigationWrappingAround() const; /** * @returns The layout aware virtual desktop grid used by this manager. **/ const VirtualDesktopGrid &grid() const; /** * @returns The ID of the desktop above desktop @a id. Wraps around to the bottom of * the layout if @a wrap is set. If @a id is not set use the current one. */ uint above(uint id = 0, bool wrap = true) const; /** * @returns The desktop above desktop @a desktop. Wraps around to the bottom of * the layout if @a wrap is set. If @a desktop is @c null use the current one. */ VirtualDesktop *above(VirtualDesktop *desktop, bool wrap = true) const; /** * @returns The ID of the desktop to the right of desktop @a id. Wraps around to the * left of the layout if @a wrap is set. If @a id is not set use the current one. */ uint toRight(uint id = 0, bool wrap = true) const; /** * @returns The desktop to the right of desktop @a desktop. Wraps around to the * left of the layout if @a wrap is set. If @a desktop is @c null use the current one. */ VirtualDesktop *toRight(VirtualDesktop *desktop, bool wrap = true) const; /** * @returns The ID of the desktop below desktop @a id. Wraps around to the top of the * layout if @a wrap is set. If @a id is not set use the current one. */ uint below(uint id = 0, bool wrap = true) const; /** * @returns The desktop below desktop @a desktop. Wraps around to the top of the * layout if @a wrap is set. If @a desktop is @c null use the current one. */ VirtualDesktop *below(VirtualDesktop *desktop, bool wrap = true) const; /** * @returns The ID of the desktop to the left of desktop @a id. Wraps around to the * right of the layout if @a wrap is set. If @a id is not set use the current one. */ uint toLeft(uint id = 0, bool wrap = true) const; /** * @returns The desktop to the left of desktop @a desktop. Wraps around to the * right of the layout if @a wrap is set. If @a desktop is @c null use the current one. */ VirtualDesktop *toLeft(VirtualDesktop *desktop, bool wrap = true) const; /** * @returns The desktop after the desktop @a desktop. Wraps around to the first * desktop if @a wrap is set. If @a desktop is @c null use the current desktop. **/ VirtualDesktop *next(VirtualDesktop *desktop = nullptr, bool wrap = true) const; /** * @returns The desktop in front of the desktop @a desktop. Wraps around to the * last desktop if @a wrap is set. If @a desktop is @c null use the current desktop. **/ VirtualDesktop *previous(VirtualDesktop *desktop = nullptr, bool wrap = true) const; void initShortcuts(); /** * @returns all currently managed VirtualDesktops **/ QVector desktops() const { return m_desktops; } /** * @returns The VirtualDesktop for the x11 @p id, if no such VirtualDesktop @c null is returned **/ VirtualDesktop *desktopForX11Id(uint id) const; /** * @returns The VirtualDesktop for the internal desktop string @p id, if no such VirtualDesktop @c null is returned **/ VirtualDesktop *desktopForId(const QByteArray &id) const; /** * Create a new virtual desktop at the requested position. * The difference with setCount is that setCount always adds new desktops at the end of the chain. The Id is automatically generated. * @param x11DesktopNumber number for the desktop. The desktop created will have an * x11DesktopNumber guaranteed to be between 1 and numberOfDesktops(). * Existing desktops will eventually have their x11DesktopNumber increased. * @param name The name for the new desktop, if empty the default name will be used. * @returns the new VirtualDesktop, nullptr if we reached the maximum number of desktops */ VirtualDesktop *createVirtualDesktop(uint x11DesktopNumber, const QString &name = QString()); /** * Remove the virtual desktop identified by id, if it exists * difference with setCount is that is possible to remove an arbitrary desktop, * not only the last one. * @param id the string id of the desktop to remove */ void removeVirtualDesktop(const QByteArray &id); /** * Updates the net root info for new number of desktops **/ void updateRootInfo(); /** * @returns The maximum number of desktops that KWin supports. */ static uint maximum(); public Q_SLOTS: /** * Set the number of available desktops to @a count. This function overrides any previous * grid layout. * There needs to be at least one virtual desktop and the new value is capped at the maximum * number of desktops. A caller of this function cannot expect that the change has been applied. * It is the callers responsibility to either check the @link numberOfDesktops or connect to the * @link countChanged signal. * * In case the @link current desktop is on a desktop higher than the new count, the current desktop * is changed to be the new desktop with highest id. In that situation the signal @link desktopRemoved * is emitted. * @param count The new number of desktops to use * @see count * @see maximum * @see countChanged * @see desktopCreated * @see desktopRemoved */ void setCount(uint count); /** * Set the current desktop to @a current. * @returns True on success, false otherwise. * @see current * @see currentChanged * @see moveTo */ bool setCurrent(uint current); /** * Set the current desktop to @a current. * @returns True on success, false otherwise. * @see current * @see currentChanged * @see moveTo **/ bool setCurrent(VirtualDesktop *current); /** * Updates the layout to a new number of rows. The number of columns will be calculated accordingly */ void setRows(uint rows); /** * Called from within setCount() to ensure the desktop layout is still valid. */ void updateLayout(); /** * @param enabled wrapping around borders for navigation in desktop layout * @see isNavigationWrappingAround * @see navigationWrappingAroundChanged **/ void setNavigationWrappingAround(bool enabled); /** * Loads number of desktops and names from configuration file **/ void load(); /** * Saves number of desktops and names to configuration file **/ void save(); Q_SIGNALS: /** * Signal emitted whenever the number of virtual desktops changes. * @param previousCount The number of desktops prior to the change * @param newCount The new current number of desktops **/ void countChanged(uint previousCount, uint newCount); /** * Signal when the number of rows in the layout changes * @param new number of rows */ void rowsChanged(uint rows); /** * A new desktop has been created * @param desktop the new just crated desktop */ void desktopCreated(KWin::VirtualDesktop *desktop); /** * A desktop has been removed and is about to be deleted * @param desktop the desktop that has been removed. * It's guaranteed to stil la valid pointer when the signal arrives, * but it's about to be deleted. */ void desktopRemoved(KWin::VirtualDesktop *desktop); /** * Signal emitted whenever the current desktop changes. * @param previousDesktop The virtual desktop changed from * @param newDesktop The virtual desktop changed to **/ void currentChanged(uint previousDesktop, uint newDesktop); /** * Signal emitted whenever the desktop layout changes. * @param columns The new number of columns in the layout * @param rows The new number of rows in the layout **/ void layoutChanged(int columns, int rows); /** * Signal emitted whenever the navigationWrappingAround property changes. **/ void navigationWrappingAroundChanged(); private Q_SLOTS: /** * Common slot for all "Switch to Desktop n" shortcuts. * This method uses the sender() method to access some data. * DO NOT CALL DIRECTLY! ONLY TO BE USED FROM AN ACTION! **/ void slotSwitchTo(); /** * Slot for switch to next desktop action. **/ void slotNext(); /** * Slot for switch to previous desktop action. **/ void slotPrevious(); /** * Slot for switch to right desktop action. **/ void slotRight(); /** * Slot for switch to left desktop action. **/ void slotLeft(); /** * Slot for switch to desktop above action. **/ void slotUp(); /** * Slot for switch to desktop below action. **/ void slotDown(); private: /** * Generate a desktop layout from EWMH _NET_DESKTOP_LAYOUT property parameters. */ void setNETDesktopLayout(Qt::Orientation orientation, uint width, uint height, int startingCorner); /** * @returns A default name for the given @p desktop **/ QString defaultName(int desktop) const; /** * Creates all the global keyboard shortcuts for "Switch To Desktop n" actions. **/ void initSwitchToShortcuts(); /** * Creates an action and connects it to the @p slot in this Manager. This method is * meant to be used for the case that an additional information needs to be stored in * the action and the label. * @param name The name of the action to be created * @param label The localized name for the action to be created * @param value An additional value added to the label and to the created action * @param key The global shortcut for the action * @param slot The slot to invoke when the action is triggered **/ QAction *addAction(const QString &name, const KLocalizedString &label, uint value, const QKeySequence &key, void (VirtualDesktopManager::*slot)()); /** * Creates an action and connects it to the @p slot in this Manager. * Overloaded method for the case that no additional value needs to be passed to the action and * no global shortcut is defined by default. * @param name The name of the action to be created * @param label The localized name for the action to be created * @param slot The slot to invoke when the action is triggered **/ QAction *addAction(const QString &name, const QString &label, void (VirtualDesktopManager::*slot)()); QVector m_desktops; QPointer m_current; bool m_navigationWrapsAround; VirtualDesktopGrid m_grid; // TODO: QPointer NETRootInfo *m_rootInfo; KWayland::Server::PlasmaVirtualDesktopManagementInterface *m_virtualDesktopManagement = nullptr; KSharedConfig::Ptr m_config; - bool m_isLoading = false; KWIN_SINGLETON_VARIABLE(VirtualDesktopManager, s_manager) }; /** * Function object to select the desktop above in the layout. * Note: does not switch to the desktop! **/ class DesktopAbove { public: DesktopAbove() {} /** * @param desktop The desktop from which the desktop above should be selected. If @c 0 the current desktop is used * @param wrap Whether to wrap around if already topmost desktop * @returns Id of the desktop above @p desktop **/ uint operator() (uint desktop, bool wrap) { return (*this)(VirtualDesktopManager::self()->desktopForX11Id(desktop), wrap)->x11DesktopNumber(); } /** * @param desktop The desktop from which the desktop above should be selected. If @c 0 the current desktop is used * @param wrap Whether to wrap around if already topmost desktop * @returns the desktop above @p desktop **/ VirtualDesktop *operator() (VirtualDesktop *desktop, bool wrap) { return VirtualDesktopManager::self()->above(desktop, wrap); } }; /** * Function object to select the desktop below in the layout. * Note: does not switch to the desktop! **/ class DesktopBelow { public: DesktopBelow() {} /** * @param desktop The desktop from which the desktop below should be selected. If @c 0 the current desktop is used * @param wrap Whether to wrap around if already lowest desktop * @returns Id of the desktop below @p desktop **/ uint operator() (uint desktop, bool wrap) { return (*this)(VirtualDesktopManager::self()->desktopForX11Id(desktop), wrap)->x11DesktopNumber(); } /** * @param desktop The desktop from which the desktop below should be selected. If @c 0 the current desktop is used * @param wrap Whether to wrap around if already lowest desktop * @returns the desktop below @p desktop **/ VirtualDesktop *operator() (VirtualDesktop *desktop, bool wrap) { return VirtualDesktopManager::self()->below(desktop, wrap); } }; /** * Function object to select the desktop to the left in the layout. * Note: does not switch to the desktop! **/ class DesktopLeft { public: DesktopLeft() {} /** * @param desktop The desktop from which the desktop on the left should be selected. If @c 0 the current desktop is used * @param wrap Whether to wrap around if already leftmost desktop * @returns Id of the desktop left of @p desktop **/ uint operator() (uint desktop, bool wrap) { return (*this)(VirtualDesktopManager::self()->desktopForX11Id(desktop), wrap)->x11DesktopNumber(); } /** * @param desktop The desktop from which the desktop on the left should be selected. If @c 0 the current desktop is used * @param wrap Whether to wrap around if already leftmost desktop * @returns the desktop left of @p desktop **/ VirtualDesktop *operator() (VirtualDesktop *desktop, bool wrap) { return VirtualDesktopManager::self()->toLeft(desktop, wrap); } }; /** * Function object to select the desktop to the right in the layout. * Note: does not switch to the desktop! **/ class DesktopRight { public: DesktopRight() {} /** * @param desktop The desktop from which the desktop on the right should be selected. If @c 0 the current desktop is used * @param wrap Whether to wrap around if already rightmost desktop * @returns Id of the desktop right of @p desktop **/ uint operator() (uint desktop, bool wrap) { return (*this)(VirtualDesktopManager::self()->desktopForX11Id(desktop), wrap)->x11DesktopNumber(); } /** * @param desktop The desktop from which the desktop on the right should be selected. If @c 0 the current desktop is used * @param wrap Whether to wrap around if already rightmost desktop * @returns the desktop right of @p desktop **/ VirtualDesktop *operator() (VirtualDesktop *desktop, bool wrap) { return VirtualDesktopManager::self()->toRight(desktop, wrap); } }; /** * Function object to select the next desktop in the layout. * Note: does not switch to the desktop! **/ class DesktopNext { public: DesktopNext() {} /** * @param desktop The desktop from which the next desktop should be selected. If @c 0 the current desktop is used * @param wrap Whether to wrap around if already last desktop * @returns Id of the next desktop **/ uint operator() (uint desktop, bool wrap) { return (*this)(VirtualDesktopManager::self()->desktopForX11Id(desktop), wrap)->x11DesktopNumber(); } /** * @param desktop The desktop from which the next desktop should be selected. If @c 0 the current desktop is used * @param wrap Whether to wrap around if already last desktop * @returns the next desktop **/ VirtualDesktop *operator() (VirtualDesktop *desktop, bool wrap) { return VirtualDesktopManager::self()->next(desktop, wrap); } }; /** * Function object to select the previous desktop in the layout. * Note: does not switch to the desktop! **/ class DesktopPrevious { public: DesktopPrevious() {} /** * @param desktop The desktop from which the previous desktop should be selected. If @c 0 the current desktop is used * @param wrap Whether to wrap around if already first desktop * @returns Id of the previous desktop **/ uint operator() (uint desktop, bool wrap) { return (*this)(VirtualDesktopManager::self()->desktopForX11Id(desktop), wrap)->x11DesktopNumber(); } /** * @param desktop The desktop from which the previous desktop should be selected. If @c 0 the current desktop is used * @param wrap Whether to wrap around if already first desktop * @returns the previous desktop **/ VirtualDesktop *operator() (VirtualDesktop *desktop, bool wrap) { return VirtualDesktopManager::self()->previous(desktop, wrap); } }; /** * Helper function to get the ID of a virtual desktop in the direction from * the given @p desktop. If @c 0 the current desktop is used as a starting point. * @param desktop The desktop from which the desktop in given Direction should be selected. * @param wrap Whether desktop navigation wraps around at the borders of the layout * @returns The next desktop in specified direction **/ template uint getDesktop(int desktop = 0, bool wrap = true); template uint getDesktop(int d, bool wrap) { Direction direction; return direction(d, wrap); } inline int VirtualDesktopGrid::width() const { return m_size.width(); } inline int VirtualDesktopGrid::height() const { return m_size.height(); } inline const QSize &VirtualDesktopGrid::size() const { return m_size; } inline uint VirtualDesktopManager::maximum() { return 20; } inline uint VirtualDesktopManager::count() const { return m_desktops.count(); } inline bool VirtualDesktopManager::isNavigationWrappingAround() const { return m_navigationWrapsAround; } inline void VirtualDesktopManager::setRootInfo(NETRootInfo *info) { m_rootInfo = info; } inline void VirtualDesktopManager::setConfig(KSharedConfig::Ptr config) { m_config = config; } inline const VirtualDesktopGrid &VirtualDesktopManager::grid() const { return m_grid; } template void VirtualDesktopManager::moveTo(bool wrap) { Direction functor; setCurrent(functor(nullptr, wrap)); } } // namespace KWin #endif