diff --git a/CHANGES b/CHANGES index c035d71d0..fdabf6914 100644 --- a/CHANGES +++ b/CHANGES @@ -44,6 +44,7 @@ Other fixes: - VFS: Failed file mapping should return NULL on POSIX Misc: - Core: Suspend runloop when a core crashes + - Debugger: Save and restore CLI history - GB Video: Add default SGB border - GBA: Automatically skip BIOS if ROM has invalid logo - GBA DMA: Enhanced logging (closes mgba.io/i/2454) diff --git a/src/feature/editline/cli-el-backend.c b/src/feature/editline/cli-el-backend.c index 530019b03..2ae6d7612 100644 --- a/src/feature/editline/cli-el-backend.c +++ b/src/feature/editline/cli-el-backend.c @@ -5,7 +5,9 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "cli-el-backend.h" +#include #include +#include #include @@ -60,12 +62,47 @@ void _CLIDebuggerEditLineInit(struct CLIDebuggerBackend* be) { HistEvent ev; history(elbe->histate, &ev, H_SETSIZE, 200); el_set(elbe->elstate, EL_HIST, history, elbe->histate); + + char path[PATH_MAX + 1]; + mCoreConfigDirectory(path, PATH_MAX); + if (path[0]) { + strncat(path, PATH_SEP, PATH_MAX); + strncat(path, "cli_history.log", PATH_MAX); + struct VFile* vf = VFileOpen(path, O_RDONLY); + if (vf) { + char line[512]; + while (vf->readline(vf, line, sizeof(line)) > 0) { + history(elbe->histate, &ev, H_ENTER, line); + } + vf->close(vf); + } + } + _activeDebugger = be->p; signal(SIGINT, _breakIntoDefault); } void _CLIDebuggerEditLineDeinit(struct CLIDebuggerBackend* be) { struct CLIDebuggerEditLineBackend* elbe = (struct CLIDebuggerEditLineBackend*) be; + char path[PATH_MAX + 1]; + mCoreConfigDirectory(path, PATH_MAX); + if (path[0]) { + strncat(path, PATH_SEP, PATH_MAX); + strncat(path, "cli_history.log", PATH_MAX); + struct VFile* vf = VFileOpen(path, O_WRONLY | O_CREAT | O_TRUNC); + if (vf) { + HistEvent ev = {0}; + if (history(elbe->histate, &ev, H_FIRST) >= 0) { + do { + if (!ev.str || ev.str[0] == '\n') { + continue; + } + vf->write(vf, ev.str, strlen(ev.str)); + } while (history(elbe->histate, &ev, H_NEXT) >= 0); + } + vf->close(vf); + } + } history_end(elbe->histate); el_end(elbe->elstate); free(elbe); diff --git a/src/platform/qt/DebuggerConsole.cpp b/src/platform/qt/DebuggerConsole.cpp index 2d4261afb..0a4beb909 100644 --- a/src/platform/qt/DebuggerConsole.cpp +++ b/src/platform/qt/DebuggerConsole.cpp @@ -26,6 +26,8 @@ DebuggerConsole::DebuggerConsole(DebuggerConsoleController* controller, QWidget* connect(controller, &DebuggerConsoleController::log, this, &DebuggerConsole::log); connect(m_ui.breakpoint, &QAbstractButton::clicked, controller, &DebuggerController::attach); connect(m_ui.breakpoint, &QAbstractButton::clicked, controller, &DebuggerController::breakInto); + + controller->historyLoad(); } void DebuggerConsole::log(const QString& line) { @@ -41,7 +43,6 @@ void DebuggerConsole::postLine() { if (line.isEmpty()) { m_consoleController->enterLine(QString("\n")); } else { - m_history.append(line); m_historyOffset = 0; log(QString("> %1\n").arg(line)); m_consoleController->enterLine(line); @@ -52,7 +53,8 @@ bool DebuggerConsole::eventFilter(QObject*, QEvent* event) { if (event->type() != QEvent::KeyPress) { return false; } - if (m_history.isEmpty()) { + QStringList history = m_consoleController->history(); + if (history.isEmpty()) { return false; } QKeyEvent* keyEvent = static_cast(event); @@ -64,7 +66,7 @@ bool DebuggerConsole::eventFilter(QObject*, QEvent* event) { --m_historyOffset; break; case Qt::Key_Up: - if (m_historyOffset >= m_history.size()) { + if (m_historyOffset >= history.size()) { return false; } ++m_historyOffset; @@ -73,7 +75,7 @@ bool DebuggerConsole::eventFilter(QObject*, QEvent* event) { m_historyOffset = 0; break; case Qt::Key_Home: - m_historyOffset = m_history.size(); + m_historyOffset = history.size(); break; default: return false; @@ -81,7 +83,7 @@ bool DebuggerConsole::eventFilter(QObject*, QEvent* event) { if (m_historyOffset == 0) { m_ui.prompt->clear(); } else { - m_ui.prompt->setText(m_history[m_history.size() - m_historyOffset]); + m_ui.prompt->setText(history[history.size() - m_historyOffset]); } return true; } diff --git a/src/platform/qt/DebuggerConsole.h b/src/platform/qt/DebuggerConsole.h index 5fe523c24..d13ef4a13 100644 --- a/src/platform/qt/DebuggerConsole.h +++ b/src/platform/qt/DebuggerConsole.h @@ -26,7 +26,6 @@ protected: private: Ui::DebuggerConsole m_ui; - QStringList m_history; int m_historyOffset; DebuggerConsoleController* m_consoleController; diff --git a/src/platform/qt/DebuggerConsoleController.cpp b/src/platform/qt/DebuggerConsoleController.cpp index b77609a5a..24ef25dba 100644 --- a/src/platform/qt/DebuggerConsoleController.cpp +++ b/src/platform/qt/DebuggerConsoleController.cpp @@ -5,7 +5,9 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "DebuggerConsoleController.h" +#include "ConfigController.h" #include "CoreController.h" +#include "LogController.h" #include #include @@ -50,12 +52,12 @@ void DebuggerConsoleController::detach() { } } DebuggerController::detach(); + historySave(); } void DebuggerConsoleController::attachInternal() { CoreController::Interrupter interrupter(m_gameController); QMutexLocker lock(&m_mutex); - m_history.clear(); mCore* core = m_gameController->thread()->core; CLIDebuggerAttachBackend(&m_cliDebugger, &m_backend.d); CLIDebuggerAttachSystem(&m_cliDebugger, core->cliDebuggerSystem(core)); @@ -129,3 +131,37 @@ void DebuggerConsoleController::historyAppend(struct CLIDebuggerBackend* be, con QMutexLocker lock(&self->m_mutex); self->m_history.append(QString::fromUtf8(line)); } + +void DebuggerConsoleController::historyLoad() { + QFile log(ConfigController::configDir() + "/cli_history.log"); + QStringList history; + if (!log.open(QIODevice::ReadOnly | QIODevice::Text)) { + return; + } + while (true) { + QByteArray line = log.readLine(); + if (line.isEmpty()) { + break; + } + if (line.endsWith("\r\n")) { + line.chop(2); + } else if (line.endsWith("\n")) { + line.chop(1); + } + history.append(QString::fromUtf8(line)); + } + QMutexLocker lock(&m_mutex); + m_history = history; +} + +void DebuggerConsoleController::historySave() { + QFile log(ConfigController::configDir() + "/cli_history.log"); + if (!log.open(QIODevice::WriteOnly | QIODevice::Text)) { + LOG(QT, WARN) << tr("Could not open CLI history for writing"); + return; + } + for (const QString& line : m_history) { + log.write(line.toUtf8()); + log.write("\n"); + } +} diff --git a/src/platform/qt/DebuggerConsoleController.h b/src/platform/qt/DebuggerConsoleController.h index 5989b7441..051d78666 100644 --- a/src/platform/qt/DebuggerConsoleController.h +++ b/src/platform/qt/DebuggerConsoleController.h @@ -23,6 +23,8 @@ Q_OBJECT public: DebuggerConsoleController(QObject* parent = nullptr); + QStringList history() const { return m_history; } + signals: void log(const QString&); void lineAppend(const QString&); @@ -30,6 +32,8 @@ signals: public slots: void enterLine(const QString&); virtual void detach() override; + void historyLoad(); + void historySave(); protected: virtual void attachInternal() override;