diff --git a/abstract_client.h b/abstract_client.h --- a/abstract_client.h +++ b/abstract_client.h @@ -414,9 +414,13 @@ virtual bool performMouseCommand(Options::MouseCommand, const QPoint &globalPos); void setOnAllDesktops(bool set); void setDesktop(int); + virtual void unSetDesktop(int desktop); int desktop() const override { return m_desktop; } + virtual QList desktops() const { + return m_desktops; + } void setMinimized(bool set); /** * Minimizes this client plus its transients @@ -1086,6 +1090,7 @@ bool m_minimized = false; QTimer *m_autoRaiseTimer = nullptr; int m_desktop = 0; // 0 means not on any desktop yet + QList m_desktops; QString m_colorScheme; std::shared_ptr m_palette; diff --git a/abstract_client.cpp b/abstract_client.cpp --- a/abstract_client.cpp +++ b/abstract_client.cpp @@ -484,36 +484,69 @@ if (desktop != NET::OnAllDesktops) // Do range check desktop = qMax(1, qMin(numberOfDesktops, desktop)); desktop = qMin(numberOfDesktops, rules()->checkDesktop(desktop)); - if (m_desktop == desktop) + + VirtualDesktop *virtualDesktop = desktop == NET::OnAllDesktops ? nullptr : VirtualDesktopManager::self()->desktopForX11Id(desktop); + + if (waylandServer()) { + if (m_desktops.contains(virtualDesktop)) { + return; + } + } else if (m_desktop == desktop) { return; + } int was_desk = m_desktop; const bool wasOnCurrentDesktop = isOnCurrentDesktop(); m_desktop = desktop; + //can't check windowManagementInterface yet as it gets created only on first show + //on x11 only one desktop at a time + if (!waylandServer()) { + m_desktops.clear(); + } + if (desktop == NET::OnAllDesktops) { + m_desktops.clear(); + } else if (!m_desktops.contains(virtualDesktop)) { + if (m_desktops.count() == VirtualDesktopManager::self()->count() - 1) { + m_desktops.clear(); + m_desktop = NET::OnAllDesktops; + } else { + m_desktops << virtualDesktop; + } + } + if (windowManagementInterface()) { + if (m_desktop == NET::OnAllDesktops) { + windowManagementInterface()->setOnAllDesktops(true); + } else { + windowManagementInterface()->addPlasmaVirtualDesktop(virtualDesktop->id()); + } + } + + if (info) { - info->setDesktop(desktop); + info->setDesktop(m_desktop); } - if ((was_desk == NET::OnAllDesktops) != (desktop == NET::OnAllDesktops)) { + + if ((was_desk == NET::OnAllDesktops) != (m_desktop == NET::OnAllDesktops)) { // onAllDesktops changed workspace()->updateOnAllDesktopsOfTransients(this); } auto transients_stacking_order = workspace()->ensureStackingOrder(transients()); for (auto it = transients_stacking_order.constBegin(); it != transients_stacking_order.constEnd(); ++it) - (*it)->setDesktop(desktop); + (*it)->setDesktop(m_desktop); if (isModal()) // if a modal dialog is moved, move the mainwindow with it as otherwise // the (just moved) modal dialog will confusingly return to the mainwindow with // the next desktop change { foreach (AbstractClient * c2, mainClients()) - c2->setDesktop(desktop); + c2->setDesktop(m_desktop); } - doSetDesktop(desktop, was_desk); + doSetDesktop(m_desktop, was_desk); FocusChain::self()->update(this, FocusChain::MakeFirst); updateWindowRules(Rules::Desktop); @@ -529,6 +562,19 @@ Q_UNUSED(was_desk) } +void AbstractClient::unSetDesktop(int desktop) +{ + VirtualDesktop *virtualDesktop = VirtualDesktopManager::self()->desktopForX11Id(desktop); + + m_desktops.removeAll(virtualDesktop); + + if (!windowManagementInterface()) { + return; + } + + windowManagementInterface()->removePlasmaVirtualDesktop(virtualDesktop->id()); +} + void AbstractClient::setOnAllDesktops(bool b) { if ((b && isOnAllDesktops()) || @@ -802,16 +848,7 @@ } ); connect(this, &AbstractClient::captionChanged, w, [w, this] { w->setTitle(caption()); }); - connect(this, &AbstractClient::desktopChanged, w, - [w, this] { - if (isOnAllDesktops()) { - w->setOnAllDesktops(true); - return; - } - w->setVirtualDesktop(desktop() - 1); - w->setOnAllDesktops(false); - } - ); + connect(this, &AbstractClient::activeChanged, w, [w, this] { w->setActive(isActive()); }); connect(this, &AbstractClient::fullScreenChanged, w, [w, this] { w->setFullscreen(isFullScreen()); }); connect(this, &AbstractClient::keepAboveChanged, w, &PlasmaWindowInterface::setKeepAbove); @@ -906,6 +943,55 @@ setShade(set); } ); + + for (const auto vd : m_desktops) { + w->addPlasmaVirtualDesktop(vd->id()); + } + + //this is only for the legacy + connect(this, &AbstractClient::desktopChanged, w, + [w, this] { + if (isOnAllDesktops()) { + w->setOnAllDesktops(true); + return; + } + w->setVirtualDesktop(desktop() - 1); + w->setOnAllDesktops(false); + } + ); + + //Plasma Virtual desktop management + //show/hide when the window enters/exits from desktop + connect(w, &PlasmaWindowInterface::enterPlasmaVirtualDesktopRequested, this, + [this] (const QString &desktopId) { + VirtualDesktop *vd = VirtualDesktopManager::self()->desktopForId(desktopId.toUtf8()); + if (vd) { + workspace()->sendClientToDesktop(this, vd->x11DesktopNumber(), false); + } + } + ); + connect(w, &PlasmaWindowInterface::enterNewPlasmaVirtualDesktopRequested, this, + [this] () { + VirtualDesktopManager::self()->setCount(VirtualDesktopManager::self()->count() + 1); + workspace()->sendClientToDesktop(this, VirtualDesktopManager::self()->count(), false); + } + ); + connect(w, &PlasmaWindowInterface::leavePlasmaVirtualDesktopRequested, this, + [this] (const QString &desktopId) { + VirtualDesktop *vd = VirtualDesktopManager::self()->desktopForId(desktopId.toUtf8()); + if (vd) { + unSetDesktop(vd->x11DesktopNumber()); + } + } + ); + + //set initial visibility + if (m_desktops.isEmpty() || m_desktops.contains(VirtualDesktopManager::self()->currentDesktop())) { + emit windowShown(this); + } else { + workspace()->clientHidden(this); + } + m_windowManagementInterface = w; } diff --git a/activation.cpp b/activation.cpp --- a/activation.cpp +++ b/activation.cpp @@ -413,6 +413,7 @@ */ void Workspace::clientHidden(AbstractClient* c) { +qWarning()<isShown(true) << c->isOnCurrentDesktop() << c->isOnCurrentActivity(); assert(!c->isShown(true) || !c->isOnCurrentDesktop() || !c->isOnCurrentActivity()); activateNextClient(c); } diff --git a/autotests/mock_effectshandler.h b/autotests/mock_effectshandler.h --- a/autotests/mock_effectshandler.h +++ b/autotests/mock_effectshandler.h @@ -156,6 +156,9 @@ int numberOfDesktops() const override { return 0; } + int desktopNumberFromId(const QString&) const override { + return 0; + } int numScreens() const override { return 0; } diff --git a/autotests/test_window_paint_data.cpp b/autotests/test_window_paint_data.cpp --- a/autotests/test_window_paint_data.cpp +++ b/autotests/test_window_paint_data.cpp @@ -19,6 +19,7 @@ *********************************************************************/ #include +#include "../virtualdesktops.h" #include #include @@ -72,6 +73,7 @@ virtual void setData(int role, const QVariant &data); virtual void referencePreviousWindowPixmap() {} virtual void unreferencePreviousWindowPixmap() {} + QList desktops() const { return QList();} }; MockEffectWindow::MockEffectWindow(QObject *parent) diff --git a/deleted.h b/deleted.h --- a/deleted.h +++ b/deleted.h @@ -52,6 +52,7 @@ void discard(); virtual int desktop() const; virtual QStringList activities() const; + virtual QList desktops() const; virtual QPoint clientPos() const; virtual QSize clientSize() const; QPoint clientContentPos() const override { diff --git a/deleted.cpp b/deleted.cpp --- a/deleted.cpp +++ b/deleted.cpp @@ -141,6 +141,11 @@ return activityList; } +QList Deleted::desktops() const +{ + return QList(); +} + QPoint Deleted::clientPos() const { return contentsRect.topLeft(); diff --git a/effects.h b/effects.h --- a/effects.h +++ b/effects.h @@ -98,6 +98,7 @@ QString currentActivity() const override; int currentDesktop() const override; int numberOfDesktops() const override; + int desktopNumberFromId(const QString &id) const override; void setCurrentDesktop(int desktop) override; void setNumberOfDesktops(int desktops) override; QSize desktopGridSize() const override; @@ -388,6 +389,9 @@ void setData(int role, const QVariant &data); QVariant data(int role) const; + bool isOnDesktop(int d) const override; + QList desktops() const override; + void registerThumbnail(AbstractThumbnailItem *item); QHash > const &thumbnails() const { return m_thumbnails; diff --git a/effects.cpp b/effects.cpp --- a/effects.cpp +++ b/effects.cpp @@ -911,6 +911,15 @@ return VirtualDesktopManager::self()->count(); } +int EffectsHandlerImpl::desktopNumberFromId(const QString &id) const +{ + VirtualDesktop *vd = VirtualDesktopManager::self()->desktopForId(id.toUtf8()); + if (vd) { + return vd->x11DesktopNumber(); + } + return 0; +} + void EffectsHandlerImpl::setCurrentDesktop(int desktop) { VirtualDesktopManager::self()->setCurrent(desktop); @@ -1734,6 +1743,21 @@ return dataMap[ role ]; } +bool EffectWindowImpl::isOnDesktop(int d) const +{ + return toplevel->isOnDesktop(d); +} + +QList EffectWindowImpl::desktops() const +{ + QList desks; + + for (auto vd : toplevel->desktops()) { + desks << vd->x11DesktopNumber(); + } + return desks; +} + EffectWindow* effectWindow(Toplevel* w) { EffectWindowImpl* ret = w->effectWindow(); diff --git a/effects/desktopgrid/desktopgrid.cpp b/effects/desktopgrid/desktopgrid.cpp --- a/effects/desktopgrid/desktopgrid.cpp +++ b/effects/desktopgrid/desktopgrid.cpp @@ -42,6 +42,8 @@ #include #include +#include + namespace KWin { @@ -1373,16 +1375,30 @@ return allDesktops; } - if (w->desktop() > effects->numberOfDesktops() || w->desktop() < 1) { // sic! desktops are [1,n] - static QVector emptyVector; - emptyVector.resize(0); - return emptyVector; - } + //Wayland, arbitrary desktops per window + if (w->surface()) { + static QVector desktops; + desktops.resize(w->desktops().count()); + int i = 0; + for (const int desk : w->desktops()) { + if (desk > 0) { + desktops[i++] = desk; + } + } + return desktops; + //X11 one desktop per window + } else { + if (w->desktop() > effects->numberOfDesktops() || w->desktop() < 1) { // sic! desktops are [1,n] + static QVector emptyVector; + emptyVector.resize(0); + return emptyVector; + } - static QVector singleDesktop; - singleDesktop.resize(1); - singleDesktop[0] = w->desktop() - 1; - return singleDesktop; + static QVector singleDesktop; + singleDesktop.resize(1); + singleDesktop[0] = w->desktop() - 1; + return singleDesktop; + } } bool DesktopGridEffect::isActive() const diff --git a/libkwineffects/kwineffects.h b/libkwineffects/kwineffects.h --- a/libkwineffects/kwineffects.h +++ b/libkwineffects/kwineffects.h @@ -952,6 +952,7 @@ * @returns Total number of desktops currently in existence. */ virtual int numberOfDesktops() const = 0; + virtual int desktopNumberFromId(const QString &id) const = 0; /** * Set the current desktop to @a desktop. */ @@ -1987,10 +1988,11 @@ bool isOnAllActivities() const; QStringList activities() const; - bool isOnDesktop(int d) const; + virtual bool isOnDesktop(int d) const; bool isOnCurrentDesktop() const; bool isOnAllDesktops() const; int desktop() const; // prefer isOnXXX() + virtual QList desktops() const = 0; int x() const; int y() const; diff --git a/toplevel.h b/toplevel.h --- a/toplevel.h +++ b/toplevel.h @@ -285,6 +285,7 @@ * isOnDesktop() instead. */ virtual int desktop() const = 0; + virtual QList desktops() const = 0; virtual QStringList activities() const = 0; bool isOnDesktop(int d) const; bool isOnActivity(const QString &activity) const; @@ -776,7 +777,11 @@ inline bool Toplevel::isOnAllDesktops() const { - return desktop() == NET::OnAllDesktops; + return surface() + //Wayland + ? desktops().isEmpty() + //X11 + : desktop() == NET::OnAllDesktops; } inline bool Toplevel::isOnAllActivities() const @@ -786,7 +791,7 @@ inline bool Toplevel::isOnDesktop(int d) const { - return desktop() == d || /*desk == 0 ||*/ isOnAllDesktops(); + return (surface() ? desktops().contains(VirtualDesktopManager::self()->desktopForX11Id(d)) : desktop() == d) || isOnAllDesktops(); } inline bool Toplevel::isOnActivity(const QString &activity) const diff --git a/unmanaged.h b/unmanaged.h --- a/unmanaged.h +++ b/unmanaged.h @@ -39,6 +39,7 @@ static void deleteUnmanaged(Unmanaged* c); virtual int desktop() const; virtual QStringList activities() const; + virtual QList desktops() const override; virtual QPoint clientPos() const; virtual QSize clientSize() const; virtual QRect transparentRect() const; diff --git a/unmanaged.cpp b/unmanaged.cpp --- a/unmanaged.cpp +++ b/unmanaged.cpp @@ -123,6 +123,11 @@ return QStringList(); } +QList Unmanaged::desktops() const +{ + return QList(); +} + QPoint Unmanaged::clientPos() const { return QPoint(0, 0); // unmanaged windows don't have decorations diff --git a/useractions.h b/useractions.h --- a/useractions.h +++ b/useractions.h @@ -145,6 +145,11 @@ **/ void desktopPopupAboutToShow(); /** + * Adjusts the multipleDesktopsMenu popup to the current values and the location of + * the Client, Wayland only. + **/ + void multipleDesktopsPopupAboutToShow(); + /** * Adjusts the screen popup to the current values and the location of * the Client. **/ @@ -161,6 +166,12 @@ **/ void slotSendToDesktop(QAction *action); /** + * Toggle whether the Client is on a desktop (Wayland only) + * + * @param action Invoked Action containing the Desktop as data element + **/ + void slotToggleOnVirtualDesktop(QAction *action); + /** * Sends the Client to screen \a screen * * @param action Invoked Action containing the Screen as data element @@ -218,6 +229,10 @@ **/ QMenu* m_desktopMenu; /** + * The move to desktop sub menu, with the Wayland protocol. + **/ + QMenu* m_multipleDesktopsMenu; + /** * The move to screen sub menu. **/ QMenu* m_screenMenu; diff --git a/useractions.cpp b/useractions.cpp --- a/useractions.cpp +++ b/useractions.cpp @@ -402,6 +402,7 @@ delete m_menu; m_menu = NULL; m_desktopMenu = NULL; + m_multipleDesktopsMenu = nullptr; m_screenMenu = NULL; m_activityMenu = NULL; m_switchToTabMenu = NULL; @@ -417,6 +418,8 @@ if (VirtualDesktopManager::self()->count() == 1) { delete m_desktopMenu; m_desktopMenu = 0; + delete m_multipleDesktopsMenu; + m_multipleDesktopsMenu = nullptr; } else { initDesktopPopup(); } @@ -604,17 +607,33 @@ void UserActionsMenu::initDesktopPopup() { - if (m_desktopMenu) - return; + if (m_client.data()->surface()) { + if (m_multipleDesktopsMenu) { + return; + } - m_desktopMenu = new QMenu(m_menu); - connect(m_desktopMenu, &QMenu::triggered, this, &UserActionsMenu::slotSendToDesktop); - connect(m_desktopMenu, &QMenu::aboutToShow, this, &UserActionsMenu::desktopPopupAboutToShow); + m_multipleDesktopsMenu = new QMenu(m_menu); + connect(m_multipleDesktopsMenu, &QMenu::triggered, this, &UserActionsMenu::slotToggleOnVirtualDesktop); + connect(m_multipleDesktopsMenu, &QMenu::aboutToShow, this, &UserActionsMenu::multipleDesktopsPopupAboutToShow); - QAction *action = m_desktopMenu->menuAction(); - // set it as the first item - m_menu->insertAction(m_minimizeOperation, action); - action->setText(i18n("Move To &Desktop")); + QAction *action = m_multipleDesktopsMenu->menuAction(); + // set it as the first item + m_menu->insertAction(m_minimizeOperation, action); + action->setText(i18n("&Desktops")); + + } else { + if (m_desktopMenu) + return; + + m_desktopMenu = new QMenu(m_menu); + connect(m_desktopMenu, &QMenu::triggered, this, &UserActionsMenu::slotSendToDesktop); + connect(m_desktopMenu, &QMenu::aboutToShow, this, &UserActionsMenu::desktopPopupAboutToShow); + + QAction *action = m_desktopMenu->menuAction(); + // set it as the first item + m_menu->insertAction(m_minimizeOperation, action); + action->setText(i18n("Move To &Desktop")); + } } void UserActionsMenu::initScreenPopup() @@ -667,6 +686,7 @@ m_desktopMenu->addSeparator(); const uint BASE = 10; + for (uint i = 1; i <= vds->count(); ++i) { QString basic_name(QStringLiteral("%1 %2")); if (i < BASE) { @@ -690,6 +710,61 @@ action->setEnabled(false); } +void UserActionsMenu::multipleDesktopsPopupAboutToShow() +{ + if (!m_multipleDesktopsMenu) + return; + const VirtualDesktopManager *vds = VirtualDesktopManager::self(); + + m_multipleDesktopsMenu->clear(); + m_multipleDesktopsMenu->setPalette(m_client.data()->palette()); + QAction *action = m_multipleDesktopsMenu->addAction(i18n("&All Desktops")); + action->setData(0); + action->setCheckable(true); + static QPointer allDesktopsGroup; + if (!allDesktopsGroup) { + allDesktopsGroup = new QActionGroup(m_multipleDesktopsMenu); + } + allDesktopsGroup->addAction(action); + + if (!m_client.isNull() && m_client.data()->isOnAllDesktops()) { + action->setChecked(true); + } + m_multipleDesktopsMenu->addSeparator(); + + + const uint BASE = 10; + + for (uint i = 1; i <= vds->count(); ++i) { + QString basic_name(QStringLiteral("%1 %2")); + if (i < BASE) { + basic_name.prepend(QLatin1Char('&')); + } + QWidgetAction *action = new QWidgetAction(m_multipleDesktopsMenu); + QCheckBox *box = new QCheckBox(basic_name.arg(i).arg(vds->name(i).replace(QLatin1Char('&'), QStringLiteral("&&"))), m_multipleDesktopsMenu); + action->setDefaultWidget(box); + + box->setBackgroundRole(m_multipleDesktopsMenu->backgroundRole()); + box->setForegroundRole(m_multipleDesktopsMenu->foregroundRole()); + box->setPalette(m_multipleDesktopsMenu->palette()); + connect(box, &QCheckBox::clicked, action, &QAction::triggered); + m_multipleDesktopsMenu->addAction(action); + action->setData(i); + + if (!m_client.isNull() && + !m_client.data()->isOnAllDesktops() && m_client.data()->isOnDesktop(i)) { + box->setChecked(true); + } + } + + m_multipleDesktopsMenu->addSeparator(); + action = m_multipleDesktopsMenu->addAction(i18nc("Create a new desktop and move there the window", "&New Desktop")); + action->setData(vds->count() + 1); + + if (vds->count() >= vds->maximum()) + action->setEnabled(false); +} + void UserActionsMenu::screenPopupAboutToShow() { if (!m_screenMenu) { @@ -816,6 +891,35 @@ ws->sendClientToDesktop(m_client.data(), desk, false); } +void UserActionsMenu::slotToggleOnVirtualDesktop(QAction *action) +{ + bool ok = false; + uint desk = action->data().toUInt(&ok); + if (!ok) { + return; + } + if (m_client.isNull()) { + return; + } + + Workspace *ws = Workspace::self(); + VirtualDesktopManager *vds = VirtualDesktopManager::self(); + if (desk == 0) { + // the 'on_all_desktops' menu entry + m_client.data()->setOnAllDesktops(!m_client.data()->isOnAllDesktops()); + return; + } else if (desk > vds->count()) { + vds->setCount(desk); + } + + VirtualDesktop *virtualDesktop = VirtualDesktopManager::self()->desktopForX11Id(desk); + if (m_client.data()->desktops().contains(virtualDesktop)) { + m_client.data()->unSetDesktop(desk); + } else { + ws->sendClientToDesktop(m_client.data(), desk, false); + } +} + void UserActionsMenu::slotSendToScreen(QAction *action) { const int screen = action->data().toInt(); diff --git a/virtualdesktops.h b/virtualdesktops.h --- a/virtualdesktops.h +++ b/virtualdesktops.h @@ -264,6 +264,29 @@ 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(). + * @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(); @@ -334,18 +357,21 @@ * @param newCount The new current number of desktops **/ void countChanged(uint previousCount, uint newCount); + /** - * Signal emitted whenever the number of virtual desktops changes in a way - * that existing desktops are removed. - * - * The signal is emitted after the @c count property has been updated but prior - * to the @link countChanged signal being emitted. - * @param previousCount The number of desktops prior to the change. - * @see countChanged - * @see setCount - * @see count - **/ - void desktopsRemoved(uint previousCount); + * 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 @@ -397,21 +423,6 @@ private: /** - * This method is called when the number of desktops is updated in a way that desktops - * are removed. At the time when this method is invoked the count property is already - * updated but the corresponding signal has not been emitted yet. - * - * Ensures that in case the current desktop is on one of the removed - * desktops the last desktop after the change becomes the new desktop. - * Emits the signal @link desktopsRemoved. - * - * @param previousCount The number of desktops prior to the change. - * @param previousCurrent The number of the previously current desktop. - * @see setCount - * @see desktopsRemoved - **/ - void handleDesktopsRemoved(uint previousCount, uint previousCurrent); - /** * Generate a desktop layout from EWMH _NET_DESKTOP_LAYOUT property parameters. */ void setNETDesktopLayout(Qt::Orientation orientation, uint width, uint height, int startingCorner); @@ -451,6 +462,7 @@ // TODO: QPointer NETRootInfo *m_rootInfo; KSharedConfig::Ptr m_config; + bool m_isLoading = false; KWIN_SINGLETON_VARIABLE(VirtualDesktopManager, s_manager) }; diff --git a/virtualdesktops.cpp b/virtualdesktops.cpp --- a/virtualdesktops.cpp +++ b/virtualdesktops.cpp @@ -27,9 +27,10 @@ #include // Qt #include +#include #include - +#include namespace KWin { extern int screen_number; @@ -69,6 +70,7 @@ : m_size(1, 2) // Default to tow rows , m_grid(QVector>{QVector{}, QVector{}}) { + } VirtualDesktopGrid::~VirtualDesktopGrid() = default; @@ -325,6 +327,91 @@ 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; + } else { + 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(); + + 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); + } + + emit desktopRemoved(desktop); + + desktop->deleteLater(); +} + uint VirtualDesktopManager::current() const { return m_current ? m_current->x11DesktopNumber() : 0; @@ -364,33 +451,51 @@ // nothing to change return; } + QList newDesktops; const uint oldCount = m_desktops.count(); - const uint oldCurrent = current(); - while (uint(m_desktops.count()) > count) { - delete m_desktops.takeLast(); - } - while (uint(m_desktops.count()) < count) { - auto vd = new VirtualDesktop(this); - vd->setX11DesktopNumber(m_desktops.count() + 1); - m_desktops << vd; - } - if (oldCount > count) { - handleDesktopsRemoved(oldCount, oldCurrent); + //this explicit check makes it more readable + if ((uint)m_desktops.count() > count) { + const auto desktopsToRemove = m_desktops.mid(count-1); + m_desktops.resize(count); + int oldCurrent = current(); + for (auto desktop : desktopsToRemove) { + emit desktopRemoved(desktop); + desktop->deleteLater(); + } + int newCurrent = qMin(oldCurrent, m_desktops.count()); + m_current = m_desktops.at(newCurrent - 1); + if (oldCurrent != newCurrent) { + emit currentChanged(oldCurrent, newCurrent); + } + } else { + while (uint(m_desktops.count()) < count) { + auto vd = new VirtualDesktop(this); + vd->setX11DesktopNumber(m_desktops.count() + 1); + if (!m_isLoading) { + 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(); - emit countChanged(oldCount, m_desktops.count()); -} - -void VirtualDesktopManager::handleDesktopsRemoved(uint previousCount, uint previousCurrent) -{ - if (!m_current) { - m_current = m_desktops.last(); - emit currentChanged(previousCurrent, m_current->x11DesktopNumber()); + for (auto vd : newDesktops) { + emit desktopCreated(vd); } - emit desktopsRemoved(previousCount); + emit countChanged(oldCount, m_desktops.count()); } void VirtualDesktopManager::updateRootInfo() @@ -437,6 +542,8 @@ if (!m_config) { return; } + //FIXME: how to avoid this? + m_isLoading = true; QString groupname; if (screen_number == 0) { groupname = QStringLiteral("Desktops"); @@ -446,14 +553,31 @@ KConfigGroup group(m_config, groupname); const int n = group.readEntry("Number", 1); setCount(n); - if (m_rootInfo) { - for (int i = 1; i <= n; i++) { - QString s = group.readEntry(QStringLiteral("Name_%1").arg(i), i18n("Desktop %1", i)); + //Use kactivitymanagerdrc directly? + + 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()); - // TODO: update desktop focus chain, why? -// m_desktopFocusChain.value()[i-1] = i; } + m_desktops[i-1]->setName(s.toUtf8().data()); + + s = group.readEntry(QStringLiteral("Id_%1").arg(i), QString()); + if (s.isEmpty()) { + s = QUuid::createUuid().toString(); + } + //load gets called 2 times, see workspace.cpp line 416 and BUG 385260 + if (m_desktops[i-1]->id().isEmpty()) { + m_desktops[i-1]->setId(s.toUtf8().data()); + } else { + Q_ASSERT(m_desktops[i-1]->id() == s.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 @@ -464,7 +588,9 @@ m_rootInfo->setDesktopLayout(NET::OrientationHorizontal, columns, rows, NET::DesktopLayoutCornerTopLeft); m_rootInfo->activate(); } + s_loadingDesktopSettings = false; + m_isLoading = false; } void VirtualDesktopManager::save() @@ -502,6 +628,8 @@ group.deleteEntry(QStringLiteral("Name_%1").arg(i)); } } + + group.writeEntry(QStringLiteral("Id_%1").arg(i), m_desktops[i-1]->id()); } // Save to disk diff --git a/wayland_server.h b/wayland_server.h --- a/wayland_server.h +++ b/wayland_server.h @@ -54,6 +54,7 @@ class OutputInterface; class PlasmaShellInterface; class PlasmaShellSurfaceInterface; +class PlasmaVirtualDesktopManagementInterface; class PlasmaWindowManagementInterface; class QtSurfaceExtensionInterface; class OutputManagementInterface; @@ -99,6 +100,9 @@ KWayland::Server::ShellInterface *shell() { return m_shell; } + KWayland::Server::PlasmaVirtualDesktopManagementInterface *virtualDesktopManagement() { + return m_virtualDesktopManagement; + } KWayland::Server::PlasmaWindowManagementInterface *windowManagement() { return m_windowManagement; } @@ -226,6 +230,7 @@ KWayland::Server::XdgShellInterface *m_xdgShell6 = nullptr; KWayland::Server::PlasmaShellInterface *m_plasmaShell = nullptr; KWayland::Server::PlasmaWindowManagementInterface *m_windowManagement = nullptr; + KWayland::Server::PlasmaVirtualDesktopManagementInterface *m_virtualDesktopManagement = nullptr; KWayland::Server::QtSurfaceExtensionInterface *m_qtExtendedSurface = nullptr; KWayland::Server::ServerSideDecorationManagerInterface *m_decorationManager = nullptr; KWayland::Server::OutputManagementInterface *m_outputManagement = nullptr; diff --git a/wayland_server.cpp b/wayland_server.cpp --- a/wayland_server.cpp +++ b/wayland_server.cpp @@ -43,6 +43,7 @@ #include #include #include +#include #include #include #include @@ -328,6 +329,12 @@ workspace()->setShowingDesktop(set); } ); + + + m_virtualDesktopManagement = m_display->createPlasmaVirtualDesktopManagement(m_display); + m_virtualDesktopManagement->create(); + m_windowManagement->setPlasmaVirtualDesktopManagementInterface(m_virtualDesktopManagement); + auto shadowManager = m_display->createShadowManager(m_display); shadowManager->create(); @@ -385,6 +392,69 @@ void WaylandServer::initWorkspace() { + //TODO: RFC: those connections are better here or in VirtualDesktopManager itself? + //handle created: from VirtualDesktopManager to the wayland interface + connect(VirtualDesktopManager::self(), &VirtualDesktopManager::desktopCreated, this, + [this](VirtualDesktop *desktop) { + 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(VirtualDesktopManager::self(), &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 = VirtualDesktopManager::self()->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 + VirtualDesktopManager::self()->removeVirtualDesktop(id.toUtf8()); + } + ); + + for (quint32 i = 1; i <= VirtualDesktopManager::self()->count(); ++i) { + VirtualDesktop *internalDesktop = VirtualDesktopManager::self()->desktopForX11Id(i); + PlasmaVirtualDesktopInterface *desktop = m_virtualDesktopManagement->createDesktop(internalDesktop->id()); + + desktop->setName(desktop->name()); + desktop->sendDone(); + + connect(desktop, &PlasmaVirtualDesktopInterface::activateRequested, this, + [this, desktop] () { + VirtualDesktopManager::self()->setCurrent(VirtualDesktopManager::self()->desktopForId(desktop->id().toUtf8())); + } + ); + } + //Now we are sure all ids are there + VirtualDesktopManager::self()->save(); + + connect(VirtualDesktopManager::self(), &VirtualDesktopManager::currentChanged, this, + [this]() { + m_virtualDesktopManagement->setActiveDesktop(VirtualDesktopManager::self()->currentDesktop()->id()); + } + ); + if (m_windowManagement) { connect(workspace(), &Workspace::showingDesktopChanged, this, [this] (bool set) { diff --git a/workspace.h b/workspace.h --- a/workspace.h +++ b/workspace.h @@ -457,7 +457,6 @@ void slotReloadConfig(); void updateCurrentActivity(const QString &new_activity); // virtual desktop handling - void moveClientsFromRemovedDesktops(); void slotDesktopCountChanged(uint previousCount, uint newCount); void slotCurrentDesktopChanged(uint oldDesktop, uint newDesktop); diff --git a/workspace.cpp b/workspace.cpp --- a/workspace.cpp +++ b/workspace.cpp @@ -219,7 +219,29 @@ // create VirtualDesktopManager and perform dependency injection VirtualDesktopManager *vds = VirtualDesktopManager::self(); - connect(vds, SIGNAL(desktopsRemoved(uint)), SLOT(moveClientsFromRemovedDesktops())); + connect(vds, &VirtualDesktopManager::desktopRemoved, this, + [this](KWin::VirtualDesktop *desktop) { + //Wayland + if (waylandServer()) { + for (auto it = m_allClients.constBegin(); it != m_allClients.constEnd(); ++it) { + if (!(*it)->isOnAllDesktops() && (*it)->desktops().count() == 1) { + const VirtualDesktop *otherDesktop = (*it)->desktops().first(); + if (desktop == otherDesktop) { + sendClientToDesktop(*it, qMin(desktop->x11DesktopNumber(), VirtualDesktopManager::self()->count()), true); + } + } + } + //X11 + } else { + for (auto it = m_allClients.constBegin(); it != m_allClients.constEnd(); ++it) { + if (!(*it)->isOnAllDesktops() && ((*it)->desktop() > static_cast(VirtualDesktopManager::self()->count()))) { + sendClientToDesktop(*it, VirtualDesktopManager::self()->count(), true); + } + } + } + } + ); + connect(vds, SIGNAL(countChanged(uint,uint)), SLOT(slotDesktopCountChanged(uint,uint))); connect(vds, SIGNAL(currentChanged(uint,uint)), SLOT(slotCurrentDesktopChanged(uint,uint))); vds->setNavigationWrappingAround(options->isRollOverDesktops()); @@ -1074,14 +1096,6 @@ #endif } -void Workspace::moveClientsFromRemovedDesktops() -{ - for (auto it = m_allClients.constBegin(); it != m_allClients.constEnd(); ++it) { - if (!(*it)->isOnAllDesktops() && (*it)->desktop() > static_cast(VirtualDesktopManager::self()->count())) - sendClientToDesktop(*it, VirtualDesktopManager::self()->count(), true); - } -} - void Workspace::slotDesktopCountChanged(uint previousCount, uint newCount) { Q_UNUSED(previousCount)