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