From cf09c67ab6146a104e2faa1b4f759639512a4853 Mon Sep 17 00:00:00 2001 From: Eoin O'Neill Date: Sun, 10 Oct 2021 15:09:48 -0700 Subject: [PATCH 1/3] Ignore QTCreator IDE generated import files. --- .gitignore | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.gitignore b/.gitignore index ab5676c5..a7d15d98 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,12 @@ *.rar */out */bin + +# No QTCreator project metadata files. +*.cflags +*.config +*.creator +*.cxxflags +*.files +*.includes + From 33d0c1f3fd5e0daa1dddac02570210df002b35c1 Mon Sep 17 00:00:00 2001 From: Eoin O'Neill Date: Sun, 10 Oct 2021 21:58:14 -0700 Subject: [PATCH 2/3] MemoryEditor: Added ability to track memory changes with highlighting. Memory will highlight as it changes and fade out over 1 second. This feature can be enabled or disabled on the right-hand side panel. --- bsnes/ui-qt/debugger/tools/memory.cpp | 4 + bsnes/ui-qt/debugger/tools/memory.moc.hpp | 1 + .../debugger/tools/qhexedit2/qhexedit.cpp | 99 +++++++++++++++++-- .../debugger/tools/qhexedit2/qhexedit.moc.hpp | 25 ++++- 4 files changed, 120 insertions(+), 9 deletions(-) diff --git a/bsnes/ui-qt/debugger/tools/memory.cpp b/bsnes/ui-qt/debugger/tools/memory.cpp index 0400f4b4..4be7d3c9 100644 --- a/bsnes/ui-qt/debugger/tools/memory.cpp +++ b/bsnes/ui-qt/debugger/tools/memory.cpp @@ -54,6 +54,9 @@ MemoryEditor::MemoryEditor() { autoUpdateBox = new QCheckBox("Auto update"); controlLayout->addWidget(autoUpdateBox); + trackMemoryBox = new QCheckBox("Highlight changes"); + controlLayout->addWidget(trackMemoryBox); + refreshButton = new QPushButton("Refresh"); controlLayout->addWidget(refreshButton); @@ -107,6 +110,7 @@ MemoryEditor::MemoryEditor() { connect(refreshButton, SIGNAL(released()), this, SLOT(refresh())); connect(exportButton, SIGNAL(released()), this, SLOT(exportMemory())); connect(importButton, SIGNAL(released()), this, SLOT(importMemory())); + connect(trackMemoryBox, SIGNAL(toggled(bool)), editor, SLOT(setMemoryTracking(bool))); searchPos = -1; breakpointPos = -1; diff --git a/bsnes/ui-qt/debugger/tools/memory.moc.hpp b/bsnes/ui-qt/debugger/tools/memory.moc.hpp index b3be5871..4ead8c03 100644 --- a/bsnes/ui-qt/debugger/tools/memory.moc.hpp +++ b/bsnes/ui-qt/debugger/tools/memory.moc.hpp @@ -10,6 +10,7 @@ class MemoryEditor : public Window { QComboBox *source; QLineEdit *addr; QCheckBox *autoUpdateBox; + QCheckBox *trackMemoryBox; QPushButton *refreshButton; QHBoxLayout *toolLayout; diff --git a/bsnes/ui-qt/debugger/tools/qhexedit2/qhexedit.cpp b/bsnes/ui-qt/debugger/tools/qhexedit2/qhexedit.cpp index 2d2a87ab..a49b055d 100644 --- a/bsnes/ui-qt/debugger/tools/qhexedit2/qhexedit.cpp +++ b/bsnes/ui-qt/debugger/tools/qhexedit2/qhexedit.cpp @@ -8,6 +8,7 @@ const int HEXCHARS_IN_LINE = 47; const int BYTES_PER_LINE = 16; +const quint16 FRAMES_TO_FADE_HEXBG = 30; // ********************************************************************** Constructor, destructor @@ -24,6 +25,9 @@ QHexEdit::QHexEdit(QWidget *parent) : QAbstractScrollArea(parent) _cursorTimer.setInterval(500); _cursorTimer.start(); + _animatedHexChangeTimer.setInterval(64); + _animatedHexChangeTimer.start(); + _editorSize = 0; _lastEventSize = 0; _asciiArea = true; @@ -32,8 +36,10 @@ QHexEdit::QHexEdit(QWidget *parent) : QAbstractScrollArea(parent) _highlighting = true; _readOnly = false; _cursorPosition = 0; + _trackMemoryChanges = false; connect(&_cursorTimer, SIGNAL(timeout()), this, SLOT(updateCursor())); + connect(&_animatedHexChangeTimer, SIGNAL(timeout()), this, SLOT(updateAnimatedHexValues())); connect(verticalScrollBar(), SIGNAL(valueChanged(int)), this, SLOT(adjust())); connect(_undoStack, SIGNAL(indexChanged(int)), this, SLOT(dataChangedPrivate(int))); @@ -73,8 +79,12 @@ QColor QHexEdit::addressAreaColor() void QHexEdit::setAddressOffset(qint64 addressOffset) { - _addressOffset = addressOffset; - adjust(); + { + ScopedMemoryTracker tracker(this); + _addressOffset = addressOffset; + adjust(); + } + setCursorPosition(_cursorPosition); viewport()->update(); } @@ -86,8 +96,11 @@ qint64 QHexEdit::addressOffset() void QHexEdit::setEditorSize(qint64 size) { - _editorSize = size; - adjust(); + { + ScopedMemoryTracker tracker(this); + _editorSize = size; + adjust(); + } setCursorPosition(_cursorPosition); viewport()->update(); } @@ -183,6 +196,12 @@ void QHexEdit::setHighlighting(bool highlighting) viewport()->update(); } +void QHexEdit::setMemoryTracking(bool mode) +{ + _trackMemoryChanges = mode; + refresh(); +} + bool QHexEdit::highlighting() { return _highlighting; @@ -738,6 +757,7 @@ void QHexEdit::paintEvent(QPaintEvent *event) for (int colIdx = 0; ((bPosLine + colIdx) < _dataShown.size() and (colIdx < BYTES_PER_LINE)); colIdx++) { QColor c = viewport()->palette().color(QPalette::Base); + QColor accent = viewport()->palette().color(QPalette::Link); painter.setPen(colStandard); qint64 posBa = _bPosFirst + bPosLine + colIdx; @@ -777,6 +797,11 @@ void QHexEdit::paintEvent(QPaintEvent *event) r.setRect(pxPosX - _pxCharWidth, pxPosY - _pxCharHeight + _pxSelectionSub, 3*_pxCharWidth, _pxCharHeight); painter.fillRect(r, c); hex = _hexDataShown.mid((bPosLine + colIdx) * 2, 2); + if (_trackMemoryChanges && _hexChangesMap.contains(_bPosFirst + colIdx + row * BYTES_PER_LINE + _addressOffset)) { + const qreal value = _hexChangesMap[_bPosFirst + colIdx + row * BYTES_PER_LINE + _addressOffset]; + accent.setAlphaF(value / qreal(FRAMES_TO_FADE_HEXBG)); + painter.fillRect(r, accent); + } painter.drawText(pxPosX, pxPosY, hex); pxPosX += 3*_pxCharWidth; @@ -918,16 +943,22 @@ void QHexEdit::refresh(bool showCursor) { if (showCursor) ensureVisible(); - readBuffers(); + + { + ScopedMemoryTracker tracker(this); + readBuffers(); + } + viewport()->update(); } void QHexEdit::readBuffers() { _dataShown.clear(); - for (qint64 i = _bPosFirst; i < qMin(_editorSize, _bPosLast + BYTES_PER_LINE + 1); i++) + for (qint64 i = _bPosFirst; i < qMin(_editorSize, _bPosLast + BYTES_PER_LINE + 1); i++) { _dataShown.append(reader ? reader(i) : 0); - + } + _hexDataShown = QByteArray(_dataShown.toHex()); } @@ -965,3 +996,57 @@ void QHexEdit::updateCursor() viewport()->update(_cursorRect); } +void QHexEdit::updateAnimatedHexValues() +{ + if (_trackMemoryChanges && !_hexChangesMap.keys().empty()) { + QHash::iterator it = _hexChangesMap.begin(); + while (it != _hexChangesMap.end()) { + it.value()--; + if (it.value() == 0) { + it = _hexChangesMap.erase(it); + } else { + ++it; + } + } + + viewport()->update(); + } +} + +QHexEdit::ScopedMemoryTracker::ScopedMemoryTracker(QHexEdit *editor) + : _editor(editor){ + Q_ASSERT(_editor); + const QByteArray currentData = _editor->_dataShown; + const qint64 first = _editor->_bPosFirst; + const qint64 last = _editor->_bPosLast; + const qint64 editorSize = _editor->_editorSize; + + for (qint64 i = first; i < qMin(editorSize, last + BYTES_PER_LINE + 1); i++) { + const qint64 addr = i + _editor->_addressOffset; + quint16 value = qFromLittleEndian(currentData.mid(i-first, 1).data()); + _dataHash.insert(addr, value); + } +} + +QHexEdit::ScopedMemoryTracker::~ScopedMemoryTracker() { + const QByteArray currentData = _editor->_dataShown; + const qint64 first = _editor->_bPosFirst; + const qint64 last = _editor->_bPosLast; + const qint64 editorSize = _editor->_editorSize; + + for (qint64 i = first; i < qMin(editorSize, last + BYTES_PER_LINE + 1); i++) { + const qint64 addr = i + _editor->_addressOffset; + // Track any changes to memory we saw when the object was created, and + // register those changes with the memory tracker... + const quint16 value = qFromLittleEndian(currentData.mid(i-first, 1).data()); + if (_dataHash.contains(addr) && _dataHash[addr] != value) { + if (_editor->_hexChangesMap.contains(addr)) { + _editor->_hexChangesMap[addr] = FRAMES_TO_FADE_HEXBG; + } else { + _editor->_hexChangesMap.insert(addr, FRAMES_TO_FADE_HEXBG); + } + } + } +} + + diff --git a/bsnes/ui-qt/debugger/tools/qhexedit2/qhexedit.moc.hpp b/bsnes/ui-qt/debugger/tools/qhexedit2/qhexedit.moc.hpp index b5ec1d99..be210231 100644 --- a/bsnes/ui-qt/debugger/tools/qhexedit2/qhexedit.moc.hpp +++ b/bsnes/ui-qt/debugger/tools/qhexedit2/qhexedit.moc.hpp @@ -93,6 +93,19 @@ class QHexEdit : public QAbstractScrollArea /*! Set the font of the widget. Please use fixed width fonts like Mono or Courier.*/ Q_PROPERTY(QFont font READ font WRITE setFont) +private: + /* Tracks values associated with certain memory addresses, + * and registers any changes in those values to _hexChangesMap */ + struct ScopedMemoryTracker { + ScopedMemoryTracker(QHexEdit* editor); + + ~ScopedMemoryTracker(); + + private: + QHexEdit* _editor; + QHash _dataHash; + }; + public: /*! Creates an instance of QHexEdit. \param parent Parent widget of QHexEdit. @@ -100,7 +113,6 @@ class QHexEdit : public QAbstractScrollArea QHexEdit(QWidget *parent=0); // Access to data of qhexedit - function reader; function writer; function usage; @@ -246,11 +258,16 @@ public slots: */ void setAsciiArea(bool asciiArea); - /*! Switch the highlighting feature on or of. + /*! Switch the highlighting feature on or off. \param mode true (show it), false (hide it). */ void setHighlighting(bool mode); + /*! Switch memory change tracking feature on or off. + \param state true (enabled), false (disabled). + */ + void setMemoryTracking(bool mode); + /*! Undoes the last operation. If there is no operation to undo, i.e. there is no undo step in the undo/redo history, nothing happens. */ @@ -321,6 +338,7 @@ private slots: void adjust(); // recalc pixel positions void dataChangedPrivate(int idx=0); // emit dataChanged() signal void updateCursor(); // update blinking cursor + void updateAnimatedHexValues(); // update animated hex value change effect. private: // Name convention: pixel positions start with _px @@ -368,6 +386,9 @@ private slots: qint64 _editorSize; QByteArray _dataShown; // data in the current View QByteArray _hexDataShown; // data in view, transformed to hex + bool _trackMemoryChanges; // Whether we should be rendering hex value changes. + QTimer _animatedHexChangeTimer; // timer used for animating hax value change background color. + QHash _hexChangesMap; // Map of changed indices to number of frames left for fade transition. qint64 _lastEventSize; // size, which was emitted last time QByteArray _markedShown; // marked data in view bool _modified; // Is any data in editor modified? From 191708165405620d655fbe54c05849ae2bcc223c Mon Sep 17 00:00:00 2001 From: Eoin O'Neill Date: Wed, 3 Nov 2021 02:08:04 -0700 Subject: [PATCH 3/3] Removed remnant of unused optimization attempt. Originally was going to parse two-char width in order to speed up iteration, but optimization was premature and wasn't any more performant. --- bsnes/ui-qt/debugger/tools/qhexedit2/qhexedit.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bsnes/ui-qt/debugger/tools/qhexedit2/qhexedit.cpp b/bsnes/ui-qt/debugger/tools/qhexedit2/qhexedit.cpp index a49b055d..df96a8ae 100644 --- a/bsnes/ui-qt/debugger/tools/qhexedit2/qhexedit.cpp +++ b/bsnes/ui-qt/debugger/tools/qhexedit2/qhexedit.cpp @@ -1023,7 +1023,7 @@ QHexEdit::ScopedMemoryTracker::ScopedMemoryTracker(QHexEdit *editor) for (qint64 i = first; i < qMin(editorSize, last + BYTES_PER_LINE + 1); i++) { const qint64 addr = i + _editor->_addressOffset; - quint16 value = qFromLittleEndian(currentData.mid(i-first, 1).data()); + char value = currentData.at(i-first); _dataHash.insert(addr, value); } } @@ -1038,7 +1038,7 @@ QHexEdit::ScopedMemoryTracker::~ScopedMemoryTracker() { const qint64 addr = i + _editor->_addressOffset; // Track any changes to memory we saw when the object was created, and // register those changes with the memory tracker... - const quint16 value = qFromLittleEndian(currentData.mid(i-first, 1).data()); + char value = currentData.at(i-first); if (_dataHash.contains(addr) && _dataHash[addr] != value) { if (_editor->_hexChangesMap.contains(addr)) { _editor->_hexChangesMap[addr] = FRAMES_TO_FADE_HEXBG;