1 | /**************************************************************************/ |
2 | /* joypad_linux.cpp */ |
3 | /**************************************************************************/ |
4 | /* This file is part of: */ |
5 | /* GODOT ENGINE */ |
6 | /* https://godotengine.org */ |
7 | /**************************************************************************/ |
8 | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ |
9 | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ |
10 | /* */ |
11 | /* Permission is hereby granted, free of charge, to any person obtaining */ |
12 | /* a copy of this software and associated documentation files (the */ |
13 | /* "Software"), to deal in the Software without restriction, including */ |
14 | /* without limitation the rights to use, copy, modify, merge, publish, */ |
15 | /* distribute, sublicense, and/or sell copies of the Software, and to */ |
16 | /* permit persons to whom the Software is furnished to do so, subject to */ |
17 | /* the following conditions: */ |
18 | /* */ |
19 | /* The above copyright notice and this permission notice shall be */ |
20 | /* included in all copies or substantial portions of the Software. */ |
21 | /* */ |
22 | /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ |
23 | /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ |
24 | /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ |
25 | /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ |
26 | /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ |
27 | /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ |
28 | /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ |
29 | /**************************************************************************/ |
30 | |
31 | #ifdef JOYDEV_ENABLED |
32 | |
33 | #include "joypad_linux.h" |
34 | |
35 | #include "core/os/os.h" |
36 | |
37 | #include <dirent.h> |
38 | #include <errno.h> |
39 | #include <fcntl.h> |
40 | #include <linux/input.h> |
41 | #include <unistd.h> |
42 | |
43 | #ifdef UDEV_ENABLED |
44 | #ifdef SOWRAP_ENABLED |
45 | #include "libudev-so_wrap.h" |
46 | #else |
47 | #include <libudev.h> |
48 | #endif |
49 | #endif |
50 | |
51 | #define LONG_BITS (sizeof(long) * 8) |
52 | #define test_bit(nr, addr) (((1UL << ((nr) % LONG_BITS)) & ((addr)[(nr) / LONG_BITS])) != 0) |
53 | #define NBITS(x) ((((x)-1) / LONG_BITS) + 1) |
54 | |
55 | #ifdef UDEV_ENABLED |
56 | static const char *ignore_str = "/dev/input/js" ; |
57 | #endif |
58 | |
59 | // On Linux with Steam Input Xbox 360 devices have an index appended to their device name, this index is |
60 | // the Steam Input gamepad index |
61 | #define VALVE_GAMEPAD_NAME_PREFIX "Microsoft X-Box 360 pad " |
62 | // IDs used by Steam Input virtual controllers. |
63 | // See https://partner.steamgames.com/doc/features/steam_controller/steam_input_gamepad_emulation_bestpractices |
64 | #define VALVE_GAMEPAD_VID 0x28DE |
65 | #define VALVE_GAMEPAD_PID 0x11FF |
66 | |
67 | JoypadLinux::Joypad::~Joypad() { |
68 | for (int i = 0; i < MAX_ABS; i++) { |
69 | if (abs_info[i]) { |
70 | memdelete(abs_info[i]); |
71 | } |
72 | } |
73 | } |
74 | |
75 | void JoypadLinux::Joypad::reset() { |
76 | dpad = 0; |
77 | fd = -1; |
78 | for (int i = 0; i < MAX_ABS; i++) { |
79 | abs_map[i] = -1; |
80 | curr_axis[i] = 0; |
81 | } |
82 | events.clear(); |
83 | } |
84 | |
85 | JoypadLinux::JoypadLinux(Input *in) { |
86 | #ifdef UDEV_ENABLED |
87 | if (OS::get_singleton()->is_sandboxed()) { |
88 | // Linux binaries in sandboxes / containers need special handling because |
89 | // libudev doesn't work there. So we need to fallback to manual parsing |
90 | // of /dev/input in such case. |
91 | use_udev = false; |
92 | print_verbose("JoypadLinux: udev enabled, but detected incompatible sandboxed mode. Falling back to /dev/input to detect joypads." ); |
93 | } |
94 | #ifdef SOWRAP_ENABLED |
95 | else { |
96 | #ifdef DEBUG_ENABLED |
97 | int dylibloader_verbose = 1; |
98 | #else |
99 | int dylibloader_verbose = 0; |
100 | #endif |
101 | use_udev = initialize_libudev(dylibloader_verbose) == 0; |
102 | if (use_udev) { |
103 | if (!udev_new || !udev_unref || !udev_enumerate_new || !udev_enumerate_add_match_subsystem || !udev_enumerate_scan_devices || !udev_enumerate_get_list_entry || !udev_list_entry_get_next || !udev_list_entry_get_name || !udev_device_new_from_syspath || !udev_device_get_devnode || !udev_device_get_action || !udev_device_unref || !udev_enumerate_unref || !udev_monitor_new_from_netlink || !udev_monitor_filter_add_match_subsystem_devtype || !udev_monitor_enable_receiving || !udev_monitor_get_fd || !udev_monitor_receive_device || !udev_monitor_unref) { |
104 | // There's no API to check version, check if functions are available instead. |
105 | use_udev = false; |
106 | print_verbose("JoypadLinux: Unsupported udev library version!" ); |
107 | } else { |
108 | print_verbose("JoypadLinux: udev enabled and loaded successfully." ); |
109 | } |
110 | } else { |
111 | print_verbose("JoypadLinux: udev enabled, but couldn't be loaded. Falling back to /dev/input to detect joypads." ); |
112 | } |
113 | } |
114 | #endif // SOWRAP_ENABLED |
115 | #else |
116 | print_verbose("JoypadLinux: udev disabled, parsing /dev/input to detect joypads." ); |
117 | #endif // UDEV_ENABLED |
118 | |
119 | input = in; |
120 | monitor_joypads_thread.start(monitor_joypads_thread_func, this); |
121 | joypad_events_thread.start(joypad_events_thread_func, this); |
122 | } |
123 | |
124 | JoypadLinux::~JoypadLinux() { |
125 | monitor_joypads_exit.set(); |
126 | joypad_events_exit.set(); |
127 | monitor_joypads_thread.wait_to_finish(); |
128 | joypad_events_thread.wait_to_finish(); |
129 | close_joypads(); |
130 | } |
131 | |
132 | void JoypadLinux::monitor_joypads_thread_func(void *p_user) { |
133 | if (p_user) { |
134 | JoypadLinux *joy = static_cast<JoypadLinux *>(p_user); |
135 | joy->monitor_joypads_thread_run(); |
136 | } |
137 | } |
138 | |
139 | void JoypadLinux::monitor_joypads_thread_run() { |
140 | #ifdef UDEV_ENABLED |
141 | if (use_udev) { |
142 | udev *_udev = udev_new(); |
143 | if (!_udev) { |
144 | use_udev = false; |
145 | ERR_PRINT("Failed getting an udev context, falling back to parsing /dev/input." ); |
146 | monitor_joypads(); |
147 | } else { |
148 | enumerate_joypads(_udev); |
149 | monitor_joypads(_udev); |
150 | udev_unref(_udev); |
151 | } |
152 | } else { |
153 | monitor_joypads(); |
154 | } |
155 | #else |
156 | monitor_joypads(); |
157 | #endif |
158 | } |
159 | |
160 | #ifdef UDEV_ENABLED |
161 | void JoypadLinux::enumerate_joypads(udev *p_udev) { |
162 | udev_enumerate *enumerate; |
163 | udev_list_entry *devices, *dev_list_entry; |
164 | udev_device *dev; |
165 | |
166 | enumerate = udev_enumerate_new(p_udev); |
167 | udev_enumerate_add_match_subsystem(enumerate, "input" ); |
168 | |
169 | udev_enumerate_scan_devices(enumerate); |
170 | devices = udev_enumerate_get_list_entry(enumerate); |
171 | udev_list_entry_foreach(dev_list_entry, devices) { |
172 | const char *path = udev_list_entry_get_name(dev_list_entry); |
173 | dev = udev_device_new_from_syspath(p_udev, path); |
174 | const char *devnode = udev_device_get_devnode(dev); |
175 | |
176 | if (devnode) { |
177 | String devnode_str = devnode; |
178 | if (devnode_str.find(ignore_str) == -1) { |
179 | open_joypad(devnode); |
180 | } |
181 | } |
182 | udev_device_unref(dev); |
183 | } |
184 | udev_enumerate_unref(enumerate); |
185 | } |
186 | |
187 | void JoypadLinux::monitor_joypads(udev *p_udev) { |
188 | udev_device *dev = nullptr; |
189 | udev_monitor *mon = udev_monitor_new_from_netlink(p_udev, "udev" ); |
190 | udev_monitor_filter_add_match_subsystem_devtype(mon, "input" , nullptr); |
191 | udev_monitor_enable_receiving(mon); |
192 | int fd = udev_monitor_get_fd(mon); |
193 | |
194 | while (!monitor_joypads_exit.is_set()) { |
195 | fd_set fds; |
196 | struct timeval tv; |
197 | int ret; |
198 | |
199 | FD_ZERO(&fds); |
200 | FD_SET(fd, &fds); |
201 | tv.tv_sec = 0; |
202 | tv.tv_usec = 0; |
203 | |
204 | ret = select(fd + 1, &fds, nullptr, nullptr, &tv); |
205 | |
206 | /* Check if our file descriptor has received data. */ |
207 | if (ret > 0 && FD_ISSET(fd, &fds)) { |
208 | /* Make the call to receive the device. |
209 | select() ensured that this will not block. */ |
210 | dev = udev_monitor_receive_device(mon); |
211 | |
212 | if (dev && udev_device_get_devnode(dev) != nullptr) { |
213 | String action = udev_device_get_action(dev); |
214 | const char *devnode = udev_device_get_devnode(dev); |
215 | if (devnode) { |
216 | String devnode_str = devnode; |
217 | if (devnode_str.find(ignore_str) == -1) { |
218 | if (action == "add" ) { |
219 | open_joypad(devnode); |
220 | } else if (String(action) == "remove" ) { |
221 | close_joypad(devnode); |
222 | } |
223 | } |
224 | } |
225 | udev_device_unref(dev); |
226 | } |
227 | } |
228 | usleep(50000); |
229 | } |
230 | udev_monitor_unref(mon); |
231 | } |
232 | #endif |
233 | |
234 | void JoypadLinux::monitor_joypads() { |
235 | while (!monitor_joypads_exit.is_set()) { |
236 | DIR *input_directory; |
237 | input_directory = opendir("/dev/input" ); |
238 | if (input_directory) { |
239 | struct dirent *current; |
240 | char fname[64]; |
241 | |
242 | while ((current = readdir(input_directory)) != nullptr) { |
243 | if (strncmp(current->d_name, "event" , 5) != 0) { |
244 | continue; |
245 | } |
246 | sprintf(fname, "/dev/input/%.*s" , 16, current->d_name); |
247 | if (attached_devices.find(fname) == -1) { |
248 | open_joypad(fname); |
249 | } |
250 | } |
251 | } |
252 | closedir(input_directory); |
253 | usleep(1000000); // 1s |
254 | } |
255 | } |
256 | |
257 | void JoypadLinux::close_joypads() { |
258 | for (int i = 0; i < JOYPADS_MAX; i++) { |
259 | MutexLock lock(joypads_mutex[i]); |
260 | Joypad &joypad = joypads[i]; |
261 | close_joypad(joypad, i); |
262 | } |
263 | } |
264 | |
265 | void JoypadLinux::close_joypad(const char *p_devpath) { |
266 | for (int i = 0; i < JOYPADS_MAX; i++) { |
267 | MutexLock lock(joypads_mutex[i]); |
268 | Joypad &joypad = joypads[i]; |
269 | if (joypads[i].devpath == p_devpath) { |
270 | close_joypad(joypad, i); |
271 | } |
272 | } |
273 | } |
274 | |
275 | void JoypadLinux::close_joypad(Joypad &p_joypad, int p_id) { |
276 | if (p_joypad.fd != -1) { |
277 | close(p_joypad.fd); |
278 | p_joypad.fd = -1; |
279 | attached_devices.erase(p_joypad.devpath); |
280 | input->joy_connection_changed(p_id, false, "" ); |
281 | } |
282 | p_joypad.events.clear(); |
283 | } |
284 | |
285 | static String _hex_str(uint8_t p_byte) { |
286 | static const char *dict = "0123456789abcdef" ; |
287 | char ret[3]; |
288 | ret[2] = 0; |
289 | |
290 | ret[0] = dict[p_byte >> 4]; |
291 | ret[1] = dict[p_byte & 0xF]; |
292 | |
293 | return ret; |
294 | } |
295 | |
296 | void JoypadLinux::setup_joypad_properties(Joypad &p_joypad) { |
297 | unsigned long keybit[NBITS(KEY_MAX)] = { 0 }; |
298 | unsigned long absbit[NBITS(ABS_MAX)] = { 0 }; |
299 | |
300 | int num_buttons = 0; |
301 | int num_axes = 0; |
302 | |
303 | if ((ioctl(p_joypad.fd, EVIOCGBIT(EV_KEY, sizeof(keybit)), keybit) < 0) || |
304 | (ioctl(p_joypad.fd, EVIOCGBIT(EV_ABS, sizeof(absbit)), absbit) < 0)) { |
305 | return; |
306 | } |
307 | for (int i = BTN_JOYSTICK; i < KEY_MAX; ++i) { |
308 | if (test_bit(i, keybit)) { |
309 | p_joypad.key_map[i] = num_buttons++; |
310 | } |
311 | } |
312 | for (int i = BTN_MISC; i < BTN_JOYSTICK; ++i) { |
313 | if (test_bit(i, keybit)) { |
314 | p_joypad.key_map[i] = num_buttons++; |
315 | } |
316 | } |
317 | for (int i = 0; i < ABS_MISC; ++i) { |
318 | /* Skip hats */ |
319 | if (i == ABS_HAT0X) { |
320 | i = ABS_HAT3Y; |
321 | continue; |
322 | } |
323 | if (test_bit(i, absbit)) { |
324 | p_joypad.abs_map[i] = num_axes++; |
325 | p_joypad.abs_info[i] = memnew(input_absinfo); |
326 | if (ioctl(p_joypad.fd, EVIOCGABS(i), p_joypad.abs_info[i]) < 0) { |
327 | memdelete(p_joypad.abs_info[i]); |
328 | p_joypad.abs_info[i] = nullptr; |
329 | } |
330 | } |
331 | } |
332 | |
333 | p_joypad.force_feedback = false; |
334 | p_joypad.ff_effect_timestamp = 0; |
335 | unsigned long ffbit[NBITS(FF_CNT)]; |
336 | if (ioctl(p_joypad.fd, EVIOCGBIT(EV_FF, sizeof(ffbit)), ffbit) != -1) { |
337 | if (test_bit(FF_RUMBLE, ffbit)) { |
338 | p_joypad.force_feedback = true; |
339 | } |
340 | } |
341 | } |
342 | |
343 | void JoypadLinux::open_joypad(const char *p_path) { |
344 | int joy_num = input->get_unused_joy_id(); |
345 | int fd = open(p_path, O_RDWR | O_NONBLOCK); |
346 | if (fd != -1 && joy_num != -1) { |
347 | unsigned long evbit[NBITS(EV_MAX)] = { 0 }; |
348 | unsigned long keybit[NBITS(KEY_MAX)] = { 0 }; |
349 | unsigned long absbit[NBITS(ABS_MAX)] = { 0 }; |
350 | |
351 | // add to attached devices so we don't try to open it again |
352 | attached_devices.push_back(String(p_path)); |
353 | |
354 | if ((ioctl(fd, EVIOCGBIT(0, sizeof(evbit)), evbit) < 0) || |
355 | (ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(keybit)), keybit) < 0) || |
356 | (ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(absbit)), absbit) < 0)) { |
357 | close(fd); |
358 | return; |
359 | } |
360 | |
361 | // Check if the device supports basic gamepad events |
362 | bool has_abs_left = (test_bit(ABS_X, absbit) && test_bit(ABS_Y, absbit)); |
363 | bool has_abs_right = (test_bit(ABS_RX, absbit) && test_bit(ABS_RY, absbit)); |
364 | if (!(test_bit(EV_KEY, evbit) && test_bit(EV_ABS, evbit) && (has_abs_left || has_abs_right))) { |
365 | close(fd); |
366 | return; |
367 | } |
368 | |
369 | char uid[128]; |
370 | char namebuf[128]; |
371 | String name = "" ; |
372 | input_id inpid; |
373 | if (ioctl(fd, EVIOCGNAME(sizeof(namebuf)), namebuf) >= 0) { |
374 | name = namebuf; |
375 | } |
376 | |
377 | if (ioctl(fd, EVIOCGID, &inpid) < 0) { |
378 | close(fd); |
379 | return; |
380 | } |
381 | |
382 | uint16_t vendor = BSWAP16(inpid.vendor); |
383 | uint16_t product = BSWAP16(inpid.product); |
384 | uint16_t version = BSWAP16(inpid.version); |
385 | |
386 | if (input->should_ignore_device(vendor, product)) { |
387 | // This can be true in cases where Steam is passing information into the game to ignore |
388 | // original gamepads when using virtual rebindings (See SteamInput). |
389 | return; |
390 | } |
391 | |
392 | MutexLock lock(joypads_mutex[joy_num]); |
393 | Joypad &joypad = joypads[joy_num]; |
394 | joypad.reset(); |
395 | joypad.fd = fd; |
396 | joypad.devpath = String(p_path); |
397 | setup_joypad_properties(joypad); |
398 | sprintf(uid, "%04x%04x" , BSWAP16(inpid.bustype), 0); |
399 | if (inpid.vendor && inpid.product && inpid.version) { |
400 | Dictionary joypad_info; |
401 | joypad_info["vendor_id" ] = inpid.vendor; |
402 | joypad_info["product_id" ] = inpid.product; |
403 | joypad_info["raw_name" ] = name; |
404 | |
405 | sprintf(uid + String(uid).length(), "%04x%04x%04x%04x%04x%04x" , vendor, 0, product, 0, version, 0); |
406 | |
407 | if (inpid.vendor == VALVE_GAMEPAD_VID && inpid.product == VALVE_GAMEPAD_PID) { |
408 | if (name.begins_with(VALVE_GAMEPAD_NAME_PREFIX)) { |
409 | String idx_str = name.substr(strlen(VALVE_GAMEPAD_NAME_PREFIX)); |
410 | if (idx_str.is_valid_int()) { |
411 | joypad_info["steam_input_index" ] = idx_str.to_int(); |
412 | } |
413 | } |
414 | } |
415 | |
416 | input->joy_connection_changed(joy_num, true, name, uid, joypad_info); |
417 | } else { |
418 | String uidname = uid; |
419 | int uidlen = MIN(name.length(), 11); |
420 | for (int i = 0; i < uidlen; i++) { |
421 | uidname = uidname + _hex_str(name[i]); |
422 | } |
423 | uidname += "00" ; |
424 | input->joy_connection_changed(joy_num, true, name, uidname); |
425 | } |
426 | } |
427 | } |
428 | |
429 | void JoypadLinux::joypad_vibration_start(Joypad &p_joypad, float p_weak_magnitude, float p_strong_magnitude, float p_duration, uint64_t p_timestamp) { |
430 | if (!p_joypad.force_feedback || p_joypad.fd == -1 || p_weak_magnitude < 0.f || p_weak_magnitude > 1.f || p_strong_magnitude < 0.f || p_strong_magnitude > 1.f) { |
431 | return; |
432 | } |
433 | if (p_joypad.ff_effect_id != -1) { |
434 | joypad_vibration_stop(p_joypad, p_timestamp); |
435 | } |
436 | |
437 | struct ff_effect effect; |
438 | effect.type = FF_RUMBLE; |
439 | effect.id = -1; |
440 | effect.u.rumble.weak_magnitude = floor(p_weak_magnitude * (float)0xffff); |
441 | effect.u.rumble.strong_magnitude = floor(p_strong_magnitude * (float)0xffff); |
442 | effect.replay.length = floor(p_duration * 1000); |
443 | effect.replay.delay = 0; |
444 | |
445 | if (ioctl(p_joypad.fd, EVIOCSFF, &effect) < 0) { |
446 | return; |
447 | } |
448 | |
449 | struct input_event play; |
450 | play.type = EV_FF; |
451 | play.code = effect.id; |
452 | play.value = 1; |
453 | if (write(p_joypad.fd, (const void *)&play, sizeof(play)) == -1) { |
454 | print_verbose("Couldn't write to Joypad device." ); |
455 | } |
456 | |
457 | p_joypad.ff_effect_id = effect.id; |
458 | p_joypad.ff_effect_timestamp = p_timestamp; |
459 | } |
460 | |
461 | void JoypadLinux::joypad_vibration_stop(Joypad &p_joypad, uint64_t p_timestamp) { |
462 | if (!p_joypad.force_feedback || p_joypad.fd == -1 || p_joypad.ff_effect_id == -1) { |
463 | return; |
464 | } |
465 | |
466 | if (ioctl(p_joypad.fd, EVIOCRMFF, p_joypad.ff_effect_id) < 0) { |
467 | return; |
468 | } |
469 | |
470 | p_joypad.ff_effect_id = -1; |
471 | p_joypad.ff_effect_timestamp = p_timestamp; |
472 | } |
473 | |
474 | float JoypadLinux::axis_correct(const input_absinfo *p_abs, int p_value) const { |
475 | int min = p_abs->minimum; |
476 | int max = p_abs->maximum; |
477 | // Convert to a value between -1.0f and 1.0f. |
478 | return 2.0f * (p_value - min) / (max - min) - 1.0f; |
479 | } |
480 | |
481 | void JoypadLinux::joypad_events_thread_func(void *p_user) { |
482 | if (p_user) { |
483 | JoypadLinux *joy = (JoypadLinux *)p_user; |
484 | joy->joypad_events_thread_run(); |
485 | } |
486 | } |
487 | |
488 | void JoypadLinux::joypad_events_thread_run() { |
489 | while (!joypad_events_exit.is_set()) { |
490 | bool no_events = true; |
491 | for (int i = 0; i < JOYPADS_MAX; i++) { |
492 | MutexLock lock(joypads_mutex[i]); |
493 | Joypad &joypad = joypads[i]; |
494 | if (joypad.fd == -1) { |
495 | continue; |
496 | } |
497 | input_event event; |
498 | while (read(joypad.fd, &event, sizeof(event)) > 0) { |
499 | no_events = false; |
500 | JoypadEvent joypad_event; |
501 | joypad_event.type = event.type; |
502 | joypad_event.code = event.code; |
503 | joypad_event.value = event.value; |
504 | joypad.events.push_back(joypad_event); |
505 | } |
506 | if (errno != EAGAIN) { |
507 | close_joypad(joypad, i); |
508 | } |
509 | } |
510 | if (no_events) { |
511 | usleep(10000); // 10ms |
512 | } |
513 | } |
514 | } |
515 | |
516 | void JoypadLinux::process_joypads() { |
517 | for (int i = 0; i < JOYPADS_MAX; i++) { |
518 | MutexLock lock(joypads_mutex[i]); |
519 | Joypad &joypad = joypads[i]; |
520 | if (joypad.fd == -1) { |
521 | continue; |
522 | } |
523 | for (uint32_t j = 0; j < joypad.events.size(); j++) { |
524 | const JoypadEvent &joypad_event = joypad.events[j]; |
525 | // joypad_event may be tainted and out of MAX_KEY range, which will cause |
526 | // joypad.key_map[joypad_event.code] to crash |
527 | if (joypad_event.code >= MAX_KEY) { |
528 | return; |
529 | } |
530 | |
531 | switch (joypad_event.type) { |
532 | case EV_KEY: |
533 | input->joy_button(i, (JoyButton)joypad.key_map[joypad_event.code], joypad_event.value); |
534 | break; |
535 | |
536 | case EV_ABS: |
537 | switch (joypad_event.code) { |
538 | case ABS_HAT0X: |
539 | if (joypad_event.value != 0) { |
540 | if (joypad_event.value < 0) { |
541 | joypad.dpad.set_flag(HatMask::LEFT); |
542 | joypad.dpad.clear_flag(HatMask::RIGHT); |
543 | } else { |
544 | joypad.dpad.set_flag(HatMask::RIGHT); |
545 | joypad.dpad.clear_flag(HatMask::LEFT); |
546 | } |
547 | } else { |
548 | joypad.dpad.clear_flag(HatMask::LEFT); |
549 | joypad.dpad.clear_flag(HatMask::RIGHT); |
550 | } |
551 | input->joy_hat(i, joypad.dpad); |
552 | break; |
553 | |
554 | case ABS_HAT0Y: |
555 | if (joypad_event.value != 0) { |
556 | if (joypad_event.value < 0) { |
557 | joypad.dpad.set_flag(HatMask::UP); |
558 | joypad.dpad.clear_flag(HatMask::DOWN); |
559 | } else { |
560 | joypad.dpad.set_flag(HatMask::DOWN); |
561 | joypad.dpad.clear_flag(HatMask::UP); |
562 | } |
563 | } else { |
564 | joypad.dpad.clear_flag(HatMask::UP); |
565 | joypad.dpad.clear_flag(HatMask::DOWN); |
566 | } |
567 | input->joy_hat(i, joypad.dpad); |
568 | break; |
569 | |
570 | default: |
571 | if (joypad_event.code >= MAX_ABS) { |
572 | return; |
573 | } |
574 | if (joypad.abs_map[joypad_event.code] != -1 && joypad.abs_info[joypad_event.code]) { |
575 | float value = axis_correct(joypad.abs_info[joypad_event.code], joypad_event.value); |
576 | joypad.curr_axis[joypad.abs_map[joypad_event.code]] = value; |
577 | } |
578 | break; |
579 | } |
580 | break; |
581 | } |
582 | } |
583 | joypad.events.clear(); |
584 | |
585 | for (int j = 0; j < MAX_ABS; j++) { |
586 | int index = joypad.abs_map[j]; |
587 | if (index != -1) { |
588 | input->joy_axis(i, (JoyAxis)index, joypad.curr_axis[index]); |
589 | } |
590 | } |
591 | |
592 | if (joypad.force_feedback) { |
593 | uint64_t timestamp = input->get_joy_vibration_timestamp(i); |
594 | if (timestamp > joypad.ff_effect_timestamp) { |
595 | Vector2 strength = input->get_joy_vibration_strength(i); |
596 | float duration = input->get_joy_vibration_duration(i); |
597 | if (strength.x == 0 && strength.y == 0) { |
598 | joypad_vibration_stop(joypad, timestamp); |
599 | } else { |
600 | joypad_vibration_start(joypad, strength.x, strength.y, duration, timestamp); |
601 | } |
602 | } |
603 | } |
604 | } |
605 | } |
606 | |
607 | #endif // JOYDEV_ENABLED |
608 | |