fix(qmltermwidget): scan only written cells and the history before pruning combined characters

main
reionwong 2 weeks ago
parent 10bab0e67c
commit c0e28079b0

@ -226,6 +226,7 @@ private:
// Drops every sequence no screen shows any more, freeing its buffer.
void pruneUnused();
static constexpr int kPruneThreshold = 4096;
int pruneAt = kPruneThreshold;
// calculates the hash key of a sequence of unicode points of size 'length'
uint extendedCharHash(uint* unicodePoints , ushort length) const;
// tests whether the entry in the table specified by 'hash' matches the

@ -449,7 +449,7 @@ void ExtendedCharTable::pruneUnused()
{
if (w->screen())
{
usedExtendedChars += w->screen()->usedExtendedChars();
w->screen()->collectUsedExtendedChars(usedExtendedChars);
}
}
@ -467,6 +467,10 @@ void ExtendedCharTable::pruneUnused()
it = extendedCharTable.erase(it);
}
}
// Still-used sequences stay; pruning again only once the table has doubled
// keeps a screen full of them from being rescanned on every new one.
pruneAt = qMax(kPruneThreshold, int(extendedCharTable.size()) * 2);
}
uint ExtendedCharTable::createExtendedChar(uint* unicodePoints , ushort length)
@ -482,7 +486,7 @@ uint ExtendedCharTable::createExtendedChar(uint* unicodePoints , ushort length)
// The table only ever grew; a long session with many combining
// sequences keeps every one until the screens are asked which are
// still in use.
if (extendedCharTable.size() >= kPruneThreshold)
if (extendedCharTable.size() >= pruneAt)
pruneUnused();
// check existing entry for match

@ -1139,6 +1139,35 @@ void Screen::eraseAboveCursor()
cuY = 0;
}
void Screen::collectUsedExtendedChars(QSet<uint> &used) const
{
// Lines grow only as far as the cursor has written, so most are shorter than columns.
for (int y = 0; y < lines; ++y)
{
for (const Character &ch : screenLines[y])
{
if (ch.rendition & RE_EXTENDED_CHAR)
used << ch.character;
}
}
// Scrollback still draws its sequences; freeing them would show another one in their place.
QVector<Character> cells;
for (int line = 0; line < history->getLines(); ++line)
{
const int length = history->getLineLen(line);
if (length <= 0)
continue;
cells.resize(length);
history->getCells(line, 0, length, cells.data());
for (const Character &ch : std::as_const(cells))
{
if (ch.rendition & RE_EXTENDED_CHAR)
used << ch.character;
}
}
}
int Screen::lastNonBlankLine() const
{
const Character blank;

@ -575,22 +575,8 @@ public:
*/
static void fillWithDefaultChar(Character* dest, int count);
QSet<uint> usedExtendedChars() const
{
QSet<uint> result;
for (int i = 0; i < lines; ++i)
{
const ImageLine &il = screenLines[i];
for (int j = 0; j < columns; ++j)
{
if (il[j].rendition & RE_EXTENDED_CHAR)
{
result << il[j].character;
}
}
}
return result;
}
/** Adds the extended characters on the screen and in its history to @p used. */
void collectUsedExtendedChars(QSet<uint> &used) const;
private:
Screen(const Screen &) = delete;

@ -53,6 +53,15 @@ public:
return line.at(column);
}
// A cell of the oldest line in the history.
Character historyCell(int column)
{
const Screen *screen = m_window->screen();
QVector<Character> line(screen->getColumns());
screen->getImage(line.data(), line.size(), 0, 0);
return line.at(column);
}
QString line(int length)
{
QString text;
@ -281,6 +290,36 @@ private slots:
QCOMPARE(terminal.cell(2).rendition, terminal.cell(1).rendition);
QVERIFY(terminal.cell(3).foregroundColor == CharacterColor(COLOR_SPACE_RGB, 0xff0000));
}
void combiningSequencesInHistorySurviveAPrune()
{
Terminal terminal;
terminal.keepHistory();
terminal.print(QStringLiteral("é\r\n").toUtf8());
for (int i = 0; i < 60; ++i)
terminal.print("\r\n");
QVERIFY(terminal.historyLines() > 0);
// Thousands of distinct sequences written over one short line, each
// gone from the screen by the next, push the table past its pruning size.
for (int base = 0x100; base < 0x180; ++base) {
QString text = QStringLiteral("\r");
for (int mark = 0x300; mark < 0x340; ++mark) {
text += QChar(base);
text += QChar(mark);
}
terminal.print(text.toUtf8());
}
const Character kept = terminal.historyCell(0);
QVERIFY(kept.rendition & RE_EXTENDED_CHAR);
ushort length = 0;
const uint *chars = ExtendedCharTable::instance.lookupExtendedChar(kept.character, length);
QVERIFY(chars);
QCOMPARE(length, ushort(2));
QCOMPARE(chars[0], uint('e'));
QCOMPARE(chars[1], uint(0x301));
}
};
QTEST_MAIN(Vt102EmulationTest)

Loading…
Cancel
Save