1 #include <wayfire/config/types.hpp>
2 #include <vector>
3 #include <map>
4 #include <cmath>
5 #include <algorithm>
6
7 #include <libevdev/libevdev.h>
8 #include <sstream>
9
10 /* --------------------------- Primitive types ------------------------------ */
11 template<>
from_string(const std::string & value)12 stdx::optional<bool> wf::option_type::from_string(const std::string& value)
13 {
14 std::string lowercase = value;
15 for (auto& c : lowercase)
16 {
17 c = std::tolower(c);
18 }
19
20 if ((lowercase == "true") || (lowercase == "1"))
21 {
22 return true;
23 }
24
25 if ((lowercase == "false") || (lowercase == "0"))
26 {
27 return false;
28 }
29
30 return {};
31 }
32
33 template<>
from_string(const std::string & value)34 stdx::optional<int> wf::option_type::from_string(const std::string& value)
35 {
36 std::istringstream in{value};
37 int result;
38 in >> result;
39
40 if (value != std::to_string(result))
41 {
42 return {};
43 }
44
45 return result;
46 }
47
48 /** Attempt to parse a string as an double value */
49 template<>
from_string(const std::string & value)50 stdx::optional<double> wf::option_type::from_string(const std::string& value)
51 {
52 auto old = std::locale::global(std::locale::classic());
53 std::istringstream in{value};
54 double result;
55 in >> result;
56 std::locale::global(old);
57
58 if (!in.eof() || in.fail() || value.empty())
59 {
60 /* XXX: is the check above enough??? Overflow? Underflow? */
61 return {};
62 }
63
64 return result;
65 }
66
67 template<>
from_string(const std::string & value)68 stdx::optional<std::string> wf::option_type::from_string(const std::string& value)
69 {
70 return value;
71 }
72
73 template<>
to_string(const bool & value)74 std::string wf::option_type::to_string(
75 const bool& value)
76 {
77 return value ? "true" : "false";
78 }
79
80 template<>
to_string(const int & value)81 std::string wf::option_type::to_string(
82 const int& value)
83 {
84 return std::to_string(value);
85 }
86
87 template<>
to_string(const double & value)88 std::string wf::option_type::to_string(
89 const double& value)
90 {
91 return std::to_string(value);
92 }
93
94 template<>
to_string(const std::string & value)95 std::string wf::option_type::to_string(
96 const std::string& value)
97 {
98 return value;
99 }
100
101 /* ----------------------------- wf::color_t -------------------------------- */
color_t()102 wf::color_t::color_t() :
103 color_t(0.0, 0.0, 0.0, 0.0)
104 {}
105
color_t(double r,double g,double b,double a)106 wf::color_t::color_t(double r, double g, double b, double a)
107 {
108 this->r = r;
109 this->g = g;
110 this->b = b;
111 this->a = a;
112 }
113
color_t(const glm::vec4 & value)114 wf::color_t::color_t(const glm::vec4& value) :
115 color_t(value.r, value.g, value.b, value.a)
116 {}
117
hex_to_double(std::string value)118 static double hex_to_double(std::string value)
119 {
120 char *dummy;
121 return std::strtol(value.c_str(), &dummy, 16);
122 }
123
try_parse_rgba(const std::string & value)124 static stdx::optional<wf::color_t> try_parse_rgba(const std::string& value)
125 {
126 wf::color_t parsed = {0, 0, 0, 0};
127 std::stringstream ss(value);
128
129 auto old = std::locale::global(std::locale::classic());
130 bool valid_color =
131 (bool)(ss >> parsed.r >> parsed.g >> parsed.b >> parsed.a);
132
133 /* Check nothing else after that */
134 std::string dummy;
135 valid_color &= !(bool)(ss >> dummy);
136
137 std::locale::global(old);
138
139 return valid_color ? parsed : stdx::optional<wf::color_t>{};
140 }
141
142 #include <iostream>
143 static const std::string hex_digits = "0123456789ABCDEF";
144 template<>
from_string(const std::string & param_value)145 stdx::optional<wf::color_t> wf::option_type::from_string(
146 const std::string& param_value)
147 {
148 auto value = param_value;
149 for (auto& ch : value)
150 {
151 ch = std::toupper(ch);
152 }
153
154 auto as_rgba = try_parse_rgba(value);
155 if (as_rgba)
156 {
157 return as_rgba;
158 }
159
160 /* Either #RGBA or #RRGGBBAA */
161 if ((value.size() != 5) && (value.size() != 9))
162 {
163 return {};
164 }
165
166 if (value[0] != '#')
167 {
168 return {};
169 }
170
171 if (value.find_first_not_of(hex_digits, 1) != std::string::npos)
172 {
173 return {};
174 }
175
176 double r, g, b, a;
177
178 /* #RRGGBBAA case */
179 if (value.size() == 9)
180 {
181 r = hex_to_double(value.substr(1, 2)) / 255.0;
182 g = hex_to_double(value.substr(3, 2)) / 255.0;
183 b = hex_to_double(value.substr(5, 2)) / 255.0;
184 a = hex_to_double(value.substr(7, 2)) / 255.0;
185 } else
186 {
187 assert(value.size() == 5);
188 r = hex_to_double(value.substr(1, 1)) / 15.0;
189 g = hex_to_double(value.substr(2, 1)) / 15.0;
190 b = hex_to_double(value.substr(3, 1)) / 15.0;
191 a = hex_to_double(value.substr(4, 1)) / 15.0;
192 }
193
194 return wf::color_t{r, g, b, a};
195 }
196
197 template<>
to_string(const color_t & value)198 std::string wf::option_type::to_string(const color_t& value)
199 {
200 const int max_byte = 255;
201 const int min_byte = 0;
202
203 auto to_hex = [=] (double number_d)
204 {
205 int number = std::round(number_d);
206 /* Clamp */
207 number = std::min(number, max_byte);
208 number = std::max(number, min_byte);
209
210 std::string result;
211 result += hex_digits[number / 16];
212 result += hex_digits[number % 16];
213 return result;
214 };
215
216 return "#" + to_hex(value.r * max_byte) + to_hex(value.g * max_byte) +
217 to_hex(value.b * max_byte) + to_hex(value.a * max_byte);
218 }
219
operator ==(const color_t & other) const220 bool wf::color_t::operator ==(const color_t& other) const
221 {
222 constexpr double epsilon = 1e-6;
223
224 bool equal = true;
225 equal &= std::abs(this->r - other.r) < epsilon;
226 equal &= std::abs(this->g - other.g) < epsilon;
227 equal &= std::abs(this->b - other.b) < epsilon;
228 equal &= std::abs(this->a - other.a) < epsilon;
229
230 return equal;
231 }
232
233 /* ------------------------- wf::keybinding_t ------------------------------- */
234 struct general_binding_t
235 {
236 bool enabled;
237 uint32_t mods;
238 uint32_t value;
239 };
240
241 /**
242 * Split @value at non-empty tokens when encountering any of the characters
243 * in @at
244 */
split_at(std::string value,std::string at,bool allow_empty=false)245 static std::vector<std::string> split_at(std::string value, std::string at,
246 bool allow_empty = false)
247 {
248 /* Trick: add a delimiter at position 0 and at the end
249 * to avoid special casing */
250 value = at[0] + value + at[0];
251
252 size_t current = 0;
253 std::vector<size_t> split_positions = {0};
254 while (current < value.size() - 1)
255 {
256 size_t next_split = value.find_first_of(at, current + 1);
257 split_positions.push_back(next_split);
258 current = next_split;
259 }
260
261 assert(split_positions.size() >= 2);
262 std::vector<std::string> tokens;
263 for (size_t i = 1; i < split_positions.size(); i++)
264 {
265 if ((split_positions[i] == split_positions[i - 1] + 1) && !allow_empty)
266 {
267 continue; // skip empty tokens
268 }
269
270 tokens.push_back(value.substr(split_positions[i - 1] + 1,
271 split_positions[i] - split_positions[i - 1] - 1));
272 }
273
274 return tokens;
275 }
276
277 static std::map<std::string, wf::keyboard_modifier_t> modifier_names =
278 {
279 {"ctrl", wf::KEYBOARD_MODIFIER_CTRL},
280 {"alt", wf::KEYBOARD_MODIFIER_ALT},
281 {"shift", wf::KEYBOARD_MODIFIER_SHIFT},
282 {"super", wf::KEYBOARD_MODIFIER_LOGO},
283 };
284
binding_to_string(general_binding_t binding)285 static std::string binding_to_string(general_binding_t binding)
286 {
287 std::string result = "";
288 for (auto& pair : modifier_names)
289 {
290 if (binding.mods & pair.second)
291 {
292 result += "<" + pair.first + "> ";
293 }
294 }
295
296 if (binding.value > 0)
297 {
298 auto evdev_name = libevdev_event_code_get_name(EV_KEY, binding.value);
299 result += evdev_name ?: "NULL";
300 }
301
302 return result;
303 }
304
305 /**
306 * @return A string which consists of the characters of value, but without
307 * those contained in filter.
308 */
filter_out(std::string value,std::string filter)309 static std::string filter_out(std::string value, std::string filter)
310 {
311 std::string result;
312 for (auto& c : value)
313 {
314 if (filter.find(c) != std::string::npos)
315 {
316 continue;
317 }
318
319 result += c;
320 }
321
322 return result;
323 }
324
325 static const std::string whitespace_chars = " \t\n\r\v\b";
326
parse_binding(std::string binding_description)327 static stdx::optional<general_binding_t> parse_binding(
328 std::string binding_description)
329 {
330 /* Handle disabled bindings */
331 auto binding_descr_no_whitespace =
332 filter_out(binding_description, whitespace_chars);
333 if ((binding_descr_no_whitespace == "none") ||
334 (binding_descr_no_whitespace == "disabled"))
335 {
336 return general_binding_t{false, 0, 0};
337 }
338
339 /* Strategy: split the binding at modifier begin/end markings and spaces,
340 * and then drop empty tokens. The tokens that are left should be either a
341 * binding or something recognizable by evdev. */
342 static const std::string delims = "<>" + whitespace_chars;
343 auto tokens = split_at(binding_description, delims);
344 if (tokens.empty())
345 {
346 return {};
347 }
348
349 general_binding_t result = {true, 0, 0};
350 for (size_t i = 0; i < tokens.size() - 1; i++)
351 {
352 if (modifier_names.count(tokens[i]))
353 {
354 result.mods |= modifier_names[tokens[i]];
355 } else
356 {
357 return {}; // invalid modifier
358 }
359 }
360
361 int code = libevdev_event_code_from_name(EV_KEY, tokens.back().c_str());
362 if (code == -1)
363 {
364 /* Last token might either be yet another modifier (in case of modifier
365 * bindings) or it may be KEY_*. If neither, we have invalid binding */
366 if (modifier_names.count(tokens.back()))
367 {
368 result.mods |= modifier_names[tokens.back()];
369 code = 0;
370 } else
371 {
372 return {}; // not found
373 }
374 }
375
376 result.value = code;
377
378 /* Do one last check: if we remove whitespaces, and add a whitespace after
379 * each > character, then the resulting string should be almost equal to the
380 * minimal description, generated by binding_to_string().
381 *
382 * Since we have already checked all identifiers and they are valid
383 * modifiers, it is enough to check just that the lengths are matching.
384 * Note we can't directly compare because the order may be different. */
385 auto filtered_descr = filter_out(binding_description, whitespace_chars);
386 std::string filtered_with_spaces;
387 for (auto c : filtered_descr)
388 {
389 filtered_with_spaces += c;
390 if (c == '>')
391 {
392 filtered_with_spaces += " ";
393 }
394 }
395
396 auto minimal_descr = binding_to_string(result);
397 if (filtered_with_spaces.length() == minimal_descr.length())
398 {
399 return result;
400 }
401
402 return {};
403 }
404
keybinding_t(uint32_t modifier,uint32_t keyval)405 wf::keybinding_t::keybinding_t(uint32_t modifier, uint32_t keyval)
406 {
407 this->mod = modifier;
408 this->keyval = keyval;
409 }
410
411 template<>
from_string(const std::string & description)412 stdx::optional<wf::keybinding_t> wf::option_type::from_string(
413 const std::string& description)
414 {
415 auto parsed_opt = parse_binding(description);
416 if (!parsed_opt)
417 {
418 return {};
419 }
420
421 auto parsed = parsed_opt.value();
422
423 /* Disallow buttons, because evdev treats buttons and keys the same */
424 if (parsed.enabled && (parsed.value > 0) &&
425 (description.find("KEY") == std::string::npos))
426 {
427 return {};
428 }
429
430 if (parsed.enabled && (parsed.mods == 0) && (parsed.value == 0))
431 {
432 return {};
433 }
434
435 return wf::keybinding_t{parsed.mods, parsed.value};
436 }
437
438 template<>
to_string(const wf::keybinding_t & value)439 std::string wf::option_type::to_string(const wf::keybinding_t& value)
440 {
441 if ((value.get_modifiers() == 0) && (value.get_key() == 0))
442 {
443 return "none";
444 }
445
446 return binding_to_string({true, value.get_modifiers(), value.get_key()});
447 }
448
operator ==(const keybinding_t & other) const449 bool wf::keybinding_t::operator ==(const keybinding_t& other) const
450 {
451 return this->mod == other.mod && this->keyval == other.keyval;
452 }
453
454 /** @return The modifiers of the keybinding */
get_modifiers() const455 uint32_t wf::keybinding_t::get_modifiers() const
456 {
457 return this->mod;
458 }
459
460 /** @return The key of the keybinding */
get_key() const461 uint32_t wf::keybinding_t::get_key() const
462 {
463 return this->keyval;
464 }
465
466 /* -------------------------- wf::buttonbinding_t --------------------------- */
buttonbinding_t(uint32_t modifier,uint32_t buttonval)467 wf::buttonbinding_t::buttonbinding_t(uint32_t modifier, uint32_t buttonval)
468 {
469 this->mod = modifier;
470 this->button = buttonval;
471 }
472
473 template<>
from_string(const std::string & description)474 stdx::optional<wf::buttonbinding_t> wf::option_type::from_string(
475 const std::string& description)
476 {
477 auto parsed_opt = parse_binding(description);
478 if (!parsed_opt)
479 {
480 return {};
481 }
482
483 auto parsed = parsed_opt.value();
484 if (!parsed.enabled)
485 {
486 return wf::buttonbinding_t{0, 0};
487 }
488
489 /* Disallow keys, because evdev treats buttons and keys the same */
490 if (description.find("BTN") == std::string::npos)
491 {
492 return {};
493 }
494
495 if (parsed.value == 0)
496 {
497 return {};
498 }
499
500 return wf::buttonbinding_t{parsed.mods, parsed.value};
501 }
502
503 template<>
to_string(const wf::buttonbinding_t & value)504 std::string wf::option_type::to_string(
505 const wf::buttonbinding_t& value)
506 {
507 if ((value.get_modifiers() == 0) && (value.get_button() == 0))
508 {
509 return "none";
510 }
511
512 return binding_to_string({true, value.get_modifiers(), value.get_button()});
513 }
514
operator ==(const buttonbinding_t & other) const515 bool wf::buttonbinding_t::operator ==(const buttonbinding_t& other) const
516 {
517 return this->mod == other.mod && this->button == other.button;
518 }
519
get_modifiers() const520 uint32_t wf::buttonbinding_t::get_modifiers() const
521 {
522 return this->mod;
523 }
524
get_button() const525 uint32_t wf::buttonbinding_t::get_button() const
526 {
527 return this->button;
528 }
529
touchgesture_t(touch_gesture_type_t type,uint32_t direction,int finger_count)530 wf::touchgesture_t::touchgesture_t(touch_gesture_type_t type, uint32_t direction,
531 int finger_count)
532 {
533 this->type = type;
534 this->direction = direction;
535 this->finger_count = finger_count;
536 }
537
538 static const std::map<std::string, wf::touch_gesture_direction_t>
539 touch_gesture_direction_string_map =
540 {
541 {"up", wf::GESTURE_DIRECTION_UP},
542 {"down", wf::GESTURE_DIRECTION_DOWN},
543 {"left", wf::GESTURE_DIRECTION_LEFT},
544 {"right", wf::GESTURE_DIRECTION_RIGHT}
545 };
546
parse_single_direction(const std::string & direction)547 static wf::touch_gesture_direction_t parse_single_direction(
548 const std::string& direction)
549 {
550 if (touch_gesture_direction_string_map.count(direction))
551 {
552 return touch_gesture_direction_string_map.at(direction);
553 }
554
555 throw std::domain_error("invalid swipe direction");
556 }
557
parse_direction(const std::string & direction)558 uint32_t parse_direction(const std::string& direction)
559 {
560 size_t hyphen = direction.find("-");
561 if (hyphen == std::string::npos)
562 {
563 return parse_single_direction(direction);
564 } else
565 {
566 /* we support up to 2 directions, because >= 3 will be invalid anyway */
567 auto first = direction.substr(0, hyphen);
568 auto second = direction.substr(hyphen + 1);
569
570 uint32_t mask =
571 parse_single_direction(first) | parse_single_direction(second);
572
573 const uint32_t both_horiz =
574 wf::GESTURE_DIRECTION_LEFT | wf::GESTURE_DIRECTION_RIGHT;
575 const uint32_t both_vert =
576 wf::GESTURE_DIRECTION_UP | wf::GESTURE_DIRECTION_DOWN;
577
578 if (((mask & both_horiz) == both_horiz) ||
579 ((mask & both_vert) == both_vert))
580 {
581 throw std::domain_error("Cannot have two opposing directions in the"
582 "same gesture");
583 }
584
585 return mask;
586 }
587 }
588
parse_gesture(const std::string & value)589 wf::touchgesture_t parse_gesture(const std::string& value)
590 {
591 if (value.empty())
592 {
593 return {wf::GESTURE_TYPE_NONE, 0, 0};
594 }
595
596 auto tokens = split_at(value, " \t\v\b\n\r");
597 assert(!tokens.empty());
598
599 if (tokens.size() != 3)
600 {
601 return {wf::GESTURE_TYPE_NONE, 0, 0};
602 }
603
604 try {
605 wf::touch_gesture_type_t type;
606 uint32_t direction = 0;
607 int32_t finger_count;
608
609 if (tokens[0] == "pinch")
610 {
611 type = wf::GESTURE_TYPE_PINCH;
612 if (tokens[1] == "in")
613 {
614 direction = wf::GESTURE_DIRECTION_IN;
615 } else if (tokens[1] == "out")
616 {
617 direction = wf::GESTURE_DIRECTION_OUT;
618 } else
619 {
620 throw std::domain_error("Invalid pinch direction: " + tokens[1]);
621 }
622 } else if (tokens[0] == "swipe")
623 {
624 type = wf::GESTURE_TYPE_SWIPE;
625 direction = parse_direction(tokens[1]);
626 } else if (tokens[0] == "edge-swipe")
627 {
628 type = wf::GESTURE_TYPE_EDGE_SWIPE;
629 direction = parse_direction(tokens[1]);
630 } else
631 {
632 throw std::domain_error("Invalid gesture type:" + tokens[0]);
633 }
634
635 // TODO: instead of atoi, check properly
636 finger_count = std::atoi(tokens[2].c_str());
637 return wf::touchgesture_t{type, direction, finger_count};
638 } catch (std::exception& e)
639 {
640 // XXX: show error?
641 // ignore it, will return GESTURE_NONE
642 }
643
644 return wf::touchgesture_t{wf::GESTURE_TYPE_NONE, 0, 0};
645 }
646
647 template<>
from_string(const std::string & description)648 stdx::optional<wf::touchgesture_t> wf::option_type::from_string(
649 const std::string& description)
650 {
651 auto as_binding = parse_binding(description);
652 if (as_binding && !as_binding.value().enabled)
653 {
654 return touchgesture_t{GESTURE_TYPE_NONE, 0, 0};
655 }
656
657 auto gesture = parse_gesture(description);
658 if (gesture.get_type() == GESTURE_TYPE_NONE)
659 {
660 return {};
661 }
662
663 return gesture;
664 }
665
direction_to_string(uint32_t direction)666 static std::string direction_to_string(uint32_t direction)
667 {
668 std::string result = "";
669 for (auto& pair : touch_gesture_direction_string_map)
670 {
671 if (direction & pair.second)
672 {
673 result += pair.first + "-";
674 }
675 }
676
677 if (result.size() > 0)
678 {
679 /* Remove trailing - */
680 result.pop_back();
681 }
682
683 return result;
684 }
685
686 template<>
to_string(const touchgesture_t & value)687 std::string wf::option_type::to_string(const touchgesture_t& value)
688 {
689 std::string result;
690 switch (value.get_type())
691 {
692 case GESTURE_TYPE_NONE:
693 return "";
694
695 case GESTURE_TYPE_EDGE_SWIPE:
696 result += "edge-";
697
698 // fallthrough
699 case GESTURE_TYPE_SWIPE:
700 result += "swipe ";
701 result += direction_to_string(value.get_direction()) + " ";
702 break;
703
704 case GESTURE_TYPE_PINCH:
705 result += "pinch ";
706
707 if (value.get_direction() == GESTURE_DIRECTION_IN)
708 {
709 result += "in ";
710 }
711
712 if (value.get_direction() == GESTURE_DIRECTION_OUT)
713 {
714 result += "out ";
715 }
716
717 break;
718 }
719
720 result += std::to_string(value.get_finger_count());
721 return result;
722 }
723
get_type() const724 wf::touch_gesture_type_t wf::touchgesture_t::get_type() const
725 {
726 return this->type;
727 }
728
get_finger_count() const729 int wf::touchgesture_t::get_finger_count() const
730 {
731 return this->finger_count;
732 }
733
get_direction() const734 uint32_t wf::touchgesture_t::get_direction() const
735 {
736 return this->direction;
737 }
738
operator ==(const touchgesture_t & other) const739 bool wf::touchgesture_t::operator ==(const touchgesture_t& other) const
740 {
741 return type == other.type && finger_count == other.finger_count &&
742 (direction == 0 || other.direction == 0 || direction == other.direction);
743 }
744
745 /* --------------------------- activatorbinding_t --------------------------- */
746 struct wf::activatorbinding_t::impl
747 {
748 std::vector<keybinding_t> keys;
749 std::vector<buttonbinding_t> buttons;
750 std::vector<touchgesture_t> gestures;
751 std::vector<hotspot_binding_t> hotspots;
752 };
753
activatorbinding_t()754 wf::activatorbinding_t::activatorbinding_t()
755 {
756 this->priv = std::make_unique<impl>();
757 }
758
759 wf::activatorbinding_t::~activatorbinding_t() = default;
760
activatorbinding_t(const activatorbinding_t & other)761 wf::activatorbinding_t::activatorbinding_t(const activatorbinding_t& other)
762 {
763 this->priv = std::make_unique<impl>(*other.priv);
764 }
765
operator =(const activatorbinding_t & other)766 wf::activatorbinding_t& wf::activatorbinding_t::operator =(
767 const activatorbinding_t& other)
768 {
769 if (&other != this)
770 {
771 this->priv = std::make_unique<impl>(*other.priv);
772 }
773
774 return *this;
775 }
776
777 template<class Type>
try_add_binding(std::vector<Type> & to,const std::string & value)778 bool try_add_binding(
779 std::vector<Type>& to, const std::string& value)
780 {
781 auto binding = wf::option_type::from_string<Type>(value);
782 if (binding)
783 {
784 to.push_back(binding.value());
785 return true;
786 }
787
788 return false;
789 }
790
791 template<>
from_string(const std::string & string)792 stdx::optional<wf::activatorbinding_t> wf::option_type::from_string(
793 const std::string& string)
794 {
795 activatorbinding_t binding;
796
797 if (filter_out(string, whitespace_chars) == "")
798 {
799 return binding; // empty binding
800 }
801
802 auto tokens = split_at(string, "|", true);
803 for (auto& token : tokens)
804 {
805 bool is_valid_binding =
806 try_add_binding(binding.priv->keys, token) ||
807 try_add_binding(binding.priv->buttons, token) ||
808 try_add_binding(binding.priv->gestures, token) ||
809 try_add_binding(binding.priv->hotspots, token);
810
811 if (!is_valid_binding)
812 {
813 return {};
814 }
815 }
816
817 return binding;
818 }
819
820 template<class Type>
concatenate_bindings(const std::vector<Type> & bindings)821 static std::string concatenate_bindings(const std::vector<Type>& bindings)
822 {
823 std::string repr = "";
824 for (auto& b : bindings)
825 {
826 repr += wf::option_type::to_string<Type>(b);
827 repr += " | ";
828 }
829
830 return repr;
831 }
832
833 template<>
to_string(const activatorbinding_t & value)834 std::string wf::option_type::to_string(
835 const activatorbinding_t& value)
836 {
837 std::string repr =
838 concatenate_bindings(value.priv->keys) +
839 concatenate_bindings(value.priv->buttons) +
840 concatenate_bindings(value.priv->gestures) +
841 concatenate_bindings(value.priv->hotspots);
842
843 /* Remove trailing " | " */
844 if (repr.size() >= 3)
845 {
846 repr.erase(repr.size() - 3);
847 }
848
849 return repr;
850 }
851
852 template<class Type>
find_in_container(const std::vector<Type> & haystack,Type needle)853 bool find_in_container(const std::vector<Type>& haystack,
854 Type needle)
855 {
856 return std::find(haystack.begin(), haystack.end(), needle) != haystack.end();
857 }
858
has_match(const keybinding_t & key) const859 bool wf::activatorbinding_t::has_match(const keybinding_t& key) const
860 {
861 return find_in_container(priv->keys, key);
862 }
863
has_match(const buttonbinding_t & button) const864 bool wf::activatorbinding_t::has_match(const buttonbinding_t& button) const
865 {
866 return find_in_container(priv->buttons, button);
867 }
868
has_match(const touchgesture_t & gesture) const869 bool wf::activatorbinding_t::has_match(const touchgesture_t& gesture) const
870 {
871 return find_in_container(priv->gestures, gesture);
872 }
873
operator ==(const activatorbinding_t & other) const874 bool wf::activatorbinding_t::operator ==(const activatorbinding_t& other) const
875 {
876 return priv->keys == other.priv->keys &&
877 priv->buttons == other.priv->buttons &&
878 priv->gestures == other.priv->gestures &&
879 priv->hotspots == other.priv->hotspots;
880 }
881
get_hotspots() const882 const std::vector<wf::hotspot_binding_t>& wf::activatorbinding_t::get_hotspots()
883 const
884 {
885 return priv->hotspots;
886 }
887
hotspot_binding_t(uint32_t edges,int32_t along_edge,int32_t away_from_edge,int32_t timeout)888 wf::hotspot_binding_t::hotspot_binding_t(uint32_t edges,
889 int32_t along_edge, int32_t away_from_edge, int32_t timeout)
890 {
891 this->edges = edges;
892 this->along = along_edge;
893 this->away = away_from_edge;
894 this->timeout = timeout;
895 }
896
operator ==(const hotspot_binding_t & other) const897 bool wf::hotspot_binding_t::operator ==(const hotspot_binding_t& other) const
898 {
899 return edges == other.edges && along == other.along && away == other.away &&
900 timeout == other.timeout;
901 }
902
get_timeout() const903 int32_t wf::hotspot_binding_t::get_timeout() const
904 {
905 return timeout;
906 }
907
get_size_away_from_edge() const908 int32_t wf::hotspot_binding_t::get_size_away_from_edge() const
909 {
910 return away;
911 }
912
get_size_along_edge() const913 int32_t wf::hotspot_binding_t::get_size_along_edge() const
914 {
915 return along;
916 }
917
get_edges() const918 uint32_t wf::hotspot_binding_t::get_edges() const
919 {
920 return edges;
921 }
922
923 static std::map<std::string, wf::output_edge_t> hotspot_edges =
924 {
925 {"top", wf::OUTPUT_EDGE_TOP},
926 {"bottom", wf::OUTPUT_EDGE_BOTTOM},
927 {"left", wf::OUTPUT_EDGE_LEFT},
928 {"right", wf::OUTPUT_EDGE_RIGHT},
929 };
930
931 template<>
from_string(const std::string & description)932 stdx::optional<wf::hotspot_binding_t> wf::option_type::from_string(
933 const std::string& description)
934 {
935 std::istringstream stream{description};
936 std::string token;
937 stream >> token; // "hotspot"
938 if (token != "hotspot")
939 {
940 return {};
941 }
942
943 stream >> token; // direction
944
945 uint32_t edges = 0;
946
947 size_t hyphen = token.find("-");
948 if (hyphen == token.npos)
949 {
950 if (hotspot_edges.count(token) == 0)
951 {
952 return {};
953 }
954
955 edges = hotspot_edges[token];
956 } else
957 {
958 std::string first_direction = token.substr(0, hyphen);
959 std::string second_direction = token.substr(hyphen + 1);
960
961 if ((hotspot_edges.count(first_direction) == 0) ||
962 (hotspot_edges.count(second_direction) == 0))
963 {
964 return {};
965 }
966
967 edges = hotspot_edges[first_direction] | hotspot_edges[second_direction];
968 }
969
970 stream >> token;
971 int32_t along, away;
972 if (2 != sscanf(token.c_str(), "%dx%d", &along, &away))
973 {
974 return {};
975 }
976
977 stream >> token;
978 auto timeout = wf::option_type::from_string<int>(token);
979
980 if (!timeout || stream >> token) // check for trailing characters
981 {
982 return {};
983 }
984
985 return wf::hotspot_binding_t(edges, along, away, timeout.value());
986 }
987
988 template<>
to_string(const wf::hotspot_binding_t & value)989 std::string wf::option_type::to_string(
990 const wf::hotspot_binding_t& value)
991 {
992 std::ostringstream out;
993 out << "hotspot ";
994
995 uint32_t remaining_edges = value.get_edges();
996
997 const auto& find_edge = [&] (bool need_hyphen)
998 {
999 for (const auto& edge : hotspot_edges)
1000 {
1001 if (remaining_edges & edge.second)
1002 {
1003 remaining_edges &= ~edge.second;
1004 if (need_hyphen)
1005 {
1006 out << "-";
1007 }
1008
1009 out << edge.first;
1010 break;
1011 }
1012 }
1013 };
1014
1015 find_edge(false);
1016 find_edge(true);
1017
1018 out << " " << value.get_size_along_edge() << "x" <<
1019 value.get_size_away_from_edge() <<
1020 " " << value.get_timeout();
1021 return out.str();
1022 }
1023
1024 /* ------------------------- Output config types ---------------------------- */
mode_t(bool auto_on)1025 wf::output_config::mode_t::mode_t(bool auto_on)
1026 {
1027 this->type = auto_on ? MODE_AUTO : MODE_OFF;
1028 }
1029
mode_t(int32_t width,int32_t height,int32_t refresh)1030 wf::output_config::mode_t::mode_t(int32_t width, int32_t height, int32_t refresh)
1031 {
1032 this->type = MODE_RESOLUTION;
1033 this->width = width;
1034 this->height = height;
1035 this->refresh = refresh;
1036 }
1037
1038 /**
1039 * Initialize a mirror mode.
1040 */
mode_t(const std::string & mirror_from)1041 wf::output_config::mode_t::mode_t(const std::string& mirror_from)
1042 {
1043 this->type = MODE_MIRROR;
1044 this->mirror_from = mirror_from;
1045 }
1046
1047 /** @return The type of this mode. */
get_type() const1048 wf::output_config::mode_type_t wf::output_config::mode_t::get_type() const
1049 {
1050 return type;
1051 }
1052
get_width() const1053 int32_t wf::output_config::mode_t::get_width() const
1054 {
1055 return width;
1056 }
1057
get_height() const1058 int32_t wf::output_config::mode_t::get_height() const
1059 {
1060 return height;
1061 }
1062
get_refresh() const1063 int32_t wf::output_config::mode_t::get_refresh() const
1064 {
1065 return refresh;
1066 }
1067
get_mirror_from() const1068 std::string wf::output_config::mode_t::get_mirror_from() const
1069 {
1070 return mirror_from;
1071 }
1072
operator ==(const mode_t & other) const1073 bool wf::output_config::mode_t::operator ==(const mode_t& other) const
1074 {
1075 if (type != other.get_type())
1076 {
1077 return false;
1078 }
1079
1080 switch (type)
1081 {
1082 case MODE_RESOLUTION:
1083 return width == other.width && height == other.height &&
1084 refresh == other.refresh;
1085
1086 case MODE_MIRROR:
1087 return mirror_from == other.mirror_from;
1088
1089 case MODE_AUTO:
1090 case MODE_OFF:
1091 return true;
1092 }
1093
1094 return false;
1095 }
1096
1097 template<>
from_string(const std::string & string)1098 stdx::optional<wf::output_config::mode_t> wf::option_type::from_string(
1099 const std::string& string)
1100 {
1101 if (string == "off")
1102 {
1103 return wf::output_config::mode_t{false};
1104 }
1105
1106 if ((string == "auto") || (string == "default"))
1107 {
1108 return wf::output_config::mode_t{true};
1109 }
1110
1111 if (string.substr(0, 6) == "mirror")
1112 {
1113 std::stringstream ss(string);
1114 std::string from, dummy;
1115 ss >> from; // the mirror word
1116 if (!(ss >> from))
1117 {
1118 return {};
1119 }
1120
1121 // trailing garbage
1122 if (ss >> dummy)
1123 {
1124 return {};
1125 }
1126
1127 return wf::output_config::mode_t{from};
1128 }
1129
1130 int w, h, rr = 0;
1131 char next;
1132
1133 int read = std::sscanf(string.c_str(), "%d x %d @ %d%c", &w, &h, &rr, &next);
1134 if ((read < 2) || (read > 3))
1135 {
1136 return {};
1137 }
1138
1139 if ((w < 0) || (h < 0) || (rr < 0))
1140 {
1141 return {};
1142 }
1143
1144 // Ensure refresh rate in mHz
1145 if (rr < 1000)
1146 {
1147 rr *= 1000;
1148 }
1149
1150 return wf::output_config::mode_t{w, h, rr};
1151 }
1152
1153 /** Represent the activator binding as a string. */
1154 template<>
to_string(const output_config::mode_t & value)1155 std::string wf::option_type::to_string(const output_config::mode_t& value)
1156 {
1157 switch (value.get_type())
1158 {
1159 case output_config::MODE_AUTO:
1160 return "auto";
1161
1162 case output_config::MODE_OFF:
1163 return "off";
1164
1165 case output_config::MODE_RESOLUTION:
1166 if (value.get_refresh() <= 0)
1167 {
1168 return to_string(value.get_width()) + "x" +
1169 to_string(value.get_height());
1170 } else
1171 {
1172 return to_string(value.get_width()) + "x" +
1173 to_string(value.get_height()) + "@" + to_string(
1174 value.get_refresh());
1175 }
1176
1177 case output_config::MODE_MIRROR:
1178 return "mirror " + value.get_mirror_from();
1179 }
1180
1181 return {};
1182 }
1183
position_t()1184 wf::output_config::position_t::position_t()
1185 {
1186 this->automatic = true;
1187 }
1188
position_t(int32_t x,int32_t y)1189 wf::output_config::position_t::position_t(int32_t x, int32_t y)
1190 {
1191 this->automatic = false;
1192 this->x = x;
1193 this->y = y;
1194 }
1195
get_x() const1196 int32_t wf::output_config::position_t::get_x() const
1197 {
1198 return x;
1199 }
1200
get_y() const1201 int32_t wf::output_config::position_t::get_y() const
1202 {
1203 return y;
1204 }
1205
is_automatic_position() const1206 bool wf::output_config::position_t::is_automatic_position() const
1207 {
1208 return automatic;
1209 }
1210
operator ==(const position_t & other) const1211 bool wf::output_config::position_t::operator ==(const position_t& other) const
1212 {
1213 if (is_automatic_position() != other.is_automatic_position())
1214 {
1215 return false;
1216 }
1217
1218 if (is_automatic_position())
1219 {
1220 return true;
1221 }
1222
1223 return x == other.x && y == other.y;
1224 }
1225
1226 template<>
from_string(const std::string & string)1227 stdx::optional<wf::output_config::position_t> wf::option_type::from_string(
1228 const std::string& string)
1229 {
1230 if ((string == "auto") || (string == "default"))
1231 {
1232 return wf::output_config::position_t();
1233 }
1234
1235 int x, y;
1236 char r;
1237 if (sscanf(string.c_str(), "%d , %d%c", &x, &y, &r) != 2)
1238 {
1239 return {};
1240 }
1241
1242 return wf::output_config::position_t(x, y);
1243 }
1244
1245 /** Represent the activator binding as a string. */
1246 template<>
to_string(const output_config::position_t & value)1247 std::string wf::option_type::to_string(const output_config::position_t& value)
1248 {
1249 if (value.is_automatic_position())
1250 {
1251 return "auto";
1252 }
1253
1254 return to_string(value.get_x()) + ", " + to_string(value.get_y());
1255 }
1256