void Terminal::sendLine()

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;
}