From 30109816fe3a0dae80795483ff28e9a50e6009a4 Mon Sep 17 00:00:00 2001
From: Cameron Cawley <ccawley2011@gmail.com>
Date: Sun, 16 Jun 2019 23:26:49 +0100
Subject: [PATCH] SDL: Initial implementation of joystick events

---
 backends/events/sdl/sdl-events.cpp | 101 +++++++++++++++++++++++++++++
 backends/events/sdl/sdl-events.h   |   4 ++
 common/events.h                    |  53 ++++++++++++++-
 engines/engine.h                   |  10 ++-
 4 files changed, 166 insertions(+), 2 deletions(-)

diff --git a/backends/events/sdl/sdl-events.cpp b/backends/events/sdl/sdl-events.cpp
index 29be7d4e746..494e1bf8cd9 100644
--- a/backends/events/sdl/sdl-events.cpp
+++ b/backends/events/sdl/sdl-events.cpp
@@ -30,6 +30,8 @@
 #include "common/config-manager.h"
 #include "common/textconsole.h"
 #include "common/fs.h"
+#include "engines/engine.h"
+#include "gui/gui-manager.h"
 
 // FIXME move joystick defines out and replace with confile file options
 // we should really allow users to map any key to a joystick button
@@ -902,7 +904,45 @@ void SdlEventSource::closeJoystick() {
 	}
 }
 
+bool SdlEventSource::shouldGenerateMouseEvents() {
+	// Engine doesn't support joystick -> emulate mouse events
+	if (g_engine && !g_engine->hasFeature(Engine::kSupportsJoystick)) {
+		return true;
+	}
+	if (g_gui.isActive()) {
+		return true;
+	}
+	return false;
+}
+
+int SdlEventSource::mapSDLJoystickButtonToOSystem(Uint8 sdlButton) {
+	Common::JoystickButton osystemButtons[] = {
+	    Common::JOYSTICK_BUTTON_A,
+	    Common::JOYSTICK_BUTTON_B,
+	    Common::JOYSTICK_BUTTON_X,
+	    Common::JOYSTICK_BUTTON_Y,
+	    Common::JOYSTICK_BUTTON_LEFT_SHOULDER,
+	    Common::JOYSTICK_BUTTON_RIGHT_SHOULDER,
+	    Common::JOYSTICK_BUTTON_BACK,
+	    Common::JOYSTICK_BUTTON_START,
+	    Common::JOYSTICK_BUTTON_LEFT_STICK,
+	    Common::JOYSTICK_BUTTON_RIGHT_STICK
+	};
+
+	if (sdlButton >= ARRAYSIZE(osystemButtons)) {
+		return -1;
+	}
+
+	return osystemButtons[sdlButton];
+}
+
 bool SdlEventSource::handleJoyButtonDown(SDL_Event &ev, Common::Event &event) {
+	if (!shouldGenerateMouseEvents()) {
+		event.type = Common::EVENT_JOYBUTTON_DOWN;
+		event.joystick.button = mapSDLJoystickButtonToOSystem(ev.jbutton.button);
+		return true;
+	}
+
 	if (ev.jbutton.button == JOY_BUT_LMOUSE) {
 		event.type = Common::EVENT_LBUTTONDOWN;
 		return processMouseEvent(event, _km.x / MULTIPLIER, _km.y / MULTIPLIER);
@@ -939,6 +979,12 @@ bool SdlEventSource::handleJoyButtonDown(SDL_Event &ev, Common::Event &event) {
 }
 
 bool SdlEventSource::handleJoyButtonUp(SDL_Event &ev, Common::Event &event) {
+	if (!shouldGenerateMouseEvents()) {
+		event.type = Common::EVENT_JOYBUTTON_UP;
+		event.joystick.button = mapSDLJoystickButtonToOSystem(ev.jbutton.button);
+		return true;
+	}
+
 	if (ev.jbutton.button == JOY_BUT_LMOUSE) {
 		event.type = Common::EVENT_LBUTTONUP;
 		return processMouseEvent(event, _km.x / MULTIPLIER, _km.y / MULTIPLIER);
@@ -975,6 +1021,16 @@ bool SdlEventSource::handleJoyButtonUp(SDL_Event &ev, Common::Event &event) {
 }
 
 bool SdlEventSource::handleJoyAxisMotion(SDL_Event &ev, Common::Event &event) {
+	// TODO: move handleAxisToMouseMotion to Common?
+#if 0
+	if (!shouldGenerateMouseEvents()) {
+		event.type = Common::EVENT_JOYAXIS_MOTION;
+		event.joystick.axis = ev.jaxis.axis;
+		event.joystick.position = ev.jaxis.value;
+		return true;
+	}
+#endif
+
 	if (ev.jaxis.axis == JOY_XAXIS) {
 		_km.joy_x = ev.jaxis.value;
 		return handleAxisToMouseMotion(_km.joy_x, _km.joy_y);
@@ -1024,6 +1080,32 @@ bool SdlEventSource::handleJoystickRemoved(const SDL_JoyDeviceEvent &device) {
 	return false;
 }
 
+int SdlEventSource::mapSDLControllerButtonToOSystem(Uint8 sdlButton) {
+	Common::JoystickButton osystemButtons[] = {
+	    Common::JOYSTICK_BUTTON_A,
+	    Common::JOYSTICK_BUTTON_B,
+	    Common::JOYSTICK_BUTTON_X,
+	    Common::JOYSTICK_BUTTON_Y,
+	    Common::JOYSTICK_BUTTON_BACK,
+	    Common::JOYSTICK_BUTTON_GUIDE,
+	    Common::JOYSTICK_BUTTON_START,
+	    Common::JOYSTICK_BUTTON_LEFT_STICK,
+	    Common::JOYSTICK_BUTTON_RIGHT_STICK,
+	    Common::JOYSTICK_BUTTON_LEFT_SHOULDER,
+	    Common::JOYSTICK_BUTTON_RIGHT_SHOULDER,
+	    Common::JOYSTICK_BUTTON_DPAD_UP,
+	    Common::JOYSTICK_BUTTON_DPAD_DOWN,
+	    Common::JOYSTICK_BUTTON_DPAD_LEFT,
+	    Common::JOYSTICK_BUTTON_DPAD_RIGHT
+	};
+
+	if (sdlButton >= ARRAYSIZE(osystemButtons)) {
+		return -1;
+	}
+
+	return osystemButtons[sdlButton];
+}
+
 bool SdlEventSource::handleControllerButton(const SDL_Event &ev, Common::Event &event, bool buttonUp) {
 	using namespace Common;
 
@@ -1071,6 +1153,15 @@ bool SdlEventSource::handleControllerButton(const SDL_Event &ev, Common::Event &
 			{ EVENT_KEYDOWN, KeyState(KEYCODE_KP6, 0), EVENT_KEYDOWN, KeyState(KEYCODE_KP3, 0) }
 	};
 
+	if (!shouldGenerateMouseEvents()) {
+		event.type = buttonUp ? Common::EVENT_JOYBUTTON_UP : Common::EVENT_JOYBUTTON_DOWN;
+		event.joystick.button = mapSDLControllerButtonToOSystem(ev.cbutton.button);
+		if (event.joystick.button == -1)
+				return false;
+
+		return true;
+	}
+
 	if (ev.cbutton.button > SDL_CONTROLLER_BUTTON_DPAD_RIGHT) {
 		warning("Unknown SDL controller button: '%d'", ev.cbutton.button);
 		return false;
@@ -1115,6 +1206,16 @@ bool SdlEventSource::handleControllerButton(const SDL_Event &ev, Common::Event &
 }
 
 bool SdlEventSource::handleControllerAxisMotion(const SDL_Event &ev, Common::Event &event) {
+	// TODO: move handleAxisToMouseMotion to Common?
+#if 0
+	if (!shouldGenerateMouseEvents()) {
+		event.type = Common::EVENT_JOYAXIS_MOTION;
+		event.joystick.axis = ev.caxis.axis;
+		event.joystick.position = ev.caxis.value;
+		return true;
+	}
+#endif
+
 	if (ev.caxis.axis == SDL_CONTROLLER_AXIS_LEFTX) {
 		_km.joy_x = ev.caxis.value;
 		return handleAxisToMouseMotion(_km.joy_x, _km.joy_y);
diff --git a/backends/events/sdl/sdl-events.h b/backends/events/sdl/sdl-events.h
index c2ae0023ceb..6050c802972 100644
--- a/backends/events/sdl/sdl-events.h
+++ b/backends/events/sdl/sdl-events.h
@@ -139,6 +139,7 @@ protected:
 	virtual bool handleMouseButtonDown(SDL_Event &ev, Common::Event &event);
 	virtual bool handleMouseButtonUp(SDL_Event &ev, Common::Event &event);
 	virtual bool handleSysWMEvent(SDL_Event &ev, Common::Event &event);
+	virtual int mapSDLJoystickButtonToOSystem(Uint8 sdlButton);
 	virtual bool handleJoyButtonDown(SDL_Event &ev, Common::Event &event);
 	virtual bool handleJoyButtonUp(SDL_Event &ev, Common::Event &event);
 	virtual bool handleJoyAxisMotion(SDL_Event &ev, Common::Event &event);
@@ -148,10 +149,13 @@ protected:
 #if SDL_VERSION_ATLEAST(2, 0, 0)
 	virtual bool handleJoystickAdded(const SDL_JoyDeviceEvent &event);
 	virtual bool handleJoystickRemoved(const SDL_JoyDeviceEvent &device);
+	virtual int mapSDLControllerButtonToOSystem(Uint8 sdlButton);
 	virtual bool handleControllerButton(const SDL_Event &ev, Common::Event &event, bool buttonUp);
 	virtual bool handleControllerAxisMotion(const SDL_Event &ev, Common::Event &event);
 #endif
 
+	bool shouldGenerateMouseEvents();
+
 	//@}
 
 	/**
diff --git a/common/events.h b/common/events.h
index 4b357252253..ff5d2874f41 100644
--- a/common/events.h
+++ b/common/events.h
@@ -86,7 +86,52 @@ enum EventType {
 	EVENT_VIRTUAL_KEYBOARD = 20,
 #endif
 
-	EVENT_DROP_FILE = 23
+	EVENT_DROP_FILE = 23,
+
+	EVENT_JOYAXIS_MOTION = 24,
+	EVENT_JOYBUTTON_DOWN = 25,
+	EVENT_JOYBUTTON_UP = 26
+};
+
+const int16 JOYAXIS_MIN = -32768;
+const int16 JOYAXIS_MAX = 32767;
+
+/**
+ * Data structure for joystick events
+ */
+struct JoystickState {
+	/** The axis for EVENT_JOYAXIS_MOTION events */
+	byte axis;
+	/** The new axis position for EVENT_JOYAXIS_MOTION events */
+	int16 position;
+	/**
+	 * The button index for EVENT_JOYBUTTON_DOWN/UP events
+	 *
+	 * Some of the button indices match well-known game controller
+	 * buttons. See JoystickButton.
+	 */
+	byte button;
+};
+
+/**
+ *  The list named buttons available from a joystick
+ */
+enum JoystickButton {
+	JOYSTICK_BUTTON_A,
+	JOYSTICK_BUTTON_B,
+	JOYSTICK_BUTTON_X,
+	JOYSTICK_BUTTON_Y,
+	JOYSTICK_BUTTON_BACK,
+	JOYSTICK_BUTTON_GUIDE,
+	JOYSTICK_BUTTON_START,
+	JOYSTICK_BUTTON_LEFT_STICK,
+	JOYSTICK_BUTTON_RIGHT_STICK,
+	JOYSTICK_BUTTON_LEFT_SHOULDER,
+	JOYSTICK_BUTTON_RIGHT_SHOULDER,
+	JOYSTICK_BUTTON_DPAD_UP,
+	JOYSTICK_BUTTON_DPAD_DOWN,
+	JOYSTICK_BUTTON_DPAD_LEFT,
+	JOYSTICK_BUTTON_DPAD_RIGHT
 };
 
 typedef uint32 CustomEventType;
@@ -130,6 +175,12 @@ struct Event {
 	/* The path of the file or directory dragged to the ScummVM window */
 	Common::String path;
 
+	/**
+	 * Joystick data; only valid for joystick events (EVENT_JOYAXIS_MOTION,
+	 * EVENT_JOYBUTTON_DOWN and EVENT_JOYBUTTON_UP).
+	 */
+	JoystickState joystick;
+
 	Event() : type(EVENT_INVALID), kbdRepeat(false) {
 #ifdef ENABLE_KEYMAPPER
 		customType = 0;
diff --git a/engines/engine.h b/engines/engine.h
index d004c29f844..cc7994e047e 100644
--- a/engines/engine.h
+++ b/engines/engine.h
@@ -132,7 +132,15 @@ public:
 		 * If this feature is supported, then the corresponding MetaEngine *must*
 		 * support the kSupportsListSaves feature.
 		 */
-		kSupportsSavingDuringRuntime
+		kSupportsSavingDuringRuntime,
+
+		/**
+		 * Engine must receive joystick events because the game uses them.
+		 * For engines which have not this feature, joystick events are converted
+		 * to mouse events.
+		 */
+		kSupportsJoystick
+
 	};