lsnes rr2-β24
[lsnes.git] / src / library / keyboard-mapper.cpp
blob41a274671ea1b9a05393efe3c279afe5969f71e3
1 #include "command.hpp"
2 #include "keyboard.hpp"
3 #include "keyboard-mapper.hpp"
4 #include "stateobject.hpp"
5 #include "string.hpp"
6 #include "threads.hpp"
8 namespace keyboard
10 namespace
12 threads::rlock* global_lock;
13 threads::rlock& get_keymap_lock()
15 if(!global_lock) global_lock = new threads::rlock;
16 return *global_lock;
19 struct set_internal
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 == "*")
44 return "";
45 if(cmd[0] != '*') {
46 if(cmd[0] != '+' && polarity)
47 return "";
48 if(cmd[0] == '+' && !polarity)
49 cmd[0] = '-';
50 } else {
51 if(cmd[1] != '+' && polarity)
52 return "";
53 if(cmd[1] == '+' && !polarity)
54 cmd[1] = '-';
56 return cmd;
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");
66 mod = r[1];
67 mask = r[2];
68 key = r[3];
71 keyspec::operator std::string() throw(std::bad_alloc)
73 return mod + "/" + mask + "|" + key;
76 keyspec::operator bool() throw()
78 return (key != "");
81 bool keyspec::operator!() throw()
83 return (key == "");
86 void keyspec::clear() throw()
88 mod = "";
89 mask = "";
90 key = "";
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;
108 if(state)
109 for(auto i : state->ibinds)
110 r.insert(i.second);
111 return r;
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];
120 else
121 return NULL;
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;
129 if(state)
130 for(auto i : state->ckeys)
131 r.insert(i.second);
132 return r;
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];
141 else
142 return NULL;
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()
184 return kbd;
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);
196 if(!state) return;
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,
205 unsigned _subkey)
207 mod = _mod;
208 mask = _mask;
209 _key = &kkey;
210 subkey = _subkey;
211 index = false;
214 mapper::triplet::triplet(key& kkey, unsigned _subkey)
216 _key = &kkey;
217 subkey = _subkey;
218 index = true;
221 bool mapper::triplet::operator<(const struct triplet& a) const
223 if((uint64_t)_key < (uint64_t)a._key)
224 return true;
225 if((uint64_t)_key > (uint64_t)a._key)
226 return false;
227 if(subkey < a.subkey)
228 return true;
229 if(subkey > a.subkey)
230 return false;
231 if(index && !a.index)
232 return true;
233 if(!index && a.index)
234 return false;
235 if(mask < a.mask)
236 return true;
237 if(a.mask < mask)
238 return false;
239 if(mod < a.mod)
240 return true;
241 if(a.mod < mod)
242 return false;
243 return false;
246 bool mapper::triplet::operator==(const struct triplet& a) const
248 if(index != a.index)
249 return false;
250 if(_key != a._key)
251 return false;
252 if(subkey != a.subkey)
253 return false;
254 if(!(mod == a.mod))
255 return false;
256 if(!(mask == a.mask))
257 return false;
258 return true;
261 keyspec mapper::triplet::as_keyspec() const throw(std::bad_alloc)
263 keyspec k;
264 k.mod = mod;
265 k.mask = mask;
266 auto s = _key->get_subkeys();
267 if(s.size() > subkey)
268 k.key = _key->get_name() + s[subkey];
269 else
270 k.key = _key->get_name();
271 return k;
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;
279 if(state)
280 for(auto i : state->bindings)
281 r.push_back(i.first.as_keyspec());
282 return r;
285 command::group& mapper::get_command_group() throw()
287 return domain;
290 void mapper::bind(std::string mod, std::string modmask, std::string keyname, std::string command)
291 throw(std::bad_alloc, std::runtime_error)
293 keyspec spec;
294 spec.mod = mod;
295 spec.mask = modmask;
296 spec.key = keyname;
297 triplet t(kbd, spec);
298 threads::arlock u(get_keymap_lock());
299 auto state = mapper_internal_t::get_soft(this);
300 if(!state)
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,
316 std::runtime_error)
318 keyspec spec;
319 spec.mod = mod;
320 spec.mask = modmask;
321 spec.key = keyname;
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))
341 return "";
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);
349 if(!state) return;
350 if(old != "" && state->ibinds.count(old)) {
351 auto& i = state->ibinds[old];
353 i->specs.clear();
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,
367 std::runtime_error)
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);
377 std::string oldcmd;
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();
387 unsigned i = 0;
388 while(mask) {
389 unsigned k = mask & 3;
390 if(k & 2)
391 on_key_event_subkey(mods, key, i, k == 3);
392 mask >>= 2;
393 i++;
397 void mapper::on_key_event_subkey(modifier_set& mods, key& key, unsigned skey,
398 bool polarity)
400 std::list<std::string> cmd;
402 threads::arlock u(get_keymap_lock());
403 auto state = mapper_internal_t::get_soft(this);
404 if(!state) return;
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))
411 continue;
412 std::string xcmd = fixup_command_polarity(i->second, polarity);
413 if(xcmd != "") cmd.push_back(xcmd);
416 for(auto i : cmd)
417 domain.invoke(i);
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);
424 if(!mod.valid(mask))
425 throw std::runtime_error("Bad modifiers");
426 auto g = keymapper_lookup_subkey(k, spec.key, false);
427 _key = g.first;
428 subkey = g.second;
429 index = 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;
438 if(state)
439 for(auto i : state->ckeys) {
440 for(unsigned j = 0;; j++) {
441 auto k = i.second->get(j);
442 if(!k.first)
443 break;
444 if(k.first == kbdkey)
445 r.push_back(i.second);
448 return r;
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;
456 try {
457 state.invbind_set_cbs.insert(&set);
458 set.add_callback(_listener);
459 } catch(...) {
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);
468 if(!state) return;
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)
475 : grp(_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());
486 ibinfo.make(grp);
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);
493 if(state)
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);
501 if(state)
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());
516 if(_mapper)
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())
524 return keyspec();
525 return specs[index];
528 void invbind::clear(unsigned index) throw(std::bad_alloc)
530 threads::arlock u(get_keymap_lock());
531 keyspec unbind;
533 if(index >= specs.size())
534 return;
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)
549 return oname;
552 void invbind::mapper_died()
554 threads::arlock u(get_keymap_lock());
555 _mapper = NULL;
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)
561 : in_set(&set)
563 command = _command;
564 name = _name;
565 in_set->do_register(command, *this);
568 invbind_info::~invbind_info() throw()
570 threads::arlock u(get_keymap_lock());
571 if(in_set)
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());
583 in_set = NULL;
586 invbind_set::invbind_set()
590 invbind_set::~invbind_set()
592 auto state = set_internal_t::get_soft(this);
593 if(!state) return;
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);
599 //Call all TCBs.
600 for(auto j : state->callbacks)
601 j->kill(*this);
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;
615 return;
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);
627 if(!state) return;
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);
649 if(!state) return;
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)
662 axis = _axis;
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);
676 return keys[index];
679 std::string ctrlrkey::get_string(unsigned index) throw(std::bad_alloc)
681 auto k = get(index);
682 if(!k.first)
683 return "";
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);
695 for(auto i : keys)
696 if(i == mkey)
697 return;
698 //No dupes, add.
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++) {
708 if(*i == mkey) {
709 mkey.first->remove_listener(*this);
710 keys.erase(i);
711 return;
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());
726 if(name == "")
727 return std::make_pair((key*)NULL, 0);
728 //Try direct lookup first.
729 key* key = kbd.try_lookup_key(name);
730 if(key)
731 return std::make_pair(key, 0);
732 //Axes only do direct lookup.
733 if(axis)
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);
739 if(!key)
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)
750 if(axis) {
751 //Axes work specially.
752 _mapper.get_command_group().invoke((stringfmt() << cmd << " " << event.get_state()).str());
753 return;
755 auto mask = event.get_change_mask();
756 for(auto i : keys) {
757 if(i.first != &key)
758 continue;
759 unsigned kmask = (mask >> (2 * i.second)) & 3;
760 std::string cmd2;
761 if(kmask & 2)
762 cmd2 = mapper::fixup_command_polarity(cmd, kmask == 3);
763 if(cmd2 != "")
764 _mapper.get_command_group().invoke(cmd2);