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