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
17#include <mgba/core/interface.h>
18#include <mgba-util/configuration.h>
19
20using namespace QGBA;
21
22#ifdef BUILD_SDL
23int InputController::s_sdlInited = 0;
24mSDLEvents InputController::s_sdlEvents;
25#endif
26
27InputController::InputController(int playerId, QWidget* topLevel, QObject* parent)
28 : QObject(parent)
29 , m_playerId(playerId)
30 , m_topLevel(topLevel)
31 , m_focusParent(topLevel)
32{
33 mInputMapInit(&m_inputMap, &GBAInputInfo);
34
35#ifdef BUILD_SDL
36 if (s_sdlInited == 0) {
37 mSDLInitEvents(&s_sdlEvents);
38 }
39 ++s_sdlInited;
40 m_sdlPlayer.bindings = &m_inputMap;
41 mSDLInitBindingsGBA(&m_inputMap);
42 updateJoysticks();
43#endif
44
45#ifdef BUILD_SDL
46 connect(&m_gamepadTimer, &QTimer::timeout, [this]() {
47 testGamepad(SDL_BINDING_BUTTON);
48 if (m_playerId == 0) {
49 updateJoysticks();
50 }
51 });
52#endif
53 m_gamepadTimer.setInterval(50);
54 m_gamepadTimer.start();
55
56 mInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_X, GBA_KEY_A);
57 mInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Z, GBA_KEY_B);
58 mInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_A, GBA_KEY_L);
59 mInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_S, GBA_KEY_R);
60 mInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Return, GBA_KEY_START);
61 mInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Backspace, GBA_KEY_SELECT);
62 mInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Up, GBA_KEY_UP);
63 mInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Down, GBA_KEY_DOWN);
64 mInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Left, GBA_KEY_LEFT);
65 mInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Right, GBA_KEY_RIGHT);
66
67
68#ifdef M_CORE_GBA
69 m_lux.p = this;
70 m_lux.sample = [](GBALuminanceSource* context) {
71 InputControllerLux* lux = static_cast<InputControllerLux*>(context);
72 lux->value = 0xFF - lux->p->m_luxValue;
73 };
74
75 m_lux.readLuminance = [](GBALuminanceSource* context) {
76 InputControllerLux* lux = static_cast<InputControllerLux*>(context);
77 return lux->value;
78 };
79 setLuminanceLevel(0);
80#endif
81}
82
83InputController::~InputController() {
84 mInputMapDeinit(&m_inputMap);
85
86#ifdef BUILD_SDL
87 if (m_playerAttached) {
88 mSDLDetachPlayer(&s_sdlEvents, &m_sdlPlayer);
89 }
90
91 --s_sdlInited;
92 if (s_sdlInited == 0) {
93 mSDLDeinitEvents(&s_sdlEvents);
94 }
95#endif
96}
97
98void InputController::setConfiguration(ConfigController* config) {
99 m_config = config;
100 loadConfiguration(KEYBOARD);
101#ifdef BUILD_SDL
102 mSDLEventsLoadConfig(&s_sdlEvents, config->input());
103 if (!m_playerAttached) {
104 m_playerAttached = mSDLAttachPlayer(&s_sdlEvents, &m_sdlPlayer);
105 }
106 loadConfiguration(SDL_BINDING_BUTTON);
107 loadProfile(SDL_BINDING_BUTTON, profileForType(SDL_BINDING_BUTTON));
108#endif
109}
110
111void InputController::loadConfiguration(uint32_t type) {
112 mInputMapLoad(&m_inputMap, type, m_config->input());
113#ifdef BUILD_SDL
114 if (m_playerAttached) {
115 mSDLPlayerLoadConfig(&m_sdlPlayer, m_config->input());
116 }
117#endif
118}
119
120void InputController::loadProfile(uint32_t type, const QString& profile) {
121 bool loaded = mInputProfileLoad(&m_inputMap, type, m_config->input(), profile.toUtf8().constData());
122 recalibrateAxes();
123 if (!loaded) {
124 const InputProfile* ip = InputProfile::findProfile(profile);
125 if (ip) {
126 ip->apply(this);
127 }
128 }
129 emit profileLoaded(profile);
130}
131
132void InputController::saveConfiguration() {
133 saveConfiguration(KEYBOARD);
134#ifdef BUILD_SDL
135 saveConfiguration(SDL_BINDING_BUTTON);
136 saveProfile(SDL_BINDING_BUTTON, profileForType(SDL_BINDING_BUTTON));
137 if (m_playerAttached) {
138 mSDLPlayerSaveConfig(&m_sdlPlayer, m_config->input());
139 }
140#endif
141 m_config->write();
142}
143
144void InputController::saveConfiguration(uint32_t type) {
145 mInputMapSave(&m_inputMap, type, m_config->input());
146 m_config->write();
147}
148
149void InputController::saveProfile(uint32_t type, const QString& profile) {
150 mInputProfileSave(&m_inputMap, type, m_config->input(), profile.toUtf8().constData());
151 m_config->write();
152}
153
154const char* InputController::profileForType(uint32_t type) {
155 UNUSED(type);
156#ifdef BUILD_SDL
157 if (type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) {
158#if SDL_VERSION_ATLEAST(2, 0, 0)
159 return SDL_JoystickName(m_sdlPlayer.joystick->joystick);
160#else
161 return SDL_JoystickName(SDL_JoystickIndex(m_sdlPlayer.joystick->joystick));
162#endif
163 }
164#endif
165 return 0;
166}
167
168QStringList InputController::connectedGamepads(uint32_t type) const {
169 UNUSED(type);
170
171#ifdef BUILD_SDL
172 if (type == SDL_BINDING_BUTTON) {
173 QStringList pads;
174 for (size_t i = 0; i < SDL_JoystickListSize(&s_sdlEvents.joysticks); ++i) {
175 const char* name;
176#if SDL_VERSION_ATLEAST(2, 0, 0)
177 name = SDL_JoystickName(SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, i)->joystick);
178#else
179 name = SDL_JoystickName(SDL_JoystickIndex(SDL_JoystickListGetPointer(&s_sdlEvents.joysticks, i)->joystick));
180#endif
181 if (name) {
182 pads.append(QString(name));
183 } else {
184 pads.append(QString());
185 }
186 }
187 return pads;
188 }
189#endif
190
191 return QStringList();
192}
193
194int InputController::gamepad(uint32_t type) const {
195#ifdef BUILD_SDL
196 if (type == SDL_BINDING_BUTTON) {
197 return m_sdlPlayer.joystick ? m_sdlPlayer.joystick->index : 0;
198 }
199#endif
200 return 0;
201}
202
203void InputController::setGamepad(uint32_t type, int index) {
204#ifdef BUILD_SDL
205 if (type == SDL_BINDING_BUTTON) {
206 mSDLPlayerChangeJoystick(&s_sdlEvents, &m_sdlPlayer, index);
207 }
208#endif
209}
210
211void InputController::setPreferredGamepad(uint32_t type, const QString& device) {
212 if (!m_config) {
213 return;
214 }
215 mInputSetPreferredDevice(m_config->input(), "gba", type, m_playerId, device.toUtf8().constData());
216}
217
218mRumble* InputController::rumble() {
219#ifdef BUILD_SDL
220#if SDL_VERSION_ATLEAST(2, 0, 0)
221 if (m_playerAttached) {
222 return &m_sdlPlayer.rumble.d;
223 }
224#endif
225#endif
226 return nullptr;
227}
228
229mRotationSource* InputController::rotationSource() {
230#ifdef BUILD_SDL
231 if (m_playerAttached) {
232 return &m_sdlPlayer.rotation.d;
233 }
234#endif
235 return nullptr;
236}
237
238void InputController::registerTiltAxisX(int axis) {
239#ifdef BUILD_SDL
240 if (m_playerAttached) {
241 m_sdlPlayer.rotation.axisX = axis;
242 }
243#endif
244}
245
246void InputController::registerTiltAxisY(int axis) {
247#ifdef BUILD_SDL
248 if (m_playerAttached) {
249 m_sdlPlayer.rotation.axisY = axis;
250 }
251#endif
252}
253
254void InputController::registerGyroAxisX(int axis) {
255#ifdef BUILD_SDL
256 if (m_playerAttached) {
257 m_sdlPlayer.rotation.gyroX = axis;
258 }
259#endif
260}
261
262void InputController::registerGyroAxisY(int axis) {
263#ifdef BUILD_SDL
264 if (m_playerAttached) {
265 m_sdlPlayer.rotation.gyroY = axis;
266 }
267#endif
268}
269
270float InputController::gyroSensitivity() const {
271#ifdef BUILD_SDL
272 if (m_playerAttached) {
273 return m_sdlPlayer.rotation.gyroSensitivity;
274 }
275#endif
276 return 0;
277}
278
279void InputController::setGyroSensitivity(float sensitivity) {
280#ifdef BUILD_SDL
281 if (m_playerAttached) {
282 m_sdlPlayer.rotation.gyroSensitivity = sensitivity;
283 }
284#endif
285}
286
287GBAKey InputController::mapKeyboard(int key) const {
288 return static_cast<GBAKey>(mInputMapKey(&m_inputMap, KEYBOARD, key));
289}
290
291void InputController::bindKey(uint32_t type, int key, GBAKey gbaKey) {
292 return mInputBindKey(&m_inputMap, type, key, gbaKey);
293}
294
295void InputController::updateJoysticks() {
296#ifdef BUILD_SDL
297 QString profile = profileForType(SDL_BINDING_BUTTON);
298 mSDLUpdateJoysticks(&s_sdlEvents, m_config->input());
299 QString newProfile = profileForType(SDL_BINDING_BUTTON);
300 if (profile != newProfile) {
301 loadProfile(SDL_BINDING_BUTTON, newProfile);
302 }
303#endif
304}
305
306int InputController::pollEvents() {
307 int activeButtons = 0;
308#ifdef BUILD_SDL
309 if (m_playerAttached && m_sdlPlayer.joystick) {
310 SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick;
311 SDL_JoystickUpdate();
312 int numButtons = SDL_JoystickNumButtons(joystick);
313 int i;
314 for (i = 0; i < numButtons; ++i) {
315 GBAKey key = static_cast<GBAKey>(mInputMapKey(&m_inputMap, SDL_BINDING_BUTTON, i));
316 if (key == GBA_KEY_NONE) {
317 continue;
318 }
319 if (hasPendingEvent(key)) {
320 continue;
321 }
322 if (SDL_JoystickGetButton(joystick, i)) {
323 activeButtons |= 1 << key;
324 }
325 }
326 int numHats = SDL_JoystickNumHats(joystick);
327 for (i = 0; i < numHats; ++i) {
328 int hat = SDL_JoystickGetHat(joystick, i);
329 activeButtons |= mInputMapHat(&m_inputMap, SDL_BINDING_BUTTON, i, hat);
330 }
331
332 int numAxes = SDL_JoystickNumAxes(joystick);
333 for (i = 0; i < numAxes; ++i) {
334 int value = SDL_JoystickGetAxis(joystick, i);
335
336 enum GBAKey key = static_cast<GBAKey>(mInputMapAxis(&m_inputMap, SDL_BINDING_BUTTON, i, value));
337 if (key != GBA_KEY_NONE) {
338 activeButtons |= 1 << key;
339 }
340 }
341 }
342#endif
343 return activeButtons;
344}
345
346QSet<int> InputController::activeGamepadButtons(int type) {
347 QSet<int> activeButtons;
348#ifdef BUILD_SDL
349 if (m_playerAttached && type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) {
350 SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick;
351 SDL_JoystickUpdate();
352 int numButtons = SDL_JoystickNumButtons(joystick);
353 int i;
354 for (i = 0; i < numButtons; ++i) {
355 if (SDL_JoystickGetButton(joystick, i)) {
356 activeButtons.insert(i);
357 }
358 }
359 }
360#endif
361 return activeButtons;
362}
363
364void InputController::recalibrateAxes() {
365#ifdef BUILD_SDL
366 if (m_playerAttached && m_sdlPlayer.joystick) {
367 SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick;
368 SDL_JoystickUpdate();
369 int numAxes = SDL_JoystickNumAxes(joystick);
370 if (numAxes < 1) {
371 return;
372 }
373 m_deadzones.resize(numAxes);
374 int i;
375 for (i = 0; i < numAxes; ++i) {
376 m_deadzones[i] = SDL_JoystickGetAxis(joystick, i);
377 }
378 }
379#endif
380}
381
382QSet<QPair<int, GamepadAxisEvent::Direction>> InputController::activeGamepadAxes(int type) {
383 QSet<QPair<int, GamepadAxisEvent::Direction>> activeAxes;
384#ifdef BUILD_SDL
385 if (m_playerAttached && type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) {
386 SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick;
387 SDL_JoystickUpdate();
388 int numAxes = SDL_JoystickNumAxes(joystick);
389 if (numAxes < 1) {
390 return activeAxes;
391 }
392 m_deadzones.resize(numAxes);
393 int i;
394 for (i = 0; i < numAxes; ++i) {
395 int32_t axis = SDL_JoystickGetAxis(joystick, i);
396 axis -= m_deadzones[i];
397 if (axis >= AXIS_THRESHOLD || axis <= -AXIS_THRESHOLD) {
398 activeAxes.insert(qMakePair(i, axis > 0 ? GamepadAxisEvent::POSITIVE : GamepadAxisEvent::NEGATIVE));
399 }
400 }
401 }
402#endif
403 return activeAxes;
404}
405
406void InputController::bindAxis(uint32_t type, int axis, GamepadAxisEvent::Direction direction, GBAKey key) {
407 const mInputAxis* old = mInputQueryAxis(&m_inputMap, type, axis);
408 mInputAxis description = { GBA_KEY_NONE, GBA_KEY_NONE, -AXIS_THRESHOLD, AXIS_THRESHOLD };
409 if (old) {
410 description = *old;
411 }
412 int deadzone = 0;
413 if (axis > 0 && m_deadzones.size() > axis) {
414 deadzone = m_deadzones[axis];
415 }
416 switch (direction) {
417 case GamepadAxisEvent::NEGATIVE:
418 description.lowDirection = key;
419
420 description.deadLow = deadzone - AXIS_THRESHOLD;
421 break;
422 case GamepadAxisEvent::POSITIVE:
423 description.highDirection = key;
424 description.deadHigh = deadzone + AXIS_THRESHOLD;
425 break;
426 default:
427 return;
428 }
429 mInputBindAxis(&m_inputMap, type, axis, &description);
430}
431
432void InputController::unbindAllAxes(uint32_t type) {
433 mInputUnbindAllAxes(&m_inputMap, type);
434}
435
436QSet<QPair<int, GamepadHatEvent::Direction>> InputController::activeGamepadHats(int type) {
437 QSet<QPair<int, GamepadHatEvent::Direction>> activeHats;
438#ifdef BUILD_SDL
439 if (m_playerAttached && type == SDL_BINDING_BUTTON && m_sdlPlayer.joystick) {
440 SDL_Joystick* joystick = m_sdlPlayer.joystick->joystick;
441 SDL_JoystickUpdate();
442 int numHats = SDL_JoystickNumHats(joystick);
443 if (numHats < 1) {
444 return activeHats;
445 }
446
447 int i;
448 for (i = 0; i < numHats; ++i) {
449 int hat = SDL_JoystickGetHat(joystick, i);
450 if (hat & GamepadHatEvent::UP) {
451 activeHats.insert(qMakePair(i, GamepadHatEvent::UP));
452 }
453 if (hat & GamepadHatEvent::RIGHT) {
454 activeHats.insert(qMakePair(i, GamepadHatEvent::RIGHT));
455 }
456 if (hat & GamepadHatEvent::DOWN) {
457 activeHats.insert(qMakePair(i, GamepadHatEvent::DOWN));
458 }
459 if (hat & GamepadHatEvent::LEFT) {
460 activeHats.insert(qMakePair(i, GamepadHatEvent::LEFT));
461 }
462 }
463 }
464#endif
465 return activeHats;
466}
467
468void InputController::bindHat(uint32_t type, int hat, GamepadHatEvent::Direction direction, GBAKey gbaKey) {
469 mInputHatBindings bindings{ -1, -1, -1, -1 };
470 mInputQueryHat(&m_inputMap, type, hat, &bindings);
471 switch (direction) {
472 case GamepadHatEvent::UP:
473 bindings.up = gbaKey;
474 break;
475 case GamepadHatEvent::RIGHT:
476 bindings.right = gbaKey;
477 break;
478 case GamepadHatEvent::DOWN:
479 bindings.down = gbaKey;
480 break;
481 case GamepadHatEvent::LEFT:
482 bindings.left = gbaKey;
483 break;
484 default:
485 return;
486 }
487 mInputBindHat(&m_inputMap, type, hat, &bindings);
488}
489
490void InputController::testGamepad(int type) {
491 auto activeAxes = activeGamepadAxes(type);
492 auto oldAxes = m_activeAxes;
493 m_activeAxes = activeAxes;
494
495 auto activeButtons = activeGamepadButtons(type);
496 auto oldButtons = m_activeButtons;
497 m_activeButtons = activeButtons;
498
499 auto activeHats = activeGamepadHats(type);
500 auto oldHats = m_activeHats;
501 m_activeHats = activeHats;
502
503 if (!QApplication::focusWidget()) {
504 return;
505 }
506
507 activeAxes.subtract(oldAxes);
508 oldAxes.subtract(m_activeAxes);
509
510 for (auto& axis : m_activeAxes) {
511 bool newlyAboveThreshold = activeAxes.contains(axis);
512 if (newlyAboveThreshold) {
513 GamepadAxisEvent* event = new GamepadAxisEvent(axis.first, axis.second, newlyAboveThreshold, type, this);
514 postPendingEvent(event->gbaKey());
515 sendGamepadEvent(event);
516 if (!event->isAccepted()) {
517 clearPendingEvent(event->gbaKey());
518 }
519 }
520 }
521 for (auto axis : oldAxes) {
522 GamepadAxisEvent* event = new GamepadAxisEvent(axis.first, axis.second, false, type, this);
523 clearPendingEvent(event->gbaKey());
524 sendGamepadEvent(event);
525 }
526
527 if (!QApplication::focusWidget()) {
528 return;
529 }
530
531 activeButtons.subtract(oldButtons);
532 oldButtons.subtract(m_activeButtons);
533
534 for (int button : activeButtons) {
535 GamepadButtonEvent* event = new GamepadButtonEvent(GamepadButtonEvent::Down(), button, type, this);
536 postPendingEvent(event->gbaKey());
537 sendGamepadEvent(event);
538 if (!event->isAccepted()) {
539 clearPendingEvent(event->gbaKey());
540 }
541 }
542 for (int button : oldButtons) {
543 GamepadButtonEvent* event = new GamepadButtonEvent(GamepadButtonEvent::Up(), button, type, this);
544 clearPendingEvent(event->gbaKey());
545 sendGamepadEvent(event);
546 }
547
548 activeHats.subtract(oldHats);
549 oldHats.subtract(m_activeHats);
550
551 for (auto& hat : activeHats) {
552 GamepadHatEvent* event = new GamepadHatEvent(GamepadHatEvent::Down(), hat.first, hat.second, type, this);
553 postPendingEvent(event->gbaKey());
554 sendGamepadEvent(event);
555 if (!event->isAccepted()) {
556 clearPendingEvent(event->gbaKey());
557 }
558 }
559 for (auto& hat : oldHats) {
560 GamepadHatEvent* event = new GamepadHatEvent(GamepadHatEvent::Up(), hat.first, hat.second, type, this);
561 clearPendingEvent(event->gbaKey());
562 sendGamepadEvent(event);
563 }
564}
565
566void InputController::sendGamepadEvent(QEvent* event) {
567 QWidget* focusWidget = nullptr;
568 if (m_focusParent) {
569 focusWidget = m_focusParent->focusWidget();
570 if (!focusWidget) {
571 focusWidget = m_focusParent;
572 }
573 } else {
574 focusWidget = QApplication::focusWidget();
575 }
576 QApplication::sendEvent(focusWidget, event);
577}
578
579void InputController::postPendingEvent(GBAKey key) {
580 m_pendingEvents.insert(key);
581}
582
583void InputController::clearPendingEvent(GBAKey key) {
584 m_pendingEvents.remove(key);
585}
586
587bool InputController::hasPendingEvent(GBAKey key) const {
588 return m_pendingEvents.contains(key);
589}
590
591void InputController::suspendScreensaver() {
592#ifdef BUILD_SDL
593#if SDL_VERSION_ATLEAST(2, 0, 0)
594 mSDLSuspendScreensaver(&s_sdlEvents);
595#endif
596#endif
597}
598
599void InputController::resumeScreensaver() {
600#ifdef BUILD_SDL
601#if SDL_VERSION_ATLEAST(2, 0, 0)
602 mSDLResumeScreensaver(&s_sdlEvents);
603#endif
604#endif
605}
606
607void InputController::setScreensaverSuspendable(bool suspendable) {
608#ifdef BUILD_SDL
609#if SDL_VERSION_ATLEAST(2, 0, 0)
610 mSDLSetScreensaverSuspendable(&s_sdlEvents, suspendable);
611#endif
612#endif
613}
614
615void InputController::stealFocus(QWidget* focus) {
616 m_focusParent = focus;
617}
618
619void InputController::releaseFocus(QWidget* focus) {
620 if (focus == m_focusParent) {
621 m_focusParent = m_topLevel;
622 }
623}
624
625void InputController::increaseLuminanceLevel() {
626 setLuminanceLevel(m_luxLevel + 1);
627}
628
629void InputController::decreaseLuminanceLevel() {
630 setLuminanceLevel(m_luxLevel - 1);
631}
632
633void InputController::setLuminanceLevel(int level) {
634 int value = 0x16;
635 level = std::max(0, std::min(10, level));
636 if (level > 0) {
637 value += GBA_LUX_LEVELS[level - 1];
638 }
639 setLuminanceValue(value);
640}
641
642void InputController::setLuminanceValue(uint8_t value) {
643 m_luxValue = value;
644 value = std::max<int>(value - 0x16, 0);
645 m_luxLevel = 10;
646 for (int i = 0; i < 10; ++i) {
647 if (value < GBA_LUX_LEVELS[i]) {
648 m_luxLevel = i;
649 break;
650 }
651 }
652 emit luminanceValueChanged(m_luxValue);
653}
654