3999 lines
110 KiB
JavaScript
3999 lines
110 KiB
JavaScript
/**
|
|
* program.js - basic curses-like functionality for blessed.
|
|
* Copyright (c) 2013-2015, Christopher Jeffrey and contributors (MIT License).
|
|
* https://github.com/chjj/blessed
|
|
*/
|
|
|
|
/**
|
|
* Modules
|
|
*/
|
|
|
|
var EventEmitter = require('events').EventEmitter
|
|
, StringDecoder = require('string_decoder').StringDecoder
|
|
, util = require('util')
|
|
, fs = require('fs');
|
|
|
|
var Tput = require('./tput')
|
|
, colors = require('./colors')
|
|
, slice = Array.prototype.slice;
|
|
|
|
var nextTick = global.setImmediate || process.nextTick.bind(process);
|
|
|
|
/**
|
|
* Program
|
|
*/
|
|
|
|
function Program(options) {
|
|
var self = this;
|
|
|
|
if (!(this instanceof Program)) {
|
|
return new Program(options);
|
|
}
|
|
|
|
EventEmitter.call(this);
|
|
|
|
if (!options || options.__proto__ !== Object.prototype) {
|
|
options = {
|
|
input: arguments[0],
|
|
output: arguments[1]
|
|
};
|
|
}
|
|
|
|
this.options = options;
|
|
this.input = options.input || process.stdin;
|
|
this.output = options.output || process.stdout;
|
|
|
|
options.log = options.log || options.dump;
|
|
if (options.log) {
|
|
this._logger = fs.createWriteStream(options.log);
|
|
if (options.dump) this.setupDump();
|
|
}
|
|
|
|
this.zero = options.zero !== false;
|
|
this.useBuffer = options.buffer;
|
|
|
|
this.x = 0;
|
|
this.y = 0;
|
|
this.savedX = 0;
|
|
this.savedY = 0;
|
|
|
|
this.cols = this.output.columns || 1;
|
|
this.rows = this.output.rows || 1;
|
|
|
|
this.scrollTop = 0;
|
|
this.scrollBottom = this.rows - 1;
|
|
|
|
this.terminal = options.term
|
|
|| options.terminal
|
|
|| process.env.TERM
|
|
|| (process.platform === 'win32' ? 'windows-ansi' : 'xterm');
|
|
|
|
// OSX
|
|
this.isOSXTerm = process.env.TERM_PROGRAM === 'Apple_Terminal';
|
|
this.isiTerm2 = process.env.TERM_PROGRAM === 'iTerm.app'
|
|
|| !!process.env.ITERM_SESSION_ID;
|
|
|
|
// VTE
|
|
// NOTE: lxterminal does not provide an env variable to check for.
|
|
// NOTE: gnome-terminal and sakura use a later version of VTE
|
|
// which provides VTE_VERSION as well as supports SGR events.
|
|
this.isXFCE = /xfce/i.test(process.env.COLORTERM);
|
|
this.isTerminator = !!process.env.TERMINATOR_UUID;
|
|
this.isLXDE = false;
|
|
this.isVTE = !!process.env.VTE_VERSION
|
|
|| this.isXFCE
|
|
|| this.isTerminator
|
|
|| this.isLXDE;
|
|
|
|
// xterm and rxvt - not accurate
|
|
this.isRxvt = /rxvt/i.test(process.env.COLORTERM);
|
|
this.isXterm = false;
|
|
|
|
this._buf = '';
|
|
this._flush = this.flush.bind(this);
|
|
|
|
unshiftEvent(process, 'exit', function() {
|
|
// Ensure the buffer is flushed (it should
|
|
// always be at this point, but who knows).
|
|
self.flush();
|
|
// Ensure _exiting is set (could technically
|
|
// use process._exiting).
|
|
self._exiting = true;
|
|
});
|
|
|
|
if (!Program.global) {
|
|
Program.global = this;
|
|
}
|
|
|
|
if (options.tput !== false) {
|
|
this.setupTput();
|
|
}
|
|
|
|
this.listen();
|
|
}
|
|
|
|
Program.prototype.__proto__ = EventEmitter.prototype;
|
|
|
|
Program.prototype.log = function() {
|
|
return this._log('LOG', util.format.apply(util, arguments));
|
|
};
|
|
|
|
Program.prototype.debug = function() {
|
|
if (!this.options.debug) return;
|
|
return this._log('DEBUG', util.format.apply(util, arguments));
|
|
};
|
|
|
|
Program.prototype._log = function(pre, msg) {
|
|
if (!this._logger) return;
|
|
return this._logger.write(pre + ': ' + msg + '\n-\n');
|
|
};
|
|
|
|
Program.prototype.setupDump = function() {
|
|
var self = this
|
|
, write = this.output.write
|
|
, decoder = new StringDecoder('utf8');
|
|
|
|
function stringify(data) {
|
|
return caret(data
|
|
.replace(/\r/g, '\\r')
|
|
.replace(/\n/g, '\\n')
|
|
.replace(/\t/g, '\\t'))
|
|
.replace(/[^ -~]/g, function(ch) {
|
|
if (ch.charCodeAt(0) > 0xff) return ch;
|
|
ch = ch.charCodeAt(0).toString(16);
|
|
if (ch.length > 2) {
|
|
if (ch.length < 4) ch = '0' + ch;
|
|
return '\\u' + ch;
|
|
}
|
|
if (ch.length < 2) ch = '0' + ch;
|
|
return '\\x' + ch;
|
|
});
|
|
}
|
|
|
|
function caret(data) {
|
|
return data.replace(/[\0\x80\x1b-\x1f\x7f\x01-\x1a]/g, function(ch) {
|
|
switch (ch) {
|
|
case '\0':
|
|
case '\200':
|
|
ch = '@';
|
|
break;
|
|
case '\x1b':
|
|
ch = '[';
|
|
break;
|
|
case '\x1c':
|
|
ch = '\\';
|
|
break;
|
|
case '\x1d':
|
|
ch = ']';
|
|
break;
|
|
case '\x1e':
|
|
ch = '^';
|
|
break;
|
|
case '\x1f':
|
|
ch = '_';
|
|
break;
|
|
case '\x7f':
|
|
ch = '?';
|
|
break;
|
|
default:
|
|
ch = ch.charCodeAt(0);
|
|
// From ('A' - 64) to ('Z' - 64).
|
|
if (ch >= 1 && ch <= 26) {
|
|
ch = String.fromCharCode(ch + 64);
|
|
} else {
|
|
return String.fromCharCode(ch);
|
|
}
|
|
break;
|
|
}
|
|
return '^' + ch;
|
|
});
|
|
}
|
|
|
|
this.input.on('data', function(data) {
|
|
self._log('IN', stringify(decoder.write(data)));
|
|
});
|
|
|
|
this.output.write = function(data) {
|
|
self._log('OUT', stringify(data));
|
|
return write.apply(this, arguments);
|
|
};
|
|
};
|
|
|
|
Program.prototype.setupTput = function() {
|
|
if (this._tputSetup) return;
|
|
this._tputSetup = true;
|
|
|
|
var self = this
|
|
, options = this.options
|
|
, write = this.write.bind(this);
|
|
|
|
var tput = this.tput = new Tput({
|
|
term: this.terminal,
|
|
padding: options.padding,
|
|
extended: options.extended,
|
|
printf: options.printf,
|
|
termcap: options.termcap
|
|
});
|
|
|
|
this.put = function() {
|
|
var args = slice.call(arguments)
|
|
, cap = args.shift();
|
|
|
|
if (tput[cap]) {
|
|
return this._write(tput[cap].apply(tput, args));
|
|
}
|
|
};
|
|
|
|
Object.keys(tput).forEach(function(key) {
|
|
if (self[key] == null) {
|
|
self[key] = tput[key];
|
|
}
|
|
|
|
if (typeof tput[key] !== 'function') {
|
|
self.put[key] = tput[key];
|
|
return;
|
|
}
|
|
|
|
if (options.padding) {
|
|
self.put[key] = function() {
|
|
return tput._print(tput[key].apply(tput, arguments), write);
|
|
};
|
|
} else {
|
|
self.put[key] = function() {
|
|
return self._write(tput[key].apply(tput, arguments));
|
|
};
|
|
}
|
|
});
|
|
};
|
|
|
|
Program.prototype.has = function(name) {
|
|
return this.tput
|
|
? this.tput.has(name)
|
|
: false;
|
|
};
|
|
|
|
Program.prototype.term = function(is) {
|
|
return this.terminal.indexOf(is) === 0;
|
|
};
|
|
|
|
Program.prototype.listen = function() {
|
|
var keys = require('./keys')
|
|
, self = this;
|
|
|
|
if (!this.input.isTTY || !this.output.isTTY) {
|
|
throw new Error('Not a terminal.');
|
|
}
|
|
|
|
// unshiftEvent(process, 'exit', function() {
|
|
// if (self._originalTitle) {
|
|
// self.setTitle(self._originalTitle);
|
|
// }
|
|
// });
|
|
// this.setTitleModeFeature(3);
|
|
// this.manipulateWindow(21, function(err, data) {
|
|
// if (err) return;
|
|
// self._originalTitle = data.text;
|
|
// });
|
|
|
|
// Input
|
|
this.input.on('keypress', function(ch, key) {
|
|
key = key || { ch: ch };
|
|
|
|
if (key.name === 'undefined'
|
|
&& (key.code === '[M' || key.code === '[I' || key.code === '[O')) {
|
|
// A mouse sequence. The `keys` module doesn't understand these.
|
|
return;
|
|
}
|
|
|
|
if (key.name === 'undefined') {
|
|
// Not sure what this is, but we should probably ignore it.
|
|
return;
|
|
}
|
|
|
|
if (key.name === 'enter' && key.sequence === '\n') {
|
|
key.name = 'linefeed';
|
|
}
|
|
|
|
if (key.name === 'return' && key.sequence === '\r') {
|
|
self.input.emit('keypress', ch, merge({}, key, { name: 'enter' }));
|
|
}
|
|
|
|
var name = (key.ctrl ? 'C-' : '')
|
|
+ (key.meta ? 'M-' : '')
|
|
+ (key.shift && key.name ? 'S-' : '')
|
|
+ (key.name || ch);
|
|
|
|
key.full = name;
|
|
|
|
self.emit('keypress', ch, key);
|
|
self.emit('key ' + name, ch, key);
|
|
});
|
|
|
|
this.input.on('data', function(data) {
|
|
self.emit('data', data);
|
|
});
|
|
|
|
keys.emitKeypressEvents(this.input);
|
|
|
|
this.on('newListener', function fn(type) {
|
|
if (type === 'keypress' || type === 'mouse') {
|
|
self.removeListener('newListener', fn);
|
|
if (!self.input.isRaw) {
|
|
self.input.setRawMode(true);
|
|
self.input.resume();
|
|
}
|
|
}
|
|
});
|
|
|
|
this.on('newListener', function fn(type) {
|
|
if (type === 'mouse') {
|
|
self.removeListener('newListener', fn);
|
|
self.bindMouse();
|
|
}
|
|
});
|
|
|
|
// Output
|
|
function resize() {
|
|
self.cols = self.output.columns;
|
|
self.rows = self.output.rows;
|
|
self.emit('resize');
|
|
}
|
|
|
|
this.output.on('resize', function() {
|
|
if (!self.options.resizeTimeout) {
|
|
return resize();
|
|
}
|
|
if (self._resizeTimer) {
|
|
clearTimeout(self._resizeTimer);
|
|
delete self._resizeTimer;
|
|
}
|
|
var time = typeof self.options.resizeTimeout === 'number'
|
|
? self.options.resizeTimeout
|
|
: 300;
|
|
self._resizeTimer = setTimeout(resize, time);
|
|
});
|
|
};
|
|
|
|
Program.prototype.key = function(key, listener) {
|
|
if (typeof key === 'string') key = key.split(/\s*,\s*/);
|
|
key.forEach(function(key) {
|
|
return this.on('key ' + key, listener);
|
|
}, this);
|
|
};
|
|
|
|
Program.prototype.onceKey = function(key, listener) {
|
|
if (typeof key === 'string') key = key.split(/\s*,\s*/);
|
|
key.forEach(function(key) {
|
|
return this.once('key ' + key, listener);
|
|
}, this);
|
|
};
|
|
|
|
Program.prototype.unkey =
|
|
Program.prototype.removeKey = function(key, listener) {
|
|
if (typeof key === 'string') key = key.split(/\s*,\s*/);
|
|
key.forEach(function(key) {
|
|
return this.removeListener('key ' + key, listener);
|
|
}, this);
|
|
};
|
|
|
|
// XTerm mouse events
|
|
// http://invisible-island.net/xterm/ctlseqs/ctlseqs.html#Mouse%20Tracking
|
|
// To better understand these
|
|
// the xterm code is very helpful:
|
|
// Relevant files:
|
|
// button.c, charproc.c, misc.c
|
|
// Relevant functions in xterm/button.c:
|
|
// BtnCode, EmitButtonCode, EditorButton, SendMousePosition
|
|
// send a mouse event:
|
|
// regular/utf8: ^[[M Cb Cx Cy
|
|
// urxvt: ^[[ Cb ; Cx ; Cy M
|
|
// sgr: ^[[ Cb ; Cx ; Cy M/m
|
|
// vt300: ^[[ 24(1/3/5)~ [ Cx , Cy ] \r
|
|
// locator: CSI P e ; P b ; P r ; P c ; P p & w
|
|
// motion example of a left click:
|
|
// ^[[M 3<^[[M@4<^[[M@5<^[[M@6<^[[M@7<^[[M#7<
|
|
// mouseup, mousedown, mousewheel
|
|
// left click: ^[[M 3<^[[M#3<
|
|
// mousewheel up: ^[[M`3>
|
|
Program.prototype.bindMouse = function() {
|
|
if (this._boundMouse) return;
|
|
this._boundMouse = true;
|
|
|
|
var decoder = new StringDecoder('utf8')
|
|
, self = this;
|
|
|
|
this.on('data', function(data) {
|
|
var text = decoder.write(data);
|
|
if (!text) return;
|
|
self._bindMouse(text, data);
|
|
});
|
|
};
|
|
|
|
Program.prototype._bindMouse = function(s, buf) {
|
|
var self = this
|
|
, key
|
|
, parts;
|
|
|
|
key = {
|
|
name: undefined,
|
|
ctrl: false,
|
|
meta: false,
|
|
shift: false
|
|
};
|
|
|
|
if (Buffer.isBuffer(s)) {
|
|
if (s[0] > 127 && s[1] === undefined) {
|
|
s[0] -= 128;
|
|
s = '\x1b' + s.toString('utf-8');
|
|
} else {
|
|
s = s.toString('utf-8');
|
|
}
|
|
}
|
|
|
|
// if (this.8bit) {
|
|
// s = s.replace(/\233/g, '\x1b[');
|
|
// buf = new Buffer(s, 'utf8');
|
|
// }
|
|
|
|
// XTerm / X10 for buggy VTE
|
|
// VTE can only send unsigned chars and no unicode for coords. This limits
|
|
// them to 0xff. However, normally the x10 protocol does not allow a byte
|
|
// under 0x20, but since VTE can have the bytes overflow, we can consider
|
|
// bytes below 0x20 to be up to 0xff + 0x20. This gives a limit of 287. Since
|
|
// characters ranging from 223 to 248 confuse javascript's utf parser, we
|
|
// need to parse the raw binary. We can detect whether the terminal is using
|
|
// a bugged VTE version by examining the coordinates and seeing whether they
|
|
// are a value they would never otherwise be with a properly implemented x10
|
|
// protocol. This method of detecting VTE is only 99% reliable because we
|
|
// can't check if the coords are 0x00 (255) since that is a valid x10 coord
|
|
// technically.
|
|
var bx = s.charCodeAt(4);
|
|
var by = s.charCodeAt(5);
|
|
if (buf[0] === 0x1b && buf[1] === 0x5b && buf[2] === 0x4d
|
|
&& (this.isVTE
|
|
|| bx >= 65533 || by >= 65533
|
|
|| (bx > 0x00 && bx < 0x20)
|
|
|| (by > 0x00 && by < 0x20)
|
|
|| (buf[4] > 223 && buf[4] < 248 && buf.length === 6)
|
|
|| (buf[5] > 223 && buf[5] < 248 && buf.length === 6))) {
|
|
var b = buf[3]
|
|
, x = buf[4]
|
|
, y = buf[5];
|
|
|
|
// unsigned char overflow.
|
|
if (x < 0x20) x += 0xff;
|
|
if (y < 0x20) y += 0xff;
|
|
|
|
// Convert the coordinates into a
|
|
// properly formatted x10 utf8 sequence.
|
|
s = '\x1b[M'
|
|
+ String.fromCharCode(b)
|
|
+ String.fromCharCode(x)
|
|
+ String.fromCharCode(y);
|
|
}
|
|
|
|
// XTerm / X10
|
|
if (parts = /^\x1b\[M([\x00\u0020-\uffff]{3})/.exec(s)) {
|
|
var b = parts[1].charCodeAt(0)
|
|
, x = parts[1].charCodeAt(1)
|
|
, y = parts[1].charCodeAt(2)
|
|
, mod;
|
|
|
|
key.name = 'mouse';
|
|
key.type = 'X10';
|
|
|
|
key.raw = [b, x, y, parts[0]];
|
|
key.buf = buf;
|
|
key.x = x - 32;
|
|
key.y = y - 32;
|
|
|
|
if (this.zero) key.x--, key.y--;
|
|
|
|
if (x === 0) key.x = 255;
|
|
if (y === 0) key.y = 255;
|
|
|
|
mod = b >> 2;
|
|
key.shift = !!(mod & 1);
|
|
key.meta = !!((mod >> 1) & 1);
|
|
key.ctrl = !!((mod >> 2) & 1);
|
|
|
|
b -= 32;
|
|
|
|
if ((b >> 6) & 1) {
|
|
key.action = b & 1 ? 'wheeldown' : 'wheelup';
|
|
key.button = 'middle';
|
|
} else if (b === 3) {
|
|
// NOTE: x10 and urxvt have no way
|
|
// of telling which button mouseup used.
|
|
key.action = 'mouseup';
|
|
key.button = this._lastButton || 'unknown';
|
|
delete this._lastButton;
|
|
} else {
|
|
key.action = 'mousedown';
|
|
var button = b & 3;
|
|
key.button =
|
|
button === 0 ? 'left'
|
|
: button === 1 ? 'middle'
|
|
: button === 2 ? 'right'
|
|
: 'unknown';
|
|
this._lastButton = key.button;
|
|
}
|
|
|
|
// Probably a movement.
|
|
// The *newer* VTE gets mouse movements comepletely wrong.
|
|
// This presents a problem: older versions of VTE that get it right might
|
|
// be confused by the second conditional in the if statement.
|
|
// NOTE: Possibly just switch back to the if statement below.
|
|
// none, shift, ctrl, alt
|
|
// gnome: 32, 36, 48, 40
|
|
// xterm: 35, _, 51, _
|
|
// urxvt: 35, _, _, _
|
|
// if (key.action === 'mousedown' && key.button === 'unknown') {
|
|
if (b === 35 || b === 39 || b === 51 || b === 43
|
|
|| (this.isVTE && (b === 32 || b === 36 || b === 48 || b === 40))) {
|
|
delete key.button;
|
|
key.action = 'mousemove';
|
|
}
|
|
|
|
self.emit('mouse', key);
|
|
|
|
return;
|
|
}
|
|
|
|
// URxvt
|
|
if (parts = /^\x1b\[(\d+;\d+;\d+)M/.exec(s)) {
|
|
var params = parts[1].split(';')
|
|
, b = +params[0]
|
|
, x = +params[1]
|
|
, y = +params[2];
|
|
|
|
key.name = 'mouse';
|
|
key.type = 'urxvt';
|
|
|
|
key.raw = [b, x, y, parts[0]];
|
|
key.buf = buf;
|
|
key.x = x;
|
|
key.y = y;
|
|
|
|
if (this.zero) key.x--, key.y--;
|
|
|
|
mod = b >> 2;
|
|
key.shift = !!(mod & 1);
|
|
key.meta = !!((mod >> 1) & 1);
|
|
key.ctrl = !!((mod >> 2) & 1);
|
|
|
|
// XXX Bug in urxvt after wheelup/down on mousemove
|
|
// NOTE: This may be different than 128/129 depending
|
|
// on mod keys.
|
|
if (b === 128 || b === 129) {
|
|
b = 67;
|
|
}
|
|
|
|
b -= 32;
|
|
|
|
if ((b >> 6) & 1) {
|
|
key.action = b & 1 ? 'wheeldown' : 'wheelup';
|
|
key.button = 'middle';
|
|
} else if (b === 3) {
|
|
// NOTE: x10 and urxvt have no way
|
|
// of telling which button mouseup used.
|
|
key.action = 'mouseup';
|
|
key.button = this._lastButton || 'unknown';
|
|
delete this._lastButton;
|
|
} else {
|
|
key.action = 'mousedown';
|
|
var button = b & 3;
|
|
key.button =
|
|
button === 0 ? 'left'
|
|
: button === 1 ? 'middle'
|
|
: button === 2 ? 'right'
|
|
: 'unknown';
|
|
// NOTE: 0/32 = mousemove, 32/64 = mousemove with left down
|
|
// if ((b >> 1) === 32)
|
|
this._lastButton = key.button;
|
|
}
|
|
|
|
// Probably a movement.
|
|
// The *newer* VTE gets mouse movements comepletely wrong.
|
|
// This presents a problem: older versions of VTE that get it right might
|
|
// be confused by the second conditional in the if statement.
|
|
// NOTE: Possibly just switch back to the if statement below.
|
|
// none, shift, ctrl, alt
|
|
// urxvt: 35, _, _, _
|
|
// gnome: 32, 36, 48, 40
|
|
// if (key.action === 'mousedown' && key.button === 'unknown') {
|
|
if (b === 35 || b === 39 || b === 51 || b === 43
|
|
|| (this.isVTE && (b === 32 || b === 36 || b === 48 || b === 40))) {
|
|
delete key.button;
|
|
key.action = 'mousemove';
|
|
}
|
|
|
|
self.emit('mouse', key);
|
|
|
|
return;
|
|
}
|
|
|
|
// SGR
|
|
if (parts = /^\x1b\[<(\d+;\d+;\d+)([mM])/.exec(s)) {
|
|
var down = parts[2] === 'M'
|
|
, params = parts[1].split(';')
|
|
, b = +params[0]
|
|
, x = +params[1]
|
|
, y = +params[2];
|
|
|
|
key.name = 'mouse';
|
|
key.type = 'sgr';
|
|
|
|
key.raw = [b, x, y, parts[0]];
|
|
key.buf = buf;
|
|
key.x = x;
|
|
key.y = y;
|
|
|
|
if (this.zero) key.x--, key.y--;
|
|
|
|
mod = b >> 2;
|
|
key.shift = !!(mod & 1);
|
|
key.meta = !!((mod >> 1) & 1);
|
|
key.ctrl = !!((mod >> 2) & 1);
|
|
|
|
if ((b >> 6) & 1) {
|
|
key.action = b & 1 ? 'wheeldown' : 'wheelup';
|
|
key.button = 'middle';
|
|
} else {
|
|
key.action = down
|
|
? 'mousedown'
|
|
: 'mouseup';
|
|
var button = b & 3;
|
|
key.button =
|
|
button === 0 ? 'left'
|
|
: button === 1 ? 'middle'
|
|
: button === 2 ? 'right'
|
|
: 'unknown';
|
|
}
|
|
|
|
// Probably a movement.
|
|
// The *newer* VTE gets mouse movements comepletely wrong.
|
|
// This presents a problem: older versions of VTE that get it right might
|
|
// be confused by the second conditional in the if statement.
|
|
// NOTE: Possibly just switch back to the if statement below.
|
|
// none, shift, ctrl, alt
|
|
// xterm: 35, _, 51, _
|
|
// gnome: 32, 36, 48, 40
|
|
// if (key.action === 'mousedown' && key.button === 'unknown') {
|
|
if (b === 35 || b === 39 || b === 51 || b === 43
|
|
|| (this.isVTE && (b === 32 || b === 36 || b === 48 || b === 40))) {
|
|
delete key.button;
|
|
key.action = 'mousemove';
|
|
}
|
|
|
|
self.emit('mouse', key);
|
|
|
|
return;
|
|
}
|
|
|
|
// DEC
|
|
// The xterm mouse documentation says there is a
|
|
// `<` prefix, the DECRQLP says there is no prefix.
|
|
if (parts = /^\x1b\[<(\d+;\d+;\d+;\d+)&w/.exec(s)) {
|
|
var params = parts[1].split(';')
|
|
, b = +params[0]
|
|
, x = +params[1]
|
|
, y = +params[2]
|
|
, page = +params[3];
|
|
|
|
key.name = 'mouse';
|
|
key.type = 'dec';
|
|
|
|
key.raw = [b, x, y, parts[0]];
|
|
key.buf = buf;
|
|
key.x = x;
|
|
key.y = y;
|
|
|
|
if (this.zero) key.x--, key.y--;
|
|
|
|
key.action = b === 3
|
|
? 'mouseup'
|
|
: 'mousedown';
|
|
|
|
key.button =
|
|
b === 2 ? 'left'
|
|
: b === 4 ? 'middle'
|
|
: b === 6 ? 'right'
|
|
: 'unknown';
|
|
|
|
self.emit('mouse', key);
|
|
|
|
return;
|
|
}
|
|
|
|
// vt300
|
|
if (parts = /^\x1b\[24([0135])~\[(\d+),(\d+)\]\r/.exec(s)) {
|
|
var b = +parts[1]
|
|
, x = +parts[2]
|
|
, y = +parts[3];
|
|
|
|
key.name = 'mouse';
|
|
key.type = 'vt300';
|
|
|
|
key.raw = [b, x, y, parts[0]];
|
|
key.buf = buf;
|
|
key.x = x;
|
|
key.y = y;
|
|
|
|
if (this.zero) key.x--, key.y--;
|
|
|
|
key.action = 'mousedown';
|
|
key.button =
|
|
b === 1 ? 'left'
|
|
: b === 2 ? 'middle'
|
|
: b === 5 ? 'right'
|
|
: 'unknown';
|
|
|
|
self.emit('mouse', key);
|
|
|
|
return;
|
|
}
|
|
|
|
if (parts = /^\x1b\[(O|I)/.exec(s)) {
|
|
key.action = parts[1] === 'I'
|
|
? 'focus'
|
|
: 'blur';
|
|
|
|
self.emit('mouse', key);
|
|
self.emit(key.action);
|
|
|
|
return;
|
|
}
|
|
};
|
|
|
|
// gpm support for linux vc
|
|
Program.prototype.enableGpm = function() {
|
|
var self = this;
|
|
var gpmclient = require('./gpmclient')
|
|
|
|
if (this.gpm) return;
|
|
|
|
this.gpm = gpmclient();
|
|
|
|
this.gpm.on('btndown', function(btn, modifier, x, y) {
|
|
x--, y--;
|
|
|
|
var key = {
|
|
name: 'mouse',
|
|
type: 'GPM',
|
|
action: 'mousedown',
|
|
button: self.gpm.ButtonName(btn),
|
|
raw: [btn, modifier, x, y],
|
|
x: x,
|
|
y: y,
|
|
shift: self.gpm.hasShiftKey(modifier),
|
|
meta: self.gpm.hasMetaKey(modifier),
|
|
ctrl: self.gpm.hasCtrlKey(modifier)
|
|
};
|
|
|
|
self.emit('mouse', key);
|
|
});
|
|
|
|
this.gpm.on('btnup', function(btn, modifier, x, y) {
|
|
x--, y--;
|
|
|
|
var key = {
|
|
name: 'mouse',
|
|
type: 'GPM',
|
|
action: 'mouseup',
|
|
button: self.gpm.ButtonName(btn),
|
|
raw: [btn, modifier, x, y],
|
|
x: x,
|
|
y: y,
|
|
shift: self.gpm.hasShiftKey(modifier),
|
|
meta: self.gpm.hasMetaKey(modifier),
|
|
ctrl: self.gpm.hasCtrlKey(modifier)
|
|
};
|
|
|
|
self.emit('mouse', key);
|
|
});
|
|
|
|
this.gpm.on('move', function(btn, modifier, x, y) {
|
|
x--, y--;
|
|
|
|
var key = {
|
|
name: 'mouse',
|
|
type: 'GPM',
|
|
action: 'mousemove',
|
|
button: self.gpm.ButtonName(btn),
|
|
raw: [btn, modifier, x, y],
|
|
x: x,
|
|
y: y,
|
|
shift: self.gpm.hasShiftKey(modifier),
|
|
meta: self.gpm.hasMetaKey(modifier),
|
|
ctrl: self.gpm.hasCtrlKey(modifier)
|
|
};
|
|
|
|
self.emit('mouse', key);
|
|
});
|
|
|
|
this.gpm.on('drag', function(btn, modifier, x, y) {
|
|
x--, y--;
|
|
|
|
var key = {
|
|
name: 'mouse',
|
|
type: 'GPM',
|
|
action: 'mousemove',
|
|
button: self.gpm.ButtonName(btn),
|
|
raw: [btn, modifier, x, y],
|
|
x: x,
|
|
y: y,
|
|
shift: self.gpm.hasShiftKey(modifier),
|
|
meta: self.gpm.hasMetaKey(modifier),
|
|
ctrl: self.gpm.hasCtrlKey(modifier)
|
|
};
|
|
|
|
self.emit('mouse', key);
|
|
});
|
|
|
|
this.gpm.on('mousewheel', function(btn, modifier, x, y, dx, dy) {
|
|
var key = {
|
|
name: 'mouse',
|
|
type: 'GPM',
|
|
action: dy > 0 ? 'wheelup' : 'wheeldown',
|
|
button: self.gpm.ButtonName(btn),
|
|
raw: [btn, modifier, x, y, dx, dy],
|
|
x: x,
|
|
y: y,
|
|
shift: self.gpm.hasShiftKey(modifier),
|
|
meta: self.gpm.hasMetaKey(modifier),
|
|
ctrl: self.gpm.hasCtrlKey(modifier)
|
|
};
|
|
|
|
self.emit('mouse', key);
|
|
});
|
|
};
|
|
|
|
Program.prototype.disableGpm = function() {
|
|
if (this.gpm) {
|
|
this.gpm.stop();
|
|
delete this.gpm;
|
|
}
|
|
};
|
|
|
|
// All possible responses from the terminal
|
|
Program.prototype.bindResponse = function() {
|
|
if (this._boundResponse) return;
|
|
this._boundResponse = true;
|
|
|
|
var decoder = new StringDecoder('utf8')
|
|
, self = this;
|
|
|
|
this.on('data', function(data) {
|
|
data = decoder.write(data);
|
|
if (!data) return;
|
|
self._bindResponse(data);
|
|
});
|
|
};
|
|
|
|
Program.prototype._bindResponse = function(s) {
|
|
var self = this
|
|
, out = {}
|
|
, parts;
|
|
|
|
if (Buffer.isBuffer(s)) {
|
|
if (s[0] > 127 && s[1] === undefined) {
|
|
s[0] -= 128;
|
|
s = '\x1b' + s.toString('utf-8');
|
|
} else {
|
|
s = s.toString('utf-8');
|
|
}
|
|
}
|
|
|
|
// CSI P s c
|
|
// Send Device Attributes (Primary DA).
|
|
// CSI > P s c
|
|
// Send Device Attributes (Secondary DA).
|
|
if (parts = /^\x1b\[(\?|>)(\d*(?:;\d*)*)c/.exec(s)) {
|
|
parts = parts[2].split(';').map(function(ch) {
|
|
return +ch || 0;
|
|
});
|
|
|
|
out.event = 'device-attributes';
|
|
out.code = 'DA';
|
|
|
|
if (parts[1] === '?') {
|
|
out.type = 'primary-attribute';
|
|
// VT100-style params:
|
|
if (parts[0] === 1 && parts[2] === 2) {
|
|
out.term = 'vt100';
|
|
out.advancedVideo = true;
|
|
} else if (parts[0] === 1 && parts[2] === 0) {
|
|
out.term = 'vt101';
|
|
} else if (parts[0] === 6) {
|
|
out.term = 'vt102';
|
|
} else if (parts[0] === 60
|
|
&& parts[1] === 1 && parts[2] === 2
|
|
&& parts[3] === 6 && parts[4] === 8
|
|
&& parts[5] === 9 && parts[6] === 15) {
|
|
out.term = 'vt220';
|
|
} else {
|
|
// VT200-style params:
|
|
parts.forEach(function(attr) {
|
|
switch (attr) {
|
|
case 1:
|
|
out.cols132 = true;
|
|
break;
|
|
case 2:
|
|
out.printer = true;
|
|
break;
|
|
case 6:
|
|
out.selectiveErase = true;
|
|
break;
|
|
case 8:
|
|
out.userDefinedKeys = true;
|
|
break;
|
|
case 9:
|
|
out.nationalReplacementCharsets = true;
|
|
break;
|
|
case 15:
|
|
out.technicalCharacters = true;
|
|
break;
|
|
case 18:
|
|
out.userWindows = true;
|
|
break;
|
|
case 21:
|
|
out.horizontalScrolling = true;
|
|
break;
|
|
case 22:
|
|
out.ansiColor = true;
|
|
break;
|
|
case 29:
|
|
out.ansiTextLocator = true;
|
|
break;
|
|
}
|
|
});
|
|
}
|
|
} else {
|
|
out.type = 'secondary-attribute';
|
|
switch (parts[0]) {
|
|
case 0:
|
|
out.term = 'vt100';
|
|
break;
|
|
case 1:
|
|
out.term = 'vt220';
|
|
break;
|
|
case 2:
|
|
out.term = 'vt240';
|
|
break;
|
|
case 18:
|
|
out.term = 'vt330';
|
|
break;
|
|
case 19:
|
|
out.term = 'vt340';
|
|
break;
|
|
case 24:
|
|
out.term = 'vt320';
|
|
break;
|
|
case 41:
|
|
out.term = 'vt420';
|
|
break;
|
|
case 61:
|
|
out.term = 'vt510';
|
|
break;
|
|
case 64:
|
|
out.term = 'vt520';
|
|
break;
|
|
case 65:
|
|
out.term = 'vt525';
|
|
break;
|
|
}
|
|
out.firmwareVersion = parts[1];
|
|
out.romCartridgeRegistrationNumber = parts[2];
|
|
}
|
|
|
|
// LEGACY
|
|
out.deviceAttributes = out;
|
|
|
|
this.emit('response', out);
|
|
this.emit('response ' + out.event, out);
|
|
|
|
return;
|
|
}
|
|
|
|
// CSI Ps n Device Status Report (DSR).
|
|
// Ps = 5 -> Status Report. Result (``OK'') is
|
|
// CSI 0 n
|
|
// CSI ? Ps n
|
|
// Device Status Report (DSR, DEC-specific).
|
|
// Ps = 1 5 -> Report Printer status as CSI ? 1 0 n (ready).
|
|
// or CSI ? 1 1 n (not ready).
|
|
// Ps = 2 5 -> Report UDK status as CSI ? 2 0 n (unlocked)
|
|
// or CSI ? 2 1 n (locked).
|
|
// Ps = 2 6 -> Report Keyboard status as
|
|
// CSI ? 2 7 ; 1 ; 0 ; 0 n (North American).
|
|
// The last two parameters apply to VT400 & up, and denote key-
|
|
// board ready and LK01 respectively.
|
|
// Ps = 5 3 -> Report Locator status as
|
|
// CSI ? 5 3 n Locator available, if compiled-in, or
|
|
// CSI ? 5 0 n No Locator, if not.
|
|
if (parts = /^\x1b\[(\?)?(\d+)(?:;(\d+);(\d+);(\d+))?n/.exec(s)) {
|
|
out.event = 'device-status';
|
|
out.code = 'DSR';
|
|
|
|
if (!parts[1] && parts[2] === '0' && !parts[3]) {
|
|
out.type = 'device-status';
|
|
out.status = 'OK';
|
|
|
|
// LEGACY
|
|
out.deviceStatus = out.status;
|
|
|
|
this.emit('response', out);
|
|
this.emit('response ' + out.event, out);
|
|
|
|
return;
|
|
}
|
|
|
|
if (parts[1] && (parts[2] === '10' || parts[2] === '11') && !parts[3]) {
|
|
out.type = 'printer-status';
|
|
out.status = parts[2] === '10'
|
|
? 'ready'
|
|
: 'not ready';
|
|
|
|
// LEGACY
|
|
out.printerStatus = out.status;
|
|
|
|
this.emit('response', out);
|
|
this.emit('response ' + out.event, out);
|
|
|
|
return;
|
|
}
|
|
|
|
if (parts[1] && (parts[2] === '20' || parts[2] === '21') && !parts[3]) {
|
|
out.type = 'udk-status';
|
|
out.status = parts[2] === '20'
|
|
? 'unlocked'
|
|
: 'locked';
|
|
|
|
// LEGACY
|
|
out.UDKStatus = out.status;
|
|
|
|
this.emit('response', out);
|
|
this.emit('response ' + out.event, out);
|
|
|
|
return;
|
|
}
|
|
|
|
if (parts[1]
|
|
&& parts[2] === '27'
|
|
&& parts[3] === '1'
|
|
&& parts[4] === '0'
|
|
&& parts[5] === '0') {
|
|
out.type = 'keyboard-status';
|
|
out.status = 'OK';
|
|
|
|
// LEGACY
|
|
out.keyboardStatus = out.status;
|
|
|
|
this.emit('response', out);
|
|
this.emit('response ' + out.event, out);
|
|
|
|
return;
|
|
}
|
|
|
|
if (parts[1] && (parts[2] === '53' || parts[2] === '50') && !parts[3]) {
|
|
out.type = 'locator-status';
|
|
out.status = parts[2] === '53'
|
|
? 'available'
|
|
: 'unavailable';
|
|
|
|
// LEGACY
|
|
out.locator = out.status;
|
|
|
|
this.emit('response', out);
|
|
this.emit('response ' + out.event, out);
|
|
|
|
return;
|
|
}
|
|
|
|
out.type = 'error';
|
|
out.text = 'Unhandled: ' + JSON.stringify(parts);
|
|
|
|
// LEGACY
|
|
out.error = out.text;
|
|
|
|
this.emit('response', out);
|
|
this.emit('response ' + out.event, out);
|
|
|
|
return;
|
|
}
|
|
|
|
// CSI Ps n Device Status Report (DSR).
|
|
// Ps = 6 -> Report Cursor Position (CPR) [row;column].
|
|
// Result is
|
|
// CSI r ; c R
|
|
// CSI ? Ps n
|
|
// Device Status Report (DSR, DEC-specific).
|
|
// Ps = 6 -> Report Cursor Position (CPR) [row;column] as CSI
|
|
// ? r ; c R (assumes page is zero).
|
|
if (parts = /^\x1b\[(\?)?(\d+);(\d+)R/.exec(s)) {
|
|
out.event = 'device-status';
|
|
out.code = 'DSR';
|
|
out.type = 'cursor-status';
|
|
|
|
out.status = {
|
|
x: +parts[3],
|
|
y: +parts[2],
|
|
page: !parts[1] ? undefined : 0
|
|
};
|
|
|
|
out.x = out.status.x;
|
|
out.y = out.status.y;
|
|
out.page = out.status.page;
|
|
|
|
// LEGACY
|
|
out.cursor = out.status;
|
|
|
|
this.emit('response', out);
|
|
this.emit('response ' + out.event, out);
|
|
|
|
return;
|
|
}
|
|
|
|
// CSI Ps ; Ps ; Ps t
|
|
// Window manipulation (from dtterm, as well as extensions).
|
|
// These controls may be disabled using the allowWindowOps
|
|
// resource. Valid values for the first (and any additional
|
|
// parameters) are:
|
|
// Ps = 1 1 -> Report xterm window state. If the xterm window
|
|
// is open (non-iconified), it returns CSI 1 t . If the xterm
|
|
// window is iconified, it returns CSI 2 t .
|
|
// Ps = 1 3 -> Report xterm window position. Result is CSI 3
|
|
// ; x ; y t
|
|
// Ps = 1 4 -> Report xterm window in pixels. Result is CSI
|
|
// 4 ; height ; width t
|
|
// Ps = 1 8 -> Report the size of the text area in characters.
|
|
// Result is CSI 8 ; height ; width t
|
|
// Ps = 1 9 -> Report the size of the screen in characters.
|
|
// Result is CSI 9 ; height ; width t
|
|
if (parts = /^\x1b\[(\d+)(?:;(\d+);(\d+))?t/.exec(s)) {
|
|
out.event = 'window-manipulation';
|
|
out.code = '';
|
|
|
|
if ((parts[1] === '1' || parts[1] === '2') && !parts[2]) {
|
|
out.type = 'window-state';
|
|
out.state = parts[1] === '1'
|
|
? 'non-iconified'
|
|
: 'iconified'
|
|
|
|
// LEGACY
|
|
out.windowState = out.state;
|
|
|
|
this.emit('response', out);
|
|
this.emit('response ' + out.event, out);
|
|
|
|
return;
|
|
}
|
|
|
|
if (parts[1] === '3' && parts[2]) {
|
|
out.type = 'window-position';
|
|
|
|
out.position = {
|
|
x: +parts[2],
|
|
y: +parts[3]
|
|
};
|
|
out.x = out.position.x;
|
|
out.y = out.position.y;
|
|
|
|
// LEGACY
|
|
out.windowPosition = out.position;
|
|
|
|
this.emit('response', out);
|
|
this.emit('response ' + out.event, out);
|
|
|
|
return;
|
|
}
|
|
|
|
if (parts[1] === '4' && parts[2]) {
|
|
out.type = 'window-size-pixels';
|
|
out.size = {
|
|
height: +parts[2],
|
|
width: +parts[3]
|
|
};
|
|
out.height = out.size.height;
|
|
out.width = out.size.width;
|
|
|
|
// LEGACY
|
|
out.windowSizePixels = out.size;
|
|
|
|
this.emit('response', out);
|
|
this.emit('response ' + out.event, out);
|
|
|
|
return;
|
|
}
|
|
|
|
if (parts[1] === '8' && parts[2]) {
|
|
out.type = 'textarea-size';
|
|
out.size = {
|
|
height: +parts[2],
|
|
width: +parts[3]
|
|
};
|
|
out.height = out.size.height;
|
|
out.width = out.size.width;
|
|
|
|
// LEGACY
|
|
out.textAreaSizeCharacters = out.size;
|
|
|
|
this.emit('response', out);
|
|
this.emit('response ' + out.event, out);
|
|
|
|
return;
|
|
}
|
|
|
|
if (parts[1] === '9' && parts[2]) {
|
|
out.type = 'screen-size';
|
|
out.size = {
|
|
height: +parts[2],
|
|
width: +parts[3]
|
|
};
|
|
out.height = out.size.height;
|
|
out.width = out.size.width;
|
|
|
|
// LEGACY
|
|
out.screenSizeCharacters = out.size;
|
|
|
|
this.emit('response', out);
|
|
this.emit('response ' + out.event, out);
|
|
|
|
return;
|
|
}
|
|
|
|
out.type = 'error';
|
|
out.text = 'Unhandled: ' + JSON.stringify(parts);
|
|
|
|
// LEGACY
|
|
out.error = out.text;
|
|
|
|
this.emit('response', out);
|
|
this.emit('response ' + out.event, out);
|
|
|
|
return;
|
|
}
|
|
|
|
// CSI Ps ; Ps ; Ps t
|
|
// Window manipulation (from dtterm, as well as extensions).
|
|
// These controls may be disabled using the allowWindowOps
|
|
// resource. Valid values for the first (and any additional
|
|
// parameters) are:
|
|
// Ps = 2 0 -> Report xterm window's icon label. Result is
|
|
// OSC L label ST
|
|
// Ps = 2 1 -> Report xterm window's title. Result is OSC l
|
|
// label ST
|
|
if (parts = /^\x1b\](l|L)([^\x07\x1b]*)(?:\x07|\x1b\\)/.exec(s)) {
|
|
out.event = 'window-manipulation';
|
|
out.code = '';
|
|
|
|
if (parts[1] === 'L') {
|
|
out.type = 'window-icon-label';
|
|
out.text = parts[2];
|
|
|
|
// LEGACY
|
|
out.windowIconLabel = out.text;
|
|
|
|
this.emit('response', out);
|
|
this.emit('response ' + out.event, out);
|
|
|
|
return;
|
|
}
|
|
|
|
if (parts[1] === 'l') {
|
|
out.type = 'window-title';
|
|
out.text = parts[2];
|
|
|
|
// LEGACY
|
|
out.windowTitle = out.text;
|
|
|
|
this.emit('response', out);
|
|
this.emit('response ' + out.event, out);
|
|
|
|
return;
|
|
}
|
|
|
|
out.type = 'error';
|
|
out.text = 'Unhandled: ' + JSON.stringify(parts);
|
|
|
|
// LEGACY
|
|
out.error = out.text;
|
|
|
|
this.emit('response', out);
|
|
this.emit('response ' + out.event, out);
|
|
|
|
return;
|
|
}
|
|
|
|
// CSI Ps ' |
|
|
// Request Locator Position (DECRQLP).
|
|
// -> CSI Pe ; Pb ; Pr ; Pc ; Pp & w
|
|
// Parameters are [event;button;row;column;page].
|
|
// Valid values for the event:
|
|
// Pe = 0 -> locator unavailable - no other parameters sent.
|
|
// Pe = 1 -> request - xterm received a DECRQLP.
|
|
// Pe = 2 -> left button down.
|
|
// Pe = 3 -> left button up.
|
|
// Pe = 4 -> middle button down.
|
|
// Pe = 5 -> middle button up.
|
|
// Pe = 6 -> right button down.
|
|
// Pe = 7 -> right button up.
|
|
// Pe = 8 -> M4 button down.
|
|
// Pe = 9 -> M4 button up.
|
|
// Pe = 1 0 -> locator outside filter rectangle.
|
|
// ``button'' parameter is a bitmask indicating which buttons are
|
|
// pressed:
|
|
// Pb = 0 <- no buttons down.
|
|
// Pb & 1 <- right button down.
|
|
// Pb & 2 <- middle button down.
|
|
// Pb & 4 <- left button down.
|
|
// Pb & 8 <- M4 button down.
|
|
// ``row'' and ``column'' parameters are the coordinates of the
|
|
// locator position in the xterm window, encoded as ASCII deci-
|
|
// mal.
|
|
// The ``page'' parameter is not used by xterm, and will be omit-
|
|
// ted.
|
|
// NOTE:
|
|
// This is already implemented in the _bindMouse
|
|
// method, but it might make more sense here.
|
|
// The xterm mouse documentation says there is a
|
|
// `<` prefix, the DECRQLP says there is no prefix.
|
|
if (parts = /^\x1b\[(\d+(?:;\d+){4})&w/.exec(s)) {
|
|
parts = parts[1].split(';').map(function(ch) {
|
|
return +ch;
|
|
});
|
|
|
|
out.event = 'locator-position';
|
|
out.code = 'DECRQLP';
|
|
|
|
switch (parts[0]) {
|
|
case 0:
|
|
out.status = 'locator-unavailable';
|
|
break;
|
|
case 1:
|
|
out.status = 'request';
|
|
break;
|
|
case 2:
|
|
out.status = 'left-button-down';
|
|
break;
|
|
case 3:
|
|
out.status = 'left-button-up';
|
|
break;
|
|
case 4:
|
|
out.status = 'middle-button-down';
|
|
break;
|
|
case 5:
|
|
out.status = 'middle-button-up';
|
|
break;
|
|
case 6:
|
|
out.status = 'right-button-down';
|
|
break;
|
|
case 7:
|
|
out.status = 'right-button-up';
|
|
break;
|
|
case 8:
|
|
out.status = 'm4-button-down';
|
|
break;
|
|
case 9:
|
|
out.status = 'm4-button-up';
|
|
break;
|
|
case 10:
|
|
out.status = 'locator-outside';
|
|
break;
|
|
}
|
|
|
|
out.mask = parts[1];
|
|
out.row = parts[2];
|
|
out.col = parts[3];
|
|
out.page = parts[4];
|
|
|
|
// LEGACY
|
|
out.locatorPosition = out;
|
|
|
|
this.emit('response', out);
|
|
this.emit('response ' + out.event, out);
|
|
|
|
return;
|
|
}
|
|
|
|
// OSC Ps ; Pt BEL
|
|
// OSC Ps ; Pt ST
|
|
// Set Text Parameters
|
|
if (parts = /^\x1b\](\d+);([^\x07\x1b]+)(?:\x07|\x1b\\)/.exec(s)) {
|
|
out.event = 'text-params';
|
|
out.code = 'Set Text Parameters';
|
|
out.ps = +s[1];
|
|
out.pt = s[2];
|
|
this.emit('response', out);
|
|
this.emit('response ' + out.event, out);
|
|
}
|
|
};
|
|
|
|
Program.prototype.response = function(name, text, callback, noBypass) {
|
|
var self = this;
|
|
|
|
if (arguments.length === 2) {
|
|
callback = text;
|
|
text = name;
|
|
name = null;
|
|
}
|
|
|
|
if (!callback) {
|
|
callback = function() {};
|
|
}
|
|
|
|
this.bindResponse();
|
|
|
|
name = name
|
|
? 'response ' + name
|
|
: 'response';
|
|
|
|
var onresponse;
|
|
|
|
this.once(name, onresponse = function(event) {
|
|
if (timeout) clearTimeout(timeout);
|
|
if (event.type === 'error') {
|
|
return callback(new Error(event.event + ': ' + event.text));
|
|
}
|
|
return callback(null, event);
|
|
});
|
|
|
|
var timeout = setTimeout(function() {
|
|
self.removeListener(name, onresponse);
|
|
return callback(new Error('Timeout.'));
|
|
}, 2000);
|
|
|
|
return noBypass
|
|
? this._write(text)
|
|
: this._twrite(text);
|
|
};
|
|
|
|
Program.prototype._buffer = function(text) {
|
|
if (this._exiting) {
|
|
this.flush();
|
|
this.output.write(text);
|
|
return;
|
|
}
|
|
|
|
if (this._buf) {
|
|
this._buf += text;
|
|
return;
|
|
}
|
|
|
|
this._buf = text;
|
|
|
|
nextTick(this._flush);
|
|
};
|
|
|
|
Program.prototype.flush = function(text) {
|
|
if (!this._buf) return;
|
|
this.output.write(this._buf);
|
|
this._buf = '';
|
|
};
|
|
|
|
Program.prototype._write = function(text) {
|
|
if (this.ret) return text;
|
|
if (this.useBuffer) {
|
|
return this._buffer(text);
|
|
}
|
|
return this.output.write(text);
|
|
};
|
|
|
|
// Example: `DCS tmux; ESC Pt ST`
|
|
// Real: `DCS tmux; ESC Pt ESC \`
|
|
Program.prototype._twrite = function(data) {
|
|
if (process.env.TMUX) data = '\x1bPtmux;\x1b' + data + '\x1b\\';
|
|
// Should work but tmux doesn't implement ST correctly:
|
|
// if (process.env.TMUX) data = '\x1bPtmux;\x1b' + data + '\x07';
|
|
return this._write(data);
|
|
};
|
|
|
|
Program.prototype.echo =
|
|
Program.prototype.write = function(text, attr) {
|
|
return attr
|
|
? this._write(this.text(text, attr))
|
|
: this._write(text);
|
|
};
|
|
|
|
Program.prototype._ncoords = function() {
|
|
if (this.x < 0) this.x = 0;
|
|
else if (this.x >= this.cols) this.x = this.cols - 1;
|
|
if (this.y < 0) this.y = 0;
|
|
else if (this.y >= this.rows) this.y = this.rows - 1;
|
|
};
|
|
|
|
Program.prototype.setx = function(x) {
|
|
return this.cursorCharAbsolute(x);
|
|
// return this.charPosAbsolute(x);
|
|
};
|
|
|
|
Program.prototype.sety = function(y) {
|
|
return this.linePosAbsolute(y);
|
|
};
|
|
|
|
Program.prototype.move = function(x, y) {
|
|
return this.cursorPos(y, x);
|
|
};
|
|
|
|
// TODO: Fix cud and cuu calls.
|
|
Program.prototype.omove = function(x, y) {
|
|
if (!this.zero) {
|
|
x = (x || 1) - 1;
|
|
y = (y || 1) - 1;
|
|
} else {
|
|
x = x || 0;
|
|
y = y || 0;
|
|
}
|
|
if (y === this.y && x === this.x) {
|
|
return;
|
|
}
|
|
if (y === this.y) {
|
|
if (x > this.x) {
|
|
this.cuf(x - this.x);
|
|
} else if (x < this.x) {
|
|
this.cub(this.x - x);
|
|
}
|
|
} else if (x === this.x) {
|
|
if (y > this.y) {
|
|
this.cud(y - this.y);
|
|
} else if (y < this.y) {
|
|
this.cuu(this.y - y);
|
|
}
|
|
} else {
|
|
if (!this.zero) x++, y++;
|
|
this.cup(y, x);
|
|
}
|
|
};
|
|
|
|
Program.prototype.rsetx = function(x) {
|
|
// return this.HPositionRelative(x);
|
|
if (!x) return;
|
|
return x > 0
|
|
? this.forward(x)
|
|
: this.back(-x);
|
|
};
|
|
|
|
Program.prototype.rsety = function(y) {
|
|
// return this.VPositionRelative(y);
|
|
if (!y) return;
|
|
return y > 0
|
|
? this.up(y)
|
|
: this.down(-y);
|
|
};
|
|
|
|
Program.prototype.rmove = function(x, y) {
|
|
this.rsetx(x);
|
|
this.rsety(y);
|
|
};
|
|
|
|
Program.prototype.simpleInsert = function(ch, i, attr) {
|
|
return this._write(this.repeat(ch, i), attr);
|
|
};
|
|
|
|
Program.prototype.repeat = function(ch, i) {
|
|
if (!(i >= 0)) i = 0;
|
|
return Array(i + 1).join(ch);
|
|
};
|
|
|
|
Program.prototype.__defineGetter__('title', function() {
|
|
return this._title;
|
|
});
|
|
|
|
Program.prototype.__defineSetter__('title', function(title) {
|
|
this.setTitle(title);
|
|
return this._title;
|
|
});
|
|
|
|
// Specific to iTerm2, but I think it's really cool.
|
|
// Example:
|
|
// if (!screen.copyToClipboard(text)) {
|
|
// execClipboardProgram(text);
|
|
// }
|
|
Program.prototype.copyToClipboard = function(text) {
|
|
if (this.isiTerm2) {
|
|
this._twrite('\x1b]50;CopyToCliboard=' + text + '\x07');
|
|
return true;
|
|
}
|
|
return false;
|
|
};
|
|
|
|
// Only XTerm and iTerm2. If you know of any others, post them.
|
|
Program.prototype.cursorShape = function(shape, blink) {
|
|
if (this.isiTerm2) {
|
|
switch (shape) {
|
|
case 'block':
|
|
if (!blink) {
|
|
this._twrite('\x1b]50;CursorShape=0;BlinkingCursorEnabled=0\x07');
|
|
} else {
|
|
this._twrite('\x1b]50;CursorShape=0;BlinkingCursorEnabled=1\x07');
|
|
}
|
|
break;
|
|
case 'underline':
|
|
if (!blink) {
|
|
// this._twrite('\x1b]50;CursorShape=n;BlinkingCursorEnabled=0\x07');
|
|
} else {
|
|
// this._twrite('\x1b]50;CursorShape=n;BlinkingCursorEnabled=1\x07');
|
|
}
|
|
break;
|
|
case 'line':
|
|
if (!blink) {
|
|
this._twrite('\x1b]50;CursorShape=1;BlinkingCursorEnabled=0\x07');
|
|
} else {
|
|
this._twrite('\x1b]50;CursorShape=1;BlinkingCursorEnabled=1\x07');
|
|
}
|
|
break;
|
|
}
|
|
return true;
|
|
} else if (this.term('xterm') || this.term('screen')) {
|
|
switch (shape) {
|
|
case 'block':
|
|
if (!blink) {
|
|
this._twrite('\x1b[0 q');
|
|
} else {
|
|
this._twrite('\x1b[1 q');
|
|
}
|
|
break;
|
|
case 'underline':
|
|
if (!blink) {
|
|
this._twrite('\x1b[2 q');
|
|
} else {
|
|
this._twrite('\x1b[3 q');
|
|
}
|
|
break;
|
|
case 'line':
|
|
if (!blink) {
|
|
this._twrite('\x1b[4 q');
|
|
} else {
|
|
this._twrite('\x1b[5 q');
|
|
}
|
|
break;
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
};
|
|
|
|
Program.prototype.cursorColor = function(color) {
|
|
if (this.term('xterm') || this.term('rxvt') || this.term('screen')) {
|
|
this._twrite('\x1b]12;' + color + '\x07');
|
|
return true;
|
|
}
|
|
return false;
|
|
};
|
|
|
|
Program.prototype.cursorReset =
|
|
Program.prototype.resetCursor = function() {
|
|
if (this.term('xterm') || this.term('rxvt') || this.term('screen')) {
|
|
// XXX
|
|
// return this.resetColors();
|
|
this._twrite('\x1b[0 q');
|
|
this._twrite('\x1b]112\x07');
|
|
// urxvt doesnt support OSC 112
|
|
this._twrite('\x1b]12;white\x07');
|
|
return true;
|
|
}
|
|
return false;
|
|
};
|
|
|
|
Program.prototype.getTextParams = function(param, callback) {
|
|
return this.response('text-params', '\x1b]' + param + ';?\x07', function(err, data) {
|
|
if (err) return callback(err);
|
|
return callback(null, data.pt);
|
|
});
|
|
};
|
|
|
|
Program.prototype.getCursorColor = function(callback) {
|
|
return this.getTextParams(12, callback);
|
|
};
|
|
|
|
/**
|
|
* Normal
|
|
*/
|
|
|
|
//Program.prototype.pad =
|
|
Program.prototype.nul = function() {
|
|
//if (this.has('pad')) return this.put.pad();
|
|
return this._write('\200');
|
|
};
|
|
|
|
Program.prototype.bel =
|
|
Program.prototype.bell = function() {
|
|
if (this.has('bel')) return this.put.bel();
|
|
return this._write('\x07');
|
|
};
|
|
|
|
Program.prototype.vtab = function() {
|
|
this.y++;
|
|
this._ncoords();
|
|
return this._write('\x0b');
|
|
};
|
|
|
|
Program.prototype.ff =
|
|
Program.prototype.form = function() {
|
|
if (this.has('ff')) return this.put.ff();
|
|
return this._write('\x0c');
|
|
};
|
|
|
|
Program.prototype.kbs =
|
|
Program.prototype.backspace = function() {
|
|
this.x--;
|
|
this._ncoords();
|
|
if (this.has('kbs')) return this.put.kbs();
|
|
return this._write('\x08');
|
|
};
|
|
|
|
Program.prototype.ht =
|
|
Program.prototype.tab = function() {
|
|
this.x += 8;
|
|
this._ncoords();
|
|
if (this.has('ht')) return this.put.ht();
|
|
return this._write('\t');
|
|
};
|
|
|
|
Program.prototype.shiftOut = function() {
|
|
// if (this.has('S2')) return this.put.S2();
|
|
return this._write('\x0e');
|
|
};
|
|
|
|
Program.prototype.shiftIn = function() {
|
|
// if (this.has('S3')) return this.put.S3();
|
|
return this._write('\x0f');
|
|
};
|
|
|
|
Program.prototype.cr =
|
|
Program.prototype.return = function() {
|
|
this.x = 0;
|
|
if (this.has('cr')) return this.put.cr();
|
|
return this._write('\r');
|
|
};
|
|
|
|
Program.prototype.nel =
|
|
Program.prototype.newline =
|
|
Program.prototype.feed = function() {
|
|
if (this.tput && this.tput.bools.eat_newline_glitch && this.x >= this.cols) {
|
|
return;
|
|
}
|
|
this.x = 0;
|
|
this.y++;
|
|
this._ncoords();
|
|
if (this.has('nel')) return this.put.nel();
|
|
return this._write('\n');
|
|
};
|
|
|
|
/**
|
|
* Esc
|
|
*/
|
|
|
|
// ESC D Index (IND is 0x84).
|
|
Program.prototype.ind =
|
|
Program.prototype.index = function() {
|
|
this.y++;
|
|
this._ncoords();
|
|
if (this.tput) return this.put.ind();
|
|
return this._write('\x1bD');
|
|
};
|
|
|
|
// ESC M Reverse Index (RI is 0x8d).
|
|
Program.prototype.ri =
|
|
Program.prototype.reverse =
|
|
Program.prototype.reverseIndex = function() {
|
|
this.y--;
|
|
this._ncoords();
|
|
if (this.tput) return this.put.ri();
|
|
return this._write('\x1bM');
|
|
};
|
|
|
|
// ESC E Next Line (NEL is 0x85).
|
|
Program.prototype.nextLine = function() {
|
|
this.y++;
|
|
this.x = 0;
|
|
this._ncoords();
|
|
if (this.has('nel')) return this.put.nel();
|
|
return this._write('\x1bE');
|
|
};
|
|
|
|
// ESC c Full Reset (RIS).
|
|
Program.prototype.reset = function() {
|
|
this.x = this.y = 0;
|
|
if (this.has('rs1') || this.has('ris')) {
|
|
return this.has('rs1')
|
|
? this.put.rs1()
|
|
: this.put.ris();
|
|
}
|
|
return this._write('\x1bc');
|
|
};
|
|
|
|
// ESC H Tab Set (HTS is 0x88).
|
|
Program.prototype.tabSet = function() {
|
|
if (this.tput) return this.put.hts();
|
|
return this._write('\x1bH');
|
|
};
|
|
|
|
// ESC 7 Save Cursor (DECSC).
|
|
Program.prototype.sc =
|
|
Program.prototype.saveCursor = function(key) {
|
|
if (key) return this.lsaveCursor(key);
|
|
this.savedX = this.x || 0;
|
|
this.savedY = this.y || 0;
|
|
if (this.tput) return this.put.sc();
|
|
return this._write('\x1b7');
|
|
};
|
|
|
|
// ESC 8 Restore Cursor (DECRC).
|
|
Program.prototype.rc =
|
|
Program.prototype.restoreCursor = function(key, hide) {
|
|
if (key) return this.lrestoreCursor(key, hide);
|
|
this.x = this.savedX || 0;
|
|
this.y = this.savedY || 0;
|
|
if (this.tput) return this.put.rc();
|
|
return this._write('\x1b8');
|
|
};
|
|
|
|
// Save Cursor Locally
|
|
Program.prototype.lsaveCursor = function(key) {
|
|
var key = key || 'local';
|
|
this._saved = this._saved || {};
|
|
this._saved[key] = this._saved[key] || {};
|
|
this._saved[key].x = this.x;
|
|
this._saved[key].y = this.y;
|
|
this._saved[key].hidden = this.cursorHidden;
|
|
};
|
|
|
|
// Restore Cursor Locally
|
|
Program.prototype.lrestoreCursor = function(key, hide) {
|
|
var key = key || 'local', pos;
|
|
if (!this._saved || !this._saved[key]) return;
|
|
pos = this._saved[key];
|
|
//delete this._saved[key];
|
|
this.cup(pos.y, pos.x);
|
|
if (hide && pos.hidden !== this.cursorHidden) {
|
|
if (pos.hidden) {
|
|
this.hideCursor();
|
|
} else {
|
|
this.showCursor();
|
|
}
|
|
}
|
|
};
|
|
|
|
// ESC # 3 DEC line height/width
|
|
Program.prototype.lineHeight = function() {
|
|
return this._write('\x1b#');
|
|
};
|
|
|
|
// ESC (,),*,+,-,. Designate G0-G2 Character Set.
|
|
Program.prototype.charset = function(val, level) {
|
|
level = level || 0;
|
|
|
|
// See also:
|
|
// acs_chars / acsc / ac
|
|
// enter_alt_charset_mode / smacs / as
|
|
// exit_alt_charset_mode / rmacs / ae
|
|
// enter_pc_charset_mode / smpch / S2
|
|
// exit_pc_charset_mode / rmpch / S3
|
|
|
|
switch (level) {
|
|
case 0:
|
|
level = '(';
|
|
break;
|
|
case 1:
|
|
level = ')';
|
|
break;
|
|
case 2:
|
|
level = '*';
|
|
break;
|
|
case 3:
|
|
level = '+';
|
|
break;
|
|
}
|
|
|
|
var name = typeof val === 'string'
|
|
? val.toLowerCase()
|
|
: val;
|
|
|
|
switch (name) {
|
|
case 'acs':
|
|
case 'scld': // DEC Special Character and Line Drawing Set.
|
|
if (this.tput) return this.put.smacs();
|
|
val = '0';
|
|
break;
|
|
case 'uk': // UK
|
|
val = 'A';
|
|
break;
|
|
case 'us': // United States (USASCII).
|
|
case 'usascii':
|
|
case 'ascii':
|
|
if (this.tput) return this.put.rmacs();
|
|
val = 'B';
|
|
break;
|
|
case 'dutch': // Dutch
|
|
val = '4';
|
|
break;
|
|
case 'finnish': // Finnish
|
|
val = 'C';
|
|
val = '5';
|
|
break;
|
|
case 'french': // French
|
|
val = 'R';
|
|
break;
|
|
case 'frenchcanadian': // FrenchCanadian
|
|
val = 'Q';
|
|
break;
|
|
case 'german': // German
|
|
val = 'K';
|
|
break;
|
|
case 'italian': // Italian
|
|
val = 'Y';
|
|
break;
|
|
case 'norwegiandanish': // NorwegianDanish
|
|
val = 'E';
|
|
val = '6';
|
|
break;
|
|
case 'spanish': // Spanish
|
|
val = 'Z';
|
|
break;
|
|
case 'swedish': // Swedish
|
|
val = 'H';
|
|
val = '7';
|
|
break;
|
|
case 'swiss': // Swiss
|
|
val = '=';
|
|
break;
|
|
case 'isolatin': // ISOLatin (actually /A)
|
|
val = '/A';
|
|
break;
|
|
default: // Default
|
|
if (this.tput) return this.put.rmacs();
|
|
val = 'B';
|
|
break;
|
|
}
|
|
|
|
return this._write('\x1b(' + val);
|
|
};
|
|
|
|
Program.prototype.enter_alt_charset_mode =
|
|
Program.prototype.as =
|
|
Program.prototype.smacs = function() {
|
|
return this.charset('acs');
|
|
};
|
|
|
|
Program.prototype.exit_alt_charset_mode =
|
|
Program.prototype.ae =
|
|
Program.prototype.rmacs = function() {
|
|
return this.charset('ascii');
|
|
};
|
|
|
|
// ESC N
|
|
// Single Shift Select of G2 Character Set
|
|
// ( SS2 is 0x8e). This affects next character only.
|
|
// ESC O
|
|
// Single Shift Select of G3 Character Set
|
|
// ( SS3 is 0x8f). This affects next character only.
|
|
// ESC n
|
|
// Invoke the G2 Character Set as GL (LS2).
|
|
// ESC o
|
|
// Invoke the G3 Character Set as GL (LS3).
|
|
// ESC |
|
|
// Invoke the G3 Character Set as GR (LS3R).
|
|
// ESC }
|
|
// Invoke the G2 Character Set as GR (LS2R).
|
|
// ESC ~
|
|
// Invoke the G1 Character Set as GR (LS1R).
|
|
Program.prototype.setG = function(val) {
|
|
// if (this.tput) return this.put.S2();
|
|
// if (this.tput) return this.put.S3();
|
|
switch (val) {
|
|
case 1:
|
|
val = '~'; // GR
|
|
break;
|
|
case 2:
|
|
val = 'n'; // GL
|
|
val = '}'; // GR
|
|
val = 'N'; // Next Char Only
|
|
break;
|
|
case 3:
|
|
val = 'o'; // GL
|
|
val = '|'; // GR
|
|
val = 'O'; // Next Char Only
|
|
break;
|
|
}
|
|
return this._write('\x1b' + val);
|
|
};
|
|
|
|
/**
|
|
* OSC
|
|
*/
|
|
|
|
// OSC Ps ; Pt ST
|
|
// OSC Ps ; Pt BEL
|
|
// Set Text Parameters.
|
|
Program.prototype.setTitle = function(title) {
|
|
this._title = title;
|
|
|
|
// if (this.term('screen')) {
|
|
// // Tmux pane
|
|
// // if (process.env.TMUX) {
|
|
// // return this._write('\x1b]2;' + title + '\x1b\\');
|
|
// // }
|
|
// return this._write('\x1bk' + title + '\x1b\\');
|
|
// }
|
|
|
|
return this._twrite('\x1b]0;' + title + '\x07');
|
|
};
|
|
|
|
// OSC Ps ; Pt ST
|
|
// OSC Ps ; Pt BEL
|
|
// Reset colors
|
|
Program.prototype.resetColors = function(param) {
|
|
if (this.has('Cr')) {
|
|
return this.put.Cr(param);
|
|
}
|
|
return this._twrite('\x1b]112\x07');
|
|
//return this._twrite('\x1b]112;' + param + '\x07');
|
|
};
|
|
|
|
// OSC Ps ; Pt ST
|
|
// OSC Ps ; Pt BEL
|
|
// Change dynamic colors
|
|
Program.prototype.dynamicColors = function(param) {
|
|
if (this.has('Cs')) {
|
|
return this.put.Cs(param);
|
|
}
|
|
return this._twrite('\x1b]12;' + param + '\x07');
|
|
};
|
|
|
|
// OSC Ps ; Pt ST
|
|
// OSC Ps ; Pt BEL
|
|
// Sel data
|
|
Program.prototype.selData = function(a, b) {
|
|
if (this.has('Ms')) {
|
|
return this.put.Ms(a, b);
|
|
}
|
|
return this._twrite('\x1b]52;' + a + ';' + b + '\x07');
|
|
};
|
|
|
|
/**
|
|
* CSI
|
|
*/
|
|
|
|
// CSI Ps A
|
|
// Cursor Up Ps Times (default = 1) (CUU).
|
|
Program.prototype.cuu =
|
|
Program.prototype.up =
|
|
Program.prototype.cursorUp = function(param) {
|
|
this.y -= param || 1;
|
|
this._ncoords();
|
|
if (this.tput) return this.put.cuu(param);
|
|
return this._write('\x1b[' + (param || '') + 'A');
|
|
};
|
|
|
|
// CSI Ps B
|
|
// Cursor Down Ps Times (default = 1) (CUD).
|
|
Program.prototype.cud =
|
|
Program.prototype.down =
|
|
Program.prototype.cursorDown = function(param) {
|
|
this.y += param || 1;
|
|
this._ncoords();
|
|
if (this.tput) return this.put.cud(param);
|
|
return this._write('\x1b[' + (param || '') + 'B');
|
|
};
|
|
|
|
// CSI Ps C
|
|
// Cursor Forward Ps Times (default = 1) (CUF).
|
|
Program.prototype.cuf =
|
|
Program.prototype.right =
|
|
Program.prototype.forward =
|
|
Program.prototype.cursorForward = function(param) {
|
|
this.x += param || 1;
|
|
this._ncoords();
|
|
if (this.tput) return this.put.cuf(param);
|
|
return this._write('\x1b[' + (param || '') + 'C');
|
|
};
|
|
|
|
// CSI Ps D
|
|
// Cursor Backward Ps Times (default = 1) (CUB).
|
|
Program.prototype.cub =
|
|
Program.prototype.left =
|
|
Program.prototype.back =
|
|
Program.prototype.cursorBackward = function(param) {
|
|
this.x -= param || 1;
|
|
this._ncoords();
|
|
if (this.tput) return this.put.cub(param);
|
|
return this._write('\x1b[' + (param || '') + 'D');
|
|
};
|
|
|
|
// CSI Ps ; Ps H
|
|
// Cursor Position [row;column] (default = [1,1]) (CUP).
|
|
Program.prototype.cup =
|
|
Program.prototype.pos =
|
|
Program.prototype.cursorPos = function(row, col) {
|
|
if (!this.zero) {
|
|
row = (row || 1) - 1;
|
|
col = (col || 1) - 1;
|
|
} else {
|
|
row = row || 0;
|
|
col = col || 0;
|
|
}
|
|
this.x = col;
|
|
this.y = row;
|
|
this._ncoords();
|
|
if (this.tput) return this.put.cup(row, col);
|
|
return this._write('\x1b[' + (row + 1) + ';' + (col + 1) + 'H');
|
|
};
|
|
|
|
// CSI Ps J Erase in Display (ED).
|
|
// Ps = 0 -> Erase Below (default).
|
|
// Ps = 1 -> Erase Above.
|
|
// Ps = 2 -> Erase All.
|
|
// Ps = 3 -> Erase Saved Lines (xterm).
|
|
// CSI ? Ps J
|
|
// Erase in Display (DECSED).
|
|
// Ps = 0 -> Selective Erase Below (default).
|
|
// Ps = 1 -> Selective Erase Above.
|
|
// Ps = 2 -> Selective Erase All.
|
|
Program.prototype.ed =
|
|
Program.prototype.eraseInDisplay = function(param) {
|
|
if (this.tput) {
|
|
switch (param) {
|
|
case 'above':
|
|
param = 1;
|
|
break;
|
|
case 'all':
|
|
param = 2;
|
|
break;
|
|
case 'saved':
|
|
param = 3;
|
|
break;
|
|
case 'below':
|
|
default:
|
|
param = 0;
|
|
break;
|
|
}
|
|
// extended tput.E3 = ^[[3;J
|
|
return this.put.ed(param);
|
|
}
|
|
switch (param) {
|
|
case 'above':
|
|
return this._write('\X1b[1J');
|
|
case 'all':
|
|
return this._write('\x1b[2J');
|
|
case 'saved':
|
|
return this._write('\x1b[3J');
|
|
case 'below':
|
|
default:
|
|
return this._write('\x1b[J');
|
|
}
|
|
};
|
|
|
|
Program.prototype.clear = function() {
|
|
this.x = 0;
|
|
this.y = 0;
|
|
if (this.tput) return this.put.clear();
|
|
return this._write('\x1b[H\x1b[J');
|
|
};
|
|
|
|
// CSI Ps K Erase in Line (EL).
|
|
// Ps = 0 -> Erase to Right (default).
|
|
// Ps = 1 -> Erase to Left.
|
|
// Ps = 2 -> Erase All.
|
|
// CSI ? Ps K
|
|
// Erase in Line (DECSEL).
|
|
// Ps = 0 -> Selective Erase to Right (default).
|
|
// Ps = 1 -> Selective Erase to Left.
|
|
// Ps = 2 -> Selective Erase All.
|
|
Program.prototype.el =
|
|
Program.prototype.eraseInLine = function(param) {
|
|
if (this.tput) {
|
|
//if (this.tput.back_color_erase) ...
|
|
switch (param) {
|
|
case 'left':
|
|
param = 1;
|
|
break;
|
|
case 'all':
|
|
param = 2;
|
|
break;
|
|
case 'right':
|
|
default:
|
|
param = 0;
|
|
break;
|
|
}
|
|
return this.put.el(param);
|
|
}
|
|
switch (param) {
|
|
case 'left':
|
|
return this._write('\x1b[1K');
|
|
case 'all':
|
|
return this._write('\x1b[2K');
|
|
case 'right':
|
|
default:
|
|
return this._write('\x1b[K');
|
|
}
|
|
};
|
|
|
|
// CSI Pm m Character Attributes (SGR).
|
|
// Ps = 0 -> Normal (default).
|
|
// Ps = 1 -> Bold.
|
|
// Ps = 4 -> Underlined.
|
|
// Ps = 5 -> Blink (appears as Bold).
|
|
// Ps = 7 -> Inverse.
|
|
// Ps = 8 -> Invisible, i.e., hidden (VT300).
|
|
// Ps = 2 2 -> Normal (neither bold nor faint).
|
|
// Ps = 2 4 -> Not underlined.
|
|
// Ps = 2 5 -> Steady (not blinking).
|
|
// Ps = 2 7 -> Positive (not inverse).
|
|
// Ps = 2 8 -> Visible, i.e., not hidden (VT300).
|
|
// Ps = 3 0 -> Set foreground color to Black.
|
|
// Ps = 3 1 -> Set foreground color to Red.
|
|
// Ps = 3 2 -> Set foreground color to Green.
|
|
// Ps = 3 3 -> Set foreground color to Yellow.
|
|
// Ps = 3 4 -> Set foreground color to Blue.
|
|
// Ps = 3 5 -> Set foreground color to Magenta.
|
|
// Ps = 3 6 -> Set foreground color to Cyan.
|
|
// Ps = 3 7 -> Set foreground color to White.
|
|
// Ps = 3 9 -> Set foreground color to default (original).
|
|
// Ps = 4 0 -> Set background color to Black.
|
|
// Ps = 4 1 -> Set background color to Red.
|
|
// Ps = 4 2 -> Set background color to Green.
|
|
// Ps = 4 3 -> Set background color to Yellow.
|
|
// Ps = 4 4 -> Set background color to Blue.
|
|
// Ps = 4 5 -> Set background color to Magenta.
|
|
// Ps = 4 6 -> Set background color to Cyan.
|
|
// Ps = 4 7 -> Set background color to White.
|
|
// Ps = 4 9 -> Set background color to default (original).
|
|
|
|
// If 16-color support is compiled, the following apply. Assume
|
|
// that xterm's resources are set so that the ISO color codes are
|
|
// the first 8 of a set of 16. Then the aixterm colors are the
|
|
// bright versions of the ISO colors:
|
|
// Ps = 9 0 -> Set foreground color to Black.
|
|
// Ps = 9 1 -> Set foreground color to Red.
|
|
// Ps = 9 2 -> Set foreground color to Green.
|
|
// Ps = 9 3 -> Set foreground color to Yellow.
|
|
// Ps = 9 4 -> Set foreground color to Blue.
|
|
// Ps = 9 5 -> Set foreground color to Magenta.
|
|
// Ps = 9 6 -> Set foreground color to Cyan.
|
|
// Ps = 9 7 -> Set foreground color to White.
|
|
// Ps = 1 0 0 -> Set background color to Black.
|
|
// Ps = 1 0 1 -> Set background color to Red.
|
|
// Ps = 1 0 2 -> Set background color to Green.
|
|
// Ps = 1 0 3 -> Set background color to Yellow.
|
|
// Ps = 1 0 4 -> Set background color to Blue.
|
|
// Ps = 1 0 5 -> Set background color to Magenta.
|
|
// Ps = 1 0 6 -> Set background color to Cyan.
|
|
// Ps = 1 0 7 -> Set background color to White.
|
|
|
|
// If xterm is compiled with the 16-color support disabled, it
|
|
// supports the following, from rxvt:
|
|
// Ps = 1 0 0 -> Set foreground and background color to
|
|
// default.
|
|
|
|
// If 88- or 256-color support is compiled, the following apply.
|
|
// Ps = 3 8 ; 5 ; Ps -> Set foreground color to the second
|
|
// Ps.
|
|
// Ps = 4 8 ; 5 ; Ps -> Set background color to the second
|
|
// Ps.
|
|
Program.prototype.sgr =
|
|
Program.prototype.attr =
|
|
Program.prototype.charAttributes = function(param, val) {
|
|
return this._write(this._attr(param, val));
|
|
};
|
|
|
|
Program.prototype.text = function(text, attr) {
|
|
return this._attr(attr, true) + text + this._attr(attr, false);
|
|
};
|
|
|
|
// NOTE: sun-color may not allow multiple params for SGR.
|
|
Program.prototype._attr = function(param, val) {
|
|
var self = this
|
|
, param
|
|
, parts
|
|
, color
|
|
, m;
|
|
|
|
if (Array.isArray(param)) {
|
|
parts = param;
|
|
param = parts[0] || 'normal';
|
|
} else {
|
|
param = param || 'normal';
|
|
parts = param.split(/\s*[,;]\s*/);
|
|
}
|
|
|
|
if (parts.length > 1) {
|
|
var used = {}
|
|
, out = [];
|
|
|
|
parts.forEach(function(part) {
|
|
part = self._attr(part, val).slice(2, -1);
|
|
if (part === '') return;
|
|
if (used[part]) return;
|
|
used[part] = true;
|
|
out.push(part);
|
|
});
|
|
|
|
return '\x1b[' + out.join(';') + 'm';
|
|
}
|
|
|
|
if (param.indexOf('no ') === 0) {
|
|
param = param.substring(3);
|
|
val = false;
|
|
} else if (param.indexOf('!') === 0) {
|
|
param = param.substring(1);
|
|
val = false;
|
|
}
|
|
|
|
switch (param) {
|
|
// attributes
|
|
case 'normal':
|
|
case 'default':
|
|
if (val === false) return '';
|
|
return '\x1b[m';
|
|
case 'bold':
|
|
return val === false
|
|
? '\x1b[22m'
|
|
: '\x1b[1m';
|
|
case 'ul':
|
|
case 'underline':
|
|
case 'underlined':
|
|
return val === false
|
|
? '\x1b[24m'
|
|
: '\x1b[4m';
|
|
case 'blink':
|
|
return val === false
|
|
? '\x1b[25m'
|
|
: '\x1b[5m';
|
|
case 'inverse':
|
|
return val === false
|
|
? '\x1b[27m'
|
|
: '\x1b[7m';
|
|
break;
|
|
case 'invisible':
|
|
return val === false
|
|
? '\x1b[28m'
|
|
: '\x1b[8m';
|
|
|
|
// 8-color foreground
|
|
case 'black fg':
|
|
return val === false
|
|
? '\x1b[39m'
|
|
: '\x1b[30m';
|
|
case 'red fg':
|
|
return val === false
|
|
? '\x1b[39m'
|
|
: '\x1b[31m';
|
|
case 'green fg':
|
|
return val === false
|
|
? '\x1b[39m'
|
|
: '\x1b[32m';
|
|
case 'yellow fg':
|
|
return val === false
|
|
? '\x1b[39m'
|
|
: '\x1b[33m';
|
|
case 'blue fg':
|
|
return val === false
|
|
? '\x1b[39m'
|
|
: '\x1b[34m';
|
|
case 'magenta fg':
|
|
return val === false
|
|
? '\x1b[39m'
|
|
: '\x1b[35m';
|
|
case 'cyan fg':
|
|
return val === false
|
|
? '\x1b[39m'
|
|
: '\x1b[36m';
|
|
case 'white fg':
|
|
return val === false
|
|
? '\x1b[39m'
|
|
: '\x1b[37m';
|
|
case 'default fg':
|
|
if (val === false) return '';
|
|
return '\x1b[39m';
|
|
|
|
// 8-color background
|
|
case 'black bg':
|
|
return val === false
|
|
? '\x1b[49m'
|
|
: '\x1b[40m';
|
|
case 'red bg':
|
|
return val === false
|
|
? '\x1b[49m'
|
|
: '\x1b[41m';
|
|
case 'green bg':
|
|
return val === false
|
|
? '\x1b[49m'
|
|
: '\x1b[42m';
|
|
case 'yellow bg':
|
|
return val === false
|
|
? '\x1b[49m'
|
|
: '\x1b[43m';
|
|
case 'blue bg':
|
|
return val === false
|
|
? '\x1b[49m'
|
|
: '\x1b[44m';
|
|
case 'magenta bg':
|
|
return val === false
|
|
? '\x1b[49m'
|
|
: '\x1b[45m';
|
|
case 'cyan bg':
|
|
return val === false
|
|
? '\x1b[49m'
|
|
: '\x1b[46m';
|
|
case 'white bg':
|
|
return val === false
|
|
? '\x1b[49m'
|
|
: '\x1b[47m';
|
|
case 'default bg':
|
|
if (val === false) return '';
|
|
return '\x1b[49m';
|
|
|
|
// 16-color foreground
|
|
case 'light black fg':
|
|
return val === false
|
|
? '\x1b[39m'
|
|
: '\x1b[90m';
|
|
case 'light red fg':
|
|
return val === false
|
|
? '\x1b[39m'
|
|
: '\x1b[91m';
|
|
case 'light green fg':
|
|
return val === false
|
|
? '\x1b[39m'
|
|
: '\x1b[92m';
|
|
case 'light yellow fg':
|
|
return val === false
|
|
? '\x1b[39m'
|
|
: '\x1b[93m';
|
|
case 'light blue fg':
|
|
return val === false
|
|
? '\x1b[39m'
|
|
: '\x1b[94m';
|
|
case 'light magenta fg':
|
|
return val === false
|
|
? '\x1b[39m'
|
|
: '\x1b[95m';
|
|
case 'light cyan fg':
|
|
return val === false
|
|
? '\x1b[39m'
|
|
: '\x1b[96m';
|
|
case 'light white fg':
|
|
return val === false
|
|
? '\x1b[39m'
|
|
: '\x1b[97m';
|
|
|
|
// 16-color background
|
|
case 'light black bg':
|
|
return val === false
|
|
? '\x1b[49m'
|
|
: '\x1b[100m';
|
|
case 'light red bg':
|
|
return val === false
|
|
? '\x1b[49m'
|
|
: '\x1b[101m';
|
|
case 'light green bg':
|
|
return val === false
|
|
? '\x1b[49m'
|
|
: '\x1b[102m';
|
|
case 'light yellow bg':
|
|
return val === false
|
|
? '\x1b[49m'
|
|
: '\x1b[103m';
|
|
case 'light blue bg':
|
|
return val === false
|
|
? '\x1b[49m'
|
|
: '\x1b[104m';
|
|
case 'light magenta bg':
|
|
return val === false
|
|
? '\x1b[49m'
|
|
: '\x1b[105m';
|
|
case 'light cyan bg':
|
|
return val === false
|
|
? '\x1b[49m'
|
|
: '\x1b[106m';
|
|
case 'light white bg':
|
|
return val === false
|
|
? '\x1b[49m'
|
|
: '\x1b[107m';
|
|
|
|
// non-16-color rxvt default fg and bg
|
|
case 'default fg bg':
|
|
if (val === false) return '';
|
|
return this.term('rxvt')
|
|
? '\x1b[100m'
|
|
: '\x1b[39;49m';
|
|
|
|
default:
|
|
// 256-color fg and bg
|
|
if (param[0] === '#') {
|
|
param = param.replace(/#(?:[0-9a-f]{3}){1,2}/i, colors.match);
|
|
}
|
|
|
|
m = /^(-?\d+) (fg|bg)$/.exec(param);
|
|
if (m) {
|
|
color = +m[1];
|
|
|
|
if (val === false || color === -1) {
|
|
return this._attr('default ' + m[2]);
|
|
}
|
|
|
|
if (color < 16 || (this.tput && this.tput.colors <= 16)) {
|
|
if (m[2] === 'fg') {
|
|
if (color < 8) {
|
|
color += 30;
|
|
} else if (color < 16) {
|
|
color += 90;
|
|
}
|
|
} else if (m[2] === 'bg') {
|
|
if (color < 8) {
|
|
color += 40;
|
|
} else if (color < 16) {
|
|
color += 100;
|
|
}
|
|
}
|
|
return '\x1b[' + color + 'm';
|
|
}
|
|
|
|
if (m[2] === 'fg') {
|
|
return '\x1b[38;5;' + color + 'm';
|
|
}
|
|
|
|
if (m[2] === 'bg') {
|
|
return '\x1b[48;5;' + color + 'm';
|
|
}
|
|
}
|
|
|
|
if (/^[\d;]*$/.test(param)) {
|
|
return '\x1b[' + param + 'm';
|
|
}
|
|
|
|
return null;
|
|
}
|
|
};
|
|
|
|
Program.prototype.fg =
|
|
Program.prototype.setForeground = function(color, val) {
|
|
color = color.split(/\s*[,;]\s*/).join(' fg, ') + ' fg';
|
|
return this.attr(color, val);
|
|
};
|
|
|
|
Program.prototype.bg =
|
|
Program.prototype.setBackground = function(color, val) {
|
|
color = color.split(/\s*[,;]\s*/).join(' bg, ') + ' bg';
|
|
return this.attr(color, val);
|
|
};
|
|
|
|
// CSI Ps n Device Status Report (DSR).
|
|
// Ps = 5 -> Status Report. Result (``OK'') is
|
|
// CSI 0 n
|
|
// Ps = 6 -> Report Cursor Position (CPR) [row;column].
|
|
// Result is
|
|
// CSI r ; c R
|
|
// CSI ? Ps n
|
|
// Device Status Report (DSR, DEC-specific).
|
|
// Ps = 6 -> Report Cursor Position (CPR) [row;column] as CSI
|
|
// ? r ; c R (assumes page is zero).
|
|
// Ps = 1 5 -> Report Printer status as CSI ? 1 0 n (ready).
|
|
// or CSI ? 1 1 n (not ready).
|
|
// Ps = 2 5 -> Report UDK status as CSI ? 2 0 n (unlocked)
|
|
// or CSI ? 2 1 n (locked).
|
|
// Ps = 2 6 -> Report Keyboard status as
|
|
// CSI ? 2 7 ; 1 ; 0 ; 0 n (North American).
|
|
// The last two parameters apply to VT400 & up, and denote key-
|
|
// board ready and LK01 respectively.
|
|
// Ps = 5 3 -> Report Locator status as
|
|
// CSI ? 5 3 n Locator available, if compiled-in, or
|
|
// CSI ? 5 0 n No Locator, if not.
|
|
Program.prototype.dsr =
|
|
Program.prototype.deviceStatus = function(param, callback, dec, noBypass) {
|
|
if (dec) {
|
|
return this.response('device-status',
|
|
'\x1b[?' + (param || '0') + 'n', callback, noBypass);
|
|
}
|
|
return this.response('device-status',
|
|
'\x1b[' + (param || '0') + 'n', callback, noBypass);
|
|
};
|
|
|
|
Program.prototype.getCursor = function(callback) {
|
|
return this.deviceStatus(6, callback, false, true);
|
|
};
|
|
|
|
Program.prototype.saveReportedCursor = function(callback) {
|
|
var self = this;
|
|
if (this.tput.strings.user7 === '\x1b[6n' || this.term('screen')) {
|
|
return this.getCursor(function(err, data) {
|
|
if (data) {
|
|
self._rx = data.status.x;
|
|
self._ry = data.status.y;
|
|
}
|
|
if (!callback) return;
|
|
return callback(err);
|
|
});
|
|
}
|
|
if (!callback) return;
|
|
return callback();
|
|
};
|
|
|
|
Program.prototype.restoreReportedCursor = function() {
|
|
if (this._rx == null) return;
|
|
return this.cup(this._ry, this._rx);
|
|
// return this.nel();
|
|
};
|
|
|
|
/**
|
|
* Additions
|
|
*/
|
|
|
|
// CSI Ps @
|
|
// Insert Ps (Blank) Character(s) (default = 1) (ICH).
|
|
Program.prototype.ich =
|
|
Program.prototype.insertChars = function(param) {
|
|
this.x += param || 1;
|
|
this._ncoords();
|
|
if (this.tput) return this.put.ich(param);
|
|
return this._write('\x1b[' + (param || 1) + '@');
|
|
};
|
|
|
|
// CSI Ps E
|
|
// Cursor Next Line Ps Times (default = 1) (CNL).
|
|
// same as CSI Ps B ?
|
|
Program.prototype.cnl =
|
|
Program.prototype.cursorNextLine = function(param) {
|
|
this.y += param || 1;
|
|
this._ncoords();
|
|
return this._write('\x1b[' + (param || '') + 'E');
|
|
};
|
|
|
|
// CSI Ps F
|
|
// Cursor Preceding Line Ps Times (default = 1) (CNL).
|
|
// reuse CSI Ps A ?
|
|
Program.prototype.cpl =
|
|
Program.prototype.cursorPrecedingLine = function(param) {
|
|
this.y -= param || 1;
|
|
this._ncoords();
|
|
return this._write('\x1b[' + (param || '') + 'F');
|
|
};
|
|
|
|
// CSI Ps G
|
|
// Cursor Character Absolute [column] (default = [row,1]) (CHA).
|
|
Program.prototype.cha =
|
|
Program.prototype.cursorCharAbsolute = function(param) {
|
|
if (!this.zero) {
|
|
param = (param || 1) - 1;
|
|
} else {
|
|
param = param || 0;
|
|
}
|
|
this.x = param;
|
|
this.y = 0;
|
|
this._ncoords();
|
|
if (this.tput) return this.put.hpa(param);
|
|
return this._write('\x1b[' + (param + 1) + 'G');
|
|
};
|
|
|
|
// CSI Ps L
|
|
// Insert Ps Line(s) (default = 1) (IL).
|
|
Program.prototype.il =
|
|
Program.prototype.insertLines = function(param) {
|
|
if (this.tput) return this.put.il(param);
|
|
return this._write('\x1b[' + (param || '') + 'L');
|
|
};
|
|
|
|
// CSI Ps M
|
|
// Delete Ps Line(s) (default = 1) (DL).
|
|
Program.prototype.dl =
|
|
Program.prototype.deleteLines = function(param) {
|
|
if (this.tput) return this.put.dl(param);
|
|
return this._write('\x1b[' + (param || '') + 'M');
|
|
};
|
|
|
|
// CSI Ps P
|
|
// Delete Ps Character(s) (default = 1) (DCH).
|
|
Program.prototype.dch =
|
|
Program.prototype.deleteChars = function(param) {
|
|
if (this.tput) return this.put.dch(param);
|
|
return this._write('\x1b[' + (param || '') + 'P');
|
|
};
|
|
|
|
// CSI Ps X
|
|
// Erase Ps Character(s) (default = 1) (ECH).
|
|
Program.prototype.ech =
|
|
Program.prototype.eraseChars = function(param) {
|
|
if (this.tput) return this.put.ech(param);
|
|
return this._write('\x1b[' + (param || '') + 'X');
|
|
};
|
|
|
|
// CSI Pm ` Character Position Absolute
|
|
// [column] (default = [row,1]) (HPA).
|
|
Program.prototype.hpa =
|
|
Program.prototype.charPosAbsolute = function(param) {
|
|
this.x = param || 0;
|
|
this._ncoords();
|
|
if (this.tput) {
|
|
return this.put.hpa.apply(this.put, arguments);
|
|
}
|
|
var param = slice.call(arguments).join(';');
|
|
return this._write('\x1b[' + (param || '') + '`');
|
|
};
|
|
|
|
// 141 61 a * HPR -
|
|
// Horizontal Position Relative
|
|
// reuse CSI Ps C ?
|
|
Program.prototype.hpr =
|
|
Program.prototype.HPositionRelative = function(param) {
|
|
this.x += param || 1;
|
|
this._ncoords();
|
|
// Does not exist:
|
|
// if (this.tput) return this.put.hpr(param);
|
|
if (this.tput) return this.put.cuf(param);
|
|
return this._write('\x1b[' + (param || '') + 'a');
|
|
};
|
|
|
|
// CSI Ps c Send Device Attributes (Primary DA).
|
|
// Ps = 0 or omitted -> request attributes from terminal. The
|
|
// response depends on the decTerminalID resource setting.
|
|
// -> CSI ? 1 ; 2 c (``VT100 with Advanced Video Option'')
|
|
// -> CSI ? 1 ; 0 c (``VT101 with No Options'')
|
|
// -> CSI ? 6 c (``VT102'')
|
|
// -> CSI ? 6 0 ; 1 ; 2 ; 6 ; 8 ; 9 ; 1 5 ; c (``VT220'')
|
|
// The VT100-style response parameters do not mean anything by
|
|
// themselves. VT220 parameters do, telling the host what fea-
|
|
// tures the terminal supports:
|
|
// Ps = 1 -> 132-columns.
|
|
// Ps = 2 -> Printer.
|
|
// Ps = 6 -> Selective erase.
|
|
// Ps = 8 -> User-defined keys.
|
|
// Ps = 9 -> National replacement character sets.
|
|
// Ps = 1 5 -> Technical characters.
|
|
// Ps = 2 2 -> ANSI color, e.g., VT525.
|
|
// Ps = 2 9 -> ANSI text locator (i.e., DEC Locator mode).
|
|
// CSI > Ps c
|
|
// Send Device Attributes (Secondary DA).
|
|
// Ps = 0 or omitted -> request the terminal's identification
|
|
// code. The response depends on the decTerminalID resource set-
|
|
// ting. It should apply only to VT220 and up, but xterm extends
|
|
// this to VT100.
|
|
// -> CSI > Pp ; Pv ; Pc c
|
|
// where Pp denotes the terminal type
|
|
// Pp = 0 -> ``VT100''.
|
|
// Pp = 1 -> ``VT220''.
|
|
// and Pv is the firmware version (for xterm, this was originally
|
|
// the XFree86 patch number, starting with 95). In a DEC termi-
|
|
// nal, Pc indicates the ROM cartridge registration number and is
|
|
// always zero.
|
|
// More information:
|
|
// xterm/charproc.c - line 2012, for more information.
|
|
// vim responds with ^[[?0c or ^[[?1c after the terminal's response (?)
|
|
Program.prototype.da =
|
|
Program.prototype.sendDeviceAttributes = function(param, callback) {
|
|
return this.response('device-attributes',
|
|
'\x1b[' + (param || '') + 'c', callback);
|
|
};
|
|
|
|
// CSI Pm d
|
|
// Line Position Absolute [row] (default = [1,column]) (VPA).
|
|
// NOTE: Can't find in terminfo, no idea why it has multiple params.
|
|
Program.prototype.vpa =
|
|
Program.prototype.linePosAbsolute = function(param) {
|
|
this.y = param || 1;
|
|
this._ncoords();
|
|
if (this.tput) {
|
|
return this.put.vpa.apply(this.put, arguments);
|
|
}
|
|
var param = slice.call(arguments).join(';');
|
|
return this._write('\x1b[' + (param || '') + 'd');
|
|
};
|
|
|
|
// 145 65 e * VPR - Vertical Position Relative
|
|
// reuse CSI Ps B ?
|
|
Program.prototype.vpr =
|
|
Program.prototype.VPositionRelative = function(param) {
|
|
this.y += param || 1;
|
|
this._ncoords();
|
|
// Does not exist:
|
|
// if (this.tput) return this.put.vpr(param);
|
|
if (this.tput) return this.put.cud(param);
|
|
return this._write('\x1b[' + (param || '') + 'e');
|
|
};
|
|
|
|
// CSI Ps ; Ps f
|
|
// Horizontal and Vertical Position [row;column] (default =
|
|
// [1,1]) (HVP).
|
|
Program.prototype.hvp =
|
|
Program.prototype.HVPosition = function(row, col) {
|
|
if (!this.zero) {
|
|
row = (row || 1) - 1;
|
|
col = (col || 1) - 1;
|
|
} else {
|
|
row = row || 0;
|
|
col = col || 0;
|
|
}
|
|
this.y = row;
|
|
this.x = col;
|
|
this._ncoords();
|
|
// Does not exist (?):
|
|
// if (this.tput) return this.put.hvp(row, col);
|
|
if (this.tput) return this.put.cup(row, col);
|
|
return this._write('\x1b[' + (row + 1) + ';' + (col + 1) + 'f');
|
|
};
|
|
|
|
// CSI Pm h Set Mode (SM).
|
|
// Ps = 2 -> Keyboard Action Mode (AM).
|
|
// Ps = 4 -> Insert Mode (IRM).
|
|
// Ps = 1 2 -> Send/receive (SRM).
|
|
// Ps = 2 0 -> Automatic Newline (LNM).
|
|
// CSI ? Pm h
|
|
// DEC Private Mode Set (DECSET).
|
|
// Ps = 1 -> Application Cursor Keys (DECCKM).
|
|
// Ps = 2 -> Designate USASCII for character sets G0-G3
|
|
// (DECANM), and set VT100 mode.
|
|
// Ps = 3 -> 132 Column Mode (DECCOLM).
|
|
// Ps = 4 -> Smooth (Slow) Scroll (DECSCLM).
|
|
// Ps = 5 -> Reverse Video (DECSCNM).
|
|
// Ps = 6 -> Origin Mode (DECOM).
|
|
// Ps = 7 -> Wraparound Mode (DECAWM).
|
|
// Ps = 8 -> Auto-repeat Keys (DECARM).
|
|
// Ps = 9 -> Send Mouse X & Y on button press. See the sec-
|
|
// tion Mouse Tracking.
|
|
// Ps = 1 0 -> Show toolbar (rxvt).
|
|
// Ps = 1 2 -> Start Blinking Cursor (att610).
|
|
// Ps = 1 8 -> Print form feed (DECPFF).
|
|
// Ps = 1 9 -> Set print extent to full screen (DECPEX).
|
|
// Ps = 2 5 -> Show Cursor (DECTCEM).
|
|
// Ps = 3 0 -> Show scrollbar (rxvt).
|
|
// Ps = 3 5 -> Enable font-shifting functions (rxvt).
|
|
// Ps = 3 8 -> Enter Tektronix Mode (DECTEK).
|
|
// Ps = 4 0 -> Allow 80 -> 132 Mode.
|
|
// Ps = 4 1 -> more(1) fix (see curses resource).
|
|
// Ps = 4 2 -> Enable Nation Replacement Character sets (DECN-
|
|
// RCM).
|
|
// Ps = 4 4 -> Turn On Margin Bell.
|
|
// Ps = 4 5 -> Reverse-wraparound Mode.
|
|
// Ps = 4 6 -> Start Logging. This is normally disabled by a
|
|
// compile-time option.
|
|
// Ps = 4 7 -> Use Alternate Screen Buffer. (This may be dis-
|
|
// abled by the titeInhibit resource).
|
|
// Ps = 6 6 -> Application keypad (DECNKM).
|
|
// Ps = 6 7 -> Backarrow key sends backspace (DECBKM).
|
|
// Ps = 1 0 0 0 -> Send Mouse X & Y on button press and
|
|
// release. See the section Mouse Tracking.
|
|
// Ps = 1 0 0 1 -> Use Hilite Mouse Tracking.
|
|
// Ps = 1 0 0 2 -> Use Cell Motion Mouse Tracking.
|
|
// Ps = 1 0 0 3 -> Use All Motion Mouse Tracking.
|
|
// Ps = 1 0 0 4 -> Send FocusIn/FocusOut events.
|
|
// Ps = 1 0 0 5 -> Enable Extended Mouse Mode.
|
|
// Ps = 1 0 1 0 -> Scroll to bottom on tty output (rxvt).
|
|
// Ps = 1 0 1 1 -> Scroll to bottom on key press (rxvt).
|
|
// Ps = 1 0 3 4 -> Interpret "meta" key, sets eighth bit.
|
|
// (enables the eightBitInput resource).
|
|
// Ps = 1 0 3 5 -> Enable special modifiers for Alt and Num-
|
|
// Lock keys. (This enables the numLock resource).
|
|
// Ps = 1 0 3 6 -> Send ESC when Meta modifies a key. (This
|
|
// enables the metaSendsEscape resource).
|
|
// Ps = 1 0 3 7 -> Send DEL from the editing-keypad Delete
|
|
// key.
|
|
// Ps = 1 0 3 9 -> Send ESC when Alt modifies a key. (This
|
|
// enables the altSendsEscape resource).
|
|
// Ps = 1 0 4 0 -> Keep selection even if not highlighted.
|
|
// (This enables the keepSelection resource).
|
|
// Ps = 1 0 4 1 -> Use the CLIPBOARD selection. (This enables
|
|
// the selectToClipboard resource).
|
|
// Ps = 1 0 4 2 -> Enable Urgency window manager hint when
|
|
// Control-G is received. (This enables the bellIsUrgent
|
|
// resource).
|
|
// Ps = 1 0 4 3 -> Enable raising of the window when Control-G
|
|
// is received. (enables the popOnBell resource).
|
|
// Ps = 1 0 4 7 -> Use Alternate Screen Buffer. (This may be
|
|
// disabled by the titeInhibit resource).
|
|
// Ps = 1 0 4 8 -> Save cursor as in DECSC. (This may be dis-
|
|
// abled by the titeInhibit resource).
|
|
// Ps = 1 0 4 9 -> Save cursor as in DECSC and use Alternate
|
|
// Screen Buffer, clearing it first. (This may be disabled by
|
|
// the titeInhibit resource). This combines the effects of the 1
|
|
// 0 4 7 and 1 0 4 8 modes. Use this with terminfo-based
|
|
// applications rather than the 4 7 mode.
|
|
// Ps = 1 0 5 0 -> Set terminfo/termcap function-key mode.
|
|
// Ps = 1 0 5 1 -> Set Sun function-key mode.
|
|
// Ps = 1 0 5 2 -> Set HP function-key mode.
|
|
// Ps = 1 0 5 3 -> Set SCO function-key mode.
|
|
// Ps = 1 0 6 0 -> Set legacy keyboard emulation (X11R6).
|
|
// Ps = 1 0 6 1 -> Set VT220 keyboard emulation.
|
|
// Ps = 2 0 0 4 -> Set bracketed paste mode.
|
|
// Modes:
|
|
// http://vt100.net/docs/vt220-rm/chapter4.html
|
|
Program.prototype.sm =
|
|
Program.prototype.setMode = function() {
|
|
var param = slice.call(arguments).join(';');
|
|
return this._write('\x1b[' + (param || '') + 'h');
|
|
};
|
|
|
|
Program.prototype.decset = function() {
|
|
var param = slice.call(arguments).join(';');
|
|
return this.setMode('?' + param);
|
|
};
|
|
|
|
Program.prototype.dectcem =
|
|
Program.prototype.cnorm =
|
|
Program.prototype.cvvis =
|
|
Program.prototype.showCursor = function() {
|
|
this.cursorHidden = false;
|
|
// NOTE: In xterm terminfo:
|
|
// cnorm stops blinking cursor
|
|
// cvvis starts blinking cursor
|
|
if (this.tput) return this.put.cnorm();
|
|
//if (this.tput) return this.put.cvvis();
|
|
// return this._write('\x1b[?12l\x1b[?25h'); // cursor_normal
|
|
// return this._write('\x1b[?12;25h'); // cursor_visible
|
|
return this.setMode('?25');
|
|
};
|
|
|
|
Program.prototype.alternate =
|
|
Program.prototype.smcup =
|
|
Program.prototype.alternateBuffer = function() {
|
|
this.isAlt = true;
|
|
if (this.tput) return this.put.smcup();
|
|
if (this.term('vt') || this.term('linux')) return;
|
|
this.setMode('?47');
|
|
return this.setMode('?1049');
|
|
};
|
|
|
|
// CSI Pm l Reset Mode (RM).
|
|
// Ps = 2 -> Keyboard Action Mode (AM).
|
|
// Ps = 4 -> Replace Mode (IRM).
|
|
// Ps = 1 2 -> Send/receive (SRM).
|
|
// Ps = 2 0 -> Normal Linefeed (LNM).
|
|
// CSI ? Pm l
|
|
// DEC Private Mode Reset (DECRST).
|
|
// Ps = 1 -> Normal Cursor Keys (DECCKM).
|
|
// Ps = 2 -> Designate VT52 mode (DECANM).
|
|
// Ps = 3 -> 80 Column Mode (DECCOLM).
|
|
// Ps = 4 -> Jump (Fast) Scroll (DECSCLM).
|
|
// Ps = 5 -> Normal Video (DECSCNM).
|
|
// Ps = 6 -> Normal Cursor Mode (DECOM).
|
|
// Ps = 7 -> No Wraparound Mode (DECAWM).
|
|
// Ps = 8 -> No Auto-repeat Keys (DECARM).
|
|
// Ps = 9 -> Don't send Mouse X & Y on button press.
|
|
// Ps = 1 0 -> Hide toolbar (rxvt).
|
|
// Ps = 1 2 -> Stop Blinking Cursor (att610).
|
|
// Ps = 1 8 -> Don't print form feed (DECPFF).
|
|
// Ps = 1 9 -> Limit print to scrolling region (DECPEX).
|
|
// Ps = 2 5 -> Hide Cursor (DECTCEM).
|
|
// Ps = 3 0 -> Don't show scrollbar (rxvt).
|
|
// Ps = 3 5 -> Disable font-shifting functions (rxvt).
|
|
// Ps = 4 0 -> Disallow 80 -> 132 Mode.
|
|
// Ps = 4 1 -> No more(1) fix (see curses resource).
|
|
// Ps = 4 2 -> Disable Nation Replacement Character sets (DEC-
|
|
// NRCM).
|
|
// Ps = 4 4 -> Turn Off Margin Bell.
|
|
// Ps = 4 5 -> No Reverse-wraparound Mode.
|
|
// Ps = 4 6 -> Stop Logging. (This is normally disabled by a
|
|
// compile-time option).
|
|
// Ps = 4 7 -> Use Normal Screen Buffer.
|
|
// Ps = 6 6 -> Numeric keypad (DECNKM).
|
|
// Ps = 6 7 -> Backarrow key sends delete (DECBKM).
|
|
// Ps = 1 0 0 0 -> Don't send Mouse X & Y on button press and
|
|
// release. See the section Mouse Tracking.
|
|
// Ps = 1 0 0 1 -> Don't use Hilite Mouse Tracking.
|
|
// Ps = 1 0 0 2 -> Don't use Cell Motion Mouse Tracking.
|
|
// Ps = 1 0 0 3 -> Don't use All Motion Mouse Tracking.
|
|
// Ps = 1 0 0 4 -> Don't send FocusIn/FocusOut events.
|
|
// Ps = 1 0 0 5 -> Disable Extended Mouse Mode.
|
|
// Ps = 1 0 1 0 -> Don't scroll to bottom on tty output
|
|
// (rxvt).
|
|
// Ps = 1 0 1 1 -> Don't scroll to bottom on key press (rxvt).
|
|
// Ps = 1 0 3 4 -> Don't interpret "meta" key. (This disables
|
|
// the eightBitInput resource).
|
|
// Ps = 1 0 3 5 -> Disable special modifiers for Alt and Num-
|
|
// Lock keys. (This disables the numLock resource).
|
|
// Ps = 1 0 3 6 -> Don't send ESC when Meta modifies a key.
|
|
// (This disables the metaSendsEscape resource).
|
|
// Ps = 1 0 3 7 -> Send VT220 Remove from the editing-keypad
|
|
// Delete key.
|
|
// Ps = 1 0 3 9 -> Don't send ESC when Alt modifies a key.
|
|
// (This disables the altSendsEscape resource).
|
|
// Ps = 1 0 4 0 -> Do not keep selection when not highlighted.
|
|
// (This disables the keepSelection resource).
|
|
// Ps = 1 0 4 1 -> Use the PRIMARY selection. (This disables
|
|
// the selectToClipboard resource).
|
|
// Ps = 1 0 4 2 -> Disable Urgency window manager hint when
|
|
// Control-G is received. (This disables the bellIsUrgent
|
|
// resource).
|
|
// Ps = 1 0 4 3 -> Disable raising of the window when Control-
|
|
// G is received. (This disables the popOnBell resource).
|
|
// Ps = 1 0 4 7 -> Use Normal Screen Buffer, clearing screen
|
|
// first if in the Alternate Screen. (This may be disabled by
|
|
// the titeInhibit resource).
|
|
// Ps = 1 0 4 8 -> Restore cursor as in DECRC. (This may be
|
|
// disabled by the titeInhibit resource).
|
|
// Ps = 1 0 4 9 -> Use Normal Screen Buffer and restore cursor
|
|
// as in DECRC. (This may be disabled by the titeInhibit
|
|
// resource). This combines the effects of the 1 0 4 7 and 1 0
|
|
// 4 8 modes. Use this with terminfo-based applications rather
|
|
// than the 4 7 mode.
|
|
// Ps = 1 0 5 0 -> Reset terminfo/termcap function-key mode.
|
|
// Ps = 1 0 5 1 -> Reset Sun function-key mode.
|
|
// Ps = 1 0 5 2 -> Reset HP function-key mode.
|
|
// Ps = 1 0 5 3 -> Reset SCO function-key mode.
|
|
// Ps = 1 0 6 0 -> Reset legacy keyboard emulation (X11R6).
|
|
// Ps = 1 0 6 1 -> Reset keyboard emulation to Sun/PC style.
|
|
// Ps = 2 0 0 4 -> Reset bracketed paste mode.
|
|
Program.prototype.rm =
|
|
Program.prototype.resetMode = function() {
|
|
var param = slice.call(arguments).join(';');
|
|
return this._write('\x1b[' + (param || '') + 'l');
|
|
};
|
|
|
|
Program.prototype.decrst = function() {
|
|
var param = slice.call(arguments).join(';');
|
|
return this.resetMode('?' + param);
|
|
};
|
|
|
|
Program.prototype.dectcemh =
|
|
Program.prototype.cursor_invisible =
|
|
Program.prototype.vi =
|
|
Program.prototype.civis =
|
|
Program.prototype.hideCursor = function() {
|
|
this.cursorHidden = true;
|
|
if (this.tput) return this.put.civis();
|
|
return this.resetMode('?25');
|
|
};
|
|
|
|
Program.prototype.rmcup =
|
|
Program.prototype.normalBuffer = function() {
|
|
this.isAlt = false;
|
|
if (this.tput) return this.put.rmcup();
|
|
this.resetMode('?47');
|
|
return this.resetMode('?1049');
|
|
};
|
|
|
|
Program.prototype.enableMouse = function() {
|
|
if (process.env.BLESSED_FORCE_MODES) {
|
|
var modes = process.env.BLESSED_FORCE_MODES.split(',');
|
|
var options = {};
|
|
for (var n = 0; n < modes.length; ++n) {
|
|
var pair = modes[n].split('=');
|
|
var v = pair[1] !== '0';
|
|
switch (pair[0].toUpperCase()) {
|
|
case 'SGRMOUSE':
|
|
options.sgrMouse = v;
|
|
break;
|
|
case 'UTFMOUSE':
|
|
options.utfMouse = v;
|
|
break;
|
|
case 'VT200MOUSE':
|
|
options.vt200Mouse = v;
|
|
break;
|
|
case 'URXVTMOUSE':
|
|
options.urxvtMouse = v;
|
|
break;
|
|
case 'X10MOUSE':
|
|
options.x10Mouse = v;
|
|
break;
|
|
case 'DECMOUSE':
|
|
options.decMouse = v;
|
|
break;
|
|
case 'PTERMMOUSE':
|
|
options.ptermMouse = v;
|
|
break;
|
|
case 'JSBTERMMOUSE':
|
|
options.jsbtermMouse = v;
|
|
break;
|
|
case 'VT200HILITE':
|
|
options.vt200Hilite = v;
|
|
break;
|
|
case 'GPMMOUSE':
|
|
options.gpmMouse = v;
|
|
break;
|
|
case 'CELLMOTION':
|
|
options.cellMotion = v;
|
|
break;
|
|
case 'ALLMOTION':
|
|
options.allMotion = v;
|
|
break;
|
|
case 'SENDFOCUS':
|
|
options.sendFocus = v;
|
|
break;
|
|
}
|
|
}
|
|
return this.setMouse(options, true);
|
|
}
|
|
|
|
if (this.term('rxvt-unicode')) {
|
|
return this.setMouse({
|
|
urxvtMouse: true,
|
|
allMotion: true
|
|
}, true);
|
|
}
|
|
|
|
// rxvt does not support the X10 UTF extensions
|
|
if (this.term('rxvt')) {
|
|
return this.setMouse({
|
|
vt200Mouse: true,
|
|
x10Mouse: true,
|
|
allMotion: true
|
|
}, true);
|
|
}
|
|
|
|
// libvte is broken. Older versions do not support the
|
|
// X10 UTF extension. However, later versions do support
|
|
// SGR/URXVT.
|
|
if (this.isVTE) {
|
|
return this.setMouse({
|
|
// NOTE: Could also use urxvtMouse here.
|
|
sgrMouse: true,
|
|
allMotion: true
|
|
}, true);
|
|
}
|
|
|
|
if (this.term('linux')) {
|
|
return this.setMouse({
|
|
vt200Mouse: true,
|
|
gpmMouse: true
|
|
}, true);
|
|
}
|
|
|
|
if (this.term('xterm')
|
|
|| this.term('screen')
|
|
|| (this.tput && this.tput.strings.key_mouse)) {
|
|
return this.setMouse({
|
|
vt200Mouse: true,
|
|
utfMouse: true,
|
|
allMotion: true
|
|
}, true);
|
|
}
|
|
};
|
|
|
|
Program.prototype.disableMouse = function() {
|
|
if (!this._currentMouse) return;
|
|
|
|
var obj = {};
|
|
|
|
Object.keys(this._currentMouse).forEach(function(key) {
|
|
obj[key] = false;
|
|
});
|
|
|
|
return this.setMouse(obj, false);
|
|
};
|
|
|
|
// Set Mouse
|
|
Program.prototype.setMouse = function(opt, enable) {
|
|
if (opt.normalMouse != null) {
|
|
opt.vt200Mouse = opt.normalMouse;
|
|
opt.allMotion = opt.normalMouse;
|
|
}
|
|
|
|
if (opt.hiliteTracking != null) {
|
|
opt.vt200Hilite = opt.hiliteTracking;
|
|
}
|
|
|
|
if (enable) {
|
|
this._currentMouse = opt;
|
|
this.mouseEnabled = true;
|
|
} else {
|
|
delete this._currentMouse;
|
|
this.mouseEnabled = false;
|
|
}
|
|
|
|
// Ps = 9 -> Send Mouse X & Y on button press. See the sec-
|
|
// tion Mouse Tracking.
|
|
// Ps = 9 -> Don't send Mouse X & Y on button press.
|
|
// x10 mouse
|
|
if (opt.x10Mouse != null) {
|
|
if (opt.x10Mouse) this.setMode('?9');
|
|
else this.resetMode('?9');
|
|
}
|
|
|
|
// Ps = 1 0 0 0 -> Send Mouse X & Y on button press and
|
|
// release. See the section Mouse Tracking.
|
|
// Ps = 1 0 0 0 -> Don't send Mouse X & Y on button press and
|
|
// release. See the section Mouse Tracking.
|
|
// vt200 mouse
|
|
if (opt.vt200Mouse != null) {
|
|
if (opt.vt200Mouse) this.setMode('?1000');
|
|
else this.resetMode('?1000');
|
|
}
|
|
|
|
// Ps = 1 0 0 1 -> Use Hilite Mouse Tracking.
|
|
// Ps = 1 0 0 1 -> Don't use Hilite Mouse Tracking.
|
|
if (opt.vt200Hilite != null) {
|
|
if (opt.vt200Hilite) this.setMode('?1001');
|
|
else this.resetMode('?1001');
|
|
}
|
|
|
|
// Ps = 1 0 0 2 -> Use Cell Motion Mouse Tracking.
|
|
// Ps = 1 0 0 2 -> Don't use Cell Motion Mouse Tracking.
|
|
// button event mouse
|
|
if (opt.cellMotion != null) {
|
|
if (opt.cellMotion) this.setMode('?1002');
|
|
else this.resetMode('?1002');
|
|
}
|
|
|
|
// Ps = 1 0 0 3 -> Use All Motion Mouse Tracking.
|
|
// Ps = 1 0 0 3 -> Don't use All Motion Mouse Tracking.
|
|
// any event mouse
|
|
if (opt.allMotion != null) {
|
|
if (opt.allMotion) this.setMode('?1003');
|
|
else this.resetMode('?1003');
|
|
}
|
|
|
|
// Ps = 1 0 0 4 -> Send FocusIn/FocusOut events.
|
|
// Ps = 1 0 0 4 -> Don't send FocusIn/FocusOut events.
|
|
if (opt.sendFocus != null) {
|
|
if (opt.sendFocus) this.setMode('?1004');
|
|
else this.resetMode('?1004');
|
|
}
|
|
|
|
// Ps = 1 0 0 5 -> Enable Extended Mouse Mode.
|
|
// Ps = 1 0 0 5 -> Disable Extended Mouse Mode.
|
|
if (opt.utfMouse != null) {
|
|
if (opt.utfMouse) this.setMode('?1005');
|
|
else this.resetMode('?1005');
|
|
}
|
|
|
|
// sgr mouse
|
|
if (opt.sgrMouse != null) {
|
|
if (opt.sgrMouse) this.setMode('?1006');
|
|
else this.resetMode('?1006');
|
|
}
|
|
|
|
// urxvt mouse
|
|
if (opt.urxvtMouse != null) {
|
|
if (opt.urxvtMouse) this.setMode('?1015');
|
|
else this.resetMode('?1015');
|
|
}
|
|
|
|
// dec mouse
|
|
if (opt.decMouse != null) {
|
|
if (opt.decMouse) this._write('\x1b[1;2\'z\x1b[1;3\'{');
|
|
else this._write('\x1b[\'z');
|
|
}
|
|
|
|
// pterm mouse
|
|
if (opt.ptermMouse != null) {
|
|
if (opt.ptermMouse) this._write('\x1b[>1h\x1b[>6h\x1b[>7h\x1b[>1h\x1b[>9l');
|
|
else this._write('\x1b[>1l\x1b[>6l\x1b[>7l\x1b[>1l\x1b[>9h');
|
|
}
|
|
|
|
// jsbterm mouse
|
|
if (opt.jsbtermMouse != null) {
|
|
// + = advanced mode
|
|
if (opt.jsbtermMouse) this._write('\x1b[0~ZwLMRK+1Q\x1b\\');
|
|
else this._write('\x1b[0~ZwQ\x1b\\');
|
|
}
|
|
|
|
// gpm mouse
|
|
if (opt.gpmMouse != null) {
|
|
if (opt.gpmMouse) this.enableGpm();
|
|
else this.disableGpm();
|
|
}
|
|
};
|
|
|
|
// CSI Ps ; Ps r
|
|
// Set Scrolling Region [top;bottom] (default = full size of win-
|
|
// dow) (DECSTBM).
|
|
// CSI ? Pm r
|
|
Program.prototype.decstbm =
|
|
Program.prototype.csr =
|
|
Program.prototype.setScrollRegion = function(top, bottom) {
|
|
if (!this.zero) {
|
|
top = (top || 1) - 1;
|
|
bottom = (bottom || this.rows) - 1;
|
|
} else {
|
|
top = top || 0;
|
|
bottom = bottom || (this.rows - 1);
|
|
}
|
|
this.scrollTop = top;
|
|
this.scrollBottom = bottom;
|
|
this.x = 0;
|
|
this.y = 0;
|
|
this._ncoords();
|
|
if (this.tput) return this.put.csr(top, bottom);
|
|
return this._write('\x1b[' + (top + 1) + ';' + (bottom + 1) + 'r');
|
|
};
|
|
|
|
// CSI s
|
|
// Save cursor (ANSI.SYS).
|
|
Program.prototype.scA =
|
|
Program.prototype.saveCursorA = function() {
|
|
this.savedX = this.x;
|
|
this.savedY = this.y;
|
|
if (this.tput) return this.put.sc();
|
|
return this._write('\x1b[s');
|
|
};
|
|
|
|
// CSI u
|
|
// Restore cursor (ANSI.SYS).
|
|
Program.prototype.rcA =
|
|
Program.prototype.restoreCursorA = function() {
|
|
this.x = this.savedX || 0;
|
|
this.y = this.savedY || 0;
|
|
if (this.tput) return this.put.rc();
|
|
return this._write('\x1b[u');
|
|
};
|
|
|
|
/**
|
|
* Lesser Used
|
|
*/
|
|
|
|
// CSI Ps I
|
|
// Cursor Forward Tabulation Ps tab stops (default = 1) (CHT).
|
|
Program.prototype.cht =
|
|
Program.prototype.cursorForwardTab = function(param) {
|
|
this.x += 8;
|
|
this._ncoords();
|
|
if (this.tput) return this.put.tab(param);
|
|
return this._write('\x1b[' + (param || 1) + 'I');
|
|
};
|
|
|
|
// CSI Ps S Scroll up Ps lines (default = 1) (SU).
|
|
Program.prototype.su =
|
|
Program.prototype.scrollUp = function(param) {
|
|
this.y -= param || 1;
|
|
this._ncoords();
|
|
if (this.tput) return this.put.parm_index(param);
|
|
return this._write('\x1b[' + (param || 1) + 'S');
|
|
};
|
|
|
|
// CSI Ps T Scroll down Ps lines (default = 1) (SD).
|
|
Program.prototype.sd =
|
|
Program.prototype.scrollDown = function(param) {
|
|
this.y += param || 1;
|
|
this._ncoords();
|
|
if (this.tput) return this.put.parm_rindex(param);
|
|
return this._write('\x1b[' + (param || 1) + 'T');
|
|
};
|
|
|
|
// CSI Ps ; Ps ; Ps ; Ps ; Ps T
|
|
// Initiate highlight mouse tracking. Parameters are
|
|
// [func;startx;starty;firstrow;lastrow]. See the section Mouse
|
|
// Tracking.
|
|
Program.prototype.initMouseTracking = function() {
|
|
return this._write('\x1b[' + slice.call(arguments).join(';') + 'T');
|
|
};
|
|
|
|
// CSI > Ps; Ps T
|
|
// Reset one or more features of the title modes to the default
|
|
// value. Normally, "reset" disables the feature. It is possi-
|
|
// ble to disable the ability to reset features by compiling a
|
|
// different default for the title modes into xterm.
|
|
// Ps = 0 -> Do not set window/icon labels using hexadecimal.
|
|
// Ps = 1 -> Do not query window/icon labels using hexadeci-
|
|
// mal.
|
|
// Ps = 2 -> Do not set window/icon labels using UTF-8.
|
|
// Ps = 3 -> Do not query window/icon labels using UTF-8.
|
|
// (See discussion of "Title Modes").
|
|
Program.prototype.resetTitleModes = function() {
|
|
return this._write('\x1b[>' + slice.call(arguments).join(';') + 'T');
|
|
};
|
|
|
|
// CSI Ps Z Cursor Backward Tabulation Ps tab stops (default = 1) (CBT).
|
|
Program.prototype.cbt =
|
|
Program.prototype.cursorBackwardTab = function(param) {
|
|
this.x -= 8;
|
|
this._ncoords();
|
|
if (this.tput) return this.put.cbt(param);
|
|
return this._write('\x1b[' + (param || 1) + 'Z');
|
|
};
|
|
|
|
// CSI Ps b Repeat the preceding graphic character Ps times (REP).
|
|
Program.prototype.rep =
|
|
Program.prototype.repeatPrecedingCharacter = function(param) {
|
|
this.x += param || 1;
|
|
this._ncoords();
|
|
if (this.tput) return this.put.rep(param);
|
|
return this._write('\x1b[' + (param || 1) + 'b');
|
|
};
|
|
|
|
// CSI Ps g Tab Clear (TBC).
|
|
// Ps = 0 -> Clear Current Column (default).
|
|
// Ps = 3 -> Clear All.
|
|
// Potentially:
|
|
// Ps = 2 -> Clear Stops on Line.
|
|
// http://vt100.net/annarbor/aaa-ug/section6.html
|
|
Program.prototype.tbc =
|
|
Program.prototype.tabClear = function(param) {
|
|
if (this.tput) return this.put.tbc(param);
|
|
return this._write('\x1b[' + (param || 0) + 'g');
|
|
};
|
|
|
|
// CSI Pm i Media Copy (MC).
|
|
// Ps = 0 -> Print screen (default).
|
|
// Ps = 4 -> Turn off printer controller mode.
|
|
// Ps = 5 -> Turn on printer controller mode.
|
|
// CSI ? Pm i
|
|
// Media Copy (MC, DEC-specific).
|
|
// Ps = 1 -> Print line containing cursor.
|
|
// Ps = 4 -> Turn off autoprint mode.
|
|
// Ps = 5 -> Turn on autoprint mode.
|
|
// Ps = 1 0 -> Print composed display, ignores DECPEX.
|
|
// Ps = 1 1 -> Print all pages.
|
|
Program.prototype.mc =
|
|
Program.prototype.mediaCopy = function() {
|
|
return this._write('\x1b[' + slice.call(arguments).join(';') + 'i');
|
|
};
|
|
|
|
Program.prototype.print_screen =
|
|
Program.prototype.ps =
|
|
Program.prototype.mc0 = function() {
|
|
if (this.tput) return this.put.mc0();
|
|
return this.mc('0');
|
|
};
|
|
|
|
Program.prototype.prtr_on =
|
|
Program.prototype.po =
|
|
Program.prototype.mc5 = function() {
|
|
if (this.tput) return this.put.mc5();
|
|
return this.mc('5');
|
|
};
|
|
|
|
Program.prototype.prtr_off =
|
|
Program.prototype.pf =
|
|
Program.prototype.mc4 = function() {
|
|
if (this.tput) return this.put.mc4();
|
|
return this.mc('4');
|
|
};
|
|
|
|
Program.prototype.prtr_non =
|
|
Program.prototype.pO =
|
|
Program.prototype.mc5p = function() {
|
|
if (this.tput) return this.put.mc5p();
|
|
return this.mc('?5');
|
|
};
|
|
|
|
// CSI > Ps; Ps m
|
|
// Set or reset resource-values used by xterm to decide whether
|
|
// to construct escape sequences holding information about the
|
|
// modifiers pressed with a given key. The first parameter iden-
|
|
// tifies the resource to set/reset. The second parameter is the
|
|
// value to assign to the resource. If the second parameter is
|
|
// omitted, the resource is reset to its initial value.
|
|
// Ps = 1 -> modifyCursorKeys.
|
|
// Ps = 2 -> modifyFunctionKeys.
|
|
// Ps = 4 -> modifyOtherKeys.
|
|
// If no parameters are given, all resources are reset to their
|
|
// initial values.
|
|
Program.prototype.setResources = function() {
|
|
return this._write('\x1b[>' + slice.call(arguments).join(';') + 'm');
|
|
};
|
|
|
|
// CSI > Ps n
|
|
// Disable modifiers which may be enabled via the CSI > Ps; Ps m
|
|
// sequence. This corresponds to a resource value of "-1", which
|
|
// cannot be set with the other sequence. The parameter identi-
|
|
// fies the resource to be disabled:
|
|
// Ps = 1 -> modifyCursorKeys.
|
|
// Ps = 2 -> modifyFunctionKeys.
|
|
// Ps = 4 -> modifyOtherKeys.
|
|
// If the parameter is omitted, modifyFunctionKeys is disabled.
|
|
// When modifyFunctionKeys is disabled, xterm uses the modifier
|
|
// keys to make an extended sequence of functions rather than
|
|
// adding a parameter to each function key to denote the modi-
|
|
// fiers.
|
|
Program.prototype.disableModifiers = function(param) {
|
|
return this._write('\x1b[>' + (param || '') + 'n');
|
|
};
|
|
|
|
// CSI > Ps p
|
|
// Set resource value pointerMode. This is used by xterm to
|
|
// decide whether to hide the pointer cursor as the user types.
|
|
// Valid values for the parameter:
|
|
// Ps = 0 -> never hide the pointer.
|
|
// Ps = 1 -> hide if the mouse tracking mode is not enabled.
|
|
// Ps = 2 -> always hide the pointer. If no parameter is
|
|
// given, xterm uses the default, which is 1 .
|
|
Program.prototype.setPointerMode = function(param) {
|
|
return this._write('\x1b[>' + (param || '') + 'p');
|
|
};
|
|
|
|
// CSI ! p Soft terminal reset (DECSTR).
|
|
// http://vt100.net/docs/vt220-rm/table4-10.html
|
|
Program.prototype.decstr =
|
|
Program.prototype.rs2 =
|
|
Program.prototype.softReset = function() {
|
|
//if (this.tput) return this.put.init_2string();
|
|
//if (this.tput) return this.put.reset_2string();
|
|
if (this.tput) return this.put.rs2();
|
|
//return this._write('\x1b[!p');
|
|
//return this._write('\x1b[!p\x1b[?3;4l\x1b[4l\x1b>'); // init
|
|
return this._write('\x1b[!p\x1b[?3;4l\x1b[4l\x1b>'); // reset
|
|
};
|
|
|
|
// CSI Ps$ p
|
|
// Request ANSI mode (DECRQM). For VT300 and up, reply is
|
|
// CSI Ps; Pm$ y
|
|
// where Ps is the mode number as in RM, and Pm is the mode
|
|
// value:
|
|
// 0 - not recognized
|
|
// 1 - set
|
|
// 2 - reset
|
|
// 3 - permanently set
|
|
// 4 - permanently reset
|
|
Program.prototype.decrqm =
|
|
Program.prototype.requestAnsiMode = function(param) {
|
|
return this._write('\x1b[' + (param || '') + '$p');
|
|
};
|
|
|
|
// CSI ? Ps$ p
|
|
// Request DEC private mode (DECRQM). For VT300 and up, reply is
|
|
// CSI ? Ps; Pm$ p
|
|
// where Ps is the mode number as in DECSET, Pm is the mode value
|
|
// as in the ANSI DECRQM.
|
|
Program.prototype.decrqmp =
|
|
Program.prototype.requestPrivateMode = function(param) {
|
|
return this._write('\x1b[?' + (param || '') + '$p');
|
|
};
|
|
|
|
// CSI Ps ; Ps " p
|
|
// Set conformance level (DECSCL). Valid values for the first
|
|
// parameter:
|
|
// Ps = 6 1 -> VT100.
|
|
// Ps = 6 2 -> VT200.
|
|
// Ps = 6 3 -> VT300.
|
|
// Valid values for the second parameter:
|
|
// Ps = 0 -> 8-bit controls.
|
|
// Ps = 1 -> 7-bit controls (always set for VT100).
|
|
// Ps = 2 -> 8-bit controls.
|
|
Program.prototype.decscl =
|
|
Program.prototype.setConformanceLevel = function() {
|
|
return this._write('\x1b[' + slice.call(arguments).join(';') + '"p');
|
|
};
|
|
|
|
// CSI Ps q Load LEDs (DECLL).
|
|
// Ps = 0 -> Clear all LEDS (default).
|
|
// Ps = 1 -> Light Num Lock.
|
|
// Ps = 2 -> Light Caps Lock.
|
|
// Ps = 3 -> Light Scroll Lock.
|
|
// Ps = 2 1 -> Extinguish Num Lock.
|
|
// Ps = 2 2 -> Extinguish Caps Lock.
|
|
// Ps = 2 3 -> Extinguish Scroll Lock.
|
|
Program.prototype.decll =
|
|
Program.prototype.loadLEDs = function(param) {
|
|
return this._write('\x1b[' + (param || '') + 'q');
|
|
};
|
|
|
|
// CSI Ps SP q
|
|
// Set cursor style (DECSCUSR, VT520).
|
|
// Ps = 0 -> blinking block.
|
|
// Ps = 1 -> blinking block (default).
|
|
// Ps = 2 -> steady block.
|
|
// Ps = 3 -> blinking underline.
|
|
// Ps = 4 -> steady underline.
|
|
Program.prototype.decscusr =
|
|
Program.prototype.setCursorStyle = function(param) {
|
|
switch (param) {
|
|
case 'blinking block':
|
|
param = 1;
|
|
break;
|
|
case 'block':
|
|
case 'steady block':
|
|
param = 2;
|
|
break;
|
|
case 'blinking underline':
|
|
param = 3;
|
|
break;
|
|
case 'underline':
|
|
case 'steady underline':
|
|
param = 4;
|
|
break;
|
|
case 'blinking bar':
|
|
param = 5;
|
|
break;
|
|
case 'bar':
|
|
case 'steady bar':
|
|
param = 6;
|
|
break;
|
|
}
|
|
if (param === 2 && this.has('Se')) {
|
|
return this.put.Se();
|
|
}
|
|
if (this.has('Ss')) {
|
|
return this.put.Ss(param);
|
|
}
|
|
return this._write('\x1b[' + (param || 1) + ' q');
|
|
};
|
|
|
|
// CSI Ps " q
|
|
// Select character protection attribute (DECSCA). Valid values
|
|
// for the parameter:
|
|
// Ps = 0 -> DECSED and DECSEL can erase (default).
|
|
// Ps = 1 -> DECSED and DECSEL cannot erase.
|
|
// Ps = 2 -> DECSED and DECSEL can erase.
|
|
Program.prototype.decsca =
|
|
Program.prototype.setCharProtectionAttr = function(param) {
|
|
return this._write('\x1b[' + (param || 0) + '"q');
|
|
};
|
|
|
|
// CSI ? Pm r
|
|
// Restore DEC Private Mode Values. The value of Ps previously
|
|
// saved is restored. Ps values are the same as for DECSET.
|
|
Program.prototype.restorePrivateValues = function() {
|
|
return this._write('\x1b[?' + slice.call(arguments).join(';') + 'r');
|
|
};
|
|
|
|
// CSI Pt; Pl; Pb; Pr; Ps$ r
|
|
// Change Attributes in Rectangular Area (DECCARA), VT400 and up.
|
|
// Pt; Pl; Pb; Pr denotes the rectangle.
|
|
// Ps denotes the SGR attributes to change: 0, 1, 4, 5, 7.
|
|
// NOTE: xterm doesn't enable this code by default.
|
|
Program.prototype.deccara =
|
|
Program.prototype.setAttrInRectangle = function() {
|
|
return this._write('\x1b[' + slice.call(arguments).join(';') + '$r');
|
|
};
|
|
|
|
// CSI ? Pm s
|
|
// Save DEC Private Mode Values. Ps values are the same as for
|
|
// DECSET.
|
|
Program.prototype.savePrivateValues = function(params) {
|
|
return this._write('\x1b[?' + slice.call(arguments).join(';') + 's');
|
|
};
|
|
|
|
// CSI Ps ; Ps ; Ps t
|
|
// Window manipulation (from dtterm, as well as extensions).
|
|
// These controls may be disabled using the allowWindowOps
|
|
// resource. Valid values for the first (and any additional
|
|
// parameters) are:
|
|
// Ps = 1 -> De-iconify window.
|
|
// Ps = 2 -> Iconify window.
|
|
// Ps = 3 ; x ; y -> Move window to [x, y].
|
|
// Ps = 4 ; height ; width -> Resize the xterm window to
|
|
// height and width in pixels.
|
|
// Ps = 5 -> Raise the xterm window to the front of the stack-
|
|
// ing order.
|
|
// Ps = 6 -> Lower the xterm window to the bottom of the
|
|
// stacking order.
|
|
// Ps = 7 -> Refresh the xterm window.
|
|
// Ps = 8 ; height ; width -> Resize the text area to
|
|
// [height;width] in characters.
|
|
// Ps = 9 ; 0 -> Restore maximized window.
|
|
// Ps = 9 ; 1 -> Maximize window (i.e., resize to screen
|
|
// size).
|
|
// Ps = 1 0 ; 0 -> Undo full-screen mode.
|
|
// Ps = 1 0 ; 1 -> Change to full-screen.
|
|
// Ps = 1 1 -> Report xterm window state. If the xterm window
|
|
// is open (non-iconified), it returns CSI 1 t . If the xterm
|
|
// window is iconified, it returns CSI 2 t .
|
|
// Ps = 1 3 -> Report xterm window position. Result is CSI 3
|
|
// ; x ; y t
|
|
// Ps = 1 4 -> Report xterm window in pixels. Result is CSI
|
|
// 4 ; height ; width t
|
|
// Ps = 1 8 -> Report the size of the text area in characters.
|
|
// Result is CSI 8 ; height ; width t
|
|
// Ps = 1 9 -> Report the size of the screen in characters.
|
|
// Result is CSI 9 ; height ; width t
|
|
// Ps = 2 0 -> Report xterm window's icon label. Result is
|
|
// OSC L label ST
|
|
// Ps = 2 1 -> Report xterm window's title. Result is OSC l
|
|
// label ST
|
|
// Ps = 2 2 ; 0 -> Save xterm icon and window title on
|
|
// stack.
|
|
// Ps = 2 2 ; 1 -> Save xterm icon title on stack.
|
|
// Ps = 2 2 ; 2 -> Save xterm window title on stack.
|
|
// Ps = 2 3 ; 0 -> Restore xterm icon and window title from
|
|
// stack.
|
|
// Ps = 2 3 ; 1 -> Restore xterm icon title from stack.
|
|
// Ps = 2 3 ; 2 -> Restore xterm window title from stack.
|
|
// Ps >= 2 4 -> Resize to Ps lines (DECSLPP).
|
|
Program.prototype.manipulateWindow = function() {
|
|
var args = slice.call(arguments);
|
|
|
|
var callback = typeof args[args.length-1] === 'function'
|
|
? args.pop()
|
|
: function() {};
|
|
|
|
return this.response('window-manipulation',
|
|
'\x1b[' + args.join(';') + 't', callback);
|
|
};
|
|
|
|
Program.prototype.getWindowSize = function(callback) {
|
|
return this.manipulateWindow(18, callback);
|
|
};
|
|
|
|
// CSI Pt; Pl; Pb; Pr; Ps$ t
|
|
// Reverse Attributes in Rectangular Area (DECRARA), VT400 and
|
|
// up.
|
|
// Pt; Pl; Pb; Pr denotes the rectangle.
|
|
// Ps denotes the attributes to reverse, i.e., 1, 4, 5, 7.
|
|
// NOTE: xterm doesn't enable this code by default.
|
|
Program.prototype.decrara =
|
|
Program.prototype.reverseAttrInRectangle = function(params) {
|
|
return this._write('\x1b[' + slice.call(arguments).join(';') + '$t');
|
|
};
|
|
|
|
// CSI > Ps; Ps t
|
|
// Set one or more features of the title modes. Each parameter
|
|
// enables a single feature.
|
|
// Ps = 0 -> Set window/icon labels using hexadecimal.
|
|
// Ps = 1 -> Query window/icon labels using hexadecimal.
|
|
// Ps = 2 -> Set window/icon labels using UTF-8.
|
|
// Ps = 3 -> Query window/icon labels using UTF-8. (See dis-
|
|
// cussion of "Title Modes")
|
|
Program.prototype.setTitleModeFeature = function(params) {
|
|
return this._write('\x1b[>' + slice.call(arguments).join(';') + 't');
|
|
};
|
|
|
|
// CSI Ps SP t
|
|
// Set warning-bell volume (DECSWBV, VT520).
|
|
// Ps = 0 or 1 -> off.
|
|
// Ps = 2 , 3 or 4 -> low.
|
|
// Ps = 5 , 6 , 7 , or 8 -> high.
|
|
Program.prototype.decswbv =
|
|
Program.prototype.setWarningBellVolume = function(params) {
|
|
return this._write('\x1b[' + (param || '') + ' t');
|
|
};
|
|
|
|
// CSI Ps SP u
|
|
// Set margin-bell volume (DECSMBV, VT520).
|
|
// Ps = 1 -> off.
|
|
// Ps = 2 , 3 or 4 -> low.
|
|
// Ps = 0 , 5 , 6 , 7 , or 8 -> high.
|
|
Program.prototype.decsmbv =
|
|
Program.prototype.setMarginBellVolume = function(params) {
|
|
return this._write('\x1b[' + (param || '') + ' u');
|
|
};
|
|
|
|
// CSI Pt; Pl; Pb; Pr; Pp; Pt; Pl; Pp$ v
|
|
// Copy Rectangular Area (DECCRA, VT400 and up).
|
|
// Pt; Pl; Pb; Pr denotes the rectangle.
|
|
// Pp denotes the source page.
|
|
// Pt; Pl denotes the target location.
|
|
// Pp denotes the target page.
|
|
// NOTE: xterm doesn't enable this code by default.
|
|
Program.prototype.deccra =
|
|
Program.prototype.copyRectangle = function(params) {
|
|
return this._write('\x1b[' + slice.call(arguments).join(';') + '$v');
|
|
};
|
|
|
|
// CSI Pt ; Pl ; Pb ; Pr ' w
|
|
// Enable Filter Rectangle (DECEFR), VT420 and up.
|
|
// Parameters are [top;left;bottom;right].
|
|
// Defines the coordinates of a filter rectangle and activates
|
|
// it. Anytime the locator is detected outside of the filter
|
|
// rectangle, an outside rectangle event is generated and the
|
|
// rectangle is disabled. Filter rectangles are always treated
|
|
// as "one-shot" events. Any parameters that are omitted default
|
|
// to the current locator position. If all parameters are omit-
|
|
// ted, any locator motion will be reported. DECELR always can-
|
|
// cels any prevous rectangle definition.
|
|
Program.prototype.decefr =
|
|
Program.prototype.enableFilterRectangle = function(params) {
|
|
return this._write('\x1b[' + slice.call(arguments).join(';') + '\'w');
|
|
};
|
|
|
|
// CSI Ps x Request Terminal Parameters (DECREQTPARM).
|
|
// if Ps is a "0" (default) or "1", and xterm is emulating VT100,
|
|
// the control sequence elicits a response of the same form whose
|
|
// parameters describe the terminal:
|
|
// Ps -> the given Ps incremented by 2.
|
|
// Pn = 1 <- no parity.
|
|
// Pn = 1 <- eight bits.
|
|
// Pn = 1 <- 2 8 transmit 38.4k baud.
|
|
// Pn = 1 <- 2 8 receive 38.4k baud.
|
|
// Pn = 1 <- clock multiplier.
|
|
// Pn = 0 <- STP flags.
|
|
Program.prototype.decreqtparm =
|
|
Program.prototype.requestParameters = function(params) {
|
|
return this._write('\x1b[' + (param || 0) + 'x');
|
|
};
|
|
|
|
// CSI Ps x Select Attribute Change Extent (DECSACE).
|
|
// Ps = 0 -> from start to end position, wrapped.
|
|
// Ps = 1 -> from start to end position, wrapped.
|
|
// Ps = 2 -> rectangle (exact).
|
|
Program.prototype.decsace =
|
|
Program.prototype.selectChangeExtent = function(params) {
|
|
return this._write('\x1b[' + (param || 0) + 'x');
|
|
};
|
|
|
|
// CSI Pc; Pt; Pl; Pb; Pr$ x
|
|
// Fill Rectangular Area (DECFRA), VT420 and up.
|
|
// Pc is the character to use.
|
|
// Pt; Pl; Pb; Pr denotes the rectangle.
|
|
// NOTE: xterm doesn't enable this code by default.
|
|
Program.prototype.decfra =
|
|
Program.prototype.fillRectangle = function(params) {
|
|
return this._write('\x1b[' + slice.call(arguments).join(';') + '$x');
|
|
};
|
|
|
|
// CSI Ps ; Pu ' z
|
|
// Enable Locator Reporting (DECELR).
|
|
// Valid values for the first parameter:
|
|
// Ps = 0 -> Locator disabled (default).
|
|
// Ps = 1 -> Locator enabled.
|
|
// Ps = 2 -> Locator enabled for one report, then disabled.
|
|
// The second parameter specifies the coordinate unit for locator
|
|
// reports.
|
|
// Valid values for the second parameter:
|
|
// Pu = 0 <- or omitted -> default to character cells.
|
|
// Pu = 1 <- device physical pixels.
|
|
// Pu = 2 <- character cells.
|
|
Program.prototype.decelr =
|
|
Program.prototype.enableLocatorReporting = function(params) {
|
|
return this._write('\x1b[' + slice.call(arguments).join(';') + '\'z');
|
|
};
|
|
|
|
// CSI Pt; Pl; Pb; Pr$ z
|
|
// Erase Rectangular Area (DECERA), VT400 and up.
|
|
// Pt; Pl; Pb; Pr denotes the rectangle.
|
|
// NOTE: xterm doesn't enable this code by default.
|
|
Program.prototype.decera =
|
|
Program.prototype.eraseRectangle = function(params) {
|
|
return this._write('\x1b[' + slice.call(arguments).join(';') + '$z');
|
|
};
|
|
|
|
// CSI Pm ' {
|
|
// Select Locator Events (DECSLE).
|
|
// Valid values for the first (and any additional parameters)
|
|
// are:
|
|
// Ps = 0 -> only respond to explicit host requests (DECRQLP).
|
|
// (This is default). It also cancels any filter
|
|
// rectangle.
|
|
// Ps = 1 -> report button down transitions.
|
|
// Ps = 2 -> do not report button down transitions.
|
|
// Ps = 3 -> report button up transitions.
|
|
// Ps = 4 -> do not report button up transitions.
|
|
Program.prototype.decsle =
|
|
Program.prototype.setLocatorEvents = function(params) {
|
|
return this._write('\x1b[' + slice.call(arguments).join(';') + '\'{');
|
|
};
|
|
|
|
// CSI Pt; Pl; Pb; Pr$ {
|
|
// Selective Erase Rectangular Area (DECSERA), VT400 and up.
|
|
// Pt; Pl; Pb; Pr denotes the rectangle.
|
|
Program.prototype.decsera =
|
|
Program.prototype.selectiveEraseRectangle = function(params) {
|
|
return this._write('\x1b[' + slice.call(arguments).join(';') + '${');
|
|
};
|
|
|
|
// CSI Ps ' |
|
|
// Request Locator Position (DECRQLP).
|
|
// Valid values for the parameter are:
|
|
// Ps = 0 , 1 or omitted -> transmit a single DECLRP locator
|
|
// report.
|
|
|
|
// If Locator Reporting has been enabled by a DECELR, xterm will
|
|
// respond with a DECLRP Locator Report. This report is also
|
|
// generated on button up and down events if they have been
|
|
// enabled with a DECSLE, or when the locator is detected outside
|
|
// of a filter rectangle, if filter rectangles have been enabled
|
|
// with a DECEFR.
|
|
|
|
// -> CSI Pe ; Pb ; Pr ; Pc ; Pp & w
|
|
|
|
// Parameters are [event;button;row;column;page].
|
|
// Valid values for the event:
|
|
// Pe = 0 -> locator unavailable - no other parameters sent.
|
|
// Pe = 1 -> request - xterm received a DECRQLP.
|
|
// Pe = 2 -> left button down.
|
|
// Pe = 3 -> left button up.
|
|
// Pe = 4 -> middle button down.
|
|
// Pe = 5 -> middle button up.
|
|
// Pe = 6 -> right button down.
|
|
// Pe = 7 -> right button up.
|
|
// Pe = 8 -> M4 button down.
|
|
// Pe = 9 -> M4 button up.
|
|
// Pe = 1 0 -> locator outside filter rectangle.
|
|
// ``button'' parameter is a bitmask indicating which buttons are
|
|
// pressed:
|
|
// Pb = 0 <- no buttons down.
|
|
// Pb & 1 <- right button down.
|
|
// Pb & 2 <- middle button down.
|
|
// Pb & 4 <- left button down.
|
|
// Pb & 8 <- M4 button down.
|
|
// ``row'' and ``column'' parameters are the coordinates of the
|
|
// locator position in the xterm window, encoded as ASCII deci-
|
|
// mal.
|
|
// The ``page'' parameter is not used by xterm, and will be omit-
|
|
// ted.
|
|
Program.prototype.decrqlp =
|
|
Program.prototype.req_mouse_pos =
|
|
Program.prototype.reqmp =
|
|
Program.prototype.requestLocatorPosition = function(params, callback) {
|
|
// See also:
|
|
// get_mouse / getm / Gm
|
|
// mouse_info / minfo / Mi
|
|
// Correct for tput?
|
|
if (this.has('req_mouse_pos')) {
|
|
var code = this.tput.req_mouse_pos.apply(this.tput, params);
|
|
return this.response('locator-position', code, callback);
|
|
}
|
|
return this.response('locator-position',
|
|
'\x1b[' + (param || '') + '\'|', callback);
|
|
};
|
|
|
|
// CSI P m SP }
|
|
// Insert P s Column(s) (default = 1) (DECIC), VT420 and up.
|
|
// NOTE: xterm doesn't enable this code by default.
|
|
Program.prototype.decic =
|
|
Program.prototype.insertColumns = function() {
|
|
return this._write('\x1b[' + slice.call(arguments).join(';') + ' }');
|
|
};
|
|
|
|
// CSI P m SP ~
|
|
// Delete P s Column(s) (default = 1) (DECDC), VT420 and up
|
|
// NOTE: xterm doesn't enable this code by default.
|
|
Program.prototype.decdc =
|
|
Program.prototype.deleteColumns = function() {
|
|
return this._write('\x1b[' + slice.call(arguments).join(';') + ' ~');
|
|
};
|
|
|
|
Program.prototype.out = function(name) {
|
|
this.ret = true;
|
|
var out = this[name].apply(this, args);
|
|
this.ret = false;
|
|
return out;
|
|
};
|
|
|
|
Program.prototype.sigtstp = function(callback) {
|
|
var resume = this.pause();
|
|
|
|
process.once('SIGCONT', function() {
|
|
resume();
|
|
if (callback) callback();
|
|
});
|
|
|
|
process.kill(process.pid, 'SIGTSTP');
|
|
};
|
|
|
|
Program.prototype.pause = function(callback) {
|
|
var self = this
|
|
, isAlt = this.isAlt
|
|
, mouseEnabled = this.mouseEnabled;
|
|
|
|
this.lsaveCursor('pause');
|
|
//this.csr(0, screen.height - 1);
|
|
if (isAlt) this.normalBuffer();
|
|
this.showCursor();
|
|
if (mouseEnabled) this.disableMouse();
|
|
|
|
var write = this.output.write;
|
|
this.output.write = function() {};
|
|
this.input.setRawMode(false);
|
|
this.input.pause();
|
|
|
|
return this._resume = function() {
|
|
delete self._resume;
|
|
|
|
self.input.setRawMode(true);
|
|
self.input.resume();
|
|
self.output.write = write;
|
|
|
|
if (isAlt) self.alternateBuffer();
|
|
//self.csr(0, screen.height - 1);
|
|
if (mouseEnabled) self.enableMouse();
|
|
self.lrestoreCursor('pause', true);
|
|
|
|
if (callback) callback();
|
|
};
|
|
};
|
|
|
|
Program.prototype.resume = function(callback) {
|
|
if (this._resume) return this._resume();
|
|
};
|
|
|
|
/**
|
|
* Helpers
|
|
*/
|
|
|
|
// We could do this easier by just manipulating the _events object, or for
|
|
// older versions of node, manipulating the array returned by listeners(), but
|
|
// neither of these methods are guaranteed to work in future versions of node.
|
|
function unshiftEvent(obj, event, listener) {
|
|
var listeners = obj.listeners(event);
|
|
obj.removeAllListeners(event);
|
|
obj.on(event, listener);
|
|
listeners.forEach(function(listener) {
|
|
obj.on(event, listener);
|
|
});
|
|
};
|
|
|
|
function merge(out) {
|
|
slice.call(arguments, 1).forEach(function(obj) {
|
|
Object.keys(obj).forEach(function(key) {
|
|
out[key] = obj[key];
|
|
});
|
|
});
|
|
return out;
|
|
}
|
|
|
|
/**
|
|
* Expose
|
|
*/
|
|
|
|
module.exports = Program;
|