From c5f9934cd81fef3210c22f2f6f5fe860ee5198b6 Mon Sep 17 00:00:00 2001 From: Adrien Destugues Date: Mon, 20 Nov 2017 21:39:29 +0100 Subject: [PATCH] SerialConnect: optimize drawing The libvterm implementation was sending characters to app_server one by one. This made it very slow to update the view and SerialConnect would have noticeable flickering. We now look through each line and try to find ranges of characters with the same attributes. We can then draw each of these with a single FillRect+DrawString. In the normal case (no special attributes or colors anywhere), this means just one call per line, plus two for the cursor. --- src/apps/serialconnect/TermView.cpp | 151 +++++++++++++++++----------- 1 file changed, 94 insertions(+), 57 deletions(-) diff --git a/src/apps/serialconnect/TermView.cpp b/src/apps/serialconnect/TermView.cpp index ce9cca3f87..a57f8fcb8d 100644 --- a/src/apps/serialconnect/TermView.cpp +++ b/src/apps/serialconnect/TermView.cpp @@ -15,6 +15,7 @@ #include #include "SerialApp.h" +#include "libvterm/src/vterm_internal.h" struct ScrollBufferItem { @@ -72,68 +73,101 @@ TermView::Draw(BRect updateRect) + kBorderSpacing; MovePenTo(x, y); + BString string; + VTermScreenCell cell; + int width = 0; + bool isCursor = false; + + pos.col = updatedChars.start_col; + _GetCell(pos, cell); + for (pos.col = updatedChars.start_col; pos.col <= updatedChars.end_col;) { - VTermScreenCell cell; - _GetCell(pos, cell); + VTermScreenCell newCell; + _GetCell(pos, newCell); - rgb_color foreground, background; - foreground.red = cell.fg.red; - foreground.green = cell.fg.green; - foreground.blue = cell.fg.blue; - foreground.alpha = 255; - background.red = cell.bg.red; - background.green = cell.bg.green; - background.blue = cell.bg.blue; - background.alpha = 255; + // We need to start a new extent if: + // - The attributes change + // - The colors change + // - The end of line is reached + // - The current cell is under the cursor + // - The current cell is right of the cursor + if (*(uint32_t*)&cell.attrs != *(uint32_t*)&newCell.attrs + || !vterm_color_equal(cell.fg, newCell.fg) + || !vterm_color_equal(cell.bg, newCell.bg) + || pos.col >= updatedChars.end_col + || (pos.col == cursorPos.col && pos.row == cursorPos.row) + || (pos.col == cursorPos.col + 1 && pos.row == cursorPos.row)) { + + rgb_color foreground, background; + foreground.red = cell.fg.red; + foreground.green = cell.fg.green; + foreground.blue = cell.fg.blue; + foreground.alpha = 255; + background.red = cell.bg.red; + background.green = cell.bg.green; + background.blue = cell.bg.blue; + background.alpha = 255; - // Draw the cursor by swapping foreground and background colors - if ((pos.col == cursorPos.col && pos.row == cursorPos.row)) { - SetLowColor(foreground); - SetViewColor(foreground); - SetHighColor(background); - } else { - SetLowColor(background); - SetViewColor(background); - SetHighColor(foreground); + // Draw the cursor by swapping foreground and background colors + if (isCursor) { + SetLowColor(foreground); + SetViewColor(foreground); + SetHighColor(background); + } else { + SetLowColor(background); + SetViewColor(background); + SetHighColor(foreground); + } + + FillRect(BRect(x, y - ceil(height.ascent) + 1, + x + width * fFontWidth - 1, + y + ceil(height.descent) + ceil(height.leading)), + B_SOLID_LOW); + + BFont font = be_fixed_font; + if (cell.attrs.bold) + font.SetFace(B_BOLD_FACE); + if (cell.attrs.underline) + font.SetFace(B_UNDERSCORE_FACE); + if (cell.attrs.italic) + font.SetFace(B_ITALIC_FACE); + if (cell.attrs.blink) // FIXME make it actually blink + font.SetFace(B_OUTLINED_FACE); + if (cell.attrs.reverse) + font.SetFace(B_NEGATIVE_FACE); + if (cell.attrs.strike) + font.SetFace(B_STRIKEOUT_FACE); + + // TODO handle "font" (alternate fonts), dwl and dhl (double size) + + SetFont(&font); + DrawString(string); + x += width * fFontWidth; + + // Prepare for next cell + cell = newCell; + string = ""; + width = 0; } - FillRect(BRect(x, y - ceil(height.ascent) + 1, - x + cell.width * fFontWidth - 1, - y + ceil(height.descent) + ceil(height.leading)), - B_SOLID_LOW); - - BFont font = be_fixed_font; - if (cell.attrs.bold) - font.SetFace(B_BOLD_FACE); - if (cell.attrs.underline) - font.SetFace(B_UNDERSCORE_FACE); - if (cell.attrs.italic) - font.SetFace(B_ITALIC_FACE); - if (cell.attrs.blink) // FIXME make it actually blink - font.SetFace(B_OUTLINED_FACE); - if (cell.attrs.reverse) - font.SetFace(B_NEGATIVE_FACE); - if (cell.attrs.strike) - font.SetFace(B_STRIKEOUT_FACE); - - // TODO handle "font" (alternate fonts), dwl and dhl (double size) - - SetFont(&font); - - if (cell.chars[0] == 0) { - DrawString(" "); + if (pos.col == cursorPos.col && pos.row == cursorPos.row) + isCursor = true; + else + isCursor = false; + + if (newCell.chars[0] == 0) { + string += " "; pos.col ++; - x += fFontWidth; + width += 1; } else { char buffer[VTERM_MAX_CHARS_PER_CELL]; - wcstombs(buffer, (wchar_t*)cell.chars, - VTERM_MAX_CHARS_PER_CELL); - - DrawString(buffer); - x += (int)ceil(StringWidth(buffer)); - pos.col += cell.width; + wcstombs(buffer, (wchar_t*)newCell.chars, + VTERM_MAX_CHARS_PER_CELL); + string += buffer; + width += newCell.width; + pos.col += newCell.width; } } } @@ -374,17 +408,20 @@ void TermView::_MoveCursor(VTermPos pos, VTermPos oldPos, int visible) { VTermRect r; - r.start_row = pos.row; - r.start_col = pos.col; - r.end_col = pos.col + 1; - r.end_row = pos.row + 1; - Invalidate(_GlyphsToPixels(r)); + // We need to erase the cursor from its old position r.start_row = oldPos.row; r.start_col = oldPos.col; r.end_col = oldPos.col + 1; r.end_row = oldPos.row + 1; Invalidate(_GlyphsToPixels(r)); + + // And we need to draw it at the new one + r.start_row = pos.row; + r.start_col = pos.col; + r.end_col = pos.col + 1; + r.end_row = pos.row + 1; + Invalidate(_GlyphsToPixels(r)); }