diff --git a/autotests/integration/dbus_interface_test.cpp b/autotests/integration/dbus_interface_test.cpp index d4de1c89bb..c21532eeac 100644 --- a/autotests/integration/dbus_interface_test.cpp +++ b/autotests/integration/dbus_interface_test.cpp @@ -140,6 +140,7 @@ void TestDbusInterface::testGetWindowInfoXdgShellClient() #if KWIN_BUILD_ACTIVITIES {QStringLiteral("activities"), QStringList()}, #endif + {QStringLiteral("layer"), NormalLayer}, }; // let's get the window info @@ -276,6 +277,7 @@ void TestDbusInterface::testGetWindowInfoX11Client() #if KWIN_BUILD_ACTIVITIES {QStringLiteral("activities"), QStringList()}, #endif + {QStringLiteral("layer"), NormalLayer}, }; // let's get the window info diff --git a/autotests/integration/xdgshellwindow_rules_test.cpp b/autotests/integration/xdgshellwindow_rules_test.cpp index 922ae5ba11..b3da084782 100644 --- a/autotests/integration/xdgshellwindow_rules_test.cpp +++ b/autotests/integration/xdgshellwindow_rules_test.cpp @@ -151,6 +151,10 @@ private Q_SLOTS: void testScreenApplyNow(); void testScreenForceTemporarily(); + void testLayerDontAffect(); + void testLayerForce(); + void testLayerForceTemporarily(); + void testMatchAfterNameChange(); private: @@ -2980,5 +2984,42 @@ void TestXdgShellWindowRules::testMatchAfterNameChange() QCOMPARE(window->keepAbove(), true); } +void TestXdgShellWindowRules::testLayerDontAffect() +{ + setWindowRule("layer", QStringLiteral("overlay"), int(Rules::DontAffect)); + + createTestWindow(); + + // The layer should not be affected by the rule. + QCOMPARE(m_window->layer(), NormalLayer); + + destroyTestWindow(); +} + +void TestXdgShellWindowRules::testLayerForce() +{ + setWindowRule("layer", QStringLiteral("overlay"), int(Rules::Force)); + + createTestWindow(); + QCOMPARE(m_window->layer(), UnmanagedLayer); + + destroyTestWindow(); +} + +void TestXdgShellWindowRules::testLayerForceTemporarily() +{ + setWindowRule("layer", QStringLiteral("overlay"), int(Rules::ForceTemporarily)); + + createTestWindow(); + QCOMPARE(m_window->layer(), UnmanagedLayer); + + // The rule should be discarded when the window is closed. + destroyTestWindow(); + createTestWindow(); + QCOMPARE(m_window->layer(), NormalLayer); + + destroyTestWindow(); +} + WAYLANDTEST_MAIN(TestXdgShellWindowRules) #include "xdgshellwindow_rules_test.moc" diff --git a/src/dbusinterface.cpp b/src/dbusinterface.cpp index 7b766ef84e..8545c63100 100644 --- a/src/dbusinterface.cpp +++ b/src/dbusinterface.cpp @@ -180,6 +180,7 @@ QVariantMap clientToVariantMap(const Window *c) #if KWIN_BUILD_ACTIVITIES {QStringLiteral("activities"), c->activities()}, #endif + {QStringLiteral("layer"), c->layer()}, }; } } diff --git a/src/kcms/rules/rulesmodel.cpp b/src/kcms/rules/rulesmodel.cpp index c7aefb5a1c..703ecb11c7 100644 --- a/src/kcms/rules/rulesmodel.cpp +++ b/src/kcms/rules/rulesmodel.cpp @@ -719,6 +719,12 @@ void RulesModel::populateRuleList() RulePolicy::ForceRule, RuleItem::Boolean, i18n("Block compositing"), i18n("Appearance & Fixes"), QIcon::fromTheme("composite-track-on"))); + + auto layer = addRule(new RuleItem(QLatin1String("layer"), + RulePolicy::ForceRule, RuleItem::Option, + i18n("Layer"), i18n("Appearance & Fixes"), + QIcon::fromTheme("view-sort"))); + layer->setOptionsData(layerModelData()); } const QHash RulesModel::x11PropertyHash() @@ -741,6 +747,7 @@ const QHash RulesModel::x11PropertyHash() {"type", "type"}, {"desktopFile", "desktopfile"}, {"desktops", "desktops"}, + {"layer", "layer"}, }; return propertyToRule; }; @@ -911,6 +918,23 @@ QList RulesModel::colorSchemesModelData() const return modelData; } +QList RulesModel::layerModelData() const +{ + static const auto modelData = QList{ + {DesktopLayer, i18n("Desktop")}, + {BelowLayer, i18n("Below")}, + {NormalLayer, i18n("Normal")}, + {AboveLayer, i18n("Above")}, + {NotificationLayer, i18n("Notification")}, + {ActiveLayer, i18n("Fullscreen")}, + {PopupLayer, i18n("Popup")}, + {CriticalNotificationLayer, i18n("Critical Notification")}, + {OnScreenDisplayLayer, i18n("OSD")}, + {UnmanagedLayer, i18n("Overlay")}, + }; + return modelData; +} + void RulesModel::detectWindowProperties(int miliseconds) { QTimer::singleShot(miliseconds, this, &RulesModel::selectX11Window); diff --git a/src/kcms/rules/rulesmodel.h b/src/kcms/rules/rulesmodel.h index d81550067c..edd97eeb8f 100644 --- a/src/kcms/rules/rulesmodel.h +++ b/src/kcms/rules/rulesmodel.h @@ -102,6 +102,7 @@ private: QList placementModelData() const; QList focusModelData() const; QList colorSchemesModelData() const; + QList layerModelData() const; private Q_SLOTS: void selectX11Window(); diff --git a/src/rules.cpp b/src/rules.cpp index c4b6e5848d..c8fbcc40fe 100644 --- a/src/rules.cpp +++ b/src/rules.cpp @@ -33,7 +33,8 @@ namespace KWin { Rules::Rules() - : wmclassmatch(UnimportantMatch) + : layerrule(UnusedForceRule) + , wmclassmatch(UnimportantMatch) , wmclasscomplete(UnimportantMatch) , windowrolematch(UnimportantMatch) , titlematch(UnimportantMatch) @@ -160,6 +161,7 @@ void Rules::readFromSettings(const RuleSettings *settings) READ_SET_RULE(shortcut); READ_FORCE_RULE(disableglobalshortcuts, ); READ_SET_RULE(desktopfile); + READ_FORCE_RULE(layer, ); } #undef READ_MATCH_STRING @@ -238,6 +240,7 @@ void Rules::write(RuleSettings *settings) const WRITE_SET_RULE(shortcut, Shortcut, ); WRITE_FORCE_RULE(disableglobalshortcuts, Disableglobalshortcuts, ); WRITE_SET_RULE(desktopfile, Desktopfile, ); + WRITE_FORCE_RULE(layer, Layer, ); } #undef WRITE_MATCH_STRING @@ -282,7 +285,8 @@ bool Rules::isEmpty() const && strictgeometryrule == UnusedForceRule && shortcutrule == UnusedSetRule && disableglobalshortcutsrule == UnusedForceRule - && desktopfilerule == UnusedSetRule); + && desktopfilerule == UnusedSetRule + && layerrule == UnusedForceRule); } Rules::ForceRule Rules::convertForceRule(int v) @@ -575,6 +579,7 @@ APPLY_RULE(ignoregeometry, IgnoreGeometry, bool) APPLY_RULE(screen, Screen, int) APPLY_RULE(activity, Activity, QStringList) APPLY_FORCE_RULE(type, Type, NET::WindowType) +APPLY_FORCE_RULE(layer, Layer, enum Layer) bool Rules::applyDesktops(QList &vds, bool init) const { @@ -698,6 +703,7 @@ bool Rules::discardUsed(bool withdrawn) DISCARD_USED_SET_RULE(shortcut); DISCARD_USED_FORCE_RULE(disableglobalshortcuts); DISCARD_USED_SET_RULE(desktopfile); + DISCARD_USED_FORCE_RULE(layer); return changed; } @@ -845,6 +851,7 @@ CHECK_FORCE_RULE(StrictGeometry, bool) CHECK_RULE(Shortcut, QString) CHECK_FORCE_RULE(DisableGlobalShortcuts, bool) CHECK_RULE(DesktopFile, QString) +CHECK_FORCE_RULE(Layer, Layer) #undef CHECK_RULE #undef CHECK_FORCE_RULE diff --git a/src/rules.h b/src/rules.h index 05cb4c0b34..5d0cf2073b 100644 --- a/src/rules.h +++ b/src/rules.h @@ -77,6 +77,7 @@ public: QString checkShortcut(QString s, bool init = false) const; bool checkDisableGlobalShortcuts(bool disable) const; QString checkDesktopFile(QString desktopFile, bool init = false) const; + Layer checkLayer(Layer layer) const; private: MaximizeMode checkMaximizeVert(MaximizeMode mode, bool init) const; @@ -111,6 +112,7 @@ public: Activity = 1 << 16, Screen = 1 << 17, DesktopFile = 1 << 18, + Layer = 1 << 19, All = 0xffffffff }; Q_DECLARE_FLAGS(Types, Type) @@ -184,6 +186,7 @@ public: bool applyShortcut(QString &shortcut, bool init) const; bool applyDisableGlobalShortcuts(bool &disable) const; bool applyDesktopFile(QString &desktopFile, bool init) const; + bool applyLayer(enum Layer &layer) const; private: #endif @@ -204,6 +207,8 @@ private: static bool checkSetStop(SetRule rule); static bool checkForceStop(ForceRule rule); #endif + enum Layer layer; + ForceRule layerrule; QString description; QString wmclass; StringMatch wmclassmatch; diff --git a/src/rulesettings.kcfg b/src/rulesettings.kcfg index 4619d4015f..c29edd1665 100644 --- a/src/rulesettings.kcfg +++ b/src/rulesettings.kcfg @@ -431,5 +431,26 @@ static_cast<Rules::SetRule>(Rules::ForceTemporarily) Rules::UnusedSetRule + + + + + + + + + + + + + + + + NormalLayer + + + + Rules::UnusedForceRule + diff --git a/src/window.cpp b/src/window.cpp index e3b69d8baf..70316d31de 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -552,7 +552,7 @@ void Window::markAsDeleted() Layer Window::layer() const { if (m_layer == UnknownLayer) { - const_cast(this)->m_layer = belongsToLayer(); + const_cast(this)->m_layer = rules()->checkLayer(belongsToLayer()); } return m_layer; } @@ -562,7 +562,7 @@ void Window::updateLayer() if (isDeleted()) { return; } - if (layer() == belongsToLayer()) { + if (layer() == rules()->checkLayer(belongsToLayer())) { return; } StackingUpdatesBlocker blocker(workspace()); @@ -4139,6 +4139,7 @@ void Window::applyWindowRules() setFullScreen(isRequestedFullScreen()); setNoBorder(noBorder()); updateColorScheme(); + updateLayer(); // FSP // AcceptFocus : if (workspace()->mostRecentlyActivatedWindow() == this