summaryrefslogtreecommitdiffstats
path: root/src/input_common
diff options
context:
space:
mode:
Diffstat (limited to 'src/input_common')
-rw-r--r--src/input_common/main.cpp11
-rw-r--r--src/input_common/main.h32
-rw-r--r--src/input_common/sdl/sdl.cpp183
-rw-r--r--src/input_common/sdl/sdl.h23
4 files changed, 248 insertions, 1 deletions
diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp
index 557353740..95d40f09f 100644
--- a/src/input_common/main.cpp
+++ b/src/input_common/main.cpp
@@ -71,4 +71,15 @@ std::string GenerateAnalogParamFromKeys(int key_up, int key_down, int key_left,
return circle_pad_param.Serialize();
}
+namespace Polling {
+
+std::vector<std::unique_ptr<DevicePoller>> GetPollers(DeviceType type) {
+#ifdef HAVE_SDL2
+ return SDL::Polling::GetPollers(type);
+#else
+ return {};
+#endif
+}
+
+} // namespace Polling
} // namespace InputCommon
diff --git a/src/input_common/main.h b/src/input_common/main.h
index 5604f0fa8..77a0ce90b 100644
--- a/src/input_common/main.h
+++ b/src/input_common/main.h
@@ -4,7 +4,13 @@
#pragma once
+#include <memory>
#include <string>
+#include <vector>
+
+namespace Common {
+class ParamPackage;
+}
namespace InputCommon {
@@ -31,4 +37,30 @@ std::string GenerateKeyboardParam(int key_code);
std::string GenerateAnalogParamFromKeys(int key_up, int key_down, int key_left, int key_right,
int key_modifier, float modifier_scale);
+namespace Polling {
+
+enum class DeviceType { Button, Analog };
+
+/**
+ * A class that can be used to get inputs from an input device like controllers without having to
+ * poll the device's status yourself
+ */
+class DevicePoller {
+public:
+ virtual ~DevicePoller() = default;
+ /// Setup and start polling for inputs, should be called before GetNextInput
+ virtual void Start() = 0;
+ /// Stop polling
+ virtual void Stop() = 0;
+ /**
+ * Every call to this function returns the next input recorded since calling Start
+ * @return A ParamPackage of the recorded input, which can be used to create an InputDevice.
+ * If there has been no input, the package is empty
+ */
+ virtual Common::ParamPackage GetNextInput() = 0;
+};
+
+// Get all DevicePoller from all backends for a specific device type
+std::vector<std::unique_ptr<DevicePoller>> GetPollers(DeviceType type);
+} // namespace Polling
} // namespace InputCommon
diff --git a/src/input_common/sdl/sdl.cpp b/src/input_common/sdl/sdl.cpp
index d404afa89..88b557c5d 100644
--- a/src/input_common/sdl/sdl.cpp
+++ b/src/input_common/sdl/sdl.cpp
@@ -3,13 +3,15 @@
// Refer to the license.txt file included.
#include <cmath>
-#include <memory>
#include <string>
#include <tuple>
#include <unordered_map>
+#include <utility>
#include <SDL.h>
#include "common/logging/log.h"
#include "common/math_util.h"
+#include "common/param_package.h"
+#include "input_common/main.h"
#include "input_common/sdl/sdl.h"
namespace InputCommon {
@@ -69,6 +71,10 @@ public:
return (SDL_JoystickGetHat(joystick.get(), hat) & direction) != 0;
}
+ SDL_JoystickID GetJoystickID() const {
+ return SDL_JoystickInstanceID(joystick.get());
+ }
+
private:
std::unique_ptr<SDL_Joystick, decltype(&SDL_JoystickClose)> joystick;
};
@@ -247,5 +253,180 @@ void Shutdown() {
}
}
+/**
+ * This function converts a joystick ID used in SDL events to the device index. This is necessary
+ * because Citra opens joysticks using their indices, not their IDs.
+ */
+static int JoystickIDToDeviceIndex(SDL_JoystickID id) {
+ int num_joysticks = SDL_NumJoysticks();
+ for (int i = 0; i < num_joysticks; i++) {
+ auto joystick = GetJoystick(i);
+ if (joystick->GetJoystickID() == id) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+Common::ParamPackage SDLEventToButtonParamPackage(const SDL_Event& event) {
+ Common::ParamPackage params({{"engine", "sdl"}});
+ switch (event.type) {
+ case SDL_JOYAXISMOTION:
+ params.Set("joystick", JoystickIDToDeviceIndex(event.jaxis.which));
+ params.Set("axis", event.jaxis.axis);
+ if (event.jaxis.value > 0) {
+ params.Set("direction", "+");
+ params.Set("threshold", "0.5");
+ } else {
+ params.Set("direction", "-");
+ params.Set("threshold", "-0.5");
+ }
+ break;
+ case SDL_JOYBUTTONUP:
+ params.Set("joystick", JoystickIDToDeviceIndex(event.jbutton.which));
+ params.Set("button", event.jbutton.button);
+ break;
+ case SDL_JOYHATMOTION:
+ params.Set("joystick", JoystickIDToDeviceIndex(event.jhat.which));
+ params.Set("hat", event.jhat.hat);
+ switch (event.jhat.value) {
+ case SDL_HAT_UP:
+ params.Set("direction", "up");
+ break;
+ case SDL_HAT_DOWN:
+ params.Set("direction", "down");
+ break;
+ case SDL_HAT_LEFT:
+ params.Set("direction", "left");
+ break;
+ case SDL_HAT_RIGHT:
+ params.Set("direction", "right");
+ break;
+ default:
+ return {};
+ }
+ break;
+ }
+ return params;
+}
+
+namespace Polling {
+
+class SDLPoller : public InputCommon::Polling::DevicePoller {
+public:
+ SDLPoller() = default;
+
+ ~SDLPoller() = default;
+
+ void Start() override {
+ // SDL joysticks must be opened, otherwise they don't generate events
+ SDL_JoystickUpdate();
+ int num_joysticks = SDL_NumJoysticks();
+ for (int i = 0; i < num_joysticks; i++) {
+ joysticks_opened.emplace_back(GetJoystick(i));
+ }
+ // Empty event queue to get rid of old events. citra-qt doesn't use the queue
+ SDL_Event dummy;
+ while (SDL_PollEvent(&dummy)) {
+ }
+ }
+
+ void Stop() override {
+ joysticks_opened.clear();
+ }
+
+private:
+ std::vector<std::shared_ptr<SDLJoystick>> joysticks_opened;
+};
+
+class SDLButtonPoller final : public SDLPoller {
+public:
+ SDLButtonPoller() = default;
+
+ ~SDLButtonPoller() = default;
+
+ Common::ParamPackage GetNextInput() override {
+ SDL_Event event;
+ while (SDL_PollEvent(&event)) {
+ switch (event.type) {
+ case SDL_JOYAXISMOTION:
+ if (std::abs(event.jaxis.value / 32767.0) < 0.5) {
+ break;
+ }
+ case SDL_JOYBUTTONUP:
+ case SDL_JOYHATMOTION:
+ return SDLEventToButtonParamPackage(event);
+ }
+ }
+ return {};
+ }
+};
+
+class SDLAnalogPoller final : public SDLPoller {
+public:
+ SDLAnalogPoller() = default;
+
+ ~SDLAnalogPoller() = default;
+
+ void Start() override {
+ SDLPoller::Start();
+
+ // Reset stored axes
+ analog_xaxis = -1;
+ analog_yaxis = -1;
+ analog_axes_joystick = -1;
+ }
+
+ Common::ParamPackage GetNextInput() override {
+ SDL_Event event;
+ while (SDL_PollEvent(&event)) {
+ if (event.type != SDL_JOYAXISMOTION || std::abs(event.jaxis.value / 32767.0) < 0.5) {
+ continue;
+ }
+ // An analog device needs two axes, so we need to store the axis for later and wait for
+ // a second SDL event. The axes also must be from the same joystick.
+ int axis = event.jaxis.axis;
+ if (analog_xaxis == -1) {
+ analog_xaxis = axis;
+ analog_axes_joystick = event.jaxis.which;
+ } else if (analog_yaxis == -1 && analog_xaxis != axis &&
+ analog_axes_joystick == event.jaxis.which) {
+ analog_yaxis = axis;
+ }
+ }
+ Common::ParamPackage params;
+ if (analog_xaxis != -1 && analog_yaxis != -1) {
+ params.Set("engine", "sdl");
+ params.Set("joystick", JoystickIDToDeviceIndex(analog_axes_joystick));
+ params.Set("axis_x", analog_xaxis);
+ params.Set("axis_y", analog_yaxis);
+ analog_xaxis = -1;
+ analog_yaxis = -1;
+ analog_axes_joystick = -1;
+ return params;
+ }
+ return params;
+ }
+
+private:
+ int analog_xaxis = -1;
+ int analog_yaxis = -1;
+ SDL_JoystickID analog_axes_joystick = -1;
+};
+
+std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> GetPollers(
+ InputCommon::Polling::DeviceType type) {
+ std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> pollers;
+ switch (type) {
+ case InputCommon::Polling::DeviceType::Analog:
+ pollers.push_back(std::make_unique<SDLAnalogPoller>());
+ break;
+ case InputCommon::Polling::DeviceType::Button:
+ pollers.push_back(std::make_unique<SDLButtonPoller>());
+ break;
+ }
+ return std::move(pollers);
+}
+} // namespace Polling
} // namespace SDL
} // namespace InputCommon
diff --git a/src/input_common/sdl/sdl.h b/src/input_common/sdl/sdl.h
index 3e72debcc..7934099d4 100644
--- a/src/input_common/sdl/sdl.h
+++ b/src/input_common/sdl/sdl.h
@@ -4,8 +4,21 @@
#pragma once
+#include <memory>
+#include <vector>
#include "core/frontend/input.h"
+union SDL_Event;
+namespace Common {
+class ParamPackage;
+}
+namespace InputCommon {
+namespace Polling {
+class DevicePoller;
+enum class DeviceType;
+} // namespace Polling
+} // namespace InputCommon
+
namespace InputCommon {
namespace SDL {
@@ -15,5 +28,15 @@ void Init();
/// Unresisters SDL device factories and shut them down.
void Shutdown();
+/// Creates a ParamPackage from an SDL_Event that can directly be used to create a ButtonDevice
+Common::ParamPackage SDLEventToButtonParamPackage(const SDL_Event& event);
+
+namespace Polling {
+
+/// Get all DevicePoller that use the SDL backend for a specific device type
+std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> GetPollers(
+ InputCommon::Polling::DeviceType type);
+
+} // namespace Polling
} // namespace SDL
} // namespace InputCommon