Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PoC for protected flag #3712

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
123 changes: 117 additions & 6 deletions src/common/InputHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,9 @@ export enum WindowsOptionsReportType {
GET_CELL_SIZE_PIXELS = 1
}

// FIXME: add to terminal settings somewhere
let is_protected = false;

// create a warning log if an async handler takes longer than the limit (in ms)
const SLOW_ASYNC_LIMIT = 5000;

Expand Down Expand Up @@ -335,9 +338,9 @@ export class InputHandler extends Disposable implements IInputHandler {
this._parser.registerCsiHandler({ final: 'H' }, params => this.cursorPosition(params));
this._parser.registerCsiHandler({ final: 'I' }, params => this.cursorForwardTab(params));
this._parser.registerCsiHandler({ final: 'J' }, params => this.eraseInDisplay(params));
this._parser.registerCsiHandler({ prefix: '?', final: 'J' }, params => this.eraseInDisplay(params));
this._parser.registerCsiHandler({ prefix: '?', final: 'J' }, params => this.eraseInDisplayProtected(params));
this._parser.registerCsiHandler({ final: 'K' }, params => this.eraseInLine(params));
this._parser.registerCsiHandler({ prefix: '?', final: 'K' }, params => this.eraseInLine(params));
this._parser.registerCsiHandler({ prefix: '?', final: 'K' }, params => this.eraseInLineProtected(params));
this._parser.registerCsiHandler({ final: 'L' }, params => this.insertLines(params));
this._parser.registerCsiHandler({ final: 'M' }, params => this.deleteLines(params));
this._parser.registerCsiHandler({ final: 'P' }, params => this.deleteChars(params));
Expand Down Expand Up @@ -370,6 +373,21 @@ export class InputHandler extends Disposable implements IInputHandler {
this._parser.registerCsiHandler({ intermediates: '\'', final: '}' }, params => this.insertColumns(params));
this._parser.registerCsiHandler({ intermediates: '\'', final: '~' }, params => this.deleteColumns(params));

// DECSCA quick hack
this._parser.registerCsiHandler({ intermediates: '"', final: 'q' }, params => {
switch (params.params[0]) {
case 1:
is_protected = true;
break;
case 0:
case 2:
is_protected = false;
break;
}
return true;
});


/**
* execute handler
*/
Expand Down Expand Up @@ -612,9 +630,11 @@ export class InputHandler extends Disposable implements IInputHandler {

this._dirtyRowService.markDirty(this._activeBuffer.y);

const _is_protected = is_protected ? 1 : 0; // FIXME: pull from terminal settings

// handle wide chars: reset start_cell-1 if we would overwrite the second cell of a wide char
if (this._activeBuffer.x && end - start > 0 && bufferRow.getWidth(this._activeBuffer.x - 1) === 2) {
bufferRow.setCellFromCodePoint(this._activeBuffer.x - 1, 0, 1, curAttr.fg, curAttr.bg, curAttr.extended);
bufferRow.setCellFromCodePoint(this._activeBuffer.x - 1, 0, 1, curAttr.fg, curAttr.bg, curAttr.extended, _is_protected);
}

for (let pos = start; pos < end; ++pos) {
Expand Down Expand Up @@ -663,7 +683,7 @@ export class InputHandler extends Disposable implements IInputHandler {
if (wraparoundMode) {
// clear left over cells to the right
while (this._activeBuffer.x < cols) {
bufferRow.setCellFromCodePoint(this._activeBuffer.x++, 0, 1, curAttr.fg, curAttr.bg, curAttr.extended);
bufferRow.setCellFromCodePoint(this._activeBuffer.x++, 0, 1, curAttr.fg, curAttr.bg, curAttr.extended, _is_protected);
}
this._activeBuffer.x = 0;
this._activeBuffer.y++;
Expand Down Expand Up @@ -703,15 +723,15 @@ export class InputHandler extends Disposable implements IInputHandler {
}

// write current char to buffer and advance cursor
bufferRow.setCellFromCodePoint(this._activeBuffer.x++, code, chWidth, curAttr.fg, curAttr.bg, curAttr.extended);
bufferRow.setCellFromCodePoint(this._activeBuffer.x++, code, chWidth, curAttr.fg, curAttr.bg, curAttr.extended, _is_protected);

// fullwidth char - also set next cell to placeholder stub and advance cursor
// for graphemes bigger than fullwidth we can simply loop to zero
// we already made sure above, that this._activeBuffer.x + chWidth will not overflow right
if (chWidth > 0) {
while (--chWidth) {
// other than a regular empty cell a cell following a wide char has no width
bufferRow.setCellFromCodePoint(this._activeBuffer.x++, 0, 0, curAttr.fg, curAttr.bg, curAttr.extended);
bufferRow.setCellFromCodePoint(this._activeBuffer.x++, 0, 0, curAttr.fg, curAttr.bg, curAttr.extended, _is_protected);
}
}
}
Expand Down Expand Up @@ -1223,6 +1243,21 @@ export class InputHandler extends Disposable implements IInputHandler {
}
}

private _eraseInBufferLineProtected(y: number, start: number, end: number, clearWrap: boolean = false): void {
// TODO: also apply fullwidth edge cases from Bufferline.replaceCells
const line = this._activeBuffer.lines.get(this._activeBuffer.ybase + y)!;
const fillCellData = this._activeBuffer.getNullCell(this._eraseAttrData());
while (start < end && start < line.length) {
if (!line.getProtected(start)) {
line.setCell(start, fillCellData);
}
start++;
}
if (clearWrap) {
line.isWrapped = false;
}
}

/**
* Helper method to reset cells in a terminal row.
* The cell gets replaced with the eraseChar of the terminal and the isWrapped property is set to false.
Expand All @@ -1234,6 +1269,17 @@ export class InputHandler extends Disposable implements IInputHandler {
this._bufferService.buffer.clearMarkers(this._activeBuffer.ybase + y);
line.isWrapped = false;
}
private _resetBufferLineProtected(y: number): void {
const line = this._activeBuffer.lines.get(this._activeBuffer.ybase + y)!;
const fillCellData = this._activeBuffer.getNullCell(this._eraseAttrData());
for (let i = 0; i < line.length; ++i) {
if (!line.getProtected(i)) {
line.setCell(i, fillCellData);
}
}
this._bufferService.buffer.clearMarkers(this._activeBuffer.ybase + y);
line.isWrapped = false;
}

/**
* CSI Ps J Erase in Display (ED).
Expand Down Expand Up @@ -1308,6 +1354,55 @@ export class InputHandler extends Disposable implements IInputHandler {
}
return true;
}
public eraseInDisplayProtected(params: IParams): boolean {
this._restrictCursor(this._bufferService.cols);
let j;
switch (params.params[0]) {
case 0:
j = this._activeBuffer.y;
this._dirtyRowService.markDirty(j);
this._eraseInBufferLineProtected(j++, this._activeBuffer.x, this._bufferService.cols, this._activeBuffer.x === 0);
for (; j < this._bufferService.rows; j++) {
this._resetBufferLineProtected(j);
}
this._dirtyRowService.markDirty(j);
break;
case 1:
j = this._activeBuffer.y;
this._dirtyRowService.markDirty(j);
// Deleted front part of line and everything before. This line will no longer be wrapped.
this._eraseInBufferLineProtected(j, 0, this._activeBuffer.x + 1, true);
if (this._activeBuffer.x + 1 >= this._bufferService.cols) {
// Deleted entire previous line. This next line can no longer be wrapped.
this._activeBuffer.lines.get(j + 1)!.isWrapped = false;
}
while (j--) {
this._resetBufferLineProtected(j);
}
this._dirtyRowService.markDirty(0);
break;
case 2:
j = this._bufferService.rows;
this._dirtyRowService.markDirty(j - 1);
while (j--) {
this._resetBufferLineProtected(j);
}
this._dirtyRowService.markDirty(0);
break;
case 3:
// Clear scrollback (everything not in viewport)
const scrollBackSize = this._activeBuffer.lines.length - this._bufferService.rows;
if (scrollBackSize > 0) {
this._activeBuffer.lines.trimStart(scrollBackSize);
this._activeBuffer.ybase = Math.max(this._activeBuffer.ybase - scrollBackSize, 0);
this._activeBuffer.ydisp = Math.max(this._activeBuffer.ydisp - scrollBackSize, 0);
// Force a scroll event to refresh viewport
this._onScroll.fire(0);
}
break;
}
return true;
}

/**
* CSI Ps K Erase in Line (EL).
Expand Down Expand Up @@ -1347,6 +1442,22 @@ export class InputHandler extends Disposable implements IInputHandler {
this._dirtyRowService.markDirty(this._activeBuffer.y);
return true;
}
public eraseInLineProtected(params: IParams): boolean {
this._restrictCursor(this._bufferService.cols);
switch (params.params[0]) {
case 0:
this._eraseInBufferLineProtected(this._activeBuffer.y, this._activeBuffer.x, this._bufferService.cols, this._activeBuffer.x === 0);
break;
case 1:
this._eraseInBufferLineProtected(this._activeBuffer.y, 0, this._activeBuffer.x + 1, false);
break;
case 2:
this._eraseInBufferLineProtected(this._activeBuffer.y, 0, this._bufferService.cols, true);
break;
}
this._dirtyRowService.markDirty(this._activeBuffer.y);
return true;
}

/**
* CSI Ps L
Expand Down
3 changes: 2 additions & 1 deletion src/common/Types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ export interface IBufferLine {
set(index: number, value: CharData): void;
loadCell(index: number, cell: ICellData): ICellData;
setCell(index: number, cell: ICellData): void;
setCellFromCodePoint(index: number, codePoint: number, width: number, fg: number, bg: number, eAttrs: IExtendedAttrs): void;
setCellFromCodePoint(index: number, codePoint: number, width: number, fg: number, bg: number, eAttrs: IExtendedAttrs, is_protected?: number): void;
addCodepointToCell(index: number, codePoint: number): void;
insertCells(pos: number, n: number, ch: ICellData, eraseAttr?: IAttributeData): void;
deleteCells(pos: number, n: number, fill: ICellData, eraseAttr?: IAttributeData): void;
Expand All @@ -191,6 +191,7 @@ export interface IBufferLine {

/* direct access to cell attrs */
getWidth(index: number): number;
getProtected(index: number): number;
hasWidth(index: number): number;
getFg(index: number): number;
getBg(index: number): number;
Expand Down
13 changes: 8 additions & 5 deletions src/common/buffer/BufferLine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ import { AttributeData, ExtendedAttrs } from 'common/buffer/AttributeData';
/**
* buffer memory layout:
*
* | uint32_t | uint32_t | uint32_t |
* | `content` | `FG` | `BG` |
* | wcwidth(2) comb(1) codepoint(21) | flags(8) R(8) G(8) B(8) | flags(8) R(8) G(8) B(8) |
* | uint32_t | uint32_t | uint32_t |
* | `content` | `FG` | `BG` |
* | wcwidth(2) protected(1) comb(1) codepoint(21) | flags(8) R(8) G(8) B(8) | flags(8) R(8) G(8) B(8) |
*/


Expand Down Expand Up @@ -107,6 +107,9 @@ export class BufferLine implements IBufferLine {
public getWidth(index: number): number {
return this._data[index * CELL_SIZE + Cell.CONTENT] >> Content.WIDTH_SHIFT;
}
public getProtected(index: number): number {
return this._data[index * CELL_SIZE + Cell.CONTENT] & Content.IS_PROTECTED_MASK;
}

/** Test whether content has width. */
public hasWidth(index: number): number {
Expand Down Expand Up @@ -201,11 +204,11 @@ export class BufferLine implements IBufferLine {
* Since the input handler see the incoming chars as UTF32 codepoints,
* it gets an optimized access method.
*/
public setCellFromCodePoint(index: number, codePoint: number, width: number, fg: number, bg: number, eAttrs: IExtendedAttrs): void {
public setCellFromCodePoint(index: number, codePoint: number, width: number, fg: number, bg: number, eAttrs: IExtendedAttrs, is_protected: number = 0): void {
if (bg & BgFlags.HAS_EXTENDED) {
this._extendedAttrs[index] = eAttrs;
}
this._data[index * CELL_SIZE + Cell.CONTENT] = codePoint | (width << Content.WIDTH_SHIFT);
this._data[index * CELL_SIZE + Cell.CONTENT] = codePoint | (width << Content.WIDTH_SHIFT) | (is_protected << 22);
this._data[index * CELL_SIZE + Cell.FG] = fg;
this._data[index * CELL_SIZE + Cell.BG] = bg;
}
Expand Down
8 changes: 6 additions & 2 deletions src/common/buffer/Constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ export const enum Content {
*/
HAS_CONTENT_MASK = 0x3FFFFF,

IS_PROTECTED_MASK = 0x400000, // 1 << 22

/**
* bit 23..24 wcwidth value of cell, takes 2 bits (ranges from 0..2)
* read: `width = (content & Content.widthMask) >> Content.widthShift;`
Expand All @@ -68,8 +70,10 @@ export const enum Content {
* shortcut if precondition `0 <= width <= 3` is met:
* `content |= width << Content.widthShift;`
*/
WIDTH_MASK = 0xC00000, // 3 << 22
WIDTH_SHIFT = 22
//WIDTH_MASK = 0xC00000, // 3 << 22
//WIDTH_SHIFT = 22
WIDTH_MASK = 0x1800000, // 3 << 23
WIDTH_SHIFT = 23
}

export const enum Attributes {
Expand Down