8 short angle_to_bitmask(int pov
)
11 if((pov
>= 0 && pov
<= 6000) || (pov
>= 30000 && pov
<= 36000))
13 if(pov
>= 3000 && pov
<= 15000)
15 if(pov
>= 12000 && pov
<= 24000)
17 if(pov
>= 21000 && pov
<= 33000)
22 int16_t map_value(int64_t val
, int64_t minus
, int64_t center
, int64_t plus
, int64_t neutral
, bool pressure
)
37 return 32767 * (v
- m
- n
) / (p
- m
- n
);
49 return -32768 * (c
- n
- v
) / (c
- n
- m
);
53 return 32767 * (v
- c
- n
) / (p
- c
- n
);
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
) {};
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
) {};
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
);
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
)
121 for(auto i
: buttons_off
)
122 button_fn(jid
, i
, 0);
123 for(auto i
: hats_off
)
127 unsigned pad::add_axis(uint64_t id
, int64_t _min
, int64_t _max
, bool pressure
, const std::string
& xname
)
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
;
143 a
.zero
= pressure
? _min
: (_min
+ _max
+ 1) / 2;
148 a
.pressure
= pressure
;
152 newitem_fn(jid
, a
.num
, 0);
156 unsigned pad::add_button(uint64_t id
, const std::string
& xname
)
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
;
167 b
.num
= next_button
++;
173 newitem_fn(jid
, b
.num
, 1);
177 unsigned pad::add_hat(uint64_t id
, const std::string
& xname
)
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
;
196 newitem_fn(jid
, h
.num
, 2);
200 unsigned pad::add_hat(uint64_t idx
, uint64_t idy
, int64_t mindev
, const std::string
& xnamex
,
201 const std::string
& xnamey
)
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
;
220 _axes_hat
[idy
] = _axes_hat
[idx
] = new hat_info(h
);
222 newitem_fn(jid
, h
.num
, 2);
226 void pad::report_axis(uint64_t id
, int64_t val
)
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
;
235 int16_t nstate
= i
.state
;
236 unsigned inum
= i
.num
;
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
;
253 hat_fn(jid
, inum
, nstate
);
258 void pad::report_button(uint64_t id
, bool val
)
261 if(!_buttons
.count(id
)) {
265 button_info
& i
= _buttons
[id
];
266 bool ostate
= i
.state
;
268 int16_t nstate
= i
.state
;
269 unsigned inum
= i
.num
;
272 button_fn(jid
, inum
, nstate
);
275 void pad::report_hat(uint64_t id
, int angle
)
278 unsigned h
= angle_to_bitmask(angle
);
279 if(!_hats
.count(id
)) {
283 hat_info
& i
= _hats
[id
];
284 unsigned ostate
= i
.state
;
286 int16_t nstate
= i
.state
;
287 unsigned inum
= i
.num
;
290 hat_fn(jid
, inum
, nstate
);
293 std::set
<unsigned> pad::online_axes()
295 umutex_class
H(mutex
);
296 std::set
<unsigned> r
;
298 if(i
.second
.online
) r
.insert(i
.second
.num
);
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
);
311 std::set
<unsigned> pad::online_hats()
313 umutex_class
H(mutex
);
314 std::set
<unsigned> r
;
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
);
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
);
336 b
.id
= bn
["id"].as_uint();
337 b
.name
= bn
.field_exists("name") ? bn
["name"].as_string8() : "";
342 notify_queue
.push_back(std::make_pair(b
.num
, 1));
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
);
353 a
.id
= bn
["id"].as_uint();
354 a
.name
= bn
.field_exists("name") ? bn
["name"].as_string8() : "";
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;
367 notify_queue
.push_back(std::make_pair(a
.num
, 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
);
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;
382 h
.mindev
= bn
.field_exists("mindev") ? bn
["mindev"].as_int() : 1;
385 if(bn
.field_exists("id2")) {
386 _axes_hat
[h
.id2
] = _axes_hat
[h
.id
] = new hat_info(h
);
390 notify_queue
.push_back(std::make_pair(h
.num
, 2));
395 for(auto i
: notify_queue
) {
397 newitem_fn(jid
, i
.first
, i
.second
);
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
)
407 for(auto& i
: _axes
) {
408 if(i
.second
.num
!= num
)
410 axis_info
& a
= i
.second
;
412 double oldtolerance
= a
.threshold
;
413 int oldmode
= a
.disabled
? -1 : a
.pressure
? 0 : 1;
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) {
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(...) {}
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
)
446 axis_info
& a
= i
.second
;
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
)
463 return i
.second
.threshold
;
468 int pad::get_mode(unsigned num
)
470 umutex_class
H(mutex
);
471 for(auto& i
: _axes
) {
472 if(i
.second
.num
!= num
)
474 return i
.second
.disabled
? -1 : i
.second
.pressure
? 0 : 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
)
485 raw
= i
.second
.rstate
;
486 if(i
.second
.disabled
)
489 pct
= (int)(i
.second
.state
/ 327.67);
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
)
502 return i
.second
.state
? 1 : 0;
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
)
513 return i
.second
.state
;
515 for(auto& i
: _axes_hat
) {
516 if(i
.second
->num
!= num
|| !i
.second
->online
)
518 return i
.second
->state
;
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
)
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
)
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
)
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
)
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
));
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
)
594 if(i
.second
.disabled
)
600 for(auto i
: _buttons
) {
601 x
<< "joystick" << jid
<< "button" << i
.second
.num
<< "[" << i
.second
.id
<< ":" << i
.second
.name
607 for(auto i
: _hats
) {
608 x
<< "joystick" << jid
<< "hat" << i
.second
.num
<< "[" << i
.second
.id
<< ":" << i
.second
.name
614 for(auto i
: _axes_hat
) {
615 if(i
.first
== i
.second
->id
)
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
)
632 for(auto i
: _gamepads
)
636 void set::load(const JSON::node
& state
)
640 for(auto i
: _gamepads
)
644 for(size_t i
= 0; i
< state
.index_count(); i
++) {
645 const JSON::node
& gpn
= state
.index(i
);
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
);
661 } catch(std::runtime_error
& e
) {
662 std::cerr
<< "Can't load gamepad #" << i
<< " configuration: " << e
.what() << std::endl
;
671 JSON::node
set::save()
673 umutex_class
h(mutex
);
674 JSON::node
n(JSON::array
);
675 for(auto i
: _gamepads
)
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);
703 //No suitable found, create one.
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;
720 void set::set_axis_cb(std::function
<void(unsigned jnum
, unsigned num
, int16_t val
)> fn
)
722 umutex_class
h(mutex
);
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
);
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
);
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
);
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
);
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();