mirror of
https://github.com/wavetermdev/xterm.js.git
synced 2026-08-05 13:43:48 -07:00
implement 14, 16, 18, 20, 21, 22, 23; restrict DECCOLM to 24
This commit is contained in:
+3
-2
@@ -227,7 +227,8 @@ function initOptions(term: TerminalType): void {
|
||||
fontWeightBold: ['normal', 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900'],
|
||||
logLevel: ['debug', 'info', 'warn', 'error', 'off'],
|
||||
rendererType: ['dom', 'canvas'],
|
||||
wordSeparator: null
|
||||
wordSeparator: null,
|
||||
allowedWindowOps: ''
|
||||
};
|
||||
const options = Object.keys((<any>term)._core.options);
|
||||
const booleanOptions = [];
|
||||
@@ -258,7 +259,7 @@ function initOptions(term: TerminalType): void {
|
||||
});
|
||||
html += '</div><div class="option-group">';
|
||||
Object.keys(stringOptions).forEach(o => {
|
||||
if (stringOptions[o]) {
|
||||
if (stringOptions[o] && typeof stringOptions[o] !== 'string') {
|
||||
html += `<div class="option"><label>${o} <select id="opt-${o}">${stringOptions[o].map(v => `<option ${term.getOption(o) === v ? 'selected' : ''}>${v}</option>`).join('')}</select></label></div>`;
|
||||
} else {
|
||||
html += `<div class="option"><label>${o} <input id="opt-${o}" type="text" value="${term.getOption(o)}"/></label></div>`;
|
||||
|
||||
@@ -1266,4 +1266,151 @@ describe('InputHandler', () => {
|
||||
[131072, 131072], [131072, 131072], [131072, 300000 - 131072 - 131072]
|
||||
]);
|
||||
});
|
||||
describe('windowOps', () => {
|
||||
it('all should be disabled by default and not report', () => {
|
||||
const term = new TestTerminal({cols: 10, rows: 10});
|
||||
assert.deepEqual(term.options.allowedWindowOps, []);
|
||||
const stack: string[] = [];
|
||||
term.onData(data => stack.push(data));
|
||||
term.writeSync('\x1b[14t');
|
||||
term.writeSync('\x1b[16t');
|
||||
term.writeSync('\x1b[18t');
|
||||
term.writeSync('\x1b[20t');
|
||||
term.writeSync('\x1b[21t');
|
||||
assert.deepEqual(stack, []);
|
||||
});
|
||||
it('14 - GetWinSizePixels', () => {
|
||||
const term = new TestTerminal({cols: 10, rows: 10, allowedWindowOps: [14]});
|
||||
assert.deepEqual(term.options.allowedWindowOps, [14]);
|
||||
const stack: string[] = [];
|
||||
term.onData(data => stack.push(data));
|
||||
term.writeSync('\x1b[14t');
|
||||
// does not report in test terminal due to missing renderer
|
||||
assert.deepEqual(stack, []);
|
||||
});
|
||||
it('16 - GetCellSizePixels', () => {
|
||||
const term = new TestTerminal({cols: 10, rows: 10, allowedWindowOps: [16]});
|
||||
assert.deepEqual(term.options.allowedWindowOps, [16]);
|
||||
const stack: string[] = [];
|
||||
term.onData(data => stack.push(data));
|
||||
term.writeSync('\x1b[16t');
|
||||
// does not report in test terminal due to missing renderer
|
||||
assert.deepEqual(stack, []);
|
||||
});
|
||||
it('18 - GetWinSizeChars', () => {
|
||||
const term = new TestTerminal({cols: 10, rows: 10, allowedWindowOps: [18]});
|
||||
assert.deepEqual(term.options.allowedWindowOps, [18]);
|
||||
const stack: string[] = [];
|
||||
term.onData(data => stack.push(data));
|
||||
term.writeSync('\x1b[18t');
|
||||
assert.deepEqual(stack, ['\x1b[8;10;10t']);
|
||||
term.resize(50, 20);
|
||||
term.writeSync('\x1b[18t');
|
||||
assert.deepEqual(stack, ['\x1b[8;10;10t', '\x1b[8;20;50t']);
|
||||
});
|
||||
it('20 - GetIconTitle', () => {
|
||||
const term = new TestTerminal({cols: 10, rows: 10, allowedWindowOps: [20]});
|
||||
assert.deepEqual(term.options.allowedWindowOps, [20]);
|
||||
const stack: string[] = [];
|
||||
term.onData(data => stack.push(data));
|
||||
term.writeSync('\x1b]1;hello world!\x07');
|
||||
term.writeSync('\x1b[20t');
|
||||
assert.deepEqual(stack, ['\x1b]Lhello world!\x1b\\']);
|
||||
term.writeSync('\x1b]1;some other\x07');
|
||||
term.writeSync('\x1b[20t');
|
||||
assert.deepEqual(stack, ['\x1b]Lhello world!\x1b\\', '\x1b]Lsome other\x1b\\']);
|
||||
});
|
||||
it('21 - GetWinTitle', () => {
|
||||
const term = new TestTerminal({cols: 10, rows: 10, allowedWindowOps: [21]});
|
||||
assert.deepEqual(term.options.allowedWindowOps, [21]);
|
||||
const stack: string[] = [];
|
||||
term.onData(data => stack.push(data));
|
||||
term.writeSync('\x1b]2;hello world!\x07');
|
||||
term.writeSync('\x1b[21t');
|
||||
assert.deepEqual(stack, ['\x1b]lhello world!\x1b\\']);
|
||||
term.writeSync('\x1b]2;some other\x07');
|
||||
term.writeSync('\x1b[21t');
|
||||
assert.deepEqual(stack, ['\x1b]lhello world!\x1b\\', '\x1b]lsome other\x1b\\']);
|
||||
});
|
||||
it('22/23 - PushTitle/PopTitle', () => {
|
||||
const term = new TestTerminal({cols: 10, rows: 10, allowedWindowOps: [22, 23]});
|
||||
assert.deepEqual(term.options.allowedWindowOps, [22, 23]);
|
||||
const stack: string[] = [];
|
||||
term.onTitleChange(data => stack.push(data));
|
||||
term.writeSync('\x1b]0;1\x07');
|
||||
term.writeSync('\x1b[22t');
|
||||
term.writeSync('\x1b]0;2\x07');
|
||||
term.writeSync('\x1b[22t');
|
||||
term.writeSync('\x1b]0;3\x07');
|
||||
term.writeSync('\x1b[22t');
|
||||
assert.deepEqual((term as any)._inputHandler._windowTitleStack, ['1', '2', '3']);
|
||||
assert.deepEqual((term as any)._inputHandler._iconNameStack, ['1', '2', '3']);
|
||||
assert.deepEqual(stack, ['1', '2', '3']);
|
||||
term.writeSync('\x1b[23t');
|
||||
term.writeSync('\x1b[23t');
|
||||
term.writeSync('\x1b[23t');
|
||||
term.writeSync('\x1b[23t'); // one more to test "overflow"
|
||||
assert.deepEqual((term as any)._inputHandler._windowTitleStack, []);
|
||||
assert.deepEqual((term as any)._inputHandler._iconNameStack, []);
|
||||
assert.deepEqual(stack, ['1', '2', '3', '3', '2', '1']);
|
||||
});
|
||||
it('22/23 - PushTitle/PopTitle with ;1', () => {
|
||||
const term = new TestTerminal({cols: 10, rows: 10, allowedWindowOps: [22, 23]});
|
||||
assert.deepEqual(term.options.allowedWindowOps, [22, 23]);
|
||||
const stack: string[] = [];
|
||||
term.onTitleChange(data => stack.push(data));
|
||||
term.writeSync('\x1b]0;1\x07');
|
||||
term.writeSync('\x1b[22;1t');
|
||||
term.writeSync('\x1b]0;2\x07');
|
||||
term.writeSync('\x1b[22;1t');
|
||||
term.writeSync('\x1b]0;3\x07');
|
||||
term.writeSync('\x1b[22;1t');
|
||||
assert.deepEqual((term as any)._inputHandler._windowTitleStack, []);
|
||||
assert.deepEqual((term as any)._inputHandler._iconNameStack, ['1', '2', '3']);
|
||||
assert.deepEqual(stack, ['1', '2', '3']);
|
||||
term.writeSync('\x1b[23;1t');
|
||||
term.writeSync('\x1b[23;1t');
|
||||
term.writeSync('\x1b[23;1t');
|
||||
term.writeSync('\x1b[23;1t'); // one more to test "overflow"
|
||||
assert.deepEqual((term as any)._inputHandler._windowTitleStack, []);
|
||||
assert.deepEqual((term as any)._inputHandler._iconNameStack, []);
|
||||
assert.deepEqual(stack, ['1', '2', '3']);
|
||||
});
|
||||
it('22/23 - PushTitle/PopTitle with ;2', () => {
|
||||
const term = new TestTerminal({cols: 10, rows: 10, allowedWindowOps: [22, 23]});
|
||||
assert.deepEqual(term.options.allowedWindowOps, [22, 23]);
|
||||
const stack: string[] = [];
|
||||
term.onTitleChange(data => stack.push(data));
|
||||
term.writeSync('\x1b]0;1\x07');
|
||||
term.writeSync('\x1b[22;2t');
|
||||
term.writeSync('\x1b]0;2\x07');
|
||||
term.writeSync('\x1b[22;2t');
|
||||
term.writeSync('\x1b]0;3\x07');
|
||||
term.writeSync('\x1b[22;2t');
|
||||
assert.deepEqual((term as any)._inputHandler._windowTitleStack, ['1', '2', '3']);
|
||||
assert.deepEqual((term as any)._inputHandler._iconNameStack, []);
|
||||
assert.deepEqual(stack, ['1', '2', '3']);
|
||||
term.writeSync('\x1b[23;2t');
|
||||
term.writeSync('\x1b[23;2t');
|
||||
term.writeSync('\x1b[23;2t');
|
||||
term.writeSync('\x1b[23;2t'); // one more to test "overflow"
|
||||
assert.deepEqual((term as any)._inputHandler._windowTitleStack, []);
|
||||
assert.deepEqual((term as any)._inputHandler._iconNameStack, []);
|
||||
assert.deepEqual(stack, ['1', '2', '3', '3', '2', '1']);
|
||||
});
|
||||
it('DECCOLM - should only work with "SetWinLines" (24) enabled', () => {
|
||||
// disabled
|
||||
const term = new TestTerminal({cols: 10, rows: 10});
|
||||
term.writeSync('\x1b[?3l');
|
||||
assert.equal((term as any)._bufferService.cols, 10);
|
||||
term.writeSync('\x1b[?3h');
|
||||
assert.equal((term as any)._bufferService.cols, 10);
|
||||
// enabled
|
||||
const term2 = new TestTerminal({cols: 10, rows: 10, allowedWindowOps: [24]});
|
||||
term2.writeSync('\x1b[?3l');
|
||||
assert.equal((term2 as any)._bufferService.cols, 80);
|
||||
term2.writeSync('\x1b[?3h');
|
||||
assert.equal((term2 as any)._bufferService.cols, 132);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+129
-98
@@ -33,6 +33,11 @@ const GLEVEL: {[key: string]: number} = {'(': 0, ')': 1, '*': 2, '+': 3, '-': 1,
|
||||
*/
|
||||
const MAX_PARSEBUFFER_LENGTH = 131072;
|
||||
|
||||
/**
|
||||
* Limit length of title and icon name stacks.
|
||||
*/
|
||||
const STACK_LIMIT = 10;
|
||||
|
||||
|
||||
/**
|
||||
* DCS subparser implementations
|
||||
@@ -127,6 +132,10 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
private _stringDecoder: StringToUtf32 = new StringToUtf32();
|
||||
private _utf8Decoder: Utf8ToUtf32 = new Utf8ToUtf32();
|
||||
private _workCell: CellData = new CellData();
|
||||
private _windowTitle = '';
|
||||
private _iconName = '';
|
||||
private _windowTitleStack: string[] = [];
|
||||
private _iconNameStack: string[] = [];
|
||||
|
||||
private _onCursorMove = new EventEmitter<void>();
|
||||
public get onCursorMove(): IEvent<void> { return this._onCursorMove.event; }
|
||||
@@ -222,7 +231,7 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
this._parser.setCsiHandler({intermediates: ' ', final: 'q'}, params => this.setCursorStyle(params));
|
||||
this._parser.setCsiHandler({final: 'r'}, params => this.setScrollRegion(params));
|
||||
this._parser.setCsiHandler({final: 's'}, params => this.saveCursor(params));
|
||||
this._parser.setCsiHandler({final: 't'}, params => this.manipulateWindowOptions(params));
|
||||
this._parser.setCsiHandler({final: 't'}, params => this.windowOptions(params));
|
||||
this._parser.setCsiHandler({final: 'u'}, params => this.restoreCursor(params));
|
||||
this._parser.setCsiHandler({intermediates: '\'', final: '}'}, params => this.insertColumns(params));
|
||||
this._parser.setCsiHandler({intermediates: '\'', final: '~'}, params => this.deleteColumns(params));
|
||||
@@ -249,8 +258,9 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
* OSC handler
|
||||
*/
|
||||
// 0 - icon name + title
|
||||
this._parser.setOscHandler(0, new OscHandler((data: string) => this.setTitle(data)));
|
||||
this._parser.setOscHandler(0, new OscHandler((data: string) => { this.setTitle(data); this.setIconName(data); }));
|
||||
// 1 - icon name
|
||||
this._parser.setOscHandler(1, new OscHandler((data: string) => this.setIconName(data)));
|
||||
// 2 - title
|
||||
this._parser.setOscHandler(2, new OscHandler((data: string) => this.setTitle(data)));
|
||||
// 3 - set property X in the form "prop=value"
|
||||
@@ -502,6 +512,15 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
* Forward addCsiHandler from parser.
|
||||
*/
|
||||
public addCsiHandler(id: IFunctionIdentifier, callback: (params: IParams) => boolean): IDisposable {
|
||||
if (id.final === 't' && !id.prefix && !id.intermediates) {
|
||||
// security: always check whether window option is allowed
|
||||
return this._parser.addCsiHandler(id, params => {
|
||||
if (!~this._optionsService.options.allowedWindowOps.indexOf(params.params[0])) {
|
||||
return true;
|
||||
}
|
||||
return callback(params);
|
||||
});
|
||||
}
|
||||
return this._parser.addCsiHandler(id, callback);
|
||||
}
|
||||
|
||||
@@ -1391,11 +1410,16 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
this._terminal.setgCharset(3, DEFAULT_CHARSET);
|
||||
// set VT100 mode here
|
||||
break;
|
||||
case 3: // 132 col mode
|
||||
// TODO: move DECCOLM into compat addon
|
||||
this._terminal.savedCols = this._bufferService.cols;
|
||||
this._terminal.resize(132, this._bufferService.rows);
|
||||
this._terminal.reset();
|
||||
case 3:
|
||||
/**
|
||||
* DECCOLM - 132 column mode.
|
||||
* This is only active if 'SetWinLines' (24) is listed
|
||||
* in `options.allowedWindowOps`.
|
||||
*/
|
||||
if (~this._optionsService.options.allowedWindowOps.indexOf(24)) {
|
||||
this._terminal.resize(132, this._bufferService.rows);
|
||||
this._terminal.reset();
|
||||
}
|
||||
break;
|
||||
case 6:
|
||||
this._terminal.originMode = true;
|
||||
@@ -1574,11 +1598,15 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
// TODO: move DECCOLM into compat addon
|
||||
// Note: This impl currently does not enforce col 80, instead reverts
|
||||
// to previous terminal width before entering DECCOLM 132
|
||||
if (this._bufferService.cols === 132 && this._terminal.savedCols) {
|
||||
this._terminal.resize(this._terminal.savedCols, this._bufferService.rows);
|
||||
/**
|
||||
* DECCOLM - 80 column mode.
|
||||
* This is only active if 'SetWinLines' (24) is listed
|
||||
* in `options.allowedWindowOps`.
|
||||
*/
|
||||
if (~this._optionsService.options.allowedWindowOps.indexOf(24)) {
|
||||
this._terminal.resize(80, this._bufferService.rows);
|
||||
this._terminal.reset();
|
||||
}
|
||||
delete this._terminal.savedCols;
|
||||
this._terminal.reset();
|
||||
break;
|
||||
case 6:
|
||||
this._terminal.originMode = false;
|
||||
@@ -2020,96 +2048,91 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
|
||||
/**
|
||||
* CSI Ps ; Ps ; Ps t - Various window manipulations and reports (xterm)
|
||||
* Ps = 1 -> De-iconify window. not supported
|
||||
* Ps = 2 -> Iconify window. not supported
|
||||
* Ps = 3 ; x ; y -> Move window to [x, y]. not supported
|
||||
* Ps = 4 ; height ; width TBD
|
||||
* Resize the xterm window to given height and width in pixels.
|
||||
* Omitted parameters reuse the current height or width.
|
||||
* Zero parameters use the display's height or width.
|
||||
* Ps = 5 not supported
|
||||
* Raise the xterm window to the front of the stacking order.
|
||||
* Ps = 6 not supported
|
||||
* Lower the xterm window to the bottom of the stacking order.
|
||||
* Ps = 7 TBD
|
||||
* Refresh the xterm window.
|
||||
* Ps = 8 ; height ; width TBD
|
||||
* Resize the text area to given height and width in characters.
|
||||
* Omitted parameters reuse the current height or width.
|
||||
* Zero parameters use the display's height or width.
|
||||
* Ps = 9 ; 0 -> Restore maximized window. TBD
|
||||
* Ps = 9 ; 1 TBD
|
||||
* Maximize window (i.e., resize to screen size).
|
||||
* Ps = 9 ; 2 -> Maximize window vertically. TBD
|
||||
* Ps = 9 ; 3 -> Maximize window horizontally. TBD
|
||||
* Ps = 10 ; 0 -> Undo full-screen mode. TBD
|
||||
* Ps = 10 ; 1 -> Change to full-screen. TBD
|
||||
* Ps = 10 ; 2 -> Toggle full-screen. TBD
|
||||
* Ps = 11 not supported (always report non-iconified?)
|
||||
* Report xterm window state.
|
||||
* If the xterm window is non-iconified, it returns CSI 1 t .
|
||||
* If the xterm window is iconified, it returns CSI 2 t .
|
||||
* Ps = 13 -> Report xterm window position. TBD
|
||||
* Note: X Toolkit positions can be negative, but the reported
|
||||
* values are unsigned, in the range 0-65535. Negative values
|
||||
* correspond to 32768-65535.
|
||||
* Result is CSI 3 ; x ; y t
|
||||
* Ps = 13 ; 2 TBD
|
||||
* Report xterm text-area position. Result is CSI 3 ; x ; y t
|
||||
* Ps = 1 4 TBD
|
||||
* Report xterm text area size in pixels. Result is CSI 4 ; height ; width t
|
||||
* Ps = 14 ; 2 TBD
|
||||
* Report xterm window size in pixels.
|
||||
* Normally xterm's window is larger than its text area, since it
|
||||
* includes the frame (or decoration) applied by the window manager,
|
||||
* as well as the area used by a scroll-bar. Result is CSI 4 ; height ; width t
|
||||
* Ps = 15 TBD
|
||||
* Report size of the screen in pixels. Result is CSI 5 ; height ; width t
|
||||
* Ps = 16 TBD
|
||||
* Report xterm character cell size in pixels. Result is CSI 6 ; height ; width t
|
||||
* Ps = 18 TBD
|
||||
* Report the size of the text area in characters. Result is CSI 8 ; height ; width t
|
||||
* Ps = 19 TBD
|
||||
* Report the size of the screen in characters. Result is CSI 9 ; height ; width t
|
||||
* Ps = 20 TBD
|
||||
* Report xterm window's icon label. Result is OSC L label ST
|
||||
* Ps = 21 TBD
|
||||
* Report xterm window's title. Result is OSC l label ST
|
||||
* Ps = 22 ; 0 -> Save xterm icon and window title on stack. TBD
|
||||
* Ps = 22 ; 1 -> Save xterm icon title on stack. TBD
|
||||
* Ps = 22 ; 2 -> Save xterm window title on stack. TBD
|
||||
* Ps = 23 ; 0 -> Restore xterm icon and window title from stack. TBD
|
||||
* Ps = 23 ; 1 -> Restore xterm icon title from stack. TBD
|
||||
* Ps = 23 ; 2 -> Restore xterm window title from stack. TBD
|
||||
* Ps >= 24 TBD
|
||||
* Resize to Ps lines (DECSLPP), VT340 and VT420. xterm adapts this by resizing its window.
|
||||
*
|
||||
* Note: Only those listed below are supported. All others are left to integrators and
|
||||
* need special treatment based on the embedding environment.
|
||||
*
|
||||
* Ps = 1 4 supported
|
||||
* Report xterm text area size in pixels.
|
||||
* Result is CSI 4 ; height ; width t
|
||||
* Ps = 14 ; 2 not implemented
|
||||
* Ps = 16 supported
|
||||
* Report xterm character cell size in pixels.
|
||||
* Result is CSI 6 ; height ; width t
|
||||
* Ps = 18 supported
|
||||
* Report the size of the text area in characters.
|
||||
* Result is CSI 8 ; height ; width t
|
||||
* Ps = 20 supported
|
||||
* Report xterm window's icon label.
|
||||
* Result is OSC L label ST
|
||||
* Ps = 21 supported
|
||||
* Report xterm window's title.
|
||||
* Result is OSC l label ST
|
||||
* Ps = 22 ; 0 -> Save xterm icon and window title on stack. supported
|
||||
* Ps = 22 ; 1 -> Save xterm icon title on stack. supported
|
||||
* Ps = 22 ; 2 -> Save xterm window title on stack. supported
|
||||
* Ps = 23 ; 0 -> Restore xterm icon and window title from stack. supported
|
||||
* Ps = 23 ; 1 -> Restore xterm icon title from stack. supported
|
||||
* Ps = 23 ; 2 -> Restore xterm window title from stack. supported
|
||||
* Ps >= 24 not implemented
|
||||
*/
|
||||
public manipulateWindowOptions(params?: IParams): void {
|
||||
console.log(params);
|
||||
public windowOptions(params?: IParams): void {
|
||||
if (!~this._optionsService.options.allowedWindowOps.indexOf(params.params[0])) {
|
||||
return;
|
||||
}
|
||||
const second = (params.length > 1) ? params.params[1] : 0;
|
||||
const rs = (this._terminal as any)._renderService; // FIXME: import renderService to get rid of any type here
|
||||
switch (params.params[0]) {
|
||||
case 8:
|
||||
if (params.length === 3) {
|
||||
// TODO: 0/1 support
|
||||
const cols = Math.max(2, params.params[2]);
|
||||
const rows = Math.max(2, params.params[1]);
|
||||
// make it work with demo thus shamelessly taken from demo's client.ts
|
||||
const width = (cols * (this._terminal as any)._renderService.dimensions.actualCellWidth + this._terminal.viewport.scrollBarWidth).toString() + 'px';
|
||||
const height = (rows * (this._terminal as any)._renderService.dimensions.actualCellHeight).toString() + 'px';
|
||||
(this._terminal as any)._parent.style.width = width;
|
||||
(this._terminal as any)._parent.style.height = height;
|
||||
const term = Function('return this.term;')();
|
||||
// move fit into browser codebase?
|
||||
for (const addon of (term as any)._addonManager._addons) {
|
||||
if (`${addon.instance.constructor}`.indexOf('FitAddon') !== -1) {
|
||||
addon.instance.fit();
|
||||
}
|
||||
case 14: // GetWinSizePixels, returns CSI 4 ; height ; width t
|
||||
if (rs && second !== 2) {
|
||||
const w = rs.dimensions.canvasWidth.toFixed(0);
|
||||
const h = rs.dimensions.canvasHeight.toFixed(0);
|
||||
this._coreService.triggerDataEvent(`${C0.ESC}[4;${h};${w}t`);
|
||||
}
|
||||
break;
|
||||
case 16: // GetCellSizePixels, returns CSI 6 ; height ; width t
|
||||
if (rs) {
|
||||
const w = rs.dimensions.actualCellWidth.toFixed(0);
|
||||
const h = rs.dimensions.actualCellHeight.toFixed(0);
|
||||
this._coreService.triggerDataEvent(`${C0.ESC}[4;${h};${w}t`);
|
||||
}
|
||||
break;
|
||||
case 18: // GetWinSizeChars, returns CSI 8 ; height ; width t
|
||||
if (this._bufferService) {
|
||||
this._coreService.triggerDataEvent(`${C0.ESC}[8;${this._bufferService.rows};${this._bufferService.cols}t`);
|
||||
}
|
||||
break;
|
||||
case 20: // GetIconTitle, returns OSC L label ST
|
||||
this._coreService.triggerDataEvent(`${C0.ESC}]L${this._iconName}${C0.ESC}\\`);
|
||||
break;
|
||||
case 21: // GetWinTitle, returns OSC l label ST
|
||||
this._coreService.triggerDataEvent(`${C0.ESC}]l${this._windowTitle}${C0.ESC}\\`);
|
||||
break;
|
||||
case 22: // PushTitle
|
||||
if (!second || second === 2) {
|
||||
this._windowTitleStack.push(this._windowTitle);
|
||||
if (this._windowTitleStack.length > STACK_LIMIT) {
|
||||
this._windowTitleStack.shift();
|
||||
}
|
||||
}
|
||||
if (!second || second === 1) {
|
||||
this._iconNameStack.push(this._iconName);
|
||||
if (this._iconNameStack.length > STACK_LIMIT) {
|
||||
this._iconNameStack.shift();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 18:
|
||||
const height = this._bufferService.rows;
|
||||
const width = this._bufferService.cols;
|
||||
this._coreService.triggerDataEvent(`${C0.ESC}[8;${height};${width}t`);
|
||||
case 23: // PopTitle
|
||||
if (!second || second === 2) {
|
||||
if (this._windowTitleStack.length) {
|
||||
this.setTitle(this._windowTitleStack.pop());
|
||||
}
|
||||
}
|
||||
if (!second || second === 1) {
|
||||
if (this._iconNameStack.length) {
|
||||
this.setIconName(this._iconNameStack.pop());
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -2148,14 +2171,22 @@ export class InputHandler extends Disposable implements IInputHandler {
|
||||
|
||||
|
||||
/**
|
||||
* OSC 0; <data> ST (set icon name + window title)
|
||||
* OSC 2; <data> ST (set window title)
|
||||
* Proxy to set window title. Icon name is not supported.
|
||||
* Proxy to set window title.
|
||||
*/
|
||||
public setTitle(data: string): void {
|
||||
this._windowTitle = data;
|
||||
this._terminal.handleTitle(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* OSC 1; <data> ST
|
||||
* Note: Icon name is not exposed.
|
||||
*/
|
||||
public setIconName(data: string): void {
|
||||
this._iconName = data;
|
||||
}
|
||||
|
||||
/**
|
||||
* ESC E
|
||||
* C1.NEL
|
||||
|
||||
+3
-3
@@ -131,8 +131,6 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
|
||||
public sendFocus: boolean;
|
||||
|
||||
// misc
|
||||
public savedCols: number;
|
||||
|
||||
public curAttrData: IAttributeData;
|
||||
private _eraseAttrData: IAttributeData;
|
||||
|
||||
@@ -1463,7 +1461,9 @@ export class Terminal extends Disposable implements ITerminal, IDisposable, IInp
|
||||
|
||||
// Sync the scroll area to make sure scroll events don't fire and scroll the viewport to an
|
||||
// invalid location
|
||||
this.viewport.syncScrollArea(true);
|
||||
if (this.viewport) {
|
||||
this.viewport.syncScrollArea(true);
|
||||
}
|
||||
|
||||
this.refresh(0, this.rows - 1);
|
||||
this._onResize.fire({ cols: x, rows: y });
|
||||
|
||||
@@ -211,7 +211,6 @@ export class MockInputHandlingTerminal implements IInputHandlingTerminal {
|
||||
wraparoundMode: boolean;
|
||||
bracketedPasteMode: boolean;
|
||||
curAttrData = new AttributeData();
|
||||
savedCols: number;
|
||||
x10Mouse: boolean;
|
||||
vt200Mouse: boolean;
|
||||
normalMouse: boolean;
|
||||
|
||||
Vendored
-1
@@ -35,7 +35,6 @@ export interface IInputHandlingTerminal {
|
||||
wraparoundMode: boolean;
|
||||
bracketedPasteMode: boolean;
|
||||
curAttrData: IAttributeData;
|
||||
savedCols: number;
|
||||
mouseEvents: CoreMouseEventType;
|
||||
sendFocus: boolean;
|
||||
cursorHidden: boolean;
|
||||
|
||||
@@ -14,6 +14,34 @@ import { clone } from 'common/Clone';
|
||||
// made, apart from the conversion to base64.
|
||||
export const DEFAULT_BELL_SOUND = 'data:audio/mp3;base64,SUQzBAAAAAAAI1RTU0UAAAAPAAADTGF2ZjU4LjMyLjEwNAAAAAAAAAAAAAAA//tQxAADB8AhSmxhIIEVCSiJrDCQBTcu3UrAIwUdkRgQbFAZC1CQEwTJ9mjRvBA4UOLD8nKVOWfh+UlK3z/177OXrfOdKl7pyn3Xf//WreyTRUoAWgBgkOAGbZHBgG1OF6zM82DWbZaUmMBptgQhGjsyYqc9ae9XFz280948NMBWInljyzsNRFLPWdnZGWrddDsjK1unuSrVN9jJsK8KuQtQCtMBjCEtImISdNKJOopIpBFpNSMbIHCSRpRR5iakjTiyzLhchUUBwCgyKiweBv/7UsQbg8isVNoMPMjAAAA0gAAABEVFGmgqK////9bP/6XCykxBTUUzLjEwMKqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq';
|
||||
|
||||
// setting names controlled by allowedWindowOps
|
||||
// not supported: GetChecksum, GetSelection, SetChecksum, SetSelection, SetXprop
|
||||
export const WINDOW_OPTIONS: {[key: string]: number} = {
|
||||
'RestoreWin': 1,
|
||||
'MinimizeWin': 2,
|
||||
'SetWinPosition': 3,
|
||||
'SetWinSizePixels': 4,
|
||||
'RaiseWin': 5,
|
||||
'LowerWin': 6,
|
||||
'RefreshWin': 7,
|
||||
'SetWinSizeChars': 8,
|
||||
'MaximizeWin': 9,
|
||||
'FullscreenWin': 10,
|
||||
'GetWinState': 11,
|
||||
'GetWinPosition': 13,
|
||||
'GetWinSizePixels': 14,
|
||||
'GetScreenSizePixels': 15, // note: name not in xterm
|
||||
'GetCellSizePixels': 16, // note: name not in xterm
|
||||
'GetWinSizeChars': 18,
|
||||
'GetScreenSizeChars': 19,
|
||||
'GetIconTitle': 20,
|
||||
'GetWinTitle': 21,
|
||||
'PushTitle': 22,
|
||||
'PopTitle': 23,
|
||||
'SetWinLines': 24 // any param >= 24, also handles DECCOLM
|
||||
};
|
||||
|
||||
|
||||
// TODO: Freeze?
|
||||
export const DEFAULT_OPTIONS: ITerminalOptions = Object.freeze({
|
||||
cols: 80,
|
||||
@@ -50,7 +78,8 @@ export const DEFAULT_OPTIONS: ITerminalOptions = Object.freeze({
|
||||
screenKeys: false,
|
||||
cancelEvents: false,
|
||||
useFlowControl: false,
|
||||
wordSeparator: ' ()[]{}\',:;"'
|
||||
wordSeparator: ' ()[]{}\',:;"',
|
||||
allowedWindowOps: []
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -128,6 +157,20 @@ export class OptionsService implements IOptionsService {
|
||||
throw new Error(`${key} cannot be less than or equal to 0, value: ${value}`);
|
||||
}
|
||||
break;
|
||||
case 'allowedWindowOps':
|
||||
const values = (value as string).split(',');
|
||||
const cleaned: number[] = [];
|
||||
for (let i = 0; i < values.length; ++i) {
|
||||
const option = parseInt(values[i].trim()) || WINDOW_OPTIONS[values[i].trim()];
|
||||
if (!option || option < 0 || option > 24) {
|
||||
throw new Error(`unknown window option "${values[i]}"`);
|
||||
}
|
||||
if (!~cleaned.indexOf(option)) {
|
||||
cleaned.push(option);
|
||||
}
|
||||
}
|
||||
value = cleaned;
|
||||
break;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
@@ -136,6 +179,9 @@ export class OptionsService implements IOptionsService {
|
||||
if (!(key in DEFAULT_OPTIONS)) {
|
||||
throw new Error(`No option with key "${key}"`);
|
||||
}
|
||||
if (key === 'allowedWindowOps') {
|
||||
return this.options[key].join();
|
||||
}
|
||||
return this.options[key];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -201,6 +201,7 @@ export interface IPartialTerminalOptions {
|
||||
theme?: ITheme;
|
||||
windowsMode?: boolean;
|
||||
wordSeparator?: string;
|
||||
allowedWindowOps?: number[];
|
||||
}
|
||||
|
||||
export interface ITerminalOptions {
|
||||
@@ -240,6 +241,7 @@ export interface ITerminalOptions {
|
||||
screenKeys: boolean;
|
||||
termName: string;
|
||||
useFlowControl: boolean;
|
||||
allowedWindowOps: number[];
|
||||
}
|
||||
|
||||
export interface ITheme {
|
||||
|
||||
Reference in New Issue
Block a user