17 #ifdef PHAVE_LINUX_INPUT_H 23 #include <linux/joystick.h> 31 LinuxJoystickDevice(LinuxInputDeviceManager *manager,
size_t index) :
37 _dpad_left_button(-1),
45 <<
"Could not open joystick device /dev/input/js" << index
46 <<
": " << strerror(errno) <<
"\n";
54 ~LinuxJoystickDevice() {
64 bool LinuxJoystickDevice::
65 check_events()
const {
66 unsigned int avail = 0;
67 ioctl(_fd, FIONREAD, &avail);
76 void LinuxJoystickDevice::
78 if (_fd != -1 && process_events()) {
79 while (process_events()) {}
84 if (_manager !=
nullptr) {
85 _manager->add_device(
this);
95 bool LinuxJoystickDevice::
97 nassertr(_lock.debug_is_locked(),
false);
100 sprintf(path,
"/dev/input/js%zd", _index);
102 _fd = open(path, O_RDONLY | O_NONBLOCK);
105 _is_connected =
false;
113 ioctl(_fd, JSIOCGNAME(
sizeof(name)), name);
116 bool emulate_dpad =
true;
117 bool have_analog_triggers =
false;
120 uint8_t num_axes = 0, num_buttons = 0;
121 ioctl(_fd, JSIOCGAXES, &num_axes);
122 ioctl(_fd, JSIOCGBUTTONS, &num_buttons);
124 _buttons.resize(num_buttons);
125 _axes.resize(num_axes);
127 if (num_buttons > 0) {
128 uint16_t btnmap[512];
129 ioctl(_fd, JSIOCGBTNMAP, btnmap);
131 for (uint8_t i = 0; i < num_buttons; ++i) {
132 ButtonHandle handle = EvdevInputDevice::map_button(btnmap[i]);
133 if (handle == ButtonHandle::none()) {
134 if (device_cat.is_debug()) {
135 device_cat.debug() <<
"Unmapped /dev/input/js" << _index
136 <<
" button " << (int)i <<
": 0x" << std::hex << btnmap[i] <<
"\n";
138 }
else if (handle == GamepadButton::face_a()) {
139 _device_class = DeviceClass::gamepad;
140 }
else if (handle == GamepadButton::trigger()) {
141 _device_class = DeviceClass::flight_stick;
142 }
else if (handle == GamepadButton::dpad_left()) {
143 emulate_dpad =
false;
144 }
else if (handle == GamepadButton::ltrigger()) {
145 _ltrigger_button = i;
146 }
else if (handle == GamepadButton::rtrigger()) {
147 _rtrigger_button = i;
149 _buttons[i].handle = handle;
155 ioctl(_fd, JSIOCGAXMAP, axmap);
157 for (uint8_t i = 0; i < num_axes; ++i) {
158 Axis axis = Axis::none;
162 if (_device_class == DeviceClass::gamepad) {
163 axis = InputDevice::Axis::left_x;
164 }
else if (_device_class == DeviceClass::flight_stick) {
165 axis = InputDevice::Axis::roll;
167 axis = InputDevice::Axis::x;
172 if (_device_class == DeviceClass::gamepad) {
173 axis = InputDevice::Axis::left_y;
174 }
else if (_device_class == DeviceClass::flight_stick) {
175 axis = InputDevice::Axis::pitch;
177 axis = InputDevice::Axis::y;
182 if (_device_class == DeviceClass::gamepad) {
183 axis = Axis::left_trigger;
190 axis = Axis::right_x;
194 axis = Axis::right_y;
198 if (_device_class == DeviceClass::gamepad) {
199 axis = InputDevice::Axis::right_trigger;
201 axis = InputDevice::Axis::yaw;
206 axis = InputDevice::Axis::throttle;
210 axis = InputDevice::Axis::rudder;
214 axis = InputDevice::Axis::wheel;
218 axis = InputDevice::Axis::accelerator;
222 axis = InputDevice::Axis::brake;
229 _dpad_left_button = (int)_buttons.size();
230 if (_device_class == DeviceClass::gamepad) {
231 add_button(GamepadButton::dpad_left());
232 add_button(GamepadButton::dpad_right());
234 add_button(GamepadButton::hat_left());
235 add_button(GamepadButton::hat_right());
245 _dpad_up_button = (int)_buttons.size();
246 if (_device_class == DeviceClass::gamepad) {
247 add_button(GamepadButton::dpad_up());
248 add_button(GamepadButton::dpad_down());
250 add_button(GamepadButton::hat_up());
251 add_button(GamepadButton::hat_down());
258 if (_device_class == DeviceClass::gamepad) {
259 axis = InputDevice::Axis::right_trigger;
264 if (_device_class == DeviceClass::gamepad) {
265 axis = InputDevice::Axis::left_trigger;
270 if (device_cat.is_debug()) {
271 device_cat.debug() <<
"Unmapped /dev/input/js" << _index
272 <<
" axis " << (int)i <<
": 0x" << std::hex << (
int)axmap[i] <<
"\n";
277 _axes[i].axis = axis;
279 if (axis == Axis::left_trigger || axis == Axis::right_trigger) {
281 _axes[i]._scale = 1.0 / 65534.0;
282 _axes[i]._bias = 0.5;
283 have_analog_triggers =
true;
284 }
else if (axis == Axis::left_y || axis == Axis::right_y || axis == Axis::y) {
285 _axes[i]._scale = 1.0 / -32767.0;
286 _axes[i]._bias = 0.0;
288 _axes[i]._scale = 1.0 / 32767.0;
289 _axes[i]._bias = 0.0;
294 if (_ltrigger_button >= 0 && _rtrigger_button >= 0 && !have_analog_triggers) {
296 _ltrigger_axis = (int)_axes.size();
297 add_axis(Axis::left_trigger, 0, 1,
false);
298 add_axis(Axis::right_trigger, 0, 1,
false);
300 _ltrigger_button = -1;
301 _rtrigger_button = -1;
305 sprintf(path,
"/sys/class/input/js%zd/device/id/vendor", _index);
306 FILE *f = fopen(path,
"r");
308 if (fscanf(f,
"%hx", &_vendor_id) < 1) {
313 sprintf(path,
"/sys/class/input/js%zd/device/id/product", _index);
314 f = fopen(path,
"r");
316 if (fscanf(f,
"%hx", &_product_id) < 1) {
322 sprintf(path,
"/sys/class/input/js%zd/device/device/../product", _index);
323 f = fopen(path,
"r");
325 if (fgets(buffer,
sizeof(buffer), f) !=
nullptr) {
326 buffer[strcspn(buffer,
"\r\n")] = 0;
327 if (buffer[0] != 0) {
328 _name.assign(buffer);
333 sprintf(path,
"/sys/class/input/js%zd/device/device/../manufacturer", _index);
334 f = fopen(path,
"r");
336 if (fgets(buffer,
sizeof(buffer), f) !=
nullptr) {
337 buffer[strcspn(buffer,
"\r\n")] = 0;
338 _manufacturer.assign(buffer);
342 sprintf(path,
"/sys/class/input/js%zd/device/device/../serial", _index);
343 f = fopen(path,
"r");
345 if (fgets(buffer,
sizeof(buffer), f) !=
nullptr) {
346 buffer[strcspn(buffer,
"\r\n")] = 0;
347 _serial_number.assign(buffer);
353 while (process_events()) {};
360 if (strncmp(name,
"Xbox 360 Wireless Receiver", 26) == 0) {
361 for (
const auto &control : _axes) {
362 if (control.value != 0.0) {
363 _is_connected =
true;
367 _is_connected =
false;
369 _is_connected =
true;
380 bool LinuxJoystickDevice::
383 struct js_event events[8];
385 int n_read = read(_fd, events,
sizeof(events));
387 if (errno == EAGAIN || errno == EWOULDBLOCK) {
390 }
else if (errno == ENODEV) {
400 device_cat.error() <<
"read: " << strerror(errno) <<
"\n";
409 n_read /=
sizeof(
struct js_event);
411 for (
int i = 0; i < n_read; ++i) {
412 int index = events[i].number;
414 if (events[i].type & JS_EVENT_BUTTON) {
415 if (index == _ltrigger_button) {
416 axis_changed(_ltrigger_axis, events[i].value);
417 }
else if (index == _rtrigger_button) {
418 axis_changed(_ltrigger_axis + 1, events[i].value);
420 button_changed(index, (events[i].value != 0));
422 }
else if (events[i].type & JS_EVENT_AXIS) {
423 if (index == _dpad_x_axis) {
424 button_changed(_dpad_left_button, events[i].value < -1000);
425 button_changed(_dpad_left_button+1, events[i].value > 1000);
426 }
else if (index == _dpad_y_axis) {
427 button_changed(_dpad_up_button, events[i].value < -1000);
428 button_changed(_dpad_up_button+1, events[i].value > 1000);
431 axis_changed(index, events[i].value);
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
Similar to MutexHolder, but for a light mutex.
TypeHandle is the identifier used to differentiate C++ class types.