diff --git a/html_orig/jssrc/term_screen.js b/html_orig/jssrc/term_screen.js
index cfc20a8..9a4ae50 100644
--- a/html_orig/jssrc/term_screen.js
+++ b/html_orig/jssrc/term_screen.js
@@ -126,6 +126,12 @@ class TermScreen {
this.screenBG = [];
this.screenAttrs = [];
+ // used to determine if a cell should be redrawn
+ this.drawnScreen = [];
+ this.drawnScreenFG = [];
+ this.drawnScreenBG = [];
+ this.drawnScreenAttrs = [];
+
this.resetBlink();
this.resetCursorBlink();
@@ -417,6 +423,12 @@ class TermScreen {
this.canvas.height = height * devicePixelRatio * cellSize.height;
this.canvas.style.height = `${realHeight}px`;
+ // the screen has been cleared (by changing canvas width)
+ this.drawnScreen = [];
+ this.drawnScreenFG = [];
+ this.drawnScreenBG = [];
+ this.drawnScreenAttrs = [];
+
// draw immediately; the canvas shouldn't flash
this.draw();
}
@@ -427,8 +439,8 @@ class TermScreen {
clearInterval(this.cursor.blinkInterval);
this.cursor.blinkInterval = setInterval(() => {
this.cursor.blinkOn = !this.cursor.blinkOn;
- this.scheduleDraw()
- }, 500)
+ this.scheduleDraw();
+ }, 500);
}
resetBlink () {
@@ -439,12 +451,12 @@ class TermScreen {
intervals++;
if (intervals >= 4 && this.window.blinkStyleOn) {
this.window.blinkStyleOn = false;
- intervals = 0
+ intervals = 0;
} else if (intervals >= 1 && !this.window.blinkStyleOn) {
this.window.blinkStyleOn = true;
- intervals = 0
+ intervals = 0;
}
- }, 200)
+ }, 200);
}
getNormalizedSelection () {
@@ -452,9 +464,9 @@ class TermScreen {
// if the start line is after the end line, or if they're both on the same
// line but the start column comes after the end column, swap
if (start[1] > end[1] || (start[1] === end[1] && start[0] > end[0])) {
- [start, end] = [end, start]
+ [start, end] = [end, start];
}
- return { start, end }
+ return { start, end };
}
isInSelection (col, line) {
@@ -467,7 +479,7 @@ class TermScreen {
if (onStartLine && onEndLine) return colAfterStart && colBeforeEnd;
else if (onStartLine) return colAfterStart;
else if (onEndLine) return colBeforeEnd;
- else return start[1] < line && line < end[1]
+ else return start[1] < line && line < end[1];
}
getSelectedText () {
@@ -482,13 +494,13 @@ class TermScreen {
if (this.isInSelection(x, y)) {
if (previousLineIndex !== y) {
previousLineIndex = y;
- lines.push('')
+ lines.push('');
}
- lines[lines.length - 1] += this.screen[cell]
+ lines[lines.length - 1] += this.screen[cell];
}
}
- return lines.join('\n')
+ return lines.join('\n');
}
copySelectionToClipboard () {
@@ -513,7 +525,7 @@ class TermScreen {
return [
Math.floor((x + cellSize.width / 2) / cellSize.width),
- Math.floor(y / cellSize.height)
+ Math.floor(y / cellSize.height),
];
}
@@ -523,15 +535,12 @@ class TermScreen {
return [ x * cellSize.width, y * cellSize.height ];
}
- drawCell ({ x, y, charSize, cellWidth, cellHeight, text, fg, bg, attrs },
- compositeAbove = false) {
+ drawCell ({ x, y, charSize, cellWidth, cellHeight, text, fg, bg, attrs }) {
const ctx = this.ctx;
const inSelection = this.isInSelection(x, y);
ctx.fillStyle = inSelection ? SELECTION_BG : this.colors[bg];
- if (!compositeAbove) ctx.globalCompositeOperation = 'destination-over';
ctx.fillRect(x * cellWidth, y * cellHeight,
Math.ceil(cellWidth), Math.ceil(cellHeight));
- ctx.globalCompositeOperation = 'source-over';
if (!text) return;
@@ -566,11 +575,11 @@ class TermScreen {
ctx.lineTo((x + 1) * cellWidth, lineY);
}
- ctx.stroke()
+ ctx.stroke();
}
}
- ctx.globalAlpha = 1
+ ctx.globalAlpha = 1;
}
draw () {
@@ -590,7 +599,6 @@ class TermScreen {
const screenLength = width * height;
ctx.setTransform(devicePixelRatio, 0, 0, devicePixelRatio, 0, 0);
- ctx.clearRect(0, 0, screenWidth, screenHeight);
ctx.font = this.getFont();
ctx.textAlign = 'center';
@@ -602,40 +610,79 @@ class TermScreen {
// Map of (attrs & FONT_MASK) -> Array of cell indices
const fontGroups = new Map();
+ // Map of (cell index) -> boolean, whether or not a cell needs to be redrawn
+ const updateMap = new Map();
+
for (let cell = 0; cell < screenLength; cell++) {
+ let x = cell % width;
+ let y = Math.floor(cell / width);
+ let isCursor = this.cursor.x === x && this.cursor.y === y &&
+ !this.cursor.hanging;
+ let invertForCursor = isCursor && this.cursor.blinkOn &&
+ this.cursor.style === 'block';
+
+ let text = this.screen[cell];
+ let fg = invertForCursor ? this.screenBG[cell] : this.screenFG[cell];
+ let bg = invertForCursor ? this.screenFG[cell] : this.screenBG[cell];
let attrs = this.screenAttrs[cell];
+
+ // HACK: ensure cursor is visible
+ if (invertForCursor && fg === bg) bg = fg === 0 ? 7 : 0;
+
+ let cellDidChange = text !== this.drawnScreen[cell] ||
+ fg !== this.drawnScreenFG[cell] ||
+ bg !== this.drawnScreenBG[cell] ||
+ attrs !== this.drawnScreenAttrs[cell];
+
let font = attrs & FONT_MASK;
if (!fontGroups.has(font)) fontGroups.set(font, []);
- fontGroups.get(font).push(cell);
+
+ fontGroups.get(font).push([cell, x, y, text, fg, bg, attrs, isCursor]);
+ updateMap.set(cell, cellDidChange);
}
for (let font of fontGroups.keys()) {
// set font once because in Firefox, this is a really slow action for some
// reason
- let modifiers = {}
- if (font & 1) modifiers.weight = 'bold'
- if (font & 1 << 2) modifiers.style = 'italic'
- ctx.font = this.getFont(modifiers)
-
- for (let cell of fontGroups.get(font)) {
- let x = cell % width;
- let y = Math.floor(cell / width);
- let isCursor = this.cursor.x === x && this.cursor.y === y;
- if (this.cursor.hanging) isCursor = false;
- let invertForCursor = isCursor && this.cursor.blinkOn &&
- this.cursor.style === 'block';
-
- let text = this.screen[cell];
- let fg = invertForCursor ? this.screenBG[cell] : this.screenFG[cell];
- let bg = invertForCursor ? this.screenFG[cell] : this.screenBG[cell];
- let attrs = this.screenAttrs[cell];
-
- // HACK: ensure cursor is visible
- if (invertForCursor && fg === bg) bg = fg === 0 ? 7 : 0;
-
- this.drawCell({
- x, y, charSize, cellWidth, cellHeight, text, fg, bg, attrs
- });
+ let modifiers = {};
+ if (font & 1) modifiers.weight = 'bold';
+ if (font & 1 << 2) modifiers.style = 'italic';
+ ctx.font = this.getFont(modifiers);
+
+ for (let data of fontGroups.get(font)) {
+ let [cell, x, y, text, fg, bg, attrs, isCursor] = data;
+
+ // check if this cell or any adjacent cells updated
+ let needsUpdate = false;
+ let updateCells = [
+ cell,
+ cell - 1,
+ cell + 1,
+ cell - width,
+ cell + width,
+ // diagonal box drawing characters exist, too
+ cell - width - 1,
+ cell - width + 1,
+ cell + width - 1,
+ cell + width + 1
+ ];
+ for (let index of updateCells) {
+ if (updateMap.has(index) && updateMap.get(index)) {
+ needsUpdate = true;
+ break;
+ }
+ }
+
+ if (needsUpdate) {
+ this.drawCell({
+ x, y, charSize, cellWidth, cellHeight, text, fg, bg, attrs
+ });
+
+ this.drawnScreen[cell] = text;
+ this.drawnScreenFG[cell] = fg;
+ this.drawnScreenBG[cell] = bg;
+ this.drawnScreenAttrs[cell] = attrs;
+ }
if (isCursor && this.cursor.blinkOn && this.cursor.style !== 'block') {
ctx.save();
@@ -660,8 +707,8 @@ class TermScreen {
this.drawCell({
x, y, charSize, cellWidth, cellHeight, text, fg, bg, attrs
- }, true);
- ctx.restore()
+ });
+ ctx.restore();
}
}
}