Introduce layer window rule

The new window rule allows to overwrite the stack layer. It can be
useful on wayland to force picture-in-picture surfaces (which are
xdg-toplevels at the moment) to be placed above fullscreen windows.
Keep above flag is unsuitable because fullscreen windows are placed
higher "above" windows.

CCBUG: 466016
wilder/Plasma/6.2
Vlad Zahorodnii 2 years ago
parent 74a193d383
commit 5c4acbdddd
  1. 2
      autotests/integration/dbus_interface_test.cpp
  2. 41
      autotests/integration/xdgshellwindow_rules_test.cpp
  3. 1
      src/dbusinterface.cpp
  4. 24
      src/kcms/rules/rulesmodel.cpp
  5. 1
      src/kcms/rules/rulesmodel.h
  6. 11
      src/rules.cpp
  7. 5
      src/rules.h
  8. 21
      src/rulesettings.kcfg
  9. 5
      src/window.cpp

@ -140,6 +140,7 @@ void TestDbusInterface::testGetWindowInfoXdgShellClient()
#if KWIN_BUILD_ACTIVITIES #if KWIN_BUILD_ACTIVITIES
{QStringLiteral("activities"), QStringList()}, {QStringLiteral("activities"), QStringList()},
#endif #endif
{QStringLiteral("layer"), NormalLayer},
}; };
// let's get the window info // let's get the window info
@ -276,6 +277,7 @@ void TestDbusInterface::testGetWindowInfoX11Client()
#if KWIN_BUILD_ACTIVITIES #if KWIN_BUILD_ACTIVITIES
{QStringLiteral("activities"), QStringList()}, {QStringLiteral("activities"), QStringList()},
#endif #endif
{QStringLiteral("layer"), NormalLayer},
}; };
// let's get the window info // let's get the window info

@ -151,6 +151,10 @@ private Q_SLOTS:
void testScreenApplyNow(); void testScreenApplyNow();
void testScreenForceTemporarily(); void testScreenForceTemporarily();
void testLayerDontAffect();
void testLayerForce();
void testLayerForceTemporarily();
void testMatchAfterNameChange(); void testMatchAfterNameChange();
private: private:
@ -2980,5 +2984,42 @@ void TestXdgShellWindowRules::testMatchAfterNameChange()
QCOMPARE(window->keepAbove(), true); 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) WAYLANDTEST_MAIN(TestXdgShellWindowRules)
#include "xdgshellwindow_rules_test.moc" #include "xdgshellwindow_rules_test.moc"

@ -180,6 +180,7 @@ QVariantMap clientToVariantMap(const Window *c)
#if KWIN_BUILD_ACTIVITIES #if KWIN_BUILD_ACTIVITIES
{QStringLiteral("activities"), c->activities()}, {QStringLiteral("activities"), c->activities()},
#endif #endif
{QStringLiteral("layer"), c->layer()},
}; };
} }
} }

@ -719,6 +719,12 @@ void RulesModel::populateRuleList()
RulePolicy::ForceRule, RuleItem::Boolean, RulePolicy::ForceRule, RuleItem::Boolean,
i18n("Block compositing"), i18n("Appearance & Fixes"), i18n("Block compositing"), i18n("Appearance & Fixes"),
QIcon::fromTheme("composite-track-on"))); 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<QString, QString> RulesModel::x11PropertyHash() const QHash<QString, QString> RulesModel::x11PropertyHash()
@ -741,6 +747,7 @@ const QHash<QString, QString> RulesModel::x11PropertyHash()
{"type", "type"}, {"type", "type"},
{"desktopFile", "desktopfile"}, {"desktopFile", "desktopfile"},
{"desktops", "desktops"}, {"desktops", "desktops"},
{"layer", "layer"},
}; };
return propertyToRule; return propertyToRule;
}; };
@ -911,6 +918,23 @@ QList<OptionsModel::Data> RulesModel::colorSchemesModelData() const
return modelData; return modelData;
} }
QList<OptionsModel::Data> RulesModel::layerModelData() const
{
static const auto modelData = QList<OptionsModel::Data>{
{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) void RulesModel::detectWindowProperties(int miliseconds)
{ {
QTimer::singleShot(miliseconds, this, &RulesModel::selectX11Window); QTimer::singleShot(miliseconds, this, &RulesModel::selectX11Window);

@ -102,6 +102,7 @@ private:
QList<OptionsModel::Data> placementModelData() const; QList<OptionsModel::Data> placementModelData() const;
QList<OptionsModel::Data> focusModelData() const; QList<OptionsModel::Data> focusModelData() const;
QList<OptionsModel::Data> colorSchemesModelData() const; QList<OptionsModel::Data> colorSchemesModelData() const;
QList<OptionsModel::Data> layerModelData() const;
private Q_SLOTS: private Q_SLOTS:
void selectX11Window(); void selectX11Window();

@ -33,7 +33,8 @@ namespace KWin
{ {
Rules::Rules() Rules::Rules()
: wmclassmatch(UnimportantMatch) : layerrule(UnusedForceRule)
, wmclassmatch(UnimportantMatch)
, wmclasscomplete(UnimportantMatch) , wmclasscomplete(UnimportantMatch)
, windowrolematch(UnimportantMatch) , windowrolematch(UnimportantMatch)
, titlematch(UnimportantMatch) , titlematch(UnimportantMatch)
@ -160,6 +161,7 @@ void Rules::readFromSettings(const RuleSettings *settings)
READ_SET_RULE(shortcut); READ_SET_RULE(shortcut);
READ_FORCE_RULE(disableglobalshortcuts, ); READ_FORCE_RULE(disableglobalshortcuts, );
READ_SET_RULE(desktopfile); READ_SET_RULE(desktopfile);
READ_FORCE_RULE(layer, );
} }
#undef READ_MATCH_STRING #undef READ_MATCH_STRING
@ -238,6 +240,7 @@ void Rules::write(RuleSettings *settings) const
WRITE_SET_RULE(shortcut, Shortcut, ); WRITE_SET_RULE(shortcut, Shortcut, );
WRITE_FORCE_RULE(disableglobalshortcuts, Disableglobalshortcuts, ); WRITE_FORCE_RULE(disableglobalshortcuts, Disableglobalshortcuts, );
WRITE_SET_RULE(desktopfile, Desktopfile, ); WRITE_SET_RULE(desktopfile, Desktopfile, );
WRITE_FORCE_RULE(layer, Layer, );
} }
#undef WRITE_MATCH_STRING #undef WRITE_MATCH_STRING
@ -282,7 +285,8 @@ bool Rules::isEmpty() const
&& strictgeometryrule == UnusedForceRule && strictgeometryrule == UnusedForceRule
&& shortcutrule == UnusedSetRule && shortcutrule == UnusedSetRule
&& disableglobalshortcutsrule == UnusedForceRule && disableglobalshortcutsrule == UnusedForceRule
&& desktopfilerule == UnusedSetRule); && desktopfilerule == UnusedSetRule
&& layerrule == UnusedForceRule);
} }
Rules::ForceRule Rules::convertForceRule(int v) Rules::ForceRule Rules::convertForceRule(int v)
@ -575,6 +579,7 @@ APPLY_RULE(ignoregeometry, IgnoreGeometry, bool)
APPLY_RULE(screen, Screen, int) APPLY_RULE(screen, Screen, int)
APPLY_RULE(activity, Activity, QStringList) APPLY_RULE(activity, Activity, QStringList)
APPLY_FORCE_RULE(type, Type, NET::WindowType) APPLY_FORCE_RULE(type, Type, NET::WindowType)
APPLY_FORCE_RULE(layer, Layer, enum Layer)
bool Rules::applyDesktops(QList<VirtualDesktop *> &vds, bool init) const bool Rules::applyDesktops(QList<VirtualDesktop *> &vds, bool init) const
{ {
@ -698,6 +703,7 @@ bool Rules::discardUsed(bool withdrawn)
DISCARD_USED_SET_RULE(shortcut); DISCARD_USED_SET_RULE(shortcut);
DISCARD_USED_FORCE_RULE(disableglobalshortcuts); DISCARD_USED_FORCE_RULE(disableglobalshortcuts);
DISCARD_USED_SET_RULE(desktopfile); DISCARD_USED_SET_RULE(desktopfile);
DISCARD_USED_FORCE_RULE(layer);
return changed; return changed;
} }
@ -845,6 +851,7 @@ CHECK_FORCE_RULE(StrictGeometry, bool)
CHECK_RULE(Shortcut, QString) CHECK_RULE(Shortcut, QString)
CHECK_FORCE_RULE(DisableGlobalShortcuts, bool) CHECK_FORCE_RULE(DisableGlobalShortcuts, bool)
CHECK_RULE(DesktopFile, QString) CHECK_RULE(DesktopFile, QString)
CHECK_FORCE_RULE(Layer, Layer)
#undef CHECK_RULE #undef CHECK_RULE
#undef CHECK_FORCE_RULE #undef CHECK_FORCE_RULE

@ -77,6 +77,7 @@ public:
QString checkShortcut(QString s, bool init = false) const; QString checkShortcut(QString s, bool init = false) const;
bool checkDisableGlobalShortcuts(bool disable) const; bool checkDisableGlobalShortcuts(bool disable) const;
QString checkDesktopFile(QString desktopFile, bool init = false) const; QString checkDesktopFile(QString desktopFile, bool init = false) const;
Layer checkLayer(Layer layer) const;
private: private:
MaximizeMode checkMaximizeVert(MaximizeMode mode, bool init) const; MaximizeMode checkMaximizeVert(MaximizeMode mode, bool init) const;
@ -111,6 +112,7 @@ public:
Activity = 1 << 16, Activity = 1 << 16,
Screen = 1 << 17, Screen = 1 << 17,
DesktopFile = 1 << 18, DesktopFile = 1 << 18,
Layer = 1 << 19,
All = 0xffffffff All = 0xffffffff
}; };
Q_DECLARE_FLAGS(Types, Type) Q_DECLARE_FLAGS(Types, Type)
@ -184,6 +186,7 @@ public:
bool applyShortcut(QString &shortcut, bool init) const; bool applyShortcut(QString &shortcut, bool init) const;
bool applyDisableGlobalShortcuts(bool &disable) const; bool applyDisableGlobalShortcuts(bool &disable) const;
bool applyDesktopFile(QString &desktopFile, bool init) const; bool applyDesktopFile(QString &desktopFile, bool init) const;
bool applyLayer(enum Layer &layer) const;
private: private:
#endif #endif
@ -204,6 +207,8 @@ private:
static bool checkSetStop(SetRule rule); static bool checkSetStop(SetRule rule);
static bool checkForceStop(ForceRule rule); static bool checkForceStop(ForceRule rule);
#endif #endif
enum Layer layer;
ForceRule layerrule;
QString description; QString description;
QString wmclass; QString wmclass;
StringMatch wmclassmatch; StringMatch wmclassmatch;

@ -431,5 +431,26 @@
<max code="true">static_cast&lt;Rules::SetRule&gt;(Rules::ForceTemporarily)</max> <max code="true">static_cast&lt;Rules::SetRule&gt;(Rules::ForceTemporarily)</max>
<default code="true">Rules::UnusedSetRule</default> <default code="true">Rules::UnusedSetRule</default>
</entry> </entry>
<entry name="layer" type="Enum">
<label>Layer</label>
<choices name="KWin::Layer">
<choice name="DesktopLayer" value="desktop" />
<choice name="BelowLayer" value="below" />
<choice name="NormalLayer" value="normal" />
<choice name="AboveLayer" value="above" />
<choice name="NotificationLayer" value="notification" />
<choice name="ActiveLayer" value="fullscreen" />
<choice name="PopupLayer" value="popup" />
<choice name="CriticalNotificationLayer" value="critical-notification" />
<choice name="OnScreenDisplayLayer" value="osd" />
<choice name="UnmanagedLayer" value="overlay" />
</choices>
<default code="true">NormalLayer</default>
</entry>
<entry name="layerrule" type="Int">
<label>Layer rule type</label>
<default code="true">Rules::UnusedForceRule</default>
</entry>
</group> </group>
</kcfg> </kcfg>

@ -552,7 +552,7 @@ void Window::markAsDeleted()
Layer Window::layer() const Layer Window::layer() const
{ {
if (m_layer == UnknownLayer) { if (m_layer == UnknownLayer) {
const_cast<Window *>(this)->m_layer = belongsToLayer(); const_cast<Window *>(this)->m_layer = rules()->checkLayer(belongsToLayer());
} }
return m_layer; return m_layer;
} }
@ -562,7 +562,7 @@ void Window::updateLayer()
if (isDeleted()) { if (isDeleted()) {
return; return;
} }
if (layer() == belongsToLayer()) { if (layer() == rules()->checkLayer(belongsToLayer())) {
return; return;
} }
StackingUpdatesBlocker blocker(workspace()); StackingUpdatesBlocker blocker(workspace());
@ -4139,6 +4139,7 @@ void Window::applyWindowRules()
setFullScreen(isRequestedFullScreen()); setFullScreen(isRequestedFullScreen());
setNoBorder(noBorder()); setNoBorder(noBorder());
updateColorScheme(); updateColorScheme();
updateLayer();
// FSP // FSP
// AcceptFocus : // AcceptFocus :
if (workspace()->mostRecentlyActivatedWindow() == this if (workspace()->mostRecentlyActivatedWindow() == this

Loading…
Cancel
Save