File indexing completed on 2024-11-10 04:57:55
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"