Lua: New function input.raw, which gives data for all buttons
[lsnes.git] / src / core / keymapper.cpp
blobdb0873137a268e68797555d5c304039f95ccf796
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"
10 #include <stdexcept>
11 #include <stdexcept>
12 #include <iostream>
13 #include <list>
14 #include <map>
15 #include <sstream>
16 #include <set>
18 namespace
20 function_ptr_command<tokensplitter&> bind_key("bind-key", "Bind a (pseudo-)key",
21 "Syntax: bind-key [<mod>/<modmask>] <key> <command>\nBind command to specified key (with specified "
22 " modifiers)\n",
23 [](tokensplitter& t) throw(std::bad_alloc, std::runtime_error) {
24 std::string mod, modmask, keyname, command;
25 std::string mod_or_key = t;
26 if(mod_or_key.find_first_of("/") < mod_or_key.length()) {
27 //Mod field.
28 size_t split = mod_or_key.find_first_of("/");
29 mod = mod_or_key.substr(0, split);
30 modmask = mod_or_key.substr(split + 1);
31 mod_or_key = static_cast<std::string>(t);
33 if(mod_or_key == "")
34 throw std::runtime_error("Expected optional modifiers and key");
35 keyname = mod_or_key;
36 command = t.tail();
37 if(command == "")
38 throw std::runtime_error("Expected command");
39 keymapper::bind(mod, modmask, keyname, command);
40 if(mod != "" || modmask != "")
41 messages << mod << "/" << modmask << " ";
42 messages << keyname << " bound to '" << command << "'" << std::endl;
44 });
46 function_ptr_command<tokensplitter&> unbind_key("unbind-key", "Unbind a (pseudo-)key",
47 "Syntax: unbind-key [<mod>/<modmask>] <key>\nUnbind specified key (with specified modifiers)\n",
48 [](tokensplitter& t) throw(std::bad_alloc, std::runtime_error) {
49 std::string mod, modmask, keyname, command;
50 std::string mod_or_key = t;
51 if(mod_or_key.find_first_of("/") < mod_or_key.length()) {
52 //Mod field.
53 size_t split = mod_or_key.find_first_of("/");
54 mod = mod_or_key.substr(0, split);
55 modmask = mod_or_key.substr(split + 1);
56 mod_or_key = static_cast<std::string>(t);
58 if(mod_or_key == "")
59 throw std::runtime_error("Expected optional modifiers and key");
60 keyname = mod_or_key;
61 command = t.tail();
62 if(command != "")
63 throw std::runtime_error("Unexpected argument");
64 keymapper::unbind(mod, modmask, keyname);
65 if(mod != "" || modmask != "")
66 messages << mod << "/" << modmask << " ";
67 messages << keyname << " unbound" << std::endl;
68 });
70 function_ptr_command<> show_bindings("show-bindings", "Show active bindings",
71 "Syntax: show-bindings\nShow bindings that are currently active.\n",
72 []() throw(std::bad_alloc, std::runtime_error) {
73 keymapper::dumpbindings();
74 });
77 std::string fixup_command_polarity(std::string cmd, bool polarity) throw(std::bad_alloc)
79 if(cmd == "" || cmd == "*")
80 return "";
81 if(cmd[0] != '*') {
82 if(cmd[0] != '+' && polarity)
83 return "";
84 if(cmd[0] == '+' && !polarity)
85 cmd[0] = '-';
86 } else {
87 if(cmd[1] != '+' && polarity)
88 return "";
89 if(cmd[1] == '+' && !polarity)
90 cmd[1] = '-';
92 return cmd;
95 namespace
97 globalwrap<std::map<std::string, modifier*>> known_modifiers;
98 globalwrap<std::map<std::string, std::string>> modifier_linkages;
99 globalwrap<std::map<std::string, keygroup*>> keygroups;
101 //Returns orig if not linked.
102 const modifier* get_linked_modifier(const modifier* orig)
104 if(!modifier_linkages().count(orig->name()))
105 return orig;
106 std::string l = modifier_linkages()[orig->name()];
107 return known_modifiers()[l];
111 modifier::modifier(const std::string& name) throw(std::bad_alloc)
113 known_modifiers()[modname = name] = this;
116 modifier::modifier(const std::string& name, const std::string& linkgroup) throw(std::bad_alloc)
118 known_modifiers()[modname = name] = this;
119 modifier_linkages()[name] = linkgroup;
122 modifier::~modifier() throw()
124 known_modifiers().erase(modname);
125 modifier_linkages().erase(modname);
128 std::set<std::string> modifier::get_set() throw(std::bad_alloc)
130 std::set<std::string> r;
131 for(auto i : known_modifiers())
132 r.insert(i.first);
133 return r;
136 modifier& modifier::lookup(const std::string& name) throw(std::bad_alloc, std::runtime_error)
138 if(!known_modifiers().count(name)) {
139 std::ostringstream x;
140 x << "Invalid modifier '" << name << "'";
141 throw std::runtime_error(x.str());
143 return *known_modifiers()[name];
146 std::string modifier::name() const throw(std::bad_alloc)
148 return modname;
151 std::string modifier::linked_name() const throw(std::bad_alloc)
153 const modifier* p = get_linked_modifier(this);
154 if(p == this)
155 return "";
156 return p->modname;
160 void modifier_set::add(const modifier& mod, bool really) throw(std::bad_alloc)
162 if(really)
163 set.insert(&mod);
166 void modifier_set::remove(const modifier& mod, bool really) throw(std::bad_alloc)
168 if(really)
169 set.erase(&mod);
172 modifier_set modifier_set::construct(const std::string& _modifiers) throw(std::bad_alloc, std::runtime_error)
174 modifier_set set;
175 std::string modifiers = _modifiers;
176 while(modifiers != "") {
177 std::string mod = modifiers;
178 std::string rest;
179 size_t split = modifiers.find_first_of(",");
180 if(split < modifiers.length()) {
181 mod = modifiers.substr(0, split);
182 rest = modifiers.substr(split + 1);
184 set.add(modifier::lookup(mod));
185 modifiers = rest;
187 return set;
190 bool modifier_set::valid(const modifier_set& set, const modifier_set& mask) throw(std::bad_alloc)
192 //No element can be together with its linkage group.
193 for(auto i : set.set) {
194 const modifier* j = get_linked_modifier(i);
195 if(i != j && set.set.count(j))
196 return false;
198 for(auto i : mask.set) {
199 const modifier* j = get_linked_modifier(i);
200 if(i != j && mask.set.count(j))
201 return false;
203 //For every element of set, it or its linkage group must be in mask.
204 for(auto i : set.set) {
205 const modifier* j = get_linked_modifier(i);
206 if(!mask.set.count(i) && !mask.set.count(j))
207 return false;
209 return true;
212 bool modifier_set::operator==(const modifier_set& m) const throw()
214 for(auto i : set)
215 if(!m.set.count(i))
216 return false;
217 for(auto i : m.set)
218 if(!set.count(i))
219 return false;
220 return true;
223 std::ostream& operator<<(std::ostream& os, const modifier_set& m)
225 os << "<modset:";
226 for(auto i : m.set)
227 os << i->name() << " ";
228 os << ">";
229 return os;
232 bool modifier_set::triggers(const modifier_set& set, const modifier_set& trigger, const modifier_set& mask)
233 throw(std::bad_alloc)
235 for(auto i : mask.set) {
236 bool ok = false;
237 //OK iff at least one of:
238 //Key itself appears in both set and trigger.
239 for(auto j : set.set) {
240 if(!trigger.set.count(j))
241 continue;
242 ok = true;
244 //Key with this linkage group appears in both set and trigger.
245 for(auto j : set.set) {
246 auto linked = get_linked_modifier(j);
247 if(linked != i)
248 continue;
249 if(!trigger.set.count(j))
250 continue;
251 ok = true;
253 //Key with this linkage appears in set and the key itself appears in trigger.
254 for(auto j : set.set) {
255 auto linked = get_linked_modifier(j);
256 if(linked != i)
257 continue;
258 if(!trigger.set.count(i))
259 continue;
260 ok = true;
262 //Nothing linked is found from neither set nor trigger.
263 bool found = false;
264 for(auto j : set.set)
265 found = found || (j == i || get_linked_modifier(j) == i);
266 for(auto j : trigger.set)
267 found = found || (j == i || get_linked_modifier(j) == i);
268 ok = ok || !found;
269 if(!ok)
270 return false;
272 return true;
275 std::string keygroup::name() throw(std::bad_alloc)
277 return keyname;
280 struct keygroup::parameters keygroup::get_parameters()
282 parameters p;
283 p.ktype = ktype;
284 p.cal_left = cal_left;
285 p.cal_center = cal_center;
286 p.cal_right = cal_right;
287 p.cal_tolerance = cal_tolerance;
288 p.last_rawval = last_rawval;
289 return p;
292 std::map<std::string, struct keygroup::parameters> keygroup::get_all_parameters()
294 std::map<std::string, struct parameters> ret;
295 for(auto i : keygroups())
296 ret[i.first] = i.second->get_parameters();
297 return ret;
300 keygroup::keygroup(const std::string& name, enum type t) throw(std::bad_alloc)
302 keygroups()[keyname = name] = this;
303 ktype = t;
304 state = 0;
305 last_rawval = 0;
306 cal_left = -32768;
307 cal_center = 0;
308 cal_right = 32767;
309 cal_tolerance = 0.5;
312 keygroup::~keygroup() throw()
314 keygroups().erase(keyname);
317 void keygroup::change_type(enum type t) throw()
319 ktype = t;
320 state = 0;
323 std::pair<keygroup*, unsigned> keygroup::lookup(const std::string& name) throw(std::bad_alloc,
324 std::runtime_error)
326 if(keygroups().count(name))
327 return std::make_pair(keygroups()[name], 0);
328 std::string prefix = name;
329 char letter = prefix[prefix.length() - 1];
330 prefix = prefix.substr(0, prefix.length() - 1);
331 if(!keygroups().count(prefix))
332 throw std::runtime_error("Invalid key");
333 keygroup* g = keygroups()[prefix];
334 switch(letter) {
335 case '+':
336 case 'n':
337 return std::make_pair(g, 0);
338 case '-':
339 case 'e':
340 return std::make_pair(g, 1);
341 case 's':
342 return std::make_pair(g, 2);
343 case 'w':
344 return std::make_pair(g, 3);
345 default:
346 throw std::runtime_error("Invalid key");
350 void keygroup::change_calibration(short left, short center, short right, double tolerance)
352 cal_left = left;
353 cal_center = center;
354 cal_right = right;
355 cal_tolerance = tolerance;
358 double keygroup::compensate(short value)
360 if(ktype == KT_HAT || ktype == KT_KEY || ktype == KT_DISABLED || ktype == KT_MOUSE)
361 return value; //These can't be calibrated.
362 if(value <= cal_left)
363 return -1.0;
364 else if(value >= cal_right)
365 return 1.0;
366 else if(value == cal_center)
367 return 0.0;
368 else if(value < cal_center)
369 return (static_cast<double>(value) - cal_center) / (static_cast<double>(cal_center) - cal_left);
370 else
371 return (static_cast<double>(value) - cal_center) / (static_cast<double>(cal_right) - cal_center);
374 double keygroup::compensate2(double value)
376 switch(ktype) {
377 case KT_DISABLED:
378 case KT_MOUSE:
379 return 0; //Always neutral.
380 case KT_KEY:
381 case KT_HAT:
382 return value; //No mapping.
383 case KT_PRESSURE_0M:
384 return -value;
385 case KT_PRESSURE_0P:
386 return value;
387 case KT_PRESSURE_M0:
388 return 1 + value;
389 case KT_PRESSURE_MP:
390 return (1 + value) / 2;
391 case KT_PRESSURE_P0:
392 return 1 - value;
393 case KT_PRESSURE_PM:
394 return (1 - value) / 2;
395 case KT_AXIS_PAIR:
396 return value;
397 case KT_AXIS_PAIR_INVERSE:
398 return -value;
402 void keygroup::set_position(short pos, const modifier_set& modifiers) throw()
404 last_rawval = pos;
405 double x = compensate2(compensate(pos));
406 unsigned tmp;
407 bool left, right, up, down;
408 bool oleft, oright, oup, odown;
409 switch(ktype) {
410 case KT_DISABLED:
411 case KT_MOUSE:
412 return;
413 case KT_KEY:
414 case KT_PRESSURE_0M:
415 case KT_PRESSURE_0P:
416 case KT_PRESSURE_M0:
417 case KT_PRESSURE_MP:
418 case KT_PRESSURE_P0:
419 case KT_PRESSURE_PM:
420 tmp = (x >= cal_tolerance);
421 run_listeners(modifiers, 0, true, (!state && tmp), x);
422 run_listeners(modifiers, 0, false, (state && !tmp), x);
423 state = tmp;
424 break;
425 case KT_AXIS_PAIR:
426 case KT_AXIS_PAIR_INVERSE:
427 if(x <= -cal_tolerance)
428 tmp = 2;
429 else if(x >= cal_tolerance)
430 tmp = 1;
431 else
432 tmp = 0;
433 run_listeners(modifiers, 0, false, state == 1 && tmp != 1, x);
434 run_listeners(modifiers, 1, false, state == 2 && tmp != 2, x);
435 run_listeners(modifiers, 0, true, tmp == 1 && state != 1, x);
436 run_listeners(modifiers, 1, true, tmp == 2 && state != 2, x);
437 state = tmp;
438 break;
439 case KT_HAT:
440 left = ((pos & 8) != 0);
441 right = ((pos & 2) != 0);
442 up = ((pos & 1) != 0);
443 down = ((pos & 4) != 0);
444 oleft = ((state & 8) != 0);
445 oright = ((state & 2) != 0);
446 oup = ((state & 1) != 0);
447 odown = ((state & 4) != 0);
448 run_listeners(modifiers, 3, false, oleft && !left, x);
449 run_listeners(modifiers, 1, false, oright && !right, x);
450 run_listeners(modifiers, 0, false, oup && !up, x);
451 run_listeners(modifiers, 2, false, odown && !down, x);
452 run_listeners(modifiers, 2, true, !odown && down, x);
453 run_listeners(modifiers, 0, true, !oup && up, x);
454 run_listeners(modifiers, 1, true, !oright && right, x);
455 run_listeners(modifiers, 3, true, !oleft && left, x);
456 state = pos;
457 break;
461 void keygroup::run_listeners(const modifier_set& modifiers, unsigned subkey, bool polarity, bool really, double x)
463 if(!really)
464 return;
465 std::string name = keyname;
466 if(ktype == KT_AXIS_PAIR && subkey == 0)
467 name = name + "+";
468 if(ktype == KT_AXIS_PAIR && subkey == 1)
469 name = name + "-";
470 if(ktype == KT_HAT && subkey == 0)
471 name = name + "n";
472 if(ktype == KT_HAT && subkey == 1)
473 name = name + "e";
474 if(ktype == KT_HAT && subkey == 2)
475 name = name + "s";
476 if(ktype == KT_HAT && subkey == 3)
477 name = name + "w";
478 information_dispatch::do_key_event(modifiers, *this, subkey, polarity, name);
481 keygroup* keygroup::lookup_by_name(const std::string& name) throw()
483 if(keygroups().count(name))
484 return keygroups()[name];
485 else
486 return NULL;
489 std::set<std::string> keygroup::get_axis_set() throw(std::bad_alloc)
491 std::set<std::string> r;
492 for(auto i : keygroups()) {
493 keygroup::parameters p = i.second->get_parameters();
494 std::string type = "";
495 switch(p.ktype) {
496 case keygroup::KT_DISABLED:
497 case keygroup::KT_AXIS_PAIR:
498 case keygroup::KT_AXIS_PAIR_INVERSE:
499 case keygroup::KT_PRESSURE_0M:
500 case keygroup::KT_PRESSURE_0P:
501 case keygroup::KT_PRESSURE_M0:
502 case keygroup::KT_PRESSURE_MP:
503 case keygroup::KT_PRESSURE_P0:
504 case keygroup::KT_PRESSURE_PM:
505 r.insert(i.first);
506 break;
507 default:
508 break;
511 return r;
514 std::set<std::string> keygroup::get_keys() throw(std::bad_alloc)
516 std::set<std::string> r;
517 for(auto i : keygroups()) {
518 switch(i.second->ktype) {
519 case KT_KEY:
520 case KT_PRESSURE_M0:
521 case KT_PRESSURE_MP:
522 case KT_PRESSURE_0M:
523 case KT_PRESSURE_0P:
524 case KT_PRESSURE_PM:
525 case KT_PRESSURE_P0:
526 r.insert(i.first);
527 break;
528 case KT_AXIS_PAIR:
529 case KT_AXIS_PAIR_INVERSE:
530 r.insert(i.first + "+");
531 r.insert(i.first + "-");
532 break;
533 case KT_HAT:
534 r.insert(i.first + "n");
535 r.insert(i.first + "e");
536 r.insert(i.first + "s");
537 r.insert(i.first + "w");
538 break;
539 default:
540 break;
543 return r;
547 namespace
550 function_ptr_command<tokensplitter&> set_axis("set-axis", "Set mode of Joystick axis",
551 "Syntax: set-axis <axis> <options>...\nKnown options: disabled, axis, axis-inverse, pressure0-\n"
552 "pressure0+, pressure-0, pressure-+, pressure+0, pressure+-\nminus=<val>, zero=<val>, plus=<val>\n"
553 "tolerance=<val>\n",
554 [](tokensplitter& t) throw(std::bad_alloc, std::runtime_error) {
555 struct keygroup::parameters p;
556 std::string axis = t;
557 if(axis == "")
558 throw std::runtime_error("Axis name required");
559 if(!keygroups().count(axis))
560 throw std::runtime_error("Unknown axis name");
561 p = keygroups()[axis]->get_parameters();
562 switch(p.ktype) {
563 case keygroup::KT_DISABLED:
564 case keygroup::KT_AXIS_PAIR:
565 case keygroup::KT_AXIS_PAIR_INVERSE:
566 case keygroup::KT_PRESSURE_0M:
567 case keygroup::KT_PRESSURE_0P:
568 case keygroup::KT_PRESSURE_M0:
569 case keygroup::KT_PRESSURE_MP:
570 case keygroup::KT_PRESSURE_P0:
571 case keygroup::KT_PRESSURE_PM:
572 break;
573 default:
574 throw std::runtime_error("Not an axis");
576 bool found_axismode = false;
577 bool found_minus = false;
578 bool found_zero = false;
579 bool found_plus = false;
580 bool found_tolerance = false;
581 while(!!t) {
582 std::string spec = t;
583 if(spec == "disabled") {
584 if(!found_axismode)
585 p.ktype = keygroup::KT_DISABLED;
586 else
587 throw std::runtime_error("Conflicting axis modes");
588 found_axismode = true;
589 } else if(spec == "axis") {
590 if(!found_axismode)
591 p.ktype = keygroup::KT_AXIS_PAIR;
592 else
593 throw std::runtime_error("Conflicting axis modes");
594 found_axismode = true;
595 } else if(spec == "axis-inverse") {
596 if(!found_axismode)
597 p.ktype = keygroup::KT_AXIS_PAIR_INVERSE;
598 else
599 throw std::runtime_error("Conflicting axis modes");
600 found_axismode = true;
601 } else if(spec == "pressure0-") {
602 if(!found_axismode)
603 p.ktype = keygroup::KT_PRESSURE_0M;
604 else
605 throw std::runtime_error("Conflicting axis modes");
606 found_axismode = true;
607 } else if(spec == "pressure0+") {
608 if(!found_axismode)
609 p.ktype = keygroup::KT_PRESSURE_0P;
610 else
611 throw std::runtime_error("Conflicting axis modes");
612 found_axismode = true;
613 } else if(spec == "pressure-0") {
614 if(!found_axismode)
615 p.ktype = keygroup::KT_PRESSURE_M0;
616 else
617 throw std::runtime_error("Conflicting axis modes");
618 found_axismode = true;
619 } else if(spec == "pressure-+") {
620 if(!found_axismode)
621 p.ktype = keygroup::KT_PRESSURE_MP;
622 else
623 throw std::runtime_error("Conflicting axis modes");
624 found_axismode = true;
625 } else if(spec == "pressure+0") {
626 if(!found_axismode)
627 p.ktype = keygroup::KT_PRESSURE_P0;
628 else
629 throw std::runtime_error("Conflicting axis modes");
630 found_axismode = true;
631 } else if(spec == "pressure+-") {
632 if(!found_axismode)
633 p.ktype = keygroup::KT_PRESSURE_PM;
634 else
635 throw std::runtime_error("Conflicting axis modes");
636 found_axismode = true;
637 } else if(spec.substr(0, 6) == "minus=") {
638 if(!found_minus)
639 p.cal_left = parse_value<int16_t>(spec.substr(6));
640 else
641 throw std::runtime_error("Conflicting minus value");
642 found_minus = true;
643 } else if(spec.substr(0, 5) == "zero=") {
644 if(!found_zero)
645 p.cal_center = parse_value<int16_t>(spec.substr(5));
646 else
647 throw std::runtime_error("Conflicting zero value");
648 found_zero = true;
649 } else if(spec.substr(0, 5) == "plus=") {
650 if(!found_plus)
651 p.cal_right = parse_value<int16_t>(spec.substr(5));
652 else
653 throw std::runtime_error("Conflicting plus value");
654 found_plus = true;
655 } else if(spec.substr(0, 10) == "tolerance=") {
656 if(!found_tolerance) {
657 p.cal_tolerance = parse_value<double>(spec.substr(10));
658 if(p.cal_tolerance <= 0 || p.cal_tolerance > 1)
659 throw std::runtime_error("Tolerance out of range");
660 } else
661 throw std::runtime_error("Conflicting tolerance value");
662 found_tolerance = true;
663 } else
664 throw std::runtime_error("Unknown axis modifier");
666 if(found_axismode)
667 keygroups()[axis]->change_type(p.ktype);
668 keygroups()[axis]->change_calibration(p.cal_left, p.cal_center, p.cal_right, p.cal_tolerance);
671 function_ptr_command<> set_axismode("show-axes", "Show all joystick axes",
672 "Syntax: show-axes\n",
673 []() throw(std::bad_alloc, std::runtime_error) {
674 for(auto i = keygroups().begin(); i != keygroups().end(); ++i) {
675 keygroup::parameters p = i->second->get_parameters();
676 std::string type = "";
677 switch(p.ktype) {
678 case keygroup::KT_DISABLED: type = "disabled"; break;
679 case keygroup::KT_AXIS_PAIR: type = "axis"; break;
680 case keygroup::KT_AXIS_PAIR_INVERSE: type = "axis-inverse"; break;
681 case keygroup::KT_PRESSURE_0M: type = "pressure0-"; break;
682 case keygroup::KT_PRESSURE_0P: type = "pressure0+"; break;
683 case keygroup::KT_PRESSURE_M0: type = "pressure-0"; break;
684 case keygroup::KT_PRESSURE_MP: type = "pressure-+"; break;
685 case keygroup::KT_PRESSURE_P0: type = "pressure+0"; break;
686 case keygroup::KT_PRESSURE_PM: type = "pressure+-"; break;
687 default: continue;
689 messages << i->first << " " << type << " -:" << p.cal_left << " 0:"
690 << p.cal_center << " +:" << p.cal_right << " t:" << p.cal_tolerance
691 << std::endl;
696 struct triple
698 triple(const std::string& _a, const std::string& _b, const std::string& _c)
700 a = _a;
701 b = _b;
702 c = _c;
704 std::string a;
705 std::string b;
706 std::string c;
707 bool operator==(const triple& t) const
709 bool x = (a == t.a && b == t.b && c == t.c);
710 return x;
712 bool operator<(const triple& t) const
714 bool x = (a < t.a || (a == t.a && b < t.b) || (a == t.a && b == t.b && c < t.c));
715 return x;
718 struct keybind_data : public information_dispatch
720 modifier_set mod;
721 modifier_set modmask;
722 keygroup* group;
723 unsigned subkey;
724 std::string command;
726 keybind_data() : information_dispatch("keybind-listener") {}
728 void on_key_event(const modifier_set& modifiers, keygroup& keygroup, unsigned _subkey, bool polarity,
729 const std::string& name)
731 if(!modifier_set::triggers(modifiers, mod, modmask))
732 return;
733 if(subkey != _subkey)
734 return;
735 if(&keygroup != group)
736 return;
737 std::string cmd = fixup_command_polarity(command, polarity);
738 if(cmd == "")
739 return;
740 command::invokeC(cmd);
744 triple parse_to_triple(const std::string& keyspec)
746 triple k("", "", "");
747 std::string _keyspec = keyspec;
748 size_t split1 = _keyspec.find_first_of("/");
749 size_t split2 = _keyspec.find_first_of("|");
750 if(split1 >= keyspec.length() || split2 >= keyspec.length() || split1 > split2)
751 throw std::runtime_error("Bad keyspec " + keyspec);
752 k.a = _keyspec.substr(0, split1);
753 k.b = _keyspec.substr(split1 + 1, split2 - split1 - 1);
754 k.c = _keyspec.substr(split2 + 1);
755 return k;
758 std::map<triple, keybind_data*> keybindings;
761 void keymapper::bind(std::string mod, std::string modmask, std::string keyname, std::string command)
762 throw(std::bad_alloc, std::runtime_error)
764 triple k(mod, modmask, keyname);
765 modifier_set _mod = modifier_set::construct(mod);
766 modifier_set _modmask = modifier_set::construct(modmask);
767 if(!modifier_set::valid(_mod, _modmask))
768 throw std::runtime_error("Invalid modifiers");
769 auto g = keygroup::lookup(keyname);
770 if(!keybindings.count(k)) {
771 keybindings[k] = new keybind_data;
772 keybindings[k]->mod = _mod;
773 keybindings[k]->modmask = _modmask;
774 keybindings[k]->group = g.first;
775 keybindings[k]->subkey = g.second;
777 keybindings[k]->command = command;
779 void keymapper::unbind(std::string mod, std::string modmask, std::string keyname) throw(std::bad_alloc,
780 std::runtime_error)
782 triple k(mod, modmask, keyname);
783 if(!keybindings.count(k))
784 throw std::runtime_error("Key is not bound");
785 delete keybindings[k];
786 keybindings.erase(k);
789 void keymapper::dumpbindings() throw(std::bad_alloc)
791 for(auto i : keybindings) {
792 messages << "bind-key ";
793 if(i.first.a != "" || i.first.b != "")
794 messages << i.first.a << "/" << i.first.b << " ";
795 messages << i.first.c << " " << i.second->command << std::endl;
799 std::set<std::string> keymapper::get_bindings() throw(std::bad_alloc)
801 std::set<std::string> r;
802 for(auto i : keybindings)
803 r.insert(i.first.a + "/" + i.first.b + "|" + i.first.c);
804 return r;
807 std::string keymapper::get_command_for(const std::string& keyspec) throw(std::bad_alloc)
809 triple k("", "", "");
810 try {
811 k = parse_to_triple(keyspec);
812 } catch(std::exception& e) {
813 return "";
815 if(!keybindings.count(k))
816 return "";
817 return keybindings[k]->command;
820 void keymapper::bind_for(const std::string& keyspec, const std::string& cmd) throw(std::bad_alloc, std::runtime_error)
822 triple k("", "", "");
823 k = parse_to_triple(keyspec);
824 if(cmd != "")
825 bind(k.a, k.b, k.c, cmd);
826 else
827 unbind(k.a, k.b, k.c);