in src/agent/Terminal.cc [318:432]
void Terminal::sendLine(int64_t line, const CHAR_INFO *lineData, int width,
int cursorColumn)
{
ASSERT(width >= 1);
moveTerminalToLine(line);
// If possible, see if we can append to what we've already output for this
// line.
if (m_lineDataValid) {
ASSERT(m_lineData.size() == static_cast<size_t>(m_remoteColumn));
if (m_remoteColumn > 0) {
// In normal mode, if m_lineData.size() equals `width`, then we
// will have trouble outputing the "erase rest of line" command,
// which must be output before reaching the end of the line. In
// plain mode, we don't output that command, so we're OK with a
// full line.
bool okWidth = false;
if (m_plainMode) {
okWidth = static_cast<size_t>(width) >= m_lineData.size();
} else {
okWidth = static_cast<size_t>(width) > m_lineData.size();
}
if (!okWidth ||
memcmp(m_lineData.data(), lineData,
sizeof(CHAR_INFO) * m_lineData.size()) != 0) {
m_lineDataValid = false;
}
}
}
if (!m_lineDataValid) {
// We can't reuse, so we must reset this line.
hideTerminalCursor();
if (m_plainMode) {
// We can't backtrack, so repeat this line.
m_output.write("\r\n");
} else {
m_output.write("\r");
}
m_lineDataValid = true;
m_lineData.clear();
m_remoteColumn = 0;
}
std::string &termLine = m_termLineWorkingBuffer;
termLine.clear();
size_t trimmedLineLength = 0;
int trimmedCellCount = m_lineData.size();
bool alreadyErasedLine = false;
int cellCount = 1;
for (int i = m_lineData.size(); i < width; i += cellCount) {
if (m_outputColor) {
int color = lineData[i].Attributes & COLOR_ATTRIBUTE_MASK;
if (color != m_remoteColor) {
outputSetColor(termLine, color);
trimmedLineLength = termLine.size();
m_remoteColor = color;
// All the cells just up to this color change will be output.
trimmedCellCount = i;
}
}
unsigned int ch;
scanUnicodeScalarValue(&lineData[i], width - i, cellCount, ch);
if (ch == ' ') {
// Tentatively add this space character. We'll only output it if
// we see something interesting after it.
termLine.push_back(' ');
} else {
if (i + cellCount == width) {
// We'd like to erase the line after outputting all non-blank
// characters, but this doesn't work if the last cell in the
// line is non-blank. At the point, the cursor is positioned
// just past the end of the line, but in many terminals,
// issuing a CSI 0K at that point also erases the last cell in
// the line. Work around this behavior by issuing the erase
// one character early in that case.
if (!m_plainMode) {
termLine.append(CSI "0K"); // Erase from cursor to EOL
}
alreadyErasedLine = true;
}
ch = fixSpecialCharacters(ch);
char enc[4];
int enclen = encodeUtf8(enc, ch);
if (enclen == 0) {
enc[0] = '?';
enclen = 1;
}
termLine.append(enc, enclen);
trimmedLineLength = termLine.size();
// All the cells up to and including this cell will be output.
trimmedCellCount = i + cellCount;
}
}
if (cursorColumn != -1 && trimmedCellCount > cursorColumn) {
// The line content would run past the cursor, so hide it before we
// output.
hideTerminalCursor();
}
m_output.write(termLine.data(), trimmedLineLength);
if (!alreadyErasedLine && !m_plainMode) {
m_output.write(CSI "0K"); // Erase from cursor to EOL
}
ASSERT(trimmedCellCount <= width);
m_lineData.insert(m_lineData.end(),
&lineData[m_lineData.size()],
&lineData[trimmedCellCount]);
m_remoteColumn = trimmedCellCount;
}