1// SuperTux
2// Copyright (C) 2014 Ingo Ruhnke <grumbel@gmail.com>
3//
4// This program is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with this program. If not, see <http://www.gnu.org/licenses/>.
16
17#include "control/joystick_manager.hpp"
18
19#include <algorithm>
20
21#include "control/input_manager.hpp"
22#include "control/joystick_config.hpp"
23#include "gui/menu_manager.hpp"
24#include "util/log.hpp"
25
26JoystickManager::JoystickManager(InputManager* parent_,
27 JoystickConfig& joystick_config) :
28 parent(parent_),
29 m_joystick_config(joystick_config),
30 min_joybuttons(),
31 max_joybuttons(),
32 max_joyaxis(),
33 max_joyhats(),
34 hat_state(0),
35 wait_for_joystick(-1),
36 joysticks()
37{
38}
39
40JoystickManager::~JoystickManager()
41{
42 for (auto& joy : joysticks)
43 {
44 SDL_JoystickClose(joy);
45 }
46}
47
48void
49JoystickManager::on_joystick_added(int joystick_index)
50{
51 log_debug << "on_joystick_added(): " << joystick_index << std::endl;
52 SDL_Joystick* joystick = SDL_JoystickOpen(joystick_index);
53 if (!joystick)
54 {
55 log_warning << "failed to open joystick: " << joystick_index
56 << ": " << SDL_GetError() << std::endl;
57 }
58 else
59 {
60 joysticks.push_back(joystick);
61 }
62
63 if (min_joybuttons < 0 || SDL_JoystickNumButtons(joystick) < min_joybuttons)
64 min_joybuttons = SDL_JoystickNumButtons(joystick);
65
66 if (SDL_JoystickNumButtons(joystick) > max_joybuttons)
67 max_joybuttons = SDL_JoystickNumButtons(joystick);
68
69 if (SDL_JoystickNumAxes(joystick) > max_joyaxis)
70 max_joyaxis = SDL_JoystickNumAxes(joystick);
71
72 if (SDL_JoystickNumHats(joystick) > max_joyhats)
73 max_joyhats = SDL_JoystickNumHats(joystick);
74}
75
76void
77JoystickManager::on_joystick_removed(int instance_id)
78{
79 log_debug << "on_joystick_removed: " << static_cast<int>(instance_id) << std::endl;
80 for (auto& joy : joysticks)
81 {
82 SDL_JoystickID id = SDL_JoystickInstanceID(joy);
83 if (id == instance_id)
84 {
85 SDL_JoystickClose(joy);
86 joy = nullptr;
87 }
88 }
89
90 joysticks.erase(std::remove(joysticks.begin(), joysticks.end(), nullptr),
91 joysticks.end());
92}
93
94void
95JoystickManager::process_hat_event(const SDL_JoyHatEvent& jhat)
96{
97 Uint8 changed = hat_state ^ jhat.value;
98
99 if (wait_for_joystick >= 0)
100 {
101 if (changed & SDL_HAT_UP && jhat.value & SDL_HAT_UP)
102 m_joystick_config.bind_joyhat(jhat.which, SDL_HAT_UP, Control(wait_for_joystick));
103
104 if (changed & SDL_HAT_DOWN && jhat.value & SDL_HAT_DOWN)
105 m_joystick_config.bind_joyhat(jhat.which, SDL_HAT_DOWN, Control(wait_for_joystick));
106
107 if (changed & SDL_HAT_LEFT && jhat.value & SDL_HAT_LEFT)
108 m_joystick_config.bind_joyhat(jhat.which, SDL_HAT_LEFT, Control(wait_for_joystick));
109
110 if (changed & SDL_HAT_RIGHT && jhat.value & SDL_HAT_RIGHT)
111 m_joystick_config.bind_joyhat(jhat.which, SDL_HAT_RIGHT, Control(wait_for_joystick));
112
113 MenuManager::instance().refresh();
114 wait_for_joystick = -1;
115 }
116 else
117 {
118 if (changed & SDL_HAT_UP)
119 {
120 auto it = m_joystick_config.m_joy_hat_map.find(std::make_pair(jhat.which, SDL_HAT_UP));
121 if (it != m_joystick_config.m_joy_hat_map.end())
122 set_joy_controls(it->second, (jhat.value & SDL_HAT_UP) != 0);
123 }
124
125 if (changed & SDL_HAT_DOWN)
126 {
127 auto it = m_joystick_config.m_joy_hat_map.find(std::make_pair(jhat.which, SDL_HAT_DOWN));
128 if (it != m_joystick_config.m_joy_hat_map.end())
129 set_joy_controls(it->second, (jhat.value & SDL_HAT_DOWN) != 0);
130 }
131
132 if (changed & SDL_HAT_LEFT)
133 {
134 auto it = m_joystick_config.m_joy_hat_map.find(std::make_pair(jhat.which, SDL_HAT_LEFT));
135 if (it != m_joystick_config.m_joy_hat_map.end())
136 set_joy_controls(it->second, (jhat.value & SDL_HAT_LEFT) != 0);
137 }
138
139 if (changed & SDL_HAT_RIGHT)
140 {
141 auto it = m_joystick_config.m_joy_hat_map.find(std::make_pair(jhat.which, SDL_HAT_RIGHT));
142 if (it != m_joystick_config.m_joy_hat_map.end())
143 set_joy_controls(it->second, (jhat.value & SDL_HAT_RIGHT) != 0);
144 }
145 }
146
147 hat_state = jhat.value;
148}
149
150void
151JoystickManager::process_axis_event(const SDL_JoyAxisEvent& jaxis)
152{
153 if (wait_for_joystick >= 0)
154 {
155 if (abs(jaxis.value) > m_joystick_config.m_dead_zone) {
156 if (jaxis.value < 0)
157 m_joystick_config.bind_joyaxis(jaxis.which, -(jaxis.axis + 1), Control(wait_for_joystick));
158 else
159 m_joystick_config.bind_joyaxis(jaxis.which, jaxis.axis + 1, Control(wait_for_joystick));
160
161 MenuManager::instance().refresh();
162 wait_for_joystick = -1;
163 }
164 }
165 else
166 {
167 // Split the axis into left and right, so that both can be
168 // mapped separately (needed for jump/down vs up/down)
169 int axis = jaxis.axis + 1;
170
171 auto left = m_joystick_config.m_joy_axis_map.find(std::make_pair(jaxis.which, -axis));
172 auto right = m_joystick_config.m_joy_axis_map.find(std::make_pair(jaxis.which, axis));
173
174 if (left == m_joystick_config.m_joy_axis_map.end()) {
175 // std::cout << "Unmapped joyaxis " << (int)jaxis.axis << " moved" << std::endl;
176 } else {
177 if (jaxis.value < -m_joystick_config.m_dead_zone)
178 set_joy_controls(left->second, true);
179 else
180 set_joy_controls(left->second, false);
181 }
182
183 if (right == m_joystick_config.m_joy_axis_map.end()) {
184 // std::cout << "Unmapped joyaxis " << (int)jaxis.axis << " moved" << std::endl;
185 } else {
186 if (jaxis.value > m_joystick_config.m_dead_zone)
187 set_joy_controls(right->second, true);
188 else
189 set_joy_controls(right->second, false);
190 }
191 }
192}
193
194void
195JoystickManager::process_button_event(const SDL_JoyButtonEvent& jbutton)
196{
197 if (wait_for_joystick >= 0)
198 {
199 if (jbutton.state == SDL_PRESSED)
200 {
201 m_joystick_config.bind_joybutton(jbutton.which, jbutton.button, static_cast<Control>(wait_for_joystick));
202 MenuManager::instance().refresh();
203 parent->reset();
204 wait_for_joystick = -1;
205 }
206 }
207 else
208 {
209 auto i = m_joystick_config.m_joy_button_map.find(std::make_pair(jbutton.which, jbutton.button));
210 if (i == m_joystick_config.m_joy_button_map.end()) {
211 log_debug << "Unmapped joybutton " << static_cast<int>(jbutton.button) << " pressed" << std::endl;
212 } else {
213 set_joy_controls(i->second, (jbutton.state == SDL_PRESSED));
214 }
215 }
216}
217
218void
219JoystickManager::bind_next_event_to(Control id)
220{
221 wait_for_joystick = static_cast<int>(id);
222}
223
224void
225JoystickManager::set_joy_controls(Control id, bool value)
226{
227 if (m_joystick_config.m_jump_with_up_joy &&
228 id == Control::UP)
229 {
230 parent->get_controller().set_control(Control::JUMP, value);
231 }
232
233 parent->get_controller().set_control(id, value);
234}
235
236/* EOF */
237