2 #include "keyboard.hpp"
3 #include "keyboard-mapper.hpp"
4 #include "stateobject.hpp"
12 threads::rlock
* global_lock
;
13 threads::rlock
& get_keymap_lock()
15 if(!global_lock
) global_lock
= new threads::rlock
;
21 std::map
<std::string
, invbind_info
*> invbinds
;
22 std::set
<invbind_set::listener
*> callbacks
;
25 struct mapper_internal
27 std::map
<std::string
, invbind
*> ibinds
;
28 std::map
<std::string
, ctrlrkey
*> ckeys
;
29 std::map
<mapper::triplet
, std::string
> bindings
;
30 std::set
<invbind_set
*> invbind_set_cbs
;
33 typedef stateobject::type
<invbind_set
, set_internal
> set_internal_t
;
34 typedef stateobject::type
<mapper
, mapper_internal
> mapper_internal_t
;
37 invbind_set::listener::~listener()
41 std::string
mapper::fixup_command_polarity(std::string cmd
, bool polarity
) throw(std::bad_alloc
)
43 if(cmd
== "" || cmd
== "*")
46 if(cmd
[0] != '+' && polarity
)
48 if(cmd
[0] == '+' && !polarity
)
51 if(cmd
[1] != '+' && polarity
)
53 if(cmd
[1] == '+' && !polarity
)
59 keyspec::keyspec() throw(std::bad_alloc
)
63 keyspec::keyspec(const std::string
& keyspec
) throw(std::bad_alloc
, std::runtime_error
)
65 regex_results r
= regex("([^/]*)/([^|]*)\\|(.*)", keyspec
, "Invalid keyspec");
71 keyspec::operator std::string() throw(std::bad_alloc
)
73 return mod
+ "/" + mask
+ "|" + key
;
76 keyspec::operator bool() throw()
81 bool keyspec::operator!() throw()
86 void keyspec::clear() throw()
93 bool keyspec::operator==(const keyspec
& keyspec
)
95 return (mod
== keyspec
.mod
&& mask
== keyspec
.mask
&& key
== keyspec
.key
);
98 bool keyspec::operator!=(const keyspec
& keyspec
)
100 return (mod
!= keyspec
.mod
|| mask
!= keyspec
.mask
|| key
!= keyspec
.key
);
103 std::set
<invbind
*> mapper::get_inverses() throw(std::bad_alloc
)
105 threads::arlock
u(get_keymap_lock());
106 auto state
= mapper_internal_t::get_soft(this);
107 std::set
<invbind
*> r
;
109 for(auto i
: state
->ibinds
)
114 invbind
* mapper::get_inverse(const std::string
& command
) throw(std::bad_alloc
)
116 threads::arlock
u(get_keymap_lock());
117 auto state
= mapper_internal_t::get_soft(this);
118 if(state
&& state
->ibinds
.count(command
))
119 return state
->ibinds
[command
];
124 std::set
<ctrlrkey
*> mapper::get_controller_keys() throw(std::bad_alloc
)
126 threads::arlock
u(get_keymap_lock());
127 auto state
= mapper_internal_t::get_soft(this);
128 std::set
<ctrlrkey
*> r
;
130 for(auto i
: state
->ckeys
)
135 ctrlrkey
* mapper::get_controllerkey(const std::string
& command
) throw(std::bad_alloc
)
137 threads::arlock
u(get_keymap_lock());
138 auto state
= mapper_internal_t::get_soft(this);
139 if(state
&& state
->ckeys
.count(command
))
140 return state
->ckeys
[command
];
145 void mapper::do_register(const std::string
& name
, invbind
& ibind
) throw(std::bad_alloc
)
147 threads::arlock
u(get_keymap_lock());
148 auto& state
= mapper_internal_t::get(this);
149 if(state
.ibinds
.count(name
)) return;
150 state
.ibinds
[name
] = &ibind
;
151 //Search for matches.
152 for(auto i
: state
.bindings
)
153 if(i
.second
== ibind
.cmd
) {
154 ibind
.specs
.push_back(i
.first
.as_keyspec());
158 void mapper::do_unregister(const std::string
& name
, invbind
& ibind
) throw(std::bad_alloc
)
160 threads::arlock
u(get_keymap_lock());
161 auto state
= mapper_internal_t::get_soft(this);
162 if(state
&& state
->ibinds
.count(name
) && state
->ibinds
[name
] == &ibind
)
163 state
->ibinds
.erase(name
);
166 void mapper::do_register(const std::string
& name
, ctrlrkey
& ckey
) throw(std::bad_alloc
)
168 threads::arlock
u(get_keymap_lock());
169 auto& state
= mapper_internal_t::get(this);
170 if(state
.ckeys
.count(name
)) return;
171 state
.ckeys
[name
] = &ckey
;
174 void mapper::do_unregister(const std::string
& name
, ctrlrkey
& ckey
) throw(std::bad_alloc
)
176 threads::arlock
u(get_keymap_lock());
177 auto state
= mapper_internal_t::get_soft(this);
178 if(state
&& state
->ckeys
.count(name
) && state
->ckeys
[name
] == &ckey
)
179 state
->ckeys
.erase(name
);
182 keyboard
& mapper::get_keyboard() throw()
187 mapper::mapper(keyboard
& _kbd
, command::group
& _domain
) throw(std::bad_alloc
)
188 : _listener(*this), kbd(_kbd
), domain(_domain
)
192 mapper::~mapper() throw()
194 threads::arlock
u(get_keymap_lock());
195 auto state
= mapper_internal_t::get_soft(this);
197 for(auto i
: state
->ibinds
)
198 i
.second
->mapper_died();
199 for(auto i
: state
->invbind_set_cbs
)
200 i
->drop_callback(_listener
);
201 mapper_internal_t::clear(this);
204 mapper::triplet::triplet(modifier_set _mod
, modifier_set _mask
, key
& kkey
,
214 mapper::triplet::triplet(key
& kkey
, unsigned _subkey
)
221 bool mapper::triplet::operator<(const struct triplet
& a
) const
223 if((uint64_t)_key
< (uint64_t)a
._key
)
225 if((uint64_t)_key
> (uint64_t)a
._key
)
227 if(subkey
< a
.subkey
)
229 if(subkey
> a
.subkey
)
231 if(index
&& !a
.index
)
233 if(!index
&& a
.index
)
246 bool mapper::triplet::operator==(const struct triplet
& a
) const
252 if(subkey
!= a
.subkey
)
256 if(!(mask
== a
.mask
))
261 keyspec
mapper::triplet::as_keyspec() const throw(std::bad_alloc
)
266 auto s
= _key
->get_subkeys();
267 if(s
.size() > subkey
)
268 k
.key
= _key
->get_name() + s
[subkey
];
270 k
.key
= _key
->get_name();
274 std::list
<keyspec
> mapper::get_bindings() throw(std::bad_alloc
)
276 threads::arlock
u(get_keymap_lock());
277 auto state
= mapper_internal_t::get_soft(this);
278 std::list
<keyspec
> r
;
280 for(auto i
: state
->bindings
)
281 r
.push_back(i
.first
.as_keyspec());
285 command::group
& mapper::get_command_group() throw()
290 void mapper::bind(std::string mod
, std::string modmask
, std::string keyname
, std::string command
)
291 throw(std::bad_alloc
, std::runtime_error
)
297 triplet
t(kbd
, spec
);
298 threads::arlock
u(get_keymap_lock());
299 auto state
= mapper_internal_t::get_soft(this);
301 throw std::runtime_error("Attempt to bind key before initializing mapper");
302 if(state
->bindings
.count(t
))
303 throw std::runtime_error("Key is already bound");
304 if(!listening
.count(t
._key
)) {
305 t
._key
->add_listener(*this, false);
306 listening
.insert(t
._key
);
308 std::string old_command
;
309 if(state
->bindings
.count(t
))
310 old_command
= state
->bindings
[t
];
311 state
->bindings
[t
] = command
;
312 change_command(spec
, old_command
, command
);
315 void mapper::unbind(std::string mod
, std::string modmask
, std::string keyname
) throw(std::bad_alloc
,
322 triplet
t(kbd
, spec
);
323 threads::arlock
u(get_keymap_lock());
324 auto state
= mapper_internal_t::get_soft(this);
325 if(!state
|| !state
->bindings
.count(t
))
326 throw std::runtime_error("Key is not bound");
327 //No harm at leaving listeners listening.
328 std::string old_command
;
329 if(state
->bindings
.count(t
))
330 old_command
= state
->bindings
[t
];
331 state
->bindings
.erase(t
);
332 change_command(spec
, old_command
, "");
335 std::string
mapper::get(const keyspec
& keyspec
) throw(std::bad_alloc
)
337 triplet
t(kbd
, keyspec
);
338 threads::arlock
u(get_keymap_lock());
339 auto state
= mapper_internal_t::get_soft(this);
340 if(!state
|| !state
->bindings
.count(t
))
342 return state
->bindings
[t
];
345 void mapper::change_command(const keyspec
& spec
, const std::string
& old
, const std::string
& newc
)
347 threads::arlock
u(get_keymap_lock());
348 auto state
= mapper_internal_t::get_soft(this);
350 if(old
!= "" && state
->ibinds
.count(old
)) {
351 auto& i
= state
->ibinds
[old
];
355 for(auto j
: state
->bindings
)
356 if(j
.second
== i
->cmd
&& j
.first
.as_keyspec() != spec
) {
357 i
->specs
.push_back(j
.first
.as_keyspec());
360 if(newc
!= "" && state
->ibinds
.count(newc
)) {
361 auto& i
= state
->ibinds
[newc
];
362 i
->specs
.push_back(spec
);
366 void mapper::set(const keyspec
& keyspec
, const std::string
& cmd
) throw(std::bad_alloc
,
369 triplet
t(kbd
, keyspec
);
370 threads::arlock
u(get_keymap_lock());
371 auto state
= mapper_internal_t::get_soft(this);
372 if(!state
) throw std::runtime_error("Attempt to set key in mapper before initializing");
373 if(!listening
.count(t
._key
)) {
374 t
._key
->add_listener(*this, false);
375 listening
.insert(t
._key
);
378 if(state
->bindings
.count(t
))
379 oldcmd
= state
->bindings
[t
];
380 state
->bindings
[t
] = cmd
;
381 change_command(keyspec
, oldcmd
, cmd
);
384 void mapper::on_key_event(modifier_set
& mods
, key
& key
, event
& event
)
386 auto mask
= event
.get_change_mask();
389 unsigned k
= mask
& 3;
391 on_key_event_subkey(mods
, key
, i
, k
== 3);
397 void mapper::on_key_event_subkey(modifier_set
& mods
, key
& key
, unsigned skey
,
400 std::list
<std::string
> cmd
;
402 threads::arlock
u(get_keymap_lock());
403 auto state
= mapper_internal_t::get_soft(this);
405 triplet
llow(key
, skey
);
406 triplet
lhigh(key
, skey
+ 1);
407 auto low
= state
->bindings
.lower_bound(llow
);
408 auto high
= state
->bindings
.lower_bound(lhigh
);
409 for(auto i
= low
; i
!= high
; i
++) {
410 if(!mods
.triggers(i
->first
.mod
, i
->first
.mask
))
412 std::string xcmd
= fixup_command_polarity(i
->second
, polarity
);
413 if(xcmd
!= "") cmd
.push_back(xcmd
);
420 mapper::triplet::triplet(keyboard
& k
, const keyspec
& spec
)
422 mod
= modifier_set::construct(k
, spec
.mod
);
423 mask
= modifier_set::construct(k
, spec
.mask
);
425 throw std::runtime_error("Bad modifiers");
426 auto g
= keymapper_lookup_subkey(k
, spec
.key
, false);
432 std::list
<ctrlrkey
*> mapper::get_controllerkeys_kbdkey(key
* kbdkey
)
433 throw(std::bad_alloc
)
435 threads::arlock
u(get_keymap_lock());
436 auto state
= mapper_internal_t::get_soft(this);
437 std::list
<ctrlrkey
*> r
;
439 for(auto i
: state
->ckeys
) {
440 for(unsigned j
= 0;; j
++) {
441 auto k
= i
.second
->get(j
);
444 if(k
.first
== kbdkey
)
445 r
.push_back(i
.second
);
451 void mapper::add_invbind_set(invbind_set
& set
)
453 threads::arlock
u(get_keymap_lock());
454 auto& state
= mapper_internal_t::get(this);
455 if(state
.invbind_set_cbs
.count(&set
)) return;
457 state
.invbind_set_cbs
.insert(&set
);
458 set
.add_callback(_listener
);
460 state
.invbind_set_cbs
.erase(&set
);
464 void mapper::drop_invbind_set(invbind_set
& set
)
466 threads::arlock
h(get_keymap_lock());
467 auto state
= mapper_internal_t::get_soft(this);
469 //Drop the callback. This unregisters all.
470 set
.drop_callback(_listener
);
471 state
->invbind_set_cbs
.erase(&set
);
474 mapper::listener::listener(mapper
& _grp
)
479 mapper::listener::~listener()
483 void mapper::listener::create(invbind_set
& s
, const std::string
& name
, invbind_info
& ibinfo
)
485 threads::arlock
h(get_keymap_lock());
489 void mapper::listener::destroy(invbind_set
& s
, const std::string
& name
)
491 threads::arlock
h(get_keymap_lock());
492 auto state
= mapper_internal_t::get_soft(&grp
);
494 state
->ibinds
.erase(name
);
497 void mapper::listener::kill(invbind_set
& s
)
499 threads::arlock
h(get_keymap_lock());
500 auto state
= mapper_internal_t::get_soft(&grp
);
502 state
->invbind_set_cbs
.erase(&s
);
505 invbind::invbind(mapper
& kmapper
, const std::string
& _command
, const std::string
& _name
, bool dynamic
)
506 throw(std::bad_alloc
)
507 : _mapper(&kmapper
), cmd(_command
), oname(_name
)
509 is_dynamic
= dynamic
;
510 _mapper
->do_register(cmd
, *this);
513 invbind::~invbind() throw()
515 threads::arlock
h(get_keymap_lock());
517 _mapper
->do_unregister(cmd
, *this);
520 keyspec
invbind::get(unsigned index
) throw(std::bad_alloc
)
522 threads::arlock
u(get_keymap_lock());
523 if(index
>= specs
.size())
528 void invbind::clear(unsigned index
) throw(std::bad_alloc
)
530 threads::arlock
u(get_keymap_lock());
533 if(index
>= specs
.size())
535 unbind
= specs
[index
];
537 if(unbind
&& _mapper
)
538 _mapper
->set(unbind
, "");
541 void invbind::append(const keyspec
& keyspec
) throw(std::bad_alloc
)
543 threads::arlock
u(get_keymap_lock());
544 _mapper
->set(keyspec
, cmd
);
547 std::string
invbind::getname() throw(std::bad_alloc
)
552 void invbind::mapper_died()
554 threads::arlock
u(get_keymap_lock());
556 if(is_dynamic
) delete this;
559 invbind_info::invbind_info(invbind_set
& set
, const std::string
& _command
, const std::string
& _name
)
560 throw(std::bad_alloc
)
565 in_set
->do_register(command
, *this);
568 invbind_info::~invbind_info() throw()
570 threads::arlock
u(get_keymap_lock());
572 in_set
->do_unregister(command
, *this);
575 invbind
* invbind_info::make(mapper
& m
)
577 return new invbind(m
, command
, name
, true);
580 void invbind_info::set_died()
582 threads::arlock
u(get_keymap_lock());
586 invbind_set::invbind_set()
590 invbind_set::~invbind_set()
592 auto state
= set_internal_t::get_soft(this);
594 threads::arlock
u(get_keymap_lock());
595 //Call all DCBs on all factories.
596 for(auto i
: state
->invbinds
)
597 for(auto j
: state
->callbacks
)
598 j
->destroy(*this, i
.first
);
600 for(auto j
: state
->callbacks
)
602 //Notify all factories that base set died.
603 for(auto i
: state
->invbinds
)
604 i
.second
->set_died();
605 //We assume factories look after themselves, so we don't destroy those.
606 set_internal_t::clear(this);
609 void invbind_set::do_register(const std::string
& name
, invbind_info
& info
)
611 threads::arlock
u(get_keymap_lock());
612 auto& state
= set_internal_t::get(this);
613 if(state
.invbinds
.count(name
)) {
614 std::cerr
<< "WARNING: Command collision for " << name
<< "!" << std::endl
;
617 state
.invbinds
[name
] = &info
;
618 //Call all CCBs on this.
619 for(auto i
: state
.callbacks
)
620 i
->create(*this, name
, info
);
623 void invbind_set::do_unregister(const std::string
& name
, invbind_info
& info
)
625 threads::arlock
u(get_keymap_lock());
626 auto state
= set_internal_t::get_soft(this);
628 if(!state
->invbinds
.count(name
) || state
->invbinds
[name
] != &info
) return; //Not this.
629 state
->invbinds
.erase(name
);
630 //Call all DCBs on this.
631 for(auto i
: state
->callbacks
)
632 i
->destroy(*this, name
);
635 void invbind_set::add_callback(invbind_set::listener
& listener
) throw(std::bad_alloc
)
637 threads::arlock
u(get_keymap_lock());
638 auto& state
= set_internal_t::get(this);
639 state
.callbacks
.insert(&listener
);
640 //To avoid races, call CCBs on all factories for this.
641 for(auto j
: state
.invbinds
)
642 listener
.create(*this, j
.first
, *j
.second
);
645 void invbind_set::drop_callback(invbind_set::listener
& listener
)
647 threads::arlock
u(get_keymap_lock());
648 auto state
= set_internal_t::get_soft(this);
650 if(state
->callbacks
.count(&listener
)) {
651 //To avoid races, call DCBs on all factories for this.
652 for(auto j
: state
->invbinds
)
653 listener
.destroy(*this, j
.first
);
654 state
->callbacks
.erase(&listener
);
658 ctrlrkey::ctrlrkey(mapper
& kmapper
, const std::string
& _command
, const std::string
& _name
,
659 bool _axis
) throw(std::bad_alloc
)
660 : _mapper(kmapper
), cmd(_command
), oname(_name
)
663 _mapper
.do_register(cmd
, *this);
666 ctrlrkey::~ctrlrkey() throw()
668 _mapper
.do_unregister(cmd
, *this);
671 std::pair
<key
*, unsigned> ctrlrkey::get(unsigned index
) throw()
673 threads::arlock
u(get_keymap_lock());
674 if(index
>= keys
.size())
675 return std::make_pair(reinterpret_cast<key
*>(NULL
), 0);
679 std::string
ctrlrkey::get_string(unsigned index
) throw(std::bad_alloc
)
684 auto s
= k
.first
->get_subkeys();
685 if(k
.second
>= s
.size() || axis
)
686 return k
.first
->get_name();
687 return k
.first
->get_name() + s
[k
.second
];
690 void ctrlrkey::append(key
* _key
, unsigned _subkey
) throw()
692 threads::arlock
u(get_keymap_lock());
693 //Search for duplicates.
694 std::pair
<key
*, unsigned> mkey
= std::make_pair(_key
, _subkey
);
699 _key
->add_listener(*this, axis
);
700 keys
.push_back(mkey
);
703 void ctrlrkey::remove(key
* _key
, unsigned _subkey
) throw()
705 threads::arlock
u(get_keymap_lock());
706 std::pair
<key
*, unsigned> mkey
= std::make_pair(_key
, _subkey
);
707 for(auto i
= keys
.begin(); i
!= keys
.end(); i
++) {
709 mkey
.first
->remove_listener(*this);
716 void ctrlrkey::append(const std::string
& _key
) throw(std::bad_alloc
, std::runtime_error
)
718 auto g
= keymapper_lookup_subkey(_mapper
.get_keyboard(), _key
, axis
);
719 append(g
.first
, g
.second
);
722 std::pair
<key
*, unsigned> keymapper_lookup_subkey(keyboard
& kbd
, const std::string
& name
,
723 bool axis
) throw(std::bad_alloc
, std::runtime_error
)
725 threads::arlock
u(get_keymap_lock());
727 return std::make_pair((key
*)NULL
, 0);
728 //Try direct lookup first.
729 key
* key
= kbd
.try_lookup_key(name
);
731 return std::make_pair(key
, 0);
732 //Axes only do direct lookup.
734 throw std::runtime_error("Invalid key");
735 std::string prefix
= name
;
736 char letter
= prefix
[prefix
.length() - 1];
737 prefix
= prefix
.substr(0, prefix
.length() - 1);
738 key
= kbd
.try_lookup_key(prefix
);
740 throw std::runtime_error("Invalid key");
741 auto s
= key
->get_subkeys();
742 for(size_t i
= 0; i
< s
.size(); i
++)
743 if(s
[i
].length() > 0 && letter
== s
[i
][0])
744 return std::make_pair(key
, i
);
745 throw std::runtime_error("Invalid key");
748 void ctrlrkey::on_key_event(modifier_set
& mods
, key
& key
, event
& event
)
751 //Axes work specially.
752 _mapper
.get_command_group().invoke((stringfmt() << cmd
<< " " << event
.get_state()).str());
755 auto mask
= event
.get_change_mask();
759 unsigned kmask
= (mask
>> (2 * i
.second
)) & 3;
762 cmd2
= mapper::fixup_command_polarity(cmd
, kmask
== 3);
764 _mapper
.get_command_group().invoke(cmd2
);