KWin
Loading...
Searching...
No Matches
rules.cpp
Go to the documentation of this file.
1/*
2 KWin - the KDE window manager
3 This file is part of the KDE project.
4
5 SPDX-FileCopyrightText: 2004 Lubos Lunak <l.lunak@kde.org>
6
7 SPDX-License-Identifier: GPL-2.0-or-later
8*/
9
10#include "rules.h"
11
12#include <QDebug>
13#include <QDir>
14#include <QFile>
15#include <QFileInfo>
16#include <QRegularExpression>
17#include <QTemporaryFile>
18#include <kconfig.h>
19
20#ifndef KCMRULES
21#include "client_machine.h"
22#include "main.h"
23#include "virtualdesktops.h"
24#include "window.h"
25#endif
26
27#include "core/output.h"
28#include "rulebooksettings.h"
29#include "rulesettings.h"
30#include "workspace.h"
31
32namespace KWin
33{
34
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)
79{
80}
81
82#define READ_MATCH_STRING(var, func) \
83 var = settings->var() func; \
84 var##match = static_cast<StringMatch>(settings->var##match())
85
86#define READ_SET_RULE(var) \
87 var = settings->var(); \
88 var##rule = static_cast<SetRule>(settings->var##rule())
89
90#define READ_FORCE_RULE(var, func) \
91 var = func(settings->var()); \
92 var##rule = convertForceRule(settings->var##rule())
93
94Rules::Rules(const RuleSettings *settings)
95{
96 readFromSettings(settings);
97}
98
99void Rules::readFromSettings(const RuleSettings *settings)
100{
101 description = settings->description();
102 if (description.isEmpty()) {
103 description = settings->descriptionLegacy();
104 }
105 READ_MATCH_STRING(wmclass, );
106 wmclasscomplete = settings->wmclasscomplete();
107 READ_MATCH_STRING(windowrole, );
108 READ_MATCH_STRING(title, );
109 READ_MATCH_STRING(clientmachine, .toLower());
110 types = NET::WindowTypeMask(settings->types());
111 READ_FORCE_RULE(placement, );
112 READ_SET_RULE(position);
113 READ_SET_RULE(size);
114 if (size.isEmpty() && sizerule != static_cast<SetRule>(Remember)) {
115 sizerule = UnusedSetRule;
116 }
117 READ_FORCE_RULE(minsize, );
118 if (!minsize.isValid()) {
119 minsize = QSize(1, 1);
120 }
121 READ_FORCE_RULE(maxsize, );
122 if (maxsize.isEmpty()) {
123 maxsize = QSize(32767, 32767);
124 }
125 READ_FORCE_RULE(opacityactive, );
126 READ_FORCE_RULE(opacityinactive, );
127 READ_SET_RULE(ignoregeometry);
128 READ_SET_RULE(desktops);
129 READ_SET_RULE(screen);
130 READ_SET_RULE(activity);
131 READ_SET_RULE(maximizevert);
132 READ_SET_RULE(maximizehoriz);
133 READ_SET_RULE(minimize);
134 READ_SET_RULE(shade);
135 READ_SET_RULE(skiptaskbar);
136 READ_SET_RULE(skippager);
137 READ_SET_RULE(skipswitcher);
138 READ_SET_RULE(above);
139 READ_SET_RULE(below);
140 READ_SET_RULE(fullscreen);
141 READ_SET_RULE(noborder);
142
143 READ_FORCE_RULE(decocolor, getDecoColor);
144 if (decocolor.isEmpty()) {
145 decocolorrule = UnusedForceRule;
146 }
147
148 READ_FORCE_RULE(blockcompositing, );
149 READ_FORCE_RULE(fsplevel, );
150 READ_FORCE_RULE(fpplevel, );
151 READ_FORCE_RULE(acceptfocus, );
152 READ_FORCE_RULE(closeable, );
153 READ_FORCE_RULE(autogroup, );
154 READ_FORCE_RULE(autogroupfg, );
155 READ_FORCE_RULE(autogroupid, );
156 READ_FORCE_RULE(strictgeometry, );
157 READ_SET_RULE(shortcut);
158 READ_FORCE_RULE(disableglobalshortcuts, );
159 READ_SET_RULE(desktopfile);
160 READ_FORCE_RULE(layer, );
161 READ_FORCE_RULE(adaptivesync, );
162}
163
164#undef READ_MATCH_STRING
165#undef READ_SET_RULE
166#undef READ_FORCE_RULE
167#undef READ_FORCE_RULE2
168
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); \
173 }
174
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)); \
179 }
180
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)); \
185 }
186
187void Rules::write(RuleSettings *settings) const
188{
189 settings->setDescription(description);
190 // always write wmclass
191 WRITE_MATCH_STRING(wmclass, Wmclass, true);
192 settings->setWmclasscomplete(wmclasscomplete);
193 WRITE_MATCH_STRING(windowrole, Windowrole, false);
194 WRITE_MATCH_STRING(title, Title, false);
195 WRITE_MATCH_STRING(clientmachine, Clientmachine, false);
196 settings->setTypes(types);
197 WRITE_FORCE_RULE(placement, Placement, );
198 WRITE_SET_RULE(position, Position, );
199 WRITE_SET_RULE(size, Size, );
200 WRITE_FORCE_RULE(minsize, Minsize, );
201 WRITE_FORCE_RULE(maxsize, Maxsize, );
202 WRITE_FORCE_RULE(opacityactive, Opacityactive, );
203 WRITE_FORCE_RULE(opacityinactive, Opacityinactive, );
204 WRITE_SET_RULE(ignoregeometry, Ignoregeometry, );
205 WRITE_SET_RULE(desktops, Desktops, );
206 WRITE_SET_RULE(screen, Screen, );
207 WRITE_SET_RULE(activity, Activity, );
208 WRITE_SET_RULE(maximizevert, Maximizevert, );
209 WRITE_SET_RULE(maximizehoriz, Maximizehoriz, );
210 WRITE_SET_RULE(minimize, Minimize, );
211 WRITE_SET_RULE(shade, Shade, );
212 WRITE_SET_RULE(skiptaskbar, Skiptaskbar, );
213 WRITE_SET_RULE(skippager, Skippager, );
214 WRITE_SET_RULE(skipswitcher, Skipswitcher, );
215 WRITE_SET_RULE(above, Above, );
216 WRITE_SET_RULE(below, Below, );
217 WRITE_SET_RULE(fullscreen, Fullscreen, );
218 WRITE_SET_RULE(noborder, Noborder, );
219 auto colorToString = [](const QString &value) -> QString {
220 if (value.endsWith(QLatin1String(".colors"))) {
221 return QFileInfo(value).baseName();
222 } else {
223 return value;
224 }
225 };
226 WRITE_FORCE_RULE(decocolor, Decocolor, colorToString);
227 WRITE_FORCE_RULE(blockcompositing, Blockcompositing, );
228 WRITE_FORCE_RULE(fsplevel, Fsplevel, );
229 WRITE_FORCE_RULE(fpplevel, Fpplevel, );
230 WRITE_FORCE_RULE(acceptfocus, Acceptfocus, );
231 WRITE_FORCE_RULE(closeable, Closeable, );
232 WRITE_FORCE_RULE(autogroup, Autogroup, );
233 WRITE_FORCE_RULE(autogroupfg, Autogroupfg, );
234 WRITE_FORCE_RULE(autogroupid, Autogroupid, );
235 WRITE_FORCE_RULE(strictgeometry, Strictgeometry, );
236 WRITE_SET_RULE(shortcut, Shortcut, );
237 WRITE_FORCE_RULE(disableglobalshortcuts, Disableglobalshortcuts, );
238 WRITE_SET_RULE(desktopfile, Desktopfile, );
239 WRITE_FORCE_RULE(layer, Layer, );
240 WRITE_FORCE_RULE(adaptivesync, Adaptivesync, );
241}
242
243#undef WRITE_MATCH_STRING
244#undef WRITE_SET_RULE
245#undef WRITE_FORCE_RULE
246
247// returns true if it doesn't affect anything
248bool Rules::isEmpty() const
249{
250 return (placementrule == UnusedForceRule
251 && positionrule == UnusedSetRule
252 && sizerule == UnusedSetRule
253 && minsizerule == UnusedForceRule
254 && maxsizerule == UnusedForceRule
255 && opacityactiverule == UnusedForceRule
256 && opacityinactiverule == UnusedForceRule
257 && ignoregeometryrule == UnusedSetRule
258 && desktopsrule == UnusedSetRule
259 && screenrule == UnusedSetRule
260 && activityrule == UnusedSetRule
261 && maximizevertrule == UnusedSetRule
262 && maximizehorizrule == UnusedSetRule
263 && minimizerule == UnusedSetRule
264 && shaderule == UnusedSetRule
265 && skiptaskbarrule == UnusedSetRule
266 && skippagerrule == UnusedSetRule
267 && skipswitcherrule == UnusedSetRule
268 && aboverule == UnusedSetRule
269 && belowrule == UnusedSetRule
270 && fullscreenrule == UnusedSetRule
271 && noborderrule == UnusedSetRule
272 && decocolorrule == UnusedForceRule
273 && blockcompositingrule == UnusedForceRule
274 && fsplevelrule == UnusedForceRule
275 && fpplevelrule == UnusedForceRule
276 && acceptfocusrule == UnusedForceRule
277 && closeablerule == UnusedForceRule
278 && autogrouprule == UnusedForceRule
279 && autogroupfgrule == UnusedForceRule
280 && autogroupidrule == UnusedForceRule
281 && strictgeometryrule == UnusedForceRule
282 && shortcutrule == UnusedSetRule
283 && disableglobalshortcutsrule == UnusedForceRule
284 && desktopfilerule == UnusedSetRule
285 && layerrule == UnusedForceRule
286 && adaptivesyncrule == UnusedForceRule);
287}
288
289Rules::ForceRule Rules::convertForceRule(int v)
290{
291 if (v == DontAffect || v == Force || v == ForceTemporarily) {
292 return static_cast<ForceRule>(v);
293 }
294 return UnusedForceRule;
295}
296
297QString Rules::getDecoColor(const QString &themeName)
298{
299 if (themeName.isEmpty()) {
300 return QString();
301 }
302 // find the actual scheme file
303 return QStandardPaths::locate(QStandardPaths::GenericDataLocation,
304 QLatin1String("color-schemes/") + themeName + QLatin1String(".colors"));
305}
306
307bool Rules::matchType(NET::WindowType match_type) const
308{
309 if (types != NET::AllTypesMask) {
310 if (match_type == NET::Unknown) {
311 match_type = NET::Normal; // NET::Unknown->NET::Normal is only here for matching
312 }
313 if (!NET::typeMatchesMask(match_type, types)) {
314 return false;
315 }
316 }
317 return true;
318}
319
320bool Rules::matchWMClass(const QString &match_class, const QString &match_name) const
321{
322 if (wmclassmatch != UnimportantMatch) {
323 // TODO optimize?
324 QString cwmclass = wmclasscomplete
325 ? match_name + ' ' + match_class
326 : match_class;
327 if (wmclassmatch == RegExpMatch && !QRegularExpression(wmclass).match(cwmclass).hasMatch()) {
328 return false;
329 }
330 if (wmclassmatch == ExactMatch && cwmclass != wmclass) {
331 return false;
332 }
333 if (wmclassmatch == SubstringMatch && !cwmclass.contains(wmclass)) {
334 return false;
335 }
336 }
337 return true;
338}
339
340bool Rules::matchRole(const QString &match_role) const
341{
342 if (windowrolematch != UnimportantMatch) {
343 if (windowrolematch == RegExpMatch && !QRegularExpression(windowrole).match(match_role).hasMatch()) {
344 return false;
345 }
346 if (windowrolematch == ExactMatch && match_role != windowrole) {
347 return false;
348 }
349 if (windowrolematch == SubstringMatch && !match_role.contains(windowrole)) {
350 return false;
351 }
352 }
353 return true;
354}
355
356bool Rules::matchTitle(const QString &match_title) const
357{
358 if (titlematch != UnimportantMatch) {
359 if (titlematch == RegExpMatch && !QRegularExpression(title).match(match_title).hasMatch()) {
360 return false;
361 }
362 if (titlematch == ExactMatch && title != match_title) {
363 return false;
364 }
365 if (titlematch == SubstringMatch && !match_title.contains(title)) {
366 return false;
367 }
368 }
369 return true;
370}
371
372bool Rules::matchClientMachine(const QString &match_machine, bool local) const
373{
374 if (clientmachinematch != UnimportantMatch) {
375 // if it's localhost, check also "localhost" before checking hostname
376 if (match_machine != "localhost" && local
377 && matchClientMachine("localhost", true)) {
378 return true;
379 }
380 if (clientmachinematch == RegExpMatch
381 && !QRegularExpression(clientmachine).match(match_machine).hasMatch()) {
382 return false;
383 }
384 if (clientmachinematch == ExactMatch
385 && clientmachine != match_machine) {
386 return false;
387 }
388 if (clientmachinematch == SubstringMatch
389 && !match_machine.contains(clientmachine)) {
390 return false;
391 }
392 }
393 return true;
394}
395
396#ifndef KCMRULES
397bool Rules::match(const Window *c) const
398{
399 if (!matchType(c->windowType())) {
400 return false;
401 }
402 if (!matchWMClass(c->resourceClass(), c->resourceName())) {
403 return false;
404 }
405 if (!matchRole(c->windowRole())) {
406 return false;
407 }
408 if (!matchClientMachine(c->clientMachine()->hostName(), c->clientMachine()->isLocal())) {
409 return false;
410 }
411 if (titlematch != UnimportantMatch) { // track title changes to rematch rules
412 QObject::connect(c, &Window::captionNormalChanged, c, &Window::evaluateWindowRules, Qt::UniqueConnection);
413 }
414 if (!matchTitle(c->captionNormal())) {
415 return false;
416 }
417 return true;
418}
419
420#define NOW_REMEMBER(_T_, _V_) ((selection & _T_) && (_V_##rule == (SetRule)Remember))
421
422bool Rules::update(Window *c, int selection)
423{
424 // TODO check this setting is for this client ?
425 bool updated = false;
426 if NOW_REMEMBER (Position, position) {
427 if (!c->isFullScreen()) {
428 QPoint new_pos = position;
429 // don't use the position in the direction which is maximized
430 if ((c->maximizeMode() & MaximizeHorizontal) == 0) {
431 new_pos.setX(c->pos().x());
432 }
433 if ((c->maximizeMode() & MaximizeVertical) == 0) {
434 new_pos.setY(c->pos().y());
435 }
436 updated = updated || position != new_pos;
437 position = new_pos;
438 }
439 }
440 if NOW_REMEMBER (Size, size) {
441 if (!c->isFullScreen()) {
442 QSize new_size = size;
443 // don't use the position in the direction which is maximized
444 if ((c->maximizeMode() & MaximizeHorizontal) == 0) {
445 new_size.setWidth(c->size().width());
446 }
447 if ((c->maximizeMode() & MaximizeVertical) == 0) {
448 new_size.setHeight(c->size().height());
449 }
450 updated = updated || size != new_size;
451 size = new_size;
452 }
453 }
454 if NOW_REMEMBER (Desktops, desktops) {
455 updated = updated || desktops != c->desktopIds();
456 desktops = c->desktopIds();
457 }
458 if NOW_REMEMBER (Screen, screen) {
459 const int index = workspace()->outputs().indexOf(c->output());
460 updated = updated || screen != index;
461 screen = index;
462 }
463 if NOW_REMEMBER (Activity, activity) {
464 updated = updated || activity != c->activities();
465 activity = c->activities();
466 }
467 if NOW_REMEMBER (MaximizeVert, maximizevert) {
468 updated = updated || maximizevert != bool(c->maximizeMode() & MaximizeVertical);
469 maximizevert = c->maximizeMode() & MaximizeVertical;
470 }
471 if NOW_REMEMBER (MaximizeHoriz, maximizehoriz) {
472 updated = updated || maximizehoriz != bool(c->maximizeMode() & MaximizeHorizontal);
473 maximizehoriz = c->maximizeMode() & MaximizeHorizontal;
474 }
475 if NOW_REMEMBER (Minimize, minimize) {
476 updated = updated || minimize != c->isMinimized();
477 minimize = c->isMinimized();
478 }
479 if NOW_REMEMBER (Shade, shade) {
480 updated = updated || (shade != (c->shadeMode() != ShadeNone));
481 shade = c->shadeMode() != ShadeNone;
482 }
483 if NOW_REMEMBER (SkipTaskbar, skiptaskbar) {
484 updated = updated || skiptaskbar != c->skipTaskbar();
485 skiptaskbar = c->skipTaskbar();
486 }
487 if NOW_REMEMBER (SkipPager, skippager) {
488 updated = updated || skippager != c->skipPager();
489 skippager = c->skipPager();
490 }
491 if NOW_REMEMBER (SkipSwitcher, skipswitcher) {
492 updated = updated || skipswitcher != c->skipSwitcher();
493 skipswitcher = c->skipSwitcher();
494 }
495 if NOW_REMEMBER (Above, above) {
496 updated = updated || above != c->keepAbove();
497 above = c->keepAbove();
498 }
499 if NOW_REMEMBER (Below, below) {
500 updated = updated || below != c->keepBelow();
501 below = c->keepBelow();
502 }
503 if NOW_REMEMBER (Fullscreen, fullscreen) {
504 updated = updated || fullscreen != c->isFullScreen();
505 fullscreen = c->isFullScreen();
506 }
507 if NOW_REMEMBER (NoBorder, noborder) {
508 updated = updated || noborder != c->noBorder();
509 noborder = c->noBorder();
510 }
511 if NOW_REMEMBER (DesktopFile, desktopfile) {
512 updated = updated || desktopfile != c->desktopFileName();
513 desktopfile = c->desktopFileName();
514 }
515 return updated;
516}
517
518#undef NOW_REMEMBER
519
520#define APPLY_RULE(var, name, type) \
521 bool Rules::apply##name(type &arg, bool init) const \
522 { \
523 if (checkSetRule(var##rule, init)) \
524 arg = this->var; \
525 return checkSetStop(var##rule); \
526 }
527
528#define APPLY_FORCE_RULE(var, name, type) \
529 bool Rules::apply##name(type &arg) const \
530 { \
531 if (checkForceRule(var##rule)) \
532 arg = this->var; \
533 return checkForceStop(var##rule); \
534 }
535
536APPLY_FORCE_RULE(placement, Placement, PlacementPolicy)
537
538bool Rules::applyGeometry(QRectF &rect, bool init) const
539{
540 QPointF p = rect.topLeft();
541 QSizeF s = rect.size();
542 bool ret = false; // no short-circuiting
543 if (applyPosition(p, init)) {
544 rect.moveTopLeft(p);
545 ret = true;
546 }
547 if (applySize(s, init)) {
548 rect.setSize(s);
549 ret = true;
550 }
551 return ret;
552}
553
554bool Rules::applyPosition(QPointF &pos, bool init) const
555{
556 if (this->position != invalidPoint && checkSetRule(positionrule, init)) {
557 pos = this->position;
558 }
559 return checkSetStop(positionrule);
560}
561
562bool Rules::applySize(QSizeF &s, bool init) const
563{
564 if (this->size.isValid() && checkSetRule(sizerule, init)) {
565 s = this->size;
566 }
567 return checkSetStop(sizerule);
568}
569
570APPLY_FORCE_RULE(minsize, MinSize, QSizeF)
571APPLY_FORCE_RULE(maxsize, MaxSize, QSizeF)
572APPLY_FORCE_RULE(opacityactive, OpacityActive, int)
573APPLY_FORCE_RULE(opacityinactive, OpacityInactive, int)
574APPLY_RULE(ignoregeometry, IgnoreGeometry, bool)
575
576APPLY_RULE(screen, Screen, int)
577APPLY_RULE(activity, Activity, QStringList)
578APPLY_FORCE_RULE(layer, Layer, enum Layer)
579
580bool Rules::applyDesktops(QList<VirtualDesktop *> &vds, bool init) const
581{
582 if (checkSetRule(desktopsrule, init)) {
583 vds.clear();
584 for (auto id : desktops) {
585 if (auto vd = VirtualDesktopManager::self()->desktopForId(id)) {
586 vds << vd;
587 }
588 }
589 }
590 return checkSetStop(desktopsrule);
591}
592
593bool Rules::applyMaximizeHoriz(MaximizeMode &mode, bool init) const
594{
595 if (checkSetRule(maximizehorizrule, init)) {
596 mode = static_cast<MaximizeMode>((maximizehoriz ? MaximizeHorizontal : 0) | (mode & MaximizeVertical));
597 }
598 return checkSetStop(maximizehorizrule);
599}
600
601bool Rules::applyMaximizeVert(MaximizeMode &mode, bool init) const
602{
603 if (checkSetRule(maximizevertrule, init)) {
604 mode = static_cast<MaximizeMode>((maximizevert ? MaximizeVertical : 0) | (mode & MaximizeHorizontal));
605 }
606 return checkSetStop(maximizevertrule);
607}
608
609APPLY_RULE(minimize, Minimize, bool)
610
611bool Rules::applyShade(ShadeMode &sh, bool init) const
612{
613 if (checkSetRule(shaderule, init)) {
614 if (!this->shade) {
615 sh = ShadeNone;
616 }
617 if (this->shade && sh == ShadeNone) {
618 sh = ShadeNormal;
619 }
620 }
621 return checkSetStop(shaderule);
622}
623
624APPLY_RULE(skiptaskbar, SkipTaskbar, bool)
625APPLY_RULE(skippager, SkipPager, bool)
626APPLY_RULE(skipswitcher, SkipSwitcher, bool)
627APPLY_RULE(above, KeepAbove, bool)
628APPLY_RULE(below, KeepBelow, bool)
629APPLY_RULE(fullscreen, FullScreen, bool)
630APPLY_RULE(noborder, NoBorder, bool)
631APPLY_FORCE_RULE(decocolor, DecoColor, QString)
632APPLY_FORCE_RULE(blockcompositing, BlockCompositing, bool)
633APPLY_FORCE_RULE(fsplevel, FSP, int)
634APPLY_FORCE_RULE(fpplevel, FPP, int)
635APPLY_FORCE_RULE(acceptfocus, AcceptFocus, bool)
636APPLY_FORCE_RULE(closeable, Closeable, bool)
637APPLY_FORCE_RULE(autogroup, Autogrouping, bool)
638APPLY_FORCE_RULE(autogroupfg, AutogroupInForeground, bool)
639APPLY_FORCE_RULE(autogroupid, AutogroupById, QString)
640APPLY_FORCE_RULE(strictgeometry, StrictGeometry, bool)
641APPLY_RULE(shortcut, Shortcut, QString)
642APPLY_FORCE_RULE(disableglobalshortcuts, DisableGlobalShortcuts, bool)
643APPLY_RULE(desktopfile, DesktopFile, QString)
644APPLY_FORCE_RULE(adaptivesync, AdaptiveSync, bool)
645
646#undef APPLY_RULE
647#undef APPLY_FORCE_RULE
648
649#define DISCARD_USED_SET_RULE(var) \
650 do { \
651 if (var##rule == (SetRule)ApplyNow || (withdrawn && var##rule == (SetRule)ForceTemporarily)) { \
652 var##rule = UnusedSetRule; \
653 changed = true; \
654 } \
655 } while (false)
656#define DISCARD_USED_FORCE_RULE(var) \
657 do { \
658 if (withdrawn && var##rule == (ForceRule)ForceTemporarily) { \
659 var##rule = UnusedForceRule; \
660 changed = true; \
661 } \
662 } while (false)
663
664bool Rules::discardUsed(bool withdrawn)
665{
666 bool changed = false;
667 DISCARD_USED_FORCE_RULE(placement);
668 DISCARD_USED_SET_RULE(position);
672 DISCARD_USED_FORCE_RULE(opacityactive);
673 DISCARD_USED_FORCE_RULE(opacityinactive);
674 DISCARD_USED_SET_RULE(ignoregeometry);
675 DISCARD_USED_SET_RULE(desktops);
676 DISCARD_USED_SET_RULE(screen);
677 DISCARD_USED_SET_RULE(activity);
678 DISCARD_USED_SET_RULE(maximizevert);
679 DISCARD_USED_SET_RULE(maximizehoriz);
680 DISCARD_USED_SET_RULE(minimize);
682 DISCARD_USED_SET_RULE(skiptaskbar);
683 DISCARD_USED_SET_RULE(skippager);
684 DISCARD_USED_SET_RULE(skipswitcher);
687 DISCARD_USED_SET_RULE(fullscreen);
688 DISCARD_USED_SET_RULE(noborder);
689 DISCARD_USED_FORCE_RULE(decocolor);
690 DISCARD_USED_FORCE_RULE(blockcompositing);
691 DISCARD_USED_FORCE_RULE(fsplevel);
692 DISCARD_USED_FORCE_RULE(fpplevel);
693 DISCARD_USED_FORCE_RULE(acceptfocus);
694 DISCARD_USED_FORCE_RULE(closeable);
695 DISCARD_USED_FORCE_RULE(autogroup);
696 DISCARD_USED_FORCE_RULE(autogroupfg);
697 DISCARD_USED_FORCE_RULE(autogroupid);
698 DISCARD_USED_FORCE_RULE(strictgeometry);
699 DISCARD_USED_SET_RULE(shortcut);
700 DISCARD_USED_FORCE_RULE(disableglobalshortcuts);
701 DISCARD_USED_SET_RULE(desktopfile);
703 DISCARD_USED_FORCE_RULE(adaptivesync);
704
705 return changed;
706}
707#undef DISCARD_USED_SET_RULE
708#undef DISCARD_USED_FORCE_RULE
709
710#endif
711
712QDebug &operator<<(QDebug &stream, const Rules *r)
713{
714 return stream << "[" << r->description << ":" << r->wmclass << "]";
715}
716
717#ifndef KCMRULES
718
719void WindowRules::update(Window *c, int selection)
720{
721 bool updated = false;
722 for (QList<Rules *>::ConstIterator it = rules.constBegin();
723 it != rules.constEnd();
724 ++it) {
725 if ((*it)->update(c, selection)) { // no short-circuiting here
726 updated = true;
727 }
728 }
729 if (updated) {
731 }
732}
733
734#define CHECK_RULE(rule, type) \
735 type WindowRules::check##rule(type arg, bool init) const \
736 { \
737 if (rules.count() == 0) \
738 return arg; \
739 type ret = arg; \
740 for (QList<Rules *>::ConstIterator it = rules.constBegin(); \
741 it != rules.constEnd(); \
742 ++it) { \
743 if ((*it)->apply##rule(ret, init)) \
744 break; \
745 } \
746 return ret; \
747 }
748
749#define CHECK_FORCE_RULE(rule, type) \
750 type WindowRules::check##rule(type arg) const \
751 { \
752 if (rules.count() == 0) \
753 return arg; \
754 type ret = arg; \
755 for (QList<Rules *>::ConstIterator it = rules.begin(); \
756 it != rules.end(); \
757 ++it) { \
758 if ((*it)->apply##rule(ret)) \
759 break; \
760 } \
761 return ret; \
762 }
763
765
766QRectF WindowRules::checkGeometry(QRectF rect, bool init) const
767{
768 return QRectF(checkPosition(rect.topLeft(), init), checkSize(rect.size(), init));
769}
770
771QRectF WindowRules::checkGeometrySafe(QRectF rect, bool init) const
772{
773 return QRectF(checkPositionSafe(rect.topLeft(), init), checkSize(rect.size(), init));
774}
775
776QPointF WindowRules::checkPositionSafe(QPointF pos, bool init) const
777{
778 const auto ret = checkPosition(pos, init);
779 if (ret == pos || ret == invalidPoint) {
780 return ret;
781 }
782 const auto outputs = workspace()->outputs();
783 const bool inAnyOutput = std::any_of(outputs.begin(), outputs.end(), [ret](const auto output) {
784 return output->geometryF().contains(ret);
785 });
786 if (inAnyOutput) {
787 return ret;
788 } else {
789 return invalidPoint;
790 }
791}
792
793CHECK_RULE(Position, QPointF)
794CHECK_RULE(Size, QSizeF)
795CHECK_FORCE_RULE(MinSize, QSizeF)
796CHECK_FORCE_RULE(MaxSize, QSizeF)
797CHECK_FORCE_RULE(OpacityActive, int)
798CHECK_FORCE_RULE(OpacityInactive, int)
799CHECK_RULE(IgnoreGeometry, bool)
800
801CHECK_RULE(Desktops, QList<VirtualDesktop *>)
802CHECK_RULE(Activity, QStringList)
803CHECK_RULE(MaximizeVert, MaximizeMode)
804CHECK_RULE(MaximizeHoriz, MaximizeMode)
805
806MaximizeMode WindowRules::checkMaximize(MaximizeMode mode, bool init) const
807{
808 bool vert = checkMaximizeVert(mode, init) & MaximizeVertical;
809 bool horiz = checkMaximizeHoriz(mode, init) & MaximizeHorizontal;
810 return static_cast<MaximizeMode>((vert ? MaximizeVertical : 0) | (horiz ? MaximizeHorizontal : 0));
811}
812
813Output *WindowRules::checkOutput(Output *output, bool init) const
814{
815 if (rules.isEmpty()) {
816 return output;
817 }
818 int ret = workspace()->outputs().indexOf(output);
819 for (Rules *rule : rules) {
820 if (rule->applyScreen(ret, init)) {
821 break;
822 }
823 }
824 Output *ruleOutput = workspace()->outputs().value(ret);
825 return ruleOutput ? ruleOutput : output;
826}
827
828CHECK_RULE(Minimize, bool)
829CHECK_RULE(Shade, ShadeMode)
830CHECK_RULE(SkipTaskbar, bool)
831CHECK_RULE(SkipPager, bool)
832CHECK_RULE(SkipSwitcher, bool)
833CHECK_RULE(KeepAbove, bool)
834CHECK_RULE(KeepBelow, bool)
835CHECK_RULE(FullScreen, bool)
836CHECK_RULE(NoBorder, bool)
837CHECK_FORCE_RULE(DecoColor, QString)
838CHECK_FORCE_RULE(BlockCompositing, bool)
839CHECK_FORCE_RULE(FSP, int)
840CHECK_FORCE_RULE(FPP, int)
841CHECK_FORCE_RULE(AcceptFocus, bool)
842CHECK_FORCE_RULE(Closeable, bool)
843CHECK_FORCE_RULE(Autogrouping, bool)
844CHECK_FORCE_RULE(AutogroupInForeground, bool)
845CHECK_FORCE_RULE(AutogroupById, QString)
846CHECK_FORCE_RULE(StrictGeometry, bool)
847CHECK_RULE(Shortcut, QString)
848CHECK_FORCE_RULE(DisableGlobalShortcuts, bool)
849CHECK_RULE(DesktopFile, QString)
852
853#undef CHECK_RULE
854#undef CHECK_FORCE_RULE
855
857 : m_updateTimer(new QTimer(this))
858 , m_updatesDisabled(false)
859{
860 connect(m_updateTimer, &QTimer::timeout, this, &RuleBook::save);
861 m_updateTimer->setInterval(1000);
862 m_updateTimer->setSingleShot(true);
863}
864
866{
867 save();
868 deleteAll();
869}
870
871void RuleBook::deleteAll()
872{
873 qDeleteAll(m_rules);
874 m_rules.clear();
875}
876
878{
879 QList<Rules *> ret;
880 for (Rules *rule : m_rules) {
881 if (rule->match(window)) {
882 qCDebug(KWIN_CORE) << "Rule found:" << rule << ":" << window;
883 ret.append(rule);
884 }
885 }
886 return WindowRules(ret);
887}
888
889void RuleBook::edit(Window *c, bool whole_app)
890{
891 save();
892 QStringList args;
893 args << QStringLiteral("uuid=%1").arg(c->internalId().toString());
894 if (whole_app) {
895 args << QStringLiteral("whole-app");
896 }
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();
906 }
907 });
908 p->start();
909}
910
912{
913 deleteAll();
914 if (!m_config) {
915 m_config = KSharedConfig::openConfig(QStringLiteral("kwinrulesrc"), KConfig::NoGlobals);
916 } else {
917 m_config->reparseConfiguration();
918 }
919 RuleBookSettings book(m_config);
920 book.load();
921 m_rules = book.rules();
922}
923
924void RuleBook::save()
925{
926 m_updateTimer->stop();
927 if (!m_config) {
928 qCWarning(KWIN_CORE) << "RuleBook::save invoked without prior invocation of RuleBook::load";
929 return;
930 }
931 RuleBookSettings settings(m_config);
932 settings.setRules(m_rules);
933 settings.save();
934}
935
936void RuleBook::discardUsed(Window *c, bool withdrawn)
937{
938 bool updated = false;
939 for (QList<Rules *>::Iterator it = m_rules.begin();
940 it != m_rules.end();) {
941 if (c->rules()->contains(*it)) {
942 if ((*it)->discardUsed(withdrawn)) {
943 updated = true;
944 }
945 if ((*it)->isEmpty()) {
946 c->removeRule(*it);
947 Rules *r = *it;
948 it = m_rules.erase(it);
949 delete r;
950 continue;
951 }
952 }
953 ++it;
954 }
955 if (updated) {
957 }
958}
959
961{
962 m_updateTimer->start();
963}
964
966{
967 m_updatesDisabled = disable;
968 if (!disable) {
969 const auto windows = Workspace::self()->windows();
970 for (Window *window : windows) {
971 if (window->supportsWindowRules()) {
972 window->updateWindowRules(Rules::All);
973 }
974 }
975 }
976}
977
978#endif
979
980} // namespace
981
982#include "moc_rules.cpp"
const QString & hostName() const
~RuleBook() override
Definition rules.cpp:865
void requestDiskStorage()
Definition rules.cpp:960
void setUpdatesDisabled(bool disable)
Definition rules.cpp:965
void load()
Definition rules.cpp:911
void edit(Window *c, bool whole_app)
Definition rules.cpp:889
void discardUsed(Window *c, bool withdraw)
Definition rules.cpp:936
WindowRules find(const Window *window) const
Definition rules.cpp:877
QList< Rules * > rules()
bool applyMaximizeVert(MaximizeMode &mode, bool init) const
Definition rules.cpp:601
bool applyMaximizeHoriz(MaximizeMode &mode, bool init) const
Definition rules.cpp:593
@ ForceTemporarily
Definition rules.h:127
@ DontAffect
Definition rules.h:122
bool applyPosition(QPointF &pos, bool init) const
Definition rules.cpp:554
void write(RuleSettings *) const
Definition rules.cpp:187
@ UnusedSetRule
Definition rules.h:138
bool update(Window *, int selection)
Definition rules.cpp:422
@ SubstringMatch
Definition rules.h:133
@ RegExpMatch
Definition rules.h:134
@ UnimportantMatch
Definition rules.h:131
@ ExactMatch
Definition rules.h:132
@ UnusedForceRule
Definition rules.h:142
bool discardUsed(bool withdrawn)
Definition rules.cpp:664
bool applySize(QSizeF &s, bool init) const
Definition rules.cpp:562
bool isEmpty() const
Definition rules.cpp:248
@ SkipTaskbar
Definition rules.h:103
@ SkipPager
Definition rules.h:104
@ MaximizeHoriz
Definition rules.h:100
@ DesktopFile
Definition rules.h:114
@ Desktops
Definition rules.h:98
@ Fullscreen
Definition rules.h:108
@ MaximizeVert
Definition rules.h:99
@ Position
Definition rules.h:96
@ SkipSwitcher
Definition rules.h:105
bool match(const Window *c) const
Definition rules.cpp:397
QPointF pos
Definition window.h:79
bool skipSwitcher
Definition window.h:320
void removeRule(Rules *r)
Definition window.cpp:4069
ClientMachine * clientMachine() const
Definition window.h:2057
QSizeF size
Definition window.h:84
QString desktopFileName
Definition window.h:501
bool keepBelow
Definition window.h:337
bool noBorder
Definition window.h:456
virtual bool isFullScreen() const
Definition window.cpp:3935
QUuid internalId
Definition window.h:259
bool isMinimized() const
Definition window.h:988
QString resourceClass
Definition window.h:115
virtual QString captionNormal() const =0
QString resourceName
Definition window.h:114
void evaluateWindowRules()
Definition window.cpp:4074
QStringList activities
Definition window.h:305
const WindowRules * rules() const
Definition window.h:1048
ShadeMode shadeMode() const
Definition window.cpp:853
int windowType
Definition window.h:224
QStringList desktopIds() const
Definition window.cpp:830
void captionNormalChanged()
bool skipPager
Definition window.h:315
bool keepAbove
Definition window.h:332
KWin::Output * output
Definition window.h:111
virtual MaximizeMode maximizeMode() const
Definition window.cpp:3987
QString windowRole
Definition window.h:116
bool skipTaskbar
Definition window.h:310
QPointF checkPositionSafe(QPointF pos, bool init=false) const
Definition rules.cpp:776
bool contains(const Rules *rule) const
Definition rules.h:369
Output * checkOutput(Output *output, bool init=false) const
Definition rules.cpp:813
void update(Window *, int selection)
Definition rules.cpp:719
QSizeF checkSize(QSizeF s, bool init=false) const
QRectF checkGeometrySafe(QRectF rect, bool init=false) const
Definition rules.cpp:771
QPointF checkPosition(QPointF pos, bool init=false) const
static Workspace * self()
Definition workspace.h:91
RuleBook * rulebook() const
QList< Output * > outputs() const
Definition workspace.h:762
const QList< Window * > windows() const
Definition workspace.h:248
std::variant< KeyboardShortcut, PointerButtonShortcut, PointerAxisShortcut, RealtimeFeedbackSwipeShortcut, RealtimeFeedbackPinchShortcut > Shortcut
ShadeMode
Definition common.h:62
@ ShadeNone
Definition common.h:63
@ ShadeNormal
Definition common.h:64
QDebug & operator<<(QDebug &s, const KWin::DrmConnector *obj)
const QPoint invalidPoint(INT_MIN, INT_MIN)
MaximizeMode
Definition common.h:74
@ MaximizeVertical
The window is maximized vertically.
Definition common.h:76
@ MaximizeHorizontal
Definition common.h:77
PlacementPolicy
Definition options.h:57
Workspace * workspace()
Definition workspace.h:830
Layer
Definition globals.h:162
#define DISCARD_USED_SET_RULE(var)
Definition rules.cpp:649
#define READ_FORCE_RULE(var, func)
Definition rules.cpp:90
#define CHECK_FORCE_RULE(rule, type)
Definition rules.cpp:749
#define READ_MATCH_STRING(var, func)
Definition rules.cpp:82
#define APPLY_RULE(var, name, type)
Definition rules.cpp:520
#define NOW_REMEMBER(_T_, _V_)
Definition rules.cpp:420
#define APPLY_FORCE_RULE(var, name, type)
Definition rules.cpp:528
#define CHECK_RULE(rule, type)
Definition rules.cpp:734
#define DISCARD_USED_FORCE_RULE(var)
Definition rules.cpp:656
#define READ_SET_RULE(var)
Definition rules.cpp:86
#define WRITE_MATCH_STRING(var, capital, force)
Definition rules.cpp:169
#define WRITE_SET_RULE(var, capital, func)
Definition rules.cpp:175
#define WRITE_FORCE_RULE(var, capital, func)
Definition rules.cpp:181