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
22#ifdef BUILD_SDL
23int InputController::s_sdlInited = 0;
24GBASDLEvents InputController::s_sdlEvents;
25#endif
26
27InputController::InputController(int playerId, QObject* parent)
28 : QObject(parent)
29 , m_playerId(playerId)
30 , m_config(nullptr)
31 , m_gamepadTimer(nullptr)
32#ifdef BUILD_SDL
33 , m_playerAttached(false)
34#endif
35 , m_allowOpposing(false)
36{
37 GBAInputMapInit(&m_inputMap);
38
39#ifdef BUILD_SDL
40 if (s_sdlInited == 0) {
41 GBASDLInitEvents(&s_sdlEvents);
42 }
43 ++s_sdlInited;
44 m_sdlPlayer.bindings = &m_inputMap;
45 GBASDLInitBindings(&m_inputMap);
46#endif
47
48 m_gamepadTimer = new QTimer(this);
49#ifdef BUILD_SDL
50 connect(m_gamepadTimer, &QTimer::timeout, [this]() {
51 testGamepad(SDL_BINDING_BUTTON);
52 });
53#endif
54 m_gamepadTimer->setInterval(50);
55 m_gamepadTimer->start();
56
57 GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_X, GBA_KEY_A);
58 GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Z, GBA_KEY_B);
59 GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_A, GBA_KEY_L);
60 GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_S, GBA_KEY_R);
61 GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Return, GBA_KEY_START);
62 GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Backspace, GBA_KEY_SELECT);
63 GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Up, GBA_KEY_UP);
64 GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Down, GBA_KEY_DOWN);
65 GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Left, GBA_KEY_LEFT);
66 GBAInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Right, GBA_KEY_RIGHT);
67}
68
69InputController::~InputController() {
70 GBAInputMapDeinit(&m_inputMap);
71
72#ifdef BUILD_SDL
73 if (m_playerAttached) {
74 GBASDLDetachPlayer(&s_sdlEvents, &m_sdlPlayer);
75 }
76
77 --s_sdlInited;
78 if (s_sdlInited == 0) {
79 GBASDLDeinitEvents(&s_sdlEvents);
80 }
81#endif
82}
83
84void InputController::setConfiguration(ConfigController* config) {
85 m_config = config;
86 setAllowOpposing(config->getOption("allowOpposingDirections").toInt());
87 loadConfiguration(KEYBOARD);
88#ifdef BUILD_SDL
89 GBASDLEventsLoadConfig(&s_sdlEvents, config->input());
90 if (!m_playerAttached) {
91 GBASDLAttachPlayer(&s_sdlEvents, &m_sdlPlayer);
92 m_playerAttached = true;
93 }
94 loadConfiguration(SDL_BINDING_BUTTON);
95 loadProfile(SDL_BINDING_BUTTON, profileForType(SDL_BINDING_BUTTON));
96#endif
97}
98
99void InputController::loadConfiguration(uint32_t type) {
100 GBAInputMapLoad(&m_inputMap, type, m_config->input());
101#ifdef BUILD_SDL
102 if (m_playerAttached) {
103 GBASDLPlayerLoadConfig(&m_sdlPlayer, m_config->input());
104 }
105#endif
106}
107
108void InputController::loadProfile(uint32_t type, const QString& profile) {
109 GBAInputProfileLoad(&m_inputMap, type, m_config->input(), profile.toUtf8().constData());
110 recalibrateAxes();
111 emit profileLoaded(profile);
112}
113
114void InputController::saveConfiguration() {
115 saveConfiguration(KEYBOARD);
116#ifdef BUILD_SDL
117 saveConfiguration(SDL_BINDING_BUTTON);
118 saveProfile(SDL_BINDING_BUTTON, profileForType(SDL_BINDING_BUTTON));
119 if (m_playerAttached) {
120 GBASDLPlayerSaveConfig(&m_sdlPlayer, m_config->input());
121 }
122 m_config->write();
123#endif
124}
125
126void InputController::saveConfiguration(uint32_t type) {
127 GBAInputMapSave(&m_inputMap, type, m_config->input());
128 m_config->write();
129}
130
131void InputController::saveProfile(uint32_t type, const QString& profile) {
132 GBAInputProfileSave(&m_inputMap, type, m_config->input(), profile.toUtf8().constData());
133 m_config->write();
134}
135
136const char* InputController::profileForType(uint32_t type) {
137 UNUSED(type);
138#ifdef BUILD_SDL
139 if (type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) {
140#if SDL_VERSION_ATLEAST(2, 0, 0)
141 return SDL_JoystickName(m_sdlPlayer.joystick);
142#else
143 return SDL_JoystickName(SDL_JoystickIndex(m_sdlPlayer.joystick));
144#endif
145 }
146#endif
147 return 0;
148}
149
150QStringList InputController::connectedGamepads(uint32_t type) const {
151 UNUSED(type);
152
153#ifdef BUILD_SDL
154 if (type == SDL_BINDING_BUTTON) {
155 QStringList pads;
156 for (size_t i = 0; i < s_sdlEvents.nJoysticks; ++i) {
157 const char* name;
158#if SDL_VERSION_ATLEAST(2, 0, 0)
159 name = SDL_JoystickName(s_sdlEvents.joysticks[i]);
160#else
161 name = SDL_JoystickName(SDL_JoystickIndex(s_sdlEvents.joysticks[i]));
162#endif
163 if (name) {
164 pads.append(QString(name));
165 } else {
166 pads.append(QString());
167 }
168 }
169 return pads;
170 }
171#endif
172
173 return QStringList();
174}
175
176int InputController::gamepad(uint32_t type) const {
177#ifdef BUILD_SDL
178 if (type == SDL_BINDING_BUTTON) {
179 return m_sdlPlayer.joystickIndex;
180 }
181#endif
182 return 0;
183}
184
185void InputController::setGamepad(uint32_t type, int index) {
186#ifdef BUILD_SDL
187 if (type == SDL_BINDING_BUTTON) {
188 GBASDLPlayerChangeJoystick(&s_sdlEvents, &m_sdlPlayer, index);
189 }
190#endif
191}
192
193void InputController::setPreferredGamepad(uint32_t type, const QString& device) {
194 if (!m_config) {
195 return;
196 }
197 GBAInputSetPreferredDevice(m_config->input(), type, m_playerId, device.toUtf8().constData());
198}
199
200GBARumble* InputController::rumble() {
201#ifdef BUILD_SDL
202 if (m_playerAttached) {
203 return &m_sdlPlayer.rumble.d;
204 }
205#endif
206 return nullptr;
207}
208
209GBARotationSource* InputController::rotationSource() {
210#ifdef BUILD_SDL
211 if (m_playerAttached) {
212 return &m_sdlPlayer.rotation.d;
213 }
214#endif
215 return nullptr;
216}
217
218void InputController::registerTiltAxisX(int axis) {
219#ifdef BUILD_SDL
220 if (m_playerAttached) {
221 m_sdlPlayer.rotation.axisX = axis;
222 }
223#endif
224}
225
226void InputController::registerTiltAxisY(int axis) {
227#ifdef BUILD_SDL
228 if (m_playerAttached) {
229 m_sdlPlayer.rotation.axisY = axis;
230 }
231#endif
232}
233
234void InputController::registerGyroAxisX(int axis) {
235#ifdef BUILD_SDL
236 if (m_playerAttached) {
237 m_sdlPlayer.rotation.gyroX = axis;
238 }
239#endif
240}
241
242void InputController::registerGyroAxisY(int axis) {
243#ifdef BUILD_SDL
244 if (m_playerAttached) {
245 m_sdlPlayer.rotation.gyroY = axis;
246 }
247#endif
248}
249
250float InputController::gyroSensitivity() const {
251#ifdef BUILD_SDL
252 if (m_playerAttached) {
253 return m_sdlPlayer.rotation.gyroSensitivity;
254 }
255#endif
256 return 0;
257}
258
259void InputController::setGyroSensitivity(float sensitivity) {
260#ifdef BUILD_SDL
261 if (m_playerAttached) {
262 m_sdlPlayer.rotation.gyroSensitivity = sensitivity;
263 }
264#endif
265}
266
267GBAKey InputController::mapKeyboard(int key) const {
268 return GBAInputMapKey(&m_inputMap, KEYBOARD, key);
269}
270
271void InputController::bindKey(uint32_t type, int key, GBAKey gbaKey) {
272 return GBAInputBindKey(&m_inputMap, type, key, gbaKey);
273}
274
275int InputController::pollEvents() {
276 int activeButtons = 0;
277#ifdef BUILD_SDL
278 if (m_playerAttached) {
279 SDL_Joystick* joystick = m_sdlPlayer.joystick;
280 SDL_JoystickUpdate();
281 int numButtons = SDL_JoystickNumButtons(joystick);
282 int i;
283 for (i = 0; i < numButtons; ++i) {
284 GBAKey key = GBAInputMapKey(&m_inputMap, SDL_BINDING_BUTTON, i);
285 if (key == GBA_KEY_NONE) {
286 continue;
287 }
288 if (hasPendingEvent(key)) {
289 continue;
290 }
291 if (SDL_JoystickGetButton(joystick, i)) {
292 activeButtons |= 1 << key;
293 }
294 }
295 int numHats = SDL_JoystickNumHats(joystick);
296 for (i = 0; i < numHats; ++i) {
297 int hat = SDL_JoystickGetHat(joystick, i);
298 if (hat & SDL_HAT_UP) {
299 activeButtons |= 1 << GBA_KEY_UP;
300 }
301 if (hat & SDL_HAT_LEFT) {
302 activeButtons |= 1 << GBA_KEY_LEFT;
303 }
304 if (hat & SDL_HAT_DOWN) {
305 activeButtons |= 1 << GBA_KEY_DOWN;
306 }
307 if (hat & SDL_HAT_RIGHT) {
308 activeButtons |= 1 << GBA_KEY_RIGHT;
309 }
310 }
311
312 int numAxes = SDL_JoystickNumAxes(joystick);
313 for (i = 0; i < numAxes; ++i) {
314 int value = SDL_JoystickGetAxis(joystick, i);
315
316 enum GBAKey key = GBAInputMapAxis(&m_inputMap, SDL_BINDING_BUTTON, i, value);
317 if (key != GBA_KEY_NONE) {
318 activeButtons |= 1 << key;
319 }
320 }
321 }
322#endif
323 return activeButtons;
324}
325
326QSet<int> InputController::activeGamepadButtons(int type) {
327 QSet<int> activeButtons;
328#ifdef BUILD_SDL
329 if (m_playerAttached && type == SDL_BINDING_BUTTON) {
330 SDL_Joystick* joystick = m_sdlPlayer.joystick;
331 SDL_JoystickUpdate();
332 int numButtons = SDL_JoystickNumButtons(joystick);
333 int i;
334 for (i = 0; i < numButtons; ++i) {
335 if (SDL_JoystickGetButton(joystick, i)) {
336 activeButtons.insert(i);
337 }
338 }
339 }
340#endif
341 return activeButtons;
342}
343
344void InputController::recalibrateAxes() {
345#ifdef BUILD_SDL
346 if (m_playerAttached) {
347 SDL_Joystick* joystick = m_sdlPlayer.joystick;
348 SDL_JoystickUpdate();
349 int numAxes = SDL_JoystickNumAxes(joystick);
350 if (numAxes < 1) {
351 return;
352 }
353 m_deadzones.resize(numAxes);
354 int i;
355 for (i = 0; i < numAxes; ++i) {
356 m_deadzones[i] = SDL_JoystickGetAxis(joystick, i);
357 }
358 }
359#endif
360}
361
362QSet<QPair<int, GamepadAxisEvent::Direction>> InputController::activeGamepadAxes(int type) {
363 QSet<QPair<int, GamepadAxisEvent::Direction>> activeAxes;
364#ifdef BUILD_SDL
365 if (m_playerAttached && type == SDL_BINDING_BUTTON) {
366 SDL_Joystick* joystick = m_sdlPlayer.joystick;
367 SDL_JoystickUpdate();
368 int numAxes = SDL_JoystickNumAxes(joystick);
369 if (numAxes < 1) {
370 return activeAxes;
371 }
372 m_deadzones.resize(numAxes);
373 int i;
374 for (i = 0; i < numAxes; ++i) {
375 int32_t axis = SDL_JoystickGetAxis(joystick, i);
376 axis -= m_deadzones[i];
377 if (axis >= AXIS_THRESHOLD || axis <= -AXIS_THRESHOLD) {
378 activeAxes.insert(qMakePair(i, axis > 0 ? GamepadAxisEvent::POSITIVE : GamepadAxisEvent::NEGATIVE));
379 }
380 }
381 }
382#endif
383 return activeAxes;
384}
385
386void InputController::bindAxis(uint32_t type, int axis, GamepadAxisEvent::Direction direction, GBAKey key) {
387 const GBAAxis* old = GBAInputQueryAxis(&m_inputMap, type, axis);
388 GBAAxis description = { GBA_KEY_NONE, GBA_KEY_NONE, -AXIS_THRESHOLD, AXIS_THRESHOLD };
389 if (old) {
390 description = *old;
391 }
392 switch (direction) {
393 case GamepadAxisEvent::NEGATIVE:
394 description.lowDirection = key;
395 description.deadLow = m_deadzones[axis] - AXIS_THRESHOLD;
396 break;
397 case GamepadAxisEvent::POSITIVE:
398 description.highDirection = key;
399 description.deadHigh = m_deadzones[axis] + AXIS_THRESHOLD;
400 break;
401 default:
402 return;
403 }
404 GBAInputBindAxis(&m_inputMap, type, axis, &description);
405}
406
407void InputController::testGamepad(int type) {
408 auto activeAxes = activeGamepadAxes(type);
409 auto oldAxes = m_activeAxes;
410 m_activeAxes = activeAxes;
411
412 auto activeButtons = activeGamepadButtons(type);
413 auto oldButtons = m_activeButtons;
414 m_activeButtons = activeButtons;
415
416 if (!QApplication::focusWidget()) {
417 return;
418 }
419
420 activeAxes.subtract(oldAxes);
421 oldAxes.subtract(m_activeAxes);
422
423 for (auto& axis : m_activeAxes) {
424 bool newlyAboveThreshold = activeAxes.contains(axis);
425 if (newlyAboveThreshold) {
426 GamepadAxisEvent* event = new GamepadAxisEvent(axis.first, axis.second, newlyAboveThreshold, type, this);
427 postPendingEvent(event->gbaKey());
428 QApplication::sendEvent(QApplication::focusWidget(), event);
429 if (!event->isAccepted()) {
430 clearPendingEvent(event->gbaKey());
431 }
432 }
433 }
434 for (auto axis : oldAxes) {
435 GamepadAxisEvent* event = new GamepadAxisEvent(axis.first, axis.second, false, type, this);
436 clearPendingEvent(event->gbaKey());
437 QApplication::sendEvent(QApplication::focusWidget(), event);
438 }
439
440 if (!QApplication::focusWidget()) {
441 return;
442 }
443
444 activeButtons.subtract(oldButtons);
445 oldButtons.subtract(m_activeButtons);
446
447 for (int button : activeButtons) {
448 GamepadButtonEvent* event = new GamepadButtonEvent(GamepadButtonEvent::Down(), button, type, this);
449 postPendingEvent(event->gbaKey());
450 QApplication::sendEvent(QApplication::focusWidget(), event);
451 if (!event->isAccepted()) {
452 clearPendingEvent(event->gbaKey());
453 }
454 }
455 for (int button : oldButtons) {
456 GamepadButtonEvent* event = new GamepadButtonEvent(GamepadButtonEvent::Up(), button, type, this);
457 clearPendingEvent(event->gbaKey());
458 QApplication::sendEvent(QApplication::focusWidget(), event);
459 }
460}
461
462void InputController::postPendingEvent(GBAKey key) {
463 m_pendingEvents.insert(key);
464}
465
466void InputController::clearPendingEvent(GBAKey key) {
467 m_pendingEvents.remove(key);
468}
469
470bool InputController::hasPendingEvent(GBAKey key) const {
471 return m_pendingEvents.contains(key);
472}
473
474#if defined(BUILD_SDL) && SDL_VERSION_ATLEAST(2, 0, 0)
475void InputController::suspendScreensaver() {
476 GBASDLSuspendScreensaver(&s_sdlEvents);
477}
478
479void InputController::resumeScreensaver() {
480 GBASDLResumeScreensaver(&s_sdlEvents);
481}
482
483void InputController::setScreensaverSuspendable(bool suspendable) {
484 GBASDLSetScreensaverSuspendable(&s_sdlEvents, suspendable);
485}
486#endif