Some tweaks to Lua docs
[lsnes.git] / src / library / gamepad.cpp
blob4edcade74cf3ecff4ee0469b86151ad2fb8c1d1a
1 #include "gamepad.hpp"
2 #include "string.hpp"
4 namespace gamepad
6 namespace
8 short angle_to_bitmask(int pov)
10 short m = 0;
11 if((pov >= 0 && pov <= 6000) || (pov >= 30000 && pov <= 36000))
12 m |= 1;
13 if(pov >= 3000 && pov <= 15000)
14 m |= 2;
15 if(pov >= 12000 && pov <= 24000)
16 m |= 4;
17 if(pov >= 21000 && pov <= 33000)
18 m |= 8;
19 return m;
22 int16_t map_value(int64_t val, int64_t minus, int64_t center, int64_t plus, int64_t neutral, bool pressure)
24 double m = minus;
25 double v = val;
26 double c = center;
27 double p = plus;
28 double n = neutral;
29 if(pressure) {
30 if(m > p) {
31 v = m - v + p;
32 std::swap(m, p);
34 if(v <= m + n)
35 return 0;
36 else if(v < p)
37 return 32767 * (v - m - n) / (p - m - n);
38 else
39 return 32767;
40 } else {
41 if(m > p) {
42 v = m - v + p;
43 c = m - c + p;
44 std::swap(m, p);
46 if(v < m)
47 return -32768;
48 else if(v < c - n)
49 return -32768 * (c - n - v) / (c - n - m);
50 else if(v <= c + n)
51 return 0;
52 else if(v < p)
53 return 32767 * (v - c - n) / (p - c - n);
54 else
55 return 32767;
60 pad::pad(const JSON::node& state, unsigned _jnum)
62 axis_fn = [](uint64_t a, uint64_t b, int16_t c) {};
63 button_fn = [](uint64_t a, uint64_t b, bool c) {};
64 hat_fn = [](uint64_t a, uint64_t b, unsigned c) {};
65 newitem_fn = [](uint64_t a, uint64_t b, int c) {};
66 jid = _jnum;
67 online_flag = false;
68 load(state);
71 pad::pad(const std::string& _xname, unsigned _jnum)
73 axis_fn = [](uint64_t a, uint64_t b, int16_t c) {};
74 button_fn = [](uint64_t a, uint64_t b, bool c) {};
75 hat_fn = [](uint64_t a, uint64_t b, unsigned c) {};
76 newitem_fn = [](uint64_t a, uint64_t b, int c) {};
77 _name = _xname;
78 jid = _jnum;
79 next_axis = 0;
80 next_button = 0;
81 next_hat = 0;
82 online_flag = false;
85 pad::~pad()
89 void pad::set_online(bool status)
91 std::list<unsigned> axes_off;
92 std::list<unsigned> buttons_off;
93 std::list<unsigned> hats_off;
95 umutex_class H(mutex);
96 if(status)
97 online_flag = status;
98 else {
99 online_flag = status;
100 //Offline everything.
101 for(auto i : _axes) {
102 if(i.second.online) try { axes_off.push_back(i.first); } catch(...) {}
103 i.second.online = false;
105 for(auto i : _buttons) {
106 if(i.second.online) try { buttons_off.push_back(i.first); } catch(...) {}
107 i.second.online = false;
109 for(auto i : _hats) {
110 if(i.second.online) try { hats_off.push_back(i.first); } catch(...) {}
111 i.second.online = false;
113 for(auto i : _axes_hat) {
114 if(i.second->online) try { hats_off.push_back(i.first); } catch(...) {}
115 i.second->online = false;
119 for(auto i : axes_off)
120 axis_fn(jid, i, 0);
121 for(auto i : buttons_off)
122 button_fn(jid, i, 0);
123 for(auto i : hats_off)
124 hat_fn(jid, i, 0);
127 unsigned pad::add_axis(uint64_t id, int64_t _min, int64_t _max, bool pressure, const std::string& xname)
129 axis_info a;
131 umutex_class H(mutex);
132 if(_axes.count(id)) {
133 _axes[id].name = xname;
134 _axes[id].online = true;
135 return _axes[id].num;
137 a.id = id;
138 a.num = next_axis++;
139 a.online = true;
140 a.state = 0;
141 a.rstate = 0;
142 a.minus = _min;
143 a.zero = pressure ? _min : (_min + _max + 1) / 2;
144 a.plus = _max;
145 a.neutral = 0;
146 a.threshold = 0.5;
147 a.name = xname;
148 a.pressure = pressure;
149 a.disabled = false;
150 _axes[id] = a;
152 newitem_fn(jid, a.num, 0);
153 return a.num;
156 unsigned pad::add_button(uint64_t id, const std::string& xname)
158 button_info b;
160 umutex_class H(mutex);
161 if(_buttons.count(id)) {
162 _buttons[id].name = xname;
163 _buttons[id].online = true;
164 return _buttons[id].num;
166 b.id = id;
167 b.num = next_button++;
168 b.name = xname;
169 b.online = true;
170 b.state = false;
171 _buttons[id] = b;
173 newitem_fn(jid, b.num, 1);
174 return b.num;
177 unsigned pad::add_hat(uint64_t id, const std::string& xname)
179 hat_info h;
181 umutex_class H(mutex);
182 if(_hats.count(id)) {
183 _hats[id].name = xname;
184 _hats[id].online = true;
185 return _hats[id].num;
187 h.id = id;
188 h.id2 = 0;
189 h.num = next_hat++;
190 h.mindev = 1;
191 h.name = xname;
192 h.online = true;
193 h.state = 0;
194 _hats[id] = h;
196 newitem_fn(jid, h.num, 2);
197 return h.num;
200 unsigned pad::add_hat(uint64_t idx, uint64_t idy, int64_t mindev, const std::string& xnamex,
201 const std::string& xnamey)
203 hat_info h;
205 umutex_class H(mutex);
206 if(_axes_hat.count(idx)) {
207 _axes_hat[idx]->name = xnamex;
208 _axes_hat[idy]->name2 = xnamey;
209 _axes_hat[idx]->online = true;
210 return _axes_hat[idx]->num;
212 h.id = idx;
213 h.id2 = idy;
214 h.num = next_hat++;
215 h.mindev = mindev;
216 h.name = xnamex;
217 h.name2 = xnamey;
218 h.online = true;
219 h.state = 0;
220 _axes_hat[idy] = _axes_hat[idx] = new hat_info(h);
222 newitem_fn(jid, h.num, 2);
223 return h.num;
226 void pad::report_axis(uint64_t id, int64_t val)
228 mutex.lock();
229 if(_axes.count(id)) {
230 axis_info& i = _axes[id];
231 int16_t val2 = map_value(val, i.minus, i.zero, i.plus, i.neutral, i.pressure);
232 int16_t ostate = i.state;
233 i.state = i.disabled ? 0 : val2;
234 i.rstate = val;
235 int16_t nstate = i.state;
236 unsigned inum = i.num;
237 mutex.unlock();
238 if(ostate != nstate)
239 axis_fn(jid, inum, nstate);
240 } else if(_axes_hat.count(id)) {
241 hat_info& i = *_axes_hat[id];
242 bool is_x = (id == i.id);
243 bool is_y = (id == i.id2);
244 unsigned ostate = i.state;
245 if(is_x) { i.state = (val <= -i.mindev) ? (i.state | 0x8) : (i.state & 0x7); }
246 if(is_x) { i.state = (val >= i.mindev) ? (i.state | 0x2) : (i.state & 0xD); }
247 if(is_y) { i.state = (val <= -i.mindev) ? (i.state | 0x1) : (i.state & 0xE); }
248 if(is_y) { i.state = (val >= i.mindev) ? (i.state | 0x4) : (i.state & 0xB); }
249 int16_t nstate = i.state;
250 unsigned inum = i.num;
251 mutex.unlock();
252 if(ostate != nstate)
253 hat_fn(jid, inum, nstate);
254 } else
255 mutex.unlock();
258 void pad::report_button(uint64_t id, bool val)
260 mutex.lock();
261 if(!_buttons.count(id)) {
262 mutex.unlock();
263 return;
265 button_info& i = _buttons[id];
266 bool ostate = i.state;
267 i.state = val;
268 int16_t nstate = i.state;
269 unsigned inum = i.num;
270 mutex.unlock();
271 if(ostate != nstate)
272 button_fn(jid, inum, nstate);
275 void pad::report_hat(uint64_t id, int angle)
277 mutex.lock();
278 unsigned h = angle_to_bitmask(angle);
279 if(!_hats.count(id)) {
280 mutex.unlock();
281 return;
283 hat_info& i = _hats[id];
284 unsigned ostate = i.state;
285 i.state = h;
286 int16_t nstate = i.state;
287 unsigned inum = i.num;
288 mutex.unlock();
289 if(ostate != nstate)
290 hat_fn(jid, inum, nstate);
293 std::set<unsigned> pad::online_axes()
295 umutex_class H(mutex);
296 std::set<unsigned> r;
297 for(auto i : _axes)
298 if(i.second.online) r.insert(i.second.num);
299 return r;
302 std::set<unsigned> pad::online_buttons()
304 umutex_class H(mutex);
305 std::set<unsigned> r;
306 for(auto i : _buttons)
307 if(i.second.online) r.insert(i.second.num);
308 return r;
311 std::set<unsigned> pad::online_hats()
313 umutex_class H(mutex);
314 std::set<unsigned> r;
315 for(auto i : _hats)
316 if(i.second.online) r.insert(i.second.num);
317 for(auto i : _axes_hat)
318 if(i.second->online) r.insert(i.second->num);
319 return r;
322 void pad::load(const JSON::node& state)
324 std::list<std::pair<unsigned, int>> notify_queue;
326 umutex_class H(mutex);
327 _name = state["name"].as_string8();
328 const JSON::node& hat_data = state["hats"];
330 if(state.field_exists("buttons")) {
331 const JSON::node& button_data = state["buttons"];
332 next_button = button_data.index_count();
333 for(size_t i = 0; i < button_data.index_count(); i++) {
334 const JSON::node& bn = button_data.index(i);
335 button_info b;
336 b.id = bn["id"].as_uint();
337 b.name = bn.field_exists("name") ? bn["name"].as_string8() : "";
338 b.num = i;
339 b.online = false;
340 b.state = false;
341 _buttons[b.id] = b;
342 notify_queue.push_back(std::make_pair(b.num, 1));
344 } else
345 next_button = 0;
347 if(state.field_exists("axes")) {
348 const JSON::node& axis_data = state["axes"];
349 next_axis = axis_data.index_count();
350 for(size_t i = 0; i < axis_data.index_count(); i++) {
351 const JSON::node& bn = axis_data.index(i);
352 axis_info a;
353 a.id = bn["id"].as_uint();
354 a.name = bn.field_exists("name") ? bn["name"].as_string8() : "";
355 a.num = i;
356 a.minus = bn["minus"].as_int();
357 a.zero = bn["zero"].as_int();
358 a.plus = bn["plus"].as_int();
359 a.neutral = bn["neutral"].as_int();
360 a.pressure = bn["pressure"].as_bool();
361 a.threshold = bn["threshold"].as_double();
362 a.disabled = bn.field_exists("disabled") ? bn["disabled"].as_bool() : false;
363 a.online = false;
364 a.state = 0;
365 a.rstate = 0;
366 _axes[a.id] = a;
367 notify_queue.push_back(std::make_pair(a.num, 0));
369 } else
370 next_axis = 0;
372 if(state.field_exists("hats")) {
373 next_hat = hat_data.index_count();
374 for(size_t i = 0; i < hat_data.index_count(); i++) {
375 const JSON::node& bn = hat_data.index(i);
376 hat_info h;
377 h.id = bn["id"].as_uint();
378 h.name = bn.field_exists("name") ? bn["name"].as_string8() : "";
379 h.name2 = bn.field_exists("name2") ? bn["name2"].as_string8() : "";
380 h.id2 = bn.field_exists("id2") ? bn["id2"].as_uint() : 0;
381 h.num = i;
382 h.mindev = bn.field_exists("mindev") ? bn["mindev"].as_int() : 1;
383 h.online = false;
384 h.state = 0;
385 if(bn.field_exists("id2")) {
386 _axes_hat[h.id2] = _axes_hat[h.id] = new hat_info(h);
387 } else {
388 _hats[h.id] = h;
390 notify_queue.push_back(std::make_pair(h.num, 2));
392 } else
393 next_hat = 0;
395 for(auto i : notify_queue) {
396 try {
397 newitem_fn(jid, i.first, i.second);
398 } catch(...) {
403 void pad::calibrate_axis(unsigned num, int64_t minus, int64_t zero, int64_t plus, int64_t neutral,
404 double threshold, bool pressure, bool disabled)
406 mutex.lock();
407 for(auto& i : _axes) {
408 if(i.second.num != num)
409 continue;
410 axis_info& a = i.second;
412 double oldtolerance = a.threshold;
413 int oldmode = a.disabled ? -1 : a.pressure ? 0 : 1;
414 a.minus = minus;
415 a.zero = zero;
416 a.plus = plus;
417 a.neutral = neutral;
418 a.pressure = pressure;
419 a.threshold = threshold;
420 a.disabled = disabled;
421 int newmode = a.disabled ? -1 : a.pressure ? 0 : 1;
422 unsigned num = a.num;
423 double newtreshold = a.threshold;
424 if(oldmode >= 0 && newmode < 0) {
425 if(a.state != 0)
426 a.state = 0;
428 mutex.unlock();
429 if(oldmode >= 0 && newmode < 0)
430 try { axis_fn(jid, num, 0); } catch(...) {}
431 if(oldmode != newmode || oldtolerance != newtreshold)
432 try { amode_fn(jid, num, newmode, newtreshold); } catch(...) {}
433 return;
435 mutex.unlock();
436 return;
439 void pad::get_calibration(unsigned num, int64_t& minus, int64_t& zero, int64_t& plus, int64_t& neutral,
440 double& threshold, bool& pressure, bool& disabled)
442 umutex_class H(mutex);
443 for(auto& i : _axes) {
444 if(i.second.num != num)
445 continue;
446 axis_info& a = i.second;
447 minus = a.minus;
448 zero = a.zero;
449 plus = a.plus;
450 neutral = a.neutral;
451 pressure = a.pressure;
452 threshold = a.threshold;
453 disabled = a.disabled;
457 double pad::get_tolerance(unsigned num)
459 umutex_class H(mutex);
460 for(auto& i : _axes) {
461 if(i.second.num != num)
462 continue;
463 return i.second.threshold;
465 return 0.5;
468 int pad::get_mode(unsigned num)
470 umutex_class H(mutex);
471 for(auto& i : _axes) {
472 if(i.second.num != num)
473 continue;
474 return i.second.disabled ? -1 : i.second.pressure ? 0 : 1;
476 return -1;
479 void pad::axis_status(unsigned num, int64_t& raw, int16_t& pct)
481 umutex_class H(mutex);
482 for(auto& i : _axes) {
483 if(i.second.num != num || !i.second.online)
484 continue;
485 raw = i.second.rstate;
486 if(i.second.disabled)
487 pct = 0;
488 else
489 pct = (int)(i.second.state / 327.67);
490 return;
492 raw = 0;
493 pct = 0;;
496 int pad::button_status(unsigned num)
498 umutex_class H(mutex);
499 for(auto& i : _buttons) {
500 if(i.second.num != num || !i.second.online)
501 continue;
502 return i.second.state ? 1 : 0;
504 return -1;
507 int pad::hat_status(unsigned num)
509 umutex_class H(mutex);
510 for(auto& i : _hats) {
511 if(i.second.num != num || !i.second.online)
512 continue;
513 return i.second.state;
515 for(auto& i : _axes_hat) {
516 if(i.second->num != num || !i.second->online)
517 continue;
518 return i.second->state;
520 return -1;
523 JSON::node pad::save()
525 umutex_class H(mutex);
526 JSON::node r(JSON::object);
527 r.insert("name", JSON::string(_name));
529 JSON::node& buttons_json = r.insert("buttons", JSON::array());
530 for(size_t i = 0; i < next_button; i++) {
531 for(auto j : _buttons) {
532 if(j.second.num != i)
533 continue;
534 JSON::node& btndata = buttons_json.append(JSON::object());
535 btndata.insert("id", JSON::number(j.second.id));
536 btndata.insert("name", JSON::string(j.second.name));
540 JSON::node& axes_json = r.insert("axes", JSON::array());
541 for(size_t i = 0; i < next_axis; i++) {
542 for(auto j : _axes) {
543 if(j.second.num != i)
544 continue;
545 JSON::node& axdata = axes_json.append(JSON::object());
546 axdata.insert("id", JSON::number(j.second.id));
547 axdata.insert("name", JSON::string(j.second.name));
548 axdata.insert("minus", JSON::number(j.second.minus));
549 axdata.insert("zero", JSON::number(j.second.zero));
550 axdata.insert("plus", JSON::number(j.second.plus));
551 axdata.insert("neutral", JSON::number(j.second.neutral));
552 axdata.insert("pressure", JSON::boolean(j.second.pressure));
553 axdata.insert("threshold", JSON::number(j.second.threshold));
554 axdata.insert("disabled", JSON::boolean(j.second.disabled));
558 JSON::node& hats_json = r.insert("hats", JSON::array());
559 for(size_t i = 0; i < next_hat; i++) {
560 for(auto j : _hats) {
561 if(j.second.num != i)
562 continue;
563 JSON::node& axdata = hats_json.append(JSON::object());
564 axdata.insert("id", JSON::number(j.second.id));
565 axdata.insert("name", JSON::string(j.second.name));
567 for(auto j : _axes_hat) {
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("id2", JSON::number(j.second->id2));
573 axdata.insert("name", JSON::string(j.second->name));
574 axdata.insert("name2", JSON::string(j.second->name2));
575 axdata.insert("mindev", JSON::number(j.second->mindev));
576 break;
580 return r;
583 std::string pad::get_summary()
585 umutex_class h(mutex);
586 std::ostringstream x;
587 x << "joystick" << jid << ": " << _name << " " << (online_flag ? "" : " [Offline]") << std::endl;
588 for(auto i : _axes) {
589 x << "joystick" << jid << "axis" << i.second.num << "[" << i.second.id << ":" << i.second.name
590 << "]: " << i.second.minus << " <- " << i.second.zero << "(" << i.second.neutral << ") -> "
591 << i.second.plus << " " << "Threshold: " << i.second.threshold;
592 if(i.second.pressure)
593 x << " [Pressure]";
594 if(i.second.disabled)
595 x << " [Disabled]";
596 if(!i.second.online)
597 x << " [Offline]";
598 x << std::endl;
600 for(auto i : _buttons) {
601 x << "joystick" << jid << "button" << i.second.num << "[" << i.second.id << ":" << i.second.name
602 << "]: ";
603 if(!i.second.online)
604 x << " [Offline]";
605 x << std::endl;
607 for(auto i : _hats) {
608 x << "joystick" << jid << "hat" << 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 : _axes_hat) {
615 if(i.first == i.second->id)
616 continue;
617 x << "joystick" << jid << "hat" << i.second->num << "[" << i.second->id << ":" << i.second->name
618 << "/" << i.second->id2 << ":" << i.second->name2 << "]: ";
619 if(!i.second->online)
620 x << " [Offline]";
621 x << std::endl;
623 return x.str();
626 set::set()
630 set::~set()
632 for(auto i : _gamepads)
633 delete i;
636 void set::load(const JSON::node& state)
638 bool locked = true;
639 mutex.lock();
640 for(auto i : _gamepads)
641 delete i;
642 _gamepads.clear();
644 for(size_t i = 0; i < state.index_count(); i++) {
645 const JSON::node& gpn = state.index(i);
646 pad* gp = NULL;
647 try {
648 gp = NULL;
649 gp = new pad("", _gamepads.size());
650 gp->set_axis_cb(axis_fn);
651 gp->set_button_cb(button_fn);
652 gp->set_hat_cb(hat_fn);
653 gp->set_axismode_cb(amode_fn);
654 gp->set_newitem_cb(newitem_fn);
655 _gamepads.push_back(gp);
656 locked = false;
657 mutex.unlock();
658 gp->load(gpn);
659 mutex.lock();
660 locked = true;
661 } catch(std::runtime_error& e) {
662 std::cerr << "Can't load gamepad #" << i << " configuration: " << e.what() << std::endl;
663 if(!locked)
664 mutex.lock();
665 delete gp;
668 mutex.unlock();
671 JSON::node set::save()
673 umutex_class h(mutex);
674 JSON::node n(JSON::array);
675 for(auto i : _gamepads)
676 n.append(i->save());
677 return n;
680 unsigned set::gamepads()
682 umutex_class h(mutex);
683 return _gamepads.size();
686 pad& set::operator[](unsigned gpnum)
688 umutex_class h(mutex);
689 if(gpnum >= _gamepads.size())
690 throw std::runtime_error("Invalid gamepad index");
691 return *_gamepads[gpnum];
694 unsigned set::add(const std::string& name)
696 umutex_class h(mutex);
697 for(size_t i = 0; i < _gamepads.size(); i++) {
698 if(!_gamepads[i]->online() && _gamepads[i]->name() == name) {
699 _gamepads[i]->set_online(true);
700 return i;
703 //No suitable found, create one.
704 pad* gp = NULL;
705 try {
706 gp = new pad(name, _gamepads.size());
707 gp->set_online(true);
708 gp->set_axis_cb(axis_fn);
709 gp->set_button_cb(button_fn);
710 gp->set_hat_cb(hat_fn);
711 gp->set_axismode_cb(amode_fn);
712 gp->set_newitem_cb(newitem_fn);
713 _gamepads.push_back(gp);
714 return _gamepads.size() - 1;
715 } catch(...) {
716 delete gp;
720 void set::set_axis_cb(std::function<void(unsigned jnum, unsigned num, int16_t val)> fn)
722 umutex_class h(mutex);
723 axis_fn = fn;
724 for(auto i : _gamepads)
725 i->set_axis_cb(axis_fn);
728 void set::set_button_cb(std::function<void(unsigned jnum, unsigned num, bool val)> fn)
730 umutex_class h(mutex);
731 button_fn = fn;
732 for(auto i : _gamepads)
733 i->set_button_cb(button_fn);
736 void set::set_hat_cb(std::function<void(unsigned jnum, unsigned num, unsigned val)> fn)
738 umutex_class h(mutex);
739 hat_fn = fn;
740 for(auto i : _gamepads)
741 i->set_hat_cb(hat_fn);
744 void set::set_axismode_cb(std::function<void(unsigned jnum, unsigned num, int mode, double tolerance)> fn)
746 umutex_class h(mutex);
747 amode_fn = fn;
748 for(auto i : _gamepads)
749 i->set_axismode_cb(amode_fn);
752 void set::set_newitem_cb(std::function<void(unsigned jnum, unsigned num, int type)> fn)
754 umutex_class h(mutex);
755 newitem_fn = fn;
756 for(auto i : _gamepads)
757 i->set_newitem_cb(newitem_fn);
760 std::string set::get_summary()
762 umutex_class h(mutex);
763 std::ostringstream x;
764 for(auto i : _gamepads)
765 x << i->get_summary();
766 return x.str();