[wayland] Use the new plasma virtual desktop protocol

Summary:
implement virtual desktop support for Wayland.
use the new virtual desktop protocol from D12820
The VirtualDesktopManager class needed some big change in order
to accomodate it, which is where most changes are.
Other than that, it's mostly connections to wire up
VirtualDesktopsManager and VirtualDesktopsManagement(the wayland protocol impl)

Depends on D12820
Other notable detail, is the client visibility updated to reflect the presence
of the client in the plasmavirtualdesktop.
(and the unSetDesktop concept)

Test Plan: used a bit a plasma session together with D12820, D13748 and D13746

Reviewers: #plasma, #kwin, graesslin, davidedmundson

Reviewed By: #plasma, #kwin, davidedmundson

Subscribers: hein, zzag, davidedmundson, kwin

Tags: #kwin

Maniphest Tasks: T4457

Differential Revision: https://phabricator.kde.org/D13887
This commit is contained in:
David Edmundson 2018-10-29 22:29:15 +00:00 committed by Marco Martin
parent e0aa3a3049
commit 7e8facc3fd
30 changed files with 1326 additions and 118 deletions

View file

@ -381,6 +381,7 @@ add_subdirectory(helpers)
set(kwin_KDEINIT_SRCS
workspace.cpp
dbusinterface.cpp
virtualdesktopsdbustypes.cpp
abstract_client.cpp
client.cpp
client_machine.cpp
@ -533,6 +534,7 @@ qt5_add_dbus_adaptor( kwin_KDEINIT_SRCS org.kde.kwin.Compositing.xml dbusinterfa
qt5_add_dbus_adaptor( kwin_KDEINIT_SRCS org.kde.kwin.ColorCorrect.xml colorcorrection/colorcorrectdbusinterface.h KWin::ColorCorrect::ColorCorrectDBusInterface )
qt5_add_dbus_adaptor( kwin_KDEINIT_SRCS ${kwin_effects_dbus_xml} effects.h KWin::EffectsHandlerImpl )
qt5_add_dbus_adaptor( kwin_KDEINIT_SRCS org.kde.kwin.OrientationSensor.xml orientation_sensor.h KWin::OrientationSensor)
qt5_add_dbus_adaptor( kwin_KDEINIT_SRCS org.kde.KWin.VirtualDesktopManager.xml dbusinterface.h KWin::VirtualDesktopManagerDBusInterface )
qt5_add_dbus_interface( kwin_KDEINIT_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/org.freedesktop.ScreenSaver.xml screenlocker_interface)
@ -671,6 +673,7 @@ install(
org.kde.kwin.Compositing.xml
org.kde.kwin.ColorCorrect.xml
org.kde.kwin.Effects.xml
org.kde.KWin.VirtualDesktopManager.xml
DESTINATION
${KDE_INSTALL_DBUSINTERFACEDIR}
)

View file

@ -489,16 +489,44 @@ void AbstractClient::setDesktop(int desktop)
if (desktop != NET::OnAllDesktops) // Do range check
desktop = qMax(1, qMin(numberOfDesktops, desktop));
desktop = qMin(numberOfDesktops, rules()->checkDesktop(desktop));
if (m_desktop == desktop)
return;
int was_desk = m_desktop;
const bool wasOnCurrentDesktop = isOnCurrentDesktop();
m_desktop = desktop;
VirtualDesktop *virtualDesktop = desktop == NET::OnAllDesktops ? nullptr : VirtualDesktopManager::self()->desktopForX11Id(desktop);
// Don't do anything if we're already there, if the desktop is already in desktops or if the desktop is NET::OnAllDesktops and m_desktops is already empty.
if (m_desktops.contains(virtualDesktop) ||
(desktop == NET::OnAllDesktops && m_desktops.isEmpty())) {
return;
}
int was_desk = AbstractClient::desktop();
const bool wasOnCurrentDesktop = isOnCurrentDesktop() && was_desk >= 0;
//on x11 we can have only one desktop at a time
if (kwinApp()->operationMode() == Application::OperationModeX11) {
m_desktops.clear();
}
if (desktop == NET::OnAllDesktops) {
m_desktops.clear();
} else {
//if would become on all desktops, clear the list, as empty == on all desktops
if (m_desktops.count() > 1 && static_cast<uint>(m_desktops.count()) == VirtualDesktopManager::self()->count() - 1) {
m_desktops.clear();
} else {
m_desktops << virtualDesktop;
}
}
if (windowManagementInterface()) {
if (m_desktops.isEmpty()) {
windowManagementInterface()->setOnAllDesktops(true);
} else {
windowManagementInterface()->addPlasmaVirtualDesktop(virtualDesktop->id());
}
}
if (info) {
info->setDesktop(desktop);
}
if ((was_desk == NET::OnAllDesktops) != (desktop == NET::OnAllDesktops)) {
// onAllDesktops changed
workspace()->updateOnAllDesktopsOfTransients(this);
@ -526,6 +554,7 @@ void AbstractClient::setDesktop(int desktop)
emit desktopChanged();
if (wasOnCurrentDesktop != isOnCurrentDesktop())
emit desktopPresenceChanged(this, was_desk);
emit x11DesktopIdsChanged();
}
void AbstractClient::doSetDesktop(int desktop, int was_desk)
@ -534,6 +563,34 @@ void AbstractClient::doSetDesktop(int desktop, int was_desk)
Q_UNUSED(was_desk)
}
void AbstractClient::unSetDesktop(int desktop)
{
// Case in which we are on all desktops and gets asked to unset
if (desktop == NET::OnAllDesktops) {
if (m_desktops.isEmpty()) {
setOnAllDesktops(false);
}
return;
}
// Out of range
if (desktop < 1 || desktop > VirtualDesktopManager::self()->count()) {
return;
}
VirtualDesktop *virtualDesktop = VirtualDesktopManager::self()->desktopForX11Id(desktop);
m_desktops.removeAll(virtualDesktop);
if (!windowManagementInterface()) {
return;
}
windowManagementInterface()->removePlasmaVirtualDesktop(virtualDesktop->id());
emit x11DesktopIdsChanged();
}
void AbstractClient::setOnAllDesktops(bool b)
{
if ((b && isOnAllDesktops()) ||
@ -545,6 +602,20 @@ void AbstractClient::setOnAllDesktops(bool b)
setDesktop(VirtualDesktopManager::self()->current());
}
QList<int> AbstractClient::x11DesktopIds() const
{
const auto desks = desktops();
QList<int> x11Ids;
x11Ids.reserve(desks.count());
std::transform(desks.constBegin(), desks.constEnd(),
std::back_inserter(x11Ids),
[] (const VirtualDesktop *vd) {
return vd->x11DesktopNumber();
}
);
return x11Ids;
}
bool AbstractClient::isShadeable() const
{
return false;
@ -808,16 +879,7 @@ void AbstractClient::setupWindowManagementInterface()
}
);
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);
@ -912,6 +974,48 @@ void AbstractClient::setupWindowManagementInterface()
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());
}
}
);
m_windowManagementInterface = w;
}

View file

@ -84,12 +84,17 @@ class KWIN_EXPORT AbstractClient : public Toplevel
Q_PROPERTY(bool active READ isActive NOTIFY activeChanged)
/**
* The desktop this Client is on. If the Client is on all desktops the property has value -1.
* This is a legacy property, use x11DesktopIds instead
**/
Q_PROPERTY(int desktop READ desktop WRITE setDesktop NOTIFY desktopChanged)
/**
* Whether the Client is on all desktops. That is desktop is -1.
**/
Q_PROPERTY(bool onAllDesktops READ isOnAllDesktops WRITE setOnAllDesktops NOTIFY desktopChanged)
/**
* The x11 ids for all desktops this client is in. On X11 this list will always have a length of 1
**/
Q_PROPERTY(QList<int> x11DesktopIds READ x11DesktopIds NOTIFY x11DesktopIdsChanged)
/**
* Indicates that the window should not be included on a taskbar.
**/
@ -423,9 +428,18 @@ public:
virtual bool performMouseCommand(Options::MouseCommand, const QPoint &globalPos);
void setOnAllDesktops(bool set);
void setDesktop(int);
Q_INVOKABLE virtual void unSetDesktop(int desktop);
int desktop() const override {
return m_desktop;
return m_desktops.isEmpty() ? (int)NET::OnAllDesktops : m_desktops.last()->x11DesktopNumber();
}
virtual QList<VirtualDesktop *> desktops() const {
return m_desktops;
}
void removeDesktop(VirtualDesktop *desktop) {
m_desktops.removeAll(desktop);
}
QList<int> x11DesktopIds() const;
void setMinimized(bool set);
/**
* Minimizes this client plus its transients
@ -757,6 +771,7 @@ Q_SIGNALS:
void demandsAttentionChanged();
void desktopPresenceChanged(KWin::AbstractClient*, int); // to be forwarded by Workspace
void desktopChanged();
void x11DesktopIdsChanged();
void shadeChanged();
void minimizedChanged();
void clientMinimized(KWin::AbstractClient* client, bool animate);
@ -1107,7 +1122,7 @@ private:
bool m_demandsAttention = false;
bool m_minimized = false;
QTimer *m_autoRaiseTimer = nullptr;
int m_desktop = 0; // 0 means not on any desktop yet
QList <VirtualDesktop *> m_desktops;
QString m_colorScheme;
std::shared_ptr<Decoration::DecorationPalette> m_palette;

View file

@ -43,6 +43,7 @@ target_link_libraries( testVirtualDesktops
KF5::GlobalAccel
KF5::ConfigCore
KF5::WindowSystem
KF5::WaylandServer
)
add_test(NAME kwin-testVirtualDesktops COMMAND testVirtualDesktops)
ecm_mark_as_test(testVirtualDesktops)
@ -336,6 +337,7 @@ target_link_libraries(testScreenEdges
KF5::GlobalAccel
KF5::Notifications
KF5::WindowSystem
KF5::WaylandServer
XCB::XCB
XCB::RANDR
XCB::XFIXES

View file

@ -43,6 +43,10 @@ private Q_SLOTS:
void testNetCurrentDesktop();
void testLastDesktopRemoved_data();
void testLastDesktopRemoved();
void testWindowOnMultipleDesktops_data();
void testWindowOnMultipleDesktops();
void testRemoveDesktopWithWindow_data();
void testRemoveDesktopWithWindow();
};
void VirtualDesktopTest::initTestCase()
@ -157,12 +161,154 @@ void VirtualDesktopTest::testLastDesktopRemoved()
QSignalSpy desktopPresenceChangedSpy(client, &ShellClient::desktopPresenceChanged);
QVERIFY(desktopPresenceChangedSpy.isValid());
QCOMPARE(client->desktops().count(), 1u);
QCOMPARE(VirtualDesktopManager::self()->currentDesktop(), client->desktops().first());
// and remove last desktop
VirtualDesktopManager::self()->setCount(1);
QCOMPARE(VirtualDesktopManager::self()->count(), 1u);
// now the client should be moved as well
QTRY_COMPARE(desktopPresenceChangedSpy.count(), 1);
QCOMPARE(client->desktop(), 1);
QCOMPARE(client->desktops().count(), 1u);
QCOMPARE(VirtualDesktopManager::self()->currentDesktop(), client->desktops().first());
}
void VirtualDesktopTest::testWindowOnMultipleDesktops_data()
{
QTest::addColumn<Test::ShellSurfaceType>("type");
QTest::newRow("wlShell") << Test::ShellSurfaceType::WlShell;
QTest::newRow("xdgShellV5") << Test::ShellSurfaceType::XdgShellV5;
QTest::newRow("xdgShellV6") << Test::ShellSurfaceType::XdgShellV6;
}
void VirtualDesktopTest::testWindowOnMultipleDesktops()
{
// first create two new desktops
QCOMPARE(VirtualDesktopManager::self()->count(), 1u);
VirtualDesktopManager::self()->setCount(3);
QCOMPARE(VirtualDesktopManager::self()->count(), 3u);
// switch to last desktop
VirtualDesktopManager::self()->setCurrent(VirtualDesktopManager::self()->desktops().last());
QCOMPARE(VirtualDesktopManager::self()->current(), 3u);
// now create a window on this desktop
QScopedPointer<Surface> surface(Test::createSurface());
QFETCH(Test::ShellSurfaceType, type);
QScopedPointer<QObject> shellSurface(Test::createShellSurface(type, surface.data()));
auto client = Test::renderAndWaitForShown(surface.data(), QSize(100, 50), Qt::blue);
QVERIFY(client);
QCOMPARE(client->desktop(), 3u);
QSignalSpy desktopPresenceChangedSpy(client, &ShellClient::desktopPresenceChanged);
QVERIFY(desktopPresenceChangedSpy.isValid());
QCOMPARE(client->desktops().count(), 1u);
QCOMPARE(VirtualDesktopManager::self()->currentDesktop(), client->desktops().first());
//Set the window on desktop 2 as well
client->setDesktop(2u);
QCOMPARE(client->desktops().count(), 2u);
QCOMPARE(VirtualDesktopManager::self()->desktops()[2], client->desktops()[0]);
QCOMPARE(VirtualDesktopManager::self()->desktops()[1], client->desktops()[1]);
QVERIFY(client->isOnDesktop(2));
QVERIFY(client->isOnDesktop(3));
//leave desktop 3
client->unSetDesktop(3);
QCOMPARE(client->desktops().count(), 1u);
//leave desktop 2
client->unSetDesktop(2);
QCOMPARE(client->desktops().count(), 0u);
//we should be on all desktops now
QVERIFY(client->isOnAllDesktops());
//put on desktop 1
client->setDesktop(1);
QVERIFY(client->isOnDesktop(1));
QVERIFY(!client->isOnDesktop(2));
QVERIFY(!client->isOnDesktop(3));
QCOMPARE(client->desktops().count(), 1u);
//put on desktop 2
client->setDesktop(2);
QVERIFY(client->isOnDesktop(1));
QVERIFY(client->isOnDesktop(2));
QVERIFY(!client->isOnDesktop(3));
QCOMPARE(client->desktops().count(), 2u);
//put on desktop 3
client->setDesktop(3);
QVERIFY(client->isOnDesktop(1));
QVERIFY(client->isOnDesktop(2));
QVERIFY(client->isOnDesktop(3));
QVERIFY(client->isOnAllDesktops());
//when it gets on all desktops, it loses all desktops()
QCOMPARE(client->desktops().count(), 0u);
}
void VirtualDesktopTest::testRemoveDesktopWithWindow_data()
{
QTest::addColumn<Test::ShellSurfaceType>("type");
QTest::newRow("wlShell") << Test::ShellSurfaceType::WlShell;
QTest::newRow("xdgShellV5") << Test::ShellSurfaceType::XdgShellV5;
QTest::newRow("xdgShellV6") << Test::ShellSurfaceType::XdgShellV6;
}
void VirtualDesktopTest::testRemoveDesktopWithWindow()
{
// first create two new desktops
QCOMPARE(VirtualDesktopManager::self()->count(), 1u);
VirtualDesktopManager::self()->setCount(3);
QCOMPARE(VirtualDesktopManager::self()->count(), 3u);
// switch to last desktop
VirtualDesktopManager::self()->setCurrent(VirtualDesktopManager::self()->desktops().last());
QCOMPARE(VirtualDesktopManager::self()->current(), 3u);
// now create a window on this desktop
QScopedPointer<Surface> surface(Test::createSurface());
QFETCH(Test::ShellSurfaceType, type);
QScopedPointer<QObject> shellSurface(Test::createShellSurface(type, surface.data()));
auto client = Test::renderAndWaitForShown(surface.data(), QSize(100, 50), Qt::blue);
QVERIFY(client);
QCOMPARE(client->desktop(), 3u);
QSignalSpy desktopPresenceChangedSpy(client, &ShellClient::desktopPresenceChanged);
QVERIFY(desktopPresenceChangedSpy.isValid());
QCOMPARE(client->desktops().count(), 1u);
QCOMPARE(VirtualDesktopManager::self()->currentDesktop(), client->desktops().first());
//Set the window on desktop 2 as well
client->setDesktop(2u);
QCOMPARE(client->desktops().count(), 2u);
QCOMPARE(VirtualDesktopManager::self()->desktops()[2], client->desktops()[0]);
QCOMPARE(VirtualDesktopManager::self()->desktops()[1], client->desktops()[1]);
QVERIFY(client->isOnDesktop(2));
QVERIFY(client->isOnDesktop(3));
//remove desktop 3
VirtualDesktopManager::self()->setCount(2);
QCOMPARE(client->desktops().count(), 1u);
//window is only on desktop 2
QCOMPARE(VirtualDesktopManager::self()->desktops()[1], client->desktops()[0]);
//Again 3 desktops
VirtualDesktopManager::self()->setCount(3);
//move window to be only on desktop 3
client->setDesktop(3);
client->unSetDesktop(2);
QCOMPARE(client->desktops().count(), 1u);
//window is only on desktop 3
QCOMPARE(VirtualDesktopManager::self()->desktops()[2], client->desktops()[0]);
//remove desktop 3
VirtualDesktopManager::self()->setCount(2);
QCOMPARE(client->desktops().count(), 1u);
//window is only on desktop 2
QCOMPARE(VirtualDesktopManager::self()->desktops()[1], client->desktops()[0]);
}
WAYLANDTEST_MAIN(VirtualDesktopTest)

View file

@ -132,8 +132,10 @@ void TestVirtualDesktops::count()
vds->setCount(s_countInitValue);
QSignalSpy spy(vds, SIGNAL(countChanged(uint,uint)));
QSignalSpy desktopsRemoved(vds, SIGNAL(desktopsRemoved(uint)));
QSignalSpy desktopsRemoved(vds, SIGNAL(desktopRemoved(KWin::VirtualDesktop *)));
auto vdToRemove = vds->desktops().last();
QFETCH(uint, request);
QFETCH(uint, result);
QFETCH(bool, signal);
@ -153,8 +155,7 @@ void TestVirtualDesktops::count()
if (!desktopsRemoved.isEmpty()) {
QList<QVariant> arguments = desktopsRemoved.takeFirst();
QCOMPARE(arguments.count(), 1);
QCOMPARE(arguments.at(0).type(), QVariant::UInt);
QCOMPARE(arguments.at(0).toUInt(), s_countInitValue);
QCOMPARE(arguments.at(0).value<KWin::VirtualDesktop*>(), vdToRemove);
}
}

View file

@ -19,6 +19,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************/
#include <kwineffects.h>
#include "../virtualdesktops.h"
#include <QVector2D>
#include <QGraphicsRotation>
@ -72,6 +73,7 @@ public:
virtual void setData(int role, const QVariant &data);
virtual void referencePreviousWindowPixmap() {}
virtual void unreferencePreviousWindowPixmap() {}
QList<int> desktops() const { return {};}
};
MockEffectWindow::MockEffectWindow(QObject *parent)

View file

@ -21,6 +21,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
// own
#include "dbusinterface.h"
#include "compositingadaptor.h"
#include "virtualdesktopmanageradaptor.h"
// kwin
#include "abstract_client.h"
@ -315,4 +316,180 @@ QStringList CompositorDBusInterface::supportedOpenGLPlatformInterfaces() const
return interfaces;
}
VirtualDesktopManagerDBusInterface::VirtualDesktopManagerDBusInterface(VirtualDesktopManager *parent)
: QObject(parent)
, m_manager(parent)
{
qDBusRegisterMetaType<KWin::DBusDesktopDataStruct>();
qDBusRegisterMetaType<KWin::DBusDesktopDataVector>();
new VirtualDesktopManagerAdaptor(this);
QDBusConnection::sessionBus().registerObject(QStringLiteral("/VirtualDesktopManager"),
QStringLiteral("org.kde.KWin.VirtualDesktopManager"),
this
);
connect(m_manager, &VirtualDesktopManager::currentChanged, this,
[this](uint previousDesktop, uint newDesktop) {
Q_UNUSED(previousDesktop);
Q_UNUSED(newDesktop);
emit currentChanged(m_manager->currentDesktop()->id());
}
);
connect(m_manager, &VirtualDesktopManager::countChanged, this,
[this](uint previousCount, uint newCount) {
Q_UNUSED(previousCount);
emit countChanged(newCount);
emit desktopsChanged(desktops());
}
);
connect(m_manager, &VirtualDesktopManager::navigationWrappingAroundChanged, this,
[this]() {
emit navigationWrappingAroundChanged(isNavigationWrappingAround());
}
);
connect(m_manager, &VirtualDesktopManager::rowsChanged, this, &VirtualDesktopManagerDBusInterface::rowsChanged);
for (auto *vd : m_manager->desktops()) {
connect(vd, &VirtualDesktop::x11DesktopNumberChanged, this,
[this, vd]() {
DBusDesktopDataStruct data{.position = vd->x11DesktopNumber() - 1, .id = vd->id(), .name = vd->name()};
emit desktopDataChanged(vd->id(), data);
emit desktopsChanged(desktops());
}
);
connect(vd, &VirtualDesktop::nameChanged, this,
[this, vd]() {
DBusDesktopDataStruct data{.position = vd->x11DesktopNumber() - 1, .id = vd->id(), .name = vd->name()};
emit desktopDataChanged(vd->id(), data);
emit desktopsChanged(desktops());
}
);
}
connect(m_manager, &VirtualDesktopManager::desktopCreated, this,
[this](VirtualDesktop *vd) {
connect(vd, &VirtualDesktop::x11DesktopNumberChanged, this,
[this, vd]() {
DBusDesktopDataStruct data{.position = vd->x11DesktopNumber() - 1, .id = vd->id(), .name = vd->name()};
emit desktopDataChanged(vd->id(), data);
emit desktopsChanged(desktops());
}
);
connect(vd, &VirtualDesktop::nameChanged, this,
[this, vd]() {
DBusDesktopDataStruct data{.position = vd->x11DesktopNumber() - 1, .id = vd->id(), .name = vd->name()};
emit desktopDataChanged(vd->id(), data);
emit desktopsChanged(desktops());
}
);
DBusDesktopDataStruct data{.position = vd->x11DesktopNumber() - 1, .id = vd->id(), .name = vd->name()};
emit desktopCreated(vd->id(), data);
emit desktopsChanged(desktops());
}
);
connect(m_manager, &VirtualDesktopManager::desktopRemoved, this,
[this](VirtualDesktop *vd) {
emit desktopRemoved(vd->id());
emit desktopsChanged(desktops());
}
);
}
uint VirtualDesktopManagerDBusInterface::count() const
{
return m_manager->count();
}
void VirtualDesktopManagerDBusInterface::setRows(uint rows)
{
if (static_cast<uint>(m_manager->grid().height()) == rows) {
return;
}
m_manager->setRows(rows);
m_manager->save();
}
uint VirtualDesktopManagerDBusInterface::rows() const
{
return m_manager->rows();
}
void VirtualDesktopManagerDBusInterface::setCurrent(const QString &id)
{
if (m_manager->currentDesktop()->id() == id) {
return;
}
auto *vd = m_manager->desktopForId(id.toUtf8());
if (vd) {
m_manager->setCurrent(vd);
}
}
QString VirtualDesktopManagerDBusInterface::current() const
{
return m_manager->currentDesktop()->id();
}
void VirtualDesktopManagerDBusInterface::setNavigationWrappingAround(bool wraps)
{
if (m_manager->isNavigationWrappingAround() == wraps) {
return;
}
m_manager->setNavigationWrappingAround(wraps);
}
bool VirtualDesktopManagerDBusInterface::isNavigationWrappingAround() const
{
return m_manager->isNavigationWrappingAround();
}
DBusDesktopDataVector VirtualDesktopManagerDBusInterface::desktops() const
{
const auto desks = m_manager->desktops();
DBusDesktopDataVector desktopVect;
desktopVect.reserve(m_manager->count());
std::transform(desks.constBegin(), desks.constEnd(),
std::back_inserter(desktopVect),
[] (const VirtualDesktop *vd) {
return DBusDesktopDataStruct{.position = vd->x11DesktopNumber() - 1, .id = vd->id(), .name = vd->name()};
}
);
return desktopVect;
}
void VirtualDesktopManagerDBusInterface::createDesktop(uint position, const QString &name)
{
m_manager->createVirtualDesktop(position + 1, name);
}
void VirtualDesktopManagerDBusInterface::setDesktopName(const QString &id, const QString &name)
{
VirtualDesktop *vd = m_manager->desktopForId(id.toUtf8());
if (!vd) {
return;
}
if (vd->name() == name) {
return;
}
vd->setName(name);
m_manager->save();
}
void VirtualDesktopManagerDBusInterface::removeDesktop(const QString &id)
{
m_manager->removeVirtualDesktop(id.toUtf8());
}
} // namespace

View file

@ -24,10 +24,13 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <QObject>
#include <QtDBus>
#include "virtualdesktopsdbustypes.h"
namespace KWin
{
class Compositor;
class VirtualDesktopManager;
/**
* @brief This class is a wrapper for the org.kde.KWin D-Bus interface.
@ -169,6 +172,76 @@ private:
Compositor *m_compositor;
};
//TODO: disable all of this in case of kiosk?
class VirtualDesktopManagerDBusInterface : public QObject
{
Q_OBJECT
Q_CLASSINFO("D-Bus Interface", "org.kde.KWin.VirtualDesktopManager")
/**
* 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 NOTIFY countChanged)
/**
* The number of rows the virtual desktops will be laid out in
**/
Q_PROPERTY(uint rows READ rows WRITE setRows NOTIFY rowsChanged)
/**
* The id of the virtual desktop which is currently in use.
**/
Q_PROPERTY(QString 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)
/**
* list of key/value pairs which every one of them is representing a desktop
*/
Q_PROPERTY(KWin::DBusDesktopDataVector desktops READ desktops NOTIFY desktopsChanged);
public:
VirtualDesktopManagerDBusInterface(VirtualDesktopManager *parent);
~VirtualDesktopManagerDBusInterface() = default;
uint count() const;
void setRows(uint rows);
uint rows() const;
void setCurrent(const QString &id);
QString current() const;
void setNavigationWrappingAround(bool wraps);
bool isNavigationWrappingAround() const;
KWin::DBusDesktopDataVector desktops() const;
Q_SIGNALS:
void countChanged(uint count);
void rowsChanged(uint rows);
void currentChanged(const QString &id);
void navigationWrappingAroundChanged(bool wraps);
void desktopsChanged(KWin::DBusDesktopDataVector);
void desktopDataChanged(const QString &id, KWin::DBusDesktopDataStruct);
void desktopCreated(const QString &id, KWin::DBusDesktopDataStruct);
void desktopRemoved(const QString &id);
public Q_SLOTS:
/**
* Create a desktop with a new name at a given position
* note: the position starts from 1
*/
void createDesktop(uint position, const QString &name);
void setDesktopName(const QString &id, const QString &name);
void removeDesktop(const QString &id);
private:
VirtualDesktopManager *m_manager;
};
} // namespace
#endif // KWIN_DBUS_INTERFACE_H

View file

@ -94,6 +94,7 @@ void Deleted::copyToDeleted(Toplevel* c)
assert(dynamic_cast< Deleted* >(c) == NULL);
Toplevel::copyToDeleted(c);
desk = c->desktop();
m_desktops = c->desktops();
activityList = c->activities();
contentsRect = QRect(c->clientPos(), c->clientSize());
m_contentPos = c->clientContentPos();
@ -177,6 +178,11 @@ QStringList Deleted::activities() const
return activityList;
}
QList<VirtualDesktop *> Deleted::desktops() const
{
return m_desktops;
}
QPoint Deleted::clientPos() const
{
return contentsRect.topLeft();

View file

@ -52,6 +52,7 @@ public:
void discard();
virtual int desktop() const;
virtual QStringList activities() const;
virtual QList<VirtualDesktop *> desktops() const;
virtual QPoint clientPos() const;
virtual QSize clientSize() const;
QPoint clientContentPos() const override {
@ -195,6 +196,7 @@ private:
QPoint m_contentPos;
QRect transparent_rect;
xcb_window_t m_frame;
QList <VirtualDesktop *> m_desktops;
bool no_border;
QRect decoration_left;

View file

@ -911,8 +911,9 @@ void EffectsHandlerImpl::moveWindow(EffectWindow* w, const QPoint& pos, bool sna
void EffectsHandlerImpl::windowToDesktop(EffectWindow* w, int desktop)
{
AbstractClient* cl = dynamic_cast< AbstractClient* >(static_cast<EffectWindowImpl*>(w)->window());
if (cl && !cl->isDesktop() && !cl->isDock())
if (cl && !cl->isDesktop() && !cl->isDock()) {
Workspace::self()->sendClientToDesktop(cl, desktop, true);
}
}
void EffectsHandlerImpl::windowToScreen(EffectWindow* w, int screen)

View file

@ -42,6 +42,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <QQmlEngine>
#include <QQuickItem>
#include <KWayland/Server/surface_interface.h>
namespace KWin
{
@ -52,6 +54,7 @@ DesktopGridEffect::DesktopGridEffect()
, timeline()
, keyboardGrab(false)
, wasWindowMove(false)
, wasWindowCopy(false)
, wasDesktopMove(false)
, isValidMove(false)
, windowMove(NULL)
@ -297,7 +300,9 @@ void DesktopGridEffect::prePaintWindow(EffectWindow* w, WindowPrePaintData& data
void DesktopGridEffect::paintWindow(EffectWindow* w, int mask, QRegion region, WindowPaintData& data)
{
if (timeline.currentValue() != 0 || (isUsingPresentWindows() && isMotionManagerMovingWindows())) {
if (isUsingPresentWindows() && w == windowMove && wasWindowMove) {
if (isUsingPresentWindows() && w == windowMove && wasWindowMove &&
((!wasWindowCopy && sourceDesktop == paintingDesktop) ||
(sourceDesktop != highlightedDesktop && highlightedDesktop == paintingDesktop))) {
return; // will be painted on top of all other windows
}
foreach (DesktopButtonsView *view, m_desktopButtonsViews) {
@ -487,7 +492,6 @@ void DesktopGridEffect::windowInputMouseEvent(QEvent* e)
if (!wasWindowMove) { // Activate on move
if (isUsingPresentWindows()) {
foreach (const int i, desktopList(windowMove)) {
const int sourceDesktop = windowMove->isOnAllDesktops() ? d : windowMove->desktop();
WindowMotionManager& manager = m_managers[(i)*(effects->numScreens()) + windowMove->screen()];
if ((i + 1) == sourceDesktop) {
const QRectF transformedGeo = manager.transformedGeometry(windowMove);
@ -513,9 +517,18 @@ void DesktopGridEffect::windowInputMouseEvent(QEvent* e)
effects->moveWindow(windowMove, unscalePos(me->pos(), NULL) + windowMoveDiff, true, 1.0 / scale[screen]);
}
if (wasWindowMove) {
effects->defineCursor(Qt::ClosedHandCursor);
if (!effects->waylandDisplay() || (me->modifiers() & Qt::ControlModifier)) {
wasWindowCopy = true;
effects->defineCursor(Qt::DragCopyCursor);
} else {
wasWindowCopy = false;
effects->defineCursor(Qt::ClosedHandCursor);
}
if (d != highlightedDesktop) {
effects->windowToDesktop(windowMove, d); // Not true all desktop move
if (highlightedDesktop != sourceDesktop || !wasWindowCopy) {
effects->removeWindowFromDesktop(windowMove, highlightedDesktop);
}
const int screen = effects->screenNumber(me->pos());
if (screen != windowMove->screen())
effects->windowToScreen(windowMove, screen);
@ -549,6 +562,8 @@ void DesktopGridEffect::windowInputMouseEvent(QEvent* e)
continue;
foreach (EffectWindow *w, stack[i]) {
effects->windowToDesktop(w, desks[i+1]);
effects->removeWindowFromDesktop(w, desks[i]);
if (isUsingPresentWindows()) {
m_managers[(desks[i]-1)*(effects->numScreens()) + w->screen()].unmanage(w);
m_managers[(desks[i+1]-1)*(effects->numScreens()) + w->screen()].manage(w);
@ -572,12 +587,15 @@ void DesktopGridEffect::windowInputMouseEvent(QEvent* e)
if (me->buttons() == Qt::LeftButton) {
isValidMove = true;
dragStartPos = me->pos();
bool isDesktop = (me->modifiers() & Qt::ControlModifier);
sourceDesktop = posToDesktop(me->pos());
bool isDesktop = (me->modifiers() & Qt::ShiftModifier);
EffectWindow* w = isDesktop ? NULL : windowAt(me->pos());
if (w != NULL)
isDesktop = w->isDesktop();
if (isDesktop)
m_originalMovingDesktop = posToDesktop(me->pos());
else
m_originalMovingDesktop = 0;
if (w != NULL && !w->isDesktop() && (w->isMovable() || w->isMovableAcrossScreens() || isUsingPresentWindows())) {
// Prepare it for moving
windowMoveDiff = w->pos() - unscalePos(me->pos(), NULL);
@ -590,12 +608,10 @@ void DesktopGridEffect::windowInputMouseEvent(QEvent* e)
w = nullptr;
}
if (w != NULL) {
int desktop = 0;
const int desktop = posToDesktop(me->pos());
if (w->isOnAllDesktops()) {
desktop = posToDesktop(me->pos());
effects->windowToDesktop(w, desktop);
} else {
desktop = w->desktop();
effects->windowToDesktop(w, NET::OnAllDesktops);
}
const bool isOnAllDesktops = w->isOnAllDesktops();
@ -630,7 +646,7 @@ void DesktopGridEffect::windowInputMouseEvent(QEvent* e)
}
if (windowMove) {
if (wasWindowMove && isUsingPresentWindows()) {
const int targetDesktop = windowMove->isOnAllDesktops() ? posToDesktop(cursorPos()) : windowMove->desktop();
const int targetDesktop = posToDesktop(cursorPos());
foreach (const int i, desktopList(windowMove)) {
WindowMotionManager& manager = m_managers[(i)*(effects->numScreens()) + windowMove->screen()];
manager.manage(windowMove);
@ -648,6 +664,7 @@ void DesktopGridEffect::windowInputMouseEvent(QEvent* e)
windowMove = NULL;
}
wasWindowMove = false;
wasWindowCopy = false;
wasDesktopMove = false;
}
}
@ -1360,7 +1377,7 @@ void DesktopGridEffect::desktopsRemoved(int old)
// and repaint
effects->addRepaintFull();
}
//TODO: kill this function? or at least keep a consistent numeration with desktops starting from 1
QVector<int> DesktopGridEffect::desktopList(const EffectWindow *w) const
{
if (w->isOnAllDesktops()) {
@ -1373,16 +1390,13 @@ QVector<int> DesktopGridEffect::desktopList(const EffectWindow *w) const
return allDesktops;
}
if (w->desktop() > effects->numberOfDesktops() || w->desktop() < 1) { // sic! desktops are [1,n]
static QVector<int> emptyVector;
emptyVector.resize(0);
return emptyVector;
QVector<int> desks;
desks.resize(w->desktops().count());
int i = 0;
for (const int desk : w->desktops()) {
desks[i++] = desk-1;
}
static QVector<int> singleDesktop;
singleDesktop.resize(1);
singleDesktop[0] = w->desktop() - 1;
return singleDesktop;
return desks;
}
bool DesktopGridEffect::isActive() const

View file

@ -151,9 +151,10 @@ private:
QTimeLine timeline;
int paintingDesktop;
int highlightedDesktop;
int sourceDesktop;
int m_originalMovingDesktop;
bool keyboardGrab;
bool wasWindowMove, wasDesktopMove, isValidMove;
bool wasWindowMove, wasWindowCopy, wasDesktopMove, isValidMove;
EffectWindow* windowMove;
QPoint windowMoveDiff;
QPoint dragStartPos;

View file

@ -762,6 +762,13 @@ bool EffectsHandler::isOpenGLCompositing() const
return compositing_type & OpenGLCompositing;
}
void EffectsHandler::removeWindowFromDesktop(KWin::EffectWindow* w, int desktop)
{
if (w->parent() && !w->isDesktop() && !w->isDock()) {
QMetaObject::invokeMethod(w->parent(), "unSetDesktop", Q_ARG(int, desktop));
}
}
EffectsHandler* effects = nullptr;
@ -826,7 +833,9 @@ WINDOW_HELPER(int, screen, "screen")
WINDOW_HELPER(QRect, geometry, "geometry")
WINDOW_HELPER(QRect, expandedGeometry, "visibleRect")
WINDOW_HELPER(QRect, rect, "rect")
#ifndef KWIN_NO_DEPRECATED
WINDOW_HELPER(int, desktop, "desktop")
#endif
WINDOW_HELPER(bool, isDesktop, "desktopWindow")
WINDOW_HELPER(bool, isDock, "dock")
WINDOW_HELPER(bool, isToolbar, "toolbar")
@ -849,6 +858,11 @@ WINDOW_HELPER(QStringList, activities, "activities")
WINDOW_HELPER(bool, skipsCloseAnimation, "skipsCloseAnimation")
WINDOW_HELPER(KWayland::Server::SurfaceInterface *, surface, "surface")
QList<int> EffectWindow::desktops() const
{
return parent()->property("x11DesktopIds").value<QList<int> >();
}
QString EffectWindow::windowClass() const
{
return parent()->property("resourceName").toString() + QLatin1Char(' ') + parent()->property("resourceClass").toString();
@ -974,12 +988,13 @@ bool EffectWindow::isOnCurrentDesktop() const
bool EffectWindow::isOnDesktop(int d) const
{
return desktop() == d || isOnAllDesktops();
const QList<int> ds = desktops();
return ds.isEmpty() || ds.contains(d);
}
bool EffectWindow::isOnAllDesktops() const
{
return desktop() == NET::OnAllDesktops;
return desktops().isEmpty();
}
bool EffectWindow::hasDecoration() const

View file

@ -944,6 +944,10 @@ public:
virtual KWin::EffectWindow* activeWindow() const = 0 ;
Q_SCRIPTABLE virtual void moveWindow(KWin::EffectWindow* w, const QPoint& pos, bool snap = false, double snapAdjust = 1.0) = 0;
Q_SCRIPTABLE virtual void windowToDesktop(KWin::EffectWindow* w, int desktop) = 0;
/**
* Removes a window from a desktop on wayland, no-op on X11
*/
Q_SCRIPTABLE void removeWindowFromDesktop(KWin::EffectWindow* w, int desktop);
Q_SCRIPTABLE virtual void windowToScreen(KWin::EffectWindow* w, int screen) = 0;
virtual void setShowingDesktop(bool showing) = 0;
@ -2071,7 +2075,23 @@ public:
Q_SCRIPTABLE bool isOnDesktop(int d) const;
bool isOnCurrentDesktop() const;
bool isOnAllDesktops() const;
int desktop() const; // prefer isOnXXX()
/**
* The desktop this window is in. This mkaes sense only on X11
* where desktops are mutually exclusive, on Wayland it's the last
* desktop the window has been added to.
* use desktops() instead.
* @see desktops()
* @deprecated
*/
#ifndef KWIN_NO_DEPRECATED
int KWIN_DEPRECATED desktop() const; // prefer isOnXXX()
#endif
/**
* All the desktops by number that the window is in. On X11 this list will always have
* a length of 1, on Wayland can be any subset.
* If the list is empty it means the window is on all desktops
*/
QList<int> desktops() const;
int x() const;
int y() const;

View file

@ -0,0 +1,50 @@
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node>
<interface name="org.kde.KWin.VirtualDesktopManager">
<property name="count" type="u" access="read"/>
<property name="current" type="s" access="readwrite"/>
<property name="rows" type="u" access="readwrite"/>
<property name="navigationWrappingAround" type="b" access="readwrite"/>
<property name="desktops" type="a(iss)" access="read">
<annotation name="org.qtproject.QtDBus.QtTypeName" value="KWin::DBusDesktopDataVector"/>
</property>
<signal name="countChanged">
<arg name="count" type="u" direction="out"/>
</signal>
<signal name="rowsChanged">
<arg name="rows" type="u" direction="out"/>
</signal>
<signal name="currentChanged">
<arg name="id" type="s" direction="out"/>
</signal>
<signal name="navigationWrappingAroundChanged">
<arg name="navigationWrappingAround" type="b" direction="out"/>
</signal>
<signal name="desktopDataChanged">
<arg name="id" type="s" direction="out"/>
<annotation name="org.qtproject.QtDBus.QtTypeName.Out1" value="KWin::DBusDesktopDataStruct"/>
<arg name="desktopData" type="(iss)" direction="out"/>
</signal>
<signal name="desktopCreated">
<arg name="id" type="s" direction="out"/>
<annotation name="org.qtproject.QtDBus.QtTypeName.Out1" value="KWin::DBusDesktopDataStruct"/>
<arg name="desktopData" type="(iss)" direction="out"/>
</signal>
<signal name="desktopRemoved">
<arg name="id" type="s" direction="out"/>
</signal>
<method name="createDesktop">
<arg name="position" type="u" direction="in"/>
<arg name="name" type="s" direction="in"/>
</method>
<method name="setDesktopName">
<arg name="id" type="s" direction="in"/>
<arg name="name" type="s" direction="in"/>
</method>
<method name="removeDesktop">
<arg name="id" type="s" direction="in"/>
</method>
</interface>
</node>

View file

@ -290,6 +290,7 @@ public:
* isOnDesktop() instead.
*/
virtual int desktop() const = 0;
virtual QList<VirtualDesktop *> desktops() const = 0;
virtual QStringList activities() const = 0;
bool isOnDesktop(int d) const;
bool isOnActivity(const QString &activity) const;
@ -790,7 +791,12 @@ const EffectWindowImpl* Toplevel::effectWindow() const
inline bool Toplevel::isOnAllDesktops() const
{
return desktop() == NET::OnAllDesktops;
return kwinApp()->operationMode() == Application::OperationModeWaylandOnly ||
kwinApp()->operationMode() == Application::OperationModeXwayland
//Wayland
? desktops().isEmpty()
//X11
: desktop() == NET::OnAllDesktops;
}
inline bool Toplevel::isOnAllActivities() const
@ -800,7 +806,11 @@ inline bool Toplevel::isOnAllActivities() const
inline bool Toplevel::isOnDesktop(int d) const
{
return desktop() == d || /*desk == 0 ||*/ isOnAllDesktops();
return (kwinApp()->operationMode() == Application::OperationModeWaylandOnly ||
kwinApp()->operationMode() == Application::OperationModeXwayland
? desktops().contains(VirtualDesktopManager::self()->desktopForX11Id(d))
: desktop() == d
) || isOnAllDesktops();
}
inline bool Toplevel::isOnActivity(const QString &activity) const

View file

@ -123,6 +123,11 @@ QStringList Unmanaged::activities() const
return QStringList();
}
QList<VirtualDesktop *> Unmanaged::desktops() const
{
return QList<VirtualDesktop *>();
}
QPoint Unmanaged::clientPos() const
{
return QPoint(0, 0); // unmanaged windows don't have decorations

View file

@ -39,6 +39,7 @@ public:
static void deleteUnmanaged(Unmanaged* c);
virtual int desktop() const;
virtual QStringList activities() const;
virtual QList<VirtualDesktop *> desktops() const override;
virtual QPoint clientPos() const;
virtual QSize clientSize() const;
virtual QRect transparentRect() const;

View file

@ -402,6 +402,7 @@ void UserActionsMenu::discard()
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 @@ void UserActionsMenu::menuAboutToShow()
if (VirtualDesktopManager::self()->count() == 1) {
delete m_desktopMenu;
m_desktopMenu = 0;
delete m_multipleDesktopsMenu;
m_multipleDesktopsMenu = nullptr;
} else {
initDesktopPopup();
}
@ -604,17 +607,34 @@ void UserActionsMenu::initTabbingPopups()
void UserActionsMenu::initDesktopPopup()
{
if (m_desktopMenu)
return;
if (kwinApp()->operationMode() == Application::OperationModeWaylandOnly ||
kwinApp()->operationMode() == Application::OperationModeXwayland) {
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 +687,7 @@ void UserActionsMenu::desktopPopupAboutToShow()
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 +711,58 @@ void UserActionsMenu::desktopPopupAboutToShow()
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);
QActionGroup *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 +889,35 @@ void UserActionsMenu::slotSendToDesktop(QAction *action)
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();

View file

@ -144,6 +144,11 @@ private Q_SLOTS:
* the Client.
**/
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.
@ -160,6 +165,12 @@ private Q_SLOTS:
* @param action Invoked Action containing the Desktop as data element
**/
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
*
@ -217,6 +228,10 @@ private:
* The move to desktop sub menu.
**/
QMenu* m_desktopMenu;
/**
* The move to desktop sub menu, with the Wayland protocol.
**/
QMenu* m_multipleDesktopsMenu;
/**
* The move to screen sub menu.
**/

View file

@ -25,11 +25,14 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <KGlobalAccel>
#include <KLocalizedString>
#include <NETWM>
#include <KWayland/Server/plasmavirtualdesktop_interface.h>
// Qt
#include <QAction>
#include <QUuid>
#include <algorithm>
#include <QDebug>
namespace KWin {
extern int screen_number;
@ -44,6 +47,81 @@ 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());
@ -52,8 +130,16 @@ void VirtualDesktop::setId(const QByteArray &id)
void VirtualDesktop::setX11DesktopNumber(uint number)
{
Q_ASSERT(m_x11DesktopNumber == 0);
//x11DesktopNumber can be changed now
if (static_cast<uint>(m_x11DesktopNumber) == number) {
return;
}
m_x11DesktopNumber = number;
if (m_x11DesktopNumber != 0) {
emit x11DesktopNumberChanged();
}
}
void VirtualDesktop::setName(const QString &name)
@ -68,7 +154,7 @@ void VirtualDesktop::setName(const QString &name)
VirtualDesktopGrid::VirtualDesktopGrid()
: m_size(1, 2) // Default to tow rows
, m_grid(QVector<QVector<VirtualDesktop*>>{QVector<VirtualDesktop*>{}, QVector<VirtualDesktop*>{}})
{
{
}
VirtualDesktopGrid::~VirtualDesktopGrid() = default;
@ -325,6 +411,99 @@ VirtualDesktop *VirtualDesktopManager::desktopForX11Id(uint id) const
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<uint>(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;
@ -364,33 +543,79 @@ void VirtualDesktopManager::setCount(uint count)
// nothing to change
return;
}
QList<VirtualDesktop *> 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);
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) {
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());
}
void VirtualDesktopManager::handleDesktopsRemoved(uint previousCount, uint previousCurrent)
uint VirtualDesktopManager::rows() const
{
if (!m_current) {
m_current = m_desktops.last();
emit currentChanged(previousCurrent, m_current->x11DesktopNumber());
return grid().height();
}
void VirtualDesktopManager::setRows(uint rows)
{
if (static_cast<uint>(grid().height()) == rows || rows == 0 || rows > count()) {
return;
}
emit desktopsRemoved(previousCount);
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()
@ -437,6 +662,8 @@ void VirtualDesktopManager::load()
if (!m_config) {
return;
}
//FIXME: how to avoid this?
m_isLoading = true;
QString groupname;
if (screen_number == 0) {
groupname = QStringLiteral("Desktops");
@ -446,14 +673,31 @@ void VirtualDesktopManager::load()
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));
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());
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<int>("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 +708,9 @@ void VirtualDesktopManager::load()
m_rootInfo->setDesktopLayout(NET::OrientationHorizontal, columns, rows, NET::DesktopLayoutCornerTopLeft);
m_rootInfo->activate();
}
s_loadingDesktopSettings = false;
m_isLoading = false;
}
void VirtualDesktopManager::save()
@ -502,6 +748,7 @@ void VirtualDesktopManager::save()
group.deleteEntry(QStringLiteral("Name_%1").arg(i));
}
}
group.writeEntry(QStringLiteral("Id_%1").arg(i), m_desktops[i-1]->id());
}
// Save to disk
@ -536,6 +783,7 @@ void VirtualDesktopManager::setNETDesktopLayout(Qt::Orientation orientation, uin
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()

View file

@ -27,6 +27,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <QPoint>
#include <QPointer>
#include <QSize>
// KDE includes
#include <KConfig>
#include <KSharedConfig>
@ -35,13 +36,21 @@ 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 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);
@ -64,6 +73,7 @@ public:
Q_SIGNALS:
void nameChanged();
void x11DesktopNumberChanged();
/**
* Emitted just before the desktop gets destroyed.
**/
@ -148,9 +158,13 @@ class KWIN_EXPORT VirtualDesktopManager : public QObject
public:
virtual ~VirtualDesktopManager();
/**
* @internal
* @internal, for X11 case
**/
void setRootInfo(NETRootInfo *info);
/**
* @internal, for Wayland case
**/
void setVirtualDesktopManagement(KWayland::Server::PlasmaVirtualDesktopManagementInterface *management);
/**
* @internal
**/
@ -161,6 +175,12 @@ public:
* @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
@ -263,6 +283,30 @@ public:
**/
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
**/
@ -283,13 +327,14 @@ public Q_SLOTS:
* @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 desktopsRemoved
* 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 desktopsRemoved
* @see desktopCreated
* @see desktopRemoved
*/
void setCount(uint count);
/**
@ -308,6 +353,10 @@ public Q_SLOTS:
* @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.
*/
@ -334,18 +383,27 @@ Q_SIGNALS:
* @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);
* 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
@ -396,21 +454,6 @@ private Q_SLOTS:
void slotDown();
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.
*/
@ -450,7 +493,9 @@ private:
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)
};

View file

@ -0,0 +1,70 @@
/********************************************************************
KWin - the KDE window manager
This file is part of the KDE project.
Copyright (C) 2018 Marco Martin <mart@kde.org>
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 <http://www.gnu.org/licenses/>.
*********************************************************************/
// own
#include "virtualdesktopsdbustypes.h"
// Marshall the DBusDesktopDataStruct data into a D-BUS argument
const QDBusArgument &operator<<(QDBusArgument &argument, const KWin::DBusDesktopDataStruct &desk)
{
argument.beginStructure();
argument << desk.position;
argument << desk.id;
argument << desk.name;
argument.endStructure();
return argument;
}
// Retrieve
const QDBusArgument &operator>>(const QDBusArgument &argument, KWin::DBusDesktopDataStruct &desk)
{
argument.beginStructure();
argument >> desk.position;
argument >> desk.id;
argument >> desk.name;
argument.endStructure();
return argument;
}
const QDBusArgument &operator<<(QDBusArgument &argument, const KWin::DBusDesktopDataVector &deskVector)
{
argument.beginArray(qMetaTypeId<KWin::DBusDesktopDataStruct>());
for (int i = 0; i < deskVector.size(); ++i) {
argument << deskVector[i];
}
argument.endArray();
return argument;
}
const QDBusArgument &operator>>(const QDBusArgument &argument, KWin::DBusDesktopDataVector &deskVector)
{
argument.beginArray();
deskVector.clear();
while (!argument.atEnd()) {
KWin::DBusDesktopDataStruct element;
argument >> element;
deskVector.append(element);
}
argument.endArray();
return argument;
}

View file

@ -0,0 +1,47 @@
/********************************************************************
KWin - the KDE window manager
This file is part of the KDE project.
Copyright (C) 2018 Marco Martin <mart@kde.org>
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 <http://www.gnu.org/licenses/>.
*********************************************************************/
#ifndef KWIN_VIRTUALDESKTOPS_DBUS_TYPES_H
#define KWIN_VIRTUALDESKTOPS_DBUS_TYPES_H
#include <QtDBus>
namespace KWin
{
struct DBusDesktopDataStruct {
uint position;
QString id;
QString name;
};
typedef QVector<DBusDesktopDataStruct> DBusDesktopDataVector;
}
const QDBusArgument &operator<<(QDBusArgument &argument, const KWin::DBusDesktopDataStruct &desk);
const QDBusArgument &operator>>(const QDBusArgument &argument, KWin::DBusDesktopDataStruct &desk);
Q_DECLARE_METATYPE(KWin::DBusDesktopDataStruct)
const QDBusArgument &operator<<(QDBusArgument &argument, const KWin::DBusDesktopDataVector &deskVector);
const QDBusArgument &operator>>(const QDBusArgument &argument, KWin::DBusDesktopDataVector &deskVector);
Q_DECLARE_METATYPE(KWin::DBusDesktopDataVector)
#endif // KWIN_VIRTUALDESKTOPS_DBUS_TYPES_H

View file

@ -43,6 +43,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <KWayland/Server/idleinhibit_interface.h>
#include <KWayland/Server/output_interface.h>
#include <KWayland/Server/plasmashell_interface.h>
#include <KWayland/Server/plasmavirtualdesktop_interface.h>
#include <KWayland/Server/plasmawindowmanagement_interface.h>
#include <KWayland/Server/pointerconstraints_interface.h>
#include <KWayland/Server/pointergestures_interface.h>
@ -333,6 +334,12 @@ bool WaylandServer::init(const QByteArray &socketName, InitalizationFlags flags)
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();
@ -390,6 +397,8 @@ void WaylandServer::shellClientShown(Toplevel *t)
void WaylandServer::initWorkspace()
{
VirtualDesktopManager::self()->setVirtualDesktopManagement(m_virtualDesktopManagement);
if (m_windowManagement) {
connect(workspace(), &Workspace::showingDesktopChanged, this,
[this] (bool set) {

View file

@ -54,6 +54,7 @@ class SurfaceInterface;
class OutputInterface;
class PlasmaShellInterface;
class PlasmaShellSurfaceInterface;
class PlasmaVirtualDesktopManagementInterface;
class PlasmaWindowManagementInterface;
class QtSurfaceExtensionInterface;
class OutputManagementInterface;
@ -99,6 +100,9 @@ public:
KWayland::Server::ShellInterface *shell() {
return m_shell;
}
KWayland::Server::PlasmaVirtualDesktopManagementInterface *virtualDesktopManagement() {
return m_virtualDesktopManagement;
}
KWayland::Server::PlasmaWindowManagementInterface *windowManagement() {
return m_windowManagement;
}
@ -227,6 +231,7 @@ private:
KWayland::Server::XdgShellInterface *m_xdgShell = 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;

View file

@ -165,6 +165,8 @@ Workspace::Workspace(const QString &sessionKey)
// and prior to TabBox, due to TabBox connecting to signals
// actual initialization happens in init()
VirtualDesktopManager::create(this);
//dbus interface
new VirtualDesktopManagerDBusInterface(VirtualDesktopManager::self());
#ifdef KWIN_BUILD_TABBOX
// need to create the tabbox before compositing scene is setup
@ -219,7 +221,30 @@ void Workspace::init()
// 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 (kwinApp()->operationMode() == Application::OperationModeWaylandOnly ||
kwinApp()->operationMode() == Application::OperationModeXwayland) {
for (auto it = m_allClients.constBegin(); it != m_allClients.constEnd(); ++it) {
const bool needsMove = (*it)->desktops().count() == 1;
(*it)->removeDesktop(desktop);
if (needsMove) {
const VirtualDesktop *otherDesktop = VirtualDesktopManager::self()->desktops().first();
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<int>(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());
@ -232,6 +257,9 @@ void Workspace::init()
// positioning object needs to be created before the virtual desktops are loaded.
vds->load();
vds->updateLayout();
//makes sure any autogenerated id is saved, necessary as in case of xwayland, load will be called 2 times
// load is needed to be called again when starting xwayalnd to sync to RootInfo, see BUG 385260
vds->save();
if (!VirtualDesktopManager::self()->setCurrent(m_initialDesktop))
VirtualDesktopManager::self()->setCurrent(1);
@ -1074,14 +1102,6 @@ void Workspace::updateCurrentActivity(const QString &new_activity)
#endif
}
void Workspace::moveClientsFromRemovedDesktops()
{
for (auto it = m_allClients.constBegin(); it != m_allClients.constEnd(); ++it) {
if (!(*it)->isOnAllDesktops() && (*it)->desktop() > static_cast<int>(VirtualDesktopManager::self()->count()))
sendClientToDesktop(*it, VirtualDesktopManager::self()->count(), true);
}
}
void Workspace::slotDesktopCountChanged(uint previousCount, uint newCount)
{
Q_UNUSED(previousCount)

View file

@ -457,7 +457,6 @@ private Q_SLOTS:
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);