28x terminal redraw improvement.
This commit is contained in:
parent
291eaf0c35
commit
0a06d8f354
5 changed files with 215 additions and 67 deletions
15
dvx/dvxApp.c
15
dvx/dvxApp.c
|
|
@ -1338,9 +1338,20 @@ static void pollAnsiTermWidgetsWalk(AppContextT *ctx, WidgetT *w, WindowT *win)
|
||||||
if (w->type == WidgetAnsiTermE) {
|
if (w->type == WidgetAnsiTermE) {
|
||||||
wgtAnsiTermPoll(w);
|
wgtAnsiTermPoll(w);
|
||||||
|
|
||||||
if (wgtAnsiTermRepaint(w) > 0) {
|
int32_t dirtyY = 0;
|
||||||
|
int32_t dirtyH = 0;
|
||||||
|
|
||||||
|
if (wgtAnsiTermRepaint(w, &dirtyY, &dirtyH) > 0) {
|
||||||
win->contentDirty = true;
|
win->contentDirty = true;
|
||||||
dvxInvalidateWindow(ctx, win);
|
|
||||||
|
// Dirty only the affected rows (in screen coords) instead of
|
||||||
|
// the entire window. This dramatically reduces compositor and
|
||||||
|
// LFB flush work for cursor blink and single-row updates.
|
||||||
|
int32_t scrollY = win->vScroll ? win->vScroll->value : 0;
|
||||||
|
int32_t rectX = win->x + win->contentX;
|
||||||
|
int32_t rectY = win->y + win->contentY + dirtyY - scrollY;
|
||||||
|
int32_t rectW = win->contentW;
|
||||||
|
dirtyListAdd(&ctx->dirty, rectX, rectY, rectW, dirtyH);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
137
dvx/dvxDraw.c
137
dvx/dvxDraw.c
|
|
@ -432,6 +432,143 @@ void drawMaskedBitmap(DisplayT *d, const BlitOpsT *ops, int32_t x, int32_t y, in
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// drawTermRow
|
||||||
|
// ============================================================
|
||||||
|
//
|
||||||
|
// Renders an entire row of terminal character cells in one pass.
|
||||||
|
// lineData points to (ch, attr) pairs. palette is a 16-entry
|
||||||
|
// packed-color table. This avoids per-character function call
|
||||||
|
// overhead, redundant clip calculation, and spanFill startup
|
||||||
|
// costs that make drawChar expensive when called 80× per row.
|
||||||
|
|
||||||
|
void drawTermRow(DisplayT *d, const BlitOpsT *ops, const BitmapFontT *font, int32_t x, int32_t y, int32_t cols, const uint8_t *lineData, const uint32_t *palette, bool blinkVisible, int32_t cursorCol) {
|
||||||
|
int32_t cw = font->charWidth;
|
||||||
|
int32_t ch = font->charHeight;
|
||||||
|
int32_t bpp = ops->bytesPerPixel;
|
||||||
|
int32_t pitch = d->pitch;
|
||||||
|
|
||||||
|
// Row-level clip: reject if entirely outside vertically
|
||||||
|
int32_t clipX1 = d->clipX;
|
||||||
|
int32_t clipX2 = d->clipX + d->clipW;
|
||||||
|
int32_t clipY1 = d->clipY;
|
||||||
|
int32_t clipY2 = d->clipY + d->clipH;
|
||||||
|
|
||||||
|
if (y + ch <= clipY1 || y >= clipY2) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Vertical clip for glyph scanlines
|
||||||
|
int32_t rowStart = 0;
|
||||||
|
int32_t rowEnd = ch;
|
||||||
|
if (y < clipY1) { rowStart = clipY1 - y; }
|
||||||
|
if (y + ch > clipY2) { rowEnd = clipY2 - y; }
|
||||||
|
|
||||||
|
// Horizontal clip: find first and last visible column
|
||||||
|
int32_t rowW = cols * cw;
|
||||||
|
int32_t firstCol = 0;
|
||||||
|
int32_t lastCol = cols;
|
||||||
|
|
||||||
|
if (x + rowW <= clipX1 || x >= clipX2) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (x < clipX1) {
|
||||||
|
firstCol = (clipX1 - x) / cw;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (x + rowW > clipX2) {
|
||||||
|
lastCol = (clipX2 - x + cw - 1) / cw;
|
||||||
|
if (lastCol > cols) { lastCol = cols; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Per-column clip for partially visible edge cells
|
||||||
|
int32_t edgeColStart = 0;
|
||||||
|
|
||||||
|
if (x + firstCol * cw < clipX1) {
|
||||||
|
edgeColStart = clipX1 - (x + firstCol * cw);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render each visible cell
|
||||||
|
for (int32_t col = firstCol; col < lastCol; col++) {
|
||||||
|
uint8_t gch = lineData[col * 2];
|
||||||
|
uint8_t attr = lineData[col * 2 + 1];
|
||||||
|
uint32_t fg = palette[attr & 0x0F];
|
||||||
|
uint32_t bg = palette[(attr >> 4) & 0x07];
|
||||||
|
|
||||||
|
// Blink: hide text during off phase
|
||||||
|
if ((attr & 0x80) && !blinkVisible) {
|
||||||
|
fg = bg;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cursor: invert colors
|
||||||
|
if (col == cursorCol) {
|
||||||
|
uint32_t tmp = fg;
|
||||||
|
fg = bg;
|
||||||
|
bg = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t cx = x + col * cw;
|
||||||
|
|
||||||
|
// Determine per-cell horizontal clip
|
||||||
|
int32_t cStart = 0;
|
||||||
|
int32_t cEnd = cw;
|
||||||
|
|
||||||
|
if (col == firstCol) {
|
||||||
|
cStart = edgeColStart;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cx + cw > clipX2) {
|
||||||
|
cEnd = clipX2 - cx;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Look up glyph data
|
||||||
|
int32_t idx = (uint8_t)gch - font->firstChar;
|
||||||
|
const uint8_t *glyph = NULL;
|
||||||
|
|
||||||
|
if (idx >= 0 && idx < font->numChars) {
|
||||||
|
glyph = font->glyphData + idx * ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render scanlines
|
||||||
|
if (bpp == 2) {
|
||||||
|
uint16_t fg16 = (uint16_t)fg;
|
||||||
|
uint16_t bg16 = (uint16_t)bg;
|
||||||
|
|
||||||
|
for (int32_t row = rowStart; row < rowEnd; row++) {
|
||||||
|
uint16_t *dst = (uint16_t *)(d->backBuf + (y + row) * pitch + cx * 2);
|
||||||
|
uint8_t bits = glyph ? glyph[row] : 0;
|
||||||
|
|
||||||
|
for (int32_t p = cStart; p < cEnd; p++) {
|
||||||
|
dst[p] = (bits & (0x80 >> p)) ? fg16 : bg16;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (bpp == 4) {
|
||||||
|
for (int32_t row = rowStart; row < rowEnd; row++) {
|
||||||
|
uint32_t *dst = (uint32_t *)(d->backBuf + (y + row) * pitch + cx * 4);
|
||||||
|
uint8_t bits = glyph ? glyph[row] : 0;
|
||||||
|
|
||||||
|
for (int32_t p = cStart; p < cEnd; p++) {
|
||||||
|
dst[p] = (bits & (0x80 >> p)) ? fg : bg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
uint8_t fg8 = (uint8_t)fg;
|
||||||
|
uint8_t bg8 = (uint8_t)bg;
|
||||||
|
|
||||||
|
for (int32_t row = rowStart; row < rowEnd; row++) {
|
||||||
|
uint8_t *dst = d->backBuf + (y + row) * pitch + cx;
|
||||||
|
uint8_t bits = glyph ? glyph[row] : 0;
|
||||||
|
|
||||||
|
for (int32_t p = cStart; p < cEnd; p++) {
|
||||||
|
dst[p] = (bits & (0x80 >> p)) ? fg8 : bg8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// drawText
|
// drawText
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,11 @@ int32_t textWidthAccel(const BitmapFontT *font, const char *text);
|
||||||
// andMask/xorData are arrays of uint16_t, one per row
|
// andMask/xorData are arrays of uint16_t, one per row
|
||||||
void drawMaskedBitmap(DisplayT *d, const BlitOpsT *ops, int32_t x, int32_t y, int32_t w, int32_t h, const uint16_t *andMask, const uint16_t *xorData, uint32_t fgColor, uint32_t bgColor);
|
void drawMaskedBitmap(DisplayT *d, const BlitOpsT *ops, int32_t x, int32_t y, int32_t w, int32_t h, const uint16_t *andMask, const uint16_t *xorData, uint32_t fgColor, uint32_t bgColor);
|
||||||
|
|
||||||
|
// Draw a row of terminal character cells (ch/attr pairs with a 16-color palette).
|
||||||
|
// Renders 'cols' cells starting at (x,y). Much faster than calling drawChar per cell.
|
||||||
|
// cursorCol: column index to draw inverted (cursor), or -1 for no cursor.
|
||||||
|
void drawTermRow(DisplayT *d, const BlitOpsT *ops, const BitmapFontT *font, int32_t x, int32_t y, int32_t cols, const uint8_t *lineData, const uint32_t *palette, bool blinkVisible, int32_t cursorCol);
|
||||||
|
|
||||||
// Dotted focus rectangle (every other pixel)
|
// Dotted focus rectangle (every other pixel)
|
||||||
void drawFocusRect(DisplayT *d, const BlitOpsT *ops, int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color);
|
void drawFocusRect(DisplayT *d, const BlitOpsT *ops, int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -327,6 +327,9 @@ typedef struct WidgetT {
|
||||||
uint32_t dirtyRows; // bitmask of rows needing repaint
|
uint32_t dirtyRows; // bitmask of rows needing repaint
|
||||||
int32_t lastCursorRow; // cursor row at last repaint
|
int32_t lastCursorRow; // cursor row at last repaint
|
||||||
int32_t lastCursorCol; // cursor col at last repaint
|
int32_t lastCursorCol; // cursor col at last repaint
|
||||||
|
// Cached packed palette (avoids packColor per repaint)
|
||||||
|
uint32_t packedPalette[16];
|
||||||
|
bool paletteValid;
|
||||||
// Communications interface (all NULL = disconnected)
|
// Communications interface (all NULL = disconnected)
|
||||||
void *commCtx;
|
void *commCtx;
|
||||||
int32_t (*commRead)(void *ctx, uint8_t *buf, int32_t maxLen);
|
int32_t (*commRead)(void *ctx, uint8_t *buf, int32_t maxLen);
|
||||||
|
|
@ -519,8 +522,9 @@ int32_t wgtAnsiTermPoll(WidgetT *w);
|
||||||
|
|
||||||
// Fast repaint: renders only dirty rows directly into the window's content
|
// Fast repaint: renders only dirty rows directly into the window's content
|
||||||
// buffer, bypassing the full widget paint pipeline. Returns number of rows
|
// buffer, bypassing the full widget paint pipeline. Returns number of rows
|
||||||
// repainted (0 if nothing was dirty).
|
// repainted (0 if nothing was dirty). If outY/outH are non-NULL, they receive
|
||||||
int32_t wgtAnsiTermRepaint(WidgetT *w);
|
// the content-buffer-relative Y and height of the repainted region.
|
||||||
|
int32_t wgtAnsiTermRepaint(WidgetT *w, int32_t *outY, int32_t *outH);
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// Operations
|
// Operations
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,7 @@ static const int32_t sAnsiToCga[8] = { 0, 4, 2, 6, 1, 5, 3, 7 };
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
||||||
static void ansiTermAddToScrollback(WidgetT *w, int32_t screenRow);
|
static void ansiTermAddToScrollback(WidgetT *w, int32_t screenRow);
|
||||||
|
static void ansiTermBuildPalette(WidgetT *w, const DisplayT *d);
|
||||||
static void ansiTermDeleteLines(WidgetT *w, int32_t count);
|
static void ansiTermDeleteLines(WidgetT *w, int32_t count);
|
||||||
static void ansiTermDirtyRange(WidgetT *w, int32_t startCell, int32_t count);
|
static void ansiTermDirtyRange(WidgetT *w, int32_t startCell, int32_t count);
|
||||||
static void ansiTermDirtyRow(WidgetT *w, int32_t row);
|
static void ansiTermDirtyRow(WidgetT *w, int32_t row);
|
||||||
|
|
@ -104,6 +105,27 @@ static void ansiTermAddToScrollback(WidgetT *w, int32_t screenRow) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// ansiTermBuildPalette
|
||||||
|
// ============================================================
|
||||||
|
//
|
||||||
|
// Build the packed 16-color palette and cache it in the widget.
|
||||||
|
// Only recomputed when paletteValid is false (first use or
|
||||||
|
// after a display format change).
|
||||||
|
|
||||||
|
static void ansiTermBuildPalette(WidgetT *w, const DisplayT *d) {
|
||||||
|
if (w->as.ansiTerm.paletteValid) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int32_t i = 0; i < 16; i++) {
|
||||||
|
w->as.ansiTerm.packedPalette[i] = packColor(d, sCgaPalette[i][0], sCgaPalette[i][1], sCgaPalette[i][2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
w->as.ansiTerm.paletteValid = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// ansiTermDirtyRange
|
// ansiTermDirtyRange
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
@ -1164,7 +1186,7 @@ int32_t wgtAnsiTermPoll(WidgetT *w) {
|
||||||
// no relayout, no other widgets). This keeps ACK turnaround fast
|
// no relayout, no other widgets). This keeps ACK turnaround fast
|
||||||
// for the serial link.
|
// for the serial link.
|
||||||
|
|
||||||
int32_t wgtAnsiTermRepaint(WidgetT *w) {
|
int32_t wgtAnsiTermRepaint(WidgetT *w, int32_t *outY, int32_t *outH) {
|
||||||
if (!w || w->type != WidgetAnsiTermE || !w->window) {
|
if (!w || w->type != WidgetAnsiTermE || !w->window) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
@ -1216,19 +1238,18 @@ int32_t wgtAnsiTermRepaint(WidgetT *w) {
|
||||||
|
|
||||||
int32_t cols = w->as.ansiTerm.cols;
|
int32_t cols = w->as.ansiTerm.cols;
|
||||||
int32_t rows = w->as.ansiTerm.rows;
|
int32_t rows = w->as.ansiTerm.rows;
|
||||||
int32_t cellW = font->charWidth;
|
|
||||||
int32_t cellH = font->charHeight;
|
int32_t cellH = font->charHeight;
|
||||||
int32_t baseX = w->x + ANSI_BORDER;
|
int32_t baseX = w->x + ANSI_BORDER;
|
||||||
int32_t baseY = w->y + ANSI_BORDER;
|
int32_t baseY = w->y + ANSI_BORDER;
|
||||||
|
|
||||||
// Build palette
|
// Use cached palette
|
||||||
uint32_t palette[16];
|
ansiTermBuildPalette(w, &cd);
|
||||||
for (int32_t i = 0; i < 16; i++) {
|
const uint32_t *palette = w->as.ansiTerm.packedPalette;
|
||||||
palette[i] = packColor(&cd, sCgaPalette[i][0], sCgaPalette[i][1], sCgaPalette[i][2]);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool viewingLive = (w->as.ansiTerm.scrollPos == w->as.ansiTerm.scrollbackCount);
|
bool viewingLive = (w->as.ansiTerm.scrollPos == w->as.ansiTerm.scrollbackCount);
|
||||||
int32_t repainted = 0;
|
int32_t repainted = 0;
|
||||||
|
int32_t minRow = rows;
|
||||||
|
int32_t maxRow = -1;
|
||||||
|
|
||||||
for (int32_t row = 0; row < rows; row++) {
|
for (int32_t row = 0; row < rows; row++) {
|
||||||
if (!(dirty & (1U << row))) {
|
if (!(dirty & (1U << row))) {
|
||||||
|
|
@ -1238,37 +1259,27 @@ int32_t wgtAnsiTermRepaint(WidgetT *w) {
|
||||||
int32_t lineIndex = w->as.ansiTerm.scrollPos + row;
|
int32_t lineIndex = w->as.ansiTerm.scrollPos + row;
|
||||||
const uint8_t *lineData = ansiTermGetLine(w, lineIndex);
|
const uint8_t *lineData = ansiTermGetLine(w, lineIndex);
|
||||||
|
|
||||||
for (int32_t col = 0; col < cols; col++) {
|
// Cursor column for this row (-1 if cursor not on this row)
|
||||||
uint8_t ch = lineData[col * 2];
|
int32_t curCol2 = -1;
|
||||||
uint8_t attr = lineData[col * 2 + 1];
|
if (viewingLive && w->as.ansiTerm.cursorVisible && w->as.ansiTerm.cursorOn &&
|
||||||
|
row == w->as.ansiTerm.cursorRow) {
|
||||||
uint32_t fg = palette[attr & ATTR_FG_MASK];
|
curCol2 = w->as.ansiTerm.cursorCol;
|
||||||
uint32_t bg = palette[(attr >> 4) & 0x07];
|
|
||||||
|
|
||||||
// Blink: hide text during off phase
|
|
||||||
if ((attr & ATTR_BLINK_BIT) && !w->as.ansiTerm.blinkVisible) {
|
|
||||||
fg = bg;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (viewingLive && w->as.ansiTerm.cursorVisible && w->as.ansiTerm.cursorOn &&
|
|
||||||
row == w->as.ansiTerm.cursorRow && col == w->as.ansiTerm.cursorCol) {
|
|
||||||
uint32_t tmp = fg;
|
|
||||||
fg = bg;
|
|
||||||
bg = tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
int32_t cx = baseX + col * cellW;
|
|
||||||
int32_t cy = baseY + row * cellH;
|
|
||||||
|
|
||||||
drawChar(&cd, ops, font, cx, cy, (char)ch, fg, bg, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
drawTermRow(&cd, ops, font, baseX, baseY + row * cellH, cols, lineData, palette, w->as.ansiTerm.blinkVisible, curCol2);
|
||||||
|
|
||||||
|
if (row < minRow) { minRow = row; }
|
||||||
|
if (row > maxRow) { maxRow = row; }
|
||||||
repainted++;
|
repainted++;
|
||||||
}
|
}
|
||||||
|
|
||||||
w->as.ansiTerm.dirtyRows = 0;
|
w->as.ansiTerm.dirtyRows = 0;
|
||||||
w->as.ansiTerm.lastCursorRow = w->as.ansiTerm.cursorRow;
|
w->as.ansiTerm.lastCursorRow = w->as.ansiTerm.cursorRow;
|
||||||
w->as.ansiTerm.lastCursorCol = w->as.ansiTerm.cursorCol;
|
w->as.ansiTerm.lastCursorCol = w->as.ansiTerm.cursorCol;
|
||||||
|
|
||||||
|
if (outY) { *outY = baseY + minRow * cellH; }
|
||||||
|
if (outH) { *outH = (maxRow - minRow + 1) * cellH; }
|
||||||
|
|
||||||
return repainted;
|
return repainted;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1497,12 +1508,9 @@ void widgetAnsiTermPaint(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const Bit
|
||||||
bevel.width = ANSI_BORDER;
|
bevel.width = ANSI_BORDER;
|
||||||
drawBevel(d, ops, w->x, w->y, w->w, w->h, &bevel);
|
drawBevel(d, ops, w->x, w->y, w->w, w->h, &bevel);
|
||||||
|
|
||||||
// Build the 16-color packed palette for this display format
|
// Build/cache the 16-color packed palette
|
||||||
uint32_t palette[16];
|
ansiTermBuildPalette(w, d);
|
||||||
|
const uint32_t *palette = w->as.ansiTerm.packedPalette;
|
||||||
for (int32_t i = 0; i < 16; i++) {
|
|
||||||
palette[i] = packColor(d, sCgaPalette[i][0], sCgaPalette[i][1], sCgaPalette[i][2]);
|
|
||||||
}
|
|
||||||
|
|
||||||
int32_t cols = w->as.ansiTerm.cols;
|
int32_t cols = w->as.ansiTerm.cols;
|
||||||
int32_t rows = w->as.ansiTerm.rows;
|
int32_t rows = w->as.ansiTerm.rows;
|
||||||
|
|
@ -1515,36 +1523,19 @@ void widgetAnsiTermPaint(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const Bit
|
||||||
// Determine if viewing live terminal or scrollback
|
// Determine if viewing live terminal or scrollback
|
||||||
bool viewingLive = (w->as.ansiTerm.scrollPos == sbCount);
|
bool viewingLive = (w->as.ansiTerm.scrollPos == sbCount);
|
||||||
|
|
||||||
// Render character cells
|
// Render character cells row by row using bulk renderer
|
||||||
for (int32_t row = 0; row < rows; row++) {
|
for (int32_t row = 0; row < rows; row++) {
|
||||||
int32_t lineIndex = w->as.ansiTerm.scrollPos + row;
|
int32_t lineIndex = w->as.ansiTerm.scrollPos + row;
|
||||||
const uint8_t *lineData = ansiTermGetLine(w, lineIndex);
|
const uint8_t *lineData = ansiTermGetLine(w, lineIndex);
|
||||||
|
|
||||||
for (int32_t col = 0; col < cols; col++) {
|
// Cursor column for this row (-1 if cursor not on this row)
|
||||||
uint8_t ch = lineData[col * 2];
|
int32_t curCol = -1;
|
||||||
uint8_t attr = lineData[col * 2 + 1];
|
if (viewingLive && w->as.ansiTerm.cursorVisible && w->as.ansiTerm.cursorOn &&
|
||||||
|
row == w->as.ansiTerm.cursorRow) {
|
||||||
uint32_t fg = palette[attr & ATTR_FG_MASK];
|
curCol = w->as.ansiTerm.cursorCol;
|
||||||
uint32_t bg = palette[(attr >> 4) & 0x07];
|
|
||||||
|
|
||||||
// Blink: hide text during off phase
|
|
||||||
if ((attr & ATTR_BLINK_BIT) && !w->as.ansiTerm.blinkVisible) {
|
|
||||||
fg = bg;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Draw cursor as inverse block (only when viewing live terminal)
|
|
||||||
if (viewingLive && w->as.ansiTerm.cursorVisible && w->as.ansiTerm.cursorOn &&
|
|
||||||
row == w->as.ansiTerm.cursorRow && col == w->as.ansiTerm.cursorCol) {
|
|
||||||
uint32_t tmp = fg;
|
|
||||||
fg = bg;
|
|
||||||
bg = tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
int32_t cx = baseX + col * cellW;
|
|
||||||
int32_t cy = baseY + row * cellH;
|
|
||||||
|
|
||||||
drawChar(d, ops, font, cx, cy, (char)ch, fg, bg, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
drawTermRow(d, ops, font, baseX, baseY + row * cellH, cols, lineData, palette, w->as.ansiTerm.blinkVisible, curCol);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw scrollbar
|
// Draw scrollbar
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue