mirror of https://github.com/mgba-emu/mgba.git
Merge branch 'feature/input-revamp' into medusa
This commit is contained in:
commit
a5bcfc7c80
4
CHANGES
4
CHANGES
|
@ -33,6 +33,7 @@ Bugfixes:
|
||||||
- Util: Fix highest-fd socket not being returned by SocketAccept
|
- Util: Fix highest-fd socket not being returned by SocketAccept
|
||||||
- Qt: Fix linking after some windows have been closed
|
- Qt: Fix linking after some windows have been closed
|
||||||
- GBA Video: Fix wrong palette on 256-color sprites in OBJWIN
|
- GBA Video: Fix wrong palette on 256-color sprites in OBJWIN
|
||||||
|
- Windows: Fix VDir.rewind
|
||||||
Misc:
|
Misc:
|
||||||
- SDL: Remove scancode key input
|
- SDL: Remove scancode key input
|
||||||
- GBA Video: Clean up unused timers
|
- GBA Video: Clean up unused timers
|
||||||
|
@ -76,6 +77,9 @@ Misc:
|
||||||
- GBA Video: Optimize when BLD* registers are written frequently
|
- GBA Video: Optimize when BLD* registers are written frequently
|
||||||
- Core: Cores can now have multiple sets of callbacks
|
- Core: Cores can now have multiple sets of callbacks
|
||||||
- GBA: Ignore invalid opcodes used by the Wii U VC emulator
|
- GBA: Ignore invalid opcodes used by the Wii U VC emulator
|
||||||
|
- Qt: Remove audio thread
|
||||||
|
- Qt: Remove audio buffer sizing in AudioProcessorQt
|
||||||
|
- Qt: Re-enable QtMultimedia on Windows
|
||||||
|
|
||||||
0.5.2: (2016-12-31)
|
0.5.2: (2016-12-31)
|
||||||
Bugfixes:
|
Bugfixes:
|
||||||
|
|
|
@ -294,6 +294,7 @@ if(NOT CMAKE_SYSTEM_NAME STREQUAL "Generic")
|
||||||
else()
|
else()
|
||||||
if(DEFINED 3DS)
|
if(DEFINED 3DS)
|
||||||
check_function_exists(snprintf_l HAVE_SNPRINTF_L)
|
check_function_exists(snprintf_l HAVE_SNPRINTF_L)
|
||||||
|
check_function_exists(strtof_l HAVE_STRTOF_L)
|
||||||
check_function_exists(newlocale HAVE_NEWLOCALE)
|
check_function_exists(newlocale HAVE_NEWLOCALE)
|
||||||
check_function_exists(freelocale HAVE_FREELOCALE)
|
check_function_exists(freelocale HAVE_FREELOCALE)
|
||||||
check_function_exists(uselocale HAVE_USELOCALE)
|
check_function_exists(uselocale HAVE_USELOCALE)
|
||||||
|
|
|
@ -14,7 +14,7 @@ CXX_GUARD_START
|
||||||
|
|
||||||
extern const struct mInputPlatformInfo DSInputInfo;
|
extern const struct mInputPlatformInfo DSInputInfo;
|
||||||
|
|
||||||
enum GBAKey {
|
enum DSKey {
|
||||||
DS_KEY_A = 0,
|
DS_KEY_A = 0,
|
||||||
DS_KEY_B = 1,
|
DS_KEY_B = 1,
|
||||||
DS_KEY_SELECT = 2,
|
DS_KEY_SELECT = 2,
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
/* Copyright (c) 2013-2017 Jeffrey Pfau
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
#ifndef GB_INPUT_H
|
||||||
|
#define GB_INPUT_H
|
||||||
|
|
||||||
|
#include <mgba-util/common.h>
|
||||||
|
|
||||||
|
CXX_GUARD_START
|
||||||
|
|
||||||
|
#include <mgba/core/input.h>
|
||||||
|
|
||||||
|
extern const struct mInputPlatformInfo GBInputInfo;
|
||||||
|
|
||||||
|
enum GBKey {
|
||||||
|
GB_KEY_A = 0,
|
||||||
|
GB_KEY_B = 1,
|
||||||
|
GB_KEY_SELECT = 2,
|
||||||
|
GB_KEY_START = 3,
|
||||||
|
GB_KEY_RIGHT = 4,
|
||||||
|
GB_KEY_LEFT = 5,
|
||||||
|
GB_KEY_UP = 6,
|
||||||
|
GB_KEY_DOWN = 7,
|
||||||
|
GB_KEY_MAX,
|
||||||
|
GB_KEY_NONE = -1
|
||||||
|
};
|
||||||
|
|
||||||
|
CXX_GUARD_END
|
||||||
|
|
||||||
|
#endif
|
|
@ -9,6 +9,7 @@
|
||||||
#include <mgba/internal/gb/cheats.h>
|
#include <mgba/internal/gb/cheats.h>
|
||||||
#include <mgba/internal/gb/extra/cli.h>
|
#include <mgba/internal/gb/extra/cli.h>
|
||||||
#include <mgba/internal/gb/gb.h>
|
#include <mgba/internal/gb/gb.h>
|
||||||
|
#include <mgba/internal/gb/input.h>
|
||||||
#include <mgba/internal/gb/mbc.h>
|
#include <mgba/internal/gb/mbc.h>
|
||||||
#include <mgba/internal/gb/overrides.h>
|
#include <mgba/internal/gb/overrides.h>
|
||||||
#include <mgba/internal/gb/renderers/software.h>
|
#include <mgba/internal/gb/renderers/software.h>
|
||||||
|
@ -68,7 +69,7 @@ static bool _GBCoreInit(struct mCore* core) {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef MINIMAL_CORE
|
#ifndef MINIMAL_CORE
|
||||||
core->inputInfo = &GBAInputInfo; // TODO: GBInputInfo
|
core->inputInfo = &GBInputInfo;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -269,7 +270,7 @@ static void _GBCoreReset(struct mCore* core) {
|
||||||
}
|
}
|
||||||
if (!found) {
|
if (!found) {
|
||||||
GBDetectModel(gb);
|
GBDetectModel(gb);
|
||||||
const char* configPath;
|
const char* configPath = NULL;
|
||||||
|
|
||||||
switch (gb->model) {
|
switch (gb->model) {
|
||||||
case GB_MODEL_DMG:
|
case GB_MODEL_DMG:
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
/* Copyright (c) 2013-2017 Jeffrey Pfau
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
#include <mgba/internal/gb/input.h>
|
||||||
|
|
||||||
|
#include <mgba/gb/interface.h>
|
||||||
|
|
||||||
|
const struct mInputPlatformInfo GBInputInfo = {
|
||||||
|
.platformName = "gb",
|
||||||
|
.keyId = (const char*[]) {
|
||||||
|
"A",
|
||||||
|
"B",
|
||||||
|
"Select",
|
||||||
|
"Start",
|
||||||
|
"Right",
|
||||||
|
"Left",
|
||||||
|
"Up",
|
||||||
|
"Down"
|
||||||
|
},
|
||||||
|
.nKeys = GB_KEY_MAX,
|
||||||
|
.hat = {
|
||||||
|
.up = GB_KEY_UP,
|
||||||
|
.left = GB_KEY_LEFT,
|
||||||
|
.down = GB_KEY_DOWN,
|
||||||
|
.right = GB_KEY_RIGHT
|
||||||
|
}
|
||||||
|
};
|
|
@ -58,7 +58,6 @@ bool AudioProcessorQt::start() {
|
||||||
|
|
||||||
m_device->setInput(input());
|
m_device->setInput(input());
|
||||||
m_device->setFormat(m_audioOutput->format());
|
m_device->setFormat(m_audioOutput->format());
|
||||||
m_audioOutput->setBufferSize(input()->core->getAudioBufferSize(input()->core) * 4);
|
|
||||||
|
|
||||||
m_audioOutput->start(m_device);
|
m_audioOutput->start(m_device);
|
||||||
return m_audioOutput->state() == QAudio::ActiveState;
|
return m_audioOutput->state() == QAudio::ActiveState;
|
||||||
|
@ -71,12 +70,6 @@ void AudioProcessorQt::pause() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioProcessorQt::setBufferSamples(int samples) {
|
void AudioProcessorQt::setBufferSamples(int samples) {
|
||||||
AudioProcessor::setBufferSamples(samples);
|
|
||||||
if (m_audioOutput) {
|
|
||||||
m_audioOutput->stop();
|
|
||||||
m_audioOutput->setBufferSize(samples * 4);
|
|
||||||
m_audioOutput->start(m_device);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioProcessorQt::inputParametersChanged() {
|
void AudioProcessorQt::inputParametersChanged() {
|
||||||
|
|
|
@ -30,11 +30,8 @@ endif()
|
||||||
set(CMAKE_AUTOMOC ON)
|
set(CMAKE_AUTOMOC ON)
|
||||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||||
|
|
||||||
if(NOT WIN32 OR NOT BUILD_SDL)
|
find_package(Qt5Multimedia)
|
||||||
find_package(Qt5Multimedia)
|
|
||||||
endif()
|
|
||||||
find_package(Qt5OpenGL)
|
find_package(Qt5OpenGL)
|
||||||
find_package(Qt5Network)
|
|
||||||
find_package(Qt5Widgets)
|
find_package(Qt5Widgets)
|
||||||
|
|
||||||
if(NOT BUILD_GL AND NOT BUILD_GLES2)
|
if(NOT BUILD_GL AND NOT BUILD_GLES2)
|
||||||
|
@ -43,7 +40,7 @@ if(NOT BUILD_GL AND NOT BUILD_GLES2)
|
||||||
return()
|
return()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(NOT Qt5OpenGL_FOUND OR NOT Qt5Widgets_FOUND OR NOT Qt5Network_FOUND)
|
if(NOT Qt5OpenGL_FOUND OR NOT Qt5Widgets_FOUND)
|
||||||
message(WARNING "Cannot find Qt modules")
|
message(WARNING "Cannot find Qt modules")
|
||||||
set(BUILD_QT OFF PARENT_SCOPE)
|
set(BUILD_QT OFF PARENT_SCOPE)
|
||||||
return()
|
return()
|
||||||
|
@ -78,7 +75,6 @@ set(SOURCE_FILES
|
||||||
DisplayGL.cpp
|
DisplayGL.cpp
|
||||||
DisplayQt.cpp
|
DisplayQt.cpp
|
||||||
GBAApp.cpp
|
GBAApp.cpp
|
||||||
GBAKeyEditor.cpp
|
|
||||||
GIFView.cpp
|
GIFView.cpp
|
||||||
GameController.cpp
|
GameController.cpp
|
||||||
GamepadAxisEvent.cpp
|
GamepadAxisEvent.cpp
|
||||||
|
@ -86,6 +82,8 @@ set(SOURCE_FILES
|
||||||
GamepadHatEvent.cpp
|
GamepadHatEvent.cpp
|
||||||
IOViewer.cpp
|
IOViewer.cpp
|
||||||
InputController.cpp
|
InputController.cpp
|
||||||
|
InputItem.cpp
|
||||||
|
InputModel.cpp
|
||||||
InputProfile.cpp
|
InputProfile.cpp
|
||||||
KeyEditor.cpp
|
KeyEditor.cpp
|
||||||
LoadSaveState.cpp
|
LoadSaveState.cpp
|
||||||
|
@ -103,7 +101,6 @@ set(SOURCE_FILES
|
||||||
SensorView.cpp
|
SensorView.cpp
|
||||||
SettingsView.cpp
|
SettingsView.cpp
|
||||||
ShaderSelector.cpp
|
ShaderSelector.cpp
|
||||||
ShortcutController.cpp
|
|
||||||
ShortcutView.cpp
|
ShortcutView.cpp
|
||||||
Swatch.cpp
|
Swatch.cpp
|
||||||
TilePainter.cpp
|
TilePainter.cpp
|
||||||
|
@ -265,16 +262,31 @@ if(APPLE OR WIN32)
|
||||||
set_target_properties(${BINARY_NAME}-qt PROPERTIES OUTPUT_NAME ${PROJECT_NAME})
|
set_target_properties(${BINARY_NAME}-qt PROPERTIES OUTPUT_NAME ${PROJECT_NAME})
|
||||||
endif()
|
endif()
|
||||||
if(APPLE)
|
if(APPLE)
|
||||||
get_target_property(QTCOCOA Qt5::QCocoaIntegrationPlugin LOCATION)
|
message(STATUS ${CMAKE_SYSTEM_NAME})
|
||||||
get_target_property(COREAUDIO Qt5::CoreAudioPlugin LOCATION)
|
if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Darwin")
|
||||||
get_target_property(BUNDLE_PATH ${BINARY_NAME}-qt LOCATION)
|
get_target_property(QTCOCOA Qt5::QCocoaIntegrationPlugin LOCATION)
|
||||||
target_sources(${BINARY_NAME}-qt PRIVATE "${PLUGINS}")
|
get_target_property(COREAUDIO Qt5::CoreAudioPlugin LOCATION)
|
||||||
set_source_files_properties("${QTCOCOA}" PROPERTIES MACOSX_PACKAGE_LOCATION Contents/PlugIns)
|
get_target_property(BUNDLE_PATH ${BINARY_NAME}-qt LOCATION)
|
||||||
set_source_files_properties("${COREAUDIO}" PROPERTIES MACOSX_PACKAGE_LOCATION Contents/PlugIns)
|
target_sources(${BINARY_NAME}-qt PRIVATE "${PLUGINS}")
|
||||||
install(CODE "
|
set_source_files_properties("${QTCOCOA}" PROPERTIES MACOSX_PACKAGE_LOCATION Contents/PlugIns)
|
||||||
include(BundleUtilities)
|
set_source_files_properties("${COREAUDIO}" PROPERTIES MACOSX_PACKAGE_LOCATION Contents/PlugIns)
|
||||||
set(BU_CHMOD_BUNDLE_ITEMS ON)
|
install(CODE "
|
||||||
file(GLOB_RECURSE PLUGINS \"${BUNDLE_PATH}/Contents/PlugIns/*${CMAKE_SHARED_LIBRARY_SUFFIX}\")
|
include(BundleUtilities)
|
||||||
fixup_bundle(\"${BUNDLE_PATH}\" \"${PLUGINS}\" \"\")
|
set(BU_CHMOD_BUNDLE_ITEMS ON)
|
||||||
" COMPONENT ${BINARY_NAME}-qt)
|
file(GLOB_RECURSE PLUGINS \"${BUNDLE_PATH}/Contents/PlugIns/*${CMAKE_SHARED_LIBRARY_SUFFIX}\")
|
||||||
|
fixup_bundle(\"${BUNDLE_PATH}\" \"${PLUGINS}\" \"\")
|
||||||
|
" COMPONENT ${BINARY_NAME}-qt)
|
||||||
|
else()
|
||||||
|
set(DEPLOY_OPTIONS -p platforms/libqcocoa.dylib,audio/libqtaudio_coreaudio.dylib)
|
||||||
|
if(NOT CMAKE_INSTALL_NAME_TOOL EQUAL "install_name_tool")
|
||||||
|
set(DEPLOY_OPTIONS ${DEPLOY_OPTIONS} -I "${CMAKE_INSTALL_NAME_TOOL}")
|
||||||
|
endif()
|
||||||
|
if(DEFINED CMAKE_OTOOL AND NOT CMAKE_OTOOL EQUAL "otool")
|
||||||
|
set(DEPLOY_OPTIONS ${DEPLOY_OPTIONS} -O "${CMAKE_OTOOL}")
|
||||||
|
endif()
|
||||||
|
if(DEFINED CROSS_ROOT)
|
||||||
|
set(DEPLOY_OPTIONS ${DEPLOY_OPTIONS} -R "${CROSS_ROOT}")
|
||||||
|
endif()
|
||||||
|
install(CODE "execute_process(COMMAND \"${CMAKE_SOURCE_DIR}/tools/deploy-mac.py\" -v ${DEPLOY_OPTIONS} \"\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/Applications/${PROJECT_NAME}.app\")")
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
|
@ -1,409 +0,0 @@
|
||||||
/* Copyright (c) 2013-2014 Jeffrey Pfau
|
|
||||||
*
|
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
||||||
#include "GBAKeyEditor.h"
|
|
||||||
|
|
||||||
#include <QComboBox>
|
|
||||||
#include <QHBoxLayout>
|
|
||||||
#include <QPaintEvent>
|
|
||||||
#include <QPainter>
|
|
||||||
#include <QPushButton>
|
|
||||||
#include <QVBoxLayout>
|
|
||||||
|
|
||||||
#include "InputController.h"
|
|
||||||
#include "KeyEditor.h"
|
|
||||||
|
|
||||||
#ifdef BUILD_SDL
|
|
||||||
#include "platform/sdl/sdl-events.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
using namespace QGBA;
|
|
||||||
|
|
||||||
const qreal GBAKeyEditor::DPAD_CENTER_X = 0.247;
|
|
||||||
const qreal GBAKeyEditor::DPAD_CENTER_Y = 0.432;
|
|
||||||
const qreal GBAKeyEditor::DPAD_WIDTH = 0.12;
|
|
||||||
const qreal GBAKeyEditor::DPAD_HEIGHT = 0.12;
|
|
||||||
|
|
||||||
GBAKeyEditor::GBAKeyEditor(InputController* controller, int type, const QString& profile, QWidget* parent)
|
|
||||||
: QWidget(parent)
|
|
||||||
, m_profileSelect(nullptr)
|
|
||||||
, m_clear(nullptr)
|
|
||||||
, m_type(type)
|
|
||||||
, m_profile(profile)
|
|
||||||
, m_controller(controller)
|
|
||||||
{
|
|
||||||
setWindowFlags(windowFlags() & ~Qt::WindowFullscreenButtonHint);
|
|
||||||
setMinimumSize(300, 300);
|
|
||||||
|
|
||||||
const mInputMap* map = controller->map();
|
|
||||||
controller->stealFocus(this);
|
|
||||||
|
|
||||||
m_keyDU = new KeyEditor(this);
|
|
||||||
m_keyDD = new KeyEditor(this);
|
|
||||||
m_keyDL = new KeyEditor(this);
|
|
||||||
m_keyDR = new KeyEditor(this);
|
|
||||||
m_keySelect = new KeyEditor(this);
|
|
||||||
m_keyStart = new KeyEditor(this);
|
|
||||||
m_keyA = new KeyEditor(this);
|
|
||||||
m_keyB = new KeyEditor(this);
|
|
||||||
m_keyL = new KeyEditor(this);
|
|
||||||
m_keyR = new KeyEditor(this);
|
|
||||||
|
|
||||||
refresh();
|
|
||||||
|
|
||||||
#ifdef BUILD_SDL
|
|
||||||
if (type == SDL_BINDING_BUTTON) {
|
|
||||||
m_profileSelect = new QComboBox(this);
|
|
||||||
connect(m_profileSelect, SIGNAL(currentIndexChanged(int)), this, SLOT(selectGamepad(int)));
|
|
||||||
|
|
||||||
updateJoysticks();
|
|
||||||
|
|
||||||
m_clear = new QWidget(this);
|
|
||||||
QHBoxLayout* layout = new QHBoxLayout;
|
|
||||||
m_clear->setLayout(layout);
|
|
||||||
layout->setSpacing(6);
|
|
||||||
|
|
||||||
QPushButton* clearButton = new QPushButton(tr("Clear Button"));
|
|
||||||
layout->addWidget(clearButton);
|
|
||||||
connect(clearButton, &QAbstractButton::pressed, [this]() {
|
|
||||||
if (!findFocus()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
bool signalsBlocked = (*m_currentKey)->blockSignals(true);
|
|
||||||
(*m_currentKey)->clearButton();
|
|
||||||
(*m_currentKey)->clearHat();
|
|
||||||
(*m_currentKey)->blockSignals(signalsBlocked);
|
|
||||||
});
|
|
||||||
|
|
||||||
QPushButton* clearAxis = new QPushButton(tr("Clear Analog"));
|
|
||||||
layout->addWidget(clearAxis);
|
|
||||||
connect(clearAxis, &QAbstractButton::pressed, [this]() {
|
|
||||||
if (!findFocus()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
bool signalsBlocked = (*m_currentKey)->blockSignals(true);
|
|
||||||
(*m_currentKey)->clearAxis();
|
|
||||||
(*m_currentKey)->blockSignals(signalsBlocked);
|
|
||||||
});
|
|
||||||
|
|
||||||
QPushButton* updateJoysticksButton = new QPushButton(tr("Refresh"));
|
|
||||||
layout->addWidget(updateJoysticksButton);
|
|
||||||
connect(updateJoysticksButton, SIGNAL(pressed()), this, SLOT(updateJoysticks()));
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
m_buttons = new QWidget(this);
|
|
||||||
QVBoxLayout* layout = new QVBoxLayout;
|
|
||||||
m_buttons->setLayout(layout);
|
|
||||||
|
|
||||||
QPushButton* setAll = new QPushButton(tr("Set all"));
|
|
||||||
connect(setAll, SIGNAL(pressed()), this, SLOT(setAll()));
|
|
||||||
layout->addWidget(setAll);
|
|
||||||
|
|
||||||
layout->setSpacing(6);
|
|
||||||
|
|
||||||
m_keyOrder = QList<KeyEditor*>{
|
|
||||||
m_keyDU,
|
|
||||||
m_keyDR,
|
|
||||||
m_keyDD,
|
|
||||||
m_keyDL,
|
|
||||||
m_keyA,
|
|
||||||
m_keyB,
|
|
||||||
m_keySelect,
|
|
||||||
m_keyStart,
|
|
||||||
m_keyL,
|
|
||||||
m_keyR
|
|
||||||
};
|
|
||||||
|
|
||||||
for (auto& key : m_keyOrder) {
|
|
||||||
connect(key, SIGNAL(valueChanged(int)), this, SLOT(setNext()));
|
|
||||||
connect(key, SIGNAL(axisChanged(int, int)), this, SLOT(setNext()));
|
|
||||||
connect(key, SIGNAL(hatChanged(int, int)), this, SLOT(setNext()));
|
|
||||||
key->installEventFilter(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
m_currentKey = m_keyOrder.end();
|
|
||||||
|
|
||||||
m_background.load(":/res/keymap.qpic");
|
|
||||||
|
|
||||||
setAll->setFocus();
|
|
||||||
}
|
|
||||||
|
|
||||||
GBAKeyEditor::~GBAKeyEditor() {
|
|
||||||
m_controller->releaseFocus(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GBAKeyEditor::setAll() {
|
|
||||||
m_currentKey = m_keyOrder.begin();
|
|
||||||
(*m_currentKey)->setFocus();
|
|
||||||
}
|
|
||||||
|
|
||||||
void GBAKeyEditor::resizeEvent(QResizeEvent* event) {
|
|
||||||
setLocation(m_buttons, 0.5, 0.2);
|
|
||||||
setLocation(m_keyDU, DPAD_CENTER_X, DPAD_CENTER_Y - DPAD_HEIGHT);
|
|
||||||
setLocation(m_keyDD, DPAD_CENTER_X, DPAD_CENTER_Y + DPAD_HEIGHT);
|
|
||||||
setLocation(m_keyDL, DPAD_CENTER_X - DPAD_WIDTH, DPAD_CENTER_Y);
|
|
||||||
setLocation(m_keyDR, DPAD_CENTER_X + DPAD_WIDTH, DPAD_CENTER_Y);
|
|
||||||
setLocation(m_keySelect, 0.415, 0.93);
|
|
||||||
setLocation(m_keyStart, 0.585, 0.93);
|
|
||||||
setLocation(m_keyA, 0.826, 0.475);
|
|
||||||
setLocation(m_keyB, 0.667, 0.514);
|
|
||||||
setLocation(m_keyL, 0.1, 0.1);
|
|
||||||
setLocation(m_keyR, 0.9, 0.1);
|
|
||||||
|
|
||||||
if (m_profileSelect) {
|
|
||||||
setLocation(m_profileSelect, 0.5, 0.67);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_clear) {
|
|
||||||
setLocation(m_clear, 0.5, 0.77);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void GBAKeyEditor::paintEvent(QPaintEvent* event) {
|
|
||||||
QPainter painter(this);
|
|
||||||
painter.scale(width() / 480.0, height() / 480.0);
|
|
||||||
painter.drawPicture(0, 0, m_background);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GBAKeyEditor::closeEvent(QCloseEvent*) {
|
|
||||||
m_controller->releaseFocus(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GBAKeyEditor::event(QEvent* event) {
|
|
||||||
QEvent::Type type = event->type();
|
|
||||||
if (type == QEvent::WindowActivate || type == QEvent::Show) {
|
|
||||||
m_controller->stealFocus(this);
|
|
||||||
} else if (type == QEvent::WindowDeactivate || type == QEvent::Hide) {
|
|
||||||
m_controller->releaseFocus(this);
|
|
||||||
}
|
|
||||||
return QWidget::event(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GBAKeyEditor::eventFilter(QObject* obj, QEvent* event) {
|
|
||||||
if (event->type() != QEvent::FocusIn) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
findFocus(static_cast<KeyEditor*>(obj));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GBAKeyEditor::setNext() {
|
|
||||||
if (m_currentKey == m_keyOrder.end()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
++m_currentKey;
|
|
||||||
if (m_currentKey != m_keyOrder.end()) {
|
|
||||||
(*m_currentKey)->setFocus();
|
|
||||||
} else {
|
|
||||||
(*(m_currentKey - 1))->clearFocus();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void GBAKeyEditor::save() {
|
|
||||||
#ifdef BUILD_SDL
|
|
||||||
m_controller->unbindAllAxes(m_type);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
bindKey(m_keyDU, GBA_KEY_UP);
|
|
||||||
bindKey(m_keyDD, GBA_KEY_DOWN);
|
|
||||||
bindKey(m_keyDL, GBA_KEY_LEFT);
|
|
||||||
bindKey(m_keyDR, GBA_KEY_RIGHT);
|
|
||||||
bindKey(m_keySelect, GBA_KEY_SELECT);
|
|
||||||
bindKey(m_keyStart, GBA_KEY_START);
|
|
||||||
bindKey(m_keyA, GBA_KEY_A);
|
|
||||||
bindKey(m_keyB, GBA_KEY_B);
|
|
||||||
bindKey(m_keyL, GBA_KEY_L);
|
|
||||||
bindKey(m_keyR, GBA_KEY_R);
|
|
||||||
m_controller->saveConfiguration(m_type);
|
|
||||||
|
|
||||||
#ifdef BUILD_SDL
|
|
||||||
if (m_profileSelect) {
|
|
||||||
m_controller->setPreferredGamepad(m_type, m_profileSelect->currentText());
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (!m_profile.isNull()) {
|
|
||||||
m_controller->saveProfile(m_type, m_profile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void GBAKeyEditor::refresh() {
|
|
||||||
const mInputMap* map = m_controller->map();
|
|
||||||
lookupBinding(map, m_keyDU, GBA_KEY_UP);
|
|
||||||
lookupBinding(map, m_keyDD, GBA_KEY_DOWN);
|
|
||||||
lookupBinding(map, m_keyDL, GBA_KEY_LEFT);
|
|
||||||
lookupBinding(map, m_keyDR, GBA_KEY_RIGHT);
|
|
||||||
lookupBinding(map, m_keySelect, GBA_KEY_SELECT);
|
|
||||||
lookupBinding(map, m_keyStart, GBA_KEY_START);
|
|
||||||
lookupBinding(map, m_keyA, GBA_KEY_A);
|
|
||||||
lookupBinding(map, m_keyB, GBA_KEY_B);
|
|
||||||
lookupBinding(map, m_keyL, GBA_KEY_L);
|
|
||||||
lookupBinding(map, m_keyR, GBA_KEY_R);
|
|
||||||
|
|
||||||
#ifdef BUILD_SDL
|
|
||||||
lookupAxes(map);
|
|
||||||
lookupHats(map);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void GBAKeyEditor::lookupBinding(const mInputMap* map, KeyEditor* keyEditor, GBAKey key) {
|
|
||||||
#ifdef BUILD_SDL
|
|
||||||
if (m_type == SDL_BINDING_BUTTON) {
|
|
||||||
int value = mInputQueryBinding(map, m_type, key);
|
|
||||||
keyEditor->setValueButton(value);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
keyEditor->setValueKey(mInputQueryBinding(map, m_type, key));
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef BUILD_SDL
|
|
||||||
void GBAKeyEditor::lookupAxes(const mInputMap* map) {
|
|
||||||
mInputEnumerateAxes(map, m_type, [](int axis, const mInputAxis* description, void* user) {
|
|
||||||
GBAKeyEditor* self = static_cast<GBAKeyEditor*>(user);
|
|
||||||
if (description->highDirection != GBA_KEY_NONE) {
|
|
||||||
KeyEditor* key = self->keyById(static_cast<enum GBAKey>(description->highDirection));
|
|
||||||
if (key) {
|
|
||||||
key->setValueAxis(axis, description->deadHigh);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (description->lowDirection != GBA_KEY_NONE) {
|
|
||||||
KeyEditor* key = self->keyById(static_cast<enum GBAKey>(description->lowDirection));
|
|
||||||
if (key) {
|
|
||||||
key->setValueAxis(axis, description->deadLow);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GBAKeyEditor::lookupHats(const mInputMap* map) {
|
|
||||||
struct mInputHatBindings bindings;
|
|
||||||
int i = 0;
|
|
||||||
while (mInputQueryHat(map, m_type, i, &bindings)) {
|
|
||||||
if (bindings.up >= 0) {
|
|
||||||
KeyEditor* key = keyById(static_cast<enum GBAKey>(bindings.up));
|
|
||||||
if (key) {
|
|
||||||
key->setValueHat(i, GamepadHatEvent::UP);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (bindings.right >= 0) {
|
|
||||||
KeyEditor* key = keyById(static_cast<enum GBAKey>(bindings.right));
|
|
||||||
if (key) {
|
|
||||||
key->setValueHat(i, GamepadHatEvent::RIGHT);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (bindings.down >= 0) {
|
|
||||||
KeyEditor* key = keyById(static_cast<enum GBAKey>(bindings.down));
|
|
||||||
if (key) {
|
|
||||||
key->setValueHat(i, GamepadHatEvent::DOWN);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (bindings.left >= 0) {
|
|
||||||
KeyEditor* key = keyById(static_cast<enum GBAKey>(bindings.left));
|
|
||||||
if (key) {
|
|
||||||
key->setValueHat(i, GamepadHatEvent::LEFT);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
++i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void GBAKeyEditor::bindKey(const KeyEditor* keyEditor, GBAKey key) {
|
|
||||||
#ifdef BUILD_SDL
|
|
||||||
if (m_type == SDL_BINDING_BUTTON && keyEditor->axis() >= 0) {
|
|
||||||
m_controller->bindAxis(m_type, keyEditor->axis(), keyEditor->direction(), key);
|
|
||||||
}
|
|
||||||
if (m_type == SDL_BINDING_BUTTON && keyEditor->hat() >= 0) {
|
|
||||||
m_controller->bindHat(m_type, keyEditor->hat(), keyEditor->hatDirection(), key);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
m_controller->bindKey(m_type, keyEditor->value(), key);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GBAKeyEditor::findFocus(KeyEditor* needle) {
|
|
||||||
if (m_currentKey != m_keyOrder.end() && (*m_currentKey)->hasFocus()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto key = m_keyOrder.begin(); key != m_keyOrder.end(); ++key) {
|
|
||||||
if ((*key)->hasFocus() || needle == *key) {
|
|
||||||
m_currentKey = key;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return m_currentKey != m_keyOrder.end();
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef BUILD_SDL
|
|
||||||
void GBAKeyEditor::setAxisValue(int axis, int32_t value) {
|
|
||||||
if (!findFocus()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
KeyEditor* focused = *m_currentKey;
|
|
||||||
focused->setValueAxis(axis, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GBAKeyEditor::selectGamepad(int index) {
|
|
||||||
m_controller->setGamepad(m_type, index);
|
|
||||||
m_profile = m_profileSelect->currentText();
|
|
||||||
m_controller->loadProfile(m_type, m_profile);
|
|
||||||
refresh();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
KeyEditor* GBAKeyEditor::keyById(GBAKey key) {
|
|
||||||
switch (key) {
|
|
||||||
case GBA_KEY_UP:
|
|
||||||
return m_keyDU;
|
|
||||||
case GBA_KEY_DOWN:
|
|
||||||
return m_keyDD;
|
|
||||||
case GBA_KEY_LEFT:
|
|
||||||
return m_keyDL;
|
|
||||||
case GBA_KEY_RIGHT:
|
|
||||||
return m_keyDR;
|
|
||||||
case GBA_KEY_A:
|
|
||||||
return m_keyA;
|
|
||||||
case GBA_KEY_B:
|
|
||||||
return m_keyB;
|
|
||||||
case GBA_KEY_L:
|
|
||||||
return m_keyL;
|
|
||||||
case GBA_KEY_R:
|
|
||||||
return m_keyR;
|
|
||||||
case GBA_KEY_SELECT:
|
|
||||||
return m_keySelect;
|
|
||||||
case GBA_KEY_START:
|
|
||||||
return m_keyStart;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GBAKeyEditor::setLocation(QWidget* widget, qreal x, qreal y) {
|
|
||||||
QSize s = size();
|
|
||||||
QSize hint = widget->sizeHint();
|
|
||||||
widget->setGeometry(s.width() * x - hint.width() / 2.0, s.height() * y - hint.height() / 2.0, hint.width(),
|
|
||||||
hint.height());
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef BUILD_SDL
|
|
||||||
void GBAKeyEditor::updateJoysticks() {
|
|
||||||
m_controller->updateJoysticks();
|
|
||||||
m_controller->recalibrateAxes();
|
|
||||||
|
|
||||||
m_profileSelect->clear();
|
|
||||||
m_profileSelect->addItems(m_controller->connectedGamepads(m_type));
|
|
||||||
int activeGamepad = m_controller->gamepad(m_type);
|
|
||||||
selectGamepad(activeGamepad);
|
|
||||||
if (activeGamepad > 0) {
|
|
||||||
m_profileSelect->setCurrentIndex(activeGamepad);
|
|
||||||
}
|
|
||||||
lookupAxes(m_controller->map());
|
|
||||||
lookupHats(m_controller->map());
|
|
||||||
}
|
|
||||||
#endif
|
|
|
@ -1,96 +0,0 @@
|
||||||
/* Copyright (c) 2013-2015 Jeffrey Pfau
|
|
||||||
*
|
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
||||||
#ifndef QGBA_GBA_KEY_EDITOR
|
|
||||||
#define QGBA_GBA_KEY_EDITOR
|
|
||||||
|
|
||||||
#include <QList>
|
|
||||||
#include <QPicture>
|
|
||||||
#include <QSet>
|
|
||||||
#include <QWidget>
|
|
||||||
|
|
||||||
#include <mgba/internal/gba/input.h>
|
|
||||||
|
|
||||||
class QComboBox;
|
|
||||||
class QTimer;
|
|
||||||
|
|
||||||
namespace QGBA {
|
|
||||||
|
|
||||||
class InputController;
|
|
||||||
class KeyEditor;
|
|
||||||
|
|
||||||
class GBAKeyEditor : public QWidget {
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
GBAKeyEditor(InputController* controller, int type, const QString& profile = QString(), QWidget* parent = nullptr);
|
|
||||||
virtual ~GBAKeyEditor();
|
|
||||||
|
|
||||||
public slots:
|
|
||||||
void setAll();
|
|
||||||
void save();
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual void resizeEvent(QResizeEvent*) override;
|
|
||||||
virtual void paintEvent(QPaintEvent*) override;
|
|
||||||
virtual bool event(QEvent*) override;
|
|
||||||
virtual void closeEvent(QCloseEvent*) override;
|
|
||||||
virtual bool eventFilter(QObject* obj, QEvent* event) override;
|
|
||||||
|
|
||||||
private slots:
|
|
||||||
void setNext();
|
|
||||||
void refresh();
|
|
||||||
#ifdef BUILD_SDL
|
|
||||||
void setAxisValue(int axis, int32_t value);
|
|
||||||
void selectGamepad(int index);
|
|
||||||
void updateJoysticks();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
private:
|
|
||||||
static const qreal DPAD_CENTER_X;
|
|
||||||
static const qreal DPAD_CENTER_Y;
|
|
||||||
static const qreal DPAD_WIDTH;
|
|
||||||
static const qreal DPAD_HEIGHT;
|
|
||||||
|
|
||||||
void setLocation(QWidget* widget, qreal x, qreal y);
|
|
||||||
|
|
||||||
void lookupBinding(const mInputMap*, KeyEditor*, GBAKey);
|
|
||||||
void bindKey(const KeyEditor*, GBAKey);
|
|
||||||
|
|
||||||
bool findFocus(KeyEditor* needle = nullptr);
|
|
||||||
|
|
||||||
#ifdef BUILD_SDL
|
|
||||||
void lookupAxes(const mInputMap*);
|
|
||||||
void lookupHats(const mInputMap*);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
KeyEditor* keyById(GBAKey);
|
|
||||||
|
|
||||||
QComboBox* m_profileSelect;
|
|
||||||
QWidget* m_clear;
|
|
||||||
QWidget* m_buttons;
|
|
||||||
KeyEditor* m_keyDU;
|
|
||||||
KeyEditor* m_keyDD;
|
|
||||||
KeyEditor* m_keyDL;
|
|
||||||
KeyEditor* m_keyDR;
|
|
||||||
KeyEditor* m_keySelect;
|
|
||||||
KeyEditor* m_keyStart;
|
|
||||||
KeyEditor* m_keyA;
|
|
||||||
KeyEditor* m_keyB;
|
|
||||||
KeyEditor* m_keyL;
|
|
||||||
KeyEditor* m_keyR;
|
|
||||||
QList<KeyEditor*> m_keyOrder;
|
|
||||||
QList<KeyEditor*>::iterator m_currentKey;
|
|
||||||
|
|
||||||
uint32_t m_type;
|
|
||||||
QString m_profile;
|
|
||||||
InputController* m_controller;
|
|
||||||
|
|
||||||
QPicture m_background;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -13,7 +13,6 @@
|
||||||
|
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
#include <QThread>
|
|
||||||
|
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
|
|
||||||
|
@ -48,7 +47,6 @@ GameController::GameController(QObject* parent)
|
||||||
, m_gameOpen(false)
|
, m_gameOpen(false)
|
||||||
, m_vf(nullptr)
|
, m_vf(nullptr)
|
||||||
, m_useBios(false)
|
, m_useBios(false)
|
||||||
, m_audioThread(new QThread(this))
|
|
||||||
, m_audioProcessor(AudioProcessor::create())
|
, m_audioProcessor(AudioProcessor::create())
|
||||||
, m_pauseAfterFrame(false)
|
, m_pauseAfterFrame(false)
|
||||||
, m_sync(true)
|
, m_sync(true)
|
||||||
|
@ -176,7 +174,7 @@ GameController::GameController(QObject* parent)
|
||||||
controller->m_patch = QString();
|
controller->m_patch = QString();
|
||||||
controller->clearOverride();
|
controller->clearOverride();
|
||||||
|
|
||||||
QMetaObject::invokeMethod(controller->m_audioProcessor, "pause", Qt::BlockingQueuedConnection);
|
controller->m_audioProcessor->pause();
|
||||||
|
|
||||||
QMetaObject::invokeMethod(controller, "gameStopped", Q_ARG(mCoreThread*, context));
|
QMetaObject::invokeMethod(controller, "gameStopped", Q_ARG(mCoreThread*, context));
|
||||||
QMetaObject::invokeMethod(controller, "cleanGame");
|
QMetaObject::invokeMethod(controller, "cleanGame");
|
||||||
|
@ -294,9 +292,6 @@ GameController::GameController(QObject* parent)
|
||||||
|
|
||||||
m_threadContext.userData = this;
|
m_threadContext.userData = this;
|
||||||
|
|
||||||
m_audioThread->setObjectName("Audio Thread");
|
|
||||||
m_audioThread->start(QThread::TimeCriticalPriority);
|
|
||||||
m_audioProcessor->moveToThread(m_audioThread);
|
|
||||||
connect(this, SIGNAL(gamePaused(mCoreThread*)), m_audioProcessor, SLOT(pause()));
|
connect(this, SIGNAL(gamePaused(mCoreThread*)), m_audioProcessor, SLOT(pause()));
|
||||||
connect(this, SIGNAL(gameStarted(mCoreThread*, const QString&)), m_audioProcessor, SLOT(setInput(mCoreThread*)));
|
connect(this, SIGNAL(gameStarted(mCoreThread*, const QString&)), m_audioProcessor, SLOT(setInput(mCoreThread*)));
|
||||||
connect(this, SIGNAL(frameAvailable(const uint32_t*)), this, SLOT(pollEvents()));
|
connect(this, SIGNAL(frameAvailable(const uint32_t*)), this, SLOT(pollEvents()));
|
||||||
|
@ -306,8 +301,6 @@ GameController::GameController(QObject* parent)
|
||||||
GameController::~GameController() {
|
GameController::~GameController() {
|
||||||
disconnect();
|
disconnect();
|
||||||
closeGame();
|
closeGame();
|
||||||
m_audioThread->quit();
|
|
||||||
m_audioThread->wait();
|
|
||||||
clearMultiplayerController();
|
clearMultiplayerController();
|
||||||
delete m_backupLoadState;
|
delete m_backupLoadState;
|
||||||
}
|
}
|
||||||
|
@ -864,7 +857,7 @@ void GameController::setAudioBufferSamples(int samples) {
|
||||||
threadInterrupt();
|
threadInterrupt();
|
||||||
redoSamples(samples);
|
redoSamples(samples);
|
||||||
threadContinue();
|
threadContinue();
|
||||||
QMetaObject::invokeMethod(m_audioProcessor, "setBufferSamples", Qt::BlockingQueuedConnection, Q_ARG(int, samples));
|
m_audioProcessor->setBufferSamples(samples);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -876,7 +869,7 @@ void GameController::setAudioSampleRate(unsigned rate) {
|
||||||
threadInterrupt();
|
threadInterrupt();
|
||||||
redoSamples(m_audioProcessor->getBufferSamples());
|
redoSamples(m_audioProcessor->getBufferSamples());
|
||||||
threadContinue();
|
threadContinue();
|
||||||
QMetaObject::invokeMethod(m_audioProcessor, "requestSampleRate", Q_ARG(unsigned, rate));
|
m_audioProcessor->requestSampleRate(rate);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -929,9 +922,7 @@ void GameController::setAudioChannelEnabled(int channel, bool enable) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameController::startAudio() {
|
void GameController::startAudio() {
|
||||||
bool started = false;
|
if (!m_audioProcessor->start()) {
|
||||||
QMetaObject::invokeMethod(m_audioProcessor, "start", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, started));
|
|
||||||
if (!started) {
|
|
||||||
LOG(QT, ERROR) << tr("Failed to start audio processor");
|
LOG(QT, ERROR) << tr("Failed to start audio processor");
|
||||||
// Don't freeze!
|
// Don't freeze!
|
||||||
m_audioSync = false;
|
m_audioSync = false;
|
||||||
|
@ -1149,7 +1140,7 @@ void GameController::reloadAudioDriver() {
|
||||||
int samples = 0;
|
int samples = 0;
|
||||||
unsigned sampleRate = 0;
|
unsigned sampleRate = 0;
|
||||||
if (m_audioProcessor) {
|
if (m_audioProcessor) {
|
||||||
QMetaObject::invokeMethod(m_audioProcessor, "pause", Qt::BlockingQueuedConnection);
|
m_audioProcessor->pause();
|
||||||
samples = m_audioProcessor->getBufferSamples();
|
samples = m_audioProcessor->getBufferSamples();
|
||||||
sampleRate = m_audioProcessor->sampleRate();
|
sampleRate = m_audioProcessor->sampleRate();
|
||||||
delete m_audioProcessor;
|
delete m_audioProcessor;
|
||||||
|
@ -1161,7 +1152,6 @@ void GameController::reloadAudioDriver() {
|
||||||
if (sampleRate) {
|
if (sampleRate) {
|
||||||
m_audioProcessor->requestSampleRate(sampleRate);
|
m_audioProcessor->requestSampleRate(sampleRate);
|
||||||
}
|
}
|
||||||
m_audioProcessor->moveToThread(m_audioThread);
|
|
||||||
connect(this, SIGNAL(gamePaused(mCoreThread*)), m_audioProcessor, SLOT(pause()));
|
connect(this, SIGNAL(gamePaused(mCoreThread*)), m_audioProcessor, SLOT(pause()));
|
||||||
connect(this, SIGNAL(gameStarted(mCoreThread*, const QString&)), m_audioProcessor, SLOT(setInput(mCoreThread*)));
|
connect(this, SIGNAL(gameStarted(mCoreThread*, const QString&)), m_audioProcessor, SLOT(setInput(mCoreThread*)));
|
||||||
if (isLoaded()) {
|
if (isLoaded()) {
|
||||||
|
@ -1236,7 +1226,7 @@ void GameController::redoSamples(int samples) {
|
||||||
if (m_threadContext.core) {
|
if (m_threadContext.core) {
|
||||||
m_threadContext.core->setAudioBufferSize(m_threadContext.core, samples);
|
m_threadContext.core->setAudioBufferSize(m_threadContext.core, samples);
|
||||||
}
|
}
|
||||||
QMetaObject::invokeMethod(m_audioProcessor, "inputParametersChanged");
|
m_audioProcessor->inputParametersChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameController::setLogLevel(int levels) {
|
void GameController::setLogLevel(int levels) {
|
||||||
|
|
|
@ -30,8 +30,6 @@ struct mCoreConfig;
|
||||||
struct mDebugger;
|
struct mDebugger;
|
||||||
struct mTileCache;
|
struct mTileCache;
|
||||||
|
|
||||||
class QThread;
|
|
||||||
|
|
||||||
namespace QGBA {
|
namespace QGBA {
|
||||||
|
|
||||||
class AudioProcessor;
|
class AudioProcessor;
|
||||||
|
@ -208,7 +206,6 @@ private:
|
||||||
QString m_patch;
|
QString m_patch;
|
||||||
Override* m_override;
|
Override* m_override;
|
||||||
|
|
||||||
QThread* m_audioThread;
|
|
||||||
AudioProcessor* m_audioProcessor;
|
AudioProcessor* m_audioProcessor;
|
||||||
|
|
||||||
QAtomicInt m_pauseAfterFrame;
|
QAtomicInt m_pauseAfterFrame;
|
||||||
|
|
|
@ -27,6 +27,7 @@ GamepadAxisEvent::GamepadAxisEvent(int axis, Direction direction, bool isNew, in
|
||||||
|
|
||||||
QEvent::Type GamepadAxisEvent::Type() {
|
QEvent::Type GamepadAxisEvent::Type() {
|
||||||
if (s_type == None) {
|
if (s_type == None) {
|
||||||
|
qRegisterMetaType<Direction>("GamepadAxisEvent::Direction");
|
||||||
s_type = static_cast<enum Type>(registerEventType());
|
s_type = static_cast<enum Type>(registerEventType());
|
||||||
}
|
}
|
||||||
return s_type;
|
return s_type;
|
||||||
|
|
|
@ -27,6 +27,7 @@ GamepadHatEvent::GamepadHatEvent(QEvent::Type pressType, int hatId, Direction di
|
||||||
|
|
||||||
QEvent::Type GamepadHatEvent::Down() {
|
QEvent::Type GamepadHatEvent::Down() {
|
||||||
if (s_downType == None) {
|
if (s_downType == None) {
|
||||||
|
qRegisterMetaType<Direction>("GamepadHatEvent::Direction");
|
||||||
s_downType = static_cast<Type>(registerEventType());
|
s_downType = static_cast<Type>(registerEventType());
|
||||||
}
|
}
|
||||||
return s_downType;
|
return s_downType;
|
||||||
|
|
|
@ -6,11 +6,15 @@
|
||||||
#include "InputController.h"
|
#include "InputController.h"
|
||||||
|
|
||||||
#include "ConfigController.h"
|
#include "ConfigController.h"
|
||||||
|
#include "GameController.h"
|
||||||
#include "GamepadAxisEvent.h"
|
#include "GamepadAxisEvent.h"
|
||||||
#include "GamepadButtonEvent.h"
|
#include "GamepadButtonEvent.h"
|
||||||
|
#include "InputModel.h"
|
||||||
#include "InputProfile.h"
|
#include "InputProfile.h"
|
||||||
|
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
|
#include <QKeyEvent>
|
||||||
|
#include <QMenu>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
|
|
||||||
|
@ -24,8 +28,10 @@ int InputController::s_sdlInited = 0;
|
||||||
mSDLEvents InputController::s_sdlEvents;
|
mSDLEvents InputController::s_sdlEvents;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
InputController::InputController(int playerId, QWidget* topLevel, QObject* parent)
|
InputController::InputController(InputModel* model, int playerId, QWidget* topLevel, QObject* parent)
|
||||||
: QObject(parent)
|
: QObject(parent)
|
||||||
|
, m_inputModel(model)
|
||||||
|
, m_platform(PLATFORM_NONE)
|
||||||
, m_playerId(playerId)
|
, m_playerId(playerId)
|
||||||
, m_config(nullptr)
|
, m_config(nullptr)
|
||||||
, m_gamepadTimer(nullptr)
|
, m_gamepadTimer(nullptr)
|
||||||
|
@ -37,15 +43,11 @@ InputController::InputController(int playerId, QWidget* topLevel, QObject* paren
|
||||||
, m_topLevel(topLevel)
|
, m_topLevel(topLevel)
|
||||||
, m_focusParent(topLevel)
|
, m_focusParent(topLevel)
|
||||||
{
|
{
|
||||||
mInputMapInit(&m_inputMap, &GBAInputInfo);
|
|
||||||
|
|
||||||
#ifdef BUILD_SDL
|
#ifdef BUILD_SDL
|
||||||
if (s_sdlInited == 0) {
|
if (s_sdlInited == 0) {
|
||||||
mSDLInitEvents(&s_sdlEvents);
|
mSDLInitEvents(&s_sdlEvents);
|
||||||
}
|
}
|
||||||
++s_sdlInited;
|
++s_sdlInited;
|
||||||
m_sdlPlayer.bindings = &m_inputMap;
|
|
||||||
mSDLInitBindingsGBA(&m_inputMap);
|
|
||||||
updateJoysticks();
|
updateJoysticks();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -60,20 +62,21 @@ InputController::InputController(int playerId, QWidget* topLevel, QObject* paren
|
||||||
m_gamepadTimer.setInterval(50);
|
m_gamepadTimer.setInterval(50);
|
||||||
m_gamepadTimer.start();
|
m_gamepadTimer.start();
|
||||||
|
|
||||||
mInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_X, GBA_KEY_A);
|
m_autofireMenu = std::unique_ptr<QMenu>(new QMenu(tr("Autofire")));
|
||||||
mInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Z, GBA_KEY_B);
|
m_inputModel->addMenu(m_autofireMenu.get());
|
||||||
mInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_A, GBA_KEY_L);
|
|
||||||
mInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_S, GBA_KEY_R);
|
m_inputMenu = std::unique_ptr<QMenu>(new QMenu(tr("Bindings")));
|
||||||
mInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Return, GBA_KEY_START);
|
m_inputModel->addMenu(m_inputMenu.get());
|
||||||
mInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Backspace, GBA_KEY_SELECT);
|
|
||||||
mInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Up, GBA_KEY_UP);
|
connect(m_inputModel, SIGNAL(keyRebound(const QModelIndex&, int)), this, SLOT(bindKey(const QModelIndex&, int)));
|
||||||
mInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Down, GBA_KEY_DOWN);
|
connect(m_inputModel, SIGNAL(buttonRebound(const QModelIndex&, int)), this, SLOT(bindButton(const QModelIndex&, int)));
|
||||||
mInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Left, GBA_KEY_LEFT);
|
connect(m_inputModel, SIGNAL(axisRebound(const QModelIndex&, int, GamepadAxisEvent::Direction)), this, SLOT(bindAxis(const QModelIndex&, int, GamepadAxisEvent::Direction)));
|
||||||
mInputBindKey(&m_inputMap, KEYBOARD, Qt::Key_Right, GBA_KEY_RIGHT);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
InputController::~InputController() {
|
InputController::~InputController() {
|
||||||
mInputMapDeinit(&m_inputMap);
|
for (auto& inputMap : m_inputMaps) {
|
||||||
|
mInputMapDeinit(&inputMap);
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef BUILD_SDL
|
#ifdef BUILD_SDL
|
||||||
if (m_playerAttached) {
|
if (m_playerAttached) {
|
||||||
|
@ -87,6 +90,42 @@ InputController::~InputController() {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void InputController::addPlatform(mPlatform platform, const QString& visibleName, const mInputPlatformInfo* info) {
|
||||||
|
mInputMap* inputMap = &m_inputMaps[platform];
|
||||||
|
mInputMapInit(inputMap, info);
|
||||||
|
|
||||||
|
QMenu* input = m_inputMenu->addMenu(visibleName);
|
||||||
|
QMenu* autofire = m_autofireMenu->addMenu(visibleName);
|
||||||
|
m_inputMenuIndices[platform] = m_inputModel->addMenu(input, m_inputMenu.get());
|
||||||
|
m_inputModel->addMenu(autofire, m_autofireMenu.get());
|
||||||
|
|
||||||
|
for (size_t i = 0; i < info->nKeys; ++i) {
|
||||||
|
m_inputModel->addKey(input, platform, i, 0, info->keyId[i], info->keyId[i]);
|
||||||
|
m_inputModel->addKey(autofire, platform, i, 0, info->keyId[i], info->keyId[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef BUILD_SDL
|
||||||
|
mSDLInitBindingsGBA(inputMap);
|
||||||
|
#endif
|
||||||
|
mInputBindKey(inputMap, KEYBOARD, Qt::Key_X, GBA_KEY_A);
|
||||||
|
mInputBindKey(inputMap, KEYBOARD, Qt::Key_Z, GBA_KEY_B);
|
||||||
|
mInputBindKey(inputMap, KEYBOARD, Qt::Key_A, GBA_KEY_L);
|
||||||
|
mInputBindKey(inputMap, KEYBOARD, Qt::Key_S, GBA_KEY_R);
|
||||||
|
mInputBindKey(inputMap, KEYBOARD, Qt::Key_Return, GBA_KEY_START);
|
||||||
|
mInputBindKey(inputMap, KEYBOARD, Qt::Key_Backspace, GBA_KEY_SELECT);
|
||||||
|
mInputBindKey(inputMap, KEYBOARD, Qt::Key_Up, GBA_KEY_UP);
|
||||||
|
mInputBindKey(inputMap, KEYBOARD, Qt::Key_Down, GBA_KEY_DOWN);
|
||||||
|
mInputBindKey(inputMap, KEYBOARD, Qt::Key_Left, GBA_KEY_LEFT);
|
||||||
|
mInputBindKey(inputMap, KEYBOARD, Qt::Key_Right, GBA_KEY_RIGHT);
|
||||||
|
}
|
||||||
|
|
||||||
|
void InputController::setPlatform(mPlatform platform) {
|
||||||
|
#ifdef BUILD_SDL
|
||||||
|
m_sdlPlayer.bindings = &m_inputMaps[platform];
|
||||||
|
#endif
|
||||||
|
m_platform = platform;
|
||||||
|
}
|
||||||
|
|
||||||
void InputController::setConfiguration(ConfigController* config) {
|
void InputController::setConfiguration(ConfigController* config) {
|
||||||
m_config = config;
|
m_config = config;
|
||||||
setAllowOpposing(config->getOption("allowOpposingDirections").toInt());
|
setAllowOpposing(config->getOption("allowOpposingDirections").toInt());
|
||||||
|
@ -99,26 +138,35 @@ void InputController::setConfiguration(ConfigController* config) {
|
||||||
loadConfiguration(SDL_BINDING_BUTTON);
|
loadConfiguration(SDL_BINDING_BUTTON);
|
||||||
loadProfile(SDL_BINDING_BUTTON, profileForType(SDL_BINDING_BUTTON));
|
loadProfile(SDL_BINDING_BUTTON, profileForType(SDL_BINDING_BUTTON));
|
||||||
#endif
|
#endif
|
||||||
|
restoreModel();
|
||||||
}
|
}
|
||||||
|
|
||||||
void InputController::loadConfiguration(uint32_t type) {
|
void InputController::loadConfiguration(uint32_t type) {
|
||||||
mInputMapLoad(&m_inputMap, type, m_config->input());
|
for (auto& inputMap : m_inputMaps) {
|
||||||
|
mInputMapLoad(&inputMap, type, m_config->input());
|
||||||
#ifdef BUILD_SDL
|
#ifdef BUILD_SDL
|
||||||
if (m_playerAttached) {
|
if (m_playerAttached) {
|
||||||
mSDLPlayerLoadConfig(&m_sdlPlayer, m_config->input());
|
mInputMap* bindings = m_sdlPlayer.bindings;
|
||||||
}
|
m_sdlPlayer.bindings = &inputMap;
|
||||||
|
mSDLPlayerLoadConfig(&m_sdlPlayer, m_config->input());
|
||||||
|
m_sdlPlayer.bindings = bindings;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void InputController::loadProfile(uint32_t type, const QString& profile) {
|
void InputController::loadProfile(uint32_t type, const QString& profile) {
|
||||||
bool loaded = mInputProfileLoad(&m_inputMap, type, m_config->input(), profile.toUtf8().constData());
|
for (auto iter = m_inputMaps.begin(); iter != m_inputMaps.end(); ++iter) {
|
||||||
recalibrateAxes();
|
bool loaded = mInputProfileLoad(&iter.value(), type, m_config->input(), profile.toUtf8().constData());
|
||||||
if (!loaded) {
|
if (!loaded) {
|
||||||
const InputProfile* ip = InputProfile::findProfile(profile);
|
const InputProfile* ip = InputProfile::findProfile(iter.key(), profile);
|
||||||
if (ip) {
|
if (ip) {
|
||||||
ip->apply(this);
|
ip->apply(iter.key(), this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
recalibrateAxes();
|
||||||
|
m_inputModel->loadProfile(PLATFORM_NONE, profile); // TODO
|
||||||
emit profileLoaded(profile);
|
emit profileLoaded(profile);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -135,12 +183,16 @@ void InputController::saveConfiguration() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void InputController::saveConfiguration(uint32_t type) {
|
void InputController::saveConfiguration(uint32_t type) {
|
||||||
mInputMapSave(&m_inputMap, type, m_config->input());
|
for (auto& inputMap : m_inputMaps) {
|
||||||
|
mInputMapSave(&inputMap, type, m_config->input());
|
||||||
|
}
|
||||||
m_config->write();
|
m_config->write();
|
||||||
}
|
}
|
||||||
|
|
||||||
void InputController::saveProfile(uint32_t type, const QString& profile) {
|
void InputController::saveProfile(uint32_t type, const QString& profile) {
|
||||||
mInputProfileSave(&m_inputMap, type, m_config->input(), profile.toUtf8().constData());
|
for (auto& inputMap : m_inputMaps) {
|
||||||
|
mInputProfileSave(&inputMap, type, m_config->input(), profile.toUtf8().constData());
|
||||||
|
}
|
||||||
m_config->write();
|
m_config->write();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -277,14 +329,6 @@ void InputController::setGyroSensitivity(float sensitivity) {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
GBAKey InputController::mapKeyboard(int key) const {
|
|
||||||
return static_cast<GBAKey>(mInputMapKey(&m_inputMap, KEYBOARD, key));
|
|
||||||
}
|
|
||||||
|
|
||||||
void InputController::bindKey(uint32_t type, int key, GBAKey gbaKey) {
|
|
||||||
return mInputBindKey(&m_inputMap, type, key, gbaKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
void InputController::updateJoysticks() {
|
void InputController::updateJoysticks() {
|
||||||
#ifdef BUILD_SDL
|
#ifdef BUILD_SDL
|
||||||
QString profile = profileForType(SDL_BINDING_BUTTON);
|
QString profile = profileForType(SDL_BINDING_BUTTON);
|
||||||
|
@ -296,6 +340,10 @@ void InputController::updateJoysticks() {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const mInputMap* InputController::map() {
|
||||||
|
return &m_inputMaps[m_platform];
|
||||||
|
}
|
||||||
|
|
||||||
int InputController::pollEvents() {
|
int InputController::pollEvents() {
|
||||||
int activeButtons = 0;
|
int activeButtons = 0;
|
||||||
#ifdef BUILD_SDL
|
#ifdef BUILD_SDL
|
||||||
|
@ -305,7 +353,7 @@ int InputController::pollEvents() {
|
||||||
int numButtons = SDL_JoystickNumButtons(joystick);
|
int numButtons = SDL_JoystickNumButtons(joystick);
|
||||||
int i;
|
int i;
|
||||||
for (i = 0; i < numButtons; ++i) {
|
for (i = 0; i < numButtons; ++i) {
|
||||||
GBAKey key = static_cast<GBAKey>(mInputMapKey(&m_inputMap, SDL_BINDING_BUTTON, i));
|
GBAKey key = static_cast<GBAKey>(mInputMapKey(&m_inputMaps[m_platform], SDL_BINDING_BUTTON, i));
|
||||||
if (key == GBA_KEY_NONE) {
|
if (key == GBA_KEY_NONE) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -319,14 +367,14 @@ int InputController::pollEvents() {
|
||||||
int numHats = SDL_JoystickNumHats(joystick);
|
int numHats = SDL_JoystickNumHats(joystick);
|
||||||
for (i = 0; i < numHats; ++i) {
|
for (i = 0; i < numHats; ++i) {
|
||||||
int hat = SDL_JoystickGetHat(joystick, i);
|
int hat = SDL_JoystickGetHat(joystick, i);
|
||||||
activeButtons |= mInputMapHat(&m_inputMap, SDL_BINDING_BUTTON, i, hat);
|
activeButtons |= mInputMapHat(&m_inputMaps[m_platform], SDL_BINDING_BUTTON, i, hat);
|
||||||
}
|
}
|
||||||
|
|
||||||
int numAxes = SDL_JoystickNumAxes(joystick);
|
int numAxes = SDL_JoystickNumAxes(joystick);
|
||||||
for (i = 0; i < numAxes; ++i) {
|
for (i = 0; i < numAxes; ++i) {
|
||||||
int value = SDL_JoystickGetAxis(joystick, i);
|
int value = SDL_JoystickGetAxis(joystick, i);
|
||||||
|
|
||||||
enum GBAKey key = static_cast<GBAKey>(mInputMapAxis(&m_inputMap, SDL_BINDING_BUTTON, i, value));
|
enum GBAKey key = static_cast<GBAKey>(mInputMapAxis(&m_inputMaps[m_platform], SDL_BINDING_BUTTON, i, value));
|
||||||
if (key != GBA_KEY_NONE) {
|
if (key != GBA_KEY_NONE) {
|
||||||
activeButtons |= 1 << key;
|
activeButtons |= 1 << key;
|
||||||
}
|
}
|
||||||
|
@ -396,8 +444,25 @@ QSet<QPair<int, GamepadAxisEvent::Direction>> InputController::activeGamepadAxes
|
||||||
return activeAxes;
|
return activeAxes;
|
||||||
}
|
}
|
||||||
|
|
||||||
void InputController::bindAxis(uint32_t type, int axis, GamepadAxisEvent::Direction direction, GBAKey key) {
|
void InputController::bindKey(mPlatform platform, uint32_t type, int key, int coreKey) {
|
||||||
const mInputAxis* old = mInputQueryAxis(&m_inputMap, type, axis);
|
QModelIndex index = m_inputModel->index(coreKey, 0, m_inputMenuIndices[platform]);
|
||||||
|
bool signalsBlocked = m_inputModel->blockSignals(true);
|
||||||
|
if (type != KEYBOARD) {
|
||||||
|
m_inputModel->updateButton(index, key);
|
||||||
|
} else {
|
||||||
|
m_inputModel->updateKey(index, key);
|
||||||
|
}
|
||||||
|
m_inputModel->blockSignals(signalsBlocked);
|
||||||
|
mInputBindKey(&m_inputMaps[platform], type, key, coreKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
void InputController::bindAxis(mPlatform platform, uint32_t type, int axis, GamepadAxisEvent::Direction direction, int key) {
|
||||||
|
QModelIndex index = m_inputModel->index(key, 0, m_inputMenuIndices[platform]);
|
||||||
|
bool signalsBlocked = m_inputModel->blockSignals(true);
|
||||||
|
m_inputModel->updateAxis(index, axis, direction);
|
||||||
|
m_inputModel->blockSignals(signalsBlocked);
|
||||||
|
|
||||||
|
const mInputAxis* old = mInputQueryAxis(&m_inputMaps[platform], type, axis);
|
||||||
mInputAxis description = { GBA_KEY_NONE, GBA_KEY_NONE, -AXIS_THRESHOLD, AXIS_THRESHOLD };
|
mInputAxis description = { GBA_KEY_NONE, GBA_KEY_NONE, -AXIS_THRESHOLD, AXIS_THRESHOLD };
|
||||||
if (old) {
|
if (old) {
|
||||||
description = *old;
|
description = *old;
|
||||||
|
@ -419,11 +484,7 @@ void InputController::bindAxis(uint32_t type, int axis, GamepadAxisEvent::Direct
|
||||||
default:
|
default:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
mInputBindAxis(&m_inputMap, type, axis, &description);
|
mInputBindAxis(&m_inputMaps[platform], type, axis, &description);
|
||||||
}
|
|
||||||
|
|
||||||
void InputController::unbindAllAxes(uint32_t type) {
|
|
||||||
mInputUnbindAllAxes(&m_inputMap, type);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QSet<QPair<int, GamepadHatEvent::Direction>> InputController::activeGamepadHats(int type) {
|
QSet<QPair<int, GamepadHatEvent::Direction>> InputController::activeGamepadHats(int type) {
|
||||||
|
@ -458,26 +519,29 @@ QSet<QPair<int, GamepadHatEvent::Direction>> InputController::activeGamepadHats(
|
||||||
return activeHats;
|
return activeHats;
|
||||||
}
|
}
|
||||||
|
|
||||||
void InputController::bindHat(uint32_t type, int hat, GamepadHatEvent::Direction direction, GBAKey gbaKey) {
|
void InputController::bindHat(mPlatform platform, uint32_t type, int hat, GamepadHatEvent::Direction direction, int coreKey) {
|
||||||
|
QModelIndex index = m_inputModel->index(coreKey, 0, m_inputMenuIndices[platform]);
|
||||||
|
//m_inputModel->updateHat(index, hat, direction);
|
||||||
|
|
||||||
mInputHatBindings bindings{ -1, -1, -1, -1 };
|
mInputHatBindings bindings{ -1, -1, -1, -1 };
|
||||||
mInputQueryHat(&m_inputMap, type, hat, &bindings);
|
mInputQueryHat(&m_inputMaps[platform], type, hat, &bindings);
|
||||||
switch (direction) {
|
switch (direction) {
|
||||||
case GamepadHatEvent::UP:
|
case GamepadHatEvent::UP:
|
||||||
bindings.up = gbaKey;
|
bindings.up = coreKey;
|
||||||
break;
|
break;
|
||||||
case GamepadHatEvent::RIGHT:
|
case GamepadHatEvent::RIGHT:
|
||||||
bindings.right = gbaKey;
|
bindings.right = coreKey;
|
||||||
break;
|
break;
|
||||||
case GamepadHatEvent::DOWN:
|
case GamepadHatEvent::DOWN:
|
||||||
bindings.down = gbaKey;
|
bindings.down = coreKey;
|
||||||
break;
|
break;
|
||||||
case GamepadHatEvent::LEFT:
|
case GamepadHatEvent::LEFT:
|
||||||
bindings.left = gbaKey;
|
bindings.left = coreKey;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
mInputBindHat(&m_inputMap, type, hat, &bindings);
|
mInputBindHat(&m_inputMaps[platform], type, hat, &bindings);
|
||||||
}
|
}
|
||||||
|
|
||||||
void InputController::testGamepad(int type) {
|
void InputController::testGamepad(int type) {
|
||||||
|
@ -569,15 +633,15 @@ void InputController::sendGamepadEvent(QEvent* event) {
|
||||||
QApplication::sendEvent(focusWidget, event);
|
QApplication::sendEvent(focusWidget, event);
|
||||||
}
|
}
|
||||||
|
|
||||||
void InputController::postPendingEvent(GBAKey key) {
|
void InputController::postPendingEvent(int key) {
|
||||||
m_pendingEvents.insert(key);
|
m_pendingEvents.insert(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
void InputController::clearPendingEvent(GBAKey key) {
|
void InputController::clearPendingEvent(int key) {
|
||||||
m_pendingEvents.remove(key);
|
m_pendingEvents.remove(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool InputController::hasPendingEvent(GBAKey key) const {
|
bool InputController::hasPendingEvent(int key) const {
|
||||||
return m_pendingEvents.contains(key);
|
return m_pendingEvents.contains(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -614,3 +678,139 @@ void InputController::releaseFocus(QWidget* focus) {
|
||||||
m_focusParent = m_topLevel;
|
m_focusParent = m_topLevel;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void InputController::setupCallback(GameController* controller) {
|
||||||
|
m_inputModel->setKeyCallback([this, controller](QMenu* menu, int key, bool down) {
|
||||||
|
if (menu == m_autofireMenu.get()) {
|
||||||
|
controller->setAutofire(key, down);
|
||||||
|
} else {
|
||||||
|
if (down) {
|
||||||
|
controller->keyPressed(key);
|
||||||
|
} else {
|
||||||
|
controller->keyReleased(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void InputController::bindKey(const QModelIndex& index, int key) {
|
||||||
|
int coreKey = m_inputModel->keyAt(index);
|
||||||
|
if (coreKey < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mPlatform platform = m_inputMenuIndices.key(index.parent(), PLATFORM_NONE);
|
||||||
|
bindKey(platform, KEYBOARD, key, coreKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef BUILD_SDL
|
||||||
|
void InputController::bindButton(const QModelIndex& index, int key) {
|
||||||
|
int coreKey = m_inputModel->keyAt(index);
|
||||||
|
if (coreKey < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mPlatform platform = m_inputMenuIndices.key(index.parent(), PLATFORM_NONE);
|
||||||
|
bindKey(platform, SDL_BINDING_BUTTON, key, coreKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
void InputController::bindAxis(const QModelIndex& index, int axis, GamepadAxisEvent::Direction direction) {
|
||||||
|
int coreKey = m_inputModel->keyAt(index);
|
||||||
|
if (coreKey < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mPlatform platform = m_inputMenuIndices.key(index.parent(), PLATFORM_NONE);
|
||||||
|
bindAxis(platform, SDL_BINDING_BUTTON, axis, direction, coreKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
void InputController::bindHat(const QModelIndex& index, int hat, GamepadHatEvent::Direction direction) {
|
||||||
|
int coreKey = m_inputModel->keyAt(index);
|
||||||
|
if (coreKey < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mPlatform platform = m_inputMenuIndices.key(index.parent(), PLATFORM_NONE);
|
||||||
|
bindHat(platform, SDL_BINDING_BUTTON, hat, direction, coreKey);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
void InputController::bindButton(const QModelIndex& index, int key, int) {}
|
||||||
|
void InputController::bindAxis(const QModelIndex& index, int axis, GamepadAxisEvent::Direction, int) {}
|
||||||
|
void InputController::bindHat(const QModelIndex& index, int hat, GamepadHatEvent::Direction, int) {}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bool InputController::eventFilter(QObject*, QEvent* event) {
|
||||||
|
if (event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease) {
|
||||||
|
QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
|
||||||
|
int key = keyEvent->key();
|
||||||
|
if (!InputModel::isModifierKey(key)) {
|
||||||
|
key |= (keyEvent->modifiers() & ~Qt::KeypadModifier);
|
||||||
|
} else {
|
||||||
|
key = InputModel::toModifierKey(key | (keyEvent->modifiers() & ~Qt::KeypadModifier));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (keyEvent->isAutoRepeat()) {
|
||||||
|
event->accept();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_inputModel->triggerKey(key, event->type() == QEvent::KeyPress, m_platform)) {
|
||||||
|
event->accept();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (event->type() == GamepadButtonEvent::Down() || event->type() == GamepadButtonEvent::Up()) {
|
||||||
|
GamepadButtonEvent* gbe = static_cast<GamepadButtonEvent*>(event);
|
||||||
|
if (m_inputModel->triggerButton(gbe->value(), event->type() == GamepadButtonEvent::Down())) {
|
||||||
|
event->accept();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (event->type() == GamepadAxisEvent::Type()) {
|
||||||
|
GamepadAxisEvent* gae = static_cast<GamepadAxisEvent*>(event);
|
||||||
|
if (m_inputModel->triggerAxis(gae->axis(), gae->direction(), gae->isNew())) {
|
||||||
|
event->accept();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InputController::restoreModel() {
|
||||||
|
bool signalsBlocked = m_inputModel->blockSignals(true);
|
||||||
|
for (auto iter = m_inputMaps.begin(); iter != m_inputMaps.end(); ++iter) {
|
||||||
|
mPlatform platform = iter.key();
|
||||||
|
QModelIndex parent = m_inputMenuIndices[platform];
|
||||||
|
int nKeys = iter->info->nKeys;
|
||||||
|
for (int i = 0; i < nKeys; ++i) {
|
||||||
|
int key = mInputQueryBinding(&iter.value(), KEYBOARD, i);
|
||||||
|
if (key >= 0) {
|
||||||
|
m_inputModel->updateKey(m_inputModel->index(i, 0, parent), key);
|
||||||
|
} else {
|
||||||
|
m_inputModel->clearKey(m_inputModel->index(i, 0, parent));
|
||||||
|
}
|
||||||
|
#ifdef BUILD_SDL
|
||||||
|
key = mInputQueryBinding(&iter.value(), SDL_BINDING_BUTTON, i);
|
||||||
|
if (key >= 0) {
|
||||||
|
m_inputModel->updateButton(m_inputModel->index(i, 0, parent), key);
|
||||||
|
} else {
|
||||||
|
m_inputModel->clearButton(m_inputModel->index(i, 0, parent));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#ifdef BUILD_SDL
|
||||||
|
struct Context {
|
||||||
|
InputModel* model;
|
||||||
|
QModelIndex parent;
|
||||||
|
} context{ m_inputModel, parent };
|
||||||
|
mInputEnumerateAxes(&iter.value(), SDL_BINDING_BUTTON, [](int axis, const struct mInputAxis* description, void* user) {
|
||||||
|
Context* context = static_cast<Context*>(user);
|
||||||
|
if (description->highDirection >= 0) {
|
||||||
|
context->model->updateAxis(context->model->index(description->highDirection, 0, context->parent), axis, GamepadAxisEvent::POSITIVE);
|
||||||
|
}
|
||||||
|
if (description->lowDirection >= 0) {
|
||||||
|
context->model->updateAxis(context->model->index(description->lowDirection, 0, context->parent), axis, GamepadAxisEvent::NEGATIVE);
|
||||||
|
}
|
||||||
|
}, &context);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
m_inputModel->blockSignals(signalsBlocked);
|
||||||
|
}
|
||||||
|
|
|
@ -9,23 +9,29 @@
|
||||||
#include "GamepadAxisEvent.h"
|
#include "GamepadAxisEvent.h"
|
||||||
#include "GamepadHatEvent.h"
|
#include "GamepadHatEvent.h"
|
||||||
|
|
||||||
|
#include <QMap>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QSet>
|
#include <QSet>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <QVector>
|
#include <QVector>
|
||||||
|
|
||||||
#include <mgba/internal/gba/input.h>
|
#include <mgba/core/core.h>
|
||||||
|
#include <mgba/core/input.h>
|
||||||
|
|
||||||
#ifdef BUILD_SDL
|
#ifdef BUILD_SDL
|
||||||
#include "platform/sdl/sdl-events.h"
|
#include "platform/sdl/sdl-events.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
class QMenu;
|
||||||
|
|
||||||
struct mRotationSource;
|
struct mRotationSource;
|
||||||
struct mRumble;
|
struct mRumble;
|
||||||
|
|
||||||
namespace QGBA {
|
namespace QGBA {
|
||||||
|
|
||||||
class ConfigController;
|
class ConfigController;
|
||||||
|
class GameController;
|
||||||
|
class InputModel;
|
||||||
|
|
||||||
class InputController : public QObject {
|
class InputController : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
@ -33,9 +39,12 @@ Q_OBJECT
|
||||||
public:
|
public:
|
||||||
static const uint32_t KEYBOARD = 0x51545F4B;
|
static const uint32_t KEYBOARD = 0x51545F4B;
|
||||||
|
|
||||||
InputController(int playerId = 0, QWidget* topLevel = nullptr, QObject* parent = nullptr);
|
InputController(InputModel* model, int playerId = 0, QWidget* topLevel = nullptr, QObject* parent = nullptr);
|
||||||
~InputController();
|
~InputController();
|
||||||
|
|
||||||
|
void addPlatform(mPlatform, const QString& visibleName, const mInputPlatformInfo*);
|
||||||
|
void setPlatform(mPlatform);
|
||||||
|
|
||||||
void setConfiguration(ConfigController* config);
|
void setConfiguration(ConfigController* config);
|
||||||
void saveConfiguration();
|
void saveConfiguration();
|
||||||
void loadConfiguration(uint32_t type);
|
void loadConfiguration(uint32_t type);
|
||||||
|
@ -47,11 +56,7 @@ public:
|
||||||
bool allowOpposing() const { return m_allowOpposing; }
|
bool allowOpposing() const { return m_allowOpposing; }
|
||||||
void setAllowOpposing(bool allowOpposing) { m_allowOpposing = allowOpposing; }
|
void setAllowOpposing(bool allowOpposing) { m_allowOpposing = allowOpposing; }
|
||||||
|
|
||||||
GBAKey mapKeyboard(int key) const;
|
const mInputMap* map();
|
||||||
|
|
||||||
void bindKey(uint32_t type, int key, GBAKey);
|
|
||||||
|
|
||||||
const mInputMap* map() const { return &m_inputMap; }
|
|
||||||
|
|
||||||
int pollEvents();
|
int pollEvents();
|
||||||
|
|
||||||
|
@ -61,10 +66,9 @@ public:
|
||||||
QSet<QPair<int, GamepadHatEvent::Direction>> activeGamepadHats(int type);
|
QSet<QPair<int, GamepadHatEvent::Direction>> activeGamepadHats(int type);
|
||||||
void recalibrateAxes();
|
void recalibrateAxes();
|
||||||
|
|
||||||
void bindAxis(uint32_t type, int axis, GamepadAxisEvent::Direction, GBAKey);
|
void bindKey(mPlatform platform, uint32_t type, int key, int);
|
||||||
void unbindAllAxes(uint32_t type);
|
void bindAxis(mPlatform platform, uint32_t type, int axis, GamepadAxisEvent::Direction, int);
|
||||||
|
void bindHat(mPlatform platform, uint32_t type, int hat, GamepadHatEvent::Direction, int);
|
||||||
void bindHat(uint32_t type, int hat, GamepadHatEvent::Direction, GBAKey);
|
|
||||||
|
|
||||||
QStringList connectedGamepads(uint32_t type) const;
|
QStringList connectedGamepads(uint32_t type) const;
|
||||||
int gamepad(uint32_t type) const;
|
int gamepad(uint32_t type) const;
|
||||||
|
@ -85,6 +89,8 @@ public:
|
||||||
mRumble* rumble();
|
mRumble* rumble();
|
||||||
mRotationSource* rotationSource();
|
mRotationSource* rotationSource();
|
||||||
|
|
||||||
|
void setupCallback(GameController* controller);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void profileLoaded(const QString& profile);
|
void profileLoaded(const QString& profile);
|
||||||
|
|
||||||
|
@ -97,13 +103,25 @@ public slots:
|
||||||
void resumeScreensaver();
|
void resumeScreensaver();
|
||||||
void setScreensaverSuspendable(bool);
|
void setScreensaverSuspendable(bool);
|
||||||
|
|
||||||
private:
|
private slots:
|
||||||
void postPendingEvent(GBAKey);
|
void bindKey(const QModelIndex&, int key);
|
||||||
void clearPendingEvent(GBAKey);
|
void bindButton(const QModelIndex&, int key);
|
||||||
bool hasPendingEvent(GBAKey) const;
|
void bindAxis(const QModelIndex&, int axis, GamepadAxisEvent::Direction);
|
||||||
void sendGamepadEvent(QEvent*);
|
void bindHat(const QModelIndex&, int hat, GamepadHatEvent::Direction);
|
||||||
|
|
||||||
mInputMap m_inputMap;
|
protected:
|
||||||
|
bool eventFilter(QObject*, QEvent*) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void postPendingEvent(int key);
|
||||||
|
void clearPendingEvent(int key);
|
||||||
|
bool hasPendingEvent(int key) const;
|
||||||
|
void sendGamepadEvent(QEvent*);
|
||||||
|
void restoreModel();
|
||||||
|
|
||||||
|
InputModel* m_inputModel;
|
||||||
|
mPlatform m_platform;
|
||||||
|
QMap<mPlatform, mInputMap> m_inputMaps;
|
||||||
ConfigController* m_config;
|
ConfigController* m_config;
|
||||||
int m_playerId;
|
int m_playerId;
|
||||||
bool m_allowOpposing;
|
bool m_allowOpposing;
|
||||||
|
@ -119,12 +137,16 @@ private:
|
||||||
|
|
||||||
QVector<int> m_deadzones;
|
QVector<int> m_deadzones;
|
||||||
|
|
||||||
|
std::unique_ptr<QMenu> m_inputMenu;
|
||||||
|
std::unique_ptr<QMenu> m_autofireMenu;
|
||||||
|
QMap<mPlatform, QModelIndex> m_inputMenuIndices;
|
||||||
|
|
||||||
QSet<int> m_activeButtons;
|
QSet<int> m_activeButtons;
|
||||||
QSet<QPair<int, GamepadAxisEvent::Direction>> m_activeAxes;
|
QSet<QPair<int, GamepadAxisEvent::Direction>> m_activeAxes;
|
||||||
QSet<QPair<int, GamepadHatEvent::Direction>> m_activeHats;
|
QSet<QPair<int, GamepadHatEvent::Direction>> m_activeHats;
|
||||||
QTimer m_gamepadTimer;
|
QTimer m_gamepadTimer;
|
||||||
|
|
||||||
QSet<GBAKey> m_pendingEvents;
|
QSet<int> m_pendingEvents;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,104 @@
|
||||||
|
/* Copyright (c) 2013-2017 Jeffrey Pfau
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
#include "InputItem.h"
|
||||||
|
|
||||||
|
#include <QMenu>
|
||||||
|
|
||||||
|
using namespace QGBA;
|
||||||
|
|
||||||
|
InputItem::InputItem(QAction* action, const QString& name, InputItem* parent)
|
||||||
|
: m_action(action)
|
||||||
|
, m_shortcut(action->shortcut().isEmpty() ? 0 : action->shortcut()[0])
|
||||||
|
, m_keys(-1)
|
||||||
|
, m_menu(nullptr)
|
||||||
|
, m_name(name)
|
||||||
|
, m_button(-1)
|
||||||
|
, m_axis(-1)
|
||||||
|
, m_direction(GamepadAxisEvent::NEUTRAL)
|
||||||
|
, m_platform(PLATFORM_NONE)
|
||||||
|
, m_parent(parent)
|
||||||
|
{
|
||||||
|
m_visibleName = action->text()
|
||||||
|
.remove(QRegExp("&(?!&)"))
|
||||||
|
.remove("...");
|
||||||
|
}
|
||||||
|
|
||||||
|
InputItem::InputItem(InputItem::Functions functions, int shortcut, const QString& visibleName, const QString& name, InputItem* parent)
|
||||||
|
: m_action(nullptr)
|
||||||
|
, m_shortcut(shortcut)
|
||||||
|
, m_functions(functions)
|
||||||
|
, m_keys(-1)
|
||||||
|
, m_menu(nullptr)
|
||||||
|
, m_name(name)
|
||||||
|
, m_visibleName(visibleName)
|
||||||
|
, m_button(-1)
|
||||||
|
, m_axis(-1)
|
||||||
|
, m_direction(GamepadAxisEvent::NEUTRAL)
|
||||||
|
, m_platform(PLATFORM_NONE)
|
||||||
|
, m_parent(parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
InputItem::InputItem(mPlatform platform, int key, int shortcut, const QString& visibleName, const QString& name, InputItem* parent)
|
||||||
|
: m_action(nullptr)
|
||||||
|
, m_shortcut(shortcut)
|
||||||
|
, m_keys(key)
|
||||||
|
, m_menu(nullptr)
|
||||||
|
, m_name(name)
|
||||||
|
, m_visibleName(visibleName)
|
||||||
|
, m_button(-1)
|
||||||
|
, m_axis(-1)
|
||||||
|
, m_direction(GamepadAxisEvent::NEUTRAL)
|
||||||
|
, m_platform(platform)
|
||||||
|
, m_parent(parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
InputItem::InputItem(QMenu* menu, InputItem* parent)
|
||||||
|
: m_action(nullptr)
|
||||||
|
, m_shortcut(0)
|
||||||
|
, m_menu(menu)
|
||||||
|
, m_button(-1)
|
||||||
|
, m_axis(-1)
|
||||||
|
, m_direction(GamepadAxisEvent::NEUTRAL)
|
||||||
|
, m_parent(parent)
|
||||||
|
{
|
||||||
|
if (menu) {
|
||||||
|
m_visibleName = menu->title()
|
||||||
|
.remove(QRegExp("&(?!&)"))
|
||||||
|
.remove("...");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void InputItem::addAction(QAction* action, const QString& name) {
|
||||||
|
m_items.append(InputItem(action, name, this));
|
||||||
|
}
|
||||||
|
|
||||||
|
void InputItem::addFunctions(InputItem::Functions functions,
|
||||||
|
int shortcut, const QString& visibleName,
|
||||||
|
const QString& name) {
|
||||||
|
m_items.append(InputItem(functions, shortcut, visibleName, name, this));
|
||||||
|
}
|
||||||
|
|
||||||
|
void InputItem::addKey(mPlatform platform, int key, int shortcut, const QString& visibleName, const QString& name) {
|
||||||
|
m_items.append(InputItem(platform, key, shortcut, visibleName, name, this));
|
||||||
|
}
|
||||||
|
|
||||||
|
void InputItem::addSubmenu(QMenu* menu) {
|
||||||
|
m_items.append(InputItem(menu, this));
|
||||||
|
}
|
||||||
|
|
||||||
|
void InputItem::setShortcut(int shortcut) {
|
||||||
|
m_shortcut = shortcut;
|
||||||
|
if (m_action) {
|
||||||
|
m_action->setShortcut(QKeySequence(shortcut));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void InputItem::setAxis(int axis, GamepadAxisEvent::Direction direction) {
|
||||||
|
m_axis = axis;
|
||||||
|
m_direction = direction;
|
||||||
|
}
|
|
@ -0,0 +1,76 @@
|
||||||
|
/* Copyright (c) 2013-2017 Jeffrey Pfau
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
#ifndef QGBA_INPUT_ITEM
|
||||||
|
#define QGBA_INPUT_ITEM
|
||||||
|
|
||||||
|
#include "GamepadAxisEvent.h"
|
||||||
|
#include "GamepadHatEvent.h"
|
||||||
|
|
||||||
|
#include <QAction>
|
||||||
|
|
||||||
|
#include <mgba/core/core.h>
|
||||||
|
|
||||||
|
namespace QGBA {
|
||||||
|
|
||||||
|
class InputItem {
|
||||||
|
public:
|
||||||
|
typedef QPair<std::function<void ()>, std::function<void ()>> Functions;
|
||||||
|
|
||||||
|
InputItem(QAction* action, const QString& name, InputItem* parent = nullptr);
|
||||||
|
InputItem(Functions functions, int shortcut, const QString& visibleName,
|
||||||
|
const QString& name, InputItem* parent = nullptr);
|
||||||
|
InputItem(mPlatform platform, int key, int shortcut, const QString& name, const QString& visibleName, InputItem* parent = nullptr);
|
||||||
|
InputItem(QMenu* action, InputItem* parent = nullptr);
|
||||||
|
|
||||||
|
QAction* action() { return m_action; }
|
||||||
|
const QAction* action() const { return m_action; }
|
||||||
|
int shortcut() const { return m_shortcut; }
|
||||||
|
mPlatform platform() const { return m_platform; }
|
||||||
|
Functions functions() const { return m_functions; }
|
||||||
|
int key() const { return m_keys; }
|
||||||
|
QMenu* menu() { return m_menu; }
|
||||||
|
const QMenu* menu() const { return m_menu; }
|
||||||
|
const QString& visibleName() const { return m_visibleName; }
|
||||||
|
const QString& name() const { return m_name; }
|
||||||
|
QList<InputItem>& items() { return m_items; }
|
||||||
|
const QList<InputItem>& items() const { return m_items; }
|
||||||
|
InputItem* parent() { return m_parent; }
|
||||||
|
const InputItem* parent() const { return m_parent; }
|
||||||
|
void addAction(QAction* action, const QString& name);
|
||||||
|
void addFunctions(Functions functions, int shortcut, const QString& visibleName,
|
||||||
|
const QString& name);
|
||||||
|
void addKey(mPlatform platform, int key, int shortcut, const QString& visibleName, const QString& name);
|
||||||
|
void addSubmenu(QMenu* menu);
|
||||||
|
int button() const { return m_button; }
|
||||||
|
void setShortcut(int sequence);
|
||||||
|
void setButton(int button) { m_button = button; }
|
||||||
|
int axis() const { return m_axis; }
|
||||||
|
GamepadAxisEvent::Direction direction() const { return m_direction; }
|
||||||
|
void setAxis(int axis, GamepadAxisEvent::Direction direction);
|
||||||
|
|
||||||
|
bool operator==(const InputItem& other) const {
|
||||||
|
return m_menu == other.m_menu && m_action == other.m_action;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
QAction* m_action;
|
||||||
|
int m_shortcut;
|
||||||
|
QMenu* m_menu;
|
||||||
|
Functions m_functions;
|
||||||
|
QString m_name;
|
||||||
|
QString m_visibleName;
|
||||||
|
int m_button;
|
||||||
|
int m_axis;
|
||||||
|
int m_keys;
|
||||||
|
GamepadAxisEvent::Direction m_direction;
|
||||||
|
mPlatform m_platform;
|
||||||
|
QList<InputItem> m_items;
|
||||||
|
InputItem* m_parent;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -3,7 +3,7 @@
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
#include "ShortcutController.h"
|
#include "InputModel.h"
|
||||||
|
|
||||||
#include "ConfigController.h"
|
#include "ConfigController.h"
|
||||||
#include "GamepadButtonEvent.h"
|
#include "GamepadButtonEvent.h"
|
||||||
|
@ -15,7 +15,7 @@
|
||||||
|
|
||||||
using namespace QGBA;
|
using namespace QGBA;
|
||||||
|
|
||||||
ShortcutController::ShortcutController(QObject* parent)
|
InputModel::InputModel(QObject* parent)
|
||||||
: QAbstractItemModel(parent)
|
: QAbstractItemModel(parent)
|
||||||
, m_rootMenu(nullptr)
|
, m_rootMenu(nullptr)
|
||||||
, m_config(nullptr)
|
, m_config(nullptr)
|
||||||
|
@ -23,16 +23,16 @@ ShortcutController::ShortcutController(QObject* parent)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShortcutController::setConfigController(ConfigController* controller) {
|
void InputModel::setConfigController(ConfigController* controller) {
|
||||||
m_config = controller;
|
m_config = controller;
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariant ShortcutController::data(const QModelIndex& index, int role) const {
|
QVariant InputModel::data(const QModelIndex& index, int role) const {
|
||||||
if (role != Qt::DisplayRole || !index.isValid()) {
|
if (role != Qt::DisplayRole || !index.isValid()) {
|
||||||
return QVariant();
|
return QVariant();
|
||||||
}
|
}
|
||||||
int row = index.row();
|
int row = index.row();
|
||||||
const ShortcutItem* item = static_cast<const ShortcutItem*>(index.internalPointer());
|
const InputItem* item = static_cast<const InputItem*>(index.internalPointer());
|
||||||
switch (index.column()) {
|
switch (index.column()) {
|
||||||
case 0:
|
case 0:
|
||||||
return item->visibleName();
|
return item->visibleName();
|
||||||
|
@ -57,7 +57,7 @@ QVariant ShortcutController::data(const QModelIndex& index, int role) const {
|
||||||
return QVariant();
|
return QVariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariant ShortcutController::headerData(int section, Qt::Orientation orientation, int role) const {
|
QVariant InputModel::headerData(int section, Qt::Orientation orientation, int role) const {
|
||||||
if (role != Qt::DisplayRole) {
|
if (role != Qt::DisplayRole) {
|
||||||
return QAbstractItemModel::headerData(section, orientation, role);
|
return QAbstractItemModel::headerData(section, orientation, role);
|
||||||
}
|
}
|
||||||
|
@ -74,69 +74,77 @@ QVariant ShortcutController::headerData(int section, Qt::Orientation orientation
|
||||||
return section;
|
return section;
|
||||||
}
|
}
|
||||||
|
|
||||||
QModelIndex ShortcutController::index(int row, int column, const QModelIndex& parent) const {
|
QModelIndex InputModel::index(int row, int column, const QModelIndex& parent) const {
|
||||||
const ShortcutItem* pmenu = &m_rootMenu;
|
const InputItem* pmenu = &m_rootMenu;
|
||||||
if (parent.isValid()) {
|
if (parent.isValid()) {
|
||||||
pmenu = static_cast<ShortcutItem*>(parent.internalPointer());
|
pmenu = static_cast<InputItem*>(parent.internalPointer());
|
||||||
}
|
}
|
||||||
return createIndex(row, column, const_cast<ShortcutItem*>(&pmenu->items()[row]));
|
return createIndex(row, column, const_cast<InputItem*>(&pmenu->items()[row]));
|
||||||
}
|
}
|
||||||
|
|
||||||
QModelIndex ShortcutController::parent(const QModelIndex& index) const {
|
QModelIndex InputModel::parent(const QModelIndex& index) const {
|
||||||
if (!index.isValid() || !index.internalPointer()) {
|
if (!index.isValid() || !index.internalPointer()) {
|
||||||
return QModelIndex();
|
return QModelIndex();
|
||||||
}
|
}
|
||||||
ShortcutItem* item = static_cast<ShortcutItem*>(index.internalPointer());
|
InputItem* item = static_cast<InputItem*>(index.internalPointer());
|
||||||
if (!item->parent() || !item->parent()->parent()) {
|
return this->index(item->parent());
|
||||||
return QModelIndex();
|
|
||||||
}
|
|
||||||
return createIndex(item->parent()->parent()->items().indexOf(*item->parent()), 0, item->parent());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int ShortcutController::columnCount(const QModelIndex& index) const {
|
QModelIndex InputModel::index(InputItem* item) const {
|
||||||
|
if (!item || !item->parent()) {
|
||||||
|
return QModelIndex();
|
||||||
|
}
|
||||||
|
return createIndex(item->parent()->items().indexOf(*item), 0, item);
|
||||||
|
}
|
||||||
|
|
||||||
|
int InputModel::columnCount(const QModelIndex& index) const {
|
||||||
return 3;
|
return 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ShortcutController::rowCount(const QModelIndex& index) const {
|
int InputModel::rowCount(const QModelIndex& index) const {
|
||||||
if (!index.isValid()) {
|
if (!index.isValid()) {
|
||||||
return m_rootMenu.items().count();
|
return m_rootMenu.items().count();
|
||||||
}
|
}
|
||||||
const ShortcutItem* item = static_cast<const ShortcutItem*>(index.internalPointer());
|
const InputItem* item = static_cast<const InputItem*>(index.internalPointer());
|
||||||
return item->items().count();
|
return item->items().count();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShortcutController::addAction(QMenu* menu, QAction* action, const QString& name) {
|
InputItem* InputModel::add(QMenu* menu, std::function<void (InputItem*)> callback) {
|
||||||
ShortcutItem* smenu = m_menuMap[menu];
|
InputItem* smenu = m_menuMap[menu];
|
||||||
if (!smenu) {
|
if (!smenu) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
QModelIndex parent = index(smenu);
|
||||||
|
beginInsertRows(parent, smenu->items().count(), smenu->items().count());
|
||||||
|
callback(smenu);
|
||||||
|
endInsertRows();
|
||||||
|
InputItem* item = &smenu->items().last();
|
||||||
|
emit dataChanged(createIndex(smenu->items().count() - 1, 0, item),
|
||||||
|
createIndex(smenu->items().count() - 1, 2, item));
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InputModel::addAction(QMenu* menu, QAction* action, const QString& name) {
|
||||||
|
InputItem* item = add(menu, [&](InputItem* smenu) {
|
||||||
|
smenu->addAction(action, name);
|
||||||
|
});
|
||||||
|
if (!item) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ShortcutItem* pmenu = smenu->parent();
|
|
||||||
int row = pmenu->items().indexOf(*smenu);
|
|
||||||
QModelIndex parent = createIndex(row, 0, smenu);
|
|
||||||
beginInsertRows(parent, smenu->items().count(), smenu->items().count());
|
|
||||||
smenu->addAction(action, name);
|
|
||||||
endInsertRows();
|
|
||||||
ShortcutItem* item = &smenu->items().last();
|
|
||||||
if (m_config) {
|
if (m_config) {
|
||||||
loadShortcuts(item);
|
loadShortcuts(item);
|
||||||
}
|
}
|
||||||
emit dataChanged(createIndex(smenu->items().count() - 1, 0, item),
|
|
||||||
createIndex(smenu->items().count() - 1, 2, item));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShortcutController::addFunctions(QMenu* menu, std::function<void()> press, std::function<void()> release,
|
void InputModel::addFunctions(QMenu* menu, std::function<void()> press, std::function<void()> release,
|
||||||
int shortcut, const QString& visibleName, const QString& name) {
|
int shortcut, const QString& visibleName, const QString& name) {
|
||||||
ShortcutItem* smenu = m_menuMap[menu];
|
InputItem* item = add(menu, [&](InputItem* smenu) {
|
||||||
if (!smenu) {
|
smenu->addFunctions(qMakePair(press, release), shortcut, visibleName, name);
|
||||||
|
});
|
||||||
|
if (!item) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ShortcutItem* pmenu = smenu->parent();
|
|
||||||
int row = pmenu->items().indexOf(*smenu);
|
|
||||||
QModelIndex parent = createIndex(row, 0, smenu);
|
|
||||||
beginInsertRows(parent, smenu->items().count(), smenu->items().count());
|
|
||||||
smenu->addFunctions(qMakePair(press, release), shortcut, visibleName, name);
|
|
||||||
endInsertRows();
|
|
||||||
ShortcutItem* item = &smenu->items().last();
|
|
||||||
bool loadedShortcut = false;
|
bool loadedShortcut = false;
|
||||||
if (m_config) {
|
if (m_config) {
|
||||||
loadedShortcut = loadShortcuts(item);
|
loadedShortcut = loadShortcuts(item);
|
||||||
|
@ -144,66 +152,92 @@ void ShortcutController::addFunctions(QMenu* menu, std::function<void()> press,
|
||||||
if (!loadedShortcut && !m_heldKeys.contains(shortcut)) {
|
if (!loadedShortcut && !m_heldKeys.contains(shortcut)) {
|
||||||
m_heldKeys[shortcut] = item;
|
m_heldKeys[shortcut] = item;
|
||||||
}
|
}
|
||||||
emit dataChanged(createIndex(smenu->items().count() - 1, 0, item),
|
|
||||||
createIndex(smenu->items().count() - 1, 2, item));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShortcutController::addFunctions(QMenu* menu, std::function<void()> press, std::function<void()> release,
|
void InputModel::addFunctions(QMenu* menu, std::function<void()> press, std::function<void()> release,
|
||||||
const QKeySequence& shortcut, const QString& visibleName, const QString& name) {
|
const QKeySequence& shortcut, const QString& visibleName, const QString& name) {
|
||||||
addFunctions(menu, press, release, shortcut[0], visibleName, name);
|
addFunctions(menu, press, release, shortcut[0], visibleName, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShortcutController::addMenu(QMenu* menu, QMenu* parentMenu) {
|
void InputModel::addKey(QMenu* menu, mPlatform platform, int key, int shortcut, const QString& visibleName, const QString& name) {
|
||||||
ShortcutItem* smenu = m_menuMap[parentMenu];
|
InputItem* item = add(menu, [&](InputItem* smenu) {
|
||||||
|
smenu->addKey(platform, key, shortcut, visibleName, name);
|
||||||
|
});
|
||||||
|
if (!item) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_keys[qMakePair(platform, key)] = item;
|
||||||
|
}
|
||||||
|
|
||||||
|
QModelIndex InputModel::addMenu(QMenu* menu, QMenu* parentMenu) {
|
||||||
|
InputItem* smenu = m_menuMap[parentMenu];
|
||||||
if (!smenu) {
|
if (!smenu) {
|
||||||
smenu = &m_rootMenu;
|
smenu = &m_rootMenu;
|
||||||
}
|
}
|
||||||
QModelIndex parent;
|
QModelIndex parent = index(smenu);
|
||||||
ShortcutItem* pmenu = smenu->parent();
|
|
||||||
if (pmenu) {
|
|
||||||
int row = pmenu->items().indexOf(*smenu);
|
|
||||||
parent = createIndex(row, 0, smenu);
|
|
||||||
}
|
|
||||||
beginInsertRows(parent, smenu->items().count(), smenu->items().count());
|
beginInsertRows(parent, smenu->items().count(), smenu->items().count());
|
||||||
smenu->addSubmenu(menu);
|
smenu->addSubmenu(menu);
|
||||||
endInsertRows();
|
endInsertRows();
|
||||||
ShortcutItem* item = &smenu->items().last();
|
InputItem* item = &smenu->items().last();
|
||||||
emit dataChanged(createIndex(smenu->items().count() - 1, 0, item),
|
emit dataChanged(createIndex(smenu->items().count() - 1, 0, item),
|
||||||
createIndex(smenu->items().count() - 1, 2, item));
|
createIndex(smenu->items().count() - 1, 2, item));
|
||||||
m_menuMap[menu] = item;
|
m_menuMap[menu] = item;
|
||||||
|
return index(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
ShortcutController::ShortcutItem* ShortcutController::itemAt(const QModelIndex& index) {
|
InputItem* InputModel::itemAt(const QModelIndex& index) {
|
||||||
if (!index.isValid()) {
|
if (!index.isValid()) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
return static_cast<ShortcutItem*>(index.internalPointer());
|
if (index.internalPointer()) {
|
||||||
|
return static_cast<InputItem*>(index.internalPointer());
|
||||||
|
}
|
||||||
|
if (!index.parent().isValid()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
InputItem* pmenu = static_cast<InputItem*>(index.parent().internalPointer());
|
||||||
|
return &pmenu->items()[index.row()];
|
||||||
}
|
}
|
||||||
|
|
||||||
const ShortcutController::ShortcutItem* ShortcutController::itemAt(const QModelIndex& index) const {
|
const InputItem* InputModel::itemAt(const QModelIndex& index) const {
|
||||||
if (!index.isValid()) {
|
if (!index.isValid()) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
return static_cast<const ShortcutItem*>(index.internalPointer());
|
if (index.internalPointer()) {
|
||||||
|
return static_cast<InputItem*>(index.internalPointer());
|
||||||
|
}
|
||||||
|
if (!index.parent().isValid()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
InputItem* pmenu = static_cast<InputItem*>(index.parent().internalPointer());
|
||||||
|
return &pmenu->items()[index.row()];
|
||||||
}
|
}
|
||||||
|
|
||||||
int ShortcutController::shortcutAt(const QModelIndex& index) const {
|
int InputModel::shortcutAt(const QModelIndex& index) const {
|
||||||
const ShortcutItem* item = itemAt(index);
|
const InputItem* item = itemAt(index);
|
||||||
if (!item) {
|
if (!item) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
return item->shortcut();
|
return item->shortcut();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ShortcutController::isMenuAt(const QModelIndex& index) const {
|
int InputModel::keyAt(const QModelIndex& index) const {
|
||||||
const ShortcutItem* item = itemAt(index);
|
const InputItem* item = itemAt(index);
|
||||||
|
if (!item) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return item->key();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InputModel::isMenuAt(const QModelIndex& index) const {
|
||||||
|
const InputItem* item = itemAt(index);
|
||||||
if (!item) {
|
if (!item) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return item->menu();
|
return item->menu();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShortcutController::updateKey(const QModelIndex& index, int keySequence) {
|
void InputModel::updateKey(const QModelIndex& index, int keySequence) {
|
||||||
if (!index.isValid()) {
|
if (!index.isValid()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -211,30 +245,33 @@ void ShortcutController::updateKey(const QModelIndex& index, int keySequence) {
|
||||||
if (!parent.isValid()) {
|
if (!parent.isValid()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ShortcutItem* item = itemAt(index);
|
InputItem* item = itemAt(index);
|
||||||
updateKey(item, keySequence);
|
updateKey(item, keySequence);
|
||||||
if (m_config) {
|
if (m_config) {
|
||||||
m_config->setQtOption(item->name(), QKeySequence(keySequence).toString(), KEY_SECTION);
|
m_config->setQtOption(item->name(), QKeySequence(keySequence).toString(), KEY_SECTION);
|
||||||
}
|
}
|
||||||
emit dataChanged(createIndex(index.row(), 0, index.internalPointer()),
|
|
||||||
createIndex(index.row(), 2, index.internalPointer()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShortcutController::updateKey(ShortcutItem* item, int keySequence) {
|
void InputModel::updateKey(InputItem* item, int keySequence) {
|
||||||
int oldShortcut = item->shortcut();
|
int oldShortcut = item->shortcut();
|
||||||
if (item->functions().first) {
|
if (item->functions().first || item->key() >= 0) {
|
||||||
if (oldShortcut > 0) {
|
if (oldShortcut > 0) {
|
||||||
m_heldKeys.take(oldShortcut);
|
m_heldKeys.take(oldShortcut);
|
||||||
}
|
}
|
||||||
if (keySequence > 0) {
|
if (keySequence >= 0) {
|
||||||
m_heldKeys[keySequence] = item;
|
m_keys[qMakePair(item->platform(), keySequence)] = item;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
item->setShortcut(keySequence);
|
item->setShortcut(keySequence);
|
||||||
|
|
||||||
|
emit dataChanged(createIndex(index(item).row(), 0, item),
|
||||||
|
createIndex(index(item).row(), 2, item));
|
||||||
|
|
||||||
|
emit keyRebound(index(item), keySequence);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShortcutController::updateButton(const QModelIndex& index, int button) {
|
void InputModel::updateButton(const QModelIndex& index, int button) {
|
||||||
if (!index.isValid()) {
|
if (!index.isValid()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -242,7 +279,7 @@ void ShortcutController::updateButton(const QModelIndex& index, int button) {
|
||||||
if (!parent.isValid()) {
|
if (!parent.isValid()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ShortcutItem* item = itemAt(index);
|
InputItem* item = itemAt(index);
|
||||||
int oldButton = item->button();
|
int oldButton = item->button();
|
||||||
if (oldButton >= 0) {
|
if (oldButton >= 0) {
|
||||||
m_buttons.take(oldButton);
|
m_buttons.take(oldButton);
|
||||||
|
@ -260,9 +297,11 @@ void ShortcutController::updateButton(const QModelIndex& index, int button) {
|
||||||
}
|
}
|
||||||
emit dataChanged(createIndex(index.row(), 0, index.internalPointer()),
|
emit dataChanged(createIndex(index.row(), 0, index.internalPointer()),
|
||||||
createIndex(index.row(), 2, index.internalPointer()));
|
createIndex(index.row(), 2, index.internalPointer()));
|
||||||
|
|
||||||
|
emit buttonRebound(index, button);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShortcutController::updateAxis(const QModelIndex& index, int axis, GamepadAxisEvent::Direction direction) {
|
void InputModel::updateAxis(const QModelIndex& index, int axis, GamepadAxisEvent::Direction direction) {
|
||||||
if (!index.isValid()) {
|
if (!index.isValid()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -270,7 +309,7 @@ void ShortcutController::updateAxis(const QModelIndex& index, int axis, GamepadA
|
||||||
if (!parent.isValid()) {
|
if (!parent.isValid()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ShortcutItem* item = itemAt(index);
|
InputItem* item = itemAt(index);
|
||||||
int oldAxis = item->axis();
|
int oldAxis = item->axis();
|
||||||
GamepadAxisEvent::Direction oldDirection = item->direction();
|
GamepadAxisEvent::Direction oldDirection = item->direction();
|
||||||
if (oldAxis >= 0) {
|
if (oldAxis >= 0) {
|
||||||
|
@ -296,86 +335,28 @@ void ShortcutController::updateAxis(const QModelIndex& index, int axis, GamepadA
|
||||||
}
|
}
|
||||||
emit dataChanged(createIndex(index.row(), 0, index.internalPointer()),
|
emit dataChanged(createIndex(index.row(), 0, index.internalPointer()),
|
||||||
createIndex(index.row(), 2, index.internalPointer()));
|
createIndex(index.row(), 2, index.internalPointer()));
|
||||||
|
|
||||||
|
emit axisRebound(index, axis, direction);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShortcutController::clearKey(const QModelIndex& index) {
|
void InputModel::clearKey(const QModelIndex& index) {
|
||||||
updateKey(index, 0);
|
updateKey(index, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShortcutController::clearButton(const QModelIndex& index) {
|
void InputModel::clearButton(const QModelIndex& index) {
|
||||||
updateButton(index, -1);
|
updateButton(index, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ShortcutController::eventFilter(QObject*, QEvent* event) {
|
bool InputModel::triggerKey(int keySequence, bool down, mPlatform platform) {
|
||||||
if (event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease) {
|
auto key = m_keys.find(qMakePair(platform, keySequence));
|
||||||
QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
|
if (key != m_keys.end()) {
|
||||||
if (keyEvent->isAutoRepeat()) {
|
m_keyCallback(key.value()->parent()->menu(), key.value()->key(), down);
|
||||||
return false;
|
|
||||||
}
|
|
||||||
int key = keyEvent->key();
|
|
||||||
if (!isModifierKey(key)) {
|
|
||||||
key |= (keyEvent->modifiers() & ~Qt::KeypadModifier);
|
|
||||||
} else {
|
|
||||||
key = toModifierKey(key | (keyEvent->modifiers() & ~Qt::KeypadModifier));
|
|
||||||
}
|
|
||||||
auto item = m_heldKeys.find(key);
|
|
||||||
if (item != m_heldKeys.end()) {
|
|
||||||
ShortcutItem::Functions pair = item.value()->functions();
|
|
||||||
if (event->type() == QEvent::KeyPress) {
|
|
||||||
if (pair.first) {
|
|
||||||
pair.first();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (pair.second) {
|
|
||||||
pair.second();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
event->accept();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (event->type() == GamepadButtonEvent::Down()) {
|
|
||||||
auto item = m_buttons.find(static_cast<GamepadButtonEvent*>(event)->value());
|
|
||||||
if (item == m_buttons.end()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
QAction* action = item.value()->action();
|
|
||||||
if (action && action->isEnabled()) {
|
|
||||||
action->trigger();
|
|
||||||
}
|
|
||||||
ShortcutItem::Functions pair = item.value()->functions();
|
|
||||||
if (pair.first) {
|
|
||||||
pair.first();
|
|
||||||
}
|
|
||||||
event->accept();
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (event->type() == GamepadButtonEvent::Up()) {
|
auto heldKey = m_heldKeys.find(keySequence);
|
||||||
auto item = m_buttons.find(static_cast<GamepadButtonEvent*>(event)->value());
|
if (heldKey != m_heldKeys.end()) {
|
||||||
if (item == m_buttons.end()) {
|
auto pair = heldKey.value()->functions();
|
||||||
return false;
|
if (down) {
|
||||||
}
|
|
||||||
ShortcutItem::Functions pair = item.value()->functions();
|
|
||||||
if (pair.second) {
|
|
||||||
pair.second();
|
|
||||||
}
|
|
||||||
event->accept();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (event->type() == GamepadAxisEvent::Type()) {
|
|
||||||
GamepadAxisEvent* gae = static_cast<GamepadAxisEvent*>(event);
|
|
||||||
auto item = m_axes.find(qMakePair(gae->axis(), gae->direction()));
|
|
||||||
if (item == m_axes.end()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (gae->isNew()) {
|
|
||||||
QAction* action = item.value()->action();
|
|
||||||
if (action && action->isEnabled()) {
|
|
||||||
action->trigger();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ShortcutItem::Functions pair = item.value()->functions();
|
|
||||||
if (gae->isNew()) {
|
|
||||||
if (pair.first) {
|
if (pair.first) {
|
||||||
pair.first();
|
pair.first();
|
||||||
}
|
}
|
||||||
|
@ -384,13 +365,59 @@ bool ShortcutController::eventFilter(QObject*, QEvent* event) {
|
||||||
pair.second();
|
pair.second();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
event->accept();
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ShortcutController::loadShortcuts(ShortcutItem* item) {
|
bool InputModel::triggerButton(int button, bool down) {
|
||||||
|
auto item = m_buttons.find(button);
|
||||||
|
if (item == m_buttons.end()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (down) {
|
||||||
|
QAction* action = item.value()->action();
|
||||||
|
if (action && action->isEnabled()) {
|
||||||
|
action->trigger();
|
||||||
|
}
|
||||||
|
auto pair = item.value()->functions();
|
||||||
|
if (pair.first) {
|
||||||
|
pair.first();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
auto pair = item.value()->functions();
|
||||||
|
if (pair.second) {
|
||||||
|
pair.second();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InputModel::triggerAxis(int axis, GamepadAxisEvent::Direction direction, bool isNew) {
|
||||||
|
auto item = m_axes.find(qMakePair(axis, direction));
|
||||||
|
if (item == m_axes.end()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (isNew) {
|
||||||
|
QAction* action = item.value()->action();
|
||||||
|
if (action && action->isEnabled()) {
|
||||||
|
action->trigger();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
auto pair = item.value()->functions();
|
||||||
|
if (isNew) {
|
||||||
|
if (pair.first) {
|
||||||
|
pair.first();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (pair.second) {
|
||||||
|
pair.second();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool InputModel::loadShortcuts(InputItem* item) {
|
||||||
if (item->name().isNull()) {
|
if (item->name().isNull()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -407,7 +434,7 @@ bool ShortcutController::loadShortcuts(ShortcutItem* item) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShortcutController::loadGamepadShortcuts(ShortcutItem* item) {
|
void InputModel::loadGamepadShortcuts(InputItem* item) {
|
||||||
if (item->name().isNull()) {
|
if (item->name().isNull()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -462,22 +489,22 @@ void ShortcutController::loadGamepadShortcuts(ShortcutItem* item) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShortcutController::loadProfile(const QString& profile) {
|
void InputModel::loadProfile(mPlatform platform, const QString& profile) {
|
||||||
m_profileName = profile;
|
m_profileName = profile;
|
||||||
m_profile = InputProfile::findProfile(profile);
|
m_profile = InputProfile::findProfile(platform, profile);
|
||||||
onSubitems(&m_rootMenu, [this](ShortcutItem* item) {
|
onSubitems(&m_rootMenu, [this](InputItem* item) {
|
||||||
loadGamepadShortcuts(item);
|
loadGamepadShortcuts(item);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShortcutController::onSubitems(ShortcutItem* item, std::function<void(ShortcutItem*)> func) {
|
void InputModel::onSubitems(InputItem* item, std::function<void(InputItem*)> func) {
|
||||||
for (ShortcutItem& subitem : item->items()) {
|
for (InputItem& subitem : item->items()) {
|
||||||
func(&subitem);
|
func(&subitem);
|
||||||
onSubitems(&subitem, func);
|
onSubitems(&subitem, func);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int ShortcutController::toModifierShortcut(const QString& shortcut) {
|
int InputModel::toModifierShortcut(const QString& shortcut) {
|
||||||
// Qt doesn't seem to work with raw modifier shortcuts!
|
// Qt doesn't seem to work with raw modifier shortcuts!
|
||||||
QStringList modifiers = shortcut.split('+');
|
QStringList modifiers = shortcut.split('+');
|
||||||
int value = 0;
|
int value = 0;
|
||||||
|
@ -502,7 +529,7 @@ int ShortcutController::toModifierShortcut(const QString& shortcut) {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ShortcutController::isModifierKey(int key) {
|
bool InputModel::isModifierKey(int key) {
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case Qt::Key_Shift:
|
case Qt::Key_Shift:
|
||||||
case Qt::Key_Control:
|
case Qt::Key_Control:
|
||||||
|
@ -514,7 +541,7 @@ bool ShortcutController::isModifierKey(int key) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int ShortcutController::toModifierKey(int key) {
|
int InputModel::toModifierKey(int key) {
|
||||||
int modifiers = key & (Qt::ShiftModifier | Qt::ControlModifier | Qt::AltModifier | Qt::MetaModifier);
|
int modifiers = key & (Qt::ShiftModifier | Qt::ControlModifier | Qt::AltModifier | Qt::MetaModifier);
|
||||||
key ^= modifiers;
|
key ^= modifiers;
|
||||||
switch (key) {
|
switch (key) {
|
||||||
|
@ -534,76 +561,4 @@ int ShortcutController::toModifierKey(int key) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return modifiers;
|
return modifiers;
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
ShortcutController::ShortcutItem::ShortcutItem(QAction* action, const QString& name, ShortcutItem* parent)
|
|
||||||
: m_action(action)
|
|
||||||
, m_shortcut(action->shortcut().isEmpty() ? 0 : action->shortcut()[0])
|
|
||||||
, m_menu(nullptr)
|
|
||||||
, m_name(name)
|
|
||||||
, m_button(-1)
|
|
||||||
, m_axis(-1)
|
|
||||||
, m_direction(GamepadAxisEvent::NEUTRAL)
|
|
||||||
, m_parent(parent)
|
|
||||||
{
|
|
||||||
m_visibleName = action->text()
|
|
||||||
.remove(QRegExp("&(?!&)"))
|
|
||||||
.remove("...");
|
|
||||||
}
|
|
||||||
|
|
||||||
ShortcutController::ShortcutItem::ShortcutItem(ShortcutController::ShortcutItem::Functions functions, int shortcut, const QString& visibleName, const QString& name, ShortcutItem* parent)
|
|
||||||
: m_action(nullptr)
|
|
||||||
, m_shortcut(shortcut)
|
|
||||||
, m_functions(functions)
|
|
||||||
, m_menu(nullptr)
|
|
||||||
, m_name(name)
|
|
||||||
, m_visibleName(visibleName)
|
|
||||||
, m_button(-1)
|
|
||||||
, m_axis(-1)
|
|
||||||
, m_direction(GamepadAxisEvent::NEUTRAL)
|
|
||||||
, m_parent(parent)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
ShortcutController::ShortcutItem::ShortcutItem(QMenu* menu, ShortcutItem* parent)
|
|
||||||
: m_action(nullptr)
|
|
||||||
, m_shortcut(0)
|
|
||||||
, m_menu(menu)
|
|
||||||
, m_button(-1)
|
|
||||||
, m_axis(-1)
|
|
||||||
, m_direction(GamepadAxisEvent::NEUTRAL)
|
|
||||||
, m_parent(parent)
|
|
||||||
{
|
|
||||||
if (menu) {
|
|
||||||
m_visibleName = menu->title()
|
|
||||||
.remove(QRegExp("&(?!&)"))
|
|
||||||
.remove("...");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ShortcutController::ShortcutItem::addAction(QAction* action, const QString& name) {
|
|
||||||
m_items.append(ShortcutItem(action, name, this));
|
|
||||||
}
|
|
||||||
|
|
||||||
void ShortcutController::ShortcutItem::addFunctions(ShortcutController::ShortcutItem::Functions functions,
|
|
||||||
int shortcut, const QString& visibleName,
|
|
||||||
const QString& name) {
|
|
||||||
m_items.append(ShortcutItem(functions, shortcut, visibleName, name, this));
|
|
||||||
}
|
|
||||||
|
|
||||||
void ShortcutController::ShortcutItem::addSubmenu(QMenu* menu) {
|
|
||||||
m_items.append(ShortcutItem(menu, this));
|
|
||||||
}
|
|
||||||
|
|
||||||
void ShortcutController::ShortcutItem::setShortcut(int shortcut) {
|
|
||||||
m_shortcut = shortcut;
|
|
||||||
if (m_action) {
|
|
||||||
m_action->setShortcut(QKeySequence(shortcut));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ShortcutController::ShortcutItem::setAxis(int axis, GamepadAxisEvent::Direction direction) {
|
|
||||||
m_axis = axis;
|
|
||||||
m_direction = direction;
|
|
||||||
}
|
}
|
|
@ -0,0 +1,117 @@
|
||||||
|
/* Copyright (c) 2013-2017 Jeffrey Pfau
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
#ifndef QGBA_INPUT_MODEL
|
||||||
|
#define QGBA_INPUT_MODEL
|
||||||
|
|
||||||
|
#include <mgba/core/core.h>
|
||||||
|
|
||||||
|
#include "GamepadAxisEvent.h"
|
||||||
|
#include "InputItem.h"
|
||||||
|
|
||||||
|
#include <QAbstractItemModel>
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
class QAction;
|
||||||
|
class QKeyEvent;
|
||||||
|
class QMenu;
|
||||||
|
class QString;
|
||||||
|
|
||||||
|
namespace QGBA {
|
||||||
|
|
||||||
|
class ConfigController;
|
||||||
|
class InputProfile;
|
||||||
|
|
||||||
|
class InputModel : public QAbstractItemModel {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
private:
|
||||||
|
constexpr static const char* const KEY_SECTION = "shortcutKey";
|
||||||
|
constexpr static const char* const BUTTON_SECTION = "shortcutButton";
|
||||||
|
constexpr static const char* const AXIS_SECTION = "shortcutAxis";
|
||||||
|
constexpr static const char* const BUTTON_PROFILE_SECTION = "shortcutProfileButton.";
|
||||||
|
constexpr static const char* const AXIS_PROFILE_SECTION = "shortcutProfileAxis.";
|
||||||
|
|
||||||
|
public:
|
||||||
|
InputModel(QObject* parent = nullptr);
|
||||||
|
|
||||||
|
void setConfigController(ConfigController* controller);
|
||||||
|
void setProfile(const QString& profile);
|
||||||
|
void setKeyCallback(std::function<void (QMenu*, int, bool)> callback) { m_keyCallback = callback; }
|
||||||
|
|
||||||
|
virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
|
||||||
|
virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
|
||||||
|
|
||||||
|
virtual QModelIndex index(int row, int column, const QModelIndex& parent) const override;
|
||||||
|
virtual QModelIndex parent(const QModelIndex& index) const override;
|
||||||
|
|
||||||
|
virtual int columnCount(const QModelIndex& parent = QModelIndex()) const override;
|
||||||
|
virtual int rowCount(const QModelIndex& parent = QModelIndex()) const override;
|
||||||
|
|
||||||
|
void addAction(QMenu* menu, QAction* action, const QString& name);
|
||||||
|
void addFunctions(QMenu* menu, std::function<void()> press, std::function<void()> release,
|
||||||
|
int shortcut, const QString& visibleName, const QString& name);
|
||||||
|
void addFunctions(QMenu* menu, std::function<void()> press, std::function<void()> release,
|
||||||
|
const QKeySequence& shortcut, const QString& visibleName, const QString& name);
|
||||||
|
void addKey(QMenu* menu, mPlatform platform, int key, int shortcut, const QString& visibleName, const QString& name);
|
||||||
|
QModelIndex addMenu(QMenu* menu, QMenu* parent = nullptr);
|
||||||
|
|
||||||
|
QAction* getAction(const QString& name);
|
||||||
|
int shortcutAt(const QModelIndex& index) const;
|
||||||
|
int keyAt(const QModelIndex& index) const;
|
||||||
|
bool isMenuAt(const QModelIndex& index) const;
|
||||||
|
|
||||||
|
void updateKey(const QModelIndex& index, int keySequence);
|
||||||
|
void updateButton(const QModelIndex& index, int button);
|
||||||
|
void updateAxis(const QModelIndex& index, int axis, GamepadAxisEvent::Direction);
|
||||||
|
void updateHat(const QModelIndex& index, int hat, GamepadHatEvent::Direction);
|
||||||
|
|
||||||
|
void clearKey(const QModelIndex& index);
|
||||||
|
void clearButton(const QModelIndex& index);
|
||||||
|
|
||||||
|
static int toModifierShortcut(const QString& shortcut);
|
||||||
|
static bool isModifierKey(int key);
|
||||||
|
static int toModifierKey(int key);
|
||||||
|
|
||||||
|
void loadProfile(mPlatform platform, const QString& profile);
|
||||||
|
|
||||||
|
bool triggerKey(int keySequence, bool down, mPlatform platform = PLATFORM_NONE);
|
||||||
|
bool triggerButton(int button, bool down);
|
||||||
|
bool triggerAxis(int axis, GamepadAxisEvent::Direction, bool isNew);
|
||||||
|
bool triggerHat(int hat, GamepadHatEvent::Direction);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void keyRebound(const QModelIndex&, int keySequence);
|
||||||
|
void buttonRebound(const QModelIndex&, int button);
|
||||||
|
void axisRebound(const QModelIndex& index, int axis, GamepadAxisEvent::Direction);
|
||||||
|
void hatRebound(const QModelIndex& index, int hat, GamepadHatEvent::Direction);
|
||||||
|
|
||||||
|
private:
|
||||||
|
InputItem* add(QMenu* menu, std::function<void (InputItem*)>);
|
||||||
|
InputItem* itemAt(const QModelIndex& index);
|
||||||
|
const InputItem* itemAt(const QModelIndex& index) const;
|
||||||
|
bool loadShortcuts(InputItem*);
|
||||||
|
void loadGamepadShortcuts(InputItem*);
|
||||||
|
void onSubitems(InputItem*, std::function<void(InputItem*)> func);
|
||||||
|
void updateKey(InputItem* item, int keySequence);
|
||||||
|
|
||||||
|
QModelIndex index(InputItem* item) const;
|
||||||
|
|
||||||
|
InputItem m_rootMenu;
|
||||||
|
QMap<QMenu*, InputItem*> m_menuMap;
|
||||||
|
QMap<int, InputItem*> m_buttons;
|
||||||
|
QMap<QPair<int, GamepadAxisEvent::Direction>, InputItem*> m_axes;
|
||||||
|
QMap<int, InputItem*> m_heldKeys;
|
||||||
|
QMap<QPair<mPlatform, int>, InputItem*> m_keys;
|
||||||
|
ConfigController* m_config;
|
||||||
|
std::function<void (QMenu*, int key, bool down)> m_keyCallback;
|
||||||
|
QString m_profileName;
|
||||||
|
const InputProfile* m_profile;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -199,7 +199,8 @@ constexpr InputProfile::InputProfile(const char* name,
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
const InputProfile* InputProfile::findProfile(const QString& name) {
|
const InputProfile* InputProfile::findProfile(mPlatform platform, const QString& name) {
|
||||||
|
// TODO: Use platform
|
||||||
for (size_t i = 0; i < sizeof(s_defaultMaps) / sizeof(*s_defaultMaps); ++i) {
|
for (size_t i = 0; i < sizeof(s_defaultMaps) / sizeof(*s_defaultMaps); ++i) {
|
||||||
QRegExp re(s_defaultMaps[i].m_profileName);
|
QRegExp re(s_defaultMaps[i].m_profileName);
|
||||||
if (re.exactMatch(name)) {
|
if (re.exactMatch(name)) {
|
||||||
|
@ -209,11 +210,11 @@ const InputProfile* InputProfile::findProfile(const QString& name) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void InputProfile::apply(InputController* controller) const {
|
void InputProfile::apply(mPlatform platform, InputController* controller) const {
|
||||||
for (size_t i = 0; i < GBA_KEY_MAX; ++i) {
|
for (size_t i = 0; i < GBA_KEY_MAX; ++i) {
|
||||||
#ifdef BUILD_SDL
|
#ifdef BUILD_SDL
|
||||||
controller->bindKey(SDL_BINDING_BUTTON, m_keys[i], static_cast<GBAKey>(i));
|
controller->bindKey(platform, SDL_BINDING_BUTTON, m_keys[i], static_cast<GBAKey>(i));
|
||||||
controller->bindAxis(SDL_BINDING_BUTTON, m_axes[i].axis, m_axes[i].direction, static_cast<GBAKey>(i));
|
controller->bindAxis(platform, SDL_BINDING_BUTTON, m_axes[i].axis, m_axes[i].direction, static_cast<GBAKey>(i));
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
controller->registerTiltAxisX(m_tiltAxis.x);
|
controller->registerTiltAxisX(m_tiltAxis.x);
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
#include "GamepadAxisEvent.h"
|
#include "GamepadAxisEvent.h"
|
||||||
|
|
||||||
|
#include <mgba/core/core.h>
|
||||||
#include <mgba/gba/interface.h>
|
#include <mgba/gba/interface.h>
|
||||||
|
|
||||||
namespace QGBA {
|
namespace QGBA {
|
||||||
|
@ -16,9 +17,9 @@ class InputController;
|
||||||
|
|
||||||
class InputProfile {
|
class InputProfile {
|
||||||
public:
|
public:
|
||||||
static const InputProfile* findProfile(const QString& name);
|
static const InputProfile* findProfile(mPlatform platform, const QString& name);
|
||||||
|
|
||||||
void apply(InputController*) const;
|
void apply(mPlatform platform, InputController*) const;
|
||||||
bool lookupShortcutButton(const QString& shortcut, int* button) const;
|
bool lookupShortcutButton(const QString& shortcut, int* button) const;
|
||||||
bool lookupShortcutAxis(const QString& shortcut, int* axis, GamepadAxisEvent::Direction* direction) const;
|
bool lookupShortcutAxis(const QString& shortcut, int* axis, GamepadAxisEvent::Direction* direction) const;
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
#include "GamepadAxisEvent.h"
|
#include "GamepadAxisEvent.h"
|
||||||
#include "GamepadButtonEvent.h"
|
#include "GamepadButtonEvent.h"
|
||||||
#include "ShortcutController.h"
|
#include "InputModel.h"
|
||||||
|
|
||||||
#include <QFontMetrics>
|
#include <QFontMetrics>
|
||||||
#include <QKeyEvent>
|
#include <QKeyEvent>
|
||||||
|
@ -103,7 +103,7 @@ void KeyEditor::keyPressEvent(QKeyEvent* event) {
|
||||||
}
|
}
|
||||||
m_lastKey.start(KEY_TIME);
|
m_lastKey.start(KEY_TIME);
|
||||||
if (m_key) {
|
if (m_key) {
|
||||||
if (ShortcutController::isModifierKey(m_key)) {
|
if (InputModel::isModifierKey(m_key)) {
|
||||||
switch (event->key()) {
|
switch (event->key()) {
|
||||||
case Qt::Key_Shift:
|
case Qt::Key_Shift:
|
||||||
setValue(Qt::ShiftModifier);
|
setValue(Qt::ShiftModifier);
|
||||||
|
@ -119,7 +119,7 @@ void KeyEditor::keyPressEvent(QKeyEvent* event) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (ShortcutController::isModifierKey(event->key())) {
|
if (InputModel::isModifierKey(event->key())) {
|
||||||
switch (event->key()) {
|
switch (event->key()) {
|
||||||
case Qt::Key_Shift:
|
case Qt::Key_Shift:
|
||||||
setValue(m_key | Qt::ShiftModifier);
|
setValue(m_key | Qt::ShiftModifier);
|
||||||
|
|
|
@ -9,7 +9,6 @@
|
||||||
#include "ConfigController.h"
|
#include "ConfigController.h"
|
||||||
#include "Display.h"
|
#include "Display.h"
|
||||||
#include "GBAApp.h"
|
#include "GBAApp.h"
|
||||||
#include "GBAKeyEditor.h"
|
|
||||||
#include "InputController.h"
|
#include "InputController.h"
|
||||||
#include "ShortcutView.h"
|
#include "ShortcutView.h"
|
||||||
|
|
||||||
|
@ -18,7 +17,7 @@
|
||||||
|
|
||||||
using namespace QGBA;
|
using namespace QGBA;
|
||||||
|
|
||||||
SettingsView::SettingsView(ConfigController* controller, InputController* inputController, ShortcutController* shortcutController, QWidget* parent)
|
SettingsView::SettingsView(ConfigController* controller, InputController* inputController, InputModel* inputModel, QWidget* parent)
|
||||||
: QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint)
|
: QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint)
|
||||||
, m_controller(controller)
|
, m_controller(controller)
|
||||||
{
|
{
|
||||||
|
@ -146,37 +145,13 @@ SettingsView::SettingsView(ConfigController* controller, InputController* inputC
|
||||||
selectBios(m_ui.gbcBios);
|
selectBios(m_ui.gbcBios);
|
||||||
});
|
});
|
||||||
|
|
||||||
GBAKeyEditor* editor = new GBAKeyEditor(inputController, InputController::KEYBOARD, QString(), this);
|
|
||||||
m_ui.stackedWidget->addWidget(editor);
|
|
||||||
m_ui.tabs->addItem(tr("Keyboard"));
|
|
||||||
connect(m_ui.buttonBox, SIGNAL(accepted()), editor, SLOT(save()));
|
|
||||||
|
|
||||||
GBAKeyEditor* buttonEditor = nullptr;
|
|
||||||
#ifdef BUILD_SDL
|
|
||||||
inputController->recalibrateAxes();
|
|
||||||
const char* profile = inputController->profileForType(SDL_BINDING_BUTTON);
|
|
||||||
buttonEditor = new GBAKeyEditor(inputController, SDL_BINDING_BUTTON, profile);
|
|
||||||
m_ui.stackedWidget->addWidget(buttonEditor);
|
|
||||||
m_ui.tabs->addItem(tr("Controllers"));
|
|
||||||
connect(m_ui.buttonBox, SIGNAL(accepted()), buttonEditor, SLOT(save()));
|
|
||||||
#endif
|
|
||||||
|
|
||||||
connect(m_ui.buttonBox, SIGNAL(accepted()), this, SLOT(updateConfig()));
|
connect(m_ui.buttonBox, SIGNAL(accepted()), this, SLOT(updateConfig()));
|
||||||
connect(m_ui.buttonBox, &QDialogButtonBox::clicked, [this, editor, buttonEditor](QAbstractButton* button) {
|
|
||||||
if (m_ui.buttonBox->buttonRole(button) == QDialogButtonBox::ApplyRole) {
|
|
||||||
updateConfig();
|
|
||||||
editor->save();
|
|
||||||
if (buttonEditor) {
|
|
||||||
buttonEditor->save();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
ShortcutView* shortcutView = new ShortcutView();
|
ShortcutView* shortcutView = new ShortcutView();
|
||||||
shortcutView->setController(shortcutController);
|
shortcutView->setModel(inputModel);
|
||||||
shortcutView->setInputController(inputController);
|
shortcutView->setInputController(inputController);
|
||||||
m_ui.stackedWidget->addWidget(shortcutView);
|
m_ui.stackedWidget->addWidget(shortcutView);
|
||||||
m_ui.tabs->addItem(tr("Shortcuts"));
|
m_ui.tabs->addItem(tr("Bindings"));
|
||||||
}
|
}
|
||||||
|
|
||||||
void SettingsView::selectBios(QLineEdit* bios) {
|
void SettingsView::selectBios(QLineEdit* bios) {
|
||||||
|
|
|
@ -16,13 +16,13 @@ namespace QGBA {
|
||||||
|
|
||||||
class ConfigController;
|
class ConfigController;
|
||||||
class InputController;
|
class InputController;
|
||||||
class ShortcutController;
|
class InputModel;
|
||||||
|
|
||||||
class SettingsView : public QDialog {
|
class SettingsView : public QDialog {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
SettingsView(ConfigController* controller, InputController* inputController, ShortcutController* shortcutController, QWidget* parent = nullptr);
|
SettingsView(ConfigController* controller, InputController* inputController, InputModel* inputModel, QWidget* parent = nullptr);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void biosLoaded(int platform, const QString&);
|
void biosLoaded(int platform, const QString&);
|
||||||
|
|
|
@ -1,148 +0,0 @@
|
||||||
/* Copyright (c) 2013-2015 Jeffrey Pfau
|
|
||||||
*
|
|
||||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
||||||
#ifndef QGBA_SHORTCUT_MODEL
|
|
||||||
#define QGBA_SHORTCUT_MODEL
|
|
||||||
|
|
||||||
#include "GamepadAxisEvent.h"
|
|
||||||
|
|
||||||
#include <QAbstractItemModel>
|
|
||||||
|
|
||||||
#include <functional>
|
|
||||||
|
|
||||||
class QAction;
|
|
||||||
class QKeyEvent;
|
|
||||||
class QMenu;
|
|
||||||
class QString;
|
|
||||||
|
|
||||||
namespace QGBA {
|
|
||||||
|
|
||||||
class ConfigController;
|
|
||||||
class InputProfile;
|
|
||||||
|
|
||||||
class ShortcutController : public QAbstractItemModel {
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
private:
|
|
||||||
constexpr static const char* const KEY_SECTION = "shortcutKey";
|
|
||||||
constexpr static const char* const BUTTON_SECTION = "shortcutButton";
|
|
||||||
constexpr static const char* const AXIS_SECTION = "shortcutAxis";
|
|
||||||
constexpr static const char* const BUTTON_PROFILE_SECTION = "shortcutProfileButton.";
|
|
||||||
constexpr static const char* const AXIS_PROFILE_SECTION = "shortcutProfileAxis.";
|
|
||||||
|
|
||||||
class ShortcutItem {
|
|
||||||
public:
|
|
||||||
typedef QPair<std::function<void ()>, std::function<void ()>> Functions;
|
|
||||||
|
|
||||||
ShortcutItem(QAction* action, const QString& name, ShortcutItem* parent = nullptr);
|
|
||||||
ShortcutItem(Functions functions, int shortcut, const QString& visibleName, const QString& name,
|
|
||||||
ShortcutItem* parent = nullptr);
|
|
||||||
ShortcutItem(QMenu* action, ShortcutItem* parent = nullptr);
|
|
||||||
|
|
||||||
QAction* action() { return m_action; }
|
|
||||||
const QAction* action() const { return m_action; }
|
|
||||||
const int shortcut() const { return m_shortcut; }
|
|
||||||
Functions functions() const { return m_functions; }
|
|
||||||
QMenu* menu() { return m_menu; }
|
|
||||||
const QMenu* menu() const { return m_menu; }
|
|
||||||
const QString& visibleName() const { return m_visibleName; }
|
|
||||||
const QString& name() const { return m_name; }
|
|
||||||
QList<ShortcutItem>& items() { return m_items; }
|
|
||||||
const QList<ShortcutItem>& items() const { return m_items; }
|
|
||||||
ShortcutItem* parent() { return m_parent; }
|
|
||||||
const ShortcutItem* parent() const { return m_parent; }
|
|
||||||
void addAction(QAction* action, const QString& name);
|
|
||||||
void addFunctions(Functions functions, int shortcut, const QString& visibleName,
|
|
||||||
const QString& name);
|
|
||||||
void addSubmenu(QMenu* menu);
|
|
||||||
int button() const { return m_button; }
|
|
||||||
void setShortcut(int sequence);
|
|
||||||
void setButton(int button) { m_button = button; }
|
|
||||||
int axis() const { return m_axis; }
|
|
||||||
GamepadAxisEvent::Direction direction() const { return m_direction; }
|
|
||||||
void setAxis(int axis, GamepadAxisEvent::Direction direction);
|
|
||||||
|
|
||||||
bool operator==(const ShortcutItem& other) const {
|
|
||||||
return m_menu == other.m_menu && m_action == other.m_action;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
QAction* m_action;
|
|
||||||
int m_shortcut;
|
|
||||||
QMenu* m_menu;
|
|
||||||
Functions m_functions;
|
|
||||||
QString m_name;
|
|
||||||
QString m_visibleName;
|
|
||||||
int m_button;
|
|
||||||
int m_axis;
|
|
||||||
GamepadAxisEvent::Direction m_direction;
|
|
||||||
QList<ShortcutItem> m_items;
|
|
||||||
ShortcutItem* m_parent;
|
|
||||||
};
|
|
||||||
|
|
||||||
public:
|
|
||||||
ShortcutController(QObject* parent = nullptr);
|
|
||||||
|
|
||||||
void setConfigController(ConfigController* controller);
|
|
||||||
void setProfile(const QString& profile);
|
|
||||||
|
|
||||||
virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
|
|
||||||
virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
|
|
||||||
|
|
||||||
virtual QModelIndex index(int row, int column, const QModelIndex& parent) const override;
|
|
||||||
virtual QModelIndex parent(const QModelIndex& index) const override;
|
|
||||||
|
|
||||||
virtual int columnCount(const QModelIndex& parent = QModelIndex()) const override;
|
|
||||||
virtual int rowCount(const QModelIndex& parent = QModelIndex()) const override;
|
|
||||||
|
|
||||||
void addAction(QMenu* menu, QAction* action, const QString& name);
|
|
||||||
void addFunctions(QMenu* menu, std::function<void()> press, std::function<void()> release,
|
|
||||||
int shortcut, const QString& visibleName, const QString& name);
|
|
||||||
void addFunctions(QMenu* menu, std::function<void()> press, std::function<void()> release,
|
|
||||||
const QKeySequence& shortcut, const QString& visibleName, const QString& name);
|
|
||||||
void addMenu(QMenu* menu, QMenu* parent = nullptr);
|
|
||||||
|
|
||||||
QAction* getAction(const QString& name);
|
|
||||||
int shortcutAt(const QModelIndex& index) const;
|
|
||||||
bool isMenuAt(const QModelIndex& index) const;
|
|
||||||
|
|
||||||
void updateKey(const QModelIndex& index, int keySequence);
|
|
||||||
void updateButton(const QModelIndex& index, int button);
|
|
||||||
void updateAxis(const QModelIndex& index, int axis, GamepadAxisEvent::Direction direction);
|
|
||||||
|
|
||||||
void clearKey(const QModelIndex& index);
|
|
||||||
void clearButton(const QModelIndex& index);
|
|
||||||
|
|
||||||
static int toModifierShortcut(const QString& shortcut);
|
|
||||||
static bool isModifierKey(int key);
|
|
||||||
static int toModifierKey(int key);
|
|
||||||
|
|
||||||
public slots:
|
|
||||||
void loadProfile(const QString& profile);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
bool eventFilter(QObject*, QEvent*) override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
ShortcutItem* itemAt(const QModelIndex& index);
|
|
||||||
const ShortcutItem* itemAt(const QModelIndex& index) const;
|
|
||||||
bool loadShortcuts(ShortcutItem*);
|
|
||||||
void loadGamepadShortcuts(ShortcutItem*);
|
|
||||||
void onSubitems(ShortcutItem*, std::function<void(ShortcutItem*)> func);
|
|
||||||
void updateKey(ShortcutItem* item, int keySequence);
|
|
||||||
|
|
||||||
ShortcutItem m_rootMenu;
|
|
||||||
QMap<QMenu*, ShortcutItem*> m_menuMap;
|
|
||||||
QMap<int, ShortcutItem*> m_buttons;
|
|
||||||
QMap<QPair<int, GamepadAxisEvent::Direction>, ShortcutItem*> m_axes;
|
|
||||||
QMap<int, ShortcutItem*> m_heldKeys;
|
|
||||||
ConfigController* m_config;
|
|
||||||
QString m_profileName;
|
|
||||||
const InputProfile* m_profile;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
#include "GamepadButtonEvent.h"
|
#include "GamepadButtonEvent.h"
|
||||||
#include "InputController.h"
|
#include "InputController.h"
|
||||||
#include "ShortcutController.h"
|
#include "InputModel.h"
|
||||||
|
|
||||||
#include <QKeyEvent>
|
#include <QKeyEvent>
|
||||||
|
|
||||||
|
@ -41,9 +41,9 @@ ShortcutView::~ShortcutView() {
|
||||||
m_input->releaseFocus(this);
|
m_input->releaseFocus(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShortcutView::setController(ShortcutController* controller) {
|
void ShortcutView::setModel(InputModel* model) {
|
||||||
m_controller = controller;
|
m_controller = model;
|
||||||
m_ui.shortcutTable->setModel(controller);
|
m_ui.shortcutTable->setModel(model);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShortcutView::setInputController(InputController* controller) {
|
void ShortcutView::setInputController(InputController* controller) {
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
namespace QGBA {
|
namespace QGBA {
|
||||||
|
|
||||||
class InputController;
|
class InputController;
|
||||||
class ShortcutController;
|
class InputModel;
|
||||||
|
|
||||||
class ShortcutView : public QWidget {
|
class ShortcutView : public QWidget {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
@ -24,7 +24,7 @@ public:
|
||||||
ShortcutView(QWidget* parent = nullptr);
|
ShortcutView(QWidget* parent = nullptr);
|
||||||
~ShortcutView();
|
~ShortcutView();
|
||||||
|
|
||||||
void setController(ShortcutController* controller);
|
void setModel(InputModel* controller);
|
||||||
void setInputController(InputController* input);
|
void setInputController(InputController* input);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -40,7 +40,7 @@ private slots:
|
||||||
private:
|
private:
|
||||||
Ui::ShortcutView m_ui;
|
Ui::ShortcutView m_ui;
|
||||||
|
|
||||||
ShortcutController* m_controller;
|
InputModel* m_controller;
|
||||||
InputController* m_input;
|
InputController* m_input;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,7 @@
|
||||||
#include "GDBController.h"
|
#include "GDBController.h"
|
||||||
#include "GDBWindow.h"
|
#include "GDBWindow.h"
|
||||||
#include "GIFView.h"
|
#include "GIFView.h"
|
||||||
|
#include "InputModel.h"
|
||||||
#include "IOViewer.h"
|
#include "IOViewer.h"
|
||||||
#include "LoadSaveState.h"
|
#include "LoadSaveState.h"
|
||||||
#include "LogView.h"
|
#include "LogView.h"
|
||||||
|
@ -40,19 +41,23 @@
|
||||||
#include "SensorView.h"
|
#include "SensorView.h"
|
||||||
#include "SettingsView.h"
|
#include "SettingsView.h"
|
||||||
#include "ShaderSelector.h"
|
#include "ShaderSelector.h"
|
||||||
#include "ShortcutController.h"
|
|
||||||
#include "TileView.h"
|
#include "TileView.h"
|
||||||
#include "VideoView.h"
|
#include "VideoView.h"
|
||||||
|
|
||||||
#include <mgba/core/version.h>
|
#include <mgba/core/version.h>
|
||||||
#ifdef M_CORE_GB
|
#ifdef M_CORE_GB
|
||||||
#include <mgba/internal/gb/gb.h>
|
#include <mgba/internal/gb/gb.h>
|
||||||
|
#include <mgba/internal/gb/input.h>
|
||||||
#include <mgba/internal/gb/video.h>
|
#include <mgba/internal/gb/video.h>
|
||||||
#endif
|
#endif
|
||||||
#ifdef M_CORE_GBA
|
#ifdef M_CORE_GBA
|
||||||
#include <mgba/internal/gba/gba.h>
|
#include <mgba/internal/gba/gba.h>
|
||||||
|
#include <mgba/internal/gba/input.h>
|
||||||
#include <mgba/internal/gba/video.h>
|
#include <mgba/internal/gba/video.h>
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef M_CORE_DS
|
||||||
|
#include <mgba/internal/ds/input.h>
|
||||||
|
#endif
|
||||||
#include "feature/commandline.h"
|
#include "feature/commandline.h"
|
||||||
#include "feature/sqlite3/no-intro.h"
|
#include "feature/sqlite3/no-intro.h"
|
||||||
#include <mgba-util/vfs.h>
|
#include <mgba-util/vfs.h>
|
||||||
|
@ -67,7 +72,8 @@ Window::Window(ConfigController* config, int playerId, QWidget* parent)
|
||||||
, m_screenWidget(new WindowBackground())
|
, m_screenWidget(new WindowBackground())
|
||||||
, m_logo(":/res/mgba-1024.png")
|
, m_logo(":/res/mgba-1024.png")
|
||||||
, m_config(config)
|
, m_config(config)
|
||||||
, m_inputController(playerId, this)
|
, m_inputModel(new InputModel(this))
|
||||||
|
, m_inputController(m_inputModel, playerId, this)
|
||||||
#ifdef USE_FFMPEG
|
#ifdef USE_FFMPEG
|
||||||
, m_videoView(nullptr)
|
, m_videoView(nullptr)
|
||||||
#endif
|
#endif
|
||||||
|
@ -81,7 +87,6 @@ Window::Window(ConfigController* config, int playerId, QWidget* parent)
|
||||||
, m_console(nullptr)
|
, m_console(nullptr)
|
||||||
#endif
|
#endif
|
||||||
, m_mruMenu(nullptr)
|
, m_mruMenu(nullptr)
|
||||||
, m_shortcutController(new ShortcutController(this))
|
|
||||||
, m_fullscreenOnStart(false)
|
, m_fullscreenOnStart(false)
|
||||||
, m_autoresume(false)
|
, m_autoresume(false)
|
||||||
, m_wasOpened(false)
|
, m_wasOpened(false)
|
||||||
|
@ -194,14 +199,24 @@ Window::Window(ConfigController* config, int playerId, QWidget* parent)
|
||||||
connect(m_display, &Display::showCursor, [this]() {
|
connect(m_display, &Display::showCursor, [this]() {
|
||||||
m_screenWidget->unsetCursor();
|
m_screenWidget->unsetCursor();
|
||||||
});
|
});
|
||||||
connect(&m_inputController, SIGNAL(profileLoaded(const QString&)), m_shortcutController, SLOT(loadProfile(const QString&)));
|
|
||||||
|
|
||||||
m_log.setLevels(mLOG_WARN | mLOG_ERROR | mLOG_FATAL);
|
m_log.setLevels(mLOG_WARN | mLOG_ERROR | mLOG_FATAL);
|
||||||
m_fpsTimer.setInterval(FPS_TIMER_INTERVAL);
|
m_fpsTimer.setInterval(FPS_TIMER_INTERVAL);
|
||||||
m_focusCheck.setInterval(200);
|
m_focusCheck.setInterval(200);
|
||||||
|
|
||||||
m_shortcutController->setConfigController(m_config);
|
m_inputModel->setConfigController(m_config);
|
||||||
setupMenu(menuBar());
|
setupMenu(menuBar());
|
||||||
|
|
||||||
|
#ifdef M_CORE_GBA
|
||||||
|
m_inputController.addPlatform(PLATFORM_GBA, tr("Game Boy Advance"), &GBAInputInfo);
|
||||||
|
#endif
|
||||||
|
#ifdef M_CORE_GB
|
||||||
|
m_inputController.addPlatform(PLATFORM_GB, tr("Game Boy"), &GBInputInfo);
|
||||||
|
#endif
|
||||||
|
#ifdef M_CORE_DS
|
||||||
|
m_inputController.addPlatform(PLATFORM_DS, tr("DS"), &DSInputInfo);
|
||||||
|
#endif
|
||||||
|
m_inputController.setupCallback(m_controller);
|
||||||
}
|
}
|
||||||
|
|
||||||
Window::~Window() {
|
Window::~Window() {
|
||||||
|
@ -491,7 +506,7 @@ void Window::exportSharkport() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Window::openSettingsWindow() {
|
void Window::openSettingsWindow() {
|
||||||
SettingsView* settingsWindow = new SettingsView(m_config, &m_inputController, m_shortcutController);
|
SettingsView* settingsWindow = new SettingsView(m_config, &m_inputController, m_inputModel);
|
||||||
connect(settingsWindow, SIGNAL(biosLoaded(int, const QString&)), m_controller, SLOT(loadBIOS(int, const QString&)));
|
connect(settingsWindow, SIGNAL(biosLoaded(int, const QString&)), m_controller, SLOT(loadBIOS(int, const QString&)));
|
||||||
connect(settingsWindow, SIGNAL(audioDriverChanged()), m_controller, SLOT(reloadAudioDriver()));
|
connect(settingsWindow, SIGNAL(audioDriverChanged()), m_controller, SLOT(reloadAudioDriver()));
|
||||||
connect(settingsWindow, SIGNAL(displayDriverChanged()), this, SLOT(mustRestart()));
|
connect(settingsWindow, SIGNAL(displayDriverChanged()), this, SLOT(mustRestart()));
|
||||||
|
@ -574,34 +589,6 @@ void Window::consoleOpen() {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void Window::keyPressEvent(QKeyEvent* event) {
|
|
||||||
if (event->isAutoRepeat()) {
|
|
||||||
QWidget::keyPressEvent(event);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
GBAKey key = m_inputController.mapKeyboard(event->key());
|
|
||||||
if (key == GBA_KEY_NONE) {
|
|
||||||
QWidget::keyPressEvent(event);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
m_controller->keyPressed(key);
|
|
||||||
event->accept();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Window::keyReleaseEvent(QKeyEvent* event) {
|
|
||||||
if (event->isAutoRepeat()) {
|
|
||||||
QWidget::keyReleaseEvent(event);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
GBAKey key = m_inputController.mapKeyboard(event->key());
|
|
||||||
if (key == GBA_KEY_NONE) {
|
|
||||||
QWidget::keyPressEvent(event);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
m_controller->keyReleased(key);
|
|
||||||
event->accept();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Window::resizeEvent(QResizeEvent* event) {
|
void Window::resizeEvent(QResizeEvent* event) {
|
||||||
if (!isFullScreen()) {
|
if (!isFullScreen()) {
|
||||||
m_config->setOption("height", m_screenWidget->height());
|
m_config->setOption("height", m_screenWidget->height());
|
||||||
|
@ -800,6 +787,8 @@ void Window::gameStarted(mCoreThread* context, const QString& fname) {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
m_inputController.setPlatform(m_controller->platform());
|
||||||
|
|
||||||
m_hitUnimplementedBiosCall = false;
|
m_hitUnimplementedBiosCall = false;
|
||||||
m_fpsTimer.start();
|
m_fpsTimer.start();
|
||||||
m_focusCheck.start();
|
m_focusCheck.start();
|
||||||
|
@ -968,8 +957,8 @@ void Window::openStateWindow(LoadSave ls) {
|
||||||
void Window::setupMenu(QMenuBar* menubar) {
|
void Window::setupMenu(QMenuBar* menubar) {
|
||||||
menubar->clear();
|
menubar->clear();
|
||||||
QMenu* fileMenu = menubar->addMenu(tr("&File"));
|
QMenu* fileMenu = menubar->addMenu(tr("&File"));
|
||||||
m_shortcutController->addMenu(fileMenu);
|
m_inputModel->addMenu(fileMenu);
|
||||||
installEventFilter(m_shortcutController);
|
installEventFilter(&m_inputController);
|
||||||
addControlledAction(fileMenu, fileMenu->addAction(tr("Load &ROM..."), this, SLOT(selectROM()), QKeySequence::Open),
|
addControlledAction(fileMenu, fileMenu->addAction(tr("Load &ROM..."), this, SLOT(selectROM()), QKeySequence::Open),
|
||||||
"loadROM");
|
"loadROM");
|
||||||
#ifdef USE_SQLITE3
|
#ifdef USE_SQLITE3
|
||||||
|
@ -1024,8 +1013,8 @@ void Window::setupMenu(QMenuBar* menubar) {
|
||||||
|
|
||||||
QMenu* quickLoadMenu = fileMenu->addMenu(tr("Quick load"));
|
QMenu* quickLoadMenu = fileMenu->addMenu(tr("Quick load"));
|
||||||
QMenu* quickSaveMenu = fileMenu->addMenu(tr("Quick save"));
|
QMenu* quickSaveMenu = fileMenu->addMenu(tr("Quick save"));
|
||||||
m_shortcutController->addMenu(quickLoadMenu);
|
m_inputModel->addMenu(quickLoadMenu);
|
||||||
m_shortcutController->addMenu(quickSaveMenu);
|
m_inputModel->addMenu(quickSaveMenu);
|
||||||
|
|
||||||
QAction* quickLoad = new QAction(tr("Load recent"), quickLoadMenu);
|
QAction* quickLoad = new QAction(tr("Load recent"), quickLoadMenu);
|
||||||
connect(quickLoad, SIGNAL(triggered()), m_controller, SLOT(loadState()));
|
connect(quickLoad, SIGNAL(triggered()), m_controller, SLOT(loadState()));
|
||||||
|
@ -1111,7 +1100,7 @@ void Window::setupMenu(QMenuBar* menubar) {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
QMenu* emulationMenu = menubar->addMenu(tr("&Emulation"));
|
QMenu* emulationMenu = menubar->addMenu(tr("&Emulation"));
|
||||||
m_shortcutController->addMenu(emulationMenu);
|
m_inputModel->addMenu(emulationMenu);
|
||||||
QAction* reset = new QAction(tr("&Reset"), emulationMenu);
|
QAction* reset = new QAction(tr("&Reset"), emulationMenu);
|
||||||
reset->setShortcut(tr("Ctrl+R"));
|
reset->setShortcut(tr("Ctrl+R"));
|
||||||
connect(reset, SIGNAL(triggered()), m_controller, SLOT(reset()));
|
connect(reset, SIGNAL(triggered()), m_controller, SLOT(reset()));
|
||||||
|
@ -1152,7 +1141,7 @@ void Window::setupMenu(QMenuBar* menubar) {
|
||||||
|
|
||||||
emulationMenu->addSeparator();
|
emulationMenu->addSeparator();
|
||||||
|
|
||||||
m_shortcutController->addFunctions(emulationMenu, [this]() {
|
m_inputModel->addFunctions(emulationMenu, [this]() {
|
||||||
m_controller->setTurbo(true, false);
|
m_controller->setTurbo(true, false);
|
||||||
}, [this]() {
|
}, [this]() {
|
||||||
m_controller->setTurbo(false, false);
|
m_controller->setTurbo(false, false);
|
||||||
|
@ -1178,7 +1167,7 @@ void Window::setupMenu(QMenuBar* menubar) {
|
||||||
}
|
}
|
||||||
m_config->updateOption("fastForwardRatio");
|
m_config->updateOption("fastForwardRatio");
|
||||||
|
|
||||||
m_shortcutController->addFunctions(emulationMenu, [this]() {
|
m_inputModel->addFunctions(emulationMenu, [this]() {
|
||||||
m_controller->startRewinding();
|
m_controller->startRewinding();
|
||||||
}, [this]() {
|
}, [this]() {
|
||||||
m_controller->stopRewinding();
|
m_controller->stopRewinding();
|
||||||
|
@ -1217,7 +1206,7 @@ void Window::setupMenu(QMenuBar* menubar) {
|
||||||
emulationMenu->addSeparator();
|
emulationMenu->addSeparator();
|
||||||
|
|
||||||
QMenu* solarMenu = emulationMenu->addMenu(tr("Solar sensor"));
|
QMenu* solarMenu = emulationMenu->addMenu(tr("Solar sensor"));
|
||||||
m_shortcutController->addMenu(solarMenu);
|
m_inputModel->addMenu(solarMenu);
|
||||||
QAction* solarIncrease = new QAction(tr("Increase solar level"), solarMenu);
|
QAction* solarIncrease = new QAction(tr("Increase solar level"), solarMenu);
|
||||||
connect(solarIncrease, SIGNAL(triggered()), m_controller, SLOT(increaseLuminanceLevel()));
|
connect(solarIncrease, SIGNAL(triggered()), m_controller, SLOT(increaseLuminanceLevel()));
|
||||||
addControlledAction(solarMenu, solarIncrease, "increaseLuminanceLevel");
|
addControlledAction(solarMenu, solarIncrease, "increaseLuminanceLevel");
|
||||||
|
@ -1244,9 +1233,9 @@ void Window::setupMenu(QMenuBar* menubar) {
|
||||||
}
|
}
|
||||||
|
|
||||||
QMenu* avMenu = menubar->addMenu(tr("Audio/&Video"));
|
QMenu* avMenu = menubar->addMenu(tr("Audio/&Video"));
|
||||||
m_shortcutController->addMenu(avMenu);
|
m_inputModel->addMenu(avMenu);
|
||||||
QMenu* frameMenu = avMenu->addMenu(tr("Frame size"));
|
QMenu* frameMenu = avMenu->addMenu(tr("Frame size"));
|
||||||
m_shortcutController->addMenu(frameMenu, avMenu);
|
m_inputModel->addMenu(frameMenu, avMenu);
|
||||||
for (int i = 1; i <= 6; ++i) {
|
for (int i = 1; i <= 6; ++i) {
|
||||||
QAction* setSize = new QAction(tr("%1x").arg(QString::number(i)), avMenu);
|
QAction* setSize = new QAction(tr("%1x").arg(QString::number(i)), avMenu);
|
||||||
setSize->setCheckable(true);
|
setSize->setCheckable(true);
|
||||||
|
@ -1360,7 +1349,7 @@ void Window::setupMenu(QMenuBar* menubar) {
|
||||||
|
|
||||||
avMenu->addSeparator();
|
avMenu->addSeparator();
|
||||||
QMenu* videoLayers = avMenu->addMenu(tr("Video layers"));
|
QMenu* videoLayers = avMenu->addMenu(tr("Video layers"));
|
||||||
m_shortcutController->addMenu(videoLayers, avMenu);
|
m_inputModel->addMenu(videoLayers, avMenu);
|
||||||
|
|
||||||
for (int i = 0; i < 4; ++i) {
|
for (int i = 0; i < 4; ++i) {
|
||||||
QAction* enableBg = new QAction(tr("Background %0").arg(i), videoLayers);
|
QAction* enableBg = new QAction(tr("Background %0").arg(i), videoLayers);
|
||||||
|
@ -1377,7 +1366,7 @@ void Window::setupMenu(QMenuBar* menubar) {
|
||||||
addControlledAction(videoLayers, enableObj, "enableOBJ");
|
addControlledAction(videoLayers, enableObj, "enableOBJ");
|
||||||
|
|
||||||
QMenu* audioChannels = avMenu->addMenu(tr("Audio channels"));
|
QMenu* audioChannels = avMenu->addMenu(tr("Audio channels"));
|
||||||
m_shortcutController->addMenu(audioChannels, avMenu);
|
m_inputModel->addMenu(audioChannels, avMenu);
|
||||||
|
|
||||||
for (int i = 0; i < 4; ++i) {
|
for (int i = 0; i < 4; ++i) {
|
||||||
QAction* enableCh = new QAction(tr("Channel %0").arg(i + 1), audioChannels);
|
QAction* enableCh = new QAction(tr("Channel %0").arg(i + 1), audioChannels);
|
||||||
|
@ -1400,7 +1389,7 @@ void Window::setupMenu(QMenuBar* menubar) {
|
||||||
addControlledAction(audioChannels, enableChB, QString("enableChB"));
|
addControlledAction(audioChannels, enableChB, QString("enableChB"));
|
||||||
|
|
||||||
QMenu* toolsMenu = menubar->addMenu(tr("&Tools"));
|
QMenu* toolsMenu = menubar->addMenu(tr("&Tools"));
|
||||||
m_shortcutController->addMenu(toolsMenu);
|
m_inputModel->addMenu(toolsMenu);
|
||||||
QAction* viewLogs = new QAction(tr("View &logs..."), toolsMenu);
|
QAction* viewLogs = new QAction(tr("View &logs..."), toolsMenu);
|
||||||
connect(viewLogs, SIGNAL(triggered()), m_logView, SLOT(show()));
|
connect(viewLogs, SIGNAL(triggered()), m_logView, SLOT(show()));
|
||||||
addControlledAction(toolsMenu, viewLogs, "viewLogs");
|
addControlledAction(toolsMenu, viewLogs, "viewLogs");
|
||||||
|
@ -1526,69 +1515,6 @@ void Window::setupMenu(QMenuBar* menubar) {
|
||||||
exitFullScreen->setShortcut(QKeySequence("Esc"));
|
exitFullScreen->setShortcut(QKeySequence("Esc"));
|
||||||
addHiddenAction(frameMenu, exitFullScreen, "exitFullScreen");
|
addHiddenAction(frameMenu, exitFullScreen, "exitFullScreen");
|
||||||
|
|
||||||
QMenu* autofireMenu = new QMenu(tr("Autofire"), this);
|
|
||||||
m_shortcutController->addMenu(autofireMenu);
|
|
||||||
|
|
||||||
m_shortcutController->addFunctions(autofireMenu, [this]() {
|
|
||||||
m_controller->setAutofire(GBA_KEY_A, true);
|
|
||||||
}, [this]() {
|
|
||||||
m_controller->setAutofire(GBA_KEY_A, false);
|
|
||||||
}, QKeySequence(), tr("Autofire A"), "autofireA");
|
|
||||||
|
|
||||||
m_shortcutController->addFunctions(autofireMenu, [this]() {
|
|
||||||
m_controller->setAutofire(GBA_KEY_B, true);
|
|
||||||
}, [this]() {
|
|
||||||
m_controller->setAutofire(GBA_KEY_B, false);
|
|
||||||
}, QKeySequence(), tr("Autofire B"), "autofireB");
|
|
||||||
|
|
||||||
m_shortcutController->addFunctions(autofireMenu, [this]() {
|
|
||||||
m_controller->setAutofire(GBA_KEY_L, true);
|
|
||||||
}, [this]() {
|
|
||||||
m_controller->setAutofire(GBA_KEY_L, false);
|
|
||||||
}, QKeySequence(), tr("Autofire L"), "autofireL");
|
|
||||||
|
|
||||||
m_shortcutController->addFunctions(autofireMenu, [this]() {
|
|
||||||
m_controller->setAutofire(GBA_KEY_R, true);
|
|
||||||
}, [this]() {
|
|
||||||
m_controller->setAutofire(GBA_KEY_R, false);
|
|
||||||
}, QKeySequence(), tr("Autofire R"), "autofireR");
|
|
||||||
|
|
||||||
m_shortcutController->addFunctions(autofireMenu, [this]() {
|
|
||||||
m_controller->setAutofire(GBA_KEY_START, true);
|
|
||||||
}, [this]() {
|
|
||||||
m_controller->setAutofire(GBA_KEY_START, false);
|
|
||||||
}, QKeySequence(), tr("Autofire Start"), "autofireStart");
|
|
||||||
|
|
||||||
m_shortcutController->addFunctions(autofireMenu, [this]() {
|
|
||||||
m_controller->setAutofire(GBA_KEY_SELECT, true);
|
|
||||||
}, [this]() {
|
|
||||||
m_controller->setAutofire(GBA_KEY_SELECT, false);
|
|
||||||
}, QKeySequence(), tr("Autofire Select"), "autofireSelect");
|
|
||||||
|
|
||||||
m_shortcutController->addFunctions(autofireMenu, [this]() {
|
|
||||||
m_controller->setAutofire(GBA_KEY_UP, true);
|
|
||||||
}, [this]() {
|
|
||||||
m_controller->setAutofire(GBA_KEY_UP, false);
|
|
||||||
}, QKeySequence(), tr("Autofire Up"), "autofireUp");
|
|
||||||
|
|
||||||
m_shortcutController->addFunctions(autofireMenu, [this]() {
|
|
||||||
m_controller->setAutofire(GBA_KEY_RIGHT, true);
|
|
||||||
}, [this]() {
|
|
||||||
m_controller->setAutofire(GBA_KEY_RIGHT, false);
|
|
||||||
}, QKeySequence(), tr("Autofire Right"), "autofireRight");
|
|
||||||
|
|
||||||
m_shortcutController->addFunctions(autofireMenu, [this]() {
|
|
||||||
m_controller->setAutofire(GBA_KEY_DOWN, true);
|
|
||||||
}, [this]() {
|
|
||||||
m_controller->setAutofire(GBA_KEY_DOWN, false);
|
|
||||||
}, QKeySequence(), tr("Autofire Down"), "autofireDown");
|
|
||||||
|
|
||||||
m_shortcutController->addFunctions(autofireMenu, [this]() {
|
|
||||||
m_controller->setAutofire(GBA_KEY_LEFT, true);
|
|
||||||
}, [this]() {
|
|
||||||
m_controller->setAutofire(GBA_KEY_LEFT, false);
|
|
||||||
}, QKeySequence(), tr("Autofire Left"), "autofireLeft");
|
|
||||||
|
|
||||||
foreach (QAction* action, m_gameActions) {
|
foreach (QAction* action, m_gameActions) {
|
||||||
action->setDisabled(true);
|
action->setDisabled(true);
|
||||||
}
|
}
|
||||||
|
@ -1644,7 +1570,7 @@ QAction* Window::addControlledAction(QMenu* menu, QAction* action, const QString
|
||||||
}
|
}
|
||||||
|
|
||||||
QAction* Window::addHiddenAction(QMenu* menu, QAction* action, const QString& name) {
|
QAction* Window::addHiddenAction(QMenu* menu, QAction* action, const QString& name) {
|
||||||
m_shortcutController->addAction(menu, action, name);
|
m_inputModel->addAction(menu, action, name);
|
||||||
action->setShortcutContext(Qt::WidgetShortcut);
|
action->setShortcutContext(Qt::WidgetShortcut);
|
||||||
addAction(action);
|
addAction(action);
|
||||||
return action;
|
return action;
|
||||||
|
|
|
@ -28,10 +28,10 @@ class Display;
|
||||||
class GameController;
|
class GameController;
|
||||||
class GDBController;
|
class GDBController;
|
||||||
class GIFView;
|
class GIFView;
|
||||||
|
class InputModel;
|
||||||
class LibraryView;
|
class LibraryView;
|
||||||
class LogView;
|
class LogView;
|
||||||
class ShaderSelector;
|
class ShaderSelector;
|
||||||
class ShortcutController;
|
|
||||||
class VideoView;
|
class VideoView;
|
||||||
class WindowBackground;
|
class WindowBackground;
|
||||||
|
|
||||||
|
@ -98,8 +98,6 @@ public slots:
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void keyPressEvent(QKeyEvent* event) override;
|
|
||||||
virtual void keyReleaseEvent(QKeyEvent* event) override;
|
|
||||||
virtual void resizeEvent(QResizeEvent*) override;
|
virtual void resizeEvent(QResizeEvent*) override;
|
||||||
virtual void showEvent(QShowEvent*) override;
|
virtual void showEvent(QShowEvent*) override;
|
||||||
virtual void closeEvent(QCloseEvent*) override;
|
virtual void closeEvent(QCloseEvent*) override;
|
||||||
|
@ -170,12 +168,12 @@ private:
|
||||||
WindowBackground* m_screenWidget;
|
WindowBackground* m_screenWidget;
|
||||||
QPixmap m_logo;
|
QPixmap m_logo;
|
||||||
ConfigController* m_config;
|
ConfigController* m_config;
|
||||||
|
InputModel* m_inputModel;
|
||||||
InputController m_inputController;
|
InputController m_inputController;
|
||||||
QList<QDateTime> m_frameList;
|
QList<QDateTime> m_frameList;
|
||||||
QTimer m_fpsTimer;
|
QTimer m_fpsTimer;
|
||||||
QList<QString> m_mruFiles;
|
QList<QString> m_mruFiles;
|
||||||
QMenu* m_mruMenu;
|
QMenu* m_mruMenu;
|
||||||
ShortcutController* m_shortcutController;
|
|
||||||
ShaderSelector* m_shaderView;
|
ShaderSelector* m_shaderView;
|
||||||
bool m_fullscreenOnStart;
|
bool m_fullscreenOnStart;
|
||||||
QTimer m_focusCheck;
|
QTimer m_focusCheck;
|
||||||
|
|
|
@ -84,7 +84,7 @@ void _vdwRewind(struct VDir* vd) {
|
||||||
struct VDirW32* vdw = (struct VDirW32*) vd;
|
struct VDirW32* vdw = (struct VDirW32*) vd;
|
||||||
FindClose(vdw->handle);
|
FindClose(vdw->handle);
|
||||||
wchar_t name[MAX_PATH + 1];
|
wchar_t name[MAX_PATH + 1];
|
||||||
MultiByteToWideChar(CP_UTF8, MB_PRECOMPOSED, vdw->path, -1, name, MAX_PATH);
|
MultiByteToWideChar(CP_UTF8, 0, vdw->path, -1, name, MAX_PATH);
|
||||||
StringCchCatNW(name, MAX_PATH, L"\\*", 2);
|
StringCchCatNW(name, MAX_PATH, L"\\*", 2);
|
||||||
if (vdw->vde.utf8Name) {
|
if (vdw->vde.utf8Name) {
|
||||||
free(vdw->vde.utf8Name);
|
free(vdw->vde.utf8Name);
|
||||||
|
|
|
@ -0,0 +1,170 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
from __future__ import print_function
|
||||||
|
import argparse
|
||||||
|
import errno
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import shutil
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
qtPath = None
|
||||||
|
verbose = False
|
||||||
|
|
||||||
|
def splitPath(path):
|
||||||
|
folders = []
|
||||||
|
while True:
|
||||||
|
path, folder = os.path.split(path)
|
||||||
|
if folder != '':
|
||||||
|
folders.append(folder)
|
||||||
|
else:
|
||||||
|
if path != '':
|
||||||
|
folders.append(path)
|
||||||
|
break
|
||||||
|
folders.reverse()
|
||||||
|
return folders
|
||||||
|
|
||||||
|
def joinPath(path):
|
||||||
|
return reduce(os.path.join, path, '')
|
||||||
|
|
||||||
|
def findFramework(path):
|
||||||
|
child = []
|
||||||
|
while path and not path[-1].endswith('.framework'):
|
||||||
|
child.append(path.pop())
|
||||||
|
child.reverse()
|
||||||
|
return path, child
|
||||||
|
|
||||||
|
def findQtPath(path):
|
||||||
|
parent, child = findFramework(splitPath(path))
|
||||||
|
return joinPath(parent[:-2])
|
||||||
|
|
||||||
|
def makedirs(path):
|
||||||
|
split = splitPath(path)
|
||||||
|
accum = []
|
||||||
|
split.reverse()
|
||||||
|
while split:
|
||||||
|
accum.append(split.pop())
|
||||||
|
newPath = joinPath(accum)
|
||||||
|
if newPath == '/':
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
os.mkdir(newPath)
|
||||||
|
except OSError as e:
|
||||||
|
if e.errno != errno.EEXIST:
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
|
def parseOtoolLine(line, execPath, root):
|
||||||
|
if not line.startswith('\t'):
|
||||||
|
return None, None, None, None
|
||||||
|
line = line[1:]
|
||||||
|
match = re.match(r'(\S.*) \(compatibility version.*\)', line)
|
||||||
|
path = match.group(1)
|
||||||
|
split = splitPath(path)
|
||||||
|
newExecPath = ['@executable_path', '..', 'Frameworks']
|
||||||
|
newPath = execPath[:-1]
|
||||||
|
newPath.append('Frameworks')
|
||||||
|
if split[:3] == ['/', 'usr', 'lib'] or split[:2] == ['/', 'System']:
|
||||||
|
return None, None, None, None
|
||||||
|
if split[0] == '@executable_path':
|
||||||
|
split[:1] = execPath
|
||||||
|
if split[0] == '/' and not os.access(joinPath(split), os.F_OK):
|
||||||
|
split[:1] = root
|
||||||
|
oldPath = os.path.realpath(joinPath(split))
|
||||||
|
split = splitPath(oldPath)
|
||||||
|
isFramework = False
|
||||||
|
if not split[-1].endswith('.dylib'):
|
||||||
|
isFramework = True
|
||||||
|
split, framework = findFramework(split)
|
||||||
|
newPath.append(split[-1])
|
||||||
|
newExecPath.append(split[-1])
|
||||||
|
if isFramework:
|
||||||
|
newPath.extend(framework)
|
||||||
|
newExecPath.extend(framework)
|
||||||
|
split.extend(framework)
|
||||||
|
newPath = joinPath(newPath)
|
||||||
|
newExecPath = joinPath(newExecPath)
|
||||||
|
return joinPath(split), newPath, path, newExecPath
|
||||||
|
|
||||||
|
def updateMachO(bin, execPath, root):
|
||||||
|
global qtPath
|
||||||
|
otoolOutput = subprocess.check_output([otool, '-L', bin])
|
||||||
|
toUpdate = []
|
||||||
|
for line in otoolOutput.split('\n'):
|
||||||
|
oldPath, newPath, oldExecPath, newExecPath = parseOtoolLine(line, execPath, root)
|
||||||
|
if not newPath:
|
||||||
|
continue
|
||||||
|
if os.access(newPath, os.F_OK):
|
||||||
|
if verbose:
|
||||||
|
print('Skipping copying {}, already done.'.format(oldPath))
|
||||||
|
newPath = None
|
||||||
|
elif os.path.abspath(oldPath) != os.path.abspath(newPath):
|
||||||
|
if verbose:
|
||||||
|
print('Copying {} to {}...'.format(oldPath, newPath))
|
||||||
|
parent, child = os.path.split(newPath)
|
||||||
|
makedirs(parent)
|
||||||
|
shutil.copy2(oldPath, newPath)
|
||||||
|
os.chmod(newPath, 0o644)
|
||||||
|
toUpdate.append((newPath, oldExecPath, newExecPath))
|
||||||
|
if not qtPath and 'Qt' in oldPath:
|
||||||
|
qtPath = findQtPath(oldPath)
|
||||||
|
if verbose:
|
||||||
|
print('Found Qt path at {}.'.format(qtPath))
|
||||||
|
args = [installNameTool]
|
||||||
|
for path, oldExecPath, newExecPath in toUpdate:
|
||||||
|
if path != bin:
|
||||||
|
if path:
|
||||||
|
updateMachO(path, execPath, root)
|
||||||
|
if verbose:
|
||||||
|
print('Updating Mach-O load from {} to {}...'.format(oldExecPath, newExecPath))
|
||||||
|
args.extend(['-change', oldExecPath, newExecPath])
|
||||||
|
else:
|
||||||
|
if verbose:
|
||||||
|
print('Updating Mach-O id from {} to {}...'.format(oldExecPath, newExecPath))
|
||||||
|
args.extend(['-id', newExecPath])
|
||||||
|
args.append(bin)
|
||||||
|
subprocess.check_call(args)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument('-R', '--root', metavar='ROOT', default='/', help='root directory to search')
|
||||||
|
parser.add_argument('-I', '--install-name-tool', metavar='INSTALL_NAME_TOOL', default='install_name_tool', help='path to install_name_tool')
|
||||||
|
parser.add_argument('-O', '--otool', metavar='OTOOL', default='otool', help='path to otool')
|
||||||
|
parser.add_argument('-p', '--qt-plugins', metavar='PLUGINS', default='', help='Qt plugins to include (comma-separated)')
|
||||||
|
parser.add_argument('-v', '--verbose', action='store_true', default=False, help='output more information')
|
||||||
|
parser.add_argument('bundle', help='application bundle to deploy')
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
otool = args.otool
|
||||||
|
installNameTool = args.install_name_tool
|
||||||
|
verbose = args.verbose
|
||||||
|
|
||||||
|
try:
|
||||||
|
shutil.rmtree(os.path.join(args.bundle, 'Contents/Frameworks/'))
|
||||||
|
except OSError as e:
|
||||||
|
if e.errno != errno.ENOENT:
|
||||||
|
raise
|
||||||
|
|
||||||
|
for executable in os.listdir(os.path.join(args.bundle, 'Contents/MacOS')):
|
||||||
|
if executable.endswith('.dSYM'):
|
||||||
|
continue
|
||||||
|
fullPath = os.path.join(args.bundle, 'Contents/MacOS/', executable)
|
||||||
|
updateMachO(fullPath, splitPath(os.path.join(args.bundle, 'Contents/MacOS')), splitPath(args.root))
|
||||||
|
if args.qt_plugins:
|
||||||
|
try:
|
||||||
|
shutil.rmtree(os.path.join(args.bundle, 'Contents/PlugIns/'))
|
||||||
|
except OSError as e:
|
||||||
|
if e.errno != errno.ENOENT:
|
||||||
|
raise
|
||||||
|
makedirs(os.path.join(args.bundle, 'Contents/PlugIns'))
|
||||||
|
makedirs(os.path.join(args.bundle, 'Contents/Resources'))
|
||||||
|
with open(os.path.join(args.bundle, 'Contents/Resources/qt.conf'), 'w') as conf:
|
||||||
|
conf.write('[Paths]\nPlugins = PlugIns\n')
|
||||||
|
plugins = args.qt_plugins.split(',')
|
||||||
|
for plugin in plugins:
|
||||||
|
plugin = plugin.strip()
|
||||||
|
kind, plug = os.path.split(plugin)
|
||||||
|
newDir = os.path.join(args.bundle, 'Contents/PlugIns/', kind)
|
||||||
|
makedirs(newDir)
|
||||||
|
newPath = os.path.join(newDir, plug)
|
||||||
|
shutil.copy2(os.path.join(qtPath, 'plugins', plugin), newPath)
|
||||||
|
updateMachO(newPath, splitPath(os.path.join(args.bundle, 'Contents/MacOS')), splitPath(args.root))
|
Loading…
Reference in New Issue