From c62f62b497238167c89ffae1a190fe939e949b09 Mon Sep 17 00:00:00 2001 From: harry Date: Sun, 18 Feb 2024 16:04:00 -0500 Subject: [PATCH 1/2] Added option on how to handle new gamepad device detection. Can now choose to do nothing, prompt user, or auto reconfigure of button mappings when a new device is detected. --- src/drivers/Qt/ConsoleUtilities.cpp | 33 ++++++++++++++++++++ src/drivers/Qt/ConsoleUtilities.h | 6 ++++ src/drivers/Qt/InputConf.cpp | 24 +++++++++++++-- src/drivers/Qt/InputConf.h | 2 ++ src/drivers/Qt/config.cpp | 1 + src/drivers/Qt/input.cpp | 47 ++++++++++++++++++++++++++++- src/drivers/Qt/sdl-joystick.h | 2 ++ 7 files changed, 112 insertions(+), 3 deletions(-) diff --git a/src/drivers/Qt/ConsoleUtilities.cpp b/src/drivers/Qt/ConsoleUtilities.cpp index fac6d5ec..05a9883f 100644 --- a/src/drivers/Qt/ConsoleUtilities.cpp +++ b/src/drivers/Qt/ConsoleUtilities.cpp @@ -1382,4 +1382,37 @@ QString fceuGetOpcodeToolTip( uint8_t *opcode, int size ) return QString::fromStdString( text ); } +//---------------------------------------------------- +void setCheckBoxFromProperty( QCheckBox *cbx, const char *property ) +{ + int pval; + g_config->getOption (property, &pval); + + cbx->setCheckState( pval ? Qt::Checked : Qt::Unchecked ); +} +//---------------------------------------------------- +void setComboBoxFromProperty( QComboBox *cbx, const char *property ) +{ + int i, pval; + g_config->getOption (property, &pval); + + for (i=0; icount(); i++) + { + if ( pval == cbx->itemData(i).toInt() ) + { + cbx->setCurrentIndex(i); break; + } + } +} +//--------------------------------------------------------------------------- +void setComboBoxFromValue( QComboBox *cbx, int pval ) +{ + for (int i=0; icount(); i++) + { + if ( pval == cbx->itemData(i).toInt() ) + { + cbx->setCurrentIndex(i); break; + } + } +} //--------------------------------------------------------------------------- diff --git a/src/drivers/Qt/ConsoleUtilities.h b/src/drivers/Qt/ConsoleUtilities.h index bbcefc07..06ad2872 100644 --- a/src/drivers/Qt/ConsoleUtilities.h +++ b/src/drivers/Qt/ConsoleUtilities.h @@ -9,6 +9,7 @@ #include #include #include +#include #include int getDirFromFile( const char *path, std::string &dir ); @@ -93,3 +94,8 @@ class QCheckBoxRO : public QCheckBox QString fceuGetOpcodeToolTip( uint8_t *opcode, int size ); QDialog *fceuCustomToolTipShow( const QPoint &globalPos, QDialog *popup ); + +//---------------------------------------------------- +void setCheckBoxFromProperty( QCheckBox *cbx, const char *property ); +void setComboBoxFromProperty( QComboBox *cbx, const char *property ); +void setComboBoxFromValue( QComboBox *cbx, int pval ); diff --git a/src/drivers/Qt/InputConf.cpp b/src/drivers/Qt/InputConf.cpp index 98e4a4b8..27476882 100644 --- a/src/drivers/Qt/InputConf.cpp +++ b/src/drivers/Qt/InputConf.cpp @@ -66,7 +66,7 @@ InputConfDialog_t::InputConfDialog_t(QWidget *parent) QPalette pal; QColor color; char stmp[256]; - int fourscore, autoInputPreset; + int fourscore, autoInputPreset, newDeviceBehavior; pal = this->palette(); @@ -80,11 +80,23 @@ InputConfDialog_t::InputConfDialog_t(QWidget *parent) nesInputFrame = new QGroupBox(tr("NES-Style Input Ports")); vbox1 = new QVBoxLayout(); - hbox = new QHBoxLayout(); + newDeviceOptionBox = new QComboBox(); fourScoreEna = new QCheckBox(tr("Attach 4-Score (Implies four gamepads)")); port2Mic = new QCheckBox(tr("Replace Port 2 Start with Microphone")); autoPreset = new QCheckBox(tr("Auto Load/Save Presets at ROM Open/Close")); + hbox = new QHBoxLayout(); + hbox->addWidget( new QLabel(tr("On New Gamepad Device Detection:")), 1); + hbox->addWidget(newDeviceOptionBox, 3); + vbox1->addLayout(hbox); + + newDeviceOptionBox->addItem("Do nothing", 0); + newDeviceOptionBox->addItem("Prompt User for Reconfigure", 1); + newDeviceOptionBox->addItem("Auto Reconfigure", 2); + + g_config->getOption("SDL.NewInputDeviceBehavior", &newDeviceBehavior); + setComboBoxFromValue( newDeviceOptionBox, newDeviceBehavior ); + g_config->getOption("SDL.FourScore", &fourscore); fourScoreEna->setChecked(fourscore); port2Mic->setChecked(replaceP2StartWithMicrophone); @@ -92,6 +104,7 @@ InputConfDialog_t::InputConfDialog_t(QWidget *parent) g_config->getOption("SDL.AutoInputPreset", &autoInputPreset); autoPreset->setChecked(autoInputPreset); + hbox = new QHBoxLayout(); hbox->addWidget(fourScoreEna); hbox->addWidget(port2Mic); vbox1->addLayout(hbox); @@ -240,6 +253,7 @@ InputConfDialog_t::InputConfDialog_t(QWidget *parent) connect(port2Mic, SIGNAL(stateChanged(int)), this, SLOT(port2MicChanged(int))); connect(autoPreset, SIGNAL(stateChanged(int)), this, SLOT(autoPresetChanged(int))); + connect(newDeviceOptionBox , SIGNAL(activated(int)), this, SLOT(newDeviceSettingsChange(int))); connect(nesPortComboxBox[0], SIGNAL(activated(int)), this, SLOT(port1Select(int))); connect(nesPortComboxBox[1], SIGNAL(activated(int)), this, SLOT(port2Select(int))); connect(expPortComboxBox, SIGNAL(activated(int)), this, SLOT(expSelect(int))); @@ -362,6 +376,12 @@ void InputConfDialog_t::updatePortComboBoxes(void) } } //---------------------------------------------------------------------------- +void InputConfDialog_t::newDeviceSettingsChange(int index) +{ + g_config->setOption("SDL.NewInputDeviceBehavior", newDeviceOptionBox->itemData(index).toInt()); + g_config->save(); +} +//---------------------------------------------------------------------------- void InputConfDialog_t::port1Select(int index) { //printf("Port 1 Number:%i \n", index); diff --git a/src/drivers/Qt/InputConf.h b/src/drivers/Qt/InputConf.h index aa9d3d48..331afb74 100644 --- a/src/drivers/Qt/InputConf.h +++ b/src/drivers/Qt/InputConf.h @@ -43,6 +43,7 @@ protected: QComboBox *expPortComboxBox; QPushButton *loadConfigButton; QPushButton *saveConfigButton; + QComboBox* newDeviceOptionBox; int curNesInput[3]; int usrNesInput[3]; @@ -62,6 +63,7 @@ private slots: void port1Select(int index); void port2Select(int index); void expSelect(int index); + void newDeviceSettingsChange(int index); void fourScoreChanged(int state); void port2MicChanged(int state); void autoPresetChanged(int state); diff --git a/src/drivers/Qt/config.cpp b/src/drivers/Qt/config.cpp index 50442ffb..e68b3f9e 100644 --- a/src/drivers/Qt/config.cpp +++ b/src/drivers/Qt/config.cpp @@ -638,6 +638,7 @@ InitConfig() config->addOption("SDL.AutofireOffFrames", 1); config->addOption("SDL.AutofireCustomOnFrames" , 1); config->addOption("SDL.AutofireCustomOffFrames", 1); + config->addOption("SDL.NewInputDeviceBehavior", 1); // display input config->addOption("inputdisplay", "SDL.InputDisplay", 0); diff --git a/src/drivers/Qt/input.cpp b/src/drivers/Qt/input.cpp index d91a7420..d4a3533e 100644 --- a/src/drivers/Qt/input.cpp +++ b/src/drivers/Qt/input.cpp @@ -18,6 +18,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include #include #include #include @@ -1362,7 +1363,51 @@ pollEventsSDL() break; case SDL_JOYDEVICEADDED: - AddJoystick(event.jdevice.which); + { + int devIdx = AddJoystick(event.jdevice.which); + if (devIdx >= 0) + { + int newDeviceBehavior = 0; + g_config->getOption("SDL.NewInputDeviceBehavior", &newDeviceBehavior); + + if (newDeviceBehavior == 1) + { + bool wasPaused = FCEUI_EmulationPaused() ? true : false; + + jsDev_t* jsDev = getJoystickDevice(devIdx); + + QString msg = "A new joystick/gamepad device has been detected.\n"; + + if (jsDev != nullptr) + { + msg += QString("\nDevice ") + QString::number(devIdx) + QString(": "); + msg += QString(jsDev->getName()) + "\n"; + } + msg += "\nDo you wish to reload button bindings?"; + QMessageBox msgBox(QMessageBox::Question, QObject::tr("New Device Detected"), msg, + QMessageBox::No | QMessageBox::Yes, consoleWindow); + + msgBox.setDefaultButton( QMessageBox::Yes ); + + FCEUI_SetEmulationPaused( EMULATIONPAUSED_PAUSED ); + + int answer = msgBox.exec(); + + if ( answer == QMessageBox::Yes ) + { + initGamepadBindings(); + } + if (!wasPaused) + { + FCEUI_SetEmulationPaused(0); + } + } + else if (newDeviceBehavior == 2) + { + initGamepadBindings(); + } + } + } break; case SDL_JOYDEVICEREMOVED: RemoveJoystick(event.jdevice.which); diff --git a/src/drivers/Qt/sdl-joystick.h b/src/drivers/Qt/sdl-joystick.h index e2f9b54c..a192cedf 100644 --- a/src/drivers/Qt/sdl-joystick.h +++ b/src/drivers/Qt/sdl-joystick.h @@ -98,4 +98,6 @@ extern GamePad_t GamePad[4]; jsDev_t *getJoystickDevice(int devNum); +void initGamepadBindings(); + #endif From 2f2279f5ffa70e40937fb59d3dd4d785bdae4fdd Mon Sep 17 00:00:00 2001 From: harry Date: Sun, 18 Feb 2024 21:10:24 -0500 Subject: [PATCH 2/2] Added more functions to JS File object. --- src/drivers/Qt/QtScriptManager.cpp | 234 +++++++++++++++++++++++++++-- src/drivers/Qt/QtScriptManager.h | 23 ++- src/drivers/Qt/sdl-joystick.cpp | 4 +- 3 files changed, 246 insertions(+), 15 deletions(-) diff --git a/src/drivers/Qt/QtScriptManager.cpp b/src/drivers/Qt/QtScriptManager.cpp index d75df940..d85c7f15 100644 --- a/src/drivers/Qt/QtScriptManager.cpp +++ b/src/drivers/Qt/QtScriptManager.cpp @@ -126,6 +126,11 @@ FileScriptObject::~FileScriptObject() //printf("FileScriptObject %p Destructor: %i\n", this, numInstances); } //---------------------------------------------------- +void FileScriptObject::setTemporary(bool value) +{ + tmp = value; +} +//---------------------------------------------------- void FileScriptObject::setFilePath(const QString& path) { if (isOpen()) @@ -161,7 +166,14 @@ bool FileScriptObject::open(int mode) return false; } - file = new QFile(filepath); + if (tmp) + { + file = new QTemporaryFile(); + } + else + { + file = new QFile(filepath); + } if (file == nullptr) { @@ -184,6 +196,10 @@ bool FileScriptObject::open(int mode) { deviceMode |= QIODevice::Append; } + if (mode & Truncate) + { + deviceMode |= QIODevice::Truncate; + } bool success = file->open(deviceMode); @@ -203,6 +219,116 @@ bool FileScriptObject::isOpen() return flag; } //---------------------------------------------------- +bool FileScriptObject::isReadable() +{ + bool flag = false; + + if (file != nullptr) + { + flag = file->isReadable(); + } + return flag; +} +//---------------------------------------------------- +bool FileScriptObject::isWritable() +{ + bool flag = false; + + if (file != nullptr) + { + flag = file->isWritable(); + } + return flag; +} +//---------------------------------------------------- +bool FileScriptObject::atEnd() +{ + bool retval = false; + + if (file != nullptr) + { + retval = file->atEnd(); + } + return retval; +} +//---------------------------------------------------- +bool FileScriptObject::truncate() +{ + bool retval = false; + + if (file != nullptr) + { + retval = file->resize(0); + } + return retval; +} +//---------------------------------------------------- +bool FileScriptObject::resize(int64_t size) +{ + bool retval = false; + + if (file != nullptr) + { + retval = file->resize(size); + } + return retval; +} +//---------------------------------------------------- +int64_t FileScriptObject::skip(int64_t size) +{ + int64_t retval = 0; + + if (file != nullptr) + { + retval = file->skip(size); + } + return retval; +} +//---------------------------------------------------- +bool FileScriptObject::seek(int64_t pos) +{ + bool retval = false; + + if (file != nullptr) + { + retval = file->seek(pos); + } + return retval; +} +//---------------------------------------------------- +int64_t FileScriptObject::pos() +{ + int64_t retval = 0; + + if (file != nullptr) + { + retval = file->pos(); + } + return retval; +} +//---------------------------------------------------- +int64_t FileScriptObject::bytesAvailable() +{ + int64_t retval = 0; + + if (file != nullptr) + { + retval = file->bytesAvailable(); + } + return retval; +} +//---------------------------------------------------- +bool FileScriptObject::flush() +{ + bool retval = false; + + if (file != nullptr) + { + retval = file->flush(); + } + return retval; +} +//---------------------------------------------------- void FileScriptObject::close() { if (file != nullptr) @@ -211,6 +337,10 @@ void FileScriptObject::close() { file->close(); } + if (tmp) + { + file->remove(); + } delete file; file = nullptr; } @@ -229,30 +359,112 @@ int FileScriptObject::writeString(const QString& s) return bytesWritten; } //---------------------------------------------------- -QJSValue FileScriptObject::readLine() +int FileScriptObject::writeData(const QByteArray& s) { - QJSValue obj; + if ( (file == nullptr) || !file->isOpen()) + { + auto* engine = FCEU::JSEngine::getCurrent(); + engine->throwError(QJSValue::GenericError, "file is not open "); + return -1; + } + int bytesWritten = file->write( s ); + + return bytesWritten; +} +//---------------------------------------------------- +QString FileScriptObject::readLine() +{ + QString line; if ( (file == nullptr) || !file->isOpen()) { auto* engine = FCEU::JSEngine::getCurrent(); engine->throwError(QJSValue::GenericError, "file is not open "); - return obj; + return line; } - auto* engine = FCEU::JSEngine::getCurrent(); - QByteArray byteArray = file->readLine(); - QString line = QString::fromLocal8Bit(byteArray); + line = QString::fromLocal8Bit(byteArray); //printf("ReadLine: %s\n", line.toLocal8Bit().constData()); - obj = engine->newObject(); + return line; +} +//---------------------------------------------------- +QByteArray FileScriptObject::readData(unsigned int maxBytes) +{ + QJSValue arrayBuffer; + QByteArray byteArray; - obj.setProperty("text", line); - obj.setProperty("size", static_cast(line.size())); + auto* engine = FCEU::JSEngine::getCurrent(); - return obj; + if ( (file == nullptr) || !file->isOpen()) + { + engine->throwError(QJSValue::GenericError, "file is not open "); + return byteArray; + } + if (maxBytes > 0) + { + byteArray = file->read(maxBytes); + } + else + { + byteArray = file->readAll(); + } + + //arrayBuffer = engine->toScriptValue(byteArray); + + return byteArray; +} +//---------------------------------------------------- +QByteArray FileScriptObject::peekData(unsigned int maxBytes) +{ + QJSValue arrayBuffer; + QByteArray byteArray; + + auto* engine = FCEU::JSEngine::getCurrent(); + + if ( (file == nullptr) || !file->isOpen()) + { + engine->throwError(QJSValue::GenericError, "file is not open "); + return byteArray; + } + byteArray = file->peek(maxBytes); + + //arrayBuffer = engine->toScriptValue(byteArray); + + return byteArray; +} +//---------------------------------------------------- +bool FileScriptObject::putChar(char c) +{ + if ( (file == nullptr) || !file->isOpen()) + { + auto* engine = FCEU::JSEngine::getCurrent(); + engine->throwError(QJSValue::GenericError, "file is not open "); + return -1; + } + bool success = file->putChar(c); + + return success; +} +//---------------------------------------------------- +char FileScriptObject::getChar() +{ + if ( (file == nullptr) || !file->isOpen()) + { + auto* engine = FCEU::JSEngine::getCurrent(); + engine->throwError(QJSValue::GenericError, "file is not open "); + return -1; + } + char c = -1; + bool success = file->getChar(&c); + + if (!success) + { + c = -1; + } + return c; } //---------------------------------------------------- //---- Joypad Object diff --git a/src/drivers/Qt/QtScriptManager.h b/src/drivers/Qt/QtScriptManager.h index 8c0ce5ba..c481ba45 100644 --- a/src/drivers/Qt/QtScriptManager.h +++ b/src/drivers/Qt/QtScriptManager.h @@ -124,13 +124,15 @@ public: ReadOnly = 0x01, WriteOnly = 0x02, ReadWrite = 0x03, - Append = 0x04 + Append = 0x04, + Truncate = 0x08 }; Q_ENUM(Mode); private: static int numInstances; QString filepath; + bool tmp = false; QFile *file = nullptr; @@ -138,12 +140,29 @@ public slots: Q_INVOKABLE bool open(int mode = ReadOnly); Q_INVOKABLE void close(); Q_INVOKABLE bool isOpen(); + Q_INVOKABLE bool isReadable(); + Q_INVOKABLE bool isWritable(); + Q_INVOKABLE bool flush(); + Q_INVOKABLE bool atEnd(); + Q_INVOKABLE bool truncate(); + Q_INVOKABLE bool resize(int64_t size); + Q_INVOKABLE bool seek(int64_t pos); + Q_INVOKABLE int64_t pos(); + Q_INVOKABLE int64_t bytesAvailable(); + Q_INVOKABLE bool isTemporary(){ return tmp; } + Q_INVOKABLE void setTemporary(bool value); Q_INVOKABLE void setFilePath(const QString& path); Q_INVOKABLE QString fileName(); Q_INVOKABLE QString fileSuffix(); Q_INVOKABLE QString filePath(){ return filepath; } - Q_INVOKABLE QJSValue readLine(); + Q_INVOKABLE QString readLine(); + Q_INVOKABLE int64_t skip(int64_t size); + Q_INVOKABLE QByteArray readData(unsigned int maxBytes = 0); + Q_INVOKABLE QByteArray peekData(unsigned int maxSize); Q_INVOKABLE int writeString(const QString& s); + Q_INVOKABLE int writeData(const QByteArray& data); + Q_INVOKABLE bool putChar(char c); + Q_INVOKABLE char getChar(); }; class JoypadScriptObject: public QObject diff --git a/src/drivers/Qt/sdl-joystick.cpp b/src/drivers/Qt/sdl-joystick.cpp index 50dec3c8..29f6b28c 100644 --- a/src/drivers/Qt/sdl-joystick.cpp +++ b/src/drivers/Qt/sdl-joystick.cpp @@ -1298,7 +1298,7 @@ int KillJoysticks(void) { jsDev[n].close(); } - FCEU_printf("Shutting down SDL joystick/game constroller subsystem\n"); + FCEU_printf("Shutting down SDL joystick/gamepad subsystem\n"); SDL_QuitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER); s_jinited = 0; @@ -1421,7 +1421,7 @@ int InitJoysticks(void) { return 1; } - FCEU_printf("Initializing SDL joystick/game constroller subsystem\n"); + FCEU_printf("Initializing SDL joystick/gamepad subsystem\n"); SDL_InitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER); total = SDL_NumJoysticks();