summaryrefslogtreecommitdiff
path: root/src/input_common
diff options
context:
space:
mode:
authorGravatar bunnei2017-03-17 14:59:39 -0400
committerGravatar GitHub2017-03-17 14:59:39 -0400
commit423ab5e2bcf5a522e5f412447c05f648df57a14c (patch)
tree1e60eaeffa59229254a47f885d2fe2cbbdc1a5c0 /src/input_common
parentMerge pull request #2618 from wwylele/log-less-filename (diff)
parentqt/config_input: don't connect for null button (diff)
downloadyuzu-423ab5e2bcf5a522e5f412447c05f648df57a14c.tar.gz
yuzu-423ab5e2bcf5a522e5f412447c05f648df57a14c.tar.xz
yuzu-423ab5e2bcf5a522e5f412447c05f648df57a14c.zip
Merge pull request #2497 from wwylele/input-2
Refactor input emulation & add SDL gamepad support
Diffstat (limited to 'src/input_common')
-rw-r--r--src/input_common/CMakeLists.txt27
-rwxr-xr-xsrc/input_common/analog_from_button.cpp58
-rwxr-xr-xsrc/input_common/analog_from_button.h31
-rw-r--r--src/input_common/keyboard.cpp82
-rw-r--r--src/input_common/keyboard.h45
-rw-r--r--src/input_common/main.cpp63
-rw-r--r--src/input_common/main.h29
-rw-r--r--src/input_common/sdl/sdl.cpp202
-rw-r--r--src/input_common/sdl/sdl.h19
9 files changed, 556 insertions, 0 deletions
diff --git a/src/input_common/CMakeLists.txt b/src/input_common/CMakeLists.txt
new file mode 100644
index 000000000..cfe5caaa3
--- /dev/null
+++ b/src/input_common/CMakeLists.txt
@@ -0,0 +1,27 @@
1set(SRCS
2 analog_from_button.cpp
3 keyboard.cpp
4 main.cpp
5 )
6
7set(HEADERS
8 analog_from_button.h
9 keyboard.h
10 main.h
11 )
12
13if(SDL2_FOUND)
14 set(SRCS ${SRCS} sdl/sdl.cpp)
15 set(HEADERS ${HEADERS} sdl/sdl.h)
16 include_directories(${SDL2_INCLUDE_DIR})
17endif()
18
19create_directory_groups(${SRCS} ${HEADERS})
20
21add_library(input_common STATIC ${SRCS} ${HEADERS})
22target_link_libraries(input_common common core)
23
24if(SDL2_FOUND)
25 target_link_libraries(input_common ${SDL2_LIBRARY})
26 set_property(TARGET input_common APPEND PROPERTY COMPILE_DEFINITIONS HAVE_SDL2)
27endif()
diff --git a/src/input_common/analog_from_button.cpp b/src/input_common/analog_from_button.cpp
new file mode 100755
index 000000000..e1a260762
--- /dev/null
+++ b/src/input_common/analog_from_button.cpp
@@ -0,0 +1,58 @@
1// Copyright 2017 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "input_common/analog_from_button.h"
6
7namespace InputCommon {
8
9class Analog final : public Input::AnalogDevice {
10public:
11 using Button = std::unique_ptr<Input::ButtonDevice>;
12
13 Analog(Button up_, Button down_, Button left_, Button right_, Button modifier_,
14 float modifier_scale_)
15 : up(std::move(up_)), down(std::move(down_)), left(std::move(left_)),
16 right(std::move(right_)), modifier(std::move(modifier_)),
17 modifier_scale(modifier_scale_) {}
18
19 std::tuple<float, float> GetStatus() const override {
20 constexpr float SQRT_HALF = 0.707106781f;
21 int x = 0, y = 0;
22
23 if (right->GetStatus())
24 ++x;
25 if (left->GetStatus())
26 --x;
27 if (up->GetStatus())
28 ++y;
29 if (down->GetStatus())
30 --y;
31
32 float coef = modifier->GetStatus() ? modifier_scale : 1.0f;
33 return std::make_tuple(x * coef * (y == 0 ? 1.0f : SQRT_HALF),
34 y * coef * (x == 0 ? 1.0f : SQRT_HALF));
35 }
36
37private:
38 Button up;
39 Button down;
40 Button left;
41 Button right;
42 Button modifier;
43 float modifier_scale;
44};
45
46std::unique_ptr<Input::AnalogDevice> AnalogFromButton::Create(const Common::ParamPackage& params) {
47 const std::string null_engine = Common::ParamPackage{{"engine", "null"}}.Serialize();
48 auto up = Input::CreateDevice<Input::ButtonDevice>(params.Get("up", null_engine));
49 auto down = Input::CreateDevice<Input::ButtonDevice>(params.Get("down", null_engine));
50 auto left = Input::CreateDevice<Input::ButtonDevice>(params.Get("left", null_engine));
51 auto right = Input::CreateDevice<Input::ButtonDevice>(params.Get("right", null_engine));
52 auto modifier = Input::CreateDevice<Input::ButtonDevice>(params.Get("modifier", null_engine));
53 auto modifier_scale = params.Get("modifier_scale", 0.5f);
54 return std::make_unique<Analog>(std::move(up), std::move(down), std::move(left),
55 std::move(right), std::move(modifier), modifier_scale);
56}
57
58} // namespace InputCommon
diff --git a/src/input_common/analog_from_button.h b/src/input_common/analog_from_button.h
new file mode 100755
index 000000000..bbd583dd9
--- /dev/null
+++ b/src/input_common/analog_from_button.h
@@ -0,0 +1,31 @@
1// Copyright 2017 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <memory>
8#include "core/frontend/input.h"
9
10namespace InputCommon {
11
12/**
13 * An analog device factory that takes direction button devices and combines them into a analog
14 * device.
15 */
16class AnalogFromButton final : public Input::Factory<Input::AnalogDevice> {
17public:
18 /**
19 * Creates an analog device from direction button devices
20 * @param params contains parameters for creating the device:
21 * - "up": a serialized ParamPackage for creating a button device for up direction
22 * - "down": a serialized ParamPackage for creating a button device for down direction
23 * - "left": a serialized ParamPackage for creating a button device for left direction
24 * - "right": a serialized ParamPackage for creating a button device for right direction
25 * - "modifier": a serialized ParamPackage for creating a button device as the modifier
26 * - "modifier_scale": a float for the multiplier the modifier gives to the position
27 */
28 std::unique_ptr<Input::AnalogDevice> Create(const Common::ParamPackage& params) override;
29};
30
31} // namespace InputCommon
diff --git a/src/input_common/keyboard.cpp b/src/input_common/keyboard.cpp
new file mode 100644
index 000000000..a8fc01f2e
--- /dev/null
+++ b/src/input_common/keyboard.cpp
@@ -0,0 +1,82 @@
1// Copyright 2017 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <atomic>
6#include <list>
7#include <mutex>
8#include "input_common/keyboard.h"
9
10namespace InputCommon {
11
12class KeyButton final : public Input::ButtonDevice {
13public:
14 explicit KeyButton(std::shared_ptr<KeyButtonList> key_button_list_)
15 : key_button_list(key_button_list_) {}
16
17 ~KeyButton();
18
19 bool GetStatus() const override {
20 return status.load();
21 }
22
23 friend class KeyButtonList;
24
25private:
26 std::shared_ptr<KeyButtonList> key_button_list;
27 std::atomic<bool> status{false};
28};
29
30struct KeyButtonPair {
31 int key_code;
32 KeyButton* key_button;
33};
34
35class KeyButtonList {
36public:
37 void AddKeyButton(int key_code, KeyButton* key_button) {
38 std::lock_guard<std::mutex> guard(mutex);
39 list.push_back(KeyButtonPair{key_code, key_button});
40 }
41
42 void RemoveKeyButton(const KeyButton* key_button) {
43 std::lock_guard<std::mutex> guard(mutex);
44 list.remove_if(
45 [key_button](const KeyButtonPair& pair) { return pair.key_button == key_button; });
46 }
47
48 void ChangeKeyStatus(int key_code, bool pressed) {
49 std::lock_guard<std::mutex> guard(mutex);
50 for (const KeyButtonPair& pair : list) {
51 if (pair.key_code == key_code)
52 pair.key_button->status.store(pressed);
53 }
54 }
55
56private:
57 std::mutex mutex;
58 std::list<KeyButtonPair> list;
59};
60
61Keyboard::Keyboard() : key_button_list{std::make_shared<KeyButtonList>()} {}
62
63KeyButton::~KeyButton() {
64 key_button_list->RemoveKeyButton(this);
65}
66
67std::unique_ptr<Input::ButtonDevice> Keyboard::Create(const Common::ParamPackage& params) {
68 int key_code = params.Get("code", 0);
69 std::unique_ptr<KeyButton> button = std::make_unique<KeyButton>(key_button_list);
70 key_button_list->AddKeyButton(key_code, button.get());
71 return std::move(button);
72}
73
74void Keyboard::PressKey(int key_code) {
75 key_button_list->ChangeKeyStatus(key_code, true);
76}
77
78void Keyboard::ReleaseKey(int key_code) {
79 key_button_list->ChangeKeyStatus(key_code, false);
80}
81
82} // namespace InputCommon
diff --git a/src/input_common/keyboard.h b/src/input_common/keyboard.h
new file mode 100644
index 000000000..76359aa30
--- /dev/null
+++ b/src/input_common/keyboard.h
@@ -0,0 +1,45 @@
1// Copyright 2017 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <memory>
8#include "core/frontend/input.h"
9
10namespace InputCommon {
11
12class KeyButtonList;
13
14/**
15 * A button device factory representing a keyboard. It receives keyboard events and forward them
16 * to all button devices it created.
17 */
18class Keyboard final : public Input::Factory<Input::ButtonDevice> {
19public:
20 Keyboard();
21
22 /**
23 * Creates a button device from a keyboard key
24 * @param params contains parameters for creating the device:
25 * - "code": the code of the key to bind with the button
26 */
27 std::unique_ptr<Input::ButtonDevice> Create(const Common::ParamPackage& params) override;
28
29 /**
30 * Sets the status of all buttons bound with the key to pressed
31 * @param key_code the code of the key to press
32 */
33 void PressKey(int key_code);
34
35 /**
36 * Sets the status of all buttons bound with the key to released
37 * @param key_code the code of the key to release
38 */
39 void ReleaseKey(int key_code);
40
41private:
42 std::shared_ptr<KeyButtonList> key_button_list;
43};
44
45} // namespace InputCommon
diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp
new file mode 100644
index 000000000..699f41e6b
--- /dev/null
+++ b/src/input_common/main.cpp
@@ -0,0 +1,63 @@
1// Copyright 2017 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <memory>
6#include "common/param_package.h"
7#include "input_common/analog_from_button.h"
8#include "input_common/keyboard.h"
9#include "input_common/main.h"
10#ifdef HAVE_SDL2
11#include "input_common/sdl/sdl.h"
12#endif
13
14namespace InputCommon {
15
16static std::shared_ptr<Keyboard> keyboard;
17
18void Init() {
19 keyboard = std::make_shared<InputCommon::Keyboard>();
20 Input::RegisterFactory<Input::ButtonDevice>("keyboard", keyboard);
21 Input::RegisterFactory<Input::AnalogDevice>("analog_from_button",
22 std::make_shared<InputCommon::AnalogFromButton>());
23#ifdef HAVE_SDL2
24 SDL::Init();
25#endif
26}
27
28void Shutdown() {
29 Input::UnregisterFactory<Input::ButtonDevice>("keyboard");
30 keyboard.reset();
31 Input::UnregisterFactory<Input::AnalogDevice>("analog_from_button");
32
33#ifdef HAVE_SDL2
34 SDL::Shutdown();
35#endif
36}
37
38Keyboard* GetKeyboard() {
39 return keyboard.get();
40}
41
42std::string GenerateKeyboardParam(int key_code) {
43 Common::ParamPackage param{
44 {"engine", "keyboard"}, {"code", std::to_string(key_code)},
45 };
46 return param.Serialize();
47}
48
49std::string GenerateAnalogParamFromKeys(int key_up, int key_down, int key_left, int key_right,
50 int key_modifier, float modifier_scale) {
51 Common::ParamPackage circle_pad_param{
52 {"engine", "analog_from_button"},
53 {"up", GenerateKeyboardParam(key_up)},
54 {"down", GenerateKeyboardParam(key_down)},
55 {"left", GenerateKeyboardParam(key_left)},
56 {"right", GenerateKeyboardParam(key_right)},
57 {"modifier", GenerateKeyboardParam(key_modifier)},
58 {"modifier_scale", std::to_string(modifier_scale)},
59 };
60 return circle_pad_param.Serialize();
61}
62
63} // namespace InputCommon
diff --git a/src/input_common/main.h b/src/input_common/main.h
new file mode 100644
index 000000000..140bbd014
--- /dev/null
+++ b/src/input_common/main.h
@@ -0,0 +1,29 @@
1// Copyright 2017 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <string>
8
9namespace InputCommon {
10
11/// Initializes and registers all built-in input device factories.
12void Init();
13
14/// Unresisters all build-in input device factories and shut them down.
15void Shutdown();
16
17class Keyboard;
18
19/// Gets the keyboard button device factory.
20Keyboard* GetKeyboard();
21
22/// Generates a serialized param package for creating a keyboard button device
23std::string GenerateKeyboardParam(int key_code);
24
25/// Generates a serialized param package for creating an analog device taking input from keyboard
26std::string GenerateAnalogParamFromKeys(int key_up, int key_down, int key_left, int key_right,
27 int key_modifier, float modifier_scale);
28
29} // namespace InputCommon
diff --git a/src/input_common/sdl/sdl.cpp b/src/input_common/sdl/sdl.cpp
new file mode 100644
index 000000000..ae0206909
--- /dev/null
+++ b/src/input_common/sdl/sdl.cpp
@@ -0,0 +1,202 @@
1// Copyright 2017 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <cmath>
6#include <memory>
7#include <string>
8#include <tuple>
9#include <unordered_map>
10#include <SDL.h>
11#include "common/math_util.h"
12#include "input_common/sdl/sdl.h"
13
14namespace InputCommon {
15
16namespace SDL {
17
18class SDLJoystick;
19class SDLButtonFactory;
20class SDLAnalogFactory;
21static std::unordered_map<int, std::weak_ptr<SDLJoystick>> joystick_list;
22static std::shared_ptr<SDLButtonFactory> button_factory;
23static std::shared_ptr<SDLAnalogFactory> analog_factory;
24
25static bool initialized = false;
26
27class SDLJoystick {
28public:
29 explicit SDLJoystick(int joystick_index)
30 : joystick{SDL_JoystickOpen(joystick_index), SDL_JoystickClose} {
31 if (!joystick) {
32 LOG_ERROR(Input, "failed to open joystick %d", joystick_index);
33 }
34 }
35
36 bool GetButton(int button) const {
37 if (!joystick)
38 return {};
39 SDL_JoystickUpdate();
40 return SDL_JoystickGetButton(joystick.get(), button) == 1;
41 }
42
43 std::tuple<float, float> GetAnalog(int axis_x, int axis_y) const {
44 if (!joystick)
45 return {};
46 SDL_JoystickUpdate();
47 float x = SDL_JoystickGetAxis(joystick.get(), axis_x) / 32767.0f;
48 float y = SDL_JoystickGetAxis(joystick.get(), axis_y) / 32767.0f;
49 y = -y; // 3DS uses an y-axis inverse from SDL
50
51 // Make sure the coordinates are in the unit circle,
52 // otherwise normalize it.
53 float r = x * x + y * y;
54 if (r > 1.0f) {
55 r = std::sqrt(r);
56 x /= r;
57 y /= r;
58 }
59
60 return std::make_tuple(x, y);
61 }
62
63 bool GetHatDirection(int hat, Uint8 direction) const {
64 return (SDL_JoystickGetHat(joystick.get(), hat) & direction) != 0;
65 }
66
67private:
68 std::unique_ptr<SDL_Joystick, decltype(&SDL_JoystickClose)> joystick;
69};
70
71class SDLButton final : public Input::ButtonDevice {
72public:
73 explicit SDLButton(std::shared_ptr<SDLJoystick> joystick_, int button_)
74 : joystick(joystick_), button(button_) {}
75
76 bool GetStatus() const override {
77 return joystick->GetButton(button);
78 }
79
80private:
81 std::shared_ptr<SDLJoystick> joystick;
82 int button;
83};
84
85class SDLDirectionButton final : public Input::ButtonDevice {
86public:
87 explicit SDLDirectionButton(std::shared_ptr<SDLJoystick> joystick_, int hat_, Uint8 direction_)
88 : joystick(joystick_), hat(hat_), direction(direction_) {}
89
90 bool GetStatus() const override {
91 return joystick->GetHatDirection(hat, direction);
92 }
93
94private:
95 std::shared_ptr<SDLJoystick> joystick;
96 int hat;
97 Uint8 direction;
98};
99
100class SDLAnalog final : public Input::AnalogDevice {
101public:
102 SDLAnalog(std::shared_ptr<SDLJoystick> joystick_, int axis_x_, int axis_y_)
103 : joystick(joystick_), axis_x(axis_x_), axis_y(axis_y_) {}
104
105 std::tuple<float, float> GetStatus() const override {
106 return joystick->GetAnalog(axis_x, axis_y);
107 }
108
109private:
110 std::shared_ptr<SDLJoystick> joystick;
111 int axis_x;
112 int axis_y;
113};
114
115static std::shared_ptr<SDLJoystick> GetJoystick(int joystick_index) {
116 std::shared_ptr<SDLJoystick> joystick = joystick_list[joystick_index].lock();
117 if (!joystick) {
118 joystick = std::make_shared<SDLJoystick>(joystick_index);
119 joystick_list[joystick_index] = joystick;
120 }
121 return joystick;
122}
123
124/// A button device factory that creates button devices from SDL joystick
125class SDLButtonFactory final : public Input::Factory<Input::ButtonDevice> {
126public:
127 /**
128 * Creates a button device from a joystick button
129 * @param params contains parameters for creating the device:
130 * - "joystick": the index of the joystick to bind
131 * - "button"(optional): the index of the button to bind
132 * - "hat"(optional): the index of the hat to bind as direction buttons
133 * - "direction"(only used for hat): the direction name of the hat to bind. Can be "up",
134 * "down", "left" or "right"
135 */
136 std::unique_ptr<Input::ButtonDevice> Create(const Common::ParamPackage& params) override {
137 const int joystick_index = params.Get("joystick", 0);
138
139 if (params.Has("hat")) {
140 const int hat = params.Get("hat", 0);
141 const std::string direction_name = params.Get("direction", "");
142 Uint8 direction;
143 if (direction_name == "up") {
144 direction = SDL_HAT_UP;
145 } else if (direction_name == "down") {
146 direction = SDL_HAT_DOWN;
147 } else if (direction_name == "left") {
148 direction = SDL_HAT_LEFT;
149 } else if (direction_name == "right") {
150 direction = SDL_HAT_RIGHT;
151 } else {
152 direction = 0;
153 }
154 return std::make_unique<SDLDirectionButton>(GetJoystick(joystick_index), hat,
155 direction);
156 }
157
158 const int button = params.Get("button", 0);
159 return std::make_unique<SDLButton>(GetJoystick(joystick_index), button);
160 }
161};
162
163/// An analog device factory that creates analog devices from SDL joystick
164class SDLAnalogFactory final : public Input::Factory<Input::AnalogDevice> {
165public:
166 /**
167 * Creates analog device from joystick axes
168 * @param params contains parameters for creating the device:
169 * - "joystick": the index of the joystick to bind
170 * - "axis_x": the index of the axis to be bind as x-axis
171 * - "axis_y": the index of the axis to be bind as y-axis
172 */
173 std::unique_ptr<Input::AnalogDevice> Create(const Common::ParamPackage& params) override {
174 const int joystick_index = params.Get("joystick", 0);
175 const int axis_x = params.Get("axis_x", 0);
176 const int axis_y = params.Get("axis_y", 1);
177 return std::make_unique<SDLAnalog>(GetJoystick(joystick_index), axis_x, axis_y);
178 }
179};
180
181void Init() {
182 if (SDL_Init(SDL_INIT_JOYSTICK) < 0) {
183 LOG_CRITICAL(Input, "SDL_Init(SDL_INIT_JOYSTICK) failed with: %s", SDL_GetError());
184 } else {
185 using namespace Input;
186 RegisterFactory<ButtonDevice>("sdl", std::make_shared<SDLButtonFactory>());
187 RegisterFactory<AnalogDevice>("sdl", std::make_shared<SDLAnalogFactory>());
188 initialized = true;
189 }
190}
191
192void Shutdown() {
193 if (initialized) {
194 using namespace Input;
195 UnregisterFactory<ButtonDevice>("sdl");
196 UnregisterFactory<AnalogDevice>("sdl");
197 SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
198 }
199}
200
201} // namespace SDL
202} // namespace InputCommon
diff --git a/src/input_common/sdl/sdl.h b/src/input_common/sdl/sdl.h
new file mode 100644
index 000000000..3e72debcc
--- /dev/null
+++ b/src/input_common/sdl/sdl.h
@@ -0,0 +1,19 @@
1// Copyright 2017 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "core/frontend/input.h"
8
9namespace InputCommon {
10namespace SDL {
11
12/// Initializes and registers SDL device factories
13void Init();
14
15/// Unresisters SDL device factories and shut them down.
16void Shutdown();
17
18} // namespace SDL
19} // namespace InputCommon