all repos — mgba @ f4a61f91d46b8a90a61ca0f9ef04f431f0a4fd9f

mGBA Game Boy Advance Emulator

src/platform/qt/GBAKeyEditor.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 "GBAKeyEditor.h"
  7
  8#include <QComboBox>
  9#include <QHBoxLayout>
 10#include <QPaintEvent>
 11#include <QPainter>
 12#include <QPushButton>
 13#include <QVBoxLayout>
 14
 15#include "InputController.h"
 16#include "KeyEditor.h"
 17
 18#ifdef BUILD_SDL
 19#include "platform/sdl/sdl-events.h"
 20#endif
 21
 22using namespace QGBA;
 23
 24const qreal GBAKeyEditor::DPAD_CENTER_X = 0.247;
 25const qreal GBAKeyEditor::DPAD_CENTER_Y = 0.432;
 26const qreal GBAKeyEditor::DPAD_WIDTH = 0.12;
 27const qreal GBAKeyEditor::DPAD_HEIGHT = 0.12;
 28
 29GBAKeyEditor::GBAKeyEditor(InputController* controller, int type, const QString& profile, QWidget* parent)
 30	: QWidget(parent)
 31	, m_profileSelect(nullptr)
 32	, m_clear(nullptr)
 33	, m_type(type)
 34	, m_profile(profile)
 35	, m_controller(controller)
 36{
 37	setWindowFlags(windowFlags() & ~Qt::WindowFullscreenButtonHint);
 38	setMinimumSize(300, 300);
 39
 40	const mInputMap* map = controller->map();
 41	controller->stealFocus(this);
 42
 43	m_keyDU = new KeyEditor(this);
 44	m_keyDD = new KeyEditor(this);
 45	m_keyDL = new KeyEditor(this);
 46	m_keyDR = new KeyEditor(this);
 47	m_keySelect = new KeyEditor(this);
 48	m_keyStart = new KeyEditor(this);
 49	m_keyA = new KeyEditor(this);
 50	m_keyB = new KeyEditor(this);
 51	m_keyL = new KeyEditor(this);
 52	m_keyR = new KeyEditor(this);
 53
 54	refresh();
 55
 56#ifdef BUILD_SDL
 57	if (type == SDL_BINDING_BUTTON) {
 58		m_profileSelect = new QComboBox(this);
 59		connect(m_profileSelect, SIGNAL(currentIndexChanged(int)), this, SLOT(selectGamepad(int)));
 60
 61		updateJoysticks();
 62
 63		m_clear = new QWidget(this);
 64		QHBoxLayout* layout = new QHBoxLayout;
 65		m_clear->setLayout(layout);
 66		layout->setSpacing(6);
 67
 68		QPushButton* clearButton = new QPushButton(tr("Clear Button"));
 69		layout->addWidget(clearButton);
 70		connect(clearButton, &QAbstractButton::pressed, [this]() {
 71			if (!findFocus()) {
 72				return;
 73			}
 74			bool signalsBlocked = (*m_currentKey)->blockSignals(true);
 75			(*m_currentKey)->clearButton();
 76			(*m_currentKey)->clearHat();
 77			(*m_currentKey)->blockSignals(signalsBlocked);
 78		});
 79
 80		QPushButton* clearAxis = new QPushButton(tr("Clear Analog"));
 81		layout->addWidget(clearAxis);
 82		connect(clearAxis, &QAbstractButton::pressed, [this]() {
 83			if (!findFocus()) {
 84				return;
 85			}
 86			bool signalsBlocked = (*m_currentKey)->blockSignals(true);
 87			(*m_currentKey)->clearAxis();
 88			(*m_currentKey)->blockSignals(signalsBlocked);
 89		});
 90
 91		QPushButton* updateJoysticksButton = new QPushButton(tr("Refresh"));
 92		layout->addWidget(updateJoysticksButton);
 93		connect(updateJoysticksButton, SIGNAL(pressed()), this, SLOT(updateJoysticks()));
 94	}
 95#endif
 96
 97	m_buttons = new QWidget(this);
 98	QVBoxLayout* layout = new QVBoxLayout;
 99	m_buttons->setLayout(layout);
100
101	QPushButton* setAll = new QPushButton(tr("Set all"));
102	connect(setAll, SIGNAL(pressed()), this, SLOT(setAll()));
103	layout->addWidget(setAll);
104
105	layout->setSpacing(6);
106
107	m_keyOrder = QList<KeyEditor*>{
108		m_keyDU,
109		m_keyDR,
110		m_keyDD,
111		m_keyDL,
112		m_keyA,
113		m_keyB,
114		m_keySelect,
115		m_keyStart,
116		m_keyL,
117		m_keyR
118	};
119
120	for (auto& key : m_keyOrder) {
121		connect(key, SIGNAL(valueChanged(int)), this, SLOT(setNext()));
122		connect(key, SIGNAL(axisChanged(int, int)), this, SLOT(setNext()));
123		connect(key, SIGNAL(hatChanged(int, int)), this, SLOT(setNext()));
124		key->installEventFilter(this);
125	}
126
127	m_currentKey = m_keyOrder.end();
128
129	m_background.load(":/res/keymap.qpic");
130
131	setAll->setFocus();
132}
133
134GBAKeyEditor::~GBAKeyEditor() {
135	m_controller->releaseFocus(this);
136}
137
138void GBAKeyEditor::setAll() {
139	m_currentKey = m_keyOrder.begin();
140	(*m_currentKey)->setFocus();
141}
142
143void GBAKeyEditor::resizeEvent(QResizeEvent* event) {
144	setLocation(m_buttons, 0.5, 0.2);
145	setLocation(m_keyDU, DPAD_CENTER_X, DPAD_CENTER_Y - DPAD_HEIGHT);
146	setLocation(m_keyDD, DPAD_CENTER_X, DPAD_CENTER_Y + DPAD_HEIGHT);
147	setLocation(m_keyDL, DPAD_CENTER_X - DPAD_WIDTH, DPAD_CENTER_Y);
148	setLocation(m_keyDR, DPAD_CENTER_X + DPAD_WIDTH, DPAD_CENTER_Y);
149	setLocation(m_keySelect, 0.415, 0.93);
150	setLocation(m_keyStart, 0.585, 0.93);
151	setLocation(m_keyA, 0.826, 0.475);
152	setLocation(m_keyB, 0.667, 0.514);
153	setLocation(m_keyL, 0.1, 0.1);
154	setLocation(m_keyR, 0.9, 0.1);
155
156	if (m_profileSelect) {
157		setLocation(m_profileSelect, 0.5, 0.67);
158	}
159
160	if (m_clear) {
161		setLocation(m_clear, 0.5, 0.77);
162	}
163}
164
165void GBAKeyEditor::paintEvent(QPaintEvent* event) {
166	QPainter painter(this);
167	painter.scale(width() / 480.0, height() / 480.0);
168	painter.drawPicture(0, 0, m_background);
169}
170
171void GBAKeyEditor::closeEvent(QCloseEvent*) {
172	m_controller->releaseFocus(this);
173}
174
175bool GBAKeyEditor::event(QEvent* event) {
176	QEvent::Type type = event->type();
177	if (type == QEvent::WindowActivate || type == QEvent::Show) {
178		m_controller->stealFocus(this);
179	} else if (type == QEvent::WindowDeactivate || type == QEvent::Hide) {
180		m_controller->releaseFocus(this);
181	}
182	return QWidget::event(event);
183}
184
185bool GBAKeyEditor::eventFilter(QObject* obj, QEvent* event) {
186	if (event->type() != QEvent::FocusIn) {
187		return false;
188	}
189	findFocus(static_cast<KeyEditor*>(obj));
190	return true;
191}
192
193void GBAKeyEditor::setNext() {
194	if (m_currentKey == m_keyOrder.end()) {
195		return;
196	}
197
198	++m_currentKey;
199	if (m_currentKey != m_keyOrder.end()) {
200		(*m_currentKey)->setFocus();
201	} else {
202		(*(m_currentKey - 1))->clearFocus();
203	}
204}
205
206void GBAKeyEditor::save() {
207#ifdef BUILD_SDL
208	m_controller->unbindAllAxes(m_type);
209#endif
210
211	bindKey(m_keyDU, GBA_KEY_UP);
212	bindKey(m_keyDD, GBA_KEY_DOWN);
213	bindKey(m_keyDL, GBA_KEY_LEFT);
214	bindKey(m_keyDR, GBA_KEY_RIGHT);
215	bindKey(m_keySelect, GBA_KEY_SELECT);
216	bindKey(m_keyStart, GBA_KEY_START);
217	bindKey(m_keyA, GBA_KEY_A);
218	bindKey(m_keyB, GBA_KEY_B);
219	bindKey(m_keyL, GBA_KEY_L);
220	bindKey(m_keyR, GBA_KEY_R);
221	m_controller->saveConfiguration(m_type);
222
223#ifdef BUILD_SDL
224	if (m_profileSelect) {
225		m_controller->setPreferredGamepad(m_type, m_profileSelect->currentText());
226	}
227#endif
228
229	if (!m_profile.isNull()) {
230		m_controller->saveProfile(m_type, m_profile);
231	}
232}
233
234void GBAKeyEditor::refresh() {
235	const mInputMap* map = m_controller->map();
236	lookupBinding(map, m_keyDU, GBA_KEY_UP);
237	lookupBinding(map, m_keyDD, GBA_KEY_DOWN);
238	lookupBinding(map, m_keyDL, GBA_KEY_LEFT);
239	lookupBinding(map, m_keyDR, GBA_KEY_RIGHT);
240	lookupBinding(map, m_keySelect, GBA_KEY_SELECT);
241	lookupBinding(map, m_keyStart, GBA_KEY_START);
242	lookupBinding(map, m_keyA, GBA_KEY_A);
243	lookupBinding(map, m_keyB, GBA_KEY_B);
244	lookupBinding(map, m_keyL, GBA_KEY_L);
245	lookupBinding(map, m_keyR, GBA_KEY_R);
246
247#ifdef BUILD_SDL
248	lookupAxes(map);
249	lookupHats(map);
250#endif
251}
252
253void GBAKeyEditor::lookupBinding(const mInputMap* map, KeyEditor* keyEditor, GBAKey key) {
254#ifdef BUILD_SDL
255	if (m_type == SDL_BINDING_BUTTON) {
256		int value = mInputQueryBinding(map, m_type, key);
257		keyEditor->setValueButton(value);
258		return;
259	}
260#endif
261	keyEditor->setValueKey(mInputQueryBinding(map, m_type, key));
262}
263
264#ifdef BUILD_SDL
265void GBAKeyEditor::lookupAxes(const mInputMap* map) {
266	mInputEnumerateAxes(map, m_type, [](int axis, const mInputAxis* description, void* user) {
267		GBAKeyEditor* self = static_cast<GBAKeyEditor*>(user);
268		if (description->highDirection != GBA_KEY_NONE) {
269			KeyEditor* key = self->keyById(static_cast<enum GBAKey>(description->highDirection));
270			if (key) {
271				key->setValueAxis(axis, description->deadHigh);
272			}
273		}
274		if (description->lowDirection != GBA_KEY_NONE) {
275			KeyEditor* key = self->keyById(static_cast<enum GBAKey>(description->lowDirection));
276			if (key) {
277				key->setValueAxis(axis, description->deadLow);
278			}
279		}
280	}, this);
281}
282
283void GBAKeyEditor::lookupHats(const mInputMap* map) {
284	struct mInputHatBindings bindings;
285	int i = 0;
286	while (mInputQueryHat(map, m_type, i, &bindings)) {
287		if (bindings.up >= 0) {
288			KeyEditor* key = keyById(static_cast<enum GBAKey>(bindings.up));
289			if (key) {
290				key->setValueHat(i, GamepadHatEvent::UP);
291			}
292		}
293		if (bindings.right >= 0) {
294			KeyEditor* key = keyById(static_cast<enum GBAKey>(bindings.right));
295			if (key) {
296				key->setValueHat(i, GamepadHatEvent::RIGHT);
297			}
298		}
299		if (bindings.down >= 0) {
300			KeyEditor* key = keyById(static_cast<enum GBAKey>(bindings.down));
301			if (key) {
302				key->setValueHat(i, GamepadHatEvent::DOWN);
303			}
304		}
305		if (bindings.left >= 0) {
306			KeyEditor* key = keyById(static_cast<enum GBAKey>(bindings.left));
307			if (key) {
308				key->setValueHat(i, GamepadHatEvent::LEFT);
309			}
310		}
311		++i;
312	}
313}
314#endif
315
316void GBAKeyEditor::bindKey(const KeyEditor* keyEditor, GBAKey key) {
317#ifdef BUILD_SDL
318	if (m_type == SDL_BINDING_BUTTON && keyEditor->axis() >= 0) {
319		m_controller->bindAxis(m_type, keyEditor->axis(), keyEditor->direction(), key);
320	}
321	if (m_type == SDL_BINDING_BUTTON && keyEditor->hat() >= 0) {
322		m_controller->bindHat(m_type, keyEditor->hat(), keyEditor->hatDirection(), key);
323	}
324#endif
325	m_controller->bindKey(m_type, keyEditor->value(), key);
326}
327
328bool GBAKeyEditor::findFocus(KeyEditor* needle) {
329	if (m_currentKey != m_keyOrder.end() && (*m_currentKey)->hasFocus()) {
330		return true;
331	}
332
333	for (auto key = m_keyOrder.begin(); key != m_keyOrder.end(); ++key) {
334		if ((*key)->hasFocus() || needle == *key) {
335			m_currentKey = key;
336			return true;
337		}
338	}
339	return m_currentKey != m_keyOrder.end();
340}
341
342#ifdef BUILD_SDL
343void GBAKeyEditor::setAxisValue(int axis, int32_t value) {
344	if (!findFocus()) {
345		return;
346	}
347	KeyEditor* focused = *m_currentKey;
348	focused->setValueAxis(axis, value);
349}
350
351void GBAKeyEditor::selectGamepad(int index) {
352	m_controller->setGamepad(m_type, index);
353	m_profile = m_profileSelect->currentText();
354	m_controller->loadProfile(m_type, m_profile);
355	refresh();
356}
357#endif
358
359KeyEditor* GBAKeyEditor::keyById(GBAKey key) {
360	switch (key) {
361	case GBA_KEY_UP:
362		return m_keyDU;
363	case GBA_KEY_DOWN:
364		return m_keyDD;
365	case GBA_KEY_LEFT:
366		return m_keyDL;
367	case GBA_KEY_RIGHT:
368		return m_keyDR;
369	case GBA_KEY_A:
370		return m_keyA;
371	case GBA_KEY_B:
372		return m_keyB;
373	case GBA_KEY_L:
374		return m_keyL;
375	case GBA_KEY_R:
376		return m_keyR;
377	case GBA_KEY_SELECT:
378		return m_keySelect;
379	case GBA_KEY_START:
380		return m_keyStart;
381	default:
382		break;
383	}
384	return nullptr;
385}
386
387void GBAKeyEditor::setLocation(QWidget* widget, qreal x, qreal y) {
388	QSize s = size();
389	QSize hint = widget->sizeHint();
390	widget->setGeometry(s.width() * x - hint.width() / 2.0, s.height() * y - hint.height() / 2.0, hint.width(),
391	                    hint.height());
392}
393
394#ifdef BUILD_SDL
395void GBAKeyEditor::updateJoysticks() {
396	m_controller->updateJoysticks();
397	m_controller->recalibrateAxes();
398
399	m_profileSelect->clear();
400	m_profileSelect->addItems(m_controller->connectedGamepads(m_type));
401	int activeGamepad = m_controller->gamepad(m_type);
402	selectGamepad(activeGamepad);
403	if (activeGamepad > 0) {
404		m_profileSelect->setCurrentIndex(activeGamepad);
405	}
406	lookupAxes(m_controller->map());
407	lookupHats(m_controller->map());
408}
409#endif