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)