9 short angle_to_bitmask(int pov
)
12 if((pov
>= 0 && pov
<= 6000) || (pov
>= 30000 && pov
<= 36000))
14 if(pov
>= 3000 && pov
<= 15000)
16 if(pov
>= 12000 && pov
<= 24000)
18 if(pov
>= 21000 && pov
<= 33000)
23 int16_t map_value(int64_t val
, int64_t minus
, int64_t center
, int64_t plus
, int64_t neutral
, bool pressure
)
38 return 32767 * (v
- m
- n
) / (p
- m
- n
);
50 return -32768 * (c
- n
- v
) / (c
- n
- m
);
54 return 32767 * (v
- c
- n
) / (p
- c
- n
);
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
) {};
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
) {};
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
)
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
);
104 online_flag
= status
;
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
)
128 for(auto i
: buttons_off
)
129 button_fn(jid
, i
, 0);
130 for(auto i
: hats_off
)
134 unsigned pad::add_axis(uint64_t id
, int64_t _min
, int64_t _max
, bool pressure
, const std::string
& xname
)
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
;
150 a
.zero
= pressure
? _min
: (_min
+ _max
+ 1) / 2;
155 a
.pressure
= pressure
;
159 newitem_fn(jid
, a
.num
, 0);
163 unsigned pad::add_button(uint64_t id
, const std::string
& xname
)
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
;
174 b
.num
= next_button
++;
180 newitem_fn(jid
, b
.num
, 1);
184 unsigned pad::add_hat(uint64_t id
, const std::string
& xname
)
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
;
203 newitem_fn(jid
, h
.num
, 2);
207 unsigned pad::add_hat(uint64_t idx
, uint64_t idy
, int64_t mindev
, const std::string
& xnamex
,
208 const std::string
& xnamey
)
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
;
227 _axes_hat
[idy
] = _axes_hat
[idx
] = new hat_info(h
);
229 newitem_fn(jid
, h
.num
, 2);
233 void pad::report_axis(uint64_t id
, int64_t val
)
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
;
242 int16_t nstate
= i
.state
;
243 unsigned inum
= i
.num
;
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
;
260 hat_fn(jid
, inum
, nstate
);
265 void pad::report_button(uint64_t id
, bool val
)
268 if(!_buttons
.count(id
)) {
272 button_info
& i
= _buttons
[id
];
273 bool ostate
= i
.state
;
275 int16_t nstate
= i
.state
;
276 unsigned inum
= i
.num
;
279 button_fn(jid
, inum
, nstate
);
282 void pad::report_hat(uint64_t id
, int angle
)
285 unsigned h
= angle_to_bitmask(angle
);
286 if(!_hats
.count(id
)) {
290 hat_info
& i
= _hats
[id
];
291 signed ostate
= i
.state
;
293 int16_t nstate
= i
.state
;
294 unsigned inum
= i
.num
;
297 hat_fn(jid
, inum
, nstate
);
300 std::set
<unsigned> pad::online_axes()
302 threads::alock
H(mlock
);
303 std::set
<unsigned> r
;
305 if(i
.second
.online
) r
.insert(i
.second
.num
);
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
);
318 std::set
<unsigned> pad::online_hats()
320 threads::alock
H(mlock
);
321 std::set
<unsigned> r
;
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
);
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
);
343 b
.id
= bn
["id"].as_uint();
344 b
.name
= bn
.field_exists("name") ? bn
["name"].as_string8() : "";
349 notify_queue
.push_back(std::make_pair(b
.num
, 1));
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
);
360 a
.id
= bn
["id"].as_uint();
361 a
.name
= bn
.field_exists("name") ? bn
["name"].as_string8() : "";
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;
374 notify_queue
.push_back(std::make_pair(a
.num
, 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
);
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;
389 h
.mindev
= bn
.field_exists("mindev") ? bn
["mindev"].as_int() : 1;
392 if(bn
.field_exists("id2")) {
393 _axes_hat
[h
.id2
] = _axes_hat
[h
.id
] = new hat_info(h
);
397 notify_queue
.push_back(std::make_pair(h
.num
, 2));
402 for(auto i
: notify_queue
) {
404 newitem_fn(jid
, i
.first
, i
.second
);
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
)
414 for(auto& i
: _axes
) {
415 if(i
.second
.num
!= num
)
417 axis_info
& a
= i
.second
;
419 double oldtolerance
= a
.threshold
;
420 int oldmode
= a
.disabled
? -1 : a
.pressure
? 0 : 1;
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) {
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(...) {}
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
)
453 axis_info
& a
= i
.second
;
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
)
470 return i
.second
.threshold
;
475 int pad::get_mode(unsigned num
)
477 threads::alock
H(mlock
);
478 for(auto& i
: _axes
) {
479 if(i
.second
.num
!= num
)
481 return i
.second
.disabled
? -1 : i
.second
.pressure
? 0 : 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
)
492 raw
= i
.second
.rstate
;
493 if(i
.second
.disabled
)
496 pct
= (int)(i
.second
.state
/ 327.67);
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
)
509 return i
.second
.state
? 1 : 0;
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
)
520 return i
.second
.state
;
522 for(auto& i
: _axes_hat
) {
523 if(i
.second
->num
!= num
|| !i
.second
->online
)
525 return i
.second
->state
;
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
)
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
)
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
)
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
)
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
));
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
)
601 if(i
.second
.disabled
)
607 for(auto i
: _buttons
) {
608 x
<< "joystick" << jid
<< "button" << i
.second
.num
<< "[" << i
.second
.id
<< ":" << i
.second
.name
614 for(auto i
: _hats
) {
615 x
<< "joystick" << jid
<< "hat" << i
.second
.num
<< "[" << i
.second
.id
<< ":" << i
.second
.name
621 for(auto i
: _axes_hat
) {
622 if(i
.first
== i
.second
->id
)
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
)
639 for(auto i
: _gamepads
)
643 void set::load(const JSON::node
& state
)
647 for(auto i
: _gamepads
)
651 for(size_t i
= 0; i
< state
.index_count(); i
++) {
652 const JSON::node
& gpn
= state
.index(i
);
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
);
668 } catch(std::runtime_error
& e
) {
669 std::cerr
<< "Can't load gamepad #" << i
<< " configuration: " << e
.what() << std::endl
;
678 JSON::node
set::save()
680 threads::alock
h(mlock
);
681 JSON::node
n(JSON::array
);
682 for(auto i
: _gamepads
)
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
);
717 //No suitable found, create one.
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;
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
);
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
);
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
);
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
);
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
);
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();