summaryrefslogtreecommitdiff
path: root/src/input_common/sdl/sdl.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/input_common/sdl/sdl.cpp')
-rw-r--r--src/input_common/sdl/sdl.cpp183
1 files changed, 182 insertions, 1 deletions
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 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <cmath> 5#include <cmath>
6#include <memory>
7#include <string> 6#include <string>
8#include <tuple> 7#include <tuple>
9#include <unordered_map> 8#include <unordered_map>
9#include <utility>
10#include <SDL.h> 10#include <SDL.h>
11#include "common/logging/log.h" 11#include "common/logging/log.h"
12#include "common/math_util.h" 12#include "common/math_util.h"
13#include "common/param_package.h"
14#include "input_common/main.h"
13#include "input_common/sdl/sdl.h" 15#include "input_common/sdl/sdl.h"
14 16
15namespace InputCommon { 17namespace InputCommon {
@@ -69,6 +71,10 @@ public:
69 return (SDL_JoystickGetHat(joystick.get(), hat) & direction) != 0; 71 return (SDL_JoystickGetHat(joystick.get(), hat) & direction) != 0;
70 } 72 }
71 73
74 SDL_JoystickID GetJoystickID() const {
75 return SDL_JoystickInstanceID(joystick.get());
76 }
77
72private: 78private:
73 std::unique_ptr<SDL_Joystick, decltype(&SDL_JoystickClose)> joystick; 79 std::unique_ptr<SDL_Joystick, decltype(&SDL_JoystickClose)> joystick;
74}; 80};
@@ -247,5 +253,180 @@ void Shutdown() {
247 } 253 }
248} 254}
249 255
256/**
257 * This function converts a joystick ID used in SDL events to the device index. This is necessary
258 * because Citra opens joysticks using their indices, not their IDs.
259 */
260static int JoystickIDToDeviceIndex(SDL_JoystickID id) {
261 int num_joysticks = SDL_NumJoysticks();
262 for (int i = 0; i < num_joysticks; i++) {
263 auto joystick = GetJoystick(i);
264 if (joystick->GetJoystickID() == id) {
265 return i;
266 }
267 }
268 return -1;
269}
270
271Common::ParamPackage SDLEventToButtonParamPackage(const SDL_Event& event) {
272 Common::ParamPackage params({{"engine", "sdl"}});
273 switch (event.type) {
274 case SDL_JOYAXISMOTION:
275 params.Set("joystick", JoystickIDToDeviceIndex(event.jaxis.which));
276 params.Set("axis", event.jaxis.axis);
277 if (event.jaxis.value > 0) {
278 params.Set("direction", "+");
279 params.Set("threshold", "0.5");
280 } else {
281 params.Set("direction", "-");
282 params.Set("threshold", "-0.5");
283 }
284 break;
285 case SDL_JOYBUTTONUP:
286 params.Set("joystick", JoystickIDToDeviceIndex(event.jbutton.which));
287 params.Set("button", event.jbutton.button);
288 break;
289 case SDL_JOYHATMOTION:
290 params.Set("joystick", JoystickIDToDeviceIndex(event.jhat.which));
291 params.Set("hat", event.jhat.hat);
292 switch (event.jhat.value) {
293 case SDL_HAT_UP:
294 params.Set("direction", "up");
295 break;
296 case SDL_HAT_DOWN:
297 params.Set("direction", "down");
298 break;
299 case SDL_HAT_LEFT:
300 params.Set("direction", "left");
301 break;
302 case SDL_HAT_RIGHT:
303 params.Set("direction", "right");
304 break;
305 default:
306 return {};
307 }
308 break;
309 }
310 return params;
311}
312
313namespace Polling {
314
315class SDLPoller : public InputCommon::Polling::DevicePoller {
316public:
317 SDLPoller() = default;
318
319 ~SDLPoller() = default;
320
321 void Start() override {
322 // SDL joysticks must be opened, otherwise they don't generate events
323 SDL_JoystickUpdate();
324 int num_joysticks = SDL_NumJoysticks();
325 for (int i = 0; i < num_joysticks; i++) {
326 joysticks_opened.emplace_back(GetJoystick(i));
327 }
328 // Empty event queue to get rid of old events. citra-qt doesn't use the queue
329 SDL_Event dummy;
330 while (SDL_PollEvent(&dummy)) {
331 }
332 }
333
334 void Stop() override {
335 joysticks_opened.clear();
336 }
337
338private:
339 std::vector<std::shared_ptr<SDLJoystick>> joysticks_opened;
340};
341
342class SDLButtonPoller final : public SDLPoller {
343public:
344 SDLButtonPoller() = default;
345
346 ~SDLButtonPoller() = default;
347
348 Common::ParamPackage GetNextInput() override {
349 SDL_Event event;
350 while (SDL_PollEvent(&event)) {
351 switch (event.type) {
352 case SDL_JOYAXISMOTION:
353 if (std::abs(event.jaxis.value / 32767.0) < 0.5) {
354 break;
355 }
356 case SDL_JOYBUTTONUP:
357 case SDL_JOYHATMOTION:
358 return SDLEventToButtonParamPackage(event);
359 }
360 }
361 return {};
362 }
363};
364
365class SDLAnalogPoller final : public SDLPoller {
366public:
367 SDLAnalogPoller() = default;
368
369 ~SDLAnalogPoller() = default;
370
371 void Start() override {
372 SDLPoller::Start();
373
374 // Reset stored axes
375 analog_xaxis = -1;
376 analog_yaxis = -1;
377 analog_axes_joystick = -1;
378 }
379
380 Common::ParamPackage GetNextInput() override {
381 SDL_Event event;
382 while (SDL_PollEvent(&event)) {
383 if (event.type != SDL_JOYAXISMOTION || std::abs(event.jaxis.value / 32767.0) < 0.5) {
384 continue;
385 }
386 // An analog device needs two axes, so we need to store the axis for later and wait for
387 // a second SDL event. The axes also must be from the same joystick.
388 int axis = event.jaxis.axis;
389 if (analog_xaxis == -1) {
390 analog_xaxis = axis;
391 analog_axes_joystick = event.jaxis.which;
392 } else if (analog_yaxis == -1 && analog_xaxis != axis &&
393 analog_axes_joystick == event.jaxis.which) {
394 analog_yaxis = axis;
395 }
396 }
397 Common::ParamPackage params;
398 if (analog_xaxis != -1 && analog_yaxis != -1) {
399 params.Set("engine", "sdl");
400 params.Set("joystick", JoystickIDToDeviceIndex(analog_axes_joystick));
401 params.Set("axis_x", analog_xaxis);
402 params.Set("axis_y", analog_yaxis);
403 analog_xaxis = -1;
404 analog_yaxis = -1;
405 analog_axes_joystick = -1;
406 return params;
407 }
408 return params;
409 }
410
411private:
412 int analog_xaxis = -1;
413 int analog_yaxis = -1;
414 SDL_JoystickID analog_axes_joystick = -1;
415};
416
417std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> GetPollers(
418 InputCommon::Polling::DeviceType type) {
419 std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> pollers;
420 switch (type) {
421 case InputCommon::Polling::DeviceType::Analog:
422 pollers.push_back(std::make_unique<SDLAnalogPoller>());
423 break;
424 case InputCommon::Polling::DeviceType::Button:
425 pollers.push_back(std::make_unique<SDLButtonPoller>());
426 break;
427 }
428 return std::move(pollers);
429}
430} // namespace Polling
250} // namespace SDL 431} // namespace SDL
251} // namespace InputCommon 432} // namespace InputCommon