diff --git a/autotests/integration/xdgshellclient_rules_test.cpp b/autotests/integration/xdgshellclient_rules_test.cpp index 9a3992a807..6f57ee8fa5 100644 --- a/autotests/integration/xdgshellclient_rules_test.cpp +++ b/autotests/integration/xdgshellclient_rules_test.cpp @@ -1783,12 +1783,18 @@ void TestXdgShellClientRules::testMaximizeForceTemporarily() void TestXdgShellClientRules::testDesktopDontAffect() { + // We need at least two virtual desktop for this test. + VirtualDesktopManager::self()->setCount(2); + QCOMPARE(VirtualDesktopManager::self()->count(), 2u); + VirtualDesktopManager::self()->setCurrent(1); + QCOMPARE(VirtualDesktopManager::self()->current(), 1); + // Initialize RuleBook with the test rule. auto config = KSharedConfig::openConfig(QString(), KConfig::SimpleConfig); config->group("General").writeEntry("count", 1); KConfigGroup group = config->group("1"); - group.writeEntry("desktop", 2); - group.writeEntry("desktoprule", int(Rules::DontAffect)); + group.writeEntry("desktops", {VirtualDesktopManager::self()->desktopForX11Id(2)->id()}); + group.writeEntry("desktopsrule", int(Rules::DontAffect)); group.writeEntry("wmclass", "org.kde.foo"); group.writeEntry("wmclasscomplete", false); group.writeEntry("wmclassmatch", int(Rules::ExactMatch)); @@ -1796,12 +1802,6 @@ void TestXdgShellClientRules::testDesktopDontAffect() RuleBook::self()->setConfig(config); workspace()->slotReconfigure(); - // We need at least two virtual desktop for this test. - VirtualDesktopManager::self()->setCount(2); - QCOMPARE(VirtualDesktopManager::self()->count(), 2u); - VirtualDesktopManager::self()->setCurrent(1); - QCOMPARE(VirtualDesktopManager::self()->current(), 1); - // Create the test client. AbstractClient *client; Surface *surface; @@ -1821,12 +1821,18 @@ void TestXdgShellClientRules::testDesktopDontAffect() void TestXdgShellClientRules::testDesktopApply() { + // We need at least two virtual desktop for this test. + VirtualDesktopManager::self()->setCount(2); + QCOMPARE(VirtualDesktopManager::self()->count(), 2u); + VirtualDesktopManager::self()->setCurrent(1); + QCOMPARE(VirtualDesktopManager::self()->current(), 1); + // Initialize RuleBook with the test rule. auto config = KSharedConfig::openConfig(QString(), KConfig::SimpleConfig); config->group("General").writeEntry("count", 1); KConfigGroup group = config->group("1"); - group.writeEntry("desktop", 2); - group.writeEntry("desktoprule", int(Rules::Apply)); + group.writeEntry("desktops", {VirtualDesktopManager::self()->desktopForX11Id(2)->id()}); + group.writeEntry("desktopsrule", int(Rules::Apply)); group.writeEntry("wmclass", "org.kde.foo"); group.writeEntry("wmclasscomplete", false); group.writeEntry("wmclassmatch", int(Rules::ExactMatch)); @@ -1834,12 +1840,6 @@ void TestXdgShellClientRules::testDesktopApply() RuleBook::self()->setConfig(config); workspace()->slotReconfigure(); - // We need at least two virtual desktop for this test. - VirtualDesktopManager::self()->setCount(2); - QCOMPARE(VirtualDesktopManager::self()->count(), 2u); - VirtualDesktopManager::self()->setCurrent(1); - QCOMPARE(VirtualDesktopManager::self()->current(), 1); - // Create the test client. AbstractClient *client; Surface *surface; @@ -1875,12 +1875,18 @@ void TestXdgShellClientRules::testDesktopApply() void TestXdgShellClientRules::testDesktopRemember() { + // We need at least two virtual desktop for this test. + VirtualDesktopManager::self()->setCount(2); + QCOMPARE(VirtualDesktopManager::self()->count(), 2u); + VirtualDesktopManager::self()->setCurrent(1); + QCOMPARE(VirtualDesktopManager::self()->current(), 1); + // Initialize RuleBook with the test rule. auto config = KSharedConfig::openConfig(QString(), KConfig::SimpleConfig); config->group("General").writeEntry("count", 1); KConfigGroup group = config->group("1"); - group.writeEntry("desktop", 2); - group.writeEntry("desktoprule", int(Rules::Remember)); + group.writeEntry("desktops", {VirtualDesktopManager::self()->desktopForX11Id(2)->id()}); + group.writeEntry("desktopsrule", int(Rules::Remember)); group.writeEntry("wmclass", "org.kde.foo"); group.writeEntry("wmclasscomplete", false); group.writeEntry("wmclassmatch", int(Rules::ExactMatch)); @@ -1888,12 +1894,6 @@ void TestXdgShellClientRules::testDesktopRemember() RuleBook::self()->setConfig(config); workspace()->slotReconfigure(); - // We need at least two virtual desktop for this test. - VirtualDesktopManager::self()->setCount(2); - QCOMPARE(VirtualDesktopManager::self()->count(), 2u); - VirtualDesktopManager::self()->setCurrent(1); - QCOMPARE(VirtualDesktopManager::self()->current(), 1); - // Create the test client. AbstractClient *client; Surface *surface; @@ -1925,12 +1925,18 @@ void TestXdgShellClientRules::testDesktopRemember() void TestXdgShellClientRules::testDesktopForce() { + // We need at least two virtual desktop for this test. + VirtualDesktopManager::self()->setCount(2); + QCOMPARE(VirtualDesktopManager::self()->count(), 2u); + VirtualDesktopManager::self()->setCurrent(1); + QCOMPARE(VirtualDesktopManager::self()->current(), 1); + // Initialize RuleBook with the test rule. auto config = KSharedConfig::openConfig(QString(), KConfig::SimpleConfig); config->group("General").writeEntry("count", 1); KConfigGroup group = config->group("1"); - group.writeEntry("desktop", 2); - group.writeEntry("desktoprule", int(Rules::Force)); + group.writeEntry("desktops", {VirtualDesktopManager::self()->desktopForX11Id(2)->id()}); + group.writeEntry("desktopsrule", int(Rules::Force)); group.writeEntry("wmclass", "org.kde.foo"); group.writeEntry("wmclasscomplete", false); group.writeEntry("wmclassmatch", int(Rules::ExactMatch)); @@ -1938,11 +1944,6 @@ void TestXdgShellClientRules::testDesktopForce() RuleBook::self()->setConfig(config); workspace()->slotReconfigure(); - // We need at least two virtual desktop for this test. - VirtualDesktopManager::self()->setCount(2); - QCOMPARE(VirtualDesktopManager::self()->count(), 2u); - VirtualDesktopManager::self()->setCurrent(1); - QCOMPARE(VirtualDesktopManager::self()->current(), 1); // Create the test client. AbstractClient *client; @@ -1998,8 +1999,8 @@ void TestXdgShellClientRules::testDesktopApplyNow() auto config = KSharedConfig::openConfig(QString(), KConfig::SimpleConfig); config->group("General").writeEntry("count", 1); KConfigGroup group = config->group("1"); - group.writeEntry("desktop", 2); - group.writeEntry("desktoprule", int(Rules::ApplyNow)); + group.writeEntry("desktops", {VirtualDesktopManager::self()->desktopForX11Id(2)->id()}); + group.writeEntry("desktopsrule", int(Rules::ApplyNow)); group.writeEntry("wmclass", "org.kde.foo"); group.writeEntry("wmclasscomplete", false); group.writeEntry("wmclassmatch", int(Rules::ExactMatch)); @@ -2029,12 +2030,18 @@ void TestXdgShellClientRules::testDesktopApplyNow() void TestXdgShellClientRules::testDesktopForceTemporarily() { + // We need at least two virtual desktop for this test. + VirtualDesktopManager::self()->setCount(2); + QCOMPARE(VirtualDesktopManager::self()->count(), 2u); + VirtualDesktopManager::self()->setCurrent(1); + QCOMPARE(VirtualDesktopManager::self()->current(), 1); + // Initialize RuleBook with the test rule. auto config = KSharedConfig::openConfig(QString(), KConfig::SimpleConfig); config->group("General").writeEntry("count", 1); KConfigGroup group = config->group("1"); - group.writeEntry("desktop", 2); - group.writeEntry("desktoprule", int(Rules::ForceTemporarily)); + group.writeEntry("desktops", {VirtualDesktopManager::self()->desktopForX11Id(2)->id()}); + group.writeEntry("desktopsrule", int(Rules::ForceTemporarily)); group.writeEntry("wmclass", "org.kde.foo"); group.writeEntry("wmclasscomplete", false); group.writeEntry("wmclassmatch", int(Rules::ExactMatch)); @@ -2042,12 +2049,6 @@ void TestXdgShellClientRules::testDesktopForceTemporarily() RuleBook::self()->setConfig(config); workspace()->slotReconfigure(); - // We need at least two virtual desktop for this test. - VirtualDesktopManager::self()->setCount(2); - QCOMPARE(VirtualDesktopManager::self()->count(), 2u); - VirtualDesktopManager::self()->setCurrent(1); - QCOMPARE(VirtualDesktopManager::self()->current(), 1); - // Create the test client. AbstractClient *client; Surface *surface; diff --git a/kconf_update/CMakeLists.txt b/kconf_update/CMakeLists.txt index 6cc9363afd..175971df46 100644 --- a/kconf_update/CMakeLists.txt +++ b/kconf_update/CMakeLists.txt @@ -23,3 +23,5 @@ install(FILES kwinrules.upd DESTINATION ${KDE_INSTALL_KCONFUPDATEDIR}) install(PROGRAMS kwinrules-5.19-placement.pl DESTINATION ${KDE_INSTALL_KCONFUPDATEDIR}) +install(PROGRAMS kwinrules-5.23-virtual-desktop-ids.py + DESTINATION ${KDE_INSTALL_KCONFUPDATEDIR}) diff --git a/kconf_update/kwinrules-5.23-virtual-desktop-ids.py b/kconf_update/kwinrules-5.23-virtual-desktop-ids.py new file mode 100644 index 0000000000..a0a21ad038 --- /dev/null +++ b/kconf_update/kwinrules-5.23-virtual-desktop-ids.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python3 + +import fileinput +import configparser +import os.path +import subprocess + +# Get the config standard locations +config_locations = subprocess.check_output(['qtpaths', '--paths', 'ConfigLocation']).decode('utf-8').strip().split(':') +config_paths = [os.path.join(folder, 'kwinrc') for folder in config_locations] + +# Get the desktops information from `kwinrc` config file +kwinrc = configparser.ConfigParser(strict=False, allow_no_value=True) +kwinrc.read(config_paths) + +num_desktops = int(kwinrc.get('Desktops', 'Number', fallback='')) + +# Generete the map from x11ids (ennumeration) to UUIDs +desktopUUIDs = { -1: "" } # NET::OnAllDesktops -> Empty string +for i in range(1, num_desktops + 1): + uuid = kwinrc.get('Desktops', f'Id_{i}', fallback='') + desktopUUIDs[i] = str(uuid) + +# Apply the conversion to `kwinrulesrc` +for line in fileinput.input(): + # Rename key `desktoprule` to `desktopsrule` + if line.startswith("desktoprule="): + value = line[len("desktoprule="):].strip() + print("desktopsrule=" + value) + print("# DELETE desktoprule") + continue + + if not line.startswith("desktop="): + print(line.strip()) + continue + + # Convert key `desktop` (x11id) to `desktops` (list of UUIDs) + try: + value = line[len("desktop="):].strip() + x11id = int(value) + uuid = desktopUUIDs[x11id] + except: + print(line.strip()) # If there is some error, print back the line + continue + + print("desktops=" + uuid) + print("# DELETE desktop") diff --git a/kconf_update/kwinrules.upd b/kconf_update/kwinrules.upd index 0b30010c99..cc381bf29f 100644 --- a/kconf_update/kwinrules.upd +++ b/kconf_update/kwinrules.upd @@ -5,3 +5,7 @@ Id=replace-placement-string-to-enum File=kwinrulesrc Script=kwinrules-5.19-placement.pl,perl +# Replace the `desktop` x11id number to `desktops` list of desktop UUIDs +Id=use-virtual-desktop-ids +File=kwinrulesrc +Script=kwinrules-5.23-virtual-desktop-ids.py,python3 diff --git a/src/abstract_client.cpp b/src/abstract_client.cpp index 8d48340f89..fda1d5c473 100644 --- a/src/abstract_client.cpp +++ b/src/abstract_client.cpp @@ -497,7 +497,7 @@ void AbstractClient::setDesktops(QVector desktops) doSetDesktop(); FocusChain::self()->update(this, FocusChain::MakeFirst); - updateWindowRules(Rules::Desktop); + updateWindowRules(Rules::Desktops); Q_EMIT desktopChanged(); if (wasOnCurrentDesktop != isOnCurrentDesktop()) @@ -572,6 +572,20 @@ QVector AbstractClient::x11DesktopIds() const return x11Ids; } +QStringList AbstractClient::desktopIds() const +{ + const auto desks = desktops(); + QStringList ids; + ids.reserve(desks.count()); + std::transform(desks.constBegin(), desks.constEnd(), + std::back_inserter(ids), + [] (const VirtualDesktop *vd) { + return vd->id(); + } + ); + return ids; +}; + ShadeMode AbstractClient::shadeMode() const { return m_shadeMode; diff --git a/src/abstract_client.h b/src/abstract_client.h index 471e9f508d..4743b74f0d 100644 --- a/src/abstract_client.h +++ b/src/abstract_client.h @@ -491,6 +491,7 @@ public: return m_desktops; } QVector x11DesktopIds() const; + QStringList desktopIds() const; void setMinimized(bool set); /** diff --git a/src/kcmkwin/kwinrules/CMakeLists.txt b/src/kcmkwin/kwinrules/CMakeLists.txt index 64b19cec58..48f16dc611 100644 --- a/src/kcmkwin/kwinrules/CMakeLists.txt +++ b/src/kcmkwin/kwinrules/CMakeLists.txt @@ -33,6 +33,7 @@ set(kwin_kcm_rules_XCB_LIBS set(kcm_libs Qt::Quick Qt::QuickWidgets + Qt::X11Extras KF5::KCMUtils KF5::I18n diff --git a/src/kcmkwin/kwinrules/ruleitem.cpp b/src/kcmkwin/kwinrules/ruleitem.cpp index 470105b81d..46830c5f73 100644 --- a/src/kcmkwin/kwinrules/ruleitem.cpp +++ b/src/kcmkwin/kwinrules/ruleitem.cpp @@ -212,6 +212,9 @@ QVariant RuleItem::typedValue(const QVariant &value) const case Size: return value.toSize(); case String: + if (value.type() == QVariant::StringList && !value.toStringList().isEmpty()) { + return value.toStringList().at(0).trimmed(); + } return value.toString().trimmed(); case Shortcut: return value.toString(); diff --git a/src/kcmkwin/kwinrules/rulesmodel.cpp b/src/kcmkwin/kwinrules/rulesmodel.cpp index 19a42b6168..b1910a20d5 100644 --- a/src/kcmkwin/kwinrules/rulesmodel.cpp +++ b/src/kcmkwin/kwinrules/rulesmodel.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -433,14 +434,26 @@ void RulesModel::populateRuleList() i18n("Maximized vertically"), i18n("Size & Position"), QIcon::fromTheme("resizerow"))); - auto desktop = addRule(new RuleItem(QLatin1String("desktop"), - RulePolicy::SetRule, RuleItem::Option, - i18n("Virtual Desktop"), i18n("Size & Position"), - QIcon::fromTheme("virtual-desktops"))); - desktop->setOptionsData(virtualDesktopsModelData()); + RuleItem *desktops; + if (QX11Info::isPlatformX11()) { + // Single selection of Virtual Desktop on X11 + desktops = new RuleItem(QLatin1String("desktops"), + RulePolicy::SetRule, RuleItem::Option, + i18n("Virtual Desktop"), i18n("Size & Position"), + QIcon::fromTheme("virtual-desktops")); + } else { + // Multiple selection on Wayland + desktops = new RuleItem(QLatin1String("desktops"), + RulePolicy::SetRule, RuleItem::OptionList, + i18n("Virtual Desktops"), i18n("Size & Position"), + QIcon::fromTheme("virtual-desktops")); + } + addRule(desktops); + desktops->setOptionsData(virtualDesktopsModelData()); connect(this, &RulesModel::virtualDesktopsUpdated, - this, [this] { m_rules["desktop"]->setOptionsData(virtualDesktopsModelData()); }); + this, [this] { m_rules["desktops"]->setOptionsData(virtualDesktopsModelData()); }); + updateVirtualDesktops(); #ifdef KWIN_BUILD_ACTIVITIES @@ -645,7 +658,6 @@ const QHash RulesModel::x11PropertyHash() { "caption", "title" }, { "role", "windowrole" }, { "clientMachine", "clientmachine" }, - { "x11DesktopNumber", "desktop" }, { "maximizeHorizontal", "maximizehoriz" }, { "maximizeVertical", "maximizevert" }, { "minimized", "minimize" }, @@ -687,6 +699,18 @@ void RulesModel::setSuggestedProperties(const QVariantMap &info) m_rules["wmclass"]->setSuggestedValue(wmsimpleclass); m_rules["wmclasshelper"]->setSuggestedValue(wmcompleteclass); + //TODO: Make the DBus method `queryWindowInfo` return the list of desktop IDs and use them directly + if (info.value("x11DesktopNumber").toInt() == NET::OnAllDesktops) { + m_rules["desktops"]->setSuggestedValue(QStringList()); + } else { + for (const auto vd : qAsConst(m_virtualDesktops)) { + if (info.value("x11DesktopNumber").toUInt() == vd.position + 1) { + m_rules["desktops"]->setSuggestedValue(QStringList{ vd.id }); + break; + } + } + } + #ifdef KWIN_BUILD_ACTIVITIES const QStringList activities = info.value("activities").toStringList(); m_rules["activity"]->setSuggestedValue(activities.isEmpty() ? QStringList{ Activities::nullUuid() } @@ -729,15 +753,14 @@ QList RulesModel::windowTypesModelData() const QList RulesModel::virtualDesktopsModelData() const { - QList modelData; + QList modelData = { {QString(), i18n("All Desktops"), QIcon::fromTheme("window-pin")} }; for (const DBusDesktopDataStruct &desktop : m_virtualDesktops) { modelData << OptionsModel::Data{ - desktop.position + 1, // "desktop" setting uses the desktop position (int) starting at 1 + desktop.id, QString::number(desktop.position + 1).rightJustified(2) + QStringLiteral(": ") + desktop.name, QIcon::fromTheme("virtual-desktops") }; } - modelData << OptionsModel::Data{ NET::OnAllDesktops, i18n("All Desktops"), QIcon::fromTheme("window-pin") }; return modelData; } diff --git a/src/rules.cpp b/src/rules.cpp index f501a7431d..214b03cb50 100644 --- a/src/rules.cpp +++ b/src/rules.cpp @@ -50,7 +50,7 @@ Rules::Rules() , opacityactiverule(UnusedForceRule) , opacityinactiverule(UnusedForceRule) , ignoregeometryrule(UnusedSetRule) - , desktoprule(UnusedSetRule) + , desktopsrule(UnusedSetRule) , screenrule(UnusedSetRule) , activityrule(UnusedSetRule) , typerule(UnusedForceRule) @@ -142,7 +142,7 @@ void Rules::readFromSettings(const RuleSettings *settings) READ_FORCE_RULE(opacityactive,); READ_FORCE_RULE(opacityinactive,); READ_SET_RULE(ignoregeometry); - READ_SET_RULE(desktop); + READ_SET_RULE(desktops); READ_SET_RULE(screen); READ_SET_RULE(activity); READ_FORCE_RULE(type, static_cast); @@ -222,7 +222,7 @@ void Rules::write(RuleSettings *settings) const WRITE_FORCE_RULE(opacityactive, Opacityactive,); WRITE_FORCE_RULE(opacityinactive, Opacityinactive,); WRITE_SET_RULE(ignoregeometry, Ignoregeometry,); - WRITE_SET_RULE(desktop, Desktop,); + WRITE_SET_RULE(desktops, Desktops,); WRITE_SET_RULE(screen, Screen,); WRITE_SET_RULE(activity, Activity,); WRITE_FORCE_RULE(type, Type,); @@ -274,7 +274,7 @@ bool Rules::isEmpty() const && opacityactiverule == UnusedForceRule && opacityinactiverule == UnusedForceRule && ignoregeometryrule == UnusedSetRule - && desktoprule == UnusedSetRule + && desktopsrule == UnusedSetRule && screenrule == UnusedSetRule && activityrule == UnusedSetRule && typerule == UnusedForceRule @@ -445,9 +445,9 @@ bool Rules::update(AbstractClient* c, int selection) size = new_size; } } - if NOW_REMEMBER(Desktop, desktop) { - updated = updated || desktop != c->desktop(); - desktop = c->desktop(); + if NOW_REMEMBER(Desktops, desktops) { + updated = updated || desktops != c->desktopIds(); + desktops = c->desktopIds(); } if NOW_REMEMBER(Screen, screen) { updated = updated || screen != c->screen(); @@ -568,20 +568,17 @@ APPLY_RULE(screen, Screen, int) APPLY_RULE(activity, Activity, QStringList) APPLY_FORCE_RULE(type, Type, NET::WindowType) -bool Rules::applyDesktops(QVector &desktops, bool init) const +bool Rules::applyDesktops(QVector &vds, bool init) const { - if (checkSetRule(desktoprule, init)) { - if (desktop == NET::OnAllDesktops) { - desktops = {}; - } else { - if (auto vd = VirtualDesktopManager::self()->desktopForX11Id(desktop)) { - desktops = {vd}; - } else { - desktops = {VirtualDesktopManager::self()->currentDesktop()}; + if (checkSetRule(desktopsrule, init)) { + vds.clear(); + for (auto id : desktops) { + if (auto vd = VirtualDesktopManager::self()->desktopForId(id)) { + vds << vd; } } } - return checkSetStop(desktoprule); + return checkSetStop(desktopsrule); } bool Rules::applyMaximizeHoriz(MaximizeMode& mode, bool init) const @@ -678,7 +675,7 @@ bool Rules::discardUsed(bool withdrawn) DISCARD_USED_FORCE_RULE(opacityactive); DISCARD_USED_FORCE_RULE(opacityinactive); DISCARD_USED_SET_RULE(ignoregeometry); - DISCARD_USED_SET_RULE(desktop); + DISCARD_USED_SET_RULE(desktops); DISCARD_USED_SET_RULE(screen); DISCARD_USED_SET_RULE(activity); DISCARD_USED_FORCE_RULE(type); diff --git a/src/rules.h b/src/rules.h index bcc4204c79..22bbb5bc73 100644 --- a/src/rules.h +++ b/src/rules.h @@ -95,7 +95,7 @@ public: explicit Rules(const RuleSettings*); Rules(const QString&, bool temporary); enum Type { - Position = 1<<0, Size = 1<<1, Desktop = 1<<2, + Position = 1<<0, Size = 1<<1, Desktops = 1<<2, MaximizeVert = 1<<3, MaximizeHoriz = 1<<4, Minimize = 1<<5, Shade = 1<<6, SkipTaskbar = 1<<7, SkipPager = 1<<8, SkipSwitcher = 1<<9, Above = 1<<10, Below = 1<<11, Fullscreen = 1<<12, @@ -182,6 +182,9 @@ private: bool matchRole(const QByteArray& match_role) const; bool matchTitle(const QString& match_title) const; bool matchClientMachine(const QByteArray& match_machine, bool local) const; +#ifdef KCMRULES +private: +#endif void readFromSettings(const RuleSettings *settings); static ForceRule convertForceRule(int v); static QString getDecoColor(const QString &themeName); @@ -219,8 +222,8 @@ private: ForceRule opacityinactiverule; bool ignoregeometry; SetRule ignoregeometryrule; - int desktop; - SetRule desktoprule; + QStringList desktops; + SetRule desktopsrule; int screen; SetRule screenrule; QStringList activity; diff --git a/src/rulesettings.kcfg b/src/rulesettings.kcfg index 8b5e9b738a..bebf098d2c 100644 --- a/src/rulesettings.kcfg +++ b/src/rulesettings.kcfg @@ -147,12 +147,12 @@ Rules::UnusedSetRule - - - 0 + + + {} - - + + Rules::UnusedSetRule static_cast<Rules::SetRule>(Rules::ForceTemporarily) Rules::UnusedSetRule