Listener object is better than 3 lambdas + handle
[lsnes.git] / src / library / keyboard-mapper.cpp
blob6dcf6134208668a5eb5d95b64eff071b6a014518
1 #include "command.hpp"
2 #include "integer-pool.hpp"
3 #include "keyboard-mapper.hpp"
4 #include "register-queue.hpp"
5 #include "stateobject.hpp"
6 #include "string.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<set_listener*> callbacks;
25 typedef stateobject::type<invbind_set, set_internal> set_internal_t;
28 set_listener::~set_listener()
32 std::string mapper::fixup_command_polarity(std::string cmd, bool polarity) throw(std::bad_alloc)
34 if(cmd == "" || cmd == "*")
35 return "";
36 if(cmd[0] != '*') {
37 if(cmd[0] != '+' && polarity)
38 return "";
39 if(cmd[0] == '+' && !polarity)
40 cmd[0] = '-';
41 } else {
42 if(cmd[1] != '+' && polarity)
43 return "";
44 if(cmd[1] == '+' && !polarity)
45 cmd[1] = '-';
47 return cmd;
50 keyspec::keyspec() throw(std::bad_alloc)
54 keyspec::keyspec(const std::string& keyspec) throw(std::bad_alloc, std::runtime_error)
56 regex_results r = regex("([^/]*)/([^|]*)\\|(.*)", keyspec, "Invalid keyspec");
57 mod = r[1];
58 mask = r[2];
59 key = r[3];
62 keyspec::operator std::string() throw(std::bad_alloc)
64 return mod + "/" + mask + "|" + key;
67 keyspec::operator bool() throw()
69 return (key != "");
72 bool keyspec::operator!() throw()
74 return (key == "");
77 void keyspec::clear() throw()
79 mod = "";
80 mask = "";
81 key = "";
84 bool keyspec::operator==(const keyspec& keyspec)
86 return (mod == keyspec.mod && mask == keyspec.mask && key == keyspec.key);
89 bool keyspec::operator!=(const keyspec& keyspec)
91 return (mod != keyspec.mod || mask != keyspec.mask || key != keyspec.key);
94 std::set<invbind*> mapper::get_inverses() throw(std::bad_alloc)
96 threads::arlock u(get_keymap_lock());
97 std::set<invbind*> r;
98 for(auto i : ibinds)
99 r.insert(i.second);
100 return r;
103 invbind* mapper::get_inverse(const std::string& command) throw(std::bad_alloc)
105 threads::arlock u(get_keymap_lock());
106 if(ibinds.count(command))
107 return ibinds[command];
108 else
109 return NULL;
112 std::set<ctrlrkey*> mapper::get_controller_keys() throw(std::bad_alloc)
114 threads::arlock u(get_keymap_lock());
115 std::set<ctrlrkey*> r;
116 for(auto i : ckeys)
117 r.insert(i.second);
118 return r;
121 ctrlrkey* mapper::get_controllerkey(const std::string& command) throw(std::bad_alloc)
123 threads::arlock u(get_keymap_lock());
124 if(ckeys.count(command))
125 return ckeys[command];
126 else
127 return NULL;
130 void mapper::do_register(const std::string& name, invbind& ibind) throw(std::bad_alloc)
132 threads::arlock u(get_keymap_lock());
133 ibinds[name] = &ibind;
134 //Search for matches.
135 for(auto i : bindings)
136 if(i.second == ibind.cmd) {
137 ibind.specs.push_back(i.first.as_keyspec());
141 void mapper::do_unregister(const std::string& name, invbind* dummy) throw(std::bad_alloc)
143 if(dtor_running) return;
144 threads::arlock u(get_keymap_lock());
145 ibinds.erase(name);
148 void mapper::do_register(const std::string& name, ctrlrkey& ckey) throw(std::bad_alloc)
150 threads::arlock u(get_keymap_lock());
151 ckeys[name] = &ckey;
154 void mapper::do_unregister(const std::string& name, ctrlrkey* dummy) throw(std::bad_alloc)
156 threads::arlock u(get_keymap_lock());
157 ckeys.erase(name);
160 keyboard& mapper::get_keyboard() throw()
162 return kbd;
165 mapper::mapper(keyboard& _kbd, command::group& _domain) throw(std::bad_alloc)
166 : _listener(*this), kbd(_kbd), domain(_domain)
168 register_queue<mapper, invbind>::do_ready(*this, true);
169 register_queue<mapper, ctrlrkey>::do_ready(*this, true);
172 mapper::~mapper() throw()
174 dtor_running = true;
175 threads::arlock u(get_keymap_lock());
176 for(auto i : ibinds)
177 i.second->mapper_died();
178 for(auto i : invbind_set_cbs)
179 i->drop_callback(_listener);
180 register_queue<mapper, invbind>::do_ready(*this, false);
181 register_queue<mapper, ctrlrkey>::do_ready(*this, false);
184 mapper::triplet::triplet(modifier_set _mod, modifier_set _mask, key& kkey,
185 unsigned _subkey)
187 mod = _mod;
188 mask = _mask;
189 _key = &kkey;
190 subkey = _subkey;
191 index = false;
194 mapper::triplet::triplet(key& kkey, unsigned _subkey)
196 _key = &kkey;
197 subkey = _subkey;
198 index = true;
201 bool mapper::triplet::operator<(const struct triplet& a) const
203 if((uint64_t)_key < (uint64_t)a._key)
204 return true;
205 if((uint64_t)_key > (uint64_t)a._key)
206 return false;
207 if(subkey < a.subkey)
208 return true;
209 if(subkey > a.subkey)
210 return false;
211 if(index && !a.index)
212 return true;
213 if(!index && a.index)
214 return false;
215 if(mask < a.mask)
216 return true;
217 if(a.mask < mask)
218 return false;
219 if(mod < a.mod)
220 return true;
221 if(a.mod < mod)
222 return false;
223 return false;
226 bool mapper::triplet::operator==(const struct triplet& a) const
228 if(index != a.index)
229 return false;
230 if(_key != a._key)
231 return false;
232 if(subkey != a.subkey)
233 return false;
234 if(!(mod == a.mod))
235 return false;
236 if(!(mask == a.mask))
237 return false;
238 return true;
241 keyspec mapper::triplet::as_keyspec() const throw(std::bad_alloc)
243 keyspec k;
244 k.mod = mod;
245 k.mask = mask;
246 auto s = _key->get_subkeys();
247 if(s.size() > subkey)
248 k.key = _key->get_name() + s[subkey];
249 else
250 k.key = _key->get_name();
251 return k;
254 std::list<keyspec> mapper::get_bindings() throw(std::bad_alloc)
256 threads::arlock u(get_keymap_lock());
257 std::list<keyspec> r;
258 for(auto i : bindings)
259 r.push_back(i.first.as_keyspec());
260 return r;
263 command::group& mapper::get_command_group() throw()
265 return domain;
268 void mapper::bind(std::string mod, std::string modmask, std::string keyname, std::string command)
269 throw(std::bad_alloc, std::runtime_error)
271 keyspec spec;
272 spec.mod = mod;
273 spec.mask = modmask;
274 spec.key = keyname;
275 triplet t(kbd, spec);
276 threads::arlock u(get_keymap_lock());
277 if(bindings.count(t))
278 throw std::runtime_error("Key is already bound");
279 if(!listening.count(t._key)) {
280 t._key->add_listener(*this, false);
281 listening.insert(t._key);
283 std::string old_command;
284 if(bindings.count(t))
285 old_command = bindings[t];
286 bindings[t] = command;
287 change_command(spec, old_command, command);
290 void mapper::unbind(std::string mod, std::string modmask, std::string keyname) throw(std::bad_alloc,
291 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 if(!bindings.count(t))
300 throw std::runtime_error("Key is not bound");
301 //No harm at leaving listeners listening.
302 std::string old_command;
303 if(bindings.count(t))
304 old_command = bindings[t];
305 bindings.erase(t);
306 change_command(spec, old_command, "");
309 std::string mapper::get(const keyspec& keyspec) throw(std::bad_alloc)
311 triplet t(kbd, keyspec);
312 threads::arlock u(get_keymap_lock());
313 if(!bindings.count(t))
314 return "";
315 return bindings[t];
318 void mapper::change_command(const keyspec& spec, const std::string& old, const std::string& newc)
320 threads::arlock u(get_keymap_lock());
321 if(old != "" && ibinds.count(old)) {
322 auto& i = ibinds[old];
324 i->specs.clear();
326 for(auto j : bindings)
327 if(j.second == i->cmd && j.first.as_keyspec() != spec) {
328 i->specs.push_back(j.first.as_keyspec());
331 if(newc != "" && ibinds.count(newc)) {
332 auto& i = ibinds[newc];
333 i->specs.push_back(spec);
337 void mapper::set(const keyspec& keyspec, const std::string& cmd) throw(std::bad_alloc,
338 std::runtime_error)
340 triplet t(kbd, keyspec);
341 threads::arlock u(get_keymap_lock());
342 if(!listening.count(t._key)) {
343 t._key->add_listener(*this, false);
344 listening.insert(t._key);
346 std::string oldcmd;
347 if(bindings.count(t))
348 oldcmd = bindings[t];
349 bindings[t] = cmd;
350 change_command(keyspec, oldcmd, cmd);
353 void mapper::on_key_event(modifier_set& mods, key& key, event& event)
355 auto mask = event.get_change_mask();
356 unsigned i = 0;
357 while(mask) {
358 unsigned k = mask & 3;
359 if(k & 2)
360 on_key_event_subkey(mods, key, i, k == 3);
361 mask >>= 2;
362 i++;
366 void mapper::on_key_event_subkey(modifier_set& mods, key& key, unsigned skey,
367 bool polarity)
369 triplet llow(key, skey);
370 triplet lhigh(key, skey + 1);
371 auto low = bindings.lower_bound(llow);
372 auto high = bindings.lower_bound(lhigh);
373 for(auto i = low; i != high; i++) {
374 if(!mods.triggers(i->first.mod, i->first.mask))
375 continue;
376 std::string cmd = fixup_command_polarity(i->second, polarity);
377 if(cmd != "")
378 domain.invoke(cmd);
382 mapper::triplet::triplet(keyboard& k, const keyspec& spec)
384 mod = modifier_set::construct(k, spec.mod);
385 mask = modifier_set::construct(k, spec.mask);
386 if(!mod.valid(mask))
387 throw std::runtime_error("Bad modifiers");
388 auto g = keymapper_lookup_subkey(k, spec.key, false);
389 _key = g.first;
390 subkey = g.second;
391 index = false;
394 std::list<ctrlrkey*> mapper::get_controllerkeys_kbdkey(key* kbdkey)
395 throw(std::bad_alloc)
397 threads::arlock u(get_keymap_lock());
398 std::list<ctrlrkey*> r;
399 for(auto i : ckeys) {
400 for(unsigned j = 0;; j++) {
401 auto k = i.second->get(j);
402 if(!k.first)
403 break;
404 if(k.first == kbdkey)
405 r.push_back(i.second);
408 return r;
411 void mapper::add_invbind_set(invbind_set& set)
413 threads::arlock u(get_keymap_lock());
414 if(invbind_set_cbs.count(&set)) return;
415 try {
416 invbind_set_cbs.insert(&set);
417 set.add_callback(_listener);
418 } catch(...) {
419 invbind_set_cbs.erase(&set);
423 void mapper::drop_invbind_set(invbind_set& set)
425 threads::arlock h(get_keymap_lock());
426 //Drop the callback. This unregisters all.
427 set.drop_callback(_listener);
428 invbind_set_cbs.erase(&set);
431 mapper::listener::listener(mapper& _grp)
432 : grp(_grp)
436 mapper::listener::~listener()
440 void mapper::listener::create(invbind_set& s, const std::string& name, invbind_info& ibinfo)
442 threads::arlock h(get_keymap_lock());
443 ibinfo.make(grp);
446 void mapper::listener::destroy(invbind_set& s, const std::string& name)
448 threads::arlock h(get_keymap_lock());
449 if(grp.dtor_running) return;
450 grp.ibinds.erase(name);
453 void mapper::listener::kill(invbind_set& s)
455 threads::arlock h(get_keymap_lock());
456 if(grp.dtor_running) return;
457 grp.invbind_set_cbs.erase(&s);
460 invbind::invbind(mapper& kmapper, const std::string& _command, const std::string& _name, bool dynamic)
461 throw(std::bad_alloc)
462 : _mapper(&kmapper), cmd(_command), oname(_name)
464 is_dynamic = dynamic;
465 register_queue<mapper, invbind>::do_register(*_mapper, cmd, *this);
468 invbind::~invbind() throw()
470 register_queue<mapper, invbind>::do_unregister(*_mapper, cmd);
473 keyspec invbind::get(unsigned index) throw(std::bad_alloc)
475 threads::arlock u(get_keymap_lock());
476 if(index >= specs.size())
477 return keyspec();
478 return specs[index];
481 void invbind::clear(unsigned index) throw(std::bad_alloc)
483 threads::arlock u(get_keymap_lock());
484 keyspec unbind;
486 if(index >= specs.size())
487 return;
488 unbind = specs[index];
490 if(unbind && _mapper)
491 _mapper->set(unbind, "");
494 void invbind::append(const keyspec& keyspec) throw(std::bad_alloc)
496 threads::arlock u(get_keymap_lock());
497 _mapper->set(keyspec, cmd);
500 std::string invbind::getname() throw(std::bad_alloc)
502 return oname;
505 void invbind::mapper_died()
507 threads::arlock u(get_keymap_lock());
508 _mapper = NULL;
509 if(is_dynamic) delete this;
512 invbind_info::invbind_info(invbind_set& set, const std::string& _command, const std::string& _name)
513 throw(std::bad_alloc)
514 : in_set(&set)
516 command = _command;
517 name = _name;
518 in_set->do_register(command, *this);
521 invbind_info::~invbind_info() throw()
523 threads::arlock u(get_keymap_lock());
524 if(in_set)
525 in_set->do_unregister(command, *this);
528 invbind* invbind_info::make(mapper& m)
530 return new invbind(m, command, name, true);
533 void invbind_info::set_died()
535 threads::arlock u(get_keymap_lock());
536 in_set = NULL;
539 invbind_set::invbind_set()
543 invbind_set::~invbind_set()
545 auto state = set_internal_t::get_soft(this);
546 if(!state) return;
547 threads::arlock u(get_keymap_lock());
548 //Call all DCBs on all factories.
549 for(auto i : state->invbinds)
550 for(auto j : state->callbacks)
551 j->destroy(*this, i.first);
552 //Call all TCBs.
553 for(auto j : state->callbacks)
554 j->kill(*this);
555 //Notify all factories that base set died.
556 for(auto i : state->invbinds)
557 i.second->set_died();
558 //We assume factories look after themselves, so we don't destroy those.
559 set_internal_t::clear(this);
562 void invbind_set::do_register(const std::string& name, invbind_info& info)
564 threads::arlock u(get_keymap_lock());
565 auto& state = set_internal_t::get(this);
566 if(state.invbinds.count(name)) {
567 std::cerr << "WARNING: Command collision for " << name << "!" << std::endl;
568 return;
570 state.invbinds[name] = &info;
571 //Call all CCBs on this.
572 for(auto i : state.callbacks)
573 i->create(*this, name, info);
576 void invbind_set::do_unregister(const std::string& name, invbind_info& info)
578 threads::arlock u(get_keymap_lock());
579 auto state = set_internal_t::get_soft(this);
580 if(!state) return;
581 if(!state->invbinds.count(name) || state->invbinds[name] != &info) return; //Not this.
582 state->invbinds.erase(name);
583 //Call all DCBs on this.
584 for(auto i : state->callbacks)
585 i->destroy(*this, name);
588 void invbind_set::add_callback(set_listener& listener) throw(std::bad_alloc)
590 threads::arlock u(get_keymap_lock());
591 auto& state = set_internal_t::get(this);
592 state.callbacks.insert(&listener);
593 //To avoid races, call CCBs on all factories for this.
594 for(auto j : state.invbinds)
595 listener.create(*this, j.first, *j.second);
598 void invbind_set::drop_callback(set_listener& listener)
600 threads::arlock u(get_keymap_lock());
601 auto state = set_internal_t::get_soft(this);
602 if(!state) return;
603 if(state->callbacks.count(&listener)) {
604 //To avoid races, call DCBs on all factories for this.
605 for(auto j : state->invbinds)
606 listener.destroy(*this, j.first);
607 state->callbacks.erase(&listener);
611 ctrlrkey::ctrlrkey(mapper& kmapper, const std::string& _command, const std::string& _name,
612 bool _axis) throw(std::bad_alloc)
613 : _mapper(kmapper), cmd(_command), oname(_name)
615 register_queue<mapper, ctrlrkey>::do_register(_mapper, cmd, *this);
616 axis = _axis;
619 ctrlrkey::~ctrlrkey() throw()
621 register_queue<mapper, ctrlrkey>::do_unregister(_mapper, cmd);
624 std::pair<key*, unsigned> ctrlrkey::get(unsigned index) throw()
626 threads::arlock u(get_keymap_lock());
627 if(index >= keys.size())
628 return std::make_pair(reinterpret_cast<key*>(NULL), 0);
629 return keys[index];
632 std::string ctrlrkey::get_string(unsigned index) throw(std::bad_alloc)
634 auto k = get(index);
635 if(!k.first)
636 return "";
637 auto s = k.first->get_subkeys();
638 if(k.second >= s.size() || axis)
639 return k.first->get_name();
640 return k.first->get_name() + s[k.second];
643 void ctrlrkey::append(key* _key, unsigned _subkey) throw()
645 threads::arlock u(get_keymap_lock());
646 //Search for duplicates.
647 std::pair<key*, unsigned> mkey = std::make_pair(_key, _subkey);
648 for(auto i : keys)
649 if(i == mkey)
650 return;
651 //No dupes, add.
652 _key->add_listener(*this, axis);
653 keys.push_back(mkey);
656 void ctrlrkey::remove(key* _key, unsigned _subkey) throw()
658 threads::arlock u(get_keymap_lock());
659 std::pair<key*, unsigned> mkey = std::make_pair(_key, _subkey);
660 for(auto i = keys.begin(); i != keys.end(); i++) {
661 if(*i == mkey) {
662 mkey.first->remove_listener(*this);
663 keys.erase(i);
664 return;
669 void ctrlrkey::append(const std::string& _key) throw(std::bad_alloc, std::runtime_error)
671 auto g = keymapper_lookup_subkey(_mapper.get_keyboard(), _key, axis);
672 append(g.first, g.second);
675 std::pair<key*, unsigned> keymapper_lookup_subkey(keyboard& kbd, const std::string& name,
676 bool axis) throw(std::bad_alloc, std::runtime_error)
678 threads::arlock u(get_keymap_lock());
679 if(name == "")
680 return std::make_pair((key*)NULL, 0);
681 //Try direct lookup first.
682 key* key = kbd.try_lookup_key(name);
683 if(key)
684 return std::make_pair(key, 0);
685 //Axes only do direct lookup.
686 if(axis)
687 throw std::runtime_error("Invalid key");
688 std::string prefix = name;
689 char letter = prefix[prefix.length() - 1];
690 prefix = prefix.substr(0, prefix.length() - 1);
691 key = kbd.try_lookup_key(prefix);
692 if(!key)
693 throw std::runtime_error("Invalid key");
694 auto s = key->get_subkeys();
695 for(size_t i = 0; i < s.size(); i++)
696 if(s[i].length() > 0 && letter == s[i][0])
697 return std::make_pair(key, i);
698 throw std::runtime_error("Invalid key");
701 void ctrlrkey::on_key_event(modifier_set& mods, key& key, event& event)
703 if(axis) {
704 //Axes work specially.
705 _mapper.get_command_group().invoke((stringfmt() << cmd << " " << event.get_state()).str());
706 return;
708 auto mask = event.get_change_mask();
709 for(auto i : keys) {
710 if(i.first != &key)
711 continue;
712 unsigned kmask = (mask >> (2 * i.second)) & 3;
713 std::string cmd2;
714 if(kmask & 2)
715 cmd2 = mapper::fixup_command_polarity(cmd, kmask == 3);
716 if(cmd2 != "")
717 _mapper.get_command_group().invoke(cmd2);