src/platform/qt/InputController.cpp (view raw)
1/* Copyright (c) 2013-2014 Jeffrey Pfau
2 *
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6#include "InputController.h"
7
8#include "ConfigController.h"
9#include "GamepadAxisEvent.h"
10#include "GamepadButtonEvent.h"
11
12#include <QApplication>
13#include <QTimer>
14#include <QWidget>
15
16extern "C" {
17#include "util/configuration.h"
18}
19
20using namespace QGBA;
21
22int InputController::s_sdlInited = 0;
23GBASDLEvents InputController::s_sdlEvents;
24
25InputController::InputController(int playerId, QObject* parent)
26 : QObject(parent)
27 , m_playerId(playerId)
28 , m_config(nullptr)
29 , m_gamepadTimer(nullptr)
30 , m_playerAttached(false)
31 , m_allowOpposing(false)
32{
33 GBAInputMapInit(&m_inputMap);
34
35#ifdef BUILD_SDL
36 if (s_sdlInited == 0) {
37 GBASDLInitEvents(&s_sdlEvents);
38 }
39 ++s_sdlInited;
40 m_sdlPlayer.bindings = &m_inputMap;
41 GBASDLInitBindings(&m_inputMap);
42
43 m_gamepadTimer = new QTimer(this);
44 connect(m_gamepadTimer, SIGNAL(timeout()), this, SLOT(testGamepad()));
45 m_gamepadTimer->setInterval(50);
46 m_gamepadTimer->start();
47#endif
48
49 GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_X, GBA_KEY_A);
50 GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Z, GBA_KEY_B);
51 GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_A, GBA_KEY_L);
52 GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_S, GBA_KEY_R);
53 GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Return, GBA_KEY_START);
54 GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Backspace, GBA_KEY_SELECT);
55 GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Up, GBA_KEY_UP);
56 GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Down, GBA_KEY_DOWN);
57 GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Left, GBA_KEY_LEFT);
58 GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Right, GBA_KEY_RIGHT);
59}
60
61InputController::~InputController() {
62 GBAInputMapDeinit(&m_inputMap);
63
64#ifdef BUILD_SDL
65 --s_sdlInited;
66 if (s_sdlInited == 0) {
67 GBASDLDeinitEvents(&s_sdlEvents);
68 }
69#endif
70}
71
72void InputController::setConfiguration(ConfigController* config) {
73 m_config = config;
74 setAllowOpposing(config->getOption("allowOpposingDirections").toInt());
75 loadConfiguration(KEYBOARD);
76#ifdef BUILD_SDL
77 GBASDLEventsLoadConfig(&s_sdlEvents, config->input());
78 if (!m_playerAttached) {
79 GBASDLAttachPlayer(&s_sdlEvents, &m_sdlPlayer);
80 m_playerAttached = true;
81 }
82 loadConfiguration(SDL_BINDING_BUTTON);
83 loadProfile(SDL_BINDING_BUTTON, profileForType(SDL_BINDING_BUTTON));
84#endif
85}
86
87void InputController::loadConfiguration(uint32_t type) {
88 GBAInputMapLoad(&m_inputMap, type, m_config->input());
89}
90
91void InputController::loadProfile(uint32_t type, const QString& profile) {
92 GBAInputProfileLoad(&m_inputMap, type, m_config->input(), profile.toLocal8Bit().constData());
93}
94
95void InputController::saveConfiguration(uint32_t type) {
96 GBAInputMapSave(&m_inputMap, type, m_config->input());
97 m_config->write();
98}
99
100void InputController::saveProfile(uint32_t type, const QString& profile) {
101 GBAInputProfileSave(&m_inputMap, type, m_config->input(), profile.toLocal8Bit().constData());
102 m_config->write();
103}
104
105const char* InputController::profileForType(uint32_t type) {
106 UNUSED(type);
107#ifdef BUILD_SDL
108 if (type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) {
109#if SDL_VERSION_ATLEAST(2, 0, 0)
110 return SDL_JoystickName(m_sdlPlayer.joystick);
111#else
112 return SDL_JoystickName(SDL_JoystickIndex(m_sdlPlayer.joystick));
113#endif
114 }
115#endif
116 return 0;
117}
118
119#ifdef BUILD_SDL
120QStringList InputController::connectedGamepads(uint32_t type) const {
121 UNUSED(type);
122 if (type != SDL_BINDING_BUTTON) {
123 return QStringList();
124 }
125
126 QStringList pads;
127 for (size_t i = 0; i < s_sdlEvents.nJoysticks; ++i) {
128 const char* name;
129#if SDL_VERSION_ATLEAST(2, 0, 0)
130 name = SDL_JoystickName(s_sdlEvents.joysticks[i]);
131#else
132 name = SDL_JoystickName(SDL_JoystickIndex(s_sdlEvents.joysticks[i]));
133#endif
134 if (name) {
135 pads.append(QString(name));
136 } else {
137 pads.append(QString());
138 }
139 }
140 return pads;
141}
142
143void InputController::setPreferredGamepad(uint32_t type, const QString& device) {
144 if (!m_config) {
145 return;
146 }
147 GBAInputSetPreferredDevice(m_config->input(), type, m_sdlPlayer.playerId, device.toLocal8Bit().constData());
148}
149#endif
150
151GBAKey InputController::mapKeyboard(int key) const {
152 return GBAInputMapKey(&m_inputMap, KEYBOARD, key);
153}
154
155void InputController::bindKey(uint32_t type, int key, GBAKey gbaKey) {
156 return GBAInputBindKey(&m_inputMap, type, key, gbaKey);
157}
158
159#ifdef BUILD_SDL
160int InputController::testSDLEvents() {
161 SDL_Joystick* joystick = m_sdlPlayer.joystick;
162 SDL_JoystickUpdate();
163 int numButtons = SDL_JoystickNumButtons(joystick);
164 int activeButtons = 0;
165 int i;
166 for (i = 0; i < numButtons; ++i) {
167 GBAKey key = GBAInputMapKey(&m_inputMap, SDL_BINDING_BUTTON, i);
168 if (key == GBA_KEY_NONE) {
169 continue;
170 }
171 if (hasPendingEvent(key)) {
172 continue;
173 }
174 if (SDL_JoystickGetButton(joystick, i)) {
175 activeButtons |= 1 << key;
176 }
177 }
178 int numHats = SDL_JoystickNumHats(joystick);
179 for (i = 0; i < numHats; ++i) {
180 int hat = SDL_JoystickGetHat(joystick, i);
181 if (hat & SDL_HAT_UP) {
182 activeButtons |= 1 << GBA_KEY_UP;
183 }
184 if (hat & SDL_HAT_LEFT) {
185 activeButtons |= 1 << GBA_KEY_LEFT;
186 }
187 if (hat & SDL_HAT_DOWN) {
188 activeButtons |= 1 << GBA_KEY_DOWN;
189 }
190 if (hat & SDL_HAT_RIGHT) {
191 activeButtons |= 1 << GBA_KEY_RIGHT;
192 }
193 }
194
195 int numAxes = SDL_JoystickNumAxes(joystick);
196 for (i = 0; i < numAxes; ++i) {
197 int value = SDL_JoystickGetAxis(joystick, i);
198
199 enum GBAKey key = GBAInputMapAxis(&m_inputMap, SDL_BINDING_BUTTON, i, value);
200 if (key != GBA_KEY_NONE) {
201 activeButtons |= 1 << key;
202 }
203 }
204 return activeButtons;
205}
206
207QSet<int> InputController::activeGamepadButtons() {
208 SDL_Joystick* joystick = m_sdlPlayer.joystick;
209 SDL_JoystickUpdate();
210 int numButtons = SDL_JoystickNumButtons(joystick);
211 QSet<int> activeButtons;
212 int i;
213 for (i = 0; i < numButtons; ++i) {
214 if (SDL_JoystickGetButton(joystick, i)) {
215 activeButtons.insert(i);
216 }
217 }
218 return activeButtons;
219}
220
221QSet<QPair<int, GamepadAxisEvent::Direction>> InputController::activeGamepadAxes() {
222 SDL_Joystick* joystick = m_sdlPlayer.joystick;
223 SDL_JoystickUpdate();
224 int numButtons = SDL_JoystickNumAxes(joystick);
225 QSet<QPair<int, GamepadAxisEvent::Direction>> activeAxes;
226 int i;
227 for (i = 0; i < numButtons; ++i) {
228 int32_t axis = SDL_JoystickGetAxis(joystick, i);
229 if (axis >= AXIS_THRESHOLD || axis <= -AXIS_THRESHOLD) {
230 activeAxes.insert(qMakePair(i, axis > 0 ? GamepadAxisEvent::POSITIVE : GamepadAxisEvent::NEGATIVE));
231 }
232 }
233 return activeAxes;
234}
235
236void InputController::bindAxis(uint32_t type, int axis, GamepadAxisEvent::Direction direction, GBAKey key) {
237 const GBAAxis* old = GBAInputQueryAxis(&m_inputMap, SDL_BINDING_BUTTON, axis);
238 GBAAxis description = { GBA_KEY_NONE, GBA_KEY_NONE, -AXIS_THRESHOLD, AXIS_THRESHOLD };
239 if (old) {
240 description = *old;
241 }
242 switch (direction) {
243 case GamepadAxisEvent::NEGATIVE:
244 description.lowDirection = key;
245 description.deadLow = -AXIS_THRESHOLD;
246 break;
247 case GamepadAxisEvent::POSITIVE:
248 description.highDirection = key;
249 description.deadHigh = AXIS_THRESHOLD;
250 break;
251 default:
252 return;
253 }
254 GBAInputBindAxis(&m_inputMap, SDL_BINDING_BUTTON, axis, &description);
255}
256#endif
257
258void InputController::testGamepad() {
259#ifdef BUILD_SDL
260 auto activeAxes = activeGamepadAxes();
261 auto oldAxes = m_activeAxes;
262 m_activeAxes = activeAxes;
263
264 auto activeButtons = activeGamepadButtons();
265 auto oldButtons = m_activeButtons;
266 m_activeButtons = activeButtons;
267
268 if (!QApplication::focusWidget()) {
269 return;
270 }
271
272 activeAxes.subtract(oldAxes);
273 oldAxes.subtract(m_activeAxes);
274
275 for (auto& axis : m_activeAxes) {
276 bool newlyAboveThreshold = activeAxes.contains(axis);
277 GamepadAxisEvent* event = new GamepadAxisEvent(axis.first, axis.second, newlyAboveThreshold, this);
278 if (newlyAboveThreshold) {
279 postPendingEvent(event->gbaKey());
280 if (!event->isAccepted()) {
281 clearPendingEvent(event->gbaKey());
282 }
283 } else if (oldAxes.contains(axis)) {
284 clearPendingEvent(event->gbaKey());
285 }
286 QApplication::sendEvent(QApplication::focusWidget(), event);
287 }
288
289 activeButtons.subtract(oldButtons);
290 oldButtons.subtract(m_activeButtons);
291
292 for (int button : activeButtons) {
293 GamepadButtonEvent* event = new GamepadButtonEvent(GamepadButtonEvent::Down(), button, this);
294 postPendingEvent(event->gbaKey());
295 QApplication::sendEvent(QApplication::focusWidget(), event);
296 if (!event->isAccepted()) {
297 clearPendingEvent(event->gbaKey());
298 }
299 }
300 for (int button : oldButtons) {
301 GamepadButtonEvent* event = new GamepadButtonEvent(GamepadButtonEvent::Up(), button, this);
302 clearPendingEvent(event->gbaKey());
303 QApplication::sendEvent(QApplication::focusWidget(), event);
304 }
305#endif
306}
307
308void InputController::postPendingEvent(GBAKey key) {
309 m_pendingEvents.insert(key);
310}
311
312void InputController::clearPendingEvent(GBAKey key) {
313 m_pendingEvents.remove(key);
314}
315
316bool InputController::hasPendingEvent(GBAKey key) const {
317 return m_pendingEvents.contains(key);
318}