Fix some memory leak complaints from Valgrind
[lsnes.git] / src / library / gamepad.cpp
blobb5a78ee437c36ca3a517cd83f15391319227f9ac
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()
87 //Set _axes_hat entries that alias to NULL.
88 for(auto i = _axes_hat.begin(); i != _axes_hat.end(); i++)
89 for(auto j = i; j != _axes_hat.end(); j++)
90 if(i != j && i->second == j->second)
91 j->second = NULL;
92 for(auto i : _axes_hat) delete i.second;
95 void pad::set_online(bool status)
97 std::list<unsigned> axes_off;
98 std::list<unsigned> buttons_off;
99 std::list<unsigned> hats_off;
101 umutex_class H(mutex);
102 if(status)
103 online_flag = status;
104 else {
105 online_flag = status;
106 //Offline everything.
107 for(auto i : _axes) {
108 if(i.second.online) try { axes_off.push_back(i.first); } catch(...) {}
109 i.second.online = false;
111 for(auto i : _buttons) {
112 if(i.second.online) try { buttons_off.push_back(i.first); } catch(...) {}
113 i.second.online = false;
115 for(auto i : _hats) {
116 if(i.second.online) try { hats_off.push_back(i.first); } catch(...) {}
117 i.second.online = false;
119 for(auto i : _axes_hat) {
120 if(i.second->online) try { hats_off.push_back(i.first); } catch(...) {}
121 i.second->online = false;
125 for(auto i : axes_off)
126 axis_fn(jid, i, 0);
127 for(auto i : buttons_off)
128 button_fn(jid, i, 0);
129 for(auto i : hats_off)
130 hat_fn(jid, i, 0);
133 unsigned pad::add_axis(uint64_t id, int64_t _min, int64_t _max, bool pressure, const std::string& xname)
135 axis_info a;
137 umutex_class H(mutex);
138 if(_axes.count(id)) {
139 _axes[id].name = xname;
140 _axes[id].online = true;
141 return _axes[id].num;
143 a.id = id;
144 a.num = next_axis++;
145 a.online = true;
146 a.state = 0;
147 a.rstate = 0;
148 a.minus = _min;
149 a.zero = pressure ? _min : (_min + _max + 1) / 2;
150 a.plus = _max;
151 a.neutral = 0;
152 a.threshold = 0.5;
153 a.name = xname;
154 a.pressure = pressure;
155 a.disabled = false;
156 _axes[id] = a;
158 newitem_fn(jid, a.num, 0);
159 return a.num;
162 unsigned pad::add_button(uint64_t id, const std::string& xname)
164 button_info b;
166 umutex_class H(mutex);
167 if(_buttons.count(id)) {
168 _buttons[id].name = xname;
169 _buttons[id].online = true;
170 return _buttons[id].num;
172 b.id = id;
173 b.num = next_button++;
174 b.name = xname;
175 b.online = true;
176 b.state = false;
177 _buttons[id] = b;
179 newitem_fn(jid, b.num, 1);
180 return b.num;
183 unsigned pad::add_hat(uint64_t id, const std::string& xname)
185 hat_info h;
187 umutex_class H(mutex);
188 if(_hats.count(id)) {
189 _hats[id].name = xname;
190 _hats[id].online = true;
191 return _hats[id].num;
193 h.id = id;
194 h.id2 = 0;
195 h.num = next_hat++;
196 h.mindev = 1;
197 h.name = xname;
198 h.online = true;
199 h.state = 0;
200 _hats[id] = h;
202 newitem_fn(jid, h.num, 2);
203 return h.num;
206 unsigned pad::add_hat(uint64_t idx, uint64_t idy, int64_t mindev, const std::string& xnamex,
207 const std::string& xnamey)
209 hat_info h;
211 umutex_class H(mutex);
212 if(_axes_hat.count(idx)) {
213 _axes_hat[idx]->name = xnamex;
214 _axes_hat[idy]->name2 = xnamey;
215 _axes_hat[idx]->online = true;
216 return _axes_hat[idx]->num;
218 h.id = idx;
219 h.id2 = idy;
220 h.num = next_hat++;
221 h.mindev = mindev;
222 h.name = xnamex;
223 h.name2 = xnamey;
224 h.online = true;
225 h.state = 0;
226 _axes_hat[idy] = _axes_hat[idx] = new hat_info(h);
228 newitem_fn(jid, h.num, 2);
229 return h.num;
232 void pad::report_axis(uint64_t id, int64_t val)
234 mutex.lock();
235 if(_axes.count(id)) {
236 axis_info& i = _axes[id];
237 int16_t val2 = map_value(val, i.minus, i.zero, i.plus, i.neutral, i.pressure);
238 int16_t ostate = i.state;
239 i.state = i.disabled ? 0 : val2;
240 i.rstate = val;
241 int16_t nstate = i.state;
242 unsigned inum = i.num;
243 mutex.unlock();
244 if(ostate != nstate)
245 axis_fn(jid, inum, nstate);
246 } else if(_axes_hat.count(id)) {
247 hat_info& i = *_axes_hat[id];
248 bool is_x = (id == i.id);
249 bool is_y = (id == i.id2);
250 unsigned ostate = i.state;
251 if(is_x) { i.state = (val <= -i.mindev) ? (i.state | 0x8) : (i.state & 0x7); }
252 if(is_x) { i.state = (val >= i.mindev) ? (i.state | 0x2) : (i.state & 0xD); }
253 if(is_y) { i.state = (val <= -i.mindev) ? (i.state | 0x1) : (i.state & 0xE); }
254 if(is_y) { i.state = (val >= i.mindev) ? (i.state | 0x4) : (i.state & 0xB); }
255 int16_t nstate = i.state;
256 unsigned inum = i.num;
257 mutex.unlock();
258 if(ostate != nstate)
259 hat_fn(jid, inum, nstate);
260 } else
261 mutex.unlock();
264 void pad::report_button(uint64_t id, bool val)
266 mutex.lock();
267 if(!_buttons.count(id)) {
268 mutex.unlock();
269 return;
271 button_info& i = _buttons[id];
272 bool ostate = i.state;
273 i.state = val;
274 int16_t nstate = i.state;
275 unsigned inum = i.num;
276 mutex.unlock();
277 if(ostate != nstate)
278 button_fn(jid, inum, nstate);
281 void pad::report_hat(uint64_t id, int angle)
283 mutex.lock();
284 unsigned h = angle_to_bitmask(angle);
285 if(!_hats.count(id)) {
286 mutex.unlock();
287 return;
289 hat_info& i = _hats[id];
290 unsigned ostate = i.state;
291 i.state = h;
292 int16_t nstate = i.state;
293 unsigned inum = i.num;
294 mutex.unlock();
295 if(ostate != nstate)
296 hat_fn(jid, inum, nstate);
299 std::set<unsigned> pad::online_axes()
301 umutex_class H(mutex);
302 std::set<unsigned> r;
303 for(auto i : _axes)
304 if(i.second.online) r.insert(i.second.num);
305 return r;
308 std::set<unsigned> pad::online_buttons()
310 umutex_class H(mutex);
311 std::set<unsigned> r;
312 for(auto i : _buttons)
313 if(i.second.online) r.insert(i.second.num);
314 return r;
317 std::set<unsigned> pad::online_hats()
319 umutex_class H(mutex);
320 std::set<unsigned> r;
321 for(auto i : _hats)
322 if(i.second.online) r.insert(i.second.num);
323 for(auto i : _axes_hat)
324 if(i.second->online) r.insert(i.second->num);
325 return r;
328 void pad::load(const JSON::node& state)
330 std::list<std::pair<unsigned, int>> notify_queue;
332 umutex_class H(mutex);
333 _name = state["name"].as_string8();
334 const JSON::node& hat_data = state["hats"];
336 if(state.field_exists("buttons")) {
337 const JSON::node& button_data = state["buttons"];
338 next_button = button_data.index_count();
339 for(size_t i = 0; i < button_data.index_count(); i++) {
340 const JSON::node& bn = button_data.index(i);
341 button_info b;
342 b.id = bn["id"].as_uint();
343 b.name = bn.field_exists("name") ? bn["name"].as_string8() : "";
344 b.num = i;
345 b.online = false;
346 b.state = false;
347 _buttons[b.id] = b;
348 notify_queue.push_back(std::make_pair(b.num, 1));
350 } else
351 next_button = 0;
353 if(state.field_exists("axes")) {
354 const JSON::node& axis_data = state["axes"];
355 next_axis = axis_data.index_count();
356 for(size_t i = 0; i < axis_data.index_count(); i++) {
357 const JSON::node& bn = axis_data.index(i);
358 axis_info a;
359 a.id = bn["id"].as_uint();
360 a.name = bn.field_exists("name") ? bn["name"].as_string8() : "";
361 a.num = i;
362 a.minus = bn["minus"].as_int();
363 a.zero = bn["zero"].as_int();
364 a.plus = bn["plus"].as_int();
365 a.neutral = bn["neutral"].as_int();
366 a.pressure = bn["pressure"].as_bool();
367 a.threshold = bn["threshold"].as_double();
368 a.disabled = bn.field_exists("disabled") ? bn["disabled"].as_bool() : false;
369 a.online = false;
370 a.state = 0;
371 a.rstate = 0;
372 _axes[a.id] = a;
373 notify_queue.push_back(std::make_pair(a.num, 0));
375 } else
376 next_axis = 0;
378 if(state.field_exists("hats")) {
379 next_hat = hat_data.index_count();
380 for(size_t i = 0; i < hat_data.index_count(); i++) {
381 const JSON::node& bn = hat_data.index(i);
382 hat_info h;
383 h.id = bn["id"].as_uint();
384 h.name = bn.field_exists("name") ? bn["name"].as_string8() : "";
385 h.name2 = bn.field_exists("name2") ? bn["name2"].as_string8() : "";
386 h.id2 = bn.field_exists("id2") ? bn["id2"].as_uint() : 0;
387 h.num = i;
388 h.mindev = bn.field_exists("mindev") ? bn["mindev"].as_int() : 1;
389 h.online = false;
390 h.state = 0;
391 if(bn.field_exists("id2")) {
392 _axes_hat[h.id2] = _axes_hat[h.id] = new hat_info(h);
393 } else {
394 _hats[h.id] = h;
396 notify_queue.push_back(std::make_pair(h.num, 2));
398 } else
399 next_hat = 0;
401 for(auto i : notify_queue) {
402 try {
403 newitem_fn(jid, i.first, i.second);
404 } catch(...) {
409 void pad::calibrate_axis(unsigned num, int64_t minus, int64_t zero, int64_t plus, int64_t neutral,
410 double threshold, bool pressure, bool disabled)
412 mutex.lock();
413 for(auto& i : _axes) {
414 if(i.second.num != num)
415 continue;
416 axis_info& a = i.second;
418 double oldtolerance = a.threshold;
419 int oldmode = a.disabled ? -1 : a.pressure ? 0 : 1;
420 a.minus = minus;
421 a.zero = zero;
422 a.plus = plus;
423 a.neutral = neutral;
424 a.pressure = pressure;
425 a.threshold = threshold;
426 a.disabled = disabled;
427 int newmode = a.disabled ? -1 : a.pressure ? 0 : 1;
428 unsigned num = a.num;
429 double newtreshold = a.threshold;
430 if(oldmode >= 0 && newmode < 0) {
431 if(a.state != 0)
432 a.state = 0;
434 mutex.unlock();
435 if(oldmode >= 0 && newmode < 0)
436 try { axis_fn(jid, num, 0); } catch(...) {}
437 if(oldmode != newmode || oldtolerance != newtreshold)
438 try { amode_fn(jid, num, newmode, newtreshold); } catch(...) {}
439 return;
441 mutex.unlock();
442 return;
445 void pad::get_calibration(unsigned num, int64_t& minus, int64_t& zero, int64_t& plus, int64_t& neutral,
446 double& threshold, bool& pressure, bool& disabled)
448 umutex_class H(mutex);
449 for(auto& i : _axes) {
450 if(i.second.num != num)
451 continue;
452 axis_info& a = i.second;
453 minus = a.minus;
454 zero = a.zero;
455 plus = a.plus;
456 neutral = a.neutral;
457 pressure = a.pressure;
458 threshold = a.threshold;
459 disabled = a.disabled;
463 double pad::get_tolerance(unsigned num)
465 umutex_class H(mutex);
466 for(auto& i : _axes) {
467 if(i.second.num != num)
468 continue;
469 return i.second.threshold;
471 return 0.5;
474 int pad::get_mode(unsigned num)
476 umutex_class H(mutex);
477 for(auto& i : _axes) {
478 if(i.second.num != num)
479 continue;
480 return i.second.disabled ? -1 : i.second.pressure ? 0 : 1;
482 return -1;
485 void pad::axis_status(unsigned num, int64_t& raw, int16_t& pct)
487 umutex_class H(mutex);
488 for(auto& i : _axes) {
489 if(i.second.num != num || !i.second.online)
490 continue;
491 raw = i.second.rstate;
492 if(i.second.disabled)
493 pct = 0;
494 else
495 pct = (int)(i.second.state / 327.67);
496 return;
498 raw = 0;
499 pct = 0;;
502 int pad::button_status(unsigned num)
504 umutex_class H(mutex);
505 for(auto& i : _buttons) {
506 if(i.second.num != num || !i.second.online)
507 continue;
508 return i.second.state ? 1 : 0;
510 return -1;
513 int pad::hat_status(unsigned num)
515 umutex_class H(mutex);
516 for(auto& i : _hats) {
517 if(i.second.num != num || !i.second.online)
518 continue;
519 return i.second.state;
521 for(auto& i : _axes_hat) {
522 if(i.second->num != num || !i.second->online)
523 continue;
524 return i.second->state;
526 return -1;
529 JSON::node pad::save()
531 umutex_class H(mutex);
532 JSON::node r(JSON::object);
533 r.insert("name", JSON::string(_name));
535 JSON::node& buttons_json = r.insert("buttons", JSON::array());
536 for(size_t i = 0; i < next_button; i++) {
537 for(auto j : _buttons) {
538 if(j.second.num != i)
539 continue;
540 JSON::node& btndata = buttons_json.append(JSON::object());
541 btndata.insert("id", JSON::number(j.second.id));
542 btndata.insert("name", JSON::string(j.second.name));
546 JSON::node& axes_json = r.insert("axes", JSON::array());
547 for(size_t i = 0; i < next_axis; i++) {
548 for(auto j : _axes) {
549 if(j.second.num != i)
550 continue;
551 JSON::node& axdata = axes_json.append(JSON::object());
552 axdata.insert("id", JSON::number(j.second.id));
553 axdata.insert("name", JSON::string(j.second.name));
554 axdata.insert("minus", JSON::number(j.second.minus));
555 axdata.insert("zero", JSON::number(j.second.zero));
556 axdata.insert("plus", JSON::number(j.second.plus));
557 axdata.insert("neutral", JSON::number(j.second.neutral));
558 axdata.insert("pressure", JSON::boolean(j.second.pressure));
559 axdata.insert("threshold", JSON::number(j.second.threshold));
560 axdata.insert("disabled", JSON::boolean(j.second.disabled));
564 JSON::node& hats_json = r.insert("hats", JSON::array());
565 for(size_t i = 0; i < next_hat; i++) {
566 for(auto j : _hats) {
567 if(j.second.num != i)
568 continue;
569 JSON::node& axdata = hats_json.append(JSON::object());
570 axdata.insert("id", JSON::number(j.second.id));
571 axdata.insert("name", JSON::string(j.second.name));
573 for(auto j : _axes_hat) {
574 if(j.second->num != i)
575 continue;
576 JSON::node& axdata = hats_json.append(JSON::object());
577 axdata.insert("id", JSON::number(j.second->id));
578 axdata.insert("id2", JSON::number(j.second->id2));
579 axdata.insert("name", JSON::string(j.second->name));
580 axdata.insert("name2", JSON::string(j.second->name2));
581 axdata.insert("mindev", JSON::number(j.second->mindev));
582 break;
586 return r;
589 std::string pad::get_summary()
591 umutex_class h(mutex);
592 std::ostringstream x;
593 x << "joystick" << jid << ": " << _name << " " << (online_flag ? "" : " [Offline]") << std::endl;
594 for(auto i : _axes) {
595 x << "joystick" << jid << "axis" << i.second.num << "[" << i.second.id << ":" << i.second.name
596 << "]: " << i.second.minus << " <- " << i.second.zero << "(" << i.second.neutral << ") -> "
597 << i.second.plus << " " << "Threshold: " << i.second.threshold;
598 if(i.second.pressure)
599 x << " [Pressure]";
600 if(i.second.disabled)
601 x << " [Disabled]";
602 if(!i.second.online)
603 x << " [Offline]";
604 x << std::endl;
606 for(auto i : _buttons) {
607 x << "joystick" << jid << "button" << i.second.num << "[" << i.second.id << ":" << i.second.name
608 << "]: ";
609 if(!i.second.online)
610 x << " [Offline]";
611 x << std::endl;
613 for(auto i : _hats) {
614 x << "joystick" << jid << "hat" << i.second.num << "[" << i.second.id << ":" << i.second.name
615 << "]: ";
616 if(!i.second.online)
617 x << " [Offline]";
618 x << std::endl;
620 for(auto i : _axes_hat) {
621 if(i.first == i.second->id)
622 continue;
623 x << "joystick" << jid << "hat" << i.second->num << "[" << i.second->id << ":" << i.second->name
624 << "/" << i.second->id2 << ":" << i.second->name2 << "]: ";
625 if(!i.second->online)
626 x << " [Offline]";
627 x << std::endl;
629 return x.str();
632 set::set()
636 set::~set()
638 for(auto i : _gamepads)
639 delete i;
642 void set::load(const JSON::node& state)
644 bool locked = true;
645 mutex.lock();
646 for(auto i : _gamepads)
647 delete i;
648 _gamepads.clear();
650 for(size_t i = 0; i < state.index_count(); i++) {
651 const JSON::node& gpn = state.index(i);
652 pad* gp = NULL;
653 try {
654 gp = NULL;
655 gp = new pad("", _gamepads.size());
656 gp->set_axis_cb(axis_fn);
657 gp->set_button_cb(button_fn);
658 gp->set_hat_cb(hat_fn);
659 gp->set_axismode_cb(amode_fn);
660 gp->set_newitem_cb(newitem_fn);
661 _gamepads.push_back(gp);
662 locked = false;
663 mutex.unlock();
664 gp->load(gpn);
665 mutex.lock();
666 locked = true;
667 } catch(std::runtime_error& e) {
668 std::cerr << "Can't load gamepad #" << i << " configuration: " << e.what() << std::endl;
669 if(!locked)
670 mutex.lock();
671 delete gp;
674 mutex.unlock();
677 JSON::node set::save()
679 umutex_class h(mutex);
680 JSON::node n(JSON::array);
681 for(auto i : _gamepads)
682 n.append(i->save());
683 return n;
686 unsigned set::gamepads()
688 umutex_class h(mutex);
689 return _gamepads.size();
692 pad& set::operator[](unsigned gpnum)
694 umutex_class h(mutex);
695 if(gpnum >= _gamepads.size())
696 throw std::runtime_error("Invalid gamepad index");
697 return *_gamepads[gpnum];
700 unsigned set::add(const std::string& name)
702 umutex_class h(mutex);
703 for(size_t i = 0; i < _gamepads.size(); i++) {
704 if(!_gamepads[i]->online() && _gamepads[i]->name() == name) {
705 _gamepads[i]->set_online(true);
706 return i;
709 //No suitable found, create one.
710 pad* gp = NULL;
711 try {
712 gp = new pad(name, _gamepads.size());
713 gp->set_online(true);
714 gp->set_axis_cb(axis_fn);
715 gp->set_button_cb(button_fn);
716 gp->set_hat_cb(hat_fn);
717 gp->set_axismode_cb(amode_fn);
718 gp->set_newitem_cb(newitem_fn);
719 _gamepads.push_back(gp);
720 return _gamepads.size() - 1;
721 } catch(...) {
722 delete gp;
726 void set::set_axis_cb(std::function<void(unsigned jnum, unsigned num, int16_t val)> fn)
728 umutex_class h(mutex);
729 axis_fn = fn;
730 for(auto i : _gamepads)
731 i->set_axis_cb(axis_fn);
734 void set::set_button_cb(std::function<void(unsigned jnum, unsigned num, bool val)> fn)
736 umutex_class h(mutex);
737 button_fn = fn;
738 for(auto i : _gamepads)
739 i->set_button_cb(button_fn);
742 void set::set_hat_cb(std::function<void(unsigned jnum, unsigned num, unsigned val)> fn)
744 umutex_class h(mutex);
745 hat_fn = fn;
746 for(auto i : _gamepads)
747 i->set_hat_cb(hat_fn);
750 void set::set_axismode_cb(std::function<void(unsigned jnum, unsigned num, int mode, double tolerance)> fn)
752 umutex_class h(mutex);
753 amode_fn = fn;
754 for(auto i : _gamepads)
755 i->set_axismode_cb(amode_fn);
758 void set::set_newitem_cb(std::function<void(unsigned jnum, unsigned num, int type)> fn)
760 umutex_class h(mutex);
761 newitem_fn = fn;
762 for(auto i : _gamepads)
763 i->set_newitem_cb(newitem_fn);
766 std::string set::get_summary()
768 umutex_class h(mutex);
769 std::ostringstream x;
770 for(auto i : _gamepads)
771 x << i->get_summary();
772 return x.str();