fceux/src/drivers/Qt/LuaControl.cpp

546 lines
12 KiB
C++

/* FCE Ultra - NES/Famicom Emulator
*
* Copyright notice for this file:
* Copyright (C) 2020 mjbudd77
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
// LuaControl.cpp
//
#include <stdio.h>
#include <string.h>
#include <list>
#ifdef WIN32
#include <Windows.h>
#endif
#include <QTextEdit>
#include <QFileDialog>
#include <QMessageBox>
#include <QSettings>
#include "../../fceu.h"
#ifdef _S9XLUA_H
#include "../../fceulua.h"
#endif
#include "common/os_utils.h"
#include "Qt/LuaControl.h"
#include "Qt/main.h"
#include "Qt/input.h"
#include "Qt/config.h"
#include "Qt/keyscan.h"
#include "Qt/fceuWrapper.h"
#include "Qt/ConsoleUtilities.h"
static bool luaScriptRunning = false;
static bool updateLuaDisplay = false;
static bool openLuaKillMsgBox = false;
static int luaKillMsgBoxRetVal = 0;
struct luaConsoleOutputBuffer
{
int head;
int tail;
int size;
char *buf;
luaConsoleOutputBuffer(void)
{
tail = head = 0;
size = 4096;
buf = (char *)malloc(size);
}
~luaConsoleOutputBuffer(void)
{
if (buf)
{
free(buf);
buf = NULL;
}
}
void addLine(const char *l)
{
int i = 0;
//printf("Adding Line %i: '%s'\n", head, l );
while (l[i] != 0)
{
buf[head] = l[i];
i++;
head = (head + 1) % size;
if (head == tail)
{
tail = (tail + 1) % size;
}
}
}
void clear(void)
{
tail = head = 0;
}
};
static luaConsoleOutputBuffer outBuf;
static std::list<LuaControlDialog_t *> winList;
static void updateLuaWindows(void);
//----------------------------------------------------
LuaControlDialog_t::LuaControlDialog_t(QWidget *parent)
: QDialog(parent, Qt::Window)
{
QVBoxLayout *mainLayout;
QHBoxLayout *hbox;
QPushButton *closeButton;
QLabel *lbl;
std::string filename;
QSettings settings;
resize(512, 512);
setWindowTitle(tr("Lua Script Control"));
mainLayout = new QVBoxLayout();
lbl = new QLabel(tr("Script File:"));
scriptPath = new QLineEdit();
scriptArgs = new QLineEdit();
g_config->getOption("SDL.LastLoadLua", &filename);
scriptPath->setText( tr(filename.c_str()) );
scriptPath->setClearButtonEnabled(true);
scriptArgs->setClearButtonEnabled(true);
luaOutput = new QTextEdit();
luaOutput->setReadOnly(true);
hbox = new QHBoxLayout();
browseButton = new QPushButton(tr("Browse"));
stopButton = new QPushButton(tr("Stop"));
if (luaScriptRunning)
{
startButton = new QPushButton(tr("Restart"));
}
else
{
startButton = new QPushButton(tr("Start"));
}
stopButton->setEnabled(luaScriptRunning);
connect(browseButton, SIGNAL(clicked()), this, SLOT(openLuaScriptFile(void)));
connect(stopButton, SIGNAL(clicked()), this, SLOT(stopLuaScript(void)));
connect(startButton, SIGNAL(clicked()), this, SLOT(startLuaScript(void)));
hbox->addWidget(browseButton);
hbox->addWidget(stopButton);
hbox->addWidget(startButton);
mainLayout->addWidget(lbl);
mainLayout->addWidget(scriptPath);
mainLayout->addLayout(hbox);
hbox = new QHBoxLayout();
lbl = new QLabel(tr("Arguments:"));
hbox->addWidget(lbl);
hbox->addWidget(scriptArgs);
mainLayout->addLayout(hbox);
lbl = new QLabel(tr("Output Console:"));
mainLayout->addWidget(lbl);
mainLayout->addWidget(luaOutput);
closeButton = new QPushButton( tr("Close") );
closeButton->setIcon(style()->standardIcon(QStyle::SP_DialogCloseButton));
connect(closeButton, SIGNAL(clicked(void)), this, SLOT(closeWindow(void)));
hbox = new QHBoxLayout();
hbox->addStretch(5);
hbox->addWidget( closeButton, 1 );
mainLayout->addLayout( hbox );
setLayout(mainLayout);
winList.push_back(this);
periodicTimer = new QTimer(this);
connect(periodicTimer, &QTimer::timeout, this, &LuaControlDialog_t::updatePeriodic);
periodicTimer->start(200); // 5hz
restoreGeometry(settings.value("LuaWindow/geometry").toByteArray());
}
//----------------------------------------------------
LuaControlDialog_t::~LuaControlDialog_t(void)
{
QSettings settings;
std::list<LuaControlDialog_t *>::iterator it;
//printf("Destroy Lua Control Window\n");
periodicTimer->stop();
for (it = winList.begin(); it != winList.end(); it++)
{
if ((*it) == this)
{
winList.erase(it);
//printf("Removing Lua Window\n");
break;
}
}
settings.setValue("LuaWindow/geometry", saveGeometry());
}
//----------------------------------------------------
void LuaControlDialog_t::closeEvent(QCloseEvent *event)
{
//printf("Lua Control Close Window Event\n");
done(0);
deleteLater();
event->accept();
}
//----------------------------------------------------
void LuaControlDialog_t::closeWindow(void)
{
//printf("Lua Control Close Window\n");
done(0);
deleteLater();
}
//----------------------------------------------------
void LuaControlDialog_t::updatePeriodic(void)
{
//printf("Update Lua\n");
if (updateLuaDisplay)
{
updateLuaWindows();
updateLuaDisplay = false;
}
if (openLuaKillMsgBox)
{
openLuaKillMessageBox();
openLuaKillMsgBox = false;
}
}
//----------------------------------------------------
void LuaControlDialog_t::openLuaKillMessageBox(void)
{
int ret;
QMessageBox msgBox(this);
luaKillMsgBoxRetVal = 0;
msgBox.setIcon(QMessageBox::Warning);
msgBox.setText(tr("The Lua script running has been running a long time.\nIt may have gone crazy. Kill it? (I won't ask again if you say No)\n"));
msgBox.setStandardButtons(QMessageBox::Yes);
msgBox.addButton(QMessageBox::No);
msgBox.setDefaultButton(QMessageBox::No);
ret = msgBox.exec();
if (ret == QMessageBox::Yes)
{
luaKillMsgBoxRetVal = 1;
}
}
//----------------------------------------------------
void LuaControlDialog_t::openLuaScriptFile(void)
{
#ifdef _S9XLUA_H
int ret, useNativeFileDialogVal;
QString filename;
std::string last;
char dir[2048];
char exePath[2048];
const char *luaPath;
QFileDialog dialog(this, tr("Open LUA Script"));
QList<QUrl> urls;
QDir d;
fceuExecutablePath(exePath, sizeof(exePath));
//urls = dialog.sidebarUrls();
urls << QUrl::fromLocalFile(QDir::rootPath());
urls << QUrl::fromLocalFile(QStandardPaths::standardLocations(QStandardPaths::HomeLocation).first());
urls << QUrl::fromLocalFile(QStandardPaths::standardLocations(QStandardPaths::DesktopLocation).first());
urls << QUrl::fromLocalFile(QStandardPaths::standardLocations(QStandardPaths::DownloadLocation).first());
urls << QUrl::fromLocalFile(QDir(FCEUI_GetBaseDirectory()).absolutePath());
if (exePath[0] != 0)
{
d.setPath(QString(exePath) + "/../luaScripts");
if (d.exists())
{
urls << QUrl::fromLocalFile(d.absolutePath());
}
}
#ifndef WIN32
d.setPath("/usr/share/fceux/luaScripts");
if (d.exists())
{
urls << QUrl::fromLocalFile(d.absolutePath());
}
#endif
luaPath = getenv("LUA_PATH");
// Parse LUA_PATH and add to urls
if (luaPath)
{
int i, j;
char stmp[2048];
i = j = 0;
while (luaPath[i] != 0)
{
if (luaPath[i] == ';')
{
stmp[j] = 0;
if (j > 0)
{
d.setPath(stmp);
if (d.exists())
{
urls << QUrl::fromLocalFile(d.absolutePath());
}
}
j = 0;
}
else
{
stmp[j] = luaPath[i];
j++;
}
i++;
}
stmp[j] = 0;
if (j > 0)
{
d.setPath(stmp);
if (d.exists())
{
urls << QUrl::fromLocalFile(d.absolutePath());
}
}
}
dialog.setFileMode(QFileDialog::ExistingFile);
dialog.setNameFilter(tr("LUA Scripts (*.lua *.LUA) ;; All files (*)"));
dialog.setViewMode(QFileDialog::List);
dialog.setFilter(QDir::AllEntries | QDir::AllDirs | QDir::Hidden);
dialog.setLabelText(QFileDialog::Accept, tr("Load"));
g_config->getOption("SDL.LastLoadLua", &last);
if (last.size() == 0)
{
#ifdef WIN32
last.assign(FCEUI_GetBaseDirectory());
#else
last.assign("/usr/share/fceux/luaScripts");
#endif
}
getDirFromFile(last.c_str(), dir, sizeof(dir));
dialog.setDirectory(tr(dir));
// Check config option to use native file dialog or not
g_config->getOption("SDL.UseNativeFileDialog", &useNativeFileDialogVal);
dialog.setOption(QFileDialog::DontUseNativeDialog, !useNativeFileDialogVal);
dialog.setSidebarUrls(urls);
ret = dialog.exec();
if (ret)
{
QStringList fileList;
fileList = dialog.selectedFiles();
if (fileList.size() > 0)
{
filename = fileList[0];
}
}
if (filename.isNull())
{
return;
}
qDebug() << "selected file path : " << filename.toUtf8();
g_config->setOption("SDL.LastLoadLua", filename.toStdString().c_str());
scriptPath->setText(filename);
#endif
}
//----------------------------------------------------
void LuaControlDialog_t::startLuaScript(void)
{
#ifdef _S9XLUA_H
outBuf.clear();
FCEU_WRAPPER_LOCK();
if (0 == FCEU_LoadLuaCode(scriptPath->text().toStdString().c_str(), scriptArgs->text().toStdString().c_str()))
{
char error_msg[2048];
sprintf( error_msg, "Error: Could not open the selected lua script: '%s'\n", scriptPath->text().toStdString().c_str());
FCEUD_PrintError(error_msg);
}
FCEU_WRAPPER_UNLOCK();
#endif
}
//----------------------------------------------------
void LuaControlDialog_t::stopLuaScript(void)
{
#ifdef _S9XLUA_H
FCEU_WRAPPER_LOCK();
FCEU_LuaStop();
FCEU_WRAPPER_UNLOCK();
#endif
}
//----------------------------------------------------
void LuaControlDialog_t::refreshState(void)
{
int i;
std::string luaOutputText;
if (luaScriptRunning)
{
stopButton->setEnabled(true);
startButton->setText(tr("Restart"));
}
else
{
stopButton->setEnabled(false);
startButton->setText(tr("Start"));
}
i = outBuf.tail;
while (i != outBuf.head)
{
luaOutputText.append(1, outBuf.buf[i]);
i = (i + 1) % outBuf.size;
}
luaOutput->setText(luaOutputText.c_str());
luaOutput->moveCursor(QTextCursor::End);
}
//----------------------------------------------------
static void updateLuaWindows(void)
{
std::list<LuaControlDialog_t *>::iterator it;
for (it = winList.begin(); it != winList.end(); it++)
{
(*it)->refreshState();
}
}
//----------------------------------------------------
void WinLuaOnStart(intptr_t hDlgAsInt)
{
luaScriptRunning = true;
//printf("Lua Script Running: %i \n", luaScriptRunning );
updateLuaDisplay = true;
}
//----------------------------------------------------
void WinLuaOnStop(intptr_t hDlgAsInt)
{
luaScriptRunning = false;
//printf("Lua Script Running: %i \n", luaScriptRunning );
updateLuaDisplay = true;
}
//----------------------------------------------------
void PrintToWindowConsole(intptr_t hDlgAsInt, const char *str)
{
//printf("%s\n", str );
outBuf.addLine(str);
updateLuaDisplay = true;
}
//----------------------------------------------------
#ifdef WIN32
int LuaPrintfToWindowConsole(_In_z_ _Printf_format_string_ const char *format, ...)
#else
int LuaPrintfToWindowConsole(const char *__restrict format, ...) throw()
#endif
{
int retval;
va_list args;
char msg[2048];
va_start(args, format);
retval = ::vsnprintf(msg, sizeof(msg), format, args);
va_end(args);
msg[sizeof(msg) - 1] = 0;
outBuf.addLine(msg);
updateLuaDisplay = true;
return (retval);
};
//----------------------------------------------------
int LuaKillMessageBox(void)
{
//printf("Kill Lua Prompted\n");
luaKillMsgBoxRetVal = 0;
openLuaKillMsgBox = true;
while (openLuaKillMsgBox)
{
msleep(100);
}
return luaKillMsgBoxRetVal;
}
//----------------------------------------------------