16#include <QRegularExpression>
17#include <QTemporaryFile>
29#include "rulesettings.h"
36 : layerrule(UnusedForceRule)
37 , wmclassmatch(UnimportantMatch)
38 , wmclasscomplete(UnimportantMatch)
39 , windowrolematch(UnimportantMatch)
40 , titlematch(UnimportantMatch)
41 , clientmachinematch(UnimportantMatch)
42 , types(NET::AllTypesMask)
43 , placementrule(UnusedForceRule)
44 , positionrule(UnusedSetRule)
45 , sizerule(UnusedSetRule)
46 , minsizerule(UnusedForceRule)
47 , maxsizerule(UnusedForceRule)
48 , opacityactiverule(UnusedForceRule)
49 , opacityinactiverule(UnusedForceRule)
50 , ignoregeometryrule(UnusedSetRule)
51 , desktopsrule(UnusedSetRule)
52 , screenrule(UnusedSetRule)
53 , activityrule(UnusedSetRule)
54 , maximizevertrule(UnusedSetRule)
55 , maximizehorizrule(UnusedSetRule)
56 , minimizerule(UnusedSetRule)
57 , shaderule(UnusedSetRule)
58 , skiptaskbarrule(UnusedSetRule)
59 , skippagerrule(UnusedSetRule)
60 , skipswitcherrule(UnusedSetRule)
61 , aboverule(UnusedSetRule)
62 , belowrule(UnusedSetRule)
63 , fullscreenrule(UnusedSetRule)
64 , noborderrule(UnusedSetRule)
65 , decocolorrule(UnusedForceRule)
66 , blockcompositingrule(UnusedForceRule)
67 , fsplevelrule(UnusedForceRule)
68 , fpplevelrule(UnusedForceRule)
69 , acceptfocusrule(UnusedForceRule)
70 , closeablerule(UnusedForceRule)
71 , autogrouprule(UnusedForceRule)
72 , autogroupfgrule(UnusedForceRule)
73 , autogroupidrule(UnusedForceRule)
74 , strictgeometryrule(UnusedForceRule)
75 , shortcutrule(UnusedSetRule)
76 , disableglobalshortcutsrule(UnusedForceRule)
77 , desktopfilerule(UnusedSetRule)
78 , adaptivesyncrule(UnusedForceRule)
82#define READ_MATCH_STRING(var, func) \
83 var = settings->var() func; \
84 var##match = static_cast<StringMatch>(settings->var##match())
86#define READ_SET_RULE(var) \
87 var = settings->var(); \
88 var##rule = static_cast<SetRule>(settings->var##rule())
90#define READ_FORCE_RULE(var, func) \
91 var = func(settings->var()); \
92 var##rule = convertForceRule(settings->var##rule())
96 readFromSettings(settings);
99void Rules::readFromSettings(
const RuleSettings *settings)
101 description = settings->description();
102 if (description.isEmpty()) {
103 description = settings->descriptionLegacy();
106 wmclasscomplete = settings->wmclasscomplete();
110 types = NET::WindowTypeMask(settings->types());
118 if (!minsize.isValid()) {
119 minsize = QSize(1, 1);
122 if (maxsize.isEmpty()) {
123 maxsize = QSize(32767, 32767);
144 if (decocolor.isEmpty()) {
164#undef READ_MATCH_STRING
166#undef READ_FORCE_RULE
167#undef READ_FORCE_RULE2
169#define WRITE_MATCH_STRING(var, capital, force) \
170 settings->set##capital##match(var##match); \
171 if (!var.isEmpty() || force) { \
172 settings->set##capital(var); \
175#define WRITE_SET_RULE(var, capital, func) \
176 settings->set##capital##rule(var##rule); \
177 if (var##rule != UnusedSetRule) { \
178 settings->set##capital(func(var)); \
181#define WRITE_FORCE_RULE(var, capital, func) \
182 settings->set##capital##rule(var##rule); \
183 if (var##rule != UnusedForceRule) { \
184 settings->set##capital(func(var)); \
189 settings->setDescription(description);
192 settings->setWmclasscomplete(wmclasscomplete);
196 settings->setTypes(types);
219 auto colorToString = [](
const QString &value) -> QString {
220 if (value.endsWith(QLatin1String(
".colors"))) {
221 return QFileInfo(value).baseName();
243#undef WRITE_MATCH_STRING
245#undef WRITE_FORCE_RULE
297QString Rules::getDecoColor(
const QString &themeName)
299 if (themeName.isEmpty()) {
303 return QStandardPaths::locate(QStandardPaths::GenericDataLocation,
304 QLatin1String(
"color-schemes/") + themeName + QLatin1String(
".colors"));
307bool Rules::matchType(NET::WindowType match_type)
const
309 if (types != NET::AllTypesMask) {
310 if (match_type == NET::Unknown) {
311 match_type = NET::Normal;
313 if (!NET::typeMatchesMask(match_type, types)) {
320bool Rules::matchWMClass(
const QString &match_class,
const QString &match_name)
const
324 QString cwmclass = wmclasscomplete
325 ? match_name +
' ' + match_class
327 if (wmclassmatch ==
RegExpMatch && !QRegularExpression(wmclass).
match(cwmclass).hasMatch()) {
330 if (wmclassmatch ==
ExactMatch && cwmclass != wmclass) {
333 if (wmclassmatch ==
SubstringMatch && !cwmclass.contains(wmclass)) {
340bool Rules::matchRole(
const QString &match_role)
const
343 if (windowrolematch ==
RegExpMatch && !QRegularExpression(windowrole).
match(match_role).hasMatch()) {
346 if (windowrolematch ==
ExactMatch && match_role != windowrole) {
349 if (windowrolematch ==
SubstringMatch && !match_role.contains(windowrole)) {
356bool Rules::matchTitle(
const QString &match_title)
const
359 if (titlematch ==
RegExpMatch && !QRegularExpression(title).
match(match_title).hasMatch()) {
362 if (titlematch ==
ExactMatch && title != match_title) {
365 if (titlematch ==
SubstringMatch && !match_title.contains(title)) {
372bool Rules::matchClientMachine(
const QString &match_machine,
bool local)
const
376 if (match_machine !=
"localhost" && local
377 && matchClientMachine(
"localhost",
true)) {
381 && !QRegularExpression(clientmachine).
match(match_machine).hasMatch()) {
385 && clientmachine != match_machine) {
389 && !match_machine.contains(clientmachine)) {
420#define NOW_REMEMBER(_T_, _V_) ((selection & _T_) && (_V_##rule == (SetRule)Remember))
425 bool updated =
false;
428 QPoint new_pos = position;
431 new_pos.setX(c->
pos().x());
434 new_pos.setY(c->
pos().y());
436 updated = updated || position != new_pos;
442 QSize new_size = size;
445 new_size.setWidth(c->
size().width());
448 new_size.setHeight(c->
size().height());
450 updated = updated || size != new_size;
455 updated = updated || desktops != c->
desktopIds();
460 updated = updated || screen != index;
464 updated = updated || activity != c->
activities();
484 updated = updated || skiptaskbar != c->
skipTaskbar();
488 updated = updated || skippager != c->
skipPager();
496 updated = updated || above != c->
keepAbove();
500 updated = updated || below != c->
keepBelow();
508 updated = updated || noborder != c->
noBorder();
520#define APPLY_RULE(var, name, type) \
521 bool Rules::apply##name(type &arg, bool init) const \
523 if (checkSetRule(var##rule, init)) \
525 return checkSetStop(var##rule); \
528#define APPLY_FORCE_RULE(var, name, type) \
529 bool Rules::apply##name(type &arg) const \
531 if (checkForceRule(var##rule)) \
533 return checkForceStop(var##rule); \
538bool
Rules::applyGeometry(QRectF &rect,
bool init)
const
540 QPointF p = rect.topLeft();
541 QSizeF s = rect.size();
543 if (applyPosition(p, init)) {
547 if (applySize(s, init)) {
556 if (this->position !=
invalidPoint && checkSetRule(positionrule, init)) {
557 pos = this->position;
559 return checkSetStop(positionrule);
564 if (this->size.isValid() && checkSetRule(sizerule, init)) {
567 return checkSetStop(sizerule);
574APPLY_RULE(ignoregeometry, IgnoreGeometry,
bool)
582 if (checkSetRule(desktopsrule, init)) {
584 for (
auto id : desktops) {
585 if (
auto vd = VirtualDesktopManager::self()->desktopForId(
id)) {
590 return checkSetStop(desktopsrule);
595 if (checkSetRule(maximizehorizrule, init)) {
598 return checkSetStop(maximizehorizrule);
603 if (checkSetRule(maximizevertrule, init)) {
606 return checkSetStop(maximizevertrule);
613 if (checkSetRule(shaderule, init)) {
621 return checkSetStop(shaderule);
647#undef APPLY_FORCE_RULE
649#define DISCARD_USED_SET_RULE(var) \
651 if (var##rule == (SetRule)ApplyNow || (withdrawn && var##rule == (SetRule)ForceTemporarily)) { \
652 var##rule = UnusedSetRule; \
656#define DISCARD_USED_FORCE_RULE(var) \
658 if (withdrawn && var##rule == (ForceRule)ForceTemporarily) { \
659 var##rule = UnusedForceRule; \
666 bool changed =
false;
707#undef DISCARD_USED_SET_RULE
708#undef DISCARD_USED_FORCE_RULE
714 return stream <<
"[" << r->description <<
":" << r->wmclass <<
"]";
721 bool updated =
false;
722 for (QList<Rules *>::ConstIterator it = rules.constBegin();
723 it != rules.constEnd();
725 if ((*it)->update(c, selection)) {
734#define CHECK_RULE(rule, type) \
735 type WindowRules::check##rule(type arg, bool init) const \
737 if (rules.count() == 0) \
740 for (QList<Rules *>::ConstIterator it = rules.constBegin(); \
741 it != rules.constEnd(); \
743 if ((*it)->apply##rule(ret, init)) \
749#define CHECK_FORCE_RULE(rule, type) \
750 type WindowRules::check##rule(type arg) const \
752 if (rules.count() == 0) \
755 for (QList<Rules *>::ConstIterator it = rules.begin(); \
758 if ((*it)->apply##rule(ret)) \
768 return QRectF(checkPosition(rect.topLeft(), init), checkSize(rect.size(), init));
783 const bool inAnyOutput = std::any_of(outputs.begin(), outputs.end(), [ret](
const auto output) {
784 return output->geometryF().contains(ret);
815 if (rules.isEmpty()) {
819 for (
Rules *rule : rules) {
820 if (rule->applyScreen(ret, init)) {
825 return ruleOutput ? ruleOutput : output;
854#undef CHECK_FORCE_RULE
857 : m_updateTimer(new QTimer(this))
858 , m_updatesDisabled(false)
860 connect(m_updateTimer, &QTimer::timeout,
this, &RuleBook::save);
861 m_updateTimer->setInterval(1000);
862 m_updateTimer->setSingleShot(
true);
871void RuleBook::deleteAll()
880 for (
Rules *rule : m_rules) {
881 if (rule->match(window)) {
882 qCDebug(KWIN_CORE) <<
"Rule found:" << rule <<
":" << window;
893 args << QStringLiteral(
"uuid=%1").arg(c->
internalId().toString());
895 args << QStringLiteral(
"whole-app");
897 QProcess *p =
new QProcess(
this);
898 p->setArguments({
"kcm_kwinrules",
"--args", args.join(QLatin1Char(
' '))});
899 p->setProcessEnvironment(kwinApp()->processStartupEnvironment());
900 p->setProgram(QStandardPaths::findExecutable(
"kcmshell6"));
901 p->setProcessChannelMode(QProcess::MergedChannels);
902 connect(p,
static_cast<void (QProcess::*)(
int, QProcess::ExitStatus)
>(&QProcess::finished), p, &QProcess::deleteLater);
903 connect(p, &QProcess::errorOccurred,
this, [p](QProcess::ProcessError e) {
904 if (e == QProcess::FailedToStart) {
905 qCDebug(KWIN_CORE) <<
"Failed to start" << p->program();
915 m_config = KSharedConfig::openConfig(QStringLiteral(
"kwinrulesrc"), KConfig::NoGlobals);
917 m_config->reparseConfiguration();
921 m_rules = book.
rules();
926 m_updateTimer->stop();
928 qCWarning(KWIN_CORE) <<
"RuleBook::save invoked without prior invocation of RuleBook::load";
931 RuleBookSettings settings(m_config);
932 settings.setRules(m_rules);
938 bool updated =
false;
939 for (QList<Rules *>::Iterator it = m_rules.begin();
940 it != m_rules.end();) {
942 if ((*it)->discardUsed(withdrawn)) {
945 if ((*it)->isEmpty()) {
948 it = m_rules.erase(it);
962 m_updateTimer->start();
967 m_updatesDisabled = disable;
970 for (
Window *window : windows) {
971 if (window->supportsWindowRules()) {
982#include "moc_rules.cpp"
const QString & hostName() const
void requestDiskStorage()
void setUpdatesDisabled(bool disable)
void edit(Window *c, bool whole_app)
void discardUsed(Window *c, bool withdraw)
WindowRules find(const Window *window) const
bool applyMaximizeVert(MaximizeMode &mode, bool init) const
bool applyMaximizeHoriz(MaximizeMode &mode, bool init) const
bool applyPosition(QPointF &pos, bool init) const
void write(RuleSettings *) const
bool update(Window *, int selection)
bool discardUsed(bool withdrawn)
bool applySize(QSizeF &s, bool init) const
bool match(const Window *c) const
void removeRule(Rules *r)
ClientMachine * clientMachine() const
virtual bool isFullScreen() const
virtual QString captionNormal() const =0
void evaluateWindowRules()
const WindowRules * rules() const
ShadeMode shadeMode() const
QStringList desktopIds() const
void captionNormalChanged()
virtual MaximizeMode maximizeMode() const
QPointF checkPositionSafe(QPointF pos, bool init=false) const
bool contains(const Rules *rule) const
Output * checkOutput(Output *output, bool init=false) const
void update(Window *, int selection)
QSizeF checkSize(QSizeF s, bool init=false) const
QRectF checkGeometrySafe(QRectF rect, bool init=false) const
QPointF checkPosition(QPointF pos, bool init=false) const
static Workspace * self()
RuleBook * rulebook() const
QList< Output * > outputs() const
const QList< Window * > windows() const
std::variant< KeyboardShortcut, PointerButtonShortcut, PointerAxisShortcut, RealtimeFeedbackSwipeShortcut, RealtimeFeedbackPinchShortcut > Shortcut
QDebug & operator<<(QDebug &s, const KWin::DrmConnector *obj)
const QPoint invalidPoint(INT_MIN, INT_MIN)
@ MaximizeVertical
The window is maximized vertically.
#define DISCARD_USED_SET_RULE(var)
#define READ_FORCE_RULE(var, func)
#define CHECK_FORCE_RULE(rule, type)
#define READ_MATCH_STRING(var, func)
#define APPLY_RULE(var, name, type)
#define NOW_REMEMBER(_T_, _V_)
#define APPLY_FORCE_RULE(var, name, type)
#define CHECK_RULE(rule, type)
#define DISCARD_USED_FORCE_RULE(var)
#define READ_SET_RULE(var)
#define WRITE_MATCH_STRING(var, capital, force)
#define WRITE_SET_RULE(var, capital, func)
#define WRITE_FORCE_RULE(var, capital, func)