Browse Source

Read and store joypad events in a separate thread on x11 platform

Marcel Admiraal 3 years ago
parent
commit
89c758a4de
2 changed files with 189 additions and 161 deletions
  1. 160 150
      platform/x11/joypad_linux.cpp
  2. 29 11
      platform/x11/joypad_linux.h

+ 160 - 150
platform/x11/joypad_linux.cpp

@@ -74,6 +74,7 @@ void JoypadLinux::Joypad::reset() {
 		abs_map[i] = -1;
 		abs_map[i] = -1;
 		curr_axis[i] = 0;
 		curr_axis[i] = 0;
 	}
 	}
+	events.clear();
 }
 }
 
 
 JoypadLinux::JoypadLinux(InputDefault *in) {
 JoypadLinux::JoypadLinux(InputDefault *in) {
@@ -88,23 +89,26 @@ JoypadLinux::JoypadLinux(InputDefault *in) {
 	print_verbose("JoypadLinux: udev disabled, parsing /dev/input to detect joypads.");
 	print_verbose("JoypadLinux: udev disabled, parsing /dev/input to detect joypads.");
 #endif
 #endif
 	input = in;
 	input = in;
-	joy_thread.start(joy_thread_func, this);
+	monitor_joypads_thread.start(monitor_joypads_thread_func, this);
+	joypad_events_thread.start(joypad_events_thread_func, this);
 }
 }
 
 
 JoypadLinux::~JoypadLinux() {
 JoypadLinux::~JoypadLinux() {
-	exit_monitor.set();
-	joy_thread.wait_to_finish();
-	close_joypad();
+	monitor_joypads_exit.set();
+	joypad_events_exit.set();
+	monitor_joypads_thread.wait_to_finish();
+	joypad_events_thread.wait_to_finish();
+	close_joypads();
 }
 }
 
 
-void JoypadLinux::joy_thread_func(void *p_user) {
+void JoypadLinux::monitor_joypads_thread_func(void *p_user) {
 	if (p_user) {
 	if (p_user) {
 		JoypadLinux *joy = (JoypadLinux *)p_user;
 		JoypadLinux *joy = (JoypadLinux *)p_user;
-		joy->run_joypad_thread();
+		joy->monitor_joypads_thread_run();
 	}
 	}
 }
 }
 
 
-void JoypadLinux::run_joypad_thread() {
+void JoypadLinux::monitor_joypads_thread_run() {
 #ifdef UDEV_ENABLED
 #ifdef UDEV_ENABLED
 	if (use_udev) {
 	if (use_udev) {
 		udev *_udev = udev_new();
 		udev *_udev = udev_new();
@@ -144,9 +148,7 @@ void JoypadLinux::enumerate_joypads(udev *p_udev) {
 		if (devnode) {
 		if (devnode) {
 			String devnode_str = devnode;
 			String devnode_str = devnode;
 			if (devnode_str.find(ignore_str) == -1) {
 			if (devnode_str.find(ignore_str) == -1) {
-				joy_mutex.lock();
 				open_joypad(devnode);
 				open_joypad(devnode);
-				joy_mutex.unlock();
 			}
 			}
 		}
 		}
 		udev_device_unref(dev);
 		udev_device_unref(dev);
@@ -161,7 +163,7 @@ void JoypadLinux::monitor_joypads(udev *p_udev) {
 	udev_monitor_enable_receiving(mon);
 	udev_monitor_enable_receiving(mon);
 	int fd = udev_monitor_get_fd(mon);
 	int fd = udev_monitor_get_fd(mon);
 
 
-	while (!exit_monitor.is_set()) {
+	while (!monitor_joypads_exit.is_set()) {
 		fd_set fds;
 		fd_set fds;
 		struct timeval tv;
 		struct timeval tv;
 		int ret;
 		int ret;
@@ -180,7 +182,6 @@ void JoypadLinux::monitor_joypads(udev *p_udev) {
 			dev = udev_monitor_receive_device(mon);
 			dev = udev_monitor_receive_device(mon);
 
 
 			if (dev && udev_device_get_devnode(dev) != nullptr) {
 			if (dev && udev_device_get_devnode(dev) != nullptr) {
-				joy_mutex.lock();
 				String action = udev_device_get_action(dev);
 				String action = udev_device_get_action(dev);
 				const char *devnode = udev_device_get_devnode(dev);
 				const char *devnode = udev_device_get_devnode(dev);
 				if (devnode) {
 				if (devnode) {
@@ -189,13 +190,11 @@ void JoypadLinux::monitor_joypads(udev *p_udev) {
 						if (action == "add") {
 						if (action == "add") {
 							open_joypad(devnode);
 							open_joypad(devnode);
 						} else if (String(action) == "remove") {
 						} else if (String(action) == "remove") {
-							close_joypad(get_joy_from_path(devnode));
+							close_joypad(devnode);
 						}
 						}
 					}
 					}
 				}
 				}
-
 				udev_device_unref(dev);
 				udev_device_unref(dev);
-				joy_mutex.unlock();
 			}
 			}
 		}
 		}
 		usleep(50000);
 		usleep(50000);
@@ -205,9 +204,7 @@ void JoypadLinux::monitor_joypads(udev *p_udev) {
 #endif
 #endif
 
 
 void JoypadLinux::monitor_joypads() {
 void JoypadLinux::monitor_joypads() {
-	while (!exit_monitor.is_set()) {
-		joy_mutex.lock();
-
+	while (!monitor_joypads_exit.is_set()) {
 		DIR *input_directory;
 		DIR *input_directory;
 		input_directory = opendir("/dev/input");
 		input_directory = opendir("/dev/input");
 		if (input_directory) {
 		if (input_directory) {
@@ -225,38 +222,36 @@ void JoypadLinux::monitor_joypads() {
 			}
 			}
 		}
 		}
 		closedir(input_directory);
 		closedir(input_directory);
-		joy_mutex.unlock();
-		usleep(1000000); // 1s
 	}
 	}
+	usleep(1000000); // 1s
 }
 }
 
 
-int JoypadLinux::get_joy_from_path(String p_path) const {
+void JoypadLinux::close_joypads() {
 	for (int i = 0; i < JOYPADS_MAX; i++) {
 	for (int i = 0; i < JOYPADS_MAX; i++) {
-		if (joypads[i].devpath == p_path) {
-			return i;
-		}
+		MutexLock lock(joypads_mutex[i]);
+		Joypad &joypad = joypads[i];
+		close_joypad(joypad, i);
 	}
 	}
-	return -2;
 }
 }
 
 
-void JoypadLinux::close_joypad(int p_id) {
-	if (p_id == -1) {
-		for (int i = 0; i < JOYPADS_MAX; i++) {
-			close_joypad(i);
-		};
-		return;
-	} else if (p_id < 0) {
-		return;
+void JoypadLinux::close_joypad(const char *p_devpath) {
+	for (int i = 0; i < JOYPADS_MAX; i++) {
+		MutexLock lock(joypads_mutex[i]);
+		Joypad &joypad = joypads[i];
+		if (joypads[i].devpath == p_devpath) {
+			close_joypad(joypad, i);
+		}
 	}
 	}
+}
 
 
-	Joypad &joy = joypads[p_id];
-
-	if (joy.fd != -1) {
-		close(joy.fd);
-		joy.fd = -1;
-		attached_devices.remove(attached_devices.find(joy.devpath));
+void JoypadLinux::close_joypad(Joypad &p_joypad, int p_id) {
+	if (p_joypad.fd != -1) {
+		close(p_joypad.fd);
+		p_joypad.fd = -1;
+		attached_devices.erase(p_joypad.devpath);
 		input->joy_connection_changed(p_id, false, "");
 		input->joy_connection_changed(p_id, false, "");
 	};
 	};
+	p_joypad.events.clear();
 }
 }
 
 
 static String _hex_str(uint8_t p_byte) {
 static String _hex_str(uint8_t p_byte) {
@@ -270,27 +265,25 @@ static String _hex_str(uint8_t p_byte) {
 	return ret;
 	return ret;
 }
 }
 
 
-void JoypadLinux::setup_joypad_properties(int p_id) {
-	Joypad *joy = &joypads[p_id];
-
+void JoypadLinux::setup_joypad_properties(Joypad &p_joypad) {
 	unsigned long keybit[NBITS(KEY_MAX)] = { 0 };
 	unsigned long keybit[NBITS(KEY_MAX)] = { 0 };
 	unsigned long absbit[NBITS(ABS_MAX)] = { 0 };
 	unsigned long absbit[NBITS(ABS_MAX)] = { 0 };
 
 
 	int num_buttons = 0;
 	int num_buttons = 0;
 	int num_axes = 0;
 	int num_axes = 0;
 
 
-	if ((ioctl(joy->fd, EVIOCGBIT(EV_KEY, sizeof(keybit)), keybit) < 0) ||
-			(ioctl(joy->fd, EVIOCGBIT(EV_ABS, sizeof(absbit)), absbit) < 0)) {
+	if ((ioctl(p_joypad.fd, EVIOCGBIT(EV_KEY, sizeof(keybit)), keybit) < 0) ||
+			(ioctl(p_joypad.fd, EVIOCGBIT(EV_ABS, sizeof(absbit)), absbit) < 0)) {
 		return;
 		return;
 	}
 	}
 	for (int i = BTN_JOYSTICK; i < KEY_MAX; ++i) {
 	for (int i = BTN_JOYSTICK; i < KEY_MAX; ++i) {
 		if (test_bit(i, keybit)) {
 		if (test_bit(i, keybit)) {
-			joy->key_map[i] = num_buttons++;
+			p_joypad.key_map[i] = num_buttons++;
 		}
 		}
 	}
 	}
 	for (int i = BTN_MISC; i < BTN_JOYSTICK; ++i) {
 	for (int i = BTN_MISC; i < BTN_JOYSTICK; ++i) {
 		if (test_bit(i, keybit)) {
 		if (test_bit(i, keybit)) {
-			joy->key_map[i] = num_buttons++;
+			p_joypad.key_map[i] = num_buttons++;
 		}
 		}
 	}
 	}
 	for (int i = 0; i < ABS_MISC; ++i) {
 	for (int i = 0; i < ABS_MISC; ++i) {
@@ -300,21 +293,21 @@ void JoypadLinux::setup_joypad_properties(int p_id) {
 			continue;
 			continue;
 		}
 		}
 		if (test_bit(i, absbit)) {
 		if (test_bit(i, absbit)) {
-			joy->abs_map[i] = num_axes++;
-			joy->abs_info[i] = memnew(input_absinfo);
-			if (ioctl(joy->fd, EVIOCGABS(i), joy->abs_info[i]) < 0) {
-				memdelete(joy->abs_info[i]);
-				joy->abs_info[i] = nullptr;
+			p_joypad.abs_map[i] = num_axes++;
+			p_joypad.abs_info[i] = memnew(input_absinfo);
+			if (ioctl(p_joypad.fd, EVIOCGABS(i), p_joypad.abs_info[i]) < 0) {
+				memdelete(p_joypad.abs_info[i]);
+				p_joypad.abs_info[i] = nullptr;
 			}
 			}
 		}
 		}
 	}
 	}
 
 
-	joy->force_feedback = false;
-	joy->ff_effect_timestamp = 0;
+	p_joypad.force_feedback = false;
+	p_joypad.ff_effect_timestamp = 0;
 	unsigned long ffbit[NBITS(FF_CNT)];
 	unsigned long ffbit[NBITS(FF_CNT)];
-	if (ioctl(joy->fd, EVIOCGBIT(EV_FF, sizeof(ffbit)), ffbit) != -1) {
+	if (ioctl(p_joypad.fd, EVIOCGBIT(EV_FF, sizeof(ffbit)), ffbit) != -1) {
 		if (test_bit(FF_RUMBLE, ffbit)) {
 		if (test_bit(FF_RUMBLE, ffbit)) {
-			joy->force_feedback = true;
+			p_joypad.force_feedback = true;
 		}
 		}
 	}
 	}
 }
 }
@@ -364,12 +357,12 @@ void JoypadLinux::open_joypad(const char *p_path) {
 			return;
 			return;
 		}
 		}
 
 
-		joypads[joy_num].reset();
-
-		Joypad &joy = joypads[joy_num];
-		joy.fd = fd;
-		joy.devpath = String(p_path);
-		setup_joypad_properties(joy_num);
+		MutexLock lock(joypads_mutex[joy_num]);
+		Joypad &joypad = joypads[joy_num];
+		joypad.reset();
+		joypad.fd = fd;
+		joypad.devpath = String(p_path);
+		setup_joypad_properties(joypad);
 		sprintf(uid, "%04x%04x", BSWAP16(inpid.bustype), 0);
 		sprintf(uid, "%04x%04x", BSWAP16(inpid.bustype), 0);
 		if (inpid.vendor && inpid.product && inpid.version) {
 		if (inpid.vendor && inpid.product && inpid.version) {
 			uint16_t vendor = BSWAP16(inpid.vendor);
 			uint16_t vendor = BSWAP16(inpid.vendor);
@@ -390,13 +383,12 @@ void JoypadLinux::open_joypad(const char *p_path) {
 	}
 	}
 }
 }
 
 
-void JoypadLinux::joypad_vibration_start(int p_id, float p_weak_magnitude, float p_strong_magnitude, float p_duration, uint64_t p_timestamp) {
-	Joypad &joy = joypads[p_id];
-	if (!joy.force_feedback || joy.fd == -1 || p_weak_magnitude < 0.f || p_weak_magnitude > 1.f || p_strong_magnitude < 0.f || p_strong_magnitude > 1.f) {
+void JoypadLinux::joypad_vibration_start(Joypad &p_joypad, float p_weak_magnitude, float p_strong_magnitude, float p_duration, uint64_t p_timestamp) {
+	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) {
 		return;
 		return;
 	}
 	}
-	if (joy.ff_effect_id != -1) {
-		joypad_vibration_stop(p_id, p_timestamp);
+	if (p_joypad.ff_effect_id != -1) {
+		joypad_vibration_stop(p_joypad, p_timestamp);
 	}
 	}
 
 
 	struct ff_effect effect;
 	struct ff_effect effect;
@@ -407,7 +399,7 @@ void JoypadLinux::joypad_vibration_start(int p_id, float p_weak_magnitude, float
 	effect.replay.length = floor(p_duration * 1000);
 	effect.replay.length = floor(p_duration * 1000);
 	effect.replay.delay = 0;
 	effect.replay.delay = 0;
 
 
-	if (ioctl(joy.fd, EVIOCSFF, &effect) < 0) {
+	if (ioctl(p_joypad.fd, EVIOCSFF, &effect) < 0) {
 		return;
 		return;
 	}
 	}
 
 
@@ -415,26 +407,25 @@ void JoypadLinux::joypad_vibration_start(int p_id, float p_weak_magnitude, float
 	play.type = EV_FF;
 	play.type = EV_FF;
 	play.code = effect.id;
 	play.code = effect.id;
 	play.value = 1;
 	play.value = 1;
-	if (write(joy.fd, (const void *)&play, sizeof(play)) == -1) {
+	if (write(p_joypad.fd, (const void *)&play, sizeof(play)) == -1) {
 		print_verbose("Couldn't write to Joypad device.");
 		print_verbose("Couldn't write to Joypad device.");
 	}
 	}
 
 
-	joy.ff_effect_id = effect.id;
-	joy.ff_effect_timestamp = p_timestamp;
+	p_joypad.ff_effect_id = effect.id;
+	p_joypad.ff_effect_timestamp = p_timestamp;
 }
 }
 
 
-void JoypadLinux::joypad_vibration_stop(int p_id, uint64_t p_timestamp) {
-	Joypad &joy = joypads[p_id];
-	if (!joy.force_feedback || joy.fd == -1 || joy.ff_effect_id == -1) {
+void JoypadLinux::joypad_vibration_stop(Joypad &p_joypad, uint64_t p_timestamp) {
+	if (!p_joypad.force_feedback || p_joypad.fd == -1 || p_joypad.ff_effect_id == -1) {
 		return;
 		return;
 	}
 	}
 
 
-	if (ioctl(joy.fd, EVIOCRMFF, joy.ff_effect_id) < 0) {
+	if (ioctl(p_joypad.fd, EVIOCRMFF, p_joypad.ff_effect_id) < 0) {
 		return;
 		return;
 	}
 	}
 
 
-	joy.ff_effect_id = -1;
-	joy.ff_effect_timestamp = p_timestamp;
+	p_joypad.ff_effect_id = -1;
+	p_joypad.ff_effect_timestamp = p_timestamp;
 }
 }
 
 
 float JoypadLinux::axis_correct(const input_absinfo *p_abs, int p_value) const {
 float JoypadLinux::axis_correct(const input_absinfo *p_abs, int p_value) const {
@@ -444,104 +435,123 @@ float JoypadLinux::axis_correct(const input_absinfo *p_abs, int p_value) const {
 	return 2.0f * (p_value - min) / (max - min) - 1.0f;
 	return 2.0f * (p_value - min) / (max - min) - 1.0f;
 }
 }
 
 
-void JoypadLinux::process_joypads() {
-	if (joy_mutex.try_lock() != OK) {
-		return;
+void JoypadLinux::joypad_events_thread_func(void *p_user) {
+	if (p_user) {
+		JoypadLinux *joy = (JoypadLinux *)p_user;
+		joy->joypad_events_thread_run();
+	}
+}
+
+void JoypadLinux::joypad_events_thread_run() {
+	while (!joypad_events_exit.is_set()) {
+		bool no_events = true;
+		for (int i = 0; i < JOYPADS_MAX; i++) {
+			MutexLock lock(joypads_mutex[i]);
+			Joypad &joypad = joypads[i];
+			if (joypad.fd == -1) {
+				continue;
+			}
+			input_event event;
+			while (read(joypad.fd, &event, sizeof(event)) > 0) {
+				JoypadEvent joypad_event;
+				joypad_event.type = event.type;
+				joypad_event.code = event.code;
+				joypad_event.value = event.value;
+				joypad.events.push_back(joypad_event);
+			}
+			if (errno != EAGAIN) {
+				close_joypad(joypad, i);
+			}
+		}
+		if (no_events) {
+			usleep(10000); // 10ms
+		}
 	}
 	}
+}
+
+void JoypadLinux::process_joypads() {
 	for (int i = 0; i < JOYPADS_MAX; i++) {
 	for (int i = 0; i < JOYPADS_MAX; i++) {
-		if (joypads[i].fd == -1) {
+		MutexLock lock(joypads_mutex[i]);
+		Joypad &joypad = joypads[i];
+		if (joypad.fd == -1) {
 			continue;
 			continue;
 		}
 		}
+		for (uint32_t j = 0; j < joypad.events.size(); j++) {
+			const JoypadEvent &joypad_event = joypad.events[j];
+			// joypad_event may be tainted and out of MAX_KEY range, which will cause
+			// joypad.key_map[joypad_event.code] to crash
+			if (joypad_event.code >= MAX_KEY) {
+				return;
+			}
 
 
-		input_event events[32];
-		Joypad *joy = &joypads[i];
-
-		int len;
-
-		while ((len = read(joy->fd, events, (sizeof events))) > 0) {
-			len /= sizeof(events[0]);
-			for (int j = 0; j < len; j++) {
-				input_event &ev = events[j];
-
-				// ev may be tainted and out of MAX_KEY range, which will cause
-				// joy->key_map[ev.code] to crash
-				if (ev.code >= MAX_KEY) {
-					return;
-				}
-
-				switch (ev.type) {
-					case EV_KEY:
-						input->joy_button(i, joy->key_map[ev.code], ev.value);
-						break;
-
-					case EV_ABS:
-
-						switch (ev.code) {
-							case ABS_HAT0X:
-								if (ev.value != 0) {
-									if (ev.value < 0) {
-										joy->dpad = (joy->dpad | InputDefault::HAT_MASK_LEFT) & ~InputDefault::HAT_MASK_RIGHT;
-									} else {
-										joy->dpad = (joy->dpad | InputDefault::HAT_MASK_RIGHT) & ~InputDefault::HAT_MASK_LEFT;
-									}
+			switch (joypad_event.type) {
+				case EV_KEY:
+					input->joy_button(i, joypad.key_map[joypad_event.code], joypad_event.value);
+					break;
+
+				case EV_ABS:
+					switch (joypad_event.code) {
+						case ABS_HAT0X:
+							if (joypad_event.value != 0) {
+								if (joypad_event.value < 0) {
+									joypad.dpad = (joypad.dpad | InputDefault::HAT_MASK_LEFT) & ~InputDefault::HAT_MASK_RIGHT;
 								} else {
 								} else {
-									joy->dpad &= ~(InputDefault::HAT_MASK_LEFT | InputDefault::HAT_MASK_RIGHT);
+									joypad.dpad = (joypad.dpad | InputDefault::HAT_MASK_RIGHT) & ~InputDefault::HAT_MASK_LEFT;
 								}
 								}
-
-								input->joy_hat(i, joy->dpad);
-								break;
-
-							case ABS_HAT0Y:
-								if (ev.value != 0) {
-									if (ev.value < 0) {
-										joy->dpad = (joy->dpad | InputDefault::HAT_MASK_UP) & ~InputDefault::HAT_MASK_DOWN;
-									} else {
-										joy->dpad = (joy->dpad | InputDefault::HAT_MASK_DOWN) & ~InputDefault::HAT_MASK_UP;
-									}
+							} else {
+								joypad.dpad &= ~(InputDefault::HAT_MASK_LEFT | InputDefault::HAT_MASK_RIGHT);
+							}
+							input->joy_hat(i, joypad.dpad);
+							break;
+
+						case ABS_HAT0Y:
+							if (joypad_event.value != 0) {
+								if (joypad_event.value < 0) {
+									joypad.dpad = (joypad.dpad | InputDefault::HAT_MASK_UP) & ~InputDefault::HAT_MASK_DOWN;
 								} else {
 								} else {
-									joy->dpad &= ~(InputDefault::HAT_MASK_UP | InputDefault::HAT_MASK_DOWN);
-								}
-
-								input->joy_hat(i, joy->dpad);
-								break;
-
-							default:
-								if (ev.code >= MAX_ABS) {
-									return;
-								}
-								if (joy->abs_map[ev.code] != -1 && joy->abs_info[ev.code]) {
-									float value = axis_correct(joy->abs_info[ev.code], ev.value);
-									joy->curr_axis[joy->abs_map[ev.code]] = value;
+									joypad.dpad = (joypad.dpad | InputDefault::HAT_MASK_DOWN) & ~InputDefault::HAT_MASK_UP;
 								}
 								}
-								break;
-						}
-						break;
-				}
+							} else {
+								joypad.dpad &= ~(InputDefault::HAT_MASK_UP | InputDefault::HAT_MASK_DOWN);
+							}
+							input->joy_hat(i, joypad.dpad);
+							break;
+
+						default:
+							if (joypad_event.code >= MAX_ABS) {
+								return;
+							}
+							if (joypad.abs_map[joypad_event.code] != -1 && joypad.abs_info[joypad_event.code]) {
+								float value = axis_correct(joypad.abs_info[joypad_event.code], joypad_event.value);
+								joypad.curr_axis[joypad.abs_map[joypad_event.code]] = value;
+							}
+							break;
+					}
+					break;
 			}
 			}
 		}
 		}
+		joypad.events.clear();
+
 		for (int j = 0; j < MAX_ABS; j++) {
 		for (int j = 0; j < MAX_ABS; j++) {
-			int index = joy->abs_map[j];
+			int index = joypad.abs_map[j];
 			if (index != -1) {
 			if (index != -1) {
-				input->joy_axis(i, index, joy->curr_axis[index]);
+				input->joy_axis(i, index, joypad.curr_axis[index]);
 			}
 			}
 		}
 		}
-		if (len == 0 || (len < 0 && errno != EAGAIN)) {
-			close_joypad(i);
-		};
 
 
-		if (joy->force_feedback) {
+		if (joypad.force_feedback) {
 			uint64_t timestamp = input->get_joy_vibration_timestamp(i);
 			uint64_t timestamp = input->get_joy_vibration_timestamp(i);
-			if (timestamp > joy->ff_effect_timestamp) {
+			if (timestamp > joypad.ff_effect_timestamp) {
 				Vector2 strength = input->get_joy_vibration_strength(i);
 				Vector2 strength = input->get_joy_vibration_strength(i);
 				float duration = input->get_joy_vibration_duration(i);
 				float duration = input->get_joy_vibration_duration(i);
 				if (strength.x == 0 && strength.y == 0) {
 				if (strength.x == 0 && strength.y == 0) {
-					joypad_vibration_stop(i, timestamp);
+					joypad_vibration_stop(joypad, timestamp);
 				} else {
 				} else {
-					joypad_vibration_start(i, strength.x, strength.y, duration, timestamp);
+					joypad_vibration_start(joypad, strength.x, strength.y, duration, timestamp);
 				}
 				}
 			}
 			}
 		}
 		}
 	}
 	}
-	joy_mutex.unlock();
 }
 }
-#endif
+
+#endif // JOYDEV_ENABLED

+ 29 - 11
platform/x11/joypad_linux.h

@@ -52,6 +52,12 @@ private:
 		MAX_KEY = 767, // Hack because <linux/input.h> can't be included here
 		MAX_KEY = 767, // Hack because <linux/input.h> can't be included here
 	};
 	};
 
 
+	struct JoypadEvent {
+		uint16_t type;
+		uint16_t code;
+		int32_t value;
+	};
+
 	struct Joypad {
 	struct Joypad {
 		float curr_axis[MAX_ABS];
 		float curr_axis[MAX_ABS];
 		int key_map[MAX_KEY];
 		int key_map[MAX_KEY];
@@ -66,6 +72,8 @@ private:
 		int ff_effect_id;
 		int ff_effect_id;
 		uint64_t ff_effect_timestamp;
 		uint64_t ff_effect_timestamp;
 
 
+		LocalVector<JoypadEvent> events;
+
 		Joypad();
 		Joypad();
 		~Joypad();
 		~Joypad();
 		void reset();
 		void reset();
@@ -74,29 +82,39 @@ private:
 #ifdef UDEV_ENABLED
 #ifdef UDEV_ENABLED
 	bool use_udev;
 	bool use_udev;
 #endif
 #endif
-	SafeFlag exit_monitor;
-	Mutex joy_mutex;
-	Thread joy_thread;
 	InputDefault *input;
 	InputDefault *input;
+
+	SafeFlag monitor_joypads_exit;
+	SafeFlag joypad_events_exit;
+	Thread monitor_joypads_thread;
+	Thread joypad_events_thread;
+
 	Joypad joypads[JOYPADS_MAX];
 	Joypad joypads[JOYPADS_MAX];
+	Mutex joypads_mutex[JOYPADS_MAX];
+
 	Vector<String> attached_devices;
 	Vector<String> attached_devices;
 
 
-	static void joy_thread_func(void *p_user);
+	static void monitor_joypads_thread_func(void *p_user);
+	void monitor_joypads_thread_run();
+
+	void open_joypad(const char *p_path);
+	void setup_joypad_properties(Joypad &p_joypad);
 
 
-	int get_joy_from_path(String p_path) const;
+	void close_joypads();
+	void close_joypad(const char *p_devpath);
+	void close_joypad(Joypad &p_joypad, int p_id);
 
 
-	void setup_joypad_properties(int p_id);
-	void close_joypad(int p_id = -1);
 #ifdef UDEV_ENABLED
 #ifdef UDEV_ENABLED
 	void enumerate_joypads(struct udev *p_udev);
 	void enumerate_joypads(struct udev *p_udev);
 	void monitor_joypads(struct udev *p_udev);
 	void monitor_joypads(struct udev *p_udev);
 #endif
 #endif
 	void monitor_joypads();
 	void monitor_joypads();
-	void run_joypad_thread();
-	void open_joypad(const char *p_path);
 
 
-	void joypad_vibration_start(int p_id, float p_weak_magnitude, float p_strong_magnitude, float p_duration, uint64_t p_timestamp);
-	void joypad_vibration_stop(int p_id, uint64_t p_timestamp);
+	void joypad_vibration_start(Joypad &p_joypad, float p_weak_magnitude, float p_strong_magnitude, float p_duration, uint64_t p_timestamp);
+	void joypad_vibration_stop(Joypad &p_joypad, uint64_t p_timestamp);
+
+	static void joypad_events_thread_func(void *p_user);
+	void joypad_events_thread_run();
 
 
 	float axis_correct(const input_absinfo *p_abs, int p_value) const;
 	float axis_correct(const input_absinfo *p_abs, int p_value) const;
 };
 };