Qt: Start hooking up input events into scripting

This commit is contained in:
Vicki Pfau 2023-01-08 14:36:17 -08:00
parent 47bf00da5e
commit 697e80a5a1
8 changed files with 328 additions and 11 deletions

View File

@ -74,14 +74,14 @@ enum mScriptKeyModifier {
mSCRIPT_KMOD_SCROLL_LOCK = 0x400,
};
#define mSCRIPT_KEYBASE 0x200000
#define mSCRIPT_KEYBASE 0x800000
enum mScriptKey {
mSCRIPT_KEY_NONE = 0,
mSCRIPT_KEY_BACKSPACE = 0x000008,
mSCRIPT_KEY_TAB = 0x000009,
mSCRIPT_KEY_LINE_FEED = 0x00000A,
mSCRIPT_KEY_ENTER = 0x00000A,
mSCRIPT_KEY_ESCAPE = 0x00001B,
mSCRIPT_KEY_DELETE = 0x00007F,
@ -121,9 +121,10 @@ enum mScriptKey {
mSCRIPT_KEY_INSERT,
mSCRIPT_KEY_BREAK,
mSCRIPT_KEY_CLEAR,
mSCRIPT_KEY_PRNTSCR,
mSCRIPT_KEY_PRINT_SCREEN,
mSCRIPT_KEY_SYSRQ,
mSCRIPT_KEY_MENU,
mSCRIPT_KEY_HELP,
mSCRIPT_KEY_LSHIFT = mSCRIPT_KEYBASE | 0x30,
mSCRIPT_KEY_RSHIFT,
@ -140,6 +141,24 @@ enum mScriptKey {
mSCRIPT_KEY_CAPS_LOCK,
mSCRIPT_KEY_NUM_LOCK,
mSCRIPT_KEY_SCROLL_LOCK,
mSCRIPT_KEY_KP_0 = mSCRIPT_KEYBASE | 0x40,
mSCRIPT_KEY_KP_1,
mSCRIPT_KEY_KP_2,
mSCRIPT_KEY_KP_3,
mSCRIPT_KEY_KP_4,
mSCRIPT_KEY_KP_5,
mSCRIPT_KEY_KP_6,
mSCRIPT_KEY_KP_7,
mSCRIPT_KEY_KP_8,
mSCRIPT_KEY_KP_9,
mSCRIPT_KEY_KP_PLUS,
mSCRIPT_KEY_KP_MINUS,
mSCRIPT_KEY_KP_MULTIPLY,
mSCRIPT_KEY_KP_DIVIDE,
mSCRIPT_KEY_KP_COMMA,
mSCRIPT_KEY_KP_POINT,
mSCRIPT_KEY_KP_ENTER,
};
struct mScriptEvent {
@ -151,22 +170,22 @@ struct mScriptEvent {
struct mScriptKeyEvent {
struct mScriptEvent d;
uint8_t state;
uint8_t reserved;
uint16_t modifiers;
uint32_t key;
};
struct mScriptMouseButtonEvent {
struct mScriptEvent d;
uint8_t state;
uint8_t mouse;
uint8_t context;
uint8_t state;
uint8_t button;
};
struct mScriptMouseMoveEvent {
struct mScriptEvent d;
bool relative;
uint8_t mouse;
uint8_t context;
int32_t x;
int32_t y;
};
@ -174,8 +193,8 @@ struct mScriptMouseMoveEvent {
struct mScriptMouseWheelEvent {
struct mScriptEvent d;
uint8_t mouse;
int32_t x;
int32_t y;
int16_t x;
int16_t y;
};
struct mScriptGamepadButtonEvent {

View File

@ -10,6 +10,7 @@
#include "DisplayGL.h"
#include "DisplayQt.h"
#include "LogController.h"
#include "utils.h"
#include <mgba-util/vfs.h>
@ -169,3 +170,32 @@ void QGBA::Display::mouseMoveEvent(QMouseEvent*) {
m_mouseTimer.stop();
m_mouseTimer.start();
}
QPoint QGBA::Display::normalizedPoint(CoreController* controller, const QPoint& localRef) {
QSize screen(controller->screenDimensions());
#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
QSize newSize((QSizeF(size()) * devicePixelRatioF()).toSize());
#else
QSize newSize((QSizeF(size()) * devicePixelRatio()).toSize());
#endif
if (m_lockAspectRatio) {
QGBA::lockAspectRatio(screen, newSize);
}
if (m_lockIntegerScaling) {
QGBA::lockIntegerScaling(screen, newSize);
}
QPointF newPos(localRef);
newPos -= QPointF(width() / 2.0, height() / 2.0);
newPos = QPointF(newPos.x() * screen.width(), newPos.y() * screen.height());
newPos = QPointF(newPos.x() / newSize.width(), newPos.y() / newSize.height());
#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
newPos *= devicePixelRatioF();
#else
newPos *= devicePixelRatio();
#endif
newPos += QPointF(screen.width() / 2.0, screen.height() / 2.0);
return newPos.toPoint();
}

View File

@ -44,6 +44,8 @@ public:
bool isShowOSD() const { return m_showOSD; }
bool isShowFrameCounter() const { return m_showFrameCounter; }
QPoint normalizedPoint(CoreController*, const QPoint& localRef);
virtual void attach(std::shared_ptr<CoreController>);
virtual void configure(ConfigController*);
virtual void startDrawing(std::shared_ptr<CoreController>) = 0;

View File

@ -7,7 +7,9 @@
#include "ConfigController.h"
#include "input/GamepadButtonEvent.h"
#include "input/GamepadHatEvent.h"
#include "InputProfile.h"
#include "scripting/ScriptingController.h"
#include <QAction>
#include <QKeyEvent>
@ -32,6 +34,10 @@ void ShortcutController::setActionMapper(ActionMapper* actions) {
rebuildItems();
}
void ShortcutController::setScriptingController(ScriptingController* controller) {
m_scripting = controller;
}
void ShortcutController::updateKey(const QString& name, int keySequence) {
auto item = m_items[name];
if (!item) {
@ -132,9 +138,14 @@ void ShortcutController::rebuildItems() {
onSubitems({}, std::bind(&ShortcutController::generateItem, this, std::placeholders::_1));
}
bool ShortcutController::eventFilter(QObject*, QEvent* event) {
bool ShortcutController::eventFilter(QObject* obj, QEvent* event) {
if (event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease) {
QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
#ifdef ENABLE_SCRIPTING
if (m_scripting) {
m_scripting->event(obj, event);
}
#endif
if (keyEvent->isAutoRepeat()) {
return false;
}
@ -153,6 +164,11 @@ bool ShortcutController::eventFilter(QObject*, QEvent* event) {
}
}
if (event->type() == GamepadButtonEvent::Down()) {
#ifdef ENABLE_SCRIPTING
if (m_scripting) {
m_scripting->event(obj, event);
}
#endif
auto item = m_buttons.find(static_cast<GamepadButtonEvent*>(event)->value());
if (item == m_buttons.end()) {
return false;
@ -169,6 +185,11 @@ bool ShortcutController::eventFilter(QObject*, QEvent* event) {
return true;
}
if (event->type() == GamepadButtonEvent::Up()) {
#ifdef ENABLE_SCRIPTING
if (m_scripting) {
m_scripting->event(obj, event);
}
#endif
auto item = m_buttons.find(static_cast<GamepadButtonEvent*>(event)->value());
if (item == m_buttons.end()) {
return false;
@ -201,6 +222,13 @@ bool ShortcutController::eventFilter(QObject*, QEvent* event) {
event->accept();
return true;
}
#ifdef ENABLE_SCRIPTING
if (event->type() == GamepadHatEvent::Type()) {
if (m_scripting) {
m_scripting->event(obj, event);
}
}
#endif
return false;
}

View File

@ -21,6 +21,7 @@ namespace QGBA {
class ConfigController;
class InputProfile;
class ScriptingController;
class Shortcut : public QObject {
Q_OBJECT
@ -74,6 +75,7 @@ public:
void setConfigController(ConfigController* controller);
void setActionMapper(ActionMapper* actionMapper);
void setScriptingController(ScriptingController* scriptingController);
void setProfile(const QString& profile);
@ -121,6 +123,7 @@ private:
QHash<int, std::shared_ptr<Shortcut>> m_heldKeys;
ActionMapper* m_actions = nullptr;
ConfigController* m_config = nullptr;
ScriptingController* m_scripting = nullptr;
QString m_profileName;
const InputProfile* m_profile = nullptr;
};

View File

@ -627,8 +627,10 @@ void Window::consoleOpen() {
void Window::scriptingOpen() {
if (!m_scripting) {
m_scripting = std::make_unique<ScriptingController>();
m_shortcutController->setScriptingController(m_scripting.get());
if (m_controller) {
m_scripting->setController(m_controller);
m_display->installEventFilter(m_scripting.get());
}
}
ScriptingView* view = new ScriptingView(m_scripting.get(), m_config);
@ -2125,6 +2127,12 @@ void Window::attachDisplay() {
m_display->attach(m_controller);
connect(m_display.get(), &QGBA::Display::drawingStarted, this, &Window::changeRenderer);
m_display->startDrawing(m_controller);
#ifdef ENABLE_SCRIPTING
if (m_scripting) {
m_display->installEventFilter(m_scripting.get());
}
#endif
}
void Window::updateMute() {

View File

@ -5,11 +5,21 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "scripting/ScriptingController.h"
#include "AudioProcessor.h"
#include <QEvent>
#include <QKeyEvent>
#include <QMouseEvent>
#include <QWidget>
#include "CoreController.h"
#include "Display.h"
#include "input/GamepadButtonEvent.h"
#include "input/GamepadHatEvent.h"
#include "scripting/ScriptingTextBuffer.h"
#include "scripting/ScriptingTextBufferModel.h"
#include <mgba/script/input.h>
#include <mgba-util/math.h>
using namespace QGBA;
ScriptingController::ScriptingController(QObject* parent)
@ -120,10 +130,93 @@ void ScriptingController::runCode(const QString& code) {
load(vf, "*prompt");
}
bool ScriptingController::eventFilter(QObject* obj, QEvent* ev) {
event(obj, ev);
return false;
}
void ScriptingController::event(QObject* obj, QEvent* event) {
if (!m_controller) {
return;
}
switch (event->type()) {
case QEvent::KeyPress:
case QEvent::KeyRelease: {
struct mScriptKeyEvent ev{mSCRIPT_EV_TYPE_KEY};
auto keyEvent = static_cast<QKeyEvent*>(event);
ev.state = event->type() == QEvent::KeyRelease ? mSCRIPT_INPUT_STATE_UP :
static_cast<QKeyEvent*>(event)->isAutoRepeat() ? mSCRIPT_INPUT_STATE_HELD : mSCRIPT_INPUT_STATE_DOWN;
ev.key = qtToScriptingKey(keyEvent);
ev.modifiers = qtToScriptingModifiers(keyEvent->modifiers());
mScriptContextFireEvent(&m_scriptContext, &ev.d);
return;
}
case QEvent::MouseButtonPress:
case QEvent::MouseButtonRelease: {
struct mScriptMouseButtonEvent ev{mSCRIPT_EV_TYPE_MOUSE_BUTTON};
auto mouseEvent = static_cast<QMouseEvent*>(event);
ev.mouse = 0;
ev.state = event->type() == QEvent::MouseButtonPress ? mSCRIPT_INPUT_STATE_DOWN : mSCRIPT_INPUT_STATE_UP;
ev.button = 31 - clz32(mouseEvent->button());
mScriptContextFireEvent(&m_scriptContext, &ev.d);
return;
}
case QEvent::MouseMove: {
struct mScriptMouseMoveEvent ev{mSCRIPT_EV_TYPE_MOUSE_MOVE};
auto mouseEvent = static_cast<QMouseEvent*>(event);
QPoint pos = mouseEvent->pos();
pos = static_cast<Display*>(obj)->normalizedPoint(m_controller.get(), pos);
ev.mouse = 0;
ev.x = pos.x();
ev.y = pos.y();
mScriptContextFireEvent(&m_scriptContext, &ev.d);
return;
}
case QEvent::Wheel: {
struct mScriptMouseWheelEvent ev{mSCRIPT_EV_TYPE_MOUSE_WHEEL};
auto wheelEvent = static_cast<QWheelEvent*>(event);
QPoint adelta = wheelEvent->angleDelta();
QPoint pdelta = wheelEvent->pixelDelta();
ev.mouse = 0;
if (!pdelta.isNull()) {
ev.x = pdelta.x();
ev.y = pdelta.y();
} else {
ev.x = adelta.x();
ev.y = adelta.y();
}
mScriptContextFireEvent(&m_scriptContext, &ev.d);
return;
}
default:
break;
}
auto type = event->type();
if (type == GamepadButtonEvent::Down() || type == GamepadButtonEvent::Up()) {
struct mScriptGamepadButtonEvent ev{mSCRIPT_EV_TYPE_GAMEPAD_BUTTON};
auto gamepadEvent = static_cast<GamepadButtonEvent*>(event);
ev.pad = 0;
ev.state = event->type() == GamepadButtonEvent::Down() ? mSCRIPT_INPUT_STATE_DOWN : mSCRIPT_INPUT_STATE_UP;
ev.button = gamepadEvent->value();
mScriptContextFireEvent(&m_scriptContext, &ev.d);
}
if (type == GamepadHatEvent::Type()) {
struct mScriptGamepadHatEvent ev{mSCRIPT_EV_TYPE_GAMEPAD_HAT};
auto gamepadEvent = static_cast<GamepadHatEvent*>(event);
ev.pad = 0;
ev.hat = gamepadEvent->hatId();
ev.direction = gamepadEvent->direction();
mScriptContextFireEvent(&m_scriptContext, &ev.d);
}
}
void ScriptingController::init() {
mScriptContextInit(&m_scriptContext);
mScriptContextAttachStdlib(&m_scriptContext);
mScriptContextAttachSocket(&m_scriptContext);
mScriptContextAttachInput(&m_scriptContext);
mScriptContextRegisterEngines(&m_scriptContext);
mScriptContextAttachLogger(&m_scriptContext, &m_logger);
@ -138,3 +231,130 @@ void ScriptingController::init() {
m_activeEngine = *m_engines.begin();
}
}
uint32_t ScriptingController::qtToScriptingKey(const QKeyEvent* event) {
QString text(event->text());
int key = event->key();
static const QHash<int, uint32_t> keypadMap{
{'0', mSCRIPT_KEY_KP_0},
{'1', mSCRIPT_KEY_KP_1},
{'2', mSCRIPT_KEY_KP_2},
{'3', mSCRIPT_KEY_KP_3},
{'4', mSCRIPT_KEY_KP_4},
{'5', mSCRIPT_KEY_KP_5},
{'6', mSCRIPT_KEY_KP_6},
{'7', mSCRIPT_KEY_KP_7},
{'8', mSCRIPT_KEY_KP_8},
{'9', mSCRIPT_KEY_KP_9},
{'+', mSCRIPT_KEY_KP_PLUS},
{'-', mSCRIPT_KEY_KP_MINUS},
{'*', mSCRIPT_KEY_KP_MULTIPLY},
{'/', mSCRIPT_KEY_KP_DIVIDE},
{',', mSCRIPT_KEY_KP_COMMA},
{'.', mSCRIPT_KEY_KP_POINT},
{Qt::Key_Enter, mSCRIPT_KEY_KP_ENTER},
};
static const QHash<int, uint32_t> extraKeyMap{
{Qt::Key_Escape, mSCRIPT_KEY_ESCAPE},
{Qt::Key_Tab, mSCRIPT_KEY_TAB},
{Qt::Key_Backtab, mSCRIPT_KEY_BACKSPACE},
{Qt::Key_Backspace, mSCRIPT_KEY_BACKSPACE},
{Qt::Key_Return, mSCRIPT_KEY_ENTER},
{Qt::Key_Enter, mSCRIPT_KEY_ENTER},
{Qt::Key_Insert, mSCRIPT_KEY_INSERT},
{Qt::Key_Delete, mSCRIPT_KEY_DELETE},
{Qt::Key_Pause, mSCRIPT_KEY_BREAK},
{Qt::Key_Print, mSCRIPT_KEY_PRINT_SCREEN},
{Qt::Key_SysReq, mSCRIPT_KEY_SYSRQ},
{Qt::Key_Clear, mSCRIPT_KEY_CLEAR},
{Qt::Key_Home, mSCRIPT_KEY_HOME},
{Qt::Key_End, mSCRIPT_KEY_END},
{Qt::Key_Left, mSCRIPT_KEY_LEFT},
{Qt::Key_Up, mSCRIPT_KEY_UP},
{Qt::Key_Right, mSCRIPT_KEY_RIGHT},
{Qt::Key_Down, mSCRIPT_KEY_DOWN},
{Qt::Key_PageUp, mSCRIPT_KEY_PAGE_UP},
{Qt::Key_PageDown, mSCRIPT_KEY_DOWN},
{Qt::Key_Shift, mSCRIPT_KEY_SHIFT},
{Qt::Key_Control, mSCRIPT_KEY_CONTROL},
{Qt::Key_Meta, mSCRIPT_KEY_SUPER},
{Qt::Key_Alt, mSCRIPT_KEY_ALT},
{Qt::Key_CapsLock, mSCRIPT_KEY_CAPS_LOCK},
{Qt::Key_NumLock, mSCRIPT_KEY_NUM_LOCK},
{Qt::Key_ScrollLock, mSCRIPT_KEY_SCROLL_LOCK},
{Qt::Key_F1, mSCRIPT_KEY_F1},
{Qt::Key_F2, mSCRIPT_KEY_F2},
{Qt::Key_F3, mSCRIPT_KEY_F3},
{Qt::Key_F4, mSCRIPT_KEY_F4},
{Qt::Key_F5, mSCRIPT_KEY_F5},
{Qt::Key_F6, mSCRIPT_KEY_F6},
{Qt::Key_F7, mSCRIPT_KEY_F7},
{Qt::Key_F8, mSCRIPT_KEY_F8},
{Qt::Key_F9, mSCRIPT_KEY_F9},
{Qt::Key_F10, mSCRIPT_KEY_F10},
{Qt::Key_F11, mSCRIPT_KEY_F11},
{Qt::Key_F12, mSCRIPT_KEY_F12},
{Qt::Key_F13, mSCRIPT_KEY_F13},
{Qt::Key_F14, mSCRIPT_KEY_F14},
{Qt::Key_F15, mSCRIPT_KEY_F15},
{Qt::Key_F16, mSCRIPT_KEY_F16},
{Qt::Key_F17, mSCRIPT_KEY_F17},
{Qt::Key_F18, mSCRIPT_KEY_F18},
{Qt::Key_F19, mSCRIPT_KEY_F19},
{Qt::Key_F20, mSCRIPT_KEY_F20},
{Qt::Key_F21, mSCRIPT_KEY_F21},
{Qt::Key_F22, mSCRIPT_KEY_F22},
{Qt::Key_F23, mSCRIPT_KEY_F23},
{Qt::Key_F24, mSCRIPT_KEY_F24},
{Qt::Key_Menu, mSCRIPT_KEY_MENU},
{Qt::Key_Super_L, mSCRIPT_KEY_SUPER},
{Qt::Key_Super_R, mSCRIPT_KEY_SUPER},
{Qt::Key_Help, mSCRIPT_KEY_HELP},
{Qt::Key_Hyper_L, mSCRIPT_KEY_SUPER},
{Qt::Key_Hyper_R, mSCRIPT_KEY_SUPER},
};
if (event->modifiers() & Qt::KeypadModifier && keypadMap.contains(key)) {
return keypadMap[key];
}
if (key >= 0 && key < 0x100) {
return key;
}
if (key < 0x01000000) {
if (text.isEmpty()) {
return 0;
}
QChar high = text[0];
if (!high.isSurrogate()) {
return high.unicode();
}
if (text.size() < 2) {
return 0;
}
return QChar::surrogateToUcs4(high, text[1]);
}
if (extraKeyMap.contains(key)) {
return extraKeyMap[key];
}
return 0;
}
uint16_t ScriptingController::qtToScriptingModifiers(Qt::KeyboardModifiers modifiers) {
uint16_t mod = 0;
if (modifiers & Qt::ShiftModifier) {
mod |= mSCRIPT_KMOD_SHIFT;
}
if (modifiers & Qt::ControlModifier) {
mod |= mSCRIPT_KMOD_CONTROL;
}
if (modifiers & Qt::AltModifier) {
mod |= mSCRIPT_KMOD_ALT;
}
if (modifiers & Qt::MetaModifier) {
mod |= mSCRIPT_KMOD_SUPER;
}
return mod;
}

View File

@ -15,6 +15,7 @@
#include <memory>
class QKeyEvent;
class QTextDocument;
namespace QGBA {
@ -35,6 +36,8 @@ public:
bool loadFile(const QString& path);
bool load(VFileDevice& vf, const QString& name);
void event(QObject* obj, QEvent* ev);
mScriptContext* context() { return &m_scriptContext; }
ScriptingTextBufferModel* textBufferModel() const { return m_bufferModel; }
@ -49,10 +52,14 @@ public slots:
void reset();
void runCode(const QString& code);
protected:
bool eventFilter(QObject*, QEvent*) override;
private:
void init();
static mScriptTextBuffer* createTextBuffer(void* context);
static uint32_t qtToScriptingKey(const QKeyEvent*);
static uint16_t qtToScriptingModifiers(Qt::KeyboardModifiers);
struct Logger : mLogger {
ScriptingController* p;