Add D-Pad joystick support

By diedel

Resident (37)

diedel's picture

21-08-2014, 01:13

It's planned to add (D-Pad | POV | Hat) joystick support in openmsx?

I use a PS3 Controller using the SCP Driver under Windows 7 x64. The OS see it as an XBOX 360 controller and the D-Pad is seen as a POV and not as buttons neither axis. In the other hand the openmsx joystick1_config setting only supports "button" and "axis".

Login or register to post comments

By Manuel

Ascended (15441)

Manuel's picture

21-08-2014, 09:43

I don't have such controller, but BiFi said: "button4-button7 are resp. up right down left on ps3 pad"
So if you map the directions to those buttons you probably have what you want Smile

By diedel

Resident (37)

diedel's picture

21-08-2014, 11:55

I've already tried it but none of the 10 buttons of the pad corresponds to the D-pad buttons (neither axis). I think that the XBOX 360 Controller behaves the same.

I could try to build my custom openmsx version adding the SDL functions SDL_JoystickNumHats and SDL_JoystickGetHat. Would it be too complicated?

Thanks.

By mth

Champion (480)

mth's picture

21-08-2014, 13:07

Hats is a joystick feature that was only used for flight sticks in the past, but it seems that d-pads are increasingly mapped as hats. So that would be something that is useful to support.

If you have a bit of C++ experience, building and modifying openMSX shouldn't be too hard. Check out the Compilation Guide for instructions on building. The joystick code is in src/input/Joystick.cc.

By diedel

Resident (37)

diedel's picture

21-08-2014, 22:03

I've added the hat support and compiled. It works OK but sometimes is a bit unresponsive/laggy.
To discard the possibility of a problem due to my local build I've tested it again with the default joystick mapping (axis0 and axis1) and the control is perfect. Maybe SDL_JoystickGetHat() is more CPU heavy? Any idea?

--- Joystick_original.cc	2014-05-01 21:12:48.000000000 +0200
+++ Joystick.cc	2014-08-21 20:38:52.271352400 +0200
@@ -115,10 +115,14 @@
 			string_ref host = value.getListIndex(j).getString();
 			if (!host.starts_with("button") &&
 			    !host.starts_with("+axis") &&
-			    !host.starts_with("-axis")) {
+			    !host.starts_with("-axis") &&
+				!host.starts_with("L_hat") &&
+				!host.starts_with("R_hat") &&
+				!host.starts_with("U_hat") &&
+				!host.starts_with("D_hat")) {
 				throw CommandException(
 					"Invalid host joystick action: must be "
-					"one of 'button', '+axis', '-axis'");
+					"one of 'button', '+axis', '-axis', 'L_hat', 'R_hat', 'U_hat', 'D_hat'");
 			}
 		}
 	}
@@ -259,6 +263,26 @@
 				if (SDL_JoystickGetAxis(joystick, n) < -THRESHOLD) {
 					return true;
 				}
+			} else if (elem. starts_with("L_hat")) {
+				int n = stoi(elem.substr(5));
+				if (SDL_JoystickGetHat(joystick, n) & SDL_HAT_LEFT) {
+					return true;
+				}
+			} else if (elem.starts_with("R_hat")) {
+				int n = stoi(elem.substr(5));
+				if (SDL_JoystickGetHat(joystick, n) & SDL_HAT_RIGHT) {
+					return true;
+				}
+			} else if (elem.starts_with("U_hat")) {
+				int n = stoi(elem.substr(5));
+				if (SDL_JoystickGetHat(joystick, n) & SDL_HAT_UP) {
+					return true;
+				}
+			} else if (elem.starts_with("D_hat")) {
+				int n = stoi(elem.substr(5));
+				if (SDL_JoystickGetHat(joystick, n) & SDL_HAT_DOWN) {
+					return true;
+				}
 			}
 		}
 	} catch (MSXException&) {

By mth

Champion (480)

mth's picture

21-08-2014, 22:41

The joystick code changed a lot since I last worked on it; Wouter might know better exactly what to change. But I think you also have to handle hat events in InputEventGenerator::handle() in src/events/InputEventGenerator.cc.

By diedel

Resident (37)

diedel's picture

22-08-2014, 00:23

Yes, I've seen this code section. But it seems it's related to the control of the OSD elements.

By mth

Champion (480)

mth's picture

22-08-2014, 01:41

The triggerOsdControlEventsFrom* methods indeed exist to allow events to be used in the OSD, but EventDistributor::distributeEvent() which is called at the end of InputEventGenerator::handle() is also used for events that will ultimately reach the MSX, as far as I know. You could try mapping SDL hat events to openMSX axis events.

By wouter_

Champion (410)

wouter_'s picture

22-08-2014, 10:03

diedel wrote:

I've added the hat support and compiled. It works OK but sometimes is a bit unresponsive/laggy.

Hi diedel, nice work! Unfortunately it's only half of the full solution and that's why it's unresponsive. The code you added is triggered whenever the Joystick code receives an event (via Joystick::signalEvent()). So that means in your current patch the joystick-hat stuff is only handled when another joystick event (currently button or axis) is received.

So the next step is to let openMSX also send events for joystick-hat stuff. This is located in InputEventGenerator:Tongueoll() and InputEventGenerator::handle(). As an initial test you could send JoystickAxis events (the current joystick code doesn't care about the actual event type, see next paragraph). In the final solution we might want to introduce actual JoystickHat events.

Some background info: historically the joystick code did use the information in the actual received events. E.g. when we received a host-joystick-button-press/release event we would press/release the corresponding msx joystick button (so we didn't use e.g. SDL_JoystickGetButton()). Later we allowed a much more flexible mapping between host and msx joystick. E.g. we allow to map host buttons to msx-axis (to configure D-Pads on some joystick types). There can be multiple host buttons/axis mapped to the same msx buttons/axis. Or the other way around: one host button/axis can be mapped to multiple msx buttons/axis. With this more flexible N-to-M mapping, e.g. when you receive a host-button-release event it's no longer correct to release the corresponding msx joystick button (or axis), because another host button might still be pressed that's mapped to the same msx stuff. So as a quick solution, now when we receive any joystick event we simply
recalculate the full msx joystick state by querying SDL about the current host joystick state (so we ignore the information transmitted in the event). I'm sure alternative (more efficient?) solutions are possible, but for now this was good enough.

By diedel

Resident (37)

diedel's picture

22-08-2014, 15:51

Finally it works Smile, thanks mth and wouter_, very helpful explanation.

--- InputEventGenerator_original.cc	2014-05-01 21:12:48.000000000 +0200
+++ InputEventGenerator.cc	2014-08-22 14:40:25.174421000 +0200
@@ -317,6 +317,12 @@
 		triggerOsdControlEventsFromJoystickAxisMotion(
 			evt.jaxis.axis, evt.jaxis.value, event);
 		break;
+	case SDL_JOYHATMOTION:
+		event = make_shared(
+			evt.jhat.which, evt.jhat.hat, evt.jhat.value);
+		triggerOsdControlEventsFromJoystickAxisMotion(
+			evt.jhat.hat, evt.jhat.value, event);
+		break;
 
 	case SDL_ACTIVEEVENT:
 		event = make_shared(evt.active.gain != 0);

I know that the OSD events are not generated, but it's OK, I use the axes.

I just discovered the OSD keyboard feature of openmsx, now I can play Chuckie Egg and type my name at the Highscore table only with the controller LOL!

By diedel

Resident (37)

diedel's picture

22-08-2014, 18:37

I just realized that the code has not been pasted properly. I replaced the <, > symbols by its HTML entities.

--- InputEventGenerator_original.cc	2014-05-01 21:12:48.000000000 +0200
+++ InputEventGenerator.cc	2014-08-22 14:40:25.174421000 +0200
@@ -317,6 +317,12 @@
 		triggerOsdControlEventsFromJoystickAxisMotion(
 			evt.jaxis.axis, evt.jaxis.value, event);
 		break;
+	case SDL_JOYHATMOTION:
+		event = make_shared<JoystickAxisMotionEvent>(
+			evt.jhat.which, evt.jhat.hat, evt.jhat.value);
+		triggerOsdControlEventsFromJoystickAxisMotion(
+			evt.jhat.hat, evt.jhat.value, event);
+		break;
 
 	case SDL_ACTIVEEVENT:
 		event = make_shared<FocusEvent>(evt.active.gain != 0);