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"
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 "
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()) {
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
);
34 throw std::runtime_error("Expected optional modifiers and key");
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
;
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()) {
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
);
59 throw std::runtime_error("Expected optional modifiers and key");
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
;
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();
77 std::string
fixup_command_polarity(std::string cmd
, bool polarity
) throw(std::bad_alloc
)
79 if(cmd
== "" || cmd
== "*")
82 if(cmd
[0] != '+' && polarity
)
84 if(cmd
[0] == '+' && !polarity
)
87 if(cmd
[1] != '+' && polarity
)
89 if(cmd
[1] == '+' && !polarity
)
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()))
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())
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
)
151 std::string
modifier::linked_name() const throw(std::bad_alloc
)
153 const modifier
* p
= get_linked_modifier(this);
160 void modifier_set::add(const modifier
& mod
, bool really
) throw(std::bad_alloc
)
166 void modifier_set::remove(const modifier
& mod
, bool really
) throw(std::bad_alloc
)
172 modifier_set
modifier_set::construct(const std::string
& _modifiers
) throw(std::bad_alloc
, std::runtime_error
)
175 std::string modifiers
= _modifiers
;
176 while(modifiers
!= "") {
177 std::string mod
= modifiers
;
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
));
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
))
198 for(auto i
: mask
.set
) {
199 const modifier
* j
= get_linked_modifier(i
);
200 if(i
!= j
&& mask
.set
.count(j
))
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
))
212 bool modifier_set::operator==(const modifier_set
& m
) const throw()
223 std::ostream
& operator<<(std::ostream
& os
, const modifier_set
& m
)
227 os
<< i
->name() << " ";
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
) {
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
))
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
);
249 if(!trigger
.set
.count(j
))
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
);
258 if(!trigger
.set
.count(i
))
262 //Nothing linked is found from neither set nor trigger.
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
);
275 std::string
keygroup::name() throw(std::bad_alloc
)
280 struct keygroup::parameters
keygroup::get_parameters()
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
;
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();
300 keygroup::keygroup(const std::string
& name
, enum type t
) throw(std::bad_alloc
)
302 keygroups()[keyname
= name
] = this;
312 keygroup::~keygroup() throw()
314 keygroups().erase(keyname
);
317 void keygroup::change_type(enum type t
) throw()
323 std::pair
<keygroup
*, unsigned> keygroup::lookup(const std::string
& name
) throw(std::bad_alloc
,
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
];
337 return std::make_pair(g
, 0);
340 return std::make_pair(g
, 1);
342 return std::make_pair(g
, 2);
344 return std::make_pair(g
, 3);
346 throw std::runtime_error("Invalid key");
350 void keygroup::change_calibration(short left
, short center
, short right
, double tolerance
)
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
)
364 else if(value
>= cal_right
)
366 else if(value
== cal_center
)
368 else if(value
< cal_center
)
369 return (static_cast<double>(value
) - cal_center
) / (static_cast<double>(cal_center
) - cal_left
);
371 return (static_cast<double>(value
) - cal_center
) / (static_cast<double>(cal_right
) - cal_center
);
374 double keygroup::compensate2(double value
)
379 return 0; //Always neutral.
382 return value
; //No mapping.
390 return (1 + value
) / 2;
394 return (1 - value
) / 2;
397 case KT_AXIS_PAIR_INVERSE
:
402 void keygroup::set_position(short pos
, const modifier_set
& modifiers
) throw()
405 double x
= compensate2(compensate(pos
));
407 bool left
, right
, up
, down
;
408 bool oleft
, oright
, oup
, odown
;
420 tmp
= (x
>= cal_tolerance
);
421 run_listeners(modifiers
, 0, true, (!state
&& tmp
), x
);
422 run_listeners(modifiers
, 0, false, (state
&& !tmp
), x
);
426 case KT_AXIS_PAIR_INVERSE
:
427 if(x
<= -cal_tolerance
)
429 else if(x
>= cal_tolerance
)
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
);
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
);
461 void keygroup::run_listeners(const modifier_set
& modifiers
, unsigned subkey
, bool polarity
, bool really
, double x
)
465 std::string name
= keyname
;
466 if(ktype
== KT_AXIS_PAIR
&& subkey
== 0)
468 if(ktype
== KT_AXIS_PAIR
&& subkey
== 1)
470 if(ktype
== KT_HAT
&& subkey
== 0)
472 if(ktype
== KT_HAT
&& subkey
== 1)
474 if(ktype
== KT_HAT
&& subkey
== 2)
476 if(ktype
== KT_HAT
&& subkey
== 3)
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
];
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
= "";
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
:
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
) {
529 case KT_AXIS_PAIR_INVERSE
:
530 r
.insert(i
.first
+ "+");
531 r
.insert(i
.first
+ "-");
534 r
.insert(i
.first
+ "n");
535 r
.insert(i
.first
+ "e");
536 r
.insert(i
.first
+ "s");
537 r
.insert(i
.first
+ "w");
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"
554 [](tokensplitter
& t
) throw(std::bad_alloc
, std::runtime_error
) {
555 struct keygroup::parameters p
;
556 std::string axis
= t
;
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();
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
:
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;
582 std::string spec
= t
;
583 if(spec
== "disabled") {
585 p
.ktype
= keygroup::KT_DISABLED
;
587 throw std::runtime_error("Conflicting axis modes");
588 found_axismode
= true;
589 } else if(spec
== "axis") {
591 p
.ktype
= keygroup::KT_AXIS_PAIR
;
593 throw std::runtime_error("Conflicting axis modes");
594 found_axismode
= true;
595 } else if(spec
== "axis-inverse") {
597 p
.ktype
= keygroup::KT_AXIS_PAIR_INVERSE
;
599 throw std::runtime_error("Conflicting axis modes");
600 found_axismode
= true;
601 } else if(spec
== "pressure0-") {
603 p
.ktype
= keygroup::KT_PRESSURE_0M
;
605 throw std::runtime_error("Conflicting axis modes");
606 found_axismode
= true;
607 } else if(spec
== "pressure0+") {
609 p
.ktype
= keygroup::KT_PRESSURE_0P
;
611 throw std::runtime_error("Conflicting axis modes");
612 found_axismode
= true;
613 } else if(spec
== "pressure-0") {
615 p
.ktype
= keygroup::KT_PRESSURE_M0
;
617 throw std::runtime_error("Conflicting axis modes");
618 found_axismode
= true;
619 } else if(spec
== "pressure-+") {
621 p
.ktype
= keygroup::KT_PRESSURE_MP
;
623 throw std::runtime_error("Conflicting axis modes");
624 found_axismode
= true;
625 } else if(spec
== "pressure+0") {
627 p
.ktype
= keygroup::KT_PRESSURE_P0
;
629 throw std::runtime_error("Conflicting axis modes");
630 found_axismode
= true;
631 } else if(spec
== "pressure+-") {
633 p
.ktype
= keygroup::KT_PRESSURE_PM
;
635 throw std::runtime_error("Conflicting axis modes");
636 found_axismode
= true;
637 } else if(spec
.substr(0, 6) == "minus=") {
639 p
.cal_left
= parse_value
<int16_t>(spec
.substr(6));
641 throw std::runtime_error("Conflicting minus value");
643 } else if(spec
.substr(0, 5) == "zero=") {
645 p
.cal_center
= parse_value
<int16_t>(spec
.substr(5));
647 throw std::runtime_error("Conflicting zero value");
649 } else if(spec
.substr(0, 5) == "plus=") {
651 p
.cal_right
= parse_value
<int16_t>(spec
.substr(5));
653 throw std::runtime_error("Conflicting plus value");
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");
661 throw std::runtime_error("Conflicting tolerance value");
662 found_tolerance
= true;
664 throw std::runtime_error("Unknown axis modifier");
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
= "";
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;
689 messages
<< i
->first
<< " " << type
<< " -:" << p
.cal_left
<< " 0:"
690 << p
.cal_center
<< " +:" << p
.cal_right
<< " t:" << p
.cal_tolerance
698 triple(const std::string
& _a
, const std::string
& _b
, const std::string
& _c
)
707 bool operator==(const triple
& t
) const
709 bool x
= (a
== t
.a
&& b
== t
.b
&& c
== t
.c
);
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
));
718 struct keybind_data
: public information_dispatch
721 modifier_set modmask
;
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
))
733 if(subkey
!= _subkey
)
735 if(&keygroup
!= group
)
737 std::string cmd
= fixup_command_polarity(command
, polarity
);
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);
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
,
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
);
807 std::string
keymapper::get_command_for(const std::string
& keyspec
) throw(std::bad_alloc
)
809 triple
k("", "", "");
811 k
= parse_to_triple(keyspec
);
812 } catch(std::exception
& e
) {
815 if(!keybindings
.count(k
))
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
);
825 bind(k
.a
, k
.b
, k
.c
, cmd
);
827 unbind(k
.a
, k
.b
, k
.c
);