/** * TODO: * scrollRegion http://www.gnu.org/software/termutils/manual/termcap-1.3/html_node/termcap_28.html * http://www.shaels.net/index.php/propterm/documents/16-scroll-control * http://www.vt100.net/docs/vt102-ug/chapter5.html * * https://github.com/Gottox/terminal.js * */ /** * Terminal Emulation References: * http://vt100.net/ * http://invisible-island.net/xterm/ctlseqs/ctlseqs.txt * http://invisible-island.net/xterm/ctlseqs/ctlseqs.html * http://invisible-island.net/vttest/ * http://www.inwap.com/pdp10/ansicode.txt * http://linux.die.net/man/4/console_codes * http://linux.die.net/man/7/urxvt */ define(function(require, exports, module) { /** * States */ var EventEmitter = require("events").EventEmitter; var wc = require("./wc"); var normal = 0 , escaped = 1 , csi = 2 , osc = 3 , charset = 4 , dcs = 5 , ignore = 6; /** * Terminal */ function Terminal(cols, rows, handler) { this.cols = cols || Terminal.geometry[0]; this.rows = rows || Terminal.geometry[1]; //if (handler) this.handler = handler; if (handler) this.on('input', handler); this.ybase = 0; this.ydisp = 0; this.x = 0; this.y = 0; this.cursorState = 0; this.cursorHidden = false; this.convertEol = false; this.state = 0; this.scrollTop = 0; this.scrollBottom = this.rows - 1; // modes this.applicationKeypad = false; this.originMode = false; this.insertMode = false; this.wraparoundMode = false; this.normal = null; // charset this.charset = null; this.gcharset = null; this.glevel = 0; this.charsets = [null]; // mouse properties this.decLocator; this.x10Mouse; this.vt200Mouse; this.vt300Mouse; this.normalMouse; this.mouseEvents; this.sendFocus; this.utfMouse; this.sgrMouse; this.urxvtMouse; // misc this.element; this.sizeChanges; this.refreshStart; this.refreshEnd; this.savedX; this.savedY; this.savedCols; // stream this.readable = true; this.writable = true; this.defAttr = (257 << 9) | 256; this.curAttr = this.defAttr; this.ch = [this.defAttr, ""]; this.params = []; this.currentParam = 0; this.prefix = ''; this.postfix = ''; this.lines = []; //TODO optimize lines var i = this.rows; while (i--) { this.lines.push(this.blankLine()); } this.tabs; this.setupStops(); this.refreshSync = this.refreshSync.bind(this); this.refreshEnd = this.refreshStart = 0; // options this.$resetScreenOnStateChange; this.showCursor(); } Terminal.prototype = new EventEmitter(); Terminal.colors = []; Terminal.setColors = require("./term_colors"); Terminal.setColors(); /** * Options */ Terminal.termName = 'xterm'; Terminal.geometry = [80, 30]; Terminal.cursorBlink = true; Terminal.visualBell = false; Terminal.popOnBell = false; Terminal.scrollback = 1000; Terminal.screenKeys = false; Terminal.programFeatures = false; Terminal.debug = false; Terminal.prototype.getCharAt = function(row, column) { return this.lines[this.ybase + row] && this.lines[this.ybase + row][column]; }; Terminal.prototype.$debugCharsAt = function(row) { return (this.lines[this.ybase + row] || []).map(function(b) { return b && b[1]; }); }; Terminal.prototype.focus = function() { // TODO is sendFocus needed? if (Terminal.focus === this) return; if (Terminal.focus) { Terminal.focus.cursorState = 0; if (Terminal.focus.sendFocus) Terminal.focus.send('\x1b[O'); } if (this.sendFocus) this.send('\x1b[I'); }; /** * Open Terminal */ Terminal.prototype.open = function() { }; /** * Destroy Terminal */ Terminal.prototype.destroy = function() { this.readable = false; this.writable = false; this._events = {}; this.handler = function() {}; this.write = function() {}; //this.emit('close'); }; Terminal.prototype.setOutputHistory = function(data, trim) { var tmp = new Terminal(this.cols, this.rows); tmp.convertEol = true; if (trim) { var lines = data.split("\n").map(function(l) {return l.trimRight()}); if (!lines[lines.length - 1]) lines.pop(); data = lines.join("\n"); } tmp.write(data); tmp.resize(); // make sure leading empty lines are removed this.lines = tmp.lines.slice(0, tmp.ybase) .concat(this.lines.slice(this.ybase)); this.ybase = tmp.ybase; // this.refresh(0, this.rows); }; /** * Rendering Engine */ // In the screen buffer, each character // is stored as a an array with a character // and a 32-bit integer. // First value: a utf-16 character. // Second value: // Next 9 bits: background color (0-511). // Next 9 bits: foreground color (0-511). // Next 14 bits: a mask for misc. flags: // 1=bold, 2=underline, 4=inverse Terminal.prototype.refresh = function(start, end) { this.refreshStart = Math.min(this.refreshStart, start) || 0; this.refreshEnd = Math.max(this.refreshEnd, end) || 0; var d = this.refreshEnd - this.refreshStart; if (!this.refreshTimer) { if (!d || this.isRenderPending()) { this.refreshSync(); } else { var timeout = d < 2 ? 10 : 60; this.refreshTimer = setTimeout(this.refreshSync, timeout); } } }; Terminal.prototype.refreshSync = function() { this.refreshView(this.refreshStart, this.refreshEnd); this.refreshStart = this.refreshEnd = this.y; this.refreshTimer = null; }; Terminal.prototype.refreshView = function(start, end) { // implemented by renderer }; Terminal.prototype.isRenderPending = function() { // implemented by renderer }; Terminal.prototype.cursorBlink = function() { if (Terminal.focus !== this || !Terminal.cursorBlink || Terminal.mousePressed) return; this.cursorState ^= 1; }; Terminal.prototype.hideCursor = function() { this.cursorState = 0; }; Terminal.prototype.showCursor = function() { if (!this.cursorState) { this.cursorState = 1; } }; Terminal.prototype.startBlink = function() { if (!Terminal.cursorBlink) return; var self = this; this._blinker = function() { self.cursorBlink(); }; this._blink = setInterval(this._blinker, 500); }; Terminal.prototype.refreshBlink = function() { if (!Terminal.cursorBlink) return; clearInterval(this._blink); this._blink = setInterval(this._blinker, 500); }; Terminal.prototype.clear = function() {//TODO optimize lines this.lines.splice(0, this.ybase + this.y); this.lines.splice(1, this.lines.length-1); while (this.lines.length < this.rows) { this.lines.push(this.blankLine()); } this.y = 0; this.ybase = 0; this.ydisp = 0; this.refresh(0, this.rows - 1); this.emit("clear"); }; Terminal.prototype.clearScreen = function(padding) {//TODO optimize lines var i = this.scrollBottom - this.scrollTop; while (i > 0 && this.isLineEmpty(this.lines[i + this.ybase])) { i--; } i += padding | 0; while (i--> 0) { this.scroll(); } this.emit("input"); }; Terminal.prototype.scroll = function() {//TODO optimize lines var row; if (++this.ybase === Terminal.scrollback) { this.ybase = this.ybase / 2 | 0; this.lines = this.lines.slice(-(this.ybase + this.rows) + 1); } this.ydisp = this.ybase; // last line row = this.ybase + this.rows - 1; // subtract the bottom scroll region row -= this.rows - 1 - this.scrollBottom; if (row === this.lines.length) { // potential optimization: // pushing is faster than splicing // when they amount to the same // behavior. this.lines.push(this.blankLine()); } else { // add our new line this.lines.splice(row, 0, this.blankLine()); } if (this.scrollTop !== 0) { if (this.ybase !== 0) { this.ybase--; this.ydisp = this.ybase; } this.lines.splice(this.ybase + this.scrollTop, 1); } // this.maxRange(); this.updateRange(this.scrollTop); this.updateRange(this.scrollBottom); }; Terminal.prototype.write = function(data) { var transformed = this.emit('beforeWrite', data); if (typeof transformed == "string") data = transformed; this.writeInternal(data); this.emit('afterWrite', data); }; Terminal.prototype.writeInternal = function(data) {//TODO optimize lines var l = data.length , i = 0 , ch; if (this.ybase !== this.ydisp) { this.ydisp = this.ybase; this.maxRange(); } // Terminal.log(data); for (; i < l; i++) { ch = data[i]; switch (this.state) { case normal: switch (ch) { // '\0' // case '\0': // break; // '\a' case '\x07': this.bell(); break; // '\n', '\v', '\f' case '\n': case '\x0b': case '\x0c': if (this.convertEol) { this.x = 0; } this.y++; if (this.y > this.scrollBottom) { this.y--; this.scroll(); } this.emit("newline", { y: this.y, lines: this.lines, ybase: this.ybase, rows: this.rows }); break; // '\r' case '\r': this.x = 0; break; // '\b' case '\x08': if (this.x > 0) { this.x--; } break; // '\t' case '\t': this.x = this.nextStop(); break; // shift out case '\x0e': this.glevel = 1; this.charset = this.charsets[1]; break; // shift in case '\x0f': this.glevel = 0; this.charset = this.charsets[0]; break; // '\e' case '\x1b': this.state = escaped; break; default: // ' ' if (ch >= ' ') { if (this.charset && this.charset[ch]) { ch = this.charset[ch]; } var lines = this.lines; var line = lines[this.y + this.ybase]; if (this.x >= this.cols) { if (line) line.wrapped = true; this.x = 0; this.y++; if (this.y > this.scrollBottom) { this.y--; this.scroll(); } } else { if (line && line.wrapped === "wait") line.wrapped = false; } var insertY = this.y + this.ybase; if (!lines[insertY]) { if (!lines.length) lines[0] = this.blankLine(); insertY = lines.length - 1; } var width = ch > "\x7f" ? wc.charWidth(ch.charCodeAt(0)) : 1; line = lines[insertY]; var x = this.x; while (line[x] && !line[x][1] && x > 0) { line[x] = [line[x][0], " "]; x--; } switch (width) { case 1: if (!this.insertMode) line[this.x] = [this.curAttr, ch]; else line[insertY].splice(this.x, 0, [this.curAttr, ch]); break; case 0: if (this.x > 0) this.x--; line[this.x] = [this.curAttr, line[this.x][1] + ch]; break; case 2: if (!this.insertMode) { line[this.x] = [this.curAttr, ch]; line[this.x + 1] = [this.curAttr, "\x00"]; } else { line[insertY].splice(this.x, 0, [this.curAttr, ch], [this.curAttr, ""]); } this.x++; break; } this.x++; this.updateRange(this.y); } break; } break; case escaped: switch (ch) { // ESC [ Control Sequence Introducer ( CSI is 0x9b). case '[': this.params = []; this.currentParam = 0; this.state = csi; break; // ESC ] Operating System Command ( OSC is 0x9d). case ']': this.params = []; this.currentParam = 0; this.state = osc; break; // ESC P Device Control String ( DCS is 0x90). case 'P': this.params = []; this.currentParam = 0; this.state = dcs; break; // ESC _ Application Program Command ( APC is 0x9f). case '_': this.state = ignore; break; // ESC ^ Privacy Message ( PM is 0x9e). case '^': this.state = ignore; break; // ESC c Full Reset (RIS). case 'c': this.reset(); break; // ESC E Next Line ( NEL is 0x85). // ESC D Index ( IND is 0x84). case 'E': this.x = 0; ; case 'D': this.index(); break; // ESC M Reverse Index ( RI is 0x8d). case 'M': this.reverseIndex(); break; // ESC % Select default/utf-8 character set. // @ = default, G = utf-8 case '%': this.charset = null; //this.glevel = 0; //this.charsets[0] = null; this.state = normal; i++; break; // ESC (,),*,+,-,. Designate G0-G2 Character Set. case '(': // <-- this seems to get all the attention case ')': case '*': case '+': case '-': case '.': switch (ch) { case '(': this.gcharset = 0; break; case ')': this.gcharset = 1; break; case '*': this.gcharset = 2; break; case '+': this.gcharset = 3; break; case '-': this.gcharset = 1; break; case '.': this.gcharset = 2; break; } this.state = charset; break; // Designate G3 Character Set (VT300). // A = ISO Latin-1 Supplemental. // Not implemented. case '/': this.gcharset = 3; this.charsets[this.gcharset] = null; if (this.glevel === this.gcharset) { this.charset = null; } this.gcharset = null; this.state = normal; i++; break; // ESC 7 Save Cursor (DECSC). case '7': this.saveCursor(); this.state = normal; break; // ESC 8 Restore Cursor (DECRC). case '8': this.restoreCursor(); this.state = normal; break; // ESC # 3 DEC line height/width case '#': this.state = normal; i++; break; // ESC H Tab Set (HTS is 0x88). case 'H': this.tabSet(); break; // ESC = Application Keypad (DECPAM). case '=': this.log('Serial port requested application keypad.'); this.applicationKeypad = true; this.state = normal; break; // ESC > Normal Keypad (DECPNM). case '>': this.log('Switching back to normal keypad.'); this.applicationKeypad = false; this.state = normal; break; default: this.state = normal; this.error('Unknown ESC control: %s.', ch); break; } break; case charset: switch (ch) { // DEC Special Character and Line Drawing Set. case '0': this.charsets[this.gcharset] = Terminal.charsets.SCLD; if (this.gcharset === this.glevel) { this.charset = Terminal.charsets.SCLD; } this.gcharset = null; break; // United States (USASCII). case 'B': default: this.charsets[this.gcharset] = null; if (this.gcharset === this.glevel) { this.charset = null; } this.gcharset = null; break; } this.state = normal; break; case osc: // OSC Ps ; Pt ST // OSC Ps ; Pt BEL // Set Text Parameters. if (ch === '\x1b' || ch === '\x07') { if (ch === '\x1b') i++; this.params.push(this.currentParam); switch (this.params[0]) { case 0: case 1: case 2: if (this.params[1]) { this.title = this.params[1]; this.handleTitle(this.title); } break; case 3: // set X property break; case 4: case 5: // change dynamic colors break; case 10: case 11: case 12: case 13: case 14: case 15: case 16: case 17: case 18: case 19: // change dynamic ui colors break; case 46: // change log file break; case 50: var args = this.params[1].match(/CursorShape=(.)/i); var shape = ['BLOCK', 'BEAM', 'UNDERLINE'][parseInt(args && args[1]) || 0]; // TODO cursor shape // dynamic font break; case 51: // emacs shell break; case 52: // manipulate selection data break; case 104: case 105: case 110: case 111: case 112: case 113: case 114: case 115: case 116: case 117: case 118: // reset colors break; } this.params = []; this.currentParam = 0; this.state = normal; } else { if (!this.params.length) { if (ch >= '0' && ch <= '9') { this.currentParam = this.currentParam * 10 + ch.charCodeAt(0) - 48; } else if (ch === ';') { this.params.push(this.currentParam); this.currentParam = ''; } } else { this.currentParam += ch; } } break; case csi: // '?', '>', '!' if (ch === '?' || ch === '>' || ch === '!') { this.prefix = ch; break; } // 0 - 9 if (ch >= '0' && ch <= '9') { this.currentParam = this.currentParam * 10 + ch.charCodeAt(0) - 48; break; } // '$', '"', ' ', '\'' if (ch === '$' || ch === '"' || ch === ' ' || ch === '\'') { this.postfix = ch; break; } this.params.push(this.currentParam); this.currentParam = 0; // ';' if (ch === ';') break; this.state = normal; switch (ch) { // CSI Ps A // Cursor Up Ps Times (default = 1) (CUU). case 'A': this.cursorUp(this.params); break; // CSI Ps B // Cursor Down Ps Times (default = 1) (CUD). case 'B': this.cursorDown(this.params); break; // CSI Ps C // Cursor Forward Ps Times (default = 1) (CUF). case 'C': this.cursorForward(this.params); break; // CSI Ps D // Cursor Backward Ps Times (default = 1) (CUB). case 'D': this.cursorBackward(this.params); break; // CSI Ps ; Ps H // Cursor Position [row;column] (default = [1,1]) (CUP). case 'H': var oldCursor = [this.y + 1, this.x + 1]; this.cursorPos(this.params); i = this.checkCursorReset(data, i, oldCursor); break; // CSI Ps J Erase in Display (ED). case 'J': this.eraseInDisplay(this.params); break; // CSI Ps K Erase in Line (EL). case 'K': this.eraseInLine(this.params); break; // CSI Pm m Character Attributes (SGR). case 'm': /** * CSI['>n'] CSI['>m'] control xterm-specific keyboard modes. * Will not implement. * see nassh and http://sourceforge.net/mailarchive/forum.php?thread_name=F892202E-B27B-400E-A5BD-596A124E058F%40high5.nl&forum_name=tmux-users */ if (this.prefix == ">") break; this.charAttributes(this.params); break; // CSI Ps n Device Status Report (DSR). case 'n': if (this.prefix == ">") break; this.deviceStatus(this.params); break; /** * Additions */ // CSI Ps @ // Insert Ps (Blank) Character(s) (default = 1) (ICH). case '@': this.insertChars(this.params); break; // CSI Ps E // Cursor Next Line Ps Times (default = 1) (CNL). case 'E': this.cursorNextLine(this.params); break; // CSI Ps F // Cursor Preceding Line Ps Times (default = 1) (CNL). case 'F': this.cursorPrecedingLine(this.params); break; // CSI Ps G // Cursor Character Absolute [column] (default = [row,1]) (CHA). case 'G': this.cursorCharAbsolute(this.params); break; // CSI Ps L // Insert Ps Line(s) (default = 1) (IL). case 'L': this.insertLines(this.params); break; // CSI Ps M // Delete Ps Line(s) (default = 1) (DL). case 'M': this.deleteLines(this.params); break; // CSI Ps P // Delete Ps Character(s) (default = 1) (DCH). case 'P': this.deleteChars(this.params); break; // CSI Ps X // Erase Ps Character(s) (default = 1) (ECH). case 'X': this.eraseChars(this.params); break; // CSI Pm ` Character Position Absolute // [column] (default = [row,1]) (HPA). case '`': this.charPosAbsolute(this.params); break; // 141 61 a * HPR - // Horizontal Position Relative case 'a': this.HPositionRelative(this.params); break; // CSI P s c // Send Device Attributes (Primary DA). // CSI > P s c // Send Device Attributes (Secondary DA) case 'c': this.sendDeviceAttributes(this.params); break; // CSI Pm d // Line Position Absolute [row] (default = [1,column]) (VPA). case 'd': this.linePosAbsolute(this.params); break; // 145 65 e * VPR - Vertical Position Relative case 'e': this.VPositionRelative(this.params); break; // CSI Ps ; Ps f // Horizontal and Vertical Position [row;column] (default = // [1,1]) (HVP). case 'f': this.HVPosition(this.params); break; // CSI Pm h Set Mode (SM). // CSI ? Pm h - mouse escape codes, cursor escape codes case 'h': this.setMode(this.params); break; // CSI Pm l Reset Mode (RM). // CSI ? Pm l case 'l': this.resetMode(this.params); break; // CSI Ps ; Ps r // Set Scrolling Region [top;bottom] (default = full size of win- // dow) (DECSTBM). // CSI ? Pm r case 'r': this.setScrollRegion(this.params); break; // CSI s // Save cursor (ANSI.SYS). case 's': this.saveCursor(this.params); break; // CSI u // Restore cursor (ANSI.SYS). case 'u': this.restoreCursor(this.params); break; /** * Lesser Used */ // CSI Ps I // Cursor Forward Tabulation Ps pane stops (default = 1) (CHT). case 'I': this.cursorForwardTab(this.params); break; // CSI Ps S Scroll up Ps lines (default = 1) (SU). case 'S': this.scrollUp(this.params); break; // CSI Ps T Scroll down Ps lines (default = 1) (SD). // CSI Ps ; Ps ; Ps ; Ps ; Ps T // CSI > Ps; Ps T case 'T': // if (this.prefix === '>') { // this.resetTitleModes(this.params); // break; // } // if (this.params.length > 2) { // this.initMouseTracking(this.params); // break; // } if (this.params.length < 2 && !this.prefix) { this.scrollDown(this.params); } break; // CSI Ps Z // Cursor Backward Tabulation Ps pane stops (default = 1) (CBT). case 'Z': this.cursorBackwardTab(this.params); break; // CSI Ps b Repeat the preceding graphic character Ps times (REP). case 'b': this.repeatPrecedingCharacter(this.params); break; // CSI Ps g Tab Clear (TBC). case 'g': this.tabClear(this.params); break; // CSI Pm i Media Copy (MC). // CSI ? Pm i // case 'i': // this.mediaCopy(this.params); // break; // CSI Pm m Character Attributes (SGR). // CSI > Ps; Ps m // case 'm': // duplicate // if (this.prefix === '>') { // this.setResources(this.params); // } else { // this.charAttributes(this.params); // } // break; // CSI Ps n Device Status Report (DSR). // CSI > Ps n // case 'n': // duplicate // if (this.prefix === '>') { // this.disableModifiers(this.params); // } else { // this.deviceStatus(this.params); // } // break; // CSI > Ps p Set pointer mode. // CSI ! p Soft terminal reset (DECSTR). // CSI Ps$ p // Request ANSI mode (DECRQM). // CSI ? Ps$ p // Request DEC private mode (DECRQM). // CSI Ps ; Ps " p case 'p': switch (this.prefix) { // case '>': // this.setPointerMode(this.params); // break; case '!': this.softReset(this.params); break; // case '?': // if (this.postfix === '$') { // this.requestPrivateMode(this.params); // } // break; // default: // if (this.postfix === '"') { // this.setConformanceLevel(this.params); // } else if (this.postfix === '$') { // this.requestAnsiMode(this.params); // } // break; } break; // CSI Ps q Load LEDs (DECLL). // CSI Ps SP q // CSI Ps " q case 'q': if (this.postfix === ' ') { this.setCursorStyle(this.params); break; } if (this.postfix === '"') { this.setCharProtectionAttr(this.params); break; } this.loadLEDs(this.params); break; // CSI Ps ; Ps r // Set Scrolling Region [top;bottom] (default = full size of win- // dow) (DECSTBM). // CSI ? Pm r // CSI Pt; Pl; Pb; Pr; Ps$ r // case 'r': // duplicate // if (this.prefix === '?') { // this.restorePrivateValues(this.params); // } else if (this.postfix === '$') { // this.setAttrInRectangle(this.params); // } else { // this.setScrollRegion(this.params); // } // break; // CSI s Save cursor (ANSI.SYS). // CSI ? Pm s // case 's': // duplicate // if (this.prefix === '?') { // this.savePrivateValues(this.params); // } else { // this.saveCursor(this.params); // } // break; // CSI Ps ; Ps ; Ps t // CSI Pt; Pl; Pb; Pr; Ps$ t // CSI > Ps; Ps t // CSI Ps SP t // case 't': // if (this.postfix === '$') { // this.reverseAttrInRectangle(this.params); // } else if (this.postfix === ' ') { // this.setWarningBellVolume(this.params); // } else { // if (this.prefix === '>') { // this.setTitleModeFeature(this.params); // } else { // this.manipulateWindow(this.params); // } // } // break; // CSI u Restore cursor (ANSI.SYS). // CSI Ps SP u // case 'u': // duplicate // if (this.postfix === ' ') { // this.setMarginBellVolume(this.params); // } else { // this.restoreCursor(this.params); // } // break; // CSI Pt; Pl; Pb; Pr; Pp; Pt; Pl; Pp$ v // case 'v': // if (this.postfix === '$') { // this.copyRectagle(this.params); // } // break; // CSI Pt ; Pl ; Pb ; Pr ' w // case 'w': // if (this.postfix === '\'') { // this.enableFilterRectangle(this.params); // } // break; // CSI Ps x Request Terminal Parameters (DECREQTPARM). // CSI Ps x Select Attribute Change Extent (DECSACE). // CSI Pc; Pt; Pl; Pb; Pr$ x // case 'x': // if (this.postfix === '$') { // this.fillRectangle(this.params); // } else { // this.requestParameters(this.params); // //this.__(this.params); // } // break; // CSI Ps ; Pu ' z // CSI Pt; Pl; Pb; Pr$ z // case 'z': // if (this.postfix === '\'') { // this.enableLocatorReporting(this.params); // } else if (this.postfix === '$') { // this.eraseRectangle(this.params); // } // break; // CSI Pm ' { // CSI Pt; Pl; Pb; Pr$ { // case '{': // if (this.postfix === '\'') { // this.setLocatorEvents(this.params); // } else if (this.postfix === '$') { // this.selectiveEraseRectangle(this.params); // } // break; // CSI Ps ' | // case '|': // if (this.postfix === '\'') { // this.requestLocatorPosition(this.params); // } // break; // CSI P m SP } // Insert P s Column(s) (default = 1) (DECIC), VT420 and up. // case '}': // if (this.postfix === ' ') { // this.insertColumns(this.params); // } // break; // CSI P m SP ~ // Delete P s Column(s) (default = 1) (DECDC), VT420 and up // case '~': // if (this.postfix === ' ') { // this.deleteColumns(this.params); // } // break; default: this.error('Unknown CSI code: %s.', ch); break; } this.prefix = ''; this.postfix = ''; break; case dcs: if (ch === '\x1b' || ch === '\x07') { if (ch === '\x1b') i++; switch (this.prefix) { // User-Defined Keys (DECUDK). case '': break; // Request Status String (DECRQSS). // test: echo -e '\eP$q"p\e\\' case '$q': var pt = this.currentParam , valid = false; switch (pt) { // DECSCA case '"q': pt = '0"q'; break; // DECSCL case '"p': pt = '61"p'; break; // DECSTBM case 'r': pt = '' + (this.scrollTop + 1) + ';' + (this.scrollBottom + 1) + 'r'; break; // SGR case 'm': pt = '0m'; break; default: this.error('Unknown DCS Pt: %s.', pt); pt = ''; break; } this.send('\x1bP' + +valid + '$r' + pt + '\x1b\\'); break; // Set Termcap/Terminfo Data (xterm, experimental). case '+p': break; // Request Termcap/Terminfo String (xterm, experimental) // Regular xterm does not even respond to this sequence. // This can cause a small glitch in vim. // test: echo -ne '\eP+q6b64\e\\' case '+q': var pt = this.currentParam , valid = false; this.send('\x1bP' + +valid + '+r' + pt + '\x1b\\'); break; default: this.error('Unknown DCS prefix: %s.', this.prefix); break; } this.currentParam = 0; this.prefix = ''; this.state = normal; } else if (!this.currentParam) { if (!this.prefix && ch !== '$' && ch !== '+') { this.currentParam = ch; } else if (this.prefix.length === 2) { this.currentParam = ch; } else { this.prefix += ch; } } else { this.currentParam += ch; } break; case ignore: // For PM and APC. if (ch === '\x1b' || ch === '\x07') { if (ch === '\x1b') i++; this.state = normal; } break; } } this.updateRange(this.y); this.refresh(this.refreshStart, this.refreshEnd); }; Terminal.prototype.writeln = function(data) { this.write(data + '\r\n'); }; Terminal.prototype.setgLevel = function(g) { this.glevel = g; this.charset = this.charsets[g]; }; Terminal.prototype.setgCharset = function(g, charset) { this.charsets[g] = charset; if (this.glevel === g) { this.charset = charset; } }; Terminal.prototype.send = function(data) { this.handler(data); }; Terminal.prototype.bell = function() { if (!Terminal.visualBell) return; var self = this; this.element.style.borderColor = 'white'; setTimeout(function() { self.element.style.borderColor = ''; }, 10); if (Terminal.popOnBell) this.focus(); }; Terminal.prototype.log = function() { if (!Terminal.debug) return; if (!window.console || !window.console.log) return; var args = Array.prototype.slice.call(arguments); window.console.log.apply(window.console, args); }; Terminal.prototype.error = function() { if (!Terminal.debug) return; if (!window.console || !window.console.error) return; var args = Array.prototype.slice.call(arguments); window.console.error.apply(window.console, args); }; Terminal.prototype.isLineEmpty = function(line) { return !line || !line.some(function(x){ return x[1] }); }; Terminal.prototype.isSmallScreen = function() { function isEmpty(x){ return x[1] === "\xb7" || x[1] === "\u2500" } var col = this.cols - 1; var row = Math.min(this.rows, this.lines.length) - 1; var line = this.lines[row]; for (var i = 0; i <= col; i++) { if (!line[i] || !isEmpty(line[i])) break; } if (i == col + 1) return true; for (var i = 0; i <= row; i++) { var x = this.lines[this.ybase + i]; if (!x || !x[col] || !isEmpty(x[col])) return false; } return true; }; Terminal.prototype.noScrollBack = function() { return (this.applicationKeypad || this.mouseEvents || this.tmuxDotCover); }; Terminal.prototype.setSize = function(x, y, local) { // this.sizeChanges.push({x:x, y:y}); if (this.checkResizeTimer) clearTimeout(this.checkResizeTimer); this.checkResizeTimer = setTimeout(function() { this.checkResizeTimer = null; }.bind(this), 1000); }; // after resize we can get data that still assumes the old size // usually this doesn't matter unless data we got tries to redraw whole screen // to prevent that we listen for Terminal.prototype.checkCursorReset = function(data, i, oldCursor) { if (this.noScrollBack() || !this.checkResizeTimer) return i; this.afterScreenRedraw = true; var m = data.substr(i + 1, 10).match(/^\x1b\[(\d);(\d+)r/); if (m) { var region = {top: parseInt(m[1]), bottom: parseInt(m[2])}; if (region.top == 1 && region.bottom != this.rows) { if (this.waitFullRedraw(data.substr(i + m[0].length), region)) { this.y = oldCursor[0] - 1; this.x = oldCursor[1] - 1; return data.length; } } return i; } return i; }; Terminal.prototype.waitFullRedraw = function(data) { if (this.tmpScreen) return; this.tmpScreen = new Terminal(this.cols, this.rows); var buffer = data; var _self = this; this.on("beforeWrite", function wait(data) { buffer += data; var m = buffer.match(/\x1b\[[\d;]+H\x1b\[[\d;]+r/); if (!m) return ""; _self.off("beforeWrite", wait); _self.tmpScreen = null; return buffer.substr(m.index); }); return true; }; Terminal.prototype.trimLine = function(line) { while (line.length) { var data = line[line.length - 1]; if (!data || !data[1]) line.pop(); else break; } }; Terminal.prototype.padRight = function(line, x) { while (line.length < x) line.push(this.ch); return line; }; Terminal.prototype.rewrapLines = function(lines, x, y, xOld, yOld, noScrollBack) { //TODO optimize lines var line, next; var ybase = this.ybase; var cursorRow = this.y + this.ybase; var cursorColumn = this.x; var addedRows = 0; // resize columns if (xOld < x) { for (var row = 0; row < lines.length; row++) { line = lines[row] || []; if (!noScrollBack) { while (line.wrapped && line.length < x) { next = lines[row + 1]; if (!next) break; this.trimLine(line); while (line.length < x && next.length) { line.push(next.shift()); } if (cursorRow == row + 1) { if (cursorColumn < x - xOld) { cursorRow--; cursorColumn = xOld + cursorColumn; } else { cursorColumn -= x - xOld; } } if (!next.length || this.isLineEmpty(next)) { line.wrapped = next.wrapped; lines.splice(row + 1, 1); addedRows--; if (cursorRow > row) { cursorRow--; } else if (cursorRow == row) { } } } } this.padRight(line, x); } } else if (xOld > x) { for (var row = 0; row < cursorRow; row++) { line = lines[row] || []; var diff = xOld - x; var newLine = line.splice(x, diff); if (!noScrollBack) { while (line.wrapped && !this.isLineEmpty(newLine)) { line = lines[row + 1]; row++; if (!line) break; line.splice.apply(line, [0, 0].concat(newLine)); if (cursorRow == row - 1) { if (this.x >= x) { cursorRow++; cursorColumn = cursorColumn - x; } else { cursorColumn = (cursorColumn + newLine.length) % x; } } else if (cursorRow == row) { cursorColumn = cursorColumn + newLine.length; if (cursorColumn > x) { cursorRow++; cursorColumn -= x; } } newLine = line.splice(x, line.length - x); } while (newLine.length && !this.isLineEmpty(newLine)) { line.wrapped = true; line = newLine.splice(0, x); this.padRight(line, x); if (cursorRow > row) cursorRow++; else if (cursorRow == row) { if (cursorColumn > x) { cursorRow++; cursorColumn -= x; } } row++; lines.splice(row, 0, line); addedRows++; } } } } this.x = cursorColumn; if (cursorRow >= this.y) { ybase = cursorRow - this.y; } while (ybase + yOld > lines.length) { lines.push(this.padRight([], x)); } this.ybase = ybase; // resize rows if (yOld < y) { var toInsert = y - yOld; var scrollBackLength = noScrollBack ? 0 : Math.max(lines.length - y, 0); var fromScrollBack = Math.min(this.ybase, toInsert, scrollBackLength); this.ybase -= fromScrollBack; this.y += fromScrollBack; if (toInsert > fromScrollBack) { var j = toInsert - fromScrollBack; while (j--) { lines.push(this.blankLine()); } } } else if (yOld > y) { var toRemove = yOld - y; while (toRemove-- > 0) { line = lines[lines.length - 1]; if (yOld - this.y > toRemove + 1 && (noScrollBack || this.isLineEmpty(line))) { lines.pop(); } else { toRemove++; break; } } if (toRemove > 0) { this.ybase += toRemove; this.y -= toRemove; } } var max = lines.length - y; while (max > 0) { line = lines[0]; if (!line || !this.isLineEmpty(line)) break; lines.shift(); max--; if (this.ybase > 0) this.ybase--; } return 0; }; Terminal.prototype.resize = function(x, y) {//TODO optimize lines this.emit("resizeStart"); if (x < 5) x = 5; if (y < 1) y = 1; var noScrollBack = this.noScrollBack(); var dy = this.rewrapLines(this.lines, x, y, this.cols, this.rows, noScrollBack); this.setupStops(x); this.cols = x; this.rows = y; this.y += dy; this.ybase += dy; // make sure the cursor stays on screen if (this.y >= y) this.y = y - 1; if (this.x >= x) this.x = x - 1; this.scrollTop = 0; this.scrollBottom = y - 1; this.refresh(0, this.rows - 1); // it's a real nightmare trying // to resize the original // screen buffer. just set it // to null for now. this.normal = null; this.emit("resize", { x: x, y: y }); }; Terminal.prototype.updateRange = function(y) { if (y < this.refreshStart) this.refreshStart = y; if (y > this.refreshEnd) this.refreshEnd = y; }; Terminal.prototype.maxRange = function() { this.refreshStart = 0; this.refreshEnd = this.rows - 1; }; Terminal.prototype.setupStops = function(i) { if (i != null) { if (!this.tabs[i]) { i = this.prevStop(i); } } else { this.tabs = {}; i = 0; } for (; i < this.cols; i += 8) { this.tabs[i] = true; } }; Terminal.prototype.prevStop = function(x) { if (x == null) x = this.x; while (!this.tabs[--x] && x > 0); return x >= this.cols ? this.cols - 1 : x < 0 ? 0 : x; }; Terminal.prototype.nextStop = function(x) { if (x == null) x = this.x; while (!this.tabs[++x] && x < this.cols); return x >= this.cols ? this.cols - 1 : x < 0 ? 0 : x; }; Terminal.prototype.eraseRight = function(x, y) {//TODO optimize lines var line = this.lines[this.ybase + y] , ch = [this.curAttr, '']; // xterm if (!line) return; if (line.wrapped) line.wrapped = "wait"; for (; x < this.cols; x++) { line[x] = ch; } this.updateRange(y); }; Terminal.prototype.eraseLeft = function(x, y) {//TODO optimize lines var line = this.lines[this.ybase + y] , ch = [this.curAttr, '']; // xterm x++; while (x--) line[x] = ch; this.updateRange(y); }; Terminal.prototype.eraseLine = function(y) { this.eraseRight(0, y); }; Terminal.prototype.blankLine = function(cur) { var attr = cur ? this.curAttr : this.defAttr; var ch = [attr, ''] , line = [] , i = 0; for (; i < this.cols; i++) { line[i] = ch; } return line; }; Terminal.prototype.ch = function(cur) { return cur ? [this.curAttr, ''] : [this.defAttr, '']; }; Terminal.prototype.is = function(term) { var name = this.termName || Terminal.termName; return (name + '').indexOf(term) === 0; }; Terminal.prototype.handler = function(data) { this.emit('input', data); }; Terminal.prototype.handleTitle = function(title) { this.emit('title', title); }; /** * ESC */ // ESC D Index (IND is 0x84). Terminal.prototype.index = function() { this.y++; if (this.y > this.scrollBottom) { this.y--; this.scroll(); } this.state = normal; }; // ESC M Reverse Index (RI is 0x8d). Terminal.prototype.reverseIndex = function() {//TODO optimize lines var j; this.y--; if (this.y < this.scrollTop) { this.y++; // possibly move the code below to term.reverseScroll(); // test: echo -ne '\e[1;1H\e[44m\eM\e[0m' // blankLine(true) is xterm/linux behavior this.lines.splice(this.y + this.ybase, 0, this.blankLine(true)); j = this.rows - 1 - this.scrollBottom; this.lines.splice(this.rows - 1 + this.ybase - j + 1, 1); // this.maxRange(); this.updateRange(this.scrollTop); this.updateRange(this.scrollBottom); } this.state = normal; }; // ESC c Full Reset (RIS). Terminal.prototype.reset = function() { Terminal.call(this, this.cols, this.rows); this.refresh(0, this.rows - 1); }; // ESC H Tab Set (HTS is 0x88). Terminal.prototype.tabSet = function() { this.tabs[this.x] = true; this.state = normal; }; /** * CSI */ // CSI Ps A // Cursor Up Ps Times (default = 1) (CUU). Terminal.prototype.cursorUp = function(params) { var param = params[0]; if (param < 1) param = 1; this.y -= param; if (this.y < 0) this.y = 0; }; // CSI Ps B // Cursor Down Ps Times (default = 1) (CUD). Terminal.prototype.cursorDown = function(params) { var param = params[0]; if (param < 1) param = 1; this.y += param; if (this.y >= this.rows) { this.y = this.rows - 1; } }; // CSI Ps C // Cursor Forward Ps Times (default = 1) (CUF). Terminal.prototype.cursorForward = function(params) { var param = params[0]; if (param < 1) param = 1; this.x += param; if (this.x >= this.cols) { this.x = this.cols - 1; } }; // CSI Ps D // Cursor Backward Ps Times (default = 1) (CUB). Terminal.prototype.cursorBackward = function(params) { var param = params[0]; if (param < 1) param = 1; this.x -= param; if (this.x < 0) this.x = 0; }; // CSI Ps ; Ps H // Cursor Position [row;column] (default = [1,1]) (CUP). Terminal.prototype.cursorPos = function(params) { var row, col; row = params[0] - 1; if (params.length >= 2) { col = params[1] - 1; } else { col = 0; } if (row < 0) { row = 0; } else if (row >= this.rows) { row = this.rows - 1; } if (col < 0) { col = 0; } else if (col >= this.cols) { col = this.cols - 1; } this.x = col; this.y = row; }; // 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. Terminal.prototype.eraseInDisplay = function(params) { var j; switch (params[0]) { case 0: this.eraseRight(this.x, this.y); j = this.y + 1; for (; j < this.rows; j++) { this.eraseLine(j); } break; case 1: this.eraseLeft(this.x, this.y); j = this.y; while (j--) { this.eraseLine(j); } break; case 2: j = this.rows; while (j--) this.eraseLine(j); break; case 3: ; // no saved lines break; } }; // 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. Terminal.prototype.eraseInLine = function(params) { switch (params[0]) { case 0: this.eraseRight(this.x, this.y); break; case 1: this.eraseLeft(this.x, this.y); break; case 2: this.eraseLine(this.y); break; } }; // 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. Terminal.prototype.charAttributes = function(params) { var l = params.length , i = 0 , bg , fg , p; for (; i < l; i++) { p = params[i]; if (p >= 30 && p <= 37) { // fg color 8 this.curAttr = (this.curAttr & ~(0x1ff << 9)) | ((p - 30) << 9); } else if (p >= 40 && p <= 47) { // bg color 8 this.curAttr = (this.curAttr & ~0x1ff) | (p - 40); } else if (p >= 90 && p <= 97) { // fg color 16 p += 8; this.curAttr = (this.curAttr & ~(0x1ff << 9)) | ((p - 90) << 9); } else if (p >= 100 && p <= 107) { // bg color 16 p += 8; this.curAttr = (this.curAttr & ~0x1ff) | (p - 100); } else if (p === 0) { // default this.curAttr = this.defAttr; } else if (p === 1) { // bold text this.curAttr = this.curAttr | (1 << 18); } else if (p === 4) { // underlined text this.curAttr = this.curAttr | (2 << 18); } else if (p === 7 || p === 27) { // inverse and positive // test with: echo -e '\e[31m\e[42mhello\e[7mworld\e[27mhi\e[m' if (p === 7) { if ((this.curAttr >> 18) & 4) continue; this.curAttr = this.curAttr | (4 << 18); } else if (p === 27) { if (~(this.curAttr >> 18) & 4) continue; this.curAttr = this.curAttr & ~(4 << 18); } bg = this.curAttr & 0x1ff; fg = (this.curAttr >> 9) & 0x1ff; this.curAttr = (this.curAttr & ~0x3ffff) | ((bg << 9) | fg); } else if (p === 22) { // not bold this.curAttr = this.curAttr & ~(1 << 18); } else if (p === 24) { // not underlined this.curAttr = this.curAttr & ~(2 << 18); } else if (p === 39) { // reset fg this.curAttr = this.curAttr & ~(0x1ff << 9); this.curAttr = this.curAttr | (((this.defAttr >> 9) & 0x1ff) << 9); } else if (p === 49) { // reset bg this.curAttr = this.curAttr & ~0x1ff; this.curAttr = this.curAttr | (this.defAttr & 0x1ff); } else if (p === 38) { // fg color 256 if (params[i+1] !== 5) continue; i += 2; p = params[i] & 0xff; // convert 88 colors to 256 // if (this.is('rxvt-unicode') && p < 88) p = p * 2.9090 | 0; this.curAttr = (this.curAttr & ~(0x1ff << 9)) | (p << 9); } else if (p === 48) { // bg color 256 if (params[i+1] !== 5) continue; i += 2; p = params[i] & 0xff; // convert 88 colors to 256 // if (this.is('rxvt-unicode') && p < 88) p = p * 2.9090 | 0; this.curAttr = (this.curAttr & ~0x1ff) | p; } } }; // 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 tab 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. Terminal.prototype.deviceStatus = function(params) { if (!this.prefix) { switch (params[0]) { case 5: // status report this.send('\x1b[0n'); break; case 6: // cursor position this.send('\x1b[' + (this.y + 1) + ';' + (this.x + 1) + 'R'); break; } } else if (this.prefix === '?') { // modern xterm doesnt seem to // respond to any of these except ?6, 6, and 5 switch (params[0]) { case 6: // cursor position this.send('\x1b[?' + (this.y + 1) + ';' + (this.x + 1) + 'R'); break; case 15: // no printer // this.send('\x1b[?11n'); break; case 25: // dont support user defined keys // this.send('\x1b[?21n'); break; case 26: // north american keyboard // this.send('\x1b[?27;1;0;0n'); break; case 53: // no dec locator/mouse // this.send('\x1b[?50n'); break; } } }; /** * Additions */ // CSI Ps @ // Insert Ps (Blank) Character(s) (default = 1) (ICH). Terminal.prototype.insertChars = function(params) {//TODO optimize lines var param, row, j, ch; param = params[0]; if (param < 1) param = 1; row = this.y + this.ybase; j = this.x; ch = [this.curAttr, '']; // xterm while (param-- && j < this.cols) { this.lines[row].splice(j++, 0, ch); this.lines[row].pop(); } }; // CSI Ps E // Cursor Next Line Ps Times (default = 1) (CNL). // same as CSI Ps B ? Terminal.prototype.cursorNextLine = function(params) { var param = params[0]; if (param < 1) param = 1; this.y += param; if (this.y >= this.rows) { this.y = this.rows - 1; } this.x = 0; }; // CSI Ps F // Cursor Preceding Line Ps Times (default = 1) (CNL). // reuse CSI Ps A ? Terminal.prototype.cursorPrecedingLine = function(params) { var param = params[0]; if (param < 1) param = 1; this.y -= param; if (this.y < 0) this.y = 0; this.x = 0; }; // CSI Ps G // Cursor Character Absolute [column] (default = [row,1]) (CHA). Terminal.prototype.cursorCharAbsolute = function(params) { var param = params[0]; if (param < 1) param = 1; this.x = param - 1; }; // CSI Ps L // Insert Ps Line(s) (default = 1) (IL). Terminal.prototype.insertLines = function(params) {//TODO optimize lines var param, row, j; param = params[0]; if (param < 1) param = 1; row = this.y + this.ybase; j = this.rows - 1 - this.scrollBottom; j = this.rows - 1 + this.ybase - j + 1; while (param--) { // test: echo -e '\e[44m\e[1L\e[0m' // blankLine(true) - xterm/linux behavior this.lines.splice(row, 0, this.blankLine(true)); this.lines.splice(j, 1); } // this.maxRange(); this.updateRange(this.y); this.updateRange(this.scrollBottom); }; // CSI Ps M // Delete Ps Line(s) (default = 1) (DL). Terminal.prototype.deleteLines = function(params) {//TODO optimize lines var param, row, j; param = params[0]; if (param < 1) param = 1; row = this.y + this.ybase; j = this.rows - 1 - this.scrollBottom; j = this.rows - 1 + this.ybase - j; while (param--) { // test: echo -e '\e[44m\e[1M\e[0m' // blankLine(true) - xterm/linux behavior this.lines.splice(j + 1, 0, this.blankLine(true)); this.lines.splice(row, 1); } // this.maxRange(); this.updateRange(this.y); this.updateRange(this.scrollBottom); }; // CSI Ps P // Delete Ps Character(s) (default = 1) (DCH). Terminal.prototype.deleteChars = function(params) {//TODO optimize lines var param, row, ch; param = params[0]; if (param < 1) param = 1; row = this.y + this.ybase; ch = [this.curAttr, '']; // xterm while (param--) { this.lines[row].splice(this.x, 1); this.lines[row].push(ch); } }; // CSI Ps X // Erase Ps Character(s) (default = 1) (ECH). Terminal.prototype.eraseChars = function(params) {//TODO optimize lines var param, row, j, ch; param = params[0]; if (param < 1) param = 1; row = this.y + this.ybase; j = this.x; ch = [this.curAttr, '']; // xterm while (param-- && j < this.cols) { this.lines[row][j++] = ch; } }; // CSI Pm ` Character Position Absolute // [column] (default = [row,1]) (HPA). Terminal.prototype.charPosAbsolute = function(params) { var param = params[0]; if (param < 1) param = 1; this.x = param - 1; if (this.x >= this.cols) { this.x = this.cols - 1; } }; // 141 61 a * HPR - // Horizontal Position Relative // reuse CSI Ps C ? Terminal.prototype.HPositionRelative = function(params) { var param = params[0]; if (param < 1) param = 1; this.x += param; if (this.x >= this.cols) { this.x = this.cols - 1; } }; // 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 (?) Terminal.prototype.sendDeviceAttributes = function(params) { if (params[0] > 0) return; if (!this.prefix) { if (this.is('xterm') || this.is('rxvt-unicode') || this.is('screen')) { this.send('\x1b[?1;2c'); } else if (this.is('linux')) { this.send('\x1b[?6c'); } } else if (this.prefix === '>') { // xterm and urxvt // seem to spit this // out around ~370 times (?). if (this.is('xterm')) { this.send('\x1b[>0;276;0c'); } else if (this.is('rxvt-unicode')) { this.send('\x1b[>85;95;0c'); } else if (this.is('linux')) { // not supported by linux console. // linux console echoes parameters. this.send(params[0] + 'c'); } else if (this.is('screen')) { this.send('\x1b[>83;40003;0c'); } } }; // CSI Pm d // Line Position Absolute [row] (default = [1,column]) (VPA). Terminal.prototype.linePosAbsolute = function(params) { var param = params[0]; if (param < 1) param = 1; this.y = param - 1; if (this.y >= this.rows) { this.y = this.rows - 1; } }; // 145 65 e * VPR - Vertical Position Relative // reuse CSI Ps B ? Terminal.prototype.VPositionRelative = function(params) { var param = params[0]; if (param < 1) param = 1; this.y += param; if (this.y >= this.rows) { this.y = this.rows - 1; } }; // CSI Ps ; Ps f // Horizontal and Vertical Position [row;column] (default = // [1,1]) (HVP). Terminal.prototype.HVPosition = function(params) { if (params[0] < 1) params[0] = 1; if (params[1] < 1) params[1] = 1; this.y = params[0] - 1; if (this.y >= this.rows) { this.y = this.rows - 1; } this.x = params[1] - 1; if (this.x >= this.cols) { this.x = this.cols - 1; } }; // 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 Terminal.prototype.setMode = function(params) { if (typeof params === 'object') { var l = params.length , i = 0; for (; i < l; i++) { this.setMode(params[i]); } return; } if (!this.prefix) { switch (params) { case 4: this.insertMode = true; break; case 20: //this.convertEol = true; break; } } else if (this.prefix === '?') { switch (params) { case 1: this.applicationKeypad = true; break; case 3: // 132 col mode this.savedCols = this.cols; this.resize(132, this.rows); break; case 6: this.originMode = true; break; case 7: this.wraparoundMode = true; break; case 12: // this.cursorBlink = true; break; case 9: // X10 Mouse // no release, no motion, no wheel, no modifiers. case 1000: // vt200 mouse // no motion. // no modifiers, except control on the wheel. case 1002: // button event mouse case 1003: // any event mouse // any event - sends motion events, // even if there is no button held down. this.x10Mouse = params === 9; this.vt200Mouse = params === 1000; this.normalMouse = params > 1000; this.mouseEvents = true; //this.element.style.cursor = 'default'; this.log('Binding to mouse events.'); break; case 1004: // send focusin/focusout events // focusin: ^[[I // focusout: ^[[O this.sendFocus = true; break; case 1005: // utf8 ext mode mouse this.utfMouse = true; // for wide terminals // simply encodes large values as utf8 characters break; case 1006: // sgr ext mode mouse this.sgrMouse = true; // for wide terminals // does not add 32 to fields // press: ^[[ 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. Terminal.prototype.resetMode = function(params) { if (typeof params === 'object') { var l = params.length , i = 0; for (; i < l; i++) { this.resetMode(params[i]); } return; } if (!this.prefix) { switch (params) { case 4: this.insertMode = false; break; case 20: //this.convertEol = false; break; } } else if (this.prefix === '?') { switch (params) { case 1: this.applicationKeypad = false; break; case 3: if (this.cols === 132 && this.savedCols) { this.resize(this.savedCols, this.rows); } delete this.savedCols; break; case 6: this.originMode = false; break; case 7: this.wraparoundMode = false; break; case 12: // this.cursorBlink = false; break; case 9: // X10 Mouse case 1000: // vt200 mouse case 1002: // button event mouse case 1003: // any event mouse this.x10Mouse = false; this.vt200Mouse = false; this.normalMouse = false; this.mouseEvents = false; //this.element.style.cursor = ''; break; case 1004: // send focusin/focusout events this.sendFocus = false; break; case 1005: // utf8 ext mode mouse this.utfMouse = false; break; case 1006: // sgr ext mode mouse this.sgrMouse = false; break; case 1015: // urxvt ext mode mouse this.urxvtMouse = false; break; case 25: // hide cursor this.cursorHidden = true; break; case 1049: // alt screen buffer cursor ; // FALL-THROUGH case 47: // normal screen buffer case 1047: // normal screen buffer - clearing it first if (this.normal) {//TODO optimize lines this.lines = this.normal.lines; this.ybase = this.normal.ybase; this.ydisp = this.normal.ydisp; this.x = this.normal.x; this.y = this.normal.y; this.scrollTop = this.normal.scrollTop; this.scrollBottom = this.normal.scrollBottom; this.tabs = this.normal.tabs; this.normal = null; // if (params === 1049) { // this.x = this.savedX; // this.y = this.savedY; // } this.refresh(0, this.rows - 1); this.showCursor(); } break; } } }; // CSI Ps ; Ps r // Set Scrolling Region [top;bottom] (default = full size of win- // dow) (DECSTBM). // CSI ? Pm r Terminal.prototype.setScrollRegion = function(params) { if (this.prefix) return; this.scrollTop = (params[0] || 1) - 1; this.scrollBottom = (params[1] || this.rows) - 1; this.x = 0; this.y = 0; this.emit("setScrollRegion"); }; // CSI s // Save cursor (ANSI.SYS). Terminal.prototype.saveCursor = function(params) { this.savedX = this.x; this.savedY = this.y; }; // CSI u // Restore cursor (ANSI.SYS). Terminal.prototype.restoreCursor = function(params) { this.x = this.savedX || 0; this.y = this.savedY || 0; }; /** * Lesser Used */ // CSI Ps I // Cursor Forward Tabulation Ps pane stops (default = 1) (CHT). Terminal.prototype.cursorForwardTab = function(params) { var param = params[0] || 1; while (param--) { this.x = this.nextStop(); } }; // CSI Ps S Scroll up Ps lines (default = 1) (SU). Terminal.prototype.scrollUp = function(params) {//TODO optimize lines var param = params[0] || 1; while (param--) { this.lines.splice(this.ybase + this.scrollTop, 1); this.lines.splice(this.ybase + this.scrollBottom, 0, this.blankLine()); } // this.maxRange(); this.updateRange(this.scrollTop); this.updateRange(this.scrollBottom); }; // CSI Ps T Scroll down Ps lines (default = 1) (SD). Terminal.prototype.scrollDown = function(params) {//TODO optimize lines var param = params[0] || 1; while (param--) { this.lines.splice(this.ybase + this.scrollBottom, 1); this.lines.splice(this.ybase + this.scrollTop, 0, this.blankLine()); } // this.maxRange(); this.updateRange(this.scrollTop); this.updateRange(this.scrollBottom); }; // CSI Ps ; Ps ; Ps ; Ps ; Ps T // Initiate highlight mouse tracking. Parameters are // [func;startx;starty;firstrow;lastrow]. See the section Mouse // Tracking. Terminal.prototype.initMouseTracking = function(params) { // Relevant: DECSET 1001 }; // 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"). Terminal.prototype.resetTitleModes = function(params) { ; }; // CSI Ps Z Cursor Backward Tabulation Ps pane stops (default = 1) (CBT). Terminal.prototype.cursorBackwardTab = function(params) { var param = params[0] || 1; while (param--) { this.x = this.prevStop(); } }; // CSI Ps b Repeat the preceding graphic character Ps times (REP). Terminal.prototype.repeatPrecedingCharacter = function(params) {//TODO optimize lines var param = params[0] || 1 , line = this.lines[this.ybase + this.y] , ch = line[this.x - 1] || [this.defAttr, '']; while (param--) line[this.x++] = ch; }; // 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 Terminal.prototype.tabClear = function(params) { var param = params[0]; if (param <= 0) { delete this.tabs[this.x]; } else if (param === 3) { this.tabs = {}; } }; // 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. Terminal.prototype.mediaCopy = function(params) { ; }; // 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. Terminal.prototype.setResources = function(params) { ; }; // 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. Terminal.prototype.disableModifiers = function(params) { ; }; // 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 . Terminal.prototype.setPointerMode = function(params) { ; }; // CSI ! p Soft terminal reset (DECSTR). // http://vt100.net/docs/vt220-rm/table4-10.html Terminal.prototype.softReset = function(params) { this.cursorHidden = false; this.insertMode = false; this.originMode = false; this.wraparoundMode = false; // autowrap this.applicationKeypad = false; // ? this.scrollTop = 0; this.scrollBottom = this.rows - 1; this.curAttr = this.defAttr; this.x = this.y = 0; // ? this.charset = null; this.charsets = [null]; // ?? }; // 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 Terminal.prototype.requestAnsiMode = function(params) { ; }; // 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. Terminal.prototype.requestPrivateMode = function(params) { ; }; // 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. Terminal.prototype.setConformanceLevel = function(params) { ; }; // 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. Terminal.prototype.loadLEDs = function(params) { ; }; // 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. Terminal.prototype.setCursorStyle = function(params) { console.log("setCursorStyle", params); }; // 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. Terminal.prototype.setCharProtectionAttr = function(params) { ; }; // CSI ? Pm r // Restore DEC Private Mode Values. The value of Ps previously // saved is restored. Ps values are the same as for DECSET. Terminal.prototype.restorePrivateValues = function(params) { ; }; // 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. Terminal.prototype.setAttrInRectangle = function(params) {//TODO optimize lines var t = params[0] , l = params[1] , b = params[2] , r = params[3] , attr = params[4]; var line , i; for (; t < b + 1; t++) { line = this.lines[this.ybase + t]; for (i = l; i < r; i++) { line[i] = [attr, line[i][1]]; } } // this.maxRange(); this.updateRange(params[0]); this.updateRange(params[2]); }; // CSI ? Pm s // Save DEC Private Mode Values. Ps values are the same as for // DECSET. Terminal.prototype.savePrivateValues = function(params) { ; }; // 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). Terminal.prototype.manipulateWindow = function(params) { ; }; // 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. Terminal.prototype.reverseAttrInRectangle = function(params) { ; }; // 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") Terminal.prototype.setTitleModeFeature = function(params) { ; }; // 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. Terminal.prototype.setWarningBellVolume = function(params) { ; }; // 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. Terminal.prototype.setMarginBellVolume = function(params) { ; }; // 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 tab. // Pt; Pl denotes the target location. // Pp denotes the target tab. // NOTE: xterm doesn't enable this code by default. Terminal.prototype.copyRectangle = function(params) { ; }; // 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. Terminal.prototype.enableFilterRectangle = function(params) { ; }; // 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. Terminal.prototype.requestParameters = function(params) { ; }; // 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). Terminal.prototype.selectChangeExtent = function(params) { ; }; // 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. Terminal.prototype.fillRectangle = function(params) {//TODO optimize lines var ch = params[0] , t = params[1] , l = params[2] , b = params[3] , r = params[4]; var line , i; for (; t < b + 1; t++) { line = this.lines[this.ybase + t]; for (i = l; i < r; i++) { line[i] = [line[i][0], String.fromCharCode(ch)]; } } // this.maxRange(); this.updateRange(params[1]); this.updateRange(params[3]); }; // 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. Terminal.prototype.enableLocatorReporting = function(params) { var val = params[0] > 0; //this.mouseEvents = val; //this.decLocator = val; }; // 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. Terminal.prototype.eraseRectangle = function(params) {//TODO optimize lines var t = params[0] , l = params[1] , b = params[2] , r = params[3]; var line , i , ch; ch = [this.curAttr, '']; // xterm? for (; t < b + 1; t++) { line = this.lines[this.ybase + t]; for (i = l; i < r; i++) { line[i] = ch; } } // this.maxRange(); this.updateRange(params[0]); this.updateRange(params[2]); }; // 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. Terminal.prototype.setLocatorEvents = function(params) { ; }; // CSI Pt; Pl; Pb; Pr$ { // Selective Erase Rectangular Area (DECSERA), VT400 and up. // Pt; Pl; Pb; Pr denotes the rectangle. Terminal.prototype.selectiveEraseRectangle = function(params) { ; }; // 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;tab]. // 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 ``tab'' parameter is not used by xterm, and will be omit- // ted. Terminal.prototype.requestLocatorPosition = function(params) { ; }; // CSI P m SP } // Insert P s Column(s) (default = 1) (DECIC), VT420 and up. // NOTE: xterm doesn't enable this code by default. Terminal.prototype.insertColumns = function(params) {//TODO optimize lines var param = params[0] , l = this.ybase + this.rows , ch = [this.curAttr, ''] // xterm? , i; while (param--) { for (i = this.ybase; i < l; i++) { this.lines[i].splice(this.x + 1, 0, ch); this.lines[i].pop(); } } this.maxRange(); }; // CSI P m SP ~ // Delete P s Column(s) (default = 1) (DECDC), VT420 and up // NOTE: xterm doesn't enable this code by default. Terminal.prototype.deleteColumns = function(params) {//TODO optimize lines var param = params[0] , l = this.ybase + this.rows , ch = [this.curAttr, ''] // xterm? , i; while (param--) { for (i = this.ybase; i < l; i++) { this.lines[i].splice(this.x, 1); this.lines[i].push(ch); } } this.maxRange(); }; /** * Character Sets */ Terminal.charsets = {}; // DEC Special Character and Line Drawing Set. // http://vt100.net/docs/vt102-ug/table5-13.html // A lot of curses apps use this if they see TERM=xterm. // testing: echo -e '\e(0a\e(B' // The xterm output sometimes seems to conflict with the // reference above. xterm seems in line with the reference // when running vttest however. // The table below now uses xterm's output from vttest. Terminal.charsets.SCLD = { // (0 '`': '\u25c6', // '◆' 'a': '\u2592', // '▒' 'b': '\u0009', // '\t' 'c': '\u000c', // '\f' 'd': '\u000d', // '\r' 'e': '\u000a', // '\n' 'f': '\u00b0', // '°' 'g': '\u00b1', // '±' 'h': '\u2424', // '\u2424' (NL) 'i': '\u000b', // '\v' 'j': '\u2518', // '┘' 'k': '\u2510', // '┐' 'l': '\u250c', // '┌' 'm': '\u2514', // '└' 'n': '\u253c', // '┼' 'o': '\u23ba', // '⎺' 'p': '\u23bb', // '⎻' 'q': '\u2500', // '─' 'r': '\u23bc', // '⎼' 's': '\u23bd', // '⎽' 't': '\u251c', // '├' 'u': '\u2524', // '┤' 'v': '\u2534', // '┴' 'w': '\u252c', // '┬' 'x': '\u2502', // '│' 'y': '\u2264', // '≤' 'z': '\u2265', // '≥' '{': '\u03c0', // 'π' '|': '\u2260', // '≠' '}': '\u00a3', // '£' '~': '\u00b7' // '·' }; Terminal.charsets.UK = null; // (A Terminal.charsets.US = null; // (B (USASCII) Terminal.charsets.Dutch = null; // (4 Terminal.charsets.Finnish = null; // (C or (5 Terminal.charsets.French = null; // (R Terminal.charsets.FrenchCanadian = null; // (Q Terminal.charsets.German = null; // (K Terminal.charsets.Italian = null; // (Y Terminal.charsets.NorwegianDanish = null; // (E or (6 Terminal.charsets.Spanish = null; // (Z Terminal.charsets.Swedish = null; // (H or (7 Terminal.charsets.Swiss = null; // (= Terminal.charsets.ISOLatin = null; // /A module.exports = Terminal; /* for debugging */ var CSIRegexp; function getCSIRegexp(argument) { if (CSIRegexp) return CSIRegexp; // http://www.inwap.com/pdp10/ansicode.txt var classes = { C0 : "\\x00-\\x1F", // SPACE : "\\x20\\xA0" , // Always and everywhere a blank space G0 : "\\x21-\\x7E", // Intermediate : "\\x20-\\x2F", // !"#$%&'()*+,-./ Parameters : "\\x30-\\x3F", // 0123456789:;<=>? Uppercase : "\\x40-\\x5F", // @ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_ Lowercase : "\\x60-\\x7E", // `abcdefghijlkmnopqrstuvwxyz{|}~ Alphabetic : "\\x40-\\x7E", // (all of upper and lower case) Delete : "\\x7F" , // Always and everywhere ignored C1 : "\\x80-\\x9F", // 32 additional control characters G1 : "\\xA1-\\xFE", // 94 additional displayable characters Special : "\\xA0\\xFF" , // Same as SPACE and DELETE ESC : "\\x1b" , // Ignore : "\\x7F\\x00-\\x1F\\x80-\\x9F" // Delete|C0|C1 }; var g = /:ESC:(\[:Parameters:*:Intermediate:?:Alphabetic:?|:Intermediate:+:Parameters:|:Parameters:|:Lowercase:|)|[:Ignore:]/; var inBrackets = false; var source = g.source.replace(/(\\.)|([\[\]])|:(\w*):/g, function(_, esc, b, cls) { if (esc) return esc; if (b) { inBrackets = b == "["; return b; } if (!cls) return ":"; var r = classes[cls]; if (!/ESC|Delete|C0|C1/.test(cls)) { r += classes.Ignore; } if (!inBrackets && cls != "ESC") r = "[" + r + "]"; return r; }); return CSIRegexp = new RegExp(source, "g"); } function log(str) { var r = getCSIRegexp(), m; var tokens = [], last = 0; while ((m = r.exec(str))) { var l = m.index; if (l > last) tokens.push({ type: "black", val: str.substring(last, l) }); var val = m[0]; tokens.push({ type: /^\x1b\[.*m$/.test(val) ? "gray" : val == "\r" ? "darkgray" : true ? "red" : "", val: JSON.stringify(val).slice(1, - 1).replace(/\\u001b/g, "\x1b").replace(/\\n/g, "\n") }); last = r.lastIndex; } if (last < str.length) tokens.push({ type: "text", val: str.substr(last) }); var args = [""]; tokens.forEach(function(t) { if (t.val) { args[0] += "%c" + t.val; args.push("color:"+ t.type + ";border-right: 0px solid gray; padding-right: 0.7em"); } }); console.log.apply(console, args); return tokens; } // log("\x1b[1;1H\x1b[1;16") // log("\r\x1b\x1b;spass\x1b0as0\n00----\x1b!a0---\xf10") Terminal.log = log; Terminal.prototype.logLine = function(line) { if (typeof line == "number") line = this.lines[line]; var fgColor, bgColor, flags; var text = "", style = "", tokens = [text]; var defAttr = this.defAttr; var attr = defAttr; for (var i = 0; i < line.length; i++) { var data = line[i][0]; var ch = line[i][1]; if (data !== attr) { if (attr !== defAttr) { tokens.push(style); text += "%c"; } if (data === defAttr) { // do nothing } else if (data === -1) { } else { style = ""; bgColor = data & 0x1ff; fgColor = (data >> 9) & 0x1ff; flags = data >> 18; if (flags & 1) { if (this.allowBoldFonts) style += 'font-weight:bold;'; // see: XTerm*boldColors if (fgColor < 8) fgColor += 8; } if (flags & 2) style += 'text-decoration:underline;'; if (bgColor === 256) { if (fgColor !== 257) style += 'color:' + ( Terminal.overridenColors[fgColor] || Terminal.colors[fgColor] ) + ';'; } else { style += 'background-color:' + Terminal.colors[bgColor] + ';'; if (fgColor !== 257) style += 'color:' + Terminal.colors[fgColor] + ';'; style += 'display:inline-block" class="aceterm-line-bg" l="' + i; } } } if (ch == '%') text += '%%'; else text += ch; attr = data; } tokens[0] = text; console.log.apply(console, tokens); return tokens; }; });