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
) {};
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
)
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 threads::alock
H(mlock
);
103 online_flag
= status
;
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
)
127 for(auto i
: buttons_off
)
128 button_fn(jid
, i
, 0);
129 for(auto i
: hats_off
)
133 unsigned pad::add_axis(uint64_t id
, int64_t _min
, int64_t _max
, bool pressure
, const std::string
& xname
)
137 threads::alock
H(mlock
);
138 if(_axes
.count(id
)) {
139 _axes
[id
].name
= xname
;
140 _axes
[id
].online
= true;
141 return _axes
[id
].num
;
149 a
.zero
= pressure
? _min
: (_min
+ _max
+ 1) / 2;
154 a
.pressure
= pressure
;
158 newitem_fn(jid
, a
.num
, 0);
162 unsigned pad::add_button(uint64_t id
, const std::string
& xname
)
166 threads::alock
H(mlock
);
167 if(_buttons
.count(id
)) {
168 _buttons
[id
].name
= xname
;
169 _buttons
[id
].online
= true;
170 return _buttons
[id
].num
;
173 b
.num
= next_button
++;
179 newitem_fn(jid
, b
.num
, 1);
183 unsigned pad::add_hat(uint64_t id
, const std::string
& xname
)
187 threads::alock
H(mlock
);
188 if(_hats
.count(id
)) {
189 _hats
[id
].name
= xname
;
190 _hats
[id
].online
= true;
191 return _hats
[id
].num
;
202 newitem_fn(jid
, h
.num
, 2);
206 unsigned pad::add_hat(uint64_t idx
, uint64_t idy
, int64_t mindev
, const std::string
& xnamex
,
207 const std::string
& xnamey
)
211 threads::alock
H(mlock
);
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
;
226 _axes_hat
[idy
] = _axes_hat
[idx
] = new hat_info(h
);
228 newitem_fn(jid
, h
.num
, 2);
232 void pad::report_axis(uint64_t id
, int64_t val
)
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
;
241 int16_t nstate
= i
.state
;
242 unsigned inum
= i
.num
;
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 signed 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
;
259 hat_fn(jid
, inum
, nstate
);
264 void pad::report_button(uint64_t id
, bool val
)
267 if(!_buttons
.count(id
)) {
271 button_info
& i
= _buttons
[id
];
272 bool ostate
= i
.state
;
274 int16_t nstate
= i
.state
;
275 unsigned inum
= i
.num
;
278 button_fn(jid
, inum
, nstate
);
281 void pad::report_hat(uint64_t id
, int angle
)
284 unsigned h
= angle_to_bitmask(angle
);
285 if(!_hats
.count(id
)) {
289 hat_info
& i
= _hats
[id
];
290 signed ostate
= i
.state
;
292 int16_t nstate
= i
.state
;
293 unsigned inum
= i
.num
;
296 hat_fn(jid
, inum
, nstate
);
299 std::set
<unsigned> pad::online_axes()
301 threads::alock
H(mlock
);
302 std::set
<unsigned> r
;
304 if(i
.second
.online
) r
.insert(i
.second
.num
);
308 std::set
<unsigned> pad::online_buttons()
310 threads::alock
H(mlock
);
311 std::set
<unsigned> r
;
312 for(auto i
: _buttons
)
313 if(i
.second
.online
) r
.insert(i
.second
.num
);
317 std::set
<unsigned> pad::online_hats()
319 threads::alock
H(mlock
);
320 std::set
<unsigned> r
;
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
);
328 void pad::load(const JSON::node
& state
)
330 std::list
<std::pair
<unsigned, int>> notify_queue
;
332 threads::alock
H(mlock
);
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
);
342 b
.id
= bn
["id"].as_uint();
343 b
.name
= bn
.field_exists("name") ? bn
["name"].as_string8() : "";
348 notify_queue
.push_back(std::make_pair(b
.num
, 1));
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
);
359 a
.id
= bn
["id"].as_uint();
360 a
.name
= bn
.field_exists("name") ? bn
["name"].as_string8() : "";
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;
373 notify_queue
.push_back(std::make_pair(a
.num
, 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
);
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;
388 h
.mindev
= bn
.field_exists("mindev") ? bn
["mindev"].as_int() : 1;
391 if(bn
.field_exists("id2")) {
392 _axes_hat
[h
.id2
] = _axes_hat
[h
.id
] = new hat_info(h
);
396 notify_queue
.push_back(std::make_pair(h
.num
, 2));
401 for(auto i
: notify_queue
) {
403 newitem_fn(jid
, i
.first
, i
.second
);
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
)
413 for(auto& i
: _axes
) {
414 if(i
.second
.num
!= num
)
416 axis_info
& a
= i
.second
;
418 double oldtolerance
= a
.threshold
;
419 int oldmode
= a
.disabled
? -1 : a
.pressure
? 0 : 1;
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) {
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(...) {}
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 threads::alock
H(mlock
);
449 for(auto& i
: _axes
) {
450 if(i
.second
.num
!= num
)
452 axis_info
& a
= i
.second
;
457 pressure
= a
.pressure
;
458 threshold
= a
.threshold
;
459 disabled
= a
.disabled
;
463 double pad::get_tolerance(unsigned num
)
465 threads::alock
H(mlock
);
466 for(auto& i
: _axes
) {
467 if(i
.second
.num
!= num
)
469 return i
.second
.threshold
;
474 int pad::get_mode(unsigned num
)
476 threads::alock
H(mlock
);
477 for(auto& i
: _axes
) {
478 if(i
.second
.num
!= num
)
480 return i
.second
.disabled
? -1 : i
.second
.pressure
? 0 : 1;
485 void pad::axis_status(unsigned num
, int64_t& raw
, int16_t& pct
)
487 threads::alock
H(mlock
);
488 for(auto& i
: _axes
) {
489 if(i
.second
.num
!= num
|| !i
.second
.online
)
491 raw
= i
.second
.rstate
;
492 if(i
.second
.disabled
)
495 pct
= (int)(i
.second
.state
/ 327.67);
502 int pad::button_status(unsigned num
)
504 threads::alock
H(mlock
);
505 for(auto& i
: _buttons
) {
506 if(i
.second
.num
!= num
|| !i
.second
.online
)
508 return i
.second
.state
? 1 : 0;
513 int pad::hat_status(unsigned num
)
515 threads::alock
H(mlock
);
516 for(auto& i
: _hats
) {
517 if(i
.second
.num
!= num
|| !i
.second
.online
)
519 return i
.second
.state
;
521 for(auto& i
: _axes_hat
) {
522 if(i
.second
->num
!= num
|| !i
.second
->online
)
524 return i
.second
->state
;
529 JSON::node
pad::save()
531 threads::alock
H(mlock
);
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
)
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
)
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
)
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
)
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
));
589 std::string
pad::get_summary()
591 threads::alock
h(mlock
);
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
)
600 if(i
.second
.disabled
)
606 for(auto i
: _buttons
) {
607 x
<< "joystick" << jid
<< "button" << i
.second
.num
<< "[" << i
.second
.id
<< ":" << i
.second
.name
613 for(auto i
: _hats
) {
614 x
<< "joystick" << jid
<< "hat" << i
.second
.num
<< "[" << i
.second
.id
<< ":" << i
.second
.name
620 for(auto i
: _axes_hat
) {
621 if(i
.first
== i
.second
->id
)
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
)
638 for(auto i
: _gamepads
)
642 void set::load(const JSON::node
& state
)
646 for(auto i
: _gamepads
)
650 for(size_t i
= 0; i
< state
.index_count(); i
++) {
651 const JSON::node
& gpn
= state
.index(i
);
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
);
667 } catch(std::runtime_error
& e
) {
668 std::cerr
<< "Can't load gamepad #" << i
<< " configuration: " << e
.what() << std::endl
;
677 JSON::node
set::save()
679 threads::alock
h(mlock
);
680 JSON::node
n(JSON::array
);
681 for(auto i
: _gamepads
)
686 unsigned set::gamepads()
688 threads::alock
h(mlock
);
689 return _gamepads
.size();
692 pad
& set::operator[](unsigned gpnum
)
694 threads::alock
h(mlock
);
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 threads::alock
h(mlock
);
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);
709 //No suitable found, create one.
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;
727 void set::offline_all()
729 for(size_t i
= 0; i
< _gamepads
.size(); i
++) {
730 if(_gamepads
[i
]->online()) {
731 _gamepads
[i
]->set_online(false);
736 void set::set_axis_cb(std::function
<void(unsigned jnum
, unsigned num
, int16_t val
)> fn
)
738 threads::alock
h(mlock
);
740 for(auto i
: _gamepads
)
741 i
->set_axis_cb(axis_fn
);
744 void set::set_button_cb(std::function
<void(unsigned jnum
, unsigned num
, bool val
)> fn
)
746 threads::alock
h(mlock
);
748 for(auto i
: _gamepads
)
749 i
->set_button_cb(button_fn
);
752 void set::set_hat_cb(std::function
<void(unsigned jnum
, unsigned num
, unsigned val
)> fn
)
754 threads::alock
h(mlock
);
756 for(auto i
: _gamepads
)
757 i
->set_hat_cb(hat_fn
);
760 void set::set_axismode_cb(std::function
<void(unsigned jnum
, unsigned num
, int mode
, double tolerance
)> fn
)
762 threads::alock
h(mlock
);
764 for(auto i
: _gamepads
)
765 i
->set_axismode_cb(amode_fn
);
768 void set::set_newitem_cb(std::function
<void(unsigned jnum
, unsigned num
, int type
)> fn
)
770 threads::alock
h(mlock
);
772 for(auto i
: _gamepads
)
773 i
->set_newitem_cb(newitem_fn
);
776 std::string
set::get_summary()
778 threads::alock
h(mlock
);
779 std::ostringstream x
;
780 for(auto i
: _gamepads
)
781 x
<< i
->get_summary();