Replaced Ref and RefCounter with std::shared_ptr<>
[supertux.git] / src / control / joystick_manager.cpp
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 <iostream>
20 #include <algorithm>
21
22 #include "control/input_manager.hpp"
23 #include "control/joystick_config.hpp"
24 #include "gui/menu_manager.hpp"
25 #include "lisp/list_iterator.hpp"
26 #include "supertux/menu/joystick_menu.hpp"
27 #include "util/gettext.hpp"
28 #include "util/log.hpp"
29 #include "util/writer.hpp"
30
31 JoystickManager::JoystickManager(InputManager* parent_,
32                                  JoystickConfig& joystick_config) :
33   parent(parent_),
34   m_joystick_config(joystick_config),
35   min_joybuttons(),
36   max_joybuttons(),
37   max_joyaxis(),
38   max_joyhats(),
39   hat_state(0),
40   wait_for_joystick(-1),
41   joysticks()
42 {
43 }
44
45 JoystickManager::~JoystickManager()
46 {
47   for(auto joy : joysticks)
48   {
49     SDL_JoystickClose(joy);
50   }
51 }
52
53 void
54 JoystickManager::on_joystick_added(int joystick_index)
55 {
56   log_debug << "on_joystick_added(): " << joystick_index << std::endl;
57   SDL_Joystick* joystick = SDL_JoystickOpen(joystick_index);
58   if (!joystick)
59   {
60     log_warning << "failed to open joystick: " << joystick_index
61                 << ": " << SDL_GetError() << std::endl;
62   }
63   else
64   {
65     joysticks.push_back(joystick);
66   }
67
68   if(min_joybuttons < 0 || SDL_JoystickNumButtons(joystick) < min_joybuttons)
69     min_joybuttons = SDL_JoystickNumButtons(joystick);
70
71   if(SDL_JoystickNumButtons(joystick) > max_joybuttons)
72     max_joybuttons = SDL_JoystickNumButtons(joystick);
73
74   if(SDL_JoystickNumAxes(joystick) > max_joyaxis)
75     max_joyaxis = SDL_JoystickNumAxes(joystick);
76
77   if(SDL_JoystickNumHats(joystick) > max_joyhats)
78     max_joyhats = SDL_JoystickNumHats(joystick);
79 }
80
81 void
82 JoystickManager::on_joystick_removed(int instance_id)
83 {
84   log_debug << "on_joystick_removed: " << static_cast<int>(instance_id) << std::endl;
85   for(auto& joy : joysticks)
86   {
87     SDL_JoystickID id = SDL_JoystickInstanceID(joy);
88     if (id == instance_id)
89     {
90       SDL_JoystickClose(joy);
91       joy = nullptr;
92     }
93   }
94
95   joysticks.erase(std::remove(joysticks.begin(), joysticks.end(), nullptr),
96                   joysticks.end());
97 }
98
99 void
100 JoystickManager::process_hat_event(const SDL_JoyHatEvent& jhat)
101 {
102   Uint8 changed = hat_state ^ jhat.value;
103
104   if (wait_for_joystick >= 0)
105   {
106     if (changed & SDL_HAT_UP && jhat.value & SDL_HAT_UP)
107       m_joystick_config.bind_joyhat(jhat.which, SDL_HAT_UP, Controller::Control(wait_for_joystick));
108
109     if (changed & SDL_HAT_DOWN && jhat.value & SDL_HAT_DOWN)
110       m_joystick_config.bind_joyhat(jhat.which, SDL_HAT_DOWN, Controller::Control(wait_for_joystick));
111
112     if (changed & SDL_HAT_LEFT && jhat.value & SDL_HAT_LEFT)
113       m_joystick_config.bind_joyhat(jhat.which, SDL_HAT_LEFT, Controller::Control(wait_for_joystick));
114
115     if (changed & SDL_HAT_RIGHT && jhat.value & SDL_HAT_RIGHT)
116       m_joystick_config.bind_joyhat(jhat.which, SDL_HAT_RIGHT, Controller::Control(wait_for_joystick));
117
118     MenuManager::instance().refresh();
119     wait_for_joystick = -1;
120   }
121   else
122   {
123     if (changed & SDL_HAT_UP)
124     {
125       JoystickConfig::HatMap::iterator it = m_joystick_config.joy_hat_map.find(std::make_pair(jhat.which, SDL_HAT_UP));
126       if (it != m_joystick_config.joy_hat_map.end())
127         set_joy_controls(it->second, jhat.value & SDL_HAT_UP);
128     }
129
130     if (changed & SDL_HAT_DOWN)
131     {
132       JoystickConfig::HatMap::iterator it = m_joystick_config.joy_hat_map.find(std::make_pair(jhat.which, SDL_HAT_DOWN));
133       if (it != m_joystick_config.joy_hat_map.end())
134         set_joy_controls(it->second, jhat.value & SDL_HAT_DOWN);
135     }
136
137     if (changed & SDL_HAT_LEFT)
138     {
139       JoystickConfig::HatMap::iterator it = m_joystick_config.joy_hat_map.find(std::make_pair(jhat.which, SDL_HAT_LEFT));
140       if (it != m_joystick_config.joy_hat_map.end())
141         set_joy_controls(it->second, jhat.value & SDL_HAT_LEFT);
142     }
143
144     if (changed & SDL_HAT_RIGHT)
145     {
146       JoystickConfig::HatMap::iterator it = m_joystick_config.joy_hat_map.find(std::make_pair(jhat.which, SDL_HAT_RIGHT));
147       if (it != m_joystick_config.joy_hat_map.end())
148         set_joy_controls(it->second, jhat.value & SDL_HAT_RIGHT);
149     }
150   }
151
152   hat_state = jhat.value;
153 }
154
155 void
156 JoystickManager::process_axis_event(const SDL_JoyAxisEvent& jaxis)
157 {
158   if (wait_for_joystick >= 0)
159   {
160     if (abs(jaxis.value) > m_joystick_config.dead_zone) {
161       if (jaxis.value < 0)
162         m_joystick_config.bind_joyaxis(jaxis.which, -(jaxis.axis + 1), Controller::Control(wait_for_joystick));
163       else
164         m_joystick_config.bind_joyaxis(jaxis.which, jaxis.axis + 1, Controller::Control(wait_for_joystick));
165
166       MenuManager::instance().refresh();
167       wait_for_joystick = -1;
168     }
169   }
170   else
171   {
172     // Split the axis into left and right, so that both can be
173     // mapped separately (needed for jump/down vs up/down)
174     int axis = jaxis.axis + 1;
175
176     auto left = m_joystick_config.joy_axis_map.find(std::make_pair(jaxis.which, -axis));
177     auto right = m_joystick_config.joy_axis_map.find(std::make_pair(jaxis.which, axis));
178
179     if(left == m_joystick_config.joy_axis_map.end()) {
180       // std::cout << "Unmapped joyaxis " << (int)jaxis.axis << " moved" << std::endl;
181     } else {
182       if (jaxis.value < -m_joystick_config.dead_zone)
183         set_joy_controls(left->second,  true);
184       else
185         set_joy_controls(left->second, false);
186     }
187
188     if(right == m_joystick_config.joy_axis_map.end()) {
189       // std::cout << "Unmapped joyaxis " << (int)jaxis.axis << " moved" << std::endl;
190     } else {
191       if (jaxis.value > m_joystick_config.dead_zone)
192         set_joy_controls(right->second, true);
193       else
194         set_joy_controls(right->second, false);
195     }
196   }
197 }
198
199 void
200 JoystickManager::process_button_event(const SDL_JoyButtonEvent& jbutton)
201 {
202   if(wait_for_joystick >= 0)
203   {
204     if(jbutton.state == SDL_PRESSED)
205     {
206       m_joystick_config.bind_joybutton(jbutton.which, jbutton.button, (Controller::Control)wait_for_joystick);
207       MenuManager::instance().refresh();
208       parent->reset();
209       wait_for_joystick = -1;
210     }
211   }
212   else
213   {
214     auto i = m_joystick_config.joy_button_map.find(std::make_pair(jbutton.which, jbutton.button));
215     if(i == m_joystick_config.joy_button_map.end()) {
216       log_debug << "Unmapped joybutton " << (int)jbutton.button << " pressed" << std::endl;
217     } else {
218       set_joy_controls(i->second, (jbutton.state == SDL_PRESSED));
219     }
220   }
221 }
222
223 void
224 JoystickManager::bind_next_event_to(Controller::Control id)
225 {
226   wait_for_joystick = id;
227 }
228
229 void
230 JoystickManager::set_joy_controls(Controller::Control id, bool value)
231 {
232   if (m_joystick_config.jump_with_up_joy &&
233       id == Controller::UP)
234   {
235     parent->get_controller()->set_control(Controller::JUMP, value);
236   }
237
238   parent->get_controller()->set_control(id, value);
239 }
240
241 /* EOF */