Upload UI
[lsnes.git] / src / library / keymapper.cpp
blob9509ca3ae350fe284ad5f8b25df0d07915101d4b
1 #include "keymapper.hpp"
2 #include "register-queue.hpp"
3 #include "string.hpp"
5 std::string keyboard_mapper::fixup_command_polarity(std::string cmd, bool polarity) throw(std::bad_alloc)
7 if(cmd == "" || cmd == "*")
8 return "";
9 if(cmd[0] != '*') {
10 if(cmd[0] != '+' && polarity)
11 return "";
12 if(cmd[0] == '+' && !polarity)
13 cmd[0] = '-';
14 } else {
15 if(cmd[1] != '+' && polarity)
16 return "";
17 if(cmd[1] == '+' && !polarity)
18 cmd[1] = '-';
20 return cmd;
23 key_specifier::key_specifier() throw(std::bad_alloc)
27 key_specifier::key_specifier(const std::string& keyspec) throw(std::bad_alloc, std::runtime_error)
29 regex_results r = regex("([^/]*)/([^|]*)\\|(.*)", keyspec, "Invalid keyspec");
30 mod = r[1];
31 mask = r[2];
32 key = r[3];
35 key_specifier::operator std::string() throw(std::bad_alloc)
37 return mod + "/" + mask + "|" + key;
40 key_specifier::operator bool() throw()
42 return (key != "");
45 bool key_specifier::operator!() throw()
47 return (key == "");
50 void key_specifier::clear() throw()
52 mod = "";
53 mask = "";
54 key = "";
57 bool key_specifier::operator==(const key_specifier& keyspec)
59 return (mod == keyspec.mod && mask == keyspec.mask && key == keyspec.key);
62 bool key_specifier::operator!=(const key_specifier& keyspec)
64 return (mod != keyspec.mod || mask != keyspec.mask || key != keyspec.key);
67 std::set<inverse_bind*> keyboard_mapper::get_inverses() throw(std::bad_alloc)
69 umutex_class u(mutex);
70 std::set<inverse_bind*> r;
71 for(auto i : ibinds)
72 r.insert(i.second);
73 return r;
76 inverse_bind* keyboard_mapper::get_inverse(const std::string& command) throw(std::bad_alloc)
78 umutex_class u(mutex);
79 if(ibinds.count(command))
80 return ibinds[command];
81 else
82 return NULL;
85 std::set<controller_key*> keyboard_mapper::get_controller_keys() throw(std::bad_alloc)
87 umutex_class u(mutex);
88 std::set<controller_key*> r;
89 for(auto i : ckeys)
90 r.insert(i.second);
91 return r;
94 controller_key* keyboard_mapper::get_controllerkey(const std::string& command) throw(std::bad_alloc)
96 umutex_class u(mutex);
97 if(ckeys.count(command))
98 return ckeys[command];
99 else
100 return NULL;
103 void keyboard_mapper::do_register_inverse(const std::string& name, inverse_bind& ibind) throw(std::bad_alloc)
105 umutex_class u(mutex);
106 ibinds[name] = &ibind;
107 //Search for matches.
108 for(auto i : bindings)
109 if(i.second == ibind.cmd) {
110 umutex_class u2(ibind.mutex);
111 ibind.specs.push_back(i.first.as_keyspec());
115 void keyboard_mapper::do_unregister_inverse(const std::string& name) throw(std::bad_alloc)
117 umutex_class u(mutex);
118 ibinds.erase(name);
121 void keyboard_mapper::do_register_ckey(const std::string& name, controller_key& ckey) throw(std::bad_alloc)
123 umutex_class u(mutex);
124 ckeys[name] = &ckey;
127 void keyboard_mapper::do_unregister_ckey(const std::string& name) throw(std::bad_alloc)
129 umutex_class u(mutex);
130 ckeys.erase(name);
133 keyboard& keyboard_mapper::get_keyboard() throw()
135 return kbd;
138 keyboard_mapper::keyboard_mapper(keyboard& _kbd, command_group& _domain) throw(std::bad_alloc)
139 : inverse_proxy(*this), controllerkey_proxy(*this), kbd(_kbd), domain(_domain)
141 register_queue<_inverse_proxy, inverse_bind>::do_ready(inverse_proxy, true);
142 register_queue<_controllerkey_proxy, controller_key>::do_ready(controllerkey_proxy, true);
145 keyboard_mapper::~keyboard_mapper() throw()
147 register_queue<_inverse_proxy, inverse_bind>::do_ready(inverse_proxy, false);
148 register_queue<_controllerkey_proxy, controller_key>::do_ready(controllerkey_proxy, false);
151 keyboard_mapper::triplet::triplet(keyboard_modifier_set _mod, keyboard_modifier_set _mask, keyboard_key& _key,
152 unsigned _subkey)
154 mod = _mod;
155 mask = _mask;
156 key = &_key;
157 subkey = _subkey;
158 index = false;
161 keyboard_mapper::triplet::triplet(keyboard_key& _key, unsigned _subkey)
163 key = &_key;
164 subkey = _subkey;
165 index = true;
168 bool keyboard_mapper::triplet::operator<(const struct triplet& a) const
170 if((uint64_t)key < (uint64_t)a.key)
171 return true;
172 if((uint64_t)key > (uint64_t)a.key)
173 return false;
174 if(subkey < a.subkey)
175 return true;
176 if(subkey > a.subkey)
177 return false;
178 if(index && !a.index)
179 return true;
180 if(!index && a.index)
181 return false;
182 if(mask < a.mask)
183 return true;
184 if(a.mask < mask)
185 return false;
186 if(mod < a.mod)
187 return true;
188 if(a.mod < mod)
189 return false;
190 return false;
193 bool keyboard_mapper::triplet::operator==(const struct triplet& a) const
195 if(index != a.index)
196 return false;
197 if(key != a.key)
198 return false;
199 if(subkey != a.subkey)
200 return false;
201 if(!(mod == a.mod))
202 return false;
203 if(!(mask == a.mask))
204 return false;
205 return true;
208 key_specifier keyboard_mapper::triplet::as_keyspec() const throw(std::bad_alloc)
210 key_specifier k;
211 k.mod = mod;
212 k.mask = mask;
213 auto s = key->get_subkeys();
214 if(s.size() > subkey)
215 k.key = key->get_name() + s[subkey];
216 else
217 k.key = key->get_name();
218 return k;
221 std::list<key_specifier> keyboard_mapper::get_bindings() throw(std::bad_alloc)
223 umutex_class u(mutex);
224 std::list<key_specifier> r;
225 for(auto i : bindings)
226 r.push_back(i.first.as_keyspec());
227 return r;
230 command_group& keyboard_mapper::get_command_group() throw()
232 return domain;
235 void keyboard_mapper::bind(std::string mod, std::string modmask, std::string keyname, std::string command)
236 throw(std::bad_alloc, std::runtime_error)
238 key_specifier spec;
239 spec.mod = mod;
240 spec.mask = modmask;
241 spec.key = keyname;
242 triplet t(kbd, spec);
243 umutex_class u(mutex);
244 if(bindings.count(t))
245 throw std::runtime_error("Key is already bound");
246 if(!listening.count(t.key)) {
247 t.key->add_listener(*this, false);
248 listening.insert(t.key);
250 std::string old_command;
251 if(bindings.count(t))
252 old_command = bindings[t];
253 bindings[t] = command;
254 change_command(spec, old_command, command);
257 void keyboard_mapper::unbind(std::string mod, std::string modmask, std::string keyname) throw(std::bad_alloc,
258 std::runtime_error)
260 key_specifier spec;
261 spec.mod = mod;
262 spec.mask = modmask;
263 spec.key = keyname;
264 triplet t(kbd, spec);
265 umutex_class u(mutex);
266 if(!bindings.count(t))
267 throw std::runtime_error("Key is not bound");
268 //No harm at leaving listeners listening.
269 std::string old_command;
270 if(bindings.count(t))
271 old_command = bindings[t];
272 bindings.erase(t);
273 change_command(spec, old_command, "");
276 std::string keyboard_mapper::get(const key_specifier& keyspec) throw(std::bad_alloc)
278 triplet t(kbd, keyspec);
279 umutex_class u(mutex);
280 if(!bindings.count(t))
281 return "";
282 return bindings[t];
285 void keyboard_mapper::change_command(const key_specifier& spec, const std::string& old, const std::string& newc)
287 if(old != "" && ibinds.count(old)) {
288 auto& i = ibinds[old];
290 umutex_class u2(i->mutex);
291 i->specs.clear();
293 for(auto j : bindings)
294 if(j.second == i->cmd && j.first.as_keyspec() != spec) {
295 umutex_class u2(i->mutex);
296 i->specs.push_back(j.first.as_keyspec());
299 if(newc != "" && ibinds.count(newc)) {
300 auto& i = ibinds[newc];
301 umutex_class u2(i->mutex);
302 i->specs.push_back(spec);
306 void keyboard_mapper::set(const key_specifier& keyspec, const std::string& cmd) throw(std::bad_alloc,
307 std::runtime_error)
309 triplet t(kbd, keyspec);
310 umutex_class u(mutex);
311 if(!listening.count(t.key)) {
312 t.key->add_listener(*this, false);
313 listening.insert(t.key);
315 std::string oldcmd;
316 if(bindings.count(t))
317 oldcmd = bindings[t];
318 bindings[t] = cmd;
319 change_command(keyspec, oldcmd, cmd);
322 void keyboard_mapper::on_key_event(keyboard_modifier_set& mods, keyboard_key& key, keyboard_event& event)
324 auto mask = event.get_change_mask();
325 unsigned i = 0;
326 while(mask) {
327 unsigned k = mask & 3;
328 if(k & 2)
329 on_key_event_subkey(mods, key, i, k == 3);
330 mask >>= 2;
331 i++;
335 void keyboard_mapper::on_key_event_subkey(keyboard_modifier_set& mods, keyboard_key& key, unsigned skey,
336 bool polarity)
338 triplet llow(key, skey);
339 triplet lhigh(key, skey + 1);
340 auto low = bindings.lower_bound(llow);
341 auto high = bindings.lower_bound(lhigh);
342 for(auto i = low; i != high; i++) {
343 if(!mods.triggers(i->first.mod, i->first.mask))
344 continue;
345 std::string cmd = fixup_command_polarity(i->second, polarity);
346 if(cmd != "")
347 domain.invoke(cmd);
351 keyboard_mapper::triplet::triplet(keyboard& k, const key_specifier& spec)
353 mod = keyboard_modifier_set::construct(k, spec.mod);
354 mask = keyboard_modifier_set::construct(k, spec.mask);
355 if(!mod.valid(mask))
356 throw std::runtime_error("Bad modifiers");
357 auto g = keymapper_lookup_subkey(k, spec.key, false);
358 key = g.first;
359 subkey = g.second;
360 index = false;
363 std::list<controller_key*> keyboard_mapper::get_controllerkeys_kbdkey(keyboard_key* kbdkey)
364 throw(std::bad_alloc)
366 umutex_class u(mutex);
367 std::list<controller_key*> r;
368 for(auto i : ckeys) {
369 for(unsigned j = 0;; j++) {
370 auto k = i.second->get(j);
371 if(!k.first)
372 break;
373 if(k.first == kbdkey)
374 r.push_back(i.second);
377 return r;
380 inverse_bind::inverse_bind(keyboard_mapper& _mapper, const std::string& _command, const std::string& _name)
381 throw(std::bad_alloc)
382 : mapper(_mapper), cmd(_command), oname(_name)
384 register_queue<keyboard_mapper::_inverse_proxy, inverse_bind>::do_register(mapper.inverse_proxy, cmd, *this);
387 inverse_bind::~inverse_bind() throw()
389 register_queue<keyboard_mapper::_inverse_proxy, inverse_bind>::do_unregister(mapper.inverse_proxy, cmd);
392 key_specifier inverse_bind::get(unsigned index) throw(std::bad_alloc)
394 umutex_class u(mutex);
395 if(index >= specs.size())
396 return key_specifier();
397 return specs[index];
400 void inverse_bind::clear(unsigned index) throw(std::bad_alloc)
402 key_specifier unbind;
404 umutex_class u(mutex);
405 if(index >= specs.size())
406 return;
407 unbind = specs[index];
409 if(unbind)
410 mapper.set(unbind, "");
413 void inverse_bind::append(const key_specifier& keyspec) throw(std::bad_alloc)
415 mapper.set(keyspec, cmd);
418 std::string inverse_bind::getname() throw(std::bad_alloc)
420 return oname;
423 controller_key::controller_key(keyboard_mapper& _mapper, const std::string& _command, const std::string& _name,
424 bool _axis) throw(std::bad_alloc)
425 : mapper(_mapper), cmd(_command), oname(_name)
427 register_queue<keyboard_mapper::_controllerkey_proxy, controller_key>::do_register(mapper.controllerkey_proxy,
428 cmd, *this);
429 axis = _axis;
432 controller_key::~controller_key() throw()
434 register_queue<keyboard_mapper::_controllerkey_proxy, controller_key>::do_unregister(
435 mapper.controllerkey_proxy, cmd);
438 std::pair<keyboard_key*, unsigned> controller_key::get(unsigned index) throw()
440 umutex_class u(mutex);
441 if(index >= keys.size())
442 return std::make_pair(reinterpret_cast<keyboard_key*>(NULL), 0);
443 return keys[index];
446 std::string controller_key::get_string(unsigned index) throw(std::bad_alloc)
448 auto k = get(index);
449 if(!k.first)
450 return "";
451 auto s = k.first->get_subkeys();
452 if(k.second >= s.size() || axis)
453 return k.first->get_name();
454 return k.first->get_name() + s[k.second];
457 void controller_key::append(keyboard_key* _key, unsigned _subkey) throw()
459 umutex_class u(mutex);
460 //Search for duplicates.
461 std::pair<keyboard_key*, unsigned> mkey = std::make_pair(_key, _subkey);
462 for(auto i : keys)
463 if(i == mkey)
464 return;
465 //No dupes, add.
466 _key->add_listener(*this, axis);
467 keys.push_back(mkey);
470 void controller_key::remove(keyboard_key* _key, unsigned _subkey) throw()
472 umutex_class u(mutex);
473 std::pair<keyboard_key*, unsigned> mkey = std::make_pair(_key, _subkey);
474 for(auto i = keys.begin(); i != keys.end(); i++) {
475 if(*i == mkey) {
476 mkey.first->remove_listener(*this);
477 keys.erase(i);
478 return;
483 void controller_key::append(const std::string& _key) throw(std::bad_alloc, std::runtime_error)
485 auto g = keymapper_lookup_subkey(mapper.get_keyboard(), _key, axis);
486 append(g.first, g.second);
489 std::pair<keyboard_key*, unsigned> keymapper_lookup_subkey(keyboard& kbd, const std::string& name, bool axis)
490 throw(std::bad_alloc, std::runtime_error)
492 if(name == "")
493 return std::make_pair((keyboard_key*)NULL, 0);
494 //Try direct lookup first.
495 keyboard_key* key = kbd.try_lookup_key(name);
496 if(key)
497 return std::make_pair(key, 0);
498 //Axes only do direct lookup.
499 if(axis)
500 throw std::runtime_error("Invalid key");
501 std::string prefix = name;
502 char letter = prefix[prefix.length() - 1];
503 prefix = prefix.substr(0, prefix.length() - 1);
504 key = kbd.try_lookup_key(prefix);
505 if(!key)
506 throw std::runtime_error("Invalid key");
507 auto s = key->get_subkeys();
508 for(size_t i = 0; i < s.size(); i++)
509 if(s[i].length() > 0 && letter == s[i][0])
510 return std::make_pair(key, i);
511 throw std::runtime_error("Invalid key");
514 void controller_key::on_key_event(keyboard_modifier_set& mods, keyboard_key& key, keyboard_event& event)
516 if(axis) {
517 //Axes work specially.
518 mapper.get_command_group().invoke((stringfmt() << cmd << " " << event.get_state()).str());
519 return;
521 auto mask = event.get_change_mask();
522 for(auto i : keys) {
523 if(i.first != &key)
524 continue;
525 unsigned kmask = (mask >> (2 * i.second)) & 3;
526 std::string cmd2;
527 if(kmask & 2)
528 cmd2 = keyboard_mapper::fixup_command_polarity(cmd, kmask == 3);
529 if(cmd2 != "")
530 mapper.get_command_group().invoke(cmd2);