X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=src%2FRenderer.ts;h=165594fd25f6d165c116d89e209f0f529db98362;hb=HEAD;hp=85d57eb93f8d7397bd7d11e4cab63d297eacbe13;hpb=47814847f9348fe33cb4c76ae78845ad9f7187c9;p=mirror_xterm.js.git diff --git a/src/Renderer.ts b/src/Renderer.ts index 85d57eb..165594f 100644 --- a/src/Renderer.ts +++ b/src/Renderer.ts @@ -139,15 +139,15 @@ export class Renderer { } for (; y <= end; y++) { - let row = y + this._terminal.ydisp; + let row = y + this._terminal.buffer.ydisp; - let line = this._terminal.lines.get(row); + let line = this._terminal.buffer.lines.get(row); let x; - if (this._terminal.y === y - (this._terminal.ybase - this._terminal.ydisp) && + if (this._terminal.buffer.y === y - (this._terminal.buffer.ybase - this._terminal.buffer.ydisp) && this._terminal.cursorState && !this._terminal.cursorHidden) { - x = this._terminal.x; + x = this._terminal.buffer.x; } else { x = -1; } @@ -170,16 +170,13 @@ export class Renderer { let data: any = line[i][0]; const ch = line[i][1]; const ch_width: any = line[i][2]; + const isCursor: boolean = i === x; if (!ch_width) { continue; } - if (i === x) { - data = -1; - } - - if (data !== attr) { - if (attr !== this._terminal.defAttr) { + if (data !== attr || isCursor) { + if (attr !== this._terminal.defAttr && !isCursor) { if (innerHTML) { currentElement.innerHTML = innerHTML; innerHTML = ''; @@ -187,7 +184,7 @@ export class Renderer { documentFragment.appendChild(currentElement); currentElement = null; } - if (data !== this._terminal.defAttr) { + if (data !== this._terminal.defAttr || isCursor) { if (innerHTML && !currentElement) { currentElement = this._spanElementObjectPool.acquire(); } @@ -199,75 +196,79 @@ export class Renderer { documentFragment.appendChild(currentElement); } currentElement = this._spanElementObjectPool.acquire(); - if (data === -1) { - currentElement.classList.add('reverse-video', 'terminal-cursor'); - } else { - let bg = data & 0x1ff; - let fg = (data >> 9) & 0x1ff; - let flags = data >> 18; - - if (flags & FLAGS.BOLD) { - if (!brokenBold) { - currentElement.classList.add('xterm-bold'); - } - // See: XTerm*boldColors - if (fg < 8) { - fg += 8; - } - } - if (flags & FLAGS.UNDERLINE) { - currentElement.classList.add('xterm-underline'); - } + let bg = data & 0x1ff; + let fg = (data >> 9) & 0x1ff; + let flags = data >> 18; - if (flags & FLAGS.BLINK) { - currentElement.classList.add('xterm-blink'); - } + if (isCursor) { + currentElement.classList.add('reverse-video'); + currentElement.classList.add('terminal-cursor'); + } - // If inverse flag is on, then swap the foreground and background variables. - if (flags & FLAGS.INVERSE) { - let temp = bg; - bg = fg; - fg = temp; - // Should inverse just be before the above boldColors effect instead? - if ((flags & 1) && fg < 8) { - fg += 8; - } + if (flags & FLAGS.BOLD) { + if (!brokenBold) { + currentElement.classList.add('xterm-bold'); } - - if (flags & FLAGS.INVISIBLE) { - currentElement.classList.add('xterm-hidden'); + // See: XTerm*boldColors + if (fg < 8) { + fg += 8; } + } - /** - * Weird situation: Invert flag used black foreground and white background results - * in invalid background color, positioned at the 256 index of the 256 terminal - * color map. Pin the colors manually in such a case. - * - * Source: https://github.com/sourcelair/xterm.js/issues/57 - */ - if (flags & FLAGS.INVERSE) { - if (bg == 257) { - bg = 15; - } - if (fg == 256) { - fg = 0; - } - } + if (flags & FLAGS.UNDERLINE) { + currentElement.classList.add('xterm-underline'); + } - if (bg < 256) { - currentElement.classList.add('xterm-bg-color-' + bg); + if (flags & FLAGS.BLINK) { + currentElement.classList.add('xterm-blink'); + } + + // If inverse flag is on, then swap the foreground and background variables. + if (flags & FLAGS.INVERSE) { + let temp = bg; + bg = fg; + fg = temp; + // Should inverse just be before the above boldColors effect instead? + if ((flags & 1) && fg < 8) { + fg += 8; } + } + + if (flags & FLAGS.INVISIBLE && !isCursor) { + currentElement.classList.add('xterm-hidden'); + } - if (fg < 256) { - currentElement.classList.add('xterm-color-' + fg); + /** + * Weird situation: Invert flag used black foreground and white background results + * in invalid background color, positioned at the 256 index of the 256 terminal + * color map. Pin the colors manually in such a case. + * + * Source: https://github.com/sourcelair/xterm.js/issues/57 + */ + if (flags & FLAGS.INVERSE) { + if (bg === 257) { + bg = 15; } + if (fg === 256) { + fg = 0; + } + } + + if (bg < 256) { + currentElement.classList.add(`xterm-bg-color-${bg}`); + } + + if (fg < 256) { + currentElement.classList.add(`xterm-color-${fg}`); } + } } if (ch_width === 2) { - // Wrap wide characters so they're sized correctly + // Wrap wide characters so they're sized correctly. It's more difficult to release these + // from the object pool so just create new ones via innerHTML. innerHTML += `${ch}`; } else if (ch.charCodeAt(0) > 255) { // Wrap any non-wide unicode character as some fonts size them badly @@ -293,7 +294,10 @@ export class Renderer { } } - attr = data; + // The cursor needs its own element, therefore we set attr to -1 + // which will cause the next character to be rendered in a new element + attr = isCursor ? -1 : data; + } if (innerHTML && !currentElement) { @@ -308,7 +312,7 @@ export class Renderer { currentElement = null; } - this._terminal.children[y].appendChild(documentFragment) + this._terminal.children[y].appendChild(documentFragment); } if (parent) { @@ -317,11 +321,70 @@ export class Renderer { this._terminal.emit('refresh', {element: this._terminal.element, start: start, end: end}); }; + + /** + * Refreshes the selection in the DOM. + * @param start The selection start. + * @param end The selection end. + */ + public refreshSelection(start: [number, number], end: [number, number]) { + // Remove all selections + while (this._terminal.selectionContainer.children.length) { + this._terminal.selectionContainer.removeChild(this._terminal.selectionContainer.children[0]); + } + + // Selection does not exist + if (!start || !end) { + return; + } + + // Translate from buffer position to viewport position + const viewportStartRow = start[1] - this._terminal.buffer.ydisp; + const viewportEndRow = end[1] - this._terminal.buffer.ydisp; + const viewportCappedStartRow = Math.max(viewportStartRow, 0); + const viewportCappedEndRow = Math.min(viewportEndRow, this._terminal.rows - 1); + + // No need to draw the selection + if (viewportCappedStartRow >= this._terminal.rows || viewportCappedEndRow < 0) { + return; + } + + // Create the selections + const documentFragment = document.createDocumentFragment(); + // Draw first row + const startCol = viewportStartRow === viewportCappedStartRow ? start[0] : 0; + const endCol = viewportCappedStartRow === viewportCappedEndRow ? end[0] : this._terminal.cols; + documentFragment.appendChild(this._createSelectionElement(viewportCappedStartRow, startCol, endCol)); + // Draw middle rows + const middleRowsCount = viewportCappedEndRow - viewportCappedStartRow - 1; + documentFragment.appendChild(this._createSelectionElement(viewportCappedStartRow + 1, 0, this._terminal.cols, middleRowsCount)); + // Draw final row + if (viewportCappedStartRow !== viewportCappedEndRow) { + // Only draw viewportEndRow if it's not the same as viewporttartRow + const endCol = viewportEndRow === viewportCappedEndRow ? end[0] : this._terminal.cols; + documentFragment.appendChild(this._createSelectionElement(viewportCappedEndRow, 0, endCol)); + } + this._terminal.selectionContainer.appendChild(documentFragment); + } + + /** + * Creates a selection element at the specified position. + * @param row The row of the selection. + * @param colStart The start column. + * @param colEnd The end columns. + */ + private _createSelectionElement(row: number, colStart: number, colEnd: number, rowCount: number = 1): HTMLElement { + const element = document.createElement('div'); + element.style.height = `${rowCount * this._terminal.charMeasure.height}px`; + element.style.top = `${row * this._terminal.charMeasure.height}px`; + element.style.left = `${colStart * this._terminal.charMeasure.width}px`; + element.style.width = `${this._terminal.charMeasure.width * (colEnd - colStart)}px`; + return element; + } } -// if bold is broken, we can't -// use it in the terminal. +// If bold is broken, we can't use it in the terminal. function checkBoldBroken(terminal) { const document = terminal.ownerDocument; const el = document.createElement('span');