Lua: input.keyhook
[lsnes.git] / src / core / keymapper.cpp
blob7531a8309745af3f26c92467fda73e06e869710e
1 #include "core/command.hpp"
2 #include "core/dispatch.hpp"
3 #include "core/globalwrap.hpp"
4 #include "core/keymapper.hpp"
5 #include "core/lua.hpp"
6 #include "core/memorymanip.hpp"
7 #include "core/misc.hpp"
8 #include "core/window.hpp"
9 #include "core/lua.hpp"
11 #include <stdexcept>
12 #include <stdexcept>
13 #include <iostream>
14 #include <list>
15 #include <map>
16 #include <sstream>
17 #include <set>
19 namespace
21 function_ptr_command<tokensplitter&> bind_key("bind-key", "Bind a (pseudo-)key",
22 "Syntax: bind-key [<mod>/<modmask>] <key> <command>\nBind command to specified key (with specified "
23 " modifiers)\n",
24 [](tokensplitter& t) throw(std::bad_alloc, std::runtime_error) {
25 std::string mod, modmask, keyname, command;
26 std::string mod_or_key = t;
27 if(mod_or_key.find_first_of("/") < mod_or_key.length()) {
28 //Mod field.
29 size_t split = mod_or_key.find_first_of("/");
30 mod = mod_or_key.substr(0, split);
31 modmask = mod_or_key.substr(split + 1);
32 mod_or_key = static_cast<std::string>(t);
34 if(mod_or_key == "")
35 throw std::runtime_error("Expected optional modifiers and key");
36 keyname = mod_or_key;
37 command = t.tail();
38 if(command == "")
39 throw std::runtime_error("Expected command");
40 keymapper::bind(mod, modmask, keyname, command);
41 if(mod != "" || modmask != "")
42 messages << mod << "/" << modmask << " ";
43 messages << keyname << " bound to '" << command << "'" << std::endl;
45 });
47 function_ptr_command<tokensplitter&> unbind_key("unbind-key", "Unbind a (pseudo-)key",
48 "Syntax: unbind-key [<mod>/<modmask>] <key>\nUnbind specified key (with specified modifiers)\n",
49 [](tokensplitter& t) throw(std::bad_alloc, std::runtime_error) {
50 std::string mod, modmask, keyname, command;
51 std::string mod_or_key = t;
52 if(mod_or_key.find_first_of("/") < mod_or_key.length()) {
53 //Mod field.
54 size_t split = mod_or_key.find_first_of("/");
55 mod = mod_or_key.substr(0, split);
56 modmask = mod_or_key.substr(split + 1);
57 mod_or_key = static_cast<std::string>(t);
59 if(mod_or_key == "")
60 throw std::runtime_error("Expected optional modifiers and key");
61 keyname = mod_or_key;
62 command = t.tail();
63 if(command != "")
64 throw std::runtime_error("Unexpected argument");
65 keymapper::unbind(mod, modmask, keyname);
66 if(mod != "" || modmask != "")
67 messages << mod << "/" << modmask << " ";
68 messages << keyname << " unbound" << std::endl;
69 });
71 function_ptr_command<> show_bindings("show-bindings", "Show active bindings",
72 "Syntax: show-bindings\nShow bindings that are currently active.\n",
73 []() throw(std::bad_alloc, std::runtime_error) {
74 keymapper::dumpbindings();
75 });
78 std::string fixup_command_polarity(std::string cmd, bool polarity) throw(std::bad_alloc)
80 if(cmd == "" || cmd == "*")
81 return "";
82 if(cmd[0] != '*') {
83 if(cmd[0] != '+' && polarity)
84 return "";
85 if(cmd[0] == '+' && !polarity)
86 cmd[0] = '-';
87 } else {
88 if(cmd[1] != '+' && polarity)
89 return "";
90 if(cmd[1] == '+' && !polarity)
91 cmd[1] = '-';
93 return cmd;
96 namespace
98 globalwrap<std::map<std::string, modifier*>> known_modifiers;
99 globalwrap<std::map<std::string, std::string>> modifier_linkages;
100 globalwrap<std::map<std::string, keygroup*>> keygroups;
102 //Returns orig if not linked.
103 const modifier* get_linked_modifier(const modifier* orig)
105 if(!modifier_linkages().count(orig->name()))
106 return orig;
107 std::string l = modifier_linkages()[orig->name()];
108 return known_modifiers()[l];
112 modifier::modifier(const std::string& name) throw(std::bad_alloc)
114 known_modifiers()[modname = name] = this;
117 modifier::modifier(const std::string& name, const std::string& linkgroup) throw(std::bad_alloc)
119 known_modifiers()[modname = name] = this;
120 modifier_linkages()[name] = linkgroup;
123 modifier::~modifier() throw()
125 known_modifiers().erase(modname);
126 modifier_linkages().erase(modname);
129 std::set<std::string> modifier::get_set() throw(std::bad_alloc)
131 std::set<std::string> r;
132 for(auto i : known_modifiers())
133 r.insert(i.first);
134 return r;
137 modifier& modifier::lookup(const std::string& name) throw(std::bad_alloc, std::runtime_error)
139 if(!known_modifiers().count(name)) {
140 std::ostringstream x;
141 x << "Invalid modifier '" << name << "'";
142 throw std::runtime_error(x.str());
144 return *known_modifiers()[name];
147 std::string modifier::name() const throw(std::bad_alloc)
149 return modname;
152 std::string modifier::linked_name() const throw(std::bad_alloc)
154 const modifier* p = get_linked_modifier(this);
155 if(p == this)
156 return "";
157 return p->modname;
161 void modifier_set::add(const modifier& mod, bool really) throw(std::bad_alloc)
163 if(really)
164 set.insert(&mod);
167 void modifier_set::remove(const modifier& mod, bool really) throw(std::bad_alloc)
169 if(really)
170 set.erase(&mod);
173 modifier_set modifier_set::construct(const std::string& _modifiers) throw(std::bad_alloc, std::runtime_error)
175 modifier_set set;
176 std::string modifiers = _modifiers;
177 while(modifiers != "") {
178 std::string mod = modifiers;
179 std::string rest;
180 size_t split = modifiers.find_first_of(",");
181 if(split < modifiers.length()) {
182 mod = modifiers.substr(0, split);
183 rest = modifiers.substr(split + 1);
185 set.add(modifier::lookup(mod));
186 modifiers = rest;
188 return set;
191 bool modifier_set::valid(const modifier_set& set, const modifier_set& mask) throw(std::bad_alloc)
193 //No element can be together with its linkage group.
194 for(auto i : set.set) {
195 const modifier* j = get_linked_modifier(i);
196 if(i != j && set.set.count(j))
197 return false;
199 for(auto i : mask.set) {
200 const modifier* j = get_linked_modifier(i);
201 if(i != j && mask.set.count(j))
202 return false;
204 //For every element of set, it or its linkage group must be in mask.
205 for(auto i : set.set) {
206 const modifier* j = get_linked_modifier(i);
207 if(!mask.set.count(i) && !mask.set.count(j))
208 return false;
210 return true;
213 bool modifier_set::operator==(const modifier_set& m) const throw()
215 for(auto i : set)
216 if(!m.set.count(i))
217 return false;
218 for(auto i : m.set)
219 if(!set.count(i))
220 return false;
221 return true;
224 std::ostream& operator<<(std::ostream& os, const modifier_set& m)
226 os << "<modset:";
227 for(auto i : m.set)
228 os << i->name() << " ";
229 os << ">";
230 return os;
233 bool modifier_set::triggers(const modifier_set& set, const modifier_set& trigger, const modifier_set& mask)
234 throw(std::bad_alloc)
236 for(auto i : mask.set) {
237 bool ok = false;
238 //OK iff at least one of:
239 //Key itself appears in both set and trigger.
240 for(auto j : set.set) {
241 if(!trigger.set.count(j))
242 continue;
243 ok = true;
245 //Key with this linkage group appears in both set and trigger.
246 for(auto j : set.set) {
247 auto linked = get_linked_modifier(j);
248 if(linked != i)
249 continue;
250 if(!trigger.set.count(j))
251 continue;
252 ok = true;
254 //Key with this linkage appears in set and the key itself appears in trigger.
255 for(auto j : set.set) {
256 auto linked = get_linked_modifier(j);
257 if(linked != i)
258 continue;
259 if(!trigger.set.count(i))
260 continue;
261 ok = true;
263 //Nothing linked is found from neither set nor trigger.
264 bool found = false;
265 for(auto j : set.set)
266 found = found || (j == i || get_linked_modifier(j) == i);
267 for(auto j : trigger.set)
268 found = found || (j == i || get_linked_modifier(j) == i);
269 ok = ok || !found;
270 if(!ok)
271 return false;
273 return true;
276 std::string keygroup::name() throw(std::bad_alloc)
278 return keyname;
281 struct keygroup::parameters keygroup::get_parameters()
283 parameters p;
284 p.ktype = ktype;
285 p.cal_left = cal_left;
286 p.cal_center = cal_center;
287 p.cal_right = cal_right;
288 p.cal_tolerance = cal_tolerance;
289 p.last_rawval = last_rawval;
290 return p;
293 std::map<std::string, struct keygroup::parameters> keygroup::get_all_parameters()
295 std::map<std::string, struct parameters> ret;
296 for(auto i : keygroups())
297 ret[i.first] = i.second->get_parameters();
298 return ret;
301 keygroup::keygroup(const std::string& name, enum type t) throw(std::bad_alloc)
303 keygroups()[keyname = name] = this;
304 ktype = t;
305 state = 0;
306 last_rawval = 0;
307 cal_left = -32768;
308 cal_center = 0;
309 cal_right = 32767;
310 cal_tolerance = 0.5;
311 requests_hook = false;
314 keygroup::~keygroup() throw()
316 keygroups().erase(keyname);
319 void keygroup::change_type(enum type t) throw()
321 ktype = t;
322 state = 0;
323 if(requests_hook)
324 lua_callback_keyhook(keyname, get_parameters());
327 void keygroup::request_hook_callback(bool state)
329 requests_hook = state;
333 std::pair<keygroup*, unsigned> keygroup::lookup(const std::string& name) throw(std::bad_alloc,
334 std::runtime_error)
336 if(keygroups().count(name))
337 return std::make_pair(keygroups()[name], 0);
338 std::string prefix = name;
339 char letter = prefix[prefix.length() - 1];
340 prefix = prefix.substr(0, prefix.length() - 1);
341 if(!keygroups().count(prefix))
342 throw std::runtime_error("Invalid key");
343 keygroup* g = keygroups()[prefix];
344 switch(letter) {
345 case '+':
346 case 'n':
347 return std::make_pair(g, 0);
348 case '-':
349 case 'e':
350 return std::make_pair(g, 1);
351 case 's':
352 return std::make_pair(g, 2);
353 case 'w':
354 return std::make_pair(g, 3);
355 default:
356 throw std::runtime_error("Invalid key");
360 void keygroup::change_calibration(short left, short center, short right, double tolerance)
362 cal_left = left;
363 cal_center = center;
364 cal_right = right;
365 cal_tolerance = tolerance;
366 if(requests_hook)
367 lua_callback_keyhook(keyname, get_parameters());
370 double keygroup::compensate(short value)
372 if(ktype == KT_HAT || ktype == KT_KEY || ktype == KT_DISABLED || ktype == KT_MOUSE)
373 return value; //These can't be calibrated.
374 if(value <= cal_left)
375 return -1.0;
376 else if(value >= cal_right)
377 return 1.0;
378 else if(value == cal_center)
379 return 0.0;
380 else if(value < cal_center)
381 return (static_cast<double>(value) - cal_center) / (static_cast<double>(cal_center) - cal_left);
382 else
383 return (static_cast<double>(value) - cal_center) / (static_cast<double>(cal_right) - cal_center);
386 double keygroup::compensate2(double value)
388 switch(ktype) {
389 case KT_DISABLED:
390 case KT_MOUSE:
391 return 0; //Always neutral.
392 case KT_KEY:
393 case KT_HAT:
394 return value; //No mapping.
395 case KT_PRESSURE_0M:
396 return -value;
397 case KT_PRESSURE_0P:
398 return value;
399 case KT_PRESSURE_M0:
400 return 1 + value;
401 case KT_PRESSURE_MP:
402 return (1 + value) / 2;
403 case KT_PRESSURE_P0:
404 return 1 - value;
405 case KT_PRESSURE_PM:
406 return (1 - value) / 2;
407 case KT_AXIS_PAIR:
408 return value;
409 case KT_AXIS_PAIR_INVERSE:
410 return -value;
414 void keygroup::set_position(short pos, const modifier_set& modifiers) throw()
416 last_rawval = pos;
417 if(requests_hook)
418 lua_callback_keyhook(keyname, get_parameters());
419 double x = compensate2(compensate(pos));
420 unsigned tmp;
421 bool left, right, up, down;
422 bool oleft, oright, oup, odown;
423 switch(ktype) {
424 case KT_DISABLED:
425 case KT_MOUSE:
426 return;
427 case KT_KEY:
428 case KT_PRESSURE_0M:
429 case KT_PRESSURE_0P:
430 case KT_PRESSURE_M0:
431 case KT_PRESSURE_MP:
432 case KT_PRESSURE_P0:
433 case KT_PRESSURE_PM:
434 tmp = (x >= cal_tolerance);
435 run_listeners(modifiers, 0, true, (!state && tmp), x);
436 run_listeners(modifiers, 0, false, (state && !tmp), x);
437 state = tmp;
438 break;
439 case KT_AXIS_PAIR:
440 case KT_AXIS_PAIR_INVERSE:
441 if(x <= -cal_tolerance)
442 tmp = 2;
443 else if(x >= cal_tolerance)
444 tmp = 1;
445 else
446 tmp = 0;
447 run_listeners(modifiers, 0, false, state == 1 && tmp != 1, x);
448 run_listeners(modifiers, 1, false, state == 2 && tmp != 2, x);
449 run_listeners(modifiers, 0, true, tmp == 1 && state != 1, x);
450 run_listeners(modifiers, 1, true, tmp == 2 && state != 2, x);
451 state = tmp;
452 break;
453 case KT_HAT:
454 left = ((pos & 8) != 0);
455 right = ((pos & 2) != 0);
456 up = ((pos & 1) != 0);
457 down = ((pos & 4) != 0);
458 oleft = ((state & 8) != 0);
459 oright = ((state & 2) != 0);
460 oup = ((state & 1) != 0);
461 odown = ((state & 4) != 0);
462 run_listeners(modifiers, 3, false, oleft && !left, x);
463 run_listeners(modifiers, 1, false, oright && !right, x);
464 run_listeners(modifiers, 0, false, oup && !up, x);
465 run_listeners(modifiers, 2, false, odown && !down, x);
466 run_listeners(modifiers, 2, true, !odown && down, x);
467 run_listeners(modifiers, 0, true, !oup && up, x);
468 run_listeners(modifiers, 1, true, !oright && right, x);
469 run_listeners(modifiers, 3, true, !oleft && left, x);
470 state = pos;
471 break;
475 void keygroup::run_listeners(const modifier_set& modifiers, unsigned subkey, bool polarity, bool really, double x)
477 if(!really)
478 return;
479 std::string name = keyname;
480 if(ktype == KT_AXIS_PAIR && subkey == 0)
481 name = name + "+";
482 if(ktype == KT_AXIS_PAIR && subkey == 1)
483 name = name + "-";
484 if(ktype == KT_HAT && subkey == 0)
485 name = name + "n";
486 if(ktype == KT_HAT && subkey == 1)
487 name = name + "e";
488 if(ktype == KT_HAT && subkey == 2)
489 name = name + "s";
490 if(ktype == KT_HAT && subkey == 3)
491 name = name + "w";
492 information_dispatch::do_key_event(modifiers, *this, subkey, polarity, name);
495 keygroup* keygroup::lookup_by_name(const std::string& name) throw()
497 if(keygroups().count(name))
498 return keygroups()[name];
499 else
500 return NULL;
503 std::set<std::string> keygroup::get_axis_set() throw(std::bad_alloc)
505 std::set<std::string> r;
506 for(auto i : keygroups()) {
507 keygroup::parameters p = i.second->get_parameters();
508 std::string type = "";
509 switch(p.ktype) {
510 case keygroup::KT_DISABLED:
511 case keygroup::KT_AXIS_PAIR:
512 case keygroup::KT_AXIS_PAIR_INVERSE:
513 case keygroup::KT_PRESSURE_0M:
514 case keygroup::KT_PRESSURE_0P:
515 case keygroup::KT_PRESSURE_M0:
516 case keygroup::KT_PRESSURE_MP:
517 case keygroup::KT_PRESSURE_P0:
518 case keygroup::KT_PRESSURE_PM:
519 r.insert(i.first);
520 break;
521 default:
522 break;
525 return r;
528 std::set<std::string> keygroup::get_keys() throw(std::bad_alloc)
530 std::set<std::string> r;
531 for(auto i : keygroups()) {
532 switch(i.second->ktype) {
533 case KT_KEY:
534 case KT_PRESSURE_M0:
535 case KT_PRESSURE_MP:
536 case KT_PRESSURE_0M:
537 case KT_PRESSURE_0P:
538 case KT_PRESSURE_PM:
539 case KT_PRESSURE_P0:
540 r.insert(i.first);
541 break;
542 case KT_AXIS_PAIR:
543 case KT_AXIS_PAIR_INVERSE:
544 r.insert(i.first + "+");
545 r.insert(i.first + "-");
546 break;
547 case KT_HAT:
548 r.insert(i.first + "n");
549 r.insert(i.first + "e");
550 r.insert(i.first + "s");
551 r.insert(i.first + "w");
552 break;
553 default:
554 break;
557 return r;
561 namespace
564 function_ptr_command<tokensplitter&> set_axis("set-axis", "Set mode of Joystick axis",
565 "Syntax: set-axis <axis> <options>...\nKnown options: disabled, axis, axis-inverse, pressure0-\n"
566 "pressure0+, pressure-0, pressure-+, pressure+0, pressure+-\nminus=<val>, zero=<val>, plus=<val>\n"
567 "tolerance=<val>\n",
568 [](tokensplitter& t) throw(std::bad_alloc, std::runtime_error) {
569 struct keygroup::parameters p;
570 std::string axis = t;
571 if(axis == "")
572 throw std::runtime_error("Axis name required");
573 if(!keygroups().count(axis))
574 throw std::runtime_error("Unknown axis name");
575 p = keygroups()[axis]->get_parameters();
576 switch(p.ktype) {
577 case keygroup::KT_DISABLED:
578 case keygroup::KT_AXIS_PAIR:
579 case keygroup::KT_AXIS_PAIR_INVERSE:
580 case keygroup::KT_PRESSURE_0M:
581 case keygroup::KT_PRESSURE_0P:
582 case keygroup::KT_PRESSURE_M0:
583 case keygroup::KT_PRESSURE_MP:
584 case keygroup::KT_PRESSURE_P0:
585 case keygroup::KT_PRESSURE_PM:
586 break;
587 default:
588 throw std::runtime_error("Not an axis");
590 bool found_axismode = false;
591 bool found_minus = false;
592 bool found_zero = false;
593 bool found_plus = false;
594 bool found_tolerance = false;
595 while(!!t) {
596 std::string spec = t;
597 if(spec == "disabled") {
598 if(!found_axismode)
599 p.ktype = keygroup::KT_DISABLED;
600 else
601 throw std::runtime_error("Conflicting axis modes");
602 found_axismode = true;
603 } else if(spec == "axis") {
604 if(!found_axismode)
605 p.ktype = keygroup::KT_AXIS_PAIR;
606 else
607 throw std::runtime_error("Conflicting axis modes");
608 found_axismode = true;
609 } else if(spec == "axis-inverse") {
610 if(!found_axismode)
611 p.ktype = keygroup::KT_AXIS_PAIR_INVERSE;
612 else
613 throw std::runtime_error("Conflicting axis modes");
614 found_axismode = true;
615 } else if(spec == "pressure0-") {
616 if(!found_axismode)
617 p.ktype = keygroup::KT_PRESSURE_0M;
618 else
619 throw std::runtime_error("Conflicting axis modes");
620 found_axismode = true;
621 } else if(spec == "pressure0+") {
622 if(!found_axismode)
623 p.ktype = keygroup::KT_PRESSURE_0P;
624 else
625 throw std::runtime_error("Conflicting axis modes");
626 found_axismode = true;
627 } else if(spec == "pressure-0") {
628 if(!found_axismode)
629 p.ktype = keygroup::KT_PRESSURE_M0;
630 else
631 throw std::runtime_error("Conflicting axis modes");
632 found_axismode = true;
633 } else if(spec == "pressure-+") {
634 if(!found_axismode)
635 p.ktype = keygroup::KT_PRESSURE_MP;
636 else
637 throw std::runtime_error("Conflicting axis modes");
638 found_axismode = true;
639 } else if(spec == "pressure+0") {
640 if(!found_axismode)
641 p.ktype = keygroup::KT_PRESSURE_P0;
642 else
643 throw std::runtime_error("Conflicting axis modes");
644 found_axismode = true;
645 } else if(spec == "pressure+-") {
646 if(!found_axismode)
647 p.ktype = keygroup::KT_PRESSURE_PM;
648 else
649 throw std::runtime_error("Conflicting axis modes");
650 found_axismode = true;
651 } else if(spec.substr(0, 6) == "minus=") {
652 if(!found_minus)
653 p.cal_left = parse_value<int16_t>(spec.substr(6));
654 else
655 throw std::runtime_error("Conflicting minus value");
656 found_minus = true;
657 } else if(spec.substr(0, 5) == "zero=") {
658 if(!found_zero)
659 p.cal_center = parse_value<int16_t>(spec.substr(5));
660 else
661 throw std::runtime_error("Conflicting zero value");
662 found_zero = true;
663 } else if(spec.substr(0, 5) == "plus=") {
664 if(!found_plus)
665 p.cal_right = parse_value<int16_t>(spec.substr(5));
666 else
667 throw std::runtime_error("Conflicting plus value");
668 found_plus = true;
669 } else if(spec.substr(0, 10) == "tolerance=") {
670 if(!found_tolerance) {
671 p.cal_tolerance = parse_value<double>(spec.substr(10));
672 if(p.cal_tolerance <= 0 || p.cal_tolerance > 1)
673 throw std::runtime_error("Tolerance out of range");
674 } else
675 throw std::runtime_error("Conflicting tolerance value");
676 found_tolerance = true;
677 } else
678 throw std::runtime_error("Unknown axis modifier");
680 if(found_axismode)
681 keygroups()[axis]->change_type(p.ktype);
682 keygroups()[axis]->change_calibration(p.cal_left, p.cal_center, p.cal_right, p.cal_tolerance);
685 function_ptr_command<> set_axismode("show-axes", "Show all joystick axes",
686 "Syntax: show-axes\n",
687 []() throw(std::bad_alloc, std::runtime_error) {
688 for(auto i = keygroups().begin(); i != keygroups().end(); ++i) {
689 keygroup::parameters p = i->second->get_parameters();
690 std::string type = "";
691 switch(p.ktype) {
692 case keygroup::KT_DISABLED: type = "disabled"; break;
693 case keygroup::KT_AXIS_PAIR: type = "axis"; break;
694 case keygroup::KT_AXIS_PAIR_INVERSE: type = "axis-inverse"; break;
695 case keygroup::KT_PRESSURE_0M: type = "pressure0-"; break;
696 case keygroup::KT_PRESSURE_0P: type = "pressure0+"; break;
697 case keygroup::KT_PRESSURE_M0: type = "pressure-0"; break;
698 case keygroup::KT_PRESSURE_MP: type = "pressure-+"; break;
699 case keygroup::KT_PRESSURE_P0: type = "pressure+0"; break;
700 case keygroup::KT_PRESSURE_PM: type = "pressure+-"; break;
701 default: continue;
703 messages << i->first << " " << type << " -:" << p.cal_left << " 0:"
704 << p.cal_center << " +:" << p.cal_right << " t:" << p.cal_tolerance
705 << std::endl;
710 struct triple
712 triple(const std::string& _a, const std::string& _b, const std::string& _c)
714 a = _a;
715 b = _b;
716 c = _c;
718 std::string a;
719 std::string b;
720 std::string c;
721 bool operator==(const triple& t) const
723 bool x = (a == t.a && b == t.b && c == t.c);
724 return x;
726 bool operator<(const triple& t) const
728 bool x = (a < t.a || (a == t.a && b < t.b) || (a == t.a && b == t.b && c < t.c));
729 return x;
732 struct keybind_data : public information_dispatch
734 modifier_set mod;
735 modifier_set modmask;
736 keygroup* group;
737 unsigned subkey;
738 std::string command;
740 keybind_data() : information_dispatch("keybind-listener") {}
742 void on_key_event(const modifier_set& modifiers, keygroup& keygroup, unsigned _subkey, bool polarity,
743 const std::string& name)
745 if(!modifier_set::triggers(modifiers, mod, modmask))
746 return;
747 if(subkey != _subkey)
748 return;
749 if(&keygroup != group)
750 return;
751 std::string cmd = fixup_command_polarity(command, polarity);
752 if(cmd == "")
753 return;
754 command::invokeC(cmd);
758 triple parse_to_triple(const std::string& keyspec)
760 triple k("", "", "");
761 std::string _keyspec = keyspec;
762 size_t split1 = _keyspec.find_first_of("/");
763 size_t split2 = _keyspec.find_first_of("|");
764 if(split1 >= keyspec.length() || split2 >= keyspec.length() || split1 > split2)
765 throw std::runtime_error("Bad keyspec " + keyspec);
766 k.a = _keyspec.substr(0, split1);
767 k.b = _keyspec.substr(split1 + 1, split2 - split1 - 1);
768 k.c = _keyspec.substr(split2 + 1);
769 return k;
772 std::map<triple, keybind_data*> keybindings;
775 void keymapper::bind(std::string mod, std::string modmask, std::string keyname, std::string command)
776 throw(std::bad_alloc, std::runtime_error)
778 triple k(mod, modmask, keyname);
779 modifier_set _mod = modifier_set::construct(mod);
780 modifier_set _modmask = modifier_set::construct(modmask);
781 if(!modifier_set::valid(_mod, _modmask))
782 throw std::runtime_error("Invalid modifiers");
783 auto g = keygroup::lookup(keyname);
784 if(!keybindings.count(k)) {
785 keybindings[k] = new keybind_data;
786 keybindings[k]->mod = _mod;
787 keybindings[k]->modmask = _modmask;
788 keybindings[k]->group = g.first;
789 keybindings[k]->subkey = g.second;
791 keybindings[k]->command = command;
793 void keymapper::unbind(std::string mod, std::string modmask, std::string keyname) throw(std::bad_alloc,
794 std::runtime_error)
796 triple k(mod, modmask, keyname);
797 if(!keybindings.count(k))
798 throw std::runtime_error("Key is not bound");
799 delete keybindings[k];
800 keybindings.erase(k);
803 void keymapper::dumpbindings() throw(std::bad_alloc)
805 for(auto i : keybindings) {
806 messages << "bind-key ";
807 if(i.first.a != "" || i.first.b != "")
808 messages << i.first.a << "/" << i.first.b << " ";
809 messages << i.first.c << " " << i.second->command << std::endl;
813 std::set<std::string> keymapper::get_bindings() throw(std::bad_alloc)
815 std::set<std::string> r;
816 for(auto i : keybindings)
817 r.insert(i.first.a + "/" + i.first.b + "|" + i.first.c);
818 return r;
821 std::string keymapper::get_command_for(const std::string& keyspec) throw(std::bad_alloc)
823 triple k("", "", "");
824 try {
825 k = parse_to_triple(keyspec);
826 } catch(std::exception& e) {
827 return "";
829 if(!keybindings.count(k))
830 return "";
831 return keybindings[k]->command;
834 void keymapper::bind_for(const std::string& keyspec, const std::string& cmd) throw(std::bad_alloc, std::runtime_error)
836 triple k("", "", "");
837 k = parse_to_triple(keyspec);
838 if(cmd != "")
839 bind(k.a, k.b, k.c, cmd);
840 else
841 unbind(k.a, k.b, k.c);