1 //
2 // AutoProperties.cc for pekwm
3 // Copyright (C) 2003-2020 Claes Nästén <pekdon@gmail.com>
4 //
5 // This program is licensed under the GNU GPL.
6 // See the LICENSE file for more information.
7 //
8
9 #include "config.h"
10
11 #include <algorithm>
12
13 #include "AutoProperties.hh"
14 #include "Charset.hh"
15 #include "Config.hh"
16 #include "Debug.hh"
17 #include "ImageHandler.hh"
18 #include "Util.hh"
19 #include "WmUtil.hh"
20
21 static Util::StringTo<ApplyOn> apply_on_map[] =
22 {{"START", APPLY_ON_START},
23 {"NEW", APPLY_ON_NEW},
24 {"RELOAD", APPLY_ON_RELOAD},
25 {"WORKSPACE", APPLY_ON_WORKSPACE},
26 {"TRANSIENT", APPLY_ON_TRANSIENT},
27 {"TRANSIENTONLY", APPLY_ON_TRANSIENT_ONLY},
28 {nullptr, APPLY_ON_ALWAYS}};
29
30 static Util::StringTo<PropertyType> property_map[] =
31 {{"WORKSPACE", AP_WORKSPACE},
32 {"PROPERTY", AP_PROPERTY},
33 {"STICKY", AP_STICKY},
34 {"SHADED", AP_SHADED},
35 {"MAXIMIZEDVERTICAL", AP_MAXIMIZED_VERTICAL},
36 {"MAXIMIZEDHORIZONTAL", AP_MAXIMIZED_HORIZONTAL},
37 {"ICONIFIED", AP_ICONIFIED},
38 {"BORDER", AP_BORDER},
39 {"TITLEBAR", AP_TITLEBAR},
40 {"FRAMEGEOMETRY", AP_FRAME_GEOMETRY},
41 {"CLIENTGEOMETRY", AP_CLIENT_GEOMETRY},
42 {"LAYER", AP_LAYER},
43 {"SKIP", AP_SKIP},
44 {"FULLSCREEN", AP_FULLSCREEN},
45 {"PLACENEW", AP_PLACE_NEW},
46 {"FOCUSNEW", AP_FOCUS_NEW},
47 {"FOCUSABLE", AP_FOCUSABLE},
48 {"CFGDENY", AP_CFG_DENY},
49 {"ALLOWEDACTIONS", AP_ALLOWED_ACTIONS},
50 {"DISALLOWEDACTIONS", AP_DISALLOWED_ACTIONS},
51 {"OPACITY", AP_OPACITY},
52 {"DECOR", AP_DECOR},
53 {"ICON", AP_ICON},
54 {nullptr, AP_NO_PROPERTY}};
55
56 static Util::StringTo<PropertyType> group_property_map[] =
57 {{"SIZE", AP_GROUP_SIZE},
58 {"BEHIND", AP_GROUP_BEHIND},
59 {"FOCUSEDFIRST", AP_GROUP_FOCUSED_FIRST},
60 {"GLOBAL", AP_GROUP_GLOBAL},
61 {"RAISE", AP_GROUP_RAISE},
62 {nullptr, AP_NO_PROPERTY}};
63
64 static Util::StringTo<AtomName> window_type_map[] =
65 {{"DESKTOP", WINDOW_TYPE_DESKTOP},
66 {"DOCK", WINDOW_TYPE_DOCK},
67 {"TOOLBAR", WINDOW_TYPE_TOOLBAR},
68 {"MENU", WINDOW_TYPE_MENU},
69 {"UTILITY", WINDOW_TYPE_UTILITY},
70 {"SPLASH", WINDOW_TYPE_SPLASH},
71 {"DIALOG", WINDOW_TYPE_DIALOG},
72 {"DROPDOWNMENU", WINDOW_TYPE_DROPDOWN_MENU},
73 {"POPUPMENU", WINDOW_TYPE_POPUP_MENU},
74 {"TOOLTIP", WINDOW_TYPE_TOOLTIP},
75 {"NOTIFICATION", WINDOW_TYPE_NOTIFICATION},
76 {"COMBO", WINDOW_TYPE_COMBO},
77 {"DND", WINDOW_TYPE_DND},
78 {"NORMAL", WINDOW_TYPE_NORMAL},
79 {nullptr, WINDOW_TYPE}};
80
81 std::ostream&
operator <<(std::ostream & os,const ClassHint & ch)82 operator<<(std::ostream& os, const ClassHint &ch)
83 {
84 os << "ClassHint "
85 << Charset::toSystem(ch.h_name) << ","
86 << Charset::toSystem(ch.h_class);
87 return os;
88 }
89
ClassHint(void)90 ClassHint::ClassHint(void)
91 {
92 }
93
ClassHint(const std::string & n_h_name,const std::string & n_h_class,const std::string & n_h_role,const std::string & n_title,const std::string & n_group)94 ClassHint::ClassHint(const std::string &n_h_name,
95 const std::string &n_h_class,
96 const std::string &n_h_role,
97 const std::string &n_title,
98 const std::string &n_group)
99 : h_name(n_h_name),
100 h_class(n_h_class),
101 h_role(n_h_role),
102 title(n_title),
103 group(n_group)
104 {
105 }
106
~ClassHint(void)107 ClassHint::~ClassHint(void)
108 {
109 }
110
111 ClassHint&
operator =(const ClassHint & rhs)112 ClassHint::operator=(const ClassHint& rhs)
113 {
114 h_name = rhs.h_name;
115 h_class = rhs.h_class;
116 h_role = rhs.h_role;
117 title = rhs.title;
118 group = rhs.group;
119 return *this;
120 }
121
122 bool
operator ==(const ClassHint & rhs) const123 ClassHint::operator==(const ClassHint& rhs) const
124 {
125 if (group.size() > 0) {
126 if (group == rhs.group) {
127 return true;
128 }
129 } else if ((h_name == rhs.h_name) && (h_class == rhs.h_class) &&
130 (h_role == rhs.h_role)) {
131 return true;
132 }
133 return false;
134 }
135
Property(void)136 Property::Property(void)
137 : _apply_mask(0)
138 {
139 }
140
~Property(void)141 Property::~Property(void)
142 {
143 }
144
AutoProperty(void)145 AutoProperty::AutoProperty(void)
146 : skip(SKIP_NONE),
147 cfg_deny(0),
148 icon(nullptr),
149 group_size(-1),
150 group_behind(false),
151 group_focused_first(false),
152 group_global(false),
153 group_raise(false),
154 _prop_mask(0)
155 {
156 }
157
~AutoProperty(void)158 AutoProperty::~AutoProperty(void)
159 {
160 if (icon != nullptr) {
161 delete icon;
162 }
163 }
164
165 //! @brief Constructor for AutoProperties class
AutoProperties(ImageHandler * image_handler)166 AutoProperties::AutoProperties(ImageHandler *image_handler)
167 : _image_handler(image_handler),
168 _extended(false),
169 _harbour_sort(false),
170 _apply_on_start(true)
171 {
172 }
173
174 //! @brief Destructor for AutoProperties class
~AutoProperties(void)175 AutoProperties::~AutoProperties(void)
176 {
177 unload();
178 }
179
180 //! @brief Loads the autoprop config file.
181 bool
load(void)182 AutoProperties::load(void)
183 {
184 std::string cfg_file(pekwm::config()->getAutoPropsFile());
185 if (! _cfg_files.requireReload(cfg_file)) {
186 return false;
187 }
188
189 // dealloc memory
190 unload();
191
192 CfgParser a_cfg;
193 if (! a_cfg.parse(cfg_file, CfgParserSource::SOURCE_FILE, false)) {
194 cfg_file = SYSCONFDIR "/autoproperties";
195 if (! a_cfg.parse (cfg_file, CfgParserSource::SOURCE_FILE, false)) {
196 setDefaultTypeProperties();
197 return false;
198 }
199 }
200
201 // Setup template parsing if requested
202 loadRequire(a_cfg, cfg_file);
203
204 if (a_cfg.isDynamicContent()) {
205 _cfg_files.clear();
206 } else {
207 _cfg_files = a_cfg.getCfgFiles();
208 }
209
210 // reset values
211 _apply_on_start = true;
212
213 // set load path for icons while loading auto-properties
214 WithIconPath with_icon_path(pekwm::config(), _image_handler);
215
216 std::vector<std::string> tokens;
217 std::vector<std::string>::iterator token_it;
218 std::vector<uint> workspaces;
219 CfgParser::Entry::entry_cit it(a_cfg.getEntryRoot()->begin());
220 for (; it != a_cfg.getEntryRoot()->end(); ++it) {
221 if (*(*it) == "PROPERTY") {
222 parseAutoProperty(*it, 0);
223 } else if (*(*it) == "TITLERULES") {
224 parseTitleProperty(*it);
225 } else if (*(*it) == "DECORRULES") {
226 parseDecorProperty(*it);
227 } else if (*(*it) == "TYPERULES") {
228 parseTypeProperty(*it);
229 } else if (*(*it) == "HARBOUR") {
230 parseDockAppProperty(*it);
231 } else if (*(*it) == "WORKSPACE") { // Workspace section
232 CfgParser::Entry *workspace = (*it)->getSection();
233 tokens.clear();
234 if (Util::splitString(workspace->getValue(), tokens, " \t")) {
235 workspaces.clear();
236 for (token_it = tokens.begin();
237 token_it != tokens.end();
238 ++token_it)
239 workspaces.push_back(strtol(token_it->c_str(), 0, 10));
240
241 // Get all properties on for these workspaces.
242 CfgParser::Entry::entry_cit workspace_it(workspace->begin());
243 for (; workspace_it != workspace->end(); ++workspace_it) {
244 parseAutoProperty(*workspace_it, &workspaces);
245 }
246 }
247 }
248 }
249
250 // Validate date
251 setDefaultTypeProperties();
252
253 return true;
254 }
255
256 /**
257 * Load autoproperties quirks.
258 */
259 void
loadRequire(CfgParser & a_cfg,std::string & file)260 AutoProperties::loadRequire(CfgParser &a_cfg, std::string &file)
261 {
262 // Look for requires section,
263 CfgParser::Entry *section = a_cfg.getEntryRoot()->findSection("REQUIRE");
264 if (section) {
265 std::vector<CfgParserKey*> keys;
266
267 keys.push_back(new CfgParserKeyBool("TEMPLATES", _extended, false));
268 section->parseKeyValues(keys.begin(), keys.end());
269 for_each(keys.begin(), keys.end(), Util::Free<CfgParserKey*>());
270
271 // Re-load configuration with templates enabled.
272 if (_extended) {
273 a_cfg.clear(true);
274 a_cfg.parse(file, CfgParserSource::SOURCE_FILE, true);
275 }
276 } else {
277 _extended = false;
278 }
279 }
280
281 //! @brief Frees allocated memory
282 void
unload(void)283 AutoProperties::unload(void)
284 {
285 std::vector<Property*>::iterator it;
286
287 // remove auto properties
288 for (it = _prop_list.begin(); it != _prop_list.end(); ++it) {
289 delete *it;
290 }
291 _prop_list.clear();
292
293 // remove title properties
294 for (it = _title_prop_list.begin(); it != _title_prop_list.end(); ++it) {
295 delete *it;
296 }
297 _title_prop_list.clear();
298
299 // remove decor properties
300 for (it = _decor_prop_list.begin(); it != _decor_prop_list.end(); ++it) {
301 delete *it;
302 }
303 _decor_prop_list.clear();
304
305 // remove dock app properties
306 for (it = _dock_app_prop_list.begin();
307 it != _dock_app_prop_list.end();
308 ++it) {
309 delete *it;
310 }
311 _dock_app_prop_list.clear();
312
313 // remove type properties
314 std::map<AtomName, AutoProperty*>::iterator wit;
315 for (wit = _window_type_prop_map.begin();
316 wit != _window_type_prop_map.end();
317 ++wit) {
318 delete wit->second;
319 }
320 _window_type_prop_map.clear();
321 }
322
323 //! @brief Finds a property from the prop_list
324 Property*
findProperty(const ClassHint * class_hint,std::vector<Property * > * prop_list,int ws,ApplyOn type)325 AutoProperties::findProperty(const ClassHint* class_hint,
326 std::vector<Property*>* prop_list,
327 int ws, ApplyOn type)
328 {
329 // Allready remove apply on start
330 if (! _apply_on_start && (type == APPLY_ON_START))
331 return nullptr;
332
333 // start searching for a suitable property
334 std::vector<Property*>::iterator it = prop_list->begin();
335 for (; it != prop_list->end(); ++it) {
336 // see if the type matches, if we have one
337 if ((type != APPLY_ON_ALWAYS) && ! (*it)->isApplyOn(type))
338 continue;
339
340 if (matchAutoClass(*class_hint, *it)) {
341 return (*it)->applyOnWs(ws) ? *it : nullptr;
342 }
343 }
344
345 return nullptr;
346 }
347
348 /**
349 * Parse regex_str and set on regex, outputting warning with name if
350 * it fails.
351 */
352 bool
parseRegexpOrWarning(RegexString & regex,const std::string regex_str,const std::string & name)353 AutoProperties::parseRegexpOrWarning(RegexString ®ex,
354 const std::string regex_str,
355 const std::string &name)
356 {
357 if (! regex_str.size() || regex.parse_match(regex_str)) {
358 return true;
359 } else {
360 USER_WARN("invalid regexp " << regex_str << " for autoproperty "
361 << name);
362 return false;
363 }
364 }
365
366 //! @brief Parses a property match rule
367 //! @param str String to parse.
368 //! @param prop Property to place result in.
369 //! @return true on success, else false.
370 bool
parsePropertyMatch(const std::string & str,Property * prop)371 AutoProperties::parsePropertyMatch(const std::string &str, Property *prop)
372 {
373 bool status = false;
374
375 // Format of property matches are regexp,regexp . Split up in class
376 // and role regexps.
377 std::vector<std::string> tokens;
378 Util::splitString(str, tokens, ",", _extended ? 5 : 2, true);
379
380 if (tokens.size() >= 2) {
381 // Make sure one of the two regexps compiles
382 status = parseRegexpOrWarning(prop->getHintName(), tokens[0], "name");
383 status = status
384 && parseRegexpOrWarning(prop->getHintClass(), tokens[1], "class");
385 }
386
387 // Parse extended part of regexp, role, title and apply on
388 if (status && _extended) {
389 if (status && tokens.size() > 2) {
390 status = parseRegexpOrWarning(prop->getRole(), tokens[2], "role");
391 }
392 if (status && tokens.size() > 3) {
393 status = parseRegexpOrWarning(prop->getTitle(), tokens[3], "title");
394 }
395 if (status && tokens.size() > 4) {
396 parsePropertyApplyOn(tokens[4], prop);
397 }
398 }
399
400 return status;
401 }
402
403 //! @brief Parses a cs and sets up a basic property
404 bool
parseProperty(CfgParser::Entry * section,Property * prop)405 AutoProperties::parseProperty(CfgParser::Entry *section, Property *prop)
406 {
407 // Get extra matching info.
408 CfgParser::Entry *value = section->findEntry("TITLE");
409 if (value) {
410 parseRegexpOrWarning(prop->getTitle(), value->getValue(), "title");
411 }
412 value = section->findEntry("ROLE");
413 if (value) {
414 parseRegexpOrWarning(prop->getRole(), value->getValue(), "role");
415 }
416
417 // Parse apply on mask.
418 value = section->findEntry("APPLYON");
419 if (value) {
420 parsePropertyApplyOn(value->getValue(), prop);
421 }
422
423 return true;
424 }
425
426 /**
427 * Parse property apply on.
428 */
429 void
parsePropertyApplyOn(const std::string & apply_on,Property * prop)430 AutoProperties::parsePropertyApplyOn(const std::string &apply_on,
431 Property *prop)
432 {
433 std::vector<std::string> tokens;
434 if (Util::splitString(apply_on, tokens, " \t", 5)) {
435 std::vector<std::string>::iterator it = tokens.begin();
436 for (; it != tokens.end(); ++it) {
437 int num = Util::StringToGet(apply_on_map, *it);
438 prop->applyAdd(static_cast<unsigned int>(num));
439 }
440 }
441 }
442
443 //! @brief Parses AutopProperty
444 void
parseAutoProperty(CfgParser::Entry * section,std::vector<uint> * ws)445 AutoProperties::parseAutoProperty(CfgParser::Entry *section,
446 std::vector<uint> *ws)
447 {
448 // Get sub section
449 section = section->getSection();
450 if (! section) {
451 return;
452 }
453
454 AutoProperty *property = new AutoProperty();
455 parsePropertyMatch(section->getValue(), property);
456
457 if (parseProperty(section, property)) {
458 parseAutoPropertyValue(section, property, ws);
459 _prop_list.push_back(property);
460 } else {
461 delete property;
462 }
463 }
464
465 //! @brief Parses a Group section of the AutoProps
466 void
parseAutoGroup(CfgParser::Entry * section,AutoProperty * property)467 AutoProperties::parseAutoGroup(CfgParser::Entry *section,
468 AutoProperty* property)
469 {
470 if (! section) {
471 return;
472 }
473
474 if (section->getValue().size()) {
475 property->group_name = section->getValue();
476 }
477
478 PropertyType property_type;
479
480 CfgParser::Entry::entry_cit it(section->begin());
481 for (; it != section->end(); ++it) {
482 property_type = Util::StringToGet(group_property_map, (*it)->getName());
483
484 switch (property_type) {
485 case AP_GROUP_SIZE:
486 property->group_size = strtol((*it)->getValue().c_str(), 0, 10);
487 break;
488 case AP_GROUP_BEHIND:
489 property->group_behind = Util::isTrue((*it)->getValue());
490 break;
491 case AP_GROUP_FOCUSED_FIRST:
492 property->group_focused_first = Util::isTrue((*it)->getValue());
493 break;
494 case AP_GROUP_GLOBAL:
495 property->group_global = Util::isTrue((*it)->getValue());
496 break;
497 case AP_GROUP_RAISE:
498 property->group_raise = Util::isTrue((*it)->getValue());
499 break;
500 default:
501 // do nothing
502 break;
503 }
504 }
505 }
506
507 /**
508 * Parses a title property section.
509 */
510 void
parseTitleProperty(CfgParser::Entry * section)511 AutoProperties::parseTitleProperty(CfgParser::Entry *section)
512 {
513 section = section->getSection();
514
515 TitleProperty *title_property;
516 CfgParser::Entry *title_section;
517
518 CfgParser::Entry::entry_cit it(section->begin());
519 for (; it != section->end(); ++it) {
520 title_section = (*it)->getSection();
521 if (! title_section) {
522 continue;
523 }
524
525 title_property = new TitleProperty();
526 parsePropertyMatch(title_section->getValue(), title_property);
527 if (parseProperty(title_section, title_property)) {
528 CfgParser::Entry *value = title_section->findEntry("RULE");
529 const std::string& wstr = value->getValue();
530 if (value && title_property->getTitleRule().parse_ed_s(wstr)) {
531 _title_prop_list.push_back(title_property);
532 title_property = 0;
533 }
534 }
535
536 if (title_property) {
537 delete title_property;
538 }
539 }
540 }
541
542 /**
543 * Parse decor property sections.
544 */
545 void
parseDecorProperty(CfgParser::Entry * section)546 AutoProperties::parseDecorProperty(CfgParser::Entry *section)
547 {
548 section = section->getSection();
549
550 DecorProperty *decor_property;
551 CfgParser::Entry *decor_section;
552
553 CfgParser::Entry::entry_cit it(section->begin());
554 for (; it != section->end(); ++it) {
555 decor_section = (*it)->getSection ();
556 if (! decor_section) {
557 continue;
558 }
559
560 decor_property = new DecorProperty();
561 parsePropertyMatch(decor_section->getValue (), decor_property);
562 if (parseProperty(decor_section, decor_property)) {
563 CfgParser::Entry *value = decor_section->findEntry("DECOR");
564 if (value) {
565 decor_property->applyAdd(APPLY_ON_START);
566 decor_property->setName(value->getValue());
567 _decor_prop_list.push_back(decor_property);
568 decor_property = 0;
569 }
570 }
571
572 if (decor_property) {
573 delete decor_property;
574 }
575 }
576 }
577
578 /**
579 * Parse dock app properties.
580 */
581 void
parseDockAppProperty(CfgParser::Entry * section)582 AutoProperties::parseDockAppProperty(CfgParser::Entry *section)
583 {
584 section = section->getSection();
585
586 // Reset harbour sort, set to true if POSITION property found.
587 _harbour_sort = false;
588
589 DockAppProperty *dock_property;
590 CfgParser::Entry *dock_section;
591
592 CfgParser::Entry::entry_cit it(section->begin());
593 for (; it != section->end(); ++it) {
594 dock_section = (*it)->getSection();
595 if (! dock_section) {
596 continue;
597 }
598
599 dock_property = new DockAppProperty();
600 parsePropertyMatch(dock_section->getValue(), dock_property);
601 if (parseProperty(dock_section, dock_property)) {
602 CfgParser::Entry *value = dock_section->findEntry("POSITION");
603 if (value) {
604 _harbour_sort = true;
605
606 int position = strtol(value->getValue().c_str(), 0, 10);
607 dock_property->setPosition(position);
608 _decor_prop_list.push_back(dock_property);
609 dock_property = 0;
610 }
611 }
612
613 if (dock_property) {
614 delete dock_property;
615 }
616 }
617 }
618
619 //! @brief Parse type auto properties.
620 //! @param section Section containing properties.
621 void
parseTypeProperty(CfgParser::Entry * section)622 AutoProperties::parseTypeProperty(CfgParser::Entry *section)
623 {
624 // Get sub section
625 section = section->getSection();
626
627 AtomName atom;
628 AutoProperty *type_property;
629 CfgParser::Entry *type_section;
630 std::map<AtomName, AutoProperty*>::iterator atom_it;
631
632 // Look for all type properties
633 CfgParser::Entry::entry_cit it(section->begin());
634 for (; it != section->end(); ++it) {
635 type_section = (*it)->getSection();
636 if (! type_section) {
637 continue;
638 }
639
640 // Create new property and try to parse
641 type_property = new AutoProperty();
642 atom = Util::StringToGet(window_type_map, type_section->getValue());
643 if (atom == WINDOW_TYPE) {
644 USER_WARN("unknown type " << type_section->getValue()
645 << " for autoproperty");
646 }
647
648 if (atom != WINDOW_TYPE && parseProperty(type_section, type_property)) {
649 // Parse of match ok, parse values
650 parseAutoPropertyValue(type_section, type_property, 0);
651
652 // Add to list, make sure it does not exist already
653 atom_it = _window_type_prop_map.find(atom);
654 if (atom_it != _window_type_prop_map.end()) {
655 USER_WARN("multiple type autoproperties for type "
656 << type_section->getValue());
657 delete atom_it->second;
658 }
659 _window_type_prop_map[atom] = type_property;
660 } else {
661 delete type_property;
662 }
663 }
664 }
665
666 //! @brief Set default values for type auto properties not in configuration.
667 void
setDefaultTypeProperties(void)668 AutoProperties::setDefaultTypeProperties(void)
669 {
670 // DESKTOP
671 if (! findWindowTypeProperty(WINDOW_TYPE_DESKTOP)) {
672 AutoProperty *prop = new AutoProperty();
673 prop->maskAdd(AP_CLIENT_GEOMETRY);
674 prop->client_gm_mask = X11::parseGeometry("0x0+0+0", prop->client_gm);
675 prop->maskAdd(AP_STICKY);
676 prop->sticky = true;
677 prop->maskAdd(AP_TITLEBAR);
678 prop->titlebar = false;
679 prop->maskAdd(AP_BORDER);
680 prop->border = false;
681 prop->maskAdd(AP_SKIP);
682 prop->skip =
683 SKIP_MENUS|SKIP_FOCUS_TOGGLE|SKIP_SNAP|SKIP_PAGER|SKIP_TASKBAR;
684 prop->maskAdd(AP_LAYER);
685 prop->layer = LAYER_DESKTOP;
686 prop->maskAdd(AP_FOCUSABLE);
687 prop->focusable = false;
688 prop->maskAdd(AP_DISALLOWED_ACTIONS);
689 prop->disallowed_actions = ACTION_ACCESS_MOVE|ACTION_ACCESS_RESIZE;
690
691 _window_type_prop_map[WINDOW_TYPE_DESKTOP] = prop;
692 }
693
694 // DOCK
695 if (! findWindowTypeProperty(WINDOW_TYPE_DOCK)) {
696 AutoProperty *prop = new AutoProperty();
697 prop->maskAdd(AP_STICKY);
698 prop->sticky = true;
699 prop->maskAdd(AP_TITLEBAR);
700 prop->titlebar = false;
701 prop->maskAdd(AP_BORDER);
702 prop->border = false;
703 prop->maskAdd(AP_SKIP);
704 prop->skip = SKIP_MENUS|SKIP_FOCUS_TOGGLE|SKIP_PAGER|SKIP_TASKBAR;
705 prop->maskAdd(AP_LAYER);
706 prop->layer = LAYER_DOCK;
707 prop->maskAdd(AP_FOCUSABLE);
708 prop->focusable = false;
709 prop->maskAdd(AP_DISALLOWED_ACTIONS);
710 prop->disallowed_actions = ACTION_ACCESS_MOVE|ACTION_ACCESS_RESIZE;
711
712 _window_type_prop_map[WINDOW_TYPE_DOCK] = prop;
713 }
714
715 // TOOLBAR
716 if (! findWindowTypeProperty(WINDOW_TYPE_TOOLBAR)) {
717 AutoProperty *prop = new AutoProperty();
718 prop->maskAdd(AP_TITLEBAR);
719 prop->titlebar = true;
720 prop->maskAdd(AP_BORDER);
721 prop->border = true;
722 prop->maskAdd(AP_SKIP);
723 prop->skip = SKIP_MENUS|SKIP_FOCUS_TOGGLE|SKIP_PAGER|SKIP_TASKBAR;
724
725 _window_type_prop_map[WINDOW_TYPE_TOOLBAR] = prop;
726 }
727
728 // MENU
729 if (! findWindowTypeProperty(WINDOW_TYPE_MENU)) {
730 AutoProperty *prop = new AutoProperty();
731 prop->maskAdd(AP_TITLEBAR);
732 prop->titlebar = false;
733 prop->maskAdd(AP_BORDER);
734 prop->border = false;
735 prop->maskAdd(AP_SKIP);
736 prop->skip = SKIP_MENUS|SKIP_FOCUS_TOGGLE|SKIP_SNAP|SKIP_PAGER
737 |SKIP_TASKBAR;
738
739 _window_type_prop_map[WINDOW_TYPE_MENU] = prop;
740 }
741
742 // UTILITY
743 if (! findWindowTypeProperty(WINDOW_TYPE_UTILITY)) {
744 AutoProperty *prop = new AutoProperty();
745 prop->maskAdd(AP_TITLEBAR);
746 prop->titlebar = true;
747 prop->maskAdd(AP_BORDER);
748 prop->border = true;
749 prop->maskAdd(AP_SKIP);
750 prop->skip = SKIP_MENUS|SKIP_FOCUS_TOGGLE|SKIP_SNAP;
751
752 _window_type_prop_map[WINDOW_TYPE_UTILITY] = prop;
753 }
754
755 // SPLASH
756 if (! findWindowTypeProperty(WINDOW_TYPE_SPLASH)) {
757 AutoProperty *prop = new AutoProperty();
758 prop->maskAdd(AP_TITLEBAR);
759 prop->titlebar = false;
760 prop->maskAdd(AP_BORDER);
761 prop->border = false;
762
763 _window_type_prop_map[WINDOW_TYPE_SPLASH] = prop;
764 }
765
766 // DIALOG
767 if (! findWindowTypeProperty(WINDOW_TYPE_DIALOG)) {
768 AutoProperty *prop = new AutoProperty();
769 _window_type_prop_map[WINDOW_TYPE_DIALOG] = prop;
770 }
771
772 // DROPDOWNMENU
773 if (! findWindowTypeProperty(WINDOW_TYPE_DROPDOWN_MENU)) {
774 AutoProperty *prop = new AutoProperty();
775 _window_type_prop_map[WINDOW_TYPE_DROPDOWN_MENU] = prop;
776 }
777
778 // POPUPMENU
779 if (! findWindowTypeProperty(WINDOW_TYPE_POPUP_MENU)) {
780 AutoProperty *prop = new AutoProperty();
781 _window_type_prop_map[WINDOW_TYPE_POPUP_MENU] = prop;
782 }
783
784 // TOOLTIP
785 if (! findWindowTypeProperty(WINDOW_TYPE_TOOLTIP)) {
786 AutoProperty *prop = new AutoProperty();
787 prop->titlebar = false;
788 prop->maskAdd(AP_TITLEBAR);
789 prop->border = false;
790 prop->maskAdd(AP_BORDER);
791 prop->focus_new = false;
792 prop->maskAdd(AP_FOCUS_NEW);
793 prop->skip = SKIP_MENUS|SKIP_FOCUS_TOGGLE|SKIP_SNAP;
794 prop->maskAdd(AP_SKIP);
795 _window_type_prop_map[WINDOW_TYPE_TOOLTIP] = prop;
796 }
797
798 // NOTIFICATION
799 if (! findWindowTypeProperty(WINDOW_TYPE_NOTIFICATION)) {
800 AutoProperty *prop = new AutoProperty();
801 prop->titlebar = false;
802 prop->maskAdd(AP_TITLEBAR);
803 prop->border = false;
804 prop->maskAdd(AP_BORDER);
805 prop->focus_new = false;
806 prop->maskAdd(AP_FOCUS_NEW);
807 prop->skip = SKIP_MENUS|SKIP_FOCUS_TOGGLE|SKIP_SNAP;
808 prop->maskAdd(AP_SKIP);
809 _window_type_prop_map[WINDOW_TYPE_NOTIFICATION] = prop;
810 }
811
812 // COMBO
813 if (! findWindowTypeProperty(WINDOW_TYPE_COMBO)) {
814 AutoProperty *prop = new AutoProperty();
815 _window_type_prop_map[WINDOW_TYPE_COMBO] = prop;
816 }
817
818 // DND
819 if (! findWindowTypeProperty(WINDOW_TYPE_DND)) {
820 AutoProperty *prop = new AutoProperty();
821 _window_type_prop_map[WINDOW_TYPE_DND] = prop;
822 }
823 }
824
825 //! @brief Parse AutoProperty value attributes.
826 //! @param section Config section to parse.
827 //! @param prop Property to store result in.
828 //! @param ws List of workspaces to apply property on.
829 void
parseAutoPropertyValue(CfgParser::Entry * section,AutoProperty * prop,std::vector<uint> * ws)830 AutoProperties::parseAutoPropertyValue(CfgParser::Entry *section,
831 AutoProperty *prop,
832 std::vector<uint> *ws)
833 {
834 // Copy workspaces, if any
835 if (ws) {
836 prop->setWorkspaces(*ws);
837 }
838
839 // See if we have a group section
840 CfgParser::Entry *group_section(section->findSection("GROUP"));
841 if (group_section) {
842 parseAutoGroup(group_section, prop);
843 }
844
845 // start parsing of values
846 std::string name, value;
847 std::vector<std::string> tokens;
848 std::vector<std::string>::iterator token_it;
849 PropertyType property_type;
850
851 CfgParser::Entry::entry_cit it(section->begin());
852 for (; it != section->end(); ++it) {
853 property_type = Util::StringToGet(property_map, (*it)->getName());
854
855 switch (property_type) {
856 case AP_STICKY:
857 prop->maskAdd(AP_STICKY);
858 prop->sticky = Util::isTrue((*it)->getValue());
859 break;
860 case AP_SHADED:
861 prop->maskAdd(AP_SHADED);
862 prop->shaded = Util::isTrue((*it)->getValue());
863 break;
864 case AP_MAXIMIZED_VERTICAL:
865 prop->maskAdd(AP_MAXIMIZED_VERTICAL);
866 prop->maximized_vertical = Util::isTrue((*it)->getValue());
867 break;
868 case AP_MAXIMIZED_HORIZONTAL:
869 prop->maskAdd(AP_MAXIMIZED_HORIZONTAL);
870 prop->maximized_horizontal = Util::isTrue((*it)->getValue());
871 break;
872 case AP_ICONIFIED:
873 prop->maskAdd(AP_ICONIFIED);
874 prop->iconified = Util::isTrue((*it)->getValue());
875 break;
876 case AP_BORDER:
877 prop->maskAdd(AP_BORDER);
878 prop->border = Util::isTrue((*it)->getValue());
879 break;
880 case AP_TITLEBAR:
881 prop->maskAdd(AP_TITLEBAR);
882 prop->titlebar = Util::isTrue((*it)->getValue());
883 break;
884 case AP_FRAME_GEOMETRY:
885 prop->maskAdd(AP_FRAME_GEOMETRY);
886 prop->frame_gm_mask =
887 X11::parseGeometry((*it)->getValue(), prop->frame_gm);
888 break;
889 case AP_CLIENT_GEOMETRY:
890 prop->maskAdd(AP_CLIENT_GEOMETRY);
891 prop->client_gm_mask =
892 X11::parseGeometry((*it)->getValue(), prop->client_gm);
893 break;
894 case AP_LAYER:
895 prop->layer = ActionConfig::getLayer((*it)->getValue());
896 if (prop->layer != LAYER_NONE) {
897 prop->maskAdd(AP_LAYER);
898 }
899 break;
900 case AP_WORKSPACE:
901 try {
902 prop->workspace = std::stoi((*it)->getValue()) - 1;
903 prop->maskAdd(AP_WORKSPACE);
904 } catch (std::invalid_argument&) {
905 }
906 break;
907 case AP_SKIP:
908 prop->maskAdd(AP_SKIP);
909 tokens.clear();
910 if ((Util::splitString((*it)->getValue(), tokens, " \t"))) {
911 for (token_it = tokens.begin();
912 token_it != tokens.end();
913 ++token_it) {
914 prop->skip |= ActionConfig::getSkip(*token_it);
915 }
916 }
917 break;
918 case AP_FULLSCREEN:
919 prop->maskAdd(AP_FULLSCREEN);
920 prop->fullscreen = Util::isTrue((*it)->getValue());
921 break;
922 case AP_PLACE_NEW:
923 prop->maskAdd(AP_PLACE_NEW);
924 prop->place_new = Util::isTrue((*it)->getValue());
925 break;
926 case AP_FOCUS_NEW:
927 prop->maskAdd(AP_FOCUS_NEW);
928 prop->focus_new = Util::isTrue((*it)->getValue());
929 break;
930 case AP_FOCUSABLE:
931 prop->maskAdd(AP_FOCUSABLE);
932 prop->focusable = Util::isTrue((*it)->getValue());
933 break;
934 case AP_CFG_DENY:
935 prop->maskAdd(AP_CFG_DENY);
936 tokens.clear();
937 if ((Util::splitString((*it)->getValue(), tokens, " \t"))) {
938 for (token_it = tokens.begin();
939 token_it != tokens.end();
940 ++token_it) {
941 prop->cfg_deny |= ActionConfig::getCfgDeny(*token_it);
942 }
943 }
944 break;
945 case AP_ALLOWED_ACTIONS:
946 prop->maskAdd(AP_ALLOWED_ACTIONS);
947 pekwm::config()->parseActionAccessMask((*it)->getValue(),
948 prop->allowed_actions);
949 break;
950 case AP_DISALLOWED_ACTIONS:
951 prop->maskAdd(AP_DISALLOWED_ACTIONS);
952 pekwm::config()->parseActionAccessMask((*it)->getValue(),
953 prop->disallowed_actions);
954 break;
955 case AP_OPACITY:
956 prop->maskAdd(AP_OPACITY);
957 Config::parseOpacity((*it)->getValue(),
958 prop->focus_opacity, prop->unfocus_opacity);
959 break;
960 case AP_DECOR:
961 prop->maskAdd(AP_DECOR);
962 prop->frame_decor = (*it)->getValue();
963 break;
964 case AP_ICON: {
965 PImage *image = _image_handler->getImage((*it)->getValue());
966 if (image == nullptr) {
967 USER_WARN("failed to load icon " << (*it)->getValue()
968 << " ignoring icon property");
969 } else {
970 prop->maskAdd(AP_ICON);
971 prop->icon = new PImageIcon(image);
972 _image_handler->returnImage(image);
973 }
974 break;
975 }
976 default:
977 // do nothing
978 break;
979 }
980 }
981 }
982
983 //! @brief Searches the _prop_list for a property
984 AutoProperty*
findAutoProperty(const ClassHint * class_hint,int ws,ApplyOn type)985 AutoProperties::findAutoProperty(const ClassHint* class_hint, int ws, ApplyOn type)
986 {
987 return static_cast<AutoProperty*>(findProperty(class_hint, &_prop_list, ws, type));
988 }
989
990 //! @brief Searches the _title_prop_list for a property
991 TitleProperty*
findTitleProperty(const ClassHint * class_hint)992 AutoProperties::findTitleProperty(const ClassHint* class_hint)
993 {
994 return static_cast<TitleProperty*>(findProperty(class_hint, &_title_prop_list, -1, APPLY_ON_ALWAYS));
995 }
996
997 DecorProperty*
findDecorProperty(const ClassHint * class_hint)998 AutoProperties::findDecorProperty(const ClassHint* class_hint)
999 {
1000 return static_cast<DecorProperty*>(findProperty(class_hint, &_decor_prop_list, -1, APPLY_ON_ALWAYS));
1001 }
1002
1003 DockAppProperty*
findDockAppProperty(const ClassHint * class_hint)1004 AutoProperties::findDockAppProperty(const ClassHint *class_hint)
1005 {
1006 return static_cast<DockAppProperty*>(findProperty(class_hint, &_dock_app_prop_list, -1, APPLY_ON_ALWAYS));
1007 }
1008
1009 //! @brief Get AutoProperty for window of type type
1010 //! @param atom Atom to get property for.
1011 //! @return AutoProperty on success, else 0.
1012 AutoProperty*
findWindowTypeProperty(AtomName atom)1013 AutoProperties::findWindowTypeProperty(AtomName atom)
1014 {
1015 AutoProperty *prop = 0;
1016 std::map<AtomName, AutoProperty*>::iterator it
1017 = _window_type_prop_map.find(atom);
1018 if (it != _window_type_prop_map.end()) {
1019 prop = it->second;
1020 }
1021
1022 return prop;
1023 }
1024
1025 //! @brief Removes all ApplyOnStart actions as they consume memory
1026 void
removeApplyOnStart(void)1027 AutoProperties::removeApplyOnStart(void)
1028 {
1029 std::vector<Property*>::iterator it = _prop_list.begin();
1030 for (; it != _prop_list.end(); ++it) {
1031 if ((*it)->isApplyOn(APPLY_ON_START)) {
1032 (*it)->applyRemove(APPLY_ON_START);
1033 if (! (*it)->getApplyOn()) {
1034 delete *it;
1035 it = _prop_list.erase(it);
1036 --it; // compensate for the ++ in the loop
1037 }
1038 }
1039 }
1040
1041 _apply_on_start = false;
1042 }
1043
1044 //! @brief Tries to match a class hint against an autoproperty data entry
1045 bool
matchAutoClass(const ClassHint & hint,Property * prop)1046 AutoProperties::matchAutoClass(const ClassHint &hint, Property *prop)
1047 {
1048 bool ok = false;
1049
1050 if ((prop->getHintName() == hint.h_name)
1051 && (prop->getHintClass() == hint.h_class))
1052 {
1053 ok = true;
1054 if (prop->getTitle ().is_match_ok ()) {
1055 ok = (prop->getTitle () == hint.title);
1056 }
1057 if (ok && prop->getRole ().is_match_ok ()) {
1058 ok = (prop->getRole () == hint.h_role);
1059 }
1060 }
1061
1062 return ok;
1063 }
1064