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 &regex,
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