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"
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 "
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()) {
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
);
35 throw std::runtime_error("Expected optional modifiers and key");
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
;
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()) {
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
);
60 throw std::runtime_error("Expected optional modifiers and key");
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
;
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();
78 std::string
fixup_command_polarity(std::string cmd
, bool polarity
) throw(std::bad_alloc
)
80 if(cmd
== "" || cmd
== "*")
83 if(cmd
[0] != '+' && polarity
)
85 if(cmd
[0] == '+' && !polarity
)
88 if(cmd
[1] != '+' && polarity
)
90 if(cmd
[1] == '+' && !polarity
)
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()))
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())
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
)
152 std::string
modifier::linked_name() const throw(std::bad_alloc
)
154 const modifier
* p
= get_linked_modifier(this);
161 void modifier_set::add(const modifier
& mod
, bool really
) throw(std::bad_alloc
)
167 void modifier_set::remove(const modifier
& mod
, bool really
) throw(std::bad_alloc
)
173 modifier_set
modifier_set::construct(const std::string
& _modifiers
) throw(std::bad_alloc
, std::runtime_error
)
176 std::string modifiers
= _modifiers
;
177 while(modifiers
!= "") {
178 std::string mod
= modifiers
;
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
));
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
))
199 for(auto i
: mask
.set
) {
200 const modifier
* j
= get_linked_modifier(i
);
201 if(i
!= j
&& mask
.set
.count(j
))
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
))
213 bool modifier_set::operator==(const modifier_set
& m
) const throw()
224 std::ostream
& operator<<(std::ostream
& os
, const modifier_set
& m
)
228 os
<< i
->name() << " ";
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
) {
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
))
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
);
250 if(!trigger
.set
.count(j
))
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
);
259 if(!trigger
.set
.count(i
))
263 //Nothing linked is found from neither set nor trigger.
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
);
276 std::string
keygroup::name() throw(std::bad_alloc
)
281 struct keygroup::parameters
keygroup::get_parameters()
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
;
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();
301 keygroup::keygroup(const std::string
& name
, enum type t
) throw(std::bad_alloc
)
303 keygroups()[keyname
= name
] = this;
311 requests_hook
= false;
314 keygroup::~keygroup() throw()
316 keygroups().erase(keyname
);
319 void keygroup::change_type(enum type t
) throw()
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
,
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
];
347 return std::make_pair(g
, 0);
350 return std::make_pair(g
, 1);
352 return std::make_pair(g
, 2);
354 return std::make_pair(g
, 3);
356 throw std::runtime_error("Invalid key");
360 void keygroup::change_calibration(short left
, short center
, short right
, double tolerance
)
365 cal_tolerance
= tolerance
;
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
)
376 else if(value
>= cal_right
)
378 else if(value
== cal_center
)
380 else if(value
< cal_center
)
381 return (static_cast<double>(value
) - cal_center
) / (static_cast<double>(cal_center
) - cal_left
);
383 return (static_cast<double>(value
) - cal_center
) / (static_cast<double>(cal_right
) - cal_center
);
386 double keygroup::compensate2(double value
)
391 return 0; //Always neutral.
394 return value
; //No mapping.
402 return (1 + value
) / 2;
406 return (1 - value
) / 2;
409 case KT_AXIS_PAIR_INVERSE
:
414 void keygroup::set_position(short pos
, const modifier_set
& modifiers
) throw()
418 lua_callback_keyhook(keyname
, get_parameters());
419 double x
= compensate2(compensate(pos
));
421 bool left
, right
, up
, down
;
422 bool oleft
, oright
, oup
, odown
;
434 tmp
= (x
>= cal_tolerance
);
435 run_listeners(modifiers
, 0, true, (!state
&& tmp
), x
);
436 run_listeners(modifiers
, 0, false, (state
&& !tmp
), x
);
440 case KT_AXIS_PAIR_INVERSE
:
441 if(x
<= -cal_tolerance
)
443 else if(x
>= cal_tolerance
)
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
);
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
);
475 void keygroup::run_listeners(const modifier_set
& modifiers
, unsigned subkey
, bool polarity
, bool really
, double x
)
479 std::string name
= keyname
;
480 if(ktype
== KT_AXIS_PAIR
&& subkey
== 0)
482 if(ktype
== KT_AXIS_PAIR
&& subkey
== 1)
484 if(ktype
== KT_HAT
&& subkey
== 0)
486 if(ktype
== KT_HAT
&& subkey
== 1)
488 if(ktype
== KT_HAT
&& subkey
== 2)
490 if(ktype
== KT_HAT
&& subkey
== 3)
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
];
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
= "";
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
:
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
) {
543 case KT_AXIS_PAIR_INVERSE
:
544 r
.insert(i
.first
+ "+");
545 r
.insert(i
.first
+ "-");
548 r
.insert(i
.first
+ "n");
549 r
.insert(i
.first
+ "e");
550 r
.insert(i
.first
+ "s");
551 r
.insert(i
.first
+ "w");
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"
568 [](tokensplitter
& t
) throw(std::bad_alloc
, std::runtime_error
) {
569 struct keygroup::parameters p
;
570 std::string axis
= t
;
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();
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
:
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;
596 std::string spec
= t
;
597 if(spec
== "disabled") {
599 p
.ktype
= keygroup::KT_DISABLED
;
601 throw std::runtime_error("Conflicting axis modes");
602 found_axismode
= true;
603 } else if(spec
== "axis") {
605 p
.ktype
= keygroup::KT_AXIS_PAIR
;
607 throw std::runtime_error("Conflicting axis modes");
608 found_axismode
= true;
609 } else if(spec
== "axis-inverse") {
611 p
.ktype
= keygroup::KT_AXIS_PAIR_INVERSE
;
613 throw std::runtime_error("Conflicting axis modes");
614 found_axismode
= true;
615 } else if(spec
== "pressure0-") {
617 p
.ktype
= keygroup::KT_PRESSURE_0M
;
619 throw std::runtime_error("Conflicting axis modes");
620 found_axismode
= true;
621 } else if(spec
== "pressure0+") {
623 p
.ktype
= keygroup::KT_PRESSURE_0P
;
625 throw std::runtime_error("Conflicting axis modes");
626 found_axismode
= true;
627 } else if(spec
== "pressure-0") {
629 p
.ktype
= keygroup::KT_PRESSURE_M0
;
631 throw std::runtime_error("Conflicting axis modes");
632 found_axismode
= true;
633 } else if(spec
== "pressure-+") {
635 p
.ktype
= keygroup::KT_PRESSURE_MP
;
637 throw std::runtime_error("Conflicting axis modes");
638 found_axismode
= true;
639 } else if(spec
== "pressure+0") {
641 p
.ktype
= keygroup::KT_PRESSURE_P0
;
643 throw std::runtime_error("Conflicting axis modes");
644 found_axismode
= true;
645 } else if(spec
== "pressure+-") {
647 p
.ktype
= keygroup::KT_PRESSURE_PM
;
649 throw std::runtime_error("Conflicting axis modes");
650 found_axismode
= true;
651 } else if(spec
.substr(0, 6) == "minus=") {
653 p
.cal_left
= parse_value
<int16_t>(spec
.substr(6));
655 throw std::runtime_error("Conflicting minus value");
657 } else if(spec
.substr(0, 5) == "zero=") {
659 p
.cal_center
= parse_value
<int16_t>(spec
.substr(5));
661 throw std::runtime_error("Conflicting zero value");
663 } else if(spec
.substr(0, 5) == "plus=") {
665 p
.cal_right
= parse_value
<int16_t>(spec
.substr(5));
667 throw std::runtime_error("Conflicting plus value");
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");
675 throw std::runtime_error("Conflicting tolerance value");
676 found_tolerance
= true;
678 throw std::runtime_error("Unknown axis modifier");
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
= "";
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;
703 messages
<< i
->first
<< " " << type
<< " -:" << p
.cal_left
<< " 0:"
704 << p
.cal_center
<< " +:" << p
.cal_right
<< " t:" << p
.cal_tolerance
712 triple(const std::string
& _a
, const std::string
& _b
, const std::string
& _c
)
721 bool operator==(const triple
& t
) const
723 bool x
= (a
== t
.a
&& b
== t
.b
&& c
== t
.c
);
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
));
732 struct keybind_data
: public information_dispatch
735 modifier_set modmask
;
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
))
747 if(subkey
!= _subkey
)
749 if(&keygroup
!= group
)
751 std::string cmd
= fixup_command_polarity(command
, polarity
);
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);
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
,
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
);
821 std::string
keymapper::get_command_for(const std::string
& keyspec
) throw(std::bad_alloc
)
823 triple
k("", "", "");
825 k
= parse_to_triple(keyspec
);
826 } catch(std::exception
& e
) {
829 if(!keybindings
.count(k
))
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
);
839 bind(k
.a
, k
.b
, k
.c
, cmd
);
841 unbind(k
.a
, k
.b
, k
.c
);