File indexing completed on 2024-04-28 05:30:29

0001 /*
0002     KWin - the KDE window manager
0003     This file is part of the KDE project.
0004 
0005     SPDX-FileCopyrightText: 2004 Lubos Lunak <l.lunak@kde.org>
0006 
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 
0010 #include "rules.h"
0011 
0012 #include <QDebug>
0013 #include <QDir>
0014 #include <QFile>
0015 #include <QFileInfo>
0016 #include <QRegularExpression>
0017 #include <QTemporaryFile>
0018 #include <kconfig.h>
0019 
0020 #ifndef KCMRULES
0021 #include "client_machine.h"
0022 #include "main.h"
0023 #include "virtualdesktops.h"
0024 #include "window.h"
0025 #endif
0026 
0027 #include "core/output.h"
0028 #include "rulebooksettings.h"
0029 #include "rulesettings.h"
0030 #include "workspace.h"
0031 
0032 namespace KWin
0033 {
0034 
0035 Rules::Rules()
0036     : layerrule(UnusedForceRule)
0037     , wmclassmatch(UnimportantMatch)
0038     , wmclasscomplete(UnimportantMatch)
0039     , windowrolematch(UnimportantMatch)
0040     , titlematch(UnimportantMatch)
0041     , clientmachinematch(UnimportantMatch)
0042     , types(NET::AllTypesMask)
0043     , placementrule(UnusedForceRule)
0044     , positionrule(UnusedSetRule)
0045     , sizerule(UnusedSetRule)
0046     , minsizerule(UnusedForceRule)
0047     , maxsizerule(UnusedForceRule)
0048     , opacityactiverule(UnusedForceRule)
0049     , opacityinactiverule(UnusedForceRule)
0050     , ignoregeometryrule(UnusedSetRule)
0051     , desktopsrule(UnusedSetRule)
0052     , screenrule(UnusedSetRule)
0053     , activityrule(UnusedSetRule)
0054     , maximizevertrule(UnusedSetRule)
0055     , maximizehorizrule(UnusedSetRule)
0056     , minimizerule(UnusedSetRule)
0057     , shaderule(UnusedSetRule)
0058     , skiptaskbarrule(UnusedSetRule)
0059     , skippagerrule(UnusedSetRule)
0060     , skipswitcherrule(UnusedSetRule)
0061     , aboverule(UnusedSetRule)
0062     , belowrule(UnusedSetRule)
0063     , fullscreenrule(UnusedSetRule)
0064     , noborderrule(UnusedSetRule)
0065     , decocolorrule(UnusedForceRule)
0066     , blockcompositingrule(UnusedForceRule)
0067     , fsplevelrule(UnusedForceRule)
0068     , fpplevelrule(UnusedForceRule)
0069     , acceptfocusrule(UnusedForceRule)
0070     , closeablerule(UnusedForceRule)
0071     , autogrouprule(UnusedForceRule)
0072     , autogroupfgrule(UnusedForceRule)
0073     , autogroupidrule(UnusedForceRule)
0074     , strictgeometryrule(UnusedForceRule)
0075     , shortcutrule(UnusedSetRule)
0076     , disableglobalshortcutsrule(UnusedForceRule)
0077     , desktopfilerule(UnusedSetRule)
0078 {
0079 }
0080 
0081 #define READ_MATCH_STRING(var, func) \
0082     var = settings->var() func;      \
0083     var##match = static_cast<StringMatch>(settings->var##match())
0084 
0085 #define READ_SET_RULE(var) \
0086     var = settings->var(); \
0087     var##rule = static_cast<SetRule>(settings->var##rule())
0088 
0089 #define READ_FORCE_RULE(var, func) \
0090     var = func(settings->var());   \
0091     var##rule = convertForceRule(settings->var##rule())
0092 
0093 Rules::Rules(const RuleSettings *settings)
0094 {
0095     readFromSettings(settings);
0096 }
0097 
0098 void Rules::readFromSettings(const RuleSettings *settings)
0099 {
0100     description = settings->description();
0101     if (description.isEmpty()) {
0102         description = settings->descriptionLegacy();
0103     }
0104     READ_MATCH_STRING(wmclass, );
0105     wmclasscomplete = settings->wmclasscomplete();
0106     READ_MATCH_STRING(windowrole, );
0107     READ_MATCH_STRING(title, );
0108     READ_MATCH_STRING(clientmachine, .toLower());
0109     types = NET::WindowTypeMask(settings->types());
0110     READ_FORCE_RULE(placement, );
0111     READ_SET_RULE(position);
0112     READ_SET_RULE(size);
0113     if (size.isEmpty() && sizerule != static_cast<SetRule>(Remember)) {
0114         sizerule = UnusedSetRule;
0115     }
0116     READ_FORCE_RULE(minsize, );
0117     if (!minsize.isValid()) {
0118         minsize = QSize(1, 1);
0119     }
0120     READ_FORCE_RULE(maxsize, );
0121     if (maxsize.isEmpty()) {
0122         maxsize = QSize(32767, 32767);
0123     }
0124     READ_FORCE_RULE(opacityactive, );
0125     READ_FORCE_RULE(opacityinactive, );
0126     READ_SET_RULE(ignoregeometry);
0127     READ_SET_RULE(desktops);
0128     READ_SET_RULE(screen);
0129     READ_SET_RULE(activity);
0130     READ_SET_RULE(maximizevert);
0131     READ_SET_RULE(maximizehoriz);
0132     READ_SET_RULE(minimize);
0133     READ_SET_RULE(shade);
0134     READ_SET_RULE(skiptaskbar);
0135     READ_SET_RULE(skippager);
0136     READ_SET_RULE(skipswitcher);
0137     READ_SET_RULE(above);
0138     READ_SET_RULE(below);
0139     READ_SET_RULE(fullscreen);
0140     READ_SET_RULE(noborder);
0141 
0142     READ_FORCE_RULE(decocolor, getDecoColor);
0143     if (decocolor.isEmpty()) {
0144         decocolorrule = UnusedForceRule;
0145     }
0146 
0147     READ_FORCE_RULE(blockcompositing, );
0148     READ_FORCE_RULE(fsplevel, );
0149     READ_FORCE_RULE(fpplevel, );
0150     READ_FORCE_RULE(acceptfocus, );
0151     READ_FORCE_RULE(closeable, );
0152     READ_FORCE_RULE(autogroup, );
0153     READ_FORCE_RULE(autogroupfg, );
0154     READ_FORCE_RULE(autogroupid, );
0155     READ_FORCE_RULE(strictgeometry, );
0156     READ_SET_RULE(shortcut);
0157     READ_FORCE_RULE(disableglobalshortcuts, );
0158     READ_SET_RULE(desktopfile);
0159     READ_FORCE_RULE(layer, );
0160 }
0161 
0162 #undef READ_MATCH_STRING
0163 #undef READ_SET_RULE
0164 #undef READ_FORCE_RULE
0165 #undef READ_FORCE_RULE2
0166 
0167 #define WRITE_MATCH_STRING(var, capital, force) \
0168     settings->set##capital##match(var##match);  \
0169     if (!var.isEmpty() || force) {              \
0170         settings->set##capital(var);            \
0171     }
0172 
0173 #define WRITE_SET_RULE(var, capital, func)   \
0174     settings->set##capital##rule(var##rule); \
0175     if (var##rule != UnusedSetRule) {        \
0176         settings->set##capital(func(var));   \
0177     }
0178 
0179 #define WRITE_FORCE_RULE(var, capital, func) \
0180     settings->set##capital##rule(var##rule); \
0181     if (var##rule != UnusedForceRule) {      \
0182         settings->set##capital(func(var));   \
0183     }
0184 
0185 void Rules::write(RuleSettings *settings) const
0186 {
0187     settings->setDescription(description);
0188     // always write wmclass
0189     WRITE_MATCH_STRING(wmclass, Wmclass, true);
0190     settings->setWmclasscomplete(wmclasscomplete);
0191     WRITE_MATCH_STRING(windowrole, Windowrole, false);
0192     WRITE_MATCH_STRING(title, Title, false);
0193     WRITE_MATCH_STRING(clientmachine, Clientmachine, false);
0194     settings->setTypes(types);
0195     WRITE_FORCE_RULE(placement, Placement, );
0196     WRITE_SET_RULE(position, Position, );
0197     WRITE_SET_RULE(size, Size, );
0198     WRITE_FORCE_RULE(minsize, Minsize, );
0199     WRITE_FORCE_RULE(maxsize, Maxsize, );
0200     WRITE_FORCE_RULE(opacityactive, Opacityactive, );
0201     WRITE_FORCE_RULE(opacityinactive, Opacityinactive, );
0202     WRITE_SET_RULE(ignoregeometry, Ignoregeometry, );
0203     WRITE_SET_RULE(desktops, Desktops, );
0204     WRITE_SET_RULE(screen, Screen, );
0205     WRITE_SET_RULE(activity, Activity, );
0206     WRITE_SET_RULE(maximizevert, Maximizevert, );
0207     WRITE_SET_RULE(maximizehoriz, Maximizehoriz, );
0208     WRITE_SET_RULE(minimize, Minimize, );
0209     WRITE_SET_RULE(shade, Shade, );
0210     WRITE_SET_RULE(skiptaskbar, Skiptaskbar, );
0211     WRITE_SET_RULE(skippager, Skippager, );
0212     WRITE_SET_RULE(skipswitcher, Skipswitcher, );
0213     WRITE_SET_RULE(above, Above, );
0214     WRITE_SET_RULE(below, Below, );
0215     WRITE_SET_RULE(fullscreen, Fullscreen, );
0216     WRITE_SET_RULE(noborder, Noborder, );
0217     auto colorToString = [](const QString &value) -> QString {
0218         if (value.endsWith(QLatin1String(".colors"))) {
0219             return QFileInfo(value).baseName();
0220         } else {
0221             return value;
0222         }
0223     };
0224     WRITE_FORCE_RULE(decocolor, Decocolor, colorToString);
0225     WRITE_FORCE_RULE(blockcompositing, Blockcompositing, );
0226     WRITE_FORCE_RULE(fsplevel, Fsplevel, );
0227     WRITE_FORCE_RULE(fpplevel, Fpplevel, );
0228     WRITE_FORCE_RULE(acceptfocus, Acceptfocus, );
0229     WRITE_FORCE_RULE(closeable, Closeable, );
0230     WRITE_FORCE_RULE(autogroup, Autogroup, );
0231     WRITE_FORCE_RULE(autogroupfg, Autogroupfg, );
0232     WRITE_FORCE_RULE(autogroupid, Autogroupid, );
0233     WRITE_FORCE_RULE(strictgeometry, Strictgeometry, );
0234     WRITE_SET_RULE(shortcut, Shortcut, );
0235     WRITE_FORCE_RULE(disableglobalshortcuts, Disableglobalshortcuts, );
0236     WRITE_SET_RULE(desktopfile, Desktopfile, );
0237     WRITE_FORCE_RULE(layer, Layer, );
0238 }
0239 
0240 #undef WRITE_MATCH_STRING
0241 #undef WRITE_SET_RULE
0242 #undef WRITE_FORCE_RULE
0243 
0244 // returns true if it doesn't affect anything
0245 bool Rules::isEmpty() const
0246 {
0247     return (placementrule == UnusedForceRule
0248             && positionrule == UnusedSetRule
0249             && sizerule == UnusedSetRule
0250             && minsizerule == UnusedForceRule
0251             && maxsizerule == UnusedForceRule
0252             && opacityactiverule == UnusedForceRule
0253             && opacityinactiverule == UnusedForceRule
0254             && ignoregeometryrule == UnusedSetRule
0255             && desktopsrule == UnusedSetRule
0256             && screenrule == UnusedSetRule
0257             && activityrule == UnusedSetRule
0258             && maximizevertrule == UnusedSetRule
0259             && maximizehorizrule == UnusedSetRule
0260             && minimizerule == UnusedSetRule
0261             && shaderule == UnusedSetRule
0262             && skiptaskbarrule == UnusedSetRule
0263             && skippagerrule == UnusedSetRule
0264             && skipswitcherrule == UnusedSetRule
0265             && aboverule == UnusedSetRule
0266             && belowrule == UnusedSetRule
0267             && fullscreenrule == UnusedSetRule
0268             && noborderrule == UnusedSetRule
0269             && decocolorrule == UnusedForceRule
0270             && blockcompositingrule == UnusedForceRule
0271             && fsplevelrule == UnusedForceRule
0272             && fpplevelrule == UnusedForceRule
0273             && acceptfocusrule == UnusedForceRule
0274             && closeablerule == UnusedForceRule
0275             && autogrouprule == UnusedForceRule
0276             && autogroupfgrule == UnusedForceRule
0277             && autogroupidrule == UnusedForceRule
0278             && strictgeometryrule == UnusedForceRule
0279             && shortcutrule == UnusedSetRule
0280             && disableglobalshortcutsrule == UnusedForceRule
0281             && desktopfilerule == UnusedSetRule
0282             && layerrule == UnusedForceRule);
0283 }
0284 
0285 Rules::ForceRule Rules::convertForceRule(int v)
0286 {
0287     if (v == DontAffect || v == Force || v == ForceTemporarily) {
0288         return static_cast<ForceRule>(v);
0289     }
0290     return UnusedForceRule;
0291 }
0292 
0293 QString Rules::getDecoColor(const QString &themeName)
0294 {
0295     if (themeName.isEmpty()) {
0296         return QString();
0297     }
0298     // find the actual scheme file
0299     return QStandardPaths::locate(QStandardPaths::GenericDataLocation,
0300                                   QLatin1String("color-schemes/") + themeName + QLatin1String(".colors"));
0301 }
0302 
0303 bool Rules::matchType(NET::WindowType match_type) const
0304 {
0305     if (types != NET::AllTypesMask) {
0306         if (match_type == NET::Unknown) {
0307             match_type = NET::Normal; // NET::Unknown->NET::Normal is only here for matching
0308         }
0309         if (!NET::typeMatchesMask(match_type, types)) {
0310             return false;
0311         }
0312     }
0313     return true;
0314 }
0315 
0316 bool Rules::matchWMClass(const QString &match_class, const QString &match_name) const
0317 {
0318     if (wmclassmatch != UnimportantMatch) {
0319         // TODO optimize?
0320         QString cwmclass = wmclasscomplete
0321             ? match_name + ' ' + match_class
0322             : match_class;
0323         if (wmclassmatch == RegExpMatch && !QRegularExpression(wmclass).match(cwmclass).hasMatch()) {
0324             return false;
0325         }
0326         if (wmclassmatch == ExactMatch && cwmclass != wmclass) {
0327             return false;
0328         }
0329         if (wmclassmatch == SubstringMatch && !cwmclass.contains(wmclass)) {
0330             return false;
0331         }
0332     }
0333     return true;
0334 }
0335 
0336 bool Rules::matchRole(const QString &match_role) const
0337 {
0338     if (windowrolematch != UnimportantMatch) {
0339         if (windowrolematch == RegExpMatch && !QRegularExpression(windowrole).match(match_role).hasMatch()) {
0340             return false;
0341         }
0342         if (windowrolematch == ExactMatch && match_role != windowrole) {
0343             return false;
0344         }
0345         if (windowrolematch == SubstringMatch && !match_role.contains(windowrole)) {
0346             return false;
0347         }
0348     }
0349     return true;
0350 }
0351 
0352 bool Rules::matchTitle(const QString &match_title) const
0353 {
0354     if (titlematch != UnimportantMatch) {
0355         if (titlematch == RegExpMatch && !QRegularExpression(title).match(match_title).hasMatch()) {
0356             return false;
0357         }
0358         if (titlematch == ExactMatch && title != match_title) {
0359             return false;
0360         }
0361         if (titlematch == SubstringMatch && !match_title.contains(title)) {
0362             return false;
0363         }
0364     }
0365     return true;
0366 }
0367 
0368 bool Rules::matchClientMachine(const QString &match_machine, bool local) const
0369 {
0370     if (clientmachinematch != UnimportantMatch) {
0371         // if it's localhost, check also "localhost" before checking hostname
0372         if (match_machine != "localhost" && local
0373             && matchClientMachine("localhost", true)) {
0374             return true;
0375         }
0376         if (clientmachinematch == RegExpMatch
0377             && !QRegularExpression(clientmachine).match(match_machine).hasMatch()) {
0378             return false;
0379         }
0380         if (clientmachinematch == ExactMatch
0381             && clientmachine != match_machine) {
0382             return false;
0383         }
0384         if (clientmachinematch == SubstringMatch
0385             && !match_machine.contains(clientmachine)) {
0386             return false;
0387         }
0388     }
0389     return true;
0390 }
0391 
0392 #ifndef KCMRULES
0393 bool Rules::match(const Window *c) const
0394 {
0395     if (!matchType(c->windowType())) {
0396         return false;
0397     }
0398     if (!matchWMClass(c->resourceClass(), c->resourceName())) {
0399         return false;
0400     }
0401     if (!matchRole(c->windowRole())) {
0402         return false;
0403     }
0404     if (!matchClientMachine(c->clientMachine()->hostName(), c->clientMachine()->isLocal())) {
0405         return false;
0406     }
0407     if (titlematch != UnimportantMatch) { // track title changes to rematch rules
0408         QObject::connect(c, &Window::captionNormalChanged, c, &Window::evaluateWindowRules, Qt::UniqueConnection);
0409     }
0410     if (!matchTitle(c->captionNormal())) {
0411         return false;
0412     }
0413     return true;
0414 }
0415 
0416 #define NOW_REMEMBER(_T_, _V_) ((selection & _T_) && (_V_##rule == (SetRule)Remember))
0417 
0418 bool Rules::update(Window *c, int selection)
0419 {
0420     // TODO check this setting is for this client ?
0421     bool updated = false;
0422     if NOW_REMEMBER (Position, position) {
0423         if (!c->isFullScreen()) {
0424             QPoint new_pos = position;
0425             // don't use the position in the direction which is maximized
0426             if ((c->maximizeMode() & MaximizeHorizontal) == 0) {
0427                 new_pos.setX(c->pos().x());
0428             }
0429             if ((c->maximizeMode() & MaximizeVertical) == 0) {
0430                 new_pos.setY(c->pos().y());
0431             }
0432             updated = updated || position != new_pos;
0433             position = new_pos;
0434         }
0435     }
0436     if NOW_REMEMBER (Size, size) {
0437         if (!c->isFullScreen()) {
0438             QSize new_size = size;
0439             // don't use the position in the direction which is maximized
0440             if ((c->maximizeMode() & MaximizeHorizontal) == 0) {
0441                 new_size.setWidth(c->size().width());
0442             }
0443             if ((c->maximizeMode() & MaximizeVertical) == 0) {
0444                 new_size.setHeight(c->size().height());
0445             }
0446             updated = updated || size != new_size;
0447             size = new_size;
0448         }
0449     }
0450     if NOW_REMEMBER (Desktops, desktops) {
0451         updated = updated || desktops != c->desktopIds();
0452         desktops = c->desktopIds();
0453     }
0454     if NOW_REMEMBER (Screen, screen) {
0455         const int index = workspace()->outputs().indexOf(c->output());
0456         updated = updated || screen != index;
0457         screen = index;
0458     }
0459     if NOW_REMEMBER (Activity, activity) {
0460         updated = updated || activity != c->activities();
0461         activity = c->activities();
0462     }
0463     if NOW_REMEMBER (MaximizeVert, maximizevert) {
0464         updated = updated || maximizevert != bool(c->maximizeMode() & MaximizeVertical);
0465         maximizevert = c->maximizeMode() & MaximizeVertical;
0466     }
0467     if NOW_REMEMBER (MaximizeHoriz, maximizehoriz) {
0468         updated = updated || maximizehoriz != bool(c->maximizeMode() & MaximizeHorizontal);
0469         maximizehoriz = c->maximizeMode() & MaximizeHorizontal;
0470     }
0471     if NOW_REMEMBER (Minimize, minimize) {
0472         updated = updated || minimize != c->isMinimized();
0473         minimize = c->isMinimized();
0474     }
0475     if NOW_REMEMBER (Shade, shade) {
0476         updated = updated || (shade != (c->shadeMode() != ShadeNone));
0477         shade = c->shadeMode() != ShadeNone;
0478     }
0479     if NOW_REMEMBER (SkipTaskbar, skiptaskbar) {
0480         updated = updated || skiptaskbar != c->skipTaskbar();
0481         skiptaskbar = c->skipTaskbar();
0482     }
0483     if NOW_REMEMBER (SkipPager, skippager) {
0484         updated = updated || skippager != c->skipPager();
0485         skippager = c->skipPager();
0486     }
0487     if NOW_REMEMBER (SkipSwitcher, skipswitcher) {
0488         updated = updated || skipswitcher != c->skipSwitcher();
0489         skipswitcher = c->skipSwitcher();
0490     }
0491     if NOW_REMEMBER (Above, above) {
0492         updated = updated || above != c->keepAbove();
0493         above = c->keepAbove();
0494     }
0495     if NOW_REMEMBER (Below, below) {
0496         updated = updated || below != c->keepBelow();
0497         below = c->keepBelow();
0498     }
0499     if NOW_REMEMBER (Fullscreen, fullscreen) {
0500         updated = updated || fullscreen != c->isFullScreen();
0501         fullscreen = c->isFullScreen();
0502     }
0503     if NOW_REMEMBER (NoBorder, noborder) {
0504         updated = updated || noborder != c->noBorder();
0505         noborder = c->noBorder();
0506     }
0507     if NOW_REMEMBER (DesktopFile, desktopfile) {
0508         updated = updated || desktopfile != c->desktopFileName();
0509         desktopfile = c->desktopFileName();
0510     }
0511     return updated;
0512 }
0513 
0514 #undef NOW_REMEMBER
0515 
0516 #define APPLY_RULE(var, name, type)                     \
0517     bool Rules::apply##name(type &arg, bool init) const \
0518     {                                                   \
0519         if (checkSetRule(var##rule, init))              \
0520             arg = this->var;                            \
0521         return checkSetStop(var##rule);                 \
0522     }
0523 
0524 #define APPLY_FORCE_RULE(var, name, type)    \
0525     bool Rules::apply##name(type &arg) const \
0526     {                                        \
0527         if (checkForceRule(var##rule))       \
0528             arg = this->var;                 \
0529         return checkForceStop(var##rule);    \
0530     }
0531 
0532 APPLY_FORCE_RULE(placement, Placement, PlacementPolicy)
0533 
0534 bool Rules::applyGeometry(QRectF &rect, bool init) const
0535 {
0536     QPointF p = rect.topLeft();
0537     QSizeF s = rect.size();
0538     bool ret = false; // no short-circuiting
0539     if (applyPosition(p, init)) {
0540         rect.moveTopLeft(p);
0541         ret = true;
0542     }
0543     if (applySize(s, init)) {
0544         rect.setSize(s);
0545         ret = true;
0546     }
0547     return ret;
0548 }
0549 
0550 bool Rules::applyPosition(QPointF &pos, bool init) const
0551 {
0552     if (this->position != invalidPoint && checkSetRule(positionrule, init)) {
0553         pos = this->position;
0554     }
0555     return checkSetStop(positionrule);
0556 }
0557 
0558 bool Rules::applySize(QSizeF &s, bool init) const
0559 {
0560     if (this->size.isValid() && checkSetRule(sizerule, init)) {
0561         s = this->size;
0562     }
0563     return checkSetStop(sizerule);
0564 }
0565 
0566 APPLY_FORCE_RULE(minsize, MinSize, QSizeF)
0567 APPLY_FORCE_RULE(maxsize, MaxSize, QSizeF)
0568 APPLY_FORCE_RULE(opacityactive, OpacityActive, int)
0569 APPLY_FORCE_RULE(opacityinactive, OpacityInactive, int)
0570 APPLY_RULE(ignoregeometry, IgnoreGeometry, bool)
0571 
0572 APPLY_RULE(screen, Screen, int)
0573 APPLY_RULE(activity, Activity, QStringList)
0574 APPLY_FORCE_RULE(layer, Layer, enum Layer)
0575 
0576 bool Rules::applyDesktops(QList<VirtualDesktop *> &vds, bool init) const
0577 {
0578     if (checkSetRule(desktopsrule, init)) {
0579         vds.clear();
0580         for (auto id : desktops) {
0581             if (auto vd = VirtualDesktopManager::self()->desktopForId(id)) {
0582                 vds << vd;
0583             }
0584         }
0585     }
0586     return checkSetStop(desktopsrule);
0587 }
0588 
0589 bool Rules::applyMaximizeHoriz(MaximizeMode &mode, bool init) const
0590 {
0591     if (checkSetRule(maximizehorizrule, init)) {
0592         mode = static_cast<MaximizeMode>((maximizehoriz ? MaximizeHorizontal : 0) | (mode & MaximizeVertical));
0593     }
0594     return checkSetStop(maximizehorizrule);
0595 }
0596 
0597 bool Rules::applyMaximizeVert(MaximizeMode &mode, bool init) const
0598 {
0599     if (checkSetRule(maximizevertrule, init)) {
0600         mode = static_cast<MaximizeMode>((maximizevert ? MaximizeVertical : 0) | (mode & MaximizeHorizontal));
0601     }
0602     return checkSetStop(maximizevertrule);
0603 }
0604 
0605 APPLY_RULE(minimize, Minimize, bool)
0606 
0607 bool Rules::applyShade(ShadeMode &sh, bool init) const
0608 {
0609     if (checkSetRule(shaderule, init)) {
0610         if (!this->shade) {
0611             sh = ShadeNone;
0612         }
0613         if (this->shade && sh == ShadeNone) {
0614             sh = ShadeNormal;
0615         }
0616     }
0617     return checkSetStop(shaderule);
0618 }
0619 
0620 APPLY_RULE(skiptaskbar, SkipTaskbar, bool)
0621 APPLY_RULE(skippager, SkipPager, bool)
0622 APPLY_RULE(skipswitcher, SkipSwitcher, bool)
0623 APPLY_RULE(above, KeepAbove, bool)
0624 APPLY_RULE(below, KeepBelow, bool)
0625 APPLY_RULE(fullscreen, FullScreen, bool)
0626 APPLY_RULE(noborder, NoBorder, bool)
0627 APPLY_FORCE_RULE(decocolor, DecoColor, QString)
0628 APPLY_FORCE_RULE(blockcompositing, BlockCompositing, bool)
0629 APPLY_FORCE_RULE(fsplevel, FSP, int)
0630 APPLY_FORCE_RULE(fpplevel, FPP, int)
0631 APPLY_FORCE_RULE(acceptfocus, AcceptFocus, bool)
0632 APPLY_FORCE_RULE(closeable, Closeable, bool)
0633 APPLY_FORCE_RULE(autogroup, Autogrouping, bool)
0634 APPLY_FORCE_RULE(autogroupfg, AutogroupInForeground, bool)
0635 APPLY_FORCE_RULE(autogroupid, AutogroupById, QString)
0636 APPLY_FORCE_RULE(strictgeometry, StrictGeometry, bool)
0637 APPLY_RULE(shortcut, Shortcut, QString)
0638 APPLY_FORCE_RULE(disableglobalshortcuts, DisableGlobalShortcuts, bool)
0639 APPLY_RULE(desktopfile, DesktopFile, QString)
0640 
0641 #undef APPLY_RULE
0642 #undef APPLY_FORCE_RULE
0643 
0644 #define DISCARD_USED_SET_RULE(var)                                                                     \
0645     do {                                                                                               \
0646         if (var##rule == (SetRule)ApplyNow || (withdrawn && var##rule == (SetRule)ForceTemporarily)) { \
0647             var##rule = UnusedSetRule;                                                                 \
0648             changed = true;                                                                            \
0649         }                                                                                              \
0650     } while (false)
0651 #define DISCARD_USED_FORCE_RULE(var)                                 \
0652     do {                                                             \
0653         if (withdrawn && var##rule == (ForceRule)ForceTemporarily) { \
0654             var##rule = UnusedForceRule;                             \
0655             changed = true;                                          \
0656         }                                                            \
0657     } while (false)
0658 
0659 bool Rules::discardUsed(bool withdrawn)
0660 {
0661     bool changed = false;
0662     DISCARD_USED_FORCE_RULE(placement);
0663     DISCARD_USED_SET_RULE(position);
0664     DISCARD_USED_SET_RULE(size);
0665     DISCARD_USED_FORCE_RULE(minsize);
0666     DISCARD_USED_FORCE_RULE(maxsize);
0667     DISCARD_USED_FORCE_RULE(opacityactive);
0668     DISCARD_USED_FORCE_RULE(opacityinactive);
0669     DISCARD_USED_SET_RULE(ignoregeometry);
0670     DISCARD_USED_SET_RULE(desktops);
0671     DISCARD_USED_SET_RULE(screen);
0672     DISCARD_USED_SET_RULE(activity);
0673     DISCARD_USED_SET_RULE(maximizevert);
0674     DISCARD_USED_SET_RULE(maximizehoriz);
0675     DISCARD_USED_SET_RULE(minimize);
0676     DISCARD_USED_SET_RULE(shade);
0677     DISCARD_USED_SET_RULE(skiptaskbar);
0678     DISCARD_USED_SET_RULE(skippager);
0679     DISCARD_USED_SET_RULE(skipswitcher);
0680     DISCARD_USED_SET_RULE(above);
0681     DISCARD_USED_SET_RULE(below);
0682     DISCARD_USED_SET_RULE(fullscreen);
0683     DISCARD_USED_SET_RULE(noborder);
0684     DISCARD_USED_FORCE_RULE(decocolor);
0685     DISCARD_USED_FORCE_RULE(blockcompositing);
0686     DISCARD_USED_FORCE_RULE(fsplevel);
0687     DISCARD_USED_FORCE_RULE(fpplevel);
0688     DISCARD_USED_FORCE_RULE(acceptfocus);
0689     DISCARD_USED_FORCE_RULE(closeable);
0690     DISCARD_USED_FORCE_RULE(autogroup);
0691     DISCARD_USED_FORCE_RULE(autogroupfg);
0692     DISCARD_USED_FORCE_RULE(autogroupid);
0693     DISCARD_USED_FORCE_RULE(strictgeometry);
0694     DISCARD_USED_SET_RULE(shortcut);
0695     DISCARD_USED_FORCE_RULE(disableglobalshortcuts);
0696     DISCARD_USED_SET_RULE(desktopfile);
0697     DISCARD_USED_FORCE_RULE(layer);
0698 
0699     return changed;
0700 }
0701 #undef DISCARD_USED_SET_RULE
0702 #undef DISCARD_USED_FORCE_RULE
0703 
0704 #endif
0705 
0706 QDebug &operator<<(QDebug &stream, const Rules *r)
0707 {
0708     return stream << "[" << r->description << ":" << r->wmclass << "]";
0709 }
0710 
0711 #ifndef KCMRULES
0712 
0713 void WindowRules::update(Window *c, int selection)
0714 {
0715     bool updated = false;
0716     for (QList<Rules *>::ConstIterator it = rules.constBegin();
0717          it != rules.constEnd();
0718          ++it) {
0719         if ((*it)->update(c, selection)) { // no short-circuiting here
0720             updated = true;
0721         }
0722     }
0723     if (updated) {
0724         workspace()->rulebook()->requestDiskStorage();
0725     }
0726 }
0727 
0728 #define CHECK_RULE(rule, type)                                      \
0729     type WindowRules::check##rule(type arg, bool init) const        \
0730     {                                                               \
0731         if (rules.count() == 0)                                     \
0732             return arg;                                             \
0733         type ret = arg;                                             \
0734         for (QList<Rules *>::ConstIterator it = rules.constBegin(); \
0735              it != rules.constEnd();                                \
0736              ++it) {                                                \
0737             if ((*it)->apply##rule(ret, init))                      \
0738                 break;                                              \
0739         }                                                           \
0740         return ret;                                                 \
0741     }
0742 
0743 #define CHECK_FORCE_RULE(rule, type)                           \
0744     type WindowRules::check##rule(type arg) const              \
0745     {                                                          \
0746         if (rules.count() == 0)                                \
0747             return arg;                                        \
0748         type ret = arg;                                        \
0749         for (QList<Rules *>::ConstIterator it = rules.begin(); \
0750              it != rules.end();                                \
0751              ++it) {                                           \
0752             if ((*it)->apply##rule(ret))                       \
0753                 break;                                         \
0754         }                                                      \
0755         return ret;                                            \
0756     }
0757 
0758 CHECK_FORCE_RULE(Placement, PlacementPolicy)
0759 
0760 QRectF WindowRules::checkGeometry(QRectF rect, bool init) const
0761 {
0762     return QRectF(checkPosition(rect.topLeft(), init), checkSize(rect.size(), init));
0763 }
0764 
0765 QRectF WindowRules::checkGeometrySafe(QRectF rect, bool init) const
0766 {
0767     return QRectF(checkPositionSafe(rect.topLeft(), init), checkSize(rect.size(), init));
0768 }
0769 
0770 QPointF WindowRules::checkPositionSafe(QPointF pos, bool init) const
0771 {
0772     const auto ret = checkPosition(pos, init);
0773     if (ret == pos || ret == invalidPoint) {
0774         return ret;
0775     }
0776     const auto outputs = workspace()->outputs();
0777     const bool inAnyOutput = std::any_of(outputs.begin(), outputs.end(), [ret](const auto output) {
0778         return output->geometryF().contains(ret);
0779     });
0780     if (inAnyOutput) {
0781         return ret;
0782     } else {
0783         return invalidPoint;
0784     }
0785 }
0786 
0787 CHECK_RULE(Position, QPointF)
0788 CHECK_RULE(Size, QSizeF)
0789 CHECK_FORCE_RULE(MinSize, QSizeF)
0790 CHECK_FORCE_RULE(MaxSize, QSizeF)
0791 CHECK_FORCE_RULE(OpacityActive, int)
0792 CHECK_FORCE_RULE(OpacityInactive, int)
0793 CHECK_RULE(IgnoreGeometry, bool)
0794 
0795 CHECK_RULE(Desktops, QList<VirtualDesktop *>)
0796 CHECK_RULE(Activity, QStringList)
0797 CHECK_RULE(MaximizeVert, MaximizeMode)
0798 CHECK_RULE(MaximizeHoriz, MaximizeMode)
0799 
0800 MaximizeMode WindowRules::checkMaximize(MaximizeMode mode, bool init) const
0801 {
0802     bool vert = checkMaximizeVert(mode, init) & MaximizeVertical;
0803     bool horiz = checkMaximizeHoriz(mode, init) & MaximizeHorizontal;
0804     return static_cast<MaximizeMode>((vert ? MaximizeVertical : 0) | (horiz ? MaximizeHorizontal : 0));
0805 }
0806 
0807 Output *WindowRules::checkOutput(Output *output, bool init) const
0808 {
0809     if (rules.isEmpty()) {
0810         return output;
0811     }
0812     int ret = workspace()->outputs().indexOf(output);
0813     for (Rules *rule : rules) {
0814         if (rule->applyScreen(ret, init)) {
0815             break;
0816         }
0817     }
0818     Output *ruleOutput = workspace()->outputs().value(ret);
0819     return ruleOutput ? ruleOutput : output;
0820 }
0821 
0822 CHECK_RULE(Minimize, bool)
0823 CHECK_RULE(Shade, ShadeMode)
0824 CHECK_RULE(SkipTaskbar, bool)
0825 CHECK_RULE(SkipPager, bool)
0826 CHECK_RULE(SkipSwitcher, bool)
0827 CHECK_RULE(KeepAbove, bool)
0828 CHECK_RULE(KeepBelow, bool)
0829 CHECK_RULE(FullScreen, bool)
0830 CHECK_RULE(NoBorder, bool)
0831 CHECK_FORCE_RULE(DecoColor, QString)
0832 CHECK_FORCE_RULE(BlockCompositing, bool)
0833 CHECK_FORCE_RULE(FSP, int)
0834 CHECK_FORCE_RULE(FPP, int)
0835 CHECK_FORCE_RULE(AcceptFocus, bool)
0836 CHECK_FORCE_RULE(Closeable, bool)
0837 CHECK_FORCE_RULE(Autogrouping, bool)
0838 CHECK_FORCE_RULE(AutogroupInForeground, bool)
0839 CHECK_FORCE_RULE(AutogroupById, QString)
0840 CHECK_FORCE_RULE(StrictGeometry, bool)
0841 CHECK_RULE(Shortcut, QString)
0842 CHECK_FORCE_RULE(DisableGlobalShortcuts, bool)
0843 CHECK_RULE(DesktopFile, QString)
0844 CHECK_FORCE_RULE(Layer, Layer)
0845 
0846 #undef CHECK_RULE
0847 #undef CHECK_FORCE_RULE
0848 
0849 RuleBook::RuleBook()
0850     : m_updateTimer(new QTimer(this))
0851     , m_updatesDisabled(false)
0852 {
0853     connect(m_updateTimer, &QTimer::timeout, this, &RuleBook::save);
0854     m_updateTimer->setInterval(1000);
0855     m_updateTimer->setSingleShot(true);
0856 }
0857 
0858 RuleBook::~RuleBook()
0859 {
0860     save();
0861     deleteAll();
0862 }
0863 
0864 void RuleBook::deleteAll()
0865 {
0866     qDeleteAll(m_rules);
0867     m_rules.clear();
0868 }
0869 
0870 WindowRules RuleBook::find(const Window *window) const
0871 {
0872     QList<Rules *> ret;
0873     for (Rules *rule : m_rules) {
0874         if (rule->match(window)) {
0875             qCDebug(KWIN_CORE) << "Rule found:" << rule << ":" << window;
0876             ret.append(rule);
0877         }
0878     }
0879     return WindowRules(ret);
0880 }
0881 
0882 void RuleBook::edit(Window *c, bool whole_app)
0883 {
0884     save();
0885     QStringList args;
0886     args << QStringLiteral("uuid=%1").arg(c->internalId().toString());
0887     if (whole_app) {
0888         args << QStringLiteral("whole-app");
0889     }
0890     QProcess *p = new QProcess(this);
0891     p->setArguments({"kcm_kwinrules", "--args", args.join(QLatin1Char(' '))});
0892     p->setProcessEnvironment(kwinApp()->processStartupEnvironment());
0893     p->setProgram(QStandardPaths::findExecutable("kcmshell6"));
0894     p->setProcessChannelMode(QProcess::MergedChannels);
0895     connect(p, static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished), p, &QProcess::deleteLater);
0896     connect(p, &QProcess::errorOccurred, this, [p](QProcess::ProcessError e) {
0897         if (e == QProcess::FailedToStart) {
0898             qCDebug(KWIN_CORE) << "Failed to start" << p->program();
0899         }
0900     });
0901     p->start();
0902 }
0903 
0904 void RuleBook::load()
0905 {
0906     deleteAll();
0907     if (!m_config) {
0908         m_config = KSharedConfig::openConfig(QStringLiteral("kwinrulesrc"), KConfig::NoGlobals);
0909     } else {
0910         m_config->reparseConfiguration();
0911     }
0912     RuleBookSettings book(m_config);
0913     book.load();
0914     m_rules = book.rules();
0915 }
0916 
0917 void RuleBook::save()
0918 {
0919     m_updateTimer->stop();
0920     if (!m_config) {
0921         qCWarning(KWIN_CORE) << "RuleBook::save invoked without prior invocation of RuleBook::load";
0922         return;
0923     }
0924     RuleBookSettings settings(m_config);
0925     settings.setRules(m_rules);
0926     settings.save();
0927 }
0928 
0929 void RuleBook::discardUsed(Window *c, bool withdrawn)
0930 {
0931     bool updated = false;
0932     for (QList<Rules *>::Iterator it = m_rules.begin();
0933          it != m_rules.end();) {
0934         if (c->rules()->contains(*it)) {
0935             if ((*it)->discardUsed(withdrawn)) {
0936                 updated = true;
0937             }
0938             if ((*it)->isEmpty()) {
0939                 c->removeRule(*it);
0940                 Rules *r = *it;
0941                 it = m_rules.erase(it);
0942                 delete r;
0943                 continue;
0944             }
0945         }
0946         ++it;
0947     }
0948     if (updated) {
0949         requestDiskStorage();
0950     }
0951 }
0952 
0953 void RuleBook::requestDiskStorage()
0954 {
0955     m_updateTimer->start();
0956 }
0957 
0958 void RuleBook::setUpdatesDisabled(bool disable)
0959 {
0960     m_updatesDisabled = disable;
0961     if (!disable) {
0962         const auto windows = Workspace::self()->windows();
0963         for (Window *window : windows) {
0964             if (window->supportsWindowRules()) {
0965                 window->updateWindowRules(Rules::All);
0966             }
0967         }
0968     }
0969 }
0970 
0971 #endif
0972 
0973 } // namespace
0974 
0975 #include "moc_rules.cpp"