lsnes rr2-β24
[lsnes.git] / src / library / gamepad.cpp
blob217b10c8f4b8f2ef9c415898f9d1b829a171a4ad
1 #include <functional>
2 #include "gamepad.hpp"
3 #include "string.hpp"
5 namespace gamepad
7 namespace
9 short angle_to_bitmask(int pov)
11 short m = 0;
12 if((pov >= 0 && pov <= 6000) || (pov >= 30000 && pov <= 36000))
13 m |= 1;
14 if(pov >= 3000 && pov <= 15000)
15 m |= 2;
16 if(pov >= 12000 && pov <= 24000)
17 m |= 4;
18 if(pov >= 21000 && pov <= 33000)
19 m |= 8;
20 return m;
23 int16_t map_value(int64_t val, int64_t minus, int64_t center, int64_t plus, int64_t neutral, bool pressure)
25 double m = minus;
26 double v = val;
27 double c = center;
28 double p = plus;
29 double n = neutral;
30 if(pressure) {
31 if(m > p) {
32 v = m - v + p;
33 std::swap(m, p);
35 if(v <= m + n)
36 return 0;
37 else if(v < p)
38 return 32767 * (v - m - n) / (p - m - n);
39 else
40 return 32767;
41 } else {
42 if(m > p) {
43 v = m - v + p;
44 c = m - c + p;
45 std::swap(m, p);
47 if(v < m)
48 return -32768;
49 else if(v < c - n)
50 return -32768 * (c - n - v) / (c - n - m);
51 else if(v <= c + n)
52 return 0;
53 else if(v < p)
54 return 32767 * (v - c - n) / (p - c - n);
55 else
56 return 32767;
61 pad::pad(const JSON::node& state, unsigned _jnum)
63 axis_fn = [](uint64_t a, uint64_t b, int16_t c) {};
64 button_fn = [](uint64_t a, uint64_t b, bool c) {};
65 hat_fn = [](uint64_t a, uint64_t b, unsigned c) {};
66 newitem_fn = [](uint64_t a, uint64_t b, int c) {};
67 jid = _jnum;
68 online_flag = false;
69 load(state);
72 pad::pad(const std::string& _xname, unsigned _jnum)
74 axis_fn = [](uint64_t a, uint64_t b, int16_t c) {};
75 button_fn = [](uint64_t a, uint64_t b, bool c) {};
76 hat_fn = [](uint64_t a, uint64_t b, unsigned c) {};
77 newitem_fn = [](uint64_t a, uint64_t b, int c) {};
78 _name = _xname;
79 jid = _jnum;
80 next_axis = 0;
81 next_button = 0;
82 next_hat = 0;
83 online_flag = false;
86 pad::~pad()
88 //Set _axes_hat entries that alias to NULL.
89 for(auto i = _axes_hat.begin(); i != _axes_hat.end(); i++)
90 for(auto j = i; j != _axes_hat.end(); j++)
91 if(i != j && i->second == j->second)
92 j->second = NULL;
93 for(auto i : _axes_hat) delete i.second;
96 void pad::set_online(bool status)
98 std::list<unsigned> axes_off;
99 std::list<unsigned> buttons_off;
100 std::list<unsigned> hats_off;
102 threads::alock H(mlock);
103 if(status)
104 online_flag = status;
105 else {
106 online_flag = status;
107 //Offline everything.
108 for(auto& i : _axes) {
109 if(i.second.online) try { axes_off.push_back(i.first); } catch(...) {}
110 i.second.online = false;
112 for(auto& i : _buttons) {
113 if(i.second.online) try { buttons_off.push_back(i.first); } catch(...) {}
114 i.second.online = false;
116 for(auto& i : _hats) {
117 if(i.second.online) try { hats_off.push_back(i.first); } catch(...) {}
118 i.second.online = false;
120 for(auto& i : _axes_hat) {
121 if(i.second->online) try { hats_off.push_back(i.first); } catch(...) {}
122 i.second->online = false;
126 for(auto i : axes_off)
127 axis_fn(jid, i, 0);
128 for(auto i : buttons_off)
129 button_fn(jid, i, 0);
130 for(auto i : hats_off)
131 hat_fn(jid, i, 0);
134 unsigned pad::add_axis(uint64_t id, int64_t _min, int64_t _max, bool pressure, const std::string& xname)
136 axis_info a;
138 threads::alock H(mlock);
139 if(_axes.count(id)) {
140 _axes[id].name = xname;
141 _axes[id].online = true;
142 return _axes[id].num;
144 a.id = id;
145 a.num = next_axis++;
146 a.online = true;
147 a.state = 0;
148 a.rstate = 0;
149 a.minus = _min;
150 a.zero = pressure ? _min : (_min + _max + 1) / 2;
151 a.plus = _max;
152 a.neutral = 0;
153 a.threshold = 0.5;
154 a.name = xname;
155 a.pressure = pressure;
156 a.disabled = false;
157 _axes[id] = a;
159 newitem_fn(jid, a.num, 0);
160 return a.num;
163 unsigned pad::add_button(uint64_t id, const std::string& xname)
165 button_info b;
167 threads::alock H(mlock);
168 if(_buttons.count(id)) {
169 _buttons[id].name = xname;
170 _buttons[id].online = true;
171 return _buttons[id].num;
173 b.id = id;
174 b.num = next_button++;
175 b.name = xname;
176 b.online = true;
177 b.state = false;
178 _buttons[id] = b;
180 newitem_fn(jid, b.num, 1);
181 return b.num;
184 unsigned pad::add_hat(uint64_t id, const std::string& xname)
186 hat_info h;
188 threads::alock H(mlock);
189 if(_hats.count(id)) {
190 _hats[id].name = xname;
191 _hats[id].online = true;
192 return _hats[id].num;
194 h.id = id;
195 h.id2 = 0;
196 h.num = next_hat++;
197 h.mindev = 1;
198 h.name = xname;
199 h.online = true;
200 h.state = 0;
201 _hats[id] = h;
203 newitem_fn(jid, h.num, 2);
204 return h.num;
207 unsigned pad::add_hat(uint64_t idx, uint64_t idy, int64_t mindev, const std::string& xnamex,
208 const std::string& xnamey)
210 hat_info h;
212 threads::alock H(mlock);
213 if(_axes_hat.count(idx)) {
214 _axes_hat[idx]->name = xnamex;
215 _axes_hat[idy]->name2 = xnamey;
216 _axes_hat[idx]->online = true;
217 return _axes_hat[idx]->num;
219 h.id = idx;
220 h.id2 = idy;
221 h.num = next_hat++;
222 h.mindev = mindev;
223 h.name = xnamex;
224 h.name2 = xnamey;
225 h.online = true;
226 h.state = 0;
227 _axes_hat[idy] = _axes_hat[idx] = new hat_info(h);
229 newitem_fn(jid, h.num, 2);
230 return h.num;
233 void pad::report_axis(uint64_t id, int64_t val)
235 mlock.lock();
236 if(_axes.count(id)) {
237 axis_info& i = _axes[id];
238 int16_t val2 = map_value(val, i.minus, i.zero, i.plus, i.neutral, i.pressure);
239 int16_t ostate = i.state;
240 i.state = i.disabled ? 0 : val2;
241 i.rstate = val;
242 int16_t nstate = i.state;
243 unsigned inum = i.num;
244 mlock.unlock();
245 if(ostate != nstate)
246 axis_fn(jid, inum, nstate);
247 } else if(_axes_hat.count(id)) {
248 hat_info& i = *_axes_hat[id];
249 bool is_x = (id == i.id);
250 bool is_y = (id == i.id2);
251 signed ostate = i.state;
252 if(is_x) { i.state = (val <= -i.mindev) ? (i.state | 0x8) : (i.state & 0x7); }
253 if(is_x) { i.state = (val >= i.mindev) ? (i.state | 0x2) : (i.state & 0xD); }
254 if(is_y) { i.state = (val <= -i.mindev) ? (i.state | 0x1) : (i.state & 0xE); }
255 if(is_y) { i.state = (val >= i.mindev) ? (i.state | 0x4) : (i.state & 0xB); }
256 int16_t nstate = i.state;
257 unsigned inum = i.num;
258 mlock.unlock();
259 if(ostate != nstate)
260 hat_fn(jid, inum, nstate);
261 } else
262 mlock.unlock();
265 void pad::report_button(uint64_t id, bool val)
267 mlock.lock();
268 if(!_buttons.count(id)) {
269 mlock.unlock();
270 return;
272 button_info& i = _buttons[id];
273 bool ostate = i.state;
274 i.state = val;
275 int16_t nstate = i.state;
276 unsigned inum = i.num;
277 mlock.unlock();
278 if(ostate != nstate)
279 button_fn(jid, inum, nstate);
282 void pad::report_hat(uint64_t id, int angle)
284 mlock.lock();
285 unsigned h = angle_to_bitmask(angle);
286 if(!_hats.count(id)) {
287 mlock.unlock();
288 return;
290 hat_info& i = _hats[id];
291 signed ostate = i.state;
292 i.state = h;
293 int16_t nstate = i.state;
294 unsigned inum = i.num;
295 mlock.unlock();
296 if(ostate != nstate)
297 hat_fn(jid, inum, nstate);
300 std::set<unsigned> pad::online_axes()
302 threads::alock H(mlock);
303 std::set<unsigned> r;
304 for(auto i : _axes)
305 if(i.second.online) r.insert(i.second.num);
306 return r;
309 std::set<unsigned> pad::online_buttons()
311 threads::alock H(mlock);
312 std::set<unsigned> r;
313 for(auto i : _buttons)
314 if(i.second.online) r.insert(i.second.num);
315 return r;
318 std::set<unsigned> pad::online_hats()
320 threads::alock H(mlock);
321 std::set<unsigned> r;
322 for(auto i : _hats)
323 if(i.second.online) r.insert(i.second.num);
324 for(auto i : _axes_hat)
325 if(i.second->online) r.insert(i.second->num);
326 return r;
329 void pad::load(const JSON::node& state)
331 std::list<std::pair<unsigned, int>> notify_queue;
333 threads::alock H(mlock);
334 _name = state["name"].as_string8();
335 const JSON::node& hat_data = state["hats"];
337 if(state.field_exists("buttons")) {
338 const JSON::node& button_data = state["buttons"];
339 next_button = button_data.index_count();
340 for(size_t i = 0; i < button_data.index_count(); i++) {
341 const JSON::node& bn = button_data.index(i);
342 button_info b;
343 b.id = bn["id"].as_uint();
344 b.name = bn.field_exists("name") ? bn["name"].as_string8() : "";
345 b.num = i;
346 b.online = false;
347 b.state = false;
348 _buttons[b.id] = b;
349 notify_queue.push_back(std::make_pair(b.num, 1));
351 } else
352 next_button = 0;
354 if(state.field_exists("axes")) {
355 const JSON::node& axis_data = state["axes"];
356 next_axis = axis_data.index_count();
357 for(size_t i = 0; i < axis_data.index_count(); i++) {
358 const JSON::node& bn = axis_data.index(i);
359 axis_info a;
360 a.id = bn["id"].as_uint();
361 a.name = bn.field_exists("name") ? bn["name"].as_string8() : "";
362 a.num = i;
363 a.minus = bn["minus"].as_int();
364 a.zero = bn["zero"].as_int();
365 a.plus = bn["plus"].as_int();
366 a.neutral = bn["neutral"].as_int();
367 a.pressure = bn["pressure"].as_bool();
368 a.threshold = bn["threshold"].as_double();
369 a.disabled = bn.field_exists("disabled") ? bn["disabled"].as_bool() : false;
370 a.online = false;
371 a.state = 0;
372 a.rstate = 0;
373 _axes[a.id] = a;
374 notify_queue.push_back(std::make_pair(a.num, 0));
376 } else
377 next_axis = 0;
379 if(state.field_exists("hats")) {
380 next_hat = hat_data.index_count();
381 for(size_t i = 0; i < hat_data.index_count(); i++) {
382 const JSON::node& bn = hat_data.index(i);
383 hat_info h;
384 h.id = bn["id"].as_uint();
385 h.name = bn.field_exists("name") ? bn["name"].as_string8() : "";
386 h.name2 = bn.field_exists("name2") ? bn["name2"].as_string8() : "";
387 h.id2 = bn.field_exists("id2") ? bn["id2"].as_uint() : 0;
388 h.num = i;
389 h.mindev = bn.field_exists("mindev") ? bn["mindev"].as_int() : 1;
390 h.online = false;
391 h.state = 0;
392 if(bn.field_exists("id2")) {
393 _axes_hat[h.id2] = _axes_hat[h.id] = new hat_info(h);
394 } else {
395 _hats[h.id] = h;
397 notify_queue.push_back(std::make_pair(h.num, 2));
399 } else
400 next_hat = 0;
402 for(auto i : notify_queue) {
403 try {
404 newitem_fn(jid, i.first, i.second);
405 } catch(...) {
410 void pad::calibrate_axis(unsigned num, int64_t minus, int64_t zero, int64_t plus, int64_t neutral,
411 double threshold, bool pressure, bool disabled)
413 mlock.lock();
414 for(auto& i : _axes) {
415 if(i.second.num != num)
416 continue;
417 axis_info& a = i.second;
419 double oldtolerance = a.threshold;
420 int oldmode = a.disabled ? -1 : a.pressure ? 0 : 1;
421 a.minus = minus;
422 a.zero = zero;
423 a.plus = plus;
424 a.neutral = neutral;
425 a.pressure = pressure;
426 a.threshold = threshold;
427 a.disabled = disabled;
428 int newmode = a.disabled ? -1 : a.pressure ? 0 : 1;
429 unsigned num = a.num;
430 double newtreshold = a.threshold;
431 if(oldmode >= 0 && newmode < 0) {
432 if(a.state != 0)
433 a.state = 0;
435 mlock.unlock();
436 if(oldmode >= 0 && newmode < 0)
437 try { axis_fn(jid, num, 0); } catch(...) {}
438 if(oldmode != newmode || oldtolerance != newtreshold)
439 try { amode_fn(jid, num, newmode, newtreshold); } catch(...) {}
440 return;
442 mlock.unlock();
443 return;
446 void pad::get_calibration(unsigned num, int64_t& minus, int64_t& zero, int64_t& plus, int64_t& neutral,
447 double& threshold, bool& pressure, bool& disabled)
449 threads::alock H(mlock);
450 for(auto& i : _axes) {
451 if(i.second.num != num)
452 continue;
453 axis_info& a = i.second;
454 minus = a.minus;
455 zero = a.zero;
456 plus = a.plus;
457 neutral = a.neutral;
458 pressure = a.pressure;
459 threshold = a.threshold;
460 disabled = a.disabled;
464 double pad::get_tolerance(unsigned num)
466 threads::alock H(mlock);
467 for(auto& i : _axes) {
468 if(i.second.num != num)
469 continue;
470 return i.second.threshold;
472 return 0.5;
475 int pad::get_mode(unsigned num)
477 threads::alock H(mlock);
478 for(auto& i : _axes) {
479 if(i.second.num != num)
480 continue;
481 return i.second.disabled ? -1 : i.second.pressure ? 0 : 1;
483 return -1;
486 void pad::axis_status(unsigned num, int64_t& raw, int16_t& pct)
488 threads::alock H(mlock);
489 for(auto& i : _axes) {
490 if(i.second.num != num || !i.second.online)
491 continue;
492 raw = i.second.rstate;
493 if(i.second.disabled)
494 pct = 0;
495 else
496 pct = (int)(i.second.state / 327.67);
497 return;
499 raw = 0;
500 pct = 0;;
503 int pad::button_status(unsigned num)
505 threads::alock H(mlock);
506 for(auto& i : _buttons) {
507 if(i.second.num != num || !i.second.online)
508 continue;
509 return i.second.state ? 1 : 0;
511 return -1;
514 int pad::hat_status(unsigned num)
516 threads::alock H(mlock);
517 for(auto& i : _hats) {
518 if(i.second.num != num || !i.second.online)
519 continue;
520 return i.second.state;
522 for(auto& i : _axes_hat) {
523 if(i.second->num != num || !i.second->online)
524 continue;
525 return i.second->state;
527 return -1;
530 JSON::node pad::save()
532 threads::alock H(mlock);
533 JSON::node r(JSON::object);
534 r.insert("name", JSON::string(_name));
536 JSON::node& buttons_json = r.insert("buttons", JSON::array());
537 for(size_t i = 0; i < next_button; i++) {
538 for(auto j : _buttons) {
539 if(j.second.num != i)
540 continue;
541 JSON::node& btndata = buttons_json.append(JSON::object());
542 btndata.insert("id", JSON::number(j.second.id));
543 btndata.insert("name", JSON::string(j.second.name));
547 JSON::node& axes_json = r.insert("axes", JSON::array());
548 for(size_t i = 0; i < next_axis; i++) {
549 for(auto j : _axes) {
550 if(j.second.num != i)
551 continue;
552 JSON::node& axdata = axes_json.append(JSON::object());
553 axdata.insert("id", JSON::number(j.second.id));
554 axdata.insert("name", JSON::string(j.second.name));
555 axdata.insert("minus", JSON::number(j.second.minus));
556 axdata.insert("zero", JSON::number(j.second.zero));
557 axdata.insert("plus", JSON::number(j.second.plus));
558 axdata.insert("neutral", JSON::number(j.second.neutral));
559 axdata.insert("pressure", JSON::boolean(j.second.pressure));
560 axdata.insert("threshold", JSON::number(j.second.threshold));
561 axdata.insert("disabled", JSON::boolean(j.second.disabled));
565 JSON::node& hats_json = r.insert("hats", JSON::array());
566 for(size_t i = 0; i < next_hat; i++) {
567 for(auto j : _hats) {
568 if(j.second.num != i)
569 continue;
570 JSON::node& axdata = hats_json.append(JSON::object());
571 axdata.insert("id", JSON::number(j.second.id));
572 axdata.insert("name", JSON::string(j.second.name));
574 for(auto j : _axes_hat) {
575 if(j.second->num != i)
576 continue;
577 JSON::node& axdata = hats_json.append(JSON::object());
578 axdata.insert("id", JSON::number(j.second->id));
579 axdata.insert("id2", JSON::number(j.second->id2));
580 axdata.insert("name", JSON::string(j.second->name));
581 axdata.insert("name2", JSON::string(j.second->name2));
582 axdata.insert("mindev", JSON::number(j.second->mindev));
583 break;
587 return r;
590 std::string pad::get_summary()
592 threads::alock h(mlock);
593 std::ostringstream x;
594 x << "joystick" << jid << ": " << _name << " " << (online_flag ? "" : " [Offline]") << std::endl;
595 for(auto i : _axes) {
596 x << "joystick" << jid << "axis" << i.second.num << "[" << i.second.id << ":" << i.second.name
597 << "]: " << i.second.minus << " <- " << i.second.zero << "(" << i.second.neutral << ") -> "
598 << i.second.plus << " " << "Threshold: " << i.second.threshold;
599 if(i.second.pressure)
600 x << " [Pressure]";
601 if(i.second.disabled)
602 x << " [Disabled]";
603 if(!i.second.online)
604 x << " [Offline]";
605 x << std::endl;
607 for(auto i : _buttons) {
608 x << "joystick" << jid << "button" << i.second.num << "[" << i.second.id << ":" << i.second.name
609 << "]: ";
610 if(!i.second.online)
611 x << " [Offline]";
612 x << std::endl;
614 for(auto i : _hats) {
615 x << "joystick" << jid << "hat" << i.second.num << "[" << i.second.id << ":" << i.second.name
616 << "]: ";
617 if(!i.second.online)
618 x << " [Offline]";
619 x << std::endl;
621 for(auto i : _axes_hat) {
622 if(i.first == i.second->id)
623 continue;
624 x << "joystick" << jid << "hat" << i.second->num << "[" << i.second->id << ":" << i.second->name
625 << "/" << i.second->id2 << ":" << i.second->name2 << "]: ";
626 if(!i.second->online)
627 x << " [Offline]";
628 x << std::endl;
630 return x.str();
633 set::set()
637 set::~set()
639 for(auto i : _gamepads)
640 delete i;
643 void set::load(const JSON::node& state)
645 bool locked = true;
646 mlock.lock();
647 for(auto i : _gamepads)
648 delete i;
649 _gamepads.clear();
651 for(size_t i = 0; i < state.index_count(); i++) {
652 const JSON::node& gpn = state.index(i);
653 pad* gp = NULL;
654 try {
655 gp = NULL;
656 gp = new pad("", _gamepads.size());
657 gp->set_axis_cb(axis_fn);
658 gp->set_button_cb(button_fn);
659 gp->set_hat_cb(hat_fn);
660 gp->set_axismode_cb(amode_fn);
661 gp->set_newitem_cb(newitem_fn);
662 _gamepads.push_back(gp);
663 locked = false;
664 mlock.unlock();
665 gp->load(gpn);
666 mlock.lock();
667 locked = true;
668 } catch(std::runtime_error& e) {
669 std::cerr << "Can't load gamepad #" << i << " configuration: " << e.what() << std::endl;
670 if(!locked)
671 mlock.lock();
672 delete gp;
675 mlock.unlock();
678 JSON::node set::save()
680 threads::alock h(mlock);
681 JSON::node n(JSON::array);
682 for(auto i : _gamepads)
683 n.append(i->save());
684 return n;
687 unsigned set::gamepads()
689 threads::alock h(mlock);
690 return _gamepads.size();
693 pad& set::operator[](unsigned gpnum)
695 threads::alock h(mlock);
696 if(gpnum >= _gamepads.size())
697 throw std::runtime_error("Invalid gamepad index");
698 return *_gamepads[gpnum];
701 unsigned set::add(const std::string& name)
703 threads::alock h(mlock);
704 for(size_t i = 0; i < _gamepads.size(); i++) {
705 if(!_gamepads[i]->online() && _gamepads[i]->name() == name) {
706 auto& gp = _gamepads[i];
707 gp->set_online(true);
708 //Reset the functions.
709 gp->set_axis_cb(axis_fn);
710 gp->set_button_cb(button_fn);
711 gp->set_hat_cb(hat_fn);
712 gp->set_axismode_cb(amode_fn);
713 gp->set_newitem_cb(newitem_fn);
714 return i;
717 //No suitable found, create one.
718 pad* gp = NULL;
719 try {
720 gp = new pad(name, _gamepads.size());
721 gp->set_online(true);
722 gp->set_axis_cb(axis_fn);
723 gp->set_button_cb(button_fn);
724 gp->set_hat_cb(hat_fn);
725 gp->set_axismode_cb(amode_fn);
726 gp->set_newitem_cb(newitem_fn);
727 _gamepads.push_back(gp);
728 return _gamepads.size() - 1;
729 } catch(...) {
730 delete gp;
731 throw;
735 void set::offline_all()
737 for(size_t i = 0; i < _gamepads.size(); i++) {
738 if(_gamepads[i]->online()) {
739 _gamepads[i]->set_online(false);
744 void set::set_axis_cb(std::function<void(unsigned jnum, unsigned num, int16_t val)> fn)
746 threads::alock h(mlock);
747 axis_fn = fn;
748 for(auto i : _gamepads)
749 i->set_axis_cb(axis_fn);
752 void set::set_button_cb(std::function<void(unsigned jnum, unsigned num, bool val)> fn)
754 threads::alock h(mlock);
755 button_fn = fn;
756 for(auto i : _gamepads)
757 i->set_button_cb(button_fn);
760 void set::set_hat_cb(std::function<void(unsigned jnum, unsigned num, unsigned val)> fn)
762 threads::alock h(mlock);
763 hat_fn = fn;
764 for(auto i : _gamepads)
765 i->set_hat_cb(hat_fn);
768 void set::set_axismode_cb(std::function<void(unsigned jnum, unsigned num, int mode, double tolerance)> fn)
770 threads::alock h(mlock);
771 amode_fn = fn;
772 for(auto i : _gamepads)
773 i->set_axismode_cb(amode_fn);
776 void set::set_newitem_cb(std::function<void(unsigned jnum, unsigned num, int type)> fn)
778 threads::alock h(mlock);
779 newitem_fn = fn;
780 for(auto i : _gamepads)
781 i->set_newitem_cb(newitem_fn);
784 std::string set::get_summary()
786 threads::alock h(mlock);
787 std::ostringstream x;
788 for(auto i : _gamepads)
789 x << i->get_summary();
790 return x.str();