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